From d06a9d843fb65351e0e4dc42ba0c404f01ea92b3 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 6 Dec 2024 12:20:29 +0100 Subject: [PATCH 0001/2892] 9pfs: fix regression regarding CVE-2023-2861 The released fix for this CVE: f6b0de53fb8 ("9pfs: prevent opening special files (CVE-2023-2861)") caused a regression with security_model=passthrough. When handling a 'Tmknod' request there was a side effect that 'Tmknod' request could fail as 9p server was trying to adjust permissions: #6 close_if_special_file (fd=30) at ../hw/9pfs/9p-util.h:140 #7 openat_file (mode=, flags=2228224, name=, dirfd=) at ../hw/9pfs/9p-util.h:181 #8 fchmodat_nofollow (dirfd=dirfd@entry=31, name=name@entry=0x5555577ea6e0 "mysocket", mode=493) at ../hw/9pfs/9p-local.c:360 #9 local_set_cred_passthrough (credp=0x7ffbbc4ace10, name=0x5555577ea6e0 "mysocket", dirfd=31, fs_ctx=0x55555811f528) at ../hw/9pfs/9p-local.c:457 #10 local_mknod (fs_ctx=0x55555811f528, dir_path=, name=0x5555577ea6e0 "mysocket", credp=0x7ffbbc4ace10) at ../hw/9pfs/9p-local.c:702 #11 v9fs_co_mknod (pdu=pdu@entry=0x555558121140, fidp=fidp@entry=0x5555574c46c0, name=name@entry=0x7ffbbc4aced0, uid=1000, gid=1000, dev=, mode=49645, stbuf=0x7ffbbc4acef0) at ../hw/9pfs/cofs.c:205 #12 v9fs_mknod (opaque=0x555558121140) at ../hw/9pfs/9p.c:3711 That's because server was opening the special file to adjust permissions, however it was using O_PATH and it would have not returned the file descriptor to guest. So the call to close_if_special_file() on that branch was incorrect. Let's lift the restriction introduced by f6b0de53fb8 such that it would allow to open special files on host if O_PATH flag is supplied, not only for 9p server's own operations as described above, but also for any client 'Topen' request. It is safe to allow opening special files with O_PATH on host, because O_PATH only allows path based operations on the resulting file descriptor and prevents I/O such as read() and write() on that file descriptor. Fixes: f6b0de53fb8 ("9pfs: prevent opening special files (CVE-2023-2861)") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2337 Reported-by: Dirk Herrendorfer Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Tested-by: Dirk Herrendorfer Message-Id: --- hw/9pfs/9p-util.h | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 51c94b0116..95ee4da9bd 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -177,20 +177,27 @@ again: return -1; } - if (close_if_special_file(fd) < 0) { - return -1; - } - - serrno = errno; - /* O_NONBLOCK was only needed to open the file. Let's drop it. We don't - * do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat() - * ignored it anyway. - */ + /* Only if O_PATH is not set ... */ if (!(flags & O_PATH_9P_UTIL)) { + /* + * Prevent I/O on special files (device files, etc.) on host side, + * however it is safe and required to allow opening them with O_PATH, + * as this is limited to (required) path based operations only. + */ + if (close_if_special_file(fd) < 0) { + return -1; + } + + serrno = errno; + /* + * O_NONBLOCK was only needed to open the file. Let's drop it. We don't + * do that with O_PATH since fcntl(F_SETFL) isn't supported, and + * openat() ignored it anyway. + */ ret = fcntl(fd, F_SETFL, flags); assert(!ret); + errno = serrno; } - errno = serrno; return fd; } From 97f2796a3736ed37a1b85dc1c76a6c45b829dd17 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 17:41:17 +0000 Subject: [PATCH 0002/2892] Open 10.0 development tree Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index deeb3d66ef..a318b6e843 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.0 +9.2.50 From 72d6ed19bf25e1289be7b05326d3ed3c9f0cabe9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 Oct 2024 07:24:32 +0200 Subject: [PATCH 0003/2892] ci: enable rust in the Debian and Ubuntu system build job We have fixed all incompatibilities with older versions of rustc and bindgen. Enable Rust on Debian to check that the minimum supported version of Rust is indeed 1.63.0, and 0.60.x for bindgen. Reviewed-by: Pierrick Bouvier Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 336223484d..4265a57783 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -40,7 +40,7 @@ build-system-ubuntu: job: amd64-ubuntu2204-container variables: IMAGE: ubuntu2204 - CONFIGURE_ARGS: --enable-docs + CONFIGURE_ARGS: --enable-docs --enable-rust TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu MAKE_CHECK_ARGS: check-build @@ -71,7 +71,7 @@ build-system-debian: job: amd64-debian-container variables: IMAGE: debian - CONFIGURE_ARGS: --with-coroutine=sigaltstack + CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensa-softmmu MAKE_CHECK_ARGS: check-build From 8c2866750319fac3f7b641d1a097f3779952631d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 12 Nov 2024 11:35:34 +0100 Subject: [PATCH 0004/2892] rust: apply --cfg MESON to all crates We might have more uses for --cfg MESON, even though right now it's only qemu-api that has generated files. Since we're going to add more flags to the add_project_arguments calls for Rust, it makes sense to also add --cfg MESON everywhere. Signed-off-by: Paolo Bonzini --- meson.build | 7 ++++--- rust/qemu-api/meson.build | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 147097c652..c35ce64cd6 100644 --- a/meson.build +++ b/meson.build @@ -3422,10 +3422,11 @@ if have_rust # is safe; https://github.com/rust-lang/rust/pull/54675 says that # passing -nodefaultlibs to the linker "was more ideological to # start with than anything". - add_project_arguments(rustc_args + ['-C', 'default-linker-libraries'], + add_project_arguments(rustc_args + + ['--cfg', 'MESON', '-C', 'default-linker-libraries'], native: false, language: 'rust') - - add_project_arguments(rustc_args, native: true, language: 'rust') + add_project_arguments(rustc_args + ['--cfg', 'MESON'], + native: true, language: 'rust') endif hxtool = find_program('scripts/hxtool') diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 6f637af7b1..cad9ac4844 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -1,4 +1,4 @@ -_qemu_api_cfg = ['--cfg', 'MESON'] +_qemu_api_cfg = [] # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] From cb7ada5409f171dae364f206a7fe3ff30fcba7cb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 12 Nov 2024 11:52:23 +0100 Subject: [PATCH 0005/2892] rust: allow using build-root bindings.rs from cargo Right now, using cargo with QEMU requires copying by hand the bindings.rs to the source tree. Instead, we can use an include file to escape the cage of cargo's mandated source directory structure. By running cargo within meson's "devenv" and adding a MESON_BUILD_ROOT environment variable, it is easy for build.rs to find the file. However, the file must be symlinked into cargo's output directory for rust-analyzer to find it. Suggested-by: Junjie Mao Signed-off-by: Paolo Bonzini --- meson.build | 4 +++- rust/hw/char/pl011/.gitignore | 2 -- rust/qemu-api/.gitignore | 2 +- rust/qemu-api/README.md | 4 ++-- rust/qemu-api/build.rs | 34 +++++++++++++++++++++++++++------- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/bindings.rs | 29 +++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 22 ---------------------- 8 files changed, 63 insertions(+), 35 deletions(-) delete mode 100644 rust/hw/char/pl011/.gitignore create mode 100644 rust/qemu-api/src/bindings.rs diff --git a/meson.build b/meson.build index c35ce64cd6..ea211c2dbb 100644 --- a/meson.build +++ b/meson.build @@ -3,6 +3,8 @@ project('qemu', ['c'], meson_version: '>=1.5.0', 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], version: files('VERSION')) +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']) @@ -4090,7 +4092,7 @@ if have_rust bindings_rs = rust.bindgen( input: 'rust/wrapper.h', dependencies: common_ss.all_dependencies(), - output: 'bindings.rs', + output: 'bindings.inc.rs', include_directories: include_directories('.', 'include'), bindgen_version: ['>=0.60.0'], args: bindgen_args, diff --git a/rust/hw/char/pl011/.gitignore b/rust/hw/char/pl011/.gitignore deleted file mode 100644 index 71eaff2035..0000000000 --- a/rust/hw/char/pl011/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore generated bindings file overrides. -src/bindings.rs.inc diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore index b9e7e004c8..df6c2163e0 100644 --- a/rust/qemu-api/.gitignore +++ b/rust/qemu-api/.gitignore @@ -1,2 +1,2 @@ # Ignore generated bindings file overrides. -src/bindings.rs +/src/bindings.inc.rs diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md index 7588fa29ef..53810f4888 100644 --- a/rust/qemu-api/README.md +++ b/rust/qemu-api/README.md @@ -5,7 +5,7 @@ This library exports helper Rust types, Rust macros and C FFI bindings for inter The C bindings can be generated with `bindgen`, using this build target: ```console -$ ninja bindings.rs +$ ninja bindings.inc.rs ``` ## Generate Rust documentation @@ -13,5 +13,5 @@ $ ninja bindings.rs To generate docs for this crate, including private items: ```sh -cargo doc --no-deps --document-private-items +pyvenv/bin/meson devenv -w ../rust cargo doc --no-deps --document-private-items ``` diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs index 20f8f718b9..40644d5e4f 100644 --- a/rust/qemu-api/build.rs +++ b/rust/qemu-api/build.rs @@ -2,22 +2,42 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::path::Path; +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use std::os::windows::fs::symlink_file; +use std::{env, fs::remove_file, io::Result, path::Path}; use version_check as rustc; -fn main() { - if !Path::new("src/bindings.rs").exists() { - panic!( - "No generated C bindings found! Either build them manually with bindgen or with meson \ - (`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson." - ); +fn main() -> Result<()> { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + let path = env::var("MESON_BUILD_ROOT") + .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR"))); + + let file = format!("{}/bindings.inc.rs", path); + let file = Path::new(&file); + if !Path::new(&file).exists() { + panic!(concat!( + "No generated C bindings found! If you want to run `cargo`, start a subshell\n", + "with `meson devenv`, or point MESON_BUILD_ROOT to the top of the build tree." + )); } + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = format!("{}/bindings.inc.rs", out_dir); + let dest_path = Path::new(&dest_path); + if dest_path.symlink_metadata().is_ok() { + remove_file(dest_path)?; + } + symlink_file(file, dest_path)?; + // Check for available rustc features if rustc::is_min_version("1.77.0").unwrap_or(false) { println!("cargo:rustc-cfg=has_offset_of"); } println!("cargo:rerun-if-changed=build.rs"); + Ok(()) } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index cad9ac4844..3be7b7e5ce 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -9,6 +9,7 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', + 'src/bindings.rs', 'src/c_str.rs', 'src/definitions.rs', 'src/device_class.rs', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs new file mode 100644 index 0000000000..0b76ec58be --- /dev/null +++ b/rust/qemu-api/src/bindings.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + clippy::missing_const_for_fn, + clippy::too_many_arguments, + clippy::approx_constant, + clippy::use_self, + clippy::useless_transmute, + clippy::missing_safety_doc +)] + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +unsafe impl Send for Property {} +unsafe impl Sync for Property {} +unsafe impl Sync for TypeInfo {} +unsafe impl Sync for VMStateDescription {} +unsafe impl Sync for VMStateField {} +unsafe impl Sync for VMStateInfo {} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index aa8d16ec94..440aff3817 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -4,31 +4,9 @@ #![cfg_attr(not(MESON), doc = include_str!("../README.md"))] -#[allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unsafe_op_in_unsafe_fn, - clippy::missing_const_for_fn, - clippy::too_many_arguments, - clippy::approx_constant, - clippy::use_self, - clippy::useless_transmute, - clippy::missing_safety_doc, -)] #[rustfmt::skip] pub mod bindings; -unsafe impl Send for bindings::Property {} -unsafe impl Sync for bindings::Property {} -unsafe impl Sync for bindings::TypeInfo {} -unsafe impl Sync for bindings::VMStateDescription {} -unsafe impl Sync for bindings::VMStateField {} -unsafe impl Sync for bindings::VMStateInfo {} - pub mod c_str; pub mod definitions; pub mod device_class; From f3a6e9bc475504152e45e5fbf86c756157438d08 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 12 Nov 2024 11:54:11 +0100 Subject: [PATCH 0006/2892] rust: build: move rustc_args.py invocation to qemu-api crate Only qemu-api needs access to the symbols in config-host.h. Remove the temptation to use them elsewhere by limiting the --cfg arguments to the qemu-api crate. Per-crate invocation of the script will also be needed to add --check-cfg options for each crate's features (when more complex, build-time configurable devices are added in the future). Signed-off-by: Paolo Bonzini --- meson.build | 56 +++++++++++++++++---------------------- rust/qemu-api/meson.build | 5 +++- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/meson.build b/meson.build index ea211c2dbb..2f1f2aece4 100644 --- a/meson.build +++ b/meson.build @@ -120,7 +120,30 @@ if have_rust endif if have_rust + rustc_args = find_program('scripts/rust/rustc_args.py') rustfmt = find_program('rustfmt', required: false) + + # Prohibit code that is forbidden in Rust 2024 + rustc_lint_args = ['-D', 'unsafe_op_in_unsafe_fn'] + + # Occasionally, we may need to silence warnings and clippy lints that + # were only introduced in newer Rust compiler versions. Do not croak + # in that case; a CI job with rust_strict_lints == true ensures that + # we do not have misspelled allow() attributes. + if not get_option('strict_rust_lints') + rustc_lint_args += ['-A', 'unknown_lints'] + endif + + # Apart from procedural macros, our Rust executables will often link + # with C code, so include all the libraries that C code needs. This + # is safe; https://github.com/rust-lang/rust/pull/54675 says that + # passing -nodefaultlibs to the linker "was more ideological to + # start with than anything". + add_project_arguments(rustc_lint_args + + ['--cfg', 'MESON', '-C', 'default-linker-libraries'], + native: false, language: 'rust') + add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], + native: true, language: 'rust') endif dtrace = not_found @@ -3399,37 +3422,8 @@ endif # Generated sources # ##################### -genh += configure_file(output: 'config-host.h', configuration: config_host_data) - -if have_rust - rustc_args = run_command( - find_program('scripts/rust/rustc_args.py'), - '--config-headers', meson.project_build_root() / 'config-host.h', - capture : true, - check: true).stdout().strip().split() - - # Prohibit code that is forbidden in Rust 2024 - rustc_args += ['-D', 'unsafe_op_in_unsafe_fn'] - - # Occasionally, we may need to silence warnings and clippy lints that - # were only introduced in newer Rust compiler versions. Do not croak - # in that case; a CI job with rust_strict_lints == true ensures that - # we do not have misspelled allow() attributes. - if not get_option('strict_rust_lints') - rustc_args += ['-A', 'unknown_lints'] - endif - - # Apart from procedural macros, our Rust executables will often link - # with C code, so include all the libraries that C code needs. This - # is safe; https://github.com/rust-lang/rust/pull/54675 says that - # passing -nodefaultlibs to the linker "was more ideological to - # start with than anything". - add_project_arguments(rustc_args + - ['--cfg', 'MESON', '-C', 'default-linker-libraries'], - native: false, language: 'rust') - add_project_arguments(rustc_args + ['--cfg', 'MESON'], - native: true, language: 'rust') -endif +config_host_h = configure_file(output: 'config-host.h', configuration: config_host_data) +genh += config_host_h hxtool = find_program('scripts/hxtool') shaderinclude = find_program('scripts/shaderinclude.py') diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 3be7b7e5ce..5df6b35bf8 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -1,4 +1,7 @@ -_qemu_api_cfg = [] +_qemu_api_cfg = run_command(rustc_args, + '--config-headers', config_host_h, + capture: true, check: true).stdout().strip().split() + # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] From 1de82059aa097340d0ffde9140d338cddef478e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 11:25:55 +0100 Subject: [PATCH 0007/2892] rust: build: restrict --cfg generation to only required symbols Parse the Cargo.toml file, looking for the unexpected_cfgs configuration. When generating --cfg options from the config-host.h file, only use those that are included in the configuration. Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 2 +- scripts/rust/rustc_args.py | 61 ++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 5df6b35bf8..2ff6d2ce3d 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -1,5 +1,5 @@ _qemu_api_cfg = run_command(rustc_args, - '--config-headers', config_host_h, + '--config-headers', config_host_h, files('Cargo.toml'), capture: true, check: true).stdout().strip().split() # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index e4cc9720e1..942dd2b2ba 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -26,30 +26,51 @@ along with this program. If not, see . import argparse import logging +from pathlib import Path +from typing import Any, Iterable, Mapping, Optional, Set -from typing import List +try: + import tomllib +except ImportError: + import tomli as tomllib -def generate_cfg_flags(header: str) -> List[str]: +class CargoTOML: + tomldata: Mapping[Any, Any] + check_cfg: Set[str] + + def __init__(self, path: str): + with open(path, 'rb') as f: + self.tomldata = tomllib.load(f) + + self.check_cfg = set(self.find_check_cfg()) + + def find_check_cfg(self) -> Iterable[str]: + toml_lints = self.lints + rust_lints = toml_lints.get("rust", {}) + cfg_lint = rust_lints.get("unexpected_cfgs", {}) + return cfg_lint.get("check-cfg", []) + + @property + def lints(self) -> Mapping[Any, Any]: + return self.get_table("lints") + + def get_table(self, key: str) -> Mapping[Any, Any]: + table = self.tomldata.get(key, {}) + + return table + + +def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: """Converts defines from config[..].h headers to rustc --cfg flags.""" - def cfg_name(name: str) -> str: - """Filter function for C #defines""" - if ( - name.startswith("CONFIG_") - or name.startswith("TARGET_") - or name.startswith("HAVE_") - ): - return name - return "" - with open(header, encoding="utf-8") as cfg: config = [l.split()[1:] for l in cfg if l.startswith("#define")] cfg_list = [] for cfg in config: - name = cfg_name(cfg[0]) - if not name: + name = cfg[0] + if f'cfg({name})' not in cargo_toml.check_cfg: continue if len(cfg) >= 2 and cfg[1] != "1": continue @@ -59,7 +80,6 @@ def generate_cfg_flags(header: str) -> List[str]: def main() -> None: - # pylint: disable=missing-function-docstring parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", action="store_true") parser.add_argument( @@ -71,12 +91,21 @@ def main() -> None: required=False, default=[], ) + parser.add_argument( + metavar="TOML_FILE", + action="store", + dest="cargo_toml", + help="path to Cargo.toml file", + ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) logging.debug("args: %s", args) + + cargo_toml = CargoTOML(args.cargo_toml) + for header in args.config_headers: - for tok in generate_cfg_flags(header): + for tok in generate_cfg_flags(header, cargo_toml): print(tok) From 97ed1e9c8e2b0744508ef61cac8a23bb1e107820 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Nov 2024 10:02:15 +0100 Subject: [PATCH 0008/2892] rust: build: generate lint flags from Cargo.toml Cargo.toml makes it possible to describe the desired lint level settings in a nice format. We can extend this to Meson-built crates, by teaching rustc_args.py to fetch lint and --check-cfg arguments from Cargo.toml. --check-cfg arguments come from the unexpected_cfgs lint as well as crate features Start with qemu-api, since it already has a [lints.rust] table and an invocation of rustc_args.py. Signed-off-by: Paolo Bonzini --- meson.build | 3 +- rust/qemu-api/meson.build | 4 +- scripts/rust/rustc_args.py | 83 +++++++++++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 2f1f2aece4..1e1d8f5cd6 100644 --- a/meson.build +++ b/meson.build @@ -120,7 +120,8 @@ if have_rust endif if have_rust - rustc_args = find_program('scripts/rust/rustc_args.py') + rustc_args = [find_program('scripts/rust/rustc_args.py'), + '--rustc-version', rustc.version()] rustfmt = find_program('rustfmt', required: false) # Prohibit code that is forbidden in Rust 2024 diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2ff6d2ce3d..1ed79672cc 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -1,6 +1,6 @@ _qemu_api_cfg = run_command(rustc_args, - '--config-headers', config_host_h, files('Cargo.toml'), - capture: true, check: true).stdout().strip().split() + '--config-headers', config_host_h, '--features', '--lints', files('Cargo.toml'), + capture: true, check: true).stdout().strip().splitlines() # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if rustc.version().version_compare('>=1.77.0') diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 942dd2b2ba..9b9778a1ca 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -25,9 +25,10 @@ along with this program. If not, see . """ import argparse +from dataclasses import dataclass import logging from pathlib import Path -from typing import Any, Iterable, Mapping, Optional, Set +from typing import Any, Iterable, List, Mapping, Optional, Set try: import tomllib @@ -61,6 +62,45 @@ class CargoTOML: return table +@dataclass +class LintFlag: + flags: List[str] + priority: int + + +def generate_lint_flags(cargo_toml: CargoTOML) -> Iterable[str]: + """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" + + toml_lints = cargo_toml.lints + + lint_list = [] + for k, v in toml_lints.items(): + prefix = "" if k == "rust" else k + "::" + for lint, data in v.items(): + level = data if isinstance(data, str) else data["level"] + priority = 0 if isinstance(data, str) else data.get("priority", 0) + if level == "deny": + flag = "-D" + elif level == "allow": + flag = "-A" + elif level == "warn": + flag = "-W" + elif level == "forbid": + flag = "-F" + else: + raise Exception(f"invalid level {level} for {prefix}{lint}") + + # This may change if QEMU ever invokes clippy-driver or rustdoc by + # hand. For now, check the syntax but do not add non-rustc lints to + # the command line. + if k == "rust": + lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) + + lint_list.sort(key=lambda x: x.priority) + for lint in lint_list: + yield from lint.flags + + def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: """Converts defines from config[..].h headers to rustc --cfg flags.""" @@ -97,13 +137,54 @@ def main() -> None: dest="cargo_toml", help="path to Cargo.toml file", ) + parser.add_argument( + "--features", + action="store_true", + dest="features", + help="generate --check-cfg arguments for features", + required=False, + default=None, + ) + parser.add_argument( + "--lints", + action="store_true", + dest="lints", + help="generate arguments from [lints] table", + required=False, + default=None, + ) + parser.add_argument( + "--rustc-version", + metavar="VERSION", + dest="rustc_version", + action="store", + help="version of rustc", + required=False, + default="1.0.0", + ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) logging.debug("args: %s", args) + rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) cargo_toml = CargoTOML(args.cargo_toml) + if args.lints: + for tok in generate_lint_flags(cargo_toml): + print(tok) + + if rustc_version >= (1, 80): + if args.lints: + for cfg in sorted(cargo_toml.check_cfg): + print("--check-cfg") + print(cfg) + if args.features: + for feature in cargo_toml.get_table("features"): + if feature != "default": + print("--check-cfg") + print(f'cfg(feature,values("{feature}"))') + for header in args.config_headers: for tok in generate_cfg_flags(header, cargo_toml): print(tok) From 90868c3dcec755f567426d1fad64e7611053778e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 13:03:45 +0100 Subject: [PATCH 0009/2892] rust: cargo: store desired warning levels in workspace Cargo.toml An extra benefit of workspaces is that they allow to place lint level settings in a single Cargo.toml; the settings are then inherited by packages in the workspace. Correspondingly, teach rustc_args.py to get the unexpected_cfgs configuration from the workspace Cargo.toml. Note that it is still possible to allow or deny warnings per crate or module, via the #![] attribute syntax. The rust/qemu-api/src/bindings.rs file is an example. Signed-off-by: Paolo Bonzini --- meson.build | 7 +++--- rust/Cargo.toml | 8 +++++++ rust/hw/char/pl011/Cargo.toml | 3 +++ rust/qemu-api-macros/Cargo.toml | 3 +++ rust/qemu-api/Cargo.toml | 5 ++--- rust/qemu-api/meson.build | 2 +- scripts/rust/rustc_args.py | 38 +++++++++++++++++++++++++++------ 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/meson.build b/meson.build index 1e1d8f5cd6..218ae441e3 100644 --- a/meson.build +++ b/meson.build @@ -121,11 +121,12 @@ endif if have_rust rustc_args = [find_program('scripts/rust/rustc_args.py'), - '--rustc-version', rustc.version()] + '--rustc-version', rustc.version(), + '--workspace', meson.project_source_root() / 'rust'] rustfmt = find_program('rustfmt', required: false) - # Prohibit code that is forbidden in Rust 2024 - rustc_lint_args = ['-D', 'unsafe_op_in_unsafe_fn'] + rustc_lint_args = run_command(rustc_args, '--lints', + capture: true, check: true).stdout().strip().splitlines() # Occasionally, we may need to silence warnings and clippy lints that # were only introduced in newer Rust compiler versions. Do not croak diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0c94d5037d..4bb52bf0bd 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -5,3 +5,11 @@ members = [ "qemu-api", "hw/char/pl011", ] + +[workspace.lints.rust] +unexpected_cfgs = { level = "deny", check-cfg = [ + 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', + 'cfg(has_offset_of)'] } + +# Prohibit code that is forbidden in Rust 2024 +unsafe_op_in_unsafe_fn = "deny" diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index a373906b9f..58f3e859f7 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -21,3 +21,6 @@ bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-api-macros/Cargo.toml index a8f7377106..5a27b52ee6 100644 --- a/rust/qemu-api-macros/Cargo.toml +++ b/rust/qemu-api-macros/Cargo.toml @@ -20,3 +20,6 @@ proc-macro = true proc-macro2 = "1" quote = "1" syn = { version = "2", features = ["extra-traits"] } + +[lints] +workspace = true diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index cc716d75d4..669f288d1c 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -23,6 +23,5 @@ version_check = "~0.9" default = [] allocator = [] -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', - 'cfg(has_offset_of)'] } +[lints] +workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 1ed79672cc..d719c13f46 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -1,5 +1,5 @@ _qemu_api_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', '--lints', files('Cargo.toml'), + '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 9b9778a1ca..9df131a02b 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -38,11 +38,21 @@ except ImportError: class CargoTOML: tomldata: Mapping[Any, Any] + workspace_data: Mapping[Any, Any] check_cfg: Set[str] - def __init__(self, path: str): - with open(path, 'rb') as f: - self.tomldata = tomllib.load(f) + def __init__(self, path: Optional[str], workspace: Optional[str]): + if path is not None: + with open(path, 'rb') as f: + self.tomldata = tomllib.load(f) + else: + self.tomldata = {"lints": {"workspace": True}} + + if workspace is not None: + with open(workspace, 'rb') as f: + self.workspace_data = tomllib.load(f) + if "workspace" not in self.workspace_data: + self.workspace_data["workspace"] = {} self.check_cfg = set(self.find_check_cfg()) @@ -54,10 +64,12 @@ class CargoTOML: @property def lints(self) -> Mapping[Any, Any]: - return self.get_table("lints") + return self.get_table("lints", True) - def get_table(self, key: str) -> Mapping[Any, Any]: + def get_table(self, key: str, can_be_workspace: bool = False) -> Mapping[Any, Any]: table = self.tomldata.get(key, {}) + if can_be_workspace and table.get("workspace", False) is True: + table = self.workspace_data["workspace"].get(key, {}) return table @@ -136,6 +148,16 @@ def main() -> None: action="store", dest="cargo_toml", help="path to Cargo.toml file", + nargs='?', + ) + parser.add_argument( + "--workspace", + metavar="DIR", + action="store", + dest="workspace", + help="path to root of the workspace", + required=False, + default=None, ) parser.add_argument( "--features", @@ -168,7 +190,11 @@ def main() -> None: logging.debug("args: %s", args) rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) - cargo_toml = CargoTOML(args.cargo_toml) + if args.workspace: + workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve() + cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml)) + else: + cargo_toml = CargoTOML(args.cargo_toml, None) if args.lints: for tok in generate_lint_flags(cargo_toml): From de98c17593218e1c713c6e5d8ad7242c17d90e7e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Nov 2024 10:14:49 +0100 Subject: [PATCH 0010/2892] rust: build: move strict lints handling to rustc_args.py Make Cargo use unknown_lints = "allow" as well. This is more future proof as we might add new lints to rust/Cargo.toml that are not supported by older versions of rustc or clippy. Signed-off-by: Paolo Bonzini --- meson.build | 12 ++++-------- rust/Cargo.toml | 6 ++++++ scripts/rust/rustc_args.py | 19 ++++++++++++++++--- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/meson.build b/meson.build index 218ae441e3..85f7485473 100644 --- a/meson.build +++ b/meson.build @@ -123,19 +123,15 @@ if have_rust rustc_args = [find_program('scripts/rust/rustc_args.py'), '--rustc-version', rustc.version(), '--workspace', meson.project_source_root() / 'rust'] + if get_option('strict_rust_lints') + rustc_args += ['--strict-lints'] + endif + rustfmt = find_program('rustfmt', required: false) rustc_lint_args = run_command(rustc_args, '--lints', capture: true, check: true).stdout().strip().splitlines() - # Occasionally, we may need to silence warnings and clippy lints that - # were only introduced in newer Rust compiler versions. Do not croak - # in that case; a CI job with rust_strict_lints == true ensures that - # we do not have misspelled allow() attributes. - if not get_option('strict_rust_lints') - rustc_lint_args += ['-A', 'unknown_lints'] - endif - # Apart from procedural macros, our Rust executables will often link # with C code, so include all the libraries that C code needs. This # is safe; https://github.com/rust-lang/rust/pull/54675 says that diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 4bb52bf0bd..358c517bc5 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -11,5 +11,11 @@ unexpected_cfgs = { level = "deny", check-cfg = [ 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', 'cfg(has_offset_of)'] } +# Occasionally, we may need to silence warnings and clippy lints that +# were only introduced in newer Rust compiler versions. Do not croak +# in that case; a CI job with rust_strict_lints == true disables this +# and ensures that we do not have misspelled allow() attributes. +unknown_lints = "allow" + # Prohibit code that is forbidden in Rust 2024 unsafe_op_in_unsafe_fn = "deny" diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 9df131a02b..5525b3886f 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -35,6 +35,8 @@ try: except ImportError: import tomli as tomllib +STRICT_LINTS = {"unknown_lints", "warnings"} + class CargoTOML: tomldata: Mapping[Any, Any] @@ -80,7 +82,7 @@ class LintFlag: priority: int -def generate_lint_flags(cargo_toml: CargoTOML) -> Iterable[str]: +def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[str]: """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" toml_lints = cargo_toml.lints @@ -105,9 +107,13 @@ def generate_lint_flags(cargo_toml: CargoTOML) -> Iterable[str]: # This may change if QEMU ever invokes clippy-driver or rustdoc by # hand. For now, check the syntax but do not add non-rustc lints to # the command line. - if k == "rust": + if k == "rust" and not (strict_lints and lint in STRICT_LINTS): lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) + if strict_lints: + for lint in STRICT_LINTS: + lint_list.append(LintFlag(flags=["-D", lint], priority=1000000)) + lint_list.sort(key=lambda x: x.priority) for lint in lint_list: yield from lint.flags @@ -184,6 +190,13 @@ def main() -> None: required=False, default="1.0.0", ) + parser.add_argument( + "--strict-lints", + action="store_true", + dest="strict_lints", + help="apply stricter checks (for nightly Rust)", + default=False, + ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) @@ -197,7 +210,7 @@ def main() -> None: cargo_toml = CargoTOML(args.cargo_toml, None) if args.lints: - for tok in generate_lint_flags(cargo_toml): + for tok in generate_lint_flags(cargo_toml, args.strict_lints): print(tok) if rustc_version >= (1, 80): From 7a35e2fb80df4c536cc808e498a259d96d0a804e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Nov 2024 18:03:12 +0100 Subject: [PATCH 0011/2892] rust: fix a couple style issues from clippy These are reported as clippy::semicolon_inside_block and clippy::as_ptr_cast_mut. clippy::semicolon_inside_block can be configured not to lint single-line blocks; just go with the default. Reviewed-by: Junjie Mao Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 8 +++++--- rust/hw/char/pl011/src/memory_ops.rs | 4 +++- rust/qemu-api/tests/tests.rs | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 476cacc844..317a9b3c5a 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,7 +2,7 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of, addr_of_mut, NonNull}; +use core::ptr::{addr_of_mut, NonNull}; use std::{ ffi::CStr, os::raw::{c_int, c_uchar, c_uint, c_void}, @@ -499,7 +499,9 @@ impl PL011State { let flags = self.int_level & self.int_enabled; for (irq, i) in self.interrupts.iter().zip(IRQMASK) { // SAFETY: self.interrupts have been initialized in init(). - unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) }; + unsafe { + qemu_set_irq(*irq, i32::from(flags & i != 0)); + } } } @@ -601,7 +603,7 @@ pub unsafe extern "C" fn pl011_create( let sysbus: *mut SysBusDevice = dev.cast::(); qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); - sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error); + sysbus_realize_and_unref(sysbus, addr_of_mut!(error_fatal)); sysbus_mmio_map(sysbus, 0, addr); sysbus_connect_irq(sysbus, 0, irq); dev diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs index 169d485a4d..c4e8599ba4 100644 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -33,7 +33,9 @@ unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) // SAFETY: self.char_backend is a valid CharBackend instance after it's been // initialized in realize(). let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; - unsafe { qemu_chr_fe_accept_input(cb_ptr) }; + unsafe { + qemu_chr_fe_accept_input(cb_ptr); + } val } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 43a4827de1..925f5a3c77 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -74,6 +74,6 @@ fn test_device_decl_macros() { unsafe { module_call_init(module_init_type::MODULE_INIT_QOM); - object_unref(object_new(DummyState::TYPE_NAME.as_ptr()) as *mut _); + object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); } } From 2f9eec8f72673f97766eba1682a75f06f16302cb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 11:42:00 +0100 Subject: [PATCH 0012/2892] rust: build: establish a baseline of lints across all crates Many lints that default to allow can be helpful in detecting bugs or keeping the code style homogeneous. Add them liberally, though perhaps not as liberally as in hw/char/pl011/src/lib.rs. In particular, enabling entire groups can be problematic because of bitrot when new links are added in the future. For Clippy, this is actually a feature that is only present in Cargo 1.74.0 but, since we are not using Cargo to *build* QEMU, only developers will need a new-enough cargo and only to run tools such as clippy. The requirement does not apply to distros that are building QEMU. Reviewed-by: Junjie Mao Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 68 +++++++++++++++++++++++++++++++++++ rust/hw/char/pl011/src/lib.rs | 19 ++-------- rust/qemu-api/src/bindings.rs | 6 ++-- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 358c517bc5..6ec19b6729 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -19,3 +19,71 @@ unknown_lints = "allow" # Prohibit code that is forbidden in Rust 2024 unsafe_op_in_unsafe_fn = "deny" + +[workspace.lints.rustdoc] +private_intra_doc_links = "allow" + +broken_intra_doc_links = "deny" +invalid_html_tags = "deny" +invalid_rust_codeblocks = "deny" +bare_urls = "deny" +unescaped_backticks = "deny" +redundant_explicit_links = "deny" + +[workspace.lints.clippy] +# default-warn lints +result_unit_err = "allow" +should_implement_trait = "deny" +# can be for a reason, e.g. in callbacks +unused_self = "allow" + +# default-allow lints +as_underscore = "deny" +assertions_on_result_states = "deny" +bool_to_int_with_if = "deny" +borrow_as_ptr = "deny" +cast_lossless = "deny" +dbg_macro = "deny" +debug_assert_with_mut_call = "deny" +derive_partial_eq_without_eq = "deny" +doc_markdown = "deny" +empty_structs_with_brackets = "deny" +ignored_unit_patterns = "deny" +implicit_clone = "deny" +macro_use_imports = "deny" +missing_const_for_fn = "deny" +missing_safety_doc = "deny" +multiple_crate_versions = "deny" +mut_mut = "deny" +needless_bitwise_bool = "deny" +needless_pass_by_ref_mut = "deny" +no_effect_underscore_binding = "deny" +option_option = "deny" +or_fun_call = "deny" +ptr_as_ptr = "deny" +pub_underscore_fields = "deny" +redundant_clone = "deny" +redundant_closure_for_method_calls = "deny" +redundant_else = "deny" +redundant_pub_crate = "deny" +ref_binding_to_reference = "deny" +ref_option_ref = "deny" +return_self_not_must_use = "deny" +same_name_method = "deny" +semicolon_inside_block = "deny" +shadow_unrelated = "deny" +significant_drop_in_scrutinee = "deny" +significant_drop_tightening = "deny" +suspicious_operation_groupings = "deny" +transmute_ptr_to_ptr = "deny" +transmute_undefined_repr = "deny" +type_repetition_in_bounds = "deny" +used_underscore_binding = "deny" + +# nice to have, but cannot be enabled yet +#wildcard_imports = "deny" # still have many bindings::* imports +#ptr_cast_constness = "deny" # needs 1.65.0 for cast_mut()/cast_const() + +# these may have false positives +#option_if_let_else = "deny" +cognitive_complexity = "deny" diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index cd0a49acb9..4dc0e8f345 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -14,28 +14,15 @@ //! the [`registers`] module for register types. #![deny( - rustdoc::broken_intra_doc_links, - rustdoc::redundant_explicit_links, clippy::correctness, clippy::suspicious, clippy::complexity, clippy::perf, clippy::cargo, clippy::nursery, - clippy::style, - // restriction group - clippy::dbg_macro, - clippy::as_underscore, - clippy::assertions_on_result_states, - // pedantic group - clippy::doc_markdown, - clippy::borrow_as_ptr, - clippy::cast_lossless, - clippy::option_if_let_else, - clippy::missing_const_for_fn, - clippy::cognitive_complexity, - clippy::missing_safety_doc, - )] + clippy::style +)] +#![allow(clippy::upper_case_acronyms)] #![allow(clippy::result_unit_err)] extern crate bilge; diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 0b76ec58be..8a9b821bb9 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -7,10 +7,10 @@ non_snake_case, non_upper_case_globals, unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, clippy::missing_const_for_fn, - clippy::too_many_arguments, - clippy::approx_constant, - clippy::use_self, clippy::useless_transmute, clippy::missing_safety_doc )] From cab1d0bceb6d042674f6892216d1769021f73916 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Nov 2024 09:11:48 +0100 Subject: [PATCH 0013/2892] rust: build: add "make clippy", "make rustfmt", "make rustdoc" Abstract common invocations of "cargo", that do not require copying the generated bindgen file or setting up MESON_BUILD_ROOT. In the future these could also do completely without cargo and invoke the underlying programs directly. Reviewed-by: Junjie Mao Signed-off-by: Paolo Bonzini --- rust/meson.build | 22 ++++++++++++++++++++++ rust/qemu-api/README.md | 10 ++++++---- rust/qemu-api/build.rs | 9 +++++++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/rust/meson.build b/rust/meson.build index def77389cd..91e52b8fb8 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -2,3 +2,25 @@ subdir('qemu-api-macros') subdir('qemu-api') subdir('hw') + +cargo = find_program('cargo', required: false) + +if cargo.found() + run_target('clippy', + command: [config_host['MESON'], 'devenv', + '--workdir', '@CURRENT_SOURCE_DIR@', + cargo, 'clippy', '--tests'], + depends: bindings_rs) + + run_target('rustfmt', + command: [config_host['MESON'], 'devenv', + '--workdir', '@CURRENT_SOURCE_DIR@', + cargo, 'fmt'], + depends: bindings_rs) + + run_target('rustdoc', + command: [config_host['MESON'], 'devenv', + '--workdir', '@CURRENT_SOURCE_DIR@', + cargo, 'doc', '--no-deps', '--document-private-items'], + depends: bindings_rs) +endif diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md index 53810f4888..ed1b7ab263 100644 --- a/rust/qemu-api/README.md +++ b/rust/qemu-api/README.md @@ -5,13 +5,15 @@ This library exports helper Rust types, Rust macros and C FFI bindings for inter The C bindings can be generated with `bindgen`, using this build target: ```console -$ ninja bindings.inc.rs +$ make bindings.inc.rs ``` ## Generate Rust documentation -To generate docs for this crate, including private items: +Common Cargo tasks can be performed from the QEMU build directory -```sh -pyvenv/bin/meson devenv -w ../rust cargo doc --no-deps --document-private-items +```console +$ make clippy +$ make rustfmt +$ make rustdoc ``` diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs index 40644d5e4f..471e6c633d 100644 --- a/rust/qemu-api/build.rs +++ b/rust/qemu-api/build.rs @@ -20,8 +20,13 @@ fn main() -> Result<()> { let file = Path::new(&file); if !Path::new(&file).exists() { panic!(concat!( - "No generated C bindings found! If you want to run `cargo`, start a subshell\n", - "with `meson devenv`, or point MESON_BUILD_ROOT to the top of the build tree." + "\n", + " No generated C bindings found! Maybe you wanted one of\n", + " `make clippy`, `make rustfmt`, `make rustdoc`?\n", + "\n", + " For other uses of `cargo`, start a subshell with\n", + " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", + " the top of the build tree." )); } From 4e0a11d12fd4439b2f72e989a647fcf3a18b0f69 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Oct 2024 15:10:29 +0100 Subject: [PATCH 0014/2892] rust: ci: add job that runs Rust tools Code checks, as well as documentation generation, are not yet tied to "make check" because they need new version of the Rust toolchain (even nightly in the case of "rustfmt"). Run them in CI using the existing nightly-Rust container. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/static_checks.yml | 24 +++++++++++++++++++ .../dockerfiles/fedora-rust-nightly.docker | 4 ++++ tests/lcitool/refresh | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index ad9f426a52..c0ba453382 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -46,3 +46,27 @@ check-python-tox: QEMU_JOB_OPTIONAL: 1 needs: job: python-container + +check-rust-tools-nightly: + extends: .base_job_template + stage: test + image: $CI_REGISTRY_IMAGE/qemu/fedora-rust-nightly:$QEMU_CI_CONTAINER_TAG + script: + - source scripts/ci/gitlab-ci-section + - section_start test "Running Rust code checks" + - cd build + - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} fmt --check + - make clippy + - make rustdoc + - section_end test + variables: + GIT_DEPTH: 1 + allow_failure: true + needs: + - job: build-system-fedora-rust-nightly + artifacts: true + artifacts: + when: on_success + expire_in: 2 days + paths: + - rust/target/doc diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index 9180c8b522..a8e4fb279a 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -155,6 +155,7 @@ ENV PYTHON "/usr/bin/python3" RUN dnf install -y wget ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc +ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo RUN set -eux && \ rustArch='x86_64-unknown-linux-gnu' && \ rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \ @@ -165,10 +166,13 @@ RUN set -eux && \ ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \ chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \ /usr/local/cargo/bin/rustup --version && \ + /usr/local/cargo/bin/rustup run nightly cargo --version && \ /usr/local/cargo/bin/rustup run nightly rustc --version && \ + test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \ test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)" ENV PATH=$CARGO_HOME/bin:$PATH RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli +RUN $CARGO --list # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 51012783c0..6720516b94 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -121,6 +121,7 @@ fedora_rustup_nightly_extras = [ "RUN dnf install -y wget\n", "ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n", "ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n", + "ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\n", "RUN set -eux && \\\n", " rustArch='x86_64-unknown-linux-gnu' && \\\n", " rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \\\n", @@ -131,10 +132,13 @@ fedora_rustup_nightly_extras = [ " ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \\\n", " chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \\\n", " /usr/local/cargo/bin/rustup --version && \\\n", + " /usr/local/cargo/bin/rustup run nightly cargo --version && \\\n", " /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n", + ' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n', ' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n', 'ENV PATH=$CARGO_HOME/bin:$PATH\n', 'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n', + 'RUN $CARGO --list\n', ] ubuntu2204_bindgen_extras = [ From 8a88b55f698ad660a11d24925cedcff106053ff4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Nov 2024 18:44:56 +0100 Subject: [PATCH 0015/2892] rust: fix doc test syntax Allow "cargo test --doc" to pass. Reviewed-by: Junjie Mao Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/zeroable.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 13cdb2ccba..6125aeed8b 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -7,9 +7,9 @@ use std::ptr; /// behavior. This trait in principle could be implemented as just: /// /// ``` -/// const ZERO: Self = unsafe { -/// ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init() -/// }, +/// pub unsafe trait Zeroable: Default { +/// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; +/// } /// ``` /// /// The need for a manual implementation is only because `zeroed()` cannot From a3057c52f45c280bffc22980f535799a12500d66 Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Thu, 17 Oct 2024 22:32:44 +0800 Subject: [PATCH 0016/2892] rust/qemu-api: Fix fragment-specifiers in define_property macro For the matcher of macro, "expr" is used for expressions, while "ident" is used for variable/function names, and "ty" matches types. In define_property macro, $field is a member name of type $state, so it should be defined as "ident", though offset_of! doesn't complain about this. $type is the type of $field, since it is not used in the macro, so that no type mismatch error is triggered either. Fix fragment-specifiers of $field and $type. Signed-off-by: Junjie Mao Co-developed-by: Zhao Liu Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20241017143245.1248589-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/device_class.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index 0ba798d3e3..922bbce1bb 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -27,7 +27,7 @@ macro_rules! device_class_init { #[macro_export] macro_rules! define_property { - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { $crate::bindings::Property { // use associated function syntax for type checking name: ::std::ffi::CStr::as_ptr($name), @@ -38,7 +38,7 @@ macro_rules! define_property { ..$crate::zeroable::Zeroable::ZERO } }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { $crate::bindings::Property { // use associated function syntax for type checking name: ::std::ffi::CStr::as_ptr($name), From 4cc055039fc1c1ab18f172fa791aa3d48d7d20aa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 Nov 2024 13:21:13 +0100 Subject: [PATCH 0017/2892] clock: clear callback on unparent Signed-off-by: Paolo Bonzini --- hw/core/clock.c | 22 +++++++++++++++++----- hw/core/qdev-clock.c | 5 +---- include/hw/clock.h | 8 -------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/hw/core/clock.c b/hw/core/clock.c index cbe7b1bc46..391095eb4e 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -44,16 +44,12 @@ Clock *clock_new(Object *parent, const char *name) void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, unsigned int events) { + assert(OBJECT(clk)->parent); clk->callback = cb; clk->callback_opaque = opaque; clk->callback_events = events; } -void clock_clear_callback(Clock *clk) -{ - clock_set_callback(clk, NULL, NULL, 0); -} - bool clock_set(Clock *clk, uint64_t period) { if (clk->period == period) { @@ -168,6 +164,16 @@ static void clock_period_prop_get(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &period, errp); } +static void clock_unparent(Object *obj) +{ + /* + * Callback are registered by the parent, which might die anytime after + * it's unparented the children. Avoid having a callback to a deleted + * object in case the clock is still referenced somewhere else (eg: by + * a clock output). + */ + clock_set_callback(CLOCK(obj), NULL, NULL, 0); +} static void clock_initfn(Object *obj) { @@ -200,11 +206,17 @@ static void clock_finalizefn(Object *obj) g_free(clk->canonical_path); } +static void clock_class_init(ObjectClass *klass, void *data) +{ + klass->unparent = clock_unparent; +} + static const TypeInfo clock_info = { .name = TYPE_CLOCK, .parent = TYPE_OBJECT, .instance_size = sizeof(Clock), .instance_init = clock_initfn, + .class_init = clock_class_init, .instance_finalize = clock_finalizefn, }; diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c index 82799577f3..ca65685c04 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -87,11 +87,8 @@ void qdev_finalize_clocklist(DeviceState *dev) if (!ncl->output && !ncl->alias) { /* * We kept a reference on the input clock to ensure it lives up to - * this point so we can safely remove the callback. - * It avoids having a callback to a deleted object if ncl->clock - * is still referenced somewhere else (eg: by a clock output). + * this point; it is used by the monitor to show the frequency. */ - clock_clear_callback(ncl->clock); object_unref(OBJECT(ncl->clock)); } g_free(ncl->name); diff --git a/include/hw/clock.h b/include/hw/clock.h index eb58599131..a279bd4ba5 100644 --- a/include/hw/clock.h +++ b/include/hw/clock.h @@ -141,14 +141,6 @@ Clock *clock_new(Object *parent, const char *name); void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, unsigned int events); -/** - * clock_clear_callback: - * @clk: the clock to delete the callback from - * - * Unregister the callback registered with clock_set_callback. - */ -void clock_clear_callback(Clock *clk); - /** * clock_set_source: * @clk: the clock. From 118b46a461fb42ecbb13cbd0ca71167b128b050e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Nov 2024 07:47:01 +0100 Subject: [PATCH 0018/2892] clock: treat outputs and inputs the same in NamedClockList Signed-off-by: Paolo Bonzini --- hw/core/qdev-clock.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c index ca65685c04..2f9d6cb757 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -48,14 +48,6 @@ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, if (clk == NULL) { clk = CLOCK(object_new(TYPE_CLOCK)); object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - if (output) { - /* - * Remove object_new()'s initial reference. - * Note that for inputs, the reference created by object_new() - * will be deleted in qdev_finalize_clocklist(). - */ - object_unref(OBJECT(clk)); - } } else { object_property_add_link(OBJECT(dev), name, object_get_typename(OBJECT(clk)), @@ -84,7 +76,7 @@ void qdev_finalize_clocklist(DeviceState *dev) QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) { QLIST_REMOVE(ncl, node); - if (!ncl->output && !ncl->alias) { + if (!ncl->alias) { /* * We kept a reference on the input clock to ensure it lives up to * this point; it is used by the monitor to show the frequency. From 7d95502ced4220edb91efa4621054bbce237528a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 Nov 2024 18:30:39 +0100 Subject: [PATCH 0019/2892] clock: inline most of qdev_init_clocklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move object creation out of qdev_init_clocklist. The input/output cases are very simple, and the aliases are completely different. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/core/qdev-clock.c | 71 +++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c index 2f9d6cb757..dacafa4e03 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -22,7 +22,7 @@ * Add a new clock in a device */ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, - bool output, Clock *clk) + bool alias, bool output, Clock *clk) { NamedClockList *ncl; @@ -38,31 +38,8 @@ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, */ ncl = g_new0(NamedClockList, 1); ncl->name = g_strdup(name); + ncl->alias = alias; ncl->output = output; - ncl->alias = (clk != NULL); - - /* - * Trying to create a clock whose name clashes with some other - * clock or property is a bug in the caller and we will abort(). - */ - if (clk == NULL) { - clk = CLOCK(object_new(TYPE_CLOCK)); - object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - } else { - object_property_add_link(OBJECT(dev), name, - object_get_typename(OBJECT(clk)), - (Object **) &ncl->clock, - NULL, OBJ_PROP_LINK_STRONG); - /* - * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk - * object reference count gets decremented on property deletion. - * However object_property_add_link does not increment it since it - * doesn't know the linked object. Increment it here to ensure the - * aliased clock stays alive during this device life-time. - */ - object_ref(OBJECT(clk)); - } - ncl->clock = clk; QLIST_INSERT_HEAD(&dev->clocks, ncl, node); @@ -90,29 +67,25 @@ void qdev_finalize_clocklist(DeviceState *dev) Clock *qdev_init_clock_out(DeviceState *dev, const char *name) { - NamedClockList *ncl; + Clock *clk = CLOCK(object_new(TYPE_CLOCK)); + object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - assert(name); - - ncl = qdev_init_clocklist(dev, name, true, NULL); - - return ncl->clock; + qdev_init_clocklist(dev, name, false, true, clk); + return clk; } Clock *qdev_init_clock_in(DeviceState *dev, const char *name, ClockCallback *callback, void *opaque, unsigned int events) { - NamedClockList *ncl; - - assert(name); - - ncl = qdev_init_clocklist(dev, name, false, NULL); + Clock *clk = CLOCK(object_new(TYPE_CLOCK)); + object_property_add_child(OBJECT(dev), name, OBJECT(clk)); + qdev_init_clocklist(dev, name, false, false, clk); if (callback) { - clock_set_callback(ncl->clock, callback, opaque, events); + clock_set_callback(clk, callback, opaque, events); } - return ncl->clock; + return clk; } void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) @@ -183,15 +156,25 @@ Clock *qdev_get_clock_out(DeviceState *dev, const char *name) Clock *qdev_alias_clock(DeviceState *dev, const char *name, DeviceState *alias_dev, const char *alias_name) { - NamedClockList *ncl; + NamedClockList *ncl = qdev_get_clocklist(dev, name); + Clock *clk = ncl->clock; - assert(name && alias_name); + ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk); - ncl = qdev_get_clocklist(dev, name); + object_property_add_link(OBJECT(alias_dev), alias_name, + TYPE_CLOCK, + (Object **) &ncl->clock, + NULL, OBJ_PROP_LINK_STRONG); + /* + * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk + * object reference count gets decremented on property deletion. + * However object_property_add_link does not increment it since it + * doesn't know the linked object. Increment it here to ensure the + * aliased clock stays alive during this device life-time. + */ + object_ref(OBJECT(clk)); - qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock); - - return ncl->clock; + return clk; } void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) From feccfa77bed975e2e60c90756f08b8a56df6daf6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Nov 2024 19:07:10 +0100 Subject: [PATCH 0020/2892] kvm: remove unnecessary #ifdef Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm_i386.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 9de9c0d303..7edb154a16 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -13,8 +13,7 @@ #include "sysemu/kvm.h" -#ifdef CONFIG_KVM - +/* always false if !CONFIG_KVM */ #define kvm_pit_in_kernel() \ (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) #define kvm_pic_in_kernel() \ @@ -22,14 +21,6 @@ #define kvm_ioapic_in_kernel() \ (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) -#else - -#define kvm_pit_in_kernel() 0 -#define kvm_pic_in_kernel() 0 -#define kvm_ioapic_in_kernel() 0 - -#endif /* CONFIG_KVM */ - bool kvm_has_smm(void); bool kvm_enable_x2apic(void); bool kvm_hv_vpindex_settable(void); From f34562766867efe94e451e9ec3a2f19a70841e16 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:19 +0800 Subject: [PATCH 0021/2892] arm: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-2-zhao1.liu@intel.com --- hw/arm/armsse.c | 2 +- hw/arm/smmuv3.c | 4 ++-- target/arm/cpu.c | 2 +- target/arm/cpu64.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 255346a595..58ed504b2b 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -1731,7 +1731,7 @@ static void armsse_register_types(void) .class_init = armsse_class_init, .class_data = (void *)&armsse_variants[i], }; - type_register(&ti); + type_register_static(&ti); } } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 4c49b5a885..6e847e8773 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -2065,8 +2065,8 @@ static const TypeInfo smmuv3_iommu_memory_region_info = { static void smmuv3_register_types(void) { - type_register(&smmuv3_type_info); - type_register(&smmuv3_iommu_memory_region_info); + type_register_static(&smmuv3_type_info); + type_register_static(&smmuv3_iommu_memory_region_info); } type_init(smmuv3_register_types) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6938161b95..17519b5e46 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2755,7 +2755,7 @@ void arm_cpu_register(const ARMCPUInfo *info) }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); - type_register(&type_info); + type_register_static(&type_info); g_free((void *)type_info.name); } diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 458d1cee01..c1cac912a0 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -841,7 +841,7 @@ void aarch64_cpu_register(const ARMCPUInfo *info) }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); - type_register(&type_info); + type_register_static(&type_info); g_free((void *)type_info.name); } From 55398938b427aa430d70f576f8240fe5977a44a7 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:20 +0800 Subject: [PATCH 0022/2892] hw/block: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-3-zhao1.liu@intel.com --- hw/block/m25p80.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index e2e84f8b5f..748594524e 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1894,7 +1894,7 @@ static void m25p80_register_types(void) .class_init = m25p80_class_init, .class_data = (void *)&known_devices[i], }; - type_register(&ti); + type_register_static(&ti); } } From 29ec04f08933702f7916ce2ffd03e87aee203fb9 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:21 +0800 Subject: [PATCH 0023/2892] hw/net: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-4-zhao1.liu@intel.com --- hw/net/e1000.c | 2 +- hw/net/eepro100.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 5012b96464..ab72236d18 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1774,7 +1774,7 @@ static void e1000_register_types(void) type_info.class_data = (void *)info; type_info.class_init = e1000_class_init; - type_register(&type_info); + type_register_static(&type_info); } } diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index c8a88b9813..20b22d8e49 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -2102,7 +2102,7 @@ static void eepro100_register_types(void) { }, }; - type_register(&type_info); + type_register_static(&type_info); } } From 6e0a88837a74f6dc668c1838d2259a83deb7285b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:22 +0800 Subject: [PATCH 0024/2892] ppc: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-5-zhao1.liu@intel.com --- hw/ppc/spapr.c | 2 +- target/ppc/kvm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 0d4efaa0c0..827e7d6b14 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4723,7 +4723,7 @@ static void spapr_machine_latest_class_options(MachineClass *mc) static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \ { \ MACHINE_VER_DELETION(__VA_ARGS__); \ - type_register(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \ + type_register_static(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, spapr, __VA_ARGS__)) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 3efc28f18b..0d464824db 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2633,7 +2633,7 @@ static int kvm_ppc_register_host_cpu_type(void) return -1; } type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); - type_register(&type_info); + type_register_static(&type_info); /* override TCG default cpu type with 'host' cpu model */ object_class_foreach(pseries_machine_class_fixup, TYPE_SPAPR_MACHINE, false, NULL); From da2608b7b6370d49b15c348e2e66c54b0a9cc90f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:23 +0800 Subject: [PATCH 0025/2892] hw/rtc: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-6-zhao1.liu@intel.com --- hw/rtc/m48t59-isa.c | 2 +- hw/rtc/m48t59.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index 6e9723fdf1..b642b82680 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -161,7 +161,7 @@ static void m48t59_isa_register_types(void) for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) { isa_type_info.name = m48txx_isa_info[i].bus_name; isa_type_info.class_data = &m48txx_isa_info[i]; - type_register(&isa_type_info); + type_register_static(&isa_type_info); } } diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 48846d8df4..90299ea56f 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -679,7 +679,7 @@ static void m48t59_register_types(void) for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { sysbus_type_info.name = m48txx_sysbus_info[i].bus_name; sysbus_type_info.class_data = &m48txx_sysbus_info[i]; - type_register(&sysbus_type_info); + type_register_static(&sysbus_type_info); } } From dbeafa453baec78a9f77580f83177f4fdfad6852 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:24 +0800 Subject: [PATCH 0026/2892] hw/scsi: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because\ type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-7-zhao1.liu@intel.com --- hw/scsi/megasas.c | 2 +- hw/scsi/mptsas.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index b33229d71a..df58aeb995 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2576,7 +2576,7 @@ static void megasas_register_types(void) type_info.class_init = megasas_class_init; type_info.interfaces = info->interfaces; - type_register(&type_info); + type_register_static(&type_info); } } diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 361b75e633..c6bc3479e9 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1450,7 +1450,7 @@ static const TypeInfo mptsas_info = { static void mptsas_register_types(void) { - type_register(&mptsas_info); + type_register_static(&mptsas_info); } type_init(mptsas_register_types) From bead9801750b8cd6da257cbf38ec184d40e02f47 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:25 +0800 Subject: [PATCH 0027/2892] hw/sensor: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-8-zhao1.liu@intel.com --- hw/sensor/tmp421.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index b6f0b62ab1..82e604279c 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -384,7 +384,7 @@ static void tmp421_register_types(void) .class_init = tmp421_class_init, .class_data = (void *) &devices[i], }; - type_register(&ti); + type_register_static(&ti); } } From 220a81eb2f08c33eeb91deab4fd43bd2853313b9 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:26 +0800 Subject: [PATCH 0028/2892] hw/usb: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-9-zhao1.liu@intel.com --- hw/usb/hcd-ehci-pci.c | 2 +- hw/usb/hcd-uhci.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index c94fc9f6c5..dd06451e23 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -228,7 +228,7 @@ static void ehci_pci_register_types(void) for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { ehci_type_info.name = ehci_pci_info[i].name; ehci_type_info.class_data = ehci_pci_info + i; - type_register(&ehci_type_info); + type_register_static(&ehci_type_info); } } diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 3d0339af7b..65c1f93cc9 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1362,7 +1362,7 @@ static void uhci_register_types(void) for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { uhci_type_info.name = uhci_info[i].name; uhci_type_info.class_data = uhci_info + i; - type_register(&uhci_type_info); + type_register_static(&uhci_type_info); } } From 3b5f3070386e5b2a48e0e96ff96ef2251131eeb8 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:27 +0800 Subject: [PATCH 0029/2892] hw/virtio: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-10-zhao1.liu@intel.com --- hw/virtio/virtio-pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 5a394821da..5c6c2019ce 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2511,9 +2511,9 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) base_type_info.class_data = (void *)t; } - type_register(&base_type_info); + type_register_static(&base_type_info); if (generic_type_info.name) { - type_register(&generic_type_info); + type_register_static(&generic_type_info); } if (t->non_transitional_name) { @@ -2527,7 +2527,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) { } }, }; - type_register(&non_transitional_type_info); + type_register_static(&non_transitional_type_info); } if (t->transitional_name) { @@ -2544,7 +2544,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) { } }, }; - type_register(&transitional_type_info); + type_register_static(&transitional_type_info); } g_free(base_name); } From 81b69b502e3f67d357766ad42ae9a3f8bd675ad2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:28 +0800 Subject: [PATCH 0030/2892] i386: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-11-zhao1.liu@intel.com --- include/hw/i386/pc.h | 4 ++-- target/i386/cpu.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 14ee06287d..b3477ad6a2 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -316,7 +316,7 @@ extern const size_t pc_compat_2_3_len; }; \ static void pc_machine_init_##suffix(void) \ { \ - type_register(&pc_machine_type_##suffix); \ + type_register_static(&pc_machine_type_##suffix); \ } \ type_init(pc_machine_init_##suffix) @@ -344,7 +344,7 @@ extern const size_t pc_compat_2_3_len; static void MACHINE_VER_SYM(register, namesym, __VA_ARGS__)(void) \ { \ MACHINE_VER_DELETION(__VA_ARGS__); \ - type_register(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \ + type_register_static(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, namesym, __VA_ARGS__)); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3725dbbc4b..305f2a41cf 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6429,7 +6429,7 @@ static void x86_register_cpu_model_type(const char *name, X86CPUModel *model) .class_data = model, }; - type_register(&ti); + type_register_static(&ti); } From 96a4616dfcd3caa956373469fc70ff882ab1af9f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:29 +0800 Subject: [PATCH 0031/2892] target/mips: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-12-zhao1.liu@intel.com --- target/mips/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index d0a43b6d5c..4feacc88c0 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -626,7 +626,7 @@ static void mips_register_cpudef_type(const struct mips_def_t *def) .class_data = (void *)def, }; - type_register(&ti); + type_register_static(&ti); g_free(typename); } From 2f02b71b5a51c0536280e56109a37e1360c52c50 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:30 +0800 Subject: [PATCH 0032/2892] target/sparc: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-13-zhao1.liu@intel.com --- target/sparc/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index dd7af86de7..adba24af2c 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1008,7 +1008,7 @@ static void sparc_register_cpudef_type(const struct sparc_def_t *def) .class_data = (void *)def, }; - type_register(&ti); + type_register_static(&ti); g_free(typename); } From 2780dd6acbed68b1abacc54b1ade0e883bbbb371 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:31 +0800 Subject: [PATCH 0033/2892] target/xtensa: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-14-zhao1.liu@intel.com --- target/xtensa/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index ca214b948a..2978c471c1 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -198,7 +198,7 @@ void xtensa_register_core(XtensaConfigList *node) node->next = xtensa_cores; xtensa_cores = node; type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name); - type_register(&type); + type_register_static(&type); g_free((gpointer)type.name); } From e5d60d930cc328e773e14670f3b6c60f94b3e167 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:32 +0800 Subject: [PATCH 0034/2892] ui: Replace type_register() with type_register_static() Replace type_register() with type_register_static() because type_register() will be deprecated. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-15-zhao1.liu@intel.com --- ui/console-vc.c | 2 +- ui/dbus.c | 2 +- ui/gtk.c | 2 +- ui/spice-app.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/console-vc.c b/ui/console-vc.c index 53fcee88f4..fe20579832 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -1073,6 +1073,6 @@ void qemu_console_early_init(void) { /* set the default vc driver */ if (!object_class_by_name(TYPE_CHARDEV_VC)) { - type_register(&char_vc_type_info); + type_register_static(&char_vc_type_info); } } diff --git a/ui/dbus.c b/ui/dbus.c index 7ecd39e784..d60b59cc54 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -476,7 +476,7 @@ early_dbus_init(DisplayOptions *opts) #endif } - type_register(&dbus_vc_type_info); + type_register_static(&dbus_vc_type_info); } static void diff --git a/ui/gtk.c b/ui/gtk.c index bf9d3dd679..f9a53ea78e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2540,7 +2540,7 @@ static void early_gtk_display_init(DisplayOptions *opts) keycode_map = gd_get_keymap(&keycode_maplen); #if defined(CONFIG_VTE) - type_register(&char_gd_vc_type_info); + type_register_static(&char_gd_vc_type_info); #endif } diff --git a/ui/spice-app.c b/ui/spice-app.c index a10b4a58fe..2a93ae5918 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -173,7 +173,7 @@ static void spice_app_display_early_init(DisplayOptions *opts) exit(1); } - type_register(&char_vc_type_info); + type_register_static(&char_vc_type_info); sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL); qopts = qemu_opts_create(list, NULL, 0, &error_abort); From 4e40d50558432c0e2344ee1838e58e6299e63884 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:33 +0800 Subject: [PATCH 0035/2892] script/codeconverter/qom_type_info: Deprecate MakeTypeRegisterStatic and MakeTypeRegisterNotStatic Deprecate MakeTypeRegisterStatic and MakeTypeRegisterNotStatic because type_register() will be deprecated, then only type_register_static() is used. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-16-zhao1.liu@intel.com --- .../codeconverter/qom_type_info.py | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/scripts/codeconverter/codeconverter/qom_type_info.py b/scripts/codeconverter/codeconverter/qom_type_info.py index 255cb59923..f92c3a4730 100644 --- a/scripts/codeconverter/codeconverter/qom_type_info.py +++ b/scripts/codeconverter/codeconverter/qom_type_info.py @@ -901,26 +901,6 @@ class TypeRegisterCall(FileMatch): regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register'), r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n') -class MakeTypeRegisterStatic(TypeRegisterCall): - """Make type_register() call static if variable is static const""" - def gen_patches(self): - var = self.file.find_match(TypeInfoVar, self.name) - if var is None: - self.warn("can't find TypeInfo var declaration for %s", self.name) - return - if var.is_static() and var.is_const(): - yield self.group_match('func_name').make_patch('type_register_static') - -class MakeTypeRegisterNotStatic(TypeRegisterStaticCall): - """Make type_register() call static if variable is static const""" - def gen_patches(self): - var = self.file.find_match(TypeInfoVar, self.name) - if var is None: - self.warn("can't find TypeInfo var declaration for %s", self.name) - return - if not var.is_static() or not var.is_const(): - yield self.group_match('func_name').make_patch('type_register') - class TypeInfoMacro(FileMatch): """TYPE_INFO macro usage""" regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n') From 281305d3e08ac7330dfe7cf7b3978c119a888bad Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 29 Oct 2024 16:59:34 +0800 Subject: [PATCH 0036/2892] qom/object: Remove type_register() At present, type_register() and type_register_static() are identical, although their documentation expects the *_static variant to accept the Typeinfo with the strings that have the static lifetime. However, the code implementation doesn't have any check or guarantee for static lifetime. In fact, this is unnecessary because type_new() duplicates all strings, thereby taking ownership of them. Therefore, type_register() and type_register_static() are redundant, so one of them should be removed. Since the changes required to remove type_register() were smaller, type_register() was replaced with type_register_static() throughout the code base. Drop its definition, and delete the requirement about string lifetime from the documentation. Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241029085934.2799066-17-zhao1.liu@intel.com --- include/qom/object.h | 14 -------------- qom/object.c | 7 +------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 43c135984a..a201c9712a 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -880,24 +880,10 @@ const char *object_get_typename(const Object *obj); * type_register_static: * @info: The #TypeInfo of the new type. * - * @info and all of the strings it points to should exist for the life time - * that the type is registered. - * * Returns: the new #Type. */ Type type_register_static(const TypeInfo *info); -/** - * type_register: - * @info: The #TypeInfo of the new type - * - * Unlike type_register_static(), this call does not require @info or its - * string members to continue to exist after the call returns. - * - * Returns: the new #Type. - */ -Type type_register(const TypeInfo *info); - /** * type_register_static_array: * @infos: The array of the new type #TypeInfo structures. diff --git a/qom/object.c b/qom/object.c index 9edc06d391..c7660f9a09 100644 --- a/qom/object.c +++ b/qom/object.c @@ -175,17 +175,12 @@ static TypeImpl *type_register_internal(const TypeInfo *info) return ti; } -TypeImpl *type_register(const TypeInfo *info) +TypeImpl *type_register_static(const TypeInfo *info) { assert(info->parent); return type_register_internal(info); } -TypeImpl *type_register_static(const TypeInfo *info) -{ - return type_register(info); -} - void type_register_static_array(const TypeInfo *infos, int nr_infos) { int i; From 37fb26601dd156369ebb84096c2ecfbe89f0a83b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 08:59:07 +0100 Subject: [PATCH 0037/2892] bql: check that the BQL is not dropped within marked sections The Big QEMU Lock (BQL) is used to provide interior mutability to Rust code. While BqlCell performs indivisible accesses, an equivalent of RefCell will allow the borrower to hold to the interior content for a long time. If the BQL is dropped, another thread could come and mutate the data from C code (Rust code would panic on borrow_mut() instead). In order to prevent this, add a new BQL primitive that can mark BQL-atomic sections and aborts if the BQL is dropped within them. Signed-off-by: Paolo Bonzini --- include/qemu/main-loop.h | 15 +++++++++++++++ stubs/iothread-lock.c | 15 +++++++++++++++ system/cpus.c | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 5764db157c..646306c272 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -262,6 +262,21 @@ AioContext *iohandler_get_aio_context(void); */ bool bql_locked(void); +/** + * bql_block: Allow/deny releasing the BQL + * + * The Big QEMU Lock (BQL) is used to provide interior mutability to + * Rust code, but this only works if other threads cannot run while + * the Rust code has an active borrow. This is because C code in + * other threads could come in and mutate data under the Rust code's + * feet. + * + * @increase: Whether to increase or decrease the blocking counter. + * Releasing the BQL while the counter is nonzero triggers + * an assertion failure. + */ +void bql_block_unlock(bool increase); + /** * qemu_in_main_thread: return whether it's possible to safely access * the global state of the block layer. diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index d7890e5581..5467659895 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -1,6 +1,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +static uint32_t bql_unlock_blocked; + bool bql_locked(void) { return false; @@ -12,4 +14,17 @@ void bql_lock_impl(const char *file, int line) void bql_unlock(void) { + assert(!bql_unlock_blocked); +} + +void bql_block_unlock(bool increase) +{ + uint32_t new_value; + + assert(bql_locked()); + + /* check for overflow! */ + new_value = bql_unlock_blocked + increase - !increase; + assert((new_value > bql_unlock_blocked) == increase); + bql_unlock_blocked = new_value; } diff --git a/system/cpus.c b/system/cpus.c index 1c818ff682..ba633c7688 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -514,6 +514,20 @@ bool qemu_in_vcpu_thread(void) QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) +static uint32_t bql_unlock_blocked; + +void bql_block_unlock(bool increase) +{ + uint32_t new_value; + + assert(bql_locked()); + + /* check for overflow! */ + new_value = bql_unlock_blocked + increase - !increase; + assert((new_value > bql_unlock_blocked) == increase); + bql_unlock_blocked = new_value; +} + bool bql_locked(void) { return get_bql_locked(); @@ -540,6 +554,7 @@ void bql_lock_impl(const char *file, int line) void bql_unlock(void) { g_assert(bql_locked()); + g_assert(!bql_unlock_blocked); set_bql_locked(false); qemu_mutex_unlock(&bql); } From 8e194c0ea5cdbae05b77125a582f9927678121ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Nov 2024 13:26:58 +0100 Subject: [PATCH 0038/2892] rust: cell: add BQL-enforcing Cell variant QEMU objects usually have their pointer shared with the "outside world" very early in their lifetime, for example when they create their MemoryRegions. Because at this point it is not valid anymore to create a &mut reference to the device, individual parts of the device struct must be made mutable in a controlled manner. QEMU's Big Lock (BQL) effectively turns multi-threaded code into single-threaded code while device code runs, as long as the BQL is not released while the device is borrowed (because C code could sneak in and mutate the device). We can then introduce custom interior mutability primitives that are semantically similar to the standard library's (single-threaded) Cell and RefCell, but account for QEMU's threading model. Accessing the "BqlCell" or borrowing the "BqlRefCell" requires proving that the BQL is held, and attempting to access without the BQL is a runtime panic, similar to RefCell's already-borrowed panic. With respect to naming I also considered omitting the "Bql" prefix or moving it to the module, e.g. qemu_api::bql::{Cell, RefCell}. However, this could easily lead to mistakes and confusion; for example rustc could suggest the wrong import, leading to subtle bugs. As a start introduce the an equivalent of Cell. Almost all of the code was taken from Rust's standard library, while removing unstable features and probably-unnecessary functionality that constitute a large of the original code. A lot of what's left is documentation, as well as unit tests in the form of doctests. These are not yet integrated in "make check" but can be run with "cargo test --doc". Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/cell.rs | 298 ++++++++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + 3 files changed, 300 insertions(+) create mode 100644 rust/qemu-api/src/cell.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index d719c13f46..edc21e1a3f 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -13,6 +13,7 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', + 'src/cell.rs', 'src/c_str.rs', 'src/definitions.rs', 'src/device_class.rs', diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs new file mode 100644 index 0000000000..2e4ea8d590 --- /dev/null +++ b/rust/qemu-api/src/cell.rs @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: MIT +// +// This file is based on library/core/src/cell.rs from +// Rust 1.82.0. +// +// 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. + +//! BQL-protected mutable containers. +//! +//! Rust memory safety is based on this rule: Given an object `T`, it is only +//! possible to have one of the following: +//! +//! - Having several immutable references (`&T`) to the object (also known as +//! **aliasing**). +//! - Having one mutable reference (`&mut T`) to the object (also known as +//! **mutability**). +//! +//! This is enforced by the Rust compiler. However, there are situations where +//! this rule is not flexible enough. Sometimes it is required to have multiple +//! references to an object and yet mutate it. In particular, QEMU objects +//! usually have their pointer shared with the "outside world very early in +//! their lifetime", for example when they create their +//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual +//! parts of a device must be made mutable in a controlled manner through the +//! use of cell types. +//! +//! This module provides a way to do so via the Big QEMU Lock. While +//! [`BqlCell`] is essentially the same single-threaded primitive that is +//! available in `std::cell`, the BQL allows it to be used from a multi-threaded +//! context and to share references across threads, while maintaining Rust's +//! safety guarantees. For this reason, unlike its `std::cell` counterpart, +//! `BqlCell` implements the `Sync` trait. +//! +//! BQL checks are performed in debug builds but can be optimized away in +//! release builds, providing runtime safety during development with no overhead +//! in production. +//! +//! Warning: While `BqlCell` is similar to its `std::cell` counterpart, the two +//! are not interchangeable. Using `std::cell` types in QEMU device +//! implementations is usually incorrect and can lead to thread-safety issues. +//! +//! ## `BqlCell` +//! +//! [`BqlCell`] implements interior mutability by moving values in and out of +//! the cell. That is, an `&mut T` to the inner value can never be obtained as +//! long as the cell is shared. The value itself cannot be directly obtained +//! without copying it, cloning it, or replacing it with something else. This +//! type provides the following methods, all of which can be called only while +//! the BQL is held: +//! +//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method +//! retrieves the current interior value by duplicating it. +//! - For types that implement [`Default`], the [`take`](BqlCell::take) method +//! replaces the current interior value with [`Default::default()`] and +//! returns the replaced value. +//! - All types have: +//! - [`replace`](BqlCell::replace): replaces the current interior value and +//! returns the replaced value. +//! - [`set`](BqlCell::set): this method replaces the interior value, +//! dropping the replaced value. + +use std::{cell::UnsafeCell, cmp::Ordering, fmt, mem}; + +use crate::bindings; + +// TODO: When building doctests do not include the actual BQL, because cargo +// does not know how to link them to libqemuutil. This can be fixed by +// running rustdoc from "meson test" instead of relying on cargo. +pub fn bql_locked() -> bool { + // SAFETY: the function does nothing but return a thread-local bool + !cfg!(MESON) || unsafe { bindings::bql_locked() } +} + +/// A mutable memory location that is protected by the Big QEMU Lock. +/// +/// # Memory layout +/// +/// `BqlCell` has the same in-memory representation as its inner type `T`. +#[repr(transparent)] +pub struct BqlCell { + value: UnsafeCell, +} + +// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex, +// except it is stored out-of-line +unsafe impl Send for BqlCell {} +unsafe impl Sync for BqlCell {} + +impl Clone for BqlCell { + #[inline] + fn clone(&self) -> BqlCell { + BqlCell::new(self.get()) + } +} + +impl Default for BqlCell { + /// Creates a `BqlCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlCell { + BqlCell::new(Default::default()) + } +} + +impl PartialEq for BqlCell { + #[inline] + fn eq(&self, other: &BqlCell) -> bool { + self.get() == other.get() + } +} + +impl Eq for BqlCell {} + +impl PartialOrd for BqlCell { + #[inline] + fn partial_cmp(&self, other: &BqlCell) -> Option { + self.get().partial_cmp(&other.get()) + } +} + +impl Ord for BqlCell { + #[inline] + fn cmp(&self, other: &BqlCell) -> Ordering { + self.get().cmp(&other.get()) + } +} + +impl From for BqlCell { + /// Creates a new `BqlCell` containing the given value. + fn from(t: T) -> BqlCell { + BqlCell::new(t) + } +} + +impl fmt::Debug for BqlCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl fmt::Display for BqlCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl BqlCell { + /// Creates a new `BqlCell` containing the given value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let c = BqlCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlCell { + BqlCell { + value: UnsafeCell::new(value), + } + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let c = BqlCell::new(5); + /// + /// c.set(10); + /// ``` + #[inline] + pub fn set(&self, val: T) { + self.replace(val); + } + + /// Replaces the contained value with `val`, and returns the old contained + /// value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let cell = BqlCell::new(5); + /// assert_eq!(cell.get(), 5); + /// assert_eq!(cell.replace(10), 5); + /// assert_eq!(cell.get(), 10); + /// ``` + #[inline] + pub fn replace(&self, val: T) -> T { + assert!(bql_locked()); + // SAFETY: This can cause data races if called from multiple threads, + // but it won't happen as long as C code accesses the value + // under BQL protection only. + mem::replace(unsafe { &mut *self.value.get() }, val) + } + + /// Unwraps the value, consuming the cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let c = BqlCell::new(5); + /// let five = c.into_inner(); + /// + /// assert_eq!(five, 5); + /// ``` + pub fn into_inner(self) -> T { + assert!(bql_locked()); + self.value.into_inner() + } +} + +impl BqlCell { + /// Returns a copy of the contained value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let c = BqlCell::new(5); + /// + /// let five = c.get(); + /// ``` + #[inline] + pub fn get(&self) -> T { + assert!(bql_locked()); + // SAFETY: This can cause data races if called from multiple threads, + // but it won't happen as long as C code accesses the value + // under BQL protection only. + unsafe { *self.value.get() } + } +} + +impl BqlCell { + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let c = BqlCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +impl BqlCell { + /// Takes the value of the cell, leaving `Default::default()` in its place. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// + /// let c = BqlCell::new(5); + /// let five = c.take(); + /// + /// assert_eq!(five, 5); + /// assert_eq!(c.into_inner(), 0); + /// ``` + pub fn take(&self) -> T { + self.replace(Default::default()) + } +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 440aff3817..b04d110b3f 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -8,6 +8,7 @@ pub mod bindings; pub mod c_str; +pub mod cell; pub mod definitions; pub mod device_class; pub mod offset_of; From c596199f639cdf4cc021c9bc076e4a1e64e9d50f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Nov 2024 12:20:35 +0100 Subject: [PATCH 0039/2892] rust: cell: add BQL-enforcing RefCell variant Similar to the existing BqlCell, introduce a custom interior mutability primitive that resembles RefCell but accounts for QEMU's threading model. Borrowing the RefCell requires proving that the BQL is held, and attempting to access without the BQL is a runtime panic. Almost all of the code was taken from Rust's standard library, while removing unstable features and probably-unnecessary functionality that amounts to 60% of the original code. A lot of what's left is documentation, as well as unit tests in the form of doctests. These are not yet integrated in "make check" but can be run with "cargo test --doc". Signed-off-by: Paolo Bonzini --- rust/qemu-api/Cargo.toml | 3 +- rust/qemu-api/meson.build | 3 + rust/qemu-api/src/cell.rs | 544 +++++++++++++++++++++++++++++++++++++- 3 files changed, 539 insertions(+), 11 deletions(-) diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 669f288d1c..4aa22f3198 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,8 +20,9 @@ qemu_api_macros = { path = "../qemu-api-macros" } version_check = "~0.9" [features] -default = [] +default = ["debug_cell"] allocator = [] +debug_cell = [] [lints] workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index edc21e1a3f..cacb112c5c 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -6,6 +6,9 @@ _qemu_api_cfg = run_command(rustc_args, if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] endif +if get_option('debug_mutex') + _qemu_api_cfg += ['--feature', 'debug_cell'] +endif _qemu_api_rs = static_library( 'qemu_api', diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 2e4ea8d590..28349de291 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -46,20 +46,30 @@ //! parts of a device must be made mutable in a controlled manner through the //! use of cell types. //! -//! This module provides a way to do so via the Big QEMU Lock. While -//! [`BqlCell`] is essentially the same single-threaded primitive that is -//! available in `std::cell`, the BQL allows it to be used from a multi-threaded -//! context and to share references across threads, while maintaining Rust's -//! safety guarantees. For this reason, unlike its `std::cell` counterpart, -//! `BqlCell` implements the `Sync` trait. +//! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. +//! While they are essentially the same single-threaded primitives that are +//! available in `std::cell`, the BQL allows them to be used from a +//! multi-threaded context and to share references across threads, while +//! maintaining Rust's safety guarantees. For this reason, unlike +//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the +//! `Sync` trait. //! //! BQL checks are performed in debug builds but can be optimized away in //! release builds, providing runtime safety during development with no overhead //! in production. //! -//! Warning: While `BqlCell` is similar to its `std::cell` counterpart, the two -//! are not interchangeable. Using `std::cell` types in QEMU device -//! implementations is usually incorrect and can lead to thread-safety issues. +//! The two provide different ways of handling interior mutability. +//! `BqlRefCell` is best suited for data that is primarily accessed by the +//! device's own methods, where multiple reads and writes can be grouped within +//! a single borrow and a mutable reference can be passed around. Instead, +//! [`BqlCell`] is a better choice when sharing small pieces of data with +//! external code (especially C code), because it provides simple get/set +//! operations that can be used one at a time. +//! +//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell` +//! counterparts, they are not interchangeable. Using `std::cell` types in +//! QEMU device implementations is usually incorrect and can lead to +//! thread-safety issues. //! //! ## `BqlCell` //! @@ -80,8 +90,37 @@ //! returns the replaced value. //! - [`set`](BqlCell::set): this method replaces the interior value, //! dropping the replaced value. +//! +//! ## `BqlRefCell` +//! +//! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a +//! process whereby one can claim temporary, exclusive, mutable access to the +//! inner value: +//! +//! ```ignore +//! fn clear_interrupts(&self, val: u32) { +//! // A mutable borrow gives read-write access to the registers +//! let mut regs = self.registers.borrow_mut(); +//! let old = regs.interrupt_status(); +//! regs.update_interrupt_status(old & !val); +//! } +//! ``` +//! +//! Borrows for `BqlRefCell`s are tracked at _runtime_, unlike Rust's native +//! reference types which are entirely tracked statically, at compile time. +//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), +//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The +//! thread will panic if these rules are violated or if the BQL is not held. -use std::{cell::UnsafeCell, cmp::Ordering, fmt, mem}; +use std::{ + cell::{Cell, UnsafeCell}, + cmp::Ordering, + fmt, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; use crate::bindings; @@ -93,6 +132,15 @@ pub fn bql_locked() -> bool { !cfg!(MESON) || unsafe { bindings::bql_locked() } } +fn bql_block_unlock(increase: bool) { + if cfg!(MESON) { + // SAFETY: this only adjusts a counter + unsafe { + bindings::bql_block_unlock(increase); + } + } +} + /// A mutable memory location that is protected by the Big QEMU Lock. /// /// # Memory layout @@ -296,3 +344,479 @@ impl BqlCell { self.replace(Default::default()) } } + +/// A mutable memory location with dynamically checked borrow rules, +/// protected by the Big QEMU Lock. +/// +/// See the [module-level documentation](self) for more. +/// +/// # Memory layout +/// +/// `BqlRefCell` starts with the same in-memory representation as its +/// inner type `T`. +#[repr(C)] +pub struct BqlRefCell { + // It is important that this is the first field (which is not the case + // for std::cell::BqlRefCell), so that we can use offset_of! on it. + // UnsafeCell and repr(C) both prevent usage of niches. + value: UnsafeCell, + borrow: Cell, + // Stores the location of the earliest currently active borrow. + // This gets updated whenever we go from having zero borrows + // to having a single borrow. When a borrow occurs, this gets included + // in the panic message + #[cfg(feature = "debug_cell")] + borrowed_at: Cell>>, +} + +// Positive values represent the number of `BqlRef` active. Negative values +// represent the number of `BqlRefMut` active. Right now QEMU's implementation +// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping +// components of a `BqlRefCell` (e.g., different ranges of a slice). +// +// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely +// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of +// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or +// underflow. However, this is not a guarantee, as a pathological program could +// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all +// code must explicitly check for overflow and underflow in order to avoid +// unsafety, or at least behave correctly in the event that overflow or +// underflow happens (e.g., see BorrowRef::new). +type BorrowFlag = isize; +const UNUSED: BorrowFlag = 0; + +#[inline(always)] +const fn is_writing(x: BorrowFlag) -> bool { + x < UNUSED +} + +#[inline(always)] +const fn is_reading(x: BorrowFlag) -> bool { + x > UNUSED +} + +impl BqlRefCell { + /// Creates a new `BqlRefCell` containing `value`. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlRefCell { + BqlRefCell { + value: UnsafeCell::new(value), + borrow: Cell::new(UNUSED), + #[cfg(feature = "debug_cell")] + borrowed_at: Cell::new(None), + } + } +} + +// This ensures the panicking code is outlined from `borrow_mut` for +// `BqlRefCell`. +#[inline(never)] +#[cold] +#[cfg(feature = "debug_cell")] +fn panic_already_borrowed(source: &Cell>>) -> ! { + // If a borrow occurred, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + panic!("already borrowed at {:?}", source.take().unwrap()) +} + +#[inline(never)] +#[cold] +#[cfg(not(feature = "debug_cell"))] +fn panic_already_borrowed() -> ! { + panic!("already borrowed") +} + +impl BqlRefCell { + #[inline] + #[allow(clippy::unused_self)] + fn panic_already_borrowed(&self) -> ! { + #[cfg(feature = "debug_cell")] + { + panic_already_borrowed(&self.borrowed_at) + } + #[cfg(not(feature = "debug_cell"))] + { + panic_already_borrowed() + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRef` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let borrowed_five = c.borrow(); + /// let borrowed_five2 = c.borrow(); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let m = c.borrow_mut(); + /// let b = c.borrow(); // this causes a panic + /// ``` + #[inline] + #[track_caller] + pub fn borrow(&self) -> BqlRef<'_, T> { + if let Some(b) = BorrowRef::new(&self.borrow) { + // `borrowed_at` is always the *first* active borrow + if b.borrow.get() == 1 { + #[cfg(feature = "debug_cell")] + self.borrowed_at.set(Some(std::panic::Location::caller())); + } + + bql_block_unlock(true); + + // SAFETY: `BorrowRef` ensures that there is only immutable access + // to the value while borrowed. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRef { value, borrow: b } + } else { + self.panic_already_borrowed() + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s + /// derived from it exit scope. The value cannot be borrowed while this + /// borrow is active. + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new("hello".to_owned()); + /// + /// *c.borrow_mut() = "bonjour".to_owned(); + /// + /// assert_eq!(&*c.borrow(), "bonjour"); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// let m = c.borrow(); + /// + /// let b = c.borrow_mut(); // this causes a panic + /// ``` + #[inline] + #[track_caller] + pub fn borrow_mut(&self) -> BqlRefMut<'_, T> { + if let Some(b) = BorrowRefMut::new(&self.borrow) { + #[cfg(feature = "debug_cell")] + { + self.borrowed_at.set(Some(std::panic::Location::caller())); + } + + // SAFETY: this only adjusts a counter + bql_block_unlock(true); + + // SAFETY: `BorrowRefMut` guarantees unique access. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRefMut { + value, + _borrow: b, + marker: PhantomData, + } + } else { + self.panic_already_borrowed() + } + } + + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is +// stored out-of-line. Even though BqlRefCell includes Cells, they are +// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU +// Lock cannot be released while any borrows is active. +unsafe impl Send for BqlRefCell where T: Send {} +unsafe impl Sync for BqlRefCell {} + +impl Clone for BqlRefCell { + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone(&self) -> BqlRefCell { + BqlRefCell::new(self.borrow().clone()) + } + + /// # Panics + /// + /// Panics if `source` is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone_from(&mut self, source: &Self) { + self.value.get_mut().clone_from(&source.borrow()) + } +} + +impl Default for BqlRefCell { + /// Creates a `BqlRefCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlRefCell { + BqlRefCell::new(Default::default()) + } +} + +impl PartialEq for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn eq(&self, other: &BqlRefCell) -> bool { + *self.borrow() == *other.borrow() + } +} + +impl Eq for BqlRefCell {} + +impl PartialOrd for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn partial_cmp(&self, other: &BqlRefCell) -> Option { + self.borrow().partial_cmp(&*other.borrow()) + } +} + +impl Ord for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn cmp(&self, other: &BqlRefCell) -> Ordering { + self.borrow().cmp(&*other.borrow()) + } +} + +impl From for BqlRefCell { + /// Creates a new `BqlRefCell` containing the given value. + fn from(t: T) -> BqlRefCell { + BqlRefCell::new(t) + } +} + +struct BorrowRef<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRef<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + let b = borrow.get().wrapping_add(1); + if !is_reading(b) { + // Incrementing borrow can result in a non-reading value (<= 0) in these cases: + // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read + // borrow due to Rust's reference aliasing rules + // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed + // into isize::MIN (the max amount of writing borrows) so we can't allow an + // additional read borrow because isize can't represent so many read borrows + // (this can only happen if you mem::forget more than a small constant amount + // of `BqlRef`s, which is not good practice) + None + } else { + // Incrementing borrow can result in a reading value (> 0) in these cases: + // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read + // borrow + // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is + // large enough to represent having one more read borrow + borrow.set(b); + Some(BorrowRef { borrow }) + } + } +} + +impl Drop for BorrowRef<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + debug_assert!(is_reading(borrow)); + self.borrow.set(borrow - 1); + bql_block_unlock(false) + } +} + +impl Clone for BorrowRef<'_> { + #[inline] + fn clone(&self) -> Self { + BorrowRef::new(self.borrow).unwrap() + } +} + +/// Wraps a borrowed reference to a value in a `BqlRefCell` box. +/// A wrapper type for an immutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRef<'b, T: 'b> { + // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a + // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops. + // `NonNull` is also covariant over `T`, just like we would have with `&T`. + value: NonNull, + borrow: BorrowRef<'b>, +} + +impl Deref for BqlRef<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl<'b, T> BqlRef<'b, T> { + /// Copies a `BqlRef`. + /// + /// The `BqlRefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `BqlRef::clone(...)`. A `Clone` implementation or a method would + /// interfere with the widespread use of `r.borrow().clone()` to clone + /// the contents of a `BqlRefCell`. + #[must_use] + #[inline] + #[allow(clippy::should_implement_trait)] + pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> { + BqlRef { + value: orig.value, + borrow: orig.borrow.clone(), + } + } +} + +impl fmt::Debug for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +struct BorrowRefMut<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRefMut<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + // There must currently be no existing references when borrow_mut() is + // called, so we explicitly only allow going from UNUSED to UNUSED - 1. + match borrow.get() { + UNUSED => { + borrow.set(UNUSED - 1); + Some(BorrowRefMut { borrow }) + } + _ => None, + } + } +} + +impl Drop for BorrowRefMut<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + debug_assert!(is_writing(borrow)); + self.borrow.set(borrow + 1); + bql_block_unlock(false) + } +} + +/// A wrapper type for a mutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRefMut<'b, T: 'b> { + // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a + // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. + value: NonNull, + _borrow: BorrowRefMut<'b>, + // `NonNull` is covariant over `T`, so we need to reintroduce invariance. + marker: PhantomData<&'b mut T>, +} + +impl Deref for BqlRefMut<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl DerefMut for BqlRefMut<'_, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_mut() } + } +} + +impl fmt::Debug for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} From 28d0ad3d425a956a1257256c46ef44581f6678c5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Dec 2024 13:42:33 +0100 Subject: [PATCH 0040/2892] rust: define prelude Add a module that will contain frequently used traits and occasionally structs. They can be included quickly with "use qemu_api::prelude::*". Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 5 +++++ rust/qemu-api/src/prelude.rs | 6 ++++++ 3 files changed, 12 insertions(+) create mode 100644 rust/qemu-api/src/prelude.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index cacb112c5c..f8b4cd39a2 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -21,6 +21,7 @@ _qemu_api_rs = static_library( 'src/definitions.rs', 'src/device_class.rs', 'src/offset_of.rs', + 'src/prelude.rs', 'src/vmstate.rs', 'src/zeroable.rs', ], diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index b04d110b3f..e5956cd5eb 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -7,6 +7,11 @@ #[rustfmt::skip] pub mod bindings; +// preserve one-item-per-"use" syntax, it is clearer +// for prelude-like modules +#[rustfmt::skip] +pub mod prelude; + pub mod c_str; pub mod cell; pub mod definitions; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs new file mode 100644 index 0000000000..dfaddbd062 --- /dev/null +++ b/rust/qemu-api/src/prelude.rs @@ -0,0 +1,6 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +pub use crate::cell::BqlCell; +pub use crate::cell::BqlRefCell; From 4ed4da164c957a4475b9d075206f33113a69abda Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Oct 2024 11:29:42 +0100 Subject: [PATCH 0041/2892] rust: add bindings for interrupt sources The InterruptSource bindings let us call qemu_set_irq() and sysbus_init_irq() as safe code. Interrupt sources, qemu_irq in C code, are pointers to IRQState objects. They are QOM link properties and can be written to outside the control of the device (i.e. from a shared reference); therefore they must be interior-mutable in Rust. Since thread-safety is provided by the BQL, what we want here is the newly-introduced BqlCell. A pointer to the contents of the BqlCell (an IRQState**, or equivalently qemu_irq*) is then passed to the C sysbus_init_irq function. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 22 ++++---- rust/qemu-api/meson.build | 2 + rust/qemu-api/src/irq.rs | 91 ++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 2 + rust/qemu-api/src/sysbus.rs | 27 ++++++++++ 5 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 rust/qemu-api/src/irq.rs create mode 100644 rust/qemu-api/src/sysbus.rs diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 317a9b3c5a..c5c8c463d3 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,6 +13,7 @@ use qemu_api::{ c_str, definitions::ObjectImpl, device_class::TYPE_SYS_BUS_DEVICE, + irq::InterruptSource, }; use crate::{ @@ -94,7 +95,7 @@ pub struct PL011State { /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line) /// ``` #[doc(alias = "irq")] - pub interrupts: [qemu_irq; 6usize], + pub interrupts: [InterruptSource; IRQMASK.len()], #[doc(alias = "clk")] pub clock: NonNull, #[doc(alias = "migrate_clk")] @@ -139,7 +140,8 @@ impl PL011State { unsafe fn init(&mut self) { const CLK_NAME: &CStr = c_str!("clk"); - let dev = addr_of_mut!(*self).cast::(); + let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::()) }; + // SAFETY: // // self and self.iomem are guaranteed to be valid at this point since callers @@ -153,12 +155,15 @@ impl PL011State { Self::TYPE_INFO.name, 0x1000, ); - let sbd = addr_of_mut!(*self).cast::(); sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); - for irq in self.interrupts.iter_mut() { - sysbus_init_irq(sbd, irq); - } } + + for irq in self.interrupts.iter() { + sbd.init_irq(irq); + } + + let dev = addr_of_mut!(*self).cast::(); + // SAFETY: // // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, @@ -498,10 +503,7 @@ impl PL011State { pub fn update(&self) { let flags = self.int_level & self.int_enabled; for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - // SAFETY: self.interrupts have been initialized in init(). - unsafe { - qemu_set_irq(*irq, i32::from(flags & i != 0)); - } + irq.set(flags & i != 0); } } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index f8b4cd39a2..b927eb58c8 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -20,8 +20,10 @@ _qemu_api_rs = static_library( 'src/c_str.rs', 'src/definitions.rs', 'src/device_class.rs', + 'src/irq.rs', 'src/offset_of.rs', 'src/prelude.rs', + 'src/sysbus.rs', 'src/vmstate.rs', 'src/zeroable.rs', ], diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs new file mode 100644 index 0000000000..6258141bdf --- /dev/null +++ b/rust/qemu-api/src/irq.rs @@ -0,0 +1,91 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for interrupt sources + +use core::ptr; +use std::{marker::PhantomData, os::raw::c_int}; + +use crate::{ + bindings::{qemu_set_irq, IRQState}, + prelude::*, +}; + +/// Interrupt sources are used by devices to pass changes to a value (typically +/// a boolean). The interrupt sink is usually an interrupt controller or +/// GPIO controller. +/// +/// As far as devices are concerned, interrupt sources are always active-high: +/// for example, `InterruptSource`'s [`raise`](InterruptSource::raise) +/// method sends a `true` value to the sink. If the guest has to see a +/// different polarity, that change is performed by the board between the +/// device and the interrupt controller. +/// +/// Interrupts are implemented as a pointer to the interrupt "sink", which has +/// type [`IRQState`]. A device exposes its source as a QOM link property using +/// a function such as +/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and +/// initially leaves the pointer to a NULL value, representing an unconnected +/// interrupt. To connect it, whoever creates the device fills the pointer with +/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because +/// devices are generally shared objects, interrupt sources are an example of +/// the interior mutability pattern. +/// +/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell` +/// allows access from whatever thread has it. +#[derive(Debug)] +#[repr(transparent)] +pub struct InterruptSource +where + c_int: From, +{ + cell: BqlCell<*mut IRQState>, + _marker: PhantomData, +} + +impl InterruptSource { + /// Send a low (`false`) value to the interrupt sink. + pub fn lower(&self) { + self.set(false); + } + + /// Send a high-low pulse to the interrupt sink. + pub fn pulse(&self) { + self.set(true); + self.set(false); + } + + /// Send a high (`true`) value to the interrupt sink. + pub fn raise(&self) { + self.set(true); + } +} + +impl InterruptSource +where + c_int: From, +{ + /// Send `level` to the interrupt sink. + pub fn set(&self, level: T) { + let ptr = self.cell.get(); + // SAFETY: the pointer is retrieved under the BQL and remains valid + // until the BQL is released, which is after qemu_set_irq() is entered. + unsafe { + qemu_set_irq(ptr, level.into()); + } + } + + pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState { + self.cell.as_ptr() + } +} + +impl Default for InterruptSource { + fn default() -> Self { + InterruptSource { + cell: BqlCell::new(ptr::null_mut()), + _marker: PhantomData, + } + } +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index e5956cd5eb..0efbef4744 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -16,7 +16,9 @@ pub mod c_str; pub mod cell; pub mod definitions; pub mod device_class; +pub mod irq; pub mod offset_of; +pub mod sysbus; pub mod vmstate; pub mod zeroable; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs new file mode 100644 index 0000000000..4e192c7589 --- /dev/null +++ b/rust/qemu-api/src/sysbus.rs @@ -0,0 +1,27 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::ptr::addr_of; + +pub use bindings::{SysBusDevice, SysBusDeviceClass}; + +use crate::{bindings, cell::bql_locked, irq::InterruptSource}; + +impl SysBusDevice { + /// Return `self` cast to a mutable pointer, for use in calls to C code. + const fn as_mut_ptr(&self) -> *mut SysBusDevice { + addr_of!(*self) as *mut _ + } + + /// Expose an interrupt source outside the device as a qdev GPIO output. + /// Note that the ordering of calls to `init_irq` is important, since + /// whoever creates the sysbus device will refer to the interrupts with + /// a number that corresponds to the order of calls to `init_irq`. + pub fn init_irq(&self, irq: &InterruptSource) { + assert!(bql_locked()); + unsafe { + bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr()); + } + } +} From ab870fa106e0e3f48db2c5ef0507d107b1b41a21 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Dec 2024 14:29:13 +0100 Subject: [PATCH 0042/2892] rust: add a bit operation module The bindgen supports `static inline` function binding since v0.64.0 as an experimental feature (`--wrap-static-fns`), and stabilizes it after v0.70.0. But the oldest version of bindgen supported by QEMU is v0.60.1, so there's no way to generate the binding for deposit64() which is `static inline` (in include/qemu/bitops.h). Instead, implement it by hand in Rust and make it available for all unsigned types through an IntegerExt trait. Since it only involves bit operations, the Rust version of the code is almost identical to the original C version, but it applies to more types than just u64. Signed-off-by: Zhao Liu Co-authored-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/bitops.rs | 119 +++++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/prelude.rs | 2 + 4 files changed, 123 insertions(+) create mode 100644 rust/qemu-api/src/bitops.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index b927eb58c8..adcee66115 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -16,6 +16,7 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', + 'src/bitops.rs', 'src/cell.rs', 'src/c_str.rs', 'src/definitions.rs', diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs new file mode 100644 index 0000000000..023ec1a998 --- /dev/null +++ b/rust/qemu-api/src/bitops.rs @@ -0,0 +1,119 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This module provides bit operation extensions to integer types. +//! It is usually included via the `qemu_api` prelude. + +use std::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, +}; + +/// Trait for extensions to integer types +pub trait IntegerExt: + Add + AddAssign + + BitAnd + BitAndAssign + + BitOr + BitOrAssign + + BitXor + BitXorAssign + + Copy + + Div + DivAssign + + Eq + + Mul + MulAssign + + Not + Ord + PartialOrd + + Rem + RemAssign + + Shl + ShlAssign + + Shl + ShlAssign + // add more as needed + Shr + ShrAssign + + Shr + ShrAssign // add more as needed +{ + const BITS: u32; + const MAX: Self; + const MIN: Self; + const ONE: Self; + const ZERO: Self; + + #[inline] + #[must_use] + fn bit(start: u32) -> Self + { + debug_assert!(start < Self::BITS); + + Self::ONE << start + } + + #[inline] + #[must_use] + fn mask(start: u32, length: u32) -> Self + { + /* FIXME: Implement a more elegant check with error handling support? */ + debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); + + (Self::MAX >> (Self::BITS - length)) << start + } + + #[inline] + #[must_use] + fn deposit(self, start: u32, length: u32, + fieldval: U) -> Self + where Self: From + { + debug_assert!(length <= U::BITS); + + let mask = Self::mask(start, length); + (self & !mask) | ((Self::from(fieldval) << start) & mask) + } + + #[inline] + #[must_use] + fn extract(self, start: u32, length: u32) -> Self + { + let mask = Self::mask(start, length); + (self & mask) >> start + } +} + +macro_rules! impl_num_ext { + ($type:ty) => { + impl IntegerExt for $type { + const BITS: u32 = <$type>::BITS; + const MAX: Self = <$type>::MAX; + const MIN: Self = <$type>::MIN; + const ONE: Self = 1; + const ZERO: Self = 0; + } + }; +} + +impl_num_ext!(u8); +impl_num_ext!(u16); +impl_num_ext!(u32); +impl_num_ext!(u64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deposit() { + assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); + assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); + } + + #[test] + fn test_extract() { + assert_eq!(15u32.extract(2, 4), 3); + } + + #[test] + fn test_bit() { + assert_eq!(u8::bit(7), 128); + assert_eq!(u32::bit(16), 0x10000); + } + + #[test] + fn test_mask() { + assert_eq!(u8::mask(7, 1), 128); + assert_eq!(u32::mask(8, 8), 0xff00); + } +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 0efbef4744..9e007e1635 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -12,6 +12,7 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; +pub mod bitops; pub mod c_str; pub mod cell; pub mod definitions; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index dfaddbd062..a39e228bab 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -2,5 +2,7 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later +pub use crate::bitops::IntegerExt; + pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; From b2a4854508a02fc8a585890e0272c8ae5fbad5c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Oct 2024 11:28:23 +0100 Subject: [PATCH 0043/2892] rust: qom: add default definitions for ObjectImpl Remove a bunch of duplicate const definitions. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ------ rust/qemu-api/src/definitions.rs | 8 ++++---- rust/qemu-api/tests/tests.rs | 4 ---- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c5c8c463d3..3d173ae816 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -109,10 +109,7 @@ impl ObjectImpl for PL011State { const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); - const ABSTRACT: bool = false; const INSTANCE_INIT: Option = Some(pl011_init); - const INSTANCE_POST_INIT: Option = None; - const INSTANCE_FINALIZE: Option = None; } #[repr(C)] @@ -666,8 +663,5 @@ impl ObjectImpl for PL011Luminary { const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); - const ABSTRACT: bool = false; const INSTANCE_INIT: Option = Some(pl011_luminary_init); - const INSTANCE_POST_INIT: Option = None; - const INSTANCE_FINALIZE: Option = None; } diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 26597934bb..92b3c6f911 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -14,10 +14,10 @@ pub trait ObjectImpl { const TYPE_INFO: TypeInfo; const TYPE_NAME: &'static CStr; const PARENT_TYPE_NAME: Option<&'static CStr>; - const ABSTRACT: bool; - const INSTANCE_INIT: Option; - const INSTANCE_POST_INIT: Option; - const INSTANCE_FINALIZE: Option; + const ABSTRACT: bool = false; + const INSTANCE_INIT: Option = None; + const INSTANCE_POST_INIT: Option = None; + const INSTANCE_FINALIZE: Option = None; } pub trait Class { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 925f5a3c77..f793ff26e5 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -58,10 +58,6 @@ fn test_device_decl_macros() { const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; const TYPE_NAME: &'static CStr = c_str!("dummy"); const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); - const ABSTRACT: bool = false; - const INSTANCE_INIT: Option = None; - const INSTANCE_POST_INIT: Option = None; - const INSTANCE_FINALIZE: Option = None; } impl Class for DummyClass { From 93ea0896eaa97adfcc664fa65b5b70e555a652ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Oct 2024 13:05:43 +0100 Subject: [PATCH 0044/2892] rust: qom: rename Class trait to ClassInitImpl While at it, document it. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 ++-- rust/qemu-api/src/definitions.rs | 25 ++++++++++++++++++++++--- rust/qemu-api/tests/tests.rs | 4 ++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 3d173ae816..bd12067aaf 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -117,7 +117,7 @@ pub struct PL011Class { _inner: [u8; 0], } -impl qemu_api::definitions::Class for PL011Class { +impl qemu_api::definitions::ClassInitImpl for PL011Class { const CLASS_INIT: Option = Some(crate::device_class::pl011_class_init); const CLASS_BASE_INIT: Option< @@ -650,7 +650,7 @@ pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) { } } -impl qemu_api::definitions::Class for PL011LuminaryClass { +impl qemu_api::definitions::ClassInitImpl for PL011LuminaryClass { const CLASS_INIT: Option = None; const CLASS_BASE_INIT: Option< diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 92b3c6f911..3291f4242c 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -20,8 +20,27 @@ pub trait ObjectImpl { const INSTANCE_FINALIZE: Option = None; } -pub trait Class { +/// Trait used to fill in a class struct. +/// +/// Each QOM class that has virtual methods describes them in a +/// _class struct_. Class structs include a parent field corresponding +/// to the vtable of the parent class, all the way up to [`ObjectClass`]. +/// Each QOM type has one such class struct. +/// +/// The Rust implementation of methods will usually come from a trait +/// like [`ObjectImpl`]. +pub trait ClassInitImpl { + /// Function that is called after all parent class initialization + /// has occurred. On entry, the virtual method pointers are set to + /// the default values coming from the parent classes; the function + /// can change them to override virtual methods of a parent class. const CLASS_INIT: Option; + + /// Called on descendent classes after all parent class initialization + /// has occurred, but before the class itself is initialized. This + /// is only useful if a class is not a leaf, and can be used to undo + /// the effects of copying the contents of the parent's class struct + /// to the descendants. const CLASS_BASE_INIT: Option< unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), >; @@ -82,8 +101,8 @@ macro_rules! type_info { instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE, abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT, class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(), - class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT, - class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT, + class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_INIT, + class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_BASE_INIT, class_data: ::core::ptr::null_mut(), interfaces: ::core::ptr::null_mut(), }; diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index f793ff26e5..704c63c846 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -7,7 +7,7 @@ use std::{ffi::CStr, os::raw::c_void}; use qemu_api::{ bindings::*, c_str, declare_properties, define_property, - definitions::{Class, ObjectImpl}, + definitions::{ClassInitImpl, ObjectImpl}, device_class, device_class_init, zeroable::Zeroable, }; @@ -60,7 +60,7 @@ fn test_device_decl_macros() { const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); } - impl Class for DummyClass { + impl ClassInitImpl for DummyClass { const CLASS_INIT: Option = Some(dummy_class_init); const CLASS_BASE_INIT: Option< From 3701fb22dfd438993e76e158beb97683359d1dd9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Oct 2024 11:47:12 +0100 Subject: [PATCH 0045/2892] rust: qom: convert type_info! macro to an associated const type_info! is only used in the definition of ObjectImpl::TYPE_INFO, and in fact in all of them. Pull type_info!'s definition into the ObjectImpl trait, thus simplifying the external interface of qemu_api::definitions. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ++-- rust/qemu-api/src/definitions.rs | 50 ++++++++++++++------------------ rust/qemu-api/tests/tests.rs | 1 - 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bd12067aaf..bcb146c24d 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -106,7 +106,6 @@ pub struct PL011State { impl ObjectImpl for PL011State { type Class = PL011Class; - const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); const INSTANCE_INIT: Option = Some(pl011_init); @@ -149,7 +148,7 @@ impl PL011State { addr_of_mut!(*self).cast::(), &PL011_OPS, addr_of_mut!(*self).cast::(), - Self::TYPE_INFO.name, + Self::TYPE_NAME.as_ptr(), 0x1000, ); sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); @@ -598,7 +597,7 @@ pub unsafe extern "C" fn pl011_create( chr: *mut Chardev, ) -> *mut DeviceState { unsafe { - let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name); + let dev: *mut DeviceState = qdev_new(PL011State::TYPE_NAME.as_ptr()); let sysbus: *mut SysBusDevice = dev.cast::(); qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); @@ -660,7 +659,6 @@ impl qemu_api::definitions::ClassInitImpl for PL011LuminaryClass { impl ObjectImpl for PL011Luminary { type Class = PL011LuminaryClass; - const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); const INSTANCE_INIT: Option = Some(pl011_luminary_init); diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 3291f4242c..6ecfaf51b0 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -9,15 +9,34 @@ use std::{ffi::CStr, os::raw::c_void}; use crate::bindings::{Object, ObjectClass, TypeInfo}; /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl { - type Class; - const TYPE_INFO: TypeInfo; +pub trait ObjectImpl: Sized { + type Class: ClassInitImpl; const TYPE_NAME: &'static CStr; const PARENT_TYPE_NAME: Option<&'static CStr>; const ABSTRACT: bool = false; const INSTANCE_INIT: Option = None; const INSTANCE_POST_INIT: Option = None; const INSTANCE_FINALIZE: Option = None; + + const TYPE_INFO: TypeInfo = TypeInfo { + name: Self::TYPE_NAME.as_ptr(), + parent: if let Some(pname) = Self::PARENT_TYPE_NAME { + pname.as_ptr() + } else { + core::ptr::null_mut() + }, + instance_size: core::mem::size_of::(), + instance_align: core::mem::align_of::(), + instance_init: Self::INSTANCE_INIT, + instance_post_init: Self::INSTANCE_POST_INIT, + instance_finalize: Self::INSTANCE_FINALIZE, + abstract_: Self::ABSTRACT, + class_size: core::mem::size_of::(), + class_init: ::CLASS_INIT, + class_base_init: ::CLASS_BASE_INIT, + class_data: core::ptr::null_mut(), + interfaces: core::ptr::null_mut(), + }; } /// Trait used to fill in a class struct. @@ -83,28 +102,3 @@ macro_rules! module_init { } }; } - -#[macro_export] -macro_rules! type_info { - ($t:ty) => { - $crate::bindings::TypeInfo { - name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(), - parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME { - pname.as_ptr() - } else { - ::core::ptr::null_mut() - }, - instance_size: ::core::mem::size_of::<$t>(), - instance_align: ::core::mem::align_of::<$t>(), - instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT, - instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT, - instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE, - abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT, - class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(), - class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_INIT, - class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_BASE_INIT, - class_data: ::core::ptr::null_mut(), - interfaces: ::core::ptr::null_mut(), - }; - } -} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 704c63c846..7f9df348b0 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -55,7 +55,6 @@ fn test_device_decl_macros() { impl ObjectImpl for DummyState { type Class = DummyClass; - const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; const TYPE_NAME: &'static CStr = c_str!("dummy"); const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); } From c6c4f3e0d90990c642523c087482eac3f42566c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Oct 2024 14:42:23 +0100 Subject: [PATCH 0046/2892] rust: qom: move ClassInitImpl to the instance side Put all traits on the instance struct, which makes it possible to reuse class structs if no new virtual methods or class fields are added. This is almost always the case for devices (because they are leaf classes), which is the primary use case for Rust. This is also simpler: soon we will find the implemented methods without macros, and this removes the need to go from the class struct to the instance struct to find the implementation of the *Impl traits. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 ++-- rust/qemu-api/src/definitions.rs | 8 ++++---- rust/qemu-api/tests/tests.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bcb146c24d..2384d4bcb9 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -116,7 +116,7 @@ pub struct PL011Class { _inner: [u8; 0], } -impl qemu_api::definitions::ClassInitImpl for PL011Class { +impl qemu_api::definitions::ClassInitImpl for PL011State { const CLASS_INIT: Option = Some(crate::device_class::pl011_class_init); const CLASS_BASE_INIT: Option< @@ -649,7 +649,7 @@ pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) { } } -impl qemu_api::definitions::ClassInitImpl for PL011LuminaryClass { +impl qemu_api::definitions::ClassInitImpl for PL011Luminary { const CLASS_INIT: Option = None; const CLASS_BASE_INIT: Option< diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 6ecfaf51b0..487712611f 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -9,8 +9,8 @@ use std::{ffi::CStr, os::raw::c_void}; use crate::bindings::{Object, ObjectClass, TypeInfo}; /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: Sized { - type Class: ClassInitImpl; +pub trait ObjectImpl: ClassInitImpl + Sized { + type Class; const TYPE_NAME: &'static CStr; const PARENT_TYPE_NAME: Option<&'static CStr>; const ABSTRACT: bool = false; @@ -32,8 +32,8 @@ pub trait ObjectImpl: Sized { instance_finalize: Self::INSTANCE_FINALIZE, abstract_: Self::ABSTRACT, class_size: core::mem::size_of::(), - class_init: ::CLASS_INIT, - class_base_init: ::CLASS_BASE_INIT, + class_init: ::CLASS_INIT, + class_base_init: ::CLASS_BASE_INIT, class_data: core::ptr::null_mut(), interfaces: core::ptr::null_mut(), }; diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 7f9df348b0..fd0c979121 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -59,7 +59,7 @@ fn test_device_decl_macros() { const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); } - impl ClassInitImpl for DummyClass { + impl ClassInitImpl for DummyState { const CLASS_INIT: Option = Some(dummy_class_init); const CLASS_BASE_INIT: Option< From 8c80c472da6342c5924bc4ea7e87c77ca61477b8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Oct 2024 10:29:27 +0100 Subject: [PATCH 0047/2892] rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro Use a trait to access the former parameters to device_class_init!. This allows hiding the details of the class_init implementation behind a generic function and makes higher-level functionality available from qemu_api. The implementation of ClassInitImpl is then the same for all devices and is easily macroized. Later on, we can remove the need to implement ClassInitImpl by hand for all device types, and stop making rust_device_class_init<>() public. While at it, document the members of DeviceImpl. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 34 +++++----- rust/hw/char/pl011/src/device_class.rs | 8 --- rust/qemu-api/src/device_class.rs | 87 +++++++++++++++++++++----- rust/qemu-api/tests/tests.rs | 30 ++++----- 4 files changed, 103 insertions(+), 56 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 2384d4bcb9..28b1924337 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -12,11 +12,13 @@ use qemu_api::{ bindings::{self, *}, c_str, definitions::ObjectImpl, - device_class::TYPE_SYS_BUS_DEVICE, + device_class::{DeviceImpl, TYPE_SYS_BUS_DEVICE}, + impl_device_class, irq::InterruptSource, }; use crate::{ + device_class, memory_ops::PL011_OPS, registers::{self, Interrupt}, RegisterOffset, @@ -116,14 +118,20 @@ pub struct PL011Class { _inner: [u8; 0], } -impl qemu_api::definitions::ClassInitImpl for PL011State { - const CLASS_INIT: Option = - Some(crate::device_class::pl011_class_init); - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), - > = None; +impl DeviceImpl for PL011State { + fn properties() -> &'static [Property] { + &device_class::PL011_PROPERTIES + } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&device_class::VMSTATE_PL011) + } + const REALIZE: Option = + Some(device_class::pl011_realize); + const RESET: Option = Some(device_class::pl011_reset); } +impl_device_class!(PL011State); + impl PL011State { /// Initializes a pre-allocated, unitialized instance of `PL011State`. /// @@ -649,17 +657,13 @@ pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) { } } -impl qemu_api::definitions::ClassInitImpl for PL011Luminary { - const CLASS_INIT: Option = - None; - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), - > = None; -} - impl ObjectImpl for PL011Luminary { type Class = PL011LuminaryClass; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); const INSTANCE_INIT: Option = Some(pl011_luminary_init); } + +impl DeviceImpl for PL011Luminary {} + +impl_device_class!(PL011Luminary); diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index a707fde138..c61b6bb025 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -93,14 +93,6 @@ qemu_api::declare_properties! { ), } -qemu_api::device_class_init! { - pl011_class_init, - props => PL011_PROPERTIES, - realize_fn => Some(pl011_realize), - legacy_reset_fn => Some(pl011_reset), - vmsd => VMSTATE_PL011, -} - /// # Safety /// /// We expect the FFI user of this function to pass a valid pointer, that has diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index 922bbce1bb..f683f94f2a 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -2,25 +2,80 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::ffi::CStr; +use std::{ffi::CStr, os::raw::c_void}; -use crate::bindings; +use crate::{ + bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, + zeroable::Zeroable, +}; + +/// Trait providing the contents of [`DeviceClass`]. +pub trait DeviceImpl { + /// _Realization_ is the second stage of device creation. It contains + /// all operations that depend on device properties and can fail (note: + /// this is not yet supported for Rust devices). + /// + /// If not `None`, the parent class's `realize` method is overridden + /// with the function pointed to by `REALIZE`. + const REALIZE: Option = None; + + /// If not `None`, the parent class's `reset` method is overridden + /// with the function pointed to by `RESET`. + /// + /// Rust does not yet support the three-phase reset protocol; this is + /// usually okay for leaf classes. + const RESET: Option = None; + + /// An array providing the properties that the user can set on the + /// device. Not a `const` because referencing statics in constants + /// is unstable until Rust 1.83.0. + fn properties() -> &'static [Property] { + &[Zeroable::ZERO; 1] + } + + /// A `VMStateDescription` providing the migration format for the device + /// Not a `const` because referencing statics in constants is unstable + /// until Rust 1.83.0. + fn vmsd() -> Option<&'static VMStateDescription> { + None + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `DeviceClass`, because `T` implements +/// `DeviceImpl`. +pub unsafe extern "C" fn rust_device_class_init( + klass: *mut ObjectClass, + _: *mut c_void, +) { + let mut dc = ::core::ptr::NonNull::new(klass.cast::()).unwrap(); + unsafe { + let dc = dc.as_mut(); + if let Some(realize_fn) = ::REALIZE { + dc.realize = Some(realize_fn); + } + if let Some(reset_fn) = ::RESET { + bindings::device_class_set_legacy_reset(dc, Some(reset_fn)); + } + if let Some(vmsd) = ::vmsd() { + dc.vmsd = vmsd; + } + bindings::device_class_set_props(dc, ::properties().as_ptr()); + } +} #[macro_export] -macro_rules! device_class_init { - ($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => { - pub unsafe extern "C" fn $func( - klass: *mut $crate::bindings::ObjectClass, - _: *mut ::std::os::raw::c_void, - ) { - let mut dc = - ::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap(); - unsafe { - dc.as_mut().realize = $realize_fn; - dc.as_mut().vmsd = &$vmsd; - $crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn); - $crate::bindings::device_class_set_props(dc.as_mut(), $props.as_ptr()); - } +macro_rules! impl_device_class { + ($type:ty) => { + impl $crate::definitions::ClassInitImpl for $type { + const CLASS_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void), + > = Some($crate::device_class::rust_device_class_init::<$type>); + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void), + > = None; } }; } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index fd0c979121..b8b12a4042 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -2,13 +2,14 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, os::raw::c_void}; +use std::ffi::CStr; use qemu_api::{ bindings::*, c_str, declare_properties, define_property, - definitions::{ClassInitImpl, ObjectImpl}, - device_class, device_class_init, + definitions::ObjectImpl, + device_class::{self, DeviceImpl}, + impl_device_class, zeroable::Zeroable, }; @@ -45,28 +46,23 @@ fn test_device_decl_macros() { ), } - device_class_init! { - dummy_class_init, - props => DUMMY_PROPERTIES, - realize_fn => None, - legacy_reset_fn => None, - vmsd => VMSTATE, - } - impl ObjectImpl for DummyState { type Class = DummyClass; const TYPE_NAME: &'static CStr = c_str!("dummy"); const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); } - impl ClassInitImpl for DummyState { - const CLASS_INIT: Option = - Some(dummy_class_init); - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), - > = None; + impl DeviceImpl for DummyState { + fn properties() -> &'static [Property] { + &DUMMY_PROPERTIES + } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&VMSTATE) + } } + impl_device_class!(DummyState); + unsafe { module_call_init(module_init_type::MODULE_INIT_QOM); object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); From f75fb90ff2af75cd4405fe4c6ba0c0c38a120590 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 12 Nov 2024 17:08:07 +0100 Subject: [PATCH 0048/2892] rust: qdev: move bridge for realize and reset functions out of pl011 Allow the DeviceImpl trait to expose safe Rust functions. rust_device_class_init<> adds thunks around the functions in DeviceImpl. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 5 ++-- rust/hw/char/pl011/src/device_class.rs | 26 ------------------- rust/qemu-api/src/definitions.rs | 2 +- rust/qemu-api/src/device_class.rs | 36 +++++++++++++++++++++----- 4 files changed, 33 insertions(+), 36 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 28b1924337..56403c3660 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -125,9 +125,8 @@ impl DeviceImpl for PL011State { fn vmsd() -> Option<&'static VMStateDescription> { Some(&device_class::VMSTATE_PL011) } - const REALIZE: Option = - Some(device_class::pl011_realize); - const RESET: Option = Some(device_class::pl011_reset); + const REALIZE: Option = Some(Self::realize); + const RESET: Option = Some(Self::reset); } impl_device_class!(PL011State); diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index c61b6bb025..975c3d42be 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -92,29 +92,3 @@ qemu_api::declare_properties! { default = true ), } - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) { - unsafe { - assert!(!dev.is_null()); - let mut state = NonNull::new_unchecked(dev.cast::()); - state.as_mut().realize(); - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) { - unsafe { - assert!(!dev.is_null()); - let mut state = NonNull::new_unchecked(dev.cast::()); - state.as_mut().reset(); - } -} diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 487712611f..0467e6290e 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -47,7 +47,7 @@ pub trait ObjectImpl: ClassInitImpl + Sized { /// Each QOM type has one such class struct. /// /// The Rust implementation of methods will usually come from a trait -/// like [`ObjectImpl`]. +/// like [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl). pub trait ClassInitImpl { /// Function that is called after all parent class initialization /// has occurred. On entry, the virtual method pointers are set to diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index f683f94f2a..f25904be4f 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -17,14 +17,14 @@ pub trait DeviceImpl { /// /// If not `None`, the parent class's `realize` method is overridden /// with the function pointed to by `REALIZE`. - const REALIZE: Option = None; + const REALIZE: Option = None; /// If not `None`, the parent class's `reset` method is overridden /// with the function pointed to by `RESET`. /// /// Rust does not yet support the three-phase reset protocol; this is /// usually okay for leaf classes. - const RESET: Option = None; + const RESET: Option = None; /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants @@ -41,6 +41,30 @@ pub trait DeviceImpl { } } +/// # Safety +/// +/// This function is only called through the QOM machinery and +/// the `impl_device_class!` macro. +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp: *mut *mut Error) { + assert!(!dev.is_null()); + let state = dev.cast::(); + T::REALIZE.unwrap()(unsafe { &mut *state }); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { + assert!(!dev.is_null()); + let state = dev.cast::(); + T::RESET.unwrap()(unsafe { &mut *state }); +} + /// # Safety /// /// We expect the FFI user of this function to pass a valid pointer that @@ -53,11 +77,11 @@ pub unsafe extern "C" fn rust_device_class_init( let mut dc = ::core::ptr::NonNull::new(klass.cast::()).unwrap(); unsafe { let dc = dc.as_mut(); - if let Some(realize_fn) = ::REALIZE { - dc.realize = Some(realize_fn); + if ::REALIZE.is_some() { + dc.realize = Some(rust_realize_fn::); } - if let Some(reset_fn) = ::RESET { - bindings::device_class_set_legacy_reset(dc, Some(reset_fn)); + if ::RESET.is_some() { + bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); } if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; From 2b1b66e01f95b07ca72ebcfab3d43df8fbc26f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 19 Nov 2024 08:13:52 +0100 Subject: [PATCH 0049/2892] arm: Remove tacoma-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removal was scheduled for 10.0. Use the rainier-bmc machine or the ast2600-evb as a replacement. Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20241119071352.515790-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 8 -------- docs/about/removed-features.rst | 10 ++++++++++ docs/system/arm/aspeed.rst | 1 - hw/arm/aspeed.c | 28 ---------------------------- 4 files changed, 10 insertions(+), 37 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d8dc29d0a4..267892b62f 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -263,14 +263,6 @@ images are not available, OpenWRT dropped support in 2019, U-Boot in 2017, Linux also is dropping support in 2024. It is time to let go of this ancient hardware and focus on newer CPUs and platforms. -Arm ``tacoma-bmc`` machine (since 9.1) -'''''''''''''''''''''''''''''''''''''''' - -The ``tacoma-bmc`` machine was a board including an AST2600 SoC based -BMC and a witherspoon like OpenPOWER system. It was used for bring up -of the AST2600 SoC in labs. It can be easily replaced by the -``rainier-bmc`` machine which is a real product. - Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (since 9.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index ee6455aeee..9bebee795c 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1019,6 +1019,16 @@ Aspeed ``swift-bmc`` machine (removed in 7.0) This machine was removed because it was unused. Alternative AST2500 based OpenPOWER machines are ``witherspoon-bmc`` and ``romulus-bmc``. +Aspeed ``tacoma-bmc`` machine (removed in 10.0) +''''''''''''''''''''''''''''''''''''''''''''''' + +The ``tacoma-bmc`` machine was removed because it didn't bring much +compared to the ``rainier-bmc`` machine. Also, the ``tacoma-bmc`` was +a board used for bring up of the AST2600 SoC that never left the +labs. It can be easily replaced by the ``rainier-bmc`` machine, which +was the actual final product, or by the ``ast2600-evb`` with some +tweaks. + ppc ``taihu`` machine (removed in 7.2) ''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index d17fe7a4fc..fa4aa28eef 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -32,7 +32,6 @@ AST2500 SoC based machines : AST2600 SoC based machines : - ``ast2600-evb`` Aspeed AST2600 Evaluation board (Cortex-A7) -- ``tacoma-bmc`` OpenPOWER Witherspoon POWER9 AST2600 BMC - ``rainier-bmc`` IBM Rainier POWER10 BMC - ``fuji-bmc`` Facebook Fuji BMC - ``bletchley-bmc`` Facebook Bletchley BMC diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 6ca145362c..556498f2a0 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -185,10 +185,6 @@ struct AspeedMachineState { #define AST2700_EVB_HW_STRAP2 0x00000003 #endif -/* Tacoma hardware value */ -#define TACOMA_BMC_HW_STRAP1 0x00000000 -#define TACOMA_BMC_HW_STRAP2 0x00000040 - /* Rainier hardware value: (QEMU prototype) */ #define RAINIER_BMC_HW_STRAP1 (0x00422016 | SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC) #define RAINIER_BMC_HW_STRAP2 0x80000848 @@ -1425,26 +1421,6 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) aspeed_machine_ast2600_class_emmc_init(oc); }; -static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - - mc->desc = "OpenPOWER Tacoma BMC (Cortex-A7)"; - amc->soc_name = "ast2600-a3"; - amc->hw_strap1 = TACOMA_BMC_HW_STRAP1; - amc->hw_strap2 = TACOMA_BMC_HW_STRAP2; - amc->fmc_model = "mx66l1g45g"; - amc->spi_model = "mx66l1g45g"; - amc->num_cs = 2; - amc->macs_mask = ASPEED_MAC2_ON; - amc->i2c_init = witherspoon_bmc_i2c_init; /* Same board layout */ - mc->default_ram_size = 1 * GiB; - aspeed_machine_class_init_cpus_defaults(mc); - - mc->deprecation_reason = "Please use the similar 'rainier-bmc' machine"; -}; - static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1766,10 +1742,6 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("yosemitev2-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_yosemitev2_class_init, - }, { - .name = MACHINE_TYPE_NAME("tacoma-bmc"), - .parent = TYPE_ASPEED_MACHINE, - .class_init = aspeed_machine_tacoma_class_init, }, { .name = MACHINE_TYPE_NAME("tiogapass-bmc"), .parent = TYPE_ASPEED_MACHINE, From b00ca20440e081a45f5c0a65faf97267a3c5446a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 4 Dec 2024 16:44:48 +0800 Subject: [PATCH 0050/2892] hw/sd/aspeed_sdhci: Fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241204084453.610660-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/sd/aspeed_sdhci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index 98d5460905..acd6538261 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -87,10 +87,12 @@ static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, sdhci->regs[TO_REG(addr)] = (uint32_t)val & ~ASPEED_SDHCI_INFO_RESET; break; case ASPEED_SDHCI_SDIO_140: - sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg, 0, 32, val); + sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg, + 0, 32, val); break; case ASPEED_SDHCI_SDIO_144: - sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg, 32, 32, val); + sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg, + 32, 32, val); break; case ASPEED_SDHCI_SDIO_148: sdhci->slots[0].maxcurr = deposit64(sdhci->slots[0].maxcurr, From 0178d1198fa225c8ecce7ae52dee5854c2d13213 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 4 Dec 2024 16:44:49 +0800 Subject: [PATCH 0051/2892] hw/arm/aspeed: Fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241204084453.610660-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index be3eb70cdd..c40d3d8443 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -541,7 +541,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); From 6a0238a4731eaf26ec28f2f7b1d624375135f00c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 4 Dec 2024 16:44:50 +0800 Subject: [PATCH 0052/2892] hw:sdhci: Introduce a new "capareg" class member to set the different Capability Registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, it set the hardcode value of capability registers to all ASPEED SOCs However, the value of capability registers should be different for all ASPEED SOCs. For example: the bit 28 of the Capability Register 1 should be 1 for 64-bits System Bus support for AST2700. Introduce a new "capareg" class member whose data type is uint_64 to set the different Capability Registers to all ASPEED SOCs. The value of Capability Register is "0x0000000001e80080" for AST2400 and AST2500. The value of Capability Register is "0x0000000701f80080" for AST2600. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241204084453.610660-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2400.c | 3 ++- hw/arm/aspeed_ast2600.c | 7 +++--- hw/sd/aspeed_sdhci.c | 47 +++++++++++++++++++++++++++++++++++- include/hw/sd/aspeed_sdhci.h | 12 +++++++-- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index ecc81ecc79..3c1b419945 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -224,7 +224,8 @@ static void aspeed_ast2400_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); object_initialize_child(obj, "gpio", &s->gpio, typename); - object_initialize_child(obj, "sdc", &s->sdhci, TYPE_ASPEED_SDHCI); + snprintf(typename, sizeof(typename), "aspeed.sdhci-%s", socname); + object_initialize_child(obj, "sdc", &s->sdhci, typename); object_property_set_int(OBJECT(&s->sdhci), "num-slots", 2, &error_abort); diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index c40d3d8443..b5703bd064 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -236,8 +236,8 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.gpio-%s-1_8v", socname); object_initialize_child(obj, "gpio_1_8v", &s->gpio_1_8v, typename); - object_initialize_child(obj, "sd-controller", &s->sdhci, - TYPE_ASPEED_SDHCI); + snprintf(typename, sizeof(typename), "aspeed.sdhci-%s", socname); + object_initialize_child(obj, "sd-controller", &s->sdhci, typename); object_property_set_int(OBJECT(&s->sdhci), "num-slots", 2, &error_abort); @@ -247,8 +247,7 @@ static void aspeed_soc_ast2600_init(Object *obj) &s->sdhci.slots[i], TYPE_SYSBUS_SDHCI); } - object_initialize_child(obj, "emmc-controller", &s->emmc, - TYPE_ASPEED_SDHCI); + object_initialize_child(obj, "emmc-controller", &s->emmc, typename); object_property_set_int(OBJECT(&s->emmc), "num-slots", 1, &error_abort); diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index acd6538261..ae2ec4a916 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -148,6 +148,7 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); + AspeedSDHCIClass *asc = ASPEED_SDHCI_GET_CLASS(sdhci); /* Create input irqs for the slots */ qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq, @@ -167,7 +168,7 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) } if (!object_property_set_uint(sdhci_slot, "capareg", - ASPEED_SDHCI_CAPABILITIES, errp)) { + asc->capareg, errp)) { return; } @@ -218,12 +219,56 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) device_class_set_props(dc, aspeed_sdhci_properties); } +static void aspeed_2400_sdhci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2400 SDHCI Controller"; + asc->capareg = 0x0000000001e80080; +} + +static void aspeed_2500_sdhci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2500 SDHCI Controller"; + asc->capareg = 0x0000000001e80080; +} + +static void aspeed_2600_sdhci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2600 SDHCI Controller"; + asc->capareg = 0x0000000701f80080; +} + static const TypeInfo aspeed_sdhci_types[] = { { .name = TYPE_ASPEED_SDHCI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedSDHCIState), .class_init = aspeed_sdhci_class_init, + .class_size = sizeof(AspeedSDHCIClass), + .abstract = true, + }, + { + .name = TYPE_ASPEED_2400_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2400_sdhci_class_init, + }, + { + .name = TYPE_ASPEED_2500_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2500_sdhci_class_init, + }, + { + .name = TYPE_ASPEED_2600_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2600_sdhci_class_init, }, }; diff --git a/include/hw/sd/aspeed_sdhci.h b/include/hw/sd/aspeed_sdhci.h index 057bc5f3d1..8083797e25 100644 --- a/include/hw/sd/aspeed_sdhci.h +++ b/include/hw/sd/aspeed_sdhci.h @@ -13,9 +13,11 @@ #include "qom/object.h" #define TYPE_ASPEED_SDHCI "aspeed.sdhci" -OBJECT_DECLARE_SIMPLE_TYPE(AspeedSDHCIState, ASPEED_SDHCI) +#define TYPE_ASPEED_2400_SDHCI TYPE_ASPEED_SDHCI "-ast2400" +#define TYPE_ASPEED_2500_SDHCI TYPE_ASPEED_SDHCI "-ast2500" +#define TYPE_ASPEED_2600_SDHCI TYPE_ASPEED_SDHCI "-ast2600" +OBJECT_DECLARE_TYPE(AspeedSDHCIState, AspeedSDHCIClass, ASPEED_SDHCI) -#define ASPEED_SDHCI_CAPABILITIES 0x01E80080 #define ASPEED_SDHCI_NUM_SLOTS 2 #define ASPEED_SDHCI_NUM_REGS (ASPEED_SDHCI_REG_SIZE / sizeof(uint32_t)) #define ASPEED_SDHCI_REG_SIZE 0x100 @@ -32,4 +34,10 @@ struct AspeedSDHCIState { uint32_t regs[ASPEED_SDHCI_NUM_REGS]; }; +struct AspeedSDHCIClass { + SysBusDeviceClass parent_class; + + uint64_t capareg; +}; + #endif /* ASPEED_SDHCI_H */ From 83991b9104b68b82a8506fc48b58a799a6e97d10 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 4 Dec 2024 16:44:51 +0800 Subject: [PATCH 0053/2892] hw/sd/aspeed_sdhci: Add AST2700 Support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new ast2700 class to support AST2700. Add a new ast2700 SDHCI class init function and set the value of capability register to "0x0000000719f80080". Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241204084453.610660-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/sd/aspeed_sdhci.c | 14 ++++++++++++++ include/hw/sd/aspeed_sdhci.h | 1 + 2 files changed, 15 insertions(+) diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index ae2ec4a916..f82b05397e 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -246,6 +246,15 @@ static void aspeed_2600_sdhci_class_init(ObjectClass *klass, void *data) asc->capareg = 0x0000000701f80080; } +static void aspeed_2700_sdhci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2700 SDHCI Controller"; + asc->capareg = 0x0000000719f80080; +} + static const TypeInfo aspeed_sdhci_types[] = { { .name = TYPE_ASPEED_SDHCI, @@ -270,6 +279,11 @@ static const TypeInfo aspeed_sdhci_types[] = { .parent = TYPE_ASPEED_SDHCI, .class_init = aspeed_2600_sdhci_class_init, }, + { + .name = TYPE_ASPEED_2700_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2700_sdhci_class_init, + }, }; DEFINE_TYPES(aspeed_sdhci_types) diff --git a/include/hw/sd/aspeed_sdhci.h b/include/hw/sd/aspeed_sdhci.h index 8083797e25..4ef1770471 100644 --- a/include/hw/sd/aspeed_sdhci.h +++ b/include/hw/sd/aspeed_sdhci.h @@ -16,6 +16,7 @@ #define TYPE_ASPEED_2400_SDHCI TYPE_ASPEED_SDHCI "-ast2400" #define TYPE_ASPEED_2500_SDHCI TYPE_ASPEED_SDHCI "-ast2500" #define TYPE_ASPEED_2600_SDHCI TYPE_ASPEED_SDHCI "-ast2600" +#define TYPE_ASPEED_2700_SDHCI TYPE_ASPEED_SDHCI "-ast2700" OBJECT_DECLARE_TYPE(AspeedSDHCIState, AspeedSDHCIClass, ASPEED_SDHCI) #define ASPEED_SDHCI_NUM_SLOTS 2 From 0696e9b00e998e7a44546f2046f49ecbc21e8d8b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 4 Dec 2024 16:44:52 +0800 Subject: [PATCH 0054/2892] aspeed/soc: Support SDHCI for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SDHCI model for AST2700 SDHCI support. The SDHCI controller only support 1 slot and registers base address is start at 0x1408_0000 and its interrupt is connected to GICINT133_INTC at bit 1. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241204084453.610660-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 63d1fcb086..baddd35ecf 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -65,6 +65,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_I2C] = 0x14C0F000, [ASPEED_DEV_GPIO] = 0x14C0B000, [ASPEED_DEV_RTC] = 0x12C0F000, + [ASPEED_DEV_SDHCI] = 0x14080000, }; #define AST2700_MAX_IRQ 256 @@ -113,6 +114,7 @@ static const int aspeed_soc_ast2700_irqmap[] = { [ASPEED_DEV_KCS] = 128, [ASPEED_DEV_DP] = 28, [ASPEED_DEV_I3C] = 131, + [ASPEED_DEV_SDHCI] = 133, }; /* GICINT 128 */ @@ -158,6 +160,7 @@ static const int aspeed_soc_ast2700_gic132_intcmap[] = { /* GICINT 133 */ static const int aspeed_soc_ast2700_gic133_intcmap[] = { + [ASPEED_DEV_SDHCI] = 1, [ASPEED_DEV_PECI] = 4, }; @@ -380,6 +383,14 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "gpio", &s->gpio, typename); object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); + + snprintf(typename, sizeof(typename), "aspeed.sdhci-%s", socname); + object_initialize_child(obj, "sd-controller", &s->sdhci, typename); + object_property_set_int(OBJECT(&s->sdhci), "num-slots", 1, &error_abort); + + /* Init sd card slot class here so that they're under the correct parent */ + object_initialize_child(obj, "sd-controller.sdhci", + &s->sdhci.slots[0], TYPE_SYSBUS_SDHCI); } /* @@ -681,6 +692,15 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); + /* SDHCI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, + sc->memmap[ASPEED_DEV_SDHCI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); From c38c53f22c3d0169ae0f663bb6a55f06f9f9c19b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 4 Dec 2024 16:44:53 +0800 Subject: [PATCH 0055/2892] aspeed/soc: Support eMMC for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SDHCI model for AST2700 eMMC support. The eMMC controller only support 1 slot and registers base address is start at 0x1209_0000 and its interrupt is connected to GICINT 15. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241204084453.610660-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index baddd35ecf..23571584b2 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -391,6 +391,12 @@ static void aspeed_soc_ast2700_init(Object *obj) /* Init sd card slot class here so that they're under the correct parent */ object_initialize_child(obj, "sd-controller.sdhci", &s->sdhci.slots[0], TYPE_SYSBUS_SDHCI); + + object_initialize_child(obj, "emmc-controller", &s->emmc, typename); + object_property_set_int(OBJECT(&s->emmc), "num-slots", 1, &error_abort); + + object_initialize_child(obj, "emmc-controller.sdhci", &s->emmc.slots[0], + TYPE_SYSBUS_SDHCI); } /* @@ -701,6 +707,15 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); + /* eMMC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->emmc), 0, + sc->memmap[ASPEED_DEV_EMMC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); From e50c72858da7d38790a2931f98c1f9cfd59f325a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:26 +0100 Subject: [PATCH 0056/2892] tests/functional: Introduce a specific test for ast1030 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simply moves the ast1030 tests to a new test file. No changes. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-2-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 1 + tests/functional/test_arm_aspeed.py | 64 ---------------- tests/functional/test_arm_aspeed_ast1030.py | 81 +++++++++++++++++++++ 3 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 tests/functional/test_arm_aspeed_ast1030.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index d6d2c0196c..66f10da99d 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -66,6 +66,7 @@ tests_alpha_system_thorough = [ tests_arm_system_thorough = [ 'arm_aspeed', + 'arm_aspeed_ast1030', 'arm_bpim2u', 'arm_canona1100', 'arm_collie', diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py index d88170ac24..9e58fcd840 100755 --- a/tests/functional/test_arm_aspeed.py +++ b/tests/functional/test_arm_aspeed.py @@ -19,70 +19,6 @@ from qemu_test.utils import archive_extract from zipfile import ZipFile from unittest import skipUnless -class AST1030Machine(LinuxKernelTest): - - ASSET_ZEPHYR_1_04 = Asset( - ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip'), - '4ac6210adcbc61294927918707c6762483fd844dde5e07f3ba834ad1f91434d3') - - def test_ast1030_zephyros_1_04(self): - self.set_machine('ast1030-evb') - - zip_file = self.ASSET_ZEPHYR_1_04.fetch() - - kernel_name = "ast1030-evb-demo/zephyr.elf" - with ZipFile(zip_file, 'r') as zf: - zf.extract(kernel_name, path=self.workdir) - kernel_file = os.path.join(self.workdir, kernel_name) - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_file, '-nographic') - self.vm.launch() - self.wait_for_console_pattern("Booting Zephyr OS") - exec_command_and_wait_for_pattern(self, "help", - "Available commands") - - ASSET_ZEPHYR_1_07 = Asset( - ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip'), - 'ad52e27959746988afaed8429bf4e12ab988c05c4d07c9d90e13ec6f7be4574c') - - def test_ast1030_zephyros_1_07(self): - self.set_machine('ast1030-evb') - - zip_file = self.ASSET_ZEPHYR_1_07.fetch() - - kernel_name = "ast1030-evb-demo/zephyr.bin" - with ZipFile(zip_file, 'r') as zf: - zf.extract(kernel_name, path=self.workdir) - kernel_file = os.path.join(self.workdir, kernel_name) - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_file, '-nographic') - self.vm.launch() - self.wait_for_console_pattern("Booting Zephyr OS") - for shell_cmd in [ - 'kernel stacks', - 'otp info conf', - 'otp info scu', - 'hwinfo devid', - 'crypto aes256_cbc_vault', - 'random get', - 'jtag JTAG1 sw_xfer high TMS', - 'adc ADC0 resolution 12', - 'adc ADC0 read 42', - 'adc ADC1 read 69', - 'i2c scan I2C_0', - 'i3c attach I3C_0', - 'hash test', - 'kernel uptime', - 'kernel reboot warm', - 'kernel uptime', - 'kernel reboot cold', - 'kernel uptime', - ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") - class AST2x00Machine(LinuxKernelTest): def do_test_arm_aspeed(self, machine, image): diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py new file mode 100644 index 0000000000..380a76ec01 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast1030.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from zipfile import ZipFile + +class AST1030Machine(LinuxKernelTest): + + ASSET_ZEPHYR_1_04 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip'), + '4ac6210adcbc61294927918707c6762483fd844dde5e07f3ba834ad1f91434d3') + + def test_ast1030_zephyros_1_04(self): + self.set_machine('ast1030-evb') + + zip_file = self.ASSET_ZEPHYR_1_04.fetch() + + kernel_name = "ast1030-evb-demo/zephyr.elf" + with ZipFile(zip_file, 'r') as zf: + zf.extract(kernel_name, path=self.workdir) + kernel_file = os.path.join(self.workdir, kernel_name) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, '-nographic') + self.vm.launch() + self.wait_for_console_pattern("Booting Zephyr OS") + exec_command_and_wait_for_pattern(self, "help", + "Available commands") + + ASSET_ZEPHYR_1_07 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip'), + 'ad52e27959746988afaed8429bf4e12ab988c05c4d07c9d90e13ec6f7be4574c') + + def test_ast1030_zephyros_1_07(self): + self.set_machine('ast1030-evb') + + zip_file = self.ASSET_ZEPHYR_1_07.fetch() + + kernel_name = "ast1030-evb-demo/zephyr.bin" + with ZipFile(zip_file, 'r') as zf: + zf.extract(kernel_name, path=self.workdir) + kernel_file = os.path.join(self.workdir, kernel_name) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, '-nographic') + self.vm.launch() + self.wait_for_console_pattern("Booting Zephyr OS") + for shell_cmd in [ + 'kernel stacks', + 'otp info conf', + 'otp info scu', + 'hwinfo devid', + 'crypto aes256_cbc_vault', + 'random get', + 'jtag JTAG1 sw_xfer high TMS', + 'adc ADC0 resolution 12', + 'adc ADC0 read 42', + 'adc ADC1 read 69', + 'i2c scan I2C_0', + 'i3c attach I3C_0', + 'hash test', + 'kernel uptime', + 'kernel reboot warm', + 'kernel uptime', + 'kernel reboot cold', + 'kernel uptime', + ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") + + +if __name__ == '__main__': + LinuxKernelTest.main() From e517cff70625c55e10e9e9b96b6f59865a7a1248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:27 +0100 Subject: [PATCH 0057/2892] tests/functional: Introduce a specific test for palmetto-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a new aspeed module for sharing code between tests and moves the palmetto test to a new test file. No changes in the test. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-3-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 23 +++++++++++++++++++ tests/functional/meson.build | 2 ++ tests/functional/test_arm_aspeed.py | 10 -------- tests/functional/test_arm_aspeed_palmetto.py | 24 ++++++++++++++++++++ 4 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 tests/functional/aspeed.py create mode 100644 tests/functional/test_arm_aspeed_palmetto.py diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py new file mode 100644 index 0000000000..d4dc5320b9 --- /dev/null +++ b/tests/functional/aspeed.py @@ -0,0 +1,23 @@ +# Test class to boot aspeed machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest + +class AspeedTest(LinuxKernelTest): + + def do_test_arm_aspeed(self, machine, image): + self.set_machine(machine) + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern("U-Boot 2016.07") + self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") + self.wait_for_console_pattern("Starting kernel ...") + self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") + self.wait_for_console_pattern( + "aspeed-smc 1e620000.spi: read control register: 203b0641") + self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") + self.wait_for_console_pattern("systemd[1]: Set hostname to") diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 66f10da99d..4752dca9d6 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -18,6 +18,7 @@ test_timeouts = { 'aarch64_tuxrun' : 240, 'aarch64_virt' : 720, 'acpi_bits' : 420, + 'arm_aspeed_palmetto' : 120, 'arm_aspeed' : 600, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -67,6 +68,7 @@ tests_alpha_system_thorough = [ tests_arm_system_thorough = [ 'arm_aspeed', 'arm_aspeed_ast1030', + 'arm_aspeed_palmetto', 'arm_bpim2u', 'arm_canona1100', 'arm_collie', diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py index 9e58fcd840..48a229608e 100755 --- a/tests/functional/test_arm_aspeed.py +++ b/tests/functional/test_arm_aspeed.py @@ -37,16 +37,6 @@ class AST2x00Machine(LinuxKernelTest): self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") self.wait_for_console_pattern("systemd[1]: Set hostname to") - ASSET_PALMETTO_FLASH = Asset( - ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-palmetto.static.mtd'), - '3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d'); - - def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): - image_path = self.ASSET_PALMETTO_FLASH.fetch() - - self.do_test_arm_aspeed('palmetto-bmc', image_path) - ASSET_ROMULUS_FLASH = Asset( ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' 'obmc-phosphor-image-romulus.static.mtd'), diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/test_arm_aspeed_palmetto.py new file mode 100644 index 0000000000..6588c02aad --- /dev/null +++ b/tests/functional/test_arm_aspeed_palmetto.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + +class PalmettoMachine(AspeedTest): + + ASSET_PALMETTO_FLASH = Asset( + ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' + 'obmc-phosphor-image-palmetto.static.mtd'), + '3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d'); + + def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): + image_path = self.ASSET_PALMETTO_FLASH.fetch() + + self.do_test_arm_aspeed('palmetto-bmc', image_path) + + +if __name__ == '__main__': + AspeedTest.main() From 5f2b9738b281894d3fdc0affbdcada2f8c0cc19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:28 +0100 Subject: [PATCH 0058/2892] tests/functional: Introduce a specific test for romulus-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simply moves the romulus-bmc test to a new test file. No changes in the test. The do_test_arm_aspeed routine is removed from the test_arm_aspeed.py file because it is now unused. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-4-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 2 ++ tests/functional/test_arm_aspeed.py | 26 --------------------- tests/functional/test_arm_aspeed_romulus.py | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 26 deletions(-) create mode 100644 tests/functional/test_arm_aspeed_romulus.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 4752dca9d6..e9ec5af0f1 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -19,6 +19,7 @@ test_timeouts = { 'aarch64_virt' : 720, 'acpi_bits' : 420, 'arm_aspeed_palmetto' : 120, + 'arm_aspeed_romulus' : 120, 'arm_aspeed' : 600, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -69,6 +70,7 @@ tests_arm_system_thorough = [ 'arm_aspeed', 'arm_aspeed_ast1030', 'arm_aspeed_palmetto', + 'arm_aspeed_romulus', 'arm_bpim2u', 'arm_canona1100', 'arm_collie', diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py index 48a229608e..bdc000a00a 100755 --- a/tests/functional/test_arm_aspeed.py +++ b/tests/functional/test_arm_aspeed.py @@ -21,32 +21,6 @@ from unittest import skipUnless class AST2x00Machine(LinuxKernelTest): - def do_test_arm_aspeed(self, machine, image): - self.set_machine(machine) - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-snapshot') - self.vm.launch() - - self.wait_for_console_pattern("U-Boot 2016.07") - self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") - self.wait_for_console_pattern("Starting kernel ...") - self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") - self.wait_for_console_pattern( - "aspeed-smc 1e620000.spi: read control register: 203b0641") - self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") - self.wait_for_console_pattern("systemd[1]: Set hostname to") - - ASSET_ROMULUS_FLASH = Asset( - ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-romulus.static.mtd'), - '820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') - - def test_arm_ast2500_romulus_openbmc_v2_9_0(self): - image_path = self.ASSET_ROMULUS_FLASH.fetch() - - self.do_test_arm_aspeed('romulus-bmc', image_path) - def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): self.require_netdev('user') self.vm.set_console() diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/test_arm_aspeed_romulus.py new file mode 100644 index 0000000000..747b616201 --- /dev/null +++ b/tests/functional/test_arm_aspeed_romulus.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + +class RomulusMachine(AspeedTest): + + ASSET_ROMULUS_FLASH = Asset( + ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' + 'obmc-phosphor-image-romulus.static.mtd'), + '820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') + + def test_arm_ast2500_romulus_openbmc_v2_9_0(self): + image_path = self.ASSET_ROMULUS_FLASH.fetch() + + self.do_test_arm_aspeed('romulus-bmc', image_path) + + +if __name__ == '__main__': + AspeedTest.main() From 08743dbaa12e0e3c1622bfcdd6cbb7eb03d51ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:29 +0100 Subject: [PATCH 0059/2892] tests/functional: Introduce a specific test for ast2500 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves the ast2500-evb tests to a new test file and extends the aspeed module with routines used to run the buildroot and sdk tests. No changes in the test. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-5-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 33 ++++++++++++ tests/functional/meson.build | 2 + tests/functional/test_arm_aspeed.py | 44 --------------- tests/functional/test_arm_aspeed_ast2500.py | 59 +++++++++++++++++++++ 4 files changed, 94 insertions(+), 44 deletions(-) create mode 100644 tests/functional/test_arm_aspeed_ast2500.py diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index d4dc5320b9..62f50bab7a 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +from qemu_test import exec_command_and_wait_for_pattern from qemu_test import LinuxKernelTest class AspeedTest(LinuxKernelTest): @@ -21,3 +22,35 @@ class AspeedTest(LinuxKernelTest): "aspeed-smc 1e620000.spi: read control register: 203b0641") self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") self.wait_for_console_pattern("systemd[1]: Set hostname to") + + def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw,read-only=true', + '-net', 'nic', '-net', 'user') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id) + self.wait_for_console_pattern('lease of 10.0.2.15') + # the line before login: + self.wait_for_console_pattern(pattern) + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, 'passw0rd', '#') + + def do_test_arm_aspeed_buildroot_poweroff(self): + exec_command_and_wait_for_pattern(self, 'poweroff', + 'reboot: System halted'); + + def do_test_arm_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e9ec5af0f1..cb97d2e3a0 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -20,6 +20,7 @@ test_timeouts = { 'acpi_bits' : 420, 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, + 'arm_aspeed_ast2500' : 480, 'arm_aspeed' : 600, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -71,6 +72,7 @@ tests_arm_system_thorough = [ 'arm_aspeed_ast1030', 'arm_aspeed_palmetto', 'arm_aspeed_romulus', + 'arm_aspeed_ast2500', 'arm_bpim2u', 'arm_canona1100', 'arm_collie', diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py index bdc000a00a..48cf0bfb27 100755 --- a/tests/functional/test_arm_aspeed.py +++ b/tests/functional/test_arm_aspeed.py @@ -41,34 +41,6 @@ class AST2x00Machine(LinuxKernelTest): def do_test_arm_aspeed_buildroot_poweroff(self): exec_command_and_wait_for_pattern(self, 'poweroff', 'reboot: System halted'); - - ASSET_BR2_202311_AST2500_FLASH = Asset( - ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2500-evb/buildroot-2023.11/flash.img'), - 'c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f') - - def test_arm_ast2500_evb_buildroot(self): - self.set_machine('ast2500-evb') - - image_path = self.ASSET_BR2_202311_AST2500_FLASH.fetch() - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); - self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', - 'ast2500-evb login:') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') - - self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_BR2_202311_AST2600_FLASH = Asset( ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' 'images/ast2600-evb/buildroot-2023.11/flash.img'), @@ -161,22 +133,6 @@ class AST2x00Machine(LinuxKernelTest): self.wait_for_console_pattern('## Loading kernel from FIT Image') self.wait_for_console_pattern('Starting kernel ...') - ASSET_SDK_V806_AST2500 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2500-default-obmc.tar.gz', - 'e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') - - def test_arm_ast2500_evb_sdk(self): - self.set_machine('ast2500-evb') - - image_path = self.ASSET_SDK_V806_AST2500.fetch() - - archive_extract(image_path, self.workdir) - - self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2500-default/image-bmc') - - self.wait_for_console_pattern('ast2500-default login:') - ASSET_SDK_V806_AST2600_A2 = Asset( 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2600-a2-obmc.tar.gz', '9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py new file mode 100644 index 0000000000..79baf37537 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import archive_extract + +class AST2500Machine(AspeedTest): + + ASSET_BR2_202311_AST2500_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2500-evb/buildroot-2023.11/flash.img'), + 'c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f') + + def test_arm_ast2500_evb_buildroot(self): + self.set_machine('ast2500-evb') + + image_path = self.ASSET_BR2_202311_AST2500_FLASH.fetch() + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', + 'ast2500-evb login:') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') + + self.do_test_arm_aspeed_buildroot_poweroff() + + ASSET_SDK_V806_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2500-default-obmc.tar.gz', + 'e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') + + def test_arm_ast2500_evb_sdk(self): + self.set_machine('ast2500-evb') + + image_path = self.ASSET_SDK_V806_AST2500.fetch() + + archive_extract(image_path, self.workdir) + + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2500-default/image-bmc') + + self.wait_for_console_pattern('ast2500-default login:') + + +if __name__ == '__main__': + AspeedTest.main() From c7bc9cab3f5e2c0f807d801d09d0bbe95c4ce825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:30 +0100 Subject: [PATCH 0060/2892] tests/functional: Introduce a specific test for ast2600 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves the ast2600-evb tests to a new test file. No changes in the test. The routines used to run the buildroot and sdk tests are removed from the test_arm_aspeed.py file because now unused. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-6-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 2 + tests/functional/test_arm_aspeed.py | 155 -------------------- tests/functional/test_arm_aspeed_ast2600.py | 143 ++++++++++++++++++ 3 files changed, 145 insertions(+), 155 deletions(-) create mode 100644 tests/functional/test_arm_aspeed_ast2600.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index cb97d2e3a0..b5fd3beded 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -21,6 +21,7 @@ test_timeouts = { 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, 'arm_aspeed_ast2500' : 480, + 'arm_aspeed_ast2600' : 720, 'arm_aspeed' : 600, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -73,6 +74,7 @@ tests_arm_system_thorough = [ 'arm_aspeed_palmetto', 'arm_aspeed_romulus', 'arm_aspeed_ast2500', + 'arm_aspeed_ast2600', 'arm_bpim2u', 'arm_canona1100', 'arm_collie', diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py index 48cf0bfb27..8cf86795af 100755 --- a/tests/functional/test_arm_aspeed.py +++ b/tests/functional/test_arm_aspeed.py @@ -19,161 +19,6 @@ from qemu_test.utils import archive_extract from zipfile import ZipFile from unittest import skipUnless -class AST2x00Machine(LinuxKernelTest): - - def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): - self.require_netdev('user') - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw,read-only=true', - '-net', 'nic', '-net', 'user') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2019.04') - self.wait_for_console_pattern('## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id) - self.wait_for_console_pattern('lease of 10.0.2.15') - # the line before login: - self.wait_for_console_pattern(pattern) - exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, 'passw0rd', '#') - - def do_test_arm_aspeed_buildroot_poweroff(self): - exec_command_and_wait_for_pattern(self, 'poweroff', - 'reboot: System halted'); - ASSET_BR2_202311_AST2600_FLASH = Asset( - ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2600-evb/buildroot-2023.11/flash.img'), - 'b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68') - - def test_arm_ast2600_evb_buildroot(self): - self.set_machine('ast2600-evb') - - image_path = self.ASSET_BR2_202311_AST2600_FLASH.fetch() - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); - self.vm.add_args('-device', - 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42'); - self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', - 'ast2600-evb login:') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') - - exec_command_and_wait_for_pattern(self, - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32'); - year = time.strftime("%Y") - exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); - - exec_command_and_wait_for_pattern(self, - 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', - 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64'); - exec_command_and_wait_for_pattern(self, - 'i2cset -y 3 0x42 0x64 0x00 0xaa i', '#'); - exec_command_and_wait_for_pattern(self, - 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', - '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff'); - self.do_test_arm_aspeed_buildroot_poweroff() - - ASSET_BR2_202302_AST2600_TPM_FLASH = Asset( - ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img'), - 'a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') - - @skipUnless(*has_cmd('swtpm')) - def test_arm_ast2600_evb_buildroot_tpm(self): - self.set_machine('ast2600-evb') - - image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch() - - tpmstate_dir = tempfile.TemporaryDirectory(prefix="qemu_") - socket = os.path.join(tpmstate_dir.name, 'swtpm-socket') - - # We must put the TPM state dir in /tmp/, not the build dir, - # because some distros use AppArmor to lock down swtpm and - # restrict the set of locations it can access files in. - subprocess.run(['swtpm', 'socket', '-d', '--tpm2', - '--tpmstate', f'dir={tpmstate_dir.name}', - '--ctrl', f'type=unixio,path={socket}']) - - self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}') - self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm') - self.vm.add_args('-device', - 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e') - self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') - - exec_command_and_wait_for_pattern(self, - 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', - 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/tpm/tpm0/pcr-sha256/0', - 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0'); - - self.do_test_arm_aspeed_buildroot_poweroff() - - def do_test_arm_aspeed_sdk_start(self, image): - self.require_netdev('user') - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-net', 'user', '-snapshot') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2019.04') - self.wait_for_console_pattern('## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - - ASSET_SDK_V806_AST2600_A2 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2600-a2-obmc.tar.gz', - '9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') - - def test_arm_ast2600_evb_sdk(self): - self.set_machine('ast2600-evb') - - image_path = self.ASSET_SDK_V806_AST2600_A2.fetch() - - archive_extract(image_path, self.workdir) - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); - self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2600-a2/image-bmc') - - self.wait_for_console_pattern('ast2600-a2 login:') - - exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, '0penBmc', 'root@ast2600-a2:~#') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') - - exec_command_and_wait_for_pattern(self, - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); - year = time.strftime("%Y") - exec_command_and_wait_for_pattern(self, - '/sbin/hwclock -f /dev/rtc1', year); - - class AST2x00MachineMMC(LinuxKernelTest): ASSET_RAINIER_EMMC = Asset( diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py new file mode 100644 index 0000000000..74d025e0fc --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time +import tempfile +import subprocess + +from qemu_test import Asset +from aspeed import AspeedTest +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import has_cmd +from qemu_test.utils import archive_extract +from unittest import skipUnless + +class AST2600Machine(AspeedTest): + + ASSET_BR2_202311_AST2600_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.11/flash.img'), + 'b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68') + + def test_arm_ast2600_evb_buildroot(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202311_AST2600_FLASH.fetch() + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); + self.vm.add_args('-device', + 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42'); + self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', + 'ast2600-evb login:') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); + + exec_command_and_wait_for_pattern(self, + 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', + 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64'); + exec_command_and_wait_for_pattern(self, + 'i2cset -y 3 0x42 0x64 0x00 0xaa i', '#'); + exec_command_and_wait_for_pattern(self, + 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', + '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff'); + self.do_test_arm_aspeed_buildroot_poweroff() + + ASSET_BR2_202302_AST2600_TPM_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img'), + 'a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') + + @skipUnless(*has_cmd('swtpm')) + def test_arm_ast2600_evb_buildroot_tpm(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch() + + tpmstate_dir = tempfile.TemporaryDirectory(prefix="qemu_") + socket = os.path.join(tpmstate_dir.name, 'swtpm-socket') + + # We must put the TPM state dir in /tmp/, not the build dir, + # because some distros use AppArmor to lock down swtpm and + # restrict the set of locations it can access files in. + subprocess.run(['swtpm', 'socket', '-d', '--tpm2', + '--tpmstate', f'dir={tpmstate_dir.name}', + '--ctrl', f'type=unixio,path={socket}']) + + self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}') + self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm') + self.vm.add_args('-device', + 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e') + self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') + + exec_command_and_wait_for_pattern(self, + 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', + 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/tpm/tpm0/pcr-sha256/0', + 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0'); + + self.do_test_arm_aspeed_buildroot_poweroff() + + ASSET_SDK_V806_AST2600_A2 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2600-a2-obmc.tar.gz', + '9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') + + def test_arm_ast2600_evb_sdk(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_SDK_V806_AST2600_A2.fetch() + + archive_extract(image_path, self.workdir) + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2600-a2/image-bmc') + + self.wait_for_console_pattern('ast2600-a2 login:') + + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', 'root@ast2600-a2:~#') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, + '/sbin/hwclock -f /dev/rtc1', year); + +if __name__ == '__main__': + AspeedTest.main() From 38cd5c5235cb3bdca5a45f4b7fb4bf3d1c0ad3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:31 +0100 Subject: [PATCH 0061/2892] tests/functional: Introduce a specific test for rainier-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simply moves the rainier-bmc test to a new test file. No changes in the test. The test_arm_aspeed.py is deleted. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-7-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 4 ++-- ...m_aspeed.py => test_arm_aspeed_rainier.py} | 22 +++++-------------- 2 files changed, 7 insertions(+), 19 deletions(-) rename tests/functional/{test_arm_aspeed.py => test_arm_aspeed_rainier.py} (71%) mode change 100755 => 100644 diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b5fd3beded..96f2291a39 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -22,7 +22,7 @@ test_timeouts = { 'arm_aspeed_romulus' : 120, 'arm_aspeed_ast2500' : 480, 'arm_aspeed_ast2600' : 720, - 'arm_aspeed' : 600, + 'arm_aspeed_rainier' : 240, 'arm_bpim2u' : 500, 'arm_collie' : 180, 'arm_orangepi' : 540, @@ -69,12 +69,12 @@ tests_alpha_system_thorough = [ ] tests_arm_system_thorough = [ - 'arm_aspeed', 'arm_aspeed_ast1030', 'arm_aspeed_palmetto', 'arm_aspeed_romulus', 'arm_aspeed_ast2500', 'arm_aspeed_ast2600', + 'arm_aspeed_rainier', 'arm_bpim2u', 'arm_canona1100', 'arm_collie', diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed_rainier.py old mode 100755 new mode 100644 similarity index 71% rename from tests/functional/test_arm_aspeed.py rename to tests/functional/test_arm_aspeed_rainier.py index 8cf86795af..a60274926d --- a/tests/functional/test_arm_aspeed.py +++ b/tests/functional/test_arm_aspeed_rainier.py @@ -1,25 +1,13 @@ #!/usr/bin/env python3 # -# Functional test that boots the ASPEED SoCs with firmware -# -# Copyright (C) 2022 ASPEED Technology Inc +# Functional test that boots the ASPEED machines # # SPDX-License-Identifier: GPL-2.0-or-later -import os -import time -import subprocess -import tempfile +from qemu_test import Asset +from aspeed import AspeedTest -from qemu_test import LinuxKernelTest, Asset -from qemu_test import exec_command_and_wait_for_pattern -from qemu_test import interrupt_interactive_console_until_pattern -from qemu_test import has_cmd -from qemu_test.utils import archive_extract -from zipfile import ZipFile -from unittest import skipUnless - -class AST2x00MachineMMC(LinuxKernelTest): +class RainierMachine(AspeedTest): ASSET_RAINIER_EMMC = Asset( ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/' @@ -49,4 +37,4 @@ class AST2x00MachineMMC(LinuxKernelTest): self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise') if __name__ == '__main__': - LinuxKernelTest.main() + AspeedTest.main() From 3cbbd9cb93d0f954e09478bcf72d177b893883af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Dec 2024 14:11:32 +0100 Subject: [PATCH 0062/2892] tests/functional: Move debian boot test from avocado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simply moves the debian boot test from the avocado testsuite to the new functional testsuite. No changes in the test. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20241206131132.520911-8-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/avocado/boot_linux_console.py | 26 --------------------- tests/functional/test_arm_aspeed_rainier.py | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 12e24bb05a..738dd5a8c4 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -470,29 +470,3 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') self.wait_for_console_pattern( 'Give root password for system maintenance') - - def test_arm_ast2600_debian(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:rainier-bmc - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20220606T211338Z/' - 'pool/main/l/linux/' - 'linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb') - deb_hash = '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash, - algorithm='sha256') - kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp') - dtb_path = self.extract_from_deb(deb_path, - '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-net', 'nic') - self.vm.launch() - self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00") - self.wait_for_console_pattern("SMP: Total of 2 processors activated") - self.wait_for_console_pattern("No filesystem could mount root") - diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/test_arm_aspeed_rainier.py index a60274926d..b856aea6db 100644 --- a/tests/functional/test_arm_aspeed_rainier.py +++ b/tests/functional/test_arm_aspeed_rainier.py @@ -36,5 +36,29 @@ class RainierMachine(AspeedTest): self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7') self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise') + ASSET_DEBIAN_LINUX_ARMHF_DEB = Asset( + ('http://snapshot.debian.org/archive/debian/20220606T211338Z/pool/main/l/linux/linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb'), + '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e') + + def test_arm_debian_kernel_boot(self): + self.set_machine('rainier-bmc') + + deb_path = self.ASSET_DEBIAN_LINUX_ARMHF_DEB.fetch() + + kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp') + dtb_path = self.extract_from_deb(deb_path, + '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-net', 'nic') + self.vm.launch() + + self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00") + self.wait_for_console_pattern("SMP: Total of 2 processors activated") + self.wait_for_console_pattern("No filesystem could mount root") + + if __name__ == '__main__': AspeedTest.main() From 755e984aa4af97070a3f63812e9ed33f729dda9b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:34 +0800 Subject: [PATCH 0063/2892] test/qtest/aspeed_smc-test: Move testcases to test_palmetto_bmc function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far, the test cases are used for testing SMC model with AST2400 BMC. However, AST2400 is end off live and ASPEED is no longer support this SOC. To test SMC model for AST2500, AST2600 and AST1030, move the test cases from main to test_palmetto_bmc function. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 4673371d95..ec1fa6ec15 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -610,14 +610,12 @@ static void test_write_block_protect_bottom_bit(void) flash_reset(); } -int main(int argc, char **argv) +static int test_palmetto_bmc(void) { g_autofree char *tmp_path = NULL; int ret; int fd; - g_test_init(&argc, &argv, NULL); - fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL); g_assert(fd >= 0); ret = ftruncate(fd, FLASH_SIZE); @@ -644,8 +642,18 @@ int main(int argc, char **argv) flash_reset(); ret = g_test_run(); - qtest_quit(global_qtest); unlink(tmp_path); + + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + ret = test_palmetto_bmc(); + return ret; } From 975d4baf686c1bf16daf0277c25cd6f494df0c68 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:35 +0800 Subject: [PATCH 0064/2892] test/qtest/aspeed_smc-test: Introduce a new TestData to test different BMC SOCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, these test cases are only used for testing fmc_cs0 for AST2400. To test others BMC SOCs, introduces a new TestData structure. Users can set the spi base address, flash base address, jedesc id and so on for different BMC SOCs and flash model testing. Introduce new helper functions to make the test case more readable. Set spi base address 0x1E620000, flash_base address 0x20000000 and jedec id 0x20ba19 for fmc_cs0 with n25q256a flash for AST2400 SMC model testing. To pass the TestData into the test case, replace qtest_add_func with qtest_add_data_func. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 546 +++++++++++++++++++--------------- 1 file changed, 299 insertions(+), 247 deletions(-) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index ec1fa6ec15..4c62009605 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -43,9 +43,6 @@ #define CTRL_USERMODE 0x3 #define SR_WEL BIT(1) -#define ASPEED_FMC_BASE 0x1E620000 -#define ASPEED_FLASH_BASE 0x20000000 - /* * Flash commands */ @@ -65,11 +62,16 @@ enum { ERASE_SECTOR = 0xd8, }; -#define FLASH_JEDEC 0x20ba19 /* n25q256a */ -#define FLASH_SIZE (32 * 1024 * 1024) - #define FLASH_PAGE_SIZE 256 +typedef struct TestData { + QTestState *s; + uint64_t spi_base; + uint64_t flash_base; + uint32_t jedec_id; + char *tmp_path; +} TestData; + /* * Use an explicit bswap for the values read/wrote to the flash region * as they are BE and the Aspeed CPU is LE. @@ -79,275 +81,315 @@ static inline uint32_t make_be32(uint32_t data) return bswap32(data); } -static void spi_conf(uint32_t value) +static inline void spi_writel(const TestData *data, uint64_t offset, + uint32_t value) { - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CONF, conf); + qtest_writel(data->s, data->spi_base + offset, value); } -static void spi_conf_remove(uint32_t value) +static inline uint32_t spi_readl(const TestData *data, uint64_t offset) { - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); + return qtest_readl(data->s, data->spi_base + offset); +} + +static inline void flash_writeb(const TestData *data, uint64_t offset, + uint8_t value) +{ + qtest_writeb(data->s, data->flash_base + offset, value); +} + +static inline void flash_writel(const TestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->flash_base + offset, value); +} + +static inline uint8_t flash_readb(const TestData *data, uint64_t offset) +{ + return qtest_readb(data->s, data->flash_base + offset); +} + +static inline uint32_t flash_readl(const TestData *data, uint64_t offset) +{ + return qtest_readl(data->s, data->flash_base + offset); +} + +static void spi_conf(const TestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf |= value; + spi_writel(data, R_CONF, conf); +} + +static void spi_conf_remove(const TestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); conf &= ~value; - writel(ASPEED_FMC_BASE + R_CONF, conf); + spi_writel(data, R_CONF, conf); } -static void spi_ce_ctrl(uint32_t value) +static void spi_ce_ctrl(const TestData *data, uint32_t value) { - uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL); + uint32_t conf = spi_readl(data, R_CE_CTRL); conf |= value; - writel(ASPEED_FMC_BASE + R_CE_CTRL, conf); + spi_writel(data, R_CE_CTRL, conf); } -static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd) +static void spi_ctrl_setmode(const TestData *data, uint8_t mode, uint8_t cmd) { - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); + uint32_t ctrl = spi_readl(data, R_CTRL0); ctrl &= ~(CTRL_USERMODE | 0xff << 16); ctrl |= mode | (cmd << 16); - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); + spi_writel(data, R_CTRL0, ctrl); } -static void spi_ctrl_start_user(void) +static void spi_ctrl_start_user(const TestData *data) { - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); + uint32_t ctrl = spi_readl(data, R_CTRL0); ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); + spi_writel(data, R_CTRL0, ctrl); ctrl &= ~CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); + spi_writel(data, R_CTRL0, ctrl); } -static void spi_ctrl_stop_user(void) +static void spi_ctrl_stop_user(const TestData *data) { - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); + uint32_t ctrl = spi_readl(data, R_CTRL0); ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); + spi_writel(data, R_CTRL0, ctrl); } -static void flash_reset(void) +static void flash_reset(const TestData *data) { - spi_conf(CONF_ENABLE_W0); + spi_conf(data, CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RESET_ENABLE); - writeb(ASPEED_FLASH_BASE, RESET_MEMORY); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WRDI); - spi_ctrl_stop_user(); + spi_ctrl_start_user(data); + flash_writeb(data, 0, RESET_ENABLE); + flash_writeb(data, 0, RESET_MEMORY); + flash_writeb(data, 0, WREN); + flash_writeb(data, 0, BULK_ERASE); + flash_writeb(data, 0, WRDI); + spi_ctrl_stop_user(data); - spi_conf_remove(CONF_ENABLE_W0); + spi_conf_remove(data, CONF_ENABLE_W0); } -static void test_read_jedec(void) +static void test_read_jedec(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t jedec = 0x0; - spi_conf(CONF_ENABLE_W0); + spi_conf(test_data, CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, JEDEC_READ); - jedec |= readb(ASPEED_FLASH_BASE) << 16; - jedec |= readb(ASPEED_FLASH_BASE) << 8; - jedec |= readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, JEDEC_READ); + jedec |= flash_readb(test_data, 0) << 16; + jedec |= flash_readb(test_data, 0) << 8; + jedec |= flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); - flash_reset(); + flash_reset(test_data); - g_assert_cmphex(jedec, ==, FLASH_JEDEC); + g_assert_cmphex(jedec, ==, test_data->jedec_id); } -static void read_page(uint32_t addr, uint32_t *page) +static void read_page(const TestData *data, uint32_t addr, uint32_t *page) { int i; - spi_ctrl_start_user(); + spi_ctrl_start_user(data); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, READ); - writel(ASPEED_FLASH_BASE, make_be32(addr)); + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, READ); + flash_writel(data, 0, make_be32(addr)); /* Continuous read are supported */ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE)); + page[i] = make_be32(flash_readl(data, 0)); } - spi_ctrl_stop_user(); + spi_ctrl_stop_user(data); } -static void read_page_mem(uint32_t addr, uint32_t *page) +static void read_page_mem(const TestData *data, uint32_t addr, uint32_t *page) { int i; /* move out USER mode to use direct reads from the AHB bus */ - spi_ctrl_setmode(CTRL_READMODE, READ); + spi_ctrl_setmode(data, CTRL_READMODE, READ); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4)); + page[i] = make_be32(flash_readl(data, addr + i * 4)); } } -static void write_page_mem(uint32_t addr, uint32_t write_value) +static void write_page_mem(const TestData *data, uint32_t addr, + uint32_t write_value) { - spi_ctrl_setmode(CTRL_WRITEMODE, PP); + spi_ctrl_setmode(data, CTRL_WRITEMODE, PP); for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + addr + i * 4, write_value); + flash_writel(data, addr + i * 4, write_value); } } -static void assert_page_mem(uint32_t addr, uint32_t expected_value) +static void assert_page_mem(const TestData *data, uint32_t addr, + uint32_t expected_value) { uint32_t page[FLASH_PAGE_SIZE / 4]; - read_page_mem(addr, page); + read_page_mem(data, addr, page); for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, expected_value); } } -static void test_erase_sector(void) +static void test_erase_sector(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - spi_conf(CONF_ENABLE_W0); + spi_conf(test_data, CONF_ENABLE_W0); /* * Previous page should be full of 0xffs after backend is * initialized */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); /* Fill the page with its own addresses */ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); } - spi_ctrl_stop_user(); + spi_ctrl_stop_user(test_data); /* Check the page is correctly written */ - read_page(some_page_addr, page); + read_page(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, some_page_addr + i * 4); } - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, ERASE_SECTOR); + flash_writel(test_data, 0, make_be32(some_page_addr)); + spi_ctrl_stop_user(test_data); /* Check the page is erased */ - read_page(some_page_addr, page); + read_page(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } - flash_reset(); + flash_reset(test_data); } -static void test_erase_all(void) +static void test_erase_all(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - spi_conf(CONF_ENABLE_W0); + spi_conf(test_data, CONF_ENABLE_W0); /* * Previous page should be full of 0xffs after backend is * initialized */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); /* Fill the page with its own addresses */ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); } - spi_ctrl_stop_user(); + spi_ctrl_stop_user(test_data); /* Check the page is correctly written */ - read_page(some_page_addr, page); + read_page(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, some_page_addr + i * 4); } - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + spi_ctrl_stop_user(test_data); /* Check the page is erased */ - read_page(some_page_addr, page); + read_page(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } - flash_reset(); + flash_reset(test_data); } -static void test_write_page(void) +static void test_write_page(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - spi_conf(CONF_ENABLE_W0); + spi_conf(test_data, CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); /* Fill the page with its own addresses */ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); } - spi_ctrl_stop_user(); + spi_ctrl_stop_user(test_data); /* Check what was written */ - read_page(my_page_addr, page); + read_page(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } /* Check some other page. It should be full of 0xff */ - read_page(some_page_addr, page); + read_page(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } - flash_reset(); + flash_reset(test_data); } -static void test_read_page_mem(void) +static void test_read_page_mem(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; @@ -357,40 +399,41 @@ static void test_read_page_mem(void) * Enable 4BYTE mode for controller. This is should be strapped by * HW for CE0 anyhow. */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); + spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); + spi_conf(test_data, CONF_ENABLE_W0); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); /* Fill the page with its own addresses */ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); } - spi_ctrl_stop_user(); - spi_conf_remove(CONF_ENABLE_W0); + spi_ctrl_stop_user(test_data); + spi_conf_remove(test_data, CONF_ENABLE_W0); /* Check what was written */ - read_page_mem(my_page_addr, page); + read_page_mem(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } /* Check some other page. It should be full of 0xff */ - read_page_mem(some_page_addr, page); + read_page_mem(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } - flash_reset(); + flash_reset(test_data); } -static void test_write_page_mem(void) +static void test_write_page_mem(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; @@ -399,150 +442,153 @@ static void test_write_page_mem(void) * Enable 4BYTE mode for controller. This is should be strapped by * HW for CE0 anyhow. */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); + spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); + spi_conf(test_data, CONF_ENABLE_W0); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); /* move out USER mode to use direct writes to the AHB bus */ - spi_ctrl_setmode(CTRL_WRITEMODE, PP); + spi_ctrl_setmode(test_data, CTRL_WRITEMODE, PP); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + my_page_addr + i * 4, + flash_writel(test_data, my_page_addr + i * 4, make_be32(my_page_addr + i * 4)); } /* Check what was written */ - read_page_mem(my_page_addr, page); + read_page_mem(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } - flash_reset(); + flash_reset(test_data); } -static void test_read_status_reg(void) +static void test_read_status_reg(const void *data) { + const TestData *test_data = (const TestData *)data; uint8_t r; - spi_conf(CONF_ENABLE_W0); + spi_conf(test_data, CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); g_assert_cmphex(r & SR_WEL, ==, 0); g_assert(!qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + (test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); g_assert_cmphex(r & SR_WEL, ==, SR_WEL); g_assert(qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + (test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WRDI); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WRDI); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); g_assert_cmphex(r & SR_WEL, ==, 0); g_assert(!qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + (test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - flash_reset(); + flash_reset(test_data); } -static void test_status_reg_write_protection(void) +static void test_status_reg_write_protection(const void *data) { + const TestData *test_data = (const TestData *)data; uint8_t r; - spi_conf(CONF_ENABLE_W0); + spi_conf(test_data, CONF_ENABLE_W0); /* default case: WP# is high and SRWD is low -> status register writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, SRWD); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); g_assert_cmphex(r & SRWD, ==, SRWD); /* WP# high and SRWD high -> status register writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, 0); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); g_assert_cmphex(r & SRWD, ==, 0); /* WP# low and SRWD low -> status register writable */ - qtest_set_irq_in(global_qtest, + qtest_set_irq_in(test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, SRWD); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); g_assert_cmphex(r & SRWD, ==, SRWD); /* WP# low and SRWD high -> status register NOT writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0 , WREN); /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, 0); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); /* write is not successful */ g_assert_cmphex(r & SRWD, ==, SRWD); - qtest_set_irq_in(global_qtest, + qtest_set_irq_in(test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); - flash_reset(); + flash_reset(test_data); } -static void test_write_block_protect(void) +static void test_write_block_protect(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t sector_size = 65536; uint32_t n_sectors = 512; - spi_ce_ctrl(1 << CRTL_EXTENDED0); - spi_conf(CONF_ENABLE_W0); + spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); + spi_conf(test_data, CONF_ENABLE_W0); uint32_t bp_bits = 0b0; for (int i = 0; i < 16; i++) { bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, bp_bits); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; uint32_t protection_start = n_sectors - num_protected_sectors; @@ -551,27 +597,28 @@ static void test_write_block_protect(void) for (int sector = 0; sector < n_sectors; sector++) { uint32_t addr = sector * sector_size; - assert_page_mem(addr, 0xffffffff); - write_page_mem(addr, make_be32(0xabcdef12)); + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); uint32_t expected_value = protection_start <= sector && sector < protection_end ? 0xffffffff : 0xabcdef12; - assert_page_mem(addr, expected_value); + assert_page_mem(test_data, addr, expected_value); } } - flash_reset(); + flash_reset(test_data); } -static void test_write_block_protect_bottom_bit(void) +static void test_write_block_protect_bottom_bit(const void *data) { + const TestData *test_data = (const TestData *)data; uint32_t sector_size = 65536; uint32_t n_sectors = 512; - spi_ce_ctrl(1 << CRTL_EXTENDED0); - spi_conf(CONF_ENABLE_W0); + spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); + spi_conf(test_data, CONF_ENABLE_W0); /* top bottom bit is enabled */ uint32_t bp_bits = 0b00100 << 3; @@ -579,15 +626,15 @@ static void test_write_block_protect_bottom_bit(void) for (int i = 0; i < 16; i++) { bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, bp_bits); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; uint32_t protection_start = 0; @@ -596,64 +643,69 @@ static void test_write_block_protect_bottom_bit(void) for (int sector = 0; sector < n_sectors; sector++) { uint32_t addr = sector * sector_size; - assert_page_mem(addr, 0xffffffff); - write_page_mem(addr, make_be32(0xabcdef12)); + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); uint32_t expected_value = protection_start <= sector && sector < protection_end ? 0xffffffff : 0xabcdef12; - assert_page_mem(addr, expected_value); + assert_page_mem(test_data, addr, expected_value); } } - flash_reset(); + flash_reset(test_data); } -static int test_palmetto_bmc(void) +static void test_palmetto_bmc(TestData *data) { - g_autofree char *tmp_path = NULL; int ret; int fd; - fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL); + fd = g_file_open_tmp("qtest.m25p80.n25q256a.XXXXXX", &data->tmp_path, NULL); g_assert(fd >= 0); - ret = ftruncate(fd, FLASH_SIZE); + ret = ftruncate(fd, 32 * 1024 * 1024); g_assert(ret == 0); close(fd); - global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " - "-drive file=%s,format=raw,if=mtd", - tmp_path); + data->s = qtest_initf("-m 256 -machine palmetto-bmc " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); - qtest_add_func("/ast2400/smc/read_jedec", test_read_jedec); - qtest_add_func("/ast2400/smc/erase_sector", test_erase_sector); - qtest_add_func("/ast2400/smc/erase_all", test_erase_all); - qtest_add_func("/ast2400/smc/write_page", test_write_page); - qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); - qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); - qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); - qtest_add_func("/ast2400/smc/status_reg_write_protection", - test_status_reg_write_protection); - qtest_add_func("/ast2400/smc/write_block_protect", - test_write_block_protect); - qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit", - test_write_block_protect_bottom_bit); + /* fmc cs0 with n25q256a flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0x20ba19; - flash_reset(); - ret = g_test_run(); - qtest_quit(global_qtest); - unlink(tmp_path); - - return ret; + qtest_add_data_func("/ast2400/smc/read_jedec", data, test_read_jedec); + qtest_add_data_func("/ast2400/smc/erase_sector", data, test_erase_sector); + qtest_add_data_func("/ast2400/smc/erase_all", data, test_erase_all); + qtest_add_data_func("/ast2400/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast2400/smc/read_page_mem", + data, test_read_page_mem); + qtest_add_data_func("/ast2400/smc/write_page_mem", + data, test_write_page_mem); + qtest_add_data_func("/ast2400/smc/read_status_reg", + data, test_read_status_reg); + qtest_add_data_func("/ast2400/smc/status_reg_write_protection", + data, test_status_reg_write_protection); + qtest_add_data_func("/ast2400/smc/write_block_protect", + data, test_write_block_protect); + qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit", + data, test_write_block_protect_bottom_bit); } int main(int argc, char **argv) { + TestData palmetto_data; int ret; g_test_init(&argc, &argv, NULL); - ret = test_palmetto_bmc(); + test_palmetto_bmc(&palmetto_data); + ret = g_test_run(); + + qtest_quit(palmetto_data.s); + unlink(palmetto_data.tmp_path); return ret; } From dc32f5baf94e901e570e48d6244897c2418fe63c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:36 +0800 Subject: [PATCH 0065/2892] test/qtest/aspeed_smc-test: Support to test all CE pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, these test cases only support to test CE0. To test all CE pins, introduces new ce and node members in TestData structure. The ce member is used for saving the ce index and node member is used for saving the node path, respectively. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 77 ++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 4c62009605..b8ab20b43d 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -32,11 +32,11 @@ * ASPEED SPI Controller registers */ #define R_CONF 0x00 -#define CONF_ENABLE_W0 (1 << 16) +#define CONF_ENABLE_W0 16 #define R_CE_CTRL 0x04 #define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ #define R_CTRL0 0x10 -#define CTRL_CE_STOP_ACTIVE (1 << 2) +#define CTRL_CE_STOP_ACTIVE BIT(2) #define CTRL_READMODE 0x0 #define CTRL_FREADMODE 0x1 #define CTRL_WRITEMODE 0x2 @@ -70,6 +70,8 @@ typedef struct TestData { uint64_t flash_base; uint32_t jedec_id; char *tmp_path; + uint8_t cs; + const char *node; } TestData; /* @@ -140,34 +142,37 @@ static void spi_ce_ctrl(const TestData *data, uint32_t value) static void spi_ctrl_setmode(const TestData *data, uint8_t mode, uint8_t cmd) { - uint32_t ctrl = spi_readl(data, R_CTRL0); + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); ctrl &= ~(CTRL_USERMODE | 0xff << 16); ctrl |= mode | (cmd << 16); - spi_writel(data, R_CTRL0, ctrl); + spi_writel(data, ctrl_reg, ctrl); } static void spi_ctrl_start_user(const TestData *data) { - uint32_t ctrl = spi_readl(data, R_CTRL0); + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - spi_writel(data, R_CTRL0, ctrl); + spi_writel(data, ctrl_reg, ctrl); ctrl &= ~CTRL_CE_STOP_ACTIVE; - spi_writel(data, R_CTRL0, ctrl); + spi_writel(data, ctrl_reg, ctrl); } static void spi_ctrl_stop_user(const TestData *data) { - uint32_t ctrl = spi_readl(data, R_CTRL0); + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - spi_writel(data, R_CTRL0, ctrl); + spi_writel(data, ctrl_reg, ctrl); } static void flash_reset(const TestData *data) { - spi_conf(data, CONF_ENABLE_W0); + spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs)); spi_ctrl_start_user(data); flash_writeb(data, 0, RESET_ENABLE); @@ -177,7 +182,7 @@ static void flash_reset(const TestData *data) flash_writeb(data, 0, WRDI); spi_ctrl_stop_user(data); - spi_conf_remove(data, CONF_ENABLE_W0); + spi_conf_remove(data, 1 << (CONF_ENABLE_W0 + data->cs)); } static void test_read_jedec(const void *data) @@ -185,7 +190,7 @@ static void test_read_jedec(const void *data) const TestData *test_data = (const TestData *)data; uint32_t jedec = 0x0; - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, JEDEC_READ); @@ -255,7 +260,7 @@ static void test_erase_sector(const void *data) uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* * Previous page should be full of 0xffs after backend is @@ -307,7 +312,7 @@ static void test_erase_all(const void *data) uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* * Previous page should be full of 0xffs after backend is @@ -358,7 +363,7 @@ static void test_write_page(const void *data) uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, EN_4BYTE_ADDR); @@ -396,13 +401,12 @@ static void test_read_page_mem(const void *data) int i; /* - * Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. + * Enable 4BYTE mode for controller. */ - spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); /* Enable 4BYTE mode for flash. */ - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, EN_4BYTE_ADDR); flash_writeb(test_data, 0, WREN); @@ -414,7 +418,7 @@ static void test_read_page_mem(const void *data) flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); } spi_ctrl_stop_user(test_data); - spi_conf_remove(test_data, CONF_ENABLE_W0); + spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* Check what was written */ read_page_mem(test_data, my_page_addr, page); @@ -439,13 +443,12 @@ static void test_write_page_mem(const void *data) int i; /* - * Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. + * Enable 4BYTE mode for controller. */ - spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); /* Enable 4BYTE mode for flash. */ - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, EN_4BYTE_ADDR); flash_writeb(test_data, 0, WREN); @@ -473,7 +476,7 @@ static void test_read_status_reg(const void *data) const TestData *test_data = (const TestData *)data; uint8_t r; - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, RDSR); @@ -482,7 +485,7 @@ static void test_read_status_reg(const void *data) g_assert_cmphex(r & SR_WEL, ==, 0); g_assert(!qtest_qom_get_bool - (test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + (test_data->s, test_data->node, "write-enable")); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, WREN); @@ -492,7 +495,7 @@ static void test_read_status_reg(const void *data) g_assert_cmphex(r & SR_WEL, ==, SR_WEL); g_assert(qtest_qom_get_bool - (test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + (test_data->s, test_data->node, "write-enable")); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, WRDI); @@ -502,7 +505,7 @@ static void test_read_status_reg(const void *data) g_assert_cmphex(r & SR_WEL, ==, 0); g_assert(!qtest_qom_get_bool - (test_data->s, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + (test_data->s, test_data->node, "write-enable")); flash_reset(test_data); } @@ -512,7 +515,7 @@ static void test_status_reg_write_protection(const void *data) const TestData *test_data = (const TestData *)data; uint8_t r; - spi_conf(test_data, CONF_ENABLE_W0); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* default case: WP# is high and SRWD is low -> status register writable */ spi_ctrl_start_user(test_data); @@ -537,8 +540,7 @@ static void test_status_reg_write_protection(const void *data) g_assert_cmphex(r & SRWD, ==, 0); /* WP# low and SRWD low -> status register writable */ - qtest_set_irq_in(test_data->s, - "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 0); spi_ctrl_start_user(test_data); flash_writeb(test_data, 0, WREN); /* test ability to write SRWD */ @@ -561,8 +563,7 @@ static void test_status_reg_write_protection(const void *data) /* write is not successful */ g_assert_cmphex(r & SRWD, ==, SRWD); - qtest_set_irq_in(test_data->s, - "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 1); flash_reset(test_data); } @@ -572,8 +573,8 @@ static void test_write_block_protect(const void *data) uint32_t sector_size = 65536; uint32_t n_sectors = 512; - spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); - spi_conf(test_data, CONF_ENABLE_W0); + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); uint32_t bp_bits = 0b0; @@ -617,8 +618,8 @@ static void test_write_block_protect_bottom_bit(const void *data) uint32_t sector_size = 65536; uint32_t n_sectors = 512; - spi_ce_ctrl(test_data, 1 << CRTL_EXTENDED0); - spi_conf(test_data, CONF_ENABLE_W0); + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* top bottom bit is enabled */ uint32_t bp_bits = 0b00100 << 3; @@ -676,6 +677,8 @@ static void test_palmetto_bmc(TestData *data) data->flash_base = 0x20000000; data->spi_base = 0x1E620000; data->jedec_id = 0x20ba19; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; qtest_add_data_func("/ast2400/smc/read_jedec", data, test_read_jedec); qtest_add_data_func("/ast2400/smc/erase_sector", data, test_erase_sector); From 369a47ae4b34379048728a3321163fc48fa109b1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:37 +0800 Subject: [PATCH 0066/2892] test/qtest/aspeed_smc-test: Introducing a "page_addr" data field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, these test cases used the hardcode offset 0x1400000 (0x14000 * 256) which was beyond the 16MB flash size for flash page read/write command testing. However, the default fmc flash model of ast1030-a1 EVB is "w25q80bl" whose size is 1MB. To test SoC flash models, introduces a new page_addr member in TestData structure, so users can set the offset for flash page read/write command testing. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index b8ab20b43d..6db18451d2 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -72,6 +72,7 @@ typedef struct TestData { char *tmp_path; uint8_t cs; const char *node; + uint32_t page_addr; } TestData; /* @@ -256,7 +257,7 @@ static void assert_page_mem(const TestData *data, uint32_t addr, static void test_erase_sector(const void *data) { const TestData *test_data = (const TestData *)data; - uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; + uint32_t some_page_addr = test_data->page_addr; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; @@ -308,7 +309,7 @@ static void test_erase_sector(const void *data) static void test_erase_all(const void *data) { const TestData *test_data = (const TestData *)data; - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; + uint32_t some_page_addr = test_data->page_addr; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; @@ -358,8 +359,8 @@ static void test_erase_all(const void *data) static void test_write_page(const void *data) { const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; @@ -395,8 +396,8 @@ static void test_write_page(const void *data) static void test_read_page_mem(const void *data) { const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; @@ -438,7 +439,7 @@ static void test_read_page_mem(const void *data) static void test_write_page_mem(const void *data) { const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE; + uint32_t my_page_addr = test_data->page_addr; uint32_t page[FLASH_PAGE_SIZE / 4]; int i; @@ -679,6 +680,8 @@ static void test_palmetto_bmc(TestData *data) data->jedec_id = 0x20ba19; data->cs = 0; data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; qtest_add_data_func("/ast2400/smc/read_jedec", data, test_read_jedec); qtest_add_data_func("/ast2400/smc/erase_sector", data, test_erase_sector); From 658ebe13687a5069f907ec8ece040adeb56667cc Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:38 +0800 Subject: [PATCH 0067/2892] test/qtest/aspeed_smc-test: Support to test AST2500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test_ast2500_evb function and reused testcases for AST2500 testing. The spi base address, flash base address and ce index of fmc_cs0 are 0x1E620000, 0x20000000 and 0, respectively. The default flash model of fmc_cs0 is "mx25l25635e" whose size is 32MB, so set jedec_id 0xc22019. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 6db18451d2..0171ecf4ed 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -701,17 +701,57 @@ static void test_palmetto_bmc(TestData *data) data, test_write_block_protect_bottom_bit); } +static void test_ast2500_evb(TestData *data) +{ + int ret; + int fd; + + fd = g_file_open_tmp("qtest.m25p80.mx25l25635e.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 32 * 1024 * 1024); + g_assert(ret == 0); + close(fd); + + data->s = qtest_initf("-machine ast2500-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with mx25l25635e flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0xc22019; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2500/smc/read_jedec", data, test_read_jedec); + qtest_add_data_func("/ast2500/smc/erase_sector", data, test_erase_sector); + qtest_add_data_func("/ast2500/smc/erase_all", data, test_erase_all); + qtest_add_data_func("/ast2500/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast2500/smc/read_page_mem", + data, test_read_page_mem); + qtest_add_data_func("/ast2500/smc/write_page_mem", + data, test_write_page_mem); + qtest_add_data_func("/ast2500/smc/read_status_reg", + data, test_read_status_reg); +} int main(int argc, char **argv) { TestData palmetto_data; + TestData ast2500_evb_data; int ret; g_test_init(&argc, &argv, NULL); test_palmetto_bmc(&palmetto_data); + test_ast2500_evb(&ast2500_evb_data); ret = g_test_run(); qtest_quit(palmetto_data.s); + qtest_quit(ast2500_evb_data.s); unlink(palmetto_data.tmp_path); + unlink(ast2500_evb_data.tmp_path); return ret; } From 84f7ea6db7709c8378362c4a1af30a3e3e3932b1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:39 +0800 Subject: [PATCH 0068/2892] test/qtest/aspeed_smc-test: Support to test AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test_ast2600_evb function and reused testcases for AST2600 testing. The spi base address, flash base address and ce index of fmc_cs0 are 0x1E620000, 0x20000000 and 0, respectively. The default flash model of fmc_cs0 is "mx66u51235f" whose size is 64MB, so set jedec_id 0xc2253a. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 0171ecf4ed..30f997679c 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -737,21 +737,62 @@ static void test_ast2500_evb(TestData *data) qtest_add_data_func("/ast2500/smc/read_status_reg", data, test_read_status_reg); } + +static void test_ast2600_evb(TestData *data) +{ + int ret; + int fd; + + fd = g_file_open_tmp("qtest.m25p80.mx66u51235f.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 64 * 1024 * 1024); + g_assert(ret == 0); + close(fd); + + data->s = qtest_initf("-machine ast2600-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with mx66u51235f flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0xc2253a; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2600/smc/read_jedec", data, test_read_jedec); + qtest_add_data_func("/ast2600/smc/erase_sector", data, test_erase_sector); + qtest_add_data_func("/ast2600/smc/erase_all", data, test_erase_all); + qtest_add_data_func("/ast2600/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast2600/smc/read_page_mem", + data, test_read_page_mem); + qtest_add_data_func("/ast2600/smc/write_page_mem", + data, test_write_page_mem); + qtest_add_data_func("/ast2600/smc/read_status_reg", + data, test_read_status_reg); +} int main(int argc, char **argv) { TestData palmetto_data; TestData ast2500_evb_data; + TestData ast2600_evb_data; int ret; g_test_init(&argc, &argv, NULL); test_palmetto_bmc(&palmetto_data); test_ast2500_evb(&ast2500_evb_data); + test_ast2600_evb(&ast2600_evb_data); ret = g_test_run(); qtest_quit(palmetto_data.s); qtest_quit(ast2500_evb_data.s); + qtest_quit(ast2600_evb_data.s); unlink(palmetto_data.tmp_path); unlink(ast2500_evb_data.tmp_path); + unlink(ast2600_evb_data.tmp_path); return ret; } From 0f6e8aa4fc5431c7f8b3d0ac083c632512a9b7d4 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:40 +0800 Subject: [PATCH 0069/2892] test/qtest/aspeed_smc-test: Support to test AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test_ast1030_evb function and reused testcases for AST1030 testing. The base address, flash base address and ce index of fmc_cs0 are 0x7E620000, 0x80000000 and 0, respectively. The default flash model of fmc_cs0 is "w25q80bl" whose size is 1MB, so set jedec_id 0xef4014. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 30f997679c..c5c38e23c5 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -774,11 +774,50 @@ static void test_ast2600_evb(TestData *data) qtest_add_data_func("/ast2600/smc/read_status_reg", data, test_read_status_reg); } + +static void test_ast1030_evb(TestData *data) +{ + int ret; + int fd; + + fd = g_file_open_tmp("qtest.m25p80.w25q80bl.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 1 * 1024 * 1024); + g_assert(ret == 0); + close(fd); + + data->s = qtest_initf("-machine ast1030-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with w25q80bl flash */ + data->flash_base = 0x80000000; + data->spi_base = 0x7E620000; + data->jedec_id = 0xef4014; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 512KB */ + data->page_addr = 0x800 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast1030/smc/read_jedec", data, test_read_jedec); + qtest_add_data_func("/ast1030/smc/erase_sector", data, test_erase_sector); + qtest_add_data_func("/ast1030/smc/erase_all", data, test_erase_all); + qtest_add_data_func("/ast1030/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast1030/smc/read_page_mem", + data, test_read_page_mem); + qtest_add_data_func("/ast1030/smc/write_page_mem", + data, test_write_page_mem); + qtest_add_data_func("/ast1030/smc/read_status_reg", + data, test_read_status_reg); +} + int main(int argc, char **argv) { TestData palmetto_data; TestData ast2500_evb_data; TestData ast2600_evb_data; + TestData ast1030_evb_data; int ret; g_test_init(&argc, &argv, NULL); @@ -786,13 +825,16 @@ int main(int argc, char **argv) test_palmetto_bmc(&palmetto_data); test_ast2500_evb(&ast2500_evb_data); test_ast2600_evb(&ast2600_evb_data); + test_ast1030_evb(&ast1030_evb_data); ret = g_test_run(); qtest_quit(palmetto_data.s); qtest_quit(ast2500_evb_data.s); qtest_quit(ast2600_evb_data.s); + qtest_quit(ast1030_evb_data.s); unlink(palmetto_data.tmp_path); unlink(ast2500_evb_data.tmp_path); unlink(ast2600_evb_data.tmp_path); + unlink(ast1030_evb_data.tmp_path); return ret; } From 08479f0ca01414aadf513717ba3e1258321e6513 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:41 +0800 Subject: [PATCH 0070/2892] test/qtest/aspeed_smc-test: Support write page command with QPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new testcase for write page command with QPI mode testing. Currently, only run this testcase for AST2500, AST2600 and AST1030. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 74 +++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index c5c38e23c5..59f3876cdc 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -36,6 +36,7 @@ #define R_CE_CTRL 0x04 #define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ #define R_CTRL0 0x10 +#define CTRL_IO_QUAD_IO BIT(31) #define CTRL_CE_STOP_ACTIVE BIT(2) #define CTRL_READMODE 0x0 #define CTRL_FREADMODE 0x1 @@ -62,6 +63,7 @@ enum { ERASE_SECTOR = 0xd8, }; +#define CTRL_IO_MODE_MASK (BIT(31) | BIT(30) | BIT(29) | BIT(28)) #define FLASH_PAGE_SIZE 256 typedef struct TestData { @@ -171,6 +173,18 @@ static void spi_ctrl_stop_user(const TestData *data) spi_writel(data, ctrl_reg, ctrl); } +static void spi_ctrl_set_io_mode(const TestData *data, uint32_t value) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + uint32_t mode; + + mode = value & CTRL_IO_MODE_MASK; + ctrl &= ~CTRL_IO_MODE_MASK; + ctrl |= mode; + spi_writel(data, ctrl_reg, ctrl); +} + static void flash_reset(const TestData *data) { spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs)); @@ -659,6 +673,60 @@ static void test_write_block_protect_bottom_bit(const void *data) flash_reset(test_data); } +static void test_write_page_qpi(const void *data) +{ + const TestData *test_data = (const TestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + uint32_t page_pattern[] = { + 0xebd8c134, 0x5da196bc, 0xae15e729, 0x5085ccdf + }; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Set QPI mode */ + spi_ctrl_set_io_mode(test_data, CTRL_IO_QUAD_IO); + + /* Fill the page pattern */ + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + flash_writel(test_data, 0, make_be32(page_pattern[i])); + } + + /* Fill the page with its own addresses */ + for (; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + + /* Restore io mode */ + spi_ctrl_set_io_mode(test_data, 0); + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + g_assert_cmphex(page[i], ==, page_pattern[i]); + } + for (; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + static void test_palmetto_bmc(TestData *data) { int ret; @@ -736,6 +804,8 @@ static void test_ast2500_evb(TestData *data) data, test_write_page_mem); qtest_add_data_func("/ast2500/smc/read_status_reg", data, test_read_status_reg); + qtest_add_data_func("/ast2500/smc/write_page_qpi", + data, test_write_page_qpi); } static void test_ast2600_evb(TestData *data) @@ -773,6 +843,8 @@ static void test_ast2600_evb(TestData *data) data, test_write_page_mem); qtest_add_data_func("/ast2600/smc/read_status_reg", data, test_read_status_reg); + qtest_add_data_func("/ast2600/smc/write_page_qpi", + data, test_write_page_qpi); } static void test_ast1030_evb(TestData *data) @@ -810,6 +882,8 @@ static void test_ast1030_evb(TestData *data) data, test_write_page_mem); qtest_add_data_func("/ast1030/smc/read_status_reg", data, test_read_status_reg); + qtest_add_data_func("/ast1030/smc/write_page_qpi", + data, test_write_page_qpi); } int main(int argc, char **argv) From 3e62a32ddb89831c4a01b9526ae656f7fb802998 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:42 +0800 Subject: [PATCH 0071/2892] test/qtest: Introduce a new aspeed-smc-utils.c to place common testcases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The testcases for ASPEED SMC model were placed in aspeed_smc-test.c. However, this test file only supports for ARM32. To support all ASPEED SOCs such as AST2700 whose CPU architecture is aarch64, introduces a new aspeed-smc-utils source file and move all common APIs and testcases from aspeed_smc-test.c to aspeed-smc-utils.c. Finally, users are able to re-used these testcase for AST2700 and future ASPEED SOCs testing. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-smc-utils.c | 686 ++++++++++++++++++++++++++++ tests/qtest/aspeed-smc-utils.h | 95 ++++ tests/qtest/aspeed_smc-test.c | 800 +++------------------------------ tests/qtest/meson.build | 1 + 4 files changed, 841 insertions(+), 741 deletions(-) create mode 100644 tests/qtest/aspeed-smc-utils.c create mode 100644 tests/qtest/aspeed-smc-utils.h diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c new file mode 100644 index 0000000000..c27d09e767 --- /dev/null +++ b/tests/qtest/aspeed-smc-utils.c @@ -0,0 +1,686 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" +#include "aspeed-smc-utils.h" + +/* + * Use an explicit bswap for the values read/wrote to the flash region + * as they are BE and the Aspeed CPU is LE. + */ +static inline uint32_t make_be32(uint32_t data) +{ + return bswap32(data); +} + +static inline void spi_writel(const AspeedSMCTestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->spi_base + offset, value); +} + +static inline uint32_t spi_readl(const AspeedSMCTestData *data, uint64_t offset) +{ + return qtest_readl(data->s, data->spi_base + offset); +} + +static inline void flash_writeb(const AspeedSMCTestData *data, uint64_t offset, + uint8_t value) +{ + qtest_writeb(data->s, data->flash_base + offset, value); +} + +static inline void flash_writel(const AspeedSMCTestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->flash_base + offset, value); +} + +static inline uint8_t flash_readb(const AspeedSMCTestData *data, + uint64_t offset) +{ + return qtest_readb(data->s, data->flash_base + offset); +} + +static inline uint32_t flash_readl(const AspeedSMCTestData *data, + uint64_t offset) +{ + return qtest_readl(data->s, data->flash_base + offset); +} + +static void spi_conf(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf |= value; + spi_writel(data, R_CONF, conf); +} + +static void spi_conf_remove(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf &= ~value; + spi_writel(data, R_CONF, conf); +} + +static void spi_ce_ctrl(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CE_CTRL); + + conf |= value; + spi_writel(data, R_CE_CTRL, conf); +} + +static void spi_ctrl_setmode(const AspeedSMCTestData *data, uint8_t mode, + uint8_t cmd) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + ctrl &= ~(CTRL_USERMODE | 0xff << 16); + ctrl |= mode | (cmd << 16); + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_start_user(const AspeedSMCTestData *data) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); + + ctrl &= ~CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_stop_user(const AspeedSMCTestData *data) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_set_io_mode(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + uint32_t mode; + + mode = value & CTRL_IO_MODE_MASK; + ctrl &= ~CTRL_IO_MODE_MASK; + ctrl |= mode; + spi_writel(data, ctrl_reg, ctrl); +} + +static void flash_reset(const AspeedSMCTestData *data) +{ + spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs)); + + spi_ctrl_start_user(data); + flash_writeb(data, 0, RESET_ENABLE); + flash_writeb(data, 0, RESET_MEMORY); + flash_writeb(data, 0, WREN); + flash_writeb(data, 0, BULK_ERASE); + flash_writeb(data, 0, WRDI); + spi_ctrl_stop_user(data); + + spi_conf_remove(data, 1 << (CONF_ENABLE_W0 + data->cs)); +} + +static void read_page(const AspeedSMCTestData *data, uint32_t addr, + uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, READ); + flash_writel(data, 0, make_be32(addr)); + + /* Continuous read are supported */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +static void read_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t *page) +{ + int i; + + /* move out USER mode to use direct reads from the AHB bus */ + spi_ctrl_setmode(data, CTRL_READMODE, READ); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +static void write_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t write_value) +{ + spi_ctrl_setmode(data, CTRL_WRITEMODE, PP); + + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(data, addr + i * 4, write_value); + } +} + +static void assert_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t expected_value) +{ + uint32_t page[FLASH_PAGE_SIZE / 4]; + read_page_mem(data, addr, page); + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, expected_value); + } +} + +void aspeed_smc_test_read_jedec(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t jedec = 0x0; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, JEDEC_READ); + jedec |= flash_readb(test_data, 0) << 16; + jedec |= flash_readb(test_data, 0) << 8; + jedec |= flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + flash_reset(test_data); + + g_assert_cmphex(jedec, ==, test_data->jedec_id); +} + +void aspeed_smc_test_erase_sector(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t some_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check the page is correctly written */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, ERASE_SECTOR); + flash_writel(test_data, 0, make_be32(some_page_addr)); + spi_ctrl_stop_user(test_data); + + /* Check the page is erased */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_erase_all(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t some_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check the page is correctly written */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + spi_ctrl_stop_user(test_data); + + /* Check the page is erased */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_page_mem(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + /* + * Enable 4BYTE mode for controller. + */ + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + + /* Enable 4BYTE mode for flash. */ + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* Check what was written */ + read_page_mem(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page_mem(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page_mem(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + /* + * Enable 4BYTE mode for controller. + */ + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + + /* Enable 4BYTE mode for flash. */ + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + /* move out USER mode to use direct writes to the AHB bus */ + spi_ctrl_setmode(test_data, CTRL_WRITEMODE, PP); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, my_page_addr + i * 4, + make_be32(my_page_addr + i * 4)); + } + + /* Check what was written */ + read_page_mem(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_status_reg(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint8_t r; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, SR_WEL); + g_assert(qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WRDI); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + flash_reset(test_data); +} + +void aspeed_smc_test_status_reg_write_protection(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint8_t r; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* default case: WP# is high and SRWD is low -> status register writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# high and SRWD high -> status register writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, 0); + + /* WP# low and SRWD low -> status register writable */ + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 0); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# low and SRWD high -> status register NOT writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0 , WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + /* write is not successful */ + g_assert_cmphex(r & SRWD, ==, SRWD); + + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 1); + flash_reset(test_data); +} + +void aspeed_smc_test_write_block_protect(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + uint32_t bp_bits = 0b0; + + for (int i = 0; i < 16; i++) { + bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = n_sectors - num_protected_sectors; + uint32_t protection_end = n_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(test_data, addr, expected_value); + } + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_block_protect_bottom_bit(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* top bottom bit is enabled */ + uint32_t bp_bits = 0b00100 << 3; + + for (int i = 0; i < 16; i++) { + bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = 0; + uint32_t protection_end = num_protected_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(test_data, addr, expected_value); + } + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page_qpi(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + uint32_t page_pattern[] = { + 0xebd8c134, 0x5da196bc, 0xae15e729, 0x5085ccdf + }; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Set QPI mode */ + spi_ctrl_set_io_mode(test_data, CTRL_IO_QUAD_IO); + + /* Fill the page pattern */ + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + flash_writel(test_data, 0, make_be32(page_pattern[i])); + } + + /* Fill the page with its own addresses */ + for (; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + + /* Restore io mode */ + spi_ctrl_set_io_mode(test_data, 0); + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + g_assert_cmphex(page[i], ==, page_pattern[i]); + } + for (; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h new file mode 100644 index 0000000000..b07870f3b8 --- /dev/null +++ b/tests/qtest/aspeed-smc-utils.h @@ -0,0 +1,95 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * 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 TESTS_ASPEED_SMC_UTILS_H +#define TESTS_ASPEED_SMC_UTILS_H + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" + +/* + * ASPEED SPI Controller registers + */ +#define R_CONF 0x00 +#define CONF_ENABLE_W0 16 +#define R_CE_CTRL 0x04 +#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ +#define R_CTRL0 0x10 +#define CTRL_IO_QUAD_IO BIT(31) +#define CTRL_CE_STOP_ACTIVE BIT(2) +#define CTRL_READMODE 0x0 +#define CTRL_FREADMODE 0x1 +#define CTRL_WRITEMODE 0x2 +#define CTRL_USERMODE 0x3 +#define SR_WEL BIT(1) + +/* + * Flash commands + */ +enum { + JEDEC_READ = 0x9f, + RDSR = 0x5, + WRDI = 0x4, + BULK_ERASE = 0xc7, + READ = 0x03, + PP = 0x02, + WRSR = 0x1, + WREN = 0x6, + SRWD = 0x80, + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + EN_4BYTE_ADDR = 0xB7, + ERASE_SECTOR = 0xd8, +}; + +#define CTRL_IO_MODE_MASK (BIT(31) | BIT(30) | BIT(29) | BIT(28)) +#define FLASH_PAGE_SIZE 256 + +typedef struct AspeedSMCTestData { + QTestState *s; + uint64_t spi_base; + uint64_t flash_base; + uint32_t jedec_id; + char *tmp_path; + uint8_t cs; + const char *node; + uint32_t page_addr; +} AspeedSMCTestData; + +void aspeed_smc_test_read_jedec(const void *data); +void aspeed_smc_test_erase_sector(const void *data); +void aspeed_smc_test_erase_all(const void *data); +void aspeed_smc_test_write_page(const void *data); +void aspeed_smc_test_read_page_mem(const void *data); +void aspeed_smc_test_write_page_mem(const void *data); +void aspeed_smc_test_read_status_reg(const void *data); +void aspeed_smc_test_status_reg_write_protection(const void *data); +void aspeed_smc_test_write_block_protect(const void *data); +void aspeed_smc_test_write_block_protect_bottom_bit(const void *data); +void aspeed_smc_test_write_page_qpi(const void *data); + +#endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 59f3876cdc..4e1389385d 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -27,707 +27,9 @@ #include "qemu/bswap.h" #include "libqtest-single.h" #include "qemu/bitops.h" +#include "aspeed-smc-utils.h" -/* - * ASPEED SPI Controller registers - */ -#define R_CONF 0x00 -#define CONF_ENABLE_W0 16 -#define R_CE_CTRL 0x04 -#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ -#define R_CTRL0 0x10 -#define CTRL_IO_QUAD_IO BIT(31) -#define CTRL_CE_STOP_ACTIVE BIT(2) -#define CTRL_READMODE 0x0 -#define CTRL_FREADMODE 0x1 -#define CTRL_WRITEMODE 0x2 -#define CTRL_USERMODE 0x3 -#define SR_WEL BIT(1) - -/* - * Flash commands - */ -enum { - JEDEC_READ = 0x9f, - RDSR = 0x5, - WRDI = 0x4, - BULK_ERASE = 0xc7, - READ = 0x03, - PP = 0x02, - WRSR = 0x1, - WREN = 0x6, - SRWD = 0x80, - RESET_ENABLE = 0x66, - RESET_MEMORY = 0x99, - EN_4BYTE_ADDR = 0xB7, - ERASE_SECTOR = 0xd8, -}; - -#define CTRL_IO_MODE_MASK (BIT(31) | BIT(30) | BIT(29) | BIT(28)) -#define FLASH_PAGE_SIZE 256 - -typedef struct TestData { - QTestState *s; - uint64_t spi_base; - uint64_t flash_base; - uint32_t jedec_id; - char *tmp_path; - uint8_t cs; - const char *node; - uint32_t page_addr; -} TestData; - -/* - * Use an explicit bswap for the values read/wrote to the flash region - * as they are BE and the Aspeed CPU is LE. - */ -static inline uint32_t make_be32(uint32_t data) -{ - return bswap32(data); -} - -static inline void spi_writel(const TestData *data, uint64_t offset, - uint32_t value) -{ - qtest_writel(data->s, data->spi_base + offset, value); -} - -static inline uint32_t spi_readl(const TestData *data, uint64_t offset) -{ - return qtest_readl(data->s, data->spi_base + offset); -} - -static inline void flash_writeb(const TestData *data, uint64_t offset, - uint8_t value) -{ - qtest_writeb(data->s, data->flash_base + offset, value); -} - -static inline void flash_writel(const TestData *data, uint64_t offset, - uint32_t value) -{ - qtest_writel(data->s, data->flash_base + offset, value); -} - -static inline uint8_t flash_readb(const TestData *data, uint64_t offset) -{ - return qtest_readb(data->s, data->flash_base + offset); -} - -static inline uint32_t flash_readl(const TestData *data, uint64_t offset) -{ - return qtest_readl(data->s, data->flash_base + offset); -} - -static void spi_conf(const TestData *data, uint32_t value) -{ - uint32_t conf = spi_readl(data, R_CONF); - - conf |= value; - spi_writel(data, R_CONF, conf); -} - -static void spi_conf_remove(const TestData *data, uint32_t value) -{ - uint32_t conf = spi_readl(data, R_CONF); - - conf &= ~value; - spi_writel(data, R_CONF, conf); -} - -static void spi_ce_ctrl(const TestData *data, uint32_t value) -{ - uint32_t conf = spi_readl(data, R_CE_CTRL); - - conf |= value; - spi_writel(data, R_CE_CTRL, conf); -} - -static void spi_ctrl_setmode(const TestData *data, uint8_t mode, uint8_t cmd) -{ - uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; - uint32_t ctrl = spi_readl(data, ctrl_reg); - ctrl &= ~(CTRL_USERMODE | 0xff << 16); - ctrl |= mode | (cmd << 16); - spi_writel(data, ctrl_reg, ctrl); -} - -static void spi_ctrl_start_user(const TestData *data) -{ - uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; - uint32_t ctrl = spi_readl(data, ctrl_reg); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - spi_writel(data, ctrl_reg, ctrl); - - ctrl &= ~CTRL_CE_STOP_ACTIVE; - spi_writel(data, ctrl_reg, ctrl); -} - -static void spi_ctrl_stop_user(const TestData *data) -{ - uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; - uint32_t ctrl = spi_readl(data, ctrl_reg); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - spi_writel(data, ctrl_reg, ctrl); -} - -static void spi_ctrl_set_io_mode(const TestData *data, uint32_t value) -{ - uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; - uint32_t ctrl = spi_readl(data, ctrl_reg); - uint32_t mode; - - mode = value & CTRL_IO_MODE_MASK; - ctrl &= ~CTRL_IO_MODE_MASK; - ctrl |= mode; - spi_writel(data, ctrl_reg, ctrl); -} - -static void flash_reset(const TestData *data) -{ - spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs)); - - spi_ctrl_start_user(data); - flash_writeb(data, 0, RESET_ENABLE); - flash_writeb(data, 0, RESET_MEMORY); - flash_writeb(data, 0, WREN); - flash_writeb(data, 0, BULK_ERASE); - flash_writeb(data, 0, WRDI); - spi_ctrl_stop_user(data); - - spi_conf_remove(data, 1 << (CONF_ENABLE_W0 + data->cs)); -} - -static void test_read_jedec(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t jedec = 0x0; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, JEDEC_READ); - jedec |= flash_readb(test_data, 0) << 16; - jedec |= flash_readb(test_data, 0) << 8; - jedec |= flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - - flash_reset(test_data); - - g_assert_cmphex(jedec, ==, test_data->jedec_id); -} - -static void read_page(const TestData *data, uint32_t addr, uint32_t *page) -{ - int i; - - spi_ctrl_start_user(data); - - flash_writeb(data, 0, EN_4BYTE_ADDR); - flash_writeb(data, 0, READ); - flash_writel(data, 0, make_be32(addr)); - - /* Continuous read are supported */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(flash_readl(data, 0)); - } - spi_ctrl_stop_user(data); -} - -static void read_page_mem(const TestData *data, uint32_t addr, uint32_t *page) -{ - int i; - - /* move out USER mode to use direct reads from the AHB bus */ - spi_ctrl_setmode(data, CTRL_READMODE, READ); - - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(flash_readl(data, addr + i * 4)); - } -} - -static void write_page_mem(const TestData *data, uint32_t addr, - uint32_t write_value) -{ - spi_ctrl_setmode(data, CTRL_WRITEMODE, PP); - - for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(data, addr + i * 4, write_value); - } -} - -static void assert_page_mem(const TestData *data, uint32_t addr, - uint32_t expected_value) -{ - uint32_t page[FLASH_PAGE_SIZE / 4]; - read_page_mem(data, addr, page); - for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, expected_value); - } -} - -static void test_erase_sector(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t some_page_addr = test_data->page_addr; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - /* - * Previous page should be full of 0xffs after backend is - * initialized - */ - read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, PP); - flash_writel(test_data, 0, make_be32(some_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); - } - spi_ctrl_stop_user(test_data); - - /* Check the page is correctly written */ - read_page(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, some_page_addr + i * 4); - } - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, ERASE_SECTOR); - flash_writel(test_data, 0, make_be32(some_page_addr)); - spi_ctrl_stop_user(test_data); - - /* Check the page is erased */ - read_page(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(test_data); -} - -static void test_erase_all(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t some_page_addr = test_data->page_addr; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - /* - * Previous page should be full of 0xffs after backend is - * initialized - */ - read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, PP); - flash_writel(test_data, 0, make_be32(some_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); - } - spi_ctrl_stop_user(test_data); - - /* Check the page is correctly written */ - read_page(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, some_page_addr + i * 4); - } - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, BULK_ERASE); - spi_ctrl_stop_user(test_data); - - /* Check the page is erased */ - read_page(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(test_data); -} - -static void test_write_page(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = test_data->page_addr; - uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, PP); - flash_writel(test_data, 0, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(test_data); - - /* Check what was written */ - read_page(test_data, my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(test_data); -} - -static void test_read_page_mem(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = test_data->page_addr; - uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - /* - * Enable 4BYTE mode for controller. - */ - spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); - - /* Enable 4BYTE mode for flash. */ - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, PP); - flash_writel(test_data, 0, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(test_data); - spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - /* Check what was written */ - read_page_mem(test_data, my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page_mem(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(test_data); -} - -static void test_write_page_mem(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = test_data->page_addr; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - /* - * Enable 4BYTE mode for controller. - */ - spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); - - /* Enable 4BYTE mode for flash. */ - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - spi_ctrl_stop_user(test_data); - - /* move out USER mode to use direct writes to the AHB bus */ - spi_ctrl_setmode(test_data, CTRL_WRITEMODE, PP); - - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(test_data, my_page_addr + i * 4, - make_be32(my_page_addr + i * 4)); - } - - /* Check what was written */ - read_page_mem(test_data, my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - flash_reset(test_data); -} - -static void test_read_status_reg(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint8_t r; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - - g_assert_cmphex(r & SR_WEL, ==, 0); - g_assert(!qtest_qom_get_bool - (test_data->s, test_data->node, "write-enable")); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - - g_assert_cmphex(r & SR_WEL, ==, SR_WEL); - g_assert(qtest_qom_get_bool - (test_data->s, test_data->node, "write-enable")); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WRDI); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - - g_assert_cmphex(r & SR_WEL, ==, 0); - g_assert(!qtest_qom_get_bool - (test_data->s, test_data->node, "write-enable")); - - flash_reset(test_data); -} - -static void test_status_reg_write_protection(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint8_t r; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - /* default case: WP# is high and SRWD is low -> status register writable */ - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - /* test ability to write SRWD */ - flash_writeb(test_data, 0, WRSR); - flash_writeb(test_data, 0, SRWD); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - g_assert_cmphex(r & SRWD, ==, SRWD); - - /* WP# high and SRWD high -> status register writable */ - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - /* test ability to write SRWD */ - flash_writeb(test_data, 0, WRSR); - flash_writeb(test_data, 0, 0); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - g_assert_cmphex(r & SRWD, ==, 0); - - /* WP# low and SRWD low -> status register writable */ - qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 0); - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - /* test ability to write SRWD */ - flash_writeb(test_data, 0, WRSR); - flash_writeb(test_data, 0, SRWD); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - g_assert_cmphex(r & SRWD, ==, SRWD); - - /* WP# low and SRWD high -> status register NOT writable */ - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0 , WREN); - /* test ability to write SRWD */ - flash_writeb(test_data, 0, WRSR); - flash_writeb(test_data, 0, 0); - flash_writeb(test_data, 0, RDSR); - r = flash_readb(test_data, 0); - spi_ctrl_stop_user(test_data); - /* write is not successful */ - g_assert_cmphex(r & SRWD, ==, SRWD); - - qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 1); - flash_reset(test_data); -} - -static void test_write_block_protect(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t sector_size = 65536; - uint32_t n_sectors = 512; - - spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - uint32_t bp_bits = 0b0; - - for (int i = 0; i < 16; i++) { - bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, BULK_ERASE); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, WRSR); - flash_writeb(test_data, 0, bp_bits); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - spi_ctrl_stop_user(test_data); - - uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; - uint32_t protection_start = n_sectors - num_protected_sectors; - uint32_t protection_end = n_sectors; - - for (int sector = 0; sector < n_sectors; sector++) { - uint32_t addr = sector * sector_size; - - assert_page_mem(test_data, addr, 0xffffffff); - write_page_mem(test_data, addr, make_be32(0xabcdef12)); - - uint32_t expected_value = protection_start <= sector - && sector < protection_end - ? 0xffffffff : 0xabcdef12; - - assert_page_mem(test_data, addr, expected_value); - } - } - - flash_reset(test_data); -} - -static void test_write_block_protect_bottom_bit(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t sector_size = 65536; - uint32_t n_sectors = 512; - - spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - /* top bottom bit is enabled */ - uint32_t bp_bits = 0b00100 << 3; - - for (int i = 0; i < 16; i++) { - bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, BULK_ERASE); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, WRSR); - flash_writeb(test_data, 0, bp_bits); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - spi_ctrl_stop_user(test_data); - - uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; - uint32_t protection_start = 0; - uint32_t protection_end = num_protected_sectors; - - for (int sector = 0; sector < n_sectors; sector++) { - uint32_t addr = sector * sector_size; - - assert_page_mem(test_data, addr, 0xffffffff); - write_page_mem(test_data, addr, make_be32(0xabcdef12)); - - uint32_t expected_value = protection_start <= sector - && sector < protection_end - ? 0xffffffff : 0xabcdef12; - - assert_page_mem(test_data, addr, expected_value); - } - } - - flash_reset(test_data); -} - -static void test_write_page_qpi(const void *data) -{ - const TestData *test_data = (const TestData *)data; - uint32_t my_page_addr = test_data->page_addr; - uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - uint32_t page_pattern[] = { - 0xebd8c134, 0x5da196bc, 0xae15e729, 0x5085ccdf - }; - int i; - - spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); - - spi_ctrl_start_user(test_data); - flash_writeb(test_data, 0, EN_4BYTE_ADDR); - flash_writeb(test_data, 0, WREN); - flash_writeb(test_data, 0, PP); - flash_writel(test_data, 0, make_be32(my_page_addr)); - - /* Set QPI mode */ - spi_ctrl_set_io_mode(test_data, CTRL_IO_QUAD_IO); - - /* Fill the page pattern */ - for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { - flash_writel(test_data, 0, make_be32(page_pattern[i])); - } - - /* Fill the page with its own addresses */ - for (; i < FLASH_PAGE_SIZE / 4; i++) { - flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); - } - - /* Restore io mode */ - spi_ctrl_set_io_mode(test_data, 0); - spi_ctrl_stop_user(test_data); - - /* Check what was written */ - read_page(test_data, my_page_addr, page); - for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { - g_assert_cmphex(page[i], ==, page_pattern[i]); - } - for (; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page(test_data, some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(test_data); -} - -static void test_palmetto_bmc(TestData *data) +static void test_palmetto_bmc(AspeedSMCTestData *data) { int ret; int fd; @@ -751,25 +53,29 @@ static void test_palmetto_bmc(TestData *data) /* beyond 16MB */ data->page_addr = 0x14000 * FLASH_PAGE_SIZE; - qtest_add_data_func("/ast2400/smc/read_jedec", data, test_read_jedec); - qtest_add_data_func("/ast2400/smc/erase_sector", data, test_erase_sector); - qtest_add_data_func("/ast2400/smc/erase_all", data, test_erase_all); - qtest_add_data_func("/ast2400/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast2400/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2400/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2400/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2400/smc/write_page", + data, aspeed_smc_test_write_page); qtest_add_data_func("/ast2400/smc/read_page_mem", - data, test_read_page_mem); + data, aspeed_smc_test_read_page_mem); qtest_add_data_func("/ast2400/smc/write_page_mem", - data, test_write_page_mem); + data, aspeed_smc_test_write_page_mem); qtest_add_data_func("/ast2400/smc/read_status_reg", - data, test_read_status_reg); + data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2400/smc/status_reg_write_protection", - data, test_status_reg_write_protection); + data, aspeed_smc_test_status_reg_write_protection); qtest_add_data_func("/ast2400/smc/write_block_protect", - data, test_write_block_protect); + data, aspeed_smc_test_write_block_protect); qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit", - data, test_write_block_protect_bottom_bit); + data, aspeed_smc_test_write_block_protect_bottom_bit); } -static void test_ast2500_evb(TestData *data) +static void test_ast2500_evb(AspeedSMCTestData *data) { int ret; int fd; @@ -794,21 +100,25 @@ static void test_ast2500_evb(TestData *data) /* beyond 16MB */ data->page_addr = 0x14000 * FLASH_PAGE_SIZE; - qtest_add_data_func("/ast2500/smc/read_jedec", data, test_read_jedec); - qtest_add_data_func("/ast2500/smc/erase_sector", data, test_erase_sector); - qtest_add_data_func("/ast2500/smc/erase_all", data, test_erase_all); - qtest_add_data_func("/ast2500/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast2500/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2500/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2500/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2500/smc/write_page", + data, aspeed_smc_test_write_page); qtest_add_data_func("/ast2500/smc/read_page_mem", - data, test_read_page_mem); + data, aspeed_smc_test_read_page_mem); qtest_add_data_func("/ast2500/smc/write_page_mem", - data, test_write_page_mem); + data, aspeed_smc_test_write_page_mem); qtest_add_data_func("/ast2500/smc/read_status_reg", - data, test_read_status_reg); + data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2500/smc/write_page_qpi", - data, test_write_page_qpi); + data, aspeed_smc_test_write_page_qpi); } -static void test_ast2600_evb(TestData *data) +static void test_ast2600_evb(AspeedSMCTestData *data) { int ret; int fd; @@ -833,21 +143,25 @@ static void test_ast2600_evb(TestData *data) /* beyond 16MB */ data->page_addr = 0x14000 * FLASH_PAGE_SIZE; - qtest_add_data_func("/ast2600/smc/read_jedec", data, test_read_jedec); - qtest_add_data_func("/ast2600/smc/erase_sector", data, test_erase_sector); - qtest_add_data_func("/ast2600/smc/erase_all", data, test_erase_all); - qtest_add_data_func("/ast2600/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast2600/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2600/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2600/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2600/smc/write_page", + data, aspeed_smc_test_write_page); qtest_add_data_func("/ast2600/smc/read_page_mem", - data, test_read_page_mem); + data, aspeed_smc_test_read_page_mem); qtest_add_data_func("/ast2600/smc/write_page_mem", - data, test_write_page_mem); + data, aspeed_smc_test_write_page_mem); qtest_add_data_func("/ast2600/smc/read_status_reg", - data, test_read_status_reg); + data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2600/smc/write_page_qpi", - data, test_write_page_qpi); + data, aspeed_smc_test_write_page_qpi); } -static void test_ast1030_evb(TestData *data) +static void test_ast1030_evb(AspeedSMCTestData *data) { int ret; int fd; @@ -872,26 +186,30 @@ static void test_ast1030_evb(TestData *data) /* beyond 512KB */ data->page_addr = 0x800 * FLASH_PAGE_SIZE; - qtest_add_data_func("/ast1030/smc/read_jedec", data, test_read_jedec); - qtest_add_data_func("/ast1030/smc/erase_sector", data, test_erase_sector); - qtest_add_data_func("/ast1030/smc/erase_all", data, test_erase_all); - qtest_add_data_func("/ast1030/smc/write_page", data, test_write_page); + qtest_add_data_func("/ast1030/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast1030/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast1030/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast1030/smc/write_page", + data, aspeed_smc_test_write_page); qtest_add_data_func("/ast1030/smc/read_page_mem", - data, test_read_page_mem); + data, aspeed_smc_test_read_page_mem); qtest_add_data_func("/ast1030/smc/write_page_mem", - data, test_write_page_mem); + data, aspeed_smc_test_write_page_mem); qtest_add_data_func("/ast1030/smc/read_status_reg", - data, test_read_status_reg); + data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast1030/smc/write_page_qpi", - data, test_write_page_qpi); + data, aspeed_smc_test_write_page_qpi); } int main(int argc, char **argv) { - TestData palmetto_data; - TestData ast2500_evb_data; - TestData ast2600_evb_data; - TestData ast1030_evb_data; + AspeedSMCTestData palmetto_data; + AspeedSMCTestData ast2500_evb_data; + AspeedSMCTestData ast2600_evb_data; + AspeedSMCTestData ast1030_evb_data; int ret; g_test_init(&argc, &argv, NULL); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index bd41c9da5f..f8b3907e37 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -360,6 +360,7 @@ qtests = { 'virtio-net-failover': files('migration-helpers.c'), 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), + 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), } if vnc.found() From 124f4dc0d832c1bf3a4513c05a2b93bac0a5fac0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 27 Nov 2024 17:15:43 +0800 Subject: [PATCH 0072/2892] test/qtest/ast2700-smc-test: Support to test AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test_ast2700_evb function and reused testcases which are from aspeed_smc-test.c for AST2700 testing. The base address, flash base address and ce index of fmc_cs0 are 0x14000000, 0x100000000 and 0, respectively. The default flash model of fmc_cs0 is "w25q01jvq" whose size is 128MB, so set jedec_id 0xef4021. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20241127091543.1243114-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/ast2700-smc-test.c | 71 ++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 4 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/ast2700-smc-test.c diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c new file mode 100644 index 0000000000..d1c4856307 --- /dev/null +++ b/tests/qtest/ast2700-smc-test.c @@ -0,0 +1,71 @@ +/* + * QTest testcase for the M25P80 Flash using the ASPEED SPI Controller since + * AST2700. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" +#include "aspeed-smc-utils.h" + +static void test_ast2700_evb(AspeedSMCTestData *data) +{ + int ret; + int fd; + + fd = g_file_open_tmp("qtest.m25p80.w25q01jvq.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 128 * 1024 * 1024); + g_assert(ret == 0); + close(fd); + + data->s = qtest_initf("-machine ast2700-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with w25q01jvq flash */ + data->flash_base = 0x100000000; + data->spi_base = 0x14000000; + data->jedec_id = 0xef4021; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 64MB */ + data->page_addr = 0x40000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2700/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2700/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2700/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2700/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2700/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2700/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2700/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2700/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +int main(int argc, char **argv) +{ + AspeedSMCTestData ast2700_evb_data; + int ret; + + g_test_init(&argc, &argv, NULL); + + test_ast2700_evb(&ast2700_evb_data); + ret = g_test_run(); + + qtest_quit(ast2700_evb_data.s); + unlink(ast2700_evb_data.tmp_path); + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index f8b3907e37..89db3ecf2f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -214,7 +214,8 @@ qtests_aspeed = \ 'aspeed_smc-test', 'aspeed_gpio-test'] qtests_aspeed64 = \ - ['ast2700-gpio-test'] + ['ast2700-gpio-test', + 'ast2700-smc-test'] qtests_stm32l4x5 = \ ['stm32l4x5_exti-test', @@ -361,6 +362,7 @@ qtests = { 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), + 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), } if vnc.found() From 7956b0606879ddeddf04bcf8440aabcfe1393b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 26 Nov 2024 11:30:04 +0100 Subject: [PATCH 0073/2892] hw/i386: define _AS_LATEST() macros for machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the other architecture targets by adding extra macros for defining a versioned machine type as the latest. This reduces the size of the changes when introducing new machine types at the start of each release cycle. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cornelia Huck Signed-off-by: Daniel P. Berrangé Message-ID: <20240910163041.3764176-1-berrange@redhat.com> Signed-off-by: Cornelia Huck Message-ID: <20241126103005.3794748-2-cohuck@redhat.com> Signed-off-by: Thomas Huth --- hw/i386/pc_piix.c | 11 +++++------ hw/i386/pc_q35.c | 11 ++++++----- include/hw/i386/pc.h | 4 +++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2bf6865d40..4953676170 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -446,7 +446,10 @@ static void pc_i440fx_init(MachineState *machine) } #define DEFINE_I440FX_MACHINE(major, minor) \ - DEFINE_PC_VER_MACHINE(pc_i440fx, "pc-i440fx", pc_i440fx_init, major, minor); + DEFINE_PC_VER_MACHINE(pc_i440fx, "pc-i440fx", pc_i440fx_init, false, NULL, major, minor); + +#define DEFINE_I440FX_MACHINE_AS_LATEST(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_i440fx, "pc-i440fx", pc_i440fx_init, true, "pc", major, minor); static void pc_i440fx_machine_options(MachineClass *m) { @@ -477,17 +480,13 @@ static void pc_i440fx_machine_options(MachineClass *m) static void pc_i440fx_machine_9_2_options(MachineClass *m) { pc_i440fx_machine_options(m); - m->alias = "pc"; - m->is_default = true; } -DEFINE_I440FX_MACHINE(9, 2); +DEFINE_I440FX_MACHINE_AS_LATEST(9, 2); static void pc_i440fx_machine_9_1_options(MachineClass *m) { pc_i440fx_machine_9_2_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_9_1, hw_compat_9_1_len); compat_props_add(m->compat_props, pc_compat_9_1, pc_compat_9_1_len); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 8319b6d45e..42bdedbaa4 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -327,10 +327,13 @@ static void pc_q35_init(MachineState *machine) } #define DEFINE_Q35_MACHINE(major, minor) \ - DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, major, minor); + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, false, NULL, major, minor); + +#define DEFINE_Q35_MACHINE_AS_LATEST(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, false, "q35", major, minor); #define DEFINE_Q35_MACHINE_BUGFIX(major, minor, micro) \ - DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, major, minor, micro); + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, false, NULL, major, minor, micro); static void pc_q35_machine_options(MachineClass *m) { @@ -359,15 +362,13 @@ static void pc_q35_machine_options(MachineClass *m) static void pc_q35_machine_9_2_options(MachineClass *m) { pc_q35_machine_options(m); - m->alias = "q35"; } -DEFINE_Q35_MACHINE(9, 2); +DEFINE_Q35_MACHINE_AS_LATEST(9, 2); static void pc_q35_machine_9_1_options(MachineClass *m) { pc_q35_machine_9_2_options(m); - m->alias = NULL; compat_props_add(m->compat_props, hw_compat_9_1, hw_compat_9_1_len); compat_props_add(m->compat_props, pc_compat_9_1, pc_compat_9_1_len); } diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 14ee06287d..890427c56e 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -320,7 +320,7 @@ extern const size_t pc_compat_2_3_len; } \ type_init(pc_machine_init_##suffix) -#define DEFINE_PC_VER_MACHINE(namesym, namestr, initfn, ...) \ +#define DEFINE_PC_VER_MACHINE(namesym, namestr, initfn, isdefault, malias, ...) \ static void MACHINE_VER_SYM(init, namesym, __VA_ARGS__)( \ MachineState *machine) \ { \ @@ -334,6 +334,8 @@ extern const size_t pc_compat_2_3_len; MACHINE_VER_SYM(options, namesym, __VA_ARGS__)(mc); \ mc->init = MACHINE_VER_SYM(init, namesym, __VA_ARGS__); \ MACHINE_VER_DEPRECATION(__VA_ARGS__); \ + mc->is_default = isdefault; \ + mc->alias = malias; \ } \ static const TypeInfo MACHINE_VER_SYM(info, namesym, __VA_ARGS__) = \ { \ From 0a7c438a42f7a178504347456b9e50e3d2c50478 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 26 Nov 2024 11:30:05 +0100 Subject: [PATCH 0074/2892] hw: add compat machines for 10.0 Add 10.0 machine types for arm/i440fx/m68k/q35/s390x/spapr. Signed-off-by: Cornelia Huck Reviewed-by: Thomas Huth Message-ID: <20241126103005.3794748-3-cohuck@redhat.com> Signed-off-by: Thomas Huth --- hw/arm/virt.c | 11 +++++++++-- hw/core/machine.c | 3 +++ hw/i386/pc.c | 3 +++ hw/i386/pc_piix.c | 13 +++++++++++-- hw/i386/pc_q35.c | 13 +++++++++++-- hw/m68k/virt.c | 11 +++++++++-- hw/ppc/spapr.c | 17 ++++++++++++++--- hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++- include/hw/boards.h | 3 +++ include/hw/i386/pc.h | 3 +++ 10 files changed, 79 insertions(+), 12 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 1a381e9a2b..3bd9dd0f86 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3353,10 +3353,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_9_2_options(MachineClass *mc) +static void virt_machine_10_0_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(9, 2) +DEFINE_VIRT_MACHINE_AS_LATEST(10, 0) + +static void virt_machine_9_2_options(MachineClass *mc) +{ + virt_machine_10_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); +} +DEFINE_VIRT_MACHINE(9, 2) static void virt_machine_9_1_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index f29fe95964..e6900b43ef 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -36,6 +36,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_9_2[] = {}; +const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); + GlobalProperty hw_compat_9_1[] = { { TYPE_PCI_DEVICE, "x-pcie-ext-tag", "false" }, }; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 317aaca25a..99b9b105e2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -79,6 +79,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, +GlobalProperty pc_compat_9_2[] = {}; +const size_t pc_compat_9_2_len = G_N_ELEMENTS(pc_compat_9_2); + GlobalProperty pc_compat_9_1[] = { { "ICH9-LPC", "x-smi-swsmi-timer", "off" }, { "ICH9-LPC", "x-smi-periodic-timer", "off" }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 4953676170..e4365cbdb0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -477,12 +477,21 @@ static void pc_i440fx_machine_options(MachineClass *m) "Use a different south bridge than PIIX3"); } -static void pc_i440fx_machine_9_2_options(MachineClass *m) +static void pc_i440fx_machine_10_0_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(9, 2); +DEFINE_I440FX_MACHINE_AS_LATEST(10, 0); + +static void pc_i440fx_machine_9_2_options(MachineClass *m) +{ + pc_i440fx_machine_10_0_options(m); + compat_props_add(m->compat_props, hw_compat_9_2, hw_compat_9_2_len); + compat_props_add(m->compat_props, pc_compat_9_2, pc_compat_9_2_len); +} + +DEFINE_I440FX_MACHINE(9, 2); static void pc_i440fx_machine_9_1_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 42bdedbaa4..bbbdacda8e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -359,12 +359,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_9_2_options(MachineClass *m) +static void pc_q35_machine_10_0_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(9, 2); +DEFINE_Q35_MACHINE_AS_LATEST(10, 0); + +static void pc_q35_machine_9_2_options(MachineClass *m) +{ + pc_q35_machine_10_0_options(m); + compat_props_add(m->compat_props, hw_compat_9_2, hw_compat_9_2_len); + compat_props_add(m->compat_props, pc_compat_9_2, pc_compat_9_2_len); +} + +DEFINE_Q35_MACHINE(9, 2); static void pc_q35_machine_9_1_options(MachineClass *m) { diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index ea5c4a5a57..d0a7a6bfe2 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -366,10 +366,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) -static void virt_machine_9_2_options(MachineClass *mc) +static void virt_machine_10_0_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(9, 2) +DEFINE_VIRT_MACHINE_AS_LATEST(10, 0) + +static void virt_machine_9_2_options(MachineClass *mc) +{ + virt_machine_10_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); +} +DEFINE_VIRT_MACHINE(9, 2) static void virt_machine_9_1_options(MachineClass *mc) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 0d4efaa0c0..7251eea521 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4733,14 +4733,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* - * pseries-9.2 + * pseries-10.0 */ -static void spapr_machine_9_2_class_options(MachineClass *mc) +static void spapr_machine_10_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE_AS_LATEST(9, 2); +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 0); + +/* + * pseries-9.2 + */ +static void spapr_machine_9_2_class_options(MachineClass *mc) +{ + spapr_machine_10_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); +} + +DEFINE_SPAPR_MACHINE(9, 2); /* * pseries-9.1 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index fe03f716f3..67ae34aead 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -849,14 +849,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_10_0_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_10_0_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(10, 0); + static void ccw_machine_9_2_instance_options(MachineState *machine) { + ccw_machine_10_0_instance_options(machine); } static void ccw_machine_9_2_class_options(MachineClass *mc) { + ccw_machine_10_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); } -DEFINE_CCW_MACHINE_AS_LATEST(9, 2); +DEFINE_CCW_MACHINE(9, 2); static void ccw_machine_9_1_instance_options(MachineState *machine) { diff --git a/include/hw/boards.h b/include/hw/boards.h index 36fbb9b59d..7456889c37 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -756,6 +756,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_9_2[]; +extern const size_t hw_compat_9_2_len; + extern GlobalProperty hw_compat_9_1[]; extern const size_t hw_compat_9_1_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 890427c56e..1b26a417bd 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -215,6 +215,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_9_2[]; +extern const size_t pc_compat_9_2_len; + extern GlobalProperty pc_compat_9_1[]; extern const size_t pc_compat_9_1_len; From b5335c330c25710296d1c3ae0077e8be2f1c668e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 9 Dec 2024 16:35:06 +0000 Subject: [PATCH 0075/2892] docs: replace 'Edit on GitLab' with 'View page source' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU takes contributions via the mailing list, so while you can edit a file on gitlab and then switch to the terminal to send a patch, the wording 'Edit on GitLab' strongly suggests we take merge requests. Switching back to "View page source" is a more agnostic term that does not imply a particular contribution approach, that we had used in QEMU before: commit 73e6aec6522e1edd63f631c52577b49a39bc234f Author: Marc-André Lureau Date: Tue Mar 23 15:53:28 2021 +0400 sphinx: adopt kernel readthedoc theme Signed-off-by: Daniel P. Berrangé Reviewed-by: Pierrick Bouvier Message-ID: <20241209163506.2089961-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index c11a6ead8a..164a8ee8b2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -186,7 +186,7 @@ html_js_files = [ ] html_context = { - "display_gitlab": True, + "source_url_prefix": "https://gitlab.com/qemu-project/qemu/-/blob/master/docs/", "gitlab_user": "qemu-project", "gitlab_repo": "qemu", "gitlab_version": "master", From 1029cd5b9827ba6aa7f5e02eed1928a928e2fa5e Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:37 +0100 Subject: [PATCH 0076/2892] s390x/cpumodel: add msa10 subfunctions MSA10 introduces new AES XTS subfunctions. Signed-off-by: Hendrik Brueckner Reviewed-by: Christian Borntraeger Message-ID: <20241206122751.189721-2-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 2 ++ target/s390x/cpu_features_def.h.inc | 6 ++++++ target/s390x/cpu_models.c | 4 ++++ target/s390x/gen-features.c | 20 ++++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index cb4e2b8920..a3c239595a 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -252,6 +252,8 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("msa8", MSA_EXT_8, "Message-security-assist-extension 8 facility"), FEAT_GROUP_INIT("msa9", MSA_EXT_9, "Message-security-assist-extension 9 facility"), FEAT_GROUP_INIT("msa9_pckmo", MSA_EXT_9_PCKMO, "Message-security-assist-extension 9 PCKMO subfunctions"), + FEAT_GROUP_INIT("msa10", MSA_EXT_10, "Message-security-assist-extension 10 facility"), + FEAT_GROUP_INIT("msa10_pckmo", MSA_EXT_10_PCKMO, "Message-security-assist-extension 10 PCKMO subfunctions"), FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("esort", ENH_SORT, "Enhanced-sort facility"), FEAT_GROUP_INIT("deflate", DEFLATE_CONVERSION, "Deflate-conversion facility"), diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index c53ac13352..104d186c3f 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -233,6 +233,10 @@ DEF_FEAT(KM_XTS_AES_128, "km-xts-aes-128", KM, 50, "KM XTS-AES-128") DEF_FEAT(KM_XTS_AES_256, "km-xts-aes-256", KM, 52, "KM XTS-AES-256") DEF_FEAT(KM_XTS_EAES_128, "km-xts-eaes-128", KM, 58, "KM XTS-Encrypted-AES-128") DEF_FEAT(KM_XTS_EAES_256, "km-xts-eaes-256", KM, 60, "KM XTS-Encrypted-AES-256") +DEF_FEAT(KM_FULL_XTS_AES_128, "km-full-xts-aes-128", KM, 82, "KM Full-XTS-AES-128") +DEF_FEAT(KM_FULL_XTS_AES_256, "km-full-xts-aes-256", KM, 84, "KM Full-XTS-AES-256") +DEF_FEAT(KM_FULL_XTS_EAES_128, "km-full-xts-eaes-128", KM, 90, "KM Full-XTS-Encrypted-AES-128") +DEF_FEAT(KM_FULL_XTS_EAES_256, "km-full-xts-eaes-256", KM, 92, "KM Full-XTS-Encrypted-AES-256") /* Features exposed via the KIMD instruction. */ DEF_FEAT(KIMD_SHA_1, "kimd-sha-1", KIMD, 1, "KIMD SHA-1") @@ -264,6 +268,8 @@ DEF_FEAT(PCKMO_ETDEA_256, "pckmo-etdea-192", PCKMO, 3, "PCKMO Encrypted-TDEA-192 DEF_FEAT(PCKMO_AES_128, "pckmo-aes-128", PCKMO, 18, "PCKMO Encrypted-AES-128-Key") DEF_FEAT(PCKMO_AES_192, "pckmo-aes-192", PCKMO, 19, "PCKMO Encrypted-AES-192-Key") DEF_FEAT(PCKMO_AES_256, "pckmo-aes-256", PCKMO, 20, "PCKMO Encrypted-AES-256-Key") +DEF_FEAT(PCKMO_AES_XTS_128_DK, "pckmo-aes-xts-128-dk", PCKMO, 21, "PCKMO Encrypt-AES-XTS-128-Double-Key") +DEF_FEAT(PCKMO_AES_XTS_256_DK, "pckmo-aes-xts-256-dk", PCKMO, 22, "PCKMO Encrypt-AES-XTS-256-Double-Key") DEF_FEAT(PCKMO_ECC_P256, "pckmo-ecc-p256", PCKMO, 32, "PCKMO Encrypt-ECC-P256-Key") DEF_FEAT(PCKMO_ECC_P384, "pckmo-ecc-p384", PCKMO, 33, "PCKMO Encrypt-ECC-P384-Key") DEF_FEAT(PCKMO_ECC_P521, "pckmo-ecc-p521", PCKMO, 34, "PCKMO Encrypt-ECC-P521-Key") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index a27f4b6f79..3eb8276aee 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -477,6 +477,10 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_KLMD_SHA3_512, S390_FEAT_MSA }, { S390_FEAT_KLMD_SHAKE_128, S390_FEAT_MSA }, { S390_FEAT_KLMD_SHAKE_256, S390_FEAT_MSA }, + { S390_FEAT_KM_FULL_XTS_AES_128, S390_FEAT_MSA_EXT_4 }, + { S390_FEAT_KM_FULL_XTS_AES_256, S390_FEAT_MSA_EXT_4 }, + { S390_FEAT_KM_FULL_XTS_EAES_128, S390_FEAT_MSA_EXT_4 }, + { S390_FEAT_KM_FULL_XTS_EAES_256, S390_FEAT_MSA_EXT_4 }, { S390_FEAT_PRNO_TRNG_QRTCR, S390_FEAT_MSA_EXT_5 }, { S390_FEAT_PRNO_TRNG, S390_FEAT_MSA_EXT_5 }, { S390_FEAT_SIE_KSS, S390_FEAT_SIE_F2 }, diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 2b2bfc3736..06c3bf64f3 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -246,6 +246,16 @@ S390_FEAT_PCKMO_ECC_ED25519, \ S390_FEAT_PCKMO_ECC_ED448 +#define S390_FEAT_GROUP_MSA_EXT_10 \ + S390_FEAT_KM_FULL_XTS_AES_128, \ + S390_FEAT_KM_FULL_XTS_AES_256, \ + S390_FEAT_KM_FULL_XTS_EAES_128, \ + S390_FEAT_KM_FULL_XTS_EAES_256 + +#define S390_FEAT_GROUP_MSA_EXT_10_PCKMO \ + S390_FEAT_PCKMO_AES_XTS_128_DK, \ + S390_FEAT_PCKMO_AES_XTS_256_DK + #define S390_FEAT_GROUP_ENH_SORT \ S390_FEAT_ESORT_BASE, \ S390_FEAT_SORTL_SFLR, \ @@ -307,10 +317,18 @@ static uint16_t group_MSA_EXT_9[] = { S390_FEAT_GROUP_MSA_EXT_9, }; +static uint16_t group_MSA_EXT_10[] = { + S390_FEAT_GROUP_MSA_EXT_10, +}; + static uint16_t group_MSA_EXT_9_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_9_PCKMO, }; +static uint16_t group_MSA_EXT_10_PCKMO[] = { + S390_FEAT_GROUP_MSA_EXT_10_PCKMO, +}; + static uint16_t group_ENH_SORT[] = { S390_FEAT_GROUP_ENH_SORT, }; @@ -858,6 +876,8 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MSA_EXT_8), FEAT_GROUP_INITIALIZER(MSA_EXT_9), FEAT_GROUP_INITIALIZER(MSA_EXT_9_PCKMO), + FEAT_GROUP_INITIALIZER(MSA_EXT_10), + FEAT_GROUP_INITIALIZER(MSA_EXT_10_PCKMO), FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(ENH_SORT), FEAT_GROUP_INITIALIZER(DEFLATE_CONVERSION), From 11dc9020824c81552b021fbe0e2910e0348e7f8e Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:38 +0100 Subject: [PATCH 0077/2892] s390x/cpumodel: add msa11 subfunctions MSA11 introduces new HMAC subfunctions. Signed-off-by: Hendrik Brueckner Reviewed-by: Christian Borntraeger Message-ID: <20241206122751.189721-3-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 2 ++ target/s390x/cpu_features_def.h.inc | 10 ++++++++++ target/s390x/cpu_models.c | 8 ++++++++ target/s390x/gen-features.c | 24 ++++++++++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index a3c239595a..36930feccd 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -254,6 +254,8 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("msa9_pckmo", MSA_EXT_9_PCKMO, "Message-security-assist-extension 9 PCKMO subfunctions"), FEAT_GROUP_INIT("msa10", MSA_EXT_10, "Message-security-assist-extension 10 facility"), FEAT_GROUP_INIT("msa10_pckmo", MSA_EXT_10_PCKMO, "Message-security-assist-extension 10 PCKMO subfunctions"), + FEAT_GROUP_INIT("msa11", MSA_EXT_11, "Message-security-assist-extension 11 facility"), + FEAT_GROUP_INIT("msa11_pckmo", MSA_EXT_11_PCKMO, "Message-security-assist-extension 11 PCKMO subfunctions"), FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("esort", ENH_SORT, "Enhanced-sort facility"), FEAT_GROUP_INIT("deflate", DEFLATE_CONVERSION, "Deflate-conversion facility"), diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 104d186c3f..15ea51fc54 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -200,6 +200,14 @@ DEF_FEAT(KMAC_AES_256, "kmac-aes-256", KMAC, 20, "KMAC AES-256") DEF_FEAT(KMAC_EAES_128, "kmac-eaes-128", KMAC, 26, "KMAC Encrypted-AES-128") DEF_FEAT(KMAC_EAES_192, "kmac-eaes-192", KMAC, 27, "KMAC Encrypted-AES-192") DEF_FEAT(KMAC_EAES_256, "kmac-eaes-256", KMAC, 28, "KMAC Encrypted-AES-256") +DEF_FEAT(KMAC_HMAC_SHA_224, "kmac-hmac-sha-224", KMAC, 112, "KMAC HMAC-SHA-224") +DEF_FEAT(KMAC_HMAC_SHA_256, "kmac-hmac-sha-246", KMAC, 113, "KMAC HMAC-SHA-256") +DEF_FEAT(KMAC_HMAC_SHA_384, "kmac-hmac-sha-384", KMAC, 114, "KMAC HMAC-SHA-384") +DEF_FEAT(KMAC_HMAC_SHA_512, "kmac-hmac-sha-512", KMAC, 115, "KMAC HMAC-SHA-512") +DEF_FEAT(KMAC_HMAC_ESHA_224, "kmac-hmac-esha-224", KMAC, 120, "KMAC HMAC-Encrypted-SHA-224") +DEF_FEAT(KMAC_HMAC_ESHA_256, "kmac-hmac-esha-246", KMAC, 121, "KMAC HMAC-Encrypted-SHA-256") +DEF_FEAT(KMAC_HMAC_ESHA_384, "kmac-hmac-esha-384", KMAC, 122, "KMAC HMAC-Encrypted-SHA-384") +DEF_FEAT(KMAC_HMAC_ESHA_512, "kmac-hmac-esha-512", KMAC, 123, "KMAC HMAC-Encrypted-SHA-512") /* Features exposed via the KMC instruction. */ DEF_FEAT(KMC_DEA, "kmc-dea", KMC, 1, "KMC DEA") @@ -275,6 +283,8 @@ DEF_FEAT(PCKMO_ECC_P384, "pckmo-ecc-p384", PCKMO, 33, "PCKMO Encrypt-ECC-P384-Ke DEF_FEAT(PCKMO_ECC_P521, "pckmo-ecc-p521", PCKMO, 34, "PCKMO Encrypt-ECC-P521-Key") DEF_FEAT(PCKMO_ECC_ED25519, "pckmo-ecc-ed25519", PCKMO, 40 , "PCKMO Encrypt-ECC-Ed25519-Key") DEF_FEAT(PCKMO_ECC_ED448, "pckmo-ecc-ed448", PCKMO, 41 , "PCKMO Encrypt-ECC-Ed448-Key") +DEF_FEAT(PCKMO_HMAC_512, "pckmo-hmac-512", PCKMO, 118, "PCKMO Encrypt-HMAC-512-Key") +DEF_FEAT(PCKMO_HMAC_1024, "pckmo-hmac-1024", PCKMO, 122, "PCKMO Encrypt-HMAC-1024-Key") /* Features exposed via the KMCTR instruction. */ DEF_FEAT(KMCTR_DEA, "kmctr-dea", KMCTR, 1, "KMCTR DEA") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 3eb8276aee..a62a3c3771 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -477,6 +477,14 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_KLMD_SHA3_512, S390_FEAT_MSA }, { S390_FEAT_KLMD_SHAKE_128, S390_FEAT_MSA }, { S390_FEAT_KLMD_SHAKE_256, S390_FEAT_MSA }, + { S390_FEAT_KMAC_HMAC_SHA_224, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_SHA_256, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_SHA_384, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_SHA_512, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_224, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_256, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_384, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_512, S390_FEAT_MSA_EXT_3 }, { S390_FEAT_KM_FULL_XTS_AES_128, S390_FEAT_MSA_EXT_4 }, { S390_FEAT_KM_FULL_XTS_AES_256, S390_FEAT_MSA_EXT_4 }, { S390_FEAT_KM_FULL_XTS_EAES_128, S390_FEAT_MSA_EXT_4 }, diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 06c3bf64f3..d6305f945a 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -256,6 +256,20 @@ S390_FEAT_PCKMO_AES_XTS_128_DK, \ S390_FEAT_PCKMO_AES_XTS_256_DK +#define S390_FEAT_GROUP_MSA_EXT_11 \ + S390_FEAT_KMAC_HMAC_SHA_224, \ + S390_FEAT_KMAC_HMAC_SHA_256, \ + S390_FEAT_KMAC_HMAC_SHA_384, \ + S390_FEAT_KMAC_HMAC_SHA_512, \ + S390_FEAT_KMAC_HMAC_ESHA_224, \ + S390_FEAT_KMAC_HMAC_ESHA_256, \ + S390_FEAT_KMAC_HMAC_ESHA_384, \ + S390_FEAT_KMAC_HMAC_ESHA_512 + +#define S390_FEAT_GROUP_MSA_EXT_11_PCKMO \ + S390_FEAT_PCKMO_HMAC_512, \ + S390_FEAT_PCKMO_HMAC_1024 + #define S390_FEAT_GROUP_ENH_SORT \ S390_FEAT_ESORT_BASE, \ S390_FEAT_SORTL_SFLR, \ @@ -321,6 +335,10 @@ static uint16_t group_MSA_EXT_10[] = { S390_FEAT_GROUP_MSA_EXT_10, }; +static uint16_t group_MSA_EXT_11[] = { + S390_FEAT_GROUP_MSA_EXT_11, +}; + static uint16_t group_MSA_EXT_9_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_9_PCKMO, }; @@ -329,6 +347,10 @@ static uint16_t group_MSA_EXT_10_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_10_PCKMO, }; +static uint16_t group_MSA_EXT_11_PCKMO[] = { + S390_FEAT_GROUP_MSA_EXT_11_PCKMO, +}; + static uint16_t group_ENH_SORT[] = { S390_FEAT_GROUP_ENH_SORT, }; @@ -878,6 +900,8 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MSA_EXT_9_PCKMO), FEAT_GROUP_INITIALIZER(MSA_EXT_10), FEAT_GROUP_INITIALIZER(MSA_EXT_10_PCKMO), + FEAT_GROUP_INITIALIZER(MSA_EXT_11), + FEAT_GROUP_INITIALIZER(MSA_EXT_11_PCKMO), FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(ENH_SORT), FEAT_GROUP_INITIALIZER(DEFLATE_CONVERSION), From 496fc02e0e532f8917fa96e45fa531231e821c31 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:39 +0100 Subject: [PATCH 0078/2892] s390x/cpumodel: add msa12 changes MSA12 changes the KIMD/KLMD instruction format for SHA3/SHAKE. Signed-off-by: Hendrik Brueckner Reviewed-by: Christian Borntraeger Message-ID: <20241206122751.189721-4-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 1 + target/s390x/cpu_features_def.h.inc | 1 + target/s390x/gen-features.c | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 36930feccd..0e0b37ab95 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -256,6 +256,7 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("msa10_pckmo", MSA_EXT_10_PCKMO, "Message-security-assist-extension 10 PCKMO subfunctions"), FEAT_GROUP_INIT("msa11", MSA_EXT_11, "Message-security-assist-extension 11 facility"), FEAT_GROUP_INIT("msa11_pckmo", MSA_EXT_11_PCKMO, "Message-security-assist-extension 11 PCKMO subfunctions"), + FEAT_GROUP_INIT("msa12", MSA_EXT_12, "Message-security-assist-extension 12 facility"), FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("esort", ENH_SORT, "Enhanced-sort facility"), FEAT_GROUP_INIT("deflate", DEFLATE_CONVERSION, "Deflate-conversion facility"), diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 15ea51fc54..2e5dc96984 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -90,6 +90,7 @@ DEF_FEAT(EDAT_2, "edat2", STFL, 78, "Enhanced-DAT facility 2") DEF_FEAT(DFP_PACKED_CONVERSION, "dfppc", STFL, 80, "Decimal-floating-point packed-conversion facility") DEF_FEAT(PPA15, "ppa15", STFL, 81, "PPA15 is installed") DEF_FEAT(BPB, "bpb", STFL, 82, "Branch prediction blocking") +DEF_FEAT(MSA_EXT_12, "msa12-base", STFL, 86, "Message-security-assist-extension-12 facility (excluding subfunctions)") DEF_FEAT(VECTOR, "vx", STFL, 129, "Vector facility") DEF_FEAT(INSTRUCTION_EXEC_PROT, "iep", STFL, 130, "Instruction-execution-protection facility") DEF_FEAT(SIDE_EFFECT_ACCESS_ESOP2, "sea_esop2", STFL, 131, "Side-effect-access facility and Enhanced-suppression-on-protection facility 2") diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index d6305f945a..ab9ad51d5e 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -270,6 +270,9 @@ S390_FEAT_PCKMO_HMAC_512, \ S390_FEAT_PCKMO_HMAC_1024 +#define S390_FEAT_GROUP_MSA_EXT_12 \ + S390_FEAT_MSA_EXT_12 + #define S390_FEAT_GROUP_ENH_SORT \ S390_FEAT_ESORT_BASE, \ S390_FEAT_SORTL_SFLR, \ @@ -339,6 +342,10 @@ static uint16_t group_MSA_EXT_11[] = { S390_FEAT_GROUP_MSA_EXT_11, }; +static uint16_t group_MSA_EXT_12[] = { + S390_FEAT_GROUP_MSA_EXT_12, +}; + static uint16_t group_MSA_EXT_9_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_9_PCKMO, }; @@ -902,6 +909,7 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MSA_EXT_10_PCKMO), FEAT_GROUP_INITIALIZER(MSA_EXT_11), FEAT_GROUP_INITIALIZER(MSA_EXT_11_PCKMO), + FEAT_GROUP_INITIALIZER(MSA_EXT_12), FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(ENH_SORT), FEAT_GROUP_INITIALIZER(DEFLATE_CONVERSION), From ba4614fdacc2ea55060ddb48bdb2ebd21d0c3464 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:40 +0100 Subject: [PATCH 0079/2892] s390x/cpumodel: add msa13 subfunctions MSA13 introduces query authentication information (QAI) subfunctions. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-5-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 2 ++ target/s390x/cpu_features_def.h.inc | 12 ++++++++++++ target/s390x/gen-features.c | 26 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 0e0b37ab95..9ba127e386 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -257,6 +257,8 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("msa11", MSA_EXT_11, "Message-security-assist-extension 11 facility"), FEAT_GROUP_INIT("msa11_pckmo", MSA_EXT_11_PCKMO, "Message-security-assist-extension 11 PCKMO subfunctions"), FEAT_GROUP_INIT("msa12", MSA_EXT_12, "Message-security-assist-extension 12 facility"), + FEAT_GROUP_INIT("msa13", MSA_EXT_13, "Message-security-assist-extension 13 facility"), + FEAT_GROUP_INIT("msa13_pckmo", MSA_EXT_13_PCKMO, "Message-security-assist-extension 13 PCKMO subfunctions"), FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("esort", ENH_SORT, "Enhanced-sort facility"), FEAT_GROUP_INIT("deflate", DEFLATE_CONVERSION, "Deflate-conversion facility"), diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 2e5dc96984..2132837ffe 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -209,6 +209,7 @@ DEF_FEAT(KMAC_HMAC_ESHA_224, "kmac-hmac-esha-224", KMAC, 120, "KMAC HMAC-Encrypt DEF_FEAT(KMAC_HMAC_ESHA_256, "kmac-hmac-esha-246", KMAC, 121, "KMAC HMAC-Encrypted-SHA-256") DEF_FEAT(KMAC_HMAC_ESHA_384, "kmac-hmac-esha-384", KMAC, 122, "KMAC HMAC-Encrypted-SHA-384") DEF_FEAT(KMAC_HMAC_ESHA_512, "kmac-hmac-esha-512", KMAC, 123, "KMAC HMAC-Encrypted-SHA-512") +DEF_FEAT(KMAC_QAI, "kmac-qai", KMAC, 127, "KMAC Query-Authentication-Information") /* Features exposed via the KMC instruction. */ DEF_FEAT(KMC_DEA, "kmc-dea", KMC, 1, "KMC DEA") @@ -246,6 +247,7 @@ DEF_FEAT(KM_FULL_XTS_AES_128, "km-full-xts-aes-128", KM, 82, "KM Full-XTS-AES-12 DEF_FEAT(KM_FULL_XTS_AES_256, "km-full-xts-aes-256", KM, 84, "KM Full-XTS-AES-256") DEF_FEAT(KM_FULL_XTS_EAES_128, "km-full-xts-eaes-128", KM, 90, "KM Full-XTS-Encrypted-AES-128") DEF_FEAT(KM_FULL_XTS_EAES_256, "km-full-xts-eaes-256", KM, 92, "KM Full-XTS-Encrypted-AES-256") +DEF_FEAT(KM_QAI, "km-qai", KM, 127, "KM Query-Authentication-Information") /* Features exposed via the KIMD instruction. */ DEF_FEAT(KIMD_SHA_1, "kimd-sha-1", KIMD, 1, "KIMD SHA-1") @@ -258,6 +260,7 @@ DEF_FEAT(KIMD_SHA3_512, "kimd-sha3-512", KIMD, 35, "KIMD SHA3-512") DEF_FEAT(KIMD_SHAKE_128, "kimd-shake-128", KIMD, 36, "KIMD SHAKE-128") DEF_FEAT(KIMD_SHAKE_256, "kimd-shake-256", KIMD, 37, "KIMD SHAKE-256") DEF_FEAT(KIMD_GHASH, "kimd-ghash", KIMD, 65, "KIMD GHASH") +DEF_FEAT(KIMD_QAI, "kimd-qai", KIMD, 127, "KIMD Query-Authentication-Information") /* Features exposed via the KLMD instruction. */ DEF_FEAT(KLMD_SHA_1, "klmd-sha-1", KLMD, 1, "KLMD SHA-1") @@ -269,6 +272,7 @@ DEF_FEAT(KLMD_SHA3_384, "klmd-sha3-384", KLMD, 34, "KLMD SHA3-384") DEF_FEAT(KLMD_SHA3_512, "klmd-sha3-512", KLMD, 35, "KLMD SHA3-512") DEF_FEAT(KLMD_SHAKE_128, "klmd-shake-128", KLMD, 36, "KLMD SHAKE-128") DEF_FEAT(KLMD_SHAKE_256, "klmd-shake-256", KLMD, 37, "KLMD SHAKE-256") +DEF_FEAT(KLMD_QAI, "klmd-qai", KLMD, 127, "KLMD Query-Authentication-Information") /* Features exposed via the PCKMO instruction. */ DEF_FEAT(PCKMO_EDEA, "pckmo-edea", PCKMO, 1, "PCKMO Encrypted-DEA-Key") @@ -286,6 +290,7 @@ DEF_FEAT(PCKMO_ECC_ED25519, "pckmo-ecc-ed25519", PCKMO, 40 , "PCKMO Encrypt-ECC- DEF_FEAT(PCKMO_ECC_ED448, "pckmo-ecc-ed448", PCKMO, 41 , "PCKMO Encrypt-ECC-Ed448-Key") DEF_FEAT(PCKMO_HMAC_512, "pckmo-hmac-512", PCKMO, 118, "PCKMO Encrypt-HMAC-512-Key") DEF_FEAT(PCKMO_HMAC_1024, "pckmo-hmac-1024", PCKMO, 122, "PCKMO Encrypt-HMAC-1024-Key") +DEF_FEAT(PCKMO_QAI, "pckmo-qai", PCKMO, 127, "PCKMO Query-Authentication-Information") /* Features exposed via the KMCTR instruction. */ DEF_FEAT(KMCTR_DEA, "kmctr-dea", KMCTR, 1, "KMCTR DEA") @@ -300,6 +305,7 @@ DEF_FEAT(KMCTR_AES_256, "kmctr-aes-256", KMCTR, 20, "KMCTR AES-256") DEF_FEAT(KMCTR_EAES_128, "kmctr-eaes-128", KMCTR, 26, "KMCTR Encrypted-AES-128") DEF_FEAT(KMCTR_EAES_192, "kmctr-eaes-192", KMCTR, 27, "KMCTR Encrypted-AES-192") DEF_FEAT(KMCTR_EAES_256, "kmctr-eaes-256", KMCTR, 28, "KMCTR Encrypted-AES-256") +DEF_FEAT(KMCTR_QAI, "kmctr-qai", KMCTR, 127, "KMCTR Query-Authentication-Information") /* Features exposed via the KMF instruction. */ DEF_FEAT(KMF_DEA, "kmf-dea", KMF, 1, "KMF DEA") @@ -314,6 +320,7 @@ DEF_FEAT(KMF_AES_256, "kmf-aes-256", KMF, 20, "KMF AES-256") DEF_FEAT(KMF_EAES_128, "kmf-eaes-128", KMF, 26, "KMF Encrypted-AES-128") DEF_FEAT(KMF_EAES_192, "kmf-eaes-192", KMF, 27, "KMF Encrypted-AES-192") DEF_FEAT(KMF_EAES_256, "kmf-eaes-256", KMF, 28, "KMF Encrypted-AES-256") +DEF_FEAT(KMF_QAI, "kmf-qai", KMF, 127, "KMF Query-Authentication-Information") /* Features exposed via the KMO instruction. */ DEF_FEAT(KMO_DEA, "kmo-dea", KMO, 1, "KMO DEA") @@ -328,6 +335,7 @@ DEF_FEAT(KMO_AES_256, "kmo-aes-256", KMO, 20, "KMO AES-256") DEF_FEAT(KMO_EAES_128, "kmo-eaes-128", KMO, 26, "KMO Encrypted-AES-128") DEF_FEAT(KMO_EAES_192, "kmo-eaes-192", KMO, 27, "KMO Encrypted-AES-192") DEF_FEAT(KMO_EAES_256, "kmo-eaes-256", KMO, 28, "KMO Encrypted-AES-256") +DEF_FEAT(KMO_QAI, "kmo-qai", KMO, 127, "KMO Query-Authentication-Information") /* Features exposed via the PCC instruction. */ DEF_FEAT(PCC_CMAC_DEA, "pcc-cmac-dea", PCC, 1, "PCC Compute-Last-Block-CMAC-Using-DEA") @@ -353,11 +361,13 @@ DEF_FEAT(PCC_SCALAR_MULT_ED25519, "pcc-scalar-mult-ed25519", PCC, 72, "PCC Scala DEF_FEAT(PCC_SCALAR_MULT_ED448, "pcc-scalar-mult-ed448", PCC, 73, "PCC Scalar-Multiply-Ed448") DEF_FEAT(PCC_SCALAR_MULT_X25519, "pcc-scalar-mult-x25519", PCC, 80, "PCC Scalar-Multiply-X25519") DEF_FEAT(PCC_SCALAR_MULT_X448, "pcc-scalar-mult-x448", PCC, 81, "PCC Scalar-Multiply-X448") +DEF_FEAT(PCC_QAI, "pcc-qai", PCC, 127, "PCC Query-Authentication-Information") /* Features exposed via the PPNO/PRNO instruction. */ DEF_FEAT(PPNO_SHA_512_DRNG, "ppno-sha-512-drng", PPNO, 3, "PPNO SHA-512-DRNG") DEF_FEAT(PRNO_TRNG_QRTCR, "prno-trng-qrtcr", PPNO, 112, "PRNO TRNG-Query-Raw-to-Conditioned-Ratio") DEF_FEAT(PRNO_TRNG, "prno-trng", PPNO, 114, "PRNO TRNG") +DEF_FEAT(PRNO_QAI, "prno-qai", PPNO, 127, "PRNO Query-Authentication-Information") /* Features exposed via the KMA instruction. */ DEF_FEAT(KMA_GCM_AES_128, "kma-gcm-aes-128", KMA, 18, "KMA GCM-AES-128") @@ -366,6 +376,7 @@ DEF_FEAT(KMA_GCM_AES_256, "kma-gcm-aes-256", KMA, 20, "KMA GCM-AES-256") DEF_FEAT(KMA_GCM_EAES_128, "kma-gcm-eaes-128", KMA, 26, "KMA GCM-Encrypted-AES-128") DEF_FEAT(KMA_GCM_EAES_192, "kma-gcm-eaes-192", KMA, 27, "KMA GCM-Encrypted-AES-192") DEF_FEAT(KMA_GCM_EAES_256, "kma-gcm-eaes-256", KMA, 28, "KMA GCM-Encrypted-AES-256") +DEF_FEAT(KMA_QAI, "kma-qai", KMA, 127, "KMA Query-Authentication-Information") /* Features exposed via the KDSA instruction. */ DEF_FEAT(KDSA_ECDSA_VERIFY_P256, "kdsa-ecdsa-verify-p256", KDSA, 1, "KDSA ECDSA-Verify-P256") @@ -383,6 +394,7 @@ DEF_FEAT(KDSA_EDDSA_SIGN_ED25519, "kdsa-eddsa-sign-ed25519", KDSA, 40, "KDSA EdD DEF_FEAT(KDSA_EDDSA_SIGN_ED448, "kdsa-eddsa-sign-ed448", KDSA, 44, "KDSA EdDSA-Sign-Ed448") DEF_FEAT(KDSA_EEDDSA_SIGN_ED25519, "kdsa-eeddsa-sign-ed25519", KDSA, 48, "KDSA Encrypted-EdDSA-Sign-Ed25519") DEF_FEAT(KDSA_EEDDSA_SIGN_ED448, "kdsa-eeddsa-sign-ed448", KDSA, 52, "KDSA Encrypted-EdDSA-Sign-Ed448") +DEF_FEAT(KDSA_QAI, "kdsa-qai", KDSA, 127, "KDSA Query-Authentication-Information") /* Features exposed via the SORTL instruction. */ DEF_FEAT(SORTL_SFLR, "sortl-sflr", SORTL, 1, "SORTL SFLR") diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index ab9ad51d5e..3326e7df43 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -273,6 +273,22 @@ #define S390_FEAT_GROUP_MSA_EXT_12 \ S390_FEAT_MSA_EXT_12 +#define S390_FEAT_GROUP_MSA_EXT_13 \ + S390_FEAT_KDSA_QAI, \ + S390_FEAT_KIMD_QAI, \ + S390_FEAT_KLMD_QAI, \ + S390_FEAT_KMAC_QAI, \ + S390_FEAT_KMA_QAI, \ + S390_FEAT_KMCTR_QAI, \ + S390_FEAT_KMF_QAI, \ + S390_FEAT_KMO_QAI, \ + S390_FEAT_KM_QAI, \ + S390_FEAT_PCC_QAI, \ + S390_FEAT_PRNO_QAI + +#define S390_FEAT_GROUP_MSA_EXT_13_PCKMO \ + S390_FEAT_PCKMO_QAI + #define S390_FEAT_GROUP_ENH_SORT \ S390_FEAT_ESORT_BASE, \ S390_FEAT_SORTL_SFLR, \ @@ -346,6 +362,10 @@ static uint16_t group_MSA_EXT_12[] = { S390_FEAT_GROUP_MSA_EXT_12, }; +static uint16_t group_MSA_EXT_13[] = { + S390_FEAT_GROUP_MSA_EXT_13, +}; + static uint16_t group_MSA_EXT_9_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_9_PCKMO, }; @@ -358,6 +378,10 @@ static uint16_t group_MSA_EXT_11_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_11_PCKMO, }; +static uint16_t group_MSA_EXT_13_PCKMO[] = { + S390_FEAT_GROUP_MSA_EXT_13_PCKMO, +}; + static uint16_t group_ENH_SORT[] = { S390_FEAT_GROUP_ENH_SORT, }; @@ -910,6 +934,8 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MSA_EXT_11), FEAT_GROUP_INITIALIZER(MSA_EXT_11_PCKMO), FEAT_GROUP_INITIALIZER(MSA_EXT_12), + FEAT_GROUP_INITIALIZER(MSA_EXT_13), + FEAT_GROUP_INITIALIZER(MSA_EXT_13_PCKMO), FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(ENH_SORT), FEAT_GROUP_INITIALIZER(DEFLATE_CONVERSION), From eba6f49128fbcf75d19acb6aef250d0664d03e9e Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:41 +0100 Subject: [PATCH 0080/2892] s390x/cpumodel: Add ptff Query Time-Stamp Event (QTSE) support Introduce a new PTFF subfunction to query-stamp events. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-6-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 1 + target/s390x/cpu_features_def.h.inc | 1 + target/s390x/gen-features.c | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 9ba127e386..385a2ff860 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -241,6 +241,7 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"), FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"), FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"), + FEAT_GROUP_INIT("gen17ptff", GEN17_PTFF, "PTFF enhancements introduced with gen17"), FEAT_GROUP_INIT("msa", MSA, "Message-security-assist facility"), FEAT_GROUP_INIT("msa1", MSA_EXT_1, "Message-security-assist-extension 1 facility"), FEAT_GROUP_INIT("msa2", MSA_EXT_2, "Message-security-assist-extension 2 facility"), diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 2132837ffe..f96cb5a7d8 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -181,6 +181,7 @@ DEF_FEAT(PTFF_QSI, "ptff-qsi", PTFF, 2, "PTFF Query Steering Information") DEF_FEAT(PTFF_QPT, "ptff-qpc", PTFF, 3, "PTFF Query Physical Clock") DEF_FEAT(PTFF_QUI, "ptff-qui", PTFF, 4, "PTFF Query UTC Information") DEF_FEAT(PTFF_QTOU, "ptff-qtou", PTFF, 5, "PTFF Query TOD Offset User") +DEF_FEAT(PTFF_QTSE, "ptff-qtse", PTFF, 6, "PTFF Query Time-Stamp Event") DEF_FEAT(PTFF_QSIE, "ptff-qsie", PTFF, 10, "PTFF Query Steering Information Extended") DEF_FEAT(PTFF_QTOUE, "ptff-qtoue", PTFF, 13, "PTFF Query TOD Offset User Extended") DEF_FEAT(PTFF_STO, "ptff-sto", PTFF, 65, "PTFF Set TOD Offset") diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 3326e7df43..302b653214 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -64,6 +64,9 @@ S390_FEAT_PTFF_STOE, \ S390_FEAT_PTFF_STOUE +#define S390_FEAT_GROUP_GEN17_PTFF \ + S390_FEAT_PTFF_QTSE + #define S390_FEAT_GROUP_MSA \ S390_FEAT_MSA, \ S390_FEAT_KMAC_DEA, \ @@ -318,6 +321,11 @@ static uint16_t group_GEN13_PTFF[] = { static uint16_t group_MULTIPLE_EPOCH_PTFF[] = { S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF, }; + +static uint16_t group_GEN17_PTFF[] = { + S390_FEAT_GROUP_GEN17_PTFF, +}; + static uint16_t group_MSA[] = { S390_FEAT_GROUP_MSA, }; @@ -918,6 +926,7 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(PLO), FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING), FEAT_GROUP_INITIALIZER(GEN13_PTFF), + FEAT_GROUP_INITIALIZER(GEN17_PTFF), FEAT_GROUP_INITIALIZER(MSA), FEAT_GROUP_INITIALIZER(MSA_EXT_1), FEAT_GROUP_INITIALIZER(MSA_EXT_2), From 44fe383c274174405da79f5fcb028e39fe688036 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:42 +0100 Subject: [PATCH 0081/2892] linux-headers: Update to Linux 6.13-rc1 This linux headers update includes required changes for the gen17 CPU model. Signed-off-by: Hendrik Brueckner Suggested-by: Thomas Huth Message-ID: <20241206122751.189721-7-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- include/standard-headers/drm/drm_fourcc.h | 1 + include/standard-headers/linux/ethtool.h | 5 + include/standard-headers/linux/pci_regs.h | 38 ++- .../standard-headers/linux/virtio_crypto.h | 1 + include/standard-headers/linux/virtio_pci.h | 131 ++++++++++ linux-headers/asm-arm64/kvm.h | 6 + linux-headers/asm-arm64/unistd_64.h | 4 + linux-headers/asm-generic/mman-common.h | 3 + linux-headers/asm-generic/mman.h | 4 + linux-headers/asm-generic/unistd.h | 11 +- linux-headers/asm-loongarch/kvm.h | 20 ++ linux-headers/asm-loongarch/unistd_64.h | 4 + linux-headers/asm-mips/mman.h | 3 + linux-headers/asm-mips/unistd_n32.h | 4 + linux-headers/asm-mips/unistd_n64.h | 4 + linux-headers/asm-mips/unistd_o32.h | 4 + linux-headers/asm-powerpc/unistd_32.h | 4 + linux-headers/asm-powerpc/unistd_64.h | 4 + linux-headers/asm-riscv/kvm.h | 4 + linux-headers/asm-riscv/unistd_32.h | 4 + linux-headers/asm-riscv/unistd_64.h | 4 + linux-headers/asm-s390/kvm.h | 3 +- linux-headers/asm-s390/unistd_32.h | 4 + linux-headers/asm-s390/unistd_64.h | 4 + linux-headers/asm-x86/kvm.h | 1 + linux-headers/asm-x86/mman.h | 3 - linux-headers/asm-x86/unistd_32.h | 4 + linux-headers/asm-x86/unistd_64.h | 4 + linux-headers/asm-x86/unistd_x32.h | 4 + linux-headers/linux/iommufd.h | 224 +++++++++++++++++- linux-headers/linux/kvm.h | 8 + linux-headers/linux/psci.h | 5 + linux-headers/linux/vfio.h | 2 +- 33 files changed, 506 insertions(+), 23 deletions(-) diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index d4a2231306..708647776f 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -1515,6 +1515,7 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) * 64K_D_2D on GFX12 is identical to 64K_D on GFX11. */ #define AMD_FMT_MOD_TILE_GFX9_64K_D 10 +#define AMD_FMT_MOD_TILE_GFX9_4K_D_X 22 #define AMD_FMT_MOD_TILE_GFX9_64K_S_X 25 #define AMD_FMT_MOD_TILE_GFX9_64K_D_X 26 #define AMD_FMT_MOD_TILE_GFX9_64K_R_X 27 diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index b05e84825b..67c47912e5 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -2526,6 +2526,11 @@ struct ethtool_link_settings { uint8_t master_slave_state; uint8_t rate_matching; uint32_t reserved[7]; + /* Linux builds with -Wflex-array-member-not-at-end but does + * not use the "link_mode_masks" member. Leave it defined for + * userspace for now, and when userspace wants to start using + * -Wfamnae, we'll need a new solution. + */ uint32_t link_mode_masks[]; /* layout of link_mode_masks fields: * uint32_t map_supported[link_mode_masks_nwords]; diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 12323b3334..1601c7ed5f 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -340,7 +340,8 @@ #define PCI_MSIX_ENTRY_UPPER_ADDR 0x4 /* Message Upper Address */ #define PCI_MSIX_ENTRY_DATA 0x8 /* Message Data */ #define PCI_MSIX_ENTRY_VECTOR_CTRL 0xc /* Vector Control */ -#define PCI_MSIX_ENTRY_CTRL_MASKBIT 0x00000001 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 0x00000001 /* Mask Bit */ +#define PCI_MSIX_ENTRY_CTRL_ST 0xffff0000 /* Steering Tag */ /* CompactPCI Hotswap Register */ @@ -659,6 +660,7 @@ #define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */ #define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */ #define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ +#define PCI_EXP_DEVCAP2_TPH_COMP_MASK 0x00003000 /* TPH completer support */ #define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ @@ -678,6 +680,7 @@ #define PCI_EXP_DEVSTA2 0x2a /* Device Status 2 */ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 0x2c /* end of v2 EPs w/o link */ #define PCI_EXP_LNKCAP2 0x2c /* Link Capabilities 2 */ +#define PCI_EXP_LNKCAP2_SLS 0x000000fe /* Supported Link Speeds Vector */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ @@ -1023,15 +1026,34 @@ #define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */ #define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */ +/* TPH Completer Support */ +#define PCI_EXP_DEVCAP2_TPH_COMP_NONE 0x0 /* None */ +#define PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY 0x1 /* TPH only */ +#define PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH 0x3 /* TPH and Extended TPH */ + /* TPH Requester */ #define PCI_TPH_CAP 4 /* capability register */ -#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */ -#define PCI_TPH_LOC_NONE 0x000 /* no location */ -#define PCI_TPH_LOC_CAP 0x200 /* in capability */ -#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ -#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST table mask */ -#define PCI_TPH_CAP_ST_SHIFT 16 /* ST table shift */ -#define PCI_TPH_BASE_SIZEOF 0xc /* size with no ST table */ +#define PCI_TPH_CAP_ST_NS 0x00000001 /* No ST Mode Supported */ +#define PCI_TPH_CAP_ST_IV 0x00000002 /* Interrupt Vector Mode Supported */ +#define PCI_TPH_CAP_ST_DS 0x00000004 /* Device Specific Mode Supported */ +#define PCI_TPH_CAP_EXT_TPH 0x00000100 /* Ext TPH Requester Supported */ +#define PCI_TPH_CAP_LOC_MASK 0x00000600 /* ST Table Location */ +#define PCI_TPH_LOC_NONE 0x00000000 /* Not present */ +#define PCI_TPH_LOC_CAP 0x00000200 /* In capability */ +#define PCI_TPH_LOC_MSIX 0x00000400 /* In MSI-X */ +#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST Table Size */ +#define PCI_TPH_CAP_ST_SHIFT 16 /* ST Table Size shift */ +#define PCI_TPH_BASE_SIZEOF 0xc /* Size with no ST table */ + +#define PCI_TPH_CTRL 8 /* control register */ +#define PCI_TPH_CTRL_MODE_SEL_MASK 0x00000007 /* ST Mode Select */ +#define PCI_TPH_ST_NS_MODE 0x0 /* No ST Mode */ +#define PCI_TPH_ST_IV_MODE 0x1 /* Interrupt Vector Mode */ +#define PCI_TPH_ST_DS_MODE 0x2 /* Device Specific Mode */ +#define PCI_TPH_CTRL_REQ_EN_MASK 0x00000300 /* TPH Requester Enable */ +#define PCI_TPH_REQ_DISABLE 0x0 /* No TPH requests allowed */ +#define PCI_TPH_REQ_TPH_ONLY 0x1 /* TPH only requests allowed */ +#define PCI_TPH_REQ_EXT_TPH 0x3 /* Extended TPH requests allowed */ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 0x04 /* DPC Capability */ diff --git a/include/standard-headers/linux/virtio_crypto.h b/include/standard-headers/linux/virtio_crypto.h index 68066dafb6..4d350ae595 100644 --- a/include/standard-headers/linux/virtio_crypto.h +++ b/include/standard-headers/linux/virtio_crypto.h @@ -329,6 +329,7 @@ struct virtio_crypto_op_header { VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x00) #define VIRTIO_CRYPTO_AKCIPHER_DECRYPT \ VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x01) + /* akcipher sign/verify opcodes are deprecated */ #define VIRTIO_CRYPTO_AKCIPHER_SIGN \ VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x02) #define VIRTIO_CRYPTO_AKCIPHER_VERIFY \ diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h index 4010216103..b177ed8972 100644 --- a/include/standard-headers/linux/virtio_pci.h +++ b/include/standard-headers/linux/virtio_pci.h @@ -40,6 +40,7 @@ #define _LINUX_VIRTIO_PCI_H #include "standard-headers/linux/types.h" +#include "standard-headers/linux/kernel.h" #ifndef VIRTIO_PCI_NO_LEGACY @@ -240,6 +241,17 @@ struct virtio_pci_cfg_cap { #define VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ 0x5 #define VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO 0x6 +/* Device parts access commands. */ +#define VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY 0x7 +#define VIRTIO_ADMIN_CMD_DEVICE_CAP_GET 0x8 +#define VIRTIO_ADMIN_CMD_DRIVER_CAP_SET 0x9 +#define VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE 0xa +#define VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY 0xd +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET 0xe +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET 0xf +#define VIRTIO_ADMIN_CMD_DEV_PARTS_SET 0x10 +#define VIRTIO_ADMIN_CMD_DEV_MODE_SET 0x11 + struct virtio_admin_cmd_hdr { uint16_t opcode; /* @@ -286,4 +298,123 @@ struct virtio_admin_cmd_notify_info_result { struct virtio_admin_cmd_notify_info_data entries[VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO]; }; +#define VIRTIO_DEV_PARTS_CAP 0x0000 + +struct virtio_dev_parts_cap { + uint8_t get_parts_resource_objects_limit; + uint8_t set_parts_resource_objects_limit; +}; + +#define MAX_CAP_ID __KERNEL_DIV_ROUND_UP(VIRTIO_DEV_PARTS_CAP + 1, 64) + +struct virtio_admin_cmd_query_cap_id_result { + uint64_t supported_caps[MAX_CAP_ID]; +}; + +struct virtio_admin_cmd_cap_get_data { + uint16_t id; + uint8_t reserved[6]; +}; + +struct virtio_admin_cmd_cap_set_data { + uint16_t id; + uint8_t reserved[6]; + uint8_t cap_specific_data[]; +}; + +struct virtio_admin_cmd_resource_obj_cmd_hdr { + uint16_t type; + uint8_t reserved[2]; + uint32_t id; /* Indicates unique resource object id per resource object type */ +}; + +struct virtio_admin_cmd_resource_obj_create_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + uint64_t flags; + uint8_t resource_obj_specific_data[]; +}; + +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS 0 + +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET 0 +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_SET 1 + +struct virtio_resource_obj_dev_parts { + uint8_t type; + uint8_t reserved[7]; +}; + +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE 0 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_COUNT 1 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_LIST 2 + +struct virtio_admin_cmd_dev_parts_metadata_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + uint8_t type; + uint8_t reserved[7]; +}; + +#define VIRTIO_DEV_PART_F_OPTIONAL 0 + +struct virtio_dev_part_hdr { + uint16_t part_type; + uint8_t flags; + uint8_t reserved; + union { + struct { + uint32_t offset; + uint32_t reserved; + } pci_common_cfg; + struct { + uint16_t index; + uint8_t reserved[6]; + } vq_index; + } selector; + uint32_t length; +}; + +struct virtio_dev_part { + struct virtio_dev_part_hdr hdr; + uint8_t value[]; +}; + +struct virtio_admin_cmd_dev_parts_metadata_result { + union { + struct { + uint32_t size; + uint32_t reserved; + } parts_size; + struct { + uint32_t count; + uint32_t reserved; + } hdr_list_count; + struct { + uint32_t count; + uint32_t reserved; + struct virtio_dev_part_hdr hdrs[]; + } hdr_list; + }; +}; + +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_SELECTED 0 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_ALL 1 + +struct virtio_admin_cmd_dev_parts_get_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + uint8_t type; + uint8_t reserved[7]; + struct virtio_dev_part_hdr hdr_list[]; +}; + +struct virtio_admin_cmd_dev_parts_set_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + struct virtio_dev_part parts[]; +}; + +#define VIRTIO_ADMIN_CMD_DEV_MODE_F_STOPPED 0 + +struct virtio_admin_cmd_dev_mode_set_data { + uint8_t flags; +}; + #endif diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 2af9931ae9..dccd5d965f 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -473,6 +473,12 @@ enum { */ #define KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2 (1ULL << 0) +/* + * Shutdown caused by a PSCI v1.3 SYSTEM_OFF2 call. + * Valid only when the system event has a type of KVM_SYSTEM_EVENT_SHUTDOWN. + */ +#define KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 (1ULL << 0) + /* run->fail_entry.hardware_entry_failure_reason codes. */ #define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) diff --git a/linux-headers/asm-arm64/unistd_64.h b/linux-headers/asm-arm64/unistd_64.h index 99a1d70459..d4e90fff76 100644 --- a/linux-headers/asm-arm64/unistd_64.h +++ b/linux-headers/asm-arm64/unistd_64.h @@ -319,6 +319,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-generic/mman-common.h b/linux-headers/asm-generic/mman-common.h index 6ce1f1ceb4..1ea2c4c33b 100644 --- a/linux-headers/asm-generic/mman-common.h +++ b/linux-headers/asm-generic/mman-common.h @@ -79,6 +79,9 @@ #define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ +#define MADV_GUARD_INSTALL 102 /* fatal signal on access to range */ +#define MADV_GUARD_REMOVE 103 /* unguard range */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-generic/mman.h b/linux-headers/asm-generic/mman.h index 57e8195d0b..5e3d61ddbd 100644 --- a/linux-headers/asm-generic/mman.h +++ b/linux-headers/asm-generic/mman.h @@ -19,4 +19,8 @@ #define MCL_FUTURE 2 /* lock all future mappings */ #define MCL_ONFAULT 4 /* lock all pages that are faulted in */ +#define SHADOW_STACK_SET_TOKEN (1ULL << 0) /* Set up a restore token in the shadow stack */ +#define SHADOW_STACK_SET_MARKER (1ULL << 1) /* Set up a top of stack marker in the shadow stack */ + + #endif /* __ASM_GENERIC_MMAN_H */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 5bf6148cac..88dc393c2b 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -841,8 +841,17 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules) #define __NR_mseal 462 __SYSCALL(__NR_mseal, sys_mseal) +#define __NR_setxattrat 463 +__SYSCALL(__NR_setxattrat, sys_setxattrat) +#define __NR_getxattrat 464 +__SYSCALL(__NR_getxattrat, sys_getxattrat) +#define __NR_listxattrat 465 +__SYSCALL(__NR_listxattrat, sys_listxattrat) +#define __NR_removexattrat 466 +__SYSCALL(__NR_removexattrat, sys_removexattrat) + #undef __NR_syscalls -#define __NR_syscalls 463 +#define __NR_syscalls 467 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h index 70d89070bf..5f354f5c68 100644 --- a/linux-headers/asm-loongarch/kvm.h +++ b/linux-headers/asm-loongarch/kvm.h @@ -8,6 +8,8 @@ #include +#define __KVM_HAVE_IRQ_LINE + /* * KVM LoongArch specific structures and definitions. * @@ -132,4 +134,22 @@ struct kvm_iocsr_entry { #define KVM_IRQCHIP_NUM_PINS 64 #define KVM_MAX_CORES 256 +#define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000001 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000002 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS 0x40000003 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE 0x2 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL 0x40000004 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED 0x3 + +#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005 +#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000006 +#define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ diff --git a/linux-headers/asm-loongarch/unistd_64.h b/linux-headers/asm-loongarch/unistd_64.h index 887ea50cca..23fb96a8a7 100644 --- a/linux-headers/asm-loongarch/unistd_64.h +++ b/linux-headers/asm-loongarch/unistd_64.h @@ -315,6 +315,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-mips/mman.h b/linux-headers/asm-mips/mman.h index 9c48d9a21a..b700dae28c 100644 --- a/linux-headers/asm-mips/mman.h +++ b/linux-headers/asm-mips/mman.h @@ -105,6 +105,9 @@ #define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ +#define MADV_GUARD_INSTALL 102 /* fatal signal on access to range */ +#define MADV_GUARD_REMOVE 103 /* unguard range */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index fc93b3be30..9a75719644 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -391,5 +391,9 @@ #define __NR_lsm_set_self_attr (__NR_Linux + 460) #define __NR_lsm_list_modules (__NR_Linux + 461) #define __NR_mseal (__NR_Linux + 462) +#define __NR_setxattrat (__NR_Linux + 463) +#define __NR_getxattrat (__NR_Linux + 464) +#define __NR_listxattrat (__NR_Linux + 465) +#define __NR_removexattrat (__NR_Linux + 466) #endif /* _ASM_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index e72a3eb2c9..7086783b0c 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -367,5 +367,9 @@ #define __NR_lsm_set_self_attr (__NR_Linux + 460) #define __NR_lsm_list_modules (__NR_Linux + 461) #define __NR_mseal (__NR_Linux + 462) +#define __NR_setxattrat (__NR_Linux + 463) +#define __NR_getxattrat (__NR_Linux + 464) +#define __NR_listxattrat (__NR_Linux + 465) +#define __NR_removexattrat (__NR_Linux + 466) #endif /* _ASM_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index b86eb0786c..b3825823e4 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -437,5 +437,9 @@ #define __NR_lsm_set_self_attr (__NR_Linux + 460) #define __NR_lsm_list_modules (__NR_Linux + 461) #define __NR_mseal (__NR_Linux + 462) +#define __NR_setxattrat (__NR_Linux + 463) +#define __NR_getxattrat (__NR_Linux + 464) +#define __NR_listxattrat (__NR_Linux + 465) +#define __NR_removexattrat (__NR_Linux + 466) #endif /* _ASM_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 28627b6546..38ee4dc35d 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -444,6 +444,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index 1fc42a8300..5e5f156834 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -416,6 +416,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index e97db32964..3482c9a73d 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -175,6 +175,10 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_ZCF, KVM_RISCV_ISA_EXT_ZCMOP, KVM_RISCV_ISA_EXT_ZAWRS, + KVM_RISCV_ISA_EXT_SMNPM, + KVM_RISCV_ISA_EXT_SSNPM, + KVM_RISCV_ISA_EXT_SVADE, + KVM_RISCV_ISA_EXT_SVADU, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/linux-headers/asm-riscv/unistd_32.h b/linux-headers/asm-riscv/unistd_32.h index 9625743dfd..74f6127aed 100644 --- a/linux-headers/asm-riscv/unistd_32.h +++ b/linux-headers/asm-riscv/unistd_32.h @@ -310,6 +310,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-riscv/unistd_64.h b/linux-headers/asm-riscv/unistd_64.h index 95bca8ae81..bb6a15a2ec 100644 --- a/linux-headers/asm-riscv/unistd_64.h +++ b/linux-headers/asm-riscv/unistd_64.h @@ -320,6 +320,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index 684c4e1205..ab5a6bce59 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -469,7 +469,8 @@ struct kvm_s390_vm_cpu_subfunc { __u8 kdsa[16]; /* with MSA9 */ __u8 sortl[32]; /* with STFLE.150 */ __u8 dfltcc[32]; /* with STFLE.151 */ - __u8 reserved[1728]; + __u8 pfcr[16]; /* with STFLE.201 */ + __u8 reserved[1712]; }; #define KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST 6 diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 7706c21b87..620201cb36 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -435,5 +435,9 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 62082d592d..e7e4a10aaf 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -383,5 +383,9 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 4711ef2c3d..96589490c4 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -438,6 +438,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_FIX_HYPERCALL_INSN (1 << 5) #define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6) #define KVM_X86_QUIRK_SLOT_ZAP_ALL (1 << 7) +#define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 diff --git a/linux-headers/asm-x86/mman.h b/linux-headers/asm-x86/mman.h index 46cdc941f9..ac1e627721 100644 --- a/linux-headers/asm-x86/mman.h +++ b/linux-headers/asm-x86/mman.h @@ -5,9 +5,6 @@ #define MAP_32BIT 0x40 /* only give out 32bit addresses */ #define MAP_ABOVE4G 0x80 /* only map above 4GB */ -/* Flags for map_shadow_stack(2) */ -#define SHADOW_STACK_SET_TOKEN (1ULL << 0) /* Set up a restore token in the shadow stack */ - #include #endif /* _ASM_X86_MMAN_H */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index fb7b8b169b..a2eb492a75 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -453,6 +453,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 24c979be54..2f5fc400f5 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -376,6 +376,10 @@ #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 #define __NR_mseal 462 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index c23dd21a2d..fecd832e7f 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -329,6 +329,10 @@ #define __NR_lsm_set_self_attr (__X32_SYSCALL_BIT + 460) #define __NR_lsm_list_modules (__X32_SYSCALL_BIT + 461) #define __NR_mseal (__X32_SYSCALL_BIT + 462) +#define __NR_setxattrat (__X32_SYSCALL_BIT + 463) +#define __NR_getxattrat (__X32_SYSCALL_BIT + 464) +#define __NR_listxattrat (__X32_SYSCALL_BIT + 465) +#define __NR_removexattrat (__X32_SYSCALL_BIT + 466) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h index 782baf477f..37aae16502 100644 --- a/linux-headers/linux/iommufd.h +++ b/linux-headers/linux/iommufd.h @@ -51,6 +51,10 @@ enum { IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP = 0x8c, IOMMUFD_CMD_HWPT_INVALIDATE = 0x8d, IOMMUFD_CMD_FAULT_QUEUE_ALLOC = 0x8e, + IOMMUFD_CMD_IOAS_MAP_FILE = 0x8f, + IOMMUFD_CMD_VIOMMU_ALLOC = 0x90, + IOMMUFD_CMD_VDEVICE_ALLOC = 0x91, + IOMMUFD_CMD_IOAS_CHANGE_PROCESS = 0x92, }; /** @@ -213,6 +217,30 @@ struct iommu_ioas_map { }; #define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP) +/** + * struct iommu_ioas_map_file - ioctl(IOMMU_IOAS_MAP_FILE) + * @size: sizeof(struct iommu_ioas_map_file) + * @flags: same as for iommu_ioas_map + * @ioas_id: same as for iommu_ioas_map + * @fd: the memfd to map + * @start: byte offset from start of file to map from + * @length: same as for iommu_ioas_map + * @iova: same as for iommu_ioas_map + * + * Set an IOVA mapping from a memfd file. All other arguments and semantics + * match those of IOMMU_IOAS_MAP. + */ +struct iommu_ioas_map_file { + __u32 size; + __u32 flags; + __u32 ioas_id; + __s32 fd; + __aligned_u64 start; + __aligned_u64 length; + __aligned_u64 iova; +}; +#define IOMMU_IOAS_MAP_FILE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP_FILE) + /** * struct iommu_ioas_copy - ioctl(IOMMU_IOAS_COPY) * @size: sizeof(struct iommu_ioas_copy) @@ -359,11 +387,19 @@ struct iommu_vfio_ioas { * enforced on device attachment * @IOMMU_HWPT_FAULT_ID_VALID: The fault_id field of hwpt allocation data is * valid. + * @IOMMU_HWPT_ALLOC_PASID: Requests a domain that can be used with PASID. The + * domain can be attached to any PASID on the device. + * Any domain attached to the non-PASID part of the + * device must also be flaged, otherwise attaching a + * PASID will blocked. + * If IOMMU does not support PASID it will return + * error (-EOPNOTSUPP). */ enum iommufd_hwpt_alloc_flags { IOMMU_HWPT_ALLOC_NEST_PARENT = 1 << 0, IOMMU_HWPT_ALLOC_DIRTY_TRACKING = 1 << 1, IOMMU_HWPT_FAULT_ID_VALID = 1 << 2, + IOMMU_HWPT_ALLOC_PASID = 1 << 3, }; /** @@ -394,14 +430,36 @@ struct iommu_hwpt_vtd_s1 { __u32 __reserved; }; +/** + * struct iommu_hwpt_arm_smmuv3 - ARM SMMUv3 nested STE + * (IOMMU_HWPT_DATA_ARM_SMMUV3) + * + * @ste: The first two double words of the user space Stream Table Entry for + * the translation. Must be little-endian. + * Allowed fields: (Refer to "5.2 Stream Table Entry" in SMMUv3 HW Spec) + * - word-0: V, Cfg, S1Fmt, S1ContextPtr, S1CDMax + * - word-1: EATS, S1DSS, S1CIR, S1COR, S1CSH, S1STALLD + * + * -EIO will be returned if @ste is not legal or contains any non-allowed field. + * Cfg can be used to select a S1, Bypass or Abort configuration. A Bypass + * nested domain will translate the same as the nesting parent. The S1 will + * install a Context Descriptor Table pointing at userspace memory translated + * by the nesting parent. + */ +struct iommu_hwpt_arm_smmuv3 { + __aligned_le64 ste[2]; +}; + /** * enum iommu_hwpt_data_type - IOMMU HWPT Data Type * @IOMMU_HWPT_DATA_NONE: no data * @IOMMU_HWPT_DATA_VTD_S1: Intel VT-d stage-1 page table + * @IOMMU_HWPT_DATA_ARM_SMMUV3: ARM SMMUv3 Context Descriptor Table */ enum iommu_hwpt_data_type { IOMMU_HWPT_DATA_NONE = 0, IOMMU_HWPT_DATA_VTD_S1 = 1, + IOMMU_HWPT_DATA_ARM_SMMUV3 = 2, }; /** @@ -409,7 +467,7 @@ enum iommu_hwpt_data_type { * @size: sizeof(struct iommu_hwpt_alloc) * @flags: Combination of enum iommufd_hwpt_alloc_flags * @dev_id: The device to allocate this HWPT for - * @pt_id: The IOAS or HWPT to connect this HWPT to + * @pt_id: The IOAS or HWPT or vIOMMU to connect this HWPT to * @out_hwpt_id: The ID of the new HWPT * @__reserved: Must be 0 * @data_type: One of enum iommu_hwpt_data_type @@ -428,11 +486,13 @@ enum iommu_hwpt_data_type { * IOMMU_HWPT_DATA_NONE. The HWPT can be allocated as a parent HWPT for a * nesting configuration by passing IOMMU_HWPT_ALLOC_NEST_PARENT via @flags. * - * A user-managed nested HWPT will be created from a given parent HWPT via - * @pt_id, in which the parent HWPT must be allocated previously via the - * same ioctl from a given IOAS (@pt_id). In this case, the @data_type - * must be set to a pre-defined type corresponding to an I/O page table - * type supported by the underlying IOMMU hardware. + * A user-managed nested HWPT will be created from a given vIOMMU (wrapping a + * parent HWPT) or a parent HWPT via @pt_id, in which the parent HWPT must be + * allocated previously via the same ioctl from a given IOAS (@pt_id). In this + * case, the @data_type must be set to a pre-defined type corresponding to an + * I/O page table type supported by the underlying IOMMU hardware. The device + * via @dev_id and the vIOMMU via @pt_id must be associated to the same IOMMU + * instance. * * If the @data_type is set to IOMMU_HWPT_DATA_NONE, @data_len and * @data_uptr should be zero. Otherwise, both @data_len and @data_uptr @@ -484,15 +544,50 @@ struct iommu_hw_info_vtd { __aligned_u64 ecap_reg; }; +/** + * struct iommu_hw_info_arm_smmuv3 - ARM SMMUv3 hardware information + * (IOMMU_HW_INFO_TYPE_ARM_SMMUV3) + * + * @flags: Must be set to 0 + * @__reserved: Must be 0 + * @idr: Implemented features for ARM SMMU Non-secure programming interface + * @iidr: Information about the implementation and implementer of ARM SMMU, + * and architecture version supported + * @aidr: ARM SMMU architecture version + * + * For the details of @idr, @iidr and @aidr, please refer to the chapters + * from 6.3.1 to 6.3.6 in the SMMUv3 Spec. + * + * User space should read the underlying ARM SMMUv3 hardware information for + * the list of supported features. + * + * Note that these values reflect the raw HW capability, without any insight if + * any required kernel driver support is present. Bits may be set indicating the + * HW has functionality that is lacking kernel software support, such as BTM. If + * a VMM is using this information to construct emulated copies of these + * registers it should only forward bits that it knows it can support. + * + * In future, presence of required kernel support will be indicated in flags. + */ +struct iommu_hw_info_arm_smmuv3 { + __u32 flags; + __u32 __reserved; + __u32 idr[6]; + __u32 iidr; + __u32 aidr; +}; + /** * enum iommu_hw_info_type - IOMMU Hardware Info Types * @IOMMU_HW_INFO_TYPE_NONE: Used by the drivers that do not report hardware * info * @IOMMU_HW_INFO_TYPE_INTEL_VTD: Intel VT-d iommu info type + * @IOMMU_HW_INFO_TYPE_ARM_SMMUV3: ARM SMMUv3 iommu info type */ enum iommu_hw_info_type { IOMMU_HW_INFO_TYPE_NONE = 0, IOMMU_HW_INFO_TYPE_INTEL_VTD = 1, + IOMMU_HW_INFO_TYPE_ARM_SMMUV3 = 2, }; /** @@ -627,9 +722,11 @@ struct iommu_hwpt_get_dirty_bitmap { * enum iommu_hwpt_invalidate_data_type - IOMMU HWPT Cache Invalidation * Data Type * @IOMMU_HWPT_INVALIDATE_DATA_VTD_S1: Invalidation data for VTD_S1 + * @IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3: Invalidation data for ARM SMMUv3 */ enum iommu_hwpt_invalidate_data_type { IOMMU_HWPT_INVALIDATE_DATA_VTD_S1 = 0, + IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3 = 1, }; /** @@ -668,10 +765,32 @@ struct iommu_hwpt_vtd_s1_invalidate { __u32 __reserved; }; +/** + * struct iommu_viommu_arm_smmuv3_invalidate - ARM SMMUv3 cahce invalidation + * (IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3) + * @cmd: 128-bit cache invalidation command that runs in SMMU CMDQ. + * Must be little-endian. + * + * Supported command list only when passing in a vIOMMU via @hwpt_id: + * CMDQ_OP_TLBI_NSNH_ALL + * CMDQ_OP_TLBI_NH_VA + * CMDQ_OP_TLBI_NH_VAA + * CMDQ_OP_TLBI_NH_ALL + * CMDQ_OP_TLBI_NH_ASID + * CMDQ_OP_ATC_INV + * CMDQ_OP_CFGI_CD + * CMDQ_OP_CFGI_CD_ALL + * + * -EIO will be returned if the command is not supported. + */ +struct iommu_viommu_arm_smmuv3_invalidate { + __aligned_le64 cmd[2]; +}; + /** * struct iommu_hwpt_invalidate - ioctl(IOMMU_HWPT_INVALIDATE) * @size: sizeof(struct iommu_hwpt_invalidate) - * @hwpt_id: ID of a nested HWPT for cache invalidation + * @hwpt_id: ID of a nested HWPT or a vIOMMU, for cache invalidation * @data_uptr: User pointer to an array of driver-specific cache invalidation * data. * @data_type: One of enum iommu_hwpt_invalidate_data_type, defining the data @@ -682,8 +801,11 @@ struct iommu_hwpt_vtd_s1_invalidate { * Output the number of requests successfully handled by kernel. * @__reserved: Must be 0. * - * Invalidate the iommu cache for user-managed page table. Modifications on a - * user-managed page table should be followed by this operation to sync cache. + * Invalidate iommu cache for user-managed page table or vIOMMU. Modifications + * on a user-managed page table should be followed by this operation, if a HWPT + * is passed in via @hwpt_id. Other caches, such as device cache or descriptor + * cache can be flushed if a vIOMMU is passed in via the @hwpt_id field. + * * Each ioctl can support one or more cache invalidation requests in the array * that has a total size of @entry_len * @entry_num. * @@ -797,4 +919,88 @@ struct iommu_fault_alloc { __u32 out_fault_fd; }; #define IOMMU_FAULT_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_FAULT_QUEUE_ALLOC) + +/** + * enum iommu_viommu_type - Virtual IOMMU Type + * @IOMMU_VIOMMU_TYPE_DEFAULT: Reserved for future use + * @IOMMU_VIOMMU_TYPE_ARM_SMMUV3: ARM SMMUv3 driver specific type + */ +enum iommu_viommu_type { + IOMMU_VIOMMU_TYPE_DEFAULT = 0, + IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1, +}; + +/** + * struct iommu_viommu_alloc - ioctl(IOMMU_VIOMMU_ALLOC) + * @size: sizeof(struct iommu_viommu_alloc) + * @flags: Must be 0 + * @type: Type of the virtual IOMMU. Must be defined in enum iommu_viommu_type + * @dev_id: The device's physical IOMMU will be used to back the virtual IOMMU + * @hwpt_id: ID of a nesting parent HWPT to associate to + * @out_viommu_id: Output virtual IOMMU ID for the allocated object + * + * Allocate a virtual IOMMU object, representing the underlying physical IOMMU's + * virtualization support that is a security-isolated slice of the real IOMMU HW + * that is unique to a specific VM. Operations global to the IOMMU are connected + * to the vIOMMU, such as: + * - Security namespace for guest owned ID, e.g. guest-controlled cache tags + * - Non-device-affiliated event reporting, e.g. invalidation queue errors + * - Access to a sharable nesting parent pagetable across physical IOMMUs + * - Virtualization of various platforms IDs, e.g. RIDs and others + * - Delivery of paravirtualized invalidation + * - Direct assigned invalidation queues + * - Direct assigned interrupts + */ +struct iommu_viommu_alloc { + __u32 size; + __u32 flags; + __u32 type; + __u32 dev_id; + __u32 hwpt_id; + __u32 out_viommu_id; +}; +#define IOMMU_VIOMMU_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VIOMMU_ALLOC) + +/** + * struct iommu_vdevice_alloc - ioctl(IOMMU_VDEVICE_ALLOC) + * @size: sizeof(struct iommu_vdevice_alloc) + * @viommu_id: vIOMMU ID to associate with the virtual device + * @dev_id: The physical device to allocate a virtual instance on the vIOMMU + * @out_vdevice_id: Object handle for the vDevice. Pass to IOMMU_DESTORY + * @virt_id: Virtual device ID per vIOMMU, e.g. vSID of ARM SMMUv3, vDeviceID + * of AMD IOMMU, and vRID of a nested Intel VT-d to a Context Table + * + * Allocate a virtual device instance (for a physical device) against a vIOMMU. + * This instance holds the device's information (related to its vIOMMU) in a VM. + */ +struct iommu_vdevice_alloc { + __u32 size; + __u32 viommu_id; + __u32 dev_id; + __u32 out_vdevice_id; + __aligned_u64 virt_id; +}; +#define IOMMU_VDEVICE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_ALLOC) + +/** + * struct iommu_ioas_change_process - ioctl(VFIO_IOAS_CHANGE_PROCESS) + * @size: sizeof(struct iommu_ioas_change_process) + * @__reserved: Must be 0 + * + * This transfers pinned memory counts for every memory map in every IOAS + * in the context to the current process. This only supports maps created + * with IOMMU_IOAS_MAP_FILE, and returns EINVAL if other maps are present. + * If the ioctl returns a failure status, then nothing is changed. + * + * This API is useful for transferring operation of a device from one process + * to another, such as during userland live update. + */ +struct iommu_ioas_change_process { + __u32 size; + __u32 __reserved; +}; + +#define IOMMU_IOAS_CHANGE_PROCESS \ + _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_CHANGE_PROCESS) + #endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 49dd1b30ce..3bcd4eabe3 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1150,7 +1150,15 @@ enum kvm_device_type { #define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_RISCV_AIA, #define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA + KVM_DEV_TYPE_LOONGARCH_IPI, +#define KVM_DEV_TYPE_LOONGARCH_IPI KVM_DEV_TYPE_LOONGARCH_IPI + KVM_DEV_TYPE_LOONGARCH_EIOINTC, +#define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC + KVM_DEV_TYPE_LOONGARCH_PCHPIC, +#define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_MAX, + }; struct kvm_vfio_spapr_tce { diff --git a/linux-headers/linux/psci.h b/linux-headers/linux/psci.h index 74f3cb5007..a982afd498 100644 --- a/linux-headers/linux/psci.h +++ b/linux-headers/linux/psci.h @@ -59,6 +59,7 @@ #define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) #define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19) #define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20) +#define PSCI_1_3_FN_SYSTEM_OFF2 PSCI_0_2_FN(21) #define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12) #define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13) @@ -68,6 +69,7 @@ #define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) #define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20) +#define PSCI_1_3_FN64_SYSTEM_OFF2 PSCI_0_2_FN64(21) /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff @@ -100,6 +102,9 @@ #define PSCI_1_1_RESET_TYPE_SYSTEM_WARM_RESET 0 #define PSCI_1_1_RESET_TYPE_VENDOR_START 0x80000000U +/* PSCI v1.3 hibernate type for SYSTEM_OFF2 */ +#define PSCI_1_3_OFF_TYPE_HIBERNATE_OFF BIT(0) + /* PSCI version decoding (independent of PSCI version) */ #define PSCI_VERSION_MAJOR_SHIFT 16 #define PSCI_VERSION_MINOR_MASK \ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index b4be37b225..1b5e254d6a 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -35,7 +35,7 @@ #define VFIO_EEH 5 /* Two-stage IOMMU */ -#define VFIO_TYPE1_NESTING_IOMMU 6 /* Implies v2 */ +#define __VFIO_RESERVED_TYPE1_NESTING_IOMMU 6 /* Implies v2 */ #define VFIO_SPAPR_TCE_v2_IOMMU 7 From c9ea365dce32bc114dacd7cfca7c478a82459b47 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:43 +0100 Subject: [PATCH 0082/2892] s390x/cpumodel: add Concurrent-functions facility support The Concurrent-functions facility introduces the new instruction Perform Functions with Concurrent Results (PFCR) with few subfunctions. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-8-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 2 ++ target/s390x/cpu_features.h | 1 + target/s390x/cpu_features_def.h.inc | 8 ++++++++ target/s390x/cpu_models.c | 5 +++++ target/s390x/gen-features.c | 13 +++++++++++++ target/s390x/kvm/kvm.c | 6 ++++++ 6 files changed, 35 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 385a2ff860..5f8b02f12c 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -93,6 +93,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type, case S390_FEAT_TYPE_KDSA: case S390_FEAT_TYPE_SORTL: case S390_FEAT_TYPE_DFLTCC: + case S390_FEAT_TYPE_PFCR: set_be_bit(0, data); /* query is always available */ break; default: @@ -263,6 +264,7 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("esort", ENH_SORT, "Enhanced-sort facility"), FEAT_GROUP_INIT("deflate", DEFLATE_CONVERSION, "Deflate-conversion facility"), + FEAT_GROUP_INIT("ccf", CONCURRENT_FUNCTIONS, "Concurrent-functions facility"), }; const S390FeatGroupDef *s390_feat_group_def(S390FeatGroup group) diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h index 661a8cd6db..5635839d03 100644 --- a/target/s390x/cpu_features.h +++ b/target/s390x/cpu_features.h @@ -44,6 +44,7 @@ typedef enum { S390_FEAT_TYPE_SORTL, S390_FEAT_TYPE_DFLTCC, S390_FEAT_TYPE_UV_FEAT_GUEST, + S390_FEAT_TYPE_PFCR, } S390FeatType; /* Definition of a CPU feature */ diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index f96cb5a7d8..09872ab3d8 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -116,6 +116,7 @@ DEF_FEAT(BEAR_ENH, "beareh", STFL, 193, "BEAR-enhancement facility") DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") DEF_FEAT(PAI, "pai", STFL, 196, "Processor-Activity-Instrumentation facility") DEF_FEAT(PAIE, "paie", STFL, 197, "Processor-Activity-Instrumentation extension-1") +DEF_FEAT(CCF_BASE, "ccf-base", STFL, 201, "Concurrent-Functions facility") /* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ DEF_FEAT(SIE_GSLS, "gsls", SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility") @@ -413,3 +414,10 @@ DEF_FEAT(DEFLATE_F0, "dfltcc-f0", DFLTCC, 192, "DFLTCC format 0 parameter-block" /* Features exposed via the UV-CALL instruction */ DEF_FEAT(UV_FEAT_AP, "appv", UV_FEAT_GUEST, 4, "AP instructions installed for secure guests") DEF_FEAT(UV_FEAT_AP_INTR, "appvi", UV_FEAT_GUEST, 5, "AP instructions interruption support for secure guests") + +/* Features exposed via the PFCR instruction (concurrent-functions facility). */ +DEF_FEAT(PFCR_QAF, "pfcr-qaf", PFCR, 0, "PFCR Query-Available-Functions") +DEF_FEAT(PFCR_CSDST, "pfcr-csdst", PFCR, 1, "PFCR Compare-and-Swap-and-Double-Store (32)") +DEF_FEAT(PFCR_CSDSTG, "pfcr-csdstg", PFCR, 2, "PFCR Compare-and-Swap-and-Double-Store (64)") +DEF_FEAT(PFCR_CSTST, "pfcr-cstst", PFCR, 3, "PFCR Compare-and-Swap-and-Triple-Store (32)") +DEF_FEAT(PFCR_CSTSTG, "pfcr-cststg", PFCR, 4, "PFCR Compare-and-Swap-and-Triple-Store (64)") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index a62a3c3771..117434b9a8 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -504,6 +504,11 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING }, { S390_FEAT_UV_FEAT_AP, S390_FEAT_AP }, { S390_FEAT_UV_FEAT_AP_INTR, S390_FEAT_UV_FEAT_AP }, + { S390_FEAT_PFCR_QAF, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSDST, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSDSTG, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSTST, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSTSTG, S390_FEAT_CCF_BASE }, }; int i; diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 302b653214..6d00ffcda7 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -308,6 +308,14 @@ S390_FEAT_DEFLATE_XPND, \ S390_FEAT_DEFLATE_F0 +#define S390_FEAT_GROUP_CONCURRENT_FUNCTIONS \ + S390_FEAT_CCF_BASE, \ + S390_FEAT_PFCR_QAF, \ + S390_FEAT_PFCR_CSDST, \ + S390_FEAT_PFCR_CSDSTG, \ + S390_FEAT_PFCR_CSTST, \ + S390_FEAT_PFCR_CSTSTG + /* cpu feature groups */ static uint16_t group_PLO[] = { S390_FEAT_GROUP_PLO, @@ -398,6 +406,10 @@ static uint16_t group_DEFLATE_CONVERSION[] = { S390_FEAT_GROUP_DEFLATE_CONVERSION, }; +static uint16_t group_CONCURRENT_FUNCTIONS[] = { + S390_FEAT_GROUP_CONCURRENT_FUNCTIONS, +}; + /* Base features (in order of release) * Only non-hypervisor managed features belong here. * Base feature sets are static meaning they do not change in future QEMU @@ -948,6 +960,7 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(ENH_SORT), FEAT_GROUP_INITIALIZER(DEFLATE_CONVERSION), + FEAT_GROUP_INITIALIZER(CONCURRENT_FUNCTIONS), }; #define QEMU_FEAT_INITIALIZER(_name) \ diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 8ffe0159d8..dd0322c43a 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -2195,6 +2195,9 @@ static int query_cpu_subfunc(S390FeatBitmap features) if (test_bit(S390_FEAT_DEFLATE_BASE, features)) { s390_add_from_feat_block(features, S390_FEAT_TYPE_DFLTCC, prop.dfltcc); } + if (test_bit(S390_FEAT_CCF_BASE, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_PFCR, prop.pfcr); + } return 0; } @@ -2248,6 +2251,9 @@ static int configure_cpu_subfunc(const S390FeatBitmap features) if (test_bit(S390_FEAT_DEFLATE_BASE, features)) { s390_fill_feat_block(features, S390_FEAT_TYPE_DFLTCC, prop.dfltcc); } + if (test_bit(S390_FEAT_CCF_BASE, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_PFCR, prop.pfcr); + } return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); } From 0b2c66a3fa5ccecf26ea011d97d65a986b42b4d8 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:44 +0100 Subject: [PATCH 0083/2892] s390x/cpumodel: add Vector Enhancements facility 3 The Vector Enhancements facility 3 introduces new instructions and extends support for doubleword/quadword elements. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-9-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features_def.h.inc | 1 + target/s390x/cpu_models.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 09872ab3d8..0b7be0e6e9 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -116,6 +116,7 @@ DEF_FEAT(BEAR_ENH, "beareh", STFL, 193, "BEAR-enhancement facility") DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") DEF_FEAT(PAI, "pai", STFL, 196, "Processor-Activity-Instrumentation facility") DEF_FEAT(PAIE, "paie", STFL, 197, "Processor-Activity-Instrumentation extension-1") +DEF_FEAT(VECTOR_ENH3, "vxeh3", STFL, 198, "Vector Enhancements facility 3") DEF_FEAT(CCF_BASE, "ccf-base", STFL, 201, "Concurrent-Functions facility") /* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 117434b9a8..5d548de79c 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -458,6 +458,8 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH, S390_FEAT_VECTOR_PACKED_DECIMAL }, { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH }, { S390_FEAT_VECTOR_ENH, S390_FEAT_VECTOR }, + { S390_FEAT_VECTOR_ENH2, S390_FEAT_VECTOR_ENH }, + { S390_FEAT_VECTOR_ENH3, S390_FEAT_VECTOR_ENH2 }, { S390_FEAT_INSTRUCTION_EXEC_PROT, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2 }, { S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2, S390_FEAT_ESOP }, { S390_FEAT_CMM_NT, S390_FEAT_CMM }, From e68e5ea6fe87c0177ea6421045c1d46f223a861e Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:45 +0100 Subject: [PATCH 0084/2892] s390x/cpumodel: add Miscellaneous-Instruction-Extensions Facility 4 This facility introduces few new instructions. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-10-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features_def.h.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 0b7be0e6e9..8be2e0e46d 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -90,6 +90,7 @@ DEF_FEAT(EDAT_2, "edat2", STFL, 78, "Enhanced-DAT facility 2") DEF_FEAT(DFP_PACKED_CONVERSION, "dfppc", STFL, 80, "Decimal-floating-point packed-conversion facility") DEF_FEAT(PPA15, "ppa15", STFL, 81, "PPA15 is installed") DEF_FEAT(BPB, "bpb", STFL, 82, "Branch prediction blocking") +DEF_FEAT(MISC_INSTRUCTION_EXT4, "minste4", STFL, 84, "Miscellaneous-Instruction-Extensions Facility 4") DEF_FEAT(MSA_EXT_12, "msa12-base", STFL, 86, "Message-security-assist-extension-12 facility (excluding subfunctions)") DEF_FEAT(VECTOR, "vx", STFL, 129, "Vector facility") DEF_FEAT(INSTRUCTION_EXEC_PROT, "iep", STFL, 130, "Instruction-execution-protection facility") From db4c208abde53a7ed16d566bf63a3520894bba8d Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:46 +0100 Subject: [PATCH 0085/2892] s390x/cpumodel: add Vector-Packed-Decimal-Enhancement facility 3 This facility introduces new capabilities for the signed-pack-decimal format. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-11-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features_def.h.inc | 1 + target/s390x/cpu_models.c | 1 + 2 files changed, 2 insertions(+) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 8be2e0e46d..df154d145f 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -118,6 +118,7 @@ DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") DEF_FEAT(PAI, "pai", STFL, 196, "Processor-Activity-Instrumentation facility") DEF_FEAT(PAIE, "paie", STFL, 197, "Processor-Activity-Instrumentation extension-1") DEF_FEAT(VECTOR_ENH3, "vxeh3", STFL, 198, "Vector Enhancements facility 3") +DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH3, "vxpdeh3", STFL, 199, "Vector-Packed-Decimal-Enhancement facility 3") DEF_FEAT(CCF_BASE, "ccf-base", STFL, 201, "Concurrent-Functions facility") /* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 5d548de79c..6a7ce77d9a 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -457,6 +457,7 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_VECTOR_PACKED_DECIMAL, S390_FEAT_VECTOR }, { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH, S390_FEAT_VECTOR_PACKED_DECIMAL }, { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH }, + { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH3, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2 }, { S390_FEAT_VECTOR_ENH, S390_FEAT_VECTOR }, { S390_FEAT_VECTOR_ENH2, S390_FEAT_VECTOR_ENH }, { S390_FEAT_VECTOR_ENH3, S390_FEAT_VECTOR_ENH2 }, From 12417b713c1fffc26c680e99e3429f055eb9af2e Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:47 +0100 Subject: [PATCH 0086/2892] s390x/cpumodel: add Ineffective-nonconstrained-transaction facility This facility indicates reduced support for noncontrained transactional-execution. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-12-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features_def.h.inc | 1 + target/s390x/cpu_models.c | 1 + 2 files changed, 2 insertions(+) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index df154d145f..2c1d1cd98a 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -112,6 +112,7 @@ DEF_FEAT(MSA_EXT_9, "msa9-base", STFL, 155, "Message-security-assist-extension-9 DEF_FEAT(ETOKEN, "etoken", STFL, 156, "Etoken facility") DEF_FEAT(UNPACK, "unpack", STFL, 161, "Unpack facility") DEF_FEAT(NNPA, "nnpa", STFL, 165, "NNPA facility") +DEF_FEAT(INEFF_NC_TX, "ineff_nc_tx", STFL, 170, "Ineffective-nonconstrained-transaction facility") DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH2, "vxpdeh2", STFL, 192, "Vector-Packed-Decimal-Enhancement facility 2") DEF_FEAT(BEAR_ENH, "beareh", STFL, 193, "BEAR-enhancement facility") DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 6a7ce77d9a..7fa1712d78 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -512,6 +512,7 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_PFCR_CSDSTG, S390_FEAT_CCF_BASE }, { S390_FEAT_PFCR_CSTST, S390_FEAT_CCF_BASE }, { S390_FEAT_PFCR_CSTSTG, S390_FEAT_CCF_BASE }, + { S390_FEAT_INEFF_NC_TX, S390_FEAT_TRANSACTIONAL_EXE }, }; int i; From a5fa8bee72847406bfc32e15c8e41c0a2a0812e1 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:48 +0100 Subject: [PATCH 0087/2892] s390x/cpumodel: Add Sequential-Instruction-Fetching facility The sequential instruction fetching facility provides few guarantees, for example, to avoid stop machine calls on enabling/disabling kprobes. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-13-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features_def.h.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 2c1d1cd98a..09a80844a7 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -91,6 +91,7 @@ DEF_FEAT(DFP_PACKED_CONVERSION, "dfppc", STFL, 80, "Decimal-floating-point packe DEF_FEAT(PPA15, "ppa15", STFL, 81, "PPA15 is installed") DEF_FEAT(BPB, "bpb", STFL, 82, "Branch prediction blocking") DEF_FEAT(MISC_INSTRUCTION_EXT4, "minste4", STFL, 84, "Miscellaneous-Instruction-Extensions Facility 4") +DEF_FEAT(SIF, "sif", STFL, 85, "Sequential-instruction-fetching facility") DEF_FEAT(MSA_EXT_12, "msa12-base", STFL, 86, "Message-security-assist-extension-12 facility (excluding subfunctions)") DEF_FEAT(VECTOR, "vx", STFL, 129, "Vector facility") DEF_FEAT(INSTRUCTION_EXEC_PROT, "iep", STFL, 130, "Instruction-execution-protection facility") From 5a0a136df71b858d01f346af4a30ae1da23e8b3c Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:49 +0100 Subject: [PATCH 0088/2892] s390x/cpumodel: correct PLO feature wording The PLO functions 0, 4, 8, 12, 16, and 20 use 32-bit registers values. The plo-*gr variants use 64-bit instead and, thus, correct the wording. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-14-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features_def.h.inc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 09a80844a7..fe7e1bd19c 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -158,27 +158,27 @@ DEF_FEAT(AP, "ap", MISC, 0, "AP instructions installed") /* Features exposed via the PLO instruction. */ DEF_FEAT(PLO_CL, "plo-cl", PLO, 0, "PLO Compare and load (32 bit in general registers)") DEF_FEAT(PLO_CLG, "plo-clg", PLO, 1, "PLO Compare and load (64 bit in parameter list)") -DEF_FEAT(PLO_CLGR, "plo-clgr", PLO, 2, "PLO Compare and load (32 bit in general registers)") +DEF_FEAT(PLO_CLGR, "plo-clgr", PLO, 2, "PLO Compare and load (64 bit in general registers)") DEF_FEAT(PLO_CLX, "plo-clx", PLO, 3, "PLO Compare and load (128 bit in parameter list)") DEF_FEAT(PLO_CS, "plo-cs", PLO, 4, "PLO Compare and swap (32 bit in general registers)") DEF_FEAT(PLO_CSG, "plo-csg", PLO, 5, "PLO Compare and swap (64 bit in parameter list)") -DEF_FEAT(PLO_CSGR, "plo-csgr", PLO, 6, "PLO Compare and swap (32 bit in general registers)") +DEF_FEAT(PLO_CSGR, "plo-csgr", PLO, 6, "PLO Compare and swap (64 bit in general registers)") DEF_FEAT(PLO_CSX, "plo-csx", PLO, 7, "PLO Compare and swap (128 bit in parameter list)") DEF_FEAT(PLO_DCS, "plo-dcs", PLO, 8, "PLO Double compare and swap (32 bit in general registers)") DEF_FEAT(PLO_DCSG, "plo-dcsg", PLO, 9, "PLO Double compare and swap (64 bit in parameter list)") -DEF_FEAT(PLO_DCSGR, "plo-dcsgr", PLO, 10, "PLO Double compare and swap (32 bit in general registers)") +DEF_FEAT(PLO_DCSGR, "plo-dcsgr", PLO, 10, "PLO Double compare and swap (64 bit in general registers)") DEF_FEAT(PLO_DCSX, "plo-dcsx", PLO, 11, "PLO Double compare and swap (128 bit in parameter list)") DEF_FEAT(PLO_CSST, "plo-csst", PLO, 12, "PLO Compare and swap and store (32 bit in general registers)") DEF_FEAT(PLO_CSSTG, "plo-csstg", PLO, 13, "PLO Compare and swap and store (64 bit in parameter list)") -DEF_FEAT(PLO_CSSTGR, "plo-csstgr", PLO, 14, "PLO Compare and swap and store (32 bit in general registers)") +DEF_FEAT(PLO_CSSTGR, "plo-csstgr", PLO, 14, "PLO Compare and swap and store (64 bit in general registers)") DEF_FEAT(PLO_CSSTX, "plo-csstx", PLO, 15, "PLO Compare and swap and store (128 bit in parameter list)") DEF_FEAT(PLO_CSDST, "plo-csdst", PLO, 16, "PLO Compare and swap and double store (32 bit in general registers)") DEF_FEAT(PLO_CSDSTG, "plo-csdstg", PLO, 17, "PLO Compare and swap and double store (64 bit in parameter list)") -DEF_FEAT(PLO_CSDSTGR, "plo-csdstgr", PLO, 18, "PLO Compare and swap and double store (32 bit in general registers)") +DEF_FEAT(PLO_CSDSTGR, "plo-csdstgr", PLO, 18, "PLO Compare and swap and double store (64 bit in general registers)") DEF_FEAT(PLO_CSDSTX, "plo-csdstx", PLO, 19, "PLO Compare and swap and double store (128 bit in parameter list)") DEF_FEAT(PLO_CSTST, "plo-cstst", PLO, 20, "PLO Compare and swap and triple store (32 bit in general registers)") DEF_FEAT(PLO_CSTSTG, "plo-cststg", PLO, 21, "PLO Compare and swap and triple store (64 bit in parameter list)") -DEF_FEAT(PLO_CSTSTGR, "plo-cststgr", PLO, 22, "PLO Compare and swap and triple store (32 bit in general registers)") +DEF_FEAT(PLO_CSTSTGR, "plo-cststgr", PLO, 22, "PLO Compare and swap and triple store (64 bit in general registers)") DEF_FEAT(PLO_CSTSTX, "plo-cststx", PLO, 23, "PLO Compare and swap and triple store (128 bit in parameter list)") /* Features exposed via the PTFF instruction. */ From 393c835e341e8921d1d6ae45da308e85176c4f00 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:50 +0100 Subject: [PATCH 0089/2892] s390x/cpumodel: Add PLO-extension facility The PLO-extension facility introduces numerous locking related subfunctions. Signed-off-by: Hendrik Brueckner Reviewed-by: Janosch Frank Message-ID: <20241206122751.189721-15-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 1 + target/s390x/cpu_features_def.h.inc | 39 +++++++++++++++++++++++++ target/s390x/cpu_models.c | 38 ++++++++++++++++++++++++ target/s390x/gen-features.c | 45 +++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 5f8b02f12c..4b5be6798e 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -240,6 +240,7 @@ void s390_get_deprecated_features(S390FeatBitmap features) /* indexed by feature group number for easy lookup */ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"), + FEAT_GROUP_INIT("plo_ext", PLO_EXT, "PLO-extension facility"), FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"), FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"), FEAT_GROUP_INIT("gen17ptff", GEN17_PTFF, "PTFF enhancements introduced with gen17"), diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index fe7e1bd19c..e23e603a79 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -93,6 +93,7 @@ DEF_FEAT(BPB, "bpb", STFL, 82, "Branch prediction blocking") DEF_FEAT(MISC_INSTRUCTION_EXT4, "minste4", STFL, 84, "Miscellaneous-Instruction-Extensions Facility 4") DEF_FEAT(SIF, "sif", STFL, 85, "Sequential-instruction-fetching facility") DEF_FEAT(MSA_EXT_12, "msa12-base", STFL, 86, "Message-security-assist-extension-12 facility (excluding subfunctions)") +DEF_FEAT(PLO_EXT, "plo-ext", STFL, 87, "PLO-extension facility") DEF_FEAT(VECTOR, "vx", STFL, 129, "Vector facility") DEF_FEAT(INSTRUCTION_EXEC_PROT, "iep", STFL, 130, "Instruction-execution-protection facility") DEF_FEAT(SIDE_EFFECT_ACCESS_ESOP2, "sea_esop2", STFL, 131, "Side-effect-access facility and Enhanced-suppression-on-protection facility 2") @@ -180,6 +181,44 @@ DEF_FEAT(PLO_CSTST, "plo-cstst", PLO, 20, "PLO Compare and swap and triple store DEF_FEAT(PLO_CSTSTG, "plo-cststg", PLO, 21, "PLO Compare and swap and triple store (64 bit in parameter list)") DEF_FEAT(PLO_CSTSTGR, "plo-cststgr", PLO, 22, "PLO Compare and swap and triple store (64 bit in general registers)") DEF_FEAT(PLO_CSTSTX, "plo-cststx", PLO, 23, "PLO Compare and swap and triple store (128 bit in parameter list)") +DEF_FEAT(PLO_CLO, "plo-clo", PLO, 24, "PLO Compare and load (256 bit in parameter list)") +DEF_FEAT(PLO_CSO, "plo-cso", PLO, 25, "PLO Compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_DCSO, "plo-dcso", PLO, 26, "PLO Double compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_CSSTO, "plo-cssto", PLO, 27, "PLO Compare and swap and store (256 bit in parameter list)") +DEF_FEAT(PLO_CSDSTO, "plo-csdsto", PLO, 28, "PLO Compare and swap and double store (256 bit in parameter list)") +DEF_FEAT(PLO_CSTSTO, "plo-cststo", PLO, 29, "PLO Compare and swap and trible store (256 bit in parameter list)") +DEF_FEAT(PLO_TCS, "plo-tcs", PLO, 30, "Triple compare and swap (32 bit in parameter list)") +DEF_FEAT(PLO_TCSG, "plo-tcsg", PLO, 31, "Triple compare and swap (64 bit in parameter list)") +DEF_FEAT(PLO_TCSX, "plo-tcsx", PLO, 32, "Triple compare and swap (128 bit in parameter list)") +DEF_FEAT(PLO_TCSO, "plo-tcso", PLO, 33, "Triple compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_QCS, "plo-qcs", PLO, 34, "Quadruple compare and swap (32 bit in parameter list)") +DEF_FEAT(PLO_QCSG, "plo-qcsg", PLO, 35, "Quadruple compare and swap (64 bit in parameter list)") +DEF_FEAT(PLO_QCSX, "plo-qcsx", PLO, 36, "Quadruple compare and swap (128 bit in parameter list)") +DEF_FEAT(PLO_QCSO, "plo-qcso", PLO, 37, "Quadruple compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_LO, "plo-lo", PLO, 38, "Load (256 bit in parameter list)") +DEF_FEAT(PLO_DLX, "plo-dlx", PLO, 39, "Double load (128 bit in parameter list)") +DEF_FEAT(PLO_DLO, "plo-dlo", PLO, 40, "Double load (256 bit in parameter list)") +DEF_FEAT(PLO_TL, "plo-tl", PLO, 41, "Triple load (32 bit in parameter list)") +DEF_FEAT(PLO_TLG, "plo-tlg", PLO, 42, "Triple load (64 bit in parameter list)") +DEF_FEAT(PLO_TLX, "plo-tlx", PLO, 43, "Triple load (128 bit in parameter list)") +DEF_FEAT(PLO_TLO, "plo-tlo", PLO, 44, "Triple load (256 bit in parameter list)") +DEF_FEAT(PLO_QL, "plo-ql", PLO, 45, "Quadruple load (32 bit in parameter list)") +DEF_FEAT(PLO_QLG, "plo-qlg", PLO, 46, "Quadruple load (64 bit in parameter list)") +DEF_FEAT(PLO_QLX, "plo-qlx", PLO, 47, "Quadruple load (128 bit in parameter list)") +DEF_FEAT(PLO_QLO, "plo-qlo", PLO, 48, "Quadruple load (256 bit in parameter list)") +DEF_FEAT(PLO_STO, "plo-sto", PLO, 49, "Store (256 bit in parameter list)") +DEF_FEAT(PLO_DST, "plo-dst", PLO, 50, "Double store (32 bit in parameter list)") +DEF_FEAT(PLO_DSTG, "plo-dstg", PLO, 51, "Double store (64 bit in parameter list)") +DEF_FEAT(PLO_DSTX, "plo-dstx", PLO, 52, "Double store (128 bit in parameter list)") +DEF_FEAT(PLO_DSTO, "plo-dsto", PLO, 53, "Double store (256 bit in parameter list)") +DEF_FEAT(PLO_TST, "plo-tst", PLO, 54, "Triple store (32 bit in parameter list)") +DEF_FEAT(PLO_TSTG, "plo-tstg", PLO, 55, "Triple store (64 bit in parameter list)") +DEF_FEAT(PLO_TSTX, "plo-tstx", PLO, 56, "Triple store (128 bit in parameter list)") +DEF_FEAT(PLO_TSTO, "plo-tsto", PLO, 57, "Triple store (256 bit in parameter list)") +DEF_FEAT(PLO_QST, "plo-qst", PLO, 58, "Quadruple store (32 bit in parameter list)") +DEF_FEAT(PLO_QSTG, "plo-qstg", PLO, 59, "Quadruple store (64 bit in parameter list)") +DEF_FEAT(PLO_QSTX, "plo-qstx", PLO, 60, "Quadruple store (128 bit in parameter list)") +DEF_FEAT(PLO_QSTO, "plo-qsto", PLO, 61, "Quadruple store (256 bit in parameter list)") /* Features exposed via the PTFF instruction. */ DEF_FEAT(PTFF_QTO, "ptff-qto", PTFF, 1, "PTFF Query TOD Offset") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 7fa1712d78..c169c080d1 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -513,6 +513,44 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_PFCR_CSTST, S390_FEAT_CCF_BASE }, { S390_FEAT_PFCR_CSTSTG, S390_FEAT_CCF_BASE }, { S390_FEAT_INEFF_NC_TX, S390_FEAT_TRANSACTIONAL_EXE }, + { S390_FEAT_PLO_CLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DCSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSDSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSTSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCS, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCSG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCSX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCS, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCSG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCSX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_LO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DLX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TL, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TLG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TLX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QL, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QLG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QLX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_STO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DST, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DSTG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DSTX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TST, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TSTG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TSTX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QST, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QSTG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QSTX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QSTO, S390_FEAT_PLO_EXT }, }; int i; diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 6d00ffcda7..680d45d303 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -46,6 +46,47 @@ S390_FEAT_PLO_CSTSTGR, \ S390_FEAT_PLO_CSTSTX +#define S390_FEAT_GROUP_PLO_EXT \ + S390_FEAT_PLO_EXT, \ + S390_FEAT_PLO_CLO, \ + S390_FEAT_PLO_CSO, \ + S390_FEAT_PLO_DCSO, \ + S390_FEAT_PLO_CSSTO, \ + S390_FEAT_PLO_CSDSTO, \ + S390_FEAT_PLO_CSTSTO, \ + S390_FEAT_PLO_TCS, \ + S390_FEAT_PLO_TCSG, \ + S390_FEAT_PLO_TCSX, \ + S390_FEAT_PLO_TCSO, \ + S390_FEAT_PLO_QCS, \ + S390_FEAT_PLO_QCSG, \ + S390_FEAT_PLO_QCSX, \ + S390_FEAT_PLO_QCSO, \ + S390_FEAT_PLO_LO, \ + S390_FEAT_PLO_DLX, \ + S390_FEAT_PLO_DLO, \ + S390_FEAT_PLO_TL, \ + S390_FEAT_PLO_TLG, \ + S390_FEAT_PLO_TLX, \ + S390_FEAT_PLO_TLO, \ + S390_FEAT_PLO_QL, \ + S390_FEAT_PLO_QLG, \ + S390_FEAT_PLO_QLX, \ + S390_FEAT_PLO_QLO, \ + S390_FEAT_PLO_STO, \ + S390_FEAT_PLO_DST, \ + S390_FEAT_PLO_DSTG, \ + S390_FEAT_PLO_DSTX, \ + S390_FEAT_PLO_DSTO, \ + S390_FEAT_PLO_TST, \ + S390_FEAT_PLO_TSTG, \ + S390_FEAT_PLO_TSTX, \ + S390_FEAT_PLO_TSTO, \ + S390_FEAT_PLO_QST, \ + S390_FEAT_PLO_QSTG, \ + S390_FEAT_PLO_QSTX, \ + S390_FEAT_PLO_QSTO + #define S390_FEAT_GROUP_TOD_CLOCK_STEERING \ S390_FEAT_TOD_CLOCK_STEERING, \ S390_FEAT_PTFF_QTO, \ @@ -320,6 +361,9 @@ static uint16_t group_PLO[] = { S390_FEAT_GROUP_PLO, }; +static uint16_t group_PLO_EXT[] = { + S390_FEAT_GROUP_PLO_EXT, +}; static uint16_t group_TOD_CLOCK_STEERING[] = { S390_FEAT_GROUP_TOD_CLOCK_STEERING, }; @@ -936,6 +980,7 @@ typedef struct { *******************************/ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(PLO), + FEAT_GROUP_INITIALIZER(PLO_EXT), FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING), FEAT_GROUP_INITIALIZER(GEN13_PTFF), FEAT_GROUP_INITIALIZER(GEN17_PTFF), From 21b8db229901a51f16aebe342c0508f588ea5006 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 6 Dec 2024 13:27:51 +0100 Subject: [PATCH 0090/2892] s390x/cpumodel: gen17 model This commit introduces the definition of the gen17a/gen17b CPU model. Signed-off-by: Hendrik Brueckner Message-ID: <20241206122751.189721-16-brueckner@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 2 ++ target/s390x/gen-features.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index c169c080d1..beb50b5300 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -94,6 +94,8 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x8562, 15, 1, 47, 0x08000000U, "gen15b", "IBM z15 T02 GA1"), CPUDEF_INIT(0x3931, 16, 1, 47, 0x08000000U, "gen16a", "IBM 3931 GA1"), CPUDEF_INIT(0x3932, 16, 1, 47, 0x08000000U, "gen16b", "IBM 3932 GA1"), + CPUDEF_INIT(0x9175, 17, 1, 47, 0x08000000U, "gen17a", "IBM 9175 GA1"), + CPUDEF_INIT(0x9176, 17, 1, 47, 0x08000000U, "gen17b", "IBM 9176 GA1"), }; #define QEMU_MAX_CPU_TYPE 0x8561 diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 680d45d303..41840677ce 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -561,6 +561,13 @@ static uint16_t base_GEN15_GA1[] = { #define base_GEN16_GA1 EmptyFeat +static uint16_t base_GEN17_GA1[] = { + S390_FEAT_MISC_INSTRUCTION_EXT4, + S390_FEAT_SIF, + S390_FEAT_GROUP_MSA_EXT_12, + S390_FEAT_GROUP_PLO_EXT, +}; + /* Full features (in order of release) * Automatically includes corresponding base features. * Full features are all features this hardware supports even if kvm/QEMU do not @@ -715,6 +722,20 @@ static uint16_t full_GEN16_GA1[] = { S390_FEAT_UV_FEAT_AP_INTR, }; +static uint16_t full_GEN17_GA1[] = { + S390_FEAT_VECTOR_ENH3, + S390_FEAT_VECTOR_PACKED_DECIMAL_ENH3, + S390_FEAT_INEFF_NC_TX, + S390_FEAT_GROUP_GEN17_PTFF, + S390_FEAT_GROUP_MSA_EXT_10, + S390_FEAT_GROUP_MSA_EXT_10_PCKMO, + S390_FEAT_GROUP_MSA_EXT_11, + S390_FEAT_GROUP_MSA_EXT_11_PCKMO, + S390_FEAT_GROUP_MSA_EXT_13, + S390_FEAT_GROUP_MSA_EXT_13_PCKMO, + S390_FEAT_GROUP_CONCURRENT_FUNCTIONS, +}; + /* Default features (in order of release) * Automatically includes corresponding base features. @@ -810,6 +831,17 @@ static uint16_t default_GEN16_GA1[] = { S390_FEAT_PAIE, }; +static uint16_t default_GEN17_GA1[] = { + S390_FEAT_VECTOR_ENH3, + S390_FEAT_VECTOR_PACKED_DECIMAL_ENH3, + S390_FEAT_GROUP_MSA_EXT_10, + S390_FEAT_GROUP_MSA_EXT_10_PCKMO, + S390_FEAT_GROUP_MSA_EXT_11, + S390_FEAT_GROUP_MSA_EXT_11_PCKMO, + S390_FEAT_GROUP_MSA_EXT_13, + S390_FEAT_GROUP_MSA_EXT_13_PCKMO, +}; + /* QEMU (CPU model) features */ static uint16_t qemu_V2_11[] = { @@ -958,6 +990,7 @@ static CpuFeatDefSpec CpuFeatDef[] = { CPU_FEAT_INITIALIZER(GEN14_GA2), CPU_FEAT_INITIALIZER(GEN15_GA1), CPU_FEAT_INITIALIZER(GEN16_GA1), + CPU_FEAT_INITIALIZER(GEN17_GA1), }; #define FEAT_GROUP_INITIALIZER(_name) \ From 73a383dcbabe4ad29087c7beab11c433ffdf699d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 4 Dec 2024 08:07:57 +0100 Subject: [PATCH 0091/2892] tests/functional: Bump the timeout of the sh4_tuxrun test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running "make -j$(nproc) check SPEED=thorough", the sh4_tuxrun test is timing out for me, and using TIMEOUT_MULTIPLIER I can see that it clearly takes more than 100 seconds to finish. Thus increase the timeout setting of this test to avoid the problem. Message-ID: <20241204070757.663119-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index d6d2c0196c..f50bf61a50 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -34,6 +34,7 @@ test_timeouts = { 'ppc64_tuxrun' : 420, 'riscv64_tuxrun' : 120, 's390x_ccw_virtio' : 420, + 'sh4_tuxrun' : 240, } tests_generic_system = [ From cd28b8db0abcf223c9e4b67c4d229be7527410a9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 4 Dec 2024 08:11:20 +0100 Subject: [PATCH 0092/2892] MAINTAINERS: Cover the tests/functional/test_sh4eb_r2d.py file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file should belong to the R2D machine in the MAINTAINERS file. Message-ID: <20241204071120.663446-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index aaf0505a21..727f18fae5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1643,7 +1643,7 @@ F: hw/pci-host/sh_pci.c F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h F: include/hw/timer/tmu012.h -F: tests/functional/test_sh4_r2d.py +F: tests/functional/test_sh4*_r2d.py F: tests/functional/test_sh4_tuxrun.py SPARC Machines From 490d25e6c52b0c32f78b5176d64cf20a53fde51a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Dec 2024 11:23:51 +0100 Subject: [PATCH 0093/2892] tests/functional: Convert the xlnx_versal_virt avocado test A straight-forward conversion of the xlnx_versal_virt boot test to the functional framework. Message-ID: <20241206102358.1186644-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/boot_linux_console.py | 27 -------------- tests/functional/meson.build | 1 + tests/functional/test_aarch64_xlnx_versal.py | 37 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 27 deletions(-) create mode 100755 tests/functional/test_aarch64_xlnx_versal.py diff --git a/MAINTAINERS b/MAINTAINERS index 727f18fae5..86cac83221 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1025,6 +1025,7 @@ F: hw/display/dpcd.c F: include/hw/display/dpcd.h F: docs/system/arm/xlnx-versal-virt.rst F: docs/system/arm/xlnx-zcu102.rst +F: tests/functional/test_aarch64_xlnx_versal.py Xilinx Versal OSPI M: Francisco Iglesias diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 12e24bb05a..44ee50c469 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -116,33 +116,6 @@ class BootLinuxConsole(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - def test_aarch64_xlnx_versal_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:xlnx-versal-virt - :avocado: tags=device:pl011 - :avocado: tags=device:arm_gicv3 - :avocado: tags=accel:tcg - """ - images_url = ('http://ports.ubuntu.com/ubuntu-ports/dists/' - 'bionic-updates/main/installer-arm64/' - '20101020ubuntu543.19/images/') - kernel_url = images_url + 'netboot/ubuntu-installer/arm64/linux' - kernel_hash = 'e167757620640eb26de0972f578741924abb3a82' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = images_url + 'netboot/ubuntu-installer/arm64/initrd.gz' - initrd_hash = 'cab5cb3fcefca8408aa5aae57f24574bfce8bdb9' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - self.vm.set_console() - self.vm.add_args('-m', '2G', - '-accel', 'tcg', - '-kernel', kernel_path, - '-initrd', initrd_path) - self.vm.launch() - self.wait_for_console_pattern('Checked W+X mappings: passed') - def test_arm_virt(self): """ :avocado: tags=arch:arm diff --git a/tests/functional/meson.build b/tests/functional/meson.build index f50bf61a50..6e22d8f4f0 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -58,6 +58,7 @@ tests_aarch64_system_thorough = [ 'aarch64_sbsaref_freebsd', 'aarch64_tuxrun', 'aarch64_virt', + 'aarch64_xlnx_versal', 'multiprocess', ] diff --git a/tests/functional/test_aarch64_xlnx_versal.py b/tests/functional/test_aarch64_xlnx_versal.py new file mode 100755 index 0000000000..4b9c49e5d6 --- /dev/null +++ b/tests/functional/test_aarch64_xlnx_versal.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class XlnxVersalVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' + 'installer-arm64/20101020ubuntu543.19/images/netboot/' + 'ubuntu-installer/arm64/linux'), + 'ce54f74ab0b15cfd13d1a293f2d27ffd79d8a85b7bb9bf21093ae9513864ac79') + + ASSET_INITRD = Asset( + ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' + 'installer-arm64/20101020ubuntu543.19/images/netboot/' + '/ubuntu-installer/arm64/initrd.gz'), + 'e7a5e716b6f516d8be315c06e7331aaf16994fe4222e0e7cfb34bc015698929e') + + def test_aarch64_xlnx_versal_virt(self): + self.set_machine('xlnx-versal-virt') + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + self.vm.set_console() + self.vm.add_args('-m', '2G', + '-accel', 'tcg', + '-kernel', kernel_path, + '-initrd', initrd_path) + self.vm.launch() + self.wait_for_console_pattern('Checked W+X mappings: passed') + +if __name__ == '__main__': + LinuxKernelTest.main() From bade2d51fb3a0ad73ffa0d369bdf56ced698074d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Dec 2024 11:23:52 +0100 Subject: [PATCH 0094/2892] tests/functional: Convert the emcraft_sf2 avocado test A pretty straight-forward conversion of the emcraft_sf2 boot test to the functional framework. This was the last test that used file_truncate() in boot_linux_console.py, so we can remove that function from that file now, too. Message-ID: <20241206102358.1186644-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/boot_linux_console.py | 44 -------------------- tests/functional/meson.build | 1 + tests/functional/test_arm_emcraft_sf2.py | 52 ++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 44 deletions(-) create mode 100755 tests/functional/test_arm_emcraft_sf2.py diff --git a/MAINTAINERS b/MAINTAINERS index 86cac83221..435a87b146 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1116,6 +1116,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c F: docs/system/arm/emcraft-sf2.rst +F: tests/functional/test_arm_emcraft_sf2.py ASPEED BMCs M: Cédric Le Goater diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 44ee50c469..5d5795e41b 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -30,11 +30,6 @@ Round up to next power of 2 def pow2ceil(x): return 1 if x == 0 else 2**(x - 1).bit_length() -def file_truncate(path, size): - if size != os.path.getsize(path): - with open(path, 'ab+') as fd: - fd.truncate(size) - """ Expand file size to next power of 2 """ @@ -137,45 +132,6 @@ class BootLinuxConsole(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - def test_arm_emcraft_sf2(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:emcraft-sf2 - :avocado: tags=endian:little - :avocado: tags=u-boot - :avocado: tags=accel:tcg - """ - self.require_netdev('user') - - uboot_url = ('https://raw.githubusercontent.com/' - 'Subbaraya-Sundeep/qemu-test-binaries/' - 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot') - uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2' - uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) - spi_url = ('https://raw.githubusercontent.com/' - 'Subbaraya-Sundeep/qemu-test-binaries/' - 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin') - spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501' - spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) - spi_path_rw = os.path.join(self.workdir, os.path.basename(spi_path)) - shutil.copy(spi_path, spi_path_rw) - - file_truncate(spi_path_rw, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE - self.vm.add_args('-kernel', uboot_path, - '-append', kernel_command_line, - '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw', - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Enter \'help\' for a list') - - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15', - 'eth0: link becomes ready') - exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', - '3 packets transmitted, 3 packets received, 0% packet loss') - def test_arm_exynos4210_initrd(self): """ :avocado: tags=arch:arm diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 6e22d8f4f0..af38494e14 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -71,6 +71,7 @@ tests_arm_system_thorough = [ 'arm_bpim2u', 'arm_canona1100', 'arm_collie', + 'arm_emcraft_sf2', 'arm_integratorcp', 'arm_orangepi', 'arm_raspi2', diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/test_arm_emcraft_sf2.py new file mode 100755 index 0000000000..ada4dfd82e --- /dev/null +++ b/tests/functional/test_arm_emcraft_sf2.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test.utils import file_truncate + +class EmcraftSf2Machine(LinuxKernelTest): + + ASSET_UBOOT = Asset( + ('https://raw.githubusercontent.com/Subbaraya-Sundeep/qemu-test-binaries/' + 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot'), + '5c6a15103375db11b21f2236473679a9dbbed6d89652bfcdd501c263d68ab725') + + ASSET_SPI = Asset( + ('https://raw.githubusercontent.com/Subbaraya-Sundeep/qemu-test-binaries/' + 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin'), + 'cd9bdd2c4cb55a59c3adb6bcf74881667c4500dde0570a43aa3be2b17eecfdb6') + + def test_arm_emcraft_sf2(self): + self.set_machine('emcraft-sf2') + self.require_netdev('user') + + uboot_path = self.ASSET_UBOOT.fetch() + spi_path = self.ASSET_SPI.fetch() + spi_path_rw = os.path.join(self.workdir, 'spi.bin') + shutil.copy(spi_path, spi_path_rw) + os.chmod(spi_path_rw, 0o600) + + file_truncate(spi_path_rw, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + self.vm.add_args('-kernel', uboot_path, + '-append', kernel_command_line, + '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw', + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Enter \'help\' for a list') + + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15', + 'eth0: link becomes ready') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + +if __name__ == '__main__': + LinuxKernelTest.main() From 81e2926d4bf37cbac9497fff5941d37f9982d741 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Dec 2024 11:23:53 +0100 Subject: [PATCH 0095/2892] tests/functional: Convert the smdkc210 avocado test A straight forward conversion, just the hashsums needed to be updated to sha256 now. Message-ID: <20241206102358.1186644-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/boot_linux_console.py | 41 ------------------- tests/functional/meson.build | 1 + tests/functional/test_arm_smdkc210.py | 57 +++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 41 deletions(-) create mode 100755 tests/functional/test_arm_smdkc210.py diff --git a/MAINTAINERS b/MAINTAINERS index 435a87b146..9b1c3459b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -720,6 +720,7 @@ S: Odd Fixes F: hw/*/exynos* F: include/hw/*/exynos* F: docs/system/arm/exynos.rst +F: tests/functional/test_arm_smdkc210.py Calxeda Highbank M: Rob Herring diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 5d5795e41b..3250168ccf 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -132,47 +132,6 @@ class BootLinuxConsole(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - def test_arm_exynos4210_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:smdkc210 - :avocado: tags=accel:tcg - """ - deb_url = ('https://snapshot.debian.org/archive/debian/' - '20190928T224601Z/pool/main/l/linux/' - 'linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb') - deb_hash = 'fa9df4a0d38936cb50084838f2cb933f570d7d82' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.19.0-6-armmp') - dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'earlycon=exynos4210,0x13800000 earlyprintk ' + - 'console=ttySAC0,115200n8 ' + - 'random.trust_cpu=off cryptomgr.notests ' + - 'cpuidle.off=1 panic=-1 noreboot') - - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - - self.wait_for_console_pattern('Boot successful.') - # TODO user command, for now the uart is stuck - def test_arm_cubieboard_initrd(self): """ :avocado: tags=arch:arm diff --git a/tests/functional/meson.build b/tests/functional/meson.build index af38494e14..8c21b53ccc 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -75,6 +75,7 @@ tests_arm_system_thorough = [ 'arm_integratorcp', 'arm_orangepi', 'arm_raspi2', + 'arm_smdkc210', 'arm_sx1', 'arm_vexpress', 'arm_tuxrun', diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py new file mode 100755 index 0000000000..967752feeb --- /dev/null +++ b/tests/functional/test_arm_smdkc210.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test.utils import gzip_uncompress + +class Smdkc210Machine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://snapshot.debian.org/archive/debian/20190928T224601Z/pool/' + 'main/l/linux/linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb'), + '421804e7579ef40d554c962850dbdf1bfc79f7fa7faec9d391397170dc806c3e') + + ASSET_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/' + 'rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_arm_exynos4210_initrd(self): + self.set_machine('smdkc210') + + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.19.0-6-armmp') + dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + + initrd_path_gz = self.ASSET_ROOTFS.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=exynos4210,0x13800000 earlyprintk ' + + 'console=ttySAC0,115200n8 ' + + 'random.trust_cpu=off cryptomgr.notests ' + + 'cpuidle.off=1 panic=-1 noreboot') + + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + + self.wait_for_console_pattern('Boot successful.') + # TODO user command, for now the uart is stuck + +if __name__ == '__main__': + LinuxKernelTest.main() From d4d183af34b7dc61496349a63594c8b47cc71c10 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Dec 2024 11:23:54 +0100 Subject: [PATCH 0096/2892] tests/functional: Convert the cubieboard avocado tests Straight forward conversion, just the hashsums needed to be updated to sha256 now. These were the last tests that used image_pow2ceil_expand in boot_linux_console.py, so we can remove that function from that file now, too. Message-ID: <20241206102358.1186644-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/boot_linux_console.py | 157 ------------------------ tests/functional/meson.build | 2 + tests/functional/test_arm_cubieboard.py | 150 ++++++++++++++++++++++ 4 files changed, 153 insertions(+), 157 deletions(-) create mode 100755 tests/functional/test_arm_cubieboard.py diff --git a/MAINTAINERS b/MAINTAINERS index 9b1c3459b4..dc583d5853 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -633,6 +633,7 @@ F: include/hw/*/allwinner* F: hw/arm/cubieboard.c F: docs/system/arm/cubieboard.rst F: hw/misc/axp209.c +F: tests/functional/test_arm_cubieboard.py Allwinner-h3 M: Niek Linnenbank diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 3250168ccf..ea31f154e3 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -24,22 +24,6 @@ from avocado_qemu import wait_for_console_pattern from avocado.utils import process from avocado.utils import archive -""" -Round up to next power of 2 -""" -def pow2ceil(x): - return 1 if x == 0 else 2**(x - 1).bit_length() - -""" -Expand file size to next power of 2 -""" -def image_pow2ceil_expand(path): - size = os.path.getsize(path) - size_aligned = pow2ceil(size) - if size != size_aligned: - with open(path, 'ab+') as fd: - fd.truncate(size_aligned) - class LinuxKernelTest(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' @@ -132,147 +116,6 @@ class BootLinuxConsole(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - def test_arm_cubieboard_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_cubieboard_sata(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.ext2.gz') - rootfs_hash = '093e89d2b4d982234bf528bc9fb2f2f17a9d1f93' - rootfs_path_gz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(rootfs_path_gz, rootfs_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'root=/dev/sda ro ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'if=none,format=raw,id=disk0,file=' - + rootfs_path, - '-device', 'ide-hd,bus=ide.0,drive=disk0', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'sda') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_cubieboard_openwrt_22_03_2(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :avocado: tags=device:sd - """ - - # This test download a 7.5 MiB compressed image and expand it - # to 126 MiB. - image_url = ('https://downloads.openwrt.org/releases/22.03.2/targets/' - 'sunxi/cortexa8/openwrt-22.03.2-sunxi-cortexa8-' - 'cubietech_a10-cubieboard-ext4-sdcard.img.gz') - image_hash = ('94b5ecbfbc0b3b56276e5146b899eafa' - '2ac5dc2d08733d6705af9f144f39f554') - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_gz, self.workdir) - image_pow2ceil_expand(image_path) - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', - '-nic', 'user', - '-no-reboot') - self.vm.launch() - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'usbcore.nousb ' - 'noreboot') - - self.wait_for_console_pattern('U-Boot SPL') - - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', '=>') - exec_command_and_wait_for_pattern(self, "setenv extraargs '" + - kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); - - self.wait_for_console_pattern( - 'Please press Enter to activate this console.') - - exec_command_and_wait_for_pattern(self, ' ', 'root@') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') def test_arm_quanta_gsj(self): """ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8c21b53ccc..b996118dc8 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -21,6 +21,7 @@ test_timeouts = { 'arm_aspeed' : 600, 'arm_bpim2u' : 500, 'arm_collie' : 180, + 'arm_cubieboard' : 360, 'arm_orangepi' : 540, 'arm_raspi2' : 120, 'arm_tuxrun' : 240, @@ -71,6 +72,7 @@ tests_arm_system_thorough = [ 'arm_bpim2u', 'arm_canona1100', 'arm_collie', + 'arm_cubieboard', 'arm_emcraft_sf2', 'arm_integratorcp', 'arm_orangepi', diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py new file mode 100755 index 0000000000..2b33a1b50b --- /dev/null +++ b/tests/functional/test_arm_cubieboard.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test.utils import gzip_uncompress, image_pow2ceil_expand +from unittest import skipUnless + +class CubieboardMachine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + ASSET_SATA_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.ext2.gz'), + '17fc750da568580b39372133051ef2f0a963c0c0b369b845614442d025701745') + + ASSET_OPENWRT = Asset( + ('https://downloads.openwrt.org/releases/22.03.2/targets/sunxi/cortexa8/' + 'openwrt-22.03.2-sunxi-cortexa8-cubietech_a10-cubieboard-ext4-sdcard.img.gz'), + '94b5ecbfbc0b3b56276e5146b899eafa2ac5dc2d08733d6705af9f144f39f554') + + def test_arm_cubieboard_initrd(self): + self.set_machine('cubieboard') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_path_gz = self.ASSET_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + def test_arm_cubieboard_sata(self): + self.set_machine('cubieboard') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + + rootfs_path_gz = self.ASSET_SATA_ROOTFS.fetch() + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(rootfs_path_gz, rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'root=/dev/sda ro ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'if=none,format=raw,id=disk0,file=' + + rootfs_path, + '-device', 'ide-hd,bus=ide.0,drive=disk0', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'sda') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_cubieboard_openwrt_22_03_2(self): + # This test download a 7.5 MiB compressed image and expand it + # to 126 MiB. + self.set_machine('cubieboard') + image_path_gz = self.ASSET_OPENWRT.fetch() + image_path = os.path.join(self.workdir, 'sdcard.img') + gzip_uncompress(image_path_gz, image_path) + image_pow2ceil_expand(image_path) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'usbcore.nousb ' + 'noreboot') + + self.wait_for_console_pattern('U-Boot SPL') + + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + + self.wait_for_console_pattern( + 'Please press Enter to activate this console.') + + exec_command_and_wait_for_pattern(self, ' ', 'root@') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + +if __name__ == '__main__': + LinuxKernelTest.main() From 6d509f637e2ee98b1ed106018b69780a4dd3c255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Nov 2024 17:31:00 +0000 Subject: [PATCH 0097/2892] tests/functional: remove unused system imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20241129173120.761728-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_sbsaref.py | 1 - tests/functional/test_acpi_bits.py | 1 - tests/functional/test_m68k_mcf5208evb.py | 2 -- tests/functional/test_microblaze_s3adsp1800.py | 1 - tests/functional/test_mips64el_loongson3v.py | 1 - tests/functional/test_or1k_sim.py | 2 -- tests/functional/test_s390x_topology.py | 1 - tests/functional/test_sh4_tuxrun.py | 4 ---- tests/functional/test_sh4eb_r2d.py | 1 - tests/functional/test_virtio_version.py | 2 -- 10 files changed, 16 deletions(-) diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index 9fda396b3a..6db08da522 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -14,7 +14,6 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import interrupt_interactive_console_until_pattern from qemu_test.utils import lzma_uncompress -from unittest import skipUnless def fetch_firmware(test): """ diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index e2f84414d7..63e2c5309d 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -39,7 +39,6 @@ import shutil import subprocess import tarfile import tempfile -import time import zipfile from pathlib import Path diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/test_m68k_mcf5208evb.py index 00c59590c3..fb178fde1c 100755 --- a/tests/functional/test_m68k_mcf5208evb.py +++ b/tests/functional/test_m68k_mcf5208evb.py @@ -5,8 +5,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test.utils import archive_extract diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index 4f692ffdb1..d2be3105a2 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -7,7 +7,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import time from qemu_test import exec_command, exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/test_mips64el_loongson3v.py index 55d62928c7..e57ec5499e 100755 --- a/tests/functional/test_mips64el_loongson3v.py +++ b/tests/functional/test_mips64el_loongson3v.py @@ -10,7 +10,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os -import time from unittest import skipUnless from qemu_test import QemuSystemTest, Asset diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/test_or1k_sim.py index 10e0437c50..5b68b6b628 100755 --- a/tests/functional/test_or1k_sim.py +++ b/tests/functional/test_or1k_sim.py @@ -5,8 +5,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test.utils import archive_extract diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py index 20727f6bdf..c54c7a8177 100755 --- a/tests/functional/test_s390x_topology.py +++ b/tests/functional/test_s390x_topology.py @@ -11,7 +11,6 @@ # later. See the COPYING file in the top-level directory. import os -import time from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command diff --git a/tests/functional/test_sh4_tuxrun.py b/tests/functional/test_sh4_tuxrun.py index b33533fc7e..1748f8c7ef 100755 --- a/tests/functional/test_sh4_tuxrun.py +++ b/tests/functional/test_sh4_tuxrun.py @@ -11,10 +11,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os -import time - -from unittest import skipUnless from qemu_test import Asset, exec_command_and_wait_for_pattern from qemu_test.tuxruntest import TuxRunBaselineTest diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/test_sh4eb_r2d.py index d9c022c8b8..cd46007942 100755 --- a/tests/functional/test_sh4eb_r2d.py +++ b/tests/functional/test_sh4eb_r2d.py @@ -10,7 +10,6 @@ import shutil from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test.utils import archive_extract -from unittest import skipUnless class R2dEBTest(LinuxKernelTest): diff --git a/tests/functional/test_virtio_version.py b/tests/functional/test_virtio_version.py index 92e3f5caf0..a5ea73237f 100755 --- a/tests/functional/test_virtio_version.py +++ b/tests/functional/test_virtio_version.py @@ -9,8 +9,6 @@ Check compatibility of virtio device types # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import sys -import os from qemu.machine import QEMUMachine from qemu_test import QemuSystemTest From f29c96d0f5760f72220623500ee659f1fc2d1542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Nov 2024 17:31:02 +0000 Subject: [PATCH 0098/2892] tests/functional: remove pointless with statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xorriso command directly writes to 'filename', so the surrounding 'with' statement is pointless. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20241129173120.761728-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_hv.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 312248bbfe..d97b62e364 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -72,10 +72,9 @@ class HypervisorTest(QemuSystemTest): cwd = os.getcwd() os.chdir(self.workdir) - with open(filename, "w") as outfile: - cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) - subprocess.run(cmd.split(), - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) + subprocess.run(cmd.split(), + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.chmod(filename, 0o600) os.chdir(cwd) From 1f9d52c9388d14c3f5a605543a8ef53dceaad5bb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Oct 2024 10:45:55 +0100 Subject: [PATCH 0099/2892] rust: qom: move bridge for TypeInfo functions out of pl011 Allow the ObjectImpl trait to expose Rust functions that avoid raw pointers (though INSTANCE_INIT for example is still unsafe). ObjectImpl::TYPE_INFO adds thunks around the functions in ObjectImpl. While at it, document `TypeInfo`. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 40 +++++++-------------- rust/qemu-api/src/definitions.rs | 61 +++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 32 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 56403c3660..b9f8fb134b 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -110,7 +110,7 @@ impl ObjectImpl for PL011State { type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); - const INSTANCE_INIT: Option = Some(pl011_init); + const INSTANCE_INIT: Option = Some(Self::init); } #[repr(C)] @@ -615,19 +615,6 @@ pub unsafe extern "C" fn pl011_create( } } -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -pub unsafe extern "C" fn pl011_init(obj: *mut Object) { - unsafe { - debug_assert!(!obj.is_null()); - let mut state = NonNull::new_unchecked(obj.cast::()); - state.as_mut().init(); - } -} - #[repr(C)] #[derive(Debug, qemu_api_macros::Object)] /// PL011 Luminary device model. @@ -640,19 +627,16 @@ pub struct PL011LuminaryClass { _inner: [u8; 0], } -/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. -/// -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011Luminary`]. We also expect the device is -/// readable/writeable from one thread at any time. -pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) { - unsafe { - debug_assert!(!obj.is_null()); - let mut state = NonNull::new_unchecked(obj.cast::()); - let state = state.as_mut(); - state.parent_obj.device_id = DeviceId::Luminary; +impl PL011Luminary { + /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. + /// + /// # Safety + /// + /// We expect the FFI user of this function to pass a valid pointer, that + /// has the same size as [`PL011Luminary`]. We also expect the device is + /// readable/writeable from one thread at any time. + unsafe fn init(&mut self) { + self.parent_obj.device_id = DeviceId::Luminary; } } @@ -660,7 +644,7 @@ impl ObjectImpl for PL011Luminary { type Class = PL011LuminaryClass; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); - const INSTANCE_INIT: Option = Some(pl011_luminary_init); + const INSTANCE_INIT: Option = Some(Self::init); } impl DeviceImpl for PL011Luminary {} diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 0467e6290e..f297075898 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -8,16 +8,63 @@ use std::{ffi::CStr, os::raw::c_void}; use crate::bindings::{Object, ObjectClass, TypeInfo}; +unsafe extern "C" fn rust_instance_init(obj: *mut Object) { + // SAFETY: obj is an instance of T, since rust_instance_init + // is called from QOM core as the instance_init function + // for class T + unsafe { T::INSTANCE_INIT.unwrap()(&mut *obj.cast::()) } +} + +unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { + // SAFETY: obj is an instance of T, since rust_instance_post_init + // is called from QOM core as the instance_post_init function + // for class T + // + // FIXME: it's not really guaranteed that there are no backpointers to + // obj; it's quite possible that they have been created by instance_init(). + // The receiver should be &self, not &mut self. + T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::() }) +} + /// Trait a type must implement to be registered with QEMU. +/// +/// # Safety +/// +/// - the struct must be `#[repr(C)]` +/// +/// - `Class` and `TYPE` must match the data in the `TypeInfo` (this is +/// automatic if the class is defined via `ObjectImpl`). +/// +/// - the first field of the struct must be of the instance struct corresponding +/// to the superclass declared as `PARENT_TYPE_NAME` pub trait ObjectImpl: ClassInitImpl + Sized { + /// The QOM class object corresponding to this struct. Not used yet. type Class; + + /// The name of the type, which can be passed to `object_new()` to + /// generate an instance of this type. const TYPE_NAME: &'static CStr; + + /// The parent of the type. This should match the first field of + /// the struct that implements `ObjectImpl`: const PARENT_TYPE_NAME: Option<&'static CStr>; + + /// Whether the object can be instantiated const ABSTRACT: bool = false; - const INSTANCE_INIT: Option = None; - const INSTANCE_POST_INIT: Option = None; const INSTANCE_FINALIZE: Option = None; + /// Function that is called to initialize an object. The parent class will + /// have already been initialized so the type is only responsible for + /// initializing its own members. + /// + /// FIXME: The argument is not really a valid reference. `&mut + /// MaybeUninit` would be a better description. + const INSTANCE_INIT: Option = None; + + /// Function that is called to finish initialization of an object, once + /// `INSTANCE_INIT` functions have been called. + const INSTANCE_POST_INIT: Option = None; + const TYPE_INFO: TypeInfo = TypeInfo { name: Self::TYPE_NAME.as_ptr(), parent: if let Some(pname) = Self::PARENT_TYPE_NAME { @@ -27,8 +74,14 @@ pub trait ObjectImpl: ClassInitImpl + Sized { }, instance_size: core::mem::size_of::(), instance_align: core::mem::align_of::(), - instance_init: Self::INSTANCE_INIT, - instance_post_init: Self::INSTANCE_POST_INIT, + instance_init: match Self::INSTANCE_INIT { + None => None, + Some(_) => Some(rust_instance_init::), + }, + instance_post_init: match Self::INSTANCE_POST_INIT { + None => None, + Some(_) => Some(rust_instance_post_init::), + }, instance_finalize: Self::INSTANCE_FINALIZE, abstract_: Self::ABSTRACT, class_size: core::mem::size_of::(), From 7bd8e3ef63330e870cf4644d21c285cce35c703d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Oct 2024 09:56:15 +0100 Subject: [PATCH 0100/2892] rust: qom: split ObjectType from ObjectImpl trait Define a separate trait for fields that also applies to classes that are defined by C code. This makes it possible to add metadata to core classes, which has multiple uses: - it makes it possible to access the parent struct's TYPE_* for types that are defined in Rust code, and to avoid repeating it in every subclass - implementors of ObjectType will be allowed to implement the IsA<> trait and therefore to perform typesafe casts from one class to another. - in the future, an ObjectType could be created with Foo::new() in a type-safe manner, without having to pass a TYPE_* constant. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 17 ++++++++++++----- rust/qemu-api/src/definitions.rs | 27 +++++++++++++++++++++------ rust/qemu-api/src/device_class.rs | 11 ++++++----- rust/qemu-api/src/prelude.rs | 2 ++ rust/qemu-api/src/sysbus.rs | 10 ++++++++-- rust/qemu-api/tests/tests.rs | 17 +++++++++-------- 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index b9f8fb134b..0ab825b1ca 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -12,9 +12,10 @@ use qemu_api::{ bindings::{self, *}, c_str, definitions::ObjectImpl, - device_class::{DeviceImpl, TYPE_SYS_BUS_DEVICE}, + device_class::DeviceImpl, impl_device_class, irq::InterruptSource, + prelude::*, }; use crate::{ @@ -106,10 +107,13 @@ pub struct PL011State { device_id: DeviceId, } -impl ObjectImpl for PL011State { +unsafe impl ObjectType for PL011State { type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; - const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); +} + +impl ObjectImpl for PL011State { + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(::TYPE_NAME); const INSTANCE_INIT: Option = Some(Self::init); } @@ -640,10 +644,13 @@ impl PL011Luminary { } } -impl ObjectImpl for PL011Luminary { +unsafe impl ObjectType for PL011Luminary { type Class = PL011LuminaryClass; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; - const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); +} + +impl ObjectImpl for PL011Luminary { + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(::TYPE_NAME); const INSTANCE_INIT: Option = Some(Self::init); } diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index f297075898..b98a692678 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -26,25 +26,40 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::() }) } -/// Trait a type must implement to be registered with QEMU. +/// Trait exposed by all structs corresponding to QOM objects. /// /// # Safety /// -/// - the struct must be `#[repr(C)]` +/// For classes declared in C: /// -/// - `Class` and `TYPE` must match the data in the `TypeInfo` (this is -/// automatic if the class is defined via `ObjectImpl`). +/// - `Class` and `TYPE` must match the data in the `TypeInfo`; +/// +/// - the first field of the struct must be of the instance type corresponding +/// to the superclass, as declared in the `TypeInfo` +/// +/// - likewise, the first field of the `Class` struct must be of the class type +/// corresponding to the superclass +/// +/// For classes declared in Rust and implementing [`ObjectImpl`]: +/// +/// - the struct must be `#[repr(C)]`; /// /// - the first field of the struct must be of the instance struct corresponding -/// to the superclass declared as `PARENT_TYPE_NAME` -pub trait ObjectImpl: ClassInitImpl + Sized { +/// to the superclass, as declared in `ObjectImpl::PARENT_TYPE_NAME` +/// +/// - likewise, the first field of the `Class` must be of the class struct +/// corresponding to the superclass +pub unsafe trait ObjectType: Sized { /// The QOM class object corresponding to this struct. Not used yet. type Class; /// The name of the type, which can be passed to `object_new()` to /// generate an instance of this type. const TYPE_NAME: &'static CStr; +} +/// Trait a type must implement to be registered with QEMU. +pub trait ObjectImpl: ObjectType + ClassInitImpl { /// The parent of the type. This should match the first field of /// the struct that implements `ObjectImpl`: const PARENT_TYPE_NAME: Option<&'static CStr>; diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index f25904be4f..03d03feee8 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -6,6 +6,7 @@ use std::{ffi::CStr, os::raw::c_void}; use crate::{ bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, + prelude::*, zeroable::Zeroable, }; @@ -146,8 +147,8 @@ macro_rules! declare_properties { }; } -// workaround until we can use --generate-cstr in bindgen. -pub const TYPE_DEVICE: &CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; -pub const TYPE_SYS_BUS_DEVICE: &CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; +unsafe impl ObjectType for bindings::DeviceState { + type Class = bindings::DeviceClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; +} diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index a39e228bab..1b8677b2d9 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -6,3 +6,5 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; + +pub use crate::definitions::ObjectType; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 4e192c7589..5ee068541c 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -2,11 +2,17 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later -use std::ptr::addr_of; +use std::{ffi::CStr, ptr::addr_of}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; -use crate::{bindings, cell::bql_locked, irq::InterruptSource}; +use crate::{bindings, cell::bql_locked, irq::InterruptSource, prelude::*}; + +unsafe impl ObjectType for SysBusDevice { + type Class = SysBusDeviceClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; +} impl SysBusDevice { /// Return `self` cast to a mutable pointer, for use in calls to C code. diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index b8b12a4042..1d027dd652 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -5,12 +5,8 @@ use std::ffi::CStr; use qemu_api::{ - bindings::*, - c_str, declare_properties, define_property, - definitions::ObjectImpl, - device_class::{self, DeviceImpl}, - impl_device_class, - zeroable::Zeroable, + bindings::*, c_str, declare_properties, define_property, definitions::ObjectImpl, + device_class::DeviceImpl, impl_device_class, prelude::*, zeroable::Zeroable, }; #[test] @@ -46,10 +42,15 @@ fn test_device_decl_macros() { ), } - impl ObjectImpl for DummyState { + unsafe impl ObjectType for DummyState { type Class = DummyClass; const TYPE_NAME: &'static CStr = c_str!("dummy"); - const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); + } + + impl ObjectImpl for DummyState { + const PARENT_TYPE_NAME: Option<&'static CStr> = + Some(::TYPE_NAME); + const ABSTRACT: bool = false; } impl DeviceImpl for DummyState { From 166e8a1fd15bfa527b25fc15ca315e572c0556d2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 Nov 2024 18:51:34 +0100 Subject: [PATCH 0101/2892] rust: qom: change the parent type to an associated type Avoid duplicated code to retrieve the QOM type strings from the Rust type. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ++++-- rust/qemu-api/src/definitions.rs | 12 ++++-------- rust/qemu-api/tests/tests.rs | 3 +-- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 0ab825b1ca..3e29442a62 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -113,7 +113,8 @@ unsafe impl ObjectType for PL011State { } impl ObjectImpl for PL011State { - const PARENT_TYPE_NAME: Option<&'static CStr> = Some(::TYPE_NAME); + type ParentType = SysBusDevice; + const INSTANCE_INIT: Option = Some(Self::init); } @@ -650,7 +651,8 @@ unsafe impl ObjectType for PL011Luminary { } impl ObjectImpl for PL011Luminary { - const PARENT_TYPE_NAME: Option<&'static CStr> = Some(::TYPE_NAME); + type ParentType = PL011State; + const INSTANCE_INIT: Option = Some(Self::init); } diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index b98a692678..df91a2e31a 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -45,10 +45,10 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { /// - the struct must be `#[repr(C)]`; /// /// - the first field of the struct must be of the instance struct corresponding -/// to the superclass, as declared in `ObjectImpl::PARENT_TYPE_NAME` +/// to the superclass, which is `ObjectImpl::ParentType` /// /// - likewise, the first field of the `Class` must be of the class struct -/// corresponding to the superclass +/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. pub unsafe trait ObjectType: Sized { /// The QOM class object corresponding to this struct. Not used yet. type Class; @@ -62,7 +62,7 @@ pub unsafe trait ObjectType: Sized { pub trait ObjectImpl: ObjectType + ClassInitImpl { /// The parent of the type. This should match the first field of /// the struct that implements `ObjectImpl`: - const PARENT_TYPE_NAME: Option<&'static CStr>; + type ParentType: ObjectType; /// Whether the object can be instantiated const ABSTRACT: bool = false; @@ -82,11 +82,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { const TYPE_INFO: TypeInfo = TypeInfo { name: Self::TYPE_NAME.as_ptr(), - parent: if let Some(pname) = Self::PARENT_TYPE_NAME { - pname.as_ptr() - } else { - core::ptr::null_mut() - }, + parent: Self::ParentType::TYPE_NAME.as_ptr(), instance_size: core::mem::size_of::(), instance_align: core::mem::align_of::(), instance_init: match Self::INSTANCE_INIT { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 1d027dd652..278efe967f 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -48,8 +48,7 @@ fn test_device_decl_macros() { } impl ObjectImpl for DummyState { - const PARENT_TYPE_NAME: Option<&'static CStr> = - Some(::TYPE_NAME); + type ParentType = DeviceState; const ABSTRACT: bool = false; } From c0cf6b412ecb099d49fe040d32fd5dd149f770d7 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 11 Dec 2024 15:30:51 +0000 Subject: [PATCH 0102/2892] hw/net/lan9118: Extract lan9118_phy A very similar implementation of the same device exists in imx_fec. Prepare for a common implementation by extracting a device model into its own files. Some migration state has been moved into the new device model which breaks migration compatibility for the following machines: * smdkc210 * realview-* * vexpress-* * kzm * mps2-* While breaking migration ABI, fix the size of the MII registers to be 16 bit, as defined by IEEE 802.3u. Signed-off-by: Bernhard Beschow Tested-by: Guenter Roeck Reviewed-by: Peter Maydell Message-id: 20241102125724.532843-2-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/Kconfig | 4 + hw/net/lan9118.c | 137 +++++----------------------- hw/net/lan9118_phy.c | 169 +++++++++++++++++++++++++++++++++++ hw/net/meson.build | 1 + include/hw/net/lan9118_phy.h | 37 ++++++++ 5 files changed, 233 insertions(+), 115 deletions(-) create mode 100644 hw/net/lan9118_phy.c create mode 100644 include/hw/net/lan9118_phy.h diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 7fcc0d7faa..6b2ff2f937 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -62,8 +62,12 @@ config VMXNET3_PCI config SMC91C111 bool +config LAN9118_PHY + bool + config LAN9118 bool + select LAN9118_PHY select PTIMER config NE2000_ISA diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index db28a0ef30..99e87b7178 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -16,6 +16,7 @@ #include "net/net.h" #include "net/eth.h" #include "hw/irq.h" +#include "hw/net/lan9118_phy.h" #include "hw/net/lan9118.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" @@ -139,14 +140,6 @@ do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) #define MAC_CR_RXEN 0x00000004 #define MAC_CR_RESERVED 0x7f404213 -#define PHY_INT_ENERGYON 0x80 -#define PHY_INT_AUTONEG_COMPLETE 0x40 -#define PHY_INT_FAULT 0x20 -#define PHY_INT_DOWN 0x10 -#define PHY_INT_AUTONEG_LP 0x08 -#define PHY_INT_PARFAULT 0x04 -#define PHY_INT_AUTONEG_PAGE 0x02 - #define GPT_TIMER_EN 0x20000000 /* @@ -228,11 +221,8 @@ struct lan9118_state { uint32_t mac_mii_data; uint32_t mac_flow; - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; + Lan9118PhyState mii; + IRQState mii_irq; int32_t eeprom_writable; uint8_t eeprom[128]; @@ -274,8 +264,8 @@ struct lan9118_state { static const VMStateDescription vmstate_lan9118 = { .name = "lan9118", - .version_id = 2, - .minimum_version_id = 1, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, lan9118_state), VMSTATE_UINT32(irq_cfg, lan9118_state), @@ -301,11 +291,6 @@ static const VMStateDescription vmstate_lan9118 = { VMSTATE_UINT32(mac_mii_acc, lan9118_state), VMSTATE_UINT32(mac_mii_data, lan9118_state), VMSTATE_UINT32(mac_flow, lan9118_state), - VMSTATE_UINT32(phy_status, lan9118_state), - VMSTATE_UINT32(phy_control, lan9118_state), - VMSTATE_UINT32(phy_advertise, lan9118_state), - VMSTATE_UINT32(phy_int, lan9118_state), - VMSTATE_UINT32(phy_int_mask, lan9118_state), VMSTATE_INT32(eeprom_writable, lan9118_state), VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128), VMSTATE_INT32(tx_fifo_size, lan9118_state), @@ -385,9 +370,11 @@ static void lan9118_reload_eeprom(lan9118_state *s) lan9118_mac_changed(s); } -static void phy_update_irq(lan9118_state *s) +static void lan9118_update_irq(void *opaque, int n, int level) { - if (s->phy_int & s->phy_int_mask) { + lan9118_state *s = opaque; + + if (level) { s->int_sts |= PHY_INT; } else { s->int_sts &= ~PHY_INT; @@ -395,33 +382,10 @@ static void phy_update_irq(lan9118_state *s) lan9118_update(s); } -static void phy_update_link(lan9118_state *s) -{ - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - phy_update_irq(s); -} - static void lan9118_set_link(NetClientState *nc) { - phy_update_link(qemu_get_nic_opaque(nc)); -} - -static void phy_reset(lan9118_state *s) -{ - s->phy_status = 0x7809; - s->phy_control = 0x3000; - s->phy_advertise = 0x01e1; - s->phy_int_mask = 0; - s->phy_int = 0; - phy_update_link(s); + lan9118_phy_update_link(&LAN9118(qemu_get_nic_opaque(nc))->mii, + nc->link_down); } static void lan9118_reset(DeviceState *d) @@ -478,8 +442,6 @@ static void lan9118_reset(DeviceState *d) s->read_word_n = 0; s->write_word_n = 0; - phy_reset(s); - s->eeprom_writable = 0; lan9118_reload_eeprom(s); } @@ -678,7 +640,7 @@ static void do_tx_packet(lan9118_state *s) uint32_t status; /* FIXME: Honor TX disable, and allow queueing of packets. */ - if (s->phy_control & 0x4000) { + if (s->mii.control & 0x4000) { /* This assumes the receive routine doesn't touch the VLANClient. */ qemu_receive_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } else { @@ -834,68 +796,6 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val) } } -static uint32_t do_phy_read(lan9118_state *s, int reg) -{ - uint32_t val; - - switch (reg) { - case 0: /* Basic Control */ - return s->phy_control; - case 1: /* Basic Status */ - return s->phy_status; - case 2: /* ID1 */ - return 0x0007; - case 3: /* ID2 */ - return 0xc0d1; - case 4: /* Auto-neg advertisement */ - return s->phy_advertise; - case 5: /* Auto-neg Link Partner Ability */ - return 0x0f71; - case 6: /* Auto-neg Expansion */ - return 1; - /* TODO 17, 18, 27, 29, 30, 31 */ - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - phy_update_irq(s); - return val; - case 30: /* Interrupt mask */ - return s->phy_int_mask; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "do_phy_read: PHY read reg %d\n", reg); - return 0; - } -} - -static void do_phy_write(lan9118_state *s, int reg, uint32_t val) -{ - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - phy_reset(s); - break; - } - s->phy_control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->phy_status |= 0x0020; - } - break; - case 4: /* Auto-neg advertisement */ - s->phy_advertise = (val & 0x2d7f) | 0x80; - break; - /* TODO 17, 18, 27, 31 */ - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - phy_update_irq(s); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val); - } -} - static void do_mac_write(lan9118_state *s, int reg, uint32_t val) { switch (reg) { @@ -929,9 +829,9 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val) if (val & 2) { DPRINTF("PHY write %d = 0x%04x\n", (val >> 6) & 0x1f, s->mac_mii_data); - do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data); + lan9118_phy_write(&s->mii, (val >> 6) & 0x1f, s->mac_mii_data); } else { - s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f); + s->mac_mii_data = lan9118_phy_read(&s->mii, (val >> 6) & 0x1f); DPRINTF("PHY read %d = 0x%04x\n", (val >> 6) & 0x1f, s->mac_mii_data); } @@ -1126,7 +1026,7 @@ static void lan9118_writel(void *opaque, hwaddr offset, break; case CSR_PMT_CTRL: if (val & 0x400) { - phy_reset(s); + lan9118_phy_reset(&s->mii); } s->pmt_ctrl &= ~0x34e; s->pmt_ctrl |= (val & 0x34e); @@ -1373,6 +1273,13 @@ static void lan9118_realize(DeviceState *dev, Error **errp) const MemoryRegionOps *mem_ops = s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops; + qemu_init_irq(&s->mii_irq, lan9118_update_irq, s, 0); + object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq); + memory_region_init_io(&s->mmio, OBJECT(dev), mem_ops, s, "lan9118-mmio", 0x100); sysbus_init_mmio(sbd, &s->mmio); diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c new file mode 100644 index 0000000000..b22c3c2855 --- /dev/null +++ b/hw/net/lan9118_phy.c @@ -0,0 +1,169 @@ +/* + * SMSC LAN9118 PHY emulation + * + * Copyright (c) 2009 CodeSourcery, LLC. + * Written by Paul Brook + * + * This code is licensed under the GNU GPL v2 + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "hw/net/lan9118_phy.h" +#include "hw/irq.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" +#include "qemu/log.h" + +#define PHY_INT_ENERGYON (1 << 7) +#define PHY_INT_AUTONEG_COMPLETE (1 << 6) +#define PHY_INT_FAULT (1 << 5) +#define PHY_INT_DOWN (1 << 4) +#define PHY_INT_AUTONEG_LP (1 << 3) +#define PHY_INT_PARFAULT (1 << 2) +#define PHY_INT_AUTONEG_PAGE (1 << 1) + +static void lan9118_phy_update_irq(Lan9118PhyState *s) +{ + qemu_set_irq(s->irq, !!(s->ints & s->int_mask)); +} + +uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg) +{ + uint16_t val; + + switch (reg) { + case 0: /* Basic Control */ + return s->control; + case 1: /* Basic Status */ + return s->status; + case 2: /* ID1 */ + return 0x0007; + case 3: /* ID2 */ + return 0xc0d1; + case 4: /* Auto-neg advertisement */ + return s->advertise; + case 5: /* Auto-neg Link Partner Ability */ + return 0x0f71; + case 6: /* Auto-neg Expansion */ + return 1; + /* TODO 17, 18, 27, 29, 30, 31 */ + case 29: /* Interrupt source. */ + val = s->ints; + s->ints = 0; + lan9118_phy_update_irq(s); + return val; + case 30: /* Interrupt mask */ + return s->int_mask; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118_phy_read: PHY read reg %d\n", reg); + return 0; + } +} + +void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val) +{ + switch (reg) { + case 0: /* Basic Control */ + if (val & 0x8000) { + lan9118_phy_reset(s); + break; + } + s->control = val & 0x7980; + /* Complete autonegotiation immediately. */ + if (val & 0x1000) { + s->status |= 0x0020; + } + break; + case 4: /* Auto-neg advertisement */ + s->advertise = (val & 0x2d7f) | 0x80; + break; + /* TODO 17, 18, 27, 31 */ + case 30: /* Interrupt mask */ + s->int_mask = val & 0xff; + lan9118_phy_update_irq(s); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118_phy_write: PHY write reg %d = 0x%04x\n", reg, val); + } +} + +void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down) +{ + s->link_down = link_down; + + /* Autonegotiation status mirrors link status. */ + if (link_down) { + s->status &= ~0x0024; + s->ints |= PHY_INT_DOWN; + } else { + s->status |= 0x0024; + s->ints |= PHY_INT_ENERGYON; + s->ints |= PHY_INT_AUTONEG_COMPLETE; + } + lan9118_phy_update_irq(s); +} + +void lan9118_phy_reset(Lan9118PhyState *s) +{ + s->control = 0x3000; + s->status = 0x7809; + s->advertise = 0x01e1; + s->int_mask = 0; + s->ints = 0; + lan9118_phy_update_link(s, s->link_down); +} + +static void lan9118_phy_reset_hold(Object *obj, ResetType type) +{ + Lan9118PhyState *s = LAN9118_PHY(obj); + + lan9118_phy_reset(s); +} + +static void lan9118_phy_init(Object *obj) +{ + Lan9118PhyState *s = LAN9118_PHY(obj); + + qdev_init_gpio_out(DEVICE(s), &s->irq, 1); +} + +static const VMStateDescription vmstate_lan9118_phy = { + .name = "lan9118-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16(control, Lan9118PhyState), + VMSTATE_UINT16(status, Lan9118PhyState), + VMSTATE_UINT16(advertise, Lan9118PhyState), + VMSTATE_UINT16(ints, Lan9118PhyState), + VMSTATE_UINT16(int_mask, Lan9118PhyState), + VMSTATE_BOOL(link_down, Lan9118PhyState), + VMSTATE_END_OF_LIST() + } +}; + +static void lan9118_phy_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + rc->phases.hold = lan9118_phy_reset_hold; + dc->vmsd = &vmstate_lan9118_phy; +} + +static const TypeInfo types[] = { + { + .name = TYPE_LAN9118_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Lan9118PhyState), + .instance_init = lan9118_phy_init, + .class_init = lan9118_phy_class_init, + } +}; + +DEFINE_TYPES(types) diff --git a/hw/net/meson.build b/hw/net/meson.build index 00a9e9dd51..3bb5d749a8 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -19,6 +19,7 @@ system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) system_ss.add(when: 'CONFIG_SMC91C111', if_true: files('smc91c111.c')) system_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) +system_ss.add(when: 'CONFIG_LAN9118_PHY', if_true: files('lan9118_phy.c')) system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) diff --git a/include/hw/net/lan9118_phy.h b/include/hw/net/lan9118_phy.h new file mode 100644 index 0000000000..af12fc33d5 --- /dev/null +++ b/include/hw/net/lan9118_phy.h @@ -0,0 +1,37 @@ +/* + * SMSC LAN9118 PHY emulation + * + * Copyright (c) 2009 CodeSourcery, LLC. + * Written by Paul Brook + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_NET_LAN9118_PHY_H +#define HW_NET_LAN9118_PHY_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +#define TYPE_LAN9118_PHY "lan9118-phy" +OBJECT_DECLARE_SIMPLE_TYPE(Lan9118PhyState, LAN9118_PHY) + +typedef struct Lan9118PhyState { + SysBusDevice parent_obj; + + uint16_t status; + uint16_t control; + uint16_t advertise; + uint16_t ints; + uint16_t int_mask; + qemu_irq irq; + bool link_down; +} Lan9118PhyState; + +void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down); +void lan9118_phy_reset(Lan9118PhyState *s); +uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg); +void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val); + +#endif From c01194e17a01b2a17805dfa0c710aad7c05eab69 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 11 Dec 2024 15:30:51 +0000 Subject: [PATCH 0103/2892] hw/net/lan9118_phy: Reuse in imx_fec and consolidate implementations imx_fec models the same PHY as lan9118_phy. The code is almost the same with imx_fec having more logging and tracing. Merge these improvements into lan9118_phy and reuse in imx_fec to fix the code duplication. Some migration state how resides in the new device model which breaks migration compatibility for the following machines: * imx25-pdk * sabrelite * mcimx7d-sabre * mcimx6ul-evk Signed-off-by: Bernhard Beschow Tested-by: Guenter Roeck Reviewed-by: Peter Maydell Message-id: 20241102125724.532843-3-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/Kconfig | 1 + hw/net/imx_fec.c | 146 ++++----------------------------------- hw/net/lan9118_phy.c | 82 ++++++++++++++++------ hw/net/trace-events | 10 +-- include/hw/net/imx_fec.h | 9 ++- 5 files changed, 85 insertions(+), 163 deletions(-) diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 6b2ff2f937..7f80218d10 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -93,6 +93,7 @@ config ALLWINNER_SUN8I_EMAC config IMX_FEC bool + select LAN9118_PHY config CADENCE bool diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 6294d29202..4ee6f74206 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -203,17 +203,12 @@ static const VMStateDescription vmstate_imx_eth_txdescs = { static const VMStateDescription vmstate_imx_eth = { .name = TYPE_IMX_FEC, - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), VMSTATE_UINT32(rx_descriptor, IMXFECState), VMSTATE_UINT32(tx_descriptor[0], IMXFECState), - VMSTATE_UINT32(phy_status, IMXFECState), - VMSTATE_UINT32(phy_control, IMXFECState), - VMSTATE_UINT32(phy_advertise, IMXFECState), - VMSTATE_UINT32(phy_int, IMXFECState), - VMSTATE_UINT32(phy_int_mask, IMXFECState), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { @@ -222,14 +217,6 @@ static const VMStateDescription vmstate_imx_eth = { }, }; -#define PHY_INT_ENERGYON (1 << 7) -#define PHY_INT_AUTONEG_COMPLETE (1 << 6) -#define PHY_INT_FAULT (1 << 5) -#define PHY_INT_DOWN (1 << 4) -#define PHY_INT_AUTONEG_LP (1 << 3) -#define PHY_INT_PARFAULT (1 << 2) -#define PHY_INT_AUTONEG_PAGE (1 << 1) - static void imx_eth_update(IMXFECState *s); /* @@ -238,47 +225,19 @@ static void imx_eth_update(IMXFECState *s); * For now we don't handle any GPIO/interrupt line, so the OS will * have to poll for the PHY status. */ -static void imx_phy_update_irq(IMXFECState *s) +static void imx_phy_update_irq(void *opaque, int n, int level) { - imx_eth_update(s); -} - -static void imx_phy_update_link(IMXFECState *s) -{ - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - trace_imx_phy_update_link("down"); - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - trace_imx_phy_update_link("up"); - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - imx_phy_update_irq(s); + imx_eth_update(opaque); } static void imx_eth_set_link(NetClientState *nc) { - imx_phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); -} - -static void imx_phy_reset(IMXFECState *s) -{ - trace_imx_phy_reset(); - - s->phy_status = 0x7809; - s->phy_control = 0x3000; - s->phy_advertise = 0x01e1; - s->phy_int_mask = 0; - s->phy_int = 0; - imx_phy_update_link(s); + lan9118_phy_update_link(&IMX_FEC(qemu_get_nic_opaque(nc))->mii, + nc->link_down); } static uint32_t imx_phy_read(IMXFECState *s, int reg) { - uint32_t val; uint32_t phy = reg / 32; if (!s->phy_connected) { @@ -296,54 +255,7 @@ static uint32_t imx_phy_read(IMXFECState *s, int reg) reg %= 32; - switch (reg) { - case 0: /* Basic Control */ - val = s->phy_control; - break; - case 1: /* Basic Status */ - val = s->phy_status; - break; - case 2: /* ID1 */ - val = 0x0007; - break; - case 3: /* ID2 */ - val = 0xc0d1; - break; - case 4: /* Auto-neg advertisement */ - val = s->phy_advertise; - break; - case 5: /* Auto-neg Link Partner Ability */ - val = 0x0f71; - break; - case 6: /* Auto-neg Expansion */ - val = 1; - break; - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - imx_phy_update_irq(s); - break; - case 30: /* Interrupt mask */ - val = s->phy_int_mask; - break; - case 17: - case 18: - case 27: - case 31: - qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", - TYPE_IMX_FEC, __func__, reg); - val = 0; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, reg); - val = 0; - break; - } - - trace_imx_phy_read(val, phy, reg); - - return val; + return lan9118_phy_read(&s->mii, reg); } static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) @@ -365,39 +277,7 @@ static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) reg %= 32; - trace_imx_phy_write(val, phy, reg); - - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - imx_phy_reset(s); - } else { - s->phy_control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->phy_status |= 0x0020; - } - } - break; - case 4: /* Auto-neg advertisement */ - s->phy_advertise = (val & 0x2d7f) | 0x80; - break; - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - imx_phy_update_irq(s); - break; - case 17: - case 18: - case 27: - case 31: - qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", - TYPE_IMX_FEC, __func__, reg); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, reg); - break; - } + lan9118_phy_write(&s->mii, reg, val); } static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) @@ -682,9 +562,6 @@ static void imx_eth_reset(DeviceState *d) s->rx_descriptor = 0; memset(s->tx_descriptor, 0, sizeof(s->tx_descriptor)); - - /* We also reset the PHY */ - imx_phy_reset(s); } static uint32_t imx_default_read(IMXFECState *s, uint32_t index) @@ -1329,6 +1206,13 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq[0]); sysbus_init_irq(sbd, &s->irq[1]); + qemu_init_irq(&s->mii_irq, imx_phy_update_irq, s, 0); + object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c index b22c3c2855..d2dcd732ac 100644 --- a/hw/net/lan9118_phy.c +++ b/hw/net/lan9118_phy.c @@ -4,6 +4,8 @@ * Copyright (c) 2009 CodeSourcery, LLC. * Written by Paul Brook * + * Copyright (c) 2013 Jean-Christophe Dubois. + * * This code is licensed under the GNU GPL v2 * * Contributions after 2012-01-13 are licensed under the terms of the @@ -16,6 +18,7 @@ #include "hw/resettable.h" #include "migration/vmstate.h" #include "qemu/log.h" +#include "trace.h" #define PHY_INT_ENERGYON (1 << 7) #define PHY_INT_AUTONEG_COMPLETE (1 << 6) @@ -36,59 +39,88 @@ uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg) switch (reg) { case 0: /* Basic Control */ - return s->control; + val = s->control; + break; case 1: /* Basic Status */ - return s->status; + val = s->status; + break; case 2: /* ID1 */ - return 0x0007; + val = 0x0007; + break; case 3: /* ID2 */ - return 0xc0d1; + val = 0xc0d1; + break; case 4: /* Auto-neg advertisement */ - return s->advertise; + val = s->advertise; + break; case 5: /* Auto-neg Link Partner Ability */ - return 0x0f71; + val = 0x0f71; + break; case 6: /* Auto-neg Expansion */ - return 1; - /* TODO 17, 18, 27, 29, 30, 31 */ + val = 1; + break; case 29: /* Interrupt source. */ val = s->ints; s->ints = 0; lan9118_phy_update_irq(s); - return val; + break; case 30: /* Interrupt mask */ - return s->int_mask; + val = s->int_mask; + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + val = 0; + break; default: - qemu_log_mask(LOG_GUEST_ERROR, - "lan9118_phy_read: PHY read reg %d\n", reg); - return 0; + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + val = 0; + break; } + + trace_lan9118_phy_read(val, reg); + + return val; } void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val) { + trace_lan9118_phy_write(val, reg); + switch (reg) { case 0: /* Basic Control */ if (val & 0x8000) { lan9118_phy_reset(s); - break; - } - s->control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->status |= 0x0020; + } else { + s->control = val & 0x7980; + /* Complete autonegotiation immediately. */ + if (val & 0x1000) { + s->status |= 0x0020; + } } break; case 4: /* Auto-neg advertisement */ s->advertise = (val & 0x2d7f) | 0x80; break; - /* TODO 17, 18, 27, 31 */ case 30: /* Interrupt mask */ s->int_mask = val & 0xff; lan9118_phy_update_irq(s); break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + break; default: - qemu_log_mask(LOG_GUEST_ERROR, - "lan9118_phy_write: PHY write reg %d = 0x%04x\n", reg, val); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + break; } } @@ -98,9 +130,11 @@ void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down) /* Autonegotiation status mirrors link status. */ if (link_down) { + trace_lan9118_phy_update_link("down"); s->status &= ~0x0024; s->ints |= PHY_INT_DOWN; } else { + trace_lan9118_phy_update_link("up"); s->status |= 0x0024; s->ints |= PHY_INT_ENERGYON; s->ints |= PHY_INT_AUTONEG_COMPLETE; @@ -110,6 +144,8 @@ void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down) void lan9118_phy_reset(Lan9118PhyState *s) { + trace_lan9118_phy_reset(); + s->control = 0x3000; s->status = 0x7809; s->advertise = 0x01e1; @@ -137,8 +173,8 @@ static const VMStateDescription vmstate_lan9118_phy = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT16(control, Lan9118PhyState), VMSTATE_UINT16(status, Lan9118PhyState), + VMSTATE_UINT16(control, Lan9118PhyState), VMSTATE_UINT16(advertise, Lan9118PhyState), VMSTATE_UINT16(ints, Lan9118PhyState), VMSTATE_UINT16(int_mask, Lan9118PhyState), diff --git a/hw/net/trace-events b/hw/net/trace-events index d0f1d8c0fb..6100ec324a 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -10,6 +10,12 @@ allwinner_sun8i_emac_set_link(bool active) "Set link: active=%u" allwinner_sun8i_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64 allwinner_sun8i_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64 +# lan9118_phy.c +lan9118_phy_read(uint16_t val, int reg) "[0x%02x] -> 0x%04" PRIx16 +lan9118_phy_write(uint16_t val, int reg) "[0x%02x] <- 0x%04" PRIx16 +lan9118_phy_update_link(const char *s) "%s" +lan9118_phy_reset(void) "" + # lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" @@ -428,12 +434,8 @@ i82596_set_multicast(uint16_t count) "Added %d multicast entries" i82596_channel_attention(void *s) "%p: Received CHANNEL ATTENTION" # imx_fec.c -imx_phy_read(uint32_t val, int phy, int reg) "0x%04"PRIx32" <= phy[%d].reg[%d]" imx_phy_read_num(int phy, int configured) "read request from unconfigured phy %d (configured %d)" -imx_phy_write(uint32_t val, int phy, int reg) "0x%04"PRIx32" => phy[%d].reg[%d]" imx_phy_write_num(int phy, int configured) "write request to unconfigured phy %d (configured %d)" -imx_phy_update_link(const char *s) "%s" -imx_phy_reset(void) "" imx_fec_read_bd(uint64_t addr, int flags, int len, int data) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x" imx_enet_read_bd(uint64_t addr, int flags, int len, int data, int options, int status) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x option 0x%04x status 0x%04x" imx_eth_tx_bd_busy(void) "tx_bd ran out of descriptors to transmit" diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h index 2d13290c78..83b21637ee 100644 --- a/include/hw/net/imx_fec.h +++ b/include/hw/net/imx_fec.h @@ -31,6 +31,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IMXFECState, IMX_FEC) #define TYPE_IMX_ENET "imx.enet" #include "hw/sysbus.h" +#include "hw/net/lan9118_phy.h" +#include "hw/irq.h" #include "net/net.h" #define ENET_EIR 1 @@ -264,11 +266,8 @@ struct IMXFECState { uint32_t tx_descriptor[ENET_TX_RING_NUM]; uint32_t tx_ring_num; - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; + Lan9118PhyState mii; + IRQState mii_irq; uint32_t phy_num; bool phy_connected; struct IMXFECState *phy_consumer; From bbaaee8cc665bbfea1a77f7940b5bb7e02d2a7ef Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 11 Dec 2024 15:30:52 +0000 Subject: [PATCH 0104/2892] hw/net/lan9118_phy: Fix off-by-one error in MII_ANLPAR register Turns 0x70 into 0xe0 (== 0x70 << 1) which adds the missing MII_ANLPAR_TX and fixes the MSB of selector field to be zero, as specified in the datasheet. Fixes: 2a424990170b "LAN9118 emulation" Signed-off-by: Bernhard Beschow Tested-by: Guenter Roeck Reviewed-by: Peter Maydell Message-id: 20241102125724.532843-4-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/lan9118_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c index d2dcd732ac..d8fc9ddd3a 100644 --- a/hw/net/lan9118_phy.c +++ b/hw/net/lan9118_phy.c @@ -54,7 +54,7 @@ uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg) val = s->advertise; break; case 5: /* Auto-neg Link Partner Ability */ - val = 0x0f71; + val = 0x0fe1; break; case 6: /* Auto-neg Expansion */ val = 1; From 212a52c8f2e4ce13251ed998db57af22b34e7e6f Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 11 Dec 2024 15:30:52 +0000 Subject: [PATCH 0105/2892] hw/net/lan9118_phy: Reuse MII constants Prefer named constants over magic values for better readability. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Tested-by: Guenter Roeck Message-id: 20241102125724.532843-5-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/lan9118_phy.c | 63 ++++++++++++++++++++++++++++---------------- include/hw/net/mii.h | 6 +++++ 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c index d8fc9ddd3a..874dae4155 100644 --- a/hw/net/lan9118_phy.c +++ b/hw/net/lan9118_phy.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "hw/net/lan9118_phy.h" +#include "hw/net/mii.h" #include "hw/irq.h" #include "hw/resettable.h" #include "migration/vmstate.h" @@ -38,26 +39,28 @@ uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg) uint16_t val; switch (reg) { - case 0: /* Basic Control */ + case MII_BMCR: val = s->control; break; - case 1: /* Basic Status */ + case MII_BMSR: val = s->status; break; - case 2: /* ID1 */ - val = 0x0007; + case MII_PHYID1: + val = SMSCLAN9118_PHYID1; break; - case 3: /* ID2 */ - val = 0xc0d1; + case MII_PHYID2: + val = SMSCLAN9118_PHYID2; break; - case 4: /* Auto-neg advertisement */ + case MII_ANAR: val = s->advertise; break; - case 5: /* Auto-neg Link Partner Ability */ - val = 0x0fe1; + case MII_ANLPAR: + val = MII_ANLPAR_PAUSEASY | MII_ANLPAR_PAUSE | MII_ANLPAR_T4 | + MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD | + MII_ANLPAR_10 | MII_ANLPAR_CSMACD; break; - case 6: /* Auto-neg Expansion */ - val = 1; + case MII_ANER: + val = MII_ANER_NWAY; break; case 29: /* Interrupt source. */ val = s->ints; @@ -92,19 +95,24 @@ void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val) trace_lan9118_phy_write(val, reg); switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { + case MII_BMCR: + if (val & MII_BMCR_RESET) { lan9118_phy_reset(s); } else { - s->control = val & 0x7980; + s->control = val & (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 | + MII_BMCR_AUTOEN | MII_BMCR_PDOWN | MII_BMCR_FD | + MII_BMCR_CTST); /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->status |= 0x0020; + if (val & MII_BMCR_AUTOEN) { + s->status |= MII_BMSR_AN_COMP; } } break; - case 4: /* Auto-neg advertisement */ - s->advertise = (val & 0x2d7f) | 0x80; + case MII_ANAR: + s->advertise = (val & (MII_ANAR_RFAULT | MII_ANAR_PAUSE_ASYM | + MII_ANAR_PAUSE | MII_ANAR_10FD | MII_ANAR_10 | + MII_ANAR_SELECT)) + | MII_ANAR_TX; break; case 30: /* Interrupt mask */ s->int_mask = val & 0xff; @@ -131,11 +139,11 @@ void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down) /* Autonegotiation status mirrors link status. */ if (link_down) { trace_lan9118_phy_update_link("down"); - s->status &= ~0x0024; + s->status &= ~(MII_BMSR_AN_COMP | MII_BMSR_LINK_ST); s->ints |= PHY_INT_DOWN; } else { trace_lan9118_phy_update_link("up"); - s->status |= 0x0024; + s->status |= MII_BMSR_AN_COMP | MII_BMSR_LINK_ST; s->ints |= PHY_INT_ENERGYON; s->ints |= PHY_INT_AUTONEG_COMPLETE; } @@ -146,9 +154,18 @@ void lan9118_phy_reset(Lan9118PhyState *s) { trace_lan9118_phy_reset(); - s->control = 0x3000; - s->status = 0x7809; - s->advertise = 0x01e1; + s->control = MII_BMCR_AUTOEN | MII_BMCR_SPEED100; + s->status = MII_BMSR_100TX_FD + | MII_BMSR_100TX_HD + | MII_BMSR_10T_FD + | MII_BMSR_10T_HD + | MII_BMSR_AUTONEG + | MII_BMSR_EXTCAP; + s->advertise = MII_ANAR_TXFD + | MII_ANAR_TX + | MII_ANAR_10FD + | MII_ANAR_10 + | MII_ANAR_CSMACD; s->int_mask = 0; s->ints = 0; lan9118_phy_update_link(s, s->link_down); diff --git a/include/hw/net/mii.h b/include/hw/net/mii.h index f7feddac9b..55bf7c92a1 100644 --- a/include/hw/net/mii.h +++ b/include/hw/net/mii.h @@ -71,6 +71,7 @@ #define MII_BMSR_JABBER (1 << 1) /* Jabber detected */ #define MII_BMSR_EXTCAP (1 << 0) /* Ext-reg capability */ +#define MII_ANAR_RFAULT (1 << 13) /* Say we can detect faults */ #define MII_ANAR_PAUSE_ASYM (1 << 11) /* Try for asymmetric pause */ #define MII_ANAR_PAUSE (1 << 10) /* Try for pause */ #define MII_ANAR_TXFD (1 << 8) @@ -78,6 +79,7 @@ #define MII_ANAR_10FD (1 << 6) #define MII_ANAR_10 (1 << 5) #define MII_ANAR_CSMACD (1 << 0) +#define MII_ANAR_SELECT (0x001f) /* Selector bits */ #define MII_ANLPAR_ACK (1 << 14) #define MII_ANLPAR_PAUSEASY (1 << 11) /* can pause asymmetrically */ @@ -112,6 +114,10 @@ #define RTL8201CP_PHYID1 0x0000 #define RTL8201CP_PHYID2 0x8201 +/* SMSC LAN9118 */ +#define SMSCLAN9118_PHYID1 0x0007 +#define SMSCLAN9118_PHYID2 0xc0d1 + /* RealTek 8211E */ #define RTL8211E_PHYID1 0x001c #define RTL8211E_PHYID2 0xc915 From 973a2fac48590d9b4e6f1601a13c7f96a1fd5deb Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 11 Dec 2024 15:30:52 +0000 Subject: [PATCH 0106/2892] hw/net/lan9118_phy: Add missing 100 mbps full duplex advertisement The real device advertises this mode and the device model already advertises 100 mbps half duplex and 10 mbps full+half duplex. So advertise this mode to make the model more realistic. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Tested-by: Guenter Roeck Message-id: 20241102125724.532843-6-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/lan9118_phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c index 874dae4155..5c53a4a1e3 100644 --- a/hw/net/lan9118_phy.c +++ b/hw/net/lan9118_phy.c @@ -110,8 +110,8 @@ void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val) break; case MII_ANAR: s->advertise = (val & (MII_ANAR_RFAULT | MII_ANAR_PAUSE_ASYM | - MII_ANAR_PAUSE | MII_ANAR_10FD | MII_ANAR_10 | - MII_ANAR_SELECT)) + MII_ANAR_PAUSE | MII_ANAR_TXFD | MII_ANAR_10FD | + MII_ANAR_10 | MII_ANAR_SELECT)) | MII_ANAR_TX; break; case 30: /* Interrupt mask */ From 8adcff4ae760a960c8b44c9d3da0428322e3f677 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:52 +0000 Subject: [PATCH 0107/2892] fpu: handle raising Invalid for infzero in pick_nan_muladd For IEEE fused multiply-add, the (0 * inf) + NaN case should raise Invalid for the multiplication of 0 by infinity. Currently we handle this in the per-architecture ifdef ladder in pickNaNMulAdd(). However, since this isn't really architecture specific we can hoist it up to the generic code. For the cases where the infzero test in pickNaNMulAdd was returning 2, we can delete the check entirely and allow the code to fall into the normal pick-a-NaN handling, because this will return 2 anyway (input 'c' being the only NaN in this case). For the cases where infzero was returning 3 to indicate "return the default NaN", we must retain that "return 3". For Arm, this looks like it might be a behaviour change because we used to set float_flag_invalid | float_flag_invalid_imz only if C is a quiet NaN. However, it is not, because Arm target code never looks at float_flag_invalid_imz, and for the (0 * inf) + SNaN case we already raised float_flag_invalid via the "abc_mask & float_cmask_snan" check in pick_nan_muladd. For any target architecture using the "default implementation" at the bottom of the ifdef, this is a behaviour change but will be fixing a bug (where we failed to raise the Invalid exception for (0 * inf + QNaN). The architectures using the default case are: * hppa * i386 * sh4 * tricore The x86, Tricore and SH4 CPU architecture manuals are clear that this should have raised Invalid; HPPA is a bit vaguer but still seems clear enough. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-2-peter.maydell@linaro.org --- fpu/softfloat-parts.c.inc | 13 +++++++------ fpu/softfloat-specialize.c.inc | 29 +---------------------------- 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index cc6e06b976..d63cd957a1 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -66,19 +66,20 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, int ab_mask, int abc_mask) { int which; + bool infzero = (ab_mask == float_cmask_infzero); if (unlikely(abc_mask & float_cmask_snan)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); } - which = pickNaNMulAdd(a->cls, b->cls, c->cls, - ab_mask == float_cmask_infzero, s); + if (infzero) { + /* This is (0 * inf) + NaN or (inf * 0) + NaN */ + float_raise(float_flag_invalid | float_flag_invalid_imz, s); + } + + which = pickNaNMulAdd(a->cls, b->cls, c->cls, infzero, s); if (s->default_nan_mode || which == 3) { - /* - * Note that this check is after pickNaNMulAdd so that function - * has an opportunity to set the Invalid flag for infzero. - */ parts_default_nan(a, s); return a; } diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 9bca03c4ae..c557c41b2a 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -480,7 +480,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * the default NaN */ if (infzero && is_qnan(c_cls)) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 3; } @@ -507,7 +506,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * case sets InvalidOp and returns the default NaN */ if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 3; } /* Prefer sNaN over qNaN, in the a, b, c order. */ @@ -529,10 +527,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) * case sets InvalidOp and returns the input value 'c' */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } /* Prefer sNaN over qNaN, in the c, a, b order. */ if (is_snan(c_cls)) { return 2; @@ -553,10 +547,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) * case sets InvalidOp and returns the input value 'c' */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } + /* Prefer sNaN over qNaN, in the c, a, b order. */ if (is_snan(c_cls)) { return 2; @@ -576,10 +567,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * to return an input NaN if we have one (ie c) rather than generating * a default NaN */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB @@ -592,14 +579,9 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 1; } #elif defined(TARGET_RISCV) - /* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - } return 3; /* default NaN */ #elif defined(TARGET_S390X) if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 3; } @@ -617,11 +599,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 2; } #elif defined(TARGET_SPARC) - /* For (inf,0,nan) return c. */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } /* Prefer SNaN over QNaN, order C, B, A. */ if (is_snan(c_cls)) { return 2; @@ -641,10 +618,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns * an input NaN if we have one (ie c). */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } if (status->use_first_nan) { if (is_nan(a_cls)) { return 0; From ed885e306900e54a618ac9cc11ab68683db472b1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:53 +0000 Subject: [PATCH 0108/2892] fpu: Check for default_nan_mode before calling pickNaNMulAdd If the target sets default_nan_mode then we're always going to return the default NaN, and pickNaNMulAdd() no longer has any side effects. For consistency with pickNaN(), check for default_nan_mode before calling pickNaNMulAdd(). When we convert pickNaNMulAdd() to allow runtime selection of the NaN propagation rule, this means we won't have to make the targets which use default_nan_mode also set a propagation rule. Since RiscV always uses default_nan_mode, this allows us to remove its ifdef case from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-3-peter.maydell@linaro.org --- fpu/softfloat-parts.c.inc | 8 ++++++-- fpu/softfloat-specialize.c.inc | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index d63cd957a1..aac1f9cd28 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -77,9 +77,13 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, float_raise(float_flag_invalid | float_flag_invalid_imz, s); } - which = pickNaNMulAdd(a->cls, b->cls, c->cls, infzero, s); + if (s->default_nan_mode) { + which = 3; + } else { + which = pickNaNMulAdd(a->cls, b->cls, c->cls, infzero, s); + } - if (s->default_nan_mode || which == 3) { + if (which == 3) { parts_default_nan(a, s); return a; } diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index c557c41b2a..81a67eb67b 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -475,6 +475,13 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, bool infzero, float_status *status) { + /* + * We guarantee not to require the target to tell us how to + * pick a NaN if we're always returning the default NaN. + * But if we're not in default-NaN mode then the target must + * specify. + */ + assert(!status->default_nan_mode); #if defined(TARGET_ARM) /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN @@ -578,8 +585,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } else { return 1; } -#elif defined(TARGET_RISCV) - return 3; /* default NaN */ #elif defined(TARGET_S390X) if (infzero) { return 3; From 4080eebd7357261dce0993e17ec6b6e9efa29581 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:53 +0000 Subject: [PATCH 0109/2892] softfloat: Allow runtime choice of inf * 0 + NaN result IEEE 758 does not define a fixed rule for what NaN to return in the case of a fused multiply-add of inf * 0 + NaN. Different architectures thus do different things: * some return the default NaN * some return the input NaN * Arm returns the default NaN if the input NaN is quiet, and the input NaN if it is signalling We want to make this logic be runtime selected rather than hardcoded into the binary, because: * this will let us have multiple targets in one QEMU binary * the Arm FEAT_AFP architectural feature includes letting the guest select a NaN propagation rule at runtime In this commit we add an enum for the propagation rule, the field in float_status, and the corresponding getters and setters. We change pickNaNMulAdd to honour this, but because all targets still leave this field at its default 0 value, the fallback logic will pick the rule type with the old ifdef ladder. Note that four architectures both use the muladd softfloat functions and did not have a branch of the ifdef ladder to specify their behaviour (and so were ending up with the "default" case, probably wrongly): i386, HPPA, SH4 and Tricore. SH4 and Tricore both set default_nan_mode, and so will never get into pickNaNMulAdd(). For HPPA and i386 we retain the same behaviour as the old default-case, which is to not ever return the default NaN. This might not be correct but it is not a behaviour change. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-4-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 91 ++++++++++++++++++++++----------- include/fpu/softfloat-helpers.h | 11 ++++ include/fpu/softfloat-types.h | 23 +++++++++ 3 files changed, 95 insertions(+), 30 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 81a67eb67b..f5b422e07b 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -475,6 +475,8 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, bool infzero, float_status *status) { + FloatInfZeroNaNRule rule = status->float_infzeronan_rule; + /* * We guarantee not to require the target to tell us how to * pick a NaN if we're always returning the default NaN. @@ -482,14 +484,68 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * specify. */ assert(!status->default_nan_mode); + + if (rule == float_infzeronan_none) { + /* + * Temporarily fall back to ifdef ladder + */ #if defined(TARGET_ARM) - /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns - * the default NaN - */ - if (infzero && is_qnan(c_cls)) { - return 3; + /* + * For ARM, the (inf,zero,qnan) case returns the default NaN, + * but (inf,zero,snan) returns the input NaN. + */ + rule = float_infzeronan_dnan_if_qnan; +#elif defined(TARGET_MIPS) + if (snan_bit_is_one(status)) { + /* + * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) + * case sets InvalidOp and returns the default NaN + */ + rule = float_infzeronan_dnan_always; + } else { + /* + * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + rule = float_infzeronan_dnan_never; + } +#elif defined(TARGET_PPC) || defined(TARGET_SPARC) || \ + defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \ + defined(TARGET_I386) || defined(TARGET_LOONGARCH) + /* + * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + /* + * For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer + * to return an input NaN if we have one (ie c) rather than generating + * a default NaN + */ + rule = float_infzeronan_dnan_never; +#elif defined(TARGET_S390X) + rule = float_infzeronan_dnan_always; +#endif } + if (infzero) { + /* + * Inf * 0 + NaN -- some implementations return the default NaN here, + * and some return the input NaN. + */ + switch (rule) { + case float_infzeronan_dnan_never: + return 2; + case float_infzeronan_dnan_always: + return 3; + case float_infzeronan_dnan_if_qnan: + return is_qnan(c_cls) ? 3 : 2; + default: + g_assert_not_reached(); + } + } + +#if defined(TARGET_ARM) + /* This looks different from the ARM ARM pseudocode, because the ARM ARM * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. */ @@ -508,13 +564,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } #elif defined(TARGET_MIPS) if (snan_bit_is_one(status)) { - /* - * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) - * case sets InvalidOp and returns the default NaN - */ - if (infzero) { - return 3; - } /* Prefer sNaN over qNaN, in the a, b, c order. */ if (is_snan(a_cls)) { return 0; @@ -530,10 +579,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 2; } } else { - /* - * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ /* Prefer sNaN over qNaN, in the c, a, b order. */ if (is_snan(c_cls)) { return 2; @@ -550,11 +595,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } } #elif defined(TARGET_LOONGARCH64) - /* - * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ - /* Prefer sNaN over qNaN, in the c, a, b order. */ if (is_snan(c_cls)) { return 2; @@ -570,11 +610,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 1; } #elif defined(TARGET_PPC) - /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer - * to return an input NaN if we have one (ie c) rather than generating - * a default NaN - */ - /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB */ @@ -586,10 +621,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 1; } #elif defined(TARGET_S390X) - if (infzero) { - return 3; - } - if (is_snan(a_cls)) { return 0; } else if (is_snan(b_cls)) { diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 453188de70..0bf44dc608 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -81,6 +81,12 @@ static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, status->float_2nan_prop_rule = rule; } +static inline void set_float_infzeronan_rule(FloatInfZeroNaNRule rule, + float_status *status) +{ + status->float_infzeronan_rule = rule; +} + static inline void set_flush_to_zero(bool val, float_status *status) { status->flush_to_zero = val; @@ -137,6 +143,11 @@ static inline Float2NaNPropRule get_float_2nan_prop_rule(float_status *status) return status->float_2nan_prop_rule; } +static inline FloatInfZeroNaNRule get_float_infzeronan_rule(float_status *status) +{ + return status->float_infzeronan_rule; +} + static inline bool get_flush_to_zero(float_status *status) { return status->flush_to_zero; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 8f39691dfd..47bb22c4e2 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -207,6 +207,28 @@ typedef enum __attribute__((__packed__)) { float_2nan_prop_x87, } Float2NaNPropRule; +/* + * Rule for result of fused multiply-add 0 * Inf + NaN. + * This must be a NaN, but implementations differ on whether this + * is the input NaN or the default NaN. + * + * You don't need to set this if default_nan_mode is enabled. + * When not in default-NaN mode, it is an error for the target + * not to set the rule in float_status if it uses muladd, and we + * will assert if we need to handle an input NaN and no rule was + * selected. + */ +typedef enum __attribute__((__packed__)) { + /* No propagation rule specified */ + float_infzeronan_none = 0, + /* Result is never the default NaN (so always the input NaN) */ + float_infzeronan_dnan_never, + /* Result is always the default NaN */ + float_infzeronan_dnan_always, + /* Result is the default NaN if the input NaN is quiet */ + float_infzeronan_dnan_if_qnan, +} FloatInfZeroNaNRule; + /* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The @@ -219,6 +241,7 @@ typedef struct float_status { FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; Float2NaNPropRule float_2nan_prop_rule; + FloatInfZeroNaNRule float_infzeronan_rule; bool tininess_before_rounding; /* should denormalised results go to zero and set the inexact flag? */ bool flush_to_zero; From 27aedf7d25e148089bb54eb80588bd380483cbe5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:53 +0000 Subject: [PATCH 0110/2892] tests/fp: Explicitly set inf-zero-nan rule Explicitly set a rule in the softfloat tests for the inf-zero-nan muladd special case. In meson.build we put -DTARGET_ARM in fpcflags, and so we should select here the Arm rule of float_infzeronan_dnan_if_qnan. Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Message-id: 20241202131347.498124-5-peter.maydell@linaro.org --- tests/fp/fp-bench.c | 5 +++++ tests/fp/fp-test.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index 75c07d5d1f..fde6483619 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -488,7 +488,12 @@ static void run_bench(void) { bench_func_t f; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); f = bench_funcs[operation][precision]; g_assert(f); diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 5f6f25c882..251c278ede 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -935,7 +935,12 @@ void run_test(void) { unsigned int i; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &qsf); genCases_setLevel(test_level); verCases_maxErrorCount = n_max_errors; From f7892f9c0094c911e5f3ad03072130caf9a1db52 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:53 +0000 Subject: [PATCH 0111/2892] target/arm: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the Arm target, so we can remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-6-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 8 +------- target/arm/cpu.c | 3 +++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index f5b422e07b..b3ffa54f36 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -489,13 +489,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, /* * Temporarily fall back to ifdef ladder */ -#if defined(TARGET_ARM) - /* - * For ARM, the (inf,zero,qnan) case returns the default NaN, - * but (inf,zero,snan) returns the input NaN. - */ - rule = float_infzeronan_dnan_if_qnan; -#elif defined(TARGET_MIPS) +#if defined(TARGET_MIPS) if (snan_bit_is_one(status)) { /* * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6938161b95..ead3979398 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -173,11 +173,14 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, * * tininess-before-rounding * * 2-input NaN propagation prefers SNaN over QNaN, and then * operand A over operand B (see FPProcessNaNs() pseudocode) + * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, + * and the input NaN if it is signalling */ static void arm_set_default_fp_behaviours(float_status *s) { set_float_detect_tininess(float_tininess_before_rounding, s); set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); } static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) From e494fe4909fd0e0d61b32af7a86050191187b60d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:54 +0000 Subject: [PATCH 0112/2892] target/s390: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for s390, so we can remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-7-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 -- target/s390x/cpu.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index b3ffa54f36..db914ddbb1 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -516,8 +516,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * a default NaN */ rule = float_infzeronan_dnan_never; -#elif defined(TARGET_S390X) - rule = float_infzeronan_dnan_always; #endif } diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 514c70f301..d5941b5b9d 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -206,6 +206,8 @@ static void s390_cpu_reset_hold(Object *obj, ResetType type) set_float_detect_tininess(float_tininess_before_rounding, &env->fpu_status); set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fpu_status); + set_float_infzeronan_rule(float_infzeronan_dnan_always, + &env->fpu_status); /* fall through */ case RESET_TYPE_S390_CPU_NORMAL: env->psw.mask &= ~PSW_MASK_RI; From 6f759b179fd72a90fed129721dc28e04fa79d7c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:54 +0000 Subject: [PATCH 0113/2892] target/ppc: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the PPC target, so we can remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-8-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 7 +------ target/ppc/cpu_init.c | 7 +++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index db914ddbb1..2023b2bd63 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -503,18 +503,13 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, */ rule = float_infzeronan_dnan_never; } -#elif defined(TARGET_PPC) || defined(TARGET_SPARC) || \ +#elif defined(TARGET_SPARC) || \ defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \ defined(TARGET_I386) || defined(TARGET_LOONGARCH) /* * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) * case sets InvalidOp and returns the input value 'c' */ - /* - * For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer - * to return an input NaN if we have one (ie c) rather than generating - * a default NaN - */ rule = float_infzeronan_dnan_never; #endif } diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index efcb80d1c2..f18908a643 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7270,6 +7270,13 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) */ set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); set_float_2nan_prop_rule(float_2nan_prop_ab, &env->vec_status); + /* + * For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer + * to return an input NaN if we have one (ie c) rather than generating + * a default NaN + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->vec_status); for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; From a71492f7267490e3cc238fa00662d29a0762fd3a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:54 +0000 Subject: [PATCH 0114/2892] target/mips: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the MIPS target, so we can remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-9-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 16 +--------------- target/mips/fpu_helper.h | 9 +++++++++ target/mips/msa.c | 4 ++++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 2023b2bd63..db9a466e05 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -489,21 +489,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, /* * Temporarily fall back to ifdef ladder */ -#if defined(TARGET_MIPS) - if (snan_bit_is_one(status)) { - /* - * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) - * case sets InvalidOp and returns the default NaN - */ - rule = float_infzeronan_dnan_always; - } else { - /* - * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ - rule = float_infzeronan_dnan_never; - } -#elif defined(TARGET_SPARC) || \ +#if defined(TARGET_SPARC) || \ defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \ defined(TARGET_I386) || defined(TARGET_LOONGARCH) /* diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index 7c3c7897b4..be66f2f813 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -28,6 +28,7 @@ static inline void restore_flush_mode(CPUMIPSState *env) static inline void restore_snan_bit_mode(CPUMIPSState *env) { bool nan2008 = env->active_fpu.fcr31 & (1 << FCR31_NAN2008); + FloatInfZeroNaNRule izn_rule; /* * With nan2008, SNaNs are silenced in the usual way. @@ -35,6 +36,14 @@ static inline void restore_snan_bit_mode(CPUMIPSState *env) */ set_snan_bit_is_one(!nan2008, &env->active_fpu.fp_status); set_default_nan_mode(!nan2008, &env->active_fpu.fp_status); + /* + * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) + * case sets InvalidOp and returns the default NaN. + * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c'. + */ + izn_rule = nan2008 ? float_infzeronan_dnan_never : float_infzeronan_dnan_always; + set_float_infzeronan_rule(izn_rule, &env->active_fpu.fp_status); } static inline void restore_fp_status(CPUMIPSState *env) diff --git a/target/mips/msa.c b/target/mips/msa.c index 9dffc428f5..cc152db27f 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -74,4 +74,8 @@ void msa_reset(CPUMIPSState *env) /* set proper signanling bit meaning ("1" means "quiet") */ set_snan_bit_is_one(0, &env->active_tc.msa_fp_status); + + /* Inf * 0 + NaN returns the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, + &env->active_tc.msa_fp_status); } From 9a31b8d0ad86e12920e01bd4c01516be9f75337e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:55 +0000 Subject: [PATCH 0115/2892] target/sparc: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the SPARC target, so we can remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-10-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 3 +-- target/sparc/cpu.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index db9a466e05..7e57e85348 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -489,8 +489,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, /* * Temporarily fall back to ifdef ladder */ -#if defined(TARGET_SPARC) || \ - defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \ +#if defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \ defined(TARGET_I386) || defined(TARGET_LOONGARCH) /* * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index dd7af86de7..61f2d3fbf2 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -814,6 +814,8 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) * the CPU state struct so it won't get zeroed on reset. */ set_float_2nan_prop_rule(float_2nan_prop_s_ba, &env->fp_status); + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { From 67c0df045ec11da24bd2f18f81813ed9ff48b4c5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:55 +0000 Subject: [PATCH 0116/2892] target/xtensa: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the xtensa target, so we can remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-11-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 +- target/xtensa/cpu.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 7e57e85348..3062d19402 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -489,7 +489,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, /* * Temporarily fall back to ifdef ladder */ -#if defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \ +#if defined(TARGET_HPPA) || \ defined(TARGET_I386) || defined(TARGET_LOONGARCH) /* * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 6f9039abae..3163b75823 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -133,6 +133,8 @@ static void xtensa_cpu_reset_hold(Object *obj, ResetType type) reset_mmu(env); cs->halted = env->runstall; #endif + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); set_no_signaling_nans(!dfpu, &env->fp_status); xtensa_use_first_nan(env, !dfpu); } From 390df9046b58249dfac2590a914422834c8333c6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:55 +0000 Subject: [PATCH 0117/2892] target/x86: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the x86 target. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-12-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 +- target/i386/tcg/fpu_helper.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 3062d19402..ad4f7096d0 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -490,7 +490,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * Temporarily fall back to ifdef ladder */ #if defined(TARGET_HPPA) || \ - defined(TARGET_I386) || defined(TARGET_LOONGARCH) + defined(TARGET_LOONGARCH) /* * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) * case sets InvalidOp and returns the input value 'c' diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 53b49bb297..3295753e07 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -173,6 +173,13 @@ void cpu_init_fp_statuses(CPUX86State *env) */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->mmx_status); set_float_2nan_prop_rule(float_2nan_prop_x87, &env->sse_status); + /* + * Only SSE has multiply-add instructions. In the SDM Section 14.5.2 + * "Fused-Multiply-ADD (FMA) Numeric Behavior" the NaN handling is + * specified -- for 0 * inf + NaN the input NaN is selected, and if + * there are multiple input NaNs they are selected in the order a, b, c. + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->sse_status); } static inline uint8_t save_exception_flags(CPUX86State *env) From 0fb7fa29d3c4a21bf5daf7f30c70f916e878935c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:56 +0000 Subject: [PATCH 0118/2892] target/loongarch: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the loongarch target. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-13-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 7 +------ target/loongarch/tcg/fpu_helper.c | 5 +++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index ad4f7096d0..05dec2fcb4 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -489,12 +489,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, /* * Temporarily fall back to ifdef ladder */ -#if defined(TARGET_HPPA) || \ - defined(TARGET_LOONGARCH) - /* - * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ +#if defined(TARGET_HPPA) rule = float_infzeronan_dnan_never; #endif } diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index 21bc3b04a9..6a2c4b5b1d 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -32,6 +32,11 @@ void restore_fp_status(CPULoongArchState *env) &env->fp_status); set_flush_to_zero(0, &env->fp_status); set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); + /* + * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); } int ieee_ex_to_loongarch(int xcpt) From 2bf5629c97af5aa57f8524af92f5459f1d2ceeac Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:56 +0000 Subject: [PATCH 0119/2892] target/hppa: Set FloatInfZeroNaNRule explicitly Set the FloatInfZeroNaNRule explicitly for the HPPA target, so we can remove the ifdef from pickNaNMulAdd(). As this is the last target to be converted to explicitly setting the rule, we can remove the fallback code in pickNaNMulAdd() entirely. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-14-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 13 +------------ target/hppa/fpu_helper.c | 2 ++ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 05dec2fcb4..3e4ec938b2 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -475,8 +475,6 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, bool infzero, float_status *status) { - FloatInfZeroNaNRule rule = status->float_infzeronan_rule; - /* * We guarantee not to require the target to tell us how to * pick a NaN if we're always returning the default NaN. @@ -485,21 +483,12 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, */ assert(!status->default_nan_mode); - if (rule == float_infzeronan_none) { - /* - * Temporarily fall back to ifdef ladder - */ -#if defined(TARGET_HPPA) - rule = float_infzeronan_dnan_never; -#endif - } - if (infzero) { /* * Inf * 0 + NaN -- some implementations return the default NaN here, * and some return the input NaN. */ - switch (rule) { + switch (status->float_infzeronan_rule) { case float_infzeronan_dnan_never: return 2; case float_infzeronan_dnan_always: diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 0e44074ba8..393cae33bf 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -55,6 +55,8 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) * HPPA does note implement a CPU reset method at all... */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); } void cpu_hppa_loaded_fr0(CPUHPPAState *env) From d62c734d52147ec312d1ada0e1bc7fa479838575 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0120/2892] softfloat: Pass have_snan to pickNaNMulAdd The new implementation of pickNaNMulAdd() will find it convenient to know whether at least one of the three arguments to the muladd was a signaling NaN. We already calculate that in the caller, so pass it in as a new bool have_snan. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-15-peter.maydell@linaro.org --- fpu/softfloat-parts.c.inc | 5 +++-- fpu/softfloat-specialize.c.inc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index aac1f9cd28..655b7d9da5 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -67,8 +67,9 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, { int which; bool infzero = (ab_mask == float_cmask_infzero); + bool have_snan = (abc_mask & float_cmask_snan); - if (unlikely(abc_mask & float_cmask_snan)) { + if (unlikely(have_snan)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); } @@ -80,7 +81,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, if (s->default_nan_mode) { which = 3; } else { - which = pickNaNMulAdd(a->cls, b->cls, c->cls, infzero, s); + which = pickNaNMulAdd(a->cls, b->cls, c->cls, infzero, have_snan, s); } if (which == 3) { diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 3e4ec938b2..a769c71f54 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -473,7 +473,7 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, | Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN *----------------------------------------------------------------------------*/ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, - bool infzero, float_status *status) + bool infzero, bool have_snan, float_status *status) { /* * We guarantee not to require the target to tell us how to From 7a944c30f72380e543b58342e12105dcf98d8496 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0121/2892] softfloat: Allow runtime choice of NaN propagation for muladd IEEE 758 does not define a fixed rule for which NaN to pick as the result if both operands of a 3-operand fused multiply-add operation are NaNs. As a result different architectures have ended up with different rules for propagating NaNs. QEMU currently hardcodes the NaN propagation logic into the binary because pickNaNMulAdd() has an ifdef ladder for different targets. We want to make the propagation rule instead be selectable at runtime, because: * this will let us have multiple targets in one QEMU binary * the Arm FEAT_AFP architectural feature includes letting the guest select a NaN propagation rule at runtime In this commit we add an enum for the propagation rule, the field in float_status, and the corresponding getters and setters. We change pickNaNMulAdd to honour this, but because all targets still leave this field at its default 0 value, the fallback logic will pick the rule type with the old ifdef ladder. It's valid not to set a propagation rule if default_nan_mode is enabled, because in that case there's no need to pick a NaN; all the callers of pickNaNMulAdd() catch this case and skip calling it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-16-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 167 ++++++++------------------------ include/fpu/softfloat-helpers.h | 11 +++ include/fpu/softfloat-types.h | 55 +++++++++++ 3 files changed, 107 insertions(+), 126 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index a769c71f54..b4f3f0efa8 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -475,6 +475,10 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, bool infzero, bool have_snan, float_status *status) { + FloatClass cls[3] = { a_cls, b_cls, c_cls }; + Float3NaNPropRule rule = status->float_3nan_prop_rule; + int which; + /* * We guarantee not to require the target to tell us how to * pick a NaN if we're always returning the default NaN. @@ -500,145 +504,56 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } } + if (rule == float_3nan_prop_none) { #if defined(TARGET_ARM) - - /* This looks different from the ARM ARM pseudocode, because the ARM ARM - * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. - */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } + /* + * This looks different from the ARM ARM pseudocode, because the ARM ARM + * puts the operands to a fused mac operation (a*b)+c in the order c,a,b + */ + rule = float_3nan_prop_s_cab; #elif defined(TARGET_MIPS) - if (snan_bit_is_one(status)) { - /* Prefer sNaN over qNaN, in the a, b, c order. */ - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_snan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else if (is_qnan(b_cls)) { - return 1; + if (snan_bit_is_one(status)) { + rule = float_3nan_prop_s_abc; } else { - return 2; + rule = float_3nan_prop_s_cab; } - } else { - /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } - } #elif defined(TARGET_LOONGARCH64) - /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } + rule = float_3nan_prop_s_cab; #elif defined(TARGET_PPC) - /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; - * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB - */ - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(c_cls)) { - return 2; - } else { - return 1; - } + /* + * If fRA is a NaN return it; otherwise if fRB is a NaN return it; + * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB + */ + rule = float_3nan_prop_acb; #elif defined(TARGET_S390X) - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_snan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 2; - } + rule = float_3nan_prop_s_abc; #elif defined(TARGET_SPARC) - /* Prefer SNaN over QNaN, order C, B, A. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 0; - } + rule = float_3nan_prop_s_cba; #elif defined(TARGET_XTENSA) - /* - * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns - * an input NaN if we have one (ie c). - */ - if (status->use_first_nan) { - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(b_cls)) { - return 1; + if (status->use_first_nan) { + rule = float_3nan_prop_abc; } else { - return 2; + rule = float_3nan_prop_cba; } - } else { - if (is_nan(c_cls)) { - return 2; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 0; - } - } #else - /* A default implementation: prefer a to b to c. - * This is unlikely to actually match any real implementation. - */ - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 2; - } + rule = float_3nan_prop_abc; #endif + } + + assert(rule != float_3nan_prop_none); + if (have_snan && (rule & R_3NAN_SNAN_MASK)) { + /* We have at least one SNaN input and should prefer it */ + do { + which = rule & R_3NAN_1ST_MASK; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_snan(cls[which])); + } else { + do { + which = rule & R_3NAN_1ST_MASK; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_nan(cls[which])); + } + return which; } /*---------------------------------------------------------------------------- diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 0bf44dc608..cf06b4e16b 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -81,6 +81,12 @@ static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, status->float_2nan_prop_rule = rule; } +static inline void set_float_3nan_prop_rule(Float3NaNPropRule rule, + float_status *status) +{ + status->float_3nan_prop_rule = rule; +} + static inline void set_float_infzeronan_rule(FloatInfZeroNaNRule rule, float_status *status) { @@ -143,6 +149,11 @@ static inline Float2NaNPropRule get_float_2nan_prop_rule(float_status *status) return status->float_2nan_prop_rule; } +static inline Float3NaNPropRule get_float_3nan_prop_rule(float_status *status) +{ + return status->float_3nan_prop_rule; +} + static inline FloatInfZeroNaNRule get_float_infzeronan_rule(float_status *status) { return status->float_infzeronan_rule; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 47bb22c4e2..d9f0797eda 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -80,6 +80,8 @@ this code that are retained. #ifndef SOFTFLOAT_TYPES_H #define SOFTFLOAT_TYPES_H +#include "hw/registerfields.h" + /* * Software IEC/IEEE floating-point types. */ @@ -207,6 +209,58 @@ typedef enum __attribute__((__packed__)) { float_2nan_prop_x87, } Float2NaNPropRule; +/* + * 3-input NaN propagation rule, for fused multiply-add. Individual + * architectures have different rules for which input NaN is + * propagated to the output when there is more than one NaN on the + * input. + * + * If default_nan_mode is enabled then it is valid not to set a NaN + * propagation rule, because the softfloat code guarantees not to try + * to pick a NaN to propagate in default NaN mode. When not in + * default-NaN mode, it is an error for the target not to set the rule + * in float_status if it uses a muladd, and we will assert if we need + * to handle an input NaN and no rule was selected. + * + * The naming scheme for Float3NaNPropRule values is: + * float_3nan_prop_s_abc: + * = "Prefer SNaN over QNaN, then operand A over B over C" + * float_3nan_prop_abc: + * = "Prefer A over B over C regardless of SNaN vs QNAN" + * + * For QEMU, the multiply-add operation is A * B + C. + */ + +/* + * We set the Float3NaNPropRule enum values up so we can select the + * right value in pickNaNMulAdd in a data driven way. + */ +FIELD(3NAN, 1ST, 0, 2) /* which operand is most preferred ? */ +FIELD(3NAN, 2ND, 2, 2) /* which operand is next most preferred ? */ +FIELD(3NAN, 3RD, 4, 2) /* which operand is least preferred ? */ +FIELD(3NAN, SNAN, 6, 1) /* do we prefer SNaN over QNaN ? */ + +#define PROPRULE(X, Y, Z) \ + ((X << R_3NAN_1ST_SHIFT) | (Y << R_3NAN_2ND_SHIFT) | (Z << R_3NAN_3RD_SHIFT)) + +typedef enum __attribute__((__packed__)) { + float_3nan_prop_none = 0, /* No propagation rule specified */ + float_3nan_prop_abc = PROPRULE(0, 1, 2), + float_3nan_prop_acb = PROPRULE(0, 2, 1), + float_3nan_prop_bac = PROPRULE(1, 0, 2), + float_3nan_prop_bca = PROPRULE(1, 2, 0), + float_3nan_prop_cab = PROPRULE(2, 0, 1), + float_3nan_prop_cba = PROPRULE(2, 1, 0), + float_3nan_prop_s_abc = float_3nan_prop_abc | R_3NAN_SNAN_MASK, + float_3nan_prop_s_acb = float_3nan_prop_acb | R_3NAN_SNAN_MASK, + float_3nan_prop_s_bac = float_3nan_prop_bac | R_3NAN_SNAN_MASK, + float_3nan_prop_s_bca = float_3nan_prop_bca | R_3NAN_SNAN_MASK, + float_3nan_prop_s_cab = float_3nan_prop_cab | R_3NAN_SNAN_MASK, + float_3nan_prop_s_cba = float_3nan_prop_cba | R_3NAN_SNAN_MASK, +} Float3NaNPropRule; + +#undef PROPRULE + /* * Rule for result of fused multiply-add 0 * Inf + NaN. * This must be a NaN, but implementations differ on whether this @@ -241,6 +295,7 @@ typedef struct float_status { FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; Float2NaNPropRule float_2nan_prop_rule; + Float3NaNPropRule float_3nan_prop_rule; FloatInfZeroNaNRule float_infzeronan_rule; bool tininess_before_rounding; /* should denormalised results go to zero and set the inexact flag? */ From 43e51128083185ec5deb2cbe322fb851f7e2d2ae Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0122/2892] tests/fp: Explicitly set 3-NaN propagation rule Explicitly set a rule in the softfloat tests for propagating NaNs in the muladd case. In meson.build we put -DTARGET_ARM in fpcflags, and so we should select here the Arm rule of float_3nan_prop_s_cab. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-17-peter.maydell@linaro.org --- tests/fp/fp-bench.c | 1 + tests/fp/fp-test.c | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index fde6483619..39d80c9038 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -493,6 +493,7 @@ static void run_bench(void) * doesn't specify match those used by the Arm architecture. */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); f = bench_funcs[operation][precision]; diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 251c278ede..f290d523ab 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -940,6 +940,7 @@ void run_test(void) * doesn't specify match those used by the Arm architecture. */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &qsf); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &qsf); genCases_setLevel(test_level); From 10519d3b1aeb24d8173631d4fe0ecb91f2732e19 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0123/2892] target/arm: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for Arm, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-18-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 8 +------- target/arm/cpu.c | 5 +++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index b4f3f0efa8..3a2d044447 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -505,13 +505,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } if (rule == float_3nan_prop_none) { -#if defined(TARGET_ARM) - /* - * This looks different from the ARM ARM pseudocode, because the ARM ARM - * puts the operands to a fused mac operation (a*b)+c in the order c,a,b - */ - rule = float_3nan_prop_s_cab; -#elif defined(TARGET_MIPS) +#if defined(TARGET_MIPS) if (snan_bit_is_one(status)) { rule = float_3nan_prop_s_abc; } else { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ead3979398..c81f6df3fc 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -173,6 +173,10 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, * * tininess-before-rounding * * 2-input NaN propagation prefers SNaN over QNaN, and then * operand A over operand B (see FPProcessNaNs() pseudocode) + * * 3-input NaN propagation prefers SNaN over QNaN, and then + * operand C over A over B (see FPProcessNaNs3() pseudocode, + * but note that for QEMU muladd is a * b + c, whereas for + * the pseudocode function the arguments are in the order c, a, b. * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, * and the input NaN if it is signalling */ @@ -180,6 +184,7 @@ static void arm_set_default_fp_behaviours(float_status *s) { set_float_detect_tininess(float_tininess_before_rounding, s); set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); } From 9d0b8f96056f45510c3f0ed7216dbc969e5e5fec Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0124/2892] target/loongarch: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for loongarch, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-19-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 -- target/loongarch/tcg/fpu_helper.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 3a2d044447..d610f46002 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -511,8 +511,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } else { rule = float_3nan_prop_s_cab; } -#elif defined(TARGET_LOONGARCH64) - rule = float_3nan_prop_s_cab; #elif defined(TARGET_PPC) /* * If fRA is a NaN return it; otherwise if fRB is a NaN return it; diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index 6a2c4b5b1d..37a4859936 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -37,6 +37,7 @@ void restore_fp_status(CPULoongArchState *env) * case sets InvalidOp and returns the input value 'c' */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &env->fp_status); } int ieee_ex_to_loongarch(int xcpt) From ac1254c4e75cd365283eca90cebc64cdb5a7e48d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0125/2892] target/ppc: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for PPC, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-20-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 6 ------ target/ppc/cpu_init.c | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index d610f46002..173b9eadb5 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -511,12 +511,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } else { rule = float_3nan_prop_s_cab; } -#elif defined(TARGET_PPC) - /* - * If fRA is a NaN return it; otherwise if fRB is a NaN return it; - * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB - */ - rule = float_3nan_prop_acb; #elif defined(TARGET_S390X) rule = float_3nan_prop_s_abc; #elif defined(TARGET_SPARC) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index f18908a643..eb9d7b1370 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7270,6 +7270,14 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) */ set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); set_float_2nan_prop_rule(float_2nan_prop_ab, &env->vec_status); + /* + * NaN propagation for fused multiply-add: + * if fRA is a NaN return it; otherwise if fRB is a NaN return it; + * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB + * whereas QEMU labels the operands as (a * b) + c. + */ + set_float_3nan_prop_rule(float_3nan_prop_acb, &env->fp_status); + set_float_3nan_prop_rule(float_3nan_prop_acb, &env->vec_status); /* * For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating From b07039a03e0261105e2de45eb077ede18b3e49b5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:57 +0000 Subject: [PATCH 0126/2892] target/s390x: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for s390x, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-21-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 -- target/s390x/cpu.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 173b9eadb5..8a36280df1 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -511,8 +511,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } else { rule = float_3nan_prop_s_cab; } -#elif defined(TARGET_S390X) - rule = float_3nan_prop_s_abc; #elif defined(TARGET_SPARC) rule = float_3nan_prop_s_cba; #elif defined(TARGET_XTENSA) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index d5941b5b9d..e74055bad7 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -206,6 +206,7 @@ static void s390_cpu_reset_hold(Object *obj, ResetType type) set_float_detect_tininess(float_tininess_before_rounding, &env->fpu_status); set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fpu_status); + set_float_3nan_prop_rule(float_3nan_prop_s_abc, &env->fpu_status); set_float_infzeronan_rule(float_infzeronan_dnan_always, &env->fpu_status); /* fall through */ From 49866dcb59a28325c99c08745ed8e7c81828ed06 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:58 +0000 Subject: [PATCH 0127/2892] target/sparc: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for SPARC, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-22-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 -- target/sparc/cpu.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 8a36280df1..c4d8d085a9 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -511,8 +511,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } else { rule = float_3nan_prop_s_cab; } -#elif defined(TARGET_SPARC) - rule = float_3nan_prop_s_cba; #elif defined(TARGET_XTENSA) if (status->use_first_nan) { rule = float_3nan_prop_abc; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 61f2d3fbf2..0f2997a85e 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -814,6 +814,8 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) * the CPU state struct so it won't get zeroed on reset. */ set_float_2nan_prop_rule(float_2nan_prop_s_ba, &env->fp_status); + /* For fused-multiply add, prefer SNaN over QNaN, then C->B->A */ + set_float_3nan_prop_rule(float_3nan_prop_s_cba, &env->fp_status); /* For inf * 0 + NaN, return the input NaN */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); From 3a45371291af21b9671ac081fae6bec9270c9af7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:58 +0000 Subject: [PATCH 0128/2892] target/mips: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for Arm, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-23-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 8 +------- target/mips/fpu_helper.h | 4 ++++ target/mips/msa.c | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index c4d8d085a9..28db409d22 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -505,13 +505,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } if (rule == float_3nan_prop_none) { -#if defined(TARGET_MIPS) - if (snan_bit_is_one(status)) { - rule = float_3nan_prop_s_abc; - } else { - rule = float_3nan_prop_s_cab; - } -#elif defined(TARGET_XTENSA) +#if defined(TARGET_XTENSA) if (status->use_first_nan) { rule = float_3nan_prop_abc; } else { diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index be66f2f813..8ca0ca7ea3 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -29,6 +29,7 @@ static inline void restore_snan_bit_mode(CPUMIPSState *env) { bool nan2008 = env->active_fpu.fcr31 & (1 << FCR31_NAN2008); FloatInfZeroNaNRule izn_rule; + Float3NaNPropRule nan3_rule; /* * With nan2008, SNaNs are silenced in the usual way. @@ -44,6 +45,9 @@ static inline void restore_snan_bit_mode(CPUMIPSState *env) */ izn_rule = nan2008 ? float_infzeronan_dnan_never : float_infzeronan_dnan_always; set_float_infzeronan_rule(izn_rule, &env->active_fpu.fp_status); + nan3_rule = nan2008 ? float_3nan_prop_s_cab : float_3nan_prop_s_abc; + set_float_3nan_prop_rule(nan3_rule, &env->active_fpu.fp_status); + } static inline void restore_fp_status(CPUMIPSState *env) diff --git a/target/mips/msa.c b/target/mips/msa.c index cc152db27f..93a9a87d76 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -66,6 +66,9 @@ void msa_reset(CPUMIPSState *env) set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->active_tc.msa_fp_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, + &env->active_tc.msa_fp_status); + /* clear float_status exception flags */ set_float_exception_flags(0, &env->active_tc.msa_fp_status); From 8e6915645e356a0bc82afd06b4fc5d5c286c9342 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:58 +0000 Subject: [PATCH 0129/2892] target/xtensa: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for xtensa, and remove the ifdef from pickNaNMulAdd(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-24-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 8 -------- target/xtensa/fpu_helper.c | 2 ++ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 28db409d22..67428dab98 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -505,15 +505,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } if (rule == float_3nan_prop_none) { -#if defined(TARGET_XTENSA) - if (status->use_first_nan) { - rule = float_3nan_prop_abc; - } else { - rule = float_3nan_prop_cba; - } -#else rule = float_3nan_prop_abc; -#endif } assert(rule != float_3nan_prop_none); diff --git a/target/xtensa/fpu_helper.c b/target/xtensa/fpu_helper.c index f2d212d05d..4b1b021d82 100644 --- a/target/xtensa/fpu_helper.c +++ b/target/xtensa/fpu_helper.c @@ -62,6 +62,8 @@ void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) set_use_first_nan(use_first, &env->fp_status); set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, &env->fp_status); + set_float_3nan_prop_rule(use_first ? float_3nan_prop_abc : float_3nan_prop_cba, + &env->fp_status); } void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) From 703990100a34e289716e0a5b4c2bb12a14b32597 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:58 +0000 Subject: [PATCH 0130/2892] target/i386: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for i386. We had no i386-specific behaviour in the old ifdef ladder, so we were using the default "prefer a then b then c" fallback; this is actually the correct per-the-spec handling for i386. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-25-peter.maydell@linaro.org --- target/i386/tcg/fpu_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 3295753e07..4303b3356a 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -180,6 +180,7 @@ void cpu_init_fp_statuses(CPUX86State *env) * there are multiple input NaNs they are selected in the order a, b, c. */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->sse_status); + set_float_3nan_prop_rule(float_3nan_prop_abc, &env->sse_status); } static inline uint8_t save_exception_flags(CPUX86State *env) From f8023791f21bf46229ce403566ca476b86e06505 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:59 +0000 Subject: [PATCH 0131/2892] target/hppa: Set Float3NaNPropRule explicitly Set the Float3NaNPropRule explicitly for HPPA, and remove the ifdef from pickNaNMulAdd(). HPPA is the only target that was using the default branch of the ifdef ladder (other targets either do not use muladd or set default_nan_mode), so we can remove the ifdef fallback entirely now (allowing the "rule not set" case to fall into the default of the switch statement and assert). We add a TODO note that the HPPA rule is probably wrong; this is not a behavioural change for this refactoring. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-26-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 4 ---- target/hppa/fpu_helper.c | 8 ++++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 67428dab98..5fbc953e71 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -504,10 +504,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, } } - if (rule == float_3nan_prop_none) { - rule = float_3nan_prop_abc; - } - assert(rule != float_3nan_prop_none); if (have_snan && (rule & R_3NAN_SNAN_MASK)) { /* We have at least one SNaN input and should prefer it */ diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 393cae33bf..69c4ce3783 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -55,6 +55,14 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) * HPPA does note implement a CPU reset method at all... */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); + /* + * TODO: The HPPA architecture reference only documents its NaN + * propagation rule for 2-operand operations. Testing on real hardware + * might be necessary to confirm whether this order for muladd is correct. + * Not preferring the SNaN is almost certainly incorrect as it diverges + * from the documented rules for 2-operand operations. + */ + set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status); /* For inf * 0 + NaN, return the input NaN */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); } From dc416d6ca74d43f3aa1bd304d241b9a11d86e184 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:59 +0000 Subject: [PATCH 0132/2892] fpu: Remove use_first_nan field from float_status The use_first_nan field in float_status was an xtensa-specific way to select at runtime from two different NaN propagation rules. Now that xtensa is using the target-agnostic NaN propagation rule selection that we've just added, we can remove use_first_nan, because there is no longer any code that reads it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-27-peter.maydell@linaro.org --- include/fpu/softfloat-helpers.h | 5 ----- include/fpu/softfloat-types.h | 1 - target/xtensa/fpu_helper.c | 1 - 3 files changed, 7 deletions(-) diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index cf06b4e16b..10a6763532 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -113,11 +113,6 @@ static inline void set_snan_bit_is_one(bool val, float_status *status) status->snan_bit_is_one = val; } -static inline void set_use_first_nan(bool val, float_status *status) -{ - status->use_first_nan = val; -} - static inline void set_no_signaling_nans(bool val, float_status *status) { status->no_signaling_nans = val; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index d9f0797eda..84ba4ed20e 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -309,7 +309,6 @@ typedef struct float_status { * softfloat-specialize.inc.c) */ bool snan_bit_is_one; - bool use_first_nan; bool no_signaling_nans; /* should overflowed results subtract re_bias to its exponent? */ bool rebias_overflow; diff --git a/target/xtensa/fpu_helper.c b/target/xtensa/fpu_helper.c index 4b1b021d82..53fc7cfd2a 100644 --- a/target/xtensa/fpu_helper.c +++ b/target/xtensa/fpu_helper.c @@ -59,7 +59,6 @@ static const struct { void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) { - set_use_first_nan(use_first, &env->fp_status); set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, &env->fp_status); set_float_3nan_prop_rule(use_first ? float_3nan_prop_abc : float_3nan_prop_cba, From 04b6a3e47185135f443d4b98d4910ad26abbcd67 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:30:59 +0000 Subject: [PATCH 0133/2892] target/m68k: Don't pass NULL float_status to floatx80_default_nan() Currently m68k_cpu_reset_hold() calls floatx80_default_nan(NULL) to get the NaN bit pattern to reset the FPU registers. This works because it happens that our implementation of floatx80_default_nan() doesn't actually look at the float_status pointer except for TARGET_MIPS. However, this isn't guaranteed, and to be able to remove the ifdef in floatx80_default_nan() we're going to need a real float_status here. Rearrange m68k_cpu_reset_hold() so that we initialize env->fp_status earlier, and thus can pass it to floatx80_default_nan(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-28-peter.maydell@linaro.org --- target/m68k/cpu.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 5fe335558a..13b76e2248 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -76,7 +76,7 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) CPUState *cs = CPU(obj); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(obj); CPUM68KState *env = cpu_env(cs); - floatx80 nan = floatx80_default_nan(NULL); + floatx80 nan; int i; if (mcc->parent_phases.hold) { @@ -89,10 +89,6 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) #else cpu_m68k_set_sr(env, SR_S | SR_I); #endif - for (i = 0; i < 8; i++) { - env->fregs[i].d = nan; - } - cpu_m68k_set_fpcr(env, 0); /* * M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL * 3.4 FLOATING-POINT INSTRUCTION DETAILS @@ -109,6 +105,12 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) * preceding paragraph for nonsignaling NaNs. */ set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); + + nan = floatx80_default_nan(&env->fp_status); + for (i = 0; i < 8; i++) { + env->fregs[i].d = nan; + } + cpu_m68k_set_fpcr(env, 0); env->fpsr = 0; /* TODO: We should set PC from the interrupt vector. */ From 3d024e359feff6237ceed5326e08850a70682b3a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:00 +0000 Subject: [PATCH 0134/2892] softfloat: Create floatx80 default NaN from parts64_default_nan We create our 128-bit default NaN by calling parts64_default_nan() and then adjusting the result. We can do the same trick for creating the floatx80 default NaN, which lets us drop a target ifdef. floatx80 is used only by: i386 m68k arm nwfpe old floating-point emulation emulation support (which is essentially dead, especially the parts involving floatx80) PPC (only in the xsrqpxp instruction, which just rounds an input value by converting to floatx80 and back, so will never generate the default NaN) The floatx80 default NaN as currently implemented is: m68k: sign = 0, exp = 1...1, int = 1, frac = 1....1 i386: sign = 1, exp = 1...1, int = 1, frac = 10...0 These are the same as the parts64_default_nan for these architectures. This is technically a possible behaviour change for arm linux-user nwfpe emulation emulation, because the default NaN will now have the sign bit clear. But we were already generating a different floatx80 default NaN from the real kernel emulation we are supposedly following, which appears to use an all-bits-1 value: https://elixir.bootlin.com/linux/v6.12/source/arch/arm/nwfpe/softfloat-specialize#L267 This won't affect the only "real" use of the nwfpe emulation, which is ancient binaries that used it as part of the old floating point calling convention; that only uses loads and stores of 32 and 64 bit floats, not any of the floatx80 behaviour the original hardware had. We also get the nwfpe float64 default NaN value wrong: https://elixir.bootlin.com/linux/v6.12/source/arch/arm/nwfpe/softfloat-specialize#L166 so if we ever cared about this obscure corner the right fix would be to correct that so nwfpe used its own default-NaN setting rather than the Arm VFP one. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-29-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 5fbc953e71..9f913ce20a 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -227,17 +227,17 @@ static void parts128_silence_nan(FloatParts128 *p, float_status *status) floatx80 floatx80_default_nan(float_status *status) { floatx80 r; + /* + * Extrapolate from the choices made by parts64_default_nan to fill + * in the floatx80 format. We assume that floatx80's explicit + * integer bit is always set (this is true for i386 and m68k, + * which are the only real users of this format). + */ + FloatParts64 p64; + parts64_default_nan(&p64, status); - /* None of the targets that have snan_bit_is_one use floatx80. */ - assert(!snan_bit_is_one(status)); -#if defined(TARGET_M68K) - r.low = UINT64_C(0xFFFFFFFFFFFFFFFF); - r.high = 0x7FFF; -#else - /* X86 */ - r.low = UINT64_C(0xC000000000000000); - r.high = 0xFFFF; -#endif + r.high = 0x7FFF | (p64.sign << 15); + r.low = (1ULL << DECOMPOSED_BINARY_POINT) | p64.frac; return r; } From a0c4297738880393a1a74d5db551ee7a832e7a91 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:01 +0000 Subject: [PATCH 0135/2892] target/loongarch: Use normal float_status in fclass_s and fclass_d helpers In target/loongarch's helper_fclass_s() and helper_fclass_d() we pass a zero-initialized float_status struct to float32_is_quiet_nan() and float64_is_quiet_nan(), with the cryptic comment "for snan_bit_is_one". This pattern appears to have been copied from target/riscv, where it is used because the functions there do not have ready access to the CPU state struct. The comment presumably refers to the fact that the main reason the is_quiet_nan() functions want the float_state is because they want to know about the snan_bit_is_one config. In the loongarch helpers, though, we have the CPU state struct to hand. Use the usual env->fp_status here. This avoids our needing to track that we need to update the initializer of the local float_status structs when the core softfloat code adds new options for targets to configure their behaviour. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-30-peter.maydell@linaro.org --- target/loongarch/tcg/fpu_helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index 37a4859936..aea5e0fe5e 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -359,8 +359,7 @@ uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj) } else if (float32_is_zero_or_denormal(f)) { return sign ? 1 << 4 : 1 << 8; } else if (float32_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; + return float32_is_quiet_nan(f, &env->fp_status) ? 1 << 1 : 1 << 0; } else { return sign ? 1 << 3 : 1 << 7; } @@ -378,8 +377,7 @@ uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj) } else if (float64_is_zero_or_denormal(f)) { return sign ? 1 << 4 : 1 << 8; } else if (float64_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; + return float64_is_quiet_nan(f, &env->fp_status) ? 1 << 1 : 1 << 0; } else { return sign ? 1 << 3 : 1 << 7; } From b1969a5dd4ad72669d739ef6e8caaefb7c7f962c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:02 +0000 Subject: [PATCH 0136/2892] target/m68k: In frem helper, initialize local float_status from env->fp_status In the frem helper, we have a local float_status because we want to execute the floatx80_div() with a custom rounding mode. Instead of zero-initializing the local float_status and then having to set it up with the m68k standard behaviour (including the NaN propagation rule and copying the rounding precision from env->fp_status), initialize it as a complete copy of env->fp_status. This will avoid our having to add new code in this function for every new config knob we add to fp_status. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-31-peter.maydell@linaro.org --- target/m68k/fpu_helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index a605162b71..e3f4a18850 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -615,15 +615,13 @@ void HELPER(frem)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) fp_rem = floatx80_rem(val1->d, val0->d, &env->fp_status); if (!floatx80_is_any_nan(fp_rem)) { - float_status fp_status = { }; + /* Use local temporary fp_status to set different rounding mode */ + float_status fp_status = env->fp_status; uint32_t quotient; int sign; /* Calculate quotient directly using round to nearest mode */ - set_float_2nan_prop_rule(float_2nan_prop_ab, &fp_status); set_float_rounding_mode(float_round_nearest_even, &fp_status); - set_floatx80_rounding_precision( - get_floatx80_rounding_precision(&env->fp_status), &fp_status); fp_quot.d = floatx80_div(val1->d, val0->d, &fp_status); sign = extractFloatx80Sign(fp_quot.d); From ca81533e94c81838fa05bb8ba7a8a6708e4dca99 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:03 +0000 Subject: [PATCH 0137/2892] target/m68k: Init local float_status from env fp_status in gdb get/set reg In cf_fpu_gdb_get_reg() and cf_fpu_gdb_set_reg() we do the conversion from float64 to floatx80 using a scratch float_status, because we don't want the conversion to affect the CPU's floating point exception status. Currently we use a zero-initialized float_status. This will get steadily more awkward as we add config knobs to float_status that the target must initialize. Avoid having to add any of that configuration here by instead initializing our local float_status from the env->fp_status. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-32-peter.maydell@linaro.org --- target/m68k/helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 9bfc6ae97c..beefeb7069 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -36,7 +36,8 @@ static int cf_fpu_gdb_get_reg(CPUState *cs, GByteArray *mem_buf, int n) CPUM68KState *env = &cpu->env; if (n < 8) { - float_status s = {}; + /* Use scratch float_status so any exceptions don't change CPU state */ + float_status s = env->fp_status; return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { @@ -56,7 +57,8 @@ static int cf_fpu_gdb_set_reg(CPUState *cs, uint8_t *mem_buf, int n) CPUM68KState *env = &cpu->env; if (n < 8) { - float_status s = {}; + /* Use scratch float_status so any exceptions don't change CPU state */ + float_status s = env->fp_status; env->fregs[n].d = float64_to_floatx80(ldq_be_p(mem_buf), &s); return 8; } From 5edd92d6c340553e1788e86fef844a389f052b72 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:03 +0000 Subject: [PATCH 0138/2892] target/sparc: Initialize local scratch float_status from env->fp_status In the helper functions flcmps and flcmpd we use a scratch float_status so that we don't change the CPU state if the comparison raises any floating point exception flags. Instead of zero-initializing this scratch float_status, initialize it as a copy of env->fp_status. This avoids the need to explicitly initialize settings like the NaN propagation rule or others we might add to softfloat in future. To do this we need to pass the CPU env pointer in to the helper. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-33-peter.maydell@linaro.org --- target/sparc/fop_helper.c | 8 ++++---- target/sparc/helper.h | 4 ++-- target/sparc/translate.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 6f9ccc008a..236d27b19c 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -490,13 +490,13 @@ uint32_t helper_fcmpeq(CPUSPARCState *env, Int128 src1, Int128 src2) return finish_fcmp(env, r, GETPC()); } -uint32_t helper_flcmps(float32 src1, float32 src2) +uint32_t helper_flcmps(CPUSPARCState *env, float32 src1, float32 src2) { /* * FLCMP never raises an exception nor modifies any FSR fields. * Perform the comparison with a dummy fp environment. */ - float_status discard = { }; + float_status discard = env->fp_status; FloatRelation r; set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); @@ -518,9 +518,9 @@ uint32_t helper_flcmps(float32 src1, float32 src2) g_assert_not_reached(); } -uint32_t helper_flcmpd(float64 src1, float64 src2) +uint32_t helper_flcmpd(CPUSPARCState *env, float64 src1, float64 src2) { - float_status discard = { }; + float_status discard = env->fp_status; FloatRelation r; set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 134e519a37..1ae3f0c467 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -51,8 +51,8 @@ DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, i32, env, f64, f64) DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, i32, env, f64, f64) DEF_HELPER_FLAGS_3(fcmpq, TCG_CALL_NO_WG, i32, env, i128, i128) DEF_HELPER_FLAGS_3(fcmpeq, TCG_CALL_NO_WG, i32, env, i128, i128) -DEF_HELPER_FLAGS_2(flcmps, TCG_CALL_NO_RWG_SE, i32, f32, f32) -DEF_HELPER_FLAGS_2(flcmpd, TCG_CALL_NO_RWG_SE, i32, f64, f64) +DEF_HELPER_FLAGS_3(flcmps, TCG_CALL_NO_RWG_SE, i32, env, f32, f32) +DEF_HELPER_FLAGS_3(flcmpd, TCG_CALL_NO_RWG_SE, i32, env, f64, f64) DEF_HELPER_2(raise_exception, noreturn, env, int) DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cdd0a95c03..322319a128 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5584,7 +5584,7 @@ static bool trans_FLCMPs(DisasContext *dc, arg_FLCMPs *a) src1 = gen_load_fpr_F(dc, a->rs1); src2 = gen_load_fpr_F(dc, a->rs2); - gen_helper_flcmps(cpu_fcc[a->cc], src1, src2); + gen_helper_flcmps(cpu_fcc[a->cc], tcg_env, src1, src2); return advance_pc(dc); } @@ -5601,7 +5601,7 @@ static bool trans_FLCMPd(DisasContext *dc, arg_FLCMPd *a) src1 = gen_load_fpr_D(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); - gen_helper_flcmpd(cpu_fcc[a->cc], src1, src2); + gen_helper_flcmpd(cpu_fcc[a->cc], tcg_env, src1, src2); return advance_pc(dc); } From 46eb7b92db497ee0d7529ac5356009d8152452c9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:03 +0000 Subject: [PATCH 0139/2892] target/ppc: Use env->fp_status in helper_compute_fprf functions In the helper_compute_fprf functions, we pass a dummy float_status in to the is_signaling_nan() function. This is unnecessary, because we have convenient access to the CPU env pointer here and that is already set up with the correct values for the snan_bit_is_one and no_signaling_nans config settings. is_signaling_nan() doesn't ever update the fp_status with any exception flags, so there is no reason not to use env->fp_status here. Use env->fp_status instead of the dummy fp_status. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-34-peter.maydell@linaro.org --- target/ppc/fpu_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 230466a87f..d93cfed17b 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -155,8 +155,7 @@ void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ } else if (tp##_is_infinity(arg)) { \ fprf = neg ? 0x09 << FPSCR_FPRF : 0x05 << FPSCR_FPRF; \ } else { \ - float_status dummy = { }; /* snan_bit_is_one = 0 */ \ - if (tp##_is_signaling_nan(arg, &dummy)) { \ + if (tp##_is_signaling_nan(arg, &env->fp_status)) { \ fprf = 0x00 << FPSCR_FPRF; \ } else { \ fprf = 0x11 << FPSCR_FPRF; \ From 47aa9001d8c88e75a20559d59f666878b77d1b16 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:03 +0000 Subject: [PATCH 0140/2892] target/arm: Copy entire float_status in is_ebf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that float_status has a bunch of fp parameters, it is easier to copy an existing structure than create one from scratch. Begin by copying the structure that corresponds to the FPSR and make only the adjustments required for BFloat16 semantics. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20241203203949.483774-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index e825d501a2..ad6f26545a 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2813,25 +2813,19 @@ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) * no effect on AArch32 instructions. */ bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; - *statusp = (float_status){ - .tininess_before_rounding = float_tininess_before_rounding, - .float_rounding_mode = float_round_to_odd_inf, - .flush_to_zero = true, - .flush_inputs_to_zero = true, - .default_nan_mode = true, - }; + + *statusp = env->vfp.fp_status; + set_default_nan_mode(true, statusp); if (ebf) { - float_status *fpst = &env->vfp.fp_status; - set_flush_to_zero(get_flush_to_zero(fpst), statusp); - set_flush_inputs_to_zero(get_flush_inputs_to_zero(fpst), statusp); - set_float_rounding_mode(get_float_rounding_mode(fpst), statusp); - /* EBF=1 needs to do a step with round-to-odd semantics */ *oddstatusp = *statusp; set_float_rounding_mode(float_round_to_odd, oddstatusp); + } else { + set_flush_to_zero(true, statusp); + set_flush_inputs_to_zero(true, statusp); + set_float_rounding_mode(float_round_to_odd_inf, statusp); } - return ebf; } From 1b2de0c3c08afdb27b24d9f03aa3ba7abca432c9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:04 +0000 Subject: [PATCH 0141/2892] fpu: Allow runtime choice of default NaN value Currently we hardcode the default NaN value in parts64_default_nan() using a compile-time ifdef ladder. This is awkward for two cases: * for single-QEMU-binary we can't hard-code target-specifics like this * for Arm FEAT_AFP the default NaN value depends on FPCR.AH (specifically the sign bit is different) Add a field to float_status to specify the default NaN value; fall back to the old ifdef behaviour if these are not set. The default NaN value is specified by setting a uint8_t to a pattern corresponding to the sign and upper fraction parts of the NaN; the lower bits of the fraction are set from bit 0 of the pattern. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-35-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 55 ++++++++++++++++++++------------- include/fpu/softfloat-helpers.h | 11 +++++++ include/fpu/softfloat-types.h | 10 ++++++ 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 9f913ce20a..b1ec534983 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -133,35 +133,46 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) { bool sign = 0; uint64_t frac; + uint8_t dnan_pattern = status->default_nan_pattern; + if (dnan_pattern == 0) { #if defined(TARGET_SPARC) || defined(TARGET_M68K) - /* !snan_bit_is_one, set all bits */ - frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1; -#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ + /* Sign bit clear, all frac bits set */ + dnan_pattern = 0b01111111; +#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ || defined(TARGET_MICROBLAZE) - /* !snan_bit_is_one, set sign and msb */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - sign = 1; + /* Sign bit set, most significant frac bit set */ + dnan_pattern = 0b11000000; #elif defined(TARGET_HPPA) - /* snan_bit_is_one, set msb-1. */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); + /* Sign bit clear, msb-1 frac bit set */ + dnan_pattern = 0b00100000; #elif defined(TARGET_HEXAGON) - sign = 1; - frac = ~0ULL; + /* Sign bit set, all frac bits set. */ + dnan_pattern = 0b11111111; #else - /* - * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, - * S390, SH4, TriCore, and Xtensa. Our other supported targets - * do not have floating-point. - */ - if (snan_bit_is_one(status)) { - /* set all bits other than msb */ - frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; - } else { - /* set msb */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - } + /* + * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, + * S390, SH4, TriCore, and Xtensa. Our other supported targets + * do not have floating-point. + */ + if (snan_bit_is_one(status)) { + /* sign bit clear, set all frac bits other than msb */ + dnan_pattern = 0b00111111; + } else { + /* sign bit clear, set frac msb */ + dnan_pattern = 0b01000000; + } #endif + } + assert(dnan_pattern != 0); + + sign = dnan_pattern >> 7; + /* + * Place default_nan_pattern [6:0] into bits [62:56], + * and replecate bit [0] down into [55:0] + */ + frac = deposit64(0, DECOMPOSED_BINARY_POINT - 7, 7, dnan_pattern); + frac = deposit64(frac, 0, DECOMPOSED_BINARY_POINT - 7, -(dnan_pattern & 1)); *p = (FloatParts64) { .cls = float_class_qnan, diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 10a6763532..dceee23c82 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -93,6 +93,12 @@ static inline void set_float_infzeronan_rule(FloatInfZeroNaNRule rule, status->float_infzeronan_rule = rule; } +static inline void set_float_default_nan_pattern(uint8_t dnan_pattern, + float_status *status) +{ + status->default_nan_pattern = dnan_pattern; +} + static inline void set_flush_to_zero(bool val, float_status *status) { status->flush_to_zero = val; @@ -154,6 +160,11 @@ static inline FloatInfZeroNaNRule get_float_infzeronan_rule(float_status *status return status->float_infzeronan_rule; } +static inline uint8_t get_float_default_nan_pattern(float_status *status) +{ + return status->default_nan_pattern; +} + static inline bool get_flush_to_zero(float_status *status) { return status->flush_to_zero; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 84ba4ed20e..79ca44dcc3 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -303,6 +303,16 @@ typedef struct float_status { /* should denormalised inputs go to zero and set the input_denormal flag? */ bool flush_inputs_to_zero; bool default_nan_mode; + /* + * The pattern to use for the default NaN. Here the high bit specifies + * the default NaN's sign bit, and bits 6..0 specify the high bits of the + * fractional part. The low bits of the fractional part are copies of bit 0. + * The exponent of the default NaN is (as for any NaN) always all 1s. + * Note that a value of 0 here is not a valid NaN. The target must set + * this to the correct non-zero value, or we will assert when trying to + * create a default NaN. + */ + uint8_t default_nan_pattern; /* * The flags below are not used on all specializations and may * constant fold away (see snan_bit_is_one()/no_signalling_nans() in From a264fd5a972afc5950da9135342e2dbc67ba5199 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:04 +0000 Subject: [PATCH 0142/2892] tests/fp: Set default NaN pattern explicitly Set the default NaN pattern explicitly for the tests/fp code. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-36-peter.maydell@linaro.org --- tests/fp/fp-bench.c | 1 + tests/fp/fp-test-log2.c | 1 + tests/fp/fp-test.c | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index 39d80c9038..eacb39b99c 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -495,6 +495,7 @@ static void run_bench(void) set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); + set_float_default_nan_pattern(0b01000000, &soft_status); f = bench_funcs[operation][precision]; g_assert(f); diff --git a/tests/fp/fp-test-log2.c b/tests/fp/fp-test-log2.c index de702c4c80..79f619cdea 100644 --- a/tests/fp/fp-test-log2.c +++ b/tests/fp/fp-test-log2.c @@ -71,6 +71,7 @@ int main(int ac, char **av) int i; set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); set_float_rounding_mode(float_round_nearest_even, &qsf); test.d = 0.0; diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index f290d523ab..c619e5dbf7 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -941,6 +941,7 @@ void run_test(void) */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); set_float_3nan_prop_rule(float_3nan_prop_s_cab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &qsf); genCases_setLevel(test_level); From 223f4f2e78bd0431d246370e82ae0ac545f0c8d8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:04 +0000 Subject: [PATCH 0143/2892] target/microblaze: Set default NaN pattern explicitly Set the default NaN pattern explicitly, and remove the ifdef from parts64_default_nan(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-37-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 3 +-- target/microblaze/cpu.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index b1ec534983..d77404f0c4 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -139,8 +139,7 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) /* Sign bit clear, all frac bits set */ dnan_pattern = 0b01111111; -#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ - || defined(TARGET_MICROBLAZE) +#elif defined(TARGET_I386) || defined(TARGET_X86_64) /* Sign bit set, most significant frac bit set */ dnan_pattern = 0b11000000; #elif defined(TARGET_HPPA) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 710eb1146c..0e1e22d1e8 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -207,6 +207,8 @@ static void mb_cpu_reset_hold(Object *obj, ResetType type) * this architecture. */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* Default NaN: sign bit set, most significant frac bit set */ + set_float_default_nan_pattern(0b11000000, &env->fp_status); #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ From f69da79196ab586a7cacbeecb6809ffa8d8e258c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:04 +0000 Subject: [PATCH 0144/2892] target/i386: Set default NaN pattern explicitly Set the default NaN pattern explicitly, and remove the ifdef from parts64_default_nan(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-38-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 3 --- target/i386/tcg/fpu_helper.c | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index d77404f0c4..452fe378cd 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -139,9 +139,6 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) /* Sign bit clear, all frac bits set */ dnan_pattern = 0b01111111; -#elif defined(TARGET_I386) || defined(TARGET_X86_64) - /* Sign bit set, most significant frac bit set */ - dnan_pattern = 0b11000000; #elif defined(TARGET_HPPA) /* Sign bit clear, msb-1 frac bit set */ dnan_pattern = 0b00100000; diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 4303b3356a..d0a1e2f3c8 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -181,6 +181,10 @@ void cpu_init_fp_statuses(CPUX86State *env) */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->sse_status); set_float_3nan_prop_rule(float_3nan_prop_abc, &env->sse_status); + /* Default NaN: sign bit set, most significant frac bit set */ + set_float_default_nan_pattern(0b11000000, &env->fp_status); + set_float_default_nan_pattern(0b11000000, &env->mmx_status); + set_float_default_nan_pattern(0b11000000, &env->sse_status); } static inline uint8_t save_exception_flags(CPUX86State *env) From e19d721cb1b3e98659ed9843b3116b13c77f132e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:05 +0000 Subject: [PATCH 0145/2892] target/hppa: Set default NaN pattern explicitly Set the default NaN pattern explicitly, and remove the ifdef from parts64_default_nan(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-39-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 3 --- target/hppa/fpu_helper.c | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 452fe378cd..b5ec1944d1 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -139,9 +139,6 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) /* Sign bit clear, all frac bits set */ dnan_pattern = 0b01111111; -#elif defined(TARGET_HPPA) - /* Sign bit clear, msb-1 frac bit set */ - dnan_pattern = 0b00100000; #elif defined(TARGET_HEXAGON) /* Sign bit set, all frac bits set. */ dnan_pattern = 0b11111111; diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 69c4ce3783..239c027ec5 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -65,6 +65,8 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status); /* For inf * 0 + NaN, return the input NaN */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + /* Default NaN: sign bit clear, msb-1 frac bit set */ + set_float_default_nan_pattern(0b00100000, &env->fp_status); } void cpu_hppa_loaded_fr0(CPUHPPAState *env) From 369cb9cfe033b43a07e13265598c3df195ce2e4e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:05 +0000 Subject: [PATCH 0146/2892] target/alpha: Set default NaN pattern explicitly Set the default NaN pattern explicitly for the alpha target. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-40-peter.maydell@linaro.org --- target/alpha/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 5d75c941f7..70f67e6fd4 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -199,6 +199,8 @@ static void alpha_cpu_initfn(Object *obj) * operand in Fa. That is float_2nan_prop_ba. */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* Default NaN: sign bit clear, msb frac bit set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); #if defined(CONFIG_USER_ONLY) env->flags = ENV_FLAG_PS_USER | ENV_FLAG_FEN; cpu_alpha_store_fpcr(env, (uint64_t)(FPCR_INVD | FPCR_DZED | FPCR_OVFD From 29fbe4786ecc1613224e64e188c93067ced6e750 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:05 +0000 Subject: [PATCH 0147/2892] target/arm: Set default NaN pattern explicitly Set the default NaN pattern explicitly for the arm target. This includes setting it for the old linux-user nwfpe emulation. For nwfpe, our default doesn't match the real kernel, but we avoid making a behaviour change in this commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-41-peter.maydell@linaro.org --- linux-user/arm/nwfpe/fpa11.c | 5 +++++ target/arm/cpu.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/linux-user/arm/nwfpe/fpa11.c b/linux-user/arm/nwfpe/fpa11.c index 8356beb52c..0f1afbd91d 100644 --- a/linux-user/arm/nwfpe/fpa11.c +++ b/linux-user/arm/nwfpe/fpa11.c @@ -69,6 +69,11 @@ void resetFPA11(void) * this late date. */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &fpa11->fp_status); + /* + * Use the same default NaN value as Arm VFP. This doesn't match + * the Linux kernel's nwfpe emulation, which uses an all-1s value. + */ + set_float_default_nan_pattern(0b01000000, &fpa11->fp_status); } void SetRoundingMode(const unsigned int opcode) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c81f6df3fc..4f7e18eb8e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -179,6 +179,7 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, * the pseudocode function the arguments are in the order c, a, b. * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, * and the input NaN if it is signalling + * * Default NaN has sign bit clear, msb frac bit set */ static void arm_set_default_fp_behaviours(float_status *s) { @@ -186,6 +187,7 @@ static void arm_set_default_fp_behaviours(float_status *s) set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); + set_float_default_nan_pattern(0b01000000, s); } static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) From 7d582aa7cc5b834a5bdbdf8a2d2dd053cc4900c0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:05 +0000 Subject: [PATCH 0148/2892] target/loongarch: Set default NaN pattern explicitly Set the default NaN pattern explicitly for loongarch. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-42-peter.maydell@linaro.org --- target/loongarch/tcg/fpu_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index aea5e0fe5e..a83acf64b0 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -38,6 +38,8 @@ void restore_fp_status(CPULoongArchState *env) */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); set_float_3nan_prop_rule(float_3nan_prop_s_cab, &env->fp_status); + /* Default NaN: sign bit clear, msb frac bit set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); } int ieee_ex_to_loongarch(int xcpt) From 34f73db53d18152d25ae857a92a2f9cf206cbfd9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:06 +0000 Subject: [PATCH 0149/2892] target/m68k: Set default NaN pattern explicitly Set the default NaN pattern explicitly for m68k. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-43-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 2 +- target/m68k/cpu.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index b5ec1944d1..ecb7a52ae7 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -136,7 +136,7 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) uint8_t dnan_pattern = status->default_nan_pattern; if (dnan_pattern == 0) { -#if defined(TARGET_SPARC) || defined(TARGET_M68K) +#if defined(TARGET_SPARC) /* Sign bit clear, all frac bits set */ dnan_pattern = 0b01111111; #elif defined(TARGET_HEXAGON) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 13b76e2248..9de8ce6707 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -105,6 +105,8 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) * preceding paragraph for nonsignaling NaNs. */ set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); + /* Default NaN: sign bit clear, all frac bits set */ + set_float_default_nan_pattern(0b01111111, &env->fp_status); nan = floatx80_default_nan(&env->fp_status); for (i = 0; i < 8; i++) { From 5c3ba8105563010f3d4b1c79a90986f03368e534 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:06 +0000 Subject: [PATCH 0150/2892] target/mips: Set default NaN pattern explicitly Set the default NaN pattern explicitly for MIPS. Note that this is our only target which currently changes the default NaN at runtime (which it was previously doing indirectly when it changed the snan_bit_is_one setting). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-44-peter.maydell@linaro.org --- target/mips/fpu_helper.h | 7 +++++++ target/mips/msa.c | 3 +++ 2 files changed, 10 insertions(+) diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index 8ca0ca7ea3..6ad1e466cf 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -47,6 +47,13 @@ static inline void restore_snan_bit_mode(CPUMIPSState *env) set_float_infzeronan_rule(izn_rule, &env->active_fpu.fp_status); nan3_rule = nan2008 ? float_3nan_prop_s_cab : float_3nan_prop_s_abc; set_float_3nan_prop_rule(nan3_rule, &env->active_fpu.fp_status); + /* + * With nan2008, the default NaN value has the sign bit clear and the + * frac msb set; with the older mode, the sign bit is clear, and all + * frac bits except the msb are set. + */ + set_float_default_nan_pattern(nan2008 ? 0b01000000 : 0b00111111, + &env->active_fpu.fp_status); } diff --git a/target/mips/msa.c b/target/mips/msa.c index 93a9a87d76..fc77bfc7b9 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -81,4 +81,7 @@ void msa_reset(CPUMIPSState *env) /* Inf * 0 + NaN returns the input NaN */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->active_tc.msa_fp_status); + /* Default NaN: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, + &env->active_tc.msa_fp_status); } From 328dea12ecc6dd44381051013d34c2a2274cfe37 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:06 +0000 Subject: [PATCH 0151/2892] target/openrisc: Set default NaN pattern explicitly Set the default NaN pattern explicitly for openrisc. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-45-peter.maydell@linaro.org --- target/openrisc/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index b96561d1f2..3ccf85e95f 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -111,6 +111,8 @@ static void openrisc_cpu_reset_hold(Object *obj, ResetType type) */ set_float_2nan_prop_rule(float_2nan_prop_x87, &cpu->env.fp_status); + /* Default NaN: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &cpu->env.fp_status); #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; From 236310812c209d641e6a386436934d01d1170ad3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:06 +0000 Subject: [PATCH 0152/2892] target/ppc: Set default NaN pattern explicitly Set the default NaN pattern explicitly for ppc. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-46-peter.maydell@linaro.org --- target/ppc/cpu_init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index eb9d7b1370..1253dbf622 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7286,6 +7286,10 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->vec_status); + /* Default NaN: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + set_float_default_nan_pattern(0b01000000, &env->vec_status); + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; From bfff809d711a9d3b2ae290b0a27063fe15ec4591 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:06 +0000 Subject: [PATCH 0153/2892] target/sh4: Set default NaN pattern explicitly Set the default NaN pattern explicitly for sh4. Note that sh4 is one of the only three targets (the others being HPPA and sometimes MIPS) that has snan_bit_is_one set. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-47-peter.maydell@linaro.org --- target/sh4/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 8f07261dcf..d5008859b8 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -127,6 +127,8 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); + /* sign bit clear, set all frac bits other than msb */ + set_float_default_nan_pattern(0b00111111, &env->fp_status); } static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) From 031af9cd9bc750a9d2f7c96535f87c86ffe80a0d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0154/2892] target/rx: Set default NaN pattern explicitly Set the default NaN pattern explicitly for rx. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-48-peter.maydell@linaro.org --- target/rx/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 65a74ce720..69ec0bc7b3 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -100,6 +100,8 @@ static void rx_cpu_reset_hold(Object *obj, ResetType type) * then prefer dest over source", which is float_2nan_prop_s_ab. */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* Default NaN value: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); } static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) From 5ab49c3a0991447d2f5b32e2b453ed111e4c968f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0155/2892] target/s390x: Set default NaN pattern explicitly Set the default NaN pattern explicitly for s390x. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-49-peter.maydell@linaro.org --- target/s390x/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index e74055bad7..adb27504ad 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -209,6 +209,8 @@ static void s390_cpu_reset_hold(Object *obj, ResetType type) set_float_3nan_prop_rule(float_3nan_prop_s_abc, &env->fpu_status); set_float_infzeronan_rule(float_infzeronan_dnan_always, &env->fpu_status); + /* Default NaN value: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fpu_status); /* fall through */ case RESET_TYPE_S390_CPU_NORMAL: env->psw.mask &= ~PSW_MASK_RI; From 45fb2cd61eee6d7f500eb96dba73023cf0057b40 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0156/2892] target/sparc: Set default NaN pattern explicitly Set the default NaN pattern explicitly for SPARC, and remove the ifdef from parts64_default_nan. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-50-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 5 +---- target/sparc/cpu.c | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index ecb7a52ae7..06185237d0 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -136,10 +136,7 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) uint8_t dnan_pattern = status->default_nan_pattern; if (dnan_pattern == 0) { -#if defined(TARGET_SPARC) - /* Sign bit clear, all frac bits set */ - dnan_pattern = 0b01111111; -#elif defined(TARGET_HEXAGON) +#if defined(TARGET_HEXAGON) /* Sign bit set, all frac bits set. */ dnan_pattern = 0b11111111; #else diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 0f2997a85e..6b66ecb3f5 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -818,6 +818,8 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) set_float_3nan_prop_rule(float_3nan_prop_s_cba, &env->fp_status); /* For inf * 0 + NaN, return the input NaN */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + /* Default NaN value: sign bit clear, all frac bits set */ + set_float_default_nan_pattern(0b01111111, &env->fp_status); cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { From b9aa1e5658dacca25b2970bba6f3bf79351ca46c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0157/2892] target/xtensa: Set default NaN pattern explicitly Set the default NaN pattern explicitly for xtensa. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-51-peter.maydell@linaro.org --- target/xtensa/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 3163b75823..0d4d79b58b 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -136,6 +136,8 @@ static void xtensa_cpu_reset_hold(Object *obj, ResetType type) /* For inf * 0 + NaN, return the input NaN */ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); set_no_signaling_nans(!dfpu, &env->fp_status); + /* Default NaN value: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); xtensa_use_first_nan(env, !dfpu); } From 38ea9ade29e1e3208dc0e82708388c0a1d73ebf2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0158/2892] target/hexagon: Set default NaN pattern explicitly Set the default NaN pattern explicitly for hexagon. Remove the ifdef from parts64_default_nan(); the only remaining unconverted targets all use the default case. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-52-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 5 ----- target/hexagon/cpu.c | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 06185237d0..5954a6213b 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -136,10 +136,6 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) uint8_t dnan_pattern = status->default_nan_pattern; if (dnan_pattern == 0) { -#if defined(TARGET_HEXAGON) - /* Sign bit set, all frac bits set. */ - dnan_pattern = 0b11111111; -#else /* * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, * S390, SH4, TriCore, and Xtensa. Our other supported targets @@ -152,7 +148,6 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) /* sign bit clear, set frac msb */ dnan_pattern = 0b01000000; } -#endif } assert(dnan_pattern != 0); diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 020038fc49..c9aa9408ec 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -286,6 +286,8 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) set_default_nan_mode(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); + /* Default NaN value: sign bit set, all frac bits set */ + set_float_default_nan_pattern(0b11111111, &env->fp_status); } static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info) From 3d3d399e76c204c718f3ec6ef48fd0bb303070ab Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0159/2892] target/riscv: Set default NaN pattern explicitly Set the default NaN pattern explicitly for riscv. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-53-peter.maydell@linaro.org --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f219f0c3b5..80b09952e7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1022,6 +1022,8 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) cs->exception_index = RISCV_EXCP_NONE; env->load_res = -1; set_default_nan_mode(1, &env->fp_status); + /* Default NaN value: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); env->vill = true; #ifndef CONFIG_USER_ONLY From d921f8fd56f144557413f8e3e35e4c12064e4091 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:07 +0000 Subject: [PATCH 0160/2892] target/tricore: Set default NaN pattern explicitly Set the default NaN pattern explicitly for tricore. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-54-peter.maydell@linaro.org --- target/tricore/helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 7014255f77..e8b0ec5161 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -117,6 +117,8 @@ void fpu_set_state(CPUTriCoreState *env) set_flush_to_zero(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); set_default_nan_mode(1, &env->fp_status); + /* Default NaN pattern: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); } uint32_t psw_read(CPUTriCoreState *env) From 86bb2f44cd6f4a45132beffcdacdf3b43d836042 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0161/2892] fpu: Remove default handling for dnan_pattern Now that all our targets have bene converted to explicitly specify their pattern for the default NaN value we can remove the remaining fallback code in parts64_default_nan(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241202131347.498124-55-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 5954a6213b..e075c47889 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -135,20 +135,6 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) uint64_t frac; uint8_t dnan_pattern = status->default_nan_pattern; - if (dnan_pattern == 0) { - /* - * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, - * S390, SH4, TriCore, and Xtensa. Our other supported targets - * do not have floating-point. - */ - if (snan_bit_is_one(status)) { - /* sign bit clear, set all frac bits other than msb */ - dnan_pattern = 0b00111111; - } else { - /* sign bit clear, set frac msb */ - dnan_pattern = 0b01000000; - } - } assert(dnan_pattern != 0); sign = dnan_pattern >> 7; From f73bd7503def86ab322a8df560b00ef67e08ce6e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0162/2892] softfloat: Inline pickNaNMulAdd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline pickNaNMulAdd into its only caller. This makes one assert redundant with the immediately preceding IF. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241203203949.483774-3-richard.henderson@linaro.org [PMM: keep comment from old code in new location] Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 41 +++++++++++++++++++++++++- fpu/softfloat-specialize.c.inc | 54 ---------------------------------- 2 files changed, 40 insertions(+), 55 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 655b7d9da5..c1a97c35b2 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -79,9 +79,48 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, } if (s->default_nan_mode) { + /* + * We guarantee not to require the target to tell us how to + * pick a NaN if we're always returning the default NaN. + * But if we're not in default-NaN mode then the target must + * specify. + */ which = 3; + } else if (infzero) { + /* + * Inf * 0 + NaN -- some implementations return the + * default NaN here, and some return the input NaN. + */ + switch (s->float_infzeronan_rule) { + case float_infzeronan_dnan_never: + which = 2; + break; + case float_infzeronan_dnan_always: + which = 3; + break; + case float_infzeronan_dnan_if_qnan: + which = is_qnan(c->cls) ? 3 : 2; + break; + default: + g_assert_not_reached(); + } } else { - which = pickNaNMulAdd(a->cls, b->cls, c->cls, infzero, have_snan, s); + FloatClass cls[3] = { a->cls, b->cls, c->cls }; + Float3NaNPropRule rule = s->float_3nan_prop_rule; + + assert(rule != float_3nan_prop_none); + if (have_snan && (rule & R_3NAN_SNAN_MASK)) { + /* We have at least one SNaN input and should prefer it */ + do { + which = rule & R_3NAN_1ST_MASK; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_snan(cls[which])); + } else { + do { + which = rule & R_3NAN_1ST_MASK; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_nan(cls[which])); + } } if (which == 3) { diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index e075c47889..f26458eaa3 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -448,60 +448,6 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, } } -/*---------------------------------------------------------------------------- -| Select which NaN to propagate for a three-input operation. -| For the moment we assume that no CPU needs the 'larger significand' -| information. -| Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN -*----------------------------------------------------------------------------*/ -static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, - bool infzero, bool have_snan, float_status *status) -{ - FloatClass cls[3] = { a_cls, b_cls, c_cls }; - Float3NaNPropRule rule = status->float_3nan_prop_rule; - int which; - - /* - * We guarantee not to require the target to tell us how to - * pick a NaN if we're always returning the default NaN. - * But if we're not in default-NaN mode then the target must - * specify. - */ - assert(!status->default_nan_mode); - - if (infzero) { - /* - * Inf * 0 + NaN -- some implementations return the default NaN here, - * and some return the input NaN. - */ - switch (status->float_infzeronan_rule) { - case float_infzeronan_dnan_never: - return 2; - case float_infzeronan_dnan_always: - return 3; - case float_infzeronan_dnan_if_qnan: - return is_qnan(c_cls) ? 3 : 2; - default: - g_assert_not_reached(); - } - } - - assert(rule != float_3nan_prop_none); - if (have_snan && (rule & R_3NAN_SNAN_MASK)) { - /* We have at least one SNaN input and should prefer it */ - do { - which = rule & R_3NAN_1ST_MASK; - rule >>= R_3NAN_1ST_LENGTH; - } while (!is_snan(cls[which])); - } else { - do { - which = rule & R_3NAN_1ST_MASK; - rule >>= R_3NAN_1ST_LENGTH; - } while (!is_nan(cls[which])); - } - return which; -} - /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. From e00286187630b81fb3360d560ff7a6ca90f7191f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0163/2892] softfloat: Use goto for default nan case in pick_nan_muladd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove "3" as a special case for which and simply branch to return the desired value. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241203203949.483774-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index c1a97c35b2..be7e93127d 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -85,7 +85,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, * But if we're not in default-NaN mode then the target must * specify. */ - which = 3; + goto default_nan; } else if (infzero) { /* * Inf * 0 + NaN -- some implementations return the @@ -93,17 +93,18 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, */ switch (s->float_infzeronan_rule) { case float_infzeronan_dnan_never: - which = 2; break; case float_infzeronan_dnan_always: - which = 3; - break; + goto default_nan; case float_infzeronan_dnan_if_qnan: - which = is_qnan(c->cls) ? 3 : 2; + if (is_qnan(c->cls)) { + goto default_nan; + } break; default: g_assert_not_reached(); } + which = 2; } else { FloatClass cls[3] = { a->cls, b->cls, c->cls }; Float3NaNPropRule rule = s->float_3nan_prop_rule; @@ -123,11 +124,6 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, } } - if (which == 3) { - parts_default_nan(a, s); - return a; - } - switch (which) { case 0: break; @@ -144,6 +140,10 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, parts_silence_nan(a, s); } return a; + + default_nan: + parts_default_nan(a, s); + return a; } /* From 229416c67e8faed47dfa1d26777e71df2cc3b320 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0164/2892] softfloat: Remove which from parts_pick_nan_muladd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assign the pointer return value to 'a' directly, rather than going through an intermediary index. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241203203949.483774-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index be7e93127d..525db61741 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -65,9 +65,9 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, FloatPartsN *c, float_status *s, int ab_mask, int abc_mask) { - int which; bool infzero = (ab_mask == float_cmask_infzero); bool have_snan = (abc_mask & float_cmask_snan); + FloatPartsN *ret; if (unlikely(have_snan)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); @@ -104,42 +104,30 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, default: g_assert_not_reached(); } - which = 2; + ret = c; } else { - FloatClass cls[3] = { a->cls, b->cls, c->cls }; + FloatPartsN *val[3] = { a, b, c }; Float3NaNPropRule rule = s->float_3nan_prop_rule; assert(rule != float_3nan_prop_none); if (have_snan && (rule & R_3NAN_SNAN_MASK)) { /* We have at least one SNaN input and should prefer it */ do { - which = rule & R_3NAN_1ST_MASK; + ret = val[rule & R_3NAN_1ST_MASK]; rule >>= R_3NAN_1ST_LENGTH; - } while (!is_snan(cls[which])); + } while (!is_snan(ret->cls)); } else { do { - which = rule & R_3NAN_1ST_MASK; + ret = val[rule & R_3NAN_1ST_MASK]; rule >>= R_3NAN_1ST_LENGTH; - } while (!is_nan(cls[which])); + } while (!is_nan(ret->cls)); } } - switch (which) { - case 0: - break; - case 1: - a = b; - break; - case 2: - a = c; - break; - default: - g_assert_not_reached(); + if (is_snan(ret->cls)) { + parts_silence_nan(ret, s); } - if (is_snan(a->cls)) { - parts_silence_nan(a, s); - } - return a; + return ret; default_nan: parts_default_nan(a, s); From 8704c753e8fe3155e22244f2c23e253a73aa4565 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0165/2892] softfloat: Pad array size in pick_nan_muladd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While all indices into val[] should be in [0-2], the mask applied is two bits. To help static analysis see there is no possibility of read beyond the end of the array, pad the array to 4 entries, with the final being (implicitly) NULL. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241203203949.483774-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 525db61741..5fcdbc87fd 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -106,7 +106,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, } ret = c; } else { - FloatPartsN *val[3] = { a, b, c }; + FloatPartsN *val[R_3NAN_1ST_MASK + 1] = { a, b, c }; Float3NaNPropRule rule = s->float_3nan_prop_rule; assert(rule != float_3nan_prop_none); From 313938c105946dacfc886217b47310adb01194c3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0166/2892] softfloat: Move propagateFloatx80NaN to softfloat.c This function is part of the public interface and is not "specialized" to any target in any way. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20241203203949.483774-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-specialize.c.inc | 52 ---------------------------------- fpu/softfloat.c | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index f26458eaa3..f7a320f6ff 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -551,58 +551,6 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status) return a; } -/*---------------------------------------------------------------------------- -| Takes two extended double-precision floating-point values `a' and `b', one -| of which is a NaN, and returns the appropriate NaN result. If either `a' or -| `b' is a signaling NaN, the invalid exception is raised. -*----------------------------------------------------------------------------*/ - -floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) -{ - bool aIsLargerSignificand; - FloatClass a_cls, b_cls; - - /* This is not complete, but is good enough for pickNaN. */ - a_cls = (!floatx80_is_any_nan(a) - ? float_class_normal - : floatx80_is_signaling_nan(a, status) - ? float_class_snan - : float_class_qnan); - b_cls = (!floatx80_is_any_nan(b) - ? float_class_normal - : floatx80_is_signaling_nan(b, status) - ? float_class_snan - : float_class_qnan); - - if (is_snan(a_cls) || is_snan(b_cls)) { - float_raise(float_flag_invalid, status); - } - - if (status->default_nan_mode) { - return floatx80_default_nan(status); - } - - if (a.low < b.low) { - aIsLargerSignificand = 0; - } else if (b.low < a.low) { - aIsLargerSignificand = 1; - } else { - aIsLargerSignificand = (a.high < b.high) ? 1 : 0; - } - - if (pickNaN(a_cls, b_cls, aIsLargerSignificand, status)) { - if (is_snan(b_cls)) { - return floatx80_silence_nan(b, status); - } - return b; - } else { - if (is_snan(a_cls)) { - return floatx80_silence_nan(a, status); - } - return a; - } -} - /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 027a8e576d..6ba1cfd32a 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -4920,6 +4920,58 @@ void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr, *zExpPtr = 1 - shiftCount; } +/*---------------------------------------------------------------------------- +| Takes two extended double-precision floating-point values `a' and `b', one +| of which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) +{ + bool aIsLargerSignificand; + FloatClass a_cls, b_cls; + + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!floatx80_is_any_nan(a) + ? float_class_normal + : floatx80_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!floatx80_is_any_nan(b) + ? float_class_normal + : floatx80_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); + + if (is_snan(a_cls) || is_snan(b_cls)) { + float_raise(float_flag_invalid, status); + } + + if (status->default_nan_mode) { + return floatx80_default_nan(status); + } + + if (a.low < b.low) { + aIsLargerSignificand = 0; + } else if (b.low < a.low) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; + } + + if (pickNaN(a_cls, b_cls, aIsLargerSignificand, status)) { + if (is_snan(b_cls)) { + return floatx80_silence_nan(b, status); + } + return b; + } else { + if (is_snan(a_cls)) { + return floatx80_silence_nan(a, status); + } + return a; + } +} + /*---------------------------------------------------------------------------- | Takes an abstract floating-point value having sign `zSign', exponent `zExp', | and extended significand formed by the concatenation of `zSig0' and `zSig1', From 1b34f934ddefc3bdc4322694c2726ddc4d9ebd45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0167/2892] softfloat: Use parts_pick_nan in propagateFloatx80NaN Unpacking and repacking the parts may be slightly more work than we did before, but we get to reuse more code. For a code path handling exceptional values, this is an improvement. Signed-off-by: Richard Henderson Message-id: 20241203203949.483774-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell --- fpu/softfloat.c | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 6ba1cfd32a..8de8d5f342 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -4928,48 +4928,15 @@ void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr, floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) { - bool aIsLargerSignificand; - FloatClass a_cls, b_cls; + FloatParts128 pa, pb, *pr; - /* This is not complete, but is good enough for pickNaN. */ - a_cls = (!floatx80_is_any_nan(a) - ? float_class_normal - : floatx80_is_signaling_nan(a, status) - ? float_class_snan - : float_class_qnan); - b_cls = (!floatx80_is_any_nan(b) - ? float_class_normal - : floatx80_is_signaling_nan(b, status) - ? float_class_snan - : float_class_qnan); - - if (is_snan(a_cls) || is_snan(b_cls)) { - float_raise(float_flag_invalid, status); - } - - if (status->default_nan_mode) { + if (!floatx80_unpack_canonical(&pa, a, status) || + !floatx80_unpack_canonical(&pb, b, status)) { return floatx80_default_nan(status); } - if (a.low < b.low) { - aIsLargerSignificand = 0; - } else if (b.low < a.low) { - aIsLargerSignificand = 1; - } else { - aIsLargerSignificand = (a.high < b.high) ? 1 : 0; - } - - if (pickNaN(a_cls, b_cls, aIsLargerSignificand, status)) { - if (is_snan(b_cls)) { - return floatx80_silence_nan(b, status); - } - return b; - } else { - if (is_snan(a_cls)) { - return floatx80_silence_nan(a, status); - } - return a; - } + pr = parts_pick_nan(&pa, &pb, status); + return floatx80_round_pack_canonical(pr, status); } /*---------------------------------------------------------------------------- From 04cbb4acc60c98c4080df6d03a09bbca0e408f67 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:08 +0000 Subject: [PATCH 0168/2892] softfloat: Inline pickNaN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline pickNaN into its only caller. This makes one assert redundant with the immediately preceding IF. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241203203949.483774-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 82 +++++++++++++++++++++++++---- fpu/softfloat-specialize.c.inc | 96 ---------------------------------- 2 files changed, 73 insertions(+), 105 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 5fcdbc87fd..a1b148e90b 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -39,24 +39,88 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s) static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, float_status *s) { + int cmp, which; + if (is_snan(a->cls) || is_snan(b->cls)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); } if (s->default_nan_mode) { parts_default_nan(a, s); - } else { - int cmp = frac_cmp(a, b); - if (cmp == 0) { - cmp = a->sign < b->sign; - } + return a; + } - if (pickNaN(a->cls, b->cls, cmp > 0, s)) { - a = b; - } + cmp = frac_cmp(a, b); + if (cmp == 0) { + cmp = a->sign < b->sign; + } + + switch (s->float_2nan_prop_rule) { + case float_2nan_prop_s_ab: if (is_snan(a->cls)) { - parts_silence_nan(a, s); + which = 0; + } else if (is_snan(b->cls)) { + which = 1; + } else if (is_qnan(a->cls)) { + which = 0; + } else { + which = 1; } + break; + case float_2nan_prop_s_ba: + if (is_snan(b->cls)) { + which = 1; + } else if (is_snan(a->cls)) { + which = 0; + } else if (is_qnan(b->cls)) { + which = 1; + } else { + which = 0; + } + break; + case float_2nan_prop_ab: + which = is_nan(a->cls) ? 0 : 1; + break; + case float_2nan_prop_ba: + which = is_nan(b->cls) ? 1 : 0; + break; + case float_2nan_prop_x87: + /* + * This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + if (is_snan(a->cls)) { + if (is_snan(b->cls)) { + which = cmp > 0 ? 0 : 1; + } else { + which = is_qnan(b->cls) ? 1 : 0; + } + } else if (is_qnan(a->cls)) { + if (is_snan(b->cls) || !is_qnan(b->cls)) { + which = 0; + } else { + which = cmp > 0 ? 0 : 1; + } + } else { + which = 1; + } + break; + default: + g_assert_not_reached(); + } + + if (which) { + a = b; + } + if (is_snan(a->cls)) { + parts_silence_nan(a, s); } return a; } diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index f7a320f6ff..cbbbab52ba 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -352,102 +352,6 @@ bool float32_is_signaling_nan(float32 a_, float_status *status) } } -/*---------------------------------------------------------------------------- -| Select which NaN to propagate for a two-input operation. -| IEEE754 doesn't specify all the details of this, so the -| algorithm is target-specific. -| The routine is passed various bits of information about the -| two NaNs and should return 0 to select NaN a and 1 for NaN b. -| Note that signalling NaNs are always squashed to quiet NaNs -| by the caller, by calling floatXX_silence_nan() before -| returning them. -| -| aIsLargerSignificand is only valid if both a and b are NaNs -| of some kind, and is true if a has the larger significand, -| or if both a and b have the same significand but a is -| positive but b is negative. It is only needed for the x87 -| tie-break rule. -*----------------------------------------------------------------------------*/ - -static int pickNaN(FloatClass a_cls, FloatClass b_cls, - bool aIsLargerSignificand, float_status *status) -{ - /* - * We guarantee not to require the target to tell us how to - * pick a NaN if we're always returning the default NaN. - * But if we're not in default-NaN mode then the target must - * specify via set_float_2nan_prop_rule(). - */ - assert(!status->default_nan_mode); - - switch (status->float_2nan_prop_rule) { - case float_2nan_prop_s_ab: - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } - break; - case float_2nan_prop_s_ba: - if (is_snan(b_cls)) { - return 1; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 0; - } - break; - case float_2nan_prop_ab: - if (is_nan(a_cls)) { - return 0; - } else { - return 1; - } - break; - case float_2nan_prop_ba: - if (is_nan(b_cls)) { - return 1; - } else { - return 0; - } - break; - case float_2nan_prop_x87: - /* - * This implements x87 NaN propagation rules: - * SNaN + QNaN => return the QNaN - * two SNaNs => return the one with the larger significand, silenced - * two QNaNs => return the one with the larger significand - * SNaN and a non-NaN => return the SNaN, silenced - * QNaN and a non-NaN => return the QNaN - * - * If we get down to comparing significands and they are the same, - * return the NaN with the positive sign bit (if any). - */ - if (is_snan(a_cls)) { - if (is_snan(b_cls)) { - return aIsLargerSignificand ? 0 : 1; - } - return is_qnan(b_cls) ? 1 : 0; - } else if (is_qnan(a_cls)) { - if (is_snan(b_cls) || !is_qnan(b_cls)) { - return 0; - } else { - return aIsLargerSignificand ? 0 : 1; - } - } else { - return 1; - } - default: - g_assert_not_reached(); - } -} - /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. From 2da7553f2ee818e98e69136ab02106f3bccddf68 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:09 +0000 Subject: [PATCH 0169/2892] softfloat: Share code between parts_pick_nan cases Remember if there was an SNaN, and use that to simplify float_2nan_prop_s_{ab,ba} to only the snan component. Then, fall through to the corresponding float_2nan_prop_{ab,ba} case to handle any remaining nans, which must be quiet. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20241203203949.483774-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index a1b148e90b..3c77dcbb15 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -39,10 +39,12 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s) static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, float_status *s) { + bool have_snan = false; int cmp, which; if (is_snan(a->cls) || is_snan(b->cls)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); + have_snan = true; } if (s->default_nan_mode) { @@ -57,30 +59,20 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, switch (s->float_2nan_prop_rule) { case float_2nan_prop_s_ab: - if (is_snan(a->cls)) { - which = 0; - } else if (is_snan(b->cls)) { - which = 1; - } else if (is_qnan(a->cls)) { - which = 0; - } else { - which = 1; + if (have_snan) { + which = is_snan(a->cls) ? 0 : 1; + break; } - break; - case float_2nan_prop_s_ba: - if (is_snan(b->cls)) { - which = 1; - } else if (is_snan(a->cls)) { - which = 0; - } else if (is_qnan(b->cls)) { - which = 1; - } else { - which = 0; - } - break; + /* fall through */ case float_2nan_prop_ab: which = is_nan(a->cls) ? 0 : 1; break; + case float_2nan_prop_s_ba: + if (have_snan) { + which = is_snan(b->cls) ? 1 : 0; + break; + } + /* fall through */ case float_2nan_prop_ba: which = is_nan(b->cls) ? 1 : 0; break; From 9778115ed780c8d8b027dea4125afb3890511797 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:09 +0000 Subject: [PATCH 0170/2892] softfloat: Sink frac_cmp in parts_pick_nan until needed Move the fractional comparison to the end of the float_2nan_prop_x87 case. This is not required for any other 2nan propagation rule. Reorganize the x87 case itself to break out of the switch when the fractional comparison is not required. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20241203203949.483774-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 3c77dcbb15..abe24aeaa0 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -52,11 +52,6 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, return a; } - cmp = frac_cmp(a, b); - if (cmp == 0) { - cmp = a->sign < b->sign; - } - switch (s->float_2nan_prop_rule) { case float_2nan_prop_s_ab: if (have_snan) { @@ -89,20 +84,24 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, * return the NaN with the positive sign bit (if any). */ if (is_snan(a->cls)) { - if (is_snan(b->cls)) { - which = cmp > 0 ? 0 : 1; - } else { + if (!is_snan(b->cls)) { which = is_qnan(b->cls) ? 1 : 0; + break; } } else if (is_qnan(a->cls)) { if (is_snan(b->cls) || !is_qnan(b->cls)) { which = 0; - } else { - which = cmp > 0 ? 0 : 1; + break; } } else { which = 1; + break; } + cmp = frac_cmp(a, b); + if (cmp == 0) { + cmp = a->sign < b->sign; + } + which = cmp > 0 ? 0 : 1; break; default: g_assert_not_reached(); From cc87d3d3818212291cb3dbcbf6b9db8680b4e965 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 15:31:09 +0000 Subject: [PATCH 0171/2892] softfloat: Replace WHICH with RET in parts_pick_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the "index" selecting between A and B with a result variable of the proper type. This improves clarity within the function. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241203203949.483774-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- fpu/softfloat-parts.c.inc | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index abe24aeaa0..ba8de7be76 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -40,7 +40,8 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, float_status *s) { bool have_snan = false; - int cmp, which; + FloatPartsN *ret; + int cmp; if (is_snan(a->cls) || is_snan(b->cls)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); @@ -55,21 +56,21 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, switch (s->float_2nan_prop_rule) { case float_2nan_prop_s_ab: if (have_snan) { - which = is_snan(a->cls) ? 0 : 1; + ret = is_snan(a->cls) ? a : b; break; } /* fall through */ case float_2nan_prop_ab: - which = is_nan(a->cls) ? 0 : 1; + ret = is_nan(a->cls) ? a : b; break; case float_2nan_prop_s_ba: if (have_snan) { - which = is_snan(b->cls) ? 1 : 0; + ret = is_snan(b->cls) ? b : a; break; } /* fall through */ case float_2nan_prop_ba: - which = is_nan(b->cls) ? 1 : 0; + ret = is_nan(b->cls) ? b : a; break; case float_2nan_prop_x87: /* @@ -85,35 +86,32 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, */ if (is_snan(a->cls)) { if (!is_snan(b->cls)) { - which = is_qnan(b->cls) ? 1 : 0; + ret = is_qnan(b->cls) ? b : a; break; } } else if (is_qnan(a->cls)) { if (is_snan(b->cls) || !is_qnan(b->cls)) { - which = 0; + ret = a; break; } } else { - which = 1; + ret = b; break; } cmp = frac_cmp(a, b); if (cmp == 0) { cmp = a->sign < b->sign; } - which = cmp > 0 ? 0 : 1; + ret = cmp > 0 ? a : b; break; default: g_assert_not_reached(); } - if (which) { - a = b; + if (is_snan(ret->cls)) { + parts_silence_nan(ret, s); } - if (is_snan(a->cls)) { - parts_silence_nan(a, s); - } - return a; + return ret; } static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, From a6240a2ec94ef12e1430e9b3baa06e552634ca8f Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Wed, 11 Dec 2024 15:31:09 +0000 Subject: [PATCH 0172/2892] MAINTAINERS: update email address for Leif Lindholm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm migrating to Qualcomm's new open source email infrastructure, so update my email address, and update the mailmap to match. Signed-off-by: Leif Lindholm Reviewed-by: Leif Lindholm Reviewed-by: Brian Cain Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20241205114047.1125842-1-leif.lindholm@oss.qualcomm.com Signed-off-by: Peter Maydell --- .mailmap | 5 +++-- MAINTAINERS | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index 727ce204b2..5f6df414e1 100644 --- a/.mailmap +++ b/.mailmap @@ -87,8 +87,9 @@ Huacai Chen Huacai Chen James Hogan Juan Quintela -Leif Lindholm -Leif Lindholm +Leif Lindholm +Leif Lindholm +Leif Lindholm Luc Michel Luc Michel Luc Michel diff --git a/MAINTAINERS b/MAINTAINERS index aaf0505a21..9ae6a78ae9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -915,7 +915,7 @@ F: include/hw/ssi/imx_spi.h SBSA-REF M: Radoslaw Biernacki M: Peter Maydell -R: Leif Lindholm +R: Leif Lindholm R: Marcin Juszkiewicz L: qemu-arm@nongnu.org S: Maintained From 1abe28d519239eea5cf9620bb13149423e5665f8 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 11 Dec 2024 15:31:09 +0000 Subject: [PATCH 0173/2892] MAINTAINERS: Add correct email address for Vikram Garhwal Previously, maintainer role was paused due to inactive email id. Commit id: c009d715721861984c4987bcc78b7ee183e86d75. Signed-off-by: Vikram Garhwal Reviewed-by: Francisco Iglesias Message-id: 20241204184205.12952-1-vikram.garhwal@bytedance.com Signed-off-by: Peter Maydell --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9ae6a78ae9..1d2003a9a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1914,6 +1914,7 @@ F: tests/qtest/fuzz-sb16-test.c Xilinx CAN M: Francisco Iglesias +M: Vikram Garhwal S: Maintained F: hw/net/can/xlnx-* F: include/hw/net/xlnx-* @@ -2673,6 +2674,7 @@ F: include/hw/rx/ CAN bus subsystem and hardware M: Pavel Pisa M: Francisco Iglesias +M: Vikram Garhwal S: Maintained W: https://canbus.pages.fel.cvut.cz/ F: net/can/* From 94aa1a0e30065e06ee81cd9f71cccb0c393ede3f Mon Sep 17 00:00:00 2001 From: Dmitry Frolov Date: Wed, 13 Nov 2024 12:43:40 +0300 Subject: [PATCH 0174/2892] tests/qtest: add TIMEOUT_MULTIPLIER Some tests need more time when qemu is built with "--enable-asan --enable-ubsan" As was discussed here: https://patchew.org/QEMU/20241112120100.176492-2-frolov@swemel.ru/ TIMEOUT_MULTIPLIER enviroment variable will be a useful option, allowing non-invasive timeouts increasing for a specific build. Signed-off-by: Dmitry Frolov Tested-by: Fabiano Rosas [changed from ifndef to ?=] Signed-off-by: Fabiano Rosas --- scripts/mtest2make.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index eb01a05ddb..2ef375fc6f 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -27,7 +27,7 @@ SPEED = quick .speed.slow = $(foreach s,$(sort $(filter-out %-thorough, $1)), --suite $s) .speed.thorough = $(foreach s,$(sort $1), --suite $s) -TIMEOUT_MULTIPLIER = 1 +TIMEOUT_MULTIPLIER ?= 1 .mtestargs = --no-rebuild -t $(TIMEOUT_MULTIPLIER) ifneq ($(SPEED), quick) .mtestargs += --setup $(SPEED) From 6b76264ed0fed402df09aab8edacf1ee03d87c7f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Nov 2024 16:50:39 +0000 Subject: [PATCH 0175/2892] tests/qtest: Add qtest_system_reset() utility function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have several qtest tests which want to reset the QEMU under test during the course of testing something. They currently generally have their own functions to do this, which work by sending a "system_reset" QMP command. However, "system_reset" only requests a reset, and many of the tests which send the QMP command forget the "and then wait for the QMP RESET event" part which is needed to ensure that the reset has completed. Provide a qtest_system_reset() function in libqtest so that we don't need to reimplement this in multiple different tests. A few tests (for example device hotplug related tests) want to perform the reset command and then wait for some other event that is produced during the reset sequence. For them we provide qtest_system_reset_nowait() so they can clearly indicate that they are deliberately not waiting for the RESET event. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 16 ++++++++++++++++ tests/qtest/libqtest.h | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 817fd7aac5..8de5f1fde3 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -215,6 +215,22 @@ static void qtest_check_status(QTestState *s) #endif } +void qtest_system_reset_nowait(QTestState *s) +{ + /* Request the system reset, but do not wait for it to complete */ + qtest_qmp_assert_success(s, "{'execute': 'system_reset' }"); +} + +void qtest_system_reset(QTestState *s) +{ + qtest_system_reset_nowait(s); + /* + * Wait for the RESET event, which is sent once the system reset + * has actually completed. + */ + qtest_qmp_eventwait(s, "RESET"); +} + void qtest_wait_qemu(QTestState *s) { if (s->qemu_pid != -1) { diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index beb96b18eb..f23d80e9e5 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -88,6 +88,31 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); */ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); +/** + * qtest_system_reset: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, and wait for + * the reset to complete before returning. + */ +void qtest_system_reset(QTestState *s); + +/** + * qtest_system_reset_nowait: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, but do not + * wait for the reset to complete before returning. The caller is + * responsible for waiting for either the RESET event or some other + * event of interest to them before proceeding. + * + * This function should only be used if you're specifically testing + * for some other event; in that case you can't use qtest_system_reset() + * because it will read and discard any other QMP events that arrive + * before the RESET event. + */ +void qtest_system_reset_nowait(QTestState *s); + /** * qtest_wait_qemu: * @s: #QTestState instance to operate on. From 9881d3d1687d1f18dc605a589d13ae5b8a9db456 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Nov 2024 16:50:40 +0000 Subject: [PATCH 0176/2892] tests/qtest: Use qtest_system_reset() instead of open-coded versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the qtest_system_reset() function in various tests that were previously open-coding the system-reset. Note that in several cases this fixes a bug where the test did not wait for the RESET QMP event before continuing. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Fabiano Rosas --- tests/qtest/bios-tables-test.c | 4 ++-- tests/qtest/boot-order-test.c | 7 +------ tests/qtest/hd-geo-test.c | 9 +-------- tests/qtest/q35-test.c | 12 ++---------- tests/qtest/qos-test.c | 3 +-- tests/qtest/stm32l4x5_gpio-test.c | 10 +--------- tests/qtest/stm32l4x5_syscfg-test.c | 12 ++---------- 7 files changed, 10 insertions(+), 47 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 16d0ffbdf6..6035ec2c61 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -959,7 +959,7 @@ static void test_acpi_piix4_tcg_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } @@ -1216,7 +1216,7 @@ static void test_acpi_q35_multif_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index c67b8cfe16..4c851c2cdb 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -40,12 +40,7 @@ static void test_a_boot_order(const char *machine, machine ?: "", test_args); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_boot); - qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }"); - /* - * system_reset only requests reset. We get a RESET event after - * the actual reset completes. Need to wait for that. - */ - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_reboot); qtest_quit(qts); diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index 85eb8d7668..1c73dea8f7 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -900,7 +900,6 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, QTestState *qts; char *joined_args; QFWCFG *fw_cfg; - QDict *response; int i; joined_args = g_strjoinv(" ", args->argv); @@ -913,13 +912,7 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, /* unplug device an restart */ qtest_qmp_device_del_send(qts, devid); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); read_bootdevices(fw_cfg, expected2); diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index c922d81bc0..7f58fc3746 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -83,7 +83,6 @@ static void test_smram_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; qts = qtest_init("-M q35"); @@ -107,10 +106,7 @@ static void test_smram_lock(void) g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check open is settable again */ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); @@ -194,7 +190,6 @@ static void test_smram_smbase_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; int i; @@ -237,10 +232,7 @@ static void test_smram_smbase_lock(void) } /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check RAM at SMBASE is available after reset */ g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index 114f6bef27..2f7e75a339 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -103,8 +103,7 @@ static void restart_qemu_or_continue(char *path) old_path = g_strdup(path); qtest_start(path); } else { /* if cmd line is the same, reset the guest */ - qobject_unref(qmp("{ 'execute': 'system_reset' }")); - qmp_eventwait("RESET"); + qtest_system_reset(global_qtest); } } diff --git a/tests/qtest/stm32l4x5_gpio-test.c b/tests/qtest/stm32l4x5_gpio-test.c index c0686c7b30..3c6ea71feb 100644 --- a/tests/qtest/stm32l4x5_gpio-test.c +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -169,14 +169,6 @@ static uint32_t reset(uint32_t gpio, unsigned int offset) return 0x0; } -static void system_reset(void) -{ - QDict *r; - r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); - g_assert_false(qdict_haskey(r, "error")); - qobject_unref(r); -} - static void test_idr_reset_value(void) { /* @@ -214,7 +206,7 @@ static void test_idr_reset_value(void) gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF); gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF); - system_reset(); + qtest_system_reset(global_qtest); uint32_t moder = gpio_readl(GPIO_A, MODER); uint32_t odr = gpio_readl(GPIO_A, ODR); diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index d5c71e2c0e..376c80e31c 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -47,14 +47,6 @@ static void syscfg_set_irq(int num, int level) qtest_set_irq_in(global_qtest, SOC, NULL, num, level); } -static void system_reset(void) -{ - QDict *response; - response = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); -} - static void test_reset(void) { /* @@ -182,7 +174,7 @@ static void test_set_only_bits(void) syscfg_writel(SYSCFG_SWPR2, 0x00000000); g_assert_cmphex(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); - system_reset(); + qtest_system_reset(global_qtest); } static void test_clear_only_bits(void) @@ -194,7 +186,7 @@ static void test_clear_only_bits(void) syscfg_writel(SYSCFG_CFGR1, 0x00000001); g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); - system_reset(); + qtest_system_reset(global_qtest); } static void test_interrupt(void) From de62427cce9060e4d631b11ba7840bb03368a448 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Nov 2024 16:50:41 +0000 Subject: [PATCH 0177/2892] tests/qtest: Use qtest_system_reset_nowait() where appropriate In the device and drive plug/unplug tests we want to trigger a system reset and then see if we get the appropriate DEVICE_DELETED event. Use qtest_system_reset_nowait() here. Signed-off-by: Peter Maydell Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/device-plug-test.c | 11 +---------- tests/qtest/drive_del-test.c | 7 +------ 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index c6f33153eb..127a7f9efe 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -15,15 +15,6 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" -static void system_reset(QTestState *qtest) -{ - QDict *resp; - - resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} - static void wait_device_deleted_event(QTestState *qtest, const char *id) { QDict *resp, *data; @@ -58,7 +49,7 @@ static void process_device_remove(QTestState *qtest, const char *id) * handled, removing the device. */ qtest_qmp_device_del_send(qtest, id); - system_reset(qtest); + qtest_system_reset_nowait(qtest); wait_device_deleted_event(qtest, id); } diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 7b67a4bbee..99f6fc2de1 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -154,15 +154,10 @@ static void device_add(QTestState *qts) static void device_del(QTestState *qts, bool and_reset) { - QDict *response; - qtest_qmp_device_del_send(qts, "dev0"); if (and_reset) { - response = qtest_qmp(qts, "{'execute': 'system_reset' }"); - g_assert(response); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); + qtest_system_reset_nowait(qts); } qtest_qmp_eventwait(qts, "DEVICE_DELETED"); From 56afcf2d481bdff5e320c94bb70af923b509551c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:45 -0300 Subject: [PATCH 0178/2892] tests/qtest/migration: Standardize hook names Standardize the hook names: - change the names to .start|end_hook to match test_migrate_start|end() - use the migrate_hook_start_ and migrate_hook_end_ prefixes Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 342 ++++++++++++++++++----------------- 1 file changed, 174 insertions(+), 168 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 74d3000198..f0f3145c59 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -631,9 +631,9 @@ typedef void * (*TestMigrateStartHook)(QTestState *from, * @opaque is a pointer to state previously returned * by the TestMigrateStartHook if any, or NULL. */ -typedef void (*TestMigrateFinishHook)(QTestState *from, - QTestState *to, - void *opaque); +typedef void (*TestMigrateEndHook)(QTestState *from, + QTestState *to, + void *opaque); typedef struct { /* Optional: fine tune start parameters */ @@ -660,7 +660,7 @@ typedef struct { /* Optional: callback to run at start to set migration parameters */ TestMigrateStartHook start_hook; /* Optional: callback to run at finish to cleanup */ - TestMigrateFinishHook finish_hook; + TestMigrateEndHook end_hook; /* * Optional: normally we expect the migration process to complete. @@ -919,7 +919,7 @@ struct TestMigrateTLSPSKData { }; static void * -test_migrate_tls_psk_start_common(QTestState *from, +migrate_hook_start_tls_psk_common(QTestState *from, QTestState *to, bool mismatch) { @@ -964,23 +964,23 @@ test_migrate_tls_psk_start_common(QTestState *from, } static void * -test_migrate_tls_psk_start_match(QTestState *from, +migrate_hook_start_tls_psk_match(QTestState *from, QTestState *to) { - return test_migrate_tls_psk_start_common(from, to, false); + return migrate_hook_start_tls_psk_common(from, to, false); } static void * -test_migrate_tls_psk_start_mismatch(QTestState *from, +migrate_hook_start_tls_psk_mismatch(QTestState *from, QTestState *to) { - return test_migrate_tls_psk_start_common(from, to, true); + return migrate_hook_start_tls_psk_common(from, to, true); } static void -test_migrate_tls_psk_finish(QTestState *from, - QTestState *to, - void *opaque) +migrate_hook_end_tls_psk(QTestState *from, + QTestState *to, + void *opaque) { struct TestMigrateTLSPSKData *data = opaque; @@ -1021,7 +1021,7 @@ typedef struct { } TestMigrateTLSX509; static void * -test_migrate_tls_x509_start_common(QTestState *from, +migrate_hook_start_tls_x509_common(QTestState *from, QTestState *to, TestMigrateTLSX509 *args) { @@ -1114,7 +1114,7 @@ test_migrate_tls_x509_start_common(QTestState *from, * whatever host we were telling QEMU to connect to (if any) */ static void * -test_migrate_tls_x509_start_default_host(QTestState *from, +migrate_hook_start_tls_x509_default_host(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1122,7 +1122,7 @@ test_migrate_tls_x509_start_default_host(QTestState *from, .clientcert = true, .certipaddr = "127.0.0.1" }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1131,7 +1131,7 @@ test_migrate_tls_x509_start_default_host(QTestState *from, * so we must give QEMU an explicit hostname to validate */ static void * -test_migrate_tls_x509_start_override_host(QTestState *from, +migrate_hook_start_tls_x509_override_host(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1139,7 +1139,7 @@ test_migrate_tls_x509_start_override_host(QTestState *from, .clientcert = true, .certhostname = "qemu.org", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1148,7 +1148,7 @@ test_migrate_tls_x509_start_override_host(QTestState *from, * expect the client to reject the server */ static void * -test_migrate_tls_x509_start_mismatch_host(QTestState *from, +migrate_hook_start_tls_x509_mismatch_host(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1156,11 +1156,11 @@ test_migrate_tls_x509_start_mismatch_host(QTestState *from, .clientcert = true, .certipaddr = "10.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } static void * -test_migrate_tls_x509_start_friendly_client(QTestState *from, +migrate_hook_start_tls_x509_friendly_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1169,11 +1169,11 @@ test_migrate_tls_x509_start_friendly_client(QTestState *from, .authzclient = true, .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } static void * -test_migrate_tls_x509_start_hostile_client(QTestState *from, +migrate_hook_start_tls_x509_hostile_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { @@ -1183,7 +1183,7 @@ test_migrate_tls_x509_start_hostile_client(QTestState *from, .authzclient = true, .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1191,13 +1191,13 @@ test_migrate_tls_x509_start_hostile_client(QTestState *from, * and no server verification */ static void * -test_migrate_tls_x509_start_allow_anon_client(QTestState *from, +migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } /* @@ -1205,20 +1205,20 @@ test_migrate_tls_x509_start_allow_anon_client(QTestState *from, * and server verification rejecting */ static void * -test_migrate_tls_x509_start_reject_anon_client(QTestState *from, +migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, QTestState *to) { TestMigrateTLSX509 args = { .verifyclient = true, .certipaddr = "127.0.0.1", }; - return test_migrate_tls_x509_start_common(from, to, &args); + return migrate_hook_start_tls_x509_common(from, to, &args); } static void -test_migrate_tls_x509_finish(QTestState *from, - QTestState *to, - void *opaque) +migrate_hook_end_tls_x509(QTestState *from, + QTestState *to, + void *opaque) { TestMigrateTLSX509Data *data = opaque; @@ -1314,8 +1314,8 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, read_blocktime(to); } - if (args->finish_hook) { - args->finish_hook(from, to, args->postcopy_data); + if (args->end_hook) { + args->end_hook(from, to, args->postcopy_data); args->postcopy_data = NULL; } @@ -1362,8 +1362,8 @@ static void test_postcopy_preempt(void) static void test_postcopy_tls_psk(void) { MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_common(&args); @@ -1373,8 +1373,8 @@ static void test_postcopy_preempt_tls_psk(void) { MigrateCommon args = { .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_common(&args); @@ -1596,8 +1596,8 @@ static void test_postcopy_recovery_fail_reconnect(void) static void test_postcopy_recovery_tls_psk(void) { MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_recovery_common(&args); @@ -1619,8 +1619,8 @@ static void test_postcopy_preempt_all(void) { MigrateCommon args = { .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_postcopy_recovery_common(&args); @@ -1794,8 +1794,8 @@ static void test_precopy_common(MigrateCommon *args) } finish: - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); + if (args->end_hook) { + args->end_hook(from, to, data_hook); } test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); @@ -1899,8 +1899,8 @@ static void test_file_common(MigrateCommon *args, bool stop_src) } finish: - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); + if (args->end_hook) { + args->end_hook(from, to, data_hook); } test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); @@ -1977,8 +1977,8 @@ static void test_precopy_unix_tls_psk(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_precopy_common(&args); @@ -1994,8 +1994,8 @@ static void test_precopy_unix_tls_x509_default_host(void) }, .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL_DEST_QUIT_ERR, }; @@ -2008,8 +2008,8 @@ static void test_precopy_unix_tls_x509_override_host(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2056,7 +2056,7 @@ static void test_ignore_shared(void) #endif static void * -test_migrate_xbzrle_start(QTestState *from, +migrate_hook_start_xbzrle(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); @@ -2073,7 +2073,7 @@ static void test_precopy_unix_xbzrle(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_xbzrle_start, + .start_hook = migrate_hook_start_xbzrle, .iterations = 2, /* * XBZRLE needs pages to be modified when doing the 2nd+ round @@ -2120,7 +2120,8 @@ static void fdset_add_fds(QTestState *qts, const char *file, int flags, } } -static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to) +static void *migrate_hook_start_file_offset_fdset(QTestState *from, + QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); @@ -2137,7 +2138,7 @@ static void test_precopy_file_offset_fdset(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = file_offset_fdset_start_hook, + .start_hook = migrate_hook_start_file_offset_fdset, }; test_file_common(&args, false); @@ -2171,7 +2172,7 @@ static void test_precopy_file_offset_bad(void) test_file_common(&args, false); } -static void *test_mode_reboot_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) { migrate_set_parameter_str(from, "mode", "cpr-reboot"); migrate_set_parameter_str(to, "mode", "cpr-reboot"); @@ -2182,7 +2183,7 @@ static void *test_mode_reboot_start(QTestState *from, QTestState *to) return NULL; } -static void *migrate_mapped_ram_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_mapped_ram(QTestState *from, QTestState *to) { migrate_set_capability(from, "mapped-ram", true); migrate_set_capability(to, "mapped-ram", true); @@ -2198,7 +2199,7 @@ static void test_mode_reboot(void) .start.use_shmem = true, .connect_uri = uri, .listen_uri = "defer", - .start_hook = test_mode_reboot_start + .start_hook = migrate_hook_start_mode_reboot, }; test_file_common(&args, true); @@ -2211,7 +2212,7 @@ static void test_precopy_file_mapped_ram_live(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_mapped_ram_start, + .start_hook = migrate_hook_start_mapped_ram, }; test_file_common(&args, false); @@ -2224,15 +2225,16 @@ static void test_precopy_file_mapped_ram(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_mapped_ram_start, + .start_hook = migrate_hook_start_mapped_ram, }; test_file_common(&args, true); } -static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, + QTestState *to) { - migrate_mapped_ram_start(from, to); + migrate_hook_start_mapped_ram(from, to); migrate_set_parameter_int(from, "multifd-channels", 4); migrate_set_parameter_int(to, "multifd-channels", 4); @@ -2250,7 +2252,7 @@ static void test_multifd_file_mapped_ram_live(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_multifd_mapped_ram_start, + .start_hook = migrate_hook_start_multifd_mapped_ram, }; test_file_common(&args, false); @@ -2263,15 +2265,16 @@ static void test_multifd_file_mapped_ram(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_multifd_mapped_ram_start, + .start_hook = migrate_hook_start_multifd_mapped_ram, }; test_file_common(&args, true); } -static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, + QTestState *to) { - migrate_multifd_mapped_ram_start(from, to); + migrate_hook_start_multifd_mapped_ram(from, to); migrate_set_parameter_bool(from, "direct-io", true); migrate_set_parameter_bool(to, "direct-io", true); @@ -2286,7 +2289,7 @@ static void test_multifd_file_mapped_ram_dio(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = multifd_mapped_ram_dio_start, + .start_hook = migrate_hook_start_multifd_mapped_ram_dio, }; if (!probe_o_direct_support(tmpfs)) { @@ -2298,8 +2301,9 @@ static void test_multifd_file_mapped_ram_dio(void) } #ifndef _WIN32 -static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, - void *opaque) +static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to, + void *opaque) { QDict *resp; QList *fdsets; @@ -2323,28 +2327,30 @@ static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, qobject_unref(resp); } -static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, + QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); fdset_add_fds(from, file, O_WRONLY, 2, true); fdset_add_fds(to, file, O_RDONLY, 2, true); - migrate_multifd_mapped_ram_start(from, to); + migrate_hook_start_multifd_mapped_ram(from, to); migrate_set_parameter_bool(from, "direct-io", true); migrate_set_parameter_bool(to, "direct-io", true); return NULL; } -static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to) +static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); fdset_add_fds(from, file, O_WRONLY, 2, false); fdset_add_fds(to, file, O_RDONLY, 2, false); - migrate_multifd_mapped_ram_start(from, to); + migrate_hook_start_multifd_mapped_ram(from, to); return NULL; } @@ -2356,8 +2362,8 @@ static void test_multifd_file_mapped_ram_fdset(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = multifd_mapped_ram_fdset, - .finish_hook = multifd_mapped_ram_fdset_end, + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, }; test_file_common(&args, true); @@ -2370,8 +2376,8 @@ static void test_multifd_file_mapped_ram_fdset_dio(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = multifd_mapped_ram_fdset_dio, - .finish_hook = multifd_mapped_ram_fdset_end, + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, }; if (!probe_o_direct_support(tmpfs)) { @@ -2392,7 +2398,7 @@ static void test_precopy_tcp_plain(void) test_precopy_common(&args); } -static void *test_migrate_switchover_ack_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) { migrate_set_capability(from, "return-path", true); @@ -2408,7 +2414,7 @@ static void test_precopy_tcp_switchover_ack(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_switchover_ack_start, + .start_hook = migrate_hook_start_switchover_ack, /* * Source VM must be running in order to consider the switchover ACK * when deciding to do switchover or not. @@ -2424,8 +2430,8 @@ static void test_precopy_tcp_tls_psk_match(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_precopy_common(&args); @@ -2438,8 +2444,8 @@ static void test_precopy_tcp_tls_psk_mismatch(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, .result = MIG_TEST_FAIL, }; @@ -2451,8 +2457,8 @@ static void test_precopy_tcp_tls_x509_default_host(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2462,8 +2468,8 @@ static void test_precopy_tcp_tls_x509_override_host(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2476,8 +2482,8 @@ static void test_precopy_tcp_tls_x509_mismatch_host(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL_DEST_QUIT_ERR, }; @@ -2488,8 +2494,8 @@ static void test_precopy_tcp_tls_x509_friendly_client(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_friendly_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_friendly_client, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2502,8 +2508,8 @@ static void test_precopy_tcp_tls_x509_hostile_client(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_hostile_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_hostile_client, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; @@ -2514,8 +2520,8 @@ static void test_precopy_tcp_tls_x509_allow_anon_client(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); @@ -2528,8 +2534,8 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) .hide_stderr = true, }, .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; @@ -2539,8 +2545,8 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) #endif /* CONFIG_GNUTLS */ #ifndef _WIN32 -static void *test_migrate_fd_start_hook(QTestState *from, - QTestState *to) +static void *migrate_hook_start_fd(QTestState *from, + QTestState *to) { int ret; int pair[2]; @@ -2567,9 +2573,9 @@ static void *test_migrate_fd_start_hook(QTestState *from, return NULL; } -static void test_migrate_fd_finish_hook(QTestState *from, - QTestState *to, - void *opaque) +static void migrate_hook_end_fd(QTestState *from, + QTestState *to, + void *opaque) { QDict *rsp; const char *error_desc; @@ -2599,13 +2605,13 @@ static void test_migrate_precopy_fd_socket(void) MigrateCommon args = { .listen_uri = "defer", .connect_uri = "fd:fd-mig", - .start_hook = test_migrate_fd_start_hook, - .finish_hook = test_migrate_fd_finish_hook + .start_hook = migrate_hook_start_fd, + .end_hook = migrate_hook_end_fd, }; test_precopy_common(&args); } -static void *migrate_precopy_fd_file_start(QTestState *from, QTestState *to) +static void *migrate_hook_start_precopy_fd_file(QTestState *from, QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); int src_flags = O_CREAT | O_RDWR; @@ -2638,8 +2644,8 @@ static void test_migrate_precopy_fd_file(void) MigrateCommon args = { .listen_uri = "defer", .connect_uri = "fd:fd-mig", - .start_hook = migrate_precopy_fd_file_start, - .finish_hook = test_migrate_fd_finish_hook + .start_hook = migrate_hook_start_precopy_fd_file, + .end_hook = migrate_hook_end_fd, }; test_file_common(&args, true); } @@ -2886,7 +2892,7 @@ static void test_migrate_auto_converge(void) } static void * -test_migrate_precopy_tcp_multifd_start_common(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, QTestState *to, const char *method) { @@ -2906,32 +2912,32 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from, } static void * -test_migrate_precopy_tcp_multifd_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd(QTestState *from, QTestState *to) { - return test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); } static void * -test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); migrate_set_parameter_str(from, "zero-page-detection", "legacy"); return NULL; } static void * -test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from, - QTestState *to) +migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, + QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); migrate_set_parameter_str(from, "zero-page-detection", "none"); return NULL; } static void * -test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, QTestState *to) { /* @@ -2941,47 +2947,47 @@ test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, migrate_set_parameter_int(from, "multifd-zlib-level", 2); migrate_set_parameter_int(to, "multifd-zlib-level", 2); - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); } #ifdef CONFIG_ZSTD static void * -test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "multifd-zstd-level", 2); migrate_set_parameter_int(to, "multifd-zstd-level", 2); - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); } #endif /* CONFIG_ZSTD */ #ifdef CONFIG_QATZIP static void * -test_migrate_precopy_tcp_multifd_qatzip_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "multifd-qatzip-level", 2); migrate_set_parameter_int(to, "multifd-qatzip-level", 2); - return test_migrate_precopy_tcp_multifd_start_common(from, to, "qatzip"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); } #endif #ifdef CONFIG_QPL static void * -test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, QTestState *to) { - return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); } #endif /* CONFIG_QPL */ #ifdef CONFIG_UADK static void * -test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from, +migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, QTestState *to) { - return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk"); + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); } #endif /* CONFIG_UADK */ @@ -2989,7 +2995,7 @@ static void test_multifd_tcp_uri_none(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -3004,7 +3010,7 @@ static void test_multifd_tcp_zero_page_legacy(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -3019,7 +3025,7 @@ static void test_multifd_tcp_no_zero_page(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page, + .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -3034,7 +3040,7 @@ static void test_multifd_tcp_channels_none(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd, .live = true, .connect_channels = ("[ { 'channel-type': 'main'," " 'addr': { 'transport': 'socket'," @@ -3049,7 +3055,7 @@ static void test_multifd_tcp_zlib(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zlib_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, }; test_precopy_common(&args); } @@ -3059,7 +3065,7 @@ static void test_multifd_tcp_zstd(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zstd_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, }; test_precopy_common(&args); } @@ -3070,7 +3076,7 @@ static void test_multifd_tcp_qatzip(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_qatzip_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, }; test_precopy_common(&args); } @@ -3081,7 +3087,7 @@ static void test_multifd_tcp_qpl(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_qpl_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, }; test_precopy_common(&args); } @@ -3092,7 +3098,7 @@ static void test_multifd_tcp_uadk(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_uadk_start, + .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, }; test_precopy_common(&args); } @@ -3100,60 +3106,60 @@ static void test_multifd_tcp_uadk(void) #ifdef CONFIG_GNUTLS static void * -test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, +migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_match(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_match(from, to); } static void * -test_migrate_multifd_tcp_tls_psk_start_mismatch(QTestState *from, +migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_mismatch(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_mismatch(from, to); } #ifdef CONFIG_TASN1 static void * -test_migrate_multifd_tls_x509_start_default_host(QTestState *from, +migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_default_host(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_default_host(from, to); } static void * -test_migrate_multifd_tls_x509_start_override_host(QTestState *from, +migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_override_host(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_override_host(from, to); } static void * -test_migrate_multifd_tls_x509_start_mismatch_host(QTestState *from, +migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_mismatch_host(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_mismatch_host(from, to); } static void * -test_migrate_multifd_tls_x509_start_allow_anon_client(QTestState *from, +migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_allow_anon_client(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_allow_anon_client(from, to); } static void * -test_migrate_multifd_tls_x509_start_reject_anon_client(QTestState *from, +migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, QTestState *to) { - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_reject_anon_client(from, to); + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_reject_anon_client(from, to); } #endif /* CONFIG_TASN1 */ @@ -3161,8 +3167,8 @@ static void test_multifd_tcp_tls_psk_match(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, }; test_precopy_common(&args); } @@ -3174,8 +3180,8 @@ static void test_multifd_tcp_tls_psk_mismatch(void) .hide_stderr = true, }, .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, .result = MIG_TEST_FAIL, }; test_precopy_common(&args); @@ -3186,8 +3192,8 @@ static void test_multifd_tcp_tls_x509_default_host(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); } @@ -3196,8 +3202,8 @@ static void test_multifd_tcp_tls_x509_override_host(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); } @@ -3222,8 +3228,8 @@ static void test_multifd_tcp_tls_x509_mismatch_host(void) .hide_stderr = true, }, .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; test_precopy_common(&args); @@ -3233,8 +3239,8 @@ static void test_multifd_tcp_tls_x509_allow_anon_client(void) { MigrateCommon args = { .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, }; test_precopy_common(&args); } @@ -3246,8 +3252,8 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void) .hide_stderr = true, }, .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, + .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, .result = MIG_TEST_FAIL, }; test_precopy_common(&args); From 413aa2e986eb7941c1042cc8bb8299f16e7ca962 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:46 -0300 Subject: [PATCH 0179/2892] tests/qtest/migration: Stop calling everything "test" Test frameworks usually prefix "test_" to the entry point of the test code. Having every function prefixed with test_ makes it hard to understand the code and to grep for the actual tests. Remove the "test" prefix from everything that is not a test. In order to still keep some namespacing, stick to the "migrate_" prefix, which is the most used currently. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index f0f3145c59..30bc965b28 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -710,8 +710,8 @@ typedef struct { PostcopyRecoveryFailStage postcopy_recovery_fail_stage; } MigrateCommon; -static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) +static int migrate_start(QTestState **from, QTestState **to, + const char *uri, MigrateStart *args) { g_autofree gchar *arch_source = NULL; g_autofree gchar *arch_target = NULL; @@ -876,7 +876,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, return 0; } -static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) +static void migrate_end(QTestState *from, QTestState *to, bool test_dest) { unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; @@ -1255,7 +1255,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, { QTestState *from, *to; - if (test_migrate_start(&from, &to, "defer", &args->start)) { + if (migrate_start(&from, &to, "defer", &args->start)) { return -1; } @@ -1319,7 +1319,7 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, args->postcopy_data = NULL; } - test_migrate_end(from, to, true); + migrate_end(from, to, true); } static void test_postcopy_common(MigrateCommon *args) @@ -1635,12 +1635,12 @@ static void test_baddest(void) }; QTestState *from, *to; - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); wait_for_migration_fail(from, false); - test_migrate_end(from, to, false); + migrate_end(from, to, false); } #ifndef _WIN32 @@ -1661,7 +1661,7 @@ static void test_analyze_script(void) } /* dummy url */ - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } @@ -1693,7 +1693,7 @@ static void test_analyze_script(void) g_test_message("Failed to analyze the migration stream"); g_test_fail(); } - test_migrate_end(from, to, false); + migrate_end(from, to, false); cleanup("migfile"); } #endif @@ -1703,7 +1703,7 @@ static void test_precopy_common(MigrateCommon *args) QTestState *from, *to; void *data_hook = NULL; - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -1798,7 +1798,7 @@ finish: args->end_hook(from, to, data_hook); } - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); } static void file_dirty_offset_region(void) @@ -1839,7 +1839,7 @@ static void test_file_common(MigrateCommon *args, bool stop_src) void *data_hook = NULL; bool check_offset = false; - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -1903,7 +1903,7 @@ finish: args->end_hook(from, to, data_hook); } - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); } static void test_precopy_unix_plain(void) @@ -2024,7 +2024,7 @@ static void test_ignore_shared(void) g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { + if (migrate_start(&from, &to, uri, false, true, NULL, NULL)) { return; } @@ -2051,7 +2051,7 @@ static void test_ignore_shared(void) /* Check whether shared RAM has been really skipped */ g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); - test_migrate_end(from, to, true); + migrate_end(from, to, true); } #endif @@ -2600,7 +2600,7 @@ static void migrate_hook_end_fd(QTestState *from, qobject_unref(rsp); } -static void test_migrate_precopy_fd_socket(void) +static void test_precopy_fd_socket(void) { MigrateCommon args = { .listen_uri = "defer", @@ -2639,7 +2639,7 @@ static void *migrate_hook_start_precopy_fd_file(QTestState *from, QTestState *to return NULL; } -static void test_migrate_precopy_fd_file(void) +static void test_precopy_fd_file(void) { MigrateCommon args = { .listen_uri = "defer", @@ -2656,7 +2656,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, args)) { + if (migrate_start(&from, &to, uri, args)) { return; } @@ -2680,7 +2680,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) wait_for_migration_complete(from); } - test_migrate_end(from, to, false); + migrate_end(from, to, false); } static void test_validate_uuid(void) @@ -2728,7 +2728,7 @@ static void do_test_validate_uri_channel(MigrateCommon *args) { QTestState *from, *to; - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -2740,7 +2740,7 @@ static void do_test_validate_uri_channel(MigrateCommon *args) * starts. */ migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - test_migrate_end(from, to, false); + migrate_end(from, to, false); } static void test_validate_uri_channels_both_set(void) @@ -2788,7 +2788,7 @@ static void test_validate_uri_channels_none_set(void) * To make things even worse, we need to run the initial stage at * 3MB/s so we enter autoconverge even when host is (over)loaded. */ -static void test_migrate_auto_converge(void) +static void test_auto_converge(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); MigrateStart args = {}; @@ -2804,7 +2804,7 @@ static void test_migrate_auto_converge(void) uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; int max_try_count, hit = 0; - if (test_migrate_start(&from, &to, uri, &args)) { + if (migrate_start(&from, &to, uri, &args)) { return; } @@ -2888,7 +2888,7 @@ static void test_migrate_auto_converge(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to, true); + migrate_end(from, to, true); } static void * @@ -3279,7 +3279,7 @@ static void test_multifd_tcp_cancel(void) }; QTestState *from, *to, *to2; - if (test_migrate_start(&from, &to, "defer", &args)) { + if (migrate_start(&from, &to, "defer", &args)) { return; } @@ -3311,7 +3311,7 @@ static void test_multifd_tcp_cancel(void) /* * Ensure the source QEMU finishes its cancellation process before we - * proceed with the setup of the next migration. The test_migrate_start() + * proceed with the setup of the next migration. The migrate_start() * function and others might want to interact with the source in a way that * is not possible while the migration is not canceled properly. For * example, setting migration capabilities when the migration is still @@ -3323,7 +3323,7 @@ static void test_multifd_tcp_cancel(void) .only_target = true, }; - if (test_migrate_start(&from, &to2, "defer", &args)) { + if (migrate_start(&from, &to2, "defer", &args)) { return; } @@ -3347,7 +3347,7 @@ static void test_multifd_tcp_cancel(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to2, true); + migrate_end(from, to2, true); } static void calc_dirty_rate(QTestState *who, uint64_t calc_time) @@ -3633,7 +3633,7 @@ static void migrate_dirty_limit_wait_showup(QTestState *from, * And see if dirty limit migration works correctly. * This test case involves many passes, so it runs in slow mode only. */ -static void test_migrate_dirty_limit(void) +static void test_dirty_limit(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; @@ -3664,7 +3664,7 @@ static void test_migrate_dirty_limit(void) }; /* Start src, dst vm */ - if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { return; } @@ -3710,7 +3710,7 @@ static void test_migrate_dirty_limit(void) }; /* Restart dst vm, src vm already show up so we needn't wait anymore */ - if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { return; } @@ -3754,7 +3754,7 @@ static void test_migrate_dirty_limit(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to, true); + migrate_end(from, to, true); } static bool kvm_dirty_ring_supported(void) @@ -3957,9 +3957,9 @@ int main(int argc, char **argv) /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ #ifndef _WIN32 migration_test_add("/migration/precopy/fd/tcp", - test_migrate_precopy_fd_socket); + test_precopy_fd_socket); migration_test_add("/migration/precopy/fd/file", - test_migrate_precopy_fd_file); + test_precopy_fd_file); #endif migration_test_add("/migration/validate_uuid", test_validate_uuid); migration_test_add("/migration/validate_uuid_error", @@ -3977,11 +3977,11 @@ int main(int argc, char **argv) */ if (g_test_slow()) { migration_test_add("/migration/auto_converge", - test_migrate_auto_converge); + test_auto_converge); if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { migration_test_add("/migration/dirty_limit", - test_migrate_dirty_limit); + test_dirty_limit); } } migration_test_add("/migration/multifd/tcp/uri/plain/none", From 212c19331b0c53ab299ae3d646409fad2da90602 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:47 -0300 Subject: [PATCH 0180/2892] tests/migration: Disambiguate guestperf vs. a-b The current build structure for migration tests is confusing. There is the tests/migration directory, which contains two different guest code implementations, one for the qtests (a-b-{bootblock|kernel}.S) and another for the guestperf script (stress.c). One uses a Makefile, while the other uses meson. The next patches will add a new qtests/migration/ directory to hold qtest code which will make the situation even more confusing. Move the guest code used by qtests into a new qtests/migration/ directory and rename the old one to tests/migration-stress. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- MAINTAINERS | 5 +++-- tests/meson.build | 2 +- .../{migration => migration-stress}/guestperf-batch.py | 0 .../{migration => migration-stress}/guestperf-plot.py | 0 tests/{migration => migration-stress}/guestperf.py | 0 .../guestperf/__init__.py | 0 .../guestperf/comparison.py | 0 .../guestperf/engine.py | 0 .../guestperf/hardware.py | 0 .../{migration => migration-stress}/guestperf/plot.py | 0 .../guestperf/progress.py | 0 .../guestperf/report.py | 0 .../guestperf/scenario.py | 0 .../{migration => migration-stress}/guestperf/shell.py | 3 ++- .../guestperf/timings.py | 0 tests/{migration => migration-stress}/initrd-stress.sh | 0 tests/{migration => migration-stress}/meson.build | 0 tests/{migration => migration-stress}/stress.c | 0 tests/qtest/migration-test.c | 10 +++++----- tests/{ => qtest}/migration/Makefile | 0 tests/{ => qtest}/migration/aarch64/Makefile | 0 tests/{ => qtest}/migration/aarch64/a-b-kernel.S | 0 tests/{ => qtest}/migration/aarch64/a-b-kernel.h | 0 tests/{ => qtest}/migration/i386/Makefile | 0 tests/{ => qtest}/migration/i386/a-b-bootblock.S | 0 tests/{ => qtest}/migration/i386/a-b-bootblock.h | 0 tests/{ => qtest}/migration/migration-test.h | 0 tests/{ => qtest}/migration/ppc64/Makefile | 0 tests/{ => qtest}/migration/ppc64/a-b-kernel.S | 0 tests/{ => qtest}/migration/ppc64/a-b-kernel.h | 0 tests/{ => qtest}/migration/s390x/Makefile | 0 tests/{ => qtest}/migration/s390x/a-b-bios.c | 0 tests/{ => qtest}/migration/s390x/a-b-bios.h | 0 33 files changed, 11 insertions(+), 9 deletions(-) rename tests/{migration => migration-stress}/guestperf-batch.py (100%) rename tests/{migration => migration-stress}/guestperf-plot.py (100%) rename tests/{migration => migration-stress}/guestperf.py (100%) rename tests/{migration => migration-stress}/guestperf/__init__.py (100%) rename tests/{migration => migration-stress}/guestperf/comparison.py (100%) rename tests/{migration => migration-stress}/guestperf/engine.py (100%) rename tests/{migration => migration-stress}/guestperf/hardware.py (100%) rename tests/{migration => migration-stress}/guestperf/plot.py (100%) rename tests/{migration => migration-stress}/guestperf/progress.py (100%) rename tests/{migration => migration-stress}/guestperf/report.py (100%) rename tests/{migration => migration-stress}/guestperf/scenario.py (100%) rename tests/{migration => migration-stress}/guestperf/shell.py (98%) rename tests/{migration => migration-stress}/guestperf/timings.py (100%) rename tests/{migration => migration-stress}/initrd-stress.sh (100%) rename tests/{migration => migration-stress}/meson.build (100%) rename tests/{migration => migration-stress}/stress.c (100%) rename tests/{ => qtest}/migration/Makefile (100%) rename tests/{ => qtest}/migration/aarch64/Makefile (100%) rename tests/{ => qtest}/migration/aarch64/a-b-kernel.S (100%) rename tests/{ => qtest}/migration/aarch64/a-b-kernel.h (100%) rename tests/{ => qtest}/migration/i386/Makefile (100%) rename tests/{ => qtest}/migration/i386/a-b-bootblock.S (100%) rename tests/{ => qtest}/migration/i386/a-b-bootblock.h (100%) rename tests/{ => qtest}/migration/migration-test.h (100%) rename tests/{ => qtest}/migration/ppc64/Makefile (100%) rename tests/{ => qtest}/migration/ppc64/a-b-kernel.S (100%) rename tests/{ => qtest}/migration/ppc64/a-b-kernel.h (100%) rename tests/{ => qtest}/migration/s390x/Makefile (100%) rename tests/{ => qtest}/migration/s390x/a-b-bios.c (100%) rename tests/{ => qtest}/migration/s390x/a-b-bios.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index aaf0505a21..e8605d394e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -118,7 +118,7 @@ F: pc-bios/s390-ccw.img F: target/s390x/ F: docs/system/target-s390x.rst F: docs/system/s390x/ -F: tests/migration/s390x/ +F: tests/qtest/migration/s390x/ K: ^Subject:.*(?i)s390x? L: qemu-s390x@nongnu.org @@ -3423,10 +3423,11 @@ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ +F: tests/qtest/migration/ F: tests/qtest/migration-* F: docs/devel/migration/ F: qapi/migration.json -F: tests/migration/ +F: tests/migration-stress/ F: util/userfaultfd.c X: migration/rdma* diff --git a/tests/meson.build b/tests/meson.build index 907a4c1c98..f96c1be574 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -84,5 +84,5 @@ endif subdir('unit') subdir('qapi-schema') subdir('qtest') -subdir('migration') +subdir('migration-stress') subdir('functional') diff --git a/tests/migration/guestperf-batch.py b/tests/migration-stress/guestperf-batch.py similarity index 100% rename from tests/migration/guestperf-batch.py rename to tests/migration-stress/guestperf-batch.py diff --git a/tests/migration/guestperf-plot.py b/tests/migration-stress/guestperf-plot.py similarity index 100% rename from tests/migration/guestperf-plot.py rename to tests/migration-stress/guestperf-plot.py diff --git a/tests/migration/guestperf.py b/tests/migration-stress/guestperf.py similarity index 100% rename from tests/migration/guestperf.py rename to tests/migration-stress/guestperf.py diff --git a/tests/migration/guestperf/__init__.py b/tests/migration-stress/guestperf/__init__.py similarity index 100% rename from tests/migration/guestperf/__init__.py rename to tests/migration-stress/guestperf/__init__.py diff --git a/tests/migration/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py similarity index 100% rename from tests/migration/guestperf/comparison.py rename to tests/migration-stress/guestperf/comparison.py diff --git a/tests/migration/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py similarity index 100% rename from tests/migration/guestperf/engine.py rename to tests/migration-stress/guestperf/engine.py diff --git a/tests/migration/guestperf/hardware.py b/tests/migration-stress/guestperf/hardware.py similarity index 100% rename from tests/migration/guestperf/hardware.py rename to tests/migration-stress/guestperf/hardware.py diff --git a/tests/migration/guestperf/plot.py b/tests/migration-stress/guestperf/plot.py similarity index 100% rename from tests/migration/guestperf/plot.py rename to tests/migration-stress/guestperf/plot.py diff --git a/tests/migration/guestperf/progress.py b/tests/migration-stress/guestperf/progress.py similarity index 100% rename from tests/migration/guestperf/progress.py rename to tests/migration-stress/guestperf/progress.py diff --git a/tests/migration/guestperf/report.py b/tests/migration-stress/guestperf/report.py similarity index 100% rename from tests/migration/guestperf/report.py rename to tests/migration-stress/guestperf/report.py diff --git a/tests/migration/guestperf/scenario.py b/tests/migration-stress/guestperf/scenario.py similarity index 100% rename from tests/migration/guestperf/scenario.py rename to tests/migration-stress/guestperf/scenario.py diff --git a/tests/migration/guestperf/shell.py b/tests/migration-stress/guestperf/shell.py similarity index 98% rename from tests/migration/guestperf/shell.py rename to tests/migration-stress/guestperf/shell.py index c85d89efec..046afeb84e 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration-stress/guestperf/shell.py @@ -46,7 +46,8 @@ class BaseShell(object): parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") parser.add_argument("--dst-host", dest="dst_host", default="localhost") parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) - parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img") + parser.add_argument("--initrd", dest="initrd", + default="tests/migration-stress/initrd-stress.img") parser.add_argument("--transport", dest="transport", default="unix") diff --git a/tests/migration/guestperf/timings.py b/tests/migration-stress/guestperf/timings.py similarity index 100% rename from tests/migration/guestperf/timings.py rename to tests/migration-stress/guestperf/timings.py diff --git a/tests/migration/initrd-stress.sh b/tests/migration-stress/initrd-stress.sh similarity index 100% rename from tests/migration/initrd-stress.sh rename to tests/migration-stress/initrd-stress.sh diff --git a/tests/migration/meson.build b/tests/migration-stress/meson.build similarity index 100% rename from tests/migration/meson.build rename to tests/migration-stress/meson.build diff --git a/tests/migration/stress.c b/tests/migration-stress/stress.c similarity index 100% rename from tests/migration/stress.c rename to tests/migration-stress/stress.c diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 30bc965b28..82b9170e3c 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -24,7 +24,7 @@ #include "ppc-util.h" #include "migration-helpers.h" -#include "tests/migration/migration-test.h" +#include "migration/migration-test.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 @@ -138,10 +138,10 @@ static char *bootpath; /* The boot file modifies memory area in [start_address, end_address) * repeatedly. It outputs a 'B' at a fixed rate while it's still running. */ -#include "tests/migration/i386/a-b-bootblock.h" -#include "tests/migration/aarch64/a-b-kernel.h" -#include "tests/migration/ppc64/a-b-kernel.h" -#include "tests/migration/s390x/a-b-bios.h" +#include "migration/i386/a-b-bootblock.h" +#include "migration/aarch64/a-b-kernel.h" +#include "migration/ppc64/a-b-kernel.h" +#include "migration/s390x/a-b-bios.h" static void bootfile_delete(void) { diff --git a/tests/migration/Makefile b/tests/qtest/migration/Makefile similarity index 100% rename from tests/migration/Makefile rename to tests/qtest/migration/Makefile diff --git a/tests/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile similarity index 100% rename from tests/migration/aarch64/Makefile rename to tests/qtest/migration/aarch64/Makefile diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S similarity index 100% rename from tests/migration/aarch64/a-b-kernel.S rename to tests/qtest/migration/aarch64/a-b-kernel.S diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h similarity index 100% rename from tests/migration/aarch64/a-b-kernel.h rename to tests/qtest/migration/aarch64/a-b-kernel.h diff --git a/tests/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile similarity index 100% rename from tests/migration/i386/Makefile rename to tests/qtest/migration/i386/Makefile diff --git a/tests/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S similarity index 100% rename from tests/migration/i386/a-b-bootblock.S rename to tests/qtest/migration/i386/a-b-bootblock.S diff --git a/tests/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h similarity index 100% rename from tests/migration/i386/a-b-bootblock.h rename to tests/qtest/migration/i386/a-b-bootblock.h diff --git a/tests/migration/migration-test.h b/tests/qtest/migration/migration-test.h similarity index 100% rename from tests/migration/migration-test.h rename to tests/qtest/migration/migration-test.h diff --git a/tests/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile similarity index 100% rename from tests/migration/ppc64/Makefile rename to tests/qtest/migration/ppc64/Makefile diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S similarity index 100% rename from tests/migration/ppc64/a-b-kernel.S rename to tests/qtest/migration/ppc64/a-b-kernel.S diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h similarity index 100% rename from tests/migration/ppc64/a-b-kernel.h rename to tests/qtest/migration/ppc64/a-b-kernel.h diff --git a/tests/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile similarity index 100% rename from tests/migration/s390x/Makefile rename to tests/qtest/migration/s390x/Makefile diff --git a/tests/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c similarity index 100% rename from tests/migration/s390x/a-b-bios.c rename to tests/qtest/migration/s390x/a-b-bios.c diff --git a/tests/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h similarity index 100% rename from tests/migration/s390x/a-b-bios.h rename to tests/qtest/migration/s390x/a-b-bios.h From 537f23fb9e215c4d42ec47055a24cf4670133544 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:48 -0300 Subject: [PATCH 0181/2892] tests/qtest/migration: Move bootfile code to its own file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the code that creates the guest binary out of migration-test and into the qtest/migration/ directory, along with the rest of the a-b-kernel code. That code is part of the basic infrastructure of migration tests, it shouldn't be among the tests themselves. Also take the chance and rename migration-test.h, which is too generic a name for this header which contains only values related to guest memory offsets. Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 8 ++- tests/qtest/migration-helpers.c | 1 + tests/qtest/migration-test.c | 58 +-------------- tests/qtest/migration/bootfile.c | 70 +++++++++++++++++++ .../{migration-test.h => bootfile.h} | 9 ++- 5 files changed, 86 insertions(+), 60 deletions(-) create mode 100644 tests/qtest/migration/bootfile.c rename tests/qtest/migration/{migration-test.h => bootfile.h} (85%) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 89db3ecf2f..c3712a9e36 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -332,7 +332,11 @@ endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] -migration_files = [files('migration-helpers.c')] +migration_files = [files( + 'migration-helpers.c', + 'migration/bootfile.c', +)] + if gnutls.found() migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] @@ -358,7 +362,7 @@ qtests = { 'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'], 'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'], - 'virtio-net-failover': files('migration-helpers.c'), + 'virtio-net-failover': migration_files, 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 3f8ba7fa8e..2786f9c860 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -21,6 +21,7 @@ #include "qemu/memalign.h" #include "migration-helpers.h" +#include "migration/bootfile.h" /* * Number of seconds we wait when looking for migration diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 82b9170e3c..e104e44802 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -24,7 +24,7 @@ #include "ppc-util.h" #include "migration-helpers.h" -#include "migration/migration-test.h" +#include "migration/bootfile.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 @@ -135,58 +135,6 @@ static bool ufd_version_check(void) static char *tmpfs; static char *bootpath; -/* The boot file modifies memory area in [start_address, end_address) - * repeatedly. It outputs a 'B' at a fixed rate while it's still running. - */ -#include "migration/i386/a-b-bootblock.h" -#include "migration/aarch64/a-b-kernel.h" -#include "migration/ppc64/a-b-kernel.h" -#include "migration/s390x/a-b-bios.h" - -static void bootfile_delete(void) -{ - if (!bootpath) { - return; - } - unlink(bootpath); - g_free(bootpath); - bootpath = NULL; -} - -static void bootfile_create(char *dir, bool suspend_me) -{ - const char *arch = qtest_get_arch(); - unsigned char *content; - size_t len; - - bootfile_delete(); - bootpath = g_strdup_printf("%s/bootsect", dir); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - /* the assembled x86 boot sector should be exactly one sector large */ - g_assert(sizeof(x86_bootsect) == 512); - x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; - content = x86_bootsect; - len = sizeof(x86_bootsect); - } else if (g_str_equal(arch, "s390x")) { - content = s390x_elf; - len = sizeof(s390x_elf); - } else if (strcmp(arch, "ppc64") == 0) { - content = ppc64_kernel; - len = sizeof(ppc64_kernel); - } else if (strcmp(arch, "aarch64") == 0) { - content = aarch64_kernel; - len = sizeof(aarch64_kernel); - g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); - } else { - g_assert_not_reached(); - } - - FILE *bootfile = fopen(bootpath, "wb"); - - g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); - fclose(bootfile); -} - /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's @@ -737,7 +685,7 @@ static int migrate_start(QTestState **from, QTestState **to, dst_state = (QTestMigrationState) { }; src_state = (QTestMigrationState) { }; - bootfile_create(tmpfs, args->suspend_me); + bootpath = bootfile_create(arch, tmpfs, args->suspend_me); src_state.suspend_me = args->suspend_me; if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { @@ -3488,7 +3436,7 @@ static QTestState *dirtylimit_start_vm(void) QTestState *vm = NULL; g_autofree gchar *cmd = NULL; - bootfile_create(tmpfs, false); + bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " "-name dirtylimit-test,debug-threads=on " "-m 150M -smp 1 " diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c new file mode 100644 index 0000000000..8f75f64093 --- /dev/null +++ b/tests/qtest/migration/bootfile.c @@ -0,0 +1,70 @@ +/* + * Guest code setup for migration tests + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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" + +/* + * The boot file modifies memory area in [start_address, end_address) + * repeatedly. It outputs a 'B' at a fixed rate while it's still running. + */ +#include "bootfile.h" +#include "i386/a-b-bootblock.h" +#include "aarch64/a-b-kernel.h" +#include "ppc64/a-b-kernel.h" +#include "s390x/a-b-bios.h" + +static char *bootpath; + +void bootfile_delete(void) +{ + if (!bootpath) { + return; + } + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + +char *bootfile_create(const char *arch, char *dir, bool suspend_me) +{ + unsigned char *content; + size_t len; + + bootfile_delete(); + bootpath = g_strdup_printf("%s/bootsect", dir); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + /* the assembled x86 boot sector should be exactly one sector large */ + g_assert(sizeof(x86_bootsect) == 512); + x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; + content = x86_bootsect; + len = sizeof(x86_bootsect); + } else if (g_str_equal(arch, "s390x")) { + content = s390x_elf; + len = sizeof(s390x_elf); + } else if (strcmp(arch, "ppc64") == 0) { + content = ppc64_kernel; + len = sizeof(ppc64_kernel); + } else if (strcmp(arch, "aarch64") == 0) { + content = aarch64_kernel; + len = sizeof(aarch64_kernel); + g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); + } else { + g_assert_not_reached(); + } + + FILE *bootfile = fopen(bootpath, "wb"); + + g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); + fclose(bootfile); + + return bootpath; +} diff --git a/tests/qtest/migration/migration-test.h b/tests/qtest/migration/bootfile.h similarity index 85% rename from tests/qtest/migration/migration-test.h rename to tests/qtest/migration/bootfile.h index 194df7df6f..4f5099d765 100644 --- a/tests/qtest/migration/migration-test.h +++ b/tests/qtest/migration/bootfile.h @@ -5,8 +5,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef MIGRATION_TEST_H -#define MIGRATION_TEST_H +#ifndef BOOTFILE_H +#define BOOTFILE_H /* Common */ #define TEST_MEM_PAGE_SIZE 4096 @@ -33,4 +33,7 @@ */ #define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) -#endif /* MIGRATION_TEST_H */ +void bootfile_delete(void); +char *bootfile_create(const char *arch, char *dir, bool suspend_me); + +#endif /* BOOTFILE_H */ From e6c5704043f0cdb3ce567b74d73a99c79457e3e6 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:49 -0300 Subject: [PATCH 0182/2892] tests/qtest/migration: Move qmp helpers to a separate file We current have a bunch of non-test functions in migration-test.c and some others in migration-helpers.c. In order to split migration-test.c into separate test binaries, these helpers need to go somewhere else. To avoid making migration-helpers even larger, move all QMP-related functions into a new migration-qmp.c file and put it under the qtest/migration/ directory. The new file holds everything that has as its main responsibility to call into QMP. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 4 +- tests/qtest/migration-helpers.c | 250 +------------ tests/qtest/migration-helpers.h | 18 +- tests/qtest/migration-test.c | 237 +------------ tests/qtest/migration/migration-qmp.c | 485 ++++++++++++++++++++++++++ tests/qtest/migration/migration-qmp.h | 44 +++ tests/qtest/virtio-net-failover.c | 1 + 7 files changed, 542 insertions(+), 497 deletions(-) create mode 100644 tests/qtest/migration/migration-qmp.c create mode 100644 tests/qtest/migration/migration-qmp.h diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c3712a9e36..0b113adc5c 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -335,6 +335,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] migration_files = [files( 'migration-helpers.c', 'migration/bootfile.c', + 'migration/migration-qmp.c', )] if gnutls.found() @@ -348,7 +349,8 @@ endif 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, + 'dbus-vmstate-test': files('migration/migration-qmp.c', + 'migration-helpers.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': migration_files, diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 2786f9c860..218ee4e59f 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qemu/ctype.h" -#include "qapi/qmp/qjson.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" @@ -23,14 +22,6 @@ #include "migration-helpers.h" #include "migration/bootfile.h" -/* - * Number of seconds we wait when looking for migration - * status changes, to avoid test suite hanging forever - * when things go wrong. Needs to be higher enough to - * avoid false positives on loaded hosts. - */ -#define MIGRATION_STATUS_WAIT_TIMEOUT 120 - static char *SocketAddress_to_str(SocketAddress *addr) { switch (addr->type) { @@ -100,8 +91,7 @@ static SocketAddressList *migrate_get_socket_address(QTestState *who) return addrs; } -static char * -migrate_get_connect_uri(QTestState *who) +char *migrate_get_connect_uri(QTestState *who) { SocketAddressList *addrs; char *connect_uri; @@ -126,7 +116,7 @@ migrate_get_connect_qdict(QTestState *who) return connect_qdict; } -static void migrate_set_ports(QTestState *to, QList *channel_list) +void migrate_set_ports(QTestState *to, QList *channel_list) { QDict *addr; QListEntry *entry; @@ -168,222 +158,6 @@ bool migrate_watch_for_events(QTestState *who, const char *name, return false; } -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args, *err; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } - - g_assert(!qdict_haskey(args, "channels")); - if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - qdict_put_obj(args, "channels", channels_obj); - } - - err = qtest_qmp_assert_failure_ref( - who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(err, "desc")); - - qobject_unref(err); -} - -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. - */ -void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args; - g_autofree char *connect_uri = NULL; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } else if (!channels) { - connect_uri = migrate_get_connect_uri(to); - qdict_put_str(args, "uri", connect_uri); - } - - g_assert(!qdict_haskey(args, "channels")); - if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - QList *channel_list = qobject_to(QList, channels_obj); - migrate_set_ports(to, channel_list); - qdict_put_obj(args, "channels", channels_obj); - } - - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate', 'arguments': %p}", args); -} - -void migrate_set_capability(QTestState *who, const char *capability, - bool value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); -} - -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args, *rsp; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - /* This function relies on the event to work, make sure it's enabled */ - migrate_set_capability(to, "events", true); - - rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - - if (!qdict_haskey(rsp, "return")) { - g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); - g_test_message("%s", s->str); - } - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - migration_event_wait(to, "setup"); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -QDict *migrate_query(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); -} - -QDict *migrate_query_not_failed(QTestState *who) -{ - const char *status; - QDict *rsp = migrate_query(who); - status = qdict_get_str(rsp, "status"); - if (g_str_equal(status, "failed")) { - g_printerr("query-migrate shows failed migration: %s\n", - qdict_get_str(rsp, "error-desc")); - } - g_assert(!g_str_equal(status, "failed")); - return rsp; -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals) -{ - g_test_timer_start(); - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } -} - -void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - -void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - g_test_timer_start(); - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } while (!failed); - - /* Is the machine currently running? */ - rsp_return = qtest_qmp_assert_success_ref(from, - "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp_return); -} - char *find_common_machine_version(const char *mtype, const char *var1, const char *var2) { @@ -509,23 +283,3 @@ bool probe_o_direct_support(const char *tmpfs) return true; } #endif - -/* - * Wait for a "MIGRATION" event. This is what Libvirt uses to track - * migration status changes. - */ -void migration_event_wait(QTestState *s, const char *target) -{ - QDict *response, *data; - const char *status; - bool found; - - do { - response = qtest_qmp_eventwait_ref(s, "MIGRATION"); - data = qdict_get_qdict(response, "data"); - g_assert(data); - status = qdict_get_str(data, "status"); - found = (strcmp(status, target) == 0); - qobject_unref(response); - } while (!found); -} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 72dba369fb..2cb1f78d9e 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -25,21 +25,6 @@ typedef struct QTestMigrationState { bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque); -G_GNUC_PRINTF(5, 6) -void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...); - -G_GNUC_PRINTF(3, 4) -void migrate_incoming_qmp(QTestState *who, const char *uri, - const char *fmt, ...); - -G_GNUC_PRINTF(4, 5) -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...); - -void migrate_set_capability(QTestState *who, const char *capability, - bool value); - QDict *migrate_query(QTestState *who); QDict *migrate_query_not_failed(QTestState *who); @@ -63,6 +48,7 @@ static inline bool probe_o_direct_support(const char *tmpfs) } #endif void migration_test_add(const char *path, void (*fn)(void)); -void migration_event_wait(QTestState *s, const char *target); +char *migrate_get_connect_uri(QTestState *who); +void migrate_set_ports(QTestState *to, QList *channel_list); #endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e104e44802..489fca7071 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -13,18 +13,18 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" #include "qemu/sockets.h" #include "chardev/char.h" #include "crypto/tlscredspsk.h" -#include "qapi/qmp/qlist.h" #include "ppc-util.h" #include "migration-helpers.h" #include "migration/bootfile.h" +#include "migration/migration-qmp.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 @@ -170,89 +170,6 @@ static void wait_for_serial(const char *side) } while (true); } -static void wait_for_stop(QTestState *who, QTestMigrationState *state) -{ - if (!state->stop_seen) { - qtest_qmp_eventwait(who, "STOP"); - } -} - -static void wait_for_resume(QTestState *who, QTestMigrationState *state) -{ - if (!state->resume_seen) { - qtest_qmp_eventwait(who, "RESUME"); - } -} - -static void wait_for_suspend(QTestState *who, QTestMigrationState *state) -{ - if (state->suspend_me && !state->suspend_seen) { - qtest_qmp_eventwait(who, "SUSPEND"); - } -} - -/* - * It's tricky to use qemu's migration event capability with qtest, - * events suddenly appearing confuse the qmp()/hmp() responses. - */ - -static int64_t read_ram_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return, *rsp_ram; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - if (!qdict_haskey(rsp_return, "ram")) { - /* Still in setup */ - result = 0; - } else { - rsp_ram = qdict_get_qdict(rsp_return, "ram"); - result = qdict_get_try_int(rsp_ram, property, 0); - } - qobject_unref(rsp_return); - return result; -} - -static int64_t read_migrate_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - result = qdict_get_try_int(rsp_return, property, 0); - qobject_unref(rsp_return); - return result; -} - -static uint64_t get_migration_pass(QTestState *who) -{ - return read_ram_property_int(who, "dirty-sync-count"); -} - -static void read_blocktime(QTestState *who) -{ - QDict *rsp_return; - - rsp_return = migrate_query_not_failed(who); - g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - qobject_unref(rsp_return); -} - -/* - * Wait for two changes in the migration pass count, but bail if we stop. - */ -static void wait_for_migration_pass(QTestState *who) -{ - uint64_t pass, prev_pass = 0, changes = 0; - - while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) { - usleep(1000); - pass = get_migration_pass(who); - changes += (pass != prev_pass); - prev_pass = pass; - } -} - static void check_guests_ram(QTestState *who) { /* Our ASM test will have been incrementing one byte from each page from @@ -308,114 +225,6 @@ static void cleanup(const char *filename) unlink(path); } -static long long migrate_get_parameter_int(QTestState *who, - const char *parameter) -{ - QDict *rsp; - long long result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_int(rsp, parameter); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - long long result; - - result = migrate_get_parameter_int(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - migrate_check_parameter_int(who, parameter, value); -} - -static char *migrate_get_parameter_str(QTestState *who, - const char *parameter) -{ - QDict *rsp; - char *result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = g_strdup(qdict_get_str(rsp, parameter)); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - g_autofree char *result = migrate_get_parameter_str(who, parameter); - g_assert_cmpstr(result, ==, value); -} - -static void migrate_set_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %s } }", - parameter, value); - migrate_check_parameter_str(who, parameter, value); -} - -static long long migrate_get_parameter_bool(QTestState *who, - const char *parameter) -{ - QDict *rsp; - int result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_bool(rsp, parameter); - qobject_unref(rsp); - return !!result; -} - -static void migrate_check_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - int result; - - result = migrate_get_parameter_bool(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %i } }", - parameter, value); - migrate_check_parameter_bool(who, parameter, value); -} - -static void migrate_ensure_non_converge(QTestState *who) -{ - /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 1); -} - -static void migrate_ensure_converge(QTestState *who) -{ - /* Should converge with 30s downtime + 1 gbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); -} - /* * Our goal is to ensure that we run a single full migration * iteration, and also dirty memory, ensuring that at least @@ -506,42 +315,6 @@ static void migrate_wait_for_dirty_mem(QTestState *from, } while (qtest_readb(from, watch_address) == watch_byte); } - -static void migrate_pause(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); -} - -static void migrate_continue(QTestState *who, const char *state) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); -} - -static void migrate_recover(QTestState *who, const char *uri) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); -} - -static void migrate_cancel(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); -} - -static void migrate_postcopy_start(QTestState *from, QTestState *to) -{ - qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); - - wait_for_stop(from, &src_state); - qtest_qmp_eventwait(to, "RESUME"); -} - typedef struct { /* * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors @@ -1277,7 +1050,7 @@ static void test_postcopy_common(MigrateCommon *args) if (migrate_postcopy_prepare(&from, &to, args)) { return; } - migrate_postcopy_start(from, to); + migrate_postcopy_start(from, to, &src_state); migrate_postcopy_complete(from, to, args); } @@ -1464,7 +1237,7 @@ static void test_postcopy_recovery_common(MigrateCommon *args) migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); /* Now we start the postcopy */ - migrate_postcopy_start(from, to); + migrate_postcopy_start(from, to, &src_state); /* * Wait until postcopy is really started; we can only run the @@ -1704,7 +1477,7 @@ static void test_precopy_common(MigrateCommon *args) * for some dirty mem before switching to converge */ while (args->iterations > 1) { - wait_for_migration_pass(from); + wait_for_migration_pass(from, &src_state); args->iterations--; } migrate_wait_for_dirty_mem(from, to); diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c new file mode 100644 index 0000000000..20be46fdf6 --- /dev/null +++ b/tests/qtest/migration/migration-qmp.c @@ -0,0 +1,485 @@ +/* + * QTest QMP helpers for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration-helpers.h" +#include "migration-qmp.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qlist.h" + +/* + * Number of seconds we wait when looking for migration + * status changes, to avoid test suite hanging forever + * when things go wrong. Needs to be higher enough to + * avoid false positives on loaded hosts. + */ +#define MIGRATION_STATUS_WAIT_TIMEOUT 120 + +/* + * Wait for a "MIGRATION" event. This is what Libvirt uses to track + * migration status changes. + */ +void migration_event_wait(QTestState *s, const char *target) +{ + QDict *response, *data; + const char *status; + bool found; + + do { + response = qtest_qmp_eventwait_ref(s, "MIGRATION"); + data = qdict_get_qdict(response, "data"); + g_assert(data); + status = qdict_get_str(data, "status"); + found = (strcmp(status, target) == 0); + qobject_unref(response); + } while (!found); +} + +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args, *err; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + qdict_put_obj(args, "channels", channels_obj); + } + + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(err, "desc")); + + qobject_unref(err); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args; + g_autofree char *connect_uri = NULL; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } else if (!channels) { + connect_uri = migrate_get_connect_uri(to); + qdict_put_str(args, "uri", connect_uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + QList *channel_list = qobject_to(QList, channels_obj); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels_obj); + } + + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate', 'arguments': %p}", args); +} + +void migrate_set_capability(QTestState *who, const char *capability, + bool value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); +} + +void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + /* This function relies on the event to work, make sure it's enabled */ + migrate_set_capability(to, "events", true); + + rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", + args); + + if (!qdict_haskey(rsp, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); + g_test_message("%s", s->str); + } + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + migration_event_wait(to, "setup"); +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + g_test_timer_start(); + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + g_test_timer_start(); + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = qtest_qmp_assert_success_ref(from, + "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} + +void wait_for_stop(QTestState *who, QTestMigrationState *state) +{ + if (!state->stop_seen) { + qtest_qmp_eventwait(who, "STOP"); + } +} + +void wait_for_resume(QTestState *who, QTestMigrationState *state) +{ + if (!state->resume_seen) { + qtest_qmp_eventwait(who, "RESUME"); + } +} + +void wait_for_suspend(QTestState *who, QTestMigrationState *state) +{ + if (state->suspend_me && !state->suspend_seen) { + qtest_qmp_eventwait(who, "SUSPEND"); + } +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); +} + +QDict *migrate_query_not_failed(QTestState *who) +{ + const char *status; + QDict *rsp = migrate_query(who); + status = qdict_get_str(rsp, "status"); + if (g_str_equal(status, "failed")) { + g_printerr("query-migrate shows failed migration: %s\n", + qdict_get_str(rsp, "error-desc")); + } + g_assert(!g_str_equal(status, "failed")); + return rsp; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +int64_t read_ram_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return, *rsp_ram; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + if (!qdict_haskey(rsp_return, "ram")) { + /* Still in setup */ + result = 0; + } else { + rsp_ram = qdict_get_qdict(rsp_return, "ram"); + result = qdict_get_try_int(rsp_ram, property, 0); + } + qobject_unref(rsp_return); + return result; +} + +int64_t read_migrate_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + result = qdict_get_try_int(rsp_return, property, 0); + qobject_unref(rsp_return); + return result; +} + +uint64_t get_migration_pass(QTestState *who) +{ + return read_ram_property_int(who, "dirty-sync-count"); +} + +void read_blocktime(QTestState *who) +{ + QDict *rsp_return; + + rsp_return = migrate_query_not_failed(who); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + qobject_unref(rsp_return); +} + +/* + * Wait for two changes in the migration pass count, but bail if we stop. + */ +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state) +{ + uint64_t pass, prev_pass = 0, changes = 0; + + while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) { + usleep(1000); + pass = get_migration_pass(who); + changes += (pass != prev_pass); + prev_pass = pass; + } +} + +static long long migrate_get_parameter_int(QTestState *who, + const char *parameter) +{ + QDict *rsp; + long long result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_int(rsp, parameter); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + long long result; + + result = migrate_get_parameter_int(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); + migrate_check_parameter_int(who, parameter, value); +} + +static char *migrate_get_parameter_str(QTestState *who, const char *parameter) +{ + QDict *rsp; + char *result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = g_strdup(qdict_get_str(rsp, parameter)); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + g_autofree char *result = migrate_get_parameter_str(who, parameter); + g_assert_cmpstr(result, ==, value); +} + +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %s } }", + parameter, value); + migrate_check_parameter_str(who, parameter, value); +} + +static long long migrate_get_parameter_bool(QTestState *who, + const char *parameter) +{ + QDict *rsp; + int result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_bool(rsp, parameter); + qobject_unref(rsp); + return !!result; +} + +static void migrate_check_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + int result; + + result = migrate_get_parameter_bool(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %i } }", + parameter, value); + migrate_check_parameter_bool(who, parameter, value); +} + +void migrate_ensure_non_converge(QTestState *who) +{ + /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 1); +} + +void migrate_ensure_converge(QTestState *who) +{ + /* Should converge with 30s downtime + 1 gbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); +} + +void migrate_pause(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); +} + +void migrate_continue(QTestState *who, const char *state) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); +} + +void migrate_recover(QTestState *who, const char *uri) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); +} + +void migrate_cancel(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); +} + +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state) +{ + qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); + + wait_for_stop(from, src_state); + qtest_qmp_eventwait(to, "RESUME"); +} diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h new file mode 100644 index 0000000000..ed927cf408 --- /dev/null +++ b/tests/qtest/migration/migration-qmp.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef MIGRATION_QMP_H +#define MIGRATION_QMP_H + +G_GNUC_PRINTF(4, 5) +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...); + +G_GNUC_PRINTF(5, 6) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...); + +G_GNUC_PRINTF(3, 4) +void migrate_incoming_qmp(QTestState *who, const char *uri, + const char *fmt, ...); + +void migration_event_wait(QTestState *s, const char *target); +void migrate_set_capability(QTestState *who, const char *capability, + bool value); +int64_t read_ram_property_int(QTestState *who, const char *property); +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value); +void wait_for_stop(QTestState *who, QTestMigrationState *state); +void wait_for_resume(QTestState *who, QTestMigrationState *state); +void wait_for_suspend(QTestState *who, QTestMigrationState *state); +gchar *migrate_query_status(QTestState *who); +int64_t read_migrate_property_int(QTestState *who, const char *property); +uint64_t get_migration_pass(QTestState *who); +void read_blocktime(QTestState *who); +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state); +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value); +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value); +void migrate_ensure_non_converge(QTestState *who); +void migrate_ensure_converge(QTestState *who); +void migrate_pause(QTestState *who); +void migrate_continue(QTestState *who, const char *state); +void migrate_recover(QTestState *who, const char *uri); +void migrate_cancel(QTestState *who); +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state); + +#endif /* MIGRATION_QMP_H */ diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 73dfabc272..28a6147d9a 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -12,6 +12,7 @@ #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "migration-helpers.h" +#include "migration/migration-qmp.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qjson.h" From b7d7f723a9853a68a648075a1f2ab96d410e929e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:50 -0300 Subject: [PATCH 0183/2892] tests/qtest/migration: Rename migration-helpers.c Rename migration-helpers.c to migration-util.c to make its purpose more explicit and avoid the "helper" terminology. Move the file to the qtest/migration/ directory along with the rest of the migration files. Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 4 ++-- tests/qtest/migration-test.c | 2 +- tests/qtest/migration/migration-qmp.c | 2 +- tests/qtest/migration/migration-qmp.h | 2 ++ .../{migration-helpers.c => migration/migration-util.c} | 4 ++-- .../{migration-helpers.h => migration/migration-util.h} | 6 +++--- tests/qtest/virtio-net-failover.c | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) rename tests/qtest/{migration-helpers.c => migration/migration-util.c} (99%) rename tests/qtest/{migration-helpers.h => migration/migration-util.h} (94%) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 0b113adc5c..ec809af0c8 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -333,9 +333,9 @@ endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] migration_files = [files( - 'migration-helpers.c', 'migration/bootfile.c', 'migration/migration-qmp.c', + 'migration/migration-util.c', )] if gnutls.found() @@ -350,7 +350,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/migration-qmp.c', - 'migration-helpers.c') + dbus_vmstate1, + 'migration/migration-util.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': migration_files, diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 489fca7071..e295bd3081 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -22,9 +22,9 @@ #include "crypto/tlscredspsk.h" #include "ppc-util.h" -#include "migration-helpers.h" #include "migration/bootfile.h" #include "migration/migration-qmp.h" +#include "migration/migration-util.h" #ifdef CONFIG_GNUTLS # include "tests/unit/crypto-tls-psk-helpers.h" # ifdef CONFIG_TASN1 diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 20be46fdf6..71b14b51b2 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -12,8 +12,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "migration-helpers.h" #include "migration-qmp.h" +#include "migration-util.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index ed927cf408..caaa78722a 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -2,6 +2,8 @@ #ifndef MIGRATION_QMP_H #define MIGRATION_QMP_H +#include "migration-util.h" + G_GNUC_PRINTF(4, 5) void migrate_qmp_fail(QTestState *who, const char *uri, const char *channels, const char *fmt, ...); diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration/migration-util.c similarity index 99% rename from tests/qtest/migration-helpers.c rename to tests/qtest/migration/migration-util.c index 218ee4e59f..8a974ded22 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration/migration-util.c @@ -1,5 +1,5 @@ /* - * QTest migration helpers + * QTest migration utilities * * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates * based on the vhost-user-test.c that is: @@ -19,8 +19,8 @@ #include "qemu/cutils.h" #include "qemu/memalign.h" -#include "migration-helpers.h" #include "migration/bootfile.h" +#include "migration/migration-util.h" static char *SocketAddress_to_str(SocketAddress *addr) { diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration/migration-util.h similarity index 94% rename from tests/qtest/migration-helpers.h rename to tests/qtest/migration/migration-util.h index 2cb1f78d9e..0fa3676235 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration/migration-util.h @@ -10,8 +10,8 @@ * */ -#ifndef MIGRATION_HELPERS_H -#define MIGRATION_HELPERS_H +#ifndef MIGRATION_UTIL_H +#define MIGRATION_UTIL_H #include "libqtest.h" @@ -51,4 +51,4 @@ void migration_test_add(const char *path, void (*fn)(void)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); -#endif /* MIGRATION_HELPERS_H */ +#endif /* MIGRATION_UTIL_H */ diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 28a6147d9a..08365ffa11 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -11,8 +11,8 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "migration-helpers.h" #include "migration/migration-qmp.h" +#include "migration/migration-util.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qjson.h" From 124a3c58b82ad8e3662711f4deca281d81d5d56a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:51 -0300 Subject: [PATCH 0184/2892] tests/qtest/migration: Move ufd_version_check to utils Move ufd_version_check() to migration-util.c file. This is a helper function that is used during tests definition so it should be available outside of migration-test.c Since the function is moving to another file, change it to stop accessing the global uffd_feature_thread_id and take it as argument instead. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 47 ++----------------------- tests/qtest/migration/migration-util.c | 48 ++++++++++++++++++++++++++ tests/qtest/migration/migration-util.h | 2 ++ 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e295bd3081..f1c1ca309a 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -35,6 +35,7 @@ /* For dirty ring test; so far only x86_64 is supported */ #if defined(__linux__) && defined(HOST_X86_64) #include "linux/kvm.h" +#include #endif unsigned start_address; @@ -88,50 +89,6 @@ typedef enum PostcopyRecoveryFailStage { #include #endif -#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) -#include -#include -#include "qemu/userfaultfd.h" - -static bool ufd_version_check(void) -{ - struct uffdio_api api_struct; - uint64_t ioctl_mask; - - int ufd = uffd_open(O_CLOEXEC); - - if (ufd == -1) { - g_test_message("Skipping test: userfaultfd not available"); - return false; - } - - api_struct.api = UFFD_API; - api_struct.features = 0; - if (ioctl(ufd, UFFDIO_API, &api_struct)) { - g_test_message("Skipping test: UFFDIO_API failed"); - return false; - } - uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; - - ioctl_mask = (1ULL << _UFFDIO_REGISTER | - 1ULL << _UFFDIO_UNREGISTER); - if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { - g_test_message("Skipping test: Missing userfault feature"); - return false; - } - - return true; -} - -#else -static bool ufd_version_check(void) -{ - g_test_message("Skipping test: Userfault not available (builtdtime)"); - return false; -} - -#endif - static char *tmpfs; static char *bootpath; @@ -3532,7 +3489,7 @@ int main(int argc, char **argv) return 0; } - has_uffd = ufd_version_check(); + has_uffd = ufd_version_check(&uffd_feature_thread_id); arch = qtest_get_arch(); is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 8a974ded22..2ff8870fd5 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -22,6 +22,13 @@ #include "migration/bootfile.h" #include "migration/migration-util.h" +/* for uffd_version_check() */ +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +#include +#include "qemu/userfaultfd.h" +#endif + + static char *SocketAddress_to_str(SocketAddress *addr) { switch (addr->type) { @@ -283,3 +290,44 @@ bool probe_o_direct_support(const char *tmpfs) return true; } #endif + +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + struct uffdio_api api_struct; + uint64_t ioctl_mask; + + int ufd = uffd_open(O_CLOEXEC); + + if (ufd == -1) { + g_test_message("Skipping test: userfaultfd not available"); + return false; + } + + api_struct.api = UFFD_API; + api_struct.features = 0; + if (ioctl(ufd, UFFDIO_API, &api_struct)) { + g_test_message("Skipping test: UFFDIO_API failed"); + return false; + } + + if (uffd_feature_thread_id) { + *uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; + } + + ioctl_mask = (1ULL << _UFFDIO_REGISTER | + 1ULL << _UFFDIO_UNREGISTER); + if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { + g_test_message("Skipping test: Missing userfault feature"); + return false; + } + + return true; +} +#else +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + g_test_message("Skipping test: Userfault not available (builtdtime)"); + return false; +} +#endif diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h index 0fa3676235..2ab22e3fd5 100644 --- a/tests/qtest/migration/migration-util.h +++ b/tests/qtest/migration/migration-util.h @@ -47,6 +47,8 @@ static inline bool probe_o_direct_support(const char *tmpfs) return false; } #endif + +bool ufd_version_check(bool *uffd_feature_thread_id); void migration_test_add(const char *path, void (*fn)(void)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); From 2fa72ba79bc84bd44385af260dbacc223f7a6181 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:52 -0300 Subject: [PATCH 0185/2892] tests/qtest/migration: Move kvm_dirty_ring_supported to utils Move kvm_dirty_ring_supported() to migration-util.c. Similarly to ufd_version_check(), this function is used during test definition so put it in the utils file. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 29 -------------------------- tests/qtest/migration/migration-util.c | 29 ++++++++++++++++++++++++++ tests/qtest/migration/migration-util.h | 1 + 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index f1c1ca309a..ab7cbb8251 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -32,12 +32,6 @@ # endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -/* For dirty ring test; so far only x86_64 is supported */ -#if defined(__linux__) && defined(HOST_X86_64) -#include "linux/kvm.h" -#include -#endif - unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; @@ -3435,29 +3429,6 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -static bool kvm_dirty_ring_supported(void) -{ -#if defined(__linux__) && defined(HOST_X86_64) - int ret, kvm_fd = open("/dev/kvm", O_RDONLY); - - if (kvm_fd < 0) { - return false; - } - - ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); - close(kvm_fd); - - /* We test with 4096 slots */ - if (ret < 4096) { - return false; - } - - return true; -#else - return false; -#endif -} - int main(int argc, char **argv) { bool has_kvm, has_tcg; diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 2ff8870fd5..525bf1eed4 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -28,6 +28,12 @@ #include "qemu/userfaultfd.h" #endif +/* For dirty ring test; so far only x86_64 is supported */ +#if defined(__linux__) && defined(HOST_X86_64) +#include "linux/kvm.h" +#include +#endif + static char *SocketAddress_to_str(SocketAddress *addr) { @@ -331,3 +337,26 @@ bool ufd_version_check(bool *uffd_feature_thread_id) return false; } #endif + +bool kvm_dirty_ring_supported(void) +{ +#if defined(__linux__) && defined(HOST_X86_64) + int ret, kvm_fd = open("/dev/kvm", O_RDONLY); + + if (kvm_fd < 0) { + return false; + } + + ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); + close(kvm_fd); + + /* We test with 4096 slots */ + if (ret < 4096) { + return false; + } + + return true; +#else + return false; +#endif +} diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h index 2ab22e3fd5..f5f2e4650e 100644 --- a/tests/qtest/migration/migration-util.h +++ b/tests/qtest/migration/migration-util.h @@ -49,6 +49,7 @@ static inline bool probe_o_direct_support(const char *tmpfs) #endif bool ufd_version_check(bool *uffd_feature_thread_id); +bool kvm_dirty_ring_supported(void); void migration_test_add(const char *path, void (*fn)(void)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); From d4bedc3514b6ffde449bcf967b04a3cafe7e89c7 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:53 -0300 Subject: [PATCH 0186/2892] tests/qtest/migration: Isolate test initialization We currently have some environment validation to perform and flags to set during the initialization of the tests. To be able to add more migration test binaries, we'll need these tasks to be in their own function so they can be called from more than one place. Move the initialization code to a function and introduce the MigrationTestEnv structure to hold the flags that are accessed during test registration. Make the env object static to avoid have to change all the code to pass it around. Similarly with the tmpfs variable, which is used extensively. Note: I'm keeping the new functions in migration-test.c because they are going to be moved in the next patch to the correct place. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 122 ++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 36 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index ab7cbb8251..5617445e6f 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -34,7 +34,6 @@ unsigned start_address; unsigned end_address; -static bool uffd_feature_thread_id; static QTestMigrationState src_state; static QTestMigrationState dst_state; @@ -86,6 +85,22 @@ typedef enum PostcopyRecoveryFailStage { static char *tmpfs; static char *bootpath; +typedef struct MigrationTestEnv { + bool has_kvm; + bool has_tcg; + bool has_uffd; + bool uffd_feature_thread_id; + bool has_dirty_ring; + bool is_x86; + const char *arch; + const char *qemu_src; + const char *qemu_dst; + char *tmpfs; +} MigrationTestEnv; + + +MigrationTestEnv *migration_get_env(void); + /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's @@ -972,6 +987,8 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, static void migrate_postcopy_complete(QTestState *from, QTestState *to, MigrateCommon *args) { + MigrationTestEnv *env = migration_get_env(); + wait_for_migration_complete(from); if (args->start.suspend_me) { @@ -982,7 +999,7 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, /* Make sure we get at least one "B" on destination */ wait_for_serial("dest_serial"); - if (uffd_feature_thread_id) { + if (env->uffd_feature_thread_id) { read_blocktime(to); } @@ -3429,63 +3446,99 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -int main(int argc, char **argv) +MigrationTestEnv *migration_get_env(void) { - bool has_kvm, has_tcg; - bool has_uffd, is_x86; - const char *arch; + static MigrationTestEnv *env; g_autoptr(GError) err = NULL; - const char *qemu_src = getenv(QEMU_ENV_SRC); - const char *qemu_dst = getenv(QEMU_ENV_DST); - int ret; - g_test_init(&argc, &argv, NULL); + if (env) { + return env; + } + + env = g_new0(MigrationTestEnv, 1); + env->qemu_src = getenv(QEMU_ENV_SRC); + env->qemu_dst = getenv(QEMU_ENV_DST); /* * The default QTEST_QEMU_BINARY must always be provided because * that is what helpers use to query the accel type and * architecture. */ - if (qemu_src && qemu_dst) { + if (env->qemu_src && env->qemu_dst) { g_test_message("Only one of %s, %s is allowed", QEMU_ENV_SRC, QEMU_ENV_DST); exit(1); } - has_kvm = qtest_has_accel("kvm"); - has_tcg = qtest_has_accel("tcg"); + env->has_kvm = qtest_has_accel("kvm"); + env->has_tcg = qtest_has_accel("tcg"); - if (!has_tcg && !has_kvm) { + if (!env->has_tcg && !env->has_kvm) { g_test_skip("No KVM or TCG accelerator available"); - return 0; + return env; } - has_uffd = ufd_version_check(&uffd_feature_thread_id); - arch = qtest_get_arch(); - is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); + env->has_dirty_ring = kvm_dirty_ring_supported(); + env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); + env->arch = qtest_get_arch(); + env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); - tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); - if (!tmpfs) { + env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); + if (!env->tmpfs) { g_test_message("Can't create temporary directory in %s: %s", g_get_tmp_dir(), err->message); } - g_assert(tmpfs); + g_assert(env->tmpfs); + return env; +} + +static int migration_env_clean(MigrationTestEnv *env) +{ + char *tmpfs; + int ret = 0; + + if (!env) { + return ret; + } + + bootfile_delete(); + + tmpfs = env->tmpfs; + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + g_free(tmpfs); + + return ret; +} + +int main(int argc, char **argv) +{ + MigrationTestEnv *env; + int ret; + + g_test_init(&argc, &argv, NULL); + env = migration_get_env(); module_call_init(MODULE_INIT_QOM); + tmpfs = env->tmpfs; + migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); #endif - if (is_x86) { + if (env->is_x86) { migration_test_add("/migration/precopy/unix/suspend/live", test_precopy_unix_suspend_live); migration_test_add("/migration/precopy/unix/suspend/notlive", test_precopy_unix_suspend_notlive); } - if (has_uffd) { + if (env->has_uffd) { migration_test_add("/migration/postcopy/plain", test_postcopy); migration_test_add("/migration/postcopy/recovery/plain", test_postcopy_recovery); @@ -3497,7 +3550,7 @@ int main(int argc, char **argv) test_postcopy_recovery_fail_handshake); migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", test_postcopy_recovery_fail_reconnect); - if (is_x86) { + if (env->is_x86) { migration_test_add("/migration/postcopy/suspend", test_postcopy_suspend); } @@ -3552,7 +3605,7 @@ int main(int argc, char **argv) migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); - if (has_uffd) { + if (env->has_uffd) { /* * NOTE: psk test is enough for postcopy, as other types of TLS * channels are tested under precopy. Here what we want to test is the @@ -3627,9 +3680,9 @@ int main(int argc, char **argv) if (g_test_slow()) { migration_test_add("/migration/auto_converge", test_auto_converge); - if (g_str_equal(arch, "x86_64") && - has_kvm && kvm_dirty_ring_supported()) { - migration_test_add("/migration/dirty_limit", + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/dirty_limit", test_dirty_limit); } } @@ -3680,7 +3733,9 @@ int main(int argc, char **argv) #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ - if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/migration/dirty_ring", test_precopy_unix_dirty_ring); if (qtest_has_machine("pc") && g_test_slow()) { @@ -3693,13 +3748,8 @@ int main(int argc, char **argv) g_assert_cmpint(ret, ==, 0); - bootfile_delete(); - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - g_free(tmpfs); + tmpfs = NULL; + ret = migration_env_clean(env); return ret; } From e1803dabdcbd26f2cf72e586f22b03e2e90d6110 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:54 -0300 Subject: [PATCH 0187/2892] tests/qtest/migration: Move common test code The migration tests have a set of core infrastructure routines. These are functions that are called by (almost) all tests and centralize the common operations of: starting migration on both sides, waiting for guests to boot, performing guest initialization and teardown, guest memory validation, etc. Move this basic framework code (and a few static helpers) into a separate file. Leave only individual test functions (and their own static helpers) in migration-test.c. Acked-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 1140 +---------------------------- tests/qtest/migration/bootfile.c | 2 +- tests/qtest/migration/bootfile.h | 2 +- tests/qtest/migration/framework.c | 971 ++++++++++++++++++++++++ tests/qtest/migration/framework.h | 218 ++++++ 6 files changed, 1205 insertions(+), 1129 deletions(-) create mode 100644 tests/qtest/migration/framework.c create mode 100644 tests/qtest/migration/framework.h diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ec809af0c8..c5cc09fbf2 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -334,6 +334,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] migration_files = [files( 'migration/bootfile.c', + 'migration/framework.c', 'migration/migration-qmp.c', 'migration/migration-util.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 5617445e6f..a60150483e 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -23,6 +23,7 @@ #include "ppc-util.h" #include "migration/bootfile.h" +#include "migration/framework.h" #include "migration/migration-qmp.h" #include "migration/migration-util.h" #ifdef CONFIG_GNUTLS @@ -32,25 +33,6 @@ # endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -unsigned start_address; -unsigned end_address; -static QTestMigrationState src_state; -static QTestMigrationState dst_state; - -/* - * An initial 3 MB offset is used as that corresponds - * to ~1 sec of data transfer with our bandwidth setting. - */ -#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) -/* - * A further 1k is added to ensure we're not a multiple - * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes - * from the migration guest workload. - */ -#define MAGIC_OFFSET_SHUFFLE 1024 -#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) -#define MAGIC_MARKER 0xFEED12345678CAFEULL - /* * Dirtylimit stop working if dirty page rate error * value less than DIRTYLIMIT_TOLERANCE_RANGE @@ -59,543 +41,13 @@ static QTestMigrationState dst_state; #define ANALYZE_SCRIPT "scripts/analyze-migration.py" -#define QEMU_VM_FILE_MAGIC 0x5145564d -#define FILE_TEST_FILENAME "migfile" -#define FILE_TEST_OFFSET 0x1000 -#define FILE_TEST_MARKER 'X' -#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" -#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" - -typedef enum PostcopyRecoveryFailStage { - /* - * "no failure" must be 0 as it's the default. OTOH, real failure - * cases must be >0 to make sure they trigger by a "if" test. - */ - POSTCOPY_FAIL_NONE = 0, - POSTCOPY_FAIL_CHANNEL_ESTABLISH, - POSTCOPY_FAIL_RECOVERY, - POSTCOPY_FAIL_MAX -} PostcopyRecoveryFailStage; - #if defined(__linux__) +#include #include #include #endif static char *tmpfs; -static char *bootpath; - -typedef struct MigrationTestEnv { - bool has_kvm; - bool has_tcg; - bool has_uffd; - bool uffd_feature_thread_id; - bool has_dirty_ring; - bool is_x86; - const char *arch; - const char *qemu_src; - const char *qemu_dst; - char *tmpfs; -} MigrationTestEnv; - - -MigrationTestEnv *migration_get_env(void); - -/* - * Wait for some output in the serial output file, - * we get an 'A' followed by an endless string of 'B's - * but on the destination we won't have the A (unless we enabled suspend/resume) - */ -static void wait_for_serial(const char *side) -{ - g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); - FILE *serialfile = fopen(serialpath, "r"); - - do { - int readvalue = fgetc(serialfile); - - switch (readvalue) { - case 'A': - /* Fine */ - break; - - case 'B': - /* It's alive! */ - fclose(serialfile); - return; - - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - - default: - fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); - g_assert_not_reached(); - } - } while (true); -} - -static void check_guests_ram(QTestState *who) -{ - /* Our ASM test will have been incrementing one byte from each page from - * start_address to < end_address in order. This gives us a constraint - * that any page's byte should be equal or less than the previous pages - * byte (mod 256); and they should all be equal except for one transition - * at the point where we meet the incrementer. (We're running this with - * the guest stopped). - */ - unsigned address; - uint8_t first_byte; - uint8_t last_byte; - bool hit_edge = false; - int bad = 0; - - qtest_memread(who, start_address, &first_byte, 1); - last_byte = first_byte; - - for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; - address += TEST_MEM_PAGE_SIZE) - { - uint8_t b; - qtest_memread(who, address, &b, 1); - if (b != last_byte) { - if (((b + 1) % 256) == last_byte && !hit_edge) { - /* This is OK, the guest stopped at the point of - * incrementing the previous page but didn't get - * to us yet. - */ - hit_edge = true; - last_byte = b; - } else { - bad++; - if (bad <= 10) { - fprintf(stderr, "Memory content inconsistency at %x" - " first_byte = %x last_byte = %x current = %x" - " hit_edge = %x\n", - address, first_byte, last_byte, b, hit_edge); - } - } - } - } - if (bad >= 10) { - fprintf(stderr, "and in another %d pages", bad - 10); - } - g_assert(bad == 0); -} - -static void cleanup(const char *filename) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); - - unlink(path); -} - -/* - * Our goal is to ensure that we run a single full migration - * iteration, and also dirty memory, ensuring that at least - * one further iteration is required. - * - * We can't directly synchronize with the start of a migration - * so we have to apply some tricks monitoring memory that is - * transferred. - * - * Initially we set the migration bandwidth to an insanely - * low value, with tiny max downtime too. This basically - * guarantees migration will never complete. - * - * This will result in a test that is unacceptably slow though, - * so we can't let the entire migration pass run at this speed. - * Our intent is to let it run just long enough that we can - * prove data prior to the marker has been transferred *AND* - * also prove this transferred data is dirty again. - * - * Before migration starts, we write a 64-bit magic marker - * into a fixed location in the src VM RAM. - * - * Then watch dst memory until the marker appears. This is - * proof that start_address -> MAGIC_OFFSET_BASE has been - * transferred. - * - * Finally we go back to the source and read a byte just - * before the marker until we see it flip in value. This - * is proof that start_address -> MAGIC_OFFSET_BASE - * is now dirty again. - * - * IOW, we're guaranteed at least a 2nd migration pass - * at this point. - * - * We can now let migration run at full speed to finish - * the test - */ -static void migrate_prepare_for_dirty_mem(QTestState *from) -{ - /* - * The guest workflow iterates from start_address to - * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE - * bytes. - * - * IOW, if we write to mem at a point which is NOT - * a multiple of TEST_MEM_PAGE_SIZE, our write won't - * conflict with the migration workflow. - * - * We put in a marker here, that we'll use to determine - * when the data has been transferred to the dst. - */ - qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); -} - -static void migrate_wait_for_dirty_mem(QTestState *from, - QTestState *to) -{ - uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; - uint64_t marker_address = start_address + MAGIC_OFFSET; - uint8_t watch_byte; - - /* - * Wait for the MAGIC_MARKER to get transferred, as an - * indicator that a migration pass has made some known - * amount of progress. - */ - do { - usleep(1000 * 10); - } while (qtest_readq(to, marker_address) != MAGIC_MARKER); - - - /* If suspended, src only iterates once, and watch_byte may never change */ - if (src_state.suspend_me) { - return; - } - - /* - * Now ensure that already transferred bytes are - * dirty again from the guest workload. Note the - * guest byte value will wrap around and by chance - * match the original watch_byte. This is harmless - * as we'll eventually see a different value if we - * keep watching - */ - watch_byte = qtest_readb(from, watch_address); - do { - usleep(1000 * 10); - } while (qtest_readb(from, watch_address) == watch_byte); -} - -typedef struct { - /* - * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors - * unconditionally, because it means the user would like to be verbose. - */ - bool hide_stderr; - bool use_shmem; - /* only launch the target process */ - bool only_target; - /* Use dirty ring if true; dirty logging otherwise */ - bool use_dirty_ring; - const char *opts_source; - const char *opts_target; - /* suspend the src before migrating to dest. */ - bool suspend_me; -} MigrateStart; - -/* - * A hook that runs after the src and dst QEMUs have been - * created, but before the migration is started. This can - * be used to set migration parameters and capabilities. - * - * Returns: NULL, or a pointer to opaque state to be - * later passed to the TestMigrateFinishHook - */ -typedef void * (*TestMigrateStartHook)(QTestState *from, - QTestState *to); - -/* - * A hook that runs after the migration has finished, - * regardless of whether it succeeded or failed, but - * before QEMU has terminated (unless it self-terminated - * due to migration error) - * - * @opaque is a pointer to state previously returned - * by the TestMigrateStartHook if any, or NULL. - */ -typedef void (*TestMigrateEndHook)(QTestState *from, - QTestState *to, - void *opaque); - -typedef struct { - /* Optional: fine tune start parameters */ - MigrateStart start; - - /* Required: the URI for the dst QEMU to listen on */ - const char *listen_uri; - - /* - * Optional: the URI for the src QEMU to connect to - * If NULL, then it will query the dst QEMU for its actual - * listening address and use that as the connect address. - * This allows for dynamically picking a free TCP port. - */ - const char *connect_uri; - - /* - * Optional: JSON-formatted list of src QEMU URIs. If a port is - * defined as '0' in any QDict key a value of '0' will be - * automatically converted to the correct destination port. - */ - const char *connect_channels; - - /* Optional: callback to run at start to set migration parameters */ - TestMigrateStartHook start_hook; - /* Optional: callback to run at finish to cleanup */ - TestMigrateEndHook end_hook; - - /* - * Optional: normally we expect the migration process to complete. - * - * There can be a variety of reasons and stages in which failure - * can happen during tests. - * - * If a failure is expected to happen at time of establishing - * the connection, then MIG_TEST_FAIL will indicate that the dst - * QEMU is expected to stay running and accept future migration - * connections. - * - * If a failure is expected to happen while processing the - * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate - * that the dst QEMU is expected to quit with non-zero exit status - */ - enum { - /* This test should succeed, the default */ - MIG_TEST_SUCCEED = 0, - /* This test should fail, dest qemu should keep alive */ - MIG_TEST_FAIL, - /* This test should fail, dest qemu should fail with abnormal status */ - MIG_TEST_FAIL_DEST_QUIT_ERR, - /* The QMP command for this migration should fail with an error */ - MIG_TEST_QMP_ERROR, - } result; - - /* - * Optional: set number of migration passes to wait for, if live==true. - * If zero, then merely wait for a few MB of dirty data - */ - unsigned int iterations; - - /* - * Optional: whether the guest CPUs should be running during a precopy - * migration test. We used to always run with live but it took much - * longer so we reduced live tests to only the ones that have solid - * reason to be tested live-only. For each of the new test cases for - * precopy please provide justifications to use live explicitly (please - * refer to existing ones with live=true), or use live=off by default. - */ - bool live; - - /* Postcopy specific fields */ - void *postcopy_data; - bool postcopy_preempt; - PostcopyRecoveryFailStage postcopy_recovery_fail_stage; -} MigrateCommon; - -static int migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) -{ - g_autofree gchar *arch_source = NULL; - g_autofree gchar *arch_target = NULL; - /* options for source and target */ - g_autofree gchar *arch_opts = NULL; - g_autofree gchar *cmd_source = NULL; - g_autofree gchar *cmd_target = NULL; - const gchar *ignore_stderr; - g_autofree char *shmem_opts = NULL; - g_autofree char *shmem_path = NULL; - const char *kvm_opts = NULL; - const char *arch = qtest_get_arch(); - const char *memory_size; - const char *machine_alias, *machine_opts = ""; - g_autofree char *machine = NULL; - - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - dst_state = (QTestMigrationState) { }; - src_state = (QTestMigrationState) { }; - bootpath = bootfile_create(arch, tmpfs, args->suspend_me); - src_state.suspend_me = args->suspend_me; - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - memory_size = "150M"; - - if (g_str_equal(arch, "i386")) { - machine_alias = "pc"; - } else { - machine_alias = "q35"; - } - arch_opts = g_strdup_printf( - "-drive if=none,id=d0,file=%s,format=raw " - "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); - start_address = X86_TEST_MEM_START; - end_address = X86_TEST_MEM_END; - } else if (g_str_equal(arch, "s390x")) { - memory_size = "128M"; - machine_alias = "s390-ccw-virtio"; - arch_opts = g_strdup_printf("-bios %s", bootpath); - start_address = S390_TEST_MEM_START; - end_address = S390_TEST_MEM_END; - } else if (strcmp(arch, "ppc64") == 0) { - memory_size = "256M"; - start_address = PPC_TEST_MEM_START; - end_address = PPC_TEST_MEM_END; - machine_alias = "pseries"; - machine_opts = "vsmt=8"; - arch_opts = g_strdup_printf( - "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " - "-bios %s", bootpath); - } else if (strcmp(arch, "aarch64") == 0) { - memory_size = "150M"; - machine_alias = "virt"; - machine_opts = "gic-version=3"; - arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); - start_address = ARM_TEST_MEM_START; - end_address = ARM_TEST_MEM_END; - } else { - g_assert_not_reached(); - } - - if (!getenv("QTEST_LOG") && args->hide_stderr) { -#ifndef _WIN32 - ignore_stderr = "2>/dev/null"; -#else - /* - * On Windows the QEMU executable is created via CreateProcess() and - * IO redirection does not work, so don't bother adding IO redirection - * to the command line. - */ - ignore_stderr = ""; -#endif - } else { - ignore_stderr = ""; - } - - if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); - shmem_opts = g_strdup_printf( - "-object memory-backend-file,id=mem0,size=%s" - ",mem-path=%s,share=on -numa node,memdev=mem0", - memory_size, shmem_path); - } - - if (args->use_dirty_ring) { - kvm_opts = ",dirty-ring-size=4096"; - } - - if (!qtest_has_machine(machine_alias)) { - g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); - g_test_skip(msg); - return -1; - } - - machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, - QEMU_ENV_DST); - - g_test_message("Using machine type: %s", machine); - - cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " - "-machine %s,%s " - "-name source,debug-threads=on " - "-m %s " - "-serial file:%s/src_serial " - "%s %s %s %s %s", - kvm_opts ? kvm_opts : "", - machine, machine_opts, - memory_size, tmpfs, - arch_opts ? arch_opts : "", - arch_source ? arch_source : "", - shmem_opts ? shmem_opts : "", - args->opts_source ? args->opts_source : "", - ignore_stderr); - if (!args->only_target) { - *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); - qtest_qmp_set_event_callback(*from, - migrate_watch_for_events, - &src_state); - } - - cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " - "-machine %s,%s " - "-name target,debug-threads=on " - "-m %s " - "-serial file:%s/dest_serial " - "-incoming %s " - "%s %s %s %s %s", - kvm_opts ? kvm_opts : "", - machine, machine_opts, - memory_size, tmpfs, uri, - arch_opts ? arch_opts : "", - arch_target ? arch_target : "", - shmem_opts ? shmem_opts : "", - args->opts_target ? args->opts_target : "", - ignore_stderr); - *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); - qtest_qmp_set_event_callback(*to, - migrate_watch_for_events, - &dst_state); - - /* - * Remove shmem file immediately to avoid memory leak in test failed case. - * It's valid because QEMU has already opened this file - */ - if (args->use_shmem) { - unlink(shmem_path); - } - - /* - * Always enable migration events. Libvirt always uses it, let's try - * to mimic as closer as that. - */ - migrate_set_capability(*from, "events", true); - migrate_set_capability(*to, "events", true); - - return 0; -} - -static void migrate_end(QTestState *from, QTestState *to, bool test_dest) -{ - unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; - - qtest_quit(from); - - if (test_dest) { - qtest_memread(to, start_address, &dest_byte_a, 1); - - /* Destination still running, wait for a byte to change */ - do { - qtest_memread(to, start_address, &dest_byte_b, 1); - usleep(1000 * 10); - } while (dest_byte_a == dest_byte_b); - - qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); - - /* With it stopped, check nothing changes */ - qtest_memread(to, start_address, &dest_byte_c, 1); - usleep(1000 * 200); - qtest_memread(to, start_address, &dest_byte_d, 1); - g_assert_cmpint(dest_byte_c, ==, dest_byte_d); - - check_guests_ram(to); - } - - qtest_quit(to); - - cleanup("migsocket"); - cleanup("src_serial"); - cleanup("dest_serial"); - cleanup(FILE_TEST_FILENAME); -} #ifdef CONFIG_GNUTLS struct TestMigrateTLSPSKData { @@ -936,92 +388,6 @@ migrate_hook_end_tls_x509(QTestState *from, #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr, - MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_start(&from, &to, "defer", &args->start)) { - return -1; - } - - if (args->start_hook) { - args->postcopy_data = args->start_hook(from, to); - } - - migrate_set_capability(from, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-blocktime", true); - - if (args->postcopy_preempt) { - migrate_set_capability(from, "postcopy-preempt", true); - migrate_set_capability(to, "postcopy-preempt", true); - } - - migrate_ensure_non_converge(from); - - migrate_prepare_for_dirty_mem(from); - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { " - " 'channels': [ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ] } }"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - wait_for_suspend(from, &src_state); - - migrate_qmp(from, to, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - *from_ptr = from; - *to_ptr = to; - - return 0; -} - -static void migrate_postcopy_complete(QTestState *from, QTestState *to, - MigrateCommon *args) -{ - MigrationTestEnv *env = migration_get_env(); - - wait_for_migration_complete(from); - - if (args->start.suspend_me) { - /* wakeup succeeds only if guest is suspended */ - qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); - } - - /* Make sure we get at least one "B" on destination */ - wait_for_serial("dest_serial"); - - if (env->uffd_feature_thread_id) { - read_blocktime(to); - } - - if (args->end_hook) { - args->end_hook(from, to, args->postcopy_data); - args->postcopy_data = NULL; - } - - migrate_end(from, to, true); -} - -static void test_postcopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - migrate_postcopy_start(from, to, &src_state); - migrate_postcopy_complete(from, to, args); -} - static void test_postcopy(void) { MigrateCommon args = { }; @@ -1070,192 +436,6 @@ static void test_postcopy_preempt_tls_psk(void) } #endif -static void wait_for_postcopy_status(QTestState *one, const char *status) -{ - wait_for_migration_status(one, status, - (const char * []) { - "failed", "active", - "completed", NULL - }); -} - -static void postcopy_recover_fail(QTestState *from, QTestState *to, - PostcopyRecoveryFailStage stage) -{ -#ifndef _WIN32 - bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); - int ret, pair1[2], pair2[2]; - char c; - - g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); - - /* Create two unrelated socketpairs */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); - g_assert_cmpint(ret, ==, 0); - - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); - g_assert_cmpint(ret, ==, 0); - - /* - * Give the guests unpaired ends of the sockets, so they'll all blocked - * at reading. This mimics a wrong channel established. - */ - qtest_qmp_fds_assert_success(from, &pair1[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qtest_qmp_fds_assert_success(to, &pair2[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - /* - * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to - * emulate the 1st byte of a real recovery, but stops from there to - * keep dest QEMU in RECOVER. This is needed so that we can kick off - * the recover process on dest QEMU (by triggering the G_IO_IN event). - * - * NOTE: this trick is not needed on src QEMUs, because src doesn't - * rely on an pre-existing G_IO_IN event, so it will always trigger the - * upcoming recovery anyway even if it can read nothing. - */ -#define QEMU_VM_COMMAND 0x08 - c = QEMU_VM_COMMAND; - ret = send(pair2[1], &c, 1, 0); - g_assert_cmpint(ret, ==, 1); - - if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { - /* - * This will make src QEMU to fail at an early stage when trying to - * resume later, where it shouldn't reach RECOVER stage at all. - */ - close(pair1[1]); - } - - migrate_recover(to, "fd:fd-mig"); - migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); - - /* - * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. - * Make sure it appears along the way. - */ - migration_event_wait(from, "postcopy-recover-setup"); - - if (fail_early) { - /* - * When fails at reconnection, src QEMU will automatically goes - * back to PAUSED state. Making sure there is an event in this - * case: Libvirt relies on this to detect early reconnection - * errors. - */ - migration_event_wait(from, "postcopy-paused"); - } else { - /* - * We want to test "fail later" at RECOVER stage here. Make sure - * both QEMU instances will go into RECOVER stage first, then test - * kicking them out using migrate-pause. - * - * Explicitly check the RECOVER event on src, that's what Libvirt - * relies on, rather than polling. - */ - migration_event_wait(from, "postcopy-recover"); - wait_for_postcopy_status(from, "postcopy-recover"); - - /* Need an explicit kick on src QEMU in this case */ - migrate_pause(from); - } - - /* - * For all failure cases, we'll reach such states on both sides now. - * Check them. - */ - wait_for_postcopy_status(from, "postcopy-paused"); - wait_for_postcopy_status(to, "postcopy-recover"); - - /* - * Kick dest QEMU out too. This is normally not needed in reality - * because when the channel is shutdown it should also happen on src. - * However here we used separate socket pairs so we need to do that - * explicitly. - */ - migrate_pause(to); - wait_for_postcopy_status(to, "postcopy-paused"); - - close(pair1[0]); - close(pair2[0]); - close(pair2[1]); - - if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { - close(pair1[1]); - } -#endif -} - -static void test_postcopy_recovery_common(MigrateCommon *args) -{ - QTestState *from, *to; - g_autofree char *uri = NULL; - - /* Always hide errors for postcopy recover tests since they're expected */ - args->start.hide_stderr = true; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - - /* Turn postcopy speed down, 4K/s is slow enough on any machines */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); - - /* Now we start the postcopy */ - migrate_postcopy_start(from, to, &src_state); - - /* - * Wait until postcopy is really started; we can only run the - * migrate-pause command during a postcopy - */ - wait_for_migration_status(from, "postcopy-active", NULL); - - /* - * Manually stop the postcopy migration. This emulates a network - * failure with the migration socket - */ - migrate_pause(from); - - /* - * Wait for destination side to reach postcopy-paused state. The - * migrate-recover command can only succeed if destination machine - * is in the paused state - */ - wait_for_postcopy_status(to, "postcopy-paused"); - wait_for_postcopy_status(from, "postcopy-paused"); - - if (args->postcopy_recovery_fail_stage) { - /* - * Test when a wrong socket specified for recover, and then the - * ability to kick it out, and continue with a correct socket. - */ - postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); - /* continue with a good recovery */ - } - - /* - * Create a new socket to emulate a new channel that is different - * from the broken migration channel; tell the destination to - * listen to the new port - */ - uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); - migrate_recover(to, uri); - - /* - * Try to rebuild the migration channel using the resume flag and - * the newly created channel - */ - migrate_qmp(from, to, uri, NULL, "{'resume': true}"); - - /* Restore the postcopy bandwidth to unlimited */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - - migrate_postcopy_complete(from, to, args); -} - static void test_postcopy_recovery(void) { MigrateCommon args = { }; @@ -1383,218 +563,10 @@ static void test_analyze_script(void) g_test_fail(); } migrate_end(from, to, false); - cleanup("migfile"); + unlink(file); } #endif -static void test_precopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - void *data_hook = NULL; - - if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - /* Wait for the first serial output from the source */ - if (args->result == MIG_TEST_SUCCEED) { - wait_for_serial("src_serial"); - wait_for_suspend(from, &src_state); - } - - if (args->live) { - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - } else { - /* - * Testing non-live migration, we allow it to run at - * full speed to ensure short test case duration. - * For tests expected to fail, we don't need to - * change anything. - */ - if (args->result == MIG_TEST_SUCCEED) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - wait_for_stop(from, &src_state); - migrate_ensure_converge(from); - } - } - - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - goto finish; - } - - migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); - - if (args->result != MIG_TEST_SUCCEED) { - bool allow_active = args->result == MIG_TEST_FAIL; - wait_for_migration_fail(from, allow_active); - - if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { - qtest_set_expected_status(to, EXIT_FAILURE); - } - } else { - if (args->live) { - /* - * For initial iteration(s) we must do a full pass, - * but for the final iteration, we need only wait - * for some dirty mem before switching to converge - */ - while (args->iterations > 1) { - wait_for_migration_pass(from, &src_state); - args->iterations--; - } - migrate_wait_for_dirty_mem(from, to); - - migrate_ensure_converge(from); - - /* - * We do this first, as it has a timeout to stop us - * hanging forever if migration didn't converge - */ - wait_for_migration_complete(from); - - wait_for_stop(from, &src_state); - - } else { - wait_for_migration_complete(from); - /* - * Must wait for dst to finish reading all incoming - * data on the socket before issuing 'cont' otherwise - * it'll be ignored - */ - wait_for_migration_complete(to); - - qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); - } - - wait_for_resume(to, &dst_state); - - if (args->start.suspend_me) { - /* wakeup succeeds only if guest is suspended */ - qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); - } - - wait_for_serial("dest_serial"); - } - -finish: - if (args->end_hook) { - args->end_hook(from, to, data_hook); - } - - migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - -static void file_dirty_offset_region(void) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET; - g_autofree char *data = g_new0(char, size); - - memset(data, FILE_TEST_MARKER, size); - g_assert(g_file_set_contents(path, data, size, NULL)); -} - -static void file_check_offset_region(void) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET; - g_autofree char *expected = g_new0(char, size); - g_autofree char *actual = NULL; - uint64_t *stream_start; - - /* - * Ensure the skipped offset region's data has not been touched - * and the migration stream starts at the right place. - */ - - memset(expected, FILE_TEST_MARKER, size); - - g_assert(g_file_get_contents(path, &actual, NULL, NULL)); - g_assert(!memcmp(actual, expected, size)); - - stream_start = (uint64_t *)(actual + size); - g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); -} - -static void test_file_common(MigrateCommon *args, bool stop_src) -{ - QTestState *from, *to; - void *data_hook = NULL; - bool check_offset = false; - - if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - /* - * File migration is never live. We can keep the source VM running - * during migration, but the destination will not be running - * concurrently. - */ - g_assert_false(args->live); - - if (g_strrstr(args->connect_uri, "offset=")) { - check_offset = true; - /* - * This comes before the start_hook because it's equivalent to - * a management application creating the file and writing to - * it so hooks should expect the file to be already present. - */ - file_dirty_offset_region(); - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - migrate_ensure_converge(from); - wait_for_serial("src_serial"); - - if (stop_src) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - wait_for_stop(from, &src_state); - } - - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); - goto finish; - } - - migrate_qmp(from, to, args->connect_uri, NULL, "{}"); - wait_for_migration_complete(from); - - /* - * We need to wait for the source to finish before starting the - * destination. - */ - migrate_incoming_qmp(to, args->connect_uri, "{}"); - wait_for_migration_complete(to); - - if (stop_src) { - qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); - } - wait_for_resume(to, &dst_state); - - wait_for_serial("dest_serial"); - - if (check_offset) { - file_check_offset_region(); - } - -finish: - if (args->end_hook) { - args->end_hook(from, to, data_hook); - } - - migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - static void test_precopy_unix_plain(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -1730,7 +702,7 @@ static void test_ignore_shared(void) migrate_wait_for_dirty_mem(from, to); - wait_for_stop(from, &src_state); + wait_for_stop(from, get_src()); qtest_qmp_eventwait(to, "RESUME"); @@ -2524,7 +1496,7 @@ static void test_auto_converge(void) break; } usleep(20); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } while (true); /* The first percentage of throttling should be at least init_pct */ g_assert_cmpint(percentage, >=, init_pct); @@ -2580,26 +1552,6 @@ static void test_auto_converge(void) migrate_end(from, to, true); } -static void * -migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, - QTestState *to, - const char *method) -{ - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_parameter_str(from, "multifd-compression", method); - migrate_set_parameter_str(to, "multifd-compression", method); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); - - return NULL; -} - static void * migrate_hook_start_precopy_tcp_multifd(QTestState *from, QTestState *to) @@ -3031,7 +1983,7 @@ static void test_multifd_tcp_cancel(void) migrate_ensure_converge(from); - wait_for_stop(from, &src_state); + wait_for_stop(from, get_src()); qtest_qmp_eventwait(to2, "RESUME"); wait_for_serial("dest_serial"); @@ -3176,6 +2128,7 @@ static QTestState *dirtylimit_start_vm(void) { QTestState *vm = NULL; g_autofree gchar *cmd = NULL; + const char *bootpath; bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " @@ -3191,8 +2144,10 @@ static QTestState *dirtylimit_start_vm(void) static void dirtylimit_stop_vm(QTestState *vm) { + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); + qtest_quit(vm); - cleanup("vm_serial"); + unlink(path); } static void test_vcpu_dirty_limit(void) @@ -3370,7 +2325,7 @@ static void test_dirty_limit(void) read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); usleep(100); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } /* Now cancel migrate and wait for dirty limit throttle switch off */ @@ -3383,7 +2338,7 @@ static void test_dirty_limit(void) read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); usleep(100); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } while (throttle_us_per_full != 0 && --max_try_count); /* Assert dirty limit is not in service */ @@ -3413,7 +2368,7 @@ static void test_dirty_limit(void) read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); usleep(100); - g_assert_false(src_state.stop_seen); + g_assert_false(get_src()->stop_seen); } /* @@ -3446,75 +2401,6 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -MigrationTestEnv *migration_get_env(void) -{ - static MigrationTestEnv *env; - g_autoptr(GError) err = NULL; - - if (env) { - return env; - } - - env = g_new0(MigrationTestEnv, 1); - env->qemu_src = getenv(QEMU_ENV_SRC); - env->qemu_dst = getenv(QEMU_ENV_DST); - - /* - * The default QTEST_QEMU_BINARY must always be provided because - * that is what helpers use to query the accel type and - * architecture. - */ - if (env->qemu_src && env->qemu_dst) { - g_test_message("Only one of %s, %s is allowed", - QEMU_ENV_SRC, QEMU_ENV_DST); - exit(1); - } - - env->has_kvm = qtest_has_accel("kvm"); - env->has_tcg = qtest_has_accel("tcg"); - - if (!env->has_tcg && !env->has_kvm) { - g_test_skip("No KVM or TCG accelerator available"); - return env; - } - - env->has_dirty_ring = kvm_dirty_ring_supported(); - env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); - env->arch = qtest_get_arch(); - env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); - - env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); - if (!env->tmpfs) { - g_test_message("Can't create temporary directory in %s: %s", - g_get_tmp_dir(), err->message); - } - g_assert(env->tmpfs); - - return env; -} - -static int migration_env_clean(MigrationTestEnv *env) -{ - char *tmpfs; - int ret = 0; - - if (!env) { - return ret; - } - - bootfile_delete(); - - tmpfs = env->tmpfs; - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - g_free(tmpfs); - - return ret; -} - int main(int argc, char **argv) { MigrationTestEnv *env; diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c index 8f75f64093..fac059d11d 100644 --- a/tests/qtest/migration/bootfile.c +++ b/tests/qtest/migration/bootfile.c @@ -34,7 +34,7 @@ void bootfile_delete(void) bootpath = NULL; } -char *bootfile_create(const char *arch, char *dir, bool suspend_me) +char *bootfile_create(const char *arch, const char *dir, bool suspend_me) { unsigned char *content; size_t len; diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h index 4f5099d765..6d6a67386e 100644 --- a/tests/qtest/migration/bootfile.h +++ b/tests/qtest/migration/bootfile.h @@ -34,6 +34,6 @@ #define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) void bootfile_delete(void); -char *bootfile_create(const char *arch, char *dir, bool suspend_me); +char *bootfile_create(const char *arch, const char *dir, bool suspend_me); #endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c new file mode 100644 index 0000000000..a902936039 --- /dev/null +++ b/tests/qtest/migration/framework.c @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qapi/qmp/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" + +unsigned start_address; +unsigned end_address; +static QTestMigrationState src_state; +static QTestMigrationState dst_state; +static char *tmpfs; + +/* + * An initial 3 MB offset is used as that corresponds + * to ~1 sec of data transfer with our bandwidth setting. + */ +#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) +/* + * A further 1k is added to ensure we're not a multiple + * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes + * from the migration guest workload. + */ +#define MAGIC_OFFSET_SHUFFLE 1024 +#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) +#define MAGIC_MARKER 0xFEED12345678CAFEULL + + +/* + * Wait for some output in the serial output file, + * we get an 'A' followed by an endless string of 'B's + * but on the destination we won't have the A (unless we enabled suspend/resume) + */ +void wait_for_serial(const char *side) +{ + g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); + FILE *serialfile = fopen(serialpath, "r"); + + do { + int readvalue = fgetc(serialfile); + + switch (readvalue) { + case 'A': + /* Fine */ + break; + + case 'B': + /* It's alive! */ + fclose(serialfile); + return; + + case EOF: + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + + default: + fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); + g_assert_not_reached(); + } + } while (true); +} + +void migrate_prepare_for_dirty_mem(QTestState *from) +{ + /* + * The guest workflow iterates from start_address to + * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE + * bytes. + * + * IOW, if we write to mem at a point which is NOT + * a multiple of TEST_MEM_PAGE_SIZE, our write won't + * conflict with the migration workflow. + * + * We put in a marker here, that we'll use to determine + * when the data has been transferred to the dst. + */ + qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); +} + +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to) +{ + uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; + uint64_t marker_address = start_address + MAGIC_OFFSET; + uint8_t watch_byte; + + /* + * Wait for the MAGIC_MARKER to get transferred, as an + * indicator that a migration pass has made some known + * amount of progress. + */ + do { + usleep(1000 * 10); + } while (qtest_readq(to, marker_address) != MAGIC_MARKER); + + + /* If suspended, src only iterates once, and watch_byte may never change */ + if (src_state.suspend_me) { + return; + } + + /* + * Now ensure that already transferred bytes are + * dirty again from the guest workload. Note the + * guest byte value will wrap around and by chance + * match the original watch_byte. This is harmless + * as we'll eventually see a different value if we + * keep watching + */ + watch_byte = qtest_readb(from, watch_address); + do { + usleep(1000 * 10); + } while (qtest_readb(from, watch_address) == watch_byte); +} + +static void check_guests_ram(QTestState *who) +{ + /* + * Our ASM test will have been incrementing one byte from each page from + * start_address to < end_address in order. This gives us a constraint + * that any page's byte should be equal or less than the previous pages + * byte (mod 256); and they should all be equal except for one transition + * at the point where we meet the incrementer. (We're running this with + * the guest stopped). + */ + unsigned address; + uint8_t first_byte; + uint8_t last_byte; + bool hit_edge = false; + int bad = 0; + + qtest_memread(who, start_address, &first_byte, 1); + last_byte = first_byte; + + for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; + address += TEST_MEM_PAGE_SIZE) + { + uint8_t b; + qtest_memread(who, address, &b, 1); + if (b != last_byte) { + if (((b + 1) % 256) == last_byte && !hit_edge) { + /* + * This is OK, the guest stopped at the point of + * incrementing the previous page but didn't get + * to us yet. + */ + hit_edge = true; + last_byte = b; + } else { + bad++; + if (bad <= 10) { + fprintf(stderr, "Memory content inconsistency at %x" + " first_byte = %x last_byte = %x current = %x" + " hit_edge = %x\n", + address, first_byte, last_byte, b, hit_edge); + } + } + } + } + if (bad >= 10) { + fprintf(stderr, "and in another %d pages", bad - 10); + } + g_assert(bad == 0); +} + +static void cleanup(const char *filename) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); + + unlink(path); +} + +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args) +{ + g_autofree gchar *arch_source = NULL; + g_autofree gchar *arch_target = NULL; + /* options for source and target */ + g_autofree gchar *arch_opts = NULL; + g_autofree gchar *cmd_source = NULL; + g_autofree gchar *cmd_target = NULL; + const gchar *ignore_stderr; + g_autofree char *shmem_opts = NULL; + g_autofree char *shmem_path = NULL; + const char *kvm_opts = NULL; + const char *arch = qtest_get_arch(); + const char *memory_size; + const char *machine_alias, *machine_opts = ""; + g_autofree char *machine = NULL; + const char *bootpath; + + if (args->use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootpath = bootfile_create(arch, tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + memory_size = "150M"; + + if (g_str_equal(arch, "i386")) { + machine_alias = "pc"; + } else { + machine_alias = "q35"; + } + arch_opts = g_strdup_printf( + "-drive if=none,id=d0,file=%s,format=raw " + "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); + start_address = X86_TEST_MEM_START; + end_address = X86_TEST_MEM_END; + } else if (g_str_equal(arch, "s390x")) { + memory_size = "128M"; + machine_alias = "s390-ccw-virtio"; + arch_opts = g_strdup_printf("-bios %s", bootpath); + start_address = S390_TEST_MEM_START; + end_address = S390_TEST_MEM_END; + } else if (strcmp(arch, "ppc64") == 0) { + memory_size = "256M"; + start_address = PPC_TEST_MEM_START; + end_address = PPC_TEST_MEM_END; + machine_alias = "pseries"; + machine_opts = "vsmt=8"; + arch_opts = g_strdup_printf( + "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " + "-bios %s", bootpath); + } else if (strcmp(arch, "aarch64") == 0) { + memory_size = "150M"; + machine_alias = "virt"; + machine_opts = "gic-version=3"; + arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); + start_address = ARM_TEST_MEM_START; + end_address = ARM_TEST_MEM_END; + } else { + g_assert_not_reached(); + } + + if (!getenv("QTEST_LOG") && args->hide_stderr) { +#ifndef _WIN32 + ignore_stderr = "2>/dev/null"; +#else + /* + * On Windows the QEMU executable is created via CreateProcess() and + * IO redirection does not work, so don't bother adding IO redirection + * to the command line. + */ + ignore_stderr = ""; +#endif + } else { + ignore_stderr = ""; + } + + if (args->use_shmem) { + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_opts = g_strdup_printf( + "-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + memory_size, shmem_path); + } + + if (args->use_dirty_ring) { + kvm_opts = ",dirty-ring-size=4096"; + } + + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return -1; + } + + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, + QEMU_ENV_DST); + + g_test_message("Using machine type: %s", machine); + + cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " + "-name source,debug-threads=on " + "-m %s " + "-serial file:%s/src_serial " + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_size, tmpfs, + arch_opts ? arch_opts : "", + arch_source ? arch_source : "", + shmem_opts ? shmem_opts : "", + args->opts_source ? args->opts_source : "", + ignore_stderr); + if (!args->only_target) { + *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_events, + &src_state); + } + + cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " + "-name target,debug-threads=on " + "-m %s " + "-serial file:%s/dest_serial " + "-incoming %s " + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_size, tmpfs, uri, + arch_opts ? arch_opts : "", + arch_target ? arch_target : "", + shmem_opts ? shmem_opts : "", + args->opts_target ? args->opts_target : "", + ignore_stderr); + *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_events, + &dst_state); + + /* + * Remove shmem file immediately to avoid memory leak in test failed case. + * It's valid because QEMU has already opened this file + */ + if (args->use_shmem) { + unlink(shmem_path); + } + + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(*from, "events", true); + migrate_set_capability(*to, "events", true); + + return 0; +} + +void migrate_end(QTestState *from, QTestState *to, bool test_dest) +{ + unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; + + qtest_quit(from); + + if (test_dest) { + qtest_memread(to, start_address, &dest_byte_a, 1); + + /* Destination still running, wait for a byte to change */ + do { + qtest_memread(to, start_address, &dest_byte_b, 1); + usleep(1000 * 10); + } while (dest_byte_a == dest_byte_b); + + qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); + + /* With it stopped, check nothing changes */ + qtest_memread(to, start_address, &dest_byte_c, 1); + usleep(1000 * 200); + qtest_memread(to, start_address, &dest_byte_d, 1); + g_assert_cmpint(dest_byte_c, ==, dest_byte_d); + + check_guests_ram(to); + } + + qtest_quit(to); + + cleanup("migsocket"); + cleanup("src_serial"); + cleanup("dest_serial"); + cleanup(FILE_TEST_FILENAME); +} + +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr, + MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_start(&from, &to, "defer", &args->start)) { + return -1; + } + + if (args->start_hook) { + args->postcopy_data = args->start_hook(from, to); + } + + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-blocktime", true); + + if (args->postcopy_preempt) { + migrate_set_capability(from, "postcopy-preempt", true); + migrate_set_capability(to, "postcopy-preempt", true); + } + + migrate_ensure_non_converge(from); + + migrate_prepare_for_dirty_mem(from); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ] } }"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + *from_ptr = from; + *to_ptr = to; + + return 0; +} + +static void migrate_postcopy_complete(QTestState *from, QTestState *to, + MigrateCommon *args) +{ + MigrationTestEnv *env = migration_get_env(); + + wait_for_migration_complete(from); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + /* Make sure we get at least one "B" on destination */ + wait_for_serial("dest_serial"); + + if (env->uffd_feature_thread_id) { + read_blocktime(to); + } + + if (args->end_hook) { + args->end_hook(from, to, args->postcopy_data); + args->postcopy_data = NULL; + } + + migrate_end(from, to, true); +} + +void test_postcopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + migrate_postcopy_start(from, to, &src_state); + migrate_postcopy_complete(from, to, args); +} + +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { + "failed", "active", + "completed", NULL + }); +} + +static void postcopy_recover_fail(QTestState *from, QTestState *to, + PostcopyRecoveryFailStage stage) +{ +#ifndef _WIN32 + bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); + int ret, pair1[2], pair2[2]; + char c; + + g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); + + /* Create two unrelated socketpairs */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); + g_assert_cmpint(ret, ==, 0); + + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); + g_assert_cmpint(ret, ==, 0); + + /* + * Give the guests unpaired ends of the sockets, so they'll all blocked + * at reading. This mimics a wrong channel established. + */ + qtest_qmp_fds_assert_success(from, &pair1[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qtest_qmp_fds_assert_success(to, &pair2[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + /* + * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to + * emulate the 1st byte of a real recovery, but stops from there to + * keep dest QEMU in RECOVER. This is needed so that we can kick off + * the recover process on dest QEMU (by triggering the G_IO_IN event). + * + * NOTE: this trick is not needed on src QEMUs, because src doesn't + * rely on an pre-existing G_IO_IN event, so it will always trigger the + * upcoming recovery anyway even if it can read nothing. + */ +#define QEMU_VM_COMMAND 0x08 + c = QEMU_VM_COMMAND; + ret = send(pair2[1], &c, 1, 0); + g_assert_cmpint(ret, ==, 1); + + if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + /* + * This will make src QEMU to fail at an early stage when trying to + * resume later, where it shouldn't reach RECOVER stage at all. + */ + close(pair1[1]); + } + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); + + /* + * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. + * Make sure it appears along the way. + */ + migration_event_wait(from, "postcopy-recover-setup"); + + if (fail_early) { + /* + * When fails at reconnection, src QEMU will automatically goes + * back to PAUSED state. Making sure there is an event in this + * case: Libvirt relies on this to detect early reconnection + * errors. + */ + migration_event_wait(from, "postcopy-paused"); + } else { + /* + * We want to test "fail later" at RECOVER stage here. Make sure + * both QEMU instances will go into RECOVER stage first, then test + * kicking them out using migrate-pause. + * + * Explicitly check the RECOVER event on src, that's what Libvirt + * relies on, rather than polling. + */ + migration_event_wait(from, "postcopy-recover"); + wait_for_postcopy_status(from, "postcopy-recover"); + + /* Need an explicit kick on src QEMU in this case */ + migrate_pause(from); + } + + /* + * For all failure cases, we'll reach such states on both sides now. + * Check them. + */ + wait_for_postcopy_status(from, "postcopy-paused"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * Kick dest QEMU out too. This is normally not needed in reality + * because when the channel is shutdown it should also happen on src. + * However here we used separate socket pairs so we need to do that + * explicitly. + */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair2[0]); + close(pair2[1]); + + if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + close(pair1[1]); + } +#endif +} + +void test_postcopy_recovery_common(MigrateCommon *args) +{ + QTestState *from, *to; + g_autofree char *uri = NULL; + + /* Always hide errors for postcopy recover tests since they're expected */ + args->start.hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to, &src_state); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active", NULL); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + wait_for_postcopy_status(to, "postcopy-paused"); + wait_for_postcopy_status(from, "postcopy-paused"); + + if (args->postcopy_recovery_fail_stage) { + /* + * Test when a wrong socket specified for recover, and then the + * ability to kick it out, and continue with a correct socket. + */ + postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); + /* continue with a good recovery */ + } + + /* + * Create a new socket to emulate a new channel that is different + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + migrate_qmp(from, to, uri, NULL, "{'resume': true}"); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); + + migrate_postcopy_complete(from, to, args); +} + +void test_precopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + void *data_hook = NULL; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + /* Wait for the first serial output from the source */ + if (args->result == MIG_TEST_SUCCEED) { + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + } + + if (args->live) { + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + } else { + /* + * Testing non-live migration, we allow it to run at + * full speed to ensure short test case duration. + * For tests expected to fail, we don't need to + * change anything. + */ + if (args->result == MIG_TEST_SUCCEED) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + migrate_ensure_converge(from); + } + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); + + if (args->result != MIG_TEST_SUCCEED) { + bool allow_active = args->result == MIG_TEST_FAIL; + wait_for_migration_fail(from, allow_active); + + if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { + qtest_set_expected_status(to, EXIT_FAILURE); + } + } else { + if (args->live) { + /* + * For initial iteration(s) we must do a full pass, + * but for the final iteration, we need only wait + * for some dirty mem before switching to converge + */ + while (args->iterations > 1) { + wait_for_migration_pass(from, &src_state); + args->iterations--; + } + migrate_wait_for_dirty_mem(from, to); + + migrate_ensure_converge(from); + + /* + * We do this first, as it has a timeout to stop us + * hanging forever if migration didn't converge + */ + wait_for_migration_complete(from); + + wait_for_stop(from, &src_state); + + } else { + wait_for_migration_complete(from); + /* + * Must wait for dst to finish reading all incoming + * data on the socket before issuing 'cont' otherwise + * it'll be ignored + */ + wait_for_migration_complete(to); + + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + + wait_for_resume(to, &dst_state); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + wait_for_serial("dest_serial"); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +static void file_dirty_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *data = g_new0(char, size); + + memset(data, FILE_TEST_MARKER, size); + g_assert(g_file_set_contents(path, data, size, NULL)); +} + +static void file_check_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *expected = g_new0(char, size); + g_autofree char *actual = NULL; + uint64_t *stream_start; + + /* + * Ensure the skipped offset region's data has not been touched + * and the migration stream starts at the right place. + */ + + memset(expected, FILE_TEST_MARKER, size); + + g_assert(g_file_get_contents(path, &actual, NULL, NULL)); + g_assert(!memcmp(actual, expected, size)); + + stream_start = (uint64_t *)(actual + size); + g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); +} + +void test_file_common(MigrateCommon *args, bool stop_src) +{ + QTestState *from, *to; + void *data_hook = NULL; + bool check_offset = false; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* + * File migration is never live. We can keep the source VM running + * during migration, but the destination will not be running + * concurrently. + */ + g_assert_false(args->live); + + if (g_strrstr(args->connect_uri, "offset=")) { + check_offset = true; + /* + * This comes before the start_hook because it's equivalent to + * a management application creating the file and writing to + * it so hooks should expect the file to be already present. + */ + file_dirty_offset_region(); + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + migrate_ensure_converge(from); + wait_for_serial("src_serial"); + + if (stop_src) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); + wait_for_migration_complete(from); + + /* + * We need to wait for the source to finish before starting the + * destination. + */ + migrate_incoming_qmp(to, args->connect_uri, "{}"); + wait_for_migration_complete(to); + + if (stop_src) { + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + wait_for_resume(to, &dst_state); + + wait_for_serial("dest_serial"); + + if (check_offset) { + file_check_offset_region(); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method) +{ + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_parameter_str(from, "multifd-compression", method); + migrate_set_parameter_str(to, "multifd-compression", method); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + + return NULL; +} + +QTestMigrationState *get_src(void) +{ + return &src_state; +} + +MigrationTestEnv *migration_get_env(void) +{ + static MigrationTestEnv *env; + g_autoptr(GError) err = NULL; + + if (env) { + return env; + } + + env = g_new0(MigrationTestEnv, 1); + env->qemu_src = getenv(QEMU_ENV_SRC); + env->qemu_dst = getenv(QEMU_ENV_DST); + + /* + * The default QTEST_QEMU_BINARY must always be provided because + * that is what helpers use to query the accel type and + * architecture. + */ + if (env->qemu_src && env->qemu_dst) { + g_test_message("Only one of %s, %s is allowed", + QEMU_ENV_SRC, QEMU_ENV_DST); + exit(1); + } + + env->has_kvm = qtest_has_accel("kvm"); + env->has_tcg = qtest_has_accel("tcg"); + + if (!env->has_tcg && !env->has_kvm) { + g_test_skip("No KVM or TCG accelerator available"); + return env; + } + + env->has_dirty_ring = kvm_dirty_ring_supported(); + env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); + env->arch = qtest_get_arch(); + env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); + + env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); + if (!env->tmpfs) { + g_test_message("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + } + g_assert(env->tmpfs); + + tmpfs = env->tmpfs; + + return env; +} + +int migration_env_clean(MigrationTestEnv *env) +{ + char *tmpfs; + int ret = 0; + + if (!env) { + return ret; + } + + bootfile_delete(); + + tmpfs = env->tmpfs; + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + g_free(tmpfs); + + return ret; +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h new file mode 100644 index 0000000000..6d5176b056 --- /dev/null +++ b/tests/qtest/migration/framework.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef TEST_FRAMEWORK_H +#define TEST_FRAMEWORK_H + +#include "libqtest.h" + +#define FILE_TEST_FILENAME "migfile" +#define FILE_TEST_OFFSET 0x1000 +#define FILE_TEST_MARKER 'X' + +typedef struct MigrationTestEnv { + bool has_kvm; + bool has_tcg; + bool has_uffd; + bool uffd_feature_thread_id; + bool has_dirty_ring; + bool is_x86; + const char *arch; + const char *qemu_src; + const char *qemu_dst; + char *tmpfs; +} MigrationTestEnv; + +MigrationTestEnv *migration_get_env(void); +int migration_env_clean(MigrationTestEnv *env); + +/* + * A hook that runs after the src and dst QEMUs have been + * created, but before the migration is started. This can + * be used to set migration parameters and capabilities. + * + * Returns: NULL, or a pointer to opaque state to be + * later passed to the TestMigrateEndHook + */ +typedef void * (*TestMigrateStartHook)(QTestState *from, + QTestState *to); + +/* + * A hook that runs after the migration has finished, + * regardless of whether it succeeded or failed, but + * before QEMU has terminated (unless it self-terminated + * due to migration error) + * + * @opaque is a pointer to state previously returned + * by the TestMigrateStartHook if any, or NULL. + */ +typedef void (*TestMigrateEndHook)(QTestState *from, + QTestState *to, + void *opaque); + +/* + * Our goal is to ensure that we run a single full migration + * iteration, and also dirty memory, ensuring that at least + * one further iteration is required. + * + * We can't directly synchronize with the start of a migration + * so we have to apply some tricks monitoring memory that is + * transferred. + * + * Initially we set the migration bandwidth to an insanely + * low value, with tiny max downtime too. This basically + * guarantees migration will never complete. + * + * This will result in a test that is unacceptably slow though, + * so we can't let the entire migration pass run at this speed. + * Our intent is to let it run just long enough that we can + * prove data prior to the marker has been transferred *AND* + * also prove this transferred data is dirty again. + * + * Before migration starts, we write a 64-bit magic marker + * into a fixed location in the src VM RAM. + * + * Then watch dst memory until the marker appears. This is + * proof that start_address -> MAGIC_OFFSET_BASE has been + * transferred. + * + * Finally we go back to the source and read a byte just + * before the marker until we see it flip in value. This + * is proof that start_address -> MAGIC_OFFSET_BASE + * is now dirty again. + * + * IOW, we're guaranteed at least a 2nd migration pass + * at this point. + * + * We can now let migration run at full speed to finish + * the test + */ +typedef struct { + /* + * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors + * unconditionally, because it means the user would like to be verbose. + */ + bool hide_stderr; + bool use_shmem; + /* only launch the target process */ + bool only_target; + /* Use dirty ring if true; dirty logging otherwise */ + bool use_dirty_ring; + const char *opts_source; + const char *opts_target; + /* suspend the src before migrating to dest. */ + bool suspend_me; +} MigrateStart; + +typedef enum PostcopyRecoveryFailStage { + /* + * "no failure" must be 0 as it's the default. OTOH, real failure + * cases must be >0 to make sure they trigger by a "if" test. + */ + POSTCOPY_FAIL_NONE = 0, + POSTCOPY_FAIL_CHANNEL_ESTABLISH, + POSTCOPY_FAIL_RECOVERY, + POSTCOPY_FAIL_MAX +} PostcopyRecoveryFailStage; + +typedef struct { + /* Optional: fine tune start parameters */ + MigrateStart start; + + /* Required: the URI for the dst QEMU to listen on */ + const char *listen_uri; + + /* + * Optional: the URI for the src QEMU to connect to + * If NULL, then it will query the dst QEMU for its actual + * listening address and use that as the connect address. + * This allows for dynamically picking a free TCP port. + */ + const char *connect_uri; + + /* + * Optional: JSON-formatted list of src QEMU URIs. If a port is + * defined as '0' in any QDict key a value of '0' will be + * automatically converted to the correct destination port. + */ + const char *connect_channels; + + /* Optional: callback to run at start to set migration parameters */ + TestMigrateStartHook start_hook; + /* Optional: callback to run at finish to cleanup */ + TestMigrateEndHook end_hook; + + /* + * Optional: normally we expect the migration process to complete. + * + * There can be a variety of reasons and stages in which failure + * can happen during tests. + * + * If a failure is expected to happen at time of establishing + * the connection, then MIG_TEST_FAIL will indicate that the dst + * QEMU is expected to stay running and accept future migration + * connections. + * + * If a failure is expected to happen while processing the + * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate + * that the dst QEMU is expected to quit with non-zero exit status + */ + enum { + /* This test should succeed, the default */ + MIG_TEST_SUCCEED = 0, + /* This test should fail, dest qemu should keep alive */ + MIG_TEST_FAIL, + /* This test should fail, dest qemu should fail with abnormal status */ + MIG_TEST_FAIL_DEST_QUIT_ERR, + /* The QMP command for this migration should fail with an error */ + MIG_TEST_QMP_ERROR, + } result; + + /* + * Optional: set number of migration passes to wait for, if live==true. + * If zero, then merely wait for a few MB of dirty data + */ + unsigned int iterations; + + /* + * Optional: whether the guest CPUs should be running during a precopy + * migration test. We used to always run with live but it took much + * longer so we reduced live tests to only the ones that have solid + * reason to be tested live-only. For each of the new test cases for + * precopy please provide justifications to use live explicitly (please + * refer to existing ones with live=true), or use live=off by default. + */ + bool live; + + /* Postcopy specific fields */ + void *postcopy_data; + bool postcopy_preempt; + PostcopyRecoveryFailStage postcopy_recovery_fail_stage; +} MigrateCommon; + +void wait_for_serial(const char *side); +void migrate_prepare_for_dirty_mem(QTestState *from); +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to); +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args); +void migrate_end(QTestState *from, QTestState *to, bool test_dest); + +void test_postcopy_common(MigrateCommon *args); +void test_postcopy_recovery_common(MigrateCommon *args); +void test_precopy_common(MigrateCommon *args); +void test_file_common(MigrateCommon *args, bool stop_src); +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method); + +typedef struct QTestMigrationState QTestMigrationState; +QTestMigrationState *get_src(void); + +#endif /* TEST_FRAMEWORK_H */ From 979ee2a76feb3bf8dea4624482f7399077e39a87 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:55 -0300 Subject: [PATCH 0188/2892] tests/qtest/migration: Split TLS tests from migration-test.c The migration-test.c file has become unwieldy large. It's quite confusing to navigate with all the test definitions mixed with hook definitions. The TLS tests make this worse with ifdef'ery. Since we're planning on having a smaller set of tests to run as smoke testing on all architectures, I'm taking the time to split some tests into their own file. Move the TLS tests into a file of their own. Acked-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 8 +- tests/qtest/migration-test.c | 788 +---------------------------- tests/qtest/migration/framework.h | 6 + tests/qtest/migration/tls-tests.c | 791 ++++++++++++++++++++++++++++++ 4 files changed, 804 insertions(+), 789 deletions(-) create mode 100644 tests/qtest/migration/tls-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c5cc09fbf2..6dc4ba9d40 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -339,11 +339,13 @@ migration_files = [files( 'migration/migration-util.c', )] +migration_tls_files = [] if gnutls.found() - migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] + migration_tls_files = [files('migration/tls-tests.c', + '../unit/crypto-tls-psk-helpers.c'), gnutls] if tasn1.found() - migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] + migration_tls_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] endif endif @@ -354,7 +356,7 @@ qtests = { 'migration/migration-util.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], - 'migration-test': migration_files, + 'migration-test': migration_files + migration_tls_files, 'pxe-test': files('boot-sector.c'), 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c'), 'qos-test': [chardev, io, qos_test_ss.apply({}).sources()], diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index a60150483e..7395403f50 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -26,12 +26,6 @@ #include "migration/framework.h" #include "migration/migration-qmp.h" #include "migration/migration-util.h" -#ifdef CONFIG_GNUTLS -# include "tests/unit/crypto-tls-psk-helpers.h" -# ifdef CONFIG_TASN1 -# include "tests/unit/crypto-tls-x509-helpers.h" -# endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ /* * Dirtylimit stop working if dirty page rate error @@ -49,345 +43,6 @@ static char *tmpfs; -#ifdef CONFIG_GNUTLS -struct TestMigrateTLSPSKData { - char *workdir; - char *workdiralt; - char *pskfile; - char *pskfilealt; -}; - -static void * -migrate_hook_start_tls_psk_common(QTestState *from, - QTestState *to, - bool mismatch) -{ - struct TestMigrateTLSPSKData *data = - g_new0(struct TestMigrateTLSPSKData, 1); - - data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); - data->pskfile = g_strdup_printf("%s/%s", data->workdir, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdir, 0700); - test_tls_psk_init(data->pskfile); - - if (mismatch) { - data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); - data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdiralt, 0700); - test_tls_psk_init_alt(data->pskfilealt); - } - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'username': 'qemu'} }", - data->workdir); - - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'server'," - " 'dir': %s } }", - mismatch ? data->workdiralt : data->workdir); - - migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); - migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); - - return data; -} - -static void * -migrate_hook_start_tls_psk_match(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_tls_psk_common(from, to, false); -} - -static void * -migrate_hook_start_tls_psk_mismatch(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_tls_psk_common(from, to, true); -} - -static void -migrate_hook_end_tls_psk(QTestState *from, - QTestState *to, - void *opaque) -{ - struct TestMigrateTLSPSKData *data = opaque; - - test_tls_psk_cleanup(data->pskfile); - if (data->pskfilealt) { - test_tls_psk_cleanup(data->pskfilealt); - } - rmdir(data->workdir); - if (data->workdiralt) { - rmdir(data->workdiralt); - } - - g_free(data->workdiralt); - g_free(data->pskfilealt); - g_free(data->workdir); - g_free(data->pskfile); - g_free(data); -} - -#ifdef CONFIG_TASN1 -typedef struct { - char *workdir; - char *keyfile; - char *cacert; - char *servercert; - char *serverkey; - char *clientcert; - char *clientkey; -} TestMigrateTLSX509Data; - -typedef struct { - bool verifyclient; - bool clientcert; - bool hostileclient; - bool authzclient; - const char *certhostname; - const char *certipaddr; -} TestMigrateTLSX509; - -static void * -migrate_hook_start_tls_x509_common(QTestState *from, - QTestState *to, - TestMigrateTLSX509 *args) -{ - TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); - - data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); - data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); - - data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); - data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); - data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); - if (args->clientcert) { - data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); - data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); - } - - g_mkdir_with_parents(data->workdir, 0700); - - test_tls_init(data->keyfile); -#ifndef _WIN32 - g_assert(link(data->keyfile, data->serverkey) == 0); -#else - g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); -#endif - if (args->clientcert) { -#ifndef _WIN32 - g_assert(link(data->keyfile, data->clientkey) == 0); -#else - g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); -#endif - } - - TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); - if (args->clientcert) { - TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, - args->hostileclient ? - QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : - QCRYPTO_TLS_TEST_CLIENT_NAME, - data->clientcert); - test_tls_deinit_cert(&servercertreq); - } - - TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, - data->servercert, - args->certhostname, - args->certipaddr); - test_tls_deinit_cert(&clientcertreq); - test_tls_deinit_cert(&cacertreq); - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509client0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': true} }", - data->workdir); - migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); - if (args->certhostname) { - migrate_set_parameter_str(from, "tls-hostname", args->certhostname); - } - - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509server0'," - " 'endpoint': 'server'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': %i} }", - data->workdir, args->verifyclient); - migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); - - if (args->authzclient) { - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'authz-simple'," - " 'id': 'tlsauthz0'," - " 'identity': %s} }", - "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); - migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); - } - - return data; -} - -/* - * The normal case: match server's cert hostname against - * whatever host we were telling QEMU to connect to (if any) - */ -static void * -migrate_hook_start_tls_x509_default_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "127.0.0.1" - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to (if any), - * so we must give QEMU an explicit hostname to validate - */ -static void * -migrate_hook_start_tls_x509_override_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certhostname = "qemu.org", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to, and so we - * expect the client to reject the server - */ -static void * -migrate_hook_start_tls_x509_mismatch_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "10.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -static void * -migrate_hook_start_tls_x509_friendly_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -static void * -migrate_hook_start_tls_x509_hostile_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .hostileclient = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and no server verification - */ -static void * -migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and server verification rejecting - */ -static void * -migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .certipaddr = "127.0.0.1", - }; - return migrate_hook_start_tls_x509_common(from, to, &args); -} - -static void -migrate_hook_end_tls_x509(QTestState *from, - QTestState *to, - void *opaque) -{ - TestMigrateTLSX509Data *data = opaque; - - test_tls_cleanup(data->keyfile); - g_free(data->keyfile); - - unlink(data->cacert); - g_free(data->cacert); - unlink(data->servercert); - g_free(data->servercert); - unlink(data->serverkey); - g_free(data->serverkey); - - if (data->clientcert) { - unlink(data->clientcert); - g_free(data->clientcert); - } - if (data->clientkey) { - unlink(data->clientkey); - g_free(data->clientkey); - } - - rmdir(data->workdir); - g_free(data->workdir); - - g_free(data); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - static void test_postcopy(void) { MigrateCommon args = { }; @@ -413,29 +68,6 @@ static void test_postcopy_preempt(void) test_postcopy_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_postcopy_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt_tls_psk(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_common(&args); -} -#endif - static void test_postcopy_recovery(void) { MigrateCommon args = { }; @@ -461,18 +93,6 @@ static void test_postcopy_recovery_fail_reconnect(void) test_postcopy_recovery_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_postcopy_recovery_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_recovery_common(&args); -} -#endif - static void test_postcopy_preempt_recovery(void) { MigrateCommon args = { @@ -482,21 +102,6 @@ static void test_postcopy_preempt_recovery(void) test_postcopy_recovery_common(&args); } -#ifdef CONFIG_GNUTLS -/* This contains preempt+recovery+tls test altogether */ -static void test_postcopy_preempt_all(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_postcopy_recovery_common(&args); -} - -#endif - static void test_baddest(void) { MigrateStart args = { @@ -631,53 +236,6 @@ static void test_precopy_unix_dirty_ring(void) test_precopy_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_precopy_unix_tls_psk(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_unix_tls_x509_default_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_tls_x509_override_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - #if 0 /* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) @@ -1086,125 +644,6 @@ static void test_precopy_tcp_switchover_ack(void) test_precopy_common(&args); } -#ifdef CONFIG_GNUTLS -static void test_precopy_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_psk_mismatch, - .end_hook = migrate_hook_end_tls_psk, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_mismatch_host(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_mismatch_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_friendly_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_friendly_client, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_hostile_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_hostile_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_allow_anon_client, - .end_hook = migrate_hook_end_tls_x509, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_reject_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - #ifndef _WIN32 static void *migrate_hook_start_fd(QTestState *from, QTestState *to) @@ -1745,163 +1184,6 @@ static void test_multifd_tcp_uadk(void) } #endif -#ifdef CONFIG_GNUTLS -static void * -migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_psk_match(from, to); -} - -static void * -migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_psk_mismatch(from, to); -} - -#ifdef CONFIG_TASN1 -static void * -migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_default_host(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_override_host(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_mismatch_host(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_allow_anon_client(from, to); -} - -static void * -migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - return migrate_hook_start_tls_x509_reject_anon_client(from, to); -} -#endif /* CONFIG_TASN1 */ - -static void test_multifd_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, - .end_hook = migrate_hook_end_tls_psk, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_multifd_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_mismatch_host(void) -{ - /* - * This has different behaviour to the non-multifd case. - * - * In non-multifd case when client aborts due to mismatched - * cert host, the server has already started trying to load - * migration state, and so it exits with I/O failure. - * - * In multifd case when client aborts due to mismatched - * cert host, the server is still waiting for the other - * multifd connections to arrive so hasn't started trying - * to load migration state, and thus just aborts the migration - * without exiting. - */ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - /* * This test does: * source target @@ -2412,6 +1694,8 @@ int main(int argc, char **argv) tmpfs = env->tmpfs; + migration_test_add_tls(env); + migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); @@ -2487,61 +1771,11 @@ int main(int argc, char **argv) test_multifd_file_mapped_ram_fdset_dio); #endif -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/precopy/unix/tls/psk", - test_precopy_unix_tls_psk); - - if (env->has_uffd) { - /* - * NOTE: psk test is enough for postcopy, as other types of TLS - * channels are tested under precopy. Here what we want to test is the - * general postcopy path that has TLS channel enabled. - */ - migration_test_add("/migration/postcopy/tls/psk", - test_postcopy_tls_psk); - migration_test_add("/migration/postcopy/recovery/tls/psk", - test_postcopy_recovery_tls_psk); - migration_test_add("/migration/postcopy/preempt/tls/psk", - test_postcopy_preempt_tls_psk); - migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", - test_postcopy_preempt_all); - } -#ifdef CONFIG_TASN1 - migration_test_add("/migration/precopy/unix/tls/x509/default-host", - test_precopy_unix_tls_x509_default_host); - migration_test_add("/migration/precopy/unix/tls/x509/override-host", - test_precopy_unix_tls_x509_override_host); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); migration_test_add("/migration/precopy/tcp/plain/switchover-ack", test_precopy_tcp_switchover_ack); -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/precopy/tcp/tls/psk/match", - test_precopy_tcp_tls_psk_match); - migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", - test_precopy_tcp_tls_psk_mismatch); -#ifdef CONFIG_TASN1 - migration_test_add("/migration/precopy/tcp/tls/x509/default-host", - test_precopy_tcp_tls_x509_default_host); - migration_test_add("/migration/precopy/tcp/tls/x509/override-host", - test_precopy_tcp_tls_x509_override_host); - migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", - test_precopy_tcp_tls_x509_mismatch_host); - migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", - test_precopy_tcp_tls_x509_friendly_client); - migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", - test_precopy_tcp_tls_x509_hostile_client); - migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", - test_precopy_tcp_tls_x509_allow_anon_client); - migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", - test_precopy_tcp_tls_x509_reject_anon_client); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ #ifndef _WIN32 migration_test_add("/migration/precopy/fd/tcp", @@ -2600,24 +1834,6 @@ int main(int argc, char **argv) migration_test_add("/migration/multifd/tcp/plain/uadk", test_multifd_tcp_uadk); #endif -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/multifd/tcp/tls/psk/match", - test_multifd_tcp_tls_psk_match); - migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", - test_multifd_tcp_tls_psk_mismatch); -#ifdef CONFIG_TASN1 - migration_test_add("/migration/multifd/tcp/tls/x509/default-host", - test_multifd_tcp_tls_x509_default_host); - migration_test_add("/migration/multifd/tcp/tls/x509/override-host", - test_multifd_tcp_tls_x509_override_host); - migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", - test_multifd_tcp_tls_x509_mismatch_host); - migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", - test_multifd_tcp_tls_x509_allow_anon_client); - migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", - test_multifd_tcp_tls_x509_reject_anon_client); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ if (g_str_equal(env->arch, "x86_64") && env->has_kvm && env->has_dirty_ring) { diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 6d5176b056..1aad4da0e0 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -215,4 +215,10 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, typedef struct QTestMigrationState QTestMigrationState; QTestMigrationState *get_src(void); +#ifdef CONFIG_GNUTLS +void migration_test_add_tls(MigrationTestEnv *env); +#else +static inline void migration_test_add_tls(MigrationTestEnv *env) {}; +#endif + #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c new file mode 100644 index 0000000000..5704a1f992 --- /dev/null +++ b/tests/qtest/migration/tls-tests.c @@ -0,0 +1,791 @@ +/* + * QTest testcases for TLS migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#include "tests/unit/crypto-tls-psk-helpers.h" +#ifdef CONFIG_TASN1 +# include "tests/unit/crypto-tls-x509-helpers.h" +#endif /* CONFIG_TASN1 */ + + +struct TestMigrateTLSPSKData { + char *workdir; + char *workdiralt; + char *pskfile; + char *pskfilealt; +}; + +static char *tmpfs; + +static void * +migrate_hook_start_tls_psk_common(QTestState *from, + QTestState *to, + bool mismatch) +{ + struct TestMigrateTLSPSKData *data = + g_new0(struct TestMigrateTLSPSKData, 1); + + data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); + data->pskfile = g_strdup_printf("%s/%s", data->workdir, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdir, 0700); + test_tls_psk_init(data->pskfile); + + if (mismatch) { + data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); + data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdiralt, 0700); + test_tls_psk_init_alt(data->pskfilealt); + } + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'username': 'qemu'} }", + data->workdir); + + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'server'," + " 'dir': %s } }", + mismatch ? data->workdiralt : data->workdir); + + migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); + migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); + + return data; +} + +static void * +migrate_hook_start_tls_psk_match(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, false); +} + +static void * +migrate_hook_start_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, true); +} + +static void +migrate_hook_end_tls_psk(QTestState *from, + QTestState *to, + void *opaque) +{ + struct TestMigrateTLSPSKData *data = opaque; + + test_tls_psk_cleanup(data->pskfile); + if (data->pskfilealt) { + test_tls_psk_cleanup(data->pskfilealt); + } + rmdir(data->workdir); + if (data->workdiralt) { + rmdir(data->workdiralt); + } + + g_free(data->workdiralt); + g_free(data->pskfilealt); + g_free(data->workdir); + g_free(data->pskfile); + g_free(data); +} + +#ifdef CONFIG_TASN1 +typedef struct { + char *workdir; + char *keyfile; + char *cacert; + char *servercert; + char *serverkey; + char *clientcert; + char *clientkey; +} TestMigrateTLSX509Data; + +typedef struct { + bool verifyclient; + bool clientcert; + bool hostileclient; + bool authzclient; + const char *certhostname; + const char *certipaddr; +} TestMigrateTLSX509; + +static void * +migrate_hook_start_tls_x509_common(QTestState *from, + QTestState *to, + TestMigrateTLSX509 *args) +{ + TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); + + data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); + data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); + + data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); + data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); + data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); + if (args->clientcert) { + data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); + data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); + } + + g_mkdir_with_parents(data->workdir, 0700); + + test_tls_init(data->keyfile); +#ifndef _WIN32 + g_assert(link(data->keyfile, data->serverkey) == 0); +#else + g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); +#endif + if (args->clientcert) { +#ifndef _WIN32 + g_assert(link(data->keyfile, data->clientkey) == 0); +#else + g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); +#endif + } + + TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); + if (args->clientcert) { + TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, + args->hostileclient ? + QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : + QCRYPTO_TLS_TEST_CLIENT_NAME, + data->clientcert); + test_tls_deinit_cert(&servercertreq); + } + + TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, + data->servercert, + args->certhostname, + args->certipaddr); + test_tls_deinit_cert(&clientcertreq); + test_tls_deinit_cert(&cacertreq); + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509client0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': true} }", + data->workdir); + migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); + if (args->certhostname) { + migrate_set_parameter_str(from, "tls-hostname", args->certhostname); + } + + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509server0'," + " 'endpoint': 'server'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': %i} }", + data->workdir, args->verifyclient); + migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); + + if (args->authzclient) { + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'authz-simple'," + " 'id': 'tlsauthz0'," + " 'identity': %s} }", + "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); + migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); + } + + return data; +} + +/* + * The normal case: match server's cert hostname against + * whatever host we were telling QEMU to connect to (if any) + */ +static void * +migrate_hook_start_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "127.0.0.1" + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to (if any), + * so we must give QEMU an explicit hostname to validate + */ +static void * +migrate_hook_start_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certhostname = "qemu.org", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to, and so we + * expect the client to reject the server + */ +static void * +migrate_hook_start_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "10.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_friendly_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_hostile_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .hostileclient = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and no server verification + */ +static void * +migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and server verification rejecting + */ +static void * +migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void +migrate_hook_end_tls_x509(QTestState *from, + QTestState *to, + void *opaque) +{ + TestMigrateTLSX509Data *data = opaque; + + test_tls_cleanup(data->keyfile); + g_free(data->keyfile); + + unlink(data->cacert); + g_free(data->cacert); + unlink(data->servercert); + g_free(data->servercert); + unlink(data->serverkey); + g_free(data->serverkey); + + if (data->clientcert) { + unlink(data->clientcert); + g_free(data->clientcert); + } + if (data->clientkey) { + unlink(data->clientkey); + g_free(data->clientkey); + } + + rmdir(data->workdir); + g_free(data->workdir); + + g_free(data); +} +#endif /* CONFIG_TASN1 */ + +static void test_postcopy_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt_tls_psk(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_recovery_common(&args); +} + +/* This contains preempt+recovery+tls test altogether */ +static void test_postcopy_preempt_all(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_precopy_unix_tls_psk(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_unix_tls_x509_default_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_tls_x509_override_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void test_precopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_mismatch_host(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_friendly_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_friendly_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_hostile_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_hostile_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void * +migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_match(from, to); +} + +static void * +migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_mismatch(from, to); +} + +#ifdef CONFIG_TASN1 +static void * +migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_default_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_override_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_mismatch_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_allow_anon_client(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_reject_anon_client(from, to); +} +#endif /* CONFIG_TASN1 */ + +static void test_multifd_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_multifd_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_mismatch_host(void) +{ + /* + * This has different behaviour to the non-multifd case. + * + * In non-multifd case when client aborts due to mismatched + * cert host, the server has already started trying to load + * migration state, and so it exits with I/O failure. + * + * In multifd case when client aborts due to mismatched + * cert host, the server is still waiting for the other + * multifd connections to arrive so hasn't started trying + * to load migration state, and thus just aborts the migration + * without exiting. + */ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +void migration_test_add_tls(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add("/migration/precopy/unix/tls/psk", + test_precopy_unix_tls_psk); + + if (env->has_uffd) { + /* + * NOTE: psk test is enough for postcopy, as other types of TLS + * channels are tested under precopy. Here what we want to test is the + * general postcopy path that has TLS channel enabled. + */ + migration_test_add("/migration/postcopy/tls/psk", + test_postcopy_tls_psk); + migration_test_add("/migration/postcopy/recovery/tls/psk", + test_postcopy_recovery_tls_psk); + migration_test_add("/migration/postcopy/preempt/tls/psk", + test_postcopy_preempt_tls_psk); + migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", + test_postcopy_preempt_all); + } +#ifdef CONFIG_TASN1 + migration_test_add("/migration/precopy/unix/tls/x509/default-host", + test_precopy_unix_tls_x509_default_host); + migration_test_add("/migration/precopy/unix/tls/x509/override-host", + test_precopy_unix_tls_x509_override_host); +#endif /* CONFIG_TASN1 */ + + migration_test_add("/migration/precopy/tcp/tls/psk/match", + test_precopy_tcp_tls_psk_match); + migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", + test_precopy_tcp_tls_psk_mismatch); +#ifdef CONFIG_TASN1 + migration_test_add("/migration/precopy/tcp/tls/x509/default-host", + test_precopy_tcp_tls_x509_default_host); + migration_test_add("/migration/precopy/tcp/tls/x509/override-host", + test_precopy_tcp_tls_x509_override_host); + migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", + test_precopy_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", + test_precopy_tcp_tls_x509_friendly_client); + migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", + test_precopy_tcp_tls_x509_hostile_client); + migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", + test_precopy_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", + test_precopy_tcp_tls_x509_reject_anon_client); +#endif /* CONFIG_TASN1 */ + + migration_test_add("/migration/multifd/tcp/tls/psk/match", + test_multifd_tcp_tls_psk_match); + migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", + test_multifd_tcp_tls_psk_mismatch); +#ifdef CONFIG_TASN1 + migration_test_add("/migration/multifd/tcp/tls/x509/default-host", + test_multifd_tcp_tls_x509_default_host); + migration_test_add("/migration/multifd/tcp/tls/x509/override-host", + test_multifd_tcp_tls_x509_override_host); + migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", + test_multifd_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", + test_multifd_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", + test_multifd_tcp_tls_x509_reject_anon_client); +#endif /* CONFIG_TASN1 */ +} From 932f74f3fe6ef6b17a642f91bed1ac76350e363c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:56 -0300 Subject: [PATCH 0189/2892] tests/qtest/migration: Split compression tests from migration-test.c Continuing the split of groups of tests from migration-test.c, split the compression tests into their own file. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 161 +-------------- tests/qtest/migration/compression-tests.c | 239 ++++++++++++++++++++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 242 insertions(+), 160 deletions(-) create mode 100644 tests/qtest/migration/compression-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 6dc4ba9d40..eeb8ecf695 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -337,6 +337,7 @@ migration_files = [files( 'migration/framework.c', 'migration/migration-qmp.c', 'migration/migration-util.c', + 'migration/compression-tests.c', )] migration_tls_files = [] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 7395403f50..3528676678 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -274,36 +274,6 @@ static void test_ignore_shared(void) } #endif -static void * -migrate_hook_start_xbzrle(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", true); - migrate_set_capability(to, "xbzrle", true); - - return NULL; -} - -static void test_precopy_unix_xbzrle(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_xbzrle, - .iterations = 2, - /* - * XBZRLE needs pages to be modified when doing the 2nd+ round - * iteration to have real data pushed to the stream. - */ - .live = true, - }; - - test_precopy_common(&args); -} - static void test_precopy_file(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -1016,61 +986,6 @@ migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, return NULL; } -static void * -migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, - QTestState *to) -{ - /* - * Overloading this test to also check that set_parameter does not error. - * This is also done in the tests for the other compression methods. - */ - migrate_set_parameter_int(from, "multifd-zlib-level", 2); - migrate_set_parameter_int(to, "multifd-zlib-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); -} - -#ifdef CONFIG_ZSTD -static void * -migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "multifd-zstd-level", 2); - migrate_set_parameter_int(to, "multifd-zstd-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); -} -#endif /* CONFIG_ZSTD */ - -#ifdef CONFIG_QATZIP -static void * -migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "multifd-qatzip-level", 2); - migrate_set_parameter_int(to, "multifd-qatzip-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); -} -#endif - -#ifdef CONFIG_QPL -static void * -migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); -} -#endif /* CONFIG_QPL */ -#ifdef CONFIG_UADK -static void * -migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); -} -#endif /* CONFIG_UADK */ - static void test_multifd_tcp_uri_none(void) { MigrateCommon args = { @@ -1131,59 +1046,6 @@ static void test_multifd_tcp_channels_none(void) test_precopy_common(&args); } -static void test_multifd_tcp_zlib(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_ZSTD -static void test_multifd_tcp_zstd(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_QATZIP -static void test_multifd_tcp_qatzip(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_QPL -static void test_multifd_tcp_qpl(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_UADK -static void test_multifd_tcp_uadk(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, - }; - test_precopy_common(&args); -} -#endif - /* * This test does: * source target @@ -1695,6 +1557,7 @@ int main(int argc, char **argv) tmpfs = env->tmpfs; migration_test_add_tls(env); + migration_test_add_compression(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 @@ -1728,10 +1591,6 @@ int main(int argc, char **argv) migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); - if (g_test_slow()) { - migration_test_add("/migration/precopy/unix/xbzrle", - test_precopy_unix_xbzrle); - } migration_test_add("/migration/precopy/file", test_precopy_file); migration_test_add("/migration/precopy/file/offset", @@ -1816,24 +1675,6 @@ int main(int argc, char **argv) test_multifd_tcp_no_zero_page); migration_test_add("/migration/multifd/tcp/plain/cancel", test_multifd_tcp_cancel); - migration_test_add("/migration/multifd/tcp/plain/zlib", - test_multifd_tcp_zlib); -#ifdef CONFIG_ZSTD - migration_test_add("/migration/multifd/tcp/plain/zstd", - test_multifd_tcp_zstd); -#endif -#ifdef CONFIG_QATZIP - migration_test_add("/migration/multifd/tcp/plain/qatzip", - test_multifd_tcp_qatzip); -#endif -#ifdef CONFIG_QPL - migration_test_add("/migration/multifd/tcp/plain/qpl", - test_multifd_tcp_qpl); -#endif -#ifdef CONFIG_UADK - migration_test_add("/migration/multifd/tcp/plain/uadk", - test_multifd_tcp_uadk); -#endif if (g_str_equal(env->arch, "x86_64") && env->has_kvm && env->has_dirty_ring) { diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c new file mode 100644 index 0000000000..6de87bc47d --- /dev/null +++ b/tests/qtest/migration/compression-tests.c @@ -0,0 +1,239 @@ +/* + * QTest testcases for migration compression + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qemu/module.h" + + +static char *tmpfs; + +#ifdef CONFIG_ZSTD +static void * +migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-zstd-level", 2); + migrate_set_parameter_int(to, "multifd-zstd-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); +} + +static void test_multifd_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_ZSTD */ + +#ifdef CONFIG_QATZIP +static void * +migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-qatzip-level", 2); + migrate_set_parameter_int(to, "multifd-qatzip-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); +} + +static void test_multifd_tcp_qatzip(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, + }; + test_precopy_common(&args); +} +#endif + +#ifdef CONFIG_QPL +static void * +migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); +} + +static void test_multifd_tcp_qpl(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_QPL */ + +#ifdef CONFIG_UADK +static void * +migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); +} + +static void * +migrate_hook_start_xbzrle(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + + migrate_set_capability(from, "xbzrle", true); + migrate_set_capability(to, "xbzrle", true); + + return NULL; +} + +static void test_precopy_unix_xbzrle(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_xbzrle, + .iterations = 2, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, + QTestState *to) +{ + /* + * Overloading this test to also check that set_parameter does not error. + * This is also done in the tests for the other compression methods. + */ + migrate_set_parameter_int(from, "multifd-zlib-level", 2); + migrate_set_parameter_int(to, "multifd-zlib-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); +} + +static void test_multifd_tcp_zlib(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_uadk(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_UADK */ + + +static void * +migrate_hook_start_xbzrle(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + + migrate_set_capability(from, "xbzrle", true); + migrate_set_capability(to, "xbzrle", true); + + return NULL; +} + +static void test_precopy_unix_xbzrle(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_xbzrle, + .iterations = 2, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, + QTestState *to) +{ + /* + * Overloading this test to also check that set_parameter does not error. + * This is also done in the tests for the other compression methods. + */ + migrate_set_parameter_int(from, "multifd-zlib-level", 2); + migrate_set_parameter_int(to, "multifd-zlib-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); +} + +static void test_multifd_tcp_zlib(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, + }; + test_precopy_common(&args); +} + +void migration_test_add_compression(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + +#ifdef CONFIG_ZSTD + migration_test_add("/migration/multifd/tcp/plain/zstd", + test_multifd_tcp_zstd); +#endif + +#ifdef CONFIG_QATZIP + migration_test_add("/migration/multifd/tcp/plain/qatzip", + test_multifd_tcp_qatzip); +#endif + +#ifdef CONFIG_QPL + migration_test_add("/migration/multifd/tcp/plain/qpl", + test_multifd_tcp_qpl); +#endif + +#ifdef CONFIG_UADK + migration_test_add("/migration/multifd/tcp/plain/uadk", + test_multifd_tcp_uadk); +#endif + + if (g_test_slow()) { + migration_test_add("/migration/precopy/unix/xbzrle", + test_precopy_unix_xbzrle); + } + + migration_test_add("/migration/multifd/tcp/plain/zlib", + test_multifd_tcp_zlib); +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 1aad4da0e0..bc978d4c1c 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -220,5 +220,6 @@ void migration_test_add_tls(MigrationTestEnv *env); #else static inline void migration_test_add_tls(MigrationTestEnv *env) {}; #endif +void migration_test_add_compression(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ From 5e87cf322f98e4b791fdcbe1d35eafd14036dba9 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:57 -0300 Subject: [PATCH 0190/2892] tests/qtest/migration: Split postcopy tests Split the next group of tests from migration-test.c, the postcopy tests. This is another well-defined group of tests and postcopy is a unique enough feature that it deserves it's own file. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 78 +----------------- tests/qtest/migration/framework.h | 1 + tests/qtest/migration/postcopy-tests.c | 106 +++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 77 deletions(-) create mode 100644 tests/qtest/migration/postcopy-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index eeb8ecf695..ff33034ca6 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -338,6 +338,7 @@ migration_files = [files( 'migration/migration-qmp.c', 'migration/migration-util.c', 'migration/compression-tests.c', + 'migration/postcopy-tests.c', )] migration_tls_files = [] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 3528676678..fa759022d1 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -43,65 +43,6 @@ static char *tmpfs; -static void test_postcopy(void) -{ - MigrateCommon args = { }; - - test_postcopy_common(&args); -} - -static void test_postcopy_suspend(void) -{ - MigrateCommon args = { - .start.suspend_me = true, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_recovery(void) -{ - MigrateCommon args = { }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_fail_handshake(void) -{ - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, - }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_fail_reconnect(void) -{ - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, - }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_preempt_recovery(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_recovery_common(&args); -} - static void test_baddest(void) { MigrateStart args = { @@ -1558,6 +1499,7 @@ int main(int argc, char **argv) migration_test_add_tls(env); migration_test_add_compression(env); + migration_test_add_postcopy(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 @@ -1571,24 +1513,6 @@ int main(int argc, char **argv) test_precopy_unix_suspend_notlive); } - if (env->has_uffd) { - migration_test_add("/migration/postcopy/plain", test_postcopy); - migration_test_add("/migration/postcopy/recovery/plain", - test_postcopy_recovery); - migration_test_add("/migration/postcopy/preempt/plain", - test_postcopy_preempt); - migration_test_add("/migration/postcopy/preempt/recovery/plain", - test_postcopy_preempt_recovery); - migration_test_add("/migration/postcopy/recovery/double-failures/handshake", - test_postcopy_recovery_fail_handshake); - migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", - test_postcopy_recovery_fail_reconnect); - if (env->is_x86) { - migration_test_add("/migration/postcopy/suspend", - test_postcopy_suspend); - } - } - migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); migration_test_add("/migration/precopy/file", diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index bc978d4c1c..1ac3aea4b2 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -221,5 +221,6 @@ void migration_test_add_tls(MigrationTestEnv *env); static inline void migration_test_add_tls(MigrationTestEnv *env) {}; #endif void migration_test_add_compression(MigrationTestEnv *env); +void migration_test_add_postcopy(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c new file mode 100644 index 0000000000..daf7449f2c --- /dev/null +++ b/tests/qtest/migration/postcopy-tests.c @@ -0,0 +1,106 @@ +/* + * QTest testcases for postcopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-util.h" +#include "qapi/qmp/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + +static void test_postcopy(void) +{ + MigrateCommon args = { }; + + test_postcopy_common(&args); +} + +static void test_postcopy_suspend(void) +{ + MigrateCommon args = { + .start.suspend_me = true, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery(void) +{ + MigrateCommon args = { }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_handshake(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_reconnect(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_preempt_recovery(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + }; + + test_postcopy_recovery_common(&args); +} + +void migration_test_add_postcopy(MigrationTestEnv *env) +{ + if (env->has_uffd) { + migration_test_add("/migration/postcopy/plain", test_postcopy); + migration_test_add("/migration/postcopy/recovery/plain", + test_postcopy_recovery); + migration_test_add("/migration/postcopy/preempt/plain", + test_postcopy_preempt); + migration_test_add("/migration/postcopy/preempt/recovery/plain", + test_postcopy_preempt_recovery); + + migration_test_add( + "/migration/postcopy/recovery/double-failures/handshake", + test_postcopy_recovery_fail_handshake); + + migration_test_add( + "/migration/postcopy/recovery/double-failures/reconnect", + test_postcopy_recovery_fail_reconnect); + + if (env->is_x86) { + migration_test_add("/migration/postcopy/suspend", + test_postcopy_suspend); + } + } +} From d8057eb3059e06e451fe3be71786525acc588ef3 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:58 -0300 Subject: [PATCH 0191/2892] tests/qtest/migration: Split file tests Split the file tests from migration-test.c. These are being moved to their own file due to being special enough compared with the regular stream migration. There is also the entire mapped-ram feature which depends on file migration. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 311 +------------------------- tests/qtest/migration/file-tests.c | 338 +++++++++++++++++++++++++++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 341 insertions(+), 310 deletions(-) create mode 100644 tests/qtest/migration/file-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ff33034ca6..ede5a9fb61 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -338,6 +338,7 @@ migration_files = [files( 'migration/migration-qmp.c', 'migration/migration-util.c', 'migration/compression-tests.c', + 'migration/file-tests.c', 'migration/postcopy-tests.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index fa759022d1..a2ce1c6b19 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -215,93 +215,6 @@ static void test_ignore_shared(void) } #endif -static void test_precopy_file(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - - test_file_common(&args, true); -} - -#ifndef _WIN32 -static void fdset_add_fds(QTestState *qts, const char *file, int flags, - int num_fds, bool direct_io) -{ - for (int i = 0; i < num_fds; i++) { - int fd; - -#ifdef O_DIRECT - /* only secondary channels can use direct-io */ - if (direct_io && i != 0) { - flags |= O_DIRECT; - } -#endif - - fd = open(file, flags, 0660); - assert(fd != -1); - - qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " - "'arguments': {'fdset-id': 1}}"); - close(fd); - } -} - -static void *migrate_hook_start_file_offset_fdset(QTestState *from, - QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 1, false); - fdset_add_fds(to, file, O_RDONLY, 1, false); - - return NULL; -} - -static void test_precopy_file_offset_fdset(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_file_offset_fdset, - }; - - test_file_common(&args, false); -} -#endif - -static void test_precopy_file_offset(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, - FILE_TEST_FILENAME, - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - - test_file_common(&args, false); -} - -static void test_precopy_file_offset_bad(void) -{ - /* using a value not supported by qemu_strtosz() */ - g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", - tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .result = MIG_TEST_QMP_ERROR, - }; - - test_file_common(&args, false); -} - static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) { migrate_set_parameter_str(from, "mode", "cpr-reboot"); @@ -313,14 +226,6 @@ static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) return NULL; } -static void *migrate_hook_start_mapped_ram(QTestState *from, QTestState *to) -{ - migrate_set_capability(from, "mapped-ram", true); - migrate_set_capability(to, "mapped-ram", true); - - return NULL; -} - static void test_mode_reboot(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -335,190 +240,6 @@ static void test_mode_reboot(void) test_file_common(&args, true); } -static void test_precopy_file_mapped_ram_live(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mapped_ram, - }; - - test_file_common(&args, false); -} - -static void test_precopy_file_mapped_ram(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mapped_ram, - }; - - test_file_common(&args, true); -} - -static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, - QTestState *to) -{ - migrate_hook_start_mapped_ram(from, to); - - migrate_set_parameter_int(from, "multifd-channels", 4); - migrate_set_parameter_int(to, "multifd-channels", 4); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - return NULL; -} - -static void test_multifd_file_mapped_ram_live(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram, - }; - - test_file_common(&args, false); -} - -static void test_multifd_file_mapped_ram(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram, - }; - - test_file_common(&args, true); -} - -static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, - QTestState *to) -{ - migrate_hook_start_multifd_mapped_ram(from, to); - - migrate_set_parameter_bool(from, "direct-io", true); - migrate_set_parameter_bool(to, "direct-io", true); - - return NULL; -} - -static void test_multifd_file_mapped_ram_dio(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_dio, - }; - - if (!probe_o_direct_support(tmpfs)) { - g_test_skip("Filesystem does not support O_DIRECT"); - return; - } - - test_file_common(&args, true); -} - -#ifndef _WIN32 -static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, - QTestState *to, - void *opaque) -{ - QDict *resp; - QList *fdsets; - - /* - * Remove the fdsets after migration, otherwise a second migration - * would fail due fdset reuse. - */ - qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " - "'arguments': { 'fdset-id': 1}}"); - - /* - * Make sure no fdsets are left after migration, otherwise a - * second migration would fail due fdset reuse. - */ - resp = qtest_qmp(from, "{'execute': 'query-fdsets', " - "'arguments': {}}"); - g_assert(qdict_haskey(resp, "return")); - fdsets = qdict_get_qlist(resp, "return"); - g_assert(fdsets && qlist_empty(fdsets)); - qobject_unref(resp); -} - -static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, - QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 2, true); - fdset_add_fds(to, file, O_RDONLY, 2, true); - - migrate_hook_start_multifd_mapped_ram(from, to); - migrate_set_parameter_bool(from, "direct-io", true); - migrate_set_parameter_bool(to, "direct-io", true); - - return NULL; -} - -static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, - QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 2, false); - fdset_add_fds(to, file, O_RDONLY, 2, false); - - migrate_hook_start_multifd_mapped_ram(from, to); - - return NULL; -} - -static void test_multifd_file_mapped_ram_fdset(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, - .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, - }; - - test_file_common(&args, true); -} - -static void test_multifd_file_mapped_ram_fdset_dio(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, - .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, - }; - - if (!probe_o_direct_support(tmpfs)) { - g_test_skip("Filesystem does not support O_DIRECT"); - return; - } - - test_file_common(&args, true); -} -#endif /* !_WIN32 */ - static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -1500,6 +1221,7 @@ int main(int argc, char **argv) migration_test_add_tls(env); migration_test_add_compression(env); migration_test_add_postcopy(env); + migration_test_add_file(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 @@ -1515,17 +1237,6 @@ int main(int argc, char **argv) migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); - migration_test_add("/migration/precopy/file", - test_precopy_file); - migration_test_add("/migration/precopy/file/offset", - test_precopy_file_offset); -#ifndef _WIN32 - migration_test_add("/migration/precopy/file/offset/fdset", - test_precopy_file_offset_fdset); -#endif - migration_test_add("/migration/precopy/file/offset/bad", - test_precopy_file_offset_bad); - /* * Our CI system has problems with shared memory. * Don't run this test until we find a workaround. @@ -1534,26 +1245,6 @@ int main(int argc, char **argv) migration_test_add("/migration/mode/reboot", test_mode_reboot); } - migration_test_add("/migration/precopy/file/mapped-ram", - test_precopy_file_mapped_ram); - migration_test_add("/migration/precopy/file/mapped-ram/live", - test_precopy_file_mapped_ram_live); - - migration_test_add("/migration/multifd/file/mapped-ram", - test_multifd_file_mapped_ram); - migration_test_add("/migration/multifd/file/mapped-ram/live", - test_multifd_file_mapped_ram_live); - - migration_test_add("/migration/multifd/file/mapped-ram/dio", - test_multifd_file_mapped_ram_dio); - -#ifndef _WIN32 - migration_test_add("/migration/multifd/file/mapped-ram/fdset", - test_multifd_file_mapped_ram_fdset); - migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", - test_multifd_file_mapped_ram_fdset_dio); -#endif - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); migration_test_add("/migration/precopy/tcp/plain/switchover-ack", diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c new file mode 100644 index 0000000000..84225c8c33 --- /dev/null +++ b/tests/qtest/migration/file-tests.c @@ -0,0 +1,338 @@ +/* + * QTest testcases for migration to file + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qapi/qmp/qlist.h" + + +static char *tmpfs; + +static void test_precopy_file(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void fdset_add_fds(QTestState *qts, const char *file, int flags, + int num_fds, bool direct_io) +{ + for (int i = 0; i < num_fds; i++) { + int fd; + +#ifdef O_DIRECT + /* only secondary channels can use direct-io */ + if (direct_io && i != 0) { + flags |= O_DIRECT; + } +#endif + + fd = open(file, flags, 0660); + assert(fd != -1); + + qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " + "'arguments': {'fdset-id': 1}}"); + close(fd); + } +} + +static void *migrate_hook_start_file_offset_fdset(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 1, false); + fdset_add_fds(to, file, O_RDONLY, 1, false); + + return NULL; +} + +static void test_precopy_file_offset_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_file_offset_fdset, + }; + + test_file_common(&args, false); +} +#endif + +static void test_precopy_file_offset(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, + FILE_TEST_FILENAME, + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_offset_bad(void) +{ + /* using a value not supported by qemu_strtosz() */ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", + tmpfs, FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .result = MIG_TEST_QMP_ERROR, + }; + + test_file_common(&args, false); +} + +static void *migrate_hook_start_mapped_ram(QTestState *from, + QTestState *to) +{ + migrate_set_capability(from, "mapped-ram", true); + migrate_set_capability(to, "mapped-ram", true); + + return NULL; +} + +static void test_precopy_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mapped_ram, + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mapped_ram, + }; + + test_file_common(&args, true); +} + +static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, + QTestState *to) +{ + migrate_hook_start_mapped_ram(from, to); + + migrate_set_parameter_int(from, "multifd-channels", 4); + migrate_set_parameter_int(to, "multifd-channels", 4); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram, + }; + + test_file_common(&args, false); +} + +static void test_multifd_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram, + }; + + test_file_common(&args, true); +} + +static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, + QTestState *to) +{ + migrate_hook_start_multifd_mapped_ram(from, to); + + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_dio, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *resp; + QList *fdsets; + + /* + * Remove the fdsets after migration, otherwise a second migration + * would fail due fdset reuse. + */ + qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " + "'arguments': { 'fdset-id': 1}}"); + + /* + * Make sure no fdsets are left after migration, otherwise a + * second migration would fail due fdset reuse. + */ + resp = qtest_qmp(from, "{'execute': 'query-fdsets', " + "'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + fdsets = qdict_get_qlist(resp, "return"); + g_assert(fdsets && qlist_empty(fdsets)); + qobject_unref(resp); +} + +static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, true); + fdset_add_fds(to, file, O_RDONLY, 2, true); + + migrate_hook_start_multifd_mapped_ram(from, to); + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, false); + fdset_add_fds(to, file, O_RDONLY, 2, false); + + migrate_hook_start_multifd_mapped_ram(from, to); + + return NULL; +} + +static void test_multifd_file_mapped_ram_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + }; + + test_file_common(&args, true); +} + +static void test_multifd_file_mapped_ram_fdset_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} +#endif /* !_WIN32 */ + +void migration_test_add_file(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add("/migration/precopy/file", + test_precopy_file); + + migration_test_add("/migration/precopy/file/offset", + test_precopy_file_offset); +#ifndef _WIN32 + migration_test_add("/migration/precopy/file/offset/fdset", + test_precopy_file_offset_fdset); +#endif + migration_test_add("/migration/precopy/file/offset/bad", + test_precopy_file_offset_bad); + + migration_test_add("/migration/precopy/file/mapped-ram", + test_precopy_file_mapped_ram); + migration_test_add("/migration/precopy/file/mapped-ram/live", + test_precopy_file_mapped_ram_live); + + migration_test_add("/migration/multifd/file/mapped-ram", + test_multifd_file_mapped_ram); + migration_test_add("/migration/multifd/file/mapped-ram/live", + test_multifd_file_mapped_ram_live); + + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); + +#ifndef _WIN32 + migration_test_add("/migration/multifd/file/mapped-ram/fdset", + test_multifd_file_mapped_ram_fdset); + migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", + test_multifd_file_mapped_ram_fdset_dio); +#endif +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 1ac3aea4b2..6be434c6bf 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -222,5 +222,6 @@ static inline void migration_test_add_tls(MigrationTestEnv *env) {}; #endif void migration_test_add_compression(MigrationTestEnv *env); void migration_test_add_postcopy(MigrationTestEnv *env); +void migration_test_add_file(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ From 8a645544565c14cf1ac1b57ad2ef4f77a9446b7d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:28:59 -0300 Subject: [PATCH 0192/2892] tests/qtest/migration: Split precopy tests Split the precopy tests from migration-test.c. This is the largest group of tests and the more difficult one to break into smaller groups, so move all of it. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 970 +----------------------- tests/qtest/migration/framework.h | 1 + tests/qtest/migration/precopy-tests.c | 1007 +++++++++++++++++++++++++ 4 files changed, 1011 insertions(+), 968 deletions(-) create mode 100644 tests/qtest/migration/precopy-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ede5a9fb61..fc38c80b43 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -339,6 +339,7 @@ migration_files = [files( 'migration/migration-util.c', 'migration/compression-tests.c', 'migration/file-tests.c', + 'migration/precopy-tests.c', 'migration/postcopy-tests.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index a2ce1c6b19..76ba820fe4 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -27,12 +27,6 @@ #include "migration/migration-qmp.h" #include "migration/migration-util.h" -/* - * Dirtylimit stop working if dirty page rate error - * value less than DIRTYLIMIT_TOLERANCE_RANGE - */ -#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ - #define ANALYZE_SCRIPT "scripts/analyze-migration.py" #if defined(__linux__) @@ -113,70 +107,6 @@ static void test_analyze_script(void) } #endif -static void test_precopy_unix_plain(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * The simplest use case of precopy, covering smoke tests of - * get-dirty-log dirty tracking. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_suspend_live(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * despite being live, the test is fast because the src - * suspends immediately. - */ - .live = true, - .start.suspend_me = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_suspend_notlive(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - .start.suspend_me = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_dirty_ring(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - /* - * Besides the precopy/unix basic test, cover dirty ring interface - * rather than get-dirty-log. - */ - .live = true, - }; - - test_precopy_common(&args); -} - #if 0 /* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) @@ -240,149 +170,6 @@ static void test_mode_reboot(void) test_file_common(&args, true); } -static void test_precopy_tcp_plain(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - }; - - test_precopy_common(&args); -} - -static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) -{ - - migrate_set_capability(from, "return-path", true); - migrate_set_capability(to, "return-path", true); - - migrate_set_capability(from, "switchover-ack", true); - migrate_set_capability(to, "switchover-ack", true); - - return NULL; -} - -static void test_precopy_tcp_switchover_ack(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_switchover_ack, - /* - * Source VM must be running in order to consider the switchover ACK - * when deciding to do switchover or not. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -#ifndef _WIN32 -static void *migrate_hook_start_fd(QTestState *from, - QTestState *to) -{ - int ret; - int pair[2]; - - /* Create two connected sockets for migration */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); - g_assert_cmpint(ret, ==, 0); - - /* Send the 1st socket to the target */ - qtest_qmp_fds_assert_success(to, &pair[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[0]); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "fd:fd-mig", "{}"); - - /* Send the 2nd socket to the target */ - qtest_qmp_fds_assert_success(from, &pair[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[1]); - - return NULL; -} - -static void migrate_hook_end_fd(QTestState *from, - QTestState *to, - void *opaque) -{ - QDict *rsp; - const char *error_desc; - - /* Test closing fds */ - /* We assume, that QEMU removes named fd from its list, - * so this should fail */ - rsp = qtest_qmp(from, - "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); - - rsp = qtest_qmp(to, - "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); -} - -static void test_precopy_fd_socket(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_hook_start_fd, - .end_hook = migrate_hook_end_fd, - }; - test_precopy_common(&args); -} - -static void *migrate_hook_start_precopy_fd_file(QTestState *from, QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - int src_flags = O_CREAT | O_RDWR; - int dst_flags = O_CREAT | O_RDWR; - int fds[2]; - - fds[0] = open(file, src_flags, 0660); - assert(fds[0] != -1); - - fds[1] = open(file, dst_flags, 0660); - assert(fds[1] != -1); - - - qtest_qmp_fds_assert_success(to, &fds[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - qtest_qmp_fds_assert_success(from, &fds[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - close(fds[0]); - close(fds[1]); - - return NULL; -} - -static void test_precopy_fd_file(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_hook_start_precopy_fd_file, - .end_hook = migrate_hook_end_fd, - }; - test_file_common(&args, true); -} -#endif /* _WIN32 */ - static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -505,708 +292,6 @@ static void test_validate_uri_channels_none_set(void) do_test_validate_uri_channel(&args); } -/* - * The way auto_converge works, we need to do too many passes to - * run this test. Auto_converge logic is only run once every - * three iterations, so: - * - * - 3 iterations without auto_converge enabled - * - 3 iterations with pct = 5 - * - 3 iterations with pct = 30 - * - 3 iterations with pct = 55 - * - 3 iterations with pct = 80 - * - 3 iterations with pct = 95 (max(95, 80 + 25)) - * - * To make things even worse, we need to run the initial stage at - * 3MB/s so we enter autoconverge even when host is (over)loaded. - */ -static void test_auto_converge(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart args = {}; - QTestState *from, *to; - int64_t percentage; - - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwidth migration may pass without throttling, - * so we need to decrease a bandwidth. - */ - const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; - uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; - int max_try_count, hit = 0; - - if (migrate_start(&from, &to, uri, &args)) { - return; - } - - migrate_set_capability(from, "auto-converge", true); - migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); - migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); - migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); - - /* - * Set the initial parameters so that the migration could not converge - * without throttling. - */ - migrate_ensure_non_converge(from); - - /* To check remaining size after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - /* Wait for throttling begins */ - percentage = 0; - do { - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - if (percentage != 0) { - break; - } - usleep(20); - g_assert_false(get_src()->stop_seen); - } while (true); - /* The first percentage of throttling should be at least init_pct */ - g_assert_cmpint(percentage, >=, init_pct); - - /* - * End the loop when the dirty sync count greater than 1. - */ - while ((dirty_sync_cnt = get_migration_pass(from)) < 2) { - usleep(1000 * 1000); - } - - prev_dirty_sync_cnt = dirty_sync_cnt; - - /* - * The RAMBlock dirty sync count must changes in 5 seconds, here we set - * the timeout to 10 seconds to ensure it changes. - * - * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s, - * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3) - * to complete; this ensures that the RAMBlock dirty sync occurs. - */ - max_try_count = 10; - while (--max_try_count) { - dirty_sync_cnt = get_migration_pass(from); - if (dirty_sync_cnt != prev_dirty_sync_cnt) { - hit = 1; - break; - } - prev_dirty_sync_cnt = dirty_sync_cnt; - sleep(1); - } - g_assert_cmpint(hit, ==, 1); - - /* Now, when we tested that throttling works, let it converge */ - migrate_ensure_converge(from); - - /* - * Wait for pre-switchover status to check last throttle percentage - * and remaining. These values will be zeroed later - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - /* The final percentage of throttling shouldn't be greater than max_pct */ - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - g_assert_cmpint(percentage, <=, max_pct); - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - migrate_end(from, to, true); -} - -static void * -migrate_hook_start_precopy_tcp_multifd(QTestState *from, - QTestState *to) -{ - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); -} - -static void * -migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - migrate_set_parameter_str(from, "zero-page-detection", "legacy"); - return NULL; -} - -static void * -migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, - QTestState *to) -{ - migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); - migrate_set_parameter_str(from, "zero-page-detection", "none"); - return NULL; -} - -static void test_multifd_tcp_uri_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_zero_page_legacy(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_no_zero_page(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_channels_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd, - .live = true, - .connect_channels = ("[ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]"), - }; - test_precopy_common(&args); -} - -/* - * This test does: - * source target - * migrate_incoming - * migrate - * migrate_cancel - * launch another target - * migrate - * - * And see that it works - */ -static void test_multifd_tcp_cancel(void) -{ - MigrateStart args = { - .hide_stderr = true, - }; - QTestState *from, *to, *to2; - - if (migrate_start(&from, &to, "defer", &args)) { - return; - } - - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - migrate_cancel(from); - - /* Make sure QEMU process "to" exited */ - qtest_set_expected_status(to, EXIT_FAILURE); - qtest_wait_qemu(to); - qtest_quit(to); - - /* - * Ensure the source QEMU finishes its cancellation process before we - * proceed with the setup of the next migration. The migrate_start() - * function and others might want to interact with the source in a way that - * is not possible while the migration is not canceled properly. For - * example, setting migration capabilities when the migration is still - * running leads to an error. - */ - wait_for_migration_status(from, "cancelled", NULL); - - args = (MigrateStart){ - .only_target = true, - }; - - if (migrate_start(&from, &to2, "defer", &args)) { - return; - } - - migrate_set_parameter_int(to2, "multifd-channels", 16); - - migrate_set_capability(to2, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); - - migrate_ensure_non_converge(from); - - migrate_qmp(from, to2, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to2); - - migrate_ensure_converge(from); - - wait_for_stop(from, get_src()); - qtest_qmp_eventwait(to2, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - migrate_end(from, to2, true); -} - -static void calc_dirty_rate(QTestState *who, uint64_t calc_time) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'calc-dirty-rate'," - "'arguments': { " - "'calc-time': %" PRIu64 "," - "'mode': 'dirty-ring' }}", - calc_time); -} - -static QDict *query_dirty_rate(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, - "{ 'execute': 'query-dirty-rate' }"); -} - -static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'set-vcpu-dirty-limit'," - "'arguments': { " - "'dirty-rate': %" PRIu64 " } }", - dirtyrate); -} - -static void cancel_vcpu_dirty_limit(QTestState *who) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'cancel-vcpu-dirty-limit' }"); -} - -static QDict *query_vcpu_dirty_limit(QTestState *who) -{ - QDict *rsp; - - rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); - g_assert(!qdict_haskey(rsp, "error")); - g_assert(qdict_haskey(rsp, "return")); - - return rsp; -} - -static bool calc_dirtyrate_ready(QTestState *who) -{ - QDict *rsp_return; - const char *status; - bool ready; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = qdict_get_str(rsp_return, "status"); - g_assert(status); - ready = g_strcmp0(status, "measuring"); - qobject_unref(rsp_return); - - return ready; -} - -static void wait_for_calc_dirtyrate_complete(QTestState *who, - int64_t time_s) -{ - int max_try_count = 10000; - usleep(time_s * 1000000); - - while (!calc_dirtyrate_ready(who) && max_try_count--) { - usleep(1000); - } - - /* - * Set the timeout with 10 s(max_try_count * 1000us), - * if dirtyrate measurement not complete, fail test. - */ - g_assert_cmpint(max_try_count, !=, 0); -} - -static int64_t get_dirty_rate(QTestState *who) -{ - QDict *rsp_return; - const char *status; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = qdict_get_str(rsp_return, "status"); - g_assert(status); - g_assert_cmpstr(status, ==, "measured"); - - rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static int64_t get_limit_rate(QTestState *who) -{ - QDict *rsp_return; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_vcpu_dirty_limit(who); - g_assert(rsp_return); - - rates = qdict_get_qlist(rsp_return, "return"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static QTestState *dirtylimit_start_vm(void) -{ - QTestState *vm = NULL; - g_autofree gchar *cmd = NULL; - const char *bootpath; - - bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); - cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " - "-name dirtylimit-test,debug-threads=on " - "-m 150M -smp 1 " - "-serial file:%s/vm_serial " - "-drive file=%s,format=raw ", - tmpfs, bootpath); - - vm = qtest_init(cmd); - return vm; -} - -static void dirtylimit_stop_vm(QTestState *vm) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); - - qtest_quit(vm); - unlink(path); -} - -static void test_vcpu_dirty_limit(void) -{ - QTestState *vm; - int64_t origin_rate; - int64_t quota_rate; - int64_t rate ; - int max_try_count = 20; - int hit = 0; - - /* Start vm for vcpu dirtylimit test */ - vm = dirtylimit_start_vm(); - - /* Wait for the first serial output from the vm*/ - wait_for_serial("vm_serial"); - - /* Do dirtyrate measurement with calc time equals 1s */ - calc_dirty_rate(vm, 1); - - /* Sleep calc time and wait for calc dirtyrate complete */ - wait_for_calc_dirtyrate_complete(vm, 1); - - /* Query original dirty page rate */ - origin_rate = get_dirty_rate(vm); - - /* VM booted from bootsect should dirty memory steadily */ - assert(origin_rate != 0); - - /* Setup quota dirty page rate at half of origin */ - quota_rate = origin_rate / 2; - - /* Set dirtylimit */ - dirtylimit_set_all(vm, quota_rate); - - /* - * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit - * works literally - */ - g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); - - /* Sleep a bit to check if it take effect */ - usleep(2000000); - - /* - * Check if dirtylimit take effect realistically, set the - * timeout with 20 s(max_try_count * 1s), if dirtylimit - * doesn't take effect, fail test. - */ - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume hitting if current rate is less - * than quota rate (within accepting error) - */ - if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; - } - } - - g_assert_cmpint(hit, ==, 1); - - hit = 0; - max_try_count = 20; - - /* Check if dirtylimit cancellation take effect */ - cancel_vcpu_dirty_limit(vm); - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume dirtylimit be canceled if current rate is - * greater than quota rate (within accepting error) - */ - if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; - } - } - - g_assert_cmpint(hit, ==, 1); - dirtylimit_stop_vm(vm); -} - -static void migrate_dirty_limit_wait_showup(QTestState *from, - const int64_t period, - const int64_t value) -{ - /* Enable dirty limit capability */ - migrate_set_capability(from, "dirty-limit", true); - - /* Set dirty limit parameters */ - migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); - migrate_set_parameter_int(from, "vcpu-dirty-limit", value); - - /* Make sure migrate can't converge */ - migrate_ensure_non_converge(from); - - /* To check limit rate after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the serial output from the source */ - wait_for_serial("src_serial"); -} - -/* - * This test does: - * source destination - * start vm - * start incoming vm - * migrate - * wait dirty limit to begin - * cancel migrate - * cancellation check - * restart incoming vm - * migrate - * wait dirty limit to begin - * wait pre-switchover event - * convergence condition check - * - * And see if dirty limit migration works correctly. - * This test case involves many passes, so it runs in slow mode only. - */ -static void test_dirty_limit(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - int64_t remaining; - uint64_t throttle_us_per_full; - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, - * so we need to decrease a bandwidth. - */ - const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; - const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ - const int64_t downtime_limit = 250; /* 250ms */ - /* - * We migrate through unix-socket (> 500Mb/s). - * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). - * So, we can predict expected_threshold - */ - const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; - int max_try_count = 10; - MigrateCommon args = { - .start = { - .hide_stderr = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; - - /* Start src, dst vm */ - if (migrate_start(&from, &to, args.listen_uri, &args.start)) { - return; - } - - /* Prepare for dirty limit migration and wait src vm show up */ - migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); - - /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); - - /* Wait for dirty limit throttle begin */ - throttle_us_per_full = 0; - while (throttle_us_per_full == 0) { - throttle_us_per_full = - read_migrate_property_int(from, - "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(get_src()->stop_seen); - } - - /* Now cancel migrate and wait for dirty limit throttle switch off */ - migrate_cancel(from); - wait_for_migration_status(from, "cancelled", NULL); - - /* Check if dirty limit throttle switched off, set timeout 1ms */ - do { - throttle_us_per_full = - read_migrate_property_int(from, - "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(get_src()->stop_seen); - } while (throttle_us_per_full != 0 && --max_try_count); - - /* Assert dirty limit is not in service */ - g_assert_cmpint(throttle_us_per_full, ==, 0); - - args = (MigrateCommon) { - .start = { - .only_target = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; - - /* Restart dst vm, src vm already show up so we needn't wait anymore */ - if (migrate_start(&from, &to, args.listen_uri, &args.start)) { - return; - } - - /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); - - /* Wait for dirty limit throttle begin */ - throttle_us_per_full = 0; - while (throttle_us_per_full == 0) { - throttle_us_per_full = - read_migrate_property_int(from, - "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(get_src()->stop_seen); - } - - /* - * The dirty limit rate should equals the return value of - * query-vcpu-dirty-limit if dirty limit cap set - */ - g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); - - /* Now, we have tested if dirty limit works, let it converge */ - migrate_set_parameter_int(from, "downtime-limit", downtime_limit); - migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); - - /* - * Wait for pre-switchover status to check if migration - * satisfy the convergence condition - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - remaining = read_ram_property_int(from, "remaining"); - g_assert_cmpint(remaining, <, - (expected_threshold + expected_threshold / 100)); - - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - migrate_end(from, to, true); -} - int main(int argc, char **argv) { MigrationTestEnv *env; @@ -1222,21 +307,13 @@ int main(int argc, char **argv) migration_test_add_compression(env); migration_test_add_postcopy(env); migration_test_add_file(env); + migration_test_add_precopy(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); #endif - if (env->is_x86) { - migration_test_add("/migration/precopy/unix/suspend/live", - test_precopy_unix_suspend_live); - migration_test_add("/migration/precopy/unix/suspend/notlive", - test_precopy_unix_suspend_notlive); - } - - migration_test_add("/migration/precopy/unix/plain", - test_precopy_unix_plain); /* * Our CI system has problems with shared memory. * Don't run this test until we find a workaround. @@ -1245,18 +322,8 @@ int main(int argc, char **argv) migration_test_add("/migration/mode/reboot", test_mode_reboot); } - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); - - migration_test_add("/migration/precopy/tcp/plain/switchover-ack", - test_precopy_tcp_switchover_ack); - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ -#ifndef _WIN32 - migration_test_add("/migration/precopy/fd/tcp", - test_precopy_fd_socket); - migration_test_add("/migration/precopy/fd/file", - test_precopy_fd_file); -#endif + migration_test_add("/migration/validate_uuid", test_validate_uuid); migration_test_add("/migration/validate_uuid_error", test_validate_uuid_error); @@ -1268,39 +335,6 @@ int main(int argc, char **argv) test_validate_uri_channels_both_set); migration_test_add("/migration/validate_uri/channels/none_set", test_validate_uri_channels_none_set); - /* - * See explanation why this test is slow on function definition - */ - if (g_test_slow()) { - migration_test_add("/migration/auto_converge", - test_auto_converge); - if (g_str_equal(env->arch, "x86_64") && - env->has_kvm && env->has_dirty_ring) { - migration_test_add("/dirty_limit", - test_dirty_limit); - } - } - migration_test_add("/migration/multifd/tcp/uri/plain/none", - test_multifd_tcp_uri_none); - migration_test_add("/migration/multifd/tcp/channels/plain/none", - test_multifd_tcp_channels_none); - migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", - test_multifd_tcp_zero_page_legacy); - migration_test_add("/migration/multifd/tcp/plain/zero-page/none", - test_multifd_tcp_no_zero_page); - migration_test_add("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); - - if (g_str_equal(env->arch, "x86_64") && - env->has_kvm && env->has_dirty_ring) { - - migration_test_add("/migration/dirty_ring", - test_precopy_unix_dirty_ring); - if (qtest_has_machine("pc") && g_test_slow()) { - migration_test_add("/migration/vcpu_dirty_limit", - test_vcpu_dirty_limit); - } - } ret = g_test_run(); diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 6be434c6bf..52c715781f 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -223,5 +223,6 @@ static inline void migration_test_add_tls(MigrationTestEnv *env) {}; void migration_test_add_compression(MigrationTestEnv *env); void migration_test_add_postcopy(MigrationTestEnv *env); void migration_test_add_file(MigrationTestEnv *env); +void migration_test_add_precopy(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c new file mode 100644 index 0000000000..b709d9051d --- /dev/null +++ b/tests/qtest/migration/precopy-tests.c @@ -0,0 +1,1007 @@ +/* + * QTest testcase for precopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qapi/qmp/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ + +static char *tmpfs; + +static void test_precopy_unix_plain(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * The simplest use case of precopy, covering smoke tests of + * get-dirty-log dirty tracking. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_live(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * despite being live, the test is fast because the src + * suspends immediately. + */ + .live = true, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_notlive(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_dirty_ring(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + /* + * Besides the precopy/unix basic test, cover dirty ring interface + * rather than get-dirty-log. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_plain(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + }; + + test_precopy_common(&args); +} + +static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) +{ + + migrate_set_capability(from, "return-path", true); + migrate_set_capability(to, "return-path", true); + + migrate_set_capability(from, "switchover-ack", true); + migrate_set_capability(to, "switchover-ack", true); + + return NULL; +} + +static void test_precopy_tcp_switchover_ack(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_switchover_ack, + /* + * Source VM must be running in order to consider the switchover ACK + * when deciding to do switchover or not. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +#ifndef _WIN32 +static void *migrate_hook_start_fd(QTestState *from, + QTestState *to) +{ + int ret; + int pair[2]; + + /* Create two connected sockets for migration */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + g_assert_cmpint(ret, ==, 0); + + /* Send the 1st socket to the target */ + qtest_qmp_fds_assert_success(to, &pair[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[0]); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "fd:fd-mig", "{}"); + + /* Send the 2nd socket to the target */ + qtest_qmp_fds_assert_success(from, &pair[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[1]); + + return NULL; +} + +static void migrate_hook_end_fd(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *rsp; + const char *error_desc; + + /* Test closing fds */ + /* + * We assume, that QEMU removes named fd from its list, + * so this should fail. + */ + rsp = qtest_qmp(from, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); + + rsp = qtest_qmp(to, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); +} + +static void test_precopy_fd_socket(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_fd, + .end_hook = migrate_hook_end_fd, + }; + test_precopy_common(&args); +} + +static void *migrate_hook_start_precopy_fd_file(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + int src_flags = O_CREAT | O_RDWR; + int dst_flags = O_CREAT | O_RDWR; + int fds[2]; + + fds[0] = open(file, src_flags, 0660); + assert(fds[0] != -1); + + fds[1] = open(file, dst_flags, 0660); + assert(fds[1] != -1); + + + qtest_qmp_fds_assert_success(to, &fds[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + qtest_qmp_fds_assert_success(from, &fds[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + close(fds[0]); + close(fds[1]); + + return NULL; +} + +static void test_precopy_fd_file(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_precopy_fd_file, + .end_hook = migrate_hook_end_fd, + }; + test_file_common(&args, true); +} +#endif /* _WIN32 */ + +/* + * The way auto_converge works, we need to do too many passes to + * run this test. Auto_converge logic is only run once every + * three iterations, so: + * + * - 3 iterations without auto_converge enabled + * - 3 iterations with pct = 5 + * - 3 iterations with pct = 30 + * - 3 iterations with pct = 55 + * - 3 iterations with pct = 80 + * - 3 iterations with pct = 95 (max(95, 80 + 25)) + * + * To make things even worse, we need to run the initial stage at + * 3MB/s so we enter autoconverge even when host is (over)loaded. + */ +static void test_auto_converge(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart args = {}; + QTestState *from, *to; + int64_t percentage; + + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without throttling, + * so we need to decrease a bandwidth. + */ + const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; + uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; + int max_try_count, hit = 0; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_set_capability(from, "auto-converge", true); + migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); + migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); + migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); + + /* + * Set the initial parameters so that the migration could not converge + * without throttling. + */ + migrate_ensure_non_converge(from); + + /* To check remaining size after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* Wait for throttling begins */ + percentage = 0; + do { + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + if (percentage != 0) { + break; + } + usleep(20); + g_assert_false(get_src()->stop_seen); + } while (true); + /* The first percentage of throttling should be at least init_pct */ + g_assert_cmpint(percentage, >=, init_pct); + + /* + * End the loop when the dirty sync count greater than 1. + */ + while ((dirty_sync_cnt = get_migration_pass(from)) < 2) { + usleep(1000 * 1000); + } + + prev_dirty_sync_cnt = dirty_sync_cnt; + + /* + * The RAMBlock dirty sync count must changes in 5 seconds, here we set + * the timeout to 10 seconds to ensure it changes. + * + * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s, + * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3) + * to complete; this ensures that the RAMBlock dirty sync occurs. + */ + max_try_count = 10; + while (--max_try_count) { + dirty_sync_cnt = get_migration_pass(from); + if (dirty_sync_cnt != prev_dirty_sync_cnt) { + hit = 1; + break; + } + prev_dirty_sync_cnt = dirty_sync_cnt; + sleep(1); + } + g_assert_cmpint(hit, ==, 1); + + /* Now, when we tested that throttling works, let it converge */ + migrate_ensure_converge(from); + + /* + * Wait for pre-switchover status to check last throttle percentage + * and remaining. These values will be zeroed later + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + /* The final percentage of throttling shouldn't be greater than max_pct */ + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + g_assert_cmpint(percentage, <=, max_pct); + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +static void * +migrate_hook_start_precopy_tcp_multifd(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "legacy"); + return NULL; +} + +static void * +migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "none"); + return NULL; +} + +static void test_multifd_tcp_uri_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_zero_page_legacy(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_no_zero_page(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_channels_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + .live = true, + .connect_channels = ("[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + test_precopy_common(&args); +} + +/* + * This test does: + * source target + * migrate_incoming + * migrate + * migrate_cancel + * launch another target + * migrate + * + * And see that it works + */ +static void test_multifd_tcp_cancel(void) +{ + MigrateStart args = { + .hide_stderr = true, + }; + QTestState *from, *to, *to2; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + migrate_cancel(from); + + /* Make sure QEMU process "to" exited */ + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_wait_qemu(to); + qtest_quit(to); + + /* + * Ensure the source QEMU finishes its cancellation process before we + * proceed with the setup of the next migration. The migrate_start() + * function and others might want to interact with the source in a way that + * is not possible while the migration is not canceled properly. For + * example, setting migration capabilities when the migration is still + * running leads to an error. + */ + wait_for_migration_status(from, "cancelled", NULL); + + args = (MigrateStart){ + .only_target = true, + }; + + if (migrate_start(&from, &to2, "defer", &args)) { + return; + } + + migrate_set_parameter_int(to2, "multifd-channels", 16); + + migrate_set_capability(to2, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); + + migrate_ensure_non_converge(from); + + migrate_qmp(from, to2, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to2); + + migrate_ensure_converge(from); + + wait_for_stop(from, get_src()); + qtest_qmp_eventwait(to2, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + migrate_end(from, to2, true); +} + +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %" PRIu64 "," + "'mode': 'dirty-ring' }}", + calc_time); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); +} + +static QDict *query_vcpu_dirty_limit(QTestState *who) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); + g_assert(!qdict_haskey(rsp, "error")); + g_assert(qdict_haskey(rsp, "return")); + + return rsp; +} + +static bool calc_dirtyrate_ready(QTestState *who) +{ + QDict *rsp_return; + const char *status; + bool ready; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + ready = g_strcmp0(status, "measuring"); + qobject_unref(rsp_return); + + return ready; +} + +static void wait_for_calc_dirtyrate_complete(QTestState *who, + int64_t time_s) +{ + int max_try_count = 10000; + usleep(time_s * 1000000); + + while (!calc_dirtyrate_ready(who) && max_try_count--) { + usleep(1000); + } + + /* + * Set the timeout with 10 s(max_try_count * 1000us), + * if dirtyrate measurement not complete, fail test. + */ + g_assert_cmpint(max_try_count, !=, 0); +} + +static int64_t get_dirty_rate(QTestState *who) +{ + QDict *rsp_return; + const char *status; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + g_assert_cmpstr(status, ==, "measured"); + + rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static int64_t get_limit_rate(QTestState *who) +{ + QDict *rsp_return; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_vcpu_dirty_limit(who); + g_assert(rsp_return); + + rates = qdict_get_qlist(rsp_return, "return"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static QTestState *dirtylimit_start_vm(void) +{ + QTestState *vm = NULL; + g_autofree gchar *cmd = NULL; + const char *bootpath; + + bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); + cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " + "-name dirtylimit-test,debug-threads=on " + "-m 150M -smp 1 " + "-serial file:%s/vm_serial " + "-drive file=%s,format=raw ", + tmpfs, bootpath); + + vm = qtest_init(cmd); + return vm; +} + +static void dirtylimit_stop_vm(QTestState *vm) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); + + qtest_quit(vm); + unlink(path); +} + +static void test_vcpu_dirty_limit(void) +{ + QTestState *vm; + int64_t origin_rate; + int64_t quota_rate; + int64_t rate ; + int max_try_count = 20; + int hit = 0; + + /* Start vm for vcpu dirtylimit test */ + vm = dirtylimit_start_vm(); + + /* Wait for the first serial output from the vm*/ + wait_for_serial("vm_serial"); + + /* Do dirtyrate measurement with calc time equals 1s */ + calc_dirty_rate(vm, 1); + + /* Sleep calc time and wait for calc dirtyrate complete */ + wait_for_calc_dirtyrate_complete(vm, 1); + + /* Query original dirty page rate */ + origin_rate = get_dirty_rate(vm); + + /* VM booted from bootsect should dirty memory steadily */ + assert(origin_rate != 0); + + /* Setup quota dirty page rate at half of origin */ + quota_rate = origin_rate / 2; + + /* Set dirtylimit */ + dirtylimit_set_all(vm, quota_rate); + + /* + * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit + * works literally + */ + g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); + + /* Sleep a bit to check if it take effect */ + usleep(2000000); + + /* + * Check if dirtylimit take effect realistically, set the + * timeout with 20 s(max_try_count * 1s), if dirtylimit + * doesn't take effect, fail test. + */ + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume hitting if current rate is less + * than quota rate (within accepting error) + */ + if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + + hit = 0; + max_try_count = 20; + + /* Check if dirtylimit cancellation take effect */ + cancel_vcpu_dirty_limit(vm); + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume dirtylimit be canceled if current rate is + * greater than quota rate (within accepting error) + */ + if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + dirtylimit_stop_vm(vm); +} + +static void migrate_dirty_limit_wait_showup(QTestState *from, + const int64_t period, + const int64_t value) +{ + /* Enable dirty limit capability */ + migrate_set_capability(from, "dirty-limit", true); + + /* Set dirty limit parameters */ + migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); + migrate_set_parameter_int(from, "vcpu-dirty-limit", value); + + /* Make sure migrate can't converge */ + migrate_ensure_non_converge(from); + + /* To check limit rate after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the serial output from the source */ + wait_for_serial("src_serial"); +} + +/* + * This test does: + * source destination + * start vm + * start incoming vm + * migrate + * wait dirty limit to begin + * cancel migrate + * cancellation check + * restart incoming vm + * migrate + * wait dirty limit to begin + * wait pre-switchover event + * convergence condition check + * + * And see if dirty limit migration works correctly. + * This test case involves many passes, so it runs in slow mode only. + */ +static void test_dirty_limit(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + int64_t remaining; + uint64_t throttle_us_per_full; + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, + * so we need to decrease a bandwidth. + */ + const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; + const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ + const int64_t downtime_limit = 250; /* 250ms */ + /* + * We migrate through unix-socket (> 500Mb/s). + * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). + * So, we can predict expected_threshold + */ + const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + int max_try_count = 10; + MigrateCommon args = { + .start = { + .hide_stderr = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Start src, dst vm */ + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Prepare for dirty limit migration and wait src vm show up */ + migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); + + /* Start migrate */ + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } + + /* Now cancel migrate and wait for dirty limit throttle switch off */ + migrate_cancel(from); + wait_for_migration_status(from, "cancelled", NULL); + + /* Check if dirty limit throttle switched off, set timeout 1ms */ + do { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } while (throttle_us_per_full != 0 && --max_try_count); + + /* Assert dirty limit is not in service */ + g_assert_cmpint(throttle_us_per_full, ==, 0); + + args = (MigrateCommon) { + .start = { + .only_target = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Restart dst vm, src vm already show up so we needn't wait anymore */ + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Start migrate */ + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } + + /* + * The dirty limit rate should equals the return value of + * query-vcpu-dirty-limit if dirty limit cap set + */ + g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); + + /* Now, we have tested if dirty limit works, let it converge */ + migrate_set_parameter_int(from, "downtime-limit", downtime_limit); + migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + + /* + * Wait for pre-switchover status to check if migration + * satisfy the convergence condition + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + remaining = read_ram_property_int(from, "remaining"); + g_assert_cmpint(remaining, <, + (expected_threshold + expected_threshold / 100)); + + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +void migration_test_add_precopy(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + if (env->is_x86) { + migration_test_add("/migration/precopy/unix/suspend/live", + test_precopy_unix_suspend_live); + migration_test_add("/migration/precopy/unix/suspend/notlive", + test_precopy_unix_suspend_notlive); + } + + migration_test_add("/migration/precopy/unix/plain", + test_precopy_unix_plain); + + migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + + migration_test_add("/migration/precopy/tcp/plain/switchover-ack", + test_precopy_tcp_switchover_ack); + +#ifndef _WIN32 + migration_test_add("/migration/precopy/fd/tcp", + test_precopy_fd_socket); + migration_test_add("/migration/precopy/fd/file", + test_precopy_fd_file); +#endif + + /* + * See explanation why this test is slow on function definition + */ + if (g_test_slow()) { + migration_test_add("/migration/auto_converge", + test_auto_converge); + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/dirty_limit", + test_dirty_limit); + } + } + migration_test_add("/migration/multifd/tcp/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/channels/plain/none", + test_multifd_tcp_channels_none); + migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", + test_multifd_tcp_zero_page_legacy); + migration_test_add("/migration/multifd/tcp/plain/zero-page/none", + test_multifd_tcp_no_zero_page); + migration_test_add("/migration/multifd/tcp/plain/cancel", + test_multifd_tcp_cancel); + if (g_str_equal(env->arch, "x86_64") + && env->has_kvm && env->has_dirty_ring) { + + migration_test_add("/migration/dirty_ring", + test_precopy_unix_dirty_ring); + if (qtest_has_machine("pc") && g_test_slow()) { + migration_test_add("/migration/vcpu_dirty_limit", + test_vcpu_dirty_limit); + } + } +} From 44bd87a500dabf65d21b704c9ce61a44e8ff60eb Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:29:00 -0300 Subject: [PATCH 0193/2892] tests/qtest/migration: Split CPR tests Move the mode/reboot test into a separate file to hold all the CPR tests. Currently there's just one test, but we're adding more CPR modes and the feature is different enough from live migration that it's worth it to have a separate file for it. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 34 +----------------- tests/qtest/migration/cpr-tests.c | 58 +++++++++++++++++++++++++++++++ tests/qtest/migration/framework.h | 1 + 4 files changed, 61 insertions(+), 33 deletions(-) create mode 100644 tests/qtest/migration/cpr-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index fc38c80b43..cda1a84122 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -338,6 +338,7 @@ migration_files = [files( 'migration/migration-qmp.c', 'migration/migration-util.c', 'migration/compression-tests.c', + 'migration/cpr-tests.c', 'migration/file-tests.c', 'migration/precopy-tests.c', 'migration/postcopy-tests.c', diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 76ba820fe4..0d1c8154d7 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -145,31 +145,6 @@ static void test_ignore_shared(void) } #endif -static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) -{ - migrate_set_parameter_str(from, "mode", "cpr-reboot"); - migrate_set_parameter_str(to, "mode", "cpr-reboot"); - - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - return NULL; -} - -static void test_mode_reboot(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .start.use_shmem = true, - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mode_reboot, - }; - - test_file_common(&args, true); -} - static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -308,20 +283,13 @@ int main(int argc, char **argv) migration_test_add_postcopy(env); migration_test_add_file(env); migration_test_add_precopy(env); + migration_test_add_cpr(env); migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); #endif - /* - * Our CI system has problems with shared memory. - * Don't run this test until we find a workaround. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/mode/reboot", test_mode_reboot); - } - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ migration_test_add("/migration/validate_uuid", test_validate_uuid); diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c new file mode 100644 index 0000000000..44ce89aa5b --- /dev/null +++ b/tests/qtest/migration/cpr-tests.c @@ -0,0 +1,58 @@ +/* + * QTest testcases for CPR + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + + +static char *tmpfs; + +static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + return NULL; +} + +static void test_mode_reboot(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .start.use_shmem = true, + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mode_reboot, + }; + + test_file_common(&args, true); +} + +void migration_test_add_cpr(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/mode/reboot", test_mode_reboot); + } +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 52c715781f..b264be76eb 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -224,5 +224,6 @@ void migration_test_add_compression(MigrationTestEnv *env); void migration_test_add_postcopy(MigrationTestEnv *env); void migration_test_add_file(MigrationTestEnv *env); void migration_test_add_precopy(MigrationTestEnv *env); +void migration_test_add_cpr(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ From 0610e2c268fbc7939163347c41541165d116da02 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 30 May 2024 19:54:06 +1000 Subject: [PATCH 0194/2892] tests/qtest/migration-test: Fix and enable test_ignore_shared This test is already starting to bitrot, so first remove it from ifdef and fix compile issues. ppc64 transfers about 2MB, so bump the size threshold too. It was said to be broken on aarch64 but it may have been due to the limited shm size under Gitlab CI. Cc: Yury Kotov Cc: Dr. David Alan Gilbert Signed-off-by: Nicholas Piggin Reviewed-by: Dr. David Alan Gilbert [put it under flaky tests, we still don't have a solution for the CI] Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0d1c8154d7..4b366a94a2 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -107,14 +107,15 @@ static void test_analyze_script(void) } #endif -#if 0 -/* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; + MigrateStart args = { + .use_shmem = true, + }; - if (migrate_start(&from, &to, uri, false, true, NULL, NULL)) { + if (migrate_start(&from, &to, uri, &args)) { return; } @@ -139,11 +140,11 @@ static void test_ignore_shared(void) wait_for_migration_complete(from); /* Check whether shared RAM has been really skipped */ - g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); + g_assert_cmpint( + read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); migrate_end(from, to, true); } -#endif static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { @@ -290,7 +291,13 @@ int main(int argc, char **argv) migration_test_add("/migration/analyze-script", test_analyze_script); #endif - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/ignore-shared", test_ignore_shared); + } migration_test_add("/migration/validate_uuid", test_validate_uuid); migration_test_add("/migration/validate_uuid_error", From de8bc62cc4871fa97d1891ebb96e782b0c75b56a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 27 Nov 2024 15:29:01 -0300 Subject: [PATCH 0195/2892] tests/qtest/migration: Split validation tests + misc Move the remaining tests into a misc-tests.c file. These tests are mostly about validation of input and should be in the future replaced by unit testing. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 1 + tests/qtest/migration-test.c | 285 +---------------------------- tests/qtest/migration/framework.h | 1 + tests/qtest/migration/misc-tests.c | 282 ++++++++++++++++++++++++++++ 4 files changed, 286 insertions(+), 283 deletions(-) create mode 100644 tests/qtest/migration/misc-tests.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index cda1a84122..c5a70021c5 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -340,6 +340,7 @@ migration_files = [files( 'migration/compression-tests.c', 'migration/cpr-tests.c', 'migration/file-tests.c', + 'migration/misc-tests.c', 'migration/precopy-tests.c', 'migration/postcopy-tests.c', )] diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 4b366a94a2..5cad5060b3 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -11,262 +11,8 @@ */ #include "qemu/osdep.h" - -#include "libqtest.h" -#include "qapi/qmp/qlist.h" -#include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/range.h" -#include "qemu/sockets.h" -#include "chardev/char.h" -#include "crypto/tlscredspsk.h" -#include "ppc-util.h" - -#include "migration/bootfile.h" #include "migration/framework.h" -#include "migration/migration-qmp.h" -#include "migration/migration-util.h" - -#define ANALYZE_SCRIPT "scripts/analyze-migration.py" - -#if defined(__linux__) -#include -#include -#include -#endif - -static char *tmpfs; - -static void test_baddest(void) -{ - MigrateStart args = { - .hide_stderr = true - }; - QTestState *from, *to; - - if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); - wait_for_migration_fail(from, false); - migrate_end(from, to, false); -} - -#ifndef _WIN32 -static void test_analyze_script(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - }; - QTestState *from, *to; - g_autofree char *uri = NULL; - g_autofree char *file = NULL; - int pid, wstatus; - const char *python = g_getenv("PYTHON"); - - if (!python) { - g_test_skip("PYTHON variable not set"); - return; - } - - /* dummy url */ - if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - - /* - * Setting these two capabilities causes the "configuration" - * vmstate to include subsections for them. The script needs to - * parse those subsections properly. - */ - migrate_set_capability(from, "validate-uuid", true); - migrate_set_capability(from, "x-ignore-shared", true); - - file = g_strdup_printf("%s/migfile", tmpfs); - uri = g_strdup_printf("exec:cat > %s", file); - - migrate_ensure_converge(from); - migrate_qmp(from, to, uri, NULL, "{}"); - wait_for_migration_complete(from); - - pid = fork(); - if (!pid) { - close(1); - open("/dev/null", O_WRONLY); - execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); - g_assert_not_reached(); - } - - g_assert(waitpid(pid, &wstatus, 0) == pid); - if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { - g_test_message("Failed to analyze the migration stream"); - g_test_fail(); - } - migrate_end(from, to, false); - unlink(file); -} -#endif - -static void test_ignore_shared(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - MigrateStart args = { - .use_shmem = true, - }; - - if (migrate_start(&from, &to, uri, &args)) { - return; - } - - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - wait_for_stop(from, get_src()); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - /* Check whether shared RAM has been really skipped */ - g_assert_cmpint( - read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); - - migrate_end(from, to, true); -} - -static void do_test_validate_uuid(MigrateStart *args, bool should_fail) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (migrate_start(&from, &to, uri, args)) { - return; - } - - /* - * UUID validation is at the begin of migration. So, the main process of - * migration is not interesting for us here. Thus, set huge downtime for - * very fast migration. - */ - migrate_set_parameter_int(from, "downtime-limit", 1000000); - migrate_set_capability(from, "validate-uuid", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - if (should_fail) { - qtest_set_expected_status(to, EXIT_FAILURE); - wait_for_migration_fail(from, true); - } else { - wait_for_migration_complete(from); - } - - migrate_end(from, to, false); -} - -static void test_validate_uuid(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_error(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, true); -} - -static void test_validate_uuid_src_not_set(void) -{ - MigrateStart args = { - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_dst_not_set(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void do_test_validate_uri_channel(MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - /* - * 'uri' and 'channels' validation is checked even before the migration - * starts. - */ - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - migrate_end(from, to, false); -} - -static void test_validate_uri_channels_both_set(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .connect_uri = "tcp:127.0.0.1:0", - .connect_channels = ("[ { ""'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]"), - }; - - do_test_validate_uri_channel(&args); -} - -static void test_validate_uri_channels_none_set(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - }; - - do_test_validate_uri_channel(&args); -} +#include "qemu/module.h" int main(int argc, char **argv) { @@ -277,45 +23,18 @@ int main(int argc, char **argv) env = migration_get_env(); module_call_init(MODULE_INIT_QOM); - tmpfs = env->tmpfs; - migration_test_add_tls(env); migration_test_add_compression(env); migration_test_add_postcopy(env); migration_test_add_file(env); migration_test_add_precopy(env); migration_test_add_cpr(env); - - migration_test_add("/migration/bad_dest", test_baddest); -#ifndef _WIN32 - migration_test_add("/migration/analyze-script", test_analyze_script); -#endif - - /* - * Our CI system has problems with shared memory. - * Don't run this test until we find a workaround. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/ignore-shared", test_ignore_shared); - } - - migration_test_add("/migration/validate_uuid", test_validate_uuid); - migration_test_add("/migration/validate_uuid_error", - test_validate_uuid_error); - migration_test_add("/migration/validate_uuid_src_not_set", - test_validate_uuid_src_not_set); - migration_test_add("/migration/validate_uuid_dst_not_set", - test_validate_uuid_dst_not_set); - migration_test_add("/migration/validate_uri/channels/both_set", - test_validate_uri_channels_both_set); - migration_test_add("/migration/validate_uri/channels/none_set", - test_validate_uri_channels_none_set); + migration_test_add_misc(env); ret = g_test_run(); g_assert_cmpint(ret, ==, 0); - tmpfs = NULL; ret = migration_env_clean(env); return ret; diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index b264be76eb..e9fc4ec363 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -225,5 +225,6 @@ void migration_test_add_postcopy(MigrationTestEnv *env); void migration_test_add_file(MigrationTestEnv *env); void migration_test_add_precopy(MigrationTestEnv *env); void migration_test_add_cpr(MigrationTestEnv *env); +void migration_test_add_misc(MigrationTestEnv *env); #endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c new file mode 100644 index 0000000000..6173430748 --- /dev/null +++ b/tests/qtest/migration/misc-tests.c @@ -0,0 +1,282 @@ +/* + * QTest testcases for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#define ANALYZE_SCRIPT "scripts/analyze-migration.py" + +static char *tmpfs; + +static void test_baddest(void) +{ + MigrateStart args = { + .hide_stderr = true + }; + QTestState *from, *to; + + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); + wait_for_migration_fail(from, false); + migrate_end(from, to, false); +} + +#ifndef _WIN32 +static void test_analyze_script(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + }; + QTestState *from, *to; + g_autofree char *uri = NULL; + g_autofree char *file = NULL; + int pid, wstatus; + const char *python = g_getenv("PYTHON"); + + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } + + /* dummy url */ + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + + /* + * Setting these two capabilities causes the "configuration" + * vmstate to include subsections for them. The script needs to + * parse those subsections properly. + */ + migrate_set_capability(from, "validate-uuid", true); + migrate_set_capability(from, "x-ignore-shared", true); + + file = g_strdup_printf("%s/migfile", tmpfs); + uri = g_strdup_printf("exec:cat > %s", file); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + wait_for_migration_complete(from); + + pid = fork(); + if (!pid) { + close(1); + open("/dev/null", O_WRONLY); + execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); + g_assert_not_reached(); + } + + g_assert(waitpid(pid, &wstatus, 0) == pid); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + g_test_message("Failed to analyze the migration stream"); + g_test_fail(); + } + migrate_end(from, to, false); + unlink(file); +} +#endif + +static void test_ignore_shared(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .use_shmem = true, + }; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + wait_for_stop(from, get_src()); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + /* Check whether shared RAM has been really skipped */ + g_assert_cmpint( + read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); + + migrate_end(from, to, true); +} + +static void do_test_validate_uuid(MigrateStart *args, bool should_fail) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (migrate_start(&from, &to, uri, args)) { + return; + } + + /* + * UUID validation is at the begin of migration. So, the main process of + * migration is not interesting for us here. Thus, set huge downtime for + * very fast migration. + */ + migrate_set_parameter_int(from, "downtime-limit", 1000000); + migrate_set_capability(from, "validate-uuid", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + if (should_fail) { + qtest_set_expected_status(to, EXIT_FAILURE); + wait_for_migration_fail(from, true); + } else { + wait_for_migration_complete(from); + } + + migrate_end(from, to, false); +} + +static void test_validate_uuid(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_error(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, true); +} + +static void test_validate_uuid_src_not_set(void) +{ + MigrateStart args = { + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_dst_not_set(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void do_test_validate_uri_channel(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + /* + * 'uri' and 'channels' validation is checked even before the migration + * starts. + */ + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + migrate_end(from, to, false); +} + +static void test_validate_uri_channels_both_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .connect_uri = "tcp:127.0.0.1:0", + .connect_channels = ("[ { ""'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + + do_test_validate_uri_channel(&args); +} + +static void test_validate_uri_channels_none_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + }; + + do_test_validate_uri_channel(&args); +} + +void migration_test_add_misc(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add("/migration/bad_dest", test_baddest); +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/ignore-shared", test_ignore_shared); + } + + migration_test_add("/migration/validate_uuid", test_validate_uuid); + migration_test_add("/migration/validate_uuid_error", + test_validate_uuid_error); + migration_test_add("/migration/validate_uuid_src_not_set", + test_validate_uuid_src_not_set); + migration_test_add("/migration/validate_uuid_dst_not_set", + test_validate_uuid_dst_not_set); + migration_test_add("/migration/validate_uri/channels/both_set", + test_validate_uri_channels_both_set); + migration_test_add("/migration/validate_uri/channels/none_set", + test_validate_uri_channels_none_set); +} From 04e006ab36a8565b92d4e21dd346367fbade7d74 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 7 Dec 2024 18:14:45 +0000 Subject: [PATCH 0196/2892] tcg: Reset free_temps before tcg_optimize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When allocating new temps during tcg_optmize, do not re-use any EBB temps that were used within the TB. We do not have any idea what span of the TB in which the temp was live. Introduce tcg_temp_ebb_reset_freed and use before tcg_optimize, as well as replacing the equivalent in plugin_gen_inject and tcg_func_start. Cc: qemu-stable@nongnu.org Fixes: fb04ab7ddd8 ("tcg/optimize: Lower TCG_COND_TST{EQ,NE} if unsupported") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2711 Reported-by: wannacu Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé --- accel/tcg/plugin-gen.c | 2 +- include/tcg/tcg-temp-internal.h | 6 ++++++ tcg/tcg.c | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 0f47bfbb48..1ef075552c 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -275,7 +275,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) * that might be live within the existing opcode stream. * The simplest solution is to release them all and create new. */ - memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps)); + tcg_temp_ebb_reset_freed(tcg_ctx); QTAILQ_FOREACH_SAFE(op, &tcg_ctx->ops, link, next) { switch (op->opc) { diff --git a/include/tcg/tcg-temp-internal.h b/include/tcg/tcg-temp-internal.h index 44192c55a9..98f91e68b7 100644 --- a/include/tcg/tcg-temp-internal.h +++ b/include/tcg/tcg-temp-internal.h @@ -42,4 +42,10 @@ TCGv_i64 tcg_temp_ebb_new_i64(void); TCGv_ptr tcg_temp_ebb_new_ptr(void); TCGv_i128 tcg_temp_ebb_new_i128(void); +/* Forget all freed EBB temps, so that new allocations produce new temps. */ +static inline void tcg_temp_ebb_reset_freed(TCGContext *s) +{ + memset(s->free_temps, 0, sizeof(s->free_temps)); +} + #endif /* TCG_TEMP_FREE_H */ diff --git a/tcg/tcg.c b/tcg/tcg.c index 0babae1b88..4578b185be 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1489,7 +1489,7 @@ void tcg_func_start(TCGContext *s) s->nb_temps = s->nb_globals; /* No temps have been previously allocated for size or locality. */ - memset(s->free_temps, 0, sizeof(s->free_temps)); + tcg_temp_ebb_reset_freed(s); /* No constant temps have been previously allocated. */ for (int i = 0; i < TCG_TYPE_COUNT; ++i) { @@ -6120,6 +6120,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) } #endif + /* Do not reuse any EBB that may be allocated within the TB. */ + tcg_temp_ebb_reset_freed(s); + tcg_optimize(s); reachable_code_pass(s); From b438362a142527b97b638b7f0f35ebe11911a8d5 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Wed, 11 Dec 2024 07:40:04 +0000 Subject: [PATCH 0197/2892] tcg/riscv: Fix StoreStore barrier generation On RISC-V to StoreStore barrier corresponds `fence w, w` not `fence r, r` Cc: qemu-stable@nongnu.org Fixes: efbea94c76b ("tcg/riscv: Add slowpath load and store instructions") Reviewed-by: Richard Henderson Signed-off-by: Denis Tomashev Signed-off-by: Roman Artemev Message-ID: Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index f8331e4688..96f9a7e348 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1624,7 +1624,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) insn |= 0x02100000; } if (a0 & TCG_MO_ST_ST) { - insn |= 0x02200000; + insn |= 0x01100000; } tcg_out32(s, insn); } From a9af119f3df7f3566c4faf81667ee7a3d49db36f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 11:03:43 -0600 Subject: [PATCH 0198/2892] include/exec: Introduce fpst alias in helper-head.h.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows targets to declare that the helper requires a float_status pointer and instead of a generic void pointer. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/helper-head.h.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/exec/helper-head.h.inc b/include/exec/helper-head.h.inc index 5ef467a79d..bce5db06ef 100644 --- a/include/exec/helper-head.h.inc +++ b/include/exec/helper-head.h.inc @@ -23,6 +23,7 @@ #define dh_alias_ptr ptr #define dh_alias_cptr ptr #define dh_alias_env ptr +#define dh_alias_fpst ptr #define dh_alias_void void #define dh_alias_noreturn noreturn #define dh_alias(t) glue(dh_alias_, t) @@ -39,6 +40,7 @@ #define dh_ctype_ptr void * #define dh_ctype_cptr const void * #define dh_ctype_env CPUArchState * +#define dh_ctype_fpst float_status * #define dh_ctype_void void #define dh_ctype_noreturn G_NORETURN void #define dh_ctype(t) dh_ctype_##t @@ -96,6 +98,7 @@ #define dh_typecode_f64 dh_typecode_i64 #define dh_typecode_cptr dh_typecode_ptr #define dh_typecode_env dh_typecode_ptr +#define dh_typecode_fpst dh_typecode_ptr #define dh_typecode(t) dh_typecode_##t #define dh_callflag_i32 0 From 7ac87b14a92234b6a89b701b4043ad6cf8bdcccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Dec 2024 21:54:18 +0100 Subject: [PATCH 0199/2892] target/sparc: Use memcpy() and remove memcpy32() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than manually copying each register, use the libc memcpy(), which is well optimized nowadays. Suggested-by: Pierrick Bouvier Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20241205205418.67613-1-philmd@linaro.org> Signed-off-by: Richard Henderson --- target/sparc/win_helper.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c index b53fc9ce94..0c4b09f2c1 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -24,29 +24,19 @@ #include "exec/helper-proto.h" #include "trace.h" -static inline void memcpy32(target_ulong *dst, const target_ulong *src) -{ - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - dst[4] = src[4]; - dst[5] = src[5]; - dst[6] = src[6]; - dst[7] = src[7]; -} - void cpu_set_cwp(CPUSPARCState *env, int new_cwp) { /* put the modified wrap registers at their proper location */ if (env->cwp == env->nwindows - 1) { - memcpy32(env->regbase, env->regbase + env->nwindows * 16); + memcpy(env->regbase, env->regbase + env->nwindows * 16, + sizeof(env->gregs)); } env->cwp = new_cwp; /* put the wrap registers at their temporary location */ if (new_cwp == env->nwindows - 1) { - memcpy32(env->regbase + env->nwindows * 16, env->regbase); + memcpy(env->regbase + env->nwindows * 16, env->regbase, + sizeof(env->gregs)); } env->regwptr = env->regbase + (new_cwp * 16); } @@ -361,8 +351,8 @@ void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl) dst = get_gl_gregset(env, env->gl); if (src != dst) { - memcpy32(dst, env->gregs); - memcpy32(env->gregs, src); + memcpy(dst, env->gregs, sizeof(env->gregs)); + memcpy(env->gregs, src, sizeof(env->gregs)); } } @@ -393,8 +383,8 @@ void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate) /* Switch global register bank */ src = get_gregset(env, new_pstate_regs); dst = get_gregset(env, pstate_regs); - memcpy32(dst, env->gregs); - memcpy32(env->gregs, src); + memcpy(dst, env->gregs, sizeof(env->gregs)); + memcpy(env->gregs, src, sizeof(env->gregs)); } else { trace_win_helper_no_switch_pstate(new_pstate_regs); } From 0cb73cb5a024bb724bb7eb39fe7fc5e07dc7180a Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 5 Nov 2024 09:27:22 -0700 Subject: [PATCH 0200/2892] Hexagon (target/hexagon) Remove HEX_DEBUG/HEX_DEBUG_LOG All Hexagon debugging is now done with QEMU mechanisms (e.g., -d in_asm) or with a connected debugger (lldb). Signed-off-by: Taylor Simpson Reviewed-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Signed-off-by: Brian Cain --- target/hexagon/README | 9 --- target/hexagon/cpu.h | 6 -- target/hexagon/genptr.c | 7 --- target/hexagon/helper.h | 3 - target/hexagon/internal.h | 11 ---- target/hexagon/op_helper.c | 112 ------------------------------------- target/hexagon/translate.c | 66 ---------------------- target/hexagon/translate.h | 2 - 8 files changed, 216 deletions(-) diff --git a/target/hexagon/README b/target/hexagon/README index 7ffd517d70..ca617e3364 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -282,10 +282,6 @@ For Hexagon Vector eXtensions (HVX), the following fields are used *** Debugging *** -You can turn on a lot of debugging by changing the HEX_DEBUG macro to 1 in -internal.h. This will stream a lot of information as it generates TCG and -executes the code. - To track down nasty issues with Hexagon->TCG generation, we compare the execution results with actual hardware running on a Hexagon Linux target. Run qemu with the "-d cpu" option. Then, we can diff the results and figure @@ -305,8 +301,3 @@ Here are some handy places to set breakpoints The helper function for each instruction is named helper_, so here's an example that will set a breakpoint at the start br helper_A2_add - If you have the HEX_DEBUG macro set, the following will be useful - At the start of execution of a packet for a given PC - br helper_debug_start_packet if env->gpr[41] == 0xdeadbeef - At the end of execution of a packet for a given PC - br helper_debug_commit_end if this_PC == 0xdeadbeef diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 764f3c38cc..25150d5214 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -79,12 +79,6 @@ typedef struct CPUArchState { uint8_t slot_cancelled; target_ulong new_value_usr; - /* - * Only used when HEX_DEBUG is on, but unconditionally included - * to reduce recompile time when turning HEX_DEBUG on/off. - */ - target_ulong reg_written[TOTAL_PER_THREAD_REGS]; - MemLog mem_log_stores[STORES_MAX]; float_status fp_status; diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index dbae6c570a..2c5e15cfcf 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -100,10 +100,6 @@ void gen_log_reg_write(DisasContext *ctx, int rnum, TCGv val) gen_masked_reg_write(val, hex_gpr[rnum], reg_mask); tcg_gen_mov_tl(get_result_gpr(ctx, rnum), val); - if (HEX_DEBUG) { - /* Do this so HELPER(debug_commit_end) will know */ - tcg_gen_movi_tl(hex_reg_written[rnum], 1); - } } static void gen_log_reg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) @@ -151,9 +147,6 @@ void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val) } else { tcg_gen_and_tl(pred, pred, base_val); } - if (HEX_DEBUG) { - tcg_gen_ori_tl(ctx->pred_written, ctx->pred_written, 1 << pnum); - } set_bit(pnum, ctx->pregs_written); } diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index fa0ebaf7c8..f8baa599c8 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -19,9 +19,6 @@ #include "helper_protos_generated.h.inc" DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32) -DEF_HELPER_1(debug_start_packet, void, env) -DEF_HELPER_FLAGS_3(debug_check_store_width, TCG_CALL_NO_WG, void, env, int, int) -DEF_HELPER_FLAGS_5(debug_commit_end, TCG_CALL_NO_WG, void, env, i32, int, int, int) DEF_HELPER_2(commit_store, void, env, int) DEF_HELPER_3(gather_store, void, env, i32, int) DEF_HELPER_1(commit_hvx_stores, void, env) diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index beb08cb7e3..32e96f00d9 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h @@ -20,17 +20,6 @@ #include "qemu/log.h" -/* - * Change HEX_DEBUG to 1 to turn on debugging output - */ -#define HEX_DEBUG 0 -#define HEX_DEBUG_LOG(...) \ - do { \ - if (HEX_DEBUG) { \ - qemu_log(__VA_ARGS__); \ - } \ - } while (0) - int hexagon_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hexagon_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int hexagon_hvx_gdb_read_register(CPUState *env, GByteArray *mem_buf, int n); diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 90e7aaa097..01d1a1b1a7 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -54,9 +54,6 @@ G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) void log_store32(CPUHexagonState *env, target_ulong addr, target_ulong val, int width, int slot) { - HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx - ", %" PRId32 " [0x08%" PRIx32 "])\n", - width, addr, val, val); env->mem_log_stores[slot].va = addr; env->mem_log_stores[slot].width = width; env->mem_log_stores[slot].data32 = val; @@ -65,35 +62,11 @@ void log_store32(CPUHexagonState *env, target_ulong addr, void log_store64(CPUHexagonState *env, target_ulong addr, int64_t val, int width, int slot) { - HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx - ", %" PRId64 " [0x016%" PRIx64 "])\n", - width, addr, val, val); env->mem_log_stores[slot].va = addr; env->mem_log_stores[slot].width = width; env->mem_log_stores[slot].data64 = val; } -/* Handy place to set a breakpoint */ -void HELPER(debug_start_packet)(CPUHexagonState *env) -{ - HEX_DEBUG_LOG("Start packet: pc = 0x" TARGET_FMT_lx "\n", - env->gpr[HEX_REG_PC]); - - for (int i = 0; i < TOTAL_PER_THREAD_REGS; i++) { - env->reg_written[i] = 0; - } -} - -/* Checks for bookkeeping errors between disassembly context and runtime */ -void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check) -{ - if (env->mem_log_stores[slot].width != check) { - HEX_DEBUG_LOG("ERROR: %d != %d\n", - env->mem_log_stores[slot].width, check); - g_assert_not_reached(); - } -} - static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { uint8_t width = env->mem_log_stores[slot_num].width; @@ -173,91 +146,6 @@ void HELPER(commit_hvx_stores)(CPUHexagonState *env) } } -static void print_store(CPUHexagonState *env, int slot) -{ - if (!(env->slot_cancelled & (1 << slot))) { - uint8_t width = env->mem_log_stores[slot].width; - if (width == 1) { - uint32_t data = env->mem_log_stores[slot].data32 & 0xff; - HEX_DEBUG_LOG("\tmemb[0x" TARGET_FMT_lx "] = %" PRId32 - " (0x%02" PRIx32 ")\n", - env->mem_log_stores[slot].va, data, data); - } else if (width == 2) { - uint32_t data = env->mem_log_stores[slot].data32 & 0xffff; - HEX_DEBUG_LOG("\tmemh[0x" TARGET_FMT_lx "] = %" PRId32 - " (0x%04" PRIx32 ")\n", - env->mem_log_stores[slot].va, data, data); - } else if (width == 4) { - uint32_t data = env->mem_log_stores[slot].data32; - HEX_DEBUG_LOG("\tmemw[0x" TARGET_FMT_lx "] = %" PRId32 - " (0x%08" PRIx32 ")\n", - env->mem_log_stores[slot].va, data, data); - } else if (width == 8) { - HEX_DEBUG_LOG("\tmemd[0x" TARGET_FMT_lx "] = %" PRId64 - " (0x%016" PRIx64 ")\n", - env->mem_log_stores[slot].va, - env->mem_log_stores[slot].data64, - env->mem_log_stores[slot].data64); - } else { - HEX_DEBUG_LOG("\tBad store width %d\n", width); - g_assert_not_reached(); - } - } -} - -/* This function is a handy place to set a breakpoint */ -void HELPER(debug_commit_end)(CPUHexagonState *env, uint32_t this_PC, - int pred_written, int has_st0, int has_st1) -{ - bool reg_printed = false; - bool pred_printed = false; - int i; - - HEX_DEBUG_LOG("Packet committed: pc = 0x" TARGET_FMT_lx "\n", this_PC); - HEX_DEBUG_LOG("slot_cancelled = %d\n", env->slot_cancelled); - - for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { - if (env->reg_written[i]) { - if (!reg_printed) { - HEX_DEBUG_LOG("Regs written\n"); - reg_printed = true; - } - HEX_DEBUG_LOG("\tr%d = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")\n", - i, env->gpr[i], env->gpr[i]); - } - } - - for (i = 0; i < NUM_PREGS; i++) { - if (pred_written & (1 << i)) { - if (!pred_printed) { - HEX_DEBUG_LOG("Predicates written\n"); - pred_printed = true; - } - HEX_DEBUG_LOG("\tp%d = 0x" TARGET_FMT_lx "\n", - i, env->pred[i]); - } - } - - if (has_st0 || has_st1) { - HEX_DEBUG_LOG("Stores\n"); - if (has_st0) { - print_store(env, 0); - } - if (has_st1) { - print_store(env, 1); - } - } - - HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->gpr[HEX_REG_PC]); - HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx - ", insn = " TARGET_FMT_lx - ", hvx = " TARGET_FMT_lx "\n", - env->gpr[HEX_REG_QEMU_PKT_CNT], - env->gpr[HEX_REG_QEMU_INSN_CNT], - env->gpr[HEX_REG_QEMU_HVX_CNT]); - -} - int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS) { uint32_t K_const = extract32(M, 24, 4); diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 4b1bee3c6d..bce85eaeb8 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -50,7 +50,6 @@ TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; TCGv hex_slot_cancelled; TCGv hex_new_value_usr; -TCGv hex_reg_written[TOTAL_PER_THREAD_REGS]; TCGv hex_store_addr[STORES_MAX]; TCGv hex_store_width[STORES_MAX]; TCGv hex_store_val32[STORES_MAX]; @@ -195,21 +194,6 @@ static void gen_exception_end_tb(DisasContext *ctx, int excp) } -#define PACKET_BUFFER_LEN 1028 -static void print_pkt(Packet *pkt) -{ - GString *buf = g_string_sized_new(PACKET_BUFFER_LEN); - snprint_a_pkt_debug(buf, pkt); - HEX_DEBUG_LOG("%s", buf->str); - g_string_free(buf, true); -} -#define HEX_DEBUG_PRINT_PKT(pkt) \ - do { \ - if (HEX_DEBUG) { \ - print_pkt(pkt); \ - } \ - } while (0) - static int read_packet_words(CPUHexagonState *env, DisasContext *ctx, uint32_t words[]) { @@ -235,14 +219,6 @@ static int read_packet_words(CPUHexagonState *env, DisasContext *ctx, g_assert(ctx->base.num_insns == 1); } - HEX_DEBUG_LOG("decode_packet: pc = 0x%" VADDR_PRIx "\n", - ctx->base.pc_next); - HEX_DEBUG_LOG(" words = { "); - for (int i = 0; i < nwords; i++) { - HEX_DEBUG_LOG("0x%x, ", words[i]); - } - HEX_DEBUG_LOG("}\n"); - return nwords; } @@ -465,11 +441,6 @@ static void gen_start_packet(DisasContext *ctx) */ bitmap_zero(ctx->pregs_written, NUM_PREGS); - if (HEX_DEBUG) { - /* Handy place to set a breakpoint before the packet executes */ - gen_helper_debug_start_packet(tcg_env); - } - /* Initialize the runtime state for packet semantics */ if (need_slot_cancelled(pkt)) { tcg_gen_movi_tl(hex_slot_cancelled, 0); @@ -484,10 +455,6 @@ static void gen_start_packet(DisasContext *ctx) tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC); } } - if (HEX_DEBUG) { - ctx->pred_written = tcg_temp_new(); - tcg_gen_movi_tl(ctx->pred_written, 0); - } /* Preload the predicated registers into get_result_gpr(ctx, i) */ if (ctx->need_commit && @@ -635,15 +602,6 @@ static void gen_pred_writes(DisasContext *ctx) } } -static void gen_check_store_width(DisasContext *ctx, int slot_num) -{ - if (HEX_DEBUG) { - TCGv slot = tcg_constant_tl(slot_num); - TCGv check = tcg_constant_tl(ctx->store_width[slot_num]); - gen_helper_debug_check_store_width(tcg_env, slot, check); - } -} - static bool slot_is_predicated(Packet *pkt, int slot_num) { for (int i = 0; i < pkt->num_insns; i++) { @@ -691,25 +649,21 @@ void process_store(DisasContext *ctx, int slot_num) */ switch (ctx->store_width[slot_num]) { case 1: - gen_check_store_width(ctx, slot_num); tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], ctx->mem_idx, MO_UB); break; case 2: - gen_check_store_width(ctx, slot_num); tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], ctx->mem_idx, MO_TEUW); break; case 4: - gen_check_store_width(ctx, slot_num); tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], ctx->mem_idx, MO_TEUL); break; case 8: - gen_check_store_width(ctx, slot_num); tcg_gen_qemu_st_i64(hex_store_val64[slot_num], hex_store_addr[slot_num], ctx->mem_idx, MO_TEUQ); @@ -937,16 +891,6 @@ static void gen_commit_packet(DisasContext *ctx) gen_commit_hvx(ctx); } update_exec_counters(ctx); - if (HEX_DEBUG) { - TCGv has_st0 = - tcg_constant_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa); - TCGv has_st1 = - tcg_constant_tl(pkt->pkt_has_store_s1 && !pkt->pkt_has_dczeroa); - - /* Handy place to set a breakpoint at the end of execution */ - gen_helper_debug_commit_end(tcg_env, tcg_constant_tl(ctx->pkt->pc), - ctx->pred_written, has_st0, has_st1); - } if (pkt->vhist_insn != NULL) { ctx->pre_commit = false; @@ -975,7 +919,6 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) ctx->pkt = &pkt; if (decode_packet(ctx, nwords, words, &pkt, false) > 0) { pkt.pc = ctx->base.pc_next; - HEX_DEBUG_PRINT_PKT(&pkt); gen_start_packet(ctx); for (i = 0; i < pkt.num_insns; i++) { ctx->insn = &pkt.insn[i]; @@ -1093,7 +1036,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, } #define NAME_LEN 64 -static char reg_written_names[TOTAL_PER_THREAD_REGS][NAME_LEN]; static char store_addr_names[STORES_MAX][NAME_LEN]; static char store_width_names[STORES_MAX][NAME_LEN]; static char store_val32_names[STORES_MAX][NAME_LEN]; @@ -1112,14 +1054,6 @@ void hexagon_translate_init(void) hex_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, gpr[i]), hexagon_regnames[i]); - - if (HEX_DEBUG) { - snprintf(reg_written_names[i], NAME_LEN, "reg_written_%s", - hexagon_regnames[i]); - hex_reg_written[i] = tcg_global_mem_new(tcg_env, - offsetof(CPUHexagonState, reg_written[i]), - reg_written_names[i]); - } } hex_new_value_usr = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, new_value_usr), "new_value_usr"); diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 00cc2bcd63..d251e2233f 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -73,7 +73,6 @@ typedef struct DisasContext { bool has_hvx_overlap; TCGv new_value[TOTAL_PER_THREAD_REGS]; TCGv new_pred_value[NUM_PREGS]; - TCGv pred_written; TCGv branch_taken; TCGv dczero_addr; } DisasContext; @@ -271,7 +270,6 @@ extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; extern TCGv hex_pred[NUM_PREGS]; extern TCGv hex_slot_cancelled; extern TCGv hex_new_value_usr; -extern TCGv hex_reg_written[TOTAL_PER_THREAD_REGS]; extern TCGv hex_store_addr[STORES_MAX]; extern TCGv hex_store_width[STORES_MAX]; extern TCGv hex_store_val32[STORES_MAX]; From eed3f358796791dbd065a69adf85ee4a588d3981 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 26 Aug 2024 17:26:30 -0700 Subject: [PATCH 0201/2892] target/hexagon: rename HEX_EXCP_*=>HEX_CAUSE_* The values previously used for "HEX_EXCP_*" were the cause code definitions and not the event numbers. So in this commit, we update the names to reflect the cause codes. In HEX_EVENT_TRAP0's case, we add a new "HEX_EVENT_*" with the correct event number. Reviewed-by: Taylor Simpson Signed-off-by: Brian Cain --- linux-user/hexagon/cpu_loop.c | 4 ++-- target/hexagon/cpu.h | 2 +- target/hexagon/cpu_bits.h | 15 ++++++++------- target/hexagon/gen_tcg.h | 2 +- target/hexagon/translate.c | 6 +++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index d41159e52a..40db596301 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -42,7 +42,7 @@ void cpu_loop(CPUHexagonState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; - case HEX_EXCP_TRAP0: + case HEX_EVENT_TRAP0: syscallnum = env->gpr[6]; env->gpr[HEX_REG_PC] += 4; ret = do_syscall(env, @@ -60,7 +60,7 @@ void cpu_loop(CPUHexagonState *env) env->gpr[0] = ret; } break; - case HEX_EXCP_PC_NOT_ALIGNED: + case HEX_CAUSE_PC_NOT_ALIGNED: force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, env->gpr[HEX_REG_R31]); break; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 25150d5214..14e6e819c2 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -143,7 +143,7 @@ static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, } *flags = hex_flags; if (*pc & PCALIGN_MASK) { - hexagon_raise_exception_err(env, HEX_EXCP_PC_NOT_ALIGNED, 0); + hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); } } diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 4279281a71..2e60c0fafe 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -23,14 +23,15 @@ #define PCALIGN 4 #define PCALIGN_MASK (PCALIGN - 1) -#define HEX_EXCP_FETCH_NO_UPAGE 0x012 -#define HEX_EXCP_INVALID_PACKET 0x015 -#define HEX_EXCP_INVALID_OPCODE 0x015 -#define HEX_EXCP_PC_NOT_ALIGNED 0x01e -#define HEX_EXCP_PRIV_NO_UREAD 0x024 -#define HEX_EXCP_PRIV_NO_UWRITE 0x025 +#define HEX_EVENT_TRAP0 0x008 -#define HEX_EXCP_TRAP0 0x172 +#define HEX_CAUSE_TRAP0 0x172 +#define HEX_CAUSE_FETCH_NO_UPAGE 0x012 +#define HEX_CAUSE_INVALID_PACKET 0x015 +#define HEX_CAUSE_INVALID_OPCODE 0x015 +#define HEX_CAUSE_PC_NOT_ALIGNED 0x01e +#define HEX_CAUSE_PRIV_NO_UREAD 0x024 +#define HEX_CAUSE_PRIV_NO_UWRITE 0x025 #define PACKET_WORDS_MAX 4 diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index 3fc1f4e281..8a3b801287 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -1365,7 +1365,7 @@ do { \ uiV = uiV; \ tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->pkt->pc); \ - TCGv excp = tcg_constant_tl(HEX_EXCP_TRAP0); \ + TCGv excp = tcg_constant_tl(HEX_EVENT_TRAP0); \ gen_helper_raise_exception(tcg_env, excp); \ } while (0) #endif diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index bce85eaeb8..562105705a 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -558,7 +558,7 @@ static void gen_insn(DisasContext *ctx) ctx->insn->generate(ctx); mark_store_width(ctx); } else { - gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE); + gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_OPCODE); } } @@ -912,7 +912,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) nwords = read_packet_words(env, ctx, words); if (!nwords) { - gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET); + gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); return; } @@ -927,7 +927,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) gen_commit_packet(ctx); ctx->base.pc_next += pkt.encod_pkt_size_in_bytes; } else { - gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET); + gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); } } From f0db9f5759372d56d65cfb2d05b03285789468bf Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 26 Aug 2024 17:26:31 -0700 Subject: [PATCH 0202/2892] target/hexagon: add enums for event, cause Reviewed-by: Taylor Simpson Signed-off-by: Brian Cain --- target/hexagon/cpu_bits.h | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 2e60c0fafe..ff596e2a94 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -23,15 +23,21 @@ #define PCALIGN 4 #define PCALIGN_MASK (PCALIGN - 1) -#define HEX_EVENT_TRAP0 0x008 +enum hex_event { + HEX_EVENT_NONE = -1, + HEX_EVENT_TRAP0 = 0x008, +}; -#define HEX_CAUSE_TRAP0 0x172 -#define HEX_CAUSE_FETCH_NO_UPAGE 0x012 -#define HEX_CAUSE_INVALID_PACKET 0x015 -#define HEX_CAUSE_INVALID_OPCODE 0x015 -#define HEX_CAUSE_PC_NOT_ALIGNED 0x01e -#define HEX_CAUSE_PRIV_NO_UREAD 0x024 -#define HEX_CAUSE_PRIV_NO_UWRITE 0x025 +enum hex_cause { + HEX_CAUSE_NONE = -1, + HEX_CAUSE_TRAP0 = 0x172, + HEX_CAUSE_FETCH_NO_UPAGE = 0x012, + HEX_CAUSE_INVALID_PACKET = 0x015, + HEX_CAUSE_INVALID_OPCODE = 0x015, + HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, + HEX_CAUSE_PRIV_NO_UREAD = 0x024, + HEX_CAUSE_PRIV_NO_UWRITE = 0x025, +}; #define PACKET_WORDS_MAX 4 From e295796726131ff9c07a53afe2642c53229e6ca3 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Fri, 6 Dec 2024 17:01:02 +0100 Subject: [PATCH 0203/2892] target/hexagon: Use argparse in all python scripts QOL commit, all the various gen_* python scripts take a large set arguments where order is implicit. Using argparse we also get decent error messages if a field is missing or too many are added. Signed-off-by: Anton Johansson Reviewed-by: Brian Cain Signed-off-by: Brian Cain --- target/hexagon/gen_analyze_funcs.py | 6 +++-- target/hexagon/gen_decodetree.py | 19 +++++++++++--- target/hexagon/gen_helper_funcs.py | 7 +++--- target/hexagon/gen_helper_protos.py | 7 +++--- target/hexagon/gen_idef_parser_funcs.py | 11 +++++++-- target/hexagon/gen_op_attribs.py | 11 +++++++-- target/hexagon/gen_opcodes_def.py | 11 +++++++-- target/hexagon/gen_printinsn.py | 11 +++++++-- target/hexagon/gen_tcg_func_table.py | 11 +++++++-- target/hexagon/gen_tcg_funcs.py | 9 ++++--- target/hexagon/gen_trans_funcs.py | 18 +++++++++++--- target/hexagon/hex_common.py | 33 ++++++++++++------------- target/hexagon/meson.build | 2 +- 13 files changed, 109 insertions(+), 47 deletions(-) diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index 54bac19724..3ac7cc2cfe 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py @@ -78,11 +78,13 @@ def gen_analyze_func(f, tag, regs, imms): def main(): - hex_common.read_common_files() + args = hex_common.parse_common_args( + "Emit functions analyzing register accesses" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[-1], "w") as f: + with open(args.out, "w") as f: f.write("#ifndef HEXAGON_ANALYZE_FUNCS_C_INC\n") f.write("#define HEXAGON_ANALYZE_FUNCS_C_INC\n\n") diff --git a/target/hexagon/gen_decodetree.py b/target/hexagon/gen_decodetree.py index a4fcd622c5..ce703af41d 100755 --- a/target/hexagon/gen_decodetree.py +++ b/target/hexagon/gen_decodetree.py @@ -24,6 +24,7 @@ import sys import textwrap import iset import hex_common +import argparse encs = { tag: "".join(reversed(iset.iset[tag]["enc"].replace(" ", ""))) @@ -191,8 +192,18 @@ def gen_decodetree_file(f, class_to_decode): f.write(f"{tag}\t{enc_str} @{tag}\n") +def main(): + parser = argparse.ArgumentParser( + description="Emit opaque macro calls with instruction semantics" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("class_to_decode", help="instruction class to decode") + parser.add_argument("out", help="output file") + args = parser.parse_args() + + hex_common.read_semantics_file(args.semantics) + with open(args.out, "w") as f: + gen_decodetree_file(f, args.class_to_decode) + if __name__ == "__main__": - hex_common.read_semantics_file(sys.argv[1]) - class_to_decode = sys.argv[2] - with open(sys.argv[3], "w") as f: - gen_decodetree_file(f, class_to_decode) + main() diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index e9685bff2f..c1f806ac4b 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -102,12 +102,13 @@ def gen_helper_function(f, tag, tagregs, tagimms): def main(): - hex_common.read_common_files() + args = hex_common.parse_common_args( + "Emit helper function definitions for each instruction" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - output_file = sys.argv[-1] - with open(output_file, "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: ## Skip the priv instructions if "A_PRIV" in hex_common.attribdict[tag]: diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index fd2bfd0f36..77f8e0a6a3 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -52,12 +52,13 @@ def gen_helper_prototype(f, tag, tagregs, tagimms): def main(): - hex_common.read_common_files() + args = hex_common.parse_common_args( + "Emit helper function prototypes for each instruction" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - output_file = sys.argv[-1] - with open(output_file, "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: ## Skip the priv instructions if "A_PRIV" in hex_common.attribdict[tag]: diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py index 72f11c68ca..2f6e826f76 100644 --- a/target/hexagon/gen_idef_parser_funcs.py +++ b/target/hexagon/gen_idef_parser_funcs.py @@ -20,6 +20,7 @@ import sys import re import string +import argparse from io import StringIO import hex_common @@ -43,13 +44,19 @@ import hex_common ## them are inputs ("in" prefix), while some others are outputs. ## def main(): - hex_common.read_semantics_file(sys.argv[1]) + parser = argparse.ArgumentParser( + "Emit instruction implementations that can be fed to idef-parser" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.calculate_attribs() hex_common.init_registers() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[-1], "w") as f: + with open(args.out, "w") as f: f.write('#include "macros.h.inc"\n\n') for tag in hex_common.tags: diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py index 99448220da..bbbb02df3a 100755 --- a/target/hexagon/gen_op_attribs.py +++ b/target/hexagon/gen_op_attribs.py @@ -21,16 +21,23 @@ import sys import re import string import hex_common +import argparse def main(): - hex_common.read_semantics_file(sys.argv[1]) + parser = argparse.ArgumentParser( + "Emit opaque macro calls containing instruction attributes" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.calculate_attribs() ## ## Generate all the attributes associated with each instruction ## - with open(sys.argv[-1], "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: f.write( f"OP_ATTRIB({tag},ATTRIBS(" diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py index 536f0eb68a..94a19ff412 100755 --- a/target/hexagon/gen_opcodes_def.py +++ b/target/hexagon/gen_opcodes_def.py @@ -21,15 +21,22 @@ import sys import re import string import hex_common +import argparse def main(): - hex_common.read_semantics_file(sys.argv[1]) + parser = argparse.ArgumentParser( + description="Emit opaque macro calls with instruction names" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) ## ## Generate a list of all the opcodes ## - with open(sys.argv[-1], "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: f.write(f"OPCODE({tag}),\n") diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py index 8bf4d0985c..d5f969960a 100755 --- a/target/hexagon/gen_printinsn.py +++ b/target/hexagon/gen_printinsn.py @@ -21,6 +21,7 @@ import sys import re import string import hex_common +import argparse ## @@ -96,11 +97,17 @@ def spacify(s): def main(): - hex_common.read_semantics_file(sys.argv[1]) + parser = argparse.ArgumentParser( + "Emit opaque macro calls with information for printing string representations of instrucions" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) immext_casere = re.compile(r"IMMEXT\(([A-Za-z])") - with open(sys.argv[-1], "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: if not hex_common.behdict[tag]: continue diff --git a/target/hexagon/gen_tcg_func_table.py b/target/hexagon/gen_tcg_func_table.py index 978ac1819b..299a39b1aa 100755 --- a/target/hexagon/gen_tcg_func_table.py +++ b/target/hexagon/gen_tcg_func_table.py @@ -21,15 +21,22 @@ import sys import re import string import hex_common +import argparse def main(): - hex_common.read_semantics_file(sys.argv[1]) + parser = argparse.ArgumentParser( + "Emit opaque macro calls with instruction semantics" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[-1], "w") as f: + with open(args.out, "w") as f: f.write("#ifndef HEXAGON_FUNC_TABLE_H\n") f.write("#define HEXAGON_FUNC_TABLE_H\n\n") diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 05aa0a7855..c2ba91ddc0 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -108,15 +108,16 @@ def gen_def_tcg_func(f, tag, tagregs, tagimms): def main(): - is_idef_parser_enabled = hex_common.read_common_files() + args = hex_common.parse_common_args( + "Emit functions calling generated code implementing instruction semantics (helpers, idef-parser)" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - output_file = sys.argv[-1] - with open(output_file, "w") as f: + with open(args.out, "w") as f: f.write("#ifndef HEXAGON_TCG_FUNCS_H\n") f.write("#define HEXAGON_TCG_FUNCS_H\n\n") - if is_idef_parser_enabled: + if args.idef_parser: f.write('#include "idef-generated-emitter.h.inc"\n\n') for tag in hex_common.tags: diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py index 30f0c73e0c..45da1b7b5d 100755 --- a/target/hexagon/gen_trans_funcs.py +++ b/target/hexagon/gen_trans_funcs.py @@ -24,6 +24,7 @@ import sys import textwrap import iset import hex_common +import argparse encs = { tag: "".join(reversed(iset.iset[tag]["enc"].replace(" ", ""))) @@ -136,8 +137,19 @@ def gen_trans_funcs(f): """)) -if __name__ == "__main__": - hex_common.read_semantics_file(sys.argv[1]) +def main(): + parser = argparse.ArgumentParser( + description="Emit trans_*() functions to be called by " \ + "instruction decoder" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.init_registers() - with open(sys.argv[2], "w") as f: + with open(args.out, "w") as f: gen_trans_funcs(f) + + +if __name__ == "__main__": + main() diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 15ed4980e4..758e5fd12d 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -21,6 +21,7 @@ import sys import re import string import textwrap +import argparse behdict = {} # tag ->behavior semdict = {} # tag -> semantics @@ -1181,22 +1182,20 @@ def helper_args(tag, regs, imms): return args -def read_common_files(): - read_semantics_file(sys.argv[1]) - read_overrides_file(sys.argv[2]) - read_overrides_file(sys.argv[3]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 4 args. -> not enabled, - ## 5 args. -> idef-parser enabled. - ## - ## The 5:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 5 - if is_idef_parser_enabled: - read_idef_parser_enabled_file(sys.argv[4]) +def parse_common_args(desc): + parser = argparse.ArgumentParser(desc) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("overrides", help="overrides file") + parser.add_argument("overrides_vec", help="vector overrides file") + parser.add_argument("out", help="output file") + parser.add_argument("--idef-parser", + help="file of instructions translated by idef-parser") + args = parser.parse_args() + read_semantics_file(args.semantics) + read_overrides_file(args.overrides) + read_overrides_file(args.overrides_vec) + if args.idef_parser: + read_idef_parser_enabled_file(args.idef_parser) calculate_attribs() init_registers() - return is_idef_parser_enabled + return args diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index f1723778a6..bb4ebaae81 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -346,7 +346,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg] - helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, '--idef-parser', idef_generated_list] else # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled From b29b11b51f1ac1884a64c5b6bde969a46206263f Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Fri, 6 Dec 2024 17:01:03 +0100 Subject: [PATCH 0204/2892] target/hexagon: Make HVX vector args. restrict * Adds restrict qualifier to HVX pointer arguments. This will allow the compiler to produce better optimized code, as input vectors are now assumed not to alias, and no runtime aliasing checks will be required. Signed-off-by: Anton Johansson Reviewed-by: Brian Cain Signed-off-by: Brian Cain --- target/hexagon/mmvec/macros.h | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index 1ceb9453ee..bcd4a1e897 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -23,26 +23,26 @@ #include "mmvec/system_ext_mmvec.h" #ifndef QEMU_GENERATE -#define VdV (*(MMVector *)(VdV_void)) -#define VsV (*(MMVector *)(VsV_void)) -#define VuV (*(MMVector *)(VuV_void)) -#define VvV (*(MMVector *)(VvV_void)) -#define VwV (*(MMVector *)(VwV_void)) -#define VxV (*(MMVector *)(VxV_void)) -#define VyV (*(MMVector *)(VyV_void)) +#define VdV (*(MMVector *restrict)(VdV_void)) +#define VsV (*(MMVector *restrict)(VsV_void)) +#define VuV (*(MMVector *restrict)(VuV_void)) +#define VvV (*(MMVector *restrict)(VvV_void)) +#define VwV (*(MMVector *restrict)(VwV_void)) +#define VxV (*(MMVector *restrict)(VxV_void)) +#define VyV (*(MMVector *restrict)(VyV_void)) -#define VddV (*(MMVectorPair *)(VddV_void)) -#define VuuV (*(MMVectorPair *)(VuuV_void)) -#define VvvV (*(MMVectorPair *)(VvvV_void)) -#define VxxV (*(MMVectorPair *)(VxxV_void)) +#define VddV (*(MMVectorPair *restrict)(VddV_void)) +#define VuuV (*(MMVectorPair *restrict)(VuuV_void)) +#define VvvV (*(MMVectorPair *restrict)(VvvV_void)) +#define VxxV (*(MMVectorPair *restrict)(VxxV_void)) -#define QeV (*(MMQReg *)(QeV_void)) -#define QdV (*(MMQReg *)(QdV_void)) -#define QsV (*(MMQReg *)(QsV_void)) -#define QtV (*(MMQReg *)(QtV_void)) -#define QuV (*(MMQReg *)(QuV_void)) -#define QvV (*(MMQReg *)(QvV_void)) -#define QxV (*(MMQReg *)(QxV_void)) +#define QeV (*(MMQReg *restrict)(QeV_void)) +#define QdV (*(MMQReg *restrict)(QdV_void)) +#define QsV (*(MMQReg *restrict)(QsV_void)) +#define QtV (*(MMQReg *restrict)(QtV_void)) +#define QuV (*(MMQReg *restrict)(QuV_void)) +#define QvV (*(MMQReg *restrict)(QvV_void)) +#define QxV (*(MMQReg *restrict)(QxV_void)) #endif #define LOG_VTCM_BYTE(VA, MASK, VAL, IDX) \ From 1d7ab0288cbc6eb6163455fc25dde7a640ef959b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:28 -0600 Subject: [PATCH 0205/2892] target/arm: Add section labels for "Data Processing (register)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the same time, use ### to separate 3rd-level sections. We already use ### for 4.1.92 Data Processing (immediate), but not the two following two third-level sections: 4.1.93 Branches, and 4.1.94 Loads and stores. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 331a8e180c..d28efb884d 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -161,7 +161,7 @@ UBFM . 10 100110 . ...... ...... ..... ..... @bitfield_32 EXTR 1 00 100111 1 0 rm:5 imm:6 rn:5 rd:5 &extract sf=1 EXTR 0 00 100111 0 0 rm:5 0 imm:5 rn:5 rd:5 &extract sf=0 -# Branches +### Branches %imm26 0:s26 !function=times_4 @branch . ..... .......................... &i imm=%imm26 @@ -291,7 +291,7 @@ HLT 1101 0100 010 ................ 000 00 @i16 # DCPS2 1101 0100 101 ................ 000 10 @i16 # DCPS3 1101 0100 101 ................ 000 11 @i16 -# Loads and stores +### Loads and stores &stxr rn rt rt2 rs sz lasr &stlr rn rt sz lasr @@ -649,6 +649,21 @@ CPYP 00 011 1 01000 ..... .... 01 ..... ..... @cpy CPYM 00 011 1 01010 ..... .... 01 ..... ..... @cpy CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy +### Data Processing (register) + +# Data Processing (2-source) +# Data Processing (1-source) +# Logical (shifted reg) +# Add/subtract (shifted reg) +# Add/subtract (extended reg) +# Add/subtract (carry) +# Rotate right into flags +# Evaluate into flags +# Conditional compare (regster) +# Conditional compare (immediate) +# Conditional select +# Data Processing (3-source) + ### Cryptographic AES AESE 01001110 00 10100 00100 10 ..... ..... @r2r_q1e0 From 68298370475c57e3057e3b4d5203e95d0f08647a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:29 -0600 Subject: [PATCH 0206/2892] target/arm: Convert UDIV, SDIV to decodetree 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 Message-id: 20241211163036.2297116-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 ++++ target/arm/tcg/translate-a64.c | 64 +++++++++++++++++----------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d28efb884d..c218f6afbc 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -28,6 +28,7 @@ &r rn &ri rd imm &rri_sf rd rn imm sf +&rrr_sf rd rn rm sf &i imm &rr_e rd rn esz &rri_e rd rn imm esz @@ -652,6 +653,12 @@ CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy ### Data Processing (register) # Data Processing (2-source) + +@rrr_sf sf:1 .......... rm:5 ...... rn:5 rd:5 &rrr_sf + +UDIV . 00 11010110 ..... 00001 0 ..... ..... @rrr_sf +SDIV . 00 11010110 ..... 00001 1 ..... ..... @rrr_sf + # Data Processing (1-source) # Logical (shifted reg) # Add/subtract (shifted reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b2851ea503..9f687ba840 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7485,6 +7485,36 @@ TRANS(UQRSHRN_si, do_scalar_shift_imm_narrow, a, uqrshrn_fns, 0, false) TRANS(SQSHRUN_si, do_scalar_shift_imm_narrow, a, sqshrun_fns, MO_SIGN, false) TRANS(SQRSHRUN_si, do_scalar_shift_imm_narrow, a, sqrshrun_fns, MO_SIGN, false) +static bool do_div(DisasContext *s, arg_rrr_sf *a, bool is_signed) +{ + TCGv_i64 tcg_n, tcg_m, tcg_rd; + tcg_rd = cpu_reg(s, a->rd); + + if (!a->sf && is_signed) { + tcg_n = tcg_temp_new_i64(); + tcg_m = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, a->rn)); + tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, a->rm)); + } else { + tcg_n = read_cpu_reg(s, a->rn, a->sf); + tcg_m = read_cpu_reg(s, a->rm, a->sf); + } + + if (is_signed) { + gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); + } else { + gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); + } + + if (!a->sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + return true; +} + +TRANS(SDIV, do_div, a, true) +TRANS(UDIV, do_div, a, false) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -8425,32 +8455,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) #undef MAP } -static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, - unsigned int rm, unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_n, tcg_m, tcg_rd; - tcg_rd = cpu_reg(s, rd); - - if (!sf && is_signed) { - tcg_n = tcg_temp_new_i64(); - tcg_m = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, rn)); - tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, rm)); - } else { - tcg_n = read_cpu_reg(s, rn, sf); - tcg_m = read_cpu_reg(s, rm, sf); - } - - if (is_signed) { - gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); - } else { - gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); - } - - if (!sf) { /* zero extend final result */ - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } -} /* LSLV, LSRV, ASRV, RORV */ static void handle_shift_reg(DisasContext *s, @@ -8552,12 +8556,6 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) } } break; - case 2: /* UDIV */ - handle_div(s, false, sf, rm, rn, rd); - break; - case 3: /* SDIV */ - handle_div(s, true, sf, rm, rn, rd); - break; case 4: /* IRG */ if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { goto do_unallocated; @@ -8616,6 +8614,8 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) } default: do_unallocated: + case 2: /* UDIV */ + case 3: /* SDIV */ unallocated_encoding(s); break; } From f2b6e3531dfccc6b22bd638c8ecfc58cc26135ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:30 -0600 Subject: [PATCH 0207/2892] target/arm: Convert LSLV, LSRV, ASRV, RORV to decodetree 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 Message-id: 20241211163036.2297116-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 +++ target/arm/tcg/translate-a64.c | 46 ++++++++++++++++------------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c218f6afbc..3db55b78a6 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -658,6 +658,10 @@ CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy UDIV . 00 11010110 ..... 00001 0 ..... ..... @rrr_sf SDIV . 00 11010110 ..... 00001 1 ..... ..... @rrr_sf +LSLV . 00 11010110 ..... 00100 0 ..... ..... @rrr_sf +LSRV . 00 11010110 ..... 00100 1 ..... ..... @rrr_sf +ASRV . 00 11010110 ..... 00101 0 ..... ..... @rrr_sf +RORV . 00 11010110 ..... 00101 1 ..... ..... @rrr_sf # Data Processing (1-source) # Logical (shifted reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9f687ba840..8b7ca2c68a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7575,6 +7575,23 @@ static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf, } } +static bool do_shift_reg(DisasContext *s, arg_rrr_sf *a, + enum a64_shift_type shift_type) +{ + TCGv_i64 tcg_shift = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_rn = read_cpu_reg(s, a->rn, a->sf); + + tcg_gen_andi_i64(tcg_shift, cpu_reg(s, a->rm), a->sf ? 63 : 31); + shift_reg(tcg_rd, tcg_rn, a->sf, shift_type, tcg_shift); + return true; +} + +TRANS(LSLV, do_shift_reg, a, A64_SHIFT_TYPE_LSL) +TRANS(LSRV, do_shift_reg, a, A64_SHIFT_TYPE_LSR) +TRANS(ASRV, do_shift_reg, a, A64_SHIFT_TYPE_ASR) +TRANS(RORV, do_shift_reg, a, A64_SHIFT_TYPE_ROR) + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8456,19 +8473,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) } -/* LSLV, LSRV, ASRV, RORV */ -static void handle_shift_reg(DisasContext *s, - enum a64_shift_type shift_type, unsigned int sf, - unsigned int rm, unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_shift = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); - - tcg_gen_andi_i64(tcg_shift, cpu_reg(s, rm), sf ? 63 : 31); - shift_reg(tcg_rd, tcg_rn, sf, shift_type, tcg_shift); -} - /* CRC32[BHWX], CRC32C[BHWX] */ static void handle_crc32(DisasContext *s, unsigned int sf, unsigned int sz, bool crc32c, @@ -8579,18 +8583,6 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) tcg_gen_or_i64(cpu_reg(s, rd), cpu_reg(s, rm), t); } break; - case 8: /* LSLV */ - handle_shift_reg(s, A64_SHIFT_TYPE_LSL, sf, rm, rn, rd); - break; - case 9: /* LSRV */ - handle_shift_reg(s, A64_SHIFT_TYPE_LSR, sf, rm, rn, rd); - break; - case 10: /* ASRV */ - handle_shift_reg(s, A64_SHIFT_TYPE_ASR, sf, rm, rn, rd); - break; - case 11: /* RORV */ - handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd); - break; case 12: /* PACGA */ if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; @@ -8616,6 +8608,10 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) do_unallocated: case 2: /* UDIV */ case 3: /* SDIV */ + case 8: /* LSLV */ + case 9: /* LSRV */ + case 10: /* ASRV */ + case 11: /* RORV */ unallocated_encoding(s); break; } From 2a1560f5d35f33f1650964fdf6337f2f4a2cd7ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:31 -0600 Subject: [PATCH 0208/2892] target/arm: Convert CRC32, CRC32C to decodetree 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 Message-id: 20241211163036.2297116-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 12 ++++ target/arm/tcg/translate-a64.c | 101 +++++++++++++-------------------- 2 files changed, 53 insertions(+), 60 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 3db55b78a6..1664f4793c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -45,7 +45,9 @@ @rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 @rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd +@rrr_b ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=0 @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 +@rrr_s ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=2 @rrr_d ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=3 @rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd @rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd @@ -663,6 +665,16 @@ LSRV . 00 11010110 ..... 00100 1 ..... ..... @rrr_sf ASRV . 00 11010110 ..... 00101 0 ..... ..... @rrr_sf RORV . 00 11010110 ..... 00101 1 ..... ..... @rrr_sf +CRC32 0 00 11010110 ..... 0100 00 ..... ..... @rrr_b +CRC32 0 00 11010110 ..... 0100 01 ..... ..... @rrr_h +CRC32 0 00 11010110 ..... 0100 10 ..... ..... @rrr_s +CRC32 1 00 11010110 ..... 0100 11 ..... ..... @rrr_d + +CRC32C 0 00 11010110 ..... 0101 00 ..... ..... @rrr_b +CRC32C 0 00 11010110 ..... 0101 01 ..... ..... @rrr_h +CRC32C 0 00 11010110 ..... 0101 10 ..... ..... @rrr_s +CRC32C 1 00 11010110 ..... 0101 11 ..... ..... @rrr_d + # Data Processing (1-source) # Logical (shifted reg) # Add/subtract (shifted reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8b7ca2c68a..22594a1149 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7592,6 +7592,39 @@ TRANS(LSRV, do_shift_reg, a, A64_SHIFT_TYPE_LSR) TRANS(ASRV, do_shift_reg, a, A64_SHIFT_TYPE_ASR) TRANS(RORV, do_shift_reg, a, A64_SHIFT_TYPE_ROR) +static bool do_crc32(DisasContext *s, arg_rrr_e *a, bool crc32c) +{ + TCGv_i64 tcg_acc, tcg_val, tcg_rd; + TCGv_i32 tcg_bytes; + + switch (a->esz) { + case MO_8: + case MO_16: + case MO_32: + tcg_val = tcg_temp_new_i64(); + tcg_gen_extract_i64(tcg_val, cpu_reg(s, a->rm), 0, 8 << a->esz); + break; + case MO_64: + tcg_val = cpu_reg(s, a->rm); + break; + default: + g_assert_not_reached(); + } + tcg_acc = cpu_reg(s, a->rn); + tcg_bytes = tcg_constant_i32(1 << a->esz); + tcg_rd = cpu_reg(s, a->rd); + + if (crc32c) { + gen_helper_crc32c_64(tcg_rd, tcg_acc, tcg_val, tcg_bytes); + } else { + gen_helper_crc32_64(tcg_rd, tcg_acc, tcg_val, tcg_bytes); + } + return true; +} + +TRANS_FEAT(CRC32, aa64_crc32, do_crc32, a, false) +TRANS_FEAT(CRC32C, aa64_crc32, do_crc32, a, true) + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8473,52 +8506,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) } -/* CRC32[BHWX], CRC32C[BHWX] */ -static void handle_crc32(DisasContext *s, - unsigned int sf, unsigned int sz, bool crc32c, - unsigned int rm, unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_acc, tcg_val; - TCGv_i32 tcg_bytes; - - if (!dc_isar_feature(aa64_crc32, s) - || (sf == 1 && sz != 3) - || (sf == 0 && sz == 3)) { - unallocated_encoding(s); - return; - } - - if (sz == 3) { - tcg_val = cpu_reg(s, rm); - } else { - uint64_t mask; - switch (sz) { - case 0: - mask = 0xFF; - break; - case 1: - mask = 0xFFFF; - break; - case 2: - mask = 0xFFFFFFFF; - break; - default: - g_assert_not_reached(); - } - tcg_val = tcg_temp_new_i64(); - tcg_gen_andi_i64(tcg_val, cpu_reg(s, rm), mask); - } - - tcg_acc = cpu_reg(s, rn); - tcg_bytes = tcg_constant_i32(1 << sz); - - if (crc32c) { - gen_helper_crc32c_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); - } else { - gen_helper_crc32_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); - } -} - /* Data-processing (2 source) * 31 30 29 28 21 20 16 15 10 9 5 4 0 * +----+---+---+-----------------+------+--------+------+------+ @@ -8590,20 +8577,6 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) gen_helper_pacga(cpu_reg(s, rd), tcg_env, cpu_reg(s, rn), cpu_reg_sp(s, rm)); break; - case 16: - case 17: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: /* CRC32 */ - { - int sz = extract32(opcode, 0, 2); - bool crc32c = extract32(opcode, 2, 1); - handle_crc32(s, sf, sz, crc32c, rm, rn, rd); - break; - } default: do_unallocated: case 2: /* UDIV */ @@ -8612,6 +8585,14 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) case 9: /* LSRV */ case 10: /* ASRV */ case 11: /* RORV */ + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: /* CRC32 */ unallocated_encoding(s); break; } From 9be60681c24f1834e93d1ac9d79f77556cd648a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:32 -0600 Subject: [PATCH 0209/2892] target/arm: Convert SUBP, IRG, GMI to decodetree 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 Message-id: 20241211163036.2297116-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 +++ target/arm/tcg/translate-a64.c | 94 +++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 1664f4793c..f0a5ffb1cd 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -26,6 +26,7 @@ %hlm 11:1 20:2 &r rn +&rrr rd rn rm &ri rd imm &rri_sf rd rn imm sf &rrr_sf rd rn rm sf @@ -656,6 +657,7 @@ CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy # Data Processing (2-source) +@rrr . .......... rm:5 ...... rn:5 rd:5 &rrr @rrr_sf sf:1 .......... rm:5 ...... rn:5 rd:5 &rrr_sf UDIV . 00 11010110 ..... 00001 0 ..... ..... @rrr_sf @@ -675,6 +677,11 @@ CRC32C 0 00 11010110 ..... 0101 01 ..... ..... @rrr_h CRC32C 0 00 11010110 ..... 0101 10 ..... ..... @rrr_s CRC32C 1 00 11010110 ..... 0101 11 ..... ..... @rrr_d +SUBP 1 00 11010110 ..... 000000 ..... ..... @rrr +SUBPS 1 01 11010110 ..... 000000 ..... ..... @rrr +IRG 1 00 11010110 ..... 000100 ..... ..... @rrr +GMI 1 00 11010110 ..... 000101 ..... ..... @rrr + # Data Processing (1-source) # Logical (shifted reg) # Add/subtract (shifted reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 22594a1149..00e55d42ff 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7625,6 +7625,55 @@ static bool do_crc32(DisasContext *s, arg_rrr_e *a, bool crc32c) TRANS_FEAT(CRC32, aa64_crc32, do_crc32, a, false) TRANS_FEAT(CRC32C, aa64_crc32, do_crc32, a, true) +static bool do_subp(DisasContext *s, arg_rrr *a, bool setflag) +{ + TCGv_i64 tcg_n = read_cpu_reg_sp(s, a->rn, true); + TCGv_i64 tcg_m = read_cpu_reg_sp(s, a->rm, true); + TCGv_i64 tcg_d = cpu_reg(s, a->rd); + + tcg_gen_sextract_i64(tcg_n, tcg_n, 0, 56); + tcg_gen_sextract_i64(tcg_m, tcg_m, 0, 56); + + if (setflag) { + gen_sub_CC(true, tcg_d, tcg_n, tcg_m); + } else { + tcg_gen_sub_i64(tcg_d, tcg_n, tcg_m); + } + return true; +} + +TRANS_FEAT(SUBP, aa64_mte_insn_reg, do_subp, a, false) +TRANS_FEAT(SUBPS, aa64_mte_insn_reg, do_subp, a, true) + +static bool trans_IRG(DisasContext *s, arg_rrr *a) +{ + if (dc_isar_feature(aa64_mte_insn_reg, s)) { + TCGv_i64 tcg_rd = cpu_reg_sp(s, a->rd); + TCGv_i64 tcg_rn = cpu_reg_sp(s, a->rn); + + if (s->ata[0]) { + gen_helper_irg(tcg_rd, tcg_env, tcg_rn, cpu_reg(s, a->rm)); + } else { + gen_address_with_allocation_tag0(tcg_rd, tcg_rn); + } + return true; + } + return false; +} + +static bool trans_GMI(DisasContext *s, arg_rrr *a) +{ + if (dc_isar_feature(aa64_mte_insn_reg, s)) { + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t, cpu_reg_sp(s, a->rn), 56, 4); + tcg_gen_shl_i64(t, tcg_constant_i64(1), t); + tcg_gen_or_i64(cpu_reg(s, a->rd), cpu_reg(s, a->rm), t); + return true; + } + return false; +} + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8528,48 +8577,6 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0: /* SUBP(S) */ - if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } else { - TCGv_i64 tcg_n, tcg_m, tcg_d; - - tcg_n = read_cpu_reg_sp(s, rn, true); - tcg_m = read_cpu_reg_sp(s, rm, true); - tcg_gen_sextract_i64(tcg_n, tcg_n, 0, 56); - tcg_gen_sextract_i64(tcg_m, tcg_m, 0, 56); - tcg_d = cpu_reg(s, rd); - - if (setflag) { - gen_sub_CC(true, tcg_d, tcg_n, tcg_m); - } else { - tcg_gen_sub_i64(tcg_d, tcg_n, tcg_m); - } - } - break; - case 4: /* IRG */ - if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } - if (s->ata[0]) { - gen_helper_irg(cpu_reg_sp(s, rd), tcg_env, - cpu_reg_sp(s, rn), cpu_reg(s, rm)); - } else { - gen_address_with_allocation_tag0(cpu_reg_sp(s, rd), - cpu_reg_sp(s, rn)); - } - break; - case 5: /* GMI */ - if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } else { - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_extract_i64(t, cpu_reg_sp(s, rn), 56, 4); - tcg_gen_shl_i64(t, tcg_constant_i64(1), t); - tcg_gen_or_i64(cpu_reg(s, rd), cpu_reg(s, rm), t); - } - break; case 12: /* PACGA */ if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; @@ -8579,8 +8586,11 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) break; default: do_unallocated: + case 0: /* SUBP(S) */ case 2: /* UDIV */ case 3: /* SDIV */ + case 4: /* IRG */ + case 5: /* GMI */ case 8: /* LSLV */ case 9: /* LSRV */ case 10: /* ASRV */ From b466ea8f216508788d33202b5db5a5666a08cb28 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:33 -0600 Subject: [PATCH 0210/2892] target/arm: Convert PACGA to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove disas_data_proc_2src, as this was the last insn decoded by that function. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 65 ++++++---------------------------- 2 files changed, 13 insertions(+), 54 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f0a5ffb1cd..a23d6a6645 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -682,6 +682,8 @@ SUBPS 1 01 11010110 ..... 000000 ..... ..... @rrr IRG 1 00 11010110 ..... 000100 ..... ..... @rrr GMI 1 00 11010110 ..... 000101 ..... ..... @rrr +PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr + # Data Processing (1-source) # Logical (shifted reg) # Add/subtract (shifted reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 00e55d42ff..ca8b644dc7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7674,6 +7674,16 @@ static bool trans_GMI(DisasContext *s, arg_rrr *a) return false; } +static bool trans_PACGA(DisasContext *s, arg_rrr *a) +{ + if (dc_isar_feature(aa64_pauth, s)) { + gen_helper_pacga(cpu_reg(s, a->rd), tcg_env, + cpu_reg(s, a->rn), cpu_reg_sp(s, a->rm)); + return true; + } + return false; +} + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8555,59 +8565,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) } -/* Data-processing (2 source) - * 31 30 29 28 21 20 16 15 10 9 5 4 0 - * +----+---+---+-----------------+------+--------+------+------+ - * | sf | 0 | S | 1 1 0 1 0 1 1 0 | Rm | opcode | Rn | Rd | - * +----+---+---+-----------------+------+--------+------+------+ - */ -static void disas_data_proc_2src(DisasContext *s, uint32_t insn) -{ - unsigned int sf, rm, opcode, rn, rd, setflag; - sf = extract32(insn, 31, 1); - setflag = extract32(insn, 29, 1); - rm = extract32(insn, 16, 5); - opcode = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (setflag && opcode != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 12: /* PACGA */ - if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - gen_helper_pacga(cpu_reg(s, rd), tcg_env, - cpu_reg(s, rn), cpu_reg_sp(s, rm)); - break; - default: - do_unallocated: - case 0: /* SUBP(S) */ - case 2: /* UDIV */ - case 3: /* SDIV */ - case 4: /* IRG */ - case 5: /* GMI */ - case 8: /* LSLV */ - case 9: /* LSRV */ - case 10: /* ASRV */ - case 11: /* RORV */ - case 16: - case 17: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: /* CRC32 */ - unallocated_encoding(s); - break; - } -} - /* * Data processing - register * 31 30 29 28 25 21 20 16 10 0 @@ -8674,7 +8631,7 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) if (op0) { /* (1 source) */ disas_data_proc_1src(s, insn); } else { /* (2 source) */ - disas_data_proc_2src(s, insn); + goto do_unallocated; } break; case 0x8 ... 0xf: /* (3 source) */ From 506a8c11e7fa1447644f728e4ebd2c00eb8e67ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:34 -0600 Subject: [PATCH 0211/2892] target/arm: Convert RBIT, REV16, REV32, REV64 to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 11 +++ target/arm/tcg/translate-a64.c | 137 +++++++++++++++------------------ 2 files changed, 72 insertions(+), 76 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index a23d6a6645..dd44651f34 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -28,6 +28,8 @@ &r rn &rrr rd rn rm &ri rd imm +&rr rd rn +&rr_sf rd rn sf &rri_sf rd rn imm sf &rrr_sf rd rn rm sf &i imm @@ -685,6 +687,15 @@ GMI 1 00 11010110 ..... 000101 ..... ..... @rrr PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr # Data Processing (1-source) + +@rr . .......... ..... ...... rn:5 rd:5 &rr +@rr_sf sf:1 .......... ..... ...... rn:5 rd:5 &rr_sf + +RBIT . 10 11010110 00000 000000 ..... ..... @rr_sf +REV16 . 10 11010110 00000 000001 ..... ..... @rr_sf +REV32 . 10 11010110 00000 000010 ..... ..... @rr_sf +REV64 1 10 11010110 00000 000011 ..... ..... @rr + # Logical (shifted reg) # Add/subtract (shifted reg) # Add/subtract (extended reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ca8b644dc7..1805d77f43 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7684,6 +7684,60 @@ static bool trans_PACGA(DisasContext *s, arg_rrr *a) return false; } +typedef void ArithOneOp(TCGv_i64, TCGv_i64); + +static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) +{ + fn(cpu_reg(s, rd), cpu_reg(s, rn)); + return true; +} + +static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t32, tcg_rn); + gen_helper_rbit(t32, t32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} + +static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) +{ + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + + tcg_gen_shri_i64(tcg_tmp, tcg_rn, 8); + tcg_gen_and_i64(tcg_rd, tcg_rn, mask); + tcg_gen_and_i64(tcg_tmp, tcg_tmp, mask); + tcg_gen_shli_i64(tcg_rd, tcg_rd, 8); + tcg_gen_or_i64(tcg_rd, tcg_rd, tcg_tmp); +} + +static void gen_rev16_32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_rev16_xx(tcg_rd, tcg_rn, tcg_constant_i64(0x00ff00ff)); +} + +static void gen_rev16_64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_rev16_xx(tcg_rd, tcg_rn, tcg_constant_i64(0x00ff00ff00ff00ffull)); +} + +static void gen_rev_32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_bswap32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); +} + +static void gen_rev32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_bswap64_i64(tcg_rd, tcg_rn); + tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); +} + +TRANS(RBIT, gen_rr, a->rd, a->rn, a->sf ? gen_helper_rbit64 : gen_rbit32) +TRANS(REV16, gen_rr, a->rd, a->rn, a->sf ? gen_rev16_64 : gen_rev16_32) +TRANS(REV32, gen_rr, a->rd, a->rn, a->sf ? gen_rev32 : gen_rev_32) +TRANS(REV64, gen_rr, a->rd, a->rn, tcg_gen_bswap64_i64) + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8302,67 +8356,6 @@ static void handle_cls(DisasContext *s, unsigned int sf, } } -static void handle_rbit(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - gen_helper_rbit64(tcg_rd, tcg_rn); - } else { - TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); - gen_helper_rbit(tcg_tmp32, tcg_tmp32); - tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); - } -} - -/* REV with sf==1, opcode==3 ("REV64") */ -static void handle_rev64(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - if (!sf) { - unallocated_encoding(s); - return; - } - tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn)); -} - -/* REV with sf==0, opcode==2 - * REV32 (sf==1, opcode==2) - */ -static void handle_rev32(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = cpu_reg(s, rn); - - if (sf) { - tcg_gen_bswap64_i64(tcg_rd, tcg_rn); - tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); - } else { - tcg_gen_bswap32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); - } -} - -/* REV16 (opcode==1) */ -static void handle_rev16(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); - TCGv_i64 mask = tcg_constant_i64(sf ? 0x00ff00ff00ff00ffull : 0x00ff00ff); - - tcg_gen_shri_i64(tcg_tmp, tcg_rn, 8); - tcg_gen_and_i64(tcg_rd, tcg_rn, mask); - tcg_gen_and_i64(tcg_tmp, tcg_tmp, mask); - tcg_gen_shli_i64(tcg_rd, tcg_rd, 8); - tcg_gen_or_i64(tcg_rd, tcg_rd, tcg_tmp); -} - /* Data-processing (1 source) * 31 30 29 28 21 20 16 15 10 9 5 4 0 * +----+---+---+-----------------+---------+--------+------+------+ @@ -8388,21 +8381,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) #define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) switch (MAP(sf, opcode2, opcode)) { - case MAP(0, 0x00, 0x00): /* RBIT */ - case MAP(1, 0x00, 0x00): - handle_rbit(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x01): /* REV16 */ - case MAP(1, 0x00, 0x01): - handle_rev16(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x02): /* REV/REV32 */ - case MAP(1, 0x00, 0x02): - handle_rev32(s, sf, rn, rd); - break; - case MAP(1, 0x00, 0x03): /* REV64 */ - handle_rev64(s, sf, rn, rd); - break; case MAP(0, 0x00, 0x04): /* CLZ */ case MAP(1, 0x00, 0x04): handle_clz(s, sf, rn, rd); @@ -8557,6 +8535,13 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) break; default: do_unallocated: + case MAP(0, 0x00, 0x00): /* RBIT */ + case MAP(1, 0x00, 0x00): + case MAP(0, 0x00, 0x01): /* REV16 */ + case MAP(1, 0x00, 0x01): + case MAP(0, 0x00, 0x02): /* REV/REV32 */ + case MAP(1, 0x00, 0x02): + case MAP(1, 0x00, 0x03): /* REV64 */ unallocated_encoding(s); break; } From faa966dffe296654b23ecc7879f34e10b5700d6f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:35 -0600 Subject: [PATCH 0212/2892] target/arm: Convert CLZ, CLS to decodetree 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 Message-id: 20241211163036.2297116-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 ++ target/arm/tcg/translate-a64.c | 72 ++++++++++++++-------------------- 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index dd44651f34..410eaa9333 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -696,6 +696,9 @@ REV16 . 10 11010110 00000 000001 ..... ..... @rr_sf REV32 . 10 11010110 00000 000010 ..... ..... @rr_sf REV64 1 10 11010110 00000 000011 ..... ..... @rr +CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf +CLS . 10 11010110 00000 000101 ..... ..... @rr_sf + # Logical (shifted reg) # Add/subtract (shifted reg) # Add/subtract (extended reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1805d77f43..552b45b4e2 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7738,6 +7738,32 @@ TRANS(REV16, gen_rr, a->rd, a->rn, a->sf ? gen_rev16_64 : gen_rev16_32) TRANS(REV32, gen_rr, a->rd, a->rn, a->sf ? gen_rev32 : gen_rev_32) TRANS(REV64, gen_rr, a->rd, a->rn, tcg_gen_bswap64_i64) +static void gen_clz32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t32, tcg_rn); + tcg_gen_clzi_i32(t32, t32, 32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} + +static void gen_clz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); +} + +static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t32, tcg_rn); + tcg_gen_clrsb_i32(t32, t32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} + +TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) +TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8322,40 +8348,6 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) } } -static void handle_clz(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); - } else { - TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); - tcg_gen_clzi_i32(tcg_tmp32, tcg_tmp32, 32); - tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); - } -} - -static void handle_cls(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - tcg_gen_clrsb_i64(tcg_rd, tcg_rn); - } else { - TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); - tcg_gen_clrsb_i32(tcg_tmp32, tcg_tmp32); - tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); - } -} - /* Data-processing (1 source) * 31 30 29 28 21 20 16 15 10 9 5 4 0 * +----+---+---+-----------------+---------+--------+------+------+ @@ -8381,14 +8373,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) #define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) switch (MAP(sf, opcode2, opcode)) { - case MAP(0, 0x00, 0x04): /* CLZ */ - case MAP(1, 0x00, 0x04): - handle_clz(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x05): /* CLS */ - case MAP(1, 0x00, 0x05): - handle_cls(s, sf, rn, rd); - break; case MAP(1, 0x01, 0x00): /* PACIA */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); @@ -8542,6 +8526,10 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(0, 0x00, 0x02): /* REV/REV32 */ case MAP(1, 0x00, 0x02): case MAP(1, 0x00, 0x03): /* REV64 */ + case MAP(0, 0x00, 0x04): /* CLZ */ + case MAP(1, 0x00, 0x04): + case MAP(0, 0x00, 0x05): /* CLS */ + case MAP(1, 0x00, 0x05): unallocated_encoding(s); break; } From 6934d6c35c5253ece2577d3475bdee78f489aeef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:36 -0600 Subject: [PATCH 0213/2892] target/arm: Convert PAC[ID]*, AUT[ID]* to decodetree This includes PACIA, PACIZA, PACIB, PACIZB, PACDA, PACDZA, PACDB, PACDZB, AUTIA, AUTIZA, AUTIB, AUTIZB, AUTDA, AUTDZA, AUTDB, AUTDZB. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 13 +++ target/arm/tcg/translate-a64.c | 173 +++++++++------------------------ 2 files changed, 58 insertions(+), 128 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 410eaa9333..9083ac4ac3 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -699,6 +699,19 @@ REV64 1 10 11010110 00000 000011 ..... ..... @rr CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf CLS . 10 11010110 00000 000101 ..... ..... @rr_sf +&pacaut rd rn z +@pacaut . .. ........ ..... .. z:1 ... rn:5 rd:5 &pacaut + +PACIA 1 10 11010110 00001 00.000 ..... ..... @pacaut +PACIB 1 10 11010110 00001 00.001 ..... ..... @pacaut +PACDA 1 10 11010110 00001 00.010 ..... ..... @pacaut +PACDB 1 10 11010110 00001 00.011 ..... ..... @pacaut + +AUTIA 1 10 11010110 00001 00.100 ..... ..... @pacaut +AUTIB 1 10 11010110 00001 00.101 ..... ..... @pacaut +AUTDA 1 10 11010110 00001 00.110 ..... ..... @pacaut +AUTDB 1 10 11010110 00001 00.111 ..... ..... @pacaut + # Logical (shifted reg) # Add/subtract (shifted reg) # Add/subtract (extended reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 552b45b4e2..852545dfcc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7764,6 +7764,35 @@ static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) +static bool gen_pacaut(DisasContext *s, arg_pacaut *a, NeonGenTwo64OpEnvFn fn) +{ + TCGv_i64 tcg_rd, tcg_rn; + + if (a->z) { + if (a->rn != 31) { + return false; + } + tcg_rn = tcg_constant_i64(0); + } else { + tcg_rn = cpu_reg_sp(s, a->rn); + } + if (s->pauth_active) { + tcg_rd = cpu_reg(s, a->rd); + fn(tcg_rd, tcg_env, tcg_rd, tcg_rn); + } + return true; +} + +TRANS_FEAT(PACIA, aa64_pauth, gen_pacaut, a, gen_helper_pacia) +TRANS_FEAT(PACIB, aa64_pauth, gen_pacaut, a, gen_helper_pacib) +TRANS_FEAT(PACDA, aa64_pauth, gen_pacaut, a, gen_helper_pacda) +TRANS_FEAT(PACDB, aa64_pauth, gen_pacaut, a, gen_helper_pacdb) + +TRANS_FEAT(AUTIA, aa64_pauth, gen_pacaut, a, gen_helper_autia) +TRANS_FEAT(AUTIB, aa64_pauth, gen_pacaut, a, gen_helper_autib) +TRANS_FEAT(AUTDA, aa64_pauth, gen_pacaut, a, gen_helper_autda) +TRANS_FEAT(AUTDB, aa64_pauth, gen_pacaut, a, gen_helper_autdb) + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8373,134 +8402,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) #define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) switch (MAP(sf, opcode2, opcode)) { - case MAP(1, 0x01, 0x00): /* PACIA */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacia(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x01): /* PACIB */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacib(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x02): /* PACDA */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacda(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x03): /* PACDB */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacdb(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x04): /* AUTIA */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autia(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x05): /* AUTIB */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autib(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x06): /* AUTDA */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autda(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x07): /* AUTDB */ - if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autdb(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x08): /* PACIZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacia(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x09): /* PACIZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacib(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x0a): /* PACDZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacda(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x0b): /* PACDZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_pacdb(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x0c): /* AUTIZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autia(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x0d): /* AUTIZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autib(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x0e): /* AUTDZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autda(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; - case MAP(1, 0x01, 0x0f): /* AUTDZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_autdb(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); - } - break; case MAP(1, 0x01, 0x10): /* XPACI */ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { goto do_unallocated; @@ -8530,6 +8431,22 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x00, 0x04): case MAP(0, 0x00, 0x05): /* CLS */ case MAP(1, 0x00, 0x05): + case MAP(1, 0x01, 0x00): /* PACIA */ + case MAP(1, 0x01, 0x01): /* PACIB */ + case MAP(1, 0x01, 0x02): /* PACDA */ + case MAP(1, 0x01, 0x03): /* PACDB */ + case MAP(1, 0x01, 0x04): /* AUTIA */ + case MAP(1, 0x01, 0x05): /* AUTIB */ + case MAP(1, 0x01, 0x06): /* AUTDA */ + case MAP(1, 0x01, 0x07): /* AUTDB */ + case MAP(1, 0x01, 0x08): /* PACIZA */ + case MAP(1, 0x01, 0x09): /* PACIZB */ + case MAP(1, 0x01, 0x0a): /* PACDZA */ + case MAP(1, 0x01, 0x0b): /* PACDZB */ + case MAP(1, 0x01, 0x0c): /* AUTIZA */ + case MAP(1, 0x01, 0x0d): /* AUTIZB */ + case MAP(1, 0x01, 0x0e): /* AUTDZA */ + case MAP(1, 0x01, 0x0f): /* AUTDZB */ unallocated_encoding(s); break; } From 44e12416e8b0689252acbb7324adda1b9297e816 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:37 -0600 Subject: [PATCH 0214/2892] target/arm: Convert XPAC[ID] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove disas_data_proc_1src, as these were the last insns decoded by that function. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 ++ target/arm/tcg/translate-a64.c | 99 +++++----------------------------- 2 files changed, 16 insertions(+), 86 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 9083ac4ac3..0e04ab6ce4 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -712,6 +712,9 @@ AUTIB 1 10 11010110 00001 00.101 ..... ..... @pacaut AUTDA 1 10 11010110 00001 00.110 ..... ..... @pacaut AUTDB 1 10 11010110 00001 00.111 ..... ..... @pacaut +XPACI 1 10 11010110 00001 010000 11111 rd:5 +XPACD 1 10 11010110 00001 010001 11111 rd:5 + # Logical (shifted reg) # Add/subtract (shifted reg) # Add/subtract (extended reg) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 852545dfcc..d92fe68299 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7793,6 +7793,18 @@ TRANS_FEAT(AUTIB, aa64_pauth, gen_pacaut, a, gen_helper_autib) TRANS_FEAT(AUTDA, aa64_pauth, gen_pacaut, a, gen_helper_autda) TRANS_FEAT(AUTDB, aa64_pauth, gen_pacaut, a, gen_helper_autdb) +static bool do_xpac(DisasContext *s, int rd, NeonGenOne64OpEnvFn *fn) +{ + if (s->pauth_active) { + TCGv_i64 tcg_rd = cpu_reg(s, rd); + fn(tcg_rd, tcg_env, tcg_rd); + } + return true; +} + +TRANS_FEAT(XPACI, aa64_pauth, do_xpac, a->rd, gen_helper_xpaci) +TRANS_FEAT(XPACD, aa64_pauth, do_xpac, a->rd, gen_helper_xpacd) + /* Logical (shifted register) * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 * +----+-----+-----------+-------+---+------+--------+------+------+ @@ -8377,84 +8389,6 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) } } -/* Data-processing (1 source) - * 31 30 29 28 21 20 16 15 10 9 5 4 0 - * +----+---+---+-----------------+---------+--------+------+------+ - * | sf | 1 | S | 1 1 0 1 0 1 1 0 | opcode2 | opcode | Rn | Rd | - * +----+---+---+-----------------+---------+--------+------+------+ - */ -static void disas_data_proc_1src(DisasContext *s, uint32_t insn) -{ - unsigned int sf, opcode, opcode2, rn, rd; - TCGv_i64 tcg_rd; - - if (extract32(insn, 29, 1)) { - unallocated_encoding(s); - return; - } - - sf = extract32(insn, 31, 1); - opcode = extract32(insn, 10, 6); - opcode2 = extract32(insn, 16, 5); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - -#define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) - - switch (MAP(sf, opcode2, opcode)) { - case MAP(1, 0x01, 0x10): /* XPACI */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_xpaci(tcg_rd, tcg_env, tcg_rd); - } - break; - case MAP(1, 0x01, 0x11): /* XPACD */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_xpacd(tcg_rd, tcg_env, tcg_rd); - } - break; - default: - do_unallocated: - case MAP(0, 0x00, 0x00): /* RBIT */ - case MAP(1, 0x00, 0x00): - case MAP(0, 0x00, 0x01): /* REV16 */ - case MAP(1, 0x00, 0x01): - case MAP(0, 0x00, 0x02): /* REV/REV32 */ - case MAP(1, 0x00, 0x02): - case MAP(1, 0x00, 0x03): /* REV64 */ - case MAP(0, 0x00, 0x04): /* CLZ */ - case MAP(1, 0x00, 0x04): - case MAP(0, 0x00, 0x05): /* CLS */ - case MAP(1, 0x00, 0x05): - case MAP(1, 0x01, 0x00): /* PACIA */ - case MAP(1, 0x01, 0x01): /* PACIB */ - case MAP(1, 0x01, 0x02): /* PACDA */ - case MAP(1, 0x01, 0x03): /* PACDB */ - case MAP(1, 0x01, 0x04): /* AUTIA */ - case MAP(1, 0x01, 0x05): /* AUTIB */ - case MAP(1, 0x01, 0x06): /* AUTDA */ - case MAP(1, 0x01, 0x07): /* AUTDB */ - case MAP(1, 0x01, 0x08): /* PACIZA */ - case MAP(1, 0x01, 0x09): /* PACIZB */ - case MAP(1, 0x01, 0x0a): /* PACDZA */ - case MAP(1, 0x01, 0x0b): /* PACDZB */ - case MAP(1, 0x01, 0x0c): /* AUTIZA */ - case MAP(1, 0x01, 0x0d): /* AUTIZB */ - case MAP(1, 0x01, 0x0e): /* AUTDZA */ - case MAP(1, 0x01, 0x0f): /* AUTDZB */ - unallocated_encoding(s); - break; - } - -#undef MAP -} - - /* * Data processing - register * 31 30 29 28 25 21 20 16 10 0 @@ -8464,7 +8398,6 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) */ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) { - int op0 = extract32(insn, 30, 1); int op1 = extract32(insn, 28, 1); int op2 = extract32(insn, 21, 4); int op3 = extract32(insn, 10, 6); @@ -8517,19 +8450,13 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) disas_cond_select(s, insn); break; - case 0x6: /* Data-processing */ - if (op0) { /* (1 source) */ - disas_data_proc_1src(s, insn); - } else { /* (2 source) */ - goto do_unallocated; - } - break; case 0x8 ... 0xf: /* (3 source) */ disas_data_proc_3src(s, insn); break; default: do_unallocated: + case 0x6: /* Data-processing */ unallocated_encoding(s); break; } From 03a1723bd187e30cf45daae120e6236164af2dc4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:38 -0600 Subject: [PATCH 0215/2892] target/arm: Convert disas_logic_reg to decodetree This includes AND, BIC, ORR, ORN, EOR, EON, ANDS, BICS (shifted reg). Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 9 +++ target/arm/tcg/translate-a64.c | 117 ++++++++++++--------------------- 2 files changed, 51 insertions(+), 75 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 0e04ab6ce4..8e2949d236 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -716,6 +716,15 @@ XPACI 1 10 11010110 00001 010000 11111 rd:5 XPACD 1 10 11010110 00001 010001 11111 rd:5 # Logical (shifted reg) + +&logic_shift rd rn rm sf sa st n +@logic_shift sf:1 .. ..... st:2 n:1 rm:5 sa:6 rn:5 rd:5 &logic_shift + +AND_r . 00 01010 .. . ..... ...... ..... ..... @logic_shift +ORR_r . 01 01010 .. . ..... ...... ..... ..... @logic_shift +EOR_r . 10 01010 .. . ..... ...... ..... ..... @logic_shift +ANDS_r . 11 01010 .. . ..... ...... ..... ..... @logic_shift + # Add/subtract (shifted reg) # Add/subtract (extended reg) # Add/subtract (carry) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d92fe68299..ecc8899dd8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7805,96 +7805,65 @@ static bool do_xpac(DisasContext *s, int rd, NeonGenOne64OpEnvFn *fn) TRANS_FEAT(XPACI, aa64_pauth, do_xpac, a->rd, gen_helper_xpaci) TRANS_FEAT(XPACD, aa64_pauth, do_xpac, a->rd, gen_helper_xpacd) -/* Logical (shifted register) - * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 - * +----+-----+-----------+-------+---+------+--------+------+------+ - * | sf | opc | 0 1 0 1 0 | shift | N | Rm | imm6 | Rn | Rd | - * +----+-----+-----------+-------+---+------+--------+------+------+ - */ -static void disas_logic_reg(DisasContext *s, uint32_t insn) +static bool do_logic_reg(DisasContext *s, arg_logic_shift *a, + ArithTwoOp *fn, ArithTwoOp *inv_fn, bool setflags) { TCGv_i64 tcg_rd, tcg_rn, tcg_rm; - unsigned int sf, opc, shift_type, invert, rm, shift_amount, rn, rd; - sf = extract32(insn, 31, 1); - opc = extract32(insn, 29, 2); - shift_type = extract32(insn, 22, 2); - invert = extract32(insn, 21, 1); - rm = extract32(insn, 16, 5); - shift_amount = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (!sf && (shift_amount & (1 << 5))) { - unallocated_encoding(s); - return; + if (!a->sf && (a->sa & (1 << 5))) { + return false; } - tcg_rd = cpu_reg(s, rd); + tcg_rd = cpu_reg(s, a->rd); + tcg_rn = cpu_reg(s, a->rn); - if (opc == 1 && shift_amount == 0 && shift_type == 0 && rn == 31) { - /* Unshifted ORR and ORN with WZR/XZR is the standard encoding for - * register-register MOV and MVN, so it is worth special casing. - */ - tcg_rm = cpu_reg(s, rm); - if (invert) { + tcg_rm = read_cpu_reg(s, a->rm, a->sf); + if (a->sa) { + shift_reg_imm(tcg_rm, tcg_rm, a->sf, a->st, a->sa); + } + + (a->n ? inv_fn : fn)(tcg_rd, tcg_rn, tcg_rm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + if (setflags) { + gen_logic_CC(a->sf, tcg_rd); + } + return true; +} + +static bool trans_ORR_r(DisasContext *s, arg_logic_shift *a) +{ + /* + * Unshifted ORR and ORN with WZR/XZR is the standard encoding for + * register-register MOV and MVN, so it is worth special casing. + */ + if (a->sa == 0 && a->st == 0 && a->rn == 31) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); + + if (a->n) { tcg_gen_not_i64(tcg_rd, tcg_rm); - if (!sf) { + if (!a->sf) { tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } } else { - if (sf) { + if (a->sf) { tcg_gen_mov_i64(tcg_rd, tcg_rm); } else { tcg_gen_ext32u_i64(tcg_rd, tcg_rm); } } - return; + return true; } - tcg_rm = read_cpu_reg(s, rm, sf); - - if (shift_amount) { - shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, shift_amount); - } - - tcg_rn = cpu_reg(s, rn); - - switch (opc | (invert << 2)) { - case 0: /* AND */ - case 3: /* ANDS */ - tcg_gen_and_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 1: /* ORR */ - tcg_gen_or_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 2: /* EOR */ - tcg_gen_xor_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 4: /* BIC */ - case 7: /* BICS */ - tcg_gen_andc_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 5: /* ORN */ - tcg_gen_orc_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 6: /* EON */ - tcg_gen_eqv_i64(tcg_rd, tcg_rn, tcg_rm); - break; - default: - assert(FALSE); - break; - } - - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - - if (opc == 3) { - gen_logic_CC(sf, tcg_rd); - } + return do_logic_reg(s, a, tcg_gen_or_i64, tcg_gen_orc_i64, false); } +TRANS(AND_r, do_logic_reg, a, tcg_gen_and_i64, tcg_gen_andc_i64, false) +TRANS(ANDS_r, do_logic_reg, a, tcg_gen_and_i64, tcg_gen_andc_i64, true) +TRANS(EOR_r, do_logic_reg, a, tcg_gen_xor_i64, tcg_gen_eqv_i64, false) + /* * Add/subtract (extended register) * @@ -8411,11 +8380,9 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) /* Add/sub (shifted register) */ disas_add_sub_reg(s, insn); } - } else { - /* Logical (shifted register) */ - disas_logic_reg(s, insn); + return; } - return; + goto do_unallocated; } switch (op2) { From 7e16f3c3d4e044279b645b56f0b500048acd7cc9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:39 -0600 Subject: [PATCH 0216/2892] target/arm: Convert disas_add_sub_ext_reg to decodetree This includes ADD, SUB, ADDS, SUBS (extended register). Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 9 +++++ target/arm/tcg/translate-a64.c | 65 +++++++++++----------------------- 2 files changed, 29 insertions(+), 45 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8e2949d236..0539694506 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -727,6 +727,15 @@ ANDS_r . 11 01010 .. . ..... ...... ..... ..... @logic_shift # Add/subtract (shifted reg) # Add/subtract (extended reg) + +&addsub_ext rd rn rm sf sa st +@addsub_ext sf:1 .. ........ rm:5 st:3 sa:3 rn:5 rd:5 &addsub_ext + +ADD_ext . 00 01011001 ..... ... ... ..... ..... @addsub_ext +SUB_ext . 10 01011001 ..... ... ... ..... ..... @addsub_ext +ADDS_ext . 01 01011001 ..... ... ... ..... ..... @addsub_ext +SUBS_ext . 11 01011001 ..... ... ... ..... ..... @addsub_ext + # Add/subtract (carry) # Rotate right into flags # Evaluate into flags diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ecc8899dd8..8f777875fe 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7864,57 +7864,27 @@ TRANS(AND_r, do_logic_reg, a, tcg_gen_and_i64, tcg_gen_andc_i64, false) TRANS(ANDS_r, do_logic_reg, a, tcg_gen_and_i64, tcg_gen_andc_i64, true) TRANS(EOR_r, do_logic_reg, a, tcg_gen_xor_i64, tcg_gen_eqv_i64, false) -/* - * Add/subtract (extended register) - * - * 31|30|29|28 24|23 22|21|20 16|15 13|12 10|9 5|4 0| - * +--+--+--+-----------+-----+--+-------+------+------+----+----+ - * |sf|op| S| 0 1 0 1 1 | opt | 1| Rm |option| imm3 | Rn | Rd | - * +--+--+--+-----------+-----+--+-------+------+------+----+----+ - * - * sf: 0 -> 32bit, 1 -> 64bit - * op: 0 -> add , 1 -> sub - * S: 1 -> set flags - * opt: 00 - * option: extension type (see DecodeRegExtend) - * imm3: optional shift to Rm - * - * Rd = Rn + LSL(extend(Rm), amount) - */ -static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) +static bool do_addsub_ext(DisasContext *s, arg_addsub_ext *a, + bool sub_op, bool setflags) { - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm3 = extract32(insn, 10, 3); - int option = extract32(insn, 13, 3); - int rm = extract32(insn, 16, 5); - int opt = extract32(insn, 22, 2); - bool setflags = extract32(insn, 29, 1); - bool sub_op = extract32(insn, 30, 1); - bool sf = extract32(insn, 31, 1); + TCGv_i64 tcg_rm, tcg_rn, tcg_rd, tcg_result; - TCGv_i64 tcg_rm, tcg_rn; /* temps */ - TCGv_i64 tcg_rd; - TCGv_i64 tcg_result; - - if (imm3 > 4 || opt != 0) { - unallocated_encoding(s); - return; + if (a->sa > 4) { + return false; } /* non-flag setting ops may use SP */ if (!setflags) { - tcg_rd = cpu_reg_sp(s, rd); + tcg_rd = cpu_reg_sp(s, a->rd); } else { - tcg_rd = cpu_reg(s, rd); + tcg_rd = cpu_reg(s, a->rd); } - tcg_rn = read_cpu_reg_sp(s, rn, sf); + tcg_rn = read_cpu_reg_sp(s, a->rn, a->sf); - tcg_rm = read_cpu_reg(s, rm, sf); - ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3); + tcg_rm = read_cpu_reg(s, a->rm, a->sf); + ext_and_shift_reg(tcg_rm, tcg_rm, a->st, a->sa); tcg_result = tcg_temp_new_i64(); - if (!setflags) { if (sub_op) { tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); @@ -7923,19 +7893,25 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) } } else { if (sub_op) { - gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); + gen_sub_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } else { - gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); + gen_add_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } } - if (sf) { + if (a->sf) { tcg_gen_mov_i64(tcg_rd, tcg_result); } else { tcg_gen_ext32u_i64(tcg_rd, tcg_result); } + return true; } +TRANS(ADD_ext, do_addsub_ext, a, false, false) +TRANS(SUB_ext, do_addsub_ext, a, true, false) +TRANS(ADDS_ext, do_addsub_ext, a, false, true) +TRANS(SUBS_ext, do_addsub_ext, a, true, true) + /* * Add/subtract (shifted register) * @@ -8374,8 +8350,7 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) if (!op1) { if (op2 & 8) { if (op2 & 1) { - /* Add/sub (extended register) */ - disas_add_sub_ext_reg(s, insn); + goto do_unallocated; } else { /* Add/sub (shifted register) */ disas_add_sub_reg(s, insn); From bde86f2868f8d271b5c5f42e13e879ef2df990e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:40 -0600 Subject: [PATCH 0217/2892] target/arm: Convert disas_add_sub_reg to decodetree This includes ADD, SUB, ADDS, SUBS (shifted register). Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 9 +++++ target/arm/tcg/translate-a64.c | 64 ++++++++++------------------------ 2 files changed, 27 insertions(+), 46 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 0539694506..27a3101bc6 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -726,6 +726,15 @@ EOR_r . 10 01010 .. . ..... ...... ..... ..... @logic_shift ANDS_r . 11 01010 .. . ..... ...... ..... ..... @logic_shift # Add/subtract (shifted reg) + +&addsub_shift rd rn rm sf sa st +@addsub_shift sf:1 .. ..... st:2 . rm:5 sa:6 rn:5 rd:5 &addsub_shift + +ADD_r . 00 01011 .. 0 ..... ...... ..... ..... @addsub_shift +SUB_r . 10 01011 .. 0 ..... ...... ..... ..... @addsub_shift +ADDS_r . 01 01011 .. 0 ..... ...... ..... ..... @addsub_shift +SUBS_r . 11 01011 .. 0 ..... ...... ..... ..... @addsub_shift + # Add/subtract (extended reg) &addsub_ext rd rn rm sf sa st diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8f777875fe..d570bbb696 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7912,47 +7912,22 @@ TRANS(SUB_ext, do_addsub_ext, a, true, false) TRANS(ADDS_ext, do_addsub_ext, a, false, true) TRANS(SUBS_ext, do_addsub_ext, a, true, true) -/* - * Add/subtract (shifted register) - * - * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 - * +--+--+--+-----------+-----+--+-------+---------+------+------+ - * |sf|op| S| 0 1 0 1 1 |shift| 0| Rm | imm6 | Rn | Rd | - * +--+--+--+-----------+-----+--+-------+---------+------+------+ - * - * sf: 0 -> 32bit, 1 -> 64bit - * op: 0 -> add , 1 -> sub - * S: 1 -> set flags - * shift: 00 -> LSL, 01 -> LSR, 10 -> ASR, 11 -> RESERVED - * imm6: Shift amount to apply to Rm before the add/sub - */ -static void disas_add_sub_reg(DisasContext *s, uint32_t insn) +static bool do_addsub_reg(DisasContext *s, arg_addsub_shift *a, + bool sub_op, bool setflags) { - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm6 = extract32(insn, 10, 6); - int rm = extract32(insn, 16, 5); - int shift_type = extract32(insn, 22, 2); - bool setflags = extract32(insn, 29, 1); - bool sub_op = extract32(insn, 30, 1); - bool sf = extract32(insn, 31, 1); + TCGv_i64 tcg_rd, tcg_rn, tcg_rm, tcg_result; - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn, tcg_rm; - TCGv_i64 tcg_result; - - if ((shift_type == 3) || (!sf && (imm6 > 31))) { - unallocated_encoding(s); - return; + if (a->st == 3 || (!a->sf && (a->sa & 32))) { + return false; } - tcg_rn = read_cpu_reg(s, rn, sf); - tcg_rm = read_cpu_reg(s, rm, sf); + tcg_rd = cpu_reg(s, a->rd); + tcg_rn = read_cpu_reg(s, a->rn, a->sf); + tcg_rm = read_cpu_reg(s, a->rm, a->sf); - shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, imm6); + shift_reg_imm(tcg_rm, tcg_rm, a->sf, a->st, a->sa); tcg_result = tcg_temp_new_i64(); - if (!setflags) { if (sub_op) { tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); @@ -7961,19 +7936,25 @@ static void disas_add_sub_reg(DisasContext *s, uint32_t insn) } } else { if (sub_op) { - gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); + gen_sub_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } else { - gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); + gen_add_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } } - if (sf) { + if (a->sf) { tcg_gen_mov_i64(tcg_rd, tcg_result); } else { tcg_gen_ext32u_i64(tcg_rd, tcg_result); } + return true; } +TRANS(ADD_r, do_addsub_reg, a, false, false) +TRANS(SUB_r, do_addsub_reg, a, true, false) +TRANS(ADDS_r, do_addsub_reg, a, false, true) +TRANS(SUBS_r, do_addsub_reg, a, true, true) + /* Data-processing (3 source) * * 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0 @@ -8348,15 +8329,6 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) int op3 = extract32(insn, 10, 6); if (!op1) { - if (op2 & 8) { - if (op2 & 1) { - goto do_unallocated; - } else { - /* Add/sub (shifted register) */ - disas_add_sub_reg(s, insn); - } - return; - } goto do_unallocated; } From 4d1c86efb3eb369643966439ec5f3717673e8616 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:41 -0600 Subject: [PATCH 0218/2892] target/arm: Convert disas_data_proc_3src to decodetree This includes MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL, SMULH, UMULH. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 16 +++++ target/arm/tcg/translate-a64.c | 119 ++++++++++++--------------------- 2 files changed, 59 insertions(+), 76 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 27a3101bc6..b0cc8bd476 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -753,6 +753,22 @@ SUBS_ext . 11 01011001 ..... ... ... ..... ..... @addsub_ext # Conditional select # Data Processing (3-source) +&rrrr rd rn rm ra +@rrrr . .. ........ rm:5 . ra:5 rn:5 rd:5 &rrrr + +MADD_w 0 00 11011000 ..... 0 ..... ..... ..... @rrrr +MSUB_w 0 00 11011000 ..... 1 ..... ..... ..... @rrrr +MADD_x 1 00 11011000 ..... 0 ..... ..... ..... @rrrr +MSUB_x 1 00 11011000 ..... 1 ..... ..... ..... @rrrr + +SMADDL 1 00 11011001 ..... 0 ..... ..... ..... @rrrr +SMSUBL 1 00 11011001 ..... 1 ..... ..... ..... @rrrr +UMADDL 1 00 11011101 ..... 0 ..... ..... ..... @rrrr +UMSUBL 1 00 11011101 ..... 1 ..... ..... ..... @rrrr + +SMULH 1 00 11011010 ..... 0 11111 ..... ..... @rrr +UMULH 1 00 11011110 ..... 0 11111 ..... ..... @rrr + ### Cryptographic AES AESE 01001110 00 10100 00100 10 ..... ..... @r2r_q1e0 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d570bbb696..99ff787c61 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7955,98 +7955,68 @@ TRANS(SUB_r, do_addsub_reg, a, true, false) TRANS(ADDS_r, do_addsub_reg, a, false, true) TRANS(SUBS_r, do_addsub_reg, a, true, true) -/* Data-processing (3 source) - * - * 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0 - * +--+------+-----------+------+------+----+------+------+------+ - * |sf| op54 | 1 1 0 1 1 | op31 | Rm | o0 | Ra | Rn | Rd | - * +--+------+-----------+------+------+----+------+------+------+ - */ -static void disas_data_proc_3src(DisasContext *s, uint32_t insn) +static bool do_mulh(DisasContext *s, arg_rrr *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int ra = extract32(insn, 10, 5); - int rm = extract32(insn, 16, 5); - int op_id = (extract32(insn, 29, 3) << 4) | - (extract32(insn, 21, 3) << 1) | - extract32(insn, 15, 1); - bool sf = extract32(insn, 31, 1); - bool is_sub = extract32(op_id, 0, 1); - bool is_high = extract32(op_id, 2, 1); - bool is_signed = false; - TCGv_i64 tcg_op1; - TCGv_i64 tcg_op2; - TCGv_i64 tcg_tmp; + TCGv_i64 discard = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); - /* Note that op_id is sf:op54:op31:o0 so it includes the 32/64 size flag */ - switch (op_id) { - case 0x42: /* SMADDL */ - case 0x43: /* SMSUBL */ - case 0x44: /* SMULH */ - is_signed = true; - break; - case 0x0: /* MADD (32bit) */ - case 0x1: /* MSUB (32bit) */ - case 0x40: /* MADD (64bit) */ - case 0x41: /* MSUB (64bit) */ - case 0x4a: /* UMADDL */ - case 0x4b: /* UMSUBL */ - case 0x4c: /* UMULH */ - break; - default: - unallocated_encoding(s); - return; - } + fn(discard, tcg_rd, tcg_rn, tcg_rm); + return true; +} - if (is_high) { - TCGv_i64 low_bits = tcg_temp_new_i64(); /* low bits discarded */ - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = cpu_reg(s, rn); - TCGv_i64 tcg_rm = cpu_reg(s, rm); +TRANS(SMULH, do_mulh, a, tcg_gen_muls2_i64) +TRANS(UMULH, do_mulh, a, tcg_gen_mulu2_i64) - if (is_signed) { - tcg_gen_muls2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); - } else { - tcg_gen_mulu2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); - } - return; - } +static bool do_muladd(DisasContext *s, arg_rrrr *a, + bool sf, bool is_sub, MemOp mop) +{ + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_op1, tcg_op2; - tcg_op1 = tcg_temp_new_i64(); - tcg_op2 = tcg_temp_new_i64(); - tcg_tmp = tcg_temp_new_i64(); - - if (op_id < 0x42) { - tcg_gen_mov_i64(tcg_op1, cpu_reg(s, rn)); - tcg_gen_mov_i64(tcg_op2, cpu_reg(s, rm)); + if (mop == MO_64) { + tcg_op1 = cpu_reg(s, a->rn); + tcg_op2 = cpu_reg(s, a->rm); } else { - if (is_signed) { - tcg_gen_ext32s_i64(tcg_op1, cpu_reg(s, rn)); - tcg_gen_ext32s_i64(tcg_op2, cpu_reg(s, rm)); - } else { - tcg_gen_ext32u_i64(tcg_op1, cpu_reg(s, rn)); - tcg_gen_ext32u_i64(tcg_op2, cpu_reg(s, rm)); - } + tcg_op1 = tcg_temp_new_i64(); + tcg_op2 = tcg_temp_new_i64(); + tcg_gen_ext_i64(tcg_op1, cpu_reg(s, a->rn), mop); + tcg_gen_ext_i64(tcg_op2, cpu_reg(s, a->rm), mop); } - if (ra == 31 && !is_sub) { + if (a->ra == 31 && !is_sub) { /* Special-case MADD with rA == XZR; it is the standard MUL alias */ - tcg_gen_mul_i64(cpu_reg(s, rd), tcg_op1, tcg_op2); + tcg_gen_mul_i64(tcg_rd, tcg_op1, tcg_op2); } else { + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_ra = cpu_reg(s, a->ra); + tcg_gen_mul_i64(tcg_tmp, tcg_op1, tcg_op2); if (is_sub) { - tcg_gen_sub_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp); + tcg_gen_sub_i64(tcg_rd, tcg_ra, tcg_tmp); } else { - tcg_gen_add_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp); + tcg_gen_add_i64(tcg_rd, tcg_ra, tcg_tmp); } } if (!sf) { - tcg_gen_ext32u_i64(cpu_reg(s, rd), cpu_reg(s, rd)); + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } + return true; } +TRANS(MADD_w, do_muladd, a, false, false, MO_64) +TRANS(MSUB_w, do_muladd, a, false, true, MO_64) +TRANS(MADD_x, do_muladd, a, true, false, MO_64) +TRANS(MSUB_x, do_muladd, a, true, true, MO_64) + +TRANS(SMADDL, do_muladd, a, true, false, MO_SL) +TRANS(SMSUBL, do_muladd, a, true, true, MO_SL) +TRANS(UMADDL, do_muladd, a, true, false, MO_UL) +TRANS(UMSUBL, do_muladd, a, true, true, MO_UL) + /* Add/subtract (with carry) * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0 * +--+--+--+------------------------+------+-------------+------+-----+ @@ -8364,13 +8334,10 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) disas_cond_select(s, insn); break; - case 0x8 ... 0xf: /* (3 source) */ - disas_data_proc_3src(s, insn); - break; - default: do_unallocated: case 0x6: /* Data-processing */ + case 0x8 ... 0xf: /* (3 source) */ unallocated_encoding(s); break; } From eeb4a519628cb48258d87ab704e8ed78048f81b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:42 -0600 Subject: [PATCH 0219/2892] target/arm: Convert disas_adc_sbc to decodetree This includes ADC, SBC, ADCS, SBCS. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 6 +++++ target/arm/tcg/translate-a64.c | 43 +++++++++++++--------------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index b0cc8bd476..7a40ca455e 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -746,6 +746,12 @@ ADDS_ext . 01 01011001 ..... ... ... ..... ..... @addsub_ext SUBS_ext . 11 01011001 ..... ... ... ..... ..... @addsub_ext # Add/subtract (carry) + +ADC . 00 11010000 ..... 000000 ..... ..... @rrr_sf +ADCS . 01 11010000 ..... 000000 ..... ..... @rrr_sf +SBC . 10 11010000 ..... 000000 ..... ..... @rrr_sf +SBCS . 11 11010000 ..... 000000 ..... ..... @rrr_sf + # Rotate right into flags # Evaluate into flags # Conditional compare (regster) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 99ff787c61..d7747fcf57 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8017,42 +8017,34 @@ TRANS(SMSUBL, do_muladd, a, true, true, MO_SL) TRANS(UMADDL, do_muladd, a, true, false, MO_UL) TRANS(UMSUBL, do_muladd, a, true, true, MO_UL) -/* Add/subtract (with carry) - * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0 - * +--+--+--+------------------------+------+-------------+------+-----+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | 0 0 0 0 0 0 | Rn | Rd | - * +--+--+--+------------------------+------+-------------+------+-----+ - */ - -static void disas_adc_sbc(DisasContext *s, uint32_t insn) +static bool do_adc_sbc(DisasContext *s, arg_rrr_sf *a, + bool is_sub, bool setflags) { - unsigned int sf, op, setflags, rm, rn, rd; TCGv_i64 tcg_y, tcg_rn, tcg_rd; - sf = extract32(insn, 31, 1); - op = extract32(insn, 30, 1); - setflags = extract32(insn, 29, 1); - rm = extract32(insn, 16, 5); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); + tcg_rd = cpu_reg(s, a->rd); + tcg_rn = cpu_reg(s, a->rn); - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (op) { + if (is_sub) { tcg_y = tcg_temp_new_i64(); - tcg_gen_not_i64(tcg_y, cpu_reg(s, rm)); + tcg_gen_not_i64(tcg_y, cpu_reg(s, a->rm)); } else { - tcg_y = cpu_reg(s, rm); + tcg_y = cpu_reg(s, a->rm); } if (setflags) { - gen_adc_CC(sf, tcg_rd, tcg_rn, tcg_y); + gen_adc_CC(a->sf, tcg_rd, tcg_rn, tcg_y); } else { - gen_adc(sf, tcg_rd, tcg_rn, tcg_y); + gen_adc(a->sf, tcg_rd, tcg_rn, tcg_y); } + return true; } +TRANS(ADC, do_adc_sbc, a, false, false) +TRANS(SBC, do_adc_sbc, a, true, false) +TRANS(ADCS, do_adc_sbc, a, false, true) +TRANS(SBCS, do_adc_sbc, a, true, true) + /* * Rotate right into flags * 31 30 29 21 15 10 5 4 0 @@ -8305,10 +8297,6 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) switch (op2) { case 0x0: switch (op3) { - case 0x00: /* Add/subtract (with carry) */ - disas_adc_sbc(s, insn); - break; - case 0x01: /* Rotate right into flags */ case 0x21: disas_rotate_right_into_flags(s, insn); @@ -8322,6 +8310,7 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) break; default: + case 0x00: /* Add/subtract (with carry) */ goto do_unallocated; } break; From 9fa4829be6f93468a73e7bd7399ad1ea332da0eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:43 -0600 Subject: [PATCH 0220/2892] target/arm: Convert RMIF to decodetree 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 Message-id: 20241211163036.2297116-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 +++ target/arm/tcg/translate-a64.c | 32 +++++++++----------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7a40ca455e..454494742e 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -753,6 +753,9 @@ SBC . 10 11010000 ..... 000000 ..... ..... @rrr_sf SBCS . 11 11010000 ..... 000000 ..... ..... @rrr_sf # Rotate right into flags + +RMIF 1 01 11010000 imm:6 00001 rn:5 0 mask:4 + # Evaluate into flags # Conditional compare (regster) # Conditional compare (immediate) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d7747fcf57..1af41e22eb 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8045,30 +8045,18 @@ TRANS(SBC, do_adc_sbc, a, true, false) TRANS(ADCS, do_adc_sbc, a, false, true) TRANS(SBCS, do_adc_sbc, a, true, true) -/* - * Rotate right into flags - * 31 30 29 21 15 10 5 4 0 - * +--+--+--+-----------------+--------+-----------+------+--+------+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | imm6 | 0 0 0 0 1 | Rn |o2| mask | - * +--+--+--+-----------------+--------+-----------+------+--+------+ - */ -static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn) +static bool trans_RMIF(DisasContext *s, arg_RMIF *a) { - int mask = extract32(insn, 0, 4); - int o2 = extract32(insn, 4, 1); - int rn = extract32(insn, 5, 5); - int imm6 = extract32(insn, 15, 6); - int sf_op_s = extract32(insn, 29, 3); + int mask = a->mask; TCGv_i64 tcg_rn; TCGv_i32 nzcv; - if (sf_op_s != 5 || o2 != 0 || !dc_isar_feature(aa64_condm_4, s)) { - unallocated_encoding(s); - return; + if (!dc_isar_feature(aa64_condm_4, s)) { + return false; } - tcg_rn = read_cpu_reg(s, rn, 1); - tcg_gen_rotri_i64(tcg_rn, tcg_rn, imm6); + tcg_rn = read_cpu_reg(s, a->rn, 1); + tcg_gen_rotri_i64(tcg_rn, tcg_rn, a->imm); nzcv = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(nzcv, tcg_rn); @@ -8086,6 +8074,7 @@ static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn) if (mask & 1) { /* V */ tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0); } + return true; } /* @@ -8297,11 +8286,6 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) switch (op2) { case 0x0: switch (op3) { - case 0x01: /* Rotate right into flags */ - case 0x21: - disas_rotate_right_into_flags(s, insn); - break; - case 0x02: /* Evaluate into flags */ case 0x12: case 0x22: @@ -8311,6 +8295,8 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) default: case 0x00: /* Add/subtract (with carry) */ + case 0x01: /* Rotate right into flags */ + case 0x21: goto do_unallocated; } break; From 729bca958d906350f994e87b9f9b68cc0d945d59 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:44 -0600 Subject: [PATCH 0221/2892] target/arm: Convert SETF8, SETF16 to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 +++ target/arm/tcg/translate-a64.c | 48 +++++----------------------------- 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 454494742e..ae2c6831d7 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -757,6 +757,10 @@ SBCS . 11 11010000 ..... 000000 ..... ..... @rrr_sf RMIF 1 01 11010000 imm:6 00001 rn:5 0 mask:4 # Evaluate into flags + +SETF8 0 01 11010000 00000 000010 rn:5 01101 +SETF16 0 01 11010000 00000 010010 rn:5 01101 + # Conditional compare (regster) # Conditional compare (immediate) # Conditional select diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1af41e22eb..774689641d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8077,38 +8077,21 @@ static bool trans_RMIF(DisasContext *s, arg_RMIF *a) return true; } -/* - * Evaluate into flags - * 31 30 29 21 15 14 10 5 4 0 - * +--+--+--+-----------------+---------+----+---------+------+--+------+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | opcode2 | sz | 0 0 1 0 | Rn |o3| mask | - * +--+--+--+-----------------+---------+----+---------+------+--+------+ - */ -static void disas_evaluate_into_flags(DisasContext *s, uint32_t insn) +static bool do_setf(DisasContext *s, int rn, int shift) { - int o3_mask = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int o2 = extract32(insn, 15, 6); - int sz = extract32(insn, 14, 1); - int sf_op_s = extract32(insn, 29, 3); - TCGv_i32 tmp; - int shift; + TCGv_i32 tmp = tcg_temp_new_i32(); - if (sf_op_s != 1 || o2 != 0 || o3_mask != 0xd || - !dc_isar_feature(aa64_condm_4, s)) { - unallocated_encoding(s); - return; - } - shift = sz ? 16 : 24; /* SETF16 or SETF8 */ - - tmp = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(tmp, cpu_reg(s, rn)); tcg_gen_shli_i32(cpu_NF, tmp, shift); tcg_gen_shli_i32(cpu_VF, tmp, shift - 1); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_VF, cpu_NF); + return true; } +TRANS_FEAT(SETF8, aa64_condm_4, do_setf, a->rn, 24) +TRANS_FEAT(SETF16, aa64_condm_4, do_setf, a->rn, 16) + /* Conditional compare (immediate / register) * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ @@ -8277,30 +8260,12 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) { int op1 = extract32(insn, 28, 1); int op2 = extract32(insn, 21, 4); - int op3 = extract32(insn, 10, 6); if (!op1) { goto do_unallocated; } switch (op2) { - case 0x0: - switch (op3) { - case 0x02: /* Evaluate into flags */ - case 0x12: - case 0x22: - case 0x32: - disas_evaluate_into_flags(s, insn); - break; - - default: - case 0x00: /* Add/subtract (with carry) */ - case 0x01: /* Rotate right into flags */ - case 0x21: - goto do_unallocated; - } - break; - case 0x2: /* Conditional compare */ disas_cc(s, insn); /* both imm and reg forms */ break; @@ -8311,6 +8276,7 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) default: do_unallocated: + case 0x0: case 0x6: /* Data-processing */ case 0x8 ... 0xf: /* (3 source) */ unallocated_encoding(s); From 916ad3f733a54e2dc424c4b210beea552bbeedd1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:45 -0600 Subject: [PATCH 0222/2892] target/arm: Convert CCMP, CCMN to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 6 ++-- target/arm/tcg/translate-a64.c | 66 +++++++++++----------------------- 2 files changed, 25 insertions(+), 47 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index ae2c6831d7..a9d7d57199 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -761,8 +761,10 @@ RMIF 1 01 11010000 imm:6 00001 rn:5 0 mask:4 SETF8 0 01 11010000 00000 000010 rn:5 01101 SETF16 0 01 11010000 00000 010010 rn:5 01101 -# Conditional compare (regster) -# Conditional compare (immediate) +# Conditional compare + +CCMP sf:1 op:1 1 11010010 y:5 cond:4 imm:1 0 rn:5 0 nzcv:4 + # Conditional select # Data Processing (3-source) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 774689641d..56a445a3c2 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8092,68 +8092,46 @@ static bool do_setf(DisasContext *s, int rn, int shift) TRANS_FEAT(SETF8, aa64_condm_4, do_setf, a->rn, 24) TRANS_FEAT(SETF16, aa64_condm_4, do_setf, a->rn, 16) -/* Conditional compare (immediate / register) - * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 - * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ - * |sf|op| S| 1 1 0 1 0 0 1 0 |imm5/rm | cond |i/r |o2| Rn |o3|nzcv | - * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ - * [1] y [0] [0] - */ -static void disas_cc(DisasContext *s, uint32_t insn) +/* CCMP, CCMN */ +static bool trans_CCMP(DisasContext *s, arg_CCMP *a) { - unsigned int sf, op, y, cond, rn, nzcv, is_imm; - TCGv_i32 tcg_t0, tcg_t1, tcg_t2; - TCGv_i64 tcg_tmp, tcg_y, tcg_rn; + TCGv_i32 tcg_t0 = tcg_temp_new_i32(); + TCGv_i32 tcg_t1 = tcg_temp_new_i32(); + TCGv_i32 tcg_t2 = tcg_temp_new_i32(); + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_rn, tcg_y; DisasCompare c; - - if (!extract32(insn, 29, 1)) { - unallocated_encoding(s); - return; - } - if (insn & (1 << 10 | 1 << 4)) { - unallocated_encoding(s); - return; - } - sf = extract32(insn, 31, 1); - op = extract32(insn, 30, 1); - is_imm = extract32(insn, 11, 1); - y = extract32(insn, 16, 5); /* y = rm (reg) or imm5 (imm) */ - cond = extract32(insn, 12, 4); - rn = extract32(insn, 5, 5); - nzcv = extract32(insn, 0, 4); + unsigned nzcv; /* Set T0 = !COND. */ - tcg_t0 = tcg_temp_new_i32(); - arm_test_cc(&c, cond); + arm_test_cc(&c, a->cond); tcg_gen_setcondi_i32(tcg_invert_cond(c.cond), tcg_t0, c.value, 0); /* Load the arguments for the new comparison. */ - if (is_imm) { - tcg_y = tcg_temp_new_i64(); - tcg_gen_movi_i64(tcg_y, y); + if (a->imm) { + tcg_y = tcg_constant_i64(a->y); } else { - tcg_y = cpu_reg(s, y); + tcg_y = cpu_reg(s, a->y); } - tcg_rn = cpu_reg(s, rn); + tcg_rn = cpu_reg(s, a->rn); /* Set the flags for the new comparison. */ - tcg_tmp = tcg_temp_new_i64(); - if (op) { - gen_sub_CC(sf, tcg_tmp, tcg_rn, tcg_y); + if (a->op) { + gen_sub_CC(a->sf, tcg_tmp, tcg_rn, tcg_y); } else { - gen_add_CC(sf, tcg_tmp, tcg_rn, tcg_y); + gen_add_CC(a->sf, tcg_tmp, tcg_rn, tcg_y); } - /* If COND was false, force the flags to #nzcv. Compute two masks + /* + * If COND was false, force the flags to #nzcv. Compute two masks * to help with this: T1 = (COND ? 0 : -1), T2 = (COND ? -1 : 0). * For tcg hosts that support ANDC, we can make do with just T1. * In either case, allow the tcg optimizer to delete any unused mask. */ - tcg_t1 = tcg_temp_new_i32(); - tcg_t2 = tcg_temp_new_i32(); tcg_gen_neg_i32(tcg_t1, tcg_t0); tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); + nzcv = a->nzcv; if (nzcv & 8) { /* N */ tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); } else { @@ -8190,6 +8168,7 @@ static void disas_cc(DisasContext *s, uint32_t insn) tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); } } + return true; } /* Conditional select @@ -8266,10 +8245,6 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) } switch (op2) { - case 0x2: /* Conditional compare */ - disas_cc(s, insn); /* both imm and reg forms */ - break; - case 0x4: /* Conditional select */ disas_cond_select(s, insn); break; @@ -8277,6 +8252,7 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) default: do_unallocated: case 0x0: + case 0x2: /* Conditional compare */ case 0x6: /* Data-processing */ case 0x8 ... 0xf: /* (3 source) */ unallocated_encoding(s); From 32f06615732bba12ef32d52b6dd292e57b83dfc6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:46 -0600 Subject: [PATCH 0223/2892] target/arm: Convert disas_cond_select to decodetree This includes CSEL, CSINC, CSINV, CSNEG. Remove disas_data_proc_reg, as these were the last insns decoded by that function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 ++ target/arm/tcg/translate-a64.c | 84 ++++++---------------------------- 2 files changed, 17 insertions(+), 70 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index a9d7d57199..5670846768 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -766,6 +766,9 @@ SETF16 0 01 11010000 00000 010010 rn:5 01101 CCMP sf:1 op:1 1 11010010 y:5 cond:4 imm:1 0 rn:5 0 nzcv:4 # Conditional select + +CSEL sf:1 else_inv:1 011010100 rm:5 cond:4 0 else_inc:1 rn:5 rd:5 + # Data Processing (3-source) &rrrr rd rn rm ra diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 56a445a3c2..9c6365f5ef 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8171,39 +8171,17 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) return true; } -/* Conditional select - * 31 30 29 28 21 20 16 15 12 11 10 9 5 4 0 - * +----+----+---+-----------------+------+------+-----+------+------+ - * | sf | op | S | 1 1 0 1 0 1 0 0 | Rm | cond | op2 | Rn | Rd | - * +----+----+---+-----------------+------+------+-----+------+------+ - */ -static void disas_cond_select(DisasContext *s, uint32_t insn) +static bool trans_CSEL(DisasContext *s, arg_CSEL *a) { - unsigned int sf, else_inv, rm, cond, else_inc, rn, rd; - TCGv_i64 tcg_rd, zero; + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 zero = tcg_constant_i64(0); DisasCompare64 c; - if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) { - /* S == 1 or op2<1> == 1 */ - unallocated_encoding(s); - return; - } - sf = extract32(insn, 31, 1); - else_inv = extract32(insn, 30, 1); - rm = extract32(insn, 16, 5); - cond = extract32(insn, 12, 4); - else_inc = extract32(insn, 10, 1); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); + a64_test_cc(&c, a->cond); - tcg_rd = cpu_reg(s, rd); - - a64_test_cc(&c, cond); - zero = tcg_constant_i64(0); - - if (rn == 31 && rm == 31 && (else_inc ^ else_inv)) { + if (a->rn == 31 && a->rm == 31 && (a->else_inc ^ a->else_inv)) { /* CSET & CSETM. */ - if (else_inv) { + if (a->else_inv) { tcg_gen_negsetcond_i64(tcg_invert_cond(c.cond), tcg_rd, c.value, zero); } else { @@ -8211,53 +8189,23 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) tcg_rd, c.value, zero); } } else { - TCGv_i64 t_true = cpu_reg(s, rn); - TCGv_i64 t_false = read_cpu_reg(s, rm, 1); - if (else_inv && else_inc) { + TCGv_i64 t_true = cpu_reg(s, a->rn); + TCGv_i64 t_false = read_cpu_reg(s, a->rm, 1); + + if (a->else_inv && a->else_inc) { tcg_gen_neg_i64(t_false, t_false); - } else if (else_inv) { + } else if (a->else_inv) { tcg_gen_not_i64(t_false, t_false); - } else if (else_inc) { + } else if (a->else_inc) { tcg_gen_addi_i64(t_false, t_false, 1); } tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); } - if (!sf) { + if (!a->sf) { tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } -} - -/* - * Data processing - register - * 31 30 29 28 25 21 20 16 10 0 - * +--+---+--+---+-------+-----+-------+-------+---------+ - * | |op0| |op1| 1 0 1 | op2 | | op3 | | - * +--+---+--+---+-------+-----+-------+-------+---------+ - */ -static void disas_data_proc_reg(DisasContext *s, uint32_t insn) -{ - int op1 = extract32(insn, 28, 1); - int op2 = extract32(insn, 21, 4); - - if (!op1) { - goto do_unallocated; - } - - switch (op2) { - case 0x4: /* Conditional select */ - disas_cond_select(s, insn); - break; - - default: - do_unallocated: - case 0x0: - case 0x2: /* Conditional compare */ - case 0x6: /* Data-processing */ - case 0x8 ... 0xf: /* (3 source) */ - unallocated_encoding(s); - break; - } + return true; } static void handle_fp_compare(DisasContext *s, int size, @@ -11212,10 +11160,6 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) static void disas_a64_legacy(DisasContext *s, uint32_t insn) { switch (extract32(insn, 25, 4)) { - case 0x5: - case 0xd: /* Data processing - register */ - disas_data_proc_reg(s, insn); - break; case 0x7: case 0xf: /* Data processing - SIMD and floating point */ disas_data_proc_simd_fp(s, insn); From 648e654efa05e21779daf0b1f623948c92c83fe0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:47 -0600 Subject: [PATCH 0224/2892] target/arm: Introduce fp_access_check_scalar_hsd Provide a simple way to check for float64, float32, and float16 support, as well as the fpu enabled. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 62 ++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9c6365f5ef..4e47b8a804 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1239,6 +1239,27 @@ static bool fp_access_check(DisasContext *s) return true; } +/* + * Return <0 for non-supported element sizes, with MO_16 controlled by + * FEAT_FP16; return 0 for fp disabled; otherwise return >0 for success. + */ +static int fp_access_check_scalar_hsd(DisasContext *s, MemOp esz) +{ + switch (esz) { + case MO_64: + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return -1; + } + break; + default: + return -1; + } + return fp_access_check(s); +} + /* * Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. @@ -6628,22 +6649,10 @@ static bool trans_FCSEL(DisasContext *s, arg_FCSEL *a) { TCGv_i64 t_true, t_false; DisasCompare64 c; + int check = fp_access_check_scalar_hsd(s, a->esz); - switch (a->esz) { - case MO_32: - case MO_64: - break; - case MO_16: - if (!dc_isar_feature(aa64_fp16, s)) { - return false; - } - break; - default: - return false; - } - - if (!fp_access_check(s)) { - return true; + if (check <= 0) { + return check == 0; } /* Zero extend sreg & hreg inputs to 64 bits now. */ @@ -6894,22 +6903,15 @@ TRANS(FMINV_s, do_fp_reduction, a, gen_helper_vfp_mins) static bool trans_FMOVI_s(DisasContext *s, arg_FMOVI_s *a) { - switch (a->esz) { - case MO_32: - case MO_64: - break; - case MO_16: - if (!dc_isar_feature(aa64_fp16, s)) { - return false; - } - break; - default: - return false; - } - if (fp_access_check(s)) { - uint64_t imm = vfp_expand_imm(a->esz, a->imm); - write_fp_dreg(s, a->rd, tcg_constant_i64(imm)); + int check = fp_access_check_scalar_hsd(s, a->esz); + uint64_t imm; + + if (check <= 0) { + return check == 0; } + + imm = vfp_expand_imm(a->esz, a->imm); + write_fp_dreg(s, a->rd, tcg_constant_i64(imm)); return true; } From 43218f234692bc4893b082a016885ae650aea8fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:48 -0600 Subject: [PATCH 0225/2892] target/arm: Introduce fp_access_check_vector_hsd Provide a simple way to check for float64, float32, and float16 support vs vector width, as well as the fpu enabled. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 135 +++++++++++++-------------------- 1 file changed, 54 insertions(+), 81 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 4e47b8a804..4611ae4ade 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1260,6 +1260,28 @@ static int fp_access_check_scalar_hsd(DisasContext *s, MemOp esz) return fp_access_check(s); } +/* Likewise, but vector MO_64 must have two elements. */ +static int fp_access_check_vector_hsd(DisasContext *s, bool is_q, MemOp esz) +{ + switch (esz) { + case MO_64: + if (!is_q) { + return -1; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return -1; + } + break; + default: + return -1; + } + return fp_access_check(s); +} + /* * Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. @@ -5420,27 +5442,14 @@ static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, gen_helper_gvec_3_ptr * const fns[3]) { MemOp esz = a->esz; + int check = fp_access_check_vector_hsd(s, a->q, esz); - switch (esz) { - case MO_64: - if (!a->q) { - return false; - } - break; - case MO_32: - break; - case MO_16: - if (!dc_isar_feature(aa64_fp16, s)) { - return false; - } - break; - default: - return false; - } - if (fp_access_check(s)) { - gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, - esz == MO_16, data, fns[esz - 1]); + if (check <= 0) { + return check == 0; } + + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16, data, fns[esz - 1]); return true; } @@ -5768,34 +5777,24 @@ TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1, f_vector_fcadd) static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) { - gen_helper_gvec_4_ptr *fn; + static gen_helper_gvec_4_ptr * const fn[] = { + [MO_16] = gen_helper_gvec_fcmlah, + [MO_32] = gen_helper_gvec_fcmlas, + [MO_64] = gen_helper_gvec_fcmlad, + }; + int check; if (!dc_isar_feature(aa64_fcma, s)) { return false; } - switch (a->esz) { - case MO_64: - if (!a->q) { - return false; - } - fn = gen_helper_gvec_fcmlad; - break; - case MO_32: - fn = gen_helper_gvec_fcmlas; - break; - case MO_16: - if (!dc_isar_feature(aa64_fp16, s)) { - return false; - } - fn = gen_helper_gvec_fcmlah; - break; - default: - return false; - } - if (fp_access_check(s)) { - gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, - a->esz == MO_16, a->rot, fn); + + check = fp_access_check_vector_hsd(s, a->q, a->esz); + if (check <= 0) { + return check == 0; } + + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->esz == MO_16, a->rot, fn[a->esz]); return true; } @@ -6337,27 +6336,14 @@ static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, gen_helper_gvec_3_ptr * const fns[3]) { MemOp esz = a->esz; + int check = fp_access_check_vector_hsd(s, a->q, esz); - switch (esz) { - case MO_64: - if (!a->q) { - return false; - } - break; - case MO_32: - break; - case MO_16: - if (!dc_isar_feature(aa64_fp16, s)) { - return false; - } - break; - default: - g_assert_not_reached(); - } - if (fp_access_check(s)) { - gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, - esz == MO_16, a->idx, fns[esz - 1]); + if (check <= 0) { + return check == 0; } + + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16, a->idx, fns[esz - 1]); return true; } @@ -6383,28 +6369,15 @@ static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) gen_helper_gvec_fmla_idx_d, }; MemOp esz = a->esz; + int check = fp_access_check_vector_hsd(s, a->q, esz); - switch (esz) { - case MO_64: - if (!a->q) { - return false; - } - break; - case MO_32: - break; - case MO_16: - if (!dc_isar_feature(aa64_fp16, s)) { - return false; - } - break; - default: - g_assert_not_reached(); - } - if (fp_access_check(s)) { - gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, - esz == MO_16, (a->idx << 1) | neg, - fns[esz - 1]); + if (check <= 0) { + return check == 0; } + + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + esz == MO_16, (a->idx << 1) | neg, + fns[esz - 1]); return true; } From cc0db9dea0e02373a7440ec6d19a00b0f70bbc9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:49 -0600 Subject: [PATCH 0226/2892] target/arm: Convert FCMP, FCMPE, FCCMP, FCCMPE to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 8 + target/arm/tcg/translate-a64.c | 283 ++++++++++++--------------------- 2 files changed, 112 insertions(+), 179 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5670846768..7868b1cb24 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1325,6 +1325,14 @@ FMINV_s 0110 1110 10 11000 01111 10 ..... ..... @rr_q1e2 FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd +# Floating-point Compare + +FCMP 00011110 .. 1 rm:5 001000 rn:5 e:1 z:1 000 esz=%esz_hsd + +# Floating-point Conditional Compare + +FCCMP 00011110 .. 1 rm:5 cond:4 01 rn:5 e:1 nzcv:4 esz=%esz_hsd + # Advanced SIMD Modified Immediate / Shift by Immediate %abcdefgh 16:3 5:5 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 4611ae4ade..a99f3d0d13 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6888,6 +6888,106 @@ static bool trans_FMOVI_s(DisasContext *s, arg_FMOVI_s *a) return true; } +/* + * Floating point compare, conditional compare + */ + +static void handle_fp_compare(DisasContext *s, int size, + unsigned int rn, unsigned int rm, + bool cmp_with_zero, bool signal_all_nans) +{ + TCGv_i64 tcg_flags = tcg_temp_new_i64(); + TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + + if (size == MO_64) { + TCGv_i64 tcg_vn, tcg_vm; + + tcg_vn = read_fp_dreg(s, rn); + if (cmp_with_zero) { + tcg_vm = tcg_constant_i64(0); + } else { + tcg_vm = read_fp_dreg(s, rm); + } + if (signal_all_nans) { + gen_helper_vfp_cmped_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmpd_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + } else { + TCGv_i32 tcg_vn = tcg_temp_new_i32(); + TCGv_i32 tcg_vm = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_vn, rn, 0, size); + if (cmp_with_zero) { + tcg_gen_movi_i32(tcg_vm, 0); + } else { + read_vec_element_i32(s, tcg_vm, rm, 0, size); + } + + switch (size) { + case MO_32: + if (signal_all_nans) { + gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + case MO_16: + if (signal_all_nans) { + gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + default: + g_assert_not_reached(); + } + } + + gen_set_nzcv(tcg_flags); +} + +/* FCMP, FCMPE */ +static bool trans_FCMP(DisasContext *s, arg_FCMP *a) +{ + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + handle_fp_compare(s, a->esz, a->rn, a->rm, a->z, a->e); + return true; +} + +/* FCCMP, FCCMPE */ +static bool trans_FCCMP(DisasContext *s, arg_FCCMP *a) +{ + TCGLabel *label_continue = NULL; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + if (a->cond < 0x0e) { /* not always */ + TCGLabel *label_match = gen_new_label(); + label_continue = gen_new_label(); + arm_gen_test_cc(a->cond, label_match); + /* nomatch: */ + gen_set_nzcv(tcg_constant_i64(a->nzcv << 28)); + tcg_gen_br(label_continue); + gen_set_label(label_match); + } + + handle_fp_compare(s, a->esz, a->rn, a->rm, false, a->e); + + if (label_continue) { + gen_set_label(label_continue); + } + return true; +} + /* * Advanced SIMD Modified Immediate */ @@ -8183,174 +8283,6 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) return true; } -static void handle_fp_compare(DisasContext *s, int size, - unsigned int rn, unsigned int rm, - bool cmp_with_zero, bool signal_all_nans) -{ - TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - - if (size == MO_64) { - TCGv_i64 tcg_vn, tcg_vm; - - tcg_vn = read_fp_dreg(s, rn); - if (cmp_with_zero) { - tcg_vm = tcg_constant_i64(0); - } else { - tcg_vm = read_fp_dreg(s, rm); - } - if (signal_all_nans) { - gen_helper_vfp_cmped_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmpd_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } - } else { - TCGv_i32 tcg_vn = tcg_temp_new_i32(); - TCGv_i32 tcg_vm = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_vn, rn, 0, size); - if (cmp_with_zero) { - tcg_gen_movi_i32(tcg_vm, 0); - } else { - read_vec_element_i32(s, tcg_vm, rm, 0, size); - } - - switch (size) { - case MO_32: - if (signal_all_nans) { - gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } - break; - case MO_16: - if (signal_all_nans) { - gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } - break; - default: - g_assert_not_reached(); - } - } - - gen_set_nzcv(tcg_flags); -} - -/* Floating point compare - * 31 30 29 28 24 23 22 21 20 16 15 14 13 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+-----+---------+------+-------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | op | 1 0 0 0 | Rn | op2 | - * +---+---+---+-----------+------+---+------+-----+---------+------+-------+ - */ -static void disas_fp_compare(DisasContext *s, uint32_t insn) -{ - unsigned int mos, type, rm, op, rn, opc, op2r; - int size; - - mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); - rm = extract32(insn, 16, 5); - op = extract32(insn, 14, 2); - rn = extract32(insn, 5, 5); - opc = extract32(insn, 3, 2); - op2r = extract32(insn, 0, 3); - - if (mos || op || op2r) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - size = MO_32; - break; - case 1: - size = MO_64; - break; - case 3: - size = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2); -} - -/* Floating point conditional compare - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 - * +---+---+---+-----------+------+---+------+------+-----+------+----+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 0 1 | Rn | op | nzcv | - * +---+---+---+-----------+------+---+------+------+-----+------+----+------+ - */ -static void disas_fp_ccomp(DisasContext *s, uint32_t insn) -{ - unsigned int mos, type, rm, cond, rn, op, nzcv; - TCGLabel *label_continue = NULL; - int size; - - mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); - rm = extract32(insn, 16, 5); - cond = extract32(insn, 12, 4); - rn = extract32(insn, 5, 5); - op = extract32(insn, 4, 1); - nzcv = extract32(insn, 0, 4); - - if (mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - size = MO_32; - break; - case 1: - size = MO_64; - break; - case 3: - size = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (cond < 0x0e) { /* not always */ - TCGLabel *label_match = gen_new_label(); - label_continue = gen_new_label(); - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - gen_set_nzcv(tcg_constant_i64(nzcv << 28)); - tcg_gen_br(label_continue); - gen_set_label(label_match); - } - - handle_fp_compare(s, size, rn, rm, false, op); - - if (cond < 0x0e) { - gen_set_label(label_continue); - } -} - /* Floating-point data-processing (1 source) - half precision */ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) { @@ -9107,16 +9039,9 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn) disas_fp_fixed_conv(s, insn); } else { switch (extract32(insn, 10, 2)) { - case 1: - /* Floating point conditional compare */ - disas_fp_ccomp(s, insn); - break; - case 2: - /* Floating point data-processing (2 source) */ - unallocated_encoding(s); /* in decodetree */ - break; - case 3: - /* Floating point conditional select */ + case 1: /* Floating point conditional compare */ + case 2: /* Floating point data-processing (2 source) */ + case 3: /* Floating point conditional select */ unallocated_encoding(s); /* in decodetree */ break; case 0: @@ -9127,7 +9052,7 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn) break; case 1: /* [15:12] == xx10 */ /* Floating point compare */ - disas_fp_compare(s, insn); + unallocated_encoding(s); /* in decodetree */ break; case 2: /* [15:12] == x100 */ /* Floating point data-processing (1 source) */ From 7261f2a7a75ab9be4aa3f8a7896f21a39df74006 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:50 -0600 Subject: [PATCH 0227/2892] target/arm: Fix decode of fp16 vector fabs, fneg, fsqrt These opcodes are only supported as vector operations, not as advsimd scalar. Set only_in_vector, and remove the unreachable implementation of scalar fneg. Reported-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20241211163036.2297116-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a99f3d0d13..3c1784593a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10816,10 +10816,13 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) break; case 0x2f: /* FABS */ case 0x6f: /* FNEG */ + only_in_vector = true; need_fpst = false; break; case 0x7d: /* FRSQRTE */ + break; case 0x7f: /* FSQRT (vector) */ + only_in_vector = true; break; default: unallocated_encoding(s); @@ -10877,9 +10880,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x7b: /* FCVTZU */ gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); break; - case 0x6f: /* FNEG */ - tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); - break; case 0x7d: /* FRSQRTE */ gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); break; From 5ee3c6c8fd8a8af1c45944d9c9794e9ec233b72e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:51 -0600 Subject: [PATCH 0228/2892] target/arm: Convert FMOV, FABS, FNEG (scalar) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 +++ target/arm/tcg/translate-a64.c | 105 +++++++++++++++++++++++---------- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7868b1cb24..b9cc8963da 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -47,6 +47,7 @@ @rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1 @rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 @rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd +@rr_hsd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_hsd @rrr_b ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=0 @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 @@ -1321,6 +1322,12 @@ FMAXV_s 0110 1110 00 11000 01111 10 ..... ..... @rr_q1e2 FMINV_h 0.00 1110 10 11000 01111 10 ..... ..... @qrr_h FMINV_s 0110 1110 10 11000 01111 10 ..... ..... @rr_q1e2 +# Floating-point data processing (1 source) + +FMOV_s 00011110 .. 1 000000 10000 ..... ..... @rr_hsd +FABS_s 00011110 .. 1 000001 10000 ..... ..... @rr_hsd +FNEG_s 00011110 .. 1 000010 10000 ..... ..... @rr_hsd + # Floating-point Immediate FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3c1784593a..ca2b95510e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8283,6 +8283,67 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) return true; } +typedef struct FPScalar1Int { + void (*gen_h)(TCGv_i32, TCGv_i32); + void (*gen_s)(TCGv_i32, TCGv_i32); + void (*gen_d)(TCGv_i64, TCGv_i64); +} FPScalar1Int; + +static bool do_fp1_scalar_int(DisasContext *s, arg_rr_e *a, + const FPScalar1Int *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + f->gen_d(t, t); + write_fp_dreg(s, a->rd, t); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t = read_fp_sreg(s, a->rn); + f->gen_s(t, t); + write_fp_sreg(s, a->rd, t); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t = read_fp_hreg(s, a->rn); + f->gen_h(t, t); + write_fp_sreg(s, a->rd, t); + } + break; + default: + return false; + } + return true; +} + +static const FPScalar1Int f_scalar_fmov = { + tcg_gen_mov_i32, + tcg_gen_mov_i32, + tcg_gen_mov_i64, +}; +TRANS(FMOV_s, do_fp1_scalar_int, a, &f_scalar_fmov) + +static const FPScalar1Int f_scalar_fabs = { + gen_vfp_absh, + gen_vfp_abss, + gen_vfp_absd, +}; +TRANS(FABS_s, do_fp1_scalar_int, a, &f_scalar_fabs) + +static const FPScalar1Int f_scalar_fneg = { + gen_vfp_negh, + gen_vfp_negs, + gen_vfp_negd, +}; +TRANS(FNEG_s, do_fp1_scalar_int, a, &f_scalar_fneg) + /* Floating-point data-processing (1 source) - half precision */ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) { @@ -8291,15 +8352,6 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) TCGv_i32 tcg_res = tcg_temp_new_i32(); switch (opcode) { - case 0x0: /* FMOV */ - tcg_gen_mov_i32(tcg_res, tcg_op); - break; - case 0x1: /* FABS */ - gen_vfp_absh(tcg_res, tcg_op); - break; - case 0x2: /* FNEG */ - gen_vfp_negh(tcg_res, tcg_op); - break; case 0x3: /* FSQRT */ fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_sqrt_f16(tcg_res, tcg_op, fpst); @@ -8327,6 +8379,9 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); break; default: + case 0x0: /* FMOV */ + case 0x1: /* FABS */ + case 0x2: /* FNEG */ g_assert_not_reached(); } @@ -8345,15 +8400,6 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) tcg_res = tcg_temp_new_i32(); switch (opcode) { - case 0x0: /* FMOV */ - tcg_gen_mov_i32(tcg_res, tcg_op); - goto done; - case 0x1: /* FABS */ - gen_vfp_abss(tcg_res, tcg_op); - goto done; - case 0x2: /* FNEG */ - gen_vfp_negs(tcg_res, tcg_op); - goto done; case 0x3: /* FSQRT */ gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); goto done; @@ -8389,6 +8435,9 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) gen_fpst = gen_helper_frint64_s; break; default: + case 0x0: /* FMOV */ + case 0x1: /* FABS */ + case 0x2: /* FNEG */ g_assert_not_reached(); } @@ -8413,22 +8462,10 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) TCGv_ptr fpst; int rmode = -1; - switch (opcode) { - case 0x0: /* FMOV */ - gen_gvec_fn2(s, false, rd, rn, tcg_gen_gvec_mov, 0); - return; - } - tcg_op = read_fp_dreg(s, rn); tcg_res = tcg_temp_new_i64(); switch (opcode) { - case 0x1: /* FABS */ - gen_vfp_absd(tcg_res, tcg_op); - goto done; - case 0x2: /* FNEG */ - gen_vfp_negd(tcg_res, tcg_op); - goto done; case 0x3: /* FSQRT */ gen_helper_vfp_sqrtd(tcg_res, tcg_op, tcg_env); goto done; @@ -8461,6 +8498,9 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) gen_fpst = gen_helper_frint64_d; break; default: + case 0x0: /* FMOV */ + case 0x1: /* FABS */ + case 0x2: /* FNEG */ g_assert_not_reached(); } @@ -8581,7 +8621,7 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } /* fall through */ - case 0x0 ... 0x3: + case 0x3: case 0x8 ... 0xc: case 0xe ... 0xf: /* 32-to-32 and 64-to-64 ops */ @@ -8631,6 +8671,9 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) default: do_unallocated: + case 0x0: /* FMOV */ + case 0x1: /* FABS */ + case 0x2: /* FNEG */ unallocated_encoding(s); break; } From cf866d81b86633bda2d60da9eca33dbeb07a78af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:52 -0600 Subject: [PATCH 0229/2892] target/arm: Pass fpstatus to vfp_sqrt* Pass fpstatus not env, like most other fp helpers. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 6 +++--- target/arm/tcg/translate-a64.c | 15 +++++++-------- target/arm/tcg/translate-vfp.c | 6 +++--- target/arm/vfp_helper.c | 12 ++++++------ 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 58919b670e..0a697e752b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -133,9 +133,9 @@ DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, ptr) DEF_HELPER_3(vfp_minnumh, f16, f16, f16, ptr) DEF_HELPER_3(vfp_minnums, f32, f32, f32, ptr) DEF_HELPER_3(vfp_minnumd, f64, f64, f64, ptr) -DEF_HELPER_2(vfp_sqrth, f16, f16, env) -DEF_HELPER_2(vfp_sqrts, f32, f32, env) -DEF_HELPER_2(vfp_sqrtd, f64, f64, env) +DEF_HELPER_2(vfp_sqrth, f16, f16, ptr) +DEF_HELPER_2(vfp_sqrts, f32, f32, ptr) +DEF_HELPER_2(vfp_sqrtd, f64, f64, ptr) DEF_HELPER_3(vfp_cmph, void, f16, f16, env) DEF_HELPER_3(vfp_cmps, void, f32, f32, env) DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ca2b95510e..cfc73b8506 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8401,8 +8401,8 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) switch (opcode) { case 0x3: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); - goto done; + gen_fpst = gen_helper_vfp_sqrts; + break; case 0x6: /* BFCVT */ gen_fpst = gen_helper_bfcvt; break; @@ -8450,7 +8450,6 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) gen_fpst(tcg_res, tcg_op, fpst); } - done: write_fp_sreg(s, rd, tcg_res); } @@ -8467,8 +8466,8 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) switch (opcode) { case 0x3: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_res, tcg_op, tcg_env); - goto done; + gen_fpst = gen_helper_vfp_sqrtd; + break; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ @@ -8513,7 +8512,6 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) gen_fpst(tcg_res, tcg_op, fpst); } - done: write_fp_dreg(s, rd, tcg_res); } @@ -9459,7 +9457,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, gen_vfp_negd(tcg_rd, tcg_rn); break; case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_env); + gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_fpstatus); break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ @@ -10402,6 +10400,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) handle_2misc_fcmp_zero(s, opcode, false, u, is_q, size, rn, rd); return; case 0x7f: /* FSQRT */ + need_fpstatus = true; if (size == 3 && !is_q) { unallocated_encoding(s); return; @@ -10631,7 +10630,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_vfp_negs(tcg_res, tcg_op); break; case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); + gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_fpstatus); break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index b6fa28a7bf..c160a86e70 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -2424,17 +2424,17 @@ DO_VFP_2OP(VNEG, dp, gen_vfp_negd, aa32_fpdp_v2) static void gen_VSQRT_hp(TCGv_i32 vd, TCGv_i32 vm) { - gen_helper_vfp_sqrth(vd, vm, tcg_env); + gen_helper_vfp_sqrth(vd, vm, fpstatus_ptr(FPST_FPCR_F16)); } static void gen_VSQRT_sp(TCGv_i32 vd, TCGv_i32 vm) { - gen_helper_vfp_sqrts(vd, vm, tcg_env); + gen_helper_vfp_sqrts(vd, vm, fpstatus_ptr(FPST_FPCR)); } static void gen_VSQRT_dp(TCGv_i64 vd, TCGv_i64 vm) { - gen_helper_vfp_sqrtd(vd, vm, tcg_env); + gen_helper_vfp_sqrtd(vd, vm, fpstatus_ptr(FPST_FPCR)); } DO_VFP_2OP(VSQRT, hp, gen_VSQRT_hp, aa32_fp16_arith) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 62638d2b1f..f24992c798 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -314,19 +314,19 @@ VFP_BINOP(minnum) VFP_BINOP(maxnum) #undef VFP_BINOP -dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, CPUARMState *env) +dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, void *fpstp) { - return float16_sqrt(a, &env->vfp.fp_status_f16); + return float16_sqrt(a, fpstp); } -float32 VFP_HELPER(sqrt, s)(float32 a, CPUARMState *env) +float32 VFP_HELPER(sqrt, s)(float32 a, void *fpstp) { - return float32_sqrt(a, &env->vfp.fp_status); + return float32_sqrt(a, fpstp); } -float64 VFP_HELPER(sqrt, d)(float64 a, CPUARMState *env) +float64 VFP_HELPER(sqrt, d)(float64 a, void *fpstp) { - return float64_sqrt(a, &env->vfp.fp_status); + return float64_sqrt(a, fpstp); } static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) From 62b85fd044a71f45a5620e1de11b2b17579ce69f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:53 -0600 Subject: [PATCH 0230/2892] target/arm: Remove helper_sqrt_f16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is identical with helper_vfp_sqrth. Replace all uses. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-27-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 11 ----------- target/arm/tcg/helper-a64.h | 1 - target/arm/tcg/translate-a64.c | 4 ++-- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 8f42a28d07..3f4d7b9aba 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -915,17 +915,6 @@ illegal_return: "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); } -/* - * Square Root and Reciprocal square root - */ - -uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) -{ - float_status *s = fpstp; - - return float16_sqrt(a, s); -} - void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) { uintptr_t ra = GETPC(); diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 481007bf39..203b7b7ac8 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -80,7 +80,6 @@ DEF_HELPER_2(advsimd_rinth_exact, f16, f16, ptr) DEF_HELPER_2(advsimd_rinth, f16, f16, ptr) DEF_HELPER_2(advsimd_f16tosinth, i32, f16, ptr) DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr) -DEF_HELPER_2(sqrt_f16, f16, f16, ptr) DEF_HELPER_2(exception_return, void, env, i64) DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index cfc73b8506..2a5cb70475 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8354,7 +8354,7 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) switch (opcode) { case 0x3: /* FSQRT */ fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_sqrt_f16(tcg_res, tcg_op, fpst); + gen_helper_vfp_sqrth(tcg_res, tcg_op, fpst); break; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ @@ -10978,7 +10978,7 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); break; case 0x7f: /* FSQRT */ - gen_helper_sqrt_f16(tcg_res, tcg_op, tcg_fpstatus); + gen_helper_vfp_sqrth(tcg_res, tcg_op, tcg_fpstatus); break; default: g_assert_not_reached(); From dbc739724aceb300c62fbe3716efceb16f392ce5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:54 -0600 Subject: [PATCH 0231/2892] target/arm: Convert FSQRT (scalar) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-28-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 1 + target/arm/tcg/translate-a64.c | 72 ++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index b9cc8963da..3b1e8e0776 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1327,6 +1327,7 @@ FMINV_s 0110 1110 10 11000 01111 10 ..... ..... @rr_q1e2 FMOV_s 00011110 .. 1 000000 10000 ..... ..... @rr_hsd FABS_s 00011110 .. 1 000001 10000 ..... ..... @rr_hsd FNEG_s 00011110 .. 1 000010 10000 ..... ..... @rr_hsd +FSQRT_s 00011110 .. 1 000011 10000 ..... ..... @rr_hsd # Floating-point Immediate diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2a5cb70475..f3989246f9 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8344,6 +8344,63 @@ static const FPScalar1Int f_scalar_fneg = { }; TRANS(FNEG_s, do_fp1_scalar_int, a, &f_scalar_fneg) +typedef struct FPScalar1 { + void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_s)(TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_ptr); +} FPScalar1; + +static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode) +{ + TCGv_i32 tcg_rmode = NULL; + TCGv_ptr fpst; + TCGv_i64 t64; + TCGv_i32 t32; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, fpst); + } + + switch (a->esz) { + case MO_64: + t64 = read_fp_dreg(s, a->rn); + f->gen_d(t64, t64, fpst); + write_fp_dreg(s, a->rd, t64); + break; + case MO_32: + t32 = read_fp_sreg(s, a->rn); + f->gen_s(t32, t32, fpst); + write_fp_sreg(s, a->rd, t32); + break; + case MO_16: + t32 = read_fp_hreg(s, a->rn); + f->gen_h(t32, t32, fpst); + write_fp_sreg(s, a->rd, t32); + break; + default: + g_assert_not_reached(); + } + + if (rmode >= 0) { + gen_restore_rmode(tcg_rmode, fpst); + } + return true; +} + +static const FPScalar1 f_scalar_fsqrt = { + gen_helper_vfp_sqrth, + gen_helper_vfp_sqrts, + gen_helper_vfp_sqrtd, +}; +TRANS(FSQRT_s, do_fp1_scalar, a, &f_scalar_fsqrt, -1) + /* Floating-point data-processing (1 source) - half precision */ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) { @@ -8352,10 +8409,6 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) TCGv_i32 tcg_res = tcg_temp_new_i32(); switch (opcode) { - case 0x3: /* FSQRT */ - fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_vfp_sqrth(tcg_res, tcg_op, fpst); - break; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ @@ -8382,6 +8435,7 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) case 0x0: /* FMOV */ case 0x1: /* FABS */ case 0x2: /* FNEG */ + case 0x3: /* FSQRT */ g_assert_not_reached(); } @@ -8400,9 +8454,6 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) tcg_res = tcg_temp_new_i32(); switch (opcode) { - case 0x3: /* FSQRT */ - gen_fpst = gen_helper_vfp_sqrts; - break; case 0x6: /* BFCVT */ gen_fpst = gen_helper_bfcvt; break; @@ -8438,6 +8489,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) case 0x0: /* FMOV */ case 0x1: /* FABS */ case 0x2: /* FNEG */ + case 0x3: /* FSQRT */ g_assert_not_reached(); } @@ -8465,9 +8517,6 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) tcg_res = tcg_temp_new_i64(); switch (opcode) { - case 0x3: /* FSQRT */ - gen_fpst = gen_helper_vfp_sqrtd; - break; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ @@ -8500,6 +8549,7 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) case 0x0: /* FMOV */ case 0x1: /* FABS */ case 0x2: /* FNEG */ + case 0x3: /* FSQRT */ g_assert_not_reached(); } @@ -8619,7 +8669,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } /* fall through */ - case 0x3: case 0x8 ... 0xc: case 0xe ... 0xf: /* 32-to-32 and 64-to-64 ops */ @@ -8672,6 +8721,7 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) case 0x0: /* FMOV */ case 0x1: /* FABS */ case 0x2: /* FNEG */ + case 0x3: /* FSQRT */ unallocated_encoding(s); break; } From cf85790bb82665887203b9e34f865158c130b27b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:55 -0600 Subject: [PATCH 0232/2892] target/arm: Convert FRINT[NPMSAXI] (scalar) to decodetree Remove handle_fp_1src_half as these were the last insns decoded by that function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-29-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 8 +++ target/arm/tcg/translate-a64.c | 117 +++++++++++---------------------- 2 files changed, 46 insertions(+), 79 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 3b1e8e0776..9d2f099c9c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1329,6 +1329,14 @@ FABS_s 00011110 .. 1 000001 10000 ..... ..... @rr_hsd FNEG_s 00011110 .. 1 000010 10000 ..... ..... @rr_hsd FSQRT_s 00011110 .. 1 000011 10000 ..... ..... @rr_hsd +FRINTN_s 00011110 .. 1 001000 10000 ..... ..... @rr_hsd +FRINTP_s 00011110 .. 1 001001 10000 ..... ..... @rr_hsd +FRINTM_s 00011110 .. 1 001010 10000 ..... ..... @rr_hsd +FRINTZ_s 00011110 .. 1 001011 10000 ..... ..... @rr_hsd +FRINTA_s 00011110 .. 1 001100 10000 ..... ..... @rr_hsd +FRINTX_s 00011110 .. 1 001110 10000 ..... ..... @rr_hsd +FRINTI_s 00011110 .. 1 001111 10000 ..... ..... @rr_hsd + # Floating-point Immediate FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f3989246f9..5a347bece3 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8401,46 +8401,24 @@ static const FPScalar1 f_scalar_fsqrt = { }; TRANS(FSQRT_s, do_fp1_scalar, a, &f_scalar_fsqrt, -1) -/* Floating-point data-processing (1 source) - half precision */ -static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) -{ - TCGv_ptr fpst = NULL; - TCGv_i32 tcg_op = read_fp_hreg(s, rn); - TCGv_i32 tcg_res = tcg_temp_new_i32(); +static const FPScalar1 f_scalar_frint = { + gen_helper_advsimd_rinth, + gen_helper_rints, + gen_helper_rintd, +}; +TRANS(FRINTN_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_TIEEVEN) +TRANS(FRINTP_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_POSINF) +TRANS(FRINTM_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_NEGINF) +TRANS(FRINTZ_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_ZERO) +TRANS(FRINTA_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_TIEAWAY) +TRANS(FRINTI_s, do_fp1_scalar, a, &f_scalar_frint, -1) - switch (opcode) { - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - { - TCGv_i32 tcg_rmode; - - fpst = fpstatus_ptr(FPST_FPCR_F16); - tcg_rmode = gen_set_rmode(opcode & 7, fpst); - gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); - gen_restore_rmode(tcg_rmode, fpst); - break; - } - case 0xe: /* FRINTX */ - fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, fpst); - break; - case 0xf: /* FRINTI */ - fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); - break; - default: - case 0x0: /* FMOV */ - case 0x1: /* FABS */ - case 0x2: /* FNEG */ - case 0x3: /* FSQRT */ - g_assert_not_reached(); - } - - write_fp_sreg(s, rd, tcg_res); -} +static const FPScalar1 f_scalar_frintx = { + gen_helper_advsimd_rinth_exact, + gen_helper_rints_exact, + gen_helper_rintd_exact, +}; +TRANS(FRINTX_s, do_fp1_scalar, a, &f_scalar_frintx, -1) /* Floating-point data-processing (1 source) - single precision */ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) @@ -8457,20 +8435,6 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) case 0x6: /* BFCVT */ gen_fpst = gen_helper_bfcvt; break; - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - rmode = opcode & 7; - gen_fpst = gen_helper_rints; - break; - case 0xe: /* FRINTX */ - gen_fpst = gen_helper_rints_exact; - break; - case 0xf: /* FRINTI */ - gen_fpst = gen_helper_rints; - break; case 0x10: /* FRINT32Z */ rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint32_s; @@ -8490,6 +8454,13 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) case 0x1: /* FABS */ case 0x2: /* FNEG */ case 0x3: /* FSQRT */ + case 0x8: /* FRINTN */ + case 0x9: /* FRINTP */ + case 0xa: /* FRINTM */ + case 0xb: /* FRINTZ */ + case 0xc: /* FRINTA */ + case 0xe: /* FRINTX */ + case 0xf: /* FRINTI */ g_assert_not_reached(); } @@ -8517,20 +8488,6 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) tcg_res = tcg_temp_new_i64(); switch (opcode) { - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - rmode = opcode & 7; - gen_fpst = gen_helper_rintd; - break; - case 0xe: /* FRINTX */ - gen_fpst = gen_helper_rintd_exact; - break; - case 0xf: /* FRINTI */ - gen_fpst = gen_helper_rintd; - break; case 0x10: /* FRINT32Z */ rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint32_d; @@ -8550,6 +8507,13 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) case 0x1: /* FABS */ case 0x2: /* FNEG */ case 0x3: /* FSQRT */ + case 0x8: /* FRINTN */ + case 0x9: /* FRINTP */ + case 0xa: /* FRINTM */ + case 0xb: /* FRINTZ */ + case 0xc: /* FRINTA */ + case 0xe: /* FRINTX */ + case 0xf: /* FRINTI */ g_assert_not_reached(); } @@ -8668,9 +8632,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) if (type > 1 || !dc_isar_feature(aa64_frint, s)) { goto do_unallocated; } - /* fall through */ - case 0x8 ... 0xc: - case 0xe ... 0xf: /* 32-to-32 and 64-to-64 ops */ switch (type) { case 0: @@ -8686,15 +8647,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) handle_fp_1src_double(s, opcode, rd, rn); break; case 3: - if (!dc_isar_feature(aa64_fp16, s)) { - goto do_unallocated; - } - - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_half(s, opcode, rd, rn); - break; default: goto do_unallocated; } @@ -8722,6 +8674,13 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) case 0x1: /* FABS */ case 0x2: /* FNEG */ case 0x3: /* FSQRT */ + case 0x8: /* FRINTN */ + case 0x9: /* FRINTP */ + case 0xa: /* FRINTM */ + case 0xb: /* FRINTZ */ + case 0xc: /* FRINTA */ + case 0xe: /* FRINTX */ + case 0xf: /* FRINTI */ unallocated_encoding(s); break; } From a55df233989a8493fcc946384a4a5109067e6c25 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:56 -0600 Subject: [PATCH 0233/2892] target/arm: Convert BFCVT to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 +++ target/arm/tcg/translate-a64.c | 26 +++++++------------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 9d2f099c9c..4a48fcff1d 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -45,6 +45,7 @@ &qrrrr_e q rd rn rm ra esz @rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1 +@rr_s ........ ... ..... ...... rn:5 rd:5 &rr_e esz=2 @rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 @rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd @rr_hsd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_hsd @@ -1337,6 +1338,8 @@ FRINTA_s 00011110 .. 1 001100 10000 ..... ..... @rr_hsd FRINTX_s 00011110 .. 1 001110 10000 ..... ..... @rr_hsd FRINTI_s 00011110 .. 1 001111 10000 ..... ..... @rr_hsd +BFCVT_s 00011110 01 1 000110 10000 ..... ..... @rr_s + # Floating-point Immediate FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5a347bece3..5b30b4caca 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8420,6 +8420,11 @@ static const FPScalar1 f_scalar_frintx = { }; TRANS(FRINTX_s, do_fp1_scalar, a, &f_scalar_frintx, -1) +static const FPScalar1 f_scalar_bfcvt = { + .gen_s = gen_helper_bfcvt, +}; +TRANS_FEAT(BFCVT_s, aa64_bf16, do_fp1_scalar, a, &f_scalar_bfcvt, -1) + /* Floating-point data-processing (1 source) - single precision */ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) { @@ -8432,9 +8437,6 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) tcg_res = tcg_temp_new_i32(); switch (opcode) { - case 0x6: /* BFCVT */ - gen_fpst = gen_helper_bfcvt; - break; case 0x10: /* FRINT32Z */ rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint32_s; @@ -8454,6 +8456,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) case 0x1: /* FABS */ case 0x2: /* FNEG */ case 0x3: /* FSQRT */ + case 0x6: /* BFCVT */ case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ @@ -8652,28 +8655,13 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) } break; - case 0x6: - switch (type) { - case 1: /* BFCVT */ - if (!dc_isar_feature(aa64_bf16, s)) { - goto do_unallocated; - } - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_single(s, opcode, rd, rn); - break; - default: - goto do_unallocated; - } - break; - default: do_unallocated: case 0x0: /* FMOV */ case 0x1: /* FABS */ case 0x2: /* FNEG */ case 0x3: /* FSQRT */ + case 0x6: /* BFCVT */ case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ From 9982ccec41cda638adb75367523c359c621928ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:57 -0600 Subject: [PATCH 0234/2892] target/arm: Convert FRINT{32, 64}[ZX] (scalar) to decodetree Remove handle_fp_1src_single and handle_fp_1src_double as these were the last insns decoded by those functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-31-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 ++ target/arm/tcg/translate-a64.c | 146 ++++----------------------------- 2 files changed, 22 insertions(+), 129 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 4a48fcff1d..4f7b3ee3d9 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1340,6 +1340,11 @@ FRINTI_s 00011110 .. 1 001111 10000 ..... ..... @rr_hsd BFCVT_s 00011110 01 1 000110 10000 ..... ..... @rr_s +FRINT32Z_s 00011110 0. 1 010000 10000 ..... ..... @rr_sd +FRINT32X_s 00011110 0. 1 010001 10000 ..... ..... @rr_sd +FRINT64Z_s 00011110 0. 1 010010 10000 ..... ..... @rr_sd +FRINT64X_s 00011110 0. 1 010011 10000 ..... ..... @rr_sd + # Floating-point Immediate FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5b30b4caca..e48dd308fc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8425,112 +8425,23 @@ static const FPScalar1 f_scalar_bfcvt = { }; TRANS_FEAT(BFCVT_s, aa64_bf16, do_fp1_scalar, a, &f_scalar_bfcvt, -1) -/* Floating-point data-processing (1 source) - single precision */ -static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) -{ - void (*gen_fpst)(TCGv_i32, TCGv_i32, TCGv_ptr); - TCGv_i32 tcg_op, tcg_res; - TCGv_ptr fpst; - int rmode = -1; +static const FPScalar1 f_scalar_frint32 = { + NULL, + gen_helper_frint32_s, + gen_helper_frint32_d, +}; +TRANS_FEAT(FRINT32Z_s, aa64_frint, do_fp1_scalar, a, + &f_scalar_frint32, FPROUNDING_ZERO) +TRANS_FEAT(FRINT32X_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint32, -1) - tcg_op = read_fp_sreg(s, rn); - tcg_res = tcg_temp_new_i32(); - - switch (opcode) { - case 0x10: /* FRINT32Z */ - rmode = FPROUNDING_ZERO; - gen_fpst = gen_helper_frint32_s; - break; - case 0x11: /* FRINT32X */ - gen_fpst = gen_helper_frint32_s; - break; - case 0x12: /* FRINT64Z */ - rmode = FPROUNDING_ZERO; - gen_fpst = gen_helper_frint64_s; - break; - case 0x13: /* FRINT64X */ - gen_fpst = gen_helper_frint64_s; - break; - default: - case 0x0: /* FMOV */ - case 0x1: /* FABS */ - case 0x2: /* FNEG */ - case 0x3: /* FSQRT */ - case 0x6: /* BFCVT */ - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - case 0xe: /* FRINTX */ - case 0xf: /* FRINTI */ - g_assert_not_reached(); - } - - fpst = fpstatus_ptr(FPST_FPCR); - if (rmode >= 0) { - TCGv_i32 tcg_rmode = gen_set_rmode(rmode, fpst); - gen_fpst(tcg_res, tcg_op, fpst); - gen_restore_rmode(tcg_rmode, fpst); - } else { - gen_fpst(tcg_res, tcg_op, fpst); - } - - write_fp_sreg(s, rd, tcg_res); -} - -/* Floating-point data-processing (1 source) - double precision */ -static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) -{ - void (*gen_fpst)(TCGv_i64, TCGv_i64, TCGv_ptr); - TCGv_i64 tcg_op, tcg_res; - TCGv_ptr fpst; - int rmode = -1; - - tcg_op = read_fp_dreg(s, rn); - tcg_res = tcg_temp_new_i64(); - - switch (opcode) { - case 0x10: /* FRINT32Z */ - rmode = FPROUNDING_ZERO; - gen_fpst = gen_helper_frint32_d; - break; - case 0x11: /* FRINT32X */ - gen_fpst = gen_helper_frint32_d; - break; - case 0x12: /* FRINT64Z */ - rmode = FPROUNDING_ZERO; - gen_fpst = gen_helper_frint64_d; - break; - case 0x13: /* FRINT64X */ - gen_fpst = gen_helper_frint64_d; - break; - default: - case 0x0: /* FMOV */ - case 0x1: /* FABS */ - case 0x2: /* FNEG */ - case 0x3: /* FSQRT */ - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - case 0xe: /* FRINTX */ - case 0xf: /* FRINTI */ - g_assert_not_reached(); - } - - fpst = fpstatus_ptr(FPST_FPCR); - if (rmode >= 0) { - TCGv_i32 tcg_rmode = gen_set_rmode(rmode, fpst); - gen_fpst(tcg_res, tcg_op, fpst); - gen_restore_rmode(tcg_rmode, fpst); - } else { - gen_fpst(tcg_res, tcg_op, fpst); - } - - write_fp_dreg(s, rd, tcg_res); -} +static const FPScalar1 f_scalar_frint64 = { + NULL, + gen_helper_frint64_s, + gen_helper_frint64_d, +}; +TRANS_FEAT(FRINT64Z_s, aa64_frint, do_fp1_scalar, a, + &f_scalar_frint64, FPROUNDING_ZERO) +TRANS_FEAT(FRINT64X_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint64, -1) static void handle_fp_fcvt(DisasContext *s, int opcode, int rd, int rn, int dtype, int ntype) @@ -8631,30 +8542,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) break; } - case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */ - if (type > 1 || !dc_isar_feature(aa64_frint, s)) { - goto do_unallocated; - } - /* 32-to-32 and 64-to-64 ops */ - switch (type) { - case 0: - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_single(s, opcode, rd, rn); - break; - case 1: - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_double(s, opcode, rd, rn); - break; - case 3: - default: - goto do_unallocated; - } - break; - default: do_unallocated: case 0x0: /* FMOV */ @@ -8669,6 +8556,7 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) case 0xc: /* FRINTA */ case 0xe: /* FRINTX */ case 0xf: /* FRINTI */ + case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */ unallocated_encoding(s); break; } From 7b0f8dc1ec6b554300c85a835100e615d7ce070b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:58 -0600 Subject: [PATCH 0235/2892] target/arm: Convert FCVT (scalar) to decodetree Remove handle_fp_fcvt and disas_fp_1src as these were the last insns decoded by those functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-32-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 ++ target/arm/tcg/translate-a64.c | 172 +++++++++++++-------------------- 2 files changed, 74 insertions(+), 105 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 4f7b3ee3d9..211346c4d9 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1345,6 +1345,13 @@ FRINT32X_s 00011110 0. 1 010001 10000 ..... ..... @rr_sd FRINT64Z_s 00011110 0. 1 010010 10000 ..... ..... @rr_sd FRINT64X_s 00011110 0. 1 010011 10000 ..... ..... @rr_sd +FCVT_s_ds 00011110 00 1 000101 10000 ..... ..... @rr +FCVT_s_hs 00011110 00 1 000111 10000 ..... ..... @rr +FCVT_s_sd 00011110 01 1 000100 10000 ..... ..... @rr +FCVT_s_hd 00011110 01 1 000111 10000 ..... ..... @rr +FCVT_s_sh 00011110 11 1 000100 10000 ..... ..... @rr +FCVT_s_dh 00011110 11 1 000101 10000 ..... ..... @rr + # Floating-point Immediate FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index e48dd308fc..b31a6d4dff 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8443,123 +8443,85 @@ TRANS_FEAT(FRINT64Z_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint64, FPROUNDING_ZERO) TRANS_FEAT(FRINT64X_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint64, -1) -static void handle_fp_fcvt(DisasContext *s, int opcode, - int rd, int rn, int dtype, int ntype) +static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) { - switch (ntype) { - case 0x0: - { - TCGv_i32 tcg_rn = read_fp_sreg(s, rn); - if (dtype == 1) { - /* Single to double */ - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, tcg_env); - write_fp_dreg(s, rd, tcg_rd); - } else { - /* Single to half */ - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - TCGv_i32 ahp = get_ahp_flag(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + if (fp_access_check(s)) { + TCGv_i32 tcg_rn = read_fp_sreg(s, a->rn); + TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, fpst, ahp); - /* write_fp_sreg is OK here because top half of tcg_rd is zero */ - write_fp_sreg(s, rd, tcg_rd); - } - break; - } - case 0x1: - { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - if (dtype == 0) { - /* Double to single */ - gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, tcg_env); - } else { - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 ahp = get_ahp_flag(); - /* Double to half */ - gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); - /* write_fp_sreg is OK here because top half of tcg_rd is zero */ - } - write_fp_sreg(s, rd, tcg_rd); - break; - } - case 0x3: - { - TCGv_i32 tcg_rn = read_fp_sreg(s, rn); - TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 tcg_ahp = get_ahp_flag(); - tcg_gen_ext16u_i32(tcg_rn, tcg_rn); - if (dtype == 0) { - /* Half to single */ - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); - write_fp_sreg(s, rd, tcg_rd); - } else { - /* Half to double */ - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); - write_fp_dreg(s, rd, tcg_rd); - } - break; - } - default: - g_assert_not_reached(); + gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, tcg_env); + write_fp_dreg(s, a->rd, tcg_rd); } + return true; } -/* Floating point data-processing (1 source) - * 31 30 29 28 24 23 22 21 20 15 14 10 9 5 4 0 - * +---+---+---+-----------+------+---+--------+-----------+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | opcode | 1 0 0 0 0 | Rn | Rd | - * +---+---+---+-----------+------+---+--------+-----------+------+------+ - */ -static void disas_fp_1src(DisasContext *s, uint32_t insn) +static bool trans_FCVT_s_hs(DisasContext *s, arg_rr *a) { - int mos = extract32(insn, 29, 3); - int type = extract32(insn, 22, 2); - int opcode = extract32(insn, 15, 6); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); + if (fp_access_check(s)) { + TCGv_i32 tmp = read_fp_sreg(s, a->rn); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - if (mos) { - goto do_unallocated; + gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); + /* write_fp_sreg is OK here because top half of result is zero */ + write_fp_sreg(s, a->rd, tmp); } + return true; +} - switch (opcode) { - case 0x4: case 0x5: case 0x7: - { - /* FCVT between half, single and double precision */ - int dtype = extract32(opcode, 0, 2); - if (type == 2 || dtype == type) { - goto do_unallocated; - } - if (!fp_access_check(s)) { - return; - } +static bool trans_FCVT_s_sd(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); - handle_fp_fcvt(s, opcode, rd, rn, dtype, type); - break; + gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, tcg_env); + write_fp_sreg(s, a->rd, tcg_rd); } + return true; +} - default: - do_unallocated: - case 0x0: /* FMOV */ - case 0x1: /* FABS */ - case 0x2: /* FNEG */ - case 0x3: /* FSQRT */ - case 0x6: /* BFCVT */ - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - case 0xe: /* FRINTX */ - case 0xf: /* FRINTI */ - case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */ - unallocated_encoding(s); - break; +static bool trans_FCVT_s_hd(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + + gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); + /* write_fp_sreg is OK here because top half of tcg_rd is zero */ + write_fp_sreg(s, a->rd, tcg_rd); } + return true; +} + +static bool trans_FCVT_s_sh(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 tcg_ahp = get_ahp_flag(); + + gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); + write_fp_sreg(s, a->rd, tcg_rd); + } + return true; +} + +static bool trans_FCVT_s_dh(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); + TCGv_i64 tcg_rd = tcg_temp_new_i64(); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 tcg_ahp = get_ahp_flag(); + + gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); + write_fp_dreg(s, a->rd, tcg_rd); + } + return true; } /* Handle floating point <=> fixed point conversions. Note that we can @@ -8982,7 +8944,7 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn) break; case 2: /* [15:12] == x100 */ /* Floating point data-processing (1 source) */ - disas_fp_1src(s, insn); + unallocated_encoding(s); /* in decodetree */ break; case 3: /* [15:12] == 1000 */ unallocated_encoding(s); From f568134a57719d22a96163d899614460a89f451b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:29:59 -0600 Subject: [PATCH 0236/2892] target/arm: Convert handle_fpfpcvt to decodetree This includes SCVTF, UCVTF, FCVT{N,P,M,Z,A}{S,U}. Remove disas_fp_fixed_conv as those were the last insns decoded by that function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-33-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 40 ++++ target/arm/tcg/translate-a64.c | 391 ++++++++++++++------------------- 2 files changed, 209 insertions(+), 222 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 211346c4d9..5e170cec7a 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1323,6 +1323,46 @@ FMAXV_s 0110 1110 00 11000 01111 10 ..... ..... @rr_q1e2 FMINV_h 0.00 1110 10 11000 01111 10 ..... ..... @qrr_h FMINV_s 0110 1110 10 11000 01111 10 ..... ..... @rr_q1e2 +# Conversion between floating-point and fixed-point (general register) + +&fcvt rd rn esz sf shift +%fcvt_shift32 10:5 !function=rsub_32 +%fcvt_shift64 10:6 !function=rsub_64 + +@fcvt32 0 ....... .. ...... 1..... rn:5 rd:5 \ + &fcvt sf=0 esz=%esz_hsd shift=%fcvt_shift32 +@fcvt64 1 ....... .. ...... ...... rn:5 rd:5 \ + &fcvt sf=1 esz=%esz_hsd shift=%fcvt_shift64 + +SCVTF_g . 0011110 .. 000010 ...... ..... ..... @fcvt32 +SCVTF_g . 0011110 .. 000010 ...... ..... ..... @fcvt64 +UCVTF_g . 0011110 .. 000011 ...... ..... ..... @fcvt32 +UCVTF_g . 0011110 .. 000011 ...... ..... ..... @fcvt64 + +FCVTZS_g . 0011110 .. 011000 ...... ..... ..... @fcvt32 +FCVTZS_g . 0011110 .. 011000 ...... ..... ..... @fcvt64 +FCVTZU_g . 0011110 .. 011001 ...... ..... ..... @fcvt32 +FCVTZU_g . 0011110 .. 011001 ...... ..... ..... @fcvt64 + +# Conversion between floating-point and integer (general register) + +@icvt sf:1 ....... .. ...... ...... rn:5 rd:5 \ + &fcvt esz=%esz_hsd shift=0 + +SCVTF_g . 0011110 .. 100010 000000 ..... ..... @icvt +UCVTF_g . 0011110 .. 100011 000000 ..... ..... @icvt + +FCVTNS_g . 0011110 .. 100000 000000 ..... ..... @icvt +FCVTNU_g . 0011110 .. 100001 000000 ..... ..... @icvt +FCVTPS_g . 0011110 .. 101000 000000 ..... ..... @icvt +FCVTPU_g . 0011110 .. 101001 000000 ..... ..... @icvt +FCVTMS_g . 0011110 .. 110000 000000 ..... ..... @icvt +FCVTMU_g . 0011110 .. 110001 000000 ..... ..... @icvt +FCVTZS_g . 0011110 .. 111000 000000 ..... ..... @icvt +FCVTZU_g . 0011110 .. 111001 000000 ..... ..... @icvt +FCVTAS_g . 0011110 .. 100100 000000 ..... ..... @icvt +FCVTAU_g . 0011110 .. 100101 000000 ..... ..... @icvt + # Floating-point data processing (1 source) FMOV_s 00011110 .. 1 000000 10000 ..... ..... @rr_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b31a6d4dff..67227e2676 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8524,227 +8524,196 @@ static bool trans_FCVT_s_dh(DisasContext *s, arg_rr *a) return true; } -/* Handle floating point <=> fixed point conversions. Note that we can - * also deal with fp <=> integer conversions as a special case (scale == 64) - * OPTME: consider handling that special case specially or at least skipping - * the call to scalbn in the helpers for zero shifts. - */ -static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, - bool itof, int rmode, int scale, int sf, int type) +static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, + TCGv_i64 tcg_int, bool is_signed) { - bool is_signed = !(opcode & 1); TCGv_ptr tcg_fpstatus; TCGv_i32 tcg_shift, tcg_single; TCGv_i64 tcg_double; - tcg_fpstatus = fpstatus_ptr(type == 3 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_shift = tcg_constant_i32(shift); - tcg_shift = tcg_constant_i32(64 - scale); - - if (itof) { - TCGv_i64 tcg_int = cpu_reg(s, rn); - if (!sf) { - TCGv_i64 tcg_extend = tcg_temp_new_i64(); - - if (is_signed) { - tcg_gen_ext32s_i64(tcg_extend, tcg_int); - } else { - tcg_gen_ext32u_i64(tcg_extend, tcg_int); - } - - tcg_int = tcg_extend; + switch (esz) { + case MO_64: + tcg_double = tcg_temp_new_i64(); + if (is_signed) { + gen_helper_vfp_sqtod(tcg_double, tcg_int, tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtod(tcg_double, tcg_int, tcg_shift, tcg_fpstatus); } + write_fp_dreg(s, rd, tcg_double); + break; - switch (type) { - case 1: /* float64 */ - tcg_double = tcg_temp_new_i64(); - if (is_signed) { - gen_helper_vfp_sqtod(tcg_double, tcg_int, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_uqtod(tcg_double, tcg_int, - tcg_shift, tcg_fpstatus); - } - write_fp_dreg(s, rd, tcg_double); - break; - - case 0: /* float32 */ - tcg_single = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_sqtos(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_uqtos(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } - write_fp_sreg(s, rd, tcg_single); - break; - - case 3: /* float16 */ - tcg_single = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_sqtoh(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_uqtoh(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } - write_fp_sreg(s, rd, tcg_single); - break; - - default: - g_assert_not_reached(); + case MO_32: + tcg_single = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_sqtos(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtos(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); } - } else { - TCGv_i64 tcg_int = cpu_reg(s, rd); - TCGv_i32 tcg_rmode; + write_fp_sreg(s, rd, tcg_single); + break; - if (extract32(opcode, 2, 1)) { - /* There are too many rounding modes to all fit into rmode, - * so FCVTA[US] is a special case. - */ - rmode = FPROUNDING_TIEAWAY; + case MO_16: + tcg_single = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_sqtoh(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtoh(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); } + write_fp_sreg(s, rd, tcg_single); + break; - tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); - - switch (type) { - case 1: /* float64 */ - tcg_double = read_fp_dreg(s, rn); - if (is_signed) { - if (!sf) { - gen_helper_vfp_tosld(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_tosqd(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } - } else { - if (!sf) { - gen_helper_vfp_tould(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touqd(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } - } - if (!sf) { - tcg_gen_ext32u_i64(tcg_int, tcg_int); - } - break; - - case 0: /* float32 */ - tcg_single = read_fp_sreg(s, rn); - if (sf) { - if (is_signed) { - gen_helper_vfp_tosqs(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touqs(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } - } else { - TCGv_i32 tcg_dest = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_tosls(tcg_dest, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touls(tcg_dest, tcg_single, - tcg_shift, tcg_fpstatus); - } - tcg_gen_extu_i32_i64(tcg_int, tcg_dest); - } - break; - - case 3: /* float16 */ - tcg_single = read_fp_sreg(s, rn); - if (sf) { - if (is_signed) { - gen_helper_vfp_tosqh(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touqh(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } - } else { - TCGv_i32 tcg_dest = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_toslh(tcg_dest, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_toulh(tcg_dest, tcg_single, - tcg_shift, tcg_fpstatus); - } - tcg_gen_extu_i32_i64(tcg_int, tcg_dest); - } - break; - - default: - g_assert_not_reached(); - } - - gen_restore_rmode(tcg_rmode, tcg_fpstatus); + default: + g_assert_not_reached(); } + return true; } -/* Floating point <-> fixed point conversions - * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 - * +----+---+---+-----------+------+---+-------+--------+-------+------+------+ - * | sf | 0 | S | 1 1 1 1 0 | type | 0 | rmode | opcode | scale | Rn | Rd | - * +----+---+---+-----------+------+---+-------+--------+-------+------+------+ - */ -static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) +static bool do_cvtf_g(DisasContext *s, arg_fcvt *a, bool is_signed) { - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int scale = extract32(insn, 10, 6); - int opcode = extract32(insn, 16, 3); - int rmode = extract32(insn, 19, 2); - int type = extract32(insn, 22, 2); - bool sbit = extract32(insn, 29, 1); - bool sf = extract32(insn, 31, 1); - bool itof; + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); - if (sbit || (!sf && scale < 32)) { - unallocated_encoding(s); - return; + if (check <= 0) { + return check == 0; } - switch (type) { - case 0: /* float32 */ - case 1: /* float64 */ - break; - case 3: /* float16 */ - if (dc_isar_feature(aa64_fp16, s)) { - break; + if (a->sf) { + tcg_int = cpu_reg(s, a->rn); + } else { + tcg_int = read_cpu_reg(s, a->rn, true); + if (is_signed) { + tcg_gen_ext32s_i64(tcg_int, tcg_int); + } else { + tcg_gen_ext32u_i64(tcg_int, tcg_int); } - /* fallthru */ - default: - unallocated_encoding(s); - return; } - - switch ((rmode << 3) | opcode) { - case 0x2: /* SCVTF */ - case 0x3: /* UCVTF */ - itof = true; - break; - case 0x18: /* FCVTZS */ - case 0x19: /* FCVTZU */ - itof = false; - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - handle_fpfpcvt(s, rd, rn, opcode, itof, FPROUNDING_ZERO, scale, sf, type); + return do_cvtf_scalar(s, a->esz, a->rd, a->shift, tcg_int, is_signed); } +TRANS(SCVTF_g, do_cvtf_g, a, true) +TRANS(UCVTF_g, do_cvtf_g, a, false) + +static void do_fcvt_scalar(DisasContext *s, MemOp out, MemOp esz, + TCGv_i64 tcg_out, int shift, int rn, + ARMFPRounding rmode) +{ + TCGv_ptr tcg_fpstatus; + TCGv_i32 tcg_shift, tcg_rmode, tcg_single; + + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_shift = tcg_constant_i32(shift); + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); + + switch (esz) { + case MO_64: + read_vec_element(s, tcg_out, rn, 0, MO_64); + switch (out) { + case MO_64 | MO_SIGN: + gen_helper_vfp_tosqd(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + case MO_64: + gen_helper_vfp_touqd(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + case MO_32 | MO_SIGN: + gen_helper_vfp_tosld(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + case MO_32: + gen_helper_vfp_tould(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + break; + + case MO_32: + tcg_single = read_fp_sreg(s, rn); + switch (out) { + case MO_64 | MO_SIGN: + gen_helper_vfp_tosqs(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_64: + gen_helper_vfp_touqs(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_32 | MO_SIGN: + gen_helper_vfp_tosls(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_32: + gen_helper_vfp_touls(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + default: + g_assert_not_reached(); + } + break; + + case MO_16: + tcg_single = read_fp_hreg(s, rn); + switch (out) { + case MO_64 | MO_SIGN: + gen_helper_vfp_tosqh(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_64: + gen_helper_vfp_touqh(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_32 | MO_SIGN: + gen_helper_vfp_toslh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_32: + gen_helper_vfp_toulh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + default: + g_assert_not_reached(); + } + break; + + default: + g_assert_not_reached(); + } + + gen_restore_rmode(tcg_rmode, tcg_fpstatus); +} + +static bool do_fcvt_g(DisasContext *s, arg_fcvt *a, + ARMFPRounding rmode, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + tcg_int = cpu_reg(s, a->rd); + do_fcvt_scalar(s, (a->sf ? MO_64 : MO_32) | (is_signed ? MO_SIGN : 0), + a->esz, tcg_int, a->shift, a->rn, rmode); + + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_int, tcg_int); + } + return true; +} + +TRANS(FCVTNS_g, do_fcvt_g, a, FPROUNDING_TIEEVEN, true) +TRANS(FCVTNU_g, do_fcvt_g, a, FPROUNDING_TIEEVEN, false) +TRANS(FCVTPS_g, do_fcvt_g, a, FPROUNDING_POSINF, true) +TRANS(FCVTPU_g, do_fcvt_g, a, FPROUNDING_POSINF, false) +TRANS(FCVTMS_g, do_fcvt_g, a, FPROUNDING_NEGINF, true) +TRANS(FCVTMU_g, do_fcvt_g, a, FPROUNDING_NEGINF, false) +TRANS(FCVTZS_g, do_fcvt_g, a, FPROUNDING_ZERO, true) +TRANS(FCVTZU_g, do_fcvt_g, a, FPROUNDING_ZERO, false) +TRANS(FCVTAS_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, true) +TRANS(FCVTAU_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, false) + static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) { /* FMOV: gpr to or from float, double, or top half of quad fp reg, @@ -8844,33 +8813,11 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) switch (opcode) { case 2: /* SCVTF */ case 3: /* UCVTF */ - itof = true; - /* fallthru */ case 4: /* FCVTAS */ case 5: /* FCVTAU */ - if (rmode != 0) { - goto do_unallocated; - } - /* fallthru */ case 0: /* FCVT[NPMZ]S */ case 1: /* FCVT[NPMZ]U */ - switch (type) { - case 0: /* float32 */ - case 1: /* float64 */ - break; - case 3: /* float16 */ - if (!dc_isar_feature(aa64_fp16, s)) { - goto do_unallocated; - } - break; - default: - goto do_unallocated; - } - if (!fp_access_check(s)) { - return; - } - handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type); - break; + goto do_unallocated; default: switch (sf << 7 | type << 5 | rmode << 3 | opcode) { @@ -8924,7 +8871,7 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn) unallocated_encoding(s); /* in decodetree */ } else if (extract32(insn, 21, 1) == 0) { /* Floating point to fixed point conversions */ - disas_fp_fixed_conv(s, insn); + unallocated_encoding(s); /* in decodetree */ } else { switch (extract32(insn, 10, 2)) { case 1: /* Floating point conditional compare */ From a769f854e6d37818d434958696e62503a5606ed5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:00 -0600 Subject: [PATCH 0237/2892] target/arm: Convert FJCVTZS to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 41 +++++++++++++++++----------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5e170cec7a..cd10961618 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1363,6 +1363,8 @@ FCVTZU_g . 0011110 .. 111001 000000 ..... ..... @icvt FCVTAS_g . 0011110 .. 100100 000000 ..... ..... @icvt FCVTAU_g . 0011110 .. 100101 000000 ..... ..... @icvt +FJCVTZS 0 0011110 01 111110 000000 ..... ..... @rr + # Floating-point data processing (1 source) FMOV_s 00011110 .. 1 000000 10000 ..... ..... @rr_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 67227e2676..d260b45ddb 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8714,6 +8714,26 @@ TRANS(FCVTZU_g, do_fcvt_g, a, FPROUNDING_ZERO, false) TRANS(FCVTAS_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, true) TRANS(FCVTAU_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, false) +static bool trans_FJCVTZS(DisasContext *s, arg_FJCVTZS *a) +{ + if (!dc_isar_feature(aa64_jscvt, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR); + + gen_helper_fjcvtzs(t, t, fpstatus); + + tcg_gen_ext32u_i64(cpu_reg(s, a->rd), t); + tcg_gen_extrh_i64_i32(cpu_ZF, t); + tcg_gen_movi_i32(cpu_CF, 0); + tcg_gen_movi_i32(cpu_NF, 0); + tcg_gen_movi_i32(cpu_VF, 0); + } + return true; +} + static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) { /* FMOV: gpr to or from float, double, or top half of quad fp reg, @@ -8775,20 +8795,6 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) } } -static void handle_fjcvtzs(DisasContext *s, int rd, int rn) -{ - TCGv_i64 t = read_fp_dreg(s, rn); - TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR); - - gen_helper_fjcvtzs(t, t, fpstatus); - - tcg_gen_ext32u_i64(cpu_reg(s, rd), t); - tcg_gen_extrh_i64_i32(cpu_ZF, t); - tcg_gen_movi_i32(cpu_CF, 0); - tcg_gen_movi_i32(cpu_NF, 0); - tcg_gen_movi_i32(cpu_VF, 0); -} - /* Floating point <-> integer conversions * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ @@ -8843,13 +8849,6 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) break; case 0b00111110: /* FJCVTZS */ - if (!dc_isar_feature(aa64_jscvt, s)) { - goto do_unallocated; - } else if (fp_access_check(s)) { - handle_fjcvtzs(s, rd, rn); - } - break; - default: do_unallocated: unallocated_encoding(s); From 5f4fe0e6580d861013de73e36692ce811860fc82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:01 -0600 Subject: [PATCH 0238/2892] target/arm: Convert handle_fmov to decodetree Remove disas_fp_int_conv and disas_data_proc_fp as these were the last insns decoded by those functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-35-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 14 ++ target/arm/tcg/translate-a64.c | 232 ++++++++++----------------------- 2 files changed, 86 insertions(+), 160 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index cd10961618..5b9f7caa7f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1365,6 +1365,20 @@ FCVTAU_g . 0011110 .. 100101 000000 ..... ..... @icvt FJCVTZS 0 0011110 01 111110 000000 ..... ..... @rr +FMOV_ws 0 0011110 00 100110 000000 ..... ..... @rr +FMOV_sw 0 0011110 00 100111 000000 ..... ..... @rr + +FMOV_xd 1 0011110 01 100110 000000 ..... ..... @rr +FMOV_dx 1 0011110 01 100111 000000 ..... ..... @rr + +# Move to/from upper half of 128-bit +FMOV_xu 1 0011110 10 101110 000000 ..... ..... @rr +FMOV_ux 1 0011110 10 101111 000000 ..... ..... @rr + +# Half-precision allows both sf=0 and sf=1 with identical results +FMOV_xh - 0011110 11 100110 000000 ..... ..... @rr +FMOV_hx - 0011110 11 100111 000000 ..... ..... @rr + # Floating-point data processing (1 source) FMOV_s 00011110 .. 1 000000 10000 ..... ..... @rr_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d260b45ddb..95bb2b1ca9 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8734,175 +8734,87 @@ static bool trans_FJCVTZS(DisasContext *s, arg_FJCVTZS *a) return true; } -static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) +static bool trans_FMOV_hx(DisasContext *s, arg_rr *a) { - /* FMOV: gpr to or from float, double, or top half of quad fp reg, - * without conversion. - */ - - if (itof) { - TCGv_i64 tcg_rn = cpu_reg(s, rn); - TCGv_i64 tmp; - - switch (type) { - case 0: - /* 32 bit */ - tmp = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(tmp, tcg_rn); - write_fp_dreg(s, rd, tmp); - break; - case 1: - /* 64 bit */ - write_fp_dreg(s, rd, tcg_rn); - break; - case 2: - /* 64 bit to top half. */ - tcg_gen_st_i64(tcg_rn, tcg_env, fp_reg_hi_offset(s, rd)); - clear_vec_high(s, true, rd); - break; - case 3: - /* 16 bit */ - tmp = tcg_temp_new_i64(); - tcg_gen_ext16u_i64(tmp, tcg_rn); - write_fp_dreg(s, rd, tmp); - break; - default: - g_assert_not_reached(); - } - } else { - TCGv_i64 tcg_rd = cpu_reg(s, rd); - - switch (type) { - case 0: - /* 32 bit */ - tcg_gen_ld32u_i64(tcg_rd, tcg_env, fp_reg_offset(s, rn, MO_32)); - break; - case 1: - /* 64 bit */ - tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_offset(s, rn, MO_64)); - break; - case 2: - /* 64 bits from top half */ - tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_hi_offset(s, rn)); - break; - case 3: - /* 16 bit */ - tcg_gen_ld16u_i64(tcg_rd, tcg_env, fp_reg_offset(s, rn, MO_16)); - break; - default: - g_assert_not_reached(); - } + if (!dc_isar_feature(aa64_fp16, s)) { + return false; } + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(tmp, tcg_rn); + write_fp_dreg(s, a->rd, tmp); + } + return true; } -/* Floating point <-> integer conversions - * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 - * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ - * | sf | 0 | S | 1 1 1 1 0 | type | 1 | rmode | opc | 0 0 0 0 0 0 | Rn | Rd | - * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ - */ -static void disas_fp_int_conv(DisasContext *s, uint32_t insn) +static bool trans_FMOV_sw(DisasContext *s, arg_rr *a) { - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 16, 3); - int rmode = extract32(insn, 19, 2); - int type = extract32(insn, 22, 2); - bool sbit = extract32(insn, 29, 1); - bool sf = extract32(insn, 31, 1); - bool itof = false; - - if (sbit) { - goto do_unallocated; - } - - switch (opcode) { - case 2: /* SCVTF */ - case 3: /* UCVTF */ - case 4: /* FCVTAS */ - case 5: /* FCVTAU */ - case 0: /* FCVT[NPMZ]S */ - case 1: /* FCVT[NPMZ]U */ - goto do_unallocated; - - default: - switch (sf << 7 | type << 5 | rmode << 3 | opcode) { - case 0b01100110: /* FMOV half <-> 32-bit int */ - case 0b01100111: - case 0b11100110: /* FMOV half <-> 64-bit int */ - case 0b11100111: - if (!dc_isar_feature(aa64_fp16, s)) { - goto do_unallocated; - } - /* fallthru */ - case 0b00000110: /* FMOV 32-bit */ - case 0b00000111: - case 0b10100110: /* FMOV 64-bit */ - case 0b10100111: - case 0b11001110: /* FMOV top half of 128-bit */ - case 0b11001111: - if (!fp_access_check(s)) { - return; - } - itof = opcode & 1; - handle_fmov(s, rd, rn, type, itof); - break; - - case 0b00111110: /* FJCVTZS */ - default: - do_unallocated: - unallocated_encoding(s); - return; - } - break; + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(tmp, tcg_rn); + write_fp_dreg(s, a->rd, tmp); } + return true; } -/* FP-specific subcases of table C3-6 (SIMD and FP data processing) - * 31 30 29 28 25 24 0 - * +---+---+---+---------+-----------------------------+ - * | | 0 | | 1 1 1 1 | | - * +---+---+---+---------+-----------------------------+ - */ -static void disas_data_proc_fp(DisasContext *s, uint32_t insn) +static bool trans_FMOV_dx(DisasContext *s, arg_rr *a) { - if (extract32(insn, 24, 1)) { - unallocated_encoding(s); /* in decodetree */ - } else if (extract32(insn, 21, 1) == 0) { - /* Floating point to fixed point conversions */ - unallocated_encoding(s); /* in decodetree */ - } else { - switch (extract32(insn, 10, 2)) { - case 1: /* Floating point conditional compare */ - case 2: /* Floating point data-processing (2 source) */ - case 3: /* Floating point conditional select */ - unallocated_encoding(s); /* in decodetree */ - break; - case 0: - switch (ctz32(extract32(insn, 12, 4))) { - case 0: /* [15:12] == xxx1 */ - /* Floating point immediate */ - unallocated_encoding(s); /* in decodetree */ - break; - case 1: /* [15:12] == xx10 */ - /* Floating point compare */ - unallocated_encoding(s); /* in decodetree */ - break; - case 2: /* [15:12] == x100 */ - /* Floating point data-processing (1 source) */ - unallocated_encoding(s); /* in decodetree */ - break; - case 3: /* [15:12] == 1000 */ - unallocated_encoding(s); - break; - default: /* [15:12] == 0000 */ - /* Floating point <-> integer conversions */ - disas_fp_int_conv(s, insn); - break; - } - break; - } + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + write_fp_dreg(s, a->rd, tcg_rn); } + return true; +} + +static bool trans_FMOV_ux(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + tcg_gen_st_i64(tcg_rn, tcg_env, fp_reg_hi_offset(s, a->rd)); + clear_vec_high(s, true, a->rd); + } + return true; +} + +static bool trans_FMOV_xh(DisasContext *s, arg_rr *a) +{ + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld16u_i64(tcg_rd, tcg_env, fp_reg_offset(s, a->rn, MO_16)); + } + return true; +} + +static bool trans_FMOV_ws(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld32u_i64(tcg_rd, tcg_env, fp_reg_offset(s, a->rn, MO_32)); + } + return true; +} + +static bool trans_FMOV_xd(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_offset(s, a->rn, MO_64)); + } + return true; +} + +static bool trans_FMOV_xu(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_hi_offset(s, a->rn)); + } + return true; } /* Common vector code for handling integer to FP conversion */ @@ -10821,7 +10733,7 @@ static void disas_data_proc_simd(DisasContext *s, uint32_t insn) static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) { if (extract32(insn, 28, 1) == 1 && extract32(insn, 30, 1) == 0) { - disas_data_proc_fp(s, insn); + unallocated_encoding(s); /* in decodetree */ } else { /* SIMD, including crypto */ disas_data_proc_simd(s, insn); From df79bfcf75d64dbd96b71e01618fc363925251cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:02 -0600 Subject: [PATCH 0239/2892] target/arm: Convert SQABS, SQNEG to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-36-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 11 +++ target/arm/tcg/translate-a64.c | 123 +++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 45 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5b9f7caa7f..17ecdac9db 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -47,6 +47,7 @@ @rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1 @rr_s ........ ... ..... ...... rn:5 rd:5 &rr_e esz=2 @rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 +@rr_e ........ esz:2 . ..... ...... rn:5 rd:5 &rr_e @rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd @rr_hsd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_hsd @@ -1626,3 +1627,13 @@ UQRSHRN_si 0111 11110 .... ... 10011 1 ..... ..... @shri_s SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_b SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_h SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_s + +# Advanced SIMD scalar two-register miscellaneous + +SQABS_s 0101 1110 ..1 00000 01111 0 ..... ..... @rr_e +SQNEG_s 0111 1110 ..1 00000 01111 0 ..... ..... @rr_e + +# Advanced SIMD two-register miscellaneous + +SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e +SQNEG_v 0.10 1110 ..1 00000 01111 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 95bb2b1ca9..9bb9668d11 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8817,6 +8817,78 @@ static bool trans_FMOV_xu(DisasContext *s, arg_rr *a) return true; } +typedef struct ENVScalar1 { + NeonGenOneOpEnvFn *gen_bhs[3]; + NeonGenOne64OpEnvFn *gen_d; +} ENVScalar1; + +static bool do_env_scalar1(DisasContext *s, arg_rr_e *a, const ENVScalar1 *f) +{ + if (!fp_access_check(s)) { + return true; + } + if (a->esz == MO_64) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + f->gen_d(t, tcg_env, t); + write_fp_dreg(s, a->rd, t); + } else { + TCGv_i32 t = tcg_temp_new_i32(); + + read_vec_element_i32(s, t, a->rn, 0, a->esz); + f->gen_bhs[a->esz](t, tcg_env, t); + write_fp_sreg(s, a->rd, t); + } + return true; +} + +static bool do_env_vector1(DisasContext *s, arg_qrr_e *a, const ENVScalar1 *f) +{ + if (a->esz == MO_64 && !a->q) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + if (a->esz == MO_64) { + TCGv_i64 t = tcg_temp_new_i64(); + + for (int i = 0; i < 2; ++i) { + read_vec_element(s, t, a->rn, i, MO_64); + f->gen_d(t, tcg_env, t); + write_vec_element(s, t, a->rd, i, MO_64); + } + } else { + TCGv_i32 t = tcg_temp_new_i32(); + int n = (a->q ? 16 : 8) >> a->esz; + + for (int i = 0; i < n; ++i) { + read_vec_element_i32(s, t, a->rn, i, a->esz); + f->gen_bhs[a->esz](t, tcg_env, t); + write_vec_element_i32(s, t, a->rd, i, a->esz); + } + } + clear_vec_high(s, a->q, a->rd); + return true; +} + +static const ENVScalar1 f_scalar_sqabs = { + { gen_helper_neon_qabs_s8, + gen_helper_neon_qabs_s16, + gen_helper_neon_qabs_s32 }, + gen_helper_neon_qabs_s64, +}; +TRANS(SQABS_s, do_env_scalar1, a, &f_scalar_sqabs) +TRANS(SQABS_v, do_env_vector1, a, &f_scalar_sqabs) + +static const ENVScalar1 f_scalar_sqneg = { + { gen_helper_neon_qneg_s8, + gen_helper_neon_qneg_s16, + gen_helper_neon_qneg_s32 }, + gen_helper_neon_qneg_s64, +}; +TRANS(SQNEG_s, do_env_scalar1, a, &f_scalar_sqneg) +TRANS(SQNEG_v, do_env_vector1, a, &f_scalar_sqneg) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9129,13 +9201,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, */ tcg_gen_not_i64(tcg_rd, tcg_rn); break; - case 0x7: /* SQABS, SQNEG */ - if (u) { - gen_helper_neon_qneg_s64(tcg_rd, tcg_env, tcg_rn); - } else { - gen_helper_neon_qabs_s64(tcg_rd, tcg_env, tcg_rn); - } - break; case 0xa: /* CMLT */ cond = TCG_COND_LT; do_cmop: @@ -9198,6 +9263,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus); break; default: + case 0x7: /* SQABS, SQNEG */ g_assert_not_reached(); } } @@ -9540,8 +9606,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0x7: /* SQABS / SQNEG */ - break; case 0xa: /* CMLT */ if (u) { unallocated_encoding(s); @@ -9640,6 +9704,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) break; default: case 0x3: /* USQADD / SUQADD */ + case 0x7: /* SQABS / SQNEG */ unallocated_encoding(s); return; } @@ -9669,18 +9734,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_rn, rn, 0, size); switch (opcode) { - case 0x7: /* SQABS, SQNEG */ - { - NeonGenOneOpEnvFn *genfn; - static NeonGenOneOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qabs_s8, gen_helper_neon_qneg_s8 }, - { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, - { gen_helper_neon_qabs_s32, gen_helper_neon_qneg_s32 }, - }; - genfn = fns[size][u]; - genfn(tcg_rd, tcg_env, tcg_rn); - break; - } case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ case 0x1c: /* FCVTAS */ @@ -9698,6 +9751,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) tcg_fpstatus); break; default: + case 0x7: /* SQABS, SQNEG */ g_assert_not_reached(); } @@ -10055,12 +10109,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) return; } break; - case 0x7: /* SQABS, SQNEG */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; case 0xc ... 0xf: case 0x16 ... 0x1f: { @@ -10231,6 +10279,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } default: case 0x3: /* SUQADD, USQADD */ + case 0x7: /* SQABS, SQNEG */ unallocated_encoding(s); return; } @@ -10321,13 +10370,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) tcg_gen_clrsb_i32(tcg_res, tcg_op); } break; - case 0x7: /* SQABS, SQNEG */ - if (u) { - gen_helper_neon_qneg_s32(tcg_res, tcg_env, tcg_op); - } else { - gen_helper_neon_qabs_s32(tcg_res, tcg_env, tcg_op); - } - break; case 0x2f: /* FABS */ gen_vfp_abss(tcg_res, tcg_op); break; @@ -10376,6 +10418,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_helper_frint64_s(tcg_res, tcg_op, tcg_fpstatus); break; default: + case 0x7: /* SQABS, SQNEG */ g_assert_not_reached(); } } else { @@ -10391,17 +10434,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_helper_neon_cnt_u8(tcg_res, tcg_op); } break; - case 0x7: /* SQABS, SQNEG */ - { - NeonGenOneOpEnvFn *genfn; - static NeonGenOneOpEnvFn * const fns[2][2] = { - { gen_helper_neon_qabs_s8, gen_helper_neon_qneg_s8 }, - { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, - }; - genfn = fns[size][u]; - genfn(tcg_res, tcg_env, tcg_op); - break; - } case 0x4: /* CLS, CLZ */ if (u) { if (size == 0) { @@ -10418,6 +10450,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } break; default: + case 0x7: /* SQABS, SQNEG */ g_assert_not_reached(); } } From 9187b72cff0372270d5b02cfd2e2ade10daafb4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:03 -0600 Subject: [PATCH 0240/2892] target/arm: Convert ABS, NEG to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-37-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 +++ target/arm/tcg/translate-a64.c | 46 +++++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 17ecdac9db..f112951df7 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1632,8 +1632,12 @@ SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_s SQABS_s 0101 1110 ..1 00000 01111 0 ..... ..... @rr_e SQNEG_s 0111 1110 ..1 00000 01111 0 ..... ..... @rr_e +ABS_s 0101 1110 111 00000 10111 0 ..... ..... @rr +NEG_s 0111 1110 111 00000 10111 0 ..... ..... @rr # Advanced SIMD two-register miscellaneous SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e SQNEG_v 0.10 1110 ..1 00000 01111 0 ..... ..... @qrr_e +ABS_v 0.00 1110 ..1 00000 10111 0 ..... ..... @qrr_e +NEG_v 0.10 1110 ..1 00000 10111 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9bb9668d11..c697f0e944 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8889,6 +8889,33 @@ static const ENVScalar1 f_scalar_sqneg = { TRANS(SQNEG_s, do_env_scalar1, a, &f_scalar_sqneg) TRANS(SQNEG_v, do_env_vector1, a, &f_scalar_sqneg) +static bool do_scalar1_d(DisasContext *s, arg_rr *a, ArithOneOp *f) +{ + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + f(t, t); + write_fp_dreg(s, a->rd, t); + } + return true; +} + +TRANS(ABS_s, do_scalar1_d, a, tcg_gen_abs_i64) +TRANS(NEG_s, do_scalar1_d, a, tcg_gen_neg_i64) + +static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn2(s, a->q, a->rd, a->rn, fn, a->esz); + } + return true; +} + +TRANS(ABS_v, do_gvec_fn2, a, tcg_gen_gvec_abs) +TRANS(NEG_v, do_gvec_fn2, a, tcg_gen_gvec_neg) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9213,13 +9240,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x9: /* CMEQ, CMLE */ cond = u ? TCG_COND_LE : TCG_COND_EQ; goto do_cmop; - case 0xb: /* ABS, NEG */ - if (u) { - tcg_gen_neg_i64(tcg_rd, tcg_rn); - } else { - tcg_gen_abs_i64(tcg_rd, tcg_rn); - } - break; case 0x2f: /* FABS */ gen_vfp_absd(tcg_rd, tcg_rn); break; @@ -9264,6 +9284,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, break; default: case 0x7: /* SQABS, SQNEG */ + case 0xb: /* ABS, NEG */ g_assert_not_reached(); } } @@ -9614,7 +9635,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) /* fall through */ case 0x8: /* CMGT, CMGE */ case 0x9: /* CMEQ, CMLE */ - case 0xb: /* ABS, NEG */ if (size != 3) { unallocated_encoding(s); return; @@ -9705,6 +9725,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) default: case 0x3: /* USQADD / SUQADD */ case 0x7: /* SQABS / SQNEG */ + case 0xb: /* ABS, NEG */ unallocated_encoding(s); return; } @@ -10103,7 +10124,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) /* fall through */ case 0x8: /* CMGT, CMGE */ case 0x9: /* CMEQ, CMLE */ - case 0xb: /* ABS, NEG */ if (size == 3 && !is_q) { unallocated_encoding(s); return; @@ -10280,6 +10300,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) default: case 0x3: /* SUQADD, USQADD */ case 0x7: /* SQABS, SQNEG */ + case 0xb: /* ABS, NEG */ unallocated_encoding(s); return; } @@ -10324,12 +10345,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clt0, size); return; case 0xb: - if (u) { /* ABS, NEG */ - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_neg, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_abs, size); - } - return; + g_assert_not_reached(); } if (size == 3) { From 4fedfb483b35cbe98863f0dafb908727676292b5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:04 -0600 Subject: [PATCH 0241/2892] target/arm: Introduce gen_gvec_cls, gen_gvec_clz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add gvec interfaces for CLS and CLZ operations. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-38-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 35 +++++++++++++++++++++++++++++++++ target/arm/tcg/translate-a64.c | 29 +++++++-------------------- target/arm/tcg/translate-neon.c | 29 ++------------------------- target/arm/tcg/translate.h | 5 +++++ 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index f652520b65..834b2961c0 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2358,3 +2358,38 @@ void gen_gvec_urhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, assert(vece <= MO_32); tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); } + +void gen_gvec_cls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g[] = { + { .fni4 = gen_helper_neon_cls_s8, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_cls_s16, + .vece = MO_16 }, + { .fni4 = tcg_gen_clrsb_i32, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_clz32_i32(TCGv_i32 d, TCGv_i32 n) +{ + tcg_gen_clzi_i32(d, n, 32); +} + +void gen_gvec_clz(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g[] = { + { .fni4 = gen_helper_neon_clz_u8, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_clz_u16, + .vece = MO_16 }, + { .fni4 = gen_clz32_i32, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c697f0e944..387bbbf906 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10321,6 +10321,13 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x4: /* CLZ, CLS */ + if (u) { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clz, size); + } else { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cls, size); + } + return; case 0x5: if (u && size == 0) { /* NOT */ gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_not, 0); @@ -10379,13 +10386,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) if (size == 2) { /* Special cases for 32 bit elements */ switch (opcode) { - case 0x4: /* CLS */ - if (u) { - tcg_gen_clzi_i32(tcg_res, tcg_op, 32); - } else { - tcg_gen_clrsb_i32(tcg_res, tcg_op); - } - break; case 0x2f: /* FABS */ gen_vfp_abss(tcg_res, tcg_op); break; @@ -10450,21 +10450,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_helper_neon_cnt_u8(tcg_res, tcg_op); } break; - case 0x4: /* CLS, CLZ */ - if (u) { - if (size == 0) { - gen_helper_neon_clz_u8(tcg_res, tcg_op); - } else { - gen_helper_neon_clz_u16(tcg_res, tcg_op); - } - } else { - if (size == 0) { - gen_helper_neon_cls_s8(tcg_res, tcg_op); - } else { - gen_helper_neon_cls_s16(tcg_res, tcg_op); - } - } - break; default: case 0x7: /* SQABS, SQNEG */ g_assert_not_reached(); diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 9c8829ad7d..1c89a53272 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -3120,6 +3120,8 @@ DO_2MISC_VEC(VCGT0, gen_gvec_cgt0) DO_2MISC_VEC(VCLE0, gen_gvec_cle0) DO_2MISC_VEC(VCGE0, gen_gvec_cge0) DO_2MISC_VEC(VCLT0, gen_gvec_clt0) +DO_2MISC_VEC(VCLS, gen_gvec_cls) +DO_2MISC_VEC(VCLZ, gen_gvec_clz) static bool trans_VMVN(DisasContext *s, arg_2misc *a) { @@ -3227,33 +3229,6 @@ static bool trans_VREV16(DisasContext *s, arg_2misc *a) return do_2misc(s, a, gen_rev16); } -static bool trans_VCLS(DisasContext *s, arg_2misc *a) -{ - static NeonGenOneOpFn * const fn[] = { - gen_helper_neon_cls_s8, - gen_helper_neon_cls_s16, - gen_helper_neon_cls_s32, - NULL, - }; - return do_2misc(s, a, fn[a->size]); -} - -static void do_VCLZ_32(TCGv_i32 rd, TCGv_i32 rm) -{ - tcg_gen_clzi_i32(rd, rm, 32); -} - -static bool trans_VCLZ(DisasContext *s, arg_2misc *a) -{ - static NeonGenOneOpFn * const fn[] = { - gen_helper_neon_clz_u8, - gen_helper_neon_clz_u16, - do_VCLZ_32, - NULL, - }; - return do_2misc(s, a, fn[a->size]); -} - static bool trans_VCNT(DisasContext *s, arg_2misc *a) { if (a->size != 0) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 20cd0e851c..5c6c24f057 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -578,6 +578,11 @@ void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_cls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_clz(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); + /* * Forward to the isar_feature_* tests given a DisasContext pointer. */ From 4e728364c919af537fbd1dc425641ae04f562915 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:05 -0600 Subject: [PATCH 0242/2892] target/arm: Convert CLS, CLZ (vector) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-39-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 37 ++++++++++++++++------------------ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f112951df7..32355ee633 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1641,3 +1641,5 @@ SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e SQNEG_v 0.10 1110 ..1 00000 01111 0 ..... ..... @qrr_e ABS_v 0.00 1110 ..1 00000 10111 0 ..... ..... @qrr_e NEG_v 0.10 1110 ..1 00000 10111 0 ..... ..... @qrr_e +CLS_v 0.00 1110 ..1 00000 01001 0 ..... ..... @qrr_e +CLZ_v 0.10 1110 ..1 00000 01001 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 387bbbf906..ecb4578998 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8916,6 +8916,20 @@ static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) TRANS(ABS_v, do_gvec_fn2, a, tcg_gen_gvec_abs) TRANS(NEG_v, do_gvec_fn2, a, tcg_gen_gvec_neg) +static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) +{ + if (a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn2(s, a->q, a->rd, a->rn, fn, a->esz); + } + return true; +} + +TRANS(CLS_v, do_gvec_fn2_bhs, a, gen_gvec_cls) +TRANS(CLZ_v, do_gvec_fn2_bhs, a, gen_gvec_clz) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9215,13 +9229,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGCond cond; switch (opcode) { - case 0x4: /* CLS, CLZ */ - if (u) { - tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); - } else { - tcg_gen_clrsb_i64(tcg_rd, tcg_rn); - } - break; case 0x5: /* NOT */ /* This opcode is shared with CNT and RBIT but we have earlier * enforced that size == 3 if and only if this is the NOT insn. @@ -9283,6 +9290,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus); break; default: + case 0x4: /* CLS, CLZ */ case 0x7: /* SQABS, SQNEG */ case 0xb: /* ABS, NEG */ g_assert_not_reached(); @@ -10089,12 +10097,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) handle_2misc_narrow(s, false, opcode, u, is_q, size, rn, rd); return; - case 0x4: /* CLS, CLZ */ - if (size == 3) { - unallocated_encoding(s); - return; - } - break; case 0x2: /* SADDLP, UADDLP */ case 0x6: /* SADALP, UADALP */ if (size == 3) { @@ -10299,6 +10301,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } default: case 0x3: /* SUQADD, USQADD */ + case 0x4: /* CLS, CLZ */ case 0x7: /* SQABS, SQNEG */ case 0xb: /* ABS, NEG */ unallocated_encoding(s); @@ -10321,13 +10324,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x4: /* CLZ, CLS */ - if (u) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clz, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cls, size); - } - return; case 0x5: if (u && size == 0) { /* NOT */ gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_not, 0); @@ -10351,6 +10347,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0xa: /* CMLT */ gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clt0, size); return; + case 0x4: /* CLZ, CLS */ case 0xb: g_assert_not_reached(); } From 4694d57458fb8850d95865dae619e284aa1cedbc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:06 -0600 Subject: [PATCH 0243/2892] target/arm: Introduce gen_gvec_cnt, gen_gvec_rbit Add gvec interfaces for CNT and RBIT operations. Use ctpop8 for CNT and revbit+bswap for RBIT. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-40-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 4 ++-- target/arm/tcg/gengvec.c | 16 ++++++++++++++++ target/arm/tcg/neon_helper.c | 21 --------------------- target/arm/tcg/translate-a64.c | 32 +++++++++----------------------- target/arm/tcg/translate-neon.c | 16 ++++++++-------- target/arm/tcg/translate.h | 4 ++++ target/arm/tcg/vec_helper.c | 24 ++++++++++++++++++++++++ 7 files changed, 63 insertions(+), 54 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 0a697e752b..167e331a83 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -363,8 +363,8 @@ DEF_HELPER_1(neon_clz_u16, i32, i32) DEF_HELPER_1(neon_cls_s8, i32, i32) DEF_HELPER_1(neon_cls_s16, i32, i32) DEF_HELPER_1(neon_cls_s32, i32, i32) -DEF_HELPER_1(neon_cnt_u8, i32, i32) -DEF_HELPER_FLAGS_1(neon_rbit_u8, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_3(gvec_cnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 834b2961c0..85a0b50496 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2393,3 +2393,19 @@ void gen_gvec_clz(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, assert(vece <= MO_32); tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); } + +void gen_gvec_cnt(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_8); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_cnt_b); +} + +void gen_gvec_rbit(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_8); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_rbit_b); +} diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 93b2076c64..4e501925de 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -525,27 +525,6 @@ uint32_t HELPER(neon_cls_s32)(uint32_t x) return count - 1; } -/* Bit count. */ -uint32_t HELPER(neon_cnt_u8)(uint32_t x) -{ - x = (x & 0x55555555) + ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f); - return x; -} - -/* Reverse bits in each 8 bit word */ -uint32_t HELPER(neon_rbit_u8)(uint32_t x) -{ - x = ((x & 0xf0f0f0f0) >> 4) - | ((x & 0x0f0f0f0f) << 4); - x = ((x & 0x88888888) >> 3) - | ((x & 0x44444444) >> 1) - | ((x & 0x22222222) << 1) - | ((x & 0x11111111) << 3); - return x; -} - #define NEON_QDMULH16(dest, src1, src2, round) do { \ uint32_t tmp = (int32_t)(int16_t) src1 * (int16_t) src2; \ if ((tmp ^ (tmp << 1)) & SIGNBIT) { \ diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ecb4578998..3e0c061b3c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10324,12 +10324,15 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x5: - if (u && size == 0) { /* NOT */ + case 0x5: /* CNT, NOT, RBIT */ + if (!u) { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cnt, 0); + } else if (size) { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_rbit, 0); + } else { gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_not, 0); - return; } - break; + return; case 0x8: /* CMGT, CMGE */ if (u) { gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cge0, size); @@ -10374,13 +10377,14 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } else { int pass; + assert(size == 2); for (pass = 0; pass < (is_q ? 4 : 2); pass++) { TCGv_i32 tcg_op = tcg_temp_new_i32(); TCGv_i32 tcg_res = tcg_temp_new_i32(); read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - if (size == 2) { + { /* Special cases for 32 bit elements */ switch (opcode) { case 0x2f: /* FABS */ @@ -10434,25 +10438,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x7: /* SQABS, SQNEG */ g_assert_not_reached(); } - } else { - /* Use helpers for 8 and 16 bit elements */ - switch (opcode) { - case 0x5: /* CNT, RBIT */ - /* For these two insns size is part of the opcode specifier - * (handled earlier); they always operate on byte elements. - */ - if (u) { - gen_helper_neon_rbit_u8(tcg_res, tcg_op); - } else { - gen_helper_neon_cnt_u8(tcg_res, tcg_op); - } - break; - default: - case 0x7: /* SQABS, SQNEG */ - g_assert_not_reached(); - } } - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); } } diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 1c89a53272..50d0bf7753 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -3131,6 +3131,14 @@ static bool trans_VMVN(DisasContext *s, arg_2misc *a) return do_2misc_vec(s, a, tcg_gen_gvec_not); } +static bool trans_VCNT(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc_vec(s, a, gen_gvec_cnt); +} + #define WRAP_2M_3_OOL_FN(WRAPNAME, FUNC, DATA) \ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ uint32_t rm_ofs, uint32_t oprsz, \ @@ -3229,14 +3237,6 @@ static bool trans_VREV16(DisasContext *s, arg_2misc *a) return do_2misc(s, a, gen_rev16); } -static bool trans_VCNT(DisasContext *s, arg_2misc *a) -{ - if (a->size != 0) { - return false; - } - return do_2misc(s, a, gen_helper_neon_cnt_u8); -} - static void gen_VABS_F(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, uint32_t oprsz, uint32_t maxsz) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 5c6c24f057..cb8e1b2586 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -582,6 +582,10 @@ void gen_gvec_cls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_clz(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_cnt(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rbit(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); /* * Forward to the isar_feature_* tests given a DisasContext pointer. diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index ad6f26545a..91a9130641 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -3066,3 +3066,27 @@ DO_CLAMP(gvec_uclamp_b, uint8_t) DO_CLAMP(gvec_uclamp_h, uint16_t) DO_CLAMP(gvec_uclamp_s, uint32_t) DO_CLAMP(gvec_uclamp_d, uint64_t) + +/* Bit count in each 8-bit word. */ +void HELPER(gvec_cnt_b)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; ++i) { + d[i] = ctpop8(n[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* Reverse bits in each 8 bit word */ +void HELPER(gvec_rbit_b)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = revbit64(bswap64(n[i])); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} From 95288cc1654c80e0ab301faa3ed55ef43e0c8f1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:07 -0600 Subject: [PATCH 0244/2892] target/arm: Convert CNT, NOT, RBIT (vector) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-41-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 34 ++++++---------------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 32355ee633..bac81eec7e 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -71,6 +71,7 @@ @rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3 @rrrr_q1e3 ........ ... rm:5 . ra:5 rn:5 rd:5 &qrrrr_e q=1 esz=3 +@qrr_b . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=0 @qrr_h . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=1 @qrr_e . q:1 ...... esz:2 ...... ...... rn:5 rd:5 &qrr_e @@ -1643,3 +1644,6 @@ ABS_v 0.00 1110 ..1 00000 10111 0 ..... ..... @qrr_e NEG_v 0.10 1110 ..1 00000 10111 0 ..... ..... @qrr_e CLS_v 0.00 1110 ..1 00000 01001 0 ..... ..... @qrr_e CLZ_v 0.10 1110 ..1 00000 01001 0 ..... ..... @qrr_e +CNT_v 0.00 1110 001 00000 01011 0 ..... ..... @qrr_b +NOT_v 0.10 1110 001 00000 01011 0 ..... ..... @qrr_b +RBIT_v 0.10 1110 011 00000 01011 0 ..... ..... @qrr_b diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3e0c061b3c..aff1984a22 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8915,6 +8915,9 @@ static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) TRANS(ABS_v, do_gvec_fn2, a, tcg_gen_gvec_abs) TRANS(NEG_v, do_gvec_fn2, a, tcg_gen_gvec_neg) +TRANS(NOT_v, do_gvec_fn2, a, tcg_gen_gvec_not) +TRANS(CNT_v, do_gvec_fn2, a, gen_gvec_cnt) +TRANS(RBIT_v, do_gvec_fn2, a, gen_gvec_rbit) static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) { @@ -9229,12 +9232,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGCond cond; switch (opcode) { - case 0x5: /* NOT */ - /* This opcode is shared with CNT and RBIT but we have earlier - * enforced that size == 3 if and only if this is the NOT insn. - */ - tcg_gen_not_i64(tcg_rd, tcg_rn); - break; case 0xa: /* CMLT */ cond = TCG_COND_LT; do_cmop: @@ -9291,6 +9288,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, break; default: case 0x4: /* CLS, CLZ */ + case 0x5: /* NOT */ case 0x7: /* SQABS, SQNEG */ case 0xb: /* ABS, NEG */ g_assert_not_reached(); @@ -10072,19 +10070,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x1: /* REV16 */ handle_rev(s, opcode, u, is_q, size, rn, rd); return; - case 0x5: /* CNT, NOT, RBIT */ - if (u && size == 0) { - /* NOT */ - break; - } else if (u && size == 1) { - /* RBIT */ - break; - } else if (!u && size == 0) { - /* CNT */ - break; - } - unallocated_encoding(s); - return; case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ if (size == 3) { @@ -10302,6 +10287,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) default: case 0x3: /* SUQADD, USQADD */ case 0x4: /* CLS, CLZ */ + case 0x5: /* CNT, NOT, RBIT */ case 0x7: /* SQABS, SQNEG */ case 0xb: /* ABS, NEG */ unallocated_encoding(s); @@ -10324,15 +10310,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x5: /* CNT, NOT, RBIT */ - if (!u) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cnt, 0); - } else if (size) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_rbit, 0); - } else { - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_not, 0); - } - return; case 0x8: /* CMGT, CMGE */ if (u) { gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cge0, size); @@ -10351,6 +10328,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clt0, size); return; case 0x4: /* CLZ, CLS */ + case 0x5: /* CNT, NOT, RBIT */ case 0xb: g_assert_not_reached(); } From 72040d9200f64960aefd9c88a539ecd8d9b3adcf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:08 -0600 Subject: [PATCH 0245/2892] target/arm: Convert CMGT, CMGE, GMLT, GMLE, CMEQ (zero) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-42-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 ++++ target/arm/tcg/translate-a64.c | 94 +++++++++++----------------------- 2 files changed, 40 insertions(+), 64 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index bac81eec7e..247d3a7bda 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1635,6 +1635,11 @@ SQABS_s 0101 1110 ..1 00000 01111 0 ..... ..... @rr_e SQNEG_s 0111 1110 ..1 00000 01111 0 ..... ..... @rr_e ABS_s 0101 1110 111 00000 10111 0 ..... ..... @rr NEG_s 0111 1110 111 00000 10111 0 ..... ..... @rr +CMGT0_s 0101 1110 111 00000 10001 0 ..... ..... @rr +CMGE0_s 0111 1110 111 00000 10001 0 ..... ..... @rr +CMEQ0_s 0101 1110 111 00000 10011 0 ..... ..... @rr +CMLE0_s 0111 1110 111 00000 10011 0 ..... ..... @rr +CMLT0_s 0101 1110 111 00000 10101 0 ..... ..... @rr # Advanced SIMD two-register miscellaneous @@ -1647,3 +1652,8 @@ CLZ_v 0.10 1110 ..1 00000 01001 0 ..... ..... @qrr_e CNT_v 0.00 1110 001 00000 01011 0 ..... ..... @qrr_b NOT_v 0.10 1110 001 00000 01011 0 ..... ..... @qrr_b RBIT_v 0.10 1110 011 00000 01011 0 ..... ..... @qrr_b +CMGT0_v 0.00 1110 ..1 00000 10001 0 ..... ..... @qrr_e +CMGE0_v 0.10 1110 ..1 00000 10001 0 ..... ..... @qrr_e +CMEQ0_v 0.00 1110 ..1 00000 10011 0 ..... ..... @qrr_e +CMLE0_v 0.10 1110 ..1 00000 10011 0 ..... ..... @qrr_e +CMLT0_v 0.00 1110 ..1 00000 10101 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index aff1984a22..547c6dc5cc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8902,6 +8902,22 @@ static bool do_scalar1_d(DisasContext *s, arg_rr *a, ArithOneOp *f) TRANS(ABS_s, do_scalar1_d, a, tcg_gen_abs_i64) TRANS(NEG_s, do_scalar1_d, a, tcg_gen_neg_i64) +static bool do_cmop0_d(DisasContext *s, arg_rr *a, TCGCond cond) +{ + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + tcg_gen_negsetcond_i64(cond, t, t, tcg_constant_i64(0)); + write_fp_dreg(s, a->rd, t); + } + return true; +} + +TRANS(CMGT0_s, do_cmop0_d, a, TCG_COND_GT) +TRANS(CMGE0_s, do_cmop0_d, a, TCG_COND_GE) +TRANS(CMLE0_s, do_cmop0_d, a, TCG_COND_LE) +TRANS(CMLT0_s, do_cmop0_d, a, TCG_COND_LT) +TRANS(CMEQ0_s, do_cmop0_d, a, TCG_COND_EQ) + static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) { if (!a->q && a->esz == MO_64) { @@ -8918,6 +8934,11 @@ TRANS(NEG_v, do_gvec_fn2, a, tcg_gen_gvec_neg) TRANS(NOT_v, do_gvec_fn2, a, tcg_gen_gvec_not) TRANS(CNT_v, do_gvec_fn2, a, gen_gvec_cnt) TRANS(RBIT_v, do_gvec_fn2, a, gen_gvec_rbit) +TRANS(CMGT0_v, do_gvec_fn2, a, gen_gvec_cgt0) +TRANS(CMGE0_v, do_gvec_fn2, a, gen_gvec_cge0) +TRANS(CMLT0_v, do_gvec_fn2, a, gen_gvec_clt0) +TRANS(CMLE0_v, do_gvec_fn2, a, gen_gvec_cle0) +TRANS(CMEQ0_v, do_gvec_fn2, a, gen_gvec_ceq0) static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) { @@ -9229,21 +9250,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, * The caller only need provide tcg_rmode and tcg_fpstatus if the op * requires them. */ - TCGCond cond; - switch (opcode) { - case 0xa: /* CMLT */ - cond = TCG_COND_LT; - do_cmop: - /* 64 bit integer comparison against zero, result is test ? -1 : 0. */ - tcg_gen_negsetcond_i64(cond, tcg_rd, tcg_rn, tcg_constant_i64(0)); - break; - case 0x8: /* CMGT, CMGE */ - cond = u ? TCG_COND_GE : TCG_COND_GT; - goto do_cmop; - case 0x9: /* CMEQ, CMLE */ - cond = u ? TCG_COND_LE : TCG_COND_EQ; - goto do_cmop; case 0x2f: /* FABS */ gen_vfp_absd(tcg_rd, tcg_rn); break; @@ -9290,6 +9297,9 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x4: /* CLS, CLZ */ case 0x5: /* NOT */ case 0x7: /* SQABS, SQNEG */ + case 0x8: /* CMGT, CMGE */ + case 0x9: /* CMEQ, CMLE */ + case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ g_assert_not_reached(); } @@ -9633,19 +9643,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0xa: /* CMLT */ - if (u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - if (size != 3) { - unallocated_encoding(s); - return; - } - break; case 0x12: /* SQXTUN */ if (!u) { unallocated_encoding(s); @@ -9731,6 +9728,9 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) default: case 0x3: /* USQADD / SUQADD */ case 0x7: /* SQABS / SQNEG */ + case 0x8: /* CMGT, CMGE */ + case 0x9: /* CMEQ, CMLE */ + case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ unallocated_encoding(s); return; @@ -10103,19 +10103,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } handle_shll(s, is_q, size, rn, rd); return; - case 0xa: /* CMLT */ - if (u == 1) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; case 0xc ... 0xf: case 0x16 ... 0x1f: { @@ -10289,6 +10276,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x4: /* CLS, CLZ */ case 0x5: /* CNT, NOT, RBIT */ case 0x7: /* SQABS, SQNEG */ + case 0x8: /* CMGT, CMGE */ + case 0x9: /* CMEQ, CMLE */ + case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ unallocated_encoding(s); return; @@ -10309,30 +10299,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) tcg_rmode = NULL; } - switch (opcode) { - case 0x8: /* CMGT, CMGE */ - if (u) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cge0, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cgt0, size); - } - return; - case 0x9: /* CMEQ, CMLE */ - if (u) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cle0, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_ceq0, size); - } - return; - case 0xa: /* CMLT */ - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clt0, size); - return; - case 0x4: /* CLZ, CLS */ - case 0x5: /* CNT, NOT, RBIT */ - case 0xb: - g_assert_not_reached(); - } - if (size == 3) { /* All 64-bit element operations can be shared with scalar 2misc */ int pass; From 38f9950c8e0315d7b26803018a3f73d5f42e6703 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:09 -0600 Subject: [PATCH 0246/2892] target/arm: Introduce gen_gvec_rev{16,32,64} Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-43-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 58 ++++++++++++++++++++++ target/arm/tcg/translate-neon.c | 88 +++++++-------------------------- target/arm/tcg/translate.h | 6 +++ 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 85a0b50496..33c0a94958 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2409,3 +2409,61 @@ void gen_gvec_rbit(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, gen_helper_gvec_rbit_b); } + +void gen_gvec_rev16(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_8); + tcg_gen_gvec_rotli(MO_16, rd_ofs, rn_ofs, 8, opr_sz, max_sz); +} + +static void gen_bswap32_i64(TCGv_i64 d, TCGv_i64 n) +{ + tcg_gen_bswap64_i64(d, n); + tcg_gen_rotli_i64(d, d, 32); +} + +void gen_gvec_rev32(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g = { + .fni8 = gen_bswap32_i64, + .fni4 = tcg_gen_bswap32_i32, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_32 + }; + + switch (vece) { + case MO_16: + tcg_gen_gvec_rotli(MO_32, rd_ofs, rn_ofs, 16, opr_sz, max_sz); + break; + case MO_8: + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g); + break; + default: + g_assert_not_reached(); + } +} + +void gen_gvec_rev64(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g[] = { + { .fni8 = tcg_gen_bswap64_i64, + .vece = MO_64 }, + { .fni8 = tcg_gen_hswap_i64, + .vece = MO_64 }, + }; + + switch (vece) { + case MO_32: + tcg_gen_gvec_rotli(MO_64, rd_ofs, rn_ofs, 32, opr_sz, max_sz); + break; + case MO_8: + case MO_16: + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); + break; + default: + g_assert_not_reached(); + } +} diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 50d0bf7753..ca6f5578b4 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -2565,58 +2565,6 @@ static bool trans_VDUP_scalar(DisasContext *s, arg_VDUP_scalar *a) return true; } -static bool trans_VREV64(DisasContext *s, arg_VREV64 *a) -{ - int pass, half; - TCGv_i32 tmp[2]; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { - return false; - } - - if ((a->vd | a->vm) & a->q) { - return false; - } - - if (a->size == 3) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - tmp[0] = tcg_temp_new_i32(); - tmp[1] = tcg_temp_new_i32(); - - for (pass = 0; pass < (a->q ? 2 : 1); pass++) { - for (half = 0; half < 2; half++) { - read_neon_element32(tmp[half], a->vm, pass * 2 + half, MO_32); - switch (a->size) { - case 0: - tcg_gen_bswap32_i32(tmp[half], tmp[half]); - break; - case 1: - gen_swap_half(tmp[half], tmp[half]); - break; - case 2: - break; - default: - g_assert_not_reached(); - } - } - write_neon_element32(tmp[1], a->vd, pass * 2, MO_32); - write_neon_element32(tmp[0], a->vd, pass * 2 + 1, MO_32); - } - return true; -} - static bool do_2misc_pairwise(DisasContext *s, arg_2misc *a, NeonGenWidenFn *widenfn, NeonGenTwo64OpFn *opfn, @@ -3122,6 +3070,7 @@ DO_2MISC_VEC(VCGE0, gen_gvec_cge0) DO_2MISC_VEC(VCLT0, gen_gvec_clt0) DO_2MISC_VEC(VCLS, gen_gvec_cls) DO_2MISC_VEC(VCLZ, gen_gvec_clz) +DO_2MISC_VEC(VREV64, gen_gvec_rev64) static bool trans_VMVN(DisasContext *s, arg_2misc *a) { @@ -3139,6 +3088,22 @@ static bool trans_VCNT(DisasContext *s, arg_2misc *a) return do_2misc_vec(s, a, gen_gvec_cnt); } +static bool trans_VREV16(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc_vec(s, a, gen_gvec_rev16); +} + +static bool trans_VREV32(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0 && a->size != 1) { + return false; + } + return do_2misc_vec(s, a, gen_gvec_rev32); +} + #define WRAP_2M_3_OOL_FN(WRAPNAME, FUNC, DATA) \ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ uint32_t rm_ofs, uint32_t oprsz, \ @@ -3218,25 +3183,6 @@ static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) return true; } -static bool trans_VREV32(DisasContext *s, arg_2misc *a) -{ - static NeonGenOneOpFn * const fn[] = { - tcg_gen_bswap32_i32, - gen_swap_half, - NULL, - NULL, - }; - return do_2misc(s, a, fn[a->size]); -} - -static bool trans_VREV16(DisasContext *s, arg_2misc *a) -{ - if (a->size != 0) { - return false; - } - return do_2misc(s, a, gen_rev16); -} - static void gen_VABS_F(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, uint32_t oprsz, uint32_t maxsz) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index cb8e1b2586..342ebedafc 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -586,6 +586,12 @@ void gen_gvec_cnt(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_rbit(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rev16(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rev32(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rev64(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); /* * Forward to the isar_feature_* tests given a DisasContext pointer. From 7c6bdcdaed9372601b1b366315c45029e0b2f28a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:10 -0600 Subject: [PATCH 0247/2892] target/arm: Convert handle_rev to decodetree This includes REV16, REV32, REV64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-44-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 +++ target/arm/tcg/translate-a64.c | 79 +++------------------------------- 2 files changed, 10 insertions(+), 74 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 247d3a7bda..05f1bc99b5 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -73,6 +73,7 @@ @qrr_b . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=0 @qrr_h . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=1 +@qrr_bh . q:1 ...... . esz:1 ...... ...... rn:5 rd:5 &qrr_e @qrr_e . q:1 ...... esz:2 ...... ...... rn:5 rd:5 &qrr_e @qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0 @@ -1657,3 +1658,7 @@ CMGE0_v 0.10 1110 ..1 00000 10001 0 ..... ..... @qrr_e CMEQ0_v 0.00 1110 ..1 00000 10011 0 ..... ..... @qrr_e CMLE0_v 0.10 1110 ..1 00000 10011 0 ..... ..... @qrr_e CMLT0_v 0.00 1110 ..1 00000 10101 0 ..... ..... @qrr_e + +REV16_v 0.00 1110 001 00000 00011 0 ..... ..... @qrr_b +REV32_v 0.10 1110 0.1 00000 00001 0 ..... ..... @qrr_bh +REV64_v 0.00 1110 ..1 00000 00001 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 547c6dc5cc..f57b5e2855 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8939,6 +8939,8 @@ TRANS(CMGE0_v, do_gvec_fn2, a, gen_gvec_cge0) TRANS(CMLT0_v, do_gvec_fn2, a, gen_gvec_clt0) TRANS(CMLE0_v, do_gvec_fn2, a, gen_gvec_cle0) TRANS(CMEQ0_v, do_gvec_fn2, a, gen_gvec_ceq0) +TRANS(REV16_v, do_gvec_fn2, a, gen_gvec_rev16) +TRANS(REV32_v, do_gvec_fn2, a, gen_gvec_rev32) static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) { @@ -8953,6 +8955,7 @@ static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) TRANS(CLS_v, do_gvec_fn2_bhs, a, gen_gvec_cls) TRANS(CLZ_v, do_gvec_fn2_bhs, a, gen_gvec_clz) +TRANS(REV64_v, do_gvec_fn2_bhs, a, gen_gvec_rev64) /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, @@ -9882,76 +9885,6 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, } } -static void handle_rev(DisasContext *s, int opcode, bool u, - bool is_q, int size, int rn, int rd) -{ - int op = (opcode << 1) | u; - int opsz = op + size; - int grp_size = 3 - opsz; - int dsize = is_q ? 128 : 64; - int i; - - if (opsz >= 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (size == 0) { - /* Special case bytes, use bswap op on each group of elements */ - int groups = dsize / (8 << grp_size); - - for (i = 0; i < groups; i++) { - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - read_vec_element(s, tcg_tmp, rn, i, grp_size); - switch (grp_size) { - case MO_16: - tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp, TCG_BSWAP_IZ); - break; - case MO_32: - tcg_gen_bswap32_i64(tcg_tmp, tcg_tmp, TCG_BSWAP_IZ); - break; - case MO_64: - tcg_gen_bswap64_i64(tcg_tmp, tcg_tmp); - break; - default: - g_assert_not_reached(); - } - write_vec_element(s, tcg_tmp, rd, i, grp_size); - } - clear_vec_high(s, is_q, rd); - } else { - int revmask = (1 << grp_size) - 1; - int esize = 8 << size; - int elements = dsize / esize; - TCGv_i64 tcg_rn = tcg_temp_new_i64(); - TCGv_i64 tcg_rd[2]; - - for (i = 0; i < 2; i++) { - tcg_rd[i] = tcg_temp_new_i64(); - tcg_gen_movi_i64(tcg_rd[i], 0); - } - - for (i = 0; i < elements; i++) { - int e_rev = (i & 0xf) ^ revmask; - int w = (e_rev * esize) / 64; - int o = (e_rev * esize) % 64; - - read_vec_element(s, tcg_rn, rn, i, size); - tcg_gen_deposit_i64(tcg_rd[w], tcg_rd[w], tcg_rn, o, esize); - } - - for (i = 0; i < 2; i++) { - write_vec_element(s, tcg_rd[i], rd, i, MO_64); - } - clear_vec_high(s, true, rd); - } -} - static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, bool is_q, int size, int rn, int rd) { @@ -10066,10 +9999,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0x0: /* REV64, REV32 */ - case 0x1: /* REV16 */ - handle_rev(s, opcode, u, is_q, size, rn, rd); - return; case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ if (size == 3) { @@ -10272,6 +10201,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) break; } default: + case 0x0: /* REV64, REV32 */ + case 0x1: /* REV16 */ case 0x3: /* SUQADD, USQADD */ case 0x4: /* CLS, CLZ */ case 0x5: /* CNT, NOT, RBIT */ From e90cf92209d07e8fb4564f82adc09b9fb764cf41 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:11 -0600 Subject: [PATCH 0248/2892] target/arm: Move helper_neon_addlp_{s8, s16} to neon_helper.c Move from helper-a64.c to neon_helper.c so that these functions are available for arm32 code as well. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-45-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 ++ target/arm/tcg/helper-a64.c | 43 ------------------------------------ target/arm/tcg/helper-a64.h | 2 -- target/arm/tcg/neon_helper.c | 43 ++++++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 167e331a83..57e0ce387b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -399,6 +399,8 @@ DEF_HELPER_2(neon_addl_u16, i64, i64, i64) DEF_HELPER_2(neon_addl_u32, i64, i64, i64) DEF_HELPER_2(neon_paddl_u16, i64, i64, i64) DEF_HELPER_2(neon_paddl_u32, i64, i64, i64) +DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(neon_subl_u16, i64, i64, i64) DEF_HELPER_2(neon_subl_u32, i64, i64, i64) DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 3f4d7b9aba..9b3c407be3 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -306,39 +306,6 @@ float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_three, float_muladd_halve_result, fpst); } -/* Pairwise long add: add pairs of adjacent elements into - * double-width elements in the result (eg _s8 is an 8x8->16 op) - */ -uint64_t HELPER(neon_addlp_s8)(uint64_t a) -{ - uint64_t nsignmask = 0x0080008000800080ULL; - uint64_t wsignmask = 0x8000800080008000ULL; - uint64_t elementmask = 0x00ff00ff00ff00ffULL; - uint64_t tmp1, tmp2; - uint64_t res, signres; - - /* Extract odd elements, sign extend each to a 16 bit field */ - tmp1 = a & elementmask; - tmp1 ^= nsignmask; - tmp1 |= wsignmask; - tmp1 = (tmp1 - nsignmask) ^ wsignmask; - /* Ditto for the even elements */ - tmp2 = (a >> 8) & elementmask; - tmp2 ^= nsignmask; - tmp2 |= wsignmask; - tmp2 = (tmp2 - nsignmask) ^ wsignmask; - - /* calculate the result by summing bits 0..14, 16..22, etc, - * and then adjusting the sign bits 15, 23, etc manually. - * This ensures the addition can't overflow the 16 bit field. - */ - signres = (tmp1 ^ tmp2) & wsignmask; - res = (tmp1 & ~wsignmask) + (tmp2 & ~wsignmask); - res ^= signres; - - return res; -} - uint64_t HELPER(neon_addlp_u8)(uint64_t a) { uint64_t tmp; @@ -348,16 +315,6 @@ uint64_t HELPER(neon_addlp_u8)(uint64_t a) return tmp; } -uint64_t HELPER(neon_addlp_s16)(uint64_t a) -{ - int32_t reslo, reshi; - - reslo = (int32_t)(int16_t)a + (int32_t)(int16_t)(a >> 16); - reshi = (int32_t)(int16_t)(a >> 32) + (int32_t)(int16_t)(a >> 48); - - return (uint32_t)reslo | (((uint64_t)reshi) << 32); -} - uint64_t HELPER(neon_addlp_u16)(uint64_t a) { uint64_t tmp; diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 203b7b7ac8..f811bb85dc 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -41,9 +41,7 @@ DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_u8, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_u16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 4e501925de..b92ddd4914 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -866,6 +866,49 @@ uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b) return low + ((uint64_t)high << 32); } +/* Pairwise long add: add pairs of adjacent elements into + * double-width elements in the result (eg _s8 is an 8x8->16 op) + */ +uint64_t HELPER(neon_addlp_s8)(uint64_t a) +{ + uint64_t nsignmask = 0x0080008000800080ULL; + uint64_t wsignmask = 0x8000800080008000ULL; + uint64_t elementmask = 0x00ff00ff00ff00ffULL; + uint64_t tmp1, tmp2; + uint64_t res, signres; + + /* Extract odd elements, sign extend each to a 16 bit field */ + tmp1 = a & elementmask; + tmp1 ^= nsignmask; + tmp1 |= wsignmask; + tmp1 = (tmp1 - nsignmask) ^ wsignmask; + /* Ditto for the even elements */ + tmp2 = (a >> 8) & elementmask; + tmp2 ^= nsignmask; + tmp2 |= wsignmask; + tmp2 = (tmp2 - nsignmask) ^ wsignmask; + + /* calculate the result by summing bits 0..14, 16..22, etc, + * and then adjusting the sign bits 15, 23, etc manually. + * This ensures the addition can't overflow the 16 bit field. + */ + signres = (tmp1 ^ tmp2) & wsignmask; + res = (tmp1 & ~wsignmask) + (tmp2 & ~wsignmask); + res ^= signres; + + return res; +} + +uint64_t HELPER(neon_addlp_s16)(uint64_t a) +{ + int32_t reslo, reshi; + + reslo = (int32_t)(int16_t)a + (int32_t)(int16_t)(a >> 16); + reshi = (int32_t)(int16_t)(a >> 32) + (int32_t)(int16_t)(a >> 48); + + return (uint32_t)reslo | (((uint64_t)reshi) << 32); +} + uint64_t HELPER(neon_subl_u16)(uint64_t a, uint64_t b) { uint64_t mask; From c14bde6998398d19187e9eff8a9f69ddb00fdf5b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:12 -0600 Subject: [PATCH 0249/2892] target/arm: Introduce gen_gvec_{s,u}{add,ada}lp Pairwise addition with and without accumulation. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-46-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 - target/arm/tcg/gengvec.c | 230 ++++++++++++++++++++++++++++++++ target/arm/tcg/neon_helper.c | 22 --- target/arm/tcg/translate-neon.c | 150 +-------------------- target/arm/tcg/translate.h | 9 ++ 5 files changed, 243 insertions(+), 170 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 57e0ce387b..6369d07d05 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -397,8 +397,6 @@ DEF_HELPER_1(neon_widen_s16, i64, i32) DEF_HELPER_2(neon_addl_u16, i64, i64, i64) DEF_HELPER_2(neon_addl_u32, i64, i64, i64) -DEF_HELPER_2(neon_paddl_u16, i64, i64, i64) -DEF_HELPER_2(neon_paddl_u32, i64, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(neon_subl_u16, i64, i64, i64) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 33c0a94958..2755da8ac7 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2467,3 +2467,233 @@ void gen_gvec_rev64(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, g_assert_not_reached(); } } + +static void gen_saddlp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + int half = 4 << vece; + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_shli_vec(vece, t, n, half); + tcg_gen_sari_vec(vece, d, n, half); + tcg_gen_sari_vec(vece, t, t, half); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_saddlp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_ext32s_i64(t, n); + tcg_gen_sari_i64(d, n, 32); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_saddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_shli_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2 g[] = { + { .fniv = gen_saddlp_vec, + .fni8 = gen_helper_neon_addlp_s8, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_saddlp_vec, + .fni8 = gen_helper_neon_addlp_s16, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_saddlp_vec, + .fni8 = gen_saddlp_s_i64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_sadalp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + gen_saddlp_vec(vece, t, n); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_sadalp_b_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_helper_neon_addlp_s8(t, n); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_sadalp_h_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_helper_neon_addlp_s16(t, n); + tcg_gen_vec_add32_i64(d, d, t); +} + +static void gen_sadalp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_saddlp_s_i64(t, n); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_sadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_shli_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2 g[] = { + { .fniv = gen_sadalp_vec, + .fni8 = gen_sadalp_b_i64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fniv = gen_sadalp_vec, + .fni8 = gen_sadalp_h_i64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fniv = gen_sadalp_vec, + .fni8 = gen_sadalp_s_i64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uaddlp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + int half = 4 << vece; + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_constant_vec_matching(d, vece, MAKE_64BIT_MASK(0, half)); + + tcg_gen_shri_vec(vece, t, n, half); + tcg_gen_and_vec(vece, d, n, m); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_uaddlp_b_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 m = tcg_constant_i64(dup_const(MO_16, 0xff)); + + tcg_gen_shri_i64(t, n, 8); + tcg_gen_and_i64(d, n, m); + tcg_gen_and_i64(t, t, m); + /* No carry between widened unsigned elements. */ + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaddlp_h_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 m = tcg_constant_i64(dup_const(MO_32, 0xffff)); + + tcg_gen_shri_i64(t, n, 16); + tcg_gen_and_i64(d, n, m); + tcg_gen_and_i64(t, t, m); + /* No carry between widened unsigned elements. */ + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaddlp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_ext32u_i64(t, n); + tcg_gen_shri_i64(d, n, 32); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_uaddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2 g[] = { + { .fniv = gen_uaddlp_vec, + .fni8 = gen_uaddlp_b_i64, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_uaddlp_vec, + .fni8 = gen_uaddlp_h_i64, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_uaddlp_vec, + .fni8 = gen_uaddlp_s_i64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uadalp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + gen_uaddlp_vec(vece, t, n); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_uadalp_b_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_uaddlp_b_i64(t, n); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_uadalp_h_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_uaddlp_h_i64(t, n); + tcg_gen_vec_add32_i64(d, d, t); +} + +static void gen_uadalp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_uaddlp_s_i64(t, n); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_uadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2 g[] = { + { .fniv = gen_uadalp_vec, + .fni8 = gen_uadalp_b_i64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_uadalp_vec, + .fni8 = gen_uadalp_h_i64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_uadalp_vec, + .fni8 = gen_uadalp_s_i64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index b92ddd4914..1a22857b5e 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -844,28 +844,6 @@ uint64_t HELPER(neon_addl_u32)(uint64_t a, uint64_t b) return (a + b) ^ mask; } -uint64_t HELPER(neon_paddl_u16)(uint64_t a, uint64_t b) -{ - uint64_t tmp; - uint64_t tmp2; - - tmp = a & 0x0000ffff0000ffffull; - tmp += (a >> 16) & 0x0000ffff0000ffffull; - tmp2 = b & 0xffff0000ffff0000ull; - tmp2 += (b << 16) & 0xffff0000ffff0000ull; - return ( tmp & 0xffff) - | ((tmp >> 16) & 0xffff0000ull) - | ((tmp2 << 16) & 0xffff00000000ull) - | ( tmp2 & 0xffff000000000000ull); -} - -uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b) -{ - uint32_t low = a + (a >> 32); - uint32_t high = b + (b >> 32); - return low + ((uint64_t)high << 32); -} - /* Pairwise long add: add pairs of adjacent elements into * double-width elements in the result (eg _s8 is an 8x8->16 op) */ diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index ca6f5578b4..19a18018f1 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -2565,152 +2565,6 @@ static bool trans_VDUP_scalar(DisasContext *s, arg_VDUP_scalar *a) return true; } -static bool do_2misc_pairwise(DisasContext *s, arg_2misc *a, - NeonGenWidenFn *widenfn, - NeonGenTwo64OpFn *opfn, - NeonGenTwo64OpFn *accfn) -{ - /* - * Pairwise long operations: widen both halves of the pair, - * combine the pairs with the opfn, and then possibly accumulate - * into the destination with the accfn. - */ - int pass; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { - return false; - } - - if ((a->vd | a->vm) & a->q) { - return false; - } - - if (!widenfn) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - for (pass = 0; pass < a->q + 1; pass++) { - TCGv_i32 tmp; - TCGv_i64 rm0_64, rm1_64, rd_64; - - rm0_64 = tcg_temp_new_i64(); - rm1_64 = tcg_temp_new_i64(); - rd_64 = tcg_temp_new_i64(); - - tmp = tcg_temp_new_i32(); - read_neon_element32(tmp, a->vm, pass * 2, MO_32); - widenfn(rm0_64, tmp); - read_neon_element32(tmp, a->vm, pass * 2 + 1, MO_32); - widenfn(rm1_64, tmp); - - opfn(rd_64, rm0_64, rm1_64); - - if (accfn) { - TCGv_i64 tmp64 = tcg_temp_new_i64(); - read_neon_element64(tmp64, a->vd, pass, MO_64); - accfn(rd_64, tmp64, rd_64); - } - write_neon_element64(rd_64, a->vd, pass, MO_64); - } - return true; -} - -static bool trans_VPADDL_S(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_s8, - gen_helper_neon_widen_s16, - tcg_gen_ext_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL); -} - -static bool trans_VPADDL_U(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_u8, - gen_helper_neon_widen_u16, - tcg_gen_extu_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL); -} - -static bool trans_VPADAL_S(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_s8, - gen_helper_neon_widen_s16, - tcg_gen_ext_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - static NeonGenTwo64OpFn * const accfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], - accfn[a->size]); -} - -static bool trans_VPADAL_U(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_u8, - gen_helper_neon_widen_u16, - tcg_gen_extu_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - static NeonGenTwo64OpFn * const accfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], - accfn[a->size]); -} - typedef void ZipFn(TCGv_ptr, TCGv_ptr); static bool do_zip_uzp(DisasContext *s, arg_2misc *a, @@ -3071,6 +2925,10 @@ DO_2MISC_VEC(VCLT0, gen_gvec_clt0) DO_2MISC_VEC(VCLS, gen_gvec_cls) DO_2MISC_VEC(VCLZ, gen_gvec_clz) DO_2MISC_VEC(VREV64, gen_gvec_rev64) +DO_2MISC_VEC(VPADDL_S, gen_gvec_saddlp) +DO_2MISC_VEC(VPADDL_U, gen_gvec_uaddlp) +DO_2MISC_VEC(VPADAL_S, gen_gvec_sadalp) +DO_2MISC_VEC(VPADAL_U, gen_gvec_uadalp) static bool trans_VMVN(DisasContext *s, arg_2misc *a) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 342ebedafc..edd775d564 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -593,6 +593,15 @@ void gen_gvec_rev32(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_gvec_rev64(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_saddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uaddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); + /* * Forward to the isar_feature_* tests given a DisasContext pointer. */ From 09e7f80e586af445ad70ba1d12c7ae282367b964 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:13 -0600 Subject: [PATCH 0250/2892] target/arm: Convert handle_2misc_pairwise to decodetree This includes SADDLP, UADDLP, SADALP, UADALP. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-47-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 ++ target/arm/tcg/helper-a64.c | 18 -------- target/arm/tcg/helper-a64.h | 2 - target/arm/tcg/translate-a64.c | 84 +++------------------------------- 4 files changed, 11 insertions(+), 98 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 05f1bc99b5..f3488766b2 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1662,3 +1662,8 @@ CMLT0_v 0.00 1110 ..1 00000 10101 0 ..... ..... @qrr_e REV16_v 0.00 1110 001 00000 00011 0 ..... ..... @qrr_b REV32_v 0.10 1110 0.1 00000 00001 0 ..... ..... @qrr_bh REV64_v 0.00 1110 ..1 00000 00001 0 ..... ..... @qrr_e + +SADDLP_v 0.00 1110 ..1 00000 00101 0 ..... ..... @qrr_e +UADDLP_v 0.10 1110 ..1 00000 00101 0 ..... ..... @qrr_e +SADALP_v 0.00 1110 ..1 00000 01101 0 ..... ..... @qrr_e +UADALP_v 0.10 1110 ..1 00000 01101 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 9b3c407be3..3de564e0fe 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -306,24 +306,6 @@ float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_three, float_muladd_halve_result, fpst); } -uint64_t HELPER(neon_addlp_u8)(uint64_t a) -{ - uint64_t tmp; - - tmp = a & 0x00ff00ff00ff00ffULL; - tmp += (a >> 8) & 0x00ff00ff00ff00ffULL; - return tmp; -} - -uint64_t HELPER(neon_addlp_u16)(uint64_t a) -{ - uint64_t tmp; - - tmp = a & 0x0000ffff0000ffffULL; - tmp += (a >> 16) & 0x0000ffff0000ffffULL; - return tmp; -} - /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) { diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index f811bb85dc..ac7ca190fa 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -41,8 +41,6 @@ DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_1(neon_addlp_u8, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(neon_addlp_u16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr) DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, ptr) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f57b5e2855..717d30dd5b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8956,6 +8956,10 @@ static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) TRANS(CLS_v, do_gvec_fn2_bhs, a, gen_gvec_cls) TRANS(CLZ_v, do_gvec_fn2_bhs, a, gen_gvec_clz) TRANS(REV64_v, do_gvec_fn2_bhs, a, gen_gvec_rev64) +TRANS(SADDLP_v, do_gvec_fn2_bhs, a, gen_gvec_saddlp) +TRANS(UADDLP_v, do_gvec_fn2_bhs, a, gen_gvec_uaddlp) +TRANS(SADALP_v, do_gvec_fn2_bhs, a, gen_gvec_sadalp) +TRANS(UADALP_v, do_gvec_fn2_bhs, a, gen_gvec_uadalp) /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, @@ -9885,73 +9889,6 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, } } -static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, - bool is_q, int size, int rn, int rd) -{ - /* Implement the pairwise operations from 2-misc: - * SADDLP, UADDLP, SADALP, UADALP. - * These all add pairs of elements in the input to produce a - * double-width result element in the output (possibly accumulating). - */ - bool accum = (opcode == 0x6); - int maxpass = is_q ? 2 : 1; - int pass; - TCGv_i64 tcg_res[2]; - - if (size == 2) { - /* 32 + 32 -> 64 op */ - MemOp memop = size + (u ? 0 : MO_SIGN); - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - - tcg_res[pass] = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, pass * 2, memop); - read_vec_element(s, tcg_op2, rn, pass * 2 + 1, memop); - tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2); - if (accum) { - read_vec_element(s, tcg_op1, rd, pass, MO_64); - tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_op1); - } - } - } else { - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - NeonGenOne64OpFn *genfn; - static NeonGenOne64OpFn * const fns[2][2] = { - { gen_helper_neon_addlp_s8, gen_helper_neon_addlp_u8 }, - { gen_helper_neon_addlp_s16, gen_helper_neon_addlp_u16 }, - }; - - genfn = fns[size][u]; - - tcg_res[pass] = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - genfn(tcg_res[pass], tcg_op); - - if (accum) { - read_vec_element(s, tcg_op, rd, pass, MO_64); - if (size == 0) { - gen_helper_neon_addl_u16(tcg_res[pass], - tcg_res[pass], tcg_op); - } else { - gen_helper_neon_addl_u32(tcg_res[pass], - tcg_res[pass], tcg_op); - } - } - } - } - if (!is_q) { - tcg_res[1] = tcg_constant_i64(0); - } - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } -} - static void handle_shll(DisasContext *s, bool is_q, int size, int rn, int rd) { /* Implement SHLL and SHLL2 */ @@ -10011,17 +9948,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) handle_2misc_narrow(s, false, opcode, u, is_q, size, rn, rd); return; - case 0x2: /* SADDLP, UADDLP */ - case 0x6: /* SADALP, UADALP */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_pairwise(s, opcode, u, is_q, size, rn, rd); - return; case 0x13: /* SHLL, SHLL2 */ if (u == 0 || size == 3) { unallocated_encoding(s); @@ -10203,9 +10129,11 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) default: case 0x0: /* REV64, REV32 */ case 0x1: /* REV16 */ + case 0x2: /* SADDLP, UADDLP */ case 0x3: /* SUQADD, USQADD */ case 0x4: /* CLS, CLZ */ case 0x5: /* CNT, NOT, RBIT */ + case 0x6: /* SADALP, UADALP */ case 0x7: /* SQABS, SQNEG */ case 0x8: /* CMGT, CMGE */ case 0x9: /* CMEQ, CMLE */ From 07e0d7a0c79fe1f694f668398bea7aa686659afb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:14 -0600 Subject: [PATCH 0251/2892] target/arm: Remove helper_neon_{add,sub}l_u{16,32} These have generic equivalents: tcg_gen_vec_{add,sub}{16,32}_i64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-48-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 4 ---- target/arm/tcg/neon_helper.c | 36 --------------------------------- target/arm/tcg/translate-neon.c | 22 ++++++++++---------- 3 files changed, 11 insertions(+), 51 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 6369d07d05..04e422ab08 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -395,12 +395,8 @@ DEF_HELPER_1(neon_widen_s8, i64, i32) DEF_HELPER_1(neon_widen_u16, i64, i32) DEF_HELPER_1(neon_widen_s16, i64, i32) -DEF_HELPER_2(neon_addl_u16, i64, i64, i64) -DEF_HELPER_2(neon_addl_u32, i64, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_2(neon_subl_u16, i64, i64, i64) -DEF_HELPER_2(neon_subl_u32, i64, i64, i64) DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 1a22857b5e..c687e882ad 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -826,24 +826,6 @@ uint64_t HELPER(neon_widen_s16)(uint32_t x) return ((uint32_t)(int16_t)x) | (high << 32); } -uint64_t HELPER(neon_addl_u16)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ b) & 0x8000800080008000ull; - a &= ~0x8000800080008000ull; - b &= ~0x8000800080008000ull; - return (a + b) ^ mask; -} - -uint64_t HELPER(neon_addl_u32)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ b) & 0x8000000080000000ull; - a &= ~0x8000000080000000ull; - b &= ~0x8000000080000000ull; - return (a + b) ^ mask; -} - /* Pairwise long add: add pairs of adjacent elements into * double-width elements in the result (eg _s8 is an 8x8->16 op) */ @@ -887,24 +869,6 @@ uint64_t HELPER(neon_addlp_s16)(uint64_t a) return (uint32_t)reslo | (((uint64_t)reshi) << 32); } -uint64_t HELPER(neon_subl_u16)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ ~b) & 0x8000800080008000ull; - a |= 0x8000800080008000ull; - b &= ~0x8000800080008000ull; - return (a - b) ^ mask; -} - -uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ ~b) & 0x8000000080000000ull; - a |= 0x8000000080000000ull; - b &= ~0x8000000080000000ull; - return (a - b) ^ mask; -} - uint64_t HELPER(neon_addl_saturate_s32)(CPUARMState *env, uint64_t a, uint64_t b) { uint32_t x, y; diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 19a18018f1..0821f10fad 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -1560,8 +1560,8 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, NULL, NULL, \ }; \ static NeonGenTwo64OpFn * const addfn[] = { \ - gen_helper_neon_##OP##l_u16, \ - gen_helper_neon_##OP##l_u32, \ + tcg_gen_vec_##OP##16_i64, \ + tcg_gen_vec_##OP##32_i64, \ tcg_gen_##OP##_i64, \ NULL, \ }; \ @@ -1639,8 +1639,8 @@ static bool do_narrow_3d(DisasContext *s, arg_3diff *a, static bool trans_##INSN##_3d(DisasContext *s, arg_3diff *a) \ { \ static NeonGenTwo64OpFn * const addfn[] = { \ - gen_helper_neon_##OP##l_u16, \ - gen_helper_neon_##OP##l_u32, \ + tcg_gen_vec_##OP##16_i64, \ + tcg_gen_vec_##OP##32_i64, \ tcg_gen_##OP##_i64, \ NULL, \ }; \ @@ -1761,8 +1761,8 @@ static bool trans_VABAL_S_3d(DisasContext *s, arg_3diff *a) NULL, }; static NeonGenTwo64OpFn * const addfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, + tcg_gen_vec_add16_i64, + tcg_gen_vec_add32_i64, tcg_gen_add_i64, NULL, }; @@ -1779,8 +1779,8 @@ static bool trans_VABAL_U_3d(DisasContext *s, arg_3diff *a) NULL, }; static NeonGenTwo64OpFn * const addfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, + tcg_gen_vec_add16_i64, + tcg_gen_vec_add32_i64, tcg_gen_add_i64, NULL, }; @@ -1840,8 +1840,8 @@ static bool trans_VMULL_U_3d(DisasContext *s, arg_3diff *a) NULL, \ }; \ static NeonGenTwo64OpFn * const accfn[] = { \ - gen_helper_neon_##ACC##l_u16, \ - gen_helper_neon_##ACC##l_u32, \ + tcg_gen_vec_##ACC##16_i64, \ + tcg_gen_vec_##ACC##32_i64, \ tcg_gen_##ACC##_i64, \ NULL, \ }; \ @@ -2371,7 +2371,7 @@ static bool trans_VMULL_U_2sc(DisasContext *s, arg_2scalar *a) }; \ static NeonGenTwo64OpFn * const accfn[] = { \ NULL, \ - gen_helper_neon_##ACC##l_u32, \ + tcg_gen_vec_##ACC##32_i64, \ tcg_gen_##ACC##_i64, \ NULL, \ }; \ From ace363a1e970a1f61b407f6e205caaacd7918e09 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:15 -0600 Subject: [PATCH 0252/2892] target/arm: Introduce clear_vec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a couple of places, clearing the entire vector before storing one element is the easiest solution. Wrap that into a helper function. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-49-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 717d30dd5b..0e8e867058 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -628,7 +628,16 @@ static TCGv_i32 read_fp_hreg(DisasContext *s, int reg) return v; } -/* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). +static void clear_vec(DisasContext *s, int rd) +{ + unsigned ofs = fp_reg_offset(s, rd, MO_64); + unsigned vsz = vec_full_reg_size(s); + + tcg_gen_gvec_dup_imm(MO_64, ofs, vsz, vsz, 0); +} + +/* + * Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). * If SVE is not enabled, then there are only 128 bits in the vector. */ static void clear_vec_high(DisasContext *s, bool is_q, int rd) @@ -4851,7 +4860,6 @@ static bool trans_SM3SS1(DisasContext *s, arg_SM3SS1 *a) TCGv_i32 tcg_op2 = tcg_temp_new_i32(); TCGv_i32 tcg_op3 = tcg_temp_new_i32(); TCGv_i32 tcg_res = tcg_temp_new_i32(); - unsigned vsz, dofs; read_vec_element_i32(s, tcg_op1, a->rn, 3, MO_32); read_vec_element_i32(s, tcg_op2, a->rm, 3, MO_32); @@ -4863,9 +4871,7 @@ static bool trans_SM3SS1(DisasContext *s, arg_SM3SS1 *a) tcg_gen_rotri_i32(tcg_res, tcg_res, 25); /* Clear the whole register first, then store bits [127:96]. */ - vsz = vec_full_reg_size(s); - dofs = vec_full_reg_offset(s, a->rd); - tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0); + clear_vec(s, a->rd); write_vec_element_i32(s, tcg_res, a->rd, 3, MO_32); } return true; @@ -6307,7 +6313,6 @@ static bool do_scalar_muladd_widening_idx(DisasContext *s, arg_rrx_e *a, TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - unsigned vsz, dofs; if (acc) { read_vec_element(s, t0, a->rd, 0, a->esz + 1); @@ -6317,9 +6322,7 @@ static bool do_scalar_muladd_widening_idx(DisasContext *s, arg_rrx_e *a, fn(t0, t1, t2); /* Clear the whole register first, then store scalar. */ - vsz = vec_full_reg_size(s); - dofs = vec_full_reg_offset(s, a->rd); - tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0); + clear_vec(s, a->rd); write_vec_element(s, t0, a->rd, 0, a->esz + 1); } return true; From fbbc0b2c8a17d4dc23311b613f82e725246a8022 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:16 -0600 Subject: [PATCH 0253/2892] target/arm: Convert XTN, SQXTUN, SQXTN, UQXTN to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-50-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 9 ++ target/arm/tcg/translate-a64.c | 153 ++++++++++++++++++++------------- 2 files changed, 102 insertions(+), 60 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f3488766b2..295329448f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1642,6 +1642,10 @@ CMEQ0_s 0101 1110 111 00000 10011 0 ..... ..... @rr CMLE0_s 0111 1110 111 00000 10011 0 ..... ..... @rr CMLT0_s 0101 1110 111 00000 10101 0 ..... ..... @rr +SQXTUN_s 0111 1110 ..1 00001 00101 0 ..... ..... @rr_e +SQXTN_s 0101 1110 ..1 00001 01001 0 ..... ..... @rr_e +UQXTN_s 0111 1110 ..1 00001 01001 0 ..... ..... @rr_e + # Advanced SIMD two-register miscellaneous SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e @@ -1667,3 +1671,8 @@ SADDLP_v 0.00 1110 ..1 00000 00101 0 ..... ..... @qrr_e UADDLP_v 0.10 1110 ..1 00000 00101 0 ..... ..... @qrr_e SADALP_v 0.00 1110 ..1 00000 01101 0 ..... ..... @qrr_e UADALP_v 0.10 1110 ..1 00000 01101 0 ..... ..... @qrr_e + +XTN 0.00 1110 ..1 00001 00101 0 ..... ..... @qrr_e +SQXTUN_v 0.10 1110 ..1 00001 00101 0 ..... ..... @qrr_e +SQXTN_v 0.00 1110 ..1 00001 01001 0 ..... ..... @qrr_e +UQXTN_v 0.10 1110 ..1 00001 01001 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0e8e867058..7b76945b0a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8921,6 +8921,62 @@ TRANS(CMLE0_s, do_cmop0_d, a, TCG_COND_LE) TRANS(CMLT0_s, do_cmop0_d, a, TCG_COND_LT) TRANS(CMEQ0_s, do_cmop0_d, a, TCG_COND_EQ) +static bool do_2misc_narrow_scalar(DisasContext *s, arg_rr_e *a, + ArithOneOp * const fn[3]) +{ + if (a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 t = tcg_temp_new_i64(); + + read_vec_element(s, t, a->rn, 0, a->esz + 1); + fn[a->esz](t, t); + clear_vec(s, a->rd); + write_vec_element(s, t, a->rd, 0, a->esz); + } + return true; +} + +#define WRAP_ENV(NAME) \ + static void gen_##NAME(TCGv_i64 d, TCGv_i64 n) \ + { gen_helper_##NAME(d, tcg_env, n); } + +WRAP_ENV(neon_unarrow_sat8) +WRAP_ENV(neon_unarrow_sat16) +WRAP_ENV(neon_unarrow_sat32) + +static ArithOneOp * const f_scalar_sqxtun[] = { + gen_neon_unarrow_sat8, + gen_neon_unarrow_sat16, + gen_neon_unarrow_sat32, +}; +TRANS(SQXTUN_s, do_2misc_narrow_scalar, a, f_scalar_sqxtun) + +WRAP_ENV(neon_narrow_sat_s8) +WRAP_ENV(neon_narrow_sat_s16) +WRAP_ENV(neon_narrow_sat_s32) + +static ArithOneOp * const f_scalar_sqxtn[] = { + gen_neon_narrow_sat_s8, + gen_neon_narrow_sat_s16, + gen_neon_narrow_sat_s32, +}; +TRANS(SQXTN_s, do_2misc_narrow_scalar, a, f_scalar_sqxtn) + +WRAP_ENV(neon_narrow_sat_u8) +WRAP_ENV(neon_narrow_sat_u16) +WRAP_ENV(neon_narrow_sat_u32) + +static ArithOneOp * const f_scalar_uqxtn[] = { + gen_neon_narrow_sat_u8, + gen_neon_narrow_sat_u16, + gen_neon_narrow_sat_u32, +}; +TRANS(UQXTN_s, do_2misc_narrow_scalar, a, f_scalar_uqxtn) + +#undef WRAP_ENV + static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) { if (!a->q && a->esz == MO_64) { @@ -8964,6 +9020,37 @@ TRANS(UADDLP_v, do_gvec_fn2_bhs, a, gen_gvec_uaddlp) TRANS(SADALP_v, do_gvec_fn2_bhs, a, gen_gvec_sadalp) TRANS(UADALP_v, do_gvec_fn2_bhs, a, gen_gvec_uadalp) +static bool do_2misc_narrow_vector(DisasContext *s, arg_qrr_e *a, + ArithOneOp * const fn[3]) +{ + if (a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rn, 1, MO_64); + fn[a->esz](t0, t0); + fn[a->esz](t1, t1); + write_vec_element(s, t0, a->rd, a->q ? 2 : 0, MO_32); + write_vec_element(s, t1, a->rd, a->q ? 3 : 1, MO_32); + clear_vec_high(s, a->q, a->rd); + } + return true; +} + +static ArithOneOp * const f_scalar_xtn[] = { + gen_helper_neon_narrow_u8, + gen_helper_neon_narrow_u16, + tcg_gen_ext32u_i64, +}; +TRANS(XTN, do_2misc_narrow_vector, a, f_scalar_xtn) +TRANS(SQXTUN_v, do_2misc_narrow_vector, a, f_scalar_sqxtun) +TRANS(SQXTN_v, do_2misc_narrow_vector, a, f_scalar_sqxtn) +TRANS(UQXTN_v, do_2misc_narrow_vector, a, f_scalar_uqxtn) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9546,38 +9633,6 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, tcg_res[pass] = tcg_temp_new_i64(); switch (opcode) { - case 0x12: /* XTN, SQXTUN */ - { - static NeonGenOne64OpFn * const xtnfns[3] = { - gen_helper_neon_narrow_u8, - gen_helper_neon_narrow_u16, - tcg_gen_ext32u_i64, - }; - static NeonGenOne64OpEnvFn * const sqxtunfns[3] = { - gen_helper_neon_unarrow_sat8, - gen_helper_neon_unarrow_sat16, - gen_helper_neon_unarrow_sat32, - }; - if (u) { - genenvfn = sqxtunfns[size]; - } else { - genfn = xtnfns[size]; - } - break; - } - case 0x14: /* SQXTN, UQXTN */ - { - static NeonGenOne64OpEnvFn * const fns[3][2] = { - { gen_helper_neon_narrow_sat_s8, - gen_helper_neon_narrow_sat_u8 }, - { gen_helper_neon_narrow_sat_s16, - gen_helper_neon_narrow_sat_u16 }, - { gen_helper_neon_narrow_sat_s32, - gen_helper_neon_narrow_sat_u32 }, - }; - genenvfn = fns[size][u]; - break; - } case 0x16: /* FCVTN, FCVTN2 */ /* 32 bit to 16 bit or 64 bit to 32 bit float conversion */ if (size == 2) { @@ -9618,6 +9673,8 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, } break; default: + case 0x12: /* XTN, SQXTUN */ + case 0x14: /* SQXTN, UQXTN */ g_assert_not_reached(); } @@ -9653,22 +9710,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0x12: /* SQXTUN */ - if (!u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x14: /* SQXTN, UQXTN */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, true, opcode, u, false, size, rn, rd); - return; case 0xc ... 0xf: case 0x16 ... 0x1d: case 0x1f: @@ -9742,6 +9783,8 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x9: /* CMEQ, CMLE */ case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ + case 0x12: /* SQXTUN */ + case 0x14: /* SQXTN, UQXTN */ unallocated_encoding(s); return; } @@ -9939,18 +9982,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ - case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - handle_2misc_narrow(s, false, opcode, u, is_q, size, rn, rd); - return; case 0x13: /* SHLL, SHLL2 */ if (u == 0 || size == 3) { unallocated_encoding(s); @@ -10142,6 +10173,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x9: /* CMEQ, CMLE */ case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ + case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ + case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ unallocated_encoding(s); return; } From febacdb237bdd93922c3c452083a2e840179ab6a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:17 -0600 Subject: [PATCH 0254/2892] target/arm: Convert FCVTN, BFCVTN to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-51-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 ++ target/arm/tcg/translate-a64.c | 89 ++++++++++++++++++---------------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 295329448f..456912cd7c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -21,6 +21,7 @@ %rd 0:5 %esz_sd 22:1 !function=plus_2 +%esz_hs 22:1 !function=plus_1 %esz_hsd 22:2 !function=xor_2 %hl 11:1 21:1 %hlm 11:1 20:2 @@ -74,6 +75,7 @@ @qrr_b . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=0 @qrr_h . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=1 @qrr_bh . q:1 ...... . esz:1 ...... ...... rn:5 rd:5 &qrr_e +@qrr_hs . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=%esz_hs @qrr_e . q:1 ...... esz:2 ...... ...... rn:5 rd:5 &qrr_e @qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0 @@ -1676,3 +1678,6 @@ XTN 0.00 1110 ..1 00001 00101 0 ..... ..... @qrr_e SQXTUN_v 0.10 1110 ..1 00001 00101 0 ..... ..... @qrr_e SQXTN_v 0.00 1110 ..1 00001 01001 0 ..... ..... @qrr_e UQXTN_v 0.10 1110 ..1 00001 01001 0 ..... ..... @qrr_e + +FCVTN_v 0.00 1110 0.1 00001 01101 0 ..... ..... @qrr_hs +BFCVTN_v 0.00 1110 101 00001 01101 0 ..... ..... @qrr_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 7b76945b0a..d4d19c9caa 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9051,6 +9051,49 @@ TRANS(SQXTUN_v, do_2misc_narrow_vector, a, f_scalar_sqxtun) TRANS(SQXTN_v, do_2misc_narrow_vector, a, f_scalar_sqxtn) TRANS(UQXTN_v, do_2misc_narrow_vector, a, f_scalar_uqxtn) +static void gen_fcvtn_hs(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i32 tcg_lo = tcg_temp_new_i32(); + TCGv_i32 tcg_hi = tcg_temp_new_i32(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 ahp = get_ahp_flag(); + + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, n); + gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, fpst, ahp); + gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, fpst, ahp); + tcg_gen_deposit_i32(tcg_lo, tcg_lo, tcg_hi, 16, 16); + tcg_gen_extu_i32_i64(d, tcg_lo); +} + +static void gen_fcvtn_sd(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_vfp_fcvtsd(tmp, n, tcg_env); + tcg_gen_extu_i32_i64(d, tmp); +} + +static ArithOneOp * const f_vector_fcvtn[] = { + NULL, + gen_fcvtn_hs, + gen_fcvtn_sd, +}; +TRANS(FCVTN_v, do_2misc_narrow_vector, a, f_vector_fcvtn) + +static void gen_bfcvtn_hs(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_bfcvt_pair(tmp, n, fpst); + tcg_gen_extu_i32_i64(d, tmp); +} + +static ArithOneOp * const f_vector_bfcvtn[] = { + NULL, + gen_bfcvtn_hs, + NULL, +}; +TRANS_FEAT(BFCVTN_v, aa64_bf16, do_2misc_narrow_vector, a, f_vector_bfcvtn) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9633,33 +9676,6 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, tcg_res[pass] = tcg_temp_new_i64(); switch (opcode) { - case 0x16: /* FCVTN, FCVTN2 */ - /* 32 bit to 16 bit or 64 bit to 32 bit float conversion */ - if (size == 2) { - TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_vfp_fcvtsd(tmp, tcg_op, tcg_env); - tcg_gen_extu_i32_i64(tcg_res[pass], tmp); - } else { - TCGv_i32 tcg_lo = tcg_temp_new_i32(); - TCGv_i32 tcg_hi = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 ahp = get_ahp_flag(); - - tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); - gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, fpst, ahp); - gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, fpst, ahp); - tcg_gen_deposit_i32(tcg_lo, tcg_lo, tcg_hi, 16, 16); - tcg_gen_extu_i32_i64(tcg_res[pass], tcg_lo); - } - break; - case 0x36: /* BFCVTN, BFCVTN2 */ - { - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_bfcvt_pair(tmp, tcg_op, fpst); - tcg_gen_extu_i32_i64(tcg_res[pass], tmp); - } - break; case 0x56: /* FCVTXN, FCVTXN2 */ { /* @@ -9675,6 +9691,8 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, default: case 0x12: /* XTN, SQXTUN */ case 0x14: /* SQXTN, UQXTN */ + case 0x16: /* FCVTN, FCVTN2 */ + case 0x36: /* BFCVTN, BFCVTN2 */ g_assert_not_reached(); } @@ -10088,21 +10106,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - /* fall through */ - case 0x16: /* FCVTN, FCVTN2 */ - /* handle_2misc_narrow does a 2*size -> size operation, but these - * instructions encode the source size rather than dest size. - */ - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); - return; - case 0x36: /* BFCVTN, BFCVTN2 */ - if (!dc_isar_feature(aa64_bf16, s) || size != 2) { - unallocated_encoding(s); - return; - } if (!fp_access_check(s)) { return; } @@ -10155,6 +10158,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } break; default: + case 0x16: /* FCVTN, FCVTN2 */ + case 0x36: /* BFCVTN, BFCVTN2 */ unallocated_encoding(s); return; } From 1fd9aa0b89f16343e9aa50cf1f2e84e7339646f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:18 -0600 Subject: [PATCH 0255/2892] target/arm: Convert FCVTXN to decodetree Remove handle_2misc_narrow as this was the last insn decoded by that function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-52-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++ target/arm/tcg/translate-a64.c | 101 +++++++-------------------------- 2 files changed, 24 insertions(+), 81 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 456912cd7c..d8902dfb22 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -74,6 +74,7 @@ @qrr_b . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=0 @qrr_h . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=1 +@qrr_s . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=2 @qrr_bh . q:1 ...... . esz:1 ...... ...... rn:5 rd:5 &qrr_e @qrr_hs . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=%esz_hs @qrr_e . q:1 ...... esz:2 ...... ...... rn:5 rd:5 &qrr_e @@ -1648,6 +1649,8 @@ SQXTUN_s 0111 1110 ..1 00001 00101 0 ..... ..... @rr_e SQXTN_s 0101 1110 ..1 00001 01001 0 ..... ..... @rr_e UQXTN_s 0111 1110 ..1 00001 01001 0 ..... ..... @rr_e +FCVTXN_s 0111 1110 011 00001 01101 0 ..... ..... @rr_s + # Advanced SIMD two-register miscellaneous SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e @@ -1680,4 +1683,5 @@ SQXTN_v 0.00 1110 ..1 00001 01001 0 ..... ..... @qrr_e UQXTN_v 0.10 1110 ..1 00001 01001 0 ..... ..... @qrr_e FCVTN_v 0.00 1110 0.1 00001 01101 0 ..... ..... @qrr_hs +FCVTXN_v 0.10 1110 011 00001 01101 0 ..... ..... @qrr_s BFCVTN_v 0.00 1110 101 00001 01101 0 ..... ..... @qrr_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d4d19c9caa..1c454a37f4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8975,6 +8975,24 @@ static ArithOneOp * const f_scalar_uqxtn[] = { }; TRANS(UQXTN_s, do_2misc_narrow_scalar, a, f_scalar_uqxtn) +static void gen_fcvtxn_sd(TCGv_i64 d, TCGv_i64 n) +{ + /* + * 64 bit to 32 bit float conversion + * with von Neumann rounding (round to odd) + */ + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_fcvtx_f64_to_f32(tmp, n, tcg_env); + tcg_gen_extu_i32_i64(d, tmp); +} + +static ArithOneOp * const f_scalar_fcvtxn[] = { + NULL, + NULL, + gen_fcvtxn_sd, +}; +TRANS(FCVTXN_s, do_2misc_narrow_scalar, a, f_scalar_fcvtxn) + #undef WRAP_ENV static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) @@ -9078,6 +9096,7 @@ static ArithOneOp * const f_vector_fcvtn[] = { gen_fcvtn_sd, }; TRANS(FCVTN_v, do_2misc_narrow_vector, a, f_vector_fcvtn) +TRANS(FCVTXN_v, do_2misc_narrow_vector, a, f_scalar_fcvtxn) static void gen_bfcvtn_hs(TCGv_i64 d, TCGv_i64 n) { @@ -9647,68 +9666,6 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode, } } -static void handle_2misc_narrow(DisasContext *s, bool scalar, - int opcode, bool u, bool is_q, - int size, int rn, int rd) -{ - /* Handle 2-reg-misc ops which are narrowing (so each 2*size element - * in the source becomes a size element in the destination). - */ - int pass; - TCGv_i64 tcg_res[2]; - int destelt = is_q ? 2 : 0; - int passes = scalar ? 1 : 2; - - if (scalar) { - tcg_res[1] = tcg_constant_i64(0); - } - - for (pass = 0; pass < passes; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - NeonGenOne64OpFn *genfn = NULL; - NeonGenOne64OpEnvFn *genenvfn = NULL; - - if (scalar) { - read_vec_element(s, tcg_op, rn, pass, size + 1); - } else { - read_vec_element(s, tcg_op, rn, pass, MO_64); - } - tcg_res[pass] = tcg_temp_new_i64(); - - switch (opcode) { - case 0x56: /* FCVTXN, FCVTXN2 */ - { - /* - * 64 bit to 32 bit float conversion - * with von Neumann rounding (round to odd) - */ - TCGv_i32 tmp = tcg_temp_new_i32(); - assert(size == 2); - gen_helper_fcvtx_f64_to_f32(tmp, tcg_op, tcg_env); - tcg_gen_extu_i32_i64(tcg_res[pass], tmp); - } - break; - default: - case 0x12: /* XTN, SQXTUN */ - case 0x14: /* SQXTN, UQXTN */ - case 0x16: /* FCVTN, FCVTN2 */ - case 0x36: /* BFCVTN, BFCVTN2 */ - g_assert_not_reached(); - } - - if (genfn) { - genfn(tcg_res[pass], tcg_op); - } else if (genenvfn) { - genenvfn(tcg_res[pass], tcg_env, tcg_op); - } - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, destelt + pass, MO_32); - } - clear_vec_high(s, is_q, rd); -} - /* AdvSIMD scalar two reg misc * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 * +-----+---+-----------+------+-----------+--------+-----+------+------+ @@ -9780,15 +9737,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) rmode = FPROUNDING_TIEAWAY; break; case 0x56: /* FCVTXN, FCVTXN2 */ - if (size == 2) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, true, opcode, u, false, size - 1, rn, rd); - return; default: unallocated_encoding(s); return; @@ -10101,16 +10049,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } handle_2misc_reciprocal(s, opcode, false, u, is_q, size, rn, rd); return; - case 0x56: /* FCVTXN, FCVTXN2 */ - if (size == 2) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); - return; case 0x17: /* FCVTL, FCVTL2 */ if (!fp_access_check(s)) { return; @@ -10160,6 +10098,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) default: case 0x16: /* FCVTN, FCVTN2 */ case 0x36: /* BFCVTN, BFCVTN2 */ + case 0x56: /* FCVTXN, FCVTXN2 */ unallocated_encoding(s); return; } From a05c56178af71852bf8035c24861bf1fb0e28c77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:19 -0600 Subject: [PATCH 0256/2892] target/arm: Convert SHLL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-53-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 + target/arm/tcg/translate-a64.c | 75 +++++++++++++++++----------------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d8902dfb22..ec0d46a563 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1685,3 +1685,5 @@ UQXTN_v 0.10 1110 ..1 00001 01001 0 ..... ..... @qrr_e FCVTN_v 0.00 1110 0.1 00001 01101 0 ..... ..... @qrr_hs FCVTXN_v 0.10 1110 011 00001 01101 0 ..... ..... @qrr_s BFCVTN_v 0.00 1110 101 00001 01101 0 ..... ..... @qrr_h + +SHLL_v 0.10 1110 ..1 00001 00111 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1c454a37f4..c5d456de3b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9113,6 +9113,43 @@ static ArithOneOp * const f_vector_bfcvtn[] = { }; TRANS_FEAT(BFCVTN_v, aa64_bf16, do_2misc_narrow_vector, a, f_vector_bfcvtn) +static bool trans_SHLL_v(DisasContext *s, arg_qrr_e *a) +{ + static NeonGenWidenFn * const widenfns[3] = { + gen_helper_neon_widen_u8, + gen_helper_neon_widen_u16, + tcg_gen_extu_i32_i64, + }; + NeonGenWidenFn *widenfn; + TCGv_i64 tcg_res[2]; + TCGv_i32 tcg_op; + int part, pass; + + if (a->esz == MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + tcg_op = tcg_temp_new_i32(); + widenfn = widenfns[a->esz]; + part = a->q ? 2 : 0; + + for (pass = 0; pass < 2; pass++) { + read_vec_element_i32(s, tcg_op, a->rn, part + pass, MO_32); + tcg_res[pass] = tcg_temp_new_i64(); + widenfn(tcg_res[pass], tcg_op); + tcg_gen_shli_i64(tcg_res[pass], tcg_res[pass], 8 << a->esz); + } + + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], a->rd, pass, MO_64); + } + return true; +} + + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9901,33 +9938,6 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, } } -static void handle_shll(DisasContext *s, bool is_q, int size, int rn, int rd) -{ - /* Implement SHLL and SHLL2 */ - int pass; - int part = is_q ? 2 : 0; - TCGv_i64 tcg_res[2]; - - for (pass = 0; pass < 2; pass++) { - static NeonGenWidenFn * const widenfns[3] = { - gen_helper_neon_widen_u8, - gen_helper_neon_widen_u16, - tcg_gen_extu_i32_i64, - }; - NeonGenWidenFn *widenfn = widenfns[size]; - TCGv_i32 tcg_op = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, part + pass, MO_32); - tcg_res[pass] = tcg_temp_new_i64(); - widenfn(tcg_res[pass], tcg_op); - tcg_gen_shli_i64(tcg_res[pass], tcg_res[pass], 8 << size); - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } -} - /* AdvSIMD two reg misc * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 * +---+---+---+-----------+------+-----------+--------+-----+------+------+ @@ -9948,16 +9958,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0x13: /* SHLL, SHLL2 */ - if (u == 0 || size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_shll(s, is_q, size, rn, rd); - return; case 0xc ... 0xf: case 0x16 ... 0x1f: { @@ -10118,6 +10118,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ + case 0x13: /* SHLL, SHLL2 */ case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ unallocated_encoding(s); return; From f0632d48a7391fabe3539d54ee1aec56a2fe1cb6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:20 -0600 Subject: [PATCH 0257/2892] target/arm: Implement gen_gvec_fabs, gen_gvec_fneg Move the current implementation out of translate-neon.c, and extend to handle all element sizes. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-54-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 14 ++++++++++++++ target/arm/tcg/translate-neon.c | 20 ++------------------ target/arm/tcg/translate.h | 6 ++++++ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 2755da8ac7..01c9d5436d 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2697,3 +2697,17 @@ void gen_gvec_uadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, assert(vece <= MO_32); tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); } + +void gen_gvec_fabs(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) +{ + uint64_t s_bit = 1ull << ((8 << vece) - 1); + tcg_gen_gvec_andi(vece, dofs, aofs, s_bit - 1, oprsz, maxsz); +} + +void gen_gvec_fneg(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) +{ + uint64_t s_bit = 1ull << ((8 << vece) - 1); + tcg_gen_gvec_xori(vece, dofs, aofs, s_bit, oprsz, maxsz); +} diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 0821f10fad..b9b3d1c1fb 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -3041,14 +3041,6 @@ static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) return true; } -static void gen_VABS_F(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t oprsz, uint32_t maxsz) -{ - tcg_gen_gvec_andi(vece, rd_ofs, rm_ofs, - vece == MO_16 ? 0x7fff : 0x7fffffff, - oprsz, maxsz); -} - static bool trans_VABS_F(DisasContext *s, arg_2misc *a) { if (a->size == MO_16) { @@ -3058,15 +3050,7 @@ static bool trans_VABS_F(DisasContext *s, arg_2misc *a) } else if (a->size != MO_32) { return false; } - return do_2misc_vec(s, a, gen_VABS_F); -} - -static void gen_VNEG_F(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t oprsz, uint32_t maxsz) -{ - tcg_gen_gvec_xori(vece, rd_ofs, rm_ofs, - vece == MO_16 ? 0x8000 : 0x80000000, - oprsz, maxsz); + return do_2misc_vec(s, a, gen_gvec_fabs); } static bool trans_VNEG_F(DisasContext *s, arg_2misc *a) @@ -3078,7 +3062,7 @@ static bool trans_VNEG_F(DisasContext *s, arg_2misc *a) } else if (a->size != MO_32) { return false; } - return do_2misc_vec(s, a, gen_VNEG_F); + return do_2misc_vec(s, a, gen_gvec_fneg); } static bool trans_VRECPE(DisasContext *s, arg_2misc *a) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index edd775d564..b996de2c15 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -602,6 +602,12 @@ void gen_gvec_uaddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_gvec_uadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t opr_sz, uint32_t max_sz); +/* These exclusively manipulate the sign bit. */ +void gen_gvec_fabs(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void gen_gvec_fneg(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); + /* * Forward to the isar_feature_* tests given a DisasContext pointer. */ From ea30b3522afe996b684bb1d9a5cd3c9cf1cadc7a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:21 -0600 Subject: [PATCH 0258/2892] target/arm: Convert FABS, FNEG (vector) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-55-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 +++++ target/arm/tcg/translate-a64.c | 54 +++++++++++++++------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index ec0d46a563..f46bd1a715 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -77,6 +77,7 @@ @qrr_s . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=2 @qrr_bh . q:1 ...... . esz:1 ...... ...... rn:5 rd:5 &qrr_e @qrr_hs . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=%esz_hs +@qrr_sd . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=%esz_sd @qrr_e . q:1 ...... esz:2 ...... ...... rn:5 rd:5 &qrr_e @qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0 @@ -1687,3 +1688,9 @@ FCVTXN_v 0.10 1110 011 00001 01101 0 ..... ..... @qrr_s BFCVTN_v 0.00 1110 101 00001 01101 0 ..... ..... @qrr_h SHLL_v 0.10 1110 ..1 00001 00111 0 ..... ..... @qrr_e + +FABS_v 0.00 1110 111 11000 11111 0 ..... ..... @qrr_h +FABS_v 0.00 1110 1.1 00000 11111 0 ..... ..... @qrr_sd + +FNEG_v 0.10 1110 111 11000 11111 0 ..... ..... @qrr_h +FNEG_v 0.10 1110 1.1 00000 11111 0 ..... ..... @qrr_sd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c5d456de3b..fd7f7ae714 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9149,6 +9149,20 @@ static bool trans_SHLL_v(DisasContext *s, arg_qrr_e *a) return true; } +static bool do_fabs_fneg_v(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) +{ + int check = fp_access_check_vector_hsd(s, a->q, a->esz); + + if (check <= 0) { + return check == 0; + } + + gen_gvec_fn2(s, a->q, a->rd, a->rn, fn, a->esz); + return true; +} + +TRANS(FABS_v, do_fabs_fneg_v, a, gen_gvec_fabs) +TRANS(FNEG_v, do_fabs_fneg_v, a, gen_gvec_fneg) /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, @@ -9447,12 +9461,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, * requires them. */ switch (opcode) { - case 0x2f: /* FABS */ - gen_vfp_absd(tcg_rd, tcg_rn); - break; - case 0x6f: /* FNEG */ - gen_vfp_negd(tcg_rd, tcg_rn); - break; case 0x7f: /* FSQRT */ gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_fpstatus); break; @@ -9497,6 +9505,8 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x9: /* CMEQ, CMLE */ case 0xa: /* CMLT */ case 0xb: /* ABS, NEG */ + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ g_assert_not_reached(); } } @@ -9968,13 +9978,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) opcode |= (extract32(size, 1, 1) << 5) | (u << 6); size = is_double ? 3 : 2; switch (opcode) { - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; case 0x1d: /* SCVTF */ case 0x5d: /* UCVTF */ { @@ -10099,6 +10102,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x16: /* FCVTN, FCVTN2 */ case 0x36: /* BFCVTN, BFCVTN2 */ case 0x56: /* FCVTXN, FCVTXN2 */ + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ unallocated_encoding(s); return; } @@ -10171,12 +10176,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) { /* Special cases for 32 bit elements */ switch (opcode) { - case 0x2f: /* FABS */ - gen_vfp_abss(tcg_res, tcg_op); - break; - case 0x6f: /* FNEG */ - gen_vfp_negs(tcg_res, tcg_op); - break; case 0x7f: /* FSQRT */ gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_fpstatus); break; @@ -10220,6 +10219,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) break; default: case 0x7: /* SQABS, SQNEG */ + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ g_assert_not_reached(); } } @@ -10362,17 +10363,14 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x7b: /* FCVTZU */ rmode = FPROUNDING_ZERO; break; - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - only_in_vector = true; - need_fpst = false; - break; case 0x7d: /* FRSQRTE */ break; case 0x7f: /* FSQRT (vector) */ only_in_vector = true; break; default: + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ unallocated_encoding(s); return; } @@ -10474,12 +10472,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x59: /* FRINTX */ gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, tcg_fpstatus); break; - case 0x2f: /* FABS */ - tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); - break; - case 0x6f: /* FNEG */ - tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); - break; case 0x7d: /* FRSQRTE */ gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); break; @@ -10487,6 +10479,8 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) gen_helper_vfp_sqrth(tcg_res, tcg_op, tcg_fpstatus); break; default: + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ g_assert_not_reached(); } From 73a6f0c245d613109b40a7d858775bedd49dff34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:22 -0600 Subject: [PATCH 0259/2892] target/arm: Convert FSQRT (vector) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-56-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 ++ target/arm/tcg/translate-a64.c | 69 ++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f46bd1a715..1e0eb4a748 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1694,3 +1694,6 @@ FABS_v 0.00 1110 1.1 00000 11111 0 ..... ..... @qrr_sd FNEG_v 0.10 1110 111 11000 11111 0 ..... ..... @qrr_h FNEG_v 0.10 1110 1.1 00000 11111 0 ..... ..... @qrr_sd + +FSQRT_v 0.10 1110 111 11001 11111 0 ..... ..... @qrr_h +FSQRT_v 0.10 1110 1.1 00001 11111 0 ..... ..... @qrr_sd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index fd7f7ae714..287e9338a4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9164,6 +9164,51 @@ static bool do_fabs_fneg_v(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) TRANS(FABS_v, do_fabs_fneg_v, a, gen_gvec_fabs) TRANS(FNEG_v, do_fabs_fneg_v, a, gen_gvec_fneg) +static bool do_fp1_vector(DisasContext *s, arg_qrr_e *a, + const FPScalar1 *f, int rmode) +{ + TCGv_i32 tcg_rmode = NULL; + TCGv_ptr fpst; + int check = fp_access_check_vector_hsd(s, a->q, a->esz); + + if (check <= 0) { + return check == 0; + } + + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, fpst); + } + + if (a->esz == MO_64) { + TCGv_i64 t64 = tcg_temp_new_i64(); + + for (int pass = 0; pass < 2; ++pass) { + read_vec_element(s, t64, a->rn, pass, MO_64); + f->gen_d(t64, t64, fpst); + write_vec_element(s, t64, a->rd, pass, MO_64); + } + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + void (*gen)(TCGv_i32, TCGv_i32, TCGv_ptr) + = (a->esz == MO_16 ? f->gen_h : f->gen_s); + + for (int pass = 0, n = (a->q ? 16 : 8) >> a->esz; pass < n; ++pass) { + read_vec_element_i32(s, t32, a->rn, pass, a->esz); + gen(t32, t32, fpst); + write_vec_element_i32(s, t32, a->rd, pass, a->esz); + } + } + clear_vec_high(s, a->q, a->rd); + + if (rmode >= 0) { + gen_restore_rmode(tcg_rmode, fpst); + } + return true; +} + +TRANS(FSQRT_v, do_fp1_vector, a, &f_scalar_fsqrt, -1) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9461,9 +9506,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, * requires them. */ switch (opcode) { - case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_fpstatus); - break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ case 0x1c: /* FCVTAS */ @@ -9507,6 +9549,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0xb: /* ABS, NEG */ case 0x2f: /* FABS */ case 0x6f: /* FNEG */ + case 0x7f: /* FSQRT */ g_assert_not_reached(); } } @@ -10004,13 +10047,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } handle_2misc_fcmp_zero(s, opcode, false, u, is_q, size, rn, rd); return; - case 0x7f: /* FSQRT */ - need_fpstatus = true; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ case 0x3a: /* FCVTPS */ @@ -10104,6 +10140,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x56: /* FCVTXN, FCVTXN2 */ case 0x2f: /* FABS */ case 0x6f: /* FNEG */ + case 0x7f: /* FSQRT */ unallocated_encoding(s); return; } @@ -10176,9 +10213,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) { /* Special cases for 32 bit elements */ switch (opcode) { - case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_fpstatus); - break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ case 0x1c: /* FCVTAS */ @@ -10221,6 +10255,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x7: /* SQABS, SQNEG */ case 0x2f: /* FABS */ case 0x6f: /* FNEG */ + case 0x7f: /* FSQRT */ g_assert_not_reached(); } } @@ -10365,12 +10400,10 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) break; case 0x7d: /* FRSQRTE */ break; - case 0x7f: /* FSQRT (vector) */ - only_in_vector = true; - break; default: case 0x2f: /* FABS */ case 0x6f: /* FNEG */ + case 0x7f: /* FSQRT (vector) */ unallocated_encoding(s); return; } @@ -10475,12 +10508,10 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x7d: /* FRSQRTE */ gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); break; - case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrth(tcg_res, tcg_op, tcg_fpstatus); - break; default: case 0x2f: /* FABS */ case 0x6f: /* FNEG */ + case 0x7f: /* FSQRT */ g_assert_not_reached(); } From 09facda3428d0a41b44949bd410702f2c52ac2d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:23 -0600 Subject: [PATCH 0260/2892] target/arm: Convert FRINT* (vector) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-57-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 26 +++++ target/arm/tcg/translate-a64.c | 176 ++++++++++++--------------------- 2 files changed, 88 insertions(+), 114 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 1e0eb4a748..5e02144f65 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1697,3 +1697,29 @@ FNEG_v 0.10 1110 1.1 00000 11111 0 ..... ..... @qrr_sd FSQRT_v 0.10 1110 111 11001 11111 0 ..... ..... @qrr_h FSQRT_v 0.10 1110 1.1 00001 11111 0 ..... ..... @qrr_sd + +FRINTN_v 0.00 1110 011 11001 10001 0 ..... ..... @qrr_h +FRINTN_v 0.00 1110 0.1 00001 10001 0 ..... ..... @qrr_sd + +FRINTM_v 0.00 1110 011 11001 10011 0 ..... ..... @qrr_h +FRINTM_v 0.00 1110 0.1 00001 10011 0 ..... ..... @qrr_sd + +FRINTP_v 0.00 1110 111 11001 10001 0 ..... ..... @qrr_h +FRINTP_v 0.00 1110 1.1 00001 10001 0 ..... ..... @qrr_sd + +FRINTZ_v 0.00 1110 111 11001 10011 0 ..... ..... @qrr_h +FRINTZ_v 0.00 1110 1.1 00001 10011 0 ..... ..... @qrr_sd + +FRINTA_v 0.10 1110 011 11001 10001 0 ..... ..... @qrr_h +FRINTA_v 0.10 1110 0.1 00001 10001 0 ..... ..... @qrr_sd + +FRINTX_v 0.10 1110 011 11001 10011 0 ..... ..... @qrr_h +FRINTX_v 0.10 1110 0.1 00001 10011 0 ..... ..... @qrr_sd + +FRINTI_v 0.10 1110 111 11001 10011 0 ..... ..... @qrr_h +FRINTI_v 0.10 1110 1.1 00001 10011 0 ..... ..... @qrr_sd + +FRINT32Z_v 0.00 1110 0.1 00001 11101 0 ..... ..... @qrr_sd +FRINT32X_v 0.10 1110 0.1 00001 11101 0 ..... ..... @qrr_sd +FRINT64Z_v 0.00 1110 0.1 00001 11111 0 ..... ..... @qrr_sd +FRINT64X_v 0.10 1110 0.1 00001 11111 0 ..... ..... @qrr_sd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 287e9338a4..0f924b07dc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9209,6 +9209,21 @@ static bool do_fp1_vector(DisasContext *s, arg_qrr_e *a, TRANS(FSQRT_v, do_fp1_vector, a, &f_scalar_fsqrt, -1) +TRANS(FRINTN_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_TIEEVEN) +TRANS(FRINTP_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_POSINF) +TRANS(FRINTM_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_NEGINF) +TRANS(FRINTZ_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_ZERO) +TRANS(FRINTA_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_TIEAWAY) +TRANS(FRINTI_v, do_fp1_vector, a, &f_scalar_frint, -1) +TRANS(FRINTX_v, do_fp1_vector, a, &f_scalar_frintx, -1) + +TRANS_FEAT(FRINT32Z_v, aa64_frint, do_fp1_vector, a, + &f_scalar_frint32, FPROUNDING_ZERO) +TRANS_FEAT(FRINT32X_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint32, -1) +TRANS_FEAT(FRINT64Z_v, aa64_frint, do_fp1_vector, a, + &f_scalar_frint64, FPROUNDING_ZERO) +TRANS_FEAT(FRINT64X_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint64, -1) + /* Common vector code for handling integer to FP conversion */ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, @@ -9520,25 +9535,6 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x7b: /* FCVTZU */ gen_helper_vfp_touqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); break; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - gen_helper_rintd(tcg_rd, tcg_rn, tcg_fpstatus); - break; - case 0x59: /* FRINTX */ - gen_helper_rintd_exact(tcg_rd, tcg_rn, tcg_fpstatus); - break; - case 0x1e: /* FRINT32Z */ - case 0x5e: /* FRINT32X */ - gen_helper_frint32_d(tcg_rd, tcg_rn, tcg_fpstatus); - break; - case 0x1f: /* FRINT64Z */ - case 0x5f: /* FRINT64X */ - gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus); - break; default: case 0x4: /* CLS, CLZ */ case 0x5: /* NOT */ @@ -9550,6 +9546,17 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x2f: /* FABS */ case 0x6f: /* FNEG */ case 0x7f: /* FSQRT */ + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + case 0x59: /* FRINTX */ + case 0x1e: /* FRINT32Z */ + case 0x5e: /* FRINT32X */ + case 0x1f: /* FRINT64Z */ + case 0x5f: /* FRINT64X */ g_assert_not_reached(); } } @@ -10094,46 +10101,12 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } handle_2misc_widening(s, opcode, is_q, size, rn, rd); return; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); - /* fall through */ - case 0x59: /* FRINTX */ - case 0x79: /* FRINTI */ - need_fpstatus = true; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x58: /* FRINTA */ - rmode = FPROUNDING_TIEAWAY; - need_fpstatus = true; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; case 0x7c: /* URSQRTE */ if (size == 3) { unallocated_encoding(s); return; } break; - case 0x1e: /* FRINT32Z */ - case 0x1f: /* FRINT64Z */ - rmode = FPROUNDING_ZERO; - /* fall through */ - case 0x5e: /* FRINT32X */ - case 0x5f: /* FRINT64X */ - need_fpstatus = true; - if ((size == 3 && !is_q) || !dc_isar_feature(aa64_frint, s)) { - unallocated_encoding(s); - return; - } - break; default: case 0x16: /* FCVTN, FCVTN2 */ case 0x36: /* BFCVTN, BFCVTN2 */ @@ -10141,6 +10114,17 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x2f: /* FABS */ case 0x6f: /* FNEG */ case 0x7f: /* FSQRT */ + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x59: /* FRINTX */ + case 0x79: /* FRINTI */ + case 0x58: /* FRINTA */ + case 0x1e: /* FRINT32Z */ + case 0x1f: /* FRINT64Z */ + case 0x5e: /* FRINT32X */ + case 0x5f: /* FRINT64X */ unallocated_encoding(s); return; } @@ -10229,33 +10213,25 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_helper_vfp_touls(tcg_res, tcg_op, tcg_constant_i32(0), tcg_fpstatus); break; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - gen_helper_rints(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x59: /* FRINTX */ - gen_helper_rints_exact(tcg_res, tcg_op, tcg_fpstatus); - break; case 0x7c: /* URSQRTE */ gen_helper_rsqrte_u32(tcg_res, tcg_op); break; - case 0x1e: /* FRINT32Z */ - case 0x5e: /* FRINT32X */ - gen_helper_frint32_s(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x1f: /* FRINT64Z */ - case 0x5f: /* FRINT64X */ - gen_helper_frint64_s(tcg_res, tcg_op, tcg_fpstatus); - break; default: case 0x7: /* SQABS, SQNEG */ case 0x2f: /* FABS */ case 0x6f: /* FNEG */ case 0x7f: /* FSQRT */ + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + case 0x59: /* FRINTX */ + case 0x1e: /* FRINT32Z */ + case 0x5e: /* FRINT32X */ + case 0x1f: /* FRINT64Z */ + case 0x5f: /* FRINT64X */ g_assert_not_reached(); } } @@ -10289,7 +10265,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) int rn, rd; bool is_q; bool is_scalar; - bool only_in_vector = false; int pass; TCGv_i32 tcg_rmode = NULL; @@ -10343,31 +10318,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x3d: /* FRECPE */ case 0x3f: /* FRECPX */ break; - case 0x18: /* FRINTN */ - only_in_vector = true; - rmode = FPROUNDING_TIEEVEN; - break; - case 0x19: /* FRINTM */ - only_in_vector = true; - rmode = FPROUNDING_NEGINF; - break; - case 0x38: /* FRINTP */ - only_in_vector = true; - rmode = FPROUNDING_POSINF; - break; - case 0x39: /* FRINTZ */ - only_in_vector = true; - rmode = FPROUNDING_ZERO; - break; - case 0x58: /* FRINTA */ - only_in_vector = true; - rmode = FPROUNDING_TIEAWAY; - break; - case 0x59: /* FRINTX */ - case 0x79: /* FRINTI */ - only_in_vector = true; - /* current rounding mode */ - break; case 0x1a: /* FCVTNS */ rmode = FPROUNDING_TIEEVEN; break; @@ -10404,6 +10354,13 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x2f: /* FABS */ case 0x6f: /* FNEG */ case 0x7f: /* FSQRT (vector) */ + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x59: /* FRINTX */ + case 0x79: /* FRINTI */ unallocated_encoding(s); return; } @@ -10415,11 +10372,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - /* FRINTxx is only in the vector form */ - if (only_in_vector) { - unallocated_encoding(s); - return; - } } if (!fp_access_check(s)) { @@ -10494,17 +10446,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x7b: /* FCVTZU */ gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); break; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - gen_helper_advsimd_rinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x59: /* FRINTX */ - gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, tcg_fpstatus); - break; case 0x7d: /* FRSQRTE */ gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); break; @@ -10512,6 +10453,13 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x2f: /* FABS */ case 0x6f: /* FNEG */ case 0x7f: /* FSQRT */ + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + case 0x59: /* FRINTX */ g_assert_not_reached(); } From fe78ebaae92668e8924e65e01fa562610b03acd4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:24 -0600 Subject: [PATCH 0261/2892] target/arm: Convert FCVT* (vector, integer) scalar to decodetree Arm silliness with naming, the scalar insns described as part of the vector instructions, as separate from the "regular" scalar insns which output to general registers. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-58-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 30 ++++++++ target/arm/tcg/translate-a64.c | 133 ++++++++++++++------------------- 2 files changed, 86 insertions(+), 77 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5e02144f65..f7fcc32adc 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1652,6 +1652,36 @@ UQXTN_s 0111 1110 ..1 00001 01001 0 ..... ..... @rr_e FCVTXN_s 0111 1110 011 00001 01101 0 ..... ..... @rr_s +@icvt_h . ....... .. ...... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=1 shift=0 +@icvt_sd . ....... .. ...... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=%esz_sd shift=0 + +FCVTNS_f 0101 1110 011 11001 10101 0 ..... ..... @icvt_h +FCVTNS_f 0101 1110 0.1 00001 10101 0 ..... ..... @icvt_sd +FCVTNU_f 0111 1110 011 11001 10101 0 ..... ..... @icvt_h +FCVTNU_f 0111 1110 0.1 00001 10101 0 ..... ..... @icvt_sd + +FCVTPS_f 0101 1110 111 11001 10101 0 ..... ..... @icvt_h +FCVTPS_f 0101 1110 1.1 00001 10101 0 ..... ..... @icvt_sd +FCVTPU_f 0111 1110 111 11001 10101 0 ..... ..... @icvt_h +FCVTPU_f 0111 1110 1.1 00001 10101 0 ..... ..... @icvt_sd + +FCVTMS_f 0101 1110 011 11001 10111 0 ..... ..... @icvt_h +FCVTMS_f 0101 1110 0.1 00001 10111 0 ..... ..... @icvt_sd +FCVTMU_f 0111 1110 011 11001 10111 0 ..... ..... @icvt_h +FCVTMU_f 0111 1110 0.1 00001 10111 0 ..... ..... @icvt_sd + +FCVTZS_f 0101 1110 111 11001 10111 0 ..... ..... @icvt_h +FCVTZS_f 0101 1110 1.1 00001 10111 0 ..... ..... @icvt_sd +FCVTZU_f 0111 1110 111 11001 10111 0 ..... ..... @icvt_h +FCVTZU_f 0111 1110 1.1 00001 10111 0 ..... ..... @icvt_sd + +FCVTAS_f 0101 1110 011 11001 11001 0 ..... ..... @icvt_h +FCVTAS_f 0101 1110 0.1 00001 11001 0 ..... ..... @icvt_sd +FCVTAU_f 0111 1110 011 11001 11001 0 ..... ..... @icvt_h +FCVTAU_f 0111 1110 0.1 00001 11001 0 ..... ..... @icvt_sd + # Advanced SIMD two-register miscellaneous SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0f924b07dc..71f1d6f778 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8674,6 +8674,16 @@ static void do_fcvt_scalar(DisasContext *s, MemOp out, MemOp esz, tcg_shift, tcg_fpstatus); tcg_gen_extu_i32_i64(tcg_out, tcg_single); break; + case MO_16 | MO_SIGN: + gen_helper_vfp_toshh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_16: + gen_helper_vfp_touhh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; default: g_assert_not_reached(); } @@ -8717,6 +8727,42 @@ TRANS(FCVTZU_g, do_fcvt_g, a, FPROUNDING_ZERO, false) TRANS(FCVTAS_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, true) TRANS(FCVTAU_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, false) +/* + * FCVT* (vector), scalar version. + * Which sounds weird, but really just means output to fp register + * instead of output to general register. Input and output element + * size are always equal. + */ +static bool do_fcvt_f(DisasContext *s, arg_fcvt *a, + ARMFPRounding rmode, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + tcg_int = tcg_temp_new_i64(); + do_fcvt_scalar(s, a->esz | (is_signed ? MO_SIGN : 0), + a->esz, tcg_int, a->shift, a->rn, rmode); + + clear_vec(s, a->rd); + write_vec_element(s, tcg_int, a->rd, 0, a->esz); + return true; +} + +TRANS(FCVTNS_f, do_fcvt_f, a, FPROUNDING_TIEEVEN, true) +TRANS(FCVTNU_f, do_fcvt_f, a, FPROUNDING_TIEEVEN, false) +TRANS(FCVTPS_f, do_fcvt_f, a, FPROUNDING_POSINF, true) +TRANS(FCVTPU_f, do_fcvt_f, a, FPROUNDING_POSINF, false) +TRANS(FCVTMS_f, do_fcvt_f, a, FPROUNDING_NEGINF, true) +TRANS(FCVTMU_f, do_fcvt_f, a, FPROUNDING_NEGINF, false) +TRANS(FCVTZS_f, do_fcvt_f, a, FPROUNDING_ZERO, true) +TRANS(FCVTZU_f, do_fcvt_f, a, FPROUNDING_ZERO, false) +TRANS(FCVTAS_f, do_fcvt_f, a, FPROUNDING_TIEAWAY, true) +TRANS(FCVTAU_f, do_fcvt_f, a, FPROUNDING_TIEAWAY, false) + static bool trans_FJCVTZS(DisasContext *s, arg_FJCVTZS *a) { if (!dc_isar_feature(aa64_jscvt, s)) { @@ -9776,10 +9822,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) int opcode = extract32(insn, 12, 5); int size = extract32(insn, 22, 2); bool u = extract32(insn, 29, 1); - bool is_fcvt = false; - int rmode; - TCGv_i32 tcg_rmode; - TCGv_ptr tcg_fpstatus; switch (opcode) { case 0xc ... 0xf: @@ -9824,15 +9866,8 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x5b: /* FCVTMU */ case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ - is_fcvt = true; - rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); - break; case 0x1c: /* FCVTAS */ case 0x5c: /* FCVTAU */ - /* TIEAWAY doesn't fit in the usual rounding mode encoding */ - is_fcvt = true; - rmode = FPROUNDING_TIEAWAY; - break; case 0x56: /* FCVTXN, FCVTXN2 */ default: unallocated_encoding(s); @@ -9851,59 +9886,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - - if (!fp_access_check(s)) { - return; - } - - if (is_fcvt) { - tcg_fpstatus = fpstatus_ptr(FPST_FPCR); - tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); - } else { - tcg_fpstatus = NULL; - tcg_rmode = NULL; - } - - if (size == 3) { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - - handle_2misc_64(s, opcode, u, tcg_rd, tcg_rn, tcg_rmode, tcg_fpstatus); - write_fp_dreg(s, rd, tcg_rd); - } else { - TCGv_i32 tcg_rn = tcg_temp_new_i32(); - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_rn, rn, 0, size); - - switch (opcode) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_vfp_tosls(tcg_rd, tcg_rn, tcg_constant_i32(0), - tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_vfp_touls(tcg_rd, tcg_rn, tcg_constant_i32(0), - tcg_fpstatus); - break; - default: - case 0x7: /* SQABS, SQNEG */ - g_assert_not_reached(); - } - - write_fp_sreg(s, rd, tcg_rd); - } - - if (is_fcvt) { - gen_restore_rmode(tcg_rmode, tcg_fpstatus); - } + g_assert_not_reached(); } /* AdvSIMD shift by immediate @@ -10391,30 +10374,26 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) TCGv_i32 tcg_res = tcg_temp_new_i32(); switch (fpop) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); - break; case 0x3d: /* FRECPE */ gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); break; case 0x3f: /* FRECPX */ gen_helper_frecpx_f16(tcg_res, tcg_op, tcg_fpstatus); break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + default: + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ case 0x5a: /* FCVTNU */ case 0x5b: /* FCVTMU */ case 0x5c: /* FCVTAU */ case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ - gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - default: g_assert_not_reached(); } From 6697b35e98997dc51201bab3a077446ac5c31a96 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:25 -0600 Subject: [PATCH 0262/2892] target/arm: Convert FCVT* (vector, fixed-point) scalar to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-59-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 19 +++++++++++++++++++ target/arm/tcg/translate-a64.c | 4 +--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f7fcc32adc..f66f62da4f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1682,6 +1682,25 @@ FCVTAS_f 0101 1110 0.1 00001 11001 0 ..... ..... @icvt_sd FCVTAU_f 0111 1110 011 11001 11001 0 ..... ..... @icvt_h FCVTAU_f 0111 1110 0.1 00001 11001 0 ..... ..... @icvt_sd +%fcvt_f_sh_h 16:4 !function=rsub_16 +%fcvt_f_sh_s 16:5 !function=rsub_32 +%fcvt_f_sh_d 16:6 !function=rsub_64 + +@fcvt_fixed_h .... .... . 001 .... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=1 shift=%fcvt_f_sh_h +@fcvt_fixed_s .... .... . 01 ..... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=2 shift=%fcvt_f_sh_s +@fcvt_fixed_d .... .... . 1 ...... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=3 shift=%fcvt_f_sh_d + +FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_h +FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_s +FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_d + +FCVTZU_f 0111 1111 0 ....... 111111 ..... ..... @fcvt_fixed_h +FCVTZU_f 0111 1111 0 ....... 111111 ..... ..... @fcvt_fixed_s +FCVTZU_f 0111 1111 0 ....... 111111 ..... ..... @fcvt_fixed_d + # Advanced SIMD two-register miscellaneous SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 71f1d6f778..894befef4d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9535,9 +9535,6 @@ static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn) handle_simd_shift_intfp_conv(s, true, false, is_u, immh, immb, opcode, rn, rd); break; - case 0x1f: /* FCVTZS, FCVTZU */ - handle_simd_shift_fpint_conv(s, true, false, is_u, immh, immb, rn, rd); - break; default: case 0x00: /* SSHR / USHR */ case 0x02: /* SSRA / USRA */ @@ -9551,6 +9548,7 @@ static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn) case 0x11: /* SQRSHRUN */ case 0x12: /* SQSHRN, UQSHRN */ case 0x13: /* SQRSHRN, UQRSHRN */ + case 0x1f: /* FCVTZS, FCVTZU */ unallocated_encoding(s); break; } From f469ca170d843b657a6e6e9d5b72059c3d171dc6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:26 -0600 Subject: [PATCH 0263/2892] target/arm: Convert [US]CVTF (vector, integer) scalar to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-60-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 6 ++++++ target/arm/tcg/translate-a64.c | 35 ++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f66f62da4f..146500d9c4 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1657,6 +1657,12 @@ FCVTXN_s 0111 1110 011 00001 01101 0 ..... ..... @rr_s @icvt_sd . ....... .. ...... ...... rn:5 rd:5 \ &fcvt sf=0 esz=%esz_sd shift=0 +SCVTF_f 0101 1110 011 11001 11011 0 ..... ..... @icvt_h +SCVTF_f 0101 1110 0.1 00001 11011 0 ..... ..... @icvt_sd + +UCVTF_f 0111 1110 011 11001 11011 0 ..... ..... @icvt_h +UCVTF_f 0111 1110 0.1 00001 11011 0 ..... ..... @icvt_sd + FCVTNS_f 0101 1110 011 11001 10101 0 ..... ..... @icvt_h FCVTNS_f 0101 1110 0.1 00001 10101 0 ..... ..... @icvt_sd FCVTNU_f 0111 1110 011 11001 10101 0 ..... ..... @icvt_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 894befef4d..6e9d040ebf 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8599,6 +8599,29 @@ static bool do_cvtf_g(DisasContext *s, arg_fcvt *a, bool is_signed) TRANS(SCVTF_g, do_cvtf_g, a, true) TRANS(UCVTF_g, do_cvtf_g, a, false) +/* + * [US]CVTF (vector), scalar version. + * Which sounds weird, but really just means input from fp register + * instead of input from general register. Input and output element + * size are always equal. + */ +static bool do_cvtf_f(DisasContext *s, arg_fcvt *a, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + tcg_int = tcg_temp_new_i64(); + read_vec_element(s, tcg_int, a->rn, 0, a->esz | (is_signed ? MO_SIGN : 0)); + return do_cvtf_scalar(s, a->esz, a->rd, a->shift, tcg_int, is_signed); +} + +TRANS(SCVTF_f, do_cvtf_f, a, true) +TRANS(UCVTF_f, do_cvtf_f, a, false) + static void do_fcvt_scalar(DisasContext *s, MemOp out, MemOp esz, TCGv_i64 tcg_out, int shift, int rn, ARMFPRounding rmode) @@ -9838,16 +9861,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x6d: /* FCMLE (zero) */ handle_2misc_fcmp_zero(s, opcode, true, u, true, size, rn, rd); return; - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - { - bool is_signed = (opcode == 0x1d); - if (!fp_access_check(s)) { - return; - } - handle_simd_intfp_conv(s, rd, rn, 1, is_signed, 0, size); - return; - } case 0x3d: /* FRECPE */ case 0x3f: /* FRECPX */ case 0x7d: /* FRSQRTE */ @@ -9867,6 +9880,8 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x1c: /* FCVTAS */ case 0x5c: /* FCVTAU */ case 0x56: /* FCVTXN, FCVTXN2 */ + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ default: unallocated_encoding(s); return; From 69d5391a6695aff76f62b64e73c3ac399dddfffa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:27 -0600 Subject: [PATCH 0264/2892] target/arm: Convert [US]CVTF (vector, fixed-point) scalar to decodetree Remove disas_simd_scalar_shift_imm as these were the last insns decoded by that function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-61-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 8 ++++++ target/arm/tcg/translate-a64.c | 47 ---------------------------------- 2 files changed, 8 insertions(+), 47 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 146500d9c4..30e1834d99 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1699,6 +1699,14 @@ FCVTAU_f 0111 1110 0.1 00001 11001 0 ..... ..... @icvt_sd @fcvt_fixed_d .... .... . 1 ...... ...... rn:5 rd:5 \ &fcvt sf=0 esz=3 shift=%fcvt_f_sh_d +SCVTF_f 0101 1111 0 ....... 111001 ..... ..... @fcvt_fixed_h +SCVTF_f 0101 1111 0 ....... 111001 ..... ..... @fcvt_fixed_s +SCVTF_f 0101 1111 0 ....... 111001 ..... ..... @fcvt_fixed_d + +UCVTF_f 0111 1111 0 ....... 111001 ..... ..... @fcvt_fixed_h +UCVTF_f 0111 1111 0 ....... 111001 ..... ..... @fcvt_fixed_s +UCVTF_f 0111 1111 0 ....... 111001 ..... ..... @fcvt_fixed_d + FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_h FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_s FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_d diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 6e9d040ebf..08f24908a4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9531,52 +9531,6 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, gen_restore_rmode(tcg_rmode, tcg_fpstatus); } -/* AdvSIMD scalar shift by immediate - * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0 - * +-----+---+-------------+------+------+--------+---+------+------+ - * | 0 1 | U | 1 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd | - * +-----+---+-------------+------+------+--------+---+------+------+ - * - * This is the scalar version so it works on a fixed sized registers - */ -static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 5); - int immb = extract32(insn, 16, 3); - int immh = extract32(insn, 19, 4); - bool is_u = extract32(insn, 29, 1); - - if (immh == 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0x1c: /* SCVTF, UCVTF */ - handle_simd_shift_intfp_conv(s, true, false, is_u, immh, immb, - opcode, rn, rd); - break; - default: - case 0x00: /* SSHR / USHR */ - case 0x02: /* SSRA / USRA */ - case 0x04: /* SRSHR / URSHR */ - case 0x06: /* SRSRA / URSRA */ - case 0x08: /* SRI */ - case 0x0a: /* SHL / SLI */ - case 0x0c: /* SQSHLU */ - case 0x0e: /* SQSHL, UQSHL */ - case 0x10: /* SQSHRUN */ - case 0x11: /* SQRSHRUN */ - case 0x12: /* SQSHRN, UQSHRN */ - case 0x13: /* SQRSHRN, UQRSHRN */ - case 0x1f: /* FCVTZS, FCVTZU */ - unallocated_encoding(s); - break; - } -} - static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) @@ -10476,7 +10430,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, { 0x0f000400, 0x9f800400, disas_simd_shift_imm }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, - { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x00000000, 0x00000000, NULL } }; From c2e13388165fb32e716c4c04a966a9b538493c37 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:28 -0600 Subject: [PATCH 0265/2892] target/arm: Rename helper_gvec_vcvt_[hf][su] with _rz Emphasize that these functions use round-to-zero mode. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-62-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 8 ++++---- target/arm/tcg/translate-neon.c | 8 ++++---- target/arm/tcg/vec_helper.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 04e422ab08..f2cfee40de 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -650,13 +650,13 @@ DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_fs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_fu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_hs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index b9b3d1c1fb..f9ca889bec 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -1409,13 +1409,13 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a, DO_FP_2SH(VCVT_SF, gen_helper_gvec_vcvt_sf) DO_FP_2SH(VCVT_UF, gen_helper_gvec_vcvt_uf) -DO_FP_2SH(VCVT_FS, gen_helper_gvec_vcvt_fs) -DO_FP_2SH(VCVT_FU, gen_helper_gvec_vcvt_fu) +DO_FP_2SH(VCVT_FS, gen_helper_gvec_vcvt_rz_fs) +DO_FP_2SH(VCVT_FU, gen_helper_gvec_vcvt_rz_fu) DO_FP_2SH(VCVT_SH, gen_helper_gvec_vcvt_sh) DO_FP_2SH(VCVT_UH, gen_helper_gvec_vcvt_uh) -DO_FP_2SH(VCVT_HS, gen_helper_gvec_vcvt_hs) -DO_FP_2SH(VCVT_HU, gen_helper_gvec_vcvt_hu) +DO_FP_2SH(VCVT_HS, gen_helper_gvec_vcvt_rz_hs) +DO_FP_2SH(VCVT_HU, gen_helper_gvec_vcvt_rz_hu) static bool do_1reg_imm(DisasContext *s, arg_1reg_imm *a, GVecGen2iFn *fn) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 91a9130641..ec6b640fda 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2507,12 +2507,12 @@ DO_3OP_PAIR(gvec_uminp_s, MIN, uint32_t, H4) DO_VCVT_FIXED(gvec_vcvt_sf, helper_vfp_sltos, uint32_t) DO_VCVT_FIXED(gvec_vcvt_uf, helper_vfp_ultos, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_fs, helper_vfp_tosls_round_to_zero, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_fu, helper_vfp_touls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_rz_fs, helper_vfp_tosls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_rz_fu, helper_vfp_touls_round_to_zero, uint32_t) DO_VCVT_FIXED(gvec_vcvt_sh, helper_vfp_shtoh, uint16_t) DO_VCVT_FIXED(gvec_vcvt_uh, helper_vfp_uhtoh, uint16_t) -DO_VCVT_FIXED(gvec_vcvt_hs, helper_vfp_toshh_round_to_zero, uint16_t) -DO_VCVT_FIXED(gvec_vcvt_hu, helper_vfp_touhh_round_to_zero, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_rz_hs, helper_vfp_toshh_round_to_zero, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_rz_hu, helper_vfp_touhh_round_to_zero, uint16_t) #undef DO_VCVT_FIXED From 53b9486be7e182cca15190e01d2d08647949515e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:29 -0600 Subject: [PATCH 0266/2892] target/arm: Convert [US]CVTF (vector) to decodetree Remove handle_simd_intfp_conv and handle_simd_shift_intfp_conv as these were the last insns decoded by those functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-63-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 3 + target/arm/tcg/a64.decode | 22 ++++ target/arm/tcg/translate-a64.c | 201 ++++++--------------------------- target/arm/tcg/vec_helper.c | 7 +- 4 files changed, 66 insertions(+), 167 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index f2cfee40de..b227ac54d9 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -658,6 +658,9 @@ DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 30e1834d99..4f832e7a4c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1786,3 +1786,25 @@ FRINT32Z_v 0.00 1110 0.1 00001 11101 0 ..... ..... @qrr_sd FRINT32X_v 0.10 1110 0.1 00001 11101 0 ..... ..... @qrr_sd FRINT64Z_v 0.00 1110 0.1 00001 11111 0 ..... ..... @qrr_sd FRINT64X_v 0.10 1110 0.1 00001 11111 0 ..... ..... @qrr_sd + +SCVTF_vi 0.00 1110 011 11001 11011 0 ..... ..... @qrr_h +SCVTF_vi 0.00 1110 0.1 00001 11011 0 ..... ..... @qrr_sd + +UCVTF_vi 0.10 1110 011 11001 11011 0 ..... ..... @qrr_h +UCVTF_vi 0.10 1110 0.1 00001 11011 0 ..... ..... @qrr_sd + +&fcvt_q rd rn esz q shift +@fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ + &fcvt_q esz=1 shift=%fcvt_f_sh_h +@fcvtq_s . q:1 . ...... 01 ..... ...... rn:5 rd:5 \ + &fcvt_q esz=2 shift=%fcvt_f_sh_s +@fcvtq_d . q:1 . ...... 1 ...... ...... rn:5 rd:5 \ + &fcvt_q esz=3 shift=%fcvt_f_sh_d + +SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_h +SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_s +SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_d + +UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_h +UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_s +UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_d diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 08f24908a4..0f94fa4fdc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9293,141 +9293,44 @@ TRANS_FEAT(FRINT64Z_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint64, FPROUNDING_ZERO) TRANS_FEAT(FRINT64X_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint64, -1) -/* Common vector code for handling integer to FP conversion */ -static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, - int elements, int is_signed, - int fracbits, int size) +static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, + int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3]) { - TCGv_ptr tcg_fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - TCGv_i32 tcg_shift = NULL; + int check = fp_access_check_vector_hsd(s, is_q, esz); + TCGv_ptr fpst; - MemOp mop = size | (is_signed ? MO_SIGN : 0); - int pass; - - if (fracbits || size == MO_64) { - tcg_shift = tcg_constant_i32(fracbits); + if (check <= 0) { + return check == 0; } - if (size == MO_64) { - TCGv_i64 tcg_int64 = tcg_temp_new_i64(); - TCGv_i64 tcg_double = tcg_temp_new_i64(); - - for (pass = 0; pass < elements; pass++) { - read_vec_element(s, tcg_int64, rn, pass, mop); - - if (is_signed) { - gen_helper_vfp_sqtod(tcg_double, tcg_int64, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_uqtod(tcg_double, tcg_int64, - tcg_shift, tcg_fpst); - } - if (elements == 1) { - write_fp_dreg(s, rd, tcg_double); - } else { - write_vec_element(s, tcg_double, rd, pass, MO_64); - } - } - } else { - TCGv_i32 tcg_int32 = tcg_temp_new_i32(); - TCGv_i32 tcg_float = tcg_temp_new_i32(); - - for (pass = 0; pass < elements; pass++) { - read_vec_element_i32(s, tcg_int32, rn, pass, mop); - - switch (size) { - case MO_32: - if (fracbits) { - if (is_signed) { - gen_helper_vfp_sltos(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_ultos(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } - } else { - if (is_signed) { - gen_helper_vfp_sitos(tcg_float, tcg_int32, tcg_fpst); - } else { - gen_helper_vfp_uitos(tcg_float, tcg_int32, tcg_fpst); - } - } - break; - case MO_16: - if (fracbits) { - if (is_signed) { - gen_helper_vfp_sltoh(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_ultoh(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } - } else { - if (is_signed) { - gen_helper_vfp_sitoh(tcg_float, tcg_int32, tcg_fpst); - } else { - gen_helper_vfp_uitoh(tcg_float, tcg_int32, tcg_fpst); - } - } - break; - default: - g_assert_not_reached(); - } - - if (elements == 1) { - write_fp_sreg(s, rd, tcg_float); - } else { - write_vec_element_i32(s, tcg_float, rd, pass, size); - } - } - } - - clear_vec_high(s, elements << size == 16, rd); + fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), + data, fns[esz - 1]); + return true; } -/* UCVTF/SCVTF - Integer to FP conversion */ -static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, - bool is_q, bool is_u, - int immh, int immb, int opcode, - int rn, int rd) -{ - int size, elements, fracbits; - int immhb = immh << 3 | immb; +static gen_helper_gvec_2_ptr * const f_scvtf_v[] = { + gen_helper_gvec_vcvt_sh, + gen_helper_gvec_vcvt_sf, + gen_helper_gvec_vcvt_sd, +}; +TRANS(SCVTF_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, 0, f_scvtf_v) +TRANS(SCVTF_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_scvtf_v) - if (immh & 8) { - size = MO_64; - if (!is_scalar && !is_q) { - unallocated_encoding(s); - return; - } - } else if (immh & 4) { - size = MO_32; - } else if (immh & 2) { - size = MO_16; - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - } else { - /* immh == 0 would be a failure of the decode logic */ - g_assert(immh == 1); - unallocated_encoding(s); - return; - } - - if (is_scalar) { - elements = 1; - } else { - elements = (8 << is_q) >> size; - } - fracbits = (16 << size) - immhb; - - if (!fp_access_check(s)) { - return; - } - - handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size); -} +static gen_helper_gvec_2_ptr * const f_ucvtf_v[] = { + gen_helper_gvec_vcvt_uh, + gen_helper_gvec_vcvt_uf, + gen_helper_gvec_vcvt_ud, +}; +TRANS(UCVTF_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, 0, f_ucvtf_v) +TRANS(UCVTF_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_ucvtf_v) /* FCVTZS, FVCVTZU - FP to fixedpoint conversion */ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, @@ -9878,10 +9781,6 @@ static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x1c: /* SCVTF / UCVTF */ - handle_simd_shift_intfp_conv(s, false, is_q, is_u, immh, immb, - opcode, rn, rd); - break; case 0x1f: /* FCVTZS/ FCVTZU */ handle_simd_shift_fpint_conv(s, false, is_q, is_u, immh, immb, rn, rd); return; @@ -9899,6 +9798,7 @@ static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) case 0x12: /* SQSHRN / UQSHRN */ case 0x13: /* SQRSHRN / UQRSHRN */ case 0x14: /* SSHLL / USHLL */ + case 0x1c: /* SCVTF / UCVTF */ unallocated_encoding(s); return; } @@ -9978,21 +9878,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) opcode |= (extract32(size, 1, 1) << 5) | (u << 6); size = is_double ? 3 : 2; switch (opcode) { - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - { - bool is_signed = (opcode == 0x1d) ? true : false; - int elements = is_double ? 2 : is_q ? 4 : 2; - if (is_double && !is_q) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_simd_intfp_conv(s, rd, rn, elements, is_signed, 0, size); - return; - } case 0x2c: /* FCMGT (zero) */ case 0x2d: /* FCMEQ (zero) */ case 0x2e: /* FCMLT (zero) */ @@ -10075,6 +9960,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x1f: /* FRINT64Z */ case 0x5e: /* FRINT32X */ case 0x5f: /* FRINT64X */ + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ unallocated_encoding(s); return; } @@ -10240,24 +10127,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) fpop = deposit32(fpop, 6, 1, u); switch (fpop) { - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - { - int elements; - - if (is_scalar) { - elements = 1; - } else { - elements = (is_q ? 8 : 4); - } - - if (!fp_access_check(s)) { - return; - } - handle_simd_intfp_conv(s, rd, rn, elements, !u, 0, MO_16); - return; - } - break; case 0x2c: /* FCMGT (zero) */ case 0x2d: /* FCMEQ (zero) */ case 0x2e: /* FCMLT (zero) */ @@ -10311,6 +10180,8 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x58: /* FRINTA */ case 0x59: /* FRINTX */ case 0x79: /* FRINTI */ + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ unallocated_encoding(s); return; } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index ec6b640fda..07f8d5f467 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2505,12 +2505,15 @@ DO_3OP_PAIR(gvec_uminp_s, MIN, uint32_t, H4) clear_tail(d, oprsz, simd_maxsz(desc)); \ } +DO_VCVT_FIXED(gvec_vcvt_sd, helper_vfp_sqtod, uint64_t) +DO_VCVT_FIXED(gvec_vcvt_ud, helper_vfp_uqtod, uint64_t) DO_VCVT_FIXED(gvec_vcvt_sf, helper_vfp_sltos, uint32_t) DO_VCVT_FIXED(gvec_vcvt_uf, helper_vfp_ultos, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_rz_fs, helper_vfp_tosls_round_to_zero, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_rz_fu, helper_vfp_touls_round_to_zero, uint32_t) DO_VCVT_FIXED(gvec_vcvt_sh, helper_vfp_shtoh, uint16_t) DO_VCVT_FIXED(gvec_vcvt_uh, helper_vfp_uhtoh, uint16_t) + +DO_VCVT_FIXED(gvec_vcvt_rz_fs, helper_vfp_tosls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_rz_fu, helper_vfp_touls_round_to_zero, uint32_t) DO_VCVT_FIXED(gvec_vcvt_rz_hs, helper_vfp_toshh_round_to_zero, uint16_t) DO_VCVT_FIXED(gvec_vcvt_rz_hu, helper_vfp_touhh_round_to_zero, uint16_t) From 9a93223c86c6721a9c868085dae28698852bb8d2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:30 -0600 Subject: [PATCH 0267/2892] target/arm: Convert FCVTZ[SU] (vector, fixed-point) to decodetree Remove handle_simd_shift_fpint_conv and disas_simd_shift_imm as these were the last insns decoded by those functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-64-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 4 + target/arm/tcg/a64.decode | 8 ++ target/arm/tcg/translate-a64.c | 160 +++------------------------------ target/arm/tcg/vec_helper.c | 2 + target/arm/vfp_helper.c | 4 + 5 files changed, 32 insertions(+), 146 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index b227ac54d9..0c8a56c3ae 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -178,8 +178,10 @@ DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, ptr) DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, ptr) DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_tosqd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_touqd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) @@ -660,6 +662,8 @@ DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 4f832e7a4c..61d519b96a 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1808,3 +1808,11 @@ SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_d UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_h UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_s UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_d + +FCVTZS_vf 0.00 11110 ....... 111111 ..... ..... @fcvtq_h +FCVTZS_vf 0.00 11110 ....... 111111 ..... ..... @fcvtq_s +FCVTZS_vf 0.00 11110 ....... 111111 ..... ..... @fcvtq_d + +FCVTZU_vf 0.10 11110 ....... 111111 ..... ..... @fcvtq_h +FCVTZU_vf 0.10 11110 ....... 111111 ..... ..... @fcvtq_s +FCVTZU_vf 0.10 11110 ....... 111111 ..... ..... @fcvtq_d diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0f94fa4fdc..1c4e53770b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9332,107 +9332,21 @@ TRANS(UCVTF_vi, do_gvec_op2_fpst, TRANS(UCVTF_vf, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, a->shift, f_ucvtf_v) -/* FCVTZS, FVCVTZU - FP to fixedpoint conversion */ -static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, - bool is_q, bool is_u, - int immh, int immb, int rn, int rd) -{ - int immhb = immh << 3 | immb; - int pass, size, fracbits; - TCGv_ptr tcg_fpstatus; - TCGv_i32 tcg_rmode, tcg_shift; +static gen_helper_gvec_2_ptr * const f_fcvtzs_vf[] = { + gen_helper_gvec_vcvt_rz_hs, + gen_helper_gvec_vcvt_rz_fs, + gen_helper_gvec_vcvt_rz_ds, +}; +TRANS(FCVTZS_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_fcvtzs_vf) - if (immh & 0x8) { - size = MO_64; - if (!is_scalar && !is_q) { - unallocated_encoding(s); - return; - } - } else if (immh & 0x4) { - size = MO_32; - } else if (immh & 0x2) { - size = MO_16; - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - } else { - /* Should have split out AdvSIMD modified immediate earlier. */ - assert(immh == 1); - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - assert(!(is_scalar && is_q)); - - tcg_fpstatus = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, tcg_fpstatus); - fracbits = (16 << size) - immhb; - tcg_shift = tcg_constant_i32(fracbits); - - if (size == MO_64) { - int maxpass = is_scalar ? 1 : 2; - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - if (is_u) { - gen_helper_vfp_touqd(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_tosqd(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } - write_vec_element(s, tcg_op, rd, pass, MO_64); - } - clear_vec_high(s, is_q, rd); - } else { - void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); - int maxpass = is_scalar ? 1 : ((8 << is_q) >> size); - - switch (size) { - case MO_16: - if (is_u) { - fn = gen_helper_vfp_touhh; - } else { - fn = gen_helper_vfp_toshh; - } - break; - case MO_32: - if (is_u) { - fn = gen_helper_vfp_touls; - } else { - fn = gen_helper_vfp_tosls; - } - break; - default: - g_assert_not_reached(); - } - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, size); - fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - if (is_scalar) { - if (size == MO_16 && !is_u) { - tcg_gen_ext16u_i32(tcg_op, tcg_op); - } - write_fp_sreg(s, rd, tcg_op); - } else { - write_vec_element_i32(s, tcg_op, rd, pass, size); - } - } - if (!is_scalar) { - clear_vec_high(s, is_q, rd); - } - } - - gen_restore_rmode(tcg_rmode, tcg_fpstatus); -} +static gen_helper_gvec_2_ptr * const f_fcvtzu_vf[] = { + gen_helper_gvec_vcvt_rz_hu, + gen_helper_gvec_vcvt_rz_fu, + gen_helper_gvec_vcvt_rz_du, +}; +TRANS(FCVTZU_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_fcvtzu_vf) static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, @@ -9759,51 +9673,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) g_assert_not_reached(); } -/* AdvSIMD shift by immediate - * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0 - * +---+---+---+-------------+------+------+--------+---+------+------+ - * | 0 | Q | U | 0 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd | - * +---+---+---+-------------+------+------+--------+---+------+------+ - */ -static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 5); - int immb = extract32(insn, 16, 3); - int immh = extract32(insn, 19, 4); - bool is_u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - - if (immh == 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0x1f: /* FCVTZS/ FCVTZU */ - handle_simd_shift_fpint_conv(s, false, is_q, is_u, immh, immb, rn, rd); - return; - default: - case 0x00: /* SSHR / USHR */ - case 0x02: /* SSRA / USRA (accumulate) */ - case 0x04: /* SRSHR / URSHR (rounding) */ - case 0x06: /* SRSRA / URSRA (accum + rounding) */ - case 0x08: /* SRI */ - case 0x0a: /* SHL / SLI */ - case 0x0c: /* SQSHLU */ - case 0x0e: /* SQSHL, UQSHL */ - case 0x10: /* SHRN / SQSHRUN */ - case 0x11: /* RSHRN / SQRSHRUN */ - case 0x12: /* SQSHRN / UQSHRN */ - case 0x13: /* SQRSHRN / UQRSHRN */ - case 0x14: /* SSHLL / USHLL */ - case 0x1c: /* SCVTF / UCVTF */ - unallocated_encoding(s); - return; - } -} - static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -10299,7 +10168,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) static const AArch64DecodeTable data_proc_simd[] = { /* pattern , mask , fn */ { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, - { 0x0f000400, 0x9f800400, disas_simd_shift_imm }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x00000000, 0x00000000, NULL } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 07f8d5f467..b5ab8d1e15 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2512,6 +2512,8 @@ DO_VCVT_FIXED(gvec_vcvt_uf, helper_vfp_ultos, uint32_t) DO_VCVT_FIXED(gvec_vcvt_sh, helper_vfp_shtoh, uint16_t) DO_VCVT_FIXED(gvec_vcvt_uh, helper_vfp_uhtoh, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_rz_ds, helper_vfp_tosqd_round_to_zero, uint64_t) +DO_VCVT_FIXED(gvec_vcvt_rz_du, helper_vfp_touqd_round_to_zero, uint64_t) DO_VCVT_FIXED(gvec_vcvt_rz_fs, helper_vfp_tosls_round_to_zero, uint32_t) DO_VCVT_FIXED(gvec_vcvt_rz_fu, helper_vfp_touls_round_to_zero, uint32_t) DO_VCVT_FIXED(gvec_vcvt_rz_hs, helper_vfp_toshh_round_to_zero, uint16_t) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index f24992c798..5a19af509c 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -495,6 +495,10 @@ VFP_CONV_FIX_A64(sq, h, 16, dh_ctype_f16, 64, int64) VFP_CONV_FIX(uh, h, 16, dh_ctype_f16, 32, uint16) VFP_CONV_FIX(ul, h, 16, dh_ctype_f16, 32, uint32) VFP_CONV_FIX_A64(uq, h, 16, dh_ctype_f16, 64, uint64) +VFP_CONV_FLOAT_FIX_ROUND(sq, d, 64, float64, 64, int64, + float_round_to_zero, _round_to_zero) +VFP_CONV_FLOAT_FIX_ROUND(uq, d, 64, float64, 64, uint64, + float_round_to_zero, _round_to_zero) #undef VFP_CONV_FIX #undef VFP_CONV_FIX_FLOAT From 475dbea47d57102682a16c798b200df7e16c0776 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:31 -0600 Subject: [PATCH 0268/2892] target/arm: Convert FCVT* (vector, integer) to decodetree Remove handle_2misc_64 as these were the last insns decoded by that function. Remove helper_advsimd_f16to[su]inth as unused; we now always go through helper_vfp_to[su]hh or a specialized vector function instead. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-65-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 + target/arm/tcg/a64.decode | 25 ++++ target/arm/tcg/helper-a64.c | 32 ----- target/arm/tcg/helper-a64.h | 2 - target/arm/tcg/translate-a64.c | 227 +++++++++++---------------------- target/arm/tcg/vec_helper.c | 2 + 6 files changed, 102 insertions(+), 188 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 0c8a56c3ae..64aa603465 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -665,6 +665,8 @@ DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ud, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 61d519b96a..05a0b84416 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1793,6 +1793,31 @@ SCVTF_vi 0.00 1110 0.1 00001 11011 0 ..... ..... @qrr_sd UCVTF_vi 0.10 1110 011 11001 11011 0 ..... ..... @qrr_h UCVTF_vi 0.10 1110 0.1 00001 11011 0 ..... ..... @qrr_sd +FCVTNS_vi 0.00 1110 011 11001 10101 0 ..... ..... @qrr_h +FCVTNS_vi 0.00 1110 0.1 00001 10101 0 ..... ..... @qrr_sd +FCVTNU_vi 0.10 1110 011 11001 10101 0 ..... ..... @qrr_h +FCVTNU_vi 0.10 1110 0.1 00001 10101 0 ..... ..... @qrr_sd + +FCVTPS_vi 0.00 1110 111 11001 10101 0 ..... ..... @qrr_h +FCVTPS_vi 0.00 1110 1.1 00001 10101 0 ..... ..... @qrr_sd +FCVTPU_vi 0.10 1110 111 11001 10101 0 ..... ..... @qrr_h +FCVTPU_vi 0.10 1110 1.1 00001 10101 0 ..... ..... @qrr_sd + +FCVTMS_vi 0.00 1110 011 11001 10111 0 ..... ..... @qrr_h +FCVTMS_vi 0.00 1110 0.1 00001 10111 0 ..... ..... @qrr_sd +FCVTMU_vi 0.10 1110 011 11001 10111 0 ..... ..... @qrr_h +FCVTMU_vi 0.10 1110 0.1 00001 10111 0 ..... ..... @qrr_sd + +FCVTZS_vi 0.00 1110 111 11001 10111 0 ..... ..... @qrr_h +FCVTZS_vi 0.00 1110 1.1 00001 10111 0 ..... ..... @qrr_sd +FCVTZU_vi 0.10 1110 111 11001 10111 0 ..... ..... @qrr_h +FCVTZU_vi 0.10 1110 1.1 00001 10111 0 ..... ..... @qrr_sd + +FCVTAS_vi 0.00 1110 011 11001 11001 0 ..... ..... @qrr_h +FCVTAS_vi 0.00 1110 0.1 00001 11001 0 ..... ..... @qrr_sd +FCVTAU_vi 0.10 1110 011 11001 11001 0 ..... ..... @qrr_h +FCVTAU_vi 0.10 1110 0.1 00001 11001 0 ..... ..... @qrr_sd + &fcvt_q rd rn esz q shift @fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ &fcvt_q esz=1 shift=%fcvt_f_sh_h diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 3de564e0fe..28de7468cd 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -618,38 +618,6 @@ uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) return ret; } -/* - * Half-precision floating point conversion functions - * - * There are a multitude of conversion functions with various - * different rounding modes. This is dealt with by the calling code - * setting the mode appropriately before calling the helper. - */ - -uint32_t HELPER(advsimd_f16tosinth)(uint32_t a, void *fpstp) -{ - float_status *fpst = fpstp; - - /* Invalid if we are passed a NaN */ - if (float16_is_any_nan(a)) { - float_raise(float_flag_invalid, fpst); - return 0; - } - return float16_to_int16(a, fpst); -} - -uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) -{ - float_status *fpst = fpstp; - - /* Invalid if we are passed a NaN */ - if (float16_is_any_nan(a)) { - float_raise(float_flag_invalid, fpst); - return 0; - } - return float16_to_uint16(a, fpst); -} - static int el_from_spsr(uint32_t spsr) { /* Return the exception level that this SPSR is requesting a return to, diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index ac7ca190fa..3c0774139b 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -74,8 +74,6 @@ DEF_HELPER_3(advsimd_mulx2h, i32, i32, i32, ptr) DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, ptr) DEF_HELPER_2(advsimd_rinth_exact, f16, f16, ptr) DEF_HELPER_2(advsimd_rinth, f16, f16, ptr) -DEF_HELPER_2(advsimd_f16tosinth, i32, f16, ptr) -DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr) DEF_HELPER_2(exception_return, void, env, i64) DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1c4e53770b..ec1ce44c4b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9348,56 +9348,38 @@ static gen_helper_gvec_2_ptr * const f_fcvtzu_vf[] = { TRANS(FCVTZU_vf, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, a->shift, f_fcvtzu_vf) -static void handle_2misc_64(DisasContext *s, int opcode, bool u, - TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, - TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) -{ - /* Handle 64->64 opcodes which are shared between the scalar and - * vector 2-reg-misc groups. We cover every integer opcode where size == 3 - * is valid in either group and also the double-precision fp ops. - * The caller only need provide tcg_rmode and tcg_fpstatus if the op - * requires them. - */ - switch (opcode) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_vfp_tosqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_vfp_touqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); - break; - default: - case 0x4: /* CLS, CLZ */ - case 0x5: /* NOT */ - case 0x7: /* SQABS, SQNEG */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - case 0xa: /* CMLT */ - case 0xb: /* ABS, NEG */ - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - case 0x7f: /* FSQRT */ - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - case 0x59: /* FRINTX */ - case 0x1e: /* FRINT32Z */ - case 0x5e: /* FRINT32X */ - case 0x1f: /* FRINT64Z */ - case 0x5f: /* FRINT64X */ - g_assert_not_reached(); - } -} +static gen_helper_gvec_2_ptr * const f_fcvt_s_vi[] = { + gen_helper_gvec_vcvt_rm_sh, + gen_helper_gvec_vcvt_rm_ss, + gen_helper_gvec_vcvt_rm_sd, +}; + +static gen_helper_gvec_2_ptr * const f_fcvt_u_vi[] = { + gen_helper_gvec_vcvt_rm_uh, + gen_helper_gvec_vcvt_rm_us, + gen_helper_gvec_vcvt_rm_ud, +}; + +TRANS(FCVTNS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_nearest_even, f_fcvt_s_vi) +TRANS(FCVTNU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_nearest_even, f_fcvt_u_vi) +TRANS(FCVTPS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_up, f_fcvt_s_vi) +TRANS(FCVTPU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_up, f_fcvt_u_vi) +TRANS(FCVTMS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_down, f_fcvt_s_vi) +TRANS(FCVTMU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_down, f_fcvt_u_vi) +TRANS(FCVTZS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_to_zero, f_fcvt_s_vi) +TRANS(FCVTZU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_to_zero, f_fcvt_u_vi) +TRANS(FCVTAS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_ties_away, f_fcvt_s_vi) +TRANS(FCVTAU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_ties_away, f_fcvt_u_vi) static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, bool is_scalar, bool is_u, bool is_q, @@ -9758,30 +9740,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } handle_2misc_fcmp_zero(s, opcode, false, u, is_q, size, rn, rd); return; - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - need_fpstatus = true; - rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x5c: /* FCVTAU */ - case 0x1c: /* FCVTAS */ - need_fpstatus = true; - rmode = FPROUNDING_TIEAWAY; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; case 0x3c: /* URECPE */ if (size == 3) { unallocated_encoding(s); @@ -9831,6 +9789,16 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x5f: /* FRINT64X */ case 0x1d: /* SCVTF */ case 0x5d: /* UCVTF */ + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + case 0x5c: /* FCVTAU */ + case 0x1c: /* FCVTAS */ unallocated_encoding(s); return; } @@ -9871,26 +9839,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) tcg_rmode = NULL; } - if (size == 3) { - /* All 64-bit element operations can be shared with scalar 2misc */ - int pass; - - /* Coverity claims (size == 3 && !is_q) has been eliminated - * from all paths leading to here. - */ - tcg_debug_assert(is_q); - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - - handle_2misc_64(s, opcode, u, tcg_res, tcg_op, - tcg_rmode, tcg_fpstatus); - - write_vec_element(s, tcg_res, rd, pass, MO_64); - } - } else { + { int pass; assert(size == 2); @@ -9903,22 +9852,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) { /* Special cases for 32 bit elements */ switch (opcode) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_vfp_tosls(tcg_res, tcg_op, - tcg_constant_i32(0), tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_vfp_touls(tcg_res, tcg_op, - tcg_constant_i32(0), tcg_fpstatus); - break; case 0x7c: /* URSQRTE */ gen_helper_rsqrte_u32(tcg_res, tcg_op); break; @@ -9938,6 +9871,16 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x5e: /* FRINT32X */ case 0x1f: /* FRINT64Z */ case 0x5f: /* FRINT64X */ + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ g_assert_not_reached(); } } @@ -10006,36 +9949,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x3d: /* FRECPE */ case 0x3f: /* FRECPX */ break; - case 0x1a: /* FCVTNS */ - rmode = FPROUNDING_TIEEVEN; - break; - case 0x1b: /* FCVTMS */ - rmode = FPROUNDING_NEGINF; - break; - case 0x1c: /* FCVTAS */ - rmode = FPROUNDING_TIEAWAY; - break; - case 0x3a: /* FCVTPS */ - rmode = FPROUNDING_POSINF; - break; - case 0x3b: /* FCVTZS */ - rmode = FPROUNDING_ZERO; - break; - case 0x5a: /* FCVTNU */ - rmode = FPROUNDING_TIEEVEN; - break; - case 0x5b: /* FCVTMU */ - rmode = FPROUNDING_NEGINF; - break; - case 0x5c: /* FCVTAU */ - rmode = FPROUNDING_TIEAWAY; - break; - case 0x7a: /* FCVTPU */ - rmode = FPROUNDING_POSINF; - break; - case 0x7b: /* FCVTZU */ - rmode = FPROUNDING_ZERO; - break; case 0x7d: /* FRSQRTE */ break; default: @@ -10051,6 +9964,16 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x79: /* FRINTI */ case 0x1d: /* SCVTF */ case 0x5d: /* UCVTF */ + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ unallocated_encoding(s); return; } @@ -10115,23 +10038,9 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op, rn, pass, MO_16); switch (fpop) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); - break; case 0x3d: /* FRECPE */ gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); - break; case 0x7d: /* FRSQRTE */ gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); break; @@ -10146,6 +10055,16 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x58: /* FRINTA */ case 0x79: /* FRINTI */ case 0x59: /* FRINTX */ + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ g_assert_not_reached(); } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index b5ab8d1e15..bc752ff988 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2537,6 +2537,8 @@ DO_VCVT_FIXED(gvec_vcvt_rz_hu, helper_vfp_touhh_round_to_zero, uint16_t) clear_tail(d, oprsz, simd_maxsz(desc)); \ } +DO_VCVT_RMODE(gvec_vcvt_rm_sd, helper_vfp_tosqd, uint64_t) +DO_VCVT_RMODE(gvec_vcvt_rm_ud, helper_vfp_touqd, uint64_t) DO_VCVT_RMODE(gvec_vcvt_rm_ss, helper_vfp_tosls, uint32_t) DO_VCVT_RMODE(gvec_vcvt_rm_us, helper_vfp_touls, uint32_t) DO_VCVT_RMODE(gvec_vcvt_rm_sh, helper_vfp_toshh, uint16_t) From df112a2578e94ffb36e0c6d091056bf708a7c24d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:32 -0600 Subject: [PATCH 0269/2892] target/arm: Convert handle_2misc_fcmp_zero to decodetree This includes FCMEQ, FCMGT, FCMGE, FCMLT, FCMLE. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-66-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 5 + target/arm/tcg/a64.decode | 30 ++++ target/arm/tcg/translate-a64.c | 249 +++++++++++++-------------------- target/arm/tcg/vec_helper.c | 4 +- 4 files changed, 138 insertions(+), 150 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 64aa603465..1132a5cab6 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -688,18 +688,23 @@ DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 05a0b84416..d1c4a330f2 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1652,6 +1652,21 @@ UQXTN_s 0111 1110 ..1 00001 01001 0 ..... ..... @rr_e FCVTXN_s 0111 1110 011 00001 01101 0 ..... ..... @rr_s +FCMGT0_s 0101 1110 111 11000 11001 0 ..... ..... @rr_h +FCMGT0_s 0101 1110 1.1 00000 11001 0 ..... ..... @rr_sd + +FCMGE0_s 0111 1110 111 11000 11001 0 ..... ..... @rr_h +FCMGE0_s 0111 1110 1.1 00000 11001 0 ..... ..... @rr_sd + +FCMEQ0_s 0101 1110 111 11000 11011 0 ..... ..... @rr_h +FCMEQ0_s 0101 1110 1.1 00000 11011 0 ..... ..... @rr_sd + +FCMLE0_s 0111 1110 111 11000 11011 0 ..... ..... @rr_h +FCMLE0_s 0111 1110 1.1 00000 11011 0 ..... ..... @rr_sd + +FCMLT0_s 0101 1110 111 11000 11101 0 ..... ..... @rr_h +FCMLT0_s 0101 1110 1.1 00000 11101 0 ..... ..... @rr_sd + @icvt_h . ....... .. ...... ...... rn:5 rd:5 \ &fcvt sf=0 esz=1 shift=0 @icvt_sd . ....... .. ...... ...... rn:5 rd:5 \ @@ -1818,6 +1833,21 @@ FCVTAS_vi 0.00 1110 0.1 00001 11001 0 ..... ..... @qrr_sd FCVTAU_vi 0.10 1110 011 11001 11001 0 ..... ..... @qrr_h FCVTAU_vi 0.10 1110 0.1 00001 11001 0 ..... ..... @qrr_sd +FCMGT0_v 0.00 1110 111 11000 11001 0 ..... ..... @qrr_h +FCMGT0_v 0.00 1110 1.1 00000 11001 0 ..... ..... @qrr_sd + +FCMGE0_v 0.10 1110 111 11000 11001 0 ..... ..... @qrr_h +FCMGE0_v 0.10 1110 1.1 00000 11001 0 ..... ..... @qrr_sd + +FCMEQ0_v 0.00 1110 111 11000 11011 0 ..... ..... @qrr_h +FCMEQ0_v 0.00 1110 1.1 00000 11011 0 ..... ..... @qrr_sd + +FCMLE0_v 0.10 1110 111 11000 11011 0 ..... ..... @qrr_h +FCMLE0_v 0.10 1110 1.1 00000 11011 0 ..... ..... @qrr_sd + +FCMLT0_v 0.00 1110 111 11000 11101 0 ..... ..... @qrr_h +FCMLT0_v 0.00 1110 1.1 00000 11101 0 ..... ..... @qrr_sd + &fcvt_q rd rn esz q shift @fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ &fcvt_q esz=1 shift=%fcvt_f_sh_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ec1ce44c4b..1776862161 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5250,6 +5250,61 @@ static const FPScalar f_scalar_frsqrts = { }; TRANS(FRSQRTS_s, do_fp3_scalar, a, &f_scalar_frsqrts) +static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, + const FPScalar *f, bool swap) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = tcg_constant_i64(0); + if (swap) { + f->gen_d(t0, t1, t0, fpstatus_ptr(FPST_FPCR)); + } else { + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + } + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rn); + TCGv_i32 t1 = tcg_constant_i32(0); + if (swap) { + f->gen_s(t0, t1, t0, fpstatus_ptr(FPST_FPCR)); + } else { + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + } + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rn); + TCGv_i32 t1 = tcg_constant_i32(0); + if (swap) { + f->gen_h(t0, t1, t0, fpstatus_ptr(FPST_FPCR_F16)); + } else { + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + } + write_fp_sreg(s, a->rd, t0); + } + break; + default: + return false; + } + return true; +} + +TRANS(FCMEQ0_s, do_fcmp0_s, a, &f_scalar_fcmeq, false) +TRANS(FCMGT0_s, do_fcmp0_s, a, &f_scalar_fcmgt, false) +TRANS(FCMGE0_s, do_fcmp0_s, a, &f_scalar_fcmge, false) +TRANS(FCMLT0_s, do_fcmp0_s, a, &f_scalar_fcmgt, true) +TRANS(FCMLE0_s, do_fcmp0_s, a, &f_scalar_fcmge, true) + static bool do_satacc_s(DisasContext *s, arg_rrr_e *a, MemOp sgn_n, MemOp sgn_m, void (*gen_bhs)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp), @@ -9381,134 +9436,40 @@ TRANS(FCVTAS_vi, do_gvec_op2_fpst, TRANS(FCVTAU_vi, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, float_round_ties_away, f_fcvt_u_vi) -static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, - bool is_scalar, bool is_u, bool is_q, - int size, int rn, int rd) -{ - bool is_double = (size == MO_64); - TCGv_ptr fpst; +static gen_helper_gvec_2_ptr * const f_fceq0[] = { + gen_helper_gvec_fceq0_h, + gen_helper_gvec_fceq0_s, + gen_helper_gvec_fceq0_d, +}; +TRANS(FCMEQ0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fceq0) - if (!fp_access_check(s)) { - return; - } +static gen_helper_gvec_2_ptr * const f_fcgt0[] = { + gen_helper_gvec_fcgt0_h, + gen_helper_gvec_fcgt0_s, + gen_helper_gvec_fcgt0_d, +}; +TRANS(FCMGT0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcgt0) - fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); +static gen_helper_gvec_2_ptr * const f_fcge0[] = { + gen_helper_gvec_fcge0_h, + gen_helper_gvec_fcge0_s, + gen_helper_gvec_fcge0_d, +}; +TRANS(FCMGE0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcge0) - if (is_double) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_zero = tcg_constant_i64(0); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - NeonGenTwoDoubleOpFn *genfn; - bool swap = false; - int pass; +static gen_helper_gvec_2_ptr * const f_fclt0[] = { + gen_helper_gvec_fclt0_h, + gen_helper_gvec_fclt0_s, + gen_helper_gvec_fclt0_d, +}; +TRANS(FCMLT0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fclt0) - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fallthrough */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_neon_cgt_f64; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_neon_ceq_f64; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_neon_cge_f64; - break; - default: - g_assert_not_reached(); - } - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - read_vec_element(s, tcg_op, rn, pass, MO_64); - if (swap) { - genfn(tcg_res, tcg_zero, tcg_op, fpst); - } else { - genfn(tcg_res, tcg_op, tcg_zero, fpst); - } - write_vec_element(s, tcg_res, rd, pass, MO_64); - } - - clear_vec_high(s, !is_scalar, rd); - } else { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_zero = tcg_constant_i32(0); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - NeonGenTwoSingleOpFn *genfn; - bool swap = false; - int pass, maxpasses; - - if (size == MO_16) { - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fall through */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_advsimd_cgt_f16; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_advsimd_ceq_f16; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_advsimd_cge_f16; - break; - default: - g_assert_not_reached(); - } - } else { - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fall through */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_neon_cgt_f32; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_neon_ceq_f32; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_neon_cge_f32; - break; - default: - g_assert_not_reached(); - } - } - - if (is_scalar) { - maxpasses = 1; - } else { - int vector_size = 8 << is_q; - maxpasses = vector_size >> size; - } - - for (pass = 0; pass < maxpasses; pass++) { - read_vec_element_i32(s, tcg_op, rn, pass, size); - if (swap) { - genfn(tcg_res, tcg_zero, tcg_op, fpst); - } else { - genfn(tcg_res, tcg_op, tcg_zero, fpst); - } - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } else { - write_vec_element_i32(s, tcg_res, rd, pass, size); - } - } - - if (!is_scalar) { - clear_vec_high(s, is_q, rd); - } - } -} +static gen_helper_gvec_2_ptr * const f_fcle0[] = { + gen_helper_gvec_fcle0_h, + gen_helper_gvec_fcle0_s, + gen_helper_gvec_fcle0_d, +}; +TRANS(FCMLE0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcle0) static void handle_2misc_reciprocal(DisasContext *s, int opcode, bool is_scalar, bool is_u, bool is_q, @@ -9607,13 +9568,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) opcode |= (extract32(size, 1, 1) << 5) | (u << 6); size = extract32(size, 0, 1) ? 3 : 2; switch (opcode) { - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - handle_2misc_fcmp_zero(s, opcode, true, u, true, size, rn, rd); - return; case 0x3d: /* FRECPE */ case 0x3f: /* FRECPX */ case 0x7d: /* FRSQRTE */ @@ -9635,6 +9589,11 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x56: /* FCVTXN, FCVTXN2 */ case 0x1d: /* SCVTF */ case 0x5d: /* UCVTF */ + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ default: unallocated_encoding(s); return; @@ -9729,17 +9688,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) opcode |= (extract32(size, 1, 1) << 5) | (u << 6); size = is_double ? 3 : 2; switch (opcode) { - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - handle_2misc_fcmp_zero(s, opcode, false, u, is_q, size, rn, rd); - return; case 0x3c: /* URECPE */ if (size == 3) { unallocated_encoding(s); @@ -9799,6 +9747,11 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x7b: /* FCVTZU */ case 0x5c: /* FCVTAU */ case 0x1c: /* FCVTAS */ + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ unallocated_encoding(s); return; } @@ -9939,13 +9892,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) fpop = deposit32(fpop, 6, 1, u); switch (fpop) { - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - handle_2misc_fcmp_zero(s, fpop, is_scalar, 0, is_q, MO_16, rn, rd); - return; case 0x3d: /* FRECPE */ case 0x3f: /* FRECPX */ break; @@ -9974,6 +9920,11 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x5c: /* FCVTAU */ case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ unallocated_encoding(s); return; } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index bc752ff988..ee84774517 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1253,8 +1253,10 @@ DO_2OP(gvec_touszh, vfp_touszh, float16) #define DO_2OP_CMP0(FN, CMPOP, DIRN) \ WRAP_CMP0_##DIRN(FN, CMPOP, float16) \ WRAP_CMP0_##DIRN(FN, CMPOP, float32) \ + WRAP_CMP0_##DIRN(FN, CMPOP, float64) \ DO_2OP(gvec_f##FN##0_h, float16_##FN##0, float16) \ - DO_2OP(gvec_f##FN##0_s, float32_##FN##0, float32) + DO_2OP(gvec_f##FN##0_s, float32_##FN##0, float32) \ + DO_2OP(gvec_f##FN##0_d, float64_##FN##0, float64) DO_2OP_CMP0(cgt, cgt, FWD) DO_2OP_CMP0(cge, cge, FWD) From 31e65acf7bc54d80910f94353b710f8bafa049c2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:33 -0600 Subject: [PATCH 0270/2892] target/arm: Convert FRECPE, FRECPX, FRSQRTE to decodetree Remove disas_simd_scalar_two_reg_misc and disas_simd_two_reg_misc_fp16 as these were the last insns decoded by those functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-67-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 15 ++ target/arm/tcg/translate-a64.c | 329 ++++----------------------------- 2 files changed, 53 insertions(+), 291 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d1c4a330f2..9b3b09c3bb 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1667,6 +1667,15 @@ FCMLE0_s 0111 1110 1.1 00000 11011 0 ..... ..... @rr_sd FCMLT0_s 0101 1110 111 11000 11101 0 ..... ..... @rr_h FCMLT0_s 0101 1110 1.1 00000 11101 0 ..... ..... @rr_sd +FRECPE_s 0101 1110 111 11001 11011 0 ..... ..... @rr_h +FRECPE_s 0101 1110 1.1 00001 11011 0 ..... ..... @rr_sd + +FRECPX_s 0101 1110 111 11001 11111 0 ..... ..... @rr_h +FRECPX_s 0101 1110 1.1 00001 11111 0 ..... ..... @rr_sd + +FRSQRTE_s 0111 1110 111 11001 11011 0 ..... ..... @rr_h +FRSQRTE_s 0111 1110 1.1 00001 11011 0 ..... ..... @rr_sd + @icvt_h . ....... .. ...... ...... rn:5 rd:5 \ &fcvt sf=0 esz=1 shift=0 @icvt_sd . ....... .. ...... ...... rn:5 rd:5 \ @@ -1848,6 +1857,12 @@ FCMLE0_v 0.10 1110 1.1 00000 11011 0 ..... ..... @qrr_sd FCMLT0_v 0.00 1110 111 11000 11101 0 ..... ..... @qrr_h FCMLT0_v 0.00 1110 1.1 00000 11101 0 ..... ..... @qrr_sd +FRECPE_v 0.00 1110 111 11001 11011 0 ..... ..... @qrr_h +FRECPE_v 0.00 1110 1.1 00001 11011 0 ..... ..... @qrr_sd + +FRSQRTE_v 0.10 1110 111 11001 11011 0 ..... ..... @qrr_h +FRSQRTE_v 0.10 1110 1.1 00001 11011 0 ..... ..... @qrr_sd + &fcvt_q rd rn esz q shift @fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ &fcvt_q esz=1 shift=%fcvt_f_sh_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1776862161..63cf25251b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8501,6 +8501,27 @@ TRANS_FEAT(FRINT64Z_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint64, FPROUNDING_ZERO) TRANS_FEAT(FRINT64X_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint64, -1) +static const FPScalar1 f_scalar_frecpe = { + gen_helper_recpe_f16, + gen_helper_recpe_f32, + gen_helper_recpe_f64, +}; +TRANS(FRECPE_s, do_fp1_scalar, a, &f_scalar_frecpe, -1) + +static const FPScalar1 f_scalar_frecpx = { + gen_helper_frecpx_f16, + gen_helper_frecpx_f32, + gen_helper_frecpx_f64, +}; +TRANS(FRECPX_s, do_fp1_scalar, a, &f_scalar_frecpx, -1) + +static const FPScalar1 f_scalar_frsqrte = { + gen_helper_rsqrte_f16, + gen_helper_rsqrte_f32, + gen_helper_rsqrte_f64, +}; +TRANS(FRSQRTE_s, do_fp1_scalar, a, &f_scalar_frsqrte, -1) + static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) { if (fp_access_check(s)) { @@ -9471,36 +9492,28 @@ static gen_helper_gvec_2_ptr * const f_fcle0[] = { }; TRANS(FCMLE0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcle0) +static gen_helper_gvec_2_ptr * const f_frecpe[] = { + gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_s, + gen_helper_gvec_frecpe_d, +}; +TRANS(FRECPE_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frecpe) + +static gen_helper_gvec_2_ptr * const f_frsqrte[] = { + gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_s, + gen_helper_gvec_frsqrte_d, +}; +TRANS(FRSQRTE_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frsqrte) + static void handle_2misc_reciprocal(DisasContext *s, int opcode, bool is_scalar, bool is_u, bool is_q, int size, int rn, int rd) { bool is_double = (size == 3); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); if (is_double) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - int pass; - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - read_vec_element(s, tcg_op, rn, pass, MO_64); - switch (opcode) { - case 0x3d: /* FRECPE */ - gen_helper_recpe_f64(tcg_res, tcg_op, fpst); - break; - case 0x3f: /* FRECPX */ - gen_helper_frecpx_f64(tcg_res, tcg_op, fpst); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f64(tcg_res, tcg_op, fpst); - break; - default: - g_assert_not_reached(); - } - write_vec_element(s, tcg_res, rd, pass, MO_64); - } - clear_vec_high(s, !is_scalar, rd); + g_assert_not_reached(); } else { TCGv_i32 tcg_op = tcg_temp_new_i32(); TCGv_i32 tcg_res = tcg_temp_new_i32(); @@ -9520,14 +9533,8 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode, gen_helper_recpe_u32(tcg_res, tcg_op); break; case 0x3d: /* FRECPE */ - gen_helper_recpe_f32(tcg_res, tcg_op, fpst); - break; case 0x3f: /* FRECPX */ - gen_helper_frecpx_f32(tcg_res, tcg_op, fpst); - break; case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f32(tcg_res, tcg_op, fpst); - break; default: g_assert_not_reached(); } @@ -9544,76 +9551,6 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode, } } -/* AdvSIMD scalar two reg misc - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd | - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 12, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - - switch (opcode) { - case 0xc ... 0xf: - case 0x16 ... 0x1d: - case 0x1f: - /* Floating point: U, size[1] and opcode indicate operation; - * size[0] indicates single or double precision. - */ - opcode |= (extract32(size, 1, 1) << 5) | (u << 6); - size = extract32(size, 0, 1) ? 3 : 2; - switch (opcode) { - case 0x3d: /* FRECPE */ - case 0x3f: /* FRECPX */ - case 0x7d: /* FRSQRTE */ - if (!fp_access_check(s)) { - return; - } - handle_2misc_reciprocal(s, opcode, true, u, true, size, rn, rd); - return; - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - case 0x1c: /* FCVTAS */ - case 0x5c: /* FCVTAU */ - case 0x56: /* FCVTXN, FCVTXN2 */ - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - default: - unallocated_encoding(s); - return; - } - break; - default: - case 0x3: /* USQADD / SUQADD */ - case 0x7: /* SQABS / SQNEG */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - case 0xa: /* CMLT */ - case 0xb: /* ABS, NEG */ - case 0x12: /* SQXTUN */ - case 0x14: /* SQXTN, UQXTN */ - unallocated_encoding(s); - return; - } - g_assert_not_reached(); -} - static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -9693,13 +9630,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - /* fall through */ - case 0x3d: /* FRECPE */ - case 0x7d: /* FRSQRTE */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } if (!fp_access_check(s)) { return; } @@ -9752,6 +9682,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x2e: /* FCMLT (zero) */ case 0x6c: /* FCMGE (zero) */ case 0x6d: /* FCMLE (zero) */ + case 0x3d: /* FRECPE */ + case 0x7d: /* FRSQRTE */ unallocated_encoding(s); return; } @@ -9847,189 +9779,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } } -/* AdvSIMD [scalar] two register miscellaneous (FP16) - * - * 31 30 29 28 27 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ - * | 0 | Q | U | S | 1 1 1 0 | a | 1 1 1 1 0 0 | opcode | 1 0 | Rn | Rd | - * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ - * mask: 1000 1111 0111 1110 0000 1100 0000 0000 0x8f7e 0c00 - * val: 0000 1110 0111 1000 0000 1000 0000 0000 0x0e78 0800 - * - * This actually covers two groups where scalar access is governed by - * bit 28. A bunch of the instructions (float to integral) only exist - * in the vector form and are un-allocated for the scalar decode. Also - * in the scalar decode Q is always 1. - */ -static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) -{ - int fpop, opcode, a, u; - int rn, rd; - bool is_q; - bool is_scalar; - - int pass; - TCGv_i32 tcg_rmode = NULL; - TCGv_ptr tcg_fpstatus = NULL; - bool need_fpst = true; - int rmode = -1; - - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - - rd = extract32(insn, 0, 5); - rn = extract32(insn, 5, 5); - - a = extract32(insn, 23, 1); - u = extract32(insn, 29, 1); - is_scalar = extract32(insn, 28, 1); - is_q = extract32(insn, 30, 1); - - opcode = extract32(insn, 12, 5); - fpop = deposit32(opcode, 5, 1, a); - fpop = deposit32(fpop, 6, 1, u); - - switch (fpop) { - case 0x3d: /* FRECPE */ - case 0x3f: /* FRECPX */ - break; - case 0x7d: /* FRSQRTE */ - break; - default: - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - case 0x7f: /* FSQRT (vector) */ - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x59: /* FRINTX */ - case 0x79: /* FRINTI */ - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - unallocated_encoding(s); - return; - } - - - /* Check additional constraints for the scalar encoding */ - if (is_scalar) { - if (!is_q) { - unallocated_encoding(s); - return; - } - } - - if (!fp_access_check(s)) { - return; - } - - if (rmode >= 0 || need_fpst) { - tcg_fpstatus = fpstatus_ptr(FPST_FPCR_F16); - } - - if (rmode >= 0) { - tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); - } - - if (is_scalar) { - TCGv_i32 tcg_op = read_fp_hreg(s, rn); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - switch (fpop) { - case 0x3d: /* FRECPE */ - gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x3f: /* FRECPX */ - gen_helper_frecpx_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - default: - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - g_assert_not_reached(); - } - - /* limit any sign extension going on */ - tcg_gen_andi_i32(tcg_res, tcg_res, 0xffff); - write_fp_sreg(s, rd, tcg_res); - } else { - for (pass = 0; pass < (is_q ? 8 : 4); pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, MO_16); - - switch (fpop) { - case 0x3d: /* FRECPE */ - gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - default: - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - case 0x7f: /* FSQRT */ - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - case 0x59: /* FRINTX */ - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - g_assert_not_reached(); - } - - write_vec_element_i32(s, tcg_res, rd, pass, MO_16); - } - - clear_vec_high(s, is_q, rd); - } - - if (tcg_rmode) { - gen_restore_rmode(tcg_rmode, tcg_fpstatus); - } -} - /* C3.6 Data processing - SIMD, inc Crypto * * As the decode gets a little complex we are using a table based @@ -10038,8 +9787,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) static const AArch64DecodeTable data_proc_simd[] = { /* pattern , mask , fn */ { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, - { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, - { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x00000000, 0x00000000, NULL } }; From 8710a43de1268948bfa2e5b3cc77504246dd6d20 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:34 -0600 Subject: [PATCH 0271/2892] target/arm: Introduce gen_gvec_urecpe, gen_gvec_ursqrte Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-68-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 3 +++ target/arm/tcg/gengvec.c | 16 ++++++++++++++++ target/arm/tcg/translate-neon.c | 4 ++-- target/arm/tcg/translate.h | 5 +++++ target/arm/tcg/vec_helper.c | 22 ++++++++++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 1132a5cab6..9919b1367b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1121,6 +1121,9 @@ DEF_HELPER_FLAGS_4(gvec_uminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_uminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" #include "tcg/helper-sve.h" diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 01c9d5436d..01867f8ace 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2711,3 +2711,19 @@ void gen_gvec_fneg(unsigned vece, uint32_t dofs, uint32_t aofs, uint64_t s_bit = 1ull << ((8 << vece) - 1); tcg_gen_gvec_xori(vece, dofs, aofs, s_bit, oprsz, maxsz); } + +void gen_gvec_urecpe(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_32); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_urecpe_s); +} + +void gen_gvec_ursqrte(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_32); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_ursqrte_s); +} diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index f9ca889bec..c4fecb8fd6 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -3070,7 +3070,7 @@ static bool trans_VRECPE(DisasContext *s, arg_2misc *a) if (a->size != 2) { return false; } - return do_2misc(s, a, gen_helper_recpe_u32); + return do_2misc_vec(s, a, gen_gvec_urecpe); } static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a) @@ -3078,7 +3078,7 @@ static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a) if (a->size != 2) { return false; } - return do_2misc(s, a, gen_helper_rsqrte_u32); + return do_2misc_vec(s, a, gen_gvec_ursqrte); } #define WRAP_1OP_ENV_FN(WRAPNAME, FUNC) \ diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index b996de2c15..9b9abf1992 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -608,6 +608,11 @@ void gen_gvec_fabs(unsigned vece, uint32_t dofs, uint32_t aofs, void gen_gvec_fneg(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz); +void gen_gvec_urecpe(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_ursqrte(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); + /* * Forward to the isar_feature_* tests given a DisasContext pointer. */ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index ee84774517..768f745828 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -3099,3 +3099,25 @@ void HELPER(gvec_rbit_b)(void *vd, void *vn, uint32_t desc) } clear_tail(d, opr_sz, simd_maxsz(desc)); } + +void HELPER(gvec_urecpe_s)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = helper_recpe_u32(n[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_ursqrte_s)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = helper_rsqrte_u32(n[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} From 4280c9b5affeb6b9907f45fb452d6ffda03b68dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:35 -0600 Subject: [PATCH 0272/2892] target/arm: Convert URECPE and URSQRTE to decodetree Remove handle_2misc_reciprocal as these were the last insns decoded by that function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-69-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 + target/arm/tcg/translate-a64.c | 139 ++------------------------------- 2 files changed, 8 insertions(+), 134 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 9b3b09c3bb..f35d123821 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1863,6 +1863,9 @@ FRECPE_v 0.00 1110 1.1 00001 11011 0 ..... ..... @qrr_sd FRSQRTE_v 0.10 1110 111 11001 11011 0 ..... ..... @qrr_h FRSQRTE_v 0.10 1110 1.1 00001 11011 0 ..... ..... @qrr_sd +URECPE_v 0.00 1110 101 00001 11001 0 ..... ..... @qrr_s +URSQRTE_v 0.10 1110 101 00001 11001 0 ..... ..... @qrr_s + &fcvt_q rd rn esz q shift @fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ &fcvt_q esz=1 shift=%fcvt_f_sh_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 63cf25251b..fa3170da86 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9163,6 +9163,8 @@ TRANS(CMLE0_v, do_gvec_fn2, a, gen_gvec_cle0) TRANS(CMEQ0_v, do_gvec_fn2, a, gen_gvec_ceq0) TRANS(REV16_v, do_gvec_fn2, a, gen_gvec_rev16) TRANS(REV32_v, do_gvec_fn2, a, gen_gvec_rev32) +TRANS(URECPE_v, do_gvec_fn2, a, gen_gvec_urecpe) +TRANS(URSQRTE_v, do_gvec_fn2, a, gen_gvec_ursqrte) static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) { @@ -9506,51 +9508,6 @@ static gen_helper_gvec_2_ptr * const f_frsqrte[] = { }; TRANS(FRSQRTE_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frsqrte) -static void handle_2misc_reciprocal(DisasContext *s, int opcode, - bool is_scalar, bool is_u, bool is_q, - int size, int rn, int rd) -{ - bool is_double = (size == 3); - - if (is_double) { - g_assert_not_reached(); - } else { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - int pass, maxpasses; - - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; - } - - for (pass = 0; pass < maxpasses; pass++) { - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - - switch (opcode) { - case 0x3c: /* URECPE */ - gen_helper_recpe_u32(tcg_res, tcg_op); - break; - case 0x3d: /* FRECPE */ - case 0x3f: /* FRECPX */ - case 0x7d: /* FRSQRTE */ - default: - g_assert_not_reached(); - } - - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } else { - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } - if (!is_scalar) { - clear_vec_high(s, is_q, rd); - } - } -} - static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -9609,10 +9566,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) bool is_q = extract32(insn, 30, 1); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - bool need_fpstatus = false; - int rmode = -1; - TCGv_i32 tcg_rmode; - TCGv_ptr tcg_fpstatus; switch (opcode) { case 0xc ... 0xf: @@ -9625,28 +9578,12 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) opcode |= (extract32(size, 1, 1) << 5) | (u << 6); size = is_double ? 3 : 2; switch (opcode) { - case 0x3c: /* URECPE */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_reciprocal(s, opcode, false, u, is_q, size, rn, rd); - return; case 0x17: /* FCVTL, FCVTL2 */ if (!fp_access_check(s)) { return; } handle_2misc_widening(s, opcode, is_q, size, rn, rd); return; - case 0x7c: /* URSQRTE */ - if (size == 3) { - unallocated_encoding(s); - return; - } - break; default: case 0x16: /* FCVTN, FCVTN2 */ case 0x36: /* BFCVTN, BFCVTN2 */ @@ -9684,6 +9621,8 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x6d: /* FCMLE (zero) */ case 0x3d: /* FRECPE */ case 0x7d: /* FRSQRTE */ + case 0x3c: /* URECPE */ + case 0x7c: /* URSQRTE */ unallocated_encoding(s); return; } @@ -9708,75 +9647,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - - if (!fp_access_check(s)) { - return; - } - - if (need_fpstatus || rmode >= 0) { - tcg_fpstatus = fpstatus_ptr(FPST_FPCR); - } else { - tcg_fpstatus = NULL; - } - if (rmode >= 0) { - tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); - } else { - tcg_rmode = NULL; - } - - { - int pass; - - assert(size == 2); - for (pass = 0; pass < (is_q ? 4 : 2); pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - - { - /* Special cases for 32 bit elements */ - switch (opcode) { - case 0x7c: /* URSQRTE */ - gen_helper_rsqrte_u32(tcg_res, tcg_op); - break; - default: - case 0x7: /* SQABS, SQNEG */ - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - case 0x7f: /* FSQRT */ - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - case 0x59: /* FRINTX */ - case 0x1e: /* FRINT32Z */ - case 0x5e: /* FRINT32X */ - case 0x1f: /* FRINT64Z */ - case 0x5f: /* FRINT64X */ - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - g_assert_not_reached(); - } - } - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } - clear_vec_high(s, is_q, rd); - - if (tcg_rmode) { - gen_restore_rmode(tcg_rmode, tcg_fpstatus); - } + g_assert_not_reached(); } /* C3.6 Data processing - SIMD, inc Crypto From 53ac794af68bcd69d121e70592bc684b14407c71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Dec 2024 10:30:36 -0600 Subject: [PATCH 0273/2892] target/arm: Convert FCVTL to decodetree Remove lookup_disas_fn, handle_2misc_widening, disas_simd_two_reg_misc, disas_data_proc_simd, disas_data_proc_simd_fp, disas_a64_legacy, as this is the final insn to be converted. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20241211163036.2297116-70-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 + target/arm/tcg/translate-a64.c | 202 +++------------------------------ 2 files changed, 18 insertions(+), 186 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f35d123821..7aa10f5147 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1866,6 +1866,8 @@ FRSQRTE_v 0.10 1110 1.1 00001 11011 0 ..... ..... @qrr_sd URECPE_v 0.00 1110 101 00001 11001 0 ..... ..... @qrr_s URSQRTE_v 0.10 1110 101 00001 11001 0 ..... ..... @qrr_s +FCVTL_v 0.00 1110 0.1 00001 01111 0 ..... ..... @qrr_sd + &fcvt_q rd rn esz q shift @fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ &fcvt_q esz=1 shift=%fcvt_f_sh_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index fa3170da86..3e57b98c27 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1465,31 +1465,6 @@ static inline void gen_check_sp_alignment(DisasContext *s) */ } -/* - * This provides a simple table based table lookup decoder. It is - * intended to be used when the relevant bits for decode are too - * awkwardly placed and switch/if based logic would be confusing and - * deeply nested. Since it's a linear search through the table, tables - * should be kept small. - * - * It returns the first handler where insn & mask == pattern, or - * NULL if there is no match. - * The table is terminated by an empty mask (i.e. 0) - */ -static inline AArch64DecodeFn *lookup_disas_fn(const AArch64DecodeTable *table, - uint32_t insn) -{ - const AArch64DecodeTable *tptr = table; - - while (tptr->mask) { - if ((insn & tptr->mask) == tptr->pattern) { - return tptr->disas_fn; - } - tptr++; - } - return NULL; -} - /* * The instruction disassembly implemented here matches * the instruction encoding classifications in chapter C4 @@ -9508,8 +9483,7 @@ static gen_helper_gvec_2_ptr * const f_frsqrte[] = { }; TRANS(FRSQRTE_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frsqrte) -static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, - int size, int rn, int rd) +static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) { /* Handle 2-reg-misc ops which are widening (so each size element * in the source becomes a 2*size element in the destination. @@ -9517,173 +9491,43 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, */ int pass; - if (size == 3) { + if (!fp_access_check(s)) { + return true; + } + + if (a->esz == MO_64) { /* 32 -> 64 bit fp conversion */ TCGv_i64 tcg_res[2]; - int srcelt = is_q ? 2 : 0; + TCGv_i32 tcg_op = tcg_temp_new_i32(); + int srcelt = a->q ? 2 : 0; for (pass = 0; pass < 2; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); tcg_res[pass] = tcg_temp_new_i64(); - - read_vec_element_i32(s, tcg_op, rn, srcelt + pass, MO_32); + read_vec_element_i32(s, tcg_op, a->rn, srcelt + pass, MO_32); gen_helper_vfp_fcvtds(tcg_res[pass], tcg_op, tcg_env); } for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); + write_vec_element(s, tcg_res[pass], a->rd, pass, MO_64); } } else { /* 16 -> 32 bit fp conversion */ - int srcelt = is_q ? 4 : 0; + int srcelt = a->q ? 4 : 0; TCGv_i32 tcg_res[4]; TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); TCGv_i32 ahp = get_ahp_flag(); for (pass = 0; pass < 4; pass++) { tcg_res[pass] = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_res[pass], rn, srcelt + pass, MO_16); + read_vec_element_i32(s, tcg_res[pass], a->rn, srcelt + pass, MO_16); gen_helper_vfp_fcvt_f16_to_f32(tcg_res[pass], tcg_res[pass], fpst, ahp); } for (pass = 0; pass < 4; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); + write_vec_element_i32(s, tcg_res[pass], a->rd, pass, MO_32); } } -} - -/* AdvSIMD two reg misc - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd | - * +---+---+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - bool u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - switch (opcode) { - case 0xc ... 0xf: - case 0x16 ... 0x1f: - { - /* Floating point: U, size[1] and opcode indicate operation; - * size[0] indicates single or double precision. - */ - int is_double = extract32(size, 0, 1); - opcode |= (extract32(size, 1, 1) << 5) | (u << 6); - size = is_double ? 3 : 2; - switch (opcode) { - case 0x17: /* FCVTL, FCVTL2 */ - if (!fp_access_check(s)) { - return; - } - handle_2misc_widening(s, opcode, is_q, size, rn, rd); - return; - default: - case 0x16: /* FCVTN, FCVTN2 */ - case 0x36: /* BFCVTN, BFCVTN2 */ - case 0x56: /* FCVTXN, FCVTXN2 */ - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - case 0x7f: /* FSQRT */ - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x59: /* FRINTX */ - case 0x79: /* FRINTI */ - case 0x58: /* FRINTA */ - case 0x1e: /* FRINT32Z */ - case 0x1f: /* FRINT64Z */ - case 0x5e: /* FRINT32X */ - case 0x5f: /* FRINT64X */ - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - case 0x5c: /* FCVTAU */ - case 0x1c: /* FCVTAS */ - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - case 0x3d: /* FRECPE */ - case 0x7d: /* FRSQRTE */ - case 0x3c: /* URECPE */ - case 0x7c: /* URSQRTE */ - unallocated_encoding(s); - return; - } - break; - } - default: - case 0x0: /* REV64, REV32 */ - case 0x1: /* REV16 */ - case 0x2: /* SADDLP, UADDLP */ - case 0x3: /* SUQADD, USQADD */ - case 0x4: /* CLS, CLZ */ - case 0x5: /* CNT, NOT, RBIT */ - case 0x6: /* SADALP, UADALP */ - case 0x7: /* SQABS, SQNEG */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - case 0xa: /* CMLT */ - case 0xb: /* ABS, NEG */ - case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ - case 0x13: /* SHLL, SHLL2 */ - case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ - unallocated_encoding(s); - return; - } - g_assert_not_reached(); -} - -/* C3.6 Data processing - SIMD, inc Crypto - * - * As the decode gets a little complex we are using a table based - * approach for this part of the decode. - */ -static const AArch64DecodeTable data_proc_simd[] = { - /* pattern , mask , fn */ - { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, - { 0x00000000, 0x00000000, NULL } -}; - -static void disas_data_proc_simd(DisasContext *s, uint32_t insn) -{ - /* Note that this is called with all non-FP cases from - * table C3-6 so it must UNDEF for entries not specifically - * allocated to instructions in that table. - */ - AArch64DecodeFn *fn = lookup_disas_fn(&data_proc_simd[0], insn); - if (fn) { - fn(s, insn); - } else { - unallocated_encoding(s); - } -} - -/* C3.6 Data processing - SIMD and floating point */ -static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) -{ - if (extract32(insn, 28, 1) == 1 && extract32(insn, 30, 1) == 0) { - unallocated_encoding(s); /* in decodetree */ - } else { - /* SIMD, including crypto */ - disas_data_proc_simd(s, insn); - } + clear_vec_high(s, true, a->rd); + return true; } static bool trans_OK(DisasContext *s, arg_OK *a) @@ -9749,20 +9593,6 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) return false; } -/* C3.1 A64 instruction index by encoding */ -static void disas_a64_legacy(DisasContext *s, uint32_t insn) -{ - switch (extract32(insn, 25, 4)) { - case 0x7: - case 0xf: /* Data processing - SIMD and floating point */ - disas_data_proc_simd_fp(s, insn); - break; - default: - unallocated_encoding(s); - break; - } -} - static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { @@ -9965,7 +9795,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (!disas_a64(s, insn) && !disas_sme(s, insn) && !disas_sve(s, insn)) { - disas_a64_legacy(s, insn); + unallocated_encoding(s); } /* From 44d9fab1f8a1d376741ff4505ec39f7f0a729ab2 Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Sat, 9 Nov 2024 18:28:44 +0600 Subject: [PATCH 0274/2892] docs/nitro-enclave: Fix terminal commands formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dorjoy Chowdhury Reviewed-by: Alexander Graf Message-ID: <20241109122844.24057-1-dorjoychy111@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/system/i386/nitro-enclave.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/system/i386/nitro-enclave.rst b/docs/system/i386/nitro-enclave.rst index 73e3edefe5..48eda5bd9e 100644 --- a/docs/system/i386/nitro-enclave.rst +++ b/docs/system/i386/nitro-enclave.rst @@ -48,13 +48,13 @@ Running a nitro-enclave VM First, run `vhost-device-vsock`__ (or a similar tool that supports vhost-user-vsock). The forward-cid option below with value 1 forwards all connections from the enclave VM to the host machine and the forward-listen (port numbers separated by '+') is used -for forwarding connections from the host machine to the enclave VM. - -__ https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-vsock#using-the-vsock-backend +for forwarding connections from the host machine to the enclave VM:: $ vhost-device-vsock \ --vm guest-cid=4,forward-cid=1,forward-listen=9001+9002,socket=/tmp/vhost4.socket +__ https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-vsock#using-the-vsock-backend + Now run the necessary applications on the host machine so that the nitro-enclave VM applications' vsock communication works. For example, the nitro-enclave VM's init process connects to CID 3 and sends a single byte hello heartbeat (0xB7) to let the @@ -65,7 +65,7 @@ the applications on the host machine that would typically be running in the pare VM for successful communication with the enclave VM. Then run the nitro-enclave VM using the following command where ``hello.eif`` is -an EIF file you would use to spawn a real AWS nitro enclave virtual machine: +an EIF file you would use to spawn a real AWS nitro enclave virtual machine:: $ qemu-system-x86_64 -M nitro-enclave,vsock=c,id=hello-world \ -kernel hello-world.eif -nographic -m 4G --enable-kvm -cpu host \ From 5b86ddd83d81add60a177804a0e5f2c373f61f82 Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Sat, 9 Nov 2024 18:30:39 +0600 Subject: [PATCH 0275/2892] hw/core/eif: Use stateful qcrypto apis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were storing the pointers to buffers in a GList due to lack of stateful crypto apis and instead doing the final hash computation at the end after we had all the necessary buffers. Now that we have the stateful qcrypto apis available, we can instead update the hashes inline in the read_eif_* functions which makes the code much simpler. Signed-off-by: Dorjoy Chowdhury Reviewed-by: Alexander Graf Message-ID: <20241109123039.24180-1-dorjoychy111@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/eif.c | 202 ++++++++++++-------------------- hw/i386/nitro_enclave.c | 12 +- include/hw/i386/nitro_enclave.h | 16 +-- 3 files changed, 91 insertions(+), 139 deletions(-) diff --git a/hw/core/eif.c b/hw/core/eif.c index a7128b71ce..513caec6b4 100644 --- a/hw/core/eif.c +++ b/hw/core/eif.c @@ -187,10 +187,16 @@ static void safe_unlink(char *f) * Upon success, the caller is reponsible for unlinking and freeing *kernel_path */ static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, - uint8_t *kernel, uint32_t *crc, Error **errp) + QCryptoHash *hash0, QCryptoHash *hash1, + uint32_t *crc, Error **errp) { size_t got; FILE *tmp_file = NULL; + uint8_t *kernel = g_try_malloc(size); + if (!kernel) { + error_setg(errp, "Out of memory reading kernel section"); + goto cleanup; + } *kernel_path = NULL; if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) { @@ -218,6 +224,11 @@ static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, } *crc = crc32(*crc, kernel, size); + if (qcrypto_hash_update(hash0, (char *)kernel, size, errp) != 0 || + qcrypto_hash_update(hash1, (char *)kernel, size, errp) != 0) { + goto cleanup; + } + g_free(kernel); fclose(tmp_file); return true; @@ -229,10 +240,12 @@ static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, g_free(*kernel_path); *kernel_path = NULL; + g_free(kernel); return false; } static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, + QCryptoHash *hash0, QCryptoHash *hash1, uint32_t *crc, Error **errp) { size_t got = fread(cmdline, 1, size, f); @@ -242,28 +255,47 @@ static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, } *crc = crc32(*crc, (uint8_t *)cmdline, size); + if (qcrypto_hash_update(hash0, cmdline, size, errp) != 0 || + qcrypto_hash_update(hash1, cmdline, size, errp) != 0) { + return false; + } return true; } static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, - uint8_t *ramdisk, uint32_t *crc, Error **errp) + QCryptoHash *hash0, QCryptoHash *h, uint32_t *crc, + Error **errp) { size_t got; + bool ret = false; + uint8_t *ramdisk = g_try_malloc(size); + if (!ramdisk) { + error_setg(errp, "Out of memory reading initrd section"); + goto cleanup; + } got = fread(ramdisk, 1, size, eif); if ((uint64_t) got != size) { error_setg(errp, "Failed to read EIF ramdisk section data"); - return false; + goto cleanup; } got = fwrite(ramdisk, 1, size, initrd); if ((uint64_t) got != size) { error_setg(errp, "Failed to write EIF ramdisk data to temporary file"); - return false; + goto cleanup; } *crc = crc32(*crc, ramdisk, size); - return true; + if (qcrypto_hash_update(hash0, (char *)ramdisk, size, errp) != 0 || + qcrypto_hash_update(h, (char *)ramdisk, size, errp) != 0) { + goto cleanup; + } + ret = true; + + cleanup: + g_free(ramdisk); + return ret; } static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size, @@ -391,34 +423,10 @@ static long get_file_size(FILE *f, Error **errp) return size; } -static bool get_SHA384_digest(GList *list, uint8_t *digest, Error **errp) +static bool get_SHA384_hash(QCryptoHash *h, uint8_t *hash, Error **errp) { - size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; - size_t list_len = g_list_length(list); - struct iovec *iovec_list = g_new0(struct iovec, list_len); - bool ret = true; - GList *l; - int i; - - for (i = 0, l = list; l != NULL; l = l->next, i++) { - iovec_list[i] = *(struct iovec *) l->data; - } - - if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iovec_list, list_len, - &digest, &digest_len, errp) < 0) { - ret = false; - } - - g_free(iovec_list); - return ret; -} - -static void free_iovec(struct iovec *iov) -{ - if (iov) { - g_free(iov->iov_base); - g_free(iov); - } + size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + return qcrypto_hash_finalize_bytes(h, &hash, &hash_len, errp) == 0; } /* @@ -427,8 +435,8 @@ static void free_iovec(struct iovec *iov) */ bool read_eif_file(const char *eif_path, const char *machine_initrd, char **kernel_path, char **initrd_path, char **cmdline, - uint8_t *image_sha384, uint8_t *bootstrap_sha384, - uint8_t *app_sha384, uint8_t *fingerprint_sha384, + uint8_t *image_hash, uint8_t *bootstrap_hash, + uint8_t *app_hash, uint8_t *fingerprint_hash, bool *signature_found, Error **errp) { FILE *f = NULL; @@ -438,18 +446,29 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, uint32_t crc = 0; EifHeader eif_header; bool seen_sections[EIF_SECTION_MAX] = {false}; - /* kernel + ramdisks + cmdline sha384 hash */ - GList *iov_PCR0 = NULL; - /* kernel + boot ramdisk + cmdline sha384 hash */ - GList *iov_PCR1 = NULL; - /* application ramdisk(s) hash */ - GList *iov_PCR2 = NULL; - uint8_t *ptr = NULL; - struct iovec *iov_ptr = NULL; + /* kernel + ramdisks + cmdline SHA384 hash */ + g_autoptr(QCryptoHash) hash0 = NULL; + /* kernel + boot ramdisk + cmdline SHA384 hash */ + g_autoptr(QCryptoHash) hash1 = NULL; + /* application ramdisk(s) SHA384 hash */ + g_autoptr(QCryptoHash) hash2 = NULL; *signature_found = false; *kernel_path = *initrd_path = *cmdline = NULL; + hash0 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash0) { + goto cleanup; + } + hash1 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash1) { + goto cleanup; + } + hash2 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash2) { + goto cleanup; + } + f = fopen(eif_path, "rb"); if (f == NULL) { error_setg_errno(errp, errno, "Failed to open %s", eif_path); @@ -517,21 +536,8 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, goto cleanup; } - ptr = g_try_malloc(hdr.section_size); - if (!ptr) { - error_setg(errp, "Out of memory reading kernel section"); - goto cleanup; - } - - iov_ptr = g_malloc(sizeof(struct iovec)); - iov_ptr->iov_base = ptr; - iov_ptr->iov_len = hdr.section_size; - - iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); - iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); - - if (!read_eif_kernel(f, hdr.section_size, kernel_path, ptr, &crc, - errp)) { + if (!read_eif_kernel(f, hdr.section_size, kernel_path, hash0, + hash1, &crc, errp)) { goto cleanup; } @@ -539,7 +545,6 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, case EIF_SECTION_CMDLINE: { uint64_t size; - uint8_t *cmdline_copy; if (seen_sections[EIF_SECTION_CMDLINE]) { error_setg(errp, "Invalid EIF image. More than 1 cmdline " "section"); @@ -551,33 +556,26 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, error_setg(errp, "Out of memory reading command line section"); goto cleanup; } - if (!read_eif_cmdline(f, size, *cmdline, &crc, errp)) { + if (!read_eif_cmdline(f, size, *cmdline, hash0, hash1, &crc, + errp)) { goto cleanup; } (*cmdline)[size] = '\0'; - /* - * We make a copy of '*cmdline' for putting it in iovecs so that - * we can easily free all the iovec entries later as we cannot - * free '*cmdline' which is used by the caller. - */ - cmdline_copy = g_memdup2(*cmdline, size); - - iov_ptr = g_malloc(sizeof(struct iovec)); - iov_ptr->iov_base = cmdline_copy; - iov_ptr->iov_len = size; - - iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); - iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); break; } case EIF_SECTION_RAMDISK: { + QCryptoHash *h = hash2; if (!seen_sections[EIF_SECTION_RAMDISK]) { /* * If this is the first time we are seeing a ramdisk section, - * we need to create the initrd temporary file. + * we need to: + * 1) hash it into bootstrap (hash1) instead of app (hash2) + * along with image (hash0) + * 2) create the initrd temporary file. */ + h = hash1; if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) { goto cleanup; } @@ -589,29 +587,7 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, } } - ptr = g_try_malloc(hdr.section_size); - if (!ptr) { - error_setg(errp, "Out of memory reading initrd section"); - goto cleanup; - } - - iov_ptr = g_malloc(sizeof(struct iovec)); - iov_ptr->iov_base = ptr; - iov_ptr->iov_len = hdr.section_size; - - iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); - /* - * If it's the first ramdisk, we need to hash it into bootstrap - * i.e., iov_PCR1, otherwise we need to hash it into app i.e., - * iov_PCR2. - */ - if (!seen_sections[EIF_SECTION_RAMDISK]) { - iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); - } else { - iov_PCR2 = g_list_append(iov_PCR2, iov_ptr); - } - - if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, ptr, + if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, hash0, h, &crc, errp)) { goto cleanup; } @@ -621,7 +597,7 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, case EIF_SECTION_SIGNATURE: *signature_found = true; if (!get_signature_fingerprint_sha384(f, hdr.section_size, - fingerprint_sha384, &crc, + fingerprint_hash, &crc, errp)) { goto cleanup; } @@ -692,52 +668,28 @@ bool read_eif_file(const char *eif_path, const char *machine_initrd, goto cleanup; } - ptr = g_try_malloc(machine_initrd_size); - if (!ptr) { - error_setg(errp, "Out of memory reading initrd file"); - goto cleanup; - } - - iov_ptr = g_malloc(sizeof(struct iovec)); - iov_ptr->iov_base = ptr; - iov_ptr->iov_len = machine_initrd_size; - - iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); - iov_PCR2 = g_list_append(iov_PCR2, iov_ptr); - if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f, - machine_initrd_size, ptr, &crc, errp)) { + machine_initrd_size, hash0, hash2, &crc, errp)) { goto cleanup; } } - if (!get_SHA384_digest(iov_PCR0, image_sha384, errp)) { + if (!get_SHA384_hash(hash0, image_hash, errp)) { goto cleanup; } - if (!get_SHA384_digest(iov_PCR1, bootstrap_sha384, errp)) { + if (!get_SHA384_hash(hash1, bootstrap_hash, errp)) { goto cleanup; } - if (!get_SHA384_digest(iov_PCR2, app_sha384, errp)) { + if (!get_SHA384_hash(hash2, app_hash, errp)) { goto cleanup; } - /* - * We only need to free iov_PCR0 entries because iov_PCR1 and - * iov_PCR2 iovec entries are subsets of iov_PCR0 iovec entries. - */ - g_list_free_full(iov_PCR0, (GDestroyNotify) free_iovec); - g_list_free(iov_PCR1); - g_list_free(iov_PCR2); fclose(f); fclose(initrd_path_f); safe_fclose(machine_initrd_f); return true; cleanup: - g_list_free_full(iov_PCR0, (GDestroyNotify) free_iovec); - g_list_free(iov_PCR1); - g_list_free(iov_PCR2); - safe_fclose(f); safe_fclose(initrd_path_f); safe_fclose(machine_initrd_f); diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c index b6263ae127..acbbc06b71 100644 --- a/hw/i386/nitro_enclave.c +++ b/hw/i386/nitro_enclave.c @@ -117,13 +117,13 @@ static void nitro_enclave_machine_reset(MachineState *machine, ResetType type) memset(ne_state->vnsm->pcrs, 0, sizeof(ne_state->vnsm->pcrs)); /* PCR0 */ - ne_state->vnsm->extend_pcr(ne_state->vnsm, 0, ne_state->image_sha384, + ne_state->vnsm->extend_pcr(ne_state->vnsm, 0, ne_state->image_hash, QCRYPTO_HASH_DIGEST_LEN_SHA384); /* PCR1 */ - ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_sha384, + ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_hash, QCRYPTO_HASH_DIGEST_LEN_SHA384); /* PCR2 */ - ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_sha384, + ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_hash, QCRYPTO_HASH_DIGEST_LEN_SHA384); /* PCR3 */ if (ne_state->parent_role) { @@ -140,7 +140,7 @@ static void nitro_enclave_machine_reset(MachineState *machine, ResetType type) /* PCR8 */ if (ne_state->signature_found) { ne_state->vnsm->extend_pcr(ne_state->vnsm, 8, - ne_state->fingerprint_sha384, + ne_state->fingerprint_hash, QCRYPTO_HASH_DIGEST_LEN_SHA384); } @@ -173,8 +173,8 @@ static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg, if (!read_eif_file(machine->kernel_filename, machine->initrd_filename, &eif_kernel, &eif_initrd, &eif_cmdline, - nems->image_sha384, nems->bootstrap_sha384, - nems->app_sha384, nems->fingerprint_sha384, + nems->image_hash, nems->bootstrap_hash, + nems->app_hash, nems->fingerprint_hash, &(nems->signature_found), &err)) { error_report_err(err); exit(1); diff --git a/include/hw/i386/nitro_enclave.h b/include/hw/i386/nitro_enclave.h index b65875033c..885163ff64 100644 --- a/include/hw/i386/nitro_enclave.h +++ b/include/hw/i386/nitro_enclave.h @@ -44,14 +44,14 @@ struct NitroEnclaveMachineState { /* Machine state */ VirtIONSM *vnsm; - /* kernel + ramdisks + cmdline sha384 hash */ - uint8_t image_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; - /* kernel + boot ramdisk + cmdline sha384 hash */ - uint8_t bootstrap_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; - /* application ramdisk(s) hash */ - uint8_t app_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; - /* certificate fingerprint hash */ - uint8_t fingerprint_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* kernel + ramdisks + cmdline SHA384 hash */ + uint8_t image_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* kernel + boot ramdisk + cmdline SHA384 hash */ + uint8_t bootstrap_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* application ramdisk(s) SHA384 hash */ + uint8_t app_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* certificate fingerprint SHA384 hash */ + uint8_t fingerprint_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; bool signature_found; }; From d4ee9a113e36e90f76cfec892cb9aacd3cd11c17 Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Sat, 9 Nov 2024 18:32:08 +0600 Subject: [PATCH 0276/2892] hw/virtio/virtio-nsm: Support string data for extendPCR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NSM device in AWS Nitro Enclaves supports extending with both bytestring and string data. Signed-off-by: Dorjoy Chowdhury Reviewed-by: Alexander Graf Message-ID: <20241109123208.24281-1-dorjoychy111@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/virtio/virtio-nsm.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c index a3db8eef3e..6830fcfe17 100644 --- a/hw/virtio/virtio-nsm.c +++ b/hw/virtio/virtio-nsm.c @@ -444,7 +444,7 @@ static bool handle_describe_pcr(VirtIONSM *vnsm, struct iovec *request, * key = String("index"), * value = Uint8(pcr), * key = String("data"), - * value = Byte_String(data), + * value = Byte_String(data) || String(data), * } * } * } @@ -504,14 +504,21 @@ static enum NSMResponseTypes get_nsm_extend_pcr_req(uint8_t *req, size_t len, if (cbor_string_length(pair[i].key) == 4 && memcmp(str, "data", 4) == 0) { - if (!cbor_isa_bytestring(pair[i].value)) { + if (cbor_isa_bytestring(pair[i].value)) { + str = cbor_bytestring_handle(pair[i].value); + if (!str) { + goto cleanup; + } + nsm_req->data_len = cbor_bytestring_length(pair[i].value); + } else if (cbor_isa_string(pair[i].value)) { + str = cbor_string_handle(pair[i].value); + if (!str) { + goto cleanup; + } + nsm_req->data_len = cbor_string_length(pair[i].value); + } else { goto cleanup; } - str = cbor_bytestring_handle(pair[i].value); - if (!str) { - goto cleanup; - } - nsm_req->data_len = cbor_bytestring_length(pair[i].value); /* * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as * we already check for the max request size before processing From 37bae93ce54cfa1eeb8c51c9fa883ad781e0853b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Nov 2024 14:07:25 +0100 Subject: [PATCH 0277/2892] hw/riscv/virt: Remove pointless GPEX_HOST() cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to QOM-cast twice, since the intermediate value is not used. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20241125140535.4526-7-philmd@linaro.org> --- hw/riscv/virt.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 45a8c4f819..2feb851f15 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1140,23 +1140,21 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, dev = qdev_new(TYPE_GPEX_HOST); /* Set GPEX object properties for the virt machine */ - object_property_set_uint(OBJECT(GPEX_HOST(dev)), PCI_HOST_ECAM_BASE, + object_property_set_uint(OBJECT(dev), PCI_HOST_ECAM_BASE, ecam_base, NULL); - object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_ECAM_SIZE, + object_property_set_int(OBJECT(dev), PCI_HOST_ECAM_SIZE, ecam_size, NULL); - object_property_set_uint(OBJECT(GPEX_HOST(dev)), - PCI_HOST_BELOW_4G_MMIO_BASE, + object_property_set_uint(OBJECT(dev), PCI_HOST_BELOW_4G_MMIO_BASE, mmio_base, NULL); - object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_BELOW_4G_MMIO_SIZE, + object_property_set_int(OBJECT(dev), PCI_HOST_BELOW_4G_MMIO_SIZE, mmio_size, NULL); - object_property_set_uint(OBJECT(GPEX_HOST(dev)), - PCI_HOST_ABOVE_4G_MMIO_BASE, + object_property_set_uint(OBJECT(dev), PCI_HOST_ABOVE_4G_MMIO_BASE, high_mmio_base, NULL); - object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_ABOVE_4G_MMIO_SIZE, + object_property_set_int(OBJECT(dev), PCI_HOST_ABOVE_4G_MMIO_SIZE, high_mmio_size, NULL); - object_property_set_uint(OBJECT(GPEX_HOST(dev)), PCI_HOST_PIO_BASE, + object_property_set_uint(OBJECT(dev), PCI_HOST_PIO_BASE, pio_base, NULL); - object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_PIO_SIZE, + object_property_set_int(OBJECT(dev), PCI_HOST_PIO_SIZE, pio_size, NULL); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -1189,7 +1187,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ + i); } - GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(GPEX_HOST(dev))->bus; + GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus; return dev; } From e20a4425ca360b473182ef60e00319c07e3a8799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 18:48:21 +0100 Subject: [PATCH 0278/2892] hw/nvram/fw_cfg: Rename fw_cfg_add_[file]_from_generator() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fw_cfg_add_from_generator() is adding a 'file' entry, so rename as fw_cfg_add_file_from_generator() for clarity. Besides, we might introduce generators for other entry types. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241206181352.6836-2-philmd@linaro.org> --- hw/nvram/fw_cfg.c | 4 ++-- include/hw/nvram/fw_cfg.h | 6 +++--- system/vl.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index b644577734..fe3b86135a 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1027,8 +1027,8 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, return NULL; } -bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename, - const char *gen_id, Error **errp) +bool fw_cfg_add_file_from_generator(FWCfgState *s, const char *filename, + const char *gen_id, Error **errp) { FWCfgDataGeneratorClass *klass; GByteArray *array; diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index fa42677619..14e68966c5 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -291,7 +291,7 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); /** - * fw_cfg_add_from_generator: + * fw_cfg_add_file_from_generator: * @s: fw_cfg device being modified * @filename: name of new fw_cfg file item * @gen_id: name of object implementing FW_CFG_DATA_GENERATOR interface @@ -307,8 +307,8 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, * * Returns: %true on success, %false on error. */ -bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename, - const char *gen_id, Error **errp); +bool fw_cfg_add_file_from_generator(FWCfgState *s, const char *filename, + const char *gen_id, Error **errp); /** * fw_cfg_add_extra_pci_roots: diff --git a/system/vl.c b/system/vl.c index 2f855d83fb..f103532a9a 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1184,7 +1184,7 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) size = strlen(str); /* NUL terminator NOT included in fw_cfg blob */ buf = g_memdup(str, size); } else if (nonempty_str(gen_id)) { - if (!fw_cfg_add_from_generator(fw_cfg, name, gen_id, errp)) { + if (!fw_cfg_add_file_from_generator(fw_cfg, name, gen_id, errp)) { return -1; } return 0; From 51680269fe08a739ac95dd651a11430b318b5bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 17:04:09 +0100 Subject: [PATCH 0279/2892] hw/nvram/fw_cfg: Pass QOM parent to fw_cfg_add_file_from_generator() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently fw_cfg_add_file_from_generator() is restricted to command line created objects which reside in the '/objects' QOM container. In order to extend to other types of containers, pass the QOM parent by argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241206181352.6836-3-philmd@linaro.org> --- hw/nvram/fw_cfg.c | 11 ++++++----- include/hw/nvram/fw_cfg.h | 10 ++++++---- system/vl.c | 3 ++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index fe3b86135a..b94cd27bd8 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1027,22 +1027,23 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, return NULL; } -bool fw_cfg_add_file_from_generator(FWCfgState *s, const char *filename, - const char *gen_id, Error **errp) +bool fw_cfg_add_file_from_generator(FWCfgState *s, + Object *parent, const char *part, + const char *filename, Error **errp) { FWCfgDataGeneratorClass *klass; GByteArray *array; Object *obj; gsize size; - obj = object_resolve_path_component(object_get_objects_root(), gen_id); + obj = object_resolve_path_component(parent, part); if (!obj) { - error_setg(errp, "Cannot find object ID '%s'", gen_id); + error_setg(errp, "Cannot find object ID '%s'", part); return false; } if (!object_dynamic_cast(obj, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)) { error_setg(errp, "Object ID '%s' is not a '%s' subclass", - gen_id, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE); + part, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE); return false; } klass = FW_CFG_DATA_GENERATOR_GET_CLASS(obj); diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 14e68966c5..fcb06f18cc 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -294,11 +294,12 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, * fw_cfg_add_file_from_generator: * @s: fw_cfg device being modified * @filename: name of new fw_cfg file item - * @gen_id: name of object implementing FW_CFG_DATA_GENERATOR interface + * @part: name of object implementing FW_CFG_DATA_GENERATOR interface + * @parent: the object in which to resolve the @part * @errp: pointer to a NULL initialized error object * * Add a new NAMED fw_cfg item with the content generated from the - * @gen_id object. The data generated by the @gen_id object is copied + * @part object. The data generated by the @part object is copied * into the data structure of the fw_cfg device. * The next available (unused) selector key starting at FW_CFG_FILE_FIRST * will be used; also, a new entry will be added to the file directory @@ -307,8 +308,9 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, * * Returns: %true on success, %false on error. */ -bool fw_cfg_add_file_from_generator(FWCfgState *s, const char *filename, - const char *gen_id, Error **errp); +bool fw_cfg_add_file_from_generator(FWCfgState *s, + Object *parent, const char *part, + const char *filename, Error **errp); /** * fw_cfg_add_extra_pci_roots: diff --git a/system/vl.c b/system/vl.c index f103532a9a..4a370da624 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1184,7 +1184,8 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) size = strlen(str); /* NUL terminator NOT included in fw_cfg blob */ buf = g_memdup(str, size); } else if (nonempty_str(gen_id)) { - if (!fw_cfg_add_file_from_generator(fw_cfg, name, gen_id, errp)) { + if (!fw_cfg_add_file_from_generator(fw_cfg, object_get_objects_root(), + gen_id, name, errp)) { return -1; } return 0; From c66be53ce7460d061cbd9061a85ecfb219515901 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Dec 2024 21:14:28 -0600 Subject: [PATCH 0280/2892] target/arm: Use float_round_to_odd in helper_fcvtx_f64_to_f32 Softfloat has native support for round-to-odd. Use it. Signed-off-by: Richard Henderson Message-id: 20241206031428.78634-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 28de7468cd..fb6fe0fcaa 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -408,23 +408,13 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) { - /* Von Neumann rounding is implemented by using round-to-zero - * and then setting the LSB of the result if Inexact was raised. - */ float32 r; float_status *fpst = &env->vfp.fp_status; - float_status tstat = *fpst; - int exflags; + int old = get_float_rounding_mode(fpst); - set_float_rounding_mode(float_round_to_zero, &tstat); - set_float_exception_flags(0, &tstat); - r = float64_to_float32(a, &tstat); - exflags = get_float_exception_flags(&tstat); - if (exflags & float_flag_inexact) { - r = make_float32(float32_val(r) | 1); - } - exflags |= get_float_exception_flags(fpst); - set_float_exception_flags(exflags, fpst); + set_float_rounding_mode(float_round_to_odd, fpst); + r = float64_to_float32(a, fpst); + set_float_rounding_mode(old, fpst); return r; } From 3297e31392890842db99b572c08c836cbcab9011 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 6 Dec 2024 11:22:51 -0800 Subject: [PATCH 0281/2892] docs/system/arm/orangepi: update links www.orangepi.org does not support https, it's expected to stick to http. Reviewed-by: Niek Linnenbank Signed-off-by: Pierrick Bouvier Message-id: 20241206192254.3889131-2-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/orangepi.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index 9afa54213b..db87e81fec 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -119,7 +119,7 @@ Orange Pi PC images Note that the mainline kernel does not have a root filesystem. You may provide it with an official Orange Pi PC image from the official website: - http://www.orangepi.org/downloadresources/ + http://www.orangepi.org/html/serviceAndSupport/index.html Another possibility is to run an Armbian image for Orange Pi PC which can be downloaded from: @@ -213,7 +213,7 @@ including the Orange Pi PC. NetBSD 9.0 is known to work best for the Orange Pi P board and provides a fully working system with serial console, networking and storage. For the Orange Pi PC machine, get the 'evbarm-earmv7hf' based image from: - https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz + https://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz The image requires manually installing U-Boot in the image. Build U-Boot with the orangepi_pc_defconfig configuration as described in the previous section. From 3314731a7f5e96e7c4649b8a5a8253887a2242a7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 6 Dec 2024 11:22:52 -0800 Subject: [PATCH 0282/2892] docs/system/arm/fby35: document execute-in-place property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Signed-off-by: Pierrick Bouvier Message-id: 20241206192254.3889131-3-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/fby35.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/system/arm/fby35.rst b/docs/system/arm/fby35.rst index bf6da6baa2..e19274e75c 100644 --- a/docs/system/arm/fby35.rst +++ b/docs/system/arm/fby35.rst @@ -45,3 +45,8 @@ process starts. $ screen /dev/tty0 # In a separate TMUX pane, terminal window, etc. $ screen /dev/tty1 $ (qemu) c # Start the boot process once screen is setup. + +This machine model supports emulation of the boot from the CE0 flash device by +setting option ``execute-in-place``. When using this option, the CPU fetches +instructions to execute by reading CE0 and not from a preloaded ROM +initialized at machine init time. As a result, execution will be slower. From 332a04b63a6c17ea38a091d1593ec78f3a8a063f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 6 Dec 2024 11:22:53 -0800 Subject: [PATCH 0283/2892] docs/system/arm/xlnx-versal-virt: document ospi-flash property Signed-off-by: Pierrick Bouvier Message-id: 20241206192254.3889131-4-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 0bafc76469..c5f35f28e4 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -178,6 +178,9 @@ Run the following at the U-Boot prompt: fdt set /chosen/dom0 reg <0x00000000 0x40000000 0x0 0x03100000> booti 30000000 - 20000000 +It's possible to change the OSPI flash model emulated by using the machine model +option ``ospi-flash``. + BBRAM File Backend """""""""""""""""" BBRAM can have an optional file backend, which must be a seekable From e30e6fdcc59c980e83514ba731394a85428a9405 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 6 Dec 2024 11:22:54 -0800 Subject: [PATCH 0284/2892] docs/system/arm/virt: document missing properties Signed-off-by: Pierrick Bouvier Message-id: 20241206192254.3889131-5-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell --- docs/system/arm/virt.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index e67e7f0f7c..f87adeb444 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -167,10 +167,18 @@ iommu ``smmuv3`` Create an SMMUv3 +default-bus-bypass-iommu + Set ``on``/``off`` to enable/disable `bypass_iommu + `_ + for default root bus. + ras Set ``on``/``off`` to enable/disable reporting host memory errors to a guest using ACPI and guest external abort exceptions. The default is off. +acpi + Set ``on``/``off``/``auto`` to enable/disable ACPI. + dtb-randomness Set ``on``/``off`` to pass random seeds via the guest DTB rng-seed and kaslr-seed nodes (in both "/chosen" and @@ -184,6 +192,14 @@ dtb-randomness dtb-kaslr-seed A deprecated synonym for dtb-randomness. +x-oem-id + Set string (up to 6 bytes) to override the default value of field OEMID in ACPI + table header. + +x-oem-table-id + Set string (up to 8 bytes) to override the default value of field OEM Table ID + in ACPI table header. + Linux guest kernel configuration """""""""""""""""""""""""""""""" From 08e199bbc6a4a9b9d929a472e14cc47116811afb Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 9 Dec 2024 10:12:42 -0800 Subject: [PATCH 0285/2892] MAINTAINERS: correct my email address Mea culpa, I don't know how I got this wrong in 2dfe93699c. Still getting used to the new address, I suppose. Somehow I got it right in the mailmap, though. Signed-off-by: Brian Cain Message-id: 20241209181242.1434231-1-brian.cain@oss.qualcomm.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7c1ab51b2d..822f34344b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -226,7 +226,7 @@ F: target/avr/ F: tests/functional/test_avr_mega2560.py Hexagon TCG CPUs -M: Brian Cain +M: Brian Cain S: Supported F: target/hexagon/ X: target/hexagon/idef-parser/ From 1e32ee23cdc5405a5b575286bbfa705d78da410c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:43 +0000 Subject: [PATCH 0286/2892] target/arm: Move some TLBI insns to their own source file target/arm/helper.c is very large and unwieldy. One subset of code that we can pull out into its own file is the cpreg arrays and corresponding functions for the TLBI instructions. Because these are instructions they are only relevant for TCG and we can make the new file only be built for CONFIG_TCG. In this commit we move the AArch32 instructions from: not_v7_cp_reginfo[] v7_cp_reginfo[] v7mp_cp_reginfo[] v8_cp_reginfo[] into a new file target/arm/tcg/tlb-insns.c. A few small functions are used both by functions we haven't yet moved across and by functions we have already moved. We temporarily make these global with a prototype in cpregs.h; when the move of all TLBI insns is complete these will return to being file-local. For CONFIG_TCG, this is just moving code around. For a KVM only build, these cpregs will no longer be added to the cpregs hashtable for the CPU. However this should not be a behaviour change, because: * we never try to migration sync or otherwise include ARM_CP_NO_RAW cpregs * for migration we treat the kernel's list of system registers as the authoritative one, so these TLBI insns were never in it anyway The no-tcg stub of define_tlb_insn_regs() therefore does nothing. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-2-peter.maydell@linaro.org --- target/arm/cpregs.h | 14 +++ target/arm/helper.c | 231 ++-------------------------------- target/arm/internals.h | 3 + target/arm/tcg-stubs.c | 5 + target/arm/tcg/meson.build | 1 + target/arm/tcg/tlb-insns.c | 246 +++++++++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+), 220 deletions(-) create mode 100644 target/arm/tcg/tlb-insns.c diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index cc7c54378f..26c27dc5cb 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1134,4 +1134,18 @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) return ri->opc1 == 4 || ri->opc1 == 5; } +/* + * Temporary declarations of functions until the move to tlb_insn_helper.c + * is complete and we can make the functions static again + */ +CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread); +CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread); +bool tlb_force_broadcast(CPUARMState *env); +void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); + #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index f38eb054c0..6a9bf70f18 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -366,8 +366,8 @@ static CPAccessResult access_tacr(CPUARMState *env, const ARMCPRegInfo *ri, } /* Check for traps from EL1 due to HCR_EL2.TTLB. */ -static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { return CP_ACCESS_TRAP_EL2; @@ -376,8 +376,8 @@ static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, } /* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBIS. */ -static CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBIS))) { @@ -455,104 +455,16 @@ static int alle1_tlbmask(CPUARMState *env) ARMMMUIdxBit_Stage2_S); } - -/* IS variants of TLB operations must affect all cores */ -static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_all_cpus_synced(cs); -} - -static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_all_cpus_synced(cs); -} - -static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); -} - -static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); -} - /* * Non-IS variants of TLB operations are upgraded to * IS versions if we are at EL1 and HCR_EL2.FB is effectively set to * force broadcast of these operations. */ -static bool tlb_force_broadcast(CPUARMState *env) +bool tlb_force_broadcast(CPUARMState *env) { return arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_FB); } -static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate all (TLBIALL) */ - CPUState *cs = env_cpu(env); - - if (tlb_force_broadcast(env)) { - tlb_flush_all_cpus_synced(cs); - } else { - tlb_flush(cs); - } -} - -static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ - CPUState *cs = env_cpu(env); - - value &= TARGET_PAGE_MASK; - if (tlb_force_broadcast(env)) { - tlb_flush_page_all_cpus_synced(cs, value); - } else { - tlb_flush_page(cs, value); - } -} - -static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate by ASID (TLBIASID) */ - CPUState *cs = env_cpu(env); - - if (tlb_force_broadcast(env)) { - tlb_flush_all_cpus_synced(cs); - } else { - tlb_flush(cs); - } -} - -static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ - CPUState *cs = env_cpu(env); - - value &= TARGET_PAGE_MASK; - if (tlb_force_broadcast(env)) { - tlb_flush_page_all_cpus_synced(cs, value); - } else { - tlb_flush_page(cs, value); - } -} - static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -586,8 +498,8 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); } -static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); @@ -595,8 +507,8 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); } -static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); @@ -605,24 +517,6 @@ static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMMMUIdxBit_E2); } -static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; - - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); -} - -static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; - - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2); -} - static const ARMCPRegInfo cp_reginfo[] = { /* * Define the secure and non-secure FCSE identifier CP registers @@ -732,22 +626,6 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { */ { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* - * MMU TLB control. Note that the wildcarding means we cover not just - * the unified TLB ops but also the dside/iside/inner-shareable variants. - */ - { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, - .type = ARM_CP_NO_RAW }, - { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, - .type = ARM_CP_NO_RAW }, - { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, - .type = ARM_CP_NO_RAW }, - { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, - .type = ARM_CP_NO_RAW }, { .name = "PRRR", .cp = 15, .crn = 10, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP }, { .name = "NMRR", .cp = 15, .crn = 10, .crm = 2, @@ -2331,55 +2209,6 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 1, .opc2 = 0, .fgt = FGT_ISR_EL1, .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, - /* 32 bit ITLB invalidates */ - { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiall_write }, - { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiasid_write }, - /* 32 bit DTLB invalidates */ - { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiall_write }, - { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiasid_write }, - /* 32 bit TLB invalidates */ - { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiall_write }, - { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiasid_write }, - { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimvaa_write }, -}; - -static const ARMCPRegInfo v7mp_cp_reginfo[] = { - /* 32 bit TLB invalidates, Inner Shareable */ - { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, - .writefn = tlbiall_is_write }, - { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, - .writefn = tlbimva_is_write }, - { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, - .writefn = tlbiasid_is_write }, - { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, - .writefn = tlbimvaa_is_write }, }; static const ARMCPRegInfo pmovsset_cp_reginfo[] = { @@ -5833,42 +5662,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.par_el[1]), .writefn = par_write }, #endif - /* TLB invalidate last level of translation table walk */ - { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, - .writefn = tlbimva_is_write }, - { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, - .writefn = tlbimvaa_is_write }, - { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimvaa_write }, - { .name = "TLBIMVALH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_write }, - { .name = "TLBIMVALHIS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_is_write }, - { .name = "TLBIIPAS2", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2_hyp_write }, - { .name = "TLBIIPAS2IS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2is_hyp_write }, - { .name = "TLBIIPAS2L", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2_hyp_write }, - { .name = "TLBIIPAS2LIS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2is_hyp_write }, /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_ticab }, @@ -8734,6 +8527,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, not_v8_cp_reginfo); } + define_tlb_insn_regs(cpu); + if (arm_feature(env, ARM_FEATURE_V6)) { /* The ID registers all have impdef reset values */ ARMCPRegInfo v6_idregs[] = { @@ -8839,10 +8634,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V6K)) { define_arm_cp_regs(cpu, v6k_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_V7MP) && - !arm_feature(env, ARM_FEATURE_PMSA)) { - define_arm_cp_regs(cpu, v7mp_cp_reginfo); - } if (arm_feature(env, ARM_FEATURE_V7VE)) { define_arm_cp_regs(cpu, pmovsset_cp_reginfo); } diff --git a/target/arm/internals.h b/target/arm/internals.h index e37f459af3..2adedb9477 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1727,6 +1727,9 @@ static inline uint64_t pauth_ptr_mask(ARMVAParameters param) /* Add the cpreg definitions for debug related system registers */ void define_debug_regs(ARMCPU *cpu); +/* Add the cpreg definitions for TLBI instructions */ +void define_tlb_insn_regs(ARMCPU *cpu); + /* Effective value of MDCR_EL2 */ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) { diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index 152b172e24..f3f45d54f2 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -25,3 +25,8 @@ void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, void assert_hflags_rebuild_correctly(CPUARMState *env) { } + +/* TLBI insns are only used by TCG, so we don't need to do anything for KVM */ +void define_tlb_insn_regs(ARMCPU *cpu) +{ +} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 508932a249..09238989c5 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -39,6 +39,7 @@ arm_ss.add(files( 'op_helper.c', 'tlb_helper.c', 'vec_helper.c', + 'tlb-insns.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c new file mode 100644 index 0000000000..cdf23352d7 --- /dev/null +++ b/target/arm/tcg/tlb-insns.c @@ -0,0 +1,246 @@ +/* + * Helpers for TLBI insns + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "exec/exec-all.h" +#include "cpu.h" +#include "internals.h" +#include "cpu-features.h" +#include "cpregs.h" + +/* IS variants of TLB operations must affect all cores */ +static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_all_cpus_synced(cs); +} + +static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_all_cpus_synced(cs); +} + +static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); +} + +static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); +} + +static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate all (TLBIALL) */ + CPUState *cs = env_cpu(env); + + if (tlb_force_broadcast(env)) { + tlb_flush_all_cpus_synced(cs); + } else { + tlb_flush(cs); + } +} + +static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ + CPUState *cs = env_cpu(env); + + value &= TARGET_PAGE_MASK; + if (tlb_force_broadcast(env)) { + tlb_flush_page_all_cpus_synced(cs, value); + } else { + tlb_flush_page(cs, value); + } +} + +static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by ASID (TLBIASID) */ + CPUState *cs = env_cpu(env); + + if (tlb_force_broadcast(env)) { + tlb_flush_all_cpus_synced(cs); + } else { + tlb_flush(cs); + } +} + +static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ + CPUState *cs = env_cpu(env); + + value &= TARGET_PAGE_MASK; + if (tlb_force_broadcast(env)) { + tlb_flush_page_all_cpus_synced(cs, value); + } else { + tlb_flush_page(cs, value); + } +} + +static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); +} + +static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2); +} + +static const ARMCPRegInfo tlbi_not_v7_cp_reginfo[] = { + /* + * MMU TLB control. Note that the wildcarding means we cover not just + * the unified TLB ops but also the dside/iside/inner-shareable variants. + */ + { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, + .type = ARM_CP_NO_RAW }, + { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, + .type = ARM_CP_NO_RAW }, + { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, + .type = ARM_CP_NO_RAW }, + { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, + .type = ARM_CP_NO_RAW }, +}; + +static const ARMCPRegInfo tlbi_v7_cp_reginfo[] = { + /* 32 bit ITLB invalidates */ + { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, + { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, + /* 32 bit DTLB invalidates */ + { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, + { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, + /* 32 bit TLB invalidates */ + { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, + { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, + { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimvaa_write }, +}; + +static const ARMCPRegInfo tlbi_v7mp_cp_reginfo[] = { + /* 32 bit TLB invalidates, Inner Shareable */ + { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, + .writefn = tlbiall_is_write }, + { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, + .writefn = tlbimva_is_write }, + { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, + .writefn = tlbiasid_is_write }, + { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, + .writefn = tlbimvaa_is_write }, +}; + +static const ARMCPRegInfo tlbi_v8_cp_reginfo[] = { + /* AArch32 TLB invalidate last level of translation table walk */ + { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, + .writefn = tlbimva_is_write }, + { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, + .writefn = tlbimvaa_is_write }, + { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimvaa_write }, + { .name = "TLBIMVALH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_write }, + { .name = "TLBIMVALHIS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_is_write }, + { .name = "TLBIIPAS2", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2_hyp_write }, + { .name = "TLBIIPAS2IS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2is_hyp_write }, + { .name = "TLBIIPAS2L", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2_hyp_write }, + { .name = "TLBIIPAS2LIS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2is_hyp_write }, +}; + +void define_tlb_insn_regs(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + + if (!arm_feature(env, ARM_FEATURE_V7)) { + define_arm_cp_regs(cpu, tlbi_not_v7_cp_reginfo); + } else { + define_arm_cp_regs(cpu, tlbi_v7_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V7MP) && + !arm_feature(env, ARM_FEATURE_PMSA)) { + define_arm_cp_regs(cpu, tlbi_v7mp_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, tlbi_v8_cp_reginfo); + } +} From d6b6da1fc84173d1d8e8777c487c21ffeab5f5ce Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:44 +0000 Subject: [PATCH 0287/2892] target/arm: Move TLBI insns for AArch32 EL2 to tlbi_insn_helper.c Move the AArch32 TLBI insns for AArch32 EL2 to tlbi_insn_helper.c. To keep this as an obviously pure code-movement, we retain the same condition for registering tlbi_el2_cp_reginfo that we use for el2_cp_reginfo. We'll be able to simplify this condition later, since the need to define the reginfo for EL3-without-EL2 doesn't apply for the TLBI ops specifically. This move brings all the uses of tlbimva_hyp_write() and tlbimva_hyp_is_write() back into a single file, so we can move those also, and make them file-local again. The helper alle1_tlbmask() is an exception to the pattern that we only need to make these functions global temporarily, because once this refactoring is complete it will be called by both code in helper.c (vttbr_write()) and by code in tlb-insns.c. We therefore put its prototype in a permanent home in internals.h. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-3-peter.maydell@linaro.org --- target/arm/cpregs.h | 4 -- target/arm/helper.c | 74 +-------------------------------- target/arm/internals.h | 6 +++ target/arm/tcg/tlb-insns.c | 85 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 77 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 26c27dc5cb..851cd045b2 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1143,9 +1143,5 @@ CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, bool isread); bool tlb_force_broadcast(CPUARMState *env); -void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 6a9bf70f18..3c69225e1d 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -438,7 +438,7 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } -static int alle1_tlbmask(CPUARMState *env) +int alle1_tlbmask(CPUARMState *env) { /* * Note that the 'ALL' scope must invalidate both stage 1 and @@ -465,58 +465,6 @@ bool tlb_force_broadcast(CPUARMState *env) return arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_FB); } -static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx(cs, alle1_tlbmask(env)); -} - -static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, alle1_tlbmask(env)); -} - - -static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); -} - -static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); -} - -void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); -} - -void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E2); -} - static const ARMCPRegInfo cp_reginfo[] = { /* * Define the secure and non-secure FCSE identifier CP registers @@ -6248,26 +6196,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, - { .name = "TLBIALLNSNH", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_nsnh_write }, - { .name = "TLBIALLNSNHIS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_nsnh_is_write }, - { .name = "TLBIALLH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_hyp_write }, - { .name = "TLBIALLHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_hyp_is_write }, - { .name = "TLBIMVAH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_write }, - { .name = "TLBIMVAHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_is_write }, { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, diff --git a/target/arm/internals.h b/target/arm/internals.h index 2adedb9477..c3a5b1385f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1820,4 +1820,10 @@ uint64_t gt_get_countervalue(CPUARMState *env); * and CNTVCT_EL0 (this will be either 0 or the value of CNTVOFF_EL2). */ uint64_t gt_virt_cnt_offset(CPUARMState *env); + +/* + * Return mask of ARMMMUIdxBit values corresponding to an "invalidate + * all EL1" scope; this covers stage 1 and stage 2. + */ +int alle1_tlbmask(CPUARMState *env); #endif diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index cdf23352d7..66096093dc 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -99,6 +99,25 @@ static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); +} + +static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, + ARMMMUIdxBit_E2); +} + static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -117,6 +136,39 @@ static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2); } +static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx(cs, alle1_tlbmask(env)); +} + +static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, alle1_tlbmask(env)); +} + + +static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); +} + +static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); +} + static const ARMCPRegInfo tlbi_not_v7_cp_reginfo[] = { /* * MMU TLB control. Note that the wildcarding means we cover not just @@ -227,6 +279,29 @@ static const ARMCPRegInfo tlbi_v8_cp_reginfo[] = { .writefn = tlbiipas2is_hyp_write }, }; +static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = { + { .name = "TLBIALLNSNH", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_nsnh_write }, + { .name = "TLBIALLNSNHIS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_nsnh_is_write }, + { .name = "TLBIALLH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_hyp_write }, + { .name = "TLBIALLHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_hyp_is_write }, + { .name = "TLBIMVAH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_write }, + { .name = "TLBIMVAHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_is_write }, +}; + void define_tlb_insn_regs(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -243,4 +318,14 @@ void define_tlb_insn_regs(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V8)) { define_arm_cp_regs(cpu, tlbi_v8_cp_reginfo); } + /* + * We retain the existing logic for when to register these TLBI + * ops (i.e. matching the condition for el2_cp_reginfo[] in + * helper.c), but we will be able to simplify this later. + */ + if (arm_feature(env, ARM_FEATURE_EL2) + || (arm_feature(env, ARM_FEATURE_EL3) + && arm_feature(env, ARM_FEATURE_V8))) { + define_arm_cp_regs(cpu, tlbi_el2_cp_reginfo); + } } From abbb82646a1fab2075aae3c9ea8acf63e9fa80f7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:45 +0000 Subject: [PATCH 0288/2892] target/arm: Move AArch64 TLBI insns from v8_cp_reginfo[] Move the AArch64 TLBI insns that are declared in v8_cp_reginfo[] into tlb-insns.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-4-peter.maydell@linaro.org --- target/arm/cpregs.h | 11 +++ target/arm/helper.c | 182 +++---------------------------------- target/arm/tcg/tlb-insns.c | 160 ++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 171 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 851cd045b2..a14f5bb6c9 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1143,5 +1143,16 @@ CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, bool isread); bool tlb_force_broadcast(CPUARMState *env); +int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, + uint64_t addr); +int vae1_tlbbits(CPUARMState *env, uint64_t addr); +int vae1_tlbmask(CPUARMState *env); +int ipas2e1_tlbmask(CPUARMState *env, int64_t value); +void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 3c69225e1d..cc7da7f115 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4685,7 +4685,7 @@ static CPAccessResult access_tocu(CPUARMState *env, const ARMCPRegInfo *ri, * Page D4-1736 (DDI0487A.b) */ -static int vae1_tlbmask(CPUARMState *env) +int vae1_tlbmask(CPUARMState *env) { uint64_t hcr = arm_hcr_el2_eff(env); uint16_t mask; @@ -4721,8 +4721,8 @@ static int vae2_tlbmask(CPUARMState *env) } /* Return 56 if TBI is enabled, 64 otherwise. */ -static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, - uint64_t addr) +int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, + uint64_t addr) { uint64_t tcr = regime_tcr(env, mmu_idx); int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); @@ -4731,7 +4731,7 @@ static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, return (tbi >> select) & 1 ? 56 : 64; } -static int vae1_tlbbits(CPUARMState *env, uint64_t addr) +int vae1_tlbbits(CPUARMState *env, uint64_t addr) { uint64_t hcr = arm_hcr_el2_eff(env); ARMMMUIdx mmu_idx; @@ -4767,8 +4767,8 @@ static int vae2_tlbbits(CPUARMState *env, uint64_t addr) return tlbbits_for_regime(env, mmu_idx, addr); } -static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); int mask = vae1_tlbmask(env); @@ -4776,19 +4776,6 @@ static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } -static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - - if (tlb_force_broadcast(env)) { - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); - } else { - tlb_flush_by_mmuidx(cs, mask); - } -} - static int e2_tlbmask(CPUARMState *env) { return (ARMMMUIdxBit_E20_0 | @@ -4797,15 +4784,6 @@ static int e2_tlbmask(CPUARMState *env) ARMMMUIdxBit_E2); } -static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = alle1_tlbmask(env); - - tlb_flush_by_mmuidx(cs, mask); -} - static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4824,8 +4802,8 @@ static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); } -static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); int mask = alle1_tlbmask(env); @@ -4881,8 +4859,8 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); } -static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); int mask = vae1_tlbmask(env); @@ -4892,27 +4870,6 @@ static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); } -static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA, EL1&0 (AArch64 version). - * Currently handles all of VAE1, VAAE1, VAALE1 and VALE1, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae1_tlbbits(env, pageaddr); - - if (tlb_force_broadcast(env)) { - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); - } else { - tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); - } -} - static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4935,7 +4892,7 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMMMUIdxBit_E3, bits); } -static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) +int ipas2e1_tlbmask(CPUARMState *env, int64_t value) { /* * The MSB of value is the NS field, which only applies if SEL2 @@ -4948,30 +4905,6 @@ static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) : ARMMMUIdxBit_Stage2); } -static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = ipas2e1_tlbmask(env, value); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - - if (tlb_force_broadcast(env)) { - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); - } else { - tlb_flush_page_by_mmuidx(cs, pageaddr, mask); - } -} - -static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = ipas2e1_tlbmask(env, value); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); -} - #ifdef TARGET_AARCH64 typedef struct { uint64_t base; @@ -5462,99 +5395,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, .fgt = FGT_DCCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, - /* TLBI operations */ - { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVMALLE1IS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIASIDE1IS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAAE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVALE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAALE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVMALLE1, - .writefn = tlbi_aa64_vmalle1_write }, - { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIASIDE1, - .writefn = tlbi_aa64_vmalle1_write }, - { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAAE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVALE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAALE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1is_write }, - { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1is_write }, - { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_VMALLS12E1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1_write }, - { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1_write }, - { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1_write }, - { .name = "TLBI_VMALLS12E1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, #ifndef CONFIG_USER_ONLY /* 64 bit address translation operations */ { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 66096093dc..ff7698e31b 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -169,6 +169,73 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); } +static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + + if (tlb_force_broadcast(env)) { + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); + } else { + tlb_flush_by_mmuidx(cs, mask); + } +} + +static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = alle1_tlbmask(env); + + tlb_flush_by_mmuidx(cs, mask); +} + +static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA, EL1&0 (AArch64 version). + * Currently handles all of VAE1, VAAE1, VAALE1 and VALE1, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae1_tlbbits(env, pageaddr); + + if (tlb_force_broadcast(env)) { + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); + } else { + tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); + } +} + +static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = ipas2e1_tlbmask(env, value); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + if (tlb_force_broadcast(env)) { + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); + } else { + tlb_flush_page_by_mmuidx(cs, pageaddr, mask); + } +} + +static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = ipas2e1_tlbmask(env, value); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); +} + static const ARMCPRegInfo tlbi_not_v7_cp_reginfo[] = { /* * MMU TLB control. Note that the wildcarding means we cover not just @@ -277,6 +344,99 @@ static const ARMCPRegInfo tlbi_v8_cp_reginfo[] = { .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, .type = ARM_CP_NO_RAW, .access = PL2_W, .writefn = tlbiipas2is_hyp_write }, + /* AArch64 TLBI operations */ + { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1IS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1IS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1, + .writefn = tlbi_aa64_vmalle1_write }, + { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1, + .writefn = tlbi_aa64_vae1_write }, + { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1, + .writefn = tlbi_aa64_vmalle1_write }, + { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1, + .writefn = tlbi_aa64_vae1_write }, + { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1, + .writefn = tlbi_aa64_vae1_write }, + { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1, + .writefn = tlbi_aa64_vae1_write }, + { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1is_write }, + { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1is_write }, + { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_VMALLS12E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1_write }, + { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1_write }, + { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1_write }, + { .name = "TLBI_VMALLS12E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, }; static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = { From 7cadf1139db301d603ff5f1f0a3cc56391f68d12 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:46 +0000 Subject: [PATCH 0289/2892] target/arm: Move the AArch64 EL2 TLBI insns Move the AArch64 EL2 TLBI insn definitions that were in el2_cp_reginfo[] across to tlb-insns.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-5-peter.maydell@linaro.org --- target/arm/cpregs.h | 7 +++++ target/arm/helper.c | 61 ++++---------------------------------- target/arm/tcg/tlb-insns.c | 49 ++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 55 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index a14f5bb6c9..57446ae1b5 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1146,13 +1146,20 @@ bool tlb_force_broadcast(CPUARMState *env); int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, uint64_t addr); int vae1_tlbbits(CPUARMState *env, uint64_t addr); +int vae2_tlbbits(CPUARMState *env, uint64_t addr); int vae1_tlbmask(CPUARMState *env); +int vae2_tlbmask(CPUARMState *env); int ipas2e1_tlbmask(CPUARMState *env, int64_t value); +int e2_tlbmask(CPUARMState *env); void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); +void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index cc7da7f115..6942d2f2fb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4705,7 +4705,7 @@ int vae1_tlbmask(CPUARMState *env) return mask; } -static int vae2_tlbmask(CPUARMState *env) +int vae2_tlbmask(CPUARMState *env) { uint64_t hcr = arm_hcr_el2_eff(env); uint16_t mask; @@ -4748,7 +4748,7 @@ int vae1_tlbbits(CPUARMState *env, uint64_t addr) return tlbbits_for_regime(env, mmu_idx, addr); } -static int vae2_tlbbits(CPUARMState *env, uint64_t addr) +int vae2_tlbbits(CPUARMState *env, uint64_t addr) { uint64_t hcr = arm_hcr_el2_eff(env); ARMMMUIdx mmu_idx; @@ -4776,7 +4776,7 @@ void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } -static int e2_tlbmask(CPUARMState *env) +int e2_tlbmask(CPUARMState *env) { return (ARMMMUIdxBit_E20_0 | ARMMMUIdxBit_E20_2 | @@ -4784,15 +4784,6 @@ static int e2_tlbmask(CPUARMState *env) ARMMMUIdxBit_E2); } -static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = e2_tlbmask(env); - - tlb_flush_by_mmuidx(cs, mask); -} - static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4811,8 +4802,8 @@ void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } -static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); int mask = e2_tlbmask(env); @@ -4828,22 +4819,6 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); } -static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA, EL2 - * Currently handles both VAE2 and VALE2, since we don't support - * flush-last-level-only. - */ - CPUState *cs = env_cpu(env); - int mask = vae2_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae2_tlbbits(env, pageaddr); - - tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); -} - static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4870,7 +4845,7 @@ void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); } -static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, +void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { CPUState *cs = env_cpu(env); @@ -6036,30 +6011,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, - { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_alle2_write }, - { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2_write }, - { .name = "TLBI_VALE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2_write }, - { .name = "TLBI_ALLE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_alle2is_write }, - { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, - { .name = "TLBI_VALE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, #ifndef CONFIG_USER_ONLY /* * Unlike the other EL2-related AT operations, these must diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index ff7698e31b..1eebb6055c 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -191,6 +191,31 @@ static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx(cs, mask); } +static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); + + tlb_flush_by_mmuidx(cs, mask); +} + +static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA, EL2 + * Currently handles both VAE2 and VALE2, since we don't support + * flush-last-level-only. + */ + CPUState *cs = env_cpu(env); + int mask = vae2_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae2_tlbbits(env, pageaddr); + + tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); +} + static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -460,6 +485,30 @@ static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = { { .name = "TLBIMVAHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, .type = ARM_CP_NO_RAW, .access = PL2_W, .writefn = tlbimva_hyp_is_write }, + { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_alle2_write }, + { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2_write }, + { .name = "TLBI_VALE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2_write }, + { .name = "TLBI_ALLE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_alle2is_write }, + { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_VALE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, }; void define_tlb_insn_regs(ARMCPU *cpu) From 5991e5abe36e228143a6488718c71ba05da05cc3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:47 +0000 Subject: [PATCH 0290/2892] target/arm: Move AArch64 EL3 TLBI insns Move the AArch64 EL3 TLBI insns from el3_cp_reginfo[] across to tlb-insns.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-6-peter.maydell@linaro.org --- target/arm/cpregs.h | 4 +++ target/arm/helper.c | 56 +++----------------------------------- target/arm/tcg/tlb-insns.c | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 52 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 57446ae1b5..722ac5bb88 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1161,5 +1161,9 @@ void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); +void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 6942d2f2fb..baeabb5ec7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4784,15 +4784,6 @@ int e2_tlbmask(CPUARMState *env) ARMMMUIdxBit_E2); } -static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); - - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); -} - void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4811,29 +4802,14 @@ void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } -static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); } -static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA, EL3 - * Currently handles both VAE3 and VALE3, since we don't support - * flush-last-level-only. - */ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); -} - void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4856,8 +4832,8 @@ void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); } -static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { CPUState *cs = env_cpu(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); @@ -6223,30 +6199,6 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 1, .opc2 = 1, .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "TLBI_ALLE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle3is_write }, - { .name = "TLBI_VAE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, - { .name = "TLBI_VALE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, - { .name = "TLBI_ALLE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle3_write }, - { .name = "TLBI_VAE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3_write }, - { .name = "TLBI_VALE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3_write }, }; #ifndef CONFIG_USER_ONLY diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 1eebb6055c..528265404d 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -200,6 +200,15 @@ static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx(cs, mask); } +static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + CPUState *cs = CPU(cpu); + + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); +} + static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -216,6 +225,21 @@ static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); } +static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA, EL3 + * Currently handles both VAE3 and VALE3, since we don't support + * flush-last-level-only. + */ + ARMCPU *cpu = env_archcpu(env); + CPUState *cs = CPU(cpu); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); +} + static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -511,6 +535,33 @@ static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = { .writefn = tlbi_aa64_vae2is_write }, }; +static const ARMCPRegInfo tlbi_el3_cp_reginfo[] = { + { .name = "TLBI_ALLE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle3is_write }, + { .name = "TLBI_VAE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_VALE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_ALLE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle3_write }, + { .name = "TLBI_VAE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3_write }, + { .name = "TLBI_VALE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3_write }, +}; + void define_tlb_insn_regs(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -537,4 +588,7 @@ void define_tlb_insn_regs(ARMCPU *cpu) && arm_feature(env, ARM_FEATURE_V8))) { define_arm_cp_regs(cpu, tlbi_el2_cp_reginfo); } + if (arm_feature(env, ARM_FEATURE_EL3)) { + define_arm_cp_regs(cpu, tlbi_el3_cp_reginfo); + } } From 65593799571abeccd9a69f9b03be6e4c48e75532 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:48 +0000 Subject: [PATCH 0291/2892] target/arm: Move TLBI range insns Move the TLBI invalidate-range insns across to tlb-insns.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-7-peter.maydell@linaro.org --- target/arm/cpregs.h | 2 + target/arm/helper.c | 330 +------------------------------------ target/arm/tcg/tlb-insns.c | 329 ++++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 328 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 722ac5bb88..fe838bcfd9 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1142,6 +1142,8 @@ CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, bool isread); CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, bool isread); +CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread); bool tlb_force_broadcast(CPUARMState *env); int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, uint64_t addr); diff --git a/target/arm/helper.c b/target/arm/helper.c index baeabb5ec7..376aa9aecd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -388,8 +388,8 @@ CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, #ifdef TARGET_AARCH64 /* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBOS. */ -static CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBOS))) { @@ -4856,202 +4856,6 @@ int ipas2e1_tlbmask(CPUARMState *env, int64_t value) : ARMMMUIdxBit_Stage2); } -#ifdef TARGET_AARCH64 -typedef struct { - uint64_t base; - uint64_t length; -} TLBIRange; - -static ARMGranuleSize tlbi_range_tg_to_gran_size(int tg) -{ - /* - * Note that the TLBI range TG field encoding differs from both - * TG0 and TG1 encodings. - */ - switch (tg) { - case 1: - return Gran4K; - case 2: - return Gran16K; - case 3: - return Gran64K; - default: - return GranInvalid; - } -} - -static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, - uint64_t value) -{ - unsigned int page_size_granule, page_shift, num, scale, exponent; - /* Extract one bit to represent the va selector in use. */ - uint64_t select = sextract64(value, 36, 1); - ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true, false); - TLBIRange ret = { }; - ARMGranuleSize gran; - - page_size_granule = extract64(value, 46, 2); - gran = tlbi_range_tg_to_gran_size(page_size_granule); - - /* The granule encoded in value must match the granule in use. */ - if (gran != param.gran) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", - page_size_granule); - return ret; - } - - page_shift = arm_granule_bits(gran); - num = extract64(value, 39, 5); - scale = extract64(value, 44, 2); - exponent = (5 * scale) + 1; - - ret.length = (num + 1) << (exponent + page_shift); - - if (param.select) { - ret.base = sextract64(value, 0, 37); - } else { - ret.base = extract64(value, 0, 37); - } - if (param.ds) { - /* - * With DS=1, BaseADDR is always shifted 16 so that it is able - * to address all 52 va bits. The input address is perforce - * aligned on a 64k boundary regardless of translation granule. - */ - page_shift = 16; - } - ret.base <<= page_shift; - - return ret; -} - -static void do_rvae_write(CPUARMState *env, uint64_t value, - int idxmap, bool synced) -{ - ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap); - TLBIRange range; - int bits; - - range = tlbi_aa64_get_range(env, one_idx, value); - bits = tlbbits_for_regime(env, one_idx, range.base); - - if (synced) { - tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env), - range.base, - range.length, - idxmap, - bits); - } else { - tlb_flush_range_by_mmuidx(env_cpu(env), range.base, - range.length, idxmap, bits); - } -} - -static void tlbi_aa64_rvae1_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL1&0. - * Currently handles all of RVAE1, RVAAE1, RVAALE1 and RVALE1, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - - do_rvae_write(env, value, vae1_tlbmask(env), - tlb_force_broadcast(env)); -} - -static void tlbi_aa64_rvae1is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, Inner/Outer Shareable EL1&0. - * Currently handles all of RVAE1IS, RVAE1OS, RVAAE1IS, RVAAE1OS, - * RVAALE1IS, RVAALE1OS, RVALE1IS and RVALE1OS, since we don't support - * flush-for-specific-ASID-only, flush-last-level-only or inner/outer - * shareable specific flushes. - */ - - do_rvae_write(env, value, vae1_tlbmask(env), true); -} - -static void tlbi_aa64_rvae2_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL2. - * Currently handles all of RVAE2 and RVALE2, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - - do_rvae_write(env, value, vae2_tlbmask(env), - tlb_force_broadcast(env)); - - -} - -static void tlbi_aa64_rvae2is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, Inner/Outer Shareable, EL2. - * Currently handles all of RVAE2IS, RVAE2OS, RVALE2IS and RVALE2OS, - * since we don't support flush-for-specific-ASID-only, - * flush-last-level-only or inner/outer shareable specific flushes. - */ - - do_rvae_write(env, value, vae2_tlbmask(env), true); - -} - -static void tlbi_aa64_rvae3_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL3. - * Currently handles all of RVAE3 and RVALE3, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - - do_rvae_write(env, value, ARMMMUIdxBit_E3, tlb_force_broadcast(env)); -} - -static void tlbi_aa64_rvae3is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL3, Inner/Outer Shareable. - * Currently handles all of RVAE3IS, RVAE3OS, RVALE3IS and RVALE3OS, - * since we don't support flush-for-specific-ASID-only, - * flush-last-level-only or inner/outer specific flushes. - */ - - do_rvae_write(env, value, ARMMMUIdxBit_E3, true); -} - -static void tlbi_aa64_ripas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - do_rvae_write(env, value, ipas2e1_tlbmask(env, value), - tlb_force_broadcast(env)); -} - -static void tlbi_aa64_ripas2e1is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - do_rvae_write(env, value, ipas2e1_tlbmask(env, value), true); -} -#endif - static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -7312,133 +7116,6 @@ static const ARMCPRegInfo pauth_reginfo[] = { .fieldoffset = offsetof(CPUARMState, keys.apib.hi) }, }; -static const ARMCPRegInfo tlbirange_reginfo[] = { - { .name = "TLBI_RVAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAAE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVALE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAALE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAAE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVALE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAALE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAAE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVALE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAALE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1is_write }, - { .name = "TLBI_RIPAS2LE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1is_write }, - { .name = "TLBI_RVAE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RVALE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RIPAS2E1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1_write }, - { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1_write }, - { .name = "TLBI_RVAE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RVALE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RVAE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2_write }, - { .name = "TLBI_RVALE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2_write }, - { .name = "TLBI_RVAE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVALE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVAE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVALE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVAE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3_write }, - { .name = "TLBI_RVALE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3_write }, -}; - static const ARMCPRegInfo tlbios_reginfo[] = { { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, @@ -9389,9 +9066,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_rndr, cpu)) { define_arm_cp_regs(cpu, rndr_reginfo); } - if (cpu_isar_feature(aa64_tlbirange, cpu)) { - define_arm_cp_regs(cpu, tlbirange_reginfo); - } if (cpu_isar_feature(aa64_tlbios, cpu)) { define_arm_cp_regs(cpu, tlbios_reginfo); } diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 528265404d..a273c6f4b5 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -6,6 +6,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "exec/exec-all.h" #include "cpu.h" #include "internals.h" @@ -562,6 +563,329 @@ static const ARMCPRegInfo tlbi_el3_cp_reginfo[] = { .writefn = tlbi_aa64_vae3_write }, }; +#ifdef TARGET_AARCH64 +typedef struct { + uint64_t base; + uint64_t length; +} TLBIRange; + +static ARMGranuleSize tlbi_range_tg_to_gran_size(int tg) +{ + /* + * Note that the TLBI range TG field encoding differs from both + * TG0 and TG1 encodings. + */ + switch (tg) { + case 1: + return Gran4K; + case 2: + return Gran16K; + case 3: + return Gran64K; + default: + return GranInvalid; + } +} + +static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, + uint64_t value) +{ + unsigned int page_size_granule, page_shift, num, scale, exponent; + /* Extract one bit to represent the va selector in use. */ + uint64_t select = sextract64(value, 36, 1); + ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true, false); + TLBIRange ret = { }; + ARMGranuleSize gran; + + page_size_granule = extract64(value, 46, 2); + gran = tlbi_range_tg_to_gran_size(page_size_granule); + + /* The granule encoded in value must match the granule in use. */ + if (gran != param.gran) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", + page_size_granule); + return ret; + } + + page_shift = arm_granule_bits(gran); + num = extract64(value, 39, 5); + scale = extract64(value, 44, 2); + exponent = (5 * scale) + 1; + + ret.length = (num + 1) << (exponent + page_shift); + + if (param.select) { + ret.base = sextract64(value, 0, 37); + } else { + ret.base = extract64(value, 0, 37); + } + if (param.ds) { + /* + * With DS=1, BaseADDR is always shifted 16 so that it is able + * to address all 52 va bits. The input address is perforce + * aligned on a 64k boundary regardless of translation granule. + */ + page_shift = 16; + } + ret.base <<= page_shift; + + return ret; +} + +static void do_rvae_write(CPUARMState *env, uint64_t value, + int idxmap, bool synced) +{ + ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap); + TLBIRange range; + int bits; + + range = tlbi_aa64_get_range(env, one_idx, value); + bits = tlbbits_for_regime(env, one_idx, range.base); + + if (synced) { + tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env), + range.base, + range.length, + idxmap, + bits); + } else { + tlb_flush_range_by_mmuidx(env_cpu(env), range.base, + range.length, idxmap, bits); + } +} + +static void tlbi_aa64_rvae1_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL1&0. + * Currently handles all of RVAE1, RVAAE1, RVAALE1 and RVALE1, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + + do_rvae_write(env, value, vae1_tlbmask(env), + tlb_force_broadcast(env)); +} + +static void tlbi_aa64_rvae1is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, Inner/Outer Shareable EL1&0. + * Currently handles all of RVAE1IS, RVAE1OS, RVAAE1IS, RVAAE1OS, + * RVAALE1IS, RVAALE1OS, RVALE1IS and RVALE1OS, since we don't support + * flush-for-specific-ASID-only, flush-last-level-only or inner/outer + * shareable specific flushes. + */ + + do_rvae_write(env, value, vae1_tlbmask(env), true); +} + +static void tlbi_aa64_rvae2_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL2. + * Currently handles all of RVAE2 and RVALE2, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + + do_rvae_write(env, value, vae2_tlbmask(env), + tlb_force_broadcast(env)); + + +} + +static void tlbi_aa64_rvae2is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, Inner/Outer Shareable, EL2. + * Currently handles all of RVAE2IS, RVAE2OS, RVALE2IS and RVALE2OS, + * since we don't support flush-for-specific-ASID-only, + * flush-last-level-only or inner/outer shareable specific flushes. + */ + + do_rvae_write(env, value, vae2_tlbmask(env), true); + +} + +static void tlbi_aa64_rvae3_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL3. + * Currently handles all of RVAE3 and RVALE3, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + + do_rvae_write(env, value, ARMMMUIdxBit_E3, tlb_force_broadcast(env)); +} + +static void tlbi_aa64_rvae3is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL3, Inner/Outer Shareable. + * Currently handles all of RVAE3IS, RVAE3OS, RVALE3IS and RVALE3OS, + * since we don't support flush-for-specific-ASID-only, + * flush-last-level-only or inner/outer specific flushes. + */ + + do_rvae_write(env, value, ARMMMUIdxBit_E3, true); +} + +static void tlbi_aa64_ripas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + do_rvae_write(env, value, ipas2e1_tlbmask(env, value), + tlb_force_broadcast(env)); +} + +static void tlbi_aa64_ripas2e1is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + do_rvae_write(env, value, ipas2e1_tlbmask(env, value), true); +} + +static const ARMCPRegInfo tlbirange_reginfo[] = { + { .name = "TLBI_RVAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1, + .writefn = tlbi_aa64_rvae1_write }, + { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1, + .writefn = tlbi_aa64_rvae1_write }, + { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1, + .writefn = tlbi_aa64_rvae1_write }, + { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1, + .writefn = tlbi_aa64_rvae1_write }, + { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1is_write }, + { .name = "TLBI_RIPAS2LE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1is_write }, + { .name = "TLBI_RVAE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RVALE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RIPAS2E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 2, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1_write }, + { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1_write }, + { .name = "TLBI_RVAE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RVALE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RVAE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2_write }, + { .name = "TLBI_RVALE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2_write }, + { .name = "TLBI_RVAE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVALE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVAE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVALE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVAE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_rvae3_write }, + { .name = "TLBI_RVALE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_rvae3_write }, +}; +#endif + void define_tlb_insn_regs(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -591,4 +915,9 @@ void define_tlb_insn_regs(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_EL3)) { define_arm_cp_regs(cpu, tlbi_el3_cp_reginfo); } +#ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_tlbirange, cpu)) { + define_arm_cp_regs(cpu, tlbirange_reginfo); + } +#endif } From b0f7cd357218a0697b03e009e3cbc74d22a8133b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:49 +0000 Subject: [PATCH 0292/2892] target/arm: Move the TLBI OS insns to tlb-insns.c. Move the TLBI OS insns across to tlb-insns.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-8-peter.maydell@linaro.org --- target/arm/helper.c | 80 -------------------------------------- target/arm/tcg/tlb-insns.c | 80 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 376aa9aecd..3f7d56e809 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7116,83 +7116,6 @@ static const ARMCPRegInfo pauth_reginfo[] = { .fieldoffset = offsetof(CPUARMState, keys.apib.hi) }, }; -static const ARMCPRegInfo tlbios_reginfo[] = { - { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVMALLE1OS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, - .fgt = FGT_TLBIVAE1OS, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIASIDE1OS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAAE1OS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVALE1OS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAALE1OS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_ALLE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_alle2is_write }, - { .name = "TLBI_VAE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, - { .name = "TLBI_ALLE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_VALE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, - { .name = "TLBI_VMALLS12E1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_IPAS2E1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_RIPAS2E1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 3, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_IPAS2LE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_RIPAS2LE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 7, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_ALLE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle3is_write }, - { .name = "TLBI_VAE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, - { .name = "TLBI_VALE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, -}; - static uint64_t rndr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) { Error *err = NULL; @@ -9066,9 +8989,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_rndr, cpu)) { define_arm_cp_regs(cpu, rndr_reginfo); } - if (cpu_isar_feature(aa64_tlbios, cpu)) { - define_arm_cp_regs(cpu, tlbios_reginfo); - } /* Data Cache clean instructions up to PoP */ if (cpu_isar_feature(aa64_dcpop, cpu)) { define_one_arm_cp_reg(cpu, dcpop_reg); diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index a273c6f4b5..45ebfc512f 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -884,6 +884,83 @@ static const ARMCPRegInfo tlbirange_reginfo[] = { .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_rvae3_write }, }; + +static const ARMCPRegInfo tlbios_reginfo[] = { + { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1OS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, + .fgt = FGT_TLBIVAE1OS, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1OS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1OS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1OS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1OS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_ALLE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_alle2is_write }, + { .name = "TLBI_VAE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_ALLE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_VALE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_VMALLS12E1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_IPAS2E1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NOP }, + { .name = "TLBI_RIPAS2E1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 3, + .access = PL2_W, .type = ARM_CP_NOP }, + { .name = "TLBI_IPAS2LE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NOP }, + { .name = "TLBI_RIPAS2LE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 7, + .access = PL2_W, .type = ARM_CP_NOP }, + { .name = "TLBI_ALLE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_alle3is_write }, + { .name = "TLBI_VAE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_VALE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_vae3is_write }, +}; #endif void define_tlb_insn_regs(ARMCPU *cpu) @@ -919,5 +996,8 @@ void define_tlb_insn_regs(ARMCPU *cpu) if (cpu_isar_feature(aa64_tlbirange, cpu)) { define_arm_cp_regs(cpu, tlbirange_reginfo); } + if (cpu_isar_feature(aa64_tlbios, cpu)) { + define_arm_cp_regs(cpu, tlbios_reginfo); + } #endif } From 27fb860fd4f1524aac245a3e9849a06dae44bdba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:50 +0000 Subject: [PATCH 0293/2892] target/arm: Move small helper functions to tlb-insns.c The remaining functions that we temporarily made global are now used only from callsits in tlb-insns.c; move them across and make them file-local again. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-9-peter.maydell@linaro.org --- target/arm/cpregs.h | 34 ------ target/arm/helper.c | 220 ------------------------------------- target/arm/tcg/tlb-insns.c | 220 +++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 254 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index fe838bcfd9..cc7c54378f 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1134,38 +1134,4 @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) return ri->opc1 == 4 || ri->opc1 == 5; } -/* - * Temporary declarations of functions until the move to tlb_insn_helper.c - * is complete and we can make the functions static again - */ -CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread); -CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread); -CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread); -bool tlb_force_broadcast(CPUARMState *env); -int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, - uint64_t addr); -int vae1_tlbbits(CPUARMState *env, uint64_t addr); -int vae2_tlbbits(CPUARMState *env, uint64_t addr); -int vae1_tlbmask(CPUARMState *env); -int vae2_tlbmask(CPUARMState *env); -int ipas2e1_tlbmask(CPUARMState *env, int64_t value); -int e2_tlbmask(CPUARMState *env); -void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); - #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 3f7d56e809..cd9f865031 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -365,40 +365,6 @@ static CPAccessResult access_tacr(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -/* Check for traps from EL1 due to HCR_EL2.TTLB. */ -CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { - return CP_ACCESS_TRAP_EL2; - } - return CP_ACCESS_OK; -} - -/* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBIS. */ -CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 1 && - (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBIS))) { - return CP_ACCESS_TRAP_EL2; - } - return CP_ACCESS_OK; -} - -#ifdef TARGET_AARCH64 -/* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBOS. */ -CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 1 && - (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBOS))) { - return CP_ACCESS_TRAP_EL2; - } - return CP_ACCESS_OK; -} -#endif - static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); @@ -455,16 +421,6 @@ int alle1_tlbmask(CPUARMState *env) ARMMMUIdxBit_Stage2_S); } -/* - * Non-IS variants of TLB operations are upgraded to - * IS versions if we are at EL1 and HCR_EL2.FB is effectively set to - * force broadcast of these operations. - */ -bool tlb_force_broadcast(CPUARMState *env) -{ - return arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_FB); -} - static const ARMCPRegInfo cp_reginfo[] = { /* * Define the secure and non-secure FCSE identifier CP registers @@ -4680,182 +4636,6 @@ static CPAccessResult access_tocu(CPUARMState *env, const ARMCPRegInfo *ri, return do_cacheop_pou_access(env, HCR_TOCU | HCR_TPU); } -/* - * See: D4.7.2 TLB maintenance requirements and the TLB maintenance instructions - * Page D4-1736 (DDI0487A.b) - */ - -int vae1_tlbmask(CPUARMState *env) -{ - uint64_t hcr = arm_hcr_el2_eff(env); - uint16_t mask; - - assert(arm_feature(env, ARM_FEATURE_AARCH64)); - - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - mask = ARMMMUIdxBit_E20_2 | - ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E20_0; - } else { - /* This is AArch64 only, so we don't need to touch the EL30_x TLBs */ - mask = ARMMMUIdxBit_E10_1 | - ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0; - } - return mask; -} - -int vae2_tlbmask(CPUARMState *env) -{ - uint64_t hcr = arm_hcr_el2_eff(env); - uint16_t mask; - - if (hcr & HCR_E2H) { - mask = ARMMMUIdxBit_E20_2 | - ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E20_0; - } else { - mask = ARMMMUIdxBit_E2; - } - return mask; -} - -/* Return 56 if TBI is enabled, 64 otherwise. */ -int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, - uint64_t addr) -{ - uint64_t tcr = regime_tcr(env, mmu_idx); - int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); - int select = extract64(addr, 55, 1); - - return (tbi >> select) & 1 ? 56 : 64; -} - -int vae1_tlbbits(CPUARMState *env, uint64_t addr) -{ - uint64_t hcr = arm_hcr_el2_eff(env); - ARMMMUIdx mmu_idx; - - assert(arm_feature(env, ARM_FEATURE_AARCH64)); - - /* Only the regime of the mmu_idx below is significant. */ - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - mmu_idx = ARMMMUIdx_E20_0; - } else { - mmu_idx = ARMMMUIdx_E10_0; - } - - return tlbbits_for_regime(env, mmu_idx, addr); -} - -int vae2_tlbbits(CPUARMState *env, uint64_t addr) -{ - uint64_t hcr = arm_hcr_el2_eff(env); - ARMMMUIdx mmu_idx; - - /* - * Only the regime of the mmu_idx below is significant. - * Regime EL2&0 has two ranges with separate TBI configuration, while EL2 - * only has one. - */ - if (hcr & HCR_E2H) { - mmu_idx = ARMMMUIdx_E20_2; - } else { - mmu_idx = ARMMMUIdx_E2; - } - - return tlbbits_for_regime(env, mmu_idx, addr); -} - -void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); -} - -int e2_tlbmask(CPUARMState *env) -{ - return (ARMMMUIdxBit_E20_0 | - ARMMMUIdxBit_E20_2 | - ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E2); -} - -void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = alle1_tlbmask(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); -} - -void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = e2_tlbmask(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); -} - -void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); -} - -void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae1_tlbbits(env, pageaddr); - - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); -} - -void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = vae2_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae2_tlbbits(env, pageaddr); - - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); -} - -void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = tlbbits_for_regime(env, ARMMMUIdx_E3, pageaddr); - - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E3, bits); -} - -int ipas2e1_tlbmask(CPUARMState *env, int64_t value) -{ - /* - * The MSB of value is the NS field, which only applies if SEL2 - * is implemented and SCR_EL3.NS is not set (i.e. in secure mode). - */ - return (value >= 0 - && cpu_isar_feature(aa64_sel2, env_archcpu(env)) - && arm_is_secure_below_el3(env) - ? ARMMMUIdxBit_Stage2_S - : ARMMMUIdxBit_Stage2); -} - static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 45ebfc512f..51b4756e31 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -13,6 +13,40 @@ #include "cpu-features.h" #include "cpregs.h" +/* Check for traps from EL1 due to HCR_EL2.TTLB. */ +static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +/* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBIS. */ +static CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && + (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBIS))) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +#ifdef TARGET_AARCH64 +/* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBOS. */ +static CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && + (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBOS))) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} +#endif + /* IS variants of TLB operations must affect all cores */ static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -46,6 +80,16 @@ static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); } +/* + * Non-IS variants of TLB operations are upgraded to + * IS versions if we are at EL1 and HCR_EL2.FB is effectively set to + * force broadcast of these operations. + */ +static bool tlb_force_broadcast(CPUARMState *env) +{ + return arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_FB); +} + static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -170,6 +214,102 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); } +/* + * See: D4.7.2 TLB maintenance requirements and the TLB maintenance instructions + * Page D4-1736 (DDI0487A.b) + */ + +static int vae1_tlbmask(CPUARMState *env) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + uint16_t mask; + + assert(arm_feature(env, ARM_FEATURE_AARCH64)); + + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + mask = ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E20_0; + } else { + /* This is AArch64 only, so we don't need to touch the EL30_x TLBs */ + mask = ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E10_0; + } + return mask; +} + +static int vae2_tlbmask(CPUARMState *env) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + uint16_t mask; + + if (hcr & HCR_E2H) { + mask = ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E20_0; + } else { + mask = ARMMMUIdxBit_E2; + } + return mask; +} + +/* Return 56 if TBI is enabled, 64 otherwise. */ +static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, + uint64_t addr) +{ + uint64_t tcr = regime_tcr(env, mmu_idx); + int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); + int select = extract64(addr, 55, 1); + + return (tbi >> select) & 1 ? 56 : 64; +} + +static int vae1_tlbbits(CPUARMState *env, uint64_t addr) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + ARMMMUIdx mmu_idx; + + assert(arm_feature(env, ARM_FEATURE_AARCH64)); + + /* Only the regime of the mmu_idx below is significant. */ + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + mmu_idx = ARMMMUIdx_E20_0; + } else { + mmu_idx = ARMMMUIdx_E10_0; + } + + return tlbbits_for_regime(env, mmu_idx, addr); +} + +static int vae2_tlbbits(CPUARMState *env, uint64_t addr) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + ARMMMUIdx mmu_idx; + + /* + * Only the regime of the mmu_idx below is significant. + * Regime EL2&0 has two ranges with separate TBI configuration, while EL2 + * only has one. + */ + if (hcr & HCR_E2H) { + mmu_idx = ARMMMUIdx_E20_2; + } else { + mmu_idx = ARMMMUIdx_E2; + } + + return tlbbits_for_regime(env, mmu_idx, addr); +} + +static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); +} + static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -183,6 +323,14 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static int e2_tlbmask(CPUARMState *env) +{ + return (ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E2); +} + static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -210,6 +358,32 @@ static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); } +static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = alle1_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); +} + +static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); +} + +static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); +} + static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -241,6 +415,17 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); } +static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae1_tlbbits(env, pageaddr); + + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); +} + static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -262,6 +447,41 @@ static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = vae2_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae2_tlbbits(env, pageaddr); + + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); +} + +static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = tlbbits_for_regime(env, ARMMMUIdx_E3, pageaddr); + + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, + ARMMMUIdxBit_E3, bits); +} + +static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) +{ + /* + * The MSB of value is the NS field, which only applies if SEL2 + * is implemented and SCR_EL3.NS is not set (i.e. in secure mode). + */ + return (value >= 0 + && cpu_isar_feature(aa64_sel2, env_archcpu(env)) + && arm_is_secure_below_el3(env) + ? ARMMMUIdxBit_Stage2_S + : ARMMMUIdxBit_Stage2); +} + static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { From 0b7aefb9ebf5c0165aea5c8b4dde18508d015eff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:51 +0000 Subject: [PATCH 0294/2892] target/arm: Move RME TLB insns to tlb-insns.c Move the FEAT_RME specific TLB insns across to tlb-insns.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-10-peter.maydell@linaro.org --- target/arm/helper.c | 38 -------------------------------- target/arm/tcg/tlb-insns.c | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index cd9f865031..910ae62c47 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6525,14 +6525,6 @@ static const ARMCPRegInfo sme_reginfo[] = { .type = ARM_CP_CONST, .resetvalue = 0 }, }; -static void tlbi_aa64_paall_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush(cs); -} - static void gpccr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -6550,14 +6542,6 @@ static void gpccr_reset(CPUARMState *env, const ARMCPRegInfo *ri) env_archcpu(env)->reset_l0gptsz); } -static void tlbi_aa64_paallos_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_all_cpus_synced(cs); -} - static const ARMCPRegInfo rme_reginfo[] = { { .name = "GPCCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 6, @@ -6569,28 +6553,6 @@ static const ARMCPRegInfo rme_reginfo[] = { { .name = "MFAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 6, .crm = 0, .opc2 = 5, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mfar_el3) }, - { .name = "TLBI_PAALL", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 4, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_paall_write }, - { .name = "TLBI_PAALLOS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 4, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_paallos_write }, - /* - * QEMU does not have a way to invalidate by physical address, thus - * invalidating a range of physical addresses is accomplished by - * flushing all tlb entries in the outer shareable domain, - * just like PAALLOS. - */ - { .name = "TLBI_RPALOS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 7, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_paallos_write }, - { .name = "TLBI_RPAOS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 3, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_paallos_write }, { .name = "DC_CIPAPA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 1, .access = PL3_W, .type = ARM_CP_NOP }, diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 51b4756e31..d20d32624d 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -1181,6 +1181,48 @@ static const ARMCPRegInfo tlbios_reginfo[] = { .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae3is_write }, }; + +static void tlbi_aa64_paall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush(cs); +} + +static void tlbi_aa64_paallos_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_all_cpus_synced(cs); +} + +static const ARMCPRegInfo tlbi_rme_reginfo[] = { + { .name = "TLBI_PAALL", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 4, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paall_write }, + { .name = "TLBI_PAALLOS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 4, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paallos_write }, + /* + * QEMU does not have a way to invalidate by physical address, thus + * invalidating a range of physical addresses is accomplished by + * flushing all tlb entries in the outer shareable domain, + * just like PAALLOS. + */ + { .name = "TLBI_RPALOS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 7, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paallos_write }, + { .name = "TLBI_RPAOS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 3, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paallos_write }, +}; + #endif void define_tlb_insn_regs(ARMCPU *cpu) @@ -1219,5 +1261,8 @@ void define_tlb_insn_regs(ARMCPU *cpu) if (cpu_isar_feature(aa64_tlbios, cpu)) { define_arm_cp_regs(cpu, tlbios_reginfo); } + if (cpu_isar_feature(aa64_rme, cpu)) { + define_arm_cp_regs(cpu, tlbi_rme_reginfo); + } #endif } From 48e652c4bd9570f6f24def25355cb3009a7300f8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Dec 2024 16:04:52 +0000 Subject: [PATCH 0295/2892] target/arm: Simplify condition for tlbi_el2_cp_reginfo[] We currently register the tlbi_el2_cp_reginfo[] TLBI insns if EL2 is implemented, or if EL3 and v8 is implemented. This is a copy of the logic used for el2_cp_reginfo[], but for the specific case of the TLBI insns we can simplify it. This is because we do not need the "if EL2 does not exist but EL3 does then EL2 registers should exist and be RAZ/WI" handling here: all our cpregs are for instructions, which UNDEF when EL3 exists and EL2 does not. Simplify the condition down to just "if EL2 exists". This is not a behaviour change because: * for AArch64 insns we marked them with ARM_CP_EL3_NO_EL2_UNDEF, which meant that define_arm_cp_regs() would ignore them if EL2 wasn't present * for AArch32 insns, the .access = PL2_W meant that if EL2 was not present the only way to get at them was from AArch32 EL3; but we have no CPUs which have ARM_FEATURE_V8 but start in AArch32 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241210160452.2427965-11-peter.maydell@linaro.org --- target/arm/tcg/tlb-insns.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index d20d32624d..0f67294edc 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -1246,9 +1246,7 @@ void define_tlb_insn_regs(ARMCPU *cpu) * ops (i.e. matching the condition for el2_cp_reginfo[] in * helper.c), but we will be able to simplify this later. */ - if (arm_feature(env, ARM_FEATURE_EL2) - || (arm_feature(env, ARM_FEATURE_EL3) - && arm_feature(env, ARM_FEATURE_V8))) { + if (arm_feature(env, ARM_FEATURE_EL2)) { define_arm_cp_regs(cpu, tlbi_el2_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL3)) { From 4016adc74aee696bca025e546edfd2ae8f57863b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Dec 2024 14:22:02 +0100 Subject: [PATCH 0296/2892] hw/nvram/fw_cfg: Skip FW_CFG_DATA_GENERATOR when no data to generate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the FW_CFG_DATA_GENERATOR interface get_data() handler to return NULL when there is nothing to generate. In that case fw_cfg_add_file_from_generator() will not add any item and return %true. Reported-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241213133352.10915-4-philmd@linaro.org> --- hw/nvram/fw_cfg.c | 3 ++- include/hw/nvram/fw_cfg.h | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index b94cd27bd8..46c62c8f09 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1031,6 +1031,7 @@ bool fw_cfg_add_file_from_generator(FWCfgState *s, Object *parent, const char *part, const char *filename, Error **errp) { + ERRP_GUARD(); FWCfgDataGeneratorClass *klass; GByteArray *array; Object *obj; @@ -1048,7 +1049,7 @@ bool fw_cfg_add_file_from_generator(FWCfgState *s, } klass = FW_CFG_DATA_GENERATOR_GET_CLASS(obj); array = klass->get_data(obj, errp); - if (!array) { + if (*errp || !array) { return false; } size = array->len; diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index fcb06f18cc..6089681f42 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -30,8 +30,9 @@ struct FWCfgDataGeneratorClass { * @obj: the object implementing this interface * @errp: pointer to a NULL-initialized error object * - * Returns: reference to a byte array containing the data on success, - * or NULL on error. + * Returns: A byte array containing data to add, or NULL without + * @errp set if no data is required, or NULL with @errp + * set on failure. * * The caller should release the reference when no longer * required. @@ -298,14 +299,16 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, * @parent: the object in which to resolve the @part * @errp: pointer to a NULL initialized error object * - * Add a new NAMED fw_cfg item with the content generated from the - * @part object. The data generated by the @part object is copied - * into the data structure of the fw_cfg device. + * If the @part object generates content, add a new NAMED fw_cfg item with it. + * The data generated by the @part object is copied into the data structure of + * the fw_cfg device. * The next available (unused) selector key starting at FW_CFG_FILE_FIRST * will be used; also, a new entry will be added to the file directory * structure residing at key value FW_CFG_FILE_DIR, containing the item name, * data size, and assigned selector key value. * + * If the @part object does not generate content, no fw_cfg item is added. + * * Returns: %true on success, %false on error. */ bool fw_cfg_add_file_from_generator(FWCfgState *s, From 59c5eea5c794ff3867504d2fba63bb535802027b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 17:41:40 +0100 Subject: [PATCH 0297/2892] hw/pci: Have PCI_BUS implement TYPE_FW_CFG_DATA_GENERATOR_INTERFACE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FW_CFG_DATA_GENERATOR interface allows any object to produce a blob of data consumable by the fw_cfg device. Implement that for PCI bus objects. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241213133352.10915-5-philmd@linaro.org> --- hw/pci/pci.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1416ae202c..8844251ece 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -46,6 +46,7 @@ #include "hw/pci/msix.h" #include "hw/hotplug.h" #include "hw/boards.h" +#include "hw/nvram/fw_cfg.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "pci-internal.h" @@ -216,11 +217,41 @@ static uint16_t pcibus_numa_node(PCIBus *bus) return NUMA_NODE_UNASSIGNED; } +static GByteArray *pci_bus_fw_cfg_gen_data(Object *obj, Error **errp) +{ + PCIBus *bus = PCI_BUS(obj); + GByteArray *byte_array; + uint64_t extra_hosts = 0; + + if (!bus) { + return NULL; + } + + QLIST_FOREACH(bus, &bus->child, sibling) { + /* look for expander root buses */ + if (pci_bus_is_root(bus)) { + extra_hosts++; + } + } + + if (!extra_hosts) { + return NULL; + } + extra_hosts = cpu_to_le64(extra_hosts); + + byte_array = g_byte_array_new(); + g_byte_array_append(byte_array, + (const void *)&extra_hosts, sizeof(extra_hosts)); + + return byte_array; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); PCIBusClass *pbc = PCI_BUS_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); + FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_CLASS(klass); k->print_dev = pcibus_dev_print; k->get_dev_path = pcibus_get_dev_path; @@ -232,6 +263,8 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) pbc->bus_num = pcibus_num; pbc->numa_node = pcibus_numa_node; + + fwgc->get_data = pci_bus_fw_cfg_gen_data; } static const TypeInfo pci_bus_info = { @@ -240,6 +273,10 @@ static const TypeInfo pci_bus_info = { .instance_size = sizeof(PCIBus), .class_size = sizeof(PCIBusClass), .class_init = pci_bus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_CFG_DATA_GENERATOR_INTERFACE }, + { } + } }; static const TypeInfo cxl_interface_info = { From 14f1f86d5111ed2dae19ae15da81a98ea048017d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 17:50:47 +0100 Subject: [PATCH 0298/2892] hw/pci: Add pci_bus_add_fw_cfg_extra_pci_roots() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_bus_add_fw_cfg_extra_pci_roots() calls the fw_cfg API with PCI bus specific arguments. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241206181352.6836-5-philmd@linaro.org> --- hw/pci/pci.c | 16 ++++++++++++++++ include/hw/pci/pci.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 8844251ece..bf0a1840db 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -217,6 +217,22 @@ static uint16_t pcibus_numa_node(PCIBus *bus) return NUMA_NODE_UNASSIGNED; } +bool pci_bus_add_fw_cfg_extra_pci_roots(FWCfgState *fw_cfg, + PCIBus *bus, + Error **errp) +{ + Object *obj; + + if (!bus) { + return true; + } + obj = OBJECT(bus); + + return fw_cfg_add_file_from_generator(fw_cfg, obj->parent, + object_get_canonical_path_component(obj), + "etc/extra-pci-roots", errp); +} + static GByteArray *pci_bus_fw_cfg_gen_data(Object *obj, Error **errp) { PCIBus *bus = PCI_BUS(obj); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index c0717e3121..603c456c3a 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -297,6 +297,9 @@ int pci_bus_get_irq_level(PCIBus *bus, int irq_num); uint32_t pci_bus_get_slot_reserved_mask(PCIBus *bus); void pci_bus_set_slot_reserved_mask(PCIBus *bus, uint32_t mask); void pci_bus_clear_slot_reserved_mask(PCIBus *bus, uint32_t mask); +bool pci_bus_add_fw_cfg_extra_pci_roots(FWCfgState *fw_cfg, + PCIBus *bus, + Error **errp); /* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ static inline int pci_swizzle(int slot, int pin) { From e5fd678a0d2489baa9af0c91408b93474836afcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 17:14:19 +0100 Subject: [PATCH 0299/2892] hw: Use pci_bus_add_fw_cfg_extra_pci_roots() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to remove fw_cfg_add_extra_pci_roots() which introduced PCI bus knowledge within the generic hw/nvram/fw_cfg.c file. Replace the calls by the pci_bus_add_fw_cfg_extra_pci_roots() which is a 1:1 equivalent, but using correct API. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241206181352.6836-6-philmd@linaro.org> --- hw/arm/virt.c | 3 ++- hw/hppa/machine.c | 2 +- hw/i386/pc.c | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3bd9dd0f86..333eaf67ea 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1750,7 +1750,8 @@ void virt_machine_done(Notifier *notifier, void *data) exit(1); } - fw_cfg_add_extra_pci_roots(vms->bus, vms->fw_cfg); + pci_bus_add_fw_cfg_extra_pci_roots(vms->fw_cfg, vms->bus, + &error_abort); virt_acpi_setup(vms); virt_build_smbios(vms); diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index a31dc32a9f..4e67335322 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -240,7 +240,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, g_memdup2(qemu_version, sizeof(qemu_version)), sizeof(qemu_version)); - fw_cfg_add_extra_pci_roots(pci_bus, fw_cfg); + pci_bus_add_fw_cfg_extra_pci_roots(fw_cfg, pci_bus, &error_abort); return fw_cfg; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 99b9b105e2..92047ce8c9 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -631,7 +631,8 @@ void pc_machine_done(Notifier *notifier, void *data) /* set the number of CPUs */ x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - fw_cfg_add_extra_pci_roots(pcms->pcibus, x86ms->fw_cfg); + pci_bus_add_fw_cfg_extra_pci_roots(x86ms->fw_cfg, pcms->pcibus, + &error_abort); acpi_setup(); if (x86ms->fw_cfg) { From 3a25075ecef85adca5ab5b4e2486955b9d7a23ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 18:10:20 +0100 Subject: [PATCH 0300/2892] hw/nvram/fw_cfg: Remove fw_cfg_add_extra_pci_roots() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all uses of fw_cfg_add_extra_pci_roots() have been converted to the newer pci_bus_add_fw_cfg_extra_pci_roots(), we can remove that bogus method. hw/nvram/fw_cfg must stay generic. Device specific entries have to be implemented using TYPE_FW_CFG_DATA_GENERATOR_INTERFACE. This mostly reverts commit 0abd38885ac0fcdb08653922f339849cad387961 ("fw_cfg: Refactor extra pci roots addition"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241206181352.6836-7-philmd@linaro.org> --- hw/nvram/fw_cfg.c | 23 ----------------------- include/hw/nvram/fw_cfg.h | 9 --------- 2 files changed, 32 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 46c62c8f09..97def3a88b 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -41,7 +41,6 @@ #include "qemu/cutils.h" #include "qapi/error.h" #include "hw/acpi/aml-build.h" -#include "hw/pci/pci_bus.h" #include "hw/loader.h" #define FW_CFG_FILE_SLOTS_DFLT 0x20 @@ -1058,28 +1057,6 @@ bool fw_cfg_add_file_from_generator(FWCfgState *s, return true; } -void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s) -{ - int extra_hosts = 0; - - if (!bus) { - return; - } - - QLIST_FOREACH(bus, &bus->child, sibling) { - /* look for expander root buses */ - if (pci_bus_is_root(bus)) { - extra_hosts++; - } - } - - if (extra_hosts && s) { - uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(extra_hosts); - fw_cfg_add_file(s, "etc/extra-pci-roots", val, sizeof(*val)); - } -} - static void fw_cfg_machine_reset(void *opaque) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 6089681f42..c60361dc9e 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -315,15 +315,6 @@ bool fw_cfg_add_file_from_generator(FWCfgState *s, Object *parent, const char *part, const char *filename, Error **errp); -/** - * fw_cfg_add_extra_pci_roots: - * @bus: main pci root bus to be scanned from - * @s: fw_cfg device being modified - * - * Add a new fw_cfg item... - */ -void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s); - FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, AddressSpace *dma_as); FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); From b7bd67fb31d0c97f7b9a6f83d6c786cbee5c09b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Dec 2024 10:06:35 +0000 Subject: [PATCH 0301/2892] hw/net/can: clean-up unnecessary includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The event_notifier, thread and socket includes look like copy and paste of standard headers. None of the canbus devices use chardev although some relied on chardev to bring in bitops and byte swapping headers. In this case include them directly. Signed-off-by: Alex Bennée Acked-by: Pavel Pisa Reviewed-by: Pierrick Bouvier Message-ID: <20241209100635.93243-1-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/can/can_kvaser_pci.c | 4 ---- hw/net/can/can_mioe3680_pci.c | 4 ---- hw/net/can/can_pcm3680_pci.c | 4 ---- hw/net/can/can_sja1000.c | 2 +- hw/net/can/ctucan_core.c | 3 ++- hw/net/can/ctucan_pci.c | 4 ---- 6 files changed, 3 insertions(+), 18 deletions(-) diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index 38434d3a04..9e363d532f 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -30,12 +30,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index 21659b7afb..580f099e00 100644 --- a/hw/net/can/can_mioe3680_pci.c +++ b/hw/net/can/can_mioe3680_pci.c @@ -26,12 +26,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index af21dc6855..3195b79954 100644 --- a/hw/net/can/can_pcm3680_pci.c +++ b/hw/net/can/can_pcm3680_pci.c @@ -26,12 +26,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 6694d7bfd8..5b6ba9df6c 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "chardev/char.h" +#include "qemu/bitops.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "net/can_emu.h" diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c index 812b83e93e..4402d4cb1f 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -28,7 +28,8 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "chardev/char.h" +#include "qemu/bswap.h" +#include "qemu/bitops.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "net/can_emu.h" diff --git a/hw/net/can/ctucan_pci.c b/hw/net/can/ctucan_pci.c index 65f1f82303..a8c77b9194 100644 --- a/hw/net/can/ctucan_pci.c +++ b/hw/net/can/ctucan_pci.c @@ -27,12 +27,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" From bd8760dcfddc8337ba1ad57c72a04e19e803d03b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 10 Nov 2024 13:39:59 +1000 Subject: [PATCH 0302/2892] hw/usb/msd: Add status to usb_msd_packet_complete() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a convenience change that accepts a status when completing a packet. Signed-off-by: Nicholas Piggin Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241110034000.379463-2-npiggin@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/dev-storage.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 341e505bd0..4f1e8b7f6c 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -177,7 +177,7 @@ static const USBDesc desc = { .str = desc_strings, }; -static void usb_msd_packet_complete(MSDState *s) +static void usb_msd_packet_complete(MSDState *s, int status) { USBPacket *p = s->packet; @@ -187,6 +187,7 @@ static void usb_msd_packet_complete(MSDState *s) * usb_packet_complete returns. */ trace_usb_msd_packet_complete(); + p->status = status; s->packet = NULL; usb_packet_complete(&s->dev, p); } @@ -196,8 +197,7 @@ static void usb_msd_fatal_error(MSDState *s) trace_usb_msd_fatal_error(); if (s->packet) { - s->packet->status = USB_RET_STALL; - usb_msd_packet_complete(s); + usb_msd_packet_complete(s, USB_RET_STALL); } /* @@ -255,8 +255,8 @@ void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) usb_msd_copy_data(s, p); p = s->packet; if (p && p->actual_length == p->iov.size) { - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); + /* USB_RET_SUCCESS status clears previous ASYNC status */ + usb_msd_packet_complete(s, USB_RET_SUCCESS); } } } @@ -295,8 +295,8 @@ void usb_msd_command_complete(SCSIRequest *req, size_t resid) s->mode = USB_MSDM_CSW; } } - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); + /* USB_RET_SUCCESS status clears previous ASYNC status */ + usb_msd_packet_complete(s, USB_RET_SUCCESS); } else if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } @@ -332,8 +332,7 @@ void usb_msd_handle_reset(USBDevice *dev) assert(s->req == NULL); if (s->packet) { - s->packet->status = USB_RET_STALL; - usb_msd_packet_complete(s); + usb_msd_packet_complete(s, USB_RET_STALL); } memset(&s->csw, 0, sizeof(s->csw)); From 2a8c16e423818f35058eba65255af011e7b96031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Nov 2024 10:51:34 +0100 Subject: [PATCH 0303/2892] hw/usb/hcd-xhci-nec: Remove unused XHCINecState::flags field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b9599519a01 ("hw/usb/hcd-xhci: Remove XHCI_FLAG_SS_FIRST flag") remove the last use of XHCINecState::flags but neglected to remove it; do that now. Reported-by: Nicholas Piggin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20241127122812.89487-1-philmd@linaro.org> --- hw/usb/hcd-xhci-nec.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 0c063b3697..1a191fc737 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -30,10 +30,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(XHCINecState, NEC_XHCI) struct XHCINecState { - /*< private >*/ XHCIPciState parent_obj; - /*< public >*/ - uint32_t flags; + uint32_t intrs; uint32_t slots; }; @@ -51,7 +49,6 @@ static void nec_xhci_instance_init(Object *obj) XHCIPciState *pci = XHCI_PCI(obj); XHCINecState *nec = NEC_XHCI(obj); - pci->xhci.flags = nec->flags; pci->xhci.numintrs = nec->intrs; pci->xhci.numslots = nec->slots; } From c0179ead952b80ccf0d9d741fc4f66b57ab1250d Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Sun, 8 Dec 2024 20:16:45 +0100 Subject: [PATCH 0304/2892] hw/usb/hcd-xhci-pci: Indentation fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes number of spaces used for indentation on one line. Signed-off-by: Phil Dennis-Jordan Message-ID: <20241208191646.64857-6-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index a039f5778a..e110840c7a 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -94,7 +94,7 @@ static int xhci_pci_vmstate_post_load(void *opaque, int version_id) PCIDevice *pci_dev = PCI_DEVICE(s); int intr; - for (intr = 0; intr < s->xhci.numintrs; intr++) { + for (intr = 0; intr < s->xhci.numintrs; intr++) { if (s->xhci.intr[intr].msix_used) { msix_vector_use(pci_dev, intr); } else { From 8a4989f526cae238d1cf044c62c847ed708c92c6 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Thu, 5 Dec 2024 12:44:53 +0100 Subject: [PATCH 0305/2892] hw/ide/ahci: Decouple from PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some adhoc profiling booting Linux VMs, it's observed that ahci_irq_lower() can be a hot path (10000+ triggers until login prompt appears). Even though the parent device never changes, this method re-determines whether the parent device is a PCI device or not using the rather expensive object_dynamic_cast() function. Avoid this overhead by pushing the interrupt handling to the parent device, essentially turning AHCIState into an "IP block". Note that this change also frees AHCIState from the PCI dependency which wasn't reflected in Kconfig. Reported-by: Peter Xu Inspired-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241212110926.23548-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/ahci-internal.h | 1 - hw/ide/ahci.c | 39 ++++----------------------------------- hw/ide/ich.c | 19 +++++++++++++++---- include/hw/ide/ahci-pci.h | 2 ++ include/hw/ide/ahci.h | 2 -- 5 files changed, 21 insertions(+), 42 deletions(-) diff --git a/hw/ide/ahci-internal.h b/hw/ide/ahci-internal.h index 7e63ea2310..a318f36811 100644 --- a/hw/ide/ahci-internal.h +++ b/hw/ide/ahci-internal.h @@ -25,7 +25,6 @@ #define HW_IDE_AHCI_INTERNAL_H #include "hw/ide/ahci.h" -#include "hw/pci/pci_device.h" #include "ide-internal.h" #define AHCI_MEM_BAR_SIZE 0x1000 diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 0eb24304ee..5836aa924b 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -23,8 +23,6 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/pci/msi.h" -#include "hw/pci/pci.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -34,8 +32,6 @@ #include "qemu/module.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include "hw/ide/pci.h" -#include "hw/ide/ahci-pci.h" #include "hw/ide/ahci-sysbus.h" #include "ahci-internal.h" #include "ide-internal.h" @@ -179,34 +175,6 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) return val; } -static void ahci_irq_raise(AHCIState *s) -{ - DeviceState *dev_state = s->container; - PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), - TYPE_PCI_DEVICE); - - trace_ahci_irq_raise(s); - - if (pci_dev && msi_enabled(pci_dev)) { - msi_notify(pci_dev, 0); - } else { - qemu_irq_raise(s->irq); - } -} - -static void ahci_irq_lower(AHCIState *s) -{ - DeviceState *dev_state = s->container; - PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), - TYPE_PCI_DEVICE); - - trace_ahci_irq_lower(s); - - if (!pci_dev || !msi_enabled(pci_dev)) { - qemu_irq_lower(s->irq); - } -} - static void ahci_check_irq(AHCIState *s) { int i; @@ -222,9 +190,11 @@ static void ahci_check_irq(AHCIState *s) trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus); if (s->control_regs.irqstatus && (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { - ahci_irq_raise(s); + trace_ahci_irq_raise(s); + qemu_irq_raise(s->irq); } else { - ahci_irq_lower(s); + trace_ahci_irq_lower(s); + qemu_irq_lower(s->irq); } } @@ -1608,7 +1578,6 @@ static const IDEDMAOps ahci_dma_ops = { void ahci_init(AHCIState *s, DeviceState *qdev) { - s->container = qdev; /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s, "ahci", AHCI_MEM_BAR_SIZE); diff --git a/hw/ide/ich.c b/hw/ide/ich.c index b311450c12..c99a44df8e 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -61,7 +61,6 @@ */ #include "qemu/osdep.h" -#include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" @@ -91,6 +90,19 @@ static const VMStateDescription vmstate_ich9_ahci = { }, }; +static void pci_ich9_ahci_update_irq(void *opaque, int irq_num, int level) +{ + PCIDevice *pci_dev = opaque; + + if (msi_enabled(pci_dev)) { + if (level) { + msi_notify(pci_dev, 0); + } + } else { + pci_set_irq(pci_dev, level); + } +} + static void pci_ich9_reset(DeviceState *dev) { AHCIPCIState *d = ICH9_AHCI(dev); @@ -102,7 +114,9 @@ static void pci_ich9_ahci_init(Object *obj) { AHCIPCIState *d = ICH9_AHCI(obj); + qemu_init_irq(&d->irq, pci_ich9_ahci_update_irq, d, 0); ahci_init(&d->ahci, DEVICE(obj)); + d->ahci.irq = &d->irq; } static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) @@ -125,8 +139,6 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) /* XXX Software should program this register */ dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ - d->ahci.irq = pci_allocate_irq(dev); - pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, &d->ahci.idp); pci_register_bar(dev, ICH9_MEM_BAR, PCI_BASE_ADDRESS_SPACE_MEMORY, @@ -161,7 +173,6 @@ static void pci_ich9_uninit(PCIDevice *dev) msi_uninit(dev); ahci_uninit(&d->ahci); - qemu_free_irq(d->ahci.irq); } static void ich_ahci_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/ide/ahci-pci.h b/include/hw/ide/ahci-pci.h index c2ee616962..face1a9a4a 100644 --- a/include/hw/ide/ahci-pci.h +++ b/include/hw/ide/ahci-pci.h @@ -9,6 +9,7 @@ #include "qom/object.h" #include "hw/ide/ahci.h" #include "hw/pci/pci_device.h" +#include "hw/irq.h" #define TYPE_ICH9_AHCI "ich9-ahci" OBJECT_DECLARE_SIMPLE_TYPE(AHCIPCIState, ICH9_AHCI) @@ -17,6 +18,7 @@ struct AHCIPCIState { PCIDevice parent_obj; AHCIState ahci; + IRQState irq; }; #endif diff --git a/include/hw/ide/ahci.h b/include/hw/ide/ahci.h index ba31e75ff9..ac0292c634 100644 --- a/include/hw/ide/ahci.h +++ b/include/hw/ide/ahci.h @@ -37,8 +37,6 @@ typedef struct AHCIControlRegs { } AHCIControlRegs; typedef struct AHCIState { - DeviceState *container; - AHCIDevice *dev; AHCIControlRegs control_regs; MemoryRegion mem; From 1b26146e898086cd85be22170e7988017b481bb0 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Thu, 5 Dec 2024 14:19:37 +0100 Subject: [PATCH 0306/2892] hw/ide/ahci: Extract TYPE_SYSBUS_AHCI into dedicated file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement in dedicated file, just like TYPE_ICH9_AHCI. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241212110926.23548-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/Kconfig | 10 ++--- hw/ide/Kconfig | 4 ++ hw/ide/ahci-sysbus.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ hw/ide/ahci.c | 67 -------------------------------- hw/ide/meson.build | 1 + 5 files changed, 101 insertions(+), 72 deletions(-) create mode 100644 hw/ide/ahci-sysbus.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 1b25e73578..e779b5af95 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -71,7 +71,7 @@ config HIGHBANK depends on TCG && ARM select A9MPCORE select A15MPCORE - select AHCI + select AHCI_SYSBUS select ARM_TIMER # sp804 select ARM_V7M select PL011 if !HAVE_RUST # UART @@ -192,7 +192,7 @@ config SBSA_REF depends on TCG && AARCH64 imply PCI_DEVICES select DEVICE_TREE - select AHCI + select AHCI_SYSBUS select ARM_SMMUV3 select GPIO_KEY select PCI_EXPRESS @@ -319,7 +319,7 @@ config ARM_V7M config ALLWINNER_A10 bool - select AHCI + select AHCI_SYSBUS select ALLWINNER_A10_PIT select ALLWINNER_A10_PIC select ALLWINNER_A10_CCM @@ -352,7 +352,7 @@ config ALLWINNER_H3 config ALLWINNER_R40 bool default y if TCG && ARM - select AHCI + select AHCI_SYSBUS select ALLWINNER_SRAMC select ALLWINNER_A10_PIT select ALLWINNER_WDT @@ -422,7 +422,7 @@ config XLNX_ZYNQMP_ARM bool default y if PIXMAN depends on TCG && AARCH64 - select AHCI + select AHCI_SYSBUS select ARM_GIC select CADENCE select CPU_CLUSTER diff --git a/hw/ide/Kconfig b/hw/ide/Kconfig index 2e22b677da..b55507b836 100644 --- a/hw/ide/Kconfig +++ b/hw/ide/Kconfig @@ -54,6 +54,10 @@ config AHCI_ICH9 depends on PCI select AHCI +config AHCI_SYSBUS + bool + select AHCI + config IDE_SII3112 bool select IDE_PCI diff --git a/hw/ide/ahci-sysbus.c b/hw/ide/ahci-sysbus.c new file mode 100644 index 0000000000..d43db0923f --- /dev/null +++ b/hw/ide/ahci-sysbus.c @@ -0,0 +1,91 @@ +/* + * QEMU AHCI Emulation (MMIO-mapped devices) + * + * Copyright (c) 2010 qiaochong@loongson.cn + * Copyright (c) 2010 Roland Elek + * Copyright (c) 2010 Sebastian Herbszt + * Copyright (c) 2010 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#include "hw/ide/ahci-sysbus.h" +#include "ahci-internal.h" + +static const VMStateDescription vmstate_sysbus_ahci = { + .name = "sysbus-ahci", + .fields = (const VMStateField[]) { + VMSTATE_AHCI(ahci, SysbusAHCIState), + VMSTATE_END_OF_LIST() + }, +}; + +static void sysbus_ahci_reset(DeviceState *dev) +{ + SysbusAHCIState *s = SYSBUS_AHCI(dev); + + ahci_reset(&s->ahci); +} + +static void sysbus_ahci_init(Object *obj) +{ + SysbusAHCIState *s = SYSBUS_AHCI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + ahci_init(&s->ahci, DEVICE(obj)); + + sysbus_init_mmio(sbd, &s->ahci.mem); + sysbus_init_irq(sbd, &s->ahci.irq); +} + +static void sysbus_ahci_realize(DeviceState *dev, Error **errp) +{ + SysbusAHCIState *s = SYSBUS_AHCI(dev); + + ahci_realize(&s->ahci, dev, &address_space_memory); +} + +static Property sysbus_ahci_properties[] = { + DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sysbus_ahci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = sysbus_ahci_realize; + dc->vmsd = &vmstate_sysbus_ahci; + device_class_set_props(dc, sysbus_ahci_properties); + device_class_set_legacy_reset(dc, sysbus_ahci_reset); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo sysbus_ahci_types[] = { + { + .name = TYPE_SYSBUS_AHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysbusAHCIState), + .instance_init = sysbus_ahci_init, + .class_init = sysbus_ahci_class_init, + }, +}; + +DEFINE_TYPES(sysbus_ahci_types) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 5836aa924b..c02357735e 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -23,16 +23,13 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "qemu/module.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include "hw/ide/ahci-sysbus.h" #include "ahci-internal.h" #include "ide-internal.h" @@ -1803,70 +1800,6 @@ const VMStateDescription vmstate_ahci = { }, }; -static const VMStateDescription vmstate_sysbus_ahci = { - .name = "sysbus-ahci", - .fields = (const VMStateField[]) { - VMSTATE_AHCI(ahci, SysbusAHCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static void sysbus_ahci_reset(DeviceState *dev) -{ - SysbusAHCIState *s = SYSBUS_AHCI(dev); - - ahci_reset(&s->ahci); -} - -static void sysbus_ahci_init(Object *obj) -{ - SysbusAHCIState *s = SYSBUS_AHCI(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - ahci_init(&s->ahci, DEVICE(obj)); - - sysbus_init_mmio(sbd, &s->ahci.mem); - sysbus_init_irq(sbd, &s->ahci.irq); -} - -static void sysbus_ahci_realize(DeviceState *dev, Error **errp) -{ - SysbusAHCIState *s = SYSBUS_AHCI(dev); - - ahci_realize(&s->ahci, dev, &address_space_memory); -} - -static Property sysbus_ahci_properties[] = { - DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sysbus_ahci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_ahci_realize; - dc->vmsd = &vmstate_sysbus_ahci; - device_class_set_props(dc, sysbus_ahci_properties); - device_class_set_legacy_reset(dc, sysbus_ahci_reset); - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sysbus_ahci_info = { - .name = TYPE_SYSBUS_AHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysbusAHCIState), - .instance_init = sysbus_ahci_init, - .class_init = sysbus_ahci_class_init, -}; - -static void sysbus_ahci_register_types(void) -{ - type_register_static(&sysbus_ahci_info); -} - -type_init(sysbus_ahci_register_types) - void ahci_ide_create_devs(AHCIState *ahci, DriveInfo **hd) { int i; diff --git a/hw/ide/meson.build b/hw/ide/meson.build index 90ea861423..ddd7066040 100644 --- a/hw/ide/meson.build +++ b/hw/ide/meson.build @@ -1,5 +1,6 @@ system_ss.add(when: 'CONFIG_AHCI', if_true: files('ahci.c')) system_ss.add(when: 'CONFIG_AHCI_ICH9', if_true: files('ich.c')) +system_ss.add(when: 'CONFIG_AHCI_SYSBUS', if_true: files('ahci-sysbus.c')) system_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('ahci-allwinner.c')) system_ss.add(when: 'CONFIG_IDE_BUS', if_true: files('ide-bus.c')) system_ss.add(when: 'CONFIG_IDE_CF', if_true: files('cf.c')) From 50e2b701876c087e14b1e7cabb2ffaa51008e174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Dec 2024 23:48:36 +0100 Subject: [PATCH 0307/2892] hw/mips: Include missing 'exec/tswap.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some files indirectly get "exec/tswap.h" declarations via "exec/cpu-all.h". Include it directly to be able to remove the former from the latter, otherwise we get: hw/mips/malta.c:674:22: error: call to undeclared function 'tswap32'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 674 | tswap32((1 << 31) /* ConfigEn */ | ^ hw/mips/fuloong2e.c:89:23: error: call to undeclared function 'tswap32'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 89 | prom_buf[index] = tswap32(ENVP_VADDR + table_addr); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241211230357.97036-7-philmd@linaro.org> --- hw/mips/fuloong2e.c | 1 + hw/mips/malta.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 7fd8296ccb..904c10b90e 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -40,6 +40,7 @@ #include "sysemu/reset.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" +#include "exec/tswap.h" #define ENVP_PADDR 0x2000 #define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 198da5ba3d..834636dae5 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -28,6 +28,7 @@ #include "qemu/datadir.h" #include "qemu/cutils.h" #include "qemu/guest-random.h" +#include "exec/tswap.h" #include "hw/clock.h" #include "hw/southbridge/piix.h" #include "hw/isa/superio.h" From ccc76731aee7d3efb16a679fa5bf3cc3f57e9f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Dec 2024 23:42:49 +0100 Subject: [PATCH 0308/2892] hw/sh4/r2d: Include missing 'exec/tswap.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit r2d.c indirectly get "exec/tswap.h" declarations via "exec/cpu-all.h". Include it directly to be able to remove the former from the latter, otherwise we get: hw/sh4/r2d.c:357:35: error: call to undeclared function 'tswap32'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 357 | boot_params.loader_type = tswap32(1); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241211230357.97036-8-philmd@linaro.org> --- hw/sh4/r2d.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 7eecd79fcc..e6cc156c23 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -43,6 +43,7 @@ #include "hw/loader.h" #include "hw/usb.h" #include "hw/block/flash.h" +#include "exec/tswap.h" #define FLASH_BASE 0x00000000 #define FLASH_SIZE (16 * MiB) From 456b247eeab067095b680fa4b0fec48137969593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Dec 2024 23:41:58 +0100 Subject: [PATCH 0309/2892] hw/xtensa: Include missing 'exec/tswap.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some files indirectly get "exec/tswap.h" declarations via "exec/cpu-all.h". Include it directly to be able to remove the former from the latter, otherwise we get: hw/xtensa/bootparam.h:40:16: error: call to undeclared function 'tswap16'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 40 | .tag = tswap16(tag), | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241211230357.97036-9-philmd@linaro.org> --- hw/xtensa/bootparam.h | 1 + hw/xtensa/xtfpga.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/xtensa/bootparam.h b/hw/xtensa/bootparam.h index f57ff850bc..4418c78d5b 100644 --- a/hw/xtensa/bootparam.h +++ b/hw/xtensa/bootparam.h @@ -2,6 +2,7 @@ #define HW_XTENSA_BOOTPARAM_H #include "exec/cpu-common.h" +#include "exec/tswap.h" #define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ #define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 398e6256e1..2e264c6198 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -35,6 +35,7 @@ #include "hw/qdev-properties.h" #include "elf.h" #include "exec/memory.h" +#include "exec/tswap.h" #include "hw/char/serial-mm.h" #include "net/net.h" #include "hw/sysbus.h" From eeed7aed0696af646261e20e18daae3fb96d2a6f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:40:50 +0000 Subject: [PATCH 0310/2892] target/arm: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/cpu.c | 26 +++++++++++++------------- target/arm/cpu64.c | 6 +++--- target/arm/tcg/cpu64.c | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 0cbda76ba0..1afa07511e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1558,39 +1558,39 @@ static void arm_cpu_initfn(Object *obj) * 0 means "unset, use the default value". That default might vary depending * on the CPU type, and is set in the realize fn. */ -static Property arm_cpu_gt_cntfrq_property = +static const Property arm_cpu_gt_cntfrq_property = DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, 0); -static Property arm_cpu_reset_cbar_property = +static const Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); -static Property arm_cpu_reset_hivecs_property = +static const Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); #ifndef CONFIG_USER_ONLY -static Property arm_cpu_has_el2_property = +static const Property arm_cpu_has_el2_property = DEFINE_PROP_BOOL("has_el2", ARMCPU, has_el2, true); -static Property arm_cpu_has_el3_property = +static const Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); #endif -static Property arm_cpu_cfgend_property = +static const Property arm_cpu_cfgend_property = DEFINE_PROP_BOOL("cfgend", ARMCPU, cfgend, false); -static Property arm_cpu_has_vfp_property = +static const Property arm_cpu_has_vfp_property = DEFINE_PROP_BOOL("vfp", ARMCPU, has_vfp, true); -static Property arm_cpu_has_vfp_d32_property = +static const Property arm_cpu_has_vfp_d32_property = DEFINE_PROP_BOOL("vfp-d32", ARMCPU, has_vfp_d32, true); -static Property arm_cpu_has_neon_property = +static const Property arm_cpu_has_neon_property = DEFINE_PROP_BOOL("neon", ARMCPU, has_neon, true); -static Property arm_cpu_has_dsp_property = +static const Property arm_cpu_has_dsp_property = DEFINE_PROP_BOOL("dsp", ARMCPU, has_dsp, true); -static Property arm_cpu_has_mpu_property = +static const Property arm_cpu_has_mpu_property = DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); /* This is like DEFINE_PROP_UINT32 but it doesn't set the default value, @@ -1598,7 +1598,7 @@ static Property arm_cpu_has_mpu_property = * the right value for that particular CPU type, and we don't want * to override that with an incorrect constant value. */ -static Property arm_cpu_pmsav7_dregion_property = +static const Property arm_cpu_pmsav7_dregion_property = DEFINE_PROP_UNSIGNED_NODEFAULT("pmsav7-dregion", ARMCPU, pmsav7_dregion, qdev_prop_uint32, uint32_t); @@ -2644,7 +2644,7 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) return oc; } -static Property arm_cpu_properties[] = { +static const Property arm_cpu_properties[] = { DEFINE_PROP_UINT64("midr", ARMCPU, midr, 0), DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, ARM64_AFFINITY_INVALID), diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index c1cac912a0..ec77c5b34a 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -547,11 +547,11 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) cpu->isar.id_aa64isar2 = isar2; } -static Property arm_cpu_pauth_property = +static const Property arm_cpu_pauth_property = DEFINE_PROP_BOOL("pauth", ARMCPU, prop_pauth, true); -static Property arm_cpu_pauth_impdef_property = +static const Property arm_cpu_pauth_impdef_property = DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false); -static Property arm_cpu_pauth_qarma3_property = +static const Property arm_cpu_pauth_qarma3_property = DEFINE_PROP_BOOL("pauth-qarma3", ARMCPU, prop_pauth_qarma3, false); void aarch64_add_pauth_properties(Object *obj) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 2963d7510f..67c110f021 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -198,7 +198,7 @@ static void cpu_max_get_l0gptsz(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } -static Property arm_cpu_lpa2_property = +static const Property arm_cpu_lpa2_property = DEFINE_PROP_BOOL("lpa2", ARMCPU, prop_lpa2, true); static void aarch64_a55_initfn(Object *obj) From 1e2a0e1d9aedfe62393c4f343cdcfa9e290f5438 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:41:43 +0000 Subject: [PATCH 0311/2892] target/avr: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/avr/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 3132842d56..a7529a1b3d 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -149,7 +149,7 @@ static void avr_cpu_initfn(Object *obj) sizeof(cpu->env.intsrc) * 8); } -static Property avr_cpu_properties[] = { +static const Property avr_cpu_properties[] = { DEFINE_PROP_UINT32("init-sp", AVRCPU, init_sp, 0), DEFINE_PROP_END_OF_LIST() }; From ea1143358fa8fa6b92ad5a466e26d66a8e22b78f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:42:37 +0000 Subject: [PATCH 0312/2892] target/hexagon: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index c9aa9408ec..a70007245e 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -48,7 +48,7 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) return oc; } -static Property hexagon_cpu_properties[] = { +static const Property hexagon_cpu_properties[] = { DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, qdev_prop_uint32, target_ulong), From 032c2ec4ee7e34e80924b3fdc970e63ff3498dc3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:44:24 +0000 Subject: [PATCH 0313/2892] target/i386: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 305f2a41cf..5253399459 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5384,7 +5384,7 @@ static X86CPUVersion x86_cpu_model_resolve_version(const X86CPUModel *model) return v; } -static Property max_x86_cpu_properties[] = { +static const Property max_x86_cpu_properties[] = { DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false), DEFINE_PROP_END_OF_LIST() @@ -8407,7 +8407,7 @@ void x86_update_hflags(CPUX86State *env) env->hflags = hflags; } -static Property x86_cpu_properties[] = { +static const Property x86_cpu_properties[] = { #ifdef CONFIG_USER_ONLY /* apic_id = 0 by default for *-user, see commit 9886e834 */ DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, 0), From f3d9225f6a259c5eea527c264d3d0d2cd425d9f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:44:59 +0000 Subject: [PATCH 0314/2892] target/microblaze: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/microblaze/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 0e1e22d1e8..0e41e39c0e 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -339,7 +339,7 @@ static void mb_cpu_initfn(Object *obj) object_property_add_alias(obj, "little-endian", obj, "endianness"); } -static Property mb_properties[] = { +static const Property mb_properties[] = { /* * Following properties are used by Xilinx DTS conversion tool * do not rename them. From b7ce9e19f4578ca52615e2639558db2aba61096f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:45:49 +0000 Subject: [PATCH 0315/2892] target/mips: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 4feacc88c0..02c0e1b0f9 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -539,7 +539,7 @@ static const struct SysemuCPUOps mips_sysemu_ops = { }; #endif -static Property mips_cpu_properties[] = { +static const Property mips_cpu_properties[] = { DEFINE_PROP_BOOL("big-endian", MIPSCPU, is_big_endian, TARGET_BIG_ENDIAN), DEFINE_PROP_END_OF_LIST(), }; From f3ef448ac535cde957b00f0b4468dd259138bac7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:48:37 +0000 Subject: [PATCH 0316/2892] target/riscv: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Signed-off-by: Richard Henderson --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 80b09952e7..4329015076 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2661,7 +2661,7 @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { NULL }; -static Property riscv_cpu_properties[] = { +static const Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), {.name = "pmu-mask", .info = &prop_pmu_mask}, From a1eeba514f6b122dafb519211d4ec43f5e993f47 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:49:20 +0000 Subject: [PATCH 0317/2892] target/s390x: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index adb27504ad..4702761ca3 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -309,7 +309,7 @@ static const gchar *s390_gdb_arch_name(CPUState *cs) return "s390:64-bit"; } -static Property s390x_cpu_properties[] = { +static const Property s390x_cpu_properties[] = { #if !defined(CONFIG_USER_ONLY) DEFINE_PROP_UINT32("core-id", S390CPU, env.core_id, 0), DEFINE_PROP_INT32("socket-id", S390CPU, env.socket_id, -1), From 3834cc6f8fa5c3d8e82e9790fef66b94f3e97e5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:50:36 +0000 Subject: [PATCH 0318/2892] target/sparc: Constify all Property and PropertyInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 284df950e0..8f494c286a 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -874,14 +874,14 @@ static void sparc_set_nwindows(Object *obj, Visitor *v, const char *name, cpu->env.def.nwindows = value; } -static PropertyInfo qdev_prop_nwindows = { +static const PropertyInfo qdev_prop_nwindows = { .name = "int", .get = sparc_get_nwindows, .set = sparc_set_nwindows, }; /* This must match feature_name[]. */ -static Property sparc_cpu_properties[] = { +static const Property sparc_cpu_properties[] = { DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, CPU_FEATURE_BIT_FLOAT128, false), #ifdef TARGET_SPARC64 From 9aec5dc3a41f6cf821d71565d9cd30bfed9541f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:52:29 +0000 Subject: [PATCH 0319/2892] cpu-target: Constify all Property 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 --- cpu-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu-target.c b/cpu-target.c index 499facf774..2ae07a779e 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -183,7 +183,7 @@ void cpu_exec_unrealizefn(CPUState *cpu) * This can't go in hw/core/cpu.c because that file is compiled only * once for both user-mode and system builds. */ -static Property cpu_common_props[] = { +static const Property cpu_common_props[] = { #ifdef CONFIG_USER_ONLY /* * Create a property for the user-only object, so users can From 46408f1811447923921cd7d618e2c1e747592558 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:57:01 +0000 Subject: [PATCH 0320/2892] hw/9pfs: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Greg Kurz Reviewed-by: Christian Schoenebeck Signed-off-by: Richard Henderson --- hw/9pfs/virtio-9p-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index efa41cfd73..b764e4cd3d 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -243,7 +243,7 @@ static const VMStateDescription vmstate_virtio_9p = { }, }; -static Property virtio_9p_properties[] = { +static const Property virtio_9p_properties[] = { DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), DEFINE_PROP_END_OF_LIST(), From c76bc08df2c05bdfa561f3486d38800718212d10 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:58:37 +0000 Subject: [PATCH 0321/2892] hw/acpi: Constify all Property 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 --- hw/acpi/erst.c | 2 +- hw/acpi/generic_event_device.c | 2 +- hw/acpi/piix4.c | 2 +- hw/acpi/vmgenid.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index a108cfe49b..5ef5ddccb6 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -1011,7 +1011,7 @@ static void erst_reset(DeviceState *dev) trace_acpi_erst_reset_out(le32_to_cpu(s->header->record_count)); } -static Property erst_properties[] = { +static const 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, diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 663d9cb093..8c4706f8cf 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -316,7 +316,7 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) qemu_irq_pulse(s->irq); } -static Property acpi_ged_properties[] = { +static const Property acpi_ged_properties[] = { DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 1de3fe3261..2bfaf5a38d 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -602,7 +602,7 @@ static void piix4_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) acpi_send_gpe_event(&s->ar, s->irq, ev); } -static Property piix4_pm_properties[] = { +static const Property piix4_pm_properties[] = { DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index e63c8af4c3..9c2ca85cc7 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -214,7 +214,7 @@ static void vmgenid_realize(DeviceState *dev, Error **errp) vmgenid_update_guest(vms); } -static Property vmgenid_device_properties[] = { +static const Property vmgenid_device_properties[] = { DEFINE_PROP_UUID(VMGENID_GUID, VmGenIdState, guid), DEFINE_PROP_END_OF_LIST(), }; From b5e4f90e64f0c2c4c3a6c65b0de11b752640e31b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:59:51 +0000 Subject: [PATCH 0322/2892] hw/adc: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Richard Henderson --- hw/adc/aspeed_adc.c | 2 +- hw/adc/npcm7xx_adc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index 598f2bdf48..f94c6f2be3 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -286,7 +286,7 @@ static const VMStateDescription vmstate_aspeed_adc_engine = { } }; -static Property aspeed_adc_engine_properties[] = { +static const Property aspeed_adc_engine_properties[] = { DEFINE_PROP_UINT32("engine-id", AspeedADCEngineState, engine_id, 0), DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index de8469dae4..1781ff4c0b 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -267,7 +267,7 @@ static const VMStateDescription vmstate_npcm7xx_adc = { }, }; -static Property npcm7xx_timer_properties[] = { +static const Property npcm7xx_timer_properties[] = { DEFINE_PROP_UINT32("iref", NPCM7xxADCState, iref, NPCM7XX_ADC_DEFAULT_IREF), DEFINE_PROP_END_OF_LIST(), }; From e15bd5dd059a8833dd30da2b78ab4f43e917f216 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:03:52 +0000 Subject: [PATCH 0323/2892] hw/arm: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Richard Henderson --- hw/arm/armsse.c | 8 ++++---- hw/arm/armv7m.c | 4 ++-- hw/arm/aspeed_soc_common.c | 2 +- hw/arm/bcm2836.c | 2 +- hw/arm/fsl-imx25.c | 2 +- hw/arm/fsl-imx6.c | 2 +- hw/arm/fsl-imx6ul.c | 2 +- hw/arm/fsl-imx7.c | 2 +- hw/arm/integratorcp.c | 2 +- hw/arm/msf2-soc.c | 2 +- hw/arm/npcm7xx.c | 2 +- hw/arm/nrf51_soc.c | 2 +- hw/arm/smmu-common.c | 2 +- hw/arm/smmuv3.c | 2 +- hw/arm/stellaris.c | 2 +- hw/arm/strongarm.c | 2 +- hw/arm/xlnx-versal.c | 2 +- hw/arm/xlnx-zynqmp.c | 2 +- 18 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 58ed504b2b..1cd6b4a4b2 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -72,12 +72,12 @@ struct ARMSSEInfo { bool has_cpu_pwrctrl; bool has_sse_counter; bool has_tcms; - Property *props; + const Property *props; const ARMSSEDeviceInfo *devinfo; const bool *irq_is_common; }; -static Property iotkit_properties[] = { +static const Property iotkit_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), @@ -90,7 +90,7 @@ static Property iotkit_properties[] = { DEFINE_PROP_END_OF_LIST() }; -static Property sse200_properties[] = { +static const Property sse200_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), @@ -107,7 +107,7 @@ static Property sse200_properties[] = { DEFINE_PROP_END_OF_LIST() }; -static Property sse300_properties[] = { +static const Property sse300_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 7c68525a9e..e20f719c9b 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -538,7 +538,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp) } } -static Property armv7m_properties[] = { +static const Property armv7m_properties[] = { DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type), DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), @@ -631,7 +631,7 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, qemu_register_reset(armv7m_reset, cpu); } -static Property bitband_properties[] = { +static const Property bitband_properties[] = { DEFINE_PROP_UINT32("base", BitBandState, base, 0), DEFINE_PROP_LINK("source-memory", BitBandState, source_memory, TYPE_MEMORY_REGION, MemoryRegion *), diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index a5ff33c46d..4221cacd51 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -139,7 +139,7 @@ static bool aspeed_soc_boot_from_emmc(AspeedSoCState *s) return false; } -static Property aspeed_soc_properties[] = { +static const Property aspeed_soc_properties[] = { DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 40a379bc36..95e16806fa 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -18,7 +18,7 @@ #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" -static Property bcm2836_enabled_cores_property = +static const Property bcm2836_enabled_cores_property = DEFINE_PROP_UINT32("enabled-cpus", BCM283XBaseState, enabled_cpus, 0); static void bcm283x_base_init(Object *obj) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 5ed87edfe4..48763b03fe 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -309,7 +309,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &s->iram_alias); } -static Property fsl_imx25_properties[] = { +static const Property fsl_imx25_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX25State, phy_num, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 85748cb233..236d15bc9c 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -481,7 +481,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->ocram_alias); } -static Property fsl_imx6_properties[] = { +static const Property fsl_imx6_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX6State, phy_num, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 19f443570b..1e0bbbb5d7 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -718,7 +718,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_OCRAM_ALIAS_ADDR, &s->ocram_alias); } -static Property fsl_imx6ul_properties[] = { +static const Property fsl_imx6ul_properties[] = { DEFINE_PROP_UINT32("fec1-phy-num", FslIMX6ULState, phy_num[0], 0), DEFINE_PROP_UINT32("fec2-phy-num", FslIMX6ULState, phy_num[1], 1), DEFINE_PROP_BOOL("fec1-phy-connected", FslIMX6ULState, phy_connected[0], diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 9f2ef34555..0310c15b0c 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -736,7 +736,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) &s->caam); } -static Property fsl_imx7_properties[] = { +static const Property fsl_imx7_properties[] = { DEFINE_PROP_UINT32("fec1-phy-num", FslIMX7State, phy_num[0], 0), DEFINE_PROP_UINT32("fec2-phy-num", FslIMX7State, phy_num[1], 1), DEFINE_PROP_BOOL("fec1-phy-connected", FslIMX7State, phy_connected[0], diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index feb0dd63df..ee6c7e0c0d 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -694,7 +694,7 @@ static void integratorcp_machine_init(MachineClass *mc) DEFINE_MACHINE("integratorcp", integratorcp_machine_init) -static Property core_properties[] = { +static const Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index c4999ebce3..5d7c3f2e5a 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -222,7 +222,7 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("usb", 0x40043000, 0x1000); } -static Property m2sxxx_soc_properties[] = { +static const Property m2sxxx_soc_properties[] = { /* * part name specifies the type of SmartFusion2 device variant(this * property is for information purpose only. diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index af04c4b7ec..2960b63b59 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -810,7 +810,7 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.spix", 0xfb001000, 4 * KiB); } -static Property npcm7xx_properties[] = { +static const Property npcm7xx_properties[] = { DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index ac53441630..43fac8a8db 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -208,7 +208,7 @@ static void nrf51_soc_init(Object *obj) s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } -static Property nrf51_soc_properties[] = { +static const Property nrf51_soc_properties[] = { DEFINE_PROP_LINK("memory", NRF51State, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("sram-size", NRF51State, sram_size, NRF51822_SRAM_SIZE), diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3f82728758..6baa9d0fc3 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -934,7 +934,7 @@ static void smmu_base_reset_hold(Object *obj, ResetType type) g_hash_table_remove_all(s->iotlb); } -static Property smmu_dev_properties[] = { +static const Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, TYPE_PCI_BUS, PCIBus *), diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 6e847e8773..026838f9ac 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1976,7 +1976,7 @@ static const VMStateDescription vmstate_smmuv3 = { } }; -static Property smmuv3_properties[] = { +static const Property smmuv3_properties[] = { /* * Stages of translation advertised. * "1": Stage 1 diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 376746251e..7fc13d96c9 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -438,7 +438,7 @@ static const VMStateDescription vmstate_stellaris_sys = { } }; -static Property stellaris_sys_properties[] = { +static const Property stellaris_sys_properties[] = { DEFINE_PROP_UINT32("user0", ssys_state, user0, 0), DEFINE_PROP_UINT32("user1", ssys_state, user1, 0), DEFINE_PROP_UINT32("did0", ssys_state, did0, 0), diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 612115ab5b..4c4ff21e80 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1332,7 +1332,7 @@ static const VMStateDescription vmstate_strongarm_uart_regs = { }, }; -static Property strongarm_uart_properties[] = { +static const Property strongarm_uart_properties[] = { DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 3a1e2e29f1..3adbe7b1fb 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -968,7 +968,7 @@ static void versal_init(Object *obj) "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); } -static Property versal_properties[] = { +static const Property versal_properties[] = { DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("canbus0", Versal, lpd.iou.canbus[0], diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index ab2d50e31b..1082c62c30 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -857,7 +857,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } } -static Property xlnx_zynqmp_props[] = { +static const Property xlnx_zynqmp_props[] = { DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu), DEFINE_PROP_BOOL("secure", XlnxZynqMPState, secure, false), DEFINE_PROP_BOOL("virtualization", XlnxZynqMPState, virt, false), From ed1e71dac96a3bf2236ece81916d4fc1ccbce029 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:05:59 +0000 Subject: [PATCH 0324/2892] hw/audio: Constify all Property 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 --- hw/audio/ac97.c | 2 +- hw/audio/adlib.c | 2 +- hw/audio/asc.c | 2 +- hw/audio/cs4231a.c | 2 +- hw/audio/es1370.c | 2 +- hw/audio/gus.c | 2 +- hw/audio/hda-codec.c | 2 +- hw/audio/intel-hda.c | 4 ++-- hw/audio/pcspk.c | 2 +- hw/audio/pl041.c | 2 +- hw/audio/sb16.c | 2 +- hw/audio/via-ac97.c | 2 +- hw/audio/virtio-snd-pci.c | 2 +- hw/audio/virtio-snd.c | 2 +- hw/audio/wm8750.c | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index e373f09d78..8033bbbaed 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1324,7 +1324,7 @@ static void ac97_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static Property ac97_properties[] = { +static const Property ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(AC97LinkState, card), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index bd73806d83..c1d8faecb4 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -297,7 +297,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0); } -static Property adlib_properties[] = { +static const Property adlib_properties[] = { DEFINE_AUDIO_PROPERTIES(AdlibState, card), DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), diff --git a/hw/audio/asc.c b/hw/audio/asc.c index 805416372c..452039418d 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -695,7 +695,7 @@ static void asc_init(Object *obj) sysbus_init_mmio(sbd, &s->asc); } -static Property asc_properties[] = { +static const Property asc_properties[] = { DEFINE_AUDIO_PROPERTIES(ASCState, card), DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 2d69372087..abc38720a3 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -689,7 +689,7 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) isa_register_ioport (d, &s->ioports, s->port); } -static Property cs4231a_properties[] = { +static const Property cs4231a_properties[] = { DEFINE_AUDIO_PROPERTIES(CSState, card), DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), DEFINE_PROP_UINT32 ("irq", CSState, irq, 9), diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 9a508e7b81..6170425a5a 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -868,7 +868,7 @@ static void es1370_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static Property es1370_properties[] = { +static const Property es1370_properties[] = { DEFINE_AUDIO_PROPERTIES(ES1370State, card), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 4beb3fd74e..dd5a5a3441 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -290,7 +290,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) AUD_set_active_out (s->voice, 1); } -static Property gus_properties[] = { +static const Property gus_properties[] = { DEFINE_AUDIO_PROPERTIES(GUSState, card), DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index c340a9481d..8bd8f62c48 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -857,7 +857,7 @@ static const VMStateDescription vmstate_hda_audio = { } }; -static Property hda_audio_properties[] = { +static const Property hda_audio_properties[] = { DEFINE_AUDIO_PROPERTIES(HDAAudioState, card), DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 6918e23c5d..3e4a755228 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -37,7 +37,7 @@ /* --------------------------------------------------------------------- */ /* hda bus */ -static Property hda_props[] = { +static const Property hda_props[] = { DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1), DEFINE_PROP_END_OF_LIST() }; @@ -1215,7 +1215,7 @@ static const VMStateDescription vmstate_intel_hda = { } }; -static Property intel_hda_properties[] = { +static const Property intel_hda_properties[] = { DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), DEFINE_PROP_ON_OFF_AUTO("msi", IntelHDAState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index a4b89f1768..7a6b9f52d3 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -215,7 +215,7 @@ static const VMStateDescription vmstate_spk = { } }; -static Property pcspk_properties[] = { +static const Property pcspk_properties[] = { DEFINE_AUDIO_PROPERTIES(PCSpkState, card), DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, 0x61), DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index eb96dc2898..6c66a240cb 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -625,7 +625,7 @@ static const VMStateDescription vmstate_pl041 = { } }; -static Property pl041_device_properties[] = { +static const Property pl041_device_properties[] = { DEFINE_AUDIO_PROPERTIES(PL041State, codec.card), /* Non-compact FIFO depth property */ DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth, diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index fd76e78d18..143b9e71e1 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1440,7 +1440,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) s->can_write = 1; } -static Property sb16_properties[] = { +static const Property sb16_properties[] = { DEFINE_AUDIO_PROPERTIES(SB16State, card), DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */ DEFINE_PROP_UINT32 ("iobase", SB16State, port, 0x220), diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c index 85243e6313..e43ddf37f3 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -459,7 +459,7 @@ static void via_ac97_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static Property via_ac97_properties[] = { +static const Property via_ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(ViaAC97State, card), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/audio/virtio-snd-pci.c b/hw/audio/virtio-snd-pci.c index ab58c6410e..b762d7e81e 100644 --- a/hw/audio/virtio-snd-pci.c +++ b/hw/audio/virtio-snd-pci.c @@ -27,7 +27,7 @@ struct VirtIOSoundPCI { VirtIOSound vdev; }; -static Property virtio_snd_pci_properties[] = { +static const Property virtio_snd_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index c5581d7b3d..e2b112e059 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -77,7 +77,7 @@ static const VMStateDescription vmstate_virtio_snd = { }, }; -static Property virtio_snd_properties[] = { +static const Property virtio_snd_properties[] = { DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, VIRTIO_SOUND_JACK_DEFAULT), diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index ec2c4e1374..19e7755060 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -706,7 +706,7 @@ void wm8750_set_bclk_in(void *opaque, int new_hz) wm8750_clk_update(s, 1); } -static Property wm8750_properties[] = { +static const Property wm8750_properties[] = { DEFINE_AUDIO_PROPERTIES(WM8750State, card), DEFINE_PROP_END_OF_LIST(), }; From 47c7764bd7255cd28a3b81a71d63c5f0bfd8bc99 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:06:43 +0000 Subject: [PATCH 0325/2892] hw/avr: Constify all Property 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 --- hw/avr/atmega.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index 31c8992d75..ce630ec572 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -355,7 +355,7 @@ static void atmega_realize(DeviceState *dev, Error **errp) create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); } -static Property atmega_props[] = { +static const Property atmega_props[] = { DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState, xtal_freq_hz, 0), DEFINE_PROP_END_OF_LIST() From eafbd38f17bb8807386b9b0404193eda4b2d9d91 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:17:26 +0000 Subject: [PATCH 0326/2892] hw/block/xen-block: Unexport PropertyInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xen_block_prop_vdev is not used outside the file. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Jason Andryuk Signed-off-by: Richard Henderson --- hw/block/xen-block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index aed1d5c330..72cfd6893a 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -659,7 +659,7 @@ invalid: * * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html */ -const PropertyInfo xen_block_prop_vdev = { +static const PropertyInfo xen_block_prop_vdev = { .name = "str", .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*", .get = xen_block_get_vdev, From 4aef8b63e48ce459870c5ed78e86c0eaa331edac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:18:23 +0000 Subject: [PATCH 0327/2892] hw/block: Constify all Property 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 --- hw/block/fdc-isa.c | 2 +- hw/block/fdc-sysbus.c | 4 ++-- hw/block/fdc.c | 2 +- hw/block/m25p80.c | 2 +- hw/block/nand.c | 2 +- hw/block/pflash_cfi01.c | 2 +- hw/block/pflash_cfi02.c | 2 +- hw/block/swim.c | 2 +- hw/block/vhost-user-blk.c | 2 +- hw/block/virtio-blk.c | 2 +- hw/block/xen-block.c | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index 5ed3c18c28..2b9f667fe4 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -283,7 +283,7 @@ static const VMStateDescription vmstate_isa_fdc = { } }; -static Property isa_fdc_properties[] = { +static const Property isa_fdc_properties[] = { DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index e1ddbf3d1a..f17e04b138 100644 --- a/hw/block/fdc-sysbus.c +++ b/hw/block/fdc-sysbus.c @@ -196,7 +196,7 @@ static const TypeInfo sysbus_fdc_common_typeinfo = { .class_size = sizeof(FDCtrlSysBusClass), }; -static Property sysbus_fdc_properties[] = { +static const Property sysbus_fdc_properties[] = { DEFINE_PROP_SIGNED("fdtypeA", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), @@ -223,7 +223,7 @@ static const TypeInfo sysbus_fdc_typeinfo = { .class_init = sysbus_fdc_class_init, }; -static Property sun4m_fdc_properties[] = { +static const Property sun4m_fdc_properties[] = { DEFINE_PROP_SIGNED("fdtype", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 6dd94e98bc..57d6844806 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -454,7 +454,7 @@ struct FloppyDrive { FloppyDriveType type; }; -static Property floppy_drive_properties[] = { +static const Property floppy_drive_properties[] = { DEFINE_PROP_UINT32("unit", FloppyDrive, unit, -1), DEFINE_BLOCK_PROPERTIES(FloppyDrive, conf), DEFINE_PROP_SIGNED("drive-type", FloppyDrive, type, diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 748594524e..ca97365926 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1720,7 +1720,7 @@ static int m25p80_pre_save(void *opaque) return 0; } -static Property m25p80_properties[] = { +static const Property m25p80_properties[] = { /* This is default value for Micron flash */ DEFINE_PROP_BOOL("write-enable", Flash, write_enable, false), DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), diff --git a/hw/block/nand.c b/hw/block/nand.c index ac0a5d2b42..b6e6bfac23 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -445,7 +445,7 @@ static void nand_realize(DeviceState *dev, Error **errp) s->ioaddr = s->io; } -static Property nand_properties[] = { +static const Property nand_properties[] = { DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 21a81b44f0..20f4fc67a0 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -895,7 +895,7 @@ static void pflash_cfi01_system_reset(DeviceState *dev) pfl->blk_offset = -1; } -static Property pflash_cfi01_properties[] = { +static const Property pflash_cfi01_properties[] = { DEFINE_PROP_DRIVE("drive", PFlashCFI01, blk), /* num-blocks is the number of blocks actually visible to the guest, * ie the total size of the device divided by the sector length. diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 8393f261b8..c82002d665 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -937,7 +937,7 @@ static void pflash_cfi02_reset(DeviceState *dev) pflash_reset_state_machine(pfl); } -static Property pflash_cfi02_properties[] = { +static const Property pflash_cfi02_properties[] = { DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk), DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, uniform_nb_blocs, 0), DEFINE_PROP_UINT32("sector-length", PFlashCFI02, uniform_sector_len, 0), diff --git a/hw/block/swim.c b/hw/block/swim.c index 64992eb72e..c336d83bdc 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -166,7 +166,7 @@ static const BlockDevOps swim_block_ops = { .change_media_cb = swim_change_cb, }; -static Property swim_drive_properties[] = { +static const Property swim_drive_properties[] = { DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1), DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 7996e49821..f3ac007108 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -570,7 +570,7 @@ static const VMStateDescription vmstate_vhost_user_blk = { }, }; -static Property vhost_user_blk_properties[] = { +static const Property vhost_user_blk_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev), DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, VHOST_USER_BLK_AUTO_NUM_QUEUES), diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 9166d7974d..9ca60fbc07 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1985,7 +1985,7 @@ static const VMStateDescription vmstate_virtio_blk = { }, }; -static Property virtio_blk_properties[] = { +static const Property virtio_blk_properties[] = { DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 72cfd6893a..0c0817f498 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -666,7 +666,7 @@ static const PropertyInfo xen_block_prop_vdev = { .set = xen_block_set_vdev, }; -static Property xen_block_props[] = { +static const Property xen_block_props[] = { DEFINE_PROP("vdev", XenBlockDevice, props.vdev, xen_block_prop_vdev, XenBlockVdev), DEFINE_BLOCK_PROPERTIES(XenBlockDevice, props.conf), From 312f37d18ad53e2e62fdd2dda7c38322866d77f1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:21:28 +0000 Subject: [PATCH 0328/2892] hw/char: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alberto Garcia Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/char/avr_usart.c | 2 +- hw/char/bcm2835_aux.c | 2 +- hw/char/cadence_uart.c | 2 +- hw/char/cmsdk-apb-uart.c | 2 +- hw/char/debugcon.c | 2 +- hw/char/digic-uart.c | 2 +- hw/char/escc.c | 2 +- hw/char/exynos4210_uart.c | 2 +- hw/char/goldfish_tty.c | 2 +- hw/char/grlib_apbuart.c | 2 +- hw/char/ibex_uart.c | 2 +- hw/char/imx_serial.c | 2 +- hw/char/ipoctal232.c | 2 +- hw/char/mcf_uart.c | 2 +- hw/char/nrf51_uart.c | 2 +- hw/char/parallel.c | 2 +- hw/char/pl011.c | 2 +- hw/char/renesas_sci.c | 2 +- hw/char/sclpconsole-lm.c | 2 +- hw/char/sclpconsole.c | 2 +- hw/char/serial-isa.c | 2 +- hw/char/serial-mm.c | 2 +- hw/char/serial-pci-multi.c | 4 ++-- hw/char/serial-pci.c | 2 +- hw/char/serial.c | 2 +- hw/char/sh_serial.c | 2 +- hw/char/shakti_uart.c | 2 +- hw/char/sifive_uart.c | 2 +- hw/char/spapr_vty.c | 2 +- hw/char/stm32f2xx_usart.c | 2 +- hw/char/stm32l4x5_usart.c | 2 +- hw/char/terminal3270.c | 2 +- hw/char/virtio-console.c | 2 +- hw/char/virtio-serial-bus.c | 4 ++-- hw/char/xen_console.c | 2 +- hw/char/xilinx_uartlite.c | 2 +- 36 files changed, 38 insertions(+), 38 deletions(-) diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c index 3aff01cd54..3421576e45 100644 --- a/hw/char/avr_usart.c +++ b/hw/char/avr_usart.c @@ -259,7 +259,7 @@ static const MemoryRegionOps avr_usart_ops = { .impl = {.min_access_size = 1, .max_access_size = 1} }; -static Property avr_usart_properties[] = { +static const Property avr_usart_properties[] = { DEFINE_PROP_CHR("chardev", AVRUsartState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index fca2f27a55..30285c97b0 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -290,7 +290,7 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) bcm2835_aux_receive, NULL, NULL, s, NULL, true); } -static Property bcm2835_aux_props[] = { +static const Property bcm2835_aux_props[] = { DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 77d9a2a221..2e778f7a9c 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -617,7 +617,7 @@ static const VMStateDescription vmstate_cadence_uart = { }, }; -static Property cadence_uart_properties[] = { +static const Property cadence_uart_properties[] = { DEFINE_PROP_CHR("chardev", CadenceUARTState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index 467e40b715..e37e14e0f2 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -377,7 +377,7 @@ static const VMStateDescription cmsdk_apb_uart_vmstate = { } }; -static Property cmsdk_apb_uart_properties[] = { +static const Property cmsdk_apb_uart_properties[] = { DEFINE_PROP_CHR("chardev", CMSDKAPBUART, chr), DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBUART, pclk_frq, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index fdb04fee09..c0f16e9bd6 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -114,7 +114,7 @@ static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) isa->iobase, &s->io); } -static Property debugcon_isa_properties[] = { +static const Property debugcon_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9), DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 5b04abec1d..03beba11ad 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -172,7 +172,7 @@ static const VMStateDescription vmstate_digic_uart = { } }; -static Property digic_uart_properties[] = { +static const Property digic_uart_properties[] = { DEFINE_PROP_CHR("chardev", DigicUartState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/escc.c b/hw/char/escc.c index b1b1bbed15..08bc65ef2c 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -1089,7 +1089,7 @@ static void escc_realize(DeviceState *dev, Error **errp) } } -static Property escc_properties[] = { +static const Property escc_properties[] = { DEFINE_PROP_UINT32("frequency", ESCCState, frequency, 0), DEFINE_PROP_UINT32("it_shift", ESCCState, it_shift, 0), DEFINE_PROP_BOOL("bit_swap", ESCCState, bit_swap, false), diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index d9e732f98b..c2836ff8fd 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -704,7 +704,7 @@ static void exynos4210_uart_realize(DeviceState *dev, Error **errp) NULL, s, NULL, true); } -static Property exynos4210_uart_properties[] = { +static const Property exynos4210_uart_properties[] = { DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr), DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index d1917b83d8..68e261236e 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -241,7 +241,7 @@ static const VMStateDescription vmstate_goldfish_tty = { } }; -static Property goldfish_tty_properties[] = { +static const Property goldfish_tty_properties[] = { DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index d0032b4d2a..caae88d77d 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -277,7 +277,7 @@ static void grlib_apbuart_reset(DeviceState *d) uart->current = 0; } -static Property grlib_apbuart_properties[] = { +static const Property grlib_apbuart_properties[] = { DEFINE_PROP_CHR("chrdev", UART, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c index 589177f85b..b1bdb2ad15 100644 --- a/hw/char/ibex_uart.c +++ b/hw/char/ibex_uart.c @@ -508,7 +508,7 @@ static const VMStateDescription vmstate_ibex_uart = { } }; -static Property ibex_uart_properties[] = { +static const Property ibex_uart_properties[] = { DEFINE_PROP_CHR("chardev", IbexUartState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 22c9080b1c..6376f2cadc 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -438,7 +438,7 @@ static void imx_serial_init(Object *obj) sysbus_init_irq(sbd, &s->irq); } -static Property imx_serial_properties[] = { +static const Property imx_serial_properties[] = { DEFINE_PROP_CHR("chardev", IMXSerialState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 64be5226d4..fb8cb6c2b7 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -558,7 +558,7 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) } } -static Property ipoctal_properties[] = { +static const Property ipoctal_properties[] = { DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev), DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev), DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev), diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index ad15e28944..c044536d5d 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -312,7 +312,7 @@ static void mcf_uart_realize(DeviceState *dev, Error **errp) mcf_uart_event, NULL, s, NULL, true); } -static Property mcf_uart_properties[] = { +static const Property mcf_uart_properties[] = { DEFINE_PROP_CHR("chardev", mcf_uart_state, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c index 04da3f8d97..b164c70f52 100644 --- a/hw/char/nrf51_uart.c +++ b/hw/char/nrf51_uart.c @@ -304,7 +304,7 @@ static const VMStateDescription nrf51_uart_vmstate = { } }; -static Property nrf51_uart_properties[] = { +static const Property nrf51_uart_properties[] = { DEFINE_PROP_CHR("chardev", NRF51UARTState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/parallel.c b/hw/char/parallel.c index c394635ada..15191698f5 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -603,7 +603,7 @@ bool parallel_mm_init(MemoryRegion *address_space, return true; } -static Property parallel_isa_properties[] = { +static const Property parallel_isa_properties[] = { DEFINE_PROP_UINT32("index", ISAParallelState, index, -1), DEFINE_PROP_UINT32("iobase", ISAParallelState, iobase, -1), DEFINE_PROP_UINT32("irq", ISAParallelState, isairq, 7), diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 0fd1334fab..5fbee5e6c5 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -603,7 +603,7 @@ static const VMStateDescription vmstate_pl011 = { } }; -static Property pl011_properties[] = { +static const Property pl011_properties[] = { DEFINE_PROP_CHR("chardev", PL011State, chr), DEFINE_PROP_BOOL("migrate-clk", PL011State, migrate_clk, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c index 7ce0408b0c..516b48648b 100644 --- a/hw/char/renesas_sci.c +++ b/hw/char/renesas_sci.c @@ -319,7 +319,7 @@ static const VMStateDescription vmstate_rsci = { } }; -static Property rsci_properties[] = { +static const Property rsci_properties[] = { DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), DEFINE_PROP_CHR("chardev", RSCIState, chr), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 4fe1c4d289..536b283471 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -333,7 +333,7 @@ static void console_reset(DeviceState *dev) scon->write_errors = 0; } -static Property console_properties[] = { +static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index e6d49e819e..a90b892d1d 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -251,7 +251,7 @@ static void console_reset(DeviceState *dev) scon->notify = false; } -static Property console_properties[] = { +static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsole, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index b562ec9d37..2cf50eb0bb 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -113,7 +113,7 @@ static const VMStateDescription vmstate_isa_serial = { } }; -static Property serial_isa_properties[] = { +static const Property serial_isa_properties[] = { DEFINE_PROP_UINT32("index", ISASerialState, index, -1), DEFINE_PROP_UINT32("iobase", ISASerialState, iobase, -1), DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), diff --git a/hw/char/serial-mm.c b/hw/char/serial-mm.c index 2f67776b19..8f51f1d3b8 100644 --- a/hw/char/serial-mm.c +++ b/hw/char/serial-mm.c @@ -125,7 +125,7 @@ static void serial_mm_instance_init(Object *o) qdev_alias_all_properties(DEVICE(&smm->serial), o); } -static Property serial_mm_properties[] = { +static const Property serial_mm_properties[] = { /* * Set the spacing between adjacent memory-mapped UART registers. * Each register will be at (1 << regshift) bytes after the previous one. diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 28b275709a..c2f20d8e74 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -132,14 +132,14 @@ static const VMStateDescription vmstate_pci_multi_serial = { } }; -static Property multi_2x_serial_pci_properties[] = { +static const Property multi_2x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), DEFINE_PROP_END_OF_LIST(), }; -static Property multi_4x_serial_pci_properties[] = { +static const Property multi_4x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index f8a1a94d0c..2f487a3a79 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -81,7 +81,7 @@ static const VMStateDescription vmstate_pci_serial = { } }; -static Property serial_pci_properties[] = { +static const Property serial_pci_properties[] = { DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/serial.c b/hw/char/serial.c index b50a8a1313..85dba02ace 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -964,7 +964,7 @@ const MemoryRegionOps serial_io_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static Property serial_properties[] = { +static const Property serial_properties[] = { DEFINE_PROP_CHR("chardev", SerialState, chr), DEFINE_PROP_UINT32("baudbase", SerialState, baudbase, 115200), DEFINE_PROP_BOOL("wakeup", SerialState, wakeup, false), diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 429b2562aa..2ab7197aee 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -447,7 +447,7 @@ static void sh_serial_init(Object *obj) { } -static Property sh_serial_properties[] = { +static const Property sh_serial_properties[] = { DEFINE_PROP_CHR("chardev", SHSerialState, chr), DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), DEFINE_PROP_END_OF_LIST() diff --git a/hw/char/shakti_uart.c b/hw/char/shakti_uart.c index 4a71953c9a..6e56754ca6 100644 --- a/hw/char/shakti_uart.c +++ b/hw/char/shakti_uart.c @@ -157,7 +157,7 @@ static void shakti_uart_instance_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &sus->mmio); } -static Property shakti_uart_properties[] = { +static const Property shakti_uart_properties[] = { DEFINE_PROP_CHR("chardev", ShaktiUartState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 5ae2a29ed6..97e4be37c0 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -251,7 +251,7 @@ static int sifive_uart_be_change(void *opaque) return 0; } -static Property sifive_uart_properties[] = { +static const Property sifive_uart_properties[] = { DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 3e23d9cbab..cd91dad709 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -163,7 +163,7 @@ void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev) qdev_realize_and_unref(dev, &bus->bus, &error_fatal); } -static Property spapr_vty_properties[] = { +static const Property spapr_vty_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev), DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 17b5b1f15f..4a3c30eddb 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -199,7 +199,7 @@ static const MemoryRegionOps stm32f2xx_usart_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static Property stm32f2xx_usart_properties[] = { +static const Property stm32f2xx_usart_properties[] = { DEFINE_PROP_CHR("chardev", STM32F2XXUsartState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 3cf200c080..360e79cc3f 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -534,7 +534,7 @@ static const MemoryRegionOps stm32l4x5_usart_base_ops = { }, }; -static Property stm32l4x5_usart_base_properties[] = { +static const Property stm32l4x5_usart_base_properties[] = { DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index 82e85fac2e..c2aafda0ce 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -283,7 +283,7 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd) return (retval <= 0) ? 0 : get_cds(t)->count; } -static Property terminal_properties[] = { +static const Property terminal_properties[] = { DEFINE_PROP_CHR("chardev", Terminal3270, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index dbe0b28e60..f58292e2bb 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -274,7 +274,7 @@ static const TypeInfo virtconsole_info = { .class_init = virtconsole_class_init, }; -static Property virtserialport_properties[] = { +static const Property virtserialport_properties[] = { DEFINE_PROP_CHR("chardev", VirtConsole, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 2094d213cd..1e631bcb2b 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -835,7 +835,7 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); -static Property virtser_props[] = { +static const Property virtser_props[] = { DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_STRING("name", VirtIOSerialPort, name), DEFINE_PROP_END_OF_LIST() @@ -1153,7 +1153,7 @@ static const VMStateDescription vmstate_virtio_console = { }, }; -static Property virtio_serial_properties[] = { +static const Property virtio_serial_properties[] = { DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports, 31), DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features, diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 683c92aca1..c20c1b4b84 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -487,7 +487,7 @@ static char *xen_console_get_frontend_path(XenDevice *xendev, Error **errp) } -static Property xen_console_properties[] = { +static const Property xen_console_properties[] = { DEFINE_PROP_CHR("chardev", XenConsole, chr), DEFINE_PROP_INT32("idx", XenConsole, dev, -1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index f325084f8b..ad77226217 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -176,7 +176,7 @@ static const MemoryRegionOps uart_ops = { } }; -static Property xilinx_uartlite_properties[] = { +static const Property xilinx_uartlite_properties[] = { DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr), DEFINE_PROP_END_OF_LIST(), }; From f5ca2d6233c88464bcf2616bc8697247cb57bcc5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:23:44 +0000 Subject: [PATCH 0329/2892] hw/core: Constify all Property 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 --- hw/core/generic-loader.c | 2 +- hw/core/guest-loader.c | 2 +- hw/core/or-irq.c | 2 +- hw/core/platform-bus.c | 2 +- hw/core/split-irq.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index ea8628b892..c1cddecf60 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -179,7 +179,7 @@ static void generic_loader_unrealize(DeviceState *dev) qemu_unregister_reset(generic_loader_reset, dev); } -static Property generic_loader_props[] = { +static const Property generic_loader_props[] = { DEFINE_PROP_UINT64("addr", GenericLoaderState, addr, 0), DEFINE_PROP_UINT64("data", GenericLoaderState, data, 0), DEFINE_PROP_UINT8("data-len", GenericLoaderState, data_len, 0), diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c index 391c875a29..74af00cee7 100644 --- a/hw/core/guest-loader.c +++ b/hw/core/guest-loader.c @@ -111,7 +111,7 @@ static void guest_loader_realize(DeviceState *dev, Error **errp) loader_insert_platform_data(s, size, errp); } -static Property guest_loader_props[] = { +static const Property guest_loader_props[] = { DEFINE_PROP_UINT64("addr", GuestLoaderState, addr, 0), DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel), DEFINE_PROP_STRING("bootargs", GuestLoaderState, args), diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index b25468e38a..fc52796f54 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -115,7 +115,7 @@ static const VMStateDescription vmstate_or_irq = { }, }; -static Property or_irq_properties[] = { +static const Property or_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index dc58bf505a..a29c9c6e59 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -204,7 +204,7 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) plaform_bus_refresh_irqs(pbus); } -static Property platform_bus_properties[] = { +static const Property platform_bus_properties[] = { DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), DEFINE_PROP_END_OF_LIST() diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c index 3b90af2e8f..40fc7e2e77 100644 --- a/hw/core/split-irq.c +++ b/hw/core/split-irq.c @@ -59,7 +59,7 @@ static void split_irq_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->out_irq, s->num_lines); } -static Property split_irq_properties[] = { +static const Property split_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1), DEFINE_PROP_END_OF_LIST(), }; From 804467e148a254a503834a2c712cad827f6f4b25 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:25:18 +0000 Subject: [PATCH 0330/2892] hw/cpu: Constify all Property 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 --- hw/cpu/a15mpcore.c | 2 +- hw/cpu/a9mpcore.c | 2 +- hw/cpu/arm11mpcore.c | 2 +- hw/cpu/cluster.c | 2 +- hw/cpu/realview_mpcore.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 967d8d3dd5..5346b8b6c6 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -144,7 +144,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) } } -static Property a15mp_priv_properties[] = { +static const Property a15mp_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1), /* The Cortex-A15MP may have anything from 0 to 224 external interrupt * IRQ lines (with another 32 internal). We default to 128+32, which diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index c30ef72c66..c3fdfb92e1 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -158,7 +158,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) } } -static Property a9mp_priv_properties[] = { +static const Property a9mp_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1), /* The Cortex-A9MP may have anything from 0 to 224 external interrupt * IRQ lines (with another 32 internal). We default to 64+32, which diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index 89c4e35143..193fc182ab 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -131,7 +131,7 @@ static void mpcore_priv_initfn(Object *obj) object_initialize_child(obj, "wdtimer", &s->wdtimer, TYPE_ARM_MPTIMER); } -static Property mpcore_priv_properties[] = { +static const Property mpcore_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1), /* The ARM11 MPCORE TRM says the on-chip controller may have * anything from 0 to 224 external interrupt IRQ lines (with another diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c index 61289a840d..8e43621b5c 100644 --- a/hw/cpu/cluster.c +++ b/hw/cpu/cluster.c @@ -25,7 +25,7 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" -static Property cpu_cluster_properties[] = { +static const Property cpu_cluster_properties[] = { DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 72c792eef1..9a0ff1df86 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -108,7 +108,7 @@ static void mpcore_rirq_init(Object *obj) } } -static Property mpcore_rirq_properties[] = { +static const Property mpcore_rirq_properties[] = { DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; From 2a3f565c529088df051f3cf1d34f6389626af261 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:27:21 +0000 Subject: [PATCH 0331/2892] hw/cxl: Constify all Property 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 --- hw/cxl/switch-mailbox-cci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c index 4f419443ab..3fde0f8aae 100644 --- a/hw/cxl/switch-mailbox-cci.c +++ b/hw/cxl/switch-mailbox-cci.c @@ -65,7 +65,7 @@ static void cswmbcci_exit(PCIDevice *pci_dev) /* Nothing to do here yet */ } -static Property cxl_switch_cci_props[] = { +static const Property cxl_switch_cci_props[] = { DEFINE_PROP_LINK("target", CSWMBCCIDev, target, TYPE_CXL_USP, PCIDevice *), DEFINE_PROP_END_OF_LIST(), From d432edd56c270d3b7322d85489bd45116bd7e2e2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:31:19 +0000 Subject: [PATCH 0332/2892] hw/display: Constify all Property 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 --- hw/display/artist.c | 2 +- hw/display/ati.c | 2 +- hw/display/bcm2835_fb.c | 2 +- hw/display/bochs-display.c | 2 +- hw/display/cg3.c | 2 +- hw/display/cirrus_vga.c | 2 +- hw/display/cirrus_vga_isa.c | 2 +- hw/display/exynos4210_fimd.c | 2 +- hw/display/g364fb.c | 2 +- hw/display/i2c-ddc.c | 2 +- hw/display/macfb.c | 4 ++-- hw/display/pl110.c | 2 +- hw/display/qxl.c | 2 +- hw/display/ramfb-standalone.c | 2 +- hw/display/sm501.c | 4 ++-- hw/display/tcx.c | 2 +- hw/display/vga-isa.c | 2 +- hw/display/vga-mmio.c | 2 +- hw/display/vga-pci.c | 4 ++-- hw/display/vhost-user-gpu.c | 2 +- hw/display/virtio-gpu-gl.c | 2 +- hw/display/virtio-gpu-pci.c | 2 +- hw/display/virtio-gpu-rutabaga.c | 2 +- hw/display/virtio-gpu.c | 2 +- hw/display/virtio-vga.c | 2 +- hw/display/vmware_vga.c | 2 +- hw/display/xlnx_dp.c | 2 +- 27 files changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/display/artist.c b/hw/display/artist.c index 5790b7a64e..49deed328d 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -1474,7 +1474,7 @@ static const VMStateDescription vmstate_artist = { } }; -static Property artist_properties[] = { +static const Property artist_properties[] = { DEFINE_PROP_UINT16("width", ARTISTState, width, 1280), DEFINE_PROP_UINT16("height", ARTISTState, height, 1024), DEFINE_PROP_UINT16("depth", ARTISTState, depth, 8), diff --git a/hw/display/ati.c b/hw/display/ati.c index 593a25328d..e24e092bbc 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1039,7 +1039,7 @@ static void ati_vga_exit(PCIDevice *dev) graphic_console_close(s->vga.con); } -static Property ati_vga_properties[] = { +static const Property ati_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", ATIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_STRING("model", ATIVGAState, model), DEFINE_PROP_UINT16("x-device-id", ATIVGAState, dev_id, diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 7005d5bfea..2539fcc8ab 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -429,7 +429,7 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp) qemu_console_resize(s->con, s->config.xres, s->config.yres); } -static Property bcm2835_fb_props[] = { +static const Property bcm2835_fb_props[] = { DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, DEFAULT_VCRAM_SIZE), diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 3b1d922b6e..9a3263aa01 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -345,7 +345,7 @@ static void bochs_display_exit(PCIDevice *dev) graphic_console_close(s->con); } -static Property bochs_display_properties[] = { +static const Property bochs_display_properties[] = { DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true), DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 95f8f98b99..75b3312c24 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -361,7 +361,7 @@ static void cg3_reset(DeviceState *d) qemu_irq_lower(s->irq); } -static Property cg3_properties[] = { +static const Property cg3_properties[] = { DEFINE_PROP_UINT32("vram-size", CG3State, vram_size, -1), DEFINE_PROP_UINT16("width", CG3State, width, -1), DEFINE_PROP_UINT16("height", CG3State, height, -1), diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 150883a971..198ed9ed9b 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2982,7 +2982,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) } } -static Property pci_vga_cirrus_properties[] = { +static const Property pci_vga_cirrus_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index 84be51670e..d0d134470f 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -69,7 +69,7 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) /* FIXME not qdev yet */ } -static Property isa_cirrus_vga_properties[] = { +static const Property isa_cirrus_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index f3d82498bf..4f097a172c 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1925,7 +1925,7 @@ static const GraphicHwOps exynos4210_fimd_ops = { .gfx_update = exynos4210_fimd_update, }; -static Property exynos4210_fimd_properties[] = { +static const Property exynos4210_fimd_properties[] = { DEFINE_PROP_LINK("framebuffer-memory", Exynos4210fimdState, fbmem, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index fa2f184908..a7533c6908 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -512,7 +512,7 @@ static void g364fb_sysbus_reset(DeviceState *d) g364fb_reset(&s->g364); } -static Property g364fb_sysbus_properties[] = { +static const Property g364fb_sysbus_properties[] = { DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, 8 * MiB), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/i2c-ddc.c b/hw/display/i2c-ddc.c index 465b00355e..a2d1f2b044 100644 --- a/hw/display/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -95,7 +95,7 @@ static const VMStateDescription vmstate_i2c_ddc = { } }; -static Property i2c_ddc_properties[] = { +static const Property i2c_ddc_properties[] = { DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/macfb.c b/hw/display/macfb.c index a5b4a499f3..977901bfdd 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -758,7 +758,7 @@ static void macfb_nubus_reset(DeviceState *d) macfb_reset(&s->macfb); } -static Property macfb_sysbus_properties[] = { +static const Property macfb_sysbus_properties[] = { DEFINE_PROP_UINT32("width", MacfbSysBusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbSysBusState, macfb.height, 480), DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8), @@ -777,7 +777,7 @@ static const VMStateDescription vmstate_macfb_sysbus = { } }; -static Property macfb_nubus_properties[] = { +static const Property macfb_nubus_properties[] = { DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480), DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8), diff --git a/hw/display/pl110.c b/hw/display/pl110.c index 7f145bbdba..eca00b4279 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -535,7 +535,7 @@ static const GraphicHwOps pl110_gfx_ops = { .gfx_update = pl110_update_display, }; -static Property pl110_properties[] = { +static const Property pl110_properties[] = { DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 0c4b1c9bf2..949949d374 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2458,7 +2458,7 @@ static const VMStateDescription qxl_vmstate = { } }; -static Property qxl_properties[] = { +static const Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * MiB), DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * MiB), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 20eab34ff4..e677f44be6 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -60,7 +60,7 @@ static const VMStateDescription ramfb_dev_vmstate = { } }; -static Property ramfb_properties[] = { +static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 38d005c168..446b648f1a 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -2054,7 +2054,7 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) /* TODO : chain irq to IRL */ } -static Property sm501_sysbus_properties[] = { +static const Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), /* this a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, DEFAULT_X_PIXMAN), @@ -2143,7 +2143,7 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) &s->state.mmio_region); } -static Property sm501_pci_properties[] = { +static const Property sm501_pci_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/display/tcx.c b/hw/display/tcx.c index f000288fcd..3eb0a91ff9 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -879,7 +879,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) qemu_console_resize(s->con, s->width, s->height); } -static Property tcx_properties[] = { +static const Property tcx_properties[] = { DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), DEFINE_PROP_UINT16("width", TCXState, width, -1), DEFINE_PROP_UINT16("height", TCXState, height, -1), diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index c025632635..a6cbf77103 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -88,7 +88,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) rom_add_vga(VGABIOS_FILENAME); } -static Property vga_isa_properties[] = { +static const Property vga_isa_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c index be33204517..b759efdde7 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -111,7 +111,7 @@ static void vga_mmio_realizefn(DeviceState *dev, Error **errp) s->vga.con = graphic_console_init(dev, 0, s->vga.hw_ops, &s->vga); } -static Property vga_mmio_properties[] = { +static const Property vga_mmio_properties[] = { DEFINE_PROP_UINT8("it_shift", VGAMmioState, it_shift, 0), DEFINE_PROP_UINT32("vgamem_mb", VGAMmioState, vga.vram_size_mb, 8), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 6b51019966..3145c448f5 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -330,7 +330,7 @@ static void pci_secondary_vga_reset(DeviceState *dev) vga_common_reset(&d->vga); } -static Property vga_pci_properties[] = { +static const Property vga_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), DEFINE_PROP_BIT("qemu-extended-regs", @@ -342,7 +342,7 @@ static Property vga_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static Property secondary_pci_properties[] = { +static const Property secondary_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 14548f1a57..a36eddcb12 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -645,7 +645,7 @@ static struct vhost_dev *vhost_user_gpu_get_vhost(VirtIODevice *vdev) return g->vhost ? &g->vhost->dev : NULL; } -static Property vhost_user_gpu_properties[] = { +static const Property vhost_user_gpu_properties[] = { VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index 7c0e448b46..6f31149e1e 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -154,7 +154,7 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) virtio_gpu_device_realize(qdev, errp); } -static Property virtio_gpu_gl_properties[] = { +static const Property virtio_gpu_gl_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), DEFINE_PROP_BIT("venus", VirtIOGPU, parent_obj.conf.flags, diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index da6a99f038..89d27c9d85 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -21,7 +21,7 @@ #include "hw/virtio/virtio-gpu-pci.h" #include "qom/object.h" -static Property virtio_gpu_pci_base_properties[] = { +static const Property virtio_gpu_pci_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index 17bf701a21..f6486acdda 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -1096,7 +1096,7 @@ static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) virtio_gpu_device_realize(qdev, errp); } -static Property virtio_gpu_rutabaga_properties[] = { +static const Property virtio_gpu_rutabaga_properties[] = { DEFINE_PROP_BIT64("gfxstream-vulkan", VirtIOGPURutabaga, capset_mask, RUTABAGA_CAPSET_GFXSTREAM_VULKAN, false), DEFINE_PROP_BIT64("cross-domain", VirtIOGPURutabaga, capset_mask, diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 7d22d03bbf..82741d19e5 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1674,7 +1674,7 @@ static const VMStateDescription vmstate_virtio_gpu = { .post_load = virtio_gpu_post_load, }; -static Property virtio_gpu_properties[] = { +static const Property virtio_gpu_properties[] = { VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf), DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem, 256 * MiB), diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 276f315108..532e4c62d5 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -209,7 +209,7 @@ static void virtio_vga_set_big_endian_fb(Object *obj, bool value, Error **errp) d->vga.big_endian_fb = value; } -static Property virtio_vga_base_properties[] = { +static const Property virtio_vga_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index f2d72c3fc7..f49bbf393a 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1332,7 +1332,7 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) &s->chip.fifo_ram); } -static Property vga_vmware_properties[] = { +static const Property vga_vmware_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s, chip.vga.vram_size_mb, 16), DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s, diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 6ab2335499..7838f28bca 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1387,7 +1387,7 @@ static void xlnx_dp_reset(DeviceState *dev) xlnx_dp_update_irq(s); } -static Property xlnx_dp_device_properties[] = { +static const Property xlnx_dp_device_properties[] = { DEFINE_AUDIO_PROPERTIES(XlnxDPState, aud_card), DEFINE_PROP_END_OF_LIST(), }; From 7d6a82a3e64d37a83d4ac2fc5234420046523b77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:33:04 +0000 Subject: [PATCH 0333/2892] hw/dma: Constify all Property 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 --- hw/dma/i82374.c | 2 +- hw/dma/i8257.c | 2 +- hw/dma/pl080.c | 2 +- hw/dma/pl330.c | 2 +- hw/dma/xilinx_axidma.c | 2 +- hw/dma/xlnx-zdma.c | 2 +- hw/dma/xlnx_csu_dma.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index e72aa2e1ce..032afedde2 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -139,7 +139,7 @@ static void i82374_realize(DeviceState *dev, Error **errp) memset(s->commands, 0, sizeof(s->commands)); } -static Property i82374_properties[] = { +static const Property i82374_properties[] = { DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 3e6700e53b..8b04177393 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -585,7 +585,7 @@ static void i8257_realize(DeviceState *dev, Error **errp) d->dma_bh = qemu_bh_new(i8257_dma_run, d); } -static Property i8257_properties[] = { +static const Property i8257_properties[] = { DEFINE_PROP_INT32("base", I8257State, base, 0x00), DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80), DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480), diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 8e76f88a69..3f392822ed 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -408,7 +408,7 @@ static void pl081_init(Object *obj) s->nchannels = 2; } -static Property pl080_properties[] = { +static const Property pl080_properties[] = { DEFINE_PROP_LINK("downstream", PL080State, downstream, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 0668caed7c..d5a0a1caa2 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -1646,7 +1646,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) pl330_fifo_init(&s->fifo, s->data_width / 4 * s->data_buffer_dep); } -static Property pl330_properties[] = { +static const Property pl330_properties[] = { /* CR0 */ DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8), DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4), diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 73a480bfbf..f09452d0b5 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -611,7 +611,7 @@ static void xilinx_axidma_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property axidma_properties[] = { +static const Property axidma_properties[] = { DEFINE_PROP_UINT32("freqhz", XilinxAXIDMA, freqhz, 50000000), DEFINE_PROP_LINK("axistream-connected", XilinxAXIDMA, tx_data_dev, TYPE_STREAM_SINK, StreamSink *), diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 46f50631ff..1a63d5f3b2 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -810,7 +810,7 @@ static const VMStateDescription vmstate_zdma = { } }; -static Property zdma_props[] = { +static const Property zdma_props[] = { DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), DEFINE_PROP_LINK("dma", XlnxZDMA, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 43738c4350..d78dc6444b 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -691,7 +691,7 @@ static const VMStateDescription vmstate_xlnx_csu_dma = { } }; -static Property xlnx_csu_dma_properties[] = { +static const Property xlnx_csu_dma_properties[] = { /* * Ref PG021, Stream Data Width: * Data width in bits of the AXI S2MM AXI4-Stream Data bus. From de531a6bd46e5a46b4e7a5f7ec66abb4f82e6207 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:34:35 +0000 Subject: [PATCH 0334/2892] hw/gpio: Constify all Property 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 --- hw/gpio/imx_gpio.c | 2 +- hw/gpio/npcm7xx_gpio.c | 2 +- hw/gpio/omap_gpio.c | 2 +- hw/gpio/pca9552.c | 2 +- hw/gpio/pca9554.c | 2 +- hw/gpio/pl061.c | 2 +- hw/gpio/sifive_gpio.c | 2 +- hw/gpio/stm32l4x5_gpio.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index 27535a577f..919d53701f 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -290,7 +290,7 @@ static const VMStateDescription vmstate_imx_gpio = { } }; -static Property imx_gpio_properties[] = { +static const Property imx_gpio_properties[] = { DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true), DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq, false), diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index ba19b9ebad..db6792b2ad 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -386,7 +386,7 @@ static const VMStateDescription vmstate_npcm7xx_gpio = { }, }; -static Property npcm7xx_gpio_properties[] = { +static const Property npcm7xx_gpio_properties[] = { /* Bit n set => pin n has pullup enabled by default. */ DEFINE_PROP_UINT32("reset-pullup", NPCM7xxGPIOState, reset_pu, 0), /* Bit n set => pin n has pulldown enabled by default. */ diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index a47a2167a6..03ee9e47c6 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -225,7 +225,7 @@ void omap_gpio_set_clk(Omap1GpioState *gpio, omap_clk clk) gpio->clk = clk; } -static Property omap_gpio_properties[] = { +static const Property omap_gpio_properties[] = { DEFINE_PROP_INT32("mpu_model", Omap1GpioState, mpu_model, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 59b233339a..427419d218 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -428,7 +428,7 @@ static void pca955x_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count); } -static Property pca955x_properties[] = { +static const Property pca955x_properties[] = { DEFINE_PROP_STRING("description", PCA955xState, description), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index 68cc9e1de4..e8b0458aac 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -291,7 +291,7 @@ static void pca9554_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT); } -static Property pca9554_properties[] = { +static const Property pca9554_properties[] = { DEFINE_PROP_STRING("description", PCA9554State, description), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index d5838b8e98..9b8ca6de32 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -562,7 +562,7 @@ static void pl061_realize(DeviceState *dev, Error **errp) } } -static Property pl061_props[] = { +static const Property pl061_props[] = { DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff), DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0), DEFINE_PROP_END_OF_LIST() diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c index e85c0406a2..5603f0c235 100644 --- a/hw/gpio/sifive_gpio.c +++ b/hw/gpio/sifive_gpio.c @@ -349,7 +349,7 @@ static const VMStateDescription vmstate_sifive_gpio = { } }; -static Property sifive_gpio_properties[] = { +static const Property sifive_gpio_properties[] = { DEFINE_PROP_UINT32("ngpio", SIFIVEGPIOState, ngpio, SIFIVE_GPIO_PINS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c index 30d8d6cba4..d1394f3f55 100644 --- a/hw/gpio/stm32l4x5_gpio.c +++ b/hw/gpio/stm32l4x5_gpio.c @@ -447,7 +447,7 @@ static const VMStateDescription vmstate_stm32l4x5_gpio = { } }; -static Property stm32l4x5_gpio_properties[] = { +static const Property stm32l4x5_gpio_properties[] = { DEFINE_PROP_STRING("name", Stm32l4x5GpioState, name), DEFINE_PROP_UINT32("mode-reset", Stm32l4x5GpioState, moder_reset, 0), DEFINE_PROP_UINT32("ospeed-reset", Stm32l4x5GpioState, ospeedr_reset, 0), From e923f5e1b853ac80e96b416143300af9d189af8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:35:45 +0000 Subject: [PATCH 0335/2892] hw/hyperv: Constify all Property 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 --- hw/hyperv/hv-balloon.c | 2 +- hw/hyperv/syndbg.c | 2 +- hw/hyperv/vmbus.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 3a9ef07691..74897b1604 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -1733,7 +1733,7 @@ static void hv_balloon_finalize(Object *obj) hv_balloon_unrealize_finalize_common(balloon); } -static Property hv_balloon_properties[] = { +static const Property hv_balloon_properties[] = { DEFINE_PROP_BOOL("status-report", HvBalloon, status_report.enabled, false), diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index 065e12fb1e..0193addd42 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -366,7 +366,7 @@ static const VMStateDescription vmstate_hv_syndbg = { .unmigratable = 1, }; -static Property hv_syndbg_properties[] = { +static const Property hv_syndbg_properties[] = { DEFINE_PROP_STRING("host_ip", HvSynDbg, host_ip), DEFINE_PROP_UINT16("host_port", HvSynDbg, host_port, 50000), DEFINE_PROP_BOOL("use_hcalls", HvSynDbg, use_hcalls, false), diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index b36bd3d67d..3d1f4d14e8 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2346,7 +2346,7 @@ static void vmbus_dev_unrealize(DeviceState *dev) free_channels(vdev); } -static Property vmbus_dev_props[] = { +static const Property vmbus_dev_props[] = { DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid), DEFINE_PROP_END_OF_LIST() }; @@ -2653,7 +2653,7 @@ static const VMStateDescription vmstate_vmbus_bridge = { }, }; -static Property vmbus_bridge_props[] = { +static const Property vmbus_bridge_props[] = { DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7), DEFINE_PROP_END_OF_LIST() }; From 6f31905dbe94e89b1c8471ac6d252e9c0f89d2f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:36:52 +0000 Subject: [PATCH 0336/2892] hw/i2c: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Corey Minyard Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/i2c/aspeed_i2c.c | 4 ++-- hw/i2c/core.c | 2 +- hw/i2c/i2c_mux_pca954x.c | 2 +- hw/i2c/omap_i2c.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 3ae22cb052..2ea68c3090 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -1258,7 +1258,7 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) } } -static Property aspeed_i2c_properties[] = { +static const Property aspeed_i2c_properties[] = { DEFINE_PROP_LINK("dram", AspeedI2CState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), @@ -1446,7 +1446,7 @@ static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr_pool); } -static Property aspeed_i2c_bus_properties[] = { +static const Property aspeed_i2c_bus_properties[] = { DEFINE_PROP_UINT8("bus-id", AspeedI2CBus, id, 0), DEFINE_PROP_LINK("controller", AspeedI2CBus, controller, TYPE_ASPEED_I2C, AspeedI2CState *), diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 4cf30b2c86..4118d3db50 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -18,7 +18,7 @@ #define I2C_BROADCAST 0x00 -static Property i2c_props[] = { +static const Property i2c_props[] = { DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index db5db956a6..80c570fd10 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -211,7 +211,7 @@ static void pca954x_init(Object *obj) } } -static Property pca954x_props[] = { +static const Property pca954x_props[] = { DEFINE_PROP_STRING("name", Pca954xState, name), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index e78505ebdd..172df135f5 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -511,7 +511,7 @@ void omap_i2c_set_fclk(OMAPI2CState *i2c, omap_clk clk) i2c->fclk = clk; } -static Property omap_i2c_properties[] = { +static const Property omap_i2c_properties[] = { DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), DEFINE_PROP_END_OF_LIST(), }; From 90d45638af23c3cdab9d357306026751f8431b83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:39:58 +0000 Subject: [PATCH 0337/2892] hw/i386: Constify all Property 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 --- hw/i386/amd_iommu.c | 2 +- hw/i386/intel_iommu.c | 2 +- hw/i386/kvm/clock.c | 2 +- hw/i386/kvm/i8254.c | 2 +- hw/i386/kvm/ioapic.c | 2 +- hw/i386/sgx-epc.c | 2 +- hw/i386/vmmouse.c | 2 +- hw/i386/vmport.c | 2 +- hw/i386/x86-iommu.c | 2 +- hw/i386/xen/xen_pvdevice.c | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index af0f4da1f6..ca3e62a244 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1668,7 +1668,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) amdvi_init(s); } -static Property amdvi_properties[] = { +static const Property amdvi_properties[] = { DEFINE_PROP_BOOL("xtsup", AMDVIState, xtsup, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 4c0d1d7d47..a5b268342f 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3404,7 +3404,7 @@ static const MemoryRegionOps vtd_mem_ops = { }, }; -static Property vtd_properties[] = { +static const Property vtd_properties[] = { DEFINE_PROP_UINT32("version", IntelIOMMUState, version, 0), DEFINE_PROP_ON_OFF_AUTO("eim", IntelIOMMUState, intr_eim, ON_OFF_AUTO_AUTO), diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 40aa9a32c3..71150ed2e0 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -305,7 +305,7 @@ static const VMStateDescription kvmclock_vmsd = { } }; -static Property kvmclock_properties[] = { +static const Property kvmclock_properties[] = { DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState, mach_use_reliable_get_clock, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index baa4b39582..2933d3f458 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -287,7 +287,7 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) kpc->parent_realize(dev, errp); } -static Property kvm_pit_properties[] = { +static const Property kvm_pit_properties[] = { DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, lost_tick_policy, LOST_TICK_POLICY_DELAY), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 2907b08164..217ff43b98 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -133,7 +133,7 @@ static void kvm_ioapic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, kvm_ioapic_set_irq, IOAPIC_NUM_PINS); } -static Property kvm_ioapic_properties[] = { +static const Property kvm_ioapic_properties[] = { DEFINE_PROP_UINT32("gsi_base", KVMIOAPICState, kvm_gsi_base, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index d664829d35..c232e825e0 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -19,7 +19,7 @@ #include "target/i386/cpu.h" #include "exec/address-spaces.h" -static Property sgx_epc_properties[] = { +static const Property sgx_epc_properties[] = { DEFINE_PROP_UINT64(SGX_EPC_ADDR_PROP, SGXEPCDevice, addr, 0), DEFINE_PROP_UINT32(SGX_EPC_NUMA_NODE_PROP, SGXEPCDevice, node, 0), DEFINE_PROP_LINK(SGX_EPC_MEMDEV_PROP, SGXEPCDevice, hostmem, diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index 76130cd46d..da9c35c1ec 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -317,7 +317,7 @@ static void vmmouse_realizefn(DeviceState *dev, Error **errp) vmport_register(VMPORT_CMD_VMMOUSE_DATA, vmmouse_ioport_read, s); } -static Property vmmouse_properties[] = { +static const Property vmmouse_properties[] = { DEFINE_PROP_LINK("i8042", VMMouseState, i8042, TYPE_I8042, ISAKBDState *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c index 7cc75dbc6d..cab6e72089 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -252,7 +252,7 @@ static void vmport_realizefn(DeviceState *dev, Error **errp) } } -static Property vmport_properties[] = { +static const Property vmport_properties[] = { /* Used to enforce compatibility for migration */ DEFINE_PROP_BIT("x-read-set-eax", VMPortState, compat_flags, VMPORT_COMPAT_READ_SET_EAX_BIT, true), diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 60af896225..155f6262ea 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -125,7 +125,7 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) } } -static Property x86_iommu_properties[] = { +static const Property x86_iommu_properties[] = { DEFINE_PROP_ON_OFF_AUTO("intremap", X86IOMMUState, intr_supported, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false), diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index ed621531d8..e71483e6e3 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -115,7 +115,7 @@ static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) &d->mmio); } -static Property xen_pv_props[] = { +static const Property xen_pv_props[] = { DEFINE_PROP_UINT16("vendor-id", XenPVDevice, vendor_id, PCI_VENDOR_ID_XEN), DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, 0xffff), DEFINE_PROP_UINT8("revision", XenPVDevice, revision, 0x01), From aaa1f1a5246c818a39d15c286bb8ace377501d00 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:41:52 +0000 Subject: [PATCH 0338/2892] hw/ide: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/ide/cf.c | 2 +- hw/ide/cmd646.c | 2 +- hw/ide/ide-dev.c | 6 +++--- hw/ide/isa.c | 2 +- hw/ide/macio.c | 2 +- hw/ide/mmio.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/ide/cf.c b/hw/ide/cf.c index 2a425cb0f2..190914f513 100644 --- a/hw/ide/cf.c +++ b/hw/ide/cf.c @@ -24,7 +24,7 @@ static void ide_cf_realize(IDEDevice *dev, Error **errp) ide_dev_initfn(dev, IDE_CFATA, errp); } -static Property ide_cf_properties[] = { +static const Property ide_cf_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 6b02fc81ec..942f6c470c 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -313,7 +313,7 @@ static void pci_cmd646_ide_exitfn(PCIDevice *dev) } } -static Property cmd646_ide_properties[] = { +static const Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ide/ide-dev.c b/hw/ide/ide-dev.c index 03f7967798..cc92531f1c 100644 --- a/hw/ide/ide-dev.c +++ b/hw/ide/ide-dev.c @@ -29,7 +29,7 @@ #include "qapi/visitor.h" #include "ide-internal.h" -static Property ide_props[] = { +static const Property ide_props[] = { DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), DEFINE_PROP_BOOL("win2k-install-hack", IDEDevice, win2k_install_hack, false), DEFINE_PROP_END_OF_LIST(), @@ -191,7 +191,7 @@ static void ide_cd_realize(IDEDevice *dev, Error **errp) ide_dev_initfn(dev, IDE_CD, errp); } -static Property ide_hd_properties[] = { +static const Property ide_hd_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", @@ -218,7 +218,7 @@ static const TypeInfo ide_hd_info = { .class_init = ide_hd_class_init, }; -static Property ide_cd_properties[] = { +static const Property ide_cd_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 211ebc9ba7..a0a7e4837c 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -101,7 +101,7 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, return isadev; } -static Property isa_ide_properties[] = { +static const Property isa_ide_properties[] = { DEFINE_PROP_UINT32("iobase", ISAIDEState, iobase, 0x1f0), DEFINE_PROP_UINT32("iobase2", ISAIDEState, iobase2, 0x3f6), DEFINE_PROP_UINT32("irq", ISAIDEState, irqnum, 14), diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 99477a3d13..25f8403e80 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -459,7 +459,7 @@ static void macio_ide_initfn(Object *obj) qdev_prop_allow_set_link_before_realize, 0); } -static Property macio_ide_properties[] = { +static const Property macio_ide_properties[] = { DEFINE_PROP_UINT32("channel", MACIOIDEState, channel, 0), DEFINE_PROP_UINT32("addr", MACIOIDEState, addr, -1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 53d22fb37f..43ab66f347 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -141,7 +141,7 @@ static void mmio_ide_initfn(Object *obj) sysbus_init_irq(d, &s->irq); } -static Property mmio_ide_properties[] = { +static const Property mmio_ide_properties[] = { DEFINE_PROP_UINT32("shift", MMIOIDEState, shift, 0), DEFINE_PROP_END_OF_LIST() }; From 9eb5c799072c521839650fc375e9d5b546296370 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:43:23 +0000 Subject: [PATCH 0339/2892] hw/input: Constify all Property 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 --- hw/input/pckbd.c | 4 ++-- hw/input/stellaris_gamepad.c | 2 +- hw/input/virtio-input-hid.c | 6 +++--- hw/input/virtio-input-host.c | 2 +- hw/input/virtio-input.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 04c1b3cbf9..24a133fd25 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -735,7 +735,7 @@ static void i8042_mmio_init(Object *obj) "ps2-mouse-input-irq", 1); } -static Property i8042_mmio_properties[] = { +static const Property i8042_mmio_properties[] = { DEFINE_PROP_UINT64("mask", MMIOKBDState, kbd.mask, UINT64_MAX), DEFINE_PROP_UINT32("size", MMIOKBDState, size, -1), DEFINE_PROP_END_OF_LIST(), @@ -933,7 +933,7 @@ static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, mou); } -static Property i8042_properties[] = { +static const Property i8042_properties[] = { DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true), DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false), DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1), diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index 17ee42b9fc..b1cc693189 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -77,7 +77,7 @@ static void stellaris_gamepad_reset_enter(Object *obj, ResetType type) memset(s->pressed, 0, s->num_buttons * sizeof(uint8_t)); } -static Property stellaris_gamepad_properties[] = { +static const Property stellaris_gamepad_properties[] = { DEFINE_PROP_ARRAY("keycodes", StellarisGamepad, num_buttons, keycodes, qdev_prop_uint32, uint32_t), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index 45e4d4c75d..7396385508 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -237,7 +237,7 @@ static void virtio_input_hid_handle_status(VirtIOInput *vinput, } } -static Property virtio_input_hid_properties[] = { +static const Property virtio_input_hid_properties[] = { DEFINE_PROP_STRING("display", VirtIOInputHID, display), DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), DEFINE_PROP_END_OF_LIST(), @@ -380,7 +380,7 @@ static struct virtio_input_config virtio_mouse_config_v2[] = { { /* end of list */ }, }; -static Property virtio_mouse_properties[] = { +static const Property virtio_mouse_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), DEFINE_PROP_END_OF_LIST(), }; @@ -505,7 +505,7 @@ static struct virtio_input_config virtio_tablet_config_v2[] = { { /* end of list */ }, }; -static Property virtio_tablet_properties[] = { +static const Property virtio_tablet_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index fea7139382..2be2c633ab 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -221,7 +221,7 @@ static const VMStateDescription vmstate_virtio_input_host = { .unmigratable = 1, }; -static Property virtio_input_host_properties[] = { +static const Property virtio_input_host_properties[] = { DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index 3bcdae41b2..edcd94dedb 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -300,7 +300,7 @@ static const VMStateDescription vmstate_virtio_input = { .post_load = virtio_input_post_load, }; -static Property virtio_input_properties[] = { +static const Property virtio_input_properties[] = { DEFINE_PROP_STRING("serial", VirtIOInput, serial), DEFINE_PROP_END_OF_LIST(), }; From 783e3b21e542d7720ccd5f9f0ac5fad0b8ef8b7a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:48:50 +0000 Subject: [PATCH 0340/2892] hw/intc: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/intc/apic_common.c | 2 +- hw/intc/arm_gic_common.c | 2 +- hw/intc/arm_gicv2m.c | 2 +- hw/intc/arm_gicv3_common.c | 2 +- hw/intc/arm_gicv3_its.c | 2 +- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/armv7m_nvic.c | 2 +- hw/intc/exynos4210_combiner.c | 2 +- hw/intc/exynos4210_gic.c | 2 +- hw/intc/goldfish_pic.c | 2 +- hw/intc/grlib_irqmp.c | 2 +- hw/intc/i8259_common.c | 2 +- hw/intc/ioapic.c | 2 +- hw/intc/loongarch_extioi.c | 2 +- hw/intc/loongarch_pch_msi.c | 2 +- hw/intc/loongarch_pch_pic.c | 2 +- hw/intc/loongson_ipi_common.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/intc/mips_gic.c | 2 +- hw/intc/omap_intc.c | 2 +- hw/intc/ompic.c | 2 +- hw/intc/openpic.c | 2 +- hw/intc/openpic_kvm.c | 2 +- hw/intc/pnv_xive.c | 2 +- hw/intc/pnv_xive2.c | 2 +- hw/intc/ppc-uic.c | 2 +- hw/intc/riscv_aclint.c | 4 ++-- hw/intc/riscv_aplic.c | 2 +- hw/intc/riscv_imsic.c | 2 +- hw/intc/rx_icu.c | 2 +- hw/intc/s390_flic.c | 4 ++-- hw/intc/sifive_plic.c | 2 +- hw/intc/spapr_xive.c | 2 +- hw/intc/xics.c | 4 ++-- hw/intc/xilinx_intc.c | 2 +- hw/intc/xive.c | 8 ++++---- hw/intc/xive2.c | 4 ++-- hw/intc/xlnx-pmu-iomod-intc.c | 2 +- 38 files changed, 45 insertions(+), 45 deletions(-) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 62f3bbf203..8be9f22de8 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -408,7 +408,7 @@ static const VMStateDescription vmstate_apic_common = { } }; -static Property apic_properties_common[] = { +static const Property apic_properties_common[] = { DEFINE_PROP_UINT8("version", APICCommonState, version, 0x14), DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, true), diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 53fb2c4e2d..e961cd9156 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -348,7 +348,7 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, } } -static Property arm_gic_common_properties[] = { +static const Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), /* Revision can be 1 or 2 for GIC architecture specification diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index d564b857eb..ffa830b433 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -170,7 +170,7 @@ static void gicv2m_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property gicv2m_properties[] = { +static const Property gicv2m_properties[] = { DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0), DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index bd50a1b079..a8ec615a3f 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -605,7 +605,7 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, } } -static Property arm_gicv3_common_properties[] = { +static const Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index bf31158470..f50b1814ea 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -2002,7 +2002,7 @@ static void gicv3_its_post_load(GICv3ITSState *s) } } -static Property gicv3_its_props[] = { +static const Property gicv3_its_props[] = { DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3", GICv3State *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 35539c099f..68a6144add 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -234,7 +234,7 @@ static void kvm_arm_its_reset_hold(Object *obj, ResetType type) } } -static Property kvm_arm_its_props[] = { +static const Property kvm_arm_its_props[] = { DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "kvm-arm-gicv3", GICv3State *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 98f3cf59bc..6e2803b123 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2569,7 +2569,7 @@ static const VMStateDescription vmstate_nvic = { } }; -static Property props_nvic[] = { +static const Property props_nvic[] = { /* Number of external IRQ lines (so excluding the 16 internal exceptions) */ DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), /* diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index afecef1e15..221dfa912a 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -325,7 +325,7 @@ static void exynos4210_combiner_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property exynos4210_combiner_properties[] = { +static const Property exynos4210_combiner_properties[] = { DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index fcca85c6c6..e1b956d990 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -111,7 +111,7 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->dist_container); } -static Property exynos4210_gic_properties[] = { +static const Property exynos4210_gic_properties[] = { DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index 166a3cba1e..f5343c9d2f 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -181,7 +181,7 @@ static void goldfish_pic_instance_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), goldfish_irq_request, GOLDFISH_PIC_IRQ_NB); } -static Property goldfish_pic_properties[] = { +static const Property goldfish_pic_properties[] = { DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index 37ac63fd80..bf53251ea2 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -376,7 +376,7 @@ static void grlib_irqmp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &irqmp->iomem); } -static Property grlib_irqmp_properties[] = { +static const Property grlib_irqmp_properties[] = { DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index d9558e3940..c3174f4fdc 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -193,7 +193,7 @@ static const VMStateDescription vmstate_pic_common = { } }; -static Property pic_properties_common[] = { +static const Property pic_properties_common[] = { DEFINE_PROP_UINT32("iobase", PICCommonState, iobase, -1), DEFINE_PROP_UINT32("elcr_addr", PICCommonState, elcr_addr, -1), DEFINE_PROP_UINT8("elcr_mask", PICCommonState, elcr_mask, -1), diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index e73c8d4f07..6d566165b0 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -476,7 +476,7 @@ static void ioapic_unrealize(DeviceState *dev) timer_free(s->delayed_ioapic_service_timer); } -static Property ioapic_properties[] = { +static const Property ioapic_properties[] = { DEFINE_PROP_UINT8("version", IOAPICCommonState, version, IOAPIC_VER_DEF), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 97d1af5ccc..dd91f89361 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -423,7 +423,7 @@ static const VMStateDescription vmstate_loongarch_extioi = { } }; -static Property extioi_properties[] = { +static const Property extioi_properties[] = { DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features, EXTIOI_HAS_VIRT_EXTENSION, 0), diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index ecf3ed0267..e2eca30660 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -83,7 +83,7 @@ static void loongarch_pch_msi_init(Object *obj) } -static Property loongarch_msi_properties[] = { +static const Property loongarch_msi_properties[] = { DEFINE_PROP_UINT32("msi_irq_base", LoongArchPCHMSI, irq_base, 0), DEFINE_PROP_UINT32("msi_irq_num", LoongArchPCHMSI, irq_num, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index b958180591..6a87b1aab7 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -411,7 +411,7 @@ static void loongarch_pch_pic_init(Object *obj) } -static Property loongarch_pch_pic_properties[] = { +static const Property loongarch_pch_pic_properties[] = { DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC, irq_num, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index a6ce0181f6..d3f894a5bd 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -315,7 +315,7 @@ static const VMStateDescription vmstate_loongson_ipi_common = { } }; -static Property ipi_common_properties[] = { +static const Property ipi_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index b4471e185a..b5d10ab1f6 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -85,7 +85,7 @@ static const VMStateDescription vmstate_m68k_irqc = { } }; -static Property m68k_irqc_properties[] = { +static const Property m68k_irqc_properties[] = { DEFINE_PROP_LINK("m68k-cpu", M68KIRQCState, cpu, TYPE_M68K_CPU, ArchCPU *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 77ba7348a3..996db095c3 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -438,7 +438,7 @@ static void mips_gic_realize(DeviceState *dev, Error **errp) } } -static Property mips_gic_properties[] = { +static const Property mips_gic_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSGICState, num_vps, 1), DEFINE_PROP_UINT32("num-irq", MIPSGICState, num_irq, 256), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index a98358d92e..28606f102b 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -375,7 +375,7 @@ void omap_intc_set_fclk(OMAPIntcState *intc, omap_clk clk) intc->fclk = clk; } -static Property omap_intc_properties[] = { +static const Property omap_intc_properties[] = { DEFINE_PROP_UINT32("size", OMAPIntcState, size, 0x100), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c index 99032ea7f7..8ee1bd64bd 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -128,7 +128,7 @@ static void or1k_ompic_realize(DeviceState *dev, Error **errp) } } -static Property or1k_ompic_properties[] = { +static const Property or1k_ompic_properties[] = { DEFINE_PROP_UINT32("num-cpus", OR1KOMPICState, num_cpus, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 2ead4b9ba0..2257ae2ee7 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -1606,7 +1606,7 @@ static void openpic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq); } -static Property openpic_properties[] = { +static const Property openpic_properties[] = { DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 838c6b9d99..135fe8301a 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -262,7 +262,7 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) kvm_arch_vcpu_id(cs)); } -static Property kvm_openpic_properties[] = { +static const Property kvm_openpic_properties[] = { DEFINE_PROP_UINT32("model", KVMOpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 5bacbce6a4..581659839b 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -2059,7 +2059,7 @@ static int pnv_xive_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static Property pnv_xive_properties[] = { +static const Property pnv_xive_properties[] = { DEFINE_PROP_UINT64("ic-bar", PnvXive, ic_base, 0), DEFINE_PROP_UINT64("vc-bar", PnvXive, vc_base, 0), DEFINE_PROP_UINT64("pc-bar", PnvXive, pc_base, 0), diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 834d32287b..5dd305453a 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2354,7 +2354,7 @@ static void pnv_xive2_realize(DeviceState *dev, Error **errp) qemu_register_reset(pnv_xive2_reset, dev); } -static Property pnv_xive2_properties[] = { +static const Property pnv_xive2_properties[] = { DEFINE_PROP_UINT64("ic-bar", PnvXive2, ic_base, 0), DEFINE_PROP_UINT64("esb-bar", PnvXive2, esb_base, 0), DEFINE_PROP_UINT64("end-bar", PnvXive2, end_base, 0), diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c index f2a224f3aa..d683413a83 100644 --- a/hw/intc/ppc-uic.c +++ b/hw/intc/ppc-uic.c @@ -259,7 +259,7 @@ static void ppc_uic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ); } -static Property ppc_uic_properties[] = { +static const Property ppc_uic_properties[] = { DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0), DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), DEFINE_PROP_END_OF_LIST() diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index e9f0536b1c..db195fb1ff 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -262,7 +262,7 @@ static const MemoryRegionOps riscv_aclint_mtimer_ops = { } }; -static Property riscv_aclint_mtimer_properties[] = { +static const Property riscv_aclint_mtimer_properties[] = { DEFINE_PROP_UINT32("hartid-base", RISCVAclintMTimerState, hartid_base, 0), DEFINE_PROP_UINT32("num-harts", RISCVAclintMTimerState, num_harts, 1), @@ -462,7 +462,7 @@ static const MemoryRegionOps riscv_aclint_swi_ops = { } }; -static Property riscv_aclint_swi_properties[] = { +static const Property riscv_aclint_swi_properties[] = { DEFINE_PROP_UINT32("hartid-base", RISCVAclintSwiState, hartid_base, 0), DEFINE_PROP_UINT32("num-harts", RISCVAclintSwiState, num_harts, 1), DEFINE_PROP_UINT32("sswi", RISCVAclintSwiState, sswi, false), diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 4a262c82f0..353eec8136 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -904,7 +904,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } -static Property riscv_aplic_properties[] = { +static const 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), diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 9ef65d4012..adc36151b4 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -388,7 +388,7 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } -static Property riscv_imsic_properties[] = { +static const Property riscv_imsic_properties[] = { DEFINE_PROP_BOOL("mmode", RISCVIMSICState, mmode, 0), DEFINE_PROP_UINT32("hartid", RISCVIMSICState, hartid, 0), DEFINE_PROP_UINT32("num-pages", RISCVIMSICState, num_pages, 0), diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c index b2d4338f61..dfe11ade20 100644 --- a/hw/intc/rx_icu.c +++ b/hw/intc/rx_icu.c @@ -361,7 +361,7 @@ static const VMStateDescription vmstate_rxicu = { } }; -static Property rxicu_properties[] = { +static const Property rxicu_properties[] = { DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map, qdev_prop_uint8, uint8_t), DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense, diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index c3d2b8d765..2963bd5bd6 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -445,7 +445,7 @@ static void qemu_s390_flic_instance_init(Object *obj) } } -static Property qemu_s390_flic_properties[] = { +static const Property qemu_s390_flic_properties[] = { DEFINE_PROP_BOOL("migrate-all-state", QEMUS390FLICState, migrate_all_state, true), DEFINE_PROP_END_OF_LIST(), @@ -471,7 +471,7 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) fsc->inject_crw_mchk = qemu_s390_inject_crw_mchk; } -static Property s390_flic_common_properties[] = { +static const Property s390_flic_common_properties[] = { DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState, adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), DEFINE_PROP_BOOL("migration-enabled", S390FLICState, diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index ed74490dba..49895be803 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -430,7 +430,7 @@ static const VMStateDescription vmstate_sifive_plic = { } }; -static Property sifive_plic_properties[] = { +static const Property sifive_plic_properties[] = { DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), /* number of interrupt sources including interrupt source 0 */ diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 283a6b8fd2..09f643d633 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -627,7 +627,7 @@ static void spapr_xive_free_irq(SpaprInterruptController *intc, int lisn) xive->eat[lisn].w &= cpu_to_be64(~EAS_VALID); } -static Property spapr_xive_properties[] = { +static const Property spapr_xive_properties[] = { DEFINE_PROP_UINT32("nr-irqs", SpaprXive, nr_irqs, 0), DEFINE_PROP_UINT32("nr-ends", SpaprXive, nr_ends, 0), DEFINE_PROP_UINT64("vc-base", SpaprXive, vc_base, SPAPR_XIVE_VC_BASE), diff --git a/hw/intc/xics.c b/hw/intc/xics.c index e893363dc9..81bbfdd84b 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -344,7 +344,7 @@ static void icp_unrealize(DeviceState *dev) vmstate_unregister(NULL, &vmstate_icp_server, icp); } -static Property icp_properties[] = { +static const Property icp_properties[] = { DEFINE_PROP_LINK(ICP_PROP_XICS, ICPState, xics, TYPE_XICS_FABRIC, XICSFabric *), DEFINE_PROP_LINK(ICP_PROP_CPU, ICPState, cs, TYPE_CPU, CPUState *), @@ -676,7 +676,7 @@ static const VMStateDescription vmstate_ics = { }, }; -static Property ics_properties[] = { +static const Property ics_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), DEFINE_PROP_LINK(ICS_PROP_XICS, ICSState, xics, TYPE_XICS_FABRIC, XICSFabric *), diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index 6e5012e66e..3e860ab582 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -176,7 +176,7 @@ static void xilinx_intc_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); } -static Property xilinx_intc_properties[] = { +static const Property xilinx_intc_properties[] = { DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 245e4d181a..308e5743bd 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -927,7 +927,7 @@ static const VMStateDescription vmstate_xive_tctx = { }, }; -static Property xive_tctx_properties[] = { +static const Property xive_tctx_properties[] = { DEFINE_PROP_LINK("cpu", XiveTCTX, cs, TYPE_CPU, CPUState *), DEFINE_PROP_LINK("presenter", XiveTCTX, xptr, TYPE_XIVE_PRESENTER, XivePresenter *), @@ -1403,7 +1403,7 @@ static const VMStateDescription vmstate_xive_source = { * The default XIVE interrupt source setting for the ESB MMIOs is two * 64k pages without Store EOI, to be in sync with KVM. */ -static Property xive_source_properties[] = { +static const Property xive_source_properties[] = { DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0), DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0), DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE), @@ -2002,7 +2002,7 @@ void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) xive_router_end_notify_handler(xrtr, &eas); } -static Property xive_router_properties[] = { +static const Property xive_router_properties[] = { DEFINE_PROP_LINK("xive-fabric", XiveRouter, xfb, TYPE_XIVE_FABRIC, XiveFabric *), DEFINE_PROP_END_OF_LIST(), @@ -2170,7 +2170,7 @@ static void xive_end_source_realize(DeviceState *dev, Error **errp) (1ull << (xsrc->esb_shift + 1)) * xsrc->nr_ends); } -static Property xive_end_source_properties[] = { +static const Property xive_end_source_properties[] = { DEFINE_PROP_UINT32("nr-ends", XiveENDSource, nr_ends, 0), DEFINE_PROP_UINT32("shift", XiveENDSource, esb_shift, XIVE_ESB_64K), DEFINE_PROP_LINK("xive", XiveENDSource, xrtr, TYPE_XIVE_ROUTER, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index d1df35e9b3..3233d3f14e 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1028,7 +1028,7 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) xive_get_field64(EAS2_END_DATA, eas.w)); } -static Property xive2_router_properties[] = { +static const Property xive2_router_properties[] = { DEFINE_PROP_LINK("xive-fabric", Xive2Router, xfb, TYPE_XIVE_FABRIC, XiveFabric *), DEFINE_PROP_END_OF_LIST(), @@ -1242,7 +1242,7 @@ static void xive2_end_source_realize(DeviceState *dev, Error **errp) (1ull << (xsrc->esb_shift + 1)) * xsrc->nr_ends); } -static Property xive2_end_source_properties[] = { +static const Property xive2_end_source_properties[] = { DEFINE_PROP_UINT32("nr-ends", Xive2EndSource, nr_ends, 0), DEFINE_PROP_UINT32("shift", Xive2EndSource, esb_shift, XIVE_ESB_64K), DEFINE_PROP_LINK("xive", Xive2EndSource, xrtr, TYPE_XIVE2_ROUTER, diff --git a/hw/intc/xlnx-pmu-iomod-intc.c b/hw/intc/xlnx-pmu-iomod-intc.c index 48cd3ae94b..21b9c83658 100644 --- a/hw/intc/xlnx-pmu-iomod-intc.c +++ b/hw/intc/xlnx-pmu-iomod-intc.c @@ -474,7 +474,7 @@ static const MemoryRegionOps xlnx_pmu_io_intc_ops = { }, }; -static Property xlnx_pmu_io_intc_properties[] = { +static const Property xlnx_pmu_io_intc_properties[] = { DEFINE_PROP_UINT32("intc-intr-size", XlnxPMUIOIntc, cfg.intr_size, 0), DEFINE_PROP_UINT32("intc-level-edge", XlnxPMUIOIntc, cfg.level_edge, 0), DEFINE_PROP_UINT32("intc-positive", XlnxPMUIOIntc, cfg.positive, 0), From 9811813e900063305bbcba11b57ff2510b439db0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:49:31 +0000 Subject: [PATCH 0341/2892] hw/ipack: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alberto Garcia Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/ipack/ipack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index c39dbb481f..7ffc4ffe6f 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -73,7 +73,7 @@ static void ipack_device_unrealize(DeviceState *dev) qemu_free_irqs(idev->irq, 2); } -static Property ipack_device_props[] = { +static const Property ipack_device_props[] = { DEFINE_PROP_INT32("slot", IPackDevice, slot, -1), DEFINE_PROP_END_OF_LIST() }; From 2acf140305ed957f945e09ca7667ad872a091958 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:50:48 +0000 Subject: [PATCH 0342/2892] hw/ipmi: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Corey Minyard Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/ipmi/ipmi.c | 2 +- hw/ipmi/ipmi_bmc_extern.c | 2 +- hw/ipmi/ipmi_bmc_sim.c | 2 +- hw/ipmi/isa_ipmi_bt.c | 2 +- hw/ipmi/isa_ipmi_kcs.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index bbb07b151e..850b3bc463 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -108,7 +108,7 @@ void ipmi_bmc_find_and_link(Object *obj, Object **bmc) OBJ_PROP_LINK_STRONG); } -static Property ipmi_bmc_properties[] = { +static const Property ipmi_bmc_properties[] = { DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 29c5af3cc3..cfec1da87c 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -515,7 +515,7 @@ static void ipmi_bmc_extern_finalize(Object *obj) timer_free(ibe->extern_timer); } -static Property ipmi_bmc_extern_properties[] = { +static const Property ipmi_bmc_extern_properties[] = { DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 33c839c65a..8a55893e89 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -2191,7 +2191,7 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } -static Property ipmi_sim_properties[] = { +static const Property ipmi_sim_properties[] = { DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 7b36d51494..16062abb31 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -135,7 +135,7 @@ static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) return &iib->bt; } -static Property ipmi_isa_properties[] = { +static const Property ipmi_isa_properties[] = { DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index f52b32e590..7e7a37659e 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -142,7 +142,7 @@ static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) return &iik->kcs; } -static Property ipmi_isa_properties[] = { +static const Property ipmi_isa_properties[] = { DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2), DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5), DEFINE_PROP_END_OF_LIST(), From 7f68219cc7b15df35f0543a3d9824584480077eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:51:35 +0000 Subject: [PATCH 0343/2892] hw/isa: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/isa/lpc_ich9.c | 2 +- hw/isa/pc87312.c | 2 +- hw/isa/piix.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index dabd1217dd..378244aa8f 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -826,7 +826,7 @@ static const VMStateDescription vmstate_ich9_lpc = { } }; -static Property ich9_lpc_properties[] = { +static const Property ich9_lpc_properties[] = { DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, false), DEFINE_PROP_BOOL("smm-compat", ICH9LPCState, pm.smm_compat, false), DEFINE_PROP_BOOL("smm-enabled", ICH9LPCState, pm.smm_enabled, false), diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index f67155498d..7bb2af817d 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -327,7 +327,7 @@ static const VMStateDescription vmstate_pc87312 = { } }; -static Property pc87312_properties[] = { +static const Property pc87312_properties[] = { DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398), DEFINE_PROP_UINT8("config", PC87312State, config, 1), DEFINE_PROP_END_OF_LIST() diff --git a/hw/isa/piix.c b/hw/isa/piix.c index b4a402f61b..8ec9c63b8a 100644 --- a/hw/isa/piix.c +++ b/hw/isa/piix.c @@ -408,7 +408,7 @@ static void pci_piix_init(Object *obj) object_initialize_child(obj, "rtc", &d->rtc, TYPE_MC146818_RTC); } -static Property pci_piix_props[] = { +static const Property pci_piix_props[] = { DEFINE_PROP_UINT32("smb_io_base", PIIXState, smb_io_base, 0), DEFINE_PROP_BOOL("has-acpi", PIIXState, has_acpi, true), DEFINE_PROP_BOOL("has-pic", PIIXState, has_pic, true), From 55f579773e00000b33eef548ec9a1abac0c21008 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:52:44 +0000 Subject: [PATCH 0344/2892] hw/m68k: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/m68k/mcf5206.c | 2 +- hw/m68k/mcf_intc.c | 2 +- hw/m68k/next-cube.c | 2 +- hw/m68k/q800-glue.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 7247cdbe5e..45e5f74600 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -600,7 +600,7 @@ static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) s->uart[1] = mcf_uart_create(s->pic[13], serial_hd(1)); } -static Property mcf5206_mbar_properties[] = { +static const Property mcf5206_mbar_properties[] = { DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu, TYPE_M68K_CPU, M68kCPU *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 9fc30b03ba..c24b0b715d 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -177,7 +177,7 @@ static void mcf_intc_instance_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } -static Property mcf_intc_properties[] = { +static const Property mcf_intc_properties[] = { DEFINE_PROP_LINK("m68k-cpu", mcf_intc_state, cpu, TYPE_M68K_CPU, M68kCPU *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 08886d432c..a37ce00874 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -914,7 +914,7 @@ static void next_pc_realize(DeviceState *dev, Error **errp) * this cpu link property and could instead provide outbound IRQ lines * that the board could wire up to the CPU. */ -static Property next_pc_properties[] = { +static const Property next_pc_properties[] = { DEFINE_PROP_LINK("cpu", NeXTPC, cpu, TYPE_M68K_CPU, M68kCPU *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index e2ae7c3201..0d8cb8b1cb 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -203,7 +203,7 @@ static const VMStateDescription vmstate_glue = { * this cpu link property and could instead provide outbound IRQ lines * that the board could wire up to the CPU. */ -static Property glue_properties[] = { +static const Property glue_properties[] = { DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), DEFINE_PROP_END_OF_LIST(), }; From f4f2248bbce2208c6946567c0a3155f02ff7b415 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:53:42 +0000 Subject: [PATCH 0345/2892] hw/mem: Constify all Property 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 --- hw/mem/cxl_type3.c | 2 +- hw/mem/nvdimm.c | 2 +- hw/mem/pc-dimm.c | 2 +- hw/mem/sparse-mem.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 5cf754b38f..12205c4d32 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1216,7 +1216,7 @@ static void ct3d_reset(DeviceState *dev) } -static Property ct3_props[] = { +static const Property ct3_props[] = { DEFINE_PROP_LINK("memdev", CXLType3Dev, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), /* for backward compatibility */ DEFINE_PROP_LINK("persistent-memdev", CXLType3Dev, hostpmem, diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 1631a7d13f..10506d52e4 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -246,7 +246,7 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, memory_region_set_dirty(mr, backend_offset, size); } -static Property nvdimm_properties[] = { +static const Property nvdimm_properties[] = { DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 27919ca45d..49c5f9fd44 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -150,7 +150,7 @@ out: return slot; } -static Property pc_dimm_properties[] = { +static const Property pc_dimm_properties[] = { DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index 6e8f4f84fb..8d681adfc0 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -96,7 +96,7 @@ static const MemoryRegionOps sparse_mem_ops = { }, }; -static Property sparse_mem_properties[] = { +static const Property sparse_mem_properties[] = { /* The base address of the memory */ DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0), /* The length of the sparse memory region */ From ce385ef3cceeb9e6d1a80b1ef6f96133b060bd58 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 15:54:54 +0000 Subject: [PATCH 0346/2892] hw/mips: Constify all Property 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 --- hw/mips/cps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 13046628cd..1a2208666c 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -166,7 +166,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0)); } -static Property mips_cps_properties[] = { +static const Property mips_cps_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1), DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 256), DEFINE_PROP_STRING("cpu-type", MIPSCPSState, cpu_type), From 56d8d2b34288c3ea6b62d9e0069cc927d542ea93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:23:55 +0000 Subject: [PATCH 0347/2892] hw/misc/xlnx-versal-trng: Constify trng_props MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use DEFINE_PROP_UNSIGNED instead of DEFINE_PROP_UINT64 so that we can set the PropertyInfo during initialization, instead of updating within trng_class_init. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/misc/xlnx-versal-trng.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 86905479b8..2f6af4f680 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -660,8 +660,9 @@ static const PropertyInfo trng_prop_fault_events = { static PropertyInfo trng_prop_uint64; /* to extend qdev_prop_uint64 */ -static Property trng_props[] = { - DEFINE_PROP_UINT64("forced-prng", XlnxVersalTRng, forced_prng_seed, 0), +static const Property trng_props[] = { + DEFINE_PROP_UNSIGNED("forced-prng", XlnxVersalTRng, forced_prng_seed, + 0, trng_prop_uint64, uint64_t), DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200), DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults, trng_prop_fault_events, uint32_t), @@ -694,7 +695,6 @@ static void trng_class_init(ObjectClass *klass, void *data) /* Clone uint64 property with set allowed after realized */ trng_prop_uint64 = qdev_prop_uint64; trng_prop_uint64.realized_set_allowed = true; - trng_props[0].info = &trng_prop_uint64; device_class_set_props(dc, trng_props); } From 30029973103ac7f7ffb7bce0f088b773ad9f5dae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:15:59 +0000 Subject: [PATCH 0348/2892] hw/misc: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/misc/a9scu.c | 2 +- hw/misc/allwinner-h3-dramc.c | 2 +- hw/misc/allwinner-r40-dramc.c | 2 +- hw/misc/allwinner-sid.c | 2 +- hw/misc/applesmc.c | 2 +- hw/misc/arm11scu.c | 2 +- hw/misc/arm_l2x0.c | 2 +- hw/misc/arm_sysctl.c | 2 +- hw/misc/armsse-cpuid.c | 2 +- hw/misc/aspeed_hace.c | 2 +- hw/misc/aspeed_i3c.c | 2 +- hw/misc/aspeed_lpc.c | 2 +- hw/misc/aspeed_sbc.c | 2 +- hw/misc/aspeed_scu.c | 2 +- hw/misc/aspeed_sdmc.c | 2 +- hw/misc/bcm2835_cprman.c | 2 +- hw/misc/bcm2835_property.c | 2 +- hw/misc/debugexit.c | 2 +- hw/misc/eccmemctl.c | 2 +- hw/misc/empty_slot.c | 2 +- hw/misc/iotkit-secctl.c | 2 +- hw/misc/iotkit-sysctl.c | 2 +- hw/misc/iotkit-sysinfo.c | 2 +- hw/misc/ivshmem.c | 4 ++-- hw/misc/led.c | 2 +- hw/misc/mac_via.c | 2 +- hw/misc/macio/cuda.c | 2 +- hw/misc/macio/macio.c | 4 ++-- hw/misc/macio/pmu.c | 2 +- hw/misc/mips_cmgcr.c | 2 +- hw/misc/mips_cpc.c | 2 +- hw/misc/mips_itu.c | 2 +- hw/misc/mos6522.c | 2 +- hw/misc/mps2-fpgaio.c | 2 +- hw/misc/mps2-scc.c | 2 +- hw/misc/msf2-sysreg.c | 2 +- hw/misc/npcm7xx_gcr.c | 2 +- hw/misc/nrf51_rng.c | 2 +- hw/misc/pci-testdev.c | 2 +- hw/misc/pvpanic-isa.c | 2 +- hw/misc/pvpanic-pci.c | 2 +- hw/misc/sifive_e_aon.c | 2 +- hw/misc/sifive_u_otp.c | 2 +- hw/misc/stm32l4x5_rcc.c | 2 +- hw/misc/tz-mpc.c | 2 +- hw/misc/tz-msc.c | 2 +- hw/misc/tz-ppc.c | 2 +- hw/misc/unimp.c | 2 +- hw/misc/xlnx-versal-cframe-reg.c | 4 ++-- hw/misc/xlnx-versal-cfu.c | 4 ++-- hw/misc/xlnx-versal-xramc.c | 2 +- hw/misc/zynq_slcr.c | 2 +- 52 files changed, 56 insertions(+), 56 deletions(-) diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c index a40d5072de..e2d73edde8 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -123,7 +123,7 @@ static const VMStateDescription vmstate_a9_scu = { } }; -static Property a9_scu_properties[] = { +static const Property a9_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index eeab0dc5d2..247bf62c43 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -314,7 +314,7 @@ static void allwinner_h3_dramc_init(Object *obj) sysbus_init_mmio(sbd, &s->dramphy_iomem); } -static Property allwinner_h3_dramc_properties[] = { +static const Property allwinner_h3_dramc_properties[] = { DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0), DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB), DEFINE_PROP_END_OF_LIST() diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c index 3ae4890037..a51284ff91 100644 --- a/hw/misc/allwinner-r40-dramc.c +++ b/hw/misc/allwinner-r40-dramc.c @@ -464,7 +464,7 @@ static void allwinner_r40_dramc_init(Object *obj) sysbus_init_mmio(sbd, &s->dramphy_iomem); } -static Property allwinner_r40_dramc_properties[] = { +static const Property allwinner_r40_dramc_properties[] = { DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState, ram_addr, 0x0), DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState, ram_size, 256), /* MiB */ DEFINE_PROP_END_OF_LIST() diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c index 19ff17d24a..3a09dca111 100644 --- a/hw/misc/allwinner-sid.c +++ b/hw/misc/allwinner-sid.c @@ -127,7 +127,7 @@ static void allwinner_sid_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property allwinner_sid_properties[] = { +static const Property allwinner_sid_properties[] = { DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 5b766277d6..9d0e273e33 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -350,7 +350,7 @@ static void applesmc_unrealize(DeviceState *dev) } } -static Property applesmc_isa_properties[] = { +static const Property applesmc_isa_properties[] = { DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, APPLESMC_DEFAULT_IOBASE), DEFINE_PROP_STRING("osk", AppleSMCState, osk), diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c index 17c36a0545..37feed9da7 100644 --- a/hw/misc/arm11scu.c +++ b/hw/misc/arm11scu.c @@ -75,7 +75,7 @@ static void arm11_scu_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property arm11_scu_properties[] = { +static const Property arm11_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11SCUState, num_cpu, 1), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 1902ebd3bc..9c209f13b0 100644 --- a/hw/misc/arm_l2x0.c +++ b/hw/misc/arm_l2x0.c @@ -173,7 +173,7 @@ static void l2x0_priv_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static Property l2x0_properties[] = { +static const Property l2x0_properties[] = { DEFINE_PROP_UINT32("cache-type", L2x0State, cache_type, 0x1c100100), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 9c4dce350a..69e379fa10 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -623,7 +623,7 @@ static void arm_sysctl_finalize(Object *obj) g_free(s->db_clock_reset); } -static Property arm_sysctl_properties[] = { +static const Property arm_sysctl_properties[] = { DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0), /* Daughterboard power supply voltages (as reported via SYS_CFG) */ diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c index e785a09051..b05bcdcabc 100644 --- a/hw/misc/armsse-cpuid.c +++ b/hw/misc/armsse-cpuid.c @@ -92,7 +92,7 @@ static const MemoryRegionOps armsse_cpuid_ops = { .valid.max_access_size = 4, }; -static Property armsse_cpuid_props[] = { +static const Property armsse_cpuid_props[] = { DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index bc1d66ad80..5cefbadf9a 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -436,7 +436,7 @@ static void aspeed_hace_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static Property aspeed_hace_properties[] = { +static const Property aspeed_hace_properties[] = { DEFINE_PROP_LINK("dram", AspeedHACEState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/aspeed_i3c.c b/hw/misc/aspeed_i3c.c index 371ee7dba8..7f5a389864 100644 --- a/hw/misc/aspeed_i3c.c +++ b/hw/misc/aspeed_i3c.c @@ -323,7 +323,7 @@ static void aspeed_i3c_realize(DeviceState *dev, Error **errp) } -static Property aspeed_i3c_device_properties[] = { +static const Property aspeed_i3c_device_properties[] = { DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/aspeed_lpc.c b/hw/misc/aspeed_lpc.c index f2d4ca6f43..bb9066b0f0 100644 --- a/hw/misc/aspeed_lpc.c +++ b/hw/misc/aspeed_lpc.c @@ -454,7 +454,7 @@ static const VMStateDescription vmstate_aspeed_lpc = { } }; -static Property aspeed_lpc_properties[] = { +static const Property aspeed_lpc_properties[] = { DEFINE_PROP_UINT32("hicr7", AspeedLPCState, hicr7, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index f5eb2a0e37..b97cf51fa1 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -136,7 +136,7 @@ static const VMStateDescription vmstate_aspeed_sbc = { } }; -static Property aspeed_sbc_properties[] = { +static const Property aspeed_sbc_properties[] = { DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 2c919349cf..ac33b8d6cb 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -602,7 +602,7 @@ static const VMStateDescription vmstate_aspeed_scu = { } }; -static Property aspeed_scu_properties[] = { +static const Property aspeed_scu_properties[] = { DEFINE_PROP_UINT32("silicon-rev", AspeedSCUState, silicon_rev, 0), DEFINE_PROP_UINT32("hw-strap1", AspeedSCUState, hw_strap1, 0), DEFINE_PROP_UINT32("hw-strap2", AspeedSCUState, hw_strap2, 0), diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 4bc9faf691..4980080f74 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -294,7 +294,7 @@ static const VMStateDescription vmstate_aspeed_sdmc = { } }; -static Property aspeed_sdmc_properties[] = { +static const Property aspeed_sdmc_properties[] = { DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), DEFINE_PROP_BOOL("unlocked", AspeedSDMCState, unlocked, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index 63e1045abf..1a20cd0bc8 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -778,7 +778,7 @@ static const VMStateDescription cprman_vmstate = { } }; -static Property cprman_properties[] = { +static const Property cprman_properties[] = { DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 8ca3128f29..09a6f2c6e3 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -551,7 +551,7 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) bcm2835_property_reset(dev); } -static Property bcm2835_property_props[] = { +static const Property bcm2835_property_props[] = { DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), DEFINE_PROP_END_OF_LIST() diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index c5c562fd93..639a8cc3e3 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -56,7 +56,7 @@ static void debug_exit_realizefn(DeviceState *d, Error **errp) isa->iobase, &isa->io); } -static Property debug_exit_properties[] = { +static const Property debug_exit_properties[] = { DEFINE_PROP_UINT32("iobase", ISADebugExitState, iobase, 0x501), DEFINE_PROP_UINT32("iosize", ISADebugExitState, iosize, 0x02), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index 0f68fbe1b6..4fc88bd4e5 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -325,7 +325,7 @@ static void ecc_realize(DeviceState *dev, Error **errp) } } -static Property ecc_properties[] = { +static const Property ecc_properties[] = { DEFINE_PROP_UINT32("version", ECCState, version, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/empty_slot.c b/hw/misc/empty_slot.c index 37b0ddfb02..79572c5be0 100644 --- a/hw/misc/empty_slot.c +++ b/hw/misc/empty_slot.c @@ -79,7 +79,7 @@ static void empty_slot_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static Property empty_slot_properties[] = { +static const Property empty_slot_properties[] = { DEFINE_PROP_UINT64("size", EmptySlot, size, 0), DEFINE_PROP_STRING("name", EmptySlot, name), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 6e22f2aad6..abb6a963ca 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -814,7 +814,7 @@ static const VMStateDescription iotkit_secctl_vmstate = { }, }; -static Property iotkit_secctl_props[] = { +static const Property iotkit_secctl_props[] = { DEFINE_PROP_UINT32("sse-version", IoTKitSecCtl, sse_version, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index c1b357e6b7..23b49d7dff 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -835,7 +835,7 @@ static const VMStateDescription iotkit_sysctl_vmstate = { } }; -static Property iotkit_sysctl_props[] = { +static const Property iotkit_sysctl_props[] = { DEFINE_PROP_UINT32("sse-version", IoTKitSysCtl, sse_version, 0), DEFINE_PROP_UINT32("CPUWAIT_RST", IoTKitSysCtl, cpuwait_rst, 0), DEFINE_PROP_UINT32("INITSVTOR0_RST", IoTKitSysCtl, initsvtor0_rst, diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index aaa9305b2e..7d4eea6bfb 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -131,7 +131,7 @@ static const MemoryRegionOps iotkit_sysinfo_ops = { .valid.max_access_size = 4, }; -static Property iotkit_sysinfo_props[] = { +static const Property iotkit_sysinfo_props[] = { DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0), DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0), DEFINE_PROP_UINT32("sse-version", IoTKitSysInfo, sse_version, 0), diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 5ce3fc0949..6d735ec29f 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1022,7 +1022,7 @@ static const VMStateDescription ivshmem_plain_vmsd = { }, }; -static Property ivshmem_plain_properties[] = { +static const Property ivshmem_plain_properties[] = { DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), @@ -1077,7 +1077,7 @@ static const VMStateDescription ivshmem_doorbell_vmsd = { }, }; -static Property ivshmem_doorbell_properties[] = { +static const Property ivshmem_doorbell_properties[] = { DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, diff --git a/hw/misc/led.c b/hw/misc/led.c index 4bb6ce8d29..76efdbc3f1 100644 --- a/hw/misc/led.c +++ b/hw/misc/led.c @@ -101,7 +101,7 @@ static void led_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(DEVICE(s), led_set_state_gpio_handler, 1); } -static Property led_properties[] = { +static const Property led_properties[] = { DEFINE_PROP_STRING("color", LEDState, color), DEFINE_PROP_STRING("description", LEDState, description), DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true), diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index af2b2b1af3..a376a2b8a0 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1322,7 +1322,7 @@ static const VMStateDescription vmstate_q800_via1 = { } }; -static Property mos6522_q800_via1_properties[] = { +static const Property mos6522_q800_via1_properties[] = { DEFINE_PROP_DRIVE("drive", MOS6522Q800VIA1State, blk), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 1db7ebf3e2..cfc8afd1dc 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -554,7 +554,7 @@ static void cuda_init(Object *obj) DEVICE(obj), "adb.0"); } -static Property cuda_properties[] = { +static const Property cuda_properties[] = { DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 3f449f91c0..7e3d5aa977 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -405,7 +405,7 @@ static const VMStateDescription vmstate_macio_newworld = { } }; -static Property macio_newworld_properties[] = { +static const Property macio_newworld_properties[] = { DEFINE_PROP_BOOL("has-pmu", NewWorldMacIOState, has_pmu, false), DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), DEFINE_PROP_END_OF_LIST() @@ -422,7 +422,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, macio_newworld_properties); } -static Property macio_properties[] = { +static const Property macio_properties[] = { DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 4b451e0af3..64bf44f67f 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -760,7 +760,7 @@ static void pmu_init(Object *obj) sysbus_init_mmio(d, &s->mem); } -static Property pmu_properties[] = { +static const Property pmu_properties[] = { DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 04256aacdc..80ca224f76 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -211,7 +211,7 @@ static const VMStateDescription vmstate_mips_gcr = { }, }; -static Property mips_gcr_properties[] = { +static const Property mips_gcr_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSGCRState, num_vps, 1), DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 2f7fb8167f..86ff0f7ad8 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -163,7 +163,7 @@ static const VMStateDescription vmstate_mips_cpc = { }, }; -static Property mips_cpc_properties[] = { +static const Property mips_cpc_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index c5214c8b30..d84a7dbf15 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -533,7 +533,7 @@ static void mips_itu_reset(DeviceState *dev) itc_reset_cells(s); } -static Property mips_itu_properties[] = { +static const Property mips_itu_properties[] = { DEFINE_PROP_UINT32("num-fifo", MIPSITUState, num_fifo, ITC_FIFO_NUM_MAX), DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores, diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 515f62e687..0225a5869b 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -696,7 +696,7 @@ static void mos6522_finalize(Object *obj) timer_free(s->timers[1].timer); } -static Property mos6522_properties[] = { +static const Property mos6522_properties[] = { DEFINE_PROP_UINT64("frequency", MOS6522State, frequency, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 20359254ba..2e8d1c721c 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -319,7 +319,7 @@ static const VMStateDescription mps2_fpgaio_vmstate = { }, }; -static Property mps2_fpgaio_properties[] = { +static const Property mps2_fpgaio_properties[] = { /* Frequency of the prescale counter */ DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000), /* Number of LEDs controlled by LED0 register */ diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index d45ff77bd6..f378b75571 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -456,7 +456,7 @@ static const VMStateDescription mps2_scc_vmstate = { } }; -static Property mps2_scc_properties[] = { +static const Property mps2_scc_properties[] = { /* Values for various read-only ID registers (which are specific * to the board model or FPGA image) */ diff --git a/hw/misc/msf2-sysreg.c b/hw/misc/msf2-sysreg.c index b8dde198c6..0d7a713c76 100644 --- a/hw/misc/msf2-sysreg.c +++ b/hw/misc/msf2-sysreg.c @@ -118,7 +118,7 @@ static const VMStateDescription vmstate_msf2_sysreg = { } }; -static Property msf2_sysreg_properties[] = { +static const Property msf2_sysreg_properties[] = { /* default divisors in Libero GUI */ DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState, apb0div, 2), DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState, apb1div, 2), diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c index c4c4e246d7..17aeaf22cb 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm7xx_gcr.c @@ -229,7 +229,7 @@ static const VMStateDescription vmstate_npcm7xx_gcr = { }, }; -static Property npcm7xx_gcr_properties[] = { +static const Property npcm7xx_gcr_properties[] = { DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0), DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c index 2d67f3f766..2b550a6bca 100644 --- a/hw/misc/nrf51_rng.c +++ b/hw/misc/nrf51_rng.c @@ -219,7 +219,7 @@ static void nrf51_rng_reset(DeviceState *dev) } -static Property nrf51_rng_properties[] = { +static const Property nrf51_rng_properties[] = { DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState, period_unfiltered_us, 167), DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState, diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 0b5f236a20..7927397a23 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -319,7 +319,7 @@ static void qdev_pci_testdev_reset(DeviceState *dev) pci_testdev_reset(d); } -static Property pci_testdev_properties[] = { +static const Property pci_testdev_properties[] = { DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index 9a923b7869..824a2e4528 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -98,7 +98,7 @@ static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static Property pvpanic_isa_properties[] = { +static const Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505), DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, PVPANIC_EVENTS), diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 106d03ccd6..1c3eafc137 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -53,7 +53,7 @@ static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp) pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &ps->mr); } -static Property pvpanic_pci_properties[] = { +static const Property pvpanic_pci_properties[] = { DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, PVPANIC_EVENTS), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c index f819fc10e6..c48429b131 100644 --- a/hw/misc/sifive_e_aon.c +++ b/hw/misc/sifive_e_aon.c @@ -289,7 +289,7 @@ static void sifive_e_aon_init(Object *obj) sysbus_init_irq(sbd, &r->wdog_irq); } -static Property sifive_e_aon_properties[] = { +static const Property sifive_e_aon_properties[] = { DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq, SIFIVE_E_LFCLK_DEFAULT_FREQ), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index 8965f5c22a..32cd8e8dfb 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -194,7 +194,7 @@ static const MemoryRegionOps sifive_u_otp_ops = { } }; -static Property sifive_u_otp_properties[] = { +static const Property sifive_u_otp_properties[] = { DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0), DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index 59d428fa66..b61241d195 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -1426,7 +1426,7 @@ static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp) clock_update(s->gnd, 0); } -static Property stm32l4x5_rcc_properties[] = { +static const Property stm32l4x5_rcc_properties[] = { DEFINE_PROP_UINT64("hse_frequency", Stm32l4x5RccState, hse_frequency, HSE_DEFAULT_FRQ), DEFINE_PROP_UINT64("sai1_extclk_frequency", Stm32l4x5RccState, diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 66a46a7b9f..b06eb9f119 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -587,7 +587,7 @@ static const VMStateDescription tz_mpc_vmstate = { } }; -static Property tz_mpc_properties[] = { +static const Property tz_mpc_properties[] = { DEFINE_PROP_LINK("downstream", TZMPC, downstream, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index 82ccaa014a..96413a502d 100644 --- a/hw/misc/tz-msc.c +++ b/hw/misc/tz-msc.c @@ -278,7 +278,7 @@ static const VMStateDescription tz_msc_vmstate = { } }; -static Property tz_msc_properties[] = { +static const Property tz_msc_properties[] = { DEFINE_PROP_LINK("downstream", TZMSC, downstream, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("idau", TZMSC, idau, diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 922dcf7f63..1943d8d165 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -305,7 +305,7 @@ static const VMStateDescription tz_ppc_vmstate = { DEFINE_PROP_LINK("port[" #N "]", TZPPC, port[N].downstream, \ TYPE_MEMORY_REGION, MemoryRegion *) -static Property tz_ppc_properties[] = { +static const Property tz_ppc_properties[] = { DEFINE_PROP_UINT32("NONSEC_MASK", TZPPC, nonsec_mask, 0), DEFINE_PORT(0), DEFINE_PORT(1), diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c index 6cfc5727f0..62e1153627 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -70,7 +70,7 @@ static void unimp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static Property unimp_properties[] = { +static const Property unimp_properties[] = { DEFINE_PROP_UINT64("size", UnimplementedDeviceState, size, 0), DEFINE_PROP_STRING("name", UnimplementedDeviceState, name), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index 3fc838bd54..8281a9baff 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -720,7 +720,7 @@ static const VMStateDescription vmstate_cframe_reg = { } }; -static Property cframe_regs_props[] = { +static const Property cframe_regs_props[] = { DEFINE_PROP_LINK("cfu-fdro", XlnxVersalCFrameReg, cfg.cfu_fdro, TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_UINT32("blktype0-frames", XlnxVersalCFrameReg, @@ -771,7 +771,7 @@ static const VMStateDescription vmstate_cframe_bcast_reg = { } }; -static Property cframe_bcast_regs_props[] = { +static const Property cframe_bcast_regs_props[] = { DEFINE_PROP_LINK("cframe0", XlnxVersalCFrameBcastReg, cfg.cframe[0], TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_LINK("cframe1", XlnxVersalCFrameBcastReg, cfg.cframe[1], diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c index 94f85814c8..7cfdabdb8e 100644 --- a/hw/misc/xlnx-versal-cfu.c +++ b/hw/misc/xlnx-versal-cfu.c @@ -426,7 +426,7 @@ static void cfu_fdro_cfi_transfer_packet(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt) } } -static Property cfu_props[] = { +static const Property cfu_props[] = { DEFINE_PROP_LINK("cframe0", XlnxVersalCFUAPB, cfg.cframe[0], TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_LINK("cframe1", XlnxVersalCFUAPB, cfg.cframe[1], @@ -460,7 +460,7 @@ static Property cfu_props[] = { DEFINE_PROP_END_OF_LIST(), }; -static Property cfu_sfr_props[] = { +static const Property cfu_sfr_props[] = { DEFINE_PROP_LINK("cfu", XlnxVersalCFUSFR, cfg.cfu, TYPE_XLNX_VERSAL_CFU_APB, XlnxVersalCFUAPB *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index ad839ce7e9..a06b9fbc05 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -218,7 +218,7 @@ static const VMStateDescription vmstate_xram_ctrl = { } }; -static Property xram_ctrl_properties[] = { +static const Property xram_ctrl_properties[] = { DEFINE_PROP_UINT64("size", XlnxXramCtrl, cfg.size, 1 * MiB), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index ad814c3a79..ffa14ecb84 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -623,7 +623,7 @@ static const VMStateDescription vmstate_zynq_slcr = { } }; -static Property zynq_slcr_props[] = { +static const Property zynq_slcr_props[] = { DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1), DEFINE_PROP_END_OF_LIST(), }; From e732f00f32e3826668db2b8b81fcf48a3a60a283 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:27:31 +0000 Subject: [PATCH 0349/2892] hw/net: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/net/allwinner-sun8i-emac.c | 2 +- hw/net/allwinner_emac.c | 2 +- hw/net/cadence_gem.c | 2 +- hw/net/can/xlnx-versal-canfd.c | 2 +- hw/net/can/xlnx-zynqmp-can.c | 2 +- hw/net/dp8393x.c | 2 +- hw/net/e1000.c | 2 +- hw/net/e1000e.c | 2 +- hw/net/eepro100.c | 2 +- hw/net/fsl_etsec/etsec.c | 2 +- hw/net/ftgmac100.c | 4 ++-- hw/net/igb.c | 2 +- hw/net/imx_fec.c | 2 +- hw/net/lan9118.c | 2 +- hw/net/lance.c | 2 +- hw/net/lasi_i82596.c | 2 +- hw/net/mcf_fec.c | 2 +- hw/net/mipsnet.c | 2 +- hw/net/msf2-emac.c | 2 +- hw/net/mv88w8618_eth.c | 2 +- hw/net/ne2000-isa.c | 2 +- hw/net/ne2000-pci.c | 2 +- hw/net/npcm7xx_emc.c | 2 +- hw/net/npcm_gmac.c | 2 +- hw/net/opencores_eth.c | 2 +- hw/net/pcnet-pci.c | 2 +- hw/net/rocker/rocker.c | 2 +- hw/net/rtl8139.c | 2 +- hw/net/smc91c111.c | 2 +- hw/net/spapr_llan.c | 2 +- hw/net/stellaris_enet.c | 2 +- hw/net/sungem.c | 2 +- hw/net/sunhme.c | 2 +- hw/net/tulip.c | 2 +- hw/net/virtio-net.c | 2 +- hw/net/vmxnet3.c | 2 +- hw/net/xen_nic.c | 2 +- hw/net/xgmac.c | 2 +- hw/net/xilinx_axienet.c | 2 +- hw/net/xilinx_ethlite.c | 2 +- 40 files changed, 41 insertions(+), 41 deletions(-) diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index cdae74f503..3f03060bf5 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -829,7 +829,7 @@ static void allwinner_sun8i_emac_realize(DeviceState *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } -static Property allwinner_sun8i_emac_properties[] = { +static const Property allwinner_sun8i_emac_properties[] = { DEFINE_NIC_PROPERTIES(AwSun8iEmacState, conf), DEFINE_PROP_UINT8("phy-addr", AwSun8iEmacState, mii_phy_addr, 0), DEFINE_PROP_LINK("dma-memory", AwSun8iEmacState, dma_mr, diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index c104c2588e..39c10426cf 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -462,7 +462,7 @@ static void aw_emac_realize(DeviceState *dev, Error **errp) fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE); } -static Property aw_emac_properties[] = { +static const Property aw_emac_properties[] = { DEFINE_NIC_PROPERTIES(AwEmacState, conf), DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 526739887c..3fce01315f 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1784,7 +1784,7 @@ static const VMStateDescription vmstate_cadence_gem = { } }; -static Property gem_properties[] = { +static const Property gem_properties[] = { DEFINE_NIC_PROPERTIES(CadenceGEMState, conf), DEFINE_PROP_UINT32("revision", CadenceGEMState, revision, GEM_MODID_VALUE), diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index e148bd7b46..97fa46c4b3 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -2042,7 +2042,7 @@ static const VMStateDescription vmstate_canfd = { } }; -static Property canfd_core_properties[] = { +static const Property canfd_core_properties[] = { DEFINE_PROP_UINT8("rx-fifo0", XlnxVersalCANFDState, cfg.rx0_fifo, 0x40), DEFINE_PROP_UINT8("rx-fifo1", XlnxVersalCANFDState, cfg.rx1_fifo, 0x40), DEFINE_PROP_UINT8("tx-fifo", XlnxVersalCANFDState, cfg.tx_fifo, 0x20), diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index 58f1432bb3..61c104c18b 100644 --- a/hw/net/can/xlnx-zynqmp-can.c +++ b/hw/net/can/xlnx-zynqmp-can.c @@ -1169,7 +1169,7 @@ static const VMStateDescription vmstate_can = { } }; -static Property xlnx_zynqmp_can_properties[] = { +static const Property xlnx_zynqmp_can_properties[] = { DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_freq, CAN_DEFAULT_CLOCK), DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS, diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index c0977308ba..e3ca11991b 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -931,7 +931,7 @@ static const VMStateDescription vmstate_dp8393x = { } }; -static Property dp8393x_properties[] = { +static const Property dp8393x_properties[] = { DEFINE_NIC_PROPERTIES(dp8393xState, conf), DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), diff --git a/hw/net/e1000.c b/hw/net/e1000.c index ab72236d18..ef0af31751 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1677,7 +1677,7 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) e1000_flush_queue_timer, d); } -static Property e1000_properties[] = { +static const Property e1000_properties[] = { DEFINE_NIC_PROPERTIES(E1000State, conf), DEFINE_PROP_BIT("extra_mac_registers", E1000State, compat_flags, E1000_FLAG_MAC_BIT, true), diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 843892ce09..e2b7576f67 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -661,7 +661,7 @@ static PropertyInfo e1000e_prop_disable_vnet, e1000e_prop_subsys_ven, e1000e_prop_subsys; -static Property e1000e_properties[] = { +static const Property e1000e_properties[] = { DEFINE_NIC_PROPERTIES(E1000EState, conf), DEFINE_PROP_SIGNED("disable_vnet_hdr", E1000EState, disable_vnet, false, e1000e_prop_disable_vnet, bool), diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 20b22d8e49..b8cb8d5cf1 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -2058,7 +2058,7 @@ static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) return eepro100_get_class_by_name(object_get_typename(OBJECT(s))); } -static Property e100_properties[] = { +static const Property e100_properties[] = { DEFINE_NIC_PROPERTIES(EEPRO100State, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index d8076e7be4..764be2c6a2 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -414,7 +414,7 @@ static void etsec_instance_init(Object *obj) sysbus_init_irq(sbd, &etsec->err_irq); } -static Property etsec_properties[] = { +static const Property etsec_properties[] = { DEFINE_NIC_PROPERTIES(eTSEC, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 478356ee3e..4adc7fb10c 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -1254,7 +1254,7 @@ static const VMStateDescription vmstate_ftgmac100 = { } }; -static Property ftgmac100_properties[] = { +static const Property ftgmac100_properties[] = { DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), DEFINE_PROP_BOOL("dma64", FTGMAC100State, dma64, false), @@ -1415,7 +1415,7 @@ static const VMStateDescription vmstate_aspeed_mii = { } }; -static Property aspeed_mii_properties[] = { +static const Property aspeed_mii_properties[] = { DEFINE_PROP_LINK("nic", AspeedMiiState, nic, TYPE_FTGMAC100, FTGMAC100State *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/net/igb.c b/hw/net/igb.c index b92bba402e..ad0f748d82 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -591,7 +591,7 @@ static const VMStateDescription igb_vmstate = { } }; -static Property igb_properties[] = { +static const Property igb_properties[] = { DEFINE_NIC_PROPERTIES(IGBState, conf), DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 4ee6f74206..9b64968477 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -1222,7 +1222,7 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } -static Property imx_eth_properties[] = { +static const Property imx_eth_properties[] = { DEFINE_NIC_PROPERTIES(IMXFECState, conf), DEFINE_PROP_UINT32("tx-ring-num", IMXFECState, tx_ring_num, 1), DEFINE_PROP_UINT32("phy-num", IMXFECState, phy_num, 0), diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 99e87b7178..237e9b97d5 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1304,7 +1304,7 @@ static void lan9118_realize(DeviceState *dev, Error **errp) ptimer_transaction_commit(s->timer); } -static Property lan9118_properties[] = { +static const Property lan9118_properties[] = { DEFINE_NIC_PROPERTIES(lan9118_state, conf), DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/net/lance.c b/hw/net/lance.c index 269615b452..9ed9c94cff 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -137,7 +137,7 @@ static void lance_instance_init(Object *obj) DEVICE(obj)); } -static Property lance_properties[] = { +static const Property lance_properties[] = { DEFINE_PROP_LINK("dma", SysBusPCNetState, state.dma_opaque, TYPE_DEVICE, DeviceState *), DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index 183fab8712..248e3841db 100644 --- a/hw/net/lasi_i82596.c +++ b/hw/net/lasi_i82596.c @@ -158,7 +158,7 @@ static void lasi_82596_instance_init(Object *obj) DEVICE(obj)); } -static Property lasi_82596_properties[] = { +static const Property lasi_82596_properties[] = { DEFINE_NIC_PROPERTIES(SysBusI82596State, state.conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 037cd2028e..55bad4c069 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -660,7 +660,7 @@ static void mcf_fec_instance_init(Object *obj) } } -static Property mcf_fec_properties[] = { +static const Property mcf_fec_properties[] = { DEFINE_NIC_PROPERTIES(mcf_fec_state, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index 31bbd6fb89..c9ef1beb7b 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -266,7 +266,7 @@ static void mipsnet_sysbus_reset(DeviceState *dev) mipsnet_reset(s); } -static Property mipsnet_properties[] = { +static const Property mipsnet_properties[] = { DEFINE_NIC_PROPERTIES(MIPSnetState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index d28fc6c570..8d9015f962 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -546,7 +546,7 @@ static void msf2_emac_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static Property msf2_emac_properties[] = { +static const Property msf2_emac_properties[] = { DEFINE_PROP_LINK("ahb-bus", MSF2EmacState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_NIC_PROPERTIES(MSF2EmacState, conf), diff --git a/hw/net/mv88w8618_eth.c b/hw/net/mv88w8618_eth.c index 96c65f4d46..ccb11512db 100644 --- a/hw/net/mv88w8618_eth.c +++ b/hw/net/mv88w8618_eth.c @@ -371,7 +371,7 @@ static const VMStateDescription mv88w8618_eth_vmsd = { } }; -static Property mv88w8618_eth_properties[] = { +static const Property mv88w8618_eth_properties[] = { DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 26980e087e..1cd070d419 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -79,7 +79,7 @@ static void isa_ne2000_realizefn(DeviceState *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); } -static Property ne2000_isa_properties[] = { +static const Property ne2000_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISANE2000State, iobase, 0x300), DEFINE_PROP_UINT32("irq", ISANE2000State, isairq, 9), DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c), diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 74773069c6..12fa579d22 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -96,7 +96,7 @@ static void ne2000_instance_init(Object *obj) &pci_dev->qdev); } -static Property ne2000_properties[] = { +static const Property ne2000_properties[] = { DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index 7307a13400..f06e908d04 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -845,7 +845,7 @@ static const VMStateDescription vmstate_npcm7xx_emc = { }, }; -static Property npcm7xx_emc_properties[] = { +static const Property npcm7xx_emc_properties[] = { DEFINE_NIC_PROPERTIES(NPCM7xxEMCState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 685905f9e2..1db29307d7 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -912,7 +912,7 @@ static const VMStateDescription vmstate_npcm_gmac = { }, }; -static Property npcm_gmac_properties[] = { +static const Property npcm_gmac_properties[] = { DEFINE_NIC_PROPERTIES(NPCMGMACState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 2c0ebda100..003b452bc9 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -743,7 +743,7 @@ static void qdev_open_eth_reset(DeviceState *dev) open_eth_reset(d); } -static Property open_eth_properties[] = { +static const Property open_eth_properties[] = { DEFINE_NIC_PROPERTIES(OpenEthState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 6190b76916..83ba8cd949 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -252,7 +252,7 @@ static void pcnet_instance_init(Object *obj) DEVICE(obj)); } -static Property pcnet_properties[] = { +static const Property pcnet_properties[] = { DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 5e74acc969..efc20396aa 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -1459,7 +1459,7 @@ static void rocker_reset(DeviceState *dev) DPRINTF("Reset done\n"); } -static Property rocker_properties[] = { +static const Property rocker_properties[] = { DEFINE_PROP_STRING("name", Rocker, name), DEFINE_PROP_STRING("world", Rocker, world_name), DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker, diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index bc56075c0d..064a73b6b4 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3410,7 +3410,7 @@ static void rtl8139_instance_init(Object *obj) DEVICE(obj)); } -static Property rtl8139_properties[] = { +static const Property rtl8139_properties[] = { DEFINE_NIC_PROPERTIES(RTL8139State, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 180ba5c791..a853c30fa2 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -788,7 +788,7 @@ static void smc91c111_realize(DeviceState *dev, Error **errp) /* ??? Save/restore. */ } -static Property smc91c111_properties[] = { +static const Property smc91c111_properties[] = { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 8af33d91b6..d381c041db 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -786,7 +786,7 @@ static target_ulong h_change_logical_lan_mac(PowerPCCPU *cpu, return H_SUCCESS; } -static Property spapr_vlan_properties[] = { +static const Property spapr_vlan_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprVioVlan, sdev), DEFINE_NIC_PROPERTIES(SpaprVioVlan, nicconf), DEFINE_PROP_BIT("use-rx-buffer-pools", SpaprVioVlan, diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 9ebff296c4..4af1afa733 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -497,7 +497,7 @@ static void stellaris_enet_realize(DeviceState *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } -static Property stellaris_enet_properties[] = { +static const Property stellaris_enet_properties[] = { DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 67087e9842..bcc7a18382 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -1420,7 +1420,7 @@ static void sungem_instance_init(Object *obj) DEVICE(obj)); } -static Property sungem_properties[] = { +static const Property sungem_properties[] = { DEFINE_NIC_PROPERTIES(SunGEMState, conf), /* Phy address should be 0 for most Apple machines except * for K2 in which case it's 1. Will be set by a machine diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 0e6c655a5b..86f472fcbe 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -177,7 +177,7 @@ struct SunHMEState { uint16_t miiregs[HME_MII_REGS_SIZE]; }; -static Property sunhme_properties[] = { +static const Property sunhme_properties[] = { DEFINE_NIC_PROPERTIES(SunHMEState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/tulip.c b/hw/net/tulip.c index 9df3e17162..f35b58a88c 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -1007,7 +1007,7 @@ static void tulip_instance_init(Object *obj) &pci_dev->qdev); } -static Property tulip_properties[] = { +static const Property tulip_properties[] = { DEFINE_NIC_PROPERTIES(TULIPState, c), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 6e8c51a2db..4fd1f9acca 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3985,7 +3985,7 @@ static const VMStateDescription vmstate_virtio_net = { .dev_unplug_pending = dev_unplug_pending, }; -static Property virtio_net_properties[] = { +static const Property virtio_net_properties[] = { DEFINE_PROP_BIT64("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true), DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features, diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8aa8c46228..f69547cad5 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2471,7 +2471,7 @@ static const VMStateDescription vmstate_vmxnet3 = { } }; -static Property vmxnet3_properties[] = { +static const Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), DEFINE_PROP_BIT("x-old-msi-offsets", VMXNET3State, compat_flags, VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false), diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 89487b49ba..5a5259150a 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -555,7 +555,7 @@ static void xen_netdev_unrealize(XenDevice *xendev) /* ------------------------------------------------------------- */ -static Property xen_netdev_properties[] = { +static const Property xen_netdev_properties[] = { DEFINE_NIC_PROPERTIES(XenNetDev, conf), DEFINE_PROP_INT32("idx", XenNetDev, dev, -1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index ffe3fc8dbe..6e0f96f485 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -414,7 +414,7 @@ static void xgmac_enet_realize(DeviceState *dev, Error **errp) s->conf.macaddr.a[0]; } -static Property xgmac_properties[] = { +static const Property xgmac_properties[] = { DEFINE_NIC_PROPERTIES(XgmacState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index faf27947b0..9d0c618e2f 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -996,7 +996,7 @@ static void xilinx_enet_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property xilinx_enet_properties[] = { +static const Property xilinx_enet_properties[] = { DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7), DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000), DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000), diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index bd81290808..9413731d20 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -251,7 +251,7 @@ static void xilinx_ethlite_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static Property xilinx_ethlite_properties[] = { +static const Property xilinx_ethlite_properties[] = { DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1), DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1), DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf), From 3fac046e4aafeecf973afaece3e1cde969b7871a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:30:22 +0000 Subject: [PATCH 0350/2892] hw/nubus: Constify all Property 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 --- hw/nubus/nubus-bridge.c | 2 +- hw/nubus/nubus-device.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index a42c86080f..83893e5a46 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -23,7 +23,7 @@ static void nubus_bridge_init(Object *obj) qdev_init_gpio_out(DEVICE(s), bus->irqs, NUBUS_IRQS); } -static Property nubus_bridge_properties[] = { +static const Property nubus_bridge_properties[] = { DEFINE_PROP_UINT16("slot-available-mask", NubusBridge, bus.slot_available_mask, 0xffff), DEFINE_PROP_END_OF_LIST() diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 26fbcf29a2..7cafc13427 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -107,7 +107,7 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) } } -static Property nubus_device_properties[] = { +static const Property nubus_device_properties[] = { DEFINE_PROP_INT32("slot", NubusDevice, slot, -1), DEFINE_PROP_STRING("romfile", NubusDevice, romfile), DEFINE_PROP_END_OF_LIST() From b32c22bf31dcf13076b6bc1fa9ba51965d038b2d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:31:13 +0000 Subject: [PATCH 0351/2892] hw/nvme: Constify all Property 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 --- hw/nvme/ctrl.c | 2 +- hw/nvme/ns.c | 2 +- hw/nvme/subsys.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index ec75419566..33a3062466 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8927,7 +8927,7 @@ static void nvme_exit(PCIDevice *pci_dev) memory_region_del_subregion(&n->bar0, &n->iomem); } -static Property nvme_props[] = { +static const Property nvme_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, namespace.blkconf), DEFINE_PROP_LINK("pmrdev", NvmeCtrl, pmr.dev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 526e15aa80..192b80f18d 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -799,7 +799,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) nvme_attach_ns(n, ns); } -static Property nvme_ns_props[] = { +static const Property nvme_ns_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf), DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false), DEFINE_PROP_BOOL("shared", NvmeNamespace, params.shared, true), diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 77deaf2c2c..3171c3888c 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -216,7 +216,7 @@ static void nvme_subsys_realize(DeviceState *dev, Error **errp) nvme_subsys_setup(subsys, errp); } -static Property nvme_subsystem_props[] = { +static const Property nvme_subsystem_props[] = { DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn), DEFINE_PROP_BOOL("fdp", NvmeSubsystem, params.fdp.enabled, false), DEFINE_PROP_SIZE("fdp.runs", NvmeSubsystem, params.fdp.runs, From 2839136833257542704695be10b19c77c927acc4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:31:56 +0000 Subject: [PATCH 0352/2892] hw/nvram: Constify all Property 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 --- hw/nvram/ds1225y.c | 2 +- hw/nvram/eeprom_at24c.c | 2 +- hw/nvram/fw_cfg.c | 6 +++--- hw/nvram/mac_nvram.c | 2 +- hw/nvram/nrf51_nvm.c | 2 +- hw/nvram/spapr_nvram.c | 2 +- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-efuse.c | 2 +- hw/nvram/xlnx-versal-efuse-cache.c | 2 +- hw/nvram/xlnx-versal-efuse-ctrl.c | 2 +- hw/nvram/xlnx-zynqmp-efuse.c | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index 6d510dcc68..19bf8d2091 100644 --- a/hw/nvram/ds1225y.c +++ b/hw/nvram/ds1225y.c @@ -142,7 +142,7 @@ static void nvram_sysbus_realize(DeviceState *dev, Error **errp) nvram_post_load(s, 0); } -static Property nvram_sysbus_properties[] = { +static const Property nvram_sysbus_properties[] = { DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index ec748e58e7..669920b2b9 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -234,7 +234,7 @@ void at24c_eeprom_reset(DeviceState *state) ee->haveaddr = 0; } -static Property at24c_eeprom_props[] = { +static const Property at24c_eeprom_props[] = { DEFINE_PROP_UINT32("rom-size", EEPROMState, rsize, 0), DEFINE_PROP_UINT8("address-size", EEPROMState, asize, 0), DEFINE_PROP_BOOL("writable", EEPROMState, writable, true), diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 97def3a88b..7461d99ff2 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1082,7 +1082,7 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) qemu_register_reset(fw_cfg_machine_reset, s); } -static Property fw_cfg_properties[] = { +static const Property fw_cfg_properties[] = { DEFINE_PROP_BOOL("acpi-mr-restore", FWCfgState, acpi_mr_restore, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1273,7 +1273,7 @@ static void fw_cfg_file_slots_allocate(FWCfgState *s, Error **errp) s->entry_order = g_new0(int, fw_cfg_max_entry(s)); } -static Property fw_cfg_io_properties[] = { +static const Property fw_cfg_io_properties[] = { DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled, true), DEFINE_PROP_UINT16("x-file-slots", FWCfgIoState, parent_obj.file_slots, @@ -1322,7 +1322,7 @@ static const TypeInfo fw_cfg_io_info = { }; -static Property fw_cfg_mem_properties[] = { +static const Property fw_cfg_mem_properties[] = { DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled, true), diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index e47e52a677..d62ad719c8 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -134,7 +134,7 @@ static void macio_nvram_unrealizefn(DeviceState *dev) g_free(s->data); } -static Property macio_nvram_properties[] = { +static const Property macio_nvram_properties[] = { DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0), DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0), DEFINE_PROP_DRIVE("drive", MacIONVRAMState, blk), diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c index b1f81752a3..236049462b 100644 --- a/hw/nvram/nrf51_nvm.c +++ b/hw/nvram/nrf51_nvm.c @@ -354,7 +354,7 @@ static void nrf51_nvm_reset(DeviceState *dev) memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); } -static Property nrf51_nvm_properties[] = { +static const Property nrf51_nvm_properties[] = { DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index bfd8aa367e..2251ff2f4c 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -252,7 +252,7 @@ static const VMStateDescription vmstate_spapr_nvram = { }, }; -static Property spapr_nvram_properties[] = { +static const Property spapr_nvram_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprNvram, sdev), DEFINE_PROP_DRIVE("drive", SpaprNvram, blk), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 1bc58e90ad..4fa528f048 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -520,7 +520,7 @@ static const VMStateDescription vmstate_bbram_ctrl = { } }; -static Property bbram_ctrl_props[] = { +static const Property bbram_ctrl_props[] = { DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index f7b849f7de..5dae9e8e9a 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -265,7 +265,7 @@ static const PropertyInfo efuse_prop_drive = { .release = efuse_prop_release_drive, }; -static Property efuse_properties[] = { +static const Property efuse_properties[] = { DEFINE_PROP("drive", XlnxEFuse, blk, efuse_prop_drive, BlockBackend *), DEFINE_PROP_UINT8("efuse-nr", XlnxEFuse, efuse_nr, 3), DEFINE_PROP_UINT32("efuse-size", XlnxEFuse, efuse_size, 64 * 32), diff --git a/hw/nvram/xlnx-versal-efuse-cache.c b/hw/nvram/xlnx-versal-efuse-cache.c index eaec64d785..1aea27afd3 100644 --- a/hw/nvram/xlnx-versal-efuse-cache.c +++ b/hw/nvram/xlnx-versal-efuse-cache.c @@ -83,7 +83,7 @@ static void efuse_cache_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property efuse_cache_props[] = { +static const Property efuse_cache_props[] = { DEFINE_PROP_LINK("efuse", XlnxVersalEFuseCache, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index 8252a5cabe..599aa126fb 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -743,7 +743,7 @@ static const VMStateDescription vmstate_efuse_ctrl = { } }; -static Property efuse_ctrl_props[] = { +static const Property efuse_ctrl_props[] = { DEFINE_PROP_LINK("efuse", XlnxVersalEFuseCtrl, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index 4e2d1b9d1e..af53187905 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -833,7 +833,7 @@ static const VMStateDescription vmstate_efuse = { } }; -static Property zynqmp_efuse_props[] = { +static const Property zynqmp_efuse_props[] = { DEFINE_PROP_LINK("efuse", XlnxZynqMPEFuse, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), From 196fd15f31d986e5a9c5da84988fa4597f30623b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:32:51 +0000 Subject: [PATCH 0353/2892] hw/pci-bridge: Constify all Property 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 --- hw/pci-bridge/cxl_downstream.c | 2 +- hw/pci-bridge/cxl_root_port.c | 2 +- hw/pci-bridge/cxl_upstream.c | 2 +- hw/pci-bridge/gen_pcie_root_port.c | 2 +- hw/pci-bridge/pci_bridge_dev.c | 2 +- hw/pci-bridge/pci_expander_bridge.c | 4 ++-- hw/pci-bridge/pcie_pci_bridge.c | 2 +- hw/pci-bridge/pcie_root_port.c | 2 +- hw/pci-bridge/xio3130_downstream.c | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index c347ac06f3..cfe50e60e9 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -212,7 +212,7 @@ static void cxl_dsp_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -static Property cxl_dsp_props[] = { +static const Property cxl_dsp_props[] = { DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot, speed, PCIE_LINK_SPEED_64), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 5e2156d7ba..5824ba3c75 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -199,7 +199,7 @@ static void cxl_rp_reset_hold(Object *obj, ResetType type) latch_registers(crp); } -static Property gen_rp_props[] = { +static const Property gen_rp_props[] = { DEFINE_PROP_UINT32("bus-reserve", CXLRootPort, res_reserve.bus, -1), DEFINE_PROP_SIZE("io-reserve", CXLRootPort, res_reserve.io, -1), DEFINE_PROP_SIZE("mem-reserve", CXLRootPort, res_reserve.mem_non_pref, -1), diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index 55f8b0053f..ef94aa3654 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -362,7 +362,7 @@ static void cxl_usp_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -static Property cxl_upstream_props[] = { +static const Property cxl_upstream_props[] = { DEFINE_PROP_UINT64("sn", CXLUpstreamPort, sn, UI64_NULL), DEFINE_PROP_STRING("cdat", CXLUpstreamPort, cxl_cstate.cdat.filename), DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CXLUpstreamPort, diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 784507c826..c319ca8263 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -128,7 +128,7 @@ static const VMStateDescription vmstate_rp_dev = { } }; -static Property gen_rp_props[] = { +static const Property gen_rp_props[] = { DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 8e7f926621..35a37e056a 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -168,7 +168,7 @@ static void qdev_pci_bridge_dev_reset(DeviceState *qdev) } } -static Property pci_bridge_dev_properties[] = { +static const Property pci_bridge_dev_properties[] = { /* Note: 0 is not a legal chassis number. */ DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, 0), diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 07d411cff5..01997c1ab3 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -420,7 +420,7 @@ static void pxb_dev_exitfn(PCIDevice *pci_dev) pxb_dev_list = g_list_remove(pxb_dev_list, pxb); } -static Property pxb_dev_properties[] = { +static const Property pxb_dev_properties[] = { /* Note: 0 is not a legal PXB bus number. */ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED), @@ -507,7 +507,7 @@ static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp) pxb_cxl_dev_reset(DEVICE(dev)); } -static Property pxb_cxl_dev_properties[] = { +static const Property pxb_cxl_dev_properties[] = { DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev, hdm_for_passthrough, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 6e8d7d9478..8834ff3dbf 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -124,7 +124,7 @@ static void pcie_pci_bridge_write_config(PCIDevice *d, shpc_cap_write_config(d, address, val, len); } -static Property pcie_pci_bridge_dev_properties[] = { +static const Property pcie_pci_bridge_dev_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 09a34786bc..a7f87a1bc4 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -148,7 +148,7 @@ static void rp_exit(PCIDevice *d) pci_bridge_exitfn(d); } -static Property rp_props[] = { +static const Property rp_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), DEFINE_PROP_BOOL("disable-acs", PCIESlot, disable_acs, false), diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index 473e2dd950..92e5fb72ec 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -134,7 +134,7 @@ static void xio3130_downstream_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -static Property xio3130_downstream_props[] = { +static const Property xio3130_downstream_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), DEFINE_PROP_END_OF_LIST() From 909a5c0afa1dd52e8145716fc36d12ba6b74721f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:35:23 +0000 Subject: [PATCH 0354/2892] hw/pci-host: Constify all Property 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 --- hw/pci-host/dino.c | 2 +- hw/pci-host/gpex.c | 2 +- hw/pci-host/grackle.c | 2 +- hw/pci-host/gt64120.c | 2 +- hw/pci-host/i440fx.c | 2 +- hw/pci-host/mv64361.c | 2 +- hw/pci-host/pnv_phb.c | 4 ++-- hw/pci-host/pnv_phb3.c | 2 +- hw/pci-host/pnv_phb4.c | 2 +- hw/pci-host/pnv_phb4_pec.c | 2 +- hw/pci-host/ppce500.c | 2 +- hw/pci-host/q35.c | 4 ++-- hw/pci-host/raven.c | 2 +- hw/pci-host/sabre.c | 2 +- hw/pci-host/uninorth.c | 2 +- hw/pci-host/versatile.c | 2 +- hw/pci-host/xilinx-pcie.c | 2 +- 17 files changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index 283fc0dc57..ead9893f21 100644 --- a/hw/pci-host/dino.c +++ b/hw/pci-host/dino.c @@ -492,7 +492,7 @@ static void dino_pcihost_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), dino_set_irq, DINO_IRQS); } -static Property dino_pcihost_properties[] = { +static const Property dino_pcihost_properties[] = { DEFINE_PROP_LINK("memory-as", DinoState, memory_as, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index e9cf455bf5..8a955ca130 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -147,7 +147,7 @@ static const char *gpex_host_root_bus_path(PCIHostState *host_bridge, return "0000:00"; } -static Property gpex_host_properties[] = { +static const Property gpex_host_properties[] = { /* * Permit CPU accesses to unmapped areas of the PIO and MMIO windows * (discarding writes and returning -1 for reads) rather than aborting. diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 8e589ff2c9..d64de73774 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -129,7 +129,7 @@ static char *grackle_ofw_unit_address(const SysBusDevice *dev) return g_strdup_printf("%x", s->ofw_addr); } -static Property grackle_properties[] = { +static const Property grackle_properties[] = { DEFINE_PROP_UINT32("ofw-addr", GrackleState, ofw_addr, -1), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index 14fc803d27..3c73ebe83f 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1274,7 +1274,7 @@ static const TypeInfo gt64120_pci_info = { }, }; -static Property gt64120_properties[] = { +static const Property gt64120_properties[] = { DEFINE_PROP_BOOL("cpu-little-endian", GT64120State, cpu_little_endian, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index 4f0a0438d7..40780fbc52 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -353,7 +353,7 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, return "0000:00"; } -static Property i440fx_props[] = { +static const Property i440fx_props[] = { DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState, pci_hole64_size, I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT), DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MEM_SIZE, I440FXState, diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 421c287eb0..2518d5abe6 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -98,7 +98,7 @@ static void mv64361_pcihost_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->irq, ARRAY_SIZE(s->irq)); } -static Property mv64361_pcihost_props[] = { +static const Property mv64361_pcihost_props[] = { DEFINE_PROP_UINT8("index", MV64361PCIState, index, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index d4c118d443..888f0786a0 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -183,7 +183,7 @@ static const char *pnv_phb_root_bus_path(PCIHostState *host_bridge, return phb->bus_path; } -static Property pnv_phb_properties[] = { +static const Property pnv_phb_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB, chip_id, 0), DEFINE_PROP_UINT32("version", PnvPHB, version, 0), @@ -302,7 +302,7 @@ static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) pci_config_set_interrupt_pin(pci->config, 0); } -static Property pnv_phb_root_port_properties[] = { +static const Property pnv_phb_root_port_properties[] = { DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 2a74dbe45f..529b33b5a2 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -1090,7 +1090,7 @@ void pnv_phb3_update_regions(PnvPHB3 *phb) pnv_phb3_check_all_m64s(phb); } -static Property pnv_phb3_properties[] = { +static const Property pnv_phb3_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 99991008c1..482fe25803 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1688,7 +1688,7 @@ static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno, } } -static Property pnv_phb4_properties[] = { +static const Property pnv_phb4_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index ce8e228f98..f8975403d3 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -283,7 +283,7 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static Property pnv_pec_properties[] = { +static const Property pnv_pec_properties[] = { DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index b70631045a..54071fc125 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -507,7 +507,7 @@ static void e500_host_bridge_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static Property pcihost_properties[] = { +static const Property pcihost_properties[] = { DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), DEFINE_PROP_UINT32("first_pin_irq", PPCE500PCIState, first_pin_irq, 0x1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index f3e713318e..af0b77ea1e 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -170,7 +170,7 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, * properties need to be initialized manually by * q35_host_initfn() after the object_initialize() call. */ -static Property q35_host_props[] = { +static const Property q35_host_props[] = { DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr, MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost, @@ -662,7 +662,7 @@ static void mch_realize(PCIDevice *d, Error **errp) OBJECT(&mch->smram)); } -static Property mch_props[] = { +static const Property mch_props[] = { DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes, 16), DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true), diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index a7dfddd69e..b0a4a669f5 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -422,7 +422,7 @@ static const TypeInfo raven_info = { }, }; -static Property raven_pcihost_properties[] = { +static const Property raven_pcihost_properties[] = { DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine, EM_NONE), DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name), diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index 1707feb951..623afed644 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -492,7 +492,7 @@ static char *sabre_ofw_unit_address(const SysBusDevice *dev) (uint32_t)(s->special_base & 0xffffffff)); } -static Property sabre_properties[] = { +static const Property sabre_properties[] = { DEFINE_PROP_UINT64("special-base", SabreState, special_base, 0), DEFINE_PROP_UINT64("mem-base", SabreState, mem_base, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index e4c1abd871..bd670cfa9d 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -423,7 +423,7 @@ static const TypeInfo unin_internal_pci_host_info = { }, }; -static Property pci_unin_main_pci_host_props[] = { +static const Property pci_unin_main_pci_host_props[] = { DEFINE_PROP_UINT32("ofw-addr", UNINHostState, ofw_addr, -1), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index d257acee17..5d59640691 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -498,7 +498,7 @@ static const TypeInfo versatile_pci_host_info = { }, }; -static Property pci_vpb_properties[] = { +static const Property pci_vpb_properties[] = { DEFINE_PROP_UINT8("broken-irq-mapping", PCIVPBState, irq_mapping_prop, PCI_VPB_IRQMAP_ASSUME_OK), DEFINE_PROP_END_OF_LIST() diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 24f691ea82..848403970b 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -156,7 +156,7 @@ static void xilinx_pcie_host_init(Object *obj) qdev_prop_set_bit(DEVICE(root), "multifunction", false); } -static Property xilinx_pcie_host_props[] = { +static const Property xilinx_pcie_host_props[] = { DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0), DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0), DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 * MiB), From 8a1852f8c20c444b8869478bf6411e3d0befc37b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:36:46 +0000 Subject: [PATCH 0355/2892] hw/pci: Constify all Property 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 --- hw/pci/pci.c | 2 +- hw/pci/pci_bridge.c | 2 +- hw/pci/pci_host.c | 2 +- hw/pci/pcie_port.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index bf0a1840db..90248481b1 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -81,7 +81,7 @@ static const PropertyInfo prop_pci_busnr = { .get = prop_pci_busnr_get, }; -static Property pci_props[] = { +static const Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 2c7bb1a525..dd4fd3674f 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -477,7 +477,7 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, return 0; } -static Property pci_bridge_properties[] = { +static const Property pci_bridge_properties[] = { DEFINE_PROP_BOOL("x-pci-express-writeable-slt-bug", PCIBridge, pcie_writeable_slt_bug, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index dfe6fe6184..321e7be709 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -240,7 +240,7 @@ const VMStateDescription vmstate_pcihost = { } }; -static Property pci_host_properties_common[] = { +static const Property pci_host_properties_common[] = { DEFINE_PROP_BOOL("x-config-reg-migration-enabled", PCIHostState, mig_enabled, true), DEFINE_PROP_BOOL(PCI_HOST_BYPASS_IOMMU, PCIHostState, bypass_iommu, false), diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 9f978ba164..bac2822e98 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -111,7 +111,7 @@ void pcie_chassis_del_slot(PCIESlot *s) QLIST_REMOVE(s, next); } -static Property pcie_port_props[] = { +static const Property pcie_port_props[] = { DEFINE_PROP_UINT8("port", PCIEPort, port, 0), DEFINE_PROP_UINT16("aer_log_max", PCIEPort, parent_obj.parent_obj.exp.aer_log.log_max, @@ -204,7 +204,7 @@ static const TypeInfo pcie_port_type_info = { .class_init = pcie_port_class_init, }; -static Property pcie_slot_props[] = { +static const Property pcie_slot_props[] = { DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), DEFINE_PROP_BOOL("hotplug", PCIESlot, hotplug, true), From 90f5755e8a6a791fa67fa860a947770326b54961 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:37:35 +0000 Subject: [PATCH 0356/2892] hw/ppc: Constify all Property 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 --- hw/ppc/pnv.c | 2 +- hw/ppc/pnv_adu.c | 2 +- hw/ppc/pnv_chiptod.c | 2 +- hw/ppc/pnv_core.c | 4 ++-- hw/ppc/pnv_homer.c | 2 +- hw/ppc/pnv_i2c.c | 2 +- hw/ppc/pnv_lpc.c | 2 +- hw/ppc/pnv_pnor.c | 2 +- hw/ppc/pnv_psi.c | 2 +- hw/ppc/ppc405_uc.c | 2 +- hw/ppc/ppc440_uc.c | 2 +- hw/ppc/ppc4xx_devs.c | 4 ++-- hw/ppc/ppc4xx_sdram.c | 4 ++-- hw/ppc/prep_systemio.c | 2 +- hw/ppc/rs6000_mc.c | 2 +- hw/ppc/spapr_cpu_core.c | 2 +- hw/ppc/spapr_nvdimm.c | 2 +- hw/ppc/spapr_pci.c | 2 +- hw/ppc/spapr_rng.c | 2 +- hw/ppc/spapr_tpm_proxy.c | 2 +- 20 files changed, 23 insertions(+), 23 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index f0f0d7567d..b90a052ce0 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2422,7 +2422,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) } } -static Property pnv_chip_properties[] = { +static const Property pnv_chip_properties[] = { DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0), DEFINE_PROP_UINT64("ram-start", PnvChip, ram_start, 0), DEFINE_PROP_UINT64("ram-size", PnvChip, ram_size, 0), diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c index f636dedf79..646736f7e9 100644 --- a/hw/ppc/pnv_adu.c +++ b/hw/ppc/pnv_adu.c @@ -185,7 +185,7 @@ static void pnv_adu_realize(DeviceState *dev, Error **errp) PNV9_XSCOM_ADU_SIZE); } -static Property pnv_adu_properties[] = { +static const Property pnv_adu_properties[] = { DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index 1e41fe557a..840ef23128 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -450,7 +450,7 @@ static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt, return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); } -static Property pnv_chiptod_properties[] = { +static const Property pnv_chiptod_properties[] = { DEFINE_PROP_BOOL("primary", PnvChipTOD, primary, false), DEFINE_PROP_BOOL("secondary", PnvChipTOD, secondary, false), DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *), diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index e6b02294b1..22864c92f3 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -435,7 +435,7 @@ static void pnv_core_unrealize(DeviceState *dev) g_free(pc->threads); } -static Property pnv_core_properties[] = { +static const Property pnv_core_properties[] = { DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0), DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0), DEFINE_PROP_BOOL("big-core", PnvCore, big_core, false), @@ -693,7 +693,7 @@ static void pnv_quad_power10_realize(DeviceState *dev, Error **errp) pqc->xscom_qme_size); } -static Property pnv_quad_properties[] = { +static const Property pnv_quad_properties[] = { DEFINE_PROP_UINT32("quad-id", PnvQuad, quad_id, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index f9a203d11d..b1f83e2cf2 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -412,7 +412,7 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) hmrc->homer_size); } -static Property pnv_homer_properties[] = { +static const Property pnv_homer_properties[] = { DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index eec5047ce8..4bd61abeed 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -543,7 +543,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(DEVICE(dev), &i2c->psi_irq, 1); } -static Property pnv_i2c_properties[] = { +static const Property pnv_i2c_properties[] = { DEFINE_PROP_LINK("chip", PnvI2C, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_UINT32("engine", PnvI2C, engine, 1), DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 8c203d2059..4d47167163 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -828,7 +828,7 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1); } -static Property pnv_lpc_properties[] = { +static const Property pnv_lpc_properties[] = { DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 6280408299..eed6d32650 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -112,7 +112,7 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) TYPE_PNV_PNOR, s->size); } -static Property pnv_pnor_properties[] = { +static const Property pnv_pnor_properties[] = { DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB), DEFINE_PROP_DRIVE("drive", PnvPnor, blk), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 37c56882b8..e7d6ceee99 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -552,7 +552,7 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) return 0; } -static Property pnv_psi_properties[] = { +static const Property pnv_psi_properties[] = { DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 58cbd0507a..801f97811f 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -965,7 +965,7 @@ static void ppc405_cpc_realize(DeviceState *dev, Error **errp) &dcr_read_epcpc, &dcr_write_epcpc); } -static Property ppc405_cpc_properties[] = { +static const Property ppc405_cpc_properties[] = { DEFINE_PROP_UINT32("sys-clk", Ppc405CpcState, sysclk, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 1312aa2080..05a5ef6f77 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1020,7 +1020,7 @@ static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp) ppc460ex_pcie_register_dcrs(s); } -static Property ppc460ex_pcie_props[] = { +static const Property ppc460ex_pcie_props[] = { DEFINE_PROP_INT32("busnum", PPC460EXPCIEState, num, -1), DEFINE_PROP_INT32("dcrn-base", PPC460EXPCIEState, dcrn_base, -1), DEFINE_PROP_LINK("cpu", PPC460EXPCIEState, cpu, TYPE_POWERPC_CPU, diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index db8f6b9497..530a392f2a 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -231,7 +231,7 @@ static void ppc4xx_mal_finalize(Object *obj) g_free(mal->txctpr); } -static Property ppc4xx_mal_properties[] = { +static const Property ppc4xx_mal_properties[] = { DEFINE_PROP_UINT8("txc-num", Ppc4xxMalState, txcnum, 0), DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), DEFINE_PROP_END_OF_LIST(), @@ -539,7 +539,7 @@ bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu, return sysbus_realize(SYS_BUS_DEVICE(dev), errp); } -static Property ppc4xx_dcr_properties[] = { +static const Property ppc4xx_dcr_properties[] = { DEFINE_PROP_LINK("cpu", Ppc4xxDcrDeviceState, cpu, TYPE_POWERPC_CPU, PowerPCCPU *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index 2ee21f1ca7..6cfb07a11f 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -425,7 +425,7 @@ static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp) s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write); } -static Property ppc4xx_sdram_ddr_props[] = { +static const Property ppc4xx_sdram_ddr_props[] = { DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), @@ -710,7 +710,7 @@ static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp) s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); } -static Property ppc4xx_sdram_ddr2_props[] = { +static const Property ppc4xx_sdram_ddr2_props[] = { DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index 4d3a251ed8..ca475c69f4 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -285,7 +285,7 @@ static const VMStateDescription vmstate_prep_systemio = { }, }; -static Property prep_systemio_properties[] = { +static const Property prep_systemio_properties[] = { DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0), DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), DEFINE_PROP_END_OF_LIST() diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index 07b0b664d9..bee9bc62d4 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -207,7 +207,7 @@ static const VMStateDescription vmstate_rs6000mc = { }, }; -static Property rs6000mc_properties[] = { +static const Property rs6000mc_properties[] = { DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0), DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), DEFINE_PROP_END_OF_LIST() diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 135f86a622..88d743a3c3 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -361,7 +361,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) } } -static Property spapr_cpu_core_properties[] = { +static const Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 7d2dfe5e3d..2ef6f29f3d 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -884,7 +884,7 @@ static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) vmstate_unregister(NULL, &vmstate_spapr_nvdimm_states, dimm); } -static Property spapr_nvdimm_properties[] = { +static const Property spapr_nvdimm_properties[] = { #ifdef CONFIG_LIBPMEM DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), #endif diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 7e24084673..3edff528ca 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -2033,7 +2033,7 @@ static void spapr_phb_reset(DeviceState *qdev) g_hash_table_remove_all(sphb->msi); } -static Property spapr_phb_properties[] = { +static const Property spapr_phb_properties[] = { DEFINE_PROP_UINT32("index", SpaprPhbState, index, -1), DEFINE_PROP_UINT64("mem_win_size", SpaprPhbState, mem_win_size, SPAPR_PCI_MEM32_WIN_SIZE), diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index c2fda7ad20..51c3a54d45 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -130,7 +130,7 @@ static void spapr_rng_realize(DeviceState *dev, Error **errp) } } -static Property spapr_rng_properties[] = { +static const Property spapr_rng_properties[] = { DEFINE_PROP_BOOL("use-kvm", SpaprRngState, use_kvm, false), DEFINE_PROP_LINK("rng", SpaprRngState, backend, TYPE_RNG_BACKEND, RngBackend *), diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index e10af35a18..37521b88cb 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -145,7 +145,7 @@ static void spapr_tpm_proxy_unrealize(DeviceState *d) qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy); } -static Property spapr_tpm_proxy_properties[] = { +static const Property spapr_tpm_proxy_properties[] = { DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), DEFINE_PROP_END_OF_LIST(), }; From 4ef4c30d2ef6d1dcfc58512e6ed273244dcd72cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:39:35 +0000 Subject: [PATCH 0357/2892] hw/remote: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Jagannathan Raman Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/remote/proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 302a0a4d4d..6f84fdd3fa 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -191,7 +191,7 @@ static void pci_proxy_write_config(PCIDevice *d, uint32_t addr, uint32_t val, config_op_send(PCI_PROXY_DEV(d), addr, &val, len, MPQEMU_CMD_PCI_CFGWRITE); } -static Property proxy_properties[] = { +static const Property proxy_properties[] = { DEFINE_PROP_STRING("fd", PCIProxyDev, fd), DEFINE_PROP_END_OF_LIST(), }; From 766bade2da0f9b1338cbe72aea114cd0922acfc5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:43:40 +0000 Subject: [PATCH 0358/2892] hw/riscv: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/riscv/opentitan.c | 2 +- hw/riscv/riscv-iommu-pci.c | 2 +- hw/riscv/riscv-iommu.c | 2 +- hw/riscv/riscv_hart.c | 2 +- hw/riscv/sifive_u.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index e2830e9dc2..8ce85ea9f7 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -306,7 +306,7 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_DEV_IBEX_CFG].base, memmap[IBEX_DEV_IBEX_CFG].size); } -static Property lowrisc_ibex_soc_props[] = { +static const Property lowrisc_ibex_soc_props[] = { DEFINE_PROP_UINT32("resetvec", LowRISCIbexSoCState, resetvec, 0x20000400), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index a42242532d..a695314bbe 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -157,7 +157,7 @@ static void riscv_iommu_pci_init(Object *obj) iommu->icvec_avail_vectors = RISCV_IOMMU_PCI_ICVEC_VECTORS; } -static Property riscv_iommu_pci_properties[] = { +static const Property riscv_iommu_pci_properties[] = { DEFINE_PROP_UINT16("vendor-id", RISCVIOMMUStatePci, vendor_id, PCI_VENDOR_ID_REDHAT), DEFINE_PROP_UINT16("device-id", RISCVIOMMUStatePci, device_id, diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index bbc95425b3..07fed36986 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2235,7 +2235,7 @@ static void riscv_iommu_unrealize(DeviceState *dev) g_hash_table_unref(s->ctx_cache); } -static Property riscv_iommu_properties[] = { +static const Property riscv_iommu_properties[] = { DEFINE_PROP_UINT32("version", RISCVIOMMUState, version, RISCV_IOMMU_SPEC_DOT_VER), DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0), diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 613ea2aaa0..0df454772f 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -27,7 +27,7 @@ #include "hw/qdev-properties.h" #include "hw/riscv/riscv_hart.h" -static Property riscv_harts_props[] = { +static const Property riscv_harts_props[] = { DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1), DEFINE_PROP_UINT32("hartid-base", RISCVHartArrayState, hartid_base, 0), DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index c5e74126b1..124ffd4842 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -936,7 +936,7 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_QSPI2_IRQ)); } -static Property sifive_u_soc_props[] = { +static const Property sifive_u_soc_props[] = { DEFINE_PROP_UINT32("serial", SiFiveUSoCState, serial, OTP_SERIAL), DEFINE_PROP_STRING("cpu-type", SiFiveUSoCState, cpu_type), DEFINE_PROP_END_OF_LIST() From 93c9015bc5cc14e3d944ac5c7f8aba9e2960bf06 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:44:13 +0000 Subject: [PATCH 0359/2892] hw/rtc: Constify all Property 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 --- hw/rtc/allwinner-rtc.c | 2 +- hw/rtc/goldfish_rtc.c | 2 +- hw/rtc/m48t59-isa.c | 2 +- hw/rtc/m48t59.c | 2 +- hw/rtc/mc146818rtc.c | 2 +- hw/rtc/pl031.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 1057d6a57f..838db72136 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -311,7 +311,7 @@ static const VMStateDescription allwinner_rtc_vmstate = { } }; -static Property allwinner_rtc_properties[] = { +static const Property allwinner_rtc_properties[] = { DEFINE_PROP_INT32("base-year", AwRtcState, base_year, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index a6dfbf89f3..389f192efa 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -286,7 +286,7 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); } -static Property goldfish_rtc_properties[] = { +static const Property goldfish_rtc_properties[] = { DEFINE_PROP_BOOL("big-endian", GoldfishRTCState, big_endian, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index b642b82680..51f80d27ef 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -77,7 +77,7 @@ static void m48txx_isa_toggle_lock(Nvram *obj, int lock) m48t59_toggle_lock(&d->state, lock); } -static Property m48t59_isa_properties[] = { +static const Property m48t59_isa_properties[] = { DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), DEFINE_PROP_UINT8("irq", M48txxISAState, isairq, 8), diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 90299ea56f..5a2c7b4abd 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -618,7 +618,7 @@ static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) m48t59_toggle_lock(&d->state, lock); } -static Property m48t59_sysbus_properties[] = { +static const Property m48t59_sysbus_properties[] = { DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 8ccee9a385..973ed9914d 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -960,7 +960,7 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, return s; } -static Property mc146818rtc_properties[] = { +static const Property mc146818rtc_properties[] = { DEFINE_PROP_INT32("base_year", MC146818RtcState, base_year, 1980), DEFINE_PROP_UINT16("iobase", MC146818RtcState, io_base, RTC_ISA_BASE), DEFINE_PROP_UINT8("irq", MC146818RtcState, isairq, RTC_ISA_IRQ), diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index 563bb4b446..1dc8e6e00f 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -319,7 +319,7 @@ static const VMStateDescription vmstate_pl031 = { } }; -static Property pl031_properties[] = { +static const Property pl031_properties[] = { /* * True to correctly migrate the tick offset of the RTC. False to * obtain backward migration compatibility with older QEMU versions, From 521e2be94d4e1cebe0e9387bd1124d5148b69da2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:44:52 +0000 Subject: [PATCH 0360/2892] hw/rx: Constify all Property 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 --- hw/rx/rx62n.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index 560f53a58a..dfa27bc94e 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -257,7 +257,7 @@ static void rx62n_realize(DeviceState *dev, Error **errp) register_sci(s, 0); } -static Property rx62n_properties[] = { +static const Property rx62n_properties[] = { DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false), From 6048081263f9baa4550dd7c13b3d9a470a55eef7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:45:40 +0000 Subject: [PATCH 0361/2892] hw/s390x: Constify all Property 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 --- hw/s390x/ccw-device.c | 2 +- hw/s390x/css-bridge.c | 2 +- hw/s390x/ipl.c | 2 +- hw/s390x/s390-pci-bus.c | 2 +- hw/s390x/s390-skeys.c | 2 +- hw/s390x/s390-stattrib.c | 2 +- hw/s390x/vhost-scsi-ccw.c | 2 +- hw/s390x/vhost-user-fs-ccw.c | 2 +- hw/s390x/vhost-vsock-ccw.c | 2 +- hw/s390x/virtio-ccw-9p.c | 2 +- hw/s390x/virtio-ccw-balloon.c | 2 +- hw/s390x/virtio-ccw-blk.c | 2 +- hw/s390x/virtio-ccw-crypto.c | 2 +- hw/s390x/virtio-ccw-gpu.c | 2 +- hw/s390x/virtio-ccw-input.c | 2 +- hw/s390x/virtio-ccw-net.c | 2 +- hw/s390x/virtio-ccw-rng.c | 2 +- hw/s390x/virtio-ccw-scsi.c | 2 +- hw/s390x/virtio-ccw-serial.c | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 30f2fb486f..0d10c3ed55 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -81,7 +81,7 @@ const PropertyInfo ccw_loadparm = { .set = ccw_device_set_loadparm, }; -static Property ccw_device_properties[] = { +static const Property ccw_device_properties[] = { DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 8657ff7bf4..860a04a7da 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -120,7 +120,7 @@ VirtualCssBus *virtual_css_bus_init(void) /***************** Virtual-css Bus Bridge Device ********************/ -static Property virtual_css_bridge_properties[] = { +static const Property virtual_css_bridge_properties[] = { DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 30734661ad..88a97f0085 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -291,7 +291,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) qemu_register_reset(resettable_cold_reset_fn, dev); } -static Property s390_ipl_properties[] = { +static const Property s390_ipl_properties[] = { DEFINE_PROP_STRING("kernel", S390IPLState, kernel), DEFINE_PROP_STRING("initrd", S390IPLState, initrd), DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 40b2567aa7..22e6be67af 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1481,7 +1481,7 @@ static const PropertyInfo s390_pci_fid_propinfo = { #define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t) -static Property s390_pci_device_properties[] = { +static const Property s390_pci_device_properties[] = { DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED), DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid), DEFINE_PROP_STRING("target", S390PCIBusDevice, target), diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index bf22d6863e..6d0a47ed73 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -475,7 +475,7 @@ static void s390_skeys_realize(DeviceState *dev, Error **errp) } } -static Property s390_skeys_props[] = { +static const Property s390_skeys_props[] = { DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index c4259b5327..6c69c01e1f 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -360,7 +360,7 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) &savevm_s390_stattrib_handlers, dev); } -static Property s390_stattrib_props[] = { +static const Property s390_stattrib_props[] = { DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/vhost-scsi-ccw.c b/hw/s390x/vhost-scsi-ccw.c index 40dc14bbc7..0be0f8a82c 100644 --- a/hw/s390x/vhost-scsi-ccw.c +++ b/hw/s390x/vhost-scsi-ccw.c @@ -41,7 +41,7 @@ static void vhost_ccw_scsi_instance_init(Object *obj) TYPE_VHOST_SCSI); } -static Property vhost_ccw_scsi_properties[] = { +static const Property vhost_ccw_scsi_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/s390x/vhost-user-fs-ccw.c b/hw/s390x/vhost-user-fs-ccw.c index 6c6f269293..934378aaec 100644 --- a/hw/s390x/vhost-user-fs-ccw.c +++ b/hw/s390x/vhost-user-fs-ccw.c @@ -23,7 +23,7 @@ typedef struct VHostUserFSCcw { OBJECT_CHECK(VHostUserFSCcw, (obj), TYPE_VHOST_USER_FS_CCW) -static Property vhost_user_fs_ccw_properties[] = { +static const Property vhost_user_fs_ccw_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 07845a9a00..3ba4008b4b 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -22,7 +22,7 @@ struct VHostVSockCCWState { VHostVSock vdev; }; -static Property vhost_vsock_ccw_properties[] = { +static const Property vhost_vsock_ccw_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c index 6f931f5994..c10b084d40 100644 --- a/hw/s390x/virtio-ccw-9p.c +++ b/hw/s390x/virtio-ccw-9p.c @@ -41,7 +41,7 @@ static void virtio_ccw_9p_instance_init(Object *obj) TYPE_VIRTIO_9P); } -static Property virtio_ccw_9p_properties[] = { +static const Property virtio_ccw_9p_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c index 44287b9bbe..bbbed494b3 100644 --- a/hw/s390x/virtio-ccw-balloon.c +++ b/hw/s390x/virtio-ccw-balloon.c @@ -46,7 +46,7 @@ static void virtio_ccw_balloon_instance_init(Object *obj) "guest-stats-polling-interval"); } -static Property virtio_ccw_balloon_properties[] = { +static const Property virtio_ccw_balloon_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 2364432c6e..3182851234 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -43,7 +43,7 @@ static void virtio_ccw_blk_instance_init(Object *obj) "bootindex"); } -static Property virtio_ccw_blk_properties[] = { +static const Property virtio_ccw_blk_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c index 0fa2f89443..b4cd7605c9 100644 --- a/hw/s390x/virtio-ccw-crypto.c +++ b/hw/s390x/virtio-ccw-crypto.c @@ -44,7 +44,7 @@ static void virtio_ccw_crypto_instance_init(Object *obj) TYPE_VIRTIO_CRYPTO); } -static Property virtio_ccw_crypto_properties[] = { +static const Property virtio_ccw_crypto_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index 0642c5281d..c44dc2d355 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -42,7 +42,7 @@ static void virtio_ccw_gpu_instance_init(Object *obj) TYPE_VIRTIO_GPU); } -static Property virtio_ccw_gpu_properties[] = { +static const Property virtio_ccw_gpu_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c index 61a07ba38d..040a9e04a9 100644 --- a/hw/s390x/virtio-ccw-input.c +++ b/hw/s390x/virtio-ccw-input.c @@ -43,7 +43,7 @@ static void virtio_ccw_input_realize(VirtioCcwDevice *ccw_dev, Error **errp) qdev_realize(vdev, BUS(&ccw_dev->bus), errp); } -static Property virtio_ccw_input_properties[] = { +static const Property virtio_ccw_input_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index a4a3f65c7e..c41d347034 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -46,7 +46,7 @@ static void virtio_ccw_net_instance_init(Object *obj) "bootindex"); } -static Property virtio_ccw_net_properties[] = { +static const Property virtio_ccw_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c index a3fffb5138..c9a15c4eb6 100644 --- a/hw/s390x/virtio-ccw-rng.c +++ b/hw/s390x/virtio-ccw-rng.c @@ -43,7 +43,7 @@ static void virtio_ccw_rng_instance_init(Object *obj) TYPE_VIRTIO_RNG); } -static Property virtio_ccw_rng_properties[] = { +static const Property virtio_ccw_rng_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c index d003f89f43..bec9a73518 100644 --- a/hw/s390x/virtio-ccw-scsi.c +++ b/hw/s390x/virtio-ccw-scsi.c @@ -53,7 +53,7 @@ static void virtio_ccw_scsi_instance_init(Object *obj) TYPE_VIRTIO_SCSI); } -static Property virtio_ccw_scsi_properties[] = { +static const Property virtio_ccw_scsi_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index 8f8d2302f8..037d4f9db1 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -53,7 +53,7 @@ static void virtio_ccw_serial_instance_init(Object *obj) TYPE_VIRTIO_SERIAL); } -static Property virtio_ccw_serial_properties[] = { +static const Property virtio_ccw_serial_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, From ec7630a2a1ecb200bfb087dffcbf5437291edfc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:47:35 +0000 Subject: [PATCH 0362/2892] hw/scsi: Constify all Property 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 --- hw/scsi/megasas.c | 6 +++--- hw/scsi/mptsas.c | 2 +- hw/scsi/scsi-bus.c | 2 +- hw/scsi/scsi-disk.c | 6 +++--- hw/scsi/scsi-generic.c | 2 +- hw/scsi/spapr_vscsi.c | 2 +- hw/scsi/vhost-scsi.c | 2 +- hw/scsi/vhost-user-scsi.c | 2 +- hw/scsi/virtio-scsi.c | 2 +- hw/scsi/vmw_pvscsi.c | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index df58aeb995..8323cd18e3 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2448,7 +2448,7 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), &megasas_scsi_info); } -static Property megasas_properties_gen1[] = { +static const Property megasas_properties_gen1[] = { DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, @@ -2462,7 +2462,7 @@ static Property megasas_properties_gen1[] = { DEFINE_PROP_END_OF_LIST(), }; -static Property megasas_properties_gen2[] = { +static const Property megasas_properties_gen2[] = { DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, @@ -2487,7 +2487,7 @@ typedef struct MegasasInfo { int mmio_bar; int osts; const VMStateDescription *vmsd; - Property *props; + const Property *props; InterfaceInfo *interfaces; } MegasasInfo; diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index c6bc3479e9..a06113d908 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1410,7 +1410,7 @@ static const VMStateDescription vmstate_mptsas = { } }; -static Property mptsas_properties[] = { +static const Property mptsas_properties[] = { DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), /* TODO: test MSI support under Windows */ DEFINE_PROP_ON_OFF_AUTO("msi", MPTSASState, msi, ON_OFF_AUTO_AUTO), diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 53eff5dd3d..2f1678d51e 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1943,7 +1943,7 @@ const VMStateDescription vmstate_scsi_device = { } }; -static Property scsi_props[] = { +static const Property scsi_props[] = { DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 7f13b0588f..a47b80907f 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3207,7 +3207,7 @@ static const TypeInfo scsi_disk_base_info = { DEFINE_PROP_BOOL("migrate-emulated-scsi-request", SCSIDiskState, migrate_emulated_scsi_request, true) -static Property scsi_hd_properties[] = { +static const Property scsi_hd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, features, SCSI_DISK_F_REMOVABLE, false), @@ -3267,7 +3267,7 @@ static const TypeInfo scsi_hd_info = { .class_init = scsi_hd_class_initfn, }; -static Property scsi_cd_properties[] = { +static const Property scsi_cd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), @@ -3310,7 +3310,7 @@ static const TypeInfo scsi_cd_info = { }; #ifdef __linux__ -static Property scsi_block_properties[] = { +static const Property scsi_block_properties[] = { DEFINE_BLOCK_ERROR_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 76f04a5ee8..d7ae7549d0 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -772,7 +772,7 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); } -static Property scsi_generic_properties[] = { +static const Property scsi_generic_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk), DEFINE_PROP_BOOL("share-rw", SCSIDevice, conf.share_rw, false), DEFINE_PROP_UINT32("io_timeout", SCSIDevice, io_timeout, diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index c75a6c8807..7c55e4d40f 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1250,7 +1250,7 @@ static int spapr_vscsi_devnode(SpaprVioDevice *dev, void *fdt, int node_off) return 0; } -static Property spapr_vscsi_properties[] = { +static const Property spapr_vscsi_properties[] = { DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 22d16dc26b..effb8dab1f 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -343,7 +343,7 @@ static struct vhost_dev *vhost_scsi_get_vhost(VirtIODevice *vdev) return &vsc->dev; } -static Property vhost_scsi_properties[] = { +static const Property vhost_scsi_properties[] = { DEFINE_PROP_STRING("vhostfd", VirtIOSCSICommon, conf.vhostfd), DEFINE_PROP_STRING("wwpn", VirtIOSCSICommon, conf.wwpn), DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 55e4be5b34..d5265c57bc 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -341,7 +341,7 @@ static void vhost_user_scsi_unrealize(DeviceState *dev) virtio_scsi_common_unrealize(dev); } -static Property vhost_user_scsi_properties[] = { +static const Property vhost_user_scsi_properties[] = { DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev), DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 6637cfeaf5..c0a4f1a620 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -1285,7 +1285,7 @@ static void virtio_scsi_device_unrealize(DeviceState *dev) qemu_mutex_destroy(&s->tmf_bh_lock); } -static Property virtio_scsi_properties[] = { +static const Property virtio_scsi_properties[] = { DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, VIRTIO_SCSI_AUTO_NUM_QUEUES), DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSI, diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 57761b5594..46cec531cc 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1296,7 +1296,7 @@ static const VMStateDescription vmstate_pvscsi = { } }; -static Property pvscsi_properties[] = { +static const Property pvscsi_properties[] = { DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags, PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false), From 2ba395a5dfc9f919731f8877e0ae8a5b54394cd5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:48:21 +0000 Subject: [PATCH 0363/2892] hw/sd: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/sd/allwinner-sdhost.c | 2 +- hw/sd/aspeed_sdhci.c | 2 +- hw/sd/sd.c | 6 +++--- hw/sd/sdhci-pci.c | 2 +- hw/sd/sdhci.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index bcfb4c1322..be39ec2e71 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -808,7 +808,7 @@ static const VMStateDescription vmstate_allwinner_sdhost = { } }; -static Property allwinner_sdhost_properties[] = { +static const Property allwinner_sdhost_properties[] = { DEFINE_PROP_LINK("dma-memory", AwSdHostState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index f82b05397e..99703f1842 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -204,7 +204,7 @@ static const VMStateDescription vmstate_aspeed_sdhci = { }, }; -static Property aspeed_sdhci_properties[] = { +static const Property aspeed_sdhci_properties[] = { DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/sd/sd.c b/hw/sd/sd.c index f9bd03f3fd..b994ef581e 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2798,18 +2798,18 @@ static void emmc_realize(DeviceState *dev, Error **errp) sd_realize(dev, errp); } -static Property sdmmc_common_properties[] = { +static const Property sdmmc_common_properties[] = { DEFINE_PROP_DRIVE("drive", SDState, blk), DEFINE_PROP_END_OF_LIST() }; -static Property sd_properties[] = { +static const Property sd_properties[] = { DEFINE_PROP_UINT8("spec_version", SDState, spec_version, SD_PHY_SPECv3_01_VERS), DEFINE_PROP_END_OF_LIST() }; -static Property emmc_properties[] = { +static const Property emmc_properties[] = { DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), DEFINE_PROP_END_OF_LIST() diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index 9b7bee8b3f..83892a7a15 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -22,7 +22,7 @@ #include "hw/sd/sdhci.h" #include "sdhci-internal.h" -static Property sdhci_pci_properties[] = { +static const Property sdhci_pci_properties[] = { DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 37875c02c3..e697ee05b3 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1544,7 +1544,7 @@ void sdhci_common_class_init(ObjectClass *klass, void *data) /* --- qdev SysBus --- */ -static Property sdhci_sysbus_properties[] = { +static const Property sdhci_sysbus_properties[] = { DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk, false), From d2616fe9616c1fd34e36c4b95c7657695f5e8a83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:48:50 +0000 Subject: [PATCH 0364/2892] hw/sparc: Constify all Property 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 --- hw/sparc/sun4m_iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index 6f765e97e4..3d6fcdf576 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -368,7 +368,7 @@ static void iommu_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static Property iommu_properties[] = { +static const Property iommu_properties[] = { DEFINE_PROP_UINT32("version", IOMMUState, version, 0), DEFINE_PROP_END_OF_LIST(), }; From 4aa3a8a89af9fced00269d5781c78e23f79a333b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:49:26 +0000 Subject: [PATCH 0365/2892] hw/sparc64: Constify all Property 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 --- hw/sparc64/sun4u.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 541c7f74fa..7088ac273e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -374,7 +374,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); } -static Property ebus_properties[] = { +static const Property ebus_properties[] = { DEFINE_PROP_UINT64("console-serial-base", EbusState, console_serial_base, 0), DEFINE_PROP_END_OF_LIST(), @@ -532,7 +532,7 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size) sysbus_mmio_map(s, 0, addr); } -static Property ram_properties[] = { +static const Property ram_properties[] = { DEFINE_PROP_UINT64("size", RamDevice, size, 0), DEFINE_PROP_END_OF_LIST(), }; From dc418eb22028d1f6181e2436c27fcdc3ad0b56a1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:51:05 +0000 Subject: [PATCH 0366/2892] hw/ssi: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/ssi/aspeed_smc.c | 4 ++-- hw/ssi/ibex_spi_host.c | 2 +- hw/ssi/npcm7xx_fiu.c | 2 +- hw/ssi/pnv_spi.c | 2 +- hw/ssi/sifive_spi.c | 2 +- hw/ssi/ssi.c | 2 +- hw/ssi/xilinx_spi.c | 2 +- hw/ssi/xilinx_spips.c | 4 ++-- hw/ssi/xlnx-versal-ospi.c | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 033cbbb59b..bbdd4e4786 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1287,7 +1287,7 @@ static const VMStateDescription vmstate_aspeed_smc = { } }; -static Property aspeed_smc_properties[] = { +static const Property aspeed_smc_properties[] = { DEFINE_PROP_BOOL("inject-failure", AspeedSMCState, inject_failure, false), DEFINE_PROP_UINT64("dram-base", AspeedSMCState, dram_base, 0), DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr, @@ -1336,7 +1336,7 @@ static void aspeed_smc_flash_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); } -static Property aspeed_smc_flash_properties[] = { +static const Property aspeed_smc_flash_properties[] = { DEFINE_PROP_UINT8("cs", AspeedSMCFlash, cs, 0), DEFINE_PROP_LINK("controller", AspeedSMCFlash, controller, TYPE_ASPEED_SMC, AspeedSMCState *), diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c index 9e07432f7c..60a0b17b62 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -561,7 +561,7 @@ static const MemoryRegionOps ibex_spi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static Property ibex_spi_properties[] = { +static const Property ibex_spi_properties[] = { DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 119c38c415..fdd3ad2fdc 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -541,7 +541,7 @@ static const VMStateDescription vmstate_npcm7xx_fiu = { }, }; -static Property npcm7xx_fiu_properties[] = { +static const Property npcm7xx_fiu_properties[] = { DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index c21b2ebb3c..4ca9c469a4 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -1195,7 +1195,7 @@ static const MemoryRegionOps pnv_spi_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static Property pnv_spi_properties[] = { +static const Property pnv_spi_properties[] = { DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c index 08a107792b..7458747779 100644 --- a/hw/ssi/sifive_spi.c +++ b/hw/ssi/sifive_spi.c @@ -328,7 +328,7 @@ static void sifive_spi_realize(DeviceState *dev, Error **errp) fifo8_create(&s->rx_fifo, FIFO_CAPACITY); } -static Property sifive_spi_properties[] = { +static const Property sifive_spi_properties[] = { DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 3f357e8f16..cab0014c3f 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -108,7 +108,7 @@ static void ssi_peripheral_realize(DeviceState *dev, Error **errp) ssc->realize(s, errp); } -static Property ssi_peripheral_properties[] = { +static const Property ssi_peripheral_properties[] = { DEFINE_PROP_UINT8("cs", SSIPeripheral, cs_index, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index 7f1e1808c5..588c1ec071 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -361,7 +361,7 @@ static const VMStateDescription vmstate_xilinx_spi = { } }; -static Property xilinx_spi_properties[] = { +static const Property xilinx_spi_properties[] = { DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index aeb462c3ce..f72cb3cbc8 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1420,12 +1420,12 @@ static const VMStateDescription vmstate_xlnx_zynqmp_qspips = { } }; -static Property xilinx_zynqmp_qspips_properties[] = { +static const Property xilinx_zynqmp_qspips_properties[] = { DEFINE_PROP_UINT32("dma-burst-size", XlnxZynqMPQSPIPS, dma_burst_size, 64), DEFINE_PROP_END_OF_LIST(), }; -static Property xilinx_spips_properties[] = { +static const Property xilinx_spips_properties[] = { DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4), DEFINE_PROP_UINT8("num-txrx-bytes", XilinxSPIPS, num_txrx_bytes, 1), diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c index ecc1903b8e..e51abe9de2 100644 --- a/hw/ssi/xlnx-versal-ospi.c +++ b/hw/ssi/xlnx-versal-ospi.c @@ -1825,7 +1825,7 @@ static const VMStateDescription vmstate_xlnx_versal_ospi = { } }; -static Property xlnx_versal_ospi_properties[] = { +static const 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), From 74734e2b1b77b7753bd5f5578acfbb2a81fe3ec2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 16:51:42 +0000 Subject: [PATCH 0367/2892] hw/timer: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/timer/a9gtimer.c | 2 +- hw/timer/allwinner-a10-pit.c | 2 +- hw/timer/arm_mptimer.c | 2 +- hw/timer/arm_timer.c | 2 +- hw/timer/aspeed_timer.c | 2 +- hw/timer/avr_timer16.c | 2 +- hw/timer/grlib_gptimer.c | 2 +- hw/timer/hpet.c | 2 +- hw/timer/i8254_common.c | 2 +- hw/timer/ibex_timer.c | 2 +- hw/timer/mss-timer.c | 2 +- hw/timer/nrf51_timer.c | 2 +- hw/timer/pxa2xx_timer.c | 2 +- hw/timer/renesas_cmt.c | 2 +- hw/timer/renesas_tmr.c | 2 +- hw/timer/sifive_pwm.c | 2 +- hw/timer/slavio_timer.c | 2 +- hw/timer/sse-timer.c | 2 +- hw/timer/stm32f2xx_timer.c | 2 +- hw/timer/xilinx_timer.c | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 8091ec18c7..c0a91bab0c 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -373,7 +373,7 @@ static const VMStateDescription vmstate_a9_gtimer = { } }; -static Property a9_gtimer_properties[] = { +static const Property a9_gtimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index d488e9782b..2904ccfb42 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -188,7 +188,7 @@ static const MemoryRegionOps a10_pit_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static Property a10_pit_properties[] = { +static const Property a10_pit_properties[] = { DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0), DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0), DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0), diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index defa30b46d..6244a7a84f 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -300,7 +300,7 @@ static const VMStateDescription vmstate_arm_mptimer = { } }; -static Property arm_mptimer_properties[] = { +static const Property arm_mptimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 0940e03f1d..dfa034296c 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -387,7 +387,7 @@ static const TypeInfo icp_pit_info = { .instance_init = icp_pit_init, }; -static Property sp804_properties[] = { +static const Property sp804_properties[] = { DEFINE_PROP_UINT32("freq0", SP804State, freq0, 1000000), DEFINE_PROP_UINT32("freq1", SP804State, freq1, 1000000), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 149f7cc5a6..4c16b5016e 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -674,7 +674,7 @@ static const VMStateDescription vmstate_aspeed_timer_state = { } }; -static Property aspeed_timer_properties[] = { +static const Property aspeed_timer_properties[] = { DEFINE_PROP_LINK("scu", AspeedTimerCtrlState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c index 421920054f..2e3ce83c43 100644 --- a/hw/timer/avr_timer16.c +++ b/hw/timer/avr_timer16.c @@ -542,7 +542,7 @@ static const MemoryRegionOps avr_timer16_ifr_ops = { .impl = {.max_access_size = 1} }; -static Property avr_timer16_properties[] = { +static const Property avr_timer16_properties[] = { DEFINE_PROP_UINT8("id", struct AVRTimer16State, id, 0), DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State, cpu_freq_hz, 0), diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 6ef08f25fd..a7428ed938 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -403,7 +403,7 @@ static void grlib_gptimer_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &unit->iomem); } -static Property grlib_gptimer_properties[] = { +static const Property grlib_gptimer_properties[] = { DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 5399f1b2a3..46886c379e 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -745,7 +745,7 @@ static void hpet_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, &s->pit_enabled, 1); } -static Property hpet_device_properties[] = { +static const Property hpet_device_properties[] = { DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0), diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c index 28fdabc321..953c1e11eb 100644 --- a/hw/timer/i8254_common.c +++ b/hw/timer/i8254_common.c @@ -238,7 +238,7 @@ static const VMStateDescription vmstate_pit_common = { } }; -static Property pit_common_properties[] = { +static const Property pit_common_properties[] = { DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index 2bdcff532d..fba4466a89 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -263,7 +263,7 @@ static const VMStateDescription vmstate_ibex_timer = { } }; -static Property ibex_timer_properties[] = { +static const Property ibex_timer_properties[] = { DEFINE_PROP_UINT32("timebase-freq", IbexTimerState, timebase_freq, 10000), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index b66aed56ea..e5c5cd6a84 100644 --- a/hw/timer/mss-timer.c +++ b/hw/timer/mss-timer.c @@ -279,7 +279,7 @@ static const VMStateDescription vmstate_mss_timer = { } }; -static Property mss_timer_properties[] = { +static const Property mss_timer_properties[] = { /* Libero GUI shows 100Mhz as default for clocks */ DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz, 100 * 1000000), diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c index 35b0e62d5b..48fccec1bf 100644 --- a/hw/timer/nrf51_timer.c +++ b/hw/timer/nrf51_timer.c @@ -379,7 +379,7 @@ static const VMStateDescription vmstate_nrf51_timer = { } }; -static Property nrf51_timer_properties[] = { +static const Property nrf51_timer_properties[] = { DEFINE_PROP_UINT8("id", NRF51TimerState, id, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 3234bbb1f4..345145bfa8 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -549,7 +549,7 @@ static const VMStateDescription vmstate_pxa2xx_timer_regs = { } }; -static Property pxa25x_timer_dev_properties[] = { +static const Property pxa25x_timer_dev_properties[] = { DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, PXA2XX_TIMER_HAVE_TM4, false), diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c index cd59b08c87..6d451fa86b 100644 --- a/hw/timer/renesas_cmt.c +++ b/hw/timer/renesas_cmt.c @@ -253,7 +253,7 @@ static const VMStateDescription vmstate_rcmt = { } }; -static Property rcmt_properties[] = { +static const Property rcmt_properties[] = { DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c index a93e075fcd..890f803cf8 100644 --- a/hw/timer/renesas_tmr.c +++ b/hw/timer/renesas_tmr.c @@ -463,7 +463,7 @@ static const VMStateDescription vmstate_rtmr = { } }; -static Property rtmr_properties[] = { +static const Property rtmr_properties[] = { DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c index 4602fc1a61..042c89c67a 100644 --- a/hw/timer/sifive_pwm.c +++ b/hw/timer/sifive_pwm.c @@ -404,7 +404,7 @@ static const VMStateDescription vmstate_sifive_pwm = { } }; -static Property sifive_pwm_properties[] = { +static const Property sifive_pwm_properties[] = { /* 0.5Ghz per spec after FSBL */ DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState, freq_hz, 500000000ULL), diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 12cb3bac97..32991f4436 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -420,7 +420,7 @@ static void slavio_timer_init(Object *obj) } } -static Property slavio_timer_properties[] = { +static const Property slavio_timer_properties[] = { DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/sse-timer.c b/hw/timer/sse-timer.c index 115b0138c8..6b7a67941c 100644 --- a/hw/timer/sse-timer.c +++ b/hw/timer/sse-timer.c @@ -440,7 +440,7 @@ static const VMStateDescription sse_timer_vmstate = { } }; -static Property sse_timer_properties[] = { +static const Property sse_timer_properties[] = { DEFINE_PROP_LINK("counter", SSETimer, counter, TYPE_SSE_COUNTER, SSECounter *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 16b47887a5..d9d745cd76 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -298,7 +298,7 @@ static const VMStateDescription vmstate_stm32f2xx_timer = { } }; -static Property stm32f2xx_timer_properties[] = { +static const Property stm32f2xx_timer_properties[] = { DEFINE_PROP_UINT64("clock-frequency", struct STM32F2XXTimerState, freq_hz, 1000000000), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 32a9df69e0..7fe3e83baa 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -242,7 +242,7 @@ static void xilinx_timer_init(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &t->irq); } -static Property xilinx_timer_properties[] = { +static const Property xilinx_timer_properties[] = { DEFINE_PROP_UINT32("clock-frequency", XpsTimerState, freq_hz, 62 * 1000000), DEFINE_PROP_UINT8("one-timer-only", XpsTimerState, one_timer_only, 0), DEFINE_PROP_END_OF_LIST(), From 3885fa159163230e338198e085f18b34ce1eb894 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:22:27 +0000 Subject: [PATCH 0368/2892] hw/tpm: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Stefan Berger Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/tpm/tpm_crb.c | 2 +- hw/tpm/tpm_spapr.c | 2 +- hw/tpm/tpm_tis_i2c.c | 2 +- hw/tpm/tpm_tis_isa.c | 2 +- hw/tpm/tpm_tis_sysbus.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 5cd5a2533b..2bf6e7ffe9 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -226,7 +226,7 @@ static const VMStateDescription vmstate_tpm_crb = { } }; -static Property tpm_crb_properties[] = { +static const Property tpm_crb_properties[] = { DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), DEFINE_PROP_BOOL("ppi", CRBState, ppi_enabled, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index 5f7a0dfc61..e15b67dd45 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -364,7 +364,7 @@ static const VMStateDescription vmstate_spapr_vtpm = { } }; -static Property tpm_spapr_properties[] = { +static const Property tpm_spapr_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprTpmState, vdev), DEFINE_PROP_TPMBE("tpmdev", SpaprTpmState, be_driver), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c index c5548b0a45..b27af230cd 100644 --- a/hw/tpm/tpm_tis_i2c.c +++ b/hw/tpm/tpm_tis_i2c.c @@ -491,7 +491,7 @@ static int tpm_tis_i2c_send(I2CSlave *i2c, uint8_t data) return 1; } -static Property tpm_tis_i2c_properties[] = { +static const Property tpm_tis_i2c_properties[] = { DEFINE_PROP_TPMBE("tpmdev", TPMStateI2C, state.be_driver), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 21109edcaa..9b2160972a 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -91,7 +91,7 @@ static void tpm_tis_isa_reset(DeviceState *dev) return tpm_tis_reset(s); } -static Property tpm_tis_isa_properties[] = { +static const Property tpm_tis_isa_properties[] = { DEFINE_PROP_UINT32("irq", TPMStateISA, state.irq_num, TPM_TIS_IRQ), DEFINE_PROP_TPMBE("tpmdev", TPMStateISA, state.be_driver), DEFINE_PROP_BOOL("ppi", TPMStateISA, state.ppi_enabled, true), diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 967f264634..88c1f1e478 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -90,7 +90,7 @@ static void tpm_tis_sysbus_reset(DeviceState *dev) return tpm_tis_reset(s); } -static Property tpm_tis_sysbus_properties[] = { +static const Property tpm_tis_sysbus_properties[] = { DEFINE_PROP_UINT32("irq", TPMStateSysBus, state.irq_num, TPM_TIS_IRQ), DEFINE_PROP_TPMBE("tpmdev", TPMStateSysBus, state.be_driver), DEFINE_PROP_END_OF_LIST(), From 35d71fd9cea66b0c66d94b9e29bfb85781ad6e7f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:23:37 +0000 Subject: [PATCH 0369/2892] hw/ufs: Constify all Property 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 --- hw/ufs/lu.c | 2 +- hw/ufs/ufs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 81bfff9b4e..74ff52ad09 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -274,7 +274,7 @@ static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) return UFS_REQUEST_NO_COMPLETE; } -static Property ufs_lu_props[] = { +static const Property ufs_lu_props[] = { DEFINE_PROP_DRIVE("drive", UfsLu, conf.blk), DEFINE_PROP_UINT8("lun", UfsLu, lun, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 79f786ed4e..fe77158439 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1752,7 +1752,7 @@ static void ufs_exit(PCIDevice *pci_dev) } } -static Property ufs_props[] = { +static const Property ufs_props[] = { DEFINE_PROP_STRING("serial", UfsHc, params.serial), DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), From 44f2f4bc3005eb48ebabea4f33466498db3ea0f2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:26:45 +0000 Subject: [PATCH 0370/2892] hw/usb: Constify all Property 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 --- hw/usb/bus.c | 2 +- hw/usb/canokey.c | 2 +- hw/usb/ccid-card-emulated.c | 2 +- hw/usb/ccid-card-passthru.c | 2 +- hw/usb/dev-audio.c | 2 +- hw/usb/dev-hid.c | 6 +++--- hw/usb/dev-hub.c | 2 +- hw/usb/dev-mtp.c | 2 +- hw/usb/dev-network.c | 2 +- hw/usb/dev-serial.c | 4 ++-- hw/usb/dev-smartcard-reader.c | 4 ++-- hw/usb/dev-storage-classic.c | 2 +- hw/usb/dev-uas.c | 2 +- hw/usb/hcd-dwc2.c | 2 +- hw/usb/hcd-dwc3.c | 2 +- hw/usb/hcd-ehci-pci.c | 2 +- hw/usb/hcd-ehci-sysbus.c | 2 +- hw/usb/hcd-ohci-pci.c | 2 +- hw/usb/hcd-ohci-sysbus.c | 2 +- hw/usb/hcd-uhci.c | 4 ++-- hw/usb/hcd-xhci-nec.c | 2 +- hw/usb/hcd-xhci-sysbus.c | 2 +- hw/usb/hcd-xhci.c | 2 +- hw/usb/host-libusb.c | 2 +- hw/usb/redirect.c | 2 +- hw/usb/u2f-emulated.c | 2 +- hw/usb/u2f-passthru.c | 2 +- 27 files changed, 32 insertions(+), 32 deletions(-) diff --git a/hw/usb/bus.c b/hw/usb/bus.c index bfab2807d7..80e6a92820 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -18,7 +18,7 @@ static char *usb_get_dev_path(DeviceState *dev); static char *usb_get_fw_dev_path(DeviceState *qdev); static void usb_qdev_unrealize(DeviceState *qdev); -static Property usb_props[] = { +static const Property usb_props[] = { DEFINE_PROP_STRING("port", USBDevice, port_path), DEFINE_PROP_STRING("serial", USBDevice, serial), DEFINE_PROP_BIT("msos-desc", USBDevice, flags, diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c index b306eeb20e..7cb600e3c8 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -296,7 +296,7 @@ static void canokey_unrealize(USBDevice *base) trace_canokey_unrealize(); } -static Property canokey_properties[] = { +static const Property canokey_properties[] = { DEFINE_PROP_STRING("file", CanoKeyState, file), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 3ee9c73b87..dd58333943 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -582,7 +582,7 @@ static void emulated_unrealize(CCIDCardState *base) qemu_mutex_destroy(&card->event_list_mutex); } -static Property emulated_card_properties[] = { +static const Property emulated_card_properties[] = { DEFINE_PROP_STRING("backend", EmulatedState, backend_str), DEFINE_PROP_STRING("cert1", EmulatedState, cert1), DEFINE_PROP_STRING("cert2", EmulatedState, cert2), diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index a515703904..f97dcf767f 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -388,7 +388,7 @@ static const VMStateDescription passthru_vmstate = { } }; -static Property passthru_card_properties[] = { +static const Property passthru_card_properties[] = { DEFINE_PROP_CHR("chardev", PassthruState, cs), DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 1897fff9e6..6007f16d30 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -990,7 +990,7 @@ static const VMStateDescription vmstate_usb_audio = { .unmigratable = 1, }; -static Property usb_audio_properties[] = { +static const Property usb_audio_properties[] = { DEFINE_AUDIO_PROPERTIES(USBAudioState, card), DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0), diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 9e358c934e..d83f67b984 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -793,7 +793,7 @@ static const TypeInfo usb_hid_type_info = { .class_init = usb_hid_class_initfn, }; -static Property usb_tablet_properties[] = { +static const Property usb_tablet_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), DEFINE_PROP_UINT32("head", USBHIDState, head, 0), @@ -818,7 +818,7 @@ static const TypeInfo usb_tablet_info = { .class_init = usb_tablet_class_initfn, }; -static Property usb_mouse_properties[] = { +static const Property usb_mouse_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_END_OF_LIST(), }; @@ -841,7 +841,7 @@ static const TypeInfo usb_mouse_info = { .class_init = usb_mouse_class_initfn, }; -static Property usb_keyboard_properties[] = { +static const Property usb_keyboard_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 2c3640c705..317ca0b081 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -665,7 +665,7 @@ static const VMStateDescription vmstate_usb_hub = { } }; -static Property usb_hub_properties[] = { +static const Property usb_hub_properties[] = { DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8), DEFINE_PROP_BOOL("port-power", USBHubState, port_power, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 554b397e88..7994727e5e 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -2078,7 +2078,7 @@ static const VMStateDescription vmstate_usb_mtp = { } }; -static Property mtp_properties[] = { +static const Property mtp_properties[] = { DEFINE_PROP_STRING("rootdir", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index d00d68b21d..6c4f5776d4 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1407,7 +1407,7 @@ static const VMStateDescription vmstate_usb_net = { .unmigratable = 1, }; -static Property net_properties[] = { +static const Property net_properties[] = { DEFINE_NIC_PROPERTIES(USBNetState, conf), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 63047d79cf..0c3e9160ec 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -634,7 +634,7 @@ static const VMStateDescription vmstate_usb_serial = { .unmigratable = 1, }; -static Property serial_properties[] = { +static const Property serial_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false), DEFINE_PROP_END_OF_LIST(), @@ -677,7 +677,7 @@ static const TypeInfo serial_info = { .class_init = usb_serial_class_initfn, }; -static Property braille_properties[] = { +static const Property braille_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index c0d63e0425..c3c02f0aad 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1171,7 +1171,7 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s) : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; } -static Property ccid_props[] = { +static const Property ccid_props[] = { DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -1431,7 +1431,7 @@ static const VMStateDescription ccid_vmstate = { } }; -static Property ccid_properties[] = { +static const Property ccid_properties[] = { DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index 6147387dc6..ca037ba96f 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -67,7 +67,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) s->scsi_dev = scsi_dev; } -static Property msd_properties[] = { +static const Property msd_properties[] = { DEFINE_BLOCK_PROPERTIES(MSDState, conf), DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), DEFINE_PROP_BOOL("removable", MSDState, removable, false), diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 1804cb6799..57e8d20051 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -953,7 +953,7 @@ static const VMStateDescription vmstate_usb_uas = { } }; -static Property uas_properties[] = { +static const Property uas_properties[] = { DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index b4f0652c7d..6a10f3e9cd 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1448,7 +1448,7 @@ const VMStateDescription vmstate_dwc2_state = { } }; -static Property dwc2_usb_properties[] = { +static const Property dwc2_usb_properties[] = { DEFINE_PROP_UINT32("usb_version", DWC2State, usb_version, 2), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index e7d8c7924b..ff970bd989 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -656,7 +656,7 @@ static const VMStateDescription vmstate_usb_dwc3 = { } }; -static Property usb_dwc3_properties[] = { +static const Property usb_dwc3_properties[] = { DEFINE_PROP_UINT32("DWC_USB3_USERID", USBDWC3, cfg.dwc_usb3_user, 0x12345678), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index dd06451e23..374f25c5ed 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -135,7 +135,7 @@ static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, i->ehci.as = busmaster ? pci_get_address_space(dev) : &address_space_memory; } -static Property ehci_pci_properties[] = { +static const Property ehci_pci_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index eb7df93ac2..f4e08aab89 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -30,7 +30,7 @@ static const VMStateDescription vmstate_ehci_sysbus = { } }; -static Property ehci_sysbus_properties[] = { +static const Property ehci_sysbus_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), DEFINE_PROP_BOOL("companion-enable", EHCISysBusState, ehci.companion_enable, false), diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 47fb659806..459644cc1b 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -109,7 +109,7 @@ static void usb_ohci_reset_pci(DeviceState *d) ohci_hard_reset(s); } -static Property ohci_pci_properties[] = { +static const Property ohci_pci_properties[] = { DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c index 313e1e71bb..81cf2e558d 100644 --- a/hw/usb/hcd-ohci-sysbus.c +++ b/hw/usb/hcd-ohci-sysbus.c @@ -57,7 +57,7 @@ static void ohci_sysbus_reset(DeviceState *dev) ohci_hard_reset(ohci); } -static Property ohci_sysbus_properties[] = { +static const Property ohci_sysbus_properties[] = { DEFINE_PROP_STRING("masterbus", OHCISysBusState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 65c1f93cc9..245352c231 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1227,14 +1227,14 @@ static void usb_uhci_exit(PCIDevice *dev) } } -static Property uhci_properties_companion[] = { +static const Property uhci_properties_companion[] = { DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), DEFINE_PROP_END_OF_LIST(), }; -static Property uhci_properties_standalone[] = { +static const Property uhci_properties_standalone[] = { DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 1a191fc737..f06e7403e2 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -36,7 +36,7 @@ struct XHCINecState { uint32_t slots; }; -static Property nec_xhci_properties[] = { +static const Property nec_xhci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index 59cf7fd4ab..f4dbad7cc6 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -82,7 +82,7 @@ void xhci_sysbus_build_aml(Aml *scope, uint32_t mmio, unsigned int irq) aml_append(scope, dev); } -static Property xhci_sysbus_props[] = { +static const Property xhci_sysbus_props[] = { DEFINE_PROP_UINT32("intrs", XHCISysbusState, xhci.numintrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCISysbusState, xhci.numslots, XHCI_MAXSLOTS), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index d85adaca0d..3c5006f425 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3605,7 +3605,7 @@ const VMStateDescription vmstate_xhci = { } }; -static Property xhci_properties[] = { +static const Property xhci_properties[] = { DEFINE_PROP_BIT("streams", XHCIState, flags, XHCI_FLAG_ENABLE_STREAMS, true), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 691bc881fb..85d33b51ba 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1758,7 +1758,7 @@ static const VMStateDescription vmstate_usb_host = { } }; -static Property usb_host_dev_properties[] = { +static const Property usb_host_dev_properties[] = { DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 0f2dd2e504..f72a612d5a 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -2573,7 +2573,7 @@ static const VMStateDescription usbredir_vmstate = { } }; -static Property usbredir_properties[] = { +static const Property usbredir_properties[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), diff --git a/hw/usb/u2f-emulated.c b/hw/usb/u2f-emulated.c index 63cceaa5fc..df86ce97fc 100644 --- a/hw/usb/u2f-emulated.c +++ b/hw/usb/u2f-emulated.c @@ -369,7 +369,7 @@ static void u2f_emulated_unrealize(U2FKeyState *base) } } -static Property u2f_emulated_properties[] = { +static const Property u2f_emulated_properties[] = { DEFINE_PROP_STRING("dir", U2FEmulatedState, dir), DEFINE_PROP_STRING("cert", U2FEmulatedState, cert), DEFINE_PROP_STRING("privkey", U2FEmulatedState, privkey), diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index c4a783d128..ec4f6165d8 100644 --- a/hw/usb/u2f-passthru.c +++ b/hw/usb/u2f-passthru.c @@ -516,7 +516,7 @@ static const VMStateDescription u2f_passthru_vmstate = { } }; -static Property u2f_passthru_properties[] = { +static const Property u2f_passthru_properties[] = { DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw), DEFINE_PROP_END_OF_LIST(), }; From d3eb6f73e14c6ba413be0a3c658882782aa93eac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:27:48 +0000 Subject: [PATCH 0371/2892] hw/vfio: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/vfio/ap.c | 2 +- hw/vfio/ccw.c | 2 +- hw/vfio/pci.c | 4 ++-- hw/vfio/platform.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 4013e7b436..2e6ea2dd93 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -191,7 +191,7 @@ static void vfio_ap_unrealize(DeviceState *dev) g_free(vapdev->vdev.name); } -static Property vfio_ap_properties[] = { +static const Property vfio_ap_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev), #ifdef CONFIG_IOMMUFD DEFINE_PROP_LINK("iommufd", VFIOAPDevice, vdev.iommufd, diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index c1cd7736cd..b96ab27e12 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -655,7 +655,7 @@ static void vfio_ccw_unrealize(DeviceState *dev) } } -static Property vfio_ccw_properties[] = { +static const Property vfio_ccw_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), DEFINE_PROP_BOOL("force-orb-pfch", VFIOCCWDevice, force_orb_pfch, false), #ifdef CONFIG_IOMMUFD diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 14bcc725c3..93aca850e3 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3354,7 +3354,7 @@ static void vfio_instance_init(Object *obj) pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; } -static Property vfio_pci_dev_properties[] = { +static const Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), DEFINE_PROP_UUID_NODEFAULT("vf-token", VFIOPCIDevice, vf_token), DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), @@ -3451,7 +3451,7 @@ static const TypeInfo vfio_pci_dev_info = { }, }; -static Property vfio_pci_dev_nohotplug_properties[] = { +static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, ON_OFF_AUTO_AUTO), diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index a85c199c76..766e8a86ef 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -629,7 +629,7 @@ static const VMStateDescription vfio_platform_vmstate = { .unmigratable = 1, }; -static Property vfio_platform_dev_properties[] = { +static const Property vfio_platform_dev_properties[] = { DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name), DEFINE_PROP_STRING("sysfsdev", VFIOPlatformDevice, vbasedev.sysfsdev), DEFINE_PROP_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false), From 1577a9180f374cd8bca32f8a5e8c8ac8977d7199 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:29:25 +0000 Subject: [PATCH 0372/2892] hw/virtio: Constify all Property 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 --- hw/virtio/vdpa-dev.c | 2 +- hw/virtio/vhost-scsi-pci.c | 2 +- hw/virtio/vhost-user-blk-pci.c | 2 +- hw/virtio/vhost-user-device.c | 2 +- hw/virtio/vhost-user-fs-pci.c | 2 +- hw/virtio/vhost-user-fs.c | 2 +- hw/virtio/vhost-user-gpio.c | 2 +- hw/virtio/vhost-user-i2c.c | 2 +- hw/virtio/vhost-user-input.c | 2 +- hw/virtio/vhost-user-rng-pci.c | 2 +- hw/virtio/vhost-user-rng.c | 2 +- hw/virtio/vhost-user-scmi.c | 2 +- hw/virtio/vhost-user-scsi-pci.c | 2 +- hw/virtio/vhost-user-snd.c | 2 +- hw/virtio/vhost-user-vsock-pci.c | 2 +- hw/virtio/vhost-user-vsock.c | 2 +- hw/virtio/vhost-vsock-common.c | 2 +- hw/virtio/vhost-vsock-pci.c | 2 +- hw/virtio/vhost-vsock.c | 2 +- hw/virtio/virtio-9p-pci.c | 2 +- hw/virtio/virtio-balloon.c | 2 +- hw/virtio/virtio-blk-pci.c | 2 +- hw/virtio/virtio-crypto-pci.c | 2 +- hw/virtio/virtio-crypto.c | 2 +- hw/virtio/virtio-input-pci.c | 2 +- hw/virtio/virtio-iommu-pci.c | 2 +- hw/virtio/virtio-iommu.c | 2 +- hw/virtio/virtio-mem.c | 2 +- hw/virtio/virtio-mmio.c | 2 +- hw/virtio/virtio-net-pci.c | 2 +- hw/virtio/virtio-nsm.c | 2 +- hw/virtio/virtio-pci.c | 4 ++-- hw/virtio/virtio-pmem.c | 2 +- hw/virtio/virtio-rng-pci.c | 2 +- hw/virtio/virtio-rng.c | 2 +- hw/virtio/virtio-scsi-pci.c | 2 +- hw/virtio/virtio-serial-pci.c | 2 +- hw/virtio/virtio.c | 2 +- 38 files changed, 39 insertions(+), 39 deletions(-) diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 64b96b226c..61849b3b0e 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -337,7 +337,7 @@ static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) } } -static Property vhost_vdpa_device_properties[] = { +static const Property vhost_vdpa_device_properties[] = { DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c index 08980bc23b..7536b37f18 100644 --- a/hw/virtio/vhost-scsi-pci.c +++ b/hw/virtio/vhost-scsi-pci.c @@ -38,7 +38,7 @@ struct VHostSCSIPCI { VHostSCSI vdev; }; -static Property vhost_scsi_pci_properties[] = { +static const Property vhost_scsi_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c index eef8641a98..99f1472023 100644 --- a/hw/virtio/vhost-user-blk-pci.c +++ b/hw/virtio/vhost-user-blk-pci.c @@ -43,7 +43,7 @@ struct VHostUserBlkPCI { VHostUserBlk vdev; }; -static Property vhost_user_blk_pci_properties[] = { +static const Property vhost_user_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), diff --git a/hw/virtio/vhost-user-device.c b/hw/virtio/vhost-user-device.c index 67aa934710..3222b67bd9 100644 --- a/hw/virtio/vhost-user-device.c +++ b/hw/virtio/vhost-user-device.c @@ -29,7 +29,7 @@ static const VMStateDescription vud_vmstate = { .unmigratable = 1, }; -static Property vud_properties[] = { +static const Property vud_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_UINT16("virtio-id", VHostUserBase, virtio_id, 0), DEFINE_PROP_UINT32("vq_size", VHostUserBase, vq_size, 64), diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c index 6829b8b743..9ba6c40655 100644 --- a/hw/virtio/vhost-user-fs-pci.c +++ b/hw/virtio/vhost-user-fs-pci.c @@ -29,7 +29,7 @@ typedef struct VHostUserFSPCI VHostUserFSPCI; DECLARE_INSTANCE_CHECKER(VHostUserFSPCI, VHOST_USER_FS_PCI, TYPE_VHOST_USER_FS_PCI) -static Property vhost_user_fs_pci_properties[] = { +static const Property vhost_user_fs_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index 32ee7f496d..c0462329a5 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -403,7 +403,7 @@ static const VMStateDescription vuf_backend_vmstate = { }, }; -static Property vuf_properties[] = { +static const Property vuf_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev), DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag), DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c index 9f37c25415..c997c66d80 100644 --- a/hw/virtio/vhost-user-gpio.c +++ b/hw/virtio/vhost-user-gpio.c @@ -14,7 +14,7 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_gpio.h" -static Property vgpio_properties[] = { +static const Property vgpio_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index a464f5e039..b0a5cbf3ea 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -14,7 +14,7 @@ #include "qemu/error-report.h" #include "standard-headers/linux/virtio_ids.h" -static Property vi2c_properties[] = { +static const Property vi2c_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-input.c b/hw/virtio/vhost-user-input.c index bedec0468c..c57cc461bb 100644 --- a/hw/virtio/vhost-user-input.c +++ b/hw/virtio/vhost-user-input.c @@ -7,7 +7,7 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio-input.h" -static Property vinput_properties[] = { +static const Property vinput_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-rng-pci.c b/hw/virtio/vhost-user-rng-pci.c index f64935453b..0016ee74ce 100644 --- a/hw/virtio/vhost-user-rng-pci.c +++ b/hw/virtio/vhost-user-rng-pci.c @@ -23,7 +23,7 @@ typedef struct VHostUserRNGPCI VHostUserRNGPCI; DECLARE_INSTANCE_CHECKER(VHostUserRNGPCI, VHOST_USER_RNG_PCI, TYPE_VHOST_USER_RNG_PCI) -static Property vhost_user_rng_pci_properties[] = { +static const Property vhost_user_rng_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c index 01879c863d..c9985b5fad 100644 --- a/hw/virtio/vhost-user-rng.c +++ b/hw/virtio/vhost-user-rng.c @@ -20,7 +20,7 @@ static const VMStateDescription vu_rng_vmstate = { .unmigratable = 1, }; -static Property vrng_properties[] = { +static const Property vrng_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index 300847e672..a15e6916ef 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c @@ -277,7 +277,7 @@ static const VMStateDescription vu_scmi_vmstate = { .unmigratable = 1, }; -static Property vu_scmi_properties[] = { +static const Property vu_scmi_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index 75882e3cf9..b2f6451f48 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -44,7 +44,7 @@ struct VHostUserSCSIPCI { VHostUserSCSI vdev; }; -static Property vhost_user_scsi_pci_properties[] = { +static const Property vhost_user_scsi_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/vhost-user-snd.c b/hw/virtio/vhost-user-snd.c index 9a217543f8..8810a9f699 100644 --- a/hw/virtio/vhost-user-snd.c +++ b/hw/virtio/vhost-user-snd.c @@ -21,7 +21,7 @@ static const VMStateDescription vu_snd_vmstate = { .unmigratable = 1, }; -static Property vsnd_properties[] = { +static const Property vsnd_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-vsock-pci.c b/hw/virtio/vhost-user-vsock-pci.c index e5a86e8013..529d967059 100644 --- a/hw/virtio/vhost-user-vsock-pci.c +++ b/hw/virtio/vhost-user-vsock-pci.c @@ -31,7 +31,7 @@ struct VHostUserVSockPCI { /* vhost-user-vsock-pci */ -static Property vhost_user_vsock_pci_properties[] = { +static const Property vhost_user_vsock_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index da3b0e0229..97885bfeab 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -148,7 +148,7 @@ static void vuv_device_unrealize(DeviceState *dev) } -static Property vuv_properties[] = { +static const Property vuv_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserVSock, conf.chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index fd88df2560..cb2253c39f 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -285,7 +285,7 @@ static struct vhost_dev *vhost_vsock_common_get_vhost(VirtIODevice *vdev) return &vvc->vhost_dev; } -static Property vhost_vsock_common_properties[] = { +static const Property vhost_vsock_common_properties[] = { DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket, ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index 9f34414d38..1d9abd02bd 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -35,7 +35,7 @@ struct VHostVSockPCI { /* vhost-vsock-pci */ -static Property vhost_vsock_pci_properties[] = { +static const Property vhost_vsock_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 3d4a5a97f4..ce80e84494 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -205,7 +205,7 @@ static void vhost_vsock_device_unrealize(DeviceState *dev) vhost_vsock_common_unrealize(vdev); } -static Property vhost_vsock_properties[] = { +static const Property vhost_vsock_properties[] = { DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c index 94c14f0b98..b33faf2fbb 100644 --- a/hw/virtio/virtio-9p-pci.c +++ b/hw/virtio/virtio-9p-pci.c @@ -43,7 +43,7 @@ static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static Property virtio_9p_pci_properties[] = { +static const Property virtio_9p_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index afd2ad6dd6..ab2ee30475 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -1015,7 +1015,7 @@ static const VMStateDescription vmstate_virtio_balloon = { }, }; -static Property virtio_balloon_properties[] = { +static const Property virtio_balloon_properties[] = { DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), DEFINE_PROP_BIT("free-page-hint", VirtIOBalloon, host_features, diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c index 9743bee965..abdcc11b2e 100644 --- a/hw/virtio/virtio-blk-pci.c +++ b/hw/virtio/virtio-blk-pci.c @@ -38,7 +38,7 @@ struct VirtIOBlkPCI { VirtIOBlock vdev; }; -static Property virtio_blk_pci_properties[] = { +static const Property virtio_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 0783dc2f7e..23c85fe586 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -37,7 +37,7 @@ struct VirtIOCryptoPCI { VirtIOCrypto vdev; }; -static Property virtio_crypto_pci_properties[] = { +static const Property virtio_crypto_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index c3ffd8328d..9ae0b02598 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -1128,7 +1128,7 @@ static const VMStateDescription vmstate_virtio_crypto = { }, }; -static Property virtio_crypto_properties[] = { +static const Property virtio_crypto_properties[] = { DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index a53edf46c4..55c0b0555b 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -37,7 +37,7 @@ struct VirtIOInputHIDPCI { VirtIOInputHID vdev; }; -static Property virtio_input_pci_properties[] = { +static const Property virtio_input_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index cbdfe4c591..92adf63684 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -34,7 +34,7 @@ struct VirtIOIOMMUPCI { VirtIOIOMMU vdev; }; -static Property virtio_iommu_pci_properties[] = { +static const Property virtio_iommu_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_ARRAY("reserved-regions", VirtIOIOMMUPCI, vdev.nr_prop_resv_regions, vdev.prop_resv_regions, diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 59ef4fb217..576ad8383f 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1655,7 +1655,7 @@ static const VMStateDescription vmstate_virtio_iommu = { }, }; -static Property virtio_iommu_properties[] = { +static const Property virtio_iommu_properties[] = { DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true), diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 80ada89551..3f6f46fad7 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1680,7 +1680,7 @@ static void virtio_mem_instance_finalize(Object *obj) vmem->mr = NULL; } -static Property virtio_mem_properties[] = { +static const Property virtio_mem_properties[] = { DEFINE_PROP_UINT64(VIRTIO_MEM_ADDR_PROP, VirtIOMEM, addr, 0), DEFINE_PROP_UINT32(VIRTIO_MEM_NODE_PROP, VirtIOMEM, node, 0), DEFINE_PROP_BOOL(VIRTIO_MEM_PREALLOC_PROP, VirtIOMEM, prealloc, false), diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index e3366fe54c..49d9fe8f30 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -751,7 +751,7 @@ static void virtio_mmio_pre_plugged(DeviceState *d, Error **errp) /* virtio-mmio device */ -static Property virtio_mmio_properties[] = { +static const Property virtio_mmio_properties[] = { DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy, format_transport_address, true), DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index e03543a70a..e86094ae22 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -38,7 +38,7 @@ struct VirtIONetPCI { VirtIONet vdev; }; -static Property virtio_net_properties[] = { +static const Property virtio_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c index 6830fcfe17..685c548361 100644 --- a/hw/virtio/virtio-nsm.c +++ b/hw/virtio/virtio-nsm.c @@ -1705,7 +1705,7 @@ static const VMStateDescription vmstate_virtio_nsm = { }, }; -static Property virtio_nsm_properties[] = { +static const Property virtio_nsm_properties[] = { DEFINE_PROP_STRING("module-id", VirtIONSM, module_id), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 5c6c2019ce..de41cb5ef2 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2349,7 +2349,7 @@ static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) } } -static Property virtio_pci_properties[] = { +static const Property virtio_pci_properties[] = { DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags, @@ -2431,7 +2431,7 @@ static const TypeInfo virtio_pci_info = { .abstract = true, }; -static Property virtio_pci_generic_properties[] = { +static const Property virtio_pci_generic_properties[] = { DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index c3512c2dae..f6f3b5ddaf 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -155,7 +155,7 @@ static MemoryRegion *virtio_pmem_get_memory_region(VirtIOPMEM *pmem, return &pmem->memdev->mr; } -static Property virtio_pmem_properties[] = { +static const Property virtio_pmem_properties[] = { DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP, VirtIOPMEM, start, 0), DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP, VirtIOPMEM, memdev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c index 6e76f8b57b..398f432237 100644 --- a/hw/virtio/virtio-rng-pci.c +++ b/hw/virtio/virtio-rng-pci.c @@ -32,7 +32,7 @@ struct VirtIORngPCI { VirtIORNG vdev; }; -static Property virtio_rng_properties[] = { +static const Property virtio_rng_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 7cf31da071..13a1a0b236 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -249,7 +249,7 @@ static const VMStateDescription vmstate_virtio_rng = { }, }; -static Property virtio_rng_properties[] = { +static const Property virtio_rng_properties[] = { /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If * you have an entropy source capable of generating more entropy than this * and you can pass it through via virtio-rng, then hats off to you. Until diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index e8e3442f38..733b5756db 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -35,7 +35,7 @@ struct VirtIOSCSIPCI { VirtIOSCSI vdev; }; -static Property virtio_scsi_pci_properties[] = { +static const Property virtio_scsi_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c index cea31adcc4..bda643ec54 100644 --- a/hw/virtio/virtio-serial-pci.c +++ b/hw/virtio/virtio-serial-pci.c @@ -69,7 +69,7 @@ static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static Property virtio_serial_pci_properties[] = { +static const Property virtio_serial_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f12c4aa81e..7fcdb55ba4 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -4012,7 +4012,7 @@ static void virtio_device_instance_finalize(Object *obj) g_free(vdev->vector_queues); } -static Property virtio_properties[] = { +static const Property virtio_properties[] = { DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), DEFINE_PROP_BOOL("use-started", VirtIODevice, use_started, true), DEFINE_PROP_BOOL("use-disabled-flag", VirtIODevice, use_disabled_flag, true), From 89996947ed9dc3bbbb57fa2823cc96a6c14e04ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:30:13 +0000 Subject: [PATCH 0373/2892] hw/watchdog: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/watchdog/sbsa_gwdt.c | 2 +- hw/watchdog/wdt_aspeed.c | 2 +- hw/watchdog/wdt_imx2.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 80f9b36e79..2e25d4b4e9 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -262,7 +262,7 @@ static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp) dev); } -static Property wdt_sbsa_gwdt_props[] = { +static const Property wdt_sbsa_gwdt_props[] = { /* * Timer frequency in Hz. This must match the frequency used by * the CPU's generic timer. Default 62.5Hz matches QEMU's legacy diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 39c3f362a8..c95877e5c7 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -288,7 +288,7 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static Property aspeed_wdt_properties[] = { +static const Property aspeed_wdt_properties[] = { DEFINE_PROP_LINK("scu", AspeedWDTState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index 8162d58afa..61fbd91ee4 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -281,7 +281,7 @@ static void imx2_wdt_realize(DeviceState *dev, Error **errp) } } -static Property imx2_wdt_properties[] = { +static const Property imx2_wdt_properties[] = { DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support, false), DEFINE_PROP_END_OF_LIST() From 2202d3fd26d2d5c38625b97830a7abd20dc12e8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:31:36 +0000 Subject: [PATCH 0374/2892] hw/xen: Constify all Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Jason Andryuk Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/xen/xen-bus.c | 2 +- hw/xen/xen_pt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 95b207ac8b..0d7defb8cd 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -1092,7 +1092,7 @@ unrealize: xen_device_unrealize(dev); } -static Property xen_device_props[] = { +static const Property xen_device_props[] = { DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, DOMID_INVALID), DEFINE_PROP_END_OF_LIST() diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 3635d1b39f..557aa98be4 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -931,7 +931,7 @@ static void xen_pt_unregister_device(PCIDevice *d) xen_pt_destroy(d); } -static Property xen_pci_passthrough_properties[] = { +static const Property xen_pci_passthrough_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false), DEFINE_PROP_END_OF_LIST(), From 869e7146b7250ed8a8e5e7c598a54d059319ef47 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 17:34:38 +0000 Subject: [PATCH 0375/2892] tests/unit: Constify all Property 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 --- tests/unit/test-qdev-global-props.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c index c8862cac5f..1eb95d2429 100644 --- a/tests/unit/test-qdev-global-props.c +++ b/tests/unit/test-qdev-global-props.c @@ -46,7 +46,7 @@ struct MyType { uint32_t prop2; }; -static Property static_props[] = { +static const Property static_props[] = { DEFINE_PROP_UINT32("prop1", MyType, prop1, PROP_DEFAULT), DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), DEFINE_PROP_END_OF_LIST() From fd363a14f68a7bcbede024fb5155371c19b8f5d2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Dec 2024 14:54:14 +0000 Subject: [PATCH 0376/2892] docs: Constify all Property in examples 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 --- docs/devel/migration/compatibility.rst | 4 ++-- docs/devel/virtio-backends.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/devel/migration/compatibility.rst b/docs/devel/migration/compatibility.rst index 5a5417ef06..c787f53738 100644 --- a/docs/devel/migration/compatibility.rst +++ b/docs/devel/migration/compatibility.rst @@ -198,7 +198,7 @@ was done:: The relevant parts for migration are:: - @@ -1281,7 +1284,8 @@ static Property virtio_blk_properties[] = { + @@ -1281,7 +1284,8 @@ static const Property virtio_blk_properties[] = { #endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), @@ -395,7 +395,7 @@ the old behaviour or the new behaviour:: index 8a87ccc8b0..5153ad63d6 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c - @@ -79,6 +79,8 @@ static Property pci_props[] = { + @@ -79,6 +79,8 @@ static const Property pci_props[] = { DEFINE_PROP_STRING("failover_pair_id", PCIDevice, failover_pair_id), DEFINE_PROP_UINT32("acpi-index", PCIDevice, acpi_index, 0), diff --git a/docs/devel/virtio-backends.rst b/docs/devel/virtio-backends.rst index 9ff092e7a0..a6f9df4845 100644 --- a/docs/devel/virtio-backends.rst +++ b/docs/devel/virtio-backends.rst @@ -101,7 +101,7 @@ manually instantiated: VirtIOBlock vdev; }; - static Property virtio_blk_pci_properties[] = { + static const Property virtio_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), From 57e2cc9abf5da38f600354fe920ff20e719607b4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 5 Sep 2024 16:12:07 +0200 Subject: [PATCH 0377/2892] x86/loader: only patch linux kernels If the binary loaded via -kernel is *not* a linux kernel (in which case protocol == 0), do not patch the linux kernel header fields. It's (a) pointless and (b) might break binaries by random patching and (c) changes the binary hash which in turn breaks secure boot verification. Background: OVMF happily loads and runs not only linux kernels but any efi binary via direct kernel boot. Note: Breaking the secure boot verification is a problem for linux kernels too, but fixed that is left for another day ... Signed-off-by: Gerd Hoffmann Message-ID: <20240905141211.1253307-3-kraxel@redhat.com> --- hw/i386/x86-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index dc031af662..dadc9d99e7 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -945,7 +945,7 @@ void x86_load_linux(X86MachineState *x86ms, * kernel on the other side of the fw_cfg interface matches the hash of the * file the user passed in. */ - if (!sev_enabled()) { + if (!sev_enabled() && protocol > 0) { memcpy(setup, header, MIN(sizeof(header), setup_size)); } From 214191f6b57458814d279a53539d64c6e54e764b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 5 Sep 2024 16:12:08 +0200 Subject: [PATCH 0378/2892] x86/loader: read complete kernel Load the complete kernel (including setup) into memory. Excluding the setup is handled later when adding the FW_CFG_KERNEL_SIZE and FW_CFG_KERNEL_DATA entries. This is a preparation for the next patch which adds a new fw_cfg file containing the complete, unpatched kernel. No functional change. Signed-off-by: Gerd Hoffmann Message-ID: <20240905141211.1253307-4-kraxel@redhat.com> --- hw/i386/x86-common.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index dadc9d99e7..28341b42d9 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -895,7 +895,6 @@ void x86_load_linux(X86MachineState *x86ms, fprintf(stderr, "qemu: invalid kernel header\n"); exit(1); } - kernel_size -= setup_size; setup = g_malloc(setup_size); kernel = g_malloc(kernel_size); @@ -904,6 +903,7 @@ void x86_load_linux(X86MachineState *x86ms, fprintf(stderr, "fread() failed\n"); exit(1); } + fseek(f, 0, SEEK_SET); if (fread(kernel, 1, kernel_size, f) != kernel_size) { fprintf(stderr, "fread() failed\n"); exit(1); @@ -950,10 +950,11 @@ void x86_load_linux(X86MachineState *x86ms, } fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); - sev_load_ctx.kernel_data = (char *)kernel; - sev_load_ctx.kernel_size = kernel_size; + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size - setup_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, + kernel + setup_size, kernel_size - setup_size); + sev_load_ctx.kernel_data = (char *)kernel + setup_size; + sev_load_ctx.kernel_size = kernel_size - setup_size; fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); From f2594d928444fc4d593117db2da8c9ffa26433f7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 5 Sep 2024 16:12:09 +0200 Subject: [PATCH 0379/2892] x86/loader: expose unpatched kernel Add a new "etc/boot/kernel" fw_cfg file, containing the kernel without the setup header patches. Intended use is booting in UEFI with secure boot enabled, where the setup header patching breaks secure boot verification. Needs OVMF changes too to be actually useful. Signed-off-by: Gerd Hoffmann Message-ID: <20240905141211.1253307-5-kraxel@redhat.com> --- hw/i386/x86-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 28341b42d9..1cef3045ad 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -962,6 +962,9 @@ void x86_load_linux(X86MachineState *x86ms, sev_load_ctx.setup_data = (char *)setup; sev_load_ctx.setup_size = setup_size; + /* kernel without setup header patches */ + fw_cfg_add_file(fw_cfg, "etc/boot/kernel", kernel, kernel_size); + if (sev_enabled()) { sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); } From a5bd044b15793ab041102a1e784813f54912a836 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 5 Sep 2024 16:12:10 +0200 Subject: [PATCH 0380/2892] x86/loader: add -shim option Add new -shim command line option, wire up for the x86 loader. When specified load shim into the new "etc/boot/shim" fw_cfg file. Needs OVMF changes too to be actually useful. Signed-off-by: Gerd Hoffmann Message-ID: <20240905141211.1253307-6-kraxel@redhat.com> --- hw/core/machine.c | 20 ++++++++++++++++++++ hw/i386/x86-common.c | 16 ++++++++++++++++ include/hw/boards.h | 1 + qemu-options.hx | 7 +++++++ system/vl.c | 9 +++++++++ 5 files changed, 53 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index e6900b43ef..d970f753e3 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -305,6 +305,21 @@ static void machine_set_kernel(Object *obj, const char *value, Error **errp) ms->kernel_filename = g_strdup(value); } +static char *machine_get_shim(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->shim_filename); +} + +static void machine_set_shim(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->shim_filename); + ms->shim_filename = g_strdup(value); +} + static char *machine_get_initrd(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -1082,6 +1097,11 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "kernel", "Linux kernel image file"); + object_class_property_add_str(oc, "shim", + machine_get_shim, machine_set_shim); + object_class_property_set_description(oc, "shim", + "shim.efi file"); + object_class_property_add_str(oc, "initrd", machine_get_initrd, machine_set_initrd); object_class_property_set_description(oc, "initrd", diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 1cef3045ad..3f78182692 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -965,6 +965,22 @@ void x86_load_linux(X86MachineState *x86ms, /* kernel without setup header patches */ fw_cfg_add_file(fw_cfg, "etc/boot/kernel", kernel, kernel_size); + if (machine->shim_filename) { + GMappedFile *mapped_file; + GError *gerr = NULL; + + mapped_file = g_mapped_file_new(machine->shim_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading shim %s: %s\n", + machine->shim_filename, gerr->message); + exit(1); + } + + fw_cfg_add_file(fw_cfg, "etc/boot/shim", + g_mapped_file_get_contents(mapped_file), + g_mapped_file_get_length(mapped_file)); + } + if (sev_enabled()) { sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); } diff --git a/include/hw/boards.h b/include/hw/boards.h index 7456889c37..5723ee76bd 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -431,6 +431,7 @@ struct MachineState { BootConfiguration boot_config; char *kernel_filename; char *kernel_cmdline; + char *shim_filename; char *initrd_filename; const char *cpu_type; AccelState *accelerator; diff --git a/qemu-options.hx b/qemu-options.hx index dacc9790a4..cc694d3b89 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4145,6 +4145,13 @@ SRST or in multiboot format. ERST +DEF("shim", HAS_ARG, QEMU_OPTION_shim, \ + "-shim shim.efi use 'shim.efi' to boot the kernel\n", QEMU_ARCH_ALL) +SRST +``-shim shim.efi`` + Use 'shim.efi' to boot the kernel +ERST + DEF("append", HAS_ARG, QEMU_OPTION_append, \ "-append cmdline use 'cmdline' as kernel command line\n", QEMU_ARCH_ALL) SRST diff --git a/system/vl.c b/system/vl.c index 4a370da624..09202b57e7 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2427,6 +2427,7 @@ static void configure_accelerators(const char *progname) static void qemu_validate_options(const QDict *machine_opts) { const char *kernel_filename = qdict_get_try_str(machine_opts, "kernel"); + const char *shim_filename = qdict_get_try_str(machine_opts, "shim"); const char *initrd_filename = qdict_get_try_str(machine_opts, "initrd"); const char *kernel_cmdline = qdict_get_try_str(machine_opts, "append"); @@ -2436,6 +2437,11 @@ static void qemu_validate_options(const QDict *machine_opts) exit(1); } + if (shim_filename != NULL) { + error_report("-shim only allowed with -kernel option"); + exit(1); + } + if (initrd_filename != NULL) { error_report("-initrd only allowed with -kernel option"); exit(1); @@ -2912,6 +2918,9 @@ void qemu_init(int argc, char **argv) case QEMU_OPTION_kernel: qdict_put_str(machine_opts_dict, "kernel", optarg); break; + case QEMU_OPTION_shim: + qdict_put_str(machine_opts_dict, "shim", optarg); + break; case QEMU_OPTION_initrd: qdict_put_str(machine_opts_dict, "initrd", optarg); break; From d1770b5834cafff007bbe5218915f18879ff8e48 Mon Sep 17 00:00:00 2001 From: "Pratik R. Sampat" Date: Mon, 18 Nov 2024 10:14:05 -0600 Subject: [PATCH 0381/2892] pc-bios: Add amd-sev-es to edk2 json With the default BIOS being compatible with amd-sev-es add the feature to the json to indicate it's support Signed-off-by: Pratik R. Sampat Message-ID: <20241118161405.208437-1-pratikrajesh.sampat@amd.com> Signed-off-by: Gerd Hoffmann --- pc-bios/descriptors/60-edk2-x86_64.json | 1 + 1 file changed, 1 insertion(+) diff --git a/pc-bios/descriptors/60-edk2-x86_64.json b/pc-bios/descriptors/60-edk2-x86_64.json index 968cb65cf9..4599c63f14 100644 --- a/pc-bios/descriptors/60-edk2-x86_64.json +++ b/pc-bios/descriptors/60-edk2-x86_64.json @@ -26,6 +26,7 @@ "features": [ "acpi-s3", "amd-sev", + "amd-sev-es", "verbose-dynamic" ], "tags": [ From 74dc38d0c6c15fd57a5dee94125d13ac5b00491d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 12 Dec 2024 10:00:59 +0100 Subject: [PATCH 0382/2892] pc-bios: add missing riscv64 descriptor Without descriptor libvirt cannot discover the EDK II binaries via the qemu:///system connection. Signed-off-by: Heinrich Schuchardt Message-ID: <20241212090059.94167-1-heinrich.schuchardt@canonical.com> Signed-off-by: Gerd Hoffmann --- pc-bios/descriptors/60-edk2-riscv64.json | 31 ++++++++++++++++++++++++ pc-bios/descriptors/meson.build | 3 ++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 pc-bios/descriptors/60-edk2-riscv64.json diff --git a/pc-bios/descriptors/60-edk2-riscv64.json b/pc-bios/descriptors/60-edk2-riscv64.json new file mode 100644 index 0000000000..14811ca307 --- /dev/null +++ b/pc-bios/descriptors/60-edk2-riscv64.json @@ -0,0 +1,31 @@ +{ + "description": "UEFI firmware for riscv64", + "interface-types": [ + "uefi" + ], + "mapping": { + "device": "flash", + "executable": { + "filename": "@DATADIR@/edk2-riscv-code.fd", + "format": "raw" + }, + "nvram-template": { + "filename": "@DATADIR@/edk2-riscv-vars.fd", + "format": "raw" + } + }, + "targets": [ + { + "architecture": "riscv64", + "machines": [ + "virt*" + ] + } + ], + "features": [ + + ], + "tags": [ + + ] +} diff --git a/pc-bios/descriptors/meson.build b/pc-bios/descriptors/meson.build index afb5a959cc..cdd0be01a3 100644 --- a/pc-bios/descriptors/meson.build +++ b/pc-bios/descriptors/meson.build @@ -6,7 +6,8 @@ if unpack_edk2_blobs and get_option('install_blobs') '60-edk2-arm.json', '60-edk2-i386.json', '60-edk2-x86_64.json', - '60-edk2-loongarch64.json' + '60-edk2-loongarch64.json', + '60-edk2-riscv64.json' ] configure_file(input: files(f), output: f, From 0f5715e4b5706b31b3550d8e6b88871e029c7823 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 Dec 2024 09:44:07 +0100 Subject: [PATCH 0383/2892] roms: re-add edk2-basetools target Needed to build ipxe nic roms. Reported-by: Liu Jaloo Fixes: 22e11539e167 ("edk2: replace build scripts") Signed-off-by: Gerd Hoffmann Message-ID: <20241212084408.1390728-1-kraxel@redhat.com> --- roms/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roms/Makefile b/roms/Makefile index dfed2b216a..31e4b97c98 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -157,6 +157,11 @@ edk2-version: edk2 touch $@; \ fi +edk2-basetools: edk2-version + $(PYTHON) edk2-build.py --config edk2-build.config \ + --silent --no-logs \ + --match none # build only basetools + efi: edk2-version $(PYTHON) edk2-build.py --config edk2-build.config \ --version-override "$(EDK2_STABLE)$(FIRMWARE_EXTRAVERSION)" \ From 30c78658aa056ae5d3995b4680bcbba5fce2e092 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Tue, 17 Dec 2024 15:05:39 +0000 Subject: [PATCH 0384/2892] target/arm: remove redundant code This call is redundant as it only retrieves a value that is not used further. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Denis Rastyogin Reviewed-by: Richard Henderson Message-id: 20241212120618.518369-1-gerben@altlinux.org Signed-off-by: Peter Maydell --- target/arm/vfp_helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 5a19af509c..df4ff46479 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -1099,8 +1099,6 @@ float64 HELPER(rintd)(float64 x, void *fp_status) ret = float64_round_to_int(x, fp_status); - new_flags = get_float_exception_flags(fp_status); - /* Suppress any inexact exceptions the conversion produced */ if (!(old_flags & float_flag_inexact)) { new_flags = get_float_exception_flags(fp_status); From cca8b4f2a150a7d44962b5da547e21e1782595f2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:39 +0000 Subject: [PATCH 0385/2892] target/arm: Convert vfp_helper.c to fpst alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 268 ++++++++++++++++++++-------------------- target/arm/vfp_helper.c | 120 ++++++++---------- 2 files changed, 186 insertions(+), 202 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 9919b1367b..5f7bb88067 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -109,33 +109,33 @@ DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, tl, i32, i32, i32) DEF_HELPER_1(vfp_get_fpscr, i32, env) DEF_HELPER_2(vfp_set_fpscr, void, env, i32) -DEF_HELPER_3(vfp_addh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_adds, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_addd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_subh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_subs, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_subd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_mulh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_divh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_maxh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_maxs, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_maxd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_minh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_mins, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_mind, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_maxnumh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_maxnums, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_minnumh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_minnums, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_minnumd, f64, f64, f64, ptr) -DEF_HELPER_2(vfp_sqrth, f16, f16, ptr) -DEF_HELPER_2(vfp_sqrts, f32, f32, ptr) -DEF_HELPER_2(vfp_sqrtd, f64, f64, ptr) +DEF_HELPER_3(vfp_addh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_adds, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_addd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_subh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_subs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_subd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_mulh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_muls, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_muld, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_divh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_divs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_divd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_maxh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_maxs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_maxd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_minh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_mins, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_mind, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_maxnumh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_maxnums, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_minnumh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_minnums, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_minnumd, f64, f64, f64, fpst) +DEF_HELPER_2(vfp_sqrth, f16, f16, fpst) +DEF_HELPER_2(vfp_sqrts, f32, f32, fpst) +DEF_HELPER_2(vfp_sqrtd, f64, f64, fpst) DEF_HELPER_3(vfp_cmph, void, f16, f16, env) DEF_HELPER_3(vfp_cmps, void, f32, f32, env) DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) @@ -145,110 +145,110 @@ DEF_HELPER_3(vfp_cmped, void, f64, f64, env) DEF_HELPER_2(vfp_fcvtds, f64, f32, env) DEF_HELPER_2(vfp_fcvtsd, f32, f64, env) -DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, ptr) -DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, ptr) +DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, fpst) +DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, fpst) -DEF_HELPER_2(vfp_uitoh, f16, i32, ptr) -DEF_HELPER_2(vfp_uitos, f32, i32, ptr) -DEF_HELPER_2(vfp_uitod, f64, i32, ptr) -DEF_HELPER_2(vfp_sitoh, f16, i32, ptr) -DEF_HELPER_2(vfp_sitos, f32, i32, ptr) -DEF_HELPER_2(vfp_sitod, f64, i32, ptr) +DEF_HELPER_2(vfp_uitoh, f16, i32, fpst) +DEF_HELPER_2(vfp_uitos, f32, i32, fpst) +DEF_HELPER_2(vfp_uitod, f64, i32, fpst) +DEF_HELPER_2(vfp_sitoh, f16, i32, fpst) +DEF_HELPER_2(vfp_sitos, f32, i32, fpst) +DEF_HELPER_2(vfp_sitod, f64, i32, fpst) -DEF_HELPER_2(vfp_touih, i32, f16, ptr) -DEF_HELPER_2(vfp_touis, i32, f32, ptr) -DEF_HELPER_2(vfp_touid, i32, f64, ptr) -DEF_HELPER_2(vfp_touizh, i32, f16, ptr) -DEF_HELPER_2(vfp_touizs, i32, f32, ptr) -DEF_HELPER_2(vfp_touizd, i32, f64, ptr) -DEF_HELPER_2(vfp_tosih, s32, f16, ptr) -DEF_HELPER_2(vfp_tosis, s32, f32, ptr) -DEF_HELPER_2(vfp_tosid, s32, f64, ptr) -DEF_HELPER_2(vfp_tosizh, s32, f16, ptr) -DEF_HELPER_2(vfp_tosizs, s32, f32, ptr) -DEF_HELPER_2(vfp_tosizd, s32, f64, ptr) +DEF_HELPER_2(vfp_touih, i32, f16, fpst) +DEF_HELPER_2(vfp_touis, i32, f32, fpst) +DEF_HELPER_2(vfp_touid, i32, f64, fpst) +DEF_HELPER_2(vfp_touizh, i32, f16, fpst) +DEF_HELPER_2(vfp_touizs, i32, f32, fpst) +DEF_HELPER_2(vfp_touizd, i32, f64, fpst) +DEF_HELPER_2(vfp_tosih, s32, f16, fpst) +DEF_HELPER_2(vfp_tosis, s32, f32, fpst) +DEF_HELPER_2(vfp_tosid, s32, f64, fpst) +DEF_HELPER_2(vfp_tosizh, s32, f16, fpst) +DEF_HELPER_2(vfp_tosizs, s32, f32, fpst) +DEF_HELPER_2(vfp_tosizd, s32, f64, fpst) -DEF_HELPER_3(vfp_toshh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toslh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_touhh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toulh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosqd_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touqd_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_touqh, i64, f16, i32, ptr) -DEF_HELPER_3(vfp_tosqh, i64, f16, i32, ptr) -DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) -DEF_HELPER_3(vfp_touhs, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touls, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touqs, i64, f32, i32, ptr) -DEF_HELPER_3(vfp_toshd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosld, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosqd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touhd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tould, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touqd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_shtos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_sltos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_sqtos, f32, i64, i32, ptr) -DEF_HELPER_3(vfp_uhtos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_ultos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_uqtos, f32, i64, i32, ptr) -DEF_HELPER_3(vfp_shtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_sltod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_sqtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_uhtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_shtoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_uhtoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, ptr) -DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr) +DEF_HELPER_3(vfp_toshh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toslh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_touhh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toulh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosqd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touqd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toshh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toulh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toslh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_touqh, i64, f16, i32, fpst) +DEF_HELPER_3(vfp_tosqh, i64, f16, i32, fpst) +DEF_HELPER_3(vfp_toshs, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosls, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosqs, i64, f32, i32, fpst) +DEF_HELPER_3(vfp_touhs, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touls, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touqs, i64, f32, i32, fpst) +DEF_HELPER_3(vfp_toshd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosld, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosqd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tould, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touqd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_shtos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sltos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sqtos, f32, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_ultos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_uqtos, f32, i64, i32, fpst) +DEF_HELPER_3(vfp_shtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sltod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sqtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_ultod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uqtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_shtoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sltoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_ultoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, fpst) +DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, fpst) -DEF_HELPER_3(vfp_shtos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_sltos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_uhtos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_ultos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_shtod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_sltod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_uhtod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_ultod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_shtoh_round_to_nearest, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_uhtoh_round_to_nearest, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_sltoh_round_to_nearest, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_ultoh_round_to_nearest, f16, i32, i32, ptr) +DEF_HELPER_3(vfp_shtos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sltos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_ultos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_shtod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sltod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_ultod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_shtoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sltoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_ultoh_round_to_nearest, f16, i32, i32, fpst) -DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) +DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, fpst) -DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, fpst, i32) -DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, ptr) -DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr) -DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, ptr) +DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, fpst) +DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, fpst) +DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, fpst) -DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_1(recpe_u32, TCG_CALL_NO_RWG, i32, i32) DEF_HELPER_FLAGS_1(rsqrte_u32, TCG_CALL_NO_RWG, i32, i32) DEF_HELPER_FLAGS_4(neon_tbl, TCG_CALL_NO_RWG, i64, env, i32, i64, i64) @@ -258,15 +258,15 @@ DEF_HELPER_3(shr_cc, i32, env, i32, i32) DEF_HELPER_3(sar_cc, i32, env, i32, i32) DEF_HELPER_3(ror_cc, i32, env, i32, i32) -DEF_HELPER_FLAGS_2(rinth_exact, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(rinth, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_2(rinth_exact, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(rinth, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) -DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) +DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, fpst) DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) @@ -867,10 +867,10 @@ DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index df4ff46479..8a56936751 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -289,19 +289,16 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val) #define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) #define VFP_BINOP(name) \ -dh_ctype_f16 VFP_HELPER(name, h)(dh_ctype_f16 a, dh_ctype_f16 b, void *fpstp) \ +dh_ctype_f16 VFP_HELPER(name, h)(dh_ctype_f16 a, dh_ctype_f16 b, float_status *fpst) \ { \ - float_status *fpst = fpstp; \ return float16_ ## name(a, b, fpst); \ } \ -float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \ +float32 VFP_HELPER(name, s)(float32 a, float32 b, float_status *fpst) \ { \ - float_status *fpst = fpstp; \ return float32_ ## name(a, b, fpst); \ } \ -float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \ +float64 VFP_HELPER(name, d)(float64 a, float64 b, float_status *fpst) \ { \ - float_status *fpst = fpstp; \ return float64_ ## name(a, b, fpst); \ } VFP_BINOP(add) @@ -314,19 +311,19 @@ VFP_BINOP(minnum) VFP_BINOP(maxnum) #undef VFP_BINOP -dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, void *fpstp) +dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, float_status *fpst) { - return float16_sqrt(a, fpstp); + return float16_sqrt(a, fpst); } -float32 VFP_HELPER(sqrt, s)(float32 a, void *fpstp) +float32 VFP_HELPER(sqrt, s)(float32 a, float_status *fpst) { - return float32_sqrt(a, fpstp); + return float32_sqrt(a, fpst); } -float64 VFP_HELPER(sqrt, d)(float64 a, void *fpstp) +float64 VFP_HELPER(sqrt, d)(float64 a, float_status *fpst) { - return float64_sqrt(a, fpstp); + return float64_sqrt(a, fpst); } static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) @@ -371,16 +368,14 @@ DO_VFP_cmp(d, float64, float64, fp_status) /* Integer to float and float to integer conversions */ #define CONV_ITOF(name, ftype, fsz, sign) \ -ftype HELPER(name)(uint32_t x, void *fpstp) \ +ftype HELPER(name)(uint32_t x, float_status *fpst) \ { \ - float_status *fpst = fpstp; \ return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ } #define CONV_FTOI(name, ftype, fsz, sign, round) \ -sign##int32_t HELPER(name)(ftype x, void *fpstp) \ +sign##int32_t HELPER(name)(ftype x, float_status *fpst) \ { \ - float_status *fpst = fpstp; \ if (float##fsz##_is_any_nan(x)) { \ float_raise(float_flag_invalid, fpst); \ return 0; \ @@ -415,12 +410,12 @@ float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) return float64_to_float32(x, &env->vfp.fp_status); } -uint32_t HELPER(bfcvt)(float32 x, void *status) +uint32_t HELPER(bfcvt)(float32 x, float_status *status) { return float32_to_bfloat16(x, status); } -uint32_t HELPER(bfcvt_pair)(uint64_t pair, void *status) +uint32_t HELPER(bfcvt_pair)(uint64_t pair, float_status *status) { bfloat16 lo = float32_to_bfloat16(extract64(pair, 0, 32), status); bfloat16 hi = float32_to_bfloat16(extract64(pair, 32, 32), status); @@ -436,26 +431,25 @@ uint32_t HELPER(bfcvt_pair)(uint64_t pair, void *status) */ #define VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ ftype HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ - void *fpstp) \ -{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); } + float_status *fpst) \ +{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpst); } #define VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ ftype HELPER(vfp_##name##to##p##_round_to_nearest)(uint##isz##_t x, \ uint32_t shift, \ - void *fpstp) \ + float_status *fpst) \ { \ ftype ret; \ - float_status *fpst = fpstp; \ FloatRoundMode oldmode = fpst->float_rounding_mode; \ fpst->float_rounding_mode = float_round_nearest_even; \ - ret = itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); \ + ret = itype##_to_##float##fsz##_scalbn(x, -shift, fpst); \ fpst->float_rounding_mode = oldmode; \ return ret; \ } #define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, ROUND, suff) \ uint##isz##_t HELPER(vfp_to##name##p##suff)(ftype x, uint32_t shift, \ - void *fpst) \ + float_status *fpst) \ { \ if (unlikely(float##fsz##_is_any_nan(x))) { \ float_raise(float_flag_invalid, fpst); \ @@ -508,10 +502,8 @@ VFP_CONV_FLOAT_FIX_ROUND(uq, d, 64, float64, 64, uint64, /* Set the current fp rounding mode and return the old one. * The argument is a softfloat float_round_ value. */ -uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp) +uint32_t HELPER(set_rmode)(uint32_t rmode, float_status *fp_status) { - float_status *fp_status = fpstp; - uint32_t prev_rmode = get_float_rounding_mode(fp_status); set_float_rounding_mode(rmode, fp_status); @@ -519,12 +511,12 @@ uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp) } /* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, float_status *fpst, + uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing input denormals. */ - float_status *fpst = fpstp; bool save = get_flush_inputs_to_zero(fpst); set_flush_inputs_to_zero(false, fpst); float32 r = float16_to_float32(a, !ahp_mode, fpst); @@ -532,12 +524,12 @@ float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) return r; } -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, float_status *fpst, + uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing output denormals. */ - float_status *fpst = fpstp; bool save = get_flush_to_zero(fpst); set_flush_to_zero(false, fpst); float16 r = float32_to_float16(a, !ahp_mode, fpst); @@ -545,12 +537,12 @@ uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) return r; } -float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, float_status *fpst, + uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing input denormals. */ - float_status *fpst = fpstp; bool save = get_flush_inputs_to_zero(fpst); set_flush_inputs_to_zero(false, fpst); float64 r = float16_to_float64(a, !ahp_mode, fpst); @@ -558,12 +550,12 @@ float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) return r; } -uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, float_status *fpst, + uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing output denormals. */ - float_status *fpst = fpstp; bool save = get_flush_to_zero(fpst); set_flush_to_zero(false, fpst); float16 r = float64_to_float16(a, !ahp_mode, fpst); @@ -664,9 +656,8 @@ static bool round_to_inf(float_status *fpst, bool sign_bit) } } -uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) +uint32_t HELPER(recpe_f16)(uint32_t input, float_status *fpst) { - float_status *fpst = fpstp; float16 f16 = float16_squash_input_denormal(input, fpst); uint32_t f16_val = float16_val(f16); uint32_t f16_sign = float16_is_neg(f16); @@ -714,9 +705,8 @@ uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) return make_float16(f16_val); } -float32 HELPER(recpe_f32)(float32 input, void *fpstp) +float32 HELPER(recpe_f32)(float32 input, float_status *fpst) { - float_status *fpst = fpstp; float32 f32 = float32_squash_input_denormal(input, fpst); uint32_t f32_val = float32_val(f32); bool f32_sign = float32_is_neg(f32); @@ -764,9 +754,8 @@ float32 HELPER(recpe_f32)(float32 input, void *fpstp) return make_float32(f32_val); } -float64 HELPER(recpe_f64)(float64 input, void *fpstp) +float64 HELPER(recpe_f64)(float64 input, float_status *fpst) { - float_status *fpst = fpstp; float64 f64 = float64_squash_input_denormal(input, fpst); uint64_t f64_val = float64_val(f64); bool f64_sign = float64_is_neg(f64); @@ -865,9 +854,8 @@ static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) return extract64(estimate, 0, 8) << 44; } -uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) +uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) { - float_status *s = fpstp; float16 f16 = float16_squash_input_denormal(input, s); uint16_t val = float16_val(f16); bool f16_sign = float16_is_neg(f16); @@ -880,7 +868,7 @@ uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) if (float16_is_signaling_nan(f16, s)) { float_raise(float_flag_invalid, s); if (!s->default_nan_mode) { - nan = float16_silence_nan(f16, fpstp); + nan = float16_silence_nan(f16, s); } } if (s->default_nan_mode) { @@ -911,9 +899,8 @@ uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) return make_float16(val); } -float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) +float32 HELPER(rsqrte_f32)(float32 input, float_status *s) { - float_status *s = fpstp; float32 f32 = float32_squash_input_denormal(input, s); uint32_t val = float32_val(f32); uint32_t f32_sign = float32_is_neg(f32); @@ -926,7 +913,7 @@ float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) if (float32_is_signaling_nan(f32, s)) { float_raise(float_flag_invalid, s); if (!s->default_nan_mode) { - nan = float32_silence_nan(f32, fpstp); + nan = float32_silence_nan(f32, s); } } if (s->default_nan_mode) { @@ -957,9 +944,8 @@ float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) return make_float32(val); } -float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) +float64 HELPER(rsqrte_f64)(float64 input, float_status *s) { - float_status *s = fpstp; float64 f64 = float64_squash_input_denormal(input, s); uint64_t val = float64_val(f64); bool f64_sign = float64_is_neg(f64); @@ -971,7 +957,7 @@ float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) if (float64_is_signaling_nan(f64, s)) { float_raise(float_flag_invalid, s); if (!s->default_nan_mode) { - nan = float64_silence_nan(f64, fpstp); + nan = float64_silence_nan(f64, s); } } if (s->default_nan_mode) { @@ -1026,41 +1012,40 @@ uint32_t HELPER(rsqrte_u32)(uint32_t a) /* VFPv4 fused multiply-accumulate */ dh_ctype_f16 VFP_HELPER(muladd, h)(dh_ctype_f16 a, dh_ctype_f16 b, - dh_ctype_f16 c, void *fpstp) + dh_ctype_f16 c, float_status *fpst) { - float_status *fpst = fpstp; return float16_muladd(a, b, c, 0, fpst); } -float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, void *fpstp) +float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, + float_status *fpst) { - float_status *fpst = fpstp; return float32_muladd(a, b, c, 0, fpst); } -float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp) +float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, + float_status *fpst) { - float_status *fpst = fpstp; return float64_muladd(a, b, c, 0, fpst); } /* ARMv8 round to integral */ -dh_ctype_f16 HELPER(rinth_exact)(dh_ctype_f16 x, void *fp_status) +dh_ctype_f16 HELPER(rinth_exact)(dh_ctype_f16 x, float_status *fp_status) { return float16_round_to_int(x, fp_status); } -float32 HELPER(rints_exact)(float32 x, void *fp_status) +float32 HELPER(rints_exact)(float32 x, float_status *fp_status) { return float32_round_to_int(x, fp_status); } -float64 HELPER(rintd_exact)(float64 x, void *fp_status) +float64 HELPER(rintd_exact)(float64 x, float_status *fp_status) { return float64_round_to_int(x, fp_status); } -dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 x, void *fp_status) +dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 x, float_status *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float16 ret; @@ -1076,7 +1061,7 @@ dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 x, void *fp_status) return ret; } -float32 HELPER(rints)(float32 x, void *fp_status) +float32 HELPER(rints)(float32 x, float_status *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float32 ret; @@ -1092,7 +1077,7 @@ float32 HELPER(rints)(float32 x, void *fp_status) return ret; } -float64 HELPER(rintd)(float64 x, void *fp_status) +float64 HELPER(rintd)(float64 x, float_status *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float64 ret; @@ -1122,9 +1107,8 @@ const FloatRoundMode arm_rmode_to_sf_map[] = { * Implement float64 to int32_t conversion without saturation; * the result is supplied modulo 2^32. */ -uint64_t HELPER(fjcvtzs)(float64 value, void *vstatus) +uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) { - float_status *status = vstatus; uint32_t frac, e_old, e_new; bool inexact; @@ -1196,12 +1180,12 @@ static float32 frint_s(float32 f, float_status *fpst, int intsize) return (0x100u + 126u + intsize) << 23; } -float32 HELPER(frint32_s)(float32 f, void *fpst) +float32 HELPER(frint32_s)(float32 f, float_status *fpst) { return frint_s(f, fpst, 32); } -float32 HELPER(frint64_s)(float32 f, void *fpst) +float32 HELPER(frint64_s)(float32 f, float_status *fpst) { return frint_s(f, fpst, 64); } @@ -1244,12 +1228,12 @@ static float64 frint_d(float64 f, float_status *fpst, int intsize) return (uint64_t)(0x800 + 1022 + intsize) << 52; } -float64 HELPER(frint32_d)(float64 f, void *fpst) +float64 HELPER(frint32_d)(float64 f, float_status *fpst) { return frint_d(f, fpst, 32); } -float64 HELPER(frint64_d)(float64 f, void *fpst) +float64 HELPER(frint64_d)(float64 f, float_status *fpst) { return frint_d(f, fpst, 64); } From d778bad9a179c709120900326ba2b6d9f7e65014 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:40 +0000 Subject: [PATCH 0386/2892] target/arm: Convert helper-a64.c to fpst alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 98 +++++++++++++------------------------ target/arm/tcg/helper-a64.h | 94 +++++++++++++++++------------------ 2 files changed, 80 insertions(+), 112 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index fb6fe0fcaa..ff48bac1a8 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -130,40 +130,38 @@ static inline uint32_t float_rel_to_flags(int res) return flags; } -uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) +uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, float_status *fp_status) { return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); } -uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, void *fp_status) +uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, float_status *fp_status) { return float_rel_to_flags(float16_compare(x, y, fp_status)); } -uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status) +uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, float_status *fp_status) { return float_rel_to_flags(float32_compare_quiet(x, y, fp_status)); } -uint64_t HELPER(vfp_cmpes_a64)(float32 x, float32 y, void *fp_status) +uint64_t HELPER(vfp_cmpes_a64)(float32 x, float32 y, float_status *fp_status) { return float_rel_to_flags(float32_compare(x, y, fp_status)); } -uint64_t HELPER(vfp_cmpd_a64)(float64 x, float64 y, void *fp_status) +uint64_t HELPER(vfp_cmpd_a64)(float64 x, float64 y, float_status *fp_status) { return float_rel_to_flags(float64_compare_quiet(x, y, fp_status)); } -uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, void *fp_status) +uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, float_status *fp_status) { return float_rel_to_flags(float64_compare(x, y, fp_status)); } -float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) +float32 HELPER(vfp_mulxs)(float32 a, float32 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float32_squash_input_denormal(a, fpst); b = float32_squash_input_denormal(b, fpst); @@ -176,10 +174,8 @@ float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) return float32_mul(a, b, fpst); } -float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) +float64 HELPER(vfp_mulxd)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float64_squash_input_denormal(a, fpst); b = float64_squash_input_denormal(b, fpst); @@ -193,21 +189,18 @@ float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) } /* 64bit/double versions of the neon float compare functions */ -uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, void *fpstp) +uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; return -float64_eq_quiet(a, b, fpst); } -uint64_t HELPER(neon_cge_f64)(float64 a, float64 b, void *fpstp) +uint64_t HELPER(neon_cge_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; return -float64_le(b, a, fpst); } -uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) +uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; return -float64_lt(b, a, fpst); } @@ -216,10 +209,8 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) * multiply-add-and-halve. */ -uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; - a = float16_squash_input_denormal(a, fpst); b = float16_squash_input_denormal(b, fpst); @@ -231,10 +222,8 @@ uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) return float16_muladd(a, b, float16_two, 0, fpst); } -float32 HELPER(recpsf_f32)(float32 a, float32 b, void *fpstp) +float32 HELPER(recpsf_f32)(float32 a, float32 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float32_squash_input_denormal(a, fpst); b = float32_squash_input_denormal(b, fpst); @@ -246,10 +235,8 @@ float32 HELPER(recpsf_f32)(float32 a, float32 b, void *fpstp) return float32_muladd(a, b, float32_two, 0, fpst); } -float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) +float64 HELPER(recpsf_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float64_squash_input_denormal(a, fpst); b = float64_squash_input_denormal(b, fpst); @@ -261,10 +248,8 @@ float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_two, 0, fpst); } -uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; - a = float16_squash_input_denormal(a, fpst); b = float16_squash_input_denormal(b, fpst); @@ -276,10 +261,8 @@ uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) return float16_muladd(a, b, float16_three, float_muladd_halve_result, fpst); } -float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, void *fpstp) +float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float32_squash_input_denormal(a, fpst); b = float32_squash_input_denormal(b, fpst); @@ -291,10 +274,8 @@ float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, void *fpstp) return float32_muladd(a, b, float32_three, float_muladd_halve_result, fpst); } -float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) +float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float64_squash_input_denormal(a, fpst); b = float64_squash_input_denormal(b, fpst); @@ -307,9 +288,8 @@ float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) } /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ -uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) +uint32_t HELPER(frecpx_f16)(uint32_t a, float_status *fpst) { - float_status *fpst = fpstp; uint16_t val16, sbit; int16_t exp; @@ -340,9 +320,8 @@ uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) } } -float32 HELPER(frecpx_f32)(float32 a, void *fpstp) +float32 HELPER(frecpx_f32)(float32 a, float_status *fpst) { - float_status *fpst = fpstp; uint32_t val32, sbit; int32_t exp; @@ -373,9 +352,8 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) } } -float64 HELPER(frecpx_f64)(float64 a, void *fpstp) +float64 HELPER(frecpx_f64)(float64 a, float_status *fpst) { - float_status *fpst = fpstp; uint64_t val64, sbit; int64_t exp; @@ -453,9 +431,8 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) #define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) #define ADVSIMD_HALFOP(name) \ -uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, void *fpstp) \ +uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, float_status *fpst) \ { \ - float_status *fpst = fpstp; \ return float16_ ## name(a, b, fpst); \ } @@ -469,11 +446,11 @@ ADVSIMD_HALFOP(minnum) ADVSIMD_HALFOP(maxnum) #define ADVSIMD_TWOHALFOP(name) \ -uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, void *fpstp) \ +uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, \ + float_status *fpst) \ { \ float16 a1, a2, b1, b2; \ uint32_t r1, r2; \ - float_status *fpst = fpstp; \ a1 = extract32(two_a, 0, 16); \ a2 = extract32(two_a, 16, 16); \ b1 = extract32(two_b, 0, 16); \ @@ -493,10 +470,8 @@ ADVSIMD_TWOHALFOP(minnum) ADVSIMD_TWOHALFOP(maxnum) /* Data processing - scalar floating-point and advanced SIMD */ -static float16 float16_mulx(float16 a, float16 b, void *fpstp) +static float16 float16_mulx(float16 a, float16 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float16_squash_input_denormal(a, fpst); b = float16_squash_input_denormal(b, fpst); @@ -514,16 +489,14 @@ ADVSIMD_TWOHALFOP(mulx) /* fused multiply-accumulate */ uint32_t HELPER(advsimd_muladdh)(uint32_t a, uint32_t b, uint32_t c, - void *fpstp) + float_status *fpst) { - float_status *fpst = fpstp; return float16_muladd(a, b, c, 0, fpst); } uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, - uint32_t two_c, void *fpstp) + uint32_t two_c, float_status *fpst) { - float_status *fpst = fpstp; float16 a1, a2, b1, b2, c1, c2; uint32_t r1, r2; a1 = extract32(two_a, 0, 16); @@ -545,31 +518,27 @@ uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, #define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 -uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; int compare = float16_compare_quiet(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_equal); } -uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_greater || compare == float_relation_equal); } -uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_greater); } -uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; float16 f0 = float16_abs(a); float16 f1 = float16_abs(b); int compare = float16_compare(f0, f1, fpst); @@ -577,9 +546,8 @@ uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; float16 f0 = float16_abs(a); float16 f1 = float16_abs(b); int compare = float16_compare(f0, f1, fpst); @@ -587,12 +555,12 @@ uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) } /* round to integral */ -uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) +uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, float_status *fp_status) { return float16_round_to_int(x, fp_status); } -uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) +uint32_t HELPER(advsimd_rinth)(uint32_t x, float_status *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float16 ret; diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 3c0774139b..f1bac6688a 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -23,57 +23,57 @@ DEF_HELPER_2(msr_i_spsel, void, env, i32) DEF_HELPER_2(msr_i_daifset, void, env, i32) DEF_HELPER_2(msr_i_daifclear, void, env, i32) DEF_HELPER_1(msr_set_allint_el1, void, env) -DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) -DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) -DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) -DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr) -DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr) -DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, ptr) +DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, fpst) +DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, fpst) +DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, fpst) +DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, fpst) +DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, fpst) +DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, fpst) DEF_HELPER_FLAGS_4(simd_tblx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_mulxs, TCG_CALL_NO_RWG, f32, f32, f32, ptr) -DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) -DEF_HELPER_FLAGS_3(neon_cge_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) -DEF_HELPER_FLAGS_3(neon_cgt_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) -DEF_HELPER_FLAGS_3(recpsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(recpsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) -DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) -DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, ptr) +DEF_HELPER_FLAGS_3(vfp_mulxs, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) +DEF_HELPER_FLAGS_3(neon_cge_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) +DEF_HELPER_FLAGS_3(neon_cgt_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) +DEF_HELPER_FLAGS_3(recpsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(recpsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, fpst) DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env) DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) -DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(advsimd_minnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_addh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_subh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_mulh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_divh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_ceq_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_cge_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_cgt_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_acge_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_acgt_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_mulxh, f16, f16, f16, ptr) -DEF_HELPER_4(advsimd_muladdh, f16, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_add2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_sub2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_mul2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_div2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_max2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_min2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_maxnum2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_minnum2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_mulx2h, i32, i32, i32, ptr) -DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, ptr) -DEF_HELPER_2(advsimd_rinth_exact, f16, f16, ptr) -DEF_HELPER_2(advsimd_rinth, f16, f16, ptr) +DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(advsimd_minnumh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_addh, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_subh, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_mulh, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_divh, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_ceq_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_cge_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_cgt_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_acge_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_acgt_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_mulxh, f16, f16, f16, fpst) +DEF_HELPER_4(advsimd_muladdh, f16, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_add2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_sub2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_mul2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_div2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_max2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_min2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_maxnum2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_minnum2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_mulx2h, i32, i32, i32, fpst) +DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, fpst) +DEF_HELPER_2(advsimd_rinth_exact, f16, f16, fpst) +DEF_HELPER_2(advsimd_rinth, f16, f16, fpst) DEF_HELPER_2(exception_return, void, env, i64) DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) From aec7ae42a9f4f3eaf40d66b7be8de8a6da6c9cea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:40 +0000 Subject: [PATCH 0387/2892] target/arm: Convert vec_helper.c to fpst alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 284 ++++++++++++++++++------------------ target/arm/tcg/helper-a64.h | 18 +-- target/arm/tcg/helper-sve.h | 12 +- target/arm/tcg/vec_helper.c | 60 ++++---- 4 files changed, 183 insertions(+), 191 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 5f7bb88067..8ba9e1ce6f 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -624,190 +624,190 @@ DEF_HELPER_FLAGS_5(gvec_usdot_idx_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fcaddd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fcmlah, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fcmlah_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fcmlas, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uitos, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_tosszh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_tosizs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_touszh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_uitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_tosszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_tosizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_touszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ud, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_uh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vrint_rm_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vrint_rm_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vrint_rm_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vrint_rm_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vrintx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_vrintx_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcgt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcge0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fceq0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcle0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -1058,9 +1058,9 @@ DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -1080,25 +1080,25 @@ DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_addp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index f1bac6688a..4d70493914 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -129,14 +129,14 @@ DEF_HELPER_4(cpyfe, void, env, i32, i32, i32) DEF_HELPER_FLAGS_1(guarded_page_check, TCG_CALL_NO_WG, void, env) DEF_HELPER_FLAGS_2(guarded_page_br, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index cc4e1d8948..1fdfb84d4c 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -959,18 +959,18 @@ DEF_HELPER_FLAGS_4(sve_umini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_umini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_5(gvec_recps_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_recps_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_recps_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 768f745828..d2d9d5e829 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -873,13 +873,12 @@ DO_DOT_IDX(gvec_sdot_idx_h, int64_t, int16_t, int16_t, H8) DO_DOT_IDX(gvec_udot_idx_h, uint64_t, uint16_t, uint16_t, H8) void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float16 *d = vd; float16 *n = vn; float16 *m = vm; - float_status *fpst = vfpst; uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = neg_real ^ 1; uintptr_t i; @@ -901,13 +900,12 @@ void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, } void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float32 *d = vd; float32 *n = vn; float32 *m = vm; - float_status *fpst = vfpst; uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = neg_real ^ 1; uintptr_t i; @@ -929,13 +927,12 @@ void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, } void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float64 *d = vd; float64 *n = vn; float64 *m = vm; - float_status *fpst = vfpst; uint64_t neg_real = extract64(desc, SIMD_DATA_SHIFT, 1); uint64_t neg_imag = neg_real ^ 1; uintptr_t i; @@ -957,11 +954,10 @@ void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, } void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float16 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); uint32_t neg_real = flip ^ neg_imag; @@ -984,11 +980,10 @@ void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float16 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); @@ -1019,11 +1014,10 @@ void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_fcmlas)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float32 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); uint32_t neg_real = flip ^ neg_imag; @@ -1046,11 +1040,10 @@ void HELPER(gvec_fcmlas)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float32 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); @@ -1081,11 +1074,10 @@ void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) + float_status *fpst, uint32_t desc) { uintptr_t opr_sz = simd_oprsz(desc); float64 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); uint64_t neg_real = flip ^ neg_imag; @@ -1187,9 +1179,8 @@ static uint64_t float64_acgt(float64 op1, float64 op2, float_status *stat) return -float64_lt(float64_abs(op2), float64_abs(op1), stat); } -static int16_t vfp_tosszh(float16 x, void *fpstp) +static int16_t vfp_tosszh(float16 x, float_status *fpst) { - float_status *fpst = fpstp; if (float16_is_any_nan(x)) { float_raise(float_flag_invalid, fpst); return 0; @@ -1197,9 +1188,8 @@ static int16_t vfp_tosszh(float16 x, void *fpstp) return float16_to_int16_round_to_zero(x, fpst); } -static uint16_t vfp_touszh(float16 x, void *fpstp) +static uint16_t vfp_touszh(float16 x, float_status *fpst) { - float_status *fpst = fpstp; if (float16_is_any_nan(x)) { float_raise(float_flag_invalid, fpst); return 0; @@ -1208,7 +1198,7 @@ static uint16_t vfp_touszh(float16 x, void *fpstp) } #define DO_2OP(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ TYPE *d = vd, *n = vn; \ @@ -1368,7 +1358,8 @@ static float32 float32_rsqrts_nf(float32 op1, float32 op2, float_status *stat) } #define DO_3OP(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ TYPE *d = vd, *n = vn, *m = vm; \ @@ -1522,8 +1513,9 @@ static float64 float64_mulsub_f(float64 dest, float64 op1, float64 op2, return float64_muladd(float64_chs(op1), op2, dest, 0, stat); } -#define DO_MULADD(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +#define DO_MULADD(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ TYPE *d = vd, *n = vn, *m = vm; \ @@ -1600,7 +1592,8 @@ DO_MLA_IDX(gvec_mls_idx_d, uint64_t, -, H8) #undef DO_MLA_IDX #define DO_FMUL_IDX(NAME, ADD, MUL, TYPE, H) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, j, oprsz = simd_oprsz(desc); \ intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ @@ -1644,7 +1637,7 @@ DO_FMUL_IDX(gvec_fmls_nf_idx_s, float32_sub, float32_mul, float32, H4) #define DO_FMLA_IDX(NAME, TYPE, H) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ - void *stat, uint32_t desc) \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, j, oprsz = simd_oprsz(desc); \ intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ @@ -2410,7 +2403,8 @@ DO_ABA(gvec_uaba_d, uint64_t) #undef DO_ABA #define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ ARMVectorReg scratch; \ intptr_t oprsz = simd_oprsz(desc); \ @@ -2495,7 +2489,7 @@ DO_3OP_PAIR(gvec_uminp_s, MIN, uint32_t, H4) #undef DO_3OP_PAIR #define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ - void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ + void HELPER(NAME)(void *vd, void *vn, float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ int shift = simd_data(desc); \ @@ -2524,9 +2518,8 @@ DO_VCVT_FIXED(gvec_vcvt_rz_hu, helper_vfp_touhh_round_to_zero, uint16_t) #undef DO_VCVT_FIXED #define DO_VCVT_RMODE(NAME, FUNC, TYPE) \ - void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ + void HELPER(NAME)(void *vd, void *vn, float_status *fpst, uint32_t desc) \ { \ - float_status *fpst = stat; \ intptr_t i, oprsz = simd_oprsz(desc); \ uint32_t rmode = simd_data(desc); \ uint32_t prev_rmode = get_float_rounding_mode(fpst); \ @@ -2549,9 +2542,8 @@ DO_VCVT_RMODE(gvec_vcvt_rm_uh, helper_vfp_touhh, uint16_t) #undef DO_VCVT_RMODE #define DO_VRINT_RMODE(NAME, FUNC, TYPE) \ - void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ + void HELPER(NAME)(void *vd, void *vn, float_status *fpst, uint32_t desc) \ { \ - float_status *fpst = stat; \ intptr_t i, oprsz = simd_oprsz(desc); \ uint32_t rmode = simd_data(desc); \ uint32_t prev_rmode = get_float_rounding_mode(fpst); \ @@ -3015,7 +3007,7 @@ void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, - void *stat, uint32_t desc) + float_status *stat, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc); intptr_t sel = simd_data(desc); @@ -3031,7 +3023,7 @@ void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, - void *va, void *stat, uint32_t desc) + void *va, float_status *stat, uint32_t desc) { intptr_t i, j, opr_sz = simd_oprsz(desc); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 1); From dae5be12c6e06712bd211e92e2f0b6252a1cc90b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:40 +0000 Subject: [PATCH 0388/2892] target/arm: Convert neon_helper.c to fpst alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 14 +++++++------- target/arm/tcg/neon_helper.c | 21 +++++++-------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 8ba9e1ce6f..9a1b6d1b6d 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -424,13 +424,13 @@ DEF_HELPER_FLAGS_2(neon_qneg_s16, TCG_CALL_NO_RWG, i32, env, i32) DEF_HELPER_FLAGS_2(neon_qneg_s32, TCG_CALL_NO_RWG, i32, env, i32) DEF_HELPER_FLAGS_2(neon_qneg_s64, TCG_CALL_NO_RWG, i64, env, i64) -DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_acge_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_acge_f64, i64, i64, i64, ptr) -DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, ptr) +DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_cge_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acge_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acge_f64, i64, i64, i64, fpst) +DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, fpst) /* iwmmxt_helper.c */ DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index c687e882ad..99fbebbe14 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -1180,51 +1180,44 @@ uint64_t HELPER(neon_qneg_s64)(CPUARMState *env, uint64_t x) * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. */ -uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; return -float32_eq_quiet(make_float32(a), make_float32(b), fpst); } -uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; return -float32_le(make_float32(b), make_float32(a), fpst); } -uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; return -float32_lt(make_float32(b), make_float32(a), fpst); } -uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; float32 f0 = float32_abs(make_float32(a)); float32 f1 = float32_abs(make_float32(b)); return -float32_le(f1, f0, fpst); } -uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; float32 f0 = float32_abs(make_float32(a)); float32 f1 = float32_abs(make_float32(b)); return -float32_lt(f1, f0, fpst); } -uint64_t HELPER(neon_acge_f64)(uint64_t a, uint64_t b, void *fpstp) +uint64_t HELPER(neon_acge_f64)(uint64_t a, uint64_t b, float_status *fpst) { - float_status *fpst = fpstp; float64 f0 = float64_abs(make_float64(a)); float64 f1 = float64_abs(make_float64(b)); return -float64_le(f1, f0, fpst); } -uint64_t HELPER(neon_acgt_f64)(uint64_t a, uint64_t b, void *fpstp) +uint64_t HELPER(neon_acgt_f64)(uint64_t a, uint64_t b, float_status *fpst) { - float_status *fpst = fpstp; float64 f0 = float64_abs(make_float64(a)); float64 f1 = float64_abs(make_float64(b)); return -float64_lt(f1, f0, fpst); From 2115be5953181c3c4f3aa3918b4cbc32de9d4602 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:41 +0000 Subject: [PATCH 0389/2892] target/arm: Convert sve_helper.c to fpst alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 414 ++++++++++++++++++------------------ target/arm/tcg/sve_helper.c | 96 +++++---- 2 files changed, 258 insertions(+), 252 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 1fdfb84d4c..fea43b319c 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -973,419 +973,419 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxnmv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxnmv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxnmv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminnmv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminnmv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminnmv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, - i64, i64, ptr, ptr, ptr, i32) + i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, - i64, i64, ptr, ptr, ptr, i32) + i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_d, TCG_CALL_NO_RWG, - i64, i64, ptr, ptr, ptr, i32) + i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmge0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmge0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmge0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmgt0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmgt0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmgt0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmlt0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmlt0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmlt0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmle0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmle0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmle0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmeq0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmeq0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmeq0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmne0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmne0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmne0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fdiv_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fdiv_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fdiv_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fabd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fabd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fabd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmulx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmulx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmulx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadds_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadds_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadds_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubs_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubs_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubs_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmuls_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmuls_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmuls_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubrs_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubrs_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubrs_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnms_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnms_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnms_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnms_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnms_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnms_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxs_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxs_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxs_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmins_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmins_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmins_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_dh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_hd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_bfcvt, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_hd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_hd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frint_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frint_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frint_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frintx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frintx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frintx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fsqrt_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fsqrt_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fsqrt_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_dh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_dh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmge_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmge_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmge_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmgt_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmgt_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmgt_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmeq_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmeq_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmeq_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmne_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmne_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmne_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmuo_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmuo_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmuo_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facge_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facge_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facge_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facgt_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facgt_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facgt_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve2_saddl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_saddl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -2582,39 +2582,39 @@ DEF_HELPER_FLAGS_4(sve2_xar_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_xar_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sve2_faddp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_faddp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_faddp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxnmp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxnmp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxnmp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminnmp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminnmp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminnmp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_eor3, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_bcax, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -2682,8 +2682,8 @@ DEF_HELPER_FLAGS_5(sve2_sqrdcmlah_zzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve2_sqrdcmlah_zzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(fmmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(fmmla_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(fmmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(fmmla_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_sqrdmlah_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -2755,20 +2755,20 @@ DEF_HELPER_FLAGS_5(sve2_cdot_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_fcvtnt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_fcvtnt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_bfcvtnt, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_fcvtlt_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_fcvtlt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(flogb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(flogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(flogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(flogb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(flogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(flogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve2_sqshl_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 904296705c..85fe3cae3e 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -730,7 +730,7 @@ DO_ZPZZ_PAIR_D(sve2_sminp_zpzz_d, int64_t, DO_MIN) #define DO_ZPZZ_PAIR_FP(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i, opr_sz = simd_oprsz(desc); \ for (i = 0; i < opr_sz; ) { \ @@ -4190,7 +4190,7 @@ static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ return TYPE##_##FUNC(lo, hi, status); \ } \ } \ -uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ +uint64_t HELPER(NAME)(void *vn, void *vg, float_status *s, uint32_t desc) \ { \ uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_data(desc); \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ @@ -4205,7 +4205,7 @@ uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ for (; i < maxsz; i += sizeof(TYPE)) { \ *(TYPE *)((void *)data + i) = IDENT; \ } \ - return NAME##_reduce(data, vs, maxsz / sizeof(TYPE)); \ + return NAME##_reduce(data, s, maxsz / sizeof(TYPE)); \ } DO_REDUCE(sve_faddv_h, float16, H1_2, add, float16_zero) @@ -4232,7 +4232,7 @@ DO_REDUCE(sve_fmaxv_d, float64, H1_8, max, float64_chs(float64_infinity)) #undef DO_REDUCE uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t i = 0, opr_sz = simd_oprsz(desc); float16 result = nn; @@ -4252,7 +4252,7 @@ uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, } uint64_t HELPER(sve_fadda_s)(uint64_t nn, void *vm, void *vg, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t i = 0, opr_sz = simd_oprsz(desc); float32 result = nn; @@ -4272,7 +4272,7 @@ uint64_t HELPER(sve_fadda_s)(uint64_t nn, void *vm, void *vg, } uint64_t HELPER(sve_fadda_d)(uint64_t nn, void *vm, void *vg, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t i = 0, opr_sz = simd_oprsz(desc) / 8; uint64_t *m = vm; @@ -4292,7 +4292,7 @@ uint64_t HELPER(sve_fadda_d)(uint64_t nn, void *vm, void *vg, */ #define DO_ZPZZ_FP(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -4381,7 +4381,7 @@ DO_ZPZZ_FP(sve_fmulx_d, uint64_t, H1_8, helper_vfp_mulxd) */ #define DO_ZPZS_FP(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vg, uint64_t scalar, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -4449,7 +4449,8 @@ DO_ZPZS_FP(sve_fmins_d, float64, H1_8, float64_min) * With the extra float_status parameter. */ #define DO_ZPZ_FP(NAME, TYPE, H, OP) \ -void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -4756,25 +4757,25 @@ static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, } void HELPER(sve_fmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0); } void HELPER(sve_fmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0); } void HELPER(sve_fnmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000); } void HELPER(sve_fnmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0x8000); } @@ -4804,25 +4805,25 @@ static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, } void HELPER(sve_fmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0); } void HELPER(sve_fmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0); } void HELPER(sve_fnmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0x80000000); } void HELPER(sve_fnmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0x80000000); } @@ -4852,25 +4853,25 @@ static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, } void HELPER(sve_fmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0); } void HELPER(sve_fmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, 0); } void HELPER(sve_fnmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, INT64_MIN); } void HELPER(sve_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, INT64_MIN); } @@ -4882,7 +4883,7 @@ void HELPER(sve_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, */ #define DO_FPCMP_PPZZ(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ uint64_t *d = vd, *g = vg; \ @@ -4944,7 +4945,7 @@ DO_FPCMP_PPZZ_ALL(sve_facgt, DO_FACGT) */ #define DO_FPCMP_PPZ0(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ uint64_t *d = vd, *g = vg; \ @@ -4982,7 +4983,8 @@ DO_FPCMP_PPZ0_ALL(sve_fcmne0, DO_FCMNE) /* FP Trig Multiply-Add. */ -void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, + float_status *s, uint32_t desc) { static const float16 coeff[16] = { 0x3c00, 0xb155, 0x2030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, @@ -4998,11 +5000,12 @@ void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) mm = float16_abs(mm); xx += 8; } - d[i] = float16_muladd(n[i], mm, coeff[xx], 0, vs); + d[i] = float16_muladd(n[i], mm, coeff[xx], 0, s); } } -void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, + float_status *s, uint32_t desc) { static const float32 coeff[16] = { 0x3f800000, 0xbe2aaaab, 0x3c088886, 0xb95008b9, @@ -5020,11 +5023,12 @@ void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) mm = float32_abs(mm); xx += 8; } - d[i] = float32_muladd(n[i], mm, coeff[xx], 0, vs); + d[i] = float32_muladd(n[i], mm, coeff[xx], 0, s); } } -void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, + float_status *s, uint32_t desc) { static const float64 coeff[16] = { 0x3ff0000000000000ull, 0xbfc5555555555543ull, @@ -5046,7 +5050,7 @@ void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) mm = float64_abs(mm); xx += 8; } - d[i] = float64_muladd(n[i], mm, coeff[xx], 0, vs); + d[i] = float64_muladd(n[i], mm, coeff[xx], 0, s); } } @@ -5055,7 +5059,7 @@ void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) */ void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, - void *vs, uint32_t desc) + float_status *s, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; @@ -5077,17 +5081,17 @@ void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, e3 = *(float16 *)(vm + H1_2(i)) ^ neg_imag; if (likely((pg >> (i & 63)) & 1)) { - *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, vs); + *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, s); } if (likely((pg >> (j & 63)) & 1)) { - *(float16 *)(vd + H1_2(j)) = float16_add(e2, e3, vs); + *(float16 *)(vd + H1_2(j)) = float16_add(e2, e3, s); } } while (i & 63); } while (i != 0); } void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, - void *vs, uint32_t desc) + float_status *s, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; @@ -5109,17 +5113,17 @@ void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, e3 = *(float32 *)(vm + H1_2(i)) ^ neg_imag; if (likely((pg >> (i & 63)) & 1)) { - *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, vs); + *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, s); } if (likely((pg >> (j & 63)) & 1)) { - *(float32 *)(vd + H1_2(j)) = float32_add(e2, e3, vs); + *(float32 *)(vd + H1_2(j)) = float32_add(e2, e3, s); } } while (i & 63); } while (i != 0); } void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, - void *vs, uint32_t desc) + float_status *s, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; @@ -5141,10 +5145,10 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, e3 = *(float64 *)(vm + H1_2(i)) ^ neg_imag; if (likely((pg >> (i & 63)) & 1)) { - *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, vs); + *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, s); } if (likely((pg >> (j & 63)) & 1)) { - *(float64 *)(vd + H1_2(j)) = float64_add(e2, e3, vs); + *(float64 *)(vd + H1_2(j)) = float64_add(e2, e3, s); } } while (i & 63); } while (i != 0); @@ -5155,7 +5159,7 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, */ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); unsigned rot = simd_data(desc); @@ -5200,7 +5204,7 @@ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, } void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); unsigned rot = simd_data(desc); @@ -5245,7 +5249,7 @@ void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, } void HELPER(sve_fcmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); unsigned rot = simd_data(desc); @@ -7389,7 +7393,7 @@ void HELPER(sve2_xar_s)(void *vd, void *vn, void *vm, uint32_t desc) } void HELPER(fmmla_s)(void *vd, void *vn, void *vm, void *va, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t s, opr_sz = simd_oprsz(desc) / (sizeof(float32) * 4); @@ -7427,7 +7431,7 @@ void HELPER(fmmla_s)(void *vd, void *vn, void *vm, void *va, } void HELPER(fmmla_d)(void *vd, void *vn, void *vm, void *va, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t s, opr_sz = simd_oprsz(desc) / (sizeof(float64) * 4); @@ -7463,7 +7467,8 @@ void HELPER(fmmla_d)(void *vd, void *vn, void *vm, void *va, } #define DO_FCVTNT(NAME, TYPEW, TYPEN, HW, HN, OP) \ -void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -7484,7 +7489,8 @@ DO_FCVTNT(sve2_fcvtnt_sh, uint32_t, uint16_t, H1_4, H1_2, sve_f32_to_f16) DO_FCVTNT(sve2_fcvtnt_ds, uint64_t, uint32_t, H1_8, H1_4, float64_to_float32) #define DO_FCVTLT(NAME, TYPEW, TYPEN, HW, HN, OP) \ -void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ From 00d9edc8d40197b7f752782dde4fdeb61e425d2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:41 +0000 Subject: [PATCH 0390/2892] target/arm: Convert sme_helper.c to fpst alias Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20241206031224.78525-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 4 ++-- target/arm/tcg/sme_helper.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 59ecaa1548..858d69188f 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -123,9 +123,9 @@ DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_bfmopa, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 8cf12654e5..a0e6b4a41e 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -904,7 +904,7 @@ void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, } void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, void *vst, uint32_t desc) + void *vpm, float_status *fpst_in, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) << 31; @@ -916,7 +916,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, * update the cumulative fp exception status. It also produces * default nans. */ - fpst = *(float_status *)vst; + fpst = *fpst_in; set_default_nan_mode(true, &fpst); for (row = 0; row < oprsz; ) { @@ -946,13 +946,13 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, } void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, void *vst, uint32_t desc) + void *vpm, float_status *fpst_in, uint32_t desc) { intptr_t row, col, oprsz = simd_oprsz(desc) / 8; uint64_t neg = (uint64_t)simd_data(desc) << 63; uint64_t *za = vza, *zn = vzn, *zm = vzm; uint8_t *pn = vpn, *pm = vpm; - float_status fpst = *(float_status *)vst; + float_status fpst = *fpst_in; set_default_nan_mode(true, &fpst); From ed57e163bcb730d8a73b516a45ad9a5733da05e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:41 +0000 Subject: [PATCH 0391/2892] target/arm: Convert vec_helper.c to use env alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the helpers to receive CPUARMState* directly instead of via void*. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 12 ++++++------ target/arm/tcg/helper-a64.h | 2 +- target/arm/tcg/vec_helper.c | 21 +++++++-------------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 9a1b6d1b6d..bb241df826 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -859,13 +859,13 @@ DEF_HELPER_FLAGS_5(gvec_suqadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, fpst) DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, fpst) @@ -1036,9 +1036,9 @@ DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sve2_fmlal_zzzw_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sve2_fmlal_zzxw_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_4(gvec_xar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 4d70493914..26e327af3a 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -29,7 +29,7 @@ DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, fpst) DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, fpst) DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, fpst) DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, fpst) -DEF_HELPER_FLAGS_4(simd_tblx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(simd_tblx, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) DEF_HELPER_FLAGS_3(vfp_mulxs, TCG_CALL_NO_RWG, f32, f32, f32, fpst) DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, fpst) DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index d2d9d5e829..e3083c6e84 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2057,28 +2057,25 @@ static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, } void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { - CPUARMState *env = venv; do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { - CPUARMState *env = venv; do_fmlal(vd, vn, vm, &env->vfp.fp_status, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { intptr_t i, oprsz = simd_oprsz(desc); uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - CPUARMState *env = venv; float_status *status = &env->vfp.fp_status; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); @@ -2122,29 +2119,26 @@ static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, } void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { - CPUARMState *env = venv; do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { - CPUARMState *env = venv; do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { intptr_t i, j, oprsz = simd_oprsz(desc); uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); - CPUARMState *env = venv; float_status *status = &env->vfp.fp_status; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); @@ -2562,10 +2556,9 @@ DO_VRINT_RMODE(gvec_vrint_rm_s, helper_rints, uint32_t) #undef DO_VRINT_RMODE #ifdef TARGET_AARCH64 -void HELPER(simd_tblx)(void *vd, void *vm, void *venv, uint32_t desc) +void HELPER(simd_tblx)(void *vd, void *vm, CPUARMState *env, uint32_t desc) { const uint8_t *indices = vm; - CPUARMState *env = venv; size_t oprsz = simd_oprsz(desc); uint32_t rn = extract32(desc, SIMD_DATA_SHIFT, 5); bool is_tbx = extract32(desc, SIMD_DATA_SHIFT + 5, 1); From 1db3b63b5ca7fb4780f8aaf4bb4f6b717218ef10 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:41 +0000 Subject: [PATCH 0392/2892] target/arm: Convert neon_helper.c to use env alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031224.78525-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 56 ++++++++++++++++++------------------ target/arm/tcg/neon_helper.c | 6 ++-- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index bb241df826..707a8daabb 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -310,34 +310,34 @@ DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) -DEF_HELPER_FLAGS_5(neon_sqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 99fbebbe14..e2cc7cf4ee 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -130,11 +130,10 @@ void HELPER(name)(void *vd, void *vn, void *vm, uint32_t desc) \ } #define NEON_GVEC_VOP2_ENV(name, vtype) \ -void HELPER(name)(void *vd, void *vn, void *vm, void *venv, uint32_t desc) \ +void HELPER(name)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) \ { \ intptr_t i, opr_sz = simd_oprsz(desc); \ vtype *d = vd, *n = vn, *m = vm; \ - CPUARMState *env = venv; \ for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ NEON_FN(d[i], n[i], m[i]); \ } \ @@ -142,12 +141,11 @@ void HELPER(name)(void *vd, void *vn, void *vm, void *venv, uint32_t desc) \ } #define NEON_GVEC_VOP2i_ENV(name, vtype) \ -void HELPER(name)(void *vd, void *vn, void *venv, uint32_t desc) \ +void HELPER(name)(void *vd, void *vn, CPUARMState *env, uint32_t desc) \ { \ intptr_t i, opr_sz = simd_oprsz(desc); \ int imm = simd_data(desc); \ vtype *d = vd, *n = vn; \ - CPUARMState *env = venv; \ for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ NEON_FN(d[i], n[i], imm); \ } \ From eec2584d3c03006131696fb343aebf48c696e804 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:42 +0000 Subject: [PATCH 0393/2892] target/arm: Use float_status in helper_fcvtx_f64_to_f32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass float_status not env to match other functions. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031952.78776-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 3 +-- target/arm/tcg/helper-a64.h | 2 +- target/arm/tcg/translate-a64.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index ff48bac1a8..35dce4bef3 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -384,10 +384,9 @@ float64 HELPER(frecpx_f64)(float64 a, float_status *fpst) } } -float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) +float32 HELPER(fcvtx_f64_to_f32)(float64 a, float_status *fpst) { float32 r; - float_status *fpst = &env->vfp.fp_status; int old = get_float_rounding_mode(fpst); set_float_rounding_mode(float_round_to_odd, fpst); diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 26e327af3a..0c120bf388 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -44,7 +44,7 @@ DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, fpst) DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, fpst) -DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env) +DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, fpst) DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3e57b98c27..fda1176b3f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9102,7 +9102,7 @@ static void gen_fcvtxn_sd(TCGv_i64 d, TCGv_i64 n) * with von Neumann rounding (round to odd) */ TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_fcvtx_f64_to_f32(tmp, n, tcg_env); + gen_helper_fcvtx_f64_to_f32(tmp, n, fpstatus_ptr(FPST_FPCR)); tcg_gen_extu_i32_i64(d, tmp); } From 1660866e2c8ab994748ad03e12296ac83a407b10 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Dec 2024 15:05:42 +0000 Subject: [PATCH 0394/2892] target/arm: Use float_status in helper_vfp_fcvt{ds,sd} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass float_status not env to match other functions. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206031952.78776-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 4 ++-- target/arm/tcg/translate-a64.c | 15 ++++++++++----- target/arm/tcg/translate-vfp.c | 4 ++-- target/arm/vfp_helper.c | 8 ++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 707a8daabb..15bad0773c 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -143,8 +143,8 @@ DEF_HELPER_3(vfp_cmpeh, void, f16, f16, env) DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) DEF_HELPER_3(vfp_cmped, void, f64, f64, env) -DEF_HELPER_2(vfp_fcvtds, f64, f32, env) -DEF_HELPER_2(vfp_fcvtsd, f32, f64, env) +DEF_HELPER_2(vfp_fcvtds, f64, f32, fpst) +DEF_HELPER_2(vfp_fcvtsd, f32, f64, fpst) DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, fpst) DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, fpst) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index fda1176b3f..ecbc46ba55 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8502,8 +8502,9 @@ static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tcg_rn = read_fp_sreg(s, a->rn); TCGv_i64 tcg_rd = tcg_temp_new_i64(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, tcg_env); + gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, fpst); write_fp_dreg(s, a->rd, tcg_rd); } return true; @@ -8528,8 +8529,9 @@ static bool trans_FCVT_s_sd(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, tcg_env); + gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, fpst); write_fp_sreg(s, a->rd, tcg_rd); } return true; @@ -9208,7 +9210,9 @@ static void gen_fcvtn_hs(TCGv_i64 d, TCGv_i64 n) static void gen_fcvtn_sd(TCGv_i64 d, TCGv_i64 n) { TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_vfp_fcvtsd(tmp, n, tcg_env); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + + gen_helper_vfp_fcvtsd(tmp, n, fpst); tcg_gen_extu_i32_i64(d, tmp); } @@ -9490,11 +9494,13 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) * The only instruction like this is FCVTL. */ int pass; + TCGv_ptr fpst; if (!fp_access_check(s)) { return true; } + fpst = fpstatus_ptr(FPST_FPCR); if (a->esz == MO_64) { /* 32 -> 64 bit fp conversion */ TCGv_i64 tcg_res[2]; @@ -9504,7 +9510,7 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) for (pass = 0; pass < 2; pass++) { tcg_res[pass] = tcg_temp_new_i64(); read_vec_element_i32(s, tcg_op, a->rn, srcelt + pass, MO_32); - gen_helper_vfp_fcvtds(tcg_res[pass], tcg_op, tcg_env); + gen_helper_vfp_fcvtds(tcg_res[pass], tcg_op, fpst); } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], a->rd, pass, MO_64); @@ -9513,7 +9519,6 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) /* 16 -> 32 bit fp conversion */ int srcelt = a->q ? 4 : 0; TCGv_i32 tcg_res[4]; - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); TCGv_i32 ahp = get_ahp_flag(); for (pass = 0; pass < 4; pass++) { diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index c160a86e70..3cbe9a7418 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -2937,7 +2937,7 @@ static bool trans_VCVT_sp(DisasContext *s, arg_VCVT_sp *a) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i64(); vfp_load_reg32(vm, a->vm); - gen_helper_vfp_fcvtds(vd, vm, tcg_env); + gen_helper_vfp_fcvtds(vd, vm, fpstatus_ptr(FPST_FPCR)); vfp_store_reg64(vd, a->vd); return true; } @@ -2963,7 +2963,7 @@ static bool trans_VCVT_dp(DisasContext *s, arg_VCVT_dp *a) vd = tcg_temp_new_i32(); vm = tcg_temp_new_i64(); vfp_load_reg64(vm, a->vm); - gen_helper_vfp_fcvtsd(vd, vm, tcg_env); + gen_helper_vfp_fcvtsd(vd, vm, fpstatus_ptr(FPST_FPCR)); vfp_store_reg32(vd, a->vd); return true; } diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 8a56936751..fc20a56753 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -400,14 +400,14 @@ FLOAT_CONVS(ui, d, float64, 64, u) #undef FLOAT_CONVS /* floating point conversion */ -float64 VFP_HELPER(fcvtd, s)(float32 x, CPUARMState *env) +float64 VFP_HELPER(fcvtd, s)(float32 x, float_status *status) { - return float32_to_float64(x, &env->vfp.fp_status); + return float32_to_float64(x, status); } -float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) +float32 VFP_HELPER(fcvts, d)(float64 x, float_status *status) { - return float64_to_float32(x, &env->vfp.fp_status); + return float64_to_float32(x, status); } uint32_t HELPER(bfcvt)(float32 x, float_status *status) From 2b745c8f91376b7bf0259b2bfc10015591d2c1c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 14:44:35 +0000 Subject: [PATCH 0395/2892] target/arm: Implement fine-grained-trap handling for FEAT_XS FEAT_XS introduces a set of new TLBI maintenance instructions with an "nXS" qualifier. These behave like the stardard ones except that they do not wait for memory accesses with the XS attribute to complete. They have an interaction with the fine-grained-trap handling: the FGT bits that a hypervisor can use to trap TLBI maintenance instructions normally trap also the nXS variants, but the hypervisor can elect to not trap the nXS variants by setting HCRX_EL2.FGTnXS to 1. Add support to our FGT mechanism for these TLBI bits. For each TLBI-trapping FGT bit we define, for example: * FGT_TLBIVAE1 -- the same value we do at present for the normal variant of the insn * FGT_TLBIVAE1NXS -- for the nXS qualified insn; the value of this enum has an NXS bit ORed into it In access_check_cp_reg() we can then ignore the trap bit for an access where ri->fgt has the NXS bit set and HCRX_EL2.FGTnXS is 1. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241211144440.2700268-2-peter.maydell@linaro.org --- target/arm/cpregs.h | 72 ++++++++++++++++++++++---------------- target/arm/cpu-features.h | 5 +++ target/arm/helper.c | 5 ++- target/arm/tcg/op_helper.c | 11 +++++- 4 files changed, 61 insertions(+), 32 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index cc7c54378f..87704762ef 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -621,6 +621,7 @@ FIELD(HDFGWTR_EL2, NBRBCTL, 60, 1) FIELD(HDFGWTR_EL2, NBRBDATA, 61, 1) FIELD(HDFGWTR_EL2, NPMSNEVFR_EL1, 62, 1) +FIELD(FGT, NXS, 13, 1) /* Honour HCR_EL2.FGTnXS to suppress FGT */ /* Which fine-grained trap bit register to check, if any */ FIELD(FGT, TYPE, 10, 3) FIELD(FGT, REV, 9, 1) /* Is bit sense reversed? */ @@ -639,6 +640,17 @@ FIELD(FGT, BITPOS, 0, 6) /* Bit position within the uint64_t */ #define DO_REV_BIT(REG, BITNAME) \ FGT_##BITNAME = FGT_##REG | FGT_REV | R_##REG##_EL2_##BITNAME##_SHIFT +/* + * The FGT bits for TLBI maintenance instructions accessible at EL1 always + * affect the "normal" TLBI insns; they affect the corresponding TLBI insns + * with the nXS qualifier only if HCRX_EL2.FGTnXS is 0. We define e.g. + * FGT_TLBIVAE1 to use for the normal insn, and FGT_TLBIVAE1NXS to use + * for the nXS qualified insn. + */ +#define DO_TLBINXS_BIT(REG, BITNAME) \ + FGT_##BITNAME = FGT_##REG | R_##REG##_EL2_##BITNAME##_SHIFT, \ + FGT_##BITNAME##NXS = FGT_##BITNAME | R_FGT_NXS_MASK + typedef enum FGTBit { /* * These bits tell us which register arrays to use: @@ -772,36 +784,36 @@ typedef enum FGTBit { DO_BIT(HFGITR, ATS1E0W), DO_BIT(HFGITR, ATS1E1RP), DO_BIT(HFGITR, ATS1E1WP), - DO_BIT(HFGITR, TLBIVMALLE1OS), - DO_BIT(HFGITR, TLBIVAE1OS), - DO_BIT(HFGITR, TLBIASIDE1OS), - DO_BIT(HFGITR, TLBIVAAE1OS), - DO_BIT(HFGITR, TLBIVALE1OS), - DO_BIT(HFGITR, TLBIVAALE1OS), - DO_BIT(HFGITR, TLBIRVAE1OS), - DO_BIT(HFGITR, TLBIRVAAE1OS), - DO_BIT(HFGITR, TLBIRVALE1OS), - DO_BIT(HFGITR, TLBIRVAALE1OS), - DO_BIT(HFGITR, TLBIVMALLE1IS), - DO_BIT(HFGITR, TLBIVAE1IS), - DO_BIT(HFGITR, TLBIASIDE1IS), - DO_BIT(HFGITR, TLBIVAAE1IS), - DO_BIT(HFGITR, TLBIVALE1IS), - DO_BIT(HFGITR, TLBIVAALE1IS), - DO_BIT(HFGITR, TLBIRVAE1IS), - DO_BIT(HFGITR, TLBIRVAAE1IS), - DO_BIT(HFGITR, TLBIRVALE1IS), - DO_BIT(HFGITR, TLBIRVAALE1IS), - DO_BIT(HFGITR, TLBIRVAE1), - DO_BIT(HFGITR, TLBIRVAAE1), - DO_BIT(HFGITR, TLBIRVALE1), - DO_BIT(HFGITR, TLBIRVAALE1), - DO_BIT(HFGITR, TLBIVMALLE1), - DO_BIT(HFGITR, TLBIVAE1), - DO_BIT(HFGITR, TLBIASIDE1), - DO_BIT(HFGITR, TLBIVAAE1), - DO_BIT(HFGITR, TLBIVALE1), - DO_BIT(HFGITR, TLBIVAALE1), + DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIASIDE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVAAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVAALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIASIDE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVAAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVAALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAE1), + DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1), + DO_TLBINXS_BIT(HFGITR, TLBIRVALE1), + DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1), + DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1), + DO_TLBINXS_BIT(HFGITR, TLBIVAE1), + DO_TLBINXS_BIT(HFGITR, TLBIASIDE1), + DO_TLBINXS_BIT(HFGITR, TLBIVAAE1), + DO_TLBINXS_BIT(HFGITR, TLBIVALE1), + DO_TLBINXS_BIT(HFGITR, TLBIVAALE1), DO_BIT(HFGITR, CFPRCTX), DO_BIT(HFGITR, DVPRCTX), DO_BIT(HFGITR, CPPRCTX), diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e806f138b8..30302d6c5b 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -474,6 +474,11 @@ static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; } +static inline bool isar_feature_aa64_xs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, XS) != 0; +} + /* * These are the values from APA/API/APA3. * In general these must be compared '>=', per the normal Arm ARM diff --git a/target/arm/helper.c b/target/arm/helper.c index 910ae62c47..8e62769ec0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5346,10 +5346,13 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; } /* FEAT_CMOW adds CMOW */ - if (cpu_isar_feature(aa64_cmow, cpu)) { valid_mask |= HCRX_CMOW; } + /* FEAT_XS adds FGTnXS, FnXS */ + if (cpu_isar_feature(aa64_xs, cpu)) { + valid_mask |= HCRX_FGTNXS | HCRX_FNXS; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 1ecb465988..1161d301b7 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -817,6 +817,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, unsigned int idx = FIELD_EX32(ri->fgt, FGT, IDX); unsigned int bitpos = FIELD_EX32(ri->fgt, FGT, BITPOS); bool rev = FIELD_EX32(ri->fgt, FGT, REV); + bool nxs = FIELD_EX32(ri->fgt, FGT, NXS); bool trapbit; if (ri->fgt & FGT_EXEC) { @@ -830,7 +831,15 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, trapword = env->cp15.fgt_write[idx]; } - trapbit = extract64(trapword, bitpos, 1); + if (nxs && (arm_hcrx_el2_eff(env) & HCRX_FGTNXS)) { + /* + * If HCRX_EL2.FGTnXS is 1 then the fine-grained trap for + * TLBI maintenance insns does *not* apply to the nXS variant. + */ + trapbit = 0; + } else { + trapbit = extract64(trapword, bitpos, 1); + } if (trapbit != rev) { res = CP_ACCESS_TRAP_EL2; goto fail; From a2508d0e29b69525f39733c5726e1412e5663eb0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 14:44:36 +0000 Subject: [PATCH 0396/2892] target/arm: Add ARM_CP_ADD_TLBI_NXS type flag for NXS insns All of the TLBI insns with an NXS variant put that variant at the same encoding but with a CRn field that is one greater than for the original TLBI insn. To avoid having to define every TLBI insn effectively twice, once in the normal way and once in a set of cpreg arrays that are only registered when FEAT_XS is present, we define a new ARM_CP_ADD_TLB_NXS type flag for cpregs. When this flag is set in a cpreg struct and FEAT_XS is present, define_one_arm_cp_reg_with_opaque() will automatically add a second cpreg to the hash table for the TLBI NXS insn with: * the crn+1 encoding * an FGT field that indicates that it should honour HCR_EL2.FGTnXS * a name with the "NXS" suffix (If there are future TLBI NXS insns that don't use this same encoding convention, it is also possible to define them manually.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241211144440.2700268-3-peter.maydell@linaro.org --- target/arm/cpregs.h | 8 ++++++++ target/arm/helper.c | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 87704762ef..1759d9defb 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -126,6 +126,14 @@ enum { * equivalent EL1 register when FEAT_NV2 is enabled. */ ARM_CP_NV2_REDIRECT = 1 << 20, + /* + * Flag: this is a TLBI insn which (when FEAT_XS is present) also has + * an NXS variant at the same encoding except that crn is 1 greater, + * so when registering this cpreg automatically also register one + * for the TLBI NXS variant. (For QEMU the NXS variant behaves + * identically to the normal one, other than FGT trapping handling.) + */ + ARM_CP_ADD_TLBI_NXS = 1 << 21, }; /* diff --git a/target/arm/helper.c b/target/arm/helper.c index 8e62769ec0..c2a70f8c05 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9146,6 +9146,31 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, if (r->state != state && r->state != ARM_CP_STATE_BOTH) { continue; } + if ((r->type & ARM_CP_ADD_TLBI_NXS) && + cpu_isar_feature(aa64_xs, cpu)) { + /* + * This is a TLBI insn which has an NXS variant. The + * NXS variant is at the same encoding except that + * crn is +1, and has the same behaviour except for + * fine-grained trapping. Add the NXS insn here and + * then fall through to add the normal register. + * add_cpreg_to_hashtable() copies the cpreg struct + * and name that it is passed, so it's OK to use + * a local struct here. + */ + ARMCPRegInfo nxs_ri = *r; + g_autofree char *name = g_strdup_printf("%sNXS", r->name); + + assert(state == ARM_CP_STATE_AA64); + assert(nxs_ri.crn < 0xf); + nxs_ri.crn++; + if (nxs_ri.fgt) { + nxs_ri.fgt |= R_FGT_NXS_MASK; + } + add_cpreg_to_hashtable(cpu, &nxs_ri, opaque, state, + ARM_CP_SECSTATE_NS, + crm, opc1, opc2, name); + } if (state == ARM_CP_STATE_AA32) { /* * Under AArch32 CP registers can be common From 4278186a3045d14723b9445a45ee9bca0fd023f4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 11 Dec 2024 14:44:37 +0000 Subject: [PATCH 0397/2892] target/arm: Add ARM_CP_ADD_TLBI_NXS type flag to TLBI insns Add the ARM_CP_ADD_TLBI_NXS to the TLBI insns with an NXS variant. This is every AArch64 TLBI encoding except for the four FEAT_RME TLBI insns. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241211144440.2700268-4-peter.maydell@linaro.org --- target/arm/tcg/tlb-insns.c | 202 +++++++++++++++++++++++-------------- 1 file changed, 124 insertions(+), 78 deletions(-) diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 0f67294edc..fadc61a76e 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -617,95 +617,107 @@ static const ARMCPRegInfo tlbi_v8_cp_reginfo[] = { /* AArch64 TLBI operations */ { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVMALLE1IS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIASIDE1IS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAAE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVALE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAALE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVMALLE1, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIASIDE1, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAAE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVALE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAALE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ipas2e1is_write }, { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ipas2e1is_write }, { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle1is_write }, { .name = "TLBI_VMALLS12E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle1is_write }, { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ipas2e1_write }, { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ipas2e1_write }, { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle1_write }, { .name = "TLBI_VMALLS12E1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle1is_write }, }; @@ -732,54 +744,60 @@ static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = { .writefn = tlbimva_hyp_is_write }, { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_alle2_write }, { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2_write }, { .name = "TLBI_VALE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2_write }, { .name = "TLBI_ALLE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_alle2is_write }, { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2is_write }, { .name = "TLBI_VALE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2is_write }, }; static const ARMCPRegInfo tlbi_el3_cp_reginfo[] = { { .name = "TLBI_ALLE3IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle3is_write }, { .name = "TLBI_VAE3IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae3is_write }, { .name = "TLBI_VALE3IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae3is_write }, { .name = "TLBI_ALLE3", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle3_write }, { .name = "TLBI_VAE3", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae3_write }, { .name = "TLBI_VALE3", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae3_write }, }; @@ -981,204 +999,232 @@ static void tlbi_aa64_ripas2e1is_write(CPUARMState *env, static const ARMCPRegInfo tlbirange_reginfo[] = { { .name = "TLBI_RVAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAAE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVALE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAALE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAAE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVALE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAALE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIRVAALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ripas2e1is_write }, { .name = "TLBI_RIPAS2LE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ripas2e1is_write }, { .name = "TLBI_RVAE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_rvae2is_write }, { .name = "TLBI_RVALE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_rvae2is_write }, { .name = "TLBI_RIPAS2E1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ripas2e1_write }, { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_ripas2e1_write }, { .name = "TLBI_RVAE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_rvae2is_write }, { .name = "TLBI_RVALE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_rvae2is_write }, { .name = "TLBI_RVAE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_rvae2_write }, { .name = "TLBI_RVALE2", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_rvae2_write }, { .name = "TLBI_RVAE3IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_rvae3is_write }, { .name = "TLBI_RVALE3IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_rvae3is_write }, { .name = "TLBI_RVAE3OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_rvae3is_write }, { .name = "TLBI_RVALE3OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_rvae3is_write }, { .name = "TLBI_RVAE3", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_rvae3_write }, { .name = "TLBI_RVALE3", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_rvae3_write }, }; static const ARMCPRegInfo tlbios_reginfo[] = { { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVMALLE1OS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, .fgt = FGT_TLBIVAE1OS, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIASIDE1OS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAAE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVALE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .fgt = FGT_TLBIVAALE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ALLE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_alle2is_write }, { .name = "TLBI_VAE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2is_write }, { .name = "TLBI_ALLE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle1is_write }, { .name = "TLBI_VALE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2is_write }, { .name = "TLBI_VMALLS12E1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle1is_write }, { .name = "TLBI_IPAS2E1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, { .name = "TLBI_RIPAS2E1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 3, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, { .name = "TLBI_IPAS2LE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, { .name = "TLBI_RIPAS2LE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 7, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, { .name = "TLBI_ALLE3OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_alle3is_write }, { .name = "TLBI_VAE3OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae3is_write }, { .name = "TLBI_VALE3OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, .writefn = tlbi_aa64_vae3is_write }, }; From a65a24b9cfcff5bb132386fc78ab87c0019d396c Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 11 Dec 2024 14:44:38 +0000 Subject: [PATCH 0398/2892] target/arm: Add decodetree entry for DSB nXS variant The DSB nXS variant is always both a reads and writes request type. Ignore the domain field like we do in plain DSB and perform a full system barrier operation. The DSB nXS variant is part of FEAT_XS made mandatory from Armv8.7. Signed-off-by: Manos Pitsidianakis Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241211144440.2700268-5-peter.maydell@linaro.org [PMM: added missing "UNDEF unless feature present" check] Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 +++ target/arm/tcg/translate-a64.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7aa10f5147..8c798cde2b 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -260,6 +260,9 @@ WFIT 1101 0101 0000 0011 0001 0000 001 rd:5 CLREX 1101 0101 0000 0011 0011 ---- 010 11111 DSB_DMB 1101 0101 0000 0011 0011 domain:2 types:2 10- 11111 +# For the DSB nXS variant, types always equals MBReqTypes_All and we ignore the +# domain bits. +DSB_nXS 1101 0101 0000 0011 0011 -- 10 001 11111 ISB 1101 0101 0000 0011 0011 ---- 110 11111 SB 1101 0101 0000 0011 0011 0000 111 11111 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ecbc46ba55..7c65fc3a3b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1986,6 +1986,15 @@ static bool trans_DSB_DMB(DisasContext *s, arg_DSB_DMB *a) return true; } +static bool trans_DSB_nXS(DisasContext *s, arg_DSB_nXS *a) +{ + if (!dc_isar_feature(aa64_xs, s)) { + return false; + } + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + return true; +} + static bool trans_ISB(DisasContext *s, arg_ISB *a) { /* From 19db1d4da7ba78f3774624a4125c7c78542ed735 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 11 Dec 2024 14:44:39 +0000 Subject: [PATCH 0399/2892] target/arm: Enable FEAT_XS for the max cpu Add FEAT_XS feature report value in max cpu's ID_AA64ISAR1 sys register. Signed-off-by: Manos Pitsidianakis Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20241211144440.2700268-6-peter.maydell@linaro.org [PMM: Add entry for FEAT_XS to documentation] Reviewed-by: Richard Henderson --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 38534dcdd3..60176d0859 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -154,6 +154,7 @@ the following architecture extensions: - FEAT_VMID16 (16-bit VMID) - FEAT_WFxT (WFE and WFI instructions with timeout) - FEAT_XNX (Translation table stage 2 Unprivileged Execute-never) +- FEAT_XS (XS attribute) For information on the specifics of these extensions, please refer to the `Arm Architecture Reference Manual for A-profile architecture diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 67c110f021..93573ceeb1 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1163,6 +1163,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 2); /* FEAT_BF16, FEAT_EBF16 */ t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ + t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* FEAT_XS */ cpu->isar.id_aa64isar1 = t; t = cpu->isar.id_aa64isar2; From daae2280cad120a307083342ed0913f523b5aeb1 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 11 Dec 2024 14:44:40 +0000 Subject: [PATCH 0400/2892] tests/tcg/aarch64: add system test for FEAT_XS Add system test to make sure FEAT_XS is enabled for max cpu emulation and that QEMU doesn't crash when encountering an NXS instruction variant. Signed-off-by: Manos Pitsidianakis Signed-off-by: Peter Maydell Message-id: 20241211144440.2700268-7-peter.maydell@linaro.org [PMM: In ISAR field test, mask with 0xf, not 0xff; use < rather than an equality test to follow the standard ID register field check guidelines] Signed-off-by: Peter Maydell --- tests/tcg/aarch64/system/feat-xs.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/tcg/aarch64/system/feat-xs.c diff --git a/tests/tcg/aarch64/system/feat-xs.c b/tests/tcg/aarch64/system/feat-xs.c new file mode 100644 index 0000000000..f310fc837e --- /dev/null +++ b/tests/tcg/aarch64/system/feat-xs.c @@ -0,0 +1,27 @@ +/* + * FEAT_XS Test + * + * Copyright (c) 2024 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +int main(void) +{ + uint64_t isar1; + + asm volatile ("mrs %0, id_aa64isar1_el1" : "=r"(isar1)); + if (((isar1 >> 56) & 0xf) < 1) { + ml_printf("FEAT_XS not supported by CPU"); + return 1; + } + /* VMALLE1NXS */ + asm volatile (".inst 0xd508971f"); + /* VMALLE1OSNXS */ + asm volatile (".inst 0xd508911f"); + + return 0; +} From 9678b9c505725732353baefedb88b53c2eb8a184 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Dec 2024 18:23:37 +0000 Subject: [PATCH 0401/2892] hw/intc/arm_gicv3_its: Zero initialize local DTEntry etc structs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the GICv3 ITS model, we have a common coding pattern which has a local C struct like "DTEntry dte", which is a C representation of an in-guest-memory data structure, and we call a function such as get_dte() to read guest memory and fill in the C struct. These functions to read in the struct sometimes have cases where they will leave early and not fill in the whole struct (for instance get_dte() will set "dte->valid = false" and nothing else for the case where it is passed an entry_addr implying that there is no L2 table entry for the DTE). This then causes potential use of uninitialized memory later, for instance when we call a trace event which prints all the fields of the struct. Sufficiently advanced compilers may produce -Wmaybe-uninitialized warnings about this, especially if LTO is enabled. Rather than trying to carefully separate out these trace events into "only the 'valid' field is initialized" and "all fields can be printed", zero-init all the structs when we define them. None of these structs are large (the biggest is 24 bytes) and having consistent behaviour is less likely to be buggy. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2718 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241213182337.3343068-1-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index f50b1814ea..0de7643008 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -465,7 +465,7 @@ static ItsCmdResult lookup_vte(GICv3ITSState *s, const char *who, static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite, int irqlevel) { - CTEntry cte; + CTEntry cte = {}; ItsCmdResult cmdres; cmdres = lookup_cte(s, __func__, ite->icid, &cte); @@ -479,7 +479,7 @@ static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite, static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite, int irqlevel) { - VTEntry vte; + VTEntry vte = {}; ItsCmdResult cmdres; cmdres = lookup_vte(s, __func__, ite->vpeid, &vte); @@ -514,8 +514,8 @@ static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite, static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint32_t eventid, ItsCmdType cmd) { - DTEntry dte; - ITEntry ite; + DTEntry dte = {}; + ITEntry ite = {}; ItsCmdResult cmdres; int irqlevel; @@ -583,8 +583,8 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, uint32_t pIntid = 0; uint64_t num_eventids; uint16_t icid = 0; - DTEntry dte; - ITEntry ite; + DTEntry dte = {}; + ITEntry ite = {}; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; eventid = cmdpkt[1] & EVENTID_MASK; @@ -651,8 +651,8 @@ static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt, { uint32_t devid, eventid, vintid, doorbell, vpeid; uint32_t num_eventids; - DTEntry dte; - ITEntry ite; + DTEntry dte = {}; + ITEntry ite = {}; if (!its_feature_virtual(s)) { return CMD_CONTINUE; @@ -761,7 +761,7 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, const CTEntry *cte) static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) { uint16_t icid; - CTEntry cte; + CTEntry cte = {}; icid = cmdpkt[2] & ICID_MASK; cte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; @@ -822,7 +822,7 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, const DTEntry *dte) static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid; - DTEntry dte; + DTEntry dte = {}; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; dte.size = cmdpkt[1] & SIZE_MASK; @@ -886,9 +886,9 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid; uint16_t new_icid; - DTEntry dte; - CTEntry old_cte, new_cte; - ITEntry old_ite; + DTEntry dte = {}; + CTEntry old_cte = {}, new_cte = {}; + ITEntry old_ite = {}; ItsCmdResult cmdres; devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); @@ -965,7 +965,7 @@ static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte) static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt) { - VTEntry vte; + VTEntry vte = {}; uint32_t vpeid; if (!its_feature_virtual(s)) { @@ -1030,7 +1030,7 @@ static void vmovp_callback(gpointer data, gpointer opaque) */ GICv3ITSState *s = data; VmovpCallbackData *cbdata = opaque; - VTEntry vte; + VTEntry vte = {}; ItsCmdResult cmdres; cmdres = lookup_vte(s, __func__, cbdata->vpeid, &vte); @@ -1085,9 +1085,9 @@ static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid, vpeid, doorbell; bool doorbell_valid; - DTEntry dte; - ITEntry ite; - VTEntry old_vte, new_vte; + DTEntry dte = {}; + ITEntry ite = {}; + VTEntry old_vte = {}, new_vte = {}; ItsCmdResult cmdres; if (!its_feature_virtual(s)) { @@ -1186,10 +1186,10 @@ static ItsCmdResult process_vinvall(GICv3ITSState *s, const uint64_t *cmdpkt) static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid; - ITEntry ite; - DTEntry dte; - CTEntry cte; - VTEntry vte; + ITEntry ite = {}; + DTEntry dte = {}; + CTEntry cte = {}; + VTEntry vte = {}; ItsCmdResult cmdres; devid = FIELD_EX64(cmdpkt[0], INV_0, DEVICEID); From e91254250acb8570bd7b8a8f89d30e6d18291d02 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Mon, 25 Nov 2024 13:54:48 +0100 Subject: [PATCH 0402/2892] tests/functional: update sbsa-ref firmware used in test Update the URLs for the binaries we use for the firmware in the sbsa-ref functional tests. The firmware is built using Debian 'bookworm' cross toolchain (gcc 12.2.0). Used versions: - Trusted Firmware v2.12.0 - Tianocore EDK2 stable202411 - Tianocore EDK2 Platforms code commit 4b3530d This allows us to move away from "some git commit on trunk" to a stable release for both TF-A and EDK2. Signed-off-by: Marcin Juszkiewicz Message-id: 20241125125448.185504-1-marcin.juszkiewicz@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/functional/test_aarch64_sbsaref.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index 6db08da522..52507af2bd 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -24,9 +24,9 @@ def fetch_firmware(test): Used components: - - Trusted Firmware v2.11.0 - - Tianocore EDK2 4d4f569924 - - Tianocore EDK2-platforms 3f08401 + - Trusted Firmware v2.12.0 + - Tianocore EDK2 edk2-stable202411 + - Tianocore EDK2-platforms 4b3530d """ @@ -62,13 +62,13 @@ class Aarch64SbsarefMachine(QemuSystemTest): ASSET_FLASH0 = Asset( ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' - '20240619-148232/edk2/SBSA_FLASH0.fd.xz'), - '0c954842a590988f526984de22e21ae0ab9cb351a0c99a8a58e928f0c7359cf7') + '20241122-189881/edk2/SBSA_FLASH0.fd.xz'), + '76eb89d42eebe324e4395329f47447cda9ac920aabcf99aca85424609c3384a5') ASSET_FLASH1 = Asset( ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' - '20240619-148232/edk2/SBSA_FLASH1.fd.xz'), - 'c6ec39374c4d79bb9e9cdeeb6db44732d90bb4a334cec92002b3f4b9cac4b5ee') + '20241122-189881/edk2/SBSA_FLASH1.fd.xz'), + 'f850f243bd8dbd49c51e061e0f79f1697546938f454aeb59ab7d93e5f0d412fc') def test_sbsaref_edk2_firmware(self): @@ -86,15 +86,15 @@ class Aarch64SbsarefMachine(QemuSystemTest): # AP Trusted ROM wait_for_console_pattern(self, "Booting Trusted Firmware") - wait_for_console_pattern(self, "BL1: v2.11.0(release):") + wait_for_console_pattern(self, "BL1: v2.12.0(release):") wait_for_console_pattern(self, "BL1: Booting BL2") # Trusted Boot Firmware - wait_for_console_pattern(self, "BL2: v2.11.0(release)") + wait_for_console_pattern(self, "BL2: v2.12.0(release)") wait_for_console_pattern(self, "Booting BL31") # EL3 Runtime Software - wait_for_console_pattern(self, "BL31: v2.11.0(release)") + wait_for_console_pattern(self, "BL31: v2.12.0(release)") # Non-trusted Firmware wait_for_console_pattern(self, "UEFI firmware (version 1.0") From 11b88e07f0c221890502250bfe758c526f93cc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:22 +0000 Subject: [PATCH 0403/2892] tests/functional: add execute permission to aspeed tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests are expected to be directly invoked when debugging so must have execute permission. Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20241217155953.3950506-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_aspeed.py | 0 tests/functional/test_arm_aspeed_ast1030.py | 0 tests/functional/test_arm_aspeed_ast2500.py | 0 tests/functional/test_arm_aspeed_ast2600.py | 0 tests/functional/test_arm_aspeed_palmetto.py | 0 tests/functional/test_arm_aspeed_rainier.py | 0 tests/functional/test_arm_aspeed_romulus.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/functional/test_aarch64_aspeed.py mode change 100644 => 100755 tests/functional/test_arm_aspeed_ast1030.py mode change 100644 => 100755 tests/functional/test_arm_aspeed_ast2500.py mode change 100644 => 100755 tests/functional/test_arm_aspeed_ast2600.py mode change 100644 => 100755 tests/functional/test_arm_aspeed_palmetto.py mode change 100644 => 100755 tests/functional/test_arm_aspeed_rainier.py mode change 100644 => 100755 tests/functional/test_arm_aspeed_romulus.py diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py old mode 100644 new mode 100755 diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py old mode 100644 new mode 100755 diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py old mode 100644 new mode 100755 diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py old mode 100644 new mode 100755 diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/test_arm_aspeed_palmetto.py old mode 100644 new mode 100755 diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/test_arm_aspeed_rainier.py old mode 100644 new mode 100755 diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/test_arm_aspeed_romulus.py old mode 100644 new mode 100755 From 8a6253a43a43084275b31ef05e674e4987b05a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:23 +0000 Subject: [PATCH 0404/2892] tests/functional: remove many unused imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Identified using 'pylint --disable=all --enable=W0611' Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 1 - tests/functional/qemu_test/tesseract.py | 1 - tests/functional/qemu_test/tuxruntest.py | 3 +-- tests/functional/test_aarch64_aspeed.py | 1 - tests/functional/test_aarch64_sbsaref_alpine.py | 1 - tests/functional/test_aarch64_sbsaref_freebsd.py | 1 - tests/functional/test_acpi_bits.py | 2 -- tests/functional/test_arm_bpim2u.py | 2 +- tests/functional/test_arm_collie.py | 2 +- tests/functional/test_arm_cubieboard.py | 1 - tests/functional/test_arm_orangepi.py | 2 +- tests/functional/test_arm_smdkc210.py | 3 +-- tests/functional/test_arm_sx1.py | 2 +- tests/functional/test_microblaze_s3adsp1800.py | 1 - tests/functional/test_ppc_amiga.py | 2 +- tests/functional/test_virtio_gpu.py | 1 - 16 files changed, 7 insertions(+), 19 deletions(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index f126cd5863..559af0351f 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -9,7 +9,6 @@ import hashlib import logging import os import stat -import subprocess import sys import unittest import urllib.request diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py index db441027b9..ef1833139d 100644 --- a/tests/functional/qemu_test/tesseract.py +++ b/tests/functional/qemu_test/tesseract.py @@ -5,7 +5,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import re import logging from . import has_cmd, run_cmd diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index ab3b27da43..d375f2713b 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -11,10 +11,9 @@ import os import stat -import time from qemu_test import QemuSystemTest -from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern from qemu_test import has_cmd, run_cmd, get_qemu_img diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 59916efd71..e196f88537 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -6,7 +6,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import sys import os from qemu_test import QemuSystemTest, Asset diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py index ebc29b2fb5..6dbc90f30e 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -12,7 +12,6 @@ import os from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test import interrupt_interactive_console_until_pattern from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index 80298dd190..77ba2ba1da 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -12,7 +12,6 @@ import os from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test import interrupt_interactive_console_until_pattern from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 63e2c5309d..3df9562394 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -31,14 +31,12 @@ including an upgraded acpica. The fork is located here: https://gitlab.com/qemu-project/biosbits-bits . """ -import logging import os import platform import re import shutil import subprocess import tarfile -import tempfile import zipfile from pathlib import Path diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index 35ea58d46c..c9cf43c147 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -9,7 +9,7 @@ import os from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern from qemu_test import Asset, interrupt_interactive_console_until_pattern -from qemu_test.utils import archive_extract, gzip_uncompress, lzma_uncompress +from qemu_test.utils import gzip_uncompress, lzma_uncompress from qemu_test.utils import image_pow2ceil_expand from unittest import skipUnless diff --git a/tests/functional/test_arm_collie.py b/tests/functional/test_arm_collie.py index 7e144a0a8f..fe1be3d079 100755 --- a/tests/functional/test_arm_collie.py +++ b/tests/functional/test_arm_collie.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class CollieTest(LinuxKernelTest): diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index 2b33a1b50b..9e42b72060 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os -import shutil from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern from qemu_test import interrupt_interactive_console_until_pattern diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index 6d57223a03..a872305e93 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -11,7 +11,7 @@ import shutil from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern from qemu_test import Asset, interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract, gzip_uncompress, lzma_uncompress +from qemu_test.utils import gzip_uncompress, lzma_uncompress from qemu_test.utils import image_pow2ceil_expand from unittest import skipUnless diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py index 967752feeb..b3b39b069d 100755 --- a/tests/functional/test_arm_smdkc210.py +++ b/tests/functional/test_arm_smdkc210.py @@ -5,9 +5,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os -import shutil -from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import LinuxKernelTest, Asset from qemu_test.utils import gzip_uncompress class Smdkc210Machine(LinuxKernelTest): diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py index 2292317946..b85bfaa178 100755 --- a/tests/functional/test_arm_sx1.py +++ b/tests/functional/test_arm_sx1.py @@ -14,7 +14,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class SX1Test(LinuxKernelTest): diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index d2be3105a2..d452a0271c 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -7,7 +7,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test import exec_command, exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test.utils import archive_extract diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/test_ppc_amiga.py index b793b5c432..f5faa0f0b3 100755 --- a/tests/functional/test_ppc_amiga.py +++ b/tests/functional/test_ppc_amiga.py @@ -10,7 +10,7 @@ import subprocess from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern, run_cmd +from qemu_test import wait_for_console_pattern from zipfile import ZipFile class AmigaOneMachine(QemuSystemTest): diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py index d5027487ac..2d298b1f02 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -12,7 +12,6 @@ from qemu_test import wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern from qemu_test import is_readable_executable_file -from qemu.utils import kvm_available import os import socket From c27f452d61a4741605da2e03c0e6c756dd249f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:24 +0000 Subject: [PATCH 0405/2892] tests/functional: resolve str(Asset) to cache file path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow an Asset object to be used in place of a filename but making its string representation resolve to the cache file path. Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20241217155953.3950506-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 559af0351f..c5d3e73c4b 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -39,6 +39,9 @@ class Asset: return "Asset: url=%s hash=%s cache=%s" % ( self.url, self.hash, self.cache_file) + def __str__(self): + return str(self.cache_file) + def _check(self, cache_file): if self.hash is None: return True From 0da341a78f00d6feae98f38d1dfbe2e9f88d0b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:25 +0000 Subject: [PATCH 0406/2892] tests/functional: remove duplicated 'which' function impl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put the 'which' function into shared code. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/cmd.py | 10 ++++++++++ tests/functional/test_acpi_bits.py | 13 +------------ tests/functional/test_ppc64_hv.py | 13 +------------ 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 67f87be9c4..8fddddbe67 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -10,6 +10,6 @@ from .asset import Asset from .config import BUILD_DIR from .cmd import has_cmd, has_cmds, run_cmd, is_readable_executable_file, \ interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ - exec_command, exec_command_and_wait_for_pattern, get_qemu_img + exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest from .linuxkernel import LinuxKernelTest diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index 11c8334a7c..4106f1ee7c 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -18,6 +18,16 @@ import subprocess from .config import BUILD_DIR +def which(tool): + """ looks up the full path for @tool, returns None if not found + or if @tool does not have executable permissions. + """ + paths=os.getenv('PATH') + for p in paths.split(os.path.pathsep): + p = os.path.join(p, tool) + if os.path.exists(p) and os.access(p, os.X_OK): + return p + return None def has_cmd(name, args=None): """ diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 3df9562394..1e6d082ecb 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -47,7 +47,7 @@ from typing import ( ) from qemu.machine import QEMUMachine from unittest import skipIf -from qemu_test import QemuSystemTest, Asset +from qemu_test import QemuSystemTest, Asset, which deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. supported_platforms = ['x86_64'] # supported test platforms. @@ -55,17 +55,6 @@ supported_platforms = ['x86_64'] # supported test platforms. # default timeout of 120 secs is sometimes not enough for bits test. BITS_TIMEOUT = 200 -def which(tool): - """ looks up the full path for @tool, returns None if not found - or if @tool does not have executable permissions. - """ - paths=os.getenv('PATH') - for p in paths.split(os.path.pathsep): - p = os.path.join(p, tool) - if os.path.exists(p) and os.access(p, os.X_OK): - return p - return None - def missing_deps(): """ returns True if any of the test dependent tools are absent. """ diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index d97b62e364..7c6f8234f5 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -11,7 +11,7 @@ from unittest import skipIf, skipUnless from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern, exec_command +from qemu_test import wait_for_console_pattern, exec_command, which import os import time import subprocess @@ -19,17 +19,6 @@ from datetime import datetime deps = ["xorriso"] # dependent tools needed in the test setup/box. -def which(tool): - """ looks up the full path for @tool, returns None if not found - or if @tool does not have executable permissions. - """ - paths=os.getenv('PATH') - for p in paths.split(os.path.pathsep): - p = os.path.join(p, tool) - if os.path.exists(p) and os.access(p, os.X_OK): - return p - return None - def missing_deps(): """ returns True if any of the test dependent tools are absent. """ From dd6dfc012c914d3e81be1da4b0ca7328949bdd08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:26 +0000 Subject: [PATCH 0407/2892] tests/functional: simplify 'which' implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'access' check implies the file exists. Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-6-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index 4106f1ee7c..600e0509db 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -25,7 +25,7 @@ def which(tool): paths=os.getenv('PATH') for p in paths.split(os.path.pathsep): p = os.path.join(p, tool) - if os.path.exists(p) and os.access(p, os.X_OK): + if os.access(p, os.X_OK): return p return None From c54edc71093bf85550245250d1f5922465848e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:27 +0000 Subject: [PATCH 0408/2892] tests/functional: drop 'tesseract_available' helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Platforms we target have new enough tesseract that it suffices to merely check if the binary exists. Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20241217155953.3950506-7-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/tesseract.py | 12 +----------- tests/functional/test_m68k_nextcube.py | 8 +++----- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py index ef1833139d..1b7818090a 100644 --- a/tests/functional/qemu_test/tesseract.py +++ b/tests/functional/qemu_test/tesseract.py @@ -7,17 +7,7 @@ import logging -from . import has_cmd, run_cmd - -def tesseract_available(expected_version): - (has_tesseract, _) = has_cmd('tesseract') - if not has_tesseract: - return False - (stdout, stderr, ret) = run_cmd([ 'tesseract', '--version']) - if ret: - return False - version = stdout.split()[1] - return int(version.split('.')[0]) >= expected_version +from . import run_cmd def tesseract_ocr(image_path, tesseract_args=''): console_logger = logging.getLogger('console') diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py index 0124622c40..e6e8d4fd3f 100755 --- a/tests/functional/test_m68k_nextcube.py +++ b/tests/functional/test_m68k_nextcube.py @@ -13,7 +13,8 @@ import time from qemu_test import QemuSystemTest, Asset from unittest import skipUnless -from qemu_test.tesseract import tesseract_available, tesseract_ocr +from qemu_test import has_cmd +from qemu_test.tesseract import tesseract_ocr PIL_AVAILABLE = True try: @@ -53,10 +54,7 @@ class NextCubeMachine(QemuSystemTest): self.assertEqual(width, 1120) self.assertEqual(height, 832) - # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The - # new version is faster and more accurate than version 3. The drawback is - # that it is still alpha-level software. - @skipUnless(tesseract_available(4), 'tesseract OCR tool not available') + @skipUnless(*has_cmd('tesseract')) def test_bootrom_framebuffer_ocr_with_tesseract(self): self.set_machine('next-cube') screenshot_path = os.path.join(self.workdir, "dump.ppm") From 3ea06d65be5ee65beabc479d92e964e3c9df5080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:28 +0000 Subject: [PATCH 0409/2892] tests/functional: introduce some helpful decorators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce repeated boilerplate with some helper decorators: @skipIfNotPlatform("x86_64", "aarch64") => Skip unless the build host platform matches @skipIfMissingCommands("mkisofs", "losetup") => Skips unless all listed commands are found in $PATH @skipIfMissingImports("numpy", "cv2") => Skips unless all listed modules can be imported @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN") => Skips unless env var requests flaky tests with the reason documented in the referenced gitlab bug @skipBigData => Skips unless env var permits tests creating big data files @skipUntrustedTest => Skips unless env var permits tests which are potentially dangerous to the host Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-8-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 3 + tests/functional/qemu_test/decorators.py | 107 +++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tests/functional/qemu_test/decorators.py diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 8fddddbe67..7dee3522f2 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -13,3 +13,6 @@ from .cmd import has_cmd, has_cmds, run_cmd, is_readable_executable_file, \ exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest from .linuxkernel import LinuxKernelTest +from .decorators import skipIfMissingCommands, skipIfNotMachine, \ + skipFlakyTest, skipUntrustedTest, skipBigDataTest, \ + skipIfMissingImports diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py new file mode 100644 index 0000000000..df088bc090 --- /dev/null +++ b/tests/functional/qemu_test/decorators.py @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Decorators useful in functional tests + +import os +import platform +from unittest import skipUnless + +from .cmd import which + +''' +Decorator to skip execution of a test if the list +of command binaries is not available in $PATH. +Example: + + @skipIfMissingCommands("mkisofs", "losetup") +''' +def skipIfMissingCommands(*args): + def has_cmds(cmdlist): + for cmd in cmdlist: + if not which(cmd): + return False + return True + + return skipUnless(lambda: has_cmds(args), + 'required command(s) "%s" not installed' % + ", ".join(args)) + +''' +Decorator to skip execution of a test if the current +host machine does not match one of the permitted +machines. +Example + + @skipIfNotMachine("x86_64", "aarch64") +''' +def skipIfNotMachine(*args): + return skipUnless(lambda: platform.machine() in args, + 'not running on one of the required machine(s) "%s"' % + ", ".join(args)) + +''' +Decorator to skip execution of flaky tests, unless +the $QEMU_TEST_FLAKY_TESTS environment variable is set. +A bug URL must be provided that documents the observed +failure behaviour, so it can be tracked & re-evaluated +in future. + +Historical tests may be providing "None" as the bug_url +but this should not be done for new test. + +Example: + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN") +''' +def skipFlakyTest(bug_url): + if bug_url is None: + bug_url = "FIXME: reproduce flaky test and file bug report or remove" + return skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), + f'Test is unstable: {bug_url}') + +''' +Decorator to skip execution of tests which are likely +to execute untrusted commands on the host, or commands +which process untrusted code, unless the +$QEMU_TEST_ALLOW_UNTRUSTED_CODE env var is set. +Example: + + @skipUntrustedTest() +''' +def skipUntrustedTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), + 'Test runs untrusted code / processes untrusted data') + +''' +Decorator to skip execution of tests which need large +data storage (over around 500MB-1GB mark) on the host, +unless the $QEMU_TEST_ALLOW_LARGE_STORAGE environment +variable is set + +Example: + + @skipBigDataTest() +''' +def skipBigDataTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), + 'Test requires large host storage space') + +''' +Decorator to skip execution of a test if the list +of python imports is not available. +Example: + + @skipIfMissingImports("numpy", "cv2") +''' +def skipIfMissingImports(*args): + def has_imports(importlist): + for impname in importlist: + try: + import impname + except ImportError: + return False + return True + + return skipUnless(lambda: has_imports(args), + 'required import(s) "%s" not installed' % + ", ".join(args)) From 3d5938607e05c4f8ac6df046a92fad19b681c23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:29 +0000 Subject: [PATCH 0410/2892] tests/functional: switch to new test skip decorators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures consistency of behaviour across all the tests, and requires that we provide gitlab bug links when marking a test to be skipped due to unreliability. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-9-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_acpi_bits.py | 25 +++-------------- tests/functional/test_arm_aspeed_ast2600.py | 7 +++-- tests/functional/test_arm_bflt.py | 7 +++-- tests/functional/test_arm_bpim2u.py | 5 ++-- tests/functional/test_arm_cubieboard.py | 5 ++-- tests/functional/test_arm_integratorcp.py | 25 +++++------------ tests/functional/test_arm_orangepi.py | 8 +++--- tests/functional/test_linux_initrd.py | 7 +++-- tests/functional/test_m68k_nextcube.py | 15 +++-------- tests/functional/test_mips64el_fuloong2e.py | 4 +-- tests/functional/test_mips64el_loongson3v.py | 8 +++--- tests/functional/test_mips64el_malta.py | 28 +++++++------------- tests/functional/test_ppc64_hv.py | 18 +++---------- tests/functional/test_ppc_40p.py | 7 ++--- tests/functional/test_rx_gdbsim.py | 6 ++--- tests/functional/test_sh4_r2d.py | 8 +++--- 16 files changed, 61 insertions(+), 122 deletions(-) diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 1e6d082ecb..8763ea0822 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -32,7 +32,6 @@ https://gitlab.com/qemu-project/biosbits-bits . """ import os -import platform import re import shutil import subprocess @@ -46,28 +45,13 @@ from typing import ( Sequence, ) from qemu.machine import QEMUMachine -from unittest import skipIf -from qemu_test import QemuSystemTest, Asset, which +from qemu_test import (QemuSystemTest, Asset, skipIfMissingCommands, + skipIfNotMachine) -deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. -supported_platforms = ['x86_64'] # supported test platforms. # default timeout of 120 secs is sometimes not enough for bits test. BITS_TIMEOUT = 200 -def missing_deps(): - """ returns True if any of the test dependent tools are absent. - """ - for dep in deps: - if which(dep) is None: - return True - return False - -def supported_platform(): - """ checks if the test is running on a supported platform. - """ - return platform.machine() in supported_platforms - class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods """ A QEMU VM, with isa-debugcon enabled and bits iso passed @@ -110,9 +94,8 @@ class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods """return the base argument to QEMU binary""" return self._base_args -@skipIf(not supported_platform() or missing_deps(), - 'unsupported platform or dependencies (%s) not installed' \ - % ','.join(deps)) +@skipIfMissingCommands("xorriso", "mformat") +@skipIfNotMachine("x86_64") class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attributes """ ACPI and SMBIOS tests using biosbits. diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py index 74d025e0fc..9c749f96b4 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -11,10 +11,9 @@ import subprocess from qemu_test import Asset from aspeed import AspeedTest -from qemu_test import exec_command_and_wait_for_pattern -from qemu_test import has_cmd +from qemu_test import exec_command_and_wait_for_pattern, skipIfMissingCommands from qemu_test.utils import archive_extract -from unittest import skipUnless + class AST2600Machine(AspeedTest): @@ -68,7 +67,7 @@ class AST2600Machine(AspeedTest): 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img'), 'a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') - @skipUnless(*has_cmd('swtpm')) + @skipIfMissingCommands('swtpm') def test_arm_ast2600_evb_buildroot_tpm(self): self.set_machine('ast2600-evb') diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py index 281925d11a..9095b08539 100755 --- a/tests/functional/test_arm_bflt.py +++ b/tests/functional/test_arm_bflt.py @@ -10,9 +10,8 @@ import os import bz2 from qemu_test import QemuUserTest, Asset -from qemu_test import has_cmd +from qemu_test import skipIfMissingCommands, skipUntrustedTest from qemu_test.utils import cpio_extract -from unittest import skipUnless class LoadBFLT(QemuUserTest): @@ -21,8 +20,8 @@ class LoadBFLT(QemuUserTest): ('https://elinux.org/images/5/51/Stm32_mini_rootfs.cpio.bz2'), 'eefb788e4980c9e8d6c9d60ce7d15d4da6bf4fbc6a80f487673824600d5ba9cc') - @skipUnless(*has_cmd('cpio')) - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipIfMissingCommands('cpio') + @skipUntrustedTest() def test_stm32(self): # See https://elinux.org/STM32#User_Space rootfs_path_bz2 = self.ASSET_ROOTFS.fetch() diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index c9cf43c147..fcd111f59d 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -9,9 +9,10 @@ import os from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern from qemu_test import Asset, interrupt_interactive_console_until_pattern +from qemu_test import skipBigDataTest from qemu_test.utils import gzip_uncompress, lzma_uncompress from qemu_test.utils import image_pow2ceil_expand -from unittest import skipUnless + class BananaPiMachine(LinuxKernelTest): @@ -143,7 +144,7 @@ class BananaPiMachine(LinuxKernelTest): os.remove(dtb_path) os.remove(rootfs_path) - @skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') + @skipBigDataTest() def test_arm_bpim2u_openwrt_22_03_3(self): self.set_machine('bpim2u') # This test download a 8.9 MiB compressed image and expand it diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index 9e42b72060..fdbd52a33c 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -8,8 +8,9 @@ import os from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test import skipBigDataTest from qemu_test.utils import gzip_uncompress, image_pow2ceil_expand -from unittest import skipUnless + class CubieboardMachine(LinuxKernelTest): @@ -105,7 +106,7 @@ class CubieboardMachine(LinuxKernelTest): # Wait for VM to shut down gracefully self.vm.wait() - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + @skipBigDataTest() def test_arm_cubieboard_openwrt_22_03_2(self): # This test download a 7.5 MiB compressed image and expand it # to 126 MiB. diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/test_arm_integratorcp.py index 0fe083f661..54fa366ad4 100755 --- a/tests/functional/test_arm_integratorcp.py +++ b/tests/functional/test_arm_integratorcp.py @@ -17,20 +17,7 @@ import logging from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from unittest import skipUnless - - -NUMPY_AVAILABLE = True -try: - import numpy as np -except ImportError: - NUMPY_AVAILABLE = False - -CV2_AVAILABLE = True -try: - import cv2 -except ImportError: - CV2_AVAILABLE = False +from qemu_test import skipIfMissingImports, skipUntrustedTest class IntegratorMachine(QemuSystemTest): @@ -63,7 +50,7 @@ class IntegratorMachine(QemuSystemTest): '-append', 'printk.time=0 console=ttyAMA0') self.vm.launch() - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_integratorcp_console(self): """ Boots the Linux kernel and checks that the console is operational @@ -71,13 +58,15 @@ class IntegratorMachine(QemuSystemTest): self.boot_integratorcp() wait_for_console_pattern(self, 'Log in as root') - @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') - @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipIfMissingImports("numpy", "cv2") + @skipUntrustedTest() def test_framebuffer_tux_logo(self): """ Boot Linux and verify the Tux logo is displayed on the framebuffer. """ + import numpy as np + import cv2 + screendump_path = os.path.join(self.workdir, "screendump.pbm") tuxlogo_path = self.ASSET_TUXLOGO.fetch() diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index a872305e93..b113adfe61 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -10,10 +10,10 @@ import shutil from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern from qemu_test import Asset, interrupt_interactive_console_until_pattern -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipBigDataTest from qemu_test.utils import gzip_uncompress, lzma_uncompress from qemu_test.utils import image_pow2ceil_expand -from unittest import skipUnless + class BananaPiMachine(LinuxKernelTest): @@ -149,7 +149,7 @@ class BananaPiMachine(LinuxKernelTest): os.remove(dtb_path) os.remove(rootfs_path) - @skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') + @skipBigDataTest() def test_arm_orangepi_armbian(self): self.set_machine('orangepi-pc') # This test download a 275 MiB compressed image and expand it @@ -185,7 +185,7 @@ class BananaPiMachine(LinuxKernelTest): 'to ') self.wait_for_console_pattern('Starting Load Kernel Modules...') - @skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') + @skipBigDataTest() def test_arm_orangepi_uboot_netbsd9(self): self.set_machine('orangepi-pc') # This test download a 304MB compressed image and expand it to 2GB diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/test_linux_initrd.py index c71a59d4c9..2207f83fbf 100755 --- a/tests/functional/test_linux_initrd.py +++ b/tests/functional/test_linux_initrd.py @@ -10,12 +10,10 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os import logging import tempfile -from qemu_test import QemuSystemTest, Asset -from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset, skipFlakyTest class LinuxInitrd(QemuSystemTest): @@ -60,7 +58,8 @@ class LinuxInitrd(QemuSystemTest): max_size + 1) self.assertRegex(self.vm.get_log(), expected_msg) - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + # XXX file tracking bug + @skipFlakyTest(bug_url=None) def test_with_2gib_file_should_work_with_linux_v4_16(self): """ QEMU has supported up to 4 GiB initrd for recent kernel diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py index e6e8d4fd3f..25a17d4794 100755 --- a/tests/functional/test_m68k_nextcube.py +++ b/tests/functional/test_m68k_nextcube.py @@ -11,17 +11,9 @@ import os import time from qemu_test import QemuSystemTest, Asset -from unittest import skipUnless - -from qemu_test import has_cmd +from qemu_test import skipIfMissingImports, skipIfMissingCommands from qemu_test.tesseract import tesseract_ocr -PIL_AVAILABLE = True -try: - from PIL import Image -except ImportError: - PIL_AVAILABLE = False - class NextCubeMachine(QemuSystemTest): @@ -44,17 +36,18 @@ class NextCubeMachine(QemuSystemTest): self.vm.cmd('human-monitor-command', command_line='screendump %s' % screenshot_path) - @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') + @skipIfMissingImports("PIL") def test_bootrom_framebuffer_size(self): self.set_machine('next-cube') screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) + from PIL import Image width, height = Image.open(screenshot_path).size self.assertEqual(width, 1120) self.assertEqual(height, 832) - @skipUnless(*has_cmd('tesseract')) + @skipIfMissingCommands('tesseract') def test_bootrom_framebuffer_ocr_with_tesseract(self): self.set_machine('next-cube') screenshot_path = os.path.join(self.workdir, "dump.ppm") diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/test_mips64el_fuloong2e.py index a32d5f9d08..531d16d8f6 100755 --- a/tests/functional/test_mips64el_fuloong2e.py +++ b/tests/functional/test_mips64el_fuloong2e.py @@ -13,7 +13,7 @@ import os import subprocess from qemu_test import LinuxKernelTest, Asset -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipUntrustedTest from unittest import skipUnless class MipsFuloong2e(LinuxKernelTest): @@ -39,7 +39,7 @@ class MipsFuloong2e(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() @skipUnless(os.getenv('RESCUE_YL_PATH'), 'RESCUE_YL_PATH not available') def test_linux_kernel_2_6_27_isa_serial(self): # Recovery system for the Yeeloong laptop diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/test_mips64el_loongson3v.py index e57ec5499e..f85371e50c 100755 --- a/tests/functional/test_mips64el_loongson3v.py +++ b/tests/functional/test_mips64el_loongson3v.py @@ -9,11 +9,9 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - -from unittest import skipUnless from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipUntrustedTest + class MipsLoongson3v(QemuSystemTest): timeout = 60 @@ -23,7 +21,7 @@ class MipsLoongson3v(QemuSystemTest): 'releases/download/20210112/pmon-3avirt.bin'), 'fcdf6bb2cb7885a4a62f31fcb0d5e368bac7b6cea28f40c6dfa678af22fea20a') - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_pmon_serial_console(self): self.set_machine('loongson3-virt') diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py index 6d1195d362..52283e2dbd 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/test_mips64el_malta.py @@ -14,20 +14,8 @@ import logging from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest from qemu_test.utils import gzip_uncompress -from unittest import skipUnless - -NUMPY_AVAILABLE = True -try: - import numpy as np -except ImportError: - NUMPY_AVAILABLE = False - -CV2_AVAILABLE = True -try: - import cv2 -except ImportError: - CV2_AVAILABLE = False class MaltaMachineConsole(LinuxKernelTest): @@ -76,7 +64,7 @@ class MaltaMachineConsole(LinuxKernelTest): 'rootfs.mipsel64r1.cpio.gz'), '75ba10cd35fb44e32948eeb26974f061b703c81c4ba2fab1ebcacf1d1bec3b61') - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_mips64el_malta_5KEc_cpio(self): kernel_path = self.ASSET_KERNEL_3_19_3.fetch() initrd_path_gz = self.ASSET_CPIO_R1.fetch() @@ -106,8 +94,7 @@ class MaltaMachineConsole(LinuxKernelTest): self.vm.wait() -@skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') -@skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') +@skipIfMissingImports('numpy', 'cv2') class MaltaMachineFramebuffer(LinuxKernelTest): timeout = 30 @@ -126,6 +113,10 @@ class MaltaMachineFramebuffer(LinuxKernelTest): """ Boot Linux kernel and check Tux logo is displayed on the framebuffer. """ + + import numpy as np + import cv2 + screendump_path = os.path.join(self.workdir, 'screendump.pbm') kernel_path_gz = self.ASSET_KERNEL_4_7_0.fetch() @@ -171,11 +162,12 @@ class MaltaMachineFramebuffer(LinuxKernelTest): def test_mips_malta_i6400_framebuffer_logo_1core(self): self.do_test_i6400_framebuffer_logo(1) - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + # XXX file tracking bug + @skipFlakyTest(bug_url=None) def test_mips_malta_i6400_framebuffer_logo_7cores(self): self.do_test_i6400_framebuffer_logo(7) - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + @skipFlakyTest(bug_url=None) def test_mips_malta_i6400_framebuffer_logo_8cores(self): self.do_test_i6400_framebuffer_logo(8) diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 7c6f8234f5..88f0f99f24 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -9,24 +9,14 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from unittest import skipIf, skipUnless from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern, exec_command, which +from qemu_test import wait_for_console_pattern, exec_command +from qemu_test import skipIfMissingCommands, skipBigDataTest import os import time import subprocess from datetime import datetime -deps = ["xorriso"] # dependent tools needed in the test setup/box. - -def missing_deps(): - """ returns True if any of the test dependent tools are absent. - """ - for dep in deps: - if which(dep) is None: - return True - return False - # Alpine is a light weight distro that supports QEMU. These tests boot # that on the machine then run a QEMU guest inside it in KVM mode, # that runs the same Alpine distro image. @@ -34,8 +24,8 @@ def missing_deps(): # large download, but it may be more polite to create qcow2 image with # QEMU already installed and use that. # XXX: The order of these tests seems to matter, see git blame. -@skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps)) -@skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') +@skipIfMissingCommands("xorriso") +@skipBigDataTest() class HypervisorTest(QemuSystemTest): timeout = 1000 diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/test_ppc_40p.py index 67bcdae53a..7a74e0cca7 100755 --- a/tests/functional/test_ppc_40p.py +++ b/tests/functional/test_ppc_40p.py @@ -7,11 +7,8 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os - -from unittest import skipUnless from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipUntrustedTest class IbmPrep40pMachine(QemuSystemTest): @@ -37,7 +34,7 @@ class IbmPrep40pMachine(QemuSystemTest): # All rights reserved. # U.S. Government Users Restricted Rights - Use, duplication or disclosure # restricted by GSA ADP Schedule Contract with IBM Corp. - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_factory_firmware_and_netbsd(self): self.set_machine('40p') self.require_accelerator("tcg") diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py index 5687f756bb..f7621d999d 100755 --- a/tests/functional/test_rx_gdbsim.py +++ b/tests/functional/test_rx_gdbsim.py @@ -12,10 +12,9 @@ import os -from unittest import skipUnless from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipFlakyTest from qemu_test.utils import gzip_uncompress @@ -52,9 +51,10 @@ class RxGdbSimMachine(QemuSystemTest): wait_for_console_pattern(self, uboot_version) gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)' # FIXME limit baudrate on chardev, else we type too fast + # https://gitlab.com/qemu-project/qemu/-/issues/2691 #exec_command_and_wait_for_pattern(self, 'version', gcc_version) - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + @skipFlakyTest(bug_url="https://gitlab.com/qemu-project/qemu/-/issues/2691") def test_linux_sash(self): """ Boots a Linux kernel and checks that the console is operational. diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/test_sh4_r2d.py index c3cfff79ad..e2fcde2d6b 100755 --- a/tests/functional/test_sh4_r2d.py +++ b/tests/functional/test_sh4_r2d.py @@ -4,11 +4,8 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - -from qemu_test import LinuxKernelTest, Asset +from qemu_test import LinuxKernelTest, Asset, skipFlakyTest from qemu_test.utils import archive_extract -from unittest import skipUnless class R2dTest(LinuxKernelTest): @@ -18,7 +15,8 @@ class R2dTest(LinuxKernelTest): # This test has a 6-10% failure rate on various hosts that look # like issues with a buggy kernel. - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable') + # XXX file tracking bug + @skipFlakyTest(bug_url=None) def test_r2d(self): self.set_machine('r2d') file_path = self.ASSET_DAY09.fetch() From 9132fff802431438a2805389e74402321fb9afed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:30 +0000 Subject: [PATCH 0411/2892] tests/functional: drop 'has_cmd' and 'has_cmds' helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'which' helper is simpler, not depending on the external 'which' binary, and is sufficient for test needs. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-10-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/cmd.py | 54 ++---------------------- tests/functional/qemu_test/tuxruntest.py | 10 ++--- 3 files changed, 9 insertions(+), 57 deletions(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 7dee3522f2..fe6cbe3a8a 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -8,7 +8,7 @@ from .asset import Asset from .config import BUILD_DIR -from .cmd import has_cmd, has_cmds, run_cmd, is_readable_executable_file, \ +from .cmd import run_cmd, is_readable_executable_file, \ interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index 600e0509db..bebcd46dcf 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -29,52 +29,6 @@ def which(tool): return p return None -def has_cmd(name, args=None): - """ - This function is for use in a @skipUnless decorator, e.g.: - - @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) - def test_something_that_needs_sudo(self): - ... - """ - - if args is None: - args = ('which', name) - - try: - _, stderr, exitcode = run_cmd(args) - except Exception as e: - exitcode = -1 - stderr = str(e) - - if exitcode != 0: - cmd_line = ' '.join(args) - err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' - return (False, err) - else: - return (True, '') - -def has_cmds(*cmds): - """ - This function is for use in a @skipUnless decorator and - allows checking for the availability of multiple commands, e.g.: - - @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), - 'cmd2', 'cmd3')) - def test_something_that_needs_cmd1_and_cmd2(self): - ... - """ - - for cmd in cmds: - if isinstance(cmd, str): - cmd = (cmd,) - - ok, errstr = has_cmd(*cmd) - if not ok: - return (False, errstr) - - return (True, '') - def run_cmd(args): subp = subprocess.Popen(args, stdout=subprocess.PIPE, @@ -254,7 +208,7 @@ def get_qemu_img(test): qemu_img = os.path.join(BUILD_DIR, 'qemu-img') if os.path.exists(qemu_img): return qemu_img - (has_system_qemu_img, errmsg) = has_cmd('qemu-img') - if has_system_qemu_img: - return 'qemu-img' - test.skipTest(errmsg) + qemu_img = which('qemu-img') + if qemu_img is not None: + return qemu_img + test.skipTest(f"qemu-img not found in {BUILD_DIR} or '$PATH'") diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index d375f2713b..2e5c6d110c 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -15,7 +15,7 @@ import stat from qemu_test import QemuSystemTest from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test import has_cmd, run_cmd, get_qemu_img +from qemu_test import which, run_cmd, get_qemu_img class TuxRunBaselineTest(QemuSystemTest): @@ -38,10 +38,8 @@ class TuxRunBaselineTest(QemuSystemTest): super().setUp() # We need zstd for all the tuxrun tests - (has_zstd, msg) = has_cmd('zstd') - if has_zstd is False: - self.skipTest(msg) - self.zstd = 'zstd' + if which('zstd') is None: + self.skipTest("zstd not found in $PATH") # Pre-init TuxRun specific settings: Most machines work with # reasonable defaults but we sometimes need to tweak the @@ -78,7 +76,7 @@ class TuxRunBaselineTest(QemuSystemTest): disk_image = self.workdir + "/rootfs.ext4" - run_cmd([self.zstd, "-f", "-d", disk_image_zst, + run_cmd(['zstd', "-f", "-d", disk_image_zst, "-o", disk_image]) # zstd copies source archive permissions for the output # file, so must make this writable for QEMU From f84f8e71eb4bdd193e89c6550a015277b686b0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:31 +0000 Subject: [PATCH 0412/2892] tests/functional: add helpers for building file paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper methods that construct paths for * log files - to be preserved at the end of a test * scratch files - to be purged at the end of a test * build files - anything relative to the build root * data files - anything relative to the functional test source root * socket files - a short temporary dir to avoid UNIX socket limits These are to be used instead of direct access to the self.workdir, or self.logdir variables, or any other place where paths are built manually. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-11-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 95 ++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 90ae59eb54..89425b737c 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -13,10 +13,12 @@ import logging import os +from pathlib import Path import pycotap import shutil import subprocess import sys +import tempfile import unittest import uuid @@ -37,9 +39,99 @@ class QemuBaseTest(unittest.TestCase): log = None logdir = None + ''' + Create a temporary directory suitable for storing UNIX + socket paths. + + Returns: a tempfile.TemporaryDirectory instance + ''' + def socket_dir(self): + if self.socketdir is None: + self.socketdir = tempfile.TemporaryDirectory( + prefix="qemu_func_test_sock_") + return self.socketdir + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing a data file located + relative to the source directory that is the root for + functional tests. + + @args may be an empty list to reference the root dir + itself, may be a single element to reference a file in + the root directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def data_file(self, *args): + return str(Path(Path(__file__).parent.parent, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing a data file located + relative to the build directory root. + + @args may be an empty list to reference the build dir + itself, may be a single element to reference a file in + the build directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def build_file(self, *args): + return str(Path(BUILD_DIR, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing/creating a scratch file + located relative to a temporary directory dedicated to + this test case. The directory and its contents will be + purged upon completion of the test. + + @args may be an empty list to reference the scratch dir + itself, may be a single element to reference a file in + the scratch directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def scratch_file(self, *args): + return str(Path(self.workdir, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing/creating a log file + located relative to a temporary directory dedicated to + this test case. The directory and its log files will be + preserved upon completion of the test. + + @args may be an empty list to reference the log dir + itself, may be a single element to reference a file in + the log directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def log_file(self, *args): + return str(Path(self.logdir, *args)) + def setUp(self, bin_prefix): self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') self.arch = self.qemu_bin.split('-')[-1] + self.socketdir = None self.outputdir = os.path.join(BUILD_DIR, 'tests', 'functional', self.arch, self.id()) @@ -65,6 +157,9 @@ class QemuBaseTest(unittest.TestCase): def tearDown(self): if "QEMU_TEST_KEEP_SCRATCH" not in os.environ: shutil.rmtree(self.workdir) + if self.socketdir is not None: + shutil.rmtree(self.socketdir.name) + self.socketdir = None self.machinelog.removeHandler(self._log_fh) self.log.removeHandler(self._log_fh) From bcc12768c242a4862a1725e22371dd87af1c2b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:32 +0000 Subject: [PATCH 0413/2892] tests/functional: switch over to using self.log_file(...) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes direct access of the 'self.logdir' variable. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-12-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 9 ++++----- tests/functional/test_virtio_gpu.py | 4 +--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 89425b737c..2174fbb155 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -126,7 +126,7 @@ class QemuBaseTest(unittest.TestCase): Returns: string representing a file path ''' def log_file(self, *args): - return str(Path(self.logdir, *args)) + return str(Path(self.outputdir, *args)) def setUp(self, bin_prefix): self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') @@ -138,8 +138,7 @@ class QemuBaseTest(unittest.TestCase): self.workdir = os.path.join(self.outputdir, 'scratch') os.makedirs(self.workdir, exist_ok=True) - self.logdir = self.outputdir - self.log_filename = os.path.join(self.logdir, 'base.log') + self.log_filename = self.log_file('base.log') self.log = logging.getLogger('qemu-test') self.log.setLevel(logging.DEBUG) self._log_fh = logging.FileHandler(self.log_filename, mode='w') @@ -215,7 +214,7 @@ class QemuSystemTest(QemuBaseTest): console_log = logging.getLogger('console') console_log.setLevel(logging.DEBUG) - self.console_log_name = os.path.join(self.logdir, 'console.log') + self.console_log_name = self.log_file('console.log') self._console_log_fh = logging.FileHandler(self.console_log_name, mode='w') self._console_log_fh.setLevel(logging.DEBUG) @@ -269,7 +268,7 @@ class QemuSystemTest(QemuBaseTest): vm = QEMUMachine(self.qemu_bin, name=name, base_temp_dir=self.workdir, - log_dir=self.logdir) + log_dir=self.log_file()) self.log.debug('QEMUMachine "%s" created', name) self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py index 2d298b1f02..7654421e6b 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -100,9 +100,7 @@ class VirtioGPUx86(QemuSystemTest): os.set_inheritable(qemu_sock.fileno(), True) os.set_inheritable(vug_sock.fileno(), True) - self._vug_log_path = os.path.join( - self.logdir, "vhost-user-gpu.log" - ) + self._vug_log_path = self.log_file("vhost-user-gpu.log") self._vug_log_file = open(self._vug_log_path, "wb") self.log.info('Complete vhost-user-gpu.log file can be ' 'found at %s', self._vug_log_path) From 8b5a0dd3a8a4526bb91430b7f548c95d46093dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:33 +0000 Subject: [PATCH 0414/2892] tests/functional: switch over to using self.build_file(...) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes direct access of the 'BUILD_DIR' variable. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-13-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 5 ++--- tests/functional/qemu_test/testcase.py | 4 ++-- tests/functional/test_aarch64_virt.py | 5 ++--- tests/functional/test_virtio_gpu.py | 11 +++-------- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index bebcd46dcf..c8971de00a 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -16,7 +16,6 @@ import os import os.path import subprocess -from .config import BUILD_DIR def which(tool): """ looks up the full path for @tool, returns None if not found @@ -205,10 +204,10 @@ def get_qemu_img(test): # If qemu-img has been built, use it, otherwise the system wide one # will be used. - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + qemu_img = test.build_file('qemu-img') if os.path.exists(qemu_img): return qemu_img qemu_img = which('qemu-img') if qemu_img is not None: return qemu_img - test.skipTest(f"qemu-img not found in {BUILD_DIR} or '$PATH'") + test.skipTest(f"qemu-img not found in build dir or '$PATH'") diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 2174fbb155..493938240c 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -133,8 +133,8 @@ class QemuBaseTest(unittest.TestCase): self.arch = self.qemu_bin.split('-')[-1] self.socketdir = None - self.outputdir = os.path.join(BUILD_DIR, 'tests', 'functional', - self.arch, self.id()) + self.outputdir = self.build_file('tests', 'functional', + self.arch, self.id()) self.workdir = os.path.join(self.outputdir, 'scratch') os.makedirs(self.workdir, exist_ok=True) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index c967da41b4..5bc461b482 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -14,7 +14,6 @@ import time import os import logging -from qemu_test import BUILD_DIR from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern from qemu_test import get_qemu_img, run_cmd @@ -54,8 +53,8 @@ class Aarch64VirtMachine(QemuSystemTest): "mte=on," "gic-version=max,iommu=smmuv3") self.vm.add_args("-smp", "2", "-m", "1024") - self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) + self.vm.add_args('-bios', self.build_file('pc-bios', + 'edk2-aarch64-code.fd')) self.vm.add_args("-drive", f"file={iso_path},media=cdrom,format=raw") self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py index 7654421e6b..81c9156d63 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -6,7 +6,6 @@ # later. See the COPYING file in the top-level directory. -from qemu_test import BUILD_DIR from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern @@ -18,12 +17,8 @@ import socket import subprocess -def pick_default_vug_bin(): - relative_path = "./contrib/vhost-user-gpu/vhost-user-gpu" - if is_readable_executable_file(relative_path): - return relative_path - - bld_dir_path = os.path.join(BUILD_DIR, relative_path) +def pick_default_vug_bin(test): + bld_dir_path = test.build_file("contrib", "vhost-user-gpu", "vhost-user-gpu") if is_readable_executable_file(bld_dir_path): return bld_dir_path @@ -86,7 +81,7 @@ class VirtioGPUx86(QemuSystemTest): # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc self.require_accelerator('kvm') - vug = pick_default_vug_bin() + vug = pick_default_vug_bin(self) if not vug: self.skipTest("Could not find vhost-user-gpu") From bd96e460d3a1fb882e01b385d695070659078ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:34 +0000 Subject: [PATCH 0415/2892] tests/functional: switch over to using self.data_file(...) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes direct path manipulation to figure out the source dir Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-14-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_acpi_bits.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 8763ea0822..59d0383563 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -38,7 +38,6 @@ import subprocess import tarfile import zipfile -from pathlib import Path from typing import ( List, Optional, @@ -119,7 +118,6 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._vm = None - self._baseDir = None self._debugcon_addr = '0x403' self._debugcon_log = 'debugcon-log.txt' @@ -134,26 +132,22 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute def copy_bits_config(self): """ copies the bios bits config file into bits. """ - config_file = 'bits-cfg.txt' - bits_config_dir = os.path.join(self._baseDir, 'acpi-bits', - 'bits-config') + bits_config_file = self.data_file('acpi-bits', + 'bits-config', + 'bits-cfg.txt') target_config_dir = os.path.join(self.workdir, 'bits-%d' %self.BITS_INTERNAL_VER, 'boot') - self.assertTrue(os.path.exists(bits_config_dir)) + self.assertTrue(os.path.exists(bits_config_file)) self.assertTrue(os.path.exists(target_config_dir)) - self.assertTrue(os.access(os.path.join(bits_config_dir, - config_file), os.R_OK)) - shutil.copy2(os.path.join(bits_config_dir, config_file), - target_config_dir) + shutil.copy2(bits_config_file, target_config_dir) self.logger.info('copied config file %s to %s', - config_file, target_config_dir) + bits_config_file, target_config_dir) def copy_test_scripts(self): """copies the python test scripts into bits. """ - bits_test_dir = os.path.join(self._baseDir, 'acpi-bits', - 'bits-tests') + bits_test_dir = self.data_file('acpi-bits', 'bits-tests') target_test_dir = os.path.join(self.workdir, 'bits-%d' %self.BITS_INTERNAL_VER, 'boot', 'python') @@ -256,8 +250,6 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute super().setUp() self.logger = self.log - self._baseDir = Path(__file__).parent - prebuiltDir = os.path.join(self.workdir, 'prebuilt') if not os.path.isdir(prebuiltDir): os.mkdir(prebuiltDir, mode=0o775) From beaf88c895a5eda649777757c80ab4171de777ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:35 +0000 Subject: [PATCH 0416/2892] tests/functional: switch over to using self.scratch_file() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace any instances of os.path.join(self.workdir, ".../...") self.workdir + "/.../..." with self.scratch_file("...", "...") which is more compact and portable Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-15-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/linuxkernel.py | 7 ++- tests/functional/qemu_test/tuxruntest.py | 2 +- tests/functional/test_aarch64_aspeed.py | 17 ++++--- tests/functional/test_aarch64_raspi3.py | 3 +- tests/functional/test_aarch64_raspi4.py | 4 +- tests/functional/test_aarch64_sbsaref.py | 6 +-- tests/functional/test_aarch64_virt.py | 3 +- tests/functional/test_acpi_bits.py | 49 +++++++++---------- tests/functional/test_alpha_clipper.py | 4 +- tests/functional/test_arm_aspeed_ast1030.py | 6 +-- tests/functional/test_arm_aspeed_ast2500.py | 2 +- tests/functional/test_arm_aspeed_ast2600.py | 2 +- tests/functional/test_arm_bflt.py | 3 +- tests/functional/test_arm_bpim2u.py | 6 +-- tests/functional/test_arm_canona1100.py | 3 +- tests/functional/test_arm_cubieboard.py | 6 +-- tests/functional/test_arm_emcraft_sf2.py | 2 +- tests/functional/test_arm_integratorcp.py | 3 +- tests/functional/test_arm_orangepi.py | 8 +-- tests/functional/test_arm_raspi2.py | 4 +- tests/functional/test_arm_smdkc210.py | 2 +- tests/functional/test_arm_vexpress.py | 5 +- tests/functional/test_m68k_mcf5208evb.py | 3 +- tests/functional/test_m68k_nextcube.py | 5 +- .../functional/test_microblaze_s3adsp1800.py | 3 +- .../test_microblazeel_s3adsp1800.py | 5 +- tests/functional/test_mips64el_malta.py | 6 +-- tests/functional/test_mips_malta.py | 4 +- tests/functional/test_mipsel_malta.py | 6 +-- tests/functional/test_or1k_sim.py | 2 +- tests/functional/test_ppc64_e500.py | 2 +- tests/functional/test_ppc64_hv.py | 12 +---- tests/functional/test_ppc_amiga.py | 12 +++-- tests/functional/test_ppc_bamboo.py | 10 ++-- tests/functional/test_ppc_mac.py | 2 +- tests/functional/test_ppc_mpc8544ds.py | 2 +- tests/functional/test_ppc_virtex_ml507.py | 5 +- tests/functional/test_rx_gdbsim.py | 4 +- tests/functional/test_s390x_ccw_virtio.py | 2 +- tests/functional/test_s390x_topology.py | 4 +- tests/functional/test_sh4_r2d.py | 3 +- tests/functional/test_sh4eb_r2d.py | 8 +-- tests/functional/test_sparc64_sun4u.py | 5 +- tests/functional/test_sparc_sun4m.py | 2 +- tests/functional/test_xtensa_lx60.py | 3 +- 45 files changed, 118 insertions(+), 139 deletions(-) diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py index 2b5b9a5fda..2e4f4e35fd 100644 --- a/tests/functional/qemu_test/linuxkernel.py +++ b/tests/functional/qemu_test/linuxkernel.py @@ -46,8 +46,7 @@ class LinuxKernelTest(QemuSystemTest): os.chdir(cwd) # Return complete path to extracted file. Because callers to # extract_from_deb() specify 'path' with a leading slash, it is - # necessary to use os.path.relpath() as otherwise os.path.join() - # interprets it as an absolute path and drops the self.workdir part. - return os.path.normpath(os.path.join(self.workdir, - os.path.relpath(path, '/'))) + # necessary to use os.path.relpath() as otherwise scratch_file() + # interprets it as an absolute path and drops the required prefix + return os.path.normpath(self.scratch_file(os.path.relpath(path, '/'))) diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index 2e5c6d110c..0b1bb8f0ed 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -74,7 +74,7 @@ class TuxRunBaselineTest(QemuSystemTest): kernel_image = kernel_asset.fetch() disk_image_zst = rootfs_asset.fetch() - disk_image = self.workdir + "/rootfs.ext4" + disk_image = self.scratch_file("rootfs.ext4") run_cmd(['zstd', "-f", "-d", disk_image_zst, "-o", disk_image]) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index e196f88537..8ba2c67248 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -38,26 +38,28 @@ class AST2x00MachineSDK(QemuSystemTest): archive_extract(image_path, self.workdir) num_cpu = 4 - image_dir = self.workdir + '/ast2700-default/' - uboot_size = os.path.getsize(image_dir + 'u-boot-nodtb.bin') + uboot_size = os.path.getsize(self.scratch_file('ast2700-default', + 'u-boot-nodtb.bin')) uboot_dtb_load_addr = hex(0x400000000 + uboot_size) load_images_list = [ { 'addr': '0x400000000', - 'file': image_dir + 'u-boot-nodtb.bin' + 'file': self.scratch_file('ast2700-default', + 'u-boot-nodtb.bin') }, { 'addr': str(uboot_dtb_load_addr), - 'file': image_dir + 'u-boot.dtb' + 'file': self.scratch_file('ast2700-default', 'u-boot.dtb') }, { 'addr': '0x430000000', - 'file': image_dir + 'bl31.bin' + 'file': self.scratch_file('ast2700-default', 'bl31.bin') }, { 'addr': '0x430080000', - 'file': image_dir + 'optee/tee-raw.bin' + 'file': self.scratch_file('ast2700-default', 'optee', + 'tee-raw.bin') } ] @@ -74,7 +76,8 @@ class AST2x00MachineSDK(QemuSystemTest): self.vm.add_args('-smp', str(num_cpu)) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') - self.do_test_aarch64_aspeed_sdk_start(image_dir + 'image-bmc') + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file('ast2700-default', 'image-bmc')) wait_for_console_pattern(self, 'ast2700-default login:') diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/test_aarch64_raspi3.py index 369f95a3d9..98ed6f9d56 100755 --- a/tests/functional/test_aarch64_raspi3.py +++ b/tests/functional/test_aarch64_raspi3.py @@ -7,7 +7,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os from zipfile import ZipFile from qemu_test import LinuxKernelTest, Asset @@ -26,7 +25,7 @@ class Aarch64Raspi3Machine(LinuxKernelTest): with ZipFile(zip_path, 'r') as zf: zf.extract(efi_name, path=self.workdir) - efi_fd = os.path.join(self.workdir, efi_name) + efi_fd = self.scratch_file(efi_name) self.set_machine('raspi3b') self.vm.set_console(console_index=1) diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/test_aarch64_raspi4.py index e5c9f77479..2cda03f86f 100755 --- a/tests/functional/test_aarch64_raspi4.py +++ b/tests/functional/test_aarch64_raspi4.py @@ -5,8 +5,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test.utils import gzip_uncompress @@ -64,7 +62,7 @@ class Aarch64Raspi4Machine(LinuxKernelTest): kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.set_machine('raspi4b') diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index 6db08da522..533ca64407 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -8,8 +8,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import interrupt_interactive_console_until_pattern @@ -32,12 +30,12 @@ def fetch_firmware(test): # Secure BootRom (TF-A code) fs0_xz_path = Aarch64SbsarefMachine.ASSET_FLASH0.fetch() - fs0_path = os.path.join(test.workdir, "SBSA_FLASH0.fd") + fs0_path = test.scratch_file("SBSA_FLASH0.fd") lzma_uncompress(fs0_xz_path, fs0_path) # Non-secure rom (UEFI and EFI variables) fs1_xz_path = Aarch64SbsarefMachine.ASSET_FLASH1.fetch() - fs1_path = os.path.join(test.workdir, "SBSA_FLASH1.fd") + fs1_path = test.scratch_file("SBSA_FLASH1.fd") lzma_uncompress(fs1_xz_path, fs1_path) for path in [fs0_path, fs1_path]: diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 5bc461b482..cc49f8963d 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -11,7 +11,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later import time -import os import logging from qemu_test import QemuSystemTest, Asset @@ -95,7 +94,7 @@ class Aarch64VirtMachine(QemuSystemTest): # Also add a scratch block device logger.info('creating scratch qcow2 image') - image_path = os.path.join(self.workdir, 'scratch.qcow2') + image_path = self.scratch_file('scratch.qcow2') qemu_img = get_qemu_img(self) run_cmd([qemu_img, 'create', '-f', 'qcow2', image_path, '8M']) diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 59d0383563..3b99ecf3a4 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -135,9 +135,9 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute bits_config_file = self.data_file('acpi-bits', 'bits-config', 'bits-cfg.txt') - target_config_dir = os.path.join(self.workdir, - 'bits-%d' %self.BITS_INTERNAL_VER, - 'boot') + target_config_dir = self.scratch_file('bits-%d' % + self.BITS_INTERNAL_VER, + 'boot') self.assertTrue(os.path.exists(bits_config_file)) self.assertTrue(os.path.exists(target_config_dir)) shutil.copy2(bits_config_file, target_config_dir) @@ -148,9 +148,8 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute """copies the python test scripts into bits. """ bits_test_dir = self.data_file('acpi-bits', 'bits-tests') - target_test_dir = os.path.join(self.workdir, - 'bits-%d' %self.BITS_INTERNAL_VER, - 'boot', 'python') + target_test_dir = self.scratch_file('bits-%d' % self.BITS_INTERNAL_VER, + 'boot', 'python') self.assertTrue(os.path.exists(bits_test_dir)) self.assertTrue(os.path.exists(target_test_dir)) @@ -187,8 +186,8 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute the directory where we have extracted our pre-built bits grub tarball. """ - grub_x86_64_mods = os.path.join(self.workdir, 'grub-inst-x86_64-efi') - grub_i386_mods = os.path.join(self.workdir, 'grub-inst') + grub_x86_64_mods = self.scratch_file('grub-inst-x86_64-efi') + grub_i386_mods = self.scratch_file('grub-inst') self.assertTrue(os.path.exists(grub_x86_64_mods)) self.assertTrue(os.path.exists(grub_i386_mods)) @@ -209,13 +208,11 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute """ Uses grub-mkrescue to generate a fresh bits iso with the python test scripts """ - bits_dir = os.path.join(self.workdir, - 'bits-%d' %self.BITS_INTERNAL_VER) - iso_file = os.path.join(self.workdir, - 'bits-%d.iso' %self.BITS_INTERNAL_VER) - mkrescue_script = os.path.join(self.workdir, - 'grub-inst-x86_64-efi', 'bin', - 'grub-mkrescue') + bits_dir = self.scratch_file('bits-%d' % self.BITS_INTERNAL_VER) + iso_file = self.scratch_file('bits-%d.iso' % self.BITS_INTERNAL_VER) + mkrescue_script = self.scratch_file('grub-inst-x86_64-efi', + 'bin', + 'grub-mkrescue') self.assertTrue(os.access(mkrescue_script, os.R_OK | os.W_OK | os.X_OK)) @@ -250,17 +247,18 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute super().setUp() self.logger = self.log - prebuiltDir = os.path.join(self.workdir, 'prebuilt') + prebuiltDir = self.scratch_file('prebuilt') if not os.path.isdir(prebuiltDir): os.mkdir(prebuiltDir, mode=0o775) - bits_zip_file = os.path.join(prebuiltDir, 'bits-%d-%s.zip' - %(self.BITS_INTERNAL_VER, - self.BITS_COMMIT_HASH)) - grub_tar_file = os.path.join(prebuiltDir, - 'bits-%d-%s-grub.tar.gz' - %(self.BITS_INTERNAL_VER, - self.BITS_COMMIT_HASH)) + bits_zip_file = self.scratch_file('prebuilt', + 'bits-%d-%s.zip' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) + grub_tar_file = self.scratch_file('prebuilt', + 'bits-%d-%s-grub.tar.gz' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) bitsLocalArtLoc = self.ASSET_BITS.fetch() self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc) @@ -284,7 +282,7 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute """parse the log generated by running bits tests and check for failures. """ - debugconf = os.path.join(self.workdir, self._debugcon_log) + debugconf = self.scratch_file(self._debugcon_log) log = "" with open(debugconf, 'r', encoding='utf-8') as filehandle: log = filehandle.read() @@ -316,8 +314,7 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute """The main test case implementation.""" self.set_machine('pc') - iso_file = os.path.join(self.workdir, - 'bits-%d.iso' %self.BITS_INTERNAL_VER) + iso_file = self.scratch_file('bits-%d.iso' % self.BITS_INTERNAL_VER) self.assertTrue(os.access(iso_file, os.R_OK)) diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/test_alpha_clipper.py index c1fbf0e395..72cd7b57e6 100755 --- a/tests/functional/test_alpha_clipper.py +++ b/tests/functional/test_alpha_clipper.py @@ -5,8 +5,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test.utils import gzip_uncompress @@ -22,7 +20,7 @@ class AlphaClipperTest(LinuxKernelTest): self.set_machine('clipper') kernel_path = self.ASSET_KERNEL.fetch() - uncompressed_kernel = os.path.join(self.workdir, 'vmlinux') + uncompressed_kernel = self.scratch_file('vmlinux') gzip_uncompress(kernel_path, uncompressed_kernel) self.vm.set_console() diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py index 380a76ec01..01b13c5591 100755 --- a/tests/functional/test_arm_aspeed_ast1030.py +++ b/tests/functional/test_arm_aspeed_ast1030.py @@ -6,8 +6,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from zipfile import ZipFile @@ -27,7 +25,7 @@ class AST1030Machine(LinuxKernelTest): kernel_name = "ast1030-evb-demo/zephyr.elf" with ZipFile(zip_file, 'r') as zf: zf.extract(kernel_name, path=self.workdir) - kernel_file = os.path.join(self.workdir, kernel_name) + kernel_file = self.scratch_file(kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -49,7 +47,7 @@ class AST1030Machine(LinuxKernelTest): kernel_name = "ast1030-evb-demo/zephyr.bin" with ZipFile(zip_file, 'r') as zf: zf.extract(kernel_name, path=self.workdir) - kernel_file = os.path.join(self.workdir, kernel_name) + kernel_file = self.scratch_file(kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py index 79baf37537..8c5593cdc8 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -50,7 +50,7 @@ class AST2500Machine(AspeedTest): archive_extract(image_path, self.workdir) self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2500-default/image-bmc') + self.scratch_file("ast2500-default", "image-bmc")) self.wait_for_console_pattern('ast2500-default login:') diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py index 9c749f96b4..25948c1c34 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -114,7 +114,7 @@ class AST2600Machine(AspeedTest): self.vm.add_args('-device', 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2600-a2/image-bmc') + self.scratch_file("ast2600-a2", "image-bmc")) self.wait_for_console_pattern('ast2600-a2 login:') diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py index 9095b08539..88ef7b1edc 100755 --- a/tests/functional/test_arm_bflt.py +++ b/tests/functional/test_arm_bflt.py @@ -6,7 +6,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os import bz2 from qemu_test import QemuUserTest, Asset @@ -25,7 +24,7 @@ class LoadBFLT(QemuUserTest): def test_stm32(self): # See https://elinux.org/STM32#User_Space rootfs_path_bz2 = self.ASSET_ROOTFS.fetch() - busybox_path = os.path.join(self.workdir, "bin/busybox") + busybox_path = self.scratch_file("bin", "busybox") with bz2.open(rootfs_path_bz2, 'rb') as cpio_handle: cpio_extract(cpio_handle, self.workdir) diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index fcd111f59d..2af6d9a18d 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -68,7 +68,7 @@ class BananaPiMachine(LinuxKernelTest): 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.vm.set_console() @@ -106,7 +106,7 @@ class BananaPiMachine(LinuxKernelTest): 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_path_xz = self.ASSET_ROOTFS.fetch() - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + rootfs_path = self.scratch_file('rootfs.cpio') lzma_uncompress(rootfs_path_xz, rootfs_path) image_pow2ceil_expand(rootfs_path) @@ -150,7 +150,7 @@ class BananaPiMachine(LinuxKernelTest): # This test download a 8.9 MiB compressed image and expand it # to 127 MiB. image_path_gz = self.ASSET_SD_IMAGE.fetch() - image_path = os.path.join(self.workdir, 'sdcard.img') + image_path = self.scratch_file('sdcard.img') gzip_uncompress(image_path_gz, image_path) image_pow2ceil_expand(image_path) diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/test_arm_canona1100.py index 65f1228296..b4e3633422 100755 --- a/tests/functional/test_arm_canona1100.py +++ b/tests/functional/test_arm_canona1100.py @@ -31,7 +31,8 @@ class CanonA1100Machine(QemuSystemTest): member="day18/barebox.canon-a1100.bin") self.vm.set_console() self.vm.add_args('-bios', - self.workdir + '/day18/barebox.canon-a1100.bin') + self.scratch_file('day18', + 'barebox.canon-a1100.bin')) self.vm.launch() wait_for_console_pattern(self, 'running /env/bin/init') diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index fdbd52a33c..d81c333d0c 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -44,7 +44,7 @@ class CubieboardMachine(LinuxKernelTest): dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.vm.set_console() @@ -78,7 +78,7 @@ class CubieboardMachine(LinuxKernelTest): dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_path_gz = self.ASSET_SATA_ROOTFS.fetch() - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + rootfs_path = self.scratch_file('rootfs.cpio') gzip_uncompress(rootfs_path_gz, rootfs_path) self.vm.set_console() @@ -112,7 +112,7 @@ class CubieboardMachine(LinuxKernelTest): # to 126 MiB. self.set_machine('cubieboard') image_path_gz = self.ASSET_OPENWRT.fetch() - image_path = os.path.join(self.workdir, 'sdcard.img') + image_path = self.scratch_file('sdcard.img') gzip_uncompress(image_path_gz, image_path) image_pow2ceil_expand(image_path) diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/test_arm_emcraft_sf2.py index ada4dfd82e..f9f3f069e2 100755 --- a/tests/functional/test_arm_emcraft_sf2.py +++ b/tests/functional/test_arm_emcraft_sf2.py @@ -28,7 +28,7 @@ class EmcraftSf2Machine(LinuxKernelTest): uboot_path = self.ASSET_UBOOT.fetch() spi_path = self.ASSET_SPI.fetch() - spi_path_rw = os.path.join(self.workdir, 'spi.bin') + spi_path_rw = self.scratch_file('spi.bin') shutil.copy(spi_path, spi_path_rw) os.chmod(spi_path_rw, 0o600) diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/test_arm_integratorcp.py index 54fa366ad4..a85b339d77 100755 --- a/tests/functional/test_arm_integratorcp.py +++ b/tests/functional/test_arm_integratorcp.py @@ -12,7 +12,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os import logging from qemu_test import QemuSystemTest, Asset @@ -67,7 +66,7 @@ class IntegratorMachine(QemuSystemTest): import numpy as np import cv2 - screendump_path = os.path.join(self.workdir, "screendump.pbm") + screendump_path = self.scratch_file("screendump.pbm") tuxlogo_path = self.ASSET_TUXLOGO.fetch() self.boot_integratorcp() diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index b113adfe61..bea67cfb6a 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -77,7 +77,7 @@ class BananaPiMachine(LinuxKernelTest): dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.vm.set_console() @@ -113,7 +113,7 @@ class BananaPiMachine(LinuxKernelTest): dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_path_xz = self.ASSET_ROOTFS.fetch() - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + rootfs_path = self.scratch_file('rootfs.cpio') lzma_uncompress(rootfs_path_xz, rootfs_path) image_pow2ceil_expand(rootfs_path) @@ -156,7 +156,7 @@ class BananaPiMachine(LinuxKernelTest): # to 1036 MiB, but the underlying filesystem is 1552 MiB... # As we expand it to 2 GiB we are safe. image_path_xz = self.ASSET_ARMBIAN.fetch() - image_path = os.path.join(self.workdir, 'armbian.img') + image_path = self.scratch_file('armbian.img') lzma_uncompress(image_path_xz, image_path) image_pow2ceil_expand(image_path) @@ -197,7 +197,7 @@ class BananaPiMachine(LinuxKernelTest): uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' uboot_path = self.extract_from_deb(deb_path, uboot_path) image_path_gz = self.ASSET_NETBSD.fetch() - image_path = os.path.join(self.workdir, 'armv7.img') + image_path = self.scratch_file('armv7.img') gzip_uncompress(image_path_gz, image_path) image_pow2ceil_expand(image_path) image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/test_arm_raspi2.py index 3bf079dc4d..075f6b3301 100755 --- a/tests/functional/test_arm_raspi2.py +++ b/tests/functional/test_arm_raspi2.py @@ -7,8 +7,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test.utils import gzip_uncompress @@ -65,7 +63,7 @@ class ArmRaspi2Machine(LinuxKernelTest): kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.set_machine('raspi2b') diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py index b3b39b069d..c6c8f9a5f4 100755 --- a/tests/functional/test_arm_smdkc210.py +++ b/tests/functional/test_arm_smdkc210.py @@ -32,7 +32,7 @@ class Smdkc210Machine(LinuxKernelTest): dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_path_gz = self.ASSET_ROOTFS.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.vm.set_console() diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/test_arm_vexpress.py index 6bd6290030..b1ac63ac36 100755 --- a/tests/functional/test_arm_vexpress.py +++ b/tests/functional/test_arm_vexpress.py @@ -18,8 +18,9 @@ class VExpressTest(LinuxKernelTest): self.set_machine('vexpress-a9') file_path = self.ASSET_DAY16.fetch() archive_extract(file_path, self.workdir) - self.launch_kernel(self.workdir + '/day16/winter.zImage', - dtb=self.workdir + '/day16/vexpress-v2p-ca9.dtb', + self.launch_kernel(self.scratch_file('day16', 'winter.zImage'), + dtb=self.scratch_file('day16', + 'vexpress-v2p-ca9.dtb'), wait_for='QEMU advent calendar') if __name__ == '__main__': diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/test_m68k_mcf5208evb.py index fb178fde1c..449248c3e8 100755 --- a/tests/functional/test_m68k_mcf5208evb.py +++ b/tests/functional/test_m68k_mcf5208evb.py @@ -19,7 +19,8 @@ class Mcf5208EvbTest(LinuxKernelTest): file_path = self.ASSET_DAY07.fetch() archive_extract(file_path, self.workdir) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day07/sanity-clause.elf') + self.vm.add_args('-kernel', + self.scratch_file('day07', 'sanity-clause.elf')) self.vm.launch() self.wait_for_console_pattern('QEMU advent calendar') diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py index 25a17d4794..ff773a7994 100755 --- a/tests/functional/test_m68k_nextcube.py +++ b/tests/functional/test_m68k_nextcube.py @@ -7,7 +7,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os import time from qemu_test import QemuSystemTest, Asset @@ -39,7 +38,7 @@ class NextCubeMachine(QemuSystemTest): @skipIfMissingImports("PIL") def test_bootrom_framebuffer_size(self): self.set_machine('next-cube') - screenshot_path = os.path.join(self.workdir, "dump.ppm") + screenshot_path = self.scratch_file("dump.ppm") self.check_bootrom_framebuffer(screenshot_path) from PIL import Image @@ -50,7 +49,7 @@ class NextCubeMachine(QemuSystemTest): @skipIfMissingCommands('tesseract') def test_bootrom_framebuffer_ocr_with_tesseract(self): self.set_machine('next-cube') - screenshot_path = os.path.join(self.workdir, "dump.ppm") + screenshot_path = self.scratch_file("dump.ppm") self.check_bootrom_framebuffer(screenshot_path) lines = tesseract_ocr(screenshot_path) text = '\n'.join(lines) diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index d452a0271c..61c4d6bbf8 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -25,7 +25,8 @@ class MicroblazeMachine(QemuSystemTest): file_path = self.ASSET_IMAGE.fetch() archive_extract(file_path, self.workdir) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin') + self.vm.add_args('-kernel', + self.scratch_file('day17', 'ballerina.bin')) self.vm.launch() wait_for_console_pattern(self, 'This architecture does not have ' 'kernel memory protection') diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index faa3927f2e..926c885f63 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -27,8 +27,9 @@ class MicroblazeelMachine(QemuSystemTest): file_path = self.ASSET_IMAGE.fetch() archive_extract(file_path, self.workdir) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day13/xmaton.bin') - self.vm.add_args('-nic', 'user,tftp=' + self.workdir + '/day13/') + self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) + tftproot = self.scratch_file('day13') + self.vm.add_args('-nic', f'user,tftp={tftproot}') self.vm.launch() wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') time.sleep(0.1) diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py index 52283e2dbd..ea362cf335 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/test_mips64el_malta.py @@ -68,7 +68,7 @@ class MaltaMachineConsole(LinuxKernelTest): def test_mips64el_malta_5KEc_cpio(self): kernel_path = self.ASSET_KERNEL_3_19_3.fetch() initrd_path_gz = self.ASSET_CPIO_R1.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.set_machine('malta') @@ -117,10 +117,10 @@ class MaltaMachineFramebuffer(LinuxKernelTest): import numpy as np import cv2 - screendump_path = os.path.join(self.workdir, 'screendump.pbm') + screendump_path = self.scratch_file('screendump.pbm') kernel_path_gz = self.ASSET_KERNEL_4_7_0.fetch() - kernel_path = self.workdir + "/vmlinux" + kernel_path = self.scratch_file("vmlinux") gzip_uncompress(kernel_path_gz, kernel_path) tuxlogo_path = self.ASSET_TUXLOGO.fetch() diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index a012081382..a6d80d0012 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -6,8 +6,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test.utils import gzip_uncompress @@ -52,7 +50,7 @@ class MaltaMachineConsole(LinuxKernelTest): kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinux-4.5.0-2-4kc-malta') initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) self.set_machine('malta') diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/test_mipsel_malta.py index b8dfddd856..77671e0081 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/test_mipsel_malta.py @@ -9,8 +9,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import QemuSystemTest, LinuxKernelTest, Asset from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern @@ -37,7 +35,7 @@ class MaltaMachineConsole(LinuxKernelTest): 'ce21ff4b07a981ecb8a39db2876616f5a2473eb2ab459c6f67465b9914b0c6b6') def do_test_mips_malta32el_nanomips(self, kernel_path_xz): - kernel_path = os.path.join(self.workdir, 'kernel') + kernel_path = self.scratch_file('kernel') lzma_uncompress(kernel_path_xz, kernel_path) self.set_machine('malta') @@ -78,7 +76,7 @@ class MaltaMachineYAMON(QemuSystemTest): zip_path = self.ASSET_YAMON_ROM.fetch() with ZipFile(zip_path, 'r') as zf: zf.extract(yamon_bin, path=self.workdir) - yamon_path = os.path.join(self.workdir, yamon_bin) + yamon_path = self.scratch_file(yamon_bin) self.set_machine('malta') self.vm.set_console() diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/test_or1k_sim.py index 5b68b6b628..a5b2b5b1e5 100755 --- a/tests/functional/test_or1k_sim.py +++ b/tests/functional/test_or1k_sim.py @@ -19,7 +19,7 @@ class OpenRISC1kSimTest(LinuxKernelTest): file_path = self.ASSET_DAY20.fetch() archive_extract(file_path, self.workdir) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day20/vmlinux') + self.vm.add_args('-kernel', self.scratch_file('day20', 'vmlinux')) self.vm.launch() self.wait_for_console_pattern('QEMU advent calendar') diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py index f1af92373e..bf4a6af9d4 100755 --- a/tests/functional/test_ppc64_e500.py +++ b/tests/functional/test_ppc64_e500.py @@ -18,7 +18,7 @@ class E500Test(LinuxKernelTest): self.cpu = 'e5500' file_path = self.ASSET_DAY19.fetch() archive_extract(file_path, self.workdir) - self.launch_kernel(self.workdir + '/day19/uImage', + self.launch_kernel(self.scratch_file('day19', 'uImage'), wait_for='QEMU advent calendar') if __name__ == '__main__': diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 88f0f99f24..037dfdf87e 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -46,23 +46,15 @@ class HypervisorTest(QemuSystemTest): :param path: path within the iso file of the file to be extracted :returns: path of the extracted file """ - filename = os.path.basename(path) - - cwd = os.getcwd() - os.chdir(self.workdir) + filename = self.scratch_file(os.path.basename(path)) cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) subprocess.run(cmd.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.chmod(filename, 0o600) - os.chdir(cwd) - # Return complete path to extracted file. Because callers to - # extract_from_iso() specify 'path' with a leading slash, it is - # necessary to use os.path.relpath() as otherwise os.path.join() - # interprets it as an absolute path and drops the self.workdir part. - return os.path.normpath(os.path.join(self.workdir, filename)) + return filename def setUp(self): super().setUp() diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/test_ppc_amiga.py index f5faa0f0b3..9ed23a1f0f 100755 --- a/tests/functional/test_ppc_amiga.py +++ b/tests/functional/test_ppc_amiga.py @@ -29,13 +29,15 @@ class AmigaOneMachine(QemuSystemTest): zip_file = self.ASSET_IMAGE.fetch() with ZipFile(zip_file, 'r') as zf: zf.extractall(path=self.workdir) - bios_fh = open(self.workdir + "/u-boot-amigaone.bin", "wb") - subprocess.run(['tail', '-c', '524288', - self.workdir + "/floppy_edition/updater.image"], - stdout=bios_fh) + bios = self.scratch_file("u-boot-amigaone.bin") + with open(bios, "wb") as bios_fh: + subprocess.run(['tail', '-c', '524288', + self.scratch_file("floppy_edition", + "updater.image")], + stdout=bios_fh) self.vm.set_console() - self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.add_args('-bios', bios) self.vm.launch() wait_for_console_pattern(self, 'FLASH:') diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py index e72cbdee12..1ae2f47bcc 100755 --- a/tests/functional/test_ppc_bamboo.py +++ b/tests/functional/test_ppc_bamboo.py @@ -28,10 +28,12 @@ class BambooMachine(QemuSystemTest): file_path = self.ASSET_IMAGE.fetch() archive_extract(file_path, self.workdir) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + - '/system-image-powerpc-440fp/linux', - '-initrd', self.workdir + - '/system-image-powerpc-440fp/rootfs.cpio.gz', + self.vm.add_args('-kernel', + self.scratch_file('system-image-powerpc-440fp', + 'linux'), + '-initrd', + self.scratch_file('system-image-powerpc-440fp', + 'rootfs.cpio.gz'), '-nic', 'user,model=rtl8139,restrict=on') self.vm.launch() wait_for_console_pattern(self, 'Type exit when done') diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/test_ppc_mac.py index 3f45e37a45..10812824bd 100755 --- a/tests/functional/test_ppc_mac.py +++ b/tests/functional/test_ppc_mac.py @@ -23,7 +23,7 @@ class MacTest(LinuxKernelTest): file_path = self.ASSET_DAY15.fetch() archive_extract(file_path, self.workdir) self.vm.add_args('-M', 'graphics=off') - self.launch_kernel(self.workdir + '/day15/invaders.elf', + self.launch_kernel(self.scratch_file('day15', 'invaders.elf'), wait_for='QEMU advent calendar') def test_ppc_g3beige(self): diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/test_ppc_mpc8544ds.py index 2b3f0894ae..87b5d4d12b 100755 --- a/tests/functional/test_ppc_mpc8544ds.py +++ b/tests/functional/test_ppc_mpc8544ds.py @@ -28,7 +28,7 @@ class Mpc8544dsMachine(QemuSystemTest): file_path = self.ASSET_IMAGE.fetch() archive_extract(file_path, self.workdir, member='creek/creek.bin') self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/creek/creek.bin') + self.vm.add_args('-kernel', self.scratch_file('creek', 'creek.bin')) self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', self.panic_message) diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/test_ppc_virtex_ml507.py index ffa9a0633e..f297651e64 100755 --- a/tests/functional/test_ppc_virtex_ml507.py +++ b/tests/functional/test_ppc_virtex_ml507.py @@ -28,8 +28,9 @@ class VirtexMl507Machine(QemuSystemTest): file_path = self.ASSET_IMAGE.fetch() archive_extract(file_path, self.workdir) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/hippo/hippo.linux', - '-dtb', self.workdir + '/hippo/virtex440-ml507.dtb', + self.vm.add_args('-kernel', self.scratch_file('hippo', 'hippo.linux'), + '-dtb', self.scratch_file('hippo', + 'virtex440-ml507.dtb'), '-m', '512') self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py index f7621d999d..b0adb38a45 100755 --- a/tests/functional/test_rx_gdbsim.py +++ b/tests/functional/test_rx_gdbsim.py @@ -10,8 +10,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os - from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern, skipFlakyTest @@ -40,7 +38,7 @@ class RxGdbSimMachine(QemuSystemTest): self.set_machine('gdbsim-r5f562n8') uboot_path_gz = self.ASSET_UBOOT.fetch() - uboot_path = os.path.join(self.workdir, 'u-boot.bin') + uboot_path = self.scratch_file('u-boot.bin') gzip_uncompress(uboot_path_gz, uboot_path) self.vm.set_console() diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/test_s390x_ccw_virtio.py index f7acd90a89..e5884a4dd0 100755 --- a/tests/functional/test_s390x_ccw_virtio.py +++ b/tests/functional/test_s390x_ccw_virtio.py @@ -175,7 +175,7 @@ class S390CCWVirtioMachine(QemuSystemTest): kernel_path = self.ASSET_F31_KERNEL.fetch() initrd_path_xz = self.ASSET_F31_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'initrd-raw.img') + initrd_path = self.scratch_file('initrd-raw.img') lzma_uncompress(initrd_path_xz, initrd_path) self.vm.set_console() diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py index c54c7a8177..82acff4e55 100755 --- a/tests/functional/test_s390x_topology.py +++ b/tests/functional/test_s390x_topology.py @@ -10,8 +10,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os - from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command from qemu_test import exec_command_and_wait_for_pattern @@ -89,7 +87,7 @@ class S390CPUTopology(QemuSystemTest): self.require_accelerator("kvm") kernel_path = self.ASSET_F35_KERNEL.fetch() initrd_path_xz = self.ASSET_F35_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'initrd-raw.img') + initrd_path = self.scratch_file('initrd-raw.img') lzma_uncompress(initrd_path_xz, initrd_path) self.vm.set_console() diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/test_sh4_r2d.py index e2fcde2d6b..dca4601392 100755 --- a/tests/functional/test_sh4_r2d.py +++ b/tests/functional/test_sh4_r2d.py @@ -22,7 +22,8 @@ class R2dTest(LinuxKernelTest): file_path = self.ASSET_DAY09.fetch() archive_extract(file_path, self.workdir) self.vm.add_args('-append', 'console=ttySC1') - self.launch_kernel(self.workdir + '/day09/zImage', console_index=1, + self.launch_kernel(self.scratch_file('day09', 'zImage'), + console_index=1, wait_for='QEMU advent calendar') if __name__ == '__main__': diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/test_sh4eb_r2d.py index cd46007942..b8dadabf3c 100755 --- a/tests/functional/test_sh4eb_r2d.py +++ b/tests/functional/test_sh4eb_r2d.py @@ -4,7 +4,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os import shutil from qemu_test import LinuxKernelTest, Asset @@ -22,11 +21,12 @@ class R2dEBTest(LinuxKernelTest): file_path = self.ASSET_TGZ.fetch() archive_extract(file_path, self.workdir) self.vm.add_args('-append', 'console=ttySC1 noiotrap') - self.launch_kernel(os.path.join(self.workdir, 'sh4eb/linux-kernel'), - initrd=os.path.join(self.workdir, 'sh4eb/initramfs.cpio.gz'), + self.launch_kernel(self.scratch_file('sh4eb', 'linux-kernel'), + initrd=self.scratch_file('sh4eb', + 'initramfs.cpio.gz'), console_index=1, wait_for='Type exit when done') exec_command_and_wait_for_pattern(self, 'exit', 'Restarting system') - shutil.rmtree(os.path.join(self.workdir, 'sh4eb')) + shutil.rmtree(self.scratch_file('sh4eb')) if __name__ == '__main__': LinuxKernelTest.main() diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/test_sparc64_sun4u.py index 32e245f4ad..e7f6db0f24 100755 --- a/tests/functional/test_sparc64_sun4u.py +++ b/tests/functional/test_sparc64_sun4u.py @@ -10,12 +10,11 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os - from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test.utils import archive_extract + class Sun4uMachine(QemuSystemTest): """Boots the Linux kernel and checks that the console is operational""" @@ -32,7 +31,7 @@ class Sun4uMachine(QemuSystemTest): kernel_name = 'day23/vmlinux' archive_extract(file_path, self.workdir, kernel_name) self.vm.set_console() - self.vm.add_args('-kernel', os.path.join(self.workdir, kernel_name), + self.vm.add_args('-kernel', self.scratch_file(kernel_name), '-append', 'printk.time=0') self.vm.launch() wait_for_console_pattern(self, 'Starting logging: OK') diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/test_sparc_sun4m.py index 573f85222a..619c03d36a 100755 --- a/tests/functional/test_sparc_sun4m.py +++ b/tests/functional/test_sparc_sun4m.py @@ -18,7 +18,7 @@ class Sun4mTest(LinuxKernelTest): self.set_machine('SS-20') file_path = self.ASSET_DAY11.fetch() archive_extract(file_path, self.workdir) - self.launch_kernel(self.workdir + '/day11/zImage.elf', + self.launch_kernel(self.scratch_file('day11', 'zImage.elf'), wait_for='QEMU advent calendar') if __name__ == '__main__': diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/test_xtensa_lx60.py index d4ad92dc6c..5048e4c69e 100755 --- a/tests/functional/test_xtensa_lx60.py +++ b/tests/functional/test_xtensa_lx60.py @@ -19,7 +19,8 @@ class XTensaLX60Test(LinuxKernelTest): self.cpu = 'dc233c' file_path = self.ASSET_DAY02.fetch() archive_extract(file_path, self.workdir) - self.launch_kernel(self.workdir + '/day02/santas-sleigh-ride.elf', + self.launch_kernel(self.scratch_file('day02', + 'santas-sleigh-ride.elf'), wait_for='QEMU advent calendar') if __name__ == '__main__': From 7788cc7c5a2395707082952bebfcdae1e2be8ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:36 +0000 Subject: [PATCH 0417/2892] tests/functional: remove redundant 'rmtree' call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everything in the scratch directory is automatically purged. Calling 'rmtree' again breaks the ability to optionally preserve the scratch directory contents. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-16-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_sh4eb_r2d.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/test_sh4eb_r2d.py index b8dadabf3c..c8954c93eb 100755 --- a/tests/functional/test_sh4eb_r2d.py +++ b/tests/functional/test_sh4eb_r2d.py @@ -4,8 +4,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import shutil - from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test.utils import archive_extract @@ -26,7 +24,6 @@ class R2dEBTest(LinuxKernelTest): 'initramfs.cpio.gz'), console_index=1, wait_for='Type exit when done') exec_command_and_wait_for_pattern(self, 'exit', 'Restarting system') - shutil.rmtree(self.scratch_file('sh4eb')) if __name__ == '__main__': LinuxKernelTest.main() From cfcb4484fc78cbbd835e2880add561e1fbef6796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:37 +0000 Subject: [PATCH 0418/2892] tests/functional: move archive handling into new archive.py file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More archive related code will be added shortly, so having a separate file makes more sense. The utils.py imports the functions from archive.py, so that existing callers don't need to be modified. This avoids redundant code churn until later in the series when all calls will be adapted for other reasons. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-17-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/archive.py | 31 +++++++++++++++++++++++++++ tests/functional/qemu_test/utils.py | 23 +++----------------- 2 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 tests/functional/qemu_test/archive.py diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py new file mode 100644 index 0000000000..9872f08d23 --- /dev/null +++ b/tests/functional/qemu_test/archive.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth + +import os +import subprocess +import tarfile + + +def tar_extract(archive, dest_dir, member=None): + with tarfile.open(archive) as tf: + if hasattr(tarfile, 'data_filter'): + tf.extraction_filter = getattr(tarfile, 'data_filter', + (lambda member, path: member)) + if member: + tf.extract(member=member, path=dest_dir) + else: + tf.extractall(path=dest_dir) + +def cpio_extract(cpio_handle, output_path): + cwd = os.getcwd() + os.chdir(output_path) + subprocess.run(['cpio', '-i'], + input=cpio_handle.read(), + stderr=subprocess.DEVNULL) + os.chdir(cwd) diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py index 1bf1c410d5..5ce1c4388e 100644 --- a/tests/functional/qemu_test/utils.py +++ b/tests/functional/qemu_test/utils.py @@ -12,8 +12,9 @@ import gzip import lzma import os import shutil -import subprocess -import tarfile + +from .archive import tar_extract as archive_extract +from .archive import cpio_extract """ Round up to next power of 2 @@ -36,16 +37,6 @@ def image_pow2ceil_expand(path): with open(path, 'ab+') as fd: fd.truncate(size_aligned) -def archive_extract(archive, dest_dir, member=None): - with tarfile.open(archive) as tf: - if hasattr(tarfile, 'data_filter'): - tf.extraction_filter = getattr(tarfile, 'data_filter', - (lambda member, path: member)) - if member: - tf.extract(member=member, path=dest_dir) - else: - tf.extractall(path=dest_dir) - def gzip_uncompress(gz_path, output_path): if os.path.exists(output_path): return @@ -67,11 +58,3 @@ def lzma_uncompress(xz_path, output_path): except: os.remove(output_path) raise - -def cpio_extract(cpio_handle, output_path): - cwd = os.getcwd() - os.chdir(output_path) - subprocess.run(['cpio', '-i'], - input=cpio_handle.read(), - stderr=subprocess.DEVNULL) - os.chdir(cwd) From ba32e50a1b2103e9f548c30ef5e96375c0002372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:38 +0000 Subject: [PATCH 0419/2892] tests/functional: move uncompress handling into new uncompress.py file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More uncompress related code will be added shortly, so having a separate file makes more sense. The utils.py imports the functions from archive.py, so that existing callers don't need to be modified. This avoids redundant code churn until later in the series when all calls will be adapted for other reasons. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-18-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/uncompress.py | 36 ++++++++++++++++++++++++ tests/functional/qemu_test/utils.py | 27 ++---------------- 2 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 tests/functional/qemu_test/uncompress.py diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py new file mode 100644 index 0000000000..955170df65 --- /dev/null +++ b/tests/functional/qemu_test/uncompress.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth + +import gzip +import lzma +import os +import shutil + + +def gzip_uncompress(gz_path, output_path): + if os.path.exists(output_path): + return + with gzip.open(gz_path, 'rb') as gz_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(gz_in, raw_out) + except: + os.remove(output_path) + raise + +def lzma_uncompress(xz_path, output_path): + if os.path.exists(output_path): + return + with lzma.open(xz_path, 'rb') as lzma_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(lzma_in, raw_out) + except: + os.remove(output_path) + raise diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py index 5ce1c4388e..6b87af4414 100644 --- a/tests/functional/qemu_test/utils.py +++ b/tests/functional/qemu_test/utils.py @@ -8,13 +8,12 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import gzip -import lzma import os -import shutil from .archive import tar_extract as archive_extract from .archive import cpio_extract +from .uncompress import gzip_uncompress +from .uncompress import lzma_uncompress """ Round up to next power of 2 @@ -36,25 +35,3 @@ def image_pow2ceil_expand(path): if size != size_aligned: with open(path, 'ab+') as fd: fd.truncate(size_aligned) - -def gzip_uncompress(gz_path, output_path): - if os.path.exists(output_path): - return - with gzip.open(gz_path, 'rb') as gz_in: - try: - with open(output_path, 'wb') as raw_out: - shutil.copyfileobj(gz_in, raw_out) - except: - os.remove(output_path) - raise - -def lzma_uncompress(xz_path, output_path): - if os.path.exists(output_path): - return - with lzma.open(xz_path, 'rb') as lzma_in: - try: - with open(output_path, 'wb') as raw_out: - shutil.copyfileobj(lzma_in, raw_out) - except: - os.remove(output_path) - raise From 379ee839f9ae374302c4b9f444c9f804ec7a2796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:39 +0000 Subject: [PATCH 0420/2892] tests/functional: add common zip_extract helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mirrors the existing archive_extract and cpio_extract helpers Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-19-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/archive.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py index 9872f08d23..06b66701c0 100644 --- a/tests/functional/qemu_test/archive.py +++ b/tests/functional/qemu_test/archive.py @@ -10,6 +10,7 @@ import os import subprocess import tarfile +import zipfile def tar_extract(archive, dest_dir, member=None): @@ -29,3 +30,10 @@ def cpio_extract(cpio_handle, output_path): input=cpio_handle.read(), stderr=subprocess.DEVNULL) os.chdir(cwd) + +def zip_extract(archive, dest_dir, member=None): + with zipfile.ZipFile(archive, 'r') as zf: + if member: + zf.extract(member=member, path=dest_dir) + else: + zf.extractall(path=dest_dir) From 512fe088b12885ddf035124cb5ff8315cfe0de06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:40 +0000 Subject: [PATCH 0421/2892] tests/functional: add common deb_extract helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mirrors the existing archive_extract, cpio_extract and zip_extract helpers Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-20-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/archive.py | 13 +++++++++++++ tests/functional/qemu_test/linuxkernel.py | 13 ++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py index 06b66701c0..a6fc97a557 100644 --- a/tests/functional/qemu_test/archive.py +++ b/tests/functional/qemu_test/archive.py @@ -12,6 +12,8 @@ import subprocess import tarfile import zipfile +from .cmd import run_cmd + def tar_extract(archive, dest_dir, member=None): with tarfile.open(archive) as tf: @@ -37,3 +39,14 @@ def zip_extract(archive, dest_dir, member=None): zf.extract(member=member, path=dest_dir) else: zf.extractall(path=dest_dir) + +def deb_extract(archive, dest_dir, member=None): + cwd = os.getcwd() + os.chdir(dest_dir) + try: + (stdout, stderr, ret) = run_cmd(['ar', 't', archive]) + file_path = stdout.split()[2] + run_cmd(['ar', 'x', archive, file_path]) + tar_extract(file_path, dest_dir, member) + finally: + os.chdir(cwd) diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py index 2e4f4e35fd..8f2810f3af 100644 --- a/tests/functional/qemu_test/linuxkernel.py +++ b/tests/functional/qemu_test/linuxkernel.py @@ -6,8 +6,9 @@ import os from .testcase import QemuSystemTest -from .cmd import run_cmd, wait_for_console_pattern -from .utils import archive_extract +from .cmd import wait_for_console_pattern +from .archive import deb_extract + class LinuxKernelTest(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' @@ -37,13 +38,7 @@ class LinuxKernelTest(QemuSystemTest): :param path: path within the deb archive of the file to be extracted :returns: path of the extracted file """ - cwd = os.getcwd() - os.chdir(self.workdir) - (stdout, stderr, ret) = run_cmd(['ar', 't', deb_path]) - file_path = stdout.split()[2] - run_cmd(['ar', 'x', deb_path, file_path]) - archive_extract(file_path, self.workdir) - os.chdir(cwd) + deb_extract(deb_path, self.workdir, member="." + path) # Return complete path to extracted file. Because callers to # extract_from_deb() specify 'path' with a leading slash, it is # necessary to use os.path.relpath() as otherwise scratch_file() From c055f1d26fbcc18de02e59569f3e2a224595835a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:41 +0000 Subject: [PATCH 0422/2892] tests/functional: let cpio_extract accept filenames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently cpio_extract differs from tar_extract/zip_extract in that it only allows a file-like object as input. Adapt it to also support filenames. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-21-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/archive.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py index a6fc97a557..bc448dee4a 100644 --- a/tests/functional/qemu_test/archive.py +++ b/tests/functional/qemu_test/archive.py @@ -8,7 +8,7 @@ # Thomas Huth import os -import subprocess +from subprocess import check_call, run, DEVNULL import tarfile import zipfile @@ -25,12 +25,18 @@ def tar_extract(archive, dest_dir, member=None): else: tf.extractall(path=dest_dir) -def cpio_extract(cpio_handle, output_path): +def cpio_extract(archive, output_path): cwd = os.getcwd() os.chdir(output_path) - subprocess.run(['cpio', '-i'], - input=cpio_handle.read(), - stderr=subprocess.DEVNULL) + # Not passing 'check=True' as cpio exits with non-zero + # status if the archive contains any device nodes :-( + if type(archive) == str: + run(['cpio', '-i', '-F', archive], + stdout=DEVNULL, stderr=DEVNULL) + else: + run(['cpio', '-i'], + input=archive.read(), + stdout=DEVNULL, stderr=DEVNULL) os.chdir(cwd) def zip_extract(archive, dest_dir, member=None): From c283afbf658cdc2156250a84be433fc0bdd002ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:42 +0000 Subject: [PATCH 0423/2892] tests/functional: add a generalized archive_extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are many types of archives that the tests deal with. Provide a generalized 'archive_extract' that can detect the format and delegate to the appropriate helper for extraction. This ensures that all archive extraction code follows the same design pattern. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-22-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 1 + tests/functional/qemu_test/archive.py | 58 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index fe6cbe3a8a..665c482d13 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -16,3 +16,4 @@ from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ skipFlakyTest, skipUntrustedTest, skipBigDataTest, \ skipIfMissingImports +from .archive import archive_extract diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py index bc448dee4a..c439d9413a 100644 --- a/tests/functional/qemu_test/archive.py +++ b/tests/functional/qemu_test/archive.py @@ -10,8 +10,10 @@ import os from subprocess import check_call, run, DEVNULL import tarfile +from urllib.parse import urlparse import zipfile +from .asset import Asset from .cmd import run_cmd @@ -56,3 +58,59 @@ def deb_extract(archive, dest_dir, member=None): tar_extract(file_path, dest_dir, member) finally: os.chdir(cwd) + +''' +@params archive: filename, Asset, or file-like object to extract +@params dest_dir: target directory to extract into +@params member: optional member file to limit extraction to + +Extracts @archive into @dest_dir. All files are extracted +unless @member specifies a limit. + +If @format is None, heuristics will be applied to guess the format +from the filename or Asset URL. @format must be non-None if @archive +is a file-like object. +''' +def archive_extract(archive, dest_dir, format=None, member=None): + if format is None: + format = guess_archive_format(archive) + if type(archive) == Asset: + archive = str(archive) + + if format == "tar": + tar_extract(archive, dest_dir, member) + elif format == "zip": + zip_extract(archive, dest_dir, member) + elif format == "cpio": + if member is not None: + raise Exception("Unable to filter cpio extraction") + cpio_extract(archive, dest_dir) + elif format == "deb": + if type(archive) != str: + raise Exception("Unable to use file-like object with deb archives") + deb_extract(archive, dest_dir, "./" + member) + else: + raise Exception(f"Unknown archive format {format}") + +''' +@params archive: filename, or Asset to guess + +Guess the format of @compressed, raising an exception if +no format can be determined +''' +def guess_archive_format(archive): + if type(archive) == Asset: + archive = urlparse(archive.url).path + elif type(archive) != str: + raise Exception(f"Unable to guess archive format for {archive}") + + if ".tar." in archive or archive.endswith("tgz"): + return "tar" + elif archive.endswith(".zip"): + return "zip" + elif archive.endswith(".cpio"): + return "cpio" + elif archive.endswith(".deb") or archive.endswith(".udeb"): + return "deb" + else: + raise Exception(f"Unknown archive format for {archive}") From 239fd29d6f82724c95a7a7f840c9f9244bcbe6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:43 +0000 Subject: [PATCH 0424/2892] tests/functional: add 'archive_extract' to QemuBaseTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper wrappers archive.archive_extract, forcing the use of the scratch directory, to ensure any extracted files are cleaned at test termination. If a specific member is requested, then the path to the extracted file is also returned. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-23-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 493938240c..19fb1d0c07 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -25,6 +25,7 @@ import uuid from qemu.machine import QEMUMachine from qemu.utils import kvm_available, tcg_available +from .archive import archive_extract from .asset import Asset from .cmd import run_cmd from .config import BUILD_DIR @@ -39,6 +40,37 @@ class QemuBaseTest(unittest.TestCase): log = None logdir = None + ''' + @params archive: filename, Asset, or file-like object to extract + @params format: optional archive format (tar, zip, deb, cpio) + @params sub_dir: optional sub-directory to extract into + @params member: optional member file to limit extraction to + + Extracts @archive into the scratch directory, or a directory beneath + named by @sub_dir. All files are extracted unless @member specifies + a limit. + + If @format is None, heuristics will be applied to guess the format + from the filename or Asset URL. @format must be non-None if @archive + is a file-like object. + + If @member is non-None, returns the fully qualified path to @member + ''' + def archive_extract(self, archive, format=None, sub_dir=None, member=None): + self.log.debug(f"Extract {archive} format={format}" + + f"sub_dir={sub_dir} member={member}") + if type(archive) == Asset: + archive.fetch() + if sub_dir is None: + archive_extract(archive, self.scratch_file(), format, member) + else: + archive_extract(archive, self.scratch_file(sub_dir), + format, member) + + if member is not None: + return self.scratch_file(member) + return None + ''' Create a temporary directory suitable for storing UNIX socket paths. From 5831ed84e7e450a652f215721aba34ed4e1ffb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:44 +0000 Subject: [PATCH 0425/2892] tests/functional: convert tests to new archive_extract helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace use of utils.archive_extract and extract_from_deb with the new archive_extract helper. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-24-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/linuxkernel.py | 19 ---------- tests/functional/test_aarch64_aspeed.py | 5 ++- tests/functional/test_aarch64_raspi3.py | 8 +---- tests/functional/test_aarch64_raspi4.py | 14 ++++---- tests/functional/test_acpi_bits.py | 15 ++------ tests/functional/test_arm_aspeed_ast1030.py | 16 +++------ tests/functional/test_arm_aspeed_ast2500.py | 6 ++-- tests/functional/test_arm_aspeed_ast2600.py | 5 +-- tests/functional/test_arm_aspeed_rainier.py | 11 +++--- tests/functional/test_arm_bflt.py | 3 +- tests/functional/test_arm_bpim2u.py | 26 +++++++------- tests/functional/test_arm_canona1100.py | 11 +++--- tests/functional/test_arm_cubieboard.py | 20 +++++------ tests/functional/test_arm_orangepi.py | 35 +++++++++---------- tests/functional/test_arm_raspi2.py | 14 ++++---- tests/functional/test_arm_smdkc210.py | 9 +++-- tests/functional/test_arm_vexpress.py | 5 ++- tests/functional/test_m68k_mcf5208evb.py | 5 ++- tests/functional/test_m68k_q800.py | 5 ++- .../functional/test_microblaze_s3adsp1800.py | 5 ++- .../test_microblazeel_s3adsp1800.py | 5 ++- tests/functional/test_mips64el_fuloong2e.py | 6 ++-- tests/functional/test_mips64el_malta.py | 6 ++-- tests/functional/test_mips_malta.py | 12 +++---- tests/functional/test_mipsel_malta.py | 5 +-- tests/functional/test_or1k_sim.py | 5 ++- tests/functional/test_ppc64_e500.py | 5 ++- tests/functional/test_ppc_amiga.py | 6 ++-- tests/functional/test_ppc_bamboo.py | 5 ++- tests/functional/test_ppc_mac.py | 6 ++-- tests/functional/test_ppc_mpc8544ds.py | 8 ++--- tests/functional/test_ppc_virtex_ml507.py | 5 ++- tests/functional/test_sh4_r2d.py | 5 ++- tests/functional/test_sh4eb_r2d.py | 5 ++- tests/functional/test_sparc64_sun4u.py | 8 ++--- tests/functional/test_sparc_sun4m.py | 5 ++- tests/functional/test_xtensa_lx60.py | 5 ++- 37 files changed, 134 insertions(+), 205 deletions(-) diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py index 8f2810f3af..2c9598102d 100644 --- a/tests/functional/qemu_test/linuxkernel.py +++ b/tests/functional/qemu_test/linuxkernel.py @@ -3,11 +3,8 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import os - from .testcase import QemuSystemTest from .cmd import wait_for_console_pattern -from .archive import deb_extract class LinuxKernelTest(QemuSystemTest): @@ -29,19 +26,3 @@ class LinuxKernelTest(QemuSystemTest): self.vm.launch() if wait_for: self.wait_for_console_pattern(wait_for) - - def extract_from_deb(self, deb_path, path): - """ - Extracts a file from a deb package into the test workdir - - :param deb_path: path to the deb archive - :param path: path within the deb archive of the file to be extracted - :returns: path of the extracted file - """ - deb_extract(deb_path, self.workdir, member="." + path) - # Return complete path to extracted file. Because callers to - # extract_from_deb() specify 'path' with a leading slash, it is - # necessary to use os.path.relpath() as otherwise scratch_file() - # interprets it as an absolute path and drops the required prefix - return os.path.normpath(self.scratch_file(os.path.relpath(path, '/'))) - diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 8ba2c67248..141d863859 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -11,7 +11,7 @@ import os from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern -from qemu_test.utils import archive_extract + class AST2x00MachineSDK(QemuSystemTest): @@ -34,8 +34,7 @@ class AST2x00MachineSDK(QemuSystemTest): def test_aarch64_ast2700_evb_sdk_v09_02(self): self.set_machine('ast2700-evb') - image_path = self.ASSET_SDK_V902_AST2700.fetch() - archive_extract(image_path, self.workdir) + self.archive_extract(self.ASSET_SDK_V902_AST2700) num_cpu = 4 uboot_size = os.path.getsize(self.scratch_file('ast2700-default', diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/test_aarch64_raspi3.py index 98ed6f9d56..74f6630ed2 100755 --- a/tests/functional/test_aarch64_raspi3.py +++ b/tests/functional/test_aarch64_raspi3.py @@ -7,8 +7,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from zipfile import ZipFile - from qemu_test import LinuxKernelTest, Asset @@ -21,11 +19,7 @@ class Aarch64Raspi3Machine(LinuxKernelTest): def test_aarch64_raspi3_atf(self): efi_name = 'RPI_EFI.fd' - zip_path = self.ASSET_RPI3_UEFI.fetch() - - with ZipFile(zip_path, 'r') as zf: - zf.extract(efi_name, path=self.workdir) - efi_fd = self.scratch_file(efi_name) + efi_fd = self.archive_extract(self.ASSET_RPI3_UEFI, member=efi_name) self.set_machine('raspi3b') self.vm.set_console(console_index=1) diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/test_aarch64_raspi4.py index 2cda03f86f..3918e35e82 100755 --- a/tests/functional/test_aarch64_raspi4.py +++ b/tests/functional/test_aarch64_raspi4.py @@ -30,9 +30,10 @@ class Aarch64Raspi4Machine(LinuxKernelTest): '7c0b16d1853772f6f4c3ca63e789b3b9ff4936efac9c8a01fb0c98c05c7a7648') def test_arm_raspi4(self): - deb_path = self.ASSET_KERNEL_20190215.fetch() - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel8.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2711-rpi-4-b.dtb') self.set_machine('raspi4b') self.vm.set_console() @@ -58,9 +59,10 @@ class Aarch64Raspi4Machine(LinuxKernelTest): def test_arm_raspi4_initrd(self): - deb_path = self.ASSET_KERNEL_20190215.fetch() - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel8.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2711-rpi-4-b.dtb') initrd_path_gz = self.ASSET_INITRD.fetch() initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 3b99ecf3a4..20da435687 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -35,8 +35,6 @@ import os import re import shutil import subprocess -import tarfile -import zipfile from typing import ( List, @@ -260,19 +258,12 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute %(self.BITS_INTERNAL_VER, self.BITS_COMMIT_HASH)) - bitsLocalArtLoc = self.ASSET_BITS.fetch() - self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc) - # extract the bits artifact in the temp working directory - with zipfile.ZipFile(bitsLocalArtLoc, 'r') as zref: - zref.extractall(prebuiltDir) + self.archive_extract(self.ASSET_BITS, sub_dir='prebuilt', format='zip') # extract the bits software in the temp working directory - with zipfile.ZipFile(bits_zip_file, 'r') as zref: - zref.extractall(self.workdir) - - with tarfile.open(grub_tar_file, 'r', encoding='utf-8') as tarball: - tarball.extractall(self.workdir) + self.archive_extract(bits_zip_file) + self.archive_extract(grub_tar_file) self.copy_test_scripts() self.copy_bits_config() diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py index 01b13c5591..d45d9f7c1c 100755 --- a/tests/functional/test_arm_aspeed_ast1030.py +++ b/tests/functional/test_arm_aspeed_ast1030.py @@ -8,7 +8,7 @@ from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from zipfile import ZipFile + class AST1030Machine(LinuxKernelTest): @@ -20,12 +20,9 @@ class AST1030Machine(LinuxKernelTest): def test_ast1030_zephyros_1_04(self): self.set_machine('ast1030-evb') - zip_file = self.ASSET_ZEPHYR_1_04.fetch() - kernel_name = "ast1030-evb-demo/zephyr.elf" - with ZipFile(zip_file, 'r') as zf: - zf.extract(kernel_name, path=self.workdir) - kernel_file = self.scratch_file(kernel_name) + kernel_file = self.archive_extract( + self.ASSET_ZEPHYR_1_04, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -42,12 +39,9 @@ class AST1030Machine(LinuxKernelTest): def test_ast1030_zephyros_1_07(self): self.set_machine('ast1030-evb') - zip_file = self.ASSET_ZEPHYR_1_07.fetch() - kernel_name = "ast1030-evb-demo/zephyr.bin" - with ZipFile(zip_file, 'r') as zf: - zf.extract(kernel_name, path=self.workdir) - kernel_file = self.scratch_file(kernel_name) + kernel_file = self.archive_extract( + self.ASSET_ZEPHYR_1_07, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py index 8c5593cdc8..743fc46eb2 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -7,7 +7,7 @@ from qemu_test import Asset from aspeed import AspeedTest from qemu_test import exec_command_and_wait_for_pattern -from qemu_test.utils import archive_extract + class AST2500Machine(AspeedTest): @@ -45,9 +45,7 @@ class AST2500Machine(AspeedTest): def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - image_path = self.ASSET_SDK_V806_AST2500.fetch() - - archive_extract(image_path, self.workdir) + self.archive_extract(self.ASSET_SDK_V806_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py index 25948c1c34..21640123ee 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -12,7 +12,6 @@ import subprocess from qemu_test import Asset from aspeed import AspeedTest from qemu_test import exec_command_and_wait_for_pattern, skipIfMissingCommands -from qemu_test.utils import archive_extract class AST2600Machine(AspeedTest): @@ -105,9 +104,7 @@ class AST2600Machine(AspeedTest): def test_arm_ast2600_evb_sdk(self): self.set_machine('ast2600-evb') - image_path = self.ASSET_SDK_V806_AST2600_A2.fetch() - - archive_extract(image_path, self.workdir) + self.archive_extract(self.ASSET_SDK_V806_AST2600_A2) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/test_arm_aspeed_rainier.py index b856aea6db..602d6194ac 100755 --- a/tests/functional/test_arm_aspeed_rainier.py +++ b/tests/functional/test_arm_aspeed_rainier.py @@ -43,11 +43,12 @@ class RainierMachine(AspeedTest): def test_arm_debian_kernel_boot(self): self.set_machine('rainier-bmc') - deb_path = self.ASSET_DEBIAN_LINUX_ARMHF_DEB.fetch() - - kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp') - dtb_path = self.extract_from_deb(deb_path, - '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') + kernel_path = self.archive_extract( + self.ASSET_DEBIAN_LINUX_ARMHF_DEB, + member='boot/vmlinuz-5.17.0-2-armmp') + dtb_path = self.archive_extract( + self.ASSET_DEBIAN_LINUX_ARMHF_DEB, + member='usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') self.vm.set_console() self.vm.add_args('-kernel', kernel_path, diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py index 88ef7b1edc..f273fc8354 100755 --- a/tests/functional/test_arm_bflt.py +++ b/tests/functional/test_arm_bflt.py @@ -10,7 +10,6 @@ import bz2 from qemu_test import QemuUserTest, Asset from qemu_test import skipIfMissingCommands, skipUntrustedTest -from qemu_test.utils import cpio_extract class LoadBFLT(QemuUserTest): @@ -27,7 +26,7 @@ class LoadBFLT(QemuUserTest): busybox_path = self.scratch_file("bin", "busybox") with bz2.open(rootfs_path_bz2, 'rb') as cpio_handle: - cpio_extract(cpio_handle, self.workdir) + self.archive_extract(cpio_handle, format="cpio") res = self.run_cmd(busybox_path) ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index 2af6d9a18d..91c56b0930 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -39,12 +39,11 @@ class BananaPiMachine(LinuxKernelTest): def test_arm_bpim2u(self): self.set_machine('bpim2u') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -61,12 +60,11 @@ class BananaPiMachine(LinuxKernelTest): def test_arm_bpim2u_initrd(self): self.set_machine('bpim2u') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) initrd_path_gz = self.ASSET_INITRD.fetch() initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) @@ -100,11 +98,11 @@ class BananaPiMachine(LinuxKernelTest): self.require_netdev('user') deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) rootfs_path_xz = self.ASSET_ROOTFS.fetch() rootfs_path = self.scratch_file('rootfs.cpio') lzma_uncompress(rootfs_path_xz, rootfs_path) diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/test_arm_canona1100.py index b4e3633422..21a1a596a0 100755 --- a/tests/functional/test_arm_canona1100.py +++ b/tests/functional/test_arm_canona1100.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract + class CanonA1100Machine(QemuSystemTest): """Boots the barebox firmware and checks that the console is operational""" @@ -26,13 +26,10 @@ class CanonA1100Machine(QemuSystemTest): def test_arm_canona1100(self): self.set_machine('canon-a1100') - file_path = self.ASSET_BIOS.fetch() - archive_extract(file_path, dest_dir=self.workdir, - member="day18/barebox.canon-a1100.bin") + bios = self.archive_extract(self.ASSET_BIOS, + member="day18/barebox.canon-a1100.bin") self.vm.set_console() - self.vm.add_args('-bios', - self.scratch_file('day18', - 'barebox.canon-a1100.bin')) + self.vm.add_args('-bios', bios) self.vm.launch() wait_for_console_pattern(self, 'running /env/bin/init') diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index d81c333d0c..32db303253 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -38,11 +38,11 @@ class CubieboardMachine(LinuxKernelTest): def test_arm_cubieboard_initrd(self): self.set_machine('cubieboard') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun4i-a10-cubieboard.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) initrd_path_gz = self.ASSET_INITRD.fetch() initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) @@ -71,11 +71,11 @@ class CubieboardMachine(LinuxKernelTest): def test_arm_cubieboard_sata(self): self.set_machine('cubieboard') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun4i-a10-cubieboard.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) rootfs_path_gz = self.ASSET_SATA_ROOTFS.fetch() rootfs_path = self.scratch_file('rootfs.cpio') diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index bea67cfb6a..aa2d9d19a4 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -50,11 +50,11 @@ class BananaPiMachine(LinuxKernelTest): def test_arm_orangepi(self): self.set_machine('orangepi-pc') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun8i-h3-orangepi-pc.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -71,11 +71,11 @@ class BananaPiMachine(LinuxKernelTest): def test_arm_orangepi_initrd(self): self.set_machine('orangepi-pc') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun8i-h3-orangepi-pc.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) initrd_path_gz = self.ASSET_INITRD.fetch() initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) @@ -107,11 +107,11 @@ class BananaPiMachine(LinuxKernelTest): def test_arm_orangepi_sd(self): self.set_machine('orangepi-pc') self.require_netdev('user') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun8i-h3-orangepi-pc.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) rootfs_path_xz = self.ASSET_ROOTFS.fetch() rootfs_path = self.scratch_file('rootfs.cpio') lzma_uncompress(rootfs_path_xz, rootfs_path) @@ -189,13 +189,12 @@ class BananaPiMachine(LinuxKernelTest): def test_arm_orangepi_uboot_netbsd9(self): self.set_machine('orangepi-pc') # This test download a 304MB compressed image and expand it to 2GB - deb_path = self.ASSET_UBOOT.fetch() # We use the common OrangePi PC 'plus' build of U-Boot for our secondary # program loader (SPL). We will then set the path to the more specific # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, # before to boot NetBSD. - uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' - uboot_path = self.extract_from_deb(deb_path, uboot_path) + uboot_path = 'usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' + uboot_path = self.archive_extract(self.ASSET_UBOOT, member=uboot_path) image_path_gz = self.ASSET_NETBSD.fetch() image_path = self.scratch_file('armv7.img') gzip_uncompress(image_path_gz, image_path) diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/test_arm_raspi2.py index 075f6b3301..5e38d1a937 100755 --- a/tests/functional/test_arm_raspi2.py +++ b/tests/functional/test_arm_raspi2.py @@ -35,9 +35,10 @@ class ArmRaspi2Machine(LinuxKernelTest): serial_kernel_cmdline = { 0: 'earlycon=pl011,0x3f201000 console=ttyAMA0', } - deb_path = self.ASSET_KERNEL_20190215.fetch() - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel7.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2709-rpi-2-b.dtb') self.set_machine('raspi2b') self.vm.set_console() @@ -59,9 +60,10 @@ class ArmRaspi2Machine(LinuxKernelTest): self.do_test_arm_raspi2(0) def test_arm_raspi2_initrd(self): - deb_path = self.ASSET_KERNEL_20190215.fetch() - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel7.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2709-rpi-2-b.dtb') initrd_path_gz = self.ASSET_INITRD.fetch() initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py index c6c8f9a5f4..f4e86f7e2b 100755 --- a/tests/functional/test_arm_smdkc210.py +++ b/tests/functional/test_arm_smdkc210.py @@ -25,11 +25,10 @@ class Smdkc210Machine(LinuxKernelTest): def test_arm_exynos4210_initrd(self): self.set_machine('smdkc210') - deb_path = self.ASSET_DEB.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.19.0-6-armmp') - dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) + kernel_path = self.archive_extract(self.ASSET_DEB, + member='boot/vmlinuz-4.19.0-6-armmp') + dtb_path = 'usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) initrd_path_gz = self.ASSET_ROOTFS.fetch() initrd_path = self.scratch_file('rootfs.cpio') diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/test_arm_vexpress.py index b1ac63ac36..6b11552894 100755 --- a/tests/functional/test_arm_vexpress.py +++ b/tests/functional/test_arm_vexpress.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class VExpressTest(LinuxKernelTest): @@ -16,8 +16,7 @@ class VExpressTest(LinuxKernelTest): def test_arm_vexpressa9(self): self.set_machine('vexpress-a9') - file_path = self.ASSET_DAY16.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY16) self.launch_kernel(self.scratch_file('day16', 'winter.zImage'), dtb=self.scratch_file('day16', 'vexpress-v2p-ca9.dtb'), diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/test_m68k_mcf5208evb.py index 449248c3e8..c7d1998933 100755 --- a/tests/functional/test_m68k_mcf5208evb.py +++ b/tests/functional/test_m68k_mcf5208evb.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class Mcf5208EvbTest(LinuxKernelTest): @@ -16,8 +16,7 @@ class Mcf5208EvbTest(LinuxKernelTest): def test_m68k_mcf5208evb(self): self.set_machine('mcf5208evb') - file_path = self.ASSET_DAY07.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY07) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day07', 'sanity-clause.elf')) diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/test_m68k_q800.py index 3b17244b98..400b7aeb5d 100755 --- a/tests/functional/test_m68k_q800.py +++ b/tests/functional/test_m68k_q800.py @@ -18,9 +18,8 @@ class Q800MachineTest(LinuxKernelTest): def test_m68k_q800(self): self.set_machine('q800') - deb_path = self.ASSET_KERNEL.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-5.3.0-1-m68k') + kernel_path = self.archive_extract(self.ASSET_KERNEL, + member='boot/vmlinux-5.3.0-1-m68k') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index 61c4d6bbf8..2c4464bd05 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -9,7 +9,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract + class MicroblazeMachine(QemuSystemTest): @@ -22,8 +22,7 @@ class MicroblazeMachine(QemuSystemTest): def test_microblaze_s3adsp1800(self): self.set_machine('petalogix-s3adsp1800') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day17', 'ballerina.bin')) diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index 926c885f63..c382afe6bf 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -11,7 +11,7 @@ import time from qemu_test import exec_command, exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract + class MicroblazeelMachine(QemuSystemTest): @@ -24,8 +24,7 @@ class MicroblazeelMachine(QemuSystemTest): def test_microblazeel_s3adsp1800(self): self.require_netdev('user') self.set_machine('petalogix-s3adsp1800') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) tftproot = self.scratch_file('day13') diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/test_mips64el_fuloong2e.py index 531d16d8f6..35e500b022 100755 --- a/tests/functional/test_mips64el_fuloong2e.py +++ b/tests/functional/test_mips64el_fuloong2e.py @@ -26,9 +26,9 @@ class MipsFuloong2e(LinuxKernelTest): '2a70f15b397f4ced632b0c15cb22660394190644146d804d60a4796eefbe1f50') def test_linux_kernel_3_16(self): - deb_path = self.ASSET_KERNEL.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-3.16.0-6-loongson-2e') + kernel_path = self.archive_extract( + self.ASSET_KERNEL, + member='boot/vmlinux-3.16.0-6-loongson-2e') self.set_machine('fuloong2e') self.vm.set_console() diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py index ea362cf335..39fafb8bf1 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/test_mips64el_malta.py @@ -39,9 +39,9 @@ class MaltaMachineConsole(LinuxKernelTest): [2] https://kernel-team.pages.debian.net/kernel-handbook/ ch-common-tasks.html#s-common-official """ - deb_path = self.ASSET_KERNEL_2_63_2.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-5kc-malta') + kernel_path = self.archive_extract( + self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-5kc-malta') self.set_machine('malta') self.vm.set_console() diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index a6d80d0012..6ab6c0832a 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -20,9 +20,9 @@ class MaltaMachineConsole(LinuxKernelTest): '16ca524148afb0626f483163e5edf352bc1ab0e4fc7b9f9d473252762f2c7a43') def test_mips_malta(self): - deb_path = self.ASSET_KERNEL_2_63_2.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-4kc-malta') + kernel_path = self.archive_extract( + self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-4kc-malta') self.set_machine('malta') self.vm.set_console() @@ -46,9 +46,9 @@ class MaltaMachineConsole(LinuxKernelTest): 'dcfe3a7fe3200da3a00d176b95caaa086495eb158f2bff64afc67d7e1eb2cddc') def test_mips_malta_cpio(self): - deb_path = self.ASSET_KERNEL_4_5_0.fetch() - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-4.5.0-2-4kc-malta') + kernel_path = self.archive_extract( + self.ASSET_KERNEL_4_5_0, + member='boot/vmlinux-4.5.0-2-4kc-malta') initrd_path_gz = self.ASSET_INITRD.fetch() initrd_path = self.scratch_file('rootfs.cpio') gzip_uncompress(initrd_path_gz, initrd_path) diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/test_mipsel_malta.py index 77671e0081..1f44881c78 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/test_mipsel_malta.py @@ -13,7 +13,6 @@ from qemu_test import QemuSystemTest, LinuxKernelTest, Asset from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern from qemu_test.utils import lzma_uncompress -from zipfile import ZipFile class MaltaMachineConsole(LinuxKernelTest): @@ -73,9 +72,7 @@ class MaltaMachineYAMON(QemuSystemTest): def test_mipsel_malta_yamon(self): yamon_bin = 'yamon-02.22.bin' - zip_path = self.ASSET_YAMON_ROM.fetch() - with ZipFile(zip_path, 'r') as zf: - zf.extract(yamon_bin, path=self.workdir) + self.archive_extract(self.ASSET_YAMON_ROM) yamon_path = self.scratch_file(yamon_bin) self.set_machine('malta') diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/test_or1k_sim.py index a5b2b5b1e5..f9f0b690a0 100755 --- a/tests/functional/test_or1k_sim.py +++ b/tests/functional/test_or1k_sim.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class OpenRISC1kSimTest(LinuxKernelTest): @@ -16,8 +16,7 @@ class OpenRISC1kSimTest(LinuxKernelTest): def test_or1k_sim(self): self.set_machine('or1k-sim') - file_path = self.ASSET_DAY20.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY20) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day20', 'vmlinux')) self.vm.launch() diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py index bf4a6af9d4..b92fe0b0e7 100755 --- a/tests/functional/test_ppc64_e500.py +++ b/tests/functional/test_ppc64_e500.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class E500Test(LinuxKernelTest): @@ -16,8 +16,7 @@ class E500Test(LinuxKernelTest): def test_ppc64_e500(self): self.set_machine('ppce500') self.cpu = 'e5500' - file_path = self.ASSET_DAY19.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY19) self.launch_kernel(self.scratch_file('day19', 'uImage'), wait_for='QEMU advent calendar') diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/test_ppc_amiga.py index 9ed23a1f0f..8600e2e963 100755 --- a/tests/functional/test_ppc_amiga.py +++ b/tests/functional/test_ppc_amiga.py @@ -11,7 +11,7 @@ import subprocess from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from zipfile import ZipFile + class AmigaOneMachine(QemuSystemTest): @@ -26,9 +26,7 @@ class AmigaOneMachine(QemuSystemTest): self.require_accelerator("tcg") self.set_machine('amigaone') tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' - zip_file = self.ASSET_IMAGE.fetch() - with ZipFile(zip_file, 'r') as zf: - zf.extractall(path=self.workdir) + self.archive_extract(self.ASSET_IMAGE, format="zip") bios = self.scratch_file("u-boot-amigaone.bin") with open(bios, "wb") as bios_fh: subprocess.run(['tail', '-c', '524288', diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py index 1ae2f47bcc..fddcc24d0d 100755 --- a/tests/functional/test_ppc_bamboo.py +++ b/tests/functional/test_ppc_bamboo.py @@ -7,11 +7,11 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test.utils import archive_extract from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern + class BambooMachine(QemuSystemTest): timeout = 90 @@ -25,8 +25,7 @@ class BambooMachine(QemuSystemTest): self.set_machine('bamboo') self.require_accelerator("tcg") self.require_netdev('user') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('system-image-powerpc-440fp', diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/test_ppc_mac.py index 10812824bd..9e4bc1a52c 100755 --- a/tests/functional/test_ppc_mac.py +++ b/tests/functional/test_ppc_mac.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class MacTest(LinuxKernelTest): @@ -19,9 +19,7 @@ class MacTest(LinuxKernelTest): # we're running kvm_hv or kvm_pr. For now let's disable this test # if we don't have TCG support. self.require_accelerator("tcg") - - file_path = self.ASSET_DAY15.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY15) self.vm.add_args('-M', 'graphics=off') self.launch_kernel(self.scratch_file('day15', 'invaders.elf'), wait_for='QEMU advent calendar') diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/test_ppc_mpc8544ds.py index 87b5d4d12b..0715410d7a 100755 --- a/tests/functional/test_ppc_mpc8544ds.py +++ b/tests/functional/test_ppc_mpc8544ds.py @@ -7,10 +7,10 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test.utils import archive_extract from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern + class Mpc8544dsMachine(QemuSystemTest): timeout = 90 @@ -25,10 +25,10 @@ class Mpc8544dsMachine(QemuSystemTest): def test_ppc_mpc8544ds(self): self.require_accelerator("tcg") self.set_machine('mpc8544ds') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir, member='creek/creek.bin') + kernel_file = self.archive_extract(self.ASSET_IMAGE, + member='creek/creek.bin') self.vm.set_console() - self.vm.add_args('-kernel', self.scratch_file('creek', 'creek.bin')) + self.vm.add_args('-kernel', kernel_file) self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', self.panic_message) diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/test_ppc_virtex_ml507.py index f297651e64..8fe43549b7 100755 --- a/tests/functional/test_ppc_virtex_ml507.py +++ b/tests/functional/test_ppc_virtex_ml507.py @@ -7,10 +7,10 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test.utils import archive_extract from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern + class VirtexMl507Machine(QemuSystemTest): timeout = 90 @@ -25,8 +25,7 @@ class VirtexMl507Machine(QemuSystemTest): def test_ppc_virtex_ml507(self): self.require_accelerator("tcg") self.set_machine('virtex-ml507') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('hippo', 'hippo.linux'), '-dtb', self.scratch_file('hippo', diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/test_sh4_r2d.py index dca4601392..03a648374c 100755 --- a/tests/functional/test_sh4_r2d.py +++ b/tests/functional/test_sh4_r2d.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset, skipFlakyTest -from qemu_test.utils import archive_extract + class R2dTest(LinuxKernelTest): @@ -19,8 +19,7 @@ class R2dTest(LinuxKernelTest): @skipFlakyTest(bug_url=None) def test_r2d(self): self.set_machine('r2d') - file_path = self.ASSET_DAY09.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY09) self.vm.add_args('-append', 'console=ttySC1') self.launch_kernel(self.scratch_file('day09', 'zImage'), console_index=1, diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/test_sh4eb_r2d.py index c8954c93eb..473093bbe1 100755 --- a/tests/functional/test_sh4eb_r2d.py +++ b/tests/functional/test_sh4eb_r2d.py @@ -6,7 +6,7 @@ from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from qemu_test.utils import archive_extract + class R2dEBTest(LinuxKernelTest): @@ -16,8 +16,7 @@ class R2dEBTest(LinuxKernelTest): def test_sh4eb_r2d(self): self.set_machine('r2d') - file_path = self.ASSET_TGZ.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_TGZ) self.vm.add_args('-append', 'console=ttySC1 noiotrap') self.launch_kernel(self.scratch_file('sh4eb', 'linux-kernel'), initrd=self.scratch_file('sh4eb', diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/test_sparc64_sun4u.py index e7f6db0f24..27ac289659 100755 --- a/tests/functional/test_sparc64_sun4u.py +++ b/tests/functional/test_sparc64_sun4u.py @@ -12,7 +12,6 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract class Sun4uMachine(QemuSystemTest): @@ -27,11 +26,10 @@ class Sun4uMachine(QemuSystemTest): def test_sparc64_sun4u(self): self.set_machine('sun4u') - file_path = self.ASSET_IMAGE.fetch() - kernel_name = 'day23/vmlinux' - archive_extract(file_path, self.workdir, kernel_name) + kernel_file = self.archive_extract(self.ASSET_IMAGE, + member='day23/vmlinux') self.vm.set_console() - self.vm.add_args('-kernel', self.scratch_file(kernel_name), + self.vm.add_args('-kernel', kernel_file, '-append', 'printk.time=0') self.vm.launch() wait_for_console_pattern(self, 'Starting logging: OK') diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/test_sparc_sun4m.py index 619c03d36a..7cd28ebdd1 100755 --- a/tests/functional/test_sparc_sun4m.py +++ b/tests/functional/test_sparc_sun4m.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class Sun4mTest(LinuxKernelTest): @@ -16,8 +16,7 @@ class Sun4mTest(LinuxKernelTest): def test_sparc_ss20(self): self.set_machine('SS-20') - file_path = self.ASSET_DAY11.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY11) self.launch_kernel(self.scratch_file('day11', 'zImage.elf'), wait_for='QEMU advent calendar') diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/test_xtensa_lx60.py index 5048e4c69e..147c920899 100755 --- a/tests/functional/test_xtensa_lx60.py +++ b/tests/functional/test_xtensa_lx60.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import archive_extract + class XTensaLX60Test(LinuxKernelTest): @@ -17,8 +17,7 @@ class XTensaLX60Test(LinuxKernelTest): def test_xtensa_lx60(self): self.set_machine('lx60') self.cpu = 'dc233c' - file_path = self.ASSET_DAY02.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_DAY02) self.launch_kernel(self.scratch_file('day02', 'santas-sleigh-ride.elf'), wait_for='QEMU advent calendar') From dd66e65f055a0d8651afcf63460b1130487aeebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:45 +0000 Subject: [PATCH 0426/2892] tests/functional: add a generalized uncompress helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are many types of compression that the tests deal with, and it makes sense to have a single helper 'uncompress' that can deal with all. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-25-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 1 + tests/functional/qemu_test/uncompress.py | 47 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 665c482d13..3bd043e608 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -17,3 +17,4 @@ from .decorators import skipIfMissingCommands, skipIfNotMachine, \ skipFlakyTest, skipUntrustedTest, skipBigDataTest, \ skipIfMissingImports from .archive import archive_extract +from .uncompress import uncompress diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py index 955170df65..6d02ded066 100644 --- a/tests/functional/qemu_test/uncompress.py +++ b/tests/functional/qemu_test/uncompress.py @@ -11,6 +11,9 @@ import gzip import lzma import os import shutil +from urllib.parse import urlparse + +from .asset import Asset def gzip_uncompress(gz_path, output_path): @@ -34,3 +37,47 @@ def lzma_uncompress(xz_path, output_path): except: os.remove(output_path) raise + +''' +@params compressed: filename, Asset, or file-like object to uncompress +@params uncompressed: filename to uncompress into +@params format: optional compression format (gzip, lzma) + +Uncompresses @compressed into @uncompressed + +If @format is None, heuristics will be applied to guess the format +from the filename or Asset URL. @format must be non-None if @uncompressed +is a file-like object. + +Returns the fully qualified path to the uncompessed file +''' +def uncompress(compressed, uncompressed, format=None): + if format is None: + format = guess_uncompress_format(compressed) + + if format == "xz": + lzma_uncompress(str(compressed), uncompressed) + elif format == "gz": + gzip_uncompress(str(compressed), uncompressed) + else: + raise Exception(f"Unknown compression format {format}") + +''' +@params compressed: filename, Asset, or file-like object to guess + +Guess the format of @compressed, raising an exception if +no format can be determined +''' +def guess_uncompress_format(compressed): + if type(compressed) == Asset: + compressed = urlparse(compressed.url).path + elif type(compressed) != str: + raise Exception(f"Unable to guess compression cformat for {compressed}") + + (name, ext) = os.path.splitext(compressed) + if ext == ".xz": + return "xz" + elif ext == ".gz": + return "gz" + else: + raise Exception(f"Unknown compression format for {compressed}") From fd4abcb00839a310fc668d86177278e789f51688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:46 +0000 Subject: [PATCH 0427/2892] tests/functional: add 'uncompress' to QemuBaseTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper wrappers utils.uncompress, forcing the use of the scratch directory, to ensure any uncompressed files are cleaned at test termination. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-26-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 19fb1d0c07..d0bb3141d5 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -29,6 +29,7 @@ from .archive import archive_extract from .asset import Asset from .cmd import run_cmd from .config import BUILD_DIR +from .uncompress import uncompress class QemuBaseTest(unittest.TestCase): @@ -40,6 +41,30 @@ class QemuBaseTest(unittest.TestCase): log = None logdir = None + ''' + @params compressed: filename, Asset, or file-like object to uncompress + @params format: optional compression format (gzip, lzma) + + Uncompresses @compressed into the scratch directory. + + If @format is None, heuristics will be applied to guess the format + from the filename or Asset URL. @format must be non-None if @uncompressed + is a file-like object. + + Returns the fully qualified path to the uncompressed file + ''' + def uncompress(self, compressed, format=None): + self.log.debug(f"Uncompress {compressed} format={format}") + if type(compressed) == Asset: + compressed.fetch() + + (name, ext) = os.path.splitext(str(compressed)) + uncompressed = self.scratch_file(os.path.basename(name)) + + uncompress(compressed, uncompressed, format) + + return uncompressed + ''' @params archive: filename, Asset, or file-like object to extract @params format: optional archive format (tar, zip, deb, cpio) From 65d35a4e27a808370bac40c06c1e24d30461b0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:47 +0000 Subject: [PATCH 0428/2892] tests/functional: convert tests to new uncompress helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace use of lzma_uncompress and gzip_uncompress with the new uncompress helper. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-27-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_raspi4.py | 5 +---- tests/functional/test_aarch64_sbsaref.py | 10 +++------- tests/functional/test_alpha_clipper.py | 4 +--- tests/functional/test_arm_bpim2u.py | 13 +++---------- tests/functional/test_arm_cubieboard.py | 14 ++++---------- tests/functional/test_arm_orangepi.py | 17 ++++------------- tests/functional/test_arm_raspi2.py | 5 +---- tests/functional/test_arm_smdkc210.py | 6 ++---- tests/functional/test_mips64el_malta.py | 9 ++------- tests/functional/test_mips_malta.py | 5 +---- tests/functional/test_mipsel_malta.py | 15 +++++---------- tests/functional/test_rx_gdbsim.py | 5 +---- tests/functional/test_s390x_ccw_virtio.py | 6 ++---- tests/functional/test_s390x_topology.py | 5 +---- 14 files changed, 31 insertions(+), 88 deletions(-) diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/test_aarch64_raspi4.py index 3918e35e82..7a4302b0c5 100755 --- a/tests/functional/test_aarch64_raspi4.py +++ b/tests/functional/test_aarch64_raspi4.py @@ -7,7 +7,6 @@ from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from qemu_test.utils import gzip_uncompress class Aarch64Raspi4Machine(LinuxKernelTest): @@ -63,9 +62,7 @@ class Aarch64Raspi4Machine(LinuxKernelTest): member='boot/kernel8.img') dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, member='boot/bcm2711-rpi-4-b.dtb') - initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_INITRD) self.set_machine('raspi4b') self.vm.set_console() diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index 533ca64407..2d756efdab 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -11,7 +11,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import interrupt_interactive_console_until_pattern -from qemu_test.utils import lzma_uncompress + def fetch_firmware(test): """ @@ -29,14 +29,10 @@ def fetch_firmware(test): """ # Secure BootRom (TF-A code) - fs0_xz_path = Aarch64SbsarefMachine.ASSET_FLASH0.fetch() - fs0_path = test.scratch_file("SBSA_FLASH0.fd") - lzma_uncompress(fs0_xz_path, fs0_path) + fs0_path = test.uncompress(Aarch64SbsarefMachine.ASSET_FLASH0) # Non-secure rom (UEFI and EFI variables) - fs1_xz_path = Aarch64SbsarefMachine.ASSET_FLASH1.fetch() - fs1_path = test.scratch_file("SBSA_FLASH1.fd") - lzma_uncompress(fs1_xz_path, fs1_path) + fs1_path = test.uncompress(Aarch64SbsarefMachine.ASSET_FLASH1) for path in [fs0_path, fs1_path]: with open(path, "ab+") as fd: diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/test_alpha_clipper.py index 72cd7b57e6..c5d7181953 100755 --- a/tests/functional/test_alpha_clipper.py +++ b/tests/functional/test_alpha_clipper.py @@ -6,7 +6,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import gzip_uncompress class AlphaClipperTest(LinuxKernelTest): @@ -20,8 +19,7 @@ class AlphaClipperTest(LinuxKernelTest): self.set_machine('clipper') kernel_path = self.ASSET_KERNEL.fetch() - uncompressed_kernel = self.scratch_file('vmlinux') - gzip_uncompress(kernel_path, uncompressed_kernel) + uncompressed_kernel = self.uncompress(self.ASSET_KERNEL, format="gz") self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index 91c56b0930..12cd359746 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -10,7 +10,6 @@ import os from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern from qemu_test import Asset, interrupt_interactive_console_until_pattern from qemu_test import skipBigDataTest -from qemu_test.utils import gzip_uncompress, lzma_uncompress from qemu_test.utils import image_pow2ceil_expand @@ -65,9 +64,7 @@ class BananaPiMachine(LinuxKernelTest): dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_INITRD) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -103,9 +100,7 @@ class BananaPiMachine(LinuxKernelTest): dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - rootfs_path_xz = self.ASSET_ROOTFS.fetch() - rootfs_path = self.scratch_file('rootfs.cpio') - lzma_uncompress(rootfs_path_xz, rootfs_path) + rootfs_path = self.uncompress(self.ASSET_ROOTFS) image_pow2ceil_expand(rootfs_path) self.vm.set_console() @@ -147,9 +142,7 @@ class BananaPiMachine(LinuxKernelTest): self.set_machine('bpim2u') # This test download a 8.9 MiB compressed image and expand it # to 127 MiB. - image_path_gz = self.ASSET_SD_IMAGE.fetch() - image_path = self.scratch_file('sdcard.img') - gzip_uncompress(image_path_gz, image_path) + image_path = self.uncompress(self.ASSET_SD_IMAGE) image_pow2ceil_expand(image_path) self.vm.set_console() diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index 32db303253..423db710e8 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -9,7 +9,7 @@ import os from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import skipBigDataTest -from qemu_test.utils import gzip_uncompress, image_pow2ceil_expand +from qemu_test.utils import image_pow2ceil_expand class CubieboardMachine(LinuxKernelTest): @@ -43,9 +43,7 @@ class CubieboardMachine(LinuxKernelTest): dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun4i-a10-cubieboard.dtb') dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_INITRD) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -77,9 +75,7 @@ class CubieboardMachine(LinuxKernelTest): 'sun4i-a10-cubieboard.dtb') dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - rootfs_path_gz = self.ASSET_SATA_ROOTFS.fetch() - rootfs_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(rootfs_path_gz, rootfs_path) + rootfs_path = self.uncompress(self.ASSET_SATA_ROOTFS) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -111,9 +107,7 @@ class CubieboardMachine(LinuxKernelTest): # This test download a 7.5 MiB compressed image and expand it # to 126 MiB. self.set_machine('cubieboard') - image_path_gz = self.ASSET_OPENWRT.fetch() - image_path = self.scratch_file('sdcard.img') - gzip_uncompress(image_path_gz, image_path) + image_path = self.uncompress(self.ASSET_OPENWRT) image_pow2ceil_expand(image_path) self.vm.set_console() diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index aa2d9d19a4..18ee50216b 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -11,7 +11,6 @@ import shutil from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern from qemu_test import Asset, interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern, skipBigDataTest -from qemu_test.utils import gzip_uncompress, lzma_uncompress from qemu_test.utils import image_pow2ceil_expand @@ -76,9 +75,7 @@ class BananaPiMachine(LinuxKernelTest): dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-h3-orangepi-pc.dtb') dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_INITRD) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -112,9 +109,7 @@ class BananaPiMachine(LinuxKernelTest): dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-h3-orangepi-pc.dtb') dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - rootfs_path_xz = self.ASSET_ROOTFS.fetch() - rootfs_path = self.scratch_file('rootfs.cpio') - lzma_uncompress(rootfs_path_xz, rootfs_path) + rootfs_path = self.uncompress(self.ASSET_ROOTFS) image_pow2ceil_expand(rootfs_path) self.vm.set_console() @@ -155,9 +150,7 @@ class BananaPiMachine(LinuxKernelTest): # This test download a 275 MiB compressed image and expand it # to 1036 MiB, but the underlying filesystem is 1552 MiB... # As we expand it to 2 GiB we are safe. - image_path_xz = self.ASSET_ARMBIAN.fetch() - image_path = self.scratch_file('armbian.img') - lzma_uncompress(image_path_xz, image_path) + image_path = self.uncompress(self.ASSET_ARMBIAN) image_pow2ceil_expand(image_path) self.vm.set_console() @@ -195,9 +188,7 @@ class BananaPiMachine(LinuxKernelTest): # before to boot NetBSD. uboot_path = 'usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' uboot_path = self.archive_extract(self.ASSET_UBOOT, member=uboot_path) - image_path_gz = self.ASSET_NETBSD.fetch() - image_path = self.scratch_file('armv7.img') - gzip_uncompress(image_path_gz, image_path) + image_path = self.uncompress(self.ASSET_NETBSD) image_pow2ceil_expand(image_path) image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/test_arm_raspi2.py index 5e38d1a937..d3c7aaa39b 100755 --- a/tests/functional/test_arm_raspi2.py +++ b/tests/functional/test_arm_raspi2.py @@ -9,7 +9,6 @@ from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from qemu_test.utils import gzip_uncompress class ArmRaspi2Machine(LinuxKernelTest): @@ -64,9 +63,7 @@ class ArmRaspi2Machine(LinuxKernelTest): member='boot/kernel7.img') dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, member='boot/bcm2709-rpi-2-b.dtb') - initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_INITRD) self.set_machine('raspi2b') self.vm.set_console() diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py index f4e86f7e2b..0fda45c63a 100755 --- a/tests/functional/test_arm_smdkc210.py +++ b/tests/functional/test_arm_smdkc210.py @@ -7,7 +7,7 @@ import os from qemu_test import LinuxKernelTest, Asset -from qemu_test.utils import gzip_uncompress + class Smdkc210Machine(LinuxKernelTest): @@ -30,9 +30,7 @@ class Smdkc210Machine(LinuxKernelTest): dtb_path = 'usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) - initrd_path_gz = self.ASSET_ROOTFS.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_ROOTFS) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py index 39fafb8bf1..a8da15a26b 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/test_mips64el_malta.py @@ -15,7 +15,6 @@ import logging from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest -from qemu_test.utils import gzip_uncompress class MaltaMachineConsole(LinuxKernelTest): @@ -67,9 +66,7 @@ class MaltaMachineConsole(LinuxKernelTest): @skipUntrustedTest() def test_mips64el_malta_5KEc_cpio(self): kernel_path = self.ASSET_KERNEL_3_19_3.fetch() - initrd_path_gz = self.ASSET_CPIO_R1.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_CPIO_R1) self.set_machine('malta') self.vm.set_console() @@ -119,9 +116,7 @@ class MaltaMachineFramebuffer(LinuxKernelTest): screendump_path = self.scratch_file('screendump.pbm') - kernel_path_gz = self.ASSET_KERNEL_4_7_0.fetch() - kernel_path = self.scratch_file("vmlinux") - gzip_uncompress(kernel_path_gz, kernel_path) + kernel_path = self.uncompress(self.ASSET_KERNEL_4_7_0) tuxlogo_path = self.ASSET_TUXLOGO.fetch() diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index 6ab6c0832a..3b15038d89 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -8,7 +8,6 @@ from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from qemu_test.utils import gzip_uncompress class MaltaMachineConsole(LinuxKernelTest): @@ -49,9 +48,7 @@ class MaltaMachineConsole(LinuxKernelTest): kernel_path = self.archive_extract( self.ASSET_KERNEL_4_5_0, member='boot/vmlinux-4.5.0-2-4kc-malta') - initrd_path_gz = self.ASSET_INITRD.fetch() - initrd_path = self.scratch_file('rootfs.cpio') - gzip_uncompress(initrd_path_gz, initrd_path) + initrd_path = self.uncompress(self.ASSET_INITRD) self.set_machine('malta') self.vm.set_console() diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/test_mipsel_malta.py index 1f44881c78..fe9c3a172e 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/test_mipsel_malta.py @@ -12,7 +12,6 @@ from qemu_test import QemuSystemTest, LinuxKernelTest, Asset from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern -from qemu_test.utils import lzma_uncompress class MaltaMachineConsole(LinuxKernelTest): @@ -33,9 +32,8 @@ class MaltaMachineConsole(LinuxKernelTest): 'generic_nano32r6el_page64k_dbg.xz'), 'ce21ff4b07a981ecb8a39db2876616f5a2473eb2ab459c6f67465b9914b0c6b6') - def do_test_mips_malta32el_nanomips(self, kernel_path_xz): - kernel_path = self.scratch_file('kernel') - lzma_uncompress(kernel_path_xz, kernel_path) + def do_test_mips_malta32el_nanomips(self, kernel): + kernel_path = self.uncompress(kernel) self.set_machine('malta') self.vm.set_console() @@ -51,16 +49,13 @@ class MaltaMachineConsole(LinuxKernelTest): self.wait_for_console_pattern(console_pattern) def test_mips_malta32el_nanomips_4k(self): - kernel_path_xz = self.ASSET_KERNEL_4K.fetch() - self.do_test_mips_malta32el_nanomips(kernel_path_xz) + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_4K) def test_mips_malta32el_nanomips_16k_up(self): - kernel_path_xz = self.ASSET_KERNEL_16K.fetch() - self.do_test_mips_malta32el_nanomips(kernel_path_xz) + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_16K) def test_mips_malta32el_nanomips_64k_dbg(self): - kernel_path_xz = self.ASSET_KERNEL_16K.fetch() - self.do_test_mips_malta32el_nanomips(kernel_path_xz) + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) class MaltaMachineYAMON(QemuSystemTest): diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py index b0adb38a45..20623aa51c 100755 --- a/tests/functional/test_rx_gdbsim.py +++ b/tests/functional/test_rx_gdbsim.py @@ -13,7 +13,6 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern, skipFlakyTest -from qemu_test.utils import gzip_uncompress class RxGdbSimMachine(QemuSystemTest): @@ -37,9 +36,7 @@ class RxGdbSimMachine(QemuSystemTest): """ self.set_machine('gdbsim-r5f562n8') - uboot_path_gz = self.ASSET_UBOOT.fetch() - uboot_path = self.scratch_file('u-boot.bin') - gzip_uncompress(uboot_path_gz, uboot_path) + uboot_path = self.uncompress(self.ASSET_UBOOT) self.vm.set_console() self.vm.add_args('-bios', uboot_path, diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/test_s390x_ccw_virtio.py index e5884a4dd0..453711aa0f 100755 --- a/tests/functional/test_s390x_ccw_virtio.py +++ b/tests/functional/test_s390x_ccw_virtio.py @@ -17,7 +17,7 @@ import tempfile from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test.utils import lzma_uncompress + class S390CCWVirtioMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' @@ -174,9 +174,7 @@ class S390CCWVirtioMachine(QemuSystemTest): kernel_path = self.ASSET_F31_KERNEL.fetch() - initrd_path_xz = self.ASSET_F31_INITRD.fetch() - initrd_path = self.scratch_file('initrd-raw.img') - lzma_uncompress(initrd_path_xz, initrd_path) + initrd_path = self.uncompress(self.ASSET_F31_INITRD, format="xz") self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 ' diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py index 82acff4e55..eefd9729cb 100755 --- a/tests/functional/test_s390x_topology.py +++ b/tests/functional/test_s390x_topology.py @@ -14,7 +14,6 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test.utils import lzma_uncompress class S390CPUTopology(QemuSystemTest): @@ -86,9 +85,7 @@ class S390CPUTopology(QemuSystemTest): """ self.require_accelerator("kvm") kernel_path = self.ASSET_F35_KERNEL.fetch() - initrd_path_xz = self.ASSET_F35_INITRD.fetch() - initrd_path = self.scratch_file('initrd-raw.img') - lzma_uncompress(initrd_path_xz, initrd_path) + initrd_path = self.uncompress(self.ASSET_F35_INITRD, format="xz") self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE From 3bb4c8b6134df5367675c4ade4f5c177d29fe903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:48 +0000 Subject: [PATCH 0429/2892] tests/functional: drop back compat imports from utils.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all tests are converted over to the higher level wrapper functions, the back compat imports from utils.py are redundant. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-28-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py index 6b87af4414..43853b4366 100644 --- a/tests/functional/qemu_test/utils.py +++ b/tests/functional/qemu_test/utils.py @@ -10,11 +10,6 @@ import os -from .archive import tar_extract as archive_extract -from .archive import cpio_extract -from .uncompress import gzip_uncompress -from .uncompress import lzma_uncompress - """ Round up to next power of 2 """ From 37e9b19c34d9500164e33ccf377e1830e956bca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:49 +0000 Subject: [PATCH 0430/2892] tests/functional: replace 'run_cmd' with subprocess helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'run_cmd' helper is re-implementing a convenient helper that already exists in the form of the 'run' and 'check_call' methods provided by 'subprocess'. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-29-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/archive.py | 9 ++++--- tests/functional/qemu_test/tesseract.py | 10 ++++---- tests/functional/qemu_test/testcase.py | 31 +++++++++++++----------- tests/functional/qemu_test/tuxruntest.py | 8 +++--- tests/functional/test_aarch64_virt.py | 6 +++-- tests/functional/test_ppc64_tuxrun.py | 7 ++++-- 6 files changed, 41 insertions(+), 30 deletions(-) diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py index c439d9413a..c803fdaf6d 100644 --- a/tests/functional/qemu_test/archive.py +++ b/tests/functional/qemu_test/archive.py @@ -14,7 +14,6 @@ from urllib.parse import urlparse import zipfile from .asset import Asset -from .cmd import run_cmd def tar_extract(archive, dest_dir, member=None): @@ -52,9 +51,11 @@ def deb_extract(archive, dest_dir, member=None): cwd = os.getcwd() os.chdir(dest_dir) try: - (stdout, stderr, ret) = run_cmd(['ar', 't', archive]) - file_path = stdout.split()[2] - run_cmd(['ar', 'x', archive, file_path]) + proc = run(['ar', 't', archive], + check=True, capture_output=True, encoding='utf8') + file_path = proc.stdout.split()[2] + check_call(['ar', 'x', archive, file_path], + stdout=DEVNULL, stderr=DEVNULL) tar_extract(file_path, dest_dir, member) finally: os.chdir(cwd) diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py index 1b7818090a..ede6c6501e 100644 --- a/tests/functional/qemu_test/tesseract.py +++ b/tests/functional/qemu_test/tesseract.py @@ -6,18 +6,18 @@ # later. See the COPYING file in the top-level directory. import logging +from subprocess import run -from . import run_cmd def tesseract_ocr(image_path, tesseract_args=''): console_logger = logging.getLogger('console') console_logger.debug(image_path) - (stdout, stderr, ret) = run_cmd(['tesseract', image_path, - 'stdout']) - if ret: + proc = run(['tesseract', image_path, 'stdout'], + capture_output=True, encoding='utf8') + if proc.returncode: return None lines = [] - for line in stdout.split('\n'): + for line in proc.stdout.split('\n'): sline = line.strip() if len(sline): console_logger.debug(sline) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index d0bb3141d5..aa6c9c0d64 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -16,7 +16,7 @@ import os from pathlib import Path import pycotap import shutil -import subprocess +from subprocess import run import sys import tempfile import unittest @@ -27,7 +27,6 @@ from qemu.utils import kvm_available, tcg_available from .archive import archive_extract from .asset import Asset -from .cmd import run_cmd from .config import BUILD_DIR from .uncompress import uncompress @@ -251,11 +250,11 @@ class QemuUserTest(QemuBaseTest): self._ldpath.append(os.path.abspath(ldpath)) def run_cmd(self, bin_path, args=[]): - return subprocess.run([self.qemu_bin] - + ["-L %s" % ldpath for ldpath in self._ldpath] - + [bin_path] - + args, - text=True, capture_output=True) + return run([self.qemu_bin] + + ["-L %s" % ldpath for ldpath in self._ldpath] + + [bin_path] + + args, + text=True, capture_output=True) class QemuSystemTest(QemuBaseTest): """Facilitates system emulation tests.""" @@ -282,7 +281,9 @@ class QemuSystemTest(QemuBaseTest): def set_machine(self, machinename): # TODO: We should use QMP to get the list of available machines if not self._machinehelp: - self._machinehelp = run_cmd([self.qemu_bin, '-M', 'help'])[0]; + self._machinehelp = run( + [self.qemu_bin, '-M', 'help'], + capture_output=True, check=True, encoding='utf8').stdout if self._machinehelp.find(machinename) < 0: self.skipTest('no support for machine ' + machinename) self.machine = machinename @@ -310,15 +311,17 @@ class QemuSystemTest(QemuBaseTest): "available" % accelerator) def require_netdev(self, netdevname): - netdevhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-netdev', 'help'])[0]; - if netdevhelp.find('\n' + netdevname + '\n') < 0: + help = run([self.qemu_bin, + '-M', 'none', '-netdev', 'help'], + capture_output=True, check=True, encoding='utf8').stdout; + if help.find('\n' + netdevname + '\n') < 0: self.skipTest('no support for " + netdevname + " networking') def require_device(self, devicename): - devhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-device', 'help'])[0]; - if devhelp.find(devicename) < 0: + help = run([self.qemu_bin, + '-M', 'none', '-device', 'help'], + capture_output=True, check=True, encoding='utf8').stdout; + if help.find(devicename) < 0: self.skipTest('no support for device ' + devicename) def _new_vm(self, name, *args): diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index 0b1bb8f0ed..7227a83757 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -11,11 +11,12 @@ import os import stat +from subprocess import check_call, DEVNULL from qemu_test import QemuSystemTest from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test import which, run_cmd, get_qemu_img +from qemu_test import which, get_qemu_img class TuxRunBaselineTest(QemuSystemTest): @@ -76,8 +77,9 @@ class TuxRunBaselineTest(QemuSystemTest): disk_image = self.scratch_file("rootfs.ext4") - run_cmd(['zstd', "-f", "-d", disk_image_zst, - "-o", disk_image]) + check_call(['zstd', "-f", "-d", disk_image_zst, + "-o", disk_image], + stdout=DEVNULL, stderr=DEVNULL) # zstd copies source archive permissions for the output # file, so must make this writable for QEMU os.chmod(disk_image, stat.S_IRUSR | stat.S_IWUSR) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index cc49f8963d..08576b0694 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -12,10 +12,11 @@ import time import logging +from subprocess import check_call, DEVNULL from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern -from qemu_test import get_qemu_img, run_cmd +from qemu_test import get_qemu_img class Aarch64VirtMachine(QemuSystemTest): @@ -96,7 +97,8 @@ class Aarch64VirtMachine(QemuSystemTest): logger.info('creating scratch qcow2 image') image_path = self.scratch_file('scratch.qcow2') qemu_img = get_qemu_img(self) - run_cmd([qemu_img, 'create', '-f', 'qcow2', image_path, '8M']) + check_call([qemu_img, 'create', '-f', 'qcow2', image_path, '8M'], + stdout=DEVNULL, stderr=DEVNULL) # Add the device self.vm.add_args('-blockdev', diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/test_ppc64_tuxrun.py index 03b47e07f2..8a98d18ab3 100755 --- a/tests/functional/test_ppc64_tuxrun.py +++ b/tests/functional/test_ppc64_tuxrun.py @@ -11,9 +11,10 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +from subprocess import check_call, DEVNULL import tempfile -from qemu_test import run_cmd, Asset +from qemu_test import Asset from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunPPC64Test(TuxRunBaselineTest): @@ -70,7 +71,9 @@ class TuxRunPPC64Test(TuxRunBaselineTest): # Create a temporary qcow2 and launch the test-case with tempfile.NamedTemporaryFile(prefix=prefix, suffix='.qcow2') as qcow2: - run_cmd([self.qemu_img, 'create', '-f', 'qcow2', qcow2.name, ' 1G']) + check_call([self.qemu_img, 'create', '-f', 'qcow2', + qcow2.name, ' 1G'], + stdout=DEVNULL, stderr=DEVNULL) self.vm.add_args('-drive', 'file=' + qcow2.name + ',format=qcow2,if=none,id=' From c5be9dd32f35d049983cc42c3835cc3d8167f13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:50 +0000 Subject: [PATCH 0431/2892] tests/functional: remove now unused 'run_cmd' helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All usage has been replaced by direct 'subprocess' helpers. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-30-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/cmd.py | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 3bd043e608..da1830286d 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -8,7 +8,7 @@ from .asset import Asset from .config import BUILD_DIR -from .cmd import run_cmd, is_readable_executable_file, \ +from .cmd import is_readable_executable_file, \ interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index c8971de00a..dc5f422b77 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -14,7 +14,6 @@ import logging import os import os.path -import subprocess def which(tool): @@ -28,16 +27,6 @@ def which(tool): return p return None -def run_cmd(args): - subp = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - stdout, stderr = subp.communicate() - ret = subp.returncode - - return (stdout, stderr, ret) - def is_readable_executable_file(path): return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) From 6ff217c2d1c231f727c1be356da4a71cdfdd7ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:51 +0000 Subject: [PATCH 0432/2892] tests/functional: skip tests if assets are not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If downloading of assets has been disabled, then skip running a test if the assets it has registered are not already downloaded. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-31-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 8 +++++++- tests/functional/qemu_test/testcase.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index c5d3e73c4b..39832b2587 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -65,6 +65,12 @@ class Asset: def valid(self): return self.cache_file.exists() and self._check(self.cache_file) + def fetchable(self): + return not os.environ.get("QEMU_TEST_NO_DOWNLOAD", False) + + def available(self): + return self.valid() or self.fetchable() + def _wait_for_other_download(self, tmp_cache_file): # Another thread already seems to download the asset, so wait until # it is done, while also checking the size to see whether it is stuck @@ -103,7 +109,7 @@ class Asset: self.cache_file, self.url) return str(self.cache_file) - if os.environ.get("QEMU_TEST_NO_DOWNLOAD", False): + if not self.fetchable(): raise Exception("Asset cache is invalid and downloads disabled") self.log.info("Downloading %s to %s...", self.url, self.cache_file) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index aa6c9c0d64..869f3949fe 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -184,6 +184,14 @@ class QemuBaseTest(unittest.TestCase): def log_file(self, *args): return str(Path(self.outputdir, *args)) + def assets_available(self): + for name, asset in vars(self.__class__).items(): + if name.startswith("ASSET_") and type(asset) == Asset: + if not asset.available(): + self.log.debug(f"Asset {asset.url} not available") + return False + return True + def setUp(self, bin_prefix): self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') self.arch = self.qemu_bin.split('-')[-1] @@ -209,6 +217,9 @@ class QemuBaseTest(unittest.TestCase): self.machinelog.setLevel(logging.DEBUG) self.machinelog.addHandler(self._log_fh) + if not self.assets_available(): + self.skipTest('One or more assets is not available') + def tearDown(self): if "QEMU_TEST_KEEP_SCRATCH" not in os.environ: shutil.rmtree(self.workdir) From 674a750b51df188d6baf55f1f1f8fc6f57fcf6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:52 +0000 Subject: [PATCH 0433/2892] tests/functional: ignore errors when caching assets, except for 404 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We see periodic errors caching assets due to a combination of transient networking and server problems. With the previous patch to skip running a test when it has missing assets, we can now treat most cache download errors as non-fatal. Only HTTP 404 is retained as fatal, since it is a strong indicator of a fully broken test rather than a transient error. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-32-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 39832b2587..f0730695f0 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -15,6 +15,7 @@ import urllib.request from time import sleep from pathlib import Path from shutil import copyfileobj +from urllib.error import HTTPError # Instances of this class must be declared as class level variables @@ -170,7 +171,18 @@ class Asset: for name, asset in vars(test.__class__).items(): if name.startswith("ASSET_") and type(asset) == Asset: log.info("Attempting to cache '%s'" % asset) - asset.fetch() + try: + asset.fetch() + except HTTPError as e: + # Treat 404 as fatal, since it is highly likely to + # indicate a broken test rather than a transient + # server or networking problem + if e.code == 404: + raise + + log.debug(f"HTTP error {e.code} from {asset.url} " + + "skipping asset precache") + log.removeHandler(handler) def precache_suite(suite): From 23cb2e9961953bb59a010115eda5e267f46352b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Dec 2024 15:59:53 +0000 Subject: [PATCH 0434/2892] MAINTAINERS: add myself as reviewer for functional test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel P. Berrangé Message-ID: <20241217155953.3950506-33-berrange@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 822f34344b..54201da578 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4157,6 +4157,7 @@ W: https://cirrus-ci.com/github/qemu/qemu Functional testing framework M: Thomas Huth R: Philippe Mathieu-Daudé +R: Daniel P. Berrange F: tests/functional/qemu_test/ Windows Hosted Continuous Integration From 799d683026e1f1b18380c2629f6c882f4c6c568b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 4 Dec 2024 15:21:36 +0100 Subject: [PATCH 0435/2892] tests/functional: Convert the quanta-gsj avocado test Straight forward conversion, basically just the hashsums needed to be updated to sha256 now. Message-ID: <20241206102358.1186644-6-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/boot_linux_console.py | 86 ---------------------- tests/functional/meson.build | 2 + tests/functional/test_arm_quanta_gsj.py | 94 +++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 86 deletions(-) create mode 100755 tests/functional/test_arm_quanta_gsj.py diff --git a/MAINTAINERS b/MAINTAINERS index 54201da578..1e2f71e3b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -872,6 +872,7 @@ F: tests/qtest/adm1266-test.c F: pc-bios/npcm7xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst +F: tests/functional/test_arm_quanta_gsj.py Raspberry Pi M: Peter Maydell diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 268b40ca31..4bd1465ba3 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -115,89 +115,3 @@ class BootLinuxConsole(LinuxKernelTest): self.vm.launch() console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - - @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') - def test_arm_quanta_gsj(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:quanta-gsj - :avocado: tags=accel:tcg - """ - # 25 MiB compressed, 32 MiB uncompressed. - image_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz') - image_hash = '14895e634923345cb5c8776037ff7876df96f6b1' - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) - image_name = 'obmc.mtd' - image_path = os.path.join(self.workdir, image_name) - archive.gzip_uncompress(image_path_gz, image_path) - - self.vm.set_console() - drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' - self.vm.add_args('-drive', drive_args) - self.vm.launch() - - # Disable drivers and services that stall for a long time during boot, - # to avoid running past the 90-second timeout. These may be removed - # as the corresponding device support is added. - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + ( - 'console=${console} ' - 'mem=${mem} ' - 'initcall_blacklist=npcm_i2c_bus_driver_init ' - 'systemd.mask=systemd-random-seed.service ' - 'systemd.mask=dropbearkey.service ' - ) - - self.wait_for_console_pattern('> BootBlock by Nuvoton') - self.wait_for_console_pattern('>Device: Poleg BMC NPCM730') - self.wait_for_console_pattern('>Skip DDR init.') - self.wait_for_console_pattern('U-Boot ') - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', 'U-Boot>') - exec_command_and_wait_for_pattern( - self, "setenv bootargs ${bootargs} " + kernel_command_line, - 'U-Boot>') - exec_command_and_wait_for_pattern( - self, 'run romboot', 'Booting Kernel from flash') - self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') - self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') - self.wait_for_console_pattern('OpenBMC Project Reference Distro') - self.wait_for_console_pattern('gsj login:') - - def test_arm_quanta_gsj_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:quanta-gsj - :avocado: tags=accel:tcg - """ - initrd_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz') - initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - kernel_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/uImage-gsj.bin') - kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - dtb_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb') - dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4' - dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart8250,mmio32,0xf0001000') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - - self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') - self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') - self.wait_for_console_pattern( - 'Give root password for system maintenance') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 1bc5ba5229..f0326788f0 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -27,6 +27,7 @@ test_timeouts = { 'arm_collie' : 180, 'arm_cubieboard' : 360, 'arm_orangepi' : 540, + 'arm_quanta_gsj' : 240, 'arm_raspi2' : 120, 'arm_tuxrun' : 240, 'arm_sx1' : 360, @@ -85,6 +86,7 @@ tests_arm_system_thorough = [ 'arm_emcraft_sf2', 'arm_integratorcp', 'arm_orangepi', + 'arm_quanta_gsj', 'arm_raspi2', 'arm_smdkc210', 'arm_sx1', diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py new file mode 100755 index 0000000000..7aa5209bea --- /dev/null +++ b/tests/functional/test_arm_quanta_gsj.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from unittest import skipUnless + +class EmcraftSf2Machine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz'), + 'eccd4e375cde53034c84aece5c511932cacf838d9fd3f63da368a511757da72b') + + ASSET_INITRD = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz'), + '37b05009fc54db1434beac12bd7ff99a2e751a2f032ee18d9042f991dd0cdeaa') + + ASSET_KERNEL = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/uImage-gsj.bin'), + 'ce6d6b37bff46c74fc7b1e90da10a431cc37a62cdb35ec199fa73473d0790110') + + ASSET_DTB = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb'), + '3249b2da787d4b9ad4e61f315b160abfceb87b5e1895a7ce898ce7f40c8d4045') + + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') + def test_arm_quanta_gsj(self): + self.set_machine('quanta-gsj') + image_path = self.uncompress(ASSET_IMAGE, 'obmc.mtd', format='gz') + + self.vm.set_console() + drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' + self.vm.add_args('-drive', drive_args) + self.vm.launch() + + # Disable drivers and services that stall for a long time during boot, + # to avoid running past the 90-second timeout. These may be removed + # as the corresponding device support is added. + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + ( + 'console=${console} ' + 'mem=${mem} ' + 'initcall_blacklist=npcm_i2c_bus_driver_init ' + 'systemd.mask=systemd-random-seed.service ' + 'systemd.mask=dropbearkey.service ' + ) + + self.wait_for_console_pattern('> BootBlock by Nuvoton') + self.wait_for_console_pattern('>Device: Poleg BMC NPCM730') + self.wait_for_console_pattern('>Skip DDR init.') + self.wait_for_console_pattern('U-Boot ') + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', 'U-Boot>') + exec_command_and_wait_for_pattern( + self, "setenv bootargs ${bootargs} " + kernel_command_line, + 'U-Boot>') + exec_command_and_wait_for_pattern( + self, 'run romboot', 'Booting Kernel from flash') + self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') + self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') + self.wait_for_console_pattern('OpenBMC Project Reference Distro') + self.wait_for_console_pattern('gsj login:') + + def test_arm_quanta_gsj_initrd(self): + self.set_machine('quanta-gsj') + initrd_path = self.ASSET_INITRD.fetch() + kernel_path = self.ASSET_KERNEL.fetch() + dtb_path = self.ASSET_DTB.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart8250,mmio32,0xf0001000') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + + self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') + self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') + self.wait_for_console_pattern( + 'Give root password for system maintenance') + +if __name__ == '__main__': + LinuxKernelTest.main() From a44b318fc45d59c0904c887957fb24421bf4ddd4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 5 Dec 2024 17:28:07 +0100 Subject: [PATCH 0436/2892] tests/functional: Convert the arm virt avocado test Straight forward conversion, basically just the hashsums needed to be updated to sha256 now. Message-ID: <20241206102358.1186644-7-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/boot_linux_console.py | 21 -------------------- tests/functional/meson.build | 1 + tests/functional/test_arm_virt.py | 30 +++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 21 deletions(-) create mode 100755 tests/functional/test_arm_virt.py diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 4bd1465ba3..c15f39ae1f 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -94,24 +94,3 @@ class BootLinuxConsole(LinuxKernelTest): self.vm.launch() console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index f0326788f0..ebb26d7044 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -91,6 +91,7 @@ tests_arm_system_thorough = [ 'arm_smdkc210', 'arm_sx1', 'arm_vexpress', + 'arm_virt', 'arm_tuxrun', ] diff --git a/tests/functional/test_arm_virt.py b/tests/functional/test_arm_virt.py new file mode 100755 index 0000000000..7b6549176f --- /dev/null +++ b/tests/functional/test_arm_virt.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class ArmVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_arm_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() From 9fa4fc23e34114971f7ee81acb9b5f4ab0017c7a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 13 Dec 2024 17:02:59 +0100 Subject: [PATCH 0437/2892] tests/functional: Add a helper function for retrieving the hostfwd port It's just a wrapper around get_info_usernet_hostfwd_port from the qemu module that is also calling the right monitor command for retrieving the information from QEMU. Message-ID: <20241217121550.141072-2-thuth@redhat.com> Acked-by: Michael S. Tsirkin Signed-off-by: Thomas Huth --- tests/functional/qemu_test/utils.py | 7 +++++++ tests/functional/test_info_usernet.py | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py index 43853b4366..e7c8de8165 100644 --- a/tests/functional/qemu_test/utils.py +++ b/tests/functional/qemu_test/utils.py @@ -10,6 +10,13 @@ import os +from qemu.utils import get_info_usernet_hostfwd_port + + +def get_usernet_hostfwd_port(vm): + res = vm.cmd('human-monitor-command', command_line='info usernet') + return get_info_usernet_hostfwd_port(res) + """ Round up to next power of 2 """ diff --git a/tests/functional/test_info_usernet.py b/tests/functional/test_info_usernet.py index cd37524d94..e8cbc37eed 100755 --- a/tests/functional/test_info_usernet.py +++ b/tests/functional/test_info_usernet.py @@ -11,8 +11,7 @@ # later. See the COPYING file in the top-level directory. from qemu_test import QemuSystemTest - -from qemu.utils import get_info_usernet_hostfwd_port +from qemu_test.utils import get_usernet_hostfwd_port class InfoUsernet(QemuSystemTest): @@ -22,9 +21,8 @@ class InfoUsernet(QemuSystemTest): self.set_machine('none') self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22') self.vm.launch() - res = self.vm.cmd('human-monitor-command', - command_line='info usernet') - port = get_info_usernet_hostfwd_port(res) + + port = get_usernet_hostfwd_port(self.vm) self.assertIsNotNone(port, ('"info usernet" output content does not seem to ' 'contain the redirected port')) From 270d4a5164ed9881ee7b9e8190a24e37af82a7e1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Dec 2024 19:02:24 +0100 Subject: [PATCH 0438/2892] tests/functional: Convert the intel_iommu avocado test Convert the intel_iommu test to the new functional framework. This test needs some changes since we neither support the old 'LinuxTest' class in the functional framework yet, nor a way to use SSH for running commands in the guest. So we now directly download a Fedora kernel and initrd and set up the serial console for executing the commands and for looking for the results. Instead of configuring the cloud image via cloud-init, we now simply mount the file system manually from an initrd rescue shell. While the old test was exercising the network with a "dnf install" command (which is not the best option for the CI since this depends on third party servers), the new code is now setting up a little HTTP server in the guest and transfers a file from the guest to the host instead. The test should now run much faster and more reliable (since we don't depend on the third party servers for "dnf install" anymore), so we can also drop the @skipUnless decorator now. Message-ID: <20241217121550.141072-3-thuth@redhat.com> Acked-by: Michael S. Tsirkin Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/intel_iommu.py | 122 ------------------- tests/functional/meson.build | 4 +- tests/functional/test_intel_iommu.py | 175 +++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 123 deletions(-) delete mode 100644 tests/avocado/intel_iommu.py create mode 100755 tests/functional/test_intel_iommu.py diff --git a/MAINTAINERS b/MAINTAINERS index 1e2f71e3b8..430a0f4f8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3682,6 +3682,7 @@ S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h F: include/hw/i386/intel_iommu.h +F: tests/functional/test_intel_iommu.py AMD-Vi Emulation S: Orphan diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py deleted file mode 100644 index 992583fa7d..0000000000 --- a/tests/avocado/intel_iommu.py +++ /dev/null @@ -1,122 +0,0 @@ -# INTEL_IOMMU Functional tests -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Eric Auger -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. -import os - -from avocado import skipUnless -from avocado_qemu.linuxtest import LinuxTest - -@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') -class IntelIOMMU(LinuxTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=distro:fedora - :avocado: tags=distro_version:31 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - :avocado: tags=intel_iommu - :avocado: tags=flaky - """ - - IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' - kernel_path = None - initrd_path = None - kernel_params = None - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + - 'drive=drv0,id=virtio-disk0,bootindex=1,' - 'werror=stop,rerror=stop' + self.IOMMU_ADDON) - self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON) - self.vm.add_args('-drive', - 'file=%s,if=none,cache=writethrough,id=drv0' % path) - - def setUp(self): - super(IntelIOMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON) - - def add_common_args(self): - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', - 'rng-random,id=rng0,filename=/dev/urandom') - - def common_vm_setup(self, custom_kernel=None): - self.require_accelerator("kvm") - self.add_common_args() - self.vm.add_args("-accel", "kvm") - - if custom_kernel is None: - return - - kernel_url = self.distro.pxeboot_url + 'vmlinuz' - kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c' - initrd_url = self.distro.pxeboot_url + 'initrd.img' - initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1' - self.kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - def run_and_check(self): - if self.kernel_path: - self.vm.add_args('-kernel', self.kernel_path, - '-append', self.kernel_params, - '-initrd', self.initrd_path) - self.launch_and_wait() - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dmesg | grep -e DMAR -e IOMMU') - self.ssh_command('find /sys/kernel/iommu_groups/ -type l') - self.ssh_command('dnf -y install numactl-devel') - - def test_intel_iommu(self): - """ - :avocado: tags=intel_iommu_intremap - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on') - self.run_and_check() - - def test_intel_iommu_strict(self): - """ - :avocado: tags=intel_iommu_strict - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on,strict') - self.run_and_check() - - def test_intel_iommu_strict_cm(self): - """ - :avocado: tags=intel_iommu_strict_cm - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on,strict') - self.run_and_check() - - def test_intel_iommu_pt(self): - """ - :avocado: tags=intel_iommu_pt - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on iommu=pt') - self.run_and_check() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index ebb26d7044..d03fe0ca36 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -31,6 +31,7 @@ test_timeouts = { 'arm_raspi2' : 120, 'arm_tuxrun' : 240, 'arm_sx1' : 360, + 'intel_iommu': 300, 'mips_malta' : 120, 'netdev_ethtool' : 180, 'ppc_40p' : 240, @@ -227,11 +228,12 @@ tests_x86_64_system_quick = [ tests_x86_64_system_thorough = [ 'acpi_bits', - 'x86_64_tuxrun', + 'intel_iommu', 'linux_initrd', 'multiprocess', 'netdev_ethtool', 'virtio_gpu', + 'x86_64_tuxrun', ] tests_xtensa_system_thorough = [ diff --git a/tests/functional/test_intel_iommu.py b/tests/functional/test_intel_iommu.py new file mode 100755 index 0000000000..a9e8f82ab5 --- /dev/null +++ b/tests/functional/test_intel_iommu.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# +# INTEL_IOMMU Functional tests +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Eric Auger +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import hashlib +import urllib.request + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test.utils import get_usernet_hostfwd_port + + +class IntelIOMMU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' + 'quiet rd.rescue ') + GUEST_PORT = 8080 + IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' + kernel_path = None + initrd_path = None + kernel_params = None + + def add_common_args(self, path): + self.vm.add_args('-drive', f'file={path},if=none,id=drv0,snapshot=on') + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1,' + 'werror=stop,rerror=stop' + self.IOMMU_ADDON) + self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON) + + self.vm.add_args('-netdev', + 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % + self.GUEST_PORT) + self.vm.add_args('-device', + 'virtio-net-pci,netdev=n1' + self.IOMMU_ADDON) + + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', + 'rng-random,id=rng0,filename=/dev/urandom') + self.vm.add_args("-m", "1G") + self.vm.add_args("-accel", "kvm") + + def common_vm_setup(self): + self.set_machine('q35') + self.require_accelerator("kvm") + self.require_netdev('user') + + self.kernel_path = self.ASSET_KERNEL.fetch() + self.initrd_path = self.ASSET_INITRD.fetch() + image_path = self.ASSET_DISKIMAGE.fetch() + self.add_common_args(image_path) + self.kernel_params = self.DEFAULT_KERNEL_PARAMS + + def run_and_check(self): + if self.kernel_path: + self.vm.add_args('-kernel', self.kernel_path, + '-append', self.kernel_params, + '-initrd', self.initrd_path) + self.vm.set_console() + self.vm.launch() + self.wait_for_console_pattern('Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + # Copy a file (checked later), umount afterwards to drop disk cache: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + filename = '/boot/initramfs-5.3.7-301.fc31.x86_64.img' + exec_command_and_wait_for_pattern(self, (f'cp /sysroot{filename}' + ' /sysroot/root/data'), + prompt) + exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) + + # Switch from initrd to the cloud image filesystem: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, + ('for d in dev proc sys run ; do ' + 'mount -o bind /$d /sysroot/$d ; done'), prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) + + # Checking for IOMMU enablement: + self.log.info("Checking whether IOMMU has been enabled...") + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', + 'intel_iommu=on') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'dmesg | grep DMAR:', + 'IOMMU enabled') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'find /sys/kernel/iommu_groups/ -type l', + 'devices/0000:00:') + self.wait_for_console_pattern(prompt) + + # Check hard disk device via sha256sum: + self.log.info("Checking hard disk...") + hashsum = '0dc7472f879be70b2f3daae279e3ae47175ffe249691e7d97f47222b65b8a720' + exec_command_and_wait_for_pattern(self, 'sha256sum ' + filename, + hashsum) + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'sha256sum /root/data', + hashsum) + self.wait_for_console_pattern(prompt) + + # Check virtio-net via HTTP: + exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) + exec_command_and_wait_for_pattern(self, + f'python3 -m http.server {self.GUEST_PORT} & sleep 1', + f'Serving HTTP on 0.0.0.0 port {self.GUEST_PORT}') + hl = hashlib.sha256() + hostport = get_usernet_hostfwd_port(self.vm) + url = f'http://localhost:{hostport}{filename}' + self.log.info(f'Downloading {url} ...') + with urllib.request.urlopen(url) as response: + while True: + chunk = response.read(1 << 20) + if not chunk: + break + hl.update(chunk) + + digest = hl.hexdigest() + self.log.info(f'sha256sum of download is {digest}.') + self.assertEqual(digest, hashsum) + + def test_intel_iommu(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on' + self.run_and_check() + + def test_intel_iommu_strict(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on,strict' + self.run_and_check() + + def test_intel_iommu_strict_cm(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on,strict' + self.run_and_check() + + def test_intel_iommu_pt(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on iommu=pt' + self.run_and_check() + +if __name__ == '__main__': + LinuxKernelTest.main() From bf850896961fe228a79044c79e97f8aac464a16f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 17 Dec 2024 14:52:45 +0100 Subject: [PATCH 0439/2892] tests/functional: Convert the hotplug_cpu avocado test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we don't have ssh support in the functional test framework yet, simply use the serial console for this test instead. It's also sufficient to only boot into an initrd here, no need to fire up a full-blown guest, so the test now finishes much faster. While we're at it, also unplug the CPU now and check that it is gone in the guest. Message-ID: <20241217142020.155776-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- tests/avocado/hotplug_cpu.py | 37 ----------- tests/functional/meson.build | 1 + tests/functional/test_x86_64_hotplug_cpu.py | 69 +++++++++++++++++++++ 3 files changed, 70 insertions(+), 37 deletions(-) delete mode 100644 tests/avocado/hotplug_cpu.py create mode 100755 tests/functional/test_x86_64_hotplug_cpu.py diff --git a/tests/avocado/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py deleted file mode 100644 index 342c838539..0000000000 --- a/tests/avocado/hotplug_cpu.py +++ /dev/null @@ -1,37 +0,0 @@ -# Functional test that hotplugs a CPU and checks it on a Linux guest -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu.linuxtest import LinuxTest - - -class HotPlugCPU(LinuxTest): - - def test(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - """ - self.require_accelerator('kvm') - self.vm.add_args('-accel', 'kvm') - self.vm.add_args('-cpu', 'Haswell') - self.vm.add_args('-smp', '1,sockets=1,cores=2,threads=1,maxcpus=2') - self.launch_and_wait() - - self.ssh_command('test -e /sys/devices/system/cpu/cpu0') - with self.assertRaises(AssertionError): - self.ssh_command('test -e /sys/devices/system/cpu/cpu1') - - self.vm.cmd('device_add', - driver='Haswell-x86_64-cpu', - socket_id=0, - core_id=1, - thread_id=0) - self.ssh_command('test -e /sys/devices/system/cpu/cpu1') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index d03fe0ca36..24f7f8f2f1 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -233,6 +233,7 @@ tests_x86_64_system_thorough = [ 'multiprocess', 'netdev_ethtool', 'virtio_gpu', + 'x86_64_hotplug_cpu', 'x86_64_tuxrun', ] diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/test_x86_64_hotplug_cpu.py new file mode 100755 index 0000000000..b1d5156c72 --- /dev/null +++ b/tests/functional/test_x86_64_hotplug_cpu.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Functional test that hotplugs a CPU and checks it on a Linux guest +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern + + +class HotPlugCPU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + def test_hotplug(self): + + self.require_accelerator('kvm') + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-cpu', 'Haswell') + self.vm.add_args('-smp', '1,sockets=1,cores=2,threads=1,maxcpus=2') + self.vm.add_args('-m', '1G') + self.vm.add_args('-append', 'console=ttyS0 rd.rescue') + + self.launch_kernel(self.ASSET_KERNEL.fetch(), + self.ASSET_INITRD.fetch(), + wait_for='Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu0', + 'cpu0#') + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu1', + 'No such file or directory') + + self.vm.cmd('device_add', + driver='Haswell-x86_64-cpu', + id='c1', + socket_id=0, + core_id=1, + thread_id=0) + self.wait_for_console_pattern('CPU1 has been hot-added') + + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu1', + 'cpu1#') + + self.vm.cmd('device_del', id='c1') + + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu1', + 'No such file or directory') + +if __name__ == '__main__': + LinuxKernelTest.main() From e2d98f257138b83b6a492d1da5847a7fe0930d10 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Dec 2024 07:21:59 +0100 Subject: [PATCH 0440/2892] meson.build: Disallow libnfs v6 to fix the broken macOS build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The macOS builds in our CI (and possibly other very recent distros) are currently broken since the update to libnfs version 6 there. That version apparently comes with a big API breakage. v5.0.3 was the final release of the old API (see the libnfs commit here: https://github.com/sahlberg/libnfs/commit/4379837 ). Disallow version 6.x for now to get the broken CI job working again. Once somebody had enough time to adapt our code in block/nfs.c, we can revert this change again. Message-ID: <20241218065157.209020-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 85f7485473..6149b50db2 100644 --- a/meson.build +++ b/meson.build @@ -1145,7 +1145,7 @@ endif libnfs = not_found if not get_option('libnfs').auto() or have_block - libnfs = dependency('libnfs', version: '>=1.9.3', + libnfs = dependency('libnfs', version: ['>=1.9.3', '<6.0.0'], required: get_option('libnfs'), method: 'pkg-config') endif From 56ef123777b7a29f5b813696a69865d6033c3c78 Mon Sep 17 00:00:00 2001 From: Jean-Louis Dupond Date: Wed, 2 Oct 2024 12:06:35 +0200 Subject: [PATCH 0441/2892] qga: skip bind mounts in fs list The filesystem list in build_fs_mount_list should skip bind mounts. This because we end up in locking situations when doing fsFreeze. Like mentioned in [1] and [2]. Next to that, the build_fs_mount_list call did a fallback via build_fs_mount_list_from_mtab if mountinfo did not exist. There it skipped bind mounts, but this is broken for newer OS. This as mounts does not return the path of the bind mount but the underlying dev/partition, so S_ISDIR will never return true in dev_major_minor call. This patch simply checks the existing devmajor:devminor tuple in the mounts, and if it already exists, this means we have the same devices mounted again, a bind mount. So skip this. Same approach is used in open-vm-tools [3]. [1]: https://gitlab.com/qemu-project/qemu/-/issues/592 [2]: https://gitlab.com/qemu-project/qemu/-/issues/520 [3]: https://github.com/vmware/open-vm-tools/commit/d58847b497e212737007958c945af1df22a8ab58 Signed-off-by: Jean-Louis Dupond Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20241002100634.162499-2-jean-louis@dupond.be Signed-off-by: Konstantin Kostiuk --- qga/commands-linux.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index cf077eb03d..9e8a934b9a 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -58,6 +58,22 @@ static int dev_major_minor(const char *devpath, return -1; } +/* + * Check if we already have the devmajor:devminor in the mounts + * If thats the case return true. + */ +static bool dev_exists(FsMountList *mounts, unsigned int devmajor, unsigned int devminor) +{ + FsMount *mount; + + QTAILQ_FOREACH(mount, mounts, next) { + if (mount->devmajor == devmajor && mount->devminor == devminor) { + return true; + } + } + return false; +} + static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) { struct mntent *ment; @@ -88,6 +104,10 @@ static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) /* Skip bind mounts */ continue; } + if (dev_exists(mounts, devmajor, devminor)) { + /* Skip already existing devices (bind mounts) */ + continue; + } mount = g_new0(FsMount, 1); mount->dirname = g_strdup(ment->mnt_dir); @@ -171,6 +191,11 @@ bool build_fs_mount_list(FsMountList *mounts, Error **errp) } } + if (dev_exists(mounts, devmajor, devminor)) { + /* Skip already existing devices (bind mounts) */ + continue; + } + mount = g_new0(FsMount, 1); mount->dirname = g_strdup(line + dir_s); mount->devtype = g_strdup(dash + type_s); From 5788e9602416d24533a19d24b087d56a75119c36 Mon Sep 17 00:00:00 2001 From: Dehan Meng Date: Tue, 10 Dec 2024 13:46:16 +0800 Subject: [PATCH 0442/2892] qemu-ga-win: Fix a typo error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a typo error for api 'guest-get-osinfo', the win2025's version in WIN_10_0_SERVER_VERSION_MATRIX should be adjusted. Signed-off-by: Dehan Meng Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20241210054616.260386-1-demeng@redhat.com Signed-off-by: Konstantin Kostiuk --- qga/commands-win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 038beb8cfa..99c026c0a0 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -2088,7 +2088,7 @@ static const ga_win_10_0_t WIN_10_0_SERVER_VERSION_MATRIX[] = { {14393, "Microsoft Windows Server 2016", "2016"}, {17763, "Microsoft Windows Server 2019", "2019"}, {20344, "Microsoft Windows Server 2022", "2022"}, - {26040, "MIcrosoft Windows Server 2025", "2025"}, + {26040, "Microsoft Windows Server 2025", "2025"}, { } }; From 2657a92b5479c8705b128ed1e55feb8960ed498a Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Thu, 5 Dec 2024 17:18:45 +0100 Subject: [PATCH 0443/2892] qga: Don't access global variable in run_agent_once() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The run_agent_once() function is already given GAState via an argument. There's no need to access the global ga_state variable which points to the argument anyways (thanks to initialize_agent()). Worse, some parts of the function use the argument and the other use the global variable. Stick with the function argument. Signed-off-by: Michal Privoznik Reviewed-by: Ján Tomko Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/8ae7f5d5032b14a5b956fe8aaf47bae5ca401699.1733414906.git.mprivozn@redhat.com Signed-off-by: Konstantin Kostiuk --- qga/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index 50186760bf..4a695235f0 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1519,7 +1519,7 @@ static int run_agent_once(GAState *s) return EXIT_FAILURE; } - g_main_loop_run(ga_state->main_loop); + g_main_loop_run(s->main_loop); if (s->channel) { ga_channel_free(s->channel); From deeca9cb0ba8d8c85e2b8eeb778b35dd0b806d8c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 14 Sep 2024 17:34:06 +0800 Subject: [PATCH 0444/2892] include: Add loongarch_pic_common header file Add common header file hw/intc/loongarch_pic_common.h, and move some macro definition from hw/intc/loongarch_pch_pic.h to the common header file. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- include/hw/intc/loongarch_pch_pic.h | 36 +++------------------- include/hw/intc/loongarch_pic_common.h | 42 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 31 deletions(-) create mode 100644 include/hw/intc/loongarch_pic_common.h diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index d5437e88f2..861b32313b 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -5,42 +5,15 @@ * Copyright (c) 2021 Loongson Technology Corporation Limited */ -#include "hw/sysbus.h" +#ifndef HW_LOONGARCH_PCH_PIC_H +#define HW_LOONGARCH_PCH_PIC_H + +#include "hw/intc/loongarch_pic_common.h" #define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" #define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC) -#define PCH_PIC_INT_ID_VAL 0x7000000UL -#define PCH_PIC_INT_ID_VER 0x1UL - -#define PCH_PIC_INT_ID_LO 0x00 -#define PCH_PIC_INT_ID_HI 0x04 -#define PCH_PIC_INT_MASK_LO 0x20 -#define PCH_PIC_INT_MASK_HI 0x24 -#define PCH_PIC_HTMSI_EN_LO 0x40 -#define PCH_PIC_HTMSI_EN_HI 0x44 -#define PCH_PIC_INT_EDGE_LO 0x60 -#define PCH_PIC_INT_EDGE_HI 0x64 -#define PCH_PIC_INT_CLEAR_LO 0x80 -#define PCH_PIC_INT_CLEAR_HI 0x84 -#define PCH_PIC_AUTO_CTRL0_LO 0xc0 -#define PCH_PIC_AUTO_CTRL0_HI 0xc4 -#define PCH_PIC_AUTO_CTRL1_LO 0xe0 -#define PCH_PIC_AUTO_CTRL1_HI 0xe4 -#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 -#define PCH_PIC_ROUTE_ENTRY_END 0x13f -#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 -#define PCH_PIC_HTMSI_VEC_END 0x23f -#define PCH_PIC_INT_STATUS_LO 0x3a0 -#define PCH_PIC_INT_STATUS_HI 0x3a4 -#define PCH_PIC_INT_POL_LO 0x3e0 -#define PCH_PIC_INT_POL_HI 0x3e4 - -#define STATUS_LO_START 0 -#define STATUS_HI_START 0x4 -#define POL_LO_START 0x40 -#define POL_HI_START 0x44 struct LoongArchPCHPIC { SysBusDevice parent_obj; qemu_irq parent_irq[64]; @@ -67,3 +40,4 @@ struct LoongArchPCHPIC { MemoryRegion iomem8; unsigned int irq_num; }; +#endif /* HW_LOONGARCH_PCH_PIC_H */ diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h new file mode 100644 index 0000000000..6d0e33e1ac --- /dev/null +++ b/include/hw/intc/loongarch_pic_common.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch 7A1000 I/O interrupt controller definitions + * Copyright (c) 2024 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_PIC_COMMON_H +#define HW_LOONGARCH_PIC_COMMON_H + +#include "hw/pci-host/ls7a.h" +#include "hw/sysbus.h" + +#define PCH_PIC_INT_ID_VAL 0x7000000UL +#define PCH_PIC_INT_ID_VER 0x1UL +#define PCH_PIC_INT_ID_LO 0x00 +#define PCH_PIC_INT_ID_HI 0x04 +#define PCH_PIC_INT_MASK_LO 0x20 +#define PCH_PIC_INT_MASK_HI 0x24 +#define PCH_PIC_HTMSI_EN_LO 0x40 +#define PCH_PIC_HTMSI_EN_HI 0x44 +#define PCH_PIC_INT_EDGE_LO 0x60 +#define PCH_PIC_INT_EDGE_HI 0x64 +#define PCH_PIC_INT_CLEAR_LO 0x80 +#define PCH_PIC_INT_CLEAR_HI 0x84 +#define PCH_PIC_AUTO_CTRL0_LO 0xc0 +#define PCH_PIC_AUTO_CTRL0_HI 0xc4 +#define PCH_PIC_AUTO_CTRL1_LO 0xe0 +#define PCH_PIC_AUTO_CTRL1_HI 0xe4 +#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 +#define PCH_PIC_ROUTE_ENTRY_END 0x13f +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 +#define PCH_PIC_HTMSI_VEC_END 0x23f +#define PCH_PIC_INT_STATUS_LO 0x3a0 +#define PCH_PIC_INT_STATUS_HI 0x3a4 +#define PCH_PIC_INT_POL_LO 0x3e0 +#define PCH_PIC_INT_POL_HI 0x3e4 + +#define STATUS_LO_START 0 +#define STATUS_HI_START 0x4 +#define POL_LO_START 0x40 +#define POL_HI_START 0x44 +#endif /* HW_LOONGARCH_PIC_COMMON_H */ From f58ac9784728e27b096f4d0086f5277a7bef01f4 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Sep 2024 14:43:10 +0800 Subject: [PATCH 0445/2892] include: Move struct LoongArchPCHPIC to loongarch_pic_common header file Move structure LoongArchPCHPIC from header file loongarch_pch_pic.h to file loongarch_pic_common.h, and rename structure name with LoongArchPICCommonState. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- include/hw/intc/loongarch_pch_pic.h | 27 +------------------------ include/hw/intc/loongarch_pic_common.h | 28 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index 861b32313b..381accbf2b 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -10,34 +10,9 @@ #include "hw/intc/loongarch_pic_common.h" +#define LoongArchPCHPIC LoongArchPICCommonState #define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" #define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC) -struct LoongArchPCHPIC { - SysBusDevice parent_obj; - qemu_irq parent_irq[64]; - uint64_t int_mask; /*0x020 interrupt mask register*/ - uint64_t htmsi_en; /*0x040 1=msi*/ - uint64_t intedge; /*0x060 edge=1 level =0*/ - uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ - uint64_t auto_crtl0; /*0x0c0*/ - uint64_t auto_crtl1; /*0x0e0*/ - uint64_t last_intirr; /* edge detection */ - uint64_t intirr; /* 0x380 interrupt request register */ - uint64_t intisr; /* 0x3a0 interrupt service register */ - /* - * 0x3e0 interrupt level polarity selection - * register 0 for high level trigger - */ - uint64_t int_polarity; - - uint8_t route_entry[64]; /*0x100 - 0x138*/ - uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ - - MemoryRegion iomem32_low; - MemoryRegion iomem32_high; - MemoryRegion iomem8; - unsigned int irq_num; -}; #endif /* HW_LOONGARCH_PCH_PIC_H */ diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index 6d0e33e1ac..124bb7d226 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -39,4 +39,32 @@ #define STATUS_HI_START 0x4 #define POL_LO_START 0x40 #define POL_HI_START 0x44 + +struct LoongArchPICCommonState { + SysBusDevice parent_obj; + + qemu_irq parent_irq[64]; + uint64_t int_mask; /* 0x020 interrupt mask register */ + uint64_t htmsi_en; /* 0x040 1=msi */ + uint64_t intedge; /* 0x060 edge=1 level=0 */ + uint64_t intclr; /* 0x080 clean edge int, set 1 clean, 0 noused */ + uint64_t auto_crtl0; /* 0x0c0 */ + uint64_t auto_crtl1; /* 0x0e0 */ + uint64_t last_intirr; /* edge detection */ + uint64_t intirr; /* 0x380 interrupt request register */ + uint64_t intisr; /* 0x3a0 interrupt service register */ + /* + * 0x3e0 interrupt level polarity selection + * register 0 for high level trigger + */ + uint64_t int_polarity; + + uint8_t route_entry[64]; /* 0x100 - 0x138 */ + uint8_t htmsi_vector[64]; /* 0x200 - 0x238 */ + + MemoryRegion iomem32_low; + MemoryRegion iomem32_high; + MemoryRegion iomem8; + unsigned int irq_num; +}; #endif /* HW_LOONGARCH_PIC_COMMON_H */ From f42e88883745ddf4ab48c26253d2dfb7f76a3f53 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Sep 2024 10:16:28 +0800 Subject: [PATCH 0446/2892] hw/intc/loongarch_pch: Merge instance_init() into realize() Memory region is created in instance_init(), merge it into function realize(). There is no special class_init() for loongarch_pch object. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 6a87b1aab7..ea87851994 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -382,6 +382,7 @@ static void loongarch_pch_pic_reset(DeviceState *d) static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) { LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { error_setg(errp, "Invalid 'pic_irq_num'"); @@ -390,19 +391,12 @@ static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); -} - -static void loongarch_pch_pic_init(Object *obj) -{ - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->iomem32_low, obj, + memory_region_init_io(&s->iomem32_low, OBJECT(dev), &loongarch_pch_pic_reg32_low_ops, s, PCH_PIC_NAME(.reg32_part1), 0x100); - memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops, + memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops, s, PCH_PIC_NAME(.reg8), 0x2a0); - memory_region_init_io(&s->iomem32_high, obj, + memory_region_init_io(&s->iomem32_high, OBJECT(dev), &loongarch_pch_pic_reg32_high_ops, s, PCH_PIC_NAME(.reg32_part2), 0xc60); sysbus_init_mmio(sbd, &s->iomem32_low); @@ -451,7 +445,6 @@ static const TypeInfo loongarch_pch_pic_info = { .name = TYPE_LOONGARCH_PCH_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongArchPCHPIC), - .instance_init = loongarch_pch_pic_init, .class_init = loongarch_pch_pic_class_init, }; From c43aceddb292229fa9206b8e26fb65a92dd90562 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Dec 2024 17:10:14 +0800 Subject: [PATCH 0447/2892] hw/intc/loongarch_pch: Rename LoongArchPCHPIC with LoongArchPICCommonState With pic vmstate, rename structure name vmstate_loongarch_pch_pic with vmstate_loongarch_pic_common, and with pic property rename loongarch_pch_pic_properties with loongarch_pic_common_properties. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 52 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index ea87851994..60a1e82253 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -379,13 +379,25 @@ static void loongarch_pch_pic_reset(DeviceState *d) s->int_polarity = 0x0; } +static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) +{ + LoongArchPICCommonState *s = LOONGARCH_PCH_PIC(dev); + + if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { + error_setg(errp, "Invalid 'pic_irq_num'"); + return; + } +} + static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) { LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; - if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { - error_setg(errp, "Invalid 'pic_irq_num'"); + loongarch_pic_common_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } @@ -405,28 +417,28 @@ static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) } -static const Property loongarch_pch_pic_properties[] = { - DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC, irq_num, 0), +static const Property loongarch_pic_common_properties[] = { + DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPICCommonState, irq_num, 0), DEFINE_PROP_END_OF_LIST(), }; -static const VMStateDescription vmstate_loongarch_pch_pic = { - .name = TYPE_LOONGARCH_PCH_PIC, +static const VMStateDescription vmstate_loongarch_pic_common = { + .name = "loongarch_pch_pic", .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT64(int_mask, LoongArchPCHPIC), - VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC), - VMSTATE_UINT64(intedge, LoongArchPCHPIC), - VMSTATE_UINT64(intclr, LoongArchPCHPIC), - VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC), - VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC), - VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64), - VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64), - VMSTATE_UINT64(last_intirr, LoongArchPCHPIC), - VMSTATE_UINT64(intirr, LoongArchPCHPIC), - VMSTATE_UINT64(intisr, LoongArchPCHPIC), - VMSTATE_UINT64(int_polarity, LoongArchPCHPIC), + VMSTATE_UINT64(int_mask, LoongArchPICCommonState), + VMSTATE_UINT64(htmsi_en, LoongArchPICCommonState), + VMSTATE_UINT64(intedge, LoongArchPICCommonState), + VMSTATE_UINT64(intclr, LoongArchPICCommonState), + VMSTATE_UINT64(auto_crtl0, LoongArchPICCommonState), + VMSTATE_UINT64(auto_crtl1, LoongArchPICCommonState), + VMSTATE_UINT8_ARRAY(route_entry, LoongArchPICCommonState, 64), + VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPICCommonState, 64), + VMSTATE_UINT64(last_intirr, LoongArchPICCommonState), + VMSTATE_UINT64(intirr, LoongArchPICCommonState), + VMSTATE_UINT64(intisr, LoongArchPICCommonState), + VMSTATE_UINT64(int_polarity, LoongArchPICCommonState), VMSTATE_END_OF_LIST() } }; @@ -437,8 +449,8 @@ static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data) dc->realize = loongarch_pch_pic_realize; device_class_set_legacy_reset(dc, loongarch_pch_pic_reset); - dc->vmsd = &vmstate_loongarch_pch_pic; - device_class_set_props(dc, loongarch_pch_pic_properties); + dc->vmsd = &vmstate_loongarch_pic_common; + device_class_set_props(dc, loongarch_pic_common_properties); } static const TypeInfo loongarch_pch_pic_info = { From b7563779f9e3a319af1a8d39084d0342e5dbb1bb Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Dec 2024 17:18:56 +0800 Subject: [PATCH 0448/2892] hw/intc/loongarch_pch: Move some functions to file loongarch_pic_common Move some common functions to file loongarch_pic_common.c, the common functions include loongarch_pic_common_realize(), property structure loongarch_pic_common_properties and vmstate structure vmstate_loongarch_pic_common. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 37 +----------------------------- hw/intc/loongarch_pic_common.c | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 hw/intc/loongarch_pic_common.c diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 60a1e82253..25c612b366 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -379,16 +379,7 @@ static void loongarch_pch_pic_reset(DeviceState *d) s->int_polarity = 0x0; } -static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) -{ - LoongArchPICCommonState *s = LOONGARCH_PCH_PIC(dev); - - if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { - error_setg(errp, "Invalid 'pic_irq_num'"); - return; - } -} - +#include "loongarch_pic_common.c" static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) { LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev); @@ -417,32 +408,6 @@ static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) } -static const Property loongarch_pic_common_properties[] = { - DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPICCommonState, irq_num, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_loongarch_pic_common = { - .name = "loongarch_pch_pic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (const VMStateField[]) { - VMSTATE_UINT64(int_mask, LoongArchPICCommonState), - VMSTATE_UINT64(htmsi_en, LoongArchPICCommonState), - VMSTATE_UINT64(intedge, LoongArchPICCommonState), - VMSTATE_UINT64(intclr, LoongArchPICCommonState), - VMSTATE_UINT64(auto_crtl0, LoongArchPICCommonState), - VMSTATE_UINT64(auto_crtl1, LoongArchPICCommonState), - VMSTATE_UINT8_ARRAY(route_entry, LoongArchPICCommonState, 64), - VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPICCommonState, 64), - VMSTATE_UINT64(last_intirr, LoongArchPICCommonState), - VMSTATE_UINT64(intirr, LoongArchPICCommonState), - VMSTATE_UINT64(intisr, LoongArchPICCommonState), - VMSTATE_UINT64(int_polarity, LoongArchPICCommonState), - VMSTATE_END_OF_LIST() - } -}; - static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/loongarch_pic_common.c b/hw/intc/loongarch_pic_common.c new file mode 100644 index 0000000000..ff8ebff93f --- /dev/null +++ b/hw/intc/loongarch_pic_common.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU Loongson 7A1000 I/O interrupt controller. + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) +{ + LoongArchPICCommonState *s = LOONGARCH_PCH_PIC(dev); + + if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { + error_setg(errp, "Invalid 'pic_irq_num'"); + return; + } +} + +static const Property loongarch_pic_common_properties[] = { + DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPICCommonState, irq_num, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_loongarch_pic_common = { + .name = "loongarch_pch_pic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(int_mask, LoongArchPICCommonState), + VMSTATE_UINT64(htmsi_en, LoongArchPICCommonState), + VMSTATE_UINT64(intedge, LoongArchPICCommonState), + VMSTATE_UINT64(intclr, LoongArchPICCommonState), + VMSTATE_UINT64(auto_crtl0, LoongArchPICCommonState), + VMSTATE_UINT64(auto_crtl1, LoongArchPICCommonState), + VMSTATE_UINT8_ARRAY(route_entry, LoongArchPICCommonState, 64), + VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPICCommonState, 64), + VMSTATE_UINT64(last_intirr, LoongArchPICCommonState), + VMSTATE_UINT64(intirr, LoongArchPICCommonState), + VMSTATE_UINT64(intisr, LoongArchPICCommonState), + VMSTATE_UINT64(int_polarity, LoongArchPICCommonState), + VMSTATE_END_OF_LIST() + } +}; From 8bf26a9ea3c8067e04c36b0280b219958955196c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Sep 2024 14:45:50 +0800 Subject: [PATCH 0449/2892] hw/intc/loongarch_pch: Inherit from loongarch_pic_common Set TYPE_LOONGARCH_PIC inherit from TYPE_LOONGARCH_PIC_COMMON object, it shares vmsate and property of TYPE_LOONGARCH_PIC_COMMON, and has its own realize() function. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 38 ++++++++++++-------------- hw/intc/loongarch_pic_common.c | 32 +++++++++++++++++++++- hw/intc/meson.build | 2 +- include/hw/intc/loongarch_pch_pic.h | 21 +++++++++++--- include/hw/intc/loongarch_pic_common.h | 10 +++++++ 5 files changed, 77 insertions(+), 26 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 25c612b366..11effd4cc9 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -379,14 +379,14 @@ static void loongarch_pch_pic_reset(DeviceState *d) s->int_polarity = 0x0; } -#include "loongarch_pic_common.c" -static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) +static void loongarch_pic_realize(DeviceState *dev, Error **errp) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); + LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; - loongarch_pic_common_realize(dev, &local_err); + lpc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -408,26 +408,24 @@ static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) } -static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data) +static void loongarch_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); - dc->realize = loongarch_pch_pic_realize; device_class_set_legacy_reset(dc, loongarch_pch_pic_reset); - dc->vmsd = &vmstate_loongarch_pic_common; - device_class_set_props(dc, loongarch_pic_common_properties); + device_class_set_parent_realize(dc, loongarch_pic_realize, + &lpc->parent_realize); } -static const TypeInfo loongarch_pch_pic_info = { - .name = TYPE_LOONGARCH_PCH_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LoongArchPCHPIC), - .class_init = loongarch_pch_pic_class_init, +static const TypeInfo loongarch_pic_types[] = { + { + .name = TYPE_LOONGARCH_PIC, + .parent = TYPE_LOONGARCH_PIC_COMMON, + .instance_size = sizeof(LoongarchPICState), + .class_size = sizeof(LoongarchPICClass), + .class_init = loongarch_pic_class_init, + } }; -static void loongarch_pch_pic_register_types(void) -{ - type_register_static(&loongarch_pch_pic_info); -} - -type_init(loongarch_pch_pic_register_types) +DEFINE_TYPES(loongarch_pic_types) diff --git a/hw/intc/loongarch_pic_common.c b/hw/intc/loongarch_pic_common.c index ff8ebff93f..f97e38368d 100644 --- a/hw/intc/loongarch_pic_common.c +++ b/hw/intc/loongarch_pic_common.c @@ -4,9 +4,15 @@ * Copyright (C) 2024 Loongson Technology Corporation Limited */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/loongarch_pic_common.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) { - LoongArchPICCommonState *s = LOONGARCH_PCH_PIC(dev); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { error_setg(errp, "Invalid 'pic_irq_num'"); @@ -39,3 +45,27 @@ static const VMStateDescription vmstate_loongarch_pic_common = { VMSTATE_END_OF_LIST() } }; + +static void loongarch_pic_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); + + device_class_set_parent_realize(dc, loongarch_pic_common_realize, + &lpcc->parent_realize); + device_class_set_props(dc, loongarch_pic_common_properties); + dc->vmsd = &vmstate_loongarch_pic_common; +} + +static const TypeInfo loongarch_pic_common_types[] = { + { + .name = TYPE_LOONGARCH_PIC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchPICCommonState), + .class_size = sizeof(LoongArchPICCommonClass), + .class_init = loongarch_pic_common_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(loongarch_pic_common_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 6bfdc4eb33..848cb6685e 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -71,6 +71,6 @@ specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) -specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c', 'loongarch_pic_common.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index 381accbf2b..f84be0ac62 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -10,9 +10,22 @@ #include "hw/intc/loongarch_pic_common.h" -#define LoongArchPCHPIC LoongArchPICCommonState -#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" -#define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC) +#define TYPE_LOONGARCH_PIC "loongarch_pic" +#define PCH_PIC_NAME(name) TYPE_LOONGARCH_PIC#name +OBJECT_DECLARE_TYPE(LoongarchPICState, LoongarchPICClass, LOONGARCH_PIC) + +struct LoongarchPICState { + LoongArchPICCommonState parent_obj; +}; + +struct LoongarchPICClass { + LoongArchPICCommonClass parent_class; + + DeviceRealize parent_realize; +}; + +#define TYPE_LOONGARCH_PCH_PIC TYPE_LOONGARCH_PIC +typedef struct LoongArchPICCommonState LoongArchPCHPIC; +#define LOONGARCH_PCH_PIC(obj) ((struct LoongArchPICCommonState *)(obj)) #endif /* HW_LOONGARCH_PCH_PIC_H */ diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index 124bb7d226..0a1a28063c 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -40,6 +40,10 @@ #define POL_LO_START 0x40 #define POL_HI_START 0x44 +#define TYPE_LOONGARCH_PIC_COMMON "loongarch_pic_common" +OBJECT_DECLARE_TYPE(LoongArchPICCommonState, + LoongArchPICCommonClass, LOONGARCH_PIC_COMMON) + struct LoongArchPICCommonState { SysBusDevice parent_obj; @@ -67,4 +71,10 @@ struct LoongArchPICCommonState { MemoryRegion iomem8; unsigned int irq_num; }; + +struct LoongArchPICCommonClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; +}; #endif /* HW_LOONGARCH_PIC_COMMON_H */ From 36d31cf812848ee65ab03ca901e7b140f5538a7a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Sep 2024 11:50:49 +0800 Subject: [PATCH 0450/2892] hw/intc/loongarch_pch: Add pre_save and post_load interfaces Add vmstate pre_save and post_load interfaces, which can be used by pic kvm driver in future. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pic_common.c | 26 ++++++++++++++++++++++++++ include/hw/intc/loongarch_pic_common.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/hw/intc/loongarch_pic_common.c b/hw/intc/loongarch_pic_common.c index f97e38368d..bcb6b7b3fc 100644 --- a/hw/intc/loongarch_pic_common.c +++ b/hw/intc/loongarch_pic_common.c @@ -10,6 +10,30 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" +static int loongarch_pic_pre_save(void *opaque) +{ + LoongArchPICCommonState *s = (LoongArchPICCommonState *)opaque; + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_GET_CLASS(s); + + if (lpcc->pre_save) { + return lpcc->pre_save(s); + } + + return 0; +} + +static int loongarch_pic_post_load(void *opaque, int version_id) +{ + LoongArchPICCommonState *s = (LoongArchPICCommonState *)opaque; + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_GET_CLASS(s); + + if (lpcc->post_load) { + return lpcc->post_load(s, version_id); + } + + return 0; +} + static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) { LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); @@ -29,6 +53,8 @@ static const VMStateDescription vmstate_loongarch_pic_common = { .name = "loongarch_pch_pic", .version_id = 1, .minimum_version_id = 1, + .pre_save = loongarch_pic_pre_save, + .post_load = loongarch_pic_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64(int_mask, LoongArchPICCommonState), VMSTATE_UINT64(htmsi_en, LoongArchPICCommonState), diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index 0a1a28063c..43cce48978 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -76,5 +76,7 @@ struct LoongArchPICCommonClass { SysBusDeviceClass parent_class; DeviceRealize parent_realize; + int (*pre_save)(LoongArchPICCommonState *s); + int (*post_load)(LoongArchPICCommonState *s, int version_id); }; #endif /* HW_LOONGARCH_PIC_COMMON_H */ From b2799f101cfb58f46cd5ef043038276d128d8e72 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Sep 2024 12:05:00 +0800 Subject: [PATCH 0451/2892] hw/intc/loongarch_pch: Code cleanup about loongarch_pch_pic Remove definition about LoongArchPCHPIC and LOONGARCH_PCH_PIC, and replace them with LoongArchPICCommonState and LOONGARCH_PIC_COMMON separately. Also remove unnecessary header files. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 24 ++++++++++-------------- hw/loongarch/virt.c | 2 +- include/hw/intc/loongarch_pch_pic.h | 4 ---- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 11effd4cc9..acd75ccb0c 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -7,17 +7,13 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" -#include "hw/sysbus.h" -#include "hw/loongarch/virt.h" -#include "hw/pci-host/ls7a.h" #include "hw/irq.h" #include "hw/intc/loongarch_pch_pic.h" -#include "hw/qdev-properties.h" -#include "migration/vmstate.h" #include "trace.h" #include "qapi/error.h" -static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) +static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, + int level) { uint64_t val; int irq; @@ -45,7 +41,7 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) static void pch_pic_irq_handler(void *opaque, int irq, int level) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint64_t mask = 1ULL << irq; assert(irq < s->irq_num); @@ -78,7 +74,7 @@ static void pch_pic_irq_handler(void *opaque, int irq, int level) static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint64_t val = 0; uint32_t offset = addr & 0xfff; @@ -136,7 +132,7 @@ static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi) static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint32_t offset, old_valid, data = (uint32_t)value; uint64_t old, int_mask; offset = addr & 0xfff; @@ -208,7 +204,7 @@ static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr, static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint64_t val = 0; uint32_t offset = addr & 0xfff; @@ -236,7 +232,7 @@ static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint32_t offset, data = (uint32_t)value; offset = addr & 0xfff; @@ -263,7 +259,7 @@ static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr, static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint64_t val = 0; uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET; int64_t offset_tmp; @@ -292,7 +288,7 @@ static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr, static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); int32_t offset_tmp; uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET; @@ -360,7 +356,7 @@ static const MemoryRegionOps loongarch_pch_pic_reg8_ops = { static void loongarch_pch_pic_reset(DeviceState *d) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(d); int i; s->int_mask = -1; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 9a635d1d3d..43a3e0d4d4 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -894,7 +894,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Add Extend I/O Interrupt Controller node */ fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); - pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); + pch_pic = qdev_new(TYPE_LOONGARCH_PIC); num = VIRT_PCH_PIC_IRQ_NUM; qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); d = SYS_BUS_DEVICE(pch_pic); diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index f84be0ac62..481cc58aed 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -24,8 +24,4 @@ struct LoongarchPICClass { DeviceRealize parent_realize; }; -#define TYPE_LOONGARCH_PCH_PIC TYPE_LOONGARCH_PIC -typedef struct LoongArchPICCommonState LoongArchPCHPIC; -#define LOONGARCH_PCH_PIC(obj) ((struct LoongArchPICCommonState *)(obj)) - #endif /* HW_LOONGARCH_PCH_PIC_H */ From fea46db1c7f3b7cd3df92e871c10a6b85a872041 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 20 Sep 2024 10:01:07 +0800 Subject: [PATCH 0452/2892] include: Add loongarch_extioi_common header file Add common header file include/hw/intc/loongarch_extioi_common.h, and move some macro definition from include/hw/intc/loongarch_extioi.h to the common header file. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- include/hw/intc/loongarch_extioi.h | 50 +------------------ include/hw/intc/loongarch_extioi_common.h | 58 +++++++++++++++++++++++ 2 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 include/hw/intc/loongarch_extioi_common.h diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 626a37dfa1..b1f87cd246 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -5,58 +5,10 @@ * Copyright (C) 2021 Loongson Technology Corporation Limited */ -#include "hw/sysbus.h" -#include "hw/loongarch/virt.h" - #ifndef LOONGARCH_EXTIOI_H #define LOONGARCH_EXTIOI_H -#define LS3A_INTC_IP 8 -#define EXTIOI_IRQS (256) -#define EXTIOI_IRQS_BITMAP_SIZE (256 / 8) -/* irq from EXTIOI is routed to no more than 4 cpus */ -#define EXTIOI_CPUS (4) -/* map to ipnum per 32 irqs */ -#define EXTIOI_IRQS_IPMAP_SIZE (256 / 32) -#define EXTIOI_IRQS_COREMAP_SIZE 256 -#define EXTIOI_IRQS_NODETYPE_COUNT 16 -#define EXTIOI_IRQS_GROUP_COUNT 8 - -#define APIC_OFFSET 0x400 -#define APIC_BASE (0x1000ULL + APIC_OFFSET) - -#define EXTIOI_NODETYPE_START (0x4a0 - APIC_OFFSET) -#define EXTIOI_NODETYPE_END (0x4c0 - APIC_OFFSET) -#define EXTIOI_IPMAP_START (0x4c0 - APIC_OFFSET) -#define EXTIOI_IPMAP_END (0x4c8 - APIC_OFFSET) -#define EXTIOI_ENABLE_START (0x600 - APIC_OFFSET) -#define EXTIOI_ENABLE_END (0x620 - APIC_OFFSET) -#define EXTIOI_BOUNCE_START (0x680 - APIC_OFFSET) -#define EXTIOI_BOUNCE_END (0x6a0 - APIC_OFFSET) -#define EXTIOI_ISR_START (0x700 - APIC_OFFSET) -#define EXTIOI_ISR_END (0x720 - APIC_OFFSET) -#define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) -#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) -#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) -#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) -#define EXTIOI_SIZE 0x800 - -#define EXTIOI_VIRT_BASE (0x40000000) -#define EXTIOI_VIRT_SIZE (0x1000) -#define EXTIOI_VIRT_FEATURES (0x0) -#define EXTIOI_HAS_VIRT_EXTENSION (0) -#define EXTIOI_HAS_ENABLE_OPTION (1) -#define EXTIOI_HAS_INT_ENCODE (2) -#define EXTIOI_HAS_CPU_ENCODE (3) -#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ - | BIT(EXTIOI_HAS_ENABLE_OPTION) \ - | BIT(EXTIOI_HAS_CPU_ENCODE)) -#define EXTIOI_VIRT_CONFIG (0x4) -#define EXTIOI_ENABLE (1) -#define EXTIOI_ENABLE_INT_ENCODE (2) -#define EXTIOI_ENABLE_CPU_ENCODE (3) -#define EXTIOI_VIRT_COREMAP_START (0x40) -#define EXTIOI_VIRT_COREMAP_END (0x240) +#include "hw/intc/loongarch_extioi_common.h" typedef struct ExtIOICore { uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h new file mode 100644 index 0000000000..09e2b760f3 --- /dev/null +++ b/include/hw/intc/loongarch_extioi_common.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch 3A5000 ext interrupt controller definitions + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_EXTIOI_COMMON_H +#define LOONGARCH_EXTIOI_COMMON_H + +#include "hw/sysbus.h" +#include "hw/loongarch/virt.h" + +#define LS3A_INTC_IP 8 +#define EXTIOI_IRQS (256) +#define EXTIOI_IRQS_BITMAP_SIZE (256 / 8) +/* irq from EXTIOI is routed to no more than 4 cpus */ +#define EXTIOI_CPUS (4) +/* map to ipnum per 32 irqs */ +#define EXTIOI_IRQS_IPMAP_SIZE (256 / 32) +#define EXTIOI_IRQS_COREMAP_SIZE 256 +#define EXTIOI_IRQS_NODETYPE_COUNT 16 +#define EXTIOI_IRQS_GROUP_COUNT 8 + +#define APIC_OFFSET 0x400 +#define APIC_BASE (0x1000ULL + APIC_OFFSET) +#define EXTIOI_NODETYPE_START (0x4a0 - APIC_OFFSET) +#define EXTIOI_NODETYPE_END (0x4c0 - APIC_OFFSET) +#define EXTIOI_IPMAP_START (0x4c0 - APIC_OFFSET) +#define EXTIOI_IPMAP_END (0x4c8 - APIC_OFFSET) +#define EXTIOI_ENABLE_START (0x600 - APIC_OFFSET) +#define EXTIOI_ENABLE_END (0x620 - APIC_OFFSET) +#define EXTIOI_BOUNCE_START (0x680 - APIC_OFFSET) +#define EXTIOI_BOUNCE_END (0x6a0 - APIC_OFFSET) +#define EXTIOI_ISR_START (0x700 - APIC_OFFSET) +#define EXTIOI_ISR_END (0x720 - APIC_OFFSET) +#define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) +#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) +#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) +#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) +#define EXTIOI_SIZE 0x800 + +#define EXTIOI_VIRT_BASE (0x40000000) +#define EXTIOI_VIRT_SIZE (0x1000) +#define EXTIOI_VIRT_FEATURES (0x0) +#define EXTIOI_HAS_VIRT_EXTENSION (0) +#define EXTIOI_HAS_ENABLE_OPTION (1) +#define EXTIOI_HAS_INT_ENCODE (2) +#define EXTIOI_HAS_CPU_ENCODE (3) +#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ + | BIT(EXTIOI_HAS_ENABLE_OPTION) \ + | BIT(EXTIOI_HAS_CPU_ENCODE)) +#define EXTIOI_VIRT_CONFIG (0x4) +#define EXTIOI_ENABLE (1) +#define EXTIOI_ENABLE_INT_ENCODE (2) +#define EXTIOI_ENABLE_CPU_ENCODE (3) +#define EXTIOI_VIRT_COREMAP_START (0x40) +#define EXTIOI_VIRT_COREMAP_END (0x240) +#endif /* LOONGARCH_EXTIOI_H */ From 593c6b868450603a3295892d8d65b1eba4ea211d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 20 Sep 2024 10:08:56 +0800 Subject: [PATCH 0453/2892] include: Move struct LoongArchExtIOI to header file loongarch_extioi_common Move definiton of structure LoongArchExtIOI from header file loongarch_extioi.h to file loongarch_extioi_common.h. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- include/hw/intc/loongarch_extioi.h | 26 ---------------------- include/hw/intc/loongarch_extioi_common.h | 27 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index b1f87cd246..64924f5a0a 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -10,32 +10,6 @@ #include "hw/intc/loongarch_extioi_common.h" -typedef struct ExtIOICore { - uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; - DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); - qemu_irq parent_irq[LS3A_INTC_IP]; -} ExtIOICore; - #define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) -struct LoongArchExtIOI { - SysBusDevice parent_obj; - uint32_t num_cpu; - uint32_t features; - uint32_t status; - /* hardware state */ - uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; - uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; - uint32_t isr[EXTIOI_IRQS / 32]; - uint32_t enable[EXTIOI_IRQS / 32]; - uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4]; - uint32_t coremap[EXTIOI_IRQS / 4]; - uint32_t sw_pending[EXTIOI_IRQS / 32]; - uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE]; - uint8_t sw_coremap[EXTIOI_IRQS]; - qemu_irq irq[EXTIOI_IRQS]; - ExtIOICore *cpu; - MemoryRegion extioi_system_mem; - MemoryRegion virt_extend; -}; #endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index 09e2b760f3..1eb8780549 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -55,4 +55,31 @@ #define EXTIOI_ENABLE_CPU_ENCODE (3) #define EXTIOI_VIRT_COREMAP_START (0x40) #define EXTIOI_VIRT_COREMAP_END (0x240) + +typedef struct ExtIOICore { + uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; + DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); + qemu_irq parent_irq[LS3A_INTC_IP]; +} ExtIOICore; + +struct LoongArchExtIOI { + SysBusDevice parent_obj; + uint32_t num_cpu; + uint32_t features; + uint32_t status; + /* hardware state */ + uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; + uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; + uint32_t isr[EXTIOI_IRQS / 32]; + uint32_t enable[EXTIOI_IRQS / 32]; + uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4]; + uint32_t coremap[EXTIOI_IRQS / 4]; + uint32_t sw_pending[EXTIOI_IRQS / 32]; + uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE]; + uint8_t sw_coremap[EXTIOI_IRQS]; + qemu_irq irq[EXTIOI_IRQS]; + ExtIOICore *cpu; + MemoryRegion extioi_system_mem; + MemoryRegion virt_extend; +}; #endif /* LOONGARCH_EXTIOI_H */ From 6f54d920935826a3ce47ba64605b3ccd20d9c0f1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 20 Sep 2024 10:17:06 +0800 Subject: [PATCH 0454/2892] include: Rename LoongArchExtIOI with LoongArchExtIOICommonState Rename structure LoongArchExtIOI with LoongArchExtIOICommonState, since it is defined in file loongarch_extioi_common.h Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- include/hw/intc/loongarch_extioi.h | 1 + include/hw/intc/loongarch_extioi_common.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 64924f5a0a..d6747046b4 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -10,6 +10,7 @@ #include "hw/intc/loongarch_extioi_common.h" +#define LoongArchExtIOI LoongArchExtIOICommonState #define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) #endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index 1eb8780549..51243b8092 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -62,7 +62,7 @@ typedef struct ExtIOICore { qemu_irq parent_irq[LS3A_INTC_IP]; } ExtIOICore; -struct LoongArchExtIOI { +struct LoongArchExtIOICommonState { SysBusDevice parent_obj; uint32_t num_cpu; uint32_t features; From c169b51b1720c2722bfacb83896cd7100859ee4c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Dec 2024 17:24:24 +0800 Subject: [PATCH 0455/2892] hw/intc/loongarch_extioi: Rename LoongArchExtIOI with LoongArchExtIOICommonState With some structure such as vmstate and property, rename LoongArchExtIOI with LoongArchExtIOICommonState, these common structure will be moved to common file. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 41 +++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index dd91f89361..039fc4dd1e 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -391,6 +391,11 @@ static int vmstate_extioi_post_load(void *opaque, int version_id) return 0; } +static int loongarch_extioi_common_post_load(void *opaque, int version_id) +{ + return vmstate_extioi_post_load(opaque, version_id); +} + static const VMStateDescription vmstate_extioi_core = { .name = "extioi-core", .version_id = 1, @@ -402,31 +407,35 @@ static const VMStateDescription vmstate_extioi_core = { }; static const VMStateDescription vmstate_loongarch_extioi = { - .name = TYPE_LOONGARCH_EXTIOI, + .name = "loongarch.extioi", .version_id = 3, .minimum_version_id = 3, - .post_load = vmstate_extioi_post_load, + .post_load = loongarch_extioi_common_post_load, .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), - VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI, + VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, + EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState, EXTIOI_IRQS_NODETYPE_COUNT / 2), - VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32), - VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32), - VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4), - VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4), - - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu, - vmstate_extioi_core, ExtIOICore), - VMSTATE_UINT32(features, LoongArchExtIOI), - VMSTATE_UINT32(status, LoongArchExtIOI), + VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState, + EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState, + EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState, + EXTIOI_IRQS_IPMAP_SIZE / 4), + VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState, + EXTIOI_IRQS / 4), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState, + num_cpu, vmstate_extioi_core, ExtIOICore), + VMSTATE_UINT32(features, LoongArchExtIOICommonState), + VMSTATE_UINT32(status, LoongArchExtIOICommonState), VMSTATE_END_OF_LIST() } }; static const Property extioi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), - DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features, - EXTIOI_HAS_VIRT_EXTENSION, 0), + DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOICommonState, num_cpu, 1), + DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, + features, EXTIOI_HAS_VIRT_EXTENSION, 0), DEFINE_PROP_END_OF_LIST(), }; From aa6330d50ccf7467a022dc348ab6ae2323c2b0b9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 20 Sep 2024 15:49:12 +0800 Subject: [PATCH 0456/2892] hw/intc/loongarch_extioi: Add common realize interface Add common realize function, it is only to check validity of property. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 039fc4dd1e..dcc278a214 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -318,14 +318,26 @@ static const MemoryRegionOps extioi_virt_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev; + + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } +} + static void loongarch_extioi_realize(DeviceState *dev, Error **errp) { LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; int i, pin; - if (s->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); + loongarch_extioi_common_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } From 4abf47126f5c37e974c58f3cbd295abcc67b8668 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 20 Sep 2024 11:48:10 +0800 Subject: [PATCH 0457/2892] hw/intc/loongarch_extioi: Add unrealize interface For loongarch extioi emulation driver, add unrealize interface and remove instance_finalize interface and move the code to unrealize interface. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index dcc278a214..d759b7f57d 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -372,9 +372,9 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) } } -static void loongarch_extioi_finalize(Object *obj) +static void loongarch_extioi_unrealize(DeviceState *dev) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI(dev); g_free(s->cpu); } @@ -456,6 +456,7 @@ static void loongarch_extioi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = loongarch_extioi_realize; + dc->unrealize = loongarch_extioi_unrealize; device_class_set_legacy_reset(dc, loongarch_extioi_reset); device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_loongarch_extioi; @@ -466,7 +467,6 @@ static const TypeInfo loongarch_extioi_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct LoongArchExtIOI), .class_init = loongarch_extioi_class_init, - .instance_finalize = loongarch_extioi_finalize, }; static void loongarch_extioi_register_types(void) From 6b69f778176d6d00842ec4e7ac9400af1620387a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Dec 2024 17:28:04 +0800 Subject: [PATCH 0458/2892] hw/intc/loongarch_extioi: Add common file loongarch_extioi_common Add new common file loongarch_extioi_common.c, and move vmstate and property structure to common file. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 59 +---------------------------- hw/intc/loongarch_extioi_common.c | 63 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 57 deletions(-) create mode 100644 hw/intc/loongarch_extioi_common.c diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index d759b7f57d..d7471ff165 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -318,15 +318,8 @@ static const MemoryRegionOps extioi_virt_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) -{ - LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev; - - if (s->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); - return; - } -} +static int vmstate_extioi_post_load(void *opaque, int version_id); +#include "loongarch_extioi_common.c" static void loongarch_extioi_realize(DeviceState *dev, Error **errp) { @@ -403,54 +396,6 @@ static int vmstate_extioi_post_load(void *opaque, int version_id) return 0; } -static int loongarch_extioi_common_post_load(void *opaque, int version_id) -{ - return vmstate_extioi_post_load(opaque, version_id); -} - -static const VMStateDescription vmstate_extioi_core = { - .name = "extioi-core", - .version_id = 1, - .minimum_version_id = 1, - .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_loongarch_extioi = { - .name = "loongarch.extioi", - .version_id = 3, - .minimum_version_id = 3, - .post_load = loongarch_extioi_common_post_load, - .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, - EXTIOI_IRQS_GROUP_COUNT), - VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState, - EXTIOI_IRQS_NODETYPE_COUNT / 2), - VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState, - EXTIOI_IRQS / 32), - VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState, - EXTIOI_IRQS / 32), - VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState, - EXTIOI_IRQS_IPMAP_SIZE / 4), - VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState, - EXTIOI_IRQS / 4), - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState, - num_cpu, vmstate_extioi_core, ExtIOICore), - VMSTATE_UINT32(features, LoongArchExtIOICommonState), - VMSTATE_UINT32(status, LoongArchExtIOICommonState), - VMSTATE_END_OF_LIST() - } -}; - -static const Property extioi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOICommonState, num_cpu, 1), - DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, - features, EXTIOI_HAS_VIRT_EXTENSION, 0), - DEFINE_PROP_END_OF_LIST(), -}; - static void loongarch_extioi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c new file mode 100644 index 0000000000..6c8366a5e5 --- /dev/null +++ b/hw/intc/loongarch_extioi_common.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson extioi interrupt controller emulation + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev; + + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } +} + +static int loongarch_extioi_common_post_load(void *opaque, int version_id) +{ + return vmstate_extioi_post_load(opaque, version_id); +} + +static const VMStateDescription vmstate_extioi_core = { + .name = "extioi-core", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_loongarch_extioi = { + .name = "loongarch.extioi", + .version_id = 3, + .minimum_version_id = 3, + .post_load = loongarch_extioi_common_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, + EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState, + EXTIOI_IRQS_NODETYPE_COUNT / 2), + VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState, + EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState, + EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState, + EXTIOI_IRQS_IPMAP_SIZE / 4), + VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState, + EXTIOI_IRQS / 4), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState, + num_cpu, vmstate_extioi_core, ExtIOICore), + VMSTATE_UINT32(features, LoongArchExtIOICommonState), + VMSTATE_UINT32(status, LoongArchExtIOICommonState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property extioi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOICommonState, num_cpu, 1), + DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, + features, EXTIOI_HAS_VIRT_EXTENSION, 0), + DEFINE_PROP_END_OF_LIST(), +}; From 272c467a48815e77db89b69dcd251b9f5b22203e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 13 Dec 2024 15:32:39 +0800 Subject: [PATCH 0459/2892] hw/intc/loongarch_extioi: Inherit from loongarch_extioi_common Set TYPE_LOONGARCH_EXTIOI inherit from TYPE_LOONGARCH_EXTIOI_COMMON object, it shares vmsate and property of TYPE_LOONGARCH_EXTIOI_COMMON, and has its own realize() function. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 39 +++++++++++------------ hw/intc/loongarch_extioi_common.c | 39 ++++++++++++++++++++++- hw/intc/meson.build | 2 +- include/hw/intc/loongarch_extioi.h | 17 ++++++++-- include/hw/intc/loongarch_extioi_common.h | 12 +++++++ 5 files changed, 85 insertions(+), 24 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index d7471ff165..c4d77a321f 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -318,17 +318,15 @@ static const MemoryRegionOps extioi_virt_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int vmstate_extioi_post_load(void *opaque, int version_id); -#include "loongarch_extioi_common.c" - static void loongarch_extioi_realize(DeviceState *dev, Error **errp) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); + LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; int i, pin; - loongarch_extioi_common_realize(dev, &local_err); + lec->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -399,24 +397,25 @@ static int vmstate_extioi_post_load(void *opaque, int version_id) static void loongarch_extioi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_CLASS(klass); + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); - dc->realize = loongarch_extioi_realize; - dc->unrealize = loongarch_extioi_unrealize; + device_class_set_parent_realize(dc, loongarch_extioi_realize, + &lec->parent_realize); + device_class_set_parent_unrealize(dc, loongarch_extioi_unrealize, + &lec->parent_unrealize); device_class_set_legacy_reset(dc, loongarch_extioi_reset); - device_class_set_props(dc, extioi_properties); - dc->vmsd = &vmstate_loongarch_extioi; + lecc->post_load = vmstate_extioi_post_load; } -static const TypeInfo loongarch_extioi_info = { - .name = TYPE_LOONGARCH_EXTIOI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct LoongArchExtIOI), - .class_init = loongarch_extioi_class_init, +static const TypeInfo loongarch_extioi_types[] = { + { + .name = TYPE_LOONGARCH_EXTIOI, + .parent = TYPE_LOONGARCH_EXTIOI_COMMON, + .instance_size = sizeof(LoongArchExtIOIState), + .class_size = sizeof(LoongArchExtIOIClass), + .class_init = loongarch_extioi_class_init, + } }; -static void loongarch_extioi_register_types(void) -{ - type_register_static(&loongarch_extioi_info); -} - -type_init(loongarch_extioi_register_types) +DEFINE_TYPES(loongarch_extioi_types) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 6c8366a5e5..428d105f78 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -3,6 +3,12 @@ * Loongson extioi interrupt controller emulation * Copyright (C) 2024 Loongson Technology Corporation Limited */ +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/intc/loongarch_extioi_common.h" +#include "migration/vmstate.h" static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) { @@ -16,7 +22,14 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) static int loongarch_extioi_common_post_load(void *opaque, int version_id) { - return vmstate_extioi_post_load(opaque, version_id); + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); + + if (lecc->post_load) { + return lecc->post_load(s, version_id); + } + + return 0; } static const VMStateDescription vmstate_extioi_core = { @@ -61,3 +74,27 @@ static const Property extioi_properties[] = { features, EXTIOI_HAS_VIRT_EXTENSION, 0), DEFINE_PROP_END_OF_LIST(), }; + +static void loongarch_extioi_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); + + device_class_set_parent_realize(dc, loongarch_extioi_common_realize, + &lecc->parent_realize); + device_class_set_props(dc, extioi_properties); + dc->vmsd = &vmstate_loongarch_extioi; +} + +static const TypeInfo loongarch_extioi_common_types[] = { + { + .name = TYPE_LOONGARCH_EXTIOI_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchExtIOICommonState), + .class_size = sizeof(LoongArchExtIOICommonClass), + .class_init = loongarch_extioi_common_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(loongarch_extioi_common_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 848cb6685e..510fdfb688 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -73,4 +73,4 @@ specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c', 'loongarch_pic_common.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) -specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index d6747046b4..cc160c52dc 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -10,7 +10,20 @@ #include "hw/intc/loongarch_extioi_common.h" -#define LoongArchExtIOI LoongArchExtIOICommonState #define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) +OBJECT_DECLARE_TYPE(LoongArchExtIOIState, LoongArchExtIOIClass, LOONGARCH_EXTIOI) + +struct LoongArchExtIOIState { + LoongArchExtIOICommonState parent_obj; +}; + +struct LoongArchExtIOIClass { + LoongArchExtIOICommonClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; +}; + +#define LoongArchExtIOI LoongArchExtIOICommonState +#define LOONGARCH_EXTIOI(obj) ((LoongArchExtIOICommonState *)obj) #endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index 51243b8092..d45caa45f2 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -7,6 +7,7 @@ #ifndef LOONGARCH_EXTIOI_COMMON_H #define LOONGARCH_EXTIOI_COMMON_H +#include "qom/object.h" #include "hw/sysbus.h" #include "hw/loongarch/virt.h" @@ -56,6 +57,10 @@ #define EXTIOI_VIRT_COREMAP_START (0x40) #define EXTIOI_VIRT_COREMAP_END (0x240) +#define TYPE_LOONGARCH_EXTIOI_COMMON "loongarch_extioi_common" +OBJECT_DECLARE_TYPE(LoongArchExtIOICommonState, + LoongArchExtIOICommonClass, LOONGARCH_EXTIOI_COMMON) + typedef struct ExtIOICore { uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); @@ -82,4 +87,11 @@ struct LoongArchExtIOICommonState { MemoryRegion extioi_system_mem; MemoryRegion virt_extend; }; + +struct LoongArchExtIOICommonClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; + int (*post_load)(void *s, int version_id); +}; #endif /* LOONGARCH_EXTIOI_H */ From ff09444a88af9f5d55b9e315aa16d3ace1f25211 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 8 Nov 2024 15:42:05 +0800 Subject: [PATCH 0460/2892] hw/intc/loongarch_extioi: Add pre_save interface Add vmstate pre_save interface, which can be used extioi kvm driver in future. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi_common.c | 13 +++++++++++++ include/hw/intc/loongarch_extioi_common.h | 1 + 2 files changed, 14 insertions(+) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 428d105f78..e50431f124 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -20,6 +20,18 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) } } +static int loongarch_extioi_common_pre_save(void *opaque) +{ + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); + + if (lecc->pre_save) { + return lecc->pre_save(s); + } + + return 0; +} + static int loongarch_extioi_common_post_load(void *opaque, int version_id) { LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; @@ -46,6 +58,7 @@ static const VMStateDescription vmstate_loongarch_extioi = { .name = "loongarch.extioi", .version_id = 3, .minimum_version_id = 3, + .pre_save = loongarch_extioi_common_pre_save, .post_load = loongarch_extioi_common_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index d45caa45f2..f6bc778a85 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -92,6 +92,7 @@ struct LoongArchExtIOICommonClass { SysBusDeviceClass parent_class; DeviceRealize parent_realize; + int (*pre_save)(void *s); int (*post_load)(void *s, int version_id); }; #endif /* LOONGARCH_EXTIOI_H */ From 6f6006ad07243543595c7607ffbeee7f45b94b80 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 13 Dec 2024 15:34:48 +0800 Subject: [PATCH 0461/2892] hw/intc/loongarch_extioi: Code cleanup about loongarch_extioi Remove definition about LoongArchExtIOI and LOONGARCH_EXTIOI, and replace them with LoongArchExtIOICommonState and macro LOONGARCH_EXTIOI_COMMON separately. Also remove unnecessary header files. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 31 ++++++++++++++---------------- include/hw/intc/loongarch_extioi.h | 2 -- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index c4d77a321f..4a1a7c357c 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -10,16 +10,13 @@ #include "qemu/log.h" #include "qapi/error.h" #include "hw/irq.h" -#include "hw/sysbus.h" #include "hw/loongarch/virt.h" -#include "hw/qdev-properties.h" #include "exec/address-spaces.h" #include "hw/intc/loongarch_extioi.h" -#include "migration/vmstate.h" #include "trace.h" -static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) +static void extioi_update_irq(LoongArchExtIOICommonState *s, int irq, int level) { int ipnum, cpu, found, irq_index, irq_mask; @@ -54,7 +51,7 @@ static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) static void extioi_setirq(void *opaque, int irq, int level) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); trace_loongarch_extioi_setirq(irq, level); if (level) { set_bit32(irq, s->isr); @@ -67,7 +64,7 @@ static void extioi_setirq(void *opaque, int irq, int level) static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); unsigned long offset = addr & 0xffff; uint32_t index, cpu; @@ -106,7 +103,7 @@ static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, return MEMTX_OK; } -static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ +static inline void extioi_enable_irq(LoongArchExtIOICommonState *s, int index,\ uint32_t mask, int level) { uint32_t val; @@ -125,8 +122,8 @@ static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ } } -static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, - uint64_t val, bool notify) +static inline void extioi_update_sw_coremap(LoongArchExtIOICommonState *s, + int irq, uint64_t val, bool notify) { int i, cpu; @@ -162,8 +159,8 @@ static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, } } -static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index, - uint64_t val) +static inline void extioi_update_sw_ipmap(LoongArchExtIOICommonState *s, + int index, uint64_t val) { int i; uint8_t ipnum; @@ -186,7 +183,7 @@ static MemTxResult extioi_writew(void *opaque, hwaddr addr, uint64_t val, unsigned size, MemTxAttrs attrs) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); int cpu, index, old_data, irq; uint32_t offset; @@ -266,7 +263,7 @@ static const MemoryRegionOps extioi_ops = { static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); switch (addr) { case EXTIOI_VIRT_FEATURES: @@ -286,7 +283,7 @@ static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr, uint64_t val, unsigned size, MemTxAttrs attrs) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); switch (addr) { case EXTIOI_VIRT_FEATURES: @@ -365,21 +362,21 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) static void loongarch_extioi_unrealize(DeviceState *dev) { - LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI(dev); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); g_free(s->cpu); } static void loongarch_extioi_reset(DeviceState *d) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(d); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(d); s->status = 0; } static int vmstate_extioi_post_load(void *opaque, int version_id) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); int i, start_irq; for (i = 0; i < (EXTIOI_IRQS / 4); i++) { diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index cc160c52dc..351f18afcf 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -24,6 +24,4 @@ struct LoongArchExtIOIClass { DeviceUnrealize parent_unrealize; }; -#define LoongArchExtIOI LoongArchExtIOICommonState -#define LOONGARCH_EXTIOI(obj) ((LoongArchExtIOICommonState *)obj) #endif /* LOONGARCH_EXTIOI_H */ From b44da016a7e86f0c8fffddb10ca10d2eaacdceac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:28 -0600 Subject: [PATCH 0462/2892] migration: Constify migration_properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-2-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- migration/options.c | 2 +- migration/options.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/options.c b/migration/options.c index ad8d6989a8..24cc8471aa 100644 --- a/migration/options.c +++ b/migration/options.c @@ -85,7 +85,7 @@ #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD 1000 /* milliseconds */ #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT 1 /* MB/s */ -Property migration_properties[] = { +const Property migration_properties[] = { DEFINE_PROP_BOOL("store-global-state", MigrationState, store_global_state, true), DEFINE_PROP_BOOL("send-configuration", MigrationState, diff --git a/migration/options.h b/migration/options.h index 79084eed0d..a360f93a44 100644 --- a/migration/options.h +++ b/migration/options.h @@ -20,7 +20,7 @@ /* migration properties */ -extern Property migration_properties[]; +extern const Property migration_properties[]; /* capabilities */ From 06be938aa8313a91322984f3226428baef551135 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:29 -0600 Subject: [PATCH 0463/2892] hw/ide: Constify sysbus_ahci_properties Reviewed-by: Bernhard Beschow Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-3-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/ide/ahci-sysbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ide/ahci-sysbus.c b/hw/ide/ahci-sysbus.c index d43db0923f..2432039290 100644 --- a/hw/ide/ahci-sysbus.c +++ b/hw/ide/ahci-sysbus.c @@ -62,7 +62,7 @@ static void sysbus_ahci_realize(DeviceState *dev, Error **errp) ahci_realize(&s->ahci, dev, &address_space_memory); } -static Property sysbus_ahci_properties[] = { +static const Property sysbus_ahci_properties[] = { DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), DEFINE_PROP_END_OF_LIST(), }; From a3367c588287b90c934a63c515e52cb760a28047 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:30 -0600 Subject: [PATCH 0464/2892] target/ppc: Remove empty property list Reviewed-by: Harsh Prateek Bora Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-4-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- target/ppc/cpu_init.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1253dbf622..5e95790def 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7414,11 +7414,6 @@ static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) #endif } -static Property ppc_cpu_properties[] = { - /* add default property here */ - DEFINE_PROP_END_OF_LIST(), -}; - #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" @@ -7468,7 +7463,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_unrealize(dc, ppc_cpu_unrealize, &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; - device_class_set_props(dc, ppc_cpu_properties); resettable_class_set_parent_phases(rc, NULL, ppc_cpu_reset_hold, NULL, &pcc->parent_phases); From 8ce650c28e0c0bb5e39ecd968ce378702c38bf4e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:31 -0600 Subject: [PATCH 0465/2892] target/s390x: Use s390x_cpu_properties for system mode only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the empty property list for user-only mode. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-5-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- target/s390x/cpu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 4702761ca3..263f9e84ed 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -309,8 +309,8 @@ static const gchar *s390_gdb_arch_name(CPUState *cs) return "s390:64-bit"; } +#ifndef CONFIG_USER_ONLY static const Property s390x_cpu_properties[] = { -#if !defined(CONFIG_USER_ONLY) DEFINE_PROP_UINT32("core-id", S390CPU, env.core_id, 0), DEFINE_PROP_INT32("socket-id", S390CPU, env.socket_id, -1), DEFINE_PROP_INT32("book-id", S390CPU, env.book_id, -1), @@ -318,9 +318,9 @@ static const Property s390x_cpu_properties[] = { DEFINE_PROP_BOOL("dedicated", S390CPU, env.dedicated, false), DEFINE_PROP_CPUS390ENTITLEMENT("entitlement", S390CPU, env.entitlement, S390_CPU_ENTITLEMENT_AUTO), -#endif DEFINE_PROP_END_OF_LIST() }; +#endif #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" @@ -388,7 +388,6 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, s390_cpu_realizefn, &scc->parent_realize); - device_class_set_props(dc, s390x_cpu_properties); dc->user_creatable = true; resettable_class_set_parent_phases(rc, NULL, s390_cpu_reset_hold, NULL, @@ -404,6 +403,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY + device_class_set_props(dc, s390x_cpu_properties); s390_cpu_class_init_sysemu(cc); #endif cc->disas_set_info = s390_cpu_disas_set_info; From c32e946e5a269f77f65ce299b1ecdeae7346d04c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:32 -0600 Subject: [PATCH 0466/2892] hw/pci-host/astro: Remove empty Property list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-6-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/pci-host/astro.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index 379095b356..62e9c8acbf 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -461,10 +461,6 @@ static void elroy_pcihost_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), elroy_set_irq, ELROY_IRQS); } -static Property elroy_pcihost_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static const VMStateDescription vmstate_elroy = { .name = "Elroy", .version_id = 1, @@ -490,7 +486,6 @@ static void elroy_pcihost_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, elroy_reset); - device_class_set_props(dc, elroy_pcihost_properties); dc->vmsd = &vmstate_elroy; dc->user_creatable = false; } From 4ee77e89721ce6c11a72af42c1aa5b0c5648fa06 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:33 -0600 Subject: [PATCH 0467/2892] hw/ppc: Only register spapr_nvdimm_properties if CONFIG_LIBPMEM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not register an empty set of properties. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-7-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/ppc/spapr_nvdimm.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 2ef6f29f3d..8bcce4146a 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -884,22 +884,23 @@ static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) vmstate_unregister(NULL, &vmstate_spapr_nvdimm_states, dimm); } -static const Property spapr_nvdimm_properties[] = { #ifdef CONFIG_LIBPMEM +static const Property spapr_nvdimm_properties[] = { DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), -#endif DEFINE_PROP_END_OF_LIST(), }; +#endif static void spapr_nvdimm_class_init(ObjectClass *oc, void *data) { - DeviceClass *dc = DEVICE_CLASS(oc); NVDIMMClass *nvc = NVDIMM_CLASS(oc); nvc->realize = spapr_nvdimm_realize; nvc->unrealize = spapr_nvdimm_unrealize; - device_class_set_props(dc, spapr_nvdimm_properties); +#ifdef CONFIG_LIBPMEM + device_class_set_props(DEVICE_CLASS(oc), spapr_nvdimm_properties); +#endif } static void spapr_nvdimm_init(Object *obj) From 7f6afa997c4ba1eb335c947dbd94784fadd74c8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:34 -0600 Subject: [PATCH 0468/2892] hw/tricore: Remove empty Property lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-8-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/tricore/tc27x_soc.c | 5 ----- hw/tricore/tricore_testdevice.c | 5 ----- 2 files changed, 10 deletions(-) diff --git a/hw/tricore/tc27x_soc.c b/hw/tricore/tc27x_soc.c index ecd92717b5..81bb16d89b 100644 --- a/hw/tricore/tc27x_soc.c +++ b/hw/tricore/tc27x_soc.c @@ -201,16 +201,11 @@ static void tc27x_soc_init(Object *obj) object_initialize_child(obj, "tc27x", &s->cpu, sc->cpu_type); } -static Property tc27x_soc_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void tc27x_soc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = tc27x_soc_realize; - device_class_set_props(dc, tc27x_soc_properties); } static void tc277d_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/tricore/tricore_testdevice.c b/hw/tricore/tricore_testdevice.c index ae95c49565..e60866d76f 100644 --- a/hw/tricore/tricore_testdevice.c +++ b/hw/tricore/tricore_testdevice.c @@ -58,15 +58,10 @@ static void tricore_testdevice_init(Object *obj) "tricore_testdevice", 0x4); } -static Property tricore_testdevice_properties[] = { - DEFINE_PROP_END_OF_LIST() -}; - static void tricore_testdevice_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, tricore_testdevice_properties); device_class_set_legacy_reset(dc, tricore_testdevice_reset); } From 624cdd89afe43c150b2e685378e4f34aaade0b58 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:35 -0600 Subject: [PATCH 0469/2892] hw/s390x: Remove empty Property lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-9-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/s390x/3270-ccw.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index 69e6783ade..3a8930dfd1 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -150,15 +150,10 @@ out_err: g_free(sch); } -static Property emulated_ccw_3270_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, emulated_ccw_3270_properties); dc->realize = emulated_ccw_3270_realize; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); From e16a83a3f0dbd1082af4179f8b129da0236a6fa7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:36 -0600 Subject: [PATCH 0470/2892] hw/xen: Remove empty Property lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in registering no properties. Remove xen_sysdev_class_init entirely, as it did nothing else. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-10-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/xen/xen-legacy-backend.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index e8e1ee4f7d..118c571b3a 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -635,15 +635,10 @@ int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) } -static Property xendev_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void xendev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, xendev_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); /* xen-backend devices can be plugged/unplugged dynamically */ dc->user_creatable = true; @@ -674,22 +669,10 @@ static const TypeInfo xensysbus_info = { } }; -static Property xen_sysdev_properties[] = { - {/* end of property list */}, -}; - -static void xen_sysdev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - device_class_set_props(dc, xen_sysdev_properties); -} - static const TypeInfo xensysdev_info = { .name = TYPE_XENSYSDEV, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), - .class_init = xen_sysdev_class_init, }; static void xenbe_register_types(void) From 3469bd68ea1e14744f61a4bc86b034d8acc33fc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:37 -0600 Subject: [PATCH 0471/2892] hw/sparc: Remove empty Property lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-11-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/sparc/sun4m.c | 5 ----- hw/sparc64/sun4u.c | 5 ----- 2 files changed, 10 deletions(-) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index d52e6a7213..7ec346533e 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -732,15 +732,10 @@ static void prom_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->prom); } -static Property prom_properties[] = { - {/* end of property list */}, -}; - static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, prom_properties); dc->realize = prom_realize; } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 7088ac273e..05b8c7369e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -471,15 +471,10 @@ static void prom_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->prom); } -static Property prom_properties[] = { - {/* end of property list */}, -}; - static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, prom_properties); dc->realize = prom_realize; } From 2885bf891ac2630f6847829e02b984e4a828309b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:38 -0600 Subject: [PATCH 0472/2892] hw/virtio: Remove empty Property lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-12-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/virtio/vdpa-dev-pci.c | 5 ----- hw/virtio/vhost-user-snd-pci.c | 5 ----- 2 files changed, 10 deletions(-) diff --git a/hw/virtio/vdpa-dev-pci.c b/hw/virtio/vdpa-dev-pci.c index 5446e6b393..787926801a 100644 --- a/hw/virtio/vdpa-dev-pci.c +++ b/hw/virtio/vdpa-dev-pci.c @@ -48,10 +48,6 @@ static void vhost_vdpa_device_pci_instance_init(Object *obj) "bootindex"); } -static Property vhost_vdpa_device_pci_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static int vhost_vdpa_device_pci_post_init(VhostVdpaDevice *v, Error **errp) { VhostVdpaDevicePCI *dev = container_of(v, VhostVdpaDevicePCI, vdev); @@ -80,7 +76,6 @@ static void vhost_vdpa_device_pci_class_init(ObjectClass *klass, void *data) VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); set_bit(DEVICE_CATEGORY_MISC, dc->categories); - device_class_set_props(dc, vhost_vdpa_device_pci_properties); k->realize = vhost_vdpa_device_pci_realize; } diff --git a/hw/virtio/vhost-user-snd-pci.c b/hw/virtio/vhost-user-snd-pci.c index d61cfdae63..0cb86b7d85 100644 --- a/hw/virtio/vhost-user-snd-pci.c +++ b/hw/virtio/vhost-user-snd-pci.c @@ -23,10 +23,6 @@ typedef struct VHostUserSoundPCI VHostUserSoundPCI; DECLARE_INSTANCE_CHECKER(VHostUserSoundPCI, VHOST_USER_SND_PCI, TYPE_VHOST_USER_SND_PCI) -static Property vhost_user_snd_pci_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void vhost_user_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VHostUserSoundPCI *dev = VHOST_USER_SND_PCI(vpci_dev); @@ -44,7 +40,6 @@ static void vhost_user_snd_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = vhost_user_snd_pci_realize; set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - device_class_set_props(dc, vhost_user_snd_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ pcidev_k->revision = 0x00; From 588611972f774cacf2e15c73e3d61b54929c0528 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:39 -0600 Subject: [PATCH 0473/2892] include/hw/qdev-core: Detect most empty Property lists at compile time Add a macro expansion of device_class_set_props which can check on the type and size of PROPS before calling the function. Avoid the macro in migration.c because migration_properties is defined externally with indeterminate size. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-13-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 2 +- include/hw/qdev-core.h | 17 +++++++++++++++++ migration/migration.c | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 315196bd85..de618a964a 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1061,7 +1061,7 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop NULL, NULL, (Property *)prop); } -void device_class_set_props(DeviceClass *dc, const Property *props) +void (device_class_set_props)(DeviceClass *dc, const Property *props) { const Property *prop; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 5be9844412..cbce3cf0b4 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -940,9 +940,26 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); * This will add a set of properties to the object. It will fault if * you attempt to add an existing property defined by a parent class. * To modify an inherited property you need to use???? + * + * Validate that @props has at least one Property plus the terminator. + * Validate that the array is terminated at compile-time (with -O2), + * which requires the array to be const. */ void device_class_set_props(DeviceClass *dc, const Property *props); +#define device_class_set_props(dc, props) \ + do { \ + QEMU_BUILD_BUG_ON(sizeof(props) != sizeof(const Property *) && \ + sizeof(props) < 2 * sizeof(Property)); \ + if (sizeof(props) != sizeof(const Property *)) { \ + size_t props_count_ = sizeof(props) / sizeof(Property) - 1; \ + if ((props)[props_count_].name != NULL) { \ + qemu_build_not_reached(); \ + } \ + } \ + (device_class_set_props)((dc), (props)); \ + } while (0) + /** * device_class_set_parent_realize() - set up for chaining realize fns * @dc: The device class diff --git a/migration/migration.c b/migration/migration.c index 8c5bd0a75c..6b3b85d31e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3822,7 +3822,7 @@ static void migration_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->user_creatable = false; - device_class_set_props(dc, migration_properties); + (device_class_set_props)(dc, migration_properties); } static void migration_instance_finalize(Object *obj) From cb9f4b28ee115f14f336a4a87c593fa1685e42df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:40 -0600 Subject: [PATCH 0474/2892] hw/core: Introduce device_class_set_props_n Record the size of the array in DeviceClass.props_count_. Iterate with known count in qdev_prop_walk. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-14-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 39 +++++++++++++++++++++++++++++---------- hw/core/qdev.c | 1 + include/hw/qdev-core.h | 18 ++++++++++++++++++ system/qdev-monitor.c | 15 +++++++-------- 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index de618a964a..31e3072b55 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -749,16 +749,13 @@ const PropertyInfo qdev_prop_array = { /* --- public helpers --- */ -static const Property *qdev_prop_walk(const Property *props, const char *name) +static const Property *qdev_prop_walk(DeviceClass *cls, const char *name) { - if (!props) { - return NULL; - } - while (props->name) { - if (strcmp(props->name, name) == 0) { - return props; + for (int i = 0, n = cls->props_count_; i < n; ++i) { + const Property *prop = &cls->props_[i]; + if (strcmp(prop->name, name) == 0) { + return prop; } - props++; } return NULL; } @@ -771,7 +768,7 @@ static const Property *qdev_prop_find(DeviceState *dev, const char *name) /* device properties */ class = object_get_class(OBJECT(dev)); do { - prop = qdev_prop_walk(DEVICE_CLASS(class)->props_, name); + prop = qdev_prop_walk(DEVICE_CLASS(class), name); if (prop) { return prop; } @@ -1064,9 +1061,31 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop void (device_class_set_props)(DeviceClass *dc, const Property *props) { const Property *prop; + size_t n; dc->props_ = props; - for (prop = props; prop && prop->name; prop++) { + for (prop = props, n = 0; prop && prop->name; prop++, n++) { + qdev_class_add_legacy_property(dc, prop); + qdev_class_add_property(dc, prop->name, prop); + } + + /* We used a hole in DeviceClass because that's still a lot. */ + assert(n <= UINT16_MAX); + dc->props_count_ = n; +} + +void device_class_set_props_n(DeviceClass *dc, const Property *props, size_t n) +{ + /* We used a hole in DeviceClass because that's still a lot. */ + assert(n <= UINT16_MAX); + assert(n != 0); + + dc->props_ = props; + dc->props_count_ = n; + + for (size_t i = 0; i < n; ++i) { + const Property *prop = &props[i]; + assert(prop->name); qdev_class_add_legacy_property(dc, prop); qdev_class_add_property(dc, prop->name, prop); } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 5f13111b77..57c1d9df3a 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -703,6 +703,7 @@ static void device_class_base_init(ObjectClass *class, void *data) * so do not propagate them to the subclasses. */ klass->props_ = NULL; + klass->props_count_ = 0; } static void device_unparent(Object *obj) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index cbce3cf0b4..e9b4891f55 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -138,6 +138,12 @@ struct DeviceClass { */ const Property *props_; + /** + * @props_count_: number of elements in @props_; should only be + * assigned by using device_class_set_props(). + */ + uint16_t props_count_; + /** * @user_creatable: Can user instantiate with -device / device_add? * @@ -960,6 +966,18 @@ void device_class_set_props(DeviceClass *dc, const Property *props); (device_class_set_props)((dc), (props)); \ } while (0) +/** + * device_class_set_props_n(): add a set of properties to an device + * @dc: the parent DeviceClass all devices inherit + * @props: an array of properties, not terminated by DEFINE_PROP_END_OF_LIST. + * @n: ARRAY_SIZE(@props) + * + * This will add a set of properties to the object. It will fault if + * you attempt to add an existing property defined by a parent class. + * To modify an inherited property you need to use???? + */ +void device_class_set_props_n(DeviceClass *dc, const Property *props, size_t n); + /** * device_class_set_parent_realize() - set up for chaining realize fns * @dc: The device class diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 03ae610649..6831fffd9d 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -752,19 +752,18 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__) -static void qdev_print_props(Monitor *mon, DeviceState *dev, const Property *props, +static void qdev_print_props(Monitor *mon, DeviceState *dev, DeviceClass *dc, int indent) { - if (!props) - return; - for (; props->name; props++) { + for (int i = 0, n = dc->props_count_; i < n; ++i) { + const Property *prop = &dc->props_[i]; char *value; - char *legacy_name = g_strdup_printf("legacy-%s", props->name); + char *legacy_name = g_strdup_printf("legacy-%s", prop->name); if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) { value = object_property_get_str(OBJECT(dev), legacy_name, NULL); } else { - value = object_property_print(OBJECT(dev), props->name, true, + value = object_property_print(OBJECT(dev), prop->name, true, NULL); } g_free(legacy_name); @@ -772,7 +771,7 @@ static void qdev_print_props(Monitor *mon, DeviceState *dev, const Property *pro if (!value) { continue; } - qdev_printf("%s = %s\n", props->name, + qdev_printf("%s = %s\n", prop->name, *value ? value : ""); g_free(value); } @@ -812,7 +811,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent) } class = object_get_class(OBJECT(dev)); do { - qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent); + qdev_print_props(mon, dev, DEVICE_CLASS(class), indent); class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); bus_print_dev(dev->parent_bus, mon, dev, indent); From 0017f5713a20a2b6f42c3d5c255c4ab066cdcd87 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:41 -0600 Subject: [PATCH 0475/2892] migration: Use device_class_set_props_n Export the migration_properties array size from options.c; use that to feed device_class_set_props_n. We must remove DEFINE_PROP_END_OF_LIST so the count is correct. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-15-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- migration/migration.c | 3 ++- migration/options.c | 2 +- migration/options.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 6b3b85d31e..d23d392685 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3822,7 +3822,8 @@ static void migration_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->user_creatable = false; - (device_class_set_props)(dc, migration_properties); + device_class_set_props_n(dc, migration_properties, + migration_properties_count); } static void migration_instance_finalize(Object *obj) diff --git a/migration/options.c b/migration/options.c index 24cc8471aa..70ff56535a 100644 --- a/migration/options.c +++ b/migration/options.c @@ -196,8 +196,8 @@ const Property migration_properties[] = { MIGRATION_CAPABILITY_SWITCHOVER_ACK), DEFINE_PROP_MIG_CAP("x-dirty-limit", MIGRATION_CAPABILITY_DIRTY_LIMIT), DEFINE_PROP_MIG_CAP("mapped-ram", MIGRATION_CAPABILITY_MAPPED_RAM), - DEFINE_PROP_END_OF_LIST(), }; +const size_t migration_properties_count = ARRAY_SIZE(migration_properties); bool migrate_auto_converge(void) { diff --git a/migration/options.h b/migration/options.h index a360f93a44..762be4e641 100644 --- a/migration/options.h +++ b/migration/options.h @@ -21,6 +21,7 @@ /* migration properties */ extern const Property migration_properties[]; +extern const size_t migration_properties_count; /* capabilities */ From 0e9cb0b4108664752e4cfa27951c707718f031bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:42 -0600 Subject: [PATCH 0476/2892] hw/scsi/megasas: Use device_class_set_props_n We must remove DEFINE_PROP_END_OF_LIST so the count is correct. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-16-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 8323cd18e3..7f012c218b 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2459,7 +2459,6 @@ static const Property megasas_properties_gen1[] = { DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), - DEFINE_PROP_END_OF_LIST(), }; static const Property megasas_properties_gen2[] = { @@ -2473,7 +2472,6 @@ static const Property megasas_properties_gen2[] = { DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), - DEFINE_PROP_END_OF_LIST(), }; typedef struct MegasasInfo { @@ -2488,6 +2486,7 @@ typedef struct MegasasInfo { int osts; const VMStateDescription *vmsd; const Property *props; + size_t props_count; InterfaceInfo *interfaces; } MegasasInfo; @@ -2504,6 +2503,7 @@ static struct MegasasInfo megasas_devices[] = { .osts = MFI_1078_RM | 1, .vmsd = &vmstate_megasas_gen1, .props = megasas_properties_gen1, + .props_count = ARRAY_SIZE(megasas_properties_gen1), .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, @@ -2520,6 +2520,7 @@ static struct MegasasInfo megasas_devices[] = { .osts = MFI_GEN2_RM, .vmsd = &vmstate_megasas_gen2, .props = megasas_properties_gen2, + .props_count = ARRAY_SIZE(megasas_properties_gen2), .interfaces = (InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } @@ -2546,7 +2547,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) e->osts = info->osts; e->product_name = info->product_name; e->product_version = info->product_version; - device_class_set_props(dc, info->props); + device_class_set_props_n(dc, info->props, info->props_count); device_class_set_legacy_reset(dc, megasas_scsi_reset); dc->vmsd = info->vmsd; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); From 662cede910b68d52c7a6c6c972070784f241c0d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:43 -0600 Subject: [PATCH 0477/2892] hw/arm/armsse: Use device_class_set_props_n We must remove DEFINE_PROP_END_OF_LIST so the count is correct. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-17-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/arm/armsse.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 1cd6b4a4b2..ffd732f806 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -72,6 +72,7 @@ struct ARMSSEInfo { bool has_cpu_pwrctrl; bool has_sse_counter; bool has_tcms; + uint8_t props_count; const Property *props; const ARMSSEDeviceInfo *devinfo; const bool *irq_is_common; @@ -87,7 +88,6 @@ static const Property iotkit_properties[] = { DEFINE_PROP_BOOL("CPU0_DSP", ARMSSE, cpu_dsp[0], true), DEFINE_PROP_UINT32("CPU0_MPU_NS", ARMSSE, cpu_mpu_ns[0], 8), DEFINE_PROP_UINT32("CPU0_MPU_S", ARMSSE, cpu_mpu_s[0], 8), - DEFINE_PROP_END_OF_LIST() }; static const Property sse200_properties[] = { @@ -104,7 +104,6 @@ static const Property sse200_properties[] = { DEFINE_PROP_UINT32("CPU0_MPU_S", ARMSSE, cpu_mpu_s[0], 8), DEFINE_PROP_UINT32("CPU1_MPU_NS", ARMSSE, cpu_mpu_ns[1], 8), DEFINE_PROP_UINT32("CPU1_MPU_S", ARMSSE, cpu_mpu_s[1], 8), - DEFINE_PROP_END_OF_LIST() }; static const Property sse300_properties[] = { @@ -117,7 +116,6 @@ static const Property sse300_properties[] = { DEFINE_PROP_BOOL("CPU0_DSP", ARMSSE, cpu_dsp[0], true), DEFINE_PROP_UINT32("CPU0_MPU_NS", ARMSSE, cpu_mpu_ns[0], 8), DEFINE_PROP_UINT32("CPU0_MPU_S", ARMSSE, cpu_mpu_s[0], 8), - DEFINE_PROP_END_OF_LIST() }; static const ARMSSEDeviceInfo iotkit_devices[] = { @@ -528,6 +526,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_sse_counter = false, .has_tcms = false, .props = iotkit_properties, + .props_count = ARRAY_SIZE(iotkit_properties), .devinfo = iotkit_devices, .irq_is_common = sse200_irq_is_common, }, @@ -549,6 +548,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_sse_counter = false, .has_tcms = false, .props = sse200_properties, + .props_count = ARRAY_SIZE(sse200_properties), .devinfo = sse200_devices, .irq_is_common = sse200_irq_is_common, }, @@ -570,6 +570,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_sse_counter = true, .has_tcms = true, .props = sse300_properties, + .props_count = ARRAY_SIZE(sse300_properties), .devinfo = sse300_devices, .irq_is_common = sse300_irq_is_common, }, @@ -1699,7 +1700,7 @@ static void armsse_class_init(ObjectClass *klass, void *data) dc->realize = armsse_realize; dc->vmsd = &armsse_vmstate; - device_class_set_props(dc, info->props); + device_class_set_props_n(dc, info->props, info->props_count); device_class_set_legacy_reset(dc, armsse_reset); iic->check = armsse_idau_check; asc->info = info; From 5f9976486970b0fec50ff4c07da7af620cd7d0a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:44 -0600 Subject: [PATCH 0478/2892] rust/qemu-api: Use device_class_set_props_n This means we can update declare_properties to drop the zero terminator at the end of the array as well. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-18-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/device_class.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index 03d03feee8..c98f0b2c7d 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -7,7 +7,6 @@ use std::{ffi::CStr, os::raw::c_void}; use crate::{ bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, prelude::*, - zeroable::Zeroable, }; /// Trait providing the contents of [`DeviceClass`]. @@ -31,7 +30,7 @@ pub trait DeviceImpl { /// device. Not a `const` because referencing statics in constants /// is unstable until Rust 1.83.0. fn properties() -> &'static [Property] { - &[Zeroable::ZERO; 1] + &[] } /// A `VMStateDescription` providing the migration format for the device @@ -87,7 +86,10 @@ pub unsafe extern "C" fn rust_device_class_init( if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; } - bindings::device_class_set_props(dc, ::properties().as_ptr()); + let prop = ::properties(); + if !prop.is_empty() { + bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + } } } @@ -134,7 +136,7 @@ macro_rules! define_property { macro_rules! declare_properties { ($ident:ident, $($prop:expr),*$(,)*) => { pub static $ident: [$crate::bindings::Property; { - let mut len = 1; + let mut len = 0; $({ _ = stringify!($prop); len += 1; @@ -142,7 +144,6 @@ macro_rules! declare_properties { len }] = [ $($prop),*, - $crate::zeroable::Zeroable::ZERO, ]; }; } From 1088d41795101479e2d88f1e6140071732f9bdb3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:45 -0600 Subject: [PATCH 0479/2892] hw/core: Remove device_class_set_props function All uses of device_class_set_props() are now using arrays. Validate this compile-time in the device_class_set_props macro and call device_class_set_props_n using the known size of the array. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-19-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 16 ---------------- include/hw/qdev-core.h | 16 ++++++---------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 31e3072b55..a3d49e2020 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1058,22 +1058,6 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop NULL, NULL, (Property *)prop); } -void (device_class_set_props)(DeviceClass *dc, const Property *props) -{ - const Property *prop; - size_t n; - - dc->props_ = props; - for (prop = props, n = 0; prop && prop->name; prop++, n++) { - qdev_class_add_legacy_property(dc, prop); - qdev_class_add_property(dc, prop->name, prop); - } - - /* We used a hole in DeviceClass because that's still a lot. */ - assert(n <= UINT16_MAX); - dc->props_count_ = n; -} - void device_class_set_props_n(DeviceClass *dc, const Property *props, size_t n) { /* We used a hole in DeviceClass because that's still a lot. */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index e9b4891f55..afa667b68f 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -948,22 +948,18 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); * To modify an inherited property you need to use???? * * Validate that @props has at least one Property plus the terminator. + * Validate that @props is an array, not a pointer, via ARRAY_SIZE. * Validate that the array is terminated at compile-time (with -O2), * which requires the array to be const. */ -void device_class_set_props(DeviceClass *dc, const Property *props); - #define device_class_set_props(dc, props) \ do { \ - QEMU_BUILD_BUG_ON(sizeof(props) != sizeof(const Property *) && \ - sizeof(props) < 2 * sizeof(Property)); \ - if (sizeof(props) != sizeof(const Property *)) { \ - size_t props_count_ = sizeof(props) / sizeof(Property) - 1; \ - if ((props)[props_count_].name != NULL) { \ - qemu_build_not_reached(); \ - } \ + QEMU_BUILD_BUG_ON(sizeof(props) == 0); \ + size_t props_count_ = ARRAY_SIZE(props) - 1; \ + if ((props)[props_count_].name != NULL) { \ + qemu_build_not_reached(); \ } \ - (device_class_set_props)((dc), (props)); \ + device_class_set_props_n((dc), (props), props_count_); \ } while (0) /** From ebe9685f12bb30ebd35575d31c6b6e0efad3a2bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:46 -0600 Subject: [PATCH 0480/2892] target/riscv: Do not abuse DEFINE_PROP_END_OF_LIST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are not arrays of Property and had no business using DEFINE_PROP_END_OF_LIST. Use plain { } instead. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-20-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4329015076..7aa041f57a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -213,7 +213,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(xtheadsync, PRIV_VERSION_1_11_0, ext_xtheadsync), ISA_EXT_DATA_ENTRY(xventanacondops, PRIV_VERSION_1_12_0, ext_XVentanaCondOps), - DEFINE_PROP_END_OF_LIST(), + { }, }; bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset) @@ -1575,7 +1575,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zvksc", ext_zvksc, false), MULTI_EXT_CFG_BOOL("zvksg", ext_zvksg, false), - DEFINE_PROP_END_OF_LIST(), + { }, }; const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { @@ -1592,12 +1592,12 @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { MULTI_EXT_CFG_BOOL("xtheadsync", ext_xtheadsync, false), MULTI_EXT_CFG_BOOL("xventanacondops", ext_XVentanaCondOps, false), - DEFINE_PROP_END_OF_LIST(), + { }, }; /* These are experimental so mark with 'x-' */ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { - DEFINE_PROP_END_OF_LIST(), + { }, }; /* @@ -1610,7 +1610,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), - DEFINE_PROP_END_OF_LIST(), + { }, }; /* Deprecated entries marked for future removal */ @@ -1627,7 +1627,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { MULTI_EXT_CFG_BOOL("Zve64f", ext_zve64f, false), MULTI_EXT_CFG_BOOL("Zve64d", ext_zve64d, false), - DEFINE_PROP_END_OF_LIST(), + { }, }; static void cpu_set_prop_err(RISCVCPU *cpu, const char *propname, From 5fcabe628b8140691dab834a22be549d242b24bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:47 -0600 Subject: [PATCH 0481/2892] include/hw/qdev-properties: Remove DEFINE_PROP_END_OF_LIST Now that all of the Property arrays are counted, we can remove the terminator object from each array. Update the assertions in device_class_set_props to match. With struct Property being 88 bytes, this was a rather large form of terminator. Saves 30k from qemu-system-aarch64. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-21-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- cpu-target.c | 1 - docs/devel/migration/compatibility.rst | 1 - docs/devel/virtio-backends.rst | 1 - hw/9pfs/virtio-9p-device.c | 1 - hw/acpi/erst.c | 1 - hw/acpi/generic_event_device.c | 1 - hw/acpi/piix4.c | 1 - hw/acpi/vmgenid.c | 1 - hw/adc/aspeed_adc.c | 1 - hw/adc/npcm7xx_adc.c | 1 - hw/arm/armv7m.c | 2 -- hw/arm/aspeed_soc_common.c | 1 - hw/arm/fsl-imx25.c | 1 - hw/arm/fsl-imx6.c | 1 - hw/arm/fsl-imx6ul.c | 1 - hw/arm/fsl-imx7.c | 1 - hw/arm/integratorcp.c | 1 - hw/arm/msf2-soc.c | 1 - hw/arm/npcm7xx.c | 1 - hw/arm/nrf51_soc.c | 1 - hw/arm/smmu-common.c | 1 - hw/arm/smmuv3.c | 1 - hw/arm/stellaris.c | 1 - hw/arm/strongarm.c | 1 - hw/arm/xlnx-versal.c | 1 - hw/arm/xlnx-zynqmp.c | 1 - hw/audio/ac97.c | 1 - hw/audio/adlib.c | 1 - hw/audio/asc.c | 1 - hw/audio/cs4231a.c | 1 - hw/audio/es1370.c | 1 - hw/audio/gus.c | 1 - hw/audio/hda-codec.c | 1 - hw/audio/intel-hda.c | 2 -- hw/audio/pcspk.c | 1 - hw/audio/pl041.c | 1 - hw/audio/sb16.c | 1 - hw/audio/via-ac97.c | 1 - hw/audio/virtio-snd-pci.c | 1 - hw/audio/virtio-snd.c | 1 - hw/audio/wm8750.c | 1 - hw/avr/atmega.c | 1 - hw/block/fdc-isa.c | 1 - hw/block/fdc-sysbus.c | 2 -- hw/block/fdc.c | 1 - hw/block/m25p80.c | 1 - hw/block/nand.c | 1 - hw/block/pflash_cfi01.c | 1 - hw/block/pflash_cfi02.c | 1 - hw/block/swim.c | 1 - hw/block/vhost-user-blk.c | 1 - hw/block/virtio-blk.c | 1 - hw/block/xen-block.c | 1 - hw/char/avr_usart.c | 1 - hw/char/bcm2835_aux.c | 1 - hw/char/cadence_uart.c | 1 - hw/char/cmsdk-apb-uart.c | 1 - hw/char/debugcon.c | 1 - hw/char/digic-uart.c | 1 - hw/char/escc.c | 1 - hw/char/exynos4210_uart.c | 1 - hw/char/goldfish_tty.c | 1 - hw/char/grlib_apbuart.c | 1 - hw/char/ibex_uart.c | 1 - hw/char/imx_serial.c | 1 - hw/char/ipoctal232.c | 1 - hw/char/mcf_uart.c | 1 - hw/char/nrf51_uart.c | 1 - hw/char/parallel.c | 1 - hw/char/pl011.c | 1 - hw/char/renesas_sci.c | 1 - hw/char/sclpconsole-lm.c | 1 - hw/char/sclpconsole.c | 1 - hw/char/serial-isa.c | 1 - hw/char/serial-mm.c | 1 - hw/char/serial-pci-multi.c | 2 -- hw/char/serial-pci.c | 1 - hw/char/serial.c | 1 - hw/char/sh_serial.c | 1 - hw/char/shakti_uart.c | 1 - hw/char/sifive_uart.c | 1 - hw/char/spapr_vty.c | 1 - hw/char/stm32f2xx_usart.c | 1 - hw/char/stm32l4x5_usart.c | 1 - hw/char/terminal3270.c | 1 - hw/char/virtio-console.c | 1 - hw/char/virtio-serial-bus.c | 2 -- hw/char/xen_console.c | 1 - hw/char/xilinx_uartlite.c | 1 - hw/core/generic-loader.c | 1 - hw/core/guest-loader.c | 1 - hw/core/or-irq.c | 1 - hw/core/platform-bus.c | 1 - hw/core/split-irq.c | 1 - hw/cpu/a15mpcore.c | 1 - hw/cpu/a9mpcore.c | 1 - hw/cpu/arm11mpcore.c | 1 - hw/cpu/cluster.c | 1 - hw/cpu/realview_mpcore.c | 1 - hw/cxl/switch-mailbox-cci.c | 1 - hw/display/artist.c | 1 - hw/display/ati.c | 1 - hw/display/bcm2835_fb.c | 1 - hw/display/bochs-display.c | 1 - hw/display/cg3.c | 1 - hw/display/cirrus_vga.c | 1 - hw/display/cirrus_vga_isa.c | 1 - hw/display/exynos4210_fimd.c | 1 - hw/display/g364fb.c | 1 - hw/display/i2c-ddc.c | 1 - hw/display/macfb.c | 2 -- hw/display/pl110.c | 1 - hw/display/qxl.c | 1 - hw/display/ramfb-standalone.c | 1 - hw/display/sm501.c | 2 -- hw/display/tcx.c | 1 - hw/display/vga-isa.c | 1 - hw/display/vga-mmio.c | 1 - hw/display/vga-pci.c | 2 -- hw/display/vhost-user-gpu.c | 1 - hw/display/virtio-gpu-gl.c | 1 - hw/display/virtio-gpu-pci.c | 1 - hw/display/virtio-gpu-rutabaga.c | 1 - hw/display/virtio-gpu.c | 1 - hw/display/virtio-vga.c | 1 - hw/display/vmware_vga.c | 1 - hw/display/xlnx_dp.c | 1 - hw/dma/i82374.c | 1 - hw/dma/i8257.c | 1 - hw/dma/pl080.c | 1 - hw/dma/pl330.c | 2 -- hw/dma/xilinx_axidma.c | 1 - hw/dma/xlnx-zdma.c | 1 - hw/dma/xlnx_csu_dma.c | 1 - hw/gpio/imx_gpio.c | 1 - hw/gpio/npcm7xx_gpio.c | 1 - hw/gpio/omap_gpio.c | 1 - hw/gpio/pca9552.c | 1 - hw/gpio/pca9554.c | 1 - hw/gpio/pl061.c | 1 - hw/gpio/sifive_gpio.c | 1 - hw/gpio/stm32l4x5_gpio.c | 1 - hw/hyperv/hv-balloon.c | 2 -- hw/hyperv/syndbg.c | 1 - hw/hyperv/vmbus.c | 2 -- hw/i2c/aspeed_i2c.c | 2 -- hw/i2c/core.c | 1 - hw/i2c/i2c_mux_pca954x.c | 1 - hw/i2c/omap_i2c.c | 1 - hw/i386/amd_iommu.c | 1 - hw/i386/intel_iommu.c | 1 - hw/i386/kvm/clock.c | 1 - hw/i386/kvm/i8254.c | 1 - hw/i386/kvm/ioapic.c | 1 - hw/i386/sgx-epc.c | 1 - hw/i386/vmmouse.c | 1 - hw/i386/vmport.c | 2 -- hw/i386/x86-iommu.c | 1 - hw/i386/xen/xen_pvdevice.c | 1 - hw/ide/ahci-sysbus.c | 1 - hw/ide/cf.c | 1 - hw/ide/cmd646.c | 1 - hw/ide/ide-dev.c | 3 --- hw/ide/isa.c | 1 - hw/ide/macio.c | 1 - hw/ide/mmio.c | 1 - hw/input/pckbd.c | 2 -- hw/input/stellaris_gamepad.c | 1 - hw/input/virtio-input-hid.c | 3 --- hw/input/virtio-input-host.c | 1 - hw/input/virtio-input.c | 1 - hw/intc/apic_common.c | 1 - hw/intc/arm_gic_common.c | 1 - hw/intc/arm_gicv2m.c | 1 - hw/intc/arm_gicv3_common.c | 1 - hw/intc/arm_gicv3_its.c | 1 - hw/intc/arm_gicv3_its_kvm.c | 1 - hw/intc/armv7m_nvic.c | 1 - hw/intc/exynos4210_combiner.c | 1 - hw/intc/exynos4210_gic.c | 1 - hw/intc/goldfish_pic.c | 1 - hw/intc/grlib_irqmp.c | 1 - hw/intc/i8259_common.c | 1 - hw/intc/ioapic.c | 1 - hw/intc/loongarch_extioi.c | 1 - hw/intc/loongarch_pch_msi.c | 1 - hw/intc/loongarch_pch_pic.c | 1 - hw/intc/loongson_ipi_common.c | 1 - hw/intc/m68k_irqc.c | 1 - hw/intc/mips_gic.c | 1 - hw/intc/omap_intc.c | 1 - hw/intc/ompic.c | 1 - hw/intc/openpic.c | 1 - hw/intc/openpic_kvm.c | 1 - hw/intc/pnv_xive.c | 1 - hw/intc/pnv_xive2.c | 1 - hw/intc/ppc-uic.c | 1 - hw/intc/riscv_aclint.c | 2 -- hw/intc/riscv_aplic.c | 1 - hw/intc/riscv_imsic.c | 1 - hw/intc/rx_icu.c | 1 - hw/intc/s390_flic.c | 2 -- hw/intc/sifive_plic.c | 1 - hw/intc/spapr_xive.c | 1 - hw/intc/xics.c | 2 -- hw/intc/xilinx_intc.c | 1 - hw/intc/xive.c | 4 ---- hw/intc/xive2.c | 2 -- hw/intc/xlnx-pmu-iomod-intc.c | 1 - hw/ipack/ipack.c | 1 - hw/ipmi/ipmi.c | 1 - hw/ipmi/ipmi_bmc_extern.c | 1 - hw/ipmi/ipmi_bmc_sim.c | 1 - hw/ipmi/isa_ipmi_bt.c | 1 - hw/ipmi/isa_ipmi_kcs.c | 1 - hw/isa/lpc_ich9.c | 1 - hw/isa/pc87312.c | 1 - hw/isa/piix.c | 1 - hw/m68k/mcf5206.c | 1 - hw/m68k/mcf_intc.c | 1 - hw/m68k/next-cube.c | 1 - hw/m68k/q800-glue.c | 1 - hw/mem/cxl_type3.c | 1 - hw/mem/nvdimm.c | 1 - hw/mem/pc-dimm.c | 1 - hw/mem/sparse-mem.c | 1 - hw/mips/cps.c | 1 - hw/misc/a9scu.c | 1 - hw/misc/allwinner-h3-dramc.c | 1 - hw/misc/allwinner-r40-dramc.c | 1 - hw/misc/allwinner-sid.c | 1 - hw/misc/applesmc.c | 1 - hw/misc/arm11scu.c | 1 - hw/misc/arm_l2x0.c | 1 - hw/misc/arm_sysctl.c | 1 - hw/misc/armsse-cpuid.c | 1 - hw/misc/aspeed_hace.c | 1 - hw/misc/aspeed_i3c.c | 1 - hw/misc/aspeed_lpc.c | 1 - hw/misc/aspeed_sbc.c | 1 - hw/misc/aspeed_scu.c | 1 - hw/misc/aspeed_sdmc.c | 1 - hw/misc/bcm2835_cprman.c | 1 - hw/misc/bcm2835_property.c | 1 - hw/misc/debugexit.c | 1 - hw/misc/eccmemctl.c | 1 - hw/misc/empty_slot.c | 1 - hw/misc/iotkit-secctl.c | 1 - hw/misc/iotkit-sysctl.c | 1 - hw/misc/iotkit-sysinfo.c | 1 - hw/misc/ivshmem.c | 2 -- hw/misc/led.c | 1 - hw/misc/mac_via.c | 1 - hw/misc/macio/cuda.c | 1 - hw/misc/macio/macio.c | 2 -- hw/misc/macio/pmu.c | 1 - hw/misc/mips_cmgcr.c | 1 - hw/misc/mips_cpc.c | 1 - hw/misc/mips_itu.c | 1 - hw/misc/mos6522.c | 1 - hw/misc/mps2-fpgaio.c | 1 - hw/misc/mps2-scc.c | 1 - hw/misc/msf2-sysreg.c | 1 - hw/misc/npcm7xx_gcr.c | 1 - hw/misc/nrf51_rng.c | 1 - hw/misc/pci-testdev.c | 1 - hw/misc/pvpanic-isa.c | 1 - hw/misc/pvpanic-pci.c | 1 - hw/misc/sifive_e_aon.c | 1 - hw/misc/sifive_u_otp.c | 1 - hw/misc/stm32l4x5_rcc.c | 1 - hw/misc/tz-mpc.c | 1 - hw/misc/tz-msc.c | 1 - hw/misc/tz-ppc.c | 1 - hw/misc/unimp.c | 1 - hw/misc/xlnx-versal-cframe-reg.c | 2 -- hw/misc/xlnx-versal-cfu.c | 2 -- hw/misc/xlnx-versal-trng.c | 2 -- hw/misc/xlnx-versal-xramc.c | 1 - hw/misc/zynq_slcr.c | 1 - hw/net/allwinner-sun8i-emac.c | 1 - hw/net/allwinner_emac.c | 1 - hw/net/cadence_gem.c | 1 - hw/net/can/xlnx-versal-canfd.c | 1 - hw/net/can/xlnx-zynqmp-can.c | 1 - hw/net/dp8393x.c | 1 - hw/net/e1000.c | 1 - hw/net/e1000e.c | 1 - hw/net/eepro100.c | 1 - hw/net/fsl_etsec/etsec.c | 1 - hw/net/ftgmac100.c | 2 -- hw/net/igb.c | 1 - hw/net/imx_fec.c | 1 - hw/net/lan9118.c | 1 - hw/net/lance.c | 1 - hw/net/lasi_i82596.c | 1 - hw/net/mcf_fec.c | 1 - hw/net/mipsnet.c | 1 - hw/net/msf2-emac.c | 1 - hw/net/mv88w8618_eth.c | 1 - hw/net/ne2000-isa.c | 1 - hw/net/ne2000-pci.c | 1 - hw/net/npcm7xx_emc.c | 1 - hw/net/npcm_gmac.c | 1 - hw/net/opencores_eth.c | 1 - hw/net/pcnet-pci.c | 1 - hw/net/rocker/rocker.c | 1 - hw/net/rtl8139.c | 1 - hw/net/smc91c111.c | 1 - hw/net/spapr_llan.c | 1 - hw/net/stellaris_enet.c | 1 - hw/net/sungem.c | 1 - hw/net/sunhme.c | 1 - hw/net/tulip.c | 1 - hw/net/virtio-net.c | 1 - hw/net/vmxnet3.c | 1 - hw/net/xen_nic.c | 1 - hw/net/xgmac.c | 1 - hw/net/xilinx_axienet.c | 1 - hw/net/xilinx_ethlite.c | 1 - hw/nubus/nubus-bridge.c | 1 - hw/nubus/nubus-device.c | 1 - hw/nvme/ctrl.c | 1 - hw/nvme/ns.c | 1 - hw/nvme/subsys.c | 1 - hw/nvram/ds1225y.c | 1 - hw/nvram/eeprom_at24c.c | 1 - hw/nvram/fw_cfg.c | 3 --- hw/nvram/mac_nvram.c | 1 - hw/nvram/nrf51_nvm.c | 1 - hw/nvram/spapr_nvram.c | 1 - hw/nvram/xlnx-bbram.c | 1 - hw/nvram/xlnx-efuse.c | 1 - hw/nvram/xlnx-versal-efuse-cache.c | 2 -- hw/nvram/xlnx-versal-efuse-ctrl.c | 2 -- hw/nvram/xlnx-zynqmp-efuse.c | 2 -- hw/pci-bridge/cxl_downstream.c | 1 - hw/pci-bridge/cxl_root_port.c | 1 - hw/pci-bridge/cxl_upstream.c | 1 - hw/pci-bridge/gen_pcie_root_port.c | 1 - hw/pci-bridge/pci_bridge_dev.c | 1 - hw/pci-bridge/pci_expander_bridge.c | 2 -- hw/pci-bridge/pcie_pci_bridge.c | 1 - hw/pci-bridge/pcie_root_port.c | 1 - hw/pci-bridge/xio3130_downstream.c | 1 - hw/pci-host/dino.c | 1 - hw/pci-host/gpex.c | 1 - hw/pci-host/grackle.c | 1 - hw/pci-host/gt64120.c | 1 - hw/pci-host/i440fx.c | 1 - hw/pci-host/mv64361.c | 1 - hw/pci-host/pnv_phb.c | 4 ---- hw/pci-host/pnv_phb3.c | 1 - hw/pci-host/pnv_phb4.c | 1 - hw/pci-host/pnv_phb4_pec.c | 1 - hw/pci-host/ppce500.c | 1 - hw/pci-host/q35.c | 2 -- hw/pci-host/raven.c | 1 - hw/pci-host/sabre.c | 1 - hw/pci-host/uninorth.c | 1 - hw/pci-host/versatile.c | 1 - hw/pci-host/xilinx-pcie.c | 1 - hw/pci/pci.c | 1 - hw/pci/pci_bridge.c | 1 - hw/pci/pci_host.c | 1 - hw/pci/pcie_port.c | 2 -- hw/ppc/pnv.c | 1 - hw/ppc/pnv_adu.c | 1 - hw/ppc/pnv_chiptod.c | 1 - hw/ppc/pnv_core.c | 2 -- hw/ppc/pnv_homer.c | 1 - hw/ppc/pnv_i2c.c | 1 - hw/ppc/pnv_lpc.c | 1 - hw/ppc/pnv_pnor.c | 1 - hw/ppc/pnv_psi.c | 1 - hw/ppc/ppc405_uc.c | 1 - hw/ppc/ppc440_uc.c | 1 - hw/ppc/ppc4xx_devs.c | 2 -- hw/ppc/ppc4xx_sdram.c | 2 -- hw/ppc/prep_systemio.c | 1 - hw/ppc/rs6000_mc.c | 1 - hw/ppc/spapr_cpu_core.c | 1 - hw/ppc/spapr_nvdimm.c | 1 - hw/ppc/spapr_pci.c | 1 - hw/ppc/spapr_rng.c | 1 - hw/ppc/spapr_tpm_proxy.c | 1 - hw/remote/proxy.c | 1 - hw/riscv/opentitan.c | 1 - hw/riscv/riscv-iommu-pci.c | 1 - hw/riscv/riscv-iommu.c | 1 - hw/riscv/riscv_hart.c | 1 - hw/riscv/sifive_u.c | 1 - hw/rtc/allwinner-rtc.c | 1 - hw/rtc/goldfish_rtc.c | 1 - hw/rtc/m48t59-isa.c | 1 - hw/rtc/m48t59.c | 1 - hw/rtc/mc146818rtc.c | 1 - hw/rtc/pl031.c | 1 - hw/rx/rx62n.c | 1 - hw/s390x/ccw-device.c | 1 - hw/s390x/css-bridge.c | 1 - hw/s390x/ipl.c | 1 - hw/s390x/s390-pci-bus.c | 1 - hw/s390x/s390-skeys.c | 1 - hw/s390x/s390-stattrib.c | 1 - hw/s390x/vhost-scsi-ccw.c | 1 - hw/s390x/vhost-user-fs-ccw.c | 1 - hw/s390x/vhost-vsock-ccw.c | 1 - hw/s390x/virtio-ccw-9p.c | 1 - hw/s390x/virtio-ccw-balloon.c | 1 - hw/s390x/virtio-ccw-blk.c | 1 - hw/s390x/virtio-ccw-crypto.c | 1 - hw/s390x/virtio-ccw-gpu.c | 1 - hw/s390x/virtio-ccw-input.c | 1 - hw/s390x/virtio-ccw-net.c | 1 - hw/s390x/virtio-ccw-rng.c | 1 - hw/s390x/virtio-ccw-scsi.c | 1 - hw/s390x/virtio-ccw-serial.c | 1 - hw/scsi/mptsas.c | 1 - hw/scsi/scsi-bus.c | 1 - hw/scsi/scsi-disk.c | 3 --- hw/scsi/scsi-generic.c | 1 - hw/scsi/spapr_vscsi.c | 1 - hw/scsi/vhost-scsi.c | 1 - hw/scsi/vhost-user-scsi.c | 1 - hw/scsi/virtio-scsi.c | 1 - hw/scsi/vmw_pvscsi.c | 1 - hw/sd/allwinner-sdhost.c | 1 - hw/sd/aspeed_sdhci.c | 1 - hw/sd/sd.c | 3 --- hw/sd/sdhci-pci.c | 1 - hw/sd/sdhci.c | 1 - hw/sparc/sun4m_iommu.c | 1 - hw/sparc64/sun4u.c | 2 -- hw/ssi/aspeed_smc.c | 2 -- hw/ssi/ibex_spi_host.c | 1 - hw/ssi/npcm7xx_fiu.c | 1 - hw/ssi/pnv_spi.c | 1 - hw/ssi/sifive_spi.c | 1 - hw/ssi/ssi.c | 1 - hw/ssi/xilinx_spi.c | 1 - hw/ssi/xilinx_spips.c | 2 -- hw/ssi/xlnx-versal-ospi.c | 1 - hw/timer/a9gtimer.c | 1 - hw/timer/allwinner-a10-pit.c | 1 - hw/timer/arm_mptimer.c | 1 - hw/timer/arm_timer.c | 1 - hw/timer/aspeed_timer.c | 1 - hw/timer/avr_timer16.c | 1 - hw/timer/grlib_gptimer.c | 1 - hw/timer/hpet.c | 1 - hw/timer/i8254_common.c | 1 - hw/timer/ibex_timer.c | 1 - hw/timer/mss-timer.c | 1 - hw/timer/nrf51_timer.c | 1 - hw/timer/pxa2xx_timer.c | 1 - hw/timer/renesas_cmt.c | 1 - hw/timer/renesas_tmr.c | 1 - hw/timer/sifive_pwm.c | 1 - hw/timer/slavio_timer.c | 1 - hw/timer/sse-timer.c | 1 - hw/timer/stm32f2xx_timer.c | 1 - hw/timer/xilinx_timer.c | 1 - hw/tpm/tpm_crb.c | 1 - hw/tpm/tpm_spapr.c | 1 - hw/tpm/tpm_tis_i2c.c | 1 - hw/tpm/tpm_tis_isa.c | 1 - hw/tpm/tpm_tis_sysbus.c | 1 - hw/ufs/lu.c | 1 - hw/ufs/ufs.c | 1 - hw/usb/bus.c | 1 - hw/usb/canokey.c | 1 - hw/usb/ccid-card-emulated.c | 1 - hw/usb/ccid-card-passthru.c | 1 - hw/usb/dev-audio.c | 1 - hw/usb/dev-hid.c | 3 --- hw/usb/dev-hub.c | 1 - hw/usb/dev-mtp.c | 1 - hw/usb/dev-network.c | 1 - hw/usb/dev-serial.c | 2 -- hw/usb/dev-smartcard-reader.c | 2 -- hw/usb/dev-storage-classic.c | 1 - hw/usb/dev-uas.c | 1 - hw/usb/hcd-dwc2.c | 1 - hw/usb/hcd-dwc3.c | 1 - hw/usb/hcd-ehci-pci.c | 1 - hw/usb/hcd-ehci-sysbus.c | 1 - hw/usb/hcd-ohci-pci.c | 1 - hw/usb/hcd-ohci-sysbus.c | 1 - hw/usb/hcd-uhci.c | 2 -- hw/usb/hcd-xhci-nec.c | 1 - hw/usb/hcd-xhci-sysbus.c | 1 - hw/usb/hcd-xhci.c | 1 - hw/usb/host-libusb.c | 1 - hw/usb/redirect.c | 1 - hw/usb/u2f-emulated.c | 1 - hw/usb/u2f-passthru.c | 1 - hw/vfio/ap.c | 1 - hw/vfio/ccw.c | 1 - hw/vfio/pci.c | 2 -- hw/vfio/platform.c | 1 - hw/virtio/vdpa-dev.c | 1 - hw/virtio/vhost-scsi-pci.c | 1 - hw/virtio/vhost-user-blk-pci.c | 1 - hw/virtio/vhost-user-device.c | 1 - hw/virtio/vhost-user-fs-pci.c | 1 - hw/virtio/vhost-user-fs.c | 1 - hw/virtio/vhost-user-gpio.c | 1 - hw/virtio/vhost-user-i2c.c | 1 - hw/virtio/vhost-user-input.c | 1 - hw/virtio/vhost-user-rng-pci.c | 1 - hw/virtio/vhost-user-rng.c | 1 - hw/virtio/vhost-user-scmi.c | 1 - hw/virtio/vhost-user-scsi-pci.c | 1 - hw/virtio/vhost-user-snd.c | 1 - hw/virtio/vhost-user-vsock-pci.c | 1 - hw/virtio/vhost-user-vsock.c | 1 - hw/virtio/vhost-vsock-common.c | 1 - hw/virtio/vhost-vsock-pci.c | 1 - hw/virtio/vhost-vsock.c | 1 - hw/virtio/virtio-9p-pci.c | 1 - hw/virtio/virtio-balloon.c | 1 - hw/virtio/virtio-blk-pci.c | 1 - hw/virtio/virtio-crypto-pci.c | 1 - hw/virtio/virtio-crypto.c | 1 - hw/virtio/virtio-input-pci.c | 1 - hw/virtio/virtio-iommu-pci.c | 1 - hw/virtio/virtio-iommu.c | 1 - hw/virtio/virtio-mem.c | 1 - hw/virtio/virtio-mmio.c | 1 - hw/virtio/virtio-net-pci.c | 1 - hw/virtio/virtio-nsm.c | 1 - hw/virtio/virtio-pci.c | 2 -- hw/virtio/virtio-pmem.c | 1 - hw/virtio/virtio-rng-pci.c | 1 - hw/virtio/virtio-rng.c | 1 - hw/virtio/virtio-scsi-pci.c | 1 - hw/virtio/virtio-serial-pci.c | 1 - hw/virtio/virtio.c | 1 - hw/watchdog/sbsa_gwdt.c | 1 - hw/watchdog/wdt_aspeed.c | 1 - hw/watchdog/wdt_imx2.c | 1 - hw/xen/xen-bus.c | 1 - hw/xen/xen_pt.c | 1 - include/hw/qdev-core.h | 14 +++++++------- include/hw/qdev-properties.h | 3 --- target/arm/cpu.c | 1 - target/avr/cpu.c | 1 - target/hexagon/cpu.c | 1 - target/i386/cpu.c | 2 -- target/microblaze/cpu.c | 1 - target/mips/cpu.c | 1 - target/riscv/cpu.c | 1 - target/s390x/cpu.c | 1 - target/sparc/cpu.c | 1 - tests/unit/test-qdev-global-props.c | 1 - 556 files changed, 7 insertions(+), 624 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 2ae07a779e..6af34098b9 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -201,7 +201,6 @@ static const Property cpu_common_props[] = { DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, MemoryRegion *), #endif - DEFINE_PROP_END_OF_LIST(), }; #ifndef CONFIG_USER_ONLY diff --git a/docs/devel/migration/compatibility.rst b/docs/devel/migration/compatibility.rst index c787f53738..ecb887e318 100644 --- a/docs/devel/migration/compatibility.rst +++ b/docs/devel/migration/compatibility.rst @@ -401,7 +401,6 @@ the old behaviour or the new behaviour:: DEFINE_PROP_UINT32("acpi-index", PCIDevice, acpi_index, 0), + DEFINE_PROP_BIT("x-pcie-err-unc-mask", PCIDevice, cap_present, + QEMU_PCIE_ERR_UNC_MASK_BITNR, true), - DEFINE_PROP_END_OF_LIST() }; Notice that we enable the feature for new machine types. diff --git a/docs/devel/virtio-backends.rst b/docs/devel/virtio-backends.rst index a6f9df4845..679d7544b8 100644 --- a/docs/devel/virtio-backends.rst +++ b/docs/devel/virtio-backends.rst @@ -107,7 +107,6 @@ manually instantiated: VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index b764e4cd3d..ef0d845109 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -246,7 +246,6 @@ static const VMStateDescription vmstate_virtio_9p = { static const Property virtio_9p_properties[] = { DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_9p_class_init(ObjectClass *klass, void *data) diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 5ef5ddccb6..dfa0f37176 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -1016,7 +1016,6 @@ static const Property erst_properties[] = { 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) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 8c4706f8cf..cfb14299b6 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -318,7 +318,6 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) static const Property acpi_ged_properties[] = { DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_memhp_state = { diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 2bfaf5a38d..cc755b36eb 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -617,7 +617,6 @@ static const Property piix4_pm_properties[] = { DEFINE_PROP_BOOL("smm-enabled", PIIX4PMState, smm_enabled, false), DEFINE_PROP_BOOL("x-not-migrate-acpi-index", PIIX4PMState, not_migrate_acpi_index, false), - DEFINE_PROP_END_OF_LIST(), }; static void piix4_pm_class_init(ObjectClass *klass, void *data) diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 9c2ca85cc7..af3ec85989 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -216,7 +216,6 @@ static void vmgenid_realize(DeviceState *dev, Error **errp) static const Property vmgenid_device_properties[] = { DEFINE_PROP_UUID(VMGENID_GUID, VmGenIdState, guid), - DEFINE_PROP_END_OF_LIST(), }; static void vmgenid_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index f94c6f2be3..1cc554f179 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -289,7 +289,6 @@ static const VMStateDescription vmstate_aspeed_adc_engine = { static const Property aspeed_adc_engine_properties[] = { DEFINE_PROP_UINT32("engine-id", AspeedADCEngineState, engine_id, 0), DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index 1781ff4c0b..0a83d28605 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -269,7 +269,6 @@ static const VMStateDescription vmstate_npcm7xx_adc = { static const Property npcm7xx_timer_properties[] = { DEFINE_PROP_UINT32("iref", NPCM7xxADCState, iref, NPCM7XX_ADC_DEFAULT_IREF), - DEFINE_PROP_END_OF_LIST(), }; static void npcm7xx_adc_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index e20f719c9b..1134606fc2 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -552,7 +552,6 @@ static const Property armv7m_properties[] = { DEFINE_PROP_BOOL("dsp", ARMv7MState, dsp, true), DEFINE_PROP_UINT32("mpu-ns-regions", ARMv7MState, mpu_ns_regions, UINT_MAX), DEFINE_PROP_UINT32("mpu-s-regions", ARMv7MState, mpu_s_regions, UINT_MAX), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_armv7m = { @@ -635,7 +634,6 @@ static const Property bitband_properties[] = { DEFINE_PROP_UINT32("base", BitBandState, base, 0), DEFINE_PROP_LINK("source-memory", BitBandState, source_memory, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void bitband_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 4221cacd51..1ddcb26c1e 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -144,7 +144,6 @@ static const Property aspeed_soc_properties[] = { MemoryRegion *), DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 48763b03fe..ef1242d0a0 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -311,7 +311,6 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) static const Property fsl_imx25_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX25State, phy_num, 0), - DEFINE_PROP_END_OF_LIST(), }; static void fsl_imx25_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 236d15bc9c..fed2dbb46d 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -483,7 +483,6 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) static const Property fsl_imx6_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX6State, phy_num, 0), - DEFINE_PROP_END_OF_LIST(), }; static void fsl_imx6_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 1e0bbbb5d7..6995746f64 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -725,7 +725,6 @@ static const Property fsl_imx6ul_properties[] = { true), DEFINE_PROP_BOOL("fec2-phy-connected", FslIMX6ULState, phy_connected[1], true), - DEFINE_PROP_END_OF_LIST(), }; static void fsl_imx6ul_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 0310c15b0c..55b3b3d3c2 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -743,7 +743,6 @@ static const Property fsl_imx7_properties[] = { true), DEFINE_PROP_BOOL("fec2-phy-connected", FslIMX7State, phy_connected[1], true), - DEFINE_PROP_END_OF_LIST(), }; static void fsl_imx7_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index ee6c7e0c0d..64025bac64 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -696,7 +696,6 @@ DEFINE_MACHINE("integratorcp", integratorcp_machine_init) static const Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), - DEFINE_PROP_END_OF_LIST(), }; static void core_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 5d7c3f2e5a..224530f4da 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -234,7 +234,6 @@ static const Property m2sxxx_soc_properties[] = { /* default divisors in Libero GUI */ DEFINE_PROP_UINT8("apb0div", MSF2State, apb0div, 2), DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2), - DEFINE_PROP_END_OF_LIST(), }; static void m2sxxx_soc_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 2960b63b59..780936493e 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -813,7 +813,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) static const Property npcm7xx_properties[] = { DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void npcm7xx_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 43fac8a8db..37dd4cf5f4 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -214,7 +214,6 @@ static const Property nrf51_soc_properties[] = { DEFINE_PROP_UINT32("sram-size", NRF51State, sram_size, NRF51822_SRAM_SIZE), DEFINE_PROP_UINT32("flash-size", NRF51State, flash_size, NRF51822_FLASH_SIZE), - DEFINE_PROP_END_OF_LIST(), }; static void nrf51_soc_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 6baa9d0fc3..dd74c2e558 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -938,7 +938,6 @@ static const Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, TYPE_PCI_BUS, PCIBus *), - DEFINE_PROP_END_OF_LIST(), }; static void smmu_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 026838f9ac..c0cf5df0f6 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1985,7 +1985,6 @@ static const Property smmuv3_properties[] = { * Defaults to stage 1 */ DEFINE_PROP_STRING("stage", SMMUv3State, stage), - DEFINE_PROP_END_OF_LIST() }; static void smmuv3_instance_init(Object *obj) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 7fc13d96c9..ea04b2837f 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -448,7 +448,6 @@ static const Property stellaris_sys_properties[] = { DEFINE_PROP_UINT32("dc2", ssys_state, dc2, 0), DEFINE_PROP_UINT32("dc3", ssys_state, dc3, 0), DEFINE_PROP_UINT32("dc4", ssys_state, dc4, 0), - DEFINE_PROP_END_OF_LIST() }; static void stellaris_sys_instance_init(Object *obj) diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 4c4ff21e80..f56781519f 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1334,7 +1334,6 @@ static const VMStateDescription vmstate_strongarm_uart_regs = { static const Property strongarm_uart_properties[] = { DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void strongarm_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 3adbe7b1fb..3760d101fd 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -975,7 +975,6 @@ static const Property versal_properties[] = { TYPE_CAN_BUS, CanBusState *), DEFINE_PROP_LINK("canbus1", Versal, lpd.iou.canbus[1], TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_END_OF_LIST() }; static void versal_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 1082c62c30..6bb4629a5c 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -867,7 +867,6 @@ static const Property xlnx_zynqmp_props[] = { CanBusState *), DEFINE_PROP_LINK("canbus1", XlnxZynqMPState, canbus[1], TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_END_OF_LIST() }; static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 8033bbbaed..35533c6bad 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1326,7 +1326,6 @@ static void ac97_exit(PCIDevice *dev) static const Property ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(AC97LinkState, card), - DEFINE_PROP_END_OF_LIST(), }; static void ac97_class_init(ObjectClass *klass, void *data) diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index c1d8faecb4..8c9767b537 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -301,7 +301,6 @@ static const Property adlib_properties[] = { DEFINE_AUDIO_PROPERTIES(AdlibState, card), DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), - DEFINE_PROP_END_OF_LIST (), }; static void adlib_class_initfn (ObjectClass *klass, void *data) diff --git a/hw/audio/asc.c b/hw/audio/asc.c index 452039418d..cc205bf063 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -698,7 +698,6 @@ static void asc_init(Object *obj) static const Property asc_properties[] = { DEFINE_AUDIO_PROPERTIES(ASCState, card), DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), - DEFINE_PROP_END_OF_LIST(), }; static void asc_class_init(ObjectClass *oc, void *data) diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index abc38720a3..5a9be80ba3 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -694,7 +694,6 @@ static const Property cs4231a_properties[] = { DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), DEFINE_PROP_UINT32 ("irq", CSState, irq, 9), DEFINE_PROP_UINT32 ("dma", CSState, dma, 3), - DEFINE_PROP_END_OF_LIST (), }; static void cs4231a_class_initfn (ObjectClass *klass, void *data) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 6170425a5a..b5756b97d5 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -870,7 +870,6 @@ static void es1370_exit(PCIDevice *dev) static const Property es1370_properties[] = { DEFINE_AUDIO_PROPERTIES(ES1370State, card), - DEFINE_PROP_END_OF_LIST(), }; static void es1370_class_init (ObjectClass *klass, void *data) diff --git a/hw/audio/gus.c b/hw/audio/gus.c index dd5a5a3441..e718c1183e 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -296,7 +296,6 @@ static const Property gus_properties[] = { DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), - DEFINE_PROP_END_OF_LIST (), }; static void gus_class_initfn (ObjectClass *klass, void *data) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 8bd8f62c48..6f3a8f691b 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -862,7 +862,6 @@ static const Property hda_audio_properties[] = { DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), - DEFINE_PROP_END_OF_LIST(), }; static void hda_audio_init_output(HDACodecDevice *hda, Error **errp) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 3e4a755228..ec24dfd77a 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -39,7 +39,6 @@ static const Property hda_props[] = { DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1), - DEFINE_PROP_END_OF_LIST() }; static const TypeInfo hda_codec_bus_info = { @@ -1219,7 +1218,6 @@ static const Property intel_hda_properties[] = { DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), DEFINE_PROP_ON_OFF_AUTO("msi", IntelHDAState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), - DEFINE_PROP_END_OF_LIST(), }; static void intel_hda_class_init(ObjectClass *klass, void *data) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 7a6b9f52d3..17be185547 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -219,7 +219,6 @@ static const Property pcspk_properties[] = { DEFINE_AUDIO_PROPERTIES(PCSpkState, card), DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, 0x61), DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), - DEFINE_PROP_END_OF_LIST(), }; static void pcspk_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index 6c66a240cb..f771d725fa 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -630,7 +630,6 @@ static const Property pl041_device_properties[] = { /* Non-compact FIFO depth property */ DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth, DEFAULT_FIFO_DEPTH), - DEFINE_PROP_END_OF_LIST(), }; static void pl041_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 143b9e71e1..0c661b4947 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1447,7 +1447,6 @@ static const Property sb16_properties[] = { DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5), DEFINE_PROP_UINT32 ("dma", SB16State, dma, 1), DEFINE_PROP_UINT32 ("dma16", SB16State, hdma, 5), - DEFINE_PROP_END_OF_LIST (), }; static void sb16_class_initfn (ObjectClass *klass, void *data) diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c index e43ddf37f3..4e115e011e 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -461,7 +461,6 @@ static void via_ac97_exit(PCIDevice *dev) static const Property via_ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(ViaAC97State, card), - DEFINE_PROP_END_OF_LIST(), }; static void via_ac97_class_init(ObjectClass *klass, void *data) diff --git a/hw/audio/virtio-snd-pci.c b/hw/audio/virtio-snd-pci.c index b762d7e81e..74d93f4e9c 100644 --- a/hw/audio/virtio-snd-pci.c +++ b/hw/audio/virtio-snd-pci.c @@ -31,7 +31,6 @@ static const Property virtio_snd_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index e2b112e059..7e8ab74ceb 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -85,7 +85,6 @@ static const Property virtio_snd_properties[] = { VIRTIO_SOUND_STREAM_DEFAULT), DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, VIRTIO_SOUND_CHMAP_DEFAULT), - DEFINE_PROP_END_OF_LIST(), }; static void diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 19e7755060..8d381dbc65 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -708,7 +708,6 @@ void wm8750_set_bclk_in(void *opaque, int new_hz) static const Property wm8750_properties[] = { DEFINE_AUDIO_PROPERTIES(WM8750State, card), - DEFINE_PROP_END_OF_LIST(), }; static void wm8750_class_init(ObjectClass *klass, void *data) diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index ce630ec572..981f34219f 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -358,7 +358,6 @@ static void atmega_realize(DeviceState *dev, Error **errp) static const Property atmega_props[] = { DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState, xtal_freq_hz, 0), - DEFINE_PROP_END_OF_LIST() }; static void atmega_class_init(ObjectClass *oc, void *data) diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index 2b9f667fe4..e71be8ab2c 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -296,7 +296,6 @@ static const Property isa_fdc_properties[] = { DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; static void isabus_fdc_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index f17e04b138..381b492aec 100644 --- a/hw/block/fdc-sysbus.c +++ b/hw/block/fdc-sysbus.c @@ -206,7 +206,6 @@ static const Property sysbus_fdc_properties[] = { DEFINE_PROP_SIGNED("fallback", FDCtrlSysBus, state.fallback, FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; static void sysbus_fdc_class_init(ObjectClass *klass, void *data) @@ -230,7 +229,6 @@ static const Property sun4m_fdc_properties[] = { DEFINE_PROP_SIGNED("fallback", FDCtrlSysBus, state.fallback, FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; static void sun4m_fdc_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 57d6844806..d81b7a2a73 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -460,7 +460,6 @@ static const Property floppy_drive_properties[] = { DEFINE_PROP_SIGNED("drive-type", FloppyDrive, type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; static void floppy_drive_realize(DeviceState *qdev, Error **errp) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index ca97365926..8b5f148796 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1729,7 +1729,6 @@ static const Property m25p80_properties[] = { DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2), DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10), DEFINE_PROP_DRIVE("drive", Flash, blk), - DEFINE_PROP_END_OF_LIST(), }; static int m25p80_pre_load(void *opaque) diff --git a/hw/block/nand.c b/hw/block/nand.c index b6e6bfac23..35a91a870b 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -449,7 +449,6 @@ static const Property nand_properties[] = { DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), - DEFINE_PROP_END_OF_LIST(), }; static void nand_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 20f4fc67a0..06db20da60 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -932,7 +932,6 @@ static const Property pflash_cfi01_properties[] = { DEFINE_PROP_STRING("name", PFlashCFI01, name), DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01, old_multiple_chip_handling, false), - DEFINE_PROP_END_OF_LIST(), }; static void pflash_cfi01_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index c82002d665..559fac8ce8 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -959,7 +959,6 @@ static const Property pflash_cfi02_properties[] = { DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0), DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0), DEFINE_PROP_STRING("name", PFlashCFI02, name), - DEFINE_PROP_END_OF_LIST(), }; static void pflash_cfi02_unrealize(DeviceState *dev) diff --git a/hw/block/swim.c b/hw/block/swim.c index c336d83bdc..4a7f8d13f7 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -169,7 +169,6 @@ static const BlockDevOps swim_block_ops = { static const Property swim_drive_properties[] = { DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1), DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf), - DEFINE_PROP_END_OF_LIST(), }; static void swim_drive_realize(DeviceState *qdev, Error **errp) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index f3ac007108..d13c597d7e 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -581,7 +581,6 @@ static const Property vhost_user_blk_properties[] = { VIRTIO_BLK_F_DISCARD, true), DEFINE_PROP_BIT64("write-zeroes", VHostUserBlk, parent_obj.host_features, VIRTIO_BLK_F_WRITE_ZEROES, true), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_blk_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 9ca60fbc07..5bcb77ed20 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -2014,7 +2014,6 @@ static const Property virtio_blk_properties[] = { conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS), DEFINE_PROP_BOOL("x-enable-wce-if-config-wce", VirtIOBlock, conf.x_enable_wce_if_config_wce, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_blk_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 0c0817f498..56a6713660 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -674,7 +674,6 @@ static const Property xen_block_props[] = { props.max_ring_page_order, 4), DEFINE_PROP_LINK("iothread", XenBlockDevice, props.iothread, TYPE_IOTHREAD, IOThread *), - DEFINE_PROP_END_OF_LIST() }; static void xen_block_class_init(ObjectClass *class, void *data) diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c index 3421576e45..e8012cae3a 100644 --- a/hw/char/avr_usart.c +++ b/hw/char/avr_usart.c @@ -261,7 +261,6 @@ static const MemoryRegionOps avr_usart_ops = { static const Property avr_usart_properties[] = { DEFINE_PROP_CHR("chardev", AVRUsartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void avr_usart_pr(void *opaque, int irq, int level) diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 30285c97b0..73ad593406 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -292,7 +292,6 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) static const Property bcm2835_aux_props[] = { DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void bcm2835_aux_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 2e778f7a9c..ebd846a083 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -619,7 +619,6 @@ static const VMStateDescription vmstate_cadence_uart = { static const Property cadence_uart_properties[] = { DEFINE_PROP_CHR("chardev", CadenceUARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void cadence_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index e37e14e0f2..0506500215 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -380,7 +380,6 @@ static const VMStateDescription cmsdk_apb_uart_vmstate = { static const Property cmsdk_apb_uart_properties[] = { DEFINE_PROP_CHR("chardev", CMSDKAPBUART, chr), DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBUART, pclk_frq, 0), - DEFINE_PROP_END_OF_LIST(), }; static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index c0f16e9bd6..1bc3bf85fe 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -118,7 +118,6 @@ static const Property debugcon_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9), DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), - DEFINE_PROP_END_OF_LIST(), }; static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 03beba11ad..b0b0714e0f 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -174,7 +174,6 @@ static const VMStateDescription vmstate_digic_uart = { static const Property digic_uart_properties[] = { DEFINE_PROP_CHR("chardev", DigicUartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void digic_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/escc.c b/hw/char/escc.c index 08bc65ef2c..a5fdd8f698 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -1099,7 +1099,6 @@ static const Property escc_properties[] = { DEFINE_PROP_CHR("chrB", ESCCState, chn[0].chr), DEFINE_PROP_CHR("chrA", ESCCState, chn[1].chr), DEFINE_PROP_STRING("chnA-sunkbd-layout", ESCCState, chn[1].sunkbd_layout), - DEFINE_PROP_END_OF_LIST(), }; static void escc_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index c2836ff8fd..a1a9a12caf 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -709,7 +709,6 @@ static const Property exynos4210_uart_properties[] = { DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), - DEFINE_PROP_END_OF_LIST(), }; static void exynos4210_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index 68e261236e..701eb98893 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -243,7 +243,6 @@ static const VMStateDescription vmstate_goldfish_tty = { static const Property goldfish_tty_properties[] = { DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void goldfish_tty_instance_init(Object *obj) diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index caae88d77d..db6bcdad41 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -279,7 +279,6 @@ static void grlib_apbuart_reset(DeviceState *d) static const Property grlib_apbuart_properties[] = { DEFINE_PROP_CHR("chrdev", UART, chr), - DEFINE_PROP_END_OF_LIST(), }; static void grlib_apbuart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c index b1bdb2ad15..392375ad55 100644 --- a/hw/char/ibex_uart.c +++ b/hw/char/ibex_uart.c @@ -510,7 +510,6 @@ static const VMStateDescription vmstate_ibex_uart = { static const Property ibex_uart_properties[] = { DEFINE_PROP_CHR("chardev", IbexUartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void ibex_uart_init(Object *obj) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 6376f2cadc..12705a1337 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -440,7 +440,6 @@ static void imx_serial_init(Object *obj) static const Property imx_serial_properties[] = { DEFINE_PROP_CHR("chardev", IMXSerialState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void imx_serial_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index fb8cb6c2b7..d1e5f6dad2 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -567,7 +567,6 @@ static const Property ipoctal_properties[] = { DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev), DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev), DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev), - DEFINE_PROP_END_OF_LIST(), }; static void ipoctal_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index c044536d5d..980a12fcb7 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -314,7 +314,6 @@ static void mcf_uart_realize(DeviceState *dev, Error **errp) static const Property mcf_uart_properties[] = { DEFINE_PROP_CHR("chardev", mcf_uart_state, chr), - DEFINE_PROP_END_OF_LIST(), }; static void mcf_uart_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c index b164c70f52..82a61ee95f 100644 --- a/hw/char/nrf51_uart.c +++ b/hw/char/nrf51_uart.c @@ -306,7 +306,6 @@ static const VMStateDescription nrf51_uart_vmstate = { static const Property nrf51_uart_properties[] = { DEFINE_PROP_CHR("chardev", NRF51UARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void nrf51_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 15191698f5..df31ce4722 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -608,7 +608,6 @@ static const Property parallel_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISAParallelState, iobase, -1), DEFINE_PROP_UINT32("irq", ISAParallelState, isairq, 7), DEFINE_PROP_CHR("chardev", ISAParallelState, state.chr), - DEFINE_PROP_END_OF_LIST(), }; static void parallel_isa_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 5fbee5e6c5..06ce851044 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -606,7 +606,6 @@ static const VMStateDescription vmstate_pl011 = { static const Property pl011_properties[] = { DEFINE_PROP_CHR("chardev", PL011State, chr), DEFINE_PROP_BOOL("migrate-clk", PL011State, migrate_clk, true), - DEFINE_PROP_END_OF_LIST(), }; static void pl011_init(Object *obj) diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c index 516b48648b..ea94494932 100644 --- a/hw/char/renesas_sci.c +++ b/hw/char/renesas_sci.c @@ -322,7 +322,6 @@ static const VMStateDescription vmstate_rsci = { static const Property rsci_properties[] = { DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), DEFINE_PROP_CHR("chardev", RSCIState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void rsci_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 536b283471..ddb9a726d5 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -337,7 +337,6 @@ static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), - DEFINE_PROP_END_OF_LIST(), }; static void console_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index a90b892d1d..01233b933d 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -253,7 +253,6 @@ static void console_reset(DeviceState *dev) static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsole, chr), - DEFINE_PROP_END_OF_LIST(), }; static void console_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 2cf50eb0bb..f44959c769 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -117,7 +117,6 @@ static const Property serial_isa_properties[] = { DEFINE_PROP_UINT32("index", ISASerialState, index, -1), DEFINE_PROP_UINT32("iobase", ISASerialState, iobase, -1), DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), - DEFINE_PROP_END_OF_LIST(), }; static void serial_isa_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/char/serial-mm.c b/hw/char/serial-mm.c index 8f51f1d3b8..6338e7c0ba 100644 --- a/hw/char/serial-mm.c +++ b/hw/char/serial-mm.c @@ -132,7 +132,6 @@ static const Property serial_mm_properties[] = { */ DEFINE_PROP_UINT8("regshift", SerialMM, regshift, 0), DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN), - DEFINE_PROP_END_OF_LIST(), }; static void serial_mm_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index c2f20d8e74..7578e863cf 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -136,7 +136,6 @@ static const Property multi_2x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), }; static const Property multi_4x_serial_pci_properties[] = { @@ -145,7 +144,6 @@ static const Property multi_4x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), }; static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 2f487a3a79..6659cef5d4 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -83,7 +83,6 @@ static const VMStateDescription vmstate_pci_serial = { static const Property serial_pci_properties[] = { DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), }; static void serial_pci_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/char/serial.c b/hw/char/serial.c index 85dba02ace..81b346a5ab 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -968,7 +968,6 @@ static const Property serial_properties[] = { DEFINE_PROP_CHR("chardev", SerialState, chr), DEFINE_PROP_UINT32("baudbase", SerialState, baudbase, 115200), DEFINE_PROP_BOOL("wakeup", SerialState, wakeup, false), - DEFINE_PROP_END_OF_LIST(), }; static void serial_class_init(ObjectClass *klass, void* data) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 2ab7197aee..247aeb071a 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -450,7 +450,6 @@ static void sh_serial_init(Object *obj) static const Property sh_serial_properties[] = { DEFINE_PROP_CHR("chardev", SHSerialState, chr), DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), - DEFINE_PROP_END_OF_LIST() }; static void sh_serial_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/shakti_uart.c b/hw/char/shakti_uart.c index 6e56754ca6..09975d9d34 100644 --- a/hw/char/shakti_uart.c +++ b/hw/char/shakti_uart.c @@ -159,7 +159,6 @@ static void shakti_uart_instance_init(Object *obj) static const Property shakti_uart_properties[] = { DEFINE_PROP_CHR("chardev", ShaktiUartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void shakti_uart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 97e4be37c0..4bc5767284 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -253,7 +253,6 @@ static int sifive_uart_be_change(void *opaque) static const Property sifive_uart_properties[] = { DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_uart_init(Object *obj) diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index cd91dad709..6451d010ac 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -166,7 +166,6 @@ void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev) static const Property spapr_vty_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev), DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_spapr_vty = { diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 4a3c30eddb..ebcc510f4e 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -201,7 +201,6 @@ static const MemoryRegionOps stm32f2xx_usart_ops = { static const Property stm32f2xx_usart_properties[] = { DEFINE_PROP_CHR("chardev", STM32F2XXUsartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void stm32f2xx_usart_init(Object *obj) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 360e79cc3f..bcc310bd97 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -536,7 +536,6 @@ static const MemoryRegionOps stm32l4x5_usart_base_ops = { static const Property stm32l4x5_usart_base_properties[] = { DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void stm32l4x5_usart_base_init(Object *obj) diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index c2aafda0ce..04ee26dcbd 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -285,7 +285,6 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd) static const Property terminal_properties[] = { DEFINE_PROP_CHR("chardev", Terminal3270, chr), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription terminal3270_vmstate = { diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index f58292e2bb..aa6d611a47 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -276,7 +276,6 @@ static const TypeInfo virtconsole_info = { static const Property virtserialport_properties[] = { DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_END_OF_LIST(), }; static void virtserialport_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 1e631bcb2b..b6d2743a9c 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -838,7 +838,6 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); static const Property virtser_props[] = { DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_STRING("name", VirtIOSerialPort, name), - DEFINE_PROP_END_OF_LIST() }; static void virtser_bus_class_init(ObjectClass *klass, void *data) @@ -1158,7 +1157,6 @@ static const Property virtio_serial_properties[] = { 31), DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features, VIRTIO_CONSOLE_F_EMERG_WRITE, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_serial_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index c20c1b4b84..96e7d5940d 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -490,7 +490,6 @@ static char *xen_console_get_frontend_path(XenDevice *xendev, Error **errp) static const Property xen_console_properties[] = { DEFINE_PROP_CHR("chardev", XenConsole, chr), DEFINE_PROP_INT32("idx", XenConsole, dev, -1), - DEFINE_PROP_END_OF_LIST(), }; static void xen_console_class_init(ObjectClass *class, void *data) diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index ad77226217..56955e0d74 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -178,7 +178,6 @@ static const MemoryRegionOps uart_ops = { static const Property xilinx_uartlite_properties[] = { DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr), - DEFINE_PROP_END_OF_LIST(), }; static void uart_rx(void *opaque, const uint8_t *buf, int size) diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index c1cddecf60..67f2008995 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -187,7 +187,6 @@ static const Property generic_loader_props[] = { DEFINE_PROP_UINT32("cpu-num", GenericLoaderState, cpu_num, CPU_NONE), DEFINE_PROP_BOOL("force-raw", GenericLoaderState, force_raw, false), DEFINE_PROP_STRING("file", GenericLoaderState, file), - DEFINE_PROP_END_OF_LIST(), }; static void generic_loader_class_init(ObjectClass *klass, void *data) diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c index 74af00cee7..57315e5cef 100644 --- a/hw/core/guest-loader.c +++ b/hw/core/guest-loader.c @@ -116,7 +116,6 @@ static const Property guest_loader_props[] = { DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel), DEFINE_PROP_STRING("bootargs", GuestLoaderState, args), DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd), - DEFINE_PROP_END_OF_LIST(), }; static void guest_loader_class_init(ObjectClass *klass, void *data) diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index fc52796f54..4d0d3cabf1 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -117,7 +117,6 @@ static const VMStateDescription vmstate_or_irq = { static const Property or_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1), - DEFINE_PROP_END_OF_LIST(), }; static void or_irq_class_init(ObjectClass *klass, void *data) diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index a29c9c6e59..1d00c4d36d 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -207,7 +207,6 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) static const Property platform_bus_properties[] = { DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), - DEFINE_PROP_END_OF_LIST() }; static void platform_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c index 40fc7e2e77..fc12274811 100644 --- a/hw/core/split-irq.c +++ b/hw/core/split-irq.c @@ -61,7 +61,6 @@ static void split_irq_realize(DeviceState *dev, Error **errp) static const Property split_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1), - DEFINE_PROP_END_OF_LIST(), }; static void split_irq_class_init(ObjectClass *klass, void *data) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 5346b8b6c6..bad44836f7 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -153,7 +153,6 @@ static const Property a15mp_priv_properties[] = { * Other boards may differ and should set this property appropriately. */ DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 160), - DEFINE_PROP_END_OF_LIST(), }; static void a15mp_priv_class_init(ObjectClass *klass, void *data) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index c3fdfb92e1..9671585b5f 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -167,7 +167,6 @@ static const Property a9mp_priv_properties[] = { * Other boards may differ and should set this property appropriately. */ DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96), - DEFINE_PROP_END_OF_LIST(), }; static void a9mp_priv_class_init(ObjectClass *klass, void *data) diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index 193fc182ab..94861a06d9 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -142,7 +142,6 @@ static const Property mpcore_priv_properties[] = { * has more IRQ lines than the kernel expects. */ DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64), - DEFINE_PROP_END_OF_LIST(), }; static void mpcore_priv_class_init(ObjectClass *klass, void *data) diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c index 8e43621b5c..9da5221f88 100644 --- a/hw/cpu/cluster.c +++ b/hw/cpu/cluster.c @@ -27,7 +27,6 @@ static const Property cpu_cluster_properties[] = { DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0), - DEFINE_PROP_END_OF_LIST() }; typedef struct CallbackData { diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 9a0ff1df86..4268735e3a 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -110,7 +110,6 @@ static void mpcore_rirq_init(Object *obj) static const Property mpcore_rirq_properties[] = { DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), }; static void mpcore_rirq_class_init(ObjectClass *klass, void *data) diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c index 3fde0f8aae..65cdac6cc1 100644 --- a/hw/cxl/switch-mailbox-cci.c +++ b/hw/cxl/switch-mailbox-cci.c @@ -68,7 +68,6 @@ static void cswmbcci_exit(PCIDevice *pci_dev) static const Property cxl_switch_cci_props[] = { DEFINE_PROP_LINK("target", CSWMBCCIDev, target, TYPE_CXL_USP, PCIDevice *), - DEFINE_PROP_END_OF_LIST(), }; static void cswmbcci_class_init(ObjectClass *oc, void *data) diff --git a/hw/display/artist.c b/hw/display/artist.c index 49deed328d..8b719b11ed 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -1478,7 +1478,6 @@ static const Property artist_properties[] = { DEFINE_PROP_UINT16("width", ARTISTState, width, 1280), DEFINE_PROP_UINT16("height", ARTISTState, height, 1024), DEFINE_PROP_UINT16("depth", ARTISTState, depth, 8), - DEFINE_PROP_END_OF_LIST(), }; static void artist_reset(DeviceState *qdev) diff --git a/hw/display/ati.c b/hw/display/ati.c index e24e092bbc..864fa4fc2c 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1047,7 +1047,6 @@ static const Property ati_vga_properties[] = { DEFINE_PROP_BOOL("guest_hwcursor", ATIVGAState, cursor_guest_mode, false), /* this is a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, DEFAULT_X_PIXMAN), - DEFINE_PROP_END_OF_LIST() }; static void ati_vga_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 2539fcc8ab..a5bded5156 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -440,7 +440,6 @@ static const Property bcm2835_fb_props[] = { initial_config.pixo, 1), /* 1=RGB, 0=BGR */ DEFINE_PROP_UINT32("alpha", BCM2835FBState, initial_config.alpha, 2), /* alpha ignored */ - DEFINE_PROP_END_OF_LIST() }; static void bcm2835_fb_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 9a3263aa01..086f7a0f06 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -349,7 +349,6 @@ static const Property bochs_display_properties[] = { DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true), DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), - DEFINE_PROP_END_OF_LIST(), }; static void bochs_display_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 75b3312c24..3f971d875f 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -366,7 +366,6 @@ static const Property cg3_properties[] = { DEFINE_PROP_UINT16("width", CG3State, width, -1), DEFINE_PROP_UINT16("height", CG3State, height, -1), DEFINE_PROP_UINT16("depth", CG3State, depth, -1), - DEFINE_PROP_END_OF_LIST(), }; static void cg3_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 198ed9ed9b..47ca6a7754 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2989,7 +2989,6 @@ static const Property pci_vga_cirrus_properties[] = { cirrus_vga.enable_blitter, true), DEFINE_PROP_BOOL("global-vmstate", struct PCICirrusVGAState, cirrus_vga.vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; static void cirrus_vga_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index d0d134470f..60b7fd20f1 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -74,7 +74,6 @@ static const Property isa_cirrus_vga_properties[] = { cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, cirrus_vga.enable_blitter, true), - DEFINE_PROP_END_OF_LIST(), }; static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 4f097a172c..04c864a308 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1928,7 +1928,6 @@ static const GraphicHwOps exynos4210_fimd_ops = { static const Property exynos4210_fimd_properties[] = { DEFINE_PROP_LINK("framebuffer-memory", Exynos4210fimdState, fbmem, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void exynos4210_fimd_init(Object *obj) diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index a7533c6908..30b5ea67f2 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -514,7 +514,6 @@ static void g364fb_sysbus_reset(DeviceState *d) static const Property g364fb_sysbus_properties[] = { DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, 8 * MiB), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_g364fb_sysbus = { diff --git a/hw/display/i2c-ddc.c b/hw/display/i2c-ddc.c index a2d1f2b044..d8ab9eee40 100644 --- a/hw/display/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -97,7 +97,6 @@ static const VMStateDescription vmstate_i2c_ddc = { static const Property i2c_ddc_properties[] = { DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info), - DEFINE_PROP_END_OF_LIST(), }; static void i2c_ddc_class_init(ObjectClass *oc, void *data) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 977901bfdd..e83fc863be 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -764,7 +764,6 @@ static const Property macfb_sysbus_properties[] = { DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8), DEFINE_PROP_UINT8("display", MacfbSysBusState, macfb.type, MACFB_DISPLAY_VGA), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_macfb_sysbus = { @@ -783,7 +782,6 @@ static const Property macfb_nubus_properties[] = { DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8), DEFINE_PROP_UINT8("display", MacfbNubusState, macfb.type, MACFB_DISPLAY_VGA), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_macfb_nubus = { diff --git a/hw/display/pl110.c b/hw/display/pl110.c index eca00b4279..4d4f477b94 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -538,7 +538,6 @@ static const GraphicHwOps pl110_gfx_ops = { static const Property pl110_properties[] = { DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void pl110_realize(DeviceState *dev, Error **errp) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 949949d374..f3b1be7ebf 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2475,7 +2475,6 @@ static const Property qxl_properties[] = { DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; static void qxl_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index e677f44be6..6c35028965 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -62,7 +62,6 @@ static const VMStateDescription ramfb_dev_vmstate = { static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), - DEFINE_PROP_END_OF_LIST(), }; static void ramfb_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 446b648f1a..09edcf86f8 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -2058,7 +2058,6 @@ static const Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), /* this a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, DEFAULT_X_PIXMAN), - DEFINE_PROP_END_OF_LIST(), }; static void sm501_reset_sysbus(DeviceState *dev) @@ -2146,7 +2145,6 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) static const Property sm501_pci_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, DEFAULT_X_PIXMAN), - DEFINE_PROP_END_OF_LIST(), }; static void sm501_reset_pci(DeviceState *dev) diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 3eb0a91ff9..2cfc1e8f01 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -884,7 +884,6 @@ static const Property tcx_properties[] = { DEFINE_PROP_UINT16("width", TCXState, width, -1), DEFINE_PROP_UINT16("height", TCXState, height, -1), DEFINE_PROP_UINT16("depth", TCXState, depth, -1), - DEFINE_PROP_END_OF_LIST(), }; static void tcx_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index a6cbf77103..2920628f78 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -90,7 +90,6 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) static const Property vga_isa_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), }; static void vga_isa_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c index b759efdde7..1e0c2dbf74 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -114,7 +114,6 @@ static void vga_mmio_realizefn(DeviceState *dev, Error **errp) static const Property vga_mmio_properties[] = { DEFINE_PROP_UINT8("it_shift", VGAMmioState, it_shift, 0), DEFINE_PROP_UINT32("vgamem_mb", VGAMmioState, vga.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), }; static void vga_mmio_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 3145c448f5..dd084c20b1 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -339,7 +339,6 @@ static const Property vga_pci_properties[] = { PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_EDID, true), DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; static const Property secondary_pci_properties[] = { @@ -349,7 +348,6 @@ static const Property secondary_pci_properties[] = { DEFINE_PROP_BIT("edid", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_EDID, true), DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), - DEFINE_PROP_END_OF_LIST(), }; static void vga_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index a36eddcb12..12d5c37ee5 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -647,7 +647,6 @@ static struct vhost_dev *vhost_user_gpu_get_vhost(VirtIODevice *vdev) static const Property vhost_user_gpu_properties[] = { VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf), - DEFINE_PROP_END_OF_LIST(), }; static void diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index 6f31149e1e..c6609ddb1b 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -159,7 +159,6 @@ static const Property virtio_gpu_gl_properties[] = { VIRTIO_GPU_FLAG_STATS_ENABLED, false), DEFINE_PROP_BIT("venus", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_VENUS_ENABLED, false), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_gpu_gl_device_unrealize(DeviceState *qdev) diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 89d27c9d85..6d789701a3 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -23,7 +23,6 @@ static const Property virtio_gpu_pci_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index f6486acdda..f6eb29472e 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -1108,7 +1108,6 @@ static const Property virtio_gpu_rutabaga_properties[] = { DEFINE_PROP_STRING("wayland-socket-path", VirtIOGPURutabaga, wayland_socket_path), DEFINE_PROP_STRING("wsi", VirtIOGPURutabaga, wsi), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_gpu_rutabaga_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 82741d19e5..c2a74a8d99 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1682,7 +1682,6 @@ static const Property virtio_gpu_properties[] = { VIRTIO_GPU_FLAG_BLOB_ENABLED, false), DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0), DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_gpu_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 532e4c62d5..fefbdb61e1 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -211,7 +211,6 @@ static void virtio_vga_set_big_endian_fb(Object *obj, bool value, Error **errp) static const Property virtio_vga_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_vga_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index f49bbf393a..2dd661e3c1 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1337,7 +1337,6 @@ static const Property vga_vmware_properties[] = { chip.vga.vram_size_mb, 16), DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s, chip.vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; static void vmsvga_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 7838f28bca..1272da0133 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1389,7 +1389,6 @@ static void xlnx_dp_reset(DeviceState *dev) static const Property xlnx_dp_device_properties[] = { DEFINE_AUDIO_PROPERTIES(XlnxDPState, aud_card), - DEFINE_PROP_END_OF_LIST(), }; static void xlnx_dp_class_init(ObjectClass *oc, void *data) diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 032afedde2..9652d47adc 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -141,7 +141,6 @@ static void i82374_realize(DeviceState *dev, Error **errp) static const Property i82374_properties[] = { DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), - DEFINE_PROP_END_OF_LIST() }; static void i82374_class_init(ObjectClass *klass, void *data) diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 8b04177393..74c38d2ee8 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -590,7 +590,6 @@ static const Property i8257_properties[] = { DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80), DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480), DEFINE_PROP_INT32("dshift", I8257State, dshift, 0), - DEFINE_PROP_END_OF_LIST() }; static void i8257_class_init(ObjectClass *klass, void *data) diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 3f392822ed..8a9b073b24 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -411,7 +411,6 @@ static void pl081_init(Object *obj) static const Property pl080_properties[] = { DEFINE_PROP_LINK("downstream", PL080State, downstream, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void pl080_class_init(ObjectClass *oc, void *data) diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index d5a0a1caa2..ffef9ebb6f 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -1669,8 +1669,6 @@ static const Property pl330_properties[] = { DEFINE_PROP_LINK("memory", PL330State, mem_mr, TYPE_MEMORY_REGION, MemoryRegion *), - - DEFINE_PROP_END_OF_LIST(), }; static void pl330_class_init(ObjectClass *klass, void *data) diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index f09452d0b5..4bae52a301 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -619,7 +619,6 @@ static const Property axidma_properties[] = { tx_control_dev, TYPE_STREAM_SINK, StreamSink *), DEFINE_PROP_LINK("dma", XilinxAXIDMA, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void axidma_class_init(ObjectClass *klass, void *data) diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 1a63d5f3b2..bb27cb2e64 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -814,7 +814,6 @@ static const Property zdma_props[] = { DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), DEFINE_PROP_LINK("dma", XlnxZDMA, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void zdma_class_init(ObjectClass *klass, void *data) diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index d78dc6444b..f46485a42c 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -710,7 +710,6 @@ static const Property xlnx_csu_dma_properties[] = { TYPE_STREAM_SINK, StreamSink *), DEFINE_PROP_LINK("dma", XlnxCSUDMA, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index 919d53701f..898f80f8c8 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -294,7 +294,6 @@ static const Property imx_gpio_properties[] = { DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true), DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq, false), - DEFINE_PROP_END_OF_LIST(), }; static void imx_gpio_reset(DeviceState *dev) diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index db6792b2ad..23e67424c9 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -395,7 +395,6 @@ static const Property npcm7xx_gpio_properties[] = { DEFINE_PROP_UINT32("reset-osrc", NPCM7xxGPIOState, reset_osrc, 0), /* Bit n set => pin n has high drive strength by default. */ DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState, reset_odsc, 0), - DEFINE_PROP_END_OF_LIST(), }; static void npcm7xx_gpio_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index 03ee9e47c6..8a9f14ba15 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -227,7 +227,6 @@ void omap_gpio_set_clk(Omap1GpioState *gpio, omap_clk clk) static const Property omap_gpio_properties[] = { DEFINE_PROP_INT32("mpu_model", Omap1GpioState, mpu_model, 0), - DEFINE_PROP_END_OF_LIST(), }; static void omap_gpio_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 427419d218..1ac0cf6c46 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -430,7 +430,6 @@ static void pca955x_realize(DeviceState *dev, Error **errp) static const Property pca955x_properties[] = { DEFINE_PROP_STRING("description", PCA955xState, description), - DEFINE_PROP_END_OF_LIST(), }; static void pca955x_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index e8b0458aac..fe03bb4b5e 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -293,7 +293,6 @@ static void pca9554_realize(DeviceState *dev, Error **errp) static const Property pca9554_properties[] = { DEFINE_PROP_STRING("description", PCA9554State, description), - DEFINE_PROP_END_OF_LIST(), }; static void pca9554_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 9b8ca6de32..60ce4a7f62 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -565,7 +565,6 @@ static void pl061_realize(DeviceState *dev, Error **errp) static const Property pl061_props[] = { DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff), DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0), - DEFINE_PROP_END_OF_LIST() }; static void pl061_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c index 5603f0c235..0d5206ae6b 100644 --- a/hw/gpio/sifive_gpio.c +++ b/hw/gpio/sifive_gpio.c @@ -351,7 +351,6 @@ static const VMStateDescription vmstate_sifive_gpio = { static const Property sifive_gpio_properties[] = { DEFINE_PROP_UINT32("ngpio", SIFIVEGPIOState, ngpio, SIFIVE_GPIO_PINS), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_gpio_realize(DeviceState *dev, Error **errp) diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c index d1394f3f55..f69fc1db4f 100644 --- a/hw/gpio/stm32l4x5_gpio.c +++ b/hw/gpio/stm32l4x5_gpio.c @@ -452,7 +452,6 @@ static const Property stm32l4x5_gpio_properties[] = { DEFINE_PROP_UINT32("mode-reset", Stm32l4x5GpioState, moder_reset, 0), DEFINE_PROP_UINT32("ospeed-reset", Stm32l4x5GpioState, ospeedr_reset, 0), DEFINE_PROP_UINT32("pupd-reset", Stm32l4x5GpioState, pupdr_reset, 0), - DEFINE_PROP_END_OF_LIST(), }; static void stm32l4x5_gpio_class_init(ObjectClass *klass, void *data) diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 74897b1604..c0bf528429 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -1741,8 +1741,6 @@ static const Property hv_balloon_properties[] = { DEFINE_PROP_LINK(HV_BALLOON_MEMDEV_PROP, HvBalloon, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_UINT64(HV_BALLOON_ADDR_PROP, HvBalloon, addr, 0), - - DEFINE_PROP_END_OF_LIST(), }; static void hv_balloon_class_init(ObjectClass *klass, void *data) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index 0193addd42..d3e3917077 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -370,7 +370,6 @@ static const Property hv_syndbg_properties[] = { DEFINE_PROP_STRING("host_ip", HvSynDbg, host_ip), DEFINE_PROP_UINT16("host_port", HvSynDbg, host_port, 50000), DEFINE_PROP_BOOL("use_hcalls", HvSynDbg, use_hcalls, false), - DEFINE_PROP_END_OF_LIST(), }; static void hv_syndbg_class_init(ObjectClass *klass, void *data) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 3d1f4d14e8..12a7dc4312 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2348,7 +2348,6 @@ static void vmbus_dev_unrealize(DeviceState *dev) static const Property vmbus_dev_props[] = { DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid), - DEFINE_PROP_END_OF_LIST() }; @@ -2655,7 +2654,6 @@ static const VMStateDescription vmstate_vmbus_bridge = { static const Property vmbus_bridge_props[] = { DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7), - DEFINE_PROP_END_OF_LIST() }; static void vmbus_bridge_class_init(ObjectClass *klass, void *data) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 2ea68c3090..a8fbb9f44a 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -1261,7 +1261,6 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) static const Property aspeed_i2c_properties[] = { DEFINE_PROP_LINK("dram", AspeedI2CState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_i2c_class_init(ObjectClass *klass, void *data) @@ -1450,7 +1449,6 @@ static const Property aspeed_i2c_bus_properties[] = { DEFINE_PROP_UINT8("bus-id", AspeedI2CBus, id, 0), DEFINE_PROP_LINK("controller", AspeedI2CBus, controller, TYPE_ASPEED_I2C, AspeedI2CState *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 4118d3db50..26bb18514a 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -20,7 +20,6 @@ static const Property i2c_props[] = { DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), - DEFINE_PROP_END_OF_LIST(), }; static const TypeInfo i2c_bus_info = { diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index 80c570fd10..779cc4e66e 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -213,7 +213,6 @@ static void pca954x_init(Object *obj) static const Property pca954x_props[] = { DEFINE_PROP_STRING("name", Pca954xState, name), - DEFINE_PROP_END_OF_LIST() }; static void pca954x_class_init(ObjectClass *klass, void *data) diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index 172df135f5..a641db2348 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -513,7 +513,6 @@ void omap_i2c_set_fclk(OMAPI2CState *i2c, omap_clk clk) static const Property omap_i2c_properties[] = { DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), - DEFINE_PROP_END_OF_LIST(), }; static void omap_i2c_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index ca3e62a244..be522b5d7d 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1670,7 +1670,6 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) static const Property amdvi_properties[] = { DEFINE_PROP_BOOL("xtsup", AMDVIState, xtsup, false), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_amdvi_sysbus = { diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index a5b268342f..f81f34dafa 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3418,7 +3418,6 @@ static const Property vtd_properties[] = { DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true), DEFINE_PROP_BOOL("stale-tm", IntelIOMMUState, stale_tm, false), - DEFINE_PROP_END_OF_LIST(), }; /* Read IRTE entry with specific index */ diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 71150ed2e0..f7b49bd9f7 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -308,7 +308,6 @@ static const VMStateDescription kvmclock_vmsd = { static const Property kvmclock_properties[] = { DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState, mach_use_reliable_get_clock, true), - DEFINE_PROP_END_OF_LIST(), }; static void kvmclock_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 2933d3f458..da5eb60052 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -290,7 +290,6 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) static const Property kvm_pit_properties[] = { DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, lost_tick_policy, LOST_TICK_POLICY_DELAY), - DEFINE_PROP_END_OF_LIST(), }; static void kvm_pit_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 217ff43b98..73b31df6ab 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -135,7 +135,6 @@ static void kvm_ioapic_realize(DeviceState *dev, Error **errp) static const Property kvm_ioapic_properties[] = { DEFINE_PROP_UINT32("gsi_base", KVMIOAPICState, kvm_gsi_base, 0), - DEFINE_PROP_END_OF_LIST() }; static void kvm_ioapic_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index c232e825e0..875e1c5c33 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -24,7 +24,6 @@ static const Property sgx_epc_properties[] = { DEFINE_PROP_UINT32(SGX_EPC_NUMA_NODE_PROP, SGXEPCDevice, node, 0), DEFINE_PROP_LINK(SGX_EPC_MEMDEV_PROP, SGXEPCDevice, hostmem, TYPE_MEMORY_BACKEND_EPC, HostMemoryBackendEpc *), - DEFINE_PROP_END_OF_LIST(), }; static void sgx_epc_get_size(Object *obj, Visitor *v, const char *name, diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index da9c35c1ec..3e07d12512 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -319,7 +319,6 @@ static void vmmouse_realizefn(DeviceState *dev, Error **errp) static const Property vmmouse_properties[] = { DEFINE_PROP_LINK("i8042", VMMouseState, i8042, TYPE_I8042, ISAKBDState *), - DEFINE_PROP_END_OF_LIST(), }; static void vmmouse_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c index cab6e72089..2096686b64 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -284,8 +284,6 @@ static const Property vmport_properties[] = { * 5 - ACE 1.x (Deprecated) */ DEFINE_PROP_UINT8("vmware-vmx-type", VMPortState, vmware_vmx_type, 2), - - DEFINE_PROP_END_OF_LIST(), }; static void vmport_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 155f6262ea..e5ec60eae7 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -130,7 +130,6 @@ static const Property x86_iommu_properties[] = { intr_supported, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false), DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true), - DEFINE_PROP_END_OF_LIST(), }; static void x86_iommu_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index e71483e6e3..9453da97bd 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -120,7 +120,6 @@ static const Property xen_pv_props[] = { DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, 0xffff), DEFINE_PROP_UINT8("revision", XenPVDevice, revision, 0x01), DEFINE_PROP_UINT32("size", XenPVDevice, size, 0x400000), - DEFINE_PROP_END_OF_LIST() }; static void xen_pv_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/ahci-sysbus.c b/hw/ide/ahci-sysbus.c index 2432039290..03a5bd42d0 100644 --- a/hw/ide/ahci-sysbus.c +++ b/hw/ide/ahci-sysbus.c @@ -64,7 +64,6 @@ static void sysbus_ahci_realize(DeviceState *dev, Error **errp) static const Property sysbus_ahci_properties[] = { DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), - DEFINE_PROP_END_OF_LIST(), }; static void sysbus_ahci_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/cf.c b/hw/ide/cf.c index 190914f513..cfb4394f80 100644 --- a/hw/ide/cf.c +++ b/hw/ide/cf.c @@ -29,7 +29,6 @@ static const Property ide_cf_properties[] = { DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), - DEFINE_PROP_END_OF_LIST(), }; static void ide_cf_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 942f6c470c..bcc3f344fc 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -315,7 +315,6 @@ static void pci_cmd646_ide_exitfn(PCIDevice *dev) static const Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), - DEFINE_PROP_END_OF_LIST(), }; static void cmd646_ide_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/ide-dev.c b/hw/ide/ide-dev.c index cc92531f1c..789056c5dc 100644 --- a/hw/ide/ide-dev.c +++ b/hw/ide/ide-dev.c @@ -32,7 +32,6 @@ static const Property ide_props[] = { DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), DEFINE_PROP_BOOL("win2k-install-hack", IDEDevice, win2k_install_hack, false), - DEFINE_PROP_END_OF_LIST(), }; static void ide_qdev_realize(DeviceState *qdev, Error **errp) @@ -197,7 +196,6 @@ static const Property ide_hd_properties[] = { DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), DEFINE_PROP_UINT16("rotation_rate", IDEDrive, dev.rotation_rate, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ide_hd_class_init(ObjectClass *klass, void *data) @@ -220,7 +218,6 @@ static const TypeInfo ide_hd_info = { static const Property ide_cd_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), - DEFINE_PROP_END_OF_LIST(), }; static void ide_cd_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/isa.c b/hw/ide/isa.c index a0a7e4837c..9a3c2d522c 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -105,7 +105,6 @@ static const Property isa_ide_properties[] = { DEFINE_PROP_UINT32("iobase", ISAIDEState, iobase, 0x1f0), DEFINE_PROP_UINT32("iobase2", ISAIDEState, iobase2, 0x3f6), DEFINE_PROP_UINT32("irq", ISAIDEState, irqnum, 14), - DEFINE_PROP_END_OF_LIST(), }; static void isa_ide_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 25f8403e80..ecaab77862 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -462,7 +462,6 @@ static void macio_ide_initfn(Object *obj) static const Property macio_ide_properties[] = { DEFINE_PROP_UINT32("channel", MACIOIDEState, channel, 0), DEFINE_PROP_UINT32("addr", MACIOIDEState, addr, -1), - DEFINE_PROP_END_OF_LIST(), }; static void macio_ide_class_init(ObjectClass *oc, void *data) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 43ab66f347..a1b0fd23de 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -143,7 +143,6 @@ static void mmio_ide_initfn(Object *obj) static const Property mmio_ide_properties[] = { DEFINE_PROP_UINT32("shift", MMIOIDEState, shift, 0), - DEFINE_PROP_END_OF_LIST() }; static void mmio_ide_class_init(ObjectClass *oc, void *data) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 24a133fd25..16ac82e34d 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -738,7 +738,6 @@ static void i8042_mmio_init(Object *obj) static const Property i8042_mmio_properties[] = { DEFINE_PROP_UINT64("mask", MMIOKBDState, kbd.mask, UINT64_MAX), DEFINE_PROP_UINT32("size", MMIOKBDState, size, -1), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_kbd_mmio = { @@ -938,7 +937,6 @@ static const Property i8042_properties[] = { DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false), DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1), DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12), - DEFINE_PROP_END_OF_LIST(), }; static void i8042_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index b1cc693189..98382a0e15 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -80,7 +80,6 @@ static void stellaris_gamepad_reset_enter(Object *obj, ResetType type) static const Property stellaris_gamepad_properties[] = { DEFINE_PROP_ARRAY("keycodes", StellarisGamepad, num_buttons, keycodes, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; static void stellaris_gamepad_class_init(ObjectClass *klass, void *data) diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index 7396385508..812faaef8f 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -240,7 +240,6 @@ static void virtio_input_hid_handle_status(VirtIOInput *vinput, static const Property virtio_input_hid_properties[] = { DEFINE_PROP_STRING("display", VirtIOInputHID, display), DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_input_hid_class_init(ObjectClass *klass, void *data) @@ -382,7 +381,6 @@ static struct virtio_input_config virtio_mouse_config_v2[] = { static const Property virtio_mouse_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_mouse_class_init(ObjectClass *klass, void *data) @@ -507,7 +505,6 @@ static struct virtio_input_config virtio_tablet_config_v2[] = { static const Property virtio_tablet_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_tablet_class_init(ObjectClass *klass, void *data) diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index 2be2c633ab..8bfb17f3c4 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -223,7 +223,6 @@ static const VMStateDescription vmstate_virtio_input_host = { static const Property virtio_input_host_properties[] = { DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_input_host_class_init(ObjectClass *klass, void *data) diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index edcd94dedb..1394d99c6b 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -302,7 +302,6 @@ static const VMStateDescription vmstate_virtio_input = { static const Property virtio_input_properties[] = { DEFINE_PROP_STRING("serial", VirtIOInput, serial), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_input_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 8be9f22de8..de8074d474 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -414,7 +414,6 @@ static const Property apic_properties_common[] = { true), DEFINE_PROP_BOOL("legacy-instance-id", APICCommonState, legacy_instance_id, false), - DEFINE_PROP_END_OF_LIST(), }; static void apic_common_get_id(Object *obj, Visitor *v, const char *name, diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index e961cd9156..50c516f2f2 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -360,7 +360,6 @@ static const Property arm_gic_common_properties[] = { /* True if the GIC should implement the virtualization extensions */ DEFINE_PROP_BOOL("has-virtualization-extensions", GICState, virt_extn, 0), DEFINE_PROP_UINT32("num-priority-bits", GICState, n_prio_bits, 8), - DEFINE_PROP_END_OF_LIST(), }; static void arm_gic_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index ffa830b433..ae389fe584 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -173,7 +173,6 @@ static void gicv2m_init(Object *obj) static const Property gicv2m_properties[] = { DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0), DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64), - DEFINE_PROP_END_OF_LIST(), }; static void gicv2m_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index a8ec615a3f..53e7a251b0 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -621,7 +621,6 @@ static const Property arm_gicv3_common_properties[] = { redist_region_count, qdev_prop_uint32, uint32_t), DEFINE_PROP_LINK("sysmem", GICv3State, dma, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index f50b1814ea..2388a96799 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -2005,7 +2005,6 @@ static void gicv3_its_post_load(GICv3ITSState *s) static const Property gicv3_its_props[] = { DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3", GICv3State *), - DEFINE_PROP_END_OF_LIST(), }; static void gicv3_its_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 68a6144add..3d9150a5c2 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -237,7 +237,6 @@ static void kvm_arm_its_reset_hold(Object *obj, ResetType type) static const Property kvm_arm_its_props[] = { DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "kvm-arm-gicv3", GICv3State *), - DEFINE_PROP_END_OF_LIST(), }; static void kvm_arm_its_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 6e2803b123..a30f31833a 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2577,7 +2577,6 @@ static const Property props_nvic[] = { * to use a reasonable default. */ DEFINE_PROP_UINT8("num-prio-bits", NVICState, num_prio_bits, 0), - DEFINE_PROP_END_OF_LIST() }; static void armv7m_nvic_reset(DeviceState *dev) diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index 221dfa912a..6ddbcd4c6d 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -327,7 +327,6 @@ static void exynos4210_combiner_init(Object *obj) static const Property exynos4210_combiner_properties[] = { DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), - DEFINE_PROP_END_OF_LIST(), }; static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index e1b956d990..01a53936d3 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -113,7 +113,6 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) static const Property exynos4210_gic_properties[] = { DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), }; static void exynos4210_gic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index f5343c9d2f..aa5162c18f 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -183,7 +183,6 @@ static void goldfish_pic_instance_init(Object *obj) static const Property goldfish_pic_properties[] = { DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0), - DEFINE_PROP_END_OF_LIST(), }; static void goldfish_pic_class_init(ObjectClass *oc, void *data) diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index bf53251ea2..95cdb411d2 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -378,7 +378,6 @@ static void grlib_irqmp_realize(DeviceState *dev, Error **errp) static const Property grlib_irqmp_properties[] = { DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1), - DEFINE_PROP_END_OF_LIST(), }; static void grlib_irqmp_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index c3174f4fdc..c77ff683bb 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -198,7 +198,6 @@ static const Property pic_properties_common[] = { DEFINE_PROP_UINT32("elcr_addr", PICCommonState, elcr_addr, -1), DEFINE_PROP_UINT8("elcr_mask", PICCommonState, elcr_mask, -1), DEFINE_PROP_BIT("master", PICCommonState, master, 0, false), - DEFINE_PROP_END_OF_LIST(), }; static void pic_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 6d566165b0..bfc8cb7ece 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -478,7 +478,6 @@ static void ioapic_unrealize(DeviceState *dev) static const Property ioapic_properties[] = { DEFINE_PROP_UINT8("version", IOAPICCommonState, version, IOAPIC_VER_DEF), - DEFINE_PROP_END_OF_LIST(), }; static void ioapic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index dd91f89361..be7d3997fd 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -427,7 +427,6 @@ static const Property extioi_properties[] = { DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features, EXTIOI_HAS_VIRT_EXTENSION, 0), - DEFINE_PROP_END_OF_LIST(), }; static void loongarch_extioi_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index e2eca30660..66b5c1e660 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -86,7 +86,6 @@ static void loongarch_pch_msi_init(Object *obj) static const Property loongarch_msi_properties[] = { DEFINE_PROP_UINT32("msi_irq_base", LoongArchPCHMSI, irq_base, 0), DEFINE_PROP_UINT32("msi_irq_num", LoongArchPCHMSI, irq_num, 0), - DEFINE_PROP_END_OF_LIST(), }; static void loongarch_pch_msi_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 6a87b1aab7..dc93c90b3c 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -413,7 +413,6 @@ static void loongarch_pch_pic_init(Object *obj) static const Property loongarch_pch_pic_properties[] = { DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC, irq_num, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_loongarch_pch_pic = { diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index d3f894a5bd..9a081565f5 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -317,7 +317,6 @@ static const VMStateDescription vmstate_loongson_ipi_common = { static const Property ipi_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), }; static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index b5d10ab1f6..a82b80f5c6 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -88,7 +88,6 @@ static const VMStateDescription vmstate_m68k_irqc = { static const Property m68k_irqc_properties[] = { DEFINE_PROP_LINK("m68k-cpu", M68KIRQCState, cpu, TYPE_M68K_CPU, ArchCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void m68k_irqc_class_init(ObjectClass *oc, void *data) diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 996db095c3..3f50eac38a 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -441,7 +441,6 @@ static void mips_gic_realize(DeviceState *dev, Error **errp) static const Property mips_gic_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSGICState, num_vps, 1), DEFINE_PROP_UINT32("num-irq", MIPSGICState, num_irq, 256), - DEFINE_PROP_END_OF_LIST(), }; static void mips_gic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index 28606f102b..095a3d504f 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -377,7 +377,6 @@ void omap_intc_set_fclk(OMAPIntcState *intc, omap_clk clk) static const Property omap_intc_properties[] = { DEFINE_PROP_UINT32("size", OMAPIntcState, size, 0x100), - DEFINE_PROP_END_OF_LIST(), }; static void omap_intc_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c index 8ee1bd64bd..42af4567c6 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -130,7 +130,6 @@ static void or1k_ompic_realize(DeviceState *dev, Error **errp) static const Property or1k_ompic_properties[] = { DEFINE_PROP_UINT32("num-cpus", OR1KOMPICState, num_cpus, 1), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_or1k_ompic_cpu = { diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 2257ae2ee7..78a82d0d30 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -1609,7 +1609,6 @@ static void openpic_realize(DeviceState *dev, Error **errp) static const Property openpic_properties[] = { DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1), - DEFINE_PROP_END_OF_LIST(), }; static void openpic_class_init(ObjectClass *oc, void *data) diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 135fe8301a..3f5d7e5886 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -265,7 +265,6 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) static const Property kvm_openpic_properties[] = { DEFINE_PROP_UINT32("model", KVMOpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), - DEFINE_PROP_END_OF_LIST(), }; static void kvm_openpic_class_init(ObjectClass *oc, void *data) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 581659839b..fc5b5a9789 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -2066,7 +2066,6 @@ static const Property pnv_xive_properties[] = { DEFINE_PROP_UINT64("tm-bar", PnvXive, tm_base, 0), /* The PnvChip id identifies the XIVE interrupt controller. */ DEFINE_PROP_LINK("chip", PnvXive, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_xive_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 5dd305453a..ff36d4d03f 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2366,7 +2366,6 @@ static const Property pnv_xive2_properties[] = { DEFINE_PROP_UINT64("config", PnvXive2, config, PNV_XIVE2_CONFIGURATION), DEFINE_PROP_LINK("chip", PnvXive2, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_xive2_instance_init(Object *obj) diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c index d683413a83..7de4bf9885 100644 --- a/hw/intc/ppc-uic.c +++ b/hw/intc/ppc-uic.c @@ -262,7 +262,6 @@ static void ppc_uic_realize(DeviceState *dev, Error **errp) static const Property ppc_uic_properties[] = { DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0), DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription ppc_uic_vmstate = { diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index db195fb1ff..db374a7c2d 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -274,7 +274,6 @@ static const Property riscv_aclint_mtimer_properties[] = { aperture_size, RISCV_ACLINT_DEFAULT_MTIMER_SIZE), DEFINE_PROP_UINT32("timebase-freq", RISCVAclintMTimerState, timebase_freq, 0), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) @@ -466,7 +465,6 @@ static const Property riscv_aclint_swi_properties[] = { DEFINE_PROP_UINT32("hartid-base", RISCVAclintSwiState, hartid_base, 0), DEFINE_PROP_UINT32("num-harts", RISCVAclintSwiState, num_harts, 1), DEFINE_PROP_UINT32("sswi", RISCVAclintSwiState, sswi, false), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 353eec8136..e160816d26 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -912,7 +912,6 @@ static const Property riscv_aplic_properties[] = { 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 = { diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index adc36151b4..64b0da6d20 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -393,7 +393,6 @@ static const Property riscv_imsic_properties[] = { DEFINE_PROP_UINT32("hartid", RISCVIMSICState, hartid, 0), DEFINE_PROP_UINT32("num-pages", RISCVIMSICState, num_pages, 0), DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_riscv_imsic = { diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c index dfe11ade20..ca13c5fb37 100644 --- a/hw/intc/rx_icu.c +++ b/hw/intc/rx_icu.c @@ -366,7 +366,6 @@ static const Property rxicu_properties[] = { qdev_prop_uint8, uint8_t), DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense, qdev_prop_uint8, uint8_t), - DEFINE_PROP_END_OF_LIST(), }; static void rxicu_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 2963bd5bd6..3f3fa939d3 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -448,7 +448,6 @@ static void qemu_s390_flic_instance_init(Object *obj) static const Property qemu_s390_flic_properties[] = { DEFINE_PROP_BOOL("migrate-all-state", QEMUS390FLICState, migrate_all_state, true), - DEFINE_PROP_END_OF_LIST(), }; static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) @@ -476,7 +475,6 @@ static const Property s390_flic_common_properties[] = { adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), DEFINE_PROP_BOOL("migration-enabled", S390FLICState, migration_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void s390_flic_common_realize(DeviceState *dev, Error **errp) diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index 49895be803..52946fb7bd 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -444,7 +444,6 @@ static const Property sifive_plic_properties[] = { DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_plic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 09f643d633..897029a65a 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -633,7 +633,6 @@ static const Property spapr_xive_properties[] = { DEFINE_PROP_UINT64("vc-base", SpaprXive, vc_base, SPAPR_XIVE_VC_BASE), DEFINE_PROP_UINT64("tm-base", SpaprXive, tm_base, SPAPR_XIVE_TM_BASE), DEFINE_PROP_UINT8("hv-prio", SpaprXive, hv_prio, 7), - DEFINE_PROP_END_OF_LIST(), }; static int spapr_xive_cpu_intc_create(SpaprInterruptController *intc, diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 81bbfdd84b..8852b68f87 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -348,7 +348,6 @@ static const Property icp_properties[] = { DEFINE_PROP_LINK(ICP_PROP_XICS, ICPState, xics, TYPE_XICS_FABRIC, XICSFabric *), DEFINE_PROP_LINK(ICP_PROP_CPU, ICPState, cs, TYPE_CPU, CPUState *), - DEFINE_PROP_END_OF_LIST(), }; static void icp_class_init(ObjectClass *klass, void *data) @@ -680,7 +679,6 @@ static const Property ics_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), DEFINE_PROP_LINK(ICS_PROP_XICS, ICSState, xics, TYPE_XICS_FABRIC, XICSFabric *), - DEFINE_PROP_END_OF_LIST(), }; static void ics_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index 3e860ab582..d99cf567ae 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -178,7 +178,6 @@ static void xilinx_intc_init(Object *obj) static const Property xilinx_intc_properties[] = { DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_intc_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 308e5743bd..3cf8780b8a 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -931,7 +931,6 @@ static const Property xive_tctx_properties[] = { DEFINE_PROP_LINK("cpu", XiveTCTX, cs, TYPE_CPU, CPUState *), DEFINE_PROP_LINK("presenter", XiveTCTX, xptr, TYPE_XIVE_PRESENTER, XivePresenter *), - DEFINE_PROP_END_OF_LIST(), }; static void xive_tctx_class_init(ObjectClass *klass, void *data) @@ -1414,7 +1413,6 @@ static const Property xive_source_properties[] = { DEFINE_PROP_UINT8("reset-pq", XiveSource, reset_pq, XIVE_ESB_OFF), DEFINE_PROP_LINK("xive", XiveSource, xive, TYPE_XIVE_NOTIFIER, XiveNotifier *), - DEFINE_PROP_END_OF_LIST(), }; static void xive_source_class_init(ObjectClass *klass, void *data) @@ -2005,7 +2003,6 @@ void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) static const Property xive_router_properties[] = { DEFINE_PROP_LINK("xive-fabric", XiveRouter, xfb, TYPE_XIVE_FABRIC, XiveFabric *), - DEFINE_PROP_END_OF_LIST(), }; static void xive_router_class_init(ObjectClass *klass, void *data) @@ -2175,7 +2172,6 @@ static const Property xive_end_source_properties[] = { DEFINE_PROP_UINT32("shift", XiveENDSource, esb_shift, XIVE_ESB_64K), DEFINE_PROP_LINK("xive", XiveENDSource, xrtr, TYPE_XIVE_ROUTER, XiveRouter *), - DEFINE_PROP_END_OF_LIST(), }; static void xive_end_source_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 3233d3f14e..07a90c900f 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1031,7 +1031,6 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) static const Property xive2_router_properties[] = { DEFINE_PROP_LINK("xive-fabric", Xive2Router, xfb, TYPE_XIVE_FABRIC, XiveFabric *), - DEFINE_PROP_END_OF_LIST(), }; static void xive2_router_class_init(ObjectClass *klass, void *data) @@ -1247,7 +1246,6 @@ static const Property xive2_end_source_properties[] = { DEFINE_PROP_UINT32("shift", Xive2EndSource, esb_shift, XIVE_ESB_64K), DEFINE_PROP_LINK("xive", Xive2EndSource, xrtr, TYPE_XIVE2_ROUTER, Xive2Router *), - DEFINE_PROP_END_OF_LIST(), }; static void xive2_end_source_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/xlnx-pmu-iomod-intc.c b/hw/intc/xlnx-pmu-iomod-intc.c index 21b9c83658..ccdab244b3 100644 --- a/hw/intc/xlnx-pmu-iomod-intc.c +++ b/hw/intc/xlnx-pmu-iomod-intc.c @@ -478,7 +478,6 @@ static const Property xlnx_pmu_io_intc_properties[] = { DEFINE_PROP_UINT32("intc-intr-size", XlnxPMUIOIntc, cfg.intr_size, 0), DEFINE_PROP_UINT32("intc-level-edge", XlnxPMUIOIntc, cfg.level_edge, 0), DEFINE_PROP_UINT32("intc-positive", XlnxPMUIOIntc, cfg.positive, 0), - DEFINE_PROP_END_OF_LIST(), }; static void xlnx_pmu_io_intc_realize(DeviceState *dev, Error **errp) diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index 7ffc4ffe6f..ed75f79183 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -75,7 +75,6 @@ static void ipack_device_unrealize(DeviceState *dev) static const Property ipack_device_props[] = { DEFINE_PROP_INT32("slot", IPackDevice, slot, -1), - DEFINE_PROP_END_OF_LIST() }; static void ipack_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 850b3bc463..047bb90be9 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -110,7 +110,6 @@ void ipmi_bmc_find_and_link(Object *obj, Object **bmc) static const Property ipmi_bmc_properties[] = { DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20), - DEFINE_PROP_END_OF_LIST(), }; static void bmc_class_init(ObjectClass *oc, void *data) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index cfec1da87c..d015500254 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -517,7 +517,6 @@ static void ipmi_bmc_extern_finalize(Object *obj) static const Property ipmi_bmc_extern_properties[] = { DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), - DEFINE_PROP_END_OF_LIST(), }; static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 8a55893e89..eb2eecd46a 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -2203,7 +2203,6 @@ static const Property ipmi_sim_properties[] = { DEFINE_PROP_UINT32("mfg_id", IPMIBmcSim, mfg_id, 0), DEFINE_PROP_UINT16("product_id", IPMIBmcSim, product_id, 0), DEFINE_PROP_UUID_NODEFAULT("guid", IPMIBmcSim, uuid), - DEFINE_PROP_END_OF_LIST(), }; static void ipmi_sim_class_init(ObjectClass *oc, void *data) diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 16062abb31..a1b66d5ee8 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -138,7 +138,6 @@ static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) static const Property ipmi_isa_properties[] = { DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), - DEFINE_PROP_END_OF_LIST(), }; static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 7e7a37659e..d9ebdd5371 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -145,7 +145,6 @@ static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) static const Property ipmi_isa_properties[] = { DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2), DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5), - DEFINE_PROP_END_OF_LIST(), }; static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 378244aa8f..0f94378a1d 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -840,7 +840,6 @@ static const Property ich9_lpc_properties[] = { pm.swsmi_timer_enabled, true), DEFINE_PROP_BOOL("x-smi-periodic-timer", ICH9LPCState, pm.periodic_timer_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 7bb2af817d..5f5868442a 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -330,7 +330,6 @@ static const VMStateDescription vmstate_pc87312 = { static const Property pc87312_properties[] = { DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398), DEFINE_PROP_UINT8("config", PC87312State, config, 1), - DEFINE_PROP_END_OF_LIST() }; static void pc87312_class_init(ObjectClass *klass, void *data) diff --git a/hw/isa/piix.c b/hw/isa/piix.c index 8ec9c63b8a..eabf256731 100644 --- a/hw/isa/piix.c +++ b/hw/isa/piix.c @@ -415,7 +415,6 @@ static const Property pci_piix_props[] = { DEFINE_PROP_BOOL("has-pit", PIIXState, has_pit, true), DEFINE_PROP_BOOL("has-usb", PIIXState, has_usb, true), DEFINE_PROP_BOOL("smm-enabled", PIIXState, smm_enabled, false), - DEFINE_PROP_END_OF_LIST(), }; static void pci_piix_class_init(ObjectClass *klass, void *data) diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 45e5f74600..395c49fea7 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -603,7 +603,6 @@ static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) static const Property mcf5206_mbar_properties[] = { DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void mcf5206_mbar_class_init(ObjectClass *oc, void *data) diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index c24b0b715d..008626f813 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -180,7 +180,6 @@ static void mcf_intc_instance_init(Object *obj) static const Property mcf_intc_properties[] = { DEFINE_PROP_LINK("m68k-cpu", mcf_intc_state, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void mcf_intc_class_init(ObjectClass *oc, void *data) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index a37ce00874..e4fb5013de 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -916,7 +916,6 @@ static void next_pc_realize(DeviceState *dev, Error **errp) */ static const Property next_pc_properties[] = { DEFINE_PROP_LINK("cpu", NeXTPC, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription next_rtc_vmstate = { diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index 0d8cb8b1cb..168665b382 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -205,7 +205,6 @@ static const VMStateDescription vmstate_glue = { */ static const Property glue_properties[] = { DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void glue_finalize(Object *obj) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 12205c4d32..b553c7d646 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1234,7 +1234,6 @@ static const Property ct3_props[] = { speed, PCIE_LINK_SPEED_32), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLType3Dev, width, PCIE_LINK_WIDTH_16), - DEFINE_PROP_END_OF_LIST(), }; static uint64_t get_lsa_size(CXLType3Dev *ct3d) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 10506d52e4..51362cfe92 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -248,7 +248,6 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, static const Property nvdimm_properties[] = { DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false), - DEFINE_PROP_END_OF_LIST(), }; static void nvdimm_class_init(ObjectClass *oc, void *data) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 49c5f9fd44..dca3db7a36 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -157,7 +157,6 @@ static const Property pc_dimm_properties[] = { PC_DIMM_UNASSIGNED_SLOT), DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index 8d681adfc0..375d728684 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -103,7 +103,6 @@ static const Property sparse_mem_properties[] = { DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX), /* Max amount of actual memory that can be used to back the sparse memory */ DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB), - DEFINE_PROP_END_OF_LIST(), }; MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length) diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 1a2208666c..f8c17f3f2b 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -171,7 +171,6 @@ static const Property mips_cps_properties[] = { DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 256), DEFINE_PROP_STRING("cpu-type", MIPSCPSState, cpu_type), DEFINE_PROP_BOOL("cpu-big-endian", MIPSCPSState, cpu_is_bigendian, false), - DEFINE_PROP_END_OF_LIST() }; static void mips_cps_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c index e2d73edde8..088d4adb0d 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -125,7 +125,6 @@ static const VMStateDescription vmstate_a9_scu = { static const Property a9_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), }; static void a9_scu_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index 247bf62c43..13bba26d0e 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -317,7 +317,6 @@ static void allwinner_h3_dramc_init(Object *obj) static const Property allwinner_h3_dramc_properties[] = { DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0), DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription allwinner_h3_dramc_vmstate = { diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c index a51284ff91..97c3664e3a 100644 --- a/hw/misc/allwinner-r40-dramc.c +++ b/hw/misc/allwinner-r40-dramc.c @@ -467,7 +467,6 @@ static void allwinner_r40_dramc_init(Object *obj) static const Property allwinner_r40_dramc_properties[] = { DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState, ram_addr, 0x0), DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState, ram_size, 256), /* MiB */ - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription allwinner_r40_dramc_vmstate = { diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c index 3a09dca111..042b747f30 100644 --- a/hw/misc/allwinner-sid.c +++ b/hw/misc/allwinner-sid.c @@ -129,7 +129,6 @@ static void allwinner_sid_init(Object *obj) static const Property allwinner_sid_properties[] = { DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription allwinner_sid_vmstate = { diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 9d0e273e33..97ea842d60 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -354,7 +354,6 @@ static const Property applesmc_isa_properties[] = { DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, APPLESMC_DEFAULT_IOBASE), DEFINE_PROP_STRING("osk", AppleSMCState, osk), - DEFINE_PROP_END_OF_LIST(), }; static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope) diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c index 37feed9da7..02493cec31 100644 --- a/hw/misc/arm11scu.c +++ b/hw/misc/arm11scu.c @@ -77,7 +77,6 @@ static void arm11_scu_init(Object *obj) static const Property arm11_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11SCUState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST() }; static void arm11_scu_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 9c209f13b0..39b4642da7 100644 --- a/hw/misc/arm_l2x0.c +++ b/hw/misc/arm_l2x0.c @@ -175,7 +175,6 @@ static void l2x0_priv_init(Object *obj) static const Property l2x0_properties[] = { DEFINE_PROP_UINT32("cache-type", L2x0State, cache_type, 0x1c100100), - DEFINE_PROP_END_OF_LIST(), }; static void l2x0_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 69e379fa10..1c25d51b96 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -632,7 +632,6 @@ static const Property arm_sysctl_properties[] = { /* Daughterboard clock reset values (as reported via SYS_CFG) */ DEFINE_PROP_ARRAY("db-clock", arm_sysctl_state, db_num_clocks, db_clock_reset, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; static void arm_sysctl_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c index b05bcdcabc..58cb37333f 100644 --- a/hw/misc/armsse-cpuid.c +++ b/hw/misc/armsse-cpuid.c @@ -94,7 +94,6 @@ static const MemoryRegionOps armsse_cpuid_ops = { static const Property armsse_cpuid_props[] = { DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0), - DEFINE_PROP_END_OF_LIST() }; static void armsse_cpuid_init(Object *obj) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 5cefbadf9a..e3f7df2e86 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -439,7 +439,6 @@ static void aspeed_hace_realize(DeviceState *dev, Error **errp) static const Property aspeed_hace_properties[] = { DEFINE_PROP_LINK("dram", AspeedHACEState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/aspeed_i3c.c b/hw/misc/aspeed_i3c.c index 7f5a389864..ab39c6435b 100644 --- a/hw/misc/aspeed_i3c.c +++ b/hw/misc/aspeed_i3c.c @@ -325,7 +325,6 @@ static void aspeed_i3c_realize(DeviceState *dev, Error **errp) static const Property aspeed_i3c_device_properties[] = { DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/aspeed_lpc.c b/hw/misc/aspeed_lpc.c index bb9066b0f0..228d250dc0 100644 --- a/hw/misc/aspeed_lpc.c +++ b/hw/misc/aspeed_lpc.c @@ -456,7 +456,6 @@ static const VMStateDescription vmstate_aspeed_lpc = { static const Property aspeed_lpc_properties[] = { DEFINE_PROP_UINT32("hicr7", AspeedLPCState, hicr7, 0), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_lpc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index b97cf51fa1..e4a6bd1581 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -139,7 +139,6 @@ static const VMStateDescription vmstate_aspeed_sbc = { static const Property aspeed_sbc_properties[] = { DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_sbc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index ac33b8d6cb..bac1441b06 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -607,7 +607,6 @@ static const Property aspeed_scu_properties[] = { DEFINE_PROP_UINT32("hw-strap1", AspeedSCUState, hw_strap1, 0), DEFINE_PROP_UINT32("hw-strap2", AspeedSCUState, hw_strap2, 0), DEFINE_PROP_UINT32("hw-prot-key", AspeedSCUState, hw_prot_key, 0), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_scu_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 4980080f74..f359640a9a 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -297,7 +297,6 @@ static const VMStateDescription vmstate_aspeed_sdmc = { static const Property aspeed_sdmc_properties[] = { DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), DEFINE_PROP_BOOL("unlocked", AspeedSDMCState, unlocked, false), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_sdmc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index 1a20cd0bc8..aa14cd931f 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -780,7 +780,6 @@ static const VMStateDescription cprman_vmstate = { static const Property cprman_properties[] = { DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000), - DEFINE_PROP_END_OF_LIST() }; static void cprman_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 09a6f2c6e3..fde66cd590 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -554,7 +554,6 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) static const Property bcm2835_property_props[] = { DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), - DEFINE_PROP_END_OF_LIST() }; static void bcm2835_property_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index 639a8cc3e3..260537bba3 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -59,7 +59,6 @@ static void debug_exit_realizefn(DeviceState *d, Error **errp) static const Property debug_exit_properties[] = { DEFINE_PROP_UINT32("iobase", ISADebugExitState, iobase, 0x501), DEFINE_PROP_UINT32("iosize", ISADebugExitState, iosize, 0x02), - DEFINE_PROP_END_OF_LIST(), }; static void debug_exit_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index 4fc88bd4e5..d7452c4cc8 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -327,7 +327,6 @@ static void ecc_realize(DeviceState *dev, Error **errp) static const Property ecc_properties[] = { DEFINE_PROP_UINT32("version", ECCState, version, -1), - DEFINE_PROP_END_OF_LIST(), }; static void ecc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/empty_slot.c b/hw/misc/empty_slot.c index 79572c5be0..221ea7cb54 100644 --- a/hw/misc/empty_slot.c +++ b/hw/misc/empty_slot.c @@ -82,7 +82,6 @@ static void empty_slot_realize(DeviceState *dev, Error **errp) static const Property empty_slot_properties[] = { DEFINE_PROP_UINT64("size", EmptySlot, size, 0), DEFINE_PROP_STRING("name", EmptySlot, name), - DEFINE_PROP_END_OF_LIST(), }; static void empty_slot_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index abb6a963ca..04ced3559c 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -816,7 +816,6 @@ static const VMStateDescription iotkit_secctl_vmstate = { static const Property iotkit_secctl_props[] = { DEFINE_PROP_UINT32("sse-version", IoTKitSecCtl, sse_version, 0), - DEFINE_PROP_END_OF_LIST() }; static void iotkit_secctl_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index 23b49d7dff..57ffdc3d02 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -842,7 +842,6 @@ static const Property iotkit_sysctl_props[] = { 0x10000000), DEFINE_PROP_UINT32("INITSVTOR1_RST", IoTKitSysCtl, initsvtor1_rst, 0x10000000), - DEFINE_PROP_END_OF_LIST() }; static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index 7d4eea6bfb..75260f7fab 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -136,7 +136,6 @@ static const Property iotkit_sysinfo_props[] = { DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0), DEFINE_PROP_UINT32("sse-version", IoTKitSysInfo, sse_version, 0), DEFINE_PROP_UINT32("IIDR", IoTKitSysInfo, iidr, 0), - DEFINE_PROP_END_OF_LIST() }; static void iotkit_sysinfo_init(Object *obj) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 6d735ec29f..8f9e1f2fc6 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1026,7 +1026,6 @@ static const Property ivshmem_plain_properties[] = { DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) @@ -1083,7 +1082,6 @@ static const Property ivshmem_doorbell_properties[] = { DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, true), DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), - DEFINE_PROP_END_OF_LIST(), }; static void ivshmem_doorbell_init(Object *obj) diff --git a/hw/misc/led.c b/hw/misc/led.c index 76efdbc3f1..9364d9945e 100644 --- a/hw/misc/led.c +++ b/hw/misc/led.c @@ -105,7 +105,6 @@ static const Property led_properties[] = { DEFINE_PROP_STRING("color", LEDState, color), DEFINE_PROP_STRING("description", LEDState, description), DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true), - DEFINE_PROP_END_OF_LIST(), }; static void led_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index a376a2b8a0..5d16bff12c 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1324,7 +1324,6 @@ static const VMStateDescription vmstate_q800_via1 = { static const Property mos6522_q800_via1_properties[] = { DEFINE_PROP_DRIVE("drive", MOS6522Q800VIA1State, blk), - DEFINE_PROP_END_OF_LIST(), }; static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index cfc8afd1dc..270adcb0cd 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -556,7 +556,6 @@ static void cuda_init(Object *obj) static const Property cuda_properties[] = { DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0), - DEFINE_PROP_END_OF_LIST() }; static void cuda_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 7e3d5aa977..194b152eff 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -408,7 +408,6 @@ static const VMStateDescription vmstate_macio_newworld = { static const Property macio_newworld_properties[] = { DEFINE_PROP_BOOL("has-pmu", NewWorldMacIOState, has_pmu, false), DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), - DEFINE_PROP_END_OF_LIST() }; static void macio_newworld_class_init(ObjectClass *oc, void *data) @@ -424,7 +423,6 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) static const Property macio_properties[] = { DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), - DEFINE_PROP_END_OF_LIST() }; static void macio_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 64bf44f67f..47ebb8e8c0 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -762,7 +762,6 @@ static void pmu_init(Object *obj) static const Property pmu_properties[] = { DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), - DEFINE_PROP_END_OF_LIST() }; static void pmu_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 80ca224f76..95f19912b4 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -219,7 +219,6 @@ static const Property mips_gcr_properties[] = { MemoryRegion *), DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void mips_gcr_realize(DeviceState *dev, Error **errp) diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 86ff0f7ad8..772b8c0017 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -166,7 +166,6 @@ static const VMStateDescription vmstate_mips_cpc = { static const Property mips_cpc_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), - DEFINE_PROP_END_OF_LIST(), }; static void mips_cpc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index d84a7dbf15..2d126ebaf8 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -538,7 +538,6 @@ static const Property mips_itu_properties[] = { ITC_FIFO_NUM_MAX), DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores, ITC_SEMAPH_NUM_MAX), - DEFINE_PROP_END_OF_LIST(), }; static void mips_itu_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 0225a5869b..0b8f6a4cb4 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -698,7 +698,6 @@ static void mos6522_finalize(Object *obj) static const Property mos6522_properties[] = { DEFINE_PROP_UINT64("frequency", MOS6522State, frequency, 0), - DEFINE_PROP_END_OF_LIST() }; static void mos6522_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 2e8d1c721c..d07568248d 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -326,7 +326,6 @@ static const Property mps2_fpgaio_properties[] = { DEFINE_PROP_UINT32("num-leds", MPS2FPGAIO, num_leds, 2), DEFINE_PROP_BOOL("has-switches", MPS2FPGAIO, has_switches, false), DEFINE_PROP_BOOL("has-dbgctrl", MPS2FPGAIO, has_dbgctrl, false), - DEFINE_PROP_END_OF_LIST(), }; static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index f378b75571..5f8d6bca43 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -472,7 +472,6 @@ static const Property mps2_scc_properties[] = { */ DEFINE_PROP_ARRAY("oscclk", MPS2SCC, num_oscclk, oscclk_reset, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; static void mps2_scc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/msf2-sysreg.c b/hw/misc/msf2-sysreg.c index 0d7a713c76..20009adbd9 100644 --- a/hw/misc/msf2-sysreg.c +++ b/hw/misc/msf2-sysreg.c @@ -122,7 +122,6 @@ static const Property msf2_sysreg_properties[] = { /* default divisors in Libero GUI */ DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState, apb0div, 2), DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState, apb1div, 2), - DEFINE_PROP_END_OF_LIST(), }; static void msf2_sysreg_realize(DeviceState *dev, Error **errp) diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c index 17aeaf22cb..07464a4dc9 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm7xx_gcr.c @@ -232,7 +232,6 @@ static const VMStateDescription vmstate_npcm7xx_gcr = { static const Property npcm7xx_gcr_properties[] = { DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0), DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0), - DEFINE_PROP_END_OF_LIST(), }; static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c index 2b550a6bca..1e67acdf23 100644 --- a/hw/misc/nrf51_rng.c +++ b/hw/misc/nrf51_rng.c @@ -224,7 +224,6 @@ static const Property nrf51_rng_properties[] = { period_unfiltered_us, 167), DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState, period_filtered_us, 660), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_rng = { diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 7927397a23..ca6a388431 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -321,7 +321,6 @@ static void qdev_pci_testdev_reset(DeviceState *dev) static const Property pci_testdev_properties[] = { DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0), - DEFINE_PROP_END_OF_LIST(), }; static void pci_testdev_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index 824a2e4528..f9a3156a5c 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -102,7 +102,6 @@ static const Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505), DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, PVPANIC_EVENTS), - DEFINE_PROP_END_OF_LIST(), }; static void pvpanic_isa_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 1c3eafc137..967842359f 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -56,7 +56,6 @@ static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp) static const Property pvpanic_pci_properties[] = { DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, PVPANIC_EVENTS), - DEFINE_PROP_END_OF_LIST(), }; static void pvpanic_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c index c48429b131..165e41dfc3 100644 --- a/hw/misc/sifive_e_aon.c +++ b/hw/misc/sifive_e_aon.c @@ -292,7 +292,6 @@ static void sifive_e_aon_init(Object *obj) static const Property sifive_e_aon_properties[] = { DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq, SIFIVE_E_LFCLK_DEFAULT_FREQ), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_e_aon_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index 32cd8e8dfb..955134af0d 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -197,7 +197,6 @@ static const MemoryRegionOps sifive_u_otp_ops = { static const Property sifive_u_otp_properties[] = { DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0), DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_u_otp_realize(DeviceState *dev, Error **errp) diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index b61241d195..fd8466dff3 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -1433,7 +1433,6 @@ static const Property stm32l4x5_rcc_properties[] = { sai1_extclk_frequency, 0), DEFINE_PROP_UINT64("sai2_extclk_frequency", Stm32l4x5RccState, sai2_extclk_frequency, 0), - DEFINE_PROP_END_OF_LIST(), }; static void stm32l4x5_rcc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index b06eb9f119..6d827d21dc 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -590,7 +590,6 @@ static const VMStateDescription tz_mpc_vmstate = { static const Property tz_mpc_properties[] = { DEFINE_PROP_LINK("downstream", TZMPC, downstream, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void tz_mpc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index 96413a502d..505df4e190 100644 --- a/hw/misc/tz-msc.c +++ b/hw/misc/tz-msc.c @@ -283,7 +283,6 @@ static const Property tz_msc_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("idau", TZMSC, idau, TYPE_IDAU_INTERFACE, IDAUInterface *), - DEFINE_PROP_END_OF_LIST(), }; static void tz_msc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 1943d8d165..1daa54c5e6 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -323,7 +323,6 @@ static const Property tz_ppc_properties[] = { DEFINE_PORT(13), DEFINE_PORT(14), DEFINE_PORT(15), - DEFINE_PROP_END_OF_LIST(), }; static void tz_ppc_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c index 62e1153627..257282a3a9 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -73,7 +73,6 @@ static void unimp_realize(DeviceState *dev, Error **errp) static const Property unimp_properties[] = { DEFINE_PROP_UINT64("size", UnimplementedDeviceState, size, 0), DEFINE_PROP_STRING("name", UnimplementedDeviceState, name), - DEFINE_PROP_END_OF_LIST(), }; static void unimp_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index 8281a9baff..8db0f7e658 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -737,7 +737,6 @@ static const Property cframe_regs_props[] = { cfg.blktype_num_frames[5], 0), DEFINE_PROP_UINT32("blktype6-frames", XlnxVersalCFrameReg, cfg.blktype_num_frames[6], 0), - DEFINE_PROP_END_OF_LIST(), }; static void cframe_bcast_reg_init(Object *obj) @@ -802,7 +801,6 @@ static const Property cframe_bcast_regs_props[] = { TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_LINK("cframe14", XlnxVersalCFrameBcastReg, cfg.cframe[14], TYPE_XLNX_CFI_IF, XlnxCfiIf *), - DEFINE_PROP_END_OF_LIST(), }; static void cframe_reg_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c index 7cfdabdb8e..26d06e2557 100644 --- a/hw/misc/xlnx-versal-cfu.c +++ b/hw/misc/xlnx-versal-cfu.c @@ -457,13 +457,11 @@ static const Property cfu_props[] = { TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_LINK("cframe14", XlnxVersalCFUAPB, cfg.cframe[14], TYPE_XLNX_CFI_IF, XlnxCfiIf *), - DEFINE_PROP_END_OF_LIST(), }; static const Property cfu_sfr_props[] = { DEFINE_PROP_LINK("cfu", XlnxVersalCFUSFR, cfg.cfu, TYPE_XLNX_VERSAL_CFU_APB, XlnxVersalCFUAPB *), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_cfu_apb = { diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 2f6af4f680..0419f648b7 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -666,8 +666,6 @@ static const Property trng_props[] = { DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200), DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults, trng_prop_fault_events, uint32_t), - - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_trng = { diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index a06b9fbc05..d1e76be027 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -220,7 +220,6 @@ static const VMStateDescription vmstate_xram_ctrl = { static const Property xram_ctrl_properties[] = { DEFINE_PROP_UINT64("size", XlnxXramCtrl, cfg.size, 1 * MiB), - DEFINE_PROP_END_OF_LIST(), }; static void xram_ctrl_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index ffa14ecb84..f4fa3b1840 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -625,7 +625,6 @@ static const VMStateDescription vmstate_zynq_slcr = { static const Property zynq_slcr_props[] = { DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1), - DEFINE_PROP_END_OF_LIST(), }; static void zynq_slcr_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index 3f03060bf5..314a28af6d 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -834,7 +834,6 @@ static const Property allwinner_sun8i_emac_properties[] = { DEFINE_PROP_UINT8("phy-addr", AwSun8iEmacState, mii_phy_addr, 0), DEFINE_PROP_LINK("dma-memory", AwSun8iEmacState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static int allwinner_sun8i_emac_post_load(void *opaque, int version_id) diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 39c10426cf..3eb9e09dc5 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -465,7 +465,6 @@ static void aw_emac_realize(DeviceState *dev, Error **errp) static const Property aw_emac_properties[] = { DEFINE_NIC_PROPERTIES(AwEmacState, conf), DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_mii = { diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 3fce01315f..0282bd1d7f 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1799,7 +1799,6 @@ static const Property gem_properties[] = { jumbo_max_len, 10240), DEFINE_PROP_LINK("dma", CadenceGEMState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void gem_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 97fa46c4b3..dc242e9215 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -2052,7 +2052,6 @@ static const Property canfd_core_properties[] = { CANFD_DEFAULT_CLOCK), DEFINE_PROP_LINK("canfdbus", XlnxVersalCANFDState, canfdbus, TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_END_OF_LIST(), }; static void canfd_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index 61c104c18b..9fbdeea368 100644 --- a/hw/net/can/xlnx-zynqmp-can.c +++ b/hw/net/can/xlnx-zynqmp-can.c @@ -1174,7 +1174,6 @@ static const Property xlnx_zynqmp_can_properties[] = { CAN_DEFAULT_CLOCK), DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_END_OF_LIST(), }; static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index e3ca11991b..c80ddb12e3 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -937,7 +937,6 @@ static const Property dp8393x_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), - DEFINE_PROP_END_OF_LIST(), }; static void dp8393x_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index ef0af31751..7348d39632 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1685,7 +1685,6 @@ static const Property e1000_properties[] = { compat_flags, E1000_FLAG_TSO_BIT, true), DEFINE_PROP_BIT("init-vet", E1000State, compat_flags, E1000_FLAG_VET_BIT, true), - DEFINE_PROP_END_OF_LIST(), }; typedef struct E1000Info { diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index e2b7576f67..544a39eae9 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -672,7 +672,6 @@ static const Property e1000e_properties[] = { e1000e_prop_subsys, uint16_t), DEFINE_PROP_BOOL("init-vet", E1000EState, init_vet, true), DEFINE_PROP_BOOL("migrate-timadj", E1000EState, timadj, true), - DEFINE_PROP_END_OF_LIST(), }; static void e1000e_class_init(ObjectClass *class, void *data) diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index b8cb8d5cf1..5801cb0635 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -2060,7 +2060,6 @@ static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) static const Property e100_properties[] = { DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), }; static void eepro100_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 764be2c6a2..781b900395 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -416,7 +416,6 @@ static void etsec_instance_init(Object *obj) static const Property etsec_properties[] = { DEFINE_NIC_PROPERTIES(eTSEC, conf), - DEFINE_PROP_END_OF_LIST(), }; static void etsec_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 4adc7fb10c..40a13d1667 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -1258,7 +1258,6 @@ static const Property ftgmac100_properties[] = { DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), DEFINE_PROP_BOOL("dma64", FTGMAC100State, dma64, false), - DEFINE_PROP_END_OF_LIST(), }; static void ftgmac100_class_init(ObjectClass *klass, void *data) @@ -1418,7 +1417,6 @@ static const VMStateDescription vmstate_aspeed_mii = { static const Property aspeed_mii_properties[] = { DEFINE_PROP_LINK("nic", AspeedMiiState, nic, TYPE_FTGMAC100, FTGMAC100State *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_mii_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/igb.c b/hw/net/igb.c index ad0f748d82..ae973f40d6 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -594,7 +594,6 @@ static const VMStateDescription igb_vmstate = { static const Property igb_properties[] = { DEFINE_NIC_PROPERTIES(IGBState, conf), DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), - DEFINE_PROP_END_OF_LIST(), }; static void igb_class_init(ObjectClass *class, void *data) diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 9b64968477..e0342d9363 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -1229,7 +1229,6 @@ static const Property imx_eth_properties[] = { DEFINE_PROP_BOOL("phy-connected", IMXFECState, phy_connected, true), DEFINE_PROP_LINK("phy-consumer", IMXFECState, phy_consumer, TYPE_IMX_FEC, IMXFECState *), - DEFINE_PROP_END_OF_LIST(), }; static void imx_eth_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 237e9b97d5..afee68c7db 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1307,7 +1307,6 @@ static void lan9118_realize(DeviceState *dev, Error **errp) static const Property lan9118_properties[] = { DEFINE_NIC_PROPERTIES(lan9118_state, conf), DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0), - DEFINE_PROP_END_OF_LIST(), }; static void lan9118_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/lance.c b/hw/net/lance.c index 9ed9c94cff..a0945e1c88 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -141,7 +141,6 @@ static const Property lance_properties[] = { DEFINE_PROP_LINK("dma", SysBusPCNetState, state.dma_opaque, TYPE_DEVICE, DeviceState *), DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), }; static void lance_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index 248e3841db..95f56311d9 100644 --- a/hw/net/lasi_i82596.c +++ b/hw/net/lasi_i82596.c @@ -160,7 +160,6 @@ static void lasi_82596_instance_init(Object *obj) static const Property lasi_82596_properties[] = { DEFINE_NIC_PROPERTIES(SysBusI82596State, state.conf), - DEFINE_PROP_END_OF_LIST(), }; static void lasi_82596_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 55bad4c069..d5572a81d3 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -662,7 +662,6 @@ static void mcf_fec_instance_init(Object *obj) static const Property mcf_fec_properties[] = { DEFINE_NIC_PROPERTIES(mcf_fec_state, conf), - DEFINE_PROP_END_OF_LIST(), }; static void mcf_fec_class_init(ObjectClass *oc, void *data) diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index c9ef1beb7b..8852b6f3a1 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -268,7 +268,6 @@ static void mipsnet_sysbus_reset(DeviceState *dev) static const Property mipsnet_properties[] = { DEFINE_NIC_PROPERTIES(MIPSnetState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void mipsnet_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index 8d9015f962..80f75f19dd 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -550,7 +550,6 @@ static const Property msf2_emac_properties[] = { DEFINE_PROP_LINK("ahb-bus", MSF2EmacState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_NIC_PROPERTIES(MSF2EmacState, conf), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_msf2_emac = { diff --git a/hw/net/mv88w8618_eth.c b/hw/net/mv88w8618_eth.c index ccb11512db..a1f51f7ade 100644 --- a/hw/net/mv88w8618_eth.c +++ b/hw/net/mv88w8618_eth.c @@ -375,7 +375,6 @@ static const Property mv88w8618_eth_properties[] = { DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 1cd070d419..c445f2a222 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -83,7 +83,6 @@ static const Property ne2000_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISANE2000State, iobase, 0x300), DEFINE_PROP_UINT32("irq", ISANE2000State, isairq, 9), DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c), - DEFINE_PROP_END_OF_LIST(), }; static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 12fa579d22..929e9a1930 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -98,7 +98,6 @@ static void ne2000_instance_init(Object *obj) static const Property ne2000_properties[] = { DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), - DEFINE_PROP_END_OF_LIST(), }; static void ne2000_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index f06e908d04..cc38080c7d 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -847,7 +847,6 @@ static const VMStateDescription vmstate_npcm7xx_emc = { static const Property npcm7xx_emc_properties[] = { DEFINE_NIC_PROPERTIES(NPCM7xxEMCState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void npcm7xx_emc_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 1db29307d7..6d7f8398b6 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -914,7 +914,6 @@ static const VMStateDescription vmstate_npcm_gmac = { static const Property npcm_gmac_properties[] = { DEFINE_NIC_PROPERTIES(NPCMGMACState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void npcm_gmac_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 003b452bc9..54daab7b04 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -745,7 +745,6 @@ static void qdev_open_eth_reset(DeviceState *dev) static const Property open_eth_properties[] = { DEFINE_NIC_PROPERTIES(OpenEthState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void open_eth_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 83ba8cd949..b8e9ff95e8 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -254,7 +254,6 @@ static void pcnet_instance_init(Object *obj) static const Property pcnet_properties[] = { DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), }; static void pcnet_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index efc20396aa..aa5d87fbc5 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -1468,7 +1468,6 @@ static const Property rocker_properties[] = { switch_id, 0), DEFINE_PROP_ARRAY("ports", Rocker, fp_ports, fp_ports_peers, qdev_prop_netdev, NICPeers), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription rocker_vmsd = { diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 064a73b6b4..3245aa1f1d 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3412,7 +3412,6 @@ static void rtl8139_instance_init(Object *obj) static const Property rtl8139_properties[] = { DEFINE_NIC_PROPERTIES(RTL8139State, conf), - DEFINE_PROP_END_OF_LIST(), }; static void rtl8139_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index a853c30fa2..b18d5c23c3 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -790,7 +790,6 @@ static void smc91c111_realize(DeviceState *dev, Error **errp) static const Property smc91c111_properties[] = { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), - DEFINE_PROP_END_OF_LIST(), }; static void smc91c111_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index d381c041db..da98a7ec3e 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -791,7 +791,6 @@ static const Property spapr_vlan_properties[] = { DEFINE_NIC_PROPERTIES(SpaprVioVlan, nicconf), DEFINE_PROP_BIT("use-rx-buffer-pools", SpaprVioVlan, compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true), - DEFINE_PROP_END_OF_LIST(), }; static bool spapr_vlan_rx_buffer_pools_needed(void *opaque) diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 4af1afa733..a420732d48 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -499,7 +499,6 @@ static void stellaris_enet_realize(DeviceState *dev, Error **errp) static const Property stellaris_enet_properties[] = { DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), - DEFINE_PROP_END_OF_LIST(), }; static void stellaris_enet_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/sungem.c b/hw/net/sungem.c index bcc7a18382..e1c672acd3 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -1427,7 +1427,6 @@ static const Property sungem_properties[] = { * override. */ DEFINE_PROP_UINT32("phy_addr", SunGEMState, phy_addr, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_sungem = { diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 86f472fcbe..539e0d5e46 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -179,7 +179,6 @@ struct SunHMEState { static const Property sunhme_properties[] = { DEFINE_NIC_PROPERTIES(SunHMEState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void sunhme_reset_tx(SunHMEState *s) diff --git a/hw/net/tulip.c b/hw/net/tulip.c index f35b58a88c..d753dc790d 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -1009,7 +1009,6 @@ static void tulip_instance_init(Object *obj) static const Property tulip_properties[] = { DEFINE_NIC_PROPERTIES(TULIPState, c), - DEFINE_PROP_END_OF_LIST(), }; static void tulip_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 4fd1f9acca..e2d3d18804 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -4057,7 +4057,6 @@ static const Property virtio_net_properties[] = { VIRTIO_NET_F_GUEST_USO6, true), DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features, VIRTIO_NET_F_HOST_USO, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_net_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index f69547cad5..4c77d69b78 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2477,7 +2477,6 @@ static const Property vmxnet3_properties[] = { VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false), DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags, VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false), - DEFINE_PROP_END_OF_LIST(), }; static void vmxnet3_realize(DeviceState *qdev, Error **errp) diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 5a5259150a..97ebd9fa30 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -558,7 +558,6 @@ static void xen_netdev_unrealize(XenDevice *xendev) static const Property xen_netdev_properties[] = { DEFINE_NIC_PROPERTIES(XenNetDev, conf), DEFINE_PROP_INT32("idx", XenNetDev, dev, -1), - DEFINE_PROP_END_OF_LIST(), }; static void xen_netdev_class_init(ObjectClass *class, void *data) diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index 6e0f96f485..e3cc4c60eb 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -416,7 +416,6 @@ static void xgmac_enet_realize(DeviceState *dev, Error **errp) static const Property xgmac_properties[] = { DEFINE_NIC_PROPERTIES(XgmacState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void xgmac_enet_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 9d0c618e2f..457952af19 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -1005,7 +1005,6 @@ static const Property xilinx_enet_properties[] = { tx_data_dev, TYPE_STREAM_SINK, StreamSink *), DEFINE_PROP_LINK("axistream-control-connected", XilinxAXIEnet, tx_control_dev, TYPE_STREAM_SINK, StreamSink *), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_enet_class_init(ObjectClass *klass, void *data) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 9413731d20..f3eb2af193 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -255,7 +255,6 @@ static const Property xilinx_ethlite_properties[] = { DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1), DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1), DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index 83893e5a46..8fe4362723 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -26,7 +26,6 @@ static void nubus_bridge_init(Object *obj) static const Property nubus_bridge_properties[] = { DEFINE_PROP_UINT16("slot-available-mask", NubusBridge, bus.slot_available_mask, 0xffff), - DEFINE_PROP_END_OF_LIST() }; static void nubus_bridge_class_init(ObjectClass *klass, void *data) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 7cafc13427..6755c3dc43 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -110,7 +110,6 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) static const Property nubus_device_properties[] = { DEFINE_PROP_INT32("slot", NubusDevice, slot, -1), DEFINE_PROP_STRING("romfile", NubusDevice, romfile), - DEFINE_PROP_END_OF_LIST() }; static void nubus_device_class_init(ObjectClass *oc, void *data) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 33a3062466..d9c8dace1e 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8965,7 +8965,6 @@ static const Property nvme_props[] = { DEFINE_PROP_BOOL("atomic.dn", NvmeCtrl, params.atomic_dn, 0), DEFINE_PROP_UINT16("atomic.awun", NvmeCtrl, params.atomic_awun, 0), DEFINE_PROP_UINT16("atomic.awupf", NvmeCtrl, params.atomic_awupf, 0), - DEFINE_PROP_END_OF_LIST(), }; static void nvme_get_smart_warning(Object *obj, Visitor *v, const char *name, diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 192b80f18d..0a7881b21f 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -834,7 +834,6 @@ static const Property nvme_ns_props[] = { DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default, false), DEFINE_PROP_STRING("fdp.ruhs", NvmeNamespace, params.fdp.ruhs), - DEFINE_PROP_END_OF_LIST(), }; static void nvme_ns_class_init(ObjectClass *oc, void *data) diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 3171c3888c..2ae56f12a5 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -223,7 +223,6 @@ static const Property nvme_subsystem_props[] = { NVME_DEFAULT_RU_SIZE), DEFINE_PROP_UINT32("fdp.nrg", NvmeSubsystem, params.fdp.nrg, 1), DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem, params.fdp.nruh, 0), - DEFINE_PROP_END_OF_LIST(), }; static void nvme_subsys_class_init(ObjectClass *oc, void *data) diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index 19bf8d2091..6b2aa8c7d2 100644 --- a/hw/nvram/ds1225y.c +++ b/hw/nvram/ds1225y.c @@ -145,7 +145,6 @@ static void nvram_sysbus_realize(DeviceState *dev, Error **errp) static const Property nvram_sysbus_properties[] = { DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), - DEFINE_PROP_END_OF_LIST(), }; static void nvram_sysbus_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 669920b2b9..5f525d37e4 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -239,7 +239,6 @@ static const Property at24c_eeprom_props[] = { DEFINE_PROP_UINT8("address-size", EEPROMState, asize, 0), DEFINE_PROP_BOOL("writable", EEPROMState, writable, true), DEFINE_PROP_DRIVE("drive", EEPROMState, blk), - DEFINE_PROP_END_OF_LIST() }; static diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 7461d99ff2..327d623d8d 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1084,7 +1084,6 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) static const Property fw_cfg_properties[] = { DEFINE_PROP_BOOL("acpi-mr-restore", FWCfgState, acpi_mr_restore, true), - DEFINE_PROP_END_OF_LIST(), }; static void fw_cfg_common_realize(DeviceState *dev, Error **errp) @@ -1278,7 +1277,6 @@ static const Property fw_cfg_io_properties[] = { true), DEFINE_PROP_UINT16("x-file-slots", FWCfgIoState, parent_obj.file_slots, FW_CFG_FILE_SLOTS_DFLT), - DEFINE_PROP_END_OF_LIST(), }; static void fw_cfg_io_realize(DeviceState *dev, Error **errp) @@ -1328,7 +1326,6 @@ static const Property fw_cfg_mem_properties[] = { true), DEFINE_PROP_UINT16("x-file-slots", FWCfgMemState, parent_obj.file_slots, FW_CFG_FILE_SLOTS_DFLT), - DEFINE_PROP_END_OF_LIST(), }; static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index d62ad719c8..db42817e1f 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -138,7 +138,6 @@ static const Property macio_nvram_properties[] = { DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0), DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0), DEFINE_PROP_DRIVE("drive", MacIONVRAMState, blk), - DEFINE_PROP_END_OF_LIST() }; static void macio_nvram_class_init(ObjectClass *oc, void *data) diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c index 236049462b..2ed4078858 100644 --- a/hw/nvram/nrf51_nvm.c +++ b/hw/nvram/nrf51_nvm.c @@ -356,7 +356,6 @@ static void nrf51_nvm_reset(DeviceState *dev) static const Property nrf51_nvm_properties[] = { DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_nvm = { diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 2251ff2f4c..1230884f52 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -255,7 +255,6 @@ static const VMStateDescription vmstate_spapr_nvram = { static const Property spapr_nvram_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprNvram, sdev), DEFINE_PROP_DRIVE("drive", SpaprNvram, blk), - DEFINE_PROP_END_OF_LIST(), }; static void spapr_nvram_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 4fa528f048..d3f238fc83 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -523,7 +523,6 @@ static const VMStateDescription vmstate_bbram_ctrl = { static const Property bbram_ctrl_props[] = { DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), - DEFINE_PROP_END_OF_LIST(), }; static void bbram_ctrl_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index 5dae9e8e9a..f1787d0db5 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -272,7 +272,6 @@ static const Property efuse_properties[] = { DEFINE_PROP_BOOL("init-factory-tbits", XlnxEFuse, init_tbits, true), DEFINE_PROP_ARRAY("read-only", XlnxEFuse, ro_bits_cnt, ro_bits, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; static void efuse_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/xlnx-versal-efuse-cache.c b/hw/nvram/xlnx-versal-efuse-cache.c index 1aea27afd3..2fb599422c 100644 --- a/hw/nvram/xlnx-versal-efuse-cache.c +++ b/hw/nvram/xlnx-versal-efuse-cache.c @@ -87,8 +87,6 @@ static const Property efuse_cache_props[] = { DEFINE_PROP_LINK("efuse", XlnxVersalEFuseCache, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), - - DEFINE_PROP_END_OF_LIST(), }; static void efuse_cache_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index 599aa126fb..3246eb3ca6 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -750,8 +750,6 @@ static const Property efuse_ctrl_props[] = { DEFINE_PROP_ARRAY("pg0-lock", XlnxVersalEFuseCtrl, extra_pg0_lock_n16, extra_pg0_lock_spec, qdev_prop_uint16, uint16_t), - - DEFINE_PROP_END_OF_LIST(), }; static void efuse_ctrl_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index af53187905..15024daf4f 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -837,8 +837,6 @@ static const Property zynqmp_efuse_props[] = { DEFINE_PROP_LINK("efuse", XlnxZynqMPEFuse, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), - - DEFINE_PROP_END_OF_LIST(), }; static void zynqmp_efuse_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index cfe50e60e9..e337f1ac50 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -217,7 +217,6 @@ static const Property cxl_dsp_props[] = { speed, PCIE_LINK_SPEED_64), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, width, PCIE_LINK_WIDTH_16), - DEFINE_PROP_END_OF_LIST() }; static void cxl_dsp_class_init(ObjectClass *oc, void *data) diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 5824ba3c75..c0037f2cfb 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -211,7 +211,6 @@ static const Property gen_rp_props[] = { speed, PCIE_LINK_SPEED_64), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, width, PCIE_LINK_WIDTH_32), - DEFINE_PROP_END_OF_LIST() }; static void cxl_rp_dvsec_write_config(PCIDevice *dev, uint32_t addr, diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index ef94aa3654..28b109c49a 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -369,7 +369,6 @@ static const Property cxl_upstream_props[] = { speed, PCIE_LINK_SPEED_32), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLUpstreamPort, width, PCIE_LINK_WIDTH_16), - DEFINE_PROP_END_OF_LIST() }; static void cxl_upstream_class_init(ObjectClass *oc, void *data) diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index c319ca8263..3c0b41ef1a 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -145,7 +145,6 @@ static const Property gen_rp_props[] = { speed, PCIE_LINK_SPEED_16), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, width, PCIE_LINK_WIDTH_32), - DEFINE_PROP_END_OF_LIST() }; static void gen_rp_dev_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 35a37e056a..0a91a8ae6c 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -186,7 +186,6 @@ static const Property pci_bridge_dev_properties[] = { res_reserve.mem_pref_32, -1), DEFINE_PROP_SIZE("pref64-reserve", PCIBridgeDev, res_reserve.mem_pref_64, -1), - DEFINE_PROP_END_OF_LIST(), }; static bool pci_device_shpc_present(void *opaque, int version_id) diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 01997c1ab3..af4591a9c3 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -425,7 +425,6 @@ static const Property pxb_dev_properties[] = { DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED), DEFINE_PROP_BOOL("bypass_iommu", PXBDev, bypass_iommu, false), - DEFINE_PROP_END_OF_LIST(), }; static void pxb_dev_class_init(ObjectClass *klass, void *data) @@ -509,7 +508,6 @@ static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp) static const Property pxb_cxl_dev_properties[] = { DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev, hdm_for_passthrough, false), - DEFINE_PROP_END_OF_LIST(), }; static void pxb_cxl_dev_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 8834ff3dbf..fd4514a595 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -126,7 +126,6 @@ static void pcie_pci_bridge_write_config(PCIDevice *d, static const Property pcie_pci_bridge_dev_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription pcie_pci_bridge_dev_vmstate = { diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index a7f87a1bc4..dd40b366bf 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -152,7 +152,6 @@ static const Property rp_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), DEFINE_PROP_BOOL("disable-acs", PCIESlot, disable_acs, false), - DEFINE_PROP_END_OF_LIST() }; static void rp_instance_post_init(Object *obj) diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index 92e5fb72ec..d4e94f2657 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -137,7 +137,6 @@ static void xio3130_downstream_exitfn(PCIDevice *d) static const Property xio3130_downstream_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription vmstate_xio3130_downstream = { diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index ead9893f21..58fdbf7bc9 100644 --- a/hw/pci-host/dino.c +++ b/hw/pci-host/dino.c @@ -495,7 +495,6 @@ static void dino_pcihost_init(Object *obj) static const Property dino_pcihost_properties[] = { DEFINE_PROP_LINK("memory-as", DinoState, memory_as, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void dino_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index 8a955ca130..c6aa8e87a2 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -166,7 +166,6 @@ static const Property gpex_host_properties[] = { gpex_cfg.mmio64.base, 0), DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost, gpex_cfg.mmio64.size, 0), - DEFINE_PROP_END_OF_LIST(), }; static void gpex_host_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index d64de73774..84e5ee8c6e 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -131,7 +131,6 @@ static char *grackle_ofw_unit_address(const SysBusDevice *dev) static const Property grackle_properties[] = { DEFINE_PROP_UINT32("ofw-addr", GrackleState, ofw_addr, -1), - DEFINE_PROP_END_OF_LIST() }; static void grackle_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index 3c73ebe83f..d5c13a89b6 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1277,7 +1277,6 @@ static const TypeInfo gt64120_pci_info = { static const Property gt64120_properties[] = { DEFINE_PROP_BOOL("cpu-little-endian", GT64120State, cpu_little_endian, false), - DEFINE_PROP_END_OF_LIST(), }; static void gt64120_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index 40780fbc52..1e69691c6d 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -362,7 +362,6 @@ static const Property i440fx_props[] = { above_4g_mem_size, 0), DEFINE_PROP_BOOL("x-pci-hole64-fix", I440FXState, pci_hole64_fix, true), DEFINE_PROP_STRING(I440FX_HOST_PROP_PCI_TYPE, I440FXState, pci_type), - DEFINE_PROP_END_OF_LIST(), }; static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 2518d5abe6..9c41c155fb 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -100,7 +100,6 @@ static void mv64361_pcihost_realize(DeviceState *dev, Error **errp) static const Property mv64361_pcihost_props[] = { DEFINE_PROP_UINT8("index", MV64361PCIState, index, 0), - DEFINE_PROP_END_OF_LIST() }; static void mv64361_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index 888f0786a0..97cfb52119 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -192,8 +192,6 @@ static const Property pnv_phb_properties[] = { DEFINE_PROP_LINK("pec", PnvPHB, pec, TYPE_PNV_PHB4_PEC, PnvPhb4PecState *), - - DEFINE_PROP_END_OF_LIST(), }; static void pnv_phb_class_init(ObjectClass *klass, void *data) @@ -304,8 +302,6 @@ static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) static const Property pnv_phb_root_port_properties[] = { DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0), - - DEFINE_PROP_END_OF_LIST(), }; static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 529b33b5a2..cd178f7eaf 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -1095,7 +1095,6 @@ static const Property pnv_phb3_properties[] = { DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_LINK("phb-base", PnvPHB3, phb_base, TYPE_PNV_PHB, PnvPHB *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_phb3_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 482fe25803..178c73f519 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1694,7 +1694,6 @@ static const Property pnv_phb4_properties[] = { DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, PnvPhb4PecState *), DEFINE_PROP_LINK("phb-base", PnvPHB4, phb_base, TYPE_PNV_PHB, PnvPHB *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_phb4_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index f8975403d3..e4d33fa060 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -288,7 +288,6 @@ static const Property pnv_pec_properties[] = { DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 54071fc125..345ce4c526 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -510,7 +510,6 @@ static void e500_host_bridge_class_init(ObjectClass *klass, void *data) static const Property pcihost_properties[] = { DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), DEFINE_PROP_UINT32("first_pin_irq", PPCE500PCIState, first_pin_irq, 0x1), - DEFINE_PROP_END_OF_LIST(), }; static void e500_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index af0b77ea1e..06be3d77cb 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -182,7 +182,6 @@ static const Property q35_host_props[] = { DEFINE_PROP_BOOL(PCI_HOST_PROP_SMM_RANGES, Q35PCIHost, mch.has_smm_ranges, true), DEFINE_PROP_BOOL("x-pci-hole64-fix", Q35PCIHost, pci_hole64_fix, true), - DEFINE_PROP_END_OF_LIST(), }; static void q35_host_class_init(ObjectClass *klass, void *data) @@ -666,7 +665,6 @@ static const Property mch_props[] = { DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes, 16), DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true), - DEFINE_PROP_END_OF_LIST(), }; static void mch_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index b0a4a669f5..918a3237a9 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -429,7 +429,6 @@ static const Property raven_pcihost_properties[] = { /* Temporary workaround until legacy prep machine is removed */ DEFINE_PROP_BOOL("is-legacy-prep", PREPPCIState, is_legacy_prep, false), - DEFINE_PROP_END_OF_LIST() }; static void raven_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index 623afed644..56f057a63f 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -495,7 +495,6 @@ static char *sabre_ofw_unit_address(const SysBusDevice *dev) static const Property sabre_properties[] = { DEFINE_PROP_UINT64("special-base", SabreState, special_base, 0), DEFINE_PROP_UINT64("mem-base", SabreState, mem_base, 0), - DEFINE_PROP_END_OF_LIST(), }; static void sabre_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index bd670cfa9d..37e2461bbb 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -425,7 +425,6 @@ static const TypeInfo unin_internal_pci_host_info = { static const Property pci_unin_main_pci_host_props[] = { DEFINE_PROP_UINT32("ofw-addr", UNINHostState, ofw_addr, -1), - DEFINE_PROP_END_OF_LIST() }; static void pci_unin_main_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 5d59640691..c3fbf4cbf9 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -501,7 +501,6 @@ static const TypeInfo versatile_pci_host_info = { static const Property pci_vpb_properties[] = { DEFINE_PROP_UINT8("broken-irq-mapping", PCIVPBState, irq_mapping_prop, PCI_VPB_IRQMAP_ASSUME_OK), - DEFINE_PROP_END_OF_LIST() }; static void pci_vpb_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 848403970b..18688485f4 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -163,7 +163,6 @@ static const Property xilinx_pcie_host_props[] = { DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0), DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 * MiB), DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 90248481b1..714208aa98 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -104,7 +104,6 @@ static const Property pci_props[] = { DEFINE_PROP_BIT("x-pcie-ext-tag", PCIDevice, cap_present, QEMU_PCIE_EXT_TAG_BITNR, true), { .name = "busnr", .info = &prop_pci_busnr }, - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription vmstate_pcibus = { diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index dd4fd3674f..aee4dd7d1f 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -480,7 +480,6 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, static const Property pci_bridge_properties[] = { DEFINE_PROP_BOOL("x-pci-express-writeable-slt-bug", PCIBridge, pcie_writeable_slt_bug, false), - DEFINE_PROP_END_OF_LIST(), }; static void pci_bridge_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 321e7be709..4510890dfc 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -244,7 +244,6 @@ static const Property pci_host_properties_common[] = { DEFINE_PROP_BOOL("x-config-reg-migration-enabled", PCIHostState, mig_enabled, true), DEFINE_PROP_BOOL(PCI_HOST_BYPASS_IOMMU, PCIHostState, bypass_iommu, false), - DEFINE_PROP_END_OF_LIST(), }; static void pci_host_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index bac2822e98..c73db30e98 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -116,7 +116,6 @@ static const Property pcie_port_props[] = { DEFINE_PROP_UINT16("aer_log_max", PCIEPort, parent_obj.parent_obj.exp.aer_log.log_max, PCIE_AER_LOG_MAX_DEFAULT), - DEFINE_PROP_END_OF_LIST() }; static void pcie_port_class_init(ObjectClass *oc, void *data) @@ -210,7 +209,6 @@ static const Property pcie_slot_props[] = { DEFINE_PROP_BOOL("hotplug", PCIESlot, hotplug, true), DEFINE_PROP_BOOL("x-do-not-expose-native-hotplug-cap", PCIESlot, hide_native_hotplug_cap, false), - DEFINE_PROP_END_OF_LIST() }; static void pcie_slot_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index b90a052ce0..770516d7e5 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2431,7 +2431,6 @@ static const Property pnv_chip_properties[] = { DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), DEFINE_PROP_BOOL("big-core", PnvChip, big_core, false), DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_chip_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c index 646736f7e9..d09a167466 100644 --- a/hw/ppc/pnv_adu.c +++ b/hw/ppc/pnv_adu.c @@ -187,7 +187,6 @@ static void pnv_adu_realize(DeviceState *dev, Error **errp) static const Property pnv_adu_properties[] = { DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_adu_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index 840ef23128..011822ea1d 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -454,7 +454,6 @@ static const Property pnv_chiptod_properties[] = { DEFINE_PROP_BOOL("primary", PnvChipTOD, primary, false), DEFINE_PROP_BOOL("secondary", PnvChipTOD, secondary, false), DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 22864c92f3..a5ebd392e1 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -443,7 +443,6 @@ static const Property pnv_core_properties[] = { false), DEFINE_PROP_BOOL("lpar-per-core", PnvCore, lpar_per_core, false), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_core_power8_class_init(ObjectClass *oc, void *data) @@ -695,7 +694,6 @@ static void pnv_quad_power10_realize(DeviceState *dev, Error **errp) static const Property pnv_quad_properties[] = { DEFINE_PROP_UINT32("quad-id", PnvQuad, quad_id, 0), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index b1f83e2cf2..11c8da8bed 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -414,7 +414,6 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) static const Property pnv_homer_properties[] = { DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_homer_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index 4bd61abeed..13f16906dc 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -547,7 +547,6 @@ static const Property pnv_i2c_properties[] = { DEFINE_PROP_LINK("chip", PnvI2C, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_UINT32("engine", PnvI2C, engine, 1), DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_i2c_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 4d47167163..0480a60f3f 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -830,7 +830,6 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) static const Property pnv_lpc_properties[] = { DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_lpc_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index eed6d32650..5bb755df76 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -115,7 +115,6 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) static const Property pnv_pnor_properties[] = { DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB), DEFINE_PROP_DRIVE("drive", PnvPnor, blk), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_pnor_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index e7d6ceee99..cd5021c4f5 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -555,7 +555,6 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) static const Property pnv_psi_properties[] = { DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 801f97811f..f8c9cec9ce 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -967,7 +967,6 @@ static void ppc405_cpc_realize(DeviceState *dev, Error **errp) static const Property ppc405_cpc_properties[] = { DEFINE_PROP_UINT32("sys-clk", Ppc405CpcState, sysclk, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ppc405_cpc_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 05a5ef6f77..0d6f8d8a04 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1025,7 +1025,6 @@ static const Property ppc460ex_pcie_props[] = { DEFINE_PROP_INT32("dcrn-base", PPC460EXPCIEState, dcrn_base, -1), DEFINE_PROP_LINK("cpu", PPC460EXPCIEState, cpu, TYPE_POWERPC_CPU, PowerPCCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void ppc460ex_pcie_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 530a392f2a..9ce9777510 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -234,7 +234,6 @@ static void ppc4xx_mal_finalize(Object *obj) static const Property ppc4xx_mal_properties[] = { DEFINE_PROP_UINT8("txc-num", Ppc4xxMalState, txcnum, 0), DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ppc4xx_mal_class_init(ObjectClass *oc, void *data) @@ -542,7 +541,6 @@ bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu, static const Property ppc4xx_dcr_properties[] = { DEFINE_PROP_LINK("cpu", Ppc4xxDcrDeviceState, cpu, TYPE_POWERPC_CPU, PowerPCCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index 6cfb07a11f..562bff8d53 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -429,7 +429,6 @@ static const Property ppc4xx_sdram_ddr_props[] = { DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), - DEFINE_PROP_END_OF_LIST(), }; static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data) @@ -714,7 +713,6 @@ static const Property ppc4xx_sdram_ddr2_props[] = { DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), - DEFINE_PROP_END_OF_LIST(), }; static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index ca475c69f4..6e2ae98625 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -288,7 +288,6 @@ static const VMStateDescription vmstate_prep_systemio = { static const Property prep_systemio_properties[] = { DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0), DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), - DEFINE_PROP_END_OF_LIST() }; static void prep_systemio_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index bee9bc62d4..0e5d53b8b6 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -210,7 +210,6 @@ static const VMStateDescription vmstate_rs6000mc = { static const Property rs6000mc_properties[] = { DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0), DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), - DEFINE_PROP_END_OF_LIST() }; static void rs6000mc_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 88d743a3c3..559dd918e1 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -363,7 +363,6 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) static const Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID), - DEFINE_PROP_END_OF_LIST() }; static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 8bcce4146a..6f875d73b2 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -887,7 +887,6 @@ static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) #ifdef CONFIG_LIBPMEM static const Property spapr_nvdimm_properties[] = { DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), - DEFINE_PROP_END_OF_LIST(), }; #endif diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 3edff528ca..0dcbd5900c 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -2055,7 +2055,6 @@ static const Property spapr_phb_properties[] = { pcie_ecs, true), DEFINE_PROP_BOOL("pre-5.1-associativity", SpaprPhbState, pre_5_1_assoc, false), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_spapr_pci_lsi = { diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 51c3a54d45..6935f00832 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -134,7 +134,6 @@ static const Property spapr_rng_properties[] = { DEFINE_PROP_BOOL("use-kvm", SpaprRngState, use_kvm, false), DEFINE_PROP_LINK("rng", SpaprRngState, backend, TYPE_RNG_BACKEND, RngBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void spapr_rng_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index 37521b88cb..2a17a5108b 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -147,7 +147,6 @@ static void spapr_tpm_proxy_unrealize(DeviceState *d) static const Property spapr_tpm_proxy_properties[] = { DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), - DEFINE_PROP_END_OF_LIST(), }; static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 6f84fdd3fa..ee9a22bba3 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -193,7 +193,6 @@ static void pci_proxy_write_config(PCIDevice *d, uint32_t addr, uint32_t val, static const Property proxy_properties[] = { DEFINE_PROP_STRING("fd", PCIProxyDev, fd), - DEFINE_PROP_END_OF_LIST(), }; static void pci_proxy_dev_class_init(ObjectClass *klass, void *data) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 8ce85ea9f7..bc26b5313a 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -308,7 +308,6 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) static const Property lowrisc_ibex_soc_props[] = { DEFINE_PROP_UINT32("resetvec", LowRISCIbexSoCState, resetvec, 0x20000400), - DEFINE_PROP_END_OF_LIST() }; static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index a695314bbe..257a5faa5f 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -163,7 +163,6 @@ static const Property riscv_iommu_pci_properties[] = { DEFINE_PROP_UINT16("device-id", RISCVIOMMUStatePci, device_id, PCI_DEVICE_ID_REDHAT_RISCV_IOMMU), DEFINE_PROP_UINT8("revision", RISCVIOMMUStatePci, revision, 0x01), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 07fed36986..41f3e6cf7c 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2248,7 +2248,6 @@ static const Property riscv_iommu_properties[] = { DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE), DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_iommu_class_init(ObjectClass *klass, void* data) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 0df454772f..74b91ac60c 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -33,7 +33,6 @@ static const Property riscv_harts_props[] = { DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec, DEFAULT_RSTVEC), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_harts_cpu_reset(void *opaque) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 124ffd4842..f5c01dbbd0 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -939,7 +939,6 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) static const Property sifive_u_soc_props[] = { DEFINE_PROP_UINT32("serial", SiFiveUSoCState, serial, OTP_SERIAL), DEFINE_PROP_STRING("cpu-type", SiFiveUSoCState, cpu_type), - DEFINE_PROP_END_OF_LIST() }; static void sifive_u_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 838db72136..b56098b0a9 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -313,7 +313,6 @@ static const VMStateDescription allwinner_rtc_vmstate = { static const Property allwinner_rtc_properties[] = { DEFINE_PROP_INT32("base-year", AwRtcState, base_year, 0), - DEFINE_PROP_END_OF_LIST(), }; static void allwinner_rtc_class_init(ObjectClass *klass, void *data) diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index 389f192efa..cd11bf8208 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -289,7 +289,6 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) static const Property goldfish_rtc_properties[] = { DEFINE_PROP_BOOL("big-endian", GoldfishRTCState, big_endian, false), - DEFINE_PROP_END_OF_LIST(), }; static void goldfish_rtc_class_init(ObjectClass *klass, void *data) diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index 51f80d27ef..38bc8dcf10 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -81,7 +81,6 @@ static const Property m48t59_isa_properties[] = { DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), DEFINE_PROP_UINT8("irq", M48txxISAState, isairq, 8), - DEFINE_PROP_END_OF_LIST(), }; static void m48t59_reset_isa(DeviceState *d) diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 5a2c7b4abd..57c17e5133 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -620,7 +620,6 @@ static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) static const Property m48t59_sysbus_properties[] = { DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), - DEFINE_PROP_END_OF_LIST(), }; static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 973ed9914d..37f247fce8 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -966,7 +966,6 @@ static const Property mc146818rtc_properties[] = { DEFINE_PROP_UINT8("irq", MC146818RtcState, isairq, RTC_ISA_IRQ), DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", MC146818RtcState, lost_tick_policy, LOST_TICK_POLICY_DISCARD), - DEFINE_PROP_END_OF_LIST(), }; static void rtc_reset_enter(Object *obj, ResetType type) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index 1dc8e6e00f..32ce5bbb99 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -330,7 +330,6 @@ static const Property pl031_properties[] = { */ DEFINE_PROP_BOOL("migrate-tick-offset", PL031State, migrate_tick_offset, true), - DEFINE_PROP_END_OF_LIST() }; static void pl031_class_init(ObjectClass *klass, void *data) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index dfa27bc94e..da1a1cc00e 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -262,7 +262,6 @@ static const Property rx62n_properties[] = { MemoryRegion *), DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false), DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0), - DEFINE_PROP_END_OF_LIST(), }; static void rx62n_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 0d10c3ed55..494faebb5a 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -85,7 +85,6 @@ static const Property ccw_device_properties[] = { DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), - DEFINE_PROP_END_OF_LIST(), }; static void ccw_device_reset_hold(Object *obj, ResetType type) diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 860a04a7da..04ab1f6402 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -123,7 +123,6 @@ VirtualCssBus *virtual_css_bus_init(void) static const Property virtual_css_bridge_properties[] = { DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path, true), - DEFINE_PROP_END_OF_LIST(), }; static bool prop_get_true(Object *obj, Error **errp) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 88a97f0085..0344c02ece 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -299,7 +299,6 @@ static const Property s390_ipl_properties[] = { DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, true), - DEFINE_PROP_END_OF_LIST(), }; static void s390_ipl_set_boot_menu(S390IPLState *ipl) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 22e6be67af..5fbbf41a3d 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1488,7 +1488,6 @@ static const Property s390_pci_device_properties[] = { DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true), DEFINE_PROP_BOOL("forwarding-assist", S390PCIBusDevice, forwarding_assist, true), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription s390_pci_device_vmstate = { diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 6d0a47ed73..4409e62b6a 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -477,7 +477,6 @@ static void s390_skeys_realize(DeviceState *dev, Error **errp) static const Property s390_skeys_props[] = { DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void s390_skeys_class_init(ObjectClass *oc, void *data) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 6c69c01e1f..8e07acbddc 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -362,7 +362,6 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) static const Property s390_stattrib_props[] = { DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void s390_stattrib_class_init(ObjectClass *oc, void *data) diff --git a/hw/s390x/vhost-scsi-ccw.c b/hw/s390x/vhost-scsi-ccw.c index 0be0f8a82c..e6bf0c55bc 100644 --- a/hw/s390x/vhost-scsi-ccw.c +++ b/hw/s390x/vhost-scsi-ccw.c @@ -44,7 +44,6 @@ static void vhost_ccw_scsi_instance_init(Object *obj) static const Property vhost_ccw_scsi_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/vhost-user-fs-ccw.c b/hw/s390x/vhost-user-fs-ccw.c index 934378aaec..6a9654d77b 100644 --- a/hw/s390x/vhost-user-fs-ccw.c +++ b/hw/s390x/vhost-user-fs-ccw.c @@ -28,7 +28,6 @@ static const Property vhost_user_fs_ccw_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_fs_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 3ba4008b4b..875ccf3485 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -25,7 +25,6 @@ struct VHostVSockCCWState { static const Property vhost_vsock_ccw_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c index c10b084d40..287ae2ba76 100644 --- a/hw/s390x/virtio-ccw-9p.c +++ b/hw/s390x/virtio-ccw-9p.c @@ -46,7 +46,6 @@ static const Property virtio_ccw_9p_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c index bbbed494b3..1180efaf6d 100644 --- a/hw/s390x/virtio-ccw-balloon.c +++ b/hw/s390x/virtio-ccw-balloon.c @@ -51,7 +51,6 @@ static const Property virtio_ccw_balloon_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 3182851234..db9d442ffb 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -49,7 +49,6 @@ static const Property virtio_ccw_blk_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c index b4cd7605c9..b693f87c70 100644 --- a/hw/s390x/virtio-ccw-crypto.c +++ b/hw/s390x/virtio-ccw-crypto.c @@ -49,7 +49,6 @@ static const Property virtio_ccw_crypto_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index c44dc2d355..a6b14c25d9 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -47,7 +47,6 @@ static const Property virtio_ccw_gpu_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c index 040a9e04a9..6ca10d58ee 100644 --- a/hw/s390x/virtio-ccw-input.c +++ b/hw/s390x/virtio-ccw-input.c @@ -48,7 +48,6 @@ static const Property virtio_ccw_input_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index c41d347034..80a5581baf 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -52,7 +52,6 @@ static const Property virtio_ccw_net_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c index c9a15c4eb6..ccd124ee91 100644 --- a/hw/s390x/virtio-ccw-rng.c +++ b/hw/s390x/virtio-ccw-rng.c @@ -48,7 +48,6 @@ static const Property virtio_ccw_rng_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c index bec9a73518..bfcea3cfe7 100644 --- a/hw/s390x/virtio-ccw-scsi.c +++ b/hw/s390x/virtio-ccw-scsi.c @@ -58,7 +58,6 @@ static const Property virtio_ccw_scsi_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index 037d4f9db1..59743d1e25 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -58,7 +58,6 @@ static const Property virtio_ccw_serial_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index a06113d908..de628ae89b 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1414,7 +1414,6 @@ static const Property mptsas_properties[] = { DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), /* TODO: test MSI support under Windows */ DEFINE_PROP_ON_OFF_AUTO("msi", MPTSASState, msi, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; static void mptsas1068_class_init(ObjectClass *oc, void *data) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 2f1678d51e..181720b7a8 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1947,7 +1947,6 @@ static const Property scsi_props[] = { DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), - DEFINE_PROP_END_OF_LIST(), }; static void scsi_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a47b80907f..16351b4988 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3227,7 +3227,6 @@ static const Property scsi_hd_properties[] = { quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE, 0), DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_scsi_disk_state = { @@ -3285,7 +3284,6 @@ static const Property scsi_cd_properties[] = { 0), DEFINE_PROP_BIT("quirk_mode_page_truncated", SCSIDiskState, quirks, SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0), - DEFINE_PROP_END_OF_LIST(), }; static void scsi_cd_class_initfn(ObjectClass *klass, void *data) @@ -3323,7 +3321,6 @@ static const Property scsi_block_properties[] = { -1), DEFINE_PROP_UINT32("io_timeout", SCSIDiskState, qdev.io_timeout, DEFAULT_IO_TIMEOUT), - DEFINE_PROP_END_OF_LIST(), }; static void scsi_block_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index d7ae7549d0..0290a196cc 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -777,7 +777,6 @@ static const Property scsi_generic_properties[] = { DEFINE_PROP_BOOL("share-rw", SCSIDevice, conf.share_rw, false), DEFINE_PROP_UINT32("io_timeout", SCSIDevice, io_timeout, DEFAULT_IO_TIMEOUT), - DEFINE_PROP_END_OF_LIST(), }; static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 7c55e4d40f..6962194eaa 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1252,7 +1252,6 @@ static int spapr_vscsi_devnode(SpaprVioDevice *dev, void *fdt, int node_off) static const Property spapr_vscsi_properties[] = { DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_spapr_vscsi = { diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index effb8dab1f..9f760663a0 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -362,7 +362,6 @@ static const Property vhost_scsi_properties[] = { DEFINE_PROP_BOOL("migratable", VHostSCSICommon, migratable, false), DEFINE_PROP_BOOL("worker_per_virtqueue", VirtIOSCSICommon, conf.worker_per_virtqueue, false), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_scsi_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index d5265c57bc..689bc6c13e 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -360,7 +360,6 @@ static const Property vhost_user_scsi_properties[] = { DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, VIRTIO_SCSI_F_T10_PI, false), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_scsi_reset(VirtIODevice *vdev) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index c0a4f1a620..eb70a7a46e 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -1302,7 +1302,6 @@ static const Property virtio_scsi_properties[] = { VIRTIO_SCSI_F_CHANGE, true), DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread, TYPE_IOTHREAD, IOThread *), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_virtio_scsi = { diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 46cec531cc..f07e377cb8 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1302,7 +1302,6 @@ static const Property pvscsi_properties[] = { PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false), DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags, PVSCSI_COMPAT_DISABLE_PCIE_BIT, false), - DEFINE_PROP_END_OF_LIST(), }; static void pvscsi_realize(DeviceState *qdev, Error **errp) diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index be39ec2e71..0b235bccfd 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -811,7 +811,6 @@ static const VMStateDescription vmstate_allwinner_sdhost = { static const Property allwinner_sdhost_properties[] = { DEFINE_PROP_LINK("dma-memory", AwSdHostState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void allwinner_sdhost_init(Object *obj) diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index 99703f1842..12cbbae5e7 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -206,7 +206,6 @@ static const VMStateDescription vmstate_aspeed_sdhci = { static const Property aspeed_sdhci_properties[] = { DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b994ef581e..779bac6631 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2800,19 +2800,16 @@ static void emmc_realize(DeviceState *dev, Error **errp) static const Property sdmmc_common_properties[] = { DEFINE_PROP_DRIVE("drive", SDState, blk), - DEFINE_PROP_END_OF_LIST() }; static const Property sd_properties[] = { DEFINE_PROP_UINT8("spec_version", SDState, spec_version, SD_PHY_SPECv3_01_VERS), - DEFINE_PROP_END_OF_LIST() }; static const Property emmc_properties[] = { DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), - DEFINE_PROP_END_OF_LIST() }; static void sdmmc_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index 83892a7a15..5268c0dee5 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -24,7 +24,6 @@ static const Property sdhci_pci_properties[] = { DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), - DEFINE_PROP_END_OF_LIST(), }; static void sdhci_pci_realize(PCIDevice *dev, Error **errp) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index e697ee05b3..64a936fcf0 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1550,7 +1550,6 @@ static const Property sdhci_sysbus_properties[] = { false), DEFINE_PROP_LINK("dma", SDHCIState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void sdhci_sysbus_init(Object *obj) diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index 3d6fcdf576..8c1fc82534 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -370,7 +370,6 @@ static void iommu_init(Object *obj) static const Property iommu_properties[] = { DEFINE_PROP_UINT32("version", IOMMUState, version, 0), - DEFINE_PROP_END_OF_LIST(), }; static void iommu_class_init(ObjectClass *klass, void *data) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 05b8c7369e..b6f65e8d2f 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -377,7 +377,6 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) static const Property ebus_properties[] = { DEFINE_PROP_UINT64("console-serial-base", EbusState, console_serial_base, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ebus_class_init(ObjectClass *klass, void *data) @@ -529,7 +528,6 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size) static const Property ram_properties[] = { DEFINE_PROP_UINT64("size", RamDevice, size, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ram_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index bbdd4e4786..faef1a8e5b 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1292,7 +1292,6 @@ static const Property aspeed_smc_properties[] = { DEFINE_PROP_UINT64("dram-base", AspeedSMCState, dram_base, 0), DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_smc_class_init(ObjectClass *klass, void *data) @@ -1340,7 +1339,6 @@ static const Property aspeed_smc_flash_properties[] = { DEFINE_PROP_UINT8("cs", AspeedSMCFlash, cs, 0), DEFINE_PROP_LINK("controller", AspeedSMCFlash, controller, TYPE_ASPEED_SMC, AspeedSMCState *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c index 60a0b17b62..46c7b633c2 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -563,7 +563,6 @@ static const MemoryRegionOps ibex_spi_ops = { static const Property ibex_spi_properties[] = { DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_ibex = { diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index fdd3ad2fdc..21fc489038 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -543,7 +543,6 @@ static const VMStateDescription vmstate_npcm7xx_fiu = { static const Property npcm7xx_fiu_properties[] = { DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0), - DEFINE_PROP_END_OF_LIST(), }; static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 4ca9c469a4..15e25bd1be 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -1198,7 +1198,6 @@ static const MemoryRegionOps pnv_spi_xscom_ops = { static const Property pnv_spi_properties[] = { DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_spi_realize(DeviceState *dev, Error **errp) diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c index 7458747779..76f8654f41 100644 --- a/hw/ssi/sifive_spi.c +++ b/hw/ssi/sifive_spi.c @@ -330,7 +330,6 @@ static void sifive_spi_realize(DeviceState *dev, Error **errp) static const Property sifive_spi_properties[] = { DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_spi_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index cab0014c3f..872c4e8036 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -110,7 +110,6 @@ static void ssi_peripheral_realize(DeviceState *dev, Error **errp) static const Property ssi_peripheral_properties[] = { DEFINE_PROP_UINT8("cs", SSIPeripheral, cs_index, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ssi_peripheral_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index 588c1ec071..fd1ff12eb1 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -363,7 +363,6 @@ static const VMStateDescription vmstate_xilinx_spi = { static const Property xilinx_spi_properties[] = { DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_spi_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index f72cb3cbc8..984c178087 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1422,14 +1422,12 @@ static const VMStateDescription vmstate_xlnx_zynqmp_qspips = { static const Property xilinx_zynqmp_qspips_properties[] = { DEFINE_PROP_UINT32("dma-burst-size", XlnxZynqMPQSPIPS, dma_burst_size, 64), - DEFINE_PROP_END_OF_LIST(), }; static const Property xilinx_spips_properties[] = { DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4), DEFINE_PROP_UINT8("num-txrx-bytes", XilinxSPIPS, num_txrx_bytes, 1), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_qspips_class_init(ObjectClass *klass, void * data) diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c index e51abe9de2..9e96c9b69a 100644 --- a/hw/ssi/xlnx-versal-ospi.c +++ b/hw/ssi/xlnx-versal-ospi.c @@ -1829,7 +1829,6 @@ static const 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) diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index c0a91bab0c..0c4eef2636 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -375,7 +375,6 @@ static const VMStateDescription vmstate_a9_gtimer = { static const Property a9_gtimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() }; static void a9_gtimer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index 2904ccfb42..ddaf2128c2 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -193,7 +193,6 @@ static const Property a10_pit_properties[] = { DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0), DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0), DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_a10_pit = { diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 6244a7a84f..803dad1e8a 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -302,7 +302,6 @@ static const VMStateDescription vmstate_arm_mptimer = { static const Property arm_mptimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() }; static void arm_mptimer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index dfa034296c..1213b77aa0 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -390,7 +390,6 @@ static const TypeInfo icp_pit_info = { static const Property sp804_properties[] = { DEFINE_PROP_UINT32("freq0", SP804State, freq0, 1000000), DEFINE_PROP_UINT32("freq1", SP804State, freq1, 1000000), - DEFINE_PROP_END_OF_LIST(), }; static void sp804_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 4c16b5016e..4868651ad4 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -677,7 +677,6 @@ static const VMStateDescription vmstate_aspeed_timer_state = { static const Property aspeed_timer_properties[] = { DEFINE_PROP_LINK("scu", AspeedTimerCtrlState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), - DEFINE_PROP_END_OF_LIST(), }; static void timer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c index 2e3ce83c43..96baf9cf60 100644 --- a/hw/timer/avr_timer16.c +++ b/hw/timer/avr_timer16.c @@ -546,7 +546,6 @@ static const Property avr_timer16_properties[] = { DEFINE_PROP_UINT8("id", struct AVRTimer16State, id, 0), DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State, cpu_freq_hz, 0), - DEFINE_PROP_END_OF_LIST(), }; static void avr_timer16_pr(void *opaque, int irq, int level) diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index a7428ed938..f0802b6eb6 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -407,7 +407,6 @@ static const Property grlib_gptimer_properties[] = { DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), - DEFINE_PROP_END_OF_LIST(), }; static void grlib_gptimer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 46886c379e..2a45410c0d 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -750,7 +750,6 @@ static const Property hpet_device_properties[] = { DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0), DEFINE_PROP_BOOL("hpet-offset-saved", HPETState, hpet_offset_saved, true), - DEFINE_PROP_END_OF_LIST(), }; static void hpet_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c index 953c1e11eb..29105afcc3 100644 --- a/hw/timer/i8254_common.c +++ b/hw/timer/i8254_common.c @@ -240,7 +240,6 @@ static const VMStateDescription vmstate_pit_common = { static const Property pit_common_properties[] = { DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), - DEFINE_PROP_END_OF_LIST(), }; static void pit_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index fba4466a89..3ebc870097 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -265,7 +265,6 @@ static const VMStateDescription vmstate_ibex_timer = { static const Property ibex_timer_properties[] = { DEFINE_PROP_UINT32("timebase-freq", IbexTimerState, timebase_freq, 10000), - DEFINE_PROP_END_OF_LIST(), }; static void ibex_timer_init(Object *obj) diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index e5c5cd6a84..594da64eae 100644 --- a/hw/timer/mss-timer.c +++ b/hw/timer/mss-timer.c @@ -283,7 +283,6 @@ static const Property mss_timer_properties[] = { /* Libero GUI shows 100Mhz as default for clocks */ DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz, 100 * 1000000), - DEFINE_PROP_END_OF_LIST(), }; static void mss_timer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c index 48fccec1bf..11ad8b575e 100644 --- a/hw/timer/nrf51_timer.c +++ b/hw/timer/nrf51_timer.c @@ -381,7 +381,6 @@ static const VMStateDescription vmstate_nrf51_timer = { static const Property nrf51_timer_properties[] = { DEFINE_PROP_UINT8("id", NRF51TimerState, id, 0), - DEFINE_PROP_END_OF_LIST(), }; static void nrf51_timer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 345145bfa8..6ec3fa54dd 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -553,7 +553,6 @@ static const Property pxa25x_timer_dev_properties[] = { DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, PXA2XX_TIMER_HAVE_TM4, false), - DEFINE_PROP_END_OF_LIST(), }; static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c index 6d451fa86b..93e7f58cc2 100644 --- a/hw/timer/renesas_cmt.c +++ b/hw/timer/renesas_cmt.c @@ -255,7 +255,6 @@ static const VMStateDescription vmstate_rcmt = { static const Property rcmt_properties[] = { DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), - DEFINE_PROP_END_OF_LIST(), }; static void rcmt_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c index 890f803cf8..884349c2cc 100644 --- a/hw/timer/renesas_tmr.c +++ b/hw/timer/renesas_tmr.c @@ -465,7 +465,6 @@ static const VMStateDescription vmstate_rtmr = { static const Property rtmr_properties[] = { DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), - DEFINE_PROP_END_OF_LIST(), }; static void rtmr_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c index 042c89c67a..fc796e9bc3 100644 --- a/hw/timer/sifive_pwm.c +++ b/hw/timer/sifive_pwm.c @@ -408,7 +408,6 @@ static const Property sifive_pwm_properties[] = { /* 0.5Ghz per spec after FSBL */ DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState, freq_hz, 500000000ULL), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_pwm_init(Object *obj) diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 32991f4436..65b24e4f06 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -422,7 +422,6 @@ static void slavio_timer_init(Object *obj) static const Property slavio_timer_properties[] = { DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), - DEFINE_PROP_END_OF_LIST(), }; static void slavio_timer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/sse-timer.c b/hw/timer/sse-timer.c index 6b7a67941c..e106739ea9 100644 --- a/hw/timer/sse-timer.c +++ b/hw/timer/sse-timer.c @@ -442,7 +442,6 @@ static const VMStateDescription sse_timer_vmstate = { static const Property sse_timer_properties[] = { DEFINE_PROP_LINK("counter", SSETimer, counter, TYPE_SSE_COUNTER, SSECounter *), - DEFINE_PROP_END_OF_LIST(), }; static void sse_timer_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index d9d745cd76..4707190d6a 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -301,7 +301,6 @@ static const VMStateDescription vmstate_stm32f2xx_timer = { static const Property stm32f2xx_timer_properties[] = { DEFINE_PROP_UINT64("clock-frequency", struct STM32F2XXTimerState, freq_hz, 1000000000), - DEFINE_PROP_END_OF_LIST(), }; static void stm32f2xx_timer_init(Object *obj) diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 7fe3e83baa..4955fe1b01 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -245,7 +245,6 @@ static void xilinx_timer_init(Object *obj) static const Property xilinx_timer_properties[] = { DEFINE_PROP_UINT32("clock-frequency", XpsTimerState, freq_hz, 62 * 1000000), DEFINE_PROP_UINT8("one-timer-only", XpsTimerState, one_timer_only, 0), - DEFINE_PROP_END_OF_LIST(), }; static void xilinx_timer_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 2bf6e7ffe9..e652679a1f 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -229,7 +229,6 @@ static const VMStateDescription vmstate_tpm_crb = { static const Property tpm_crb_properties[] = { DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), DEFINE_PROP_BOOL("ppi", CRBState, ppi_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_crb_reset(void *dev) diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index e15b67dd45..797fd8b290 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -367,7 +367,6 @@ static const VMStateDescription vmstate_spapr_vtpm = { static const Property tpm_spapr_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprTpmState, vdev), DEFINE_PROP_TPMBE("tpmdev", SpaprTpmState, be_driver), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp) diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c index b27af230cd..504328e3b0 100644 --- a/hw/tpm/tpm_tis_i2c.c +++ b/hw/tpm/tpm_tis_i2c.c @@ -493,7 +493,6 @@ static int tpm_tis_i2c_send(I2CSlave *i2c, uint8_t data) static const Property tpm_tis_i2c_properties[] = { DEFINE_PROP_TPMBE("tpmdev", TPMStateI2C, state.be_driver), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_tis_i2c_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 9b2160972a..876cb02cb5 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -95,7 +95,6 @@ static const Property tpm_tis_isa_properties[] = { DEFINE_PROP_UINT32("irq", TPMStateISA, state.irq_num, TPM_TIS_IRQ), DEFINE_PROP_TPMBE("tpmdev", TPMStateISA, state.be_driver), DEFINE_PROP_BOOL("ppi", TPMStateISA, state.ppi_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_tis_isa_initfn(Object *obj) diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 88c1f1e478..ee0bfe9538 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -93,7 +93,6 @@ static void tpm_tis_sysbus_reset(DeviceState *dev) static const Property tpm_tis_sysbus_properties[] = { DEFINE_PROP_UINT32("irq", TPMStateSysBus, state.irq_num, TPM_TIS_IRQ), DEFINE_PROP_TPMBE("tpmdev", TPMStateSysBus, state.be_driver), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_tis_sysbus_initfn(Object *obj) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 74ff52ad09..4100ea28e2 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -277,7 +277,6 @@ static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) static const Property ufs_lu_props[] = { DEFINE_PROP_DRIVE("drive", UfsLu, conf.blk), DEFINE_PROP_UINT8("lun", UfsLu, lun, 0), - DEFINE_PROP_END_OF_LIST(), }; static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index fe77158439..8d26d13791 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1758,7 +1758,6 @@ static const Property ufs_props[] = { DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false), DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription ufs_vmstate = { diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 80e6a92820..7e4ff36fed 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -24,7 +24,6 @@ static const Property usb_props[] = { DEFINE_PROP_BIT("msos-desc", USBDevice, flags, USB_DEV_FLAG_MSOS_DESC_ENABLE, true), DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename), - DEFINE_PROP_END_OF_LIST() }; static void usb_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c index 7cb600e3c8..fae212f053 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -298,7 +298,6 @@ static void canokey_unrealize(USBDevice *base) static const Property canokey_properties[] = { DEFINE_PROP_STRING("file", CanoKeyState, file), - DEFINE_PROP_END_OF_LIST(), }; static void canokey_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index dd58333943..b1e330f21d 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -589,7 +589,6 @@ static const Property emulated_card_properties[] = { DEFINE_PROP_STRING("cert3", EmulatedState, cert3), DEFINE_PROP_STRING("db", EmulatedState, db), DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), - DEFINE_PROP_END_OF_LIST(), }; static void emulated_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index f97dcf767f..bf81485f87 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -391,7 +391,6 @@ static const VMStateDescription passthru_vmstate = { static const Property passthru_card_properties[] = { DEFINE_PROP_CHR("chardev", PassthruState, cs), DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), - DEFINE_PROP_END_OF_LIST(), }; static void passthru_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 6007f16d30..40f031252a 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -995,7 +995,6 @@ static const Property usb_audio_properties[] = { DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0), DEFINE_PROP_BOOL("multi", USBAudioState, multi, false), - DEFINE_PROP_END_OF_LIST(), }; static void usb_audio_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index d83f67b984..accdd460e3 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -797,7 +797,6 @@ static const Property usb_tablet_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), DEFINE_PROP_UINT32("head", USBHIDState, head, 0), - DEFINE_PROP_END_OF_LIST(), }; static void usb_tablet_class_initfn(ObjectClass *klass, void *data) @@ -820,7 +819,6 @@ static const TypeInfo usb_tablet_info = { static const Property usb_mouse_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_END_OF_LIST(), }; static void usb_mouse_class_initfn(ObjectClass *klass, void *data) @@ -844,7 +842,6 @@ static const TypeInfo usb_mouse_info = { static const Property usb_keyboard_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), - DEFINE_PROP_END_OF_LIST(), }; static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 317ca0b081..3880e2aca8 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -668,7 +668,6 @@ static const VMStateDescription vmstate_usb_hub = { static const Property usb_hub_properties[] = { DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8), DEFINE_PROP_BOOL("port-power", USBHubState, port_power, false), - DEFINE_PROP_END_OF_LIST(), }; static void usb_hub_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 7994727e5e..326c92a43d 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -2082,7 +2082,6 @@ static const Property mtp_properties[] = { DEFINE_PROP_STRING("rootdir", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), - DEFINE_PROP_END_OF_LIST(), }; static void usb_mtp_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 6c4f5776d4..81863105ac 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1409,7 +1409,6 @@ static const VMStateDescription vmstate_usb_net = { static const Property net_properties[] = { DEFINE_NIC_PROPERTIES(USBNetState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void usb_net_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 0c3e9160ec..a0821db902 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -637,7 +637,6 @@ static const VMStateDescription vmstate_usb_serial = { static const Property serial_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false), - DEFINE_PROP_END_OF_LIST(), }; static void usb_serial_dev_class_init(ObjectClass *klass, void *data) @@ -679,7 +678,6 @@ static const TypeInfo serial_info = { static const Property braille_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_END_OF_LIST(), }; static void usb_braille_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index c3c02f0aad..73deb3ce83 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1173,7 +1173,6 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s) static const Property ccid_props[] = { DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), - DEFINE_PROP_END_OF_LIST(), }; static const TypeInfo ccid_bus_info = { @@ -1433,7 +1432,6 @@ static const VMStateDescription ccid_vmstate = { static const Property ccid_properties[] = { DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ccid_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index ca037ba96f..818f58db2f 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -72,7 +72,6 @@ static const Property msd_properties[] = { DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), DEFINE_PROP_BOOL("removable", MSDState, removable, false), DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false), - DEFINE_PROP_END_OF_LIST(), }; static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 57e8d20051..44e30013d7 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -955,7 +955,6 @@ static const VMStateDescription vmstate_usb_uas = { static const Property uas_properties[] = { DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0), - DEFINE_PROP_END_OF_LIST(), }; static void usb_uas_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index 6a10f3e9cd..e8152719f8 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1450,7 +1450,6 @@ const VMStateDescription vmstate_dwc2_state = { static const Property dwc2_usb_properties[] = { DEFINE_PROP_UINT32("usb_version", DWC2State, usb_version, 2), - DEFINE_PROP_END_OF_LIST(), }; static void dwc2_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index ff970bd989..9ce9ba0b04 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -659,7 +659,6 @@ static const VMStateDescription vmstate_usb_dwc3 = { static const Property usb_dwc3_properties[] = { DEFINE_PROP_UINT32("DWC_USB3_USERID", USBDWC3, cfg.dwc_usb3_user, 0x12345678), - DEFINE_PROP_END_OF_LIST(), }; static void usb_dwc3_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 374f25c5ed..d410c38a8a 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -137,7 +137,6 @@ static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, static const Property ehci_pci_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_ehci_pci = { diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index f4e08aab89..768c3dd797 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -34,7 +34,6 @@ static const Property ehci_sysbus_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), DEFINE_PROP_BOOL("companion-enable", EHCISysBusState, ehci.companion_enable, false), - DEFINE_PROP_END_OF_LIST(), }; static void usb_ehci_sysbus_realize(DeviceState *dev, Error **errp) diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 459644cc1b..b3684a2ef6 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -113,7 +113,6 @@ static const Property ohci_pci_properties[] = { DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_ohci = { diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c index 81cf2e558d..15311949b3 100644 --- a/hw/usb/hcd-ohci-sysbus.c +++ b/hw/usb/hcd-ohci-sysbus.c @@ -62,7 +62,6 @@ static const Property ohci_sysbus_properties[] = { DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), - DEFINE_PROP_END_OF_LIST(), }; static void ohci_sysbus_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 245352c231..142f24f2ea 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1232,12 +1232,10 @@ static const Property uhci_properties_companion[] = { DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), }; static const Property uhci_properties_standalone[] = { DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), }; static void uhci_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index f06e7403e2..b1df95b52a 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -41,7 +41,6 @@ static const Property nec_xhci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS), - DEFINE_PROP_END_OF_LIST(), }; static void nec_xhci_instance_init(Object *obj) diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index f4dbad7cc6..ce43322396 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -85,7 +85,6 @@ void xhci_sysbus_build_aml(Aml *scope, uint32_t mmio, unsigned int irq) static const Property xhci_sysbus_props[] = { DEFINE_PROP_UINT32("intrs", XHCISysbusState, xhci.numintrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCISysbusState, xhci.numslots, XHCI_MAXSLOTS), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_xhci_sysbus = { diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 3c5006f425..3719c0f190 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3612,7 +3612,6 @@ static const Property xhci_properties[] = { DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), DEFINE_PROP_LINK("host", XHCIState, hostOpaque, TYPE_DEVICE, DeviceState *), - DEFINE_PROP_END_OF_LIST(), }; static void xhci_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 85d33b51ba..0c753f5cc5 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1779,7 +1779,6 @@ static const Property usb_host_dev_properties[] = { USB_HOST_OPT_PIPELINE, true), DEFINE_PROP_BOOL("suppress-remote-wake", USBHostDevice, suppress_remote_wake, true), - DEFINE_PROP_END_OF_LIST(), }; static void usb_host_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index f72a612d5a..96fb963809 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -2580,7 +2580,6 @@ static const Property usbredir_properties[] = { DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true), DEFINE_PROP_BOOL("suppress-remote-wake", USBRedirDevice, suppress_remote_wake, true), - DEFINE_PROP_END_OF_LIST(), }; static void usbredir_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/usb/u2f-emulated.c b/hw/usb/u2f-emulated.c index df86ce97fc..e1dd19ee92 100644 --- a/hw/usb/u2f-emulated.c +++ b/hw/usb/u2f-emulated.c @@ -375,7 +375,6 @@ static const Property u2f_emulated_properties[] = { DEFINE_PROP_STRING("privkey", U2FEmulatedState, privkey), DEFINE_PROP_STRING("entropy", U2FEmulatedState, entropy), DEFINE_PROP_STRING("counter", U2FEmulatedState, counter), - DEFINE_PROP_END_OF_LIST(), }; static void u2f_emulated_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index ec4f6165d8..8df5215a1f 100644 --- a/hw/usb/u2f-passthru.c +++ b/hw/usb/u2f-passthru.c @@ -518,7 +518,6 @@ static const VMStateDescription u2f_passthru_vmstate = { static const Property u2f_passthru_properties[] = { DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw), - DEFINE_PROP_END_OF_LIST(), }; static void u2f_passthru_class_init(ObjectClass *klass, void *data) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 2e6ea2dd93..529aad6332 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -197,7 +197,6 @@ static const Property vfio_ap_properties[] = { DEFINE_PROP_LINK("iommufd", VFIOAPDevice, vdev.iommufd, TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), #endif - DEFINE_PROP_END_OF_LIST(), }; static void vfio_ap_reset(DeviceState *dev) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index b96ab27e12..bac56e8dd9 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -663,7 +663,6 @@ static const Property vfio_ccw_properties[] = { TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), #endif DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vfio_ccw_vmstate = { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 93aca850e3..0c5621da36 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3409,7 +3409,6 @@ static const Property vfio_pci_dev_properties[] = { TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), #endif DEFINE_PROP_BOOL("skip-vsc-check", VFIOPCIDevice, skip_vsc_check, true), - DEFINE_PROP_END_OF_LIST(), }; #ifdef CONFIG_IOMMUFD @@ -3455,7 +3454,6 @@ static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, void *data) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 766e8a86ef..7bc52a6f56 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -640,7 +640,6 @@ static const Property vfio_platform_dev_properties[] = { DEFINE_PROP_LINK("iommufd", VFIOPlatformDevice, vbasedev.iommufd, TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), #endif - DEFINE_PROP_END_OF_LIST(), }; static void vfio_platform_instance_init(Object *obj) diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 61849b3b0e..d7b6af03f6 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -340,7 +340,6 @@ static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) static const Property vhost_vdpa_device_properties[] = { DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_vhost_vdpa_device = { diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c index 7536b37f18..3778f6131e 100644 --- a/hw/virtio/vhost-scsi-pci.c +++ b/hw/virtio/vhost-scsi-pci.c @@ -41,7 +41,6 @@ struct VHostSCSIPCI { static const Property vhost_scsi_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c index 99f1472023..1767ef2c9c 100644 --- a/hw/virtio/vhost-user-blk-pci.c +++ b/hw/virtio/vhost-user-blk-pci.c @@ -47,7 +47,6 @@ static const Property vhost_user_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-user-device.c b/hw/virtio/vhost-user-device.c index 3222b67bd9..86eba138b4 100644 --- a/hw/virtio/vhost-user-device.c +++ b/hw/virtio/vhost-user-device.c @@ -35,7 +35,6 @@ static const Property vud_properties[] = { DEFINE_PROP_UINT32("vq_size", VHostUserBase, vq_size, 64), DEFINE_PROP_UINT32("num_vqs", VHostUserBase, num_vqs, 1), DEFINE_PROP_UINT32("config_size", VHostUserBase, config_size, 0), - DEFINE_PROP_END_OF_LIST(), }; static void vud_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c index 9ba6c40655..116eaab907 100644 --- a/hw/virtio/vhost-user-fs-pci.c +++ b/hw/virtio/vhost-user-fs-pci.c @@ -32,7 +32,6 @@ DECLARE_INSTANCE_CHECKER(VHostUserFSPCI, VHOST_USER_FS_PCI, static const Property vhost_user_fs_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index c0462329a5..f8714b5660 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -409,7 +409,6 @@ static const Property vuf_properties[] = { DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, conf.num_request_queues, 1), DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128), - DEFINE_PROP_END_OF_LIST(), }; static void vuf_instance_init(Object *obj) diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c index c997c66d80..4a08814904 100644 --- a/hw/virtio/vhost-user-gpio.c +++ b/hw/virtio/vhost-user-gpio.c @@ -16,7 +16,6 @@ static const Property vgpio_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vgpio_realize(DeviceState *dev, Error **errp) diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index b0a5cbf3ea..1c7cde503c 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -16,7 +16,6 @@ static const Property vi2c_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vi2c_realize(DeviceState *dev, Error **errp) diff --git a/hw/virtio/vhost-user-input.c b/hw/virtio/vhost-user-input.c index c57cc461bb..917405329f 100644 --- a/hw/virtio/vhost-user-input.c +++ b/hw/virtio/vhost-user-input.c @@ -9,7 +9,6 @@ static const Property vinput_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vinput_realize(DeviceState *dev, Error **errp) diff --git a/hw/virtio/vhost-user-rng-pci.c b/hw/virtio/vhost-user-rng-pci.c index 0016ee74ce..a4e690148d 100644 --- a/hw/virtio/vhost-user-rng-pci.c +++ b/hw/virtio/vhost-user-rng-pci.c @@ -26,7 +26,6 @@ DECLARE_INSTANCE_CHECKER(VHostUserRNGPCI, VHOST_USER_RNG_PCI, static const Property vhost_user_rng_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c index c9985b5fad..5aa432e5e1 100644 --- a/hw/virtio/vhost-user-rng.c +++ b/hw/virtio/vhost-user-rng.c @@ -22,7 +22,6 @@ static const VMStateDescription vu_rng_vmstate = { static const Property vrng_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vu_rng_base_realize(DeviceState *dev, Error **errp) diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index a15e6916ef..410a936ca7 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c @@ -279,7 +279,6 @@ static const VMStateDescription vu_scmi_vmstate = { static const Property vu_scmi_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vu_scmi_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index b2f6451f48..34e1a701b1 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -47,7 +47,6 @@ struct VHostUserSCSIPCI { static const Property vhost_user_scsi_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-user-snd.c b/hw/virtio/vhost-user-snd.c index 8810a9f699..8610370af8 100644 --- a/hw/virtio/vhost-user-snd.c +++ b/hw/virtio/vhost-user-snd.c @@ -23,7 +23,6 @@ static const VMStateDescription vu_snd_vmstate = { static const Property vsnd_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vu_snd_base_realize(DeviceState *dev, Error **errp) diff --git a/hw/virtio/vhost-user-vsock-pci.c b/hw/virtio/vhost-user-vsock-pci.c index 529d967059..f730a05e78 100644 --- a/hw/virtio/vhost-user-vsock-pci.c +++ b/hw/virtio/vhost-user-vsock-pci.c @@ -33,7 +33,6 @@ struct VHostUserVSockPCI { static const Property vhost_user_vsock_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 97885bfeab..293273080b 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -150,7 +150,6 @@ static void vuv_device_unrealize(DeviceState *dev) static const Property vuv_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserVSock, conf.chardev), - DEFINE_PROP_END_OF_LIST(), }; static void vuv_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index cb2253c39f..9ac587d20c 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -288,7 +288,6 @@ static struct vhost_dev *vhost_vsock_common_get_vhost(VirtIODevice *vdev) static const Property vhost_vsock_common_properties[] = { DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_vsock_common_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index 1d9abd02bd..6c618ee908 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -37,7 +37,6 @@ struct VHostVSockPCI { static const Property vhost_vsock_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index ce80e84494..940b30fa27 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -208,7 +208,6 @@ static void vhost_vsock_device_unrealize(DeviceState *dev) static const Property vhost_vsock_properties[] = { DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_vsock_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c index b33faf2fbb..aa1dce8f28 100644 --- a/hw/virtio/virtio-9p-pci.c +++ b/hw/virtio/virtio-9p-pci.c @@ -47,7 +47,6 @@ static const Property virtio_9p_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index ab2ee30475..ec4aa945f2 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -1032,7 +1032,6 @@ static const Property virtio_balloon_properties[] = { qemu_4_0_config_size, false), DEFINE_PROP_LINK("iothread", VirtIOBalloon, iothread, TYPE_IOTHREAD, IOThread *), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_balloon_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c index abdcc11b2e..fc06cec656 100644 --- a/hw/virtio/virtio-blk-pci.c +++ b/hw/virtio/virtio-blk-pci.c @@ -44,7 +44,6 @@ static const Property virtio_blk_pci_properties[] = { VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 23c85fe586..8699481375 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -41,7 +41,6 @@ static const Property virtio_crypto_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 9ae0b02598..617163f127 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -1131,7 +1131,6 @@ static const VMStateDescription vmstate_virtio_crypto = { static const Property virtio_crypto_properties[] = { DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config) diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index 55c0b0555b..9e3c106777 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -39,7 +39,6 @@ struct VirtIOInputHIDPCI { static const Property virtio_input_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 92adf63684..97e03ce803 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -39,7 +39,6 @@ static const Property virtio_iommu_pci_properties[] = { DEFINE_PROP_ARRAY("reserved-regions", VirtIOIOMMUPCI, vdev.nr_prop_resv_regions, vdev.prop_resv_regions, qdev_prop_reserved_region, ReservedRegion), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 576ad8383f..0988d21209 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1662,7 +1662,6 @@ static const Property virtio_iommu_properties[] = { DEFINE_PROP_GRANULE_MODE("granule", VirtIOIOMMU, granule_mode, GRANULE_MODE_HOST), DEFINE_PROP_UINT8("aw-bits", VirtIOIOMMU, aw_bits, 64), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_iommu_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 3f6f46fad7..317d056eba 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1694,7 +1694,6 @@ static const Property virtio_mem_properties[] = { early_migration, true), DEFINE_PROP_BOOL(VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, VirtIOMEM, dynamic_memslots, false), - DEFINE_PROP_END_OF_LIST(), }; static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm, diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 49d9fe8f30..27d2f4b3b0 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -757,7 +757,6 @@ static const Property virtio_mmio_properties[] = { DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags, VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_mmio_realizefn(DeviceState *d, Error **errp) diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index e86094ae22..e18953ad67 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -43,7 +43,6 @@ static const Property virtio_net_properties[] = { VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c index 685c548361..098e1aeac6 100644 --- a/hw/virtio/virtio-nsm.c +++ b/hw/virtio/virtio-nsm.c @@ -1707,7 +1707,6 @@ static const VMStateDescription vmstate_virtio_nsm = { static const Property virtio_nsm_properties[] = { DEFINE_PROP_STRING("module-id", VirtIONSM, module_id), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_nsm_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index de41cb5ef2..cb61adc659 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2378,7 +2378,6 @@ static const Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_INIT_FLR_BIT, true), DEFINE_PROP_BIT("aer", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_AER_BIT, false), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) @@ -2435,7 +2434,6 @@ static const Property virtio_pci_generic_properties[] = { DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_pci_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index f6f3b5ddaf..9759023ab3 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -159,7 +159,6 @@ static const Property virtio_pmem_properties[] = { DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP, VirtIOPMEM, start, 0), DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP, VirtIOPMEM, memdev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_pmem_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c index 398f432237..a94ff767b2 100644 --- a/hw/virtio/virtio-rng-pci.c +++ b/hw/virtio/virtio-rng-pci.c @@ -37,7 +37,6 @@ static const Property virtio_rng_properties[] = { VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 13a1a0b236..0660e872f7 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -258,7 +258,6 @@ static const Property virtio_rng_properties[] = { DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_rng_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index 733b5756db..d44fd2fffb 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -40,7 +40,6 @@ static const Property virtio_scsi_pci_properties[] = { VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c index bda643ec54..b5b77eb266 100644 --- a/hw/virtio/virtio-serial-pci.c +++ b/hw/virtio/virtio-serial-pci.c @@ -74,7 +74,6 @@ static const Property virtio_serial_pci_properties[] = { VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7fcdb55ba4..b871295b94 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -4018,7 +4018,6 @@ static const Property virtio_properties[] = { DEFINE_PROP_BOOL("use-disabled-flag", VirtIODevice, use_disabled_flag, true), DEFINE_PROP_BOOL("x-disable-legacy-check", VirtIODevice, disable_legacy_check, false), - DEFINE_PROP_END_OF_LIST(), }; static int virtio_device_start_ioeventfd_impl(VirtIODevice *vdev) diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 2e25d4b4e9..6b6fc31f22 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -270,7 +270,6 @@ static const Property wdt_sbsa_gwdt_props[] = { */ DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq, 62500000), - DEFINE_PROP_END_OF_LIST(), }; static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index c95877e5c7..78dedb065e 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -291,7 +291,6 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) static const Property aspeed_wdt_properties[] = { DEFINE_PROP_LINK("scu", AspeedWDTState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), - DEFINE_PROP_END_OF_LIST(), }; static void aspeed_wdt_class_init(ObjectClass *klass, void *data) diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index 61fbd91ee4..878e5098b6 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -284,7 +284,6 @@ static void imx2_wdt_realize(DeviceState *dev, Error **errp) static const Property imx2_wdt_properties[] = { DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support, false), - DEFINE_PROP_END_OF_LIST() }; static void imx2_wdt_class_init(ObjectClass *klass, void *data) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 0d7defb8cd..2007ec0892 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -1095,7 +1095,6 @@ unrealize: static const Property xen_device_props[] = { DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, DOMID_INVALID), - DEFINE_PROP_END_OF_LIST() }; static void xen_device_class_init(ObjectClass *class, void *data) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 557aa98be4..e2bd4c7d41 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -934,7 +934,6 @@ static void xen_pt_unregister_device(PCIDevice *d) static const Property xen_pci_passthrough_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false), - DEFINE_PROP_END_OF_LIST(), }; static void xen_pci_passthrough_instance_init(Object *obj) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index afa667b68f..e6ef80b7fd 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -941,22 +941,22 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); /** * device_class_set_props(): add a set of properties to an device * @dc: the parent DeviceClass all devices inherit - * @props: an array of properties, terminate by DEFINE_PROP_END_OF_LIST() + * @props: an array of properties * * This will add a set of properties to the object. It will fault if * you attempt to add an existing property defined by a parent class. * To modify an inherited property you need to use???? * - * Validate that @props has at least one Property plus the terminator. + * Validate that @props has at least one Property. * Validate that @props is an array, not a pointer, via ARRAY_SIZE. - * Validate that the array is terminated at compile-time (with -O2), - * which requires the array to be const. + * Validate that the array does not have a legacy terminator at compile-time; + * requires -O2 and the array to be const. */ #define device_class_set_props(dc, props) \ do { \ QEMU_BUILD_BUG_ON(sizeof(props) == 0); \ - size_t props_count_ = ARRAY_SIZE(props) - 1; \ - if ((props)[props_count_].name != NULL) { \ + size_t props_count_ = ARRAY_SIZE(props); \ + if ((props)[props_count_ - 1].name == NULL) { \ qemu_build_not_reached(); \ } \ device_class_set_props_n((dc), (props), props_count_); \ @@ -965,7 +965,7 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); /** * device_class_set_props_n(): add a set of properties to an device * @dc: the parent DeviceClass all devices inherit - * @props: an array of properties, not terminated by DEFINE_PROP_END_OF_LIST. + * @props: an array of properties * @n: ARRAY_SIZE(@props) * * This will add a set of properties to the object. It will fault if diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 26ebd23068..ef3fd7b4ae 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -171,9 +171,6 @@ extern const PropertyInfo qdev_prop_link; #define DEFINE_PROP_SIZE32(_n, _s, _f, _d) \ DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size32, uint32_t) -#define DEFINE_PROP_END_OF_LIST() \ - {} - /* * Set properties between creation and realization. * diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1afa07511e..0e882c474e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2652,7 +2652,6 @@ static const Property arm_cpu_properties[] = { DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */ DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false), - DEFINE_PROP_END_OF_LIST() }; static const gchar *arm_gdb_arch_name(CPUState *cs) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index a7529a1b3d..64dc15655b 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -151,7 +151,6 @@ static void avr_cpu_initfn(Object *obj) static const Property avr_cpu_properties[] = { DEFINE_PROP_UINT32("init-sp", AVRCPU, init_sp, 0), - DEFINE_PROP_END_OF_LIST() }; static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index a70007245e..8c89a8ce31 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -53,7 +53,6 @@ static const Property hexagon_cpu_properties[] = { DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, qdev_prop_uint32, target_ulong), DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true), - DEFINE_PROP_END_OF_LIST() }; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5253399459..c28adee34f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5387,7 +5387,6 @@ static X86CPUVersion x86_cpu_model_resolve_version(const X86CPUModel *model) static const Property max_x86_cpu_properties[] = { DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false), - DEFINE_PROP_END_OF_LIST() }; static void max_x86_cpu_realize(DeviceState *dev, Error **errp) @@ -8548,7 +8547,6 @@ static const Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, true), DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), - DEFINE_PROP_END_OF_LIST() }; #ifndef CONFIG_USER_ONLY diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 0e41e39c0e..83554f62d3 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -403,7 +403,6 @@ static const Property mb_properties[] = { /* * End of properties reserved by Xilinx DTS conversion tool. */ - DEFINE_PROP_END_OF_LIST(), }; static ObjectClass *mb_cpu_class_by_name(const char *cpu_model) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 02c0e1b0f9..aa3d905e70 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -541,7 +541,6 @@ static const struct SysemuCPUOps mips_sysemu_ops = { static const Property mips_cpu_properties[] = { DEFINE_PROP_BOOL("big-endian", MIPSCPU, is_big_endian, TARGET_BIG_ENDIAN), - DEFINE_PROP_END_OF_LIST(), }; #ifdef CONFIG_TCG diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7aa041f57a..a5aa3a8670 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2699,7 +2699,6 @@ static const Property riscv_cpu_properties[] = { * it with -x and default to 'false'. */ DEFINE_PROP_BOOL("x-misa-w", RISCVCPU, cfg.misa_w, false), - DEFINE_PROP_END_OF_LIST(), }; #if defined(TARGET_RISCV64) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 263f9e84ed..9b367ed2d5 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -318,7 +318,6 @@ static const Property s390x_cpu_properties[] = { DEFINE_PROP_BOOL("dedicated", S390CPU, env.dedicated, false), DEFINE_PROP_CPUS390ENTITLEMENT("entitlement", S390CPU, env.entitlement, S390_CPU_ENTITLEMENT_AUTO), - DEFINE_PROP_END_OF_LIST() }; #endif diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 8f494c286a..a65a6466a7 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -917,7 +917,6 @@ static const Property sparc_cpu_properties[] = { DEFINE_PROP_UINT32("mmu-version", SPARCCPU, env.def.mmu_version, 0), DEFINE_PROP("nwindows", SPARCCPU, env.def.nwindows, qdev_prop_nwindows, uint32_t), - DEFINE_PROP_END_OF_LIST() }; #ifndef CONFIG_USER_ONLY diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c index 1eb95d2429..de7a572e68 100644 --- a/tests/unit/test-qdev-global-props.c +++ b/tests/unit/test-qdev-global-props.c @@ -49,7 +49,6 @@ struct MyType { static const Property static_props[] = { DEFINE_PROP_UINT32("prop1", MyType, prop1, PROP_DEFAULT), DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), - DEFINE_PROP_END_OF_LIST() }; static void static_prop_class_init(ObjectClass *oc, void *data) From ef1ac71ff0f2272ab8af4419597ba204e498766f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:48 -0600 Subject: [PATCH 0482/2892] include/hw/qdev-properties: Shrink struct Property Before, via pahole: arm32, a 32-bit host which aligns uint64_t: struct Property { const char * name; /* 0 4 */ const PropertyInfo * info; /* 4 4 */ ptrdiff_t offset; /* 8 4 */ uint8_t bitnr; /* 12 1 */ /* XXX 3 bytes hole, try to pack */ uint64_t bitmask; /* 16 8 */ _Bool set_default; /* 24 1 */ /* XXX 7 bytes hole, try to pack */ union { int64_t i; /* 32 8 */ uint64_t u; /* 32 8 */ } defval; /* 32 8 */ int arrayoffset; /* 40 4 */ const PropertyInfo * arrayinfo; /* 44 4 */ int arrayfieldsize; /* 48 4 */ const char * link_type; /* 52 4 */ /* size: 56, cachelines: 1, members: 11 */ /* sum members: 46, holes: 2, sum holes: 10 */ }; arm64, an arbitrary 64-bit host: struct Property { const char * name; /* 0 8 */ const PropertyInfo * info; /* 8 8 */ ptrdiff_t offset; /* 16 8 */ uint8_t bitnr; /* 24 1 */ /* XXX 7 bytes hole, try to pack */ uint64_t bitmask; /* 32 8 */ _Bool set_default; /* 40 1 */ /* XXX 7 bytes hole, try to pack */ union { int64_t i; /* 48 8 */ uint64_t u; /* 48 8 */ } defval; /* 48 8 */ int arrayoffset; /* 56 4 */ /* XXX 4 bytes hole, try to pack */ const PropertyInfo * arrayinfo; /* 64 8 */ int arrayfieldsize; /* 72 4 */ /* XXX 4 bytes hole, try to pack */ const char * link_type; /* 80 8 */ /* size: 88, cachelines: 2, members: 11 */ /* sum members: 66, holes: 4, sum holes: 22 */ }; Afterward there are no holes in either structure. For arm32, size 48, padding 2, saved 8 bytes. For arm64, size 72, padding 6, saved 16 bytes. Saves 20k from qemu-system-aarch64 on a 64-bit host. Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-22-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- include/hw/qdev-properties.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index ef3fd7b4ae..3680cd239f 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -16,17 +16,17 @@ struct Property { const char *name; const PropertyInfo *info; ptrdiff_t offset; - uint8_t bitnr; + const char *link_type; uint64_t bitmask; - bool set_default; union { int64_t i; uint64_t u; } defval; - int arrayoffset; const PropertyInfo *arrayinfo; + int arrayoffset; int arrayfieldsize; - const char *link_type; + uint8_t bitnr; + bool set_default; }; struct PropertyInfo { From e9c0346a958a87c573229ceaacbc99a05337bad4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:49 -0600 Subject: [PATCH 0483/2892] hw/core/qdev-properties: Constify Property argument to object_field_prop_ptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This logically should have accompanied d36f165d952 which allowed const Property to be registered. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-23-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 2 +- include/hw/qdev-properties.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index a3d49e2020..61929b2865 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -51,7 +51,7 @@ void qdev_prop_allow_set_link_before_realize(const Object *obj, } } -void *object_field_prop_ptr(Object *obj, Property *prop) +void *object_field_prop_ptr(Object *obj, const Property *prop) { void *ptr = obj; ptr += prop->offset; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 3680cd239f..447767688b 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -201,7 +201,7 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); /* Takes ownership of @values */ void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values); -void *object_field_prop_ptr(Object *obj, Property *prop); +void *object_field_prop_ptr(Object *obj, const Property *prop); void qdev_prop_register_global(GlobalProperty *prop); const GlobalProperty *qdev_find_global_prop(Object *obj, From 857c4a8a33ea36d9d2856df33130b26a1aa3a53a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:50 -0600 Subject: [PATCH 0484/2892] hw/core/qdev-properties: Constify Property argument to PropertyInfo.print MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This logically should have accompanied d36f165d952 which allowed const Property to be registered. There is exactly one instance of this method: print_pci_devfn. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-24-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties-system.c | 2 +- include/hw/qdev-properties.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 22ea1ed358..1034e9b580 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -869,7 +869,7 @@ out: visit_end_alternate(v, (void **) &alt); } -static int print_pci_devfn(Object *obj, Property *prop, char *dest, +static int print_pci_devfn(Object *obj, const Property *prop, char *dest, size_t len) { int32_t *ptr = object_field_prop_ptr(obj, prop); diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 447767688b..bf27375a3c 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -34,7 +34,7 @@ struct PropertyInfo { const char *description; const QEnumLookup *enum_table; bool realized_set_allowed; /* allow setting property on realized device */ - int (*print)(Object *obj, Property *prop, char *dest, size_t len); + int (*print)(Object *obj, const Property *prop, char *dest, size_t len); void (*set_default_value)(ObjectProperty *op, const Property *prop); ObjectProperty *(*create)(ObjectClass *oc, const char *name, const Property *prop); From b1987a2547c8e32fd3c32f504fe8d4bc58b7f961 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:51 -0600 Subject: [PATCH 0485/2892] Constify all opaque Property pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Via sed "s/ Property [*]/ const Property */". The opaque pointers passed to ObjectProperty callbacks are the last instances of non-const Property pointers in the tree. For the most part, these callbacks only use object_field_prop_ptr, which now takes a const pointer itself. This logically should have accompanied d36f165d952 which allowed const Property to be registered. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-25-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- backends/tpm/tpm_util.c | 4 +- hw/block/xen-block.c | 4 +- hw/core/qdev-properties-system.c | 48 ++++++++++---------- hw/core/qdev-properties.c | 78 ++++++++++++++++---------------- hw/misc/xlnx-versal-trng.c | 2 +- hw/nvme/nguid.c | 4 +- hw/s390x/css.c | 4 +- hw/s390x/s390-pci-bus.c | 4 +- hw/vfio/pci-quirks.c | 4 +- 9 files changed, 76 insertions(+), 76 deletions(-) diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index cf138551df..485982b17b 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -46,7 +46,7 @@ static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque, static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; TPMBackend *s, **be = object_field_prop_ptr(obj, prop); char *str; @@ -66,7 +66,7 @@ static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, static void release_tpm(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; TPMBackend **be = object_field_prop_ptr(obj, prop); if (*be) { diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 56a6713660..ec3413f116 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -485,7 +485,7 @@ static char *disk_to_vbd_name(unsigned int disk) static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; XenBlockVdev *vdev = object_field_prop_ptr(obj, prop); char *str; @@ -545,7 +545,7 @@ static int vbd_name_to_disk(const char *name, const char **endp, static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; XenBlockVdev *vdev = object_field_prop_ptr(obj, prop); char *str, *p; const char *end; diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 1034e9b580..1bae135276 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -90,7 +90,7 @@ bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str, static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; void **ptr = object_field_prop_ptr(obj, prop); const char *value; char *p; @@ -116,7 +116,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, void *opaque, bool iothread, Error **errp) { DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; void **ptr = object_field_prop_ptr(obj, prop); char *str; BlockBackend *blk; @@ -225,7 +225,7 @@ static void set_drive_iothread(Object *obj, Visitor *v, const char *name, static void release_drive(Object *obj, const char *name, void *opaque) { DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; BlockBackend **ptr = object_field_prop_ptr(obj, prop); if (*ptr) { @@ -269,7 +269,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { ERRP_GUARD(); - Property *prop = opaque; + const Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); Chardev *s; char *str; @@ -305,7 +305,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, static void release_chr(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); qemu_chr_fe_deinit(be, false); @@ -329,7 +329,7 @@ const PropertyInfo qdev_prop_chr = { static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; MACAddr *mac = object_field_prop_ptr(obj, prop); char buffer[2 * 6 + 5 + 1]; char *p = buffer; @@ -344,7 +344,7 @@ static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; MACAddr *mac = object_field_prop_ptr(obj, prop); int i, pos; char *str; @@ -406,7 +406,7 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, static void get_netdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); @@ -417,7 +417,7 @@ static void get_netdev(Object *obj, Visitor *v, const char *name, static void set_netdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); NetClientState **ncs = peers_ptr->ncs; NetClientState *peers[MAX_QUEUE_NUM]; @@ -485,7 +485,7 @@ const PropertyInfo qdev_prop_netdev = { static void get_audiodev(Object *obj, Visitor *v, const char* name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QEMUSoundCard *card = object_field_prop_ptr(obj, prop); char *p = g_strdup(audio_get_id(card)); @@ -496,7 +496,7 @@ static void get_audiodev(Object *obj, Visitor *v, const char* name, static void set_audiodev(Object *obj, Visitor *v, const char* name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QEMUSoundCard *card = object_field_prop_ptr(obj, prop); AudioState *state; g_autofree char *str = NULL; @@ -578,7 +578,7 @@ static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); int value; @@ -614,7 +614,7 @@ const PropertyInfo qdev_prop_losttickpolicy = { static void set_blocksize(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value; @@ -737,7 +737,7 @@ const PropertyInfo qdev_prop_zero_page_detection = { static void get_reserved_region(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); char buffer[64]; char *p = buffer; @@ -753,7 +753,7 @@ static void get_reserved_region(Object *obj, Visitor *v, const char *name, static void set_reserved_region(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); const char *endptr; uint64_t lob, upb; @@ -815,7 +815,7 @@ const PropertyInfo qdev_prop_reserved_region = { static void set_pci_devfn(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; g_autofree GenericAlternate *alt; int32_t value, *ptr = object_field_prop_ptr(obj, prop); unsigned int slot, fn, n; @@ -895,7 +895,7 @@ const PropertyInfo qdev_prop_pci_devfn = { static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); char buffer[] = "ffff:ff:ff.f"; char *p = buffer; @@ -921,7 +921,7 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); char *str, *p; char *e; @@ -1011,7 +1011,7 @@ const PropertyInfo qdev_prop_off_auto_pcibar = { static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); int speed; @@ -1045,7 +1045,7 @@ static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); int speed; @@ -1093,7 +1093,7 @@ const PropertyInfo qdev_prop_pcie_link_speed = { static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); int width; @@ -1130,7 +1130,7 @@ static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); int width; @@ -1181,7 +1181,7 @@ const PropertyInfo qdev_prop_pcie_link_width = { static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); char buffer[UUID_STR_LEN]; char *p = buffer; @@ -1196,7 +1196,7 @@ static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, static void set_uuid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); char *str; diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 61929b2865..434a76f503 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -61,7 +61,7 @@ void *object_field_prop_ptr(Object *obj, const Property *prop) static void field_prop_get(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; return prop->info->get(obj, v, name, opaque, errp); } @@ -78,7 +78,7 @@ static ObjectPropertyAccessor *field_prop_getter(const PropertyInfo *info) static void field_prop_set(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; if (!qdev_prop_allow_set(obj, name, prop->info, errp)) { return; @@ -100,7 +100,7 @@ static ObjectPropertyAccessor *field_prop_setter(const PropertyInfo *info) void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); visit_type_enum(v, name, ptr, prop->info->enum_table, errp); @@ -109,7 +109,7 @@ void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, void qdev_propinfo_set_enum(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); visit_type_enum(v, name, ptr, prop->info->enum_table, errp); @@ -131,13 +131,13 @@ const PropertyInfo qdev_prop_enum = { /* Bit */ -static uint32_t qdev_get_prop_mask(Property *prop) +static uint32_t qdev_get_prop_mask(const Property *prop) { assert(prop->info == &qdev_prop_bit); return 0x1 << prop->bitnr; } -static void bit_prop_set(Object *obj, Property *props, bool val) +static void bit_prop_set(Object *obj, const Property *props, bool val) { uint32_t *p = object_field_prop_ptr(obj, props); uint32_t mask = qdev_get_prop_mask(props); @@ -151,7 +151,7 @@ static void bit_prop_set(Object *obj, Property *props, bool val) static void prop_get_bit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *p = object_field_prop_ptr(obj, prop); bool value = (*p & qdev_get_prop_mask(prop)) != 0; @@ -161,7 +161,7 @@ static void prop_get_bit(Object *obj, Visitor *v, const char *name, static void prop_set_bit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool value; if (!visit_type_bool(v, name, &value, errp)) { @@ -185,13 +185,13 @@ const PropertyInfo qdev_prop_bit = { /* Bit64 */ -static uint64_t qdev_get_prop_mask64(Property *prop) +static uint64_t qdev_get_prop_mask64(const Property *prop) { assert(prop->info == &qdev_prop_bit64); return 0x1ull << prop->bitnr; } -static void bit64_prop_set(Object *obj, Property *props, bool val) +static void bit64_prop_set(Object *obj, const Property *props, bool val) { uint64_t *p = object_field_prop_ptr(obj, props); uint64_t mask = qdev_get_prop_mask64(props); @@ -205,7 +205,7 @@ static void bit64_prop_set(Object *obj, Property *props, bool val) static void prop_get_bit64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *p = object_field_prop_ptr(obj, prop); bool value = (*p & qdev_get_prop_mask64(prop)) != 0; @@ -215,7 +215,7 @@ static void prop_get_bit64(Object *obj, Visitor *v, const char *name, static void prop_set_bit64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool value; if (!visit_type_bool(v, name, &value, errp)) { @@ -237,7 +237,7 @@ const PropertyInfo qdev_prop_bit64 = { static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool *ptr = object_field_prop_ptr(obj, prop); visit_type_bool(v, name, ptr, errp); @@ -246,7 +246,7 @@ static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool *ptr = object_field_prop_ptr(obj, prop); visit_type_bool(v, name, ptr, errp); @@ -264,7 +264,7 @@ const PropertyInfo qdev_prop_bool = { static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -273,7 +273,7 @@ static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -303,7 +303,7 @@ const PropertyInfo qdev_prop_uint8 = { static void get_uint16(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint16_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint16(v, name, ptr, errp); @@ -312,7 +312,7 @@ static void get_uint16(Object *obj, Visitor *v, const char *name, static void set_uint16(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint16_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint16(v, name, ptr, errp); @@ -330,7 +330,7 @@ const PropertyInfo qdev_prop_uint16 = { static void get_uint32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -339,7 +339,7 @@ static void get_uint32(Object *obj, Visitor *v, const char *name, static void set_uint32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -348,7 +348,7 @@ static void set_uint32(Object *obj, Visitor *v, const char *name, void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int32(v, name, ptr, errp); @@ -357,7 +357,7 @@ void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int32(v, name, ptr, errp); @@ -382,7 +382,7 @@ const PropertyInfo qdev_prop_int32 = { static void get_uint64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -391,7 +391,7 @@ static void get_uint64(Object *obj, Visitor *v, const char *name, static void set_uint64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -400,7 +400,7 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, static void get_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int64(v, name, ptr, errp); @@ -409,7 +409,7 @@ static void get_int64(Object *obj, Visitor *v, const char *name, static void set_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int64(v, name, ptr, errp); @@ -432,7 +432,7 @@ const PropertyInfo qdev_prop_int64 = { static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -452,14 +452,14 @@ const PropertyInfo qdev_prop_uint64_checkmask = { static void release_string(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; g_free(*(char **)object_field_prop_ptr(obj, prop)); } static void get_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char **ptr = object_field_prop_ptr(obj, prop); if (!*ptr) { @@ -473,7 +473,7 @@ static void get_string(Object *obj, Visitor *v, const char *name, static void set_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char **ptr = object_field_prop_ptr(obj, prop); char *str; @@ -507,7 +507,7 @@ const PropertyInfo qdev_prop_on_off_auto = { void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value = *ptr; @@ -517,7 +517,7 @@ void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value; @@ -557,7 +557,7 @@ struct ArrayElementList { * specific element of the array. Arrays are backed by an uint32_t length field * and an element array. @elem points at an element in this element array. */ -static Property array_elem_prop(Object *obj, Property *parent_prop, +static Property array_elem_prop(Object *obj, const Property *parent_prop, const char *name, char *elem) { return (Property) { @@ -582,7 +582,7 @@ static Property array_elem_prop(Object *obj, Property *parent_prop, */ static void release_prop_array(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; char *elem = *arrayptr; @@ -609,7 +609,7 @@ static void set_prop_array(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { ERRP_GUARD(); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; ArrayElementList *list, *elem, *next; @@ -685,7 +685,7 @@ static void get_prop_array(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { ERRP_GUARD(); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; char *elemptr = *arrayptr; @@ -928,7 +928,7 @@ void qdev_prop_set_globals(DeviceState *dev) static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_size(v, name, ptr, errp); @@ -937,7 +937,7 @@ static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_size(v, name, ptr, errp); @@ -1020,7 +1020,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char buffer[1024]; char *ptr = buffer; diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 0419f648b7..dbd9b58a4e 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -641,7 +641,7 @@ static void trng_prop_fault_event_set(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *events = object_field_prop_ptr(obj, prop); if (!visit_type_uint32(v, name, events, errp)) { diff --git a/hw/nvme/nguid.c b/hw/nvme/nguid.c index 829832bd9f..be63cb75e1 100644 --- a/hw/nvme/nguid.c +++ b/hw/nvme/nguid.c @@ -149,7 +149,7 @@ static void nvme_nguid_stringify(const NvmeNGUID *nguid, char *out) static void get_nguid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NvmeNGUID *nguid = object_field_prop_ptr(obj, prop); char buffer[NGUID_STR_LEN]; char *p = buffer; @@ -162,7 +162,7 @@ static void get_nguid(Object *obj, Visitor *v, const char *name, void *opaque, static void set_nguid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NvmeNGUID *nguid = object_field_prop_ptr(obj, prop); char *str; diff --git a/hw/s390x/css.c b/hw/s390x/css.c index b2d5327dbf..4e27b2961b 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -2463,7 +2463,7 @@ void css_reset(void) static void get_css_devid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; CssDevId *dev_id = object_field_prop_ptr(obj, prop); char buffer[] = "xx.x.xxxx"; char *p = buffer; @@ -2492,7 +2492,7 @@ static void get_css_devid(Object *obj, Visitor *v, const char *name, static void set_css_devid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; CssDevId *dev_id = object_field_prop_ptr(obj, prop); char *str; int num, n1, n2; diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 5fbbf41a3d..94b1a5f639 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1453,7 +1453,7 @@ static void s390_pci_device_reset(DeviceState *dev) static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -1463,7 +1463,7 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); if (!visit_type_uint32(v, name, ptr, errp)) { diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index d37f722cce..c8e60475d5 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1499,7 +1499,7 @@ static void get_nv_gpudirect_clique_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -1509,7 +1509,7 @@ static void set_nv_gpudirect_clique_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t value, *ptr = object_field_prop_ptr(obj, prop); if (!visit_type_uint8(v, name, &value, errp)) { From 6dd818fbbbe3efc63889e7d811ac6b70e788c629 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Nov 2024 15:19:23 +0100 Subject: [PATCH 0486/2892] rust: qom: put class_init together from multiple ClassInitImpl<> Parameterize the implementation of ClassInitImpl so that it is possible to call up the chain of implementations, one superclass at a time starting at ClassInitImpl. In order to avoid having to implement (for example) ClassInitImpl, also remove the dummy PL011Class and PL011LuminaryClass structs and specify the same ObjectType::Class as the superclass. In the future this default behavior can be handled by a procedural macro, by looking at the first field in the struct. Note that the new trait is safe: the calls are started by rust_class_init<>(), which is not public and can convert the class pointer to a Rust reference. Since CLASS_BASE_INIT applies to the type that is being defined, and only to it, move it to ObjectImpl. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 19 +---- rust/qemu-api/src/definitions.rs | 111 ++++++++++++++++++++++++------ rust/qemu-api/src/device_class.rs | 50 +++++--------- rust/qemu-api/src/sysbus.rs | 18 ++++- rust/qemu-api/tests/tests.rs | 9 +-- 5 files changed, 127 insertions(+), 80 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 3e29442a62..d9e9f35f45 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,7 +13,6 @@ use qemu_api::{ c_str, definitions::ObjectImpl, device_class::DeviceImpl, - impl_device_class, irq::InterruptSource, prelude::*, }; @@ -108,7 +107,7 @@ pub struct PL011State { } unsafe impl ObjectType for PL011State { - type Class = PL011Class; + type Class = ::Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } @@ -118,11 +117,6 @@ impl ObjectImpl for PL011State { const INSTANCE_INIT: Option = Some(Self::init); } -#[repr(C)] -pub struct PL011Class { - _inner: [u8; 0], -} - impl DeviceImpl for PL011State { fn properties() -> &'static [Property] { &device_class::PL011_PROPERTIES @@ -134,8 +128,6 @@ impl DeviceImpl for PL011State { const RESET: Option = Some(Self::reset); } -impl_device_class!(PL011State); - impl PL011State { /// Initializes a pre-allocated, unitialized instance of `PL011State`. /// @@ -627,11 +619,6 @@ pub struct PL011Luminary { parent_obj: PL011State, } -#[repr(C)] -pub struct PL011LuminaryClass { - _inner: [u8; 0], -} - impl PL011Luminary { /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. /// @@ -646,7 +633,7 @@ impl PL011Luminary { } unsafe impl ObjectType for PL011Luminary { - type Class = PL011LuminaryClass; + type Class = ::Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; } @@ -657,5 +644,3 @@ impl ObjectImpl for PL011Luminary { } impl DeviceImpl for PL011Luminary {} - -impl_device_class!(PL011Luminary); diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index df91a2e31a..13f8f6fd2a 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -26,6 +26,16 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::() }) } +unsafe extern "C" fn rust_class_init>( + klass: *mut ObjectClass, + _data: *mut c_void, +) { + // SAFETY: klass is a T::Class, since rust_class_init + // is called from QOM core as the class_init function + // for class T + T::class_init(unsafe { &mut *klass.cast::() }) +} + /// Trait exposed by all structs corresponding to QOM objects. /// /// # Safety @@ -50,7 +60,8 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { /// - likewise, the first field of the `Class` must be of the class struct /// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. pub unsafe trait ObjectType: Sized { - /// The QOM class object corresponding to this struct. Not used yet. + /// The QOM class object corresponding to this struct. This is used + /// to automatically generate a `class_init` method. type Class; /// The name of the type, which can be passed to `object_new()` to @@ -59,7 +70,7 @@ pub unsafe trait ObjectType: Sized { } /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + ClassInitImpl { +pub trait ObjectImpl: ObjectType + ClassInitImpl { /// The parent of the type. This should match the first field of /// the struct that implements `ObjectImpl`: type ParentType: ObjectType; @@ -80,6 +91,15 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// `INSTANCE_INIT` functions have been called. const INSTANCE_POST_INIT: Option = None; + /// Called on descendent classes after all parent class initialization + /// has occurred, but before the class itself is initialized. This + /// is only useful if a class is not a leaf, and can be used to undo + /// the effects of copying the contents of the parent's class struct + /// to the descendants. + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + > = None; + const TYPE_INFO: TypeInfo = TypeInfo { name: Self::TYPE_NAME.as_ptr(), parent: Self::ParentType::TYPE_NAME.as_ptr(), @@ -96,37 +116,86 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { instance_finalize: Self::INSTANCE_FINALIZE, abstract_: Self::ABSTRACT, class_size: core::mem::size_of::(), - class_init: ::CLASS_INIT, - class_base_init: ::CLASS_BASE_INIT, + class_init: Some(rust_class_init::), + class_base_init: Self::CLASS_BASE_INIT, class_data: core::ptr::null_mut(), interfaces: core::ptr::null_mut(), }; } -/// Trait used to fill in a class struct. +/// Internal trait used to automatically fill in a class struct. /// /// Each QOM class that has virtual methods describes them in a /// _class struct_. Class structs include a parent field corresponding /// to the vtable of the parent class, all the way up to [`ObjectClass`]. -/// Each QOM type has one such class struct. +/// Each QOM type has one such class struct; this trait takes care of +/// initializing the `T` part of the class struct, for the type that +/// implements the trait. /// -/// The Rust implementation of methods will usually come from a trait -/// like [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl). -pub trait ClassInitImpl { - /// Function that is called after all parent class initialization - /// has occurred. On entry, the virtual method pointers are set to +/// Each struct will implement this trait with `T` equal to each +/// superclass. For example, a device should implement at least +/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>`. +/// Such implementations are made in one of two ways. +/// +/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` +/// crate itself. The Rust implementation of methods will come from a +/// trait like [`ObjectImpl`] or +/// [`DeviceImpl`](crate::device_class::DeviceImpl), and `ClassInitImpl` is +/// provided by blanket implementations that operate on all implementors of the +/// `*Impl`* trait. For example: +/// +/// ```ignore +/// impl ClassInitImpl for T +/// where +/// T: DeviceImpl, +/// ``` +/// +/// The other case is when manual implementation of the trait is needed. +/// This covers the following cases: +/// +/// * if a class implements a QOM interface, the Rust code _has_ to define its +/// own class struct `FooClass` and implement `ClassInitImpl`. +/// `ClassInitImpl`'s `class_init` method will then forward to +/// multiple other `class_init`s, for the interfaces as well as the +/// superclass. (Note that there is no Rust example yet for using interfaces). +/// +/// * for classes implemented outside the ``qemu-api`` crate, it's not possible +/// to add blanket implementations like the above one, due to orphan rules. In +/// that case, the easiest solution is to implement +/// `ClassInitImpl` for each subclass and not have a +/// `YourSuperclassImpl` trait at all. +/// +/// ```ignore +/// impl ClassInitImpl for YourSubclass { +/// fn class_init(klass: &mut YourSuperclass) { +/// klass.some_method = Some(Self::some_method); +/// >::class_init(&mut klass.parent_class); +/// } +/// } +/// ``` +/// +/// While this method incurs a small amount of code duplication, +/// it is generally limited to the recursive call on the last line. +/// This is because classes defined in Rust do not need the same +/// glue code that is needed when the classes are defined in C code. +/// You may consider using a macro if you have many subclasses. +pub trait ClassInitImpl { + /// Initialize `klass` to point to the virtual method implementations + /// for `Self`. On entry, the virtual method pointers are set to /// the default values coming from the parent classes; the function /// can change them to override virtual methods of a parent class. - const CLASS_INIT: Option; - - /// Called on descendent classes after all parent class initialization - /// has occurred, but before the class itself is initialized. This - /// is only useful if a class is not a leaf, and can be used to undo - /// the effects of copying the contents of the parent's class struct - /// to the descendants. - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), - >; + /// + /// The virtual method implementations usually come from another + /// trait, for example [`DeviceImpl`](crate::device_class::DeviceImpl) + /// when `T` is [`DeviceClass`](crate::bindings::DeviceClass). + /// + /// On entry, `klass`'s parent class is initialized, while the other fields + /// are all zero; it is therefore assumed that all fields in `T` can be + /// zeroed, otherwise it would not be possible to provide the class as a + /// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable) + /// to T; this is more easily done once Zeroable does not require a manual + /// implementation (Rust 1.75.0). + fn class_init(klass: &mut T); } #[macro_export] diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index c98f0b2c7d..dcec548829 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -2,10 +2,11 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, os::raw::c_void}; +use std::ffi::CStr; use crate::{ - bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, + bindings::{self, DeviceClass, DeviceState, Error, Property, VMStateDescription}, + definitions::ClassInitImpl, prelude::*, }; @@ -44,7 +45,7 @@ pub trait DeviceImpl { /// # Safety /// /// This function is only called through the QOM machinery and -/// the `impl_device_class!` macro. +/// used by the `ClassInitImpl` trait. /// We expect the FFI user of this function to pass a valid pointer that /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. @@ -65,48 +66,31 @@ unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { T::RESET.unwrap()(unsafe { &mut *state }); } -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `DeviceClass`, because `T` implements -/// `DeviceImpl`. -pub unsafe extern "C" fn rust_device_class_init( - klass: *mut ObjectClass, - _: *mut c_void, -) { - let mut dc = ::core::ptr::NonNull::new(klass.cast::()).unwrap(); - unsafe { - let dc = dc.as_mut(); +impl ClassInitImpl for T +where + T: DeviceImpl, +{ + fn class_init(dc: &mut DeviceClass) { if ::REALIZE.is_some() { dc.realize = Some(rust_realize_fn::); } if ::RESET.is_some() { - bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); + unsafe { + bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); + } } if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; } let prop = ::properties(); if !prop.is_empty() { - bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + unsafe { + bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + } } } } -#[macro_export] -macro_rules! impl_device_class { - ($type:ty) => { - impl $crate::definitions::ClassInitImpl for $type { - const CLASS_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void), - > = Some($crate::device_class::rust_device_class_init::<$type>); - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void), - > = None; - } - }; -} - #[macro_export] macro_rules! define_property { ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { @@ -148,8 +132,8 @@ macro_rules! declare_properties { }; } -unsafe impl ObjectType for bindings::DeviceState { - type Class = bindings::DeviceClass; +unsafe impl ObjectType for DeviceState { + type Class = DeviceClass; const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 5ee068541c..5d15b31740 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -6,7 +6,13 @@ use std::{ffi::CStr, ptr::addr_of}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; -use crate::{bindings, cell::bql_locked, irq::InterruptSource, prelude::*}; +use crate::{ + bindings::{self, DeviceClass}, + cell::bql_locked, + definitions::ClassInitImpl, + irq::InterruptSource, + prelude::*, +}; unsafe impl ObjectType for SysBusDevice { type Class = SysBusDeviceClass; @@ -14,6 +20,16 @@ unsafe impl ObjectType for SysBusDevice { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; } +// TODO: add SysBusDeviceImpl +impl ClassInitImpl for T +where + T: ClassInitImpl, +{ + fn class_init(sdc: &mut SysBusDeviceClass) { + >::class_init(&mut sdc.parent_class); + } +} + impl SysBusDevice { /// Return `self` cast to a mutable pointer, for use in calls to C code. const fn as_mut_ptr(&self) -> *mut SysBusDevice { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 278efe967f..ed3a555e76 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -26,11 +26,6 @@ fn test_device_decl_macros() { pub migrate_clock: bool, } - #[repr(C)] - pub struct DummyClass { - pub _parent: DeviceClass, - } - declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -43,7 +38,7 @@ fn test_device_decl_macros() { } unsafe impl ObjectType for DummyState { - type Class = DummyClass; + type Class = ::Class; const TYPE_NAME: &'static CStr = c_str!("dummy"); } @@ -61,8 +56,6 @@ fn test_device_decl_macros() { } } - impl_device_class!(DummyState); - unsafe { module_call_init(module_init_type::MODULE_INIT_QOM); object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); From cb36da9bd84076470f36da56542e85a2436e3d95 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Oct 2024 15:00:26 +0100 Subject: [PATCH 0487/2892] rust: qom: add possibility of overriding unparent Add a blanket definition of ClassInitImpl that thunks ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not None. ClassInitImpl can now call its superclass's ClassInitImpl, so that the C and Rust hierarchies match more closely. This is mostly done as an example of implementing the metaclass hierarchy under ClassInitImpl. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/definitions.rs | 44 ++++++++++++++++++++++++++++--- rust/qemu-api/src/device_class.rs | 6 +++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 13f8f6fd2a..a2481c1fe7 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -6,7 +6,7 @@ use std::{ffi::CStr, os::raw::c_void}; -use crate::bindings::{Object, ObjectClass, TypeInfo}; +use crate::bindings::{self, Object, ObjectClass, TypeInfo}; unsafe extern "C" fn rust_instance_init(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init @@ -121,6 +121,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { class_data: core::ptr::null_mut(), interfaces: core::ptr::null_mut(), }; + + // methods on ObjectClass + const UNPARENT: Option = None; } /// Internal trait used to automatically fill in a class struct. @@ -134,7 +137,8 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// /// Each struct will implement this trait with `T` equal to each /// superclass. For example, a device should implement at least -/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>`. +/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>` and +/// `ClassInitImpl<`[`ObjectClass`](crate::bindings::ObjectClass)`>`. /// Such implementations are made in one of two ways. /// /// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` @@ -147,9 +151,13 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// ```ignore /// impl ClassInitImpl for T /// where -/// T: DeviceImpl, +/// T: ClassInitImpl + DeviceImpl, /// ``` /// +/// The bound on `ClassInitImpl` is needed so that, +/// after initializing the `DeviceClass` part of the class struct, +/// the parent [`ObjectClass`] is initialized as well. +/// /// The other case is when manual implementation of the trait is needed. /// This covers the following cases: /// @@ -235,3 +243,33 @@ macro_rules! module_init { } }; } + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_unparent_fn(dev: *mut Object) { + unsafe { + assert!(!dev.is_null()); + let state = core::ptr::NonNull::new_unchecked(dev.cast::()); + T::UNPARENT.unwrap()(state.as_ref()); + } +} + +impl ClassInitImpl for T +where + T: ObjectImpl, +{ + fn class_init(oc: &mut ObjectClass) { + if ::UNPARENT.is_some() { + oc.unparent = Some(rust_unparent_fn::); + } + } +} + +unsafe impl ObjectType for Object { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; +} diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index dcec548829..a9965d1f12 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -5,7 +5,7 @@ use std::ffi::CStr; use crate::{ - bindings::{self, DeviceClass, DeviceState, Error, Property, VMStateDescription}, + bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, definitions::ClassInitImpl, prelude::*, }; @@ -68,7 +68,7 @@ unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { impl ClassInitImpl for T where - T: DeviceImpl, + T: ClassInitImpl + DeviceImpl, { fn class_init(dc: &mut DeviceClass) { if ::REALIZE.is_some() { @@ -88,6 +88,8 @@ where bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); } } + + >::class_init(&mut dc.parent_class); } } From 4aed0296b307b6e2e3b7d38ee6c5204cf2dfe1ca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Oct 2024 14:15:27 +0100 Subject: [PATCH 0488/2892] rust: rename qemu-api modules to follow C code a bit more A full match would mean calling them qom::object and hw::core::qdev. For now, keep the names shorter but still a bit easier to find. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 +- rust/qemu-api-macros/src/lib.rs | 2 +- rust/qemu-api/meson.build | 5 +- rust/qemu-api/src/lib.rs | 5 +- rust/qemu-api/src/module.rs | 43 +++++++++++ rust/qemu-api/src/prelude.rs | 2 +- .../qemu-api/src/{device_class.rs => qdev.rs} | 4 +- rust/qemu-api/src/{definitions.rs => qom.rs} | 76 ++++++++----------- rust/qemu-api/src/sysbus.rs | 2 +- rust/qemu-api/tests/tests.rs | 4 +- 10 files changed, 91 insertions(+), 56 deletions(-) create mode 100644 rust/qemu-api/src/module.rs rename rust/qemu-api/src/{device_class.rs => qdev.rs} (97%) rename rust/qemu-api/src/{definitions.rs => qom.rs} (84%) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index d9e9f35f45..3fed8b4ad2 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -11,10 +11,10 @@ use std::{ use qemu_api::{ bindings::{self, *}, c_str, - definitions::ObjectImpl, - device_class::DeviceImpl, irq::InterruptSource, prelude::*, + qdev::DeviceImpl, + qom::ObjectImpl, }; use crate::{ diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index cf99ac04b8..74a8bc7503 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -40,7 +40,7 @@ pub fn derive_object(input: TokenStream) -> TokenStream { let expanded = quote! { ::qemu_api::module_init! { MODULE_INIT_QOM => unsafe { - ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO); + ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); } } }; diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index adcee66115..7ff408ad68 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -19,11 +19,12 @@ _qemu_api_rs = static_library( 'src/bitops.rs', 'src/cell.rs', 'src/c_str.rs', - 'src/definitions.rs', - 'src/device_class.rs', 'src/irq.rs', + 'src/module.rs', 'src/offset_of.rs', 'src/prelude.rs', + 'src/qdev.rs', + 'src/qom.rs', 'src/sysbus.rs', 'src/vmstate.rs', 'src/zeroable.rs', diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 9e007e1635..124bece044 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -15,10 +15,11 @@ pub mod prelude; pub mod bitops; pub mod c_str; pub mod cell; -pub mod definitions; -pub mod device_class; pub mod irq; +pub mod module; pub mod offset_of; +pub mod qdev; +pub mod qom; pub mod sysbus; pub mod vmstate; pub mod zeroable; diff --git a/rust/qemu-api/src/module.rs b/rust/qemu-api/src/module.rs new file mode 100644 index 0000000000..fa5cea3598 --- /dev/null +++ b/rust/qemu-api/src/module.rs @@ -0,0 +1,43 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Macro to register blocks of code that run as QEMU starts up. + +#[macro_export] +macro_rules! module_init { + ($type:ident => $body:block) => { + const _: () = { + #[used] + #[cfg_attr( + not(any(target_vendor = "apple", target_os = "windows")), + link_section = ".init_array" + )] + #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")] + #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] + pub static LOAD_MODULE: extern "C" fn() = { + extern "C" fn init_fn() { + $body + } + + extern "C" fn ctor_fn() { + unsafe { + $crate::bindings::register_module_init( + Some(init_fn), + $crate::bindings::module_init_type::$type, + ); + } + } + + ctor_fn + }; + }; + }; + + // shortcut because it's quite common that $body needs unsafe {} + ($type:ident => unsafe $body:block) => { + $crate::module_init! { + $type => { unsafe { $body } } + } + }; +} diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 1b8677b2d9..5cc41f081f 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,4 +7,4 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; -pub use crate::definitions::ObjectType; +pub use crate::qom::ObjectType; diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/qdev.rs similarity index 97% rename from rust/qemu-api/src/device_class.rs rename to rust/qemu-api/src/qdev.rs index a9965d1f12..ad4c12d097 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/qdev.rs @@ -2,12 +2,14 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later +//! Bindings to create devices and access device functionality from Rust. + use std::ffi::CStr; use crate::{ bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, - definitions::ClassInitImpl, prelude::*, + qom::ClassInitImpl, }; /// Trait providing the contents of [`DeviceClass`]. diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/qom.rs similarity index 84% rename from rust/qemu-api/src/definitions.rs rename to rust/qemu-api/src/qom.rs index a2481c1fe7..2222d1a5ab 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/qom.rs @@ -2,7 +2,34 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -//! Definitions required by QEMU when registering a device. +//! Bindings to access QOM functionality from Rust. +//! +//! This module provides automatic creation and registration of `TypeInfo` +//! for classes that are written in Rust, and mapping between Rust traits +//! and QOM vtables. +//! +//! # Structure of a class +//! +//! A leaf class only needs a struct holding instance state. The struct must +//! implement the [`ObjectType`] trait, as well as any `*Impl` traits that exist +//! for its superclasses. +//! +//! If a class has subclasses, it will also provide a struct for instance data, +//! with the same characteristics as for concrete classes, but it also needs +//! additional components to support virtual methods: +//! +//! * a struct for class data, for example `DeviceClass`. This corresponds to +//! the C "class struct" and holds the vtable that is used by instances of the +//! class and its subclasses. It must start with its parent's class struct. +//! +//! * a trait for virtual method implementations, for example `DeviceImpl`. +//! Child classes implement this trait to provide their own behavior for +//! virtual methods. The trait's methods take `&self` to access instance data. +//! +//! * an implementation of [`ClassInitImpl`], for example +//! `ClassInitImpl`. This fills the vtable in the class struct; +//! the source for this is the `*Impl` trait; the associated consts and +//! functions if needed are wrapped to map C types into Rust types. use std::{ffi::CStr, os::raw::c_void}; @@ -143,10 +170,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// /// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` /// crate itself. The Rust implementation of methods will come from a -/// trait like [`ObjectImpl`] or -/// [`DeviceImpl`](crate::device_class::DeviceImpl), and `ClassInitImpl` is -/// provided by blanket implementations that operate on all implementors of the -/// `*Impl`* trait. For example: +/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl), +/// and `ClassInitImpl` is provided by blanket implementations that +/// operate on all implementors of the `*Impl`* trait. For example: /// /// ```ignore /// impl ClassInitImpl for T @@ -194,7 +220,7 @@ pub trait ClassInitImpl { /// can change them to override virtual methods of a parent class. /// /// The virtual method implementations usually come from another - /// trait, for example [`DeviceImpl`](crate::device_class::DeviceImpl) + /// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl) /// when `T` is [`DeviceClass`](crate::bindings::DeviceClass). /// /// On entry, `klass`'s parent class is initialized, while the other fields @@ -206,44 +232,6 @@ pub trait ClassInitImpl { fn class_init(klass: &mut T); } -#[macro_export] -macro_rules! module_init { - ($type:ident => $body:block) => { - const _: () = { - #[used] - #[cfg_attr( - not(any(target_vendor = "apple", target_os = "windows")), - link_section = ".init_array" - )] - #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static LOAD_MODULE: extern "C" fn() = { - extern "C" fn init_fn() { - $body - } - - extern "C" fn ctor_fn() { - unsafe { - $crate::bindings::register_module_init( - Some(init_fn), - $crate::bindings::module_init_type::$type, - ); - } - } - - ctor_fn - }; - }; - }; - - // shortcut because it's quite common that $body needs unsafe {} - ($type:ident => unsafe $body:block) => { - $crate::module_init! { - $type => { unsafe { $body } } - } - }; -} - /// # Safety /// /// We expect the FFI user of this function to pass a valid pointer that diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 5d15b31740..fa69cadd7c 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -9,9 +9,9 @@ pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ bindings::{self, DeviceClass}, cell::bql_locked, - definitions::ClassInitImpl, irq::InterruptSource, prelude::*, + qom::ClassInitImpl, }; unsafe impl ObjectType for SysBusDevice { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index ed3a555e76..78f7da474b 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -5,8 +5,8 @@ use std::ffi::CStr; use qemu_api::{ - bindings::*, c_str, declare_properties, define_property, definitions::ObjectImpl, - device_class::DeviceImpl, impl_device_class, prelude::*, zeroable::Zeroable, + bindings::*, c_str, declare_properties, define_property, prelude::*, qdev::DeviceImpl, + qom::ObjectImpl, zeroable::Zeroable, }; #[test] From 716d89f9cc14faf784d83c945c40b7e8256ae525 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Oct 2024 10:14:11 +0100 Subject: [PATCH 0489/2892] rust: re-export C types from qemu-api submodules Long term we do not want device code to use "bindings" at all, so make it possible to get the relevant types from the other modules of qemu-api. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 7 +++++-- rust/qemu-api/src/qom.rs | 12 +++++++----- rust/qemu-api/src/sysbus.rs | 5 +---- rust/qemu-api/src/vmstate.rs | 9 +++++---- rust/qemu-api/tests/tests.rs | 9 +++++++-- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index ad4c12d097..07a502a837 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -6,10 +6,13 @@ use std::ffi::CStr; +pub use bindings::{DeviceClass, DeviceState, Property}; + use crate::{ - bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, + bindings::{self, Error}, prelude::*, - qom::ClassInitImpl, + qom::{ClassInitImpl, ObjectClass}, + vmstate::VMStateDescription, }; /// Trait providing the contents of [`DeviceClass`]. diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 2222d1a5ab..a663647ffe 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -33,7 +33,9 @@ use std::{ffi::CStr, os::raw::c_void}; -use crate::bindings::{self, Object, ObjectClass, TypeInfo}; +pub use bindings::{Object, ObjectClass}; + +use crate::bindings::{self, TypeInfo}; unsafe extern "C" fn rust_instance_init(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init @@ -164,9 +166,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// /// Each struct will implement this trait with `T` equal to each /// superclass. For example, a device should implement at least -/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>` and -/// `ClassInitImpl<`[`ObjectClass`](crate::bindings::ObjectClass)`>`. -/// Such implementations are made in one of two ways. +/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and +/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are made +/// in one of two ways. /// /// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` /// crate itself. The Rust implementation of methods will come from a @@ -221,7 +223,7 @@ pub trait ClassInitImpl { /// /// The virtual method implementations usually come from another /// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl) - /// when `T` is [`DeviceClass`](crate::bindings::DeviceClass). + /// when `T` is [`DeviceClass`](crate::qdev::DeviceClass). /// /// On entry, `klass`'s parent class is initialized, while the other fields /// are all zero; it is therefore assumed that all fields in `T` can be diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index fa69cadd7c..9abc687a26 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -7,10 +7,7 @@ use std::{ffi::CStr, ptr::addr_of}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ - bindings::{self, DeviceClass}, - cell::bql_locked, - irq::InterruptSource, - prelude::*, + bindings, cell::bql_locked, irq::InterruptSource, prelude::*, qdev::DeviceClass, qom::ClassInitImpl, }; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index bedcf1e8f3..25c68b703e 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -10,6 +10,8 @@ //! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when //! declaring a device model state struct. +pub use crate::bindings::VMStateDescription; + #[doc(alias = "VMSTATE_UNUSED_BUFFER")] #[macro_export] macro_rules! vmstate_unused_buffer { @@ -328,7 +330,7 @@ macro_rules! vmstate_fields { } /// A transparent wrapper type for the `subsections` field of -/// [`VMStateDescription`](crate::bindings::VMStateDescription). +/// [`VMStateDescription`]. /// /// This is necessary to be able to declare subsection descriptions as statics, /// because the only way to implement `Sync` for a foreign type (and `*const` @@ -342,9 +344,8 @@ pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMSta unsafe impl Sync for VMStateSubsectionsWrapper {} -/// Helper macro to declare a list of subsections -/// ([`VMStateDescription`](`crate::bindings::VMStateDescription`)) into a -/// static and return a pointer to the array of pointers it created. +/// Helper macro to declare a list of subsections ([`VMStateDescription`]) +/// into a static and return a pointer to the array of pointers it created. #[macro_export] macro_rules! vmstate_subsections { ($($subsection:expr),*$(,)*) => {{ diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 78f7da474b..68557fb85c 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -5,8 +5,13 @@ use std::ffi::CStr; use qemu_api::{ - bindings::*, c_str, declare_properties, define_property, prelude::*, qdev::DeviceImpl, - qom::ObjectImpl, zeroable::Zeroable, + bindings::*, + c_str, declare_properties, define_property, + prelude::*, + qdev::{DeviceImpl, DeviceState, Property}, + qom::ObjectImpl, + vmstate::VMStateDescription, + zeroable::Zeroable, }; #[test] From d4873c5d4fdd11f484df183e01c0825fe347fd8b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Nov 2024 12:08:43 +0100 Subject: [PATCH 0490/2892] bql: add a "mock" BQL for Rust unit tests Right now, the stub BQL in stubs/iothread-lock.c always reports itself as unlocked. However, Rust would like to run its tests in an environment where the BQL *is* locked. Provide an extremely dirty function that flips the return value of bql_is_locked() to true. Signed-off-by: Paolo Bonzini --- include/qemu/main-loop.h | 8 ++++++++ rust/qemu-api/meson.build | 2 +- rust/qemu-api/src/cell.rs | 26 +++++++++++++++++++++++--- stubs/iothread-lock.c | 8 +++++++- system/cpus.c | 6 ++++++ 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 646306c272..3935a57339 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -247,6 +247,14 @@ void event_notifier_set_handler(EventNotifier *e, GSource *iohandler_get_g_source(void); AioContext *iohandler_get_aio_context(void); +/** + * rust_bql_mock_lock: + * + * Called from Rust doctests to make bql_lock() return true. + * Do not touch. + */ +void rust_bql_mock_lock(void); + /** * bql_locked: Return lock status of the Big QEMU Lock (BQL) * diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 7ff408ad68..50ec00e128 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -60,7 +60,7 @@ test('rust-qemu-api-integration', dependencies: [qemu_api, qemu_api_macros], link_whole: [rust_qemu_api_objs, libqemuutil]), args: [ - '--test', + '--test', '--test-threads', '1', '--format', 'pretty', ], protocol: 'rust', diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 28349de291..eae4e2ce78 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -124,9 +124,18 @@ use std::{ use crate::bindings; -// TODO: When building doctests do not include the actual BQL, because cargo -// does not know how to link them to libqemuutil. This can be fixed by -// running rustdoc from "meson test" instead of relying on cargo. +/// An internal function that is used by doctests. +pub fn bql_start_test() { + if cfg!(MESON) { + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + bindings::rust_bql_mock_lock(); + } + } +} + pub fn bql_locked() -> bool { // SAFETY: the function does nothing but return a thread-local bool !cfg!(MESON) || unsafe { bindings::bql_locked() } @@ -220,6 +229,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// ``` @@ -236,6 +246,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -253,6 +264,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let cell = BqlCell::new(5); /// assert_eq!(cell.get(), 5); @@ -274,6 +286,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// let five = c.into_inner(); @@ -293,6 +306,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -315,6 +329,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -333,6 +348,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// let five = c.take(); @@ -461,6 +477,7 @@ impl BqlRefCell { /// /// ``` /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -472,6 +489,7 @@ impl BqlRefCell { /// /// ```should_panic /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -513,6 +531,7 @@ impl BqlRefCell { /// /// ``` /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new("hello".to_owned()); /// @@ -525,6 +544,7 @@ impl BqlRefCell { /// /// ```should_panic /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// let m = c.borrow(); diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 5467659895..6050c081f5 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -1,11 +1,17 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +static bool bql_is_locked = false; static uint32_t bql_unlock_blocked; bool bql_locked(void) { - return false; + return bql_is_locked; +} + +void rust_bql_mock_lock(void) +{ + bql_is_locked = true; } void bql_lock_impl(const char *file, int line) diff --git a/system/cpus.c b/system/cpus.c index ba633c7688..4b43ceb543 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -538,6 +538,12 @@ bool qemu_in_main_thread(void) return bql_locked(); } +void rust_bql_mock_lock(void) +{ + error_report("This function should be used only from tests"); + abort(); +} + /* * The BQL is taken from so many places that it is worth profiling the * callers directly, instead of funneling them all through a single function. From c2f41c1b152bfe9aa72bbdf413c11c5ae9209f30 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Nov 2024 22:34:13 +0100 Subject: [PATCH 0491/2892] rust: tests: allow writing more than one test Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/tests.rs | 111 ++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 68557fb85c..dc0ed19019 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -6,7 +6,9 @@ use std::ffi::CStr; use qemu_api::{ bindings::*, - c_str, declare_properties, define_property, + c_str, + cell::{self, BqlCell}, + declare_properties, define_property, prelude::*, qdev::{DeviceImpl, DeviceState, Property}, qom::ObjectImpl, @@ -14,55 +16,68 @@ use qemu_api::{ zeroable::Zeroable, }; +// Test that macros can compile. +pub static VMSTATE: VMStateDescription = VMStateDescription { + name: c_str!("name").as_ptr(), + unmigratable: true, + ..Zeroable::ZERO +}; + +#[derive(qemu_api_macros::offsets)] +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct DummyState { + parent: DeviceState, + migrate_clock: bool, +} + +declare_properties! { + DUMMY_PROPERTIES, + define_property!( + c_str!("migrate-clk"), + DummyState, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool + ), +} + +unsafe impl ObjectType for DummyState { + type Class = ::Class; + const TYPE_NAME: &'static CStr = c_str!("dummy"); +} + +impl ObjectImpl for DummyState { + type ParentType = DeviceState; + const ABSTRACT: bool = false; +} + +impl DeviceImpl for DummyState { + fn properties() -> &'static [Property] { + &DUMMY_PROPERTIES + } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&VMSTATE) + } +} + +fn init_qom() { + static ONCE: BqlCell = BqlCell::new(false); + + cell::bql_start_test(); + if !ONCE.get() { + unsafe { + module_call_init(module_init_type::MODULE_INIT_QOM); + } + ONCE.set(true); + } +} + #[test] -fn test_device_decl_macros() { - // Test that macros can compile. - pub static VMSTATE: VMStateDescription = VMStateDescription { - name: c_str!("name").as_ptr(), - unmigratable: true, - ..Zeroable::ZERO - }; - - #[derive(qemu_api_macros::offsets)] - #[repr(C)] - #[derive(qemu_api_macros::Object)] - pub struct DummyState { - pub _parent: DeviceState, - pub migrate_clock: bool, - } - - declare_properties! { - DUMMY_PROPERTIES, - define_property!( - c_str!("migrate-clk"), - DummyState, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool - ), - } - - unsafe impl ObjectType for DummyState { - type Class = ::Class; - const TYPE_NAME: &'static CStr = c_str!("dummy"); - } - - impl ObjectImpl for DummyState { - type ParentType = DeviceState; - const ABSTRACT: bool = false; - } - - impl DeviceImpl for DummyState { - fn properties() -> &'static [Property] { - &DUMMY_PROPERTIES - } - fn vmsd() -> Option<&'static VMStateDescription> { - Some(&VMSTATE) - } - } - +/// Create and immediately drop an instance. +fn test_object_new() { + init_qom(); unsafe { - module_call_init(module_init_type::MODULE_INIT_QOM); object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); } } From f50cd85c8475c16374d0e138efda222ce4455f53 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 14:32:16 +0100 Subject: [PATCH 0492/2892] rust: qom: add casting functionality Add traits that let client cast typecast safely between object types. In particular, an upcast is compile-time guaranteed to succeed, and a YOLO C-style downcast must be marked as unsafe. The traits are based on an IsA<> trait that declares what is a subclass of what, which is an idea taken from glib-rs (https://docs.rs/glib/latest/glib/object/trait.IsA.html). The four primitives are also taken from there (https://docs.rs/glib/latest/glib/object/trait.Cast.html). However, the implementation of casting itself is a bit different and uses the Deref trait. This removes some pointer arithmetic from the pl011 device; it is also a prerequisite for the definition of methods, so that they can be invoked on all subclass structs. This will use the IsA<> trait to detect the structs that support the methods. glib also has a "monadic" casting trait which could be implemented on Option (as in https://docs.rs/glib/latest/glib/object/trait.CastNone.html) and perhaps even Result. For now I'm leaving it out, as the patch is already big enough and the benefit seems debatable. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 1 + rust/hw/char/pl011/src/device.rs | 12 +- rust/qemu-api/src/prelude.rs | 7 + rust/qemu-api/src/qdev.rs | 1 + rust/qemu-api/src/qom.rs | 283 ++++++++++++++++++++++++++++++- rust/qemu-api/src/sysbus.rs | 7 +- rust/qemu-api/tests/tests.rs | 68 +++++++- 7 files changed, 366 insertions(+), 13 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6ec19b6729..5b6b6ca438 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -38,6 +38,7 @@ should_implement_trait = "deny" unused_self = "allow" # default-allow lints +as_ptr_cast_mut = "deny" as_underscore = "deny" assertions_on_result_states = "deny" bool_to_int_with_if = "deny" diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 3fed8b4ad2..e85d13c5a2 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -106,6 +106,8 @@ pub struct PL011State { device_id: DeviceId, } +qom_isa!(PL011State : SysBusDevice, DeviceState, Object); + unsafe impl ObjectType for PL011State { type Class = ::Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; @@ -140,8 +142,6 @@ impl PL011State { unsafe fn init(&mut self) { const CLK_NAME: &CStr = c_str!("clk"); - let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::()) }; - // SAFETY: // // self and self.iomem are guaranteed to be valid at this point since callers @@ -155,15 +155,16 @@ impl PL011State { Self::TYPE_NAME.as_ptr(), 0x1000, ); + + let sbd: &mut SysBusDevice = self.upcast_mut(); sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); } for irq in self.interrupts.iter() { + let sbd: &SysBusDevice = self.upcast(); sbd.init_irq(irq); } - let dev = addr_of_mut!(*self).cast::(); - // SAFETY: // // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, @@ -172,6 +173,7 @@ impl PL011State { // calls this function to initialize the fields; therefore no code is // able to access an invalid self.clock value. unsafe { + let dev: &mut DeviceState = self.upcast_mut(); self.clock = NonNull::new(qdev_init_clock_in( dev, CLK_NAME.as_ptr(), @@ -632,6 +634,8 @@ impl PL011Luminary { } } +qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); + unsafe impl ObjectType for PL011Luminary { type Class = ::Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 5cc41f081f..a0a71fcd6b 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,4 +7,11 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; +pub use crate::qom::IsA; +pub use crate::qom::Object; +pub use crate::qom::ObjectCast; +pub use crate::qom::ObjectCastMut; +pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectType; + +pub use crate::qom_isa; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 07a502a837..686054e737 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -144,3 +144,4 @@ unsafe impl ObjectType for DeviceState { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } +qom_isa!(DeviceState: Object); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index a663647ffe..74ea5721f7 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -4,15 +4,22 @@ //! Bindings to access QOM functionality from Rust. //! -//! This module provides automatic creation and registration of `TypeInfo` -//! for classes that are written in Rust, and mapping between Rust traits -//! and QOM vtables. +//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU +//! devices. This module makes QOM's features available in Rust through two main +//! mechanisms: +//! +//! * Automatic creation and registration of `TypeInfo` for classes that are +//! written in Rust, as well as mapping between Rust traits and QOM vtables. +//! +//! * Type-safe casting between parent and child classes, through the [`IsA`] +//! trait and methods such as [`upcast`](ObjectCast::upcast) and +//! [`downcast`](ObjectCast::downcast). //! //! # Structure of a class //! //! A leaf class only needs a struct holding instance state. The struct must -//! implement the [`ObjectType`] trait, as well as any `*Impl` traits that exist -//! for its superclasses. +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` +//! traits that exist for its superclasses. //! //! If a class has subclasses, it will also provide a struct for instance data, //! with the same characteristics as for concrete classes, but it also needs @@ -31,11 +38,57 @@ //! the source for this is the `*Impl` trait; the associated consts and //! functions if needed are wrapped to map C types into Rust types. -use std::{ffi::CStr, os::raw::c_void}; +use std::{ + ffi::CStr, + ops::{Deref, DerefMut}, + os::raw::c_void, +}; pub use bindings::{Object, ObjectClass}; -use crate::bindings::{self, TypeInfo}; +use crate::bindings::{self, object_dynamic_cast, TypeInfo}; + +/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct +/// or indirect parent of `Self`). +/// +/// # Safety +/// +/// The struct `Self` must be `#[repr(C)]` and must begin, directly or +/// indirectly, with a field of type `P`. This ensures that invalid casts, +/// which rely on `IsA<>` for static checking, are rejected at compile time. +pub unsafe trait IsA: ObjectType {} + +// SAFETY: it is always safe to cast to your own type +unsafe impl IsA for T {} + +/// Macro to mark superclasses of QOM classes. This enables type-safe +/// up- and downcasting. +/// +/// # Safety +/// +/// This macro is a thin wrapper around the [`IsA`] trait and performs +/// no checking whatsoever of what is declared. It is the caller's +/// responsibility to have $struct begin, directly or indirectly, with +/// a field of type `$parent`. +#[macro_export] +macro_rules! qom_isa { + ($struct:ty : $($parent:ty),* ) => { + $( + // SAFETY: it is the caller responsibility to have $parent as the + // first field + unsafe impl $crate::qom::IsA<$parent> for $struct {} + + impl AsRef<$parent> for $struct { + fn as_ref(&self) -> &$parent { + // SAFETY: follows the same rules as for IsA, which is + // declared above. + let ptr: *const Self = self; + unsafe { &*ptr.cast::<$parent>() } + } + } + )* + }; +} unsafe extern "C" fn rust_instance_init(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init @@ -96,8 +149,224 @@ pub unsafe trait ObjectType: Sized { /// The name of the type, which can be passed to `object_new()` to /// generate an instance of this type. const TYPE_NAME: &'static CStr; + + /// Return the receiver as an Object. This is always safe, even + /// if this type represents an interface. + fn as_object(&self) -> &Object { + unsafe { &*self.as_object_ptr() } + } + + /// Return the receiver as a const raw pointer to Object. + /// This is preferrable to `as_object_mut_ptr()` if a C + /// function only needs a `const Object *`. + fn as_object_ptr(&self) -> *const Object { + self.as_ptr().cast() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// This cast is always safe, but because the result is mutable + /// and the incoming reference is not, this should only be used + /// for calls to C functions, and only if needed. + unsafe fn as_object_mut_ptr(&self) -> *mut Object { + self.as_object_ptr() as *mut _ + } } +/// This trait provides safe casting operations for QOM objects to raw pointers, +/// to be used for example for FFI. The trait can be applied to any kind of +/// reference or smart pointers, and enforces correctness through the [`IsA`] +/// trait. +pub trait ObjectDeref: Deref +where + Self::Target: ObjectType, +{ + /// Convert to a const Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass + fn as_ptr(&self) -> *const U + where + Self::Target: IsA, + { + let ptr: *const Self::Target = self.deref(); + ptr.cast::() + } + + /// Convert to a mutable Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass. + /// Used to implement interior mutability for objects. + /// + /// # Safety + /// + /// This method is unsafe because it overrides const-ness of `&self`. + /// Bindings to C APIs will use it a lot, but otherwise it should not + /// be necessary. + unsafe fn as_mut_ptr(&self) -> *mut U + where + Self::Target: IsA, + { + #[allow(clippy::as_ptr_cast_mut)] + { + self.as_ptr::() as *mut _ + } + } +} + +/// Trait that adds extra functionality for `&T` where `T` is a QOM +/// object type. Allows conversion to/from C objects in generic code. +pub trait ObjectCast: ObjectDeref + Copy +where + Self::Target: ObjectType, +{ + /// Safely convert from a derived type to one of its parent types. + /// + /// This is always safe; the [`IsA`] trait provides static verification + /// trait that `Self` dereferences to `U` or a child of `U`. + fn upcast<'a, U: ObjectType>(self) -> &'a U + where + Self::Target: IsA, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast::() } + } + + /// Attempt to convert to a derived type. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn downcast<'a, U: IsA>(self) -> Option<&'a U> + where + Self: 'a, + { + self.dynamic_cast::() + } + + /// Attempt to convert between any two types in the QOM hierarchy. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> + where + Self: 'a, + { + unsafe { + // SAFETY: upcasting to Object is always valid, and the + // return type is either NULL or the argument itself + let result: *const U = + object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); + + result.as_ref() + } + } + + /// Convert to any QOM type without verification. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct; only + /// use when performance is paramount. It is still better than a raw + /// pointer `cast()`, which does not even check that you remain in the + /// realm of QOM `ObjectType`s. + /// + /// `unsafe_cast::()` is always safe. + unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U + where + Self: 'a, + { + unsafe { &*(self.as_ptr::().cast::()) } + } +} + +impl ObjectDeref for &T {} +impl ObjectCast for &T {} + +/// Trait for mutable type casting operations in the QOM hierarchy. +/// +/// This trait provides the mutable counterparts to [`ObjectCast`]'s conversion +/// functions. Unlike `ObjectCast`, this trait returns `Result` for fallible +/// conversions to preserve the original smart pointer if the cast fails. This +/// is necessary because mutable references cannot be copied, so a failed cast +/// must return ownership of the original reference. For example: +/// +/// ```ignore +/// let mut dev = get_device(); +/// // If this fails, we need the original `dev` back to try something else +/// match dev.dynamic_cast_mut::() { +/// Ok(foodev) => /* use foodev */, +/// Err(dev) => /* still have ownership of dev */ +/// } +/// ``` +pub trait ObjectCastMut: Sized + ObjectDeref + DerefMut +where + Self::Target: ObjectType, +{ + /// Safely convert from a derived type to one of its parent types. + /// + /// This is always safe; the [`IsA`] trait provides static verification + /// that `Self` dereferences to `U` or a child of `U`. + fn upcast_mut<'a, U: ObjectType>(self) -> &'a mut U + where + Self::Target: IsA, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast_mut::() } + } + + /// Attempt to convert to a derived type. + /// + /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the + /// object if the conversion failed. This is verified at runtime by + /// checking the object's type information. + fn downcast_mut<'a, U: IsA>(self) -> Result<&'a mut U, Self> + where + Self: 'a, + { + self.dynamic_cast_mut::() + } + + /// Attempt to convert between any two types in the QOM hierarchy. + /// + /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the + /// object if the conversion failed. This is verified at runtime by + /// checking the object's type information. + fn dynamic_cast_mut<'a, U: ObjectType>(self) -> Result<&'a mut U, Self> + where + Self: 'a, + { + unsafe { + // SAFETY: upcasting to Object is always valid, and the + // return type is either NULL or the argument itself + let result: *mut U = + object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); + + result.as_mut().ok_or(self) + } + } + + /// Convert to any QOM type without verification. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct; only + /// use when performance is paramount. It is still better than a raw + /// pointer `cast()`, which does not even check that you remain in the + /// realm of QOM `ObjectType`s. + /// + /// `unsafe_cast::()` is always safe. + unsafe fn unsafe_cast_mut<'a, U: ObjectType>(self) -> &'a mut U + where + Self: 'a, + { + unsafe { &mut *self.as_mut_ptr::().cast::() } + } +} + +impl ObjectDeref for &mut T {} +impl ObjectCastMut for &mut T {} + /// Trait a type must implement to be registered with QEMU. pub trait ObjectImpl: ObjectType + ClassInitImpl { /// The parent of the type. This should match the first field of diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 9abc687a26..8193734bde 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -7,7 +7,11 @@ use std::{ffi::CStr, ptr::addr_of}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ - bindings, cell::bql_locked, irq::InterruptSource, prelude::*, qdev::DeviceClass, + bindings, + cell::bql_locked, + irq::InterruptSource, + prelude::*, + qdev::{DeviceClass, DeviceState}, qom::ClassInitImpl, }; @@ -16,6 +20,7 @@ unsafe impl ObjectType for SysBusDevice { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; } +qom_isa!(SysBusDevice: DeviceState, Object); // TODO: add SysBusDeviceImpl impl ClassInitImpl for T diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index dc0ed19019..7b63e28c2f 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -2,7 +2,11 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::ffi::CStr; +use std::{ + ffi::CStr, + os::raw::c_void, + ptr::{addr_of, addr_of_mut}, +}; use qemu_api::{ bindings::*, @@ -31,6 +35,8 @@ pub struct DummyState { migrate_clock: bool, } +qom_isa!(DummyState: Object, DeviceState); + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -81,3 +87,63 @@ fn test_object_new() { object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); } } + +// a note on all "cast" tests: usually, especially for downcasts the desired +// class would be placed on the right, for example: +// +// let sbd_ref = p.dynamic_cast::(); +// +// Here I am doing the opposite to check that the resulting type is correct. + +#[test] +#[allow(clippy::shadow_unrelated)] +/// Test casts on shared references. +fn test_cast() { + init_qom(); + let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; + + let p_ref: &DummyState = unsafe { &*p }; + let obj_ref: &Object = p_ref.upcast(); + assert_eq!(addr_of!(*obj_ref), p.cast()); + + let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); + assert!(sbd_ref.is_none()); + + let dev_ref: Option<&DeviceState> = obj_ref.downcast(); + assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast()); + + // SAFETY: the cast is wrong, but the value is only used for comparison + unsafe { + let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); + assert_eq!(addr_of!(*sbd_ref), p.cast()); + + object_unref(p_ref.as_object_mut_ptr().cast::()); + } +} + +#[test] +#[allow(clippy::shadow_unrelated)] +/// Test casts on mutable references. +fn test_cast_mut() { + init_qom(); + let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; + + let p_ref: &mut DummyState = unsafe { &mut *p }; + let obj_ref: &mut Object = p_ref.upcast_mut(); + assert_eq!(addr_of_mut!(*obj_ref), p.cast()); + + let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut(); + let obj_ref = sbd_ref.unwrap_err(); + + let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut(); + let dev_ref = dev_ref.unwrap(); + assert_eq!(addr_of_mut!(*dev_ref), p.cast()); + + // SAFETY: the cast is wrong, but the value is only used for comparison + unsafe { + let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut(); + assert_eq!(addr_of_mut!(*sbd_ref), p.cast()); + + object_unref(p_ref.as_object_mut_ptr().cast::()); + } +} From ba3b81f3b668d3faa6cbdb39e123394f7bf637c7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 00:01:57 +0100 Subject: [PATCH 0493/2892] rust: qom: add initial subset of methods on Object Add an example of implementing instance methods and converting the result back to a Rust type. In this case the returned types are a string (actually a Cow; but that's transparent as long as it derefs to &str) and a QOM class. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/prelude.rs | 1 + rust/qemu-api/src/qom.rs | 56 ++++++++++++++++++++++++++++++++++-- rust/qemu-api/tests/tests.rs | 12 ++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index a0a71fcd6b..6f32deeb2e 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -12,6 +12,7 @@ pub use crate::qom::Object; pub use crate::qom::ObjectCast; pub use crate::qom::ObjectCastMut; pub use crate::qom::ObjectDeref; +pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; pub use crate::qom_isa; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 74ea5721f7..7d5fbef1e1 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -5,8 +5,8 @@ //! Bindings to access QOM functionality from Rust. //! //! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU -//! devices. This module makes QOM's features available in Rust through two main -//! mechanisms: +//! devices. This module makes QOM's features available in Rust through three +//! main mechanisms: //! //! * Automatic creation and registration of `TypeInfo` for classes that are //! written in Rust, as well as mapping between Rust traits and QOM vtables. @@ -15,6 +15,11 @@ //! trait and methods such as [`upcast`](ObjectCast::upcast) and //! [`downcast`](ObjectCast::downcast). //! +//! * Automatic delegation of parent class methods to child classes. When a +//! trait uses [`IsA`] as a bound, its contents become available to all child +//! classes through blanket implementations. This works both for class methods +//! and for instance methods accessed through references or smart pointers. +//! //! # Structure of a class //! //! A leaf class only needs a struct holding instance state. The struct must @@ -37,6 +42,16 @@ //! `ClassInitImpl`. This fills the vtable in the class struct; //! the source for this is the `*Impl` trait; the associated consts and //! functions if needed are wrapped to map C types into Rust types. +//! +//! * a trait for instance methods, for example `DeviceMethods`. This trait is +//! automatically implemented for any reference or smart pointer to a device +//! instance. It calls into the vtable provides access across all subclasses +//! to methods defined for the class. +//! +//! * optionally, a trait for class methods, for example `DeviceClassMethods`. +//! This provides access to class-wide functionality that doesn't depend on +//! instance data. Like instance methods, these are automatically inherited by +//! child classes. use std::{ ffi::CStr, @@ -46,7 +61,7 @@ use std::{ pub use bindings::{Object, ObjectClass}; -use crate::bindings::{self, object_dynamic_cast, TypeInfo}; +use crate::bindings::{self, object_dynamic_cast, object_get_class, object_get_typename, TypeInfo}; /// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct /// or indirect parent of `Self`). @@ -532,3 +547,38 @@ unsafe impl ObjectType for Object { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; } + +/// Trait for methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Return the name of the type of `self` + fn typename(&self) -> std::borrow::Cow<'_, str> { + let obj = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + // The result of the C API has static lifetime + unsafe { + let p = object_get_typename(obj.as_mut_ptr()); + CStr::from_ptr(p).to_string_lossy() + } + } + + fn get_class(&self) -> &'static ::Class { + let obj = self.upcast::(); + + // SAFETY: all objects can call object_get_class; the actual class + // type is guaranteed by the implementation of `ObjectType` and + // `ObjectImpl`. + let klass: &'static ::Class = + unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; + + klass + } +} + +impl ObjectMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 7b63e28c2f..1d2825b098 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -88,6 +88,18 @@ fn test_object_new() { } } +#[test] +/// Try invoking a method on an object. +fn test_typename() { + init_qom(); + let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; + let p_ref: &DummyState = unsafe { &*p }; + assert_eq!(p_ref.typename(), "dummy"); + unsafe { + object_unref(p_ref.as_object_mut_ptr().cast::()); + } +} + // a note on all "cast" tests: usually, especially for downcasts the desired // class would be placed on the right, for example: // From e05fbacd20366de436db82b776dfbc77071f6e29 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 Nov 2024 17:26:24 +0100 Subject: [PATCH 0494/2892] rust: qemu-api: add a module to wrap functions and zero-sized closures One recurring issue when writing Rust bindings is how to convert a Rust function ("fn" or "impl Fn") to a C function, and how to pass around "self" to a C function that only takes a void*. An easy solution would be to store on the heap a pair consisting of a pointer to the Rust function and the pointer to "self", but it is possible to do better. If an "Fn" has zero size (that is, if it is a zero-capture closures or a function pointer---which in turn includes all methods), it is possible to build a generic Rust function that calls it even if you only have the type; you don't need either the pointer to the function itself (because the address of the code is part of the type) or any closure data (because it has size zero). Introduce a wrapper that provides the functionality of calling the function given only its type. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/callbacks.rs | 144 +++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + 3 files changed, 146 insertions(+) create mode 100644 rust/qemu-api/src/callbacks.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 50ec00e128..ccb20f38c1 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -17,6 +17,7 @@ _qemu_api_rs = static_library( 'src/lib.rs', 'src/bindings.rs', 'src/bitops.rs', + 'src/callbacks.rs', 'src/cell.rs', 'src/c_str.rs', 'src/irq.rs', diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs new file mode 100644 index 0000000000..314f9dce96 --- /dev/null +++ b/rust/qemu-api/src/callbacks.rs @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT + +//! Utility functions to deal with callbacks from C to Rust. + +use std::{mem, ptr::NonNull}; + +/// Trait for functions (types implementing [`Fn`]) that can be used as +/// callbacks. These include both zero-capture closures and function pointers. +/// +/// In Rust, calling a function through the `Fn` trait normally requires a +/// `self` parameter, even though for zero-sized functions (including function +/// pointers) the type itself contains all necessary information to call the +/// function. This trait provides a `call` function that doesn't require `self`, +/// allowing zero-sized functions to be called using only their type. +/// +/// This enables zero-sized functions to be passed entirely through generic +/// parameters and resolved at compile-time. A typical use is a function +/// receiving an unused parameter of generic type `F` and calling it via +/// `F::call` or passing it to another function via `func::`. +/// +/// QEMU uses this trick to create wrappers to C callbacks. The wrappers +/// are needed to convert an opaque `*mut c_void` into a Rust reference, +/// but they only have a single opaque that they can use. The `FnCall` +/// trait makes it possible to use that opaque for `self` or any other +/// reference: +/// +/// ```ignore +/// // The compiler creates a new `rust_bh_cb` wrapper for each function +/// // passed to `qemu_bh_schedule_oneshot` below. +/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( +/// opaque: *mut c_void, +/// ) { +/// // SAFETY: the opaque was passed as a reference to `T`. +/// F::call((unsafe { &*(opaque.cast::()) }, )) +/// } +/// +/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. +/// // Using a reference allows usage in const context. +/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { +/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; +/// unsafe { +/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) +/// } +/// } +/// ``` +/// +/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore +/// compiled to a separate function ("monomorphization"). If you wanted +/// to pass `self` as the opaque value, the generic parameters would be +/// `rust_bh_cb::`. +/// +/// `Args` is a tuple type whose types are the arguments of the function, +/// while `R` is the returned type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::callbacks::FnCall; +/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// F::call((s,)) +/// } +/// +/// let s: String = call_it(&str::to_owned, "hello world"); +/// assert_eq!(s, "hello world"); +/// ``` +/// +/// Note that the compiler will produce a different version of `call_it` for +/// each function that is passed to it. Therefore the argument is not really +/// used, except to decide what is `F` and what `F::call` does. +/// +/// Attempting to pass a non-zero-sized closure causes a compile-time failure: +/// +/// ```compile_fail +/// # use qemu_api::callbacks::FnCall; +/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { +/// # F::call((s,)) +/// # } +/// let x: &'static str = "goodbye world"; +/// call_it(&move |_| String::from(x), "hello workd"); +/// ``` +/// +/// # Safety +/// +/// Because `Self` is a zero-sized type, all instances of the type are +/// equivalent. However, in addition to this, `Self` must have no invariants +/// that could be violated by creating a reference to it. +/// +/// This is always true for zero-capture closures and function pointers, as long +/// as the code is able to name the function in the first place. +pub unsafe trait FnCall: 'static + Sync + Sized { + /// Referring to this internal constant asserts that the `Self` type is + /// zero-sized. Can be replaced by an inline const expression in + /// Rust 1.79.0+. + const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::() == 0) }; + + /// Call the function with the arguments in args. + fn call(a: Args) -> R; +} + +macro_rules! impl_call { + ($($args:ident,)* ) => ( + // SAFETY: because each function is treated as a separate type, + // accessing `FnCall` is only possible in code that would be + // allowed to call the function. + unsafe impl FnCall<($($args,)*), R> for F + where + F: 'static + Sync + Sized + Fn($($args, )*) -> R, + { + #[inline(always)] + fn call(a: ($($args,)*)) -> R { + let _: () = Self::ASSERT_ZERO_SIZED; + + // SAFETY: the safety of this method is the condition for implementing + // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, + // see https://github.com/rust-lang/libs-team/issues/292. + let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; + let ($($args,)*) = a; + f($($args,)*) + } + } + ) +} + +impl_call!(_1, _2, _3, _4, _5,); +impl_call!(_1, _2, _3, _4,); +impl_call!(_1, _2, _3,); +impl_call!(_1, _2,); +impl_call!(_1,); +impl_call!(); + +#[cfg(test)] +mod tests { + use super::*; + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { + F::call(("hello world",)) + } + + #[test] + fn test_call() { + assert_eq!(do_test_call(&str::to_owned), "hello world") + } +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 124bece044..4b43e02c0f 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -14,6 +14,7 @@ pub mod prelude; pub mod bitops; pub mod c_str; +pub mod callbacks; pub mod cell; pub mod irq; pub mod module; From b8f8c10f85a62502ac5f9d66dfdf719e6d8b91b5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Dec 2024 16:55:51 +0100 Subject: [PATCH 0495/2892] kvm: consistently return 0/-errno from kvm_convert_memory In case of incorrect parameters, kvm_convert_memory() was returning -1 instead of -EINVAL. The guest won't notice because it will move anyway to RUN_STATE_INTERNAL_ERROR, but fix this for consistency and clarity. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 801cff16a5..77d811ca70 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2999,17 +2999,17 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) MemoryRegion *mr; RAMBlock *rb; void *addr; - int ret = -1; + int ret = -EINVAL; trace_kvm_convert_memory(start, size, to_private ? "shared_to_private" : "private_to_shared"); if (!QEMU_PTR_IS_ALIGNED(start, qemu_real_host_page_size()) || !QEMU_PTR_IS_ALIGNED(size, qemu_real_host_page_size())) { - return -1; + return ret; } if (!size) { - return -1; + return ret; } section = memory_region_find(get_system_memory(), start, size); @@ -3027,7 +3027,7 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) if (!to_private) { return 0; } - return -1; + return ret; } if (!memory_region_has_guest_memfd(mr)) { From 3f2a05b31ee9ce2ddb6c75a9bc3f5e7f7af9a76f Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Thu, 12 Dec 2024 15:51:15 +0100 Subject: [PATCH 0496/2892] target/i386: Reset TSCs of parked vCPUs too on VM reset Since commit 5286c3662294 ("target/i386: properly reset TSC on reset") QEMU writes the special value of "1" to each online vCPU TSC on VM reset to reset it. However parked vCPUs don't get that handling and due to that their TSCs get desynchronized when the VM gets reset. This in turn causes KVM to turn off PVCLOCK_TSC_STABLE_BIT in its exported PV clock. Note that KVM has no understanding of vCPU being currently parked. Without PVCLOCK_TSC_STABLE_BIT the sched clock is marked unstable in the guest's kvm_sched_clock_init(). This causes a performance regressions to show in some tests. Fix this issue by writing the special value of "1" also to TSCs of parked vCPUs on VM reset. Reproducing the issue: 1) Boot a VM with "-smp 2,maxcpus=3" or similar 2) device_add host-x86_64-cpu,id=vcpu,node-id=0,socket-id=0,core-id=2,thread-id=0 3) Wait a few seconds 4) device_del vcpu 5) Inside the VM run: # echo "t" >/proc/sysrq-trigger; dmesg | grep sched_clock_stable Observe the sched_clock_stable() value is 1. 6) Reboot the VM 7) Once the VM boots once again run inside it: # echo "t" >/proc/sysrq-trigger; dmesg | grep sched_clock_stable Observe the sched_clock_stable() value is now 0. Fixes: 5286c3662294 ("target/i386: properly reset TSC on reset") Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/r/5a605a88e9a231386dc803c60f5fed9b48108139.1734014926.git.maciej.szmigiero@oracle.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 11 +++++++++++ configs/targets/i386-softmmu.mak | 1 + configs/targets/x86_64-softmmu.mak | 1 + include/sysemu/kvm.h | 8 ++++++++ target/i386/kvm/kvm.c | 15 +++++++++++++++ 5 files changed, 36 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 77d811ca70..4ab277cc84 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -437,6 +437,16 @@ int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) return kvm_fd; } +static void kvm_reset_parked_vcpus(void *param) +{ + KVMState *s = param; + struct KVMParkedVcpu *cpu; + + QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { + kvm_arch_reset_parked_vcpu(cpu->vcpu_id, cpu->kvm_fd); + } +} + int kvm_create_vcpu(CPUState *cpu) { unsigned long vcpu_id = kvm_arch_vcpu_id(cpu); @@ -2728,6 +2738,7 @@ static int kvm_init(MachineState *ms) } qemu_register_reset(kvm_unpoison_all, NULL); + qemu_register_reset(kvm_reset_parked_vcpus, s); if (s->kernel_irqchip_allowed) { kvm_irqchip_create(s); diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 2ac69d5ba3..2eb0e86250 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=i386 TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y +TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index e12ac3dc59..920e9a4200 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y +TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index c3a60b2890..ab17c09a55 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -377,6 +377,14 @@ int kvm_arch_init(MachineState *ms, KVMState *s); int kvm_arch_init_vcpu(CPUState *cpu); int kvm_arch_destroy_vcpu(CPUState *cpu); +#ifdef TARGET_KVM_HAVE_RESET_PARKED_VCPU +void kvm_arch_reset_parked_vcpu(unsigned long vcpu_id, int kvm_fd); +#else +static inline void kvm_arch_reset_parked_vcpu(unsigned long vcpu_id, int kvm_fd) +{ +} +#endif + bool kvm_vcpu_id_is_valid(int vcpu_id); /* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 8e17942c3b..2ff618fbf1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2415,6 +2415,21 @@ void kvm_arch_after_reset_vcpu(X86CPU *cpu) } } +void kvm_arch_reset_parked_vcpu(unsigned long vcpu_id, int kvm_fd) +{ + g_autofree struct kvm_msrs *msrs = NULL; + + msrs = g_malloc0(sizeof(*msrs) + sizeof(msrs->entries[0])); + msrs->entries[0].index = MSR_IA32_TSC; + msrs->entries[0].data = 1; /* match the value in x86_cpu_reset() */ + msrs->nmsrs++; + + if (ioctl(kvm_fd, KVM_SET_MSRS, msrs) != 1) { + warn_report("parked vCPU %lu TSC reset failed: %d", + vcpu_id, errno); + } +} + void kvm_arch_do_init_vcpu(X86CPU *cpu) { CPUX86State *env = &cpu->env; From ffb0945140fde217507c5a56c3ab2fcb47ba94fa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Dec 2024 12:05:02 +0100 Subject: [PATCH 0497/2892] rust: pl011: fix declaration of LineControl bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bits in the LineControl struct were backwards. :( Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/lib.rs | 82 +++++++++++++++++------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 4dc0e8f345..d5089f7885 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -319,32 +319,21 @@ pub mod registers { /// Line Control Register, `UARTLCR_H` #[doc(alias = "UARTLCR_H")] pub struct LineControl { - /// 15:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u8, - /// 7 SPS Stick parity select. - /// 0 = stick parity is disabled - /// 1 = either: - /// • if the EPS bit is 0 then the parity bit is transmitted and checked - /// as a 1 • if the EPS bit is 1 then the parity bit is - /// transmitted and checked as a 0. This bit has no effect when - /// the PEN bit disables parity checking and generation. See Table 3-11 - /// on page 3-14 for the parity truth table. - pub sticky_parity: bool, - /// WLEN Word length. These bits indicate the number of data bits - /// transmitted or received in a frame as follows: b11 = 8 bits - /// b10 = 7 bits - /// b01 = 6 bits - /// b00 = 5 bits. - pub word_length: WordLength, - /// FEN Enable FIFOs: - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers 1 = transmit and receive FIFO - /// buffers are enabled (FIFO mode). - pub fifos_enabled: Mode, - /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits - /// are transmitted at the end of the frame. The receive - /// logic does not check for two stop bits being received. - pub two_stops_bits: bool, + /// BRK Send break. + /// + /// If this bit is set to `1`, a low-level is continually output on the + /// `UARTTXD` output, after completing transmission of the + /// current character. For the proper execution of the break command, + /// the software must set this bit for at least two complete + /// frames. For normal use, this bit must be cleared to `0`. + pub send_break: bool, + /// 1 PEN Parity enable: + /// + /// - 0 = parity is disabled and no parity bit added to the data frame + /// - 1 = parity checking and generation is enabled. + /// + /// See Table 3-11 on page 3-14 for the parity truth table. + pub parity_enabled: bool, /// EPS Even parity select. Controls the type of parity the UART uses /// during transmission and reception: /// - 0 = odd parity. The UART generates or checks for an odd number of @@ -355,21 +344,32 @@ pub mod registers { /// and generation. See Table 3-11 on page 3-14 for the parity /// truth table. pub parity: Parity, - /// 1 PEN Parity enable: - /// - /// - 0 = parity is disabled and no parity bit added to the data frame - /// - 1 = parity checking and generation is enabled. - /// - /// See Table 3-11 on page 3-14 for the parity truth table. - pub parity_enabled: bool, - /// BRK Send break. - /// - /// If this bit is set to `1`, a low-level is continually output on the - /// `UARTTXD` output, after completing transmission of the - /// current character. For the proper execution of the break command, - /// the software must set this bit for at least two complete - /// frames. For normal use, this bit must be cleared to `0`. - pub send_break: bool, + /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits + /// are transmitted at the end of the frame. The receive + /// logic does not check for two stop bits being received. + pub two_stops_bits: bool, + /// FEN Enable FIFOs: + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers 1 = transmit and receive FIFO + /// buffers are enabled (FIFO mode). + pub fifos_enabled: Mode, + /// WLEN Word length. These bits indicate the number of data bits + /// transmitted or received in a frame as follows: b11 = 8 bits + /// b10 = 7 bits + /// b01 = 6 bits + /// b00 = 5 bits. + pub word_length: WordLength, + /// 7 SPS Stick parity select. + /// 0 = stick parity is disabled + /// 1 = either: + /// • if the EPS bit is 0 then the parity bit is transmitted and checked + /// as a 1 • if the EPS bit is 1 then the parity bit is + /// transmitted and checked as a 0. This bit has no effect when + /// the PEN bit disables parity checking and generation. See Table 3-11 + /// on page 3-14 for the parity truth table. + pub sticky_parity: bool, + /// 15:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u8, } impl LineControl { From ac096b0bef98a79fafb1254fef121a175c9b73fc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2024 16:40:29 +0100 Subject: [PATCH 0498/2892] rust: pl011: match break logic of C version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check loopback_enabled(), not fifo_enabled(), like the C code. Also, set_break_error() must not happen until the break is read from the FIFO. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index e85d13c5a2..960ee38ed6 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -460,9 +460,8 @@ impl PL011State { } pub fn event(&mut self, event: QEMUChrEvent) { - if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.fifo_enabled() { + if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { self.put_fifo(DATA_BREAK); - self.receive_status_error_clear.set_break_error(true); } } From f65314bdd0c287097f7dd4b002c67ceee9729039 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2024 16:44:42 +0100 Subject: [PATCH 0499/2892] rust: pl011: always use reset() method on registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For CR, the ugly-ish "0.into()" idiom is already hidden within the reset method. Do not repeat it. For FR, standardize on reset() being equivalent to "*self = Self::default()" and let reset_fifo toggle only the bits that are related to FIFOs. This commit also reproduces C commit 02b1f7f6192 ("hw/char/pl011: Split RX/TX path of pl011_reset_fifo()", 2024-09-13). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 23 ++++++++++++++++------- rust/hw/char/pl011/src/lib.rs | 13 +++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 960ee38ed6..f2ee8763d8 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -262,7 +262,7 @@ impl PL011State { self.update(); } Ok(RSR) => { - self.receive_status_error_clear = 0.into(); + self.receive_status_error_clear.reset(); } Ok(FR) => { // flag writes are ignored @@ -283,7 +283,8 @@ impl PL011State { if bool::from(self.line_control.fifos_enabled()) ^ bool::from(new_val.fifos_enabled()) { - self.reset_fifo(); + self.reset_rx_fifo(); + self.reset_tx_fifo(); } if self.line_control.send_break() ^ new_val.send_break() { let mut break_enable: c_int = new_val.send_break().into(); @@ -442,16 +443,24 @@ impl PL011State { self.read_trigger = 1; self.ifl = 0x12; self.control.reset(); - self.flags = 0.into(); - self.reset_fifo(); + self.flags.reset(); + self.reset_rx_fifo(); + self.reset_tx_fifo(); } - pub fn reset_fifo(&mut self) { + pub fn reset_rx_fifo(&mut self) { self.read_count = 0; self.read_pos = 0; - /* Reset FIFO flags */ - self.flags.reset(); + // Reset FIFO flags + self.flags.set_receive_fifo_full(false); + self.flags.set_receive_fifo_empty(true); + } + + pub fn reset_tx_fifo(&mut self) { + // Reset FIFO flags + self.flags.set_transmit_fifo_full(false); + self.flags.set_transmit_fifo_empty(true); } pub fn can_receive(&self) -> bool { diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index d5089f7885..e3eacb0e6b 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -230,7 +230,7 @@ pub mod registers { impl ReceiveStatusErrorClear { pub fn reset(&mut self) { // All the bits are cleared to 0 on reset. - *self = 0.into(); + *self = Self::default(); } } @@ -297,19 +297,16 @@ pub mod registers { impl Flags { pub fn reset(&mut self) { - // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - self.set_receive_fifo_full(false); - self.set_transmit_fifo_full(false); - self.set_busy(false); - self.set_receive_fifo_empty(true); - self.set_transmit_fifo_empty(true); + *self = Self::default(); } } impl Default for Flags { fn default() -> Self { let mut ret: Self = 0.into(); - ret.reset(); + // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 + ret.set_receive_fifo_empty(true); + ret.set_transmit_fifo_empty(true); ret } } From e1f9353334859ea325f25bd88e01645af63b133b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2024 17:14:00 +0100 Subject: [PATCH 0500/2892] rust: pl011: fix break errors and definition of Data struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Data struct is wrong, and does not show how bits 8-15 of DR are the receive status. Fix it, and use it to fix break errors ("c >> 8" in the C code does not translate to "c.to_be_bytes()[3]"). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 15 ++++++------ rust/hw/char/pl011/src/lib.rs | 41 ++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index f2ee8763d8..5e3a9c6f58 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -30,8 +30,6 @@ const IBRD_MASK: u32 = 0xffff; /// Fractional Baud Rate Divider, `UARTFBRD` const FBRD_MASK: u32 = 0x3f; -const DATA_BREAK: u32 = 1 << 10; - /// QEMU sourced constant. pub const PL011_FIFO_DEPTH: usize = 16_usize; @@ -75,7 +73,7 @@ pub struct PL011State { pub dmacr: u32, pub int_enabled: u32, pub int_level: u32, - pub read_fifo: [u32; PL011_FIFO_DEPTH], + pub read_fifo: [registers::Data; PL011_FIFO_DEPTH], pub ilpr: u32, pub ibrd: u32, pub fbrd: u32, @@ -210,10 +208,11 @@ impl PL011State { self.int_level &= !registers::INT_RX; } // Update error bits. - self.receive_status_error_clear = c.to_be_bytes()[3].into(); + self.receive_status_error_clear.set_from_data(c); self.update(); // Must call qemu_chr_fe_accept_input, so return Continue: - return std::ops::ControlFlow::Continue(c.into()); + let c = u32::from(c); + return std::ops::ControlFlow::Continue(u64::from(c)); } Ok(RSR) => u8::from(self.receive_status_error_clear).into(), Ok(FR) => u16::from(self.flags).into(), @@ -406,7 +405,7 @@ impl PL011State { fn loopback_break(&mut self, enable: bool) { if enable { - self.loopback_tx(DATA_BREAK); + self.loopback_tx(registers::Data::BREAK.into()); } } @@ -470,7 +469,7 @@ impl PL011State { pub fn event(&mut self, event: QEMUChrEvent) { if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { - self.put_fifo(DATA_BREAK); + self.put_fifo(registers::Data::BREAK.into()); } } @@ -497,7 +496,7 @@ impl PL011State { let depth = self.fifo_depth(); assert!(depth > 0); let slot = (self.read_pos + self.read_count) & (depth - 1); - self.read_fifo[slot] = value; + self.read_fifo[slot] = registers::Data::from(value); self.read_count += 1; self.flags.set_receive_fifo_empty(false); if self.read_count == depth { diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index e3eacb0e6b..463ae60543 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -139,6 +139,21 @@ pub mod registers { //! unused thus treated as zero when read or written. use bilge::prelude::*; + /// Receive Status Register / Data Register common error bits + /// + /// The `UARTRSR` register is updated only when a read occurs + /// from the `UARTDR` register with the same status information + /// that can also be obtained by reading the `UARTDR` register + #[bitsize(8)] + #[derive(Clone, Copy, Default, DebugBits, FromBits)] + pub struct Errors { + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + _reserved_unpredictable: u4, + } + // TODO: FIFO Mode has different semantics /// Data Register, `UARTDR` /// @@ -181,16 +196,18 @@ pub mod registers { /// /// # Source /// ARM DDI 0183G 3.3.1 Data Register, UARTDR - #[bitsize(16)] - #[derive(Clone, Copy, DebugBits, FromBits)] + #[bitsize(32)] + #[derive(Clone, Copy, Default, DebugBits, FromBits)] #[doc(alias = "UARTDR")] pub struct Data { - _reserved: u4, pub data: u8, - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, + pub errors: Errors, + _reserved: u16, + } + + impl Data { + // bilge is not very const-friendly, unfortunately + pub const BREAK: Self = Self { value: 1 << 10 }; } // TODO: FIFO Mode has different semantics @@ -220,14 +237,14 @@ pub mod registers { #[bitsize(8)] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct ReceiveStatusErrorClear { - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, - _reserved_unpredictable: u4, + pub errors: Errors, } impl ReceiveStatusErrorClear { + pub fn set_from_data(&mut self, data: Data) { + self.set_errors(data.errors()); + } + pub fn reset(&mut self) { // All the bits are cleared to 0 on reset. *self = Self::default(); From e2e0828e0f25042a09b1cbada41a436d1258fdb8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Dec 2024 12:12:47 +0100 Subject: [PATCH 0501/2892] rust: pl011: extend registers to 32 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PL011 Technical Reference Manual lists the "real" size of the registers in table 3-1, and only rounds up to the next byte when describing the registers; for example, UARTDR is listed as having width 12/8 (12 bits read, 8 written) and only bits 15:0 are listed in "Table 3-2 UARTDR Register". However, in practice these are 32-bit registers, accessible only through 32-bit MMIO accesses; preserving the fiction that they're smaller introduces multiple casts (to go from the bilge bitfield type to e.g u16 to u64) and more importantly it breaks the migration stream because the Rust vmstate macros are not yet type safe. So, just make everything 32-bits wide. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 36 ++++++++++++++------------------ rust/hw/char/pl011/src/lib.rs | 23 +++++++++----------- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 5e3a9c6f58..090e5d6450 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -186,9 +186,9 @@ impl PL011State { pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow { use RegisterOffset::*; - std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) { + let value = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - u64::from(self.device_id[(offset - 0xfe0) >> 2]) + u32::from(self.device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); @@ -214,27 +214,25 @@ impl PL011State { let c = u32::from(c); return std::ops::ControlFlow::Continue(u64::from(c)); } - Ok(RSR) => u8::from(self.receive_status_error_clear).into(), - Ok(FR) => u16::from(self.flags).into(), - Ok(FBRD) => self.fbrd.into(), - Ok(ILPR) => self.ilpr.into(), - Ok(IBRD) => self.ibrd.into(), - Ok(LCR_H) => u16::from(self.line_control).into(), - Ok(CR) => { - // We exercise our self-control. - u16::from(self.control).into() - } - Ok(FLS) => self.ifl.into(), - Ok(IMSC) => self.int_enabled.into(), - Ok(RIS) => self.int_level.into(), - Ok(MIS) => u64::from(self.int_level & self.int_enabled), + Ok(RSR) => u32::from(self.receive_status_error_clear), + Ok(FR) => u32::from(self.flags), + Ok(FBRD) => self.fbrd, + Ok(ILPR) => self.ilpr, + Ok(IBRD) => self.ibrd, + Ok(LCR_H) => u32::from(self.line_control), + Ok(CR) => u32::from(self.control), + Ok(FLS) => self.ifl, + Ok(IMSC) => self.int_enabled, + Ok(RIS) => self.int_level, + Ok(MIS) => self.int_level & self.int_enabled, Ok(ICR) => { // "The UARTICR Register is the interrupt clear register and is write-only" // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR 0 } - Ok(DMACR) => self.dmacr.into(), - }) + Ok(DMACR) => self.dmacr, + }; + std::ops::ControlFlow::Break(value.into()) } pub fn write(&mut self, offset: hwaddr, value: u64) { @@ -276,7 +274,6 @@ impl PL011State { self.fbrd = value; } Ok(LCR_H) => { - let value = value as u16; let new_val: registers::LineControl = value.into(); // Reset the FIFO state on FIFO enable or disable if bool::from(self.line_control.fifos_enabled()) @@ -303,7 +300,6 @@ impl PL011State { } Ok(CR) => { // ??? Need to implement the enable bit. - let value = value as u16; self.control = value.into(); self.loopback_mdmctrl(); } diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 463ae60543..0747e130ca 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -131,12 +131,6 @@ impl core::convert::TryFrom for RegisterOffset { pub mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. - //! - //! All PL011 registers are essentially 32-bit wide, but are typed here as - //! bitmaps with only the necessary width. That is, if a struct bitmap - //! in this module is for example 16 bits long, it should be conceived - //! as a 32-bit register where the unmentioned higher bits are always - //! unused thus treated as zero when read or written. use bilge::prelude::*; /// Receive Status Register / Data Register common error bits @@ -234,10 +228,11 @@ pub mod registers { /// # Source /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, /// UARTRSR/UARTECR - #[bitsize(8)] + #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct ReceiveStatusErrorClear { pub errors: Errors, + _reserved_unpredictable: u24, } impl ReceiveStatusErrorClear { @@ -257,7 +252,7 @@ pub mod registers { } } - #[bitsize(16)] + #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] /// Flag Register, `UARTFR` #[doc(alias = "UARTFR")] @@ -309,7 +304,7 @@ pub mod registers { pub transmit_fifo_empty: bool, /// `RI`, is `true` when `nUARTRI` is `LOW`. pub ring_indicator: bool, - _reserved_zero_no_modify: u7, + _reserved_zero_no_modify: u23, } impl Flags { @@ -328,7 +323,7 @@ pub mod registers { } } - #[bitsize(16)] + #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] /// Line Control Register, `UARTLCR_H` #[doc(alias = "UARTLCR_H")] @@ -382,8 +377,8 @@ pub mod registers { /// the PEN bit disables parity checking and generation. See Table 3-11 /// on page 3-14 for the parity truth table. pub sticky_parity: bool, - /// 15:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u8, + /// 31:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u24, } impl LineControl { @@ -454,7 +449,7 @@ pub mod registers { /// /// # Source /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 - #[bitsize(16)] + #[bitsize(32)] #[doc(alias = "UARTCR")] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct Control { @@ -532,6 +527,8 @@ pub mod registers { /// CTS hardware flow control is enabled. Data is only transmitted when /// the `nUARTCTS` signal is asserted. pub cts_hardware_flow_control_enable: bool, + /// 31:16 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify2: u16, } impl Control { From 6b4f7b0705be31c7df6ea01c81a42a42950959a9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Dec 2024 12:53:22 +0100 Subject: [PATCH 0502/2892] rust: pl011: fix migration stream The Rust vmstate macros lack the type-safety of their C equivalents (so safe, much abstraction), and therefore they were predictably wrong. The registers have already been changed to 32-bits in the previous patch, but read_pos/read_count/read_trigger also have to be u32 instead of usize. The easiest way to do so is to let the FIFO use u32 indices instead of usize. My plan for making VMStateField typesafe is to have a trait to retrieve a basic VMStateField; for example something like vmstate_uint32 would become an implementation of the VMState trait on u32. Then you'd write something like "vmstate_of!(Type, field).with_version_id(2)". That is, vmstate_of retrieves the basic VMStateField and fills in the offset, and then more changes can be applied on top. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 38 ++++++++++++++++++++++---- rust/hw/char/pl011/src/device_class.rs | 10 +++---- rust/qemu-api/src/vmstate.rs | 22 --------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 090e5d6450..4d620b442e 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -31,7 +31,7 @@ const IBRD_MASK: u32 = 0xffff; const FBRD_MASK: u32 = 0x3f; /// QEMU sourced constant. -pub const PL011_FIFO_DEPTH: usize = 16_usize; +pub const PL011_FIFO_DEPTH: u32 = 16; #[derive(Clone, Copy, Debug)] enum DeviceId { @@ -56,6 +56,32 @@ impl DeviceId { const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]; } +// FIFOs use 32-bit indices instead of usize, for compatibility with +// the migration stream produced by the C version of this device. +#[repr(transparent)] +#[derive(Debug, Default)] +pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); + +impl Fifo { + const fn len(&self) -> u32 { + self.0.len() as u32 + } +} + +impl std::ops::IndexMut for Fifo { + fn index_mut(&mut self, idx: u32) -> &mut Self::Output { + &mut self.0[idx as usize] + } +} + +impl std::ops::Index for Fifo { + type Output = registers::Data; + + fn index(&self, idx: u32) -> &Self::Output { + &self.0[idx as usize] + } +} + #[repr(C)] #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] /// PL011 Device Model in QEMU @@ -73,14 +99,14 @@ pub struct PL011State { pub dmacr: u32, pub int_enabled: u32, pub int_level: u32, - pub read_fifo: [registers::Data; PL011_FIFO_DEPTH], + pub read_fifo: Fifo, pub ilpr: u32, pub ibrd: u32, pub fbrd: u32, pub ifl: u32, - pub read_pos: usize, - pub read_count: usize, - pub read_trigger: usize, + pub read_pos: u32, + pub read_count: u32, + pub read_trigger: u32, #[doc(alias = "chr")] pub char_backend: CharBackend, /// QEMU interrupts @@ -480,7 +506,7 @@ impl PL011State { } #[inline] - pub fn fifo_depth(&self) -> usize { + pub fn fifo_depth(&self) -> u32 { // Note: FIFO depth is expected to be power-of-2 if self.fifo_enabled() { return PL011_FIFO_DEPTH; diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 975c3d42be..7f3ca89507 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,8 +6,8 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, - vmstate_uint32, vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, + bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_subsections, vmstate_uint32, + vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, }; use crate::device::{PL011State, PL011_FIFO_DEPTH}; @@ -64,9 +64,9 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { vmstate_uint32!(ibrd, PL011State), vmstate_uint32!(fbrd, PL011State), vmstate_uint32!(ifl, PL011State), - vmstate_int32!(read_pos, PL011State), - vmstate_int32!(read_count, PL011State), - vmstate_int32!(read_trigger, PL011State), + vmstate_uint32!(read_pos, PL011State), + vmstate_uint32!(read_count, PL011State), + vmstate_uint32!(read_trigger, PL011State), }, subsections: vmstate_subsections! { VMSTATE_PL011_CLOCK diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 25c68b703e..63c897abcd 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -106,28 +106,6 @@ macro_rules! vmstate_uint32 { }}; } -#[doc(alias = "VMSTATE_INT32_V")] -#[macro_export] -macro_rules! vmstate_int32_v { - ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ - $crate::vmstate_single!( - $field_name, - $struct_name, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32), - ::core::mem::size_of::() - ) - }}; -} - -#[doc(alias = "VMSTATE_INT32")] -#[macro_export] -macro_rules! vmstate_int32 { - ($field_name:ident, $struct_name:ty) => {{ - $crate::vmstate_int32_v!($field_name, $struct_name, 0) - }}; -} - #[doc(alias = "VMSTATE_ARRAY")] #[macro_export] macro_rules! vmstate_array { From bf9987c06eb8274c2503174b944b8fbe94cc24d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2024 21:49:34 +0100 Subject: [PATCH 0503/2892] rust: pl011: simplify handling of the FIFO enabled bit in LCR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use ==/!= instead of going through bool and xor. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ++---- rust/hw/char/pl011/src/lib.rs | 6 ------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 4d620b442e..18cc122951 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -302,9 +302,7 @@ impl PL011State { Ok(LCR_H) => { let new_val: registers::LineControl = value.into(); // Reset the FIFO state on FIFO enable or disable - if bool::from(self.line_control.fifos_enabled()) - ^ bool::from(new_val.fifos_enabled()) - { + if self.line_control.fifos_enabled() != new_val.fifos_enabled() { self.reset_rx_fifo(); self.reset_tx_fifo(); } @@ -497,7 +495,7 @@ impl PL011State { #[inline] pub fn fifo_enabled(&self) -> bool { - matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO) + self.line_control.fifos_enabled() == registers::Mode::FIFO } #[inline] diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 0747e130ca..69064d6929 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -419,12 +419,6 @@ pub mod registers { FIFO = 1, } - impl From for bool { - fn from(val: Mode) -> Self { - matches!(val, Mode::FIFO) - } - } - #[bitsize(2)] #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] /// `WLEN` Word length, field of [Line Control register](LineControl). From e5d28bf2b3064e986bfd7e0f0c9653017e20f3d8 Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Thu, 14 Nov 2024 14:56:17 +0800 Subject: [PATCH 0504/2892] hw/riscv/riscv-iommu.c: Correct the validness check of iova From RISCV IOMMU spec section 2.1.3: When SXL is 1, the following rules apply: - If the first-stage is not Bare, then a page fault corresponding to the original access type occurs if the IOVA has bits beyond bit 31 set to 1. - If the second-stage is not Bare, then a guest page fault corresponding to the original access type occurs if the incoming GPA has bits beyond bit 33 set to 1. From RISCV IOMMU spec section 2.3 step 17: Use the process specified in Section "Two-Stage Address Translation" of the RISC-V Privileged specification to determine the GPA accessed by the transaction. From RISCV IOMMU spec section 2.3 step 19: Use the second-stage address translation process specified in Section "Two-Stage Address Translation" of the RISC-V Privileged specification to translate the GPA A to determine the SPA accessed by the transaction. This commit adds the iova check with the following rules: - For Sv32, Sv32x4, Sv39x4, Sv48x4 and Sv57x4, the iova must be zero extended. - For Sv39, Sv48 and Sv57, the iova must be signed extended with most significant bit. Signed-off-by: Jason Chien Reviewed-by: Daniel Henrique Barboza Message-ID: <20241114065617.25133-1-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 07fed36986..b6b9477129 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -392,9 +392,26 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, /* Address range check before first level lookup */ if (!sc[pass].step) { - const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1; - if ((addr & va_mask) != addr) { - return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED; + const uint64_t va_len = va_skip + va_bits; + const uint64_t va_mask = (1ULL << va_len) - 1; + + if (pass == S_STAGE && va_len > 32) { + target_ulong mask, masked_msbs; + + mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1; + masked_msbs = (addr >> (va_len - 1)) & mask; + + if (masked_msbs != 0 && masked_msbs != mask) { + return (iotlb->perm & IOMMU_WO) ? + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; + } + } else { + if ((addr & va_mask) != addr) { + return (iotlb->perm & IOMMU_WO) ? + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; + } } } From 0d0141fadc9063e527865ee420b2baf34e306093 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Tue, 29 Oct 2024 16:53:47 +0800 Subject: [PATCH 0505/2892] hw/intc/riscv_aplic: Fix APLIC in_clrip and clripnum write emulation In the section "4.7 Precise effects on interrupt-pending bits" of the RISC-V AIA specification defines that: "If the source mode is Level1 or Level0 and the interrupt domain is configured in MSI delivery mode (domaincfg.DM = 1): The pending bit is cleared whenever the rectified input value is low, when the interrupt is forwarded by MSI, or by a relevant write to an in_clrip register or to clripnum." Update the riscv_aplic_set_pending() to match the spec. Fixes: bf31cf06eb ("hw/intc/riscv_aplic: Fix setipnum_le write emulation for APLIC MSI-mode") Signed-off-by: Yong-Xuan Wang Acked-by: Alistair Francis Message-ID: <20241029085349.30412-1-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 353eec8136..3edab64b97 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -248,9 +248,12 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic, if ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) || (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) { - if (!aplic->msimode || (aplic->msimode && !pending)) { + if (!aplic->msimode) { return; } + if (aplic->msimode && !pending) { + goto noskip_write_pending; + } if ((aplic->state[irq] & APLIC_ISTATE_INPUT) && (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) { return; @@ -261,6 +264,7 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic, } } +noskip_write_pending: riscv_aplic_set_pending_raw(aplic, irq, pending); } From 4876e6f7b51cf7dcbfaa43ae323e51ce9ebfcf79 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 6 Nov 2024 10:34:01 -0300 Subject: [PATCH 0506/2892] hw/riscv/riscv-iommu.c: add riscv_iommu_instance_init() Move all the static initializion of the device to an init() function, leaving only the dynamic initialization to be done during realize. With this change s->cap is initialized with RISCV_IOMMU_CAP_DBG during init(), and realize() will increment s->cap with the extra caps. This will allow callers to add IOMMU capabilities before the realization. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241106133407.604587-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 71 +++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index b6b9477129..c461ebbd87 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2130,11 +2130,48 @@ static const MemoryRegionOps riscv_iommu_trap_ops = { } }; +static void riscv_iommu_instance_init(Object *obj) +{ + RISCVIOMMUState *s = RISCV_IOMMU(obj); + + /* Enable translation debug interface */ + s->cap = RISCV_IOMMU_CAP_DBG; + + /* Report QEMU target physical address space limits */ + s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS, + TARGET_PHYS_ADDR_SPACE_BITS); + + /* TODO: method to report supported PID bits */ + s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */ + s->cap |= RISCV_IOMMU_CAP_PD8; + + /* register storage */ + s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + + /* Mark all registers read-only */ + memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE); + + /* Device translation context cache */ + s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, + riscv_iommu_ctx_equal, + g_free, NULL); + + s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, + riscv_iommu_iot_equal, + g_free, NULL); + + s->iommus.le_next = NULL; + s->iommus.le_prev = NULL; + QLIST_INIT(&s->spaces); +} + static void riscv_iommu_realize(DeviceState *dev, Error **errp) { RISCVIOMMUState *s = RISCV_IOMMU(dev); - s->cap = s->version & RISCV_IOMMU_CAP_VERSION; + s->cap |= s->version & RISCV_IOMMU_CAP_VERSION; if (s->enable_msi) { s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF; } @@ -2149,29 +2186,11 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 | RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; } - /* Enable translation debug interface */ - s->cap |= RISCV_IOMMU_CAP_DBG; - - /* Report QEMU target physical address space limits */ - s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS, - TARGET_PHYS_ADDR_SPACE_BITS); - - /* TODO: method to report supported PID bits */ - s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */ - s->cap |= RISCV_IOMMU_CAP_PD8; /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */ s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ? RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE); - /* register storage */ - s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); - s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); - s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); - - /* Mark all registers read-only */ - memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE); - /* * Register complete MMIO space, including MSI/PBA registers. * Note, PCIDevice implementation will add overlapping MR for MSI/PBA, @@ -2229,19 +2248,6 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s, "riscv-iommu-trap", ~0ULL); address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as"); - - /* Device translation context cache */ - s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, - riscv_iommu_ctx_equal, - g_free, NULL); - - s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, - riscv_iommu_iot_equal, - g_free, NULL); - - s->iommus.le_next = NULL; - s->iommus.le_prev = NULL; - QLIST_INIT(&s->spaces); } static void riscv_iommu_unrealize(DeviceState *dev) @@ -2283,6 +2289,7 @@ static const TypeInfo riscv_iommu_info = { .name = TYPE_RISCV_IOMMU, .parent = TYPE_DEVICE, .instance_size = sizeof(RISCVIOMMUState), + .instance_init = riscv_iommu_instance_init, .class_init = riscv_iommu_class_init, }; From d13346d105c396e0d95851b58f52cac43ad55952 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 6 Nov 2024 10:34:02 -0300 Subject: [PATCH 0507/2892] hw/riscv/riscv-iommu: parametrize CAP.IGS Interrupt Generation Support (IGS) is a capability that is tied to the interrupt deliver mechanism, not with the core IOMMU emulation. We should allow device implementations to set IGS as they wish. A new helper is added to make it easier for device impls to set IGS. Use it in our existing IOMMU device (riscv-iommu-pci) to set RISCV_IOMMU_CAPS_IGS_MSI. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241106133407.604587-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-bits.h | 6 ++++++ hw/riscv/riscv-iommu-pci.c | 1 + hw/riscv/riscv-iommu.c | 5 +++++ hw/riscv/riscv-iommu.h | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index 6359ae0353..485f36b9c9 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -88,6 +88,12 @@ struct riscv_iommu_pq_record { #define RISCV_IOMMU_CAP_PD17 BIT_ULL(39) #define RISCV_IOMMU_CAP_PD20 BIT_ULL(40) +enum riscv_iommu_igs_modes { + RISCV_IOMMU_CAP_IGS_MSI = 0, + RISCV_IOMMU_CAP_IGS_WSI, + RISCV_IOMMU_CAP_IGS_BOTH +}; + /* 5.4 Features control register (32bits) */ #define RISCV_IOMMU_REG_FCTL 0x0008 #define RISCV_IOMMU_FCTL_BE BIT(0) diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index a695314bbe..a95d0f74c8 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -155,6 +155,7 @@ static void riscv_iommu_pci_init(Object *obj) qdev_alias_all_properties(DEVICE(iommu), obj); iommu->icvec_avail_vectors = RISCV_IOMMU_PCI_ICVEC_VECTORS; + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_MSI); } static const Property riscv_iommu_pci_properties[] = { diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index c461ebbd87..24b879822b 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2130,6 +2130,11 @@ static const MemoryRegionOps riscv_iommu_trap_ops = { } }; +void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode) +{ + s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode); +} + static void riscv_iommu_instance_init(Object *obj) { RISCVIOMMUState *s = RISCV_IOMMU(obj); diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index da3f03440c..f9f2827808 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -21,6 +21,9 @@ #include "qom/object.h" #include "hw/riscv/iommu.h" +#include "hw/riscv/riscv-iommu-bits.h" + +typedef enum riscv_iommu_igs_modes riscv_iommu_igs_mode; struct RISCVIOMMUState { /*< private >*/ @@ -85,6 +88,7 @@ struct RISCVIOMMUState { void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, Error **errp); +void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); /* private helpers */ From 5b128435dcf1e6545b544e3e402470ecf5b45ac7 Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Wed, 6 Nov 2024 10:34:03 -0300 Subject: [PATCH 0508/2892] hw/riscv: add riscv-iommu-sys platform device This device models the RISC-V IOMMU as a sysbus device. The same design decisions taken in the riscv-iommu-pci device were kept, namely the existence of 4 vectors are available for each interrupt cause. The WSIs are emitted using the input of the s->notify() callback as a index to an IRQ list. The IRQ list starts at 'base_irq' and goes until base_irq + 3. This means that boards must have 4 contiguous IRQ lines available, starting from 'base_irq'. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241106133407.604587-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/meson.build | 2 +- hw/riscv/riscv-iommu-sys.c | 128 +++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu.c | 3 +- include/hw/riscv/iommu.h | 4 ++ 4 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 hw/riscv/riscv-iommu-sys.c diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index adbef8a9b2..3be13d7774 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -10,6 +10,6 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) -riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c')) +riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c new file mode 100644 index 0000000000..4b82046ce9 --- /dev/null +++ b/hw/riscv/riscv-iommu-sys.c @@ -0,0 +1,128 @@ +/* + * QEMU emulation of an RISC-V IOMMU Platform Device + * + * Copyright (C) 2022-2023 Rivos Inc. + * + * 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 that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qom/object.h" + +#include "riscv-iommu.h" + +#define RISCV_IOMMU_SYSDEV_ICVEC_VECTORS 0x3333 + +/* RISC-V IOMMU System Platform Device Emulation */ + +struct RISCVIOMMUStateSys { + SysBusDevice parent; + uint64_t addr; + uint32_t base_irq; + DeviceState *irqchip; + RISCVIOMMUState iommu; + qemu_irq irqs[RISCV_IOMMU_INTR_COUNT]; +}; + +static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu, + unsigned vector) +{ + RISCVIOMMUStateSys *s = container_of(iommu, RISCVIOMMUStateSys, iommu); + uint32_t fctl = riscv_iommu_reg_get32(iommu, RISCV_IOMMU_REG_FCTL); + + /* We do not support MSIs yet */ + if (!(fctl & RISCV_IOMMU_FCTL_WSI)) { + return; + } + + qemu_irq_pulse(s->irqs[vector]); +} + +static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) +{ + RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(dev); + SysBusDevice *sysdev = SYS_BUS_DEVICE(s); + PCIBus *pci_bus; + qemu_irq irq; + + qdev_realize(DEVICE(&s->iommu), NULL, errp); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iommu.regs_mr); + if (s->addr) { + sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, s->addr); + } + + pci_bus = (PCIBus *) object_resolve_path_type("", TYPE_PCI_BUS, NULL); + if (pci_bus) { + riscv_iommu_pci_setup_iommu(&s->iommu, pci_bus, errp); + } + + s->iommu.notify = riscv_iommu_sysdev_notify; + + /* 4 IRQs are defined starting from s->base_irq */ + for (int i = 0; i < RISCV_IOMMU_INTR_COUNT; i++) { + sysbus_init_irq(sysdev, &s->irqs[i]); + irq = qdev_get_gpio_in(s->irqchip, s->base_irq + i); + sysbus_connect_irq(sysdev, i, irq); + } +} + +static void riscv_iommu_sys_init(Object *obj) +{ + RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(obj); + RISCVIOMMUState *iommu = &s->iommu; + + object_initialize_child(obj, "iommu", iommu, TYPE_RISCV_IOMMU); + qdev_alias_all_properties(DEVICE(iommu), obj); + + iommu->icvec_avail_vectors = RISCV_IOMMU_SYSDEV_ICVEC_VECTORS; + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_WSI); +} + +static Property riscv_iommu_sys_properties[] = { + DEFINE_PROP_UINT64("addr", RISCVIOMMUStateSys, addr, 0), + DEFINE_PROP_UINT32("base-irq", RISCVIOMMUStateSys, base_irq, 0), + DEFINE_PROP_LINK("irqchip", RISCVIOMMUStateSys, irqchip, + TYPE_DEVICE, DeviceState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void riscv_iommu_sys_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = riscv_iommu_sys_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, riscv_iommu_sys_properties); +} + +static const TypeInfo riscv_iommu_sys = { + .name = TYPE_RISCV_IOMMU_SYS, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = riscv_iommu_sys_class_init, + .instance_init = riscv_iommu_sys_init, + .instance_size = sizeof(RISCVIOMMUStateSys), +}; + +static void riscv_iommu_register_sys(void) +{ + type_register_static(&riscv_iommu_sys); +} + +type_init(riscv_iommu_register_sys) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 24b879822b..9137217cb7 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -94,10 +94,9 @@ static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) { - const uint32_t fctl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FCTL); uint32_t ipsr, icvec, vector; - if (fctl & RISCV_IOMMU_FCTL_WSI || !s->notify) { + if (!s->notify) { return; } diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index 80769a1400..fc20808553 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -33,4 +33,8 @@ typedef struct RISCVIOMMUSpace RISCVIOMMUSpace; OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI) typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; +#define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device" +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) +typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; + #endif From 2c12de146071beca8021d71cf2ac49bde36b2c8e Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Wed, 6 Nov 2024 10:34:04 -0300 Subject: [PATCH 0509/2892] hw/riscv/virt: Add IOMMU as platform device if the option is set Add a new machine option called 'iommu-sys' that enables a riscv-iommu-sys platform device for the 'virt' machine. The option is default 'off'. The device will use IRQs 36 to 39. We will not support both riscv-iommu-sys and riscv-iommu-pci devices in the same board in this first implementation. If a riscv-iommu-pci device is added in the command line we will disable the riscv-iommu-sys device. Signed-off-by: Sunil V L Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241106133407.604587-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 104 ++++++++++++++++++++++++++++++++++++++- include/hw/riscv/iommu.h | 2 + include/hw/riscv/virt.h | 6 ++- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2feb851f15..c5ada635f1 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -33,6 +33,7 @@ #include "target/riscv/pmu.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/iommu.h" +#include "hw/riscv/riscv-iommu-bits.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" @@ -76,6 +77,7 @@ static const MemMapEntry virt_memmap[] = { [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 }, [VIRT_PCIE_PIO] = { 0x3000000, 0x10000 }, + [VIRT_IOMMU_SYS] = { 0x3010000, 0x1000 }, [VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 }, [VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) }, [VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) }, @@ -853,7 +855,8 @@ static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, uint32_t irq_pcie_phandle, - uint32_t msi_pcie_phandle) + uint32_t msi_pcie_phandle, + uint32_t iommu_sys_phandle) { g_autofree char *name = NULL; MachineState *ms = MACHINE(s); @@ -887,6 +890,12 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size); + if (virt_is_iommu_sys_enabled(s)) { + qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map", + 0, iommu_sys_phandle, 0, 0, 0, + iommu_sys_phandle, 0, 0xffff); + } + create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle); } @@ -1033,6 +1042,44 @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf) bdf + 1, iommu_phandle, bdf + 1, 0xffff - bdf); } +static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, + uint32_t *iommu_sys_phandle) +{ + const char comp[] = "riscv,iommu"; + void *fdt = MACHINE(s)->fdt; + uint32_t iommu_phandle; + g_autofree char *iommu_node = NULL; + hwaddr addr = s->memmap[VIRT_IOMMU_SYS].base; + hwaddr size = s->memmap[VIRT_IOMMU_SYS].size; + uint32_t iommu_irq_map[RISCV_IOMMU_INTR_COUNT] = { + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_CQ, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_FQ, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PM, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PQ, + }; + + iommu_node = g_strdup_printf("/soc/iommu@%x", + (unsigned int) s->memmap[VIRT_IOMMU_SYS].base); + iommu_phandle = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_add_subnode(fdt, iommu_node); + + qemu_fdt_setprop(fdt, iommu_node, "compatible", comp, sizeof(comp)); + qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle); + + qemu_fdt_setprop_cells(fdt, iommu_node, "reg", + addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip); + + qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts", + iommu_irq_map[0], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[1], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW); + + *iommu_sys_phandle = iommu_phandle; +} + static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) { const char comp[] = "riscv,pci-iommu"; @@ -1061,6 +1108,7 @@ static void finalize_fdt(RISCVVirtState *s) { uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; + uint32_t iommu_sys_phandle = 1; create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle, @@ -1068,7 +1116,11 @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); - create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle); + if (virt_is_iommu_sys_enabled(s)) { + create_fdt_iommu_sys(s, irq_mmio_phandle, &iommu_sys_phandle); + } + create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle, + iommu_sys_phandle); create_fdt_reset(s, virt_memmap, &phandle); @@ -1648,6 +1700,22 @@ static void virt_machine_init(MachineState *machine) create_fdt(s, memmap); } + if (virt_is_iommu_sys_enabled(s)) { + DeviceState *iommu_sys = qdev_new(TYPE_RISCV_IOMMU_SYS); + + object_property_set_uint(OBJECT(iommu_sys), "addr", + s->memmap[VIRT_IOMMU_SYS].base, + &error_fatal); + object_property_set_uint(OBJECT(iommu_sys), "base-irq", + IOMMU_SYS_IRQ, + &error_fatal); + object_property_set_link(OBJECT(iommu_sys), "irqchip", + OBJECT(mmio_irqchip), + &error_fatal); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(iommu_sys), &error_fatal); + } + s->machine_done.notify = virt_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); } @@ -1661,6 +1729,7 @@ static void virt_machine_instance_init(Object *obj) s->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); s->acpi = ON_OFF_AUTO_AUTO; + s->iommu_sys = ON_OFF_AUTO_AUTO; } static char *virt_get_aia_guests(Object *obj, Error **errp) @@ -1733,6 +1802,28 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) s->have_aclint = value; } +bool virt_is_iommu_sys_enabled(RISCVVirtState *s) +{ + return s->iommu_sys == ON_OFF_AUTO_ON; +} + +static void virt_get_iommu_sys(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + OnOffAuto iommu_sys = s->iommu_sys; + + visit_type_OnOffAuto(v, name, &iommu_sys, errp); +} + +static void virt_set_iommu_sys(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &s->iommu_sys, errp); +} + bool virt_is_acpi_enabled(RISCVVirtState *s) { return s->acpi != ON_OFF_AUTO_OFF; @@ -1759,10 +1850,12 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); + RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); if (device_is_dynamic_sysbus(mc, dev) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) { + s->iommu_sys = ON_OFF_AUTO_OFF; return HOTPLUG_HANDLER(machine); } @@ -1789,6 +1882,7 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) { create_fdt_iommu(s, pci_get_bdf(PCI_DEVICE(dev))); + s->iommu_sys = ON_OFF_AUTO_OFF; } } @@ -1851,6 +1945,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, "acpi", "Enable ACPI"); + + object_class_property_add(oc, "iommu-sys", "OnOffAuto", + virt_get_iommu_sys, virt_set_iommu_sys, + NULL, NULL); + object_class_property_set_description(oc, "iommu-sys", + "Enable IOMMU platform device"); } static const TypeInfo virt_machine_typeinfo = { diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index fc20808553..8a8acfc3f0 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -37,4 +37,6 @@ typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; +#define FDT_IRQ_TYPE_EDGE_LOW 1 + #endif diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index c0dc41ff9a..48a14bea2e 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -62,6 +62,7 @@ struct RISCVVirtState { OnOffAuto acpi; const MemMapEntry *memmap; struct GPEXHost *gpex_host; + OnOffAuto iommu_sys; }; enum { @@ -84,7 +85,8 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PLATFORM_BUS, - VIRT_PCIE_ECAM + VIRT_PCIE_ECAM, + VIRT_IOMMU_SYS, }; enum { @@ -93,6 +95,7 @@ enum { VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ + IOMMU_SYS_IRQ = 0x24, /* 36-39 */ VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 95 */ }; @@ -129,6 +132,7 @@ enum { 1 + FDT_APLIC_INT_CELLS) bool virt_is_acpi_enabled(RISCVVirtState *s); +bool virt_is_iommu_sys_enabled(RISCVVirtState *s); void virt_acpi_setup(RISCVVirtState *vms); uint32_t imsic_num_bits(uint32_t count); From 01c1caa9d1b4a1938e0baba1a64c26636a3aff9f Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 6 Nov 2024 10:34:05 -0300 Subject: [PATCH 0510/2892] hw/riscv/virt.c, riscv-iommu-sys.c: add MSIx support MSIx support is added in the RISC-V IOMMU platform device by including the required MSIx facilities to alow software to properly setup the MSIx subsystem. We took inspiration of what is being done in the riscv-iommu-pci device, mainly msix_init() and msix_notify(), while keeping in mind that riscv-iommu-sys isn't a true PCI device and we don't need to copy/paste all the contents of these MSIx functions. Two extra MSI MemoryRegions were added: 'msix-table' and 'msix-pba'. They are used to manage r/w of the MSI table and Pending Bit Array (PBA) respectively. Both are subregions of the main IOMMU memory region, iommu->regs_mr, initialized during riscv_iommu_realize(), and each one has their own handlers for MSIx reads and writes. This is the expected memory map when using this device in the 'virt' machine: 0000000003010000-0000000003010fff (prio 0, i/o): riscv-iommu-regs 0000000003010300-000000000301034f (prio 0, i/o): msix-table 0000000003010400-0000000003010407 (prio 0, i/o): msix-pba We're now able to set IGS to RISCV_IOMMU_CAP_IGS_BOTH, and userspace is free to decide which interrupt model to use. Enabling MSIx support for this device in the 'virt' machine requires adding 'msi-parent' in the iommu-sys DT. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20241106133407.604587-6-dbarboza@ventanamicro.com> [ Changes by AF: - Used PRIx64 in trace ] Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-sys.c | 116 +++++++++++++++++++++++++++++++++++-- hw/riscv/trace-events | 2 + hw/riscv/virt.c | 6 +- 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index 4b82046ce9..a0ef67a20b 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -26,11 +26,15 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "qom/object.h" +#include "exec/exec-all.h" +#include "trace.h" #include "riscv-iommu.h" #define RISCV_IOMMU_SYSDEV_ICVEC_VECTORS 0x3333 +#define RISCV_IOMMU_PCI_MSIX_VECTORS 5 + /* RISC-V IOMMU System Platform Device Emulation */ struct RISCVIOMMUStateSys { @@ -39,21 +43,123 @@ struct RISCVIOMMUStateSys { uint32_t base_irq; DeviceState *irqchip; RISCVIOMMUState iommu; + + /* Wired int support */ qemu_irq irqs[RISCV_IOMMU_INTR_COUNT]; + + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + uint8_t *msix_table; + uint8_t *msix_pba; }; +static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE); + return pci_get_long(s->msix_table + addr); +} + +static void msix_table_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE); + pci_set_long(s->msix_table + addr, val); +} + +static const MemoryRegionOps msix_table_mmio_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .max_access_size = 4, + }, +}; + +static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + return pci_get_long(s->msix_pba + addr); +} + +static void msix_pba_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + +static const MemoryRegionOps msix_pba_mmio_ops = { + .read = msix_pba_mmio_read, + .write = msix_pba_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .max_access_size = 4, + }, +}; + +static void riscv_iommu_sysdev_init_msi(RISCVIOMMUStateSys *s, + uint32_t n_vectors) +{ + RISCVIOMMUState *iommu = &s->iommu; + uint32_t table_size = table_size = n_vectors * PCI_MSIX_ENTRY_SIZE; + uint32_t table_offset = RISCV_IOMMU_REG_MSI_CONFIG; + uint32_t pba_size = QEMU_ALIGN_UP(n_vectors, 64) / 8; + uint32_t pba_offset = RISCV_IOMMU_REG_MSI_CONFIG + 256; + + s->msix_table = g_malloc0(table_size); + s->msix_pba = g_malloc0(pba_size); + + memory_region_init_io(&s->msix_table_mmio, OBJECT(s), &msix_table_mmio_ops, + s, "msix-table", table_size); + memory_region_add_subregion(&iommu->regs_mr, table_offset, + &s->msix_table_mmio); + + memory_region_init_io(&s->msix_pba_mmio, OBJECT(s), &msix_pba_mmio_ops, s, + "msix-pba", pba_size); + memory_region_add_subregion(&iommu->regs_mr, pba_offset, + &s->msix_pba_mmio); +} + +static void riscv_iommu_sysdev_send_MSI(RISCVIOMMUStateSys *s, + uint32_t vector) +{ + uint8_t *table_entry = s->msix_table + vector * PCI_MSIX_ENTRY_SIZE; + uint64_t msi_addr = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + uint32_t msi_data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); + MemTxResult result; + + address_space_stl_le(&address_space_memory, msi_addr, + msi_data, MEMTXATTRS_UNSPECIFIED, &result); + trace_riscv_iommu_sys_msi_sent(vector, msi_addr, msi_data, result); +} + static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu, unsigned vector) { RISCVIOMMUStateSys *s = container_of(iommu, RISCVIOMMUStateSys, iommu); uint32_t fctl = riscv_iommu_reg_get32(iommu, RISCV_IOMMU_REG_FCTL); - /* We do not support MSIs yet */ - if (!(fctl & RISCV_IOMMU_FCTL_WSI)) { + if (fctl & RISCV_IOMMU_FCTL_WSI) { + qemu_irq_pulse(s->irqs[vector]); + trace_riscv_iommu_sys_irq_sent(vector); return; } - qemu_irq_pulse(s->irqs[vector]); + riscv_iommu_sysdev_send_MSI(s, vector); } static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) @@ -82,6 +188,8 @@ static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) irq = qdev_get_gpio_in(s->irqchip, s->base_irq + i); sysbus_connect_irq(sysdev, i, irq); } + + riscv_iommu_sysdev_init_msi(s, RISCV_IOMMU_PCI_MSIX_VECTORS); } static void riscv_iommu_sys_init(Object *obj) @@ -93,7 +201,7 @@ static void riscv_iommu_sys_init(Object *obj) qdev_alias_all_properties(DEVICE(iommu), obj); iommu->icvec_avail_vectors = RISCV_IOMMU_SYSDEV_ICVEC_VECTORS; - riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_WSI); + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_BOTH); } static Property riscv_iommu_sys_properties[] = { diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events index 0527c56c91..525fd5b730 100644 --- a/hw/riscv/trace-events +++ b/hw/riscv/trace-events @@ -15,3 +15,5 @@ riscv_iommu_icvec_write(uint32_t orig, uint32_t actual) "ICVEC write: incoming 0 riscv_iommu_ats(const char *id, unsigned b, unsigned d, unsigned f, uint64_t iova) "%s: translate request %04x:%02x.%u iova: 0x%"PRIx64 riscv_iommu_ats_inval(const char *id) "%s: dev-iotlb invalidate" riscv_iommu_ats_prgr(const char *id) "%s: dev-iotlb page request group response" +riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u" +riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u" diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c5ada635f1..5d058511be 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1043,6 +1043,7 @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf) } static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, + uint32_t msi_phandle, uint32_t *iommu_sys_phandle) { const char comp[] = "riscv,iommu"; @@ -1077,6 +1078,8 @@ static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW, iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW); + qemu_fdt_setprop_cell(fdt, iommu_node, "msi-parent", msi_phandle); + *iommu_sys_phandle = iommu_phandle; } @@ -1117,7 +1120,8 @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); if (virt_is_iommu_sys_enabled(s)) { - create_fdt_iommu_sys(s, irq_mmio_phandle, &iommu_sys_phandle); + create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle, + &iommu_sys_phandle); } create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle, iommu_sys_phandle); From 9afd26715ef4f887f5eaf2ecfe365a7837f2e500 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 6 Nov 2024 10:34:06 -0300 Subject: [PATCH 0511/2892] hw/riscv/riscv-iommu: implement reset protocol Add a riscv_iommu_reset() helper in the base emulation code that implements the expected reset behavior as defined by the riscv-iommu spec. Devices can then use this helper in their own reset callbacks. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20241106133407.604587-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-pci.c | 20 ++++++++++++++++++++ hw/riscv/riscv-iommu-sys.c | 20 ++++++++++++++++++++ hw/riscv/riscv-iommu.c | 35 +++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu.h | 1 + hw/riscv/trace-events | 2 ++ include/hw/riscv/iommu.h | 6 ++++-- 6 files changed, 82 insertions(+), 2 deletions(-) diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index a95d0f74c8..2c1ae72461 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -31,6 +31,7 @@ #include "cpu_bits.h" #include "riscv-iommu.h" #include "riscv-iommu-bits.h" +#include "trace.h" /* RISC-V IOMMU PCI Device Emulation */ #define RISCV_PCI_CLASS_SYSTEM_IOMMU 0x0806 @@ -66,6 +67,12 @@ typedef struct RISCVIOMMUStatePci { RISCVIOMMUState iommu; /* common IOMMU state */ } RISCVIOMMUStatePci; +struct RISCVIOMMUPciClass { + /*< public >*/ + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + /* interrupt delivery callback */ static void riscv_iommu_pci_notify(RISCVIOMMUState *iommu, unsigned vector) { @@ -167,10 +174,23 @@ static const Property riscv_iommu_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void riscv_iommu_pci_reset_hold(Object *obj, ResetType type) +{ + RISCVIOMMUStatePci *pci = RISCV_IOMMU_PCI(obj); + RISCVIOMMUState *iommu = &pci->iommu; + + riscv_iommu_reset(iommu); + + trace_riscv_iommu_pci_reset_hold(type); +} + static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = riscv_iommu_pci_reset_hold; k->realize = riscv_iommu_pci_realize; k->exit = riscv_iommu_pci_exit; diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index a0ef67a20b..605979a0ac 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -54,6 +54,12 @@ struct RISCVIOMMUStateSys { uint8_t *msix_pba; }; +struct RISCVIOMMUSysClass { + /*< public >*/ + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, unsigned size) { @@ -212,9 +218,23 @@ static Property riscv_iommu_sys_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void riscv_iommu_sys_reset_hold(Object *obj, ResetType type) +{ + RISCVIOMMUStateSys *sys = RISCV_IOMMU_SYS(obj); + RISCVIOMMUState *iommu = &sys->iommu; + + riscv_iommu_reset(iommu); + + trace_riscv_iommu_sys_reset_hold(type); +} + static void riscv_iommu_sys_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = riscv_iommu_sys_reset_hold; + dc->realize = riscv_iommu_sys_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); device_class_set_props(dc, riscv_iommu_sys_properties); diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 9137217cb7..72e607c9eb 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2262,6 +2262,41 @@ static void riscv_iommu_unrealize(DeviceState *dev) g_hash_table_unref(s->ctx_cache); } +void riscv_iommu_reset(RISCVIOMMUState *s) +{ + uint32_t reg_clr; + int ddtp_mode; + + /* + * Clear DDTP while setting DDTP_mode back to user + * initial setting. + */ + ddtp_mode = s->enable_off ? + RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE; + s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode); + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp); + + reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE | + RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr); + + reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE | + RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr); + + reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE | + RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr); + + riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0, + RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); + + riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0); + + g_hash_table_remove_all(s->ctx_cache); + g_hash_table_remove_all(s->iot_cache); +} + static const Property riscv_iommu_properties[] = { DEFINE_PROP_UINT32("version", RISCVIOMMUState, version, RISCV_IOMMU_SPEC_DOT_VER), diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index f9f2827808..9424989df4 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -89,6 +89,7 @@ struct RISCVIOMMUState { void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, Error **errp); void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); +void riscv_iommu_reset(RISCVIOMMUState *s); /* private helpers */ diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events index 525fd5b730..7bcbb03d08 100644 --- a/hw/riscv/trace-events +++ b/hw/riscv/trace-events @@ -17,3 +17,5 @@ riscv_iommu_ats_inval(const char *id) "%s: dev-iotlb invalidate" riscv_iommu_ats_prgr(const char *id) "%s: dev-iotlb page request group response" riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u" riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u" +riscv_iommu_sys_reset_hold(int reset_type) "reset type %d" +riscv_iommu_pci_reset_hold(int reset_type) "reset type %d" diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index 8a8acfc3f0..b03339d75c 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -30,12 +30,14 @@ typedef struct RISCVIOMMUState RISCVIOMMUState; typedef struct RISCVIOMMUSpace RISCVIOMMUSpace; #define TYPE_RISCV_IOMMU_PCI "riscv-iommu-pci" -OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI) +OBJECT_DECLARE_TYPE(RISCVIOMMUStatePci, RISCVIOMMUPciClass, RISCV_IOMMU_PCI) typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; +typedef struct RISCVIOMMUPciClass RISCVIOMMUPciClass; #define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device" -OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) +OBJECT_DECLARE_TYPE(RISCVIOMMUStateSys, RISCVIOMMUSysClass, RISCV_IOMMU_SYS) typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; +typedef struct RISCVIOMMUSysClass RISCVIOMMUSysClass; #define FDT_IRQ_TYPE_EDGE_LOW 1 From 1c187ad5c06a45c083d5d24b2db62af5504880a2 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 6 Nov 2024 10:34:07 -0300 Subject: [PATCH 0512/2892] docs/specs: add riscv-iommu-sys information Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241106133407.604587-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- docs/specs/riscv-iommu.rst | 30 +++++++++++++++++++++++++++--- docs/system/riscv/virt.rst | 10 ++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst index 463f4cffb6..b1538c9ead 100644 --- a/docs/specs/riscv-iommu.rst +++ b/docs/specs/riscv-iommu.rst @@ -6,9 +6,9 @@ RISC-V IOMMU support for RISC-V machines QEMU implements a RISC-V IOMMU emulation based on the RISC-V IOMMU spec version 1.0 `iommu1.0`_. -The emulation includes a PCI reference device, riscv-iommu-pci, that QEMU -RISC-V boards can use. The 'virt' RISC-V machine is compatible with this -device. +The emulation includes a PCI reference device (riscv-iommu-pci) and a platform +bus device (riscv-iommu-sys) that QEMU RISC-V boards can use. The 'virt' +RISC-V machine is compatible with both devices. riscv-iommu-pci reference device -------------------------------- @@ -83,6 +83,30 @@ Several options are available to control the capabilities of the device, namely: - "s-stage": enable s-stage support - "g-stage": enable g-stage support +riscv-iommu-sys device +---------------------- + +This device implements the RISC-V IOMMU emulation as a platform bus device that +RISC-V boards can use. + +For the 'virt' board the device is disabled by default. To enable it use the +'iommu-sys' machine option: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt,iommu-sys=on (...) + +There is no options to configure the capabilities of this device in the 'virt' +board using the QEMU command line. The device is configured with the following +riscv-iommu options: + +- "ioatc-limit": default value (2Mb) +- "intremap": enabled +- "ats": enabled +- "off": on (DMA disabled) +- "s-stage": enabled +- "g-stage": enabled + .. _iommu1.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0/riscv-iommu.pdf .. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/ diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index 8e9a2e4dda..537aac0340 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -94,6 +94,12 @@ command line: $ qemu-system-riscv64 -M virt -device riscv-iommu-pci (...) +It also has support for the riscv-iommu-sys platform device: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt,iommu-sys=on (...) + Refer to :ref:`riscv-iommu` for more information on how the RISC-V IOMMU support works. @@ -129,6 +135,10 @@ The following machine-specific options are supported: having AIA IMSIC (i.e. "aia=aplic-imsic" selected). When not specified, the default number of per-HART VS-level AIA IMSIC pages is 0. +- iommu-sys=[on|off] + + Enables the riscv-iommu-sys platform device. Defaults to 'off'. + Running Linux kernel -------------------- From 997570359ecd11347ecadf309b485f7ce7bd018e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 13 Nov 2024 22:04:59 +1100 Subject: [PATCH 0513/2892] target/riscv: Add Tenstorrent Ascalon CPU Add a CPU entry for the Tenstorrent Ascalon CPU, a series of 2 wide to 8 wide RV64 cores. More details can be found at https://tenstorrent.com/ip/tt-ascalon Signed-off-by: Anton Blanchard Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241113110459.1607299-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 62115375cd..6547642287 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -49,6 +49,7 @@ #define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") +#define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4329015076..66e00ed260 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -579,6 +579,72 @@ static void rv64_veyron_v1_cpu_init(Object *obj) #endif } +/* Tenstorrent Ascalon */ +static void rv64_tt_ascalon_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU | RVH | RVV); + env->priv_ver = PRIV_VERSION_1_13_0; + + /* Enable ISA extensions */ + cpu->cfg.mmu = true; + cpu->cfg.vlenb = 256 >> 3; + cpu->cfg.elen = 64; + cpu->env.vext_ver = VEXT_VERSION_1_00_0; + cpu->cfg.rvv_ma_all_1s = true; + cpu->cfg.rvv_ta_all_1s = true; + cpu->cfg.misa_w = true; + cpu->cfg.pmp = true; + cpu->cfg.cbom_blocksize = 64; + cpu->cfg.cbop_blocksize = 64; + cpu->cfg.cboz_blocksize = 64; + cpu->cfg.ext_zic64b = true; + cpu->cfg.ext_zicbom = true; + cpu->cfg.ext_zicbop = true; + cpu->cfg.ext_zicboz = true; + cpu->cfg.ext_zicntr = true; + cpu->cfg.ext_zicond = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zihintntl = true; + cpu->cfg.ext_zihintpause = true; + cpu->cfg.ext_zihpm = true; + cpu->cfg.ext_zimop = true; + cpu->cfg.ext_zawrs = true; + cpu->cfg.ext_zfa = true; + cpu->cfg.ext_zfbfmin = true; + cpu->cfg.ext_zfh = true; + cpu->cfg.ext_zfhmin = true; + cpu->cfg.ext_zcb = true; + cpu->cfg.ext_zcmop = true; + cpu->cfg.ext_zba = true; + cpu->cfg.ext_zbb = true; + cpu->cfg.ext_zbs = true; + cpu->cfg.ext_zkt = true; + cpu->cfg.ext_zvbb = true; + cpu->cfg.ext_zvbc = true; + cpu->cfg.ext_zvfbfmin = true; + cpu->cfg.ext_zvfbfwma = true; + cpu->cfg.ext_zvfh = true; + cpu->cfg.ext_zvfhmin = true; + cpu->cfg.ext_zvkng = true; + cpu->cfg.ext_smaia = true; + cpu->cfg.ext_smstateen = true; + cpu->cfg.ext_ssaia = true; + cpu->cfg.ext_sscofpmf = true; + cpu->cfg.ext_sstc = true; + cpu->cfg.ext_svade = true; + cpu->cfg.ext_svinval = true; + cpu->cfg.ext_svnapot = true; + cpu->cfg.ext_svpbmt = true; + +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_SV57); +#endif +} + #ifdef CONFIG_TCG static void rv128_base_cpu_init(Object *obj) { @@ -2984,6 +3050,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), #ifdef CONFIG_TCG DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), From 7d0b35b3c916cf52e5d7c57e214c69bfb23f96d6 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:16:59 -0300 Subject: [PATCH 0514/2892] hw/intc/riscv_aplic: rename is_kvm_aia() The helper is_kvm_aia() is checking not only for AIA, but for aplic-imsic (i.e. "aia=aplic-imsic" in 'virt' RISC-V machine) with an in-kernel chip present. Rename it to be a bit clear what the helper is doing since we'll add more AIA helpers in the next patches. Make the helper public because the 'virt' machine will use it as well. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 8 ++++---- include/hw/intc/riscv_aplic.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 3edab64b97..9f9814a40b 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -154,7 +154,7 @@ * KVM AIA only supports APLIC MSI, fallback to QEMU emulation if we want to use * APLIC Wired. */ -static bool is_kvm_aia(bool msimode) +bool riscv_is_kvm_aia_aplic_imsic(bool msimode) { return kvm_irqchip_in_kernel() && msimode; } @@ -857,7 +857,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) uint32_t i; RISCVAPLICState *aplic = RISCV_APLIC(dev); - if (!is_kvm_aia(aplic->msimode)) { + if (!riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); aplic->state = g_new0(uint32_t, aplic->num_irqs); @@ -881,7 +881,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) * have IRQ lines delegated by their parent APLIC. */ if (!aplic->parent) { - if (kvm_enabled() && is_kvm_aia(aplic->msimode)) { + if (kvm_enabled() && riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs); } else { qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); @@ -1025,7 +1025,7 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - if (!is_kvm_aia(msimode)) { + if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); } diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index de8532fbc3..fd0e6427d9 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -71,6 +71,7 @@ struct RISCVAPLICState { }; void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); +bool riscv_is_kvm_aia_aplic_imsic(bool msimode); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, From 01948b1dea1ce9cafa8df7f0dc3d2aac6056ddbc Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:00 -0300 Subject: [PATCH 0515/2892] hw/riscv/virt.c: reduce virt_use_kvm_aia() usage In create_fdt_sockets() we have the following pattern: if (kvm_enabled() && virt_use_kvm_aia(s)) { (... do stuff ...) } else { (... do other stuff ...) } if (kvm_enabled() && virt_use_kvm_aia(s)) { (... do more stuff ...) } else { (... do more other stuff) } Do everything in a single if/else clause to reduce the usage of virt_use_kvm_aia() helper and to make the code a bit less repetitive. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 5d058511be..e54e1257c0 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -783,6 +783,10 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, ms->smp.cpus); + + *irq_mmio_phandle = xplic_phandles[0]; + *irq_virtio_phandle = xplic_phandles[0]; + *irq_pcie_phandle = xplic_phandles[0]; } else { phandle_pos = ms->smp.cpus; for (socket = (socket_count - 1); socket >= 0; socket--) { @@ -800,13 +804,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, s->soc[socket].num_harts); } } - } - if (kvm_enabled() && virt_use_kvm_aia(s)) { - *irq_mmio_phandle = xplic_phandles[0]; - *irq_virtio_phandle = xplic_phandles[0]; - *irq_pcie_phandle = xplic_phandles[0]; - } else { for (socket = 0; socket < socket_count; socket++) { if (socket == 0) { *irq_mmio_phandle = xplic_phandles[socket]; From 2711e1e324a84e5ce3d7408d723c25d86fc892a8 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:01 -0300 Subject: [PATCH 0516/2892] hw/riscv/virt.c: rename helper to virt_use_kvm_aia_aplic_imsic() Similar to the riscv_is_kvm_aia_aplic_imsic() helper from riscv_aplic.c, the existing virt_use_kvm_aia() is testing for KVM aia=aplic-imsic with in-kernel irqchip enabled. It is not checking for a generic AIA support. Rename the helper to virt_use_kvm_aia_aplic_imsic() to reflect what the helper is doing, and use the existing riscv_is_kvm_aia_aplic_imsic() to obscure details such as the presence of the in-kernel irqchip. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e54e1257c0..a67ab80b16 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -59,9 +59,11 @@ #include "hw/virtio/virtio-iommu.h" /* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ -static bool virt_use_kvm_aia(RISCVVirtState *s) +static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) { - return kvm_irqchip_in_kernel() && s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + + return riscv_is_kvm_aia_aplic_imsic(msimode); } static bool virt_aclint_allowed(void) @@ -777,8 +779,8 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, *msi_pcie_phandle = msi_s_phandle; } - /* KVM AIA only has one APLIC instance */ - if (kvm_enabled() && virt_use_kvm_aia(s)) { + /* KVM AIA aplic-imsic only has one APLIC instance */ + if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { create_fdt_socket_aplic(s, memmap, 0, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, @@ -1619,7 +1621,7 @@ static void virt_machine_init(MachineState *machine) } } - if (kvm_enabled() && virt_use_kvm_aia(s)) { + if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT, VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS, memmap[VIRT_APLIC_S].base, From 3fd619db239fb37557dcd51a4b900417b893d706 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:02 -0300 Subject: [PATCH 0517/2892] target/riscv/kvm: consider irqchip_split() in aia_create() Before adding support to kernel-irqchip=split when using KVM AIA we need to change how we create the in-kernel AIA device. In the use case we have so far, i.e. in-kernel irqchip without split mode, both the s-mode APLIC and IMSIC controllers are provided by the irqchip. In irqchip_split() mode we'll emulate the s-mode APLIC controller, which will send MSIs to the in-kernel IMSIC controller. To do that we need to change kvm_riscv_aia_create() to not create the in-kernel s-mode APLIC controller. In the kernel source arch/riscv/kvm/aia_aplic.c, function kvm_riscv_aia_aplic_init(), we verify that the APLIC controller won't be instantiated by KVM if we do not set 'nr_sources', which is set via KVM_DEV_RISCV_AIA_CONFIG_SRCS. For QEMU this means that we should not set 'aia_irq_num' during kvm_riscv_aia_create() in irqchip_split() mode. In this same condition, skip KVM_DEV_RISCV_AIA_ADDR_APLIC as well since it is used to set the base address for the in-kernel APLIC controller. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index c53ca1f76b..a9680f2447 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1734,13 +1734,29 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, } } - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_SRCS, - &aia_irq_num, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set number of input irq lines"); - exit(1); - } + /* + * Skip APLIC creation in KVM if we're running split mode. + * This is done by leaving KVM_DEV_RISCV_AIA_CONFIG_SRCS + * unset. We can also skip KVM_DEV_RISCV_AIA_ADDR_APLIC + * since KVM won't be using it. + */ + if (!kvm_kernel_irqchip_split()) { + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_SRCS, + &aia_irq_num, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set number of input irq lines"); + exit(1); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, + KVM_DEV_RISCV_AIA_ADDR_APLIC, + &aplic_base, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set the base address of APLIC"); + exit(1); + } + } ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, KVM_DEV_RISCV_AIA_CONFIG_IDS, @@ -1781,14 +1797,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, exit(1); } - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, - KVM_DEV_RISCV_AIA_ADDR_APLIC, - &aplic_base, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set the base address of APLIC"); - exit(1); - } - for (socket = 0; socket < socket_count; socket++) { socket_imsic_base = imsic_base + socket * (1U << group_shift); hart_count = riscv_socket_hart_count(machine, socket); From b319ef15b899c31cad985e50640393483479a3db Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:03 -0300 Subject: [PATCH 0518/2892] hw/riscv/virt.c, riscv_aplic.c: add 'emulated_aplic' helpers The current logic to determine if we don't need an emulated APLIC controller, i.e. KVM will provide for us, is to determine if we're running KVM, with in-kernel irqchip support, and running aia=aplic-imsic. This is modelled by riscv_is_kvm_aia_aplic_imsic() and virt_use_kvm_aia_aplic_imsic(). This won't suffice to support irqchip_split() mode: it will match exactly the same conditions as the one above, but setting the irqchip to 'split' mode will now require us to emulate an APLIC s-mode controller, like we're doing with 'aia=aplic'. Create a new riscv_use_emulated_aplic() helper that will encapsulate this logic. Replace the uses of "riscv_is_kvm_aia_aplic_imsic()" with this helper every time we're taking a decision on emulate an APLIC controller or not. Do the same in virt.c with virt_use_emulated_aplic(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 24 +++++++++++++++++++++--- hw/riscv/virt.c | 14 ++++++++++++-- include/hw/intc/riscv_aplic.h | 1 + 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 9f9814a40b..ba4e802888 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -32,6 +32,7 @@ #include "target/riscv/cpu.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "kvm/kvm_riscv.h" #include "migration/vmstate.h" @@ -159,6 +160,23 @@ bool riscv_is_kvm_aia_aplic_imsic(bool msimode) return kvm_irqchip_in_kernel() && msimode; } +bool riscv_use_emulated_aplic(bool msimode) +{ +#ifdef CONFIG_KVM + if (tcg_enabled()) { + return true; + } + + if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { + return true; + } + + return kvm_kernel_irqchip_split(); +#else + return true; +#endif +} + static bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic, uint32_t irq) { @@ -857,7 +875,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) uint32_t i; RISCVAPLICState *aplic = RISCV_APLIC(dev); - if (!riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { + if (riscv_use_emulated_aplic(aplic->msimode)) { aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); aplic->state = g_new0(uint32_t, aplic->num_irqs); @@ -881,7 +899,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) * have IRQ lines delegated by their parent APLIC. */ if (!aplic->parent) { - if (kvm_enabled() && riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { + if (kvm_enabled() && !riscv_use_emulated_aplic(aplic->msimode)) { qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs); } else { qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); @@ -1025,7 +1043,7 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { + if (riscv_use_emulated_aplic(msimode)) { sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index a67ab80b16..937dfd1ef2 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -66,6 +66,13 @@ static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) return riscv_is_kvm_aia_aplic_imsic(msimode); } +static bool virt_use_emulated_aplic(RISCVVirtAIAType aia_type) +{ + bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + + return riscv_use_emulated_aplic(msimode); +} + static bool virt_aclint_allowed(void) { return tcg_enabled() || qtest_enabled(); @@ -779,8 +786,11 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, *msi_pcie_phandle = msi_s_phandle; } - /* KVM AIA aplic-imsic only has one APLIC instance */ - if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { + /* + * With KVM AIA aplic-imsic, using an irqchip without split + * mode, we'll use only one APLIC instance. + */ + if (!virt_use_emulated_aplic(s->aia_type)) { create_fdt_socket_aplic(s, memmap, 0, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index fd0e6427d9..74ae5d87b5 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -72,6 +72,7 @@ struct RISCVAPLICState { void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); bool riscv_is_kvm_aia_aplic_imsic(bool msimode); +bool riscv_use_emulated_aplic(bool msimode); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, From e0c87e30678394cbca7585ef103b773a396be4d8 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:04 -0300 Subject: [PATCH 0519/2892] hw/intc/riscv_aplic: add kvm_msicfgaddr for split mode aplic-imsic The last step to enable KVM AIA aplic-imsic with irqchip in split mode is to deal with how MSIs are going to be sent. In our current design we don't allow an APLIC controller to send MSIs unless it's on m-mode. And we also do not allow Supervisor MSI address configuration via the 'smsiaddrcfg' and 'smsiaddrcfgh' registers unless it's also a m-mode APLIC controller. Add a new RISCVACPLICState attribute called 'kvm_msicfgaddr'. This attribute represents the base configuration address for MSIs, in our case the base addr of the IMSIC controller. This attribute is being set only when running irqchip_split() mode with aia=aplic-imsic. During riscv_aplic_msi_send() we'll check if the attribute was set to skip the check for a m-mode APLIC controller and to change the resulting MSI addr by adding kvm_msicfgaddr right before address_space_stl_le(). Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20241119191706.718860-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 42 +++++++++++++++++++++++++++-------- hw/riscv/virt.c | 6 ++++- include/hw/intc/riscv_aplic.h | 6 +++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index ba4e802888..1e4cdb500c 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -177,6 +177,16 @@ bool riscv_use_emulated_aplic(bool msimode) #endif } +void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr) +{ +#ifdef CONFIG_KVM + if (riscv_use_emulated_aplic(aplic->msimode)) { + aplic->kvm_msicfgaddr = extract64(addr, 0, 32); + aplic->kvm_msicfgaddrH = extract64(addr, 32, 32); + } +#endif +} + static bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic, uint32_t irq) { @@ -381,13 +391,16 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, 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->kvm_splitmode) { + 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) { @@ -419,6 +432,11 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, addr |= (uint64_t)(guest_idx & APLIC_xMSICFGADDR_PPN_HART(lhxs)); addr <<= APLIC_xMSICFGADDR_PPN_SHIFT; + if (aplic->kvm_splitmode) { + addr |= aplic->kvm_msicfgaddr; + addr |= ((uint64_t)aplic->kvm_msicfgaddrH << 32); + } + address_space_stl_le(&address_space_memory, addr, eiid, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { @@ -892,6 +910,10 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) 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); + + if (kvm_enabled()) { + aplic->kvm_splitmode = true; + } } /* @@ -939,8 +961,8 @@ static const Property riscv_aplic_properties[] = { static const VMStateDescription vmstate_riscv_aplic = { .name = "riscv_aplic", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32(domaincfg, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), @@ -948,6 +970,8 @@ static const VMStateDescription vmstate_riscv_aplic = { VMSTATE_UINT32(smsicfgaddr, RISCVAPLICState), VMSTATE_UINT32(smsicfgaddrH, RISCVAPLICState), VMSTATE_UINT32(genmsi, RISCVAPLICState), + VMSTATE_UINT32(kvm_msicfgaddr, RISCVAPLICState), + VMSTATE_UINT32(kvm_msicfgaddrH, RISCVAPLICState), VMSTATE_VARRAY_UINT32(sourcecfg, RISCVAPLICState, num_irqs, 0, vmstate_info_uint32, uint32_t), diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 937dfd1ef2..43a1c86c33 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1300,7 +1300,7 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, int base_hartid, int hart_count) { int i; - hwaddr addr; + hwaddr addr = 0; uint32_t guest_bits; DeviceState *aplic_s = NULL; DeviceState *aplic_m = NULL; @@ -1350,6 +1350,10 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, VIRT_IRQCHIP_NUM_PRIO_BITS, msimode, false, aplic_m); + if (kvm_enabled() && msimode) { + riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s), addr); + } + return kvm_enabled() ? aplic_s : aplic_m; } diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index 74ae5d87b5..489b9133c2 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -68,11 +68,17 @@ struct RISCVAPLICState { uint32_t num_irqs; bool msimode; bool mmode; + + /* To support KVM aia=aplic-imsic with irqchip split mode */ + bool kvm_splitmode; + uint32_t kvm_msicfgaddr; + uint32_t kvm_msicfgaddrH; }; void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); bool riscv_is_kvm_aia_aplic_imsic(bool msimode); bool riscv_use_emulated_aplic(bool msimode); +void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, From ce7320bf5641bfcf864c2ad9a31358c41a686c10 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:05 -0300 Subject: [PATCH 0520/2892] target/riscv/kvm: remove irqchip_split() restriction Remove the 'irqchip_split()' restriction in kvm_arch_init() now that we have support for "-accel kvm,kernel-irqchip=split". Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index a9680f2447..aaff4a0f42 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1408,11 +1408,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) int kvm_arch_irqchip_create(KVMState *s) { - if (kvm_kernel_irqchip_split()) { - error_report("-machine kernel_irqchip=split is not supported on RISC-V."); - exit(1); - } - /* * We can create the VAIA using the newer device control API. */ From fc560153b4da0562959de4d566a1f99d20c50055 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 19 Nov 2024 16:17:06 -0300 Subject: [PATCH 0521/2892] docs: update riscv/virt.rst with kernel-irqchip=split support Also add a new page, docs/specs/riscv-aia.rst, where we're documenting the state of AIA support in QEMU w.r.t the controllers being emulated or not depending on the AIA and accelerator settings. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241119191706.718860-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- docs/specs/index.rst | 1 + docs/specs/riscv-aia.rst | 83 ++++++++++++++++++++++++++++++++++++++ docs/system/riscv/virt.rst | 7 ++++ 3 files changed, 91 insertions(+) create mode 100644 docs/specs/riscv-aia.rst diff --git a/docs/specs/index.rst b/docs/specs/index.rst index ff5a1f03da..d7675cebc2 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -37,3 +37,4 @@ guest hardware that is specific to QEMU. rapl-msr rocker riscv-iommu + riscv-aia diff --git a/docs/specs/riscv-aia.rst b/docs/specs/riscv-aia.rst new file mode 100644 index 0000000000..8097e2f897 --- /dev/null +++ b/docs/specs/riscv-aia.rst @@ -0,0 +1,83 @@ +.. _riscv-aia: + +RISC-V AIA support for RISC-V machines +====================================== + +AIA (Advanced Interrupt Architecture) support is implemented in the ``virt`` +RISC-V machine for TCG and KVM accelerators. + +The support consists of two main modes: + +- "aia=aplic": adds one or more APLIC (Advanced Platform Level Interrupt Controller) + devices +- "aia=aplic-imsic": adds one or more APLIC device and an IMSIC (Incoming MSI + Controller) device for each CPU + +From an user standpoint, these modes will behave the same regardless of the accelerator +used. From a developer standpoint the accelerator settings will change what it being +emulated in userspace versus what is being emulated by an in-kernel irqchip. + +When running TCG, all controllers are emulated in userspace, including machine mode +(m-mode) APLIC and IMSIC (when applicable). + +When running KVM: + +- no m-mode is provided, so there is no m-mode APLIC or IMSIC emulation regardless of + the AIA mode chosen +- with "aia=aplic", s-mode APLIC will be emulated by userspace +- with "aia=aplic-imsic" there are two possibilities. If no additional KVM option + is provided there will be no APLIC or IMSIC emulation in userspace, and the virtual + machine will use the provided in-kernel APLIC and IMSIC controllers. If the user + chooses to use the irqchip in split mode via "-accel kvm,kernel-irqchip=split", + s-mode APLIC will be emulated while using the s-mode IMSIC from the irqchip + +The following table summarizes how the AIA and accelerator options defines what +we will emulate in userspace: + + +.. list-table:: How AIA and accel options changes controller emulation + :widths: 25 25 25 25 25 25 25 + :header-rows: 1 + + * - Accel + - Accel props + - AIA type + - APLIC m-mode + - IMSIC m-mode + - APLIC s-mode + - IMSIC s-mode + * - tcg + - --- + - aplic + - emul + - n/a + - emul + - n/a + * - tcg + - --- + - aplic-imsic + - emul + - emul + - emul + - emul + * - kvm + - --- + - aplic + - n/a + - n/a + - emul + - n/a + * - kvm + - none + - aplic-imsic + - n/a + - n/a + - in-kernel + - in-kernel + * - kvm + - irqchip=split + - aplic-imsic + - n/a + - n/a + - emul + - in-kernel diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index 537aac0340..60850970ce 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -129,6 +129,13 @@ The following machine-specific options are supported: MSIs. When not specified, this option is assumed to be "none" which selects SiFive PLIC to handle wired interrupts. + This option also interacts with '-accel kvm'. When using "aia=aplic-imsic" + with KVM, it is possible to set the use of the kernel irqchip in split mode + by using "-accel kvm,kernel-irqchip=split". In this case the ``virt`` machine + will emulate the APLIC controller instead of using the APLIC controller from + the irqchip. See :ref:`riscv-aia` for more details on all available AIA + modes. + - aia-guests=nnn The number of per-HART VS-level AIA IMSIC pages to be emulated for a guest From 77aad42ee222ebf695295e56a88fc812255b4a8b Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Mon, 25 Nov 2024 19:17:39 +0530 Subject: [PATCH 0522/2892] hw/riscv: Add Microblaze V generic board Add a basic board with interrupt controller (intc), timer, serial (uartlite), small memory called LMB@0 (128kB) and DDR@0x80000000 (configured via command line eg. -m 2g). This is basic configuration which matches HW generated out of AMD Vivado (design tools). But initial configuration is going beyond what it is configured by default because validation should be done on other configurations too. That's why wire also additional uart16500, axi ethernet(with axi dma). GPIOs, i2c and qspi is also listed for completeness. IRQ map is: (addr) 0 - timer (0x41c00000) 1 - uartlite (0x40600000) 2 - i2c (0x40800000) 3 - qspi (0x44a00000) 4 - uart16550 (0x44a10000) 5 - emaclite (0x40e00000) 6 - timer2 (0x41c10000) 7 - axi emac (0x40c00000) 8 - axi dma (0x41e00000) 9 - axi dma 10 - gpio (0x40000000) 11 - gpio2 (0x40010000) 12 - gpio3 (0x40020000) Signed-off-by: Sai Pavan Boddu Signed-off-by: Michal Simek Reviewed-by: Alistair Francis Message-ID: <20241125134739.18189-1-sai.pavan.boddu@amd.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 6 + docs/system/riscv/microblaze-v-generic.rst | 42 +++++ docs/system/target-riscv.rst | 1 + hw/riscv/Kconfig | 8 + hw/riscv/meson.build | 1 + hw/riscv/microblaze-v-generic.c | 184 +++++++++++++++++++++ 6 files changed, 242 insertions(+) create mode 100644 docs/system/riscv/microblaze-v-generic.rst create mode 100644 hw/riscv/microblaze-v-generic.c diff --git a/MAINTAINERS b/MAINTAINERS index 430a0f4f8c..fb6a66f1f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1626,6 +1626,12 @@ F: docs/system/riscv/sifive_u.rst F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h +AMD Microblaze-V Generic Board +M: Sai Pavan Boddu +S: Maintained +F: hw/riscv/microblaze-v-generic.c +F: docs/system/riscv/microblaze-v-generic.rst + RX Machines ----------- rx-gdbsim diff --git a/docs/system/riscv/microblaze-v-generic.rst b/docs/system/riscv/microblaze-v-generic.rst new file mode 100644 index 0000000000..5606f88d57 --- /dev/null +++ b/docs/system/riscv/microblaze-v-generic.rst @@ -0,0 +1,42 @@ +Microblaze-V generic board (``amd-microblaze-v-generic``) +========================================================= +The AMD MicroBlaze™ V processor is a soft-core RISC-V processor IP for AMD +adaptive SoCs and FPGAs. The MicroBlaze™ V processor is based on the 32-bit (or +64-bit) RISC-V instruction set architecture (ISA) and contains interfaces +compatible with the classic MicroBlaze™ V processor (i.e it is a drop in +replacement for the classic MicroBlaze™ processor in existing RTL designs). +More information can be found in below document. + +https://docs.amd.com/r/en-US/ug1629-microblaze-v-user-guide/MicroBlaze-V-Architecture + +The MicroBlaze™ V generic board in QEMU has following supported devices: + + - timer + - uartlite + - uart16550 + - emaclite + - timer2 + - axi emac + - axi dma + +The MicroBlaze™ V core in QEMU has the following configuration: + + - RV32I base integer instruction set + - "Zicsr" Control and Status register instructions + - "Zifencei" instruction-fetch + - Extensions: m, a, f, c + +Running +""""""" +Below is an example command line for launching mainline U-boot +(xilinx_mbv32_defconfig) on the Microblaze-V generic board. + +.. code-block:: bash + + $ qemu-system-riscv32 -M amd-microblaze-v-generic \ + -display none \ + -device loader,addr=0x80000000,file=u-boot-spl.bin,cpu-num=0 \ + -device loader,addr=0x80200000,file=u-boot.img \ + -serial mon:stdio \ + -device loader,addr=0x83000000,file=system.dtb \ + -m 2g diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index ba195f1518..95457af130 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -66,6 +66,7 @@ undocumented; you can get a complete list by running .. toctree:: :maxdepth: 1 + riscv/microblaze-v-generic riscv/microchip-icicle-kit riscv/shakti-c riscv/sifive_u diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 2e88467c4a..e6a0ac1fa1 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -25,6 +25,14 @@ config MICROCHIP_PFSOC select SIFIVE_PLIC select UNIMP +config MICROBLAZE_V + bool + default y + depends on RISCV32 || RISCV64 + select XILINX + select XILINX_AXI + select XILINX_ETHLITE + config OPENTITAN bool default y diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 3be13d7774..3c7e083aca 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -11,5 +11,6 @@ riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c')) +riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c new file mode 100644 index 0000000000..427e3baca7 --- /dev/null +++ b/hw/riscv/microblaze-v-generic.c @@ -0,0 +1,184 @@ +/* + * QEMU model of Microblaze V generic board. + * + * based on hw/microblaze/petalogix_ml605_mmu.c + * + * Copyright (c) 2011 Michal Simek + * Copyright (c) 2011 PetaLogix + * Copyright (c) 2009 Edgar E. Iglesias. + * Copyright (C) 2024, Advanced Micro Devices, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Sai Pavan Boddu . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "hw/boards.h" +#include "hw/char/serial-mm.h" +#include "exec/address-spaces.h" +#include "hw/char/xilinx_uartlite.h" +#include "hw/misc/unimp.h" + +#define LMB_BRAM_SIZE (128 * KiB) +#define MEMORY_BASEADDR 0x80000000 +#define INTC_BASEADDR 0x41200000 +#define TIMER_BASEADDR 0x41c00000 +#define TIMER_BASEADDR2 0x41c10000 +#define UARTLITE_BASEADDR 0x40600000 +#define ETHLITE_BASEADDR 0x40e00000 +#define UART16550_BASEADDR 0x44a10000 +#define AXIENET_BASEADDR 0x40c00000 +#define AXIDMA_BASEADDR 0x41e00000 +#define GPIO_BASEADDR 0x40000000 +#define GPIO_BASEADDR2 0x40010000 +#define GPIO_BASEADDR3 0x40020000 +#define I2C_BASEADDR 0x40800000 +#define QSPI_BASEADDR 0x44a00000 + +#define TIMER_IRQ 0 +#define UARTLITE_IRQ 1 +#define UART16550_IRQ 4 +#define ETHLITE_IRQ 5 +#define TIMER_IRQ2 6 +#define AXIENET_IRQ 7 +#define AXIDMA_IRQ1 8 +#define AXIDMA_IRQ0 9 + +static void mb_v_generic_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + DeviceState *dev, *dma, *eth0; + Object *ds, *cs; + int i; + RISCVCPU *cpu; + hwaddr ddr_base = MEMORY_BASEADDR; + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); + MemoryRegion *phys_ram = g_new(MemoryRegion, 1); + qemu_irq irq[32]; + MemoryRegion *sysmem = get_system_memory(); + + cpu = RISCV_CPU(object_new(machine->cpu_type)); + object_property_set_bool(OBJECT(cpu), "h", false, NULL); + object_property_set_bool(OBJECT(cpu), "d", false, NULL); + qdev_realize(DEVICE(cpu), NULL, &error_abort); + /* Attach emulated BRAM through the LMB. */ + memory_region_init_ram(phys_lmb_bram, NULL, + "mb_v.lmb_bram", LMB_BRAM_SIZE, + &error_fatal); + memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram); + + memory_region_init_ram(phys_ram, NULL, "mb_v.ram", + ram_size, &error_fatal); + memory_region_add_subregion(sysmem, ddr_base, phys_ram); + + dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", + 1 << UARTLITE_IRQ); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(DEVICE(cpu), 11)); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + /* Uartlite */ + dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, UARTLITE_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[UARTLITE_IRQ]); + + /* Full uart */ + serial_mm_init(sysmem, UART16550_BASEADDR + 0x1000, 2, + irq[UART16550_IRQ], 115200, serial_hd(1), + DEVICE_LITTLE_ENDIAN); + + /* 2 timers at irq 0 @ 100 Mhz. */ + dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100000000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); + + /* 2 timers at irq 3 @ 100 Mhz. */ + dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100000000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR2); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ2]); + + /* Emaclite */ + dev = qdev_new("xlnx.xps-ethernetlite"); + qemu_configure_nic_device(dev, true, NULL); + qdev_prop_set_uint32(dev, "tx-ping-pong", 0); + qdev_prop_set_uint32(dev, "rx-ping-pong", 0); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]); + + /* axi ethernet and dma initialization. */ + eth0 = qdev_new("xlnx.axi-ethernet"); + dma = qdev_new("xlnx.axi-dma"); + + /* FIXME: attach to the sysbus instead */ + object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0)); + object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma)); + + ds = object_property_get_link(OBJECT(dma), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(dma), + "axistream-control-connected-target", NULL); + qemu_configure_nic_device(eth0, true, NULL); + qdev_prop_set_uint32(eth0, "rxmem", 0x1000); + qdev_prop_set_uint32(eth0, "txmem", 0x1000); + object_property_set_link(OBJECT(eth0), "axistream-connected", ds, + &error_abort); + object_property_set_link(OBJECT(eth0), "axistream-control-connected", cs, + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(eth0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]); + + ds = object_property_get_link(OBJECT(eth0), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(eth0), + "axistream-control-connected-target", NULL); + qdev_prop_set_uint32(dma, "freqhz", 100000000); + object_property_set_link(OBJECT(dma), "axistream-connected", ds, + &error_abort); + object_property_set_link(OBJECT(dma), "axistream-control-connected", cs, + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dma), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]); + + /* unimplemented devices */ + create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000); + create_unimplemented_device("gpio2", GPIO_BASEADDR2, 0x10000); + create_unimplemented_device("gpio3", GPIO_BASEADDR3, 0x10000); + create_unimplemented_device("i2c", I2C_BASEADDR, 0x10000); + create_unimplemented_device("qspi", QSPI_BASEADDR, 0x10000); +} + +static void mb_v_generic_machine_init(MachineClass *mc) +{ + mc->desc = "AMD Microblaze-V generic platform"; + mc->init = mb_v_generic_init; + mc->min_cpus = 1; + mc->max_cpus = 1; + mc->default_cpu_type = TYPE_RISCV_CPU_BASE; + mc->default_cpus = 1; +} + +DEFINE_MACHINE("amd-microblaze-v-generic", mb_v_generic_machine_init) From a205d0bcc89c887efcb636924b98e9f77d637369 Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Sun, 27 Oct 2024 18:57:42 -0700 Subject: [PATCH 0523/2892] qtest: allow SPCR acpi table changes Signed-off-by: Sia Jee Heng Reviewed-by: Sunil V L Acked-by: Alistair Francis Message-ID: <20241028015744.624943-2-jeeheng.sia@starfivetech.com> Signed-off-by: Alistair Francis --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..aae973048a 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/riscv64/virt/SPCR", From 6ab861421c90d4392003081c2a3952b5b5df0204 Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Sun, 27 Oct 2024 18:57:43 -0700 Subject: [PATCH 0524/2892] hw/acpi: Upgrade ACPI SPCR table to support SPCR table revision 4 format Update the SPCR table to accommodate the SPCR Table revision 4 [1]. The SPCR table has been modified to adhere to the revision 4 format [2]. [1]: https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table [2]: https://github.com/acpica/acpica/pull/931 Signed-off-by: Sia Jee Heng Reviewed-by: Sunil V L Reviewed-by: Michael S. Tsirkin Acked-by: Alistair Francis Reviewed-by: Bibo Mao Message-ID: <20241028015744.624943-3-jeeheng.sia@starfivetech.com> Signed-off-by: Alistair Francis --- hw/acpi/aml-build.c | 20 ++++++++++++++++---- hw/arm/virt-acpi-build.c | 8 ++++++-- hw/loongarch/acpi-build.c | 6 +++++- hw/riscv/virt-acpi-build.c | 12 +++++++++--- include/hw/acpi/acpi-defs.h | 7 +++++-- include/hw/acpi/aml-build.h | 2 +- 6 files changed, 42 insertions(+), 13 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 72282b173e..e4d58d7051 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2078,7 +2078,7 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, void build_spcr(GArray *table_data, BIOSLinker *linker, const AcpiSpcrData *f, const uint8_t rev, - const char *oem_id, const char *oem_table_id) + const char *oem_id, const char *oem_table_id, const char *name) { AcpiTable table = { .sig = "SPCR", .rev = rev, .oem_id = oem_id, .oem_table_id = oem_table_id }; @@ -2124,9 +2124,21 @@ void build_spcr(GArray *table_data, BIOSLinker *linker, build_append_int_noprefix(table_data, f->pci_flags, 4); /* PCI Segment */ build_append_int_noprefix(table_data, f->pci_segment, 1); - /* Reserved */ - build_append_int_noprefix(table_data, 0, 4); - + if (rev < 4) { + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + } else { + /* UartClkFreq */ + build_append_int_noprefix(table_data, f->uart_clk_freq, 4); + /* PreciseBaudrate */ + build_append_int_noprefix(table_data, f->precise_baudrate, 4); + /* NameSpaceStringLength */ + build_append_int_noprefix(table_data, f->namespace_string_length, 2); + /* NameSpaceStringOffset */ + build_append_int_noprefix(table_data, f->namespace_string_offset, 2); + /* NamespaceString[] */ + g_array_append_vals(table_data, name, f->namespace_string_length); + } acpi_table_end(linker, &table); } /* diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 620992c92c..e92d3bddc8 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -463,8 +463,12 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) .pci_flags = 0, .pci_segment = 0, }; - - build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id); + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ + build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id, + NULL); } /* diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 50709bda0f..4e04f7b6c1 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -276,8 +276,12 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine) }; lvms = LOONGARCH_VIRT_MACHINE(machine); + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ build_spcr(table_data, linker, &serial, 2, lvms->oem_id, - lvms->oem_table_id); + lvms->oem_table_id, NULL); } typedef diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 36d6a3a412..68ef15acac 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -200,14 +200,15 @@ acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, /* * Serial Port Console Redirection Table (SPCR) - * Rev: 1.07 + * Rev: 1.10 */ static void spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) { + const char name[] = "."; AcpiSpcrData serial = { - .interface_type = 0, /* 16550 compatible */ + .interface_type = 0x12, /* 16550 compatible */ .base_addr.id = AML_AS_SYSTEM_MEMORY, .base_addr.width = 32, .base_addr.offset = 0, @@ -229,9 +230,14 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) .pci_function = 0, .pci_flags = 0, .pci_segment = 0, + .uart_clk_freq = 0, + .precise_baudrate = 0, + .namespace_string_length = sizeof(name), + .namespace_string_offset = 88, }; - build_spcr(table_data, linker, &serial, 2, s->oem_id, s->oem_table_id); + build_spcr(table_data, linker, &serial, 4, s->oem_id, s->oem_table_id, + name); } /* RHCT Node[N] starts at offset 56 */ diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 0e6e82b339..2e6e341998 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -112,7 +112,6 @@ typedef struct AcpiSpcrData { uint8_t flow_control; uint8_t terminal_type; uint8_t language; - uint8_t reserved1; uint16_t pci_device_id; /* Must be 0xffff if not PCI device */ uint16_t pci_vendor_id; /* Must be 0xffff if not PCI device */ uint8_t pci_bus; @@ -120,7 +119,11 @@ typedef struct AcpiSpcrData { uint8_t pci_function; uint32_t pci_flags; uint8_t pci_segment; - uint32_t reserved2; + uint32_t uart_clk_freq; + uint32_t precise_baudrate; + uint32_t namespace_string_length; + uint32_t namespace_string_offset; + char namespace_string[]; } AcpiSpcrData; #define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0) diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 4fd5da49e7..c18f681342 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -507,5 +507,5 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, void build_spcr(GArray *table_data, BIOSLinker *linker, const AcpiSpcrData *f, const uint8_t rev, - const char *oem_id, const char *oem_table_id); + const char *oem_id, const char *oem_table_id, const char *name); #endif From 97b682e61d981c1aa90e157599ee6cdea68dd370 Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Sun, 27 Oct 2024 18:57:44 -0700 Subject: [PATCH 0525/2892] tests/qtest/bios-tables-test: Update virt SPCR golden reference for RISC-V Update the virt SPCR golden reference file for RISC-V to accommodate the SPCR Table revision 4 [1], utilizing the iasl binary compiled from the latest ACPICA repository. The SPCR table has been modified to adhere to the revision 4 format [2]. [1]: https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table [2]: https://github.com/acpica/acpica/pull/931 Diffs from iasl: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/riscv64/virt/SPCR, Wed Aug 28 18:28:19 2024 + * Disassembly of /tmp/aml-MN0NS2, Wed Aug 28 18:28:19 2024 * * ACPI Data Table [SPCR] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "SPCR" [Serial Port Console Redirection table] -[004h 0004 4] Table Length : 00000050 -[008h 0008 1] Revision : 02 -[009h 0009 1] Checksum : B9 +[004h 0004 4] Table Length : 0000005A +[008h 0008 1] Revision : 04 +[009h 0009 1] Checksum : 13 [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 1] Interface Type : 00 +[024h 0036 1] Interface Type : 12 [025h 0037 3] Reserved : 000000 [028h 0040 12] Serial Port Register : [Generic Address Structure] [028h 0040 1] Space ID : 00 [SystemMemory] [029h 0041 1] Bit Width : 20 [02Ah 0042 1] Bit Offset : 00 [02Bh 0043 1] Encoded Access Width : 01 [Byte Access:8] [02Ch 0044 8] Address : 0000000010000000 [034h 0052 1] Interrupt Type : 10 [035h 0053 1] PCAT-compatible IRQ : 00 [036h 0054 4] Interrupt : 0000000A [03Ah 0058 1] Baud Rate : 07 [03Bh 0059 1] Parity : 00 [03Ch 0060 1] Stop Bits : 01 [03Dh 0061 1] Flow Control : 00 [03Eh 0062 1] Terminal Type : 00 [04Ch 0076 1] Reserved : 00 [040h 0064 2] PCI Device ID : FFFF [042h 0066 2] PCI Vendor ID : FFFF [044h 0068 1] PCI Bus : 00 [045h 0069 1] PCI Device : 00 [046h 0070 1] PCI Function : 00 [047h 0071 4] PCI Flags : 00000000 [04Bh 0075 1] PCI Segment : 00 -[04Ch 0076 4] Reserved : 00000000 +[04Ch 0076 004h] Uart Clock Freq : 00000000 +[050h 0080 004h] Precise Baud rate : 00000000 +[054h 0084 002h] NameSpaceStringLength : 0002 +[056h 0086 002h] NameSpaceStringOffset : 0058 +[058h 0088 002h] NamespaceString : "." -Raw Table Data: Length 80 (0x50) +Raw Table Data: Length 90 (0x5A) - 0000: 53 50 43 52 50 00 00 00 02 B9 42 4F 43 48 53 20 // SPCRP.....BOCHS + 0000: 53 50 43 52 5A 00 00 00 04 13 42 4F 43 48 53 20 // SPCRZ.....BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC - 0020: 01 00 00 00 00 00 00 00 00 20 00 01 00 00 00 10 // ......... ...... + 0020: 01 00 00 00 12 00 00 00 00 20 00 01 00 00 00 10 // ......... ...... 0030: 00 00 00 00 10 00 0A 00 00 00 07 00 01 00 00 03 // ................ 0040: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 // ................ + 0050: 00 00 00 00 02 00 58 00 2E 00 // ......X... Signed-off-by: Sia Jee Heng Reviewed-by: Sunil V L Message-ID: <20241028015744.624943-4-jeeheng.sia@starfivetech.com> Signed-off-by: Alistair Francis --- tests/data/acpi/riscv64/virt/SPCR | Bin 80 -> 90 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/riscv64/virt/SPCR b/tests/data/acpi/riscv64/virt/SPCR index 4da9daf65f71a13ac2b488d4e9728f194b569a43..09617f8793a6f7b1f08172f735b58aa748671540 100644 GIT binary patch delta 32 mcmWHD;tCFM4vJ!6U|+&Vu)bSV*mhNumqU^ delta 21 ccmazF;0g|K4hmpkU|`xgkxPn^VWO%w05v59j{pDw diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index aae973048a..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/riscv64/virt/SPCR", From a2ce7f806d894f04cbb541d619cead44cd588725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Nov 2024 16:43:02 +0100 Subject: [PATCH 0526/2892] MAINTAINERS: Cover RISC-V HTIF interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HTIF interface is RISC-V specific, add it within the MAINTAINERS section covering hw/riscv/. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241129154304.34946-2-philmd@linaro.org> Signed-off-by: Alistair Francis --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fb6a66f1f8..1076a028a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -324,8 +324,10 @@ S: Supported F: configs/targets/riscv* F: docs/system/target-riscv.rst F: target/riscv/ +F: hw/char/riscv_htif.c F: hw/riscv/ F: hw/intc/riscv* +F: include/hw/char/riscv_htif.h F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ From be0a70b93f4ff3dda77b9db50ec452f03170ff9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Nov 2024 16:43:03 +0100 Subject: [PATCH 0527/2892] hw/char/riscv_htif: Explicit little-endian implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since our RISC-V system emulation is only built for little endian, the HTIF device aims to interface with little endian memory accesses, thus we can explicit htif_mm_ops:endianness being DEVICE_LITTLE_ENDIAN. In that case tswap64() is equivalent to le64_to_cpu(), as in "convert this 64-bit little-endian value into host cpu order". Replace to simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241129154304.34946-3-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/char/riscv_htif.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 0345088e8b..3f84d8d673 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -29,7 +29,7 @@ #include "qemu/timer.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" -#include "exec/tswap.h" +#include "qemu/bswap.h" #include "sysemu/dma.h" #include "sysemu/runstate.h" @@ -212,11 +212,11 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) } else { uint64_t syscall[8]; cpu_physical_memory_read(payload, syscall, sizeof(syscall)); - if (tswap64(syscall[0]) == PK_SYS_WRITE && - tswap64(syscall[1]) == HTIF_DEV_CONSOLE && - tswap64(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { + if (le64_to_cpu(syscall[0]) == PK_SYS_WRITE && + le64_to_cpu(syscall[1]) == HTIF_DEV_CONSOLE && + le64_to_cpu(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { uint8_t ch; - cpu_physical_memory_read(tswap64(syscall[2]), &ch, 1); + cpu_physical_memory_read(le64_to_cpu(syscall[2]), &ch, 1); /* * XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks @@ -324,6 +324,7 @@ static void htif_mm_write(void *opaque, hwaddr addr, static const MemoryRegionOps htif_mm_ops = { .read = htif_mm_read, .write = htif_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, }; HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, From d2ed9fffba07a7ce87f33d5b9662e3e8eadb11d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Nov 2024 16:43:04 +0100 Subject: [PATCH 0528/2892] hw/char/riscv_htif: Clarify MemoryRegionOps expect 32-bit accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looking at htif_mm_ops[] read/write handlers, we notice they expect 32-bit values to accumulate into to the 'fromhost' and 'tohost' 64-bit variables. Explicit by setting the .impl min/max fields. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241129154304.34946-4-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/char/riscv_htif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 3f84d8d673..db69b5e3ca 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -325,6 +325,10 @@ static const MemoryRegionOps htif_mm_ops = { .read = htif_mm_read, .write = htif_mm_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, }; HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, From b4132a9e62978e247bce66e70499c4e2cad8d870 Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Wed, 20 Nov 2024 23:39:33 +0800 Subject: [PATCH 0529/2892] hw/riscv: Support to load DTB after 3GB memory on 64-bit system. Larger initrd image will overlap the DTB at 3GB address. Since 64-bit system doesn't have 32-bit addressable issue, we just load DTB to the end of dram in 64-bit system. Signed-off-by: Jim Shu Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241120153935.24706-2-jim.shu@sifive.com> [ Changes by AF - Store fdt_load_addr_hi32 in the reset vector ] Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 14 +++++++++----- hw/riscv/microchip_pfsoc.c | 4 ++-- hw/riscv/sifive_u.c | 8 +++++--- hw/riscv/spike.c | 4 ++-- hw/riscv/virt.c | 2 +- include/hw/riscv/boot.h | 2 +- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 2e319168db..d36d3a7104 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -293,7 +293,7 @@ out: * The FDT is fdt_packed() during the calculation. */ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, - MachineState *ms) + MachineState *ms, RISCVHartArrayState *harts) { int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; @@ -317,11 +317,15 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, /* * We should put fdt as far as possible to avoid kernel/initrd overwriting - * its content. But it should be addressable by 32 bit system as well. - * Thus, put it at an 2MB aligned address that less than fdt size from the - * end of dram or 3GB whichever is lesser. + * its content. But it should be addressable by 32 bit system as well in RV32. + * Thus, put it near to the end of dram in RV64, and put it near to the end + * of dram or 3GB whichever is lesser in RV32. */ - temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; + if (!riscv_is_32bit(harts)) { + temp = dram_end; + } else { + temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; + } return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); } diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index f9a3b43d2e..ba8b0a2c26 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -519,7 +519,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) bool kernel_as_payload = false; target_ulong firmware_end_addr, kernel_start_addr; uint64_t kernel_entry; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; DriveInfo *dinfo = drive_get(IF_SD, 0, 0); /* Sanity check on RAM size */ @@ -625,7 +625,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) /* Compute the fdt load address in dram */ fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, memmap[MICROCHIP_PFSOC_DRAM_LO].size, - machine); + machine, &s->soc.u_cpus); riscv_load_fdt(fdt_load_addr, machine->fdt); /* Load the reset vector */ diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 124ffd4842..fd59124500 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -518,8 +518,9 @@ static void sifive_u_machine_init(MachineState *machine) target_ulong firmware_end_addr, kernel_start_addr; const char *firmware_name; uint32_t start_addr_hi32 = 0x00000000; + uint32_t fdt_load_addr_hi32 = 0x00000000; int i; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; uint64_t kernel_entry; DriveInfo *dinfo; BlockBackend *blk; @@ -606,11 +607,12 @@ static void sifive_u_machine_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base, memmap[SIFIVE_U_DEV_DRAM].size, - machine); + machine, &s->soc.u_cpus); riscv_load_fdt(fdt_load_addr, machine->fdt); if (!riscv_is_32bit(&s->soc.u_cpus)) { start_addr_hi32 = (uint64_t)start_addr >> 32; + fdt_load_addr_hi32 = fdt_load_addr >> 32; } /* reset vector */ @@ -625,7 +627,7 @@ static void sifive_u_machine_init(MachineState *machine) start_addr, /* start: .dword */ start_addr_hi32, fdt_load_addr, /* fdt_laddr: .dword */ - 0x00000000, + fdt_load_addr_hi32, 0x00000000, /* fw_dyn: */ }; diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index fceb91d946..acd7ab1ae1 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -201,7 +201,7 @@ static void spike_board_init(MachineState *machine) hwaddr firmware_load_addr = memmap[SPIKE_DRAM].base; target_ulong kernel_start_addr; char *firmware_name; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; uint64_t kernel_entry; char *soc_name; int i, base_hartid, hart_count; @@ -317,7 +317,7 @@ static void spike_board_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, memmap[SPIKE_DRAM].size, - machine); + machine, &s->soc[0]); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 43a1c86c33..98da79a5be 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1492,7 +1492,7 @@ static void virt_machine_done(Notifier *notifier, void *data) fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, memmap[VIRT_DRAM].size, - machine); + machine, &s->soc[0]); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index f778b560de..34a80c5ff4 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -49,7 +49,7 @@ target_ulong riscv_load_kernel(MachineState *machine, bool load_initrd, symbol_fn_t sym_cb); uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, - MachineState *ms); + MachineState *ms, RISCVHartArrayState *harts); void riscv_load_fdt(hwaddr fdt_addr, void *fdt); void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr saddr, From d3592955af2a015be1d7138643b4a010eee0ff0c Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Wed, 20 Nov 2024 23:39:34 +0800 Subject: [PATCH 0530/2892] hw/riscv: Add a new struct RISCVBootInfo Add a new struct RISCVBootInfo to sync boot information between multiple boot functions. Signed-off-by: Jim Shu Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241120153935.24706-3-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 65 ++++++++++++++++++++++---------------- hw/riscv/microchip_pfsoc.c | 11 ++++--- hw/riscv/opentitan.c | 4 ++- hw/riscv/sifive_e.c | 4 ++- hw/riscv/sifive_u.c | 12 ++++--- hw/riscv/spike.c | 12 ++++--- hw/riscv/virt.c | 15 +++++---- include/hw/riscv/boot.h | 25 ++++++++++----- 8 files changed, 91 insertions(+), 57 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index d36d3a7104..81d27f935e 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -67,9 +67,15 @@ char *riscv_plic_hart_config_string(int hart_count) return g_strjoinv(",", (char **)vals); } -target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, +void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts) +{ + info->kernel_size = 0; + info->is_32bit = riscv_is_32bit(harts); +} + +target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info, target_ulong firmware_end_addr) { - if (riscv_is_32bit(harts)) { + if (info->is_32bit) { return QEMU_ALIGN_UP(firmware_end_addr, 4 * MiB); } else { return QEMU_ALIGN_UP(firmware_end_addr, 2 * MiB); @@ -175,7 +181,7 @@ target_ulong riscv_load_firmware(const char *firmware_filename, exit(1); } -static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) +static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info) { const char *filename = machine->initrd_filename; uint64_t mem_size = machine->ram_size; @@ -196,7 +202,7 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) * halfway into RAM, and for boards with 1GB of RAM or more we put * the initrd at 512MB. */ - start = kernel_entry + MIN(mem_size / 2, 512 * MiB); + start = info->image_low_addr + MIN(mem_size / 2, 512 * MiB); size = load_ramdisk(filename, start, mem_size - start); if (size == -1) { @@ -215,14 +221,14 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) } } -target_ulong riscv_load_kernel(MachineState *machine, - RISCVHartArrayState *harts, - target_ulong kernel_start_addr, - bool load_initrd, - symbol_fn_t sym_cb) +void riscv_load_kernel(MachineState *machine, + RISCVBootInfo *info, + target_ulong kernel_start_addr, + bool load_initrd, + symbol_fn_t sym_cb) { const char *kernel_filename = machine->kernel_filename; - uint64_t kernel_load_base, kernel_entry; + ssize_t kernel_size; void *fdt = machine->fdt; g_assert(kernel_filename != NULL); @@ -234,21 +240,28 @@ target_ulong riscv_load_kernel(MachineState *machine, * the (expected) load address load address. This allows kernels to have * separate SBI and ELF entry points (used by FreeBSD, for example). */ - if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, - NULL, &kernel_load_base, NULL, NULL, 0, - EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { - kernel_entry = kernel_load_base; + kernel_size = load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL, + &info->image_low_addr, &info->image_high_addr, + NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb); + if (kernel_size > 0) { + info->kernel_size = kernel_size; goto out; } - if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, - NULL, NULL, NULL) > 0) { + kernel_size = load_uimage_as(kernel_filename, &info->image_low_addr, + NULL, NULL, NULL, NULL, NULL); + if (kernel_size > 0) { + info->kernel_size = kernel_size; + info->image_high_addr = info->image_low_addr + kernel_size; goto out; } - if (load_image_targphys_as(kernel_filename, kernel_start_addr, - current_machine->ram_size, NULL) > 0) { - kernel_entry = kernel_start_addr; + kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr, + current_machine->ram_size, NULL); + if (kernel_size > 0) { + info->kernel_size = kernel_size; + info->image_low_addr = kernel_start_addr; + info->image_high_addr = info->image_low_addr + kernel_size; goto out; } @@ -257,23 +270,21 @@ target_ulong riscv_load_kernel(MachineState *machine, out: /* - * For 32 bit CPUs 'kernel_entry' can be sign-extended by + * For 32 bit CPUs 'image_low_addr' can be sign-extended by * load_elf_ram_sym(). */ - if (riscv_is_32bit(harts)) { - kernel_entry = extract64(kernel_entry, 0, 32); + if (info->is_32bit) { + info->image_low_addr = extract64(info->image_low_addr, 0, 32); } if (load_initrd && machine->initrd_filename) { - riscv_load_initrd(machine, kernel_entry); + riscv_load_initrd(machine, info); } if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) { qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", machine->kernel_cmdline); } - - return kernel_entry; } /* @@ -293,7 +304,7 @@ out: * The FDT is fdt_packed() during the calculation. */ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, - MachineState *ms, RISCVHartArrayState *harts) + MachineState *ms, RISCVBootInfo *info) { int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; @@ -321,7 +332,7 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, * Thus, put it near to the end of dram in RV64, and put it near to the end * of dram or 3GB whichever is lesser in RV32. */ - if (!riscv_is_32bit(harts)) { + if (!info->is_32bit) { temp = dram_end; } else { temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index ba8b0a2c26..a302965b6d 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -521,6 +521,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) uint64_t kernel_entry; uint64_t fdt_load_addr; DriveInfo *dinfo = drive_get(IF_SD, 0, 0); + RISCVBootInfo boot_info; /* Sanity check on RAM size */ if (machine->ram_size < mc->default_ram_size) { @@ -615,17 +616,19 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, &firmware_load_addr, NULL); + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (kernel_as_payload) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; /* Compute the fdt load address in dram */ fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, memmap[MICROCHIP_PFSOC_DRAM_LO].size, - machine, &s->soc.u_cpus); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* Load the reset vector */ diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 8ce85ea9f7..87ad9c8f34 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -81,6 +81,7 @@ static void opentitan_machine_init(MachineState *machine) OpenTitanState *s = OPENTITAN_MACHINE(machine); const MemMapEntry *memmap = ibex_memmap; MemoryRegion *sys_mem = get_system_memory(); + RISCVBootInfo boot_info; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -102,8 +103,9 @@ static void opentitan_machine_init(MachineState *machine) riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL); } + riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { - riscv_load_kernel(machine, &s->soc.cpus, + riscv_load_kernel(machine, &boot_info, memmap[IBEX_DEV_RAM].base, false, NULL); } diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 5a1959f2a9..ebcd33ab95 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -78,6 +78,7 @@ static void sifive_e_machine_init(MachineState *machine) SiFiveEState *s = RISCV_E_MACHINE(machine); MemoryRegion *sys_mem = get_system_memory(); int i; + RISCVBootInfo boot_info; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -113,8 +114,9 @@ static void sifive_e_machine_init(MachineState *machine) rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory); + riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { - riscv_load_kernel(machine, &s->soc.cpus, + riscv_load_kernel(machine, &boot_info, memmap[SIFIVE_E_DEV_DTIM].base, false, NULL); } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index fd59124500..6270e1ccec 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -526,6 +526,7 @@ static void sifive_u_machine_init(MachineState *machine) BlockBackend *blk; DeviceState *flash_dev, *sd_dev, *card_dev; qemu_irq flash_cs, sd_cs; + RISCVBootInfo boot_info; /* Initialize SoC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC); @@ -591,12 +592,13 @@ static void sifive_u_machine_init(MachineState *machine) firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, &start_addr, NULL); + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (machine->kernel_filename) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - - kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -607,7 +609,7 @@ static void sifive_u_machine_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base, memmap[SIFIVE_U_DEV_DRAM].size, - machine, &s->soc.u_cpus); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); if (!riscv_is_32bit(&s->soc.u_cpus)) { diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index acd7ab1ae1..c3ad16d316 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -206,6 +206,7 @@ static void spike_board_init(MachineState *machine) char *soc_name; int i, base_hartid, hart_count; bool htif_custom_base = false; + RISCVBootInfo boot_info; /* Check socket count limit */ if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) { @@ -300,13 +301,14 @@ static void spike_board_init(MachineState *machine) create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base); /* Load kernel */ + riscv_boot_info_init(&boot_info, &s->soc[0]); if (machine->kernel_filename) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine, &s->soc[0], - kernel_start_addr, - true, htif_symbol_callback); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, htif_symbol_callback); + kernel_entry = boot_info.image_low_addr; } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -317,7 +319,7 @@ static void spike_board_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, memmap[SPIKE_DRAM].size, - machine, &s->soc[0]); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 98da79a5be..d2bfdec56a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1434,6 +1434,7 @@ static void virt_machine_done(Notifier *notifier, void *data) uint64_t fdt_load_addr; uint64_t kernel_entry = 0; BlockBackend *pflash_blk0; + RISCVBootInfo boot_info; /* * An user provided dtb must include everything, including @@ -1482,17 +1483,19 @@ static void virt_machine_done(Notifier *notifier, void *data) } } - if (machine->kernel_filename && !kernel_entry) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], - firmware_end_addr); + riscv_boot_info_init(&boot_info, &s->soc[0]); - kernel_entry = riscv_load_kernel(machine, &s->soc[0], - kernel_start_addr, true, NULL); + if (machine->kernel_filename && !kernel_entry) { + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, + firmware_end_addr); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; } fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, memmap[VIRT_DRAM].size, - machine, &s->soc[0]); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index 34a80c5ff4..06b51ed086 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -27,11 +27,20 @@ #define RISCV32_BIOS_BIN "opensbi-riscv32-generic-fw_dynamic.bin" #define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin" +typedef struct RISCVBootInfo { + ssize_t kernel_size; + hwaddr image_low_addr; + hwaddr image_high_addr; + + bool is_32bit; +} RISCVBootInfo; + bool riscv_is_32bit(RISCVHartArrayState *harts); char *riscv_plic_hart_config_string(int hart_count); -target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, +void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts); +target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info, target_ulong firmware_end_addr); target_ulong riscv_find_and_load_firmware(MachineState *machine, const char *default_machine_firmware, @@ -43,13 +52,13 @@ char *riscv_find_firmware(const char *firmware_filename, target_ulong riscv_load_firmware(const char *firmware_filename, hwaddr *firmware_load_addr, symbol_fn_t sym_cb); -target_ulong riscv_load_kernel(MachineState *machine, - RISCVHartArrayState *harts, - target_ulong firmware_end_addr, - bool load_initrd, - symbol_fn_t sym_cb); -uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, - MachineState *ms, RISCVHartArrayState *harts); +void riscv_load_kernel(MachineState *machine, + RISCVBootInfo *info, + target_ulong kernel_start_addr, + bool load_initrd, + symbol_fn_t sym_cb); +uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, + MachineState *ms, RISCVBootInfo *info); void riscv_load_fdt(hwaddr fdt_addr, void *fdt); void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr saddr, From 1a65064c1f4dc701370c1d59ac1b85afa198b3b0 Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Wed, 20 Nov 2024 23:39:35 +0800 Subject: [PATCH 0531/2892] hw/riscv: Add the checking if DTB overlaps to kernel or initrd DTB is placed to the end of memory, so we will check if the start address of DTB overlaps to the address of kernel/initrd. Signed-off-by: Jim Shu Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241120153935.24706-4-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 25 ++++++++++++++++++++++++- include/hw/riscv/boot.h | 3 +++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 81d27f935e..bc8074fec8 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -70,6 +70,7 @@ char *riscv_plic_hart_config_string(int hart_count) void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts) { info->kernel_size = 0; + info->initrd_size = 0; info->is_32bit = riscv_is_32bit(harts); } @@ -213,6 +214,9 @@ static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info) } } + info->initrd_start = start; + info->initrd_size = size; + /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ if (fdt) { end = start + size; @@ -309,6 +313,7 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; int fdtsize; + uint64_t dtb_start, dtb_start_limit; /* Should only fail if we've built a corrupted tree */ g_assert(ret == 0); @@ -319,6 +324,17 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, exit(1); } + if (info->initrd_size) { + /* If initrd is successfully loaded, place DTB after it. */ + dtb_start_limit = info->initrd_start + info->initrd_size; + } else if (info->kernel_size) { + /* If only kernel is successfully loaded, place DTB after it. */ + dtb_start_limit = info->image_high_addr; + } else { + /* Otherwise, do not check DTB overlapping */ + dtb_start_limit = 0; + } + /* * A dram_size == 0, usually from a MemMapEntry[].size element, * means that the DRAM block goes all the way to ms->ram_size. @@ -338,7 +354,14 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; } - return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); + dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); + + if (dtb_start_limit && (dtb_start < dtb_start_limit)) { + error_report("No enough memory to place DTB after kernel/initrd"); + exit(1); + } + + return dtb_start; } /* diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index 06b51ed086..7d59b2e6c6 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -32,6 +32,9 @@ typedef struct RISCVBootInfo { hwaddr image_low_addr; hwaddr image_high_addr; + hwaddr initrd_start; + ssize_t initrd_size; + bool is_32bit; } RISCVBootInfo; From 59018ec72f4bbc8bc291006899847dbf66a65255 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Tue, 3 Dec 2024 11:49:27 +0800 Subject: [PATCH 0532/2892] target/riscv: Add svukte extension capability variable Refer to the draft of svukte extension from: https://github.com/riscv/riscv-isa-manual/pull/1564 Svukte provides a means to make user-mode accesses to supervisor memory raise page faults in constant time, mitigating attacks that attempt to discover the supervisor software's address-space layout. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Jim Shu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241203034932.25185-2-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 59d6fc445d..d8771ca641 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -84,6 +84,7 @@ struct RISCVCPUConfig { bool ext_svnapot; bool ext_svpbmt; bool ext_svvptc; + bool ext_svukte; bool ext_zdinx; bool ext_zaamo; bool ext_zacas; From 81c8436277cae4d9f63a036c9241abea16ae2dc5 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Tue, 3 Dec 2024 11:49:28 +0800 Subject: [PATCH 0533/2892] target/riscv: Support senvcfg[UKTE] bit when svukte extension is enabled Svukte extension add UKTE bit, bit[8] in senvcfg CSR. The bit will be supported when the svukte extension is enabled. When senvcfg[UKTE] bit is set, the memory access from U-mode should do the svukte check only except HLV/HLVX/HSV H-mode instructions which depend on hstatus[HUKTE]. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Jim Shu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241203034932.25185-3-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 1 + target/riscv/csr.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 385a2c67c2..4b9f899217 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -785,6 +785,7 @@ typedef enum RISCVException { #define SENVCFG_CBIE MENVCFG_CBIE #define SENVCFG_CBCFE MENVCFG_CBCFE #define SENVCFG_CBZE MENVCFG_CBZE +#define SENVCFG_UKTE BIT(8) #define HENVCFG_FIOM MENVCFG_FIOM #define HENVCFG_LPE MENVCFG_LPE diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9846770820..1936a6f32a 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2453,6 +2453,10 @@ static RISCVException write_senvcfg(CPURISCVState *env, int csrno, mask |= SENVCFG_SSE; } + if (env_archcpu(env)->cfg.ext_svukte) { + mask |= SENVCFG_UKTE; + } + env->senvcfg = (env->senvcfg & ~mask) | (val & mask); return RISCV_EXCP_NONE; } From 19eb69d09a3e1c3c6fef75d0679ee8b3078b82a2 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Tue, 3 Dec 2024 11:49:29 +0800 Subject: [PATCH 0534/2892] target/riscv: Support hstatus[HUKTE] bit when svukte extension is enabled Svukte extension add HUKTE bit, bit[24] in hstatus CSR. The written value will be masked when the svukte extension is not enabled. When hstatus[HUKTE] bit is set, HLV/HLVX/HSV work in the U-mode should do svukte check. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Jim Shu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241203034932.25185-4-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 1 + target/riscv/csr.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 4b9f899217..fe4e34c64a 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -604,6 +604,7 @@ typedef enum { #define HSTATUS_VTVM 0x00100000 #define HSTATUS_VTW 0x00200000 #define HSTATUS_VTSR 0x00400000 +#define HSTATUS_HUKTE 0x01000000 #define HSTATUS_VSXL 0x300000000 #define HSTATUS32_WPRI 0xFF8FF87E diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 1936a6f32a..b6fa8ae53f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3540,6 +3540,9 @@ static RISCVException read_hstatus(CPURISCVState *env, int csrno, static RISCVException write_hstatus(CPURISCVState *env, int csrno, target_ulong val) { + if (!env_archcpu(env)->cfg.ext_svukte) { + val = val & (~HSTATUS_HUKTE); + } env->hstatus = val; if (riscv_cpu_mxl(env) != MXL_RV32 && get_field(val, HSTATUS_VSXL) != 2) { qemu_log_mask(LOG_UNIMP, From ab348b09823cb2d50271dcb039350bfb25d86aad Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Tue, 3 Dec 2024 11:49:30 +0800 Subject: [PATCH 0535/2892] target/riscv: Check memory access to meet svukte rule Follow the Svukte spec, do the memory access address checking 1. Include instruction fetches or explicit memory accesses 2. System run in effective privilege U or VU 3. Check senvcfg[UKTE] being set, or hstatus[HUKTE] being set if instruction is HLV, HLVX, HSV and execute from U mode to VU mode 4. Depend on Sv39 and check virtual addresses bit[SXLEN-1] 5. Raises a page-fault exception corresponding to the original access type. Ref: https://github.com/riscv/riscv-isa-manual/pull/1564/files Signed-off-by: Frank Chang Signed-off-by: Fea.Wang Signed-off-by: Daniel Henrique Barboza Reviewed-by: Jim Shu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241203034932.25185-5-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 45806f5ab0..750c0537ca 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -857,6 +857,55 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, return TRANSLATE_SUCCESS; } +/* Returns 'true' if a svukte address check is needed */ +static bool do_svukte_check(CPURISCVState *env, bool first_stage, + int mode, bool virt) +{ + /* Svukte extension depends on Sv39. */ + if (!(env_archcpu(env)->cfg.ext_svukte || + !first_stage || + VM_1_10_SV39 != get_field(env->satp, SATP64_MODE))) { + return false; + } + + /* + * Check hstatus.HUKTE if the effective mode is switched to VU-mode by + * executing HLV/HLVX/HSV in U-mode. + * For other cases, check senvcfg.UKTE. + */ + if (env->priv == PRV_U && !env->virt_enabled && virt) { + if (!get_field(env->hstatus, HSTATUS_HUKTE)) { + return false; + } + } else if (!get_field(env->senvcfg, SENVCFG_UKTE)) { + return false; + } + + /* + * Svukte extension is qualified only in U or VU-mode. + * + * Effective mode can be switched to U or VU-mode by: + * - M-mode + mstatus.MPRV=1 + mstatus.MPP=U-mode. + * - Execute HLV/HLVX/HSV from HS-mode + hstatus.SPVP=0. + * - U-mode. + * - VU-mode. + * - Execute HLV/HLVX/HSV from U-mode + hstatus.HU=1. + */ + if (mode != PRV_U) { + return false; + } + + return true; +} + +static bool check_svukte_addr(CPURISCVState *env, vaddr addr) +{ + /* svukte extension excludes RV32 */ + uint32_t sxlen = 32 * riscv_cpu_sxl(env); + uint64_t high_bit = addr & (1UL << (sxlen - 1)); + return !high_bit; +} + /* * get_physical_address - get the physical address for this virtual address * @@ -894,6 +943,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmuidx_priv(mmu_idx); + bool virt = mmuidx_2stage(mmu_idx); bool use_background = false; hwaddr ppn; int napot_bits = 0; @@ -901,6 +951,11 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, bool is_sstack_idx = ((mmu_idx & MMU_IDX_SS_WRITE) == MMU_IDX_SS_WRITE); bool sstack_page = false; + if (do_svukte_check(env, first_stage, mode, virt) && + !check_svukte_addr(env, addr)) { + return TRANSLATE_FAIL; + } + /* * Check if we should use the background registers for the two * stage translation. We don't need to check if we actually need From 093c613cb69aa06bf38ce94e3261a0f44f266393 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Tue, 3 Dec 2024 11:49:31 +0800 Subject: [PATCH 0536/2892] target/riscv: Expose svukte ISA extension Add "svukte" in the ISA string when svukte extension is enabled. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Jim Shu Reviewed-by: Alistair Francis Message-ID: <20241203034932.25185-6-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 66e00ed260..18f4d94b6e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -199,6 +199,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot), ISA_EXT_DATA_ENTRY(svpbmt, PRIV_VERSION_1_12_0, ext_svpbmt), + ISA_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte), ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba), ISA_EXT_DATA_ENTRY(xtheadbb, PRIV_VERSION_1_11_0, ext_xtheadbb), @@ -1663,6 +1664,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { /* These are experimental so mark with 'x-' */ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { + MULTI_EXT_CFG_BOOL("x-svukte", ext_svukte, false), DEFINE_PROP_END_OF_LIST(), }; From 19d476ff13afbad04c52efb2dff0362839ae510a Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Tue, 3 Dec 2024 11:49:32 +0800 Subject: [PATCH 0537/2892] target/riscv: Check svukte is not enabled in RV32 The spec explicitly says svukte doesn't support RV32. So check that it is not enabled in RV32. Signed-off-by: Fea.Wang Reviewed-by: Alistair Francis Message-ID: <20241203034932.25185-7-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index c62c221696..3b99c8c9e3 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -652,6 +652,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if (mcc->misa_mxl_max == MXL_RV32 && cpu->cfg.ext_svukte) { + error_setg(errp, "svukte is not supported for RV32"); + return; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. From 04480a0e22d7c2a82289a393cd9b7ab4c193f2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 3 Dec 2024 21:08:27 +0100 Subject: [PATCH 0538/2892] target/riscv: Include missing headers in 'vector_internals.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than relying on implicit includes, explicit them, in order to avoid when refactoring unrelated headers: target/riscv/vector_internals.h:36:12: error: call to undeclared function 'FIELD_EX32'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 36 | return FIELD_EX32(simd_data(desc), VDATA, NF); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241203200828.47311-2-philmd@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/vector_internals.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/vector_internals.h b/target/riscv/vector_internals.h index 9e1e15b575..a11cc8366d 100644 --- a/target/riscv/vector_internals.h +++ b/target/riscv/vector_internals.h @@ -20,6 +20,7 @@ #define TARGET_RISCV_VECTOR_INTERNALS_H #include "qemu/bitops.h" +#include "hw/registerfields.h" #include "cpu.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" From fcea54c21261af715a79aece39add80e725cdcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 3 Dec 2024 21:08:28 +0100 Subject: [PATCH 0539/2892] target/riscv: Include missing headers in 'internals.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than relying on implicit includes, explicit them, in order to avoid when refactoring unrelated headers: target/riscv/internals.h:49:15: error: use of undeclared identifier 'PRV_S' 49 | ret = PRV_S; | ^ target/riscv/internals.h:93:9: error: call to undeclared function 'env_archcpu'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 93 | if (env_archcpu(env)->cfg.ext_zfinx) { | ^ target/riscv/internals.h:101:15: error: unknown type name 'float32'; did you mean 'float'? 101 | static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) | ^~~~~~~ | float Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241203200828.47311-3-philmd@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/internals.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index ddbdee885b..76934eaa7b 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -19,7 +19,10 @@ #ifndef RISCV_CPU_INTERNALS_H #define RISCV_CPU_INTERNALS_H +#include "exec/cpu-common.h" #include "hw/registerfields.h" +#include "fpu/softfloat-types.h" +#include "target/riscv/cpu_bits.h" /* * The current MMU Modes are: From 7e4f75cadf44ee67809c7ca82645a289a5268966 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 13 Nov 2024 14:17:47 -0300 Subject: [PATCH 0540/2892] target/riscv/tcg: hide warn for named feats when disabling via priv_ver Commit 68c9e54bea handled a situation where a warning was being shown when using the 'sifive_e' cpu when disabling the named extension zic64b. It makes little sense to show user warnings for named extensions that users can't control, and the solution taken was to disable zic64b manually in riscv_cpu_update_named_features(). This solution won't scale well when adding more named features, and can eventually end up repeating riscv_cpu_disable_priv_spec_isa_exts(). Change riscv_cpu_disable_priv_spec_isa_exts() to not show warnings when disabling a named feature. This will accomplish the same thing we're doing today while avoiding having two points where we're disabling exts via priv_ver mismatch. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241113171755.978109-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 3b99c8c9e3..48a55ba1d8 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -304,6 +304,15 @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) } isa_ext_update_enabled(cpu, edata->ext_enable_offset, false); + + /* + * Do not show user warnings for named features that users + * can't enable/disable in the command line. See commit + * 68c9e54bea for more info. + */ + if (cpu_cfg_offset_is_named_feat(edata->ext_enable_offset)) { + continue; + } #ifndef CONFIG_USER_ONLY warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx " because privilege spec version does not match", @@ -331,11 +340,9 @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.has_priv_1_13 = true; } - /* zic64b is 1.12 or later */ cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && cpu->cfg.cbop_blocksize == 64 && - cpu->cfg.cboz_blocksize == 64 && - cpu->cfg.has_priv_1_12; + cpu->cfg.cboz_blocksize == 64; } static void riscv_cpu_validate_g(RISCVCPU *cpu) From c3de19c0cc02fc19a12e70521be907416c0d2643 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 13 Nov 2024 14:17:48 -0300 Subject: [PATCH 0541/2892] target/riscv: add ssstateen ssstateen is defined in RVA22 as: "Supervisor-mode view of the state-enable extension. The supervisor-mode (sstateen0-3) and hypervisor-mode (hstateen0-3) state-enable registers must be provided." Add ssstateen as a named feature that is available if we also have smstateen. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20241113171755.978109-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 9 ++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 18f4d94b6e..d7b830d489 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -191,6 +191,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), @@ -1677,6 +1678,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { */ const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), + MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index d8771ca641..a1457ab4f4 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -140,6 +140,7 @@ struct RISCVCPUConfig { /* Named features */ bool ext_svade; bool ext_zic64b; + bool ext_ssstateen; /* * Always 'true' booleans for named features diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 48a55ba1d8..cbf2cf1963 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -204,10 +204,15 @@ static void riscv_cpu_enable_named_feat(RISCVCPU *cpu, uint32_t feat_offset) * All other named features are already enabled * in riscv_tcg_cpu_instance_init(). */ - if (feat_offset == CPU_CFG_OFFSET(ext_zic64b)) { + switch (feat_offset) { + case CPU_CFG_OFFSET(ext_zic64b): cpu->cfg.cbom_blocksize = 64; cpu->cfg.cbop_blocksize = 64; cpu->cfg.cboz_blocksize = 64; + break; + case CPU_CFG_OFFSET(ext_ssstateen): + cpu->cfg.ext_smstateen = true; + break; } } @@ -343,6 +348,8 @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && cpu->cfg.cbop_blocksize == 64 && cpu->cfg.cboz_blocksize == 64; + + cpu->cfg.ext_ssstateen = cpu->cfg.ext_smstateen; } static void riscv_cpu_validate_g(RISCVCPU *cpu) From 2fc8f50eadad5dcc99bc5de1333808b9de47a097 Mon Sep 17 00:00:00 2001 From: MollyChen Date: Thu, 5 Dec 2024 07:36:20 +0000 Subject: [PATCH 0542/2892] target/riscv: add support for RV64 Xiangshan Nanhu CPU Add a CPU entry for the RV64 XiangShan NANHU CPU which supports single-core and dual-core configurations. More details can be found at https://docs.xiangshan.cc/zh-cn/latest/integration/overview Signed-off-by: MollyChen Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20241205073622.46052-1-xiaoou@iscas.ac.cn> [ Changes by AF - Fixup code formatting ] Signed-off-by: Alistair Francis --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 6547642287..d56b067bf2 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -50,6 +50,7 @@ #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") +#define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d7b830d489..58bb5196a8 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -647,6 +647,34 @@ static void rv64_tt_ascalon_cpu_init(Object *obj) #endif } +static void rv64_xiangshan_nanhu_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVG | RVC | RVB | RVS | RVU); + env->priv_ver = PRIV_VERSION_1_12_0; + + /* Enable ISA extensions */ + cpu->cfg.ext_zbc = true; + cpu->cfg.ext_zbkb = true; + cpu->cfg.ext_zbkc = true; + cpu->cfg.ext_zbkx = true; + cpu->cfg.ext_zknd = true; + cpu->cfg.ext_zkne = true; + cpu->cfg.ext_zknh = true; + cpu->cfg.ext_zksed = true; + cpu->cfg.ext_zksh = true; + cpu->cfg.ext_svinval = true; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_SV39); +#endif +} + #ifdef CONFIG_TCG static void rv128_base_cpu_init(Object *obj) { @@ -3056,6 +3084,8 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, + MXL_RV64, rv64_xiangshan_nanhu_cpu_init), #ifdef CONFIG_TCG DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), #endif /* CONFIG_TCG */ From a7e7066b9359f21b80b1ee08e9496c05aa1e618f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:40:30 +0000 Subject: [PATCH 0543/2892] hvf: arm: Ignore writes to CNTP_CTL_EL0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MacOS unconditionally disables interrupts of the physical timer on boot and then continues to use the virtual one. We don't really want to support a full physical timer emulation, so let's just ignore those writes. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Message-ID: <20230830161425.91946-5-graf@amazon.com> Signed-off-by: Philippe Mathieu-Daudé --- target/arm/hvf/hvf.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index ca7ea92774..d75e504dcd 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/log.h" #include "sysemu/runstate.h" #include "sysemu/hvf.h" @@ -184,6 +185,7 @@ void hvf_arm_init_debug(void) #define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4) #define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4) #define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1) +#define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1) #define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0) #define SYSREG_PMUSERENR_EL0 SYSREG(3, 3, 9, 14, 0) #define SYSREG_PMCNTENSET_EL0 SYSREG(3, 3, 9, 12, 1) @@ -1620,6 +1622,13 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_OSLAR_EL1: env->cp15.oslsr_el1 = val & 1; return 0; + case SYSREG_CNTP_CTL_EL0: + /* + * Guests should not rely on the physical counter, but macOS emits + * disable writes to it. Let it do so, but ignore the requests. + */ + qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CTL_EL0\n"); + return 0; case SYSREG_OSDLR_EL1: /* Dummy register */ return 0; From 678bf8f22a2cbbaea465d1ce181e4367cb9760ae Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 2 Nov 2024 13:17:35 +0100 Subject: [PATCH 0544/2892] log: Add separate debug option for logging invalid memory accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently -d guest_errors enables logging of different invalid actions by the guest such as misusing hardware, accessing missing features or invalid memory areas. The memory access logging can be quite verbose which obscures the other messages enabled by this debug switch so separate it by adding a new -d invalid_mem option to make it possible to control it independently of other guest error logs. Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-ID: <1bb0d0e91ba14aca13056df3b0a774f89cbf966c.1730549443.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- include/qemu/log.h | 1 + system/memory.c | 6 +++--- system/physmem.c | 2 +- util/log.c | 2 ++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/qemu/log.h b/include/qemu/log.h index e10e24cd4f..60da703e67 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -37,6 +37,7 @@ bool qemu_log_separate(void); #define LOG_PER_THREAD (1 << 20) #define CPU_LOG_TB_VPU (1 << 21) #define LOG_TB_OP_PLUGIN (1 << 22) +#define LOG_INVALID_MEM (1 << 23) /* Lock/unlock output. */ diff --git a/system/memory.c b/system/memory.c index 85f6834cb3..a789064fbf 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1412,7 +1412,7 @@ bool memory_region_access_valid(MemoryRegion *mr, { if (mr->ops->valid.accepts && !mr->ops->valid.accepts(mr->opaque, addr, size, is_write, attrs)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX ", size %u, region '%s', reason: rejected\n", is_write ? "write" : "read", addr, size, memory_region_name(mr)); @@ -1420,7 +1420,7 @@ bool memory_region_access_valid(MemoryRegion *mr, } if (!mr->ops->valid.unaligned && (addr & (size - 1))) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX ", size %u, region '%s', reason: unaligned\n", is_write ? "write" : "read", addr, size, memory_region_name(mr)); @@ -1434,7 +1434,7 @@ bool memory_region_access_valid(MemoryRegion *mr, if (size > mr->ops->valid.max_access_size || size < mr->ops->valid.min_access_size) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX ", size %u, region '%s', reason: invalid size " "(min:%u max:%u)\n", is_write ? "write" : "read", diff --git a/system/physmem.c b/system/physmem.c index dc1db3a384..4bc0228a50 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2745,7 +2745,7 @@ static bool flatview_access_allowed(MemoryRegion *mr, MemTxAttrs attrs, if (memory_region_is_ram(mr)) { return true; } - qemu_log_mask(LOG_GUEST_ERROR, + qemu_log_mask(LOG_INVALID_MEM, "Invalid access to non-RAM device at " "addr 0x%" HWADDR_PRIX ", size %" HWADDR_PRIu ", " "region '%s'\n", addr, len, memory_region_name(mr)); diff --git a/util/log.c b/util/log.c index 6219819855..b87d399e4c 100644 --- a/util/log.c +++ b/util/log.c @@ -503,6 +503,8 @@ const QEMULogItem qemu_log_items[] = { "open a separate log file per thread; filename must contain '%d'" }, { CPU_LOG_TB_VPU, "vpu", "include VPU registers in the 'cpu' logging" }, + { LOG_INVALID_MEM, "invalid_mem", + "log invalid memory accesses" }, { 0, NULL, NULL }, }; From e469b331cd1bb2a3d4a0b02b37390fd79947e225 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:50 -0500 Subject: [PATCH 0545/2892] qom: Add TYPE_CONTAINER macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide a macro for the container type across QEMU source tree, rather than hard code it every time. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Xu Message-ID: <20241121192202.4155849-2-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/stellaris.c | 2 +- include/qom/object.h | 1 + qom/container.c | 4 ++-- qom/object.c | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 7fc13d96c9..e31884b23e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1053,7 +1053,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) flash_size = (((board->dc0 & 0xffff) + 1) << 1) * 1024; sram_size = ((board->dc0 >> 18) + 1) * 1024; - soc_container = object_new("container"); + soc_container = object_new(TYPE_CONTAINER); object_property_add_child(OBJECT(ms), "soc", soc_container); /* Flash programming is done via the SCU, so pretend it is ROM. */ diff --git a/include/qom/object.h b/include/qom/object.h index a201c9712a..de02e16817 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -26,6 +26,7 @@ typedef struct InterfaceClass InterfaceClass; typedef struct InterfaceInfo InterfaceInfo; #define TYPE_OBJECT "object" +#define TYPE_CONTAINER "container" typedef struct ObjectProperty ObjectProperty; diff --git a/qom/container.c b/qom/container.c index 455e8410c6..cfec92a944 100644 --- a/qom/container.c +++ b/qom/container.c @@ -15,7 +15,7 @@ #include "qemu/module.h" static const TypeInfo container_info = { - .name = "container", + .name = TYPE_CONTAINER, .parent = TYPE_OBJECT, }; @@ -37,7 +37,7 @@ Object *container_get(Object *root, const char *path) for (i = 1; parts[i] != NULL; i++, obj = child) { child = object_resolve_path_component(obj, parts[i]); if (!child) { - child = object_new("container"); + child = object_new(TYPE_CONTAINER); object_property_add_child(obj, parts[i], child); object_unref(child); } diff --git a/qom/object.c b/qom/object.c index c7660f9a09..c9f8442b13 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1734,7 +1734,7 @@ Object *object_get_root(void) static Object *root; if (!root) { - root = object_new("container"); + root = object_new(TYPE_CONTAINER); } return root; @@ -1750,7 +1750,7 @@ Object *object_get_internal_root(void) static Object *internal_root; if (!internal_root) { - internal_root = object_new("container"); + internal_root = object_new(TYPE_CONTAINER); } return internal_root; From 6e1e04ef035cf687e868858ab6bd18a177e1b822 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:51 -0500 Subject: [PATCH 0546/2892] qom: New object_property_add_new_container() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To move towards explicit creations of containers, starting that by providing a helper for creating container objects. Signed-off-by: Peter Xu Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-3-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/qom/object.h | 12 ++++++++++++ qom/container.c | 14 +++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index de02e16817..95d6e064d9 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -2019,6 +2019,18 @@ int object_child_foreach_recursive(Object *obj, */ Object *container_get(Object *root, const char *path); +/** + * object_property_add_new_container: + * @obj: the parent object + * @name: the name of the parent object's property to add + * + * Add a newly created container object to a parent object. + * + * Returns: the newly created container object. Its reference count is 1, + * and the reference is owned by the parent object. + */ +Object *object_property_add_new_container(Object *obj, const char *name); + /** * object_property_help: * @name: the name of the property diff --git a/qom/container.c b/qom/container.c index cfec92a944..20ab74b0e8 100644 --- a/qom/container.c +++ b/qom/container.c @@ -24,6 +24,16 @@ static void container_register_types(void) type_register_static(&container_info); } +Object *object_property_add_new_container(Object *obj, const char *name) +{ + Object *child = object_new(TYPE_CONTAINER); + + object_property_add_child(obj, name, child); + object_unref(child); + + return child; +} + Object *container_get(Object *root, const char *path) { Object *obj, *child; @@ -37,9 +47,7 @@ Object *container_get(Object *root, const char *path) for (i = 1; parts[i] != NULL; i++, obj = child) { child = object_resolve_path_component(obj, parts[i]); if (!child) { - child = object_new(TYPE_CONTAINER); - object_property_add_child(obj, parts[i], child); - object_unref(child); + child = object_property_add_new_container(obj, parts[i]); } } From ff18687db3baed91691c78b9338945b038f62392 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:52 -0500 Subject: [PATCH 0547/2892] tests: Fix test-qdev-global-props on anonymous qdev realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test-qdev-global-props creates a few subprocesses and test things based on qdev realize(). One thing was overlooked since the start, that anonymous creations of qdev (then realize() the device) requires the machine object's presence, as all these devices need to be attached to QOM tree, by default to path "/machine/unattached". The test didn't crash simply because container_get() has an implicit semantic to silently create any missing container, hence what happened here is container_get() (when running these tests) will try to create containers at QOM path "/machine" on the fly. That's probably unexpected by the test, but worked like charm before. We're going to fix device_set_realized() soon, but before that make the test case prepared, by creating the machine object on its own. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Xu Message-ID: <20241121192202.4155849-4-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/test-qdev-global-props.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c index 1eb95d2429..d2f9800148 100644 --- a/tests/unit/test-qdev-global-props.c +++ b/tests/unit/test-qdev-global-props.c @@ -72,6 +72,26 @@ static const TypeInfo subclass_type = { .parent = TYPE_STATIC_PROPS, }; +/* + * Initialize a fake machine, being prepared for future tests. + * + * All the tests later (even if to be run in subprocesses.. which will + * inherit the global states of the parent process) will try to create qdev + * and realize the device. + * + * Realization of such anonymous qdev (with no parent object) requires both + * the machine object and its "unattached" container to be at least present. + */ +static void test_init_machine(void) +{ + /* This is a fake machine - it doesn't need to be a machine object */ + Object *machine = object_property_add_new_container( + object_get_root(), "machine"); + + /* This container must exist for anonymous qdevs to realize() */ + object_property_add_new_container(machine, "unattached"); +} + /* Test simple static property setting to default value */ static void test_static_prop_subprocess(void) { @@ -295,6 +315,8 @@ int main(int argc, char **argv) type_register_static(&nohotplug_type); type_register_static(&nondevice_type); + test_init_machine(); + g_test_add_func("/qdev/properties/static/default/subprocess", test_static_prop_subprocess); g_test_add_func("/qdev/properties/static/default", From d95f60593d82bc2578608153962bc01919de5b19 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:53 -0500 Subject: [PATCH 0548/2892] tests: Explicitly create containers in test_qom_partial_path() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop one use of container_get(), instead switch to the explicit function to create a container. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Xu Message-ID: <20241121192202.4155849-5-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/check-qom-proplist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index 79d4a8b89d..b48e890577 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -610,7 +610,7 @@ static void test_dummy_delchild(void) static void test_qom_partial_path(void) { Object *root = object_get_objects_root(); - Object *cont1 = container_get(root, "/cont1"); + Object *cont1 = object_property_add_new_container(root, "cont1"); Object *obj1 = object_new(TYPE_DUMMY); Object *obj2a = object_new(TYPE_DUMMY); Object *obj2b = object_new(TYPE_DUMMY); From 6de3c4917fcfe46f4ae9621c78f768c37a428fda Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:54 -0500 Subject: [PATCH 0549/2892] ppc/e500: Avoid abuse of container_get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit container_get() is going to become strict on not allowing to return a non-container. Switch the e500 user to use object_resolve_path_component() explicitly. Cc: Bharat Bhushan Cc: qemu-ppc@nongnu.org Reviewed-by: Cédric Le Goater Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Xu Message-ID: <20241121192202.4155849-6-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/ppce500.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 54071fc125..67b22537c5 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -418,8 +418,8 @@ static const VMStateDescription vmstate_ppce500_pci = { static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp) { PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d); - PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(), - "/e500-ccsr")); + PPCE500CCSRState *ccsr = CCSR( + object_resolve_path_component(qdev_get_machine(), "e500-ccsr")); memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", &ccsr->ccsr_space, 0, int128_get64(ccsr->ccsr_space.size)); From 7c03a17c8da54d30727b9bb4558cc298aaaaa99b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:55 -0500 Subject: [PATCH 0550/2892] hw/ppc: Explicitly create the drc container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU will start to not rely on implicit creations of containers soon. Make PPC drc devices follow by explicitly create the container whenever a drc device is realized, dropping container_get() calls. No functional change intended. Cc: Nicholas Piggin Cc: Daniel Henrique Barboza Cc: Harsh Prateek Bora Cc: qemu-ppc@nongnu.org Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-7-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/spapr_drc.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 1484e3209d..7868048bb9 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -27,7 +27,7 @@ #include "sysemu/reset.h" #include "trace.h" -#define DRC_CONTAINER_PATH "/dr-connector" +#define DRC_CONTAINER_PATH "dr-connector" #define DRC_INDEX_TYPE_SHIFT 28 #define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) @@ -514,6 +514,16 @@ static const VMStateDescription vmstate_spapr_drc = { } }; +static void drc_container_create(void) +{ + object_property_add_new_container(object_get_root(), DRC_CONTAINER_PATH); +} + +static Object *drc_container_get(void) +{ + return object_resolve_path_component(object_get_root(), DRC_CONTAINER_PATH); +} + static void drc_realize(DeviceState *d, Error **errp) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); @@ -529,7 +539,7 @@ static void drc_realize(DeviceState *d, Error **errp) * inaccessible by the guest, since lookups rely on this path * existing in the composition tree */ - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + root_container = drc_container_get(); child_name = object_get_canonical_path_component(OBJECT(drc)); trace_spapr_drc_realize_child(spapr_drc_index(drc), child_name); object_property_add_alias(root_container, link_name, @@ -543,12 +553,10 @@ static void drc_unrealize(DeviceState *d) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); g_autofree gchar *name = g_strdup_printf("%x", spapr_drc_index(drc)); - Object *root_container; trace_spapr_drc_unrealize(spapr_drc_index(drc)); vmstate_unregister(VMSTATE_IF(drc), &vmstate_spapr_drc, drc); - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - object_property_del(root_container, name); + object_property_del(drc_container_get(), name); } SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, @@ -585,6 +593,8 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) { DeviceClass *dk = DEVICE_CLASS(k); + drc_container_create(); + dk->realize = drc_realize; dk->unrealize = drc_unrealize; /* @@ -796,9 +806,8 @@ static const TypeInfo spapr_drc_pmem_info = { SpaprDrc *spapr_drc_by_index(uint32_t index) { Object *obj; - g_autofree gchar *name = g_strdup_printf("%s/%x", DRC_CONTAINER_PATH, - index); - obj = object_resolve_path(name, NULL); + g_autofree gchar *name = g_strdup_printf("%x", index); + obj = object_resolve_path_component(drc_container_get(), name); return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); } @@ -860,7 +869,7 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) /* aliases for all DRConnector objects will be rooted in QOM * composition tree at DRC_CONTAINER_PATH */ - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + root_container = drc_container_get(); object_property_iter_init(&iter, root_container); while ((prop = object_property_iter_next(&iter))) { @@ -953,7 +962,7 @@ void spapr_drc_reset_all(SpaprMachineState *spapr) ObjectProperty *prop; ObjectPropertyIterator iter; - drc_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + drc_container = drc_container_get(); restart: object_property_iter_init(&iter, drc_container); while ((prop = object_property_iter_next(&iter))) { From 5cfd38a2e7d531f45607944ceb34eee5bf2c0ab5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:56 -0500 Subject: [PATCH 0551/2892] qom: Create system containers explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always explicitly create QEMU system containers upfront. Root containers will be created when trying to fetch the root object the 1st time. They are: /objects /chardevs /backend Machine sub-containers will be created only until machine is being initialized. They are: /machine/unattached /machine/peripheral /machine/peripheral-anon Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-8-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine.c | 3 --- qom/object.c | 24 +++++++++++++++++++++++- system/vl.c | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index d970f753e3..3d734f8c18 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1229,9 +1229,6 @@ static void machine_initfn(Object *obj) MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - container_get(obj, "/peripheral"); - container_get(obj, "/peripheral-anon"); - ms->dump_guest_core = true; ms->mem_merge = (QEMU_MADV_MERGEABLE != QEMU_MADV_INVALID); ms->enable_graphics = true; diff --git a/qom/object.c b/qom/object.c index c9f8442b13..b4c52d055d 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1729,12 +1729,34 @@ const char *object_property_get_type(Object *obj, const char *name, Error **errp return prop->type; } +static const char *const root_containers[] = { + "chardevs", + "objects", + "backend" +}; + +static Object *object_root_initialize(void) +{ + Object *root = object_new(TYPE_CONTAINER); + int i; + + /* + * Create all QEMU system containers. "machine" and its sub-containers + * are only created when machine initializes (qemu_create_machine()). + */ + for (i = 0; i < ARRAY_SIZE(root_containers); i++) { + object_property_add_new_container(root, root_containers[i]); + } + + return root; +} + Object *object_get_root(void) { static Object *root; if (!root) { - root = object_new(TYPE_CONTAINER); + root = object_root_initialize(); } return root; diff --git a/system/vl.c b/system/vl.c index 09202b57e7..85fcc8f96e 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2113,6 +2113,21 @@ static void parse_memory_options(void) loc_pop(&loc); } +static const char *const machine_containers[] = { + "unattached", + "peripheral", + "peripheral-anon" +}; + +static void qemu_create_machine_containers(Object *machine) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(machine_containers); i++) { + object_property_add_new_container(machine, machine_containers[i]); + } +} + static void qemu_create_machine(QDict *qdict) { MachineClass *machine_class = select_machine(qdict, &error_fatal); @@ -2121,6 +2136,7 @@ static void qemu_create_machine(QDict *qdict) current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class))); object_property_add_child(object_get_root(), "machine", OBJECT(current_machine)); + qemu_create_machine_containers(OBJECT(current_machine)); object_property_add_child(container_get(OBJECT(current_machine), "/unattached"), "sysbus", OBJECT(sysbus_get_default())); From 63cda19446c5307cc05b965c203742a583fc5abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Nov 2024 17:32:16 +0000 Subject: [PATCH 0552/2892] target/i386/sev: Reduce system specific declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "system/confidential-guest-support.h" is not needed, remove it. Reorder #ifdef'ry to reduce declarations exposed on user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Message-Id: <20241218155913.72288-3-philmd@linaro.org> --- hw/i386/pc_sysfw.c | 2 +- target/i386/sev.h | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index ef80281d28..e6271e1020 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -36,7 +36,7 @@ #include "hw/qdev-properties.h" #include "hw/block/flash.h" #include "sysemu/kvm.h" -#include "sev.h" +#include "target/i386/sev.h" #define FLASH_SECTOR_SIZE 4096 diff --git a/target/i386/sev.h b/target/i386/sev.h index 858005a119..373669eaac 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -18,7 +18,17 @@ #include CONFIG_DEVICES /* CONFIG_SEV */ #endif -#include "exec/confidential-guest-support.h" +#if !defined(CONFIG_SEV) || defined(CONFIG_USER_ONLY) +#define sev_enabled() 0 +#define sev_es_enabled() 0 +#define sev_snp_enabled() 0 +#else +bool sev_enabled(void); +bool sev_es_enabled(void); +bool sev_snp_enabled(void); +#endif + +#if !defined(CONFIG_USER_ONLY) #define TYPE_SEV_COMMON "sev-common" #define TYPE_SEV_GUEST "sev-guest" @@ -45,18 +55,6 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; -#ifdef CONFIG_SEV -bool sev_enabled(void); -bool sev_es_enabled(void); -bool sev_snp_enabled(void); -#else -#define sev_enabled() 0 -#define sev_es_enabled() 0 -#define sev_snp_enabled() 0 -#endif - -uint32_t sev_get_cbit_position(void); -uint32_t sev_get_reduced_phys_bits(void); bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp); @@ -68,4 +66,9 @@ void sev_es_set_reset_vector(CPUState *cpu); void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size); +#endif /* !CONFIG_USER_ONLY */ + +uint32_t sev_get_cbit_position(void); +uint32_t sev_get_reduced_phys_bits(void); + #endif From 32cad1ffb81dcecf6f4a8af56d6e5892682839b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 3 Dec 2024 15:20:13 +0100 Subject: [PATCH 0553/2892] include: Rename sysemu/ -> system/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headers in include/sysemu/ are not only related to system *emulation*, they are also used by virtualization. Rename as system/ which is clearer. Files renamed manually then mechanical change using sed tool. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Tested-by: Lei Yang Message-Id: <20241203172445.28576-1-philmd@linaro.org> --- MAINTAINERS | 76 +++++++++---------- accel/accel-blocker.c | 2 +- accel/accel-system.c | 2 +- accel/dummy-cpus.c | 2 +- accel/hvf/hvf-accel-ops.c | 8 +- accel/hvf/hvf-all.c | 4 +- accel/kvm/kvm-accel-ops.c | 8 +- accel/kvm/kvm-all.c | 16 ++-- accel/kvm/kvm-cpus.h | 2 +- accel/qtest/qtest.c | 4 +- accel/stubs/kvm-stub.c | 2 +- accel/stubs/xen-stub.c | 2 +- accel/tcg/cpu-exec-common.c | 4 +- accel/tcg/cpu-exec.c | 6 +- accel/tcg/icount-common.c | 12 +-- accel/tcg/monitor.c | 6 +- accel/tcg/tb-maint.c | 2 +- accel/tcg/tcg-accel-ops-icount.c | 4 +- accel/tcg/tcg-accel-ops-mttcg.c | 6 +- accel/tcg/tcg-accel-ops-rr.c | 6 +- accel/tcg/tcg-accel-ops.c | 6 +- accel/tcg/tcg-accel-ops.h | 2 +- accel/tcg/tcg-all.c | 4 +- accel/tcg/translate-all.c | 6 +- accel/tcg/watchpoint.c | 4 +- accel/xen/xen-all.c | 6 +- audio/audio.c | 6 +- backends/cryptodev-builtin.c | 2 +- backends/cryptodev-lkcf.c | 2 +- backends/cryptodev-vhost-user.c | 4 +- backends/cryptodev-vhost.c | 4 +- backends/cryptodev.c | 4 +- backends/host_iommu_device.c | 2 +- backends/hostmem-epc.c | 2 +- backends/hostmem-file.c | 2 +- backends/hostmem-memfd.c | 2 +- backends/hostmem-ram.c | 2 +- backends/hostmem-shm.c | 2 +- backends/hostmem.c | 2 +- backends/iommufd.c | 2 +- backends/rng-builtin.c | 4 +- backends/rng-egd.c | 2 +- backends/rng-random.c | 4 +- backends/rng.c | 2 +- backends/spdm-socket.c | 2 +- backends/tpm/tpm_backend.c | 4 +- backends/tpm/tpm_emulator.c | 6 +- backends/tpm/tpm_int.h | 2 +- backends/tpm/tpm_passthrough.c | 4 +- backends/tpm/tpm_util.c | 4 +- backends/vhost-user.c | 4 +- block.c | 2 +- block/accounting.c | 2 +- block/backup.c | 2 +- block/blkdebug.c | 2 +- block/blkio.c | 2 +- block/blkreplay.c | 2 +- block/block-backend.c | 8 +- block/block-copy.c | 2 +- block/block-ram-registrar.c | 4 +- block/commit.c | 2 +- block/copy-before-write.c | 2 +- block/coroutines.h | 2 +- block/crypto.c | 2 +- block/export/export.c | 4 +- block/export/fuse.c | 2 +- block/export/virtio-blk-handler.h | 2 +- block/io.c | 4 +- block/io_uring.c | 2 +- block/iscsi.c | 4 +- block/linux-aio.c | 2 +- block/meson.build | 6 +- block/mirror.c | 2 +- block/monitor/block-hmp-cmds.c | 6 +- block/nfs.c | 2 +- block/null.c | 2 +- block/nvme.c | 4 +- block/parallels.c | 2 +- block/{qapi-sysemu.c => qapi-system.c} | 4 +- block/qapi.c | 2 +- block/qcow.c | 2 +- block/qcow2-snapshot.c | 2 +- block/qcow2.c | 2 +- block/qed.c | 2 +- block/rbd.c | 2 +- block/replication.c | 2 +- block/snapshot-access.c | 2 +- block/snapshot.c | 2 +- block/stream.c | 2 +- block/throttle-groups.c | 4 +- block/vdi.c | 2 +- block/vhdx.c | 2 +- block/vmdk.c | 2 +- block/vpc.c | 2 +- blockdev-nbd.c | 4 +- blockdev.c | 12 +-- blockjob.c | 2 +- chardev/char-fe.c | 2 +- chardev/char-mux.c | 2 +- chardev/char.c | 2 +- cpu-common.c | 2 +- cpu-target.c | 4 +- crypto/akcipher-gcrypt.c.inc | 2 +- crypto/akcipher-nettle.c.inc | 2 +- docs/specs/tpm.rst | 8 +- dump/dump.c | 6 +- dump/win_dump.c | 2 +- dump/win_dump.h | 2 +- event-loop-base.c | 2 +- gdbstub/gdbstub.c | 4 +- gdbstub/syscalls.c | 2 +- gdbstub/system.c | 6 +- hw/9pfs/9p-synth.c | 2 +- hw/9pfs/virtio-9p-device.c | 2 +- hw/acpi/aml-build.c | 2 +- hw/acpi/core.c | 2 +- hw/acpi/cpu.c | 2 +- hw/acpi/erst.c | 2 +- hw/acpi/generic_event_device.c | 2 +- hw/acpi/hmat.c | 2 +- hw/acpi/hmat.h | 2 +- hw/acpi/ich9.c | 4 +- hw/acpi/ich9_tco.c | 2 +- hw/acpi/piix4.c | 6 +- hw/acpi/vmgenid.c | 2 +- hw/arm/allwinner-a10.c | 2 +- hw/arm/allwinner-h3.c | 2 +- hw/arm/allwinner-r40.c | 2 +- hw/arm/armv7m.c | 2 +- hw/arm/aspeed.c | 6 +- hw/arm/aspeed_ast10x0.c | 2 +- hw/arm/aspeed_ast2400.c | 2 +- hw/arm/aspeed_ast2600.c | 2 +- hw/arm/aspeed_ast27x0.c | 2 +- hw/arm/bcm2835_peripherals.c | 2 +- hw/arm/boot.c | 12 +-- hw/arm/digic.c | 2 +- hw/arm/digic_boards.c | 2 +- hw/arm/exynos4210.c | 4 +- hw/arm/fby35.c | 4 +- hw/arm/fsl-imx25.c | 2 +- hw/arm/fsl-imx31.c | 2 +- hw/arm/fsl-imx6.c | 2 +- hw/arm/fsl-imx6ul.c | 2 +- hw/arm/fsl-imx7.c | 2 +- hw/arm/highbank.c | 4 +- hw/arm/imx25_pdk.c | 2 +- hw/arm/integratorcp.c | 4 +- hw/arm/kzm.c | 4 +- hw/arm/mcimx6ul-evk.c | 2 +- hw/arm/mcimx7d-sabre.c | 2 +- hw/arm/microbit.c | 2 +- hw/arm/mps2-tz.c | 4 +- hw/arm/mps2.c | 2 +- hw/arm/mps3r.c | 2 +- hw/arm/msf2-soc.c | 2 +- hw/arm/musca.c | 2 +- hw/arm/musicpal.c | 8 +- hw/arm/npcm7xx.c | 2 +- hw/arm/npcm7xx_boards.c | 6 +- hw/arm/omap1.c | 12 +-- hw/arm/omap_sx1.c | 2 +- hw/arm/raspi4b.c | 2 +- hw/arm/realview.c | 2 +- hw/arm/sabrelite.c | 2 +- hw/arm/sbsa-ref.c | 10 +-- hw/arm/stellaris.c | 2 +- hw/arm/stm32f100_soc.c | 2 +- hw/arm/stm32f205_soc.c | 2 +- hw/arm/stm32f405_soc.c | 2 +- hw/arm/stm32l4x5_soc.c | 2 +- hw/arm/strongarm.c | 4 +- hw/arm/versatilepb.c | 2 +- hw/arm/vexpress.c | 4 +- hw/arm/virt-acpi-build.c | 6 +- hw/arm/virt.c | 16 ++-- hw/arm/xen-pvh.c | 2 +- hw/arm/xen-stubs.c | 2 +- hw/arm/xilinx_zynq.c | 4 +- hw/arm/xlnx-versal-virt.c | 2 +- hw/arm/xlnx-versal.c | 4 +- hw/arm/xlnx-zcu102.c | 2 +- hw/arm/xlnx-zynqmp.c | 4 +- hw/audio/ac97.c | 2 +- hw/audio/es1370.c | 2 +- hw/audio/intel-hda.c | 2 +- hw/audio/virtio-snd.c | 2 +- hw/avr/atmega.c | 2 +- hw/block/block.c | 4 +- hw/block/dataplane/xen-block.c | 4 +- hw/block/dataplane/xen-block.h | 2 +- hw/block/fdc-isa.c | 6 +- hw/block/fdc.c | 6 +- hw/block/hd-geometry.c | 2 +- hw/block/m25p80.c | 2 +- hw/block/nand.c | 2 +- hw/block/pflash_cfi01.c | 6 +- hw/block/pflash_cfi02.c | 2 +- hw/block/swim.c | 2 +- hw/block/vhost-user-blk.c | 4 +- hw/block/virtio-blk.c | 8 +- hw/block/xen-block.c | 6 +- hw/char/goldfish_tty.c | 2 +- hw/char/parallel-isa.c | 2 +- hw/char/parallel.c | 4 +- hw/char/riscv_htif.c | 4 +- hw/char/serial-isa.c | 2 +- hw/char/serial.c | 4 +- hw/char/xen_console.c | 2 +- hw/core/clock.c | 2 +- hw/core/cpu-common.c | 4 +- hw/core/{cpu-sysemu.c => cpu-system.c} | 2 +- hw/core/generic-loader.c | 4 +- hw/core/guest-loader.c | 4 +- hw/core/loader-fit.c | 2 +- hw/core/loader.c | 6 +- hw/core/machine-hmp-cmds.c | 2 +- hw/core/machine-qmp-cmds.c | 10 +-- hw/core/machine.c | 14 ++-- hw/core/meson.build | 2 +- hw/core/numa.c | 6 +- hw/core/ptimer.c | 4 +- hw/core/qdev-properties-system.c | 4 +- hw/core/reset.c | 2 +- hw/core/sysbus-fdt.c | 4 +- hw/core/vm-change-state-handler.c | 2 +- hw/cpu/a15mpcore.c | 2 +- hw/cxl/cxl-host.c | 2 +- hw/cxl/cxl-mailbox-utils.c | 2 +- hw/display/cirrus_vga.c | 2 +- hw/display/qxl-render.c | 2 +- hw/display/qxl.c | 2 +- hw/display/ramfb.c | 2 +- hw/display/vga.c | 2 +- hw/display/virtio-gpu-gl.c | 2 +- hw/display/virtio-gpu-udmabuf.c | 2 +- hw/display/virtio-gpu.c | 6 +- hw/display/xenfb.c | 2 +- hw/dma/pl330.c | 2 +- hw/dma/sifive_pdma.c | 2 +- hw/dma/sparc32_dma.c | 2 +- hw/dma/xilinx_axidma.c | 2 +- hw/dma/xlnx-zynq-devcfg.c | 2 +- hw/dma/xlnx_csu_dma.c | 2 +- hw/gpio/gpio_pwr.c | 2 +- hw/hppa/machine.c | 8 +- hw/hyperv/hv-balloon.c | 6 +- hw/hyperv/hyperv.c | 2 +- hw/i386/acpi-build.c | 8 +- hw/i386/fw_cfg.c | 2 +- hw/i386/intel_iommu.c | 6 +- hw/i386/kvm/apic.c | 4 +- hw/i386/kvm/clock.c | 6 +- hw/i386/kvm/i8254.c | 4 +- hw/i386/kvm/i8259.c | 2 +- hw/i386/kvm/ioapic.c | 2 +- hw/i386/kvm/xen_evtchn.c | 4 +- hw/i386/kvm/xen_gnttab.c | 4 +- hw/i386/kvm/xen_overlay.c | 4 +- hw/i386/kvm/xen_primary_console.c | 4 +- hw/i386/kvm/xen_xenstore.c | 4 +- hw/i386/microvm-dt.c | 2 +- hw/i386/microvm.c | 10 +-- hw/i386/multiboot.c | 2 +- hw/i386/nitro_enclave.c | 2 +- hw/i386/pc.c | 8 +- hw/i386/pc_piix.c | 8 +- hw/i386/pc_q35.c | 6 +- hw/i386/pc_sysfw.c | 4 +- hw/i386/port92.c | 2 +- hw/i386/sgx.c | 4 +- hw/i386/vapic.c | 10 +-- hw/i386/vmport.c | 6 +- hw/i386/x86-common.c | 6 +- hw/i386/x86-cpu.c | 6 +- hw/i386/x86-iommu.c | 2 +- hw/i386/x86.c | 4 +- hw/i386/xen/xen-pvh.c | 2 +- hw/i386/xen/xen_platform.c | 4 +- hw/ide/ahci-allwinner.c | 2 +- hw/ide/ahci.c | 4 +- hw/ide/atapi.c | 2 +- hw/ide/cmd646.c | 4 +- hw/ide/core.c | 12 +-- hw/ide/ich.c | 2 +- hw/ide/ide-bus.c | 6 +- hw/ide/ide-dev.c | 6 +- hw/ide/isa.c | 2 +- hw/ide/macio.c | 4 +- hw/ide/mmio.c | 2 +- hw/ide/pci.c | 2 +- hw/ide/via.c | 2 +- hw/input/pckbd.c | 4 +- hw/input/ps2.c | 4 +- hw/intc/apic.c | 2 +- hw/intc/apic_common.c | 2 +- hw/intc/arm_gic.c | 4 +- hw/intc/arm_gic_common.c | 2 +- hw/intc/arm_gic_kvm.c | 2 +- hw/intc/arm_gicv2m.c | 2 +- hw/intc/arm_gicv3_common.c | 2 +- hw/intc/arm_gicv3_cpuif.c | 4 +- hw/intc/arm_gicv3_its_common.c | 2 +- hw/intc/arm_gicv3_its_kvm.c | 4 +- hw/intc/arm_gicv3_kvm.c | 4 +- hw/intc/armv7m_nvic.c | 4 +- hw/intc/ioapic.c | 4 +- hw/intc/mips_gic.c | 4 +- hw/intc/openpic_kvm.c | 2 +- hw/intc/pnv_xive.c | 6 +- hw/intc/pnv_xive2.c | 8 +- hw/intc/riscv_aplic.c | 4 +- hw/intc/riscv_imsic.c | 4 +- hw/intc/s390_flic_kvm.c | 2 +- hw/intc/sifive_plic.c | 2 +- hw/intc/spapr_xive.c | 4 +- hw/intc/spapr_xive_kvm.c | 6 +- hw/intc/xics.c | 4 +- hw/intc/xics_kvm.c | 2 +- hw/intc/xive.c | 6 +- hw/intc/xive2.c | 4 +- hw/ipmi/ipmi.c | 2 +- hw/ipmi/ipmi_bmc_sim.c | 2 +- hw/isa/isa-bus.c | 2 +- hw/isa/isa-superio.c | 2 +- hw/isa/lpc_ich9.c | 4 +- hw/isa/piix.c | 2 +- hw/loongarch/acpi-build.c | 6 +- hw/loongarch/boot.c | 4 +- hw/loongarch/fw_cfg.c | 2 +- hw/loongarch/virt.c | 20 ++--- hw/m68k/an5206.c | 2 +- hw/m68k/mcf5206.c | 2 +- hw/m68k/mcf5208.c | 4 +- hw/m68k/next-cube.c | 4 +- hw/m68k/q800.c | 8 +- hw/m68k/virt.c | 8 +- hw/mem/cxl_type3.c | 4 +- hw/mem/memory-device.c | 2 +- hw/mem/nvdimm.c | 2 +- hw/mem/pc-dimm.c | 4 +- hw/mem/sparse-mem.c | 2 +- hw/microblaze/boot.c | 4 +- hw/microblaze/petalogix_ml605_mmu.c | 2 +- hw/microblaze/petalogix_s3adsp1800_mmu.c | 2 +- hw/mips/boston.c | 10 +-- hw/mips/cps.c | 4 +- hw/mips/fuloong2e.c | 6 +- hw/mips/jazz.c | 6 +- hw/mips/loongson3_virt.c | 8 +- hw/mips/malta.c | 8 +- hw/mips/mips_int.c | 2 +- hw/mips/mipssim.c | 6 +- hw/misc/arm_sysctl.c | 2 +- hw/misc/bcm2835_powermgt.c | 2 +- hw/misc/bcm2835_property.c | 2 +- hw/misc/debugexit.c | 2 +- hw/misc/exynos4210_pmu.c | 2 +- hw/misc/imx7_snvs.c | 6 +- hw/misc/iotkit-sysctl.c | 2 +- hw/misc/ivshmem.c | 4 +- hw/misc/lasi.c | 4 +- hw/misc/mac_via.c | 6 +- hw/misc/macio/cuda.c | 4 +- hw/misc/macio/mac_dbdma.c | 2 +- hw/misc/macio/pmu.c | 4 +- hw/misc/npcm7xx_clk.c | 2 +- hw/misc/pci-testdev.c | 2 +- hw/misc/pvpanic-isa.c | 2 +- hw/misc/pvpanic-pci.c | 2 +- hw/misc/pvpanic.c | 2 +- hw/misc/sbsa_ec.c | 2 +- hw/misc/sifive_e_aon.c | 2 +- hw/misc/sifive_test.c | 4 +- hw/misc/sifive_u_otp.c | 4 +- hw/misc/slavio_misc.c | 2 +- hw/misc/virt_ctrl.c | 2 +- hw/misc/vmcoreinfo.c | 2 +- hw/misc/zynq_slcr.c | 2 +- hw/net/allwinner-sun8i-emac.c | 2 +- hw/net/cadence_gem.c | 2 +- hw/net/e1000.c | 4 +- hw/net/e1000e.c | 2 +- hw/net/e1000e_core.c | 2 +- hw/net/eepro100.c | 6 +- hw/net/ftgmac100.c | 2 +- hw/net/igb.c | 2 +- hw/net/igb_core.c | 2 +- hw/net/imx_fec.c | 2 +- hw/net/lance.c | 2 +- hw/net/lasi_i82596.c | 2 +- hw/net/mv88w8618_eth.c | 2 +- hw/net/ne2000-isa.c | 2 +- hw/net/ne2000-pci.c | 2 +- hw/net/npcm7xx_emc.c | 2 +- hw/net/npcm_gmac.c | 2 +- hw/net/pcnet-pci.c | 4 +- hw/net/rtl8139.c | 4 +- hw/net/spapr_llan.c | 2 +- hw/net/sungem.c | 2 +- hw/net/sunhme.c | 2 +- hw/net/tulip.c | 2 +- hw/net/virtio-net.c | 6 +- hw/net/vmxnet3.c | 2 +- hw/nvme/ctrl.c | 8 +- hw/nvme/dif.c | 2 +- hw/nvme/ns.c | 4 +- hw/nvram/chrp_nvram.c | 2 +- hw/nvram/eeprom_at24c.c | 2 +- hw/nvram/fw_cfg.c | 6 +- hw/nvram/mac_nvram.c | 2 +- hw/nvram/spapr_nvram.c | 8 +- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-efuse.c | 2 +- hw/openrisc/boot.c | 6 +- hw/openrisc/cputimer.c | 2 +- hw/openrisc/openrisc_sim.c | 8 +- hw/openrisc/virt.c | 8 +- hw/pci-bridge/pci_expander_bridge.c | 2 +- hw/pci-host/bonito.c | 2 +- hw/pci-host/pnv_phb.c | 2 +- hw/pci-host/pnv_phb3.c | 2 +- hw/pci-host/pnv_phb3_msi.c | 2 +- hw/pci-host/pnv_phb4_pec.c | 2 +- hw/pci-host/ppc4xx_pci.c | 2 +- hw/pci-host/sabre.c | 2 +- hw/pci/msi.c | 2 +- hw/pci/msix.c | 2 +- hw/pci/pci.c | 6 +- hw/ppc/amigaone.c | 4 +- hw/ppc/e500.c | 12 +-- hw/ppc/e500plat.c | 4 +- hw/ppc/mac_newworld.c | 6 +- hw/ppc/mac_oldworld.c | 6 +- hw/ppc/mpc8544_guts.c | 2 +- hw/ppc/mpc8544ds.c | 2 +- hw/ppc/pef.c | 2 +- hw/ppc/pegasos2.c | 10 +-- hw/ppc/pnv.c | 16 ++-- hw/ppc/pnv_chiptod.c | 2 +- hw/ppc/pnv_core.c | 2 +- hw/ppc/pnv_homer.c | 2 +- hw/ppc/pnv_i2c.c | 2 +- hw/ppc/pnv_pnor.c | 4 +- hw/ppc/pnv_psi.c | 2 +- hw/ppc/pnv_xscom.c | 2 +- hw/ppc/ppc.c | 8 +- hw/ppc/ppc405_boards.c | 6 +- hw/ppc/ppc405_uc.c | 4 +- hw/ppc/ppc440_bamboo.c | 8 +- hw/ppc/ppc440_uc.c | 2 +- hw/ppc/ppc_booke.c | 4 +- hw/ppc/ppce500_spin.c | 2 +- hw/ppc/prep.c | 4 +- hw/ppc/prep_systemio.c | 2 +- hw/ppc/sam460ex.c | 10 +-- hw/ppc/spapr.c | 20 ++--- hw/ppc/spapr_caps.c | 4 +- hw/ppc/spapr_cpu_core.c | 10 +-- hw/ppc/spapr_drc.c | 4 +- hw/ppc/spapr_events.c | 4 +- hw/ppc/spapr_hcall.c | 6 +- hw/ppc/spapr_iommu.c | 4 +- hw/ppc/spapr_irq.c | 2 +- hw/ppc/spapr_pci.c | 8 +- hw/ppc/spapr_rng.c | 4 +- hw/ppc/spapr_rtas.c | 12 +-- hw/ppc/spapr_rtc.c | 4 +- hw/ppc/spapr_tpm_proxy.c | 2 +- hw/ppc/spapr_vio.c | 4 +- hw/ppc/spapr_vof.c | 2 +- hw/ppc/virtex_ml507.c | 6 +- hw/ppc/vof.c | 2 +- hw/remote/message.c | 4 +- hw/remote/mpqemu-link.c | 2 +- hw/remote/proxy.c | 2 +- hw/remote/remote-obj.c | 2 +- hw/remote/vfio-user-obj.c | 4 +- hw/riscv/boot.c | 8 +- hw/riscv/microchip_pfsoc.c | 4 +- hw/riscv/numa.c | 2 +- hw/riscv/opentitan.c | 2 +- hw/riscv/riscv_hart.c | 2 +- hw/riscv/shakti_c.c | 2 +- hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_u.c | 6 +- hw/riscv/spike.c | 4 +- hw/riscv/virt-acpi-build.c | 2 +- hw/riscv/virt.c | 12 +-- 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 | 4 +- hw/rtc/ls7a_rtc.c | 4 +- hw/rtc/m41t80.c | 2 +- hw/rtc/m48t59.c | 8 +- hw/rtc/mc146818rtc.c | 10 +-- hw/rtc/pl031.c | 4 +- hw/rtc/xlnx-zynqmp-rtc.c | 4 +- hw/rx/rx-gdbsim.c | 6 +- hw/rx/rx62n.c | 2 +- hw/s390x/ipl.c | 6 +- hw/s390x/s390-ccw.c | 2 +- hw/s390x/s390-pci-bus.c | 4 +- hw/s390x/s390-pci-inst.c | 2 +- hw/s390x/s390-skeys-kvm.c | 2 +- hw/s390x/s390-skeys.c | 4 +- hw/s390x/s390-stattrib-kvm.c | 2 +- hw/s390x/s390-virtio-ccw.c | 6 +- hw/s390x/sclpcpu.c | 2 +- hw/s390x/sclpquiesce.c | 2 +- hw/s390x/tod-kvm.c | 2 +- hw/s390x/tod-tcg.c | 2 +- hw/s390x/tod.c | 6 +- hw/s390x/virtio-ccw.c | 4 +- hw/scsi/lsi53c895a.c | 2 +- hw/scsi/megasas.c | 6 +- hw/scsi/mptendian.c | 2 +- hw/scsi/mptsas.c | 2 +- hw/scsi/scsi-bus.c | 10 +-- hw/scsi/scsi-disk.c | 10 +-- hw/scsi/scsi-generic.c | 2 +- hw/scsi/vhost-scsi.c | 2 +- hw/scsi/vhost-user-scsi.c | 2 +- hw/scsi/virtio-scsi-dataplane.c | 2 +- hw/scsi/virtio-scsi.c | 4 +- hw/sd/allwinner-sdhost.c | 4 +- hw/sd/bcm2835_sdhost.c | 2 +- hw/sd/pl181.c | 2 +- hw/sd/sd.c | 2 +- hw/sd/sdhci.c | 2 +- hw/sd/ssi-sd.c | 2 +- hw/sh4/r2d.c | 6 +- hw/sh4/sh7750.c | 2 +- hw/smbios/smbios.c | 2 +- hw/smbios/smbios_legacy.c | 2 +- hw/sparc/leon3.c | 6 +- hw/sparc/sun4m.c | 6 +- hw/sparc64/niagara.c | 6 +- hw/sparc64/sparc64.c | 2 +- hw/sparc64/sun4u.c | 4 +- hw/ssi/xilinx_spips.c | 2 +- hw/timer/a9gtimer.c | 2 +- hw/timer/pxa2xx_timer.c | 4 +- hw/tpm/tpm_crb.c | 8 +- hw/tpm/tpm_ppi.c | 2 +- hw/tpm/tpm_prop.h | 2 +- hw/tpm/tpm_spapr.c | 4 +- hw/tpm/tpm_tis.h | 2 +- hw/tpm/tpm_tis_common.c | 4 +- hw/ufs/lu.c | 2 +- hw/usb/bus-stub.c | 2 +- hw/usb/bus.c | 2 +- hw/usb/dev-network.c | 2 +- hw/usb/dev-storage-classic.c | 4 +- hw/usb/hcd-dwc2.h | 2 +- hw/usb/hcd-ehci.c | 2 +- hw/usb/hcd-ehci.h | 2 +- hw/usb/hcd-ohci.h | 2 +- hw/usb/hcd-uhci.c | 2 +- hw/usb/hcd-xhci.h | 2 +- hw/usb/host-libusb.c | 4 +- hw/usb/libhw.c | 2 +- hw/usb/redirect.c | 4 +- hw/vfio/ap.c | 2 +- hw/vfio/ccw.c | 2 +- hw/vfio/common.c | 8 +- hw/vfio/container.c | 2 +- hw/vfio/cpr.c | 2 +- hw/vfio/iommufd.c | 4 +- hw/vfio/migration.c | 2 +- hw/vfio/pci.c | 6 +- hw/vfio/pci.h | 2 +- hw/vfio/platform.c | 4 +- hw/vfio/spapr.c | 2 +- hw/virtio/vdpa-dev.c | 4 +- hw/virtio/vhost-user-fs.c | 2 +- hw/virtio/vhost-user-scsi-pci.c | 2 +- hw/virtio/vhost-user.c | 6 +- hw/virtio/vhost.c | 2 +- hw/virtio/virtio-balloon.c | 2 +- hw/virtio/virtio-crypto.c | 2 +- hw/virtio/virtio-iommu.c | 6 +- hw/virtio/virtio-mem.c | 8 +- hw/virtio/virtio-mmio.c | 4 +- hw/virtio/virtio-pci.c | 4 +- hw/virtio/virtio-pmem.c | 2 +- hw/virtio/virtio-rng.c | 4 +- hw/virtio/virtio.c | 4 +- hw/watchdog/allwinner-wdt.c | 2 +- hw/watchdog/cmsdk-apb-watchdog.c | 2 +- hw/watchdog/sbsa_gwdt.c | 4 +- hw/watchdog/watchdog.c | 4 +- hw/watchdog/wdt_aspeed.c | 2 +- hw/watchdog/wdt_diag288.c | 4 +- hw/watchdog/wdt_i6300esb.c | 2 +- hw/watchdog/wdt_ib700.c | 2 +- hw/watchdog/wdt_imx2.c | 2 +- hw/xen/xen-bus.c | 2 +- hw/xen/xen-mapcache.c | 4 +- hw/xen/xen-pvh-common.c | 6 +- hw/xen/xen_devconfig.c | 4 +- hw/xenpv/xen_machine_pv.c | 4 +- hw/xtensa/sim.c | 4 +- hw/xtensa/virt.c | 2 +- hw/xtensa/xtfpga.c | 8 +- include/exec/ram_addr.h | 4 +- include/hw/acpi/tpm.h | 2 +- include/hw/arm/allwinner-a10.h | 2 +- include/hw/arm/allwinner-h3.h | 2 +- include/hw/arm/allwinner-r40.h | 2 +- include/hw/arm/virt.h | 2 +- include/hw/boards.h | 4 +- include/hw/core/sysemu-cpu-ops.h | 6 +- include/hw/dma/xlnx-zdma.h | 2 +- include/hw/dma/xlnx_dpdma.h | 2 +- include/hw/hyperv/vmbus.h | 4 +- include/hw/i386/hostmem-epc.h | 2 +- include/hw/ide/ide-dev.h | 2 +- include/hw/isa/superio.h | 2 +- include/hw/nvram/fw_cfg.h | 2 +- include/hw/nvram/xlnx-bbram.h | 2 +- include/hw/nvram/xlnx-efuse.h | 2 +- include/hw/pci/pci.h | 4 +- include/hw/ppc/mac_dbdma.h | 2 +- include/hw/ppc/spapr.h | 2 +- include/hw/ppc/spapr_drc.h | 2 +- include/hw/ppc/spapr_vio.h | 2 +- include/hw/ppc/xive.h | 2 +- include/hw/riscv/numa.h | 2 +- include/hw/s390x/css.h | 2 +- include/hw/s390x/s390-pci-inst.h | 2 +- include/hw/tricore/triboard.h | 2 +- include/hw/vfio/vfio-common.h | 6 +- include/hw/virtio/virtio-balloon.h | 2 +- include/hw/virtio/virtio-blk.h | 6 +- include/hw/virtio/virtio-crypto.h | 4 +- include/hw/virtio/virtio-gpu.h | 2 +- include/hw/virtio/virtio-input.h | 2 +- include/hw/virtio/virtio-iommu.h | 2 +- include/hw/virtio/virtio-mem.h | 2 +- include/hw/virtio/virtio-rng.h | 2 +- include/hw/virtio/virtio-scsi.h | 2 +- include/hw/xen/xen-block.h | 2 +- include/hw/xen/xen-hvm-common.h | 8 +- include/qemu/main-loop.h | 2 +- include/qemu/osdep.h | 8 +- include/{sysemu => system}/accel-blocker.h | 2 +- include/{sysemu => system}/accel-ops.h | 0 include/{sysemu => system}/arch_init.h | 0 include/{sysemu => system}/balloon.h | 0 .../{sysemu => system}/block-backend-common.h | 0 .../block-backend-global-state.h | 0 include/{sysemu => system}/block-backend-io.h | 0 include/{sysemu => system}/block-backend.h | 0 .../{sysemu => system}/block-ram-registrar.h | 0 include/{sysemu => system}/blockdev.h | 0 include/{sysemu => system}/cpu-throttle.h | 6 +- .../{sysemu => system}/cpu-timers-internal.h | 0 include/{sysemu => system}/cpu-timers.h | 6 +- include/{sysemu => system}/cpus.h | 2 +- .../{sysemu => system}/cryptodev-vhost-user.h | 2 +- include/{sysemu => system}/cryptodev-vhost.h | 2 +- include/{sysemu => system}/cryptodev.h | 0 include/{sysemu => system}/device_tree.h | 0 include/{sysemu => system}/dirtylimit.h | 0 include/{sysemu => system}/dirtyrate.h | 0 include/{sysemu => system}/dma.h | 0 include/{sysemu => system}/dump-arch.h | 0 include/{sysemu => system}/dump.h | 4 +- include/{sysemu => system}/event-loop-base.h | 0 .../{sysemu => system}/host_iommu_device.h | 0 include/{sysemu => system}/hostmem.h | 6 +- include/{sysemu => system}/hvf.h | 0 include/{sysemu => system}/hvf_int.h | 0 include/{sysemu => system}/hw_accel.h | 8 +- include/{sysemu => system}/iommufd.h | 6 +- include/{sysemu => system}/iothread.h | 2 +- include/{sysemu => system}/kvm.h | 0 include/{sysemu => system}/kvm_int.h | 2 +- include/{sysemu => system}/kvm_xen.h | 6 +- include/{sysemu => system}/memory_mapping.h | 0 include/{sysemu => system}/numa.h | 4 +- include/{sysemu => system}/nvmm.h | 0 include/{sysemu => system}/os-posix.h | 0 include/{sysemu => system}/os-win32.h | 0 include/{sysemu => system}/qtest.h | 0 include/{sysemu => system}/replay.h | 4 +- include/{sysemu => system}/reset.h | 4 +- include/{sysemu => system}/rng-random.h | 0 include/{sysemu => system}/rng.h | 0 include/{sysemu => system}/rtc.h | 4 +- include/{sysemu => system}/runstate-action.h | 0 include/{sysemu => system}/runstate.h | 4 +- include/{sysemu => system}/seccomp.h | 0 include/{sysemu => system}/spdm-socket.h | 0 include/{sysemu => system}/stats.h | 0 include/{sysemu/sysemu.h => system/system.h} | 4 +- include/{sysemu => system}/tcg.h | 4 +- include/{sysemu => system}/tpm.h | 0 include/{sysemu => system}/tpm_backend.h | 2 +- include/{sysemu => system}/tpm_util.h | 8 +- .../{sysemu => system}/vhost-user-backend.h | 0 include/{sysemu => system}/watchdog.h | 0 include/{sysemu => system}/whpx.h | 0 include/{sysemu => system}/xen-mapcache.h | 2 +- include/{sysemu => system}/xen.h | 6 +- iothread.c | 4 +- migration/block-dirty-bitmap.c | 4 +- migration/colo.c | 6 +- migration/cpu-throttle.c | 4 +- migration/dirtyrate.c | 4 +- migration/dirtyrate.h | 2 +- migration/global_state.c | 2 +- migration/migration-hmp-cmds.c | 4 +- migration/migration.c | 14 ++-- migration/migration.h | 2 +- migration/multifd.c | 2 +- migration/options.c | 4 +- migration/postcopy-ram.c | 2 +- migration/ram.c | 8 +- migration/savevm.c | 12 +-- monitor/fds.c | 2 +- monitor/hmp-cmds-target.c | 2 +- monitor/hmp-cmds.c | 2 +- monitor/hmp-target.c | 2 +- monitor/hmp.c | 2 +- monitor/monitor-internal.h | 2 +- monitor/monitor.c | 2 +- monitor/qmp-cmds-control.c | 2 +- monitor/qmp-cmds.c | 10 +-- nbd/nbd-internal.h | 2 +- net/colo-compare.c | 2 +- net/dump.c | 2 +- net/filter-replay.c | 2 +- net/hub.c | 2 +- net/net.c | 2 +- net/slirp.c | 2 +- net/tap.c | 2 +- net/vmnet-common.m | 2 +- os-posix.c | 2 +- os-win32.c | 2 +- qemu-img.c | 2 +- qemu-io-cmds.c | 2 +- qemu-io.c | 2 +- qemu-nbd.c | 4 +- replay/replay-audio.c | 2 +- replay/replay-char.c | 2 +- replay/replay-debugging.c | 4 +- replay/replay-events.c | 2 +- replay/replay-input.c | 2 +- replay/replay-internal.c | 4 +- replay/replay-net.c | 2 +- replay/replay-random.c | 2 +- replay/replay-snapshot.c | 2 +- replay/replay-time.c | 2 +- replay/replay.c | 8 +- replay/stubs-system.c | 2 +- rust/wrapper.h | 2 +- scripts/clean-includes | 6 +- .../codeconverter/test_regexps.py | 4 +- scripts/coverity-scan/COMPONENTS.md | 4 +- stats/stats-qmp-cmds.c | 2 +- storage-daemon/qemu-storage-daemon.c | 2 +- stubs/blk-commit-all.c | 2 +- stubs/change-state-handler.c | 2 +- stubs/cpu-get-clock.c | 2 +- stubs/cpu-synchronize-state.c | 2 +- stubs/cpus-virtual-clock.c | 2 +- stubs/dump.c | 2 +- stubs/get-vm-name.c | 2 +- stubs/icount.c | 2 +- stubs/qemu-timer-notify-cb.c | 2 +- stubs/qtest.c | 2 +- stubs/replay-mode.c | 2 +- stubs/replay-tools.c | 2 +- stubs/runstate-check.c | 2 +- stubs/vm-stop.c | 2 +- system/arch_init.c | 2 +- system/balloon.c | 4 +- system/bootdevice.c | 4 +- system/cpu-timers.c | 10 +-- system/cpus.c | 12 +-- system/device_tree.c | 2 +- system/dirtylimit.c | 6 +- system/dma-helpers.c | 6 +- system/globals.c | 4 +- system/main.c | 2 +- system/memory.c | 6 +- system/memory_mapping.c | 2 +- system/physmem.c | 18 ++--- system/qdev-monitor.c | 6 +- system/qemu-seccomp.c | 2 +- system/qtest.c | 6 +- system/rtc.c | 6 +- system/runstate-action.c | 4 +- system/runstate.c | 16 ++-- system/tpm.c | 4 +- system/vl.c | 38 +++++----- target/alpha/sys_helper.c | 4 +- target/alpha/translate.c | 2 +- target/arm/arch_dump.c | 2 +- target/arm/arm-powerctl.c | 2 +- target/arm/cpu.c | 6 +- target/arm/cpu64.c | 8 +- target/arm/debug_helper.c | 2 +- target/arm/gdbstub.c | 2 +- target/arm/helper.c | 6 +- target/arm/hvf/hvf.c | 10 +-- target/arm/kvm.c | 8 +- target/arm/kvm_arm.h | 2 +- target/arm/machine.c | 4 +- target/arm/tcg/psci.c | 2 +- target/hppa/sys_helper.c | 4 +- target/i386/arch_dump.c | 4 +- target/i386/arch_memory_mapping.c | 2 +- target/i386/cpu-apic.c | 6 +- target/i386/cpu-internal.h | 2 +- target/i386/{cpu-sysemu.c => cpu-system.c} | 2 +- target/i386/cpu.c | 4 +- target/i386/cpu.h | 2 +- target/i386/helper.c | 4 +- target/i386/host-cpu.c | 2 +- target/i386/hvf/hvf-cpu.c | 4 +- target/i386/hvf/hvf.c | 8 +- target/i386/hvf/vmx.h | 4 +- target/i386/hvf/x86_cpuid.c | 2 +- target/i386/hvf/x86_task.c | 2 +- target/i386/hvf/x86hvf.c | 2 +- target/i386/kvm/hyperv.h | 2 +- target/i386/kvm/kvm-cpu.c | 2 +- target/i386/kvm/kvm.c | 8 +- target/i386/kvm/kvm_i386.h | 2 +- target/i386/kvm/xen-emu.c | 6 +- target/i386/machine.c | 6 +- target/i386/meson.build | 5 +- target/i386/nvmm/nvmm-accel-ops.c | 6 +- target/i386/nvmm/nvmm-accel-ops.h | 2 +- target/i386/nvmm/nvmm-all.c | 6 +- .../{sev-sysemu-stub.c => sev-system-stub.c} | 0 target/i386/sev.c | 6 +- target/i386/tcg/excp_helper.c | 2 +- target/i386/tcg/meson.build | 2 +- .../i386/tcg/{sysemu => system}/bpt_helper.c | 2 +- .../i386/tcg/{sysemu => system}/excp_helper.c | 2 +- .../i386/tcg/{sysemu => system}/fpu_helper.c | 2 +- .../i386/tcg/{sysemu => system}/meson.build | 0 .../i386/tcg/{sysemu => system}/misc_helper.c | 2 +- .../i386/tcg/{sysemu => system}/seg_helper.c | 2 +- .../i386/tcg/{sysemu => system}/smm_helper.c | 2 +- .../i386/tcg/{sysemu => system}/svm_helper.c | 2 +- target/i386/tcg/{sysemu => system}/tcg-cpu.c | 4 +- target/i386/tcg/translate.c | 2 +- target/i386/whpx/whpx-accel-ops.c | 6 +- target/i386/whpx/whpx-accel-ops.h | 2 +- target/i386/whpx/whpx-all.c | 6 +- target/i386/whpx/whpx-apic.c | 4 +- target/loongarch/arch_dump.c | 2 +- target/loongarch/cpu.c | 8 +- target/loongarch/kvm/kvm.c | 8 +- target/loongarch/machine.c | 2 +- target/mips/cpu.c | 4 +- target/mips/helper.h | 2 +- target/mips/kvm.c | 6 +- target/mips/meson.build | 2 +- target/mips/{sysemu => system}/addr.c | 0 target/mips/{sysemu => system}/cp0.c | 0 target/mips/{sysemu => system}/cp0_timer.c | 2 +- target/mips/{sysemu => system}/machine.c | 0 target/mips/{sysemu => system}/meson.build | 0 .../mips/{sysemu => system}/mips-qmp-cmds.c | 0 target/mips/{sysemu => system}/physaddr.c | 0 target/mips/tcg/meson.build | 2 +- .../mips/tcg/{sysemu => system}/cp0_helper.c | 0 .../mips/tcg/{sysemu => system}/lcsr_helper.c | 0 .../mips/tcg/{sysemu => system}/meson.build | 0 .../mips/tcg/{sysemu => system}/mips-semi.c | 0 .../tcg/{sysemu => system}/semihosting-stub.c | 0 .../tcg/{sysemu => system}/special_helper.c | 0 .../mips/tcg/{sysemu => system}/tlb_helper.c | 0 ...ysemu_helper.h.inc => system_helper.h.inc} | 2 +- target/mips/tcg/translate.c | 2 +- target/ppc/arch_dump.c | 4 +- target/ppc/compat.c | 6 +- target/ppc/cpu.c | 2 +- target/ppc/cpu_init.c | 6 +- target/ppc/excp_helper.c | 6 +- target/ppc/helper_regs.c | 4 +- target/ppc/kvm.c | 14 ++-- target/ppc/kvm_ppc.h | 2 +- target/ppc/machine.c | 6 +- target/ppc/mmu-hash32.c | 2 +- target/ppc/mmu-hash64.c | 2 +- target/ppc/mmu-radix64.c | 2 +- target/ppc/mmu_common.c | 2 +- target/ppc/mmu_helper.c | 2 +- target/riscv/arch_dump.c | 2 +- target/riscv/cpu.c | 6 +- target/riscv/cpu_helper.c | 2 +- target/riscv/csr.c | 2 +- target/riscv/debug.c | 2 +- target/riscv/kvm/kvm-cpu.c | 8 +- target/riscv/machine.c | 4 +- target/riscv/pmu.c | 4 +- target/riscv/riscv-qmp-cmds.c | 4 +- target/s390x/arch_dump.c | 2 +- target/s390x/cpu-dump.c | 2 +- target/s390x/{cpu-sysemu.c => cpu-system.c} | 18 ++--- target/s390x/cpu.c | 14 ++-- target/s390x/cpu_models.c | 6 +- ...pu_models_sysemu.c => cpu_models_system.c} | 4 +- target/s390x/diag.c | 4 +- target/s390x/gdbstub.c | 4 +- target/s390x/helper.c | 6 +- target/s390x/interrupt.c | 4 +- target/s390x/kvm/kvm.c | 10 +-- target/s390x/kvm/pv.c | 4 +- target/s390x/kvm/pv.h | 2 +- target/s390x/machine.c | 4 +- target/s390x/meson.build | 4 +- target/s390x/mmu_helper.c | 4 +- target/s390x/s390x-internal.h | 6 +- target/s390x/sigp.c | 6 +- target/s390x/tcg/misc_helper.c | 4 +- target/s390x/trace-events | 2 +- target/sh4/helper.c | 2 +- target/sparc/int32_helper.c | 2 +- tests/qtest/fuzz/fuzz.c | 6 +- tests/qtest/tpm-emu.h | 2 +- tests/qtest/vhost-user-test.c | 2 +- tests/unit/test-bdrv-drain.c | 2 +- tests/unit/test-bdrv-graph-mod.c | 2 +- tests/unit/test-block-backend.c | 2 +- tests/unit/test-block-iothread.c | 2 +- tests/unit/test-blockjob-txn.c | 2 +- tests/unit/test-blockjob.c | 2 +- tests/unit/test-char.c | 2 +- tests/unit/test-image-locking.c | 2 +- tests/unit/test-replication.c | 2 +- tests/unit/test-seccomp.c | 2 +- tests/unit/test-throttle.c | 2 +- tests/unit/test-timed-average.c | 2 +- tests/unit/test-yank.c | 2 +- ui/cocoa.m | 10 +-- ui/curses.c | 2 +- ui/dbus-clipboard.c | 2 +- ui/dbus-listener.c | 2 +- ui/dbus.c | 2 +- ui/egl-helpers.c | 2 +- ui/gtk-egl.c | 2 +- ui/gtk-gl-area.c | 2 +- ui/gtk.c | 4 +- ui/input-barrier.c | 2 +- ui/input-linux.c | 2 +- ui/input.c | 6 +- ui/sdl2.c | 6 +- ui/spice-app.c | 2 +- ui/spice-core.c | 4 +- ui/vnc.c | 4 +- ui/win32-kbd-hook.c | 2 +- util/async.c | 2 +- util/main-loop.c | 4 +- util/oslib-posix.c | 2 +- util/qemu-timer.c | 6 +- 965 files changed, 1708 insertions(+), 1707 deletions(-) rename block/{qapi-sysemu.c => qapi-system.c} (99%) rename hw/core/{cpu-sysemu.c => cpu-system.c} (98%) rename include/{sysemu => system}/accel-blocker.h (98%) rename include/{sysemu => system}/accel-ops.h (100%) rename include/{sysemu => system}/arch_init.h (100%) rename include/{sysemu => system}/balloon.h (100%) rename include/{sysemu => system}/block-backend-common.h (100%) rename include/{sysemu => system}/block-backend-global-state.h (100%) rename include/{sysemu => system}/block-backend-io.h (100%) rename include/{sysemu => system}/block-backend.h (100%) rename include/{sysemu => system}/block-ram-registrar.h (100%) rename include/{sysemu => system}/blockdev.h (100%) rename include/{sysemu => system}/cpu-throttle.h (95%) rename include/{sysemu => system}/cpu-timers-internal.h (100%) rename include/{sysemu => system}/cpu-timers.h (96%) rename include/{sysemu => system}/cpus.h (97%) rename include/{sysemu => system}/cryptodev-vhost-user.h (97%) rename include/{sysemu => system}/cryptodev-vhost.h (99%) rename include/{sysemu => system}/cryptodev.h (100%) rename include/{sysemu => system}/device_tree.h (100%) rename include/{sysemu => system}/dirtylimit.h (100%) rename include/{sysemu => system}/dirtyrate.h (100%) rename include/{sysemu => system}/dma.h (100%) rename include/{sysemu => system}/dump-arch.h (100%) rename include/{sysemu => system}/dump.h (99%) rename include/{sysemu => system}/event-loop-base.h (100%) rename include/{sysemu => system}/host_iommu_device.h (100%) rename include/{sysemu => system}/hostmem.h (97%) rename include/{sysemu => system}/hvf.h (100%) rename include/{sysemu => system}/hvf_int.h (100%) rename include/{sysemu => system}/hw_accel.h (83%) rename include/{sysemu => system}/iommufd.h (96%) rename include/{sysemu => system}/iothread.h (97%) rename include/{sysemu => system}/kvm.h (100%) rename include/{sysemu => system}/kvm_int.h (99%) rename include/{sysemu => system}/kvm_xen.h (93%) rename include/{sysemu => system}/memory_mapping.h (100%) rename include/{sysemu => system}/numa.h (98%) rename include/{sysemu => system}/nvmm.h (100%) rename include/{sysemu => system}/os-posix.h (100%) rename include/{sysemu => system}/os-win32.h (100%) rename include/{sysemu => system}/qtest.h (100%) rename include/{sysemu => system}/replay.h (99%) rename include/{sysemu => system}/reset.h (98%) rename include/{sysemu => system}/rng-random.h (100%) rename include/{sysemu => system}/rng.h (100%) rename include/{sysemu => system}/rtc.h (98%) rename include/{sysemu => system}/runstate-action.h (100%) rename include/{sysemu => system}/runstate.h (98%) rename include/{sysemu => system}/seccomp.h (100%) rename include/{sysemu => system}/spdm-socket.h (100%) rename include/{sysemu => system}/stats.h (100%) rename include/{sysemu/sysemu.h => system/system.h} (99%) rename include/{sysemu => system}/tcg.h (88%) rename include/{sysemu => system}/tpm.h (100%) rename include/{sysemu => system}/tpm_backend.h (99%) rename include/{sysemu => system}/tpm_util.h (94%) rename include/{sysemu => system}/vhost-user-backend.h (100%) rename include/{sysemu => system}/watchdog.h (100%) rename include/{sysemu => system}/whpx.h (100%) rename include/{sysemu => system}/xen-mapcache.h (98%) rename include/{sysemu => system}/xen.h (92%) rename target/i386/{cpu-sysemu.c => cpu-system.c} (99%) rename target/i386/{sev-sysemu-stub.c => sev-system-stub.c} (100%) rename target/i386/tcg/{sysemu => system}/bpt_helper.c (99%) rename target/i386/tcg/{sysemu => system}/excp_helper.c (99%) rename target/i386/tcg/{sysemu => system}/fpu_helper.c (99%) rename target/i386/tcg/{sysemu => system}/meson.build (100%) rename target/i386/tcg/{sysemu => system}/misc_helper.c (99%) rename target/i386/tcg/{sysemu => system}/seg_helper.c (99%) rename target/i386/tcg/{sysemu => system}/smm_helper.c (99%) rename target/i386/tcg/{sysemu => system}/svm_helper.c (99%) rename target/i386/tcg/{sysemu => system}/tcg-cpu.c (96%) rename target/mips/{sysemu => system}/addr.c (100%) rename target/mips/{sysemu => system}/cp0.c (100%) rename target/mips/{sysemu => system}/cp0_timer.c (99%) rename target/mips/{sysemu => system}/machine.c (100%) rename target/mips/{sysemu => system}/meson.build (100%) rename target/mips/{sysemu => system}/mips-qmp-cmds.c (100%) rename target/mips/{sysemu => system}/physaddr.c (100%) rename target/mips/tcg/{sysemu => system}/cp0_helper.c (100%) rename target/mips/tcg/{sysemu => system}/lcsr_helper.c (100%) rename target/mips/tcg/{sysemu => system}/meson.build (100%) rename target/mips/tcg/{sysemu => system}/mips-semi.c (100%) rename target/mips/tcg/{sysemu => system}/semihosting-stub.c (100%) rename target/mips/tcg/{sysemu => system}/special_helper.c (100%) rename target/mips/tcg/{sysemu => system}/tlb_helper.c (100%) rename target/mips/tcg/{sysemu_helper.h.inc => system_helper.h.inc} (99%) rename target/s390x/{cpu-sysemu.c => cpu-system.c} (96%) rename target/s390x/{cpu_models_sysemu.c => cpu_models_system.c} (99%) diff --git a/MAINTAINERS b/MAINTAINERS index 430a0f4f8c..d4bf816287 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -170,8 +170,8 @@ F: include/exec/helper*.h F: include/exec/helper*.h.inc F: include/exec/helper-info.c.inc F: include/exec/page-protection.h -F: include/sysemu/cpus.h -F: include/sysemu/tcg.h +F: include/system/cpus.h +F: include/system/tcg.h F: include/hw/core/tcg-cpu-ops.h F: host/include/*/host/cpuinfo.h F: util/cpuinfo-*.c @@ -434,7 +434,7 @@ F: */*/kvm* F: accel/kvm/ F: accel/stubs/kvm-stub.c F: include/hw/kvm/ -F: include/sysemu/kvm*.h +F: include/system/kvm*.h F: scripts/kvm/kvm_flightrecorder ARM KVM CPUs @@ -447,7 +447,7 @@ MIPS KVM CPUs M: Huacai Chen S: Odd Fixes F: target/mips/kvm* -F: target/mips/sysemu/ +F: target/mips/system/ PPC KVM CPUs M: Nicholas Piggin @@ -481,7 +481,7 @@ Xen emulation on X86 KVM CPUs M: David Woodhouse M: Paul Durrant S: Supported -F: include/sysemu/kvm_xen.h +F: include/system/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* F: tests/avocado/kvm_xen_guest.py @@ -493,7 +493,7 @@ M: Richard Henderson R: Paolo Bonzini S: Maintained F: include/qemu/accel.h -F: include/sysemu/accel-*.h +F: include/system/accel-*.h F: include/hw/core/accel-cpu.h F: accel/accel-*.c F: accel/Makefile.objs @@ -517,14 +517,14 @@ M: Roman Bolshakov W: https://wiki.qemu.org/Features/HVF S: Maintained F: accel/hvf/ -F: include/sysemu/hvf.h -F: include/sysemu/hvf_int.h +F: include/system/hvf.h +F: include/system/hvf_int.h WHPX CPUs M: Sunil Muthuswamy S: Supported F: target/i386/whpx/ -F: include/sysemu/whpx.h +F: include/system/whpx.h Guest CPU Cores (Xen) --------------------- @@ -550,8 +550,8 @@ F: hw/i386/xen/ F: hw/pci-host/xen_igd_pt.c F: include/hw/block/dataplane/xen* F: include/hw/xen/ -F: include/sysemu/xen.h -F: include/sysemu/xen-mapcache.h +F: include/system/xen.h +F: include/system/xen-mapcache.h F: stubs/xen-hw-stub.c F: docs/system/arm/xenpvh.rst F: docs/system/i386/xenpvh.rst @@ -561,7 +561,7 @@ Guest CPU Cores (NVMM) NetBSD Virtual Machine Monitor (NVMM) CPU support M: Reinoud Zandijk S: Maintained -F: include/sysemu/nvmm.h +F: include/system/nvmm.h F: target/i386/nvmm/ Hosts @@ -579,7 +579,7 @@ POSIX M: Paolo Bonzini S: Maintained F: os-posix.c -F: include/sysemu/os-posix.h +F: include/system/os-posix.h F: util/*posix*.c F: include/qemu/*posix*.h @@ -1866,7 +1866,7 @@ R: Yanan Wang R: Zhao Liu S: Supported F: hw/core/cpu-common.c -F: hw/core/cpu-sysemu.c +F: hw/core/cpu-system.c F: hw/core/machine-qmp-cmds.c F: hw/core/machine.c F: hw/core/machine-smp.c @@ -1879,7 +1879,7 @@ F: qapi/machine-target.json F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h -F: include/sysemu/numa.h +F: include/system/numa.h F: tests/functional/test_cpu_queries.py F: tests/functional/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c @@ -2189,9 +2189,9 @@ M: Eric Auger M: Zhenzhong Duan S: Supported F: backends/iommufd.c -F: include/sysemu/iommufd.h +F: include/system/iommufd.h F: backends/host_iommu_device.c -F: include/sysemu/host_iommu_device.h +F: include/system/host_iommu_device.h F: include/qemu/chardev_open.h F: util/chardev_open.c F: docs/devel/vfio-iommufd.rst @@ -2205,7 +2205,7 @@ F: docs/interop/vhost-user.json F: docs/interop/vhost-user.rst F: contrib/vhost-user-*/ F: backends/vhost-user.c -F: include/sysemu/vhost-user-backend.h +F: include/system/vhost-user-backend.h F: subprojects/libvhost-user/ vhost-shadow-virtqueue @@ -2233,7 +2233,7 @@ F: docs/interop/virtio-balloon-stats.rst F: hw/virtio/virtio-balloon*.c F: include/hw/virtio/virtio-balloon.h F: system/balloon.c -F: include/sysemu/balloon.h +F: include/system/balloon.h F: tests/qtest/virtio-balloon-test.c virtio-9p @@ -2315,7 +2315,7 @@ R: Amit Shah S: Supported F: hw/virtio/virtio-rng.c F: include/hw/virtio/virtio-rng.h -F: include/sysemu/rng*.h +F: include/system/rng*.h F: backends/rng*.c F: tests/qtest/virtio-rng-test.c @@ -2841,7 +2841,7 @@ F: hw/block/ F: qapi/block*.json F: qapi/transaction.json F: include/block/ -F: include/sysemu/block-*.h +F: include/system/block-*.h F: qemu-img* F: docs/tools/qemu-img.rst F: qemu-io* @@ -2980,7 +2980,7 @@ M: Alistair Francis R: David Gibson S: Maintained F: system/device_tree.c -F: include/sysemu/device_tree.h +F: include/system/device_tree.h Dump S: Supported @@ -2989,8 +2989,8 @@ F: dump/ F: hw/misc/vmcoreinfo.c F: include/hw/misc/vmcoreinfo.h F: include/qemu/win_dump_defs -F: include/sysemu/dump-arch.h -F: include/sysemu/dump.h +F: include/system/dump-arch.h +F: include/system/dump.h F: qapi/dump.json F: scripts/dump-guest-memory.py F: stubs/dump.c @@ -3036,7 +3036,7 @@ F: include/exec/memop.h F: include/exec/memory.h F: include/exec/ram_addr.h F: include/exec/ramblock.h -F: include/sysemu/memory_mapping.h +F: include/system/memory_mapping.h F: system/dma-helpers.c F: system/ioport.c F: system/memory.c @@ -3089,8 +3089,8 @@ Main loop M: Paolo Bonzini S: Maintained F: include/qemu/main-loop.h -F: include/sysemu/runstate.h -F: include/sysemu/runstate-action.h +F: include/system/runstate.h +F: include/system/runstate-action.h F: util/main-loop.c F: util/qemu-timer*.c F: system/vl.c @@ -3153,7 +3153,7 @@ M: David Hildenbrand M: Igor Mammedov S: Maintained F: backends/hostmem*.c -F: include/sysemu/hostmem.h +F: include/system/hostmem.h F: docs/system/vm-templating.rst T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -3161,7 +3161,7 @@ Cryptodev Backends M: Gonglei M: zhenwei pi S: Maintained -F: include/sysemu/cryptodev*.h +F: include/system/cryptodev*.h F: backends/cryptodev*.c F: qapi/cryptodev.json @@ -3310,7 +3310,7 @@ M: Laurent Vivier R: Paolo Bonzini S: Maintained F: system/qtest.c -F: include/sysemu/qtest.h +F: include/system/qtest.h F: accel/qtest/ F: tests/qtest/ F: docs/devel/qgraph.rst @@ -3361,7 +3361,7 @@ T: git https://people.debian.org/~sthibault/qemu.git slirp Stats S: Orphan -F: include/sysemu/stats.h +F: include/system/stats.h F: stats/ F: qapi/stats.json @@ -3402,7 +3402,7 @@ S: Maintained F: system/tpm* F: hw/tpm/* F: include/hw/acpi/tpm.h -F: include/sysemu/tpm* +F: include/system/tpm* F: qapi/tpm.json F: backends/tpm/ F: tests/qtest/*tpm* @@ -3413,7 +3413,7 @@ SPDM M: Alistair Francis S: Maintained F: backends/spdm-socket.c -F: include/sysemu/spdm-socket.h +F: include/system/spdm-socket.h Checkpatch S: Odd Fixes @@ -3448,10 +3448,10 @@ Migration dirty limit and dirty page rate M: Hyman Huang S: Maintained F: system/dirtylimit.c -F: include/sysemu/dirtylimit.h +F: include/system/dirtylimit.h F: migration/dirtyrate.c F: migration/dirtyrate.h -F: include/sysemu/dirtyrate.h +F: include/system/dirtyrate.h F: docs/devel/migration/dirty-limit.rst Detached LUKS header @@ -3479,7 +3479,7 @@ Seccomp M: Daniel P. Berrange S: Odd Fixes F: system/qemu-seccomp.c -F: include/sysemu/seccomp.h +F: include/system/seccomp.h F: tests/unit/test-seccomp.c Cryptography @@ -3592,7 +3592,7 @@ F: replay/* F: block/blkreplay.c F: net/filter-replay.c F: include/exec/replay-core.h -F: include/sysemu/replay.h +F: include/system/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst F: stubs/replay.c @@ -3711,7 +3711,7 @@ M: Peter Maydell S: Maintained F: include/hw/resettable.h F: include/hw/core/resetcontainer.h -F: include/sysemu/reset.h +F: include/system/reset.h F: hw/core/reset.c F: hw/core/resettable.c F: hw/core/resetcontainer.c diff --git a/accel/accel-blocker.c b/accel/accel-blocker.c index 75daaa2911..51132d1b8a 100644 --- a/accel/accel-blocker.c +++ b/accel/accel-blocker.c @@ -29,7 +29,7 @@ #include "qemu/thread.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" -#include "sysemu/accel-blocker.h" +#include "system/accel-blocker.h" static QemuLockCnt accel_in_ioctl_lock; static QemuEvent accel_in_ioctl_event; diff --git a/accel/accel-system.c b/accel/accel-system.c index 61d689935e..a7596aef59 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "hw/boards.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/error-report.h" #include "accel-system.h" diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index f32d8c8dc3..867276144f 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/rcu.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index d60874d3e6..945ba72051 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -54,10 +54,10 @@ #include "exec/exec-all.h" #include "gdbstub/enums.h" #include "hw/boards.h" -#include "sysemu/cpus.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" -#include "sysemu/runstate.h" +#include "system/cpus.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "system/runstate.h" #include "qemu/guest-random.h" HVFState *hvf_state; diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 6ca0850b20..d404e01ade 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -10,8 +10,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" +#include "system/hvf.h" +#include "system/hvf_int.h" const char *hvf_return_string(hv_return_t ret) { diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index c239dfc87a..a81e8f3b03 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -16,10 +16,10 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" +#include "system/kvm.h" +#include "system/kvm_int.h" +#include "system/runstate.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #include "qapi/error.h" diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 801cff16a5..672050e800 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -28,10 +28,10 @@ #include "hw/pci/msix.h" #include "hw/s390x/adapter.h" #include "gdbstub/enums.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "sysemu/accel-blocker.h" +#include "system/kvm_int.h" +#include "system/runstate.h" +#include "system/cpus.h" +#include "system/accel-blocker.h" #include "qemu/bswap.h" #include "exec/memory.h" #include "exec/ram_addr.h" @@ -42,15 +42,15 @@ #include "qapi/visitor.h" #include "qapi/qapi-types-common.h" #include "qapi/qapi-visit-common.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qemu/guest-random.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "kvm-cpus.h" -#include "sysemu/dirtylimit.h" +#include "system/dirtylimit.h" #include "qemu/range.h" #include "hw/boards.h" -#include "sysemu/stats.h" +#include "system/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index 171b22fd29..b5435286e4 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -10,7 +10,7 @@ #ifndef KVM_CPUS_H #define KVM_CPUS_H -#include "sysemu/cpus.h" +#include "system/cpus.h" int kvm_init_vcpu(CPUState *cpu, Error **errp); int kvm_cpu_exec(CPUState *cpu); diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index bf14032d29..ad7e3441a5 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -18,8 +18,8 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/accel.h" -#include "sysemu/qtest.h" -#include "sysemu/cpus.h" +#include "system/qtest.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 8e0eb22e61..ecfd7636f5 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/pci/msi.h" KVMState *kvm_state; diff --git a/accel/stubs/xen-stub.c b/accel/stubs/xen-stub.c index 7054965c48..cf929b644b 100644 --- a/accel/stubs/xen-stub.c +++ b/accel/stubs/xen-stub.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/xen.h" +#include "system/xen.h" #include "qapi/qapi-commands-migration.h" bool xen_allowed; diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index bc9b1a260e..6ecfc4e7c2 100644 --- a/accel/tcg/cpu-exec-common.c +++ b/accel/tcg/cpu-exec-common.c @@ -18,8 +18,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/cpus.h" -#include "sysemu/tcg.h" +#include "system/cpus.h" +#include "system/tcg.h" #include "qemu/plugin.h" #include "internal-common.h" diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8163295f34..c13f4a7cbb 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -30,11 +30,11 @@ #include "qemu/rcu.h" #include "exec/log.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "exec/cpu-all.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "exec/replay-core.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "exec/helper-proto-common.h" #include "tb-jmp-cache.h" #include "tb-hash.h" diff --git a/accel/tcg/icount-common.c b/accel/tcg/icount-common.c index 30bf8500dc..b178dccec4 100644 --- a/accel/tcg/icount-common.c +++ b/accel/tcg/icount-common.c @@ -27,16 +27,16 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/cpus.h" -#include "sysemu/qtest.h" +#include "system/cpus.h" +#include "system/qtest.h" #include "qemu/main-loop.h" #include "qemu/option.h" #include "qemu/seqlock.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "hw/core/cpu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/cpu-timers-internal.h" +#include "system/cpu-timers.h" +#include "system/cpu-timers-internal.h" /* * ICOUNT: Instruction Counter diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 093efe9714..ae1dbeb79f 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -13,9 +13,9 @@ #include "qapi/type-helpers.h" #include "qapi/qapi-commands-machine.h" #include "monitor/monitor.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/tcg.h" +#include "system/cpus.h" +#include "system/cpu-timers.h" +#include "system/tcg.h" #include "tcg/tcg.h" #include "internal-common.h" #include "tb-context.h" diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index cc0f5afd47..97d2e39ec0 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -26,7 +26,7 @@ #include "exec/page-protection.h" #include "exec/tb-flush.h" #include "exec/translate-all.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "tcg/tcg.h" #include "tb-hash.h" #include "tb-context.h" diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index 9e1ae66f65..d6b472a0b0 100644 --- a/accel/tcg/tcg-accel-ops-icount.c +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -24,8 +24,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/replay.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 49814ec4af..ba7cf6819d 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -24,9 +24,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 8ebadf8e9e..028b385af9 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -25,9 +25,9 @@ #include "qemu/osdep.h" #include "qemu/lockable.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 3c19e68a79..d9a35b7667 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -26,9 +26,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "qemu/timer.h" diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h index 44c4079972..6feeb3f3e9 100644 --- a/accel/tcg/tcg-accel-ops.h +++ b/accel/tcg/tcg-accel-ops.h @@ -12,7 +12,7 @@ #ifndef TCG_ACCEL_OPS_H #define TCG_ACCEL_OPS_H -#include "sysemu/cpus.h" +#include "system/cpus.h" void tcg_cpu_destroy(CPUState *cpu); int tcg_cpu_exec(CPUState *cpu); diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 2090907dba..c256575887 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -24,9 +24,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "exec/replay-core.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "tcg/startup.h" #include "tcg/oversized-guest.h" #include "qapi/error.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index fdf6d8ac19..a8b24be0b9 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -53,9 +53,9 @@ #include "qemu/cacheinfo.h" #include "qemu/timer.h" #include "exec/log.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/tcg.h" +#include "system/cpus.h" +#include "system/cpu-timers.h" +#include "system/tcg.h" #include "qapi/error.h" #include "hw/core/tcg-cpu-ops.h" #include "tb-jmp-cache.h" diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index d3aab11458..ca641eb95c 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -22,8 +22,8 @@ #include "qemu/error-report.h" #include "exec/exec-all.h" #include "exec/translate-all.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" +#include "system/tcg.h" +#include "system/replay.h" #include "hw/core/tcg-cpu-ops.h" #include "hw/core/cpu.h" diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 0bdefce537..852e9fbe5f 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -18,9 +18,9 @@ #include "hw/xen/xen_igd.h" #include "chardev/char.h" #include "qemu/accel.h" -#include "sysemu/cpus.h" -#include "sysemu/xen.h" -#include "sysemu/runstate.h" +#include "system/cpus.h" +#include "system/xen.h" +#include "system/runstate.h" #include "migration/misc.h" #include "migration/global_state.h" #include "hw/boards.h" diff --git a/audio/audio.c b/audio/audio.c index af0ae33fed..87b4e9b6f2 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -38,9 +38,9 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/help_option.h" -#include "sysemu/sysemu.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/replay.h" +#include "system/runstate.h" #include "ui/qemu-spice.h" #include "trace.h" diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index b1486be630..764cee4311 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -22,7 +22,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c index 38deac0717..41cf24b737 100644 --- a/backends/cryptodev-lkcf.c +++ b/backends/cryptodev-lkcf.c @@ -30,7 +30,7 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qom/object.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "standard-headers/linux/virtio_crypto.h" #include diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index e33fb78521..43efdf9747 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -27,9 +27,9 @@ #include "qemu/error-report.h" #include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_crypto.h" -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #include "chardev/char-fe.h" -#include "sysemu/cryptodev-vhost-user.h" +#include "system/cryptodev-vhost-user.h" #include "qom/object.h" diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index 93523732f3..8718c97326 100644 --- a/backends/cryptodev-vhost.c +++ b/backends/cryptodev-vhost.c @@ -24,13 +24,13 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio-bus.h" -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #ifdef CONFIG_VHOST_CRYPTO #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/virtio/virtio-crypto.h" -#include "sysemu/cryptodev-vhost-user.h" +#include "system/cryptodev-vhost-user.h" uint64_t cryptodev_vhost_get_max_queues( diff --git a/backends/cryptodev.c b/backends/cryptodev.c index d8bd2a1ae6..1157a149d0 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -22,8 +22,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/cryptodev.h" -#include "sysemu/stats.h" +#include "system/cryptodev.h" +#include "system/stats.h" #include "qapi/error.h" #include "qapi/qapi-commands-cryptodev.h" #include "qapi/qapi-types-stats.h" diff --git a/backends/host_iommu_device.c b/backends/host_iommu_device.c index 8f2dda1beb..cea76c6925 100644 --- a/backends/host_iommu_device.c +++ b/backends/host_iommu_device.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/host_iommu_device.h" +#include "system/host_iommu_device.h" OBJECT_DEFINE_ABSTRACT_TYPE(HostIOMMUDevice, host_iommu_device, diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index 6c024d6217..eb4b95dfd7 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -14,7 +14,7 @@ #include #include "qom/object_interfaces.h" #include "qapi/error.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "hw/i386/hostmem-epc.h" static bool diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 7e5072e33e..46321fda84 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -15,7 +15,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/madvise.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qom/object_interfaces.h" #include "qom/object.h" #include "qapi/visitor.h" diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 9f890a813e..d4d0620e6c 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qom/object_interfaces.h" #include "qemu/memfd.h" #include "qemu/module.h" diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index f7d81af783..39aac6bf35 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c index 374edc3db8..5551ba78a6 100644 --- a/backends/hostmem-shm.c +++ b/backends/hostmem-shm.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qapi/error.h" #define TYPE_MEMORY_BACKEND_SHM "memory-backend-shm" diff --git a/backends/hostmem.c b/backends/hostmem.c index 181446626a..bceca1a8d9 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "hw/boards.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" diff --git a/backends/iommufd.c b/backends/iommufd.c index 9bc466a89c..7b4fc8ec46 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/iommufd.h" +#include "system/iommufd.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" diff --git a/backends/rng-builtin.c b/backends/rng-builtin.c index f367eb665c..4cfa7e578b 100644 --- a/backends/rng-builtin.c +++ b/backends/rng-builtin.c @@ -6,11 +6,11 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "qom/object.h" -#include "sysemu/replay.h" +#include "system/replay.h" OBJECT_DECLARE_SIMPLE_TYPE(RngBuiltin, RNG_BUILTIN) diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 684c3cf3d6..82da46365d 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" diff --git a/backends/rng-random.c b/backends/rng-random.c index 489c0917f0..3ce6cc9b4a 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -11,8 +11,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng-random.h" -#include "sysemu/rng.h" +#include "system/rng-random.h" +#include "system/rng.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/main-loop.h" diff --git a/backends/rng.c b/backends/rng.c index 9bbd0c77b6..1f6fb106ae 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c index d0663d696c..2c709c68c8 100644 --- a/backends/spdm-socket.c +++ b/backends/spdm-socket.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/spdm-socket.h" +#include "system/spdm-socket.h" #include "qapi/error.h" static bool read_bytes(const int socket, uint8_t *buffer, diff --git a/backends/tpm/tpm_backend.c b/backends/tpm/tpm_backend.c index 485a20b9e0..8cf80043ac 100644 --- a/backends/tpm/tpm_backend.c +++ b/backends/tpm/tpm_backend.c @@ -13,9 +13,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "qapi/error.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "qemu/thread.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index aa05dab6ae..00fe015a94 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -32,9 +32,9 @@ #include "qemu/sockets.h" #include "qemu/lockable.h" #include "io/channel-socket.h" -#include "sysemu/runstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/runstate.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_int.h" #include "tpm_ioctl.h" #include "migration/blocker.h" diff --git a/backends/tpm/tpm_int.h b/backends/tpm/tpm_int.h index ba6109306e..2319a1ce0c 100644 --- a/backends/tpm/tpm_int.h +++ b/backends/tpm/tpm_int.h @@ -13,7 +13,7 @@ #define BACKENDS_TPM_INT_H #include "qemu/option.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #define TPM_STANDARD_CMDLINE_OPTS \ { \ diff --git a/backends/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c index 179697a3a9..09a6abf02d 100644 --- a/backends/tpm/tpm_passthrough.c +++ b/backends/tpm/tpm_passthrough.c @@ -26,8 +26,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/sockets.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_int.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-tpm.h" diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index cf138551df..f5feb48acf 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -26,8 +26,8 @@ #include "tpm_int.h" #include "exec/memory.h" #include "hw/qdev-properties.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "trace.h" /* tpm backend property */ diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 94c6a82d52..d0e4d71a63 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c @@ -15,8 +15,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qom/object_interfaces.h" -#include "sysemu/vhost-user-backend.h" -#include "sysemu/kvm.h" +#include "system/vhost-user-backend.h" +#include "system/kvm.h" #include "io/channel-command.h" #include "hw/virtio/virtio-bus.h" diff --git a/block.c b/block.c index 7d90007cae..f60606f242 100644 --- a/block.c +++ b/block.c @@ -42,7 +42,7 @@ #include "qapi/qmp/qstring.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qapi-visit-block-core.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/notify.h" #include "qemu/option.h" #include "qemu/coroutine.h" diff --git a/block/accounting.c b/block/accounting.c index 2829745377..3e46159569 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -27,7 +27,7 @@ #include "block/accounting.h" #include "block/block_int.h" #include "qemu/timer.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; diff --git a/block/backup.c b/block/backup.c index a1292c01ec..79652bf57b 100644 --- a/block/backup.c +++ b/block/backup.c @@ -23,7 +23,7 @@ #include "block/dirty-bitmap.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/bitmap.h" #include "qemu/error-report.h" diff --git a/block/blkdebug.c b/block/blkdebug.c index c95c818c38..f500824608 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -37,7 +37,7 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" #include "qapi/qobject-input-visitor.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* All APIs are thread-safe */ diff --git a/block/blkio.c b/block/blkio.c index e0e765af63..003cb63832 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -18,7 +18,7 @@ #include "qemu/error-report.h" #include "qapi/qmp/qdict.h" #include "qemu/module.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "exec/memory.h" /* for ram_block_discard_disable() */ #include "block/block-io.h" diff --git a/block/blkreplay.c b/block/blkreplay.c index 792d980aa9..16d8b12dd9 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "block/block-io.h" #include "block/block_int.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/error.h" typedef struct Request { diff --git a/block/block-backend.c b/block/block-backend.c index 85bcdedcef..c93a7525ad 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -11,15 +11,15 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" #include "block/coroutines.h" #include "block/throttle-groups.h" #include "hw/qdev-core.h" -#include "sysemu/blockdev.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" +#include "system/blockdev.h" +#include "system/runstate.h" +#include "system/replay.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" #include "qemu/id.h" diff --git a/block/block-copy.c b/block/block-copy.c index eddb0b81e0..1826c2e1c7 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -20,7 +20,7 @@ #include "block/block_int-io.h" #include "block/dirty-bitmap.h" #include "block/reqlist.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/units.h" #include "qemu/co-shared-resource.h" #include "qemu/coroutine.h" diff --git a/block/block-ram-registrar.c b/block/block-ram-registrar.c index 25dbafa789..fcda2b86af 100644 --- a/block/block-ram-registrar.c +++ b/block/block-ram-registrar.c @@ -5,8 +5,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/block-ram-registrar.h" +#include "system/block-backend.h" +#include "system/block-ram-registrar.h" #include "qapi/error.h" static void ram_block_added(RAMBlockNotifier *n, void *host, size_t size, diff --git a/block/commit.c b/block/commit.c index 7c3fdcb0ca..5df3d05346 100644 --- a/block/commit.c +++ b/block/commit.c @@ -20,7 +20,7 @@ #include "qapi/error.h" #include "qemu/ratelimit.h" #include "qemu/memalign.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" enum { /* diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 81afeff1c7..c00bc2351b 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qapi/qmp/qjson.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "block/block_int.h" diff --git a/block/coroutines.h b/block/coroutines.h index f3226682d6..79e5efbf75 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -28,7 +28,7 @@ #include "block/block_int.h" /* For blk_bs() in generated block/block-gen.c */ -#include "sysemu/block-backend.h" +#include "system/block-backend.h" /* * I/O API functions. These functions are thread-safe. diff --git a/block/crypto.c b/block/crypto.c index 80b2dba17a..d4226cc68a 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -22,7 +22,7 @@ #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "crypto/block.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-crypto.h" diff --git a/block/export/export.c b/block/export/export.c index 6d51ae8ed7..79c71ee245 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -14,8 +14,8 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/iothread.h" +#include "system/block-backend.h" +#include "system/iothread.h" #include "block/export.h" #include "block/fuse.h" #include "block/nbd.h" diff --git a/block/export/fuse.c b/block/export/fuse.c index 3307b64089..465cc9891d 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-block.h" #include "qemu/main-loop.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include #include diff --git a/block/export/virtio-blk-handler.h b/block/export/virtio-blk-handler.h index 150d44cff2..cca1544e9f 100644 --- a/block/export/virtio-blk-handler.h +++ b/block/export/virtio-blk-handler.h @@ -13,7 +13,7 @@ #ifndef VIRTIO_BLK_HANDLER_H #define VIRTIO_BLK_HANDLER_H -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #define VIRTIO_BLK_SECTOR_BITS 9 #define VIRTIO_BLK_SECTOR_SIZE (1ULL << VIRTIO_BLK_SECTOR_BITS) diff --git a/block/io.c b/block/io.c index 301514c880..d369b994df 100644 --- a/block/io.c +++ b/block/io.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "trace.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/aio-wait.h" #include "block/blockjob.h" #include "block/blockjob_int.h" @@ -37,7 +37,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/replay.h" +#include "system/replay.h" /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) diff --git a/block/io_uring.c b/block/io_uring.c index d11b2051ab..f52b66b340 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -17,7 +17,7 @@ #include "qemu/coroutine.h" #include "qemu/defer-call.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "trace.h" /* Only used for assertions. */ diff --git a/block/iscsi.c b/block/iscsi.c index 979bf90cb7..a5f8921426 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -28,7 +28,7 @@ #include #include #include -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/bitops.h" @@ -41,7 +41,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/uuid.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qmp/qdict.h" diff --git a/block/linux-aio.c b/block/linux-aio.c index e3b5ec9aba..194c8f434f 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -16,7 +16,7 @@ #include "qemu/coroutine.h" #include "qemu/defer-call.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" /* Only used for assertions. */ #include "qemu/coroutine_int.h" diff --git a/block/meson.build b/block/meson.build index f1262ec2ba..34b1b2a306 100644 --- a/block/meson.build +++ b/block/meson.build @@ -154,8 +154,8 @@ block_gen_c = custom_target('block-gen.c', '../include/block/dirty-bitmap.h', '../include/block/block_int-io.h', '../include/block/block-global-state.h', - '../include/sysemu/block-backend-global-state.h', - '../include/sysemu/block-backend-io.h', + '../include/system/block-backend-global-state.h', + '../include/system/block-backend-io.h', 'coroutines.h' ), command: [wrapper_py, '@OUTPUT@', '@INPUT@']) @@ -163,7 +163,7 @@ block_ss.add(block_gen_c) block_ss.add(files('stream.c')) -system_ss.add(files('qapi-sysemu.c')) +system_ss.add(files('qapi-system.c')) subdir('export') subdir('monitor') diff --git a/block/mirror.c b/block/mirror.c index 2afe700b4d..a53582f17b 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -19,7 +19,7 @@ #include "block/blockjob_int.h" #include "block/block_int.h" #include "block/dirty-bitmap.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index bdf2eb50b6..1d312513fc 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -37,8 +37,8 @@ #include "qemu/osdep.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-block-export.h" #include "qapi/qmp/qdict.h" @@ -49,7 +49,7 @@ #include "qemu/sockets.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "monitor/monitor.h" #include "monitor/hmp.h" #include "block/nbd.h" diff --git a/block/nfs.c b/block/nfs.c index 0500f60c08..7d34b58750 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -39,7 +39,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/cutils.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" diff --git a/block/null.c b/block/null.c index 4730acc1eb..8135055834 100644 --- a/block/null.c +++ b/block/null.c @@ -18,7 +18,7 @@ #include "qemu/option.h" #include "block/block-io.h" #include "block/block_int.h" -#include "sysemu/replay.h" +#include "system/replay.h" #define NULL_OPT_LATENCY "latency-ns" #define NULL_OPT_ZEROES "read-zeroes" diff --git a/block/nvme.c b/block/nvme.c index 3b588b139f..5ba6a0c9c9 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -26,8 +26,8 @@ #include "qemu/vfio-helpers.h" #include "block/block-io.h" #include "block/block_int.h" -#include "sysemu/block-backend.h" -#include "sysemu/replay.h" +#include "system/block-backend.h" +#include "system/replay.h" #include "trace.h" #include "block/nvme.h" diff --git a/block/parallels.c b/block/parallels.c index 071b6dcaf8..23751b28a9 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -33,7 +33,7 @@ #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qapi/qmp/qdict.h" diff --git a/block/qapi-sysemu.c b/block/qapi-system.c similarity index 99% rename from block/qapi-sysemu.c rename to block/qapi-system.c index e4282631d2..3277f37fd0 100644 --- a/block/qapi-sysemu.c +++ b/block/qapi-system.c @@ -36,8 +36,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-block.h" #include "qapi/qmp/qdict.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id, Error **errp) diff --git a/block/qapi.c b/block/qapi.c index 2b5793f1d9..902ecb08e0 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -39,7 +39,7 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/qemu-print.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, BlockDriverState *bs, diff --git a/block/qcow.c b/block/qcow.c index 84d1cca296..37be7e7cb4 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -27,7 +27,7 @@ #include "qemu/error-report.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 92e47978bf..1e8dc48be1 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qcow2.h" #include "qemu/bswap.h" diff --git a/block/qcow2.c b/block/qcow2.c index 803ca73a2f..d732162391 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qcow2.h" diff --git a/block/qed.c b/block/qed.c index fa5bc11085..8b33594546 100644 --- a/block/qed.c +++ b/block/qed.c @@ -23,7 +23,7 @@ #include "qemu/memalign.h" #include "trace.h" #include "qed.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" diff --git a/block/rbd.c b/block/rbd.c index 04ed0e242e..e814856609 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -23,7 +23,7 @@ #include "block/qdict.h" #include "crypto/secret.h" #include "qemu/cutils.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" diff --git a/block/replication.c b/block/replication.c index 0415a5e8b7..2ce16f0589 100644 --- a/block/replication.c +++ b/block/replication.c @@ -19,7 +19,7 @@ #include "block/blockjob.h" #include "block/block_int.h" #include "block/block_backup.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "block/replication.h" diff --git a/block/snapshot-access.c b/block/snapshot-access.c index 84d0d13f86..71ac83c01f 100644 --- a/block/snapshot-access.c +++ b/block/snapshot-access.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/cutils.h" #include "block/block_int.h" diff --git a/block/snapshot.c b/block/snapshot.c index 8fd1756777..d27afe7c0e 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -30,7 +30,7 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" #include "qemu/option.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" QemuOptsList internal_snapshot_opts = { .name = "snapshot", diff --git a/block/stream.c b/block/stream.c index 9076203193..9a06c0decb 100644 --- a/block/stream.c +++ b/block/stream.c @@ -18,7 +18,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu/ratelimit.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/copy-on-read.h" enum { diff --git a/block/throttle-groups.c b/block/throttle-groups.c index f5c0fac581..32553b39e3 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -23,13 +23,13 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/throttle-groups.h" #include "qemu/throttle-options.h" #include "qemu/main-loop.h" #include "qemu/queue.h" #include "qemu/thread.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-block-core.h" #include "qom/object.h" diff --git a/block/vdi.c b/block/vdi.c index 26f7638f1f..a2da6ecab0 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -58,7 +58,7 @@ #include "qapi/qapi-visit-block-core.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" diff --git a/block/vhdx.c b/block/vhdx.c index 5aa1a13506..42c919f51a 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -19,7 +19,7 @@ #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/crc32c.h" diff --git a/block/vmdk.c b/block/vmdk.c index 78f6433607..6ef266df87 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qmp/qdict.h" #include "qemu/error-report.h" #include "qemu/module.h" diff --git a/block/vpc.c b/block/vpc.c index d95a204612..6489ee756a 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "migration/blocker.h" diff --git a/blockdev-nbd.c b/blockdev-nbd.c index b36f41b7c5..9e61fbaf2b 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -10,8 +10,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/block-backend.h" #include "hw/block/block.h" #include "qapi/error.h" #include "qapi/clone-visitor.h" diff --git a/blockdev.c b/blockdev.c index 6740663fda..218024497b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -31,8 +31,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" #include "block/dirty-bitmap.h" @@ -53,12 +53,12 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qlist.h" #include "qapi/qobject-output-visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/iothread.h" +#include "system/system.h" +#include "system/iothread.h" #include "block/block_int.h" #include "block/trace.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" +#include "system/runstate.h" +#include "system/replay.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/main-loop.h" diff --git a/blockjob.c b/blockjob.c index d5f29e14af..e94a840d7f 100644 --- a/blockjob.c +++ b/blockjob.c @@ -29,7 +29,7 @@ #include "block/blockjob_int.h" #include "block/block_int.h" #include "block/trace.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 8ac6bebb6f..158a5f4f55 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "chardev/char-fe.h" #include "chardev/char-io.h" diff --git a/chardev/char-mux.c b/chardev/char-mux.c index e13042d381..d5f7e1a9cf 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -28,7 +28,7 @@ #include "qemu/option.h" #include "qemu/bitops.h" #include "chardev/char.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qapi-commands-control.h" #include "chardev-internal.h" diff --git a/chardev/char.c b/chardev/char.c index d06698228a..44ff116fcd 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -33,7 +33,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-char.h" #include "qapi/qmp/qerror.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/cpu-common.c b/cpu-common.c index 0d607bbe49..4248b2d727 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -21,7 +21,7 @@ #include "qemu/main-loop.h" #include "exec/cpu-common.h" #include "hw/core/cpu.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/lockable.h" #include "trace/trace-root.h" diff --git a/cpu-target.c b/cpu-target.c index 2ae07a779e..5480cfb721 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -34,8 +34,8 @@ #include "exec/address-spaces.h" #include "exec/memory.h" #endif -#include "sysemu/cpus.h" -#include "sysemu/tcg.h" +#include "system/cpus.h" +#include "system/tcg.h" #include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/cpu-common.h" diff --git a/crypto/akcipher-gcrypt.c.inc b/crypto/akcipher-gcrypt.c.inc index 5a880f6638..bcf030fdec 100644 --- a/crypto/akcipher-gcrypt.c.inc +++ b/crypto/akcipher-gcrypt.c.inc @@ -26,7 +26,7 @@ #include "crypto/akcipher.h" #include "crypto/random.h" #include "qapi/error.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "rsakey.h" typedef struct QCryptoGcryptRSA { diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc index 1720f84362..1d4bd6960e 100644 --- a/crypto/akcipher-nettle.c.inc +++ b/crypto/akcipher-nettle.c.inc @@ -26,7 +26,7 @@ #include "crypto/akcipher.h" #include "crypto/random.h" #include "qapi/error.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "rsakey.h" typedef struct QCryptoNettleRSA { diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst index 1ad36ad709..b630a351b4 100644 --- a/docs/specs/tpm.rst +++ b/docs/specs/tpm.rst @@ -205,8 +205,8 @@ to be used with the passthrough backend or the swtpm backend. QEMU files related to TPM backends: - ``backends/tpm.c`` - - ``include/sysemu/tpm.h`` - - ``include/sysemu/tpm_backend.h`` + - ``include/system/tpm.h`` + - ``include/system/tpm_backend.h`` The QEMU TPM passthrough device ------------------------------- @@ -240,7 +240,7 @@ PCRs. QEMU files related to the TPM passthrough device: - ``backends/tpm/tpm_passthrough.c`` - ``backends/tpm/tpm_util.c`` - - ``include/sysemu/tpm_util.h`` + - ``include/system/tpm_util.h`` Command line to start QEMU with the TPM passthrough device using the host's @@ -301,7 +301,7 @@ command. QEMU files related to the TPM emulator device: - ``backends/tpm/tpm_emulator.c`` - ``backends/tpm/tpm_util.c`` - - ``include/sysemu/tpm_util.h`` + - ``include/system/tpm_util.h`` The following commands start the swtpm with a UnixIO control channel over a socket interface. They do not need to be run as root. diff --git a/dump/dump.c b/dump/dump.c index 45e84428ae..15bbcc0c61 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -17,9 +17,9 @@ #include "qemu/bswap.h" #include "exec/target_page.h" #include "monitor/monitor.h" -#include "sysemu/dump.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" +#include "system/dump.h" +#include "system/runstate.h" +#include "system/cpus.h" #include "qapi/error.h" #include "qapi/qapi-commands-dump.h" #include "qapi/qapi-events-dump.h" diff --git a/dump/win_dump.c b/dump/win_dump.c index 0e4fe692ce..2c2576672a 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "exec/cpu-defs.h" diff --git a/dump/win_dump.h b/dump/win_dump.h index c9b49f87dc..9d6cfa47c5 100644 --- a/dump/win_dump.h +++ b/dump/win_dump.h @@ -11,7 +11,7 @@ #ifndef WIN_DUMP_H #define WIN_DUMP_H -#include "sysemu/dump.h" +#include "system/dump.h" /* Check Windows dump availability for the current target */ bool win_dump_available(Error **errp); diff --git a/event-loop-base.c b/event-loop-base.c index d5be4dc6fc..0cfb1c9496 100644 --- a/event-loop-base.c +++ b/event-loop-base.c @@ -15,7 +15,7 @@ #include "qom/object_interfaces.h" #include "qapi/error.h" #include "block/thread-pool.h" -#include "sysemu/event-loop-base.h" +#include "system/event-loop-base.h" typedef struct { const char *name; diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index b1def7e71d..e366df12d4 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -41,8 +41,8 @@ #endif #include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" +#include "system/hw_accel.h" +#include "system/runstate.h" #include "exec/replay-core.h" #include "exec/hwaddr.h" diff --git a/gdbstub/syscalls.c b/gdbstub/syscalls.c index 4ddd5cae06..e855df21ab 100644 --- a/gdbstub/syscalls.c +++ b/gdbstub/syscalls.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "semihosting/semihost.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "gdbstub/user.h" #include "gdbstub/syscalls.h" #include "gdbstub/commands.h" diff --git a/gdbstub/system.c b/gdbstub/system.c index c9f236e94f..2d9fdff2fe 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -19,9 +19,9 @@ #include "gdbstub/commands.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" +#include "system/cpus.h" +#include "system/runstate.h" +#include "system/replay.h" #include "hw/core/cpu.h" #include "hw/cpu/cluster.h" #include "hw/boards.h" diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 0ac79a500b..2abaf3a291 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -24,7 +24,7 @@ #include "qemu/rcu.h" #include "qemu/rcu_queue.h" #include "qemu/cutils.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* Root node for synth file system */ static V9fsSynthNode synth_root = { diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index b764e4cd3d..4f30054cc3 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -26,7 +26,7 @@ #include "hw/virtio/virtio-access.h" #include "qemu/iov.h" #include "qemu/module.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static void virtio_9p_push_and_notify(V9fsPDU *pdu) { diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 72282b173e..331de43dad 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -24,7 +24,7 @@ #include "hw/acpi/aml-build.h" #include "qemu/bswap.h" #include "qemu/bitops.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" #include "hw/acpi/tpm.h" #include "hw/pci/pci_host.h" diff --git a/hw/acpi/core.c b/hw/acpi/core.c index ec5e127d17..870391ed7c 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -31,7 +31,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" struct acpi_table_header { diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 5cb60ca8bc..9d530a24da 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -5,7 +5,7 @@ #include "qapi/error.h" #include "qapi/qapi-events-acpi.h" #include "trace.h" -#include "sysemu/numa.h" +#include "system/numa.h" #define ACPI_CPU_SELECTOR_OFFSET_WR 0 #define ACPI_CPU_FLAGS_OFFSET_RW 4 diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 5ef5ddccb6..536092039b 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -24,7 +24,7 @@ #include "hw/acpi/aml-build.h" #include "hw/acpi/bios-linker-loader.h" #include "exec/address-spaces.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "hw/acpi/erst.h" #include "trace.h" diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 8c4706f8cf..dbcd0f5366 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -19,7 +19,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" static const uint32_t ged_supported_events[] = { ACPI_GED_MEM_HOTPLUG_EVT, diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c index 9b1662b6b8..ca7b183d9e 100644 --- a/hw/acpi/hmat.c +++ b/hw/acpi/hmat.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/hmat.h" diff --git a/hw/acpi/hmat.h b/hw/acpi/hmat.h index fd989cb661..362b05e2dd 100644 --- a/hw/acpi/hmat.h +++ b/hw/acpi/hmat.h @@ -28,7 +28,7 @@ #define HMAT_H #include "hw/acpi/bios-linker-loader.h" -#include "sysemu/numa.h" +#include "system/numa.h" /* * ACPI 6.3: 5.2.27.3 Memory Proximity Domain Attributes Structure, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index c15e5b8281..c7a735bf64 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -31,8 +31,8 @@ #include "migration/vmstate.h" #include "qemu/timer.h" #include "hw/core/cpu.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ich9_tco.h" #include "hw/acpi/ich9_timer.h" diff --git a/hw/acpi/ich9_tco.c b/hw/acpi/ich9_tco.c index 81606219f7..6300db65b7 100644 --- a/hw/acpi/ich9_tco.c +++ b/hw/acpi/ich9_tco.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/southbridge/ich9.h" #include "migration/vmstate.h" diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 2bfaf5a38d..85fbd1ab68 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -28,9 +28,9 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/pcihp.h" #include "hw/acpi/piix4.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" #include "qapi/error.h" #include "qemu/range.h" #include "hw/acpi/cpu_hotplug.h" diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 9c2ca85cc7..8f32c0c616 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -20,7 +20,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" -#include "sysemu/reset.h" +#include "system/reset.h" void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, BIOSLinker *linker, const char *oem_id) diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 52327d9210..a829913f1b 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -23,7 +23,7 @@ #include "hw/sysbus.h" #include "hw/arm/allwinner-a10.h" #include "hw/misc/unimp.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/usb/hcd-ohci.h" #include "hw/loader.h" diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index fd7638dbe8..2efced3f66 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -28,7 +28,7 @@ #include "hw/misc/unimp.h" #include "hw/usb/hcd-ehci.h" #include "hw/loader.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/arm/allwinner-h3.h" #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index c6f7cab1da..47b3180f0e 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -30,7 +30,7 @@ #include "hw/misc/unimp.h" #include "hw/usb/hcd-ehci.h" #include "hw/loader.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/arm/allwinner-r40.h" #include "hw/misc/allwinner-r40-dramc.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index e20f719c9b..74f4d36e61 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -16,7 +16,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" #include "elf.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/log.h" diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 556498f2a0..a18d4ed1fb 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -23,13 +23,13 @@ #include "hw/sensor/tmp105.h" #include "hw/misc/led.h" #include "hw/qdev-properties.h" -#include "sysemu/block-backend.h" -#include "sysemu/reset.h" +#include "system/block-backend.h" +#include "system/reset.h" #include "hw/loader.h" #include "qemu/error-report.h" #include "qemu/units.h" #include "hw/qdev-clock.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index 9f98ad8e87..e76c7100a1 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index 3c1b419945..8784b6e793 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -20,7 +20,7 @@ #include "qemu/error-report.h" #include "hw/i2c/aspeed_i2c.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "target/arm/cpu-qom.h" #define ASPEED_SOC_IOMEM_SIZE 0x00200000 diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index b5703bd064..07210483bb 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -15,7 +15,7 @@ #include "qemu/error-report.h" #include "hw/i2c/aspeed_i2c.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "target/arm/cpu-qom.h" #define ASPEED_SOC_IOMEM_SIZE 0x00200000 diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 23571584b2..fee3755837 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -18,7 +18,7 @@ #include "qemu/error-report.h" #include "hw/i2c/aspeed_i2c.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/intc/arm_gicv3.h" #include "qapi/qmp/qlist.h" #include "qemu/log.h" diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index ac153a96b9..adc9730c2e 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -15,7 +15,7 @@ #include "hw/arm/bcm2835_peripherals.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Peripheral base address on the VC (GPU) system bus */ #define BCM2835_VC_PERI_BASE 0x7e000000 diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 5301d8d318..68fe8654e6 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -14,15 +14,15 @@ #include #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/sysemu.h" -#include "sysemu/numa.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/system.h" +#include "system/numa.h" #include "hw/boards.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/units.h" diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 6df5547977..5836619d9f 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -25,7 +25,7 @@ #include "qemu/module.h" #include "hw/arm/digic.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 4093af09cb..2492fafeb8 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -31,7 +31,7 @@ #include "hw/arm/digic.h" #include "hw/block/flash.h" #include "hw/loader.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "qemu/units.h" #include "qemu/cutils.h" diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index e3f1de2631..dd0edc81d5 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -27,8 +27,8 @@ #include "cpu.h" #include "hw/cpu/a9mpcore.h" #include "hw/irq.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/blockdev.h" +#include "system/system.h" #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/loader.h" diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index c9964bd283..83d08e578b 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -8,8 +8,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/block-backend.h" #include "hw/boards.h" #include "hw/qdev-clock.h" #include "hw/arm/aspeed_soc.h" diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 48763b03fe..58d818d002 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx25.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "chardev/char.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 4b8d9b8e4f..9de0f2148f 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx31.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "exec/address-spaces.h" #include "hw/qdev-properties.h" #include "chardev/char.h" diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 236d15bc9c..7f56d68b93 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -26,7 +26,7 @@ #include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "chardev/char.h" #include "qemu/error-report.h" #include "qemu/module.h" diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 1e0bbbb5d7..4aceefaa0f 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -22,7 +22,7 @@ #include "hw/misc/unimp.h" #include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 0310c15b0c..4c38876101 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -23,7 +23,7 @@ #include "hw/arm/fsl-imx7.h" #include "hw/misc/unimp.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index f103921d49..495704d972 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -25,8 +25,8 @@ #include "hw/arm/boot.h" #include "hw/loader.h" #include "net/net.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "hw/char/pl011.h" diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 7dfddd49e2..c9c2e5dd3b 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -30,7 +30,7 @@ #include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/i2c/i2c.h" #include "qemu/cutils.h" diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index ee6c7e0c0d..d85564893d 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -17,8 +17,8 @@ #include "hw/net/smc91c111.h" #include "net/net.h" #include "exec/address-spaces.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "hw/char/pl011.h" diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index fbd140e383..08d2b3025c 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -23,8 +23,8 @@ #include "net/net.h" #include "hw/net/lan9118.h" #include "hw/char/serial-mm.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" +#include "system/qtest.h" +#include "system/system.h" #include "qemu/cutils.h" /* Memory map for Kzm Emulation Baseboard: diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 500427e94b..690cb64ef3 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -17,7 +17,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static void mcimx6ul_evk_init(MachineState *machine) { diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 693a1023b6..b3e8e50779 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -19,7 +19,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static void mcimx7d_sabre_init(MachineState *machine) { diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 50df362088..374fbcb361 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -12,7 +12,7 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/arm/boot.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "exec/address-spaces.h" #include "hw/arm/nrf51_soc.h" diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8edf57a66d..0136e419bf 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -55,8 +55,8 @@ #include "hw/or-irq.h" #include "hw/boards.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/reset.h" #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" #include "hw/timer/cmsdk-apb-timer.h" diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 50919ee46d..efb3500742 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -34,7 +34,7 @@ #include "hw/or-irq.h" #include "hw/boards.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index 4d55a6564c..2b104671db 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -30,7 +30,7 @@ #include "qapi/qmp/qlist.h" #include "exec/address-spaces.h" #include "cpu.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/or-irq.h" #include "hw/qdev-clock.h" diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 5d7c3f2e5a..0f0ac46dab 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -30,7 +30,7 @@ #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" #include "hw/qdev-clock.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #define MSF2_TIMER_BASE 0x40004000 #define MSF2_SYSREG_BASE 0x40038000 diff --git a/hw/arm/musca.c b/hw/arm/musca.c index e2c9d49af5..3c3b534cb7 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -23,7 +23,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/arm/boot.h" #include "hw/arm/armsse.h" #include "hw/boards.h" diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 33ece06bbd..a712ff954b 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -16,7 +16,7 @@ #include "migration/vmstate.h" #include "hw/arm/boot.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "qemu/timer.h" @@ -29,9 +29,9 @@ #include "hw/irq.h" #include "hw/or-irq.h" #include "hw/audio/wm8750.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/runstate.h" +#include "system/dma.h" #include "ui/pixel_ops.h" #include "qemu/cutils.h" #include "qom/object.h" diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 2960b63b59..49ea8f949b 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu/bswap.h" #include "qemu/units.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "target/arm/cpu-qom.h" /* diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index e229efb447..7727e0dc4b 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -27,9 +27,9 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "qemu/units.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" +#include "system/block-backend.h" #include "qemu/error-report.h" diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 25030c7e40..f3a0ac40e4 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -29,13 +29,13 @@ #include "hw/qdev-properties.h" #include "hw/arm/boot.h" #include "hw/arm/omap.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/blockdev.h" +#include "system/system.h" #include "hw/arm/soc_dma.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "qemu/range.h" #include "hw/sysbus.h" #include "qemu/cutils.h" diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 62d7915fb8..623ebd6639 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -33,7 +33,7 @@ #include "hw/boards.h" #include "hw/arm/boot.h" #include "hw/block/flash.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "exec/address-spaces.h" #include "qemu/cutils.h" #include "qemu/error-report.h" diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 85877880fc..1264e0d6ee 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -15,7 +15,7 @@ #include "hw/display/bcm2835_fb.h" #include "hw/registerfields.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/arm/boot.h" diff --git a/hw/arm/realview.c b/hw/arm/realview.c index b186f965c6..9900a98f3b 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -19,7 +19,7 @@ #include "hw/pci/pci.h" #include "hw/qdev-core.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/i2c/i2c.h" #include "qemu/error-report.h" diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 56f184b9ae..1eb47042ec 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -17,7 +17,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static struct arm_boot_info sabrelite_binfo = { /* DDR memory start */ diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index e3195d5449..581655d771 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -23,11 +23,11 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/units.h" -#include "sysemu/device_tree.h" -#include "sysemu/kvm.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/kvm.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.h" #include "exec/hwaddr.h" #include "kvm_arm.h" #include "hw/arm/boot.h" diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index e31884b23e..1fee37a7c1 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -20,7 +20,7 @@ #include "hw/boards.h" #include "qemu/log.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/arm/armv7m.h" #include "hw/char/pl011.h" #include "hw/input/stellaris_gamepad.h" diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index 808b783515..53b5636452 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -32,7 +32,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* stm32f100_soc implementation is derived from stm32f205_soc */ diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index a451e21f59..47a54e592b 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -30,7 +30,7 @@ #include "hw/arm/stm32f205_soc.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* At the moment only Timer 2 to 5 are modelled */ static const uint32_t timer_addr[STM_NUM_TIMERS] = { 0x40000000, 0x40000400, diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index 72ae62156f..18d8824f29 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/arm/stm32f405_soc.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 16e3505dcb..dbf75329f7 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -25,7 +25,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/or-irq.h" #include "hw/arm/stm32l4x5_soc.h" #include "hw/char/stm32l4x5_usart.h" diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 4c4ff21e80..051b1d87a1 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -38,8 +38,8 @@ #include "hw/arm/boot.h" #include "chardev/char-fe.h" #include "chardev/char-serial.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "hw/ssi/ssi.h" #include "qapi/error.h" #include "qemu/cutils.h" diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index d48235453e..bc4522989e 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -14,7 +14,7 @@ #include "hw/arm/boot.h" #include "hw/net/smc91c111.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" #include "hw/i2c/arm_sbcon_i2c.h" diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index de815d84cc..42c6703406 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -30,11 +30,11 @@ #include "hw/net/lan9118.h" #include "hw/i2c/i2c.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/block/flash.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "qemu/error-report.h" #include #include "hw/char/pl011.h" diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 620992c92c..94c3248212 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -51,9 +51,9 @@ #include "hw/intc/arm_gicv3_its_common.h" #include "hw/mem/nvdimm.h" #include "hw/platform-bus.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/tpm.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/tpm.h" #include "migration/vmstate.h" #include "hw/acpi/ghes.h" #include "hw/acpi/viot.h" diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 333eaf67ea..f9b3380815 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -42,14 +42,14 @@ #include "hw/vfio/vfio-amd-xgbe.h" #include "hw/display/ramfb.h" #include "net/net.h" -#include "sysemu/device_tree.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/tpm.h" -#include "sysemu/tcg.h" -#include "sysemu/kvm.h" -#include "sysemu/hvf.h" -#include "sysemu/qtest.h" +#include "system/device_tree.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/tpm.h" +#include "system/tcg.h" +#include "system/kvm.h" +#include "system/hvf.h" +#include "system/qtest.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c index 33f0dd5982..d1509bd235 100644 --- a/hw/arm/xen-pvh.c +++ b/hw/arm/xen-pvh.c @@ -8,7 +8,7 @@ #include "qemu/error-report.h" #include "qapi/qapi-commands-migration.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/xen/xen-pvh-common.h" #include "hw/xen/arch_hvm.h" diff --git a/hw/arm/xen-stubs.c b/hw/arm/xen-stubs.c index 4ac6a56a96..34beb8b08c 100644 --- a/hw/arm/xen-stubs.c +++ b/hw/arm/xen-stubs.c @@ -8,7 +8,7 @@ #include "qemu/error-report.h" #include "qapi/qapi-commands-migration.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/xen/xen-hvm-common.h" #include "hw/xen/arch_hvm.h" diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index fde4d946b7..8477b82874 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -21,7 +21,7 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/block/flash.h" #include "hw/loader.h" @@ -35,7 +35,7 @@ #include "hw/cpu/a9mpcore.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qom/object.h" #include "exec/tswap.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 8b12d3e7cb..1401d37959 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/block/flash.h" #include "hw/boards.h" #include "hw/sysbus.h" diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 3adbe7b1fb..6eb8acf4f1 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -16,8 +16,8 @@ #include "qemu/module.h" #include "hw/sysbus.h" #include "net/net.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" +#include "system/system.h" +#include "system/kvm.h" #include "hw/arm/boot.h" #include "kvm_arm.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 4667cb333c..70fb444bbd 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -22,7 +22,7 @@ #include "hw/boards.h" #include "qemu/error-report.h" #include "qemu/log.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "qom/object.h" #include "net/can_emu.h" #include "audio/audio.h" diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 1082c62c30..3974392946 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -22,8 +22,8 @@ #include "hw/intc/arm_gic_common.h" #include "hw/misc/unimp.h" #include "hw/boards.h" -#include "sysemu/kvm.h" -#include "sysemu/sysemu.h" +#include "system/kvm.h" +#include "system/system.h" #include "kvm_arm.h" #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 8033bbbaed..667286207c 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -24,7 +24,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #include "ac97.h" diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 6170425a5a..513f987cc4 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -32,7 +32,7 @@ #include "migration/vmstate.h" #include "qemu/cutils.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #include "trace.h" diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 3e4a755228..fe9f4058fe 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -30,7 +30,7 @@ #include "intel-hda.h" #include "migration/vmstate.h" #include "intel-hda-defs.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qapi/error.h" #include "qom/object.h" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index e2b112e059..4c89ed3fc9 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "qemu/lockable.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" #include "qapi/error.h" #include "hw/audio/virtio-snd.h" diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index ce630ec572..1f38fa21c8 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -14,7 +14,7 @@ #include "qapi/error.h" #include "exec/memory.h" #include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qom/object.h" diff --git a/hw/block/block.c b/hw/block/block.c index 3ceca7dce6..1d405e02bf 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" #include "block/block_int-common.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/block-backend.h" #include "hw/block/block.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index 98501e6885..48c2e315f3 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -27,8 +27,8 @@ #include "hw/xen/xen.h" #include "hw/block/xen_blkif.h" #include "hw/xen/interface/io/ring.h" -#include "sysemu/block-backend.h" -#include "sysemu/iothread.h" +#include "system/block-backend.h" +#include "system/iothread.h" #include "xen-block.h" typedef struct XenBlockRequest { diff --git a/hw/block/dataplane/xen-block.h b/hw/block/dataplane/xen-block.h index 7b8e9df09f..eb70327db3 100644 --- a/hw/block/dataplane/xen-block.h +++ b/hw/block/dataplane/xen-block.h @@ -10,7 +10,7 @@ #include "hw/block/block.h" #include "hw/xen/xen-bus.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" typedef struct XenBlockDataPlane XenBlockDataPlane; diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index 2b9f667fe4..4d9b6d53d3 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -39,9 +39,9 @@ #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" #include "exec/ioport.h" #include "qemu/log.h" #include "qemu/main-loop.h" diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 57d6844806..3790d5685f 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -39,9 +39,9 @@ #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index 2b0af4430f..f3939e73f4 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -31,7 +31,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qapi-types-block.h" #include "qemu/bswap.h" #include "hw/block/block.h" diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index ca97365926..bc055c9d85 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/block/block.h" #include "hw/block/flash.h" #include "hw/qdev-properties.h" diff --git a/hw/block/nand.c b/hw/block/nand.c index b6e6bfac23..385deb2a91 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -23,7 +23,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 20f4fc67a0..26bf6e4601 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -41,7 +41,7 @@ #include "hw/block/flash.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/bitops.h" @@ -50,8 +50,8 @@ #include "qemu/option.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/blockdev.h" -#include "sysemu/runstate.h" +#include "system/blockdev.h" +#include "system/runstate.h" #include "trace.h" #define PFLASH_BE 0 diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index c82002d665..6a7d16b855 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -41,7 +41,7 @@ #include "qemu/error-report.h" #include "qemu/bitmap.h" #include "qemu/timer.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/host-utils.h" #include "qemu/module.h" #include "hw/sysbus.h" diff --git a/hw/block/swim.c b/hw/block/swim.c index c336d83bdc..7b392e0dea 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/block/block.h" diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index f3ac007108..879913a42b 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -29,8 +29,8 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" static const int user_feature_bits[] = { VIRTIO_BLK_F_SIZE_MAX, diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 9ca60fbc07..89d9279eda 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -22,10 +22,10 @@ #include "trace.h" #include "hw/block/block.h" #include "hw/qdev-properties.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-ram-registrar.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/blockdev.h" +#include "system/block-ram-registrar.h" +#include "system/system.h" +#include "system/runstate.h" #include "hw/virtio/virtio-blk.h" #include "scsi/constants.h" #ifdef __linux__ diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 0c0817f498..990d8f3d76 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -23,9 +23,9 @@ #include "hw/qdev-properties.h" #include "hw/xen/xen-block.h" #include "hw/xen/xen-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" -#include "sysemu/iothread.h" +#include "system/blockdev.h" +#include "system/block-backend.h" +#include "system/iothread.h" #include "dataplane/xen-block.h" #include "hw/xen/interface/io/xs_wire.h" #include "trace.h" diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index 68e261236e..e3461e00a8 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -16,7 +16,7 @@ #include "qemu/log.h" #include "trace.h" #include "exec/address-spaces.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/char/goldfish_tty.h" #define GOLDFISH_TTY_VERSION 1 diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c index a5ce6ee13a..b6dfb6cc31 100644 --- a/hw/char/parallel-isa.c +++ b/hw/char/parallel-isa.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "hw/char/parallel-isa.h" diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 15191698f5..6560341419 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -33,8 +33,8 @@ #include "migration/vmstate.h" #include "hw/char/parallel-isa.h" #include "hw/char/parallel.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 0345088e8b..2a283a2dec 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -30,8 +30,8 @@ #include "qemu/error-report.h" #include "exec/address-spaces.h" #include "exec/tswap.h" -#include "sysemu/dma.h" -#include "sysemu/runstate.h" +#include "system/dma.h" +#include "system/runstate.h" #define RISCV_DEBUG_HTIF 0 #define HTIF_DEBUG(fmt, ...) \ diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 2cf50eb0bb..9472abf917 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/acpi/acpi_aml_interface.h" #include "hw/char/serial.h" #include "hw/char/serial-isa.h" diff --git a/hw/char/serial.c b/hw/char/serial.c index 85dba02ace..76a17f1a4b 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -31,8 +31,8 @@ #include "chardev/char-serial.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/error-report.h" #include "trace.h" #include "hw/qdev-properties.h" diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index c20c1b4b84..88e12bb410 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -25,7 +25,7 @@ #include #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "chardev/char-fe.h" #include "hw/xen/xen-backend.h" #include "hw/xen/xen-bus-helper.h" diff --git a/hw/core/clock.c b/hw/core/clock.c index 391095eb4e..a81f888e62 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/visitor.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/clock.h" #include "trace.h" diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 09c7903594..1edc16f65c 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -21,13 +21,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/lockcnt.h" #include "exec/log.h" #include "exec/gdbstub.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "trace.h" diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-system.c similarity index 98% rename from hw/core/cpu-sysemu.c rename to hw/core/cpu-system.c index 2a9a2a4eb5..6aae28a349 100644 --- a/hw/core/cpu-sysemu.c +++ b/hw/core/cpu-system.c @@ -1,5 +1,5 @@ /* - * QEMU CPU model (system emulation specific) + * QEMU CPU model (system specific) * * Copyright (c) 2012-2014 SUSE LINUX Products GmbH * diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index c1cddecf60..fca9ac0e36 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -32,8 +32,8 @@ #include "qemu/osdep.h" #include "exec/tswap.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/qdev-properties.h" diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c index 74af00cee7..fb23882786 100644 --- a/hw/core/guest-loader.c +++ b/hw/core/guest-loader.c @@ -26,13 +26,13 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/loader.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/module.h" #include "guest-loader.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/boards.h" /* diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 7ccc9d5fbc..9bdd4fa17c 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -25,7 +25,7 @@ #include "hw/loader-fit.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include #include diff --git a/hw/core/loader.c b/hw/core/loader.c index 31593a1171..c0407e2d0d 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -53,15 +53,15 @@ #include "disas/disas.h" #include "migration/vmstate.h" #include "monitor/monitor.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "uboot_image.h" #include "hw/loader.h" #include "hw/nvram/fw_cfg.h" #include "exec/memory.h" #include "hw/boards.h" #include "qemu/cutils.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "tcg/debuginfo.h" #include diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 8701f00cc7..916727961c 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -22,7 +22,7 @@ #include "qapi/qmp/qdict.h" #include "qapi/string-output-visitor.h" #include "qemu/error-report.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" void hmp_info_cpus(Monitor *mon, const QDict *qdict) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 130217da8f..4eefe6ba86 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -20,11 +20,11 @@ #include "qapi/type-helpers.h" #include "qemu/uuid.h" #include "qom/qom-qobject.h" -#include "sysemu/hostmem.h" -#include "sysemu/hw_accel.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/hostmem.h" +#include "system/hw_accel.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.h" /* * fast means: we NEVER interrupt vCPU threads to retrieve diff --git a/hw/core/machine.c b/hw/core/machine.c index 3d734f8c18..008d3379e1 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/accel.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "hw/boards.h" #include "hw/loader.h" #include "qemu/error-report.h" @@ -21,12 +21,12 @@ #include "qapi/qapi-visit-machine.h" #include "qemu/madvise.h" #include "qom/object_interfaces.h" -#include "sysemu/cpus.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/xen.h" -#include "sysemu/qtest.h" +#include "system/cpus.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/xen.h" +#include "system/qtest.h" #include "hw/pci/pci_bridge.h" #include "hw/mem/nvdimm.h" #include "migration/global_state.h" diff --git a/hw/core/meson.build b/hw/core/meson.build index 9fd0b5aaa5..ce9dfa3f4b 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -27,7 +27,7 @@ system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls]) system_ss.add(files( - 'cpu-sysemu.c', + 'cpu-system.c', 'fw-path-provider.c', 'gpio.c', 'hotplug.c', diff --git a/hw/core/numa.c b/hw/core/numa.c index 1b5f44baea..218576f745 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -24,15 +24,15 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "exec/cpu-common.h" #include "exec/ramlist.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-machine.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/core/cpu.h" #include "hw/mem/pc-dimm.h" #include "hw/boards.h" diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 1d8964d804..7f63d17ca1 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -11,8 +11,8 @@ #include "migration/vmstate.h" #include "qemu/host-utils.h" #include "exec/replay-core.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/qtest.h" +#include "system/cpu-timers.h" +#include "system/qtest.h" #include "block/aio.h" #include "hw/clock.h" diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 22ea1ed358..c91c383511 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -29,8 +29,8 @@ #include "audio/audio.h" #include "chardev/char-fe.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "net/net.h" #include "hw/pci/pci.h" #include "hw/pci/pcie.h" diff --git a/hw/core/reset.c b/hw/core/reset.c index 14a2639fbf..8a3e0e518f 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/resettable.h" #include "hw/core/resetcontainer.h" diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index eebcd28f9a..774c0aed41 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -29,8 +29,8 @@ #endif #include "hw/core/sysbus-fdt.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" -#include "sysemu/tpm.h" +#include "system/device_tree.h" +#include "system/tpm.h" #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" diff --git a/hw/core/vm-change-state-handler.c b/hw/core/vm-change-state-handler.c index 8e2639224e..7064995578 100644 --- a/hw/core/vm-change-state-handler.c +++ b/hw/core/vm-change-state-handler.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" static int qdev_get_dev_tree_depth(DeviceState *dev) { diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 5346b8b6c6..993550583f 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -24,7 +24,7 @@ #include "hw/cpu/a15mpcore.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_arm.h" #include "target/arm/gtimer.h" diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index e9f2543c43..2c6b43cd0d 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -10,7 +10,7 @@ #include "qemu/bitmap.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/boards.h" #include "qapi/qapi-visit-machine.h" diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index ce9aa18364..516c01d840 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -19,7 +19,7 @@ #include "qemu/log.h" #include "qemu/units.h" #include "qemu/uuid.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qemu/range.h" #define CXL_CAPACITY_MULTIPLIER (256 * MiB) diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 198ed9ed9b..6dbf06f6e9 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -36,7 +36,7 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qemu/log.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" #include "trace.h" #include "hw/pci/pci_device.h" diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 335d01edde..eda6d3de37 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qxl.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 949949d374..f38a602aab 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -29,7 +29,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/qdev-properties.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "trace.h" diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index 6086baf7a9..8c0f907673 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -17,7 +17,7 @@ #include "hw/display/ramfb.h" #include "hw/display/bochs-vbe.h" /* for limits */ #include "ui/console.h" -#include "sysemu/reset.h" +#include "system/reset.h" struct QEMU_PACKED RAMFBCfg { uint64_t addr; diff --git a/hw/display/vga.c b/hw/display/vga.c index b074b58c90..b01f67c65f 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" #include "exec/tswap.h" #include "hw/display/vga.h" diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index 6f31149e1e..d8a1e1d8db 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -16,7 +16,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-bswap.h" diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index c02ec6d37d..85ca23cb32 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -20,7 +20,7 @@ #include "hw/virtio/virtio-gpu-pixman.h" #include "trace.h" #include "exec/ramblock.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include #include #include "qemu/memfd.h" diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 82741d19e5..96baa40231 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -14,12 +14,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/iov.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "ui/console.h" #include "ui/rect.h" #include "trace.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/dma.h" +#include "system/system.h" #include "hw/virtio/virtio.h" #include "migration/qemu-file-types.h" #include "hw/virtio/virtio-gpu.h" diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 314d378a1b..22822fecea 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -29,7 +29,7 @@ #include "ui/input.h" #include "ui/console.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/interface/io/fbif.h" diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index d5a0a1caa2..94f42c933b 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -22,7 +22,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c index 1dd88f3479..25b3d6a155 100644 --- a/hw/dma/sifive_pdma.c +++ b/hw/dma/sifive_pdma.c @@ -28,7 +28,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/dma/sifive_pdma.h" #define DMA_CONTROL 0x000 diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 9fdba16603..280b747521 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -32,7 +32,7 @@ #include "hw/sparc/sun4m_iommu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qapi/error.h" #include "qemu/module.h" #include "trace.h" diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index f09452d0b5..e02aec78bc 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -33,7 +33,7 @@ #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/stream.h" #include "qom/object.h" #include "trace.h" diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c index b8544d0731..0fd0d23f57 100644 --- a/hw/dma/xlnx-zynq-devcfg.c +++ b/hw/dma/xlnx-zynq-devcfg.c @@ -29,7 +29,7 @@ #include "hw/irq.h" #include "migration/vmstate.h" #include "qemu/bitops.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index d78dc6444b..4170714351 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -25,7 +25,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ptimer.h" #include "hw/stream.h" #include "hw/register.h" diff --git a/hw/gpio/gpio_pwr.c b/hw/gpio/gpio_pwr.c index dbaf1c70c8..2d14f8b344 100644 --- a/hw/gpio/gpio_pwr.c +++ b/hw/gpio/gpio_pwr.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define TYPE_GPIOPWR "gpio-pwr" OBJECT_DECLARE_SIMPLE_TYPE(GPIO_PWR_State, GPIOPWR) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 4e67335322..65259308e2 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -11,10 +11,10 @@ #include "elf.h" #include "hw/loader.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" #include "hw/rtc/mc146818rtc.h" #include "hw/timer/i8254.h" #include "hw/char/serial-mm.h" diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 74897b1604..ba622e80ff 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -32,9 +32,9 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qemu/timer.h" -#include "sysemu/balloon.h" -#include "sysemu/hostmem.h" -#include "sysemu/reset.h" +#include "system/balloon.h" +#include "system/hostmem.h" +#include "system/reset.h" #include "hv-balloon-our_range_memslots.h" #include "hv-balloon-page_range_tree.h" #include "trace.h" diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index ba94bf9f8d..831e04f214 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -13,7 +13,7 @@ #include "qapi/error.h" #include "exec/address-spaces.h" #include "exec/memory.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/bitops.h" #include "qemu/error-report.h" #include "qemu/lockable.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 9fcc2897b8..733b8f0851 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -40,18 +40,18 @@ #include "hw/acpi/acpi_aml_interface.h" #include "hw/input/i8042.h" #include "hw/acpi/memory_hotplug.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "hw/acpi/tpm.h" #include "hw/acpi/vmgenid.h" #include "hw/acpi/erst.h" #include "hw/acpi/piix4.h" -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/mem/memory-device.h" #include "hw/mem/nvdimm.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" +#include "system/numa.h" +#include "system/reset.h" #include "hw/hyperv/vmbus-bridge.h" /* Supported chipsets: */ diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 0e4494627c..91bf1df0f2 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" #include "hw/firmware/smbios.h" diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index a5b268342f..fa28c33f21 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -32,9 +32,9 @@ #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "hw/pci-host/q35.h" -#include "sysemu/kvm.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/kvm.h" +#include "system/dma.h" +#include "system/system.h" #include "hw/i386/apic_internal.h" #include "kvm/kvm_i386.h" #include "migration/vmstate.h" diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index a72c28e8a7..602c769656 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -14,8 +14,8 @@ #include "qemu/module.h" #include "hw/i386/apic_internal.h" #include "hw/pci/msi.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" +#include "system/hw_accel.h" +#include "system/kvm.h" #include "kvm/kvm_i386.h" static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic, diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 71150ed2e0..610af634cc 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -16,9 +16,9 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "sysemu/hw_accel.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/hw_accel.h" #include "kvm/kvm_i386.h" #include "migration/vmstate.h" #include "hw/sysbus.h" diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 2933d3f458..eb9a6f79fe 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -29,11 +29,11 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/timer/i8254.h" #include "hw/timer/i8254_internal.h" #include "hw/qdev-properties-system.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "target/i386/kvm/kvm_i386.h" #include "qom/object.h" diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index 9c2fb645fe..272c04df0b 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -16,7 +16,7 @@ #include "qemu/module.h" #include "hw/intc/kvm_irqcount.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qom/object.h" #define TYPE_KVM_I8259 "kvm-i8259" diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 217ff43b98..b8bc46518b 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -15,7 +15,7 @@ #include "hw/qdev-properties.h" #include "hw/intc/ioapic_internal.h" #include "hw/intc/kvm_irqcount.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm/kvm_i386.h" /* PC Utility function */ diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 07bd0c9ab8..bd2a3cbee0 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -41,8 +41,8 @@ #include "xen_overlay.h" #include "xen_xenstore.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" #include #include diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 245e4b15db..7b843a72b1 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -27,8 +27,8 @@ #include "xen_gnttab.h" #include "xen_primary_console.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" #include "hw/xen/interface/memory.h" #include "hw/xen/interface/grant_table.h" diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c index 3483a332a6..db9aa7942d 100644 --- a/hw/i386/kvm/xen_overlay.c +++ b/hw/i386/kvm/xen_overlay.c @@ -23,8 +23,8 @@ #include "hw/xen/xen.h" #include "xen_overlay.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" #include #include "hw/xen/interface/memory.h" diff --git a/hw/i386/kvm/xen_primary_console.c b/hw/i386/kvm/xen_primary_console.c index abe79f565b..8ad2363d18 100644 --- a/hw/i386/kvm/xen_primary_console.c +++ b/hw/i386/kvm/xen_primary_console.c @@ -20,8 +20,8 @@ #include "xen_overlay.h" #include "xen_primary_console.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" #include "trace.h" diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 1a9bc342b8..5969105667 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -28,8 +28,8 @@ #include "xen_primary_console.h" #include "xen_xenstore.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" #include "trace.h" diff --git a/hw/i386/microvm-dt.c b/hw/i386/microvm-dt.c index fc5db6ed7f..cb27dfd732 100644 --- a/hw/i386/microvm-dt.c +++ b/hw/i386/microvm-dt.c @@ -33,7 +33,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/char/serial-isa.h" #include "hw/i386/fw_cfg.h" #include "hw/rtc/mc146818rtc.h" diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 86637afa0f..c3d7fe3c42 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -22,11 +22,11 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qapi/qapi-visit-common.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/cpus.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/runstate.h" #include "acpi-microvm.h" #include "microvm-dt.h" diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index b2648bff71..1d66ca3204 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -29,7 +29,7 @@ #include "multiboot.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" /* Show multiboot debug output */ diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c index acbbc06b71..a058608afc 100644 --- a/hw/i386/nitro_enclave.c +++ b/hw/i386/nitro_enclave.c @@ -22,7 +22,7 @@ #include "hw/virtio/virtio-mmio.h" #include "hw/virtio/virtio-nsm.h" #include "hw/virtio/vhost-user-vsock.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" static BusState *find_free_virtio_mmio_bus(void) { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 92047ce8c9..53a2f226d0 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -30,7 +30,7 @@ #include "hw/hyperv/hv-balloon.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/vmport.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "hw/ide/ide-bus.h" #include "hw/timer/hpet.h" #include "hw/loader.h" @@ -39,9 +39,9 @@ #include "hw/timer/i8254.h" #include "hw/input/i8042.h" #include "hw/audio/pcspk.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/xen.h" +#include "system/reset.h" #include "kvm/kvm_i386.h" #include "hw/xen/xen.h" #include "qapi/qmp/qlist.h" diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e4365cbdb0..04d2957adc 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -43,7 +43,7 @@ #include "hw/ide/isa.h" #include "hw/ide/pci.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/i386/kvm/clock.h" #include "hw/sysbus.h" #include "hw/i2c/smbus_eeprom.h" @@ -51,7 +51,7 @@ #include "hw/acpi/acpi.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/xen.h" +#include "system/xen.h" #ifdef CONFIG_XEN #include #include "hw/xen/xen_pt.h" @@ -61,8 +61,8 @@ #include "hw/xen/xen.h" #include "migration/global_state.h" #include "migration/misc.h" -#include "sysemu/runstate.h" -#include "sysemu/numa.h" +#include "system/runstate.h" +#include "system/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" #include "hw/i386/acpi-build.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index bbbdacda8e..77536dd697 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -35,8 +35,8 @@ #include "hw/loader.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/rtc/mc146818rtc.h" -#include "sysemu/tcg.h" -#include "sysemu/kvm.h" +#include "system/tcg.h" +#include "system/kvm.h" #include "hw/i386/kvm/clock.h" #include "hw/pci-host/q35.h" #include "hw/pci/pcie_port.h" @@ -55,7 +55,7 @@ #include "hw/usb/hcd-uhci.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" #include "hw/i386/acpi-build.h" diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index e6271e1020..1eeb58ab37 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/units.h" @@ -35,7 +35,7 @@ #include "hw/loader.h" #include "hw/qdev-properties.h" #include "hw/block/flash.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "target/i386/sev.h" #define FLASH_SECTOR_SIZE 4096 diff --git a/hw/i386/port92.c b/hw/i386/port92.c index 1b03b34f1d..1ba3f32887 100644 --- a/hw/i386/port92.c +++ b/hw/i386/port92.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/isa/isa.h" diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 4900dd414a..e665e2111c 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -21,8 +21,8 @@ #include "qemu/error-report.h" #include "qapi/qapi-commands-misc-target.h" #include "exec/address-spaces.h" -#include "sysemu/hw_accel.h" -#include "sysemu/reset.h" +#include "system/hw_accel.h" +#include "system/reset.h" #include #include "hw/acpi/aml-build.h" diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c index ef7f8b967f..0e6d058d06 100644 --- a/hw/i386/vapic.c +++ b/hw/i386/vapic.c @@ -11,11 +11,11 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "exec/address-spaces.h" #include "hw/i386/apic_internal.h" #include "hw/sysbus.h" diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c index cab6e72089..7be25524f1 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -33,9 +33,9 @@ #include "hw/i386/vmport.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/hw_accel.h" +#include "system/qtest.h" #include "qemu/log.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 3f78182692..a7d46c3105 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -26,9 +26,9 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qapi/error.h" -#include "sysemu/numa.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" +#include "system/numa.h" +#include "system/system.h" +#include "system/xen.h" #include "trace.h" #include "hw/i386/x86.h" diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c index ab2920522d..c876e6709e 100644 --- a/hw/i386/x86-cpu.c +++ b/hw/i386/x86-cpu.c @@ -21,15 +21,15 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "sysemu/whpx.h" -#include "sysemu/cpu-timers.h" +#include "system/whpx.h" +#include "system/cpu-timers.h" #include "trace.h" #include "hw/i386/x86.h" #include "target/i386/cpu.h" #include "hw/intc/i8259.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" /* TSC handling */ uint64_t cpu_get_tsc(CPUX86State *env) diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 155f6262ea..06d91c6c13 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -25,7 +25,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, iec_notify_fn fn, void *data) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 01fc5e6562..69bfc00b9a 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -27,8 +27,8 @@ #include "qapi/qapi-visit-common.h" #include "qapi/qapi-visit-machine.h" #include "qapi/visitor.h" -#include "sysemu/qtest.h" -#include "sysemu/numa.h" +#include "system/qtest.h" +#include "system/numa.h" #include "trace.h" #include "hw/acpi/aml-build.h" diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c index f1f02d3311..33c1027976 100644 --- a/hw/i386/xen/xen-pvh.c +++ b/hw/i386/xen/xen-pvh.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/xen/arch_hvm.h" #include #include "hw/xen/xen-pvh-common.h" diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index ec0e536e85..0f68c3fe7b 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -30,8 +30,8 @@ #include "migration/vmstate.h" #include "net/net.h" #include "trace.h" -#include "sysemu/xen.h" -#include "sysemu/block-backend.h" +#include "system/xen.h" +#include "system/block-backend.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index 9620de8ce8..575be36fc5 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -18,7 +18,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "migration/vmstate.h" #include "hw/ide/ahci-sysbus.h" diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index c02357735e..1303c21cb7 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -28,8 +28,8 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/dma.h" #include "ahci-internal.h" #include "ide-internal.h" diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index e82959dc2d..a42b748521 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "hw/scsi/scsi.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "scsi/constants.h" #include "ide-internal.h" #include "trace.h" diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 942f6c470c..20410c81fa 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -29,8 +29,8 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/isa/isa.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/ide/pci.h" #include "ide-internal.h" diff --git a/hw/ide/core.c b/hw/ide/core.c index 08d9218455..f9baba59e9 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -32,15 +32,15 @@ #include "qemu/timer.h" #include "qemu/hw-version.h" #include "qemu/memalign.h" -#include "sysemu/sysemu.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" +#include "system/system.h" +#include "system/blockdev.h" +#include "system/dma.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "ide-internal.h" #include "trace.h" diff --git a/hw/ide/ich.c b/hw/ide/ich.c index c99a44df8e..a83128465f 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -66,7 +66,7 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/isa/isa.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ide/pci.h" #include "hw/ide/ahci-pci.h" #include "ahci-internal.h" diff --git a/hw/ide/ide-bus.c b/hw/ide/ide-bus.c index 37d003dd9a..437502b5b4 100644 --- a/hw/ide/ide-bus.c +++ b/hw/ide/ide-bus.c @@ -21,9 +21,9 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/runstate.h" #include "ide-internal.h" static char *idebus_get_fw_dev_path(DeviceState *dev); diff --git a/hw/ide/ide-dev.c b/hw/ide/ide-dev.c index cc92531f1c..6eae80c588 100644 --- a/hw/ide/ide-dev.c +++ b/hw/ide/ide-dev.c @@ -23,9 +23,9 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/ide/ide-dev.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" #include "qapi/visitor.h" #include "ide-internal.h" diff --git a/hw/ide/isa.c b/hw/ide/isa.c index a0a7e4837c..b542549160 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -29,7 +29,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ide/isa.h" #include "qom/object.h" diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 25f8403e80..f5579bb896 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -30,8 +30,8 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/misc/macio/macio.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/dma.h" #include "ide-internal.h" diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 43ab66f347..71a17fe41c 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -27,7 +27,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ide/mmio.h" #include "hw/qdev-properties.h" diff --git a/hw/ide/pci.c b/hw/ide/pci.c index a008fe7316..0ed72e4223 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -27,7 +27,7 @@ #include "hw/irq.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/ide/pci.h" diff --git a/hw/ide/via.c b/hw/ide/via.c index c88eb6c025..89fd28f646 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -29,7 +29,7 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "qemu/range.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/isa/vt82c686.h" #include "hw/ide/pci.h" #include "hw/irq.h" diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 24a133fd25..6338a7525a 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -34,8 +34,8 @@ #include "hw/irq.h" #include "hw/input/i8042.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "trace.h" diff --git a/hw/input/ps2.c b/hw/input/ps2.c index d6f834443d..6a41b024c8 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -30,8 +30,8 @@ #include "migration/vmstate.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qapi/error.h" #include "trace.h" diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 4186c57b34..d1d343d421 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -26,7 +26,7 @@ #include "hw/intc/kvm_irqcount.h" #include "hw/pci/msi.h" #include "qemu/host-utils.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "trace.h" #include "hw/i386/apic-msidef.h" #include "qapi/error.h" diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 8be9f22de8..85ded28032 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -28,7 +28,7 @@ #include "hw/intc/kvm_irqcount.h" #include "trace.h" #include "hw/boards.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 2a48f0da2f..3581ff8e8a 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -27,8 +27,8 @@ #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" +#include "system/kvm.h" +#include "system/qtest.h" /* #define DEBUG_GIC */ diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index e961cd9156..5eba8badb8 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -26,7 +26,7 @@ #include "hw/arm/linux-boot-if.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static int gic_pre_save(void *opaque) { diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index e2a73337b1..40adb02865 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -23,7 +23,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "migration/blocker.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_arm.h" #include "gic_internal.h" #include "vgic_common.h" diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index ffa830b433..66d4377cde 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -31,7 +31,7 @@ #include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index a8ec615a3f..67d0ab71cc 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -31,7 +31,7 @@ #include "migration/vmstate.h" #include "gicv3_internal.h" #include "hw/arm/linux-boot-if.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index ea1d1b3455..9cad8313a3 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -22,8 +22,8 @@ #include "cpu.h" #include "target/arm/cpregs.h" #include "target/arm/cpu-features.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" +#include "system/tcg.h" +#include "system/qtest.h" /* * Special case return value from hppvi_index(); must be larger than diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 0b97362cd2..70dbee83a6 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -24,7 +24,7 @@ #include "hw/intc/arm_gicv3_its_common.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static int gicv3_its_pre_save(void *opaque) { diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 68a6144add..62961b62b4 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -24,8 +24,8 @@ #include "qemu/error-report.h" #include "hw/intc/arm_gicv3_its_common.h" #include "hw/qdev-properties.h" -#include "sysemu/runstate.h" -#include "sysemu/kvm.h" +#include "system/runstate.h" +#include "system/kvm.h" #include "kvm_arm.h" #include "migration/blocker.h" #include "qom/object.h" diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 9ea6b8e218..8e17cab2a0 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -24,8 +24,8 @@ #include "hw/intc/arm_gicv3_common.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "kvm_arm.h" #include "gicv3_internal.h" #include "vgic_common.h" diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 6e2803b123..10a773c07f 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -18,8 +18,8 @@ #include "hw/intc/armv7m_nvic.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/tcg.h" -#include "sysemu/runstate.h" +#include "system/tcg.h" +#include "system/runstate.h" #include "target/arm/cpu.h" #include "target/arm/cpu-features.h" #include "exec/exec-all.h" diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 6d566165b0..d101df1ca0 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -30,8 +30,8 @@ #include "hw/intc/ioapic_internal.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" -#include "sysemu/sysemu.h" +#include "system/kvm.h" +#include "system/system.h" #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "trace.h" diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 996db095c3..f34f0bc350 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -15,8 +15,8 @@ #include "qapi/error.h" #include "hw/sysbus.h" #include "exec/memory.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "kvm_mips.h" #include "hw/intc/mips_gic.h" #include "hw/irq.h" diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 135fe8301a..13aa653aa1 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -30,7 +30,7 @@ #include "hw/pci/msi.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 581659839b..c374aed43a 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -12,9 +12,9 @@ #include "qemu/module.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/cpus.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 5dd305453a..ae6da95df2 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -11,8 +11,8 @@ #include "qemu/log.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" +#include "system/cpus.h" +#include "system/dma.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" @@ -24,8 +24,8 @@ #include "hw/ppc/xive2_regs.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" -#include "sysemu/qtest.h" +#include "system/reset.h" +#include "system/qtest.h" #include diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 353eec8136..b56edecb50 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -30,8 +30,8 @@ #include "hw/intc/riscv_aplic.h" #include "hw/irq.h" #include "target/riscv/cpu.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" +#include "system/system.h" +#include "system/kvm.h" #include "kvm/kvm_riscv.h" #include "migration/vmstate.h" diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index adc36151b4..cfd9eca8bc 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -31,8 +31,8 @@ #include "hw/irq.h" #include "target/riscv/cpu.h" #include "target/riscv/cpu_bits.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" +#include "system/system.h" +#include "system/kvm.h" #include "migration/vmstate.h" #define IMSIC_MMIO_PAGE_LE 0x00 diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 7930d72390..10aaafbb31 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -16,7 +16,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/adapter.h" #include "hw/s390x/css.h" diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index 49895be803..5b011b2a7d 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -30,7 +30,7 @@ #include "target/riscv/cpu.h" #include "migration/vmstate.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static bool addr_between(uint32_t addr, uint32_t base, uint32_t num) { diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 09f643d633..bb9b2a4a12 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -13,8 +13,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/reset.h" +#include "system/cpus.h" +#include "system/reset.h" #include "migration/vmstate.h" #include "hw/ppc/fdt.h" #include "hw/ppc/spapr.h" diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 7a86197fc9..26d30b41c1 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -12,9 +12,9 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/cpus.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/spapr_xive.h" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 81bbfdd84b..aa3f7d7daa 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -37,8 +37,8 @@ #include "migration/vmstate.h" #include "hw/intc/intc.h" #include "hw/irq.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "target/ppc/cpu.h" void icp_pic_print_info(ICPState *icp, GString *buf) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 9719d98a17..ee72969f5f 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "trace.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/xics.h" diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 308e5743bd..5994e4ce36 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -12,9 +12,9 @@ #include "qemu/module.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/cpus.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "hw/irq.h" diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 3233d3f14e..91d3d68705 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -12,8 +12,8 @@ #include "qemu/module.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" +#include "system/cpus.h" +#include "system/dma.h" #include "hw/qdev-properties.h" #include "hw/ppc/xive.h" #include "hw/ppc/xive2.h" diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 850b3bc463..a1cb853d92 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -26,7 +26,7 @@ #include "hw/ipmi/ipmi.h" #include "hw/qdev-properties.h" #include "qom/object_interfaces.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qemu/module.h" #include "hw/nmi.h" diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 8a55893e89..bd02e8244e 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/timer.h" #include "hw/ipmi/ipmi.h" #include "qemu/error-report.h" diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index f1e0f14007..2599c1219a 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -22,7 +22,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "hw/sysbus.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/isa/isa.h" static ISABus *isabus; diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index cff756e791..4260da547c 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -14,7 +14,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "chardev/char.h" #include "hw/char/parallel.h" #include "hw/block/fdc.h" diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 378244aa8f..46a74fd6ef 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -46,8 +46,8 @@ #include "hw/acpi/ich9_timer.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/core/cpu.h" #include "hw/nvram/fw_cfg.h" #include "qemu/cutils.h" diff --git a/hw/isa/piix.c b/hw/isa/piix.c index 8ec9c63b8a..fee4d8804c 100644 --- a/hw/isa/piix.c +++ b/hw/isa/piix.c @@ -34,7 +34,7 @@ #include "hw/ide/piix.h" #include "hw/intc/i8259.h" #include "hw/isa/isa.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "hw/acpi/acpi_aml_interface.h" diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 50709bda0f..d538946bc3 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -18,7 +18,7 @@ #include "hw/acpi/bios-linker-loader.h" #include "migration/vmstate.h" #include "hw/mem/memory-device.h" -#include "sysemu/reset.h" +#include "system/reset.h" /* Supported chipsets: */ #include "hw/pci-host/ls7a.h" @@ -31,8 +31,8 @@ #include "hw/acpi/generic_event_device.h" #include "hw/pci-host/gpex.h" -#include "sysemu/sysemu.h" -#include "sysemu/tpm.h" +#include "system/system.h" +#include "system/tpm.h" #include "hw/platform-bus.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/hmat.h" diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index f258eefe9a..48154cdce6 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -12,8 +12,8 @@ #include "hw/loader.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" -#include "sysemu/qtest.h" +#include "system/reset.h" +#include "system/qtest.h" struct memmap_entry *memmap_table; unsigned memmap_entries; diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c index 35aeb2decb..493563669e 100644 --- a/hw/loongarch/fw_cfg.c +++ b/hw/loongarch/fw_cfg.c @@ -9,7 +9,7 @@ #include "hw/loongarch/fw_cfg.h" #include "hw/loongarch/virt.h" #include "hw/nvram/fw_cfg.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 43a3e0d4d4..3a905cf71d 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -10,13 +10,13 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/char/serial-mm.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" -#include "sysemu/rtc.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" +#include "system/rtc.h" #include "hw/loongarch/virt.h" #include "exec/address-spaces.h" #include "hw/irq.h" @@ -37,14 +37,14 @@ #include "qapi/qapi-visit-common.h" #include "hw/acpi/generic_event_device.h" #include "hw/mem/nvdimm.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include #include "hw/core/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/display/ramfb.h" #include "hw/mem/pc-dimm.h" -#include "sysemu/tpm.h" -#include "sysemu/block-backend.h" +#include "system/tpm.h" +#include "system/block-backend.h" #include "hw/block/flash.h" #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 1e8e64f8bd..7b8210475e 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -14,7 +14,7 @@ #include "hw/loader.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #define KERNEL_LOAD_ADDR 0x10000 #define AN5206_MBAR_ADDR 0x10000000 diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 45e5f74600..427b2ba095 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -16,7 +16,7 @@ #include "hw/m68k/mcf.h" #include "qemu/timer.h" #include "hw/ptimer.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/sysbus.h" /* General purpose timer module. */ diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index e37cd50d18..f290ccc739 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -26,8 +26,8 @@ #include "hw/m68k/mcf_fec.h" #include "qemu/timer.h" #include "hw/ptimer.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/qtest.h" #include "net/net.h" #include "hw/boards.h" #include "hw/loader.h" diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index a37ce00874..ec0794adba 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "exec/hwaddr.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/qtest.h" #include "hw/irq.h" #include "hw/m68k/next-cube.h" #include "hw/boards.h" diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 556604e1dc..ca3adb9a8a 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -24,7 +24,7 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qemu/guest-random.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "cpu.h" #include "hw/boards.h" #include "hw/or-irq.h" @@ -51,9 +51,9 @@ #include "net/util.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" #include "migration/vmstate.h" #define MACROM_ADDR 0x40800000 diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index d0a7a6bfe2..87ec39eeae 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/guest-random.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "cpu.h" #include "hw/boards.h" #include "hw/qdev-properties.h" @@ -24,9 +24,9 @@ #include "net/net.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" #include "hw/intc/m68k_irqc.h" #include "hw/misc/virt_ctrl.h" diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 12205c4d32..dcaf401764 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -25,8 +25,8 @@ #include "qemu/range.h" #include "qemu/rcu.h" #include "qemu/guest-random.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "hw/cxl/cxl.h" #include "hw/pci/msix.h" diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index a5f279adcc..1de8dfec7d 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -16,7 +16,7 @@ #include "hw/boards.h" #include "qemu/range.h" #include "hw/virtio/vhost.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "exec/address-spaces.h" #include "trace.h" diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 10506d52e4..9786319f27 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -30,7 +30,7 @@ #include "hw/mem/nvdimm.h" #include "hw/qdev-properties.h" #include "hw/mem/memory-device.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" static void nvdimm_get_label_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 49c5f9fd44..d2783523a8 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -28,8 +28,8 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "trace.h" static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index 8d681adfc0..1f647f8096 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -17,7 +17,7 @@ #include "hw/sysbus.h" #include "qapi/error.h" #include "qemu/units.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/mem/sparse-mem.h" #define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM) diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index ed61e483ee..966fb2cb2a 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -31,8 +31,8 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/guest-random.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 61e47d8398..deab275495 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -32,7 +32,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/block/flash.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "hw/qdev-properties.h" diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 6c0f5c6c65..4a969af1a0 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -30,7 +30,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/block/flash.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/misc/unimp.h" #include "exec/address-spaces.h" diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 1ced1e337a..67044af962 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -37,11 +37,11 @@ #include "qemu/guest-random.h" #include "qemu/log.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" #include #include "qom/object.h" diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 1a2208666c..2d442fe113 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -24,8 +24,8 @@ #include "hw/mips/mips.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number) { diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 904c10b90e..16b6a5129e 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -36,9 +36,9 @@ #include "hw/qdev-properties.h" #include "elf.h" #include "hw/isa/vt82c686.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/system.h" #include "qemu/error-report.h" #include "exec/tswap.h" diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 0e43c9f0ba..c89610639a 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -32,7 +32,7 @@ #include "hw/char/parallel.h" #include "hw/isa/isa.h" #include "hw/block/fdc.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "net/net.h" #include "hw/scsi/esp.h" @@ -44,8 +44,8 @@ #include "hw/audio/pcspk.h" #include "hw/input/i8042.h" #include "hw/sysbus.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/help_option.h" diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index f3b6326cc5..f12f8c3d3c 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -45,10 +45,10 @@ #include "hw/pci-host/gpex.h" #include "hw/usb.h" #include "net/net.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/error-report.h" #define PM_CNTL_MODE 0x10 diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 834636dae5..4e9cccaa34 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -49,12 +49,12 @@ #include "qom/object.h" #include "hw/sysbus.h" /* SysBusDevice */ #include "qemu/host-utils.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "semihosting/semihost.h" #include "hw/mips/cps.h" #include "hw/qdev-clock.h" diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index eef2fd2cd1..26fdb934f5 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_mips.h" static void cpu_mips_irq_request(void *opaque, int irq, int level) diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index 5f4835a38d..a294779a82 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -33,15 +33,15 @@ #include "hw/mips/mips.h" #include "hw/char/serial-mm.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" #include "cpu.h" #define BIOS_SIZE (4 * MiB) diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 69e379fa10..d13a070204 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -11,7 +11,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qemu/bitops.h" #include "hw/sysbus.h" #include "migration/vmstate.h" diff --git a/hw/misc/bcm2835_powermgt.c b/hw/misc/bcm2835_powermgt.c index d88689a0a5..e4e9bae374 100644 --- a/hw/misc/bcm2835_powermgt.c +++ b/hw/misc/bcm2835_powermgt.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "hw/misc/bcm2835_powermgt.h" #include "migration/vmstate.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define PASSWORD 0x5a000000 #define PASSWORD_MASK 0xff000000 diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 09a6f2c6e3..ffba747f8c 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -13,7 +13,7 @@ #include "hw/irq.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspberrypi-fw-defs.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index 639a8cc3e3..8bd4563ae9 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -12,7 +12,7 @@ #include "hw/qdev-properties.h" #include "qemu/module.h" #include "qom/object.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define TYPE_ISA_DEBUG_EXIT_DEVICE "isa-debug-exit" OBJECT_DECLARE_SIMPLE_TYPE(ISADebugExitState, ISA_DEBUG_EXIT_DEVICE) diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index 9d3c2e817d..d44aac3af5 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -28,7 +28,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qom/object.h" #ifndef DEBUG_PMU diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index 070d55339e..c8a096bc13 100644 --- a/hw/misc/imx7_snvs.c +++ b/hw/misc/imx7_snvs.c @@ -19,9 +19,9 @@ #include "hw/misc/imx7_snvs.h" #include "qemu/cutils.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/rtc.h" +#include "system/runstate.h" #include "trace.h" #define RTC_FREQ 32768ULL diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index 23b49d7dff..93db754ce0 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -20,7 +20,7 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" #include "qapi/error.h" #include "hw/sysbus.h" diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 6d735ec29f..e7c1d23570 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -26,7 +26,7 @@ #include "hw/qdev-properties-system.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "migration/blocker.h" #include "migration/vmstate.h" #include "qemu/error-report.h" @@ -34,7 +34,7 @@ #include "qemu/module.h" #include "qom/object_interfaces.h" #include "chardev/char-fe.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qapi/visitor.h" #include "hw/misc/ivshmem.h" diff --git a/hw/misc/lasi.c b/hw/misc/lasi.c index 5dc209cf8d..24d20ffcb8 100644 --- a/hw/misc/lasi.c +++ b/hw/misc/lasi.c @@ -15,8 +15,8 @@ #include "qapi/error.h" #include "trace.h" #include "hw/irq.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "qom/object.h" #include "hw/misc/lasi.h" diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index a376a2b8a0..ed767cde60 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -24,13 +24,13 @@ #include "hw/misc/mac_via.h" #include "hw/misc/mos6522.h" #include "hw/input/adb.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" -#include "sysemu/rtc.h" +#include "system/block-backend.h" +#include "system/rtc.h" #include "trace.h" #include "qemu/log.h" diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index cfc8afd1dc..8130421d5d 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -29,8 +29,8 @@ #include "migration/vmstate.h" #include "hw/misc/macio/cuda.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/log.h" diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 74c2cb3462..de0f934f7d 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -44,7 +44,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" -#include "sysemu/dma.h" +#include "system/dma.h" /* debug DBDMA */ #define DEBUG_DBDMA 0 diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 64bf44f67f..2dd8af1ae6 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -34,8 +34,8 @@ #include "hw/irq.h" #include "hw/misc/macio/pmu.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/log.h" diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index 2098c85ee0..46f907b61c 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -26,7 +26,7 @@ #include "qemu/timer.h" #include "qemu/units.h" #include "trace.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" /* * The reference clock hz, and the SECCNT and CNTR25M registers in this module, diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 7927397a23..6268f5c048 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -23,7 +23,7 @@ #include "hw/qdev-properties.h" #include "qemu/event_notifier.h" #include "qemu/module.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qom/object.h" typedef struct PCITestDevHdr { diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index 824a2e4528..89ccd8b575 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 1c3eafc137..590b9d39ff 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 3b893340c0..c83247c408 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" diff --git a/hw/misc/sbsa_ec.c b/hw/misc/sbsa_ec.c index 86b23a5372..a1e813691e 100644 --- a/hw/misc/sbsa_ec.c +++ b/hw/misc/sbsa_ec.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/sysbus.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" typedef struct SECUREECState { SysBusDevice parent_obj; diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c index c48429b131..b3595d0460 100644 --- a/hw/misc/sifive_e_aon.c +++ b/hw/misc/sifive_e_aon.c @@ -24,7 +24,7 @@ #include "hw/misc/sifive_e_aon.h" #include "qapi/visitor.h" #include "qapi/error.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/qdev-properties.h" REG32(AON_WDT_WDOGCFG, 0x0) diff --git a/hw/misc/sifive_test.c b/hw/misc/sifive_test.c index ad688079c4..b94bb2d29d 100644 --- a/hw/misc/sifive_test.c +++ b/hw/misc/sifive_test.c @@ -23,9 +23,9 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/misc/sifive_test.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size) { diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index 32cd8e8dfb..2ac41f0e78 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -27,8 +27,8 @@ #include "qemu/log.h" #include "qemu/module.h" #include "hw/misc/sifive_u_otp.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/block-backend.h" #define WRITTEN_BIT_ON 0x1 diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c index c7905942fb..dace6d28cc 100644 --- a/hw/misc/slavio_misc.c +++ b/hw/misc/slavio_misc.c @@ -27,7 +27,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c index aa00d6c574..a210a5924c 100644 --- a/hw/misc/virt_ctrl.c +++ b/hw/misc/virt_ctrl.c @@ -10,7 +10,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "trace.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/misc/virt_ctrl.h" enum { diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index 833773ade5..0910c64866 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/nvram/fw_cfg.h" #include "migration/vmstate.h" #include "hw/misc/vmcoreinfo.h" diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index ffa14ecb84..d01ba87e11 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/log.h" diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index 3f03060bf5..8cbcceca46 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -30,7 +30,7 @@ #include "net/checksum.h" #include "qemu/module.h" #include "exec/cpu-common.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/net/allwinner-sun8i-emac.h" /* EMAC register offsets */ diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 3fce01315f..1c9681cabc 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -33,7 +33,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "net/checksum.h" #include "net/eth.h" diff --git a/hw/net/e1000.c b/hw/net/e1000.c index ef0af31751..9fc71fd1c7 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -33,8 +33,8 @@ #include "net/eth.h" #include "net/net.h" #include "net/checksum.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" +#include "system/system.h" +#include "system/dma.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/range.h" diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index e2b7576f67..8c7a2cddfc 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -40,7 +40,7 @@ #include "net/tap.h" #include "qemu/module.h" #include "qemu/range.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/hw.h" #include "hw/net/mii.h" #include "hw/pci/msi.h" diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 2e4c50ddba..2413858790 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -40,7 +40,7 @@ #include "hw/net/mii.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "net_tx_pkt.h" #include "net_rx_pkt.h" diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index b8cb8d5cf1..50c287e3bb 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -50,9 +50,9 @@ #include "net/net.h" #include "net/eth.h" #include "hw/nvram/eeprom93xx.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/dma.h" +#include "system/reset.h" #include "qemu/bitops.h" #include "qemu/module.h" #include "qapi/error.h" diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 4adc7fb10c..d3f84b2205 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/net/ftgmac100.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/net/igb.c b/hw/net/igb.c index ad0f748d82..bea4d446bf 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -44,7 +44,7 @@ #include "net/tap.h" #include "qemu/module.h" #include "qemu/range.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/hw.h" #include "hw/net/mii.h" #include "hw/pci/pci.h" diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c index 5dffa12c64..39e3ce1c8f 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -44,7 +44,7 @@ #include "hw/net/mii.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "net_tx_pkt.h" #include "net_rx_pkt.h" diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 9b64968477..eb405f2d46 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -26,7 +26,7 @@ #include "hw/net/imx_fec.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "net/checksum.h" diff --git a/hw/net/lance.c b/hw/net/lance.c index 9ed9c94cff..a5d72d4fe4 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -43,7 +43,7 @@ #include "hw/net/lance.h" #include "hw/qdev-properties.h" #include "trace.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void parent_lance_reset(void *opaque, int irq, int level) diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index 248e3841db..e5076d55e1 100644 --- a/hw/net/lasi_i82596.c +++ b/hw/net/lasi_i82596.c @@ -14,7 +14,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "hw/sysbus.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "net/eth.h" #include "hw/net/lasi_82596.h" #include "hw/net/i82596.h" diff --git a/hw/net/mv88w8618_eth.c b/hw/net/mv88w8618_eth.c index ccb11512db..b8abffdfa2 100644 --- a/hw/net/mv88w8618_eth.c +++ b/hw/net/mv88w8618_eth.c @@ -12,7 +12,7 @@ #include "hw/irq.h" #include "hw/net/mv88w8618_eth.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "net/net.h" #define MP_ETH_SIZE 0x00001000 diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 1cd070d419..98d5b93a3f 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -27,7 +27,7 @@ #include "hw/net/ne2000-isa.h" #include "migration/vmstate.h" #include "ne2000.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 12fa579d22..b2b0eacd06 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -28,7 +28,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "ne2000.h" -#include "sysemu/sysemu.h" +#include "system/system.h" typedef struct PCINE2000State { PCIDevice dev; diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index f06e908d04..5173364b64 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -42,7 +42,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/units.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" #define CRC_LENGTH 4 diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 1db29307d7..98465024f3 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -33,7 +33,7 @@ #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/units.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" REG32(NPCM_DMA_BUS_MODE, 0x1000) diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 83ba8cd949..1a6f60e4b4 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -35,8 +35,8 @@ #include "net/net.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/dma.h" +#include "system/system.h" #include "trace.h" #include "pcnet.h" diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 064a73b6b4..e6b13054f9 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -54,12 +54,12 @@ #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/module.h" #include "qemu/timer.h" #include "net/net.h" #include "net/eth.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qom/object.h" /* debug RTL8139 card */ diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index d381c041db..5ce29bd9aa 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -33,7 +33,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include diff --git a/hw/net/sungem.c b/hw/net/sungem.c index bcc7a18382..af0ff0a3db 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -17,7 +17,7 @@ #include "net/eth.h" #include "net/checksum.h" #include "hw/net/mii.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 86f472fcbe..9961901bad 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -31,7 +31,7 @@ #include "qemu/module.h" #include "net/checksum.h" #include "net/eth.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/net/tulip.c b/hw/net/tulip.c index f35b58a88c..9adbfa7bb1 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -13,7 +13,7 @@ #include "hw/qdev-properties.h" #include "hw/nvram/eeprom93xx.h" #include "migration/vmstate.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "tulip.h" #include "trace.h" #include "net/eth.h" diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 4fd1f9acca..250fe1c0ff 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -39,15 +39,15 @@ #include "hw/virtio/virtio-access.h" #include "migration/misc.h" #include "standard-headers/linux/ethtool.h" -#include "sysemu/sysemu.h" -#include "sysemu/replay.h" +#include "system/system.h" +#include "system/replay.h" #include "trace.h" #include "monitor/qdev.h" #include "monitor/monitor.h" #include "hw/pci/pci_device.h" #include "net_rx_pkt.h" #include "hw/virtio/vhost.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #define VIRTIO_NET_VM_VERSION 11 diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index f69547cad5..5ede2a38c9 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -21,7 +21,7 @@ #include "hw/qdev-properties.h" #include "net/tap.h" #include "net/checksum.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 33a3062466..fe10c8e076 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -201,12 +201,12 @@ #include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "sysemu/hostmem.h" +#include "system/system.h" +#include "system/block-backend.h" +#include "system/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" -#include "sysemu/spdm-socket.h" +#include "system/spdm-socket.h" #include "migration/vmstate.h" #include "nvme.h" diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c index 2805128498..4e7874f322 100644 --- a/hw/nvme/dif.c +++ b/hw/nvme/dif.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "nvme.h" #include "dif.h" diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 192b80f18d..76d185d948 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -18,8 +18,8 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/bitops.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/block-backend.h" #include "nvme.h" #include "trace.h" diff --git a/hw/nvram/chrp_nvram.c b/hw/nvram/chrp_nvram.c index d4d10a7c03..0b204e36c6 100644 --- a/hw/nvram/chrp_nvram.c +++ b/hw/nvram/chrp_nvram.c @@ -23,7 +23,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "hw/nvram/chrp_nvram.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str, int max_len) diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 669920b2b9..32069881f5 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -15,7 +15,7 @@ #include "hw/nvram/eeprom_at24c.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qom/object.h" /* #define DEBUG_AT24C */ diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 7461d99ff2..a2db79bf6f 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -24,9 +24,9 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/dma.h" +#include "system/reset.h" #include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index d62ad719c8..d18575a3ee 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -29,7 +29,7 @@ #include "hw/nvram/mac_nvram.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "migration/vmstate.h" #include "qemu/cutils.h" #include "qemu/module.h" diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 2251ff2f4c..fa884e9bbb 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -28,10 +28,10 @@ #include "qapi/error.h" #include -#include "sysemu/block-backend.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "hw/nvram/chrp_nvram.h" #include "hw/ppc/spapr.h" diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 4fa528f048..d5a6930e8c 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -29,7 +29,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index 5dae9e8e9a..7cd3d401ce 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -30,7 +30,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 55475aa6d6..83c1fc6705 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -12,9 +12,9 @@ #include "elf.h" #include "hw/loader.h" #include "hw/openrisc/boot.h" -#include "sysemu/device_tree.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/qtest.h" +#include "system/reset.h" #include "qemu/error-report.h" #include diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 87aa353323..6331997d56 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "qemu/timer.h" -#include "sysemu/reset.h" +#include "system/reset.h" #define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */ diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 42f002985b..87f9cbc300 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -29,11 +29,11 @@ #include "hw/openrisc/boot.h" #include "hw/qdev-properties.h" #include "exec/address-spaces.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/system.h" #include "hw/sysbus.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" #include "hw/core/split-irq.h" #include diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 47d2c9bd3c..0d83e33f9e 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -24,10 +24,10 @@ #include "hw/rtc/goldfish_rtc.h" #include "hw/sysbus.h" #include "hw/virtio/virtio-mmio.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/reset.h" #include diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 01997c1ab3..7e8f4ae5ea 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -23,7 +23,7 @@ #include "qemu/range.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" #include "qom/object.h" diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 1516d0074d..4966914892 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -48,7 +48,7 @@ #include "hw/pci-host/bonito.h" #include "hw/pci/pci_host.h" #include "migration/vmstate.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/misc/unimp.h" #include "hw/registerfields.h" #include "qom/object.h" diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index 888f0786a0..350c04e69d 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -17,7 +17,7 @@ #include "hw/ppc/pnv.h" #include "hw/qdev-properties.h" #include "qom/object.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 529b33b5a2..dbd26a68a8 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -20,7 +20,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qom/object.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #define phb3_error(phb, fmt, ...) \ qemu_log_mask(LOG_GUEST_ERROR, "phb3[%d:%d]: " fmt "\n", \ diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index 77d673da54..81986644b1 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -15,7 +15,7 @@ #include "hw/pci/msi.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" +#include "system/reset.h" static uint64_t phb3_msi_ive_addr(PnvPHB3 *phb, int srcno) { diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index f8975403d3..28c35b40c8 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -19,7 +19,7 @@ #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include diff --git a/hw/pci-host/ppc4xx_pci.c b/hw/pci-host/ppc4xx_pci.c index b6c6c8993c..292cb308ba 100644 --- a/hw/pci-host/ppc4xx_pci.c +++ b/hw/pci-host/ppc4xx_pci.c @@ -27,7 +27,7 @@ #include "hw/pci-host/ppc4xx.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "trace.h" diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index 623afed644..d3e6943136 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -37,7 +37,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" /* diff --git a/hw/pci/msi.c b/hw/pci/msi.c index 8104ac1d91..b9f5b45920 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -23,7 +23,7 @@ #include "hw/xen/xen.h" #include "qemu/range.h" #include "qapi/error.h" -#include "sysemu/xen.h" +#include "system/xen.h" #include "hw/i386/kvm/xen_evtchn.h" diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 487e49834e..d8a55a6474 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -19,7 +19,7 @@ #include "hw/pci/msix.h" #include "hw/pci/pci.h" #include "hw/xen/xen.h" -#include "sysemu/xen.h" +#include "system/xen.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "qemu/range.h" diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 90248481b1..c7f898cbf6 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -35,9 +35,9 @@ #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "net/net.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/loader.h" #include "qemu/error-report.h" #include "qemu/range.h" diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 900f93c15e..b02792221c 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -21,8 +21,8 @@ #include "hw/ide/pci.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/ppc/ppc.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" #include "kvm_ppc.h" #define BUS_FREQ_HZ 100000000 diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 46261223f3..4551157c01 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -26,13 +26,13 @@ #include "hw/block/flash.h" #include "hw/char/serial-mm.h" #include "hw/pci/pci.h" -#include "sysemu/block-backend-io.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/block-backend-io.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/reset.h" +#include "system/runstate.h" #include "kvm_ppc.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/ppc/openpic.h" #include "hw/ppc/openpic_kvm.h" #include "hw/ppc/ppc.h" diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 7aa2f2107a..70a8033373 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -13,8 +13,8 @@ #include "qemu/units.h" #include "e500.h" #include "hw/net/fsl_etsec/etsec.h" -#include "sysemu/device_tree.h" -#include "sysemu/kvm.h" +#include "system/device_tree.h" +#include "system/kvm.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" #include "hw/ppc/openpic.h" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 9d249a506c..6369961f78 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -59,7 +59,7 @@ #include "hw/ppc/mac_dbdma.h" #include "hw/pci/pci.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" @@ -68,8 +68,8 @@ #include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "kvm_ppc.h" #include "hw/usb.h" #include "hw/sysbus.h" diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index eef3261002..59653e174b 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -32,7 +32,7 @@ #include "hw/qdev-properties.h" #include "hw/boards.h" #include "hw/input/adb.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "net/net.h" #include "hw/isa/isa.h" #include "hw/pci/pci.h" @@ -45,8 +45,8 @@ #include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "kvm_ppc.h" #define MAX_IDE_BUS 2 diff --git a/hw/ppc/mpc8544_guts.c b/hw/ppc/mpc8544_guts.c index e3c51458e6..a25041e836 100644 --- a/hw/ppc/mpc8544_guts.c +++ b/hw/ppc/mpc8544_guts.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "cpu.h" #include "hw/sysbus.h" #include "qom/object.h" diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index b7130903d6..d74af766ee 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "e500.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/ppc/openpic.h" #include "qemu/error-report.h" #include "qemu/units.h" diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index 47553348b1..cffda44602 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -12,7 +12,7 @@ #include "qapi/error.h" #include "qom/object_interfaces.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "migration/blocker.h" #include "exec/confidential-guest-support.h" diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 16abeaac82..b3c21bdc57 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -20,23 +20,23 @@ #include "hw/ide/pci.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/qtest.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" #include "qom/qom-qobject.h" #include "qapi/qmp/qdict.h" #include "trace.h" #include "qemu/datadir.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/ppc/vof.h" #include diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index b90a052ce0..d2be6502d2 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -22,14 +22,14 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "sysemu/device_tree.h" -#include "sysemu/hw_accel.h" +#include "system/qtest.h" +#include "system/system.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/cpus.h" +#include "system/device_tree.h" +#include "system/hw_accel.h" #include "target/ppc/cpu.h" #include "hw/ppc/fdt.h" #include "hw/ppc/ppc.h" diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index 840ef23128..9f2eb994ca 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "target/ppc/cpu.h" #include "qapi/error.h" #include "qemu/log.h" diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 22864c92f3..3b7cf38f5f 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index b1f83e2cf2..0f0b42bbeb 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "exec/hwaddr.h" #include "exec/memory.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index 4bd61abeed..b56582899f 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/log.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/irq.h" #include "hw/qdev-properties.h" diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index eed6d32650..07e5fcbdb6 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -11,8 +11,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/units.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "hw/loader.h" #include "hw/ppc/pnv_pnor.h" #include "hw/qdev-properties.h" diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index e7d6ceee99..e953e4990e 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -23,7 +23,7 @@ #include "target/ppc/cpu.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index d192bbe2c2..fbfec829d5 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "target/ppc/cpu.h" #include "hw/sysbus.h" diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index b86b5847de..90e3db5cfe 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -27,13 +27,13 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/replay.h" +#include "system/runstate.h" #include "kvm_ppc.h" #include "migration/vmstate.h" #include "trace.h" diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 347428e633..e9f65fab70 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -33,9 +33,9 @@ #include "ppc405.h" #include "hw/rtc/m48t59.h" #include "hw/block/flash.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/block-backend.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/block-backend.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "hw/loader.h" diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 801f97811f..b5cff8d3c5 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -34,8 +34,8 @@ #include "ppc405.h" #include "hw/char/serial-mm.h" #include "qemu/timer.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "exec/address-spaces.h" #include "hw/intc/ppc-uic.h" #include "trace.h" diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index a55f108434..45c5b8678d 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -19,15 +19,15 @@ #include "net/net.h" #include "hw/pci/pci.h" #include "hw/boards.h" -#include "sysemu/kvm.h" -#include "sysemu/device_tree.h" +#include "system/kvm.h" +#include "system/device_tree.h" #include "hw/loader.h" #include "elf.h" #include "hw/char/serial-mm.h" #include "hw/ppc/ppc.h" #include "hw/pci-host/ppc4xx.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/reset.h" #include "hw/sysbus.h" #include "hw/intc/ppc-uic.h" #include "hw/qdev-properties.h" diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 05a5ef6f77..a1b0145c30 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -17,7 +17,7 @@ #include "hw/pci-host/ppc4xx.h" #include "hw/qdev-properties.h" #include "hw/pci/pci.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "cpu.h" #include "ppc440.h" diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index c8849e66ff..925e670ba0 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -26,8 +26,8 @@ #include "cpu.h" #include "hw/ppc/ppc.h" #include "qemu/timer.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "hw/loader.h" #include "kvm_ppc.h" diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index 93b16320d4..baab74c4ed 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -32,7 +32,7 @@ #include "qemu/units.h" #include "hw/hw.h" #include "hw/sysbus.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "hw/ppc/ppc.h" #include "e500.h" #include "qom/object.h" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index fb58c312ac..3e68d8e6e2 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -39,8 +39,8 @@ #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "trace.h" #include "elf.h" #include "qemu/units.h" diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index ca475c69f4..b5380eaee4 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -32,7 +32,7 @@ #include "qom/object.h" #include "qemu/error-report.h" /* for error_report() */ #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "cpu.h" #include "trace.h" diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 78e2a46e75..e74642a3b7 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -17,10 +17,10 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/boards.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" -#include "sysemu/device_tree.h" -#include "sysemu/block-backend.h" +#include "system/device_tree.h" +#include "system/block-backend.h" #include "exec/page-protection.h" #include "hw/loader.h" #include "elf.h" @@ -28,8 +28,8 @@ #include "ppc440.h" #include "hw/pci-host/ppc4xx.h" #include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/reset.h" #include "hw/sysbus.h" #include "hw/char/serial-mm.h" #include "hw/i2c/ppc4xx_i2c.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3b022e8da9..ad21018b5a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -32,20 +32,20 @@ #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" #include "qapi/visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/hostmem.h" +#include "system/numa.h" +#include "system/tcg.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/log.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "net/net.h" -#include "sysemu/device_tree.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" +#include "system/device_tree.h" +#include "system/cpus.h" +#include "system/hw_accel.h" #include "kvm_ppc.h" #include "migration/misc.h" #include "migration/qemu-file-types.h" diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 2f74923560..7edd138360 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -26,14 +26,14 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "exec/ram_addr.h" #include "target/ppc/cpu.h" #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" #include "kvm_ppc.h" #include "migration/vmstate.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "hw/ppc/spapr.h" diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 88d743a3c3..d725dd05ca 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -15,15 +15,15 @@ #include "target/ppc/cpu.h" #include "hw/ppc/spapr.h" #include "qapi/error.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" +#include "system/cpus.h" +#include "system/kvm.h" #include "target/ppc/kvm_ppc.h" #include "hw/ppc/ppc.h" #include "target/ppc/mmu-hash64.h" #include "target/ppc/power8-pmu.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/hw_accel.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/hw_accel.h" #include "qemu/error-report.h" static void spapr_reset_vcpu(PowerPCCPU *cpu) diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 7868048bb9..2435397e94 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -23,8 +23,8 @@ #include "hw/ppc/spapr.h" /* for RTAS return codes */ #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */ #include "hw/ppc/spapr_nvdimm.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/reset.h" #include "trace.h" #define DRC_CONTAINER_PATH "dr-connector" diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 4dbf8e2e2e..832b0212f3 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -27,8 +27,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/device_tree.h" -#include "sysemu/runstate.h" +#include "system/device_tree.h" +#include "system/runstate.h" #include "hw/ppc/fdt.h" #include "hw/ppc/spapr.h" diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 5e1d020e3d..f8ab767063 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1,9 +1,9 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "sysemu/tcg.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/tcg.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 7836dc71fc..db3a14c1df 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -21,10 +21,10 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" #include "hw/ppc/spapr.h" diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index aebd7eaabb..d6d368dd08 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -19,7 +19,7 @@ #include "hw/ppc/xics_spapr.h" #include "hw/qdev-properties.h" #include "cpu-models.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "trace.h" diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 3edff528ca..42eef8c501 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -45,10 +45,10 @@ #include "hw/pci/pci_ids.h" #include "hw/ppc/spapr_drc.h" #include "hw/qdev-properties.h" -#include "sysemu/device_tree.h" -#include "sysemu/kvm.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/device_tree.h" +#include "system/kvm.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "hw/ppc/spapr_numa.h" #include "qemu/log.h" diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 51c3a54d45..8cfd61b7db 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -22,8 +22,8 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "sysemu/device_tree.h" -#include "sysemu/rng.h" +#include "system/device_tree.h" +#include "system/rng.h" #include "hw/ppc/spapr.h" #include "hw/qdev-properties.h" #include "kvm_ppc.h" diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index f329693c55..df2e837632 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -28,12 +28,12 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "sysemu/device_tree.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/device_tree.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/qtest.h" #include "kvm_ppc.h" #include "hw/ppc/spapr.h" diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index deb3ea4e49..46fbc78900 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -27,8 +27,8 @@ #include "qemu/osdep.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "hw/ppc/spapr.h" #include "migration/vmstate.h" #include "qapi/error.h" diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index 37521b88cb..dc0d09eca3 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/ppc/spapr.h" #include "hw/qdev-properties.h" #include "trace.h" diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 6a5a7f57c7..09243c183b 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -27,8 +27,8 @@ #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "sysemu/device_tree.h" +#include "system/kvm.h" +#include "system/device_tree.h" #include "kvm_ppc.h" #include "migration/vmstate.h" diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index c02eaacfed..46d78756e6 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -10,7 +10,7 @@ #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/fdt.h" #include "hw/ppc/vof.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qom/qom-qobject.h" #include "trace.h" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index f378e5c4a9..ea7ab8a569 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -30,10 +30,10 @@ #include "hw/sysbus.h" #include "hw/char/serial-mm.h" #include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/reset.h" #include "hw/boards.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/loader.h" #include "elf.h" #include "qapi/error.h" diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index b5b6514d79..09cb77de93 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -18,7 +18,7 @@ #include "exec/address-spaces.h" #include "hw/ppc/vof.h" #include "hw/ppc/fdt.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qom/qom-qobject.h" #include "trace.h" diff --git a/hw/remote/message.c b/hw/remote/message.c index 38ae6c75b4..273f1e0323 100644 --- a/hw/remote/message.c +++ b/hw/remote/message.c @@ -13,12 +13,12 @@ #include "io/channel.h" #include "hw/remote/mpqemu-link.h" #include "qapi/error.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "hw/remote/memory.h" #include "hw/remote/iohub.h" -#include "sysemu/reset.h" +#include "system/reset.h" static void process_config_write(QIOChannel *ioc, PCIDevice *dev, MPQemuMsg *msg, Error **errp); diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c index 4394dc4d82..e25f97680d 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -17,7 +17,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "io/channel.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "trace.h" /* diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 6f84fdd3fa..3c08291c61 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -21,7 +21,7 @@ #include "hw/remote/proxy-memory-listener.h" #include "qom/object.h" #include "qemu/event_notifier.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static void probe_pci_info(PCIDevice *dev, Error **errp); static void proxy_device_reset(DeviceState *dev); diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index dc27cc8da1..2f25f92dcd 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -17,7 +17,7 @@ #include "hw/remote/machine.h" #include "io/channel-util.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/pci/pci.h" #include "qemu/sockets.h" #include "monitor/monitor.h" diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 8dbafafb9e..9e5ff6d87a 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -43,7 +43,7 @@ #include "qom/object_interfaces.h" #include "qemu/error-report.h" #include "trace.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/boards.h" #include "hw/remote/machine.h" #include "qapi/error.h" @@ -52,7 +52,7 @@ #include "qemu/notify.h" #include "qemu/thread.h" #include "qemu/main-loop.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "libvfio-user.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 2e319168db..7731b88f8a 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -27,10 +27,10 @@ #include "hw/riscv/boot.h" #include "hw/riscv/boot_opensbi.h" #include "elf.h" -#include "sysemu/device_tree.h" -#include "sysemu/qtest.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/qtest.h" +#include "system/kvm.h" +#include "system/reset.h" #include diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index f9a3b43d2e..1208d684a4 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -51,8 +51,8 @@ #include "hw/riscv/microchip_pfsoc.h" #include "hw/intc/riscv_aclint.h" #include "hw/intc/sifive_plic.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/system.h" /* * The BIOS image used by this machine is called Hart Software Services (HSS). diff --git a/hw/riscv/numa.c b/hw/riscv/numa.c index cf686f4ff1..7a7b012007 100644 --- a/hw/riscv/numa.c +++ b/hw/riscv/numa.c @@ -23,7 +23,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/riscv/numa.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" static bool numa_enabled(const MachineState *ms) { diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 8ce85ea9f7..e4f643cb52 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -27,7 +27,7 @@ #include "hw/misc/unimp.h" #include "hw/riscv/boot.h" #include "qemu/units.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* * This version of the OpenTitan machine currently supports diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 0df454772f..e73479a943 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" #include "hw/qdev-properties.h" diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 2dccc1eff2..e2242b97d0 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -23,7 +23,7 @@ #include "qemu/error-report.h" #include "hw/intc/sifive_plic.h" #include "hw/intc/riscv_aclint.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "exec/address-spaces.h" #include "hw/riscv/boot.h" diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 5a1959f2a9..407bf51ff1 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -46,7 +46,7 @@ #include "hw/misc/sifive_e_prci.h" #include "hw/misc/sifive_e_aon.h" #include "chardev/char.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static const MemMapEntry sifive_e_memmap[] = { [SIFIVE_E_DEV_DEBUG] = { 0x0, 0x1000 }, diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 124ffd4842..506779db7c 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -56,9 +56,9 @@ #include "hw/intc/sifive_plic.h" #include "chardev/char.h" #include "net/eth.h" -#include "sysemu/device_tree.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/runstate.h" +#include "system/system.h" #include diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index fceb91d946..586168b6ef 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -36,8 +36,8 @@ #include "hw/char/riscv_htif.h" #include "hw/intc/riscv_aclint.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/system.h" #include diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 36d6a3a412..825bd321ea 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -38,7 +38,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" +#include "system/reset.h" #define ACPI_BUILD_TABLE_SIZE 0x20000 #define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index)) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2feb851f15..80eda67f39 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -44,12 +44,12 @@ #include "hw/misc/sifive_test.h" #include "hw/platform-bus.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/tcg.h" -#include "sysemu/kvm.h" -#include "sysemu/tpm.h" -#include "sysemu/qtest.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/tcg.h" +#include "system/kvm.h" +#include "system/tpm.h" +#include "system/qtest.h" #include "hw/pci/pci.h" #include "hw/pci-host/gpex.h" #include "hw/display/ramfb.h" diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 838db72136..be591f77ab 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -25,7 +25,7 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "hw/rtc/allwinner-rtc.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "trace.h" /* RTC registers */ diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c index 3cddf43eea..fbdeb0781f 100644 --- a/hw/rtc/aspeed_rtc.c +++ b/hw/rtc/aspeed_rtc.c @@ -11,7 +11,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/timer.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "trace.h" diff --git a/hw/rtc/ds1338.c b/hw/rtc/ds1338.c index c993182ae4..8dd17fdc07 100644 --- a/hw/rtc/ds1338.c +++ b/hw/rtc/ds1338.c @@ -15,7 +15,7 @@ #include "migration/vmstate.h" #include "qemu/bcd.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "trace.h" /* Size of NVRAM including both the user-accessible area and the diff --git a/hw/rtc/exynos4210_rtc.c b/hw/rtc/exynos4210_rtc.c index ca28a45672..aa1b3cd115 100644 --- a/hw/rtc/exynos4210_rtc.c +++ b/hw/rtc/exynos4210_rtc.c @@ -38,7 +38,7 @@ #include "hw/arm/exynos4210.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #define DEBUG_RTC 0 diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index 389f192efa..425c8ba0c2 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -27,8 +27,8 @@ #include "hw/sysbus.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index c9c3cd84da..fce23a3dbe 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -10,12 +10,12 @@ #include "hw/irq.h" #include "hw/register.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "migration/vmstate.h" #include "hw/misc/unimp.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "hw/registerfields.h" #define SYS_TOYTRIM 0x20 diff --git a/hw/rtc/m41t80.c b/hw/rtc/m41t80.c index e045c864bb..9600695679 100644 --- a/hw/rtc/m41t80.c +++ b/hw/rtc/m41t80.c @@ -14,7 +14,7 @@ #include "qemu/bcd.h" #include "hw/i2c/i2c.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/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 5a2c7b4abd..4c578e7c54 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -28,15 +28,15 @@ #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 "system/runstate.h" +#include "system/rtc.h" +#include "system/system.h" #include "hw/sysbus.h" #include "qapi/error.h" #include "qemu/bcd.h" #include "qemu/module.h" #include "trace.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "m48t59-internal.h" #include "migration/vmstate.h" diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 973ed9914d..2103f774e9 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -32,11 +32,11 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/replay.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/replay.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/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 1dc8e6e00f..a037bdbf42 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -18,8 +18,8 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/rtc/xlnx-zynqmp-rtc.c b/hw/rtc/xlnx-zynqmp-rtc.c index f37df09cfb..b596b608c5 100644 --- a/hw/rtc/xlnx-zynqmp-rtc.c +++ b/hw/rtc/xlnx-zynqmp-rtc.c @@ -32,8 +32,8 @@ #include "qemu/module.h" #include "hw/irq.h" #include "qemu/cutils.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "trace.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "migration/vmstate.h" diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index bb4746c556..02fdbdf824 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -24,9 +24,9 @@ #include "qapi/error.h" #include "hw/loader.h" #include "hw/rx/rx62n.h" -#include "sysemu/qtest.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/device_tree.h" +#include "system/reset.h" #include "hw/boards.h" #include "qom/object.h" diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index dfa27bc94e..f421de4e6f 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -28,7 +28,7 @@ #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/qmp/qlist.h" #include "qom/object.h" diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 88a97f0085..f0275bf34c 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -15,9 +15,9 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qapi/error.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/tcg.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/tcg.h" #include "elf.h" #include "hw/loader.h" #include "hw/qdev-properties.h" diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 3c09750550..909475f048 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -18,7 +18,7 @@ #include "hw/s390x/css.h" #include "hw/s390x/css-bridge.h" #include "hw/s390x/s390-ccw.h" -#include "sysemu/sysemu.h" +#include "system/system.h" IOInstEnding s390_ccw_cmd_request(SubchDev *sch) { diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 22e6be67af..1a507ea472 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -24,8 +24,8 @@ #include "hw/pci/msi.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "trace.h" diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 41655082da..e386d75d58 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -15,7 +15,7 @@ #include "exec/memop.h" #include "exec/memory.h" #include "qemu/error-report.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "hw/pci/pci_device.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" diff --git a/hw/s390x/s390-skeys-kvm.c b/hw/s390x/s390-skeys-kvm.c index 3ff9d94b80..0215e94388 100644 --- a/hw/s390x/s390-skeys-kvm.c +++ b/hw/s390x/s390-skeys-kvm.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/s390x/storage-keys.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/error-report.h" #include "qemu/module.h" diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 6d0a47ed73..c6a4db5f7e 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -18,9 +18,9 @@ #include "qapi/qapi-commands-misc-target.h" #include "qapi/qmp/qdict.h" #include "qemu/error-report.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" #include "exec/address-spaces.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "migration/qemu-file-types.h" #include "migration/register.h" #include "trace.h" diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index eeaa811098..508fd558e8 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -14,7 +14,7 @@ #include "migration/qemu-file.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "exec/ram_addr.h" #include "kvm/kvm_s390x.h" #include "qapi/error.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 67ae34aead..f4d64d64f9 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -29,7 +29,7 @@ #include "qemu/qemu-print.h" #include "qemu/units.h" #include "hw/s390x/s390-pci-bus.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/s390x/storage-keys.h" #include "hw/s390x/storage-attributes.h" #include "hw/s390x/event-facility.h" @@ -42,8 +42,8 @@ #include "hw/nmi.h" #include "hw/qdev-properties.h" #include "hw/s390x/tod.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" +#include "system/system.h" +#include "system/cpus.h" #include "target/s390x/kvm/pv.h" #include "migration/blocker.h" #include "qapi/visitor.h" diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c index fa79891f5a..a178a9dd4c 100644 --- a/hw/s390x/sclpcpu.c +++ b/hw/s390x/sclpcpu.c @@ -17,7 +17,7 @@ #include "hw/s390x/sclp.h" #include "qemu/module.h" #include "hw/s390x/event-facility.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" typedef struct ConfigMgtData { EventBufferHeader ebh; diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index a32d6a91f5..7bb5aad520 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -16,7 +16,7 @@ #include "hw/s390x/sclp.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/s390x/event-facility.h" typedef struct SignalQuiesce { diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index 9588b90f2b..5da9037e0c 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/s390x/tod.h" #include "target/s390x/kvm/pv.h" #include "kvm/kvm_s390x.h" diff --git a/hw/s390x/tod-tcg.c b/hw/s390x/tod-tcg.c index 2d540dba65..3b3ef8843e 100644 --- a/hw/s390x/tod-tcg.c +++ b/hw/s390x/tod-tcg.c @@ -16,7 +16,7 @@ #include "qemu/module.h" #include "cpu.h" #include "tcg/tcg_s390x.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index c81b1c0338..6afbb23fc7 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -13,9 +13,9 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/qtest.h" #include "migration/qemu-file-types.h" #include "migration/register.h" diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 96747318d2..7cbce4766a 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "net/net.h" #include "hw/virtio/virtio.h" #include "migration/qemu-file-types.h" @@ -32,7 +32,7 @@ #include "trace.h" #include "hw/s390x/css-bridge.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "sysemu/replay.h" +#include "system/replay.h" #define NR_CLASSIC_INDICATOR_BITS 64 diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 1f728416e2..d85e384ad6 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -19,7 +19,7 @@ #include "hw/pci/pci_device.h" #include "hw/scsi/scsi.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 8323cd18e3..70d444e093 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -21,9 +21,9 @@ #include "qemu/osdep.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 "system/dma.h" +#include "system/block-backend.h" +#include "system/rtc.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "qemu/iov.h" diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c index 0d5abb4b6c..6cba92f376 100644 --- a/hw/scsi/mptendian.c +++ b/hw/scsi/mptendian.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index a06113d908..8397c93bf2 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "qemu/main-loop.h" diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 2f1678d51e..5bfdcfca45 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -9,12 +9,12 @@ #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "scsi/constants.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" +#include "system/runstate.h" #include "trace.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/cutils.h" static char *scsibus_get_dev_path(DeviceState *dev); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a47b80907f..33c169b013 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -32,14 +32,14 @@ #include "migration/vmstate.h" #include "hw/scsi/emulation.h" #include "scsi/constants.h" -#include "sysemu/arch_init.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/arch_init.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "hw/block/block.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/dma.h" +#include "system/system.h" #include "qemu/cutils.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index d7ae7549d0..5748060a9a 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -21,7 +21,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/scsi/emulation.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "trace.h" #ifdef __linux__ diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index effb8dab1f..4bd6d2d7a8 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -29,7 +29,7 @@ #include "hw/fw-path-provider.h" #include "hw/qdev-properties.h" #include "qemu/cutils.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Features supported by host kernel. */ static const int kernel_feature_bits[] = { diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index d5265c57bc..8b84f9508e 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -27,7 +27,7 @@ #include "hw/virtio/vhost-user-scsi.h" #include "hw/virtio/virtio.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Features supported by the host application */ static const int user_feature_bits[] = { diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 2806a121b2..f49ab98ecc 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -15,7 +15,7 @@ #include "qapi/error.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" #include "hw/virtio/virtio-bus.h" diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index c0a4f1a620..e4d01e4adb 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -22,8 +22,8 @@ #include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/module.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/dma.h" #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index be39ec2e71..f46213dfc7 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -22,8 +22,8 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" +#include "system/blockdev.h" +#include "system/dma.h" #include "hw/qdev-properties.h" #include "hw/irq.h" #include "hw/sd/allwinner-sdhost.h" diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 4e411ff798..0724949d0c 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/irq.h" #include "hw/sd/bcm2835_sdhost.h" #include "migration/vmstate.h" diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 51b10cadca..03d2ae7d21 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/irq.h" diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b994ef581e..5b7b07194a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -37,7 +37,7 @@ #include "qemu/cutils.h" #include "hw/irq.h" #include "hw/registerfields.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/sd/sd.h" #include "hw/sd/sdcard_legacy.h" #include "migration/vmstate.h" diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index e697ee05b3..11a298f4b8 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -30,7 +30,7 @@ #include "qapi/error.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/timer.h" #include "qemu/bitops.h" #include "hw/sd/sdhci.h" diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 15940515ab..c4a58da0ab 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -16,7 +16,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/ssi/ssi.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index e6cc156c23..2fa439819e 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -30,9 +30,9 @@ #include "cpu.h" #include "hw/sysbus.h" #include "hw/sh4/sh.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 8041b3b651..8892eaddcb 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -28,7 +28,7 @@ #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/sh4/sh.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "sh7750_regs.h" diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index a394514264..02a09eb9cd 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -21,7 +21,7 @@ #include "qemu/config-file.h" #include "qemu/module.h" #include "qemu/option.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/uuid.h" #include "hw/firmware/smbios.h" #include "hw/loader.h" diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c index c37a8ee821..14319d4897 100644 --- a/hw/smbios/smbios_legacy.c +++ b/hw/smbios/smbios_legacy.c @@ -18,7 +18,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" #include "hw/firmware/smbios.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" struct smbios_header { diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 6aaa04cb19..84381254ad 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -34,9 +34,9 @@ #include "qemu/timer.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index d52e6a7213..470b6877cd 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -35,9 +35,9 @@ #include "migration/vmstate.h" #include "hw/sparc/sparc32_dma.h" #include "hw/block/fdc.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" #include "net/net.h" #include "hw/boards.h" #include "hw/scsi/esp.h" diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 67ec403e1d..805ba6b1e3 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -32,10 +32,10 @@ #include "hw/loader.h" #include "hw/sparc/sparc64.h" #include "hw/rtc/sun4v-rtc.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" +#include "system/qtest.h" +#include "system/system.h" #include "qapi/error.h" typedef struct NiagaraBoardState { diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c index 3091cde586..9cffc92aa3 100644 --- a/hw/sparc64/sparc64.c +++ b/hw/sparc64/sparc64.c @@ -29,7 +29,7 @@ #include "hw/boards.h" #include "hw/sparc/sparc64.h" #include "qemu/timer.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "trace.h" diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 7088ac273e..8546b30266 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -43,8 +43,8 @@ #include "hw/block/fdc.h" #include "net/net.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/nvram/sun_nvram.h" #include "hw/nvram/chrp_nvram.h" diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index f72cb3cbc8..0acac4ef70 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -33,7 +33,7 @@ #include "hw/ssi/xilinx_spips.h" #include "qapi/error.h" #include "hw/register.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "migration/blocker.h" #include "migration/vmstate.h" diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index c0a91bab0c..0c226d6ea6 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -32,7 +32,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "hw/core/cpu.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #ifndef A9_GTIMER_ERR_DEBUG #define A9_GTIMER_ERR_DEBUG 0 diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 345145bfa8..6328ad6ab7 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -11,13 +11,13 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #define OSMR0 0x00 #define OSMR1 0x04 diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 2bf6e7ffe9..a9f7aba303 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -23,10 +23,10 @@ #include "hw/pci/pci_ids.h" #include "hw/acpi/tpm.h" #include "migration/vmstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" -#include "sysemu/reset.h" -#include "sysemu/xen.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" +#include "system/reset.h" +#include "system/xen.h" #include "tpm_prop.h" #include "tpm_ppi.h" #include "trace.h" diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index f27ed6c35e..984d3d1080 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/memalign.h" #include "qapi/error.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" #include "migration/vmstate.h" #include "hw/qdev-core.h" #include "hw/acpi/tpm.h" diff --git a/hw/tpm/tpm_prop.h b/hw/tpm/tpm_prop.h index bbd4225d66..c4df74805a 100644 --- a/hw/tpm/tpm_prop.h +++ b/hw/tpm/tpm_prop.h @@ -22,7 +22,7 @@ #ifndef HW_TPM_PROP_H #define HW_TPM_PROP_H -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "hw/qdev-properties.h" extern const PropertyInfo qdev_prop_tpm; diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index e15b67dd45..645079f2e5 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -19,8 +19,8 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_prop.h" #include "hw/ppc/spapr.h" diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h index 6f14896b97..184632ff66 100644 --- a/hw/tpm/tpm_tis.h +++ b/hw/tpm/tpm_tis.h @@ -24,7 +24,7 @@ #ifndef TPM_TPM_TIS_H #define TPM_TPM_TIS_H -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "tpm_ppi.h" #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c index 1bfa28bfd9..cdd0df1137 100644 --- a/hw/tpm/tpm_tis_common.c +++ b/hw/tpm/tpm_tis_common.c @@ -34,8 +34,8 @@ #include "hw/pci/pci_ids.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_ppi.h" #include "trace.h" diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 74ff52ad09..34e4bd8936 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -14,7 +14,7 @@ #include "qemu/memalign.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/cutils.h" #include "trace.h" #include "ufs.h" diff --git a/hw/usb/bus-stub.c b/hw/usb/bus-stub.c index fcabe8429e..cd0c317b71 100644 --- a/hw/usb/bus-stub.c +++ b/hw/usb/bus-stub.c @@ -10,7 +10,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "monitor/monitor.h" #include "hw/usb.h" diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 80e6a92820..8277efb98d 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -6,7 +6,7 @@ #include "qapi/type-helpers.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "migration/vmstate.h" #include "monitor/monitor.h" #include "trace.h" diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 6c4f5776d4..39e8905f0e 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -33,7 +33,7 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qemu/config-file.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/cutils.h" diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index ca037ba96f..9e54006c60 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -13,8 +13,8 @@ #include "hw/usb.h" #include "hw/usb/desc.h" #include "hw/usb/msd.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/block-backend.h" static const struct SCSIBusInfo usb_msd_scsi_info_storage = { .tcq = false, diff --git a/hw/usb/hcd-dwc2.h b/hw/usb/hcd-dwc2.h index 9c3d88ea14..2d5a5690fc 100644 --- a/hw/usb/hcd-dwc2.h +++ b/hw/usb/hcd-dwc2.h @@ -23,7 +23,7 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/usb.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define DWC2_MMIO_SIZE 0x11000 diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 01864d4649..6c4c14c895 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -35,7 +35,7 @@ #include "trace.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define FRAME_TIMER_FREQ 1000 #define FRAME_TIMER_NS (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 56a1c09d1f..ffd6c5108e 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -20,7 +20,7 @@ #include "qemu/timer.h" #include "hw/usb.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/pci/pci_device.h" #include "hw/sysbus.h" diff --git a/hw/usb/hcd-ohci.h b/hw/usb/hcd-ohci.h index e1827227ac..3cc35a5cdc 100644 --- a/hw/usb/hcd-ohci.h +++ b/hw/usb/hcd-ohci.h @@ -22,7 +22,7 @@ #define HCD_OHCI_H #include "hw/sysbus.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/usb.h" #include "qom/object.h" diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 245352c231..b6e6316a65 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -36,7 +36,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/iov.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index fe16d7ad05..9609b83514 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -25,7 +25,7 @@ #include "hw/usb.h" #include "hw/usb/xhci.h" -#include "sysemu/dma.h" +#include "system/dma.h" OBJECT_DECLARE_SIMPLE_TYPE(XHCIState, XHCI) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 85d33b51ba..7d716ef0bc 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -51,8 +51,8 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "trace.h" #include "hw/qdev-properties.h" diff --git a/hw/usb/libhw.c b/hw/usb/libhw.c index f350eae443..4f03ef4ba9 100644 --- a/hw/usb/libhw.c +++ b/hw/usb/libhw.c @@ -21,7 +21,7 @@ */ #include "qemu/osdep.h" #include "hw/usb.h" -#include "sysemu/dma.h" +#include "system/dma.h" int usb_packet_map(USBPacket *p, QEMUSGList *sgl) { diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index f72a612d5a..39000740e6 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -30,8 +30,8 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/iov.h" diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 2e6ea2dd93..6dff88e630 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -16,7 +16,7 @@ #include #include "qapi/error.h" #include "hw/vfio/vfio-common.h" -#include "sysemu/iommufd.h" +#include "system/iommufd.h" #include "hw/s390x/ap-device.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index b96ab27e12..87b58d8f66 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "hw/vfio/vfio-common.h" -#include "sysemu/iommufd.h" +#include "system/iommufd.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/vfio-ccw.h" #include "hw/qdev-properties.h" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index dcef44fe55..598272f4dd 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -34,15 +34,15 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/range.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/reset.h" +#include "system/runstate.h" #include "trace.h" #include "qapi/error.h" #include "migration/misc.h" #include "migration/blocker.h" #include "migration/qemu-file.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" VFIODeviceList vfio_device_list = QLIST_HEAD_INITIALIZER(vfio_device_list); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 9ccdb639ac..78a3c2d55f 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -28,7 +28,7 @@ #include "exec/ram_addr.h" #include "qemu/error-report.h" #include "qemu/range.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "trace.h" #include "qapi/error.h" #include "pci.h" diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 87e51fcee1..3d1c8d290a 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -9,7 +9,7 @@ #include "hw/vfio/vfio-common.h" #include "migration/misc.h" #include "qapi/error.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, Error **errp) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index e7bece4ea1..3490a8f1eb 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -19,9 +19,9 @@ #include "qemu/error-report.h" #include "trace.h" #include "qapi/error.h" -#include "sysemu/iommufd.h" +#include "system/iommufd.h" #include "hw/qdev-core.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qemu/cutils.h" #include "qemu/chardev_open.h" #include "pci.h" diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 01aa11013e..adfa752db5 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -15,7 +15,7 @@ #include #include -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/vfio/vfio-common.h" #include "migration/misc.h" #include "migration/savevm.h" diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 93aca850e3..bd8565c50e 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -36,14 +36,14 @@ #include "qemu/module.h" #include "qemu/range.h" #include "qemu/units.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "pci.h" #include "trace.h" #include "qapi/error.h" #include "migration/blocker.h" #include "migration/qemu-file.h" -#include "sysemu/iommufd.h" +#include "system/iommufd.h" #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 5ad090a229..43c166680a 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -19,7 +19,7 @@ #include "qemu/queue.h" #include "qemu/timer.h" #include "qom/object.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #define PCI_ANY_ID (~0) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 766e8a86ef..2e17fb96b2 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -21,7 +21,7 @@ #include #include "hw/vfio/vfio-platform.h" -#include "sysemu/iommufd.h" +#include "system/iommufd.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/lockable.h" @@ -36,7 +36,7 @@ #include "hw/irq.h" #include "hw/platform-bus.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" /* * Functions used whatever the injection method diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 018bd20481..ad4c499eaf 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -14,7 +14,7 @@ #ifdef CONFIG_KVM #include #endif -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "exec/address-spaces.h" #include "hw/vfio/vfio-common.h" diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 61849b3b0e..78f8feed2b 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -26,8 +26,8 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/vdpa-dev.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" static void vhost_vdpa_device_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index c0462329a5..83201ad232 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -23,7 +23,7 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user-fs.h" #include "monitor/monitor.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static const int user_feature_bits[] = { VIRTIO_F_VERSION_1, diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index b2f6451f48..510981b39b 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -29,7 +29,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/virtio/virtio-pci.h" #include "qom/object.h" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index f170f0b25b..267b612587 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -19,13 +19,13 @@ #include "hw/virtio/virtio-net.h" #include "chardev/char-fe.h" #include "io/channel-socket.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/uuid.h" #include "qemu/sockets.h" -#include "sysemu/runstate.h" -#include "sysemu/cryptodev.h" +#include "system/runstate.h" +#include "system/cryptodev.h" #include "migration/postcopy-ram.h" #include "trace.h" #include "exec/ramblock.h" diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index c40f48ac4d..6aa72fd434 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -26,7 +26,7 @@ #include "hw/mem/memory-device.h" #include "migration/blocker.h" #include "migration/qemu-file-types.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" /* enabled until disconnected backend stabilizes */ diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index ab2ee30475..e2ce18c258 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -22,7 +22,7 @@ #include "hw/mem/pc-dimm.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "sysemu/balloon.h" +#include "system/balloon.h" #include "hw/virtio/virtio-balloon.h" #include "exec/address-spaces.h" #include "qapi/error.h" diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 9ae0b02598..74669145d2 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -22,7 +22,7 @@ #include "hw/virtio/virtio-crypto.h" #include "hw/qdev-properties.h" #include "standard-headers/linux/virtio_ids.h" -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #define VIRTIO_CRYPTO_VM_VERSION 1 diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 576ad8383f..3d645a5339 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -25,9 +25,9 @@ #include "exec/target_page.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/kvm.h" +#include "system/reset.h" +#include "system/system.h" #include "qemu/reserved-region.h" #include "qemu/units.h" #include "qapi/error.h" diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 3f6f46fad7..80362a39f3 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -15,10 +15,10 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/units.h" -#include "sysemu/numa.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/numa.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/runstate.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-mem.h" diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 49d9fe8f30..0bcb9a6faf 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -28,8 +28,8 @@ #include "migration/qemu-file-types.h" #include "qemu/host-utils.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/replay.h" +#include "system/kvm.h" +#include "system/replay.h" #include "hw/virtio/virtio-mmio.h" #include "qemu/error-report.h" #include "qemu/log.h" diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index de41cb5ef2..ce2927e99b 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -33,12 +33,12 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/virtio/virtio-pci.h" #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" #include "qapi/visitor.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "trace.h" #define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev)) diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index f6f3b5ddaf..4583e3ffe3 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -21,7 +21,7 @@ #include "hw/virtio/virtio-access.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pmem.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "block/aio.h" #include "block/thread-pool.h" #include "trace.h" diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 13a1a0b236..a551c4c3a3 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -17,8 +17,8 @@ #include "hw/virtio/virtio.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-rng.h" -#include "sysemu/rng.h" -#include "sysemu/runstate.h" +#include "system/rng.h" +#include "system/runstate.h" #include "qom/object_interfaces.h" #include "trace.h" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7fcdb55ba4..f290c8ce7b 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -30,8 +30,8 @@ #include "hw/virtio/virtio-bus.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-access.h" -#include "sysemu/dma.h" -#include "sysemu/runstate.h" +#include "system/dma.h" +#include "system/runstate.h" #include "virtio-qmp.h" #include "standard-headers/linux/virtio_ids.h" diff --git a/hw/watchdog/allwinner-wdt.c b/hw/watchdog/allwinner-wdt.c index d35711c7c5..1bfec41ff8 100644 --- a/hw/watchdog/allwinner-wdt.c +++ b/hw/watchdog/allwinner-wdt.c @@ -28,7 +28,7 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/watchdog/allwinner-wdt.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "migration/vmstate.h" /* WDT registers */ diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index ed5ff4257c..a52121dc44 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -25,7 +25,7 @@ #include "trace.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 2e25d4b4e9..dd7e2a4708 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -16,8 +16,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" -#include "sysemu/watchdog.h" +#include "system/reset.h" +#include "system/watchdog.h" #include "hw/qdev-properties.h" #include "hw/watchdog/sbsa_gwdt.h" #include "qemu/timer.h" diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index d0ce3c4ac5..0721373948 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -26,8 +26,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-events-run-state.h" -#include "sysemu/runstate.h" -#include "sysemu/watchdog.h" +#include "system/runstate.h" +#include "system/watchdog.h" #include "hw/nmi.h" #include "qemu/help_option.h" #include "trace.h" diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index c95877e5c7..9b86772cc1 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -13,7 +13,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "hw/watchdog/wdt_aspeed.h" diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c index 040d20fde7..39f2894f21 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -12,8 +12,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" -#include "sysemu/watchdog.h" +#include "system/reset.h" +#include "system/watchdog.h" #include "qemu/timer.h" #include "hw/watchdog/wdt_diag288.h" #include "migration/vmstate.h" diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 9427abfb49..aa1d0866c8 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -23,7 +23,7 @@ #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/pci/pci_device.h" #include "migration/vmstate.h" #include "qom/object.h" diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index 17c82897bf..23519e058e 100644 --- a/hw/watchdog/wdt_ib700.c +++ b/hw/watchdog/wdt_ib700.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "qom/object.h" diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index 61fbd91ee4..3a4c15d1a4 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/module.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 0d7defb8cd..7919f8ef79 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -19,7 +19,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "net/net.h" #include "trace.h" diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 18ba7b1d8f..00bfbcc6fb 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -18,8 +18,8 @@ #include "hw/xen/xen_native.h" #include "qemu/bitmap.h" -#include "sysemu/runstate.h" -#include "sysemu/xen-mapcache.h" +#include "system/runstate.h" +#include "system/xen-mapcache.h" #include "trace.h" #include diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index 218ac851cf..a10c44cc75 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -13,9 +13,9 @@ #include "hw/boards.h" #include "hw/irq.h" #include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "sysemu/tpm.h" -#include "sysemu/tpm_backend.h" +#include "system/system.h" +#include "system/tpm.h" +#include "system/tpm_backend.h" #include "hw/xen/xen-pvh-common.h" #include "trace.h" diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index 45ae134b84..5698cc7c0e 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "hw/xen/xen-legacy-backend.h" #include "qemu/option.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/blockdev.h" +#include "system/system.h" /* ------------------------------------------------------------- */ diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 24395f42cb..99c02492ef 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -27,8 +27,8 @@ #include "hw/boards.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen-bus.h" -#include "sysemu/block-backend.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/system.h" static void xen_init_pv(MachineState *machine) { diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 2160e61964..0a1fd90037 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -27,8 +27,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index 5310a88861..98622ae86d 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/pci-host/gpex.h" diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 2e264c6198..9efe91933f 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -29,7 +29,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/qdev-properties.h" @@ -41,9 +41,9 @@ #include "hw/sysbus.h" #include "hw/block/flash.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "bootparam.h" diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 891c44cf2d..53785cdb87 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -21,8 +21,8 @@ #ifndef CONFIG_USER_ONLY #include "cpu.h" -#include "sysemu/xen.h" -#include "sysemu/tcg.h" +#include "system/xen.h" +#include "system/tcg.h" #include "exec/ramlist.h" #include "exec/ramblock.h" #include "exec/exec-all.h" diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 579c45f5ba..9d0fe6f2f9 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -19,7 +19,7 @@ #include "qemu/units.h" #include "hw/registerfields.h" #include "hw/acpi/aml-build.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #ifdef CONFIG_TPM diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index e5815b0d12..445ba1be21 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -14,7 +14,7 @@ #include "hw/i2c/allwinner-i2c.h" #include "hw/ssi/allwinner-a10-spi.h" #include "hw/watchdog/allwinner-wdt.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "target/arm/cpu.h" #include "qom/object.h" diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 24ba4e1bf4..db897c86f0 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -49,7 +49,7 @@ #include "hw/i2c/allwinner-i2c.h" #include "hw/watchdog/allwinner-wdt.h" #include "target/arm/cpu.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" /** * Allwinner H3 device list diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 614e74b7ed..f8a0e94251 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -35,7 +35,7 @@ #include "hw/usb/hcd-ehci.h" #include "hw/watchdog/allwinner-wdt.h" #include "target/arm/cpu.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" enum { AW_R40_DEV_SRAM_A1, diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index aca4f8061b..c8e94e6aed 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -36,7 +36,7 @@ #include "hw/arm/boot.h" #include "hw/arm/bsa.h" #include "hw/block/flash.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/intc/arm_gicv3_common.h" #include "qom/object.h" diff --git a/include/hw/boards.h b/include/hw/boards.h index 5723ee76bd..2ad711e56d 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -4,8 +4,8 @@ #define HW_BOARDS_H #include "exec/memory.h" -#include "sysemu/hostmem.h" -#include "sysemu/blockdev.h" +#include "system/hostmem.h" +#include "system/blockdev.h" #include "qapi/qapi-types-machine.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h index 24d003fe04..0df5b058f5 100644 --- a/include/hw/core/sysemu-cpu-ops.h +++ b/include/hw/core/sysemu-cpu-ops.h @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef SYSEMU_CPU_OPS_H -#define SYSEMU_CPU_OPS_H +#ifndef SYSTEM_CPU_OPS_H +#define SYSTEM_CPU_OPS_H #include "hw/core/cpu.h" @@ -89,4 +89,4 @@ typedef struct SysemuCPUOps { } SysemuCPUOps; -#endif /* SYSEMU_CPU_OPS_H */ +#endif /* SYSTEM_CPU_OPS_H */ diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h index efc75217d5..9c57c49910 100644 --- a/include/hw/dma/xlnx-zdma.h +++ b/include/hw/dma/xlnx-zdma.h @@ -31,7 +31,7 @@ #include "hw/sysbus.h" #include "hw/register.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define ZDMA_R_MAX (0x204 / 4) diff --git a/include/hw/dma/xlnx_dpdma.h b/include/hw/dma/xlnx_dpdma.h index 40537a848b..1ec0d265be 100644 --- a/include/hw/dma/xlnx_dpdma.h +++ b/include/hw/dma/xlnx_dpdma.h @@ -27,7 +27,7 @@ #include "hw/sysbus.h" #include "ui/console.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define XLNX_DPDMA_REG_ARRAY_SIZE (0x1000 >> 2) diff --git a/include/hw/hyperv/vmbus.h b/include/hw/hyperv/vmbus.h index 5c505852f2..06b948bbb0 100644 --- a/include/hw/hyperv/vmbus.h +++ b/include/hw/hyperv/vmbus.h @@ -10,8 +10,8 @@ #ifndef HW_HYPERV_VMBUS_H #define HW_HYPERV_VMBUS_H -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" +#include "system/system.h" +#include "system/dma.h" #include "hw/qdev-core.h" #include "migration/vmstate.h" #include "hw/hyperv/vmbus-proto.h" diff --git a/include/hw/i386/hostmem-epc.h b/include/hw/i386/hostmem-epc.h index 846c726085..3988deca85 100644 --- a/include/hw/i386/hostmem-epc.h +++ b/include/hw/i386/hostmem-epc.h @@ -12,7 +12,7 @@ #ifndef QEMU_HOSTMEM_EPC_H #define QEMU_HOSTMEM_EPC_H -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #define TYPE_MEMORY_BACKEND_EPC "memory-backend-epc" diff --git a/include/hw/ide/ide-dev.h b/include/hw/ide/ide-dev.h index 9a0d71db4e..92e8868780 100644 --- a/include/hw/ide/ide-dev.h +++ b/include/hw/ide/ide-dev.h @@ -20,7 +20,7 @@ #ifndef IDE_DEV_H #define IDE_DEV_H -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/qdev-properties.h" #include "hw/block/block.h" diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index 0dc45104d4..14d051348b 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -10,7 +10,7 @@ #ifndef HW_ISA_SUPERIO_H #define HW_ISA_SUPERIO_H -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/isa/isa.h" #include "qom/object.h" diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index c60361dc9e..47578ccc7f 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -4,7 +4,7 @@ #include "exec/hwaddr.h" #include "standard-headers/linux/qemu_fw_cfg.h" #include "hw/sysbus.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define TYPE_FW_CFG "fw_cfg" diff --git a/include/hw/nvram/xlnx-bbram.h b/include/hw/nvram/xlnx-bbram.h index bce8e89d90..58acbe9f51 100644 --- a/include/hw/nvram/xlnx-bbram.h +++ b/include/hw/nvram/xlnx-bbram.h @@ -26,7 +26,7 @@ #ifndef XLNX_BBRAM_H #define XLNX_BBRAM_H -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/qdev-core.h" #include "hw/irq.h" #include "hw/sysbus.h" diff --git a/include/hw/nvram/xlnx-efuse.h b/include/hw/nvram/xlnx-efuse.h index cff7924106..ef14fb0528 100644 --- a/include/hw/nvram/xlnx-efuse.h +++ b/include/hw/nvram/xlnx-efuse.h @@ -27,7 +27,7 @@ #ifndef XLNX_EFUSE_H #define XLNX_EFUSE_H -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/qdev-core.h" #define TYPE_XLNX_EFUSE "xlnx-efuse" diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 603c456c3a..cefeb388bd 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -2,8 +2,8 @@ #define QEMU_PCI_H #include "exec/memory.h" -#include "sysemu/dma.h" -#include "sysemu/host_iommu_device.h" +#include "system/dma.h" +#include "system/host_iommu_device.h" /* PCI includes legacy ISA access. */ #include "hw/isa/isa.h" diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index c774f6bf84..672c2be471 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -25,7 +25,7 @@ #include "exec/memory.h" #include "qemu/iov.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/sysbus.h" #include "qom/object.h" diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index af4aa1cb0f..a6c0547e31 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -2,7 +2,7 @@ #define HW_SPAPR_H #include "qemu/units.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/boards.h" #include "hw/ppc/spapr_drc.h" #include "hw/mem/pc-dimm.h" diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index 02a63b3666..9ff42909c9 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -15,7 +15,7 @@ #include #include "qom/object.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/qdev-core.h" #include "qapi/error.h" diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 7eae1a4847..b8de4b06fb 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -23,7 +23,7 @@ */ #include "hw/ppc/spapr.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/irq.h" #include "qom/object.h" diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index ebee982528..ea5d03a346 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -140,7 +140,7 @@ #ifndef PPC_XIVE_H #define PPC_XIVE_H -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/sysbus.h" #include "hw/ppc/xive_regs.h" #include "qom/object.h" diff --git a/include/hw/riscv/numa.h b/include/hw/riscv/numa.h index 8f5280211d..147f01619b 100644 --- a/include/hw/riscv/numa.h +++ b/include/hw/riscv/numa.h @@ -21,7 +21,7 @@ #include "hw/boards.h" #include "hw/sysbus.h" -#include "sysemu/numa.h" +#include "system/numa.h" /** * riscv_socket_count: diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 8289e45837..cd97e2b707 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -15,7 +15,7 @@ #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/ioinst.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "target/s390x/cpu-qom.h" /* Channel subsystem constants. */ diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h index a55c448aad..5cb8da540b 100644 --- a/include/hw/s390x/s390-pci-inst.h +++ b/include/hw/s390x/s390-pci-inst.h @@ -15,7 +15,7 @@ #define HW_S390_PCI_INST_H #include "s390-pci-bus.h" -#include "sysemu/dma.h" +#include "system/dma.h" /* Load/Store status codes */ #define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4 diff --git a/include/hw/tricore/triboard.h b/include/hw/tricore/triboard.h index 4fdd2d7d97..8250470643 100644 --- a/include/hw/tricore/triboard.h +++ b/include/hw/tricore/triboard.h @@ -20,7 +20,7 @@ #include "qapi/error.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "exec/address-spaces.h" #include "qom/object.h" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index e0ce6ec3a9..d57111843d 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -29,10 +29,10 @@ #ifdef CONFIG_LINUX #include #endif -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/vfio/vfio-container-base.h" -#include "sysemu/host_iommu_device.h" -#include "sysemu/iommufd.h" +#include "system/host_iommu_device.h" +#include "system/iommufd.h" #define VFIO_MSG_PREFIX "vfio %s: " diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 5139cf8ab6..b12c18a43b 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -17,7 +17,7 @@ #include "standard-headers/linux/virtio_balloon.h" #include "hw/virtio/virtio.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "qom/object.h" #define TYPE_VIRTIO_BALLOON "virtio-balloon-device" diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index 5c14110c4b..8a16218c40 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -17,9 +17,9 @@ #include "standard-headers/linux/virtio_blk.h" #include "hw/virtio/virtio.h" #include "hw/block/block.h" -#include "sysemu/iothread.h" -#include "sysemu/block-backend.h" -#include "sysemu/block-ram-registrar.h" +#include "system/iothread.h" +#include "system/block-backend.h" +#include "system/block-ram-registrar.h" #include "qom/object.h" #include "qapi/qapi-types-virtio.h" diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h index 348749f5d5..2d56513693 100644 --- a/include/hw/virtio/virtio-crypto.h +++ b/include/hw/virtio/virtio-crypto.h @@ -16,8 +16,8 @@ #include "standard-headers/linux/virtio_crypto.h" #include "hw/virtio/virtio.h" -#include "sysemu/iothread.h" -#include "sysemu/cryptodev.h" +#include "system/iothread.h" +#include "system/cryptodev.h" #include "qom/object.h" diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 8c977beebd..bd93672185 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -19,7 +19,7 @@ #include "ui/console.h" #include "hw/virtio/virtio.h" #include "qemu/log.h" -#include "sysemu/vhost-user-backend.h" +#include "system/vhost-user-backend.h" #include "standard-headers/linux/virtio_gpu.h" #include "standard-headers/linux/virtio_ids.h" diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h index e69c0aeca3..e097b0b521 100644 --- a/include/hw/virtio/virtio-input.h +++ b/include/hw/virtio/virtio-input.h @@ -4,7 +4,7 @@ #include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-user-base.h" #include "ui/input.h" -#include "sysemu/vhost-user-backend.h" +#include "system/vhost-user-backend.h" /* ----------------------------------------------------------------- */ /* virtio input protocol */ diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 7db4210b16..3b86050f2c 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -25,7 +25,7 @@ #include "hw/pci/pci.h" #include "qom/object.h" #include "qapi/qapi-types-virtio.h" -#include "sysemu/host_iommu_device.h" +#include "system/host_iommu_device.h" #define TYPE_VIRTIO_IOMMU "virtio-iommu-device" #define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci" diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index a1af144c28..b23946b770 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -17,7 +17,7 @@ #include "hw/resettable.h" #include "hw/virtio/virtio.h" #include "qapi/qapi-types-misc.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qom/object.h" #define TYPE_VIRTIO_MEM "virtio-mem" diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h index 82734255d9..7e6d27f9f0 100644 --- a/include/hw/virtio/virtio-rng.h +++ b/include/hw/virtio/virtio-rng.h @@ -13,7 +13,7 @@ #define QEMU_VIRTIO_RNG_H #include "hw/virtio/virtio.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "standard-headers/linux/virtio_rng.h" #include "qom/object.h" diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 7be0105918..be230cd4bf 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -22,7 +22,7 @@ #include "hw/virtio/virtio.h" #include "hw/scsi/scsi.h" #include "chardev/char-fe.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSCSICommon, VIRTIO_SCSI_COMMON) diff --git a/include/hw/xen/xen-block.h b/include/hw/xen/xen-block.h index d692ea7580..449a7f75fb 100644 --- a/include/hw/xen/xen-block.h +++ b/include/hw/xen/xen-block.h @@ -11,7 +11,7 @@ #include "hw/xen/xen-bus.h" #include "hw/block/block.h" #include "hw/block/dataplane/xen-block.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "qom/object.h" typedef enum XenBlockVdevType { diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h index 0f586c4384..c1ea2c0d78 100644 --- a/include/hw/xen/xen-hvm-common.h +++ b/include/hw/xen/xen-hvm-common.h @@ -8,10 +8,10 @@ #include "hw/hw.h" #include "hw/xen/xen_native.h" #include "hw/xen/xen-legacy-backend.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" -#include "sysemu/xen-mapcache.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" +#include "system/xen-mapcache.h" #include "qemu/error-report.h" #include diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 646306c272..2d71f24cb3 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -27,7 +27,7 @@ #include "block/aio.h" #include "qom/object.h" -#include "sysemu/event-loop-base.h" +#include "system/event-loop-base.h" #define SIG_IPI SIGUSR1 diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index fdff07fd99..b94fb5fab8 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -8,7 +8,7 @@ * To avoid getting into possible circular include dependencies, this * file should not include any other QEMU headers, with the exceptions * of config-host.h, config-target.h, qemu/compiler.h, - * sysemu/os-posix.h, sysemu/os-win32.h, glib-compat.h and + * system/os-posix.h, system/os-win32.h, glib-compat.h and * qemu/typedefs.h, all of which are doing a similar job to this file * and are under similar constraints. * @@ -128,7 +128,7 @@ QEMU_EXTERN_C int daemon(int, int); #include #include #include -/* setjmp must be declared before sysemu/os-win32.h +/* setjmp must be declared before system/os-win32.h * because it is redefined there. */ #include #include @@ -161,11 +161,11 @@ QEMU_EXTERN_C int daemon(int, int); #include "glib-compat.h" #ifdef _WIN32 -#include "sysemu/os-win32.h" +#include "system/os-win32.h" #endif #ifdef CONFIG_POSIX -#include "sysemu/os-posix.h" +#include "system/os-posix.h" #endif #ifdef __cplusplus diff --git a/include/sysemu/accel-blocker.h b/include/system/accel-blocker.h similarity index 98% rename from include/sysemu/accel-blocker.h rename to include/system/accel-blocker.h index f07f368358..e10099d6a9 100644 --- a/include/sysemu/accel-blocker.h +++ b/include/system/accel-blocker.h @@ -14,7 +14,7 @@ #ifndef ACCEL_BLOCKER_H #define ACCEL_BLOCKER_H -#include "sysemu/cpus.h" +#include "system/cpus.h" void accel_blocker_init(void); diff --git a/include/sysemu/accel-ops.h b/include/system/accel-ops.h similarity index 100% rename from include/sysemu/accel-ops.h rename to include/system/accel-ops.h diff --git a/include/sysemu/arch_init.h b/include/system/arch_init.h similarity index 100% rename from include/sysemu/arch_init.h rename to include/system/arch_init.h diff --git a/include/sysemu/balloon.h b/include/system/balloon.h similarity index 100% rename from include/sysemu/balloon.h rename to include/system/balloon.h diff --git a/include/sysemu/block-backend-common.h b/include/system/block-backend-common.h similarity index 100% rename from include/sysemu/block-backend-common.h rename to include/system/block-backend-common.h diff --git a/include/sysemu/block-backend-global-state.h b/include/system/block-backend-global-state.h similarity index 100% rename from include/sysemu/block-backend-global-state.h rename to include/system/block-backend-global-state.h diff --git a/include/sysemu/block-backend-io.h b/include/system/block-backend-io.h similarity index 100% rename from include/sysemu/block-backend-io.h rename to include/system/block-backend-io.h diff --git a/include/sysemu/block-backend.h b/include/system/block-backend.h similarity index 100% rename from include/sysemu/block-backend.h rename to include/system/block-backend.h diff --git a/include/sysemu/block-ram-registrar.h b/include/system/block-ram-registrar.h similarity index 100% rename from include/sysemu/block-ram-registrar.h rename to include/system/block-ram-registrar.h diff --git a/include/sysemu/blockdev.h b/include/system/blockdev.h similarity index 100% rename from include/sysemu/blockdev.h rename to include/system/blockdev.h diff --git a/include/sysemu/cpu-throttle.h b/include/system/cpu-throttle.h similarity index 95% rename from include/sysemu/cpu-throttle.h rename to include/system/cpu-throttle.h index 420702b8d3..44bf6a5389 100644 --- a/include/sysemu/cpu-throttle.h +++ b/include/system/cpu-throttle.h @@ -16,8 +16,8 @@ * */ -#ifndef SYSEMU_CPU_THROTTLE_H -#define SYSEMU_CPU_THROTTLE_H +#ifndef SYSTEM_CPU_THROTTLE_H +#define SYSTEM_CPU_THROTTLE_H #include "qemu/timer.h" @@ -79,4 +79,4 @@ void cpu_throttle_dirty_sync_timer_tick(void *opaque); */ void cpu_throttle_dirty_sync_timer(bool enable); -#endif /* SYSEMU_CPU_THROTTLE_H */ +#endif /* SYSTEM_CPU_THROTTLE_H */ diff --git a/include/sysemu/cpu-timers-internal.h b/include/system/cpu-timers-internal.h similarity index 100% rename from include/sysemu/cpu-timers-internal.h rename to include/system/cpu-timers-internal.h diff --git a/include/sysemu/cpu-timers.h b/include/system/cpu-timers.h similarity index 96% rename from include/sysemu/cpu-timers.h rename to include/system/cpu-timers.h index 7bfa960fbd..64ae54f6d6 100644 --- a/include/sysemu/cpu-timers.h +++ b/include/system/cpu-timers.h @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. * */ -#ifndef SYSEMU_CPU_TIMERS_H -#define SYSEMU_CPU_TIMERS_H +#ifndef SYSTEM_CPU_TIMERS_H +#define SYSTEM_CPU_TIMERS_H #include "qemu/timer.h" @@ -101,4 +101,4 @@ int64_t cpus_get_virtual_clock(void); void cpus_set_virtual_clock(int64_t new_time); int64_t cpus_get_elapsed_ticks(void); -#endif /* SYSEMU_CPU_TIMERS_H */ +#endif /* SYSTEM_CPU_TIMERS_H */ diff --git a/include/sysemu/cpus.h b/include/system/cpus.h similarity index 97% rename from include/sysemu/cpus.h rename to include/system/cpus.h index b4a566cfe7..3d8fd368f3 100644 --- a/include/sysemu/cpus.h +++ b/include/system/cpus.h @@ -1,7 +1,7 @@ #ifndef QEMU_CPUS_H #define QEMU_CPUS_H -#include "sysemu/accel-ops.h" +#include "system/accel-ops.h" /* register accel-specific operations */ void cpus_register_accel(const AccelOpsClass *i); diff --git a/include/sysemu/cryptodev-vhost-user.h b/include/system/cryptodev-vhost-user.h similarity index 97% rename from include/sysemu/cryptodev-vhost-user.h rename to include/system/cryptodev-vhost-user.h index 60710502c2..5138c146fa 100644 --- a/include/sysemu/cryptodev-vhost-user.h +++ b/include/system/cryptodev-vhost-user.h @@ -24,7 +24,7 @@ #ifndef CRYPTODEV_VHOST_USER_H #define CRYPTODEV_VHOST_USER_H -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #define VHOST_USER_MAX_AUTH_KEY_LEN 512 #define VHOST_USER_MAX_CIPHER_KEY_LEN 64 diff --git a/include/sysemu/cryptodev-vhost.h b/include/system/cryptodev-vhost.h similarity index 99% rename from include/sysemu/cryptodev-vhost.h rename to include/system/cryptodev-vhost.h index 4c3c22acae..b0bb09e70a 100644 --- a/include/sysemu/cryptodev-vhost.h +++ b/include/system/cryptodev-vhost.h @@ -28,7 +28,7 @@ #include "hw/virtio/vhost-backend.h" #include "chardev/char.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" typedef struct CryptoDevBackendVhostOptions { diff --git a/include/sysemu/cryptodev.h b/include/system/cryptodev.h similarity index 100% rename from include/sysemu/cryptodev.h rename to include/system/cryptodev.h diff --git a/include/sysemu/device_tree.h b/include/system/device_tree.h similarity index 100% rename from include/sysemu/device_tree.h rename to include/system/device_tree.h diff --git a/include/sysemu/dirtylimit.h b/include/system/dirtylimit.h similarity index 100% rename from include/sysemu/dirtylimit.h rename to include/system/dirtylimit.h diff --git a/include/sysemu/dirtyrate.h b/include/system/dirtyrate.h similarity index 100% rename from include/sysemu/dirtyrate.h rename to include/system/dirtyrate.h diff --git a/include/sysemu/dma.h b/include/system/dma.h similarity index 100% rename from include/sysemu/dma.h rename to include/system/dma.h diff --git a/include/sysemu/dump-arch.h b/include/system/dump-arch.h similarity index 100% rename from include/sysemu/dump-arch.h rename to include/system/dump-arch.h diff --git a/include/sysemu/dump.h b/include/system/dump.h similarity index 99% rename from include/sysemu/dump.h rename to include/system/dump.h index d702854853..607bd7b220 100644 --- a/include/sysemu/dump.h +++ b/include/system/dump.h @@ -39,8 +39,8 @@ #define DUMP_LEVEL (1) #define DISKDUMP_HEADER_BLOCKS (1) -#include "sysemu/dump-arch.h" -#include "sysemu/memory_mapping.h" +#include "system/dump-arch.h" +#include "system/memory_mapping.h" typedef struct QEMU_PACKED MakedumpfileHeader { char signature[16]; /* = "makedumpfile" */ diff --git a/include/sysemu/event-loop-base.h b/include/system/event-loop-base.h similarity index 100% rename from include/sysemu/event-loop-base.h rename to include/system/event-loop-base.h diff --git a/include/sysemu/host_iommu_device.h b/include/system/host_iommu_device.h similarity index 100% rename from include/sysemu/host_iommu_device.h rename to include/system/host_iommu_device.h diff --git a/include/sysemu/hostmem.h b/include/system/hostmem.h similarity index 97% rename from include/sysemu/hostmem.h rename to include/system/hostmem.h index 67f45abe39..5c21ca55c0 100644 --- a/include/sysemu/hostmem.h +++ b/include/system/hostmem.h @@ -10,10 +10,10 @@ * See the COPYING file in the top-level directory. */ -#ifndef SYSEMU_HOSTMEM_H -#define SYSEMU_HOSTMEM_H +#ifndef SYSTEM_HOSTMEM_H +#define SYSTEM_HOSTMEM_H -#include "sysemu/numa.h" +#include "system/numa.h" #include "qapi/qapi-types-machine.h" #include "qom/object.h" #include "exec/memory.h" diff --git a/include/sysemu/hvf.h b/include/system/hvf.h similarity index 100% rename from include/sysemu/hvf.h rename to include/system/hvf.h diff --git a/include/sysemu/hvf_int.h b/include/system/hvf_int.h similarity index 100% rename from include/sysemu/hvf_int.h rename to include/system/hvf_int.h diff --git a/include/sysemu/hw_accel.h b/include/system/hw_accel.h similarity index 83% rename from include/sysemu/hw_accel.h rename to include/system/hw_accel.h index c71b77e71f..380e9e640b 100644 --- a/include/sysemu/hw_accel.h +++ b/include/system/hw_accel.h @@ -12,10 +12,10 @@ #define QEMU_HW_ACCEL_H #include "hw/core/cpu.h" -#include "sysemu/kvm.h" -#include "sysemu/hvf.h" -#include "sysemu/whpx.h" -#include "sysemu/nvmm.h" +#include "system/kvm.h" +#include "system/hvf.h" +#include "system/whpx.h" +#include "system/nvmm.h" void cpu_synchronize_state(CPUState *cpu); void cpu_synchronize_post_reset(CPUState *cpu); diff --git a/include/sysemu/iommufd.h b/include/system/iommufd.h similarity index 96% rename from include/sysemu/iommufd.h rename to include/system/iommufd.h index 4c4886c778..cbab75bfbf 100644 --- a/include/sysemu/iommufd.h +++ b/include/system/iommufd.h @@ -11,13 +11,13 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef SYSEMU_IOMMUFD_H -#define SYSEMU_IOMMUFD_H +#ifndef SYSTEM_IOMMUFD_H +#define SYSTEM_IOMMUFD_H #include "qom/object.h" #include "exec/hwaddr.h" #include "exec/cpu-common.h" -#include "sysemu/host_iommu_device.h" +#include "system/host_iommu_device.h" #define TYPE_IOMMUFD_BACKEND "iommufd" OBJECT_DECLARE_TYPE(IOMMUFDBackend, IOMMUFDBackendClass, IOMMUFD_BACKEND) diff --git a/include/sysemu/iothread.h b/include/system/iothread.h similarity index 97% rename from include/sysemu/iothread.h rename to include/system/iothread.h index 2102a90eca..d95c17a645 100644 --- a/include/sysemu/iothread.h +++ b/include/system/iothread.h @@ -17,7 +17,7 @@ #include "block/aio.h" #include "qemu/thread.h" #include "qom/object.h" -#include "sysemu/event-loop-base.h" +#include "system/event-loop-base.h" #define TYPE_IOTHREAD "iothread" diff --git a/include/sysemu/kvm.h b/include/system/kvm.h similarity index 100% rename from include/sysemu/kvm.h rename to include/system/kvm.h diff --git a/include/sysemu/kvm_int.h b/include/system/kvm_int.h similarity index 99% rename from include/sysemu/kvm_int.h rename to include/system/kvm_int.h index a1e72763da..4de6106869 100644 --- a/include/sysemu/kvm_int.h +++ b/include/system/kvm_int.h @@ -13,7 +13,7 @@ #include "qapi/qapi-types-common.h" #include "qemu/accel.h" #include "qemu/queue.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/boards.h" #include "hw/i386/topology.h" #include "io/channel-socket.h" diff --git a/include/sysemu/kvm_xen.h b/include/system/kvm_xen.h similarity index 93% rename from include/sysemu/kvm_xen.h rename to include/system/kvm_xen.h index 961c702c4e..7d0e69f133 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/system/kvm_xen.h @@ -9,8 +9,8 @@ * */ -#ifndef QEMU_SYSEMU_KVM_XEN_H -#define QEMU_SYSEMU_KVM_XEN_H +#ifndef QEMU_SYSTEM_KVM_XEN_H +#define QEMU_SYSTEM_KVM_XEN_H /* The KVM API uses these to indicate "no GPA" or "no GFN" */ #define INVALID_GPA UINT64_MAX @@ -41,4 +41,4 @@ uint16_t kvm_xen_get_evtchn_max_pirq(void); #define XEN_SPECIAL_PFN(x) ((XEN_SPECIAL_AREA_ADDR >> TARGET_PAGE_BITS) + \ XEN_SPECIALPAGE_##x) -#endif /* QEMU_SYSEMU_KVM_XEN_H */ +#endif /* QEMU_SYSTEM_KVM_XEN_H */ diff --git a/include/sysemu/memory_mapping.h b/include/system/memory_mapping.h similarity index 100% rename from include/sysemu/memory_mapping.h rename to include/system/memory_mapping.h diff --git a/include/sysemu/numa.h b/include/system/numa.h similarity index 98% rename from include/sysemu/numa.h rename to include/system/numa.h index 0467614147..96d4ff9b85 100644 --- a/include/sysemu/numa.h +++ b/include/system/numa.h @@ -1,5 +1,5 @@ -#ifndef SYSEMU_NUMA_H -#define SYSEMU_NUMA_H +#ifndef SYSTEM_NUMA_H +#define SYSTEM_NUMA_H #include "qemu/bitmap.h" #include "qapi/qapi-types-machine.h" diff --git a/include/sysemu/nvmm.h b/include/system/nvmm.h similarity index 100% rename from include/sysemu/nvmm.h rename to include/system/nvmm.h diff --git a/include/sysemu/os-posix.h b/include/system/os-posix.h similarity index 100% rename from include/sysemu/os-posix.h rename to include/system/os-posix.h diff --git a/include/sysemu/os-win32.h b/include/system/os-win32.h similarity index 100% rename from include/sysemu/os-win32.h rename to include/system/os-win32.h diff --git a/include/sysemu/qtest.h b/include/system/qtest.h similarity index 100% rename from include/sysemu/qtest.h rename to include/system/qtest.h diff --git a/include/sysemu/replay.h b/include/system/replay.h similarity index 99% rename from include/sysemu/replay.h rename to include/system/replay.h index cba74fa9bc..8926d8cf4b 100644 --- a/include/sysemu/replay.h +++ b/include/system/replay.h @@ -8,8 +8,8 @@ * See the COPYING file in the top-level directory. * */ -#ifndef SYSEMU_REPLAY_H -#define SYSEMU_REPLAY_H +#ifndef SYSTEM_REPLAY_H +#define SYSTEM_REPLAY_H #ifdef CONFIG_USER_ONLY #error Cannot include this header from user emulation diff --git a/include/sysemu/reset.h b/include/system/reset.h similarity index 98% rename from include/sysemu/reset.h rename to include/system/reset.h index 0e297c0e02..97131d94cf 100644 --- a/include/sysemu/reset.h +++ b/include/system/reset.h @@ -24,8 +24,8 @@ * THE SOFTWARE. */ -#ifndef QEMU_SYSEMU_RESET_H -#define QEMU_SYSEMU_RESET_H +#ifndef QEMU_SYSTEM_RESET_H +#define QEMU_SYSTEM_RESET_H #include "hw/resettable.h" #include "qapi/qapi-events-run-state.h" diff --git a/include/sysemu/rng-random.h b/include/system/rng-random.h similarity index 100% rename from include/sysemu/rng-random.h rename to include/system/rng-random.h diff --git a/include/sysemu/rng.h b/include/system/rng.h similarity index 100% rename from include/sysemu/rng.h rename to include/system/rng.h diff --git a/include/sysemu/rtc.h b/include/system/rtc.h similarity index 98% rename from include/sysemu/rtc.h rename to include/system/rtc.h index 0fc8ad6fdf..cde83fab15 100644 --- a/include/sysemu/rtc.h +++ b/include/system/rtc.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#ifndef SYSEMU_RTC_H -#define SYSEMU_RTC_H +#ifndef SYSTEM_RTC_H +#define SYSTEM_RTC_H /** * qemu_get_timedate: Get the current RTC time diff --git a/include/sysemu/runstate-action.h b/include/system/runstate-action.h similarity index 100% rename from include/sysemu/runstate-action.h rename to include/system/runstate-action.h diff --git a/include/sysemu/runstate.h b/include/system/runstate.h similarity index 98% rename from include/sysemu/runstate.h rename to include/system/runstate.h index 11c7ff3ffb..bffc3719d4 100644 --- a/include/sysemu/runstate.h +++ b/include/system/runstate.h @@ -1,5 +1,5 @@ -#ifndef SYSEMU_RUNSTATE_H -#define SYSEMU_RUNSTATE_H +#ifndef SYSTEM_RUNSTATE_H +#define SYSTEM_RUNSTATE_H #include "qapi/qapi-types-run-state.h" #include "qemu/notify.h" diff --git a/include/sysemu/seccomp.h b/include/system/seccomp.h similarity index 100% rename from include/sysemu/seccomp.h rename to include/system/seccomp.h diff --git a/include/sysemu/spdm-socket.h b/include/system/spdm-socket.h similarity index 100% rename from include/sysemu/spdm-socket.h rename to include/system/spdm-socket.h diff --git a/include/sysemu/stats.h b/include/system/stats.h similarity index 100% rename from include/sysemu/stats.h rename to include/system/stats.h diff --git a/include/sysemu/sysemu.h b/include/system/system.h similarity index 99% rename from include/sysemu/sysemu.h rename to include/system/system.h index 7ec419ce13..5364ad4f27 100644 --- a/include/sysemu/sysemu.h +++ b/include/system/system.h @@ -1,5 +1,5 @@ -#ifndef SYSEMU_H -#define SYSEMU_H +#ifndef SYSTEM_H +#define SYSTEM_H /* Misc. things related to the system emulator. */ #include "qemu/timer.h" diff --git a/include/sysemu/tcg.h b/include/system/tcg.h similarity index 88% rename from include/sysemu/tcg.h rename to include/system/tcg.h index 5e2ca9aab3..73229648c6 100644 --- a/include/sysemu/tcg.h +++ b/include/system/tcg.h @@ -7,8 +7,8 @@ /* header to be included in non-TCG-specific code */ -#ifndef SYSEMU_TCG_H -#define SYSEMU_TCG_H +#ifndef SYSTEM_TCG_H +#define SYSTEM_TCG_H #ifdef CONFIG_TCG extern bool tcg_allowed; diff --git a/include/sysemu/tpm.h b/include/system/tpm.h similarity index 100% rename from include/sysemu/tpm.h rename to include/system/tpm.h diff --git a/include/sysemu/tpm_backend.h b/include/system/tpm_backend.h similarity index 99% rename from include/sysemu/tpm_backend.h rename to include/system/tpm_backend.h index 7fabafefee..01b11f629c 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/system/tpm_backend.h @@ -15,7 +15,7 @@ #include "qom/object.h" #include "qemu/option.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "qapi/error.h" #ifdef CONFIG_TPM diff --git a/include/sysemu/tpm_util.h b/include/system/tpm_util.h similarity index 94% rename from include/sysemu/tpm_util.h rename to include/system/tpm_util.h index 08f05172a7..1858693225 100644 --- a/include/sysemu/tpm_util.h +++ b/include/system/tpm_util.h @@ -19,10 +19,10 @@ * License along with this library; if not, see */ -#ifndef SYSEMU_TPM_UTIL_H -#define SYSEMU_TPM_UTIL_H +#ifndef SYSTEM_TPM_UTIL_H +#define SYSTEM_TPM_UTIL_H -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "qemu/bswap.h" void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len); @@ -69,4 +69,4 @@ static inline void tpm_cmd_set_error(void *b, uint32_t error) void tpm_util_show_buffer(const unsigned char *buffer, size_t buffer_size, const char *string); -#endif /* SYSEMU_TPM_UTIL_H */ +#endif /* SYSTEM_TPM_UTIL_H */ diff --git a/include/sysemu/vhost-user-backend.h b/include/system/vhost-user-backend.h similarity index 100% rename from include/sysemu/vhost-user-backend.h rename to include/system/vhost-user-backend.h diff --git a/include/sysemu/watchdog.h b/include/system/watchdog.h similarity index 100% rename from include/sysemu/watchdog.h rename to include/system/watchdog.h diff --git a/include/sysemu/whpx.h b/include/system/whpx.h similarity index 100% rename from include/sysemu/whpx.h rename to include/system/whpx.h diff --git a/include/sysemu/xen-mapcache.h b/include/system/xen-mapcache.h similarity index 98% rename from include/sysemu/xen-mapcache.h rename to include/system/xen-mapcache.h index b5e3ea1bc0..b68f196ddd 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/system/xen-mapcache.h @@ -10,7 +10,7 @@ #define XEN_MAPCACHE_H #include "exec/cpu-common.h" -#include "sysemu/xen.h" +#include "system/xen.h" typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, ram_addr_t size); diff --git a/include/sysemu/xen.h b/include/system/xen.h similarity index 92% rename from include/sysemu/xen.h rename to include/system/xen.h index d70eacfbe2..990c19a8ef 100644 --- a/include/sysemu/xen.h +++ b/include/system/xen.h @@ -7,11 +7,11 @@ /* header to be included in non-Xen-specific code */ -#ifndef SYSEMU_XEN_H -#define SYSEMU_XEN_H +#ifndef SYSTEM_XEN_H +#define SYSTEM_XEN_H #ifdef CONFIG_USER_ONLY -#error Cannot include sysemu/xen.h from user emulation +#error Cannot include system/xen.h from user emulation #endif #include "exec/cpu-common.h" diff --git a/iothread.c b/iothread.c index e1e9e04736..589bcd3552 100644 --- a/iothread.c +++ b/iothread.c @@ -17,8 +17,8 @@ #include "qemu/module.h" #include "block/aio.h" #include "block/block.h" -#include "sysemu/event-loop-base.h" -#include "sysemu/iothread.h" +#include "system/event-loop-base.h" +#include "system/iothread.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qemu/error-report.h" diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index a7d55048c2..f2c352d4a7 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -62,8 +62,8 @@ #include "block/block.h" #include "block/block_int.h" #include "block/dirty-bitmap.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "migration/misc.h" diff --git a/migration/colo.c b/migration/colo.c index 9590f281d0..afc9869020 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" #include "migration.h" @@ -30,8 +30,8 @@ #include "net/colo.h" #include "block/block.h" #include "qapi/qapi-events-migration.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "net/filter.h" #include "options.h" diff --git a/migration/cpu-throttle.c b/migration/cpu-throttle.c index 5179019e33..0642e6bdea 100644 --- a/migration/cpu-throttle.c +++ b/migration/cpu-throttle.c @@ -26,8 +26,8 @@ #include "qemu/thread.h" #include "hw/core/cpu.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-throttle.h" +#include "system/cpus.h" +#include "system/cpu-throttle.h" #include "migration.h" #include "migration-stats.h" #include "trace.h" diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index f7e86686fc..7c955894e4 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -25,8 +25,8 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qmp/qdict.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "exec/memory.h" #include "qemu/xxhash.h" #include "migration.h" diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h index 869c060941..35225c36b6 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -13,7 +13,7 @@ #ifndef QEMU_MIGRATION_DIRTYRATE_H #define QEMU_MIGRATION_DIRTYRATE_H -#include "sysemu/dirtyrate.h" +#include "system/dirtyrate.h" /* * Sample 512 pages per GB as default. diff --git a/migration/global_state.c b/migration/global_state.c index 3a9796cae2..c1f90fce0f 100644 --- a/migration/global_state.c +++ b/migration/global_state.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qapi/error.h" #include "migration.h" #include "migration/global_state.h" diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 20d1a6e219..e8527bef80 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -27,9 +27,9 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/sockets.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "ui/qemu-spice.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "options.h" #include "migration.h" diff --git a/migration/migration.c b/migration/migration.c index 8c5bd0a75c..b3e5156643 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -22,9 +22,9 @@ #include "fd.h" #include "file.h" #include "socket.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpu-throttle.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/cpu-throttle.h" #include "rdma.h" #include "ram.h" #include "migration/global_state.h" @@ -59,13 +59,13 @@ #include "multifd.h" #include "threadinfo.h" #include "qemu/yank.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "yank_functions.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "options.h" -#include "sysemu/dirtylimit.h" +#include "system/dirtylimit.h" #include "qemu/sockets.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #define NOTIFIER_ELEM_INIT(array, elem) \ [elem] = NOTIFIER_WITH_RETURN_LIST_INITIALIZER((array)[elem]) diff --git a/migration/migration.h b/migration/migration.h index 3857905c0e..7b6e718690 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -25,7 +25,7 @@ #include "net/announce.h" #include "qom/object.h" #include "postcopy-ram.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "migration/misc.h" #define MIGRATION_THREAD_SNAPSHOT "mig/snapshot" diff --git a/migration/multifd.c b/migration/multifd.c index 498e71fd10..4f973d70e0 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "qemu/rcu.h" #include "exec/target_page.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "exec/ramblock.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/migration/options.c b/migration/options.c index ad8d6989a8..daac3c4514 100644 --- a/migration/options.c +++ b/migration/options.c @@ -20,7 +20,7 @@ #include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qnull.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "migration/colo.h" #include "migration/misc.h" #include "migration.h" @@ -28,7 +28,7 @@ #include "qemu-file.h" #include "ram.h" #include "options.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" /* Maximum migrate downtime set to 2000 seconds */ #define MAX_MIGRATE_DOWNTIME_SECONDS 2000 diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index a535fd2e30..6a6da6ba7f 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qemu/notify.h" #include "qemu/rcu.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" #include "trace.h" #include "hw/boards.h" diff --git a/migration/ram.c b/migration/ram.c index 05ff9eb328..a60666d3f6 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -52,15 +52,15 @@ #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "migration/colo.h" -#include "sysemu/cpu-throttle.h" +#include "system/cpu-throttle.h" #include "savevm.h" #include "qemu/iov.h" #include "multifd.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "rdma.h" #include "options.h" -#include "sysemu/dirtylimit.h" -#include "sysemu/kvm.h" +#include "system/dirtylimit.h" +#include "system/kvm.h" #include "hw/boards.h" /* for machine_dump_guest_core() */ diff --git a/migration/savevm.c b/migration/savevm.c index 98821c8120..927b1146c0 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -46,7 +46,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-builtin-visit.h" #include "qemu/error-report.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "exec/memory.h" #include "exec/target_page.h" #include "trace.h" @@ -57,16 +57,16 @@ #include "qemu/cutils.h" #include "io/channel-buffer.h" #include "io/channel-file.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" #include "migration/colo.h" #include "qemu/bitmap.h" #include "net/announce.h" #include "qemu/yank.h" #include "yank_functions.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "options.h" const unsigned int postcopy_ram_discard_version; diff --git a/monitor/fds.c b/monitor/fds.c index b5416b5b5d..cc35d2ec33 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -29,7 +29,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ctype.h" #include "qemu/cutils.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" /* file descriptors passed via SCM_RIGHTS */ typedef struct mon_fd_t mon_fd_t; diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index ff01cf9d8d..0300faa8a2 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -30,7 +30,7 @@ #include "monitor/monitor-internal.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" /* Set the current CPU defined by the user. Callers must hold BQL. */ int monitor_set_cpu(Monitor *mon, int cpu_index) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index f601d06ab8..80b2e5ff9f 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -28,7 +28,7 @@ #include "qapi/qmp/qdict.h" #include "qemu/cutils.h" #include "qemu/log.h" -#include "sysemu/sysemu.h" +#include "system/system.h" bool hmp_handle_error(Monitor *mon, Error *err) { diff --git a/monitor/hmp-target.c b/monitor/hmp-target.c index 1eb72ac1bf..37dfd7fd4c 100644 --- a/monitor/hmp-target.c +++ b/monitor/hmp-target.c @@ -26,7 +26,7 @@ #include "monitor-internal.h" #include "monitor/qdev.h" #include "net/slirp.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "block/block-hmp-cmds.h" diff --git a/monitor/hmp.c b/monitor/hmp.c index 460e8832f6..db90360553 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -35,7 +35,7 @@ #include "qemu/log.h" #include "qemu/option.h" #include "qemu/units.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "trace.h" static void monitor_command_cb(void *opaque, const char *cmdline, diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index cb628f681d..088960a503 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -31,7 +31,7 @@ #include "qapi/qmp/dispatch.h" #include "qapi/qmp/json-parser.h" #include "qemu/readline.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" /* * Supported types: diff --git a/monitor/monitor.c b/monitor/monitor.c index 56786c0ccc..9fad61f9df 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -31,7 +31,7 @@ #include "qapi/qmp/qdict.h" #include "qemu/error-report.h" #include "qemu/option.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "trace.h" /* diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index f21506efa5..150ca9f5cb 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -1,5 +1,5 @@ /* - * QMP commands related to the monitor (common to sysemu and tools) + * QMP commands related to the monitor (common to system and tools) * * Copyright (c) 2003-2004 Fabrice Bellard * diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index f84a0dc523..34f215097c 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -18,11 +18,11 @@ #include "monitor-internal.h" #include "monitor/qdev.h" #include "monitor/qmp-helpers.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qapi-init-commands.h" #include "qapi/qapi-commands-control.h" diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index 91895106a9..715d92d6ef 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -10,7 +10,7 @@ #ifndef NBD_INTERNAL_H #define NBD_INTERNAL_H #include "block/nbd.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "io/channel-tls.h" #include "qemu/iov.h" diff --git a/net/colo-compare.c b/net/colo-compare.c index 39f90c4065..165610bfe2 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -25,7 +25,7 @@ #include "chardev/char-fe.h" #include "qemu/sockets.h" #include "colo.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "net/colo-compare.h" #include "migration/colo.h" #include "util.h" diff --git a/net/dump.c b/net/dump.c index 956e34a123..d7dd2ce461 100644 --- a/net/dump.c +++ b/net/dump.c @@ -32,7 +32,7 @@ #include "qapi/visitor.h" #include "net/filter.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" typedef struct DumpState { int64_t start_ts; diff --git a/net/filter-replay.c b/net/filter-replay.c index 54690676ef..81b71afe35 100644 --- a/net/filter-replay.c +++ b/net/filter-replay.c @@ -17,7 +17,7 @@ #include "qemu/timer.h" #include "qapi/visitor.h" #include "net/filter.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qom/object.h" #define TYPE_FILTER_REPLAY "filter-replay" diff --git a/net/hub.c b/net/hub.c index 496a3d3b4b..cba20ebd87 100644 --- a/net/hub.c +++ b/net/hub.c @@ -20,7 +20,7 @@ #include "hub.h" #include "qemu/iov.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* * A hub broadcasts incoming packets to all its ports except the source port. diff --git a/net/net.c b/net/net.c index 7ef6885876..c1bb19a523 100644 --- a/net/net.c +++ b/net/net.c @@ -51,7 +51,7 @@ #include "qemu/keyval.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "net/colo-compare.h" #include "net/filter.h" #include "qapi/string-output-visitor.h" diff --git a/net/slirp.c b/net/slirp.c index eb9a456ed4..49dc62f776 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -40,7 +40,7 @@ #include "qemu/sockets.h" #include #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" diff --git a/net/tap.c b/net/tap.c index 3f90022c0b..ae1c7e3983 100644 --- a/net/tap.c +++ b/net/tap.c @@ -36,7 +36,7 @@ #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 30c4e53c13..dba5b5bab1 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -17,7 +17,7 @@ #include "clients.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include #include diff --git a/os-posix.c b/os-posix.c index 43f9a43f3f..9cce55ff2f 100644 --- a/os-posix.c +++ b/os-posix.c @@ -32,7 +32,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qemu/cutils.h" #ifdef CONFIG_LINUX diff --git a/os-win32.c b/os-win32.c index 725ad652e8..c1bff808b4 100644 --- a/os-win32.c +++ b/os-win32.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include #include -#include "sysemu/runstate.h" +#include "system/runstate.h" static BOOL WINAPI qemu_ctrl_handler(DWORD type) { diff --git a/qemu-img.c b/qemu-img.c index 7668f86769..2f2fac90e8 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -45,7 +45,7 @@ #include "qemu/units.h" #include "qemu/memalign.h" #include "qom/object_interfaces.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" #include "block/dirty-bitmap.h" diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index e2fab57183..bf08dcb8f5 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -12,7 +12,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu-io.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block.h" #include "block/block_int.h" /* for info_f() */ #include "block/qapi.h" diff --git a/qemu-io.c b/qemu-io.c index 6cb1e00385..fa04695d1d 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -30,7 +30,7 @@ #include "qapi/qmp/qstring.h" #include "qapi/qmp/qdict.h" #include "qom/object_interfaces.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block_int.h" #include "trace/control.h" #include "crypto/init.h" diff --git a/qemu-nbd.c b/qemu-nbd.c index a186d2e119..e7a961a556 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -24,8 +24,8 @@ #include "qemu/help-texts.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" /* for qemu_system_killed() prototype */ +#include "system/block-backend.h" +#include "system/runstate.h" /* for qemu_system_killed() prototype */ #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" diff --git a/replay/replay-audio.c b/replay/replay-audio.c index 91854f02ea..ed2ba2164b 100644 --- a/replay/replay-audio.c +++ b/replay/replay-audio.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "audio/audio.h" diff --git a/replay/replay-char.c b/replay/replay-char.c index 72b1f832dd..81dc416e98 100644 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "chardev/char.h" diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c index 82c66fff26..b672ec3e3f 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "replay-internal.h" #include "monitor/hmp.h" #include "monitor/monitor.h" diff --git a/replay/replay-events.c b/replay/replay-events.c index 2e46eda6bf..8959da9f1f 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "block/aio.h" #include "ui/input.h" diff --git a/replay/replay-input.c b/replay/replay-input.c index bee3dbe528..562bbf3717 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "qemu/notify.h" #include "ui/input.h" diff --git a/replay/replay-internal.c b/replay/replay-internal.c index 13fcbdd8f4..c2a7200339 100644 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -10,8 +10,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "replay-internal.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/replay/replay-net.c b/replay/replay-net.c index 3b70f71cf1..d4b197e91e 100644 --- a/replay/replay-net.c +++ b/replay/replay-net.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "net/net.h" #include "net/filter.h" diff --git a/replay/replay-random.c b/replay/replay-random.c index afc7a0fccc..7f4c46f74d 100644 --- a/replay/replay-random.c +++ b/replay/replay-random.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" void replay_save_random(int ret, void *buf, size_t len) diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index ccb4d89dda..7b7b326925 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "monitor/monitor.h" #include "qapi/qmp/qstring.h" diff --git a/replay/replay-time.c b/replay/replay-time.c index ee0ebfcf09..f3d62e1139 100644 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "qemu/error-report.h" diff --git a/replay/replay.c b/replay/replay.c index 895fa6b67a..3adc387b3d 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -11,13 +11,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/cpu-timers.h" +#include "system/replay.h" +#include "system/runstate.h" #include "replay-internal.h" #include "qemu/main-loop.h" #include "qemu/option.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/error-report.h" /* Current version of the replay mechanism. diff --git a/replay/stubs-system.c b/replay/stubs-system.c index 50cefdb2d6..8f2b2d326e 100644 --- a/replay/stubs-system.c +++ b/replay/stubs-system.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "ui/input.h" void replay_input_event(QemuConsole *src, InputEvent *evt) diff --git a/rust/wrapper.h b/rust/wrapper.h index 285d0eb6ad..a9bc67af0d 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -50,7 +50,7 @@ typedef enum memory_order { #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu-io.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/sysbus.h" #include "exec/memory.h" #include "chardev/char-fe.h" diff --git a/scripts/clean-includes b/scripts/clean-includes index bdbf404024..25dbf16c02 100755 --- a/scripts/clean-includes +++ b/scripts/clean-includes @@ -130,8 +130,8 @@ for f in "$@"; do *include/qemu/compiler.h | \ *include/qemu/qemu-plugin.h | \ *include/glib-compat.h | \ - *include/sysemu/os-posix.h | \ - *include/sysemu/os-win32.h | \ + *include/system/os-posix.h | \ + *include/system/os-win32.h | \ *include/standard-headers/ ) # Removing include lines from osdep.h itself would be counterproductive. echo "SKIPPING $f (special case header)" @@ -174,7 +174,7 @@ for f in "$@"; do - "sysemu/os-posix.h, sysemu/os-win32.h "glib-compat.h" + "system/os-posix.h, system/os-win32.h "glib-compat.h" "qemu/typedefs.h" ))' "$f" diff --git a/scripts/codeconverter/codeconverter/test_regexps.py b/scripts/codeconverter/codeconverter/test_regexps.py index a445634d88..7211392796 100644 --- a/scripts/codeconverter/codeconverter/test_regexps.py +++ b/scripts/codeconverter/codeconverter/test_regexps.py @@ -269,7 +269,7 @@ def test_initial_includes(): #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" /* Missing stuff: SCTRL_P[12](END|ST)INC @@ -278,5 +278,5 @@ def test_initial_includes(): m = InitialIncludes.domatch(c) assert m print(repr(m.group(0))) - assert m.group(0).endswith('#include "sysemu/dma.h"\n') + assert m.group(0).endswith('#include "system/dma.h"\n') diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index a58e7414c7..72995903ff 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -76,7 +76,7 @@ chardev ~ .*/qemu((/include)?/chardev/.*) crypto - ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*|/host/include/.*/host/crypto/.*) + ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/system|/backends)/cryptodev.*|/host/include/.*/host/crypto/.*) disas ~ .*/qemu((/include)?/disas.*) @@ -144,7 +144,7 @@ kvm tcg ~ .*/qemu(/accel/tcg|/replay|/tcg)/.* -sysemu +system ~ .*/qemu(/system/.*|/accel/.*) (headers) diff --git a/stats/stats-qmp-cmds.c b/stats/stats-qmp-cmds.c index e214b964fd..884674ee32 100644 --- a/stats/stats-qmp-cmds.c +++ b/stats/stats-qmp-cmds.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/stats.h" +#include "system/stats.h" #include "qapi/qapi-commands-stats.h" #include "qemu/queue.h" #include "qapi/error.h" diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index 0e9354faa6..325966eb9e 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -58,7 +58,7 @@ #include "storage-daemon/qapi/qapi-commands.h" #include "storage-daemon/qapi/qapi-init-commands.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace/control.h" static const char *pid_file; diff --git a/stubs/blk-commit-all.c b/stubs/blk-commit-all.c index e156c57f8d..76b08275a5 100644 --- a/stubs/blk-commit-all.c +++ b/stubs/blk-commit-all.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" int blk_commit_all(void) { diff --git a/stubs/change-state-handler.c b/stubs/change-state-handler.c index d1ed46bfb0..002d248abf 100644 --- a/stubs/change-state-handler.c +++ b/stubs/change-state-handler.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque) diff --git a/stubs/cpu-get-clock.c b/stubs/cpu-get-clock.c index 9e92404816..53b9c83d76 100644 --- a/stubs/cpu-get-clock.c +++ b/stubs/cpu-get-clock.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" int64_t cpu_get_clock(void) diff --git a/stubs/cpu-synchronize-state.c b/stubs/cpu-synchronize-state.c index d9211da66c..2ed09ff3ed 100644 --- a/stubs/cpu-synchronize-state.c +++ b/stubs/cpu-synchronize-state.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" void cpu_synchronize_state(CPUState *cpu) { diff --git a/stubs/cpus-virtual-clock.c b/stubs/cpus-virtual-clock.c index af7c1a1d40..0b83a92533 100644 --- a/stubs/cpus-virtual-clock.c +++ b/stubs/cpus-virtual-clock.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" int64_t cpus_get_virtual_clock(void) diff --git a/stubs/dump.c b/stubs/dump.c index 1f28ec2be3..df7897b72b 100644 --- a/stubs/dump.c +++ b/stubs/dump.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/dump-arch.h" +#include "system/dump-arch.h" int cpu_get_dump_info(ArchDumpInfo *info, const struct GuestPhysBlockList *guest_phys_blocks) diff --git a/stubs/get-vm-name.c b/stubs/get-vm-name.c index 0906303f73..4cfac484e5 100644 --- a/stubs/get-vm-name.c +++ b/stubs/get-vm-name.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" const char *qemu_get_vm_name(void) { diff --git a/stubs/icount.c b/stubs/icount.c index 9f9a59f55b..edbf60cbfa 100644 --- a/stubs/icount.c +++ b/stubs/icount.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" /* icount - Instruction Counter API */ diff --git a/stubs/qemu-timer-notify-cb.c b/stubs/qemu-timer-notify-cb.c index 845e46f8e0..b57b983c6f 100644 --- a/stubs/qemu-timer-notify-cb.c +++ b/stubs/qemu-timer-notify-cb.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" void qemu_timer_notify_cb(void *opaque, QEMUClockType type) diff --git a/stubs/qtest.c b/stubs/qtest.c index 39e376eb67..6c39725927 100644 --- a/stubs/qtest.c +++ b/stubs/qtest.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* Needed for qtest_allowed() */ bool qtest_allowed; diff --git a/stubs/replay-mode.c b/stubs/replay-mode.c index 264be9d96c..439d97e4a8 100644 --- a/stubs/replay-mode.c +++ b/stubs/replay-mode.c @@ -1,4 +1,4 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" ReplayMode replay_mode; diff --git a/stubs/replay-tools.c b/stubs/replay-tools.c index 3e8ca3212d..c537485f40 100644 --- a/stubs/replay-tools.c +++ b/stubs/replay-tools.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "block/aio.h" bool replay_events_enabled(void) diff --git a/stubs/runstate-check.c b/stubs/runstate-check.c index 2ccda2b70f..c47abdf84b 100644 --- a/stubs/runstate-check.c +++ b/stubs/runstate-check.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" bool runstate_check(RunState state) { return state == RUN_STATE_PRELAUNCH; diff --git a/stubs/vm-stop.c b/stubs/vm-stop.c index 7f8a9da8a5..e139aabf7b 100644 --- a/stubs/vm-stop.c +++ b/stubs/vm-stop.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" void qemu_system_vmstop_request_prepare(void) { abort(); diff --git a/system/arch_init.c b/system/arch_init.c index 79716f959b..d2c32f8488 100644 --- a/system/arch_init.c +++ b/system/arch_init.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" #include "qemu/module.h" -#include "sysemu/arch_init.h" +#include "system/arch_init.h" #ifdef TARGET_SPARC int graphic_width = 1024; diff --git a/system/balloon.c b/system/balloon.c index fda7af832e..311fa5058b 100644 --- a/system/balloon.c +++ b/system/balloon.c @@ -26,8 +26,8 @@ #include "qemu/osdep.h" #include "qemu/atomic.h" -#include "sysemu/kvm.h" -#include "sysemu/balloon.h" +#include "system/kvm.h" +#include "system/balloon.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qmp/qerror.h" diff --git a/system/bootdevice.c b/system/bootdevice.c index 2579b26dc8..1845be4507 100644 --- a/system/bootdevice.c +++ b/system/bootdevice.c @@ -24,10 +24,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/visitor.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/qdev-core.h" #include "hw/boards.h" diff --git a/system/cpu-timers.c b/system/cpu-timers.c index 856e502e34..23dd82b465 100644 --- a/system/cpu-timers.c +++ b/system/cpu-timers.c @@ -27,15 +27,15 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/main-loop.h" #include "qemu/option.h" #include "qemu/seqlock.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "hw/core/cpu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/cpu-timers-internal.h" +#include "system/cpu-timers.h" +#include "system/cpu-timers-internal.h" /* clock and ticks */ diff --git a/system/cpus.c b/system/cpus.c index ba633c7688..3460a80482 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -31,18 +31,18 @@ #include "qapi/qapi-events-run-state.h" #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "exec/cpu-common.h" #include "qemu/thread.h" #include "qemu/main-loop.h" #include "qemu/plugin.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #include "hw/nmi.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/whpx.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "system/cpu-timers.h" +#include "system/whpx.h" #include "hw/boards.h" #include "hw/hw.h" #include "trace.h" diff --git a/system/device_tree.c b/system/device_tree.c index 2e38259d34..11f3178095 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -23,7 +23,7 @@ #include "qemu/bswap.h" #include "qemu/cutils.h" #include "qemu/guest-random.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/loader.h" #include "hw/boards.h" #include "qemu/config-file.h" diff --git a/system/dirtylimit.c b/system/dirtylimit.c index ab20da34bb..d94b994bd8 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -15,14 +15,14 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qmp/qdict.h" #include "qapi/error.h" -#include "sysemu/dirtyrate.h" -#include "sysemu/dirtylimit.h" +#include "system/dirtyrate.h" +#include "system/dirtylimit.h" #include "monitor/hmp.h" #include "monitor/monitor.h" #include "exec/memory.h" #include "exec/target_page.h" #include "hw/boards.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "trace.h" #include "migration/misc.h" diff --git a/system/dma-helpers.c b/system/dma-helpers.c index cbcd89dfaa..f6403242f5 100644 --- a/system/dma-helpers.c +++ b/system/dma-helpers.c @@ -8,12 +8,12 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/dma.h" #include "trace.h" #include "qemu/thread.h" #include "qemu/main-loop.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/range.h" /* #define DEBUG_IOMMU */ diff --git a/system/globals.c b/system/globals.c index 84ce943ac9..4867c93ca6 100644 --- a/system/globals.c +++ b/system/globals.c @@ -28,8 +28,8 @@ #include "hw/loader.h" #include "hw/xen/xen.h" #include "net/net.h" -#include "sysemu/cpus.h" -#include "sysemu/sysemu.h" +#include "system/cpus.h" +#include "system/system.h" enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; int display_opengl; diff --git a/system/main.c b/system/main.c index 9b91d21ea8..4923520741 100644 --- a/system/main.c +++ b/system/main.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qemu-main.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #ifdef CONFIG_SDL #include diff --git a/system/memory.c b/system/memory.c index a789064fbf..78e17e0efa 100644 --- a/system/memory.c +++ b/system/memory.c @@ -27,9 +27,9 @@ #include "exec/memory-internal.h" #include "exec/ram_addr.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/tcg.h" #include "qemu/accel.h" #include "hw/boards.h" #include "migration/vmstate.h" diff --git a/system/memory_mapping.c b/system/memory_mapping.c index ca2390eb80..37d3325f77 100644 --- a/system/memory_mapping.c +++ b/system/memory_mapping.c @@ -15,7 +15,7 @@ #include "qemu/range.h" #include "qapi/error.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" #include "exec/memory.h" #include "exec/address-spaces.h" #include "hw/core/cpu.h" diff --git a/system/physmem.c b/system/physmem.c index 4bc0228a50..c0e95e6f7c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -37,10 +37,10 @@ #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "sysemu/xen.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" +#include "system/xen.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/qtest.h" #include "qemu/timer.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -49,10 +49,10 @@ #include "qemu/memalign.h" #include "exec/memory.h" #include "exec/ioport.h" -#include "sysemu/dma.h" -#include "sysemu/hostmem.h" -#include "sysemu/hw_accel.h" -#include "sysemu/xen-mapcache.h" +#include "system/dma.h" +#include "system/hostmem.h" +#include "system/hw_accel.h" +#include "system/xen-mapcache.h" #include "trace.h" #ifdef CONFIG_FALLOCATE_PUNCH_HOLE @@ -62,7 +62,7 @@ #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" #include "exec/translate-all.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 03ae610649..efa284750e 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -22,8 +22,8 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "monitor/qdev.h" -#include "sysemu/arch_init.h" -#include "sysemu/runstate.h" +#include "system/arch_init.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" #include "qapi/qmp/dispatch.h" @@ -37,7 +37,7 @@ #include "qemu/option.h" #include "qemu/qemu-print.h" #include "qemu/option_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "migration/misc.h" #include "qemu/cutils.h" #include "hw/qdev-properties.h" diff --git a/system/qemu-seccomp.c b/system/qemu-seccomp.c index 71ac444802..f8e1238b91 100644 --- a/system/qemu-seccomp.c +++ b/system/qemu-seccomp.c @@ -20,7 +20,7 @@ #include "qemu/module.h" #include #include -#include "sysemu/seccomp.h" +#include "system/seccomp.h" #include /* For some architectures (notably ARM) cacheflush is not supported until diff --git a/system/qtest.c b/system/qtest.c index 12703a2045..99ef2042f6 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -13,8 +13,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" +#include "system/qtest.h" +#include "system/runstate.h" #include "chardev/char-fe.h" #include "exec/ioport.h" #include "exec/memory.h" @@ -23,7 +23,7 @@ #include "hw/irq.h" #include "hw/core/cpu.h" #include "qemu/accel.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/error-report.h" diff --git a/system/rtc.c b/system/rtc.c index 216d2aee3a..56951288c4 100644 --- a/system/rtc.c +++ b/system/rtc.c @@ -29,9 +29,9 @@ #include "qemu/option.h" #include "qemu/timer.h" #include "qom/object.h" -#include "sysemu/replay.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/replay.h" +#include "system/system.h" +#include "system/rtc.h" #include "hw/rtc/mc146818rtc.h" static enum { diff --git a/system/runstate-action.c b/system/runstate-action.c index ae0761a9c3..f912bc837f 100644 --- a/system/runstate-action.c +++ b/system/runstate-action.c @@ -7,8 +7,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/runstate-action.h" -#include "sysemu/watchdog.h" +#include "system/runstate-action.h" +#include "system/watchdog.h" #include "qemu/config-file.h" #include "qapi/error.h" #include "qemu/option_int.h" diff --git a/system/runstate.c b/system/runstate.c index c2c9afa905..3a8fe866bc 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -51,14 +51,14 @@ #include "qemu/thread.h" #include "qom/object.h" #include "qom/object_interfaces.h" -#include "sysemu/cpus.h" -#include "sysemu/qtest.h" -#include "sysemu/replay.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/sysemu.h" -#include "sysemu/tpm.h" +#include "system/cpus.h" +#include "system/qtest.h" +#include "system/replay.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/system.h" +#include "system/tpm.h" #include "trace.h" static NotifierList exit_notifiers = diff --git a/system/tpm.c b/system/tpm.c index 7164ea7ff1..8df0f6e72b 100644 --- a/system/tpm.c +++ b/system/tpm.c @@ -17,8 +17,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-tpm.h" #include "qapi/qmp/qerror.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm.h" +#include "system/tpm_backend.h" +#include "system/tpm.h" #include "qemu/config-file.h" #include "qemu/error-report.h" diff --git a/system/vl.c b/system/vl.c index 85fcc8f96e..91d6d4f7f7 100644 --- a/system/vl.c +++ b/system/vl.c @@ -39,12 +39,12 @@ #include "qemu/help_option.h" #include "qemu/hw-version.h" #include "qemu/uuid.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/seccomp.h" -#include "sysemu/tcg.h" -#include "sysemu/xen.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/seccomp.h" +#include "system/tcg.h" +#include "system/xen.h" #include "qemu/error-report.h" #include "qemu/sockets.h" @@ -64,30 +64,30 @@ #include "monitor/monitor.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/sysemu.h" -#include "sysemu/numa.h" -#include "sysemu/hostmem.h" +#include "system/system.h" +#include "system/numa.h" +#include "system/hostmem.h" #include "exec/gdbstub.h" #include "gdbstub/enums.h" #include "qemu/timer.h" #include "chardev/char.h" #include "qemu/bitmap.h" #include "qemu/log.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/block/block.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "migration/misc.h" #include "migration/snapshot.h" -#include "sysemu/tpm.h" -#include "sysemu/dma.h" +#include "system/tpm.h" +#include "system/dma.h" #include "hw/audio/soundhw.h" #include "audio/audio.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" +#include "system/cpus.h" +#include "system/cpu-timers.h" #include "migration/colo.h" #include "migration/postcopy-ram.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qapi/qobject-input-visitor.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -95,7 +95,7 @@ #ifdef CONFIG_VIRTFS #include "fsdev/qemu-fsdev.h" #endif -#include "sysemu/qtest.h" +#include "system/qtest.h" #ifdef CONFIG_TCG #include "tcg/perf.h" #endif @@ -106,7 +106,7 @@ #include "trace/control.h" #include "qemu/plugin.h" #include "qemu/queue.h" -#include "sysemu/arch_init.h" +#include "system/arch_init.h" #include "exec/confidential-guest-support.h" #include "ui/qemu-spice.h" @@ -116,7 +116,7 @@ #include "qom/object_interfaces.h" #include "semihosting/semihost.h" #include "crypto/init.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/qapi-events-run-state.h" #include "qapi/qapi-types-audio.h" #include "qapi/qapi-visit-audio.h" @@ -131,7 +131,7 @@ #include "qapi/qapi-commands-ui.h" #include "block/qdict.h" #include "qapi/qmp/qerror.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "qemu/guest-random.h" #include "qemu/keyval.h" diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c index 768116ef32..54ee93f34c 100644 --- a/target/alpha/sys_helper.c +++ b/target/alpha/sys_helper.c @@ -22,8 +22,8 @@ #include "exec/exec-all.h" #include "exec/tb-flush.h" #include "exec/helper-proto.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "qemu/timer.h" diff --git a/target/alpha/translate.c b/target/alpha/translate.c index fb6cac4b53..660788d5c3 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index 06cdf4ba28..5c943dc27b 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "cpu-features.h" /* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index 2b2055c6ac..20c70c7d6b 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -15,7 +15,7 @@ #include "arm-powerctl.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "target/arm/multiprocessing.h" #ifndef DEBUG_ARM_POWERCTL diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1afa07511e..b085c068ad 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -41,9 +41,9 @@ #include "hw/intc/armv7m_nvic.h" #endif /* CONFIG_TCG */ #endif /* !CONFIG_USER_ONLY */ -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" -#include "sysemu/hw_accel.h" +#include "system/tcg.h" +#include "system/qtest.h" +#include "system/hw_accel.h" #include "kvm_arm.h" #include "disas/capstone.h" #include "fpu/softfloat.h" diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index ec77c5b34a..dca83e4518 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -24,10 +24,10 @@ #include "cpregs.h" #include "qemu/module.h" #include "qemu/units.h" -#include "sysemu/kvm.h" -#include "sysemu/hvf.h" -#include "sysemu/qtest.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/hvf.h" +#include "system/qtest.h" +#include "system/tcg.h" #include "kvm_arm.h" #include "hvf_arm.h" #include "qapi/visitor.h" diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 7d856acddf..2212ef4a3b 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -13,7 +13,7 @@ #include "cpregs.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #ifdef CONFIG_TCG /* Return the Exception Level targeted by debug exceptions. */ diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 554b8736bb..30068c2262 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -22,7 +22,7 @@ #include "exec/gdbstub.h" #include "gdbstub/helpers.h" #include "gdbstub/commands.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "internals.h" #include "cpu-features.h" #include "cpregs.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index c2a70f8c05..146ddf06c5 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -21,9 +21,9 @@ #include "exec/exec-all.h" #include /* for crc32 */ #include "hw/irq.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/cpu-timers.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "qapi/error.h" #include "qemu/guest-random.h" #ifdef CONFIG_TCG diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index d75e504dcd..0afd96018e 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -13,10 +13,10 @@ #include "qemu/error-report.h" #include "qemu/log.h" -#include "sysemu/runstate.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" -#include "sysemu/hw_accel.h" +#include "system/runstate.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "system/hw_accel.h" #include "hvf_arm.h" #include "cpregs.h" @@ -26,7 +26,7 @@ #include "hw/boards.h" #include "hw/irq.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "arm-powerctl.h" #include "target/arm/cpu.h" #include "target/arm/internals.h" diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 7b6812c0de..a9444a2c7a 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -20,10 +20,10 @@ #include "qemu/main-loop.h" #include "qom/object.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" +#include "system/system.h" +#include "system/runstate.h" +#include "system/kvm.h" +#include "system/kvm_int.h" #include "kvm_arm.h" #include "cpu.h" #include "trace.h" diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 2e6b49bf13..05c3de8cd4 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -11,7 +11,7 @@ #ifndef QEMU_KVM_ARM_H #define QEMU_KVM_ARM_H -#include "sysemu/kvm.h" +#include "system/kvm.h" #define KVM_ARM_VGIC_V2 (1 << 0) #define KVM_ARM_VGIC_V3 (1 << 1) diff --git a/target/arm/machine.c b/target/arm/machine.c index a3c1e05e65..978249fb71 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "kvm_arm.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/arm/tcg/psci.c b/target/arm/tcg/psci.c index 51d2ca3d30..cabed43e8a 100644 --- a/target/arm/tcg/psci.c +++ b/target/arm/tcg/psci.c @@ -21,7 +21,7 @@ #include "exec/helper-proto.h" #include "kvm-consts.h" #include "qemu/main-loop.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "internals.h" #include "arm-powerctl.h" #include "target/arm/multiprocessing.h" diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 9b43b556fd..da5b569de8 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -23,8 +23,8 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "chardev/char-fe.h" void HELPER(write_interval_timer)(CPUHPPAState *env, target_ulong val) diff --git a/target/i386/arch_dump.c b/target/i386/arch_dump.c index c290910a04..16e47c4747 100644 --- a/target/i386/arch_dump.c +++ b/target/i386/arch_dump.c @@ -13,9 +13,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "elf.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" #define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ ((DIV_ROUND_UP((hdr_size), 4) \ diff --git a/target/i386/arch_memory_mapping.c b/target/i386/arch_memory_mapping.c index d1ff659128..ced199862d 100644 --- a/target/i386/arch_memory_mapping.c +++ b/target/i386/arch_memory_mapping.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" /* PAE Paging or IA-32e Paging */ static void walk_pte(MemoryMappingList *list, AddressSpace *as, diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c index d397ec94dc..dc844cae8b 100644 --- a/target/i386/cpu-apic.c +++ b/target/i386/cpu-apic.c @@ -11,9 +11,9 @@ #include "qapi/error.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" -#include "sysemu/xen.h" +#include "system/hw_accel.h" +#include "system/kvm.h" +#include "system/xen.h" #include "exec/address-spaces.h" #include "hw/qdev-properties.h" #include "hw/i386/apic_internal.h" diff --git a/target/i386/cpu-internal.h b/target/i386/cpu-internal.h index 9baac5c0b4..37c61a1bc3 100644 --- a/target/i386/cpu-internal.h +++ b/target/i386/cpu-internal.h @@ -1,5 +1,5 @@ /* - * i386 CPU internal definitions to be shared between cpu.c and cpu-sysemu.c + * i386 CPU internal definitions to be shared between cpu.c and cpu-system.c * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/cpu-sysemu.c b/target/i386/cpu-system.c similarity index 99% rename from target/i386/cpu-sysemu.c rename to target/i386/cpu-system.c index 227ac021f6..9d007afdab 100644 --- a/target/i386/cpu-sysemu.c +++ b/target/i386/cpu-system.c @@ -1,5 +1,5 @@ /* - * i386 CPUID, CPU class, definitions, models: sysemu-only code + * i386 CPUID, CPU class, definitions, models: system-only code * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5253399459..96a2608e99 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -24,7 +24,7 @@ #include "qemu/hw-version.h" #include "cpu.h" #include "tcg/helper-tcg.h" -#include "sysemu/hvf.h" +#include "system/hvf.h" #include "hvf/hvf-i386.h" #include "kvm/kvm_i386.h" #include "sev.h" @@ -35,7 +35,7 @@ #include "hw/qdev-properties.h" #include "hw/i386/topology.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/qapi-commands-machine-target.h" #include "exec/address-spaces.h" #include "hw/boards.h" diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 4c239a6970..dbd8f1ffc7 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -20,7 +20,7 @@ #ifndef I386_CPU_H #define I386_CPU_H -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "cpu-qom.h" #include "kvm/hyperv-proto.h" #include "exec/cpu-defs.h" diff --git a/target/i386/helper.c b/target/i386/helper.c index 01a268a30b..a78d06c95b 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -21,9 +21,9 @@ #include "qapi/qapi-events-run-state.h" #include "cpu.h" #include "exec/exec-all.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "monitor/monitor.h" #include "kvm/kvm_i386.h" #endif diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 03b9d1b169..3e4e85e729 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -12,7 +12,7 @@ #include "host-cpu.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Note: Only safe for use on x86(-64) hosts */ static uint32_t host_cpu_phys_bits(void) diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index ac617f17e7..560b5a0594 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -11,9 +11,9 @@ #include "cpu.h" #include "host-cpu.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" -#include "sysemu/hvf.h" +#include "system/hvf.h" #include "hw/core/accel-cpu.h" #include "hvf-i386.h" diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index c5d025d557..ca08f0753f 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -52,10 +52,10 @@ #include "qapi/error.h" #include "migration/blocker.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "system/runstate.h" +#include "system/cpus.h" #include "hvf-i386.h" #include "vmcs.h" #include "vmx.h" diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 3954ef883d..80ce26279b 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -30,8 +30,8 @@ #include "vmcs.h" #include "cpu.h" #include "x86.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" +#include "system/hvf.h" +#include "system/hvf_int.h" #include "exec/address-spaces.h" diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index af9ee17a11..ae836f65cc 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -26,7 +26,7 @@ #include "cpu.h" #include "x86.h" #include "vmx.h" -#include "sysemu/hvf.h" +#include "system/hvf.h" #include "hvf-i386.h" static bool cached_xcr0; diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index cdea2ea69d..bcd844cff6 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -10,7 +10,7 @@ #include "panic.h" #include "qemu/error-report.h" -#include "sysemu/hvf.h" +#include "system/hvf.h" #include "hvf-i386.h" #include "vmcs.h" #include "vmx.h" diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 1569f860eb..531a340b37 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -25,7 +25,7 @@ #include "cpu.h" #include "x86_descr.h" #include "x86_decode.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "hw/i386/apic_internal.h" diff --git a/target/i386/kvm/hyperv.h b/target/i386/kvm/hyperv.h index e3982c8f4d..e45a4512fe 100644 --- a/target/i386/kvm/hyperv.h +++ b/target/i386/kvm/hyperv.h @@ -15,7 +15,7 @@ #define TARGET_I386_HYPERV_H #include "cpu.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/hyperv/hyperv.h" #ifdef CONFIG_KVM diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 99d1941cf5..1bda403f88 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -11,7 +11,7 @@ #include "cpu.h" #include "host-cpu.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "kvm_i386.h" diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 8e17942c3b..80fa3bac11 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -31,10 +31,10 @@ #include "cpu.h" #include "host-cpu.h" #include "vmsr_energy.h" -#include "sysemu/sysemu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/hw_accel.h" +#include "system/kvm_int.h" +#include "system/runstate.h" #include "kvm_i386.h" #include "../confidential-guest.h" #include "sev.h" diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 7edb154a16..88565e8dba 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -11,7 +11,7 @@ #ifndef QEMU_KVM_I386_H #define QEMU_KVM_I386_H -#include "sysemu/kvm.h" +#include "system/kvm.h" /* always false if !CONFIG_KVM */ #define kvm_pit_in_kernel() \ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 2f89dc628e..e81a245881 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -14,13 +14,13 @@ #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "hw/xen/xen.h" -#include "sysemu/kvm_int.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm_int.h" +#include "system/kvm_xen.h" #include "kvm/kvm_i386.h" #include "exec/address-spaces.h" #include "xen-emu.h" #include "trace.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/pci/msi.h" #include "hw/i386/apic-msidef.h" diff --git a/target/i386/machine.c b/target/i386/machine.c index b4610325aa..d9d4f25d1a 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -8,9 +8,9 @@ #include "kvm/kvm_i386.h" #include "hw/xen/xen.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" +#include "system/tcg.h" #include "qemu/error-report.h" diff --git a/target/i386/meson.build b/target/i386/meson.build index 075117989b..2e9c472f49 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -19,9 +19,10 @@ i386_system_ss.add(files( 'machine.c', 'monitor.c', 'cpu-apic.c', - 'cpu-sysemu.c', + 'cpu-system.c', )) -i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) +i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), + if_false: files('sev-system-stub.c')) i386_user_ss = ss.source_set() diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 0ba31201e2..e7b56662fe 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -8,12 +8,12 @@ */ #include "qemu/osdep.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/guest-random.h" -#include "sysemu/nvmm.h" +#include "system/nvmm.h" #include "nvmm-accel-ops.h" static void *qemu_nvmm_cpu_thread_fn(void *arg) diff --git a/target/i386/nvmm/nvmm-accel-ops.h b/target/i386/nvmm/nvmm-accel-ops.h index 7c5461bd75..931bb5ca24 100644 --- a/target/i386/nvmm/nvmm-accel-ops.h +++ b/target/i386/nvmm/nvmm-accel-ops.h @@ -10,7 +10,7 @@ #ifndef TARGET_I386_NVMM_ACCEL_OPS_H #define TARGET_I386_NVMM_ACCEL_OPS_H -#include "sysemu/cpus.h" +#include "system/cpus.h" int nvmm_init_vcpu(CPUState *cpu); int nvmm_vcpu_exec(CPUState *cpu); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 65768aca03..04e5f7e637 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -12,9 +12,9 @@ #include "exec/address-spaces.h" #include "exec/ioport.h" #include "qemu/accel.h" -#include "sysemu/nvmm.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" +#include "system/nvmm.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-system-stub.c similarity index 100% rename from target/i386/sev-sysemu-stub.c rename to target/i386/sev-system-stub.c diff --git a/target/i386/sev.c b/target/i386/sev.c index 1a4eb1ada6..0e1dbb6959 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -26,11 +26,11 @@ #include "qemu/uuid.h" #include "qemu/error-report.h" #include "crypto/hash.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm/kvm_i386.h" #include "sev.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "trace.h" #include "migration/blocker.h" #include "qom/object.h" diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c index 72387aac24..de71e68f98 100644 --- a/target/i386/tcg/excp_helper.c +++ b/target/i386/tcg/excp_helper.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "qemu/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "exec/helper-proto.h" #include "helper-tcg.h" diff --git a/target/i386/tcg/meson.build b/target/i386/tcg/meson.build index 1105b35d92..c57e661752 100644 --- a/target/i386/tcg/meson.build +++ b/target/i386/tcg/meson.build @@ -12,5 +12,5 @@ i386_ss.add(when: 'CONFIG_TCG', if_true: files( 'tcg-cpu.c', 'translate.c'), if_false: files('tcg-stub.c')) -subdir('sysemu') +subdir('system') subdir('user') diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/system/bpt_helper.c similarity index 99% rename from target/i386/tcg/sysemu/bpt_helper.c rename to target/i386/tcg/system/bpt_helper.c index b29acf41c3..be232c1ca9 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/system/bpt_helper.c @@ -1,5 +1,5 @@ /* - * i386 breakpoint helpers - sysemu code + * i386 breakpoint helpers - system code * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/system/excp_helper.c similarity index 99% rename from target/i386/tcg/sysemu/excp_helper.c rename to target/i386/tcg/system/excp_helper.c index b1f40040f8..864e3140e3 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -1,5 +1,5 @@ /* - * x86 exception helpers - sysemu code + * x86 exception helpers - system code * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/fpu_helper.c b/target/i386/tcg/system/fpu_helper.c similarity index 99% rename from target/i386/tcg/sysemu/fpu_helper.c rename to target/i386/tcg/system/fpu_helper.c index e0305ba234..0b4fa187df 100644 --- a/target/i386/tcg/sysemu/fpu_helper.c +++ b/target/i386/tcg/system/fpu_helper.c @@ -1,5 +1,5 @@ /* - * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers (sysemu code) + * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers (system code) * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/meson.build b/target/i386/tcg/system/meson.build similarity index 100% rename from target/i386/tcg/sysemu/meson.build rename to target/i386/tcg/system/meson.build diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/system/misc_helper.c similarity index 99% rename from target/i386/tcg/sysemu/misc_helper.c rename to target/i386/tcg/system/misc_helper.c index 094aa56a20..ffed8a3215 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -1,5 +1,5 @@ /* - * x86 misc helpers - sysemu code + * x86 misc helpers - system code * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/seg_helper.c b/target/i386/tcg/system/seg_helper.c similarity index 99% rename from target/i386/tcg/sysemu/seg_helper.c rename to target/i386/tcg/system/seg_helper.c index 05174a79e7..b07cc9f9b1 100644 --- a/target/i386/tcg/sysemu/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -1,5 +1,5 @@ /* - * x86 segmentation related helpers: (sysemu-only code) + * x86 segmentation related helpers: (system-only code) * TSS, interrupts, system calls, jumps and call/task gates, descriptors * * Copyright (c) 2003 Fabrice Bellard diff --git a/target/i386/tcg/sysemu/smm_helper.c b/target/i386/tcg/system/smm_helper.c similarity index 99% rename from target/i386/tcg/sysemu/smm_helper.c rename to target/i386/tcg/system/smm_helper.c index a45b5651c3..251eb7856c 100644 --- a/target/i386/tcg/sysemu/smm_helper.c +++ b/target/i386/tcg/system/smm_helper.c @@ -1,5 +1,5 @@ /* - * x86 SMM helpers (sysemu-only) + * x86 SMM helpers (system-only) * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/system/svm_helper.c similarity index 99% rename from target/i386/tcg/sysemu/svm_helper.c rename to target/i386/tcg/system/svm_helper.c index 9db8ad62a0..5f95b5227b 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -1,5 +1,5 @@ /* - * x86 SVM helpers (sysemu only) + * x86 SVM helpers (system only) * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/tcg-cpu.c b/target/i386/tcg/system/tcg-cpu.c similarity index 96% rename from target/i386/tcg/sysemu/tcg-cpu.c rename to target/i386/tcg/system/tcg-cpu.c index c223c0fe9b..13a3507863 100644 --- a/target/i386/tcg/sysemu/tcg-cpu.c +++ b/target/i386/tcg/system/tcg-cpu.c @@ -1,5 +1,5 @@ /* - * i386 TCG cpu class initialization functions specific to sysemu + * i386 TCG cpu class initialization functions specific to system emulation * * Copyright (c) 2003 Fabrice Bellard * @@ -21,7 +21,7 @@ #include "cpu.h" #include "tcg/helper-tcg.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/units.h" #include "exec/address-spaces.h" diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5d729e68c9..ea7995106f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -228,7 +228,7 @@ typedef struct DisasContext { #endif /* - * Many sysemu-only helpers are not reachable for user-only. + * Many system-only helpers are not reachable for user-only. * Define stub generators here, so that we need not either sprinkle * ifdefs through the translator, nor provide the helper function. */ diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 1a2b4e1c43..ab2e014c9e 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -9,12 +9,12 @@ */ #include "qemu/osdep.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/guest-random.h" -#include "sysemu/whpx.h" +#include "system/whpx.h" #include "whpx-internal.h" #include "whpx-accel-ops.h" diff --git a/target/i386/whpx/whpx-accel-ops.h b/target/i386/whpx/whpx-accel-ops.h index 7a1bb1ab57..e6cf15511d 100644 --- a/target/i386/whpx/whpx-accel-ops.h +++ b/target/i386/whpx/whpx-accel-ops.h @@ -10,7 +10,7 @@ #ifndef TARGET_I386_WHPX_ACCEL_OPS_H #define TARGET_I386_WHPX_ACCEL_OPS_H -#include "sysemu/cpus.h" +#include "system/cpus.h" int whpx_init_vcpu(CPUState *cpu); int whpx_vcpu_exec(CPUState *cpu); diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index a6674a826d..41fb8c5a4e 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -14,9 +14,9 @@ #include "exec/ioport.h" #include "gdbstub/helpers.h" #include "qemu/accel.h" -#include "sysemu/whpx.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" +#include "system/whpx.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "hw/boards.h" #include "hw/intc/ioapic.h" diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index 7e14ded978..4245ab68a2 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -16,8 +16,8 @@ #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" #include "hw/pci/msi.h" -#include "sysemu/hw_accel.h" -#include "sysemu/whpx.h" +#include "system/hw_accel.h" +#include "system/whpx.h" #include "whpx-internal.h" struct whpx_lapic_state { diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c index d9e1120333..2b0955a209 100644 --- a/target/loongarch/arch_dump.c +++ b/target/loongarch/arch_dump.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "internals.h" /* struct user_pt_regs from arch/loongarch/include/uapi/asm/ptrace.h */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 57cc4f314b..078766feaf 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -10,9 +10,9 @@ #include "qemu/qemu-print.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/qtest.h" -#include "sysemu/tcg.h" -#include "sysemu/kvm.h" +#include "system/qtest.h" +#include "system/tcg.h" +#include "system/kvm.h" #include "kvm/kvm_loongarch.h" #include "exec/exec-all.h" #include "cpu.h" @@ -20,7 +20,7 @@ #include "fpu/softfloat-helpers.h" #include "cpu-csr.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/reset.h" +#include "system/reset.h" #endif #include "vec.h" #ifdef CONFIG_KVM diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index ff81806ca3..3c86f5ffb9 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -13,9 +13,9 @@ #include "qemu/timer.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/kvm_int.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" @@ -23,7 +23,7 @@ #include "hw/irq.h" #include "qemu/log.h" #include "hw/loader.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "cpu-csr.h" #include "kvm_loongarch.h" #include "trace.h" diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c index efb20e2fbe..4e70f5c879 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "migration/cpu.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "vec.h" static const VMStateDescription vmstate_fpu_reg = { diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 02c0e1b0f9..441067060f 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -27,8 +27,8 @@ #include "internal.h" #include "kvm_mips.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" +#include "system/kvm.h" +#include "system/qtest.h" #include "exec/exec-all.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" diff --git a/target/mips/helper.h b/target/mips/helper.h index 0f8462febb..7e40041828 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -594,7 +594,7 @@ DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env) DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env) #ifndef CONFIG_USER_ONLY -#include "tcg/sysemu_helper.h.inc" +#include "tcg/system_helper.h.inc" #endif /* !CONFIG_USER_ONLY */ #include "tcg/msa_helper.h.inc" diff --git a/target/mips/kvm.c b/target/mips/kvm.c index a98798c669..d67b7c1a8e 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -18,9 +18,9 @@ #include "internal.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/kvm_int.h" +#include "system/runstate.h" #include "kvm_mips.h" #include "hw/boards.h" #include "fpu_helper.h" diff --git a/target/mips/meson.build b/target/mips/meson.build index a26d1e1f79..247979a2cf 100644 --- a/target/mips/meson.build +++ b/target/mips/meson.build @@ -9,7 +9,7 @@ mips_ss.add(files( )) if have_system - subdir('sysemu') + subdir('system') endif if 'CONFIG_TCG' in config_all_accel diff --git a/target/mips/sysemu/addr.c b/target/mips/system/addr.c similarity index 100% rename from target/mips/sysemu/addr.c rename to target/mips/system/addr.c diff --git a/target/mips/sysemu/cp0.c b/target/mips/system/cp0.c similarity index 100% rename from target/mips/sysemu/cp0.c rename to target/mips/system/cp0.c diff --git a/target/mips/sysemu/cp0_timer.c b/target/mips/system/cp0_timer.c similarity index 99% rename from target/mips/sysemu/cp0_timer.c rename to target/mips/system/cp0_timer.c index 62de502caa..ca16945cee 100644 --- a/target/mips/sysemu/cp0_timer.c +++ b/target/mips/system/cp0_timer.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" #include "qemu/timer.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "internal.h" /* MIPS R4K timer */ diff --git a/target/mips/sysemu/machine.c b/target/mips/system/machine.c similarity index 100% rename from target/mips/sysemu/machine.c rename to target/mips/system/machine.c diff --git a/target/mips/sysemu/meson.build b/target/mips/system/meson.build similarity index 100% rename from target/mips/sysemu/meson.build rename to target/mips/system/meson.build diff --git a/target/mips/sysemu/mips-qmp-cmds.c b/target/mips/system/mips-qmp-cmds.c similarity index 100% rename from target/mips/sysemu/mips-qmp-cmds.c rename to target/mips/system/mips-qmp-cmds.c diff --git a/target/mips/sysemu/physaddr.c b/target/mips/system/physaddr.c similarity index 100% rename from target/mips/sysemu/physaddr.c rename to target/mips/system/physaddr.c diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index 7b18e6c4c8..fff9cd6c7f 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -36,5 +36,5 @@ mips_ss.add(when: 'TARGET_MIPS64', if_true: files( )) if have_system - subdir('sysemu') + subdir('system') endif diff --git a/target/mips/tcg/sysemu/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c similarity index 100% rename from target/mips/tcg/sysemu/cp0_helper.c rename to target/mips/tcg/system/cp0_helper.c diff --git a/target/mips/tcg/sysemu/lcsr_helper.c b/target/mips/tcg/system/lcsr_helper.c similarity index 100% rename from target/mips/tcg/sysemu/lcsr_helper.c rename to target/mips/tcg/system/lcsr_helper.c diff --git a/target/mips/tcg/sysemu/meson.build b/target/mips/tcg/system/meson.build similarity index 100% rename from target/mips/tcg/sysemu/meson.build rename to target/mips/tcg/system/meson.build diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/system/mips-semi.c similarity index 100% rename from target/mips/tcg/sysemu/mips-semi.c rename to target/mips/tcg/system/mips-semi.c diff --git a/target/mips/tcg/sysemu/semihosting-stub.c b/target/mips/tcg/system/semihosting-stub.c similarity index 100% rename from target/mips/tcg/sysemu/semihosting-stub.c rename to target/mips/tcg/system/semihosting-stub.c diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/system/special_helper.c similarity index 100% rename from target/mips/tcg/sysemu/special_helper.c rename to target/mips/tcg/system/special_helper.c diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c similarity index 100% rename from target/mips/tcg/sysemu/tlb_helper.c rename to target/mips/tcg/system/tlb_helper.c diff --git a/target/mips/tcg/sysemu_helper.h.inc b/target/mips/tcg/system_helper.h.inc similarity index 99% rename from target/mips/tcg/sysemu_helper.h.inc rename to target/mips/tcg/system_helper.h.inc index 1861d538de..eaac5e2c6f 100644 --- a/target/mips/tcg/sysemu_helper.h.inc +++ b/target/mips/tcg/system_helper.h.inc @@ -1,5 +1,5 @@ /* - * QEMU MIPS sysemu helpers + * QEMU MIPS TCG system helpers * * Copyright (c) 2004-2005 Jocelyn Mayer * Copyright (c) 2006 Marius Groeger (FPU operations) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index de7045874d..bd1ef4e1fc 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -37,7 +37,7 @@ /* - * Many sysemu-only helpers are not reachable for user-only. + * Many system-only helpers are not reachable for user-only. * Define stub generators here, so that we need not either sprinkle * ifdefs through the translator, nor provide the helper function. */ diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index f45474133a..80ac6c3e32 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -15,8 +15,8 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" -#include "sysemu/kvm.h" +#include "system/dump.h" +#include "system/kvm.h" #ifdef TARGET_PPC64 #define ELFCLASS ELFCLASS64 diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 0cec1bde91..55de3bd5d5 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -18,10 +18,10 @@ */ #include "qemu/osdep.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" +#include "system/hw_accel.h" +#include "system/kvm.h" #include "kvm_ppc.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/visitor.h" diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index e3ad8e0c27..d148cd76b4 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -25,7 +25,7 @@ #include "fpu/softfloat-helpers.h" #include "mmu-hash64.h" #include "helper_regs.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" target_ulong cpu_read_xer(const CPUPPCState *env) { diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1253dbf622..c04ecafef9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -22,9 +22,9 @@ #include "qemu/osdep.h" #include "disas/dis-asm.h" #include "gdbstub/helpers.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" -#include "sysemu/tcg.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include "system/tcg.h" #include "cpu-models.h" #include "mmu-hash32.h" #include "mmu-hash64.h" diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 9f811af0a4..fde9912230 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,8 +19,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "cpu.h" #include "exec/exec-all.h" #include "internal.h" @@ -30,7 +30,7 @@ #include "trace.h" #ifdef CONFIG_TCG -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #endif diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 42c681ca4a..3ad4273c16 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -21,8 +21,8 @@ #include "cpu.h" #include "qemu/main-loop.h" #include "exec/exec-all.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "helper_regs.h" #include "power8-pmu.h" #include "cpu-models.h" diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 0d464824db..966c2c6572 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -26,10 +26,10 @@ #include "cpu.h" #include "cpu-models.h" #include "qemu/timer.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "kvm_ppc.h" -#include "sysemu/cpus.h" -#include "sysemu/device_tree.h" +#include "system/cpus.h" +#include "system/device_tree.h" #include "mmu-hash64.h" #include "hw/ppc/spapr.h" @@ -37,18 +37,18 @@ #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "migration/qemu-file-types.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "trace.h" #include "gdbstub/enums.h" #include "exec/memattrs.h" #include "exec/ram_addr.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "qemu/mmap-alloc.h" #include "elf.h" -#include "sysemu/kvm_int.h" -#include "sysemu/kvm.h" +#include "system/kvm_int.h" +#include "system/kvm.h" #include "hw/core/accel-cpu.h" #include CONFIG_DEVICES diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 1975fb5ee6..1d8cb76a6b 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -9,7 +9,7 @@ #ifndef KVM_PPC_H #define KVM_PPC_H -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "exec/hwaddr.h" #include "cpu.h" diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 717bf93e88..0bd7ae6c0c 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -1,15 +1,15 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "helper_regs.h" #include "mmu-hash64.h" #include "migration/cpu.h" #include "qapi/error.h" #include "kvm_ppc.h" #include "power8-pmu.h" -#include "sysemu/replay.h" +#include "system/replay.h" static void post_load_update_msr(CPUPPCState *env) { diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 44b16142ab..1f791a7f2f 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/page-protection.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "internal.h" #include "mmu-hash32.h" diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c8c2f8910a..5ca4faee2a 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -24,7 +24,7 @@ #include "exec/page-protection.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "exec/log.h" diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index be7a45f254..1d3d9e1be7 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -22,7 +22,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "exec/log.h" #include "internal.h" diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 60f8736210..fb62b947f1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "cpu.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "mmu-hash32.h" diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index b167b37e0a..a802bc9c62 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "cpu.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "mmu-hash32.h" diff --git a/target/riscv/arch_dump.c b/target/riscv/arch_dump.c index 434c8a3dbb..12b6879907 100644 --- a/target/riscv/arch_dump.c +++ b/target/riscv/arch_dump.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" /* struct user_regs_struct from arch/riscv/include/uapi/asm/ptrace.h */ struct riscv64_user_regs { diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4329015076..3b316184fd 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -32,9 +32,9 @@ #include "hw/core/qdev-prop-internal.h" #include "migration/vmstate.h" #include "fpu/softfloat-helpers.h" -#include "sysemu/device_tree.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/device_tree.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "kvm/kvm_riscv.h" #include "tcg/tcg-cpu.h" #include "tcg/tcg.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 45806f5ab0..39e1dd76a5 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -29,7 +29,7 @@ #include "tcg/tcg-op.h" #include "trace.h" #include "semihosting/common-semi.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "cpu_bits.h" #include "debug.h" #include "tcg/oversized-guest.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9846770820..7f8c76e229 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -26,7 +26,7 @@ #include "time_helper.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/guest-random.h" #include "qapi/error.h" diff --git a/target/riscv/debug.c b/target/riscv/debug.c index c79b51af30..f6241a80be 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -30,7 +30,7 @@ #include "trace.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" /* * The following M-mode trigger CSRs are implemented: diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index c53ca1f76b..189e460ee2 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -27,9 +27,9 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qapi/visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/kvm_int.h" #include "cpu.h" #include "trace.h" #include "hw/core/accel-cpu.h" @@ -45,7 +45,7 @@ #include "sbi_ecall_interface.h" #include "chardev/char-fe.h" #include "migration/misc.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/riscv/numa.h" #define PR_RISCV_V_SET_CONTROL 69 diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 99f0af5077..b2e1f2503c 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -19,9 +19,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "migration/cpu.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "debug.h" static bool pmp_needed(void *opaque) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index e05ab067d2..cf713663ee 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -22,8 +22,8 @@ #include "qemu/timer.h" #include "cpu.h" #include "pmu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/device_tree.h" +#include "system/cpu-timers.h" +#include "system/device_tree.h" #define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index d363dc318d..e945b3eb02 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -31,8 +31,8 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" #include "qom/qom-qobject.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "cpu-qom.h" #include "cpu.h" diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index 029d91d93a..2c26e99295 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -16,7 +16,7 @@ #include "cpu.h" #include "s390x-internal.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "kvm/kvm_s390x.h" #include "target/s390x/kvm/pv.h" diff --git a/target/s390x/cpu-dump.c b/target/s390x/cpu-dump.c index 69cc9f7746..869d3a4ad5 100644 --- a/target/s390x/cpu-dump.c +++ b/target/s390x/cpu-dump.c @@ -23,7 +23,7 @@ #include "cpu.h" #include "s390x-internal.h" #include "qemu/qemu-print.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" void s390_cpu_dump_state(CPUState *cs, FILE *f, int flags) { diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-system.c similarity index 96% rename from target/s390x/cpu-sysemu.c rename to target/s390x/cpu-system.c index 1cd30c1d84..2ba2598ae0 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-system.c @@ -1,5 +1,5 @@ /* - * QEMU S/390 CPU - System Emulation-only code + * QEMU S/390 CPU - System-only code * * Copyright (c) 2009 Ulrich Hecht * Copyright (c) 2011 Alexander Graf @@ -26,17 +26,17 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "qemu/timer.h" #include "trace.h" #include "qapi/qapi-visit-run-state.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "target/s390x/kvm/pv.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "sysemu/tcg.h" +#include "system/system.h" +#include "system/tcg.h" #include "hw/core/sysemu-cpu-ops.h" /* S390CPUClass::load_normal() */ @@ -107,7 +107,7 @@ static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, qapi_free_GuestPanicInformation(panic_info); } -void s390_cpu_init_sysemu(Object *obj) +void s390_cpu_system_init(Object *obj) { CPUState *cs = CPU(obj); S390CPU *cpu = S390_CPU(obj); @@ -122,7 +122,7 @@ void s390_cpu_init_sysemu(Object *obj) s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } -bool s390_cpu_realize_sysemu(DeviceState *dev, Error **errp) +bool s390_cpu_system_realize(DeviceState *dev, Error **errp) { S390CPU *cpu = S390_CPU(dev); MachineState *ms = MACHINE(qdev_get_machine()); @@ -164,7 +164,7 @@ static const struct SysemuCPUOps s390_sysemu_ops = { .legacy_vmsd = &vmstate_s390_cpu, }; -void s390_cpu_class_init_sysemu(CPUClass *cc) +void s390_cpu_system_class_init(CPUClass *cc) { S390CPUClass *scc = S390_CPU_CLASS(cc); diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 4702761ca3..a149dbd848 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -25,19 +25,19 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/module.h" #include "trace.h" #include "qapi/qapi-types-machine.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/resettable.h" #include "fpu/softfloat-helpers.h" #include "disas/capstone.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/reset.h" +#include "system/reset.h" #endif #include "hw/s390x/cpu-topology.h" @@ -260,7 +260,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) } #if !defined(CONFIG_USER_ONLY) - if (!s390_cpu_realize_sysemu(dev, &err)) { + if (!s390_cpu_system_realize(dev, &err)) { goto out; } #endif @@ -300,7 +300,7 @@ static void s390_cpu_initfn(Object *obj) cs->exception_index = EXCP_HLT; #if !defined(CONFIG_USER_ONLY) - s390_cpu_init_sysemu(obj); + s390_cpu_system_init(obj); #endif } @@ -404,7 +404,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY - s390_cpu_class_init_sysemu(cc); + s390_cpu_system_class_init(cc); #endif cc->disas_set_info = s390_cpu_disas_set_info; cc->gdb_core_xml_file = "s390x-core64.xml"; diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index beb50b5300..93a05e43d7 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -14,8 +14,8 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qapi/visitor.h" @@ -23,7 +23,7 @@ #include "qemu/hw-version.h" #include "qemu/qemu-print.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/sysemu.h" +#include "system/system.h" #include "target/s390x/kvm/pv.h" #include CONFIG_DEVICES #endif diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_system.c similarity index 99% rename from target/s390x/cpu_models_sysemu.c rename to target/s390x/cpu_models_system.c index f6df691b66..6b65fa2276 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_system.c @@ -1,5 +1,5 @@ /* - * CPU models for s390x - System Emulation-only + * CPU models for s390x - System-only * * Copyright 2016 IBM Corp. * @@ -14,7 +14,7 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qapi/qobject-input-visitor.h" diff --git a/target/s390x/diag.c b/target/s390x/diag.c index a1fd54ddac..da44b0133e 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -16,10 +16,10 @@ #include "cpu.h" #include "s390x-internal.h" #include "hw/watchdog/wdt_diag288.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm/kvm_s390x.h" #include "target/s390x/kvm/pv.h" #include "qemu/error-report.h" diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index 63373f02ce..6879430adc 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -25,8 +25,8 @@ #include "exec/gdbstub.h" #include "gdbstub/helpers.h" #include "qemu/bitops.h" -#include "sysemu/hw_accel.h" -#include "sysemu/tcg.h" +#include "system/hw_accel.h" +#include "system/tcg.h" int s390_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 00d5d403f3..c689e11b46 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -1,5 +1,5 @@ /* - * S/390 helpers - sysemu only + * S/390 helpers - system only * * Copyright (c) 2009 Ulrich Hecht * Copyright (c) 2011 Alexander Graf @@ -25,8 +25,8 @@ #include "qemu/timer.h" #include "hw/s390x/ioinst.h" #include "target/s390x/kvm/pv.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" +#include "system/hw_accel.h" +#include "system/runstate.h" void s390x_tod_timer(void *opaque) { diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index 5195f060ec..d68d8955b1 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -12,8 +12,8 @@ #include "kvm/kvm_s390x.h" #include "s390x-internal.h" #include "exec/exec-all.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "hw/s390x/ioinst.h" #include "tcg/tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index dd0322c43a..d4ba83eff9 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -27,7 +27,7 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm_s390x.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -36,10 +36,10 @@ #include "qemu/main-loop.h" #include "qemu/mmap-alloc.h" #include "qemu/log.h" -#include "sysemu/sysemu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "sysemu/device_tree.h" +#include "system/system.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/device_tree.h" #include "gdbstub/enums.h" #include "exec/ram_addr.h" #include "trace.h" diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index dde836d21a..e4b0d17a48 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -16,8 +16,8 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/cpus.h" +#include "system/kvm.h" +#include "system/cpus.h" #include "qom/object_interfaces.h" #include "exec/confidential-guest-support.h" #include "hw/s390x/ipl.h" diff --git a/target/s390x/kvm/pv.h b/target/s390x/kvm/pv.h index 4b40817439..5e9c8bd351 100644 --- a/target/s390x/kvm/pv.h +++ b/target/s390x/kvm/pv.h @@ -13,7 +13,7 @@ #define HW_S390_PV_H #include "qapi/error.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/s390x/s390-virtio-ccw.h" #ifdef CONFIG_KVM diff --git a/target/s390x/machine.c b/target/s390x/machine.c index a125ebcc2f..3bea6103ff 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -20,8 +20,8 @@ #include "kvm/kvm_s390x.h" #include "migration/vmstate.h" #include "tcg/tcg_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" static int cpu_post_load(void *opaque, int version_id) { diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 02ca43d9f0..3b34ae034c 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -27,8 +27,8 @@ s390x_system_ss.add(files( 'machine.c', 'mmu_helper.c', 'sigp.c', - 'cpu-sysemu.c', - 'cpu_models_sysemu.c', + 'cpu-system.c', + 'cpu_models_system.c', )) s390x_user_ss = ss.source_set() diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 6c59d0d216..d8f483898d 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -21,8 +21,8 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "hw/hw.h" diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 825252d728..4cc435042c 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -240,10 +240,10 @@ uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, #ifndef CONFIG_USER_ONLY unsigned int s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); -void s390_cpu_init_sysemu(Object *obj); -bool s390_cpu_realize_sysemu(DeviceState *dev, Error **errp); +void s390_cpu_system_init(Object *obj); +bool s390_cpu_system_realize(DeviceState *dev, Error **errp); void s390_cpu_finalize(Object *obj); -void s390_cpu_class_init_sysemu(CPUClass *cc); +void s390_cpu_system_class_init(CPUClass *cc); void s390_cpu_machine_reset_cb(void *opaque); #else diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 08aaecf12b..cf53b23291 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -12,11 +12,11 @@ #include "cpu.h" #include "s390x-internal.h" #include "hw/boards.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" +#include "system/hw_accel.h" +#include "system/runstate.h" #include "exec/address-spaces.h" #include "exec/exec-all.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "trace.h" #include "qapi/qapi-types-machine.h" diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 303f86d363..ea25197d73 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -33,8 +33,8 @@ #include "s390-tod.h" #if !defined(CONFIG_USER_ONLY) -#include "sysemu/cpus.h" -#include "sysemu/sysemu.h" +#include "system/cpus.h" +#include "system/system.h" #include "hw/s390x/ebcdic.h" #include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" diff --git a/target/s390x/trace-events b/target/s390x/trace-events index d371ef71b9..ef3120d3b1 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -6,7 +6,7 @@ ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x. ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command 0x%04x, len 0x%04x" -# cpu-sysemu.c +# cpu-system.c cpu_set_state(int cpu_index, uint8_t state) "setting cpu %d state to %" PRIu8 cpu_halt(int cpu_index) "halting cpu %d" cpu_unhalt(int cpu_index) "unhalting cpu %d" diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 9659c69550..b8774e046e 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -26,7 +26,7 @@ #if !defined(CONFIG_USER_ONLY) #include "hw/sh4/sh_intc.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #endif #define MMU_OK 0 diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index f2dd8bcb2e..f026606102 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -23,7 +23,7 @@ #include "trace.h" #include "exec/cpu_ldst.h" #include "exec/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" static const char * const excp_names[0x80] = { [TT_TFAULT] = "Instruction Access Fault", diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 9b9c9f9c36..8274000bd5 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -17,9 +17,9 @@ #include "qemu/cutils.h" #include "qemu/datadir.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/rcu.h" #include "tests/qtest/libqtest.h" diff --git a/tests/qtest/tpm-emu.h b/tests/qtest/tpm-emu.h index 712cee9b7a..59c8009f4c 100644 --- a/tests/qtest/tpm-emu.h +++ b/tests/qtest/tpm-emu.h @@ -21,7 +21,7 @@ #include "qemu/sockets.h" #include "io/channel.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "libqtest.h" struct tpm_hdr { diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 8948fb81ef..76d142a158 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -20,7 +20,7 @@ #include "chardev/char-fe.h" #include "qemu/memfd.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/virtio-pci.h" diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index c112d5b189..7410e6f352 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "block/block_int.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" #include "iothread.h" diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index cafc023db4..d743abb4bb 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/block_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" static BlockDriver bdrv_pass_through = { .format_name = "pass-through", diff --git a/tests/unit/test-block-backend.c b/tests/unit/test-block-backend.c index 2fb1a444bd..4257b3f815 100644 --- a/tests/unit/test-block-backend.c +++ b/tests/unit/test-block-backend.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 20ed54f570..26a6c05175 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -26,7 +26,7 @@ #include "block/block.h" #include "block/block_int-global-state.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu/main-loop.h" diff --git a/tests/unit/test-blockjob-txn.c b/tests/unit/test-blockjob-txn.c index d3b0bb24be..c7b4e55483 100644 --- a/tests/unit/test-blockjob-txn.c +++ b/tests/unit/test-blockjob-txn.c @@ -14,7 +14,7 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qmp/qdict.h" typedef struct { diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index fe3e0d2d38..1df5739b13 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -14,7 +14,7 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qmp/qdict.h" #include "iothread.h" diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c index a6e8753e1c..98a60d86b1 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -7,7 +7,7 @@ #include "qemu/option.h" #include "qemu/sockets.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" #include "qapi/qmp/qdict.h" diff --git a/tests/unit/test-image-locking.c b/tests/unit/test-image-locking.c index 2624cec6a0..7ccf9567f1 100644 --- a/tests/unit/test-image-locking.c +++ b/tests/unit/test-image-locking.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu/main-loop.h" diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c index 5d2003b8ce..2a60f78e0a 100644 --- a/tests/unit/test-replication.c +++ b/tests/unit/test-replication.c @@ -17,7 +17,7 @@ #include "block/replication.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #define IMG_SIZE (64 * 1024 * 1024) diff --git a/tests/unit/test-seccomp.c b/tests/unit/test-seccomp.c index bab93fd6da..71d4083439 100644 --- a/tests/unit/test-seccomp.c +++ b/tests/unit/test-seccomp.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu/config-file.h" #include "qemu/option.h" -#include "sysemu/seccomp.h" +#include "system/seccomp.h" #include "qapi/error.h" #include "qemu/module.h" diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c index 24032a0266..dfa61c75ea 100644 --- a/tests/unit/test-throttle.c +++ b/tests/unit/test-throttle.c @@ -21,7 +21,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "block/throttle-groups.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" static AioContext *ctx; static LeakyBucket bkt; diff --git a/tests/unit/test-timed-average.c b/tests/unit/test-timed-average.c index 82c92500df..747ed1ee60 100644 --- a/tests/unit/test-timed-average.c +++ b/tests/unit/test-timed-average.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/timed-average.h" /* This is the clock for QEMU_CLOCK_VIRTUAL */ diff --git a/tests/unit/test-yank.c b/tests/unit/test-yank.c index e6c036a64d..4acfb2f3f6 100644 --- a/tests/unit/test-yank.c +++ b/tests/unit/test-yank.c @@ -14,7 +14,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" #include "qapi/qapi-types-char.h" diff --git a/ui/cocoa.m b/ui/cocoa.m index dd88115dc6..3a88535374 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -34,15 +34,15 @@ #include "ui/console.h" #include "ui/input.h" #include "ui/kbd-state.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/cpu-throttle.h" +#include "system/system.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/cpu-throttle.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "qemu-version.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" diff --git a/ui/curses.c b/ui/curses.c index 4d0be9b37d..a39aee8762 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -36,7 +36,7 @@ #include "qemu/module.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #ifdef __APPLE__ #define _XOPEN_SOURCE_EXTENDED 1 diff --git a/ui/dbus-clipboard.c b/ui/dbus-clipboard.c index fbb043abca..6787a77668 100644 --- a/ui/dbus-clipboard.c +++ b/ui/dbus-clipboard.c @@ -26,7 +26,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qom/object_interfaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "trace.h" diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 99738e769b..51244c9240 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "dbus.h" #include "glib.h" #ifdef G_OS_UNIX diff --git a/ui/dbus.c b/ui/dbus.c index d60b59cc54..7b258c6823 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -28,7 +28,7 @@ #include "qemu/main-loop.h" #include "qemu/option.h" #include "qom/object_interfaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "ui/dbus-module.h" #ifdef CONFIG_OPENGL #include "ui/egl-helpers.h" diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 81a57fa975..d591159480 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -20,7 +20,7 @@ #include "qemu/error-report.h" #include "ui/console.h" #include "ui/egl-helpers.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "trace.h" diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 9831c10e1b..f7a428c86a 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -22,7 +22,7 @@ #include "ui/egl-helpers.h" #include "ui/shader.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) { diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index b628b35451..2c9a0db425 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -16,7 +16,7 @@ #include "ui/gtk.h" #include "ui/egl-helpers.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) { diff --git a/ui/gtk.c b/ui/gtk.c index f9a53ea78e..0d38c070e4 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -55,8 +55,8 @@ #include "trace.h" #include "ui/input.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "keymaps.h" #include "chardev/char.h" #include "qom/object.h" diff --git a/ui/input-barrier.c b/ui/input-barrier.c index 2d57ca7079..c86e5d67eb 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qapi/error.h" diff --git a/ui/input-linux.c b/ui/input-linux.c index e572a2e905..381148ea99 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -12,7 +12,7 @@ #include "qemu/sockets.h" #include "ui/input.h" #include "qom/object_interfaces.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "block/aio.h" #include diff --git a/ui/input.c b/ui/input.c index 7ddefebc43..147e69c1c3 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,12 +1,12 @@ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "trace.h" #include "ui/input.h" #include "ui/console.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" struct QemuInputHandlerState { DeviceState *dev; diff --git a/ui/sdl2.c b/ui/sdl2.c index bd4f5a9da1..1fb72f67a6 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -29,9 +29,9 @@ #include "ui/console.h" #include "ui/input.h" #include "ui/sdl2.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/system.h" #include "ui/win32-kbd-hook.h" #include "qemu/log.h" diff --git a/ui/spice-app.c b/ui/spice-app.c index 2a93ae5918..91e258a621 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -36,7 +36,7 @@ #include "qapi/error.h" #include "io/channel-command.h" #include "chardev/spice.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qom/object.h" static const char *tmp_dir; diff --git a/ui/spice-core.c b/ui/spice-core.c index bd9dbe03f1..0326c63bec 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -18,8 +18,8 @@ #include "qemu/osdep.h" #include -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "ui/qemu-spice.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/ui/vnc.c b/ui/vnc.c index 5fcb35bf25..9241caaad9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -29,8 +29,8 @@ #include "vnc-jobs.h" #include "trace.h" #include "hw/qdev-core.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/ui/win32-kbd-hook.c b/ui/win32-kbd-hook.c index 1ac237db9e..f448247b19 100644 --- a/ui/win32-kbd-hook.c +++ b/ui/win32-kbd-hook.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "ui/win32-kbd-hook.h" static Notifier win32_unhook_notifier; diff --git a/util/async.c b/util/async.c index 99db28389f..0fe2943609 100644 --- a/util/async.c +++ b/util/async.c @@ -35,7 +35,7 @@ #include "block/raw-aio.h" #include "qemu/coroutine_int.h" #include "qemu/coroutine-tls.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "trace.h" /***********************************************************/ diff --git a/util/main-loop.c b/util/main-loop.c index a0386cfeb6..acad8c2e6c 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -26,8 +26,8 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/timer.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" +#include "system/cpu-timers.h" +#include "system/replay.h" #include "qemu/main-loop.h" #include "block/aio.h" #include "block/thread-pool.h" diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 11b35e48fb..7a542cb50b 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -31,7 +31,7 @@ #include -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include "qapi/error.h" #include "qemu/error-report.h" diff --git a/util/qemu-timer.c b/util/qemu-timer.c index ffe9a3c5c1..16f847ff98 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -26,9 +26,9 @@ #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/lockable.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" -#include "sysemu/cpus.h" +#include "system/cpu-timers.h" +#include "system/replay.h" +#include "system/cpus.h" #ifdef CONFIG_POSIX #include From 433442a75d04f446eae229d91d65b0afc9bf92fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Nov 2024 18:00:31 +0100 Subject: [PATCH 0554/2892] system: Move 'exec/confidential-guest-support.h' to system/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/confidential-guest-support.h" is specific to system emulation, so move it under the system/ namespace. Mechanical change doing: $ sed -i \ -e 's,exec/confidential-guest-support.h,sysemu/confidential-guest-support.h,' \ $(git grep -l exec/confidential-guest-support.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Message-Id: <20241218155913.72288-2-philmd@linaro.org> --- backends/confidential-guest-support.c | 2 +- hw/core/machine.c | 2 +- hw/ppc/pef.c | 2 +- hw/ppc/spapr.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- include/{exec => system}/confidential-guest-support.h | 6 +++--- system/vl.c | 2 +- target/i386/confidential-guest.h | 2 +- target/s390x/kvm/pv.c | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) rename include/{exec => system}/confidential-guest-support.h (96%) diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c index 052fde8db0..1cd9bed505 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, confidential_guest_support, diff --git a/hw/core/machine.c b/hw/core/machine.c index 008d3379e1..c949af9766 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -30,7 +30,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/mem/nvdimm.h" #include "migration/global_state.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-net.h" #include "hw/virtio/virtio-iommu.h" diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index cffda44602..8b2d726e00 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -14,7 +14,7 @@ #include "qom/object_interfaces.h" #include "system/kvm.h" #include "migration/blocker.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #define TYPE_PEF_GUEST "pef-guest" OBJECT_DECLARE_SIMPLE_TYPE(PefGuest, PEF_GUEST) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ad21018b5a..623842f806 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -75,7 +75,7 @@ #include "hw/virtio/vhost-scsi-common.h" #include "exec/ram_addr.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" #include "qemu/error-report.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index f4d64d64f9..b45d8963b3 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/ram_addr.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "hw/boards.h" #include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" diff --git a/include/exec/confidential-guest-support.h b/include/system/confidential-guest-support.h similarity index 96% rename from include/exec/confidential-guest-support.h rename to include/system/confidential-guest-support.h index 02dc4e518f..b68c4bebbc 100644 --- a/include/exec/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -18,7 +18,9 @@ #ifndef QEMU_CONFIDENTIAL_GUEST_SUPPORT_H #define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY +#error Cannot include system/confidential-guest-support.h from user emulation +#endif #include "qom/object.h" @@ -94,6 +96,4 @@ static inline int confidential_guest_kvm_reset(ConfidentialGuestSupport *cgs, return 0; } -#endif /* !CONFIG_USER_ONLY */ - #endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */ diff --git a/system/vl.c b/system/vl.c index 91d6d4f7f7..0843b7ab49 100644 --- a/system/vl.c +++ b/system/vl.c @@ -107,7 +107,7 @@ #include "qemu/plugin.h" #include "qemu/queue.h" #include "system/arch_init.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index 7342d2843a..0afb8317b5 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -14,7 +14,7 @@ #include "qom/object.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #define TYPE_X86_CONFIDENTIAL_GUEST "x86-confidential-guest" diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index e4b0d17a48..69c1811e15 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -19,7 +19,7 @@ #include "system/kvm.h" #include "system/cpus.h" #include "qom/object_interfaces.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "hw/s390x/ipl.h" #include "hw/s390x/sclp.h" #include "target/s390x/kvm/kvm_s390x.h" From 069ea4c825ef2bd897139536aa00856bed1ef5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 19 Apr 2024 07:37:00 +0200 Subject: [PATCH 0555/2892] tcg/tci: Include missing 'disas/dis-asm.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "disas/dis-asm.h" defines bfd_vma and disassemble_info, include it in order to avoid (when refactoring other headers): tcg/tci.c:1066:20: error: unknown type name 'bfd_vma' int print_insn_tci(bfd_vma addr, disassemble_info *info) ^ tcg/tci.c:1066:34: error: unknown type name 'disassemble_info' int print_insn_tci(bfd_vma addr, disassemble_info *info) ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241218155202.71931-3-philmd@linaro.org> --- tcg/tci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tcg/tci.c b/tcg/tci.c index 3afb223528..3eb95e20b6 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -21,6 +21,7 @@ #include "tcg/tcg.h" #include "tcg/helper-info.h" #include "tcg/tcg-ldst.h" +#include "disas/dis-asm.h" #include From ea77480146c4c84a604273de25744c78ef8968b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Dec 2024 23:37:35 +0100 Subject: [PATCH 0556/2892] accel/tcg: Include missing 'exec/tswap.h' header in translator.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit translator.c indirectly gets "exec/tswap.h" declarations via "exec/cpu-all.h". Include it directly to be able to remove the former from the latter, otherwise we get: accel/tcg/translator.c:433:15: error: call to undeclared function 'tswap16'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 433 | tgt = tswap16(raw); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241211230357.97036-4-philmd@linaro.org> --- accel/tcg/translator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index cbad00a517..ff5dabc901 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -15,6 +15,7 @@ #include "exec/cpu_ldst.h" #include "exec/plugin-gen.h" #include "exec/cpu_ldst.h" +#include "exec/tswap.h" #include "tcg/tcg-op-common.h" #include "internal-target.h" #include "disas/disas.h" From 9c6e54f4754dfdc1e278a1239553a935b92e3062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 08:29:44 +0100 Subject: [PATCH 0557/2892] accel/tcg: Have tlb_vaddr_to_host() use vaddr type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit abi_ptr is expected to be used in user emulation. tlb_vaddr_to_host() uses it, but can be used in system emulation. Replace the type by 'vaddr' which is equivalent on user emulation but also works on system. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-13-philmd@linaro.org> --- accel/tcg/cputlb.c | 2 +- include/exec/cpu_ldst.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b76a4eac4e..080cbcb34d 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1504,7 +1504,7 @@ void *probe_access(CPUArchState *env, vaddr addr, int size, return host; } -void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, MMUAccessType access_type, int mmu_idx) { CPUTLBEntryFull *full; diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index dac12bd8eb..c1dd424dc1 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -67,6 +67,7 @@ #endif #include "exec/memopidx.h" +#include "exec/vaddr.h" #include "exec/abi_ptr.h" #include "exec/mmu-access-type.h" #include "qemu/int128.h" @@ -375,7 +376,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, return g2h(env_cpu(env), addr); } #else -void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, MMUAccessType access_type, int mmu_idx); #endif From 1f52d85e36e3c87ddfe04807fb9bbe57ee023a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 13:54:54 +0100 Subject: [PATCH 0558/2892] exec/cpu-all: Include missing 'exec/cpu-defs.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TARGET_PAGE_BITS is defined in each target "cpu-param.h", itself included by "exec/cpu-defs.h". Include the latter in order to avoid when refactoring: In file included from ../../system/watchpoint.c:23: include/exec/cpu-all.h:356:19: error: use of undeclared identifier 'TARGET_PAGE_BITS' 356 | QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-2-philmd@linaro.org> --- include/exec/cpu-all.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 45e6676938..1c40e27672 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -136,7 +136,7 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val #endif /* page related stuff */ - +#include "exec/cpu-defs.h" #ifdef TARGET_PAGE_BITS_VARY # include "exec/page-vary.h" extern const TargetPageBits target_page; From 600c63d4dc5b2f036a21534bc1ea0640c64c1628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 23:07:12 +0100 Subject: [PATCH 0559/2892] exec/cpu-defs: Remove unnecessary headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/cpu-defs.h" should be kept as minimal as possible; besides these includes don't seem necessary. Remove them. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Tested-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-3-philmd@linaro.org> --- include/exec/cpu-defs.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index 0dbef3010c..ae18398fa9 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -23,14 +23,6 @@ #error cpu.h included from common code #endif -#include "qemu/host-utils.h" -#include "qemu/thread.h" -#ifndef CONFIG_USER_ONLY -#include "exec/hwaddr.h" -#endif -#include "exec/memattrs.h" -#include "hw/core/cpu.h" - #include "cpu-param.h" #ifndef TARGET_LONG_BITS From 4081f5a698667e5584fb154d336e7e54c3949944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 22:42:40 +0100 Subject: [PATCH 0560/2892] exec/translation-block: Include missing 'exec/vaddr.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'vaddr' type is declared in "exec/vaddr.h". "exec/translation-block.h" uses this type without including the corresponding header. It works because this header is indirectly included, but won't work when the other headers are refactored: include/exec/translation-block.h:56:5: error: unknown type name 'vaddr' 56 | vaddr pc; | ^ Explitly include "exec/vaddr.h" to avoid such problem in a few commits. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-4-philmd@linaro.org> --- include/exec/translation-block.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index a6d1af6e9b..b99afb0077 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -9,6 +9,7 @@ #include "qemu/thread.h" #include "exec/cpu-common.h" +#include "exec/vaddr.h" #ifdef CONFIG_USER_ONLY #include "qemu/interval-tree.h" #endif From a6c0102a864014e8824abed3a499d93b87670e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 23:17:34 +0100 Subject: [PATCH 0561/2892] linux-user/aarch64: Include missing 'user/abitypes.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_set_mte_tcf0() uses the abi_long type which is defined in "user/abitypes.h". Include it in order to avoid when refactoring: In file included from ../../target/arm/gdbstub64.c:28: ../linux-user/aarch64/mte_user_helper.h:30:42: error: unknown type name ‘abi_long’; did you mean ‘u_long’? 30 | void arm_set_mte_tcf0(CPUArchState *env, abi_long value); | ^~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-3-philmd@linaro.org> --- linux-user/aarch64/mte_user_helper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/aarch64/mte_user_helper.h b/linux-user/aarch64/mte_user_helper.h index 8685e5175a..0c53abda22 100644 --- a/linux-user/aarch64/mte_user_helper.h +++ b/linux-user/aarch64/mte_user_helper.h @@ -9,6 +9,8 @@ #ifndef AARCH64_MTE_USER_HELPER_H #define AARCH64_MTE USER_HELPER_H +#include "user/abitypes.h" + #ifndef PR_MTE_TCF_SHIFT # define PR_MTE_TCF_SHIFT 1 # define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) From f9ba56a03c2e954c37737826203574a8cde0b3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 00:08:02 +0100 Subject: [PATCH 0562/2892] user: Introduce 'user/guest-host.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract all declarations related to 'guest from/to host' address translation to a new "user/guest-host.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-2-philmd@linaro.org> --- include/exec/cpu-all.h | 34 +-------------- include/exec/cpu_ldst.h | 47 +-------------------- include/user/guest-host.h | 87 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 79 deletions(-) create mode 100644 include/user/guest-host.h diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 1c40e27672..1c8e0446d0 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -64,39 +64,7 @@ /* MMU memory access macros */ -#if defined(CONFIG_USER_ONLY) -#include "user/abitypes.h" - -/* - * If non-zero, the guest virtual address space is a contiguous subset - * of the host virtual address space, i.e. '-R reserved_va' is in effect - * either from the command-line or by default. The value is the last - * byte of the guest address space e.g. UINT32_MAX. - * - * If zero, the host and guest virtual address spaces are intermingled. - */ -extern unsigned long reserved_va; - -/* - * Limit the guest addresses as best we can. - * - * When not using -R reserved_va, we cannot really limit the guest - * to less address space than the host. For 32-bit guests, this - * acts as a sanity check that we're not giving the guest an address - * that it cannot even represent. For 64-bit guests... the address - * might not be what the real kernel would give, but it is at least - * representable in the guest. - * - * TODO: Improve address allocation to avoid this problem, and to - * avoid setting bits at the top of guest addresses that might need - * to be used for tags. - */ -#define GUEST_ADDR_MAX_ \ - ((MIN_CONST(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) ? \ - UINT32_MAX : ~0ul) -#define GUEST_ADDR_MAX (reserved_va ? : GUEST_ADDR_MAX_) - -#else +#if !defined(CONFIG_USER_ONLY) #include "exec/hwaddr.h" diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index c1dd424dc1..769e9fc440 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -73,52 +73,7 @@ #include "qemu/int128.h" #if defined(CONFIG_USER_ONLY) - -#include "user/guest-base.h" - -#ifndef TARGET_TAGGED_ADDRESSES -static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) -{ - return x; -} -#endif - -/* All direct uses of g2h and h2g need to go away for usermode softmmu. */ -static inline void *g2h_untagged(abi_ptr x) -{ - return (void *)((uintptr_t)(x) + guest_base); -} - -static inline void *g2h(CPUState *cs, abi_ptr x) -{ - return g2h_untagged(cpu_untagged_addr(cs, x)); -} - -static inline bool guest_addr_valid_untagged(abi_ulong x) -{ - return x <= GUEST_ADDR_MAX; -} - -static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) -{ - return len - 1 <= GUEST_ADDR_MAX && start <= GUEST_ADDR_MAX - len + 1; -} - -#define h2g_valid(x) \ - (HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS || \ - (uintptr_t)(x) - guest_base <= GUEST_ADDR_MAX) - -#define h2g_nocheck(x) ({ \ - uintptr_t __ret = (uintptr_t)(x) - guest_base; \ - (abi_ptr)__ret; \ -}) - -#define h2g(x) ({ \ - /* Check if given address fits target address space */ \ - assert(h2g_valid(x)); \ - h2g_nocheck(x); \ -}) - +#include "user/guest-host.h" #endif /* CONFIG_USER_ONLY */ uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); diff --git a/include/user/guest-host.h b/include/user/guest-host.h new file mode 100644 index 0000000000..8d2079bbbb --- /dev/null +++ b/include/user/guest-host.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * guest <-> host helpers. + * + * Copyright (c) 2003 Fabrice Bellard + */ + +#ifndef USER_GUEST_HOST_H +#define USER_GUEST_HOST_H + +#include "user/abitypes.h" +#include "user/guest-base.h" +#include "cpu.h" + +/* + * If non-zero, the guest virtual address space is a contiguous subset + * of the host virtual address space, i.e. '-R reserved_va' is in effect + * either from the command-line or by default. The value is the last + * byte of the guest address space e.g. UINT32_MAX. + * + * If zero, the host and guest virtual address spaces are intermingled. + */ +extern unsigned long reserved_va; + +/* + * Limit the guest addresses as best we can. + * + * When not using -R reserved_va, we cannot really limit the guest + * to less address space than the host. For 32-bit guests, this + * acts as a sanity check that we're not giving the guest an address + * that it cannot even represent. For 64-bit guests... the address + * might not be what the real kernel would give, but it is at least + * representable in the guest. + * + * TODO: Improve address allocation to avoid this problem, and to + * avoid setting bits at the top of guest addresses that might need + * to be used for tags. + */ +#define GUEST_ADDR_MAX_ \ + ((MIN_CONST(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) ? \ + UINT32_MAX : ~0ul) +#define GUEST_ADDR_MAX (reserved_va ? : GUEST_ADDR_MAX_) + +#ifndef TARGET_TAGGED_ADDRESSES +static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) +{ + return x; +} +#endif + +/* All direct uses of g2h and h2g need to go away for usermode softmmu. */ +static inline void *g2h_untagged(abi_ptr x) +{ + return (void *)((uintptr_t)(x) + guest_base); +} + +static inline void *g2h(CPUState *cs, abi_ptr x) +{ + return g2h_untagged(cpu_untagged_addr(cs, x)); +} + +static inline bool guest_addr_valid_untagged(abi_ulong x) +{ + return x <= GUEST_ADDR_MAX; +} + +static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) +{ + return len - 1 <= GUEST_ADDR_MAX && start <= GUEST_ADDR_MAX - len + 1; +} + +#define h2g_valid(x) \ + (HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS || \ + (uintptr_t)(x) - guest_base <= GUEST_ADDR_MAX) + +#define h2g_nocheck(x) ({ \ + uintptr_t __ret = (uintptr_t)(x) - guest_base; \ + (abi_ptr)__ret; \ +}) + +#define h2g(x) ({ \ + /* Check if given address fits target address space */ \ + assert(h2g_valid(x)); \ + h2g_nocheck(x); \ +}) + +#endif From e3b64ebf494d07dfd6adf113115eac8819dbf0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 23:21:17 +0100 Subject: [PATCH 0563/2892] target/arm/cpu: Restrict cpu_untagged_addr() to user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the #endif guard where it belongs to restrict the cpu_untagged_addr() implementation to user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-11-philmd@linaro.org> --- target/arm/cpu.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index d86e641280..12b8466542 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3355,8 +3355,8 @@ extern const uint64_t pred_esz_masks[5]; #define TAG_GRANULE (1 << LOG2_TAG_GRANULE) #ifdef CONFIG_USER_ONLY + #define TARGET_PAGE_DATA_SIZE (TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1)) -#endif #ifdef TARGET_TAGGED_ADDRESSES /** @@ -3382,6 +3382,7 @@ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) } return x; } -#endif +#endif /* TARGET_TAGGED_ADDRESSES */ +#endif /* CONFIG_USER_ONLY */ #endif From a6b3f532852463400c4602418f40628b6b9d24ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 23:21:26 +0100 Subject: [PATCH 0564/2892] target/arm/mte: Restrict 'exec/ram_addr.h' to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/ram_addr.h" contains system specific declarations. Restrict its inclusion to sysemu to avoid build errors when refactoring. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-10-philmd@linaro.org> --- target/arm/tcg/mte_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 9d2ba287ee..b017b26d07 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -23,7 +23,9 @@ #include "internals.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#ifndef CONFIG_USER_ONLY #include "exec/ram_addr.h" +#endif #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "hw/core/tcg-cpu-ops.h" From 975cb16cb5c69c681ea97d6ddcb52690167c3d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Nov 2024 18:09:34 +0100 Subject: [PATCH 0565/2892] exec/ram_addr: Include missing 'exec/hwaddr.h' and 'exec/cpu-common.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'hwaddr' is defined in "exec/hwaddr.h", 'ram_addr_t' in "exec/cpu-common.h". Include these headers in order to avoid when refactoring unrelated headers: In file included from ../../hw/s390x/s390-virtio-ccw.c:17: include/sysemu/physmem-target.h:37:24: error: unknown type name 'hwaddr' 37 | (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client); | ^ In file included from ../../hw/s390x/s390-virtio-ccw.c:16: include/exec/ram_addr.h:52:36: error: unknown type name 'ram_addr_t' 52 | RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241218155202.71931-4-philmd@linaro.org> --- include/exec/ram_addr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 53785cdb87..ff157c1f42 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -28,6 +28,9 @@ #include "exec/exec-all.h" #include "qemu/rcu.h" +#include "exec/hwaddr.h" +#include "exec/cpu-common.h" + extern uint64_t total_dirty_pages; /** From edf3bce969a70f6d97d6b26e686fe04f603f4b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Nov 2024 07:34:10 +0100 Subject: [PATCH 0566/2892] include: Include missing 'qemu/clang-tsa.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next commit will remove "qemu/clang-tsa.h" of "exec/exec-all.h", however the following files indirectly include it: $ git grep -L qemu/clang-tsa.h $(git grep -wl TSA_NO_TSA) block/create.c include/block/block_int-common.h tests/unit/test-bdrv-drain.c tests/unit/test-block-iothread.c util/qemu-thread-posix.c Explicitly include it so we can process with the removal in the next commit. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-4-philmd@linaro.org> --- block/create.c | 1 + include/block/block_int-common.h | 1 + tests/unit/test-bdrv-drain.c | 1 + tests/unit/test-block-iothread.c | 1 + util/qemu-thread-posix.c | 1 + 5 files changed, 5 insertions(+) diff --git a/block/create.c b/block/create.c index 6b23a21675..72abafb4c1 100644 --- a/block/create.c +++ b/block/create.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "qemu/clang-tsa.h" #include "qemu/job.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-block-core.h" diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index ebb4e56a50..bb91a0f62f 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -28,6 +28,7 @@ #include "block/block-common.h" #include "block/block-global-state.h" #include "block/snapshot.h" +#include "qemu/clang-tsa.h" #include "qemu/iov.h" #include "qemu/rcu.h" #include "qemu/stats64.h" diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 7410e6f352..98ad89b390 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -28,6 +28,7 @@ #include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" +#include "qemu/clang-tsa.h" #include "iothread.h" static QemuEvent done_event; diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 26a6c05175..1de04a8a13 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -29,6 +29,7 @@ #include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" +#include "qemu/clang-tsa.h" #include "qemu/main-loop.h" #include "iothread.h" diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index b2e26e2120..6fff4162ac 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -17,6 +17,7 @@ #include "qemu-thread-common.h" #include "qemu/tsan.h" #include "qemu/bitmap.h" +#include "qemu/clang-tsa.h" #ifdef CONFIG_PTHREAD_SET_NAME_NP #include From 487a31e0acf9c5e11506ac34feba5a5671fd2dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Nov 2024 07:26:43 +0100 Subject: [PATCH 0567/2892] accel/tcg: Declare mmap_[un]lock() in 'exec/page-protection.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move mmap_lock(), mmap_unlock() declarations and the WITH_MMAP_LOCK_GUARD() definition to 'exec/page-protection.h'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-5-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 1 + accel/tcg/translate-all.c | 1 + accel/tcg/watchpoint.c | 1 + include/exec/exec-all.h | 17 ----------------- include/exec/page-protection.h | 24 ++++++++++++++++++++++++ linux-user/flatload.c | 1 + target/arm/helper.c | 1 + 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c13f4a7cbb..f82870a1e2 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -25,6 +25,7 @@ #include "trace.h" #include "disas/disas.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "tcg/tcg.h" #include "qemu/atomic.h" #include "qemu/rcu.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index a8b24be0b9..d586ac9bb1 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -44,6 +44,7 @@ #endif #include "exec/cputlb.h" +#include "exec/page-protection.h" #include "exec/translate-all.h" #include "exec/translator.h" #include "exec/tb-flush.h" diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index ca641eb95c..8923301b8b 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -22,6 +22,7 @@ #include "qemu/error-report.h" #include "exec/exec-all.h" #include "exec/translate-all.h" +#include "exec/page-protection.h" #include "system/tcg.h" #include "system/replay.h" #include "hw/core/tcg-cpu-ops.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 2e4c4cc4b4..b5ea607cf4 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -27,7 +27,6 @@ #endif #include "exec/mmu-access-type.h" #include "exec/translation-block.h" -#include "qemu/clang-tsa.h" /** * cpu_loop_exit_requested: @@ -520,18 +519,6 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, } #if defined(CONFIG_USER_ONLY) -void TSA_NO_TSA mmap_lock(void); -void TSA_NO_TSA mmap_unlock(void); -bool have_mmap_lock(void); - -static inline void mmap_unlock_guard(void *unused) -{ - mmap_unlock(); -} - -#define WITH_MMAP_LOCK_GUARD() \ - for (int _mmap_lock_iter __attribute__((cleanup(mmap_unlock_guard))) \ - = (mmap_lock(), 0); _mmap_lock_iter == 0; _mmap_lock_iter = 1) /** * adjust_signal_pc: @@ -585,10 +572,6 @@ G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, uintptr_t ra); #else -static inline void mmap_lock(void) {} -static inline void mmap_unlock(void) {} -#define WITH_MMAP_LOCK_GUARD() - void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h index c43231af8b..bae3355f62 100644 --- a/include/exec/page-protection.h +++ b/include/exec/page-protection.h @@ -38,4 +38,28 @@ */ #define PAGE_PASSTHROUGH 0x0800 +#ifdef CONFIG_USER_ONLY + +#include "qemu/clang-tsa.h" + +void TSA_NO_TSA mmap_lock(void); +void TSA_NO_TSA mmap_unlock(void); +bool have_mmap_lock(void); + +static inline void mmap_unlock_guard(void *unused) +{ + mmap_unlock(); +} + +#define WITH_MMAP_LOCK_GUARD() \ + for (int _mmap_lock_iter __attribute__((cleanup(mmap_unlock_guard))) \ + = (mmap_lock(), 0); _mmap_lock_iter == 0; _mmap_lock_iter = 1) +#else + +static inline void mmap_lock(void) {} +static inline void mmap_unlock(void) {} +#define WITH_MMAP_LOCK_GUARD() + +#endif /* !CONFIG_USER_ONLY */ + #endif diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 0e4be5bf44..d5cb1830dd 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -34,6 +34,7 @@ #include "qemu/osdep.h" #include "qemu.h" +#include "exec/page-protection.h" #include "user-internals.h" #include "loader.h" #include "user-mmap.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index 146ddf06c5..449e69a6e3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -13,6 +13,7 @@ #include "internals.h" #include "cpu-features.h" #include "exec/helper-proto.h" +#include "exec/page-protection.h" #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/bitops.h" From 384fd3543b8a4f53269bb0e57d62189b43b4a635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 19 Apr 2024 10:39:26 +0200 Subject: [PATCH 0568/2892] accel/tcg: Use tb_page_addr_t type in page_unprotect() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match with the page_protect() prototype, use a tb_page_addr_t argument instead of target_ulong. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-6-philmd@linaro.org> --- accel/tcg/user-exec.c | 2 +- include/exec/translate-all.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 06016eb030..4ed6dd19f3 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -696,7 +696,7 @@ void page_protect(tb_page_addr_t address) * immediately exited. (We can only return 2 if the 'pc' argument is * non-zero.) */ -int page_unprotect(target_ulong address, uintptr_t pc) +int page_unprotect(tb_page_addr_t address, uintptr_t pc) { PageFlagsNode *p; bool current_tb_invalidated; diff --git a/include/exec/translate-all.h b/include/exec/translate-all.h index 85c9460c7c..c50661a05d 100644 --- a/include/exec/translate-all.h +++ b/include/exec/translate-all.h @@ -27,7 +27,7 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); #ifdef CONFIG_USER_ONLY void page_protect(tb_page_addr_t page_addr); -int page_unprotect(target_ulong address, uintptr_t pc); +int page_unprotect(tb_page_addr_t address, uintptr_t pc); #endif #endif /* TRANSLATE_ALL_H */ From 634f1455fc34d417df209bc744c4134db26708bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 17:10:46 +0100 Subject: [PATCH 0569/2892] accel/tcg: Move page_[un]protect() to 'user/page-protection.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-7-philmd@linaro.org> --- accel/tcg/internal-target.h | 1 + accel/tcg/user-exec.c | 2 +- include/exec/translate-all.h | 5 ----- include/user/page-protection.h | 20 ++++++++++++++++++++ linux-user/elfload.c | 2 +- 5 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 include/user/page-protection.h diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index fe109724c6..a03c05315a 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -37,6 +37,7 @@ void page_table_config_init(void); #endif #ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" /* * For user-only, page_protect sets the page read-only. * Since most execution is already on read-only pages, and we'd need to diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 4ed6dd19f3..636932303b 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -25,7 +25,7 @@ #include "qemu/rcu.h" #include "exec/cpu_ldst.h" #include "qemu/main-loop.h" -#include "exec/translate-all.h" +#include "user/page-protection.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" diff --git a/include/exec/translate-all.h b/include/exec/translate-all.h index c50661a05d..039668ff8a 100644 --- a/include/exec/translate-all.h +++ b/include/exec/translate-all.h @@ -25,9 +25,4 @@ /* translate-all.c */ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); -#ifdef CONFIG_USER_ONLY -void page_protect(tb_page_addr_t page_addr); -int page_unprotect(tb_page_addr_t address, uintptr_t pc); -#endif - #endif /* TRANSLATE_ALL_H */ diff --git a/include/user/page-protection.h b/include/user/page-protection.h new file mode 100644 index 0000000000..448c7a0344 --- /dev/null +++ b/include/user/page-protection.h @@ -0,0 +1,20 @@ +/* + * QEMU page protection declarations. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#ifndef USER_PAGE_PROTECTION_H +#define USER_PAGE_PROTECTION_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +#include "exec/translation-block.h" + +void page_protect(tb_page_addr_t page_addr); +int page_unprotect(tb_page_addr_t address, uintptr_t pc); + +#endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 471a384b22..effd3ab47e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -8,6 +8,7 @@ #include "qemu.h" #include "user/tswap-target.h" +#include "user/page-protection.h" #include "exec/page-protection.h" #include "user/guest-base.h" #include "user-internals.h" @@ -3918,7 +3919,6 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) } #ifdef USE_ELF_CORE_DUMP -#include "exec/translate-all.h" /* * Definitions to generate Intel SVR4-like core files. From 733d05bdc75c10209f72a7f07a602e85d337fd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 16:56:02 +0100 Subject: [PATCH 0570/2892] system: Remove unnecessary 'exec/translate-all.h' include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At this point "exec/translate-all.h" only declare tb_check_watchpoint(), which isn't used by any of cpu-target.c or system/physmem.c, so remove its inclusion. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-8-philmd@linaro.org> --- cpu-target.c | 1 - system/physmem.c | 1 - 2 files changed, 2 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 5480cfb721..e9fc4a5be0 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -41,7 +41,6 @@ #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" -#include "exec/translate-all.h" #include "exec/log.h" #include "hw/core/accel-cpu.h" #include "trace/trace-root.h" diff --git a/system/physmem.c b/system/physmem.c index c0e95e6f7c..1459dd15eb 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -61,7 +61,6 @@ #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" -#include "exec/translate-all.h" #include "system/replay.h" #include "exec/memory-internal.h" From 93ef2c2f15a4ddb335f100d5c31b33ebad426253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 16:54:43 +0100 Subject: [PATCH 0571/2892] accel/tcg: Move 'exec/translate-all.h' -> 'tb-internal.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/translate-all.h" is only useful to TCG accelerator, so move it to accel/tcg/, after renaming it 'tb-internal.h'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-9-philmd@linaro.org> --- accel/tcg/cputlb.c | 2 +- accel/tcg/internal-target.h | 2 +- accel/tcg/tb-internal.h | 14 ++++++++++++++ accel/tcg/tb-maint.c | 2 +- accel/tcg/translate-all.c | 2 +- accel/tcg/user-exec.c | 1 + accel/tcg/watchpoint.c | 2 +- include/exec/translate-all.h | 28 ---------------------------- 8 files changed, 20 insertions(+), 33 deletions(-) create mode 100644 accel/tcg/tb-internal.h delete mode 100644 include/exec/translate-all.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 080cbcb34d..337801feed 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -37,7 +37,7 @@ #include "exec/helper-proto-common.h" #include "qemu/atomic.h" #include "qemu/atomic128.h" -#include "exec/translate-all.h" +#include "tb-internal.h" #include "trace.h" #include "tb-hash.h" #include "internal-common.h" diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index a03c05315a..6f4ec0bd42 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -10,7 +10,7 @@ #define ACCEL_TCG_INTERNAL_TARGET_H #include "exec/exec-all.h" -#include "exec/translate-all.h" +#include "tb-internal.h" /* * Access to the various translations structures need to be serialised diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h new file mode 100644 index 0000000000..8313f90fd7 --- /dev/null +++ b/accel/tcg/tb-internal.h @@ -0,0 +1,14 @@ +/* + * TranslationBlock internal declarations (target specific) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_TB_INTERNAL_TARGET_H +#define ACCEL_TCG_TB_INTERNAL_TARGET_H + +void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); + +#endif diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 97d2e39ec0..bdf5a0b7d5 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -25,7 +25,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/tb-flush.h" -#include "exec/translate-all.h" +#include "tb-internal.h" #include "system/tcg.h" #include "tcg/tcg.h" #include "tb-hash.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index d586ac9bb1..bad3fce0ff 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -45,7 +45,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" -#include "exec/translate-all.h" +#include "tb-internal.h" #include "exec/translator.h" #include "exec/tb-flush.h" #include "qemu/bitmap.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 636932303b..815a39503f 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -33,6 +33,7 @@ #include "tcg/tcg-ldst.h" #include "internal-common.h" #include "internal-target.h" +#include "tb-internal.h" __thread uintptr_t helper_retaddr; diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index 8923301b8b..e24baead56 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -21,8 +21,8 @@ #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "exec/exec-all.h" -#include "exec/translate-all.h" #include "exec/page-protection.h" +#include "tb-internal.h" #include "system/tcg.h" #include "system/replay.h" #include "hw/core/tcg-cpu-ops.h" diff --git a/include/exec/translate-all.h b/include/exec/translate-all.h deleted file mode 100644 index 039668ff8a..0000000000 --- a/include/exec/translate-all.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Translated block handling - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#ifndef TRANSLATE_ALL_H -#define TRANSLATE_ALL_H - -#include "exec/exec-all.h" - - -/* translate-all.c */ -void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); - -#endif /* TRANSLATE_ALL_H */ From e07788a98909431ea32a7e5baf1e90b246b5b1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 16:30:17 +0100 Subject: [PATCH 0572/2892] accel/tcg: Un-inline log_pc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit log_pc() is only used within cpu-exec.c, no need to expose it via "internal-target.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-10-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 11 +++++++++++ accel/tcg/internal-target.h | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index f82870a1e2..396fa6f4a6 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -21,6 +21,7 @@ #include "qemu/qemu-print.h" #include "qapi/error.h" #include "qapi/type-helpers.h" +#include "hw/core/cpu.h" #include "hw/core/tcg-cpu-ops.h" #include "trace.h" #include "disas/disas.h" @@ -434,6 +435,16 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) return tb->tc.ptr; } +/* Return the current PC from CPU, which may be cached in TB. */ +static vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) +{ + if (tb_cflags(tb) & CF_PCREL) { + return cpu->cc->get_pc(cpu); + } else { + return tb->pc; + } +} + /* Execute a TB, and fix up the CPU state afterwards if necessary */ /* * Disable CFI checks. diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 6f4ec0bd42..0437d79829 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -72,16 +72,6 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); -/* Return the current PC from CPU, which may be cached in TB. */ -static inline vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) -{ - if (tb_cflags(tb) & CF_PCREL) { - return cpu->cc->get_pc(cpu); - } else { - return tb->pc; - } -} - /** * tcg_req_mo: * @type: TCGBar From 3e6bfabfbb12fbc19b92b03b72b948dc4f40d144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 16:20:12 +0100 Subject: [PATCH 0573/2892] accel/tcg: Move TranslationBlock declarations to 'tb-internal.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move declarations related to TranslationBlock out of the generic "internal-target.h" to "tb-internal.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-11-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 1 + accel/tcg/cputlb.c | 1 + accel/tcg/internal-target.h | 32 ------------------------------ accel/tcg/tb-internal.h | 39 +++++++++++++++++++++++++++++++++++++ accel/tcg/tb-maint.c | 1 + accel/tcg/translate-all.c | 1 + accel/tcg/translator.c | 1 + 7 files changed, 44 insertions(+), 32 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 396fa6f4a6..e9eaab223f 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -41,6 +41,7 @@ #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" +#include "tb-internal.h" #include "internal-common.h" #include "internal-target.h" diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 337801feed..b4ccf0cdcb 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -40,6 +40,7 @@ #include "tb-internal.h" #include "trace.h" #include "tb-hash.h" +#include "tb-internal.h" #include "internal-common.h" #include "internal-target.h" #ifdef CONFIG_PLUGIN diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 0437d79829..1cfa318dc6 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -36,42 +36,10 @@ static inline void page_table_config_init(void) { } void page_table_config_init(void); #endif -#ifdef CONFIG_USER_ONLY -#include "user/page-protection.h" -/* - * For user-only, page_protect sets the page read-only. - * Since most execution is already on read-only pages, and we'd need to - * account for other TBs on the same page, defer undoing any page protection - * until we receive the write fault. - */ -static inline void tb_lock_page0(tb_page_addr_t p0) -{ - page_protect(p0); -} - -static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1) -{ - page_protect(p1); -} - -static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { } -static inline void tb_unlock_pages(TranslationBlock *tb) { } -#else -void tb_lock_page0(tb_page_addr_t); -void tb_lock_page1(tb_page_addr_t, tb_page_addr_t); -void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t); -void tb_unlock_pages(TranslationBlock *); -#endif - #ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, - unsigned size, - uintptr_t retaddr); G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); #endif /* CONFIG_SOFTMMU */ -bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); - /** * tcg_req_mo: * @type: TCGBar diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index 8313f90fd7..90be61f296 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -9,6 +9,45 @@ #ifndef ACCEL_TCG_TB_INTERNAL_TARGET_H #define ACCEL_TCG_TB_INTERNAL_TARGET_H +#include "exec/cpu-all.h" +#include "exec/exec-all.h" +#include "exec/translation-block.h" + +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +/* + * For user-only, page_protect sets the page read-only. + * Since most execution is already on read-only pages, and we'd need to + * account for other TBs on the same page, defer undoing any page protection + * until we receive the write fault. + */ +static inline void tb_lock_page0(tb_page_addr_t p0) +{ + page_protect(p0); +} + +static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1) +{ + page_protect(p1); +} + +static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { } +static inline void tb_unlock_pages(TranslationBlock *tb) { } +#else +void tb_lock_page0(tb_page_addr_t); +void tb_lock_page1(tb_page_addr_t, tb_page_addr_t); +void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t); +void tb_unlock_pages(TranslationBlock *); +#endif + +#ifdef CONFIG_SOFTMMU +void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, + unsigned size, + uintptr_t retaddr); +#endif /* CONFIG_SOFTMMU */ + +bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); + void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); #endif diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index bdf5a0b7d5..8e272cf790 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -30,6 +30,7 @@ #include "tcg/tcg.h" #include "tb-hash.h" #include "tb-context.h" +#include "tb-internal.h" #include "internal-common.h" #include "internal-target.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index bad3fce0ff..572a8a8797 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -62,6 +62,7 @@ #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" +#include "tb-internal.h" #include "internal-common.h" #include "internal-target.h" #include "tcg/perf.h" diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index ff5dabc901..ce5eae4349 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -19,6 +19,7 @@ #include "tcg/tcg-op-common.h" #include "internal-target.h" #include "disas/disas.h" +#include "tb-internal.h" static void set_can_do_io(DisasContextBase *db, bool val) { From b7cc677478f329912fbc5c16ec36193c72aa9044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Dec 2024 17:02:50 +0100 Subject: [PATCH 0574/2892] accel/tcg: Really restrict cpu_io_recompile() to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 38fc4b11e03 ("accel/tcg: Restrict cpu_io_recompile() to system emulation") inadvertently restricted cpu_io_recompile() to SoftMMU. Correct to restrict to system emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241216160514.56630-1-philmd@linaro.org> --- accel/tcg/internal-target.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 1cfa318dc6..3ed81e740d 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -36,9 +36,9 @@ static inline void page_table_config_init(void) { } void page_table_config_init(void); #endif -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); -#endif /* CONFIG_SOFTMMU */ +#endif /* CONFIG_USER_ONLY */ /** * tcg_req_mo: From 970ae60e9bdcc2e831b8226a6ebeb37efdc9f5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 17:50:19 +0100 Subject: [PATCH 0575/2892] accel/tcg: Move user-related declarations out of 'exec/cpu-all.h' (1/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move declarations related to page protection under user emulation from "exec/cpu-all.h" to "user/page-protection.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-12-philmd@linaro.org> --- bsd-user/main.c | 1 + bsd-user/mmap.c | 1 + include/exec/cpu-all.h | 5 ----- include/user/page-protection.h | 8 ++++++++ linux-user/main.c | 1 + linux-user/mmap.c | 1 + linux-user/syscall.c | 1 + 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 61ca73c478..0a5bc57836 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -38,6 +38,7 @@ #include "qemu/plugin.h" #include "exec/exec-all.h" #include "user/guest-base.h" +#include "user/page-protection.h" #include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 775e905960..346f2cefd3 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" #include "exec/page-protection.h" +#include "user/page-protection.h" #include "qemu.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 1c8e0446d0..3d97323893 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -127,11 +127,6 @@ extern const TargetPageBits target_page; #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) #if defined(CONFIG_USER_ONLY) -void page_dump(FILE *f); - -typedef int (*walk_memory_regions_fn)(void *, target_ulong, - target_ulong, unsigned long); -int walk_memory_regions(void *, walk_memory_regions_fn); int page_get_flags(target_ulong address); diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 448c7a0344..ea11cf9e32 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -12,9 +12,17 @@ #error Cannot include this header from system emulation #endif +#include "cpu-param.h" +#include "exec/target_long.h" #include "exec/translation-block.h" void page_protect(tb_page_addr_t page_addr); int page_unprotect(tb_page_addr_t address, uintptr_t pc); +typedef int (*walk_memory_regions_fn)(void *, target_ulong, + target_ulong, unsigned long); + +int walk_memory_regions(void *, walk_memory_regions_fn); + +void page_dump(FILE *f); #endif diff --git a/linux-user/main.c b/linux-user/main.c index b09af8d436..06037304cb 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -39,6 +39,7 @@ #include "qemu/module.h" #include "qemu/plugin.h" #include "user/guest-base.h" +#include "user/page-protection.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" #include "gdbstub/user.h" diff --git a/linux-user/mmap.c b/linux-user/mmap.c index e4bf5d5f39..4e0444b4cb 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -22,6 +22,7 @@ #include "exec/log.h" #include "exec/page-protection.h" #include "qemu.h" +#include "user/page-protection.h" #include "user-internals.h" #include "user-mmap.h" #include "target_mman.h" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1ce4c79784..c54b199b6d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -135,6 +135,7 @@ #include "signal-common.h" #include "loader.h" #include "user-mmap.h" +#include "user/page-protection.h" #include "user/safe-syscall.h" #include "qemu/guest-random.h" #include "qemu/selfmap.h" From 62ef949bbc06f23d9f4e773f8a50c7bb191531d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 17:51:10 +0100 Subject: [PATCH 0576/2892] accel/tcg: Move user-related declarations out of 'exec/cpu-all.h' (2/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move declarations related to page protection under user emulation from "exec/cpu-all.h" to "user/page-protection.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-13-philmd@linaro.org> --- bsd-user/bsd-mem.h | 1 + include/exec/cpu-all.h | 55 -------------------------------- include/user/page-protection.h | 57 ++++++++++++++++++++++++++++++++++ target/arm/tcg/mte_helper.c | 4 ++- 4 files changed, 61 insertions(+), 56 deletions(-) diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h index eef6b222d9..f5ec0de24c 100644 --- a/bsd-user/bsd-mem.h +++ b/bsd-user/bsd-mem.h @@ -57,6 +57,7 @@ #include "qemu-bsd.h" #include "exec/page-protection.h" +#include "user/page-protection.h" extern struct bsd_shm_regions bsd_shm_regions[]; extern abi_ulong target_brk; diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 3d97323893..86cd40020c 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -130,21 +130,6 @@ extern const TargetPageBits target_page; int page_get_flags(target_ulong address); -/** - * page_set_flags: - * @start: first byte of range - * @last: last byte of range - * @flags: flags to set - * Context: holding mmap lock - * - * Modify the flags of a page and invalidate the code if necessary. - * The flag PAGE_WRITE_ORG is positioned automatically depending - * on PAGE_WRITE. The mmap_lock should already be held. - */ -void page_set_flags(target_ulong start, target_ulong last, int flags); - -void page_reset_target_data(target_ulong start, target_ulong last); - /** * page_check_range * @start: first byte of range @@ -157,46 +142,6 @@ void page_reset_target_data(target_ulong start, target_ulong last); */ bool page_check_range(target_ulong start, target_ulong last, int flags); -/** - * page_check_range_empty: - * @start: first byte of range - * @last: last byte of range - * Context: holding mmap lock - * - * Return true if the entire range [@start, @last] is unmapped. - * The memory lock must be held so that the caller will can ensure - * the result stays true until a new mapping can be installed. - */ -bool page_check_range_empty(target_ulong start, target_ulong last); - -/** - * page_find_range_empty - * @min: first byte of search range - * @max: last byte of search range - * @len: size of the hole required - * @align: alignment of the hole required (power of 2) - * - * If there is a range [x, x+@len) within [@min, @max] such that - * x % @align == 0, then return x. Otherwise return -1. - * The memory lock must be held, as the caller will want to ensure - * the returned range stays empty until a new mapping can be installed. - */ -target_ulong page_find_range_empty(target_ulong min, target_ulong max, - target_ulong len, target_ulong align); - -/** - * page_get_target_data(address) - * @address: guest virtual address - * - * Return TARGET_PAGE_DATA_SIZE bytes of out-of-band data to associate - * with the guest page at @address, allocating it if necessary. The - * caller should already have verified that the address is valid. - * - * The memory will be freed when the guest page is deallocated, - * e.g. with the munmap system call. - */ -void *page_get_target_data(target_ulong address) - __attribute__((returns_nonnull)); #endif CPUArchState *cpu_copy(CPUArchState *env); diff --git a/include/user/page-protection.h b/include/user/page-protection.h index ea11cf9e32..d21fab1aaf 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -18,6 +18,63 @@ void page_protect(tb_page_addr_t page_addr); int page_unprotect(tb_page_addr_t address, uintptr_t pc); + +/** + * page_set_flags: + * @start: first byte of range + * @last: last byte of range + * @flags: flags to set + * Context: holding mmap lock + * + * Modify the flags of a page and invalidate the code if necessary. + * The flag PAGE_WRITE_ORG is positioned automatically depending + * on PAGE_WRITE. The mmap_lock should already be held. + */ +void page_set_flags(target_ulong start, target_ulong last, int flags); + +void page_reset_target_data(target_ulong start, target_ulong last); + +/** + * page_check_range_empty: + * @start: first byte of range + * @last: last byte of range + * Context: holding mmap lock + * + * Return true if the entire range [@start, @last] is unmapped. + * The memory lock must be held so that the caller will can ensure + * the result stays true until a new mapping can be installed. + */ +bool page_check_range_empty(target_ulong start, target_ulong last); + +/** + * page_find_range_empty + * @min: first byte of search range + * @max: last byte of search range + * @len: size of the hole required + * @align: alignment of the hole required (power of 2) + * + * If there is a range [x, x+@len) within [@min, @max] such that + * x % @align == 0, then return x. Otherwise return -1. + * The memory lock must be held, as the caller will want to ensure + * the returned range stays empty until a new mapping can be installed. + */ +target_ulong page_find_range_empty(target_ulong min, target_ulong max, + target_ulong len, target_ulong align); + +/** + * page_get_target_data(address) + * @address: guest virtual address + * + * Return TARGET_PAGE_DATA_SIZE bytes of out-of-band data to associate + * with the guest page at @address, allocating it if necessary. The + * caller should already have verified that the address is valid. + * + * The memory will be freed when the guest page is deallocated, + * e.g. with the munmap system call. + */ +__attribute__((returns_nonnull)) +void *page_get_target_data(target_ulong address); + typedef int (*walk_memory_regions_fn)(void *, target_ulong, target_ulong, unsigned long); diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index b017b26d07..7943ae2d60 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -23,7 +23,9 @@ #include "internals.h" #include "exec/exec-all.h" #include "exec/page-protection.h" -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#else #include "exec/ram_addr.h" #endif #include "exec/cpu_ldst.h" From 187b7ca96a3e682226ba43a3b4b3d4c8954834b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 18:08:43 +0100 Subject: [PATCH 0577/2892] accel/tcg: Move user-related declarations out of 'exec/cpu-all.h' (3/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move declarations related to page protection under user emulation from "exec/cpu-all.h" to "user/page-protection.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-14-philmd@linaro.org> --- bsd-user/qemu.h | 1 + include/exec/cpu-all.h | 12 ------------ include/user/page-protection.h | 12 ++++++++++++ linux-user/qemu.h | 1 + target/arm/tcg/sve_helper.c | 3 +++ target/hppa/op_helper.c | 3 +++ target/sparc/ldst_helper.c | 3 +++ 7 files changed, 23 insertions(+), 12 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 3736c41786..04faee459d 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -26,6 +26,7 @@ #include "exec/exec-all.h" #include "user/abitypes.h" +#include "user/page-protection.h" extern char **environ; diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 86cd40020c..73b11f58ab 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -130,18 +130,6 @@ extern const TargetPageBits target_page; int page_get_flags(target_ulong address); -/** - * page_check_range - * @start: first byte of range - * @len: length of range - * @flags: flags required for each page - * - * Return true if every page in [@start, @start+@len) has @flags set. - * Return false if any page is unmapped. Thus testing flags == 0 is - * equivalent to testing for flags == PAGE_VALID. - */ -bool page_check_range(target_ulong start, target_ulong last, int flags); - #endif CPUArchState *cpu_copy(CPUArchState *env); diff --git a/include/user/page-protection.h b/include/user/page-protection.h index d21fab1aaf..bdd98a37de 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -34,6 +34,18 @@ void page_set_flags(target_ulong start, target_ulong last, int flags); void page_reset_target_data(target_ulong start, target_ulong last); +/** + * page_check_range + * @start: first byte of range + * @len: length of range + * @flags: flags required for each page + * + * Return true if every page in [@start, @start+@len) has @flags set. + * Return false if any page is unmapped. Thus testing flags == 0 is + * equivalent to testing for flags == PAGE_VALID. + */ +bool page_check_range(target_ulong start, target_ulong last, int flags); + /** * page_check_range_empty: * @start: first byte of range diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 67bc81b149..5f00750151 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -5,6 +5,7 @@ #include "exec/cpu_ldst.h" #include "user/abitypes.h" +#include "user/page-protection.h" #include "syscall_defs.h" #include "target_syscall.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 85fe3cae3e..d0865dece3 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -29,6 +29,9 @@ #include "vec_internal.h" #include "sve_ldst_internal.h" #include "hw/core/tcg-cpu-ops.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif /* Return a value for NZCV as per the ARM PredTest pseudofunction. diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 744325969f..beb8f88799 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -25,6 +25,9 @@ #include "exec/cpu_ldst.h" #include "qemu/timer.h" #include "trace.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp) { diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index d92c9f1593..4c54e45655 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -26,6 +26,9 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif #include "asi.h" //#define DEBUG_MMU From f47dcf519de985501339b83a46eab7db692883b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 18:01:41 +0100 Subject: [PATCH 0578/2892] accel/tcg: Move user-related declarations out of 'exec/cpu-all.h' (4/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move declarations related to page protection under user emulation from "exec/cpu-all.h" to "user/page-protection.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-15-philmd@linaro.org> --- accel/tcg/tb-maint.c | 3 +++ bsd-user/signal.c | 1 + cpu-target.c | 1 + include/exec/cpu-all.h | 6 ------ include/user/page-protection.h | 2 ++ linux-user/arm/cpu_loop.c | 1 + linux-user/signal.c | 1 + target/arm/tcg/helper-a64.c | 3 +++ target/s390x/tcg/mem_helper.c | 4 +++- 9 files changed, 15 insertions(+), 7 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 8e272cf790..3f1bebf6ab 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -33,6 +33,9 @@ #include "tb-internal.h" #include "internal-common.h" #include "internal-target.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif /* List iterators for lists of tagged pointers in TranslationBlock. */ diff --git a/bsd-user/signal.c b/bsd-user/signal.c index da49b9bffc..edbcd461bf 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -22,6 +22,7 @@ #include "qemu/log.h" #include "qemu.h" #include "exec/page-protection.h" +#include "user/page-protection.h" #include "user/tswap-target.h" #include "gdbstub/user.h" #include "signal-common.h" diff --git a/cpu-target.c b/cpu-target.c index e9fc4a5be0..7a2efa890c 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -29,6 +29,7 @@ #include "migration/vmstate.h" #ifdef CONFIG_USER_ONLY #include "qemu.h" +#include "user/page-protection.h" #else #include "hw/core/sysemu-cpu-ops.h" #include "exec/address-spaces.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 73b11f58ab..f7eea33b10 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -126,12 +126,6 @@ extern const TargetPageBits target_page; #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) -#if defined(CONFIG_USER_ONLY) - -int page_get_flags(target_ulong address); - -#endif - CPUArchState *cpu_copy(CPUArchState *env); /* Flags for use in ENV->INTERRUPT_PENDING. diff --git a/include/user/page-protection.h b/include/user/page-protection.h index bdd98a37de..51daa18648 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -19,6 +19,8 @@ void page_protect(tb_page_addr_t page_addr); int page_unprotect(tb_page_addr_t address, uintptr_t pc); +int page_get_flags(target_ulong address); + /** * page_set_flags: * @start: first byte of range diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index ec665862d9..709f718a99 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -25,6 +25,7 @@ #include "signal-common.h" #include "semihosting/common-semi.h" #include "exec/page-protection.h" +#include "user/page-protection.h" #include "target/arm/syndrome.h" #define get_user_code_u32(x, gaddr, env) \ diff --git a/linux-user/signal.c b/linux-user/signal.c index 9b6d772882..a191e9b26f 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -33,6 +33,7 @@ #include "trace.h" #include "signal-common.h" #include "host-signal.h" +#include "user/page-protection.h" #include "user/safe-syscall.h" #include "tcg/tcg.h" diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 35dce4bef3..b6af2a59d6 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -34,6 +34,9 @@ #include "qemu/atomic128.h" #include "fpu/softfloat.h" #include /* for crc32 */ +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif /* C2.4.7 Multiply and divide */ /* special cases for 0 and LLONG_MIN are mandated by the standard */ diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 0e12dae2aa..307388e5bd 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -31,7 +31,9 @@ #include "qemu/int128.h" #include "qemu/atomic128.h" -#if !defined(CONFIG_USER_ONLY) +#if defined(CONFIG_USER_ONLY) +#include "user/page-protection.h" +#else #include "hw/s390x/storage-keys.h" #include "hw/boards.h" #endif From fb5c28e1955537228fe59a901e6cf6258da682d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Nov 2024 18:28:43 +0100 Subject: [PATCH 0579/2892] user: Forward declare target_cpu_copy_regs structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-16-philmd@linaro.org> --- linux-user/aarch64/cpu_loop.c | 2 +- linux-user/alpha/cpu_loop.c | 2 +- linux-user/arm/cpu_loop.c | 2 +- linux-user/cpu_loop-common.h | 5 ++++- linux-user/hexagon/cpu_loop.c | 2 +- linux-user/hppa/cpu_loop.c | 2 +- linux-user/i386/cpu_loop.c | 2 +- linux-user/loongarch64/cpu_loop.c | 2 +- linux-user/m68k/cpu_loop.c | 2 +- linux-user/microblaze/cpu_loop.c | 2 +- linux-user/mips/cpu_loop.c | 2 +- linux-user/openrisc/cpu_loop.c | 2 +- linux-user/ppc/cpu_loop.c | 2 +- linux-user/riscv/cpu_loop.c | 2 +- linux-user/s390x/cpu_loop.c | 2 +- linux-user/sh4/cpu_loop.c | 2 +- linux-user/sparc/cpu_loop.c | 2 +- linux-user/xtensa/cpu_loop.c | 2 +- 18 files changed, 21 insertions(+), 18 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 71cdc8be50..77f857a821 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -185,7 +185,7 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { ARMCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 2ea039aa71..b5403ed0e4 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -173,7 +173,7 @@ void cpu_loop(CPUAlphaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 709f718a99..be1f11022a 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -513,7 +513,7 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { CPUState *cpu = env_cpu(env); TaskState *ts = get_task_state(cpu); diff --git a/linux-user/cpu_loop-common.h b/linux-user/cpu_loop-common.h index e644d2ef90..aca51f5323 100644 --- a/linux-user/cpu_loop-common.h +++ b/linux-user/cpu_loop-common.h @@ -27,5 +27,8 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code); #define EXCP_DUMP(env, fmt, code) \ target_exception_dump(env, fmt, code) -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs); +typedef struct target_pt_regs target_pt_regs; + +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); + #endif diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 40db596301..750150a78d 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -79,7 +79,7 @@ void cpu_loop(CPUHexagonState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { env->gpr[HEX_REG_PC] = regs->sepc; env->gpr[HEX_REG_SP] = regs->sp; diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 23b38ff9b2..503a204460 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -188,7 +188,7 @@ void cpu_loop(CPUHPPAState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; for (i = 1; i < 32; i++) { diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 7a35215278..e06aa23de4 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -331,7 +331,7 @@ static void target_cpu_free(void *obj) g_free(obj); } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { CPUState *cpu = env_cpu(env); bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index 73d7b6796a..935d5bcf2c 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -97,7 +97,7 @@ void cpu_loop(CPULoongArchState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index f79b8e4ab0..69cdce9551 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -92,7 +92,7 @@ void cpu_loop(CPUM68KState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { CPUState *cpu = env_cpu(env); TaskState *ts = get_task_state(cpu); diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 212e62d0a6..ee4ba76869 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -127,7 +127,7 @@ void cpu_loop(CPUMBState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { env->regs[0] = regs->r0; env->regs[1] = regs->r1; diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 462387a073..1331a9a2b8 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -211,7 +211,7 @@ done_syscall: } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { CPUState *cpu = env_cpu(env); TaskState *ts = get_task_state(cpu); diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index a7aa586c8f..1bf9542d16 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -83,7 +83,7 @@ void cpu_loop(CPUOpenRISCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 02204ad8be..e168f1ce94 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -378,7 +378,7 @@ void cpu_loop(CPUPPCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 0af533e186..a72bc76da3 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -94,7 +94,7 @@ void cpu_loop(CPURISCVState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { CPUState *cpu = env_cpu(env); TaskState *ts = get_task_state(cpu); diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 8b7ac2879e..42e4d24102 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -180,7 +180,7 @@ void cpu_loop(CPUS390XState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; for (i = 0; i < 16; i++) { diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index c805f9db11..937d9fa265 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -81,7 +81,7 @@ void cpu_loop(CPUSH4State *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 50424a54df..58a2464da9 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -357,7 +357,7 @@ void cpu_loop (CPUSPARCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; env->pc = regs->pc; diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index d51ce05392..7564e25a96 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -238,7 +238,7 @@ void cpu_loop(CPUXtensaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; for (i = 0; i < 16; ++i) { From b74c89815841abd80cca9d2bba13b19afb62d1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Nov 2024 17:54:37 +0100 Subject: [PATCH 0580/2892] user: Move 'linux-user/cpu_loop-common.h' -> 'user/cpu_loop.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "linux-user/cpu_loop-common.h" is generic enough to be used by bsd-user, so rename it as "user/cpu_loop.h". Mechanical change running: $ sed -i -e 's,cpu_loop-common.h,user/cpu_loop.h,' \ $(git grep -l cpu_loop-common.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-17-philmd@linaro.org> --- linux-user/cpu_loop-common.h => include/user/cpu_loop.h | 4 ++-- linux-user/aarch64/cpu_loop.c | 2 +- linux-user/alpha/cpu_loop.c | 2 +- linux-user/arm/cpu_loop.c | 2 +- linux-user/hexagon/cpu_loop.c | 2 +- linux-user/hppa/cpu_loop.c | 2 +- linux-user/i386/cpu_loop.c | 2 +- linux-user/loongarch64/cpu_loop.c | 2 +- linux-user/m68k/cpu_loop.c | 2 +- linux-user/main.c | 2 +- linux-user/microblaze/cpu_loop.c | 2 +- linux-user/mips/cpu_loop.c | 2 +- linux-user/openrisc/cpu_loop.c | 2 +- linux-user/ppc/cpu_loop.c | 2 +- linux-user/riscv/cpu_loop.c | 2 +- linux-user/s390x/cpu_loop.c | 2 +- linux-user/sh4/cpu_loop.c | 2 +- linux-user/sparc/cpu_loop.c | 2 +- linux-user/syscall.c | 2 +- linux-user/xtensa/cpu_loop.c | 2 +- 20 files changed, 21 insertions(+), 21 deletions(-) rename linux-user/cpu_loop-common.h => include/user/cpu_loop.h (95%) diff --git a/linux-user/cpu_loop-common.h b/include/user/cpu_loop.h similarity index 95% rename from linux-user/cpu_loop-common.h rename to include/user/cpu_loop.h index aca51f5323..8e2df23275 100644 --- a/linux-user/cpu_loop-common.h +++ b/include/user/cpu_loop.h @@ -17,8 +17,8 @@ * along with this program; if not, see . */ -#ifndef CPU_LOOP_COMMON_H -#define CPU_LOOP_COMMON_H +#ifndef USER_CPU_LOOP_H +#define USER_CPU_LOOP_H #include "exec/log.h" #include "special-errno.h" diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 77f857a821..c5d8a483a3 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "qemu/guest-random.h" #include "semihosting/common-semi.h" diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index b5403ed0e4..80ad536c5f 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUAlphaState *env) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index be1f11022a..10d8561f9b 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu.h" #include "user-internals.h" #include "elf.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "semihosting/common-semi.h" #include "exec/page-protection.h" diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 750150a78d..e18a0183b5 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "internal.h" diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 503a204460..890e758cd1 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" static abi_ulong hppa_lws(CPUHPPAState *env) diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index e06aa23de4..d96d5553fa 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu.h" #include "qemu/timer.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "user-mmap.h" diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index 935d5bcf2c..0614d3de22 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPULoongArchState *env) diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 69cdce9551..5da91b997a 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUM68KState *env) diff --git a/linux-user/main.c b/linux-user/main.c index 06037304cb..b97634a32d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -50,7 +50,7 @@ #include "elf.h" #include "trace/control.h" #include "target_elf.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "crypto/init.h" #include "fd-trans.h" #include "signal-common.h" diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index ee4ba76869..87236c166f 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUMBState *env) diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 1331a9a2b8..6405806eb0 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "elf.h" #include "internal.h" diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 1bf9542d16..306b4f8eb4 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUOpenRISCState *env) diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index e168f1ce94..2a0efaffcd 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu.h" #include "qemu/timer.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index a72bc76da3..3ac8bbfec1 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu/error-report.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "elf.h" #include "semihosting/common-semi.h" diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 42e4d24102..c9124444ed 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index 937d9fa265..ee9eff3428 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUSH4State *env) diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 58a2464da9..68f1e8ecd4 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #define SPARC64_STACK_BIAS 2047 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c54b199b6d..1b335688f1 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -143,7 +143,7 @@ #include "special-errno.h" #include "qapi/error.h" #include "fd-trans.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index 7564e25a96..c0fcf743e7 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" static void xtensa_rfw(CPUXtensaState *env) From 166a4b6e43b8904a150a946243457b7db9567c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Nov 2024 17:56:56 +0100 Subject: [PATCH 0581/2892] user: Declare cpu_loop() once in 'user/cpu_loop.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare cpu_loop() once in "user/cpu_loop.h". bsd-user gets the G_NORETURN attribute. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-18-philmd@linaro.org> --- bsd-user/aarch64/target_arch_cpu.h | 2 +- bsd-user/arm/target_arch_cpu.h | 2 +- bsd-user/i386/target_arch_cpu.h | 2 +- bsd-user/qemu.h | 2 +- bsd-user/riscv/target_arch_cpu.h | 2 +- bsd-user/x86_64/target_arch_cpu.h | 2 +- include/user/cpu_loop.h | 2 ++ linux-user/user-internals.h | 1 - 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bsd-user/aarch64/target_arch_cpu.h b/bsd-user/aarch64/target_arch_cpu.h index b288e0d069..87fbf6d677 100644 --- a/bsd-user/aarch64/target_arch_cpu.h +++ b/bsd-user/aarch64/target_arch_cpu.h @@ -43,7 +43,7 @@ static inline void target_cpu_init(CPUARMState *env, } -static inline void target_cpu_loop(CPUARMState *env) +static inline G_NORETURN void target_cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); int trapnr, ec, fsc, si_code, si_signo; diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 517d008764..bc2eaa0bf4 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -37,7 +37,7 @@ static inline void target_cpu_init(CPUARMState *env, } } -static inline void target_cpu_loop(CPUARMState *env) +static inline G_NORETURN void target_cpu_loop(CPUARMState *env) { int trapnr, si_signo, si_code; CPUState *cs = env_cpu(env); diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 9bf2c4244b..5d4c931dec 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -102,7 +102,7 @@ static inline void target_cpu_init(CPUX86State *env, env->segs[R_FS].selector = 0; } -static inline void target_cpu_loop(CPUX86State *env) +static inline G_NORETURN void target_cpu_loop(CPUX86State *env) { CPUState *cs = env_cpu(env); int trapnr; diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 04faee459d..3eaa14f3f5 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -26,6 +26,7 @@ #include "exec/exec-all.h" #include "user/abitypes.h" +#include "user/cpu_loop.h" #include "user/page-protection.h" extern char **environ; @@ -187,7 +188,6 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg5, abi_long arg6); void gemu_log(const char *fmt, ...) G_GNUC_PRINTF(1, 2); extern __thread CPUState *thread_cpu; -void cpu_loop(CPUArchState *env); char *target_strerror(int err); int get_osversion(void); void fork_start(void); diff --git a/bsd-user/riscv/target_arch_cpu.h b/bsd-user/riscv/target_arch_cpu.h index a93ea3915a..ef92f00480 100644 --- a/bsd-user/riscv/target_arch_cpu.h +++ b/bsd-user/riscv/target_arch_cpu.h @@ -37,7 +37,7 @@ static inline void target_cpu_init(CPURISCVState *env, env->pc = regs->sepc; } -static inline void target_cpu_loop(CPURISCVState *env) +static inline G_NORETURN void target_cpu_loop(CPURISCVState *env) { CPUState *cs = env_cpu(env); int trapnr; diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 4094d61da1..f82042e30a 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -110,7 +110,7 @@ static inline void target_cpu_init(CPUX86State *env, cpu_x86_load_seg(env, R_GS, 0); } -static inline void target_cpu_loop(CPUX86State *env) +static inline G_NORETURN void target_cpu_loop(CPUX86State *env) { CPUState *cs = env_cpu(env); int trapnr; diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index 8e2df23275..b0d4704018 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -23,6 +23,8 @@ #include "exec/log.h" #include "special-errno.h" +G_NORETURN void cpu_loop(CPUArchState *env); + void target_exception_dump(CPUArchState *env, const char *fmt, int code); #define EXCP_DUMP(env, fmt, code) \ target_exception_dump(env, fmt, code) diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 46ffc093f4..b9b05c1d11 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -65,7 +65,6 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8); extern __thread CPUState *thread_cpu; -G_NORETURN void cpu_loop(CPUArchState *env); abi_long get_errno(abi_long ret); const char *target_strerror(int err); int get_osversion(void); From 1bf0d6e476f34aadda8b052f747a5a5026119de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Nov 2024 18:16:43 +0100 Subject: [PATCH 0582/2892] user: Move various declarations out of 'exec/exec-all.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move various declarations related to user emulation from "exec/exec-all.h" to "user/cpu_loop.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241212185341.2857-19-philmd@linaro.org> --- accel/tcg/user-exec.c | 1 + bsd-user/signal.c | 1 + include/exec/exec-all.h | 55 +------------------------------------ include/user/cpu_loop.h | 54 ++++++++++++++++++++++++++++++++++++ linux-user/signal.c | 1 + target/arm/tcg/mte_helper.c | 1 + 6 files changed, 59 insertions(+), 54 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 815a39503f..0561c4f6dc 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -24,6 +24,7 @@ #include "qemu/bitops.h" #include "qemu/rcu.h" #include "exec/cpu_ldst.h" +#include "user/cpu_loop.h" #include "qemu/main-loop.h" #include "user/page-protection.h" #include "exec/page-protection.h" diff --git a/bsd-user/signal.c b/bsd-user/signal.c index edbcd461bf..b4e1458237 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu.h" +#include "user/cpu_loop.h" #include "exec/page-protection.h" #include "user/page-protection.h" #include "user/tswap-target.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index b5ea607cf4..c4a6ad3af2 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -22,7 +22,6 @@ #include "cpu.h" #if defined(CONFIG_USER_ONLY) -#include "exec/abi_ptr.h" #include "exec/cpu_ldst.h" #endif #include "exec/mmu-access-type.h" @@ -518,60 +517,8 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, return get_page_addr_code_hostp(env, addr, NULL); } -#if defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) -/** - * adjust_signal_pc: - * @pc: raw pc from the host signal ucontext_t. - * @is_write: host memory operation was write, or read-modify-write. - * - * Alter @pc as required for unwinding. Return the type of the - * guest memory access -- host reads may be for guest execution. - */ -MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); - -/** - * handle_sigsegv_accerr_write: - * @cpu: the cpu context - * @old_set: the sigset_t from the signal ucontext_t - * @host_pc: the host pc, adjusted for the signal - * @host_addr: the host address of the fault - * - * Return true if the write fault has been handled, and should be re-tried. - */ -bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, - uintptr_t host_pc, abi_ptr guest_addr); - -/** - * cpu_loop_exit_sigsegv: - * @cpu: the cpu context - * @addr: the guest address of the fault - * @access_type: access was read/write/execute - * @maperr: true for invalid page, false for permission fault - * @ra: host pc for unwinding - * - * Use the TCGCPUOps hook to record cpu state, do guest operating system - * specific things to raise SIGSEGV, and jump to the main cpu loop. - */ -G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); - -/** - * cpu_loop_exit_sigbus: - * @cpu: the cpu context - * @addr: the guest address of the alignment fault - * @access_type: access was read/write/execute - * @ra: host pc for unwinding - * - * Use the TCGCPUOps hook to record cpu state, do guest operating system - * specific things to raise SIGBUS, and jump to the main cpu loop. - */ -G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, - MMUAccessType access_type, - uintptr_t ra); - -#else void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index b0d4704018..589c66543f 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -20,9 +20,63 @@ #ifndef USER_CPU_LOOP_H #define USER_CPU_LOOP_H +#include "exec/abi_ptr.h" +#include "exec/mmu-access-type.h" #include "exec/log.h" +#include "exec/target_long.h" #include "special-errno.h" +/** + * adjust_signal_pc: + * @pc: raw pc from the host signal ucontext_t. + * @is_write: host memory operation was write, or read-modify-write. + * + * Alter @pc as required for unwinding. Return the type of the + * guest memory access -- host reads may be for guest execution. + */ +MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); + +/** + * handle_sigsegv_accerr_write: + * @cpu: the cpu context + * @old_set: the sigset_t from the signal ucontext_t + * @host_pc: the host pc, adjusted for the signal + * @host_addr: the host address of the fault + * + * Return true if the write fault has been handled, and should be re-tried. + */ +bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, + uintptr_t host_pc, abi_ptr guest_addr); + +/** + * cpu_loop_exit_sigsegv: + * @cpu: the cpu context + * @addr: the guest address of the fault + * @access_type: access was read/write/execute + * @maperr: true for invalid page, false for permission fault + * @ra: host pc for unwinding + * + * Use the TCGCPUOps hook to record cpu state, do guest operating system + * specific things to raise SIGSEGV, and jump to the main cpu loop. + */ +G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); + +/** + * cpu_loop_exit_sigbus: + * @cpu: the cpu context + * @addr: the guest address of the alignment fault + * @access_type: access was read/write/execute + * @ra: host pc for unwinding + * + * Use the TCGCPUOps hook to record cpu state, do guest operating system + * specific things to raise SIGBUS, and jump to the main cpu loop. + */ +G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, + uintptr_t ra); + G_NORETURN void cpu_loop(CPUArchState *env); void target_exception_dump(CPUArchState *env, const char *fmt, int code); diff --git a/linux-user/signal.c b/linux-user/signal.c index a191e9b26f..087c4d270e 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -33,6 +33,7 @@ #include "trace.h" #include "signal-common.h" #include "host-signal.h" +#include "user/cpu_loop.h" #include "user/page-protection.h" #include "user/safe-syscall.h" #include "tcg/tcg.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 7943ae2d60..f72ce2ae0d 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #ifdef CONFIG_USER_ONLY +#include "user/cpu_loop.h" #include "user/page-protection.h" #else #include "exec/ram_addr.h" From 92b3938ca63ba57be3752a6bc03f5c9dcc15887f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Nov 2024 11:24:56 +0100 Subject: [PATCH 0583/2892] target/loongarch: Declare loongarch_cpu_dump_state() locally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit loongarch_cpu_dump_state() is not used outside of cpu.c, no need to expose its prototype. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20241115152053.66442-3-philmd@linaro.org> --- target/loongarch/cpu.c | 2 +- target/loongarch/internals.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 078766feaf..fa838dce2e 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -742,7 +742,7 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) return oc; } -void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) +static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) { CPULoongArchState *env = cpu_env(cs); int i; diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 1a02427627..0655ac948b 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -18,8 +18,6 @@ void loongarch_translate_init(void); -void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags); - void G_NORETURN do_raise_exception(CPULoongArchState *env, uint32_t exception, uintptr_t pc); From 32cf0ac2ccef1182705531a5383d432b3c76d995 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Fri, 19 Jan 2024 15:40:03 +0100 Subject: [PATCH 0584/2892] target/sparc: Uninline cpu_get_tb_cpu_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Required to compile accel/tcg/translate-all.c once for softmmu targets. The function gets quite big for some targets so uninlining makes sense. Reviewed-by: Richard Henderson Signed-off-by: Anton Johansson Message-Id: <20240119144024.14289-14-anjo@rev.ng> [PMD: Only take SPARC part] Signed-off-by: Philippe Mathieu-Daudé --- target/sparc/cpu.c | 38 ++++++++++++++++++++++++++++++++++++++ target/sparc/cpu.h | 39 ++------------------------------------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 8f494c286a..b11f3248d8 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -713,6 +713,44 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs, cpu->env.npc = tb->cs_base; } +void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + uint32_t flags; + *pc = env->pc; + *cs_base = env->npc; + flags = cpu_mmu_index(env_cpu(env), false); +#ifndef CONFIG_USER_ONLY + if (cpu_supervisor_mode(env)) { + flags |= TB_FLAG_SUPER; + } +#endif +#ifdef TARGET_SPARC64 +#ifndef CONFIG_USER_ONLY + if (cpu_hypervisor_mode(env)) { + flags |= TB_FLAG_HYPER; + } +#endif + if (env->pstate & PS_AM) { + flags |= TB_FLAG_AM_ENABLED; + } + if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) { + flags |= TB_FLAG_FPU_ENABLED; + } + flags |= env->asi << TB_FLAG_ASI_SHIFT; +#else + if (env->psref) { + flags |= TB_FLAG_FPU_ENABLED; + } +#ifndef CONFIG_USER_ONLY + if (env->fsr_qne) { + flags |= TB_FLAG_FSR_QNE; + } +#endif /* !CONFIG_USER_ONLY */ +#endif /* TARGET_SPARC64 */ + *pflags = flags; +} + static bool sparc_cpu_has_work(CPUState *cs) { return (cs->interrupt_request & CPU_INTERRUPT_HARD) && diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index f517e5a383..e9ccec6175 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -747,43 +747,8 @@ trap_state* cpu_tsptr(CPUSPARCState* env); #define TB_FLAG_FSR_QNE (1 << 8) #define TB_FLAG_ASI_SHIFT 24 -static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - uint32_t flags; - *pc = env->pc; - *cs_base = env->npc; - flags = cpu_mmu_index(env_cpu(env), false); -#ifndef CONFIG_USER_ONLY - if (cpu_supervisor_mode(env)) { - flags |= TB_FLAG_SUPER; - } -#endif -#ifdef TARGET_SPARC64 -#ifndef CONFIG_USER_ONLY - if (cpu_hypervisor_mode(env)) { - flags |= TB_FLAG_HYPER; - } -#endif - if (env->pstate & PS_AM) { - flags |= TB_FLAG_AM_ENABLED; - } - if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) { - flags |= TB_FLAG_FPU_ENABLED; - } - flags |= env->asi << TB_FLAG_ASI_SHIFT; -#else - if (env->psref) { - flags |= TB_FLAG_FPU_ENABLED; - } -#ifndef CONFIG_USER_ONLY - if (env->fsr_qne) { - flags |= TB_FLAG_FSR_QNE; - } -#endif /* !CONFIG_USER_ONLY */ -#endif /* TARGET_SPARC64 */ - *pflags = flags; -} +void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags); static inline bool tb_fpu_enabled(int tb_flags) { From fc3630b2a9d17c7bc7cfd03a15cf91d7fdd26355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Nov 2024 11:03:46 +0100 Subject: [PATCH 0585/2892] target/sparc: Move sparc_restore_state_to_opc() to cpu.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most targets define their restore_state_to_opc() handler in cpu.c. In order to keep SPARC aligned, move sparc_restore_state_to_opc() from translate.c to cpu.c. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20241115152053.66442-4-philmd@linaro.org> [PMD: Move definitions to new target/sparc/translate.h] --- target/sparc/cpu.c | 24 ++++++++++++++++++++++++ target/sparc/cpu.h | 4 ---- target/sparc/translate.c | 31 +------------------------------ target/sparc/translate.h | 17 +++++++++++++++++ 4 files changed, 42 insertions(+), 34 deletions(-) create mode 100644 target/sparc/translate.h diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index b11f3248d8..fc0c66afec 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -27,6 +27,7 @@ #include "qapi/visitor.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" +#include "target/sparc/translate.h" //#define DEBUG_FEATURES @@ -751,6 +752,29 @@ void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, *pflags = flags; } +static void sparc_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + CPUSPARCState *env = cpu_env(cs); + target_ulong pc = data[0]; + target_ulong npc = data[1]; + + env->pc = pc; + if (npc == DYNAMIC_PC) { + /* dynamic NPC: already stored */ + } else if (npc & JUMP_PC) { + /* jump PC: use 'cond' and the jump targets of the translation */ + if (env->cond) { + env->npc = npc & ~3; + } else { + env->npc = pc + 4; + } + } else { + env->npc = npc; + } +} + static bool sparc_cpu_has_work(CPUState *cs) { return (cs->interrupt_request & CPU_INTERRUPT_HARD) && diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index e9ccec6175..5c981234bb 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -607,12 +607,8 @@ int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr, uint8_t *buf, int len, bool is_write); #endif - /* translate.c */ void sparc_tcg_init(void); -void sparc_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data); /* fop_helper.c */ target_ulong cpu_get_fsr(CPUSPARCState *); diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 322319a128..ac06377231 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -30,6 +30,7 @@ #include "exec/log.h" #include "fpu/softfloat.h" #include "asi.h" +#include "target/sparc/translate.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -101,13 +102,6 @@ # define MAXTL_MASK 0 #endif -/* Dynamic PC, must exit to main loop. */ -#define DYNAMIC_PC 1 -/* Dynamic PC, one of two values according to jump_pc[T2]. */ -#define JUMP_PC 2 -/* Dynamic PC, may lookup next TB. */ -#define DYNAMIC_PC_LOOKUP 3 - #define DISAS_EXIT DISAS_TARGET_0 /* global register indexes */ @@ -5881,26 +5875,3 @@ void sparc_tcg_init(void) gregnames[i]); } } - -void sparc_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data) -{ - CPUSPARCState *env = cpu_env(cs); - target_ulong pc = data[0]; - target_ulong npc = data[1]; - - env->pc = pc; - if (npc == DYNAMIC_PC) { - /* dynamic NPC: already stored */ - } else if (npc & JUMP_PC) { - /* jump PC: use 'cond' and the jump targets of the translation */ - if (env->cond) { - env->npc = npc & ~3; - } else { - env->npc = pc + 4; - } - } else { - env->npc = npc; - } -} diff --git a/target/sparc/translate.h b/target/sparc/translate.h new file mode 100644 index 0000000000..a46fa4f124 --- /dev/null +++ b/target/sparc/translate.h @@ -0,0 +1,17 @@ +/* + * QEMU translation definitions for SPARC + * + * Copyright (c) 2024 Linaro, Ltd + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef SPARC_TRANSLATION_H +#define SPARC_TRANSLATION_H + +/* Dynamic PC, must exit to main loop. */ +#define DYNAMIC_PC 1 +/* Dynamic PC, one of two values according to jump_pc[T2]. */ +#define JUMP_PC 2 +/* Dynamic PC, may lookup next TB. */ +#define DYNAMIC_PC_LOOKUP 3 + +#endif From 5c09d295e0e4cd9c252fb2c8f7f71b0cbd3a7b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Nov 2024 19:17:24 +0000 Subject: [PATCH 0586/2892] exec/cpu-all: Include 'cpu.h' earlier so MMU_USER_IDX is always defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include "cpu.h" earlier to get the MMU_USER_IDX definition soon enough and avoid when refactoring unrelated headers: In file included from include/exec/translator.h:271, from ../../accel/tcg/translator.c:13: include/exec/cpu-all.h: In function ‘cpu_mmu_index’: include/exec/cpu-all.h:274:12: error: ‘MMU_USER_IDX’ undeclared (first use in this function) 274 | return MMU_USER_IDX; | ^~~~~~~~~~~~ include/exec/cpu-all.h:274:12: note: each undeclared identifier is reported only once for each function it appears in ninja: build stopped: subcommand failed. We need to forward-declare cpu_mmu_index() to avoid on user emulation: In file included from include/exec/cpu-all.h:263, from include/exec/translator.h:271, from ../../accel/tcg/translator.c:13: ../../target/sparc/cpu.h: In function ‘cpu_get_tb_cpu_state’: ../../target/sparc/cpu.h:757:13: error: implicit declaration of function ‘cpu_mmu_index’ [-Werror=implicit-function-declaration] 757 | flags = cpu_mmu_index(env_cpu(env), false); | ^~~~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241218155202.71931-5-philmd@linaro.org> --- include/exec/cpu-all.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index f7eea33b10..09f537d06f 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -180,8 +180,12 @@ CPUArchState *cpu_copy(CPUArchState *env); | CPU_INTERRUPT_TGT_EXT_3 \ | CPU_INTERRUPT_TGT_EXT_4) +#include "cpu.h" + #ifdef CONFIG_USER_ONLY +static inline int cpu_mmu_index(CPUState *cs, bool ifetch); + /* * Allow some level of source compatibility with softmmu. We do not * support any of the more exotic features, so only invalid pages may @@ -271,7 +275,6 @@ static inline bool tlb_hit(uint64_t tlb_addr, vaddr addr) #endif /* !CONFIG_USER_ONLY */ /* Validate correct placement of CPUArchState. */ -#include "cpu.h" QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); From 47f7b6441a89af2bc0d2587112073b26e87493ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2024 14:47:19 +0100 Subject: [PATCH 0587/2892] accel/tcg: Declare cpu_loop_exit_requested() in 'exec/cpu-common.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move cpu_loop_exit_requested() declaration to "exec/cpu-common.h", along with the related cpu_loop_exit_noexc(), cpu_loop_exit(), cpu_loop_exit_atomic() and cpu_loop_exit_restore() methods. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-Id: <20241217140648.98538-1-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 2 +- include/exec/cpu-common.h | 18 ++++++++++++++++++ include/exec/exec-all.h | 17 ----------------- target/arm/tcg/helper-a64.c | 1 + target/s390x/tcg/mem_helper.c | 1 + 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index e9eaab223f..c07e59cd0b 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -25,7 +25,7 @@ #include "hw/core/tcg-cpu-ops.h" #include "trace.h" #include "disas/disas.h" -#include "exec/exec-all.h" +#include "exec/cpu-common.h" #include "exec/page-protection.h" #include "tcg/tcg.h" #include "qemu/atomic.h" diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 638dc806a5..0cf9a3d369 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -186,6 +186,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, void list_cpus(void); #ifdef CONFIG_TCG +#include "qemu/atomic.h" bool tcg_cflags_has(CPUState *cpu, uint32_t flags); void tcg_cflags_set(CPUState *cpu, uint32_t flags); @@ -218,6 +219,23 @@ bool cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data); */ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc); +/** + * cpu_loop_exit_requested: + * @cpu: The CPU state to be tested + * + * Indicate if somebody asked for a return of the CPU to the main loop + * (e.g., via cpu_exit() or cpu_interrupt()). + * + * This is helpful for architectures that support interruptible + * instructions. After writing back all state to registers/memory, this + * call can be used to check if it makes sense to return to the main loop + * or to continue executing the interruptible instruction. + */ +static inline bool cpu_loop_exit_requested(CPUState *cpu) +{ + return (int32_t)qatomic_read(&cpu->neg.icount_decr.u32) < 0; +} + G_NORETURN void cpu_loop_exit_noexc(CPUState *cpu); G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); #endif /* CONFIG_TCG */ diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index c4a6ad3af2..d9045c9ac4 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -27,23 +27,6 @@ #include "exec/mmu-access-type.h" #include "exec/translation-block.h" -/** - * cpu_loop_exit_requested: - * @cpu: The CPU state to be tested - * - * Indicate if somebody asked for a return of the CPU to the main loop - * (e.g., via cpu_exit() or cpu_interrupt()). - * - * This is helpful for architectures that support interruptible - * instructions. After writing back all state to registers/memory, this - * call can be used to check if it makes sense to return to the main loop - * or to continue executing the interruptible instruction. - */ -static inline bool cpu_loop_exit_requested(CPUState *cpu) -{ - return (int32_t)qatomic_read(&cpu->neg.icount_decr.u32) < 0; -} - #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) /* cputlb.c */ /** diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index b6af2a59d6..0e130501fd 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -28,6 +28,7 @@ #include "qemu/bitops.h" #include "internals.h" #include "qemu/crc32c.h" +#include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "qemu/int128.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 307388e5bd..32717acb7d 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -24,6 +24,7 @@ #include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" +#include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" From 0ec02378e69ef7a0e6eea068a44572a40fa31cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2024 14:51:01 +0100 Subject: [PATCH 0588/2892] exec/translation-block: Include missing 'qemu/atomic.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When moving tb_cflags() in commit 88d4b5138a8 ("tcg: Make tb_cflags() usable from target-agnostic code") we forgot to include "qemu/atomic.h", which declares qatomic_read(). Explicitly include it now to avoid issue when refactoring unrelated headers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Ilya Leoshkevich Message-Id: <20241217141326.98947-2-philmd@linaro.org> --- include/exec/translation-block.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index b99afb0077..81299b7bdb 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -7,6 +7,7 @@ #ifndef EXEC_TRANSLATION_BLOCK_H #define EXEC_TRANSLATION_BLOCK_H +#include "qemu/atomic.h" #include "qemu/thread.h" #include "exec/cpu-common.h" #include "exec/vaddr.h" From f3adff92ce625ec1dc90d4764a8a470eee6b7eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2024 14:51:11 +0100 Subject: [PATCH 0589/2892] qemu/coroutine: Include missing 'qemu/atomic.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 944f3d5dd21 ("coroutine: Add qemu_co_mutex_assert_locked") added an inline method which uses qatomic_read(), itself declared in "qemu/atomic.h". Explicitly include it now to avoid issue when refactoring unrelated headers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Ilya Leoshkevich Message-Id: <20241217141326.98947-3-philmd@linaro.org> --- include/qemu/coroutine.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index ff3084538b..e545bbf620 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -16,6 +16,7 @@ #define QEMU_COROUTINE_H #include "qemu/coroutine-core.h" +#include "qemu/atomic.h" #include "qemu/queue.h" #include "qemu/timer.h" From 1760c5cce8301c4a4f007c793c872a8f16439326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Dec 2024 22:34:00 +0100 Subject: [PATCH 0590/2892] accel/tcg: Restrict curr_cflags() declaration to 'internal-common.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit curr_cflags() is only used within accel/tcg/, move its declaration to accel/tcg/internal-common.h. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241216214030.59393-1-philmd@linaro.org> --- accel/tcg/internal-common.h | 3 +++ accel/tcg/watchpoint.c | 1 + include/exec/cpu-common.h | 3 --- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index a8fc3db774..c8d714256c 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -56,4 +56,7 @@ void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, bool tcg_exec_realizefn(CPUState *cpu, Error **errp); void tcg_exec_unrealizefn(CPUState *cpu); +/* current cflags for hashing/comparison */ +uint32_t curr_cflags(CPUState *cpu); + #endif diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index e24baead56..fbaf45d10f 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -27,6 +27,7 @@ #include "system/replay.h" #include "hw/core/tcg-cpu-ops.h" #include "hw/core/cpu.h" +#include "internal-common.h" /* * Return true if this watchpoint address matches the specified diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 0cf9a3d369..74e947f3ad 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -191,9 +191,6 @@ void list_cpus(void); bool tcg_cflags_has(CPUState *cpu, uint32_t flags); void tcg_cflags_set(CPUState *cpu, uint32_t flags); -/* current cflags for hashing/comparison */ -uint32_t curr_cflags(CPUState *cpu); - /** * cpu_unwind_state_data: * @cpu: the cpu context From 8865049bab9957eb5b027d3e53bd05316817ea07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 15:38:34 +0100 Subject: [PATCH 0591/2892] accel/tcg: Move tcg_cflags_has/set() to 'exec/translation-block.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TranslationBlock flags are defined in 'exec/translation-block.h'. tcg_cflags_has/set() use them, it is more logical to declare them in the same place. Move them there too. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241212144430.66224-2-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 1 + accel/tcg/tcg-accel-ops.c | 1 + accel/tcg/watchpoint.c | 1 + include/exec/cpu-common.h | 3 --- include/exec/translation-block.h | 3 +++ linux-user/mmap.c | 1 + linux-user/syscall.c | 1 + target/arm/cpu.c | 1 + target/avr/cpu.c | 1 + target/hexagon/cpu.c | 1 + target/hppa/cpu.c | 1 + target/i386/cpu.c | 1 + target/i386/helper.c | 1 + target/loongarch/cpu.c | 1 + target/microblaze/cpu.c | 1 + target/mips/tcg/exception.c | 1 + target/mips/tcg/system/special_helper.c | 1 + target/openrisc/cpu.c | 1 + target/riscv/tcg/tcg-cpu.c | 1 + target/rx/cpu.c | 1 + target/sh4/cpu.c | 1 + target/sparc/cpu.c | 1 + target/tricore/cpu.c | 1 + 23 files changed, 24 insertions(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c07e59cd0b..b507049ddb 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -27,6 +27,7 @@ #include "disas/disas.h" #include "exec/cpu-common.h" #include "exec/page-protection.h" +#include "exec/translation-block.h" #include "tcg/tcg.h" #include "qemu/atomic.h" #include "qemu/rcu.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index d9a35b7667..6e3f1fa92b 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -35,6 +35,7 @@ #include "exec/exec-all.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" +#include "exec/translation-block.h" #include "gdbstub/enums.h" #include "hw/core/cpu.h" diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index fbaf45d10f..af57d182d5 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -22,6 +22,7 @@ #include "qemu/error-report.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/translation-block.h" #include "tb-internal.h" #include "system/tcg.h" #include "system/replay.h" diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 74e947f3ad..b1d76d6985 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -188,9 +188,6 @@ void list_cpus(void); #ifdef CONFIG_TCG #include "qemu/atomic.h" -bool tcg_cflags_has(CPUState *cpu, uint32_t flags); -void tcg_cflags_set(CPUState *cpu, uint32_t flags); - /** * cpu_unwind_state_data: * @cpu: the cpu context diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index 81299b7bdb..3c69bc71a9 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -154,4 +154,7 @@ static inline uint32_t tb_cflags(const TranslationBlock *tb) return qatomic_read(&tb->cflags); } +bool tcg_cflags_has(CPUState *cpu, uint32_t flags); +void tcg_cflags_set(CPUState *cpu, uint32_t flags); + #endif /* EXEC_TRANSLATION_BLOCK_H */ diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 4e0444b4cb..6828b17a63 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -21,6 +21,7 @@ #include "trace.h" #include "exec/log.h" #include "exec/page-protection.h" +#include "exec/translation-block.h" #include "qemu.h" #include "user/page-protection.h" #include "user-internals.h" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1b335688f1..78c7c0b34e 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -26,6 +26,7 @@ #include "tcg/startup.h" #include "target_mman.h" #include "exec/page-protection.h" +#include "exec/translation-block.h" #include #include #include diff --git a/target/arm/cpu.c b/target/arm/cpu.c index b085c068ad..f45cd18ff7 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "cpu.h" #ifdef CONFIG_TCG +#include "exec/translation-block.h" #include "hw/core/tcg-cpu-ops.h" #endif /* CONFIG_TCG */ #include "internals.h" diff --git a/target/avr/cpu.c b/target/avr/cpu.c index a7529a1b3d..698e0c5161 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "qemu/qemu-print.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "cpu.h" #include "disas/dis-asm.h" #include "tcg/debug-assert.h" diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index a70007245e..c213ce8d88 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "internal.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "qapi/error.h" #include "hw/qdev-properties.h" #include "fpu/softfloat-helpers.h" diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index c38439c180..c9062e60b6 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "qemu/module.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "fpu/softfloat.h" #include "tcg/tcg.h" diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 96a2608e99..f3a97dc61b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -24,6 +24,7 @@ #include "qemu/hw-version.h" #include "cpu.h" #include "tcg/helper-tcg.h" +#include "exec/translation-block.h" #include "system/hvf.h" #include "hvf/hvf-i386.h" #include "kvm/kvm_i386.h" diff --git a/target/i386/helper.c b/target/i386/helper.c index a78d06c95b..3bc15fba6e 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -21,6 +21,7 @@ #include "qapi/qapi-events-run-state.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "system/runstate.h" #ifndef CONFIG_USER_ONLY #include "system/hw_accel.h" diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index fa838dce2e..f5bc8720d1 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -15,6 +15,7 @@ #include "system/kvm.h" #include "kvm/kvm_loongarch.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "cpu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 0e41e39c0e..9db5c4d2a3 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -30,6 +30,7 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/gdbstub.h" +#include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index 4886d087b2..1a8902ea1b 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -24,6 +24,7 @@ #include "internal.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" target_ulong exception_resume_pc(CPUMIPSState *env) { diff --git a/target/mips/tcg/system/special_helper.c b/target/mips/tcg/system/special_helper.c index 9ce5e2ceac..3ce3ae1e12 100644 --- a/target/mips/tcg/system/special_helper.c +++ b/target/mips/tcg/system/special_helper.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "internal.h" /* Specials */ diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 3ccf85e95f..7913a0c3e1 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -22,6 +22,7 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index c62c221696..958b8c89cb 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "tcg-cpu.h" #include "cpu.h" #include "internals.h" diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 69ec0bc7b3..558280c794 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -23,6 +23,7 @@ #include "migration/vmstate.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/translation-block.h" #include "hw/loader.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index d5008859b8..e9d3e12a62 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index fc0c66afec..960ed90351 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -23,6 +23,7 @@ #include "qemu/module.h" #include "qemu/qemu-print.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "hw/qdev-properties.h" #include "qapi/visitor.h" #include "tcg/tcg.h" diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 1a26171590..95fb546666 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -21,6 +21,7 @@ #include "qapi/error.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "qemu/error-report.h" #include "tcg/debug-assert.h" From 68df8c8dba57f539d24f1a92a8699a179d9bb6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Nov 2024 22:46:13 +0100 Subject: [PATCH 0592/2892] accel/tcg: Include missing 'exec/translation-block.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TB compile flags, tb_page_addr_t type, tb_cflags() and few other methods are defined in "exec/translation-block.h". All these files don't include "exec/translation-block.h" but include "exec/exec-all.h" which include it. Explicitly include "exec/translation-block.h" to be able to remove it from "exec/exec-all.h" later when it won't be necessary. Otherwise we'd get errors such: accel/tcg/internal-target.h:59:20: error: a parameter list without types is only allowed in a function definition 59 | void tb_lock_page0(tb_page_addr_t); | ^ accel/tcg/tb-hash.h:64:23: error: unknown type name 'tb_page_addr_t' 64 | uint32_t tb_hash_func(tb_page_addr_t phys_pc, vaddr pc, | ^ accel/tcg/tcg-accel-ops.c:62:36: error: use of undeclared identifier 'CF_CLUSTER_SHIFT' 62 | cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; | ^ accel/tcg/watchpoint.c:102:47: error: use of undeclared identifier 'CF_NOIRQ' 102 | cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); | ^ target/i386/helper.c:536:28: error: use of undeclared identifier 'CF_PCREL' 536 | if (tcg_cflags_has(cs, CF_PCREL)) { | ^ target/rx/cpu.c:51:21: error: incomplete definition of type 'struct TranslationBlock' 51 | cpu->env.pc = tb->pc; | ~~^ system/physmem.c:2977:9: error: call to undeclared function 'tb_invalidate_phys_range'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 2977 | tb_invalidate_phys_range(addr, addr + length - 1); | ^ plugins/api.c:96:12: error: call to undeclared function 'tb_cflags'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 96 | return tb_cflags(tcg_ctx->gen_tb) & CF_MEMI_ONLY; | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241114011310.3615-5-philmd@linaro.org> --- accel/tcg/internal-target.h | 1 + accel/tcg/tb-hash.h | 1 + cpu-target.c | 1 + linux-user/elfload.c | 1 + plugins/api.c | 1 + system/physmem.c | 1 + target/alpha/cpu.c | 1 + target/alpha/translate.c | 1 + target/arm/helper.c | 1 + target/arm/tcg/translate.h | 1 + target/avr/translate.c | 1 + target/hppa/translate.c | 1 + target/i386/tcg/tcg-cpu.c | 1 + target/i386/tcg/translate.c | 1 + target/m68k/translate.c | 1 + target/microblaze/translate.c | 1 + target/openrisc/translate.c | 1 + target/ppc/translate.c | 1 + target/riscv/translate.c | 1 + target/rx/translate.c | 1 + target/s390x/tcg/translate.c | 1 + target/sh4/translate.c | 1 + target/sparc/translate.c | 1 + target/tricore/translate.c | 1 + target/xtensa/translate.c | 1 + 25 files changed, 25 insertions(+) diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 3ed81e740d..a664be02cc 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -10,6 +10,7 @@ #define ACCEL_TCG_INTERNAL_TARGET_H #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "tb-internal.h" /* diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h index a0c61f25cd..a5382f460d 100644 --- a/accel/tcg/tb-hash.h +++ b/accel/tcg/tb-hash.h @@ -22,6 +22,7 @@ #include "exec/cpu-defs.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "qemu/xxhash.h" #include "tb-jmp-cache.h" diff --git a/cpu-target.c b/cpu-target.c index 7a2efa890c..beec773790 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -42,6 +42,7 @@ #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "hw/core/accel-cpu.h" #include "trace/trace-root.h" diff --git a/linux-user/elfload.c b/linux-user/elfload.c index effd3ab47e..a2c152e5ad 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -10,6 +10,7 @@ #include "user/tswap-target.h" #include "user/page-protection.h" #include "exec/page-protection.h" +#include "exec/translation-block.h" #include "user/guest-base.h" #include "user-internals.h" #include "signal-common.h" diff --git a/plugins/api.c b/plugins/api.c index 24ea64e2de..4110cfaa23 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -43,6 +43,7 @@ #include "tcg/tcg.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" +#include "exec/translation-block.h" #include "exec/translator.h" #include "disas/disas.h" #include "plugin.h" diff --git a/system/physmem.c b/system/physmem.c index 1459dd15eb..c76503aea8 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -34,6 +34,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" +#include "exec/translation-block.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/boards.h" diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 70f67e6fd4..9fa506bff9 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -24,6 +24,7 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "fpu/softfloat.h" diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 660788d5c3..629ff3cde9 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -26,6 +26,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index 449e69a6e3..5b595f951b 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -20,6 +20,7 @@ #include "qemu/crc32c.h" #include "qemu/qemu-print.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include /* for crc32 */ #include "hw/irq.h" #include "system/cpu-timers.h" diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 9b9abf1992..2d37d7c9f2 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -6,6 +6,7 @@ #include "tcg/tcg-op-gvec.h" #include "exec/exec-all.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/helper-gen.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/avr/translate.c b/target/avr/translate.c index 2d51892115..f13b997f8d 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -23,6 +23,7 @@ #include "tcg/tcg.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 51c1762435..d13f80fe3e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -27,6 +27,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index cca19cd40e..231ecac37d 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -22,6 +22,7 @@ #include "helper-tcg.h" #include "qemu/accel.h" #include "hw/core/accel-cpu.h" +#include "exec/translation-block.h" #include "tcg-cpu.h" diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ea7995106f..57e8387393 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -21,6 +21,7 @@ #include "qemu/host-utils.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/translator.h" diff --git a/target/m68k/translate.c b/target/m68k/translate.c index ad3ce34501..077151c62d 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/translation-block.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 4beaf69e76..d53995c26d 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -26,6 +26,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "qemu/qemu-print.h" #include "exec/log.h" diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index ca566847cb..028ba66631 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -26,6 +26,7 @@ #include "qemu/bitops.h" #include "qemu/qemu-print.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 47ca50a064..8ab87f42d6 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -30,6 +30,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "qemu/atomic128.h" #include "spr_common.h" diff --git a/target/riscv/translate.c b/target/riscv/translate.c index bccaf8e89a..5fedde363f 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -25,6 +25,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "semihosting/semihost.h" diff --git a/target/rx/translate.c b/target/rx/translate.c index 9aade2b6e5..4f43654bad 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -25,6 +25,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index bcfff40b25..e78815c4f7 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -40,6 +40,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "qemu/atomic128.h" diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 53b092175d..f076da9bac 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -23,6 +23,7 @@ #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/translation-block.h" #include "exec/translator.h" #include "exec/log.h" #include "qemu/qemu-print.h" diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ac06377231..9be26c804e 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -27,6 +27,7 @@ #include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "fpu/softfloat.h" #include "asi.h" diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 4a12d2ca19..2b67395c09 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -30,6 +30,7 @@ #include "tricore-opcodes.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index f4da4a40f9..3c62c99b4f 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -37,6 +37,7 @@ #include "qemu/qemu-print.h" #include "semihosting/semihost.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" From a9ca97ea9e582a77629db9af61603b5fddc9ba2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Nov 2024 18:05:17 +0100 Subject: [PATCH 0593/2892] accel/tcg: Un-inline translator_is_same_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the single target-specific definition used in "exec/translator.h" (TARGET_PAGE_MASK) by un-inlining is_same_page(). Rename the method as translator_is_same_page() and improve its documentation. Use it in translator_use_goto_tb(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241218154145.71353-1-philmd@linaro.org> --- accel/tcg/translator.c | 7 ++++++- include/exec/translator.h | 15 +++++++-------- target/i386/tcg/translate.c | 6 +++--- target/riscv/translate.c | 4 ++-- target/s390x/tcg/translate.c | 4 ++-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index ce5eae4349..ef1538b4fc 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -104,6 +104,11 @@ static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags, } } +bool translator_is_same_page(const DisasContextBase *db, vaddr addr) +{ + return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; +} + bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) { /* Suppress goto_tb if requested. */ @@ -112,7 +117,7 @@ bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) } /* Check for the dest on the same page as the start of the TB. */ - return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; + return translator_is_same_page(db, dest); } void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, diff --git a/include/exec/translator.h b/include/exec/translator.h index d8dcb77b5f..41e2a41180 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -267,16 +267,15 @@ bool translator_st(const DisasContextBase *db, void *dest, */ size_t translator_st_len(const DisasContextBase *db); -#ifdef COMPILING_PER_TARGET -/* - * Return whether addr is on the same page as where disassembly started. +/** + * translator_is_same_page + * @db: disassembly context + * @addr: virtual address within TB + * + * Return whether @addr is on the same page as where disassembly started. * Translators can use this to enforce the rule that only single-insn * translation blocks are allowed to cross page boundaries. */ -static inline bool is_same_page(const DisasContextBase *db, vaddr addr) -{ - return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; -} -#endif +bool translator_is_same_page(const DisasContextBase *db, vaddr addr); #endif /* EXEC__TRANSLATOR_H */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 57e8387393..903553dc88 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1512,7 +1512,7 @@ static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) /* This is a subsequent insn that crosses a page boundary. */ if (s->base.num_insns > 1 && - !is_same_page(&s->base, s->pc + num_bytes - 1)) { + !translator_is_same_page(&s->base, s->pc + num_bytes - 1)) { siglongjmp(s->jmpbuf, 2); } @@ -2226,7 +2226,7 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) * no extra masking to apply (data16 branch in code32, see above), * then we have also proven that the addition does not wrap. */ - if (!use_goto_tb || !is_same_page(&s->base, new_pc)) { + if (!use_goto_tb || !translator_is_same_page(&s->base, new_pc)) { tcg_gen_andi_tl(cpu_eip, cpu_eip, mask); use_goto_tb = false; } @@ -3763,7 +3763,7 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * chance to happen. */ dc->base.is_jmp = DISAS_EOB_NEXT; - } else if (!is_same_page(&dc->base, dc->base.pc_next)) { + } else if (!translator_is_same_page(&dc->base, dc->base.pc_next)) { dc->base.is_jmp = DISAS_TOO_MANY; } } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 5fedde363f..a76f67c5dd 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1305,7 +1305,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { - if (ctx->itrigger || !is_same_page(&ctx->base, ctx->base.pc_next)) { + if (ctx->itrigger || !translator_is_same_page(&ctx->base, ctx->base.pc_next)) { ctx->base.is_jmp = DISAS_TOO_MANY; } else { unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK; @@ -1315,7 +1315,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) translator_lduw(env, &ctx->base, ctx->base.pc_next); int len = insn_len(next_insn); - if (!is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { + if (!translator_is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { ctx->base.is_jmp = DISAS_TOO_MANY; } } diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index e78815c4f7..81554f2ad9 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6423,8 +6423,8 @@ static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) dc->base.is_jmp = translate_one(env, dc); if (dc->base.is_jmp == DISAS_NEXT) { if (dc->ex_value || - !is_same_page(dcbase, dc->base.pc_next) || - !is_same_page(dcbase, get_next_pc(env, dc, dc->base.pc_next))) { + !translator_is_same_page(dcbase, dc->base.pc_next) || + !translator_is_same_page(dcbase, get_next_pc(env, dc, dc->base.pc_next))) { dc->base.is_jmp = DISAS_TOO_MANY; } } From b82f70bb9dddfd6d1315a1c0a710c22dd5089a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 00:21:14 +0100 Subject: [PATCH 0594/2892] target/xtensa: Remove tswap() calls in semihosting simcall() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of heterogeneous emulation where cores with different endianness can run concurrently, replace the pair of cpu_memory_rw_debug() + tswap() calls by put/get_user_u32() ones, which still do the same under the hood, but simplify the code maintenance (having less sites to do endianness code conversion). Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <6e1e69d8-a9f3-4a30-83c8-84c5647578d5@linaro.org> --- target/xtensa/xtensa-semi.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index fa21b7e11f..2ded8e5634 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -30,6 +30,7 @@ #include "chardev/char-fe.h" #include "exec/helper-proto.h" #include "semihosting/semihost.h" +#include "semihosting/uaccess.h" #include "qapi/error.h" #include "qemu/log.h" @@ -323,15 +324,12 @@ void HELPER(simcall)(CPUXtensaState *env) uint32_t fd = regs[3]; uint32_t rq = regs[4]; uint32_t target_tv = regs[5]; - uint32_t target_tvv[2]; struct timeval tv = {0}; if (target_tv) { - cpu_memory_rw_debug(cs, target_tv, - (uint8_t *)target_tvv, sizeof(target_tvv), 0); - tv.tv_sec = (int32_t)tswap32(target_tvv[0]); - tv.tv_usec = (int32_t)tswap32(target_tvv[1]); + get_user_u32(tv.tv_sec, target_tv); + get_user_u32(tv.tv_sec, target_tv + 4); } if (fd < 3 && sim_console) { if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) { @@ -387,11 +385,8 @@ void HELPER(simcall)(CPUXtensaState *env) const char *str = semihosting_get_arg(i); int str_size = strlen(str) + 1; - argptr = tswap32(regs[3] + str_offset); - - cpu_memory_rw_debug(cs, - regs[3] + i * sizeof(uint32_t), - (uint8_t *)&argptr, sizeof(argptr), 1); + put_user_u32(regs[3] + str_offset, + regs[3] + i * sizeof(uint32_t)); cpu_memory_rw_debug(cs, regs[3] + str_offset, (uint8_t *)str, str_size, 1); From 6277e181a7dabbe1b4003e1509656c1ee477aa65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Dec 2024 00:09:24 +0100 Subject: [PATCH 0595/2892] target/mips: Remove tswap() calls in semihosting uhi_fstat_cb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of heterogeneous emulation where cores with different endianness can run concurrently, we need to remove the tswap() calls -- which use a fixed per-binary endianness. Get the endianness of the UHI CPU accessed using mips_env_is_bigendian() and replace the tswap() calls by bswap() ones when necessary. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241211230357.97036-3-philmd@linaro.org> --- target/mips/tcg/system/mips-semi.c | 43 +++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/target/mips/tcg/system/mips-semi.c b/target/mips/tcg/system/mips-semi.c index 5ba06e9573..df0c3256d9 100644 --- a/target/mips/tcg/system/mips-semi.c +++ b/target/mips/tcg/system/mips-semi.c @@ -168,6 +168,7 @@ static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) if (!err) { CPUMIPSState *env = cpu_env(cs); + bool swap_needed = HOST_BIG_ENDIAN != mips_env_is_bigendian(env); target_ulong addr = env->active_tc.gpr[5]; UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1); struct gdb_stat s; @@ -179,19 +180,35 @@ static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) memcpy(&s, dst, sizeof(struct gdb_stat)); memset(dst, 0, sizeof(UHIStat)); - dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev)); - dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino)); - dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode)); - dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink)); - dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid)); - dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid)); - dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev)); - dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size)); - dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime)); - dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime)); - dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime)); - dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize)); - dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks)); + dst->uhi_st_dev = be32_to_cpu(s.gdb_st_dev); + dst->uhi_st_ino = be32_to_cpu(s.gdb_st_ino); + dst->uhi_st_mode = be32_to_cpu(s.gdb_st_mode); + dst->uhi_st_nlink = be32_to_cpu(s.gdb_st_nlink); + dst->uhi_st_uid = be32_to_cpu(s.gdb_st_uid); + dst->uhi_st_gid = be32_to_cpu(s.gdb_st_gid); + dst->uhi_st_rdev = be32_to_cpu(s.gdb_st_rdev); + dst->uhi_st_size = be64_to_cpu(s.gdb_st_size); + dst->uhi_st_atime = be32_to_cpu(s.gdb_st_atime); + dst->uhi_st_mtime = be32_to_cpu(s.gdb_st_mtime); + dst->uhi_st_ctime = be32_to_cpu(s.gdb_st_ctime); + dst->uhi_st_blksize = be64_to_cpu(s.gdb_st_blksize); + dst->uhi_st_blocks = be64_to_cpu(s.gdb_st_blocks); + + if (swap_needed) { + dst->uhi_st_dev = bswap16(dst->uhi_st_dev); + dst->uhi_st_ino = bswap16(dst->uhi_st_ino); + dst->uhi_st_mode = bswap32(dst->uhi_st_mode); + dst->uhi_st_nlink = bswap16(dst->uhi_st_nlink); + dst->uhi_st_uid = bswap16(dst->uhi_st_uid); + dst->uhi_st_gid = bswap16(dst->uhi_st_gid); + dst->uhi_st_rdev = bswap16(dst->uhi_st_rdev); + dst->uhi_st_size = bswap64(dst->uhi_st_size); + dst->uhi_st_atime = bswap64(dst->uhi_st_atime); + dst->uhi_st_mtime = bswap64(dst->uhi_st_mtime); + dst->uhi_st_ctime = bswap64(dst->uhi_st_ctime); + dst->uhi_st_blksize = bswap64(dst->uhi_st_blksize); + dst->uhi_st_blocks = bswap64(dst->uhi_st_blocks); + } unlock_user(dst, addr, sizeof(UHIStat)); } From abecbbbb215bb73571e74bf04c561b5286a74e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Nov 2024 10:36:18 +0100 Subject: [PATCH 0596/2892] target/mips: Drop left-over comment about Jazz machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 3803b6b427 ("target/mips: Fold jazz behaviour into mips_cpu_do_transaction_failed") removed update on TCGCPUOps and commit 119065574d ("hw/core: Constify TCGCPUOps") made it const. Remove the now irrelevant comment. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20241115152053.66442-2-philmd@linaro.org> --- target/mips/cpu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 441067060f..270611ce96 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -546,10 +546,6 @@ static const Property mips_cpu_properties[] = { #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -/* - * NB: cannot be const, as some elements are changed for specific - * mips hardware (see hw/mips/jazz.c). - */ static const TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, .synchronize_from_tb = mips_cpu_synchronize_from_tb, From e8cd5053f01080db4472428184bd808bff852ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2024 15:58:50 +0100 Subject: [PATCH 0597/2892] hw/xen: Remove unnecessary 'exec/cpu-common.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing requires definitions from "exec/cpu-common.h", do not include this header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20241217151305.29196-2-philmd@linaro.org> --- include/hw/xen/xen.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index ecb89ecfc1..e94c6e5a31 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -24,8 +24,6 @@ #define __XEN_INTERFACE_VERSION__ 0x00040e00 #endif -#include "exec/cpu-common.h" - /* xen-machine.c */ enum xen_mode { XEN_DISABLED = 0, /* xen support disabled (default) */ From 5d4ffa8962636cd4492b13f084e32586bdc6ee6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2024 15:56:26 +0100 Subject: [PATCH 0598/2892] system/numa: Remove unnecessary 'exec/cpu-common.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing requires definitions from "exec/cpu-common.h", do not include this header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20241217151305.29196-3-philmd@linaro.org> --- include/system/numa.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/system/numa.h b/include/system/numa.h index 96d4ff9b85..1044b0eb6e 100644 --- a/include/system/numa.h +++ b/include/system/numa.h @@ -3,7 +3,6 @@ #include "qemu/bitmap.h" #include "qapi/qapi-types-machine.h" -#include "exec/cpu-common.h" struct CPUArchId; From 984f0e7f69b8e4aef153d622ed0e4310d338b7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2024 15:56:10 +0100 Subject: [PATCH 0599/2892] system/accel-ops: Remove unnecessary 'exec/cpu-common.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit c4b3f46c151 ("include/exec: Move vaddr defines to separate file") we only need to include "exec/vaddr.h" to get the 'vaddr' type definition, no need for "exec/cpu-common.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20241217151305.29196-4-philmd@linaro.org> --- include/system/accel-ops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index a088672230..137fb96d44 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -10,7 +10,7 @@ #ifndef ACCEL_OPS_H #define ACCEL_OPS_H -#include "exec/cpu-common.h" +#include "exec/vaddr.h" #include "qom/object.h" #define ACCEL_OPS_SUFFIX "-ops" From edbceacf5ad0b2d5b863ebf21811e92e25803803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 18 Dec 2024 15:36:07 +0100 Subject: [PATCH 0600/2892] meson: Do not define CONFIG_DEVICES on user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_DEVICES is not generated on user emulation, so do not define it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241218151256.68625-1-philmd@linaro.org> --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6149b50db2..f4109cd3ca 100644 --- a/meson.build +++ b/meson.build @@ -4110,7 +4110,7 @@ foreach target : target_dirs arch_deps = [] c_args = ['-DCOMPILING_PER_TARGET', '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), - '-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] + ] link_args = emulator_link_args target_inc = [include_directories('target' / config_target['TARGET_BASE_ARCH'])] @@ -4130,6 +4130,7 @@ foreach target : target_dirs arch_deps += hw.dependencies() endif + c_args += ['-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] arch_srcs += config_devices_h[target] link_args += ['@block.syms', '@qemu.syms'] else From 1a1f4a51f3708619fdda48dd061cf527778554af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 18 Dec 2024 16:20:52 +0000 Subject: [PATCH 0601/2892] util/qemu-timer: fix indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Purely cosmetic. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241218162104.3493551-17-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- util/qemu-timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 16f847ff98..0e8a453eaa 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -680,7 +680,7 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) aio_context = qemu_get_aio_context(); deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); + QEMU_TIMER_ATTR_ALL); /* * A deadline of < 0 indicates this timer is not enabled, so we * won't get far trying to run it forward. From 713484d0389c9d1cbb87eca060361281248b69f5 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 25 Oct 2024 12:41:03 +0200 Subject: [PATCH 0602/2892] virtio-mem: unplug memory only during system resets, not device resets We recently converted from the LegacyReset to the new reset framework in commit c009a311e939 ("virtio-mem: Use new Resettable framework instead of LegacyReset") to be able to use the ResetType to filter out wakeup resets. However, this change had an undesired implications: as we override the Resettable interface methods in VirtIOMEMClass, the reset handler will not only get called during system resets (i.e., qemu_devices_reset()) but also during any direct or indirect device rests (e.g., device_cold_reset()). Further, we might now receive two reset callbacks during qemu_devices_reset(), first when reset by a parent and later when reset directly. The memory state of virtio-mem devices is rather special: it's supposed to be persistent/unchanged during most resets (similar to resetting a hard disk will not destroy the data), unless actually cold-resetting the whole system (different to a hard disk where a reboot will not destroy the data): ripping out system RAM is something guest OSes don't particularly enjoy, but we want to detect when rebooting to an OS that does not support virtio-mem and wouldn't be able to detect+use the memory -- and we want to force-defragment hotplugged memory to also shrink the usable device memory region. So we rally want to catch system resets to do that. On supported targets (e.g., x86), getting a cold reset on the device/parent triggers is not that easy (but looks like PCI code might trigger it), so this implication went unnoticed. However, with upcoming s390x support it is problematic: during kdump, s390x triggers a subsystem reset, ending up in s390_machine_reset() and calling only subsystem_reset() instead of qemu_devices_reset() -- because it's not a full system reset. In subsystem_reset(), s390x performs a device_cold_reset() of any TYPE_VIRTUAL_CSS_BRIDGE device, which ends up resetting all children, including the virtio-mem device. Consequently, we wrongly detect a system reset and unplug all device memory, resulting in hotplugged memory not getting included in the crash dump -- undesired. We really must not mess with hotplugged memory state during simple device resets. To fix, create+register a new reset object that will only get triggered during qemu_devices_reset() calls, but not during any other resets as it is logically not the child of any other object. Message-ID: <20241025104103.342188-1-david@redhat.com> Acked-by: Michael S. Tsirkin Cc: "Michael S. Tsirkin" Cc: Juraj Marcin Cc: Peter Maydell Signed-off-by: David Hildenbrand --- hw/virtio/virtio-mem.c | 103 +++++++++++++++++++++++---------- include/hw/virtio/virtio-mem.h | 13 ++++- 2 files changed, 84 insertions(+), 32 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 3f6f46fad7..a0dceaddec 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -956,6 +956,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) VirtIOMEM *vmem = VIRTIO_MEM(dev); uint64_t page_size; RAMBlock *rb; + Object *obj; int ret; if (!vmem->memdev) { @@ -1121,7 +1122,28 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) vmstate_register_any(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early, vmem); } - qemu_register_resettable(OBJECT(vmem)); + + /* + * We only want to unplug all memory to start with a clean slate when + * it is safe for the guest -- during system resets that call + * qemu_devices_reset(). + * + * We'll filter out selected qemu_devices_reset() calls used for other + * purposes, like resetting all devices during wakeup from suspend on + * x86 based on the reset type passed to qemu_devices_reset(). + * + * Unplugging all memory during simple device resets can result in the VM + * unexpectedly losing RAM, corrupting VM state. + * + * Simple device resets (or resets triggered by getting a parent device + * reset) must not change the state of plugged memory blocks. Therefore, + * we need a dedicated reset object that only gets called during + * qemu_devices_reset(). + */ + obj = object_new(TYPE_VIRTIO_MEM_SYSTEM_RESET); + vmem->system_reset = VIRTIO_MEM_SYSTEM_RESET(obj); + vmem->system_reset->vmem = vmem; + qemu_register_resettable(obj); /* * Set ourselves as RamDiscardManager before the plug handler maps the @@ -1141,7 +1163,10 @@ static void virtio_mem_device_unrealize(DeviceState *dev) * found via an address space anymore. Unset ourselves. */ memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); - qemu_unregister_resettable(OBJECT(vmem)); + + qemu_unregister_resettable(OBJECT(vmem->system_reset)); + object_unref(OBJECT(vmem->system_reset)); + if (vmem->early_migration) { vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early, vmem); @@ -1841,38 +1866,12 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) } } -static ResettableState *virtio_mem_get_reset_state(Object *obj) -{ - VirtIOMEM *vmem = VIRTIO_MEM(obj); - return &vmem->reset_state; -} - -static void virtio_mem_system_reset_hold(Object *obj, ResetType type) -{ - VirtIOMEM *vmem = VIRTIO_MEM(obj); - - /* - * When waking up from standby/suspend-to-ram, do not unplug any memory. - */ - if (type == RESET_TYPE_WAKEUP) { - return; - } - - /* - * During usual resets, we will unplug all memory and shrink the usable - * region size. This is, however, not possible in all scenarios. Then, - * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL). - */ - virtio_mem_unplug_all(vmem); -} - static void virtio_mem_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass); RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass); - ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_mem_properties); dc->vmsd = &vmstate_virtio_mem; @@ -1899,9 +1898,6 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data) rdmc->replay_discarded = virtio_mem_rdm_replay_discarded; rdmc->register_listener = virtio_mem_rdm_register_listener; rdmc->unregister_listener = virtio_mem_rdm_unregister_listener; - - rc->get_state = virtio_mem_get_reset_state; - rc->phases.hold = virtio_mem_system_reset_hold; } static const TypeInfo virtio_mem_info = { @@ -1924,3 +1920,48 @@ static void virtio_register_types(void) } type_init(virtio_register_types) + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(VirtioMemSystemReset, virtio_mem_system_reset, VIRTIO_MEM_SYSTEM_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) + +static void virtio_mem_system_reset_init(Object *obj) +{ +} + +static void virtio_mem_system_reset_finalize(Object *obj) +{ +} + +static ResettableState *virtio_mem_system_reset_get_state(Object *obj) +{ + VirtioMemSystemReset *vmem_reset = VIRTIO_MEM_SYSTEM_RESET(obj); + + return &vmem_reset->reset_state; +} + +static void virtio_mem_system_reset_hold(Object *obj, ResetType type) +{ + VirtioMemSystemReset *vmem_reset = VIRTIO_MEM_SYSTEM_RESET(obj); + VirtIOMEM *vmem = vmem_reset->vmem; + + /* + * When waking up from standby/suspend-to-ram, do not unplug any memory. + */ + if (type == RESET_TYPE_WAKEUP) { + return; + } + + /* + * During usual resets, we will unplug all memory and shrink the usable + * region size. This is, however, not possible in all scenarios. Then, + * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL). + */ + virtio_mem_unplug_all(vmem); +} + +static void virtio_mem_system_reset_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->get_state = virtio_mem_system_reset_get_state; + rc->phases.hold = virtio_mem_system_reset_hold; +} diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index a1af144c28..550ce585b2 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -25,6 +25,10 @@ OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass, VIRTIO_MEM) +#define TYPE_VIRTIO_MEM_SYSTEM_RESET "virtio-mem-system-reset" + +OBJECT_DECLARE_SIMPLE_TYPE(VirtioMemSystemReset, VIRTIO_MEM_SYSTEM_RESET) + #define VIRTIO_MEM_MEMDEV_PROP "memdev" #define VIRTIO_MEM_NODE_PROP "node" #define VIRTIO_MEM_SIZE_PROP "size" @@ -117,8 +121,15 @@ struct VirtIOMEM { /* listeners to notify on plug/unplug activity. */ QLIST_HEAD(, RamDiscardListener) rdl_list; - /* State of the resettable container */ + /* Catch system resets -> qemu_devices_reset() only. */ + VirtioMemSystemReset *system_reset; +}; + +struct VirtioMemSystemReset { + Object parent; + ResettableState reset_state; + VirtIOMEM *vmem; }; struct VirtIOMEMClass { From 14e568ab4836347481af2e334009c385f456a734 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:02 +0100 Subject: [PATCH 0603/2892] s390x/s390-virtio-ccw: don't crash on weird RAM sizes KVM is not happy when starting a VM with weird RAM sizes: # qemu-system-s390x --enable-kvm --nographic -m 1234K qemu-system-s390x: kvm_set_user_memory_region: KVM_SET_USER_MEMORY_REGION failed, slot=0, start=0x0, size=0x244000: Invalid argument kvm_set_phys_mem: error registering slot: Invalid argument Aborted (core dumped) Let's handle that in a better way by rejecting such weird RAM sizes right from the start: # qemu-system-s390x --enable-kvm --nographic -m 1234K qemu-system-s390x: ram size must be multiples of 1 MiB Message-ID: <20241219144115.2820241-2-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Acked-by: Janosch Frank Signed-off-by: David Hildenbrand --- hw/s390x/s390-virtio-ccw.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 67ae34aead..f2a17ecace 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -180,6 +180,17 @@ static void s390_memory_init(MemoryRegion *ram) { MemoryRegion *sysmem = get_system_memory(); + if (!QEMU_IS_ALIGNED(memory_region_size(ram), 1 * MiB)) { + /* + * SCLP cannot possibly expose smaller granularity right now and KVM + * cannot handle smaller granularity. As we don't support NUMA, the + * region size directly corresponds to machine->ram_size, and the region + * is a single RAM memory region. + */ + error_report("ram size must be multiples of 1 MiB"); + exit(EXIT_FAILURE); + } + /* allocate RAM for core */ memory_region_add_subregion(sysmem, 0, ram); From 4be0fce498d0a08f18b3a9accdb9ded79484d30a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:03 +0100 Subject: [PATCH 0604/2892] s390x/s390-virtio-hcall: remove hypercall registration mechanism Nowadays, we only have a single machine type in QEMU, everything is based on virtio-ccw and the traditional virtio machine does no longer exist. No need to dynamically register diag500 handlers. Move the two existing handlers into s390-virtio-hcall.c. Message-ID: <20241219144115.2820241-3-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: David Hildenbrand --- hw/s390x/meson.build | 6 ++- hw/s390x/s390-virtio-ccw.c | 58 ----------------------------- hw/s390x/s390-virtio-hcall.c | 67 +++++++++++++++++++++++++--------- hw/s390x/s390-virtio-hcall.h | 2 - target/s390x/kvm/kvm.c | 5 ++- target/s390x/tcg/misc_helper.c | 3 ++ 6 files changed, 61 insertions(+), 80 deletions(-) diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 482fd13420..d6c8c33915 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -12,7 +12,6 @@ s390x_ss.add(files( 's390-pci-inst.c', 's390-skeys.c', 's390-stattrib.c', - 's390-virtio-hcall.c', 'sclp.c', 'sclpcpu.c', 'sclpquiesce.c', @@ -28,7 +27,10 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( s390x_ss.add(when: 'CONFIG_TCG', if_true: files( 'tod-tcg.c', )) -s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files('s390-virtio-ccw.c')) +s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files( + 's390-virtio-ccw.c', + 's390-virtio-hcall.c', +)) s390x_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('3270-ccw.c')) s390x_ss.add(when: 'CONFIG_VFIO', if_true: files('s390-pci-vfio.c')) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index f2a17ecace..b0edaa0872 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -16,11 +16,8 @@ #include "exec/ram_addr.h" #include "exec/confidential-guest-support.h" #include "hw/boards.h" -#include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" -#include "hw/s390x/ioinst.h" -#include "hw/s390x/css.h" #include "virtio-ccw.h" #include "qemu/config-file.h" #include "qemu/ctype.h" @@ -124,58 +121,6 @@ static void subsystem_reset(void) } } -static int virtio_ccw_hcall_notify(const uint64_t *args) -{ - uint64_t subch_id = args[0]; - uint64_t data = args[1]; - SubchDev *sch; - VirtIODevice *vdev; - int cssid, ssid, schid, m; - uint16_t vq_idx = data; - - if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { - return -EINVAL; - } - sch = css_find_subch(m, cssid, ssid, schid); - if (!sch || !css_subch_visible(sch)) { - return -EINVAL; - } - - vdev = virtio_ccw_get_vdev(sch); - if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { - return -EINVAL; - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { - virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx), - (data >> 16) & 0xFFFF); - } - - virtio_queue_notify(vdev, vq_idx); - return 0; -} - -static int virtio_ccw_hcall_early_printk(const uint64_t *args) -{ - uint64_t mem = args[0]; - MachineState *ms = MACHINE(qdev_get_machine()); - - if (mem < ms->ram_size) { - /* Early printk */ - return 0; - } - return -EINVAL; -} - -static void virtio_ccw_register_hcalls(void) -{ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, - virtio_ccw_hcall_notify); - /* Tolerate early printk. */ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, - virtio_ccw_hcall_early_printk); -} - static void s390_memory_init(MemoryRegion *ram) { MemoryRegion *sysmem = get_system_memory(); @@ -296,9 +241,6 @@ static void ccw_init(MachineState *machine) OBJECT(dev)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - /* register hypercalls */ - virtio_ccw_register_hcalls(); - s390_enable_css_support(s390_cpu_addr2state(0)); ret = css_create_css_image(VIRTUAL_CSSID, true); diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c index ec7cf8beb3..ca49e3cd22 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-virtio-hcall.c @@ -11,31 +11,64 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "hw/boards.h" #include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" +#include "virtio-ccw.h" -#define MAX_DIAG_SUBCODES 255 - -static s390_virtio_fn s390_diag500_table[MAX_DIAG_SUBCODES]; - -void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) +static int handle_virtio_notify(uint64_t mem) { - assert(code < MAX_DIAG_SUBCODES); - assert(!s390_diag500_table[code]); + MachineState *ms = MACHINE(qdev_get_machine()); - s390_diag500_table[code] = fn; + if (mem < ms->ram_size) { + /* Early printk */ + return 0; + } + return -EINVAL; +} + +static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) +{ + SubchDev *sch; + VirtIODevice *vdev; + int cssid, ssid, schid, m; + uint16_t vq_idx = data; + + if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { + return -EINVAL; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (!sch || !css_subch_visible(sch)) { + return -EINVAL; + } + + vdev = virtio_ccw_get_vdev(sch); + if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { + return -EINVAL; + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx), + (data >> 16) & 0xFFFF); + } + + virtio_queue_notify(vdev, vq_idx); + return 0; } int s390_virtio_hypercall(CPUS390XState *env) { - s390_virtio_fn fn; + const uint64_t subcode = env->regs[1]; - if (env->regs[1] < MAX_DIAG_SUBCODES) { - fn = s390_diag500_table[env->regs[1]]; - if (fn) { - env->regs[2] = fn(&env->regs[2]); - return 0; - } + switch (subcode) { + case KVM_S390_VIRTIO_NOTIFY: + env->regs[2] = handle_virtio_notify(env->regs[2]); + return 0; + case KVM_S390_VIRTIO_CCW_NOTIFY: + env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); + return 0; + default: + return -EINVAL; } - - return -EINVAL; } diff --git a/hw/s390x/s390-virtio-hcall.h b/hw/s390x/s390-virtio-hcall.h index 3ae6d6ae3a..3d9fe147d2 100644 --- a/hw/s390x/s390-virtio-hcall.h +++ b/hw/s390x/s390-virtio-hcall.h @@ -18,8 +18,6 @@ /* The only thing that we need from the old kvm_virtio.h file */ #define KVM_S390_VIRTIO_NOTIFY 0 -typedef int (*s390_virtio_fn)(const uint64_t *args); -void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); int s390_virtio_hypercall(CPUS390XState *env); #endif /* HW_S390_VIRTIO_HCALL_H */ diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index dd0322c43a..32cf70bb19 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -51,6 +51,7 @@ #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-virtio-hcall.h" #include "target/s390x/kvm/pv.h" +#include CONFIG_DEVICES #define kvm_vm_check_mem_attr(s, attr) \ kvm_vm_check_attr(s, KVM_S390_VM_MEM_CTRL, attr) @@ -1494,9 +1495,11 @@ static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) { CPUS390XState *env = &cpu->env; - int ret; + int ret = -EINVAL; +#ifdef CONFIG_S390_CCW_VIRTIO ret = s390_virtio_hypercall(env); +#endif /* CONFIG_S390_CCW_VIRTIO */ if (ret == -EINVAL) { kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION); return 0; diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 303f86d363..f44136a568 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -43,6 +43,7 @@ #include "hw/s390x/s390-pci-inst.h" #include "hw/boards.h" #include "hw/s390x/tod.h" +#include CONFIG_DEVICES #endif /* #define DEBUG_HELPER */ @@ -116,12 +117,14 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) uint64_t r; switch (num) { +#ifdef CONFIG_S390_CCW_VIRTIO case 0x500: /* KVM hypercall */ bql_lock(); r = s390_virtio_hypercall(env); bql_unlock(); break; +#endif /* CONFIG_S390_CCW_VIRTIO */ case 0x44: /* yield */ r = 0; From 6e9cc2da4e8b997fd6ff3249034f436b84fc7974 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:04 +0100 Subject: [PATCH 0605/2892] s390x/s390-virtio-hcall: prepare for more diag500 hypercalls Let's generalize, abstracting the virtio bits. diag500 is now a generic hypercall to handle QEMU/KVM specific things. Explicitly specify all already defined subcodes, including legacy ones (so we know what we can use for new hypercalls). Move the PGM_SPECIFICATION injection into the renamed function handle_diag_500(), so we can turn it into a void function. We'll rename the files separately, so git properly detects the rename. Message-ID: <20241219144115.2820241-4-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-virtio-hcall.c | 15 ++++++++------- hw/s390x/s390-virtio-hcall.h | 11 ++++++----- target/s390x/kvm/kvm.c | 20 +++----------------- target/s390x/tcg/misc_helper.c | 5 +++-- 4 files changed, 20 insertions(+), 31 deletions(-) diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c index ca49e3cd22..5fb78a719e 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-virtio-hcall.c @@ -1,5 +1,5 @@ /* - * Support for virtio hypercalls on s390 + * Support for QEMU/KVM hypercalls on s390 * * Copyright 2012 IBM Corp. * Author(s): Cornelia Huck @@ -57,18 +57,19 @@ static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) return 0; } -int s390_virtio_hypercall(CPUS390XState *env) +void handle_diag_500(S390CPU *cpu, uintptr_t ra) { + CPUS390XState *env = &cpu->env; const uint64_t subcode = env->regs[1]; switch (subcode) { - case KVM_S390_VIRTIO_NOTIFY: + case DIAG500_VIRTIO_NOTIFY: env->regs[2] = handle_virtio_notify(env->regs[2]); - return 0; - case KVM_S390_VIRTIO_CCW_NOTIFY: + break; + case DIAG500_VIRTIO_CCW_NOTIFY: env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); - return 0; + break; default: - return -EINVAL; + s390_program_interrupt(env, PGM_SPECIFICATION, ra); } } diff --git a/hw/s390x/s390-virtio-hcall.h b/hw/s390x/s390-virtio-hcall.h index 3d9fe147d2..dca456b926 100644 --- a/hw/s390x/s390-virtio-hcall.h +++ b/hw/s390x/s390-virtio-hcall.h @@ -1,5 +1,5 @@ /* - * Support for virtio hypercalls on s390x + * Support for QEMU/KVM hypercalls on s390x * * Copyright IBM Corp. 2012, 2017 * Author(s): Cornelia Huck @@ -12,12 +12,13 @@ #ifndef HW_S390_VIRTIO_HCALL_H #define HW_S390_VIRTIO_HCALL_H -#include "standard-headers/asm-s390/virtio-ccw.h" #include "cpu.h" -/* The only thing that we need from the old kvm_virtio.h file */ -#define KVM_S390_VIRTIO_NOTIFY 0 +#define DIAG500_VIRTIO_NOTIFY 0 /* legacy, implemented as a NOP */ +#define DIAG500_VIRTIO_RESET 1 /* legacy */ +#define DIAG500_VIRTIO_SET_STATUS 2 /* legacy */ +#define DIAG500_VIRTIO_CCW_NOTIFY 3 /* KVM_S390_VIRTIO_CCW_NOTIFY */ -int s390_virtio_hypercall(CPUS390XState *env); +void handle_diag_500(S390CPU *cpu, uintptr_t ra); #endif /* HW_S390_VIRTIO_HCALL_H */ diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 32cf70bb19..508403609f 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -1492,22 +1492,6 @@ static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) return r; } -static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) -{ - CPUS390XState *env = &cpu->env; - int ret = -EINVAL; - -#ifdef CONFIG_S390_CCW_VIRTIO - ret = s390_virtio_hypercall(env); -#endif /* CONFIG_S390_CCW_VIRTIO */ - if (ret == -EINVAL) { - kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION); - return 0; - } - - return ret; -} - static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run) { uint64_t r1, r3; @@ -1603,9 +1587,11 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) case DIAG_SET_CONTROL_PROGRAM_CODES: handle_diag_318(cpu, run); break; +#ifdef CONFIG_S390_CCW_VIRTIO case DIAG_KVM_HYPERCALL: - r = handle_hypercall(cpu, run); + handle_diag_500(cpu, RA_IGNORED); break; +#endif /* CONFIG_S390_CCW_VIRTIO */ case DIAG_KVM_BREAKPOINT: r = handle_sw_breakpoint(cpu, run); break; diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index f44136a568..2b4310003b 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -119,10 +119,11 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) switch (num) { #ifdef CONFIG_S390_CCW_VIRTIO case 0x500: - /* KVM hypercall */ + /* QEMU/KVM hypercall */ bql_lock(); - r = s390_virtio_hypercall(env); + handle_diag_500(env_archcpu(env), GETPC()); bql_unlock(); + r = 0; break; #endif /* CONFIG_S390_CCW_VIRTIO */ case 0x44: From 85489fc3652d0c4433c940f1a80a952e8cb5d3cb Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:05 +0100 Subject: [PATCH 0606/2892] s390x: rename s390-virtio-hcall* to s390-hypercall* Let's make it clearer that we are talking about general QEMU/KVM-specific hypercalls. Message-ID: <20241219144115.2820241-5-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/meson.build | 2 +- hw/s390x/{s390-virtio-hcall.c => s390-hypercall.c} | 2 +- hw/s390x/{s390-virtio-hcall.h => s390-hypercall.h} | 6 +++--- target/s390x/kvm/kvm.c | 2 +- target/s390x/tcg/misc_helper.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename hw/s390x/{s390-virtio-hcall.c => s390-hypercall.c} (97%) rename hw/s390x/{s390-virtio-hcall.h => s390-hypercall.h} (86%) diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index d6c8c33915..e344a3bd8c 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -29,7 +29,7 @@ s390x_ss.add(when: 'CONFIG_TCG', if_true: files( )) s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files( 's390-virtio-ccw.c', - 's390-virtio-hcall.c', + 's390-hypercall.c', )) s390x_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('3270-ccw.c')) s390x_ss.add(when: 'CONFIG_VFIO', if_true: files('s390-pci-vfio.c')) diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-hypercall.c similarity index 97% rename from hw/s390x/s390-virtio-hcall.c rename to hw/s390x/s390-hypercall.c index 5fb78a719e..f816c2b1ef 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-hypercall.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "hw/boards.h" -#include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/s390-hypercall.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/css.h" #include "virtio-ccw.h" diff --git a/hw/s390x/s390-virtio-hcall.h b/hw/s390x/s390-hypercall.h similarity index 86% rename from hw/s390x/s390-virtio-hcall.h rename to hw/s390x/s390-hypercall.h index dca456b926..2fa81dbfdd 100644 --- a/hw/s390x/s390-virtio-hcall.h +++ b/hw/s390x/s390-hypercall.h @@ -9,8 +9,8 @@ * directory. */ -#ifndef HW_S390_VIRTIO_HCALL_H -#define HW_S390_VIRTIO_HCALL_H +#ifndef HW_S390_HYPERCALL_H +#define HW_S390_HYPERCALL_H #include "cpu.h" @@ -21,4 +21,4 @@ void handle_diag_500(S390CPU *cpu, uintptr_t ra); -#endif /* HW_S390_VIRTIO_HCALL_H */ +#endif /* HW_S390_HYPERCALL_H */ diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 508403609f..7a3e1a8e1e 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -49,7 +49,7 @@ #include "hw/s390x/ebcdic.h" #include "exec/memattrs.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/s390-hypercall.h" #include "target/s390x/kvm/pv.h" #include CONFIG_DEVICES diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 2b4310003b..b726a95352 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -36,7 +36,7 @@ #include "sysemu/cpus.h" #include "sysemu/sysemu.h" #include "hw/s390x/ebcdic.h" -#include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/s390-hypercall.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/ioinst.h" From 3c6fb557d295949bea291c3bf88ee9c83392e78c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:06 +0100 Subject: [PATCH 0607/2892] s390x/s390-virtio-ccw: move setting the maximum guest size from sclp to machine code Nowadays, it feels more natural to have that code located in s390_memory_init(), where we also have direct access to the machine object. While at it, use the actual RAM size, not the maximum RAM size which cannot currently be reached without support for any memory devices. Consequently update s390_pv_vm_try_disable_async() to rely on the RAM size as well, to avoid temporary issues while we further rework that handling. set_memory_limit() is temporary, we'll merge it with s390_set_memory_limit() next. Message-ID: <20241219144115.2820241-6-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-virtio-ccw.c | 28 ++++++++++++++++++++++++---- hw/s390x/sclp.c | 11 ----------- target/s390x/kvm/pv.c | 2 +- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index b0edaa0872..a28e615c5a 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -121,11 +121,29 @@ static void subsystem_reset(void) } } -static void s390_memory_init(MemoryRegion *ram) +static void set_memory_limit(uint64_t new_limit) +{ + uint64_t hw_limit; + int ret; + + ret = s390_set_memory_limit(new_limit, &hw_limit); + if (ret == -E2BIG) { + error_report("host supports a maximum of %" PRIu64 " GB", + hw_limit / GiB); + exit(EXIT_FAILURE); + } else if (ret) { + error_report("setting the guest size failed"); + exit(EXIT_FAILURE); + } +} + +static void s390_memory_init(MachineState *machine) { MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = machine->ram; + uint64_t ram_size = memory_region_size(ram); - if (!QEMU_IS_ALIGNED(memory_region_size(ram), 1 * MiB)) { + if (!QEMU_IS_ALIGNED(ram_size, 1 * MiB)) { /* * SCLP cannot possibly expose smaller granularity right now and KVM * cannot handle smaller granularity. As we don't support NUMA, the @@ -136,7 +154,9 @@ static void s390_memory_init(MemoryRegion *ram) exit(EXIT_FAILURE); } - /* allocate RAM for core */ + set_memory_limit(ram_size); + + /* Map the initial memory. Must happen after setting the memory limit. */ memory_region_add_subregion(sysmem, 0, ram); /* @@ -211,7 +231,7 @@ static void ccw_init(MachineState *machine) qdev_realize_and_unref(DEVICE(ms->sclp), NULL, &error_fatal); /* init memory + setup max page size. Required for the CPU model */ - s390_memory_init(machine->ram); + s390_memory_init(machine); /* init CPUs (incl. CPU model) early so s390_has_feature() works */ s390_init_cpus(machine); diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 8757626b5c..73e88ab4eb 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -376,10 +376,7 @@ void sclp_service_interrupt(uint32_t sccb) /* qemu object creation and initialization functions */ static void sclp_realize(DeviceState *dev, Error **errp) { - MachineState *machine = MACHINE(qdev_get_machine()); SCLPDevice *sclp = SCLP(dev); - uint64_t hw_limit; - int ret; /* * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long @@ -389,14 +386,6 @@ static void sclp_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(sclp->event_facility), errp)) { return; } - - ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); - if (ret == -E2BIG) { - error_setg(errp, "host supports a maximum of %" PRIu64 " GB", - hw_limit / GiB); - } else if (ret) { - error_setg(errp, "setting the guest size failed"); - } } static void sclp_memory_init(SCLPDevice *sclp) diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index dde836d21a..424cce75ca 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -133,7 +133,7 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) * If the feature is not present or if the VM is not larger than 2 GiB, * KVM_PV_ASYNC_CLEANUP_PREPARE fill fail; no point in attempting it. */ - if ((MACHINE(ms)->maxram_size <= 2 * GiB) || + if ((MACHINE(ms)->ram_size <= 2 * GiB) || !kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) { return false; } From 27221b69a3ea49339a1f82b9622126f3928e0915 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:07 +0100 Subject: [PATCH 0608/2892] s390x: introduce s390_get_memory_limit() Let's add s390_get_memory_limit(), to query what has been successfully set via s390_set_memory_limit(). Allow setting the limit only once. We'll remember the limit in the machine state. Move s390_set_memory_limit() to machine code, merging it into set_memory_limit(), because this really is a machine property. Message-ID: <20241219144115.2820241-7-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-virtio-ccw.c | 17 ++++++++++++----- include/hw/s390x/s390-virtio-ccw.h | 8 ++++++++ target/s390x/cpu-sysemu.c | 8 -------- target/s390x/cpu.h | 1 - 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a28e615c5a..1c56b70dcd 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -45,6 +45,7 @@ #include "migration/blocker.h" #include "qapi/visitor.h" #include "hw/s390x/cpu-topology.h" +#include "kvm/kvm_s390x.h" #include CONFIG_DEVICES static Error *pv_mig_blocker; @@ -121,12 +122,16 @@ static void subsystem_reset(void) } } -static void set_memory_limit(uint64_t new_limit) +static void s390_set_memory_limit(S390CcwMachineState *s390ms, + uint64_t new_limit) { - uint64_t hw_limit; - int ret; + uint64_t hw_limit = 0; + int ret = 0; - ret = s390_set_memory_limit(new_limit, &hw_limit); + assert(!s390ms->memory_limit && new_limit); + if (kvm_enabled()) { + ret = kvm_s390_set_mem_limit(new_limit, &hw_limit); + } if (ret == -E2BIG) { error_report("host supports a maximum of %" PRIu64 " GB", hw_limit / GiB); @@ -135,10 +140,12 @@ static void set_memory_limit(uint64_t new_limit) error_report("setting the guest size failed"); exit(EXIT_FAILURE); } + s390ms->memory_limit = new_limit; } static void s390_memory_init(MachineState *machine) { + S390CcwMachineState *s390ms = S390_CCW_MACHINE(machine); MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = machine->ram; uint64_t ram_size = memory_region_size(ram); @@ -154,7 +161,7 @@ static void s390_memory_init(MachineState *machine) exit(EXIT_FAILURE); } - set_memory_limit(ram_size); + s390_set_memory_limit(s390ms, ram_size); /* Map the initial memory. Must happen after setting the memory limit. */ memory_region_add_subregion(sysmem, 0, ram); diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 996864a34e..de04336c5a 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -29,10 +29,18 @@ struct S390CcwMachineState { bool dea_key_wrap; bool pv; uint8_t loadparm[8]; + uint64_t memory_limit; SCLPDevice *sclp; }; +static inline uint64_t s390_get_memory_limit(S390CcwMachineState *s390ms) +{ + /* We expect to be called only after the limit was set. */ + assert(s390ms->memory_limit); + return s390ms->memory_limit; +} + #define S390_PTF_REASON_NONE (0x00 << 8) #define S390_PTF_REASON_DONE (0x01 << 8) #define S390_PTF_REASON_BUSY (0x02 << 8) diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index 1cd30c1d84..3118a25fee 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -255,14 +255,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) return s390_count_running_cpus(); } -int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) -{ - if (kvm_enabled()) { - return kvm_s390_set_mem_limit(new_limit, hw_limit); - } - return 0; -} - void s390_set_max_pagesize(uint64_t pagesize, Error **errp) { if (kvm_enabled()) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 5ef61b1f75..b4506539f0 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -881,7 +881,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) /* cpu.c */ void s390_crypto_reset(void); -int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); void s390_set_max_pagesize(uint64_t pagesize, Error **errp); void s390_cmma_reset(void); void s390_enable_css_support(S390CPU *cpu); From f7c168657816486527727d860b73747d41f0c5f6 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:08 +0100 Subject: [PATCH 0609/2892] s390x/s390-hypercall: introduce DIAG500 STORAGE_LIMIT A guest OS that supports memory hotplug / memory devices must during boot be aware of the maximum possible physical memory address that it might have to handle at a later stage during its runtime. For example, the maximum possible memory address might be required to prepare the kernel virtual address space accordingly (e.g., select page table hierarchy depth). On s390x there is currently no such mechanism that is compatible with paravirtualized memory devices, because the whole SCLP interface was designed around the idea of "storage increments" and "standby memory". Paravirtualized memory devices we want to support, such as virtio-mem, have no intersection with any of that, but could co-exist with them in the future if ever needed. In particular, a guest OS must never detect and use device memory without the help of a proper device driver. Device memory must not be exposed in any firmware-provided memory map (SCLP or diag260 on s390x). For this reason, these memory devices will be places in memory *above* the "maximum storage increment" exposed via SCLP. Let's provide a new diag500 subcode to query the memory limit determined in s390_memory_init(). Message-ID: <20241219144115.2820241-8-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-hypercall.c | 12 +++++++++++- hw/s390x/s390-hypercall.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-hypercall.c b/hw/s390x/s390-hypercall.c index f816c2b1ef..ac1b08b2cd 100644 --- a/hw/s390x/s390-hypercall.c +++ b/hw/s390x/s390-hypercall.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/boards.h" +#include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-hypercall.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/css.h" @@ -57,6 +57,13 @@ static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) return 0; } +static uint64_t handle_storage_limit(void) +{ + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + + return s390_get_memory_limit(s390ms) - 1; +} + void handle_diag_500(S390CPU *cpu, uintptr_t ra) { CPUS390XState *env = &cpu->env; @@ -69,6 +76,9 @@ void handle_diag_500(S390CPU *cpu, uintptr_t ra) case DIAG500_VIRTIO_CCW_NOTIFY: env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); break; + case DIAG500_STORAGE_LIMIT: + env->regs[2] = handle_storage_limit(); + break; default: s390_program_interrupt(env, PGM_SPECIFICATION, ra); } diff --git a/hw/s390x/s390-hypercall.h b/hw/s390x/s390-hypercall.h index 2fa81dbfdd..4f07209128 100644 --- a/hw/s390x/s390-hypercall.h +++ b/hw/s390x/s390-hypercall.h @@ -18,6 +18,7 @@ #define DIAG500_VIRTIO_RESET 1 /* legacy */ #define DIAG500_VIRTIO_SET_STATUS 2 /* legacy */ #define DIAG500_VIRTIO_CCW_NOTIFY 3 /* KVM_S390_VIRTIO_CCW_NOTIFY */ +#define DIAG500_STORAGE_LIMIT 4 void handle_diag_500(S390CPU *cpu, uintptr_t ra); From 241e6b2d27b090b17cda5b011b2064544b0c458b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:09 +0100 Subject: [PATCH 0610/2892] s390x/s390-stattrib-kvm: prepare for memory devices and sparse memory layouts With memory devices, we will have storage attributes for memory that exceeds the initial ram size. Further, we can easily have memory holes, for which there (currently) are no storage attributes. In particular, with memory holes, KVM_S390_SET_CMMA_BITS will fail to set some storage attributes. So let's do it like we handle storage keys migration, relying on guest_phys_blocks_append(). However, in contrast to storage key migration, we will handle it on the migration destination. This is a preparation for virtio-mem support. Note that ever since the "early migration" feature was added (x-early-migration), the state of device blocks (plugged/unplugged) is migrated early such that guest_phys_blocks_append() will properly consider all currently plugged memory blocks and skip any unplugged ones. In the future, we should try getting rid of the large temporary buffer and also not send any attributes for any memory holes, just so they get ignored on the destination. Message-ID: <20241219144115.2820241-9-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-stattrib-kvm.c | 77 ++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index eeaa811098..33ec91422a 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -10,11 +10,12 @@ */ #include "qemu/osdep.h" -#include "hw/boards.h" +#include "hw/s390x/s390-virtio-ccw.h" #include "migration/qemu-file.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "sysemu/memory_mapping.h" #include "exec/ram_addr.h" #include "kvm/kvm_s390x.h" #include "qapi/error.h" @@ -84,8 +85,8 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa, uint8_t *values) { KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); - MachineState *machine = MACHINE(qdev_get_machine()); - unsigned long max = machine->ram_size / TARGET_PAGE_SIZE; + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; if (start_gfn + count > max) { error_report("Out of memory bounds when setting storage attributes"); @@ -103,39 +104,57 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa, static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) { KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); - MachineState *machine = MACHINE(qdev_get_machine()); - unsigned long max = machine->ram_size / TARGET_PAGE_SIZE; - /* We do not need to reach the maximum buffer size allowed */ - unsigned long cx, len = KVM_S390_SKEYS_MAX / 2; + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; + unsigned long start_gfn, end_gfn, pages; + GuestPhysBlockList guest_phys_blocks; + GuestPhysBlock *block; int r; struct kvm_s390_cmma_log clog = { .flags = 0, .mask = ~0ULL, }; - if (sas->incoming_buffer) { - for (cx = 0; cx + len <= max; cx += len) { - clog.start_gfn = cx; - clog.count = len; - clog.values = (uint64_t)(sas->incoming_buffer + cx); - r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); - if (r) { - error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); - return; - } - } - if (cx < max) { - clog.start_gfn = cx; - clog.count = max - cx; - clog.values = (uint64_t)(sas->incoming_buffer + cx); - r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); - if (r) { - error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); - } - } - g_free(sas->incoming_buffer); - sas->incoming_buffer = NULL; + if (!sas->incoming_buffer) { + return; } + guest_phys_blocks_init(&guest_phys_blocks); + guest_phys_blocks_append(&guest_phys_blocks); + + QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) { + assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE)); + assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE)); + + start_gfn = block->target_start / TARGET_PAGE_SIZE; + end_gfn = block->target_end / TARGET_PAGE_SIZE; + + while (start_gfn < end_gfn) { + /* Don't exceed the maximum buffer size. */ + pages = MIN(end_gfn - start_gfn, KVM_S390_SKEYS_MAX / 2); + + /* + * If we ever get guest physical memory beyond the configured + * memory limit, something went very wrong. + */ + assert(start_gfn + pages <= max); + + clog.start_gfn = start_gfn; + clog.count = pages; + clog.values = (uint64_t)(sas->incoming_buffer + start_gfn); + r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); + if (r) { + error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); + goto out; + } + + start_gfn += pages; + } + } + +out: + guest_phys_blocks_free(&guest_phys_blocks); + g_free(sas->incoming_buffer); + sas->incoming_buffer = NULL; } static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val, From d1e3c2ac41b3f73708682e4e8212c32ad35013b9 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:10 +0100 Subject: [PATCH 0611/2892] s390x/s390-skeys: prepare for memory devices With memory devices, we will have storage keys for memory that exceeds the initial ram size. The TODO already states that current handling is subopimal, but we won't worry about improving that (TCG-only) thing for now. Message-ID: <20241219144115.2820241-10-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-skeys.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 6d0a47ed73..6ea4d8c20e 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/boards.h" +#include "hw/s390x/s390-virtio-ccw.h" #include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" @@ -251,9 +251,9 @@ static bool qemu_s390_enable_skeys(S390SKeysState *ss) * g_once_init_enter() is good enough. */ if (g_once_init_enter(&initialized)) { - MachineState *machine = MACHINE(qdev_get_machine()); + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); - skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE; + skeys->key_count = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; skeys->keydata = g_malloc0(skeys->key_count); g_once_init_leave(&initialized, 1); } From 1e86400298cf0fed5f7d49427db477775b859093 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:11 +0100 Subject: [PATCH 0612/2892] s390x/s390-virtio-ccw: prepare for memory devices Let's prepare our address space for memory devices if enabled via "maxmem" and if we have CONFIG_MEM_DEVICE enabled at all. Note that CONFIG_MEM_DEVICE will be selected automatically once we add support for devices. Just like on other architectures, the region container for memory devices is placed directly above our initial memory. For now, we only align the start address of the region up to 1 GiB, but we won't add any additional space to the region for internal alignment purposes; this can be done in the future if really required. The RAM size returned via SCLP is not modified, as this only covers initial RAM (and standby memory we don't implement) and not memory devices; clarify that in the docs of read_SCP_info(). Existing OSes without support for memory devices will keep working as is, even when memory devices would be attached the VM. Guest OSs which support memory devices, such as virtio-mem, will consult diag500(), to find out the maximum possible pfn. Guest OSes that don't support memory devices, don't have to be changed and will continue relying on information provided by SCLP. There are no remaining maxram_size users in s390x code, and the remaining ram_size users only care about initial RAM: * hw/s390x/ipl.c * hw/s390x/s390-hypercall.c * hw/s390x/sclp.c * target/s390x/kvm/pv.c Message-ID: <20241219144115.2820241-11-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-virtio-ccw.c | 23 ++++++++++++++++++++++- hw/s390x/sclp.c | 6 +++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 1c56b70dcd..2ba66be018 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -149,6 +149,7 @@ static void s390_memory_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = machine->ram; uint64_t ram_size = memory_region_size(ram); + uint64_t devmem_base, devmem_size; if (!QEMU_IS_ALIGNED(ram_size, 1 * MiB)) { /* @@ -161,11 +162,31 @@ static void s390_memory_init(MachineState *machine) exit(EXIT_FAILURE); } - s390_set_memory_limit(s390ms, ram_size); + devmem_size = 0; + devmem_base = ram_size; +#ifdef CONFIG_MEM_DEVICE + if (machine->ram_size < machine->maxram_size) { + + /* + * Make sure memory devices have a sane default alignment, even + * when weird initial memory sizes are specified. + */ + devmem_base = QEMU_ALIGN_UP(devmem_base, 1 * GiB); + devmem_size = machine->maxram_size - machine->ram_size; + } +#endif + s390_set_memory_limit(s390ms, devmem_base + devmem_size); /* Map the initial memory. Must happen after setting the memory limit. */ memory_region_add_subregion(sysmem, 0, ram); + /* Initialize address space for memory devices. */ +#ifdef CONFIG_MEM_DEVICE + if (devmem_size) { + machine_memory_devices_init(machine, devmem_base, devmem_size); + } +#endif /* CONFIG_MEM_DEVICE */ + /* * Configure the maximum page size. As no memory devices were created * yet, this is the page size of initial memory only. diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 73e88ab4eb..5945c9b1d8 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -161,7 +161,11 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnsize2 = cpu_to_be32(rnsize); } - /* we don't support standby memory, maxram_size is never exposed */ + /* + * We don't support standby memory. maxram_size is used for sizing the + * memory device region, which is not exposed through SCLP but through + * diag500. + */ rnmax = machine->ram_size >> sclp->increment_size; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); From a056332e732110c8ef0d40ffd49bd03afc2f04ca Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:12 +0100 Subject: [PATCH 0613/2892] s390x/pv: prepare for memory devices Let's avoid checking for the maxram_size, and instead rely on the memory limit determined in s390_memory_init(), that might be larger than maxram_size, for example due to alignment purposes. This check now correctly mimics what the kernel will check in kvm_s390_pv_set_aside(), whereby a VM <= 2 GiB VM would end up using a segment type ASCE. Message-ID: <20241219144115.2820241-12-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Nina Schoetterl-Glausch Signed-off-by: David Hildenbrand --- target/s390x/kvm/pv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index 424cce75ca..fa66607e7b 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -133,7 +133,7 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) * If the feature is not present or if the VM is not larger than 2 GiB, * KVM_PV_ASYNC_CLEANUP_PREPARE fill fail; no point in attempting it. */ - if ((MACHINE(ms)->ram_size <= 2 * GiB) || + if (s390_get_memory_limit(ms) <= 2 * GiB || !kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) { return false; } From df2ac211a62e6ced7f1495b634fa6f78962f2321 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:13 +0100 Subject: [PATCH 0614/2892] s390x: remember the maximum page size Let's remember the value (successfully) set via s390_set_max_pagesize(). This will be helpful to reject hotplugged memory devices that would exceed this initially set page size. Handle it just like how we handle s390_get_memory_limit(), storing it in the machine, and moving the handling to machine code. Message-ID: <20241219144115.2820241-13-david@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand --- hw/s390x/s390-virtio-ccw.c | 12 +++++++++++- include/hw/s390x/s390-virtio-ccw.h | 1 + target/s390x/cpu-sysemu.c | 7 ------- target/s390x/cpu.h | 1 - 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 2ba66be018..9f8d830798 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -143,6 +143,16 @@ static void s390_set_memory_limit(S390CcwMachineState *s390ms, s390ms->memory_limit = new_limit; } +static void s390_set_max_pagesize(S390CcwMachineState *s390ms, + uint64_t pagesize) +{ + assert(!s390ms->max_pagesize && pagesize); + if (kvm_enabled()) { + kvm_s390_set_max_pagesize(pagesize, &error_fatal); + } + s390ms->max_pagesize = pagesize; +} + static void s390_memory_init(MachineState *machine) { S390CcwMachineState *s390ms = S390_CCW_MACHINE(machine); @@ -191,7 +201,7 @@ static void s390_memory_init(MachineState *machine) * Configure the maximum page size. As no memory devices were created * yet, this is the page size of initial memory only. */ - s390_set_max_pagesize(qemu_maxrampagesize(), &error_fatal); + s390_set_max_pagesize(s390ms, qemu_maxrampagesize()); /* Initialize storage key device */ s390_skeys_init(); /* Initialize storage attributes device */ diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index de04336c5a..599740a998 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -30,6 +30,7 @@ struct S390CcwMachineState { bool pv; uint8_t loadparm[8]; uint64_t memory_limit; + uint64_t max_pagesize; SCLPDevice *sclp; }; diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index 3118a25fee..706a5c53e2 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -255,13 +255,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) return s390_count_running_cpus(); } -void s390_set_max_pagesize(uint64_t pagesize, Error **errp) -{ - if (kvm_enabled()) { - kvm_s390_set_max_pagesize(pagesize, errp); - } -} - void s390_cmma_reset(void) { if (kvm_enabled()) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index b4506539f0..5b7992deda 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -881,7 +881,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) /* cpu.c */ void s390_crypto_reset(void); -void s390_set_max_pagesize(uint64_t pagesize, Error **errp); void s390_cmma_reset(void); void s390_enable_css_support(S390CPU *cpu); void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg); From 88d86f6f1e36741ba9e1625da19a7ccf1a343d39 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:14 +0100 Subject: [PATCH 0615/2892] s390x/virtio-ccw: add support for virtio based memory devices Let's implement support for abstract virtio based memory devices, using the virtio-pci implementation as an orientation. Wire them up in the machine hotplug handler, taking care of s390x page size limitations. As we neither support virtio-mem or virtio-pmem yet, the code is effectively unused. We'll implement support for virtio-mem based on this next. Note that we won't wire up the virtio-pci variant (should currently be impossible due to lack of support for MSI-X), but we'll add a safety net to reject plugging them in the pre-plug handler. Message-ID: <20241219144115.2820241-14-david@redhat.com> Acked-by: Michael S. Tsirkin Signed-off-by: David Hildenbrand --- MAINTAINERS | 3 + hw/s390x/meson.build | 3 + hw/s390x/s390-virtio-ccw.c | 47 +++++++++- hw/s390x/virtio-ccw-md-stubs.c | 24 ++++++ hw/s390x/virtio-ccw-md.c | 153 +++++++++++++++++++++++++++++++++ hw/s390x/virtio-ccw-md.h | 44 ++++++++++ hw/virtio/Kconfig | 1 + 7 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 hw/s390x/virtio-ccw-md-stubs.c create mode 100644 hw/s390x/virtio-ccw-md.c create mode 100644 hw/s390x/virtio-ccw-md.h diff --git a/MAINTAINERS b/MAINTAINERS index 430a0f4f8c..afb1203597 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2376,6 +2376,9 @@ F: include/hw/virtio/virtio-crypto.h virtio based memory device M: David Hildenbrand S: Supported +F: hw/s390x/virtio-ccw-md.c +F: hw/s390x/virtio-ccw-md.h +F: hw/s390x/virtio-ccw-md-stubs.c F: hw/virtio/virtio-md-pci.c F: include/hw/virtio/virtio-md-pci.h F: stubs/virtio-md-pci.c diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index e344a3bd8c..4431868408 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -50,8 +50,11 @@ endif virtio_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-ccw.c')) +virtio_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-ccw-md.c')) s390x_ss.add_all(when: 'CONFIG_VIRTIO_CCW', if_true: virtio_ss) +s390x_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-ccw-md-stubs.c')) + hw_arch += {'s390x': s390x_ss} hw_s390x_modules = {} diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 9f8d830798..097ec78826 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -46,6 +46,8 @@ #include "qapi/visitor.h" #include "hw/s390x/cpu-topology.h" #include "kvm/kvm_s390x.h" +#include "hw/virtio/virtio-md-pci.h" +#include "hw/s390x/virtio-ccw-md.h" #include CONFIG_DEVICES static Error *pv_mig_blocker; @@ -546,11 +548,39 @@ static void s390_machine_reset(MachineState *machine, ResetType type) s390_ipl_clear_reset_request(); } +static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_pre_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + error_setg(errp, + "PCI-attached virtio based memory devices not supported"); + } +} + static void s390_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + S390CcwMachineState *s390ms = S390_CCW_MACHINE(hotplug_dev); + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { s390_cpu_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + /* + * At this point, the device is realized and set all memdevs mapped, so + * qemu_maxrampagesize() will pick up the page sizes of these memdevs + * as well. Before we plug the device and expose any RAM memory regions + * to the system, make sure we don't exceed the previously set max page + * size. While only relevant for KVM, there is not really any use case + * for this with TCG, so we'll unconditionally reject it. + */ + if (qemu_maxrampagesize() != s390ms->max_pagesize) { + error_setg(errp, "Memory device uses a bigger page size than" + " initial memory"); + return; + } + virtio_ccw_md_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); } } @@ -560,9 +590,20 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { error_setg(errp, "CPU hot unplug not supported on this machine"); return; + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_unplug_request(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), + errp); } } +static void s390_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_unplug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); + } + } + static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { @@ -609,7 +650,9 @@ static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms) static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, DeviceState *dev) { - if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { return HOTPLUG_HANDLER(machine); } return NULL; @@ -769,8 +812,10 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids; /* it is overridden with 'host' cpu *in kvm_arch_init* */ mc->default_cpu_type = S390_CPU_TYPE_NAME("qemu"); + hc->pre_plug = s390_machine_device_pre_plug; hc->plug = s390_machine_device_plug; hc->unplug_request = s390_machine_device_unplug_request; + hc->unplug = s390_machine_device_unplug; nc->nmi_monitor_handler = s390_nmi; mc->default_ram_id = "s390.ram"; mc->default_nic = "virtio-net-ccw"; diff --git a/hw/s390x/virtio-ccw-md-stubs.c b/hw/s390x/virtio-ccw-md-stubs.c new file mode 100644 index 0000000000..e937865550 --- /dev/null +++ b/hw/s390x/virtio-ccw-md-stubs.c @@ -0,0 +1,24 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/virtio-ccw-md.h" + +void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms, + Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} diff --git a/hw/s390x/virtio-ccw-md.c b/hw/s390x/virtio-ccw-md.c new file mode 100644 index 0000000000..de333282df --- /dev/null +++ b/hw/s390x/virtio-ccw-md.c @@ -0,0 +1,153 @@ +/* + * Virtio CCW support for abstract virtio based memory device + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/s390x/virtio-ccw-md.h" +#include "hw/mem/memory-device.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + MemoryDeviceState *md = MEMORY_DEVICE(vmd); + Error *local_err = NULL; + + if (!bus_handler && dev->hotplugged) { + /* + * Without a bus hotplug handler, we cannot control the plug/unplug + * order. We should never reach this point when hotplugging, but + * better add a safety net. + */ + error_setg(errp, "hotplug of virtio based memory devices not supported" + " on this bus."); + return; + } + + /* + * First, see if we can plug this memory device at all. If that + * succeeds, branch of to the actual hotplug handler. + */ + memory_device_pre_plug(md, ms, &local_err); + if (!local_err && bus_handler) { + hotplug_handler_pre_plug(bus_handler, dev, &local_err); + } + error_propagate(errp, local_err); +} + +void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + MemoryDeviceState *md = MEMORY_DEVICE(vmd); + Error *local_err = NULL; + + /* + * Plug the memory device first and then branch off to the actual + * hotplug handler. If that one fails, we can easily undo the memory + * device bits. + */ + memory_device_plug(md, ms); + if (bus_handler) { + hotplug_handler_plug(bus_handler, dev, &local_err); + if (local_err) { + memory_device_unplug(md, ms); + } + } + error_propagate(errp, local_err); +} + +void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms, + Error **errp) +{ + VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_GET_CLASS(vmd); + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + HotplugHandlerClass *hdc; + Error *local_err = NULL; + + if (!vmdc->unplug_request_check) { + error_setg(errp, + "this virtio based memory devices cannot be unplugged"); + return; + } + + if (!bus_handler) { + error_setg(errp, "hotunplug of virtio based memory devices not" + "supported on this bus"); + return; + } + + vmdc->unplug_request_check(vmd, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * Forward the async request or turn it into a sync request (handling it + * like qdev_unplug()). + */ + hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler); + if (hdc->unplug_request) { + hotplug_handler_unplug_request(bus_handler, dev, &local_err); + } else { + virtio_ccw_md_unplug(vmd, ms, &local_err); + if (!local_err) { + object_unparent(OBJECT(dev)); + } + } +} + +void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + MemoryDeviceState *md = MEMORY_DEVICE(vmd); + Error *local_err = NULL; + + /* Unplug the memory device while it is still realized. */ + memory_device_unplug(md, ms); + + if (bus_handler) { + hotplug_handler_unplug(bus_handler, dev, &local_err); + if (local_err) { + /* Not expected to fail ... but still try to recover. */ + memory_device_plug(md, ms); + error_propagate(errp, local_err); + return; + } + } else { + /* Very unexpected, but let's just try to do the right thing. */ + warn_report("Unexpected unplug of virtio based memory device"); + qdev_unrealize(dev); + } +} + +static const TypeInfo virtio_ccw_md_info = { + .name = TYPE_VIRTIO_MD_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOMDCcw), + .class_size = sizeof(VirtIOMDCcwClass), + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, +}; + +static void virtio_ccw_md_register(void) +{ + type_register_static(&virtio_ccw_md_info); +} +type_init(virtio_ccw_md_register) diff --git a/hw/s390x/virtio-ccw-md.h b/hw/s390x/virtio-ccw-md.h new file mode 100644 index 0000000000..39ba864c92 --- /dev/null +++ b/hw/s390x/virtio-ccw-md.h @@ -0,0 +1,44 @@ +/* + * Virtio CCW support for abstract virtio based memory device + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_S390X_VIRTIO_CCW_MD_H +#define HW_S390X_VIRTIO_CCW_MD_H + +#include "virtio-ccw.h" +#include "qom/object.h" + +/* + * virtio-md-ccw: This extends VirtioCcwDevice. + */ +#define TYPE_VIRTIO_MD_CCW "virtio-md-ccw" + +OBJECT_DECLARE_TYPE(VirtIOMDCcw, VirtIOMDCcwClass, VIRTIO_MD_CCW) + +struct VirtIOMDCcwClass { + /* private */ + VirtIOCCWDeviceClass parent; + + /* public */ + void (*unplug_request_check)(VirtIOMDCcw *vmd, Error **errp); +}; + +struct VirtIOMDCcw { + VirtioCcwDevice parent_obj; +}; + +void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp); +void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp); +void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms, + Error **errp); +void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp); + +#endif /* HW_S390X_VIRTIO_CCW_MD_H */ diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 70c77e183d..7648a2d68d 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -29,6 +29,7 @@ config VIRTIO_MMIO config VIRTIO_CCW bool select VIRTIO + select VIRTIO_MD_SUPPORTED config VIRTIO_BALLOON bool From aa910c20ec5f3b10551da19e441b3e2b54406e25 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 19 Dec 2024 15:41:15 +0100 Subject: [PATCH 0616/2892] s390x: virtio-mem support Let's add our virtio-mem-ccw proxy device and wire it up. We should be supporting everything (e.g., device unplug, "dynamic-memslots") that we already support for the virtio-pci variant. With a Linux guest that supports virtio-mem (and has automatic memory onlining properly configured) the following example will work: 1. Start a VM with 4G initial memory and a virtio-mem device with a maximum capacity of 16GB: qemu/build/qemu-system-s390x \ --enable-kvm \ -m 4G,maxmem=20G \ -nographic \ -smp 8 \ -hda Fedora-Server-KVM-40-1.14.s390x.qcow2 \ -chardev socket,id=monitor,path=/var/tmp/monitor,server,nowait \ -mon chardev=monitor,mode=readline \ -object memory-backend-ram,id=mem0,size=16G,reserve=off \ -device virtio-mem-ccw,id=vmem0,memdev=mem0,dynamic-memslots=on 2. Query the current size of virtio-mem device: (qemu) info memory-devices Memory device [virtio-mem]: "vmem0" memaddr: 0x100000000 node: 0 requested-size: 0 size: 0 max-size: 17179869184 block-size: 1048576 memdev: /objects/mem0 3. Request to grow it to 8GB (hotplug 8GB): (qemu) qom-set vmem0 requested-size 8G (qemu) info memory-devices Memory device [virtio-mem]: "vmem0" memaddr: 0x100000000 node: 0 requested-size: 8589934592 size: 8589934592 max-size: 17179869184 block-size: 1048576 memdev: /objects/mem0 4. Request to grow to 16GB (hotplug another 8GB): (qemu) qom-set vmem0 requested-size 16G (qemu) info memory-devices Memory device [virtio-mem]: "vmem0" memaddr: 0x100000000 node: 0 requested-size: 17179869184 size: 17179869184 max-size: 17179869184 block-size: 1048576 memdev: /objects/mem0 5. Try to hotunplug all memory again, shrinking to 0GB: (qemu) qom-set vmem0 requested-size 0G (qemu) info memory-devices Memory device [virtio-mem]: "vmem0" memaddr: 0x100000000 node: 0 requested-size: 0 size: 0 max-size: 17179869184 block-size: 1048576 memdev: /objects/mem0 6. If it worked, unplug the device (qemu) device_del vmem0 (qemu) info memory-devices (qemu) object_del mem0 7. Hotplug a new device with a smaller capacity and directly size it to 1GB (qemu) object_add memory-backend-ram,id=mem0,size=8G,reserve=off (qemu) device_add virtio-mem-ccw,id=vmem0,memdev=mem0,\ dynamic-memslots=on,requested-size=1G (qemu) info memory-devices Memory device [virtio-mem]: "vmem0" memaddr: 0x100000000 node: 0 requested-size: 1073741824 size: 1073741824 max-size: 8589934592 block-size: 1048576 memdev: /objects/mem0 Trying to use a virtio-mem device backed by hugetlb into a !hugetlb VM correctly results in the error: ... Memory device uses a bigger page size than initial memory Note that the virtio-mem driver in Linux will supports 1 MiB (pageblock) granularity. Message-ID: <20241219144115.2820241-15-david@redhat.com> Acked-by: Michael S. Tsirkin Signed-off-by: David Hildenbrand --- MAINTAINERS | 2 + hw/s390x/Kconfig | 1 + hw/s390x/meson.build | 1 + hw/s390x/virtio-ccw-mem.c | 226 ++++++++++++++++++++++++++++++++++++++ hw/s390x/virtio-ccw-mem.h | 34 ++++++ hw/virtio/virtio-mem.c | 4 +- 6 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 hw/s390x/virtio-ccw-mem.c create mode 100644 hw/s390x/virtio-ccw-mem.h diff --git a/MAINTAINERS b/MAINTAINERS index afb1203597..0b7605fbeb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2390,6 +2390,8 @@ W: https://virtio-mem.gitlab.io/ F: hw/virtio/virtio-mem.c F: hw/virtio/virtio-mem-pci.h F: hw/virtio/virtio-mem-pci.c +F: hw/s390x/virtio-ccw-mem.c +F: hw/s390x/virtio-ccw-mem.h F: include/hw/virtio/virtio-mem.h virtio-snd diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig index 82afdaa9dc..02ea199701 100644 --- a/hw/s390x/Kconfig +++ b/hw/s390x/Kconfig @@ -16,3 +16,4 @@ config S390_CCW_VIRTIO select SCLPCONSOLE select VIRTIO_CCW select MSI_NONBROKEN + select VIRTIO_MEM_SUPPORTED diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 4431868408..3bbebfd817 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -51,6 +51,7 @@ virtio_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-ccw.c')) virtio_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-ccw-md.c')) +virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-ccw-mem.c')) s390x_ss.add_all(when: 'CONFIG_VIRTIO_CCW', if_true: virtio_ss) s390x_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-ccw-md-stubs.c')) diff --git a/hw/s390x/virtio-ccw-mem.c b/hw/s390x/virtio-ccw-mem.c new file mode 100644 index 0000000000..bee0d560cb --- /dev/null +++ b/hw/s390x/virtio-ccw-mem.c @@ -0,0 +1,226 @@ +/* + * virtio-mem CCW implementation + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "virtio-ccw-mem.h" +#include "hw/mem/memory-device.h" +#include "qapi/qapi-events-machine.h" +#include "qapi/qapi-events-misc.h" + +static void virtio_ccw_mem_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_realize(vdev, BUS(&ccw_dev->bus), errp); +} + +static void virtio_ccw_mem_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) +{ + object_property_set_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP, addr, errp); +} + +static uint64_t virtio_ccw_mem_get_addr(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP, + &error_abort); +} + +static MemoryRegion *virtio_ccw_mem_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = &dev->vdev; + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + return vmc->get_memory_region(vmem, errp); +} + +static void virtio_ccw_mem_decide_memslots(MemoryDeviceState *md, + unsigned int limit) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = VIRTIO_MEM(&dev->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + vmc->decide_memslots(vmem, limit); +} + +static unsigned int virtio_ccw_mem_get_memslots(MemoryDeviceState *md) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = VIRTIO_MEM(&dev->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + return vmc->get_memslots(vmem); +} + +static uint64_t virtio_ccw_mem_get_plugged_size(const MemoryDeviceState *md, + Error **errp) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_SIZE_PROP, + errp); +} + +static void virtio_ccw_mem_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + VirtioMEMDeviceInfo *vi = g_new0(VirtioMEMDeviceInfo, 1); + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = &dev->vdev; + VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); + DeviceState *vdev = DEVICE(md); + + if (vdev->id) { + vi->id = g_strdup(vdev->id); + } + + /* let the real device handle everything else */ + vpc->fill_device_info(vmem, vi); + + info->u.virtio_mem.data = vi; + info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM; +} + +static uint64_t virtio_ccw_mem_get_min_alignment(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_BLOCK_SIZE_PROP, + &error_abort); +} + +static void virtio_ccw_mem_size_change_notify(Notifier *notifier, void *data) +{ + VirtIOMEMCcw *dev = container_of(notifier, VirtIOMEMCcw, + size_change_notifier); + DeviceState *vdev = DEVICE(dev); + char *qom_path = object_get_canonical_path(OBJECT(dev)); + const uint64_t * const size_p = data; + + qapi_event_send_memory_device_size_change(vdev->id, *size_p, qom_path); + g_free(qom_path); +} + +static void virtio_ccw_mem_unplug_request_check(VirtIOMDCcw *vmd, Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(vmd); + VirtIOMEM *vmem = &dev->vdev; + VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); + + vpc->unplug_request_check(vmem, errp); +} + +static void virtio_ccw_mem_get_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj); + + object_property_get(OBJECT(&dev->vdev), name, v, errp); +} + +static void virtio_ccw_mem_set_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj); + DeviceState *vdev = DEVICE(obj); + + /* + * If we passed virtio_ccw_mem_unplug_request_check(), making sure that + * the requested size is 0, don't allow modifying the requested size + * anymore, otherwise the VM might end up hotplugging memory before + * handling the unplug request. + */ + if (vdev->pending_deleted_event) { + error_setg(errp, "'%s' cannot be changed if the device is in the" + " process of unplug", name); + return; + } + + object_property_set(OBJECT(&dev->vdev), name, v, errp); +} + +static Property virtio_ccw_mem_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_mem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); + VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_CLASS(klass); + + k->realize = virtio_ccw_mem_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, virtio_ccw_mem_properties); + + mdc->get_addr = virtio_ccw_mem_get_addr; + mdc->set_addr = virtio_ccw_mem_set_addr; + mdc->get_plugged_size = virtio_ccw_mem_get_plugged_size; + mdc->get_memory_region = virtio_ccw_mem_get_memory_region; + mdc->decide_memslots = virtio_ccw_mem_decide_memslots; + mdc->get_memslots = virtio_ccw_mem_get_memslots; + mdc->fill_device_info = virtio_ccw_mem_fill_device_info; + mdc->get_min_alignment = virtio_ccw_mem_get_min_alignment; + + vmdc->unplug_request_check = virtio_ccw_mem_unplug_request_check; +} + +static void virtio_ccw_mem_instance_init(Object *obj) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj); + VirtIOMEMClass *vmc; + VirtIOMEM *vmem; + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_MEM); + + dev->size_change_notifier.notify = virtio_ccw_mem_size_change_notify; + vmem = &dev->vdev; + vmc = VIRTIO_MEM_GET_CLASS(vmem); + /* + * We never remove the notifier again, as we expect both devices to + * disappear at the same time. + */ + vmc->add_size_change_notifier(vmem, &dev->size_change_notifier); + + object_property_add_alias(obj, VIRTIO_MEM_BLOCK_SIZE_PROP, + OBJECT(&dev->vdev), VIRTIO_MEM_BLOCK_SIZE_PROP); + object_property_add_alias(obj, VIRTIO_MEM_SIZE_PROP, OBJECT(&dev->vdev), + VIRTIO_MEM_SIZE_PROP); + object_property_add(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, "size", + virtio_ccw_mem_get_requested_size, + virtio_ccw_mem_set_requested_size, NULL, NULL); +} + +static const TypeInfo virtio_ccw_mem = { + .name = TYPE_VIRTIO_MEM_CCW, + .parent = TYPE_VIRTIO_MD_CCW, + .instance_size = sizeof(VirtIOMEMCcw), + .instance_init = virtio_ccw_mem_instance_init, + .class_init = virtio_ccw_mem_class_init, +}; + +static void virtio_ccw_mem_register_types(void) +{ + type_register_static(&virtio_ccw_mem); +} +type_init(virtio_ccw_mem_register_types) diff --git a/hw/s390x/virtio-ccw-mem.h b/hw/s390x/virtio-ccw-mem.h new file mode 100644 index 0000000000..738ab2c744 --- /dev/null +++ b/hw/s390x/virtio-ccw-mem.h @@ -0,0 +1,34 @@ +/* + * Virtio MEM CCW device + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_S390X_VIRTIO_CCW_MEM_H +#define HW_S390X_VIRTIO_CCW_MEM_H + +#include "virtio-ccw-md.h" +#include "hw/virtio/virtio-mem.h" +#include "qom/object.h" + +typedef struct VirtIOMEMCcw VirtIOMEMCcw; + +/* + * virtio-mem-ccw: This extends VirtIOMDCcw + */ +#define TYPE_VIRTIO_MEM_CCW "virtio-mem-ccw" +DECLARE_INSTANCE_CHECKER(VirtIOMEMCcw, VIRTIO_MEM_CCW, TYPE_VIRTIO_MEM_CCW) + +struct VirtIOMEMCcw { + VirtIOMDCcw parent_obj; + VirtIOMEM vdev; + Notifier size_change_notifier; +}; + +#endif /* HW_S390X_VIRTIO_CCW_MEM_H */ diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index a0dceaddec..48e4a58239 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -61,6 +61,8 @@ static uint32_t virtio_mem_default_thp_size(void) } else if (qemu_real_host_page_size() == 64 * KiB) { default_thp_size = 512 * MiB; } +#elif defined(__s390x__) + default_thp_size = 1 * MiB; #endif return default_thp_size; @@ -168,7 +170,7 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) * necessary (as the section size can change). But it's more likely that the * section size will rather get smaller and not bigger over time. */ -#if defined(TARGET_X86_64) || defined(TARGET_I386) +#if defined(TARGET_X86_64) || defined(TARGET_I386) || defined(TARGET_S390X) #define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB)) #elif defined(TARGET_ARM) #define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB)) From 0ccbac336b74c3006d4234bb6b5c4b3c32c61171 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 10 Oct 2024 10:58:55 +0200 Subject: [PATCH 0617/2892] tests/tcg: Do not use inttypes.h in multiarch/system/memory.c make check-tcg fails on Fedora with the following error message: alpha-linux-gnu-gcc [...] qemu/tests/tcg/multiarch/system/memory.c -o memory [...] qemu/tests/tcg/multiarch/system/memory.c:17:10: fatal error: inttypes.h: No such file or directory 17 | #include | ^~~~~~~~~~~~ compilation terminated. The reason is that Fedora has cross-compilers, but no cross-glibc headers. Fix by hardcoding the format specifiers and dropping the include. An alternative fix would be to introduce a configure check for inttypes.h. But this would make it impossible to use Fedora cross-compilers for softmmu tests, which used to work so far. Fixes: ecbcc9ead2f8 ("tests/tcg: add a system test to check memory instrumentation") Signed-off-by: Ilya Leoshkevich Reviewed-by: Paolo Bonzini Message-ID: <20241010085906.226249-1-iii@linux.ibm.com> Signed-off-by: Richard Henderson --- tests/tcg/multiarch/system/memory.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/system/memory.c index 65a6038a24..7508f6b916 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -14,7 +14,6 @@ #include #include -#include #include #ifndef CHECK_UNALIGNED @@ -511,8 +510,8 @@ int main(void) int i; bool ok = true; - ml_printf("Test data start: 0x%"PRIxPTR"\n", &test_data[0]); - ml_printf("Test data end: 0x%"PRIxPTR"\n", &test_data[TEST_SIZE]); + ml_printf("Test data start: 0x%lx\n", (unsigned long)&test_data[0]); + ml_printf("Test data end: 0x%lx\n", (unsigned long)&test_data[TEST_SIZE]); /* Run through the unsigned tests first */ for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) { @@ -529,8 +528,8 @@ int main(void) ok = do_signed_reads(true); } - ml_printf("Test data read: %"PRId32"\n", test_read_count); - ml_printf("Test data write: %"PRId32"\n", test_write_count); + ml_printf("Test data read: %lu\n", (unsigned long)test_read_count); + ml_printf("Test data write: %lu\n", (unsigned long)test_write_count); ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED"); return ok ? 0 : -1; } From dbf408b6678a6076bd2412159d0ce665dce6acd0 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 28 Nov 2024 13:38:43 -0800 Subject: [PATCH 0618/2892] plugins: optimize cpu_index code generation When running with a single vcpu, we can return a constant instead of a load when accessing cpu_index. A side effect is that all tcg operations using it are optimized, most notably scoreboard access. When running a simple loop in user-mode, the speedup is around 20%. Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20241128213843.1023080-1-pierrick.bouvier@linaro.org> --- accel/tcg/plugin-gen.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 1ef075552c..7e5f040bf7 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -102,6 +102,15 @@ static void gen_disable_mem_helper(void) static TCGv_i32 gen_cpu_index(void) { + /* + * Optimize when we run with a single vcpu. All values using cpu_index, + * including scoreboard index, will be optimized out. + * User-mode calls tb_flush when setting this flag. In system-mode, all + * vcpus are created before generating code. + */ + if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) { + return tcg_constant_i32(current_cpu->cpu_index); + } TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); From 1526855c012a7f1314278e4f8fbb0741ec74a372 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 07:45:11 -0600 Subject: [PATCH 0619/2892] tcg/optimize: Split out finish_bb, finish_ebb Call them directly from the opcode switch statement in tcg_optimize, rather than in finish_folding based on opcode flags. Adjust folding of conditional branches to match. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e9ef16b3c6..453e8c43bd 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -964,24 +964,25 @@ static void copy_propagate(OptContext *ctx, TCGOp *op, } } +static void finish_bb(OptContext *ctx) +{ + /* We only optimize memory barriers across basic blocks. */ + ctx->prev_mb = NULL; +} + +static void finish_ebb(OptContext *ctx) +{ + finish_bb(ctx); + /* We only optimize across extended basic blocks. */ + memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); + remove_mem_copy_all(ctx); +} + static void finish_folding(OptContext *ctx, TCGOp *op) { const TCGOpDef *def = &tcg_op_defs[op->opc]; int i, nb_oargs; - /* - * We only optimize extended basic blocks. If the opcode ends a BB - * and is not a conditional branch, reset all temp data. - */ - if (def->flags & TCG_OPF_BB_END) { - ctx->prev_mb = NULL; - if (!(def->flags & TCG_OPF_COND_BRANCH)) { - memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); - remove_mem_copy_all(ctx); - } - return; - } - nb_oargs = def->nb_oargs; for (i = 0; i < nb_oargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); @@ -1351,8 +1352,11 @@ static bool fold_brcond(OptContext *ctx, TCGOp *op) if (i > 0) { op->opc = INDEX_op_br; op->args[0] = op->args[3]; + finish_ebb(ctx); + } else { + finish_bb(ctx); } - return false; + return true; } static bool fold_brcond2(OptContext *ctx, TCGOp *op) @@ -1443,9 +1447,12 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) } op->opc = INDEX_op_br; op->args[0] = label; - break; + finish_ebb(ctx); + return true; } - return false; + + finish_bb(ctx); + return true; } static bool fold_bswap(OptContext *ctx, TCGOp *op) @@ -3037,6 +3044,14 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(xor): done = fold_xor(&ctx, op); break; + case INDEX_op_set_label: + case INDEX_op_br: + case INDEX_op_exit_tb: + case INDEX_op_goto_tb: + case INDEX_op_goto_ptr: + finish_ebb(&ctx); + done = true; + break; default: break; } From 045ace35a8587f9552b528595f2d67a76b77f1b5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 19 Dec 2024 10:33:51 -0800 Subject: [PATCH 0620/2892] tcg/optimize: Split out fold_affected_mask There are only a few logical operations which can compute an "affected" mask. Split out handling of this optimization to a separate function, only to be called when applicable. Remove the a_mask field from OptContext, as the mask is no longer stored anywhere. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 453e8c43bd..6757fe0036 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -64,7 +64,6 @@ typedef struct OptContext { QSIMPLEQ_HEAD(, MemCopyInfo) mem_free; /* In flight values from optimization. */ - uint64_t a_mask; /* mask bit is 0 iff value identical to first input */ uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ uint64_t s_mask; /* mask of clrsb(value) bits */ TCGType type; @@ -1047,7 +1046,6 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) static bool fold_masks(OptContext *ctx, TCGOp *op) { - uint64_t a_mask = ctx->a_mask; uint64_t z_mask = ctx->z_mask; uint64_t s_mask = ctx->s_mask; @@ -1059,7 +1057,6 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) * type changing opcodes. */ if (ctx->type == TCG_TYPE_I32) { - a_mask = (int32_t)a_mask; z_mask = (int32_t)z_mask; s_mask |= MAKE_64BIT_MASK(32, 32); ctx->z_mask = z_mask; @@ -1069,6 +1066,19 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) if (z_mask == 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], 0); } + return false; +} + +/* + * An "affected" mask bit is 0 if and only if the result is identical + * to the first input. Thus if the entire mask is 0, the operation + * is equivalent to a copy. + */ +static bool fold_affected_mask(OptContext *ctx, TCGOp *op, uint64_t a_mask) +{ + if (ctx->type == TCG_TYPE_I32) { + a_mask = (uint32_t)a_mask; + } if (a_mask == 0) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); } @@ -1305,8 +1315,9 @@ static bool fold_and(OptContext *ctx, TCGOp *op) * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer affected bits from it. */ - if (arg_is_const(op->args[2])) { - ctx->a_mask = z1 & ~z2; + if (arg_is_const(op->args[2]) && + fold_affected_mask(ctx, op, z1 & ~z2)) { + return true; } return fold_masks(ctx, op); @@ -1331,7 +1342,9 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) */ if (arg_is_const(op->args[2])) { uint64_t z2 = ~arg_info(op->args[2])->z_mask; - ctx->a_mask = z1 & ~z2; + if (fold_affected_mask(ctx, op, z1 & ~z2)) { + return true; + } z1 &= z2; } ctx->z_mask = z1; @@ -1709,8 +1722,8 @@ static bool fold_extract(OptContext *ctx, TCGOp *op) z_mask_old = arg_info(op->args[1])->z_mask; z_mask = extract64(z_mask_old, pos, len); - if (pos == 0) { - ctx->a_mask = z_mask_old ^ z_mask; + if (pos == 0 && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { + return true; } ctx->z_mask = z_mask; ctx->s_mask = smask_from_zmask(z_mask); @@ -1777,8 +1790,8 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) ctx->z_mask = z_mask; ctx->s_mask = s_mask; - if (!type_change) { - ctx->a_mask = s_mask & ~s_mask_old; + if (!type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + return true; } return fold_masks(ctx, op); @@ -1819,8 +1832,8 @@ static bool fold_extu(OptContext *ctx, TCGOp *op) ctx->z_mask = z_mask; ctx->s_mask = smask_from_zmask(z_mask); - if (!type_change) { - ctx->a_mask = z_mask_old ^ z_mask; + if (!type_change && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { + return true; } return fold_masks(ctx, op); } @@ -2482,8 +2495,8 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) s_mask |= MAKE_64BIT_MASK(len, 64 - len); ctx->s_mask = s_mask; - if (pos == 0) { - ctx->a_mask = s_mask & ~s_mask_old; + if (pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + return true; } return fold_masks(ctx, op); @@ -2843,7 +2856,6 @@ void tcg_optimize(TCGContext *s) } /* Assume all bits affected, no bits known zero, no sign reps. */ - ctx.a_mask = -1; ctx.z_mask = -1; ctx.s_mask = 0; From 56e06ecfa5f13816b68b850f4ce46d8756e2c32b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 18:26:48 -0600 Subject: [PATCH 0621/2892] tcg/optimize: Copy mask writeback to fold_masks Use of fold_masks should be restricted to those opcodes that can reliably make use of it -- those with a single output, and from higher-level folders that set up the masks. Prepare for conversion of each folder in turn. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 6757fe0036..2aa57afd64 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1048,6 +1048,12 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) { uint64_t z_mask = ctx->z_mask; uint64_t s_mask = ctx->s_mask; + const TCGOpDef *def = &tcg_op_defs[op->opc]; + TCGTemp *ts; + TempOptInfo *ti; + + /* Only single-output opcodes are supported here. */ + tcg_debug_assert(def->nb_oargs == 1); /* * 32-bit ops generate 32-bit results, which for the purpose of @@ -1059,14 +1065,19 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) if (ctx->type == TCG_TYPE_I32) { z_mask = (int32_t)z_mask; s_mask |= MAKE_64BIT_MASK(32, 32); - ctx->z_mask = z_mask; - ctx->s_mask = s_mask; } if (z_mask == 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], 0); } - return false; + + ts = arg_temp(op->args[0]); + reset_ts(ctx, ts); + + ti = ts_info(ts); + ti->z_mask = z_mask; + ti->s_mask = s_mask; + return true; } /* From d582b14d808ee9b9c624140a1d253b6381406a9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 19 Dec 2024 10:43:26 -0800 Subject: [PATCH 0622/2892] tcg/optimize: Split out fold_masks_zs Add a routine to which masks can be passed directly, rather than storing them into OptContext. To be used in upcoming patches. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2aa57afd64..d70127b88d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1044,10 +1044,14 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } -static bool fold_masks(OptContext *ctx, TCGOp *op) +/* + * Record "zero" and "sign" masks for the single output of @op. + * See TempOptInfo definition of z_mask and s_mask. + * If z_mask allows, fold the output to constant zero. + */ +static bool fold_masks_zs(OptContext *ctx, TCGOp *op, + uint64_t z_mask, uint64_t s_mask) { - uint64_t z_mask = ctx->z_mask; - uint64_t s_mask = ctx->s_mask; const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGTemp *ts; TempOptInfo *ti; @@ -1080,6 +1084,11 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) return true; } +static bool fold_masks(OptContext *ctx, TCGOp *op) +{ + return fold_masks_zs(ctx, op, ctx->z_mask, ctx->s_mask); +} + /* * An "affected" mask bit is 0 if and only if the result is identical * to the first input. Thus if the entire mask is 0, the operation From 75c3bf324d21f8f57be1349007b0252ae64b4c51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 19 Dec 2024 10:50:40 -0800 Subject: [PATCH 0623/2892] tcg/optimize: Augment s_mask from z_mask in fold_masks_zs Consider the passed s_mask to be a minimum deduced from either existing s_mask or from a sign-extension operation. We may be able to deduce more from the set of known zeros. Remove identical logic from several opcode folders. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d70127b88d..d8f6542c4f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1048,6 +1048,7 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) * Record "zero" and "sign" masks for the single output of @op. * See TempOptInfo definition of z_mask and s_mask. * If z_mask allows, fold the output to constant zero. + * The passed s_mask may be augmented by z_mask. */ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, uint64_t z_mask, uint64_t s_mask) @@ -1080,7 +1081,7 @@ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, ti = ts_info(ts); ti->z_mask = z_mask; - ti->s_mask = s_mask; + ti->s_mask = s_mask | smask_from_zmask(z_mask); return true; } @@ -1519,8 +1520,8 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) default: g_assert_not_reached(); } - s_mask = smask_from_zmask(z_mask); + s_mask = 0; switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { case TCG_BSWAP_OZ: break; @@ -1534,7 +1535,6 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) default: /* The high bits are undefined: force all bits above the sign to 1. */ z_mask |= sign << 1; - s_mask = 0; break; } ctx->z_mask = z_mask; @@ -1605,7 +1605,6 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) g_assert_not_reached(); } ctx->z_mask = arg_info(op->args[2])->z_mask | z_mask; - ctx->s_mask = smask_from_zmask(ctx->z_mask); return false; } @@ -1625,7 +1624,6 @@ static bool fold_ctpop(OptContext *ctx, TCGOp *op) default: g_assert_not_reached(); } - ctx->s_mask = smask_from_zmask(ctx->z_mask); return false; } @@ -1746,7 +1744,6 @@ static bool fold_extract(OptContext *ctx, TCGOp *op) return true; } ctx->z_mask = z_mask; - ctx->s_mask = smask_from_zmask(z_mask); return fold_masks(ctx, op); } @@ -1851,7 +1848,6 @@ static bool fold_extu(OptContext *ctx, TCGOp *op) } ctx->z_mask = z_mask; - ctx->s_mask = smask_from_zmask(z_mask); if (!type_change && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { return true; } @@ -2116,10 +2112,10 @@ static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) int width = 8 * memop_size(mop); if (width < 64) { - ctx->s_mask = MAKE_64BIT_MASK(width, 64 - width); - if (!(mop & MO_SIGN)) { + if (mop & MO_SIGN) { + ctx->s_mask = MAKE_64BIT_MASK(width, 64 - width); + } else { ctx->z_mask = MAKE_64BIT_MASK(0, width); - ctx->s_mask <<= 1; } } @@ -2354,7 +2350,6 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) fold_setcond_tst_pow2(ctx, op, false); ctx->z_mask = 1; - ctx->s_mask = smask_from_zmask(1); return false; } @@ -2455,7 +2450,6 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) } ctx->z_mask = 1; - ctx->s_mask = smask_from_zmask(1); return false; do_setcond_const: @@ -2649,21 +2643,18 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) break; CASE_OP_32_64(ld8u): ctx->z_mask = MAKE_64BIT_MASK(0, 8); - ctx->s_mask = MAKE_64BIT_MASK(9, 55); break; CASE_OP_32_64(ld16s): ctx->s_mask = MAKE_64BIT_MASK(16, 48); break; CASE_OP_32_64(ld16u): ctx->z_mask = MAKE_64BIT_MASK(0, 16); - ctx->s_mask = MAKE_64BIT_MASK(17, 47); break; case INDEX_op_ld32s_i64: ctx->s_mask = MAKE_64BIT_MASK(32, 32); break; case INDEX_op_ld32u_i64: ctx->z_mask = MAKE_64BIT_MASK(0, 32); - ctx->s_mask = MAKE_64BIT_MASK(33, 31); break; default: g_assert_not_reached(); From 6d70ddc6350361c38e7720d1ffc594e5cc648900 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 21 Dec 2024 21:08:10 -0800 Subject: [PATCH 0624/2892] tcg/optimize: Change representation of s_mask Change the representation from sign bit repetitions to all bits equal to the sign bit, including the sign bit itself. The previous format has a problem in that it is difficult to recreate a valid sign mask after a shift operation: the "repetitions" part of the previous format meant that applying the same shift as for the value lead to an off-by-one value. The new format, including the sign bit itself, means that the sign mask can be manipulated in exactly the same way as the value, canonicalization is easier. Canonicalize the s_mask in fold_masks_zs, rather than requiring callers to do so. Treat 0 as a non-canonical but typeless input for no sign information, which will be reset as appropriate for the data type. We can easily fold in the data from z_mask while canonicalizing. Temporarily disable optimizations using s_mask while each operation is converted to use fold_masks_zs and to the new form. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 64 ++++++++++++-------------------------------------- 1 file changed, 15 insertions(+), 49 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d8f6542c4f..fbc0dc5588 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -52,7 +52,7 @@ typedef struct TempOptInfo { QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy; uint64_t val; uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ - uint64_t s_mask; /* a left-aligned mask of clrsb(value) bits. */ + uint64_t s_mask; /* mask bit is 1 if value bit matches msb */ } TempOptInfo; typedef struct OptContext { @@ -65,49 +65,10 @@ typedef struct OptContext { /* In flight values from optimization. */ uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ - uint64_t s_mask; /* mask of clrsb(value) bits */ + uint64_t s_mask; /* mask bit is 1 if value bit matches msb */ TCGType type; } OptContext; -/* Calculate the smask for a specific value. */ -static uint64_t smask_from_value(uint64_t value) -{ - int rep = clrsb64(value); - return ~(~0ull >> rep); -} - -/* - * Calculate the smask for a given set of known-zeros. - * If there are lots of zeros on the left, we can consider the remainder - * an unsigned field, and thus the corresponding signed field is one bit - * larger. - */ -static uint64_t smask_from_zmask(uint64_t zmask) -{ - /* - * Only the 0 bits are significant for zmask, thus the msb itself - * must be zero, else we have no sign information. - */ - int rep = clz64(zmask); - if (rep == 0) { - return 0; - } - rep -= 1; - return ~(~0ull >> rep); -} - -/* - * Recreate a properly left-aligned smask after manipulation. - * Some bit-shuffling, particularly shifts and rotates, may - * retain sign bits on the left, but may scatter disconnected - * sign bits on the right. Retain only what remains to the left. - */ -static uint64_t smask_from_smask(int64_t smask) -{ - /* Only the 1 bits are significant for smask */ - return smask_from_zmask(~smask); -} - static inline TempOptInfo *ts_info(TCGTemp *ts) { return ts->state_ptr; @@ -173,7 +134,7 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->is_const = true; ti->val = ts->val; ti->z_mask = ts->val; - ti->s_mask = smask_from_value(ts->val); + ti->s_mask = INT64_MIN >> clrsb64(ts->val); } else { ti->is_const = false; ti->z_mask = -1; @@ -992,7 +953,6 @@ static void finish_folding(OptContext *ctx, TCGOp *op) */ if (i == 0) { ts_info(ts)->z_mask = ctx->z_mask; - ts_info(ts)->s_mask = ctx->s_mask; } } } @@ -1051,11 +1011,12 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) * The passed s_mask may be augmented by z_mask. */ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, - uint64_t z_mask, uint64_t s_mask) + uint64_t z_mask, int64_t s_mask) { const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGTemp *ts; TempOptInfo *ti; + int rep; /* Only single-output opcodes are supported here. */ tcg_debug_assert(def->nb_oargs == 1); @@ -1069,7 +1030,7 @@ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, */ if (ctx->type == TCG_TYPE_I32) { z_mask = (int32_t)z_mask; - s_mask |= MAKE_64BIT_MASK(32, 32); + s_mask |= INT32_MIN; } if (z_mask == 0) { @@ -1081,7 +1042,13 @@ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, ti = ts_info(ts); ti->z_mask = z_mask; - ti->s_mask = s_mask | smask_from_zmask(z_mask); + + /* Canonicalize s_mask and incorporate data from z_mask. */ + rep = clz64(~s_mask); + rep = MAX(rep, clz64(z_mask)); + rep = MAX(rep - 1, 0); + ti->s_mask = INT64_MIN >> rep; + return true; } @@ -1807,7 +1774,7 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) ctx->z_mask = z_mask; ctx->s_mask = s_mask; - if (!type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + if (0 && !type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { return true; } @@ -2509,7 +2476,7 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) s_mask |= MAKE_64BIT_MASK(len, 64 - len); ctx->s_mask = s_mask; - if (pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + if (0 && pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { return true; } @@ -2535,7 +2502,6 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) ctx->z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); - ctx->s_mask = smask_from_smask(s_mask); return fold_masks(ctx, op); } From f3ed3cffb96f96875755ac4b057a15fd50ed0f32 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 18:39:47 -0600 Subject: [PATCH 0625/2892] tcg/optimize: Use finish_folding in fold_add, fold_add_vec, fold_addsub2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index fbc0dc5588..26d1c5d4a1 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -938,7 +938,7 @@ static void finish_ebb(OptContext *ctx) remove_mem_copy_all(ctx); } -static void finish_folding(OptContext *ctx, TCGOp *op) +static bool finish_folding(OptContext *ctx, TCGOp *op) { const TCGOpDef *def = &tcg_op_defs[op->opc]; int i, nb_oargs; @@ -955,6 +955,7 @@ static void finish_folding(OptContext *ctx, TCGOp *op) ts_info(ts)->z_mask = ctx->z_mask; } } + return true; } /* @@ -1188,7 +1189,7 @@ static bool fold_add(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } /* We cannot as yet do_constant_folding with vectors. */ @@ -1198,7 +1199,7 @@ static bool fold_add_vec(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) @@ -1265,7 +1266,7 @@ static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) op->args[4] = arg_new_constant(ctx, bl); op->args[5] = arg_new_constant(ctx, bh); } - return false; + return finish_folding(ctx, op); } static bool fold_add2(OptContext *ctx, TCGOp *op) From e1b6c141e98034d44d7e9004dc35545b87ebcade Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Dec 2024 10:26:14 -0800 Subject: [PATCH 0626/2892] tcg/optimize: Introduce const value accessors for TempOptInfo Introduce ti_is_const, ti_const_val, ti_is_const_val. Signed-off-by: Richard Henderson --- tcg/optimize.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 26d1c5d4a1..5090f6e759 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -79,15 +79,29 @@ static inline TempOptInfo *arg_info(TCGArg arg) return ts_info(arg_temp(arg)); } +static inline bool ti_is_const(TempOptInfo *ti) +{ + return ti->is_const; +} + +static inline uint64_t ti_const_val(TempOptInfo *ti) +{ + return ti->val; +} + +static inline bool ti_is_const_val(TempOptInfo *ti, uint64_t val) +{ + return ti_is_const(ti) && ti_const_val(ti) == val; +} + static inline bool ts_is_const(TCGTemp *ts) { - return ts_info(ts)->is_const; + return ti_is_const(ts_info(ts)); } static inline bool ts_is_const_val(TCGTemp *ts, uint64_t val) { - TempOptInfo *ti = ts_info(ts); - return ti->is_const && ti->val == val; + return ti_is_const_val(ts_info(ts), val); } static inline bool arg_is_const(TCGArg arg) From 1ca7372c033d8d958add8f4f4c7d8e37c06e6ef7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 18:47:15 -0600 Subject: [PATCH 0627/2892] tcg/optimize: Use fold_masks_zs in fold_and Avoid the use of the OptContext slots. Find TempOptInfo once. Sink mask computation below fold_affected_mask early exit. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 5090f6e759..4a5b52916a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1294,7 +1294,8 @@ static bool fold_add2(OptContext *ctx, TCGOp *op) static bool fold_and(OptContext *ctx, TCGOp *op) { - uint64_t z1, z2; + uint64_t z1, z2, z_mask, s_mask; + TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0) || @@ -1303,27 +1304,28 @@ static bool fold_and(OptContext *ctx, TCGOp *op) return true; } - z1 = arg_info(op->args[1])->z_mask; - z2 = arg_info(op->args[2])->z_mask; - ctx->z_mask = z1 & z2; - - /* - * Sign repetitions are perforce all identical, whether they are 1 or 0. - * Bitwise operations preserve the relative quantity of the repetitions. - */ - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z1 = t1->z_mask; + z2 = t2->z_mask; /* * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer affected bits from it. */ - if (arg_is_const(op->args[2]) && - fold_affected_mask(ctx, op, z1 & ~z2)) { + if (ti_is_const(t2) && fold_affected_mask(ctx, op, z1 & ~z2)) { return true; } - return fold_masks(ctx, op); + z_mask = z1 & z2; + + /* + * Sign repetitions are perforce all identical, whether they are 1 or 0. + * Bitwise operations preserve the relative quantity of the repetitions. + */ + s_mask = t1->s_mask & t2->s_mask; + + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_andc(OptContext *ctx, TCGOp *op) From 21e2b5f9fa79eb122cb7240436b84a56263547aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 18:56:55 -0600 Subject: [PATCH 0628/2892] tcg/optimize: Use fold_masks_zs in fold_andc Avoid the use of the OptContext slots. Find TempOptInfo once. Avoid double inversion of the value of second const operand. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 4a5b52916a..2096d705bd 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1330,7 +1330,8 @@ static bool fold_and(OptContext *ctx, TCGOp *op) static bool fold_andc(OptContext *ctx, TCGOp *op) { - uint64_t z1; + uint64_t z_mask, s_mask; + TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, 0) || @@ -1339,24 +1340,24 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) return true; } - z1 = arg_info(op->args[1])->z_mask; + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z_mask = t1->z_mask; /* * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer anything from it. */ - if (arg_is_const(op->args[2])) { - uint64_t z2 = ~arg_info(op->args[2])->z_mask; - if (fold_affected_mask(ctx, op, z1 & ~z2)) { + if (ti_is_const(t2)) { + uint64_t v2 = ti_const_val(t2); + if (fold_affected_mask(ctx, op, z_mask & v2)) { return true; } - z1 &= z2; + z_mask &= ~v2; } - ctx->z_mask = z1; - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks(ctx, op); + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_brcond(OptContext *ctx, TCGOp *op) From c1e7b989c8f05a4e78896a8856530492d87b51b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 19:42:20 -0600 Subject: [PATCH 0629/2892] tcg/optimize: Use fold_masks_zs in fold_bswap Avoid the use of the OptContext slots. Find TempOptInfo once. Always set s_mask along the BSWAP_OS path, since the result is being explicitly sign-extended. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2096d705bd..054109d347 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1477,16 +1477,16 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) static bool fold_bswap(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask, sign; + TempOptInfo *t1 = arg_info(op->args[1]); - if (arg_is_const(op->args[1])) { - uint64_t t = arg_info(op->args[1])->val; - - t = do_constant_folding(op->opc, ctx->type, t, op->args[2]); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + if (ti_is_const(t1)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + do_constant_folding(op->opc, ctx->type, + ti_const_val(t1), + op->args[2])); } - z_mask = arg_info(op->args[1])->z_mask; - + z_mask = t1->z_mask; switch (op->opc) { case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: @@ -1514,18 +1514,17 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) /* If the sign bit may be 1, force all the bits above to 1. */ if (z_mask & sign) { z_mask |= sign; - s_mask = sign << 1; } + /* The value and therefore s_mask is explicitly sign-extended. */ + s_mask = sign; break; default: /* The high bits are undefined: force all bits above the sign to 1. */ z_mask |= sign << 1; break; } - ctx->z_mask = z_mask; - ctx->s_mask = s_mask; - return fold_masks(ctx, op); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_call(OptContext *ctx, TCGOp *op) From ce1d663ff8a4535e4c72471cab3e52cb24fb7eb1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 19:47:51 -0600 Subject: [PATCH 0630/2892] tcg/optimize: Use fold_masks_zs in fold_count_zeros Avoid the use of the OptContext slots. Find TempOptInfo once. Compute s_mask from the union of the maximum count and the op2 fallback for op1 being zero. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 054109d347..0766a452b5 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1566,10 +1566,12 @@ static bool fold_call(OptContext *ctx, TCGOp *op) static bool fold_count_zeros(OptContext *ctx, TCGOp *op) { - uint64_t z_mask; + uint64_t z_mask, s_mask; + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); - if (arg_is_const(op->args[1])) { - uint64_t t = arg_info(op->args[1])->val; + if (ti_is_const(t1)) { + uint64_t t = ti_const_val(t1); if (t != 0) { t = do_constant_folding(op->opc, ctx->type, t, 0); @@ -1588,8 +1590,11 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) default: g_assert_not_reached(); } - ctx->z_mask = arg_info(op->args[2])->z_mask | z_mask; - return false; + s_mask = ~z_mask; + z_mask |= t2->z_mask; + s_mask &= t2->s_mask; + + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_ctpop(OptContext *ctx, TCGOp *op) From 81be07f905b187743b69adeb2877e5a9efc00d8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 19:49:17 -0600 Subject: [PATCH 0631/2892] tcg/optimize: Use fold_masks_z in fold_ctpop Add fold_masks_z as a trivial wrapper around fold_masks_zs. Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 0766a452b5..2f5030c899 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1067,6 +1067,11 @@ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, return true; } +static bool fold_masks_z(OptContext *ctx, TCGOp *op, uint64_t z_mask) +{ + return fold_masks_zs(ctx, op, z_mask, 0); +} + static bool fold_masks(OptContext *ctx, TCGOp *op) { return fold_masks_zs(ctx, op, ctx->z_mask, ctx->s_mask); @@ -1599,21 +1604,23 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) static bool fold_ctpop(OptContext *ctx, TCGOp *op) { + uint64_t z_mask; + if (fold_const1(ctx, op)) { return true; } switch (ctx->type) { case TCG_TYPE_I32: - ctx->z_mask = 32 | 31; + z_mask = 32 | 31; break; case TCG_TYPE_I64: - ctx->z_mask = 64 | 63; + z_mask = 64 | 63; break; default: g_assert_not_reached(); } - return false; + return fold_masks_z(ctx, op, z_mask); } static bool fold_deposit(OptContext *ctx, TCGOp *op) From c7739ab83e02b93cb15f54984c3f66ba3c5bd8d2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 19:57:28 -0600 Subject: [PATCH 0632/2892] tcg/optimize: Use fold_and and fold_masks_z in fold_deposit Avoid the use of the OptContext slots. Find TempOptInfo once. When we fold to and, use fold_and. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2f5030c899..c0f0390431 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1625,14 +1625,17 @@ static bool fold_ctpop(OptContext *ctx, TCGOp *op) static bool fold_deposit(OptContext *ctx, TCGOp *op) { + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); + int ofs = op->args[3]; + int len = op->args[4]; TCGOpcode and_opc; + uint64_t z_mask; - if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - uint64_t t1 = arg_info(op->args[1])->val; - uint64_t t2 = arg_info(op->args[2])->val; - - t1 = deposit64(t1, op->args[3], op->args[4], t2); - return tcg_opt_gen_movi(ctx, op, op->args[0], t1); + if (ti_is_const(t1) && ti_is_const(t2)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + deposit64(ti_const_val(t1), ofs, len, + ti_const_val(t2))); } switch (ctx->type) { @@ -1647,30 +1650,26 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) } /* Inserting a value into zero at offset 0. */ - if (arg_is_const_val(op->args[1], 0) && op->args[3] == 0) { - uint64_t mask = MAKE_64BIT_MASK(0, op->args[4]); + if (ti_is_const_val(t1, 0) && ofs == 0) { + uint64_t mask = MAKE_64BIT_MASK(0, len); op->opc = and_opc; op->args[1] = op->args[2]; op->args[2] = arg_new_constant(ctx, mask); - ctx->z_mask = mask & arg_info(op->args[1])->z_mask; - return false; + return fold_and(ctx, op); } /* Inserting zero into a value. */ - if (arg_is_const_val(op->args[2], 0)) { - uint64_t mask = deposit64(-1, op->args[3], op->args[4], 0); + if (ti_is_const_val(t2, 0)) { + uint64_t mask = deposit64(-1, ofs, len, 0); op->opc = and_opc; op->args[2] = arg_new_constant(ctx, mask); - ctx->z_mask = mask & arg_info(op->args[1])->z_mask; - return false; + return fold_and(ctx, op); } - ctx->z_mask = deposit64(arg_info(op->args[1])->z_mask, - op->args[3], op->args[4], - arg_info(op->args[2])->z_mask); - return false; + z_mask = deposit64(t1->z_mask, ofs, len, t2->z_mask); + return fold_masks_z(ctx, op, z_mask); } static bool fold_divide(OptContext *ctx, TCGOp *op) From edb832cb51dd98d955dc33973063853bd333752e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 19 Dec 2024 17:56:05 -0800 Subject: [PATCH 0633/2892] tcg/optimize: Compute sign mask in fold_deposit The input which overlaps the sign bit of the output can have its input s_mask propagated to the output s_mask. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c0f0390431..b774c96f49 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1629,8 +1629,9 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) TempOptInfo *t2 = arg_info(op->args[2]); int ofs = op->args[3]; int len = op->args[4]; + int width; TCGOpcode and_opc; - uint64_t z_mask; + uint64_t z_mask, s_mask; if (ti_is_const(t1) && ti_is_const(t2)) { return tcg_opt_gen_movi(ctx, op, op->args[0], @@ -1641,9 +1642,11 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) switch (ctx->type) { case TCG_TYPE_I32: and_opc = INDEX_op_and_i32; + width = 32; break; case TCG_TYPE_I64: and_opc = INDEX_op_and_i64; + width = 64; break; default: g_assert_not_reached(); @@ -1668,8 +1671,15 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) return fold_and(ctx, op); } + /* The s_mask from the top portion of the deposit is still valid. */ + if (ofs + len == width) { + s_mask = t2->s_mask << ofs; + } else { + s_mask = t1->s_mask & ~MAKE_64BIT_MASK(0, ofs + len); + } + z_mask = deposit64(t1->z_mask, ofs, len, t2->z_mask); - return fold_masks_z(ctx, op, z_mask); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_divide(OptContext *ctx, TCGOp *op) From 3d5ec804da6e119ad16632675f4ffbbc880ea291 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 19:59:15 -0600 Subject: [PATCH 0634/2892] tcg/optimize: Use finish_folding in fold_divide Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index b774c96f49..a68221a027 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1688,7 +1688,7 @@ static bool fold_divide(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 1)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_dup(OptContext *ctx, TCGOp *op) From e089d694e1d3a7b4406535b748a324fa28bfaf0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:00:51 -0600 Subject: [PATCH 0635/2892] tcg/optimize: Use finish_folding in fold_dup, fold_dup2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index a68221a027..803bceb4bd 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1698,7 +1698,7 @@ static bool fold_dup(OptContext *ctx, TCGOp *op) t = dup_const(TCGOP_VECE(op), t); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } - return false; + return finish_folding(ctx, op); } static bool fold_dup2(OptContext *ctx, TCGOp *op) @@ -1713,7 +1713,7 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) op->opc = INDEX_op_dup_vec; TCGOP_VECE(op) = MO_32; } - return false; + return finish_folding(ctx, op); } static bool fold_eqv(OptContext *ctx, TCGOp *op) From ef6be624f6bfc655bbc2dd7c86f50a46ab90a414 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:03:15 -0600 Subject: [PATCH 0636/2892] tcg/optimize: Use fold_masks_s in fold_eqv Add fold_masks_s as a trivial wrapper around fold_masks_zs. Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 803bceb4bd..f948cc48c9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1072,6 +1072,11 @@ static bool fold_masks_z(OptContext *ctx, TCGOp *op, uint64_t z_mask) return fold_masks_zs(ctx, op, z_mask, 0); } +static bool fold_masks_s(OptContext *ctx, TCGOp *op, uint64_t s_mask) +{ + return fold_masks_zs(ctx, op, -1, s_mask); +} + static bool fold_masks(OptContext *ctx, TCGOp *op) { return fold_masks_zs(ctx, op, ctx->z_mask, ctx->s_mask); @@ -1718,15 +1723,17 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) static bool fold_eqv(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, -1) || fold_xi_to_not(ctx, op, 0)) { return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + return fold_masks_s(ctx, op, s_mask); } static bool fold_extract(OptContext *ctx, TCGOp *op) From b6cd00f1ef6d7d0b789094559f8d8a4537356515 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:05:11 -0600 Subject: [PATCH 0637/2892] tcg/optimize: Use fold_masks_z in fold_extract Avoid the use of the OptContext slots. Find TempOptInfo once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f948cc48c9..8111c120af 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1739,25 +1739,22 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) static bool fold_extract(OptContext *ctx, TCGOp *op) { uint64_t z_mask_old, z_mask; + TempOptInfo *t1 = arg_info(op->args[1]); int pos = op->args[2]; int len = op->args[3]; - if (arg_is_const(op->args[1])) { - uint64_t t; - - t = arg_info(op->args[1])->val; - t = extract64(t, pos, len); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + if (ti_is_const(t1)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + extract64(ti_const_val(t1), pos, len)); } - z_mask_old = arg_info(op->args[1])->z_mask; + z_mask_old = t1->z_mask; z_mask = extract64(z_mask_old, pos, len); if (pos == 0 && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { return true; } - ctx->z_mask = z_mask; - return fold_masks(ctx, op); + return fold_masks_z(ctx, op, z_mask); } static bool fold_extract2(OptContext *ctx, TCGOp *op) From c9df99ee8d549875cd274db10c072dc0b373a168 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:06:42 -0600 Subject: [PATCH 0638/2892] tcg/optimize: Use finish_folding in fold_extract2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 8111c120af..04ec6fdcef 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1773,7 +1773,7 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) } return tcg_opt_gen_movi(ctx, op, op->args[0], v1 | v2); } - return false; + return finish_folding(ctx, op); } static bool fold_exts(OptContext *ctx, TCGOp *op) From a96219204f6944def14bdb3a6b2a0bbba172f88a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:08:46 -0600 Subject: [PATCH 0639/2892] tcg/optimize: Use fold_masks_zs in fold_exts Avoid the use of the OptContext slots. Find TempOptInfo once. Explicitly sign-extend z_mask instead of doing that manually. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 04ec6fdcef..3aafe039ed 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1778,49 +1778,44 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) static bool fold_exts(OptContext *ctx, TCGOp *op) { - uint64_t s_mask_old, s_mask, z_mask, sign; + uint64_t s_mask_old, s_mask, z_mask; bool type_change = false; + TempOptInfo *t1; if (fold_const1(ctx, op)) { return true; } - z_mask = arg_info(op->args[1])->z_mask; - s_mask = arg_info(op->args[1])->s_mask; + t1 = arg_info(op->args[1]); + z_mask = t1->z_mask; + s_mask = t1->s_mask; s_mask_old = s_mask; switch (op->opc) { CASE_OP_32_64(ext8s): - sign = INT8_MIN; - z_mask = (uint8_t)z_mask; + s_mask |= INT8_MIN; + z_mask = (int8_t)z_mask; break; CASE_OP_32_64(ext16s): - sign = INT16_MIN; - z_mask = (uint16_t)z_mask; + s_mask |= INT16_MIN; + z_mask = (int16_t)z_mask; break; case INDEX_op_ext_i32_i64: type_change = true; QEMU_FALLTHROUGH; case INDEX_op_ext32s_i64: - sign = INT32_MIN; - z_mask = (uint32_t)z_mask; + s_mask |= INT32_MIN; + z_mask = (int32_t)z_mask; break; default: g_assert_not_reached(); } - if (z_mask & sign) { - z_mask |= sign; - } - s_mask |= sign << 1; - - ctx->z_mask = z_mask; - ctx->s_mask = s_mask; if (0 && !type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { return true; } - return fold_masks(ctx, op); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_extu(OptContext *ctx, TCGOp *op) From 08abe2908fa597fb3af298408c261e17378c54d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:11:44 -0600 Subject: [PATCH 0640/2892] tcg/optimize: Use fold_masks_z in fold_extu Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 3aafe039ed..f62e7adfe1 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1851,11 +1851,11 @@ static bool fold_extu(OptContext *ctx, TCGOp *op) g_assert_not_reached(); } - ctx->z_mask = z_mask; if (!type_change && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { return true; } - return fold_masks(ctx, op); + + return fold_masks_z(ctx, op, z_mask); } static bool fold_mb(OptContext *ctx, TCGOp *op) From 322027841f2e35adef592e28ee1288d90232185f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:16:38 -0600 Subject: [PATCH 0641/2892] tcg/optimize: Use fold_masks_zs in fold_movcond Avoid the use of the OptContext slots. Find TempOptInfo once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f62e7adfe1..0104582b3a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1889,6 +1889,8 @@ static bool fold_mov(OptContext *ctx, TCGOp *op) static bool fold_movcond(OptContext *ctx, TCGOp *op) { + uint64_t z_mask, s_mask; + TempOptInfo *tt, *ft; int i; /* If true and false values are the same, eliminate the cmp. */ @@ -1910,14 +1912,14 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[4 - i]); } - ctx->z_mask = arg_info(op->args[3])->z_mask - | arg_info(op->args[4])->z_mask; - ctx->s_mask = arg_info(op->args[3])->s_mask - & arg_info(op->args[4])->s_mask; + tt = arg_info(op->args[3]); + ft = arg_info(op->args[4]); + z_mask = tt->z_mask | ft->z_mask; + s_mask = tt->s_mask & ft->s_mask; - if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { - uint64_t tv = arg_info(op->args[3])->val; - uint64_t fv = arg_info(op->args[4])->val; + if (ti_is_const(tt) && ti_is_const(ft)) { + uint64_t tv = ti_const_val(tt); + uint64_t fv = ti_const_val(ft); TCGOpcode opc, negopc = 0; TCGCond cond = op->args[5]; @@ -1956,7 +1958,8 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) } } } - return false; + + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_mul(OptContext *ctx, TCGOp *op) From cd9c5834d83ccde38268a52d3201659a6286428b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:18:02 -0600 Subject: [PATCH 0642/2892] tcg/optimize: Use finish_folding in fold_mul* Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 0104582b3a..10d1376f62 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1969,7 +1969,7 @@ static bool fold_mul(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 1)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) @@ -1978,7 +1978,7 @@ static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) fold_xi_to_i(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_multiply2(OptContext *ctx, TCGOp *op) @@ -2023,7 +2023,7 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) tcg_opt_gen_movi(ctx, op2, rh, h); return true; } - return false; + return finish_folding(ctx, op); } static bool fold_nand(OptContext *ctx, TCGOp *op) From fa3168ee93e4a9cabe31824f7918bfe4b7a56369 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:20:40 -0600 Subject: [PATCH 0643/2892] tcg/optimize: Use fold_masks_s in fold_nand Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 10d1376f62..7fe5bd6012 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2028,14 +2028,16 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) static bool fold_nand(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, -1)) { return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + return fold_masks_s(ctx, op, s_mask); } static bool fold_neg_no_const(OptContext *ctx, TCGOp *op) From d151fd34b090ddb40b073f1bd2ac4c893a67d1eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:23:11 -0600 Subject: [PATCH 0644/2892] tcg/optimize: Use fold_masks_z in fold_neg_no_const Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 7fe5bd6012..fbaaece152 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2044,14 +2044,9 @@ static bool fold_neg_no_const(OptContext *ctx, TCGOp *op) { /* Set to 1 all bits to the left of the rightmost. */ uint64_t z_mask = arg_info(op->args[1])->z_mask; - ctx->z_mask = -(z_mask & -z_mask); + z_mask = -(z_mask & -z_mask); - /* - * Because of fold_sub_to_neg, we want to always return true, - * via finish_folding. - */ - finish_folding(ctx, op); - return true; + return fold_masks_z(ctx, op, z_mask); } static bool fold_neg(OptContext *ctx, TCGOp *op) From 2b7b69575733d5568e86d850a1c17e3414be974b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:25:21 -0600 Subject: [PATCH 0645/2892] tcg/optimize: Use fold_masks_s in fold_nor Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index fbaaece152..acff3985f3 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2056,14 +2056,16 @@ static bool fold_neg(OptContext *ctx, TCGOp *op) static bool fold_nor(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, 0)) { return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + return fold_masks_s(ctx, op, s_mask); } static bool fold_not(OptContext *ctx, TCGOp *op) From 608e75fc0c957e6d07cb65d8203c2646a6723dc9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:27:02 -0600 Subject: [PATCH 0646/2892] tcg/optimize: Use fold_masks_s in fold_not Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index acff3985f3..4ede218bfc 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2073,12 +2073,7 @@ static bool fold_not(OptContext *ctx, TCGOp *op) if (fold_const1(ctx, op)) { return true; } - - ctx->s_mask = arg_info(op->args[1])->s_mask; - - /* Because of fold_to_not, we want to always return true, via finish. */ - finish_folding(ctx, op); - return true; + return fold_masks_s(ctx, op, arg_info(op->args[1])->s_mask); } static bool fold_or(OptContext *ctx, TCGOp *op) From 83b1ba3696a4c647d500705cdd11e79b69462cd9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:28:59 -0600 Subject: [PATCH 0647/2892] tcg/optimize: Use fold_masks_zs in fold_or Avoid the use of the OptContext slots. Find TempOptInfo once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 4ede218bfc..e284d79fb1 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2078,17 +2078,20 @@ static bool fold_not(OptContext *ctx, TCGOp *op) static bool fold_or(OptContext *ctx, TCGOp *op) { + uint64_t z_mask, s_mask; + TempOptInfo *t1, *t2; + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, 0) || fold_xx_to_x(ctx, op)) { return true; } - ctx->z_mask = arg_info(op->args[1])->z_mask - | arg_info(op->args[2])->z_mask; - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z_mask = t1->z_mask | t2->z_mask; + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_orc(OptContext *ctx, TCGOp *op) From 54e26b292bbf9602f49a66c0c022a623d0beec4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:30:20 -0600 Subject: [PATCH 0648/2892] tcg/optimize: Use fold_masks_zs in fold_orc Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e284d79fb1..81ed26a376 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2096,6 +2096,8 @@ static bool fold_or(OptContext *ctx, TCGOp *op) static bool fold_orc(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, -1) || fold_xi_to_x(ctx, op, -1) || @@ -2103,9 +2105,9 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + return fold_masks_s(ctx, op, s_mask); } static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) From 6813be9b9bb962865eb6770555f34d4b0d6066f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:33:30 -0600 Subject: [PATCH 0649/2892] tcg/optimize: Use fold_masks_zs in fold_qemu_ld Avoid the use of the OptContext slots. Be careful not to call fold_masks_zs when the memory operation is wide enough to require multiple outputs, so split into two functions: fold_qemu_ld_1reg and fold_qemu_ld_2reg. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 81ed26a376..7bd17a36c7 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2110,24 +2110,33 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) return fold_masks_s(ctx, op, s_mask); } -static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) +static bool fold_qemu_ld_1reg(OptContext *ctx, TCGOp *op) { const TCGOpDef *def = &tcg_op_defs[op->opc]; MemOpIdx oi = op->args[def->nb_oargs + def->nb_iargs]; MemOp mop = get_memop(oi); int width = 8 * memop_size(mop); + uint64_t z_mask = -1, s_mask = 0; if (width < 64) { if (mop & MO_SIGN) { - ctx->s_mask = MAKE_64BIT_MASK(width, 64 - width); + s_mask = MAKE_64BIT_MASK(width - 1, 64 - (width - 1)); } else { - ctx->z_mask = MAKE_64BIT_MASK(0, width); + z_mask = MAKE_64BIT_MASK(0, width); } } /* Opcodes that touch guest memory stop the mb optimization. */ ctx->prev_mb = NULL; - return false; + + return fold_masks_zs(ctx, op, z_mask, s_mask); +} + +static bool fold_qemu_ld_2reg(OptContext *ctx, TCGOp *op) +{ + /* Opcodes that touch guest memory stop the mb optimization. */ + ctx->prev_mb = NULL; + return finish_folding(ctx, op); } static bool fold_qemu_st(OptContext *ctx, TCGOp *op) @@ -3012,11 +3021,18 @@ void tcg_optimize(TCGContext *s) break; case INDEX_op_qemu_ld_a32_i32: case INDEX_op_qemu_ld_a64_i32: + done = fold_qemu_ld_1reg(&ctx, op); + break; case INDEX_op_qemu_ld_a32_i64: case INDEX_op_qemu_ld_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + done = fold_qemu_ld_1reg(&ctx, op); + break; + } + QEMU_FALLTHROUGH; case INDEX_op_qemu_ld_a32_i128: case INDEX_op_qemu_ld_a64_i128: - done = fold_qemu_ld(&ctx, op); + done = fold_qemu_ld_2reg(&ctx, op); break; case INDEX_op_qemu_st8_a32_i32: case INDEX_op_qemu_st8_a64_i32: From 082b3ef9195571d543c32ab4afe5fc516153a9e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:34:57 -0600 Subject: [PATCH 0650/2892] tcg/optimize: Return true from fold_qemu_st, fold_tcg_st Stores have no output operands, and so need no further work. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 7bd17a36c7..07792c5351 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2143,7 +2143,7 @@ static bool fold_qemu_st(OptContext *ctx, TCGOp *op) { /* Opcodes that touch guest memory stop the mb optimization. */ ctx->prev_mb = NULL; - return false; + return true; } static bool fold_remainder(OptContext *ctx, TCGOp *op) @@ -2706,7 +2706,7 @@ static bool fold_tcg_st(OptContext *ctx, TCGOp *op) if (op->args[1] != tcgv_ptr_arg(tcg_env)) { remove_mem_copy_all(ctx); - return false; + return true; } switch (op->opc) { @@ -2730,7 +2730,7 @@ static bool fold_tcg_st(OptContext *ctx, TCGOp *op) g_assert_not_reached(); } remove_mem_copy_in(ctx, ofs, ofs + lm1); - return false; + return true; } static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) @@ -2740,8 +2740,7 @@ static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) TCGType type; if (op->args[1] != tcgv_ptr_arg(tcg_env)) { - fold_tcg_st(ctx, op); - return false; + return fold_tcg_st(ctx, op); } src = arg_temp(op->args[0]); @@ -2763,7 +2762,7 @@ static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) last = ofs + tcg_type_size(type) - 1; remove_mem_copy_in(ctx, ofs, last); record_mem_copy(ctx, type, src, ofs, last); - return false; + return true; } static bool fold_xor(OptContext *ctx, TCGOp *op) From f9e3934903a8e388559c373f93544e3f9e6a9fc0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:36:50 -0600 Subject: [PATCH 0651/2892] tcg/optimize: Use finish_folding in fold_remainder Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 07792c5351..e78f5a79a3 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2152,7 +2152,7 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) fold_xx_to_i(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) From 95eb229363f28aaacf506974cdb3047d816345fe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:47:59 -0600 Subject: [PATCH 0652/2892] tcg/optimize: Distinguish simplification in fold_setcond_zmask Change return from bool to int; distinguish between complete folding, simplification, and no change. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e78f5a79a3..678015a94a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2155,7 +2155,8 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) return finish_folding(ctx, op); } -static bool fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) +/* Return 1 if finished, -1 if simplified, 0 if unchanged. */ +static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) { uint64_t a_zmask, b_val; TCGCond cond; @@ -2250,11 +2251,10 @@ static bool fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) op->opc = xor_opc; op->args[2] = arg_new_constant(ctx, 1); } - return false; + return -1; } } - - return false; + return 0; } static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) @@ -2359,10 +2359,13 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) return tcg_opt_gen_movi(ctx, op, op->args[0], i); } - if (fold_setcond_zmask(ctx, op, false)) { + i = fold_setcond_zmask(ctx, op, false); + if (i > 0) { return true; } - fold_setcond_tst_pow2(ctx, op, false); + if (i == 0) { + fold_setcond_tst_pow2(ctx, op, false); + } ctx->z_mask = 1; return false; @@ -2376,10 +2379,13 @@ static bool fold_negsetcond(OptContext *ctx, TCGOp *op) return tcg_opt_gen_movi(ctx, op, op->args[0], -i); } - if (fold_setcond_zmask(ctx, op, true)) { + i = fold_setcond_zmask(ctx, op, true); + if (i > 0) { return true; } - fold_setcond_tst_pow2(ctx, op, true); + if (i == 0) { + fold_setcond_tst_pow2(ctx, op, true); + } /* Value is {0,-1} so all bits are repetitions of the sign. */ ctx->s_mask = -1; From 2c8a28398d65e2e4ff31061533873ed09b894543 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:50:37 -0600 Subject: [PATCH 0653/2892] tcg/optimize: Use fold_masks_z in fold_setcond Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 678015a94a..74be827f51 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2367,8 +2367,7 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) fold_setcond_tst_pow2(ctx, op, false); } - ctx->z_mask = 1; - return false; + return fold_masks_z(ctx, op, 1); } static bool fold_negsetcond(OptContext *ctx, TCGOp *op) From 081cf08b09edf0bea704126b607220150c9b5630 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:50:58 -0600 Subject: [PATCH 0654/2892] tcg/optimize: Use fold_masks_s in fold_negsetcond Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 74be827f51..7e909791e1 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2387,8 +2387,7 @@ static bool fold_negsetcond(OptContext *ctx, TCGOp *op) } /* Value is {0,-1} so all bits are repetitions of the sign. */ - ctx->s_mask = -1; - return false; + return fold_masks_s(ctx, op, -1); } static bool fold_setcond2(OptContext *ctx, TCGOp *op) From a53502c0b4b63bc6dd5bf891231e145cf7a637ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:56:36 -0600 Subject: [PATCH 0655/2892] tcg/optimize: Use fold_masks_z in fold_setcond2 Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 7e909791e1..c61d0eae4e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2468,8 +2468,7 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) return fold_setcond(ctx, op); } - ctx->z_mask = 1; - return false; + return fold_masks_z(ctx, op, 1); do_setcond_const: return tcg_opt_gen_movi(ctx, op, op->args[0], i); From 4d20104f9f2deef6d30109f0bba5725c0dcc08da Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:57:53 -0600 Subject: [PATCH 0656/2892] tcg/optimize: Use finish_folding in fold_cmp_vec Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c61d0eae4e..ccdac7b7d7 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2480,7 +2480,7 @@ static bool fold_cmp_vec(OptContext *ctx, TCGOp *op) if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { op->args[3] = tcg_swap_cond(op->args[3]); } - return false; + return finish_folding(ctx, op); } static bool fold_cmpsel_vec(OptContext *ctx, TCGOp *op) From 210c70b7ac449f2eabc55893eca15fe36d36391f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 20:59:15 -0600 Subject: [PATCH 0657/2892] tcg/optimize: Use finish_folding in fold_cmpsel_vec Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index ccdac7b7d7..4090ffe12c 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2501,7 +2501,7 @@ static bool fold_cmpsel_vec(OptContext *ctx, TCGOp *op) if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { op->args[5] = tcg_invert_cond(op->args[5]); } - return false; + return finish_folding(ctx, op); } static bool fold_sextract(OptContext *ctx, TCGOp *op) From baff507e50fbcb270a1a7b448c2cc37cc7f9ec05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 21:09:30 -0600 Subject: [PATCH 0658/2892] tcg/optimize: Use fold_masks_zs in fold_sextract Avoid the use of the OptContext slots. Find TempOptInfo once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 4090ffe12c..2d634c8925 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2507,31 +2507,25 @@ static bool fold_cmpsel_vec(OptContext *ctx, TCGOp *op) static bool fold_sextract(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask, s_mask_old; + TempOptInfo *t1 = arg_info(op->args[1]); int pos = op->args[2]; int len = op->args[3]; - if (arg_is_const(op->args[1])) { - uint64_t t; - - t = arg_info(op->args[1])->val; - t = sextract64(t, pos, len); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + if (ti_is_const(t1)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + sextract64(ti_const_val(t1), pos, len)); } - z_mask = arg_info(op->args[1])->z_mask; - z_mask = sextract64(z_mask, pos, len); - ctx->z_mask = z_mask; - - s_mask_old = arg_info(op->args[1])->s_mask; - s_mask = sextract64(s_mask_old, pos, len); - s_mask |= MAKE_64BIT_MASK(len, 64 - len); - ctx->s_mask = s_mask; + s_mask_old = t1->s_mask; + s_mask = s_mask_old >> pos; + s_mask |= -1ull << (len - 1); if (0 && pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { return true; } - return fold_masks(ctx, op); + z_mask = sextract64(t1->z_mask, pos, len); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_shift(OptContext *ctx, TCGOp *op) From 4e9ce6a2ec73d42e12aedf21b255dc00b378fc8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 21:13:41 -0600 Subject: [PATCH 0659/2892] tcg/optimize: Use fold_masks_zs, fold_masks_s in fold_shift Avoid the use of the OptContext slots. Find TempOptInfo once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2d634c8925..b70e9bdaf5 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2531,6 +2531,7 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) static bool fold_shift(OptContext *ctx, TCGOp *op) { uint64_t s_mask, z_mask, sign; + TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || fold_ix_to_i(ctx, op, 0) || @@ -2538,17 +2539,18 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) return true; } - s_mask = arg_info(op->args[1])->s_mask; - z_mask = arg_info(op->args[1])->z_mask; + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + s_mask = t1->s_mask; + z_mask = t1->z_mask; - if (arg_is_const(op->args[2])) { - int sh = arg_info(op->args[2])->val; - - ctx->z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); + if (ti_is_const(t2)) { + int sh = ti_const_val(t2); + z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); - return fold_masks(ctx, op); + return fold_masks_zs(ctx, op, z_mask, s_mask); } switch (op->opc) { @@ -2557,23 +2559,22 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) * Arithmetic right shift will not reduce the number of * input sign repetitions. */ - ctx->s_mask = s_mask; - break; + return fold_masks_s(ctx, op, s_mask); CASE_OP_32_64(shr): /* * If the sign bit is known zero, then logical right shift - * will not reduced the number of input sign repetitions. + * will not reduce the number of input sign repetitions. */ - sign = (s_mask & -s_mask) >> 1; + sign = -s_mask; if (sign && !(z_mask & sign)) { - ctx->s_mask = s_mask; + return fold_masks_s(ctx, op, s_mask); } break; default: break; } - return false; + return finish_folding(ctx, op); } static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) From 4ed2ba3f4abe6b3c03a905d44cdd18b3a3c1ce33 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 19 Dec 2024 19:38:54 -0800 Subject: [PATCH 0660/2892] tcg/optimize: Simplify sign bit test in fold_shift Merge the two conditions, sign != 0 && !(z_mask & sign), by testing ~z_mask & sign. If sign == 0, the logical and will produce false. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index b70e9bdaf5..26790f7c27 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2530,7 +2530,7 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) static bool fold_shift(OptContext *ctx, TCGOp *op) { - uint64_t s_mask, z_mask, sign; + uint64_t s_mask, z_mask; TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || @@ -2565,8 +2565,7 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) * If the sign bit is known zero, then logical right shift * will not reduce the number of input sign repetitions. */ - sign = -s_mask; - if (sign && !(z_mask & sign)) { + if (~z_mask & -s_mask) { return fold_masks_s(ctx, op, s_mask); } break; From fe1d0074b5cc64e0a548dfba8ab322bd8710c7e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 21:15:22 -0600 Subject: [PATCH 0661/2892] tcg/optimize: Use finish_folding in fold_sub, fold_sub_vec Duplicate fold_sub_vec into fold_sub instead of calling it, now that fold_sub_vec always returns true. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 26790f7c27..cd052a2dbf 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2620,12 +2620,15 @@ static bool fold_sub_vec(OptContext *ctx, TCGOp *op) fold_sub_to_neg(ctx, op)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_sub(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || fold_sub_vec(ctx, op)) { + if (fold_const2(ctx, op) || + fold_xx_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, 0) || + fold_sub_to_neg(ctx, op)) { return true; } @@ -2637,7 +2640,7 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) ? INDEX_op_add_i32 : INDEX_op_add_i64); op->args[2] = arg_new_constant(ctx, -val); } - return false; + return finish_folding(ctx, op); } static bool fold_sub2(OptContext *ctx, TCGOp *op) From d33e0f01db0f75c890a8ed1f1116d45080ca1c3c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 08:53:20 -0600 Subject: [PATCH 0662/2892] tcg/optimize: Use fold_masks_zs in fold_tcg_ld Avoid the use of the OptContext slots. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index cd052a2dbf..7141b18496 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2650,30 +2650,32 @@ static bool fold_sub2(OptContext *ctx, TCGOp *op) static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) { + uint64_t z_mask = -1, s_mask = 0; + /* We can't do any folding with a load, but we can record bits. */ switch (op->opc) { CASE_OP_32_64(ld8s): - ctx->s_mask = MAKE_64BIT_MASK(8, 56); + s_mask = INT8_MIN; break; CASE_OP_32_64(ld8u): - ctx->z_mask = MAKE_64BIT_MASK(0, 8); + z_mask = MAKE_64BIT_MASK(0, 8); break; CASE_OP_32_64(ld16s): - ctx->s_mask = MAKE_64BIT_MASK(16, 48); + s_mask = INT16_MIN; break; CASE_OP_32_64(ld16u): - ctx->z_mask = MAKE_64BIT_MASK(0, 16); + z_mask = MAKE_64BIT_MASK(0, 16); break; case INDEX_op_ld32s_i64: - ctx->s_mask = MAKE_64BIT_MASK(32, 32); + s_mask = INT32_MIN; break; case INDEX_op_ld32u_i64: - ctx->z_mask = MAKE_64BIT_MASK(0, 32); + z_mask = MAKE_64BIT_MASK(0, 32); break; default: g_assert_not_reached(); } - return false; + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_tcg_ld_memcopy(OptContext *ctx, TCGOp *op) From 0fb5b757c344cb57d7c81922262bc8546c3ab504 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 09:44:40 -0600 Subject: [PATCH 0663/2892] tcg/optimize: Use finish_folding in fold_tcg_ld_memcopy Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 7141b18496..047cb5a1ee 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2685,7 +2685,7 @@ static bool fold_tcg_ld_memcopy(OptContext *ctx, TCGOp *op) TCGType type; if (op->args[1] != tcgv_ptr_arg(tcg_env)) { - return false; + return finish_folding(ctx, op); } type = ctx->type; From c890fd71794601431694ce0650055fbe927a1d8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 21:39:01 -0600 Subject: [PATCH 0664/2892] tcg/optimize: Use fold_masks_zs in fold_xor Avoid the use of the OptContext slots. Find TempOptInfo once. Remove fold_masks as the function becomes unused. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 047cb5a1ee..d543266b8d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1077,11 +1077,6 @@ static bool fold_masks_s(OptContext *ctx, TCGOp *op, uint64_t s_mask) return fold_masks_zs(ctx, op, -1, s_mask); } -static bool fold_masks(OptContext *ctx, TCGOp *op) -{ - return fold_masks_zs(ctx, op, ctx->z_mask, ctx->s_mask); -} - /* * An "affected" mask bit is 0 if and only if the result is identical * to the first input. Thus if the entire mask is 0, the operation @@ -2769,6 +2764,9 @@ static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) static bool fold_xor(OptContext *ctx, TCGOp *op) { + uint64_t z_mask, s_mask; + TempOptInfo *t1, *t2; + if (fold_const2_commutative(ctx, op) || fold_xx_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 0) || @@ -2776,11 +2774,11 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) return true; } - ctx->z_mask = arg_info(op->args[1])->z_mask - | arg_info(op->args[2])->z_mask; - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z_mask = t1->z_mask | t2->z_mask; + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) From 4fcd14ca64aaaed88a8b6a5a22b517397a7053b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 21:40:25 -0600 Subject: [PATCH 0665/2892] tcg/optimize: Use finish_folding in fold_bitsel_vec Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d543266b8d..4271d14d2c 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2833,7 +2833,7 @@ static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) return fold_orc(ctx, op); } } - return false; + return finish_folding(ctx, op); } /* Propagate constants and copies, fold constant expressions. */ From 0ae564288947d3670aaa75c931e838d5265d2a64 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 21:42:53 -0600 Subject: [PATCH 0666/2892] tcg/optimize: Use finish_folding as default in tcg_optimize All non-default cases now finish folding within each function. Do the same with the default case and assert it is done after. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 4271d14d2c..51cfcb15d2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -3096,11 +3096,9 @@ void tcg_optimize(TCGContext *s) done = true; break; default: + done = finish_folding(&ctx, op); break; } - - if (!done) { - finish_folding(&ctx, op); - } + tcg_debug_assert(done); } } From a3a88b17c2b2f682554a113e87e764c516e93e08 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 13:57:09 -0600 Subject: [PATCH 0667/2892] tcg/optimize: Remove z_mask, s_mask from OptContext All mask setting is now done with parameters via fold_masks_*. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 51cfcb15d2..98b41975af 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -64,8 +64,6 @@ typedef struct OptContext { QSIMPLEQ_HEAD(, MemCopyInfo) mem_free; /* In flight values from optimization. */ - uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ - uint64_t s_mask; /* mask bit is 1 if value bit matches msb */ TCGType type; } OptContext; @@ -961,13 +959,6 @@ static bool finish_folding(OptContext *ctx, TCGOp *op) for (i = 0; i < nb_oargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); reset_ts(ctx, ts); - /* - * Save the corresponding known-zero/sign bits mask for the - * first output argument (only one supported so far). - */ - if (i == 0) { - ts_info(ts)->z_mask = ctx->z_mask; - } } return true; } @@ -2879,10 +2870,6 @@ void tcg_optimize(TCGContext *s) ctx.type = TCG_TYPE_I32; } - /* Assume all bits affected, no bits known zero, no sign reps. */ - ctx.z_mask = -1; - ctx.s_mask = 0; - /* * Process each opcode. * Sorted alphabetically by opcode as much as possible. From aa9e0501a445d1897b960f5014050497e8e70105 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 21 Dec 2024 22:03:53 -0800 Subject: [PATCH 0668/2892] tcg/optimize: Re-enable sign-mask optimizations All instances of s_mask have been converted to the new representation. We can now re-enable usage. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 98b41975af..182be7e63c 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1797,7 +1797,7 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) g_assert_not_reached(); } - if (0 && !type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + if (!type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { return true; } @@ -2506,7 +2506,7 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) s_mask = s_mask_old >> pos; s_mask |= -1ull << (len - 1); - if (0 && pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + if (pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { return true; } From 7d3c63aca11ee365b36524c95d2d75d70516c6bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 14:06:08 -0600 Subject: [PATCH 0669/2892] tcg/optimize: Move fold_bitsel_vec into alphabetic sort The big comment just above says functions should be sorted. Add forward declarations as needed. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 114 +++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 182be7e63c..1df61378ea 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1193,6 +1193,10 @@ static bool fold_xx_to_x(OptContext *ctx, TCGOp *op) * 3) those that produce information about the result value. */ +static bool fold_or(OptContext *ctx, TCGOp *op); +static bool fold_orc(OptContext *ctx, TCGOp *op); +static bool fold_xor(OptContext *ctx, TCGOp *op); + static bool fold_add(OptContext *ctx, TCGOp *op) { if (fold_const2_commutative(ctx, op) || @@ -1356,6 +1360,61 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) return fold_masks_zs(ctx, op, z_mask, s_mask); } +static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) +{ + /* If true and false values are the same, eliminate the cmp. */ + if (args_are_copies(op->args[2], op->args[3])) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[2]); + } + + if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { + uint64_t tv = arg_info(op->args[2])->val; + uint64_t fv = arg_info(op->args[3])->val; + + if (tv == -1 && fv == 0) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + if (tv == 0 && fv == -1) { + if (TCG_TARGET_HAS_not_vec) { + op->opc = INDEX_op_not_vec; + return fold_not(ctx, op); + } else { + op->opc = INDEX_op_xor_vec; + op->args[2] = arg_new_constant(ctx, -1); + return fold_xor(ctx, op); + } + } + } + if (arg_is_const(op->args[2])) { + uint64_t tv = arg_info(op->args[2])->val; + if (tv == -1) { + op->opc = INDEX_op_or_vec; + op->args[2] = op->args[3]; + return fold_or(ctx, op); + } + if (tv == 0 && TCG_TARGET_HAS_andc_vec) { + op->opc = INDEX_op_andc_vec; + op->args[2] = op->args[1]; + op->args[1] = op->args[3]; + return fold_andc(ctx, op); + } + } + if (arg_is_const(op->args[3])) { + uint64_t fv = arg_info(op->args[3])->val; + if (fv == 0) { + op->opc = INDEX_op_and_vec; + return fold_and(ctx, op); + } + if (fv == -1 && TCG_TARGET_HAS_orc_vec) { + op->opc = INDEX_op_orc_vec; + op->args[2] = op->args[1]; + op->args[1] = op->args[3]; + return fold_orc(ctx, op); + } + } + return finish_folding(ctx, op); +} + static bool fold_brcond(OptContext *ctx, TCGOp *op) { int i = do_constant_folding_cond1(ctx, op, NO_DEST, &op->args[0], @@ -2772,61 +2831,6 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) return fold_masks_zs(ctx, op, z_mask, s_mask); } -static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) -{ - /* If true and false values are the same, eliminate the cmp. */ - if (args_are_copies(op->args[2], op->args[3])) { - return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[2]); - } - - if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { - uint64_t tv = arg_info(op->args[2])->val; - uint64_t fv = arg_info(op->args[3])->val; - - if (tv == -1 && fv == 0) { - return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); - } - if (tv == 0 && fv == -1) { - if (TCG_TARGET_HAS_not_vec) { - op->opc = INDEX_op_not_vec; - return fold_not(ctx, op); - } else { - op->opc = INDEX_op_xor_vec; - op->args[2] = arg_new_constant(ctx, -1); - return fold_xor(ctx, op); - } - } - } - if (arg_is_const(op->args[2])) { - uint64_t tv = arg_info(op->args[2])->val; - if (tv == -1) { - op->opc = INDEX_op_or_vec; - op->args[2] = op->args[3]; - return fold_or(ctx, op); - } - if (tv == 0 && TCG_TARGET_HAS_andc_vec) { - op->opc = INDEX_op_andc_vec; - op->args[2] = op->args[1]; - op->args[1] = op->args[3]; - return fold_andc(ctx, op); - } - } - if (arg_is_const(op->args[3])) { - uint64_t fv = arg_info(op->args[3])->val; - if (fv == 0) { - op->opc = INDEX_op_and_vec; - return fold_and(ctx, op); - } - if (fv == -1 && TCG_TARGET_HAS_orc_vec) { - op->opc = INDEX_op_orc_vec; - op->args[2] = op->args[1]; - op->args[1] = op->args[3]; - return fold_orc(ctx, op); - } - } - return finish_folding(ctx, op); -} - /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { From 29f6586f6167a0ef6c8eaeb8c3cbdf4ff4c9d762 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 14:09:49 -0600 Subject: [PATCH 0670/2892] tcg/optimize: Move fold_cmp_vec, fold_cmpsel_vec into alphabetic sort The big comment just above says functions should be sorted. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 1df61378ea..c23f0d1392 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1619,6 +1619,36 @@ static bool fold_call(OptContext *ctx, TCGOp *op) return true; } +static bool fold_cmp_vec(OptContext *ctx, TCGOp *op) +{ + /* Canonicalize the comparison to put immediate second. */ + if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { + op->args[3] = tcg_swap_cond(op->args[3]); + } + return finish_folding(ctx, op); +} + +static bool fold_cmpsel_vec(OptContext *ctx, TCGOp *op) +{ + /* If true and false values are the same, eliminate the cmp. */ + if (args_are_copies(op->args[3], op->args[4])) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[3]); + } + + /* Canonicalize the comparison to put immediate second. */ + if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { + op->args[5] = tcg_swap_cond(op->args[5]); + } + /* + * Canonicalize the "false" input reg to match the destination, + * so that the tcg backend can implement "move if true". + */ + if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { + op->args[5] = tcg_invert_cond(op->args[5]); + } + return finish_folding(ctx, op); +} + static bool fold_count_zeros(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask; @@ -2519,36 +2549,6 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) return tcg_opt_gen_movi(ctx, op, op->args[0], i); } -static bool fold_cmp_vec(OptContext *ctx, TCGOp *op) -{ - /* Canonicalize the comparison to put immediate second. */ - if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { - op->args[3] = tcg_swap_cond(op->args[3]); - } - return finish_folding(ctx, op); -} - -static bool fold_cmpsel_vec(OptContext *ctx, TCGOp *op) -{ - /* If true and false values are the same, eliminate the cmp. */ - if (args_are_copies(op->args[3], op->args[4])) { - return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[3]); - } - - /* Canonicalize the comparison to put immediate second. */ - if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { - op->args[5] = tcg_swap_cond(op->args[5]); - } - /* - * Canonicalize the "false" input reg to match the destination, - * so that the tcg backend can implement "move if true". - */ - if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { - op->args[5] = tcg_invert_cond(op->args[5]); - } - return finish_folding(ctx, op); -} - static bool fold_sextract(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask, s_mask_old; From 910556bbf4ffe41c9de5cf7f2c3a269ac2de5324 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 7 Dec 2024 17:21:25 -0600 Subject: [PATCH 0671/2892] softfloat: Add float{16,32,64}_muladd_scalbn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently have a flag, float_muladd_halve_result, to scale the result by 2**-1. Extend this to handle arbitrary scaling. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 7 +++-- fpu/softfloat.c | 58 ++++++++++++++++++++++----------------- include/fpu/softfloat.h | 6 ++++ 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index ba8de7be76..4a62d6ca24 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -562,8 +562,9 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, * Requires A and C extracted into a double-sized structure to provide the * extra space for the widening multiply. */ -static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, - FloatPartsN *c, int flags, float_status *s) +static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, + FloatPartsN *c, int scale, + int flags, float_status *s) { int ab_mask, abc_mask; FloatPartsW p_widen, c_widen; @@ -652,9 +653,11 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, a->exp = p_widen.exp; return_normal: + /* TODO: Replace all use of float_muladd_halve_result with scale. */ if (flags & float_muladd_halve_result) { a->exp -= 1; } + a->exp += scale; finish_sign: if (flags & float_muladd_negate_result) { a->sign ^= 1; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 8de8d5f342..b5936cc0f8 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -789,15 +789,15 @@ static FloatParts128 *parts128_mul(FloatParts128 *a, FloatParts128 *b, #define parts_mul(A, B, S) \ PARTS_GENERIC_64_128(mul, A)(A, B, S) -static FloatParts64 *parts64_muladd(FloatParts64 *a, FloatParts64 *b, - FloatParts64 *c, int flags, - float_status *s); -static FloatParts128 *parts128_muladd(FloatParts128 *a, FloatParts128 *b, - FloatParts128 *c, int flags, - float_status *s); +static FloatParts64 *parts64_muladd_scalbn(FloatParts64 *a, FloatParts64 *b, + FloatParts64 *c, int scale, + int flags, float_status *s); +static FloatParts128 *parts128_muladd_scalbn(FloatParts128 *a, FloatParts128 *b, + FloatParts128 *c, int scale, + int flags, float_status *s); -#define parts_muladd(A, B, C, Z, S) \ - PARTS_GENERIC_64_128(muladd, A)(A, B, C, Z, S) +#define parts_muladd_scalbn(A, B, C, Z, Y, S) \ + PARTS_GENERIC_64_128(muladd_scalbn, A)(A, B, C, Z, Y, S) static FloatParts64 *parts64_div(FloatParts64 *a, FloatParts64 *b, float_status *s); @@ -2212,43 +2212,50 @@ floatx80_mul(floatx80 a, floatx80 b, float_status *status) * Fused multiply-add */ -float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c, - int flags, float_status *status) +float16 QEMU_FLATTEN +float16_muladd_scalbn(float16 a, float16 b, float16 c, + int scale, int flags, float_status *status) { FloatParts64 pa, pb, pc, *pr; float16_unpack_canonical(&pa, a, status); float16_unpack_canonical(&pb, b, status); float16_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); return float16_round_pack_canonical(pr, status); } -static float32 QEMU_SOFTFLOAT_ATTR -soft_f32_muladd(float32 a, float32 b, float32 c, int flags, - float_status *status) +float16 float16_muladd(float16 a, float16 b, float16 c, + int flags, float_status *status) +{ + return float16_muladd_scalbn(a, b, c, 0, flags, status); +} + +float32 QEMU_SOFTFLOAT_ATTR +float32_muladd_scalbn(float32 a, float32 b, float32 c, + int scale, int flags, float_status *status) { FloatParts64 pa, pb, pc, *pr; float32_unpack_canonical(&pa, a, status); float32_unpack_canonical(&pb, b, status); float32_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); return float32_round_pack_canonical(pr, status); } -static float64 QEMU_SOFTFLOAT_ATTR -soft_f64_muladd(float64 a, float64 b, float64 c, int flags, - float_status *status) +float64 QEMU_SOFTFLOAT_ATTR +float64_muladd_scalbn(float64 a, float64 b, float64 c, + int scale, int flags, float_status *status) { FloatParts64 pa, pb, pc, *pr; float64_unpack_canonical(&pa, a, status); float64_unpack_canonical(&pb, b, status); float64_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); return float64_round_pack_canonical(pr, status); } @@ -2323,7 +2330,7 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) return ur.s; soft: - return soft_f32_muladd(ua.s, ub.s, uc.s, flags, s); + return float32_muladd_scalbn(ua.s, ub.s, uc.s, 0, flags, s); } float64 QEMU_FLATTEN @@ -2394,7 +2401,7 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) return ur.s; soft: - return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s); + return float64_muladd_scalbn(ua.s, ub.s, uc.s, 0, flags, s); } float64 float64r32_muladd(float64 a, float64 b, float64 c, @@ -2405,7 +2412,7 @@ float64 float64r32_muladd(float64 a, float64 b, float64 c, float64_unpack_canonical(&pa, a, status); float64_unpack_canonical(&pb, b, status); float64_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); return float64r32_round_pack_canonical(pr, status); } @@ -2418,7 +2425,7 @@ bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c, bfloat16_unpack_canonical(&pa, a, status); bfloat16_unpack_canonical(&pb, b, status); bfloat16_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); return bfloat16_round_pack_canonical(pr, status); } @@ -2431,7 +2438,7 @@ float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c, float128_unpack_canonical(&pa, a, status); float128_unpack_canonical(&pb, b, status); float128_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); return float128_round_pack_canonical(pr, status); } @@ -5249,8 +5256,9 @@ float32 float32_exp2(float32 a, float_status *status) float64_unpack_canonical(&rp, float64_one, status); for (i = 0 ; i < 15 ; i++) { + float64_unpack_canonical(&tp, float32_exp2_coefficients[i], status); - rp = *parts_muladd(&tp, &xnp, &rp, 0, status); + rp = *parts_muladd_scalbn(&tp, &xnp, &rp, 0, 0, status); xnp = *parts_mul(&xnp, &xp, status); } diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index eb64075b9c..c34ce0477d 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -238,6 +238,8 @@ float16 float16_add(float16, float16, float_status *status); float16 float16_sub(float16, float16, float_status *status); float16 float16_mul(float16, float16, float_status *status); float16 float16_muladd(float16, float16, float16, int, float_status *status); +float16 float16_muladd_scalbn(float16, float16, float16, + int, int, float_status *status); float16 float16_div(float16, float16, float_status *status); float16 float16_scalbn(float16, int, float_status *status); float16 float16_min(float16, float16, float_status *status); @@ -597,6 +599,8 @@ float32 float32_mul(float32, float32, float_status *status); float32 float32_div(float32, float32, float_status *status); float32 float32_rem(float32, float32, float_status *status); float32 float32_muladd(float32, float32, float32, int, float_status *status); +float32 float32_muladd_scalbn(float32, float32, float32, + int, int, float_status *status); float32 float32_sqrt(float32, float_status *status); float32 float32_exp2(float32, float_status *status); float32 float32_log2(float32, float_status *status); @@ -792,6 +796,8 @@ float64 float64_mul(float64, float64, float_status *status); float64 float64_div(float64, float64, float_status *status); float64 float64_rem(float64, float64, float_status *status); float64 float64_muladd(float64, float64, float64, int, float_status *status); +float64 float64_muladd_scalbn(float64, float64, float64, + int, int, float_status *status); float64 float64_sqrt(float64, float_status *status); float64 float64_log2(float64, float_status *status); FloatRelation float64_compare(float64, float64, float_status *status); From 912400a362e6d25a137e8314fa78ca0429d908aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 7 Dec 2024 17:43:10 -0600 Subject: [PATCH 0672/2892] target/arm: Use float*_muladd_scalbn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the scalbn interface instead of float_muladd_halve_result. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/helper-a64.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 0e130501fd..3b226daee7 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -262,7 +262,7 @@ uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, float_status *fpst) (float16_is_infinity(b) && float16_is_zero(a))) { return float16_one_point_five; } - return float16_muladd(a, b, float16_three, float_muladd_halve_result, fpst); + return float16_muladd_scalbn(a, b, float16_three, -1, 0, fpst); } float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, float_status *fpst) @@ -275,7 +275,7 @@ float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, float_status *fpst) (float32_is_infinity(b) && float32_is_zero(a))) { return float32_one_point_five; } - return float32_muladd(a, b, float32_three, float_muladd_halve_result, fpst); + return float32_muladd_scalbn(a, b, float32_three, -1, 0, fpst); } float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, float_status *fpst) @@ -288,7 +288,7 @@ float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, float_status *fpst) (float64_is_infinity(b) && float64_is_zero(a))) { return float64_one_point_five; } - return float64_muladd(a, b, float64_three, float_muladd_halve_result, fpst); + return float64_muladd_scalbn(a, b, float64_three, -1, 0, fpst); } /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ From 88d5f550bd570cd2837c0316bea6bae8cb4b745a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 7 Dec 2024 18:00:59 -0600 Subject: [PATCH 0673/2892] target/sparc: Use float*_muladd_scalbn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the scalbn interface instead of float_muladd_halve_result. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/fop_helper.c | 8 ++-- target/sparc/helper.h | 4 +- target/sparc/translate.c | 80 +++++++++++++++++++++++---------------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 236d27b19c..c25097d07f 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -344,17 +344,17 @@ Int128 helper_fsqrtq(CPUSPARCState *env, Int128 src) } float32 helper_fmadds(CPUSPARCState *env, float32 s1, - float32 s2, float32 s3, uint32_t op) + float32 s2, float32 s3, int32_t sc, uint32_t op) { - float32 ret = float32_muladd(s1, s2, s3, op, &env->fp_status); + float32 ret = float32_muladd_scalbn(s1, s2, s3, sc, op, &env->fp_status); check_ieee_exceptions(env, GETPC()); return ret; } float64 helper_fmaddd(CPUSPARCState *env, float64 s1, - float64 s2, float64 s3, uint32_t op) + float64 s2, float64 s3, int32_t sc, uint32_t op) { - float64 ret = float64_muladd(s1, s2, s3, op, &env->fp_status); + float64 ret = float64_muladd_scalbn(s1, s2, s3, sc, op, &env->fp_status); check_ieee_exceptions(env, GETPC()); return ret; } diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 1ae3f0c467..3a7f7dc129 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -59,7 +59,7 @@ DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_WG, f64, env, f64, f64) -DEF_HELPER_FLAGS_5(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, i32) +DEF_HELPER_FLAGS_6(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, s32, i32) DEF_HELPER_FLAGS_3(fnaddd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fnmuld, TCG_CALL_NO_WG, f64, env, f64, f64) @@ -72,7 +72,7 @@ DEF_HELPER_FLAGS_3(fadds, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_5(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, i32) +DEF_HELPER_FLAGS_6(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, s32, i32) DEF_HELPER_FLAGS_3(fnadds, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fnmuls, TCG_CALL_NO_WG, f32, env, f32, f32) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9be26c804e..465e20f4f3 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1359,93 +1359,109 @@ static void gen_op_fabsq(TCGv_i128 dst, TCGv_i128 src) static void gen_op_fmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(0)); + TCGv_i32 z = tcg_constant_i32(0); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, z); } static void gen_op_fmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(0)); + TCGv_i32 z = tcg_constant_i32(0); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, z); } static void gen_op_fmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - int op = float_muladd_negate_c; - gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op); } static void gen_op_fmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - int op = float_muladd_negate_c; - gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op); } static void gen_op_fnmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - int op = float_muladd_negate_c | float_muladd_negate_result; - gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c | + float_muladd_negate_result); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op); } static void gen_op_fnmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - int op = float_muladd_negate_c | float_muladd_negate_result; - gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c | + float_muladd_negate_result); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op); } static void gen_op_fnmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - int op = float_muladd_negate_result; - gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op); } static void gen_op_fnmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - int op = float_muladd_negate_result; - gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op); } /* Use muladd to compute (1 * src1) + src2 / 2 with one rounding. */ static void gen_op_fhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) { - TCGv_i32 one = tcg_constant_i32(float32_one); - int op = float_muladd_halve_result; - gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); + TCGv_i32 fone = tcg_constant_i32(float32_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(0); + gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op); } static void gen_op_fhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) { - TCGv_i64 one = tcg_constant_i64(float64_one); - int op = float_muladd_halve_result; - gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); + TCGv_i64 fone = tcg_constant_i64(float64_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(0); + gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op); } /* Use muladd to compute (1 * src1) - src2 / 2 with one rounding. */ static void gen_op_fhsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) { - TCGv_i32 one = tcg_constant_i32(float32_one); - int op = float_muladd_negate_c | float_muladd_halve_result; - gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); + TCGv_i32 fone = tcg_constant_i32(float32_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op); } static void gen_op_fhsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) { - TCGv_i64 one = tcg_constant_i64(float64_one); - int op = float_muladd_negate_c | float_muladd_halve_result; - gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); + TCGv_i64 fone = tcg_constant_i64(float64_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op); } /* Use muladd to compute -((1 * src1) + src2 / 2) with one rounding. */ static void gen_op_fnhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) { - TCGv_i32 one = tcg_constant_i32(float32_one); - int op = float_muladd_negate_result | float_muladd_halve_result; - gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); + TCGv_i32 fone = tcg_constant_i32(float32_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op); } static void gen_op_fnhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) { - TCGv_i64 one = tcg_constant_i64(float64_one); - int op = float_muladd_negate_result | float_muladd_halve_result; - gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); + TCGv_i64 fone = tcg_constant_i64(float64_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op); } static void gen_op_fpexception_im(DisasContext *dc, int ftt) From 6a243913aa46f3d60ce36c7a826562c6e40b64d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 7 Dec 2024 18:06:30 -0600 Subject: [PATCH 0674/2892] softfloat: Remove float_muladd_halve_result MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All uses have been convered to float*_muladd_scalbn. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 4 ---- fpu/softfloat.c | 6 ------ include/fpu/softfloat.h | 3 --- 3 files changed, 13 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 4a62d6ca24..a724f317c5 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -653,10 +653,6 @@ static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, a->exp = p_widen.exp; return_normal: - /* TODO: Replace all use of float_muladd_halve_result with scale. */ - if (flags & float_muladd_halve_result) { - a->exp -= 1; - } a->exp += scale; finish_sign: if (flags & float_muladd_negate_result) { diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b5936cc0f8..6967fb5c9f 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -2274,9 +2274,6 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) if (unlikely(!can_use_fpu(s))) { goto soft; } - if (unlikely(flags & float_muladd_halve_result)) { - goto soft; - } float32_input_flush3(&ua.s, &ub.s, &uc.s, s); if (unlikely(!f32_is_zon3(ua, ub, uc))) { @@ -2345,9 +2342,6 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) if (unlikely(!can_use_fpu(s))) { goto soft; } - if (unlikely(flags & float_muladd_halve_result)) { - goto soft; - } float64_input_flush3(&ua.s, &ub.s, &uc.s, s); if (unlikely(!f64_is_zon3(ua, ub, uc))) { diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index c34ce0477d..aa69aecfb0 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -120,14 +120,11 @@ bfloat16 bfloat16_squash_input_denormal(bfloat16 a, float_status *status); | Using these differs from negating an input or output before calling | the muladd function in that this means that a NaN doesn't have its | sign bit inverted before it is propagated. -| We also support halving the result before rounding, as a special -| case to support the ARM fused-sqrt-step instruction FRSQRTS. *----------------------------------------------------------------------------*/ enum { float_muladd_negate_c = 1, float_muladd_negate_product = 2, float_muladd_negate_result = 4, - float_muladd_halve_result = 8, }; /*---------------------------------------------------------------------------- From 72330260cdb42015ae72096bae37e6fdaf361737 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 08:54:41 -0600 Subject: [PATCH 0675/2892] softfloat: Add float_round_nearest_even_max This rounding mode is used by Hexagon. Signed-off-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 3 +++ include/fpu/softfloat-types.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index a724f317c5..37d046cfe9 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -241,6 +241,9 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, int exp, flags = 0; switch (s->float_rounding_mode) { + case float_round_nearest_even_max: + overflow_norm = true; + /* fall through */ case float_round_nearest_even: if (N > 64 && frac_lsb == 0) { inc = ((p->frac_hi & 1) || (p->frac_lo & round_mask) != frac_lsbm1 diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 79ca44dcc3..9d37cdfaa8 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -140,6 +140,8 @@ typedef enum __attribute__((__packed__)) { float_round_to_odd = 5, /* Not an IEEE rounding mode: round to closest odd, overflow to inf */ float_round_to_odd_inf = 6, + /* Not an IEEE rounding mode: round to nearest even, overflow to max */ + float_round_nearest_even_max = 7, } FloatRoundMode; /* From 82f898f3b660fb11e601ee5ea1cab4b2fdafacc8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 09:13:45 -0600 Subject: [PATCH 0676/2892] softfloat: Add float_muladd_suppress_add_product_zero Certain Hexagon instructions suppress changes to the result when the product of fma() is a true zero. Signed-off-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 4 +++- fpu/softfloat.c | 3 +++ include/fpu/softfloat.h | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 37d046cfe9..ebde42992f 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -615,7 +615,9 @@ static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, goto return_normal; } if (c->cls == float_class_zero) { - if (a->sign != c->sign) { + if (flags & float_muladd_suppress_add_product_zero) { + a->sign = c->sign; + } else if (a->sign != c->sign) { goto return_sub_zero; } goto return_zero; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 6967fb5c9f..8d75d66817 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -2274,6 +2274,9 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) if (unlikely(!can_use_fpu(s))) { goto soft; } + if (unlikely(flags & float_muladd_suppress_add_product_zero)) { + goto soft; + } float32_input_flush3(&ua.s, &ub.s, &uc.s, s); if (unlikely(!f32_is_zon3(ua, ub, uc))) { diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index aa69aecfb0..09a40b4310 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -120,11 +120,16 @@ bfloat16 bfloat16_squash_input_denormal(bfloat16 a, float_status *status); | Using these differs from negating an input or output before calling | the muladd function in that this means that a NaN doesn't have its | sign bit inverted before it is propagated. +| +| With float_muladd_suppress_add_product_zero, if A or B is zero +| such that the product is a true zero, then return C without addition. +| This preserves the sign of C when C is +/- 0. Used for Hexagon. *----------------------------------------------------------------------------*/ enum { float_muladd_negate_c = 1, float_muladd_negate_product = 2, float_muladd_negate_result = 4, + float_muladd_suppress_add_product_zero = 8, }; /*---------------------------------------------------------------------------- From 6e7422dc22fd2a3bd581e6a496470f6edecc6357 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 09:27:44 -0600 Subject: [PATCH 0677/2892] target/hexagon: Use float32_mul in helper_sfmpy There are no special cases for this instruction. Remove internal_mpyf as unused. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 8 -------- target/hexagon/fma_emu.h | 1 - target/hexagon/op_helper.c | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index 05a56d8c10..35971b8b99 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -655,14 +655,6 @@ float32 internal_fmafx(float32 a, float32 b, float32 c, int scale, return accum_round_float32(result, fp_status); } -float32 internal_mpyf(float32 a, float32 b, float_status *fp_status) -{ - if (float32_is_zero(a) || float32_is_zero(b)) { - return float32_mul(a, b, fp_status); - } - return internal_fmafx(a, b, float32_zero, 0, fp_status); -} - float64 internal_mpyhh(float64 a, float64 b, unsigned long long int accumulated, float_status *fp_status) diff --git a/target/hexagon/fma_emu.h b/target/hexagon/fma_emu.h index 91591d6050..ad5df5d038 100644 --- a/target/hexagon/fma_emu.h +++ b/target/hexagon/fma_emu.h @@ -32,7 +32,6 @@ int32_t float32_getexp(float32 f32); float32 infinite_float32(uint8_t sign); float32 internal_fmafx(float32 a, float32 b, float32 c, int scale, float_status *fp_status); -float32 internal_mpyf(float32 a, float32 b, float_status *fp_status); float64 internal_mpyhh(float64 a, float64 b, unsigned long long int accumulated, float_status *fp_status); diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 01d1a1b1a7..d257097091 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1045,7 +1045,7 @@ float32 HELPER(sfmpy)(CPUHexagonState *env, float32 RsV, float32 RtV) { float32 RdV; arch_fpop_start(env); - RdV = internal_mpyf(RsV, RtV, &env->fp_status); + RdV = float32_mul(RsV, RtV, &env->fp_status); arch_fpop_end(env); return RdV; } From 655a83cac128574c7ea386042f8eefa5be5708e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 09:32:05 -0600 Subject: [PATCH 0678/2892] target/hexagon: Use float32_muladd for helper_sffma There are no special cases for this instruction. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/op_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index d257097091..15b143a568 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1054,7 +1054,7 @@ float32 HELPER(sffma)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { arch_fpop_start(env); - RxV = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status); + RxV = float32_muladd(RsV, RtV, RxV, 0, &env->fp_status); arch_fpop_end(env); return RxV; } From 2eca1928f9afb7cbc8e72a59dffb964c8319469a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 13:51:04 -0600 Subject: [PATCH 0679/2892] target/hexagon: Use float32_muladd for helper_sffms There are no special cases for this instruction. Since hexagon always uses default-nan mode, explicitly negating the first input is unnecessary. Use float_muladd_negate_product instead. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/op_helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 15b143a568..95bfa5d029 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1096,10 +1096,9 @@ float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV, float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { - float32 neg_RsV; arch_fpop_start(env); - neg_RsV = float32_set_sign(RsV, float32_is_neg(RsV) ? 0 : 1); - RxV = internal_fmafx(neg_RsV, RtV, RxV, 0, &env->fp_status); + RxV = float32_muladd(RsV, RtV, RxV, float_muladd_negate_product, + &env->fp_status); arch_fpop_end(env); return RxV; } From 904624ab8e1f41dd97bb6e2e524605f4c61bc795 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 14:14:36 -0600 Subject: [PATCH 0680/2892] target/hexagon: Use float32_muladd_scalbn for helper_sffma_sc This instruction has a special case that 0 * x + c returns c without the normal sign folding that comes with 0 + -0. Use the new float_muladd_suppress_add_product_zero to describe this. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/op_helper.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 95bfa5d029..53c65e852e 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1080,15 +1080,10 @@ static float32 check_nan(float32 dst, float32 x, float_status *fp_status) float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV, float32 PuV) { - size4s_t tmp; arch_fpop_start(env); - RxV = check_nan(RxV, RxV, &env->fp_status); - RxV = check_nan(RxV, RsV, &env->fp_status); - RxV = check_nan(RxV, RtV, &env->fp_status); - tmp = internal_fmafx(RsV, RtV, RxV, fSXTN(8, 64, PuV), &env->fp_status); - if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { - RxV = tmp; - } + RxV = float32_muladd_scalbn(RsV, RtV, RxV, fSXTN(8, 64, PuV), + float_muladd_suppress_add_product_zero, + &env->fp_status); arch_fpop_end(env); return RxV; } From 316dca398579f2de2f433db02685e6799159c498 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 14:33:22 -0600 Subject: [PATCH 0681/2892] target/hexagon: Use float32_muladd for helper_sffm[as]_lib There are multiple special cases for this instruction. (1) The saturate to normal maximum instead of overflow to infinity is handled by the new float_round_nearest_even_max rounding mode. (2) The 0 * n + c special case is handled by the new float_muladd_suppress_add_product_zero flag. (3) The Inf - Inf -> 0 special case can be detected after the fact by examining float_flag_invalid_isi. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/op_helper.c | 105 +++++++++---------------------------- 1 file changed, 26 insertions(+), 79 deletions(-) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 53c65e852e..6da8db8ea5 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1059,24 +1059,6 @@ float32 HELPER(sffma)(CPUHexagonState *env, float32 RxV, return RxV; } -static bool is_zero_prod(float32 a, float32 b) -{ - return ((float32_is_zero(a) && is_finite(b)) || - (float32_is_zero(b) && is_finite(a))); -} - -static float32 check_nan(float32 dst, float32 x, float_status *fp_status) -{ - float32 ret = dst; - if (float32_is_any_nan(x)) { - if (extract32(x, 22, 1) == 0) { - float_raise(float_flag_invalid, fp_status); - } - ret = make_float32(0xffffffff); /* nan */ - } - return ret; -} - float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV, float32 PuV) { @@ -1098,78 +1080,43 @@ float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV, return RxV; } -static bool is_inf_prod(int32_t a, int32_t b) +static float32 do_sffma_lib(CPUHexagonState *env, float32 RxV, + float32 RsV, float32 RtV, int negate) { - return (float32_is_infinity(a) && float32_is_infinity(b)) || - (float32_is_infinity(a) && is_finite(b) && !float32_is_zero(b)) || - (float32_is_infinity(b) && is_finite(a) && !float32_is_zero(a)); + int flags; + + arch_fpop_start(env); + + set_float_rounding_mode(float_round_nearest_even_max, &env->fp_status); + RxV = float32_muladd(RsV, RtV, RxV, + negate | float_muladd_suppress_add_product_zero, + &env->fp_status); + + flags = get_float_exception_flags(&env->fp_status); + if (flags) { + /* Flags are suppressed by this instruction. */ + set_float_exception_flags(0, &env->fp_status); + + /* Return 0 for Inf - Inf. */ + if (flags & float_flag_invalid_isi) { + RxV = 0; + } + } + + arch_fpop_end(env); + return RxV; } float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { - bool infinp; - bool infminusinf; - float32 tmp; - - arch_fpop_start(env); - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - infminusinf = float32_is_infinity(RxV) && - is_inf_prod(RsV, RtV) && - (fGETBIT(31, RsV ^ RxV ^ RtV) != 0); - infinp = float32_is_infinity(RxV) || - float32_is_infinity(RtV) || - float32_is_infinity(RsV); - RxV = check_nan(RxV, RxV, &env->fp_status); - RxV = check_nan(RxV, RsV, &env->fp_status); - RxV = check_nan(RxV, RtV, &env->fp_status); - tmp = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status); - if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { - RxV = tmp; - } - set_float_exception_flags(0, &env->fp_status); - if (float32_is_infinity(RxV) && !infinp) { - RxV = RxV - 1; - } - if (infminusinf) { - RxV = 0; - } - arch_fpop_end(env); - return RxV; + return do_sffma_lib(env, RxV, RsV, RtV, 0); } float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { - bool infinp; - bool infminusinf; - float32 tmp; - - arch_fpop_start(env); - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - infminusinf = float32_is_infinity(RxV) && - is_inf_prod(RsV, RtV) && - (fGETBIT(31, RsV ^ RxV ^ RtV) == 0); - infinp = float32_is_infinity(RxV) || - float32_is_infinity(RtV) || - float32_is_infinity(RsV); - RxV = check_nan(RxV, RxV, &env->fp_status); - RxV = check_nan(RxV, RsV, &env->fp_status); - RxV = check_nan(RxV, RtV, &env->fp_status); - float32 minus_RsV = float32_sub(float32_zero, RsV, &env->fp_status); - tmp = internal_fmafx(minus_RsV, RtV, RxV, 0, &env->fp_status); - if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { - RxV = tmp; - } - set_float_exception_flags(0, &env->fp_status); - if (float32_is_infinity(RxV) && !infinp) { - RxV = RxV - 1; - } - if (infminusinf) { - RxV = 0; - } - arch_fpop_end(env); - return RxV; + return do_sffma_lib(env, RxV, RsV, RtV, float_muladd_negate_product); } float64 HELPER(dfmpyfix)(CPUHexagonState *env, float64 RssV, float64 RttV) From 813437e5002b0726535df4c77527986acac5de3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 14:52:52 -0600 Subject: [PATCH 0682/2892] target/hexagon: Remove internal_fmafx The function is now unused. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 171 --------------------------------------- target/hexagon/fma_emu.h | 2 - 2 files changed, 173 deletions(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index 35971b8b99..0c7c7f636c 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -90,21 +90,6 @@ int32_t float64_getexp(float64 f64) return -1; } -static uint64_t float32_getmant(float32 f32) -{ - Float a = { .i = f32 }; - if (float32_is_normal(f32)) { - return a.mant | 1ULL << 23; - } - if (float32_is_zero(f32)) { - return 0; - } - if (float32_is_denormal(f32)) { - return a.mant; - } - return ~0ULL; -} - int32_t float32_getexp(float32 f32) { Float a = { .i = f32 }; @@ -369,25 +354,6 @@ float32 infinite_float32(uint8_t sign) } /* Return a maximum finite value with the requested sign */ -static float32 maxfinite_float32(uint8_t sign) -{ - if (sign) { - return make_float32(SF_MINUS_MAXF); - } else { - return make_float32(SF_MAXF); - } -} - -/* Return a zero value with requested sign */ -static float32 zero_float32(uint8_t sign) -{ - if (sign) { - return make_float32(0x80000000); - } else { - return float32_zero; - } -} - #define GEN_XF_ROUND(SUFFIX, MANTBITS, INF_EXP, INTERNAL_TYPE) \ static SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \ { \ @@ -517,143 +483,6 @@ static SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \ } GEN_XF_ROUND(float64, DF_MANTBITS, DF_INF_EXP, Double) -GEN_XF_ROUND(float32, SF_MANTBITS, SF_INF_EXP, Float) - -static bool is_inf_prod(float64 a, float64 b) -{ - return ((float64_is_infinity(a) && float64_is_infinity(b)) || - (float64_is_infinity(a) && is_finite(b) && (!float64_is_zero(b))) || - (float64_is_infinity(b) && is_finite(a) && (!float64_is_zero(a)))); -} - -static float64 special_fma(float64 a, float64 b, float64 c, - float_status *fp_status) -{ - float64 ret = make_float64(0); - - /* - * If A multiplied by B is an exact infinity and C is also an infinity - * but with the opposite sign, FMA returns NaN and raises invalid. - */ - uint8_t a_sign = float64_is_neg(a); - uint8_t b_sign = float64_is_neg(b); - uint8_t c_sign = float64_is_neg(c); - if (is_inf_prod(a, b) && float64_is_infinity(c)) { - if ((a_sign ^ b_sign) != c_sign) { - ret = make_float64(DF_NAN); - float_raise(float_flag_invalid, fp_status); - return ret; - } - } - if ((float64_is_infinity(a) && float64_is_zero(b)) || - (float64_is_zero(a) && float64_is_infinity(b))) { - ret = make_float64(DF_NAN); - float_raise(float_flag_invalid, fp_status); - return ret; - } - /* - * If none of the above checks are true and C is a NaN, - * a NaN shall be returned - * If A or B are NaN, a NAN shall be returned. - */ - if (float64_is_any_nan(a) || - float64_is_any_nan(b) || - float64_is_any_nan(c)) { - if (float64_is_any_nan(a) && (fGETBIT(51, a) == 0)) { - float_raise(float_flag_invalid, fp_status); - } - if (float64_is_any_nan(b) && (fGETBIT(51, b) == 0)) { - float_raise(float_flag_invalid, fp_status); - } - if (float64_is_any_nan(c) && (fGETBIT(51, c) == 0)) { - float_raise(float_flag_invalid, fp_status); - } - ret = make_float64(DF_NAN); - return ret; - } - /* - * We have checked for adding opposite-signed infinities. - * Other infinities return infinity with the correct sign - */ - if (float64_is_infinity(c)) { - ret = infinite_float64(c_sign); - return ret; - } - if (float64_is_infinity(a) || float64_is_infinity(b)) { - ret = infinite_float64(a_sign ^ b_sign); - return ret; - } - g_assert_not_reached(); -} - -static float32 special_fmaf(float32 a, float32 b, float32 c, - float_status *fp_status) -{ - float64 aa, bb, cc; - aa = float32_to_float64(a, fp_status); - bb = float32_to_float64(b, fp_status); - cc = float32_to_float64(c, fp_status); - return float64_to_float32(special_fma(aa, bb, cc, fp_status), fp_status); -} - -float32 internal_fmafx(float32 a, float32 b, float32 c, int scale, - float_status *fp_status) -{ - Accum prod; - Accum acc; - Accum result; - accum_init(&prod); - accum_init(&acc); - accum_init(&result); - - uint8_t a_sign = float32_is_neg(a); - uint8_t b_sign = float32_is_neg(b); - uint8_t c_sign = float32_is_neg(c); - if (float32_is_infinity(a) || - float32_is_infinity(b) || - float32_is_infinity(c)) { - return special_fmaf(a, b, c, fp_status); - } - if (float32_is_any_nan(a) || - float32_is_any_nan(b) || - float32_is_any_nan(c)) { - return special_fmaf(a, b, c, fp_status); - } - if ((scale == 0) && (float32_is_zero(a) || float32_is_zero(b))) { - float32 tmp = float32_mul(a, b, fp_status); - tmp = float32_add(tmp, c, fp_status); - return tmp; - } - - /* (a * 2**b) * (c * 2**d) == a*c * 2**(b+d) */ - prod.mant = int128_mul_6464(float32_getmant(a), float32_getmant(b)); - - /* - * Note: extracting the mantissa into an int is multiplying by - * 2**23, so adjust here - */ - prod.exp = float32_getexp(a) + float32_getexp(b) - SF_BIAS - 23; - prod.sign = a_sign ^ b_sign; - if (float32_is_zero(a) || float32_is_zero(b)) { - prod.exp = -2 * WAY_BIG_EXP; - } - if ((scale > 0) && float32_is_denormal(c)) { - acc.mant = int128_mul_6464(0, 0); - acc.exp = -WAY_BIG_EXP; - acc.sign = c_sign; - acc.sticky = 1; - result = accum_add(prod, acc); - } else if (!float32_is_zero(c)) { - acc.mant = int128_mul_6464(float32_getmant(c), 1); - acc.exp = float32_getexp(c); - acc.sign = c_sign; - result = accum_add(prod, acc); - } else { - result = prod; - } - result.exp += scale; - return accum_round_float32(result, fp_status); -} float64 internal_mpyhh(float64 a, float64 b, unsigned long long int accumulated, diff --git a/target/hexagon/fma_emu.h b/target/hexagon/fma_emu.h index ad5df5d038..fed054b609 100644 --- a/target/hexagon/fma_emu.h +++ b/target/hexagon/fma_emu.h @@ -30,8 +30,6 @@ static inline uint32_t float32_getexp_raw(float32 f32) } int32_t float32_getexp(float32 f32); float32 infinite_float32(uint8_t sign); -float32 internal_fmafx(float32 a, float32 b, float32 c, - int scale, float_status *fp_status); float64 internal_mpyhh(float64 a, float64 b, unsigned long long int accumulated, float_status *fp_status); From 795d6a2c4960325c514323147e13a22d5fe21ddf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 15:19:19 -0600 Subject: [PATCH 0683/2892] target/hexagon: Expand GEN_XF_ROUND This massive macro is now only used once. Expand it for use only by float64. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 255 +++++++++++++++++++-------------------- 1 file changed, 127 insertions(+), 128 deletions(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index 0c7c7f636c..0769de43de 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -354,136 +354,135 @@ float32 infinite_float32(uint8_t sign) } /* Return a maximum finite value with the requested sign */ -#define GEN_XF_ROUND(SUFFIX, MANTBITS, INF_EXP, INTERNAL_TYPE) \ -static SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \ -{ \ - if ((int128_gethi(a.mant) == 0) && (int128_getlo(a.mant) == 0) \ - && ((a.guard | a.round | a.sticky) == 0)) { \ - /* result zero */ \ - switch (fp_status->float_rounding_mode) { \ - case float_round_down: \ - return zero_##SUFFIX(1); \ - default: \ - return zero_##SUFFIX(0); \ - } \ - } \ - /* Normalize right */ \ - /* We want MANTBITS bits of mantissa plus the leading one. */ \ - /* That means that we want MANTBITS+1 bits, or 0x000000000000FF_FFFF */ \ - /* So we need to normalize right while the high word is non-zero and \ - * while the low word is nonzero when masked with 0xffe0_0000_0000_0000 */ \ - while ((int128_gethi(a.mant) != 0) || \ - ((int128_getlo(a.mant) >> (MANTBITS + 1)) != 0)) { \ - a = accum_norm_right(a, 1); \ - } \ - /* \ - * OK, now normalize left \ - * We want to normalize left until we have a leading one in bit 24 \ - * Theoretically, we only need to shift a maximum of one to the left if we \ - * shifted out lots of bits from B, or if we had no shift / 1 shift sticky \ - * should be 0 \ - */ \ - while ((int128_getlo(a.mant) & (1ULL << MANTBITS)) == 0) { \ - a = accum_norm_left(a); \ - } \ - /* \ - * OK, now we might need to denormalize because of potential underflow. \ - * We need to do this before rounding, and rounding might make us normal \ - * again \ - */ \ - while (a.exp <= 0) { \ - a = accum_norm_right(a, 1 - a.exp); \ - /* \ - * Do we have underflow? \ - * That's when we get an inexact answer because we ran out of bits \ - * in a denormal. \ - */ \ - if (a.guard || a.round || a.sticky) { \ - float_raise(float_flag_underflow, fp_status); \ - } \ - } \ - /* OK, we're relatively canonical... now we need to round */ \ - if (a.guard || a.round || a.sticky) { \ - float_raise(float_flag_inexact, fp_status); \ - switch (fp_status->float_rounding_mode) { \ - case float_round_to_zero: \ - /* Chop and we're done */ \ - break; \ - case float_round_up: \ - if (a.sign == 0) { \ - a.mant = int128_add(a.mant, int128_one()); \ - } \ - break; \ - case float_round_down: \ - if (a.sign != 0) { \ - a.mant = int128_add(a.mant, int128_one()); \ - } \ - break; \ - default: \ - if (a.round || a.sticky) { \ - /* round up if guard is 1, down if guard is zero */ \ - a.mant = int128_add(a.mant, int128_make64(a.guard)); \ - } else if (a.guard) { \ - /* exactly .5, round up if odd */ \ - a.mant = int128_add(a.mant, int128_and(a.mant, int128_one())); \ - } \ - break; \ - } \ - } \ - /* \ - * OK, now we might have carried all the way up. \ - * So we might need to shr once \ - * at least we know that the lsb should be zero if we rounded and \ - * got a carry out... \ - */ \ - if ((int128_getlo(a.mant) >> (MANTBITS + 1)) != 0) { \ - a = accum_norm_right(a, 1); \ - } \ - /* Overflow? */ \ - if (a.exp >= INF_EXP) { \ - /* Yep, inf result */ \ - float_raise(float_flag_overflow, fp_status); \ - float_raise(float_flag_inexact, fp_status); \ - switch (fp_status->float_rounding_mode) { \ - case float_round_to_zero: \ - return maxfinite_##SUFFIX(a.sign); \ - case float_round_up: \ - if (a.sign == 0) { \ - return infinite_##SUFFIX(a.sign); \ - } else { \ - return maxfinite_##SUFFIX(a.sign); \ - } \ - case float_round_down: \ - if (a.sign != 0) { \ - return infinite_##SUFFIX(a.sign); \ - } else { \ - return maxfinite_##SUFFIX(a.sign); \ - } \ - default: \ - return infinite_##SUFFIX(a.sign); \ - } \ - } \ - /* Underflow? */ \ - if (int128_getlo(a.mant) & (1ULL << MANTBITS)) { \ - /* Leading one means: No, we're normal. So, we should be done... */ \ - INTERNAL_TYPE ret; \ - ret.i = 0; \ - ret.sign = a.sign; \ - ret.exp = a.exp; \ - ret.mant = int128_getlo(a.mant); \ - return ret.i; \ - } \ - assert(a.exp == 1); \ - INTERNAL_TYPE ret; \ - ret.i = 0; \ - ret.sign = a.sign; \ - ret.exp = 0; \ - ret.mant = int128_getlo(a.mant); \ - return ret.i; \ +static float64 accum_round_float64(Accum a, float_status *fp_status) +{ + if ((int128_gethi(a.mant) == 0) && (int128_getlo(a.mant) == 0) + && ((a.guard | a.round | a.sticky) == 0)) { + /* result zero */ + switch (fp_status->float_rounding_mode) { + case float_round_down: + return zero_float64(1); + default: + return zero_float64(0); + } + } + /* + * Normalize right + * We want DF_MANTBITS bits of mantissa plus the leading one. + * That means that we want DF_MANTBITS+1 bits, or 0x000000000000FF_FFFF + * So we need to normalize right while the high word is non-zero and + * while the low word is nonzero when masked with 0xffe0_0000_0000_0000 + */ + while ((int128_gethi(a.mant) != 0) || + ((int128_getlo(a.mant) >> (DF_MANTBITS + 1)) != 0)) { + a = accum_norm_right(a, 1); + } + /* + * OK, now normalize left + * We want to normalize left until we have a leading one in bit 24 + * Theoretically, we only need to shift a maximum of one to the left if we + * shifted out lots of bits from B, or if we had no shift / 1 shift sticky + * should be 0 + */ + while ((int128_getlo(a.mant) & (1ULL << DF_MANTBITS)) == 0) { + a = accum_norm_left(a); + } + /* + * OK, now we might need to denormalize because of potential underflow. + * We need to do this before rounding, and rounding might make us normal + * again + */ + while (a.exp <= 0) { + a = accum_norm_right(a, 1 - a.exp); + /* + * Do we have underflow? + * That's when we get an inexact answer because we ran out of bits + * in a denormal. + */ + if (a.guard || a.round || a.sticky) { + float_raise(float_flag_underflow, fp_status); + } + } + /* OK, we're relatively canonical... now we need to round */ + if (a.guard || a.round || a.sticky) { + float_raise(float_flag_inexact, fp_status); + switch (fp_status->float_rounding_mode) { + case float_round_to_zero: + /* Chop and we're done */ + break; + case float_round_up: + if (a.sign == 0) { + a.mant = int128_add(a.mant, int128_one()); + } + break; + case float_round_down: + if (a.sign != 0) { + a.mant = int128_add(a.mant, int128_one()); + } + break; + default: + if (a.round || a.sticky) { + /* round up if guard is 1, down if guard is zero */ + a.mant = int128_add(a.mant, int128_make64(a.guard)); + } else if (a.guard) { + /* exactly .5, round up if odd */ + a.mant = int128_add(a.mant, int128_and(a.mant, int128_one())); + } + break; + } + } + /* + * OK, now we might have carried all the way up. + * So we might need to shr once + * at least we know that the lsb should be zero if we rounded and + * got a carry out... + */ + if ((int128_getlo(a.mant) >> (DF_MANTBITS + 1)) != 0) { + a = accum_norm_right(a, 1); + } + /* Overflow? */ + if (a.exp >= DF_INF_EXP) { + /* Yep, inf result */ + float_raise(float_flag_overflow, fp_status); + float_raise(float_flag_inexact, fp_status); + switch (fp_status->float_rounding_mode) { + case float_round_to_zero: + return maxfinite_float64(a.sign); + case float_round_up: + if (a.sign == 0) { + return infinite_float64(a.sign); + } else { + return maxfinite_float64(a.sign); + } + case float_round_down: + if (a.sign != 0) { + return infinite_float64(a.sign); + } else { + return maxfinite_float64(a.sign); + } + default: + return infinite_float64(a.sign); + } + } + /* Underflow? */ + if (int128_getlo(a.mant) & (1ULL << DF_MANTBITS)) { + /* Leading one means: No, we're normal. So, we should be done... */ + Double ret; + ret.i = 0; + ret.sign = a.sign; + ret.exp = a.exp; + ret.mant = int128_getlo(a.mant); + return ret.i; + } + assert(a.exp == 1); + Double ret; + ret.i = 0; + ret.sign = a.sign; + ret.exp = 0; + ret.mant = int128_getlo(a.mant); + return ret.i; } -GEN_XF_ROUND(float64, DF_MANTBITS, DF_INF_EXP, Double) - float64 internal_mpyhh(float64 a, float64 b, unsigned long long int accumulated, float_status *fp_status) From fefc9702e618cef00d199e6ddd43f4b2d4c2fad6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 15:23:36 -0600 Subject: [PATCH 0684/2892] target/hexagon: Remove Float This structure, with bitfields, is incorrect for big-endian. Use the existing float32_getexp_raw which uses extract32. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index 0769de43de..2a8f72fee3 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -53,16 +53,6 @@ typedef union { }; } Double; -typedef union { - float f; - uint32_t i; - struct { - uint32_t mant:23; - uint32_t exp:8; - uint32_t sign:1; - }; -} Float; - static uint64_t float64_getmant(float64 f64) { Double a = { .i = f64 }; @@ -92,12 +82,12 @@ int32_t float64_getexp(float64 f64) int32_t float32_getexp(float32 f32) { - Float a = { .i = f32 }; + int exp = float32_getexp_raw(f32); if (float32_is_normal(f32)) { - return a.exp; + return exp; } if (float32_is_denormal(f32)) { - return a.exp + 1; + return exp + 1; } return -1; } From 8429306c327e59786d96497bd6c36c42ccf58a06 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 15:34:33 -0600 Subject: [PATCH 0685/2892] target/hexagon: Remove Double This structure, with bitfields, is incorrect for big-endian. Use extract64 and deposit64 instead. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 46 ++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index 2a8f72fee3..ddc793fe14 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -43,39 +43,29 @@ #define WAY_BIG_EXP 4096 -typedef union { - double f; - uint64_t i; - struct { - uint64_t mant:52; - uint64_t exp:11; - uint64_t sign:1; - }; -} Double; - static uint64_t float64_getmant(float64 f64) { - Double a = { .i = f64 }; + uint64_t mant = extract64(f64, 0, 52); if (float64_is_normal(f64)) { - return a.mant | 1ULL << 52; + return mant | 1ULL << 52; } if (float64_is_zero(f64)) { return 0; } if (float64_is_denormal(f64)) { - return a.mant; + return mant; } return ~0ULL; } int32_t float64_getexp(float64 f64) { - Double a = { .i = f64 }; + int exp = extract64(f64, 52, 11); if (float64_is_normal(f64)) { - return a.exp; + return exp; } if (float64_is_denormal(f64)) { - return a.exp + 1; + return exp + 1; } return -1; } @@ -346,6 +336,8 @@ float32 infinite_float32(uint8_t sign) /* Return a maximum finite value with the requested sign */ static float64 accum_round_float64(Accum a, float_status *fp_status) { + uint64_t ret; + if ((int128_gethi(a.mant) == 0) && (int128_getlo(a.mant) == 0) && ((a.guard | a.round | a.sticky) == 0)) { /* result zero */ @@ -455,22 +447,16 @@ static float64 accum_round_float64(Accum a, float_status *fp_status) } } /* Underflow? */ - if (int128_getlo(a.mant) & (1ULL << DF_MANTBITS)) { + ret = int128_getlo(a.mant); + if (ret & (1ULL << DF_MANTBITS)) { /* Leading one means: No, we're normal. So, we should be done... */ - Double ret; - ret.i = 0; - ret.sign = a.sign; - ret.exp = a.exp; - ret.mant = int128_getlo(a.mant); - return ret.i; + ret = deposit64(ret, 52, 11, a.exp); + } else { + assert(a.exp == 1); + ret = deposit64(ret, 52, 11, 0); } - assert(a.exp == 1); - Double ret; - ret.i = 0; - ret.sign = a.sign; - ret.exp = 0; - ret.mant = int128_getlo(a.mant); - return ret.i; + ret = deposit64(ret, 63, 1, a.sign); + return ret; } float64 internal_mpyhh(float64 a, float64 b, From 65b4dce393cddb5fb0295bf6666f7db8512b8cff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 16:11:42 -0600 Subject: [PATCH 0686/2892] target/hexagon: Use mulu64 for int128_mul_6464 No need to open-code 64x64->128-bit multiplication. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index ddc793fe14..07d2880776 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -82,38 +82,12 @@ int32_t float32_getexp(float32 f32) return -1; } -static uint32_t int128_getw0(Int128 x) -{ - return int128_getlo(x); -} - -static uint32_t int128_getw1(Int128 x) -{ - return int128_getlo(x) >> 32; -} - static Int128 int128_mul_6464(uint64_t ai, uint64_t bi) { - Int128 a, b; - uint64_t pp0, pp1a, pp1b, pp1s, pp2; + uint64_t l, h; - a = int128_make64(ai); - b = int128_make64(bi); - pp0 = (uint64_t)int128_getw0(a) * (uint64_t)int128_getw0(b); - pp1a = (uint64_t)int128_getw1(a) * (uint64_t)int128_getw0(b); - pp1b = (uint64_t)int128_getw1(b) * (uint64_t)int128_getw0(a); - pp2 = (uint64_t)int128_getw1(a) * (uint64_t)int128_getw1(b); - - pp1s = pp1a + pp1b; - if ((pp1s < pp1a) || (pp1s < pp1b)) { - pp2 += (1ULL << 32); - } - uint64_t ret_low = pp0 + (pp1s << 32); - if ((ret_low < pp0) || (ret_low < (pp1s << 32))) { - pp2 += 1; - } - - return int128_make128(ret_low, pp2 + (pp1s >> 32)); + mulu64(&l, &h, ai, bi); + return int128_make128(l, h); } static Int128 int128_sub_borrow(Int128 a, Int128 b, int borrow) From 59abfb444e1d9654e15f85c50d09a3366e4c1c1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 8 Dec 2024 16:15:30 -0600 Subject: [PATCH 0687/2892] target/hexagon: Simplify internal_mpyhh setup Initialize x with accumulated via direct assignment, rather than multiplying by 1. Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/fma_emu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index 07d2880776..c557141f11 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -455,7 +455,7 @@ float64 internal_mpyhh(float64 a, float64 b, float64_is_infinity(b)) { return float64_mul(a, b, fp_status); } - x.mant = int128_mul_6464(accumulated, 1); + x.mant = int128_make64(accumulated); x.sticky = sticky; prod = fGETUWORD(1, float64_getmant(a)) * fGETUWORD(1, float64_getmant(b)); x.mant = int128_add(x.mant, int128_mul_6464(prod, 0x100000000ULL)); From e4a8e093dc74be049f4829831dce76e5edab0003 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 21 Dec 2024 16:50:26 +0000 Subject: [PATCH 0688/2892] accel/tcg: Move gen_intermediate_code to TCGCPUOps.translate_core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert all targets simultaneously, as the gen_intermediate_code function disappears from the target. While there are possible workarounds, they're larger than simply performing the conversion. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 8 +++++--- accel/tcg/translate-all.c | 8 +++++--- include/exec/translator.h | 14 -------------- include/hw/core/tcg-cpu-ops.h | 13 +++++++++++++ target/alpha/cpu.c | 1 + target/alpha/cpu.h | 2 ++ target/alpha/translate.c | 4 ++-- target/arm/cpu.c | 1 + target/arm/internals.h | 2 ++ target/arm/tcg/cpu-v7m.c | 1 + target/arm/tcg/translate.c | 5 ++--- target/avr/cpu.c | 1 + target/avr/cpu.h | 2 ++ target/avr/translate.c | 6 +++--- target/hexagon/cpu.c | 1 + target/hexagon/cpu.h | 2 ++ target/hexagon/translate.c | 4 ++-- target/hppa/cpu.c | 1 + target/hppa/cpu.h | 2 ++ target/hppa/translate.c | 4 ++-- target/i386/tcg/helper-tcg.h | 2 ++ target/i386/tcg/tcg-cpu.c | 1 + target/i386/tcg/translate.c | 5 ++--- target/loongarch/cpu.c | 1 + target/loongarch/internals.h | 2 ++ target/loongarch/tcg/translate.c | 4 ++-- target/m68k/cpu.c | 1 + target/m68k/cpu.h | 2 ++ target/m68k/translate.c | 4 ++-- target/microblaze/cpu.c | 1 + target/microblaze/cpu.h | 2 ++ target/microblaze/translate.c | 4 ++-- target/mips/cpu.c | 1 + target/mips/tcg/tcg-internal.h | 2 ++ target/mips/tcg/translate.c | 4 ++-- target/openrisc/cpu.c | 1 + target/openrisc/cpu.h | 2 ++ target/openrisc/translate.c | 4 ++-- target/ppc/cpu.h | 2 ++ target/ppc/cpu_init.c | 1 + target/ppc/translate.c | 4 ++-- target/riscv/cpu.h | 3 +++ target/riscv/tcg/tcg-cpu.c | 1 + target/riscv/translate.c | 4 ++-- target/rx/cpu.c | 1 + target/rx/cpu.h | 2 ++ target/rx/translate.c | 4 ++-- target/s390x/cpu.c | 1 + target/s390x/s390x-internal.h | 2 ++ target/s390x/tcg/translate.c | 4 ++-- target/sh4/cpu.c | 1 + target/sh4/cpu.h | 2 ++ target/sh4/translate.c | 4 ++-- target/sparc/cpu.c | 1 + target/sparc/cpu.h | 2 ++ target/sparc/translate.c | 4 ++-- target/tricore/cpu.c | 1 + target/tricore/cpu.h | 2 ++ target/tricore/translate.c | 5 ++--- target/xtensa/cpu.c | 1 + target/xtensa/cpu.h | 2 ++ target/xtensa/translate.c | 4 ++-- 62 files changed, 121 insertions(+), 62 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index b507049ddb..d48b82a932 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -1088,11 +1088,13 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) if (!tcg_target_initialized) { /* Check mandatory TCGCPUOps handlers */ + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; #ifndef CONFIG_USER_ONLY - assert(cpu->cc->tcg_ops->cpu_exec_halt); - assert(cpu->cc->tcg_ops->cpu_exec_interrupt); + assert(tcg_ops->cpu_exec_halt); + assert(tcg_ops->cpu_exec_interrupt); #endif /* !CONFIG_USER_ONLY */ - cpu->cc->tcg_ops->initialize(); + assert(tcg_ops->translate_code); + tcg_ops->initialize(); tcg_target_initialized = true; } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 572a8a8797..453eb20ec9 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -276,8 +276,10 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, tcg_func_start(tcg_ctx); - tcg_ctx->cpu = env_cpu(env); - gen_intermediate_code(env_cpu(env), tb, max_insns, pc, host_pc); + CPUState *cs = env_cpu(env); + tcg_ctx->cpu = cs; + cs->cc->tcg_ops->translate_code(cs, tb, max_insns, pc, host_pc); + assert(tb->size != 0); tcg_ctx->cpu = NULL; *max_insns = tb->icount; @@ -364,7 +366,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, /* * Overflow of code_gen_buffer, or the current slice of it. * - * TODO: We don't need to re-do gen_intermediate_code, nor + * TODO: We don't need to re-do tcg_ops->translate_code, nor * should we re-do the tcg optimization currently hidden * inside tcg_gen_code. All that should be required is to * flush the TBs, allocate a new TB, re-initialize it per diff --git a/include/exec/translator.h b/include/exec/translator.h index 41e2a41180..d70942a10f 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -21,20 +21,6 @@ #include "qemu/bswap.h" #include "exec/vaddr.h" -/** - * gen_intermediate_code - * @cpu: cpu context - * @tb: translation block - * @max_insns: max number of instructions to translate - * @pc: guest virtual program counter address - * @host_pc: host physical program counter address - * - * This function must be provided by the target, which should create - * the target-specific DisasContext, and then invoke translator_loop. - */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc); - /** * DisasJumpType: * @DISAS_NEXT: Next instruction in program order. diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 663efb9133..2e3f1690f1 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -24,6 +24,19 @@ struct TCGCPUOps { * Called when the first CPU is realized. */ void (*initialize)(void); + /** + * @translate_code: Translate guest instructions to TCGOps + * @cpu: cpu context + * @tb: translation block + * @max_insns: max number of instructions to translate + * @pc: guest virtual program counter address + * @host_pc: host physical program counter address + * + * This function must be provided by the target, which should create + * the target-specific DisasContext, and then invoke translator_loop. + */ + void (*translate_code)(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); /** * @synchronize_from_tb: Synchronize state from a TCG #TranslationBlock * diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 9fa506bff9..e1b898e575 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -224,6 +224,7 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { static const TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, + .translate_code = alpha_translate_code, .synchronize_from_tb = alpha_cpu_synchronize_from_tb, .restore_state_to_opc = alpha_restore_state_to_opc, diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 3556d3227f..80562adfb5 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -431,6 +431,8 @@ enum { }; void alpha_translate_init(void); +void alpha_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 629ff3cde9..2156c02214 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -2955,8 +2955,8 @@ static const TranslatorOps alpha_tr_ops = { .tb_stop = alpha_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void alpha_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; translator_loop(cpu, tb, max_insns, pc, host_pc, &alpha_tr_ops, &dc.base); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 019183c9ea..dcedadc89e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2682,6 +2682,7 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #ifdef CONFIG_TCG static const TCGCPUOps arm_tcg_ops = { .initialize = arm_translate_init, + .translate_code = arm_translate_code, .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, diff --git a/target/arm/internals.h b/target/arm/internals.h index c3a5b1385f..863a84edf8 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -357,6 +357,8 @@ void init_cpreg_list(ARMCPU *cpu); void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); +void arm_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void arm_cpu_register_gdb_commands(ARMCPU *cpu); void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 58e54578d6..03acdf83e0 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -234,6 +234,7 @@ static void cortex_m55_initfn(Object *obj) static const TCGCPUOps arm_v7m_tcg_ops = { .initialize = arm_translate_init, + .translate_code = arm_translate_code, .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 9ee761fc64..c16b59ab88 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -8093,9 +8093,8 @@ static const TranslatorOps thumb_translator_ops = { .tb_stop = arm_tr_tb_stop, }; -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void arm_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = { }; const TranslatorOps *ops = &arm_translator_ops; diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 2dccb09c5e..8a126ff322 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -207,6 +207,7 @@ static const struct SysemuCPUOps avr_sysemu_ops = { static const TCGCPUOps avr_tcg_ops = { .initialize = avr_cpu_tcg_init, + .translate_code = avr_cpu_translate_code, .synchronize_from_tb = avr_cpu_synchronize_from_tb, .restore_state_to_opc = avr_restore_state_to_opc, .cpu_exec_interrupt = avr_cpu_exec_interrupt, diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 4725535102..06f5ae4d1b 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -183,6 +183,8 @@ static inline void set_avr_feature(CPUAVRState *env, int feature) } void avr_cpu_tcg_init(void); +void avr_cpu_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); int cpu_avr_exec(CPUState *cpu); diff --git a/target/avr/translate.c b/target/avr/translate.c index f13b997f8d..4ab71d8138 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -2599,7 +2599,7 @@ static bool trans_WDR(DisasContext *ctx, arg_WDR *a) * * - translate() * - canonicalize_skip() - * - gen_intermediate_code() + * - translate_code() * - restore_state_to_opc() * */ @@ -2795,8 +2795,8 @@ static const TranslatorOps avr_tr_ops = { .tb_stop = avr_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void avr_cpu_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = { }; translator_loop(cs, tb, max_insns, pc, host_pc, &avr_tr_ops, &dc.base); diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index c9817c7192..0b7fc98f6c 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -325,6 +325,7 @@ static void hexagon_cpu_init(Object *obj) static const TCGCPUOps hexagon_tcg_ops = { .initialize = hexagon_translate_init, + .translate_code = hexagon_translate_code, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, }; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 14e6e819c2..79e60d4bfa 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -150,6 +150,8 @@ static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, typedef HexagonCPU ArchCPU; void hexagon_translate_init(void); +void hexagon_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #include "exec/cpu-all.h" diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 562105705a..fe7858703c 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1026,8 +1026,8 @@ static const TranslatorOps hexagon_tr_ops = { .tb_stop = hexagon_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void hexagon_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index c9062e60b6..47d0160955 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -223,6 +223,7 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { static const TCGCPUOps hppa_tcg_ops = { .initialize = hppa_translate_init, + .translate_code = hppa_translate_code, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, .restore_state_to_opc = hppa_restore_state_to_opc, diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index e45ba50a59..22a6510e08 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -303,6 +303,8 @@ static inline int HPPA_BTLB_ENTRIES(CPUHPPAState *env) } void hppa_translate_init(void); +void hppa_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU diff --git a/target/hppa/translate.c b/target/hppa/translate.c index d13f80fe3e..dc04f9f3c0 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4869,8 +4869,8 @@ static const TranslatorOps hppa_tr_ops = { #endif }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void hppa_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx = { }; translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base); diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 696d6ef016..54d845379c 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -59,6 +59,8 @@ static inline target_long lshift(target_long x, int n) /* translate.c */ void tcg_x86_init(void); +void x86_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); /* excp_helper.c */ G_NORETURN void raise_exception(CPUX86State *env, int exception_index); diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 231ecac37d..14ee038079 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -109,6 +109,7 @@ static bool x86_debug_check_breakpoint(CPUState *cs) static const TCGCPUOps x86_tcg_ops = { .initialize = tcg_x86_init, + .translate_code = x86_translate_code, .synchronize_from_tb = x86_cpu_synchronize_from_tb, .restore_state_to_opc = x86_restore_state_to_opc, .cpu_exec_enter = x86_cpu_exec_enter, diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 903553dc88..834aea1e59 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3814,9 +3814,8 @@ static const TranslatorOps i386_tr_ops = { .tb_stop = i386_tr_tb_stop, }; -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void x86_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index f5bc8720d1..58415ffe99 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -795,6 +795,7 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) static const TCGCPUOps loongarch_tcg_ops = { .initialize = loongarch_translate_init, + .translate_code = loongarch_translate_code, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, .restore_state_to_opc = loongarch_restore_state_to_opc, diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 0655ac948b..ad9cf4fc7a 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -17,6 +17,8 @@ #define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS) void loongarch_translate_init(void); +void loongarch_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void G_NORETURN do_raise_exception(CPULoongArchState *env, uint32_t exception, diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index 1fca4afc73..68be999410 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -333,8 +333,8 @@ static const TranslatorOps loongarch_tr_ops = { .tb_stop = loongarch_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void loongarch_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 9de8ce6707..41dfdf5804 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -551,6 +551,7 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { static const TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, + .translate_code = m68k_translate_code, .restore_state_to_opc = m68k_restore_state_to_opc, #ifndef CONFIG_USER_ONLY diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index b5bbeedb7a..ddb0f29f4a 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -193,6 +193,8 @@ int m68k_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void m68k_tcg_init(void); +void m68k_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void m68k_cpu_init_gdb(M68kCPU *cpu); uint32_t cpu_m68k_get_ccr(CPUM68KState *env); void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 077151c62d..dec2967fce 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -6118,8 +6118,8 @@ static const TranslatorOps m68k_tr_ops = { .tb_stop = m68k_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void m68k_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; translator_loop(cpu, tb, max_insns, pc, host_pc, &m68k_tr_ops, &dc.base); diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index eba819378d..f114789abd 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -423,6 +423,7 @@ static const struct SysemuCPUOps mb_sysemu_ops = { static const TCGCPUOps mb_tcg_ops = { .initialize = mb_tcg_init, + .translate_code = mb_translate_code, .synchronize_from_tb = mb_cpu_synchronize_from_tb, .restore_state_to_opc = mb_restore_state_to_opc, diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 3e5a3e5c60..f6879eee35 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -398,6 +398,8 @@ static inline void mb_cpu_write_msr(CPUMBState *env, uint32_t val) } void mb_tcg_init(void); +void mb_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #define CPU_RESOLVING_TYPE TYPE_MICROBLAZE_CPU diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index d53995c26d..24005f05b2 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1779,8 +1779,8 @@ static const TranslatorOps mb_tr_ops = { .tb_stop = mb_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void mb_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; translator_loop(cpu, tb, max_insns, pc, host_pc, &mb_tr_ops, &dc.base); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 1b0cf6df9c..e3af02a4e6 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -547,6 +547,7 @@ static const Property mips_cpu_properties[] = { #include "hw/core/tcg-cpu-ops.h" static const TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, + .translate_code = mips_translate_code, .synchronize_from_tb = mips_cpu_synchronize_from_tb, .restore_state_to_opc = mips_restore_state_to_opc, diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index aef032c48d..74fc1309a7 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -16,6 +16,8 @@ #include "cpu.h" void mips_tcg_init(void); +void mips_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); G_NORETURN void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index bd1ef4e1fc..78b848a6d9 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -15231,8 +15231,8 @@ static const TranslatorOps mips_tr_ops = { .tb_stop = mips_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void mips_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 7913a0c3e1..b7bab0d7ab 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -236,6 +236,7 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { static const TCGCPUOps openrisc_tcg_ops = { .initialize = openrisc_translate_init, + .translate_code = openrisc_translate_code, .synchronize_from_tb = openrisc_cpu_synchronize_from_tb, .restore_state_to_opc = openrisc_restore_state_to_opc, diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index c9fe9ae12d..b97d2ffdd2 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -301,6 +301,8 @@ void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); +void openrisc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); int print_insn_or1k(bfd_vma addr, disassemble_info *info); #ifndef CONFIG_USER_ONLY diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 028ba66631..7a6af183ae 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1646,8 +1646,8 @@ static const TranslatorOps openrisc_tr_ops = { .tb_stop = openrisc_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void openrisc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2ffac2ed03..0b8b4c0517 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1581,6 +1581,8 @@ extern const VMStateDescription vmstate_ppc_cpu; /*****************************************************************************/ void ppc_translate_init(void); +void ppc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1(CPUPPCState *env, target_ulong value); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 0fcef630f1..c05c2dc42d 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7431,6 +7431,7 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { static const TCGCPUOps ppc_tcg_ops = { .initialize = ppc_translate_init, + .translate_code = ppc_translate_code, .restore_state_to_opc = ppc_restore_state_to_opc, #ifdef CONFIG_USER_ONLY diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 8ab87f42d6..80638ab535 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -6669,8 +6669,8 @@ static const TranslatorOps ppc_tr_ops = { .tb_stop = ppc_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void ppc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 284b112821..252fdb8672 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -602,6 +602,9 @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); void riscv_translate_init(void); +void riscv_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); + G_NORETURN void riscv_raise_exception(CPURISCVState *env, uint32_t exception, uintptr_t pc); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index f0129811fd..8b89c99c0f 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -135,6 +135,7 @@ static void riscv_restore_state_to_opc(CPUState *cs, static const TCGCPUOps riscv_tcg_ops = { .initialize = riscv_translate_init, + .translate_code = riscv_translate_code, .synchronize_from_tb = riscv_cpu_synchronize_from_tb, .restore_state_to_opc = riscv_restore_state_to_opc, diff --git a/target/riscv/translate.c b/target/riscv/translate.c index a76f67c5dd..a992d4f3c6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1346,8 +1346,8 @@ static const TranslatorOps riscv_tr_ops = { .tb_stop = riscv_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void riscv_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 558280c794..8c50c7a1bc 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -196,6 +196,7 @@ static const struct SysemuCPUOps rx_sysemu_ops = { static const TCGCPUOps rx_tcg_ops = { .initialize = rx_translate_init, + .translate_code = rx_translate_code, .synchronize_from_tb = rx_cpu_synchronize_from_tb, .restore_state_to_opc = rx_restore_state_to_opc, .tlb_fill = rx_cpu_tlb_fill, diff --git a/target/rx/cpu.h b/target/rx/cpu.h index c53593d7aa..5ba1874bd7 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -139,6 +139,8 @@ int rx_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int rx_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void rx_translate_init(void); +void rx_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); #include "exec/cpu-all.h" diff --git a/target/rx/translate.c b/target/rx/translate.c index 4f43654bad..bbda703be8 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -2258,8 +2258,8 @@ static const TranslatorOps rx_tr_ops = { .tb_stop = rx_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void rx_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 0a6847b027..97d41c23de 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -362,6 +362,7 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, + .translate_code = s390x_translate_code, .restore_state_to_opc = s390x_restore_state_to_opc, #ifdef CONFIG_USER_ONLY diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 4cc435042c..a750e7a343 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -399,6 +399,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, /* translate.c */ void s390x_translate_init(void); +void s390x_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void s390x_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data); diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 81554f2ad9..00073c5560 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6481,8 +6481,8 @@ static const TranslatorOps s390x_tr_ops = { .disas_log = s390x_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void s390x_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index e9d3e12a62..24a22724c6 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -251,6 +251,7 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { static const TCGCPUOps superh_tcg_ops = { .initialize = sh4_translate_init, + .translate_code = sh4_translate_code, .synchronize_from_tb = superh_cpu_synchronize_from_tb, .restore_state_to_opc = superh_restore_state_to_opc, diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index d928bcf006..d536d5d715 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -248,6 +248,8 @@ G_NORETURN void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, uintptr_t retaddr); void sh4_translate_init(void); +void sh4_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #if !defined(CONFIG_USER_ONLY) hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); diff --git a/target/sh4/translate.c b/target/sh4/translate.c index f076da9bac..bcdd558818 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2318,8 +2318,8 @@ static const TranslatorOps sh4_tr_ops = { .tb_stop = sh4_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void sh4_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 373a335c39..fbd38ec334 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -996,6 +996,7 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { static const TCGCPUOps sparc_tcg_ops = { .initialize = sparc_tcg_init, + .translate_code = sparc_translate_code, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, .restore_state_to_opc = sparc_restore_state_to_opc, diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 5c981234bb..dda811503b 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -609,6 +609,8 @@ int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr, /* translate.c */ void sparc_tcg_init(void); +void sparc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); /* fop_helper.c */ target_ulong cpu_get_fsr(CPUSPARCState *); diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 465e20f4f3..7e5c7351cb 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5819,8 +5819,8 @@ static const TranslatorOps sparc_tr_ops = { .tb_stop = sparc_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void sparc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = {}; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 95fb546666..95202fadbf 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -172,6 +172,7 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { static const TCGCPUOps tricore_tcg_ops = { .initialize = tricore_tcg_init, + .translate_code = tricore_translate_code, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, .restore_state_to_opc = tricore_restore_state_to_opc, .tlb_fill = tricore_cpu_tlb_fill, diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index 220af69fc2..8e431d7922 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -252,6 +252,8 @@ FIELD(TB_FLAGS, PRIV, 0, 2) void cpu_state_reset(CPUTriCoreState *s); void tricore_tcg_init(void); +void tricore_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 2b67395c09..0ef3743f3e 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -8460,9 +8460,8 @@ static const TranslatorOps tricore_tr_ops = { .tb_stop = tricore_tr_tb_stop, }; - -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void tricore_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; translator_loop(cs, tb, max_insns, pc, host_pc, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 0d4d79b58b..0910a3d290 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -232,6 +232,7 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { static const TCGCPUOps xtensa_tcg_ops = { .initialize = xtensa_translate_init, + .translate_code = xtensa_translate_code, .debug_excp_handler = xtensa_breakpoint_handler, .restore_state_to_opc = xtensa_restore_state_to_opc, diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 77e48eef19..0e6302c5bd 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -617,6 +617,8 @@ G_NORETURN void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, void xtensa_collect_sr_names(const XtensaConfig *config); void xtensa_translate_init(void); +void xtensa_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void **xtensa_get_regfile_by_name(const char *name, int entries, int bits); void xtensa_breakpoint_handler(CPUState *cs); void xtensa_register_core(XtensaConfigList *node); diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 3c62c99b4f..4f02cefde3 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1228,8 +1228,8 @@ static const TranslatorOps xtensa_translator_ops = { .tb_stop = xtensa_tr_tb_stop, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) +void xtensa_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = {}; translator_loop(cpu, tb, max_insns, pc, host_pc, From 03828b00a2bfa14fb70a423b811b57d463842622 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:39 +0800 Subject: [PATCH 0689/2892] vfio/igd: fix GTT stolen memory size calculation for gen 8+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On gen 8 and later devices, the GTT stolen memory size when GGMS equals 0 is 0 (no preallocated memory) rather than 1MB [1]. [1] 3.1.13, 5th Generation Intel Core Processor Family Datasheet Vol. 2 https://www.intel.com/content/www/us/en/content-details/330835 Fixes: c4c45e943e51 ("vfio/pci: Intel graphics legacy mode assignment") Reported-By: Alex Williamson Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-2-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 4047f4f071..73ed1ec8e6 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -268,7 +268,7 @@ static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6) { + if (gen > 6 && ggms != 0) { ggms = 1 << ggms; } @@ -678,7 +678,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) /* Determine the size of stolen memory needed for GTT */ ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6) { + if (gen > 6 && ggms_mb != 0) { ggms_mb = 1 << ggms_mb; } From 8492a6129c3c581cd6f94a0c11e9c4c5ebbac9dc Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:40 +0800 Subject: [PATCH 0690/2892] vfio/igd: remove unsupported device ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since e433f208973f ("vfio/igd: return an invalid generation for unknown devices"), the default return of igd_gen() was changed to unsupported. There is no need to filter out those unsupported devices. Reviewed-by: Alex Williamson Reviewed-by: Corvin Köhne Signed-off-by: Tomita Moeko Link: https://lore.kernel.org/r/20241206122749.9893-3-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 73ed1ec8e6..059ed56439 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -64,16 +64,6 @@ static int igd_gen(VFIOPCIDevice *vdev) } switch (vdev->device_id & 0xff00) { - /* Old, untested, unavailable, unknown */ - case 0x0000: - case 0x2500: - case 0x2700: - case 0x2900: - case 0x2a00: - case 0x2e00: - case 0x3500: - case 0xa000: - return -1; /* SandyBridge, IvyBridge, ValleyView, Haswell */ case 0x0100: case 0x0400: From 0bb758e97acec8c7bbd1c3d4d61ca20a050ae9b0 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:41 +0800 Subject: [PATCH 0691/2892] vfio/igd: align generation with i915 kernel driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the igd device generations according to i915 kernel driver to avoid confusion, and adjust comment placement to clearly reflect the relationship between ids and devices. The condition of how GTT stolen memory size is calculated is changed accordingly as GGMS is in multiple of 2 starting from gen 8. Reviewed-by: Corvin Köhne Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-4-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 059ed56439..09bd4e5383 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -59,33 +59,34 @@ */ static int igd_gen(VFIOPCIDevice *vdev) { - if ((vdev->device_id & 0xfff) == 0xa84) { - return 8; /* Broxton */ + /* + * Device IDs for Broxton/Apollo Lake are 0x0a84, 0x1a84, 0x1a85, 0x5a84 + * and 0x5a85, match bit 11:1 here + * Prefix 0x0a is taken by Haswell, this rule should be matched first. + */ + if ((vdev->device_id & 0xffe) == 0xa84) { + return 9; } switch (vdev->device_id & 0xff00) { - /* SandyBridge, IvyBridge, ValleyView, Haswell */ - case 0x0100: - case 0x0400: - case 0x0a00: - case 0x0c00: - case 0x0d00: - case 0x0f00: + case 0x0100: /* SandyBridge, IvyBridge */ return 6; - /* BroadWell, CherryView, SkyLake, KabyLake */ - case 0x1600: - case 0x1900: - case 0x2200: - case 0x5900: + case 0x0400: /* Haswell */ + case 0x0a00: /* Haswell */ + case 0x0c00: /* Haswell */ + case 0x0d00: /* Haswell */ + case 0x0f00: /* Valleyview/Bay Trail */ + return 7; + case 0x1600: /* Broadwell */ + case 0x2200: /* Cherryview */ return 8; - /* CoffeeLake */ - case 0x3e00: + case 0x1900: /* Skylake */ + case 0x5900: /* Kaby Lake */ + case 0x3e00: /* Coffee Lake */ return 9; - /* ElkhartLake */ - case 0x4500: + case 0x4500: /* Elkhart Lake */ return 11; - /* TigerLake */ - case 0x9A00: + case 0x9A00: /* Tiger Lake */ return 12; } @@ -258,7 +259,7 @@ static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6 && ggms != 0) { + if (gen >= 8 && ggms != 0) { ggms = 1 << ggms; } @@ -668,7 +669,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) /* Determine the size of stolen memory needed for GTT */ ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6 && ggms_mb != 0) { + if (gen >= 8 && ggms_mb != 0) { ggms_mb = 1 << ggms_mb; } From 1e1eac5f3dcd9836088d59ced0ba23337f49eb04 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:42 +0800 Subject: [PATCH 0692/2892] vfio/igd: canonicalize memory size calculations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper functions igd_gtt_memory_size() and igd_stolen_size() for calculating GTT stolen memory and Data stolen memory size in bytes, and use macros to replace the hardware-related magic numbers for better readability. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-5-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 101 ++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 09bd4e5383..e231865d72 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -107,6 +107,53 @@ typedef struct VFIOIGDQuirk { #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ #define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ +#define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */ +#define IGD_GMCH_GEN6_GMS_MASK 0x1f +#define IGD_GMCH_GEN6_GGMS_SHIFT 8 +#define IGD_GMCH_GEN6_GGMS_MASK 0x3 +#define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */ +#define IGD_GMCH_GEN8_GMS_MASK 0xff +#define IGD_GMCH_GEN8_GGMS_SHIFT 6 +#define IGD_GMCH_GEN8_GGMS_MASK 0x3 + +static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch) +{ + uint64_t ggms; + + if (gen < 8) { + ggms = (gmch >> IGD_GMCH_GEN6_GGMS_SHIFT) & IGD_GMCH_GEN6_GGMS_MASK; + } else { + ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK; + if (ggms != 0) { + ggms = 1 << ggms; + } + } + + return ggms * MiB; +} + +static uint64_t igd_stolen_memory_size(int gen, uint32_t gmch) +{ + uint64_t gms; + + if (gen < 8) { + gms = (gmch >> IGD_GMCH_GEN6_GMS_SHIFT) & IGD_GMCH_GEN6_GMS_MASK; + } else { + gms = (gmch >> IGD_GMCH_GEN8_GMS_SHIFT) & IGD_GMCH_GEN8_GMS_MASK; + } + + if (gen < 9) { + return gms * 32 * MiB; + } else { + if (gms < 0xf0) { + return gms * 32 * MiB; + } else { + return (gms - 0xf0 + 1) * 4 * MiB; + } + } + + return 0; +} /* * The rather short list of registers that we copy from the host devices. @@ -255,17 +302,10 @@ static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) { uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - int ggms, gen = igd_gen(vdev); + int gen = igd_gen(vdev); + uint64_t ggms_size = igd_gtt_memory_size(gen, gmch); - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen >= 8 && ggms != 0) { - ggms = 1 << ggms; - } - - ggms *= MiB; - - return (ggms / (4 * KiB)) * (gen < 8 ? 4 : 8); + return (ggms_size / (4 * KiB)) * (gen < 8 ? 4 : 8); } /* @@ -472,30 +512,6 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); } -static int igd_get_stolen_mb(int gen, uint32_t gmch) -{ - int gms; - - if (gen < 8) { - gms = (gmch >> 3) & 0x1f; - } else { - gms = (gmch >> 8) & 0xff; - } - - if (gen < 9) { - if (gms > 0x10) { - error_report("Unsupported IGD GMS value 0x%x", gms); - return 0; - } - return gms * 32; - } else { - if (gms < 0xf0) - return gms * 32; - else - return (gms - 0xf0) * 4 + 4; - } -} - void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) { g_autofree struct vfio_region_info *rom = NULL; @@ -505,7 +521,8 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) VFIOQuirk *quirk; VFIOIGDQuirk *igd; PCIDevice *lpc_bridge; - int i, ret, ggms_mb, gms_mb = 0, gen; + int i, ret, gen; + uint64_t ggms_size, gms_size; uint64_t *bdsm_size; uint32_t gmch; uint16_t cmd_orig, cmd; @@ -667,13 +684,8 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - /* Determine the size of stolen memory needed for GTT */ - ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen >= 8 && ggms_mb != 0) { - ggms_mb = 1 << ggms_mb; - } - - gms_mb = igd_get_stolen_mb(gen, gmch); + ggms_size = igd_gtt_memory_size(gen, gmch); + gms_size = igd_stolen_memory_size(gen, gmch); /* * Request reserved memory for stolen memory via fw_cfg. VM firmware @@ -684,7 +696,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * config offset 0x5C. */ bdsm_size = g_malloc(sizeof(*bdsm_size)); - *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB); + *bdsm_size = cpu_to_le64(ggms_size + gms_size); fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", bdsm_size, sizeof(*bdsm_size)); @@ -735,5 +747,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) vdev->vbasedev.name); } - trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); + trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, + (ggms_size + gms_size) / MiB); } From 183714d8f9406b3d0c6e3daeee2e00e6f4aec9bb Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:43 +0800 Subject: [PATCH 0693/2892] vfio/igd: add Gemini Lake and Comet Lake device ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both Gemini Lake and Comet Lake are gen 9 devices. Many user reports on internet shows legacy mode of igd passthrough works as qemu treats them as gen 8 devices by default before e433f208973f ("vfio/igd: return an invalid generation for unknown devices"). Reviewed-by: Corvin Köhne Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-6-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index e231865d72..ed236f443a 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -81,8 +81,10 @@ static int igd_gen(VFIOPCIDevice *vdev) case 0x2200: /* Cherryview */ return 8; case 0x1900: /* Skylake */ + case 0x3100: /* Gemini Lake */ case 0x5900: /* Kaby Lake */ case 0x3e00: /* Coffee Lake */ + case 0x9B00: /* Comet Lake */ return 9; case 0x4500: /* Elkhart Lake */ return 11; From 960f62770ae4c603f92317166495e4a59cf051fc Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:44 +0800 Subject: [PATCH 0694/2892] vfio/igd: add Alder/Raptor/Rocket/Ice/Jasper Lake device ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All gen 11 and 12 igd devices have 64 bit BDSM register at 0xC0 in its config space, add them to the list to support igd passthrough on Alder/ Raptor/Rocket/Ice/Jasper Lake platforms. Tested legacy mode of igd passthrough works properly on both linux and windows guests with AlderLake-S GT1 (8086:4680). Reviewed-by: Corvin Köhne Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-7-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index ed236f443a..49b6547776 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -86,9 +86,14 @@ static int igd_gen(VFIOPCIDevice *vdev) case 0x3e00: /* Coffee Lake */ case 0x9B00: /* Comet Lake */ return 9; + case 0x8A00: /* Ice Lake */ case 0x4500: /* Elkhart Lake */ + case 0x4E00: /* Jasper Lake */ return 11; case 0x9A00: /* Tiger Lake */ + case 0x4C00: /* Rocket Lake */ + case 0x4600: /* Alder Lake */ + case 0xA700: /* Raptor Lake */ return 12; } From 1a2623b5c9e7cb6c9cc69dd4b467c9cbb1c98877 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:45 +0800 Subject: [PATCH 0695/2892] vfio/igd: add macro for declaring mirrored registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit igd devices have multipe registers mirroring mmio address and pci config space, more than a single BDSM register. To support this, the read/write functions are made common and a macro is defined to simplify the declaration of MemoryRegionOps. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-8-tomitamoeko@gmail.com [ clg : Fixed conversion specifier on 32-bit platform ] Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 60 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 49b6547776..4e93af1f8d 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -421,16 +421,9 @@ static const MemoryRegionOps vfio_igd_index_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -#define IGD_BDSM_MMIO_OFFSET 0x1080C0 - -static uint64_t vfio_igd_quirk_bdsm_read(void *opaque, - hwaddr addr, unsigned size) +static uint64_t vfio_igd_pci_config_read(VFIOPCIDevice *vdev, uint64_t offset, + unsigned size) { - VFIOPCIDevice *vdev = opaque; - uint64_t offset; - - offset = IGD_BDSM_GEN11 + addr; - switch (size) { case 1: return pci_get_byte(vdev->pdev.config + offset); @@ -441,21 +434,17 @@ static uint64_t vfio_igd_quirk_bdsm_read(void *opaque, case 8: return pci_get_quad(vdev->pdev.config + offset); default: - hw_error("igd: unsupported read size, %u bytes", size); + hw_error("igd: unsupported pci config read at %"PRIx64", size %u", + offset, size); break; } return 0; } -static void vfio_igd_quirk_bdsm_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) +static void vfio_igd_pci_config_write(VFIOPCIDevice *vdev, uint64_t offset, + uint64_t data, unsigned size) { - VFIOPCIDevice *vdev = opaque; - uint64_t offset; - - offset = IGD_BDSM_GEN11 + addr; - switch (size) { case 1: pci_set_byte(vdev->pdev.config + offset, data); @@ -470,17 +459,39 @@ static void vfio_igd_quirk_bdsm_write(void *opaque, hwaddr addr, pci_set_quad(vdev->pdev.config + offset, data); break; default: - hw_error("igd: unsupported read size, %u bytes", size); + hw_error("igd: unsupported pci config write at %"PRIx64", size %u", + offset, size); break; } } -static const MemoryRegionOps vfio_igd_bdsm_quirk = { - .read = vfio_igd_quirk_bdsm_read, - .write = vfio_igd_quirk_bdsm_write, - .endianness = DEVICE_LITTLE_ENDIAN, +#define VFIO_IGD_QUIRK_MIRROR_REG(reg, name) \ +static uint64_t vfio_igd_quirk_read_##name(void *opaque, \ + hwaddr addr, unsigned size) \ +{ \ + VFIOPCIDevice *vdev = opaque; \ + \ + return vfio_igd_pci_config_read(vdev, reg + addr, size); \ +} \ + \ +static void vfio_igd_quirk_write_##name(void *opaque, hwaddr addr, \ + uint64_t data, unsigned size) \ +{ \ + VFIOPCIDevice *vdev = opaque; \ + \ + vfio_igd_pci_config_write(vdev, reg + addr, data, size); \ +} \ + \ +static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \ + .read = vfio_igd_quirk_read_##name, \ + .write = vfio_igd_quirk_write_##name, \ + .endianness = DEVICE_LITTLE_ENDIAN, \ }; +VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm) + +#define IGD_BDSM_MMIO_OFFSET 0x1080C0 + void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) { VFIOQuirk *quirk; @@ -510,8 +521,9 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) quirk = vfio_quirk_alloc(1); quirk->data = vdev; - memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_bdsm_quirk, - vdev, "vfio-igd-bdsm-quirk", 8); + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), + &vfio_igd_quirk_mirror_bdsm, vdev, + "vfio-igd-bdsm-quirk", 8); memory_region_add_subregion_overlap(vdev->bars[0].region.mem, IGD_BDSM_MMIO_OFFSET, &quirk->mem[0], 1); From ea652c2beeaec51035f76aeb976af06858ee85ce Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:46 +0800 Subject: [PATCH 0696/2892] vfio/igd: emulate GGC register in mmio bar0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GGC register at 0x50 of pci config space is a mirror of the same register at 0x108040 of mmio bar0 [1]. i915 driver also reads that register from mmio bar0 instead of config space. As GGC is programmed and emulated by qemu, the mmio address should also be emulated, in the same way of BDSM register. [1] 4.1.28, 12th Generation Intel Core Processors Datasheet Volume 2 https://www.intel.com/content/www/us/en/content-details/655259 Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-9-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 4e93af1f8d..828222cad1 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -488,8 +488,10 @@ static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \ .endianness = DEVICE_LITTLE_ENDIAN, \ }; +VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc) VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm) +#define IGD_GGC_MMIO_OFFSET 0x108040 #define IGD_BDSM_MMIO_OFFSET 0x1080C0 void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) @@ -518,14 +520,21 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = vfio_quirk_alloc(1); + quirk = vfio_quirk_alloc(2); quirk->data = vdev; memory_region_init_io(&quirk->mem[0], OBJECT(vdev), + &vfio_igd_quirk_mirror_ggc, vdev, + "vfio-igd-ggc-quirk", 2); + memory_region_add_subregion_overlap(vdev->bars[0].region.mem, + IGD_GGC_MMIO_OFFSET, &quirk->mem[0], + 1); + + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_quirk_mirror_bdsm, vdev, "vfio-igd-bdsm-quirk", 8); memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_BDSM_MMIO_OFFSET, &quirk->mem[0], + IGD_BDSM_MMIO_OFFSET, &quirk->mem[1], 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); From f926baa03b7babb8291ea4c1cbeadaf224977dae Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:47 +0800 Subject: [PATCH 0697/2892] vfio/igd: emulate BDSM in mmio bar0 for gen 6-10 devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A recent commit in i915 driver [1] claims the BDSM register at 0x1080c0 of mmio bar0 has been there since gen 6. Mirror this register to the 32 bit BDSM register at 0x5c in pci config space for gen6-10 devices. [1] https://patchwork.freedesktop.org/patch/msgid/20240202224340.30647-7-ville.syrjala@linux.intel.com Reviewed-by: Corvin Köhne Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-10-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 828222cad1..c15a14e445 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -489,7 +489,8 @@ static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \ }; VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc) -VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm) +VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM, bdsm) +VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm64) #define IGD_GGC_MMIO_OFFSET 0x108040 #define IGD_BDSM_MMIO_OFFSET 0x1080C0 @@ -516,7 +517,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) * into MMIO space and read from MMIO space by the Windows driver. */ gen = igd_gen(vdev); - if (gen < 11) { + if (gen < 6) { return; } @@ -530,12 +531,21 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) IGD_GGC_MMIO_OFFSET, &quirk->mem[0], 1); - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), - &vfio_igd_quirk_mirror_bdsm, vdev, - "vfio-igd-bdsm-quirk", 8); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_BDSM_MMIO_OFFSET, &quirk->mem[1], - 1); + if (gen < 11) { + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), + &vfio_igd_quirk_mirror_bdsm, vdev, + "vfio-igd-bdsm-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[0].region.mem, + IGD_BDSM_MMIO_OFFSET, + &quirk->mem[1], 1); + } else { + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), + &vfio_igd_quirk_mirror_bdsm64, vdev, + "vfio-igd-bdsm-quirk", 8); + memory_region_add_subregion_overlap(vdev->bars[0].region.mem, + IGD_BDSM_MMIO_OFFSET, + &quirk->mem[1], 1); + } QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); } From 37f05a59e8695df3d1206e7100190e48ec0af847 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 6 Dec 2024 20:27:48 +0800 Subject: [PATCH 0698/2892] vfio/igd: add x-igd-gms option back to set DSM region size for guest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DSM region is likely to store framebuffer in Windows, a small DSM region may cause display issues (e.g. half of the screen is black). Since 971ca22f041b ("vfio/igd: don't set stolen memory size to zero"), the x-igd-gms option was functionally removed, QEMU uses host's original value, which is determined by DVMT Pre-Allocated option in Intel FSP of host bios. However, some vendors do not expose this config item to users. In such cases, x-igd-gms option can be used to manually set the data stolen memory size for guest. So this commit brings this option back, keeping its old behavior. When it is not specified, QEMU uses host's value. When DVMT Pre-Allocated option is available in host BIOS, user should set DSM region size there instead of using x-igd-gms option. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20241206122749.9893-11-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index c15a14e445..0740a5dd8c 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -14,6 +14,7 @@ #include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/qmp/qerror.h" #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" #include "pci.h" @@ -722,6 +723,31 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + /* + * Allow user to override dsm size using x-igd-gms option, in multiples of + * 32MiB. This option should only be used when the desired size cannot be + * set from DVMT Pre-Allocated option in host BIOS. + */ + if (vdev->igd_gms) { + if (gen < 8) { + if (vdev->igd_gms <= 0x10) { + gmch &= ~(IGD_GMCH_GEN6_GMS_MASK << IGD_GMCH_GEN6_GMS_SHIFT); + gmch |= vdev->igd_gms << IGD_GMCH_GEN6_GMS_SHIFT; + } else { + error_report(QERR_INVALID_PARAMETER_VALUE, + "x-igd-gms", "0~0x10"); + } + } else { + if (vdev->igd_gms <= 0x40) { + gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT); + gmch |= vdev->igd_gms << IGD_GMCH_GEN8_GMS_SHIFT; + } else { + error_report(QERR_INVALID_PARAMETER_VALUE, + "x-igd-gms", "0~0x40"); + } + } + } + ggms_size = igd_gtt_memory_size(gen, gmch); gms_size = igd_stolen_memory_size(gen, gmch); From d77e85dbd7655cc41af51df74c077d8b31aee93c Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:16 +0200 Subject: [PATCH 0699/2892] vfio/container: Add dirty tracking started flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a flag to VFIOContainerBase that indicates whether dirty tracking has been started for the container or not. This will be used in the following patches to allow dirty page syncs only if dirty tracking has been started. Signed-off-by: Avihai Horon Reviewed-by: Joao Martins Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-2-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 12 +++++++++++- include/hw/vfio/vfio-container-base.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 6f86c37d97..749a3fd29d 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -64,13 +64,23 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + int ret; if (!bcontainer->dirty_pages_supported) { return 0; } g_assert(vioc->set_dirty_page_tracking); - return vioc->set_dirty_page_tracking(bcontainer, start, errp); + if (bcontainer->dirty_pages_started == start) { + return 0; + } + + ret = vioc->set_dirty_page_tracking(bcontainer, start, errp); + if (!ret) { + bcontainer->dirty_pages_started = start; + } + + return ret; } int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 62a8b60d87..4cff9943ab 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -44,6 +44,7 @@ typedef struct VFIOContainerBase { unsigned long pgsizes; unsigned int dma_max_mappings; bool dirty_pages_supported; + bool dirty_pages_started; /* Protected by BQL */ QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; QLIST_ENTRY(VFIOContainerBase) next; From 0ae05e087f99a8e4ca84537bdce65d25912d084e Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:17 +0200 Subject: [PATCH 0700/2892] vfio/migration: Refactor vfio_devices_all_dirty_tracking() logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During dirty page log sync, vfio_devices_all_dirty_tracking() is used to check if dirty tracking has been started in order to avoid errors. The current logic checks if migration is in ACTIVE or DEVICE states to ensure dirty tracking has been started. However, recently there has been an effort to simplify the migration status API and reduce it to a single migration_is_running() function. To accommodate this, refactor vfio_devices_all_dirty_tracking() logic so it won't use migration_is_active() and migration_is_device(). Instead, use internal VFIO dirty tracking flags. As a side effect, now that migration status is no longer used to detect dirty tracking status, VFIO log syncs are untied from migration. This will make calc-dirty-rate more accurate as now it will also include VFIO dirty pages. While at it, as VFIODevice->dirty_tracking is now used to detect dirty tracking status, add a comment that states how it's protected. Signed-off-by: Avihai Horon Reviewed-by: Joao Martins Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-3-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 17 ++++++++++++++++- include/hw/vfio/vfio-common.h | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 598272f4dd..fd24b7ced8 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -170,11 +170,26 @@ bool vfio_device_state_is_precopy(VFIODevice *vbasedev) migration->device_state == VFIO_DEVICE_STATE_PRE_COPY_P2P; } +static bool vfio_devices_all_device_dirty_tracking_started( + const VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (!vbasedev->dirty_tracking) { + return false; + } + } + + return true; +} + static bool vfio_devices_all_dirty_tracking(VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; - if (!migration_is_active() && !migration_is_device()) { + if (!(vfio_devices_all_device_dirty_tracking_started(bcontainer) || + bcontainer->dirty_pages_started)) { return false; } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index d57111843d..a9a68e3fd9 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -143,7 +143,7 @@ typedef struct VFIODevice { OnOffAuto pre_copy_dirty_page_tracking; OnOffAuto device_dirty_page_tracking; bool dirty_pages_supported; - bool dirty_tracking; + bool dirty_tracking; /* Protected by BQL */ bool iommu_dirty_tracking; HostIOMMUDevice *hiod; int devid; From 6e9df66e8afaa2e2fc98e7a3470edf00ffa16f03 Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:18 +0200 Subject: [PATCH 0701/2892] vfio/migration: Refactor vfio_devices_all_running_and_mig_active() logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During DMA unmap with vIOMMU, vfio_devices_all_running_and_mig_active() is used to check whether a dirty page log sync of the unmapped pages is required. Such log sync is needed during migration pre-copy phase, and the current logic detects it by checking if migration is active and if the VFIO devices are running. However, recently there has been an effort to simplify the migration status API and reduce it to a single migration_is_running() function. To accommodate this, refactor vfio_devices_all_running_and_mig_active() logic so it won't use migration_is_active(). Do it by simply checking if dirty tracking has been started using internal VFIO flags. This should be equivalent to the previous logic as during migration dirty tracking is active and when the guest is stopped there shouldn't be DMA unmaps coming from it. As a side effect, now that migration status is no longer used, DMA unmap log syncs are untied from migration. This will make calc-dirty-rate more accurate as now it will also include VFIO dirty pages that were DMA unmapped. Also rename the function to properly reflect its new logic and extract common code from vfio_devices_all_dirty_tracking(). Signed-off-by: Avihai Horon Reviewed-by: Joao Martins Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-4-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 40 +++++++---------------------------- hw/vfio/container.c | 2 +- include/hw/vfio/vfio-common.h | 4 ++-- 3 files changed, 11 insertions(+), 35 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index fd24b7ced8..9b5524377c 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -184,12 +184,18 @@ static bool vfio_devices_all_device_dirty_tracking_started( return true; } +bool vfio_devices_all_dirty_tracking_started( + const VFIOContainerBase *bcontainer) +{ + return vfio_devices_all_device_dirty_tracking_started(bcontainer) || + bcontainer->dirty_pages_started; +} + static bool vfio_devices_all_dirty_tracking(VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; - if (!(vfio_devices_all_device_dirty_tracking_started(bcontainer) || - bcontainer->dirty_pages_started)) { + if (!vfio_devices_all_dirty_tracking_started(bcontainer)) { return false; } @@ -225,36 +231,6 @@ bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer) return true; } -/* - * Check if all VFIO devices are running and migration is active, which is - * essentially equivalent to the migration being in pre-copy phase. - */ -bool -vfio_devices_all_running_and_mig_active(const VFIOContainerBase *bcontainer) -{ - VFIODevice *vbasedev; - - if (!migration_is_active()) { - return false; - } - - QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { - VFIOMigration *migration = vbasedev->migration; - - if (!migration) { - return false; - } - - if (vfio_device_state_is_running(vbasedev) || - vfio_device_state_is_precopy(vbasedev)) { - continue; - } else { - return false; - } - } - return true; -} - static bool vfio_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 78a3c2d55f..4ebb526808 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -131,7 +131,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, int ret; Error *local_err = NULL; - if (iotlb && vfio_devices_all_running_and_mig_active(bcontainer)) { + if (iotlb && vfio_devices_all_dirty_tracking_started(bcontainer)) { if (!vfio_devices_all_device_dirty_tracking(bcontainer) && bcontainer->dirty_pages_supported) { return vfio_dma_unmap_bitmap(container, iova, size, iotlb); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index a9a68e3fd9..0c60be5b15 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -296,8 +296,8 @@ bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); void vfio_migration_exit(VFIODevice *vbasedev); int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); -bool -vfio_devices_all_running_and_mig_active(const VFIOContainerBase *bcontainer); +bool vfio_devices_all_dirty_tracking_started( + const VFIOContainerBase *bcontainer); bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, From 1f21670ec0700575280bd6aa837eaa8d87fc98c1 Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:19 +0200 Subject: [PATCH 0702/2892] vfio/migration: Rename vfio_devices_all_dirty_tracking() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_devices_all_dirty_tracking() is used to check if dirty page log sync is needed. However, besides checking the dirty page tracking status, it also checks the pre_copy_dirty_page_tracking flag. Rename it to vfio_devices_log_sync_needed() which reflects its purpose more accurately and makes the code clearer as there are already several helpers with similar names. Signed-off-by: Avihai Horon Reviewed-by: Joao Martins Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-5-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 9b5524377c..f7499a9b74 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -191,7 +191,7 @@ bool vfio_devices_all_dirty_tracking_started( bcontainer->dirty_pages_started; } -static bool vfio_devices_all_dirty_tracking(VFIOContainerBase *bcontainer) +static bool vfio_log_sync_needed(const VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; @@ -1364,7 +1364,7 @@ static void vfio_listener_log_sync(MemoryListener *listener, return; } - if (vfio_devices_all_dirty_tracking(bcontainer)) { + if (vfio_log_sync_needed(bcontainer)) { ret = vfio_sync_dirty_bitmap(bcontainer, section, &local_err); if (ret) { error_report_err(local_err); From 7d5d9c8864f55983e5e9b9e6a8fd05440a80d48f Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:20 +0200 Subject: [PATCH 0703/2892] system/dirtylimit: Don't use migration_is_active() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vcpu_dirty_rate_stat_collect() uses migration_is_active() to detect whether migration is running or not, in order to get the correct dirty rate period value. However, recently there has been an effort to simplify the migration status API and reduce it to a single migration_is_running() function. To accommodate this, and since the same functionality can be achieved with migration_is_running(), use it instead of migration_is_active(). Signed-off-by: Avihai Horon Reviewed-by: Hyman Huang Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-6-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- system/dirtylimit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/dirtylimit.c b/system/dirtylimit.c index d94b994bd8..7c071248bb 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -80,8 +80,7 @@ static void vcpu_dirty_rate_stat_collect(void) int i = 0; int64_t period = DIRTYLIMIT_CALC_TIME_MS; - if (migrate_dirty_limit() && - migration_is_active()) { + if (migrate_dirty_limit() && migration_is_running()) { period = migrate_vcpu_dirty_limit_period(); } From 844ed0f7622679d86c2244a2101d2a166c5313e0 Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:21 +0200 Subject: [PATCH 0704/2892] migration: Drop migration_is_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After being removed from VFIO, migration_is_device() no longer has any users. Drop it. Signed-off-by: Avihai Horon Reviewed-by: Cédric Le Goater Acked-by: Peter Xu Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-7-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- include/migration/misc.h | 1 - migration/migration.c | 7 ------- 2 files changed, 8 deletions(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index 804eb23c06..ad1e25826a 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -54,7 +54,6 @@ void migration_object_init(void); void migration_shutdown(void); bool migration_is_active(void); -bool migration_is_device(void); bool migration_is_running(void); bool migration_thread_is_self(void); diff --git a/migration/migration.c b/migration/migration.c index 056a90daaf..3606d1e263 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1645,13 +1645,6 @@ bool migration_is_active(void) s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); } -bool migration_is_device(void) -{ - MigrationState *s = current_migration; - - return s->state == MIGRATION_STATUS_DEVICE; -} - bool migration_thread_is_self(void) { MigrationState *s = current_migration; From 3bdb1a75f1bb4234904dec7753de9c0c0ece3dbf Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 18 Dec 2024 15:40:22 +0200 Subject: [PATCH 0705/2892] migration: Unexport migration_is_active() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After being removed from VFIO and dirty limit, migration_is_active() no longer has any users outside the migration subsystem, and in fact, it's only used in migration.c. Unexport it and also relocate it so it can be made static. Signed-off-by: Avihai Horon Reviewed-by: Cédric Le Goater Acked-by: Peter Xu Tested-by: Joao Martins Link: https://lore.kernel.org/r/20241218134022.21264-8-avihaih@nvidia.com Signed-off-by: Cédric Le Goater --- include/migration/misc.h | 1 - migration/migration.c | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index ad1e25826a..c0e23fdac9 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -53,7 +53,6 @@ void dump_vmstate_json_to_file(FILE *out_fp); void migration_object_init(void); void migration_shutdown(void); -bool migration_is_active(void); bool migration_is_running(void); bool migration_thread_is_self(void); diff --git a/migration/migration.c b/migration/migration.c index 3606d1e263..df61ca4e93 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1139,6 +1139,14 @@ bool migration_is_running(void) } } +static bool migration_is_active(void) +{ + MigrationState *s = current_migration; + + return (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); +} + static bool migrate_show_downtime(MigrationState *s) { return (s->state == MIGRATION_STATUS_COMPLETED) || migration_in_postcopy(); @@ -1637,14 +1645,6 @@ bool migration_in_bg_snapshot(void) return migrate_background_snapshot() && migration_is_running(); } -bool migration_is_active(void) -{ - MigrationState *s = current_migration; - - return (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); -} - bool migration_thread_is_self(void) { MigrationState *s = current_migration; From 02ce6cea71be4f6774351f5e658d50044c5b53b2 Mon Sep 17 00:00:00 2001 From: Guo Hongyu Date: Thu, 19 Dec 2024 20:23:11 +0800 Subject: [PATCH 0706/2892] target/loongarch: Fix vldi inst Refer to the link below for a description of the vldi instructions: https://jia.je/unofficial-loongarch-intrinsics-guide/lsx/misc/#synopsis_88 Fixed errors in vldi instruction implementation. Signed-off-by: Guo Hongyu Tested-by: Xianglai Li Signed-off-by: Xianglai Li Reviewed-by: Bibo Mao Signed-off-by: Bibo Mao --- target/loongarch/tcg/insn_trans/trans_vec.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc index 92b1d22e28..d317dfcc1c 100644 --- a/target/loongarch/tcg/insn_trans/trans_vec.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -3480,7 +3480,7 @@ static uint64_t vldi_get_value(DisasContext *ctx, uint32_t imm) break; case 1: /* data: {2{16'0, imm[7:0], 8'0}} */ - data = (t << 24) | (t << 8); + data = (t << 40) | (t << 8); break; case 2: /* data: {2{8'0, imm[7:0], 16'0}} */ From d41989e7548397b469ec9c7be4cee699321a317e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 12 Dec 2024 16:22:34 +0800 Subject: [PATCH 0707/2892] target/loongarch: Use actual operand size with vbsrl check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hardcoded 32 bytes is used for vbsrl emulation check, there is problem when options lsx=on,lasx=off is used for vbsrl.v instruction in TCG mode. It injects LASX exception rather LSX exception. Here actual operand size is used. Cc: qemu-stable@nongnu.org Fixes: df97f338076 ("target/loongarch: Implement xvreplve xvinsve0 xvpickve") Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- target/loongarch/tcg/insn_trans/trans_vec.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc index d317dfcc1c..dff92772ad 100644 --- a/target/loongarch/tcg/insn_trans/trans_vec.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -5126,7 +5126,7 @@ static bool do_vbsrl_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) { int i, ofs; - if (!check_vec(ctx, 32)) { + if (!check_vec(ctx, oprsz)) { return true; } From 2f1399b008e5aeab6283fbe2cda5c440f62ff1bb Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 24 Dec 2024 17:13:53 +0800 Subject: [PATCH 0708/2892] hw/loongarch/virt: Create fdt table on machine creation done notification The same with ACPI table, fdt table is created on machine done notification. Some objects like CPU objects can be created with cold-plug method with command such as -smp x, -device la464-loongarch-cpu, so all objects finish to create when machine is done. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt.c | 103 ++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 3a905cf71d..266f291509 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -615,12 +615,67 @@ static void virt_build_smbios(LoongArchVirtMachineState *lvms) } } +static void virt_fdt_setup(LoongArchVirtMachineState *lvms) +{ + MachineState *machine = MACHINE(lvms); + uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; + int i; + + create_fdt(lvms); + fdt_add_cpu_nodes(lvms); + fdt_add_memory_nodes(machine); + fdt_add_fw_cfg_node(lvms); + fdt_add_flash_node(lvms); + + /* Add cpu interrupt-controller */ + fdt_add_cpuic_node(lvms, &cpuintc_phandle); + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); + /* Add PCH PIC node */ + fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + /* Add pcie node */ + fdt_add_pcie_node(lvms, &pch_pic_phandle, &pch_msi_phandle); + + /* + * Create uart fdt node in reverse order so that they appear + * in the finished device tree lowest address first + */ + for (i = VIRT_UART_COUNT; i-- > 0;) { + hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE; + int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE; + fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0); + } + + fdt_add_rtc_node(lvms, &pch_pic_phandle); + fdt_add_ged_reset(lvms); + platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", + VIRT_PLATFORM_BUS_BASEADDRESS, + VIRT_PLATFORM_BUS_SIZE, + VIRT_PLATFORM_BUS_IRQ); + + /* + * Since lowmem region starts from 0 and Linux kernel legacy start address + * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer + * access. FDT size limit with 1 MiB. + * Put the FDT into the memory map as a ROM image: this will ensure + * the FDT is copied again upon reset, even if addr points into RAM. + */ + qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); + rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, + &address_space_memory); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); +} + static void virt_done(Notifier *notifier, void *data) { LoongArchVirtMachineState *lvms = container_of(notifier, LoongArchVirtMachineState, machine_done); virt_build_smbios(lvms); loongarch_acpi_setup(lvms); + virt_fdt_setup(lvms); } static void virt_powerdown_req(Notifier *notifier, void *opaque) @@ -699,9 +754,7 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) } static void virt_devices_init(DeviceState *pch_pic, - LoongArchVirtMachineState *lvms, - uint32_t *pch_pic_phandle, - uint32_t *pch_msi_phandle) + LoongArchVirtMachineState *lvms) { MachineClass *mc = MACHINE_GET_CLASS(lvms); DeviceState *gpex_dev; @@ -747,9 +800,6 @@ static void virt_devices_init(DeviceState *pch_pic, gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); } - /* Add pcie node */ - fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle); - /* * Create uart fdt node in reverse order so that they appear * in the finished device tree lowest address first @@ -760,7 +810,6 @@ static void virt_devices_init(DeviceState *pch_pic, serial_mm_init(get_system_memory(), base, 0, qdev_get_gpio_in(pch_pic, irq), 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN); - fdt_add_uart_node(lvms, pch_pic_phandle, base, irq, i == 0); } /* Network init */ @@ -774,8 +823,6 @@ static void virt_devices_init(DeviceState *pch_pic, sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, qdev_get_gpio_in(pch_pic, VIRT_RTC_IRQ - VIRT_GSI_BASE)); - fdt_add_rtc_node(lvms, pch_pic_phandle); - fdt_add_ged_reset(lvms); /* acpi ged */ lvms->acpi_ged = create_acpi_ged(pch_pic, lvms); @@ -793,7 +840,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) CPULoongArchState *env; CPUState *cpu_state; int cpu, pin, i, start, num; - uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; /* * Extended IRQ model. @@ -850,9 +896,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); - /* Add cpu interrupt-controller */ - fdt_add_cpuic_node(lvms, &cpuintc_phandle); - for (cpu = 0; cpu < ms->smp.cpus; cpu++) { cpu_state = qemu_get_cpu(cpu); cpudev = DEVICE(cpu_state); @@ -891,9 +934,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) } } - /* Add Extend I/O Interrupt Controller node */ - fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); - pch_pic = qdev_new(TYPE_LOONGARCH_PIC); num = VIRT_PCH_PIC_IRQ_NUM; qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); @@ -913,9 +953,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); } - /* Add PCH PIC node */ - fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); - pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); start = num; num = EXTIOI_IRQS - start; @@ -930,10 +967,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) qdev_get_gpio_in(extioi, i + start)); } - /* Add PCH MSI node */ - fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); - - virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle); + virt_devices_init(pch_pic, lvms); } static void virt_firmware_init(LoongArchVirtMachineState *lvms) @@ -1151,8 +1185,6 @@ static void virt_init(MachineState *machine) cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); } - create_fdt(lvms); - /* Create IOCSR space */ memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL, machine, "iocsr", UINT64_MAX); @@ -1171,8 +1203,6 @@ static void virt_init(MachineState *machine) lacpu = LOONGARCH_CPU(cpu); lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; } - fdt_add_cpu_nodes(lvms); - fdt_add_memory_nodes(machine); fw_cfg_add_memory(machine); /* Node0 memory */ @@ -1224,34 +1254,15 @@ static void virt_init(MachineState *machine) memmap_table, sizeof(struct memmap_entry) * (memmap_entries)); } - fdt_add_fw_cfg_node(lvms); - fdt_add_flash_node(lvms); /* Initialize the IO interrupt subsystem */ virt_irq_init(lvms); - platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", - VIRT_PLATFORM_BUS_BASEADDRESS, - VIRT_PLATFORM_BUS_SIZE, - VIRT_PLATFORM_BUS_IRQ); lvms->machine_done.notify = virt_done; qemu_add_machine_init_done_notifier(&lvms->machine_done); /* connect powerdown request */ lvms->powerdown_notifier.notify = virt_powerdown_req; qemu_register_powerdown_notifier(&lvms->powerdown_notifier); - /* - * Since lowmem region starts from 0 and Linux kernel legacy start address - * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer - * access. FDT size limit with 1 MiB. - * Put the FDT into the memory map as a ROM image: this will ensure - * the FDT is copied again upon reset, even if addr points into RAM. - */ - qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); - rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, - &address_space_memory); - qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, - rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); - lvms->bootinfo.ram_size = ram_size; loongarch_load_kernel(machine, &lvms->bootinfo); } From b360109fc6b3b6ffae071d5b38c9bba0f9728160 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 26 Nov 2024 15:29:39 +0800 Subject: [PATCH 0709/2892] hw/loongarch/virt: Improve fdt table creation for CPU object For CPU object, possible_cpu_arch_ids() function is used rather than smp.cpus. With command -smp x, -device la464-loongarch-cpu, smp.cpus is not accurate for all possible CPU objects, possible_cpu_arch_ids() is used here. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 266f291509..99594a13a0 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -365,26 +365,35 @@ static void create_fdt(LoongArchVirtMachineState *lvms) static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) { int num; - const MachineState *ms = MACHINE(lvms); - int smp_cpus = ms->smp.cpus; + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus; + LoongArchCPU *cpu; + CPUState *cs; + char *nodename, *map_path; qemu_fdt_add_subnode(ms->fdt, "/cpus"); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); /* cpu nodes */ - for (num = smp_cpus - 1; num >= 0; num--) { - char *nodename = g_strdup_printf("/cpus/cpu@%d", num); - LoongArchCPU *cpu = LOONGARCH_CPU(qemu_get_cpu(num)); - CPUState *cs = CPU(cpu); + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + nodename = g_strdup_printf("/cpus/cpu@%d", num); + cpu = LOONGARCH_CPU(cs); qemu_fdt_add_subnode(ms->fdt, nodename); qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", cpu->dtb_compatible); - if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { + if (possible_cpus->cpus[num].props.has_node_id) { qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", - ms->possible_cpus->cpus[cs->cpu_index].props.node_id); + possible_cpus->cpus[num].props.node_id); } qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", @@ -394,11 +403,13 @@ static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) /*cpu map */ qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } - for (num = smp_cpus - 1; num >= 0; num--) { - char *cpu_path = g_strdup_printf("/cpus/cpu@%d", num); - char *map_path; - + nodename = g_strdup_printf("/cpus/cpu@%d", num); if (ms->smp.threads > 1) { map_path = g_strdup_printf( "/cpus/cpu-map/socket%d/core%d/thread%d", @@ -412,10 +423,10 @@ static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) num % ms->smp.cores); } qemu_fdt_add_path(ms->fdt, map_path); - qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", cpu_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename); g_free(map_path); - g_free(cpu_path); + g_free(nodename); } } From 936c3f4d79160b0c4d9d0097b5643fc7e460c605 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 19 Dec 2024 20:54:23 +0800 Subject: [PATCH 0710/2892] target/loongarch: Use auto method with LSX feature Like LBT feature, add type OnOffAuto for LSX feature setting. Also add LSX feature detection with new VM ioctl command, fallback to old method if it is not supported. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.c | 38 +++++++++++++++------------ target/loongarch/cpu.h | 2 ++ target/loongarch/kvm/kvm.c | 54 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 58415ffe99..8e52338350 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -379,6 +379,7 @@ static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; + uint32_t data = 0; int i; for (i = 0; i < 21; i++) { @@ -388,7 +389,6 @@ static void loongarch_la464_initfn(Object *obj) cpu->dtb_compatible = "loongarch,Loongson-3A5000"; env->cpucfg[0] = 0x14c010; /* PRID */ - uint32_t data = 0; data = FIELD_DP32(data, CPUCFG1, ARCH, 2); data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); @@ -477,7 +477,7 @@ static void loongarch_la132_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; - + uint32_t data = 0; int i; for (i = 0; i < 21; i++) { @@ -487,7 +487,6 @@ static void loongarch_la132_initfn(Object *obj) cpu->dtb_compatible = "loongarch,Loongson-1C103"; env->cpucfg[0] = 0x148042; /* PRID */ - uint32_t data = 0; data = FIELD_DP32(data, CPUCFG1, ARCH, 1); /* LA32 */ data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); @@ -615,27 +614,30 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) static bool loongarch_get_lsx(Object *obj, Error **errp) { - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - bool ret; - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { - ret = true; - } else { - ret = false; - } - return ret; + return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; } static void loongarch_set_lsx(Object *obj, bool value, Error **errp) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; - if (value) { - cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); - } else { - cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 0); - cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; } + + /* LSX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { + error_setg(errp, "Failed to enable LSX in TCG mode"); + return; + } + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); } static bool loongarch_get_lasx(Object *obj, Error **errp) @@ -693,6 +695,7 @@ void loongarch_cpu_post_init(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); + cpu->lsx = ON_OFF_AUTO_AUTO; object_property_add_bool(obj, "lsx", loongarch_get_lsx, loongarch_set_lsx); object_property_add_bool(obj, "lasx", loongarch_get_lasx, @@ -713,6 +716,7 @@ void loongarch_cpu_post_init(Object *obj) } else { cpu->lbt = ON_OFF_AUTO_OFF; + cpu->pmu = ON_OFF_AUTO_OFF; } } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 86c86c6c95..5bddf72c22 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -283,6 +283,7 @@ typedef struct LoongArchTLB LoongArchTLB; #endif enum loongarch_features { + LOONGARCH_FEATURE_LSX, LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ LOONGARCH_FEATURE_PMU, }; @@ -404,6 +405,7 @@ struct ArchCPU { uint32_t phy_id; OnOffAuto lbt; OnOffAuto pmu; + OnOffAuto lsx; /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 3c86f5ffb9..eeedf3175e 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -798,8 +798,35 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) { int ret; struct kvm_device_attr attr; + uint64_t val; switch (feature) { + case LOONGARCH_FEATURE_LSX: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_LSX; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + if (ret == 0) { + return true; + } + + /* Fallback to old kernel detect interface */ + val = 0; + attr.group = KVM_LOONGARCH_VCPU_CPUCFG; + /* Cpucfg2 */ + attr.attr = 2; + attr.addr = (uint64_t)&val; + ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr); + if (!ret) { + ret = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr); + if (ret) { + return false; + } + + ret = FIELD_EX32((uint32_t)val, CPUCFG2, LSX); + return (ret != 0); + } + return false; + case LOONGARCH_FEATURE_LBT: /* * Return all if all the LBT features are supported such as: @@ -829,6 +856,28 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) return false; } +static int kvm_cpu_check_lsx(CPUState *cs, Error **errp) +{ + CPULoongArchState *env = cpu_env(cs); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_LSX); + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LSX, 0); + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LSX, 1); + } else { + error_setg(errp, "'lsx' feature not supported by KVM on this host"); + return -ENOTSUP; + } + } else if ((cpu->lsx == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LSX, 1); + } + + return 0; +} + static int kvm_cpu_check_lbt(CPUState *cs, Error **errp) { CPULoongArchState *env = cpu_env(cs); @@ -889,6 +938,11 @@ int kvm_arch_init_vcpu(CPUState *cs) brk_insn = val; } + ret = kvm_cpu_check_lsx(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + } + ret = kvm_cpu_check_lbt(cs, &local_err); if (ret < 0) { error_report_err(local_err); From 5e360dabedb1ab1f15cce27a134ccbe4b8e18424 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 19 Dec 2024 20:54:24 +0800 Subject: [PATCH 0711/2892] target/loongarch: Use auto method with LASX feature Like LSX feature, add type OnOffAuto for LASX feature setting. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.c | 50 +++++++++++++++++++++++------------ target/loongarch/cpu.h | 2 ++ target/loongarch/kvm/kvm.c | 53 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 16 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 8e52338350..d611a60470 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -623,6 +623,14 @@ static void loongarch_set_lsx(Object *obj, bool value, Error **errp) uint32_t val; cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if (cpu->lsx == ON_OFF_AUTO_OFF) { + cpu->lasx = ON_OFF_AUTO_OFF; + if (cpu->lasx == ON_OFF_AUTO_ON) { + error_setg(errp, "Failed to disable LSX since LASX is enabled"); + return; + } + } + if (kvm_enabled()) { /* kvm feature detection in function kvm_arch_init_vcpu */ return; @@ -635,6 +643,9 @@ static void loongarch_set_lsx(Object *obj, bool value, Error **errp) error_setg(errp, "Failed to enable LSX in TCG mode"); return; } + } else { + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); + val = cpu->env.cpucfg[2]; } cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); @@ -642,29 +653,35 @@ static void loongarch_set_lsx(Object *obj, bool value, Error **errp) static bool loongarch_get_lasx(Object *obj, Error **errp) { - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - bool ret; - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { - ret = true; - } else { - ret = false; - } - return ret; + return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; } static void loongarch_set_lasx(Object *obj, bool value, Error **errp) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; - if (value) { - if (!FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { - cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); - } - cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 1); - } else { - cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { + error_setg(errp, "Failed to enable LASX since lSX is disabled"); + return; } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LASX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { + error_setg(errp, "Failed to enable LASX in TCG mode"); + return; + } + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); } static bool loongarch_get_lbt(Object *obj, Error **errp) @@ -696,6 +713,7 @@ void loongarch_cpu_post_init(Object *obj) LoongArchCPU *cpu = LOONGARCH_CPU(obj); cpu->lsx = ON_OFF_AUTO_AUTO; + cpu->lasx = ON_OFF_AUTO_AUTO; object_property_add_bool(obj, "lsx", loongarch_get_lsx, loongarch_set_lsx); object_property_add_bool(obj, "lasx", loongarch_get_lasx, diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 5bddf72c22..8eee49a984 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -284,6 +284,7 @@ typedef struct LoongArchTLB LoongArchTLB; enum loongarch_features { LOONGARCH_FEATURE_LSX, + LOONGARCH_FEATURE_LASX, LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ LOONGARCH_FEATURE_PMU, }; @@ -406,6 +407,7 @@ struct ArchCPU { OnOffAuto lbt; OnOffAuto pmu; OnOffAuto lsx; + OnOffAuto lasx; /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index eeedf3175e..a3f55155b0 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -827,6 +827,32 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) } return false; + case LOONGARCH_FEATURE_LASX: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_LASX; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + if (ret == 0) { + return true; + } + + /* Fallback to old kernel detect interface */ + val = 0; + attr.group = KVM_LOONGARCH_VCPU_CPUCFG; + /* Cpucfg2 */ + attr.attr = 2; + attr.addr = (uint64_t)&val; + ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr); + if (!ret) { + ret = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr); + if (ret) { + return false; + } + + ret = FIELD_EX32((uint32_t)val, CPUCFG2, LASX); + return (ret != 0); + } + return false; + case LOONGARCH_FEATURE_LBT: /* * Return all if all the LBT features are supported such as: @@ -878,6 +904,28 @@ static int kvm_cpu_check_lsx(CPUState *cs, Error **errp) return 0; } +static int kvm_cpu_check_lasx(CPUState *cs, Error **errp) +{ + CPULoongArchState *env = cpu_env(cs); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_LASX); + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LASX, 0); + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LASX, 1); + } else { + error_setg(errp, "'lasx' feature not supported by KVM on host"); + return -ENOTSUP; + } + } else if ((cpu->lasx == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LASX, 1); + } + + return 0; +} + static int kvm_cpu_check_lbt(CPUState *cs, Error **errp) { CPULoongArchState *env = cpu_env(cs); @@ -943,6 +991,11 @@ int kvm_arch_init_vcpu(CPUState *cs) error_report_err(local_err); } + ret = kvm_cpu_check_lasx(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + } + ret = kvm_cpu_check_lbt(cs, &local_err); if (ret < 0) { error_report_err(local_err); From 6a4fa294ebe56aa09cf65430305c80415a4d73d9 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 9 Dec 2024 10:30:59 -0800 Subject: [PATCH 0712/2892] docs/devel: remove dead video link for sourcehut submit process Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/devel/submitting-a-patch.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index 10b062eec2..03b2ac298a 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -252,10 +252,7 @@ patches to the QEMU mailing list by following these steps: #. Send your patches to the QEMU mailing list using the web-based ``git-send-email`` UI at https://git.sr.ht/~USERNAME/qemu/send-email -`This video -`__ -shows the web-based ``git-send-email`` workflow. Documentation is -available `here +Documentation for sourcehut is available `here `__. .. _cc_the_relevant_maintainer: From f2ccc4149363ed52444efc975ed838d6757c775c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 27 Nov 2024 00:30:45 +0800 Subject: [PATCH 0713/2892] hw/timer/hpet: Fix comment about capabilities register HPETState.capability stores the emulated value for "general capabilities and id register" instead of "main counter register". Fix the comment to accurately reflect this. Signed-off-by: Zhao Liu Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/timer/hpet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 2a45410c0d..1c8c6c69ef 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -736,7 +736,7 @@ static void hpet_realize(DeviceState *dev, Error **errp) timer->state = s; } - /* 64-bit main counter; LegacyReplacementRoute. */ + /* 64-bit General Capabilities and ID Register; LegacyReplacementRoute. */ s->capability = 0x8086a001ULL; s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32); From 11ea52fcbeebe2bbef24ef2eedcc64800f5eaa82 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 27 Nov 2024 00:30:46 +0800 Subject: [PATCH 0714/2892] hw/timer/hpet: Drop the unused macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HPET_TN_CFG_BITS_READONLY_OR_RESERVED is not used in any place since HPET_TN_CFG_WRITE_MASK has been already used to check and fix the writable bits in hpet_ram_write(). Drop this unused macro. Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- include/hw/timer/hpet.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h index d17a8d4319..71e8c62453 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -58,7 +58,6 @@ #define HPET_TN_CFG_WRITE_MASK 0x7f4e #define HPET_TN_INT_ROUTE_SHIFT 9 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32 -#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U struct hpet_fw_entry { From 0cb3ff7c22671aa1e1e227318799ccf6762c3bea Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 4 Dec 2024 11:51:11 -0800 Subject: [PATCH 0715/2892] vvfat: fix ubsan issue in create_long_filename Found with test sbsaref introduced in [1]. [1] https://patchew.org/QEMU/20241203213629.2482806-1-pierrick.bouvier@linaro.org/ ../block/vvfat.c:433:24: runtime error: index 14 out of bounds for type 'uint8_t [11]' #0 0x56151a66b93a in create_long_filename ../block/vvfat.c:433 #1 0x56151a66f3d7 in create_short_and_long_name ../block/vvfat.c:725 #2 0x56151a670403 in read_directory ../block/vvfat.c:804 #3 0x56151a674432 in init_directories ../block/vvfat.c:964 #4 0x56151a67867b in vvfat_open ../block/vvfat.c:1258 #5 0x56151a3b8e19 in bdrv_open_driver ../block.c:1660 #6 0x56151a3bb666 in bdrv_open_common ../block.c:1985 #7 0x56151a3cadb9 in bdrv_open_inherit ../block.c:4153 #8 0x56151a3c8850 in bdrv_open_child_bs ../block.c:3731 #9 0x56151a3ca832 in bdrv_open_inherit ../block.c:4098 #10 0x56151a3cbe40 in bdrv_open ../block.c:4248 #11 0x56151a46344f in blk_new_open ../block/block-backend.c:457 #12 0x56151a388bd9 in blockdev_init ../blockdev.c:612 #13 0x56151a38ab2d in drive_new ../blockdev.c:1006 #14 0x5615190fca41 in drive_init_func ../system/vl.c:649 #15 0x56151aa796dd in qemu_opts_foreach ../util/qemu-option.c:1135 #16 0x5615190fd2b6 in configure_blockdev ../system/vl.c:708 #17 0x56151910a307 in qemu_create_early_backends ../system/vl.c:2004 #18 0x561519113fcf in qemu_init ../system/vl.c:3685 #19 0x56151a7e438e in main ../system/main.c:47 #20 0x7f72d1a46249 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #21 0x7f72d1a46304 in __libc_start_main_impl ../csu/libc-start.c:360 #22 0x561517e98510 in _start (/home/user/.work/qemu/build/qemu-system-aarch64+0x3b9b510) The offset used can easily go beyond entry->name size. It's probably a bug, but I don't have the time to dive into vfat specifics for now. This change solves the ubsan issue, and is functionally equivalent, as anything written past the entry->name array would not be read anyway. Signed-off-by: Pierrick Bouvier Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- block/vvfat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/vvfat.c b/block/vvfat.c index 8ffe8b3b9b..f2eafaa923 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -426,6 +426,10 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename) else if(offset<22) offset=14+offset-10; else offset=28+offset-22; entry=array_get(&(s->directory),s->directory.next-1-(i/26)); + /* ensure we don't write anything past entry->name */ + if (offset >= sizeof(entry->name)) { + continue; + } if (i >= 2 * length + 2) { entry->name[offset] = 0xff; } else if (i % 2 == 0) { From 916f50172baa91ddf0e669a9d6d2747055c0e610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 16:02:01 +0100 Subject: [PATCH 0716/2892] docs: Correct '-runas' and '-fsdev/-virtfs proxy' indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the same style for deprecated / removed commands. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/about/deprecated.rst | 2 +- docs/about/removed-features.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 267892b62f..d6809f94ea 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -75,7 +75,7 @@ marked deprecated since 9.0, users have to ensure that all the topology members described with -smp are supported by the target machine. ``-runas`` (since 9.1) ----------------------- +'''''''''''''''''''''' Use ``-run-with user=..`` instead. diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 9bebee795c..68fe0b47f9 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -518,7 +518,7 @@ and later do not support it because the virtio-scsi device was introduced for full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. ``-fsdev proxy`` and ``-virtfs proxy`` (since 9.2) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +'''''''''''''''''''''''''''''''''''''''''''''''''' The 9p ``proxy`` filesystem backend driver was originally developed to enhance security by dispatching low level filesystem operations from 9p From b4859e8f33a7d9c793a60395f792c10190cb4f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 16:02:02 +0100 Subject: [PATCH 0717/2892] docs: Correct release of TCG trace-events removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCG trace-events were deprecated before the v6.2 release, and removed for v7.0. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/about/removed-features.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 68fe0b47f9..e3a87f3f55 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1087,8 +1087,8 @@ processor IP (see `Intel discontinuance notification`_). TCG introspection features -------------------------- -TCG trace-events (since 6.2) -'''''''''''''''''''''''''''' +TCG trace-events (removed in 7.0) +''''''''''''''''''''''''''''''''' 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 From 1dd1a36de5b8651506f653aa27ba936ceed54c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 16:02:03 +0100 Subject: [PATCH 0718/2892] docs: Replace 'since' -> 'removed in' in removed-features.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/about/removed-features.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index e3a87f3f55..cb1388049a 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -403,13 +403,13 @@ Sound card devices should be created using ``-device`` or ``-audio``. The exception is ``pcspk`` which can be activated using ``-machine pcspk-audiodev=``. -``-watchdog`` (since 7.2) -''''''''''''''''''''''''' +``-watchdog`` (removed in 7.2) +'''''''''''''''''''''''''''''' Use ``-device`` instead. -Hexadecimal sizes with scaling multipliers (since 8.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' +Hexadecimal sizes with scaling multipliers (removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Input parameters that take a size value should only use a size suffix (such as 'k' or 'M') when the base is written in decimal, and not when @@ -510,15 +510,15 @@ than zero. Removed along with the ``compression`` migration capability. -``-device virtio-blk,scsi=on|off`` (since 9.1) -'''''''''''''''''''''''''''''''''''''''''''''' +``-device virtio-blk,scsi=on|off`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''' The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 and later do not support it because the virtio-scsi device was introduced for full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. -``-fsdev proxy`` and ``-virtfs proxy`` (since 9.2) -'''''''''''''''''''''''''''''''''''''''''''''''''' +``-fsdev proxy`` and ``-virtfs proxy`` (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''' The 9p ``proxy`` filesystem backend driver was originally developed to enhance security by dispatching low level filesystem operations from 9p @@ -532,8 +532,8 @@ security model option, or switch to ``virtiofs``. The virtiofs daemon ``virtiofsd`` uses vhost to eliminate the high latency costs of the 9p ``proxy`` backend. -``-portrait`` and ``-rotate`` (since 9.2) -''''''''''''''''''''''''''''''''''''''''' +``-portrait`` and ``-rotate`` (removed in 9.2) +'''''''''''''''''''''''''''''''''''''''''''''' The ``-portrait`` and ``-rotate`` options were documented as only working with the PXA LCD device, and all the machine types using From 93dcc9390e5ad0696ae7e9b7b3a5b08c2d1b6de6 Mon Sep 17 00:00:00 2001 From: Han Han Date: Thu, 19 Dec 2024 16:51:38 +0800 Subject: [PATCH 0719/2892] target/i386/cpu: Fix notes for CPU models Fixes: 644e3c5d812 ("missing vmx features for Skylake-Server and Cascadelake-Server") Signed-off-by: Han Han Reviewed-by: Chenyi Qiang Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- target/i386/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 660ddafc28..0b639848cd 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3692,6 +3692,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 4, + .note = "IBRS, EPT switching, no TSX", .props = (PropValue[]) { { "vmx-eptp-switching", "on" }, { /* end of list */ } @@ -3826,7 +3827,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, }, { .version = 4, - .note = "ARCH_CAPABILITIES, no TSX", + .note = "ARCH_CAPABILITIES, EPT switching, no TSX", .props = (PropValue[]) { { "vmx-eptp-switching", "on" }, { /* end of list */ } From a8743193ff1ed221f42c0341182cada3d67793cc Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Sun, 22 Dec 2024 18:45:07 -0300 Subject: [PATCH 0720/2892] hw/riscv/riscv-iommu-sys.c: fix duplicated 'table_size' Trivial fix for the following ticket: CID 1568580: Incorrect expression (EVALUATION_ORDER) In "table_size = table_size = n_vectors * 16U", "table_size" is written twice with the same value. Cc: qemu-trivial@nongnu.org Cc: Peter Maydell Resolves: Coverity CID 1568580 Fixes: 01c1caa9d1 ("hw/riscv/virt.c, riscv-iommu-sys.c: add MSIx support") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/riscv/riscv-iommu-sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index 28153f38da..65b24fb07d 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -121,7 +121,7 @@ static void riscv_iommu_sysdev_init_msi(RISCVIOMMUStateSys *s, uint32_t n_vectors) { RISCVIOMMUState *iommu = &s->iommu; - uint32_t table_size = table_size = n_vectors * PCI_MSIX_ENTRY_SIZE; + uint32_t table_size = n_vectors * PCI_MSIX_ENTRY_SIZE; uint32_t table_offset = RISCV_IOMMU_REG_MSI_CONFIG; uint32_t pba_size = QEMU_ALIGN_UP(n_vectors, 64) / 8; uint32_t pba_offset = RISCV_IOMMU_REG_MSI_CONFIG + 256; From 8ee904b3a4b5638a0046ee3e1948d89ecb2e2668 Mon Sep 17 00:00:00 2001 From: ckf104 <1900011634@pku.edu.cn> Date: Tue, 17 Dec 2024 22:24:15 +0800 Subject: [PATCH 0721/2892] contrib/plugins/bbv.c: Start bb index from 1 Standard simpoint tool reqeusts that index of basic block index starts from 1. Signed-off-by: ckf104 <1900011634@pku.edu.cn> Reviewed-by: Pierrick Bouvier Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- contrib/plugins/bbv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bbv.c b/contrib/plugins/bbv.c index a5256517dd..b9da6f815e 100644 --- a/contrib/plugins/bbv.c +++ b/contrib/plugins/bbv.c @@ -109,7 +109,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) bb = g_new(Bb, 1); bb->vaddr = vaddr; bb->count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); - bb->index = g_hash_table_size(bbs); + bb->index = g_hash_table_size(bbs) + 1; g_hash_table_replace(bbs, &bb->vaddr, bb); } g_rw_lock_writer_unlock(&bbs_lock); From 1e3d4d9a1a32ac6835f0d295a5117851c421fb5d Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 16 Dec 2024 18:09:14 +0100 Subject: [PATCH 0722/2892] qmp: update vhost-user protocol feature maps Add VHOST_USER_PROTOCOL_F_SHARED_OBJECT and VHOST_USER_PROTOCOL_F_DEVICE_STATE protocol feature maps to the virtio introspection. Cc: jonah.palmer@oracle.com Fixes: 160947666276 ("vhost-user: add shared_object msg") Cc: aesteve@redhat.com Fixes: cda83adc62b6 ("vhost-user: Interface for migration state transfer") Cc: hreitz@redhat.com Signed-off-by: Laurent Vivier Signed-off-by: Michael Tokarev --- hw/virtio/virtio-qmp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index cccc6fe761..8a32a3b105 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -121,6 +121,12 @@ static const qmp_virtio_feature_map_t vhost_user_protocol_map[] = { FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_STATUS, \ "VHOST_USER_PROTOCOL_F_STATUS: Querying and notifying back-end " "device status supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_SHARED_OBJECT, \ + "VHOST_USER_PROTOCOL_F_SHARED_OBJECT: Backend shared object " + "supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_DEVICE_STATE, \ + "VHOST_USER_PROTOCOL_F_DEVICE_STATE: Backend device state transfer " + "supported"), { -1, "" } }; From 17df47e87c8f71e57cb327fd720af23c39949304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 18:46:10 +0000 Subject: [PATCH 0723/2892] hw/m68k: Mark devices as big-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These devices are only used by the M68K target, which is only built as big-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_BIG_ENDIAN (besides, the DEVICE_LITTLE_ENDIAN case isn't tested). Simplify directly using DEVICE_BIG_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20241106184612.71897-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/m68k/mcf5206.c | 2 +- hw/m68k/mcf5208.c | 6 +++--- hw/m68k/mcf_intc.c | 2 +- hw/m68k/next-kbd.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index ef619cb9a4..c22e615f7a 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -582,7 +582,7 @@ static const MemoryRegionOps m5206_mbar_ops = { .write = m5206_mbar_writefn, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index f290ccc739..409bb72574 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -155,7 +155,7 @@ static uint64_t m5208_timer_read(void *opaque, hwaddr addr, static const MemoryRegionOps m5208_timer_ops = { .read = m5208_timer_read, .write = m5208_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static uint64_t m5208_sys_read(void *opaque, hwaddr addr, @@ -192,7 +192,7 @@ static void m5208_sys_write(void *opaque, hwaddr addr, static const MemoryRegionOps m5208_sys_ops = { .read = m5208_sys_read, .write = m5208_sys_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static uint64_t m5208_rcm_read(void *opaque, hwaddr addr, @@ -224,7 +224,7 @@ static void m5208_rcm_write(void *opaque, hwaddr addr, static const MemoryRegionOps m5208_rcm_ops = { .read = m5208_rcm_read, .write = m5208_rcm_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic, diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 008626f813..7b9213947d 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -166,7 +166,7 @@ static void mcf_intc_reset(DeviceState *dev) static const MemoryRegionOps mcf_intc_ops = { .read = mcf_intc_read, .write = mcf_intc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static void mcf_intc_instance_init(Object *obj) diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index dacc26413f..68b17786b2 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -162,7 +162,7 @@ static const MemoryRegionOps kbd_ops = { .write = kbd_writefn, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static const int qcode_to_nextkbd_keycode[] = { From 36a0d3748dcaf21ab12cdfb814b8f49f2de4742b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 14 Dec 2024 10:17:20 +0100 Subject: [PATCH 0724/2892] hw/m68k/next-cube: Disable the default CD-ROM drive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NeXT-Cube does not have a CD-ROM drive by default, and the kernel does not seem to deal with the empty drive very well, so let's disable the CD-ROM drive for this machine. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Message-ID: <20241214091720.49779-1-huth@tuxfamily.org> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 0ecdcea823..94d56b4654 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -1067,6 +1067,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = RAM_SIZE; mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); + mc->no_cdrom = true; } static const TypeInfo next_typeinfo = { From 347a9a975d2380647ccc5a2577a1c23722d471b8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:40 +0000 Subject: [PATCH 0725/2892] next-cube: remove 0x14020 dummy value from next_mmio_read() This is a dummy value for the SCSI CSR which appears to have no effect when removed. Eventually the reads/writes to this register will be directed towards the WIP implementations in next_scr_readfn() and next_scr_writefn(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 94d56b4654..243d9e7e66 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -286,10 +286,6 @@ static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size) size << 3); break; - case 0x14020: - val = 0x7f; - break; - default: val = 0; DPRINTF("MMIO Read @ 0x%"HWADDR_PRIx" size %d\n", addr, size); From e1699c7bc84d8ae9da6cbe4407c14059007aa223 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:41 +0000 Subject: [PATCH 0726/2892] next-cube: remove overlap between next.dma and next.mmio memory regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the start of the next.mmio memory region so that it follows on directly after the next.dma memory region. Increase the address offsets in next_mmio_read() and next_mmio_write(), and reduce the size of the next.mmio memory region accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 243d9e7e66..5f07791707 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -266,23 +266,23 @@ static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size) uint64_t val; switch (addr) { - case 0x7000: + case 0x2000: /* 0x2007000 */ /* DPRINTF("Read INT status: %x\n", s->int_status); */ val = s->int_status; break; - case 0x7800: + case 0x2800: /* 0x2007800 */ DPRINTF("MMIO Read INT mask: %x\n", s->int_mask); val = s->int_mask; break; - case 0xc000 ... 0xc003: - val = extract32(s->scr1, (4 - (addr - 0xc000) - size) << 3, + case 0x7000 ... 0x7003: /* 0x200c000 */ + val = extract32(s->scr1, (4 - (addr - 0x7000) - size) << 3, size << 3); break; - case 0xd000 ... 0xd003: - val = extract32(s->scr2, (4 - (addr - 0xd000) - size) << 3, + case 0x8000 ... 0x8003: /* 0x200d000 */ + val = extract32(s->scr2, (4 - (addr - 0x8000) - size) << 3, size << 3); break; @@ -301,25 +301,25 @@ static void next_mmio_write(void *opaque, hwaddr addr, uint64_t val, NeXTPC *s = NEXT_PC(opaque); switch (addr) { - case 0x7000: + case 0x2000: /* 0x2007000 */ DPRINTF("INT Status old: %x new: %x\n", s->int_status, (unsigned int)val); s->int_status = val; break; - case 0x7800: + case 0x2800: /* 0x2007800 */ DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, (unsigned int)val); s->int_mask = val; break; - case 0xc000 ... 0xc003: + case 0x7000 ... 0x7003: /* 0x200c000 */ DPRINTF("SCR1 Write: %x\n", (unsigned int)val); - s->scr1 = deposit32(s->scr1, (4 - (addr - 0xc000) - size) << 3, + s->scr1 = deposit32(s->scr1, (4 - (addr - 0x7000) - size) << 3, size << 3, val); break; - case 0xd000 ... 0xd003: - s->scr2 = deposit32(s->scr2, (4 - (addr - 0xd000) - size) << 3, + case 0x8000 ... 0x8003: /* 0x200d000 */ + s->scr2 = deposit32(s->scr2, (4 - (addr - 0x8000) - size) << 3, size << 3, val); next_scr2_led_update(s); next_scr2_rtc_update(s); @@ -897,7 +897,7 @@ static void next_pc_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS); memory_region_init_io(&s->mmiomem, OBJECT(s), &next_mmio_ops, s, - "next.mmio", 0xd0000); + "next.mmio", 0x9000); memory_region_init_io(&s->scrmem, OBJECT(s), &next_scr_ops, s, "next.scr", 0x20000); sysbus_init_mmio(sbd, &s->mmiomem); @@ -999,7 +999,7 @@ static void next_cube_init(MachineState *machine) sysbus_create_simple(TYPE_NEXTFB, 0x0B000000, NULL); /* MMIO */ - sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000); + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02005000); /* BMAP IO - acts as a catch-all for now */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000); From d39b0e6832d9b9914e322306628aee341cd26055 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:42 +0000 Subject: [PATCH 0727/2892] next-cube: create new next.scsi container memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the ESP SCSI and SCSI CSR registers to the new next.scsi container memory region. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 5f07791707..31c0532aff 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -94,6 +94,7 @@ struct NeXTPC { MemoryRegion mmiomem; MemoryRegion scrmem; + MemoryRegion scsimem; uint32_t scr1; uint32_t scr2; @@ -843,7 +844,12 @@ static void next_scsi_init(DeviceState *pcdev) sysbusdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sysbusdev, &error_fatal); sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(pcdev, NEXT_SCSI_I)); - sysbus_mmio_map(sysbusdev, 0, 0x2114000); + + memory_region_init(&next_pc->scsimem, OBJECT(next_pc), "next.scsi", 0x40); + memory_region_add_subregion(&next_pc->scsimem, 0x0, + sysbus_mmio_get_region(sysbusdev, 0)); + + memory_region_add_subregion(&next_pc->scrmem, 0x14000, &next_pc->scsimem); next_pc->scsi_reset = qdev_get_gpio_in(dev, 0); next_pc->scsi_dma = qdev_get_gpio_in(dev, 1); From 1441b8f922579c470e11dd9922122025f1c8cab2 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:43 +0000 Subject: [PATCH 0728/2892] next-cube: move next_scsi_init() to next_pc_realize() This reflects that the SCSI interface exists within the NeXT Peripheral Controller (PC). Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-5-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 31c0532aff..97dea45b13 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -908,6 +908,9 @@ static void next_pc_realize(DeviceState *dev, Error **errp) "next.scr", 0x20000); sysbus_init_mmio(sbd, &s->mmiomem); sysbus_init_mmio(sbd, &s->scrmem); + + /* SCSI */ + next_scsi_init(dev); } /* @@ -1050,8 +1053,6 @@ static void next_cube_init(MachineState *machine) /* TODO: */ /* Network */ - /* SCSI */ - next_scsi_init(pcdev); /* DMA */ memory_region_init_io(&m->dmamem, NULL, &next_dma_ops, machine, From 9364f7b875ba6db906014b43cde0d9c80ba159d8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:44 +0000 Subject: [PATCH 0729/2892] next-cube: introduce next_pc_init() object init function Move initialisation of the memory regions and GPIOs from next_pc_realize() to the new next_pc_init() function. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-6-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 97dea45b13..c187a469d9 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -897,20 +897,24 @@ static void next_pc_reset(DeviceState *dev) static void next_pc_realize(DeviceState *dev, Error **errp) { - NeXTPC *s = NEXT_PC(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + /* SCSI */ + next_scsi_init(dev); +} - qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS); +static void next_pc_init(Object *obj) +{ + NeXTPC *s = NEXT_PC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + qdev_init_gpio_in(DEVICE(obj), next_irq, NEXT_NUM_IRQS); memory_region_init_io(&s->mmiomem, OBJECT(s), &next_mmio_ops, s, "next.mmio", 0x9000); memory_region_init_io(&s->scrmem, OBJECT(s), &next_scr_ops, s, "next.scr", 0x20000); + sysbus_init_mmio(sbd, &s->mmiomem); sysbus_init_mmio(sbd, &s->scrmem); - - /* SCSI */ - next_scsi_init(dev); } /* @@ -971,6 +975,7 @@ static void next_pc_class_init(ObjectClass *klass, void *data) static const TypeInfo next_pc_info = { .name = TYPE_NEXT_PC, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = next_pc_init, .instance_size = sizeof(NeXTPC), .class_init = next_pc_class_init, }; From acfa52bda68d3f12da5c7379693951818cfd7bc8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:45 +0000 Subject: [PATCH 0730/2892] next-cube: introduce next-scsi device This device is intended to hold the ESP SCSI controller and the NeXT SCSI CSRs. Start by creating the device and moving the ESP SCSI controller to be an embedded child device. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-7-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 101 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 23 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index c187a469d9..ce147fa9af 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -83,6 +83,18 @@ struct NeXTState { next_dma dma[10]; }; +#define TYPE_NEXT_SCSI "next-scsi" +OBJECT_DECLARE_SIMPLE_TYPE(NeXTSCSI, NEXT_SCSI) + +/* NeXT SCSI Controller */ +struct NeXTSCSI { + SysBusDevice parent_obj; + + MemoryRegion scsi_mem; + + SysBusESPState sysbus_esp; +}; + #define TYPE_NEXT_PC "next-pc" OBJECT_DECLARE_SIMPLE_TYPE(NeXTPC, NEXT_PC) @@ -94,7 +106,6 @@ struct NeXTPC { MemoryRegion mmiomem; MemoryRegion scrmem; - MemoryRegion scsimem; uint32_t scr1; uint32_t scr2; @@ -102,6 +113,8 @@ struct NeXTPC { uint32_t int_mask; uint32_t int_status; uint32_t led; + + NeXTSCSI next_scsi; uint8_t scsi_csr_1; uint8_t scsi_csr_2; @@ -825,38 +838,61 @@ static void nextscsi_write(void *opaque, uint8_t *buf, int size) nextdma_write(opaque, buf, size, NEXTDMA_SCSI); } -static void next_scsi_init(DeviceState *pcdev) +static void next_scsi_init(Object *obj) { - struct NeXTPC *next_pc = NEXT_PC(pcdev); - DeviceState *dev; - SysBusDevice *sysbusdev; - SysBusESPState *sysbus_esp; - ESPState *esp; + NeXTSCSI *s = NEXT_SCSI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - dev = qdev_new(TYPE_SYSBUS_ESP); - sysbus_esp = SYSBUS_ESP(dev); + object_initialize_child(obj, "esp", &s->sysbus_esp, TYPE_SYSBUS_ESP); + + memory_region_init(&s->scsi_mem, obj, "next.scsi", 0x40); + sysbus_init_mmio(sbd, &s->scsi_mem); +} + +static void next_scsi_realize(DeviceState *dev, Error **errp) +{ + NeXTSCSI *s = NEXT_SCSI(dev); + SysBusESPState *sysbus_esp; + SysBusDevice *sbd; + ESPState *esp; + NeXTPC *pcdev; + + pcdev = NEXT_PC(container_of(s, NeXTPC, next_scsi)); + + /* ESP */ + sysbus_esp = SYSBUS_ESP(&s->sysbus_esp); esp = &sysbus_esp->esp; esp->dma_memory_read = nextscsi_read; esp->dma_memory_write = nextscsi_write; esp->dma_opaque = pcdev; sysbus_esp->it_shift = 0; esp->dma_enabled = 1; - sysbusdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbusdev, &error_fatal); - sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(pcdev, NEXT_SCSI_I)); + sbd = SYS_BUS_DEVICE(sysbus_esp); + if (!sysbus_realize(sbd, errp)) { + return; + } + memory_region_add_subregion(&s->scsi_mem, 0x0, + sysbus_mmio_get_region(sbd, 0)); - memory_region_init(&next_pc->scsimem, OBJECT(next_pc), "next.scsi", 0x40); - memory_region_add_subregion(&next_pc->scsimem, 0x0, - sysbus_mmio_get_region(sysbusdev, 0)); - - memory_region_add_subregion(&next_pc->scrmem, 0x14000, &next_pc->scsimem); - - next_pc->scsi_reset = qdev_get_gpio_in(dev, 0); - next_pc->scsi_dma = qdev_get_gpio_in(dev, 1); - - scsi_bus_legacy_handle_cmdline(&esp->bus); + scsi_bus_legacy_handle_cmdline(&s->sysbus_esp.esp.bus); } +static void next_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NeXT SCSI Controller"; + dc->realize = next_scsi_realize; +} + +static const TypeInfo next_scsi_info = { + .name = TYPE_NEXT_SCSI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = next_scsi_init, + .instance_size = sizeof(NeXTSCSI), + .class_init = next_scsi_class_init, +}; + static void next_escc_init(DeviceState *pcdev) { DeviceState *dev; @@ -897,8 +933,24 @@ static void next_pc_reset(DeviceState *dev) static void next_pc_realize(DeviceState *dev, Error **errp) { + NeXTPC *s = NEXT_PC(dev); + SysBusDevice *sbd; + DeviceState *d; + /* SCSI */ - next_scsi_init(dev); + sbd = SYS_BUS_DEVICE(&s->next_scsi); + if (!sysbus_realize(sbd, errp)) { + return; + } + memory_region_add_subregion(&s->scrmem, 0x14000, + sysbus_mmio_get_region(sbd, 0)); + + d = DEVICE(object_resolve_path_component(OBJECT(&s->next_scsi), "esp")); + sysbus_connect_irq(SYS_BUS_DEVICE(d), 0, + qdev_get_gpio_in(DEVICE(s), NEXT_SCSI_I)); + + s->scsi_reset = qdev_get_gpio_in(d, 0); + s->scsi_dma = qdev_get_gpio_in(d, 1); } static void next_pc_init(Object *obj) @@ -915,6 +967,8 @@ static void next_pc_init(Object *obj) sysbus_init_mmio(sbd, &s->mmiomem); sysbus_init_mmio(sbd, &s->scrmem); + + object_initialize_child(obj, "next-scsi", &s->next_scsi, TYPE_NEXT_SCSI); } /* @@ -1089,6 +1143,7 @@ static void next_register_type(void) { type_register_static(&next_typeinfo); type_register_static(&next_pc_info); + type_register_static(&next_scsi_info); } type_init(next_register_type) From 92167b30b66bac29a354f4094a728b151409f004 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:46 +0000 Subject: [PATCH 0731/2892] next-cube: move SCSI CSRs from next-pc to the next-scsi device The SCSI CSRs are located within the SCSI subsystem of the NeXT PC (Peripheral Contoller) which is now modelled as a separate QEMU device. Add a new memory region subregion to contain the SCSI CSRs that simply store and retrieve the register values. Add a new VMStateDescription for the next-scsi device to enable the SCSI CSRs to be migrated. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-8-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 88 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 10 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index ce147fa9af..687d1b3cb0 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -93,6 +93,10 @@ struct NeXTSCSI { MemoryRegion scsi_mem; SysBusESPState sysbus_esp; + + MemoryRegion scsi_csr_mem; + uint8_t scsi_csr_1; + uint8_t scsi_csr_2; }; #define TYPE_NEXT_PC "next-pc" @@ -115,8 +119,6 @@ struct NeXTPC { uint32_t led; NeXTSCSI next_scsi; - uint8_t scsi_csr_1; - uint8_t scsi_csr_2; qemu_irq scsi_reset; qemu_irq scsi_dma; @@ -364,6 +366,7 @@ static const MemoryRegionOps next_mmio_ops = { static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) { NeXTPC *s = NEXT_PC(opaque); + NeXTSCSI *ns = NEXT_SCSI(&s->next_scsi); uint64_t val; switch (addr) { @@ -373,12 +376,12 @@ static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) break; case 0x14020: - DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1); - val = s->scsi_csr_1; + DPRINTF("SCSI 4020 STATUS READ %X\n", ns->scsi_csr_1); + val = ns->scsi_csr_1; break; case 0x14021: - DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2); + DPRINTF("SCSI 4021 STATUS READ %X\n", ns->scsi_csr_2); val = 0x40; break; @@ -411,6 +414,7 @@ static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, unsigned size) { NeXTPC *s = NEXT_PC(opaque); + NeXTSCSI *ns = NEXT_SCSI(&s->next_scsi); switch (addr) { case 0x14108: @@ -445,7 +449,7 @@ static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, DPRINTF("SCSICSR Reset\n"); /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ qemu_irq_raise(s->scsi_reset); - s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); + ns->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); qemu_irq_lower(s->scsi_reset); } if (val & SCSICSR_DMADIR) { @@ -838,6 +842,54 @@ static void nextscsi_write(void *opaque, uint8_t *buf, int size) nextdma_write(opaque, buf, size, NEXTDMA_SCSI); } +static void next_scsi_csr_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + NeXTSCSI *s = NEXT_SCSI(opaque); + + switch (addr) { + case 0: + s->scsi_csr_1 = val; + break; + + case 1: + s->scsi_csr_2 = val; + break; + + default: + g_assert_not_reached(); + } +} + +static uint64_t next_scsi_csr_read(void *opaque, hwaddr addr, unsigned size) +{ + NeXTSCSI *s = NEXT_SCSI(opaque); + uint64_t val; + + switch (addr) { + case 0: + val = s->scsi_csr_1; + break; + + case 1: + val = s->scsi_csr_2; + break; + + default: + g_assert_not_reached(); + } + + return val; +} + +static const MemoryRegionOps next_scsi_csr_ops = { + .read = next_scsi_csr_read, + .write = next_scsi_csr_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void next_scsi_init(Object *obj) { NeXTSCSI *s = NEXT_SCSI(obj); @@ -845,6 +897,9 @@ static void next_scsi_init(Object *obj) object_initialize_child(obj, "esp", &s->sysbus_esp, TYPE_SYSBUS_ESP); + memory_region_init_io(&s->scsi_csr_mem, obj, &next_scsi_csr_ops, + s, "csrs", 2); + memory_region_init(&s->scsi_mem, obj, "next.scsi", 0x40); sysbus_init_mmio(sbd, &s->scsi_mem); } @@ -874,15 +929,30 @@ static void next_scsi_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->scsi_mem, 0x0, sysbus_mmio_get_region(sbd, 0)); + /* SCSI CSRs */ + memory_region_add_subregion(&s->scsi_mem, 0x20, &s->scsi_csr_mem); + scsi_bus_legacy_handle_cmdline(&s->sysbus_esp.esp.bus); } +static const VMStateDescription next_scsi_vmstate = { + .name = "next-scsi", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(scsi_csr_1, NeXTSCSI), + VMSTATE_UINT8(scsi_csr_2, NeXTSCSI), + VMSTATE_END_OF_LIST() + }, +}; + static void next_scsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "NeXT SCSI Controller"; dc->realize = next_scsi_realize; + dc->vmsd = &next_scsi_vmstate; } static const TypeInfo next_scsi_info = { @@ -999,8 +1069,8 @@ static const VMStateDescription next_rtc_vmstate = { static const VMStateDescription next_pc_vmstate = { .name = "next-pc", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_UINT32(scr1, NeXTPC), VMSTATE_UINT32(scr2, NeXTPC), @@ -1008,8 +1078,6 @@ static const VMStateDescription next_pc_vmstate = { VMSTATE_UINT32(int_mask, NeXTPC), VMSTATE_UINT32(int_status, NeXTPC), VMSTATE_UINT32(led, NeXTPC), - VMSTATE_UINT8(scsi_csr_1, NeXTPC), - VMSTATE_UINT8(scsi_csr_2, NeXTPC), VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NextRtc), VMSTATE_END_OF_LIST() }, From 7bce8d12728f4d263b444508218f17da9bec6c3a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:47 +0000 Subject: [PATCH 0732/2892] next-cube: move SCSI 4020/4021 logic from next-pc device to next-scsi device The SCSI 4020/4021 logic refers to the offset of the SCSI CSRs within the NeXTCube address space. Due to the previously overlapping memory regions, there were duplicate MMIO accessors in the next.scr memory region for these registers but this has now been resolved. Move the remaining SCSI 4020/4021 logic from the next-pc device to the next-scsi device, with the exception that the SCSI 4021 register now returns its previous value like a normal register instead of a hardcoded 0x40 value. This also matches how the registers are implemented in the Previous emulator. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-9-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 139 ++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 77 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 687d1b3cb0..402e268f6b 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -365,8 +365,6 @@ static const MemoryRegionOps next_mmio_ops = { static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) { - NeXTPC *s = NEXT_PC(opaque); - NeXTSCSI *ns = NEXT_SCSI(&s->next_scsi); uint64_t val; switch (addr) { @@ -375,16 +373,6 @@ static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) val = 0x40 | 0x04 | 0x2 | 0x1; break; - case 0x14020: - DPRINTF("SCSI 4020 STATUS READ %X\n", ns->scsi_csr_1); - val = ns->scsi_csr_1; - break; - - case 0x14021: - DPRINTF("SCSI 4021 STATUS READ %X\n", ns->scsi_csr_2); - val = 0x40; - break; - /* * These 4 registers are the hardware timer, not sure which register * is the latch instead of data, but no problems so far. @@ -413,9 +401,6 @@ static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - NeXTPC *s = NEXT_PC(opaque); - NeXTSCSI *ns = NEXT_SCSI(&s->next_scsi); - switch (addr) { case 0x14108: DPRINTF("FDCSR Write: %"PRIx64 "\n", val); @@ -424,68 +409,6 @@ static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, } break; - case 0x14020: /* SCSI Control Register */ - if (val & SCSICSR_FIFOFL) { - DPRINTF("SCSICSR FIFO Flush\n"); - /* will have to add another irq to the esp if this is needed */ - /* esp_puflush_fifo(esp_g); */ - } - - if (val & SCSICSR_ENABLE) { - DPRINTF("SCSICSR Enable\n"); - /* - * qemu_irq_raise(s->scsi_dma); - * s->scsi_csr_1 = 0xc0; - * s->scsi_csr_1 |= 0x1; - * qemu_irq_pulse(s->scsi_dma); - */ - } - /* - * else - * s->scsi_csr_1 &= ~SCSICSR_ENABLE; - */ - - if (val & SCSICSR_RESET) { - DPRINTF("SCSICSR Reset\n"); - /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ - qemu_irq_raise(s->scsi_reset); - ns->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); - qemu_irq_lower(s->scsi_reset); - } - if (val & SCSICSR_DMADIR) { - DPRINTF("SCSICSR DMAdir\n"); - } - if (val & SCSICSR_CPUDMA) { - DPRINTF("SCSICSR CPUDMA\n"); - /* qemu_irq_raise(s->scsi_dma); */ - s->int_status |= 0x4000000; - } else { - /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */ - s->int_status &= ~(0x4000000); - /* qemu_irq_lower(s->scsi_dma); */ - } - if (val & SCSICSR_INTMASK) { - DPRINTF("SCSICSR INTMASK\n"); - /* - * int_mask &= ~0x1000; - * s->scsi_csr_1 |= val; - * s->scsi_csr_1 &= ~SCSICSR_INTMASK; - * if (s->scsi_queued) { - * s->scsi_queued = 0; - * next_irq(s, NEXT_SCSI_I, level); - * } - */ - } else { - /* int_mask |= 0x1000; */ - } - if (val & 0x80) { - /* int_mask |= 0x1000; */ - /* s->scsi_csr_1 |= 0x80; */ - } - DPRINTF("SCSICSR Write: %"PRIx64 "\n", val); - /* s->scsi_csr_1 = val; */ - break; - /* Hardware timer latch - not implemented yet */ case 0x1a000: default: @@ -846,13 +769,73 @@ static void next_scsi_csr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { NeXTSCSI *s = NEXT_SCSI(opaque); + NeXTPC *pc = NEXT_PC(container_of(s, NeXTPC, next_scsi)); switch (addr) { case 0: + if (val & SCSICSR_FIFOFL) { + DPRINTF("SCSICSR FIFO Flush\n"); + /* will have to add another irq to the esp if this is needed */ + /* esp_puflush_fifo(esp_g); */ + } + + if (val & SCSICSR_ENABLE) { + DPRINTF("SCSICSR Enable\n"); + /* + * qemu_irq_raise(s->scsi_dma); + * s->scsi_csr_1 = 0xc0; + * s->scsi_csr_1 |= 0x1; + * qemu_irq_pulse(s->scsi_dma); + */ + } + /* + * else + * s->scsi_csr_1 &= ~SCSICSR_ENABLE; + */ + + if (val & SCSICSR_RESET) { + DPRINTF("SCSICSR Reset\n"); + /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ + qemu_irq_raise(pc->scsi_reset); + s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); + qemu_irq_lower(pc->scsi_reset); + } + if (val & SCSICSR_DMADIR) { + DPRINTF("SCSICSR DMAdir\n"); + } + if (val & SCSICSR_CPUDMA) { + DPRINTF("SCSICSR CPUDMA\n"); + /* qemu_irq_raise(s->scsi_dma); */ + pc->int_status |= 0x4000000; + } else { + /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */ + pc->int_status &= ~(0x4000000); + /* qemu_irq_lower(s->scsi_dma); */ + } + if (val & SCSICSR_INTMASK) { + DPRINTF("SCSICSR INTMASK\n"); + /* + * int_mask &= ~0x1000; + * s->scsi_csr_1 |= val; + * s->scsi_csr_1 &= ~SCSICSR_INTMASK; + * if (s->scsi_queued) { + * s->scsi_queued = 0; + * next_irq(s, NEXT_SCSI_I, level); + * } + */ + } else { + /* int_mask |= 0x1000; */ + } + if (val & 0x80) { + /* int_mask |= 0x1000; */ + /* s->scsi_csr_1 |= 0x80; */ + } + DPRINTF("SCSICSR1 Write: %"PRIx64 "\n", val); s->scsi_csr_1 = val; break; case 1: + DPRINTF("SCSICSR2 Write: %"PRIx64 "\n", val); s->scsi_csr_2 = val; break; @@ -868,10 +851,12 @@ static uint64_t next_scsi_csr_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { case 0: + DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1); val = s->scsi_csr_1; break; case 1: + DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2); val = s->scsi_csr_2; break; From df219805911aa91f7c6b84c821d717b4765a76df Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:48 +0000 Subject: [PATCH 0733/2892] next-cube: move floppy disk MMIO to separate memory region in next-pc The dummy floppy disk device is part of the next-pc device, and not related to the NeXTCube SCRs. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-10-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 61 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 402e268f6b..4d7fcdd943 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -108,6 +108,7 @@ struct NeXTPC { M68kCPU *cpu; + MemoryRegion floppy_mem; MemoryRegion mmiomem; MemoryRegion scrmem; @@ -368,11 +369,6 @@ static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) uint64_t val; switch (addr) { - case 0x14108: - DPRINTF("FD read @ %x\n", (unsigned int)addr); - val = 0x40 | 0x04 | 0x2 | 0x1; - break; - /* * These 4 registers are the hardware timer, not sure which register * is the latch instead of data, but no problems so far. @@ -402,13 +398,6 @@ static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, unsigned size) { switch (addr) { - case 0x14108: - DPRINTF("FDCSR Write: %"PRIx64 "\n", val); - if (val == 0x0) { - /* qemu_irq_raise(s->fd_irq[0]); */ - } - break; - /* Hardware timer latch - not implemented yet */ case 0x1a000: default: @@ -948,6 +937,47 @@ static const TypeInfo next_scsi_info = { .class_init = next_scsi_class_init, }; +static void next_floppy_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + switch (addr) { + case 0: + DPRINTF("FDCSR Write: %"PRIx64 "\n", val); + if (val == 0x0) { + /* qemu_irq_raise(s->fd_irq[0]); */ + } + break; + + default: + g_assert_not_reached(); + } +} + +static uint64_t next_floppy_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + + switch (addr) { + case 0: + DPRINTF("FD read @ %x\n", (unsigned int)addr); + val = 0x40 | 0x04 | 0x2 | 0x1; + break; + + default: + g_assert_not_reached(); + } + + return val; +} + +static const MemoryRegionOps next_floppy_ops = { + .read = next_floppy_read, + .write = next_floppy_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void next_escc_init(DeviceState *pcdev) { DeviceState *dev; @@ -1006,6 +1036,10 @@ static void next_pc_realize(DeviceState *dev, Error **errp) s->scsi_reset = qdev_get_gpio_in(d, 0); s->scsi_dma = qdev_get_gpio_in(d, 1); + + /* Floppy */ + memory_region_add_subregion(&s->scrmem, 0x14108, + &s->floppy_mem); } static void next_pc_init(Object *obj) @@ -1024,6 +1058,9 @@ static void next_pc_init(Object *obj) sysbus_init_mmio(sbd, &s->scrmem); object_initialize_child(obj, "next-scsi", &s->next_scsi, TYPE_NEXT_SCSI); + + memory_region_init_io(&s->floppy_mem, OBJECT(s), &next_floppy_ops, s, + "next.floppy", 4); } /* From f85929270c584541c1ae1115ae2edd725192c56f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:49 +0000 Subject: [PATCH 0734/2892] next-cube: map ESCC registers as a subregion of the next.scr memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the ESCC device exists within the memory range of the next.scr memory region, map the ESCC device registers as a subregion of the next.scr memory region instead of directly to the system address space. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-11-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 4d7fcdd943..6ddd9ad0ec 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -980,6 +980,7 @@ static const MemoryRegionOps next_floppy_ops = { static void next_escc_init(DeviceState *pcdev) { + NeXTPC *next_pc = NEXT_PC(pcdev); DeviceState *dev; SysBusDevice *s; @@ -997,7 +998,9 @@ static void next_escc_init(DeviceState *pcdev) sysbus_realize_and_unref(s, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(pcdev, NEXT_SCC_I)); sysbus_connect_irq(s, 1, qdev_get_gpio_in(pcdev, NEXT_SCC_DMA_I)); - sysbus_mmio_map(s, 0, 0x2118000); + + memory_region_add_subregion(&next_pc->scrmem, 0x18000, + sysbus_mmio_get_region(s, 0)); } static void next_pc_reset(DeviceState *dev) From bdde194414e4ec63b90535a98d89093035a906e6 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:50 +0000 Subject: [PATCH 0735/2892] next-cube: move ESCC to be QOM child of next-pc device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the ESCC is part of the next-pc device, move the ESCC to be a QOM child of the next-pc device. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-12-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 54 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 6ddd9ad0ec..9f49c33bdd 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -124,6 +124,8 @@ struct NeXTPC { qemu_irq scsi_reset; qemu_irq scsi_dma; + ESCCState escc; + NextRtc rtc; }; @@ -978,31 +980,6 @@ static const MemoryRegionOps next_floppy_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void next_escc_init(DeviceState *pcdev) -{ - NeXTPC *next_pc = NEXT_PC(pcdev); - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new(TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", 9600 * 384); - qdev_prop_set_uint32(dev, "it_shift", 0); - qdev_prop_set_bit(dev, "bit_swap", true); - qdev_prop_set_chr(dev, "chrB", serial_hd(1)); - qdev_prop_set_chr(dev, "chrA", serial_hd(0)); - qdev_prop_set_uint32(dev, "chnBtype", escc_serial); - qdev_prop_set_uint32(dev, "chnAtype", escc_serial); - - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, qdev_get_gpio_in(pcdev, NEXT_SCC_I)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in(pcdev, NEXT_SCC_DMA_I)); - - memory_region_add_subregion(&next_pc->scrmem, 0x18000, - sysbus_mmio_get_region(s, 0)); -} - static void next_pc_reset(DeviceState *dev) { NeXTPC *s = NEXT_PC(dev); @@ -1043,6 +1020,28 @@ static void next_pc_realize(DeviceState *dev, Error **errp) /* Floppy */ memory_region_add_subregion(&s->scrmem, 0x14108, &s->floppy_mem); + + /* ESCC */ + d = DEVICE(&s->escc); + qdev_prop_set_uint32(d, "disabled", 0); + qdev_prop_set_uint32(d, "frequency", 9600 * 384); + qdev_prop_set_uint32(d, "it_shift", 0); + qdev_prop_set_bit(d, "bit_swap", true); + qdev_prop_set_chr(d, "chrB", serial_hd(1)); + qdev_prop_set_chr(d, "chrA", serial_hd(0)); + qdev_prop_set_uint32(d, "chnBtype", escc_serial); + qdev_prop_set_uint32(d, "chnAtype", escc_serial); + + sbd = SYS_BUS_DEVICE(d); + if (!sysbus_realize(sbd, errp)) { + return; + } + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(dev, NEXT_SCC_I)); + sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(dev, NEXT_SCC_DMA_I)); + + memory_region_add_subregion(&s->scrmem, 0x18000, + sysbus_mmio_get_region(sbd, 0)); + } static void next_pc_init(Object *obj) @@ -1064,6 +1063,8 @@ static void next_pc_init(Object *obj) memory_region_init_io(&s->floppy_mem, OBJECT(s), &next_floppy_ops, s, "next.floppy", 4); + + object_initialize_child(obj, "escc", &s->escc, TYPE_ESCC); } /* @@ -1200,9 +1201,6 @@ static void next_cube_init(MachineState *machine) } } - /* Serial */ - next_escc_init(pcdev); - /* TODO: */ /* Network */ From 71936afe418c1900880d22265fc6da4c5c4ed865 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:51 +0000 Subject: [PATCH 0736/2892] next-cube: move timer MMIO to separate memory region on next-pc device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the timer MMIO accesses to a separate memory region on the next-pc device instead of being part of the next.scr MMIO memory region. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-13-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 63 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 9f49c33bdd..ba468ce922 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -109,6 +109,7 @@ struct NeXTPC { M68kCPU *cpu; MemoryRegion floppy_mem; + MemoryRegion timer_mem; MemoryRegion mmiomem; MemoryRegion scrmem; @@ -371,17 +372,6 @@ static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) uint64_t val; switch (addr) { - /* - * These 4 registers are the hardware timer, not sure which register - * is the latch instead of data, but no problems so far. - * - * Hack: We need to have the LSB change consistently to make it work - */ - case 0x1a000 ... 0x1a003: - val = extract32(clock(), (4 - (addr - 0x1a000) - size) << 3, - size << 3); - break; - /* For now return dummy byte to allow the Ethernet test to timeout */ case 0x6000: val = 0xff; @@ -400,8 +390,6 @@ static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, unsigned size) { switch (addr) { - /* Hardware timer latch - not implemented yet */ - case 0x1a000: default: DPRINTF("BMAP Write @ 0x%x with 0x%"PRIx64 " size %u\n", (unsigned int)addr, val, size); @@ -980,6 +968,50 @@ static const MemoryRegionOps next_floppy_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void next_timer_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + switch (addr) { + case 0 ... 3: + /* Hardware timer latch - not implemented yet */ + break; + + default: + g_assert_not_reached(); + } +} + +static uint64_t next_timer_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + + switch (addr) { + case 0 ... 3: + /* + * These 4 registers are the hardware timer, not sure which register + * is the latch instead of data, but no problems so far. + * + * Hack: We need to have the LSB change consistently to make it work + */ + val = extract32(clock(), (4 - addr - size) << 3, + size << 3); + break; + + default: + g_assert_not_reached(); + } + + return val; +} + +static const MemoryRegionOps next_timer_ops = { + .read = next_timer_read, + .write = next_timer_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void next_pc_reset(DeviceState *dev) { NeXTPC *s = NEXT_PC(dev); @@ -1042,6 +1074,8 @@ static void next_pc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->scrmem, 0x18000, sysbus_mmio_get_region(sbd, 0)); + /* Timer */ + memory_region_add_subregion(&s->scrmem, 0x1a000, &s->timer_mem); } static void next_pc_init(Object *obj) @@ -1065,6 +1099,9 @@ static void next_pc_init(Object *obj) "next.floppy", 4); object_initialize_child(obj, "escc", &s->escc, TYPE_ESCC); + + memory_region_init_io(&s->timer_mem, OBJECT(s), &next_timer_ops, s, + "next.timer", 4); } /* From 292ab2faa7bce3a66c855bf80dd134e4156fa9d7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:52 +0000 Subject: [PATCH 0737/2892] next-cube: move en ethernet MMIO to separate memory region on next-pc device Move the en ethernet MMIO accesses to a separate memory region on the next-pc device instead of being part of the next.scr MMIO memory region. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-14-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 48 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index ba468ce922..97a6f6c472 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -110,6 +110,7 @@ struct NeXTPC { MemoryRegion floppy_mem; MemoryRegion timer_mem; + MemoryRegion dummyen_mem; MemoryRegion mmiomem; MemoryRegion scrmem; @@ -372,11 +373,6 @@ static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) uint64_t val; switch (addr) { - /* For now return dummy byte to allow the Ethernet test to timeout */ - case 0x6000: - val = 0xff; - break; - default: DPRINTF("BMAP Read @ 0x%x size %u\n", (unsigned int)addr, size); val = 0; @@ -1012,6 +1008,38 @@ static const MemoryRegionOps next_timer_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void next_dummy_en_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + /* Do nothing */ + return; +} + +static uint64_t next_dummy_en_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + + switch (addr) { + case 0: + /* For now return dummy byte to allow the Ethernet test to timeout */ + val = 0xff; + break; + + default: + val = 0; + } + + return val; +} + +static const MemoryRegionOps next_dummy_en_ops = { + .read = next_dummy_en_read, + .write = next_dummy_en_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void next_pc_reset(DeviceState *dev) { NeXTPC *s = NEXT_PC(dev); @@ -1034,6 +1062,10 @@ static void next_pc_realize(DeviceState *dev, Error **errp) SysBusDevice *sbd; DeviceState *d; + /* en network (dummy) */ + memory_region_add_subregion(&s->scrmem, 0x6000, + &s->dummyen_mem); + /* SCSI */ sbd = SYS_BUS_DEVICE(&s->next_scsi); if (!sysbus_realize(sbd, errp)) { @@ -1093,6 +1125,9 @@ static void next_pc_init(Object *obj) sysbus_init_mmio(sbd, &s->mmiomem); sysbus_init_mmio(sbd, &s->scrmem); + memory_region_init_io(&s->dummyen_mem, OBJECT(s), &next_dummy_en_ops, s, + "next.en", 0x20); + object_initialize_child(obj, "next-scsi", &s->next_scsi, TYPE_NEXT_SCSI); memory_region_init_io(&s->floppy_mem, OBJECT(s), &next_floppy_ops, s, @@ -1238,9 +1273,6 @@ static void next_cube_init(MachineState *machine) } } - /* TODO: */ - /* Network */ - /* DMA */ memory_region_init_io(&m->dmamem, NULL, &next_dma_ops, machine, "next.dma", 0x5000); From c178be67fdece8e0184b4014233c918899198fcb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:53 +0000 Subject: [PATCH 0738/2892] next-cube: add empty slots for unknown accesses to next.scr memory region The next.scr memory is now effectively unused, however there are 3 separate region accesses still logged that occur when booting a NeXTStep disk image. Use the empty_slot device to capture and ignore memory accesses to these 3 memory regions. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-15-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/Kconfig | 1 + hw/m68k/next-cube.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index 0092cda4e9..aff769b30f 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -18,6 +18,7 @@ config NEXTCUBE depends on M68K select FRAMEBUFFER select ESCC + select EMPTY_SLOT config Q800 bool diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 97a6f6c472..6ca9170108 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -22,6 +22,7 @@ #include "qom/object.h" #include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */ #include "hw/block/fdc.h" +#include "hw/misc/empty_slot.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -1238,6 +1239,13 @@ static void next_cube_init(MachineState *machine) /* BMAP IO - acts as a catch-all for now */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000); + /* unknown: Brightness control register? */ + empty_slot_init("next.unknown.0", 0x02110000, 0x10); + /* unknown: Magneto-Optical drive controller? */ + empty_slot_init("next.unknown.1", 0x02112000, 0x10); + /* unknown: Serial clock configuration register? */ + empty_slot_init("next.unknown.2", 0x02118004, 0x10); + /* BMAP memory */ memory_region_init_ram_flags_nomigrate(&m->bmapm1, NULL, "next.bmapmem", 64, RAM_SHARED, &error_fatal); From d06a0ca96a24e4a2b70f3ae52fead31316621920 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:54 +0000 Subject: [PATCH 0739/2892] next-cube: remove unused next.scr memory region Now that the next.scr memory region is unused it can be removed and the next-pc devices mapped directly within the machine init function. This is the last remaining overlapping memory region within the NeXTCube machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-16-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 73 +++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 6ca9170108..73c4d4ea9c 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -369,38 +369,6 @@ static const MemoryRegionOps next_mmio_ops = { #define SCSICSR_CPUDMA 0x10 /* if set, dma enabled */ #define SCSICSR_INTMASK 0x20 /* if set, interrupt enabled */ -static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) -{ - uint64_t val; - - switch (addr) { - default: - DPRINTF("BMAP Read @ 0x%x size %u\n", (unsigned int)addr, size); - val = 0; - break; - } - - return val; -} - -static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - switch (addr) { - default: - DPRINTF("BMAP Write @ 0x%x with 0x%"PRIx64 " size %u\n", - (unsigned int)addr, val, size); - } -} - -static const MemoryRegionOps next_scr_ops = { - .read = next_scr_readfn, - .write = next_scr_writefn, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - #define NEXTDMA_SCSI(x) (0x10 + x) #define NEXTDMA_FD(x) (0x10 + x) #define NEXTDMA_ENTX(x) (0x110 + x) @@ -1063,17 +1031,11 @@ static void next_pc_realize(DeviceState *dev, Error **errp) SysBusDevice *sbd; DeviceState *d; - /* en network (dummy) */ - memory_region_add_subregion(&s->scrmem, 0x6000, - &s->dummyen_mem); - /* SCSI */ sbd = SYS_BUS_DEVICE(&s->next_scsi); if (!sysbus_realize(sbd, errp)) { return; } - memory_region_add_subregion(&s->scrmem, 0x14000, - sysbus_mmio_get_region(sbd, 0)); d = DEVICE(object_resolve_path_component(OBJECT(&s->next_scsi), "esp")); sysbus_connect_irq(SYS_BUS_DEVICE(d), 0, @@ -1082,10 +1044,6 @@ static void next_pc_realize(DeviceState *dev, Error **errp) s->scsi_reset = qdev_get_gpio_in(d, 0); s->scsi_dma = qdev_get_gpio_in(d, 1); - /* Floppy */ - memory_region_add_subregion(&s->scrmem, 0x14108, - &s->floppy_mem); - /* ESCC */ d = DEVICE(&s->escc); qdev_prop_set_uint32(d, "disabled", 0); @@ -1103,12 +1061,6 @@ static void next_pc_realize(DeviceState *dev, Error **errp) } sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(dev, NEXT_SCC_I)); sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(dev, NEXT_SCC_DMA_I)); - - memory_region_add_subregion(&s->scrmem, 0x18000, - sysbus_mmio_get_region(sbd, 0)); - - /* Timer */ - memory_region_add_subregion(&s->scrmem, 0x1a000, &s->timer_mem); } static void next_pc_init(Object *obj) @@ -1120,24 +1072,27 @@ static void next_pc_init(Object *obj) memory_region_init_io(&s->mmiomem, OBJECT(s), &next_mmio_ops, s, "next.mmio", 0x9000); - memory_region_init_io(&s->scrmem, OBJECT(s), &next_scr_ops, s, - "next.scr", 0x20000); - sysbus_init_mmio(sbd, &s->mmiomem); - sysbus_init_mmio(sbd, &s->scrmem); memory_region_init_io(&s->dummyen_mem, OBJECT(s), &next_dummy_en_ops, s, "next.en", 0x20); + sysbus_init_mmio(sbd, &s->dummyen_mem); object_initialize_child(obj, "next-scsi", &s->next_scsi, TYPE_NEXT_SCSI); + sysbus_init_mmio(sbd, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->next_scsi), 0)); memory_region_init_io(&s->floppy_mem, OBJECT(s), &next_floppy_ops, s, "next.floppy", 4); + sysbus_init_mmio(sbd, &s->floppy_mem); object_initialize_child(obj, "escc", &s->escc, TYPE_ESCC); + sysbus_init_mmio(sbd, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->escc), 0)); memory_region_init_io(&s->timer_mem, OBJECT(s), &next_timer_ops, s, "next.timer", 4); + sysbus_init_mmio(sbd, &s->timer_mem); } /* @@ -1239,13 +1194,27 @@ static void next_cube_init(MachineState *machine) /* BMAP IO - acts as a catch-all for now */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000); + /* en network (dummy) */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02106000); + /* unknown: Brightness control register? */ empty_slot_init("next.unknown.0", 0x02110000, 0x10); /* unknown: Magneto-Optical drive controller? */ empty_slot_init("next.unknown.1", 0x02112000, 0x10); + + /* SCSI */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 2, 0x02114000); + /* Floppy */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 3, 0x02114108); + /* ESCC */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 4, 0x02118000); + /* unknown: Serial clock configuration register? */ empty_slot_init("next.unknown.2", 0x02118004, 0x10); + /* Timer */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 5, 0x0211a000); + /* BMAP memory */ memory_region_init_ram_flags_nomigrate(&m->bmapm1, NULL, "next.bmapmem", 64, RAM_SHARED, &error_fatal); From 9f15303ce29fdd422e5b571b78979abd97005267 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:55 +0000 Subject: [PATCH 0740/2892] next-cube: rearrange NeXTState declarations to improve readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the NeXTState, next_dma and TYPE_NEXT_MACHINE definition to the same area at the top of next-cube.c. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-17-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 64 ++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 73c4d4ea9c..0cf4470ce8 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -38,30 +38,10 @@ #define DPRINTF(fmt, ...) do { } while (0) #endif -#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube") -OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, NEXT_MACHINE) - #define ENTRY 0x0100001e #define RAM_SIZE 0x4000000 #define ROM_FILE "Rev_2.5_v66.bin" -typedef struct next_dma { - uint32_t csr; - - uint32_t saved_next; - uint32_t saved_limit; - uint32_t saved_start; - uint32_t saved_stop; - - uint32_t next; - uint32_t limit; - uint32_t start; - uint32_t stop; - - uint32_t next_initbuf; - uint32_t size; -} next_dma; - typedef struct NextRtc { int8_t phase; uint8_t ram[32]; @@ -72,18 +52,6 @@ typedef struct NextRtc { uint8_t retval; } NextRtc; -struct NeXTState { - MachineState parent; - - MemoryRegion rom; - MemoryRegion rom2; - MemoryRegion dmamem; - MemoryRegion bmapm1; - MemoryRegion bmapm2; - - next_dma dma[10]; -}; - #define TYPE_NEXT_SCSI "next-scsi" OBJECT_DECLARE_SIMPLE_TYPE(NeXTSCSI, NEXT_SCSI) @@ -132,6 +100,38 @@ struct NeXTPC { NextRtc rtc; }; +typedef struct next_dma { + uint32_t csr; + + uint32_t saved_next; + uint32_t saved_limit; + uint32_t saved_start; + uint32_t saved_stop; + + uint32_t next; + uint32_t limit; + uint32_t start; + uint32_t stop; + + uint32_t next_initbuf; + uint32_t size; +} next_dma; + +#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube") +OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, NEXT_MACHINE) + +struct NeXTState { + MachineState parent; + + MemoryRegion rom; + MemoryRegion rom2; + MemoryRegion dmamem; + MemoryRegion bmapm1; + MemoryRegion bmapm2; + + next_dma dma[10]; +}; + /* Thanks to NeXT forums for this */ /* static const uint8_t rtc_ram3[32] = { From ce788d3740f7510be7a40dea5e0856d0986f350a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:56 +0000 Subject: [PATCH 0741/2892] next-cube: convert next-pc device to use Resettable interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Acked-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-18-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 0cf4470ce8..091e05465e 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -1009,9 +1009,9 @@ static const MemoryRegionOps next_dummy_en_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void next_pc_reset(DeviceState *dev) +static void next_pc_reset_hold(Object *obj, ResetType type) { - NeXTPC *s = NEXT_PC(dev); + NeXTPC *s = NEXT_PC(obj); /* Set internal registers to initial values */ /* 0x0000XX00 << vital bits */ @@ -1140,12 +1140,13 @@ static const VMStateDescription next_pc_vmstate = { static void next_pc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->desc = "NeXT Peripheral Controller"; dc->realize = next_pc_realize; - device_class_set_legacy_reset(dc, next_pc_reset); device_class_set_props(dc, next_pc_properties); dc->vmsd = &next_pc_vmstate; + rc->phases.hold = next_pc_reset_hold; } static const TypeInfo next_pc_info = { From 214de32ea832f896afd5c66eb408979e6f99bea0 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:57 +0000 Subject: [PATCH 0742/2892] next-cube: rename typedef struct NextRtc to NeXTRTC This brings the capitalisation in line with the other NeXTCube definitions. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-19-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 091e05465e..19b9100094 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -42,7 +42,7 @@ #define RAM_SIZE 0x4000000 #define ROM_FILE "Rev_2.5_v66.bin" -typedef struct NextRtc { +typedef struct NeXTRTC { int8_t phase; uint8_t ram[32]; uint8_t command; @@ -50,7 +50,7 @@ typedef struct NextRtc { uint8_t status; uint8_t control; uint8_t retval; -} NextRtc; +} NeXTRTC; #define TYPE_NEXT_SCSI "next-scsi" OBJECT_DECLARE_SIMPLE_TYPE(NeXTSCSI, NEXT_SCSI) @@ -97,7 +97,7 @@ struct NeXTPC { ESCCState escc; - NextRtc rtc; + NeXTRTC rtc; }; typedef struct next_dma { @@ -167,7 +167,7 @@ static void next_scr2_led_update(NeXTPC *s) static void next_scr2_rtc_update(NeXTPC *s) { uint8_t old_scr2, scr2_2; - NextRtc *rtc = &s->rtc; + NeXTRTC *rtc = &s->rtc; old_scr2 = extract32(s->old_scr2, 8, 8); scr2_2 = extract32(s->scr2, 8, 8); @@ -1110,13 +1110,13 @@ static const VMStateDescription next_rtc_vmstate = { .version_id = 2, .minimum_version_id = 2, .fields = (const VMStateField[]) { - VMSTATE_INT8(phase, NextRtc), - VMSTATE_UINT8_ARRAY(ram, NextRtc, 32), - VMSTATE_UINT8(command, NextRtc), - VMSTATE_UINT8(value, NextRtc), - VMSTATE_UINT8(status, NextRtc), - VMSTATE_UINT8(control, NextRtc), - VMSTATE_UINT8(retval, NextRtc), + VMSTATE_INT8(phase, NeXTRTC), + VMSTATE_UINT8_ARRAY(ram, NeXTRTC, 32), + VMSTATE_UINT8(command, NeXTRTC), + VMSTATE_UINT8(value, NeXTRTC), + VMSTATE_UINT8(status, NeXTRTC), + VMSTATE_UINT8(control, NeXTRTC), + VMSTATE_UINT8(retval, NeXTRTC), VMSTATE_END_OF_LIST() }, }; @@ -1132,7 +1132,7 @@ static const VMStateDescription next_pc_vmstate = { VMSTATE_UINT32(int_mask, NeXTPC), VMSTATE_UINT32(int_status, NeXTPC), VMSTATE_UINT32(led, NeXTPC), - VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NextRtc), + VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NeXTRTC), VMSTATE_END_OF_LIST() }, }; From 825ac1256426b196daac626f96054a152bb5f887 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:58 +0000 Subject: [PATCH 0743/2892] next-cube: use qemu_irq to drive int_status in next_scr2_rtc_update() Rather than directly clear bit 3 in int_status in next_scr2_rtc_update(), use a qemu_irq to drive the equivalent NEXT_PWR_I signal. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-20-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 19b9100094..12f8ecd2d4 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -98,6 +98,7 @@ struct NeXTPC { ESCCState escc; NeXTRTC rtc; + qemu_irq rtc_power_irq; }; typedef struct next_dma { @@ -267,7 +268,7 @@ static void next_scr2_rtc_update(NeXTPC *s) /* clear FTU */ if (rtc->value & 0x04) { rtc->status = rtc->status & (~0x18); - s->int_status = s->int_status & (~0x04); + qemu_irq_lower(s->rtc_power_irq); } } } @@ -1093,6 +1094,8 @@ static void next_pc_init(Object *obj) memory_region_init_io(&s->timer_mem, OBJECT(s), &next_timer_ops, s, "next.timer", 4); sysbus_init_mmio(sbd, &s->timer_mem); + + s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); } /* From b37da8b95f50f355d173789bec8419f01371473e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 12:59:59 +0000 Subject: [PATCH 0744/2892] next-cube: separate rtc read and write shift logic Introduce a new next_rtc_cmd_is_write() function to determine if an rtc command is a read or write, and start by using it to avoid shifting the rtc input value if a rtc read command is executed. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-21-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 142 ++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 12f8ecd2d4..eb7d3a9cbd 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -165,6 +165,12 @@ static void next_scr2_led_update(NeXTPC *s) } } +static bool next_rtc_cmd_is_write(uint8_t cmd) +{ + return (cmd >= 0x80 && cmd <= 0x9f) || + (cmd == 0xb1); +} + static void next_scr2_rtc_update(NeXTPC *s) { uint8_t old_scr2, scr2_2; @@ -186,76 +192,80 @@ static void next_scr2_rtc_update(NeXTPC *s) ((scr2_2 & SCR2_RTDATA) ? 1 : 0); } if (rtc->phase >= 8 && rtc->phase < 16) { - rtc->value = (rtc->value << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + if (next_rtc_cmd_is_write(rtc->command)) { + /* Shift in value to write */ + rtc->value = (rtc->value << 1) | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + } else { + /* Shift out value to read */ - /* if we read RAM register, output RT_DATA bit */ - if (rtc->command <= 0x1F) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->ram[rtc->command] & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; + /* if we read RAM register, output RT_DATA bit */ + if (rtc->command <= 0x1F) { + scr2_2 = scr2_2 & (~SCR2_RTDATA); + if (rtc->ram[rtc->command] & + (0x80 >> (rtc->phase - 8))) { + scr2_2 |= SCR2_RTDATA; + } + + rtc->retval = (rtc->retval << 1) | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + } + /* read the status 0x30 */ + if (rtc->command == 0x30) { + scr2_2 = scr2_2 & (~SCR2_RTDATA); + /* for now status = 0x98 (new rtc + FTU) */ + if (rtc->status & (0x80 >> (rtc->phase - 8))) { + scr2_2 |= SCR2_RTDATA; + } + + rtc->retval = (rtc->retval << 1) | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + } + /* read the status 0x31 */ + if (rtc->command == 0x31) { + scr2_2 = scr2_2 & (~SCR2_RTDATA); + if (rtc->control & (0x80 >> (rtc->phase - 8))) { + scr2_2 |= SCR2_RTDATA; + } + rtc->retval = (rtc->retval << 1) | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) { + scr2_2 = scr2_2 & (~SCR2_RTDATA); + /* for now 0x00 */ + time_t time_h = time(NULL); + struct tm *info = localtime(&time_h); + int ret = 0; + + switch (rtc->command) { + case 0x20: + ret = SCR2_TOBCD(info->tm_sec); + break; + case 0x21: + ret = SCR2_TOBCD(info->tm_min); + break; + case 0x22: + ret = SCR2_TOBCD(info->tm_hour); + break; + case 0x24: + ret = SCR2_TOBCD(info->tm_mday); + break; + case 0x25: + ret = SCR2_TOBCD((info->tm_mon + 1)); + break; + case 0x26: + ret = SCR2_TOBCD((info->tm_year - 100)); + break; + } + + if (ret & (0x80 >> (rtc->phase - 8))) { + scr2_2 |= SCR2_RTDATA; + } + rtc->retval = (rtc->retval << 1) | + ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + } } - /* read the status 0x30 */ - if (rtc->command == 0x30) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - /* for now status = 0x98 (new rtc + FTU) */ - if (rtc->status & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - /* read the status 0x31 */ - if (rtc->command == 0x31) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->control & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - - if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - /* for now 0x00 */ - time_t time_h = time(NULL); - struct tm *info = localtime(&time_h); - int ret = 0; - - switch (rtc->command) { - case 0x20: - ret = SCR2_TOBCD(info->tm_sec); - break; - case 0x21: - ret = SCR2_TOBCD(info->tm_min); - break; - case 0x22: - ret = SCR2_TOBCD(info->tm_hour); - break; - case 0x24: - ret = SCR2_TOBCD(info->tm_mday); - break; - case 0x25: - ret = SCR2_TOBCD((info->tm_mon + 1)); - break; - case 0x26: - ret = SCR2_TOBCD((info->tm_year - 100)); - break; - - } - - if (ret & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - } rtc->phase++; From bbcaced2bfc9865f37ea4e4988af2108abadee51 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:00 +0000 Subject: [PATCH 0745/2892] next-cube: always use retval to return rtc read values Instead of shifting out rtc read values from individual rtc registers, change the logic so that rtc read commands are executed when the last bit of the rtc command is received and the result stored in retval. This simplifies the rtc read logic such that the shift out logic can be consolidated for rtc phases between 8 and 16. Signed-off-by: Mark Cave-Ayland Message-ID: <20241222130012.1013374-22-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 123 +++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index eb7d3a9cbd..a39117144c 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -190,6 +190,48 @@ static void next_scr2_rtc_update(NeXTPC *s) if (rtc->phase < 8) { rtc->command = (rtc->command << 1) | ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + + if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command <= 0x1f) { + /* RAM registers */ + rtc->retval = rtc->ram[rtc->command]; + } + if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) { + /* RTC */ + time_t time_h = time(NULL); + struct tm *info = localtime(&time_h); + rtc->retval = 0; + + switch (rtc->command) { + case 0x20: + rtc->retval = SCR2_TOBCD(info->tm_sec); + break; + case 0x21: + rtc->retval = SCR2_TOBCD(info->tm_min); + break; + case 0x22: + rtc->retval = SCR2_TOBCD(info->tm_hour); + break; + case 0x24: + rtc->retval = SCR2_TOBCD(info->tm_mday); + break; + case 0x25: + rtc->retval = SCR2_TOBCD((info->tm_mon + 1)); + break; + case 0x26: + rtc->retval = SCR2_TOBCD((info->tm_year - 100)); + break; + } + } + if (rtc->command == 0x30) { + /* read the status 0x30 */ + rtc->retval = rtc->status; + } + if (rtc->command == 0x31) { + /* read the control 0x31 */ + rtc->retval = rtc->control; + } + } } if (rtc->phase >= 8 && rtc->phase < 16) { if (next_rtc_cmd_is_write(rtc->command)) { @@ -198,85 +240,24 @@ static void next_scr2_rtc_update(NeXTPC *s) ((scr2_2 & SCR2_RTDATA) ? 1 : 0); } else { /* Shift out value to read */ - - /* if we read RAM register, output RT_DATA bit */ - if (rtc->command <= 0x1F) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->ram[rtc->command] & - (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - /* read the status 0x30 */ - if (rtc->command == 0x30) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - /* for now status = 0x98 (new rtc + FTU) */ - if (rtc->status & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - /* read the status 0x31 */ - if (rtc->command == 0x31) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->control & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - - if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - /* for now 0x00 */ - time_t time_h = time(NULL); - struct tm *info = localtime(&time_h); - int ret = 0; - - switch (rtc->command) { - case 0x20: - ret = SCR2_TOBCD(info->tm_sec); - break; - case 0x21: - ret = SCR2_TOBCD(info->tm_min); - break; - case 0x22: - ret = SCR2_TOBCD(info->tm_hour); - break; - case 0x24: - ret = SCR2_TOBCD(info->tm_mday); - break; - case 0x25: - ret = SCR2_TOBCD((info->tm_mon + 1)); - break; - case 0x26: - ret = SCR2_TOBCD((info->tm_year - 100)); - break; - } - - if (ret & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + if (rtc->retval & (0x80 >> (rtc->phase - 8))) { + scr2_2 |= SCR2_RTDATA; + } else { + scr2_2 &= ~SCR2_RTDATA; } } } rtc->phase++; - if (rtc->phase == 16) { - if (rtc->command >= 0x80 && rtc->command <= 0x9F) { + if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command >= 0x80 && rtc->command <= 0x9f) { + /* RAM registers */ rtc->ram[rtc->command - 0x80] = rtc->value; } - /* write to x30 register */ - if (rtc->command == 0xB1) { - /* clear FTU */ + if (rtc->command == 0xb1) { + /* write to 0x30 register */ if (rtc->value & 0x04) { + /* clear FTU */ rtc->status = rtc->status & (~0x18); qemu_irq_lower(s->rtc_power_irq); } From ccbc8fa3f9bf5f62ae16e9201e739613d365f30f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:01 +0000 Subject: [PATCH 0746/2892] next-cube: use named gpio to set RTC data bit in scr2 This is in preparation for moving NeXTRTC to its own separate device. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-23-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index a39117144c..1e9e662b40 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -240,10 +240,13 @@ static void next_scr2_rtc_update(NeXTPC *s) ((scr2_2 & SCR2_RTDATA) ? 1 : 0); } else { /* Shift out value to read */ + qemu_irq rtc_data_in_irq = qdev_get_gpio_in_named( + DEVICE(s), "pc-rtc-data-in", 0); + if (rtc->retval & (0x80 >> (rtc->phase - 8))) { - scr2_2 |= SCR2_RTDATA; + qemu_irq_raise(rtc_data_in_irq); } else { - scr2_2 &= ~SCR2_RTDATA; + qemu_irq_lower(rtc_data_in_irq); } } } @@ -270,8 +273,6 @@ static void next_scr2_rtc_update(NeXTPC *s) rtc->command = 0; rtc->value = 0; } - - s->scr2 = deposit32(s->scr2, 8, 8, scr2_2); } static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size) @@ -1001,6 +1002,20 @@ static const MemoryRegionOps next_dummy_en_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void next_pc_rtc_data_in_irq(void *opaque, int n, int level) +{ + NeXTPC *s = NEXT_PC(opaque); + uint8_t scr2_2 = extract32(s->scr2, 8, 8); + + if (level) { + scr2_2 |= SCR2_RTDATA; + } else { + scr2_2 &= ~SCR2_RTDATA; + } + + s->scr2 = deposit32(s->scr2, 8, 8, scr2_2); +} + static void next_pc_reset_hold(Object *obj, ResetType type) { NeXTPC *s = NEXT_PC(obj); @@ -1087,6 +1102,8 @@ static void next_pc_init(Object *obj) sysbus_init_mmio(sbd, &s->timer_mem); s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); + qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, + "pc-rtc-data-in", 1); } /* From 68f54f7e5943048efb7990094f980609d5602021 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:02 +0000 Subject: [PATCH 0747/2892] next-cube: use named gpio to read RTC data bit in scr2 This is in preparation for moving NeXTRTC to its own separate device. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-24-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 169 ++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 77 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 1e9e662b40..0f24905525 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -171,6 +171,90 @@ static bool next_rtc_cmd_is_write(uint8_t cmd) (cmd == 0xb1); } +static void next_rtc_data_in_irq(void *opaque, int n, int level) +{ + NeXTPC *s = NEXT_PC(opaque); + NeXTRTC *rtc = &s->rtc; + + if (rtc->phase < 8) { + rtc->command = (rtc->command << 1) | level; + + if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command <= 0x1f) { + /* RAM registers */ + rtc->retval = rtc->ram[rtc->command]; + } + if ((rtc->command >= 0x20) && (rtc->command <= 0x2f)) { + /* RTC */ + time_t time_h = time(NULL); + struct tm *info = localtime(&time_h); + rtc->retval = 0; + + switch (rtc->command) { + case 0x20: + rtc->retval = SCR2_TOBCD(info->tm_sec); + break; + case 0x21: + rtc->retval = SCR2_TOBCD(info->tm_min); + break; + case 0x22: + rtc->retval = SCR2_TOBCD(info->tm_hour); + break; + case 0x24: + rtc->retval = SCR2_TOBCD(info->tm_mday); + break; + case 0x25: + rtc->retval = SCR2_TOBCD((info->tm_mon + 1)); + break; + case 0x26: + rtc->retval = SCR2_TOBCD((info->tm_year - 100)); + break; + } + } + if (rtc->command == 0x30) { + /* read the status 0x30 */ + rtc->retval = rtc->status; + } + if (rtc->command == 0x31) { + /* read the control 0x31 */ + rtc->retval = rtc->control; + } + } + } + if (rtc->phase >= 8 && rtc->phase < 16) { + if (next_rtc_cmd_is_write(rtc->command)) { + /* Shift in value to write */ + rtc->value = (rtc->value << 1) | level; + } else { + /* Shift out value to read */ + qemu_irq rtc_data_in_irq = qdev_get_gpio_in_named( + DEVICE(s), "pc-rtc-data-in", 0); + + if (rtc->retval & (0x80 >> (rtc->phase - 8))) { + qemu_irq_raise(rtc_data_in_irq); + } else { + qemu_irq_lower(rtc_data_in_irq); + } + } + } + + rtc->phase++; + if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command >= 0x80 && rtc->command <= 0x9f) { + /* RAM registers */ + rtc->ram[rtc->command - 0x80] = rtc->value; + } + if (rtc->command == 0xb1) { + /* write to 0x30 register */ + if (rtc->value & 0x04) { + /* clear FTU */ + rtc->status = rtc->status & (~0x18); + qemu_irq_lower(s->rtc_power_irq); + } + } + } +} + static void next_scr2_rtc_update(NeXTPC *s) { uint8_t old_scr2, scr2_2; @@ -187,84 +271,13 @@ static void next_scr2_rtc_update(NeXTPC *s) /* If we are in going down clock... do something */ if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) && ((scr2_2 & SCR2_RTCLK) == 0)) { - if (rtc->phase < 8) { - rtc->command = (rtc->command << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); + qemu_irq rtc_data_in_irq = qdev_get_gpio_in_named( + DEVICE(s), "rtc-data-in", 0); - if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) { - if (rtc->command <= 0x1f) { - /* RAM registers */ - rtc->retval = rtc->ram[rtc->command]; - } - if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) { - /* RTC */ - time_t time_h = time(NULL); - struct tm *info = localtime(&time_h); - rtc->retval = 0; - - switch (rtc->command) { - case 0x20: - rtc->retval = SCR2_TOBCD(info->tm_sec); - break; - case 0x21: - rtc->retval = SCR2_TOBCD(info->tm_min); - break; - case 0x22: - rtc->retval = SCR2_TOBCD(info->tm_hour); - break; - case 0x24: - rtc->retval = SCR2_TOBCD(info->tm_mday); - break; - case 0x25: - rtc->retval = SCR2_TOBCD((info->tm_mon + 1)); - break; - case 0x26: - rtc->retval = SCR2_TOBCD((info->tm_year - 100)); - break; - } - } - if (rtc->command == 0x30) { - /* read the status 0x30 */ - rtc->retval = rtc->status; - } - if (rtc->command == 0x31) { - /* read the control 0x31 */ - rtc->retval = rtc->control; - } - } - } - if (rtc->phase >= 8 && rtc->phase < 16) { - if (next_rtc_cmd_is_write(rtc->command)) { - /* Shift in value to write */ - rtc->value = (rtc->value << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } else { - /* Shift out value to read */ - qemu_irq rtc_data_in_irq = qdev_get_gpio_in_named( - DEVICE(s), "pc-rtc-data-in", 0); - - if (rtc->retval & (0x80 >> (rtc->phase - 8))) { - qemu_irq_raise(rtc_data_in_irq); - } else { - qemu_irq_lower(rtc_data_in_irq); - } - } - } - - rtc->phase++; - if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) { - if (rtc->command >= 0x80 && rtc->command <= 0x9f) { - /* RAM registers */ - rtc->ram[rtc->command - 0x80] = rtc->value; - } - if (rtc->command == 0xb1) { - /* write to 0x30 register */ - if (rtc->value & 0x04) { - /* clear FTU */ - rtc->status = rtc->status & (~0x18); - qemu_irq_lower(s->rtc_power_irq); - } - } + if (scr2_2 & SCR2_RTDATA) { + qemu_irq_raise(rtc_data_in_irq); + } else { + qemu_irq_lower(rtc_data_in_irq); } } } else { @@ -1104,6 +1117,8 @@ static void next_pc_init(Object *obj) s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, "pc-rtc-data-in", 1); + qdev_init_gpio_in_named(DEVICE(obj), next_rtc_data_in_irq, + "rtc-data-in", 1); } /* From 501b5099f69e75520fc1d6368c44c42ebe337ddc Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:03 +0000 Subject: [PATCH 0748/2892] next-cube: don't use rtc phase value of -1 The rtc phase value of -1 is directly equivalent to using a phase value of 0 so simplify the logic to use an initial rtc phase of 0. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-25-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 0f24905525..3ca70e376e 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -265,9 +265,6 @@ static void next_scr2_rtc_update(NeXTPC *s) if (scr2_2 & 0x1) { /* DPRINTF("RTC %x phase %i\n", scr2_2, rtc->phase); */ - if (rtc->phase == -1) { - rtc->phase = 0; - } /* If we are in going down clock... do something */ if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) && ((scr2_2 & SCR2_RTCLK) == 0)) { @@ -282,7 +279,7 @@ static void next_scr2_rtc_update(NeXTPC *s) } } else { /* else end or abort */ - rtc->phase = -1; + rtc->phase = 0; rtc->command = 0; rtc->value = 0; } From 49100693e9fdea57d2d3272bf0e1b236901123d7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:04 +0000 Subject: [PATCH 0749/2892] next-cube: QOMify NeXTRTC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to allow the RTC functionality to be maintained within its own separate device rather than as part of the next-pc device. Signed-off-by: Mark Cave-Ayland Message-ID: <20241222130012.1013374-26-mark.cave-ayland@ilande.co.uk> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 71 +++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 3ca70e376e..883891ce6b 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -42,7 +42,13 @@ #define RAM_SIZE 0x4000000 #define ROM_FILE "Rev_2.5_v66.bin" -typedef struct NeXTRTC { + +#define TYPE_NEXT_RTC "next-rtc" +OBJECT_DECLARE_SIMPLE_TYPE(NeXTRTC, NEXT_RTC) + +struct NeXTRTC { + SysBusDevice parent_obj; + int8_t phase; uint8_t ram[32]; uint8_t command; @@ -50,7 +56,7 @@ typedef struct NeXTRTC { uint8_t status; uint8_t control; uint8_t retval; -} NeXTRTC; +}; #define TYPE_NEXT_SCSI "next-scsi" OBJECT_DECLARE_SIMPLE_TYPE(NeXTSCSI, NEXT_SCSI) @@ -1012,6 +1018,37 @@ static const MemoryRegionOps next_dummy_en_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static const VMStateDescription next_rtc_vmstate = { + .name = "next-rtc", + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { + VMSTATE_INT8(phase, NeXTRTC), + VMSTATE_UINT8_ARRAY(ram, NeXTRTC, 32), + VMSTATE_UINT8(command, NeXTRTC), + VMSTATE_UINT8(value, NeXTRTC), + VMSTATE_UINT8(status, NeXTRTC), + VMSTATE_UINT8(control, NeXTRTC), + VMSTATE_UINT8(retval, NeXTRTC), + VMSTATE_END_OF_LIST() + }, +}; + +static void next_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NeXT RTC"; + dc->vmsd = &next_rtc_vmstate; +} + +static const TypeInfo next_rtc_info = { + .name = TYPE_NEXT_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NeXTRTC), + .class_init = next_rtc_class_init, +}; + static void next_pc_rtc_data_in_irq(void *opaque, int n, int level) { NeXTPC *s = NEXT_PC(opaque); @@ -1078,6 +1115,12 @@ static void next_pc_realize(DeviceState *dev, Error **errp) } sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(dev, NEXT_SCC_I)); sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(dev, NEXT_SCC_DMA_I)); + + /* RTC */ + d = DEVICE(&s->rtc); + if (!sysbus_realize(SYS_BUS_DEVICE(d), errp)) { + return; + } } static void next_pc_init(Object *obj) @@ -1111,6 +1154,8 @@ static void next_pc_init(Object *obj) "next.timer", 4); sysbus_init_mmio(sbd, &s->timer_mem); + object_initialize_child(obj, "rtc", &s->rtc, TYPE_NEXT_RTC); + s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, "pc-rtc-data-in", 1); @@ -1128,26 +1173,10 @@ static const Property next_pc_properties[] = { DEFINE_PROP_LINK("cpu", NeXTPC, cpu, TYPE_M68K_CPU, M68kCPU *), }; -static const VMStateDescription next_rtc_vmstate = { - .name = "next-rtc", - .version_id = 2, - .minimum_version_id = 2, - .fields = (const VMStateField[]) { - VMSTATE_INT8(phase, NeXTRTC), - VMSTATE_UINT8_ARRAY(ram, NeXTRTC, 32), - VMSTATE_UINT8(command, NeXTRTC), - VMSTATE_UINT8(value, NeXTRTC), - VMSTATE_UINT8(status, NeXTRTC), - VMSTATE_UINT8(control, NeXTRTC), - VMSTATE_UINT8(retval, NeXTRTC), - VMSTATE_END_OF_LIST() - }, -}; - static const VMStateDescription next_pc_vmstate = { .name = "next-pc", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (const VMStateField[]) { VMSTATE_UINT32(scr1, NeXTPC), VMSTATE_UINT32(scr2, NeXTPC), @@ -1155,7 +1184,6 @@ static const VMStateDescription next_pc_vmstate = { VMSTATE_UINT32(int_mask, NeXTPC), VMSTATE_UINT32(int_status, NeXTPC), VMSTATE_UINT32(led, NeXTPC), - VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NeXTRTC), VMSTATE_END_OF_LIST() }, }; @@ -1305,6 +1333,7 @@ static void next_register_type(void) type_register_static(&next_typeinfo); type_register_static(&next_pc_info); type_register_static(&next_scsi_info); + type_register_static(&next_rtc_info); } type_init(next_register_type) From 96d5c4d524ea1355dc2cde2d6c38a95a0ecbe358 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:05 +0000 Subject: [PATCH 0750/2892] next-cube: move reset of next-rtc fields from next-pc to next-rtc Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-27-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 883891ce6b..bd7c76c35e 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -1018,6 +1018,16 @@ static const MemoryRegionOps next_dummy_en_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void next_rtc_reset_hold(Object *obj, ResetType type) +{ + NeXTRTC *rtc = NEXT_RTC(obj); + + rtc->status = 0x90; + + /* Load RTC RAM - TODO: provide possibility to load contents from file */ + memcpy(rtc->ram, rtc_ram2, 32); +} + static const VMStateDescription next_rtc_vmstate = { .name = "next-rtc", .version_id = 3, @@ -1037,9 +1047,11 @@ static const VMStateDescription next_rtc_vmstate = { static void next_rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->desc = "NeXT RTC"; dc->vmsd = &next_rtc_vmstate; + rc->phases.hold = next_rtc_reset_hold; } static const TypeInfo next_rtc_info = { @@ -1072,11 +1084,6 @@ static void next_pc_reset_hold(Object *obj, ResetType type) s->scr1 = 0x00011102; s->scr2 = 0x00ff0c80; s->old_scr2 = s->scr2; - - s->rtc.status = 0x90; - - /* Load RTC RAM - TODO: provide possibility to load contents from file */ - memcpy(s->rtc.ram, rtc_ram2, 32); } static void next_pc_realize(DeviceState *dev, Error **errp) From 4387e938f4eb66515ae9a240522d5c77825e51b1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:06 +0000 Subject: [PATCH 0751/2892] next-cube: move rtc-data-in gpio from next-pc to next-rtc device Add a new rtc-data-out gpio to the next-pc device and wire it up to the next-rtc rtc-data-in gpio using the standard qdev gpio APIs. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-28-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index bd7c76c35e..69f5c7aaec 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -105,6 +105,7 @@ struct NeXTPC { NeXTRTC rtc; qemu_irq rtc_power_irq; + qemu_irq rtc_data_irq; }; typedef struct next_dma { @@ -179,8 +180,8 @@ static bool next_rtc_cmd_is_write(uint8_t cmd) static void next_rtc_data_in_irq(void *opaque, int n, int level) { - NeXTPC *s = NEXT_PC(opaque); - NeXTRTC *rtc = &s->rtc; + NeXTRTC *rtc = NEXT_RTC(opaque); + NeXTPC *s = NEXT_PC(container_of(rtc, NeXTPC, rtc)); if (rtc->phase < 8) { rtc->command = (rtc->command << 1) | level; @@ -274,13 +275,10 @@ static void next_scr2_rtc_update(NeXTPC *s) /* If we are in going down clock... do something */ if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) && ((scr2_2 & SCR2_RTCLK) == 0)) { - qemu_irq rtc_data_in_irq = qdev_get_gpio_in_named( - DEVICE(s), "rtc-data-in", 0); - if (scr2_2 & SCR2_RTDATA) { - qemu_irq_raise(rtc_data_in_irq); + qemu_irq_raise(s->rtc_data_irq); } else { - qemu_irq_lower(rtc_data_in_irq); + qemu_irq_lower(s->rtc_data_irq); } } } else { @@ -1028,6 +1026,12 @@ static void next_rtc_reset_hold(Object *obj, ResetType type) memcpy(rtc->ram, rtc_ram2, 32); } +static void next_rtc_init(Object *obj) +{ + qdev_init_gpio_in_named(DEVICE(obj), next_rtc_data_in_irq, + "rtc-data-in", 1); +} + static const VMStateDescription next_rtc_vmstate = { .name = "next-rtc", .version_id = 3, @@ -1057,6 +1061,7 @@ static void next_rtc_class_init(ObjectClass *klass, void *data) static const TypeInfo next_rtc_info = { .name = TYPE_NEXT_RTC, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = next_rtc_init, .instance_size = sizeof(NeXTRTC), .class_init = next_rtc_class_init, }; @@ -1128,6 +1133,9 @@ static void next_pc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(d), errp)) { return; } + /* Data from NeXTPC to RTC */ + qdev_connect_gpio_out_named(dev, "rtc-data-out", 0, + qdev_get_gpio_in_named(d, "rtc-data-in", 0)); } static void next_pc_init(Object *obj) @@ -1166,8 +1174,8 @@ static void next_pc_init(Object *obj) s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, "pc-rtc-data-in", 1); - qdev_init_gpio_in_named(DEVICE(obj), next_rtc_data_in_irq, - "rtc-data-in", 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_data_irq, + "rtc-data-out", 1); } /* From 44df9533686fcc8899b45120b0dc8d676b57c616 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:07 +0000 Subject: [PATCH 0752/2892] next-cube: use named gpio output for next-rtc data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a named gpio output for the next-rtc data and then update next_rtc_data_in_irq() to drive the IRQ directly. This enables the next-rtc to next-pc data to be wired up using the standard qdev gpio APIs. At the same time rename the pc-rtc-data-in gpio to rtc-data-in which is possible now that the previous rtc-data-in gpio has been moved to the next-rtc device. Signed-off-by: Mark Cave-Ayland Message-ID: <20241222130012.1013374-29-mark.cave-ayland@ilande.co.uk> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 69f5c7aaec..2bc6d49c34 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -56,6 +56,8 @@ struct NeXTRTC { uint8_t status; uint8_t control; uint8_t retval; + + qemu_irq data_out_irq; }; #define TYPE_NEXT_SCSI "next-scsi" @@ -234,13 +236,10 @@ static void next_rtc_data_in_irq(void *opaque, int n, int level) rtc->value = (rtc->value << 1) | level; } else { /* Shift out value to read */ - qemu_irq rtc_data_in_irq = qdev_get_gpio_in_named( - DEVICE(s), "pc-rtc-data-in", 0); - if (rtc->retval & (0x80 >> (rtc->phase - 8))) { - qemu_irq_raise(rtc_data_in_irq); + qemu_irq_raise(rtc->data_out_irq); } else { - qemu_irq_lower(rtc_data_in_irq); + qemu_irq_lower(rtc->data_out_irq); } } } @@ -1028,8 +1027,12 @@ static void next_rtc_reset_hold(Object *obj, ResetType type) static void next_rtc_init(Object *obj) { + NeXTRTC *rtc = NEXT_RTC(obj); + qdev_init_gpio_in_named(DEVICE(obj), next_rtc_data_in_irq, "rtc-data-in", 1); + qdev_init_gpio_out_named(DEVICE(obj), &rtc->data_out_irq, + "rtc-data-out", 1); } static const VMStateDescription next_rtc_vmstate = { @@ -1136,6 +1139,10 @@ static void next_pc_realize(DeviceState *dev, Error **errp) /* Data from NeXTPC to RTC */ qdev_connect_gpio_out_named(dev, "rtc-data-out", 0, qdev_get_gpio_in_named(d, "rtc-data-in", 0)); + /* Data from RTC to NeXTPC */ + qdev_connect_gpio_out_named(d, "rtc-data-out", 0, + qdev_get_gpio_in_named(dev, + "rtc-data-in", 0)); } static void next_pc_init(Object *obj) @@ -1173,7 +1180,7 @@ static void next_pc_init(Object *obj) s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, - "pc-rtc-data-in", 1); + "rtc-data-in", 1); qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_data_irq, "rtc-data-out", 1); } From c1322be69d960b69ee57e402db6103ddfbe5d251 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:08 +0000 Subject: [PATCH 0753/2892] next-cube: add rtc-cmd-reset named gpio to reset the rtc state machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to decouple the next-pc and next-rtc devices from each other in next_scr2_rtc_update(). Signed-off-by: Mark Cave-Ayland Message-ID: <20241222130012.1013374-30-mark.cave-ayland@ilande.co.uk> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 2bc6d49c34..621e8dc832 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -108,6 +108,7 @@ struct NeXTPC { NeXTRTC rtc; qemu_irq rtc_power_irq; qemu_irq rtc_data_irq; + qemu_irq rtc_cmd_reset_irq; }; typedef struct next_dma { @@ -264,7 +265,6 @@ static void next_rtc_data_in_irq(void *opaque, int n, int level) static void next_scr2_rtc_update(NeXTPC *s) { uint8_t old_scr2, scr2_2; - NeXTRTC *rtc = &s->rtc; old_scr2 = extract32(s->old_scr2, 8, 8); scr2_2 = extract32(s->scr2, 8, 8); @@ -282,9 +282,7 @@ static void next_scr2_rtc_update(NeXTPC *s) } } else { /* else end or abort */ - rtc->phase = 0; - rtc->command = 0; - rtc->value = 0; + qemu_irq_raise(s->rtc_cmd_reset_irq); } } @@ -1015,6 +1013,17 @@ static const MemoryRegionOps next_dummy_en_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void next_rtc_cmd_reset_irq(void *opaque, int n, int level) +{ + NeXTRTC *rtc = NEXT_RTC(opaque); + + if (level) { + rtc->phase = 0; + rtc->command = 0; + rtc->value = 0; + } +} + static void next_rtc_reset_hold(Object *obj, ResetType type) { NeXTRTC *rtc = NEXT_RTC(obj); @@ -1033,6 +1042,8 @@ static void next_rtc_init(Object *obj) "rtc-data-in", 1); qdev_init_gpio_out_named(DEVICE(obj), &rtc->data_out_irq, "rtc-data-out", 1); + qdev_init_gpio_in_named(DEVICE(obj), next_rtc_cmd_reset_irq, + "rtc-cmd-reset", 1); } static const VMStateDescription next_rtc_vmstate = { @@ -1143,6 +1154,8 @@ static void next_pc_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out_named(d, "rtc-data-out", 0, qdev_get_gpio_in_named(dev, "rtc-data-in", 0)); + qdev_connect_gpio_out_named(dev, "rtc-cmd-reset", 0, + qdev_get_gpio_in_named(d, "rtc-cmd-reset", 0)); } static void next_pc_init(Object *obj) @@ -1183,6 +1196,8 @@ static void next_pc_init(Object *obj) "rtc-data-in", 1); qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_data_irq, "rtc-data-out", 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_cmd_reset_irq, + "rtc-cmd-reset", 1); } /* From eb1f03642dddac5b6385228274461e8306871288 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:09 +0000 Subject: [PATCH 0754/2892] next-cube: add rtc-power-out named gpio to trigger the NEXT_PWR_I interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to decouple the next-pc and next-rtc devices from each other in next_rtc_data_in_irq(). Signed-off-by: Mark Cave-Ayland Message-ID: <20241222130012.1013374-31-mark.cave-ayland@ilande.co.uk> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 621e8dc832..9f40640b5d 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -58,6 +58,7 @@ struct NeXTRTC { uint8_t retval; qemu_irq data_out_irq; + qemu_irq power_irq; }; #define TYPE_NEXT_SCSI "next-scsi" @@ -106,7 +107,6 @@ struct NeXTPC { ESCCState escc; NeXTRTC rtc; - qemu_irq rtc_power_irq; qemu_irq rtc_data_irq; qemu_irq rtc_cmd_reset_irq; }; @@ -184,7 +184,6 @@ static bool next_rtc_cmd_is_write(uint8_t cmd) static void next_rtc_data_in_irq(void *opaque, int n, int level) { NeXTRTC *rtc = NEXT_RTC(opaque); - NeXTPC *s = NEXT_PC(container_of(rtc, NeXTPC, rtc)); if (rtc->phase < 8) { rtc->command = (rtc->command << 1) | level; @@ -256,7 +255,7 @@ static void next_rtc_data_in_irq(void *opaque, int n, int level) if (rtc->value & 0x04) { /* clear FTU */ rtc->status = rtc->status & (~0x18); - qemu_irq_lower(s->rtc_power_irq); + qemu_irq_lower(rtc->power_irq); } } } @@ -1044,6 +1043,8 @@ static void next_rtc_init(Object *obj) "rtc-data-out", 1); qdev_init_gpio_in_named(DEVICE(obj), next_rtc_cmd_reset_irq, "rtc-cmd-reset", 1); + qdev_init_gpio_out_named(DEVICE(obj), &rtc->power_irq, + "rtc-power-out", 1); } static const VMStateDescription next_rtc_vmstate = { @@ -1156,6 +1157,8 @@ static void next_pc_realize(DeviceState *dev, Error **errp) "rtc-data-in", 0)); qdev_connect_gpio_out_named(dev, "rtc-cmd-reset", 0, qdev_get_gpio_in_named(d, "rtc-cmd-reset", 0)); + qdev_connect_gpio_out_named(d, "rtc-power-out", 0, + qdev_get_gpio_in(dev, NEXT_PWR_I)); } static void next_pc_init(Object *obj) @@ -1191,7 +1194,6 @@ static void next_pc_init(Object *obj) object_initialize_child(obj, "rtc", &s->rtc, TYPE_NEXT_RTC); - s->rtc_power_irq = qdev_get_gpio_in(DEVICE(obj), NEXT_PWR_I); qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, "rtc-data-in", 1); qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_data_irq, From 6963b2c0545121b57d05bc3b3694c48e8ea4c1ad Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:10 +0000 Subject: [PATCH 0755/2892] next-cube: move next_rtc_cmd_is_write() and next_rtc_data_in_irq() functions Move these functions in next-cube.c so that they are with the rest of the next-rtc functions. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-32-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 172 ++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 9f40640b5d..360a46c32e 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -175,92 +175,6 @@ static void next_scr2_led_update(NeXTPC *s) } } -static bool next_rtc_cmd_is_write(uint8_t cmd) -{ - return (cmd >= 0x80 && cmd <= 0x9f) || - (cmd == 0xb1); -} - -static void next_rtc_data_in_irq(void *opaque, int n, int level) -{ - NeXTRTC *rtc = NEXT_RTC(opaque); - - if (rtc->phase < 8) { - rtc->command = (rtc->command << 1) | level; - - if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) { - if (rtc->command <= 0x1f) { - /* RAM registers */ - rtc->retval = rtc->ram[rtc->command]; - } - if ((rtc->command >= 0x20) && (rtc->command <= 0x2f)) { - /* RTC */ - time_t time_h = time(NULL); - struct tm *info = localtime(&time_h); - rtc->retval = 0; - - switch (rtc->command) { - case 0x20: - rtc->retval = SCR2_TOBCD(info->tm_sec); - break; - case 0x21: - rtc->retval = SCR2_TOBCD(info->tm_min); - break; - case 0x22: - rtc->retval = SCR2_TOBCD(info->tm_hour); - break; - case 0x24: - rtc->retval = SCR2_TOBCD(info->tm_mday); - break; - case 0x25: - rtc->retval = SCR2_TOBCD((info->tm_mon + 1)); - break; - case 0x26: - rtc->retval = SCR2_TOBCD((info->tm_year - 100)); - break; - } - } - if (rtc->command == 0x30) { - /* read the status 0x30 */ - rtc->retval = rtc->status; - } - if (rtc->command == 0x31) { - /* read the control 0x31 */ - rtc->retval = rtc->control; - } - } - } - if (rtc->phase >= 8 && rtc->phase < 16) { - if (next_rtc_cmd_is_write(rtc->command)) { - /* Shift in value to write */ - rtc->value = (rtc->value << 1) | level; - } else { - /* Shift out value to read */ - if (rtc->retval & (0x80 >> (rtc->phase - 8))) { - qemu_irq_raise(rtc->data_out_irq); - } else { - qemu_irq_lower(rtc->data_out_irq); - } - } - } - - rtc->phase++; - if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) { - if (rtc->command >= 0x80 && rtc->command <= 0x9f) { - /* RAM registers */ - rtc->ram[rtc->command - 0x80] = rtc->value; - } - if (rtc->command == 0xb1) { - /* write to 0x30 register */ - if (rtc->value & 0x04) { - /* clear FTU */ - rtc->status = rtc->status & (~0x18); - qemu_irq_lower(rtc->power_irq); - } - } - } -} - static void next_scr2_rtc_update(NeXTPC *s) { uint8_t old_scr2, scr2_2; @@ -1012,6 +926,92 @@ static const MemoryRegionOps next_dummy_en_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static bool next_rtc_cmd_is_write(uint8_t cmd) +{ + return (cmd >= 0x80 && cmd <= 0x9f) || + (cmd == 0xb1); +} + +static void next_rtc_data_in_irq(void *opaque, int n, int level) +{ + NeXTRTC *rtc = NEXT_RTC(opaque); + + if (rtc->phase < 8) { + rtc->command = (rtc->command << 1) | level; + + if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command <= 0x1f) { + /* RAM registers */ + rtc->retval = rtc->ram[rtc->command]; + } + if ((rtc->command >= 0x20) && (rtc->command <= 0x2f)) { + /* RTC */ + time_t time_h = time(NULL); + struct tm *info = localtime(&time_h); + rtc->retval = 0; + + switch (rtc->command) { + case 0x20: + rtc->retval = SCR2_TOBCD(info->tm_sec); + break; + case 0x21: + rtc->retval = SCR2_TOBCD(info->tm_min); + break; + case 0x22: + rtc->retval = SCR2_TOBCD(info->tm_hour); + break; + case 0x24: + rtc->retval = SCR2_TOBCD(info->tm_mday); + break; + case 0x25: + rtc->retval = SCR2_TOBCD((info->tm_mon + 1)); + break; + case 0x26: + rtc->retval = SCR2_TOBCD((info->tm_year - 100)); + break; + } + } + if (rtc->command == 0x30) { + /* read the status 0x30 */ + rtc->retval = rtc->status; + } + if (rtc->command == 0x31) { + /* read the control 0x31 */ + rtc->retval = rtc->control; + } + } + } + if (rtc->phase >= 8 && rtc->phase < 16) { + if (next_rtc_cmd_is_write(rtc->command)) { + /* Shift in value to write */ + rtc->value = (rtc->value << 1) | level; + } else { + /* Shift out value to read */ + if (rtc->retval & (0x80 >> (rtc->phase - 8))) { + qemu_irq_raise(rtc->data_out_irq); + } else { + qemu_irq_lower(rtc->data_out_irq); + } + } + } + + rtc->phase++; + if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command >= 0x80 && rtc->command <= 0x9f) { + /* RAM registers */ + rtc->ram[rtc->command - 0x80] = rtc->value; + } + if (rtc->command == 0xb1) { + /* write to 0x30 register */ + if (rtc->value & 0x04) { + /* clear FTU */ + rtc->status = rtc->status & (~0x18); + qemu_irq_lower(rtc->power_irq); + } + } + } +} + static void next_rtc_cmd_reset_irq(void *opaque, int n, int level) { NeXTRTC *rtc = NEXT_RTC(opaque); From b28c9bd6b2112af0df8dec807f3cd0e27198678d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:11 +0000 Subject: [PATCH 0756/2892] next-cube: rename old_scr2 and scr2_2 in next_scr2_rtc_update() Rename them to old_scr2_rtc and scr2_rtc to reflect that they contain the previous and current values of the SCR2 RTC bits. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Message-ID: <20241222130012.1013374-33-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 360a46c32e..513ce5844b 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -177,17 +177,17 @@ static void next_scr2_led_update(NeXTPC *s) static void next_scr2_rtc_update(NeXTPC *s) { - uint8_t old_scr2, scr2_2; + uint8_t old_scr2_rtc, scr2_rtc; - old_scr2 = extract32(s->old_scr2, 8, 8); - scr2_2 = extract32(s->scr2, 8, 8); + old_scr2_rtc = extract32(s->old_scr2, 8, 8); + scr2_rtc = extract32(s->scr2, 8, 8); - if (scr2_2 & 0x1) { + if (scr2_rtc & 0x1) { /* DPRINTF("RTC %x phase %i\n", scr2_2, rtc->phase); */ /* If we are in going down clock... do something */ - if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) && - ((scr2_2 & SCR2_RTCLK) == 0)) { - if (scr2_2 & SCR2_RTDATA) { + if (((old_scr2_rtc & SCR2_RTCLK) != (scr2_rtc & SCR2_RTCLK)) && + ((scr2_rtc & SCR2_RTCLK) == 0)) { + if (scr2_rtc & SCR2_RTDATA) { qemu_irq_raise(s->rtc_data_irq); } else { qemu_irq_lower(s->rtc_data_irq); From ee58d282aa87a7f4a4409528ff12192c0bcfeb82 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 22 Dec 2024 13:00:12 +0000 Subject: [PATCH 0757/2892] next-cube: add my copyright to the top of the file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This series has involved rewriting and/or updating a considerable part of the next-cube emulation so update the copyright in next-cube.c to reflect this. Signed-off-by: Mark Cave-Ayland Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241222130012.1013374-34-mark.cave-ayland@ilande.co.uk> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 513ce5844b..0570e4a76f 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -2,6 +2,7 @@ * NeXT Cube System Driver * * Copyright (c) 2011 Bryce Lanham + * Copyright (c) 2024 Mark Cave-Ayland * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published From ff871d0462d0a7ebdbfadf8c8d6c3726af507f0a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:56:24 +0000 Subject: [PATCH 0758/2892] hw/pci-host/gpex: Allow more than 4 legacy IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some boards such as vmapple don't do real legacy PCI IRQ swizzling. Instead, they just keep allocating more board IRQ lines for each new legacy IRQ. Let's support that mode by giving instantiators a new "nr_irqs" property they can use to support more than 4 legacy IRQ lines. In this mode, GPEX will export more IRQ lines, one for each device. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241223221645.29911-9-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/sbsa-ref.c | 2 +- hw/arm/virt.c | 2 +- hw/i386/microvm.c | 2 +- hw/loongarch/virt.c | 12 +++++------ hw/mips/loongson3_virt.c | 2 +- hw/openrisc/virt.c | 12 +++++------ hw/pci-host/gpex.c | 43 ++++++++++++++++++++++++++++++-------- hw/riscv/virt.c | 12 +++++------ hw/xen/xen-pvh-common.c | 2 +- hw/xtensa/virt.c | 2 +- include/hw/pci-host/gpex.h | 7 +++---- 11 files changed, 61 insertions(+), 37 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 581655d771..6183111f2d 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -673,7 +673,7 @@ static void create_pcie(SBSAMachineState *sms) /* Map IO port space */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(sms->gic, irq + i)); gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index f9b3380815..99e0a68b6c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1547,7 +1547,7 @@ static void create_pcie(VirtMachineState *vms) /* Map IO port space */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(vms->gic, irq + i)); gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index c3d7fe3c42..a8d354aabe 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -139,7 +139,7 @@ static void create_gpex(MicrovmMachineState *mms) mms->gpex.mmio64.base, mmio64_alias); } - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, x86ms->gsi[mms->gpex.irq + i]); } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 99594a13a0..60bd4dc9d3 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -452,7 +452,7 @@ static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, { int pin, dev; uint32_t irq_map_stride = 0; - uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {}; + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {}; uint32_t *irq_map = full_irq_map; const MachineState *ms = MACHINE(lvms); @@ -465,11 +465,11 @@ static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, * to wrap to any number of devices. */ - for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { + for (dev = 0; dev < PCI_NUM_PINS; dev++) { int devfn = dev * 0x8; - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { - int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); int i = 0; /* Fill PCI address cells */ @@ -493,7 +493,7 @@ static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map, - GPEX_NUM_IRQS * GPEX_NUM_IRQS * + PCI_NUM_PINS * PCI_NUM_PINS * irq_map_stride * sizeof(uint32_t)); qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", 0x1800, 0, 0, 0x7); @@ -805,7 +805,7 @@ static void virt_devices_init(DeviceState *pch_pic, memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, pio_alias); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(d, i, qdev_get_gpio_in(pch_pic, 16 + i)); gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index f12f8c3d3c..f3cc7a8376 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -458,7 +458,7 @@ static inline void loongson3_virt_devices_init(MachineState *machine, virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i); diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 0d83e33f9e..22ae057992 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -318,7 +318,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, { int pin, dev; uint32_t irq_map_stride = 0; - uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * 6] = {}; + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 6] = {}; uint32_t *irq_map = full_irq_map; /* @@ -330,11 +330,11 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, * possible slot) seeing the interrupt-map-mask will allow the table * to wrap to any number of devices. */ - for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { + for (dev = 0; dev < PCI_NUM_PINS; dev++) { int devfn = dev << 3; - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { - int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); int i = 0; /* Fill PCI address cells */ @@ -357,7 +357,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, } qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, - GPEX_NUM_IRQS * GPEX_NUM_IRQS * + PCI_NUM_PINS * PCI_NUM_PINS * irq_map_stride * sizeof(uint32_t)); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", @@ -409,7 +409,7 @@ static void openrisc_virt_pcie_init(OR1KVirtState *state, memory_region_add_subregion(get_system_memory(), pio_base, alias); /* Connect IRQ lines. */ - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { pcie_irq = get_per_cpu_irq(cpus, num_cpus, irq_base + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pcie_irq); diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index c6aa8e87a2..9fcedd7fc5 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/irq.h" +#include "hw/pci/pci_bus.h" #include "hw/pci-host/gpex.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -41,20 +42,25 @@ * GPEX host */ +struct GPEXIrq { + qemu_irq irq; + int irq_num; +}; + static void gpex_set_irq(void *opaque, int irq_num, int level) { GPEXHost *s = opaque; - qemu_set_irq(s->irq[irq_num], level); + qemu_set_irq(s->irq[irq_num].irq, level); } int gpex_set_irq_num(GPEXHost *s, int index, int gsi) { - if (index >= GPEX_NUM_IRQS) { + if (index >= s->num_irqs) { return -EINVAL; } - s->irq_num[index] = gsi; + s->irq[index].irq_num = gsi; return 0; } @@ -62,7 +68,7 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin) { PCIINTxRoute route; GPEXHost *s = opaque; - int gsi = s->irq_num[pin]; + int gsi = s->irq[pin].irq_num; route.irq = gsi; if (gsi < 0) { @@ -74,6 +80,13 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin) return route; } +static int gpex_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) +{ + PCIBus *bus = pci_device_root_bus(pci_dev); + + return (PCI_SLOT(pci_dev->devfn) + pin) % bus->nirq; +} + static void gpex_host_realize(DeviceState *dev, Error **errp) { PCIHostState *pci = PCI_HOST_BRIDGE(dev); @@ -82,6 +95,8 @@ static void gpex_host_realize(DeviceState *dev, Error **errp) PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); int i; + s->irq = g_malloc0_n(s->num_irqs, sizeof(*s->irq)); + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX); sysbus_init_mmio(sbd, &pex->mmio); @@ -128,19 +143,27 @@ static void gpex_host_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->io_ioport); } - for (i = 0; i < GPEX_NUM_IRQS; i++) { - sysbus_init_irq(sbd, &s->irq[i]); - s->irq_num[i] = -1; + for (i = 0; i < s->num_irqs; i++) { + sysbus_init_irq(sbd, &s->irq[i].irq); + s->irq[i].irq_num = -1; } pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq, - pci_swizzle_map_irq_fn, s, &s->io_mmio, - &s->io_ioport, 0, 4, TYPE_PCIE_BUS); + gpex_swizzle_map_irq_fn, + s, &s->io_mmio, &s->io_ioport, 0, + s->num_irqs, TYPE_PCIE_BUS); pci_bus_set_route_irq_fn(pci->bus, gpex_route_intx_pin_to_irq); qdev_realize(DEVICE(&s->gpex_root), BUS(pci->bus), &error_fatal); } +static void gpex_host_unrealize(DeviceState *dev) +{ + GPEXHost *s = GPEX_HOST(dev); + + g_free(s->irq); +} + static const char *gpex_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { @@ -166,6 +189,7 @@ static const Property gpex_host_properties[] = { gpex_cfg.mmio64.base, 0), DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost, gpex_cfg.mmio64.size, 0), + DEFINE_PROP_UINT8("num-irqs", GPEXHost, num_irqs, PCI_NUM_PINS), }; static void gpex_host_class_init(ObjectClass *klass, void *data) @@ -175,6 +199,7 @@ static void gpex_host_class_init(ObjectClass *klass, void *data) hc->root_bus_path = gpex_host_root_bus_path; dc->realize = gpex_host_realize; + dc->unrealize = gpex_host_unrealize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; device_class_set_props(dc, gpex_host_properties); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c792ab9c35..2bc5a9dd98 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -179,7 +179,7 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, { int pin, dev; uint32_t irq_map_stride = 0; - uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * FDT_MAX_INT_MAP_WIDTH] = {}; uint32_t *irq_map = full_irq_map; @@ -191,11 +191,11 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, * possible slot) seeing the interrupt-map-mask will allow the table * to wrap to any number of devices. */ - for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { + for (dev = 0; dev < PCI_NUM_PINS; dev++) { int devfn = dev * 0x8; - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { - int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); int i = 0; /* Fill PCI address cells */ @@ -221,7 +221,7 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, } qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, - GPEX_NUM_IRQS * GPEX_NUM_IRQS * + PCI_NUM_PINS * PCI_NUM_PINS * irq_map_stride * sizeof(uint32_t)); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", @@ -1246,7 +1246,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { irq = qdev_get_gpio_in(irqchip, PCIE_IRQ + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index a10c44cc75..9c21fa858d 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -169,7 +169,7 @@ static inline void xenpvh_gpex_init(XenPVHMachineState *s, */ assert(xpc->set_pci_intx_irq); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { qemu_irq irq = qemu_allocate_irq(xpc->set_pci_intx_irq, s, i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index 98622ae86d..b08404fc17 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -93,7 +93,7 @@ static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base, /* Connect IRQ lines. */ extints = xtensa_get_extints(env); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { void *q = extints[irq_base + i]; sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, q); diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h index dce883573b..84471533af 100644 --- a/include/hw/pci-host/gpex.h +++ b/include/hw/pci-host/gpex.h @@ -32,8 +32,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(GPEXHost, GPEX_HOST) #define TYPE_GPEX_ROOT_DEVICE "gpex-root" OBJECT_DECLARE_SIMPLE_TYPE(GPEXRootState, GPEX_ROOT_DEVICE) -#define GPEX_NUM_IRQS 4 - struct GPEXRootState { /*< private >*/ PCIDevice parent_obj; @@ -49,6 +47,7 @@ struct GPEXConfig { PCIBus *bus; }; +typedef struct GPEXIrq GPEXIrq; struct GPEXHost { /*< private >*/ PCIExpressHost parent_obj; @@ -60,8 +59,8 @@ struct GPEXHost { MemoryRegion io_mmio; MemoryRegion io_ioport_window; MemoryRegion io_mmio_window; - qemu_irq irq[GPEX_NUM_IRQS]; - int irq_num[GPEX_NUM_IRQS]; + GPEXIrq *irq; + uint8_t num_irqs; bool allow_unmapped_accesses; From d8d17d2bf6181cdc9b8ef3db862006ddb6af12d4 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 30 Dec 2024 00:08:50 +0300 Subject: [PATCH 0759/2892] Revert "vvfat: fix ubsan issue in create_long_filename" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0cb3ff7c22671aa1e1e227318799ccf6762c3bea. The original code was right in that long name in LFN directory entry uses other parts of the entry for the name too, not just the original "name" field. So it is wrong to limit the offset to be within the name field. Some other mechanism is needed to fix the ubsan report and whole messy usage of bytes past the given field. Reported-by: Volker Rümelin Signed-off-by: Michael Tokarev --- block/vvfat.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/block/vvfat.c b/block/vvfat.c index f2eafaa923..8ffe8b3b9b 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -426,10 +426,6 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename) else if(offset<22) offset=14+offset-10; else offset=28+offset-22; entry=array_get(&(s->directory),s->directory.next-1-(i/26)); - /* ensure we don't write anything past entry->name */ - if (offset >= sizeof(entry->name)) { - continue; - } if (i >= 2 * length + 2) { entry->name[offset] = 0xff; } else if (i % 2 == 0) { From e6c33efed3ca8ffbf89f0e1dbeac1a0e32d0f8b7 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 27 Nov 2023 05:20:20 +0000 Subject: [PATCH 0760/2892] hw/misc/ivshmem-flat: Add ivshmem-flat device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new device, ivshmem-flat, which is similar to the ivshmem PCI but does not require a PCI bus. It's meant to be used on machines like those with Cortex-M MCUs, which usually lack a PCI/PCIe bus, e.g. lm3s6965evb and mps2-an385. The device currently only supports the sysbus bus. The new device, just like the ivshmem PCI device, supports both peer notification via hardware interrupts and shared memory. The device shared memory size can be set using the 'shmem-size' option and it defaults to 4 MiB, which is the default size of shmem allocated by the ivshmem server. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1134 Signed-off-by: Gustavo Romero [PMD: Rebased updating Property and using DEFINE_TYPES macro] Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20241216141818.111255-2-gustavo.romero@linaro.org> --- docs/system/device-emulation.rst | 1 + docs/system/devices/ivshmem-flat.rst | 33 ++ hw/misc/Kconfig | 5 + hw/misc/ivshmem-flat.c | 459 +++++++++++++++++++++++++++ hw/misc/meson.build | 2 + hw/misc/trace-events | 16 + include/hw/misc/ivshmem-flat.h | 85 +++++ 7 files changed, 601 insertions(+) create mode 100644 docs/system/devices/ivshmem-flat.rst create mode 100644 hw/misc/ivshmem-flat.c create mode 100644 include/hw/misc/ivshmem-flat.h diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index f19777411c..a1b0d7997e 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -86,6 +86,7 @@ Emulated Devices devices/ccid.rst devices/cxl.rst devices/ivshmem.rst + devices/ivshmem-flat.rst devices/keyboard.rst devices/net.rst devices/nvme.rst diff --git a/docs/system/devices/ivshmem-flat.rst b/docs/system/devices/ivshmem-flat.rst new file mode 100644 index 0000000000..1f97052804 --- /dev/null +++ b/docs/system/devices/ivshmem-flat.rst @@ -0,0 +1,33 @@ +Inter-VM Shared Memory Flat Device +---------------------------------- + +The ivshmem-flat device is meant to be used on machines that lack a PCI bus, +making them unsuitable for the use of the traditional ivshmem device modeled as +a PCI device. Machines like those with a Cortex-M MCU are good candidates to use +the ivshmem-flat device. Also, since the flat version maps the control and +status registers directly to the memory, it requires a quite tiny "device +driver" to interact with other VMs, which is useful in some RTOSes, like +Zephyr, which usually run on constrained resource targets. + +Similar to the ivshmem device, the ivshmem-flat device supports both peer +notification via HW interrupts and Inter-VM shared memory. This allows the +device to be used together with the traditional ivshmem, enabling communication +between, for instance, an aarch64 VM (using the traditional ivshmem device and +running Linux), and an arm VM (using the ivshmem-flat device and running Zephyr +instead). + +The ivshmem-flat device does not support the use of a ``memdev`` option (see +ivshmem.rst for more details). It relies on the ivshmem server to create and +distribute the proper shared memory file descriptor and the eventfd(s) to notify +(interrupt) the peers. Therefore, to use this device, it is always necessary to +have an ivshmem server up and running for proper device creation. + +Although the ivshmem-flat supports both peer notification (interrupts) and +shared memory, the interrupt mechanism is optional. If no input IRQ is +specified for the device it is disabled, preventing the VM from notifying or +being notified by other VMs (a warning will be displayed to the user to inform +the IRQ mechanism is disabled). The shared memory region is always present. + +The MMRs (INTRMASK, INTRSTATUS, IVPOSITION, and DOORBELL registers) offsets at +the MMR region, and their functions, follow the ivshmem spec, so they work +exactly as in the ivshmem PCI device (see ./specs/ivshmem-spec.txt). diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 1f1baa5dde..8f9ce2f68c 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -72,6 +72,11 @@ config IVSHMEM_DEVICE default y if PCI_DEVICES depends on PCI && LINUX && IVSHMEM && MSI_NONBROKEN +config IVSHMEM_FLAT_DEVICE + bool + default y + depends on LINUX && IVSHMEM + config ECCMEMCTL bool diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c new file mode 100644 index 0000000000..33fc9425d2 --- /dev/null +++ b/hw/misc/ivshmem-flat.c @@ -0,0 +1,459 @@ +/* + * Inter-VM Shared Memory Flat Device + * + * SPDX-FileCopyrightText: 2023 Linaro Ltd. + * SPDX-FileContributor: Gustavo Romero + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/irq.h" +#include "hw/qdev-properties-system.h" +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "exec/address-spaces.h" +#include "trace.h" + +#include "hw/misc/ivshmem-flat.h" + +static int64_t ivshmem_flat_recv_msg(IvshmemFTState *s, int *pfd) +{ + int64_t msg; + int n, ret; + + n = 0; + do { + ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, + sizeof(msg) - n); + if (ret < 0) { + if (ret == -EINTR) { + continue; + } + exit(1); + } + n += ret; + } while (n < sizeof(msg)); + + if (pfd) { + *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); + } + return le64_to_cpu(msg); +} + +static void ivshmem_flat_irq_handler(void *opaque) +{ + VectorInfo *vi = opaque; + EventNotifier *e = &vi->event_notifier; + uint16_t vector_id; + const VectorInfo (*v)[64]; + + assert(e->initialized); + + vector_id = vi->id; + + /* + * The vector info struct is passed to the handler via the 'opaque' pointer. + * This struct pointer allows the retrieval of the vector ID and its + * associated event notifier. However, for triggering an interrupt using + * qemu_set_irq, it's necessary to also have a pointer to the device state, + * i.e., a pointer to the IvshmemFTState struct. Since the vector info + * struct is contained within the IvshmemFTState struct, its pointer can be + * used to obtain the pointer to IvshmemFTState through simple pointer math. + */ + v = (void *)(vi - vector_id); /* v = &IvshmemPeer->vector[0] */ + IvshmemPeer *own_peer = container_of(v, IvshmemPeer, vector); + IvshmemFTState *s = container_of(own_peer, IvshmemFTState, own); + + /* Clear event */ + if (!event_notifier_test_and_clear(e)) { + return; + } + + trace_ivshmem_flat_irq_handler(vector_id); + + /* + * Toggle device's output line, which is connected to interrupt controller, + * generating an interrupt request to the CPU. + */ + qemu_irq_pulse(s->irq); +} + +static IvshmemPeer *ivshmem_flat_find_peer(IvshmemFTState *s, uint16_t peer_id) +{ + IvshmemPeer *peer; + + /* Own ID */ + if (s->own.id == peer_id) { + return &s->own; + } + + /* Peer ID */ + QTAILQ_FOREACH(peer, &s->peer, next) { + if (peer->id == peer_id) { + return peer; + } + } + + return NULL; +} + +static IvshmemPeer *ivshmem_flat_add_peer(IvshmemFTState *s, uint16_t peer_id) +{ + IvshmemPeer *new_peer; + + new_peer = g_malloc0(sizeof(*new_peer)); + new_peer->id = peer_id; + new_peer->vector_counter = 0; + + QTAILQ_INSERT_TAIL(&s->peer, new_peer, next); + + trace_ivshmem_flat_new_peer(peer_id); + + return new_peer; +} + +static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id) +{ + IvshmemPeer *peer; + + peer = ivshmem_flat_find_peer(s, peer_id); + assert(peer); + + QTAILQ_REMOVE(&s->peer, peer, next); + for (int n = 0; n < peer->vector_counter; n++) { + int efd; + efd = event_notifier_get_fd(&(peer->vector[n].event_notifier)); + close(efd); + } + + g_free(peer); +} + +static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, + int vector_fd) +{ + if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) { + trace_ivshmem_flat_add_vector_failure(peer->vector_counter, + vector_fd, peer->id); + close(vector_fd); + + return; + } + + trace_ivshmem_flat_add_vector_success(peer->vector_counter, + vector_fd, peer->id); + + /* + * Set vector ID and its associated eventfd notifier and add them to the + * peer. + */ + peer->vector[peer->vector_counter].id = peer->vector_counter; + g_unix_set_fd_nonblocking(vector_fd, true, NULL); + event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, + vector_fd); + + /* + * If it's the device's own ID, register also the handler for the eventfd + * so the device can be notified by the other peers. + */ + if (peer == &s->own) { + qemu_set_fd_handler(vector_fd, ivshmem_flat_irq_handler, NULL, + &peer->vector); + } + + peer->vector_counter++; +} + +static void ivshmem_flat_process_msg(IvshmemFTState *s, uint64_t msg, int fd) +{ + uint16_t peer_id; + IvshmemPeer *peer; + + peer_id = msg & 0xFFFF; + peer = ivshmem_flat_find_peer(s, peer_id); + + if (!peer) { + peer = ivshmem_flat_add_peer(s, peer_id); + } + + if (fd >= 0) { + ivshmem_flat_add_vector(s, peer, fd); + } else { /* fd == -1, which is received when peers disconnect. */ + ivshmem_flat_remove_peer(s, peer_id); + } +} + +static int ivshmem_flat_can_receive_data(void *opaque) +{ + IvshmemFTState *s = opaque; + + assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); + return sizeof(s->msg_buf) - s->msg_buffered_bytes; +} + +static void ivshmem_flat_read_msg(void *opaque, const uint8_t *buf, int size) +{ + IvshmemFTState *s = opaque; + int fd; + int64_t msg; + + assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); + memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); + s->msg_buffered_bytes += size; + if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { + return; + } + msg = le64_to_cpu(s->msg_buf); + s->msg_buffered_bytes = 0; + + fd = qemu_chr_fe_get_msgfd(&s->server_chr); + + ivshmem_flat_process_msg(s, msg, fd); +} + +static uint64_t ivshmem_flat_iomem_read(void *opaque, + hwaddr offset, unsigned size) +{ + IvshmemFTState *s = opaque; + uint32_t ret; + + trace_ivshmem_flat_read_mmr(offset); + + switch (offset) { + case INTMASK: + ret = 0; /* Ignore read since all bits are reserved in rev 1. */ + break; + case INTSTATUS: + ret = 0; /* Ignore read since all bits are reserved in rev 1. */ + break; + case IVPOSITION: + ret = s->own.id; + break; + case DOORBELL: + trace_ivshmem_flat_read_mmr_doorbell(); /* DOORBELL is write-only */ + ret = 0; + break; + default: + /* Should never reach out here due to iomem map range being exact */ + trace_ivshmem_flat_read_write_mmr_invalid(offset); + ret = 0; + } + + return ret; +} + +static int ivshmem_flat_interrupt_peer(IvshmemFTState *s, + uint16_t peer_id, uint16_t vector_id) +{ + IvshmemPeer *peer; + + peer = ivshmem_flat_find_peer(s, peer_id); + if (!peer) { + trace_ivshmem_flat_interrupt_invalid_peer(peer_id); + return 1; + } + + event_notifier_set(&(peer->vector[vector_id].event_notifier)); + + return 0; +} + +static void ivshmem_flat_iomem_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IvshmemFTState *s = opaque; + uint16_t peer_id = (value >> 16) & 0xFFFF; + uint16_t vector_id = value & 0xFFFF; + + trace_ivshmem_flat_write_mmr(offset); + + switch (offset) { + case INTMASK: + break; + case INTSTATUS: + break; + case IVPOSITION: + break; + case DOORBELL: + trace_ivshmem_flat_interrupt_peer(peer_id, vector_id); + ivshmem_flat_interrupt_peer(s, peer_id, vector_id); + break; + default: + /* Should never reach out here due to iomem map range being exact. */ + trace_ivshmem_flat_read_write_mmr_invalid(offset); + break; + } + + return; +} + +static const MemoryRegionOps ivshmem_flat_ops = { + .read = ivshmem_flat_iomem_read, + .write = ivshmem_flat_iomem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { /* Read/write aligned at 32 bits. */ + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void ivshmem_flat_instance_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IvshmemFTState *s = IVSHMEM_FLAT(obj); + + /* + * Init mem region for 4 MMRs (ivshmem_registers), + * 32 bits each => 16 bytes (0x10). + */ + memory_region_init_io(&s->iomem, obj, &ivshmem_flat_ops, s, + "ivshmem-mmio", 0x10); + sysbus_init_mmio(sbd, &s->iomem); + + /* + * Create one output IRQ that will be connect to the + * machine's interrupt controller. + */ + sysbus_init_irq(sbd, &s->irq); + + QTAILQ_INIT(&s->peer); +} + +static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp) +{ + IvshmemFTState *s = IVSHMEM_FLAT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int64_t protocol_version, msg; + int shmem_fd; + uint16_t peer_id; + struct stat fdstat; + + /* Check ivshmem server connection. */ + if (!qemu_chr_fe_backend_connected(&s->server_chr)) { + error_setg(errp, "ivshmem server socket not specified or incorret." + " Can't create device."); + return false; + } + + /* + * Message sequence from server on new connection: + * _____________________________________ + * |STEP| uint64_t msg | int fd | + * ------------------------------------- + * + * 0 PROTOCOL -1 \ + * 1 OWN PEER ID -1 |-- Header/Greeting + * 2 -1 shmem fd / + * + * 3 PEER IDx Other peer's Vector 0 eventfd + * 4 PEER IDx Other peer's Vector 1 eventfd + * . . + * . . + * . . + * N PEER IDy Other peer's Vector 0 eventfd + * N+1 PEER IDy Other peer's Vector 1 eventfd + * . . + * . . + * . . + * + * ivshmem_flat_recv_msg() calls return 'msg' and 'fd'. + * + * See ./docs/specs/ivshmem-spec.txt for details on the protocol. + */ + + /* Step 0 */ + protocol_version = ivshmem_flat_recv_msg(s, NULL); + + /* Step 1 */ + msg = ivshmem_flat_recv_msg(s, NULL); + peer_id = 0xFFFF & msg; + s->own.id = peer_id; + s->own.vector_counter = 0; + + trace_ivshmem_flat_proto_ver_own_id(protocol_version, s->own.id); + + /* Step 2 */ + msg = ivshmem_flat_recv_msg(s, &shmem_fd); + /* Map shmem fd and MMRs into memory regions. */ + if (msg != -1 || shmem_fd < 0) { + error_setg(errp, "Could not receive valid shmem fd." + " Can't create device!"); + return false; + } + + if (fstat(shmem_fd, &fdstat) != 0) { + error_setg(errp, "Could not determine shmem fd size." + " Can't create device!"); + return false; + } + trace_ivshmem_flat_shmem_size(shmem_fd, fdstat.st_size); + + /* + * Shmem size provided by the ivshmem server must be equal to + * device's shmem size. + */ + if (fdstat.st_size != s->shmem_size) { + error_setg(errp, "Can't map shmem fd: shmem size different" + " from device size!"); + return false; + } + + /* + * Beyond step 2 ivshmem_process_msg, called by ivshmem_flat_read_msg + * handler -- when data is available on the server socket -- will handle + * the additional messages that will be generated by the server as peers + * connect or disconnect. + */ + qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_flat_can_receive_data, + ivshmem_flat_read_msg, NULL, NULL, s, NULL, true); + + memory_region_init_ram_from_fd(&s->shmem, OBJECT(s), + "ivshmem-shmem", s->shmem_size, + RAM_SHARED, shmem_fd, 0, NULL); + sysbus_init_mmio(sbd, &s->shmem); + + return true; +} + +static void ivshmem_flat_realize(DeviceState *dev, Error **errp) +{ + if (!ivshmem_flat_connect_server(dev, errp)) { + return; + } +} + +static const Property ivshmem_flat_props[] = { + DEFINE_PROP_CHR("chardev", IvshmemFTState, server_chr), + DEFINE_PROP_UINT32("shmem-size", IvshmemFTState, shmem_size, 4 * MiB), +}; + +static void ivshmem_flat_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->hotpluggable = true; + dc->realize = ivshmem_flat_realize; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, ivshmem_flat_props); + + /* Reason: Must be wired up in code (sysbus MRs and IRQ) */ + dc->user_creatable = false; +} + +static const TypeInfo ivshmem_flat_types[] = { + { + .name = TYPE_IVSHMEM_FLAT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IvshmemFTState), + .instance_init = ivshmem_flat_instance_init, + .class_init = ivshmem_flat_class_init, + }, +}; + +DEFINE_TYPES(ivshmem_flat_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index d02d96e403..7a16ddb1dc 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -37,7 +37,9 @@ system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) subdir('macio') +# ivshmem devices system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) +system_ss.add(when: 'CONFIG_IVSHMEM_FLAT_DEVICE', if_true: files('ivshmem-flat.c')) system_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c')) system_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b9fbcb0924..0f5d2b5666 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -368,3 +368,19 @@ aspeed_sli_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx aspeed_sliio_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_sliio_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +# ivshmem-flat.c +ivshmem_flat_irq_handler(uint16_t vector_id) "Caught interrupt request: vector %d" +ivshmem_flat_new_peer(uint16_t peer_id) "New peer ID: %d" +ivshmem_flat_add_vector_failure(uint16_t vector_id, uint32_t vector_fd, uint16_t peer_id) "Failed to add vector %u (fd = %u) to peer ID %u, maximum number of vectors reached" +ivshmem_flat_add_vector_success(uint16_t vector_id, uint32_t vector_fd, uint16_t peer_id) "Successful addition of vector %u (fd = %u) to peer ID %u" +ivshmem_flat_irq_resolved(const char *irq_qompath) "IRQ QOM path '%s' correctly resolved" +ivshmem_flat_proto_ver_own_id(uint64_t proto_ver, uint16_t peer_id) "Protocol Version = 0x%"PRIx64", Own Peer ID = %u" +ivshmem_flat_shmem_size(int fd, uint64_t size) "Shmem fd (%d) total size is %"PRIu64" byte(s)" +ivshmem_flat_shmem_map(uint64_t addr) "Mapping shmem @ 0x%"PRIx64 +ivshmem_flat_mmio_map(uint64_t addr) "Mapping MMRs @ 0x%"PRIx64 +ivshmem_flat_read_mmr(uint64_t addr_offset) "Read access at offset %"PRIu64 +ivshmem_flat_read_mmr_doorbell(void) "DOORBELL register is write-only!" +ivshmem_flat_read_write_mmr_invalid(uint64_t addr_offset) "No ivshmem register mapped at offset %"PRIu64 +ivshmem_flat_interrupt_invalid_peer(uint16_t peer_id) "Can't interrupt non-existing peer %u" +ivshmem_flat_write_mmr(uint64_t addr_offset) "Write access at offset %"PRIu64 +ivshmem_flat_interrupt_peer(uint16_t peer_id, uint16_t vector_id) "Interrupting peer ID %u, vector %u..." diff --git a/include/hw/misc/ivshmem-flat.h b/include/hw/misc/ivshmem-flat.h new file mode 100644 index 0000000000..97ca0ddce6 --- /dev/null +++ b/include/hw/misc/ivshmem-flat.h @@ -0,0 +1,85 @@ +/* + * Inter-VM Shared Memory Flat Device + * + * SPDX-FileCopyrightText: 2023 Linaro Ltd. + * SPDX-FileContributor: Gustavo Romero + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#ifndef IVSHMEM_FLAT_H +#define IVSHMEM_FLAT_H + +#include "qemu/queue.h" +#include "qemu/event_notifier.h" +#include "chardev/char-fe.h" +#include "exec/memory.h" +#include "qom/object.h" +#include "hw/sysbus.h" + +#define IVSHMEM_MAX_VECTOR_NUM 64 + +/* + * QEMU interface: + * + QOM property "chardev" is the character device id of the ivshmem server + * socket + * + QOM property "shmem-size" sets the size of the RAM region shared between + * the device and the ivshmem server + * + sysbus MMIO region 0: device I/O mapped registers + * + sysbus MMIO region 1: shared memory with ivshmem server + * + sysbus IRQ 0: single output interrupt + */ + +#define TYPE_IVSHMEM_FLAT "ivshmem-flat" +typedef struct IvshmemFTState IvshmemFTState; + +DECLARE_INSTANCE_CHECKER(IvshmemFTState, IVSHMEM_FLAT, TYPE_IVSHMEM_FLAT) + +/* Ivshmem registers. See ./docs/specs/ivshmem-spec.txt for details. */ +enum ivshmem_registers { + INTMASK = 0, + INTSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +typedef struct VectorInfo { + EventNotifier event_notifier; + uint16_t id; +} VectorInfo; + +typedef struct IvshmemPeer { + QTAILQ_ENTRY(IvshmemPeer) next; + VectorInfo vector[IVSHMEM_MAX_VECTOR_NUM]; + int vector_counter; + uint16_t id; +} IvshmemPeer; + +struct IvshmemFTState { + SysBusDevice parent_obj; + + uint64_t msg_buf; + int msg_buffered_bytes; + + QTAILQ_HEAD(, IvshmemPeer) peer; + IvshmemPeer own; + + CharBackend server_chr; + + /* IRQ */ + qemu_irq irq; + + /* I/O registers */ + MemoryRegion iomem; + uint32_t intmask; + uint32_t intstatus; + uint32_t ivposition; + uint32_t doorbell; + + /* Shared memory */ + MemoryRegion shmem; + int shmem_fd; + uint32_t shmem_size; +}; + +#endif /* IVSHMEM_FLAT_H */ From 4daf88c16550a07aa5f228aa1b2660ea1fa1ddc1 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 27 Nov 2023 05:20:24 +0000 Subject: [PATCH 0761/2892] hw/misc/ivshmem: Rename ivshmem to ivshmem-pci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because now there is also an MMIO ivshmem device (ivshmem-flat.c), and ivshmem.c is a PCI specific implementation, rename it to ivshmem-pci.c. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gustavo Romero Message-ID: <20241216141818.111255-5-gustavo.romero@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/{ivshmem.c => ivshmem-pci.c} | 0 hw/misc/meson.build | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename hw/misc/{ivshmem.c => ivshmem-pci.c} (100%) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem-pci.c similarity index 100% rename from hw/misc/ivshmem.c rename to hw/misc/ivshmem-pci.c diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 7a16ddb1dc..55f493521b 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -38,7 +38,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) subdir('macio') # ivshmem devices -system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) +system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem-pci.c')) system_ss.add(when: 'CONFIG_IVSHMEM_FLAT_DEVICE', if_true: files('ivshmem-flat.c')) system_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c')) From 9d59b65d82415a39d862e996770c8561ef8f30b5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 6 Sep 2024 05:25:35 -0700 Subject: [PATCH 0762/2892] hw/usb/uhci: checkpatch cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix reported checkpatch issues to prepare for next patches in the series. No functional change. Signed-off-by: Guenter Roeck Reviewed-by: Cédric Le Goater Message-ID: <20240906122542.3808997-2-linux@roeck-us.net> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-uhci.c | 90 +++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 0559b3ae3e..7c0cc69455 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -67,7 +67,7 @@ struct UHCIPCIDeviceClass { UHCIInfo info; }; -/* +/* * Pending async transaction. * 'packet' must be the first field because completion * handler does "(UHCIAsync *) pkt" cast. @@ -220,8 +220,9 @@ static void uhci_async_cancel(UHCIAsync *async) uhci_async_unlink(async); trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr, async->done); - if (!async->done) + if (!async->done) { usb_cancel_packet(&async->packet); + } uhci_async_free(async); } @@ -322,7 +323,7 @@ static void uhci_reset(DeviceState *dev) s->fl_base_addr = 0; s->sof_timing = 64; - for(i = 0; i < UHCI_PORTS; i++) { + for (i = 0; i < UHCI_PORTS; i++) { port = &s->ports[i]; port->ctrl = 0x0080; if (port->port.dev && port->port.dev->attached) { @@ -387,7 +388,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, trace_usb_uhci_mmio_writew(addr, val); - switch(addr) { + switch (addr) { case 0x00: if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ @@ -404,7 +405,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, int i; /* send reset on the USB bus */ - for(i = 0; i < UHCI_PORTS; i++) { + for (i = 0; i < UHCI_PORTS; i++) { port = &s->ports[i]; usb_device_reset(port->port.dev); } @@ -425,10 +426,13 @@ static void uhci_port_write(void *opaque, hwaddr addr, break; case 0x02: s->status &= ~val; - /* XXX: the chip spec is not coherent, so we add a hidden - register to distinguish between IOC and SPD */ - if (val & UHCI_STS_USBINT) + /* + * XXX: the chip spec is not coherent, so we add a hidden + * register to distinguish between IOC and SPD + */ + if (val & UHCI_STS_USBINT) { s->status2 = 0; + } uhci_update_irq(s); break; case 0x04: @@ -436,8 +440,9 @@ static void uhci_port_write(void *opaque, hwaddr addr, uhci_update_irq(s); break; case 0x06: - if (s->status & UHCI_STS_HCHALTED) + if (s->status & UHCI_STS_HCHALTED) { s->frnum = val & 0x7ff; + } break; case 0x08: s->fl_base_addr &= 0xffff0000; @@ -464,8 +469,8 @@ static void uhci_port_write(void *opaque, hwaddr addr, dev = port->port.dev; if (dev && dev->attached) { /* port reset */ - if ( (val & UHCI_PORT_RESET) && - !(port->ctrl & UHCI_PORT_RESET) ) { + if ((val & UHCI_PORT_RESET) && + !(port->ctrl & UHCI_PORT_RESET)) { usb_device_reset(dev); } } @@ -487,7 +492,7 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) UHCIState *s = opaque; uint32_t val; - switch(addr) { + switch (addr) { case 0x00: val = s->cmd; break; @@ -533,12 +538,13 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) } /* signal resume if controller suspended */ -static void uhci_resume (void *opaque) +static void uhci_resume(void *opaque) { UHCIState *s = (UHCIState *)opaque; - if (!s) + if (!s) { return; + } if (s->cmd & UHCI_CMD_EGSM) { s->cmd |= UHCI_CMD_FGR; @@ -674,7 +680,8 @@ static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, return ret; } -static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) +static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, + uint32_t *int_mask) { int len = 0, max_len; uint8_t pid; @@ -682,8 +689,9 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - if (td->ctrl & TD_CTRL_IOS) + if (td->ctrl & TD_CTRL_IOS) { td->ctrl &= ~TD_CTRL_ACTIVE; + } if (async->packet.status != USB_RET_SUCCESS) { return uhci_handle_td_error(s, td, async->td_addr, @@ -693,12 +701,15 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ len = async->packet.actual_length; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); - /* The NAK bit may have been set by a previous frame, so clear it - here. The docs are somewhat unclear, but win2k relies on this - behavior. */ + /* + * The NAK bit may have been set by a previous frame, so clear it + * here. The docs are somewhat unclear, but win2k relies on this + * behavior. + */ td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); - if (td->ctrl & TD_CTRL_IOC) + if (td->ctrl & TD_CTRL_IOC) { *int_mask |= 0x01; + } if (pid == USB_TOKEN_IN) { pci_dma_write(&s->dev, td->buffer, async->buf, len); @@ -780,9 +791,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, if (async) { if (queuing) { - /* we are busy filling the queue, we are not prepared - to consume completed packages then, just leave them - in async state */ + /* + * we are busy filling the queue, we are not prepared + * to consume completed packages then, just leave them + * in async state + */ return TD_RESULT_ASYNC_CONT; } if (!async->done) { @@ -832,7 +845,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, } usb_packet_addbuf(&async->packet, async->buf, max_len); - switch(pid) { + switch (pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: pci_dma_read(&s->dev, td->buffer, async->buf, max_len); @@ -911,12 +924,15 @@ static void qhdb_reset(QhDb *db) static int qhdb_insert(QhDb *db, uint32_t addr) { int i; - for (i = 0; i < db->count; i++) - if (db->addr[i] == addr) + for (i = 0; i < db->count; i++) { + if (db->addr[i] == addr) { return 1; + } + } - if (db->count >= UHCI_MAX_QUEUES) + if (db->count >= UHCI_MAX_QUEUES) { return 1; + } db->addr[db->count++] = addr; return 0; @@ -970,8 +986,10 @@ static void uhci_process_frame(UHCIState *s) for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { if (!s->completions_only && s->frame_bytes >= s->frame_bandwidth) { - /* We've reached the usb 1.1 bandwidth, which is - 1280 bytes/frame, stop processing */ + /* + * We've reached the usb 1.1 bandwidth, which is + * 1280 bytes/frame, stop processing + */ trace_usb_uhci_frame_stop_bandwidth(); break; } @@ -1120,8 +1138,10 @@ static void uhci_frame_timer(void *opaque) uhci_async_validate_begin(s); uhci_process_frame(s); uhci_async_validate_end(s); - /* The spec says frnum is the frame currently being processed, and - * the guest must look at frnum - 1 on interrupt, so inc frnum now */ + /* + * The spec says frnum is the frame currently being processed, and + * the guest must look at frnum - 1 on interrupt, so inc frnum now + */ s->frnum = (s->frnum + 1) & 0x7ff; s->expire_time += frame_t; } @@ -1174,7 +1194,7 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) if (s->masterbus) { USBPort *ports[UHCI_PORTS]; - for(i = 0; i < UHCI_PORTS; i++) { + for (i = 0; i < UHCI_PORTS; i++) { ports[i] = &s->ports[i].port; } usb_register_companion(s->masterbus, ports, UHCI_PORTS, @@ -1200,8 +1220,10 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s, "uhci", 0x20); - /* Use region 4 for consistency with real hardware. BSD guests seem - to rely on this. */ + /* + * Use region 4 for consistency with real hardware. BSD guests seem + * to rely on this. + */ pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); } From d826e47404cdf4f462a3f27b2f47455f21e7764e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 6 Sep 2024 05:25:36 -0700 Subject: [PATCH 0763/2892] hw/usb/uhci: Introduce and use register defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce defines for UHCI registers to simplify adding register access in subsequent patches of the series. No functional change. Signed-off-by: Guenter Roeck Reviewed-by: Cédric Le Goater Message-ID: <20240906122542.3808997-3-linux@roeck-us.net> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-uhci.c | 32 ++++++++++++++++---------------- include/hw/usb/uhci-regs.h | 11 +++++++++++ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 7c0cc69455..8528d493d6 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -389,7 +389,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, trace_usb_uhci_mmio_writew(addr, val); switch (addr) { - case 0x00: + case UHCI_USBCMD: if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ trace_usb_uhci_schedule_start(); @@ -424,7 +424,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, } } break; - case 0x02: + case UHCI_USBSTS: s->status &= ~val; /* * XXX: the chip spec is not coherent, so we add a hidden @@ -435,27 +435,27 @@ static void uhci_port_write(void *opaque, hwaddr addr, } uhci_update_irq(s); break; - case 0x04: + case UHCI_USBINTR: s->intr = val; uhci_update_irq(s); break; - case 0x06: + case UHCI_USBFRNUM: if (s->status & UHCI_STS_HCHALTED) { s->frnum = val & 0x7ff; } break; - case 0x08: + case UHCI_USBFLBASEADD: s->fl_base_addr &= 0xffff0000; s->fl_base_addr |= val & ~0xfff; break; - case 0x0a: + case UHCI_USBFLBASEADD + 2: s->fl_base_addr &= 0x0000ffff; s->fl_base_addr |= (val << 16); break; - case 0x0c: + case UHCI_USBSOF: s->sof_timing = val & 0xff; break; - case 0x10 ... 0x1f: + case UHCI_USBPORTSC1 ... UHCI_USBPORTSC4: { UHCIPort *port; USBDevice *dev; @@ -493,28 +493,28 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) uint32_t val; switch (addr) { - case 0x00: + case UHCI_USBCMD: val = s->cmd; break; - case 0x02: + case UHCI_USBSTS: val = s->status; break; - case 0x04: + case UHCI_USBINTR: val = s->intr; break; - case 0x06: + case UHCI_USBFRNUM: val = s->frnum; break; - case 0x08: + case UHCI_USBFLBASEADD: val = s->fl_base_addr & 0xffff; break; - case 0x0a: + case UHCI_USBFLBASEADD + 2: val = (s->fl_base_addr >> 16) & 0xffff; break; - case 0x0c: + case UHCI_USBSOF: val = s->sof_timing; break; - case 0x10 ... 0x1f: + case UHCI_USBPORTSC1 ... UHCI_USBPORTSC4: { UHCIPort *port; int n; diff --git a/include/hw/usb/uhci-regs.h b/include/hw/usb/uhci-regs.h index fd45d29db0..5b81714e5c 100644 --- a/include/hw/usb/uhci-regs.h +++ b/include/hw/usb/uhci-regs.h @@ -1,6 +1,17 @@ #ifndef HW_USB_UHCI_REGS_H #define HW_USB_UHCI_REGS_H +#define UHCI_USBCMD 0 +#define UHCI_USBSTS 2 +#define UHCI_USBINTR 4 +#define UHCI_USBFRNUM 6 +#define UHCI_USBFLBASEADD 8 +#define UHCI_USBSOF 0x0c +#define UHCI_USBPORTSC1 0x10 +#define UHCI_USBPORTSC2 0x12 +#define UHCI_USBPORTSC3 0x14 +#define UHCI_USBPORTSC4 0x16 + #define UHCI_CMD_FGR (1 << 4) #define UHCI_CMD_EGSM (1 << 3) #define UHCI_CMD_GRESET (1 << 2) From 8f27e70e3bcffe3e3d7236f62d50156555043061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Nov 2024 16:36:49 +0100 Subject: [PATCH 0764/2892] hw/microblaze: Propagate CPU endianness to microblaze_load_kernel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass vCPU endianness as argument so we can load kernels with different endianness (different from the qemu-system-binary builtin one). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Message-Id: <20241107012223.94337-3-philmd@linaro.org> --- hw/microblaze/boot.c | 8 ++++---- hw/microblaze/boot.h | 4 ++-- hw/microblaze/petalogix_ml605_mmu.c | 2 +- hw/microblaze/petalogix_s3adsp1800_mmu.c | 2 +- hw/microblaze/xlnx-zynqmp-pmu.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 966fb2cb2a..3854bc2291 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -114,8 +114,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return addr - 0x30000000LL; } -void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, +void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian, + hwaddr ddr_base, uint32_t ramsize, const char *initrd_filename, const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)) @@ -144,13 +144,13 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, &high, NULL, - TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0); + !is_little_endian, EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, &entry, NULL, NULL, NULL, - TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0); + !is_little_endian, EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = (uint32_t)entry; diff --git a/hw/microblaze/boot.h b/hw/microblaze/boot.h index 5a8c2f7975..d179a551a6 100644 --- a/hw/microblaze/boot.h +++ b/hw/microblaze/boot.h @@ -2,8 +2,8 @@ #define MICROBLAZE_BOOT_H -void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, +void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian, + hwaddr ddr_base, uint32_t ramsize, const char *initrd_filename, const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)); diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index deab275495..8b44be75a2 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -204,7 +204,7 @@ petalogix_ml605_init(MachineState *machine) cpu->cfg.pvr_regs[5] = 0xc56be000; cpu->cfg.pvr_regs[10] = 0x0e000000; /* virtex 6 */ - microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size, + microblaze_load_kernel(cpu, true, MEMORY_BASEADDR, ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, NULL); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 4a969af1a0..2c0d8c34cd 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -129,7 +129,7 @@ petalogix_s3adsp1800_init(MachineState *machine) create_unimplemented_device("xps_gpio", GPIO_BASEADDR, 0x10000); - microblaze_load_kernel(cpu, ddr_base, ram_size, + microblaze_load_kernel(cpu, !TARGET_BIG_ENDIAN, ddr_base, ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, NULL); diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 567aad47bf..bdbf7328bf 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -172,7 +172,7 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine) qdev_realize(DEVICE(pmu), NULL, &error_fatal); /* Load the kernel */ - microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR, + microblaze_load_kernel(&pmu->cpu, true, XLNX_ZYNQMP_PMU_RAM_ADDR, machine->ram_size, machine->initrd_filename, machine->dtb, From a115ab5ba30ae78efdf346df594940042b69aa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 17:45:11 +0000 Subject: [PATCH 0765/2892] hw/i386: Mark devices as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These devices are only used by the X86 targets, which are only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-Id: <20241106184612.71897-2-philmd@linaro.org> --- hw/i386/kvm/apic.c | 2 +- hw/i386/pc.c | 4 ++-- hw/i386/vapic.c | 2 +- hw/i386/xen/xen_apic.c | 2 +- hw/i386/xen/xen_platform.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 602c769656..7575106000 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -214,7 +214,7 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps kvm_apic_io_ops = { .read = kvm_apic_mem_read, .write = kvm_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void kvm_apic_reset(APICCommonState *s) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 53a2f226d0..7111876588 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1068,7 +1068,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) static const MemoryRegionOps ioport80_io_ops = { .write = ioport80_write, .read = ioport80_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, @@ -1078,7 +1078,7 @@ static const MemoryRegionOps ioport80_io_ops = { static const MemoryRegionOps ioportF0_io_ops = { .write = ioportF0_write, .read = ioportF0_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c index 0e6d058d06..14de9b7a82 100644 --- a/hw/i386/vapic.c +++ b/hw/i386/vapic.c @@ -718,7 +718,7 @@ static uint64_t vapic_read(void *opaque, hwaddr addr, unsigned size) static const MemoryRegionOps vapic_ops = { .write = vapic_write, .read = vapic_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void vapic_realize(DeviceState *dev, Error **errp) diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c index 101e16a766..a94e9005cb 100644 --- a/hw/i386/xen/xen_apic.c +++ b/hw/i386/xen/xen_apic.c @@ -36,7 +36,7 @@ static void xen_apic_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps xen_apic_io_ops = { .read = xen_apic_mem_read, .write = xen_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void xen_apic_realize(DeviceState *dev, Error **errp) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 0f68c3fe7b..dd648a2ee9 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -514,7 +514,7 @@ static void platform_mmio_write(void *opaque, hwaddr addr, static const MemoryRegionOps platform_mmio_handler = { .read = &platform_mmio_read, .write = &platform_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void platform_mmio_setup(PCIXenPlatformState *d) From 625a975f67314a91084e59e2320c910b4129cbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 17:44:01 +0000 Subject: [PATCH 0766/2892] hw/tricore: Mark devices as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These devices are only used by the TriCore target, which is only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-Id: <20241106184612.71897-3-philmd@linaro.org> --- hw/tricore/tricore_testdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/tricore/tricore_testdevice.c b/hw/tricore/tricore_testdevice.c index e60866d76f..d2da74e384 100644 --- a/hw/tricore/tricore_testdevice.c +++ b/hw/tricore/tricore_testdevice.c @@ -47,7 +47,7 @@ static const MemoryRegionOps tricore_testdevice_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void tricore_testdevice_init(Object *obj) From e72eee9684b5eca48815e762ad097101c487514f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 17:42:06 +0000 Subject: [PATCH 0767/2892] hw/openrisc: Mark devices as big-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The openrisc little-endian control is in a control register: SR[LEE] (which we do not implement at present). These devices are only used by the OpenRISC target, which is only built as big-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_BIG_ENDIAN (besides, the DEVICE_LITTLE_ENDIAN case isn't tested). Simplify directly using DEVICE_BIG_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-Id: <20241106184612.71897-5-philmd@linaro.org> --- hw/openrisc/openrisc_sim.c | 2 +- hw/openrisc/virt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 87f9cbc300..e0da4067ba 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -266,7 +266,7 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, } serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200, serial_hd(uart_idx), - DEVICE_NATIVE_ENDIAN); + DEVICE_BIG_ENDIAN); /* Add device tree node for serial. */ nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base); diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 22ae057992..7b60bf8509 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -236,7 +236,7 @@ static void openrisc_virt_serial_init(OR1KVirtState *state, hwaddr base, qemu_irq serial_irq = get_per_cpu_irq(cpus, num_cpus, irq_pin); serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200, - serial_hd(0), DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_BIG_ENDIAN); /* Add device tree node for serial. */ nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base); From eba75400f37d4b80835b371d834c9d94a52063a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 17:43:47 +0000 Subject: [PATCH 0768/2892] hw/sparc: Mark devices as big-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These devices are only used by the SPARC targets, which are only built as big-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_BIG_ENDIAN (besides, the DEVICE_LITTLE_ENDIAN case isn't tested). Simplify directly using DEVICE_BIG_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Reviewed-by: Richard Henderson Message-Id: <20241106184612.71897-6-philmd@linaro.org> --- hw/sparc/sun4m_iommu.c | 2 +- hw/sparc64/sun4u.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index 8c1fc82534..5a4c1f5e3b 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -238,7 +238,7 @@ static void iommu_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps iommu_mem_ops = { .read = iommu_mem_read, .write = iommu_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 5778709b41..0980b44659 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -254,7 +254,7 @@ static void power_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps power_mem_ops = { .read = power_mem_read, .write = power_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, From 2f44fddd191d002cbd9e63c02e1048a8d076b6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:25:28 +0100 Subject: [PATCH 0769/2892] hw/net/xilinx_ethlite: Convert some debug logs to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-3-philmd@linaro.org> --- hw/net/trace-events | 4 ++++ hw/net/xilinx_ethlite.c | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/net/trace-events b/hw/net/trace-events index 6100ec324a..c35bfb2eb8 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -513,3 +513,7 @@ xen_netdev_connect(int dev, unsigned int tx, unsigned int rx, int port) "vif%u t xen_netdev_frontend_changed(const char *dev, int state) "vif%s state %d" xen_netdev_tx(int dev, int ref, int off, int len, unsigned int flags, const char *c, const char *d, const char *m, const char *e) "vif%u ref %u off %u len %u flags 0x%x%s%s%s%s" xen_netdev_rx(int dev, int idx, int status, int flags) "vif%u idx %d status %d flags 0x%x" + +# xilinx_ethlite.c +ethlite_pkt_lost(uint32_t rx_ctrl) "rx_ctrl:0x%" PRIx32 +ethlite_pkt_size_too_big(uint64_t size) "size:0x%" PRIx64 diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index f3eb2af193..c38a71c71b 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -30,6 +30,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "net/net.h" +#include "trace.h" #define D(x) #define R_TX_BUF0 0 @@ -194,13 +195,13 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return size; if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { - D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0])); + trace_ethlite_pkt_lost(s->regs[R_RX_CTRL0]); return -1; } D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) { - D(qemu_log("ethlite packet is too big, size=%x\n", size)); + trace_ethlite_pkt_size_too_big(size); return -1; } memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); From 9dd886c04d3ce759fa1bed3bd537ba8f6782285d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:26:49 +0100 Subject: [PATCH 0770/2892] hw/net/xilinx_ethlite: Remove unuseful debug logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-4-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index c38a71c71b..4626a55b06 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -32,7 +32,6 @@ #include "net/net.h" #include "trace.h" -#define D(x) #define R_TX_BUF0 0 #define R_TX_LEN0 (0x07f4 / 4) #define R_TX_GIE0 (0x07f8 / 4) @@ -100,7 +99,6 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_RX_CTRL1: case R_RX_CTRL0: r = s->regs[addr]; - D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr * 4, r)); break; default: @@ -126,13 +124,10 @@ eth_write(void *opaque, hwaddr addr, if (addr == R_TX_CTRL1) base = 0x800 / 4; - D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n", - __func__, addr * 4, value)); if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { qemu_send_packet(qemu_get_queue(s->nic), (void *) &s->regs[base], s->regs[base + R_TX_LEN0]); - D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); if (s->regs[base + R_TX_CTRL0] & CTRL_I) eth_pulse_irq(s); } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { @@ -156,8 +151,6 @@ eth_write(void *opaque, hwaddr addr, case R_TX_LEN0: case R_TX_LEN1: case R_TX_GIE0: - D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n", - __func__, addr * 4, value)); s->regs[addr] = value; break; @@ -199,7 +192,6 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return -1; } - D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) { trace_ethlite_pkt_size_too_big(size); return -1; From 0fb867ed63dc14086e74c97865530bc69866b060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:28:39 +0100 Subject: [PATCH 0771/2892] hw/net/xilinx_ethlite: Update QOM style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use XlnxXpsEthLite typedef, OBJECT_DECLARE_SIMPLE_TYPE macro; convert type_init() to DEFINE_TYPES(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-5-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 48 +++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 4626a55b06..330dccb83d 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -53,10 +53,9 @@ #define CTRL_S 0x1 #define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite" -DECLARE_INSTANCE_CHECKER(struct xlx_ethlite, XILINX_ETHLITE, - TYPE_XILINX_ETHLITE) +OBJECT_DECLARE_SIMPLE_TYPE(XlnxXpsEthLite, XILINX_ETHLITE) -struct xlx_ethlite +struct XlnxXpsEthLite { SysBusDevice parent_obj; @@ -73,7 +72,7 @@ struct xlx_ethlite uint32_t regs[R_MAX]; }; -static inline void eth_pulse_irq(struct xlx_ethlite *s) +static inline void eth_pulse_irq(XlnxXpsEthLite *s) { /* Only the first gie reg is active. */ if (s->regs[R_TX_GIE0] & GIE_GIE) { @@ -84,7 +83,7 @@ static inline void eth_pulse_irq(struct xlx_ethlite *s) static uint64_t eth_read(void *opaque, hwaddr addr, unsigned int size) { - struct xlx_ethlite *s = opaque; + XlnxXpsEthLite *s = opaque; uint32_t r = 0; addr >>= 2; @@ -112,7 +111,7 @@ static void eth_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { - struct xlx_ethlite *s = opaque; + XlnxXpsEthLite *s = opaque; unsigned int base = 0; uint32_t value = val64; @@ -172,7 +171,7 @@ static const MemoryRegionOps eth_ops = { static bool eth_can_rx(NetClientState *nc) { - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); + XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); unsigned int rxbase = s->rxbuf * (0x800 / 4); return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); @@ -180,7 +179,7 @@ static bool eth_can_rx(NetClientState *nc) static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); + XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); unsigned int rxbase = s->rxbuf * (0x800 / 4); /* DA filter. */ @@ -210,7 +209,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) static void xilinx_ethlite_reset(DeviceState *dev) { - struct xlx_ethlite *s = XILINX_ETHLITE(dev); + XlnxXpsEthLite *s = XILINX_ETHLITE(dev); s->rxbuf = 0; } @@ -224,7 +223,7 @@ static NetClientInfo net_xilinx_ethlite_info = { static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { - struct xlx_ethlite *s = XILINX_ETHLITE(dev); + XlnxXpsEthLite *s = XILINX_ETHLITE(dev); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, @@ -235,7 +234,7 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) static void xilinx_ethlite_init(Object *obj) { - struct xlx_ethlite *s = XILINX_ETHLITE(obj); + XlnxXpsEthLite *s = XILINX_ETHLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); @@ -245,9 +244,9 @@ static void xilinx_ethlite_init(Object *obj) } static const Property xilinx_ethlite_properties[] = { - DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1), - DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1), - DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf), + DEFINE_PROP_UINT32("tx-ping-pong", XlnxXpsEthLite, c_tx_pingpong, 1), + DEFINE_PROP_UINT32("rx-ping-pong", XlnxXpsEthLite, c_rx_pingpong, 1), + DEFINE_NIC_PROPERTIES(XlnxXpsEthLite, conf), }; static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) @@ -259,17 +258,14 @@ static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, xilinx_ethlite_properties); } -static const TypeInfo xilinx_ethlite_info = { - .name = TYPE_XILINX_ETHLITE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct xlx_ethlite), - .instance_init = xilinx_ethlite_init, - .class_init = xilinx_ethlite_class_init, +static const TypeInfo xilinx_ethlite_types[] = { + { + .name = TYPE_XILINX_ETHLITE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxXpsEthLite), + .instance_init = xilinx_ethlite_init, + .class_init = xilinx_ethlite_class_init, + }, }; -static void xilinx_ethlite_register_types(void) -{ - type_register_static(&xilinx_ethlite_info); -} - -type_init(xilinx_ethlite_register_types) +DEFINE_TYPES(xilinx_ethlite_types) From 7eb77fa4cd88bd70f68a603aa829207bf403e987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:32:56 +0100 Subject: [PATCH 0772/2892] hw/net/xilinx_ethlite: Correct maximum RX buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current max RX bufsize is set to 0x800. This is invalid, since it contains the MMIO registers region. Add the correct definition (valid for both TX & RX, see datasheet p. 20, Table 11 "XPS Ethernet Lite MAC Memory Map") and use it. Reviewed-by: Edgar E. Iglesias Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241112181044.92193-6-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 330dccb83d..d54f96df24 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -3,6 +3,9 @@ * * Copyright (c) 2009 Edgar E. Iglesias. * + * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite + * LogiCORE IP XPS Ethernet Lite Media Access Controller + * * 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 @@ -33,6 +36,7 @@ #include "trace.h" #define R_TX_BUF0 0 +#define BUFSZ_MAX 0x07e4 #define R_TX_LEN0 (0x07f4 / 4) #define R_TX_GIE0 (0x07f8 / 4) #define R_TX_CTRL0 (0x07fc / 4) @@ -191,7 +195,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return -1; } - if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) { + if (size >= BUFSZ_MAX) { trace_ethlite_pkt_size_too_big(size); return -1; } From 0798e09f3badf25a5b490ab0146b0d41ae78a798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:29:55 +0100 Subject: [PATCH 0773/2892] hw/net/xilinx_ethlite: Rename rxbuf -> port_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'rxbuf' is the index of the dual port RAM used. Rename it as 'port_index'. Reviewed-by: Edgar E. Iglesias Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241112181044.92193-8-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index d54f96df24..4c0c7fcae3 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -70,8 +70,7 @@ struct XlnxXpsEthLite uint32_t c_tx_pingpong; uint32_t c_rx_pingpong; - unsigned int txbuf; - unsigned int rxbuf; + unsigned int port_index; /* dual port RAM index */ uint32_t regs[R_MAX]; }; @@ -176,7 +175,7 @@ static const MemoryRegionOps eth_ops = { static bool eth_can_rx(NetClientState *nc) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->rxbuf * (0x800 / 4); + unsigned int rxbase = s->port_index * (0x800 / 4); return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); } @@ -184,7 +183,7 @@ static bool eth_can_rx(NetClientState *nc) static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->rxbuf * (0x800 / 4); + unsigned int rxbase = s->port_index * (0x800 / 4); /* DA filter. */ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6)) @@ -207,7 +206,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) } /* If c_rx_pingpong was set flip buffers. */ - s->rxbuf ^= s->c_rx_pingpong; + s->port_index ^= s->c_rx_pingpong; return size; } @@ -215,7 +214,7 @@ static void xilinx_ethlite_reset(DeviceState *dev) { XlnxXpsEthLite *s = XILINX_ETHLITE(dev); - s->rxbuf = 0; + s->port_index = 0; } static NetClientInfo net_xilinx_ethlite_info = { From 5a7b6029c1e26c3bb171050938757e048398c576 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 3 Dec 2024 13:18:06 +0000 Subject: [PATCH 0774/2892] fw_cfg: Don't set callback_opaque NULL in fw_cfg_modify_bytes_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On arm/virt platform, Chen Xiang reported a Guest crash while attempting the below steps, 1. Launch the Guest with nvdimm=on 2. Hot-add a NVDIMM dev 3. Reboot 4. Guest boots fine. 5. Reboot again. 6. Guest boot fails. QEMU_EFI reports the below error: ProcessCmdAddPointer: invalid pointer value in "etc/acpi/tables" OnRootBridgesConnected: InstallAcpiTables: Protocol Error Debugging shows that on first reboot(after hot adding NVDIMM), Qemu updates the etc/table-loader len, qemu_ram_resize()   fw_cfg_modify_file()      fw_cfg_modify_bytes_read() And in fw_cfg_modify_bytes_read() we set the "callback_opaque" for the key entry to NULL. Because of this, on the second reboot, virt_acpi_build_update() is called with a NULL "build_state" and returns without updating the ACPI tables. This seems to be upsetting the firmware. To fix this, don't change the callback_opaque in fw_cfg_modify_bytes_read(). Fixes: bdbb5b1706d165 ("fw_cfg: add fw_cfg_machine_reset function") Reported-by: chenxiang Acked-by: Igor Mammedov Acked-by: Gerd Hoffmann Signed-off-by: Shameer Kolothum Message-ID: <20241203131806.37548-1-shameerali.kolothum.thodi@huawei.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/nvram/fw_cfg.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index c5537166d9..a757939cfb 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -729,7 +729,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, ptr = s->entries[arch][key].data; s->entries[arch][key].data = data; s->entries[arch][key].len = len; - s->entries[arch][key].callback_opaque = NULL; s->entries[arch][key].allow_write = false; return ptr; From 3154922c7fb8a62ebdcf7fe65e11cd4115207150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 14:30:35 +0100 Subject: [PATCH 0775/2892] hw/misc/vmcoreinfo: Declare QOM type using DEFINE_TYPES macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241219153857.57450-2-philmd@linaro.org> --- hw/misc/vmcoreinfo.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index 0910c64866..31073c8de2 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -93,16 +93,13 @@ static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); } -static const TypeInfo vmcoreinfo_device_info = { - .name = VMCOREINFO_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VMCoreInfoState), - .class_init = vmcoreinfo_device_class_init, +static const TypeInfo vmcoreinfo_types[] = { + { + .name = VMCOREINFO_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VMCoreInfoState), + .class_init = vmcoreinfo_device_class_init, + } }; -static void vmcoreinfo_register_types(void) -{ - type_register_static(&vmcoreinfo_device_info); -} - -type_init(vmcoreinfo_register_types) +DEFINE_TYPES(vmcoreinfo_types) From 924e1be17571968f81ef0bde87729678c201df21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 14:46:11 +0100 Subject: [PATCH 0776/2892] hw/misc/vmcoreinfo: Rename opaque pointer as 'opaque' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both QEMUResetHandler and FWCfgWriteCallback take an opaque pointer argument, no need to cast. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241219153857.57450-3-philmd@linaro.org> --- hw/misc/vmcoreinfo.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index 31073c8de2..b1fcc22e92 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -18,17 +18,17 @@ #include "migration/vmstate.h" #include "hw/misc/vmcoreinfo.h" -static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len) +static void fw_cfg_vmci_write(void *opaque, off_t offset, size_t len) { - VMCoreInfoState *s = VMCOREINFO(dev); + VMCoreInfoState *s = opaque; s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo) && s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE; } -static void vmcoreinfo_reset(void *dev) +static void vmcoreinfo_reset(void *opaque) { - VMCoreInfoState *s = VMCOREINFO(dev); + VMCoreInfoState *s = opaque; s->has_vmcoreinfo = false; memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo)); @@ -65,7 +65,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) * This device requires to register a global reset because it is * not plugged to a bus (which, as its QOM parent, would reset it). */ - qemu_register_reset(vmcoreinfo_reset, dev); + qemu_register_reset(vmcoreinfo_reset, s); vmcoreinfo_state = s; } From 8c6619f3e692c5173c9f4919dbf99fb14dc0b7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Nov 2024 12:15:40 +0100 Subject: [PATCH 0777/2892] hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generic code wanting to access KVM specific methods should do so being protected by the 'kvm_enabled()' helper. Doing so avoid link failures when optimization is disabled (using --enable-debug), see for example commits c04cfb4596a ("hw/i386: fix short-circuit logic with non-optimizing builds") and 0266aef8cd6 ("amd_iommu: Fix kvm_enable_x2apic link error with clang in non-KVM builds"). XTSup feature depends on KVM, so protect the whole block checking the XTSup feature with a check on whether KVM is enabled. Since x86_cpus_init() already checks APIC ID > 255 imply kernel support for irqchip and X2APIC, remove the confuse and unlikely reachable "AMD IOMMU xtsup=on requires support on the KVM side" message. Fix a type in "configuration" in error message. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Vasant Hegde Message-Id: <20241129155802.35534-1-philmd@linaro.org> --- hw/i386/amd_iommu.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index be522b5d7d..6b13ce894b 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1652,17 +1652,10 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST, &s->mr_ir, 1); - /* AMD IOMMU with x2APIC mode requires xtsup=on */ - if (x86ms->apic_id_limit > 255 && !s->xtsup) { - error_report("AMD IOMMU with x2APIC confguration requires xtsup=on"); + if (kvm_enabled() && x86ms->apic_id_limit > 255 && !s->xtsup) { + error_report("AMD IOMMU with x2APIC configuration requires xtsup=on"); exit(EXIT_FAILURE); } - if (s->xtsup) { - if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) { - error_report("AMD IOMMU xtsup=on requires support on the KVM side"); - exit(EXIT_FAILURE); - } - } pci_setup_iommu(bus, &amdvi_iommu_ops, s); amdvi_init(s); From 12ed6aca7ca328a12e235f4daa9824873da60315 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Thu, 24 Oct 2024 12:28:12 +0200 Subject: [PATCH 0778/2892] hw/block/virtio-blk: Replaces request free function with g_free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The virtio_blk_free_request() function has been a 1-liner forwarding to g_free() for a while now. We may as well call g_free on the request pointer directly. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-14-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/block/virtio-blk.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 8806c03f7c..e0acce89e1 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -50,11 +50,6 @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, req->mr_next = NULL; } -static void virtio_blk_free_request(VirtIOBlockReq *req) -{ - g_free(req); -} - static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) { VirtIOBlock *s = req->dev; @@ -93,7 +88,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, if (acct_failed) { block_acct_failed(blk_get_stats(s->blk), &req->acct); } - virtio_blk_free_request(req); + g_free(req); } blk_error_action(s->blk, action, is_read, error); @@ -136,7 +131,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret) virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(s->blk), &req->acct); - virtio_blk_free_request(req); + g_free(req); } } @@ -151,7 +146,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret) virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(s->blk), &req->acct); - virtio_blk_free_request(req); + g_free(req); } static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) @@ -169,7 +164,7 @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) if (is_write_zeroes) { block_acct_done(blk_get_stats(s->blk), &req->acct); } - virtio_blk_free_request(req); + g_free(req); } static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) @@ -214,7 +209,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) fail: virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); + g_free(req); } static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb, @@ -612,7 +607,7 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); g_free(data->zone_report_data.zones); g_free(data); } @@ -661,7 +656,7 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req, return; out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); } static void virtio_blk_zone_mgmt_complete(void *opaque, int ret) @@ -677,7 +672,7 @@ static void virtio_blk_zone_mgmt_complete(void *opaque, int ret) } virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); } static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) @@ -719,7 +714,7 @@ static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) return 0; out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); return err_status; } @@ -750,7 +745,7 @@ static void virtio_blk_zone_append_complete(void *opaque, int ret) out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); g_free(data); } @@ -788,7 +783,7 @@ static int virtio_blk_handle_zone_append(VirtIOBlockReq *req, out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); return err_status; } @@ -855,7 +850,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); block_acct_invalid(blk_get_stats(s->blk), is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); - virtio_blk_free_request(req); + g_free(req); return 0; } @@ -911,7 +906,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) VIRTIO_BLK_ID_BYTES)); iov_from_buf(in_iov, in_num, 0, serial, size); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - virtio_blk_free_request(req); + g_free(req); break; } case VIRTIO_BLK_T_ZONE_APPEND & ~VIRTIO_BLK_T_OUT: @@ -943,7 +938,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) if (unlikely(!(type & VIRTIO_BLK_T_OUT) || out_len > sizeof(dwz_hdr))) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - virtio_blk_free_request(req); + g_free(req); return 0; } @@ -960,14 +955,14 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) is_write_zeroes); if (err_status != VIRTIO_BLK_S_OK) { virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); } break; } default: virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - virtio_blk_free_request(req); + g_free(req); } return 0; } @@ -988,7 +983,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) while ((req = virtio_blk_get_request(s, vq))) { if (virtio_blk_handle_request(req, &mrb)) { virtqueue_detach_element(req->vq, &req->elem, 0); - virtio_blk_free_request(req); + g_free(req); break; } } @@ -1038,7 +1033,7 @@ static void virtio_blk_dma_restart_bh(void *opaque) while (req) { next = req->next; virtqueue_detach_element(req->vq, &req->elem, 0); - virtio_blk_free_request(req); + g_free(req); req = next; } break; @@ -1121,7 +1116,7 @@ static void virtio_blk_reset(VirtIODevice *vdev) /* No other threads can access req->vq here */ virtqueue_detach_element(req->vq, &req->elem, 0); - virtio_blk_free_request(req); + g_free(req); } } From 92270bdff02bc6ba77a091f066576f96d5a96f7b Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Sun, 8 Dec 2024 20:16:42 +0100 Subject: [PATCH 0779/2892] hw/usb/hcd-xhci-pci: Move msi/msix properties from NEC to superclass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NEC XHCI controller exposes the underlying PCI device's msi and msix properties, but the superclass and thus the qemu-xhci device do not. There does not seem to be any obvious reason for this limitation. This change moves these properties to the superclass so they are exposed by both PCI XHCI device variants. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241227121336.25838-3-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci-nec.c | 2 -- hw/usb/hcd-xhci-pci.c | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index b1df95b52a..1df518baf5 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -37,8 +37,6 @@ struct XHCINecState { }; static const Property nec_xhci_properties[] = { - DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), - DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS), }; diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index e110840c7a..a069b42338 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -197,6 +197,11 @@ static void xhci_instance_init(Object *obj) qdev_alias_all_properties(DEVICE(&s->xhci), obj); } +static const Property xhci_pci_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), +}; + static void xhci_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -208,6 +213,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) k->realize = usb_xhci_pci_realize; k->exit = usb_xhci_pci_exit; k->class_id = PCI_CLASS_SERIAL_USB; + device_class_set_props(dc, xhci_pci_properties); } static const TypeInfo xhci_pci_info = { From 916bf7f93793df8690f48111207961a85f5a25b1 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Fri, 27 Dec 2024 13:13:34 +0100 Subject: [PATCH 0780/2892] hw/usb/hcd-xhci: Unimplemented/guest error logging for port MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XHCI device code uses tracing rather than logging on various code paths that are so far unimplemented. In some cases, these code paths actually indicate faulty guest software. This patch switches instances in the read and write handlers for the port MMIO region to use qemu_log_mask() with LOG_UNIMP or LOG_GUEST_ERROR, as appropriate in each case. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241227121336.25838-5-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 3719c0f190..7dc0994c89 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2810,9 +2810,15 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size) case 0x08: /* PORTLI */ ret = 0; break; - case 0x0c: /* reserved */ + case 0x0c: /* PORTHLPMC */ + ret = 0; + qemu_log_mask(LOG_UNIMP, "%s: read from port register PORTHLPMC", + __func__); + break; default: - trace_usb_xhci_unimplemented("port read", reg); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from port offset 0x%" HWADDR_PRIx, + __func__, reg); ret = 0; } @@ -2881,9 +2887,22 @@ static void xhci_port_write(void *ptr, hwaddr reg, } break; case 0x04: /* PORTPMSC */ + case 0x0c: /* PORTHLPMC */ + qemu_log_mask(LOG_UNIMP, + "%s: write 0x%" PRIx64 + " (%u bytes) to port register at offset 0x%" HWADDR_PRIx, + __func__, val, size, reg); + break; case 0x08: /* PORTLI */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Write to read-only PORTLI register", + __func__); + break; default: - trace_usb_xhci_unimplemented("port write", reg); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write 0x%" PRIx64 " (%u bytes) to unknown port " + "register at offset 0x%" HWADDR_PRIx, + __func__, val, size, reg); + break; } } From f5ab12caba4f1656479c1feb5248beac1c833243 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Thu, 24 Oct 2024 12:27:59 +0200 Subject: [PATCH 0781/2892] ui & main loop: Redesign of system-specific main thread event handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS's Cocoa event handling must be done on the initial (main) thread of the process. Furthermore, if library or application code uses libdispatch, the main dispatch queue must be handling events on the main thread as well. So far, this has affected Qemu in both the Cocoa and SDL UIs, although in different ways: the Cocoa UI replaces the default qemu_main function with one that spins Qemu's internal main event loop off onto a background thread. SDL (which uses Cocoa internally) on the other hand uses a polling approach within Qemu's main event loop. Events are polled during the SDL UI's dpy_refresh callback, which happens to run on the main thread by default. As UIs are mutually exclusive, this works OK as long as nothing else needs platform-native event handling. In the next patch, a new device is introduced based on the ParavirtualizedGraphics.framework in macOS. This uses libdispatch internally, and only works when events are being handled on the main runloop. With the current system, it works when using either the Cocoa or the SDL UI. However, it does not when running headless. Moreover, any attempt to install a similar scheme to the Cocoa UI's main thread replacement fails when combined with the SDL UI. This change tidies up main thread management to be more flexible. * The qemu_main global function pointer is a custom function for the main thread, and it may now be NULL. When it is, the main thread runs the main Qemu loop. This represents the traditional setup. * When non-null, spawning the main Qemu event loop on a separate thread is now done centrally rather than inside the Cocoa UI code. * For most platforms, qemu_main is indeed NULL by default, but on Darwin, it defaults to a function that runs the CFRunLoop. * The Cocoa UI sets qemu_main to a function which runs the NSApplication event handling runloop, as is usual for a Cocoa app. * The SDL UI overrides the qemu_main function to NULL, thus specifying that Qemu's main loop must run on the main thread. * The GTK UI also overrides the qemu_main function to NULL. * For other UIs, or in the absence of UIs, the platform's default behaviour is followed. This means that on macOS, the platform's runloop events are always handled, regardless of chosen UI. The new PV graphics device will thus work in all configurations. There is no functional change on other operating systems. Implementing this via a global function pointer variable is a bit ugly, but it's probably worth investigating the existing UI thread rule violations in the SDL (e.g. #2537) and GTK+ back-ends. Fixing those issues might precipitate requirements similar but not identical to those of the Cocoa UI; hopefully we'll see some kind of pattern emerge, which can then be used as a basis for an overhaul. (In fact, it may turn out to be simplest to split the UI/native platform event thread from the QEMU main event loop on all platforms, with any UI or even none at all.) Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-2-phil@philjordan.eu> [PMD: Declare 'qemu_main' symbol in tests/qtest/fuzz/fuzz.c, add missing g_assert_not_reached() call in main()] Signed-off-by: Philippe Mathieu-Daudé --- include/qemu-main.h | 14 ++++++++++- system/main.c | 38 ++++++++++++++++++++++++++--- tests/qtest/fuzz/fuzz.c | 1 + ui/cocoa.m | 54 ++++++++++------------------------------- ui/gtk.c | 4 +++ ui/sdl2.c | 4 +++ 6 files changed, 69 insertions(+), 46 deletions(-) diff --git a/include/qemu-main.h b/include/qemu-main.h index 940960a7db..2ee83bedff 100644 --- a/include/qemu-main.h +++ b/include/qemu-main.h @@ -5,7 +5,19 @@ #ifndef QEMU_MAIN_H #define QEMU_MAIN_H -int qemu_default_main(void); +/* + * The function to run on the main (initial) thread of the process. + * NULL means QEMU's main event loop. + * When non-NULL, QEMU's main event loop will run on a purposely created + * thread, after which the provided function pointer will be invoked on + * the initial thread. + * This is useful on platforms which treat the main thread as special + * (macOS/Darwin) and/or require all UI API calls to occur from the main + * thread. Those platforms can initialise it to a specific function, + * while UI implementations may reset it to NULL during their init if they + * will handle system and UI events on the main thread via QEMU's own main + * event loop. + */ extern int (*qemu_main)(void); #endif /* QEMU_MAIN_H */ diff --git a/system/main.c b/system/main.c index 4923520741..ecb12fd397 100644 --- a/system/main.c +++ b/system/main.c @@ -24,26 +24,56 @@ #include "qemu/osdep.h" #include "qemu-main.h" +#include "qemu/main-loop.h" #include "system/system.h" #ifdef CONFIG_SDL +/* + * SDL insists on wrapping the main() function with its own implementation on + * some platforms; it does so via a macro that renames our main function, so + * must be #included here even with no SDL code called from this file. + */ #include #endif -int qemu_default_main(void) +#ifdef CONFIG_DARWIN +#include +#endif + +static void *qemu_default_main(void *opaque) { int status; + bql_lock(); status = qemu_main_loop(); qemu_cleanup(status); + bql_unlock(); - return status; + exit(status); } -int (*qemu_main)(void) = qemu_default_main; +int (*qemu_main)(void); + +#ifdef CONFIG_DARWIN +static int os_darwin_cfrunloop_main(void) +{ + CFRunLoopRun(); + g_assert_not_reached(); +} +int (*qemu_main)(void) = os_darwin_cfrunloop_main; +#endif int main(int argc, char **argv) { qemu_init(argc, argv); - return qemu_main(); + bql_unlock(); + if (qemu_main) { + QemuThread main_loop_thread; + qemu_thread_create(&main_loop_thread, "qemu_main", + qemu_default_main, NULL, QEMU_THREAD_DETACHED); + return qemu_main(); + } else { + qemu_default_main(NULL); + g_assert_not_reached(); + } } diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 8274000bd5..ca248a51a6 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -41,6 +41,7 @@ static FuzzTargetList *fuzz_target_list; static FuzzTarget *fuzz_target; static QTestState *fuzz_qts; +int (*qemu_main)(void); void flush_events(QTestState *s) diff --git a/ui/cocoa.m b/ui/cocoa.m index 3a88535374..23b7a736d7 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -73,6 +73,8 @@ typedef struct { int height; } QEMUScreen; +@class QemuCocoaPasteboardTypeOwner; + static void cocoa_update(DisplayChangeListener *dcl, int x, int y, int w, int h); @@ -107,6 +109,7 @@ static bool allow_events; static NSInteger cbchangecount = -1; static QemuClipboardInfo *cbinfo; static QemuEvent cbevent; +static QemuCocoaPasteboardTypeOwner *cbowner; // Utility functions to run specified code block with the BQL held typedef void (^CodeBlock)(void); @@ -1326,8 +1329,10 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { COCOA_DEBUG("QemuCocoaAppController: dealloc\n"); - if (cocoaView) - [cocoaView release]; + [cocoaView release]; + [cbowner release]; + cbowner = nil; + [super dealloc]; } @@ -1943,8 +1948,6 @@ static Notifier mouse_mode_change_notifier = { @end -static QemuCocoaPasteboardTypeOwner *cbowner; - static void cocoa_clipboard_notify(Notifier *notifier, void *data); static void cocoa_clipboard_request(QemuClipboardInfo *info, QemuClipboardType type); @@ -2007,43 +2010,8 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info, } } -/* - * The startup process for the OSX/Cocoa UI is complicated, because - * OSX insists that the UI runs on the initial main thread, and so we - * need to start a second thread which runs the qemu_default_main(): - * in main(): - * in cocoa_display_init(): - * assign cocoa_main to qemu_main - * create application, menus, etc - * in cocoa_main(): - * create qemu-main thread - * enter OSX run loop - */ - -static void *call_qemu_main(void *opaque) -{ - int status; - - COCOA_DEBUG("Second thread: calling qemu_default_main()\n"); - bql_lock(); - status = qemu_default_main(); - bql_unlock(); - COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n"); - [cbowner release]; - exit(status); -} - static int cocoa_main(void) { - QemuThread thread; - - COCOA_DEBUG("Entered %s()\n", __func__); - - bql_unlock(); - qemu_thread_create(&thread, "qemu_main", call_qemu_main, - NULL, QEMU_THREAD_DETACHED); - - // Start the main event loop COCOA_DEBUG("Main thread: entering OSX run loop\n"); [NSApp run]; COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n"); @@ -2125,8 +2093,6 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); - qemu_main = cocoa_main; - // Pull this console process up to being a fully-fledged graphical // app with a menubar and Dock icon ProcessSerialNumber psn = { 0, kCurrentProcess }; @@ -2190,6 +2156,12 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) qemu_clipboard_peer_register(&cbpeer); [pool release]; + + /* + * The Cocoa UI will run the NSApplication runloop on the main thread + * rather than the default Core Foundation one. + */ + qemu_main = cocoa_main; } static QemuDisplay qemu_display_cocoa = { diff --git a/ui/gtk.c b/ui/gtk.c index 0d38c070e4..c023743148 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -38,6 +38,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qemu-main.h" #include "ui/console.h" #include "ui/gtk.h" @@ -2485,6 +2486,9 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) #ifdef CONFIG_GTK_CLIPBOARD gd_clipboard_init(s); #endif /* CONFIG_GTK_CLIPBOARD */ + + /* GTK's event polling must happen on the main thread. */ + qemu_main = NULL; } static void early_gtk_display_init(DisplayOptions *opts) diff --git a/ui/sdl2.c b/ui/sdl2.c index 1fb72f67a6..445eb1dd9f 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -34,6 +34,7 @@ #include "system/system.h" #include "ui/win32-kbd-hook.h" #include "qemu/log.h" +#include "qemu-main.h" static int sdl2_num_outputs; static struct sdl2_console *sdl2_console; @@ -965,6 +966,9 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) } atexit(sdl_cleanup); + + /* SDL's event polling (in dpy_refresh) must happen on the main thread. */ + qemu_main = NULL; } static QemuDisplay qemu_display_sdl2 = { From 2352159c97a1fd245e998daafa08fcaaf57d4fa8 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 14 Jun 2023 22:57:33 +0000 Subject: [PATCH 0782/2892] hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MacOS provides a framework (library) that allows any vmm to implement a paravirtualized 3d graphics passthrough to the host metal stack called ParavirtualizedGraphics.Framework (PVG). The library abstracts away almost every aspect of the paravirtualized device model and only provides and receives callbacks on MMIO access as well as to share memory address space between the VM and PVG. This patch implements a QEMU device that drives PVG for the VMApple variant of it. Signed-off-by: Alexander Graf Co-authored-by: Alexander Graf Subsequent changes: * Cherry-pick/rebase conflict fixes, API use updates. * Moved from hw/vmapple/ (useful outside that machine type) * Overhaul of threading model, many thread safety improvements. * Asynchronous rendering. * Memory and object lifetime fixes. * Refactoring to split generic and (vmapple) MMIO variant specific code. Implementation wise, most of the complexity lies in the differing threading models of ParavirtualizedGraphics.framework, which uses libdispatch and internal locks, versus QEMU, which heavily uses the BQL, especially during memory-mapped device I/O. Great care has therefore been taken to prevent deadlocks by never calling into PVG methods while holding the BQL, and similarly never acquiring the BQL in a callback from PVG. Different strategies have been used (libdispatch, blocking and non-blocking BHs, RCU, etc.) depending on the specific requirements at each framework entry and exit point. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-3-phil@philjordan.eu> [PMD: Re-ordered imported headers, style fixups] Signed-off-by: Philippe Mathieu-Daudé --- hw/display/Kconfig | 9 + hw/display/apple-gfx-mmio.m | 278 +++++++++++++ hw/display/apple-gfx.h | 63 +++ hw/display/apple-gfx.m | 783 ++++++++++++++++++++++++++++++++++++ hw/display/meson.build | 6 + hw/display/trace-events | 28 ++ meson.build | 4 + 7 files changed, 1171 insertions(+) create mode 100644 hw/display/apple-gfx-mmio.m create mode 100644 hw/display/apple-gfx.h create mode 100644 hw/display/apple-gfx.m diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 2250c74007..6a9b7b19ad 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -140,3 +140,12 @@ config XLNX_DISPLAYPORT config DM163 bool + +config MAC_PVG + bool + default y + +config MAC_PVG_MMIO + bool + depends on MAC_PVG && AARCH64 + diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m new file mode 100644 index 0000000000..5a489d2d44 --- /dev/null +++ b/hw/display/apple-gfx-mmio.m @@ -0,0 +1,278 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device, MMIO (arm64) variant + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides + * which implements 3d graphics passthrough to the host as well as a + * proprietary guest communication channel to drive it. This device model + * implements support to drive that library from within QEMU as an MMIO-based + * system device for macOS on arm64 VMs. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "block/aio-wait.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "apple-gfx.h" +#include "trace.h" + +#import + +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXMMIOState, APPLE_GFX_MMIO) + +/* + * ParavirtualizedGraphics.Framework only ships header files for the PCI + * variant which does not include IOSFC descriptors and host devices. We add + * their definitions here so that we can also work with the ARM version. + */ +typedef bool(^IOSFCRaiseInterrupt)(uint32_t vector); +typedef bool(^IOSFCUnmapMemory)(void *, void *, void *, void *, void *, void *); +typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va, + void *, void *); + +@interface PGDeviceDescriptor (IOSurfaceMapper) +@property (readwrite, nonatomic) bool usingIOSurfaceMapper; +@end + +@interface PGIOSurfaceHostDeviceDescriptor : NSObject +-(PGIOSurfaceHostDeviceDescriptor *)init; +@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory; +@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory; +@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt; +@end + +@interface PGIOSurfaceHostDevice : NSObject +-(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *)desc; +-(uint32_t)mmioReadAtOffset:(size_t)offset; +-(void)mmioWriteAtOffset:(size_t)offset value:(uint32_t)value; +@end + +struct AppleGFXMapSurfaceMemoryJob; +struct AppleGFXMMIOState { + SysBusDevice parent_obj; + + AppleGFXState common; + + qemu_irq irq_gfx; + qemu_irq irq_iosfc; + MemoryRegion iomem_iosfc; + PGIOSurfaceHostDevice *pgiosfc; +}; + +typedef struct AppleGFXMMIOJob { + AppleGFXMMIOState *state; + uint64_t offset; + uint64_t value; + bool completed; +} AppleGFXMMIOJob; + +static void iosfc_do_read(void *opaque) +{ + AppleGFXMMIOJob *job = opaque; + job->value = [job->state->pgiosfc mmioReadAtOffset:job->offset]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static uint64_t iosfc_read(void *opaque, hwaddr offset, unsigned size) +{ + AppleGFXMMIOJob job = { + .state = opaque, + .offset = offset, + .completed = false, + }; + dispatch_queue_t queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_async_f(queue, &job, iosfc_do_read); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_mmio_iosfc_read(offset, job.value); + return job.value; +} + +static void iosfc_do_write(void *opaque) +{ + AppleGFXMMIOJob *job = opaque; + [job->state->pgiosfc mmioWriteAtOffset:job->offset value:job->value]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static void iosfc_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + AppleGFXMMIOJob job = { + .state = opaque, + .offset = offset, + .value = val, + .completed = false, + }; + dispatch_queue_t queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_async_f(queue, &job, iosfc_do_write); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_mmio_iosfc_write(offset, val); +} + +static const MemoryRegionOps apple_iosfc_ops = { + .read = iosfc_read, + .write = iosfc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static void raise_irq_bh(void *opaque) +{ + qemu_irq *irq = opaque; + + qemu_irq_pulse(*irq); +} + +static void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address, + uint64_t length, bool read_only) +{ + void *mem; + MemoryRegion *region = NULL; + + RCU_READ_LOCK_GUARD(); + mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address, + length, read_only, ®ion); + if (mem) { + memory_region_ref(region); + } + return mem; +} + +static bool apple_gfx_mmio_unmap_surface_memory(void *ptr) +{ + MemoryRegion *region; + ram_addr_t offset = 0; + + RCU_READ_LOCK_GUARD(); + region = memory_region_from_host(ptr, &offset); + if (!region) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: memory at %p to be unmapped not found.\n", + __func__, ptr); + return false; + } + + trace_apple_gfx_iosfc_unmap_memory_region(ptr, region); + memory_region_unref(region); + return true; +} + +static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device( + AppleGFXMMIOState *s) +{ + PGIOSurfaceHostDeviceDescriptor *iosfc_desc = + [PGIOSurfaceHostDeviceDescriptor new]; + PGIOSurfaceHostDevice *iosfc_host_dev; + + iosfc_desc.mapMemory = + ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) { + *va = apple_gfx_mmio_map_surface_memory(phys, len, ro); + + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va); + + return *va != NULL; + }; + + iosfc_desc.unmapMemory = + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) { + return apple_gfx_mmio_unmap_surface_memory(va); + }; + + iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) { + trace_apple_gfx_iosfc_raise_irq(vector); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + raise_irq_bh, &s->irq_iosfc); + return true; + }; + + iosfc_host_dev = + [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc]; + [iosfc_desc release]; + return iosfc_host_dev; +} + +static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp) +{ + @autoreleasepool { + AppleGFXMMIOState *s = APPLE_GFX_MMIO(dev); + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + + desc.raiseInterrupt = ^(uint32_t vector) { + trace_apple_gfx_raise_irq(vector); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + raise_irq_bh, &s->irq_gfx); + }; + + desc.usingIOSurfaceMapper = true; + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s); + + if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) { + [s->pgiosfc release]; + s->pgiosfc = nil; + } + + [desc release]; + desc = nil; + } +} + +static void apple_gfx_mmio_init(Object *obj) +{ + AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj); + + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_MMIO); + + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx); + memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s, + TYPE_APPLE_GFX_MMIO, 0x10000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc); +} + +static void apple_gfx_mmio_reset(Object *obj, ResetType type) +{ + AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj); + [s->common.pgdev reset]; +} + + +static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = apple_gfx_mmio_reset; + dc->hotpluggable = false; + dc->realize = apple_gfx_mmio_realize; +} + +static const TypeInfo apple_gfx_mmio_types[] = { + { + .name = TYPE_APPLE_GFX_MMIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AppleGFXMMIOState), + .class_init = apple_gfx_mmio_class_init, + .instance_init = apple_gfx_mmio_init, + } +}; +DEFINE_TYPES(apple_gfx_mmio_types) diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h new file mode 100644 index 0000000000..4cd4163f22 --- /dev/null +++ b/hw/display/apple-gfx.h @@ -0,0 +1,63 @@ +/* + * Data structures and functions shared between variants of the macOS + * ParavirtualizedGraphics.framework based apple-gfx display adapter. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_APPLE_GFX_H +#define QEMU_APPLE_GFX_H + +#include "qemu/queue.h" +#include "exec/memory.h" +#include "hw/qdev-properties.h" +#include "ui/surface.h" + +#define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio" + +@class PGDeviceDescriptor; +@protocol PGDevice; +@protocol PGDisplay; +@protocol MTLDevice; +@protocol MTLTexture; +@protocol MTLCommandQueue; + +typedef QTAILQ_HEAD(, PGTask_s) PGTaskList; + +typedef struct AppleGFXState { + /* Initialised on init/realize() */ + MemoryRegion iomem_gfx; + id pgdev; + id pgdisp; + QemuConsole *con; + id mtl; + id mtl_queue; + + /* List `tasks` is protected by task_mutex */ + QemuMutex task_mutex; + PGTaskList tasks; + + /* Mutable state (BQL protected) */ + QEMUCursor *cursor; + DisplaySurface *surface; + id texture; + int8_t pending_frames; /* # guest frames in the rendering pipeline */ + bool gfx_update_requested; /* QEMU display system wants a new frame */ + bool new_frame_ready; /* Guest has rendered a frame, ready to be used */ + bool using_managed_texture_storage; + uint32_t rendering_frame_width; + uint32_t rendering_frame_height; + + /* Mutable state (atomic) */ + bool cursor_show; +} AppleGFXState; + +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); +bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, + PGDeviceDescriptor *desc, Error **errp); +void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, + uint64_t length, bool read_only, + MemoryRegion **mapping_in_region); + +#endif + diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m new file mode 100644 index 0000000000..59299e339d --- /dev/null +++ b/hw/display/apple-gfx.m @@ -0,0 +1,783 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides + * which implements 3d graphics passthrough to the host as well as a + * proprietary guest communication channel to drive it. This device model + * implements support to drive that library from within QEMU. + */ + +#include "qemu/osdep.h" +#include "qemu/lockable.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "block/aio-wait.h" +#include "exec/address-spaces.h" +#include "system/dma.h" +#include "migration/blocker.h" +#include "ui/console.h" +#include "apple-gfx.h" +#include "trace.h" + +#include +#include +#include + +#import + +static const PGDisplayCoord_t apple_gfx_modes[] = { + { .x = 1440, .y = 1080 }, + { .x = 1280, .y = 1024 }, +}; + +static Error *apple_gfx_mig_blocker; +static uint32_t next_pgdisplay_serial_num = 1; + +static dispatch_queue_t get_background_queue(void) +{ + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +/* ------ PGTask and task operations: new/destroy/map/unmap ------ */ + +/* + * This implements the type declared in + * which is opaque from the framework's point of view. It is used in callbacks + * in the form of its typedef PGTask_t, which also already exists in the + * framework headers. + * + * A "task" in PVG terminology represents a host-virtual contiguous address + * range which is reserved in a large chunk on task creation. The mapMemory + * callback then requests ranges of guest system memory (identified by their + * GPA) to be mapped into subranges of this reserved address space. + * This type of operation isn't well-supported by QEMU's memory subsystem, + * but it is fortunately trivial to achieve with Darwin's mach_vm_remap() call, + * which allows us to refer to the same backing memory via multiple virtual + * address ranges. The Mach VM APIs are therefore used throughout for managing + * task memory. + */ +struct PGTask_s { + QTAILQ_ENTRY(PGTask_s) node; + AppleGFXState *s; + mach_vm_address_t address; + uint64_t len; + /* + * All unique MemoryRegions for which a mapping has been created in in this + * task, and on which we have thus called memory_region_ref(). There are + * usually very few regions of system RAM in total, so we expect this array + * to be very short. Therefore, no need for sorting or fancy search + * algorithms, linear search will do. + * Protected by AppleGFXState's task_mutex. + */ + GPtrArray *mapped_regions; +}; + +static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len) +{ + mach_vm_address_t task_mem; + PGTask_t *task; + kern_return_t r; + + r = mach_vm_allocate(mach_task_self(), &task_mem, len, VM_FLAGS_ANYWHERE); + if (r != KERN_SUCCESS) { + return NULL; + } + + task = g_new0(PGTask_t, 1); + task->s = s; + task->address = task_mem; + task->len = len; + task->mapped_regions = g_ptr_array_sized_new(2 /* Usually enough */); + + QEMU_LOCK_GUARD(&s->task_mutex); + QTAILQ_INSERT_TAIL(&s->tasks, task, node); + + return task; +} + +static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task) +{ + GPtrArray *regions = task->mapped_regions; + MemoryRegion *region; + size_t i; + + for (i = 0; i < regions->len; ++i) { + region = g_ptr_array_index(regions, i); + memory_region_unref(region); + } + g_ptr_array_unref(regions); + + mach_vm_deallocate(mach_task_self(), task->address, task->len); + + QEMU_LOCK_GUARD(&s->task_mutex); + QTAILQ_REMOVE(&s->tasks, task, node); + g_free(task); +} + +void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, + uint64_t length, bool read_only, + MemoryRegion **mapping_in_region) +{ + MemoryRegion *ram_region; + char *host_ptr; + hwaddr ram_region_offset = 0; + hwaddr ram_region_length = length; + + ram_region = address_space_translate(&address_space_memory, + guest_physical, + &ram_region_offset, + &ram_region_length, !read_only, + MEMTXATTRS_UNSPECIFIED); + + if (!ram_region || ram_region_length < length || + !memory_access_is_direct(ram_region, !read_only)) { + return NULL; + } + + host_ptr = memory_region_get_ram_ptr(ram_region); + if (!host_ptr) { + return NULL; + } + host_ptr += ram_region_offset; + *mapping_in_region = ram_region; + return host_ptr; +} + +static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task, + uint64_t virtual_offset, + PGPhysicalMemoryRange_t *ranges, + uint32_t range_count, bool read_only) +{ + kern_return_t r; + void *source_ptr; + mach_vm_address_t target; + vm_prot_t cur_protection, max_protection; + bool success = true; + MemoryRegion *region; + + RCU_READ_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->task_mutex); + + trace_apple_gfx_map_memory(task, range_count, virtual_offset, read_only); + for (int i = 0; i < range_count; i++) { + PGPhysicalMemoryRange_t *range = &ranges[i]; + + target = task->address + virtual_offset; + virtual_offset += range->physicalLength; + + trace_apple_gfx_map_memory_range(i, range->physicalAddress, + range->physicalLength); + + region = NULL; + source_ptr = apple_gfx_host_ptr_for_gpa_range(range->physicalAddress, + range->physicalLength, + read_only, ®ion); + if (!source_ptr) { + success = false; + continue; + } + + if (!g_ptr_array_find(task->mapped_regions, region, NULL)) { + g_ptr_array_add(task->mapped_regions, region); + memory_region_ref(region); + } + + cur_protection = 0; + max_protection = 0; + /* Map guest RAM at range->physicalAddress into PG task memory range */ + r = mach_vm_remap(mach_task_self(), + &target, range->physicalLength, vm_page_size - 1, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + mach_task_self(), (mach_vm_address_t)source_ptr, + false /* shared mapping, no copy */, + &cur_protection, &max_protection, + VM_INHERIT_COPY); + trace_apple_gfx_remap(r, source_ptr, target); + g_assert(r == KERN_SUCCESS); + } + + return success; +} + +static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task, + uint64_t virtual_offset, uint64_t length) +{ + kern_return_t r; + mach_vm_address_t range_address; + + trace_apple_gfx_unmap_memory(task, virtual_offset, length); + + /* + * Replace task memory range with fresh 0 pages, undoing the mapping + * from guest RAM. + */ + range_address = task->address + virtual_offset; + r = mach_vm_allocate(mach_task_self(), &range_address, length, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); + g_assert(r == KERN_SUCCESS); +} + +/* ------ Rendering and frame management ------ */ + +static void apple_gfx_render_frame_completed_bh(void *opaque); + +static void apple_gfx_render_new_frame(AppleGFXState *s) +{ + bool managed_texture = s->using_managed_texture_storage; + uint32_t width = surface_width(s->surface); + uint32_t height = surface_height(s->surface); + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + id command_buffer = [s->mtl_queue commandBuffer]; + id texture = s->texture; + + assert(bql_locked()); + [texture retain]; + [command_buffer retain]; + + s->rendering_frame_width = width; + s->rendering_frame_height = height; + + dispatch_async(get_background_queue(), ^{ + /* + * This is not safe to call from the BQL/BH due to PVG-internal locks + * causing deadlocks. + */ + bool r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer + texture:texture + region:region]; + if (!r) { + [texture release]; + [command_buffer release]; + qemu_log_mask(LOG_GUEST_ERROR, + "%s: encodeCurrentFrameToCommandBuffer:texture:region: " + "failed\n", __func__); + bql_lock(); + --s->pending_frames; + if (s->pending_frames > 0) { + apple_gfx_render_new_frame(s); + } + bql_unlock(); + return; + } + + if (managed_texture) { + /* "Managed" textures exist in both VRAM and RAM and must be synced. */ + id blit = [command_buffer blitCommandEncoder]; + [blit synchronizeResource:texture]; + [blit endEncoding]; + } + [texture release]; + [command_buffer addCompletedHandler: + ^(id cb) + { + aio_bh_schedule_oneshot(qemu_get_aio_context(), + apple_gfx_render_frame_completed_bh, s); + }]; + [command_buffer commit]; + [command_buffer release]; + }); +} + +static void copy_mtl_texture_to_surface_mem(id texture, void *vram) +{ + /* + * TODO: Skip this entirely on a pure Metal or headless/guest-only + * rendering path, else use a blit command encoder? Needs careful + * (double?) buffering design. + */ + size_t width = texture.width, height = texture.height; + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + [texture getBytes:vram + bytesPerRow:(width * 4) + bytesPerImage:(width * height * 4) + fromRegion:region + mipmapLevel:0 + slice:0]; +} + +static void apple_gfx_render_frame_completed_bh(void *opaque) +{ + AppleGFXState *s = opaque; + + @autoreleasepool { + --s->pending_frames; + assert(s->pending_frames >= 0); + + /* Only update display if mode hasn't changed since we started rendering. */ + if (s->rendering_frame_width == surface_width(s->surface) && + s->rendering_frame_height == surface_height(s->surface)) { + copy_mtl_texture_to_surface_mem(s->texture, surface_data(s->surface)); + if (s->gfx_update_requested) { + s->gfx_update_requested = false; + dpy_gfx_update_full(s->con); + graphic_hw_update_done(s->con); + s->new_frame_ready = false; + } else { + s->new_frame_ready = true; + } + } + if (s->pending_frames > 0) { + apple_gfx_render_new_frame(s); + } + } +} + +static void apple_gfx_fb_update_display(void *opaque) +{ + AppleGFXState *s = opaque; + + assert(bql_locked()); + if (s->new_frame_ready) { + dpy_gfx_update_full(s->con); + s->new_frame_ready = false; + graphic_hw_update_done(s->con); + } else if (s->pending_frames > 0) { + s->gfx_update_requested = true; + } else { + graphic_hw_update_done(s->con); + } +} + +static const GraphicHwOps apple_gfx_fb_ops = { + .gfx_update = apple_gfx_fb_update_display, + .gfx_update_async = true, +}; + +/* ------ Mouse cursor and display mode setting ------ */ + +static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) +{ + MTLTextureDescriptor *textureDescriptor; + + if (s->surface && + width == surface_width(s->surface) && + height == surface_height(s->surface)) { + return; + } + + [s->texture release]; + + s->surface = qemu_create_displaysurface(width, height); + + @autoreleasepool { + textureDescriptor = + [MTLTextureDescriptor + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:width + height:height + mipmapped:NO]; + textureDescriptor.usage = s->pgdisp.minimumTextureUsage; + s->texture = [s->mtl newTextureWithDescriptor:textureDescriptor]; + s->using_managed_texture_storage = + (s->texture.storageMode == MTLStorageModeManaged); + } + + dpy_gfx_replace_surface(s->con, s->surface); +} + +static void update_cursor(AppleGFXState *s) +{ + assert(bql_locked()); + dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x, + s->pgdisp.cursorPosition.y, qatomic_read(&s->cursor_show)); +} + +static void update_cursor_bh(void *opaque) +{ + AppleGFXState *s = opaque; + update_cursor(s); +} + +typedef struct AppleGFXSetCursorGlyphJob { + AppleGFXState *s; + NSBitmapImageRep *glyph; + PGDisplayCoord_t hotspot; +} AppleGFXSetCursorGlyphJob; + +static void set_cursor_glyph(void *opaque) +{ + AppleGFXSetCursorGlyphJob *job = opaque; + AppleGFXState *s = job->s; + NSBitmapImageRep *glyph = job->glyph; + uint32_t bpp = glyph.bitsPerPixel; + size_t width = glyph.pixelsWide; + size_t height = glyph.pixelsHigh; + size_t padding_bytes_per_row = glyph.bytesPerRow - width * 4; + const uint8_t* px_data = glyph.bitmapData; + + trace_apple_gfx_cursor_set(bpp, width, height); + + if (s->cursor) { + cursor_unref(s->cursor); + s->cursor = NULL; + } + + if (bpp == 32) { /* Shouldn't be anything else, but just to be safe... */ + s->cursor = cursor_alloc(width, height); + s->cursor->hot_x = job->hotspot.x; + s->cursor->hot_y = job->hotspot.y; + + uint32_t *dest_px = s->cursor->data; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + /* + * NSBitmapImageRep's red & blue channels are swapped + * compared to QEMUCursor's. + */ + *dest_px = + (px_data[0] << 16u) | + (px_data[1] << 8u) | + (px_data[2] << 0u) | + (px_data[3] << 24u); + ++dest_px; + px_data += 4; + } + px_data += padding_bytes_per_row; + } + dpy_cursor_define(s->con, s->cursor); + update_cursor(s); + } + [glyph release]; + + g_free(job); +} + +/* ------ DMA (device reading system memory) ------ */ + +typedef struct AppleGFXReadMemoryJob { + QemuSemaphore sem; + hwaddr physical_address; + uint64_t length; + void *dst; + bool success; +} AppleGFXReadMemoryJob; + +static void apple_gfx_do_read_memory(void *opaque) +{ + AppleGFXReadMemoryJob *job = opaque; + MemTxResult r; + + r = dma_memory_read(&address_space_memory, job->physical_address, + job->dst, job->length, MEMTXATTRS_UNSPECIFIED); + job->success = (r == MEMTX_OK); + + qemu_sem_post(&job->sem); +} + +static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address, + uint64_t length, void *dst) +{ + AppleGFXReadMemoryJob job = { + .physical_address = physical_address, .length = length, .dst = dst + }; + + trace_apple_gfx_read_memory(physical_address, length, dst); + + /* Performing DMA requires BQL, so do it in a BH. */ + qemu_sem_init(&job.sem, 0); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + apple_gfx_do_read_memory, &job); + qemu_sem_wait(&job.sem); + qemu_sem_destroy(&job.sem); + return job.success; +} + +/* ------ Memory-mapped device I/O operations ------ */ + +typedef struct AppleGFXIOJob { + AppleGFXState *state; + uint64_t offset; + uint64_t value; + bool completed; +} AppleGFXIOJob; + +static void apple_gfx_do_read(void *opaque) +{ + AppleGFXIOJob *job = opaque; + job->value = [job->state->pgdev mmioReadAtOffset:job->offset]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static uint64_t apple_gfx_read(void *opaque, hwaddr offset, unsigned size) +{ + AppleGFXIOJob job = { + .state = opaque, + .offset = offset, + .completed = false, + }; + dispatch_queue_t queue = get_background_queue(); + + dispatch_async_f(queue, &job, apple_gfx_do_read); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_read(offset, job.value); + return job.value; +} + +static void apple_gfx_do_write(void *opaque) +{ + AppleGFXIOJob *job = opaque; + [job->state->pgdev mmioWriteAtOffset:job->offset value:job->value]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static void apple_gfx_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + /* + * The methods mmioReadAtOffset: and especially mmioWriteAtOffset: can + * trigger synchronous operations on other dispatch queues, which in turn + * may call back out on one or more of the callback blocks. For this reason, + * and as we are holding the BQL, we invoke the I/O methods on a pool + * thread and handle AIO tasks while we wait. Any work in the callbacks + * requiring the BQL will in turn schedule BHs which this thread will + * process while waiting. + */ + AppleGFXIOJob job = { + .state = opaque, + .offset = offset, + .value = val, + .completed = false, + }; + dispatch_queue_t queue = get_background_queue(); + + dispatch_async_f(queue, &job, apple_gfx_do_write); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_write(offset, val); +} + +static const MemoryRegionOps apple_gfx_ops = { + .read = apple_gfx_read, + .write = apple_gfx_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static size_t apple_gfx_get_default_mmio_range_size(void) +{ + size_t mmio_range_size; + @autoreleasepool { + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + mmio_range_size = desc.mmioLength; + [desc release]; + } + return mmio_range_size; +} + +/* ------ Initialisation and startup ------ */ + +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) +{ + size_t mmio_range_size = apple_gfx_get_default_mmio_range_size(); + + trace_apple_gfx_common_init(obj_name, mmio_range_size); + memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name, + mmio_range_size); + + /* TODO: PVG framework supports serialising device state: integrate it! */ +} + +static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, + PGDeviceDescriptor *desc) +{ + desc.createTask = ^(uint64_t vmSize, void * _Nullable * _Nonnull baseAddress) { + PGTask_t *task = apple_gfx_new_task(s, vmSize); + *baseAddress = (void *)task->address; + trace_apple_gfx_create_task(vmSize, *baseAddress); + return task; + }; + + desc.destroyTask = ^(PGTask_t * _Nonnull task) { + trace_apple_gfx_destroy_task(task, task->mapped_regions->len); + + apple_gfx_destroy_task(s, task); + }; + + desc.mapMemory = ^bool(PGTask_t * _Nonnull task, uint32_t range_count, + uint64_t virtual_offset, bool read_only, + PGPhysicalMemoryRange_t * _Nonnull ranges) { + return apple_gfx_task_map_memory(s, task, virtual_offset, + ranges, range_count, read_only); + }; + + desc.unmapMemory = ^bool(PGTask_t * _Nonnull task, uint64_t virtual_offset, + uint64_t length) { + apple_gfx_task_unmap_memory(s, task, virtual_offset, length); + return true; + }; + + desc.readMemory = ^bool(uint64_t physical_address, uint64_t length, + void * _Nonnull dst) { + return apple_gfx_read_memory(s, physical_address, length, dst); + }; +} + +static void new_frame_handler_bh(void *opaque) +{ + AppleGFXState *s = opaque; + + /* Drop frames if guest gets too far ahead. */ + if (s->pending_frames >= 2) { + return; + } + ++s->pending_frames; + if (s->pending_frames > 1) { + return; + } + + @autoreleasepool { + apple_gfx_render_new_frame(s); + } +} + +static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState *s) +{ + PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new]; + + disp_desc.name = @"QEMU display"; + disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */ + disp_desc.queue = dispatch_get_main_queue(); + disp_desc.newFrameEventHandler = ^(void) { + trace_apple_gfx_new_frame(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), new_frame_handler_bh, s); + }; + disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels, + OSType pixelFormat) { + trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y); + + BQL_LOCK_GUARD(); + set_mode(s, sizeInPixels.x, sizeInPixels.y); + }; + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph, + PGDisplayCoord_t hotspot) { + AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job)); + job->s = s; + job->glyph = glyph; + job->hotspot = hotspot; + [glyph retain]; + aio_bh_schedule_oneshot(qemu_get_aio_context(), + set_cursor_glyph, job); + }; + disp_desc.cursorShowHandler = ^(BOOL show) { + trace_apple_gfx_cursor_show(show); + qatomic_set(&s->cursor_show, show); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + update_cursor_bh, s); + }; + disp_desc.cursorMoveHandler = ^(void) { + trace_apple_gfx_cursor_move(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + update_cursor_bh, s); + }; + + return disp_desc; +} + +static NSArray* apple_gfx_prepare_display_mode_array(void) +{ + PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; + NSArray* mode_array; + int i; + + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { + modes[i] = + [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; + } + + mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; + + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { + [modes[i] release]; + modes[i] = nil; + } + + return mode_array; +} + +static id copy_suitable_metal_device(void) +{ + id dev = nil; + NSArray> *devs = MTLCopyAllDevices(); + + /* Prefer a unified memory GPU. Failing that, pick a non-removable GPU. */ + for (size_t i = 0; i < devs.count; ++i) { + if (devs[i].hasUnifiedMemory) { + dev = devs[i]; + break; + } + if (!devs[i].removable) { + dev = devs[i]; + } + } + + if (dev != nil) { + [dev retain]; + } else { + dev = MTLCreateSystemDefaultDevice(); + } + [devs release]; + + return dev; +} + +bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, + PGDeviceDescriptor *desc, Error **errp) +{ + PGDisplayDescriptor *disp_desc; + + if (apple_gfx_mig_blocker == NULL) { + error_setg(&apple_gfx_mig_blocker, + "Migration state blocked by apple-gfx display device"); + if (migrate_add_blocker(&apple_gfx_mig_blocker, errp) < 0) { + return false; + } + } + + qemu_mutex_init(&s->task_mutex); + QTAILQ_INIT(&s->tasks); + s->mtl = copy_suitable_metal_device(); + s->mtl_queue = [s->mtl newCommandQueue]; + + desc.device = s->mtl; + + apple_gfx_register_task_mapping_handlers(s, desc); + + s->cursor_show = true; + + s->pgdev = PGNewDeviceWithDescriptor(desc); + + disp_desc = apple_gfx_prepare_display_descriptor(s); + /* + * Although the framework does, this integration currently does not support + * multiple virtual displays connected to a single PV graphics device. + * It is however possible to create + * more than one instance of the device, each with one display. The macOS + * guest will ignore these displays if they share the same serial number, + * so ensure each instance gets a unique one. + */ + s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc + port:0 + serialNum:next_pgdisplay_serial_num++]; + [disp_desc release]; + s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); + + s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s); + return true; +} diff --git a/hw/display/meson.build b/hw/display/meson.build index 20a94973fa..cf9e6dd35d 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -61,6 +61,12 @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) +if host_os == 'darwin' + system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) + if cpu == 'aarch64' + system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal]) + endif +endif if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() diff --git a/hw/display/trace-events b/hw/display/trace-events index d26d663f96..a50e4eea0c 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -194,3 +194,31 @@ dm163_bits_ppi(unsigned dest_width) "dest_width : %u" dm163_leds(int led, uint32_t value) "led %d: 0x%x" dm163_channels(int channel, uint8_t value) "channel %d: 0x%x" dm163_refresh_rate(uint32_t rr) "refresh rate %d" + +# apple-gfx.m +apple_gfx_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +apple_gfx_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +apple_gfx_create_task(uint32_t vm_size, void *va) "vm_size=0x%x base_addr=%p" +apple_gfx_destroy_task(void *task, unsigned int num_mapped_regions) "task=%p, task->mapped_regions->len=%u" +apple_gfx_map_memory(void *task, uint32_t range_count, uint64_t virtual_offset, uint32_t read_only) "task=%p range_count=0x%x virtual_offset=0x%"PRIx64" read_only=%d" +apple_gfx_map_memory_range(uint32_t i, uint64_t phys_addr, uint64_t phys_len) "[%d] phys_addr=0x%"PRIx64" phys_len=0x%"PRIx64 +apple_gfx_remap(uint64_t retval, void *source_ptr, uint64_t target) "retval=%"PRId64" source=%p target=0x%"PRIx64 +apple_gfx_unmap_memory(void *task, uint64_t virtual_offset, uint64_t length) "task=%p virtual_offset=0x%"PRIx64" length=0x%"PRIx64 +apple_gfx_read_memory(uint64_t phys_address, uint64_t length, void *dst) "phys_addr=0x%"PRIx64" length=0x%"PRIx64" dest=%p" +apple_gfx_raise_irq(uint32_t vector) "vector=0x%x" +apple_gfx_new_frame(void) "" +apple_gfx_mode_change(uint64_t x, uint64_t y) "x=%"PRId64" y=%"PRId64 +apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d width=%"PRId64" height=0x%"PRId64 +apple_gfx_cursor_show(uint32_t show) "show=%d" +apple_gfx_cursor_move(void) "" +apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes" + +# apple-gfx-mmio.m +apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +apple_gfx_mmio_iosfc_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +apple_gfx_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f, void* va_result) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p -> *va=%p" +apple_gfx_iosfc_map_memory_new_region(size_t i, void *region, uint64_t start, uint64_t end) "index=%zu, region=%p, 0x%"PRIx64"-0x%"PRIx64 +apple_gfx_iosfc_unmap_memory(void *a, void *b, void *c, void *d, void *e, void *f) "a=%p b=%p c=%p d=%p e=%p f=%p" +apple_gfx_iosfc_unmap_memory_region(void* mem, void *region) "unmapping @ %p from memory region %p" +apple_gfx_iosfc_raise_irq(uint32_t vector) "vector=0x%x" + diff --git a/meson.build b/meson.build index f4109cd3ca..e62251c7ca 100644 --- a/meson.build +++ b/meson.build @@ -817,6 +817,8 @@ socket = [] version_res = [] coref = [] iokit = [] +pvg = not_found +metal = [] emulator_link_args = [] midl = not_found widl = not_found @@ -838,6 +840,8 @@ elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' + pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics') + metal = dependency('appleframeworks', modules: 'Metal') elif host_os == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), From b21f17cce561316f1eb1917a4ca660e65c75c420 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Mon, 15 Jul 2024 23:06:57 +0200 Subject: [PATCH 0783/2892] hw/display/apple-gfx: Adds PCI implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change wires up the PCI variant of the paravirtualised graphics device, mainly useful for x86-64 macOS guests, implemented by macOS's ParavirtualizedGraphics.framework. It builds on code shared with the vmapple/mmio variant of the PVG device. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Message-ID: <20241223221645.29911-4-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/Kconfig | 4 + hw/display/apple-gfx-pci.m | 151 +++++++++++++++++++++++++++++++++++++ hw/display/apple-gfx.h | 1 + hw/display/meson.build | 1 + 4 files changed, 157 insertions(+) create mode 100644 hw/display/apple-gfx-pci.m diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 6a9b7b19ad..2b53dfd7d2 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -149,3 +149,7 @@ config MAC_PVG_MMIO bool depends on MAC_PVG && AARCH64 +config MAC_PVG_PCI + bool + depends on MAC_PVG && PCI + default y if PCI_DEVICES diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m new file mode 100644 index 0000000000..35a3c7a7ce --- /dev/null +++ b/hw/display/apple-gfx-pci.m @@ -0,0 +1,151 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device, PCI variant + * + * Copyright © 2023-2024 Phil Dennis-Jordan + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides + * which implements 3d graphics passthrough to the host as well as a + * proprietary guest communication channel to drive it. This device model + * implements support to drive that library from within QEMU as a PCI device + * aimed primarily at x86-64 macOS VMs. + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/msi.h" +#include "apple-gfx.h" +#include "trace.h" + +#import + +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI) + +struct AppleGFXPCIState { + PCIDevice parent_obj; + + AppleGFXState common; +}; + +static const char *apple_gfx_pci_option_rom_path = NULL; + +static void apple_gfx_init_option_rom_path(void) +{ + NSURL *option_rom_url = PGCopyOptionROMURL(); + const char *option_rom_path = option_rom_url.fileSystemRepresentation; + apple_gfx_pci_option_rom_path = g_strdup(option_rom_path); + [option_rom_url release]; +} + +static void apple_gfx_pci_init(Object *obj) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); + + if (!apple_gfx_pci_option_rom_path) { + /* + * The following is done on device not class init to avoid running + * ObjC code before fork() in -daemonize mode. + */ + PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj)); + apple_gfx_init_option_rom_path(); + pci->romfile = apple_gfx_pci_option_rom_path; + } + + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI); +} + +typedef struct AppleGFXPCIInterruptJob { + PCIDevice *device; + uint32_t vector; +} AppleGFXPCIInterruptJob; + +static void apple_gfx_pci_raise_interrupt(void *opaque) +{ + AppleGFXPCIInterruptJob *job = opaque; + + if (msi_enabled(job->device)) { + msi_notify(job->device, job->vector); + } + g_free(job); +} + +static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector) +{ + AppleGFXPCIInterruptJob *job; + + trace_apple_gfx_raise_irq(vector); + job = g_malloc0(sizeof(*job)); + job->device = dev; + job->vector = vector; + aio_bh_schedule_oneshot(qemu_get_aio_context(), + apple_gfx_pci_raise_interrupt, job); +} + +static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(dev); + int ret; + + pci_register_bar(dev, PG_PCI_BAR_MMIO, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx); + + ret = msi_init(dev, 0x0 /* config offset; 0 = find space */, + PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */, + false /* msi_per_vector_mask */, errp); + if (ret != 0) { + return; + } + + @autoreleasepool { + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + desc.raiseInterrupt = ^(uint32_t vector) { + apple_gfx_pci_interrupt(dev, vector); + }; + + apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp); + [desc release]; + desc = nil; + } +} + +static void apple_gfx_pci_reset(Object *obj, ResetType type) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); + [s->common.pgdev reset]; +} + +static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = apple_gfx_pci_reset; + dc->desc = "macOS Paravirtualized Graphics PCI Display Controller"; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + + pci->vendor_id = PG_PCI_VENDOR_ID; + pci->device_id = PG_PCI_DEVICE_ID; + pci->class_id = PCI_CLASS_DISPLAY_OTHER; + pci->realize = apple_gfx_pci_realize; + + /* TODO: Property for setting mode list */ +} + +static const TypeInfo apple_gfx_pci_types[] = { + { + .name = TYPE_APPLE_GFX_PCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(AppleGFXPCIState), + .class_init = apple_gfx_pci_class_init, + .instance_init = apple_gfx_pci_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { }, + }, + } +}; +DEFINE_TYPES(apple_gfx_pci_types) + diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h index 4cd4163f22..6c74209b36 100644 --- a/hw/display/apple-gfx.h +++ b/hw/display/apple-gfx.h @@ -14,6 +14,7 @@ #include "ui/surface.h" #define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio" +#define TYPE_APPLE_GFX_PCI "apple-gfx-pci" @class PGDeviceDescriptor; @protocol PGDevice; diff --git a/hw/display/meson.build b/hw/display/meson.build index cf9e6dd35d..94f4f05d36 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -63,6 +63,7 @@ system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_ if host_os == 'darwin' system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) + system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal]) if cpu == 'aarch64' system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal]) endif From bb43a2342ddf57e6cd4f24161ee4e641cfbcceca Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Mon, 15 Jul 2024 23:07:03 +0200 Subject: [PATCH 0784/2892] hw/display/apple-gfx: Adds configurable mode list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds a property 'display_modes' on the graphics device which permits specifying a list of display modes. (screen resolution and refresh rate) The property is an array of a custom type to make the syntax slightly less awkward to use, for example: -device '{"driver":"apple-gfx-pci", "display-modes":["1920x1080@60", "3840x2160@60"]}' Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-5-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/apple-gfx-mmio.m | 7 ++ hw/display/apple-gfx-pci.m | 8 ++- hw/display/apple-gfx.h | 10 +++ hw/display/apple-gfx.m | 130 +++++++++++++++++++++++++++++++----- hw/display/trace-events | 2 + 5 files changed, 139 insertions(+), 18 deletions(-) diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m index 5a489d2d44..b2e0e7a30f 100644 --- a/hw/display/apple-gfx-mmio.m +++ b/hw/display/apple-gfx-mmio.m @@ -255,6 +255,11 @@ static void apple_gfx_mmio_reset(Object *obj, ResetType type) [s->common.pgdev reset]; } +static const Property apple_gfx_mmio_properties[] = { + DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState, + common.num_display_modes, common.display_modes, + qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode), +}; static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data) { @@ -264,6 +269,8 @@ static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data) rc->phases.hold = apple_gfx_mmio_reset; dc->hotpluggable = false; dc->realize = apple_gfx_mmio_realize; + + device_class_set_props(dc, apple_gfx_mmio_properties); } static const TypeInfo apple_gfx_mmio_types[] = { diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m index 35a3c7a7ce..b939bb9b23 100644 --- a/hw/display/apple-gfx-pci.m +++ b/hw/display/apple-gfx-pci.m @@ -115,6 +115,12 @@ static void apple_gfx_pci_reset(Object *obj, ResetType type) [s->common.pgdev reset]; } +static const Property apple_gfx_pci_properties[] = { + DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState, + common.num_display_modes, common.display_modes, + qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode), +}; + static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -131,7 +137,7 @@ static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) pci->class_id = PCI_CLASS_DISPLAY_OTHER; pci->realize = apple_gfx_pci_realize; - /* TODO: Property for setting mode list */ + device_class_set_props(dc, apple_gfx_pci_properties); } static const TypeInfo apple_gfx_pci_types[] = { diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h index 6c74209b36..3900cdbabb 100644 --- a/hw/display/apple-gfx.h +++ b/hw/display/apple-gfx.h @@ -25,6 +25,12 @@ typedef QTAILQ_HEAD(, PGTask_s) PGTaskList; +typedef struct AppleGFXDisplayMode { + uint16_t width_px; + uint16_t height_px; + uint16_t refresh_rate_hz; +} AppleGFXDisplayMode; + typedef struct AppleGFXState { /* Initialised on init/realize() */ MemoryRegion iomem_gfx; @@ -33,6 +39,8 @@ typedef struct AppleGFXState { QemuConsole *con; id mtl; id mtl_queue; + AppleGFXDisplayMode *display_modes; + uint32_t num_display_modes; /* List `tasks` is protected by task_mutex */ QemuMutex task_mutex; @@ -60,5 +68,7 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, uint64_t length, bool read_only, MemoryRegion **mapping_in_region); +extern const PropertyInfo qdev_prop_apple_gfx_display_mode; + #endif diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 59299e339d..aa1455b629 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -31,9 +31,10 @@ #import -static const PGDisplayCoord_t apple_gfx_modes[] = { - { .x = 1440, .y = 1080 }, - { .x = 1280, .y = 1024 }, +static const AppleGFXDisplayMode apple_gfx_default_modes[] = { + { 1920, 1080, 60 }, + { 1440, 1080, 60 }, + { 1280, 1024, 60 }, }; static Error *apple_gfx_mig_blocker; @@ -690,22 +691,23 @@ static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState * return disp_desc; } -static NSArray* apple_gfx_prepare_display_mode_array(void) +static NSArray *apple_gfx_create_display_mode_array( + const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count) { - PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; - NSArray* mode_array; - int i; + PGDisplayMode *mode_obj; + NSMutableArray *mode_array = + [[NSMutableArray alloc] initWithCapacity:display_mode_count]; - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { - modes[i] = - [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; - } + for (unsigned i = 0; i < display_mode_count; i++) { + const AppleGFXDisplayMode *mode = &display_modes[i]; + trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px); + PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px }; - mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; - - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { - [modes[i] release]; - modes[i] = nil; + mode_obj = + [[PGDisplayMode alloc] initWithSizeInPixels:mode_size + refreshRateInHz:mode->refresh_rate_hz]; + [mode_array addObject:mode_obj]; + [mode_obj release]; } return mode_array; @@ -741,6 +743,9 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, PGDeviceDescriptor *desc, Error **errp) { PGDisplayDescriptor *disp_desc; + const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes; + uint32_t num_display_modes = ARRAY_SIZE(apple_gfx_default_modes); + NSArray *mode_array; if (apple_gfx_mig_blocker == NULL) { error_setg(&apple_gfx_mig_blocker, @@ -776,8 +781,99 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, port:0 serialNum:next_pgdisplay_serial_num++]; [disp_desc release]; - s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); + + if (s->display_modes != NULL && s->num_display_modes > 0) { + trace_apple_gfx_common_realize_modes_property(s->num_display_modes); + display_modes = s->display_modes; + num_display_modes = s->num_display_modes; + } + s->pgdisp.modeList = mode_array = + apple_gfx_create_display_mode_array(display_modes, num_display_modes); + [mode_array release]; s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s); return true; } + +/* ------ Display mode list device property ------ */ + +static void apple_gfx_get_display_mode(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop); + /* 3 uint16s (max 5 digits) + 2 separator characters + nul. */ + char buffer[5 * 3 + 2 + 1]; + char *pos = buffer; + + int rc = snprintf(buffer, sizeof(buffer), + "%"PRIu16"x%"PRIu16"@%"PRIu16, + mode->width_px, mode->height_px, + mode->refresh_rate_hz); + assert(rc < sizeof(buffer)); + + visit_type_str(v, name, &pos, errp); +} + +static void apple_gfx_set_display_mode(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop); + const char *endptr; + g_autofree char *str = NULL; + int ret; + int val; + + if (!visit_type_str(v, name, &str, errp)) { + return; + } + + endptr = str; + + ret = qemu_strtoi(endptr, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val <= 0) { + error_setg(errp, "width in '%s' must be a decimal integer number" + " of pixels in the range 1..65535", name); + return; + } + mode->width_px = val; + if (*endptr != 'x') { + goto separator_error; + } + + ret = qemu_strtoi(endptr + 1, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val <= 0) { + error_setg(errp, "height in '%s' must be a decimal integer number" + " of pixels in the range 1..65535", name); + return; + } + mode->height_px = val; + if (*endptr != '@') { + goto separator_error; + } + + ret = qemu_strtoi(endptr + 1, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val <= 0) { + error_setg(errp, "refresh rate in '%s'" + " must be a positive decimal integer (Hertz)", name); + return; + } + mode->refresh_rate_hz = val; + return; + +separator_error: + error_setg(errp, + "Each display mode takes the format 'x@'"); +} + +const PropertyInfo qdev_prop_apple_gfx_display_mode = { + .name = "display_mode", + .description = + "Display mode in pixels and Hertz, as x@ " + "Example: 3840x2160@60", + .get = apple_gfx_get_display_mode, + .set = apple_gfx_set_display_mode, +}; diff --git a/hw/display/trace-events b/hw/display/trace-events index a50e4eea0c..52786e6e18 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -212,6 +212,8 @@ apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d widt apple_gfx_cursor_show(uint32_t show) "show=%d" apple_gfx_cursor_move(void) "" apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes" +apple_gfx_common_realize_modes_property(uint32_t num_modes) "using %u modes supplied by 'display-modes' device property" +apple_gfx_display_mode(uint32_t mode_idx, uint16_t width_px, uint16_t height_px) "mode %2"PRIu32": %4"PRIu16"x%4"PRIu16 # apple-gfx-mmio.m apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 From 67e908c936b5f568b2ee913c1558e0235cd96293 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Mon, 15 Jul 2024 23:07:04 +0200 Subject: [PATCH 0785/2892] MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm happy to take responsibility for the macOS PV graphics code. As HVF patches don't seem to get much attention at the moment, I'm also adding myself as designated reviewer for HVF and x86 HVF to try and improve that. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Roman Bolshakov Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241223221645.29911-6-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 38a290e9c2..2101b51217 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -509,6 +509,7 @@ F: target/arm/hvf/ X86 HVF CPUs M: Cameron Esfahani M: Roman Bolshakov +R: Phil Dennis-Jordan W: https://wiki.qemu.org/Features/HVF S: Maintained F: target/i386/hvf/ @@ -516,6 +517,7 @@ F: target/i386/hvf/ HVF M: Cameron Esfahani M: Roman Bolshakov +R: Phil Dennis-Jordan W: https://wiki.qemu.org/Features/HVF S: Maintained F: accel/hvf/ @@ -2631,6 +2633,11 @@ F: hw/display/edid* F: include/hw/display/edid.h F: qemu-edid.c +macOS PV Graphics (apple-gfx) +M: Phil Dennis-Jordan +S: Maintained +F: hw/display/apple-gfx* + PIIX4 South Bridge (i82371AB) M: Hervé Poussineau M: Philippe Mathieu-Daudé From 79b6a98587c65c8b8fdde8eb778396f1fc98a852 Mon Sep 17 00:00:00 2001 From: William Hooper Date: Sat, 6 Jan 2024 14:35:46 -0800 Subject: [PATCH 0786/2892] net/vmnet: Pad short Ethernet frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least on macOS 12.7.2, vmnet doesn't pad Ethernet frames, such as the host's ARP replies, to the minimum size (60 bytes before the frame check sequence) defined in IEEE Std 802.3-2022, so guests' Ethernet device drivers may drop them with "frame too short" errors. This patch calls eth_pad_short_frame() to add padding, as in net/tap.c and net/slirp.c. Thanks to Bin Meng, Philippe Mathieu-Daudé, and Phil Dennis-Jordan for reviewing earlier versions. Signed-off-by: William Hooper Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2058 Reviewed-by: Phil Dennis-Jordan Message-ID: <20241102205653.30476-1-wsh@wshooper.org> Signed-off-by: Philippe Mathieu-Daudé --- net/vmnet-common.m | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/net/vmnet-common.m b/net/vmnet-common.m index dba5b5bab1..54d900ba67 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "system/runstate.h" +#include "net/eth.h" #include #include @@ -147,10 +148,26 @@ static int vmnet_read_packets(VmnetState *s) */ static void vmnet_write_packets_to_qemu(VmnetState *s) { + uint8_t *pkt; + size_t pktsz; + uint8_t min_pkt[ETH_ZLEN]; + size_t min_pktsz; + ssize_t size; + while (s->packets_send_current_pos < s->packets_send_end_pos) { - ssize_t size = qemu_send_packet_async(&s->nc, - s->iov_buf[s->packets_send_current_pos].iov_base, - s->packets_buf[s->packets_send_current_pos].vm_pkt_size, + pkt = s->iov_buf[s->packets_send_current_pos].iov_base; + pktsz = s->packets_buf[s->packets_send_current_pos].vm_pkt_size; + + if (net_peer_needs_padding(&s->nc)) { + min_pktsz = sizeof(min_pkt); + + if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pktsz)) { + pkt = min_pkt; + pktsz = min_pktsz; + } + } + + size = qemu_send_packet_async(&s->nc, pkt, pktsz, vmnet_send_completed); if (size == 0) { From c6f59e3b68abefc1f6942d4b4e3063d96d903b27 Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Fri, 20 Dec 2024 02:46:57 +0800 Subject: [PATCH 0787/2892] hw/display/qxl: Do not use C99 // comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not use C99 // comments to fix the checkpatch.pl error Signed-off-by: Hyman Huang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <7d287eaf00e0b52b600431efd350b15a0b5b3544.1734633496.git.yong.huang@smartx.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/qxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index f54a15e740..2efdc77e61 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -50,7 +50,7 @@ #undef ALIGN #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) -#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" +#define PIXEL_SIZE 0.2936875 /* 1280x1024 is 14.8" x 11.9" */ #define QXL_MODE(_x, _y, _b, _o) \ { .x_res = _x, \ From 3d5d015ca419edc122d7977c93bafaaca43307a4 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Wed, 1 Jan 2025 13:31:16 +0530 Subject: [PATCH 0788/2892] docs: update copyright date to the year 2025 We are already in January 2025! Update copyright notices. Cc: peter.maydell@linaro.org Cc: qemu-trivial@nongnu.org Signed-off-by: Ani Sinha Message-ID: <20250101080116.1050336-1-anisinha@redhat.com> Signed-off-by: Thomas Huth --- docs/conf.py | 2 +- include/qemu/help-texts.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 164a8ee8b2..31bb9a3789 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -87,7 +87,7 @@ default_role = 'any' # General information about the project. project = u'QEMU' -copyright = u'2024, The QEMU Project Developers' +copyright = u'2025, 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/help-texts.h b/include/qemu/help-texts.h index 353ab2ad8b..bc8fab9169 100644 --- a/include/qemu/help-texts.h +++ b/include/qemu/help-texts.h @@ -2,7 +2,7 @@ #define QEMU_HELP_TEXTS_H /* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2024 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2025 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ From c82bfaf42dc6f8dbf101190c86cfa8af9ea400fd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Dec 2024 14:14:35 +0100 Subject: [PATCH 0789/2892] tests/functional: Convert the vnc test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing thrilling in here, it's just a straight forward conversion. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20241218131439.255841-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/meson.build | 1 + tests/{avocado/vnc.py => functional/test_vnc.py} | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) rename tests/{avocado/vnc.py => functional/test_vnc.py} (97%) mode change 100644 => 100755 diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 24f7f8f2f1..a5087fcb34 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -224,6 +224,7 @@ tests_x86_64_system_quick = [ 'pc_cpu_hotplug_props', 'virtio_version', 'x86_cpu_model_versions', + 'vnc', ] tests_x86_64_system_thorough = [ diff --git a/tests/avocado/vnc.py b/tests/functional/test_vnc.py old mode 100644 new mode 100755 similarity index 97% rename from tests/avocado/vnc.py rename to tests/functional/test_vnc.py index 862c8996a8..b769d3b268 --- a/tests/avocado/vnc.py +++ b/tests/functional/test_vnc.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Simple functional tests for VNC functionality # # Copyright (c) 2018 Red Hat, Inc. @@ -11,7 +13,7 @@ import socket from typing import List -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest VNC_ADDR = '127.0.0.1' @@ -51,10 +53,7 @@ def find_free_ports(count: int) -> List[int]: class Vnc(QemuSystemTest): - """ - :avocado: tags=vnc,quick - :avocado: tags=machine:none - """ + def test_no_vnc(self): self.vm.add_args('-nodefaults', '-S') self.vm.launch() @@ -113,3 +112,6 @@ class Vnc(QemuSystemTest): self.assertFalse(check_connect(a)) self.assertTrue(check_connect(b)) self.assertTrue(check_connect(c)) + +if __name__ == '__main__': + QemuSystemTest.main() From 56d3a1482921e7e23233f3abcce9c29f3f56cb72 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Dec 2024 14:14:37 +0100 Subject: [PATCH 0790/2892] tests/functional/test_vnc: Do not use a hard-coded VNC port Two tests here are using the hard-coded VNC port :0 ... if there is already a QEMU or other program running that is using this port, the tests will be failing. Fortunately, QEMU can also auto-detect a free port with the "to=..." parameter, so let's use that for the tests to avoid the problem. Message-ID: <20241218131439.255841-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_vnc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index b769d3b268..e6328567c7 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -72,7 +72,7 @@ class Vnc(QemuSystemTest): 'Could not set password') def test_change_password_requires_a_password(self): - self.vm.add_args('-nodefaults', '-S', '-vnc', ':0') + self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999') self.vm.launch() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) set_password_response = self.vm.qmp('change-vnc-password', @@ -84,7 +84,7 @@ class Vnc(QemuSystemTest): 'Could not set password') def test_change_password(self): - self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on') + self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999,password=on') self.vm.launch() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) self.vm.cmd('change-vnc-password', From 93a9fdc5504f15d319927c1497522cb72929d78e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Dec 2024 14:14:36 +0100 Subject: [PATCH 0791/2892] tests/functional/test_vnc: Remove the test_no_vnc test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test matches exactly the first three lines of the following test_no_vnc_change_password test, so there is exactly zero additional test coverage in here. Reviewed-by: Daniel P. Berrangé Message-ID: <20241218131439.255841-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_vnc.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index e6328567c7..e600d75234 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -54,11 +54,6 @@ def find_free_ports(count: int) -> List[int]: class Vnc(QemuSystemTest): - def test_no_vnc(self): - self.vm.add_args('-nodefaults', '-S') - self.vm.launch() - self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) - def test_no_vnc_change_password(self): self.vm.add_args('-nodefaults', '-S') self.vm.launch() From b7edbbf4321fea9efefda2a5d6bcea4f7140f866 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Dec 2024 14:14:38 +0100 Subject: [PATCH 0792/2892] tests/functional: Extract the find_free_ports() function into a helper file We'll need this functionality in other functional tests, too, so let's extract it into the qemu_test module. Also add an __enter__ and __exit__ function that can be used for using this functionality in a locked context, so that tests that are running in parallel don't try to compete for the same ports later. Also make sure to only use ports in the "Dynamic Ports" range (see https://www.rfc-editor.org/rfc/rfc6335) and "randomize" the start of the probed range with the PID of the test process to further avoid possible clashes with other competing processes. Message-ID: <20241218131439.255841-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/ports.py | 56 +++++++++++++++++++++++++++++ tests/functional/test_vnc.py | 36 +++++-------------- 2 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 tests/functional/qemu_test/ports.py diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py new file mode 100644 index 0000000000..cc39939d48 --- /dev/null +++ b/tests/functional/qemu_test/ports.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Simple functional tests for VNC functionality +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import fcntl +import os +import socket +import sys +import tempfile + +from .config import BUILD_DIR +from typing import List + +class Ports(): + + PORTS_ADDR = '127.0.0.1' + PORTS_RANGE_SIZE = 1024 + PORTS_START = 49152 + ((os.getpid() * PORTS_RANGE_SIZE) % 16384) + PORTS_END = PORTS_START + PORTS_RANGE_SIZE + + def __enter__(self): + lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock") + self.lock_fh = os.open(lock_file, os.O_CREAT) + fcntl.flock(self.lock_fh, fcntl.LOCK_EX) + return self + + def __exit__(self, exc_type, exc_value, traceback): + fcntl.flock(self.lock_fh, fcntl.LOCK_UN) + os.close(self.lock_fh) + + def check_bind(self, port: int) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.bind((self.PORTS_ADDR, port)) + except OSError: + return False + + return True + + def find_free_ports(self, count: int) -> List[int]: + result = [] + for port in range(self.PORTS_START, self.PORTS_END): + if self.check_bind(port): + result.append(port) + if len(result) >= count: + break + assert len(result) == count + return result + + def find_free_port(self) -> int: + return self.find_free_ports(1)[0] diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index e600d75234..1916be0103 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -14,22 +14,9 @@ import socket from typing import List from qemu_test import QemuSystemTest - +from qemu_test.ports import Ports VNC_ADDR = '127.0.0.1' -VNC_PORT_START = 32768 -VNC_PORT_END = VNC_PORT_START + 1024 - - -def check_bind(port: int) -> bool: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - try: - sock.bind((VNC_ADDR, port)) - except OSError: - return False - - return True - def check_connect(port: int) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: @@ -40,18 +27,6 @@ def check_connect(port: int) -> bool: return True - -def find_free_ports(count: int) -> List[int]: - result = [] - for port in range(VNC_PORT_START, VNC_PORT_END): - if check_bind(port): - result.append(port) - if len(result) >= count: - break - assert len(result) == count - return result - - class Vnc(QemuSystemTest): def test_no_vnc_change_password(self): @@ -85,8 +60,7 @@ class Vnc(QemuSystemTest): self.vm.cmd('change-vnc-password', password='new_password') - def test_change_listen(self): - a, b, c = find_free_ports(3) + def do_test_change_listen(self, a, b, c): self.assertFalse(check_connect(a)) self.assertFalse(check_connect(b)) self.assertFalse(check_connect(c)) @@ -108,5 +82,11 @@ class Vnc(QemuSystemTest): self.assertTrue(check_connect(b)) self.assertTrue(check_connect(c)) + def test_change_listen(self): + with Ports() as ports: + a, b, c = ports.find_free_ports(3) + self.do_test_change_listen(a, b, c) + + if __name__ == '__main__': QemuSystemTest.main() From 8c8dd70037952b3e6b700ecec7be829a5432a432 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 20 Dec 2024 12:46:15 +1000 Subject: [PATCH 0793/2892] tests/functional/test_ppc64_hv: Simplify console handling Since functional tests have character-based console output parsing, there is no need for strange hacks to work around old line-based. Signed-off-by: Nicholas Piggin Message-ID: <20241220024617.1968556-3-npiggin@gmail.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_hv.py | 43 ++++++++++++++----------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 037dfdf87e..2182a68c91 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -12,6 +12,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern, exec_command from qemu_test import skipIfMissingCommands, skipBigDataTest +from qemu_test import exec_command_and_wait_for_pattern import os import time import subprocess @@ -73,31 +74,28 @@ class HypervisorTest(QemuSystemTest): "id=drive0,read-only=true") self.vm.launch() - wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') - exec_command(self, 'root') + ps1='localhost:~#' wait_for_console_pattern(self, 'localhost login:') - wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') + exec_command_and_wait_for_pattern(self, 'root', ps1) # If the time is wrong, SSL certificates can fail. - exec_command(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"')) - exec_command(self, 'setup-alpine -qe') - wait_for_console_pattern(self, 'Updating repository indexes... done.') + exec_command_and_wait_for_pattern(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'), ps1) + ps1='alpine:~#' + exec_command_and_wait_for_pattern(self, 'setup-alpine -qe', ps1) def do_stop_alpine(self): - exec_command(self, 'poweroff') + exec_command(self, 'echo "TEST ME"') wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'poweroff') + wait_for_console_pattern(self, 'reboot: Power down') self.vm.wait() def do_setup_kvm(self): - exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'apk update') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'apk add qemu-system-ppc64') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'modprobe kvm-hv') - wait_for_console_pattern(self, 'alpine:~#') + ps1='alpine:~#' + exec_command_and_wait_for_pattern(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories', ps1) + exec_command_and_wait_for_pattern(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories', ps1) + exec_command_and_wait_for_pattern(self, 'apk update', ps1) + exec_command_and_wait_for_pattern(self, 'apk add qemu-system-ppc64', ps1) + exec_command_and_wait_for_pattern(self, 'modprobe kvm-hv', ps1) # This uses the host's block device as the source file for guest block # device for install media. This is a bit hacky but allows reuse of the @@ -116,15 +114,12 @@ class HypervisorTest(QemuSystemTest): '-kernel /media/nvme0n1/boot/vmlinuz-lts ' '-append \'usbcore.nousb ' + append + '\'') # Alpine 3.18 kernel seems to crash in XHCI USB driver. - wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') - exec_command(self, 'root') + ps1='localhost:~#' wait_for_console_pattern(self, 'localhost login:') - wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') - exec_command(self, 'poweroff >& /dev/null') - wait_for_console_pattern(self, 'localhost:~#') + exec_command_and_wait_for_pattern(self, 'root', ps1) + exec_command(self, 'poweroff') wait_for_console_pattern(self, 'reboot: Power down') - time.sleep(1) - exec_command(self, '') + # Now wait for the host's prompt to come back wait_for_console_pattern(self, 'alpine:~#') def test_hv_pseries(self): From e6c81cf3d00a769d105290699b4efc3aeff0c66d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 20 Dec 2024 12:46:16 +1000 Subject: [PATCH 0794/2892] tests/functional/test_ppc64_hv: Update repo management `setup-apkrepos` can be used to set repos rather than open-coding URLs. Signed-off-by: Nicholas Piggin Message-ID: <20241220024617.1968556-4-npiggin@gmail.com> Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_hv.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 2182a68c91..e0dffb1f15 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -81,6 +81,10 @@ class HypervisorTest(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'), ps1) ps1='alpine:~#' exec_command_and_wait_for_pattern(self, 'setup-alpine -qe', ps1) + exec_command_and_wait_for_pattern(self, 'setup-apkrepos -c1', ps1) + exec_command_and_wait_for_pattern(self, 'apk update', ps1) + # Could upgrade here but it usually should not be necessary + # exec_command_and_wait_for_pattern(self, 'apk upgrade --available', ps1) def do_stop_alpine(self): exec_command(self, 'echo "TEST ME"') @@ -91,9 +95,6 @@ class HypervisorTest(QemuSystemTest): def do_setup_kvm(self): ps1='alpine:~#' - exec_command_and_wait_for_pattern(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories', ps1) - exec_command_and_wait_for_pattern(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories', ps1) - exec_command_and_wait_for_pattern(self, 'apk update', ps1) exec_command_and_wait_for_pattern(self, 'apk add qemu-system-ppc64', ps1) exec_command_and_wait_for_pattern(self, 'modprobe kvm-hv', ps1) From 9f6b6106dc450c4027d48b4c6347b75ec4c9eb96 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 20 Dec 2024 12:46:17 +1000 Subject: [PATCH 0795/2892] tests/functional/test_ppc64_hv: Update to Alpine 3.21.0 Signed-off-by: Nicholas Piggin Message-ID: <20241220024617.1968556-5-npiggin@gmail.com> Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_hv.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index e0dffb1f15..62f996adf6 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -35,9 +35,9 @@ class HypervisorTest(QemuSystemTest): good_message = 'VFS: Cannot open root device' ASSET_ISO = Asset( - ('https://dl-cdn.alpinelinux.org/alpine/v3.18/' - 'releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso'), - 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff') + ('https://dl-cdn.alpinelinux.org/alpine/v3.21/' + 'releases/ppc64le/alpine-standard-3.21.0-ppc64le.iso'), + '7651ab4e3027604535c0b36e86c901b4695bf8fe97b908f5b48590f6baae8f30') def extract_from_iso(self, iso, path): """ @@ -114,7 +114,7 @@ class HypervisorTest(QemuSystemTest): '-initrd /media/nvme0n1/boot/initramfs-lts ' '-kernel /media/nvme0n1/boot/vmlinuz-lts ' '-append \'usbcore.nousb ' + append + '\'') - # Alpine 3.18 kernel seems to crash in XHCI USB driver. + # Alpine 3.21 kernel seems to crash in XHCI USB driver. ps1='localhost:~#' wait_for_console_pattern(self, 'localhost login:') exec_command_and_wait_for_pattern(self, 'root', ps1) From ec2dfb7c389b94d71ee825caa20b709d5df6c166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 29 Dec 2024 09:34:19 +0100 Subject: [PATCH 0796/2892] tests/functional/test_rx_gdbsim: Use stable URL for test_linux_sash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yoshinori said [*] URL references on OSDN were stable, but they appear not to be. Mirror the artifacts on GitHub to avoid failures while testing on CI. [*] https://www.mail-archive.com/qemu-devel@nongnu.org/msg686487.html Cc: Yoshinori Sato Reported-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20200630202631.7345-1-f4bug@amsat.org> [huth: Adapt the patch to the new version in the functional framework] Message-ID: <20241229083419.180423-1-huth@tuxfamily.org> Signed-off-by: Thomas Huth --- tests/functional/test_rx_gdbsim.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py index 20623aa51c..49245793e1 100755 --- a/tests/functional/test_rx_gdbsim.py +++ b/tests/functional/test_rx_gdbsim.py @@ -21,13 +21,16 @@ class RxGdbSimMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' ASSET_UBOOT = Asset( - 'https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz', - '7146567d669e91dbac166384b29aeba1715beb844c8551e904b86831bfd9d046') + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'u-boot.bin'), + 'dd7dd4220cccf7aeb32227b26233bf39600db05c3f8e26005bcc2bf6c927207d') ASSET_DTB = Asset( - 'https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb', + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'rx-gdbsim.dtb'), 'aa278d9c1907a4501741d7ee57e7f65c02dd1b3e0323b33c6d4247f1b32cf29a') ASSET_KERNEL = Asset( - 'http://acc.dl.osdn.jp/users/23/23845/zImage', + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'zImage'), 'baa43205e74a7220ed8482188c5e9ce497226712abb7f4e7e4f825ce19ff9656') def test_uboot(self): @@ -36,7 +39,7 @@ class RxGdbSimMachine(QemuSystemTest): """ self.set_machine('gdbsim-r5f562n8') - uboot_path = self.uncompress(self.ASSET_UBOOT) + uboot_path = self.ASSET_UBOOT.fetch() self.vm.set_console() self.vm.add_args('-bios', uboot_path, From c5efe54622953c4350566ab42323de61a1c06b8f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 2 Jan 2025 08:30:35 +0100 Subject: [PATCH 0797/2892] tests/functional/test_arm_quanta_gsj: Fix broken test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASSET_IMAGE needs to be prefixed with "self." ... this bug apparently went in unnoticed because the test is not run by default. Message-ID: <20250102073403.36328-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- tests/functional/test_arm_quanta_gsj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py index 7aa5209bea..7b82e2185c 100755 --- a/tests/functional/test_arm_quanta_gsj.py +++ b/tests/functional/test_arm_quanta_gsj.py @@ -35,7 +35,7 @@ class EmcraftSf2Machine(LinuxKernelTest): @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') def test_arm_quanta_gsj(self): self.set_machine('quanta-gsj') - image_path = self.uncompress(ASSET_IMAGE, 'obmc.mtd', format='gz') + image_path = self.uncompress(self.ASSET_IMAGE, format='gz') self.vm.set_console() drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' From 4ff969ce8e20586f1cc340c673e346a7d18e80d7 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 17 Dec 2024 13:10:46 +0000 Subject: [PATCH 0798/2892] tests/qtest/migration: Fix compile errors when CONFIG_UADK is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes accidental inclusion of unrelated functions within CONFIG_UADK as this causes compile errors like: error: redefinition of ‘migrate_hook_start_xbzrle’ Fixes: 932f74f3fe6e ("tests/qtest/migration: Split compression tests from migration-test.c") Signed-off-by: Shameer Kolothum Reviewed-by: Fabiano Rosas Message-Id: <20241217131046.83844-1-shameerali.kolothum.thodi@huawei.com> Signed-off-by: Fabiano Rosas --- tests/qtest/migration/compression-tests.c | 54 ----------------------- 1 file changed, 54 deletions(-) diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c index 6de87bc47d..d78f1f11f1 100644 --- a/tests/qtest/migration/compression-tests.c +++ b/tests/qtest/migration/compression-tests.c @@ -88,59 +88,6 @@ migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); } -static void * -migrate_hook_start_xbzrle(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", true); - migrate_set_capability(to, "xbzrle", true); - - return NULL; -} - -static void test_precopy_unix_xbzrle(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_xbzrle, - .iterations = 2, - /* - * XBZRLE needs pages to be modified when doing the 2nd+ round - * iteration to have real data pushed to the stream. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void * -migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, - QTestState *to) -{ - /* - * Overloading this test to also check that set_parameter does not error. - * This is also done in the tests for the other compression methods. - */ - migrate_set_parameter_int(from, "multifd-zlib-level", 2); - migrate_set_parameter_int(to, "multifd-zlib-level", 2); - - return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); -} - -static void test_multifd_tcp_zlib(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, - }; - test_precopy_common(&args); -} - static void test_multifd_tcp_uadk(void) { MigrateCommon args = { @@ -151,7 +98,6 @@ static void test_multifd_tcp_uadk(void) } #endif /* CONFIG_UADK */ - static void * migrate_hook_start_xbzrle(QTestState *from, QTestState *to) From cd196679f413c7905143e5eb9b7d63b7a7eea158 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 9 Dec 2024 17:44:22 -0300 Subject: [PATCH 0799/2892] tests/qtest/migration: Do proper cleanup in the dirty_limit test The dirty_limit test does two migrations in a row and is leaking the first 'to' instance. Do proper cleanup. Reviewed-by: Peter Xu Message-Id: <20241209204427.17763-2-farosas@suse.de> Signed-off-by: Fabiano Rosas --- tests/qtest/migration/precopy-tests.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index b709d9051d..23599b29ee 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -877,6 +877,11 @@ static void test_dirty_limit(void) migrate_cancel(from); wait_for_migration_status(from, "cancelled", NULL); + /* destination always fails after cancel */ + migration_event_wait(to, "failed"); + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_quit(to); + /* Check if dirty limit throttle switched off, set timeout 1ms */ do { throttle_us_per_full = From 117221ad999a3d027d38ae9ddbaa94d4e1d6a303 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 9 Dec 2024 17:44:23 -0300 Subject: [PATCH 0800/2892] tests/qtest/migration: Initialize buffer in probe_o_direct_support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Valgrind complains about the probe_o_direct_support() function reading from an uninitialized buffer. For probing O_DIRECT support we don't actually need to write to the file, just make sure the pwrite call doesn't reject the write. Still, write zeroes to the buffer to suppress the warning. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Message-Id: <20241209204427.17763-3-farosas@suse.de> Signed-off-by: Fabiano Rosas --- tests/qtest/migration/migration-util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 525bf1eed4..401fe27f00 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -284,6 +284,7 @@ bool probe_o_direct_support(const char *tmpfs) buf = qemu_try_memalign(len, len); g_assert(buf); + memset(buf, 0, len); ret = pwrite(fd, buf, len, offset); unlink(filename); From a475e5de01f3d0938d73a1062d1ec7a3df37d621 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 9 Dec 2024 17:44:24 -0300 Subject: [PATCH 0801/2892] tests/qtest/bios-tables-test: Free tables at dump_aml_files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dump_aml_files() function calls load_expected_aml() to allocate the tables but never frees it. Add the missing call to free_test_data(). Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20241209204427.17763-4-farosas@suse.de> Signed-off-by: Fabiano Rosas --- tests/qtest/bios-tables-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 6035ec2c61..0a333ec435 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -292,6 +292,7 @@ static void dump_aml_files(test_data *data, bool rebuild) g_free(aml_file); } + free_test_data(&exp_data); } static bool create_tmp_asl(AcpiSdtTable *sdt) From 9a9320a543704087e3f27f243de6f58af948ce56 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 9 Dec 2024 17:44:25 -0300 Subject: [PATCH 0802/2892] tests/qtest/virtio-iommu-test: Don't pass uninitialized data into qtest_memwrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Valgrind complains about: Use of uninitialised value of size 8 & Conditional jump or move depends on uninitialised value(s) both at: at 0x5265931: _itoa_word (_itoa.c:180) by 0x527EEC7: __vfprintf_internal (vfprintf-internal.c:1687) by 0x528C8B0: __vsprintf_internal (iovsprintf.c:96) by 0x526B920: sprintf (sprintf.c:30) by 0x1296C7: qtest_memwrite (libqtest.c:1273) by 0x193C04: send_map (virtio-iommu-test.c:125) by 0x194392: test_attach_detach (virtio-iommu-test.c:214) by 0x17BDE7: run_one_test (qos-test.c:181) by 0x4B0699D: test_case_run (gtestutils.c:2900) by 0x4B0699D: g_test_run_suite_internal (gtestutils.c:2988) by 0x4B068B2: g_test_run_suite_internal (gtestutils.c:3005) by 0x4B068B2: g_test_run_suite_internal (gtestutils.c:3005) by 0x4B068B2: g_test_run_suite_internal (gtestutils.c:3005) Uninitialised value was created by a stack allocation at 0x193AFD: send_map (virtio-iommu-test.c:103) Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20241209204427.17763-5-farosas@suse.de> Signed-off-by: Fabiano Rosas --- tests/qtest/virtio-iommu-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c index afb225971d..98ffa27912 100644 --- a/tests/qtest/virtio-iommu-test.c +++ b/tests/qtest/virtio-iommu-test.c @@ -105,7 +105,7 @@ static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu, QVirtQueue *vq = v_iommu->vq; uint64_t ro_addr, wr_addr; uint32_t free_head; - struct virtio_iommu_req_map req; + struct virtio_iommu_req_map req = {}; size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail); size_t wr_size = sizeof(struct virtio_iommu_req_tail); struct virtio_iommu_req_tail buffer; @@ -147,7 +147,7 @@ static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu, QVirtQueue *vq = v_iommu->vq; uint64_t ro_addr, wr_addr; uint32_t free_head; - struct virtio_iommu_req_unmap req; + struct virtio_iommu_req_unmap req = {}; size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail); size_t wr_size = sizeof(struct virtio_iommu_req_tail); struct virtio_iommu_req_tail buffer; From bc3ace691492a5aefa6e9ad1006879a30502d842 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 16 Dec 2024 11:14:13 -0500 Subject: [PATCH 0803/2892] tests/migration: Drop arch_[source|target] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity complained about them. These two variables are never used now after commit 832c732c5d ("migration-test: Create arch_opts"), and/or commit 34cc54fb35 ("tests/qtest/migration-test: Use custom asm bios for ppc64"). Resolves: Coverity CID 1568379 Resolves: Coverity CID 1568380 Signed-off-by: Peter Xu Reviewed-by: Alex Bennée Message-Id: <20241216161413.1644171-4-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index a902936039..47ce07856e 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -197,8 +197,6 @@ static void cleanup(const char *filename) int migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { - g_autofree gchar *arch_source = NULL; - g_autofree gchar *arch_target = NULL; /* options for source and target */ g_autofree gchar *arch_opts = NULL; g_autofree gchar *cmd_source = NULL; @@ -307,12 +305,11 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, "-name source,debug-threads=on " "-m %s " "-serial file:%s/src_serial " - "%s %s %s %s %s", + "%s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, memory_size, tmpfs, arch_opts ? arch_opts : "", - arch_source ? arch_source : "", shmem_opts ? shmem_opts : "", args->opts_source ? args->opts_source : "", ignore_stderr); @@ -329,12 +326,11 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, "-m %s " "-serial file:%s/dest_serial " "-incoming %s " - "%s %s %s %s %s", + "%s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, memory_size, tmpfs, uri, arch_opts ? arch_opts : "", - arch_target ? arch_target : "", shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); From 06056ef1ff1e457787d277796bff927f51c092c9 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 18 Dec 2024 16:22:22 -0300 Subject: [PATCH 0804/2892] tests/qtest/migration: Re-enable postcopy tests Postcopy tests have been inadvertently disabled since commit 124a3c58b8 ("tests/qtest/migration: Move ufd_version_check to utils"). That commit moved the ufd_version_check() function to another file but failed to make sense of the ifdefs and includes: The include was incorrectly dropped. It is needed to pull in for __NR_userfaultfd. The was moved under the wrong ifdef. Fixes: 124a3c58b8 ("tests/qtest/migration: Move ufd_version_check to utils") Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Message-Id: <20241218192223.10551-2-farosas@suse.de> --- tests/qtest/migration/migration-util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 401fe27f00..526bed74ea 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -22,6 +22,11 @@ #include "migration/bootfile.h" #include "migration/migration-util.h" +#if defined(__linux__) +#include +#include +#endif + /* for uffd_version_check() */ #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) #include @@ -31,7 +36,6 @@ /* For dirty ring test; so far only x86_64 is supported */ #if defined(__linux__) && defined(HOST_X86_64) #include "linux/kvm.h" -#include #endif From d9f2b09dd6c3030cda9af9c29e26a0bdfc02bec6 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Wed, 1 Jan 2025 13:45:55 +0530 Subject: [PATCH 0805/2892] qtest/fw-cfg: remove compiled out code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove code that is already compiled out. This prevents confusion. CC: qemu-trivial@nongnu.org Signed-off-by: Ani Sinha Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250101081555.1050736-1-anisinha@redhat.com> Signed-off-by: Fabiano Rosas --- tests/qtest/fw_cfg-test.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c index 5dc807ba23..e48b34afa5 100644 --- a/tests/qtest/fw_cfg-test.c +++ b/tests/qtest/fw_cfg-test.c @@ -243,12 +243,6 @@ int main(int argc, char **argv) qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size); qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic); qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); -#if 0 - qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id); - qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel); - qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd); - qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device); -#endif qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus); qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); From 5288d9d0853622668bd293023d2dfe200f3606f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 2 Dec 2024 12:19:27 +0000 Subject: [PATCH 0806/2892] qga: implement a 'guest-get-load' command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide a way to report the process load average, via a new 'guest-get-load' command. This is only implemented for POSIX platforms providing 'getloadavg'. Example illustrated with qmp-shell: (QEMU) guest-get-load { "return": { "load15m": 1.546875, "load1m": 1.669921875, "load5m": 1.9306640625 } } Windows has no native equivalent API, but it would be possible to simulate it as illustrated here (BSD-3-Clause): https://github.com/giampaolo/psutil/pull/1485 This is left as an exercise for future contributors. Signed-off-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Message-ID: <20241202121927.864335-1-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk --- meson.build | 1 + qga/commands-posix.c | 20 ++++++++++++++++++++ qga/qapi-schema.json | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/meson.build b/meson.build index e62251c7ca..d06f59095c 100644 --- a/meson.build +++ b/meson.build @@ -2646,6 +2646,7 @@ config_host_data.set('CONFIG_SETNS', cc.has_function('setns') and cc.has_functio config_host_data.set('CONFIG_SYNCFS', cc.has_function('syncfs')) config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range')) config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create')) +config_host_data.set('CONFIG_GETLOADAVG', cc.has_function('getloadavg')) config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range')) config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 636307bedf..6e3c15f539 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1368,3 +1368,23 @@ char *qga_get_host_name(Error **errp) return g_steal_pointer(&hostname); } + +#ifdef CONFIG_GETLOADAVG +GuestLoadAverage *qmp_guest_get_load(Error **errp) +{ + double loadavg[3]; + GuestLoadAverage *ret = NULL; + + if (getloadavg(loadavg, G_N_ELEMENTS(loadavg)) < 0) { + error_setg_errno(errp, errno, + "cannot query load average"); + return NULL; + } + + ret = g_new0(GuestLoadAverage, 1); + ret->load1m = loadavg[0]; + ret->load5m = loadavg[1]; + ret->load15m = loadavg[2]; + return ret; +} +#endif diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 0537bb7886..995594aaf4 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1843,6 +1843,43 @@ 'if': 'CONFIG_LINUX' } + +## +# @GuestLoadAverage: +# +# Statistics about process load information +# +# @load1m: 1-minute load avage +# +# @load5m: 5-minute load avage +# +# @load15m: 15-minute load avage +# +# Since: 10.0 +## +{ 'struct': 'GuestLoadAverage', + 'data': { + 'load1m': 'number', + 'load5m': 'number', + 'load15m': 'number' + }, + 'if': 'CONFIG_GETLOADAVG' +} + +## +# @guest-get-load: +# +# Retrieve CPU process load information +# +# Returns: load information +# +# Since: 10.0 +## +{ 'command': 'guest-get-load', + 'returns': 'GuestLoadAverage', + 'if': 'CONFIG_GETLOADAVG' +} + ## # @GuestNetworkRoute: # From 85978dfb6b1c1334ed6aa998ca06c3f45e2127e0 Mon Sep 17 00:00:00 2001 From: Dehan Meng Date: Wed, 25 Dec 2024 16:37:44 +0800 Subject: [PATCH 0807/2892] qemu-ga: Optimize freeze-hook script logic of logging error Make sure the error log of fsfreeze hooks when freeze/thaw/snapshot could be logged to system logs if the default logfile of qga can't be written or other situations Signed-off-by: Dehan Meng Reviewed-by: Yan Vugenfirer Reviewed-by: Konstantin Kostiuk Message-ID: <20241225083744.277374-1-demeng@redhat.com> Signed-off-by: Konstantin Kostiuk --- scripts/qemu-guest-agent/fsfreeze-hook | 36 +++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/scripts/qemu-guest-agent/fsfreeze-hook b/scripts/qemu-guest-agent/fsfreeze-hook index 13aafd4845..c1feb6f5ce 100755 --- a/scripts/qemu-guest-agent/fsfreeze-hook +++ b/scripts/qemu-guest-agent/fsfreeze-hook @@ -19,15 +19,43 @@ is_ignored_file() { return 1 } +USE_SYSLOG=0 +# if log file is not writable, fallback to syslog +[ ! -w "$LOGFILE" ] && USE_SYSLOG=1 +# try to update log file and fallback to syslog if it fails +touch "$LOGFILE" &>/dev/null || USE_SYSLOG=1 + +# Ensure the log file is writable, fallback to syslog if not +log_message() { + local message="$1" + if [ "$USE_SYSLOG" -eq 0 ]; then + printf "%s: %s\n" "$(date)" "$message" >>"$LOGFILE" + else + logger -t qemu-ga-freeze-hook "$message" + fi +} + # Iterate executables in directory "fsfreeze-hook.d" with the specified args [ ! -d "$FSFREEZE_D" ] && exit 0 + for file in "$FSFREEZE_D"/* ; do is_ignored_file "$file" && continue [ -x "$file" ] || continue - printf "$(date): execute $file $@\n" >>$LOGFILE - "$file" "$@" >>$LOGFILE 2>&1 - STATUS=$? - printf "$(date): $file finished with status=$STATUS\n" >>$LOGFILE + + log_message "Executing $file $@" + if [ "$USE_SYSLOG" -eq 0 ]; then + "$file" "$@" >>"$LOGFILE" 2>&1 + STATUS=$? + else + "$file" "$@" 2>&1 | logger -t qemu-ga-freeze-hook + STATUS=${PIPESTATUS[0]} + fi + + if [ $STATUS -ne 0 ]; then + log_message "Error: $file finished with status=$STATUS" + else + log_message "$file finished successfully" + fi done exit 0 From 122748c83d2a331b43ea17efd78c4117a362f3f2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 16:37:20 +0100 Subject: [PATCH 0808/2892] rust: fix --enable-debug-mutex --feature is an option for cargo but not for rustc. Reported-by: Bernhard Beschow Reviewed-by: Bernhard Beschow Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index ccb20f38c1..9425ba7100 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -7,7 +7,7 @@ if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] endif if get_option('debug_mutex') - _qemu_api_cfg += ['--feature', 'debug_cell'] + _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] endif _qemu_api_rs = static_library( From 3fa010d5317e64276c67ed468b994a2aaa570475 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 16 Dec 2024 14:37:56 +0100 Subject: [PATCH 0809/2892] tests/tcg/s390x: Use the SLOF libc headers for the multiarch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling the s390x tests on Fedora, which has the s390x cross-compiler installed, fails with: In file included from qemu/tests/tcg/s390x/console.c:8: qemu/tests/tcg/s390x/../../../pc-bios/s390-ccw/sclp.c:11:10: fatal error: string.h: No such file or directory 11 | #include This is because Fedora does not have a cross-libc. Since console.c already uses the SLOF libc implementation, add the respective headers to the include path. Signed-off-by: Ilya Leoshkevich Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241216133819.78583-1-iii@linux.ibm.com> Tested-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.softmmu-target | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 969bc5728f..7adde2fa08 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -45,7 +45,8 @@ TESTS += $(ASM_TESTS) S390X_MULTIARCH_RUNTIME_OBJS = head64.o console.o $(MINILIB_OBJS) $(MULTIARCH_TESTS): $(S390X_MULTIARCH_RUNTIME_OBJS) $(MULTIARCH_TESTS): LDFLAGS += $(S390X_MULTIARCH_RUNTIME_OBJS) -$(MULTIARCH_TESTS): CFLAGS += $(MINILIB_INC) +$(MULTIARCH_TESTS): CFLAGS += $(MINILIB_INC) \ + -I$(SRC_PATH)/roms/SLOF/lib/libc/include/ memory: CFLAGS += -DCHECK_UNALIGNED=0 # s390x clears the BSS section so we need to account for that From 419613a103af4322682003e95346ed438fcdfb41 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:23 +0100 Subject: [PATCH 0810/2892] hw/s390x/s390-virtio-ccw: Remove the deprecated 2.4 and 2.5 machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are older than 6 years, so according to our machine support policy, they can be removed now. This removes the requirements for the storage keys "migration-enabled" property which will be removed in the next patch. It also removes the code that sets "max_revision" to 0 for some CCW devices, but the relating code in virtio-ccw.c indicates that 0 could have also been in use for other machines types < 5.1, so further clean-up for code related to "max_revision" won't be done yet (see also commit d55f518248f - "virtio: skip legacy support check on machine types less than 5.1"). Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 2be8da2913..bca61488cc 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1325,43 +1325,6 @@ static void ccw_machine_2_6_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2, 6); -static void ccw_machine_2_5_instance_options(MachineState *machine) -{ - ccw_machine_2_6_instance_options(machine); -} - -static void ccw_machine_2_5_class_options(MachineClass *mc) -{ - ccw_machine_2_6_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_5, hw_compat_2_5_len); -} -DEFINE_CCW_MACHINE(2, 5); - -static void ccw_machine_2_4_instance_options(MachineState *machine) -{ - ccw_machine_2_5_instance_options(machine); -} - -static void ccw_machine_2_4_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_S390_SKEYS, "migration-enabled", "off", }, - { "virtio-blk-ccw", "max_revision", "0", }, - { "virtio-balloon-ccw", "max_revision", "0", }, - { "virtio-serial-ccw", "max_revision", "0", }, - { "virtio-9p-ccw", "max_revision", "0", }, - { "virtio-rng-ccw", "max_revision", "0", }, - { "virtio-net-ccw", "max_revision", "0", }, - { "virtio-scsi-ccw", "max_revision", "0", }, - { "vhost-scsi-ccw", "max_revision", "0", }, - }; - - ccw_machine_2_5_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} -DEFINE_CCW_MACHINE(2, 4); - #endif static void ccw_machine_register_types(void) From 93edd339ff18a55d28f5070eaf9a59b3f8385249 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:24 +0100 Subject: [PATCH 0811/2892] hw/s390x/s390-skeys: Remove the "migration-enabled" property This property was only set to "off" by the old s390-ccw-virtio-2.4 machine type which has now been removed. So we can now remove the property and the related code, too. Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-skeys.c | 10 +--------- include/hw/s390x/storage-keys.h | 2 -- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index dda96ea32a..995817f4a3 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -469,23 +469,15 @@ static void s390_skeys_realize(DeviceState *dev, Error **errp) { S390SKeysState *ss = S390_SKEYS(dev); - if (ss->migration_enabled) { - register_savevm_live(TYPE_S390_SKEYS, 0, 1, - &savevm_s390_storage_keys, ss); - } + register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); } -static const Property s390_skeys_props[] = { - DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true), -}; - static void s390_skeys_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->hotpluggable = false; dc->realize = s390_skeys_realize; - device_class_set_props(dc, s390_skeys_props); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/include/hw/s390x/storage-keys.h b/include/hw/s390x/storage-keys.h index 976ffb2039..408d2815d4 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -21,8 +21,6 @@ OBJECT_DECLARE_TYPE(S390SKeysState, S390SKeysClass, S390_SKEYS) struct S390SKeysState { DeviceState parent_obj; - bool migration_enabled; - }; From 29ac4a4a4cae350c48a2c84386a5db594b79bc68 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:25 +0100 Subject: [PATCH 0812/2892] hw/s390x/s390-virtio-ccw: Remove the deprecated 2.6 machine type The s390-ccw-virtio-2.6 machine is older than 6 years, so according to our machine support policy, it can be removed now. Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index bca61488cc..341430a101 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1305,26 +1305,6 @@ static void ccw_machine_2_7_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2, 7); -static void ccw_machine_2_6_instance_options(MachineState *machine) -{ - ccw_machine_2_7_instance_options(machine); -} - -static void ccw_machine_2_6_class_options(MachineClass *mc) -{ - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - { TYPE_S390_IPL, "iplbext_migration", "off", }, - { TYPE_VIRTUAL_CSS_BRIDGE, "css_dev_path", "off", }, - }; - - s390mc->ri_allowed = false; - ccw_machine_2_7_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} -DEFINE_CCW_MACHINE(2, 6); - #endif static void ccw_machine_register_types(void) From db65ac5e258e75e9aec45626bf1071626094e057 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:26 +0100 Subject: [PATCH 0813/2892] hw/s390x: Remove the "ri_allowed" switch Only s390-ccw-virtio-2.6 and older used to set this switch to "off", for newer machine types it is always enabled. Since we removed the old machine types now, we can also remove the switch in the code and assume that it is always enabled now. Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 14 ++++---------- include/hw/s390x/s390-virtio-ccw.h | 3 --- target/s390x/kvm/kvm.c | 6 ++---- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 341430a101..ffeee2da48 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -725,10 +725,10 @@ static S390CcwMachineClass *current_mc; * various "*_allowed" variables are enabled, so that the *_allowed() wrappers * below return the correct default value for the "none" machine. * - * Attention! Do *not* add additional new wrappers for CPU features (e.g. like - * the ri_allowed() wrapper) via this mechanism anymore. CPU features should - * be handled via the CPU models, i.e. checking with cpu_model_allowed() during - * CPU initialization and s390_has_feat() later should be sufficient. + * Attention! Do *not* add additional new wrappers for CPU features via this + * mechanism anymore. CPU features should be handled via the CPU models, + * i.e. checking with cpu_model_allowed() during CPU initialization and + * s390_has_feat() later should be sufficient. */ static S390CcwMachineClass *get_machine_class(void) { @@ -744,11 +744,6 @@ static S390CcwMachineClass *get_machine_class(void) return current_mc; } -bool ri_allowed(void) -{ - return get_machine_class()->ri_allowed; -} - bool cpu_model_allowed(void) { return get_machine_class()->cpu_model_allowed; @@ -791,7 +786,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - s390mc->ri_allowed = true; s390mc->cpu_model_allowed = true; s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 599740a998..4559dbf1bd 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -53,14 +53,11 @@ struct S390CcwMachineClass { MachineClass parent_class; /*< public >*/ - bool ri_allowed; bool cpu_model_allowed; bool hpage_1m_allowed; int max_threads; }; -/* runtime-instrumentation allowed by the machine */ -bool ri_allowed(void); /* cpu model allowed by the machine */ bool cpu_model_allowed(void); /* 1M huge page mappings allowed by the machine */ diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index db645a4813..540b474398 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -374,10 +374,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_CPU_TOPOLOGY, 0); - if (ri_allowed()) { - if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { - cap_ri = 1; - } + if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { + cap_ri = 1; } if (cpu_model_allowed()) { kvm_vm_enable_cap(s, KVM_CAP_S390_GS, 0); From 25a65a274d5c6030c1e1ce79b845c7143ec0dc7a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:27 +0100 Subject: [PATCH 0814/2892] hw/s390x/ipl: Remove the "iplbext_migration" property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the old machine types that used this property have been removed, we can remove the property and the corresponding code. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-6-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 10 ---------- hw/s390x/ipl.h | 1 - 2 files changed, 11 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 3a946be7a5..4aa21c91fc 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -49,13 +49,6 @@ #define BIOS_MAX_SIZE 0x300000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) -static bool iplb_extended_needed(void *opaque) -{ - S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); - - return ipl->iplbext_migration; -} - /* Place the IPLB chain immediately before the BIOS in memory */ static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count) { @@ -67,7 +60,6 @@ static const VMStateDescription vmstate_iplb_extended = { .name = "ipl/iplb_extended", .version_id = 0, .minimum_version_id = 0, - .needed = iplb_extended_needed, .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200), VMSTATE_END_OF_LIST() @@ -297,8 +289,6 @@ static const Property s390_ipl_properties[] = { DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), DEFINE_PROP_STRING("firmware", S390IPLState, firmware), DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), - DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, - true), }; static void s390_ipl_set_boot_menu(S390IPLState *ipl) diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index d7d0b7bfd2..8e3882d506 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -80,7 +80,6 @@ struct S390IPLState { uint8_t cssid; uint8_t ssid; uint16_t devno; - bool iplbext_migration; }; QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong"); From 1a276185401a95a1341e52837d07b3a8f76b3634 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:28 +0100 Subject: [PATCH 0815/2892] hw/s390x/css-bridge: Remove the "css_dev_path" property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the s390-ccw-virtio-2.6 and older machine types have been removed, the "css_dev_path" property of the css-bridge is also not used anymore and thus can be removed. This way we finally get rid of the problem that has been described in: https://gitlab.com/qemu-project/qemu/-/issues/2213 Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-7-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/css-bridge.c | 16 +--------------- include/hw/s390x/css-bridge.h | 1 - 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 04ab1f6402..c48d5571b5 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -66,16 +66,8 @@ static char *virtual_css_bus_get_dev_path(DeviceState *dev) { CcwDevice *ccw_dev = CCW_DEVICE(dev); SubchDev *sch = ccw_dev->sch; - VirtualCssBridge *bridge = - VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent); - /* - * We can't provide a dev path for backward compatibility on - * older machines, as it is visible in the migration stream. - */ - return bridge->css_dev_path ? - g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno) : - NULL; + return g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno); } static void virtual_css_bus_class_init(ObjectClass *klass, void *data) @@ -120,11 +112,6 @@ VirtualCssBus *virtual_css_bus_init(void) /***************** Virtual-css Bus Bridge Device ********************/ -static const Property virtual_css_bridge_properties[] = { - DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path, - true), -}; - static bool prop_get_true(Object *obj, Error **errp) { return true; @@ -137,7 +124,6 @@ static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) hc->unplug = ccw_device_unplug; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - device_class_set_props(dc, virtual_css_bridge_properties); object_class_property_add_bool(klass, "cssid-unrestricted", prop_get_true, NULL); object_class_property_set_description(klass, "cssid-unrestricted", diff --git a/include/hw/s390x/css-bridge.h b/include/hw/s390x/css-bridge.h index deb606d71f..4f874ed781 100644 --- a/include/hw/s390x/css-bridge.h +++ b/include/hw/s390x/css-bridge.h @@ -19,7 +19,6 @@ /* virtual css bridge */ struct VirtualCssBridge { SysBusDevice sysbus_dev; - bool css_dev_path; }; #define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge" From 3199c7ee76089fb6844f6b2bed1f5d3d99a7527c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:29 +0100 Subject: [PATCH 0816/2892] hw/s390x/s390-virtio-ccw: Remove the deprecated 2.7 machine type The s390-ccw-virtio-2.7 machine is older than 6 years, so according to our machine support policy, it can be removed now. Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-8-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index ffeee2da48..a1e9c1f6e8 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1284,21 +1284,6 @@ static void ccw_machine_2_8_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2, 8); -static void ccw_machine_2_7_instance_options(MachineState *machine) -{ - ccw_machine_2_8_instance_options(machine); -} - -static void ccw_machine_2_7_class_options(MachineClass *mc) -{ - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - - s390mc->cpu_model_allowed = false; - ccw_machine_2_8_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); -} -DEFINE_CCW_MACHINE(2, 7); - #endif static void ccw_machine_register_types(void) From fd58c03a0ecb67420ac81d6b3095605ad0c45edd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:30 +0100 Subject: [PATCH 0817/2892] hw/s390x: Remove the cpu_model_allowed flag and related code Now that the last machine type that disabled cpu_model_allowed has been removed, we can also remove the cpu_model_allowed flag itself and all the related conditional code. Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-9-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 9 +-------- include/hw/s390x/s390-virtio-ccw.h | 3 --- target/s390x/kvm/kvm.c | 10 ++-------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a1e9c1f6e8..08562e45a8 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -727,8 +727,7 @@ static S390CcwMachineClass *current_mc; * * Attention! Do *not* add additional new wrappers for CPU features via this * mechanism anymore. CPU features should be handled via the CPU models, - * i.e. checking with cpu_model_allowed() during CPU initialization and - * s390_has_feat() later should be sufficient. + * i.e. checking with s390_has_feat() should be sufficient. */ static S390CcwMachineClass *get_machine_class(void) { @@ -744,11 +743,6 @@ static S390CcwMachineClass *get_machine_class(void) return current_mc; } -bool cpu_model_allowed(void) -{ - return get_machine_class()->cpu_model_allowed; -} - bool hpage_1m_allowed(void) { return get_machine_class()->hpage_1m_allowed; @@ -786,7 +780,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - s390mc->cpu_model_allowed = true; s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; mc->init = ccw_init; diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 4559dbf1bd..686d9497d2 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -53,13 +53,10 @@ struct S390CcwMachineClass { MachineClass parent_class; /*< public >*/ - bool cpu_model_allowed; bool hpage_1m_allowed; int max_threads; }; -/* cpu model allowed by the machine */ -bool cpu_model_allowed(void); /* 1M huge page mappings allowed by the machine */ bool hpage_1m_allowed(void); diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 540b474398..4d56e653dd 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -374,12 +374,10 @@ int kvm_arch_init(MachineState *ms, KVMState *s) kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_CPU_TOPOLOGY, 0); + kvm_vm_enable_cap(s, KVM_CAP_S390_GS, 0); if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { cap_ri = 1; } - if (cpu_model_allowed()) { - kvm_vm_enable_cap(s, KVM_CAP_S390_GS, 0); - } /* * The migration interface for ais was introduced with kernel 4.13 @@ -387,7 +385,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) * support is considered necessary, we only try to enable this for * newer machine types if KVM_CAP_S390_AIS_MIGRATION is available. */ - if (cpu_model_allowed() && kvm_kernel_irqchip_allowed() && + if (kvm_kernel_irqchip_allowed() && kvm_check_extension(s, KVM_CAP_S390_AIS_MIGRATION)) { kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0); } @@ -2352,10 +2350,6 @@ static int configure_cpu_feat(const S390FeatBitmap features) bool kvm_s390_cpu_models_supported(void) { - if (!cpu_model_allowed()) { - /* compatibility machines interfere with the cpu model */ - return false; - } return kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, KVM_S390_VM_CPU_MACHINE) && kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, From 66924fe36977d9d9e45ba3e0b6e851ee170507f6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:31 +0100 Subject: [PATCH 0818/2892] hw/s390x/s390-virtio-ccw: Remove the deprecated 2.8 machine type The s390-ccw-virtio-2.8 machine is older than 6 years, so according to our machine support policy, it can be removed now. Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-10-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 08562e45a8..8a242cc1ec 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1260,23 +1260,6 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2, 9); -static void ccw_machine_2_8_instance_options(MachineState *machine) -{ - ccw_machine_2_9_instance_options(machine); -} - -static void ccw_machine_2_8_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_S390_FLIC_COMMON, "adapter_routes_max_batch", "64", }, - }; - - ccw_machine_2_9_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} -DEFINE_CCW_MACHINE(2, 8); - #endif static void ccw_machine_register_types(void) From 921dee4645c4e8abffbbecaf595c82935170a072 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:42:32 +0100 Subject: [PATCH 0819/2892] hw/s390x: Remove the "adapter_routes_max_batch" property from the flic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the s390-ccw-virtio-2.8 machine has been removed, we don't need the "adapter_routes_max_batch" property anymore and can remove it. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Message-ID: <20250103144232.520383-11-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/intc/s390_flic.c | 9 --------- hw/s390x/virtio-ccw.c | 5 ++--- include/hw/s390x/s390_flic.h | 2 -- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 3f3fa939d3..c20f4c1075 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -471,8 +471,6 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) } static const Property s390_flic_common_properties[] = { - DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState, - adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), DEFINE_PROP_BOOL("migration-enabled", S390FLICState, migration_enabled, true), }; @@ -480,13 +478,6 @@ static const Property s390_flic_common_properties[] = { static void s390_flic_common_realize(DeviceState *dev, Error **errp) { S390FLICState *fs = S390_FLIC_COMMON(dev); - uint32_t max_batch = fs->adapter_routes_max_batch; - - if (max_batch > ADAPTER_ROUTES_MAX_GSI) { - error_setg(errp, "flic property adapter_routes_max_batch too big" - " (%d > %d)", max_batch, ADAPTER_ROUTES_MAX_GSI); - return; - } fs->ais_supported = s390_has_feat(S390_FEAT_ADAPTER_INT_SUPPRESSION); } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 7cbce4766a..43f3b162c8 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1157,7 +1157,6 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) CcwDevice *ccw_dev = CCW_DEVICE(d); SubchDev *sch = ccw_dev->sch; int n = virtio_get_num_queues(vdev); - S390FLICState *flic = s390_get_flic(); if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) { dev->max_rev = 0; @@ -1184,10 +1183,10 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) VIRTIO_QUEUE_MAX); return; } - if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) { + if (virtio_get_num_queues(vdev) > ADAPTER_ROUTES_MAX_GSI) { error_setg(errp, "The number of virtqueues %d " "exceeds flic adapter route limit %d", n, - flic->adapter_routes_max_batch); + ADAPTER_ROUTES_MAX_GSI); return; } diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 4d66c5e42e..85016d5ccc 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -41,8 +41,6 @@ OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass, struct S390FLICState { SysBusDevice parent_obj; - /* to limit AdapterRoutes.num_routes for compat */ - uint32_t adapter_routes_max_batch; bool ais_supported; bool migration_enabled; }; From 00cfbdcbe16dd1af2af8aba0584a2acf2b4d8ce6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 16:54:11 +0100 Subject: [PATCH 0820/2892] Remove the deprecated "-runas" command line option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been marked as deprecated two releases ago, so it should be fine now to remove this command line option. Reviewed-by: Daniel P. Berrangé Message-ID: <20250103155411.721759-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- docs/about/deprecated.rst | 6 ------ docs/about/removed-features.rst | 6 ++++++ qemu-options.hx | 15 +-------------- system/vl.c | 9 --------- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d6809f94ea..63b46fd520 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -74,12 +74,6 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is marked deprecated since 9.0, users have to ensure that all the topology members described with -smp are supported by the target machine. -``-runas`` (since 9.1) -'''''''''''''''''''''' - -Use ``-run-with user=..`` instead. - - User-mode emulator command line arguments ----------------------------------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index cb1388049a..c6616ce05e 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -555,6 +555,12 @@ to produce an odd effect (rotating input but not display output). But this was never intended or documented behaviour, so we have dropped the options along with the machine models they were intended for. +``-runas`` (removed in 10.0) +'''''''''''''''''''''''''''' + +Use ``-run-with user=..`` instead. + + User-mode emulator command line arguments ----------------------------------------- diff --git a/qemu-options.hx b/qemu-options.hx index cc694d3b89..7090d59f6f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4976,19 +4976,6 @@ SRST ``-nodefaults`` option will disable all those default devices. ERST -#ifndef _WIN32 -DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ - "-runas user change to user id user just before starting the VM\n" \ - " user can be numeric uid:gid instead\n", - QEMU_ARCH_ALL) -#endif -SRST -``-runas user`` - Immediately before starting guest execution, drop root privileges, - switching to the specified user. This option is deprecated, use - ``-run-with user=...`` instead. -ERST - DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env, "-prom-env variable=value\n" " set OpenBIOS nvram variables\n", @@ -5176,7 +5163,7 @@ SRST ``chroot=dir`` can be used for doing a chroot to the specified directory immediately before starting the guest execution. This is especially useful - in combination with -runas. + in combination with ``user=...``. ``user=username`` or ``user=uid:gid`` can be used to drop root privileges before starting guest execution. QEMU will use the ``setuid`` and ``setgid`` diff --git a/system/vl.c b/system/vl.c index 0843b7ab49..3c5bd36d7d 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3611,15 +3611,6 @@ void qemu_init(int argc, char **argv) /* Nothing to be parsed here. Especially, do not error out below. */ break; #if defined(CONFIG_POSIX) - case QEMU_OPTION_runas: - warn_report("-runas is deprecated, use '-run-with user=...' instead"); - if (!os_set_runas(optarg)) { - error_report("User \"%s\" doesn't exist" - " (and is not :)", - optarg); - exit(1); - } - break; case QEMU_OPTION_daemonize: os_set_daemonize(true); break; From 5cd37fe6dd278302ed6ceb86727526a38f410314 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 15:57:02 +0100 Subject: [PATCH 0821/2892] docs/about/deprecated: Remove paragraph about initial deprecation in 2.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we introduced the deprecation rule of keeping deprecated features for two more releases, we had to state that we would not remove features by surprise that had already been marked as deprecated before. Nowadays, this paragraph is not needed anymore, so we can remove it now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250103145702.597139-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- docs/about/deprecated.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 63b46fd520..4a3c302962 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -24,12 +24,6 @@ should exclusively use a non-deprecated machine type, with use of the most recent version highly recommended. Non-versioned machine types follow the general feature deprecation policy. -Prior to the 2.10.0 release there was no official policy on how -long features would be deprecated prior to their removal, nor -any documented list of which features were deprecated. Thus -any features deprecated prior to 2.10.0 will be treated as if -they were first deprecated in the 2.10.0 release. - What follows is a list of all features currently marked as deprecated. From f69705f300f487936919359354925395105ec018 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 7 Jan 2025 12:52:45 +0100 Subject: [PATCH 0822/2892] tests/functional/test_x86_64_hotplug_cpu: Fix race condition during unplug When unplugging the CPU, the test tries to check for a successful unplug by changing to the /sys/devices/system/cpu/cpu1 directory to see whether that fails. However, the "cd" could be faster than the unplug operation in the kernel, so there is a race condition and the test sometimes fails here. Fix it by trying to change the directory in a loop until the the CPU has really been unplugged. While we're at it, also add a "cd .." before unplugging to make the console output a little bit less confusing (since the path is echoed in the shell prompt). Reported-by: Stefan Hajnoczi Message-ID: <20250107115245.52755-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_x86_64_hotplug_cpu.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/test_x86_64_hotplug_cpu.py index b1d5156c72..7b9200ac2e 100755 --- a/tests/functional/test_x86_64_hotplug_cpu.py +++ b/tests/functional/test_x86_64_hotplug_cpu.py @@ -59,11 +59,13 @@ class HotPlugCPU(LinuxKernelTest): 'cd /sys/devices/system/cpu/cpu1', 'cpu1#') + exec_command_and_wait_for_pattern(self, 'cd ..', prompt) self.vm.cmd('device_del', id='c1') exec_command_and_wait_for_pattern(self, - 'cd /sys/devices/system/cpu/cpu1', - 'No such file or directory') + 'while cd /sys/devices/system/cpu/cpu1 ;' + ' do sleep 0.2 ; done', + 'No such file or directory') if __name__ == '__main__': LinuxKernelTest.main() From e3526d0fd73949fd6eafd97711351a7934b496a3 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Thu, 2 Jan 2025 22:47:21 +0000 Subject: [PATCH 0823/2892] hw/core/loader: Use ssize_t for efi zboot unpacker Convert to use sszie_t to represent size internally to avoid large image overflowing the size. Suggested-by: Richard Henderson Signed-off-by: Jiaxun Yang Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/arm/boot.c | 2 +- hw/core/loader.c | 4 ++-- include/hw/loader.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 68fe8654e6..b44bea8a82 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -857,7 +857,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR; uint64_t kernel_size = 0; uint8_t *buffer; - int size; + ssize_t size; /* On aarch64, it's the bootloader's job to uncompress the kernel. */ size = load_image_gzipped_buffer(filename, LOAD_IMAGE_MAX_GUNZIP_BYTES, diff --git a/hw/core/loader.c b/hw/core/loader.c index c0407e2d0d..4dfdb027ee 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -886,11 +886,11 @@ struct linux_efi_zboot_header { * * If the image is not a Linux EFI zboot image, do nothing and return success. */ -ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size) +ssize_t unpack_efi_zboot_image(uint8_t **buffer, ssize_t *size) { const struct linux_efi_zboot_header *header; uint8_t *data = NULL; - int ploff, plsize; + ssize_t ploff, plsize; ssize_t bytes; /* ignore if this is too small to be a EFI zboot image */ diff --git a/include/hw/loader.h b/include/hw/loader.h index 7f6d06b956..8985046be4 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -101,7 +101,7 @@ ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, * Returns the size of the decompressed payload if decompression was performed * successfully. */ -ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size); +ssize_t unpack_efi_zboot_image(uint8_t **buffer, ssize_t *size); #define ELF_LOAD_FAILED -1 #define ELF_LOAD_NOT_ELF -2 From cc4ba2c2d25229ad39bf04a620f7d3b071b2af1d Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 Jan 2025 10:01:07 +0800 Subject: [PATCH 0824/2892] hw/loongarch/boot: Support Linux raw boot image Support booting such image by parsing header as per Linux's specification [1]. This enabled booting vmlinux.efi/vmlinuz.efi shipped by distros without supplying BIOS. [1]: https://docs.kernel.org/arch/loongarch/booting.html Signed-off-by: Jiaxun Yang Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/boot.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 48154cdce6..241c0eef1f 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -15,6 +15,26 @@ #include "system/reset.h" #include "system/qtest.h" +/* + * Linux Image Format + * https://docs.kernel.org/arch/loongarch/booting.html + */ +#define LINUX_PE_MAGIC 0x818223cd +#define MZ_MAGIC 0x5a4d /* "MZ" */ + +struct loongarch_linux_hdr { + uint32_t mz_magic; + uint32_t res0; + uint64_t kernel_entry; + uint64_t kernel_size; + uint64_t load_offset; + uint64_t res1; + uint64_t res2; + uint64_t res3; + uint32_t linux_pe_magic; + uint32_t pe_header_offset; +} QEMU_PACKED; + struct memmap_entry *memmap_table; unsigned memmap_entries; @@ -171,6 +191,50 @@ static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); } +static int64_t load_loongarch_linux_image(const char *filename, + uint64_t *kernel_entry, + uint64_t *kernel_low, + uint64_t *kernel_high) +{ + gsize len; + ssize_t size; + uint8_t *buffer; + struct loongarch_linux_hdr *hdr; + + /* Load as raw file otherwise */ + if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) { + return -1; + } + size = len; + + /* Unpack the image if it is a EFI zboot image */ + if (unpack_efi_zboot_image(&buffer, &size) < 0) { + g_free(buffer); + return -1; + } + + hdr = (struct loongarch_linux_hdr *)buffer; + + if (extract32(le32_to_cpu(hdr->mz_magic), 0, 16) != MZ_MAGIC || + le32_to_cpu(hdr->linux_pe_magic) != LINUX_PE_MAGIC) { + g_free(buffer); + return -1; + } + + /* Early kernel versions may have those fields in virtual address */ + *kernel_entry = extract64(le64_to_cpu(hdr->kernel_entry), + 0, TARGET_PHYS_ADDR_SPACE_BITS); + *kernel_low = extract64(le64_to_cpu(hdr->load_offset), + 0, TARGET_PHYS_ADDR_SPACE_BITS); + *kernel_high = *kernel_low + size; + + rom_add_blob_fixed(filename, buffer, size, *kernel_low); + + g_free(buffer); + + return size; +} + static int64_t load_kernel_info(struct loongarch_boot_info *info) { uint64_t kernel_entry, kernel_low, kernel_high; @@ -181,6 +245,11 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) &kernel_entry, &kernel_low, &kernel_high, NULL, 0, EM_LOONGARCH, 1, 0); + if (kernel_size < 0) { + kernel_size = load_loongarch_linux_image(info->kernel_filename, + &kernel_entry, &kernel_low, + &kernel_high); + } if (kernel_size < 0) { error_report("could not load kernel '%s': %s", From 38adceb4c38de657f03132ccd9fd380395957a9f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 31 Dec 2024 18:07:18 +0800 Subject: [PATCH 0825/2892] target/loongarch: Only support 64bit pte width iFrom LoongArch Reference Manual pte width can be 64bit, 128bit or more. Instead real hardware only supports 64bit pte width. For 12bit pte, there is no detail definition for all 128bit from manual. Here only 64bit pte width is supported for simplicity, will add this in later if real hw support it and there is definition for all the bits from manual. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/helper.h | 1 + target/loongarch/tcg/csr_helper.c | 21 +++++++++++++++++++ .../tcg/insn_trans/trans_privileged.c.inc | 2 +- target/loongarch/tcg/tlb_helper.c | 17 +++------------ 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h index b3b64a0215..943517b5f2 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -104,6 +104,7 @@ DEF_HELPER_2(csrwr_estat, i64, env, tl) DEF_HELPER_2(csrwr_asid, i64, env, tl) DEF_HELPER_2(csrwr_tcfg, i64, env, tl) DEF_HELPER_2(csrwr_ticlr, i64, env, tl) +DEF_HELPER_2(csrwr_pwcl, i64, env, tl) DEF_HELPER_2(iocsrrd_b, i64, env, tl) DEF_HELPER_2(iocsrrd_h, i64, env, tl) DEF_HELPER_2(iocsrrd_w, i64, env, tl) diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 15f94caefa..6c95be9910 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/main-loop.h" #include "cpu.h" #include "internals.h" @@ -95,3 +96,23 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) } return old_v; } + +target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) +{ + int shift; + int64_t old_v = env->CSR_PWCL; + + /* + * The real hardware only supports 64bit PTE width now, 128bit or others + * treated as illegal. + */ + shift = FIELD_EX64(val, CSR_PWCL, PTEWIDTH); + if (shift) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attempted set pte width with %d bit\n", 64 << shift); + val = FIELD_DP64(val, CSR_PWCL, PTEWIDTH, 0); + } + + env->CSR_PWCL = val; + return old_v; +} diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 7e4ec93edb..30f9b83fb2 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -95,7 +95,7 @@ static const CSRInfo csr_info[] = { CSR_OFF(PGDL), CSR_OFF(PGDH), CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL), - CSR_OFF(PWCL), + CSR_OFF_FUNCS(PWCL, 0, NULL, gen_helper_csrwr_pwcl), CSR_OFF(PWCH), CSR_OFF(STLBPS), CSR_OFF(RVACFG), diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 97f38fc391..8c61fe728c 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -512,7 +512,6 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, { CPUState *cs = env_cpu(env); target_ulong badvaddr, index, phys, ret; - int shift; uint64_t dir_base, dir_width; if (unlikely((level == 0) || (level > 4))) { @@ -537,14 +536,9 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, badvaddr = env->CSR_TLBRBADV; base = base & TARGET_PHYS_MASK; - - /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ - shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); - shift = (shift + 1) * 3; - get_dir_base_width(env, &dir_base, &dir_width, level); index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); - phys = base | index << shift; + phys = base | index << 3; ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; return ret; } @@ -554,7 +548,6 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, { CPUState *cs = env_cpu(env); target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; - int shift; uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); uint64_t dir_base, dir_width; @@ -595,16 +588,12 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, tmp0 += MAKE_64BIT_MASK(ps, 1); } } else { - /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ - shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); - shift = (shift + 1) * 3; badv = env->CSR_TLBRBADV; ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); ptindex = ptindex & ~0x1; /* clear bit 0 */ - ptoffset0 = ptindex << shift; - ptoffset1 = (ptindex + 1) << shift; - + ptoffset0 = ptindex << 3; + ptoffset1 = (ptindex + 1) << 3; phys = base | (odd ? ptoffset1 : ptoffset0); tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; ps = ptbase; From 5a3e068d411c563d924f5eb40f82e91aaca1eaed Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 6 Jan 2025 15:36:25 +0800 Subject: [PATCH 0826/2892] hw/intc/loongarch_extioi: Get cpu number from possible_cpu_arch_ids Supported CPU number can be acquired from function possible_cpu_arch_ids(), cpu-num property is not necessary. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_extioi.c | 6 ------ hw/intc/loongarch_extioi_common.c | 17 +++++++++++++++-- include/hw/intc/loongarch_extioi_common.h | 2 ++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 4a1a7c357c..d18f47def7 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -347,12 +347,6 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) s->status |= BIT(EXTIOI_ENABLE); } - s->cpu = g_new0(ExtIOICore, s->num_cpu); - if (s->cpu == NULL) { - error_setg(errp, "Memory allocation for ExtIOICore faile"); - return; - } - for (i = 0; i < s->num_cpu; i++) { for (pin = 0; pin < LS3A_INTC_IP; pin++) { qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index e4c1cc3c98..99a091e30b 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -13,11 +13,24 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) { LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev; + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *id_list; + int i; - if (s->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + s->num_cpu = id_list->len; + s->cpu = g_new0(ExtIOICore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for ExtIOICore faile"); return; } + + for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].arch_id = id_list->cpus[i].arch_id; + s->cpu[i].cpu = CPU(id_list->cpus[i].cpu); + } } static int loongarch_extioi_common_pre_save(void *opaque) diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index f6bc778a85..22d7880977 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -65,6 +65,8 @@ typedef struct ExtIOICore { uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); qemu_irq parent_irq[LS3A_INTC_IP]; + uint64_t arch_id; + CPUState *cpu; } ExtIOICore; struct LoongArchExtIOICommonState { From 0443b858873d1a80eb9ede901513b247b83ea76b Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 6 Jan 2025 15:36:26 +0800 Subject: [PATCH 0827/2892] hw/intc/loongarch_extioi: Remove num-cpu property Since cpu number can be acquired from possible_cpu_arch_ids(), num-cpu property is not necessary. Here remove num-cpu property for object TYPE_LOONGARCH_EXTIOI_COMMON object. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_extioi_common.c | 1 - hw/loongarch/virt.c | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 99a091e30b..fd56253d10 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -95,7 +95,6 @@ static const VMStateDescription vmstate_loongarch_extioi = { }; static const Property extioi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOICommonState, num_cpu, 1), DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, features, EXTIOI_HAS_VIRT_EXTENSION, 0), }; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 60bd4dc9d3..df56d75a6e 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -921,7 +921,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Create EXTIOI device */ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); - qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); if (virt_is_veiointc_enabled(lvms)) { qdev_prop_set_bit(extioi, "has-virtualization-extension", true); } From c3afa714bcea4c8b014fec99881bd0bdbe8262b8 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 6 Jan 2025 15:36:27 +0800 Subject: [PATCH 0828/2892] hw/intc/loongarch_extioi: Add irq routing support from physical id The simliar with IPI interrupt controller, physical cpu id is used for irq routing for extioi interrupt controller. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_extioi.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index d18f47def7..f3055ec4d2 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -15,6 +15,23 @@ #include "hw/intc/loongarch_extioi.h" #include "trace.h" +static int extioi_get_index_from_archid(LoongArchExtIOICommonState *s, + uint64_t arch_id) +{ + int i; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].arch_id == arch_id) { + break; + } + } + + if ((i < s->num_cpu) && s->cpu[i].cpu) { + return i; + } + + return -1; +} static void extioi_update_irq(LoongArchExtIOICommonState *s, int irq, int level) { @@ -125,7 +142,7 @@ static inline void extioi_enable_irq(LoongArchExtIOICommonState *s, int index,\ static inline void extioi_update_sw_coremap(LoongArchExtIOICommonState *s, int irq, uint64_t val, bool notify) { - int i, cpu; + int i, cpu, cpuid; /* * loongarch only support little endian, @@ -134,12 +151,17 @@ static inline void extioi_update_sw_coremap(LoongArchExtIOICommonState *s, val = cpu_to_le64(val); for (i = 0; i < 4; i++) { - cpu = val & 0xff; + cpuid = val & 0xff; val = val >> 8; if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { - cpu = ctz32(cpu); - cpu = (cpu >= 4) ? 0 : cpu; + cpuid = ctz32(cpuid); + cpuid = (cpuid >= 4) ? 0 : cpuid; + } + + cpu = extioi_get_index_from_archid(s, cpuid); + if (cpu < 0) { + continue; } if (s->sw_coremap[irq + i] == cpu) { From 981780cdda5a60ae7ae319933673ff9475245965 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 Dec 2024 16:10:30 +0000 Subject: [PATCH 0829/2892] hw/i386/pc: Fix level interrupt sharing for Xen event channel GSI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The system GSIs are not designed for sharing. One device might assert a shared interrupt with qemu_set_irq() and another might deassert it, and the level from the first device is lost. This could be solved by refactoring the x86 GSI code to use an OrIrq device, but that still wouldn't be ideal. The best answer would be to have a 'resample' callback which is invoked when the interrupt is acked at the interrupt controller, and causes the devices to re-trigger the interrupt if it should still be pending. This is the model that VFIO in Linux uses, with a 'resampler' eventfd that actually unmasks the interrupt on the hardware device and thus triggers a new interrupt from it if needed. As things stand, QEMU currently doesn't use that VFIO interface correctly, and just bashes on the resampler for every MMIO access to the device "just in case". Which requires unmapping and trapping the MMIO while an interrupt is pending! For the Xen callback GSI, QEMU does something similar — a flag is set which triggers a poll on *every* vmexst to see if the GSI should be deasserted. Proper resampler support would solve all of that, but is a task for later which has already been on the TODO list for a while. Since the Xen event channel GSI support *already* has hooks into the PC gsi_handler() code for routing GSIs to PIRQs, we can use that for a simpler bug fix. So... remember the externally-driven state of the line (from e.g. PCI INTx) and set the logical OR of that with the GSI. As a bonus, we now only need to enable the polling of vcpu_info on vmexit if the Xen callback GSI is the *only* reason the corresponding line is asserted. Closes: https://gitlab.com/qemu-project/qemu/-/issues/2731 Fixes: ddf0fd9ae1fd ("hw/xen: Support HVM_PARAM_CALLBACK_TYPE_GSI callback") Reported-by: Thomas Huth Signed-off-by: David Woodhouse Acked-by: Michael S. Tsirkin --- hw/i386/kvm/xen_evtchn.c | 60 +++++++++++++++++++++++++++++++--------- hw/i386/kvm/xen_evtchn.h | 2 +- hw/i386/x86-common.c | 32 +++++++++++++-------- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index bd2a3cbee0..58484f308e 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -140,6 +140,8 @@ struct XenEvtchnState { uint64_t callback_param; bool evtchn_in_kernel; + bool setting_callback_gsi; + int extern_gsi_level; uint32_t callback_gsi; QEMUBH *gsi_bh; @@ -431,9 +433,22 @@ void xen_evtchn_set_callback_level(int level) } if (s->callback_gsi && s->callback_gsi < s->nr_callback_gsis) { - qemu_set_irq(s->callback_gsis[s->callback_gsi], level); - if (level) { - /* Ensure the vCPU polls for deassertion */ + /* + * Ugly, but since we hold the BQL we can set this flag so that + * xen_evtchn_set_gsi() can tell the difference between this code + * setting the GSI, and an external device (PCI INTx) doing so. + */ + s->setting_callback_gsi = true; + /* Do not deassert the line if an external device is asserting it. */ + qemu_set_irq(s->callback_gsis[s->callback_gsi], + level || s->extern_gsi_level); + s->setting_callback_gsi = false; + + /* + * If the callback GSI is the only one asserted, ensure the status + * is polled for deassertion in kvm_arch_post_run(). + */ + if (level && !s->extern_gsi_level) { kvm_xen_set_callback_asserted(); } } @@ -1596,7 +1611,7 @@ static int allocate_pirq(XenEvtchnState *s, int type, int gsi) return pirq; } -bool xen_evtchn_set_gsi(int gsi, int level) +bool xen_evtchn_set_gsi(int gsi, int *level) { XenEvtchnState *s = xen_evtchn_singleton; int pirq; @@ -1608,16 +1623,35 @@ bool xen_evtchn_set_gsi(int gsi, int level) } /* - * Check that that it *isn't* the event channel GSI, and thus - * that we are not recursing and it's safe to take s->port_lock. - * - * Locking aside, it's perfectly sane to bail out early for that - * special case, as it would make no sense for the event channel - * GSI to be routed back to event channels, when the delivery - * method is to raise the GSI... that recursion wouldn't *just* - * be a locking issue. + * For the callback_gsi we need to implement a logical OR of the event + * channel GSI and the external input (e.g. from PCI INTx), because + * QEMU itself doesn't support shared level interrupts via demux or + * resamplers. */ if (gsi && gsi == s->callback_gsi) { + /* Remember the external state of the GSI pin (e.g. from PCI INTx) */ + if (!s->setting_callback_gsi) { + s->extern_gsi_level = *level; + + /* + * Don't allow the external device to deassert the line if the + * eveht channel GSI should still be asserted. + */ + if (!s->extern_gsi_level) { + struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0); + if (vi && vi->evtchn_upcall_pending) { + /* Need to poll for deassertion */ + kvm_xen_set_callback_asserted(); + *level = 1; + } + } + } + + /* + * The event channel GSI cannot be routed to PIRQ, as that would make + * no sense. It could also deadlock on s->port_lock, if we proceed. + * So bail out now. + */ return false; } @@ -1628,7 +1662,7 @@ bool xen_evtchn_set_gsi(int gsi, int level) return false; } - if (level) { + if (*level) { int port = s->pirq[pirq].port; s->pirq_gsi_set |= (1U << gsi); diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index b740acfc0d..0521ebc092 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -23,7 +23,7 @@ void xen_evtchn_set_callback_level(int level); int xen_evtchn_set_port(uint16_t port); -bool xen_evtchn_set_gsi(int gsi, int level); +bool xen_evtchn_set_gsi(int gsi, int *level); void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, uint64_t addr, uint32_t data, bool is_masked); void xen_evtchn_remove_pci_device(PCIDevice *dev); diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index a7d46c3105..97b4f7d4a0 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -450,8 +450,27 @@ static long get_file_size(FILE *f) void gsi_handler(void *opaque, int n, int level) { GSIState *s = opaque; + bool bypass_ioapic = false; trace_x86_gsi_interrupt(n, level); + +#ifdef CONFIG_XEN_EMU + /* + * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC + * routing actually works properly under Xen). And then to + * *either* the PIRQ handling or the I/OAPIC depending on whether + * the former wants it. + * + * Additionally, this hook allows the Xen event channel GSI to + * work around QEMU's lack of support for shared level interrupts, + * by keeping track of the externally driven state of the pin and + * implementing a logical OR with the state of the evtchn GSI. + */ + if (xen_mode == XEN_EMULATE) { + bypass_ioapic = xen_evtchn_set_gsi(n, &level); + } +#endif + switch (n) { case 0 ... ISA_NUM_IRQS - 1: if (s->i8259_irq[n]) { @@ -460,18 +479,9 @@ void gsi_handler(void *opaque, int n, int level) } /* fall through */ case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: -#ifdef CONFIG_XEN_EMU - /* - * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC - * routing actually works properly under Xen). And then to - * *either* the PIRQ handling or the I/OAPIC depending on - * whether the former wants it. - */ - if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { - break; + if (!bypass_ioapic) { + qemu_set_irq(s->ioapic_irq[n], level); } -#endif - qemu_set_irq(s->ioapic_irq[n], level); break; case IO_APIC_SECONDARY_IRQBASE ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: From b6014c5089a313ac84fe74970eee56e3fc87b49b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 8 Jan 2025 20:31:46 +0900 Subject: [PATCH 0830/2892] hw/xen: Check if len is 0 before memcpy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit data->data can be NULL when len is 0. Strictly speaking, the behavior of memcpy() in such a scenario is undefined so UBSan complaints. Satisfy UBSan by checking if len is 0 before memcpy(). Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Woodhouse --- hw/i386/kvm/xen_xenstore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 5969105667..17802aa33d 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -532,6 +532,10 @@ static void xs_read(XenXenstoreState *s, unsigned int req_id, return; } + if (!len) { + return; + } + memcpy(&rsp_data[rsp->len], data->data, len); rsp->len += len; } From a7a3784128fa1de275b5eb2406f3f46842fdbd1a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 12 Feb 2024 19:20:29 +0900 Subject: [PATCH 0831/2892] hw/pci: Use -1 as the default value for rombar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_pci_size_rom() distinguishes whether rombar is explicitly set to 1 by checking dev->opts, bypassing the QOM property infrastructure. Use -1 as the default value for rombar to tell if the user explicitly set it to 1. The property is also converted from unsigned to signed. -1 is signed so it is safe to give it a new meaning. The values in [2 ^ 31, 2 ^ 32) become invalid, but nobody should have typed these values by chance. Suggested-by: Markus Armbruster Signed-off-by: Akihiko Odaki Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250104-reuse-v18-13-c349eafd8673@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci/pci.c | 2 +- hw/vfio/pci.c | 5 ++--- include/hw/pci/pci_device.h | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index b6c630c323..78907527f2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -85,7 +85,7 @@ static const Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), - DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), + DEFINE_PROP_INT32("rombar", PCIDevice, rom_bar, -1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present, diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 17080b9dc0..ab17a98ee5 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1012,7 +1012,6 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; - DeviceState *dev = DEVICE(vdev); char *name; int fd = vdev->vbasedev.fd; @@ -1046,12 +1045,12 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (dev->opts && qdict_haskey(dev->opts, "rombar")) { + if (vdev->pdev.rom_bar > 0) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); error_printf("Proceeding anyway since user specified" - " non zero value for rombar\n"); + " positive value for rombar\n"); } else { warn_report("Rom loading for device at %s has been disabled" " due to system instability issues", diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index 8eaf0d58bb..16ea7f4c19 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -148,7 +148,7 @@ struct PCIDevice { uint32_t romsize; bool has_rom; MemoryRegion rom; - uint32_t rom_bar; + int32_t rom_bar; /* INTx routing notifier */ PCIINTxRoutingNotifier intx_routing_notifier; From ad1ea5ffa10d4cf365c142caf627f2c43b3592c2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 2 Dec 2023 17:00:25 +0900 Subject: [PATCH 0832/2892] qdev: Remove opts member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is no longer used. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-ID: <20250104-reuse-v18-14-c349eafd8673@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/qdev.c | 1 - include/hw/qdev-core.h | 4 ---- system/qdev-monitor.c | 12 +++++++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 57c1d9df3a..09c4489e3c 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -691,7 +691,6 @@ static void device_finalize(Object *obj) dev->canonical_path = NULL; } - qobject_unref(dev->opts); g_free(dev->id); } diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index e6ef80b7fd..c4d3dc3906 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -248,10 +248,6 @@ struct DeviceState { * @pending_deleted_expires_ms: optional timeout for deletion events */ int64_t pending_deleted_expires_ms; - /** - * @opts: QDict of options for the device - */ - QDict *opts; /** * @hotplugged: was device added after PHASE_MACHINE_READY? */ diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index c844f53802..6a38b56787 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -631,6 +631,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, char *id; DeviceState *dev = NULL; BusState *bus = NULL; + QDict *properties; driver = qdict_get_try_str(opts, "driver"); if (!driver) { @@ -712,13 +713,14 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } /* set properties */ - dev->opts = qdict_clone_shallow(opts); - qdict_del(dev->opts, "driver"); - qdict_del(dev->opts, "bus"); - qdict_del(dev->opts, "id"); + properties = qdict_clone_shallow(opts); + qdict_del(properties, "driver"); + qdict_del(properties, "bus"); + qdict_del(properties, "id"); - object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, + object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, errp); + qobject_unref(properties); if (*errp) { goto err_del_dev; } From 558ee1ede6cc95d3dde806f0ac323911c5dbb4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Jan 2025 14:50:19 +0100 Subject: [PATCH 0833/2892] qdev: Implement qdev_create_fake_machine() for user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a QDev instance is realized, qdev_get_machine() ends up called. In the next commit, qdev_get_machine() will require a "machine" container to be always present. To satisfy this QOM containers design, Implement qdev_create_fake_machine() which creates a fake "machine" container for user emulation. On system emulation, qemu_create_machine() is called from qemu_init(). For user emulation, since the TCG accelerator always calls tcg_init_machine(), we use it to hook our fake machine creation. Suggested-by: Peter Xu Signed-off-by: Philippe Mathieu-Daudé Acked-by: Peter Xu Reviewed-by: Richard Henderson Message-Id: <20250102211800.79235-2-philmd@linaro.org> --- accel/tcg/tcg-all.c | 8 +++++++- hw/core/meson.build | 1 + hw/core/qdev-user.c | 19 +++++++++++++++++++ include/hw/qdev-core.h | 10 ++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 hw/core/qdev-user.c diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index c256575887..95adaacee8 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -35,7 +35,9 @@ #include "qemu/atomic.h" #include "qapi/qapi-builtin-visit.h" #include "qemu/units.h" -#if !defined(CONFIG_USER_ONLY) +#if defined(CONFIG_USER_ONLY) +#include "hw/qdev-core.h" +#else #include "hw/boards.h" #endif #include "internal-common.h" @@ -124,6 +126,10 @@ static int tcg_init_machine(MachineState *ms) tcg_prologue_init(); #endif +#ifdef CONFIG_USER_ONLY + qdev_create_fake_machine(); +#endif + return 0; } diff --git a/hw/core/meson.build b/hw/core/meson.build index ce9dfa3f4b..65a1698ed1 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -46,3 +46,4 @@ system_ss.add(files( 'vm-change-state-handler.c', 'clock-vmstate.c', )) +user_ss.add(files('qdev-user.c')) diff --git a/hw/core/qdev-user.c b/hw/core/qdev-user.c new file mode 100644 index 0000000000..3d421d8f4e --- /dev/null +++ b/hw/core/qdev-user.c @@ -0,0 +1,19 @@ +/* + * QDev helpers specific to user emulation. + * + * Copyright 2025 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qom/object.h" +#include "hw/qdev-core.h" + +void qdev_create_fake_machine(void) +{ + Object *fake_machine_obj; + + fake_machine_obj = object_property_add_new_container(object_get_root(), + "machine"); + object_property_add_new_container(fake_machine_obj, "unattached"); +} diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index c4d3dc3906..50cbbf8121 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -1023,6 +1023,16 @@ const char *qdev_fw_name(DeviceState *dev); void qdev_assert_realized_properly(void); Object *qdev_get_machine(void); +/** + * qdev_create_fake_machine(): Create a fake machine container. + * + * .. note:: + * This function is a kludge for user emulation (USER_ONLY) + * because when thread (TYPE_CPU) are realized, qdev_realize() + * access a machine container. + */ +void qdev_create_fake_machine(void); + /** * qdev_get_human_name() - Return a human-readable name for a device * @dev: The device. Must be a valid and non-NULL pointer. From 63450f322bf76faab7add3def89815d9198492dc Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 29 Oct 2024 17:16:04 -0400 Subject: [PATCH 0834/2892] qdev: Make qdev_get_machine() not use container_get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, qdev_get_machine() has a slight misuse on container_get(), as the helper says "get a container" but in reality the goal is to get the machine object. It is still a "container" but not strictly. Note that it _may_ get a container (at "/machine") in our current unit test of test-qdev-global-props.c before all these changes, but it's probably unexpected and worked by accident. Switch to an explicit object_resolve_path_component(), with a side benefit that qdev_get_machine() can happen a lot, and we don't need to split the string ("/machine") every time. This also paves way for making the helper container_get() never try to return a non-container at all. Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-9-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/core/qdev.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 09c4489e3c..48bc9a7b9c 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -817,7 +817,12 @@ Object *qdev_get_machine(void) static Object *dev; if (dev == NULL) { - dev = container_get(object_get_root(), "/machine"); + dev = object_resolve_path_component(object_get_root(), "machine"); + /* + * Any call to this function before machine is created is treated + * as a programming error as of now. + */ + assert(dev); } return dev; From 41fc91772841c93c218df78d7e359cb2cd00dff5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:58 -0500 Subject: [PATCH 0835/2892] qdev: Add machine_get_container() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper to fetch machine containers. Add some sanity check around. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Xu Message-ID: <20241121192202.4155849-10-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/core/qdev.c | 11 +++++++++++ include/hw/qdev-core.h | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 48bc9a7b9c..9abc4e8322 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -828,6 +828,17 @@ Object *qdev_get_machine(void) return dev; } +Object *machine_get_container(const char *name) +{ + Object *container, *machine; + + machine = qdev_get_machine(); + container = object_resolve_path_component(machine, name); + assert(object_dynamic_cast(container, TYPE_CONTAINER)); + + return container; +} + char *qdev_get_human_name(DeviceState *dev) { g_assert(dev != NULL); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 50cbbf8121..89575e74e2 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -1033,6 +1033,16 @@ Object *qdev_get_machine(void); */ void qdev_create_fake_machine(void); +/** + * machine_get_container: + * @name: The name of container to lookup + * + * Get a container of the machine (QOM path "/machine/NAME"). + * + * Returns: the machine container object. + */ +Object *machine_get_container(const char *name); + /** * qdev_get_human_name() - Return a human-readable name for a device * @dev: The device. Must be a valid and non-NULL pointer. From 1c34335844950a152c020ec80ce7cf711b1861bc Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:21:59 -0500 Subject: [PATCH 0836/2892] qdev: Use machine_get_container() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use machine_get_container() whenever applicable across the tree. Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-11-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/core/gpio.c | 3 +-- hw/core/qdev.c | 3 +-- hw/core/sysbus.c | 4 ++-- hw/i386/pc.c | 4 ++-- system/ioport.c | 2 +- system/memory.c | 2 +- system/qdev-monitor.c | 6 +++--- system/vl.c | 3 +-- 8 files changed, 12 insertions(+), 15 deletions(-) diff --git a/hw/core/gpio.c b/hw/core/gpio.c index 80d07a6ec9..6e32a8eec6 100644 --- a/hw/core/gpio.c +++ b/hw/core/gpio.c @@ -121,8 +121,7 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, name ? name : "unnamed-gpio-out", n); if (input_pin && !OBJECT(input_pin)->parent) { /* We need a name for object_property_set_link to work */ - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), "non-qdev-gpio[*]", OBJECT(input_pin)); } object_property_set_link(OBJECT(dev), propname, diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 9abc4e8322..82bbdcb654 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -476,8 +476,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) if (!obj->parent) { gchar *name = g_strdup_printf("device[%d]", unattached_count++); - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), name, obj); unattached_parent = true; g_free(name); diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index e64d99c8ed..9355849ff0 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -65,9 +65,9 @@ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) }; /* Loop through all sysbus devices that were spawned outside the machine */ - container = container_get(qdev_get_machine(), "/peripheral"); + container = machine_get_container("peripheral"); find_sysbus_device(container, &find); - container = container_get(qdev_get_machine(), "/peripheral-anon"); + container = machine_get_container("peripheral-anon"); find_sysbus_device(container, &find); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7111876588..9334b033f6 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -463,7 +463,7 @@ static int check_fdc(Object *obj, void *opaque) } static const char * const fdc_container_path[] = { - "/unattached", "/peripheral", "/peripheral-anon" + "unattached", "peripheral", "peripheral-anon" }; /* @@ -477,7 +477,7 @@ static ISADevice *pc_find_fdc0(void) CheckFdcState state = { 0 }; for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) { - container = container_get(qdev_get_machine(), fdc_container_path[i]); + container = machine_get_container(fdc_container_path[i]); object_child_foreach(container, check_fdc, &state); } diff --git a/system/ioport.c b/system/ioport.c index fd551d0375..55c2a75239 100644 --- a/system/ioport.c +++ b/system/ioport.c @@ -258,7 +258,7 @@ static void portio_list_add_1(PortioList *piolist, object_ref(&mrpio->mr); object_unparent(OBJECT(&mrpio->mr)); if (!piolist->owner) { - owner = container_get(qdev_get_machine(), "/unattached"); + owner = machine_get_container("unattached"); } else { owner = piolist->owner; } diff --git a/system/memory.c b/system/memory.c index 78e17e0efa..b17b5538ff 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1238,7 +1238,7 @@ static void memory_region_do_init(MemoryRegion *mr, char *name_array = g_strdup_printf("%s[*]", escaped_name); if (!owner) { - owner = container_get(qdev_get_machine(), "/unattached"); + owner = machine_get_container("unattached"); } object_property_add_child(owner, name_array, OBJECT(mr)); diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 6a38b56787..23043b1e0d 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -348,7 +348,7 @@ static Object *qdev_get_peripheral(void) static Object *dev; if (dev == NULL) { - dev = container_get(qdev_get_machine(), "/peripheral"); + dev = machine_get_container("peripheral"); } return dev; @@ -359,7 +359,7 @@ static Object *qdev_get_peripheral_anon(void) static Object *dev; if (dev == NULL) { - dev = container_get(qdev_get_machine(), "/peripheral-anon"); + dev = machine_get_container("peripheral-anon"); } return dev; @@ -1100,7 +1100,7 @@ static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) static void peripheral_device_del_completion(ReadLineState *rs, const char *str) { - Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); + Object *peripheral = machine_get_container("peripheral"); GSList *list, *item; list = qdev_build_hotpluggable_device_list(peripheral); diff --git a/system/vl.c b/system/vl.c index 3c5bd36d7d..ed1623b26b 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2137,8 +2137,7 @@ static void qemu_create_machine(QDict *qdict) object_property_add_child(object_get_root(), "machine", OBJECT(current_machine)); qemu_create_machine_containers(OBJECT(current_machine)); - object_property_add_child(container_get(OBJECT(current_machine), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), "sysbus", OBJECT(sysbus_get_default())); if (machine_class->minimum_page_bits) { From 180e8f16f0ad6835ce0c437c7ffc9f25801a399e Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:22:00 -0500 Subject: [PATCH 0837/2892] qom: Add object_get_container() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper to fetch a root container (under object_get_root()). Sanity check on the type of the object. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Xu Message-ID: <20241121192202.4155849-12-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- include/qom/object.h | 10 ++++++++++ qom/object.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/qom/object.h b/include/qom/object.h index 95d6e064d9..bcf9910b42 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1510,6 +1510,16 @@ const char *object_property_get_type(Object *obj, const char *name, */ Object *object_get_root(void); +/** + * object_get_container: + * @name: the name of container to lookup + * + * Lookup a root level container. + * + * Returns: the container with @name. + */ +Object *object_get_container(const char *name); + /** * object_get_objects_root: diff --git a/qom/object.c b/qom/object.c index b4c52d055d..81c06906d3 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1751,6 +1751,16 @@ static Object *object_root_initialize(void) return root; } +Object *object_get_container(const char *name) +{ + Object *container; + + container = object_resolve_path_component(object_get_root(), name); + assert(object_dynamic_cast(container, TYPE_CONTAINER)); + + return container; +} + Object *object_get_root(void) { static Object *root; From d3176a9f387f8b6b56882045d36f5b3f82565d90 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:22:01 -0500 Subject: [PATCH 0838/2892] qom: Use object_get_container() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use object_get_container() whenever applicable across the tree. Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-13-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- backends/cryptodev.c | 4 ++-- chardev/char.c | 2 +- qom/object.c | 2 +- scsi/pr-manager.c | 4 ++-- ui/console.c | 2 +- ui/dbus-chardev.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 1157a149d0..1187b08dac 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -97,7 +97,7 @@ static int qmp_query_cryptodev_foreach(Object *obj, void *data) QCryptodevInfoList *qmp_query_cryptodev(Error **errp) { QCryptodevInfoList *list = NULL; - Object *objs = container_get(object_get_root(), "/objects"); + Object *objs = object_get_container("objects"); object_child_foreach(objs, qmp_query_cryptodev_foreach, &list); @@ -557,7 +557,7 @@ static void cryptodev_backend_stats_cb(StatsResultList **result, switch (target) { case STATS_TARGET_CRYPTODEV: { - Object *objs = container_get(object_get_root(), "/objects"); + Object *objs = object_get_container("objects"); StatsArgs stats_args; stats_args.result.stats = result; stats_args.names = names; diff --git a/chardev/char.c b/chardev/char.c index 44ff116fcd..7705da5ad0 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -48,7 +48,7 @@ Object *get_chardevs_root(void) { - return container_get(object_get_root(), "/chardevs"); + return object_get_container("chardevs"); } static void chr_be_event(Chardev *s, QEMUChrEvent event) diff --git a/qom/object.c b/qom/object.c index 81c06906d3..58897a79a7 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1774,7 +1774,7 @@ Object *object_get_root(void) Object *object_get_objects_root(void) { - return container_get(object_get_root(), "/objects"); + return object_get_container("objects"); } Object *object_get_internal_root(void) diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index fb5fc29730..1977d99ce0 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -21,7 +21,7 @@ #include "qemu/module.h" #include "qapi/qapi-commands-block.h" -#define PR_MANAGER_PATH "/objects" +#define PR_MANAGER_PATH "objects" typedef struct PRManagerData { PRManager *pr_mgr; @@ -135,7 +135,7 @@ PRManagerInfoList *qmp_query_pr_managers(Error **errp) { PRManagerInfoList *head = NULL; PRManagerInfoList **prev = &head; - Object *container = container_get(object_get_root(), PR_MANAGER_PATH); + Object *container = object_get_container(PR_MANAGER_PATH); object_child_foreach(container, query_one_pr_manager, &prev); return head; diff --git a/ui/console.c b/ui/console.c index 5165f17125..914ed2cc76 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1160,7 +1160,7 @@ DisplayState *init_displaystate(void) * all QemuConsoles are created and the order / numbering * doesn't change any more */ name = g_strdup_printf("console[%d]", con->index); - object_property_add_child(container_get(object_get_root(), "/backend"), + object_property_add_child(object_get_container("backend"), name, OBJECT(con)); g_free(name); } diff --git a/ui/dbus-chardev.c b/ui/dbus-chardev.c index 1d3a7122a1..bf061cbc93 100644 --- a/ui/dbus-chardev.c +++ b/ui/dbus-chardev.c @@ -106,7 +106,7 @@ dbus_chardev_init(DBusDisplay *dpy) dpy->notifier.notify = dbus_display_on_notify; dbus_display_notifier_add(&dpy->notifier); - object_child_foreach(container_get(object_get_root(), "/chardevs"), + object_child_foreach(object_get_container("chardevs"), dbus_display_chardev_foreach, dpy); } From f6f0284b6fd495c4a0d7d3b91317105d8e1a8bf3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Nov 2024 14:22:02 -0500 Subject: [PATCH 0839/2892] qom: Remove container_get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there's no user of container_get(), remove it. Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241121192202.4155849-14-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- include/qom/object.h | 11 ----------- qom/container.c | 23 ----------------------- 2 files changed, 34 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index bcf9910b42..7793557289 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -2017,17 +2017,6 @@ int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), int object_child_foreach_recursive(Object *obj, int (*fn)(Object *child, void *opaque), void *opaque); -/** - * container_get: - * @root: root of the #path, e.g., object_get_root() - * @path: path to the container - * - * Return a container object whose path is @path. Create more containers - * along the path if necessary. - * - * Returns: the container object. - */ -Object *container_get(Object *root, const char *path); /** * object_property_add_new_container: diff --git a/qom/container.c b/qom/container.c index 20ab74b0e8..38a27ec1ed 100644 --- a/qom/container.c +++ b/qom/container.c @@ -34,27 +34,4 @@ Object *object_property_add_new_container(Object *obj, const char *name) return child; } -Object *container_get(Object *root, const char *path) -{ - Object *obj, *child; - char **parts; - int i; - - parts = g_strsplit(path, "/", 0); - assert(parts != NULL && parts[0] != NULL && !parts[0][0]); - obj = root; - - for (i = 1; parts[i] != NULL; i++, obj = child) { - child = object_resolve_path_component(obj, parts[i]); - if (!child) { - child = object_property_add_new_container(obj, parts[i]); - } - } - - g_strfreev(parts); - - return obj; -} - - type_init(container_register_types) From bc4e7522ad19890eab8cf1df04360abf610b1236 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Jan 2025 12:13:08 +0100 Subject: [PATCH 0840/2892] qom: remove unused InterfaceInfo::concrete_class field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "concrete_class" field of InterfaceClass is only ever written, and as far as I can tell is not particularly useful when debugging either; remove it. Signed-off-by: Paolo Bonzini Reviewed-by: Peter Maydell Message-ID: <20250107111308.21886-1-pbonzini@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/qom/object.h | 5 ++++- qom/object.c | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 7793557289..9192265db7 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -573,12 +573,15 @@ struct InterfaceInfo { * * The class for all interfaces. Subclasses of this class should only add * virtual methods. + * + * Note that most of the fields of ObjectClass are unused (all except + * "type", in fact). They are only present in InterfaceClass to allow + * @object_class_dynamic_cast to work with both regular classes and interfaces. */ struct InterfaceClass { ObjectClass parent_class; /* private: */ - ObjectClass *concrete_class; Type interface_type; }; diff --git a/qom/object.c b/qom/object.c index 58897a79a7..ec447f14a7 100644 --- a/qom/object.c +++ b/qom/object.c @@ -314,7 +314,6 @@ static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, g_free((char *)info.name); new_iface = (InterfaceClass *)iface_impl->class; - new_iface->concrete_class = ti->class; new_iface->interface_type = interface_type; ti->class->interfaces = g_slist_append(ti->class->interfaces, new_iface); From 5f396935f8f1628005ef14a3c4c3dc84c6aa3d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Jan 2025 14:53:15 +0100 Subject: [PATCH 0841/2892] system: Inline machine_containers[] in qemu_create_machine_containers() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only qemu_create_machine_containers() uses the machine_containers[] array, restrict the scope to this single user. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Peter Xu Reviewed-by: Richard Henderson Message-Id: <20250102211800.79235-9-philmd@linaro.org> --- system/vl.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/system/vl.c b/system/vl.c index ed1623b26b..be029c52ef 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2113,18 +2113,16 @@ static void parse_memory_options(void) loc_pop(&loc); } -static const char *const machine_containers[] = { - "unattached", - "peripheral", - "peripheral-anon" -}; - static void qemu_create_machine_containers(Object *machine) { - int i; + static const char *const containers[] = { + "unattached", + "peripheral", + "peripheral-anon", + }; - for (i = 0; i < ARRAY_SIZE(machine_containers); i++) { - object_property_add_new_container(machine, machine_containers[i]); + for (unsigned i = 0; i < ARRAY_SIZE(containers); i++) { + object_property_add_new_container(machine, containers[i]); } } From d127294f265e6a17f8d614f2bef7df8455e81f56 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 3 Dec 2024 12:49:43 +0000 Subject: [PATCH 0842/2892] migration/multifd: Fix compile error caused by page_size usage >From Commit 90fa121c6c07 ("migration/multifd: Inline page_size and page_count") onwards page_size is not part of MutiFD*Params but uses an inline constant instead. However, it missed updating an old usage, causing a compile error. Fixes: 90fa121c6c07 ("migration/multifd: Inline page_size and page_count") Signed-off-by: Shameer Kolothum Reviewed-by: Fabiano Rosas Message-Id: <20241203124943.52572-1-shameerali.kolothum.thodi@huawei.com> Signed-off-by: Fabiano Rosas --- migration/multifd-uadk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index 6e6a290ae9..6895c1f65a 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -169,7 +169,7 @@ static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) .src_len = page_size, .dst = buf, /* Set dst_len to double the src in case compressed out >= page_size */ - .dst_len = p->page_size * 2, + .dst_len = page_size * 2, }; if (uadk_data->handle) { From 1d457daf868191dc1c0b58dc7280799964f40334 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:49 -0500 Subject: [PATCH 0843/2892] migration/multifd: Further remove the SYNC on complete Commit 637280aeb2 ("migration/multifd: Avoid the final FLUSH in complete()") stopped sending the RAM_SAVE_FLAG_MULTIFD_FLUSH flag at ram_save_complete(), because the sync on the destination side is not needed due to the last iteration of find_dirty_block() having already done it. However, that commit overlooked that multifd_ram_flush_and_sync() on the source side is also not needed at ram_save_complete(), for the same reason. Moreover, removing the RAM_SAVE_FLAG_MULTIFD_FLUSH but keeping the multifd_ram_flush_and_sync() means that currently the recv threads will hang when receiving the MULTIFD_FLAG_SYNC message, waiting for the destination sync which only happens when RAM_SAVE_FLAG_MULTIFD_FLUSH is received. Luckily, multifd is still all working fine because recv side cleanup code (mostly multifd_recv_sync_main()) is smart enough to make sure even if recv threads are stuck at SYNC it'll get kicked out. And since this is the completion phase of migration, nothing else will be sent after the SYNCs. This needs to be fixed because in the future VFIO will have data to push after ram_save_complete() and we don't want the recv thread to be stuck in the MULTIFD_FLAG_SYNC message. Remove the unnecessary (and buggy) invocation of multifd_ram_flush_and_sync(). For very old binaries (multifd_flush_after_each_section==true), the flush_and_sync is still needed because each EOS received on destination will enforce all-channel sync once. Stable branches do not need this patch, as no real bug I can think of that will go wrong there.. so not attaching Fixes to be clear on the backport not needed. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Message-Id: <20241206224755.1108686-2-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index a60666d3f6..f0ddd5eabe 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3283,9 +3283,16 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } } - ret = multifd_ram_flush_and_sync(); - if (ret < 0) { - return ret; + if (migrate_multifd() && + migrate_multifd_flush_after_each_section()) { + /* + * Only the old dest QEMU will need this sync, because each EOS + * will require one SYNC message on each channel. + */ + ret = multifd_ram_flush_and_sync(); + if (ret < 0) { + return ret; + } } if (migrate_mapped_ram()) { From 10801e08ac926a5a6083a9bd2ff87b153ccb95b1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:50 -0500 Subject: [PATCH 0844/2892] migration/multifd: Allow to sync with sender threads only Teach multifd_send_sync_main() to sync with threads only. We already have such requests, which is when mapped-ram is enabled with multifd. In that case, no SYNC messages will be pushed to the stream when multifd syncs the sender threads because there's no destination threads waiting for that. The whole point of the sync is to make sure all threads finished their jobs. So fundamentally we have a request to do the sync in different ways: - Either to sync the threads only, - Or to sync the threads but also with the destination side. Mapped-ram did it already because of the use_packet check in the sync handler of the sender thread. It works. However it may stop working when e.g. VFIO may start to reuse multifd channels to push device states. In that case VFIO has similar request on "thread-only sync" however we can't check a flag because such sync request can still come from RAM which needs the on-wire notifications. Paving way for that by allowing the multifd_send_sync_main() to specify what kind of sync the caller needs. We can use it for mapped-ram already. No functional change intended. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206224755.1108686-3-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/multifd-nocomp.c | 7 ++++++- migration/multifd.c | 17 +++++++++++------ migration/multifd.h | 23 ++++++++++++++++++++--- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index 55191152f9..219f9e58ef 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -345,6 +345,8 @@ retry: int multifd_ram_flush_and_sync(void) { + MultiFDSyncReq req; + if (!migrate_multifd()) { return 0; } @@ -356,7 +358,10 @@ int multifd_ram_flush_and_sync(void) } } - return multifd_send_sync_main(); + /* File migrations only need to sync with threads */ + req = migrate_mapped_ram() ? MULTIFD_SYNC_LOCAL : MULTIFD_SYNC_ALL; + + return multifd_send_sync_main(req); } bool multifd_send_prepare_common(MultiFDSendParams *p) diff --git a/migration/multifd.c b/migration/multifd.c index 4f973d70e0..64e0ac2488 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -523,11 +523,13 @@ static int multifd_zero_copy_flush(QIOChannel *c) return ret; } -int multifd_send_sync_main(void) +int multifd_send_sync_main(MultiFDSyncReq req) { int i; bool flush_zero_copy; + assert(req != MULTIFD_SYNC_NONE); + flush_zero_copy = migrate_zero_copy_send(); for (i = 0; i < migrate_multifd_channels(); i++) { @@ -543,8 +545,8 @@ int multifd_send_sync_main(void) * We should be the only user so far, so not possible to be set by * others concurrently. */ - assert(qatomic_read(&p->pending_sync) == false); - qatomic_set(&p->pending_sync, true); + assert(qatomic_read(&p->pending_sync) == MULTIFD_SYNC_NONE); + qatomic_set(&p->pending_sync, req); qemu_sem_post(&p->sem); } for (i = 0; i < migrate_multifd_channels(); i++) { @@ -635,14 +637,17 @@ static void *multifd_send_thread(void *opaque) */ qatomic_store_release(&p->pending_job, false); } else { + MultiFDSyncReq req = qatomic_read(&p->pending_sync); + /* * If not a normal job, must be a sync request. Note that * pending_sync is a standalone flag (unlike pending_job), so * it doesn't require explicit memory barriers. */ - assert(qatomic_read(&p->pending_sync)); + assert(req != MULTIFD_SYNC_NONE); - if (use_packets) { + /* Only push the SYNC message if it involves a remote sync */ + if (req == MULTIFD_SYNC_ALL) { p->flags = MULTIFD_FLAG_SYNC; multifd_send_fill_packet(p); ret = qio_channel_write_all(p->c, (void *)p->packet, @@ -654,7 +659,7 @@ static void *multifd_send_thread(void *opaque) stat64_add(&mig_stats.multifd_bytes, p->packet_len); } - qatomic_set(&p->pending_sync, false); + qatomic_set(&p->pending_sync, MULTIFD_SYNC_NONE); qemu_sem_post(&p->sem_sync); } } diff --git a/migration/multifd.h b/migration/multifd.h index 50d58c0c9c..6493512305 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -19,6 +19,22 @@ typedef struct MultiFDRecvData MultiFDRecvData; typedef struct MultiFDSendData MultiFDSendData; +typedef enum { + /* No sync request */ + MULTIFD_SYNC_NONE = 0, + /* Sync locally on the sender threads without pushing messages */ + MULTIFD_SYNC_LOCAL, + /* + * Sync not only on the sender threads, but also push MULTIFD_FLAG_SYNC + * message to the wire for each iochannel (which is for a remote sync). + * + * When remote sync is used, need to be paired with a follow up + * RAM_SAVE_FLAG_EOS / RAM_SAVE_FLAG_MULTIFD_FLUSH message on the main + * channel. + */ + MULTIFD_SYNC_ALL, +} MultiFDSyncReq; + bool multifd_send_setup(void); void multifd_send_shutdown(void); void multifd_send_channel_created(void); @@ -28,7 +44,7 @@ void multifd_recv_shutdown(void); bool multifd_recv_all_channels_created(void); void multifd_recv_new_channel(QIOChannel *ioc, Error **errp); void multifd_recv_sync_main(void); -int multifd_send_sync_main(void); +int multifd_send_sync_main(MultiFDSyncReq req); bool multifd_queue_page(RAMBlock *block, ram_addr_t offset); bool multifd_recv(void); MultiFDRecvData *multifd_get_recv_data(void); @@ -143,7 +159,7 @@ typedef struct { /* multifd flags for each packet */ uint32_t flags; /* - * The sender thread has work to do if either of below boolean is set. + * The sender thread has work to do if either of below field is set. * * @pending_job: a job is pending * @pending_sync: a sync request is pending @@ -152,7 +168,8 @@ typedef struct { * cleared by the multifd sender threads. */ bool pending_job; - bool pending_sync; + MultiFDSyncReq pending_sync; + MultiFDSendData *data; /* thread local variables. No locking required */ From 604b4749c58f676aa37bd4d96496152f36f3b293 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:51 -0500 Subject: [PATCH 0845/2892] migration/ram: Move RAM_SAVE_FLAG* into ram.h Firstly, we're going to use the multifd flag soon in multifd code, so ram.c isn't gonna work. Secondly, we have a separate RDMA flag dangling around, which is definitely not obvious. There's one comment that helps, but not too much. Put all RAM save flags altogether, so nothing will get overlooked. Add a section explain why we can't use bits over 0x200. Remove RAM_SAVE_FLAG_FULL as it's already not used in QEMU, as the comment explained. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Message-Id: <20241206224755.1108686-4-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 21 --------------------- migration/ram.h | 28 ++++++++++++++++++++++++++++ migration/rdma.h | 7 ------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index f0ddd5eabe..01521de71f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -71,27 +71,6 @@ /***********************************************************/ /* ram save/restore */ -/* - * RAM_SAVE_FLAG_ZERO used to be named RAM_SAVE_FLAG_COMPRESS, it - * worked for pages that were filled with the same char. We switched - * it to only search for the zero value. And to avoid confusion with - * RAM_SAVE_FLAG_COMPRESS_PAGE just rename it. - * - * RAM_SAVE_FLAG_FULL was obsoleted in 2009. - * - * RAM_SAVE_FLAG_COMPRESS_PAGE (0x100) was removed in QEMU 9.1. - */ -#define RAM_SAVE_FLAG_FULL 0x01 -#define RAM_SAVE_FLAG_ZERO 0x02 -#define RAM_SAVE_FLAG_MEM_SIZE 0x04 -#define RAM_SAVE_FLAG_PAGE 0x08 -#define RAM_SAVE_FLAG_EOS 0x10 -#define RAM_SAVE_FLAG_CONTINUE 0x20 -#define RAM_SAVE_FLAG_XBZRLE 0x40 -/* 0x80 is reserved in rdma.h for RAM_SAVE_FLAG_HOOK */ -#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 -/* We can't use any flag that is bigger than 0x200 */ - /* * mapped-ram migration supports O_DIRECT, so we need to make sure the * userspace buffer, the IO operation size and the file offset are diff --git a/migration/ram.h b/migration/ram.h index 0d1981f888..921c39a2c5 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -33,6 +33,34 @@ #include "exec/cpu-common.h" #include "io/channel.h" +/* + * RAM_SAVE_FLAG_ZERO used to be named RAM_SAVE_FLAG_COMPRESS, it + * worked for pages that were filled with the same char. We switched + * it to only search for the zero value. And to avoid confusion with + * RAM_SAVE_FLAG_COMPRESS_PAGE just rename it. + * + * RAM_SAVE_FLAG_FULL (0x01) was obsoleted in 2009. + * + * RAM_SAVE_FLAG_COMPRESS_PAGE (0x100) was removed in QEMU 9.1. + * + * RAM_SAVE_FLAG_HOOK is only used in RDMA. Whenever this is found in the + * data stream, the flags will be passed to rdma functions in the + * incoming-migration side. + * + * We can't use any flag that is bigger than 0x200, because the flags are + * always assumed to be encoded in a ramblock address offset, which is + * multiple of PAGE_SIZE. Here it means QEMU supports migration with any + * architecture that has PAGE_SIZE>=1K (0x400). + */ +#define RAM_SAVE_FLAG_ZERO 0x002 +#define RAM_SAVE_FLAG_MEM_SIZE 0x004 +#define RAM_SAVE_FLAG_PAGE 0x008 +#define RAM_SAVE_FLAG_EOS 0x010 +#define RAM_SAVE_FLAG_CONTINUE 0x020 +#define RAM_SAVE_FLAG_XBZRLE 0x040 +#define RAM_SAVE_FLAG_HOOK 0x080 +#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 + extern XBZRLECacheStats xbzrle_counters; /* Should be holding either ram_list.mutex, or the RCU lock. */ diff --git a/migration/rdma.h b/migration/rdma.h index a8d27f33b8..f55f28bbed 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -33,13 +33,6 @@ void rdma_start_incoming_migration(InetSocketAddress *host_port, Error **errp); #define RAM_CONTROL_ROUND 1 #define RAM_CONTROL_FINISH 3 -/* - * Whenever this is found in the data stream, the flags - * will be passed to rdma functions in the incoming-migration - * side. - */ -#define RAM_SAVE_FLAG_HOOK 0x80 - #define RAM_SAVE_CONTROL_NOT_SUPP -1000 #define RAM_SAVE_CONTROL_DELAYED -2000 From e5f14aa5fe7f44649f5413558cac81c09d6c7f93 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:52 -0500 Subject: [PATCH 0846/2892] migration/multifd: Unify RAM_SAVE_FLAG_MULTIFD_FLUSH messages RAM_SAVE_FLAG_MULTIFD_FLUSH message should always be correlated to a sync request on src. Unify such message into one place, and conditionally send the message only if necessary. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Message-Id: <20241206224755.1108686-5-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/multifd-nocomp.c | 27 +++++++++++++++++++++++++-- migration/multifd.h | 2 +- migration/ram.c | 18 ++++-------------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index 219f9e58ef..58372db0f4 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -20,6 +20,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "trace.h" +#include "qemu-file.h" static MultiFDSendData *multifd_ram_send; @@ -343,9 +344,10 @@ retry: return true; } -int multifd_ram_flush_and_sync(void) +int multifd_ram_flush_and_sync(QEMUFile *f) { MultiFDSyncReq req; + int ret; if (!migrate_multifd()) { return 0; @@ -361,7 +363,28 @@ int multifd_ram_flush_and_sync(void) /* File migrations only need to sync with threads */ req = migrate_mapped_ram() ? MULTIFD_SYNC_LOCAL : MULTIFD_SYNC_ALL; - return multifd_send_sync_main(req); + ret = multifd_send_sync_main(req); + if (ret) { + return ret; + } + + /* If we don't need to sync with remote at all, nothing else to do */ + if (req == MULTIFD_SYNC_LOCAL) { + return 0; + } + + /* + * Old QEMUs don't understand RAM_SAVE_FLAG_MULTIFD_FLUSH, it relies + * on RAM_SAVE_FLAG_EOS instead. + */ + if (migrate_multifd_flush_after_each_section()) { + return 0; + } + + qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + qemu_fflush(f); + + return 0; } bool multifd_send_prepare_common(MultiFDSendParams *p) diff --git a/migration/multifd.h b/migration/multifd.h index 6493512305..0fef431f6b 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -354,7 +354,7 @@ static inline uint32_t multifd_ram_page_count(void) void multifd_ram_save_setup(void); void multifd_ram_save_cleanup(void); -int multifd_ram_flush_and_sync(void); +int multifd_ram_flush_and_sync(QEMUFile *f); size_t multifd_ram_payload_size(void); void multifd_ram_fill_packet(MultiFDSendParams *p); int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); diff --git a/migration/ram.c b/migration/ram.c index 01521de71f..ef683d11f0 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1306,15 +1306,10 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) (!migrate_multifd_flush_after_each_section() || migrate_mapped_ram())) { QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel; - int ret = multifd_ram_flush_and_sync(); + int ret = multifd_ram_flush_and_sync(f); if (ret < 0) { return ret; } - - if (!migrate_mapped_ram()) { - qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); - qemu_fflush(f); - } } /* Hit the end of the list */ @@ -3044,18 +3039,13 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) } bql_unlock(); - ret = multifd_ram_flush_and_sync(); + ret = multifd_ram_flush_and_sync(f); bql_lock(); if (ret < 0) { error_setg(errp, "%s: multifd synchronization failed", __func__); return ret; } - if (migrate_multifd() && !migrate_multifd_flush_after_each_section() - && !migrate_mapped_ram()) { - qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); - } - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); ret = qemu_fflush(f); if (ret < 0) { @@ -3190,7 +3180,7 @@ out: if (ret >= 0 && migration_is_running()) { if (migrate_multifd() && migrate_multifd_flush_after_each_section() && !migrate_mapped_ram()) { - ret = multifd_ram_flush_and_sync(); + ret = multifd_ram_flush_and_sync(f); if (ret < 0) { return ret; } @@ -3268,7 +3258,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) * Only the old dest QEMU will need this sync, because each EOS * will require one SYNC message on each channel. */ - ret = multifd_ram_flush_and_sync(); + ret = multifd_ram_flush_and_sync(f); if (ret < 0) { return ret; } From de695b1399242da0c618049932a9a6f1a0a0a4f1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:53 -0500 Subject: [PATCH 0847/2892] migration/multifd: Remove sync processing on postcopy Multifd never worked with postcopy, at least yet so far. Remove the sync processing there, because it's confusing, and they should never appear. Now if RAM_SAVE_FLAG_MULTIFD_FLUSH is observed, we fail hard instead of trying to invoke multifd code. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Message-Id: <20241206224755.1108686-6-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index ef683d11f0..9eeb77665b 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3772,15 +3772,7 @@ int ram_load_postcopy(QEMUFile *f, int channel) TARGET_PAGE_SIZE); } break; - case RAM_SAVE_FLAG_MULTIFD_FLUSH: - multifd_recv_sync_main(); - break; case RAM_SAVE_FLAG_EOS: - /* normal exit */ - if (migrate_multifd() && - migrate_multifd_flush_after_each_section()) { - multifd_recv_sync_main(); - } break; default: error_report("Unknown combination of migration flags: 0x%x" From 1aa81c3098f0270905deff516d455604fcbfaab5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:54 -0500 Subject: [PATCH 0848/2892] migration/multifd: Cleanup src flushes on condition check The src flush condition check is over complicated, and it's getting more out of control if postcopy will be involved. In general, we have two modes to do the sync: legacy or modern ways. Legacy uses per-section flush, modern uses per-round flush. Mapped-ram always uses the modern, which is per-round. Introduce two helpers, which can greatly simplify the code, and hopefully make it readable again. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206224755.1108686-7-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/multifd-nocomp.c | 42 ++++++++++++++++++++++++++++++++++++++ migration/multifd.h | 2 ++ migration/ram.c | 10 +++------ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index 58372db0f4..c1f686c0ce 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -344,6 +344,48 @@ retry: return true; } +/* + * We have two modes for multifd flushes: + * + * - Per-section mode: this is the legacy way to flush, it requires one + * MULTIFD_FLAG_SYNC message for each RAM_SAVE_FLAG_EOS. + * + * - Per-round mode: this is the modern way to flush, it requires one + * MULTIFD_FLAG_SYNC message only for each round of RAM scan. Normally + * it's paired with a new RAM_SAVE_FLAG_MULTIFD_FLUSH message in network + * based migrations. + * + * One thing to mention is mapped-ram always use the modern way to sync. + */ + +/* Do we need a per-section multifd flush (legacy way)? */ +bool multifd_ram_sync_per_section(void) +{ + if (!migrate_multifd()) { + return false; + } + + if (migrate_mapped_ram()) { + return false; + } + + return migrate_multifd_flush_after_each_section(); +} + +/* Do we need a per-round multifd flush (modern way)? */ +bool multifd_ram_sync_per_round(void) +{ + if (!migrate_multifd()) { + return false; + } + + if (migrate_mapped_ram()) { + return true; + } + + return !migrate_multifd_flush_after_each_section(); +} + int multifd_ram_flush_and_sync(QEMUFile *f) { MultiFDSyncReq req; diff --git a/migration/multifd.h b/migration/multifd.h index 0fef431f6b..bd785b9873 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -355,6 +355,8 @@ static inline uint32_t multifd_ram_page_count(void) void multifd_ram_save_setup(void); void multifd_ram_save_cleanup(void); int multifd_ram_flush_and_sync(QEMUFile *f); +bool multifd_ram_sync_per_round(void); +bool multifd_ram_sync_per_section(void); size_t multifd_ram_payload_size(void); void multifd_ram_fill_packet(MultiFDSendParams *p); int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); diff --git a/migration/ram.c b/migration/ram.c index 9eeb77665b..d9336d8a09 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1302,9 +1302,7 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); if (!pss->block) { - if (migrate_multifd() && - (!migrate_multifd_flush_after_each_section() || - migrate_mapped_ram())) { + if (multifd_ram_sync_per_round()) { QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel; int ret = multifd_ram_flush_and_sync(f); if (ret < 0) { @@ -3178,8 +3176,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) out: if (ret >= 0 && migration_is_running()) { - if (migrate_multifd() && migrate_multifd_flush_after_each_section() && - !migrate_mapped_ram()) { + if (multifd_ram_sync_per_section()) { ret = multifd_ram_flush_and_sync(f); if (ret < 0) { return ret; @@ -3252,8 +3249,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } } - if (migrate_multifd() && - migrate_multifd_flush_after_each_section()) { + if (multifd_ram_sync_per_section()) { /* * Only the old dest QEMU will need this sync, because each EOS * will require one SYNC message on each channel. From baab4473dba2b85adf3c0622b92bc209f7a8dec0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 17:47:55 -0500 Subject: [PATCH 0849/2892] migration/multifd: Document the reason to sync for save_setup() It's not straightforward to see why src QEMU needs to sync multifd during setup() phase. After all, there's no page queued at that point. For old QEMUs, there's a solid reason: EOS requires it to work. While it's clueless on the new QEMUs which do not take EOS message as sync requests. One will figure that out only when this is conditionally removed. In fact, the author did try it out. Logically we could still avoid doing this on new machine types, however that needs a separate compat field and that can be an overkill in some trivial overhead in setup() phase. Let's instead document it completely, to avoid someone else tries this again and do the debug one more time, or anyone confused on why this ever existed. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206224755.1108686-8-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index d9336d8a09..ce28328141 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3036,6 +3036,31 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) migration_ops->ram_save_target_page = ram_save_target_page_legacy; } + /* + * This operation is unfortunate.. + * + * For legacy QEMUs using per-section sync + * ======================================= + * + * This must exist because the EOS below requires the SYNC messages + * per-channel to work. + * + * For modern QEMUs using per-round sync + * ===================================== + * + * Logically such sync is not needed, and recv threads should not run + * until setup ready (using things like channels_ready on src). Then + * we should be all fine. + * + * However even if we add channels_ready to recv side in new QEMUs, old + * QEMU won't have them so this sync will still be needed to make sure + * multifd recv threads won't start processing guest pages early before + * ram_load_setup() is properly done. + * + * Let's stick with this. Fortunately the overhead is low to sync + * during setup because the VM is running, so at least it's not + * accounted as part of downtime. + */ bql_unlock(); ret = multifd_ram_flush_and_sync(f); bql_lock(); From b93d897ea2f0abbe7fc341a9ac176b5ecd0f3c93 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 13 Dec 2024 13:01:19 -0300 Subject: [PATCH 0850/2892] migration/multifd: Fix compat with QEMU < 9.0 Commit f5f48a7891 ("migration/multifd: Separate SYNC request with normal jobs") changed the multifd source side to stop sending data along with the MULTIFD_FLAG_SYNC, effectively introducing the concept of a SYNC-only packet. Relying on that, commit d7e58f412c ("migration/multifd: Don't send ram data during SYNC") later came along and skipped reading data from SYNC packets. In a versions timeline like this: 8.2 f5f48a7 9.0 9.1 d7e58f41 9.2 The issue arises that QEMUs < 9.0 still send data along with SYNC, but QEMUs > 9.1 don't gather that data anymore. This leads to various kinds of migration failures due to desync/missing data. Stop checking for a SYNC packet on the destination and unconditionally unfill the packet. >From now on: old -> new: the source sends data + sync, destination reads normally new -> new: source sends only sync, destination reads zeros new -> old: source sends only sync, destination reads zeros CC: qemu-stable@nongnu.org Fixes: d7e58f412c ("migration/multifd: Don't send ram data during SYNC") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2720 Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Message-Id: <20241213160120.23880-2-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/multifd.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 64e0ac2488..ab73d6d984 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -252,9 +252,8 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) p->packet_num = be64_to_cpu(packet->packet_num); p->packets_recved++; - if (!(p->flags & MULTIFD_FLAG_SYNC)) { - ret = multifd_ram_unfill_packet(p, errp); - } + /* Always unfill, old QEMUs (<9.0) send data along with SYNC */ + ret = multifd_ram_unfill_packet(p, errp); trace_multifd_recv_unfill(p->id, p->packet_num, p->flags, p->next_packet_size); @@ -1156,9 +1155,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; - if (!(flags & MULTIFD_FLAG_SYNC)) { - has_data = p->normal_num || p->zero_num; - } + + /* + * Even if it's a SYNC packet, this needs to be set + * because older QEMUs (<9.0) still send data along with + * the SYNC packet. + */ + has_data = p->normal_num || p->zero_num; qemu_mutex_unlock(&p->mutex); } else { /* From 7815f69867da92335055d4b5248430b0f122ce4e Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 18:08:33 -0500 Subject: [PATCH 0851/2892] migration: Add helper to get target runstate In 99% cases, after QEMU migrates to dest host, it tries to detect the target VM runstate using global_state_get_runstate(). There's one outlier so far which is Xen that won't send global state. That's the major reason why global_state_received() check was always there together with global_state_get_runstate(). However it's utterly confusing why global_state_received() has anything to do with "let's start VM or not". Provide a helper to explain it, then we have an unified entry for getting the target dest QEMU runstate after migration. Suggested-by: Fabiano Rosas Signed-off-by: Peter Xu Message-Id: <20241206230838.1111496-2-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/migration.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index df61ca4e93..969b03cdcd 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -135,6 +135,21 @@ static bool migration_needs_multiple_sockets(void) return migrate_multifd() || migrate_postcopy_preempt(); } +static RunState migration_get_target_runstate(void) +{ + /* + * When the global state is not migrated, it means we don't know the + * runstate of the src QEMU. We don't have much choice but assuming + * the VM is running. NOTE: this is pretty rare case, so far only Xen + * uses it. + */ + if (!global_state_received()) { + return RUN_STATE_RUNNING; + } + + return global_state_get_runstate(); +} + static bool transport_supports_multi_channels(MigrationAddress *addr) { if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { @@ -735,8 +750,7 @@ static void process_incoming_migration_bh(void *opaque) * unless we really are starting the VM. */ if (!migrate_late_block_activate() || - (autostart && (!global_state_received() || - runstate_is_live(global_state_get_runstate())))) { + (autostart && runstate_is_live(migration_get_target_runstate()))) { /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ bdrv_activate_all(&local_err); @@ -759,8 +773,7 @@ static void process_incoming_migration_bh(void *opaque) dirty_bitmap_mig_before_vm_start(); - if (!global_state_received() || - runstate_is_live(global_state_get_runstate())) { + if (runstate_is_live(migration_get_target_runstate())) { if (autostart) { vm_start(); } else { From e4e5e89bbd8e731e86735d9d25b7b5f49e8f08b6 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 18:08:34 -0500 Subject: [PATCH 0852/2892] qmp/cont: Only activate disks if migration completed As the comment says, the activation of disks is for the case where migration has completed, rather than when QEMU is still during migration (RUN_STATE_INMIGRATE). Move the code over to reflect what the comment is describing. Cc: Kevin Wolf Cc: Markus Armbruster Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206230838.1111496-3-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- monitor/qmp-cmds.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 34f215097c..415e645130 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -96,21 +96,23 @@ void qmp_cont(Error **errp) } } - /* Continuing after completed migration. Images have been inactivated to - * allow the destination to take control. Need to get control back now. - * - * If there are no inactive block nodes (e.g. because the VM was just - * paused rather than completing a migration), bdrv_inactivate_all() simply - * doesn't do anything. */ - bdrv_activate_all(&local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (runstate_check(RUN_STATE_INMIGRATE)) { autostart = 1; } else { + /* + * Continuing after completed migration. Images have been + * inactivated to allow the destination to take control. Need to + * get control back now. + * + * If there are no inactive block nodes (e.g. because the VM was + * just paused rather than completing a migration), + * bdrv_inactivate_all() simply doesn't do anything. + */ + bdrv_activate_all(&local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } vm_start(); } } From fca9aef1c8d8fc4482cc541638dbfac76dc125d6 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 18:08:35 -0500 Subject: [PATCH 0853/2892] migration/block: Make late-block-active the default Migration capability 'late-block-active' controls when the block drives will be activated. If enabled, block drives will only be activated until VM starts, either src runstate was "live" (RUNNING, or SUSPENDED), or it'll be postponed until qmp_cont(). Let's do this unconditionally. There's no harm to delay activation of block drives. Meanwhile there's no ABI breakage if dest does it, because src QEMU has nothing to do with it, so it's no concern on ABI breakage. IIUC we could avoid introducing this cap when introducing it before, but now it's still not too late to just always do it. Cap now prone to removal, but it'll be for later patches. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206230838.1111496-4-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/migration.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 969b03cdcd..c80fc7b94c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -743,24 +743,6 @@ static void process_incoming_migration_bh(void *opaque) trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter"); - /* If capability late_block_activate is set: - * Only fire up the block code now if we're going to restart the - * VM, else 'cont' will do it. - * This causes file locking to happen; so we don't want it to happen - * unless we really are starting the VM. - */ - if (!migrate_late_block_activate() || - (autostart && runstate_is_live(migration_get_target_runstate()))) { - /* Make sure all file formats throw away their mutable metadata. - * If we get an error here, just don't restart the VM yet. */ - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; - } - } - /* * This must happen after all error conditions are dealt with and * we're sure the VM is going to be running on this host. @@ -775,7 +757,25 @@ static void process_incoming_migration_bh(void *opaque) if (runstate_is_live(migration_get_target_runstate())) { if (autostart) { - vm_start(); + /* + * Block activation is always delayed until VM starts, either + * here (which means we need to start the dest VM right now..), + * or until qmp_cont() later. + * + * We used to have cap 'late-block-activate' but now we do this + * unconditionally, as it has no harm but only benefit. E.g., + * it's not part of migration ABI on the time of disk activation. + * + * Make sure all file formats throw away their mutable + * metadata. If error, don't restart the VM yet. + */ + bdrv_activate_all(&local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + } else { + vm_start(); + } } else { runstate_set(RUN_STATE_PAUSED); } From 61f2b489987c51159c53101a072c6aa901b50506 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 18:08:36 -0500 Subject: [PATCH 0854/2892] migration/block: Apply late-block-active behavior to postcopy Postcopy never cared about late-block-active. However there's no mention in the capability that it doesn't apply to postcopy. Considering that we _assumed_ late activation is always good, do that too for postcopy unconditionally, just like precopy. After this patch, we should have unified the behavior across all. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206230838.1111496-5-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/savevm.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 927b1146c0..d4842b519d 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2137,22 +2137,21 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) trace_vmstate_downtime_checkpoint("dst-postcopy-bh-announced"); - /* Make sure all file formats throw away their mutable metadata. - * If we get an error here, just don't restart the VM yet. */ - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; - } - - trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); - dirty_bitmap_mig_before_vm_start(); if (autostart) { - /* Hold onto your hats, starting the CPU */ - vm_start(); + /* + * Make sure all file formats throw away their mutable metadata. + * If we get an error here, just don't restart the VM yet. + */ + bdrv_activate_all(&local_err); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + } else { + vm_start(); + } } else { /* leave it paused and let management decide when to start the CPU */ runstate_set(RUN_STATE_PAUSED); From 8c97c5a476d146b35b2873ef73df601216a494d9 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 18:08:37 -0500 Subject: [PATCH 0855/2892] migration/block: Fix possible race with block_inactive Src QEMU sets block_inactive=true very early before the invalidation takes place. It means if something wrong happened during setting the flag but before reaching qemu_savevm_state_complete_precopy_non_iterable() where it did the invalidation work, it'll make block_inactive flag inconsistent. For example, think about when qemu_savevm_state_complete_precopy_iterable() can fail: it will have block_inactive set to true even if all block drives are active. Fix that by only update the flag after the invalidation is done. No Fixes for any commit, because it's not an issue if bdrv_activate_all() is re-entrant upon all-active disks - false positive block_inactive can bring nothing more than "trying to active the blocks but they're already active". However let's still do it right to avoid the inconsistent flag v.s. reality. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206230838.1111496-6-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/migration.c | 9 +++------ migration/savevm.c | 2 ++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index c80fc7b94c..fd42a549e6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2742,14 +2742,11 @@ static int migration_completion_precopy(MigrationState *s, goto out_unlock; } - /* - * Inactivate disks except in COLO, and track that we have done so in order - * to remember to reactivate them if migration fails or is cancelled. - */ - s->block_inactive = !migrate_colo(); migration_rate_set(RATE_LIMIT_DISABLED); + + /* Inactivate disks except in COLO */ ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false, - s->block_inactive); + !migrate_colo()); out_unlock: bql_unlock(); return ret; diff --git a/migration/savevm.c b/migration/savevm.c index d4842b519d..3a414aff52 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1558,6 +1558,8 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, qemu_file_set_error(f, ret); return ret; } + /* Remember that we did this */ + s->block_inactive = true; } if (!in_postcopy) { /* Postcopy stream will still be going */ From 8597af76153a87068b675d8099063c3ad8695773 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 6 Dec 2024 18:08:38 -0500 Subject: [PATCH 0856/2892] migration/block: Rewrite disk activation This patch proposes a flag to maintain disk activation status globally. It mostly rewrites disk activation mgmt for QEMU, including COLO and QMP command xen_save_devices_state. Backgrounds =========== We have two problems on disk activations, one resolved, one not. Problem 1: disk activation recover (for switchover interruptions) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When migration is either cancelled or failed during switchover, especially when after the disks are inactivated, QEMU needs to remember re-activate the disks again before vm starts. It used to be done separately in two paths: one in qmp_migrate_cancel(), the other one in the failure path of migration_completion(). It used to be fixed in different commits, all over the places in QEMU. So these are the relevant changes I saw, I'm not sure if it's complete list: - In 2016, commit fe904ea824 ("migration: regain control of images when migration fails to complete") - In 2017, commit 1d2acc3162 ("migration: re-active images while migration been canceled after inactive them") - In 2023, commit 6dab4c93ec ("migration: Attempt disk reactivation in more failure scenarios") Now since we have a slightly better picture maybe we can unify the reactivation in a single path. One side benefit of doing so is, we can move the disk operation outside QMP command "migrate_cancel". It's possible that in the future we may want to make "migrate_cancel" be OOB-compatible, while that requires the command doesn't need BQL in the first place. This will already do that and make migrate_cancel command lightweight. Problem 2: disk invalidation on top of invalidated disks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is an unresolved bug for current QEMU. Link in "Resolves:" at the end. It turns out besides the src switchover phase (problem 1 above), QEMU also needs to remember block activation on destination. Consider two continuous migration in a row, where the VM was always paused. In that scenario, the disks are not activated even until migration completed in the 1st round. When the 2nd round starts, if QEMU doesn't know the status of the disks, it needs to try inactivate the disk again. Here the issue is the block layer API bdrv_inactivate_all() will crash a QEMU if invoked on already inactive disks for the 2nd migration. For detail, see the bug link at the end. Implementation ============== This patch proposes to maintain disk activation with a global flag, so we know: - If we used to inactivate disks for migration, but migration got cancelled, or failed, QEMU will know it should reactivate the disks. - On incoming side, if the disks are never activated but then another migration is triggered, QEMU should be able to tell that inactivate is not needed for the 2nd migration. We used to have disk_inactive, but it only solves the 1st issue, not the 2nd. Also, it's done in completely separate paths so it's extremely hard to follow either how the flag changes, or the duration that the flag is valid, and when we will reactivate the disks. Convert the existing disk_inactive flag into that global flag (also invert its naming), and maintain the disk activation status for the whole lifecycle of qemu. That includes the incoming QEMU. Put both of the error cases of source migration (failure, cancelled) together into migration_iteration_finish(), which will be invoked for either of the scenario. So from that part QEMU should behave the same as before. However with such global maintenance on disk activation status, we not only cleanup quite a few temporary paths that we try to maintain the disk activation status (e.g. in postcopy code), meanwhile it fixes the crash for problem 2 in one shot. For freshly started QEMU, the flag is initialized to TRUE showing that the QEMU owns the disks by default. For incoming migrated QEMU, the flag will be initialized to FALSE once and for all showing that the dest QEMU doesn't own the disks until switchover. That is guaranteed by the "once" variable. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2395 Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-Id: <20241206230838.1111496-7-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- include/migration/misc.h | 4 ++ migration/block-active.c | 94 ++++++++++++++++++++++++++++++++++++++++ migration/colo.c | 2 +- migration/meson.build | 1 + migration/migration.c | 80 ++++++++-------------------------- migration/migration.h | 6 +-- migration/savevm.c | 33 ++++++-------- migration/trace-events | 3 ++ monitor/qmp-cmds.c | 8 +--- 9 files changed, 140 insertions(+), 91 deletions(-) create mode 100644 migration/block-active.c diff --git a/include/migration/misc.h b/include/migration/misc.h index c0e23fdac9..67f7ef7a0e 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -104,4 +104,8 @@ bool migration_incoming_postcopy_advised(void); /* True if background snapshot is active */ bool migration_in_bg_snapshot(void); +/* Wrapper for block active/inactive operations */ +bool migration_block_activate(Error **errp); +bool migration_block_inactivate(void); + #endif diff --git a/migration/block-active.c b/migration/block-active.c new file mode 100644 index 0000000000..d477cf8182 --- /dev/null +++ b/migration/block-active.c @@ -0,0 +1,94 @@ +/* + * Block activation tracking for migration purpose + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2024 Red Hat, Inc. + */ +#include "qemu/osdep.h" +#include "block/block.h" +#include "qapi/error.h" +#include "migration/migration.h" +#include "qemu/error-report.h" +#include "trace.h" + +/* + * Migration-only cache to remember the block layer activation status. + * Protected by BQL. + * + * We need this because.. + * + * - Migration can fail after block devices are invalidated (during + * switchover phase). When that happens, we need to be able to recover + * the block drive status by re-activating them. + * + * - Currently bdrv_inactivate_all() is not safe to be invoked on top of + * invalidated drives (even if bdrv_activate_all() is actually safe to be + * called any time!). It means remembering this could help migration to + * make sure it won't invalidate twice in a row, crashing QEMU. It can + * happen when we migrate a PAUSED VM from host1 to host2, then migrate + * again to host3 without starting it. TODO: a cleaner solution is to + * allow safe invoke of bdrv_inactivate_all() at anytime, like + * bdrv_activate_all(). + * + * For freshly started QEMU, the flag is initialized to TRUE reflecting the + * scenario where QEMU owns block device ownerships. + * + * For incoming QEMU taking a migration stream, the flag is initialized to + * FALSE reflecting that the incoming side doesn't own the block devices, + * not until switchover happens. + */ +static bool migration_block_active; + +/* Setup the disk activation status */ +void migration_block_active_setup(bool active) +{ + migration_block_active = active; +} + +bool migration_block_activate(Error **errp) +{ + ERRP_GUARD(); + + assert(bql_locked()); + + if (migration_block_active) { + trace_migration_block_activation("active-skipped"); + return true; + } + + trace_migration_block_activation("active"); + + bdrv_activate_all(errp); + if (*errp) { + error_report_err(error_copy(*errp)); + return false; + } + + migration_block_active = true; + return true; +} + +bool migration_block_inactivate(void) +{ + int ret; + + assert(bql_locked()); + + if (!migration_block_active) { + trace_migration_block_activation("inactive-skipped"); + return true; + } + + trace_migration_block_activation("inactive"); + + ret = bdrv_inactivate_all(); + if (ret) { + error_report("%s: bdrv_inactivate_all() failed: %d", + __func__, ret); + return false; + } + + migration_block_active = false; + return true; +} diff --git a/migration/colo.c b/migration/colo.c index afc9869020..9a8e5fbe9b 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -836,7 +836,7 @@ static void *colo_process_incoming_thread(void *opaque) /* Make sure all file formats throw away their mutable metadata */ bql_lock(); - bdrv_activate_all(&local_err); + migration_block_activate(&local_err); bql_unlock(); if (local_err) { error_report_err(local_err); diff --git a/migration/meson.build b/migration/meson.build index d53cf3417a..dac687ee3a 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -11,6 +11,7 @@ migration_files = files( system_ss.add(files( 'block-dirty-bitmap.c', + 'block-active.c', 'channel.c', 'channel-block.c', 'cpu-throttle.c', diff --git a/migration/migration.c b/migration/migration.c index fd42a549e6..2d1da917c7 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -738,7 +738,6 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, static void process_incoming_migration_bh(void *opaque) { - Error *local_err = NULL; MigrationIncomingState *mis = opaque; trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter"); @@ -769,11 +768,7 @@ static void process_incoming_migration_bh(void *opaque) * Make sure all file formats throw away their mutable * metadata. If error, don't restart the VM yet. */ - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - } else { + if (migration_block_activate(NULL)) { vm_start(); } } else { @@ -1560,16 +1555,6 @@ static void migrate_fd_cancel(MigrationState *s) } } } - if (s->state == MIGRATION_STATUS_CANCELLING && s->block_inactive) { - Error *local_err = NULL; - - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - } else { - s->block_inactive = false; - } - } } void migration_add_notifier_mode(NotifierWithReturn *notify, @@ -1853,6 +1838,12 @@ void qmp_migrate_incoming(const char *uri, bool has_channels, return; } + /* + * Newly setup incoming QEMU. Mark the block active state to reflect + * that the src currently owns the disks. + */ + migration_block_active_setup(false); + once = false; } @@ -2505,7 +2496,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) QIOChannelBuffer *bioc; QEMUFile *fb; uint64_t bandwidth = migrate_max_postcopy_bandwidth(); - bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; if (migrate_postcopy_preempt()) { @@ -2541,13 +2531,10 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail; } - ret = bdrv_inactivate_all(); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Failed in bdrv_inactivate_all()", - __func__); + if (!migration_block_inactivate()) { + error_setg(errp, "%s: Failed in bdrv_inactivate_all()", __func__); goto fail; } - restart_block = true; /* * Cause any non-postcopiable, but iterative devices to @@ -2617,8 +2604,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail_closefb; } - restart_block = false; - /* Now send that blob */ if (qemu_savevm_send_packaged(ms->to_dst_file, bioc->data, bioc->usage)) { error_setg(errp, "%s: Failed to send packaged data", __func__); @@ -2663,17 +2648,7 @@ fail_closefb: fail: migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, MIGRATION_STATUS_FAILED); - if (restart_block) { - /* A failure happened early enough that we know the destination hasn't - * accessed block devices, so we're safe to recover. - */ - Error *local_err = NULL; - - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - } - } + migration_block_activate(NULL); migration_call_notifiers(ms, MIG_EVENT_PRECOPY_FAILED, NULL); bql_unlock(); return -1; @@ -2771,31 +2746,6 @@ static void migration_completion_postcopy(MigrationState *s) trace_migration_completion_postcopy_end_after_complete(); } -static void migration_completion_failed(MigrationState *s, - int current_active_state) -{ - if (s->block_inactive && (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_DEVICE)) { - /* - * If not doing postcopy, vm_start() will be called: let's - * regain control on images. - */ - Error *local_err = NULL; - - bql_lock(); - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - } else { - s->block_inactive = false; - } - bql_unlock(); - } - - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_FAILED); -} - /** * migration_completion: Used by migration_thread when there's not much left. * The caller 'breaks' the loop when this returns. @@ -2849,7 +2799,8 @@ fail: error_free(local_err); } - migration_completion_failed(s, current_active_state); + migrate_set_state(&s->state, current_active_state, + MIGRATION_STATUS_FAILED); } /** @@ -3279,6 +3230,11 @@ static void migration_iteration_finish(MigrationState *s) case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: case MIGRATION_STATUS_CANCELLING: + /* + * Re-activate the block drives if they're inactivated. Note, COLO + * shouldn't use block_active at all, so it should be no-op there. + */ + migration_block_activate(NULL); if (runstate_is_live(s->vm_old_state)) { if (!runstate_check(RUN_STATE_SHUTDOWN)) { vm_start(); @@ -3852,6 +3808,8 @@ static void migration_instance_init(Object *obj) ms->state = MIGRATION_STATUS_NONE; ms->mbps = -1; ms->pages_per_second = -1; + /* Freshly started QEMU owns all the block devices */ + migration_block_active_setup(true); qemu_sem_init(&ms->pause_sem, 0); qemu_mutex_init(&ms->error_mutex); diff --git a/migration/migration.h b/migration/migration.h index 7b6e718690..0df2a187af 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -370,9 +370,6 @@ struct MigrationState { /* Flag set once the migration thread is running (and needs joining) */ bool migration_thread_running; - /* Flag set once the migration thread called bdrv_inactivate_all */ - bool block_inactive; - /* Migration is waiting for guest to unplug device */ QemuSemaphore wait_unplug_sem; @@ -556,4 +553,7 @@ void migration_bitmap_sync_precopy(bool last_stage); /* migration/block-dirty-bitmap.c */ void dirty_bitmap_mig_init(void); +/* migration/block-active.c */ +void migration_block_active_setup(bool active); + #endif diff --git a/migration/savevm.c b/migration/savevm.c index 3a414aff52..c929da1ca5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1547,19 +1547,18 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, } if (inactivate_disks) { - /* Inactivate before sending QEMU_VM_EOF so that the - * bdrv_activate_all() on the other end won't fail. */ - ret = bdrv_inactivate_all(); - if (ret) { - error_setg(&local_err, "%s: bdrv_inactivate_all() failed (%d)", - __func__, ret); + /* + * Inactivate before sending QEMU_VM_EOF so that the + * bdrv_activate_all() on the other end won't fail. + */ + if (!migration_block_inactivate()) { + error_setg(&local_err, "%s: bdrv_inactivate_all() failed", + __func__); migrate_set_error(ms, local_err); error_report_err(local_err); - qemu_file_set_error(f, ret); + qemu_file_set_error(f, -EFAULT); return ret; } - /* Remember that we did this */ - s->block_inactive = true; } if (!in_postcopy) { /* Postcopy stream will still be going */ @@ -2123,7 +2122,6 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) static void loadvm_postcopy_handle_run_bh(void *opaque) { - Error *local_err = NULL; MigrationIncomingState *mis = opaque; trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter"); @@ -2146,12 +2144,11 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) * Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ - bdrv_activate_all(&local_err); + bool success = migration_block_activate(NULL); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - } else { + + if (success) { vm_start(); } } else { @@ -3193,11 +3190,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, * side of the migration take control of the images. */ if (live && !saved_vm_running) { - ret = bdrv_inactivate_all(); - if (ret) { - error_setg(errp, "%s: bdrv_inactivate_all() failed (%d)", - __func__, ret); - } + migration_block_inactivate(); } } diff --git a/migration/trace-events b/migration/trace-events index bb0e0cc6dc..b82a1c5e40 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -383,3 +383,6 @@ migration_pagecache_insert(void) "Error allocating page" # cpu-throttle.c cpu_throttle_set(int new_throttle_pct) "set guest CPU throttled by %d%%" cpu_throttle_dirty_sync(void) "" + +# block-active.c +migration_block_activation(const char *name) "%s" diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 415e645130..1ca44fbd72 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -31,6 +31,7 @@ #include "qapi/type-helpers.h" #include "hw/mem/memory-device.h" #include "hw/intc/intc.h" +#include "migration/misc.h" NameInfo *qmp_query_name(Error **errp) { @@ -103,13 +104,8 @@ void qmp_cont(Error **errp) * Continuing after completed migration. Images have been * inactivated to allow the destination to take control. Need to * get control back now. - * - * If there are no inactive block nodes (e.g. because the VM was - * just paused rather than completing a migration), - * bdrv_inactivate_all() simply doesn't do anything. */ - bdrv_activate_all(&local_err); - if (local_err) { + if (!migration_block_activate(&local_err)) { error_propagate(errp, local_err); return; } From 86bee9e0c761a3d0e67c43b44001fd752f894cb0 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 9 Jan 2025 15:52:43 -0300 Subject: [PATCH 0857/2892] migration: Add more error handling to analyze-migration.py The analyze-migration script was seen failing in s390x in misterious ways. It seems we're reaching the VMSDFieldStruct constructor without any fields, which would indicate an empty .subsection entry, a VMSTATE_STRUCT with no fields or a vmsd with no fields. We don't have any of those, at least not without the unmigratable flag set, so this should never happen. Add some debug statements so that we can see what's going on the next time the issue happens. Reviewed-by: Peter Xu Message-Id: <20250109185249.23952-2-farosas@suse.de> Signed-off-by: Fabiano Rosas --- scripts/analyze-migration.py | 75 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 8a254a5b6a..f2457b1dde 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -429,6 +429,9 @@ class VMSDFieldStruct(VMSDFieldGeneric): super(VMSDFieldStruct, self).__init__(desc, file) self.data = collections.OrderedDict() + if 'fields' not in self.desc['struct']: + raise Exception("No fields in struct. VMSD:\n%s" % self.desc) + # When we see compressed array elements, unfold them here new_fields = [] for field in self.desc['struct']['fields']: @@ -477,6 +480,10 @@ class VMSDFieldStruct(VMSDFieldGeneric): raise Exception("Subsection %s not found at offset %x" % ( subsection['vmsd_name'], self.file.tell())) name = self.file.readstr() version_id = self.file.read32() + + if not subsection: + raise Exception("Empty description for subsection: %s" % name) + self.data[name] = VMSDSection(self.file, version_id, subsection, (name, 0)) self.data[name].read() @@ -574,10 +581,13 @@ class MigrationDump(object): } self.filename = filename self.vmsd_desc = None + self.vmsd_json = "" - def read(self, desc_only = False, dump_memory = False, write_memory = False): + def read(self, desc_only = False, dump_memory = False, + write_memory = False): # Read in the whole file file = MigrationFile(self.filename) + self.vmsd_json = file.read_migration_debug_json() # File magic data = file.read32() @@ -635,9 +645,11 @@ class MigrationDump(object): file.close() def load_vmsd_json(self, file): - vmsd_json = file.read_migration_debug_json() - self.vmsd_desc = json.loads(vmsd_json, object_pairs_hook=collections.OrderedDict) + self.vmsd_desc = json.loads(self.vmsd_json, + object_pairs_hook=collections.OrderedDict) for device in self.vmsd_desc['devices']: + if 'fields' not in device: + raise Exception("vmstate for device %s has no fields" % device['name']) key = (device['name'], device['instance_id']) value = ( VMSDSection, device ) self.section_classes[key] = value @@ -666,31 +678,34 @@ args = parser.parse_args() jsonenc = JSONEncoder(indent=4, separators=(',', ': ')) -if args.extract: - dump = MigrationDump(args.file) - - dump.read(desc_only = True) - print("desc.json") - f = open("desc.json", "w") - f.truncate() - f.write(jsonenc.encode(dump.vmsd_desc)) - f.close() - - dump.read(write_memory = True) - dict = dump.getDict() - print("state.json") - f = open("state.json", "w") - f.truncate() - f.write(jsonenc.encode(dict)) - f.close() -elif args.dump == "state": - dump = MigrationDump(args.file) - dump.read(dump_memory = args.memory) - dict = dump.getDict() - print(jsonenc.encode(dict)) -elif args.dump == "desc": - dump = MigrationDump(args.file) - dump.read(desc_only = True) - print(jsonenc.encode(dump.vmsd_desc)) -else: +if not any([args.extract, args.dump == "state", args.dump == "desc"]): raise Exception("Please specify either -x, -d state or -d desc") + +try: + dump = MigrationDump(args.file) + + if args.extract: + dump.read(desc_only = True) + + print("desc.json") + f = open("desc.json", "w") + f.truncate() + f.write(jsonenc.encode(dump.vmsd_desc)) + f.close() + + dump.read(write_memory = True) + dict = dump.getDict() + print("state.json") + f = open("state.json", "w") + f.truncate() + f.write(jsonenc.encode(dict)) + f.close() + elif args.dump == "state": + dump.read(dump_memory = args.memory) + dict = dump.getDict() + print(jsonenc.encode(dict)) + elif args.dump == "desc": + dump.read(desc_only = True) + print(jsonenc.encode(dump.vmsd_desc)) +except Exception: + raise Exception("Full JSON dump:\n%s", dump.vmsd_json) From 2aead53d39b828f8d9d0769ffa3579dadd64d846 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 9 Jan 2025 15:52:44 -0300 Subject: [PATCH 0858/2892] migration: Remove unused argument in vmsd_desc_field_end Reviewed-by: Peter Xu Message-Id: <20250109185249.23952-3-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/vmstate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/vmstate.c b/migration/vmstate.c index fa002b24e8..aa2821dec6 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -311,7 +311,7 @@ static void vmsd_desc_field_start(const VMStateDescription *vmsd, static void vmsd_desc_field_end(const VMStateDescription *vmsd, JSONWriter *vmdesc, - const VMStateField *field, size_t size, int i) + const VMStateField *field, size_t size) { if (!vmdesc) { return; @@ -420,7 +420,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } written_bytes = qemu_file_transferred(f) - old_offset; - vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); + vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes); /* Compressed arrays only care about the first element */ if (vmdesc_loop && vmsd_can_compress(field)) { From 69d1f784569fdb950f2923c3b6d00d7c1b71acc1 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 9 Jan 2025 15:52:45 -0300 Subject: [PATCH 0859/2892] migration: Fix parsing of s390 stream The parsing for the S390StorageAttributes section is currently leaving an unconsumed token that is later interpreted by the generic code as QEMU_VM_EOF, cutting the parsing short. The migration will issue a STATTR_FLAG_DONE between iterations, which the script consumes correctly, but there's a final STATTR_FLAG_EOS at .save_complete that the script is ignoring. Since the EOS flag is a u64 0x1ULL and the stream is big endian, on little endian hosts a byte read from it will be 0x0, the same as QEMU_VM_EOF. Fixes: 81c2c9dd5d ("tests/qtest/migration-test: Fix analyze-migration.py for s390x") Reviewed-by: Peter Xu Message-Id: <20250109185249.23952-4-farosas@suse.de> Signed-off-by: Fabiano Rosas --- scripts/analyze-migration.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index f2457b1dde..fcda11f31d 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -65,6 +65,9 @@ class MigrationFile(object): def tell(self): return self.file.tell() + def seek(self, a, b): + return self.file.seek(a, b) + # The VMSD description is at the end of the file, after EOF. Look for # the last NULL byte, then for the beginning brace of JSON. def read_migration_debug_json(self): @@ -272,11 +275,24 @@ class S390StorageAttributes(object): self.section_key = section_key def read(self): + pos = 0 while True: addr_flags = self.file.read64() flags = addr_flags & 0xfff - if (flags & (self.STATTR_FLAG_DONE | self.STATTR_FLAG_EOS)): + + if flags & self.STATTR_FLAG_DONE: + pos = self.file.tell() + continue + elif flags & self.STATTR_FLAG_EOS: return + else: + # No EOS came after DONE, that's OK, but rewind the + # stream because this is not our data. + if pos: + self.file.seek(pos, os.SEEK_SET) + return + raise Exception("Unknown flags %x", flags) + if (flags & self.STATTR_FLAG_ERROR): raise Exception("Error in migration stream") count = self.file.read64() From f52965bf0eeee28e89933264f1a9dbdcdaa76a7e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 9 Jan 2025 15:52:46 -0300 Subject: [PATCH 0860/2892] migration: Rename vmstate_info_nullptr Rename vmstate_info_nullptr from "uint64_t" to "nullptr". This vmstate actually reads and writes just a byte, so the proper name would be uint8. However, since this is a marker for a NULL pointer, it's convenient to have a more explicit name that can be identified by the consumers of the JSON part of the stream. Change the name to "nullptr" and add support for it in the analyze-migration.py script. Arbitrarily use the name of the type as the value of the field to avoid the script showing 0x30 or '0', which could be confusing for readers. Reviewed-by: Peter Xu Message-Id: <20250109185249.23952-5-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/vmstate-types.c | 2 +- scripts/analyze-migration.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index e83bfccb9e..d70d573dbd 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -338,7 +338,7 @@ static int put_nullptr(QEMUFile *f, void *pv, size_t size, } const VMStateInfo vmstate_info_nullptr = { - .name = "uint64", + .name = "nullptr", .get = get_nullptr, .put = put_nullptr, }; diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index fcda11f31d..923f174f1b 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -417,6 +417,28 @@ class VMSDFieldIntLE(VMSDFieldInt): super(VMSDFieldIntLE, self).__init__(desc, file) self.dtype = ' Date: Thu, 9 Jan 2025 15:52:47 -0300 Subject: [PATCH 0861/2892] migration: Dump correct JSON format for nullptr replacement QEMU plays a trick with null pointers inside an array of pointers in a VMSD field. See 07d4e69147 ("migration/vmstate: fix array of ptr with nullptrs") for more details on why. The idea makes sense in general, but it may overlooked the JSON writer where it could write nothing in a "struct" in the JSON hints section. We hit some analyze-migration.py issues on s390 recently, showing that some of the struct field contains nothing, like: {"name": "css", "array_len": 256, "type": "struct", "struct": {}, "size": 1} As described in details by Fabiano: https://lore.kernel.org/r/87pll37cin.fsf@suse.de It could be that we hit some null pointers there, and JSON was gone when they're null pointers. To fix it, instead of hacking around only at VMStateInfo level, do that from VMStateField level, so that JSON writer can also be involved. In this case, JSON writer will replace the pointer array (which used to be a "struct") to be the real representation of the nullptr field. Signed-off-by: Peter Xu Message-Id: <20250109185249.23952-6-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/vmstate.c | 118 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 27 deletions(-) diff --git a/migration/vmstate.c b/migration/vmstate.c index aa2821dec6..52704c822c 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -51,6 +51,36 @@ vmstate_field_exists(const VMStateDescription *vmsd, const VMStateField *field, return result; } +/* + * Create a fake nullptr field when there's a NULL pointer detected in the + * array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we + * can't dereference the NULL pointer. + */ +static const VMStateField * +vmsd_create_fake_nullptr_field(const VMStateField *field) +{ + VMStateField *fake = g_new0(VMStateField, 1); + + /* It can only happen on an array of pointers! */ + assert(field->flags & VMS_ARRAY_OF_POINTER); + + /* Some of fake's properties should match the original's */ + fake->name = field->name; + fake->version_id = field->version_id; + + /* Do not need "field_exists" check as it always exists (which is null) */ + fake->field_exists = NULL; + + /* See vmstate_info_nullptr - use 1 byte to represent nullptr */ + fake->size = 1; + fake->info = &vmstate_info_nullptr; + fake->flags = VMS_SINGLE; + + /* All the rest fields shouldn't matter.. */ + + return (const VMStateField *)fake; +} + static int vmstate_n_elems(void *opaque, const VMStateField *field) { int n_elems = 1; @@ -143,23 +173,39 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } for (i = 0; i < n_elems; i++) { void *curr_elem = first_elem + size * i; + const VMStateField *inner_field; if (field->flags & VMS_ARRAY_OF_POINTER) { curr_elem = *(void **)curr_elem; } + if (!curr_elem && size) { - /* if null pointer check placeholder and do not follow */ - assert(field->flags & VMS_ARRAY_OF_POINTER); - ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL); - } else if (field->flags & VMS_STRUCT) { - ret = vmstate_load_state(f, field->vmsd, curr_elem, - field->vmsd->version_id); - } else if (field->flags & VMS_VSTRUCT) { - ret = vmstate_load_state(f, field->vmsd, curr_elem, - field->struct_version_id); + /* + * If null pointer found (which should only happen in + * an array of pointers), use null placeholder and do + * not follow. + */ + inner_field = vmsd_create_fake_nullptr_field(field); } else { - ret = field->info->get(f, curr_elem, size, field); + inner_field = field; } + + if (inner_field->flags & VMS_STRUCT) { + ret = vmstate_load_state(f, inner_field->vmsd, curr_elem, + inner_field->vmsd->version_id); + } else if (inner_field->flags & VMS_VSTRUCT) { + ret = vmstate_load_state(f, inner_field->vmsd, curr_elem, + inner_field->struct_version_id); + } else { + ret = inner_field->info->get(f, curr_elem, size, + inner_field); + } + + /* If we used a fake temp field.. free it now */ + if (inner_field != field) { + g_clear_pointer((gpointer *)&inner_field, g_free); + } + if (ret >= 0) { ret = qemu_file_get_error(f); } @@ -387,29 +433,50 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } for (i = 0; i < n_elems; i++) { void *curr_elem = first_elem + size * i; + const VMStateField *inner_field; - vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); old_offset = qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { assert(curr_elem); curr_elem = *(void **)curr_elem; } + if (!curr_elem && size) { - /* if null pointer write placeholder and do not follow */ - assert(field->flags & VMS_ARRAY_OF_POINTER); - ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL, - NULL); - } else if (field->flags & VMS_STRUCT) { - ret = vmstate_save_state(f, field->vmsd, curr_elem, - vmdesc_loop); - } else if (field->flags & VMS_VSTRUCT) { - ret = vmstate_save_state_v(f, field->vmsd, curr_elem, - vmdesc_loop, - field->struct_version_id, errp); + /* + * If null pointer found (which should only happen in + * an array of pointers), use null placeholder and do + * not follow. + */ + inner_field = vmsd_create_fake_nullptr_field(field); } else { - ret = field->info->put(f, curr_elem, size, field, - vmdesc_loop); + inner_field = field; } + + vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field, + i, n_elems); + + if (inner_field->flags & VMS_STRUCT) { + ret = vmstate_save_state(f, inner_field->vmsd, + curr_elem, vmdesc_loop); + } else if (inner_field->flags & VMS_VSTRUCT) { + ret = vmstate_save_state_v(f, inner_field->vmsd, + curr_elem, vmdesc_loop, + inner_field->struct_version_id, + errp); + } else { + ret = inner_field->info->put(f, curr_elem, size, + inner_field, vmdesc_loop); + } + + written_bytes = qemu_file_transferred(f) - old_offset; + vmsd_desc_field_end(vmsd, vmdesc_loop, inner_field, + written_bytes); + + /* If we used a fake temp field.. free it now */ + if (inner_field != field) { + g_clear_pointer((gpointer *)&inner_field, g_free); + } + if (ret) { error_setg(errp, "Save of field %s/%s failed", vmsd->name, field->name); @@ -419,9 +486,6 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, return ret; } - written_bytes = qemu_file_transferred(f) - old_offset; - vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes); - /* Compressed arrays only care about the first element */ if (vmdesc_loop && vmsd_can_compress(field)) { vmdesc_loop = NULL; From 35049eb0d2fc72bb8c563196ec75b4d6c13fce02 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 9 Jan 2025 15:52:48 -0300 Subject: [PATCH 0862/2892] migration: Fix arrays of pointers in JSON writer Currently, if an array of pointers contains a NULL pointer, that pointer will be encoded as '0' in the stream. Since the JSON writer doesn't define a "pointer" type, that '0' will now be an uint8, which is different from the original type being pointed to, e.g. struct. (we're further calling uint8 "nullptr", but that's irrelevant to the issue) That mixed-type array shouldn't be compressed, otherwise data is lost as the code currently makes the whole array have the type of the first element: css = {NULL, NULL, ..., 0x5555568a7940, NULL}; {"name": "s390_css", "instance_id": 0, "vmsd_name": "s390_css", "version": 1, "fields": [ ..., {"name": "css", "array_len": 256, "type": "nullptr", "size": 1}, ..., ]} In the above, the valid pointer at position 254 got lost among the compressed array of nullptr. While we could disable the array compression when a NULL pointer is found, the JSON part of the stream still makes part of downtime, so we should avoid writing unecessary bytes to it. Keep the array compression in place, but if NULL and non-NULL pointers are mixed break the array into several type-contiguous pieces : css = {NULL, NULL, ..., 0x5555568a7940, NULL}; {"name": "s390_css", "instance_id": 0, "vmsd_name": "s390_css", "version": 1, "fields": [ ..., {"name": "css", "array_len": 254, "type": "nullptr", "size": 1}, {"name": "css", "type": "struct", "struct": {"vmsd_name": "s390_css_img", ... }, "size": 768}, {"name": "css", "type": "nullptr", "size": 1}, ..., ]} Now each type-discontiguous region will become a new JSON entry. The reader should interpret this as a concatenation of values, all part of the same field. Parsing the JSON with analyze-script.py now shows the proper data being pointed to at the places where the pointer is valid and "nullptr" where there's NULL: "s390_css (14)": { ... "css": [ "nullptr", "nullptr", ... "nullptr", { "chpids": [ { "in_use": "0x00", "type": "0x00", "is_virtual": "0x00" }, ... ] }, "nullptr", } Reviewed-by: Peter Xu Message-Id: <20250109185249.23952-7-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/vmstate.c | 33 ++++++++++++++++++++++++++++++++- scripts/analyze-migration.py | 26 ++++++++++++++++++-------- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/migration/vmstate.c b/migration/vmstate.c index 52704c822c..82bd005a83 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -425,15 +425,19 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, int size = vmstate_size(opaque, field); uint64_t old_offset, written_bytes; JSONWriter *vmdesc_loop = vmdesc; + bool is_prev_null = false; trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems); if (field->flags & VMS_POINTER) { first_elem = *(void **)first_elem; assert(first_elem || !n_elems || !size); } + for (i = 0; i < n_elems; i++) { void *curr_elem = first_elem + size * i; const VMStateField *inner_field; + bool is_null; + int max_elems = n_elems - i; old_offset = qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { @@ -448,12 +452,39 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, * not follow. */ inner_field = vmsd_create_fake_nullptr_field(field); + is_null = true; } else { inner_field = field; + is_null = false; + } + + /* + * Due to the fake nullptr handling above, if there's mixed + * null/non-null data, it doesn't make sense to emit a + * compressed array representation spanning the entire array + * because the field types will be different (e.g. struct + * vs. nullptr). Search ahead for the next null/non-null element + * and start a new compressed array if found. + */ + if (field->flags & VMS_ARRAY_OF_POINTER && + is_null != is_prev_null) { + + is_prev_null = is_null; + vmdesc_loop = vmdesc; + + for (int j = i + 1; j < n_elems; j++) { + void *elem = *(void **)(first_elem + size * j); + bool elem_is_null = !elem && size; + + if (is_null != elem_is_null) { + max_elems = j - i; + break; + } + } } vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field, - i, n_elems); + i, max_elems); if (inner_field->flags & VMS_STRUCT) { ret = vmstate_save_state(f, inner_field->vmsd, diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 923f174f1b..8e1fbf4c9d 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -502,15 +502,25 @@ class VMSDFieldStruct(VMSDFieldGeneric): field['data'] = reader(field, self.file) field['data'].read() - if 'index' in field: - if field['name'] not in self.data: - self.data[field['name']] = [] - a = self.data[field['name']] - if len(a) != int(field['index']): - raise Exception("internal index of data field unmatched (%d/%d)" % (len(a), int(field['index']))) - a.append(field['data']) + fname = field['name'] + fdata = field['data'] + + # The field could be: + # i) a single data entry, e.g. uint64 + # ii) an array, indicated by it containing the 'index' key + # + # However, the overall data after parsing the whole + # stream, could be a mix of arrays and single data fields, + # all sharing the same field name due to how QEMU breaks + # up arrays with NULL pointers into multiple compressed + # array segments. + if fname not in self.data: + self.data[fname] = fdata + elif type(self.data[fname]) == list: + self.data[fname].append(fdata) else: - self.data[field['name']] = field['data'] + tmp = self.data[fname] + self.data[fname] = [tmp, fdata] if 'subsections' in self.desc['struct']: for subsection in self.desc['struct']['subsections']: From c76ee1f6255c3988a9447d363bb17072f1ec84e1 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 9 Jan 2025 15:52:49 -0300 Subject: [PATCH 0863/2892] s390x: Fix CSS migration Commit a55ae46683 ("s390: move css_migration_enabled from machine to css.c") disabled CSS migration globally instead of doing it per-instance. CC: Paolo Bonzini CC: qemu-stable@nongnu.org #9.1 Fixes: a55ae46683 ("s390: move css_migration_enabled from machine to css.c") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2704 Reviewed-by: Thomas Huth Message-Id: <20250109185249.23952-8-farosas@suse.de> Signed-off-by: Fabiano Rosas --- hw/s390x/s390-virtio-ccw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 8a242cc1ec..38aeba14ee 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1244,6 +1244,7 @@ static void ccw_machine_2_9_instance_options(MachineState *machine) s390_cpudef_featoff_greater(12, 1, S390_FEAT_ZPCI); s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_INT_SUPPRESSION); s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_EVENT_NOTIFICATION); + css_migration_enabled = false; } static void ccw_machine_2_9_class_options(MachineClass *mc) @@ -1256,7 +1257,6 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) ccw_machine_2_10_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - css_migration_enabled = false; } DEFINE_CCW_MACHINE(2, 9); From cdc3970f8597ebdc1a4c2090cfb4d11e297329ed Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Wed, 18 Dec 2024 17:14:11 +0800 Subject: [PATCH 0864/2892] multifd: bugfix for migration using compression methods When compression is enabled on the migration channel and the pages processed are all zero pages, these pages will not be sent and updated on the target side, resulting in incorrect memory data on the source and target sides. The root cause is that all compression methods call multifd_send_prepare_common to determine whether to compress dirty pages, but multifd_send_prepare_common does not update the IOV of MultiFDPacket_t when all dirty pages are zero pages. The solution is to always update the IOV of MultiFDPacket_t regardless of whether the dirty pages are all zero pages. Fixes: 303e6f54f9 ("migration/multifd: Implement zero page transmission on the multifd thread.") Cc: qemu-stable@nongnu.org #9.0+ Signed-off-by: Yuan Liu Reviewed-by: Jason Zeng Reviewed-by: Peter Xu Message-Id: <20241218091413.140396-2-yuan1.liu@intel.com> Signed-off-by: Fabiano Rosas --- migration/multifd-nocomp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index c1f686c0ce..1325dba97c 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -432,6 +432,7 @@ int multifd_ram_flush_and_sync(QEMUFile *f) bool multifd_send_prepare_common(MultiFDSendParams *p) { MultiFDPages_t *pages = &p->data->u.ram; + multifd_send_prepare_header(p); multifd_send_zero_page_detect(p); if (!pages->normal_num) { @@ -439,8 +440,6 @@ bool multifd_send_prepare_common(MultiFDSendParams *p) return false; } - multifd_send_prepare_header(p); - return true; } From 2588a5f99b0c3493b4690e3ff01ed36f80e830cc Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Wed, 18 Dec 2024 17:14:12 +0800 Subject: [PATCH 0865/2892] multifd: bugfix for incorrect migration data with QPL compression When QPL compression is enabled on the migration channel and the same dirty page changes from a normal page to a zero page in the iterative memory copy, the dirty page will not be updated to a zero page again on the target side, resulting in incorrect memory data on the source and target sides. The root cause is that the target side does not record the normal pages to the receivedmap. The solution is to add ramblock_recv_bitmap_set_offset in target side to record the normal pages. Signed-off-by: Yuan Liu Reviewed-by: Jason Zeng Reviewed-by: Peter Xu Message-Id: <20241218091413.140396-3-yuan1.liu@intel.com> Signed-off-by: Fabiano Rosas --- migration/multifd-qpl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index bbe466617f..88e2344af2 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -679,6 +679,7 @@ static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]); assert(qpl->zlen[i] <= multifd_ram_page_size()); zbuf_len += qpl->zlen[i]; + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); } /* read compressed pages */ From a523bc52166c80d8a04d46584f9f3868bd53ef69 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Wed, 18 Dec 2024 17:14:13 +0800 Subject: [PATCH 0866/2892] multifd: bugfix for incorrect migration data with qatzip compression When QPL compression is enabled on the migration channel and the same dirty page changes from a normal page to a zero page in the iterative memory copy, the dirty page will not be updated to a zero page again on the target side, resulting in incorrect memory data on the source and target sides. The root cause is that the target side does not record the normal pages to the receivedmap. The solution is to add ramblock_recv_bitmap_set_offset in target side to record the normal pages. Signed-off-by: Yuan Liu Reviewed-by: Jason Zeng Reviewed-by: Peter Xu Message-Id: <20241218091413.140396-4-yuan1.liu@intel.com> Signed-off-by: Fabiano Rosas --- migration/multifd-qatzip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/multifd-qatzip.c b/migration/multifd-qatzip.c index 7b68397625..6a0e989fae 100644 --- a/migration/multifd-qatzip.c +++ b/migration/multifd-qatzip.c @@ -373,6 +373,7 @@ static int qatzip_recv(MultiFDRecvParams *p, Error **errp) /* Copy each page to its appropriate location. */ for (int i = 0; i < p->normal_num; i++) { memcpy(p->host + p->normal[i], q->out_buf + page_size * i, page_size); + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); } return 0; } From 08258d7e6b1da61fe534d0202286a0778d24a123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:23 +0000 Subject: [PATCH 0867/2892] tests/functional: update the arm tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-2-alex.bennee@linaro.org> --- tests/functional/test_arm_tuxrun.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/test_arm_tuxrun.py b/tests/functional/test_arm_tuxrun.py index 944f0756e1..4ac85f48ac 100755 --- a/tests/functional/test_arm_tuxrun.py +++ b/tests/functional/test_arm_tuxrun.py @@ -17,14 +17,14 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunArmTest(TuxRunBaselineTest): ASSET_ARMV5_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/armv5/zImage', - 'c95af2f27647c12265d75e9df44c22ff5228c59855f54aaa70f41ec2842e3a4d') + 'https://storage.tuxboot.com/buildroot/20241119/armv5/zImage', + '3931a3908dbcf0ec0fe292d035ffc4dfed95f797dedd4a59ccfcf7a46e6f92d4') ASSET_ARMV5_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/armv5/rootfs.ext4.zst', - '17177afa74e7294da0642861f08c88ca3c836764299a54bf6d1ce276cb9712a5') + 'https://storage.tuxboot.com/buildroot/20241119/armv5/rootfs.ext4.zst', + '60ff78b68c7021df378e4fc2d66d3b016484d1acc7e07fb8920c1d8e30f4571f') ASSET_ARMV5_DTB = Asset( - 'https://storage.tuxboot.com/20230331/armv5/versatile-pb.dtb', - '0bc0c0b0858cefd3c32b385c0d66d97142ded29472a496f4f490e42fc7615b25') + 'https://storage.tuxboot.com/buildroot/20241119/armv5/versatile-pb.dtb', + '50988e69ef3f3b08bfb9146e8fe414129990029e8dfbed444953b7e14809530a') def test_armv5(self): self.set_machine('versatilepb') @@ -37,11 +37,11 @@ class TuxRunArmTest(TuxRunBaselineTest): drive="virtio-blk-pci") ASSET_ARMV7_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/armv7/zImage', - '4c7a22e9f15875bec06bd2a29d822496571eb297d4f22694099ffcdb19077572') + 'https://storage.tuxboot.com/buildroot/20241119/armv7/zImage', + '1377bc3d90de5ce57ab17cd67429fe8b15c2e9964248c775c682b67e6299b991') ASSET_ARMV7_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/armv7/rootfs.ext4.zst', - 'ab1fbbeaddda1ffdd45c9405a28cd5370c20f23a7cbc809cc90dc9f243a8eb5a') + 'https://storage.tuxboot.com/buildroot/20241119/armv7/rootfs.ext4.zst', + 'ed2cbc69bd6b3fbd5cafb5ee961393c7cfbe726446f14301c67d6b1f28bfdb51') def test_armv7(self): self.set_machine('virt') @@ -52,11 +52,11 @@ class TuxRunArmTest(TuxRunBaselineTest): rootfs_asset=self.ASSET_ARMV7_ROOTFS) ASSET_ARMV7BE_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/armv7be/zImage', - '7facc62082b57af12015b08f7fdbaf2f123ba07a478367853ae12b219afc9f2f') + 'https://storage.tuxboot.com/buildroot/20241119/armv7be/zImage', + 'a244e6da99f1bbd254827ec7681bd4aac9eb1aa05aaebc6b15e5d289ebb683f3') ASSET_ARMV7BE_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/armv7be/rootfs.ext4.zst', - '42ed46dd2d59986206c5b1f6cf35eab58fe3fd20c96b41aaa16b32f3f90a9835') + 'https://storage.tuxboot.com/buildroot/20241119/armv7be/rootfs.ext4.zst', + 'd4f9c57860a512163f30ecc69b2174d1a1bdeb853a43dc49a09cfcfe84e428ea') def test_armv7be(self): self.set_machine('virt') From 24b49f725356d2ff6acd4011e782e0893c38e192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:24 +0000 Subject: [PATCH 0868/2892] tests/functional: update the i386 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-3-alex.bennee@linaro.org> --- tests/functional/test_i386_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/test_i386_tuxrun.py index c593ffbe8c..f3ccf11ae8 100755 --- a/tests/functional/test_i386_tuxrun.py +++ b/tests/functional/test_i386_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunI386Test(TuxRunBaselineTest): ASSET_I386_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/i386/bzImage', - 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956') + 'https://storage.tuxboot.com/buildroot/20241119/i386/bzImage', + '47fb44e38e34101eb0f71a2a01742b959d40ed5fd67cefb5608a39be11d3b74e') ASSET_I386_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/i386/rootfs.ext4.zst', - 'f15e66b2bf673a210ec2a4b2e744a80530b36289e04f5388aab812b97f69754a') + 'https://storage.tuxboot.com/buildroot/20241119/i386/rootfs.ext4.zst', + 'a1a3b3b4c9dccd6475b58db95c107b468b736b700f6620985a8ed050a73d51c8') def test_i386(self): self.set_machine('q35') From 3356bb834a90b521b03e9b2b7e3df877a0f5edac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:25 +0000 Subject: [PATCH 0869/2892] tests/functional: add a m68k tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We didn't have this before and as it exercises the m68k virt platform it seems worth adding. We don't wait for the shutdown because QEMU will auto-exit on the shutdown. Cc: Laurent Vivier Cc: Anders Roxell Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-4-alex.bennee@linaro.org> --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_m68k_tuxrun.py | 34 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100755 tests/functional/test_m68k_tuxrun.py diff --git a/MAINTAINERS b/MAINTAINERS index 2101b51217..cab9018d9f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1288,6 +1288,7 @@ F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h F: docs/specs/virt-ctlr.rst +F: tests/functional/test_m68k_tuxrun.py MicroBlaze Machines ------------------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index a5087fcb34..7890dcb86d 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -116,6 +116,7 @@ tests_m68k_system_thorough = [ 'm68k_mcf5208evb', 'm68k_nextcube', 'm68k_q800', + 'm68k_tuxrun', ] tests_microblaze_system_thorough = [ diff --git a/tests/functional/test_m68k_tuxrun.py b/tests/functional/test_m68k_tuxrun.py new file mode 100755 index 0000000000..7eacba135f --- /dev/null +++ b/tests/functional/test_m68k_tuxrun.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunM68KTest(TuxRunBaselineTest): + + ASSET_M68K_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/m68k/vmlinux', + '7754e1d5cec753ccf1dc6894729a7f54c1a4965631ebf56df8e4ce1163ad19d8') + ASSET_M68K_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/m68k/rootfs.ext4.zst', + '557962ffff265607912e82232cf21adbe0e4e5a88e1e1d411ce848c37f0213e9') + + def test_m68k(self): + self.set_machine('virt') + self.cpu="m68040" + self.common_tuxrun(kernel_asset=self.ASSET_M68K_KERNEL, + rootfs_asset=self.ASSET_M68K_ROOTFS, + drive="virtio-blk-device") + +if __name__ == '__main__': + TuxRunBaselineTest.main() From f273473d2f0567e24fdb15106a444e8a1d844c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:26 +0000 Subject: [PATCH 0870/2892] tests/functional: update the mips32 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-5-alex.bennee@linaro.org> --- tests/functional/test_mips_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_mips_tuxrun.py b/tests/functional/test_mips_tuxrun.py index 6fec44c2bf..6771dbd57e 100755 --- a/tests/functional/test_mips_tuxrun.py +++ b/tests/functional/test_mips_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunMipsTest(TuxRunBaselineTest): ASSET_MIPS_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/mips32/vmlinux', - 'bfd2172f8b17fb32970ca0c8c58f59c5a4ca38aa5855d920be3a69b5d16e52f0') + 'https://storage.tuxboot.com/buildroot/20241119/mips32/vmlinux', + 'b6f97fc698ae8c96456ad8c996c7454228074df0d7520dedd0a15e2913700a19') ASSET_MIPS_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/mips32/rootfs.ext4.zst', - 'fc3da0b4c2f38d74c6d705123bb0f633c76ed953128f9d0859378c328a6d11a0') + 'https://storage.tuxboot.com/buildroot/20241119/mips32/rootfs.ext4.zst', + '87055cf3cbde3fd134e5039e7b87feb03231d8c4b21ee712b8ba3308dfa72f50') def test_mips32(self): self.set_machine('malta') From 16476efde81142ea91213be859771042893be3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:27 +0000 Subject: [PATCH 0871/2892] tests/functional: update the mips32el tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-6-alex.bennee@linaro.org> --- tests/functional/test_mipsel_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_mipsel_tuxrun.py b/tests/functional/test_mipsel_tuxrun.py index 2965bbd913..d4b39baab5 100755 --- a/tests/functional/test_mipsel_tuxrun.py +++ b/tests/functional/test_mipsel_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunMipsELTest(TuxRunBaselineTest): ASSET_MIPSEL_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/mips32el/vmlinux', - '8573867c68a8443db8de6d08bb33fb291c189ca2ca671471d3973a3e712096a3') + 'https://storage.tuxboot.com/buildroot/20241119/mips32el/vmlinux', + '660dd8c7a6ca7a32d37b4e6348865532ab0edb66802e8cc07869338444cf4929') ASSET_MIPSEL_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/mips32el/rootfs.ext4.zst', - 'e799768e289fd69209c21f4dacffa11baea7543d5db101e8ce27e3bc2c41d90e') + 'https://storage.tuxboot.com/buildroot/20241119/mips32el/rootfs.ext4.zst', + 'c5d69542bcaed54a4f34671671eb4be5c608ee02671d4d0436544367816a73b1') def test_mips32el(self): self.set_machine('malta') From f0749a83bd3139cadcfbc055623bf420361dc5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:28 +0000 Subject: [PATCH 0872/2892] tests/functional: update the mips64 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-7-alex.bennee@linaro.org> --- tests/functional/test_mips64_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/test_mips64_tuxrun.py index 54af1ae794..0e4c65961d 100755 --- a/tests/functional/test_mips64_tuxrun.py +++ b/tests/functional/test_mips64_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunMips64Test(TuxRunBaselineTest): ASSET_MIPS64_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/mips64/vmlinux', - '09010e51e4b8bcbbd2494786ffb48eca78f228e96e5c5438344b0eac4029dc61') + 'https://storage.tuxboot.com/buildroot/20241119/mips64/vmlinux', + 'fe2882d216898ba2c56b49ba59f46ad392f36871f7fe325373cd926848b9dbdc') ASSET_MIPS64_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/mips64/rootfs.ext4.zst', - '69d91eeb04df3d8d172922c6993bb37d4deeb6496def75d8580f6f9de3e431da') + 'https://storage.tuxboot.com/buildroot/20241119/mips64/rootfs.ext4.zst', + 'b8c98400216b6d4fb3b3ff05e9929aa015948b596cf0b82234813c84a4f7f4d5') def test_mips64(self): self.set_machine('malta') From c5856795fade7cff0b01acd4617118347f0a02e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:29 +0000 Subject: [PATCH 0873/2892] tests/functional: update the mips64el tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-8-alex.bennee@linaro.org> --- tests/functional/test_mips64el_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_mips64el_tuxrun.py b/tests/functional/test_mips64el_tuxrun.py index 819549a27b..0a24757c51 100755 --- a/tests/functional/test_mips64el_tuxrun.py +++ b/tests/functional/test_mips64el_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunMips64ELTest(TuxRunBaselineTest): ASSET_MIPS64EL_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/mips64el/vmlinux', - 'd4e08965e2155c4cccce7c5f34d18fe34c636cda2f2c9844387d614950155266') + 'https://storage.tuxboot.com/buildroot/20241119/mips64el/vmlinux', + '0d2829a96f005229839c4cd586d4d8a136ea4b488d29821611c8e97f2266bfa9') ASSET_MIPS64EL_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/mips64el/rootfs.ext4.zst', - 'fba585368f5915b1498ed081863474b2d7ec4e97cdd46d21bdcb2f9698f83de4') + 'https://storage.tuxboot.com/buildroot/20241119/mips64el/rootfs.ext4.zst', + '69c8b69a4f1582ce4c6f01a994968f5d73bffb2fc99cbeeeb26c8b5a28eaeb84') def test_mips64el(self): self.set_machine('malta') From dfcab187c5a8630859ee5d1dfe2ca5a1808307e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:30 +0000 Subject: [PATCH 0874/2892] tests/functional: update the ppc32 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-9-alex.bennee@linaro.org> --- tests/functional/test_ppc_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/test_ppc_tuxrun.py index 50b76946c4..5458a7fb71 100755 --- a/tests/functional/test_ppc_tuxrun.py +++ b/tests/functional/test_ppc_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunPPC32Test(TuxRunBaselineTest): ASSET_PPC32_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/ppc32/uImage', - '1a68f74b860fda022fb12e03c5efece8c2b8b590d96cca37a8481a3ae0b3f81f') + 'https://storage.tuxboot.com/buildroot/20241119/ppc32/uImage', + 'aa5d81deabdb255a318c4bc5ffd6fdd2b5da1ef39f1955dcc35b671d258b68e9') ASSET_PPC32_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/ppc32/rootfs.ext4.zst', - '8885b9d999cc24d679542a02e9b6aaf48f718f2050ece6b8347074b6ee41dd09') + 'https://storage.tuxboot.com/buildroot/20241119/ppc32/rootfs.ext4.zst', + '67554f830269d6bf53b67c7dd206bcc821e463993d526b1644066fea8117019b') def test_ppc32(self): self.set_machine('ppce500') From aa880bb099457da5f445ae5c9231db53b189d7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:31 +0000 Subject: [PATCH 0875/2892] tests/functional: update the ppc64 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-10-alex.bennee@linaro.org> --- tests/functional/test_ppc64_tuxrun.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/test_ppc64_tuxrun.py index 8a98d18ab3..05c6162b5e 100755 --- a/tests/functional/test_ppc64_tuxrun.py +++ b/tests/functional/test_ppc64_tuxrun.py @@ -85,11 +85,11 @@ class TuxRunPPC64Test(TuxRunBaselineTest): drive="scsi-hd") ASSET_PPC64_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/ppc64/vmlinux', - 'f22a9b9e924174a4c199f4c7e5d91a2339fcfe51c6eafd0907dc3e09b64ab728') + 'https://storage.tuxboot.com/buildroot/20241119/ppc64/vmlinux', + '8219d5cb26e7654ad7826fe8aee6290f7c01eef44f2cd6d26c15fe8f99e1c17c') ASSET_PPC64_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/ppc64/rootfs.ext4.zst', - '1d953e81a4379e537fc8e41e05a0a59d9b453eef97aa03d47866c6c45b00bdff') + 'https://storage.tuxboot.com/buildroot/20241119/ppc64/rootfs.ext4.zst', + 'b68e12314303c5dd0fef37ae98021299a206085ae591893e73557af99a02d373') def test_ppc64(self): self.ppc64_common_tuxrun(kernel_asset=self.ASSET_PPC64_KERNEL, @@ -97,11 +97,11 @@ class TuxRunPPC64Test(TuxRunBaselineTest): prefix='tuxrun_ppc64_') ASSET_PPC64LE_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/ppc64le/vmlinux', - '979eb61b445a010fb13e2b927126991f8ceef9c590fa2be0996c00e293e80cf2') + 'https://storage.tuxboot.com/buildroot/20241119/ppc64le/vmlinux', + '21aea1fbc18bf6fa7d8ca4ea48d4940b2c8363c077acd564eb47d769b7495279') ASSET_PPC64LE_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/ppc64le/rootfs.ext4.zst', - 'b442678c93fb8abe1f7d3bfa20556488de6b475c22c8fed363f42cf81a0a3906') + 'https://storage.tuxboot.com/buildroot/20241119/ppc64le/rootfs.ext4.zst', + '67d36a3f9597b738e8b7359bdf04500f4d9bb82fc35eaa65aa439d888b2392f4') def test_ppc64le(self): self.ppc64_common_tuxrun(kernel_asset=self.ASSET_PPC64LE_KERNEL, From f341873b503598f8ff211ec75662d448be42a180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:32 +0000 Subject: [PATCH 0876/2892] tests/functional: update the riscv32 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-11-alex.bennee@linaro.org> --- tests/functional/test_riscv32_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_riscv32_tuxrun.py b/tests/functional/test_riscv32_tuxrun.py index 49b57cd428..3c570208d0 100755 --- a/tests/functional/test_riscv32_tuxrun.py +++ b/tests/functional/test_riscv32_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunRiscV32Test(TuxRunBaselineTest): ASSET_RISCV32_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/riscv32/Image', - '89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5') + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/Image', + '872bc8f8e0d4661825d5f47f7bec64988e9d0a8bd5db8917d57e16f66d83b329') ASSET_RISCV32_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/riscv32/rootfs.ext4.zst', - '7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba') + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/rootfs.ext4.zst', + '511ad34e63222db08d6c1da16fad224970de36517a784110956ba6a24a0ee5f6') def test_riscv32(self): self.set_machine('virt') From f051adae86ae544ec47a7666d77f7a95ef6d2721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:33 +0000 Subject: [PATCH 0877/2892] tests/functional: update the riscv64 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Note we re-use the riscv32 kernel and rootfs for test_riscv64_rv32. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-12-alex.bennee@linaro.org> --- tests/functional/test_riscv64_tuxrun.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/test_riscv64_tuxrun.py b/tests/functional/test_riscv64_tuxrun.py index 4e2449539c..0d8de36204 100755 --- a/tests/functional/test_riscv64_tuxrun.py +++ b/tests/functional/test_riscv64_tuxrun.py @@ -17,18 +17,18 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunRiscV64Test(TuxRunBaselineTest): ASSET_RISCV64_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/riscv64/Image', - 'cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e') + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/Image', + '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba') ASSET_RISCV64_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/riscv64/rootfs.ext4.zst', - 'b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb') + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/rootfs.ext4.zst', + 'aa4736a9872651dfc0d95e709465eedf1134fd19d42b8cb305bfd776f9801004') ASSET_RISCV32_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/riscv32/Image', - '89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5') + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/Image', + '872bc8f8e0d4661825d5f47f7bec64988e9d0a8bd5db8917d57e16f66d83b329') ASSET_RISCV32_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/riscv32/rootfs.ext4.zst', - '7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba') + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/rootfs.ext4.zst', + '511ad34e63222db08d6c1da16fad224970de36517a784110956ba6a24a0ee5f6') def test_riscv64(self): self.set_machine('virt') From 9507445bad546ed03758b8ad5244d9b0c65f1da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:34 +0000 Subject: [PATCH 0878/2892] tests/functional: update the s390x tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-13-alex.bennee@linaro.org> --- tests/functional/test_s390x_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/test_s390x_tuxrun.py index dcab17c68b..a7db4bfd84 100755 --- a/tests/functional/test_s390x_tuxrun.py +++ b/tests/functional/test_s390x_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunS390xTest(TuxRunBaselineTest): ASSET_S390X_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/s390/bzImage', - '0414e98dd1c3dafff8496c9cd9c28a5f8d04553bb5ba37e906a812b48d442ef0') + 'https://storage.tuxboot.com/buildroot/20241119/s390/bzImage', + 'ee67e91db52a2aed104a7c72b2a08987c678f8179c029626789c35d6dd0fedf1') ASSET_S390X_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/s390/rootfs.ext4.zst', - '88c37c32276677f873a25ab9ec6247895b8e3e6f8259134de2a616080b8ab3fc') + 'https://storage.tuxboot.com/buildroot/20241119/s390/rootfs.ext4.zst', + 'bff7971fc2fef56372d98afe4557b82fd0a785a241e44c29b058e577ad1bbb44') def test_s390(self): self.wait_for_shutdown=False From 070cf5372b12982bc0fedb01800cc923cb3cee04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:35 +0000 Subject: [PATCH 0879/2892] tests/functional: update the sparc64 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-14-alex.bennee@linaro.org> --- tests/functional/test_sparc64_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/test_sparc64_tuxrun.py index 1c2c005630..3be08d6102 100755 --- a/tests/functional/test_sparc64_tuxrun.py +++ b/tests/functional/test_sparc64_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunSparc64Test(TuxRunBaselineTest): ASSET_SPARC64_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/sparc64/vmlinux', - 'e34313e4325ff21deaa3d38a502aa09a373ef62b9bd4d7f8f29388b688225c55') + 'https://storage.tuxboot.com/buildroot/20241119/sparc64/vmlinux', + 'a04cfb2e70a264051d161fdd93aabf4b2a9472f2e435c14ed18c5848c5fed261') ASSET_SPARC64_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/sparc64/rootfs.ext4.zst', - 'ad2f1dc436ab51583543d25d2c210cab478645d47078d30d129a66ab0e281d76') + 'https://storage.tuxboot.com/buildroot/20241119/sparc64/rootfs.ext4.zst', + '479c3dc104c82b68be55e2c0c5c38cd473d0b37ad4badccde4775bb88ce34611') def test_sparc64(self): self.root='sda' From bf319c47dae9ee481c56263220d4942e6b696d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:36 +0000 Subject: [PATCH 0880/2892] tests/functional: update the x86_64 tuxrun tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now there are new up to date images available we should update to them. Cc: Anders Roxell Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-15-alex.bennee@linaro.org> --- tests/functional/test_x86_64_tuxrun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/test_x86_64_tuxrun.py index 4f96139871..fcbc62b1b0 100755 --- a/tests/functional/test_x86_64_tuxrun.py +++ b/tests/functional/test_x86_64_tuxrun.py @@ -17,11 +17,11 @@ from qemu_test.tuxruntest import TuxRunBaselineTest class TuxRunX86Test(TuxRunBaselineTest): ASSET_X86_64_KERNEL = Asset( - 'https://storage.tuxboot.com/20230331/x86_64/bzImage', - '2bc7480a669ee9b6b82500a236aba0c54233debe98cb968268fa230f52f03461') + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/bzImage', + 'f57bfc6553bcd6e0a54aab86095bf642b33b5571d14e3af1731b18c87ed5aef8') ASSET_X86_64_ROOTFS = Asset( - 'https://storage.tuxboot.com/20230331/x86_64/rootfs.ext4.zst', - 'b72ac729769b8f51c6dffb221113c9a063c774dbe1d66af30eb593c4e9999b4b') + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/rootfs.ext4.zst', + '4b8b2a99117519c5290e1202cb36eb6c7aaba92b357b5160f5970cf5fb78a751') def test_x86_64(self): self.set_machine('q35') From 0d77c908f29fad72d1417178b4cc5704d481a791 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 8 Jan 2025 12:10:37 +0000 Subject: [PATCH 0881/2892] tests/functional/aarch64: add tests for FEAT_RME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This boot an OP-TEE environment, and launch a nested guest VM inside it using the Realms feature. We do it for virt and sbsa-ref platforms. Signed-off-by: Pierrick Bouvier Message-Id: <20241220165212.3653495-1-pierrick.bouvier@linaro.org> [AJB: tweak ordering of setup, strip changelog from commit] Signed-off-by: Alex Bennée Tested-by: Thomas Huth Message-Id: <20250108121054.1126164-16-alex.bennee@linaro.org> --- tests/functional/meson.build | 4 + tests/functional/test_aarch64_rme_sbsaref.py | 69 ++++++++++++++ tests/functional/test_aarch64_rme_virt.py | 98 ++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100755 tests/functional/test_aarch64_rme_sbsaref.py create mode 100755 tests/functional/test_aarch64_rme_virt.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7890dcb86d..bd3d903cfc 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -13,6 +13,8 @@ endif test_timeouts = { 'aarch64_aspeed' : 600, 'aarch64_raspi4' : 480, + 'aarch64_rme_virt' : 1200, + 'aarch64_rme_sbsaref' : 1200, 'aarch64_sbsaref_alpine' : 720, 'aarch64_sbsaref_freebsd' : 720, 'aarch64_tuxrun' : 240, @@ -60,6 +62,8 @@ tests_aarch64_system_thorough = [ 'aarch64_aspeed', 'aarch64_raspi3', 'aarch64_raspi4', + 'aarch64_rme_virt', + 'aarch64_rme_sbsaref', 'aarch64_sbsaref', 'aarch64_sbsaref_alpine', 'aarch64_sbsaref_freebsd', diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py new file mode 100755 index 0000000000..93bb528338 --- /dev/null +++ b/tests/functional/test_aarch64_rme_sbsaref.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on sbsa-ref machine and a +# nested guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import time +import os +import logging + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from test_aarch64_rme_virt import test_realms_guest + +class Aarch64RMESbsaRefMachine(QemuSystemTest): + + # Stack is built with OP-TEE build environment from those instructions: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-rme-stack + ASSET_RME_STACK_SBSA = Asset( + ('https://fileserver.linaro.org/s/KJyeBxL82mz2r7F/' + 'download/rme-stack-op-tee-4.2.0-cca-v4-sbsa.tar.gz'), + 'dd9ab28ec869bdf3b5376116cb3689103b43433fd5c4bca0f4a8d8b3c104999e') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_sbsaref(self): + self.set_machine('sbsa-ref') + self.require_accelerator('tcg') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-sbsa') + pflash0 = os.path.join(rme_stack, 'images', 'SBSA_FLASH0.fd') + pflash1 = os.path.join(rme_stack, 'images', 'SBSA_FLASH1.fd') + virtual = os.path.join(rme_stack, 'images', 'disks', 'virtual') + drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'sbsa-ref') + self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash') + self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash') + self.vm.add_args('-drive', f'file=fat:rw:{virtual},format=raw') + self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') + self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') + self.vm.add_args('-device', 'virtio-9p-pci,fsdev=shr0,mount_tag=shr0') + self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') + self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') + self.vm.add_args('-netdev', 'user,id=net0') + + self.vm.launch() + # Wait for host VM boot to complete. + wait_for_console_pattern(self, 'Welcome to Buildroot') + exec_command_and_wait_for_pattern(self, 'root', '#') + + test_realms_guest(self) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py new file mode 100755 index 0000000000..42b9229b4c --- /dev/null +++ b/tests/functional/test_aarch64_rme_virt.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on virt machine and a nested +# guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import time +import os +import logging + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +def test_realms_guest(test_rme_instance): + + # Boot the (nested) guest VM + exec_command(test_rme_instance, + 'qemu-system-aarch64 -M virt,gic-version=3 ' + '-cpu host -enable-kvm -m 512M ' + '-M confidential-guest-support=rme0 ' + '-object rme-guest,id=rme0 ' + '-device virtio-net-pci,netdev=net0,romfile= ' + '-netdev user,id=net0 ' + '-kernel /mnt/out/bin/Image ' + '-initrd /mnt/out-br/images/rootfs.cpio ' + '-serial stdio') + # Detect Realm activation during (nested) guest boot. + wait_for_console_pattern(test_rme_instance, + 'SMC_RMI_REALM_ACTIVATE') + # Wait for (nested) guest boot to complete. + wait_for_console_pattern(test_rme_instance, + 'Welcome to Buildroot') + exec_command_and_wait_for_pattern(test_rme_instance, 'root', '#') + # query (nested) guest cca report + exec_command(test_rme_instance, 'cca-workload-attestation report') + wait_for_console_pattern(test_rme_instance, + '"cca-platform-hash-algo-id": "sha-256"') + wait_for_console_pattern(test_rme_instance, + '"cca-realm-hash-algo-id": "sha-512"') + wait_for_console_pattern(test_rme_instance, + '"cca-realm-public-key-hash-algo-id": "sha-256"') + +class Aarch64RMEVirtMachine(QemuSystemTest): + + # Stack is built with OP-TEE build environment from those instructions: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-rme-stack + ASSET_RME_STACK_VIRT = Asset( + ('https://fileserver.linaro.org/s/iaRsNDJp2CXHMSJ/' + 'download/rme-stack-op-tee-4.2.0-cca-v4-qemu_v8.tar.gz'), + '1851adc232b094384d8b879b9a2cfff07ef3d6205032b85e9b3a4a9ae6b0b7ad') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_virt(self): + self.set_machine('virt') + self.vm.set_console() + self.require_accelerator('tcg') + + stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-qemu_v8') + kernel = os.path.join(rme_stack, 'out', 'bin', 'Image') + bios = os.path.join(rme_stack, 'out', 'bin', 'flash.bin') + drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'virt,acpi=off,' + 'virtualization=on,' + 'secure=on,' + 'gic-version=3') + self.vm.add_args('-bios', bios) + self.vm.add_args('-kernel', kernel) + self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') + self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') + self.vm.add_args('-device', 'virtio-9p-device,fsdev=shr0,mount_tag=shr0') + self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') + self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') + self.vm.add_args('-netdev', 'user,id=net0') + self.vm.add_args('-append', 'root=/dev/vda') + + self.vm.launch() + # Wait for host VM boot to complete. + wait_for_console_pattern(self, 'Welcome to Buildroot') + exec_command_and_wait_for_pattern(self, 'root', '#') + + test_realms_guest(self) + +if __name__ == '__main__': + QemuSystemTest.main() From 92cb8f8bf67e89254b0e4167521675eb6cd84981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:38 +0000 Subject: [PATCH 0882/2892] tests/qtest: remove clock_steps from virtio tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the qtest environment time will not step forward if the system is paused (timers disabled) or we have no timer events to fire. As a result VirtIO events are responded to directly and we don't need to step time forward. We still do timeout processing to handle the fact the target QEMU may not be ready to respond right away. This will usually be due to a slow CI system or if QEMU is running under something like rr. Future qtest patches will assert that time actually changes when a step is requested. Reviewed-by: Fabiano Rosas Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-17-alex.bennee@linaro.org> --- tests/qtest/libqos/virtio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c index a21b6eee9c..2e7979652f 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -170,7 +170,6 @@ void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d, gint64 start_time = g_get_monotonic_time(); for (;;) { - qtest_clock_step(qts, 100); if (d->bus->get_queue_isr_status(d, vq)) { return; } @@ -192,7 +191,6 @@ uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d, uint8_t val; while ((val = qtest_readb(qts, addr)) == 0xff) { - qtest_clock_step(qts, 100); g_assert(!d->bus->get_queue_isr_status(d, vq)); g_assert(g_get_monotonic_time() - start_time <= timeout_us); } @@ -219,14 +217,12 @@ void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d, for (;;) { uint32_t got_desc_idx; - qtest_clock_step(qts, 100); if (d->bus->get_queue_isr_status(d, vq) && qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) { g_assert_cmpint(got_desc_idx, ==, desc_idx); return; } - g_assert(g_get_monotonic_time() - start_time <= timeout_us); } } From d524441a3610ba33c12b641c00d8e6886d411aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:39 +0000 Subject: [PATCH 0883/2892] system/qtest: properly feedback results of clock_[step|set] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Time will not advance if the system is paused or there are no timer events set for the future. In absence of pending timer events advancing time would make no difference the system state. Attempting to do so would be a bug and the test or device under test would need fixing. Tighten up the result reporting to `FAIL` if time was not advanced. Reviewed-by: Fabiano Rosas Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2687 Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-18-alex.bennee@linaro.org> --- system/qtest.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/system/qtest.c b/system/qtest.c index 99ef2042f6..e68ed0f2a8 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -78,6 +78,11 @@ static void *qtest_server_send_opaque; * let you adjust the value of the clock (monotonically). All the commands * return the current value of the clock in nanoseconds. * + * If the commands FAIL then time wasn't advanced which is likely + * because the machine was in a paused state or no timer events exist + * in the future. This will cause qtest to abort and the test will + * need to check its assumptions. + * * .. code-block:: none * * > clock_step @@ -710,7 +715,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_sendf(chr, "OK little\n"); } } else if (qtest_enabled() && strcmp(words[0], "clock_step") == 0) { - int64_t ns; + int64_t old_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t ns, new_ns; if (words[1]) { int ret = qemu_strtoi64(words[1], NULL, 0, &ns); @@ -719,11 +725,10 @@ static void qtest_process_command(CharBackend *chr, gchar **words) ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); } - qemu_clock_advance_virtual_time( - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns); + new_ns = qemu_clock_advance_virtual_time(old_ns + ns); qtest_send_prefix(chr); - qtest_sendf(chr, "OK %"PRIi64"\n", - (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + qtest_sendf(chr, "%s %"PRIi64"\n", + new_ns > old_ns ? "OK" : "FAIL", new_ns); } else if (strcmp(words[0], "module_load") == 0) { Error *local_err = NULL; int rv; @@ -740,16 +745,16 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_sendf(chr, "FAIL\n"); } } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) { - int64_t ns; + int64_t ns, new_ns; int ret; g_assert(words[1]); ret = qemu_strtoi64(words[1], NULL, 0, &ns); g_assert(ret == 0); - qemu_clock_advance_virtual_time(ns); + new_ns = qemu_clock_advance_virtual_time(ns); qtest_send_prefix(chr); - qtest_sendf(chr, "OK %"PRIi64"\n", - (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + qtest_sendf(chr, "%s %"PRIi64"\n", + new_ns == ns ? "OK" : "FAIL", new_ns); } else if (process_command_cb && process_command_cb(chr, words)) { /* Command got consumed by the callback handler */ } else { From b233de2af7ef97f18297ff63d658e449bad6eee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:40 +0000 Subject: [PATCH 0884/2892] tests/functional: remove hacky sleep from the tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have proper detection of prompts now so we don't need to guess with sleep() sprinkled through the test. The extra step of calling halt is just to flush the final bits of the log (although the last line is still missed). Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-19-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt.py | 32 +++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 08576b0694..2d9995a95d 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -10,12 +10,12 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import time import logging from subprocess import check_call, DEVNULL from qemu_test import QemuSystemTest, Asset -from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern from qemu_test import get_qemu_img @@ -107,18 +107,22 @@ class Aarch64VirtMachine(QemuSystemTest): 'virtio-blk-device,drive=scratch') self.vm.launch() - self.wait_for_console_pattern('Welcome to Buildroot') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command(self, 'dd if=/dev/hwrng of=/dev/vda bs=512 count=4') - time.sleep(0.1) - exec_command(self, 'md5sum /dev/vda') - time.sleep(0.1) - exec_command(self, 'cat /proc/interrupts') - time.sleep(0.1) - exec_command(self, 'cat /proc/self/maps') - time.sleep(0.1) + + ps1='#' + self.wait_for_console_pattern('login:') + + commands = [ + ('root', ps1), + ('cat /proc/interrupts', ps1), + ('cat /proc/self/maps', ps1), + ('uname -a', ps1), + ('dd if=/dev/hwrng of=/dev/vda bs=512 count=4', ps1), + ('md5sum /dev/vda', ps1), + ('halt -n', 'reboot: System halted') + ] + + for cmd, pattern in commands: + exec_command_and_wait_for_pattern(self, cmd, pattern) def test_aarch64_virt_gicv3(self): self.common_aarch64_virt("virt,gic_version=3") From e6c9ab0b1808004ded669fcad93134422c70e62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:41 +0000 Subject: [PATCH 0885/2892] tests/functional: add zstd support to uncompress utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using the python library (which has a different API anyway) lets just call the binary. zstdtools is already in out qemu.yml so all test containers should have it around. Tests should still use @skipIfMissingCommands('zstd') to gracefully handle when only minimal dependencies have been installed. Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-20-alex.bennee@linaro.org> --- tests/functional/qemu_test/uncompress.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py index 6d02ded066..76dcf22385 100644 --- a/tests/functional/qemu_test/uncompress.py +++ b/tests/functional/qemu_test/uncompress.py @@ -10,8 +10,10 @@ import gzip import lzma import os +import stat import shutil from urllib.parse import urlparse +from subprocess import check_call, CalledProcessError from .asset import Asset @@ -38,6 +40,24 @@ def lzma_uncompress(xz_path, output_path): os.remove(output_path) raise + +def zstd_uncompress(zstd_path, output_path): + if os.path.exists(output_path): + return + + try: + check_call(['zstd', "-f", "-d", zstd_path, + "-o", output_path]) + except CalledProcessError as e: + os.remove(output_path) + raise Exception( + f"Unable to decompress zstd file {zstd_path} with {e}") from e + + # zstd copies source archive permissions for the output + # file, so must make this writable for QEMU + os.chmod(output_path, stat.S_IRUSR | stat.S_IWUSR) + + ''' @params compressed: filename, Asset, or file-like object to uncompress @params uncompressed: filename to uncompress into @@ -59,6 +79,8 @@ def uncompress(compressed, uncompressed, format=None): lzma_uncompress(str(compressed), uncompressed) elif format == "gz": gzip_uncompress(str(compressed), uncompressed) + elif format == "zstd": + zstd_uncompress(str(compressed), uncompressed) else: raise Exception(f"Unknown compression format {format}") @@ -79,5 +101,7 @@ def guess_uncompress_format(compressed): return "xz" elif ext == ".gz": return "gz" + elif ext in [".zstd", ".zst"]: + return 'zstd' else: raise Exception(f"Unknown compression format for {compressed}") From 3b9ec25e48310124bed4b5dd8c4f89f8928015d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:42 +0000 Subject: [PATCH 0886/2892] tests/functional: update tuxruntest to use uncompress utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the utility functions to reduce code duplication. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-21-alex.bennee@linaro.org> --- tests/functional/qemu_test/tuxruntest.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index 7227a83757..41a4945a14 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -73,17 +73,7 @@ class TuxRunBaselineTest(QemuSystemTest): Fetch the TuxBoot assets. """ kernel_image = kernel_asset.fetch() - disk_image_zst = rootfs_asset.fetch() - - disk_image = self.scratch_file("rootfs.ext4") - - check_call(['zstd', "-f", "-d", disk_image_zst, - "-o", disk_image], - stdout=DEVNULL, stderr=DEVNULL) - # zstd copies source archive permissions for the output - # file, so must make this writable for QEMU - os.chmod(disk_image, stat.S_IRUSR | stat.S_IWUSR) - + disk_image = self.uncompress(rootfs_asset) dtb = dtb_asset.fetch() if dtb_asset is not None else None return (kernel_image, disk_image, dtb) From f2e116184e723e23f7e0c3099db57e3c53cb3a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:43 +0000 Subject: [PATCH 0887/2892] tests/functional: remove unused kernel_command_line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Alpine test boots from the CDROM so we don't --append a command line. Drop the unused code. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-22-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 2d9995a95d..b3d3b0ee51 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -41,8 +41,6 @@ class Aarch64VirtMachine(QemuSystemTest): self.set_machine('virt') self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') self.require_accelerator("tcg") self.vm.add_args("-accel", "tcg") From 0d3af961f751e0b424828d25e2ca47bc8729485c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:44 +0000 Subject: [PATCH 0888/2892] tests/functional: bail aarch64_virt tests early if missing TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The set_machine and require_accelerator steps can bail early so move those to the front of the test functions. While we are at it also clean up some long lines when adding the vm arguments. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-23-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index b3d3b0ee51..201c5ed023 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -40,9 +40,9 @@ class Aarch64VirtMachine(QemuSystemTest): iso_path = self.ASSET_ALPINE_ISO.fetch() self.set_machine('virt') - self.vm.set_console() self.require_accelerator("tcg") + self.vm.set_console() self.vm.add_args("-accel", "tcg") self.vm.add_args("-cpu", "max,pauth-impdef=on") self.vm.add_args("-machine", @@ -71,15 +71,16 @@ class Aarch64VirtMachine(QemuSystemTest): Common code to launch basic virt machine with kernel+initrd and a scratch disk. """ + self.set_machine('virt') + self.require_accelerator("tcg") + logger = logging.getLogger('aarch64_virt') kernel_path = self.ASSET_KERNEL.fetch() - self.set_machine('virt') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') - self.require_accelerator("tcg") self.vm.add_args('-cpu', 'max,pauth-impdef=on', '-machine', machine, '-accel', 'tcg', @@ -100,7 +101,9 @@ class Aarch64VirtMachine(QemuSystemTest): # Add the device self.vm.add_args('-blockdev', - f"driver=qcow2,file.driver=file,file.filename={image_path},node-name=scratch") + "driver=qcow2," + "file.driver=file," + f"file.filename={image_path},node-name=scratch") self.vm.add_args('-device', 'virtio-blk-device,drive=scratch') From 1d03e9771e05685e11bbd3cc8cdd072c02cf580d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Jan 2025 16:52:25 +0100 Subject: [PATCH 0889/2892] rust: add --check-cfg test to rustc arguments rustc will check that every reachable #[cfg] matches a list of the expected config names and values. Recent versions of rustc are also complaining about #[cfg(test)], even if it is basically a standard part of the language. So, always allow it. Signed-off-by: Paolo Bonzini --- scripts/rust/rustc_args.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 5525b3886f..2633157df2 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -215,6 +215,8 @@ def main() -> None: if rustc_version >= (1, 80): if args.lints: + print("--check-cfg") + print("cfg(test)") for cfg in sorted(cargo_toml.check_cfg): print("--check-cfg") print(cfg) From ca0d60a6ad777ab617cbc4e6f328eaff60617b3f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Dec 2024 11:38:20 +0100 Subject: [PATCH 0890/2892] rust: qom: add ParentField Add a type that, together with the C function object_deinit, ensures the correct drop order for QOM objects relative to their superclasses. Right now it is not possible to implement the Drop trait for QOM classes that are defined in Rust, as the drop() function would not be called when the object goes away; instead what is called is ObjectImpl::INSTANCE_FINALIZE. It would be nice for INSTANCE_FINALIZE to just drop the object, but this has a problem: suppose you have pub struct MySuperclass { parent: DeviceState, field: Box, ... } impl Drop for MySuperclass { ... } pub struct MySubclass { parent: MySuperclass, ... } and an instance_finalize implementation that is like unsafe extern "C" fn drop_object(obj: *mut Object) { unsafe { std::ptr::drop_in_place(obj.cast::()) } } When instance_finalize is called for MySubclass, it will walk the struct's list of fields and call the drop method for MySuperclass. Then, object_deinit recurses to the superclass and calls the same drop method again. This will cause double-freeing of the Box. What's happening here is that QOM wants to control the drop order of MySuperclass and MySubclass's fields. To do so, the parent field must be marked ManuallyDrop<>, which is quite ugly. Instead, add a wrapper type ParentField<> that is specific to QOM. This hides the implementation detail of *what* is special about the ParentField, and will also be easy to check in the #[derive(Object)] macro. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 +-- rust/qemu-api/src/qom.rs | 64 +++++++++++++++++++++++++++++--- rust/qemu-api/tests/tests.rs | 4 +- 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 18cc122951..689202f455 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -14,7 +14,7 @@ use qemu_api::{ irq::InterruptSource, prelude::*, qdev::DeviceImpl, - qom::ObjectImpl, + qom::{ObjectImpl, ParentField}, }; use crate::{ @@ -86,7 +86,7 @@ impl std::ops::Index for Fifo { #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] /// PL011 Device Model in QEMU pub struct PL011State { - pub parent_obj: SysBusDevice, + pub parent_obj: ParentField, pub iomem: MemoryRegion, #[doc(alias = "fr")] pub flags: registers::Flags, @@ -645,7 +645,7 @@ pub unsafe extern "C" fn pl011_create( #[derive(Debug, qemu_api_macros::Object)] /// PL011 Luminary device model. pub struct PL011Luminary { - parent_obj: PL011State, + parent_obj: ParentField, } impl PL011Luminary { diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 7d5fbef1e1..40d17a92e1 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -55,6 +55,7 @@ use std::{ ffi::CStr, + fmt, ops::{Deref, DerefMut}, os::raw::c_void, }; @@ -105,6 +106,52 @@ macro_rules! qom_isa { }; } +/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though +/// it hides the standard methods of `ManuallyDrop`. +/// +/// The first field of an `ObjectType` must be of type `ParentField`. +/// (Technically, this is only necessary if there is at least one Rust +/// superclass in the hierarchy). This is to ensure that the parent field is +/// dropped after the subclass; this drop order is enforced by the C +/// `object_deinit` function. +/// +/// # Examples +/// +/// ```ignore +/// #[repr(C)] +/// #[derive(qemu_api_macros::Object)] +/// pub struct MyDevice { +/// parent: ParentField, +/// ... +/// } +/// ``` +#[derive(Debug)] +#[repr(transparent)] +pub struct ParentField(std::mem::ManuallyDrop); + +impl Deref for ParentField { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ParentField { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for ParentField { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + unsafe extern "C" fn rust_instance_init(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init // is called from QOM core as the instance_init function @@ -151,11 +198,16 @@ unsafe extern "C" fn rust_class_init>( /// /// - the struct must be `#[repr(C)]`; /// -/// - the first field of the struct must be of the instance struct corresponding -/// to the superclass, which is `ObjectImpl::ParentType` +/// - the first field of the struct must be of type +/// [`ParentField`](ParentField), where `T` is the parent type +/// [`ObjectImpl::ParentType`] /// -/// - likewise, the first field of the `Class` must be of the class struct -/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. +/// - the first field of the `Class` must be of the class struct corresponding +/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` +/// is not needed here. +/// +/// In both cases, having a separate class type is not necessary if the subclass +/// does not add any field. pub unsafe trait ObjectType: Sized { /// The QOM class object corresponding to this struct. This is used /// to automatically generate a `class_init` method. @@ -384,8 +436,8 @@ impl ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. pub trait ObjectImpl: ObjectType + ClassInitImpl { - /// The parent of the type. This should match the first field of - /// the struct that implements `ObjectImpl`: + /// The parent of the type. This should match the first field of the + /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. type ParentType: ObjectType; /// Whether the object can be instantiated diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 1d2825b098..526c3f4f8e 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -15,7 +15,7 @@ use qemu_api::{ declare_properties, define_property, prelude::*, qdev::{DeviceImpl, DeviceState, Property}, - qom::ObjectImpl, + qom::{ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -31,7 +31,7 @@ pub static VMSTATE: VMStateDescription = VMStateDescription { #[repr(C)] #[derive(qemu_api_macros::Object)] pub struct DummyState { - parent: DeviceState, + parent: ParentField, migrate_clock: bool, } From 7f65d4e58b67d6e5ee05c9381a50ef1eba3a5a1e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Dec 2024 12:18:06 +0100 Subject: [PATCH 0891/2892] rust: add a utility module for compile-time type checks It is relatively common in the low-level qemu_api code to assert that a field of a struct has a specific type; for example, it can be used to ensure that the fields match what the qemu_api and C code expects for safety. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/assertions.rs | 90 +++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + 3 files changed, 92 insertions(+) create mode 100644 rust/qemu-api/src/assertions.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 9425ba7100..60944a657d 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -15,6 +15,7 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', + 'src/assertions.rs', 'src/bindings.rs', 'src/bitops.rs', 'src/callbacks.rs', diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs new file mode 100644 index 0000000000..6e42046980 --- /dev/null +++ b/rust/qemu-api/src/assertions.rs @@ -0,0 +1,90 @@ +// Copyright 2024, Red Hat Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This module provides macros to check the equality of types and +//! the type of `struct` fields. This can be useful to ensure that +//! types match the expectations of C code. + +// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 +// (stackoverflow answers are released under MIT license). + +#[doc(hidden)] +pub trait EqType { + type Itself; +} + +impl EqType for T { + type Itself = T; +} + +/// Assert that two types are the same. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_same_type; +/// # use std::ops::Deref; +/// assert_same_type!(u32, u32); +/// assert_same_type!( as Deref>::Target, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use qemu_api::assert_same_type; +/// assert_same_type!(&Box, &u32); +/// ``` +#[macro_export] +macro_rules! assert_same_type { + ($t1:ty, $t2:ty) => { + const _: () = { + #[allow(unused)] + fn assert_same_type(v: $t1) { + fn types_must_be_equal(_: T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $t2>(v); + } + }; + }; +} + +/// Assert that a field of a struct has the given type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_field_type; +/// pub struct A { +/// field1: u32, +/// } +/// +/// assert_field_type!(A, field1, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use qemu_api::assert_field_type; +/// # pub struct A { field1: u32 } +/// assert_field_type!(A, field1, i32); +/// ``` +#[macro_export] +macro_rules! assert_field_type { + ($t:ty, $i:tt, $ti:ty) => { + const _: () = { + #[allow(unused)] + fn assert_field_type(v: $t) { + fn types_must_be_equal(_: T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $ti>(v.$i); + } + }; + }; +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 4b43e02c0f..83c6a987c0 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -12,6 +12,7 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; +pub mod assertions; pub mod bitops; pub mod c_str; pub mod callbacks; From 20f0b8e98b4851bfd52bbb4edd4b602d08b9b817 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 24 Oct 2024 11:57:02 +0200 Subject: [PATCH 0892/2892] rust: macros: check that #[derive(Object)] requires #[repr(C)] Convert derive_object to the same pattern of first making a Result, and then doing .unwrap_or_else(Into::into) to support checking the validity of the input. Add is_c_repr to check that all QOM structs include a #[repr(C)] attribute. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 74a8bc7503..160b283d7f 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -32,18 +32,23 @@ fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> { } } -#[proc_macro_derive(Object)] -pub fn derive_object(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let name = input.ident; +fn derive_object_or_error(input: DeriveInput) -> Result { + is_c_repr(&input, "#[derive(Object)]")?; - let expanded = quote! { + let name = &input.ident; + Ok(quote! { ::qemu_api::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); } } - }; + }) +} + +#[proc_macro_derive(Object)] +pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); TokenStream::from(expanded) } From e3ff5a17aa9fc2420197403e69876db48e390ee4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Dec 2024 11:48:44 +0100 Subject: [PATCH 0893/2892] rust: macros: check that the first field of a #[derive(Object)] struct is a ParentField Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 46 +++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 160b283d7f..0f04cca384 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -19,6 +19,27 @@ impl From for proc_macro2::TokenStream { } } +fn get_fields<'a>( + input: &'a DeriveInput, + msg: &str, +) -> Result<&'a Punctuated, CompileError> { + if let Data::Struct(s) = &input.data { + if let Fields::Named(fs) = &s.fields { + Ok(&fs.named) + } else { + Err(CompileError( + format!("Named fields required for {}", msg), + input.ident.span(), + )) + } + } else { + Err(CompileError( + format!("Struct required for {}", msg), + input.ident.span(), + )) + } +} + fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> { let expected = parse_quote! { #[repr(C)] }; @@ -36,7 +57,12 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); + ::qemu_api::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); @@ -53,30 +79,12 @@ pub fn derive_object(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } -fn get_fields(input: &DeriveInput) -> Result<&Punctuated, CompileError> { - if let Data::Struct(s) = &input.data { - if let Fields::Named(fs) = &s.fields { - Ok(&fs.named) - } else { - Err(CompileError( - "Cannot generate offsets for unnamed fields.".to_string(), - input.ident.span(), - )) - } - } else { - Err(CompileError( - "Cannot generate offsets for union or enum.".to_string(), - input.ident.span(), - )) - } -} - #[rustfmt::skip::macros(quote)] fn derive_offsets_or_error(input: DeriveInput) -> Result { is_c_repr(&input, "#[derive(offsets)]")?; let name = &input.ident; - let fields = get_fields(&input)?; + let fields = get_fields(&input, "#[derive(offsets)]")?; let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect(); let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect(); let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect(); From 33aa660575f7174752a7308229e5fc108921c2a6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Dec 2024 10:33:31 +0100 Subject: [PATCH 0894/2892] rust: qom: automatically use Drop trait to implement instance_finalize Replace the customizable INSTANCE_FINALIZE with a generic function that drops the Rust object. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 40d17a92e1..b0332ba247 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -180,6 +180,16 @@ unsafe extern "C" fn rust_class_init>( T::class_init(unsafe { &mut *klass.cast::() }) } +unsafe extern "C" fn drop_object(obj: *mut Object) { + // SAFETY: obj is an instance of T, since drop_object is called + // from the QOM core function object_deinit() as the instance_finalize + // function for class T. Note that while object_deinit() will drop the + // superclass field separately after this function returns, `T` must + // implement the unsafe trait ObjectType; the safety rules for the + // trait mandate that the parent field is manually dropped. + unsafe { std::ptr::drop_in_place(obj.cast::()) } +} + /// Trait exposed by all structs corresponding to QOM objects. /// /// # Safety @@ -442,7 +452,6 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// Whether the object can be instantiated const ABSTRACT: bool = false; - const INSTANCE_FINALIZE: Option = None; /// Function that is called to initialize an object. The parent class will /// have already been initialized so the type is only responsible for @@ -478,7 +487,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { None => None, Some(_) => Some(rust_instance_post_init::), }, - instance_finalize: Self::INSTANCE_FINALIZE, + instance_finalize: Some(drop_object::), abstract_: Self::ABSTRACT, class_size: core::mem::size_of::(), class_init: Some(rust_class_init::), From d9434f29ca83e114fe02ed24c8ad2ccfa7ac3fe9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Nov 2024 11:38:59 +0100 Subject: [PATCH 0895/2892] rust: qom: move device_id to PL011 class side There is no need to monkeypatch DeviceId::Luminary into the already-initialized PL011State. Instead, now that we can define a class hierarchy, we can define PL011Class and make device_id a field in there. There is also no need anymore to have "Arm" as zero, so change DeviceId into a wrapper for the array; all it does is provide an Index implementation because arrays can only be indexed by usize. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 59 +++++++++++++++----------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 689202f455..215f94a6e4 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,7 +5,7 @@ use core::ptr::{addr_of_mut, NonNull}; use std::{ ffi::CStr, - os::raw::{c_int, c_uchar, c_uint, c_void}, + os::raw::{c_int, c_uint, c_void}, }; use qemu_api::{ @@ -14,7 +14,7 @@ use qemu_api::{ irq::InterruptSource, prelude::*, qdev::DeviceImpl, - qom::{ObjectImpl, ParentField}, + qom::{ClassInitImpl, ObjectImpl, ParentField}, }; use crate::{ @@ -33,27 +33,20 @@ const FBRD_MASK: u32 = 0x3f; /// QEMU sourced constant. pub const PL011_FIFO_DEPTH: u32 = 16; -#[derive(Clone, Copy, Debug)] -enum DeviceId { - #[allow(dead_code)] - Arm = 0, - Luminary, -} +#[derive(Clone, Copy)] +struct DeviceId(&'static [u8; 8]); impl std::ops::Index for DeviceId { - type Output = c_uchar; + type Output = u8; fn index(&self, idx: hwaddr) -> &Self::Output { - match self { - Self::Arm => &Self::PL011_ID_ARM[idx as usize], - Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize], - } + &self.0[idx as usize] } } impl DeviceId { - const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; - const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]; + const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); + const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); } // FIFOs use 32-bit indices instead of usize, for compatibility with @@ -126,17 +119,28 @@ pub struct PL011State { pub clock: NonNull, #[doc(alias = "migrate_clk")] pub migrate_clock: bool, - /// The byte string that identifies the device. - device_id: DeviceId, } qom_isa!(PL011State : SysBusDevice, DeviceState, Object); +pub struct PL011Class { + parent_class: ::Class, + /// The byte string that identifies the device. + device_id: DeviceId, +} + unsafe impl ObjectType for PL011State { - type Class = ::Class; + type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } +impl ClassInitImpl for PL011State { + fn class_init(klass: &mut PL011Class) { + klass.device_id = DeviceId::ARM; + >::class_init(&mut klass.parent_class); + } +} + impl ObjectImpl for PL011State { type ParentType = SysBusDevice; @@ -214,7 +218,8 @@ impl PL011State { let value = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - u32::from(self.device_id[(offset - 0xfe0) >> 2]) + let device_id = self.get_class().device_id; + u32::from(device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); @@ -648,16 +653,10 @@ pub struct PL011Luminary { parent_obj: ParentField, } -impl PL011Luminary { - /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. - /// - /// # Safety - /// - /// We expect the FFI user of this function to pass a valid pointer, that - /// has the same size as [`PL011Luminary`]. We also expect the device is - /// readable/writeable from one thread at any time. - unsafe fn init(&mut self) { - self.parent_obj.device_id = DeviceId::Luminary; +impl ClassInitImpl for PL011Luminary { + fn class_init(klass: &mut PL011Class) { + klass.device_id = DeviceId::LUMINARY; + >::class_init(&mut klass.parent_class); } } @@ -670,8 +669,6 @@ unsafe impl ObjectType for PL011Luminary { impl ObjectImpl for PL011Luminary { type ParentType = PL011State; - - const INSTANCE_INIT: Option = Some(Self::init); } impl DeviceImpl for PL011Luminary {} From af68b41d403b81b18de07ebab0ca4c1025c94bf7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Dec 2024 13:16:19 +0100 Subject: [PATCH 0896/2892] rust: pl011: only leave embedded object initialization in instance_init Leave IRQ and MMIO initialization to instance_post_init. In Rust the two callbacks are more distinct, because only instance_post_init has a fully initialized object available. While at it, add a wrapper for sysbus_init_mmio so that accesses to the SysBusDevice correctly use shared references. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 18 ++++++++++-------- rust/qemu-api/src/sysbus.rs | 12 ++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 215f94a6e4..72a4cea042 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -145,6 +145,7 @@ impl ObjectImpl for PL011State { type ParentType = SysBusDevice; const INSTANCE_INIT: Option = Some(Self::init); + const INSTANCE_POST_INIT: Option = Some(Self::post_init); } impl DeviceImpl for PL011State { @@ -183,14 +184,6 @@ impl PL011State { Self::TYPE_NAME.as_ptr(), 0x1000, ); - - let sbd: &mut SysBusDevice = self.upcast_mut(); - sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); - } - - for irq in self.interrupts.iter() { - let sbd: &SysBusDevice = self.upcast(); - sbd.init_irq(irq); } // SAFETY: @@ -213,6 +206,15 @@ impl PL011State { } } + fn post_init(&mut self) { + let sbd: &SysBusDevice = self.upcast(); + + sbd.init_mmio(&self.iomem); + for irq in self.interrupts.iter() { + sbd.init_irq(irq); + } + } + pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow { use RegisterOffset::*; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 8193734bde..b96eaaf25f 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -38,6 +38,18 @@ impl SysBusDevice { addr_of!(*self) as *mut _ } + /// Expose a memory region to the board so that it can give it an address + /// in guest memory. Note that the ordering of calls to `init_mmio` is + /// important, since whoever creates the sysbus device will refer to the + /// region with a number that corresponds to the order of calls to + /// `init_mmio`. + pub fn init_mmio(&self, iomem: &bindings::MemoryRegion) { + assert!(bql_locked()); + unsafe { + bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _); + } + } + /// Expose an interrupt source outside the device as a qdev GPIO output. /// Note that the ordering of calls to `init_irq` is important, since /// whoever creates the sysbus device will refer to the interrupts with From 22a18f0a98a1ca5d0cd8fff1d81cc74a269083de Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Nov 2024 08:48:07 +0100 Subject: [PATCH 0897/2892] rust: qom: make INSTANCE_POST_INIT take a shared reference Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 ++-- rust/qemu-api/src/qom.rs | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 72a4cea042..6792d13fb7 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -145,7 +145,7 @@ impl ObjectImpl for PL011State { type ParentType = SysBusDevice; const INSTANCE_INIT: Option = Some(Self::init); - const INSTANCE_POST_INIT: Option = Some(Self::post_init); + const INSTANCE_POST_INIT: Option = Some(Self::post_init); } impl DeviceImpl for PL011State { @@ -206,7 +206,7 @@ impl PL011State { } } - fn post_init(&mut self) { + fn post_init(&self) { let sbd: &SysBusDevice = self.upcast(); sbd.init_mmio(&self.iomem); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index b0332ba247..97901fb908 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -163,11 +163,7 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_post_init // is called from QOM core as the instance_post_init function // for class T - // - // FIXME: it's not really guaranteed that there are no backpointers to - // obj; it's quite possible that they have been created by instance_init(). - // The receiver should be &self, not &mut self. - T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::() }) + T::INSTANCE_POST_INIT.unwrap()(unsafe { &*obj.cast::() }) } unsafe extern "C" fn rust_class_init>( @@ -463,7 +459,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// Function that is called to finish initialization of an object, once /// `INSTANCE_INIT` functions have been called. - const INSTANCE_POST_INIT: Option = None; + const INSTANCE_POST_INIT: Option = None; /// Called on descendent classes after all parent class initialization /// has occurred, but before the class itself is initialized. This From a3b620fff73ec762f2a77a077eb8389dddab4833 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2024 08:57:27 +0100 Subject: [PATCH 0898/2892] rust: qemu-api-macros: extend error reporting facility to parse errors Generalize the CompileError tuple to an enum, that can be either an error message or a parse error from syn. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 27 ++++++++++----------------- rust/qemu-api-macros/src/utils.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 rust/qemu-api-macros/src/utils.rs diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 0f04cca384..539c48df29 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -3,57 +3,50 @@ // SPDX-License-Identifier: GPL-2.0-or-later use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::{quote, quote_spanned}; +use quote::quote; use syn::{ parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, Fields, Ident, Type, Visibility, }; -struct CompileError(String, Span); - -impl From for proc_macro2::TokenStream { - fn from(err: CompileError) -> Self { - let CompileError(msg, span) = err; - quote_spanned! { span => compile_error!(#msg); } - } -} +mod utils; +use utils::MacroError; fn get_fields<'a>( input: &'a DeriveInput, msg: &str, -) -> Result<&'a Punctuated, CompileError> { +) -> Result<&'a Punctuated, MacroError> { if let Data::Struct(s) = &input.data { if let Fields::Named(fs) = &s.fields { Ok(&fs.named) } else { - Err(CompileError( + Err(MacroError::Message( format!("Named fields required for {}", msg), input.ident.span(), )) } } else { - Err(CompileError( + Err(MacroError::Message( format!("Struct required for {}", msg), input.ident.span(), )) } } -fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> { +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { let expected = parse_quote! { #[repr(C)] }; if input.attrs.iter().any(|attr| attr == &expected) { Ok(()) } else { - Err(CompileError( + Err(MacroError::Message( format!("#[repr(C)] required for {}", msg), input.ident.span(), )) } } -fn derive_object_or_error(input: DeriveInput) -> Result { +fn derive_object_or_error(input: DeriveInput) -> Result { is_c_repr(&input, "#[derive(Object)]")?; let name = &input.ident; @@ -80,7 +73,7 @@ pub fn derive_object(input: TokenStream) -> TokenStream { } #[rustfmt::skip::macros(quote)] -fn derive_offsets_or_error(input: DeriveInput) -> Result { +fn derive_offsets_or_error(input: DeriveInput) -> Result { is_c_repr(&input, "#[derive(offsets)]")?; let name = &input.ident; diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs new file mode 100644 index 0000000000..02c91aed7f --- /dev/null +++ b/rust/qemu-api-macros/src/utils.rs @@ -0,0 +1,26 @@ +// Procedural macro utilities. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro2::Span; +use quote::quote_spanned; + +pub enum MacroError { + Message(String, Span), + ParseError(syn::Error), +} + +impl From for MacroError { + fn from(err: syn::Error) -> Self { + MacroError::ParseError(err) + } +} + +impl From for proc_macro2::TokenStream { + fn from(err: MacroError) -> Self { + match err { + MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); }, + MacroError::ParseError(err) => err.into_compile_error(), + } + } +} From 809c703a60240125eec16ec134f60793134b4f61 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2024 08:58:46 +0100 Subject: [PATCH 0899/2892] rust: qemu-api-macros: add automatic TryFrom/TryInto derivation This is going to be fairly common. Using a custom procedural macro provides better error messages and automatically finds the right type. Note that this is different from the same-named macro in the derive_more crate. That one provides conversion from e.g. tuples to enums with tuple variants, not from integers to enums. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/lib.rs | 28 +------------ rust/qemu-api-macros/src/lib.rs | 74 ++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 69064d6929..0a89d393e0 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -45,7 +45,7 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug)] +#[derive(Debug, qemu_api_macros::TryInto)] pub enum RegisterOffset { /// Data Register /// @@ -102,32 +102,6 @@ pub enum RegisterOffset { //Reserved = 0x04C, } -impl core::convert::TryFrom for RegisterOffset { - type Error = u64; - - fn try_from(value: u64) -> Result { - macro_rules! case { - ($($discriminant:ident),*$(,)*) => { - /* check that matching on all macro arguments compiles, which means we are not - * missing any enum value; if the type definition ever changes this will stop - * compiling. - */ - const fn _assert_exhaustive(val: RegisterOffset) { - match val { - $(RegisterOffset::$discriminant => (),)* - } - } - - match value { - $(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)* - _ => Err(value), - } - } - } - case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR } - } -} - pub mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 539c48df29..7ec218202f 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -5,8 +5,8 @@ use proc_macro::TokenStream; use quote::quote; use syn::{ - parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, - Fields, Ident, Type, Visibility, + parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, + DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility, }; mod utils; @@ -98,3 +98,73 @@ pub fn derive_offsets(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } + +#[allow(non_snake_case)] +fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { + let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); + if let Some(repr) = repr { + let nested = repr.parse_args_with(Punctuated::::parse_terminated)?; + for meta in nested { + match meta { + Meta::Path(path) if path.is_ident("u8") => return Ok(path), + Meta::Path(path) if path.is_ident("u16") => return Ok(path), + Meta::Path(path) if path.is_ident("u32") => return Ok(path), + Meta::Path(path) if path.is_ident("u64") => return Ok(path), + _ => {} + } + } + } + + Err(MacroError::Message( + format!("#[repr(u8/u16/u32/u64) required for {}", msg), + input.ident.span(), + )) +} + +fn get_variants(input: &DeriveInput) -> Result<&Punctuated, MacroError> { + if let Data::Enum(e) = &input.data { + if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { + return Err(MacroError::Message( + "Cannot derive TryInto for enum with non-unit variants.".to_string(), + v.fields.span(), + )); + } + Ok(&e.variants) + } else { + Err(MacroError::Message( + "Cannot derive TryInto for union or struct.".to_string(), + input.ident.span(), + )) + } +} + +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_or_error(input: DeriveInput) -> Result { + let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; + + let name = &input.ident; + let variants = get_variants(&input)?; + let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); + + Ok(quote! { + impl core::convert::TryFrom<#repr> for #name { + type Error = #repr; + + fn try_from(value: #repr) -> Result { + #(const #discriminants: #repr = #name::#discriminants as #repr;)*; + match value { + #(#discriminants => Ok(Self::#discriminants),)* + _ => Err(value), + } + } + } + }) +} + +#[proc_macro_derive(TryInto)] +pub fn derive_tryinto(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} From 559a779c6aa309853474240b01fcc2beff1f04ca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Nov 2024 10:46:44 +0100 Subject: [PATCH 0900/2892] rust: qdev: expose inherited methods to subclasses of SysBusDevice The ObjectDeref trait now provides all the magic that is required to fake inheritance. Replace the "impl SysBusDevice" block of qemu_api::sysbus with a trait, so that sysbus_init_irq() can be invoked as "self.init_irq()" without any intermediate upcast. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ++---- rust/qemu-api/src/irq.rs | 3 +-- rust/qemu-api/src/prelude.rs | 2 ++ rust/qemu-api/src/sysbus.rs | 17 +++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 6792d13fb7..994c2fc059 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -207,11 +207,9 @@ impl PL011State { } fn post_init(&self) { - let sbd: &SysBusDevice = self.upcast(); - - sbd.init_mmio(&self.iomem); + self.init_mmio(&self.iomem); for irq in self.interrupts.iter() { - sbd.init_irq(irq); + self.init_irq(irq); } } diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 6258141bdf..378e520295 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -24,8 +24,7 @@ use crate::{ /// /// Interrupts are implemented as a pointer to the interrupt "sink", which has /// type [`IRQState`]. A device exposes its source as a QOM link property using -/// a function such as -/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and +/// a function such as [`SysBusDeviceMethods::init_irq`], and /// initially leaves the pointer to a NULL value, representing an unconnected /// interrupt. To connect it, whoever creates the device fills the pointer with /// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 6f32deeb2e..4ea70b9c82 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -16,3 +16,5 @@ pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; pub use crate::qom_isa; + +pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b96eaaf25f..e6762b5c14 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -32,18 +32,17 @@ where } } -impl SysBusDevice { - /// Return `self` cast to a mutable pointer, for use in calls to C code. - const fn as_mut_ptr(&self) -> *mut SysBusDevice { - addr_of!(*self) as *mut _ - } - +/// Trait for methods of [`SysBusDevice`] and its subclasses. +pub trait SysBusDeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ /// Expose a memory region to the board so that it can give it an address /// in guest memory. Note that the ordering of calls to `init_mmio` is /// important, since whoever creates the sysbus device will refer to the /// region with a number that corresponds to the order of calls to /// `init_mmio`. - pub fn init_mmio(&self, iomem: &bindings::MemoryRegion) { + fn init_mmio(&self, iomem: &bindings::MemoryRegion) { assert!(bql_locked()); unsafe { bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _); @@ -54,10 +53,12 @@ impl SysBusDevice { /// Note that the ordering of calls to `init_irq` is important, since /// whoever creates the sysbus device will refer to the interrupts with /// a number that corresponds to the order of calls to `init_irq`. - pub fn init_irq(&self, irq: &InterruptSource) { + fn init_irq(&self, irq: &InterruptSource) { assert!(bql_locked()); unsafe { bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr()); } } } + +impl SysBusDeviceMethods for R where R::Target: IsA {} From d2c12785be06da708fe1c8e4e81d7134f4f3c56a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 13:10:03 +0100 Subject: [PATCH 0901/2892] rust: hide warnings for subprojects This matches cargo's own usage of "--cap-lints allow" when building dependencies. The dummy changes to the .wrap files help Meson notice that the subproject is out of date. Also remove an unnecessary subprojects/unicode-ident-1-rs/meson.build file. Signed-off-by: Paolo Bonzini --- subprojects/arbitrary-int-1-rs.wrap | 3 +++ subprojects/bilge-0.2-rs.wrap | 3 +++ subprojects/bilge-impl-0.2-rs.wrap | 3 +++ subprojects/either-1-rs.wrap | 3 +++ subprojects/itertools-0.11-rs.wrap | 3 +++ .../arbitrary-int-1-rs/meson.build | 1 + .../packagefiles/bilge-0.2-rs/meson.build | 1 + .../bilge-impl-0.2-rs/meson.build | 1 + .../packagefiles/either-1-rs/meson.build | 1 + .../itertools-0.11-rs/meson.build | 1 + .../proc-macro-error-1-rs/meson.build | 1 + .../proc-macro-error-attr-1-rs/meson.build | 1 + .../packagefiles/proc-macro2-1-rs/meson.build | 1 + .../packagefiles/quote-1-rs/meson.build | 1 + subprojects/packagefiles/syn-2-rs/meson.build | 1 + .../unicode-ident-1-rs/meson.build | 1 + subprojects/proc-macro-error-1-rs.wrap | 3 +++ subprojects/proc-macro-error-attr-1-rs.wrap | 3 +++ subprojects/proc-macro2-1-rs.wrap | 3 +++ subprojects/quote-1-rs.wrap | 3 +++ subprojects/syn-2-rs.wrap | 3 +++ subprojects/unicode-ident-1-rs.wrap | 3 +++ subprojects/unicode-ident-1-rs/meson.build | 20 ------------------- 23 files changed, 44 insertions(+), 20 deletions(-) delete mode 100644 subprojects/unicode-ident-1-rs/meson.build diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap index e580538a87..a1838b20b0 100644 --- a/subprojects/arbitrary-int-1-rs.wrap +++ b/subprojects/arbitrary-int-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = arbitrary-int-1.2.7.tar.gz source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d #method = cargo patch_directory = arbitrary-int-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap index 7a4339d298..900bb1497b 100644 --- a/subprojects/bilge-0.2-rs.wrap +++ b/subprojects/bilge-0.2-rs.wrap @@ -5,3 +5,6 @@ source_filename = bilge-0.2.0.tar.gz source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 #method = cargo patch_directory = bilge-0.2-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap index b24c34a904..d14c3dc769 100644 --- a/subprojects/bilge-impl-0.2-rs.wrap +++ b/subprojects/bilge-impl-0.2-rs.wrap @@ -6,3 +6,6 @@ source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 #method = cargo patch_directory = bilge-impl-0.2-rs diff_files = bilge-impl-1.63.0.patch + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap index 6046712036..352e11cfee 100644 --- a/subprojects/either-1-rs.wrap +++ b/subprojects/either-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = either-1.12.0.tar.gz source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b #method = cargo patch_directory = either-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap index 66b05252cd..ee12d0053b 100644 --- a/subprojects/itertools-0.11-rs.wrap +++ b/subprojects/itertools-0.11-rs.wrap @@ -5,3 +5,6 @@ source_filename = itertools-0.11.0.tar.gz source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 #method = cargo patch_directory = itertools-0.11-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build index cff3f62ce7..00733d1faa 100644 --- a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build +++ b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build @@ -9,6 +9,7 @@ _arbitrary_int_rs = static_library( files('src/lib.rs'), gnu_symbol_visibility: 'hidden', override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--cap-lints', 'allow'], rust_abi: 'rust', dependencies: [], ) diff --git a/subprojects/packagefiles/bilge-0.2-rs/meson.build b/subprojects/packagefiles/bilge-0.2-rs/meson.build index e69bac91b4..ce13d0fe80 100644 --- a/subprojects/packagefiles/bilge-0.2-rs/meson.build +++ b/subprojects/packagefiles/bilge-0.2-rs/meson.build @@ -17,6 +17,7 @@ lib = static_library( 'src/lib.rs', override_options : ['rust_std=2021', 'build.rust_std=2021'], rust_abi : 'rust', + rust_args: ['--cap-lints', 'allow'], dependencies: [ arbitrary_int_dep, bilge_impl_dep, diff --git a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build index f8f3486fc0..42b03dcd53 100644 --- a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build +++ b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build @@ -25,6 +25,7 @@ _bilge_impl_rs = rust.proc_macro( files('src/lib.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: [ + '--cap-lints', 'allow', '--cfg', 'use_fallback', '--cfg', 'feature="syn-error"', '--cfg', 'feature="proc-macro"', diff --git a/subprojects/packagefiles/either-1-rs/meson.build b/subprojects/packagefiles/either-1-rs/meson.build index 608e64e31f..04c96cc5fb 100644 --- a/subprojects/packagefiles/either-1-rs/meson.build +++ b/subprojects/packagefiles/either-1-rs/meson.build @@ -11,6 +11,7 @@ _either_rs = static_library( override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="use_std"', '--cfg', 'feature="use_alloc"', ], diff --git a/subprojects/packagefiles/itertools-0.11-rs/meson.build b/subprojects/packagefiles/itertools-0.11-rs/meson.build index 30982a4ee7..2a3fbe9ee5 100644 --- a/subprojects/packagefiles/itertools-0.11-rs/meson.build +++ b/subprojects/packagefiles/itertools-0.11-rs/meson.build @@ -15,6 +15,7 @@ _itertools_rs = static_library( override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="use_std"', '--cfg', 'feature="use_alloc"', ], diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build index ae27a69686..10c2741085 100644 --- a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build @@ -20,6 +20,7 @@ _proc_macro_error_rs = static_library( override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'use_fallback', '--cfg', 'feature="syn-error"', '--cfg', 'feature="proc-macro"', diff --git a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build index 3281b26433..c4c4c5e397 100644 --- a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build @@ -16,6 +16,7 @@ _proc_macro_error_attr_rs = rust.proc_macro( files('src/lib.rs'), override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_args: [ + '--cap-lints', 'allow', '--cfg', 'use_fallback', '--cfg', 'feature="syn-error"', '--cfg', 'feature="proc-macro"' diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build index f9c8675eba..5759df3ecc 100644 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -15,6 +15,7 @@ _proc_macro2_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="proc-macro"', '--cfg', 'no_literal_byte_character', '--cfg', 'no_literal_c_string', diff --git a/subprojects/packagefiles/quote-1-rs/meson.build b/subprojects/packagefiles/quote-1-rs/meson.build index 7f7792569b..bf41fad99b 100644 --- a/subprojects/packagefiles/quote-1-rs/meson.build +++ b/subprojects/packagefiles/quote-1-rs/meson.build @@ -15,6 +15,7 @@ _quote_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="proc-macro"', ], dependencies: [ diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build index 2c62cf7e1b..a009417408 100644 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -19,6 +19,7 @@ _syn_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="full"', '--cfg', 'feature="derive"', '--cfg', 'feature="parsing"', diff --git a/subprojects/packagefiles/unicode-ident-1-rs/meson.build b/subprojects/packagefiles/unicode-ident-1-rs/meson.build index 9d76ebbd1a..11a5dab97d 100644 --- a/subprojects/packagefiles/unicode-ident-1-rs/meson.build +++ b/subprojects/packagefiles/unicode-ident-1-rs/meson.build @@ -10,6 +10,7 @@ _unicode_ident_rs = static_library( gnu_symbol_visibility: 'hidden', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', + rust_args: ['--cap-lints', 'allow'], dependencies: [], native: true, ) diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap index b7db03b06a..59f892f782 100644 --- a/subprojects/proc-macro-error-1-rs.wrap +++ b/subprojects/proc-macro-error-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = proc-macro-error-1.0.4.tar.gz source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c #method = cargo patch_directory = proc-macro-error-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap index d13d8a239a..5aeb224a10 100644 --- a/subprojects/proc-macro-error-attr-1-rs.wrap +++ b/subprojects/proc-macro-error-attr-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = proc-macro-error-attr-1.0.4.tar.gz source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 #method = cargo patch_directory = proc-macro-error-attr-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 7053e2c013..6c9369f0df 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = proc-macro2-1.0.84.0.tar.gz source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6 #method = cargo patch_directory = proc-macro2-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap index 6e7ea69049..8b721dfa00 100644 --- a/subprojects/quote-1-rs.wrap +++ b/subprojects/quote-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = quote-1.0.36.0.tar.gz source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7 #method = cargo patch_directory = quote-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index 13ffdac3c3..d79cf750fb 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -5,3 +5,6 @@ source_filename = syn-2.0.66.0.tar.gz source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5 #method = cargo patch_directory = syn-2-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap index 4609f96ed9..50988f612e 100644 --- a/subprojects/unicode-ident-1-rs.wrap +++ b/subprojects/unicode-ident-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = unicode-ident-1.0.12.tar.gz source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b #method = cargo patch_directory = unicode-ident-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/unicode-ident-1-rs/meson.build b/subprojects/unicode-ident-1-rs/meson.build deleted file mode 100644 index 54f2376854..0000000000 --- a/subprojects/unicode-ident-1-rs/meson.build +++ /dev/null @@ -1,20 +0,0 @@ -project('unicode-ident-1-rs', 'rust', - version: '1.0.12', - license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016', - default_options: []) - -_unicode_ident_rs = static_library( - 'unicode_ident', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [], - native: true, -) - -unicode_ident_dep = declare_dependency( - link_with: _unicode_ident_rs, -) - -meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true) From b7bd800eba69bab75b8c296ad788df8df8947aae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Jan 2025 11:42:49 +0100 Subject: [PATCH 0902/2892] qom: remove unused field The "concrete_class" field of InterfaceClass is only ever written, and as far as I can tell is not particularly useful when debugging either; remove it. Signed-off-by: Paolo Bonzini --- include/qom/object.h | 5 ++++- qom/object.c | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 95d6e064d9..f28ffea9a6 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -573,12 +573,15 @@ struct InterfaceInfo { * * The class for all interfaces. Subclasses of this class should only add * virtual methods. + * + * Note that most of the fields of ObjectClass are unused (all except + * "type", in fact). They are only present in InterfaceClass to allow + * @object_class_dynamic_cast to work with both regular classes and interfaces. */ struct InterfaceClass { ObjectClass parent_class; /* private: */ - ObjectClass *concrete_class; Type interface_type; }; diff --git a/qom/object.c b/qom/object.c index b4c52d055d..e9dfad854b 100644 --- a/qom/object.c +++ b/qom/object.c @@ -314,7 +314,6 @@ static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, g_free((char *)info.name); new_iface = (InterfaceClass *)iface_impl->class; - new_iface->concrete_class = ti->class; new_iface->interface_type = interface_type; ti->class->interfaces = g_slist_append(ti->class->interfaces, new_iface); From be27b5149c86f81531f8fc609baf3480fc4d9ca0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 11:24:13 +0100 Subject: [PATCH 0903/2892] make-release: only leave tarball of wrap-file subprojects The QEMU source archive is including the sources downloaded from crates.io in both tarball form (in subprojects/packagecache) and expanded/patched form (in the subprojects directory). The former is the more authoritative form, as it has a hash that can be verified in the wrap file and checked against the download URL, so keep that one only. This works also with --disable-download; when building QEMU for the first time from the tarball, Meson will print something like Using proc-macro2-1-rs source from cache. for each subproject, and then go on to extract the tarball and apply the overlay or the patches in subprojects/packagefiles. Reported-by: Michael Tokarev Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2719 Signed-off-by: Paolo Bonzini --- scripts/make-release | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/scripts/make-release b/scripts/make-release index 8dc939124c..2885e87210 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -10,6 +10,27 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +function subproject_dir() { + if test ! -f "subprojects/$1.wrap"; then + error "scripts/archive-source.sh should only process wrap subprojects" + fi + + # Print the directory key of the wrap file, defaulting to the + # subproject name. The wrap file is in ini format and should + # have a single section only. There should be only one section + # named "[wrap-*]", which helps keeping the script simple. + local dir + dir=$(sed -n \ + -e '/^\[wrap-[a-z][a-z]*\]$/,/^\[/{' \ + -e '/^directory *= */!b' \ + -e 's///p' \ + -e 'q' \ + -e '}' \ + "subprojects/$1.wrap") + + echo "${dir:-$1}" +} + if [ $# -ne 2 ]; then echo "Usage:" echo " $0 gitrepo version" @@ -51,5 +72,13 @@ meson subprojects download $SUBPROJECTS CryptoPkg/Library/OpensslLib/openssl \ MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) popd -tar --exclude=.git -cJf ${destination}.tar.xz ${destination} + +exclude=(--exclude=.git) +# include the tarballs in subprojects/packagecache but not their expansion +for sp in $SUBPROJECTS; do + if grep -xqF "[wrap-file]" subprojects/$sp.wrap; then + exclude+=(--exclude=subprojects/"$(subproject_dir $sp)") + fi +done +tar "${exclude[@]}" -cJf ${destination}.tar.xz ${destination} rm -rf ${destination} From 88716ae79f89bd6510f0c9e182a73ad40d1ff531 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 Oct 2024 12:10:39 +0200 Subject: [PATCH 0904/2892] target/i386: improve code generation for BT Because BT does not write back to the source operand, it can modify it to ensure that one of the operands of TSTNE is a constant (after either gen_BT or the optimizer's constant propagation). This produces better and more optimizable TCG ops. For example, the sequence movl $0x60013f, %ebx btl %ecx, %ebx becomes just and_i32 tmp1,ecx,$0x1f dead: 1 2 pref=0xffff shr_i32 tmp0,$0x60013f,tmp1 dead: 1 2 pref=0xffff and_i32 tmp16,tmp0,$0x1 dead: 1 pref=0xbf80 On s390x, it can use four instructions to isolate bit 0 of 0x60013f >> (ecx & 31): nilf %r12, 0x1f lgfi %r11, 0x60013f srlk %r12, %r11, 0(%r12) nilf %r12, 1 Previously, it used five instructions to build 1 << (ecx & 31) and compute TSTEQ, and also needed two more to construct the result of setcond: nilf %r12, 0x1f lghi %r11, 1 sllk %r12, %r11, 0(%r12) lgfi %r9, 0x60013f nrk %r0, %r12, %r9 lghi %r12, 0 locghilh %r12, 1 Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 785ff63f2a..5c11542935 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1443,8 +1443,9 @@ static TCGv gen_bt_mask(DisasContext *s, X86DecodedInsn *decode) return mask; } -/* Expects truncated bit index in s->T1, 1 << s->T1 in MASK. */ -static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv mask) +/* Expects truncated bit index in COUNT, 1 << COUNT in MASK. */ +static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, + TCGv count, TCGv mask) { TCGv cf; @@ -1467,15 +1468,34 @@ static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv decode->cc_src = tcg_temp_new(); decode->cc_dst = cpu_cc_dst; decode->cc_op = CC_OP_SARB + cc_op_size(s->cc_op); - tcg_gen_shr_tl(decode->cc_src, src, s->T1); + tcg_gen_shr_tl(decode->cc_src, src, count); } } static void gen_BT(DisasContext *s, X86DecodedInsn *decode) { - TCGv mask = gen_bt_mask(s, decode); + TCGv count = s->T1; + TCGv mask; - gen_bt_flags(s, decode, s->T0, mask); + /* + * Try to ensure that the rhs of the TSTNE condition is a constant (and a + * power of two), as that is more readily available on most TCG backends. + * + * For immediate bit number gen_bt_mask()'s output is already a constant; + * for register bit number, shift the source right and check bit 0. + */ + if (decode->e.op2 == X86_TYPE_I) { + mask = gen_bt_mask(s, decode); + } else { + MemOp ot = decode->op[1].ot; + + tcg_gen_andi_tl(s->T1, s->T1, (8 << ot) - 1); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); + + count = tcg_constant_tl(0); + mask = tcg_constant_tl(1); + } + gen_bt_flags(s, decode, s->T0, count, mask); } static void gen_BTC(DisasContext *s, X86DecodedInsn *decode) @@ -1491,7 +1511,7 @@ static void gen_BTC(DisasContext *s, X86DecodedInsn *decode) tcg_gen_xor_tl(s->T0, s->T0, mask); } - gen_bt_flags(s, decode, old, mask); + gen_bt_flags(s, decode, old, s->T1, mask); } static void gen_BTR(DisasContext *s, X86DecodedInsn *decode) @@ -1509,7 +1529,7 @@ static void gen_BTR(DisasContext *s, X86DecodedInsn *decode) tcg_gen_andc_tl(s->T0, s->T0, mask); } - gen_bt_flags(s, decode, old, mask); + gen_bt_flags(s, decode, old, s->T1, mask); } static void gen_BTS(DisasContext *s, X86DecodedInsn *decode) @@ -1525,7 +1545,7 @@ static void gen_BTS(DisasContext *s, X86DecodedInsn *decode) tcg_gen_or_tl(s->T0, s->T0, mask); } - gen_bt_flags(s, decode, old, mask); + gen_bt_flags(s, decode, old, s->T1, mask); } static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode) From ef682b08a0b52f4e6d9d790e26291f146e05734a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 21 Nov 2024 13:01:45 +0100 Subject: [PATCH 0905/2892] target/i386: use shr to load high-byte registers into T0/T1 Using a sextract or extract operation is only necessary if a sign or zero extended value is needed. If not, a shift is enough. Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 23 ++++++++++++----------- target/i386/tcg/translate.c | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 5c11542935..c4cc5f48d8 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -286,24 +286,25 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) gen_op_ld_v(s, op->ot, v, s->A0); } - } else if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) { - if (v == s->T0 && decode->e.special == X86_SPECIAL_SExtT0) { - tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8); - } else { - tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8); - } - } else if (op->ot < MO_TL && v == s->T0 && (decode->e.special == X86_SPECIAL_SExtT0 || decode->e.special == X86_SPECIAL_ZExtT0)) { - if (decode->e.special == X86_SPECIAL_SExtT0) { - tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN); + if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) { + if (decode->e.special == X86_SPECIAL_SExtT0) { + tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8); + } else { + tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8); + } } else { - tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot); + if (decode->e.special == X86_SPECIAL_SExtT0) { + tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN); + } else { + tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot); + } } } else { - tcg_gen_mov_tl(v, cpu_regs[op->n]); + gen_op_mov_v_reg(s, op->ot, v, op->n); } break; case X86_OP_IMM: diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 834aea1e59..dbc9d637c4 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -486,7 +486,7 @@ static inline void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg) { if (ot == MO_8 && byte_reg_is_xH(s, reg)) { - tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8); + tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8); } else { tcg_gen_mov_tl(t0, cpu_regs[reg]); } From cf4c263551886964c5d58bd7b675b13fd497b402 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:18 +0800 Subject: [PATCH 0906/2892] i386/cpu: Mark avx10_version filtered when prefix is NULL In x86_cpu_filter_features(), if host doesn't support AVX10, the configured avx10_version should be marked as filtered regardless of whether prefix is NULL or not. Check prefix before warn_report() instead of checking for have_filtered_features. Cc: qemu-stable@nongnu.org Fixes: commit bccfb846fd52 ("target/i386: add AVX10 feature and AVX10 version property") Signed-off-by: Zhao Liu Reviewed-by: Tao Su Link: https://lore.kernel.org/r/20241106030728.553238-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0b639848cd..579d9bac95 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7719,8 +7719,10 @@ static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose) env->avx10_version = version; have_filtered_features = true; } - } else if (env->avx10_version && prefix) { - warn_report("%s: avx10.%d.", prefix, env->avx10_version); + } else if (env->avx10_version) { + if (prefix) { + warn_report("%s: avx10.%d.", prefix, env->avx10_version); + } have_filtered_features = true; } From cee1f341ce0c803ee26deed0a68fa1985391d517 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:19 +0800 Subject: [PATCH 0907/2892] target/i386/kvm: Add feature bit definitions for KVM CPUID Add feature definitions for KVM_CPUID_FEATURES in CPUID ( CPUID[4000_0001].EAX and CPUID[4000_0001].EDX), to get rid of lots of offset calculations. Signed-off-by: Zhao Liu Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/kvm/clock.c | 5 ++--- target/i386/cpu.h | 23 +++++++++++++++++++++++ target/i386/kvm/kvm.c | 28 ++++++++++++++-------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 63be508842..17443552e9 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -27,7 +27,6 @@ #include "qapi/error.h" #include -#include "standard-headers/asm-x86/kvm_para.h" #include "qom/object.h" #define TYPE_KVM_CLOCK "kvmclock" @@ -333,8 +332,8 @@ void kvmclock_create(bool create_always) assert(kvm_enabled()); if (create_always || - cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | - (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { + cpu->env.features[FEAT_KVM] & (CPUID_KVM_CLOCK | + CPUID_KVM_CLOCK2)) { sysbus_create_simple(TYPE_KVM_CLOCK, -1, NULL); } } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index dbd8f1ffc7..f41462d8c1 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -29,6 +29,7 @@ #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" #include "qemu/timer.h" +#include "standard-headers/asm-x86/kvm_para.h" #define XEN_NR_VIRQS 24 @@ -1010,6 +1011,28 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_8000_0007_EBX_OVERFLOW_RECOV (1U << 0) #define CPUID_8000_0007_EBX_SUCCOR (1U << 1) +/* (Old) KVM paravirtualized clocksource */ +#define CPUID_KVM_CLOCK (1U << KVM_FEATURE_CLOCKSOURCE) +/* (New) KVM specific paravirtualized clocksource */ +#define CPUID_KVM_CLOCK2 (1U << KVM_FEATURE_CLOCKSOURCE2) +/* KVM asynchronous page fault */ +#define CPUID_KVM_ASYNCPF (1U << KVM_FEATURE_ASYNC_PF) +/* KVM stolen (when guest vCPU is not running) time accounting */ +#define CPUID_KVM_STEAL_TIME (1U << KVM_FEATURE_STEAL_TIME) +/* KVM paravirtualized end-of-interrupt signaling */ +#define CPUID_KVM_PV_EOI (1U << KVM_FEATURE_PV_EOI) +/* KVM paravirtualized spinlocks support */ +#define CPUID_KVM_PV_UNHALT (1U << KVM_FEATURE_PV_UNHALT) +/* KVM host-side polling on HLT control from the guest */ +#define CPUID_KVM_POLL_CONTROL (1U << KVM_FEATURE_POLL_CONTROL) +/* KVM interrupt based asynchronous page fault*/ +#define CPUID_KVM_ASYNCPF_INT (1U << KVM_FEATURE_ASYNC_PF_INT) +/* KVM 'Extended Destination ID' support for external interrupts */ +#define CPUID_KVM_MSI_EXT_DEST_ID (1U << KVM_FEATURE_MSI_EXT_DEST_ID) + +/* Hint to KVM that vCPUs expect never preempted for an unlimited time */ +#define CPUID_KVM_HINTS_REALTIME (1U << KVM_HINTS_REALTIME) + /* CLZERO instruction */ #define CPUID_8000_0008_EBX_CLZERO (1U << 0) /* Always save/restore FP error pointers */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 2f66e63b88..d6fb3bee86 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -564,13 +564,13 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, * be enabled without the in-kernel irqchip */ if (!kvm_irqchip_in_kernel()) { - ret &= ~(1U << KVM_FEATURE_PV_UNHALT); + ret &= ~CPUID_KVM_PV_UNHALT; } if (kvm_irqchip_is_split()) { - ret |= 1U << KVM_FEATURE_MSI_EXT_DEST_ID; + ret |= CPUID_KVM_MSI_EXT_DEST_ID; } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= 1U << KVM_HINTS_REALTIME; + ret |= CPUID_KVM_HINTS_REALTIME; } if (current_machine->cgs) { @@ -3978,20 +3978,20 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc); kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, env->async_pf_int_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { + if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { + if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) { kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, env->poll_control_msr); } @@ -4456,19 +4456,19 @@ static int kvm_get_msrs(X86CPU *cpu) #endif kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { + if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { + if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) { kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, 1); } if (has_architectural_pmu_version > 0) { @@ -6195,7 +6195,7 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address) return address; } env = &X86_CPU(first_cpu)->env; - if (!(env->features[FEAT_KVM] & (1 << KVM_FEATURE_MSI_EXT_DEST_ID))) { + if (!(env->features[FEAT_KVM] & CPUID_KVM_MSI_EXT_DEST_ID)) { return address; } From f5bec7652ddff7e10a0c14aa123352275c720e48 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:20 +0800 Subject: [PATCH 0908/2892] target/i386/kvm: Remove local MSR_KVM_WALL_CLOCK and MSR_KVM_SYSTEM_TIME definitions These 2 MSRs have been already defined in kvm_para.h (standard-headers/ asm-x86/kvm_para.h). Remove QEMU local definitions to avoid duplication. Signed-off-by: Zhao Liu Reviewed-by: Xiaoyao Li Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d6fb3bee86..7870820a2b 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -95,9 +95,6 @@ #define KVM_APIC_BUS_CYCLE_NS 1 #define KVM_APIC_BUS_FREQUENCY (1000000000ULL / KVM_APIC_BUS_CYCLE_NS) -#define MSR_KVM_WALL_CLOCK 0x11 -#define MSR_KVM_SYSTEM_TIME 0x12 - /* A 4096-byte buffer can hold the 8-byte kvm_msrs header, plus * 255 kvm_msr_entry structs */ #define MSR_BUF_SIZE 4096 From 86e032bb7b5ac5a319cef914aa779825fe39a2e2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:21 +0800 Subject: [PATCH 0909/2892] target/i386/kvm: Only save/load kvmclock MSRs when kvmclock enabled MSR_KVM_SYSTEM_TIME and MSR_KVM_WALL_CLOCK are attached with the (old) kvmclock feature (KVM_FEATURE_CLOCKSOURCE). So, just save/load them only when kvmclock (KVM_FEATURE_CLOCKSOURCE) is enabled. Signed-off-by: Zhao Liu Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 7870820a2b..7536a3c9fd 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3973,8 +3973,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) */ if (level >= KVM_PUT_RESET_STATE) { kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc); - kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); - kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); + if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) { + kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); + kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); + } if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, env->async_pf_int_msr); } @@ -4451,8 +4453,10 @@ static int kvm_get_msrs(X86CPU *cpu) } } #endif - kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); - kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); + if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) { + kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); + kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); + } if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, 0); } From 5dabc87b51030fc08b76b2401a5566f0b1463b59 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:23 +0800 Subject: [PATCH 0910/2892] target/i386/kvm: Drop workaround for KVM_X86_DISABLE_EXITS_HTL typo The KVM_X86_DISABLE_EXITS_HTL typo has been fixed in commit 77d361b13c19 ("linux-headers: Update to kernel mainline commit b357bf602"). Drop the related workaround. Signed-off-by: Zhao Liu Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 7536a3c9fd..17f23607ed 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3100,10 +3100,7 @@ static int kvm_vm_set_tss_addr(KVMState *s, uint64_t tss_base) static int kvm_vm_enable_disable_exits(KVMState *s) { int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); -/* Work around for kernel header with a typo. TODO: fix header and drop. */ -#if defined(KVM_X86_DISABLE_EXITS_HTL) && !defined(KVM_X86_DISABLE_EXITS_HLT) -#define KVM_X86_DISABLE_EXITS_HLT KVM_X86_DISABLE_EXITS_HTL -#endif + if (disable_exits) { disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT | KVM_X86_DISABLE_EXITS_HLT | From 26824f9cac69979586b30410ffac8bca4157909e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:24 +0800 Subject: [PATCH 0911/2892] target/i386/confidential-guest: Fix comment of x86_confidential_guest_kvm_type() Update the comment to match the X86ConfidentialGuestClass implementation. Reported-by: Xiaoyao Li Signed-off-by: Zhao Liu Reviewed-by: Pankaj Gupta Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/confidential-guest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index 0afb8317b5..164be7633a 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -46,7 +46,7 @@ struct X86ConfidentialGuestClass { /** * x86_confidential_guest_kvm_type: * - * Calls #X86ConfidentialGuestClass.unplug callback of @plug_handler. + * Calls #X86ConfidentialGuestClass.kvm_type() callback. */ static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg) { From fb81c9cfdd9fc37687a36f41ca07ab0e8a6d9899 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:25 +0800 Subject: [PATCH 0912/2892] target/i386/kvm: Clean up return values of MSR filter related functions Before commit 0cc42e63bb54 ("kvm/i386: refactor kvm_arch_init and split it into smaller functions"), error_report() attempts to print the error code from kvm_filter_msr(). However, printing error code does not work due to kvm_filter_msr() returns bool instead int. 0cc42e63bb54 fixed the error by removing error code printing, but this lost useful error messages. Bring it back by making kvm_filter_msr() return int. This also makes the function call chain processing clearer, allowing for better handling of error result propagation from kvm_filter_msr() to kvm_arch_init(), preparing for the subsequent cleanup work of error handling in kvm_arch_init(). Signed-off-by: Zhao Liu Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-9-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 87 ++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 17f23607ed..097a040da3 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -108,8 +108,8 @@ typedef struct { } KVMMSRHandlers; static void kvm_init_msrs(X86CPU *cpu); -static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, - QEMUWRMSRHandler *wrmsr); +static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr); const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(SET_TSS_ADDR), @@ -3150,17 +3150,21 @@ static int kvm_vm_enable_notify_vmexit(KVMState *s) static int kvm_vm_enable_userspace_msr(KVMState *s) { - int ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0, - KVM_MSR_EXIT_REASON_FILTER); + int ret; + + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0, + KVM_MSR_EXIT_REASON_FILTER); if (ret < 0) { error_report("Could not enable user space MSRs: %s", strerror(-ret)); exit(1); } - if (!kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, - kvm_rdmsr_core_thread_count, NULL)) { - error_report("Could not install MSR_CORE_THREAD_COUNT handler!"); + ret = kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, + kvm_rdmsr_core_thread_count, NULL); + if (ret < 0) { + error_report("Could not install MSR_CORE_THREAD_COUNT handler: %s", + strerror(-ret)); exit(1); } @@ -3169,36 +3173,37 @@ static int kvm_vm_enable_userspace_msr(KVMState *s) static void kvm_vm_enable_energy_msrs(KVMState *s) { - bool r; + int ret; + if (s->msr_energy.enable == true) { - r = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, - kvm_rdmsr_rapl_power_unit, NULL); - if (!r) { - error_report("Could not install MSR_RAPL_POWER_UNIT \ - handler"); + ret = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, + kvm_rdmsr_rapl_power_unit, NULL); + if (ret < 0) { + error_report("Could not install MSR_RAPL_POWER_UNIT handler: %s", + strerror(-ret)); exit(1); } - r = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, - kvm_rdmsr_pkg_power_limit, NULL); - if (!r) { - error_report("Could not install MSR_PKG_POWER_LIMIT \ - handler"); + ret = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, + kvm_rdmsr_pkg_power_limit, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_POWER_LIMIT handler: %s", + strerror(-ret)); exit(1); } - r = kvm_filter_msr(s, MSR_PKG_POWER_INFO, - kvm_rdmsr_pkg_power_info, NULL); - if (!r) { - error_report("Could not install MSR_PKG_POWER_INFO \ - handler"); + ret = kvm_filter_msr(s, MSR_PKG_POWER_INFO, + kvm_rdmsr_pkg_power_info, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_POWER_INFO handler: %s", + strerror(-ret)); exit(1); } - r = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, - kvm_rdmsr_pkg_energy_status, NULL); - if (!r) { - error_report("Could not install MSR_PKG_ENERGY_STATUS \ - handler"); + ret = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, + kvm_rdmsr_pkg_energy_status, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_ENERGY_STATUS handler: %s", + strerror(-ret)); exit(1); } } @@ -5841,13 +5846,13 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) } } -static bool kvm_install_msr_filters(KVMState *s) +static int kvm_install_msr_filters(KVMState *s) { uint64_t zero = 0; struct kvm_msr_filter filter = { .flags = KVM_MSR_FILTER_DEFAULT_ALLOW, }; - int r, i, j = 0; + int i, j = 0; for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) { KVMMSRHandlers *handler = &msr_handlers[i]; @@ -5871,18 +5876,13 @@ static bool kvm_install_msr_filters(KVMState *s) } } - r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); - if (r) { - return false; - } - - return true; + return kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); } -static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, - QEMUWRMSRHandler *wrmsr) +static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr) { - int i; + int i, ret; for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { if (!msr_handlers[i].msr) { @@ -5892,16 +5892,17 @@ static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, .wrmsr = wrmsr, }; - if (!kvm_install_msr_filters(s)) { + ret = kvm_install_msr_filters(s); + if (ret) { msr_handlers[i] = (KVMMSRHandlers) { }; - return false; + return ret; } - return true; + return 0; } } - return false; + return -EINVAL; } static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run) From d7f895cb62f3a709e7b9aeeab19678811deda973 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:26 +0800 Subject: [PATCH 0913/2892] target/i386/kvm: Return -1 when kvm_msr_energy_thread_init() fails It is common practice to return a negative value (like -1) to indicate an error, and other functions in kvm_arch_init() follow this style. To avoid confusion (sometimes returned -1 indicates failure, and sometimes -1, in a same function), return -1 when kvm_msr_energy_thread_init() fails. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20241106030728.553238-10-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 097a040da3..3624abbb39 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2936,7 +2936,6 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); struct KVMMsrEnergy *r = &s->msr_energy; - int ret = 0; /* * Sanity check @@ -2946,13 +2945,11 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) if (!is_host_cpu_intel()) { error_report("The RAPL feature can only be enabled on hosts " "with Intel CPU models"); - ret = 1; - goto out; + return -1; } if (!is_rapl_enabled()) { - ret = 1; - goto out; + return -1; } /* Retrieve the virtual topology */ @@ -2974,16 +2971,14 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) r->host_topo.maxcpus = vmsr_get_maxcpus(); if (r->host_topo.maxcpus == 0) { error_report("host max cpus = 0"); - ret = 1; - goto out; + return -1; } /* Max number of packages on the host */ r->host_topo.maxpkgs = vmsr_get_max_physical_package(r->host_topo.maxcpus); if (r->host_topo.maxpkgs == 0) { error_report("host max pkgs = 0"); - ret = 1; - goto out; + return -1; } /* Allocate memory for each package on the host */ @@ -2995,8 +2990,7 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) for (int i = 0; i < r->host_topo.maxpkgs; i++) { if (r->host_topo.pkg_cpu_count[i] == 0) { error_report("cpu per packages = 0 on package_%d", i); - ret = 1; - goto out; + return -1; } } @@ -3013,8 +3007,7 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) if (s->msr_energy.sioc == NULL) { error_report("vmsr socket opening failed"); - ret = 1; - goto out; + return -1; } /* Those MSR values should not change */ @@ -3026,15 +3019,13 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) s->msr_energy.sioc); if (r->msr_unit == 0 || r->msr_limit == 0 || r->msr_info == 0) { error_report("can't read any virtual msr"); - ret = 1; - goto out; + return -1; } qemu_thread_create(&r->msr_thr, "kvm-msr", kvm_msr_energy_thread, s, QEMU_THREAD_JOINABLE); -out: - return ret; + return 0; } int kvm_arch_get_default_type(MachineState *ms) @@ -3342,7 +3333,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) if (s->msr_energy.enable == true) { kvm_vm_enable_energy_msrs(s); - if (kvm_msr_energy_thread_init(s, ms)) { + + ret = kvm_msr_energy_thread_init(s, ms); + if (ret < 0) { error_report("kvm : error RAPL feature requirement not met"); exit(1); } From d2401a6eae8f8fbd8e569c8b0638f0cbc80ec88e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 6 Nov 2024 11:07:27 +0800 Subject: [PATCH 0914/2892] target/i386/kvm: Clean up error handling in kvm_arch_init() Currently, there're following incorrect error handling cases in kvm_arch_init(): * Missed to handle failure of kvm_get_supported_feature_msrs(). * Missed to return when kvm_vm_enable_disable_exits() fails. * MSR filter related cases called exit() directly instead of returning to kvm_init(). (The caller of kvm_arch_init() - kvm_init() - needs to know if kvm_arch_init() fails in order to perform cleanup). Fix the above cases. Signed-off-by: Zhao Liu Reviewed-by: Zide Chen Link: https://lore.kernel.org/r/20241106030728.553238-11-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 3624abbb39..6f424774b3 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3162,7 +3162,7 @@ static int kvm_vm_enable_userspace_msr(KVMState *s) return 0; } -static void kvm_vm_enable_energy_msrs(KVMState *s) +static int kvm_vm_enable_energy_msrs(KVMState *s) { int ret; @@ -3172,7 +3172,7 @@ static void kvm_vm_enable_energy_msrs(KVMState *s) if (ret < 0) { error_report("Could not install MSR_RAPL_POWER_UNIT handler: %s", strerror(-ret)); - exit(1); + return ret; } ret = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, @@ -3180,7 +3180,7 @@ static void kvm_vm_enable_energy_msrs(KVMState *s) if (ret < 0) { error_report("Could not install MSR_PKG_POWER_LIMIT handler: %s", strerror(-ret)); - exit(1); + return ret; } ret = kvm_filter_msr(s, MSR_PKG_POWER_INFO, @@ -3188,17 +3188,17 @@ static void kvm_vm_enable_energy_msrs(KVMState *s) if (ret < 0) { error_report("Could not install MSR_PKG_POWER_INFO handler: %s", strerror(-ret)); - exit(1); + return ret; } ret = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, kvm_rdmsr_pkg_energy_status, NULL); if (ret < 0) { error_report("Could not install MSR_PKG_ENERGY_STATUS handler: %s", strerror(-ret)); - exit(1); + return ret; } } - return; + return 0; } int kvm_arch_init(MachineState *ms, KVMState *s) @@ -3265,7 +3265,10 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - kvm_get_supported_feature_msrs(s); + ret = kvm_get_supported_feature_msrs(s); + if (ret < 0) { + return ret; + } uname(&utsname); lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; @@ -3301,6 +3304,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) if (ret < 0) { error_report("kvm: guest stopping CPU not supported: %s", strerror(-ret)); + return ret; } } @@ -3332,12 +3336,15 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } if (s->msr_energy.enable == true) { - kvm_vm_enable_energy_msrs(s); + ret = kvm_vm_enable_energy_msrs(s); + if (ret < 0) { + return ret; + } ret = kvm_msr_energy_thread_init(s, ms); if (ret < 0) { error_report("kvm : error RAPL feature requirement not met"); - exit(1); + return ret; } } } From d662b66da4bef9272144f7b79715aad90cdbc33e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 24 Dec 2024 16:59:12 +0100 Subject: [PATCH 0915/2892] target/i386/kvm: Replace ARRAY_SIZE(msr_handlers) with KVM_MSR_FILTER_MAX_RANGES kvm_install_msr_filters() uses KVM_MSR_FILTER_MAX_RANGES as the bound when traversing msr_handlers[], while other places still compute the size by ARRAY_SIZE(msr_handlers). In fact, msr_handlers[] is an array with the fixed size KVM_MSR_FILTER_MAX_RANGES, and this has to be true because kvm_install_msr_filters copies from one array to the other. For code consistency, assert that they match and use ARRAY_SIZE(msr_handlers) everywehere. Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6f424774b3..1d7214b6a6 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5854,7 +5854,8 @@ static int kvm_install_msr_filters(KVMState *s) }; int i, j = 0; - for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) { + QEMU_BUILD_BUG_ON(ARRAY_SIZE(msr_handlers) != ARRAY_SIZE(filter.ranges)); + for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { KVMMSRHandlers *handler = &msr_handlers[i]; if (handler->msr) { struct kvm_msr_filter_range *range = &filter.ranges[j++]; From d3bb5d0d4f5d4ad7dc6c02ea5fea51ca2f946593 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:16 -0500 Subject: [PATCH 0916/2892] i386/cpu: Extract a common fucntion to setup value of MSR_CORE_THREAD_COUNT There are duplicated code to setup the value of MSR_CORE_THREAD_COUNT. Extract a common function for it. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241219110125.1266461-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu-system.c | 11 +++++++++++ target/i386/cpu.h | 2 ++ target/i386/hvf/x86_emu.c | 3 +-- target/i386/kvm/kvm.c | 5 +---- target/i386/tcg/system/misc_helper.c | 3 +-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/target/i386/cpu-system.c b/target/i386/cpu-system.c index 9d007afdab..eb38cca68f 100644 --- a/target/i386/cpu-system.c +++ b/target/i386/cpu-system.c @@ -309,3 +309,14 @@ void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, errp); qapi_free_GuestPanicInformation(panic_info); } + +uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu) +{ + CPUState *cs = CPU(cpu); + uint64_t val; + + val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ + val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + + return val; +} diff --git a/target/i386/cpu.h b/target/i386/cpu.h index f41462d8c1..e8c46d877e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2413,6 +2413,8 @@ static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu, cs->halted = 0; } +uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu); + int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, target_ulong *base, unsigned int *limit, unsigned int *flags); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 015f760acb..69c61c9c07 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -765,8 +765,7 @@ void simulate_rdmsr(CPUX86State *env) val = env->mtrr_deftype; break; case MSR_CORE_THREAD_COUNT: - val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ - val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + val = cpu_x86_get_msr_core_thread_count(cpu); break; default: /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 1d7214b6a6..6c749d4ee8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2614,10 +2614,7 @@ static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, uint64_t *val) { - CPUState *cs = CPU(cpu); - - *val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ - *val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + *val = cpu_x86_get_msr_core_thread_count(cpu); return true; } diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index ffed8a3215..c9c4d42f84 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -468,8 +468,7 @@ void helper_rdmsr(CPUX86State *env) val = x86_cpu->ucode_rev; break; case MSR_CORE_THREAD_COUNT: { - CPUState *cs = CPU(x86_cpu); - val = (cs->nr_threads * cs->nr_cores) | (cs->nr_cores << 16); + val = cpu_x86_get_msr_core_thread_count(x86_cpu); break; } case MSR_APIC_START ... MSR_APIC_END: { From 81bd60625fc23cb8d4d0e682dcc4223d5e1ead84 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:17 -0500 Subject: [PATCH 0917/2892] i386/cpu: Drop the variable smp_cores and smp_threads in x86_cpu_pre_plug() No need to define smp_cores and smp_threads, just using ms->smp.cores and ms->smp.threads is straightforward. It's also consistent with other checks of socket/die/module. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241219110125.1266461-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index a7d46c3105..5b0629f9ad 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -248,8 +248,6 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - unsigned int smp_cores = ms->smp.cores; - unsigned int smp_threads = ms->smp.threads; X86CPUTopoInfo topo_info; if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { @@ -329,17 +327,17 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, if (cpu->core_id < 0) { error_setg(errp, "CPU core-id is not set"); return; - } else if (cpu->core_id > (smp_cores - 1)) { + } else if (cpu->core_id > (ms->smp.cores - 1)) { error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", - cpu->core_id, smp_cores - 1); + cpu->core_id, ms->smp.cores - 1); return; } if (cpu->thread_id < 0) { error_setg(errp, "CPU thread-id is not set"); return; - } else if (cpu->thread_id > (smp_threads - 1)) { + } else if (cpu->thread_id > (ms->smp.threads - 1)) { error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", - cpu->thread_id, smp_threads - 1); + cpu->thread_id, ms->smp.threads - 1); return; } From 00ec7be67c3981b486293aa8e0aef9534f229c5e Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:18 -0500 Subject: [PATCH 0918/2892] i386/cpu: Drop cores_per_pkg in cpu_x86_cpuid() Local variable cores_per_pkg is only used to calculate threads_per_pkg. No need for it. Drop it and open-code it instead. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241219110125.1266461-4-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 579d9bac95..6d9c85576f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6499,7 +6499,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t limit; uint32_t signature[3]; X86CPUTopoInfo topo_info; - uint32_t cores_per_pkg; uint32_t threads_per_pkg; topo_info.dies_per_pkg = env->nr_dies; @@ -6507,9 +6506,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules; topo_info.threads_per_core = cs->nr_threads; - cores_per_pkg = topo_info.cores_per_module * topo_info.modules_per_die * - topo_info.dies_per_pkg; - threads_per_pkg = cores_per_pkg * topo_info.threads_per_core; + threads_per_pkg = topo_info.threads_per_core * topo_info.cores_per_module * + topo_info.modules_per_die * topo_info.dies_per_pkg; /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { From 8f78378de70fc79fdc7e1318496bd91ddd22df49 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:19 -0500 Subject: [PATCH 0919/2892] i386/topology: Update the comment of x86_apicid_from_topo_ids() Update the comment of x86_apicid_from_topo_ids() to match the current implementation, Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241219110125.1266461-5-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- include/hw/i386/topology.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index b2c8bf2de1..21b65219a5 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -121,9 +121,10 @@ static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) } /* - * Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * Make APIC ID for the CPU based on topology and IDs of each topology level. * - * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + * The caller must make sure the ID of each level doesn't exceed the width of + * the level. */ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, const X86CPUTopoIDs *topo_ids) From e60cbeec190d349682bf97cf55446e8ae260b11a Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:20 -0500 Subject: [PATCH 0920/2892] i386/topology: Introduce helpers for various topology info of different level Introduce various helpers for getting the topology info of different semantics. Using the helper is more self-explanatory. Besides, the semantic of the helper will stay unchanged even when new topology is added in the future. At that time, updating the implementation of the helper without affecting the callers. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20241219110125.1266461-6-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- include/hw/i386/topology.h | 25 +++++++++++++++++++++++++ target/i386/cpu.c | 11 ++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 21b65219a5..f6380f1ed7 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -203,4 +203,29 @@ static inline bool x86_has_extended_topo(unsigned long *topo_bitmap) test_bit(CPU_TOPOLOGY_LEVEL_DIE, topo_bitmap); } +static inline unsigned x86_module_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->modules_per_die * topo_info->dies_per_pkg; +} + +static inline unsigned x86_cores_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->cores_per_module * x86_module_per_pkg(topo_info); +} + +static inline unsigned x86_threads_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->threads_per_core * x86_cores_per_pkg(topo_info); +} + +static inline unsigned x86_threads_per_module(X86CPUTopoInfo *topo_info) +{ + return topo_info->threads_per_core * topo_info->cores_per_module; +} + +static inline unsigned x86_threads_per_die(X86CPUTopoInfo *topo_info) +{ + return x86_threads_per_module(topo_info) * topo_info->modules_per_die; +} + #endif /* HW_I386_TOPOLOGY_H */ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6d9c85576f..a58c719e90 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -312,13 +312,11 @@ static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, case CPU_TOPOLOGY_LEVEL_CORE: return topo_info->threads_per_core; case CPU_TOPOLOGY_LEVEL_MODULE: - return topo_info->threads_per_core * topo_info->cores_per_module; + return x86_threads_per_module(topo_info); case CPU_TOPOLOGY_LEVEL_DIE: - return topo_info->threads_per_core * topo_info->cores_per_module * - topo_info->modules_per_die; + return x86_threads_per_die(topo_info); case CPU_TOPOLOGY_LEVEL_SOCKET: - return topo_info->threads_per_core * topo_info->cores_per_module * - topo_info->modules_per_die * topo_info->dies_per_pkg; + return x86_threads_per_pkg(topo_info); default: g_assert_not_reached(); } @@ -6506,8 +6504,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules; topo_info.threads_per_core = cs->nr_threads; - threads_per_pkg = topo_info.threads_per_core * topo_info.cores_per_module * - topo_info.modules_per_die * topo_info.dies_per_pkg; + threads_per_pkg = x86_threads_per_pkg(&topo_info); /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { From 84b71a131c1bc84c36fafb63271080ecf9f2ff7a Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:21 -0500 Subject: [PATCH 0921/2892] i386/cpu: Track a X86CPUTopoInfo directly in CPUX86State The name of nr_modules/nr_dies are ambiguous and they mislead people. The purpose of them is to record and form the topology information. So just maintain a X86CPUTopoInfo member in CPUX86State instead. Then nr_modules and nr_dies can be dropped. As the benefit, x86 can switch to use information in CPUX86State::topo_info and get rid of the nr_cores and nr_threads in CPUState. This helps remove the dependency on qemu_init_vcpu(), so that x86 can get and use topology info earlier in x86_cpu_realizefn(); drop the comment that highlighted the depedency. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20241219110125.1266461-7-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 12 ++++------ target/i386/cpu-system.c | 6 ++--- target/i386/cpu.c | 51 +++++++++++++++++----------------------- target/i386/cpu.h | 6 +---- 4 files changed, 30 insertions(+), 45 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 5b0629f9ad..d5a44af243 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -248,7 +248,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - X86CPUTopoInfo topo_info; + X86CPUTopoInfo *topo_info = &env->topo_info; if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", @@ -267,15 +267,13 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, } } - init_topo_info(&topo_info, x86ms); + init_topo_info(topo_info, x86ms); if (ms->smp.modules > 1) { - env->nr_modules = ms->smp.modules; set_bit(CPU_TOPOLOGY_LEVEL_MODULE, env->avail_cpu_topo); } if (ms->smp.dies > 1) { - env->nr_dies = ms->smp.dies; set_bit(CPU_TOPOLOGY_LEVEL_DIE, env->avail_cpu_topo); } @@ -346,12 +344,12 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, topo_ids.module_id = cpu->module_id; topo_ids.core_id = cpu->core_id; topo_ids.smt_id = cpu->thread_id; - cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); + cpu->apic_id = x86_apicid_from_topo_ids(topo_info, &topo_ids); } cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); if (!cpu_slot) { - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + x86_topo_ids_from_apicid(cpu->apic_id, topo_info, &topo_ids); error_setg(errp, "Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]" @@ -374,7 +372,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() * once -smp refactoring is complete and there will be CPU private * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + x86_topo_ids_from_apicid(cpu->apic_id, topo_info, &topo_ids); if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { error_setg(errp, "property socket-id: %u doesn't match set apic-id:" " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, diff --git a/target/i386/cpu-system.c b/target/i386/cpu-system.c index eb38cca68f..b56a2821af 100644 --- a/target/i386/cpu-system.c +++ b/target/i386/cpu-system.c @@ -312,11 +312,11 @@ void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu) { - CPUState *cs = CPU(cpu); + CPUX86State *env = &cpu->env; uint64_t val; - val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ - val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + val = x86_threads_per_pkg(&env->topo_info); /* thread count, bits 15..0 */ + val |= x86_cores_per_pkg(&env->topo_info) << 16; /* core count, bits 31..16 */ return val; } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a58c719e90..1797bd8c07 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6496,15 +6496,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, CPUState *cs = env_cpu(env); uint32_t limit; uint32_t signature[3]; - X86CPUTopoInfo topo_info; + X86CPUTopoInfo *topo_info = &env->topo_info; uint32_t threads_per_pkg; - topo_info.dies_per_pkg = env->nr_dies; - topo_info.modules_per_die = env->nr_modules; - topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules; - topo_info.threads_per_core = cs->nr_threads; - - threads_per_pkg = x86_threads_per_pkg(&topo_info); + threads_per_pkg = x86_threads_per_pkg(topo_info); /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { @@ -6581,12 +6576,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); *eax &= ~0xFC000000; - *eax |= max_core_ids_in_package(&topo_info) << 26; + *eax |= max_core_ids_in_package(topo_info) << 26; if (host_vcpus_per_cache > threads_per_pkg) { *eax &= ~0x3FFC000; /* Share the cache at package level. */ - *eax |= max_thread_ids_for_cache(&topo_info, + *eax |= max_thread_ids_for_cache(topo_info, CPU_TOPOLOGY_LEVEL_SOCKET) << 14; } } @@ -6598,7 +6593,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); @@ -6606,7 +6601,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 1: /* L1 icache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); @@ -6614,13 +6609,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 2: /* L2 cache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ if (cpu->enable_l3_cache) { encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); break; } @@ -6703,12 +6698,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: - *eax = apicid_core_offset(&topo_info); - *ebx = topo_info.threads_per_core; + *eax = apicid_core_offset(topo_info); + *ebx = topo_info->threads_per_core; *ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8; break; case 1: - *eax = apicid_pkg_offset(&topo_info); + *eax = apicid_pkg_offset(topo_info); *ebx = threads_per_pkg; *ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8; break; @@ -6734,7 +6729,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } - encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx); + encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx); break; case 0xD: { /* Processor Extended State */ @@ -7037,7 +7032,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * thread ID within a package". * Bits 7:0 is "The number of threads in the package is NC+1" */ - *ecx = (apicid_pkg_offset(&topo_info) << 12) | + *ecx = (apicid_pkg_offset(topo_info) << 12) | (threads_per_pkg - 1); } else { *ecx = 0; @@ -7066,19 +7061,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ *eax = *ebx = *ecx = *edx = 0; @@ -7090,7 +7085,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x8000001E: if (cpu->core_id <= 255) { - encode_topo_cpuid8000001e(cpu, &topo_info, eax, ebx, ecx, edx); + encode_topo_cpuid8000001e(cpu, topo_info, eax, ebx, ecx, edx); } else { *eax = 0; *ebx = 0; @@ -7997,17 +7992,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX * based on inputs (sockets,cores,threads), it is still better to give * users a warning. - * - * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise - * cs->nr_threads hasn't be populated yet and the checking is incorrect. */ if (IS_AMD_CPU(env) && !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && - cs->nr_threads > 1) { + env->topo_info.threads_per_core > 1) { warn_report_once("This family of AMD CPU doesn't support " "hyperthreading(%d). Please configure -smp " "options properly or try enabling topoext " - "feature.", cs->nr_threads); + "feature.", env->topo_info.threads_per_core); } #ifndef CONFIG_USER_ONLY @@ -8168,8 +8160,7 @@ static void x86_cpu_init_default_topo(X86CPU *cpu) { CPUX86State *env = &cpu->env; - env->nr_modules = 1; - env->nr_dies = 1; + env->topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; /* thread, core and socket levels are set by default. */ set_bit(CPU_TOPOLOGY_LEVEL_THREAD, env->avail_cpu_topo); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e8c46d877e..b26e25ba15 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2068,11 +2068,7 @@ typedef struct CPUArchState { TPRAccess tpr_access_type; - /* Number of dies within this CPU package. */ - unsigned nr_dies; - - /* Number of modules within one die. */ - unsigned nr_modules; + X86CPUTopoInfo topo_info; /* Bitmap of available CPU topology levels for this CPU. */ DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX); From 473d79b56a1645be90b890f9623b27acd0afba49 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:22 -0500 Subject: [PATCH 0922/2892] i386/cpu: Hoist check of CPUID_EXT3_TOPOEXT against threads_per_core Now it changes to use env->topo_info.threads_per_core and doesn't depend on qemu_init_vcpu() anymore. Put it together with other feature checks before qemu_init_vcpu() Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20241219110125.1266461-8-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1797bd8c07..3f9475b485 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7883,6 +7883,21 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) */ cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + /* + * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU + * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX + * based on inputs (sockets,cores,threads), it is still better to give + * users a warning. + */ + if (IS_AMD_CPU(env) && + !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && + env->topo_info.threads_per_core > 1) { + warn_report_once("This family of AMD CPU doesn't support " + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext " + "feature.", env->topo_info.threads_per_core); + } + /* For 64bit systems think about the number of physical bits to present. * ideally this should be the same as the host; anything other than matching * the host can cause incorrect guest behaviour. @@ -7987,21 +8002,6 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) x86_cpu_gdb_init(cs); qemu_init_vcpu(cs); - /* - * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU - * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX - * based on inputs (sockets,cores,threads), it is still better to give - * users a warning. - */ - if (IS_AMD_CPU(env) && - !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && - env->topo_info.threads_per_core > 1) { - warn_report_once("This family of AMD CPU doesn't support " - "hyperthreading(%d). Please configure -smp " - "options properly or try enabling topoext " - "feature.", env->topo_info.threads_per_core); - } - #ifndef CONFIG_USER_ONLY x86_cpu_apic_realize(cpu, &local_err); if (local_err != NULL) { From 6e090ffe0d188e1f09d4efcd10d82158f92abfbb Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:23 -0500 Subject: [PATCH 0923/2892] cpu: Remove nr_cores from struct CPUState There is no user of it now, remove it. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241219110125.1266461-9-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/core/cpu-common.c | 1 - include/hw/core/cpu.h | 2 -- system/cpus.c | 1 - 3 files changed, 4 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 1edc16f65c..cb79566cc5 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -243,7 +243,6 @@ static void cpu_common_initfn(Object *obj) cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; /* user-mode doesn't have configurable SMP topology */ /* the default value is changed by qemu_init_vcpu() for system-mode */ - cpu->nr_cores = 1; cpu->nr_threads = 1; cpu->cflags_next_tb = -1; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c3ca0babcb..fb397cdfc5 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -407,7 +407,6 @@ struct qemu_work_item; * Under TCG this value is propagated to @tcg_cflags. * See TranslationBlock::TCG CF_CLUSTER_MASK. * @tcg_cflags: Pre-computed cflags for this cpu. - * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU core. * @thread: Host thread details, only live once @created is #true * @sem: WIN32 only semaphore used only for qtest @@ -466,7 +465,6 @@ struct CPUState { CPUClass *cc; /*< public >*/ - int nr_cores; int nr_threads; struct QemuThread *thread; diff --git a/system/cpus.c b/system/cpus.c index 99f83806c1..37e5892c24 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -687,7 +687,6 @@ void qemu_init_vcpu(CPUState *cpu) { MachineState *ms = MACHINE(qdev_get_machine()); - cpu->nr_cores = machine_topo_get_cores_per_socket(ms); cpu->nr_threads = ms->smp.threads; cpu->stopped = true; cpu->random_seed = qemu_guest_random_seed_thread_part1(); From c6bd2dd634208ca717b6dc010064fe34d1359080 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:24 -0500 Subject: [PATCH 0924/2892] i386/cpu: Set up CPUID_HT in x86_cpu_expand_features() instead of cpu_x86_cpuid() Currently CPUID_HT is evaluated in cpu_x86_cpuid() each time. It's not a correct usage of how feature bit is maintained and evaluated. The expected practice is that features are tracked in env->features[] and cpu_x86_cpuid() should be the consumer of env->features[]. Track CPUID_HT in env->features[FEAT_1_EDX] instead and evaluate it in cpu's realizefn(). Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20241219110125.1266461-10-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3f9475b485..3f0821c15f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6538,7 +6538,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = env->features[FEAT_1_EDX]; if (threads_per_pkg > 1) { *ebx |= threads_per_pkg << 16; - *edx |= CPUID_HT; } if (!cpu->enable_pmu) { *ecx &= ~CPUID_EXT_PDCM; @@ -7529,6 +7528,10 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } + if (x86_threads_per_pkg(&env->topo_info) > 1) { + env->features[FEAT_1_EDX] |= CPUID_HT; + } + for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { FeatureDep *d = &feature_dependencies[i]; if (!(env->features[d->from.index] & d->from.mask)) { From 99a637a86f55c8486b06c698656befdf012eec4d Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 19 Dec 2024 06:01:25 -0500 Subject: [PATCH 0925/2892] i386/cpu: Set and track CPUID_EXT3_CMP_LEG in env->features[FEAT_8000_0001_ECX] The correct usage is tracking and maintaining features in env->features[] instead of manually set it in cpu_x86_cpuid(). Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20241219110125.1266461-11-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3f0821c15f..1b9c11022c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6953,17 +6953,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->features[FEAT_8000_0001_ECX]; *edx = env->features[FEAT_8000_0001_EDX]; - /* The Linux kernel checks for the CMPLegacy bit and - * discards multiple thread information if it is set. - * So don't set it here for Intel to make Linux guests happy. - */ - if (threads_per_pkg > 1) { - if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || - env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || - env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { - *ecx |= 1 << 1; /* CmpLegacy bit */ - } - } if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && !(env->hflags & HF_LMA_MASK)) { *edx &= ~CPUID_EXT2_SYSCALL; @@ -7530,6 +7519,15 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) if (x86_threads_per_pkg(&env->topo_info) > 1) { env->features[FEAT_1_EDX] |= CPUID_HT; + + /* + * The Linux kernel checks for the CMPLegacy bit and + * discards multiple thread information if it is set. + * So don't set it here for Intel to make Linux guests happy. + */ + if (!IS_INTEL_CPU(env)) { + env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; + } } for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { From 3d30f882ce76bb4e61f033f1680e78695fed8ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:45 +0000 Subject: [PATCH 0926/2892] tests/functional: extend test_aarch64_virt with vulkan test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have virtio-gpu Vulkan support, let's add a test for it. Currently this is using images build by buildroot: https://lists.buildroot.org/pipermail/buildroot/2024-December/768196.html Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-24-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt.py | 76 ++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 201c5ed023..07c1c13638 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -13,10 +13,12 @@ import logging from subprocess import check_call, DEVNULL +from qemu.machine.machine import VMLaunchFailure + from qemu_test import QemuSystemTest, Asset -from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import exec_command, exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test import get_qemu_img +from qemu_test import skipIfMissingCommands, get_qemu_img class Aarch64VirtMachine(QemuSystemTest): @@ -132,5 +134,75 @@ class Aarch64VirtMachine(QemuSystemTest): self.common_aarch64_virt("virt,gic-version=2") + ASSET_VIRT_GPU_KERNEL = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'Image', + '89e5099d26166204cc5ca4bb6d1a11b92c217e1f82ec67e3ba363d09157462f6') + + ASSET_VIRT_GPU_ROOTFS = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'rootfs.ext4.zstd', + '792da7573f5dc2913ddb7c638151d4a6b2d028a4cb2afb38add513c1924bdad4') + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_gpu(self): + # This tests boots with a buildroot test image that contains + # vkmark and other GPU exercising tools. We run a headless + # weston that nevertheless still exercises the virtio-gpu + # backend. + + self.set_machine('virt') + self.require_accelerator("tcg") + + kernel_path = self.ASSET_VIRT_GPU_KERNEL.fetch() + image_path = self.uncompress(self.ASSET_VIRT_GPU_ROOTFS, format="zstd") + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0 root=/dev/vda') + + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "neoverse-v1,pauth-impdef=on") + self.vm.add_args("-machine", "virt,gic-version=max", + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.add_args("-smp", "2", "-m", "2048") + self.vm.add_args("-device", + "virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") + self.vm.add_args("-display", "egl-headless") + self.vm.add_args("-display", "dbus,gl=on") + self.vm.add_args("-device", "virtio-blk-device,drive=hd0") + self.vm.add_args("-blockdev", + "driver=raw,file.driver=file," + "node-name=hd0,read-only=on," + f"file.filename={image_path}") + self.vm.add_args("-snapshot") + + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "old virglrenderer, blob resources unsupported" in excp.output: + self.skipTest("No blob support for virtio-gpu") + elif "old virglrenderer, venus unsupported" in excp.output: + self.skipTest("No venus support for virtio-gpu") + elif "egl: no drm render node available" in excp.output: + self.skipTest("Can't access host DRM render node") + else: + self.log.info(f"unhandled launch failure: {excp.output}") + raise excp + + self.wait_for_console_pattern('buildroot login:') + exec_command(self, 'root') + exec_command(self, 'export XDG_RUNTIME_DIR=/tmp') + exec_command_and_wait_for_pattern(self, + "weston -B headless " + "--renderer gl " + "--shell kiosk " + "-- vkmark -b:duration=1.0", + "vkmark Score") + + if __name__ == '__main__': QemuSystemTest.main() From 453005c01a80bcd12cf6181badac717135b26ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:46 +0000 Subject: [PATCH 0927/2892] tests/lcitool: bump to latest version of libvirt-ci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will shortly need this to build our riscv64 cross container. However to keep the delta down just do the bump first. As ccache4 is now preferred for FreeBSD to get the latest version there is a little update in the FreeBSD metadata. Reviewed-by: Daniel P. Berrangé Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-25-alex.bennee@linaro.org> --- .gitlab-ci.d/cirrus/freebsd-14.vars | 2 +- tests/lcitool/libvirt-ci | 2 +- tests/vm/generated/freebsd.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/cirrus/freebsd-14.vars b/.gitlab-ci.d/cirrus/freebsd-14.vars index 0a7ac5e0e1..0997c47af5 100644 --- a/.gitlab-ci.d/cirrus/freebsd-14.vars +++ b/.gitlab-ci.d/cirrus/freebsd-14.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip' -PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index 9ad3f70bde..b6a65806bc 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit 9ad3f70bde9865d5ad18f36d256d472e72b5cbf3 +Subproject commit b6a65806bc9b2b56985f5e97c936b77c7e7a99fc diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 3cb7fb7060..81fc38d798 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -13,7 +13,7 @@ "bzip2", "ca_root_nss", "capstone4", - "ccache", + "ccache4", "cmocka", "ctags", "curl", From 66944b69b27af359ab98cf9c8815345f74d1d8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:47 +0000 Subject: [PATCH 0928/2892] tests/docker: move riscv64 cross container from sid to trixie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although riscv64 isn't going to be a release architecture for trixie the packages are still built while it is testing. Moving from sid will also avoid some of the volatility we get from tracking the bleeding edge. Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-26-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-riscv64-cross.docker | 4 ++-- tests/lcitool/refresh | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 4d8ca83cb3..b0386cd3a1 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-sid qemu-minimal +# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-13 qemu-minimal # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:sid-slim +FROM docker.io/library/debian:trixie-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 6720516b94..53f8d2585f 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -220,7 +220,9 @@ try: trailer=cross_build("powerpc64le-linux-gnu-", "ppc64-softmmu,ppc64-linux-user")) - generate_dockerfile("debian-riscv64-cross", "debian-sid", + # while not yet a release architecture the packages are still + # build while part of testing + generate_dockerfile("debian-riscv64-cross", "debian-13", project="qemu-minimal", cross="riscv64", trailer=cross_build("riscv64-linux-gnu-", From 41618a2674d7b5c2ec685d30ec5c543c5c8be93d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Jan 2025 12:10:48 +0000 Subject: [PATCH 0929/2892] tests/lcitool: remove temp workaround for debian mips64el MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workaround applied in commit c60473d29254b79d9437eface8b342e84663ba66 Author: Alex Bennée Date: Wed Oct 2 10:03:33 2024 +0200 testing: bump mips64el cross to bookworm and fix package list Is no longer required since the affected builds are now fixed. Signed-off-by: Daniel P. Berrangé Tested-by: Thomas Huth Message-Id: <20241217133525.3836570-1-berrange@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-27-alex.bennee@linaro.org> --- .../dockerfiles/debian-mips64el-cross.docker | 9 ++++++ tests/lcitool/mappings.yml | 29 ------------------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index c09a8da890..9f6c4763c5 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -93,13 +93,18 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcmocka-dev:mips64el \ libcurl4-gnutls-dev:mips64el \ libdaxctl-dev:mips64el \ + libdrm-dev:mips64el \ + libepoxy-dev:mips64el \ libfdt-dev:mips64el \ libffi-dev:mips64el \ libfuse3-dev:mips64el \ + libgbm-dev:mips64el \ libgcrypt20-dev:mips64el \ libglib2.0-dev:mips64el \ libglusterfs-dev:mips64el \ libgnutls28-dev:mips64el \ + libgtk-3-dev:mips64el \ + libgtk-vnc-2.0-dev:mips64el \ libibverbs-dev:mips64el \ libiscsi-dev:mips64el \ libjemalloc-dev:mips64el \ @@ -119,6 +124,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ librbd-dev:mips64el \ librdmacm-dev:mips64el \ libsasl2-dev:mips64el \ + libsdl2-dev:mips64el \ + libsdl2-image-dev:mips64el \ libseccomp-dev:mips64el \ libselinux1-dev:mips64el \ libslirp-dev:mips64el \ @@ -134,6 +141,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libusb-1.0-0-dev:mips64el \ libusbredirhost-dev:mips64el \ libvdeplug-dev:mips64el \ + libvirglrenderer-dev:mips64el \ + libvte-2.91-dev:mips64el \ libxdp-dev:mips64el \ libzstd-dev:mips64el \ nettle-dev:mips64el \ diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index f8186b0e69..74eb13d62b 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -6,23 +6,6 @@ mappings: flake8: OpenSUSELeap15: - # Due to https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1081535 we - # have to disable all packages that depend on libgl1-mesa-dri:mips64el - gtk3: - mips64el-deb: - - libdrm: - mips64el-deb: - - libepoxy: - mips64el-deb: - - gtk-vnc: - mips64el-deb: - - mesa-libgbm: - mips64el-deb: - meson: OpenSUSELeap15: @@ -81,18 +64,6 @@ mappings: python3-wheel: OpenSUSELeap15: python311-pip - sdl2: - mips64el-deb: - - sdl2-image: - mips64el-deb: - - virglrenderer: - mips64el-deb: - - vte: - mips64el-deb: - pypi_mappings: # Request more recent version meson: From f673a45725cd80c2a2456217a60625f5306678d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:49 +0000 Subject: [PATCH 0930/2892] tests/vm: fix build_path based path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We no longer need to go into the per-arch build directories to find the build directories binary. Lets call it directly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-28-alex.bennee@linaro.org> --- tests/vm/basevm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 4a1af04b9a..6f3f2e76df 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -520,8 +520,7 @@ def get_qemu_path(arch, build_path=None): if "QEMU" in os.environ: qemu_path = os.environ["QEMU"] elif build_path: - qemu_path = os.path.join(build_path, arch + "-softmmu") - qemu_path = os.path.join(qemu_path, "qemu-system-" + arch) + qemu_path = os.path.join(build_path, "qemu-system-" + arch) else: # Default is to use system path for qemu. qemu_path = "qemu-system-" + arch From 3f6b694bf06c9b19e3d18b94cc33292e94df497d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:50 +0000 Subject: [PATCH 0931/2892] tests/vm: partially un-tabify help output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the make syntax itself uses tabs having a mixture of tabs and spaces in the vm-help output make no sense and confuses things lining up between terminal and editor. Fix that. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-29-alex.bennee@linaro.org> --- tests/vm/Makefile.include | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 13ed80f72d..d80ca79a28 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -64,23 +64,23 @@ endif @echo " vm-boot-ssh- - Boot guest and login via ssh" @echo @echo "Special variables:" - @echo " BUILD_TARGET=foo - Override the build target" - @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" - @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' - @echo " J=[0..9]* - Override the -jN parameter for make commands" - @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " - @echo " USE_TCG=1 - Use TCG for cross-arch images" - @echo " QEMU=/path/to/qemu - Change path to QEMU binary" + @echo " BUILD_TARGET=foo - Override the build target" + @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" + @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' + @echo " J=[0..9]* - Override the -jN parameter for make commands" + @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " + @echo " USE_TCG=1 - Use TCG for cross-arch images" + @echo " QEMU=/path/to/qemu - Change path to QEMU binary" ifeq ($(HAVE_PYTHON_YAML),yes) - @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file." + @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file." else @echo " (install python3-yaml to enable support for yaml file to configure a VM.)" endif - @echo " See conf_example_*.yml for file format details." - @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" - @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build." - @echo " TARGET_LIST=a,b,c - Override target list in builds" - @echo " V=1 - Enable verbose output on host and guest commands" + @echo " See conf_example_*.yml for file format details." + @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" + @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build." + @echo " TARGET_LIST=a,b,c - Override target list in builds" + @echo " V=1 - Enable verbose output on host and guest commands" vm-build-all: $(addprefix vm-build-, $(IMAGES)) From 376c490c1ed9366e73513ada8f577642ab57ec8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:51 +0000 Subject: [PATCH 0932/2892] tests/vm: allow interactive login as root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful when debugging and you want to add packages to an image. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-30-alex.bennee@linaro.org> --- tests/vm/Makefile.include | 3 ++- tests/vm/basevm.py | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index d80ca79a28..14188bba1c 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -66,6 +66,7 @@ endif @echo "Special variables:" @echo " BUILD_TARGET=foo - Override the build target" @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" + @echo " ROOT_USER=1 - Login as root user for interactive shell" @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' @echo " J=[0..9]* - Override the -jN parameter for make commands" @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " @@ -141,6 +142,6 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(if $(EFI_AARCH64),--efi-aarch64 $(EFI_AARCH64)) \ $(if $(LOG_CONSOLE),--log-console) \ --image "$<" \ - --interactive \ + $(if $(ROOT_USER),--interactive-root,-interactive) \ false, \ " VM-BOOT-SSH $*") || true diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 6f3f2e76df..6d41ac7574 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -612,8 +612,11 @@ def parse_args(vmcls): parser.add_argument("--source-path", default=None, help="Path of source directory, "\ "for finding additional files. ") - parser.add_argument("--interactive", "-I", action="store_true", - help="Interactively run command") + int_ops = parser.add_mutually_exclusive_group() + int_ops.add_argument("--interactive", "-I", action="store_true", + help="Interactively run command") + int_ops.add_argument("--interactive-root", action="store_true", + help="Interactively run command as root") parser.add_argument("--snapshot", "-s", action="store_true", help="run tests with a snapshot") parser.add_argument("--genisoimage", default="genisoimage", @@ -675,6 +678,8 @@ def main(vmcls, config=None): exitcode = 3 if args.interactive: vm.ssh() + elif args.interactive_root: + vm.ssh_root() if not args.snapshot: vm.graceful_shutdown() From 1b1e1b00714eddee53f01c33ee18034de2299ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 8 Jan 2025 12:10:52 +0000 Subject: [PATCH 0933/2892] pc-bios: ensure keymaps dependencies set vnc tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I was seeing failures on vnc-display-test on FreeBSD: make vm-build-freebsd V=1 TARGET_LIST=aarch64-softmmu BUILD_TARGET=check-qtest QEMU_LOCAL=1 DEBUG=1 Leads to: qemu-system-aarch64: -vnc none: could not read keymap file: 'en-us' Broken pipe ../src/tests/qtest/libqtest.c:196: kill_qemu() tried to terminate QEMU process but encountered exit status 1 (expected 0) which was as far as I could tell because we don't populate the $BLD/pc-bios/keymaps (although scripts/symlink-install-tree.py attempts to symlink qemu-bundle/usr/local/share/qemu/keymaps/ to that dir). Reviewed-by: Paolo Bonzini Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-31-alex.bennee@linaro.org> --- pc-bios/keymaps/meson.build | 17 ++++++++--------- tests/qtest/meson.build | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/pc-bios/keymaps/meson.build b/pc-bios/keymaps/meson.build index 0bd8ce0077..a79a09b276 100644 --- a/pc-bios/keymaps/meson.build +++ b/pc-bios/keymaps/meson.build @@ -39,19 +39,18 @@ else native_qemu_keymap = qemu_keymap endif +keymap_targets = [] if native_qemu_keymap.found() - t = [] foreach km, args: keymaps # generate with qemu-kvm - t += custom_target(km, - build_by_default: true, - output: km, - command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], - install: have_system, - install_dir: qemu_datadir / 'keymaps') + keymap_targets += custom_target(km, + build_by_default: true, + output: km, + command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], + install: have_system, + install_dir: qemu_datadir / 'keymaps') endforeach - - alias_target('update-keymaps', t) + alias_target('update-keymaps', keymap_targets) else install_data(keymaps.keys(), install_dir: qemu_datadir / 'keymaps') endif diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c5a70021c5..f75c1057a4 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -383,7 +383,7 @@ qtests = { if vnc.found() gvnc = dependency('gvnc-1.0', method: 'pkg-config', required: false) if gvnc.found() - qtests += {'vnc-display-test': [gvnc]} + qtests += {'vnc-display-test': [gvnc, keymap_targets]} qtests_generic += [ 'vnc-display-test' ] endif endif From e1fca1bddd003e159a7d8e050d5cd768bdba8128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Jan 2025 12:10:53 +0000 Subject: [PATCH 0934/2892] dockerfiles: Remove 'MAINTAINER' entry in debian-tricore-cross.docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMSAT closed its email service [*] so my personal email address is now defunct. Remove it to avoid bouncing emails. [*] https://web.archive.org/web/20240617194936/https://forum.amsat-dl.org/index.php?thread/4581-amsat-mail-alias-service-to-end-august-1-2024/ Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250102152513.61065-1-philmd@linaro.org> [AJB: update URL to web.archive.org] Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-32-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-tricore-cross.docker | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 479b4d6eba..7e00e870ce 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -11,8 +11,6 @@ # FROM docker.io/library/debian:11-slim -MAINTAINER Philippe Mathieu-Daudé - RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \ From ae0aef5e761ad6425c634f3d83b8cc5b52d1ce0a Mon Sep 17 00:00:00 2001 From: Wainer dos Santos Moschetta Date: Wed, 8 Jan 2025 12:10:54 +0000 Subject: [PATCH 0935/2892] MAINTAINERS: Remove myself from reviewers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The time I spent contributing to QEMU was great, but I've not been active for a long time now. So removing myself from the reviewers list of "Integration Testing with the Avocado framework" and "Build and test automation" subsystems. Signed-off-by: Wainer dos Santos Moschetta Message-Id: <20250103194450.360789-1-wainersm@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20250108121054.1126164-33-alex.bennee@linaro.org> --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index cab9018d9f..c1d954c9de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4152,7 +4152,6 @@ M: Alex Bennée T: git https://gitlab.com/stsquad/qemu testing/next M: Philippe Mathieu-Daudé M: Thomas Huth -R: Wainer dos Santos Moschetta S: Maintained F: .github/workflows/lockdown.yml F: .gitlab-ci.yml @@ -4198,7 +4197,6 @@ Integration Testing with the Avocado framework W: https://trello.com/b/6Qi1pxVn/avocado-qemu R: Cleber Rosa R: Philippe Mathieu-Daudé -R: Wainer dos Santos Moschetta S: Odd Fixes F: tests/avocado/ From 906853e1427a1ff89c64c0ebb6faa9c68f0a5d74 Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Mon, 13 Jan 2025 12:35:32 +0000 Subject: [PATCH 0936/2892] hw/arm_sysctl: fix extracting 31th bit of val 1 << 31 is casted to uint64_t while bitwise and with val. So this value may become 0xffffffff80000000 but only 31th "start" bit is required. This is not possible in practice because the MemoryRegionOps uses the default max access size of 4 bytes and so none of the upper bytes of val will be set, but the bitfield extract API is clearer anyway. Use the bitfield extract() API instead. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Anastasia Belova Message-id: 20241220125429.7552-1-abelova@astralinux.ru Reviewed-by: Peter Maydell [PMM: add clarification to commit message] Signed-off-by: Peter Maydell --- hw/misc/arm_sysctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 016a302e67..01663407ec 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -520,7 +520,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, * as zero. */ s->sys_cfgctrl = val & ~((3 << 18) | (1 << 31)); - if (val & (1 << 31)) { + if (extract64(val, 31, 1)) { /* Start bit set -- actually do something */ unsigned int dcc = extract32(s->sys_cfgctrl, 26, 4); unsigned int function = extract32(s->sys_cfgctrl, 20, 6); From 593b910ebedb9f8969aba22ca35970f710722ba7 Mon Sep 17 00:00:00 2001 From: Tigran Sogomonian Date: Mon, 13 Jan 2025 12:35:32 +0000 Subject: [PATCH 0937/2892] hw/misc: cast rpm to uint64_t The value of an arithmetic expression 'rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION' is a subject to overflow because its operands are not cast to a larger data type before performing arithmetic. Thus, need to cast rpm to uint64_t. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Tigran Sogomonian Reviewed-by: Patrick Leis Reviewed-by: Hao Wu Message-id: 20241226130311.1349-1-tsogomonian@astralinux.ru Signed-off-by: Peter Maydell --- hw/misc/npcm7xx_mft.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c index 9fcc69fe5c..e565cac05d 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -172,8 +172,9 @@ static NPCM7xxMFTCaptureState npcm7xx_mft_compute_cnt( * RPM = revolution/min. The time for one revlution (in ns) is * MINUTE_TO_NANOSECOND / RPM. */ - count = clock_ns_to_ticks(clock, (60 * NANOSECONDS_PER_SECOND) / - (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); + count = clock_ns_to_ticks(clock, + (uint64_t)(60 * NANOSECONDS_PER_SECOND) / + ((uint64_t)rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); } if (count > NPCM7XX_MFT_MAX_CNT) { From 16e8c947bd6f741d328bb1521f62f69dccaa3fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jan 2025 12:35:33 +0000 Subject: [PATCH 0938/2892] tests/qtest/boot-serial-test: Improve ASM comments of PL011 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-indent ASM comments adding the 'loop:' label. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Fabiano Rosas Signed-off-by: Peter Maydell --- tests/qtest/boot-serial-test.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index 3b92fa5d50..a71d285780 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -70,18 +70,18 @@ static const uint8_t kernel_plml605[] = { }; static const uint8_t bios_raspi2[] = { - 0x08, 0x30, 0x9f, 0xe5, /* ldr r3,[pc,#8] Get base */ - 0x54, 0x20, 0xa0, 0xe3, /* mov r2,#'T' */ - 0x00, 0x20, 0xc3, 0xe5, /* strb r2,[r3] */ - 0xfb, 0xff, 0xff, 0xea, /* b loop */ - 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */ + 0x08, 0x30, 0x9f, 0xe5, /* loop: ldr r3, [pc, #8] Get &UART0 */ + 0x54, 0x20, 0xa0, 0xe3, /* mov r2, #'T' */ + 0x00, 0x20, 0xc3, 0xe5, /* strb r2, [r3] *TXDAT = 'T' */ + 0xfb, 0xff, 0xff, 0xea, /* b -12 (loop) */ + 0x00, 0x10, 0x20, 0x3f, /* UART0: 0x3f201000 */ }; static const uint8_t kernel_aarch64[] = { - 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */ - 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */ - 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */ - 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ + 0x81, 0x0a, 0x80, 0x52, /* loop: mov w1, #'T' */ + 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */ + 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] *TXDAT = 'T' */ + 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ }; static const uint8_t kernel_nrf51[] = { From afd757e46166afe86a6df4407b64203e53ea44f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jan 2025 12:35:33 +0000 Subject: [PATCH 0939/2892] tests/qtest/boot-serial-test: Reduce for() loop in PL011 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since registers are not modified, we don't need to refill their values. Directly jump to the previous store instruction to keep filling the TXDAT register. The equivalent C code remains: while (true) { *UART_DATA = 'T'; } Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Fabiano Rosas Signed-off-by: Peter Maydell --- tests/qtest/boot-serial-test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index a71d285780..553575ca75 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -70,18 +70,18 @@ static const uint8_t kernel_plml605[] = { }; static const uint8_t bios_raspi2[] = { - 0x08, 0x30, 0x9f, 0xe5, /* loop: ldr r3, [pc, #8] Get &UART0 */ + 0x08, 0x30, 0x9f, 0xe5, /* ldr r3, [pc, #8] Get &UART0 */ 0x54, 0x20, 0xa0, 0xe3, /* mov r2, #'T' */ - 0x00, 0x20, 0xc3, 0xe5, /* strb r2, [r3] *TXDAT = 'T' */ - 0xfb, 0xff, 0xff, 0xea, /* b -12 (loop) */ + 0x00, 0x20, 0xc3, 0xe5, /* loop: strb r2, [r3] *TXDAT = 'T' */ + 0xff, 0xff, 0xff, 0xea, /* b -4 (loop) */ 0x00, 0x10, 0x20, 0x3f, /* UART0: 0x3f201000 */ }; static const uint8_t kernel_aarch64[] = { - 0x81, 0x0a, 0x80, 0x52, /* loop: mov w1, #'T' */ + 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */ 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */ - 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] *TXDAT = 'T' */ - 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ + 0x41, 0x00, 0x00, 0x39, /* loop: strb w1, [x2] *TXDAT = 'T' */ + 0xff, 0xff, 0xff, 0x17, /* b -4 (loop) */ }; static const uint8_t kernel_nrf51[] = { From 80b8b01f89387181378aa251e7a5454470ec5045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jan 2025 12:35:33 +0000 Subject: [PATCH 0940/2892] tests/qtest/boot-serial-test: Reorder pair of instructions in PL011 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the next commit we are going to use a different value for the $w1 register, maintaining the same $x2 value. In order to keep the next commit trivial to review, set $x2 before $w1. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Fabiano Rosas Signed-off-by: Peter Maydell --- tests/qtest/boot-serial-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index 553575ca75..bcfa504826 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -78,8 +78,8 @@ static const uint8_t bios_raspi2[] = { }; static const uint8_t kernel_aarch64[] = { - 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */ 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */ + 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */ 0x41, 0x00, 0x00, 0x39, /* loop: strb w1, [x2] *TXDAT = 'T' */ 0xff, 0xff, 0xff, 0x17, /* b -4 (loop) */ }; From 1bb7f615a517ba85ebdbbd30d8de2120b4598d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jan 2025 12:35:33 +0000 Subject: [PATCH 0941/2892] tests/qtest/boot-serial-test: Initialize PL011 Control register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests using the PL011 UART of the virt and raspi machines weren't properly enabling the UART and its transmitter previous to sending characters. Follow the PL011 manual initialization recommendation by setting the proper bits of the control register. Update the ASM code prefixing: *UART_CTRL = UART_ENABLE | TX_ENABLE; to: while (true) { *UART_DATA = 'T'; } Note, since commit 51b61dd4d56 ("hw/char/pl011: Warn when using disabled transmitter") incomplete PL011 initialization can be logged using the '-d guest_errors' command line option. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- tests/qtest/boot-serial-test.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index bcfa504826..ffa9e780ad 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -70,15 +70,20 @@ static const uint8_t kernel_plml605[] = { }; static const uint8_t bios_raspi2[] = { - 0x08, 0x30, 0x9f, 0xe5, /* ldr r3, [pc, #8] Get &UART0 */ + 0x10, 0x30, 0x9f, 0xe5, /* ldr r3, [pc, #16] Get &UART0 */ + 0x10, 0x20, 0x9f, 0xe5, /* ldr r2, [pc, #16] Get &CR */ + 0xb0, 0x23, 0xc3, 0xe1, /* strh r2, [r3, #48] Set CR */ 0x54, 0x20, 0xa0, 0xe3, /* mov r2, #'T' */ 0x00, 0x20, 0xc3, 0xe5, /* loop: strb r2, [r3] *TXDAT = 'T' */ 0xff, 0xff, 0xff, 0xea, /* b -4 (loop) */ 0x00, 0x10, 0x20, 0x3f, /* UART0: 0x3f201000 */ + 0x01, 0x01, 0x00, 0x00, /* CR: 0x101 = UARTEN|TXE */ }; static const uint8_t kernel_aarch64[] = { 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */ + 0x21, 0x20, 0x80, 0x52, /* mov w1, 0x101 CR = UARTEN|TXE */ + 0x41, 0x60, 0x00, 0x79, /* strh w1, [x2, #48] Set CR */ 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */ 0x41, 0x00, 0x00, 0x39, /* loop: strb w1, [x2] *TXDAT = 'T' */ 0xff, 0xff, 0xff, 0x17, /* b -4 (loop) */ From 538b764d3417b9295cf7f8b617eb7bc90cbc4ad6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 13 Jan 2025 12:35:34 +0000 Subject: [PATCH 0942/2892] target/arm: Move minor arithmetic helpers out of helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit helper.c includes some small TCG helper functions used for mostly arithmetic instructions. These are TCG only and there's no need for them to be in the large and unwieldy helper.c. Move them out to their own source file in the tcg/ subdirectory, together with the op_addsub.h multiply-included template header that they use. Since we are moving op_addsub.h, we take the opportunity to give it a name which matches our convention for files which are not true header files but which are #included from other C files: op_addsub.c.inc. (Ironically, this means that helper.c no longer contains any TCG helper function definitions at all.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250110131211.2546314-1-peter.maydell@linaro.org Reviewed-by: Alex Bennée --- target/arm/helper.c | 285 ----------------- target/arm/tcg/arith_helper.c | 296 ++++++++++++++++++ target/arm/tcg/meson.build | 1 + .../arm/{op_addsub.h => tcg/op_addsub.c.inc} | 0 4 files changed, 297 insertions(+), 285 deletions(-) create mode 100644 target/arm/tcg/arith_helper.c rename target/arm/{op_addsub.h => tcg/op_addsub.c.inc} (100%) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5b595f951b..6399767851 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -17,11 +17,9 @@ #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/bitops.h" -#include "qemu/crc32c.h" #include "qemu/qemu-print.h" #include "exec/exec-all.h" #include "exec/translation-block.h" -#include /* for crc32 */ #include "hw/irq.h" #include "system/cpu-timers.h" #include "system/kvm.h" @@ -10984,289 +10982,6 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, }; } -/* - * Note that signed overflow is undefined in C. The following routines are - * careful to use unsigned types where modulo arithmetic is required. - * Failure to do so _will_ break on newer gcc. - */ - -/* Signed saturating arithmetic. */ - -/* Perform 16-bit signed saturating addition. */ -static inline uint16_t add16_sat(uint16_t a, uint16_t b) -{ - uint16_t res; - - res = a + b; - if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) { - if (a & 0x8000) { - res = 0x8000; - } else { - res = 0x7fff; - } - } - return res; -} - -/* Perform 8-bit signed saturating addition. */ -static inline uint8_t add8_sat(uint8_t a, uint8_t b) -{ - uint8_t res; - - res = a + b; - if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) { - if (a & 0x80) { - res = 0x80; - } else { - res = 0x7f; - } - } - return res; -} - -/* Perform 16-bit signed saturating subtraction. */ -static inline uint16_t sub16_sat(uint16_t a, uint16_t b) -{ - uint16_t res; - - res = a - b; - if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) { - if (a & 0x8000) { - res = 0x8000; - } else { - res = 0x7fff; - } - } - return res; -} - -/* Perform 8-bit signed saturating subtraction. */ -static inline uint8_t sub8_sat(uint8_t a, uint8_t b) -{ - uint8_t res; - - res = a - b; - if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) { - if (a & 0x80) { - res = 0x80; - } else { - res = 0x7f; - } - } - return res; -} - -#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16); -#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16); -#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8); -#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8); -#define PFX q - -#include "op_addsub.h" - -/* Unsigned saturating arithmetic. */ -static inline uint16_t add16_usat(uint16_t a, uint16_t b) -{ - uint16_t res; - res = a + b; - if (res < a) { - res = 0xffff; - } - return res; -} - -static inline uint16_t sub16_usat(uint16_t a, uint16_t b) -{ - if (a > b) { - return a - b; - } else { - return 0; - } -} - -static inline uint8_t add8_usat(uint8_t a, uint8_t b) -{ - uint8_t res; - res = a + b; - if (res < a) { - res = 0xff; - } - return res; -} - -static inline uint8_t sub8_usat(uint8_t a, uint8_t b) -{ - if (a > b) { - return a - b; - } else { - return 0; - } -} - -#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16); -#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16); -#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8); -#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8); -#define PFX uq - -#include "op_addsub.h" - -/* Signed modulo arithmetic. */ -#define SARITH16(a, b, n, op) do { \ - int32_t sum; \ - sum = (int32_t)(int16_t)(a) op (int32_t)(int16_t)(b); \ - RESULT(sum, n, 16); \ - if (sum >= 0) \ - ge |= 3 << (n * 2); \ - } while (0) - -#define SARITH8(a, b, n, op) do { \ - int32_t sum; \ - sum = (int32_t)(int8_t)(a) op (int32_t)(int8_t)(b); \ - RESULT(sum, n, 8); \ - if (sum >= 0) \ - ge |= 1 << n; \ - } while (0) - - -#define ADD16(a, b, n) SARITH16(a, b, n, +) -#define SUB16(a, b, n) SARITH16(a, b, n, -) -#define ADD8(a, b, n) SARITH8(a, b, n, +) -#define SUB8(a, b, n) SARITH8(a, b, n, -) -#define PFX s -#define ARITH_GE - -#include "op_addsub.h" - -/* Unsigned modulo arithmetic. */ -#define ADD16(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \ - RESULT(sum, n, 16); \ - if ((sum >> 16) == 1) \ - ge |= 3 << (n * 2); \ - } while (0) - -#define ADD8(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \ - RESULT(sum, n, 8); \ - if ((sum >> 8) == 1) \ - ge |= 1 << n; \ - } while (0) - -#define SUB16(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \ - RESULT(sum, n, 16); \ - if ((sum >> 16) == 0) \ - ge |= 3 << (n * 2); \ - } while (0) - -#define SUB8(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \ - RESULT(sum, n, 8); \ - if ((sum >> 8) == 0) \ - ge |= 1 << n; \ - } while (0) - -#define PFX u -#define ARITH_GE - -#include "op_addsub.h" - -/* Halved signed arithmetic. */ -#define ADD16(a, b, n) \ - RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16) -#define SUB16(a, b, n) \ - RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16) -#define ADD8(a, b, n) \ - RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8) -#define SUB8(a, b, n) \ - RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8) -#define PFX sh - -#include "op_addsub.h" - -/* Halved unsigned arithmetic. */ -#define ADD16(a, b, n) \ - RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16) -#define SUB16(a, b, n) \ - RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16) -#define ADD8(a, b, n) \ - RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8) -#define SUB8(a, b, n) \ - RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8) -#define PFX uh - -#include "op_addsub.h" - -static inline uint8_t do_usad(uint8_t a, uint8_t b) -{ - if (a > b) { - return a - b; - } else { - return b - a; - } -} - -/* Unsigned sum of absolute byte differences. */ -uint32_t HELPER(usad8)(uint32_t a, uint32_t b) -{ - uint32_t sum; - sum = do_usad(a, b); - sum += do_usad(a >> 8, b >> 8); - sum += do_usad(a >> 16, b >> 16); - sum += do_usad(a >> 24, b >> 24); - return sum; -} - -/* For ARMv6 SEL instruction. */ -uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) -{ - uint32_t mask; - - mask = 0; - if (flags & 1) { - mask |= 0xff; - } - if (flags & 2) { - mask |= 0xff00; - } - if (flags & 4) { - mask |= 0xff0000; - } - if (flags & 8) { - mask |= 0xff000000; - } - return (a & mask) | (b & ~mask); -} - -/* - * CRC helpers. - * The upper bytes of val (above the number specified by 'bytes') must have - * been zeroed out by the caller. - */ -uint32_t HELPER(crc32)(uint32_t acc, uint32_t val, uint32_t bytes) -{ - uint8_t buf[4]; - - stl_le_p(buf, val); - - /* zlib crc32 converts the accumulator and output to one's complement. */ - return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; -} - -uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) -{ - uint8_t buf[4]; - - stl_le_p(buf, val); - - /* Linux crc32c converts the output to one's complement. */ - return crc32c(acc, buf, bytes) ^ 0xffffffff; -} /* * Return the exception level to which FP-disabled exceptions should diff --git a/target/arm/tcg/arith_helper.c b/target/arm/tcg/arith_helper.c new file mode 100644 index 0000000000..9a555c7966 --- /dev/null +++ b/target/arm/tcg/arith_helper.c @@ -0,0 +1,296 @@ +/* + * ARM generic helpers for various arithmetical operations. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/crc32c.h" +#include /* for crc32 */ + +/* + * Note that signed overflow is undefined in C. The following routines are + * careful to use unsigned types where modulo arithmetic is required. + * Failure to do so _will_ break on newer gcc. + */ + +/* Signed saturating arithmetic. */ + +/* Perform 16-bit signed saturating addition. */ +static inline uint16_t add16_sat(uint16_t a, uint16_t b) +{ + uint16_t res; + + res = a + b; + if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) { + if (a & 0x8000) { + res = 0x8000; + } else { + res = 0x7fff; + } + } + return res; +} + +/* Perform 8-bit signed saturating addition. */ +static inline uint8_t add8_sat(uint8_t a, uint8_t b) +{ + uint8_t res; + + res = a + b; + if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) { + if (a & 0x80) { + res = 0x80; + } else { + res = 0x7f; + } + } + return res; +} + +/* Perform 16-bit signed saturating subtraction. */ +static inline uint16_t sub16_sat(uint16_t a, uint16_t b) +{ + uint16_t res; + + res = a - b; + if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) { + if (a & 0x8000) { + res = 0x8000; + } else { + res = 0x7fff; + } + } + return res; +} + +/* Perform 8-bit signed saturating subtraction. */ +static inline uint8_t sub8_sat(uint8_t a, uint8_t b) +{ + uint8_t res; + + res = a - b; + if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) { + if (a & 0x80) { + res = 0x80; + } else { + res = 0x7f; + } + } + return res; +} + +#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16); +#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16); +#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8); +#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8); +#define PFX q + +#include "op_addsub.c.inc" + +/* Unsigned saturating arithmetic. */ +static inline uint16_t add16_usat(uint16_t a, uint16_t b) +{ + uint16_t res; + res = a + b; + if (res < a) { + res = 0xffff; + } + return res; +} + +static inline uint16_t sub16_usat(uint16_t a, uint16_t b) +{ + if (a > b) { + return a - b; + } else { + return 0; + } +} + +static inline uint8_t add8_usat(uint8_t a, uint8_t b) +{ + uint8_t res; + res = a + b; + if (res < a) { + res = 0xff; + } + return res; +} + +static inline uint8_t sub8_usat(uint8_t a, uint8_t b) +{ + if (a > b) { + return a - b; + } else { + return 0; + } +} + +#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16); +#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16); +#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8); +#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8); +#define PFX uq + +#include "op_addsub.c.inc" + +/* Signed modulo arithmetic. */ +#define SARITH16(a, b, n, op) do { \ + int32_t sum; \ + sum = (int32_t)(int16_t)(a) op (int32_t)(int16_t)(b); \ + RESULT(sum, n, 16); \ + if (sum >= 0) \ + ge |= 3 << (n * 2); \ + } while (0) + +#define SARITH8(a, b, n, op) do { \ + int32_t sum; \ + sum = (int32_t)(int8_t)(a) op (int32_t)(int8_t)(b); \ + RESULT(sum, n, 8); \ + if (sum >= 0) \ + ge |= 1 << n; \ + } while (0) + + +#define ADD16(a, b, n) SARITH16(a, b, n, +) +#define SUB16(a, b, n) SARITH16(a, b, n, -) +#define ADD8(a, b, n) SARITH8(a, b, n, +) +#define SUB8(a, b, n) SARITH8(a, b, n, -) +#define PFX s +#define ARITH_GE + +#include "op_addsub.c.inc" + +/* Unsigned modulo arithmetic. */ +#define ADD16(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \ + RESULT(sum, n, 16); \ + if ((sum >> 16) == 1) \ + ge |= 3 << (n * 2); \ + } while (0) + +#define ADD8(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \ + RESULT(sum, n, 8); \ + if ((sum >> 8) == 1) \ + ge |= 1 << n; \ + } while (0) + +#define SUB16(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \ + RESULT(sum, n, 16); \ + if ((sum >> 16) == 0) \ + ge |= 3 << (n * 2); \ + } while (0) + +#define SUB8(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \ + RESULT(sum, n, 8); \ + if ((sum >> 8) == 0) \ + ge |= 1 << n; \ + } while (0) + +#define PFX u +#define ARITH_GE + +#include "op_addsub.c.inc" + +/* Halved signed arithmetic. */ +#define ADD16(a, b, n) \ + RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16) +#define SUB16(a, b, n) \ + RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16) +#define ADD8(a, b, n) \ + RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8) +#define SUB8(a, b, n) \ + RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8) +#define PFX sh + +#include "op_addsub.c.inc" + +/* Halved unsigned arithmetic. */ +#define ADD16(a, b, n) \ + RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16) +#define SUB16(a, b, n) \ + RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16) +#define ADD8(a, b, n) \ + RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8) +#define SUB8(a, b, n) \ + RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8) +#define PFX uh + +#include "op_addsub.c.inc" + +static inline uint8_t do_usad(uint8_t a, uint8_t b) +{ + if (a > b) { + return a - b; + } else { + return b - a; + } +} + +/* Unsigned sum of absolute byte differences. */ +uint32_t HELPER(usad8)(uint32_t a, uint32_t b) +{ + uint32_t sum; + sum = do_usad(a, b); + sum += do_usad(a >> 8, b >> 8); + sum += do_usad(a >> 16, b >> 16); + sum += do_usad(a >> 24, b >> 24); + return sum; +} + +/* For ARMv6 SEL instruction. */ +uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) +{ + uint32_t mask; + + mask = 0; + if (flags & 1) { + mask |= 0xff; + } + if (flags & 2) { + mask |= 0xff00; + } + if (flags & 4) { + mask |= 0xff0000; + } + if (flags & 8) { + mask |= 0xff000000; + } + return (a & mask) | (b & ~mask); +} + +/* + * CRC helpers. + * The upper bytes of val (above the number specified by 'bytes') must have + * been zeroed out by the caller. + */ +uint32_t HELPER(crc32)(uint32_t acc, uint32_t val, uint32_t bytes) +{ + uint8_t buf[4]; + + stl_le_p(buf, val); + + /* zlib crc32 converts the accumulator and output to one's complement. */ + return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; +} + +uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) +{ + uint8_t buf[4]; + + stl_le_p(buf, val); + + /* Linux crc32c converts the output to one's complement. */ + return crc32c(acc, buf, bytes) ^ 0xffffffff; +} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 09238989c5..1f9077c372 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -40,6 +40,7 @@ arm_ss.add(files( 'tlb_helper.c', 'vec_helper.c', 'tlb-insns.c', + 'arith_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( diff --git a/target/arm/op_addsub.h b/target/arm/tcg/op_addsub.c.inc similarity index 100% rename from target/arm/op_addsub.h rename to target/arm/tcg/op_addsub.c.inc From 86a00f2046f5f5b613bdf18d6c972b495a907c37 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 13 Jan 2025 12:35:34 +0000 Subject: [PATCH 0943/2892] target/arm: add new property to select pauth-qarma5 Before changing default pauth algorithm, we need to make sure current default one (QARMA5) can still be selected. $ qemu-system-aarch64 -cpu max,pauth-qarma5=on ... Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-id: 20241219183211.3493974-2-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/cpu-features.rst | 5 ++++- target/arm/arm-qmp-cmds.c | 2 +- target/arm/cpu.h | 1 + target/arm/cpu64.c | 20 ++++++++++++++------ tests/qtest/arm-cpu-features.c | 15 +++++++++++---- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index a5fb929243..d69ebc2b85 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -219,7 +219,10 @@ Below is the list of TCG VCPU features and their descriptions. ``pauth-qarma3`` When ``pauth`` is enabled, select the architected QARMA3 algorithm. -Without either ``pauth-impdef`` or ``pauth-qarma3`` enabled, +``pauth-qarma5`` + When ``pauth`` is enabled, select the architected QARMA5 algorithm. + +Without ``pauth-impdef``, ``pauth-qarma3`` or ``pauth-qarma5`` enabled, the architected QARMA5 algorithm is used. The architected QARMA5 and QARMA3 algorithms have good cryptographic properties, but can be quite slow to emulate. The impdef algorithm used by QEMU is diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index 3cc8cc738b..33cea080d1 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -94,7 +94,7 @@ static const char *cpu_model_advertised_features[] = { "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", "kvm-no-adjvtime", "kvm-steal-time", - "pauth", "pauth-impdef", "pauth-qarma3", + "pauth", "pauth-impdef", "pauth-qarma3", "pauth-qarma5", NULL }; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 12b8466542..01d9ff1781 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1062,6 +1062,7 @@ struct ArchCPU { bool prop_pauth; bool prop_pauth_impdef; bool prop_pauth_qarma3; + bool prop_pauth_qarma5; bool prop_lpa2; /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index dca83e4518..6ee0af6912 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -520,9 +520,12 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) } if (cpu->prop_pauth) { - if (cpu->prop_pauth_impdef && cpu->prop_pauth_qarma3) { + if ((cpu->prop_pauth_impdef && cpu->prop_pauth_qarma3) || + (cpu->prop_pauth_impdef && cpu->prop_pauth_qarma5) || + (cpu->prop_pauth_qarma3 && cpu->prop_pauth_qarma5)) { error_setg(errp, - "cannot enable both pauth-impdef and pauth-qarma3"); + "cannot enable pauth-impdef, pauth-qarma3 and " + "pauth-qarma5 at the same time"); return; } @@ -532,13 +535,15 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) } else if (cpu->prop_pauth_qarma3) { isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, APA3, features); isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, GPA3, 1); - } else { + } else { /* default is pauth-qarma5 */ isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, features); isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 1); } - } else if (cpu->prop_pauth_impdef || cpu->prop_pauth_qarma3) { - error_setg(errp, "cannot enable pauth-impdef or " - "pauth-qarma3 without pauth"); + } else if (cpu->prop_pauth_impdef || + cpu->prop_pauth_qarma3 || + cpu->prop_pauth_qarma5) { + error_setg(errp, "cannot enable pauth-impdef, pauth-qarma3 or " + "pauth-qarma5 without pauth"); error_append_hint(errp, "Add pauth=on to the CPU property list.\n"); } } @@ -553,6 +558,8 @@ static const Property arm_cpu_pauth_impdef_property = DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false); static const Property arm_cpu_pauth_qarma3_property = DEFINE_PROP_BOOL("pauth-qarma3", ARMCPU, prop_pauth_qarma3, false); +static Property arm_cpu_pauth_qarma5_property = + DEFINE_PROP_BOOL("pauth-qarma5", ARMCPU, prop_pauth_qarma5, false); void aarch64_add_pauth_properties(Object *obj) { @@ -573,6 +580,7 @@ void aarch64_add_pauth_properties(Object *obj) } else { qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property); qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_qarma3_property); + qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_qarma5_property); } } diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index cfd6f77353..98d6c970ea 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -419,21 +419,28 @@ static void pauth_tests_default(QTestState *qts, const char *cpu_type) assert_has_feature_enabled(qts, cpu_type, "pauth"); assert_has_feature_disabled(qts, cpu_type, "pauth-impdef"); assert_has_feature_disabled(qts, cpu_type, "pauth-qarma3"); + assert_has_feature_disabled(qts, cpu_type, "pauth-qarma5"); assert_set_feature(qts, cpu_type, "pauth", false); assert_set_feature(qts, cpu_type, "pauth", true); assert_set_feature(qts, cpu_type, "pauth-impdef", true); assert_set_feature(qts, cpu_type, "pauth-impdef", false); assert_set_feature(qts, cpu_type, "pauth-qarma3", true); assert_set_feature(qts, cpu_type, "pauth-qarma3", false); + assert_set_feature(qts, cpu_type, "pauth-qarma5", true); + assert_set_feature(qts, cpu_type, "pauth-qarma5", false); assert_error(qts, cpu_type, - "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", "{ 'pauth': false, 'pauth-impdef': true }"); assert_error(qts, cpu_type, - "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", "{ 'pauth': false, 'pauth-qarma3': true }"); assert_error(qts, cpu_type, - "cannot enable both pauth-impdef and pauth-qarma3", - "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true }"); + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", + "{ 'pauth': false, 'pauth-qarma5': true }"); + assert_error(qts, cpu_type, + "cannot enable pauth-impdef, pauth-qarma3 and pauth-qarma5 at the same time", + "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true," + " 'pauth-qarma5': true }"); } static void test_query_cpu_model_expansion(const void *data) From 39d70016d9abe15967f7051741dd5621c659b1f4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 13 Jan 2025 12:35:34 +0000 Subject: [PATCH 0944/2892] tests/tcg/aarch64: force qarma5 for pauth-3 test The pauth-3 test explicitly tests that a computation of the pointer-authentication produces the expected result. This means that it must be run with the QARMA5 algorithm. Explicitly set the pauth algorithm when running this test, so that it doesn't break when we change the default algorithm the 'max' CPU uses. Signed-off-by: Peter Maydell --- tests/tcg/aarch64/Makefile.softmmu-target | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index d08d9b01de..9c52475b7a 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -91,6 +91,9 @@ EXTRA_RUNS+=run-memory-replay ifneq ($(CROSS_CC_HAS_ARMV8_3),) pauth-3: CFLAGS += $(CROSS_CC_HAS_ARMV8_3) +# This test explicitly checks the output of the pauth operation so we +# must force the use of the QARMA5 algorithm for it. +run-pauth-3: QEMU_BASE_MACHINE=-M virt -cpu max,pauth-qarma5=on -display none else pauth-3: $(call skip-test, "BUILD of $@", "missing compiler support") From 132f8ec799cea261ad6b60ac8ae86f17cc98b9a1 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 13 Jan 2025 12:35:34 +0000 Subject: [PATCH 0945/2892] target/arm: change default pauth algorithm to impdef Pointer authentication on aarch64 is pretty expensive (up to 50% of execution time) when running a virtual machine with tcg and -cpu max (which enables pauth=on). The advice is always: use pauth-impdef=on. Our documentation even mentions it "by default" in docs/system/introduction.rst. Thus, we change the default to use impdef by default. This does not affect kvm or hvf acceleration, since pauth algorithm used is the one from host cpu. This change is retro compatible, in terms of cli, with previous versions, as the semantic of using -cpu max,pauth-impdef=on, and -cpu max,pauth-qarma3=on is preserved. The new option introduced in previous patch and matching old default is -cpu max,pauth-qarma5=on. It is retro compatible with migration as well, by defining a backcompat property, that will use qarma5 by default for virt machine <= 9.2. Tested by saving and restoring a vm from qemu 9.2.0 into qemu-master (10.0) for cpus neoverse-n2 and max. Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-id: 20241219183211.3493974-3-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/cpu-features.rst | 2 +- docs/system/introduction.rst | 2 +- hw/core/machine.c | 4 +++- target/arm/cpu.c | 2 ++ target/arm/cpu.h | 3 +++ target/arm/cpu64.c | 22 ++++++++++++++++------ 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index d69ebc2b85..37d5dfd15b 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -223,7 +223,7 @@ Below is the list of TCG VCPU features and their descriptions. When ``pauth`` is enabled, select the architected QARMA5 algorithm. Without ``pauth-impdef``, ``pauth-qarma3`` or ``pauth-qarma5`` enabled, -the architected QARMA5 algorithm is used. The architected QARMA5 +the QEMU impdef algorithm is used. The architected QARMA5 and QARMA3 algorithms have good cryptographic properties, but can be quite slow to emulate. The impdef algorithm used by QEMU is non-cryptographic but significantly faster. diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst index 746707eb00..338d3745c3 100644 --- a/docs/system/introduction.rst +++ b/docs/system/introduction.rst @@ -169,7 +169,7 @@ would default to it anyway. .. code:: - -cpu max,pauth-impdef=on \ + -cpu max \ -smp 4 \ -accel tcg \ diff --git a/hw/core/machine.c b/hw/core/machine.c index c949af9766..c23b399496 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -36,7 +36,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" -GlobalProperty hw_compat_9_2[] = {}; +GlobalProperty hw_compat_9_2[] = { + {"arm-cpu", "backcompat-pauth-default-use-qarma5", "true"}, +}; const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); GlobalProperty hw_compat_9_1[] = { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index dcedadc89e..dc0231233a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2653,6 +2653,8 @@ static const Property arm_cpu_properties[] = { DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */ DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false), + DEFINE_PROP_BOOL("backcompat-pauth-default-use-qarma5", ARMCPU, + backcompat_pauth_default_use_qarma5, false), }; static const gchar *arm_gdb_arch_name(CPUState *cs) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 01d9ff1781..9a6e8e589c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -972,6 +972,9 @@ struct ArchCPU { /* QOM property to indicate we should use the back-compat CNTFRQ default */ bool backcompat_cntfrq; + /* QOM property to indicate we should use the back-compat QARMA5 default */ + bool backcompat_pauth_default_use_qarma5; + /* Specify the number of cores in this CPU cluster. Used for the L2CTLR * register. */ diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 6ee0af6912..8188ede5cc 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -529,15 +529,25 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) return; } - if (cpu->prop_pauth_impdef) { - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, features); - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 1); + bool use_default = !cpu->prop_pauth_qarma5 && + !cpu->prop_pauth_qarma3 && + !cpu->prop_pauth_impdef; + + if (cpu->prop_pauth_qarma5 || + (use_default && + cpu->backcompat_pauth_default_use_qarma5)) { + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, features); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 1); } else if (cpu->prop_pauth_qarma3) { isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, APA3, features); isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, GPA3, 1); - } else { /* default is pauth-qarma5 */ - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, features); - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 1); + } else if (cpu->prop_pauth_impdef || + (use_default && + !cpu->backcompat_pauth_default_use_qarma5)) { + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, features); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 1); + } else { + g_assert_not_reached(); } } else if (cpu->prop_pauth_impdef || cpu->prop_pauth_qarma3 || From 435d260e7ec5ff9c79e3e62f1d66ec82d2d691ae Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 13 Jan 2025 12:35:35 +0000 Subject: [PATCH 0946/2892] docs/system/arm/virt: mention specific migration information Signed-off-by: Pierrick Bouvier Message-id: 20241219183211.3493974-4-pierrick.bouvier@linaro.org [PMM: Removed a paragraph about using non-versioned models.] Signed-off-by: Peter Maydell --- docs/system/arm/virt.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index f87adeb444..766a7455f0 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -19,6 +19,10 @@ of the 5.0 release and ``virt-5.0`` of the 5.1 release. Migration is not guaranteed to work between different QEMU releases for the non-versioned ``virt`` machine type. +VM migration is not guaranteed when using ``-cpu max``, as features +supported may change between QEMU versions. To ensure your VM can be +migrated, it is recommended to use another cpu model instead. + Supported devices """"""""""""""""" From f09a8d8dc9687c9ad1a5bae26c8e15516f6798a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 08:24:21 +0200 Subject: [PATCH 0947/2892] pc-bios/meson.build: Silent unuseful DTC warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU consumes some device tree blobs, so these have been committed to the tree in as firmware, along with the device tree source used to generate them. We know the blobs are "good enough" to have QEMU boot a system, so we don't really maintain and rebuild the sources. These blobs were generated with older 'dtc' binaries. We use the v1.6.1 version since 2021 (commit 962fde57b7 "dtc: Update to version 1.6.1"). Since commit 6e0dc9d2a8 ("meson: compile bundled device trees"), if dtc binary is available, it is directly used to compile the device tree sources. New versions of 'dtc' add checks which display warnings or errors. Our sources are a bit old, so dtc v1.6.1 now emit the following warnings on a fresh build: [163/3414] Generating pc-bios/canyonlands.dts with a custom command pc-bios/canyonlands.dts:47.9-50.4: Warning (unit_address_vs_reg): /memory: node has a reg or ranges property, but no unit name pc-bios/canyonlands.dts:210.13-429.5: Warning (unit_address_vs_reg): /plb/opb: node has a reg or ranges property, but no unit name pc-bios/canyonlands.dts:464.26-504.5: Warning (pci_bridge): /plb/pciex@d00000000: node name is not "pci" or "pcie" pc-bios/canyonlands.dts:506.26-546.5: Warning (pci_bridge): /plb/pciex@d20000000: node name is not "pci" or "pcie" pc-bios/canyonlands.dtb: Warning (unit_address_format): Failed prerequisite 'pci_bridge' pc-bios/canyonlands.dtb: Warning (pci_device_reg): Failed prerequisite 'pci_bridge' pc-bios/canyonlands.dtb: Warning (pci_device_bus_num): Failed prerequisite 'pci_bridge' pc-bios/canyonlands.dts:268.14-289.7: Warning (avoid_unnecessary_addr_size): /plb/opb/ebc/ndfc@3,0: unnecessary #address-cells/#size-cells without "ranges" or child "reg" property [164/3414] Generating pc-bios/petalogix-s3adsp1800.dts with a custom command pc-bios/petalogix-s3adsp1800.dts:258.33-266.5: Warning (interrupt_provider): /plb/interrupt-controller@81800000: Missing #address-cells in interrupt provider [165/3414] Generating pc-bios/petalogix-ml605.dts with a custom command pc-bios/petalogix-ml605.dts:234.39-241.5: Warning (interrupt_provider): /axi/interrupt-controller@81800000: Missing #address-cells in interrupt provider [177/3414] Generating pc-bios/bamboo.dts with a custom command pc-bios/bamboo.dts:45.9-48.4: Warning (unit_address_vs_reg): /memory: node has a reg or ranges property, but no unit name pc-bios/bamboo.dts:87.13-154.5: Warning (unit_address_vs_reg): /plb/opb: node has a reg or ranges property, but no unit name pc-bios/bamboo.dts:198.3-50: Warning (chosen_node_stdout_path): /chosen:linux,stdout-path: Use 'stdout-path' instead pc-bios/bamboo.dts:87.13-154.5: Warning (interrupts_property): /plb/opb: Missing interrupt-parent pc-bios/bamboo.dts:100.14-108.6: Warning (interrupts_property): /plb/opb/ebc: Missing interrupt-parent From QEMU perspective, these warnings are not really useful. It is the responsibility of developers adding DT source/blob to QEMU repository to check the source doesn't produce warnings, but as long as the blob is useful enough, QEMU can consume it. So these warnings don't add any value, instead they are noisy and might distract us to focus on important warnings. Better disable them. 'dtc' provides the '--quiet' option for that [*]: $ dtc --help Usage: dtc [options] Options: -[qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@AThv] -q, --quiet Quiet: -q suppress warnings, -qq errors, -qqq all Update meson to disable these unuseful DTC warnings. [*] https://lore.kernel.org/qemu-devel/CAFEAcA-WJ9J1YQunJ+bSG=wnpxh1By+Bf18j2CyV7G0vZ=8b7g@mail.gmail.com/ Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Acked-by: BALATON Zoltan Message-Id: <20231006064750.33852-1-philmd@linaro.org> --- pc-bios/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 4823dff189..b68b29cc7d 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -99,7 +99,8 @@ foreach f : [ output: out, install: get_option('install_blobs'), install_dir: qemu_datadir, - command: [ dtc, '-I', 'dts', '-O', 'dtb', '-o', '@OUTPUT@', '@INPUT0@' ]) + command: [ dtc, '-q', '-I', 'dts', '-O', 'dtb', + '-o', '@OUTPUT@', '@INPUT0@' ]) else blobs += out endif From b708e31185e060dac0fbc1420656f1dc812451eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 16 Dec 2023 03:07:33 +0100 Subject: [PATCH 0948/2892] target: Replace DEVICE(object_new) -> qdev_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer QDev API for QDev objects, avoid the underlying QOM layer. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20240216110313.17039-8-philmd@linaro.org> --- target/mips/cpu.c | 2 +- target/xtensa/cpu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index e3af02a4e6..47cd7cfdce 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -644,7 +644,7 @@ MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk, { DeviceState *cpu; - cpu = DEVICE(object_new(cpu_type)); + cpu = qdev_new(cpu_type); qdev_connect_clock_in(cpu, "clk-in", cpu_refclk); object_property_set_bool(OBJECT(cpu), "big-endian", is_big_endian, &error_abort); diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 0910a3d290..4eb699d1f4 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -208,7 +208,7 @@ XtensaCPU *xtensa_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk) { DeviceState *cpu; - cpu = DEVICE(object_new(cpu_type)); + cpu = qdev_new(cpu_type); qdev_connect_clock_in(cpu, "clk-in", cpu_refclk); qdev_realize(cpu, NULL, &error_abort); From 901b78a0eeda7d8d955e9ffe6608801df14b5638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 16 Dec 2023 03:07:33 +0100 Subject: [PATCH 0949/2892] hw: Replace DEVICE(object_new) -> qdev_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer QDev API for QDev objects, avoid the underlying QOM layer. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20240216110313.17039-7-philmd@linaro.org> --- hw/arm/musicpal.c | 2 +- hw/sparc/sun4m.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index a712ff954b..48a32c2407 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1238,7 +1238,7 @@ static void musicpal_init(MachineState *machine) qdev_get_gpio_in(pic, MP_TIMER4_IRQ), NULL); /* Logically OR both UART IRQs together */ - uart_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + uart_orgate = qdev_new(TYPE_OR_IRQ); object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(uart_orgate, NULL, &error_fatal); qdev_connect_gpio_out(uart_orgate, 0, diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 217a69e4d5..e070360a2c 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -974,7 +974,7 @@ static void sun4m_hw_init(MachineState *machine) sysbus_mmio_map(s, 0, hwdef->ms_kb_base); /* Logically OR both its IRQs together */ - ms_kb_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + ms_kb_orgate = qdev_new(TYPE_OR_IRQ); object_property_set_int(OBJECT(ms_kb_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(ms_kb_orgate, NULL, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(ms_kb_orgate, 0)); @@ -995,7 +995,7 @@ static void sun4m_hw_init(MachineState *machine) sysbus_mmio_map(s, 0, hwdef->serial_base); /* Logically OR both its IRQs together */ - serial_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + serial_orgate = qdev_new(TYPE_OR_IRQ); object_property_set_int(OBJECT(serial_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(serial_orgate, NULL, &error_fatal); From 8bf6275f7e08ed8fea309ecda29c5da8837ed952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 16 Dec 2023 18:23:26 +0100 Subject: [PATCH 0950/2892] hw/usb: Inline usb_try_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline the single use of usb_try_new(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20240216110313.17039-10-philmd@linaro.org> --- hw/usb/bus.c | 2 +- include/hw/usb.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/usb/bus.c b/hw/usb/bus.c index b19b0b13eb..7e7deaadca 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -411,7 +411,7 @@ void usb_claim_port(USBDevice *dev, Error **errp) } else { if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { /* Create a new hub and chain it on */ - hub = usb_try_new("usb-hub"); + hub = USB_DEVICE(qdev_try_new("usb-hub")); if (hub) { usb_realize_and_unref(hub, bus, NULL); } diff --git a/include/hw/usb.h b/include/hw/usb.h index d46d96779a..bb778cb844 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -584,11 +584,6 @@ static inline USBDevice *usb_new(const char *name) return USB_DEVICE(qdev_new(name)); } -static inline USBDevice *usb_try_new(const char *name) -{ - return USB_DEVICE(qdev_try_new(name)); -} - static inline bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp) { return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp); From 8d2701072e44cda756148a29ef013a4b91316644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 16 Dec 2023 18:24:11 +0100 Subject: [PATCH 0951/2892] hw/usb: Inline usb_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline the 3 uses of usb_new(). Reviewed-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240216110313.17039-11-philmd@linaro.org> --- hw/usb/bus.c | 3 ++- hw/usb/dev-serial.c | 2 +- include/hw/usb.h | 7 +------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 7e7deaadca..f45b82c776 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -662,7 +662,8 @@ USBDevice *usbdevice_create(const char *driver) return NULL; } - dev = f->usbdevice_init ? f->usbdevice_init() : usb_new(f->name); + dev = f->usbdevice_init ? f->usbdevice_init() + : USB_DEVICE(qdev_new(f->name)); if (!dev) { error_report("Failed to create USB device '%s'", f->name); return NULL; diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index a0821db902..aa50a92e26 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -624,7 +624,7 @@ static USBDevice *usb_braille_init(void) return NULL; } - dev = usb_new("usb-braille"); + dev = USB_DEVICE(qdev_new("usb-braille")); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); return dev; } diff --git a/include/hw/usb.h b/include/hw/usb.h index bb778cb844..e410693d0c 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -579,11 +579,6 @@ void usb_pcap_init(FILE *fp); void usb_pcap_ctrl(USBPacket *p, bool setup); void usb_pcap_data(USBPacket *p, bool setup); -static inline USBDevice *usb_new(const char *name) -{ - return USB_DEVICE(qdev_new(name)); -} - static inline bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp) { return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp); @@ -591,7 +586,7 @@ static inline bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **er static inline USBDevice *usb_create_simple(USBBus *bus, const char *name) { - USBDevice *dev = usb_new(name); + USBDevice *dev = USB_DEVICE(qdev_new(name)); usb_realize_and_unref(dev, bus, &error_abort); return dev; From 8915c118599d47c8d73f0b5982d28289c3ad797f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 18 Feb 2024 15:57:11 +0900 Subject: [PATCH 0952/2892] hw/qdev: Pass bus argument to qdev_hotplug_allowed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of checking the parent bus is hot(un)pluggable in a few commits, pass a 'bus' argument to qdev_hotplug_allowed(). Signed-off-by: Akihiko Odaki [PMD: Split from bigger patch, part 1/6] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20250110091908.64454-2-philmd@linaro.org> --- hw/core/qdev-hotplug.c | 2 +- include/hw/qdev-core.h | 2 +- system/qdev-monitor.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index d495d0e9c7..19fbb11a31 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -30,7 +30,7 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) return NULL; } -bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) +bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp) { MachineState *machine; MachineClass *mc; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 89575e74e2..930b00fb09 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -540,7 +540,7 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); -bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); +bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp); /** * qdev_get_hotplug_handler() - Get handler responsible for device wiring diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 23043b1e0d..83388dc0c4 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -691,7 +691,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, /* Check whether the hotplug is allowed by the machine */ if (phase_check(PHASE_MACHINE_READY)) { - if (!qdev_hotplug_allowed(dev, errp)) { + if (!qdev_hotplug_allowed(dev, bus, errp)) { goto err_del_dev; } From 206d602e9b73d9079449b44899d572624d57390a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 18 Feb 2024 15:57:11 +0900 Subject: [PATCH 0953/2892] hw/qdev: Factor qdev_hotunplug_allowed() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor qdev_hotunplug_allowed() out of qdev_unplug(). Start checking the device is not blocked. Signed-off-by: Akihiko Odaki [PMD: Split from bigger patch, part 2/6] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20250110091908.64454-3-philmd@linaro.org> --- hw/core/qdev-hotplug.c | 5 +++++ include/hw/qdev-core.h | 1 + system/qdev-monitor.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index 19fbb11a31..dc35110e73 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -47,6 +47,11 @@ bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp) return true; } +bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp) +{ + return !qdev_unplug_blocked(dev, errp); +} + HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) { if (dev->parent_bus) { diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 930b00fb09..530f3da702 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -541,6 +541,7 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp); +bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp); /** * qdev_get_hotplug_handler() - Get handler responsible for device wiring diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 83388dc0c4..511d1aa83c 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -909,7 +909,7 @@ void qdev_unplug(DeviceState *dev, Error **errp) HotplugHandlerClass *hdc; Error *local_err = NULL; - if (qdev_unplug_blocked(dev, errp)) { + if (!qdev_hotunplug_allowed(dev, errp)) { return; } From f2694f1b1a1a38c586ee6f00e88b729828012d3a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 18 Feb 2024 15:57:11 +0900 Subject: [PATCH 0954/2892] hw/qdev: Introduce qdev_hotplug_unplug_allowed_common() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce qdev_hotplug_unplug_allowed_common() to hold common code between checking hot-plug/unplug is allowed. Signed-off-by: Akihiko Odaki [PMD: Split from bigger patch, part 3/6] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20250110091908.64454-4-philmd@linaro.org> --- hw/core/qdev-hotplug.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index dc35110e73..168d796474 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -30,12 +30,22 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) return NULL; } +static bool qdev_hotplug_unplug_allowed_common(DeviceState *dev, BusState *bus, + Error **errp) +{ + return true; +} + bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp) { MachineState *machine; MachineClass *mc; Object *m_obj = qdev_get_machine(); + if (!qdev_hotplug_unplug_allowed_common(dev, bus, errp)) { + return false; + } + if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { machine = MACHINE(m_obj); mc = MACHINE_GET_CLASS(machine); @@ -49,7 +59,8 @@ bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp) bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp) { - return !qdev_unplug_blocked(dev, errp); + return !qdev_unplug_blocked(dev, errp) && + qdev_hotplug_unplug_allowed_common(dev, dev->parent_bus, errp); } HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) From 1bff035be76cc30ff0fc4e8f0b0fbda84db7d1dc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 18 Feb 2024 15:57:11 +0900 Subject: [PATCH 0955/2892] hw/qdev: Check DevClass::hotpluggable in hotplug_unplug_allowed_common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the same code once in the common helper. Signed-off-by: Akihiko Odaki [PMD: Split from bigger patch, part 4/6] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20250110091908.64454-5-philmd@linaro.org> --- hw/core/qdev-hotplug.c | 9 +++++++++ system/qdev-monitor.c | 10 +--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index 168d796474..1d77fffb5e 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" #include "hw/boards.h" +#include "qapi/error.h" HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) { @@ -33,6 +34,14 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) static bool qdev_hotplug_unplug_allowed_common(DeviceState *dev, BusState *bus, Error **errp) { + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->hotpluggable) { + error_setg(errp, "Device '%s' does not support hotplugging", + object_get_typename(OBJECT(dev))); + return false; + } + return true; } diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 511d1aa83c..81f747b38f 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -263,8 +263,7 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) } dc = DEVICE_CLASS(oc); - if (!dc->user_creatable || - (phase_check(PHASE_MACHINE_READY) && !dc->hotpluggable)) { + if (!dc->user_creatable) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", "a pluggable device type"); return NULL; @@ -904,7 +903,6 @@ static DeviceState *find_device_state(const char *id, bool use_generic_error, void qdev_unplug(DeviceState *dev, Error **errp) { - DeviceClass *dc = DEVICE_GET_CLASS(dev); HotplugHandler *hotplug_ctrl; HotplugHandlerClass *hdc; Error *local_err = NULL; @@ -919,12 +917,6 @@ void qdev_unplug(DeviceState *dev, Error **errp) return; } - if (!dc->hotpluggable) { - error_setg(errp, "Device '%s' does not support hotplugging", - object_get_typename(OBJECT(dev))); - return; - } - if (migration_is_running() && !dev->allow_unplug_during_migration) { error_setg(errp, "device_del not allowed while migrating"); return; From ccaca8929d53b23e2f7acc45cc88d05fc0479a1b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 18 Feb 2024 15:57:11 +0900 Subject: [PATCH 0956/2892] hw/qdev: Check qbus_is_hotpluggable in hotplug_unplug_allowed_common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the same code once in the common helper. Signed-off-by: Akihiko Odaki [PMD: Split from bigger patch, part 5/6] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20250110091908.64454-6-philmd@linaro.org> --- hw/core/qdev-hotplug.c | 8 ++++++++ system/qdev-monitor.c | 11 ----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index 1d77fffb5e..f6422cd0e4 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -42,6 +42,14 @@ static bool qdev_hotplug_unplug_allowed_common(DeviceState *dev, BusState *bus, return false; } + if (bus) { + if (!qbus_is_hotpluggable(bus)) { + error_setg(errp, "Bus '%s' does not support hotplugging", + bus->name); + return false; + } + } + return true; } diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 81f747b38f..e27d25c585 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -675,11 +675,6 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, return NULL; } - if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, "Bus '%s' does not support hotplugging", bus->name); - return NULL; - } - if (migration_is_running()) { error_setg(errp, "device_add not allowed while migrating"); return NULL; @@ -911,12 +906,6 @@ void qdev_unplug(DeviceState *dev, Error **errp) return; } - if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, "Bus '%s' does not support hotplugging", - dev->parent_bus->name); - return; - } - if (migration_is_running() && !dev->allow_unplug_during_migration) { error_setg(errp, "device_del not allowed while migrating"); return; From 937874a83d149a1b871a569d6abded3fdd302267 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 18 Feb 2024 15:57:11 +0900 Subject: [PATCH 0957/2892] hw/qdev: Check machine_hotplug_handler in hotplug_unplug_allowed_common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 03fcbd9dc508 ("qdev: Check for the availability of a hotplug controller before adding a device") says: > The qdev_unplug() function contains a g_assert(hotplug_ctrl) > statement, so QEMU crashes when the user tries to device_add + > device_del a device that does not have a corresponding hotplug > controller. > The code in qdev_device_add() already checks whether the bus has a > proper hotplug controller, but for devices that do not have a > corresponding bus, here is no appropriate check available yet. In that > case we should check whether the machine itself provides a suitable > hotplug controller and refuse to plug the device if none is available. However, it forgot to add the corresponding check to qdev_unplug(). Check the machine hotplug handler once in the common qdev_hotplug_unplug_allowed_common() helper so both hotplug and hot-unplug path are covered. Fixes: 7716b8ca74 ("qdev: HotplugHandler: Add support for unplugging BUS-less devices") Signed-off-by: Akihiko Odaki [PMD: Split from bigger patch, part 6/6] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20250110091908.64454-7-philmd@linaro.org> --- hw/core/qdev-hotplug.c | 10 ++++++++++ system/qdev-monitor.c | 14 +++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index f6422cd0e4..ff176dc1bb 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -48,6 +48,16 @@ static bool qdev_hotplug_unplug_allowed_common(DeviceState *dev, BusState *bus, bus->name); return false; } + } else { + if (!qdev_get_machine_hotplug_handler(dev)) { + /* + * No bus, no machine hotplug handler --> device is not hotpluggable + */ + error_setg(errp, + "Device '%s' can not be hotplugged on this machine", + object_get_typename(OBJECT(dev))); + return false; + } } return true; diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index e27d25c585..861c25c855 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -684,17 +684,9 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, dev = qdev_new(driver); /* Check whether the hotplug is allowed by the machine */ - if (phase_check(PHASE_MACHINE_READY)) { - if (!qdev_hotplug_allowed(dev, bus, errp)) { - goto err_del_dev; - } - - if (!bus && !qdev_get_machine_hotplug_handler(dev)) { - /* No bus, no machine hotplug handler --> device is not hotpluggable */ - error_setg(errp, "Device '%s' can not be hotplugged on this machine", - driver); - goto err_del_dev; - } + if (phase_check(PHASE_MACHINE_READY) && + !qdev_hotplug_allowed(dev, bus, errp)) { + goto err_del_dev; } /* From 6909b616efbc9b627a0c9ea87d25a10d508cc879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Sep 2024 23:14:08 +0200 Subject: [PATCH 0958/2892] hw/microblaze: Restrict MemoryRegionOps are implemented as 32-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All these MemoryRegionOps read() and write() handlers are implemented expecting 32-bit accesses. Clarify that setting .impl.min/max_access_size fields. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20241105130431.22564-8-philmd@linaro.org> --- hw/intc/xilinx_intc.c | 4 ++++ hw/net/xilinx_ethlite.c | 4 ++++ hw/timer/xilinx_timer.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index d99cf567ae..6930f83907 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -144,6 +144,10 @@ static const MemoryRegionOps pic_ops = { .read = pic_read, .write = pic_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .valid = { .min_access_size = 4, .max_access_size = 4 diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 4c0c7fcae3..88ab331acc 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -166,6 +166,10 @@ static const MemoryRegionOps eth_ops = { .read = eth_read, .write = eth_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .valid = { .min_access_size = 4, .max_access_size = 4 diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 4955fe1b01..6595cf5f51 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -193,6 +193,10 @@ static const MemoryRegionOps timer_ops = { .read = timer_read, .write = timer_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .valid = { .min_access_size = 4, .max_access_size = 4 From fa3ca9aa1ceb9b762f4c7a51245226f247e35560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:35:36 +0100 Subject: [PATCH 0959/2892] hw/net/xilinx_ethlite: Map MDIO registers (as unimplemented) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than handling the MDIO registers as RAM, map them as unimplemented I/O within the device MR. The memory flat view becomes: (qemu) info mtree -f FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, i/o): xlnx.xps-ethernetlite 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-0000000081001fff (prio 0, i/o): xlnx.xps-ethernetlite @00000000000007f4 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-7-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 88ab331acc..442467abeb 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -28,15 +28,18 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qom/object.h" +#include "qapi/error.h" #include "exec/tswap.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/misc/unimp.h" #include "net/net.h" #include "trace.h" #define R_TX_BUF0 0 #define BUFSZ_MAX 0x07e4 +#define A_MDIO_BASE 0x07e4 #define R_TX_LEN0 (0x07f4 / 4) #define R_TX_GIE0 (0x07f8 / 4) #define R_TX_CTRL0 (0x07fc / 4) @@ -72,6 +75,7 @@ struct XlnxXpsEthLite uint32_t c_rx_pingpong; unsigned int port_index; /* dual port RAM index */ + UnimplementedDeviceState mdio; uint32_t regs[R_MAX]; }; @@ -232,6 +236,14 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { XlnxXpsEthLite *s = XILINX_ETHLITE(dev); + object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio, + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio"); + qdev_prop_set_uint64(DEVICE(&s->mdio), "size", 4 * 4); + sysbus_realize(SYS_BUS_DEVICE(&s->mdio), &error_fatal); + memory_region_add_subregion(&s->mmio, A_MDIO_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); + qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, From 8d956610f5e79bcc53a9f296ebecd53ee7c449ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Nov 2024 18:49:56 +0100 Subject: [PATCH 0960/2892] hw/net/xilinx_ethlite: Introduce txbuf_ptr() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a particular physical address within the EthLite MMIO range, addr_to_port_index() returns which port is accessed. txbuf_ptr() points to the beginning of a (RAM) TX buffer within the device state. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-10-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 442467abeb..8df621904a 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "qemu/bitops.h" #include "qom/object.h" #include "qapi/error.h" #include "exec/tswap.h" @@ -87,6 +88,18 @@ static inline void eth_pulse_irq(XlnxXpsEthLite *s) } } +static unsigned addr_to_port_index(hwaddr addr) +{ + return extract64(addr, 11, 1); +} + +static void *txbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) +{ + unsigned int rxbase = port_index * (0x800 / 4); + + return &s->regs[rxbase + R_TX_BUF0]; +} + static uint64_t eth_read(void *opaque, hwaddr addr, unsigned int size) { @@ -119,6 +132,7 @@ eth_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { XlnxXpsEthLite *s = opaque; + unsigned int port_index = addr_to_port_index(addr); unsigned int base = 0; uint32_t value = val64; @@ -132,12 +146,12 @@ eth_write(void *opaque, hwaddr addr, if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { qemu_send_packet(qemu_get_queue(s->nic), - (void *) &s->regs[base], + txbuf_ptr(s, port_index), s->regs[base + R_TX_LEN0]); if (s->regs[base + R_TX_CTRL0] & CTRL_I) eth_pulse_irq(s); } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { - memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6); + memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6); if (s->regs[base + R_TX_CTRL0] & CTRL_I) eth_pulse_irq(s); } From 785fd1a9afd5cb894f3e53753d732c7fbbb3d74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Nov 2024 18:51:57 +0100 Subject: [PATCH 0961/2892] hw/net/xilinx_ethlite: Introduce rxbuf_ptr() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rxbuf_ptr() points to the beginning of a (RAM) RX buffer within the device state. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-11-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 8df621904a..67adecc088 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -60,6 +60,12 @@ #define CTRL_P 0x2 #define CTRL_S 0x1 +typedef struct XlnxXpsEthLitePort { + struct { + uint32_t rx_ctrl; + } reg; +} XlnxXpsEthLitePort; + #define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite" OBJECT_DECLARE_SIMPLE_TYPE(XlnxXpsEthLite, XILINX_ETHLITE) @@ -77,6 +83,7 @@ struct XlnxXpsEthLite unsigned int port_index; /* dual port RAM index */ UnimplementedDeviceState mdio; + XlnxXpsEthLitePort port[2]; uint32_t regs[R_MAX]; }; @@ -100,10 +107,18 @@ static void *txbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) return &s->regs[rxbase + R_TX_BUF0]; } +static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) +{ + unsigned int rxbase = port_index * (0x800 / 4); + + return &s->regs[rxbase + R_RX_BUF0]; +} + static uint64_t eth_read(void *opaque, hwaddr addr, unsigned int size) { XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); uint32_t r = 0; addr >>= 2; @@ -115,9 +130,12 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_TX_LEN1: case R_TX_CTRL1: case R_TX_CTRL0: + r = s->regs[addr]; + break; + case R_RX_CTRL1: case R_RX_CTRL0: - r = s->regs[addr]; + r = s->port[port_index].reg.rx_ctrl; break; default: @@ -167,7 +185,9 @@ eth_write(void *opaque, hwaddr addr, if (!(value & CTRL_S)) { qemu_flush_queued_packets(qemu_get_queue(s->nic)); } - /* fall through */ + s->port[port_index].reg.rx_ctrl = value; + break; + case R_TX_LEN0: case R_TX_LEN1: case R_TX_GIE0: @@ -197,22 +217,21 @@ static const MemoryRegionOps eth_ops = { static bool eth_can_rx(NetClientState *nc) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->port_index * (0x800 / 4); - return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); + return !(s->port[s->port_index].reg.rx_ctrl & CTRL_S); } static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->port_index * (0x800 / 4); + unsigned int port_index = s->port_index; /* DA filter. */ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6)) return size; - if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { - trace_ethlite_pkt_lost(s->regs[R_RX_CTRL0]); + if (s->port[port_index].reg.rx_ctrl & CTRL_S) { + trace_ethlite_pkt_lost(s->port[port_index].reg.rx_ctrl); return -1; } @@ -220,10 +239,10 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) trace_ethlite_pkt_size_too_big(size); return -1; } - memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); + memcpy(rxbuf_ptr(s, port_index), buf, size); - s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; - if (s->regs[R_RX_CTRL0] & CTRL_I) { + s->port[port_index].reg.rx_ctrl |= CTRL_S; + if (s->port[port_index].reg.rx_ctrl & CTRL_I) { eth_pulse_irq(s); } From 64fdbae7e1bc6408204a3cf3b8e6a2e7d8e36fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:45:58 +0100 Subject: [PATCH 0962/2892] hw/net/xilinx_ethlite: Access TX_GIE register for each port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than accessing the registers within the mixed RAM/MMIO region as indexed register, declare a per-port TX_GIE. This will help to map the RAM as RAM (keeping MMIO as MMIO) in few commits. Previous s->regs[R_TX_GIE0] and s->regs[R_TX_GIE1] are now unused. Not a concern, this array will soon disappear. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-13-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 67adecc088..3252c9d508 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -62,6 +62,8 @@ typedef struct XlnxXpsEthLitePort { struct { + uint32_t tx_gie; + uint32_t rx_ctrl; } reg; } XlnxXpsEthLitePort; @@ -90,7 +92,7 @@ struct XlnxXpsEthLite static inline void eth_pulse_irq(XlnxXpsEthLite *s) { /* Only the first gie reg is active. */ - if (s->regs[R_TX_GIE0] & GIE_GIE) { + if (s->port[0].reg.tx_gie & GIE_GIE) { qemu_irq_pulse(s->irq); } } @@ -126,6 +128,9 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) switch (addr) { case R_TX_GIE0: + r = s->port[port_index].reg.tx_gie; + break; + case R_TX_LEN0: case R_TX_LEN1: case R_TX_CTRL1: @@ -190,10 +195,13 @@ eth_write(void *opaque, hwaddr addr, case R_TX_LEN0: case R_TX_LEN1: - case R_TX_GIE0: s->regs[addr] = value; break; + case R_TX_GIE0: + s->port[port_index].reg.tx_gie = value; + break; + default: s->regs[addr] = tswap32(value); break; From c629791859d5d1777d8471f260f418e76078e97e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:48:36 +0100 Subject: [PATCH 0963/2892] hw/net/xilinx_ethlite: Access TX_LEN register for each port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than accessing the registers within the mixed RAM/MMIO region as indexed register, declare a per-port TX_LEN. This will help to map the RAM as RAM (keeping MMIO as MMIO) in few commits. Previous s->regs[R_TX_LEN0] and s->regs[R_TX_LEN1] are now unused. Not a concern, this array will soon disappear. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-14-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 3252c9d508..ce9555bd1e 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -62,6 +62,7 @@ typedef struct XlnxXpsEthLitePort { struct { + uint32_t tx_len; uint32_t tx_gie; uint32_t rx_ctrl; @@ -133,6 +134,9 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_TX_LEN0: case R_TX_LEN1: + r = s->port[port_index].reg.tx_len; + break; + case R_TX_CTRL1: case R_TX_CTRL0: r = s->regs[addr]; @@ -170,7 +174,7 @@ eth_write(void *opaque, hwaddr addr, if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { qemu_send_packet(qemu_get_queue(s->nic), txbuf_ptr(s, port_index), - s->regs[base + R_TX_LEN0]); + s->port[port_index].reg.tx_len); if (s->regs[base + R_TX_CTRL0] & CTRL_I) eth_pulse_irq(s); } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { @@ -195,7 +199,7 @@ eth_write(void *opaque, hwaddr addr, case R_TX_LEN0: case R_TX_LEN1: - s->regs[addr] = value; + s->port[port_index].reg.tx_len = value; break; case R_TX_GIE0: From a37506699109d2dbf86ec5c02734eee35d065d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 19:51:55 +0100 Subject: [PATCH 0964/2892] hw/net/xilinx_ethlite: Access TX_CTRL register for each port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than accessing the registers within the mixed RAM/MMIO region as indexed register, declare a per-port TX_CTRL. This will help to map the RAM as RAM (keeping MMIO as MMIO) in few commits. Previous s->regs[R_TX_CTRL0] and s->regs[R_TX_CTRL1] are now unused. Not a concern, this array will soon disappear. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-15-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index ce9555bd1e..f8b01fe9a6 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -64,6 +64,7 @@ typedef struct XlnxXpsEthLitePort { struct { uint32_t tx_len; uint32_t tx_gie; + uint32_t tx_ctrl; uint32_t rx_ctrl; } reg; @@ -139,7 +140,7 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_TX_CTRL1: case R_TX_CTRL0: - r = s->regs[addr]; + r = s->port[port_index].reg.tx_ctrl; break; case R_RX_CTRL1: @@ -160,7 +161,6 @@ eth_write(void *opaque, hwaddr addr, { XlnxXpsEthLite *s = opaque; unsigned int port_index = addr_to_port_index(addr); - unsigned int base = 0; uint32_t value = val64; addr >>= 2; @@ -168,24 +168,23 @@ eth_write(void *opaque, hwaddr addr, { case R_TX_CTRL0: case R_TX_CTRL1: - if (addr == R_TX_CTRL1) - base = 0x800 / 4; - if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { qemu_send_packet(qemu_get_queue(s->nic), txbuf_ptr(s, port_index), s->port[port_index].reg.tx_len); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { eth_pulse_irq(s); + } } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { eth_pulse_irq(s); + } } /* We are fast and get ready pretty much immediately so we actually never flip the S nor P bits to one. */ - s->regs[addr] = value & ~(CTRL_P | CTRL_S); + s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S); break; /* Keep these native. */ From 72294962065e5d237dda3cd298016b47fef0c3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 9 Nov 2024 20:03:00 +0100 Subject: [PATCH 0965/2892] hw/net/xilinx_ethlite: Map RX_CTRL as MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare RX registers as MMIO region, split it out of the current mixed RAM/MMIO region. The memory flat view becomes: (qemu) info mtree -f FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, i/o): xlnx.xps-ethernetlite 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-00000000810017fb (prio 0, i/o): xlnx.xps-ethernetlite @00000000000007f4 00000000810017fc-00000000810017ff (prio 0, i/o): ethlite.rx[0]io 0000000081001800-0000000081001ffb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000001800 0000000081001ffc-0000000081001fff (prio 0, i/o): ethlite.rx[1]io Reviewed-by: Edgar E. Iglesias Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241112181044.92193-16-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 82 +++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index f8b01fe9a6..9ac81ca1e0 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -49,11 +49,16 @@ #define R_TX_CTRL1 (0x0ffc / 4) #define R_RX_BUF0 (0x1000 / 4) -#define R_RX_CTRL0 (0x17fc / 4) +#define A_RX_BASE0 0x17fc #define R_RX_BUF1 (0x1800 / 4) -#define R_RX_CTRL1 (0x1ffc / 4) +#define A_RX_BASE1 0x1ffc #define R_MAX (0x2000 / 4) +enum { + RX_CTRL = 0, + RX_MAX +}; + #define GIE_GIE 0x80000000 #define CTRL_I 0x8 @@ -61,6 +66,8 @@ #define CTRL_S 0x1 typedef struct XlnxXpsEthLitePort { + MemoryRegion rxio; + struct { uint32_t tx_len; uint32_t tx_gie; @@ -118,6 +125,55 @@ static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) return &s->regs[rxbase + R_RX_BUF0]; } +static uint64_t port_rx_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + uint32_t r = 0; + + switch (addr >> 2) { + case RX_CTRL: + r = s->port[port_index].reg.rx_ctrl; + break; + default: + g_assert_not_reached(); + } + + return r; +} + +static void port_rx_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + + switch (addr >> 2) { + case RX_CTRL: + if (!(value & CTRL_S)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + s->port[port_index].reg.rx_ctrl = value; + break; + default: + g_assert_not_reached(); + } +} + +static const MemoryRegionOps eth_portrx_ops = { + .read = port_rx_read, + .write = port_rx_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + static uint64_t eth_read(void *opaque, hwaddr addr, unsigned int size) { @@ -143,11 +199,6 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) r = s->port[port_index].reg.tx_ctrl; break; - case R_RX_CTRL1: - case R_RX_CTRL0: - r = s->port[port_index].reg.rx_ctrl; - break; - default: r = tswap32(s->regs[addr]); break; @@ -188,14 +239,6 @@ eth_write(void *opaque, hwaddr addr, break; /* Keep these native. */ - case R_RX_CTRL0: - case R_RX_CTRL1: - if (!(value & CTRL_S)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - s->port[port_index].reg.rx_ctrl = value; - break; - case R_TX_LEN0: case R_TX_LEN1: s->port[port_index].reg.tx_len = value; @@ -288,6 +331,15 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->mmio, A_MDIO_BASE, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); + for (unsigned i = 0; i < 2; i++) { + memory_region_init_io(&s->port[i].rxio, OBJECT(dev), + ð_portrx_ops, s, + i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", + 4 * RX_MAX); + memory_region_add_subregion(&s->mmio, i ? A_RX_BASE1 : A_RX_BASE0, + &s->port[i].rxio); + } + qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, From 46dd6af2592d7a7d876ed8617b1e70d43c676ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 10 Nov 2024 16:51:24 +0100 Subject: [PATCH 0966/2892] hw/net/xilinx_ethlite: Map TX_LEN as MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare TX registers as MMIO region, split it out of the current mixed RAM/MMIO region. The memory flat view becomes: (qemu) info mtree -f FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, i/o): xlnx.xps-ethernetlite 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-00000000810007f7 (prio 0, i/o): ethlite.tx[0]io 00000000810007f8-0000000081000ff3 (prio 0, i/o): xlnx.xps-ethernetlite @00000000000007f8 0000000081000ff4-0000000081000ff7 (prio 0, i/o): ethlite.tx[1]io 0000000081000ff8-00000000810017fb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000000ff8 00000000810017fc-00000000810017ff (prio 0, i/o): ethlite.rx[0]io 0000000081001800-0000000081001ffb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000001800 0000000081001ffc-0000000081001fff (prio 0, i/o): ethlite.rx[1]io Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-17-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 73 ++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 9ac81ca1e0..5dac44fa68 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -41,11 +41,11 @@ #define R_TX_BUF0 0 #define BUFSZ_MAX 0x07e4 #define A_MDIO_BASE 0x07e4 -#define R_TX_LEN0 (0x07f4 / 4) +#define A_TX_BASE0 0x07f4 #define R_TX_GIE0 (0x07f8 / 4) #define R_TX_CTRL0 (0x07fc / 4) #define R_TX_BUF1 (0x0800 / 4) -#define R_TX_LEN1 (0x0ff4 / 4) +#define A_TX_BASE1 0x0ff4 #define R_TX_CTRL1 (0x0ffc / 4) #define R_RX_BUF0 (0x1000 / 4) @@ -54,6 +54,11 @@ #define A_RX_BASE1 0x1ffc #define R_MAX (0x2000 / 4) +enum { + TX_LEN = 0, + TX_MAX +}; + enum { RX_CTRL = 0, RX_MAX @@ -66,6 +71,7 @@ enum { #define CTRL_S 0x1 typedef struct XlnxXpsEthLitePort { + MemoryRegion txio; MemoryRegion rxio; struct { @@ -125,6 +131,52 @@ static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) return &s->regs[rxbase + R_RX_BUF0]; } +static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + uint32_t r = 0; + + switch (addr >> 2) { + case TX_LEN: + r = s->port[port_index].reg.tx_len; + break; + default: + g_assert_not_reached(); + } + + return r; +} + +static void port_tx_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + + switch (addr >> 2) { + case TX_LEN: + s->port[port_index].reg.tx_len = value; + break; + default: + g_assert_not_reached(); + } +} + +static const MemoryRegionOps eth_porttx_ops = { + .read = port_tx_read, + .write = port_tx_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + static uint64_t port_rx_read(void *opaque, hwaddr addr, unsigned int size) { XlnxXpsEthLite *s = opaque; @@ -189,11 +241,6 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) r = s->port[port_index].reg.tx_gie; break; - case R_TX_LEN0: - case R_TX_LEN1: - r = s->port[port_index].reg.tx_len; - break; - case R_TX_CTRL1: case R_TX_CTRL0: r = s->port[port_index].reg.tx_ctrl; @@ -239,11 +286,6 @@ eth_write(void *opaque, hwaddr addr, break; /* Keep these native. */ - case R_TX_LEN0: - case R_TX_LEN1: - s->port[port_index].reg.tx_len = value; - break; - case R_TX_GIE0: s->port[port_index].reg.tx_gie = value; break; @@ -332,6 +374,13 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); for (unsigned i = 0; i < 2; i++) { + memory_region_init_io(&s->port[i].txio, OBJECT(dev), + ð_porttx_ops, s, + i ? "ethlite.tx[1]io" : "ethlite.tx[0]io", + 4 * TX_MAX); + memory_region_add_subregion(&s->mmio, i ? A_TX_BASE1 : A_TX_BASE0, + &s->port[i].txio); + memory_region_init_io(&s->port[i].rxio, OBJECT(dev), ð_portrx_ops, s, i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", From 01198add29ddecc1283f1ab5cb4b34885e7daaa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Nov 2024 17:15:56 +0100 Subject: [PATCH 0967/2892] hw/net/xilinx_ethlite: Map TX_GIE as MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TX_GIE to the TX registers MMIO region. Before TX_GIE1 was accessed as RAM, with no effect. Now it is accessed as MMIO, also without any effect. The memory flat view becomes: (qemu) info mtree -f FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, i/o): xlnx.xps-ethernetlite 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-00000000810007fb (prio 0, i/o): ethlite.tx[0]io 00000000810007fc-0000000081000ff3 (prio 0, i/o): xlnx.xps-ethernetlite @00000000000007fc 0000000081000ff4-0000000081000ffb (prio 0, i/o): ethlite.tx[1]io 0000000081000ffc-00000000810017fb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000000ffc 00000000810017fc-00000000810017ff (prio 0, i/o): ethlite.rx[0]io 0000000081001800-0000000081001ffb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000001800 0000000081001ffc-0000000081001fff (prio 0, i/o): ethlite.rx[1]io Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-18-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 5dac44fa68..898c09b398 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -42,7 +42,6 @@ #define BUFSZ_MAX 0x07e4 #define A_MDIO_BASE 0x07e4 #define A_TX_BASE0 0x07f4 -#define R_TX_GIE0 (0x07f8 / 4) #define R_TX_CTRL0 (0x07fc / 4) #define R_TX_BUF1 (0x0800 / 4) #define A_TX_BASE1 0x0ff4 @@ -56,6 +55,7 @@ enum { TX_LEN = 0, + TX_GIE = 1, TX_MAX }; @@ -141,6 +141,9 @@ static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size) case TX_LEN: r = s->port[port_index].reg.tx_len; break; + case TX_GIE: + r = s->port[port_index].reg.tx_gie; + break; default: g_assert_not_reached(); } @@ -158,6 +161,9 @@ static void port_tx_write(void *opaque, hwaddr addr, uint64_t value, case TX_LEN: s->port[port_index].reg.tx_len = value; break; + case TX_GIE: + s->port[port_index].reg.tx_gie = value; + break; default: g_assert_not_reached(); } @@ -237,10 +243,6 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) switch (addr) { - case R_TX_GIE0: - r = s->port[port_index].reg.tx_gie; - break; - case R_TX_CTRL1: case R_TX_CTRL0: r = s->port[port_index].reg.tx_ctrl; @@ -285,11 +287,6 @@ eth_write(void *opaque, hwaddr addr, s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S); break; - /* Keep these native. */ - case R_TX_GIE0: - s->port[port_index].reg.tx_gie = value; - break; - default: s->regs[addr] = tswap32(value); break; From a34606dbb3c7094b6e9fde5e1463a306b6921255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Nov 2024 17:19:39 +0100 Subject: [PATCH 0968/2892] hw/net/xilinx_ethlite: Map TX_CTRL as MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TX_CTRL to the TX registers MMIO region. The memory flat view becomes: (qemu) info mtree -f FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, i/o): xlnx.xps-ethernetlite 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-00000000810007ff (prio 0, i/o): ethlite.tx[0]io 0000000081000800-0000000081000ff3 (prio 0, i/o): xlnx.xps-ethernetlite @0000000000000800 0000000081000ff4-0000000081000fff (prio 0, i/o): ethlite.tx[1]io 0000000081001000-00000000810017fb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000001000 00000000810017fc-00000000810017ff (prio 0, i/o): ethlite.rx[0]io 0000000081001800-0000000081001ffb (prio 0, i/o): xlnx.xps-ethernetlite @0000000000001800 0000000081001ffc-0000000081001fff (prio 0, i/o): ethlite.rx[1]io Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20241112181044.92193-19-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 54 ++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 898c09b398..5ab8ae43b2 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -42,10 +42,8 @@ #define BUFSZ_MAX 0x07e4 #define A_MDIO_BASE 0x07e4 #define A_TX_BASE0 0x07f4 -#define R_TX_CTRL0 (0x07fc / 4) #define R_TX_BUF1 (0x0800 / 4) #define A_TX_BASE1 0x0ff4 -#define R_TX_CTRL1 (0x0ffc / 4) #define R_RX_BUF0 (0x1000 / 4) #define A_RX_BASE0 0x17fc @@ -56,6 +54,7 @@ enum { TX_LEN = 0, TX_GIE = 1, + TX_CTRL = 2, TX_MAX }; @@ -144,6 +143,9 @@ static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size) case TX_GIE: r = s->port[port_index].reg.tx_gie; break; + case TX_CTRL: + r = s->port[port_index].reg.tx_ctrl; + break; default: g_assert_not_reached(); } @@ -164,6 +166,26 @@ static void port_tx_write(void *opaque, hwaddr addr, uint64_t value, case TX_GIE: s->port[port_index].reg.tx_gie = value; break; + case TX_CTRL: + if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { + qemu_send_packet(qemu_get_queue(s->nic), + txbuf_ptr(s, port_index), + s->port[port_index].reg.tx_len); + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { + eth_pulse_irq(s); + } + } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { + memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6); + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { + eth_pulse_irq(s); + } + } + /* + * We are fast and get ready pretty much immediately + * so we actually never flip the S nor P bits to one. + */ + s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S); + break; default: g_assert_not_reached(); } @@ -236,18 +258,12 @@ static uint64_t eth_read(void *opaque, hwaddr addr, unsigned int size) { XlnxXpsEthLite *s = opaque; - unsigned port_index = addr_to_port_index(addr); uint32_t r = 0; addr >>= 2; switch (addr) { - case R_TX_CTRL1: - case R_TX_CTRL0: - r = s->port[port_index].reg.tx_ctrl; - break; - default: r = tswap32(s->regs[addr]); break; @@ -260,33 +276,11 @@ eth_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { XlnxXpsEthLite *s = opaque; - unsigned int port_index = addr_to_port_index(addr); uint32_t value = val64; addr >>= 2; switch (addr) { - case R_TX_CTRL0: - case R_TX_CTRL1: - if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(qemu_get_queue(s->nic), - txbuf_ptr(s, port_index), - s->port[port_index].reg.tx_len); - if (s->port[port_index].reg.tx_ctrl & CTRL_I) { - eth_pulse_irq(s); - } - } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { - memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6); - if (s->port[port_index].reg.tx_ctrl & CTRL_I) { - eth_pulse_irq(s); - } - } - - /* We are fast and get ready pretty much immediately so - we actually never flip the S nor P bits to one. */ - s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S); - break; - default: s->regs[addr] = tswap32(value); break; From 0bd0ba87a0fa22b5d3dda206f1920547df6eb918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Nov 2024 17:44:53 +0100 Subject: [PATCH 0969/2892] hw/net/xilinx_ethlite: Map the RAM buffer as RAM memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using I/O registers for RAM buffer, having to swap endianness back and forth (because the core memory layer automatically swaps endiannes for us), declare the buffers as RAM regions. The "xlnx.xps-ethernetlite" MR doesn't have any more I/O regions. Remove the now unused s->regs[] array. The memory flat view becomes: FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, ram): ethlite.tx[0]buf 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-00000000810007ff (prio 0, i/o): ethlite.tx[0]io 0000000081000800-0000000081000fe3 (prio 0, ram): ethlite.tx[1]buf 0000000081000ff4-0000000081000fff (prio 0, i/o): ethlite.tx[1]io 0000000081001000-00000000810017e3 (prio 0, ram): ethlite.rx[0]buf 00000000810017fc-00000000810017ff (prio 0, i/o): ethlite.rx[0]io 0000000081001800-0000000081001fe3 (prio 0, ram): ethlite.rx[1]buf 0000000081001ffc-0000000081001fff (prio 0, i/o): ethlite.rx[1]io Reported-by: Paolo Bonzini Reviewed-by: Edgar E. Iglesias Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241114210010.34502-18-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 81 +++++++++-------------------------------- 1 file changed, 17 insertions(+), 64 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 5ab8ae43b2..758226a65d 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -2,6 +2,7 @@ * QEMU model of the Xilinx Ethernet Lite MAC. * * Copyright (c) 2009 Edgar E. Iglesias. + * Copyright (c) 2024 Linaro, Ltd * * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite * LogiCORE IP XPS Ethernet Lite Media Access Controller @@ -30,7 +31,6 @@ #include "qemu/bitops.h" #include "qom/object.h" #include "qapi/error.h" -#include "exec/tswap.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -38,18 +38,12 @@ #include "net/net.h" #include "trace.h" -#define R_TX_BUF0 0 #define BUFSZ_MAX 0x07e4 #define A_MDIO_BASE 0x07e4 #define A_TX_BASE0 0x07f4 -#define R_TX_BUF1 (0x0800 / 4) #define A_TX_BASE1 0x0ff4 - -#define R_RX_BUF0 (0x1000 / 4) #define A_RX_BASE0 0x17fc -#define R_RX_BUF1 (0x1800 / 4) #define A_RX_BASE1 0x1ffc -#define R_MAX (0x2000 / 4) enum { TX_LEN = 0, @@ -72,6 +66,8 @@ enum { typedef struct XlnxXpsEthLitePort { MemoryRegion txio; MemoryRegion rxio; + MemoryRegion txbuf; + MemoryRegion rxbuf; struct { uint32_t tx_len; @@ -100,7 +96,6 @@ struct XlnxXpsEthLite UnimplementedDeviceState mdio; XlnxXpsEthLitePort port[2]; - uint32_t regs[R_MAX]; }; static inline void eth_pulse_irq(XlnxXpsEthLite *s) @@ -118,16 +113,12 @@ static unsigned addr_to_port_index(hwaddr addr) static void *txbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) { - unsigned int rxbase = port_index * (0x800 / 4); - - return &s->regs[rxbase + R_TX_BUF0]; + return memory_region_get_ram_ptr(&s->port[port_index].txbuf); } static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) { - unsigned int rxbase = port_index * (0x800 / 4); - - return &s->regs[rxbase + R_RX_BUF0]; + return memory_region_get_ram_ptr(&s->port[port_index].rxbuf); } static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size) @@ -254,53 +245,6 @@ static const MemoryRegionOps eth_portrx_ops = { }, }; -static uint64_t -eth_read(void *opaque, hwaddr addr, unsigned int size) -{ - XlnxXpsEthLite *s = opaque; - uint32_t r = 0; - - addr >>= 2; - - switch (addr) - { - default: - r = tswap32(s->regs[addr]); - break; - } - return r; -} - -static void -eth_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - XlnxXpsEthLite *s = opaque; - uint32_t value = val64; - - addr >>= 2; - switch (addr) - { - default: - s->regs[addr] = tswap32(value); - break; - } -} - -static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - static bool eth_can_rx(NetClientState *nc) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); @@ -356,6 +300,9 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { XlnxXpsEthLite *s = XILINX_ETHLITE(dev); + memory_region_init(&s->mmio, OBJECT(dev), + "xlnx.xps-ethernetlite", 0x2000); + object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio, TYPE_UNIMPLEMENTED_DEVICE); qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio"); @@ -365,6 +312,10 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); for (unsigned i = 0; i < 2; i++) { + memory_region_init_ram(&s->port[i].txbuf, OBJECT(dev), + i ? "ethlite.tx[1]buf" : "ethlite.tx[0]buf", + BUFSZ_MAX, &error_abort); + memory_region_add_subregion(&s->mmio, 0x0800 * i, &s->port[i].txbuf); memory_region_init_io(&s->port[i].txio, OBJECT(dev), ð_porttx_ops, s, i ? "ethlite.tx[1]io" : "ethlite.tx[0]io", @@ -372,6 +323,11 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->mmio, i ? A_TX_BASE1 : A_TX_BASE0, &s->port[i].txio); + memory_region_init_ram(&s->port[i].rxbuf, OBJECT(dev), + i ? "ethlite.rx[1]buf" : "ethlite.rx[0]buf", + BUFSZ_MAX, &error_abort); + memory_region_add_subregion(&s->mmio, 0x1000 + 0x0800 * i, + &s->port[i].rxbuf); memory_region_init_io(&s->port[i].rxio, OBJECT(dev), ð_portrx_ops, s, i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", @@ -392,9 +348,6 @@ static void xilinx_ethlite_init(Object *obj) XlnxXpsEthLite *s = XILINX_ETHLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, ð_ops, s, - "xlnx.xps-ethernetlite", R_MAX * 4); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } From ce336679b72cfb5cbbf1539d209764242fc5458a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Nov 2024 17:44:53 +0100 Subject: [PATCH 0970/2892] hw/net/xilinx_ethlite: Rename 'mmio' MR as 'container' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having all its address range mapped by subregions, s->mmio MemoryRegion effectively became a container. Rename it as 'container' for clarity. Reviewed-by: Edgar E. Iglesias Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241112181044.92193-21-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 758226a65d..a7f6d1b368 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -85,7 +85,7 @@ struct XlnxXpsEthLite { SysBusDevice parent_obj; - MemoryRegion mmio; + MemoryRegion container; qemu_irq irq; NICState *nic; NICConf conf; @@ -300,7 +300,7 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { XlnxXpsEthLite *s = XILINX_ETHLITE(dev); - memory_region_init(&s->mmio, OBJECT(dev), + memory_region_init(&s->container, OBJECT(dev), "xlnx.xps-ethernetlite", 0x2000); object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio, @@ -308,31 +308,31 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio"); qdev_prop_set_uint64(DEVICE(&s->mdio), "size", 4 * 4); sysbus_realize(SYS_BUS_DEVICE(&s->mdio), &error_fatal); - memory_region_add_subregion(&s->mmio, A_MDIO_BASE, + memory_region_add_subregion(&s->container, A_MDIO_BASE, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); for (unsigned i = 0; i < 2; i++) { memory_region_init_ram(&s->port[i].txbuf, OBJECT(dev), i ? "ethlite.tx[1]buf" : "ethlite.tx[0]buf", BUFSZ_MAX, &error_abort); - memory_region_add_subregion(&s->mmio, 0x0800 * i, &s->port[i].txbuf); + memory_region_add_subregion(&s->container, 0x0800 * i, &s->port[i].txbuf); memory_region_init_io(&s->port[i].txio, OBJECT(dev), ð_porttx_ops, s, i ? "ethlite.tx[1]io" : "ethlite.tx[0]io", 4 * TX_MAX); - memory_region_add_subregion(&s->mmio, i ? A_TX_BASE1 : A_TX_BASE0, + memory_region_add_subregion(&s->container, i ? A_TX_BASE1 : A_TX_BASE0, &s->port[i].txio); memory_region_init_ram(&s->port[i].rxbuf, OBJECT(dev), i ? "ethlite.rx[1]buf" : "ethlite.rx[0]buf", BUFSZ_MAX, &error_abort); - memory_region_add_subregion(&s->mmio, 0x1000 + 0x0800 * i, + memory_region_add_subregion(&s->container, 0x1000 + 0x0800 * i, &s->port[i].rxbuf); memory_region_init_io(&s->port[i].rxio, OBJECT(dev), ð_portrx_ops, s, i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", 4 * RX_MAX); - memory_region_add_subregion(&s->mmio, i ? A_RX_BASE1 : A_RX_BASE0, + memory_region_add_subregion(&s->container, i ? A_RX_BASE1 : A_RX_BASE0, &s->port[i].rxio); } @@ -348,7 +348,7 @@ static void xilinx_ethlite_init(Object *obj) XlnxXpsEthLite *s = XILINX_ETHLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); } static const Property xilinx_ethlite_properties[] = { From 74f1caa8c33f608168c4224bb23d9a6453224a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 18:46:36 +0100 Subject: [PATCH 0971/2892] hw/net/xilinx_ethlite: Map RESERVED I/O as unimplemented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to track access to reserved I/O space, use yet another UnimplementedDevice covering the whole device memory range. Mapped with lower priority (-1). The memory flat view becomes: (qemu) info mtree -f FlatView #0 Root memory region: system 0000000081000000-00000000810007e3 (prio 0, ram): ethlite.tx[0]buf 00000000810007e4-00000000810007f3 (prio 0, i/o): ethlite.mdio 00000000810007f4-00000000810007ff (prio 0, i/o): ethlite.tx[0]io 0000000081000800-0000000081000fe3 (prio 0, ram): ethlite.tx[1]buf 0000000081000fe4-0000000081000ff3 (prio -1, i/o): ethlite.reserved @0000000000000fe4 0000000081000ff4-0000000081000fff (prio 0, i/o): ethlite.tx[1]io 0000000081001000-00000000810017e3 (prio 0, ram): ethlite.rx[0]buf 00000000810017e4-00000000810017fb (prio -1, i/o): ethlite.reserved @00000000000017e4 00000000810017fc-00000000810017ff (prio 0, i/o): ethlite.rx[0]io 0000000081001800-0000000081001fe3 (prio 0, ram): ethlite.rx[1]buf 0000000081001fe4-0000000081001ffb (prio -1, i/o): ethlite.reserved @0000000000001fe4 0000000081001ffc-0000000081001fff (prio 0, i/o): ethlite.rx[1]io Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20241114210010.34502-20-philmd@linaro.org> --- hw/net/xilinx_ethlite.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index a7f6d1b368..14bf2b2e17 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -94,6 +94,7 @@ struct XlnxXpsEthLite uint32_t c_rx_pingpong; unsigned int port_index; /* dual port RAM index */ + UnimplementedDeviceState rsvd; UnimplementedDeviceState mdio; XlnxXpsEthLitePort port[2]; }; @@ -303,6 +304,16 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) memory_region_init(&s->container, OBJECT(dev), "xlnx.xps-ethernetlite", 0x2000); + object_initialize_child(OBJECT(dev), "ethlite.reserved", &s->rsvd, + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&s->rsvd), "name", "ethlite.reserved"); + qdev_prop_set_uint64(DEVICE(&s->rsvd), "size", + memory_region_size(&s->container)); + sysbus_realize(SYS_BUS_DEVICE(&s->rsvd), &error_fatal); + memory_region_add_subregion_overlap(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rsvd), 0), + -1); + object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio, TYPE_UNIMPLEMENTED_DEVICE); qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio"); From d024d0adf48e28d4f93161878053936d55dab9c9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 11 Dec 2024 22:25:12 +0000 Subject: [PATCH 0972/2892] docs/nitro-enclave: Clarify Enclave and Firecracker relationship MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation says that Nitro Enclaves are based on Firecracker. AWS has never made that statement. This patch nudges the wording to instead say it "looks like a Firecracker microvm". Signed-off-by: Alexander Graf Reviewed-by: Dorjoy Chowdhury Message-ID: <20241211222512.95660-1-graf@amazon.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/system/i386/nitro-enclave.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/i386/nitro-enclave.rst b/docs/system/i386/nitro-enclave.rst index 48eda5bd9e..7317f547dc 100644 --- a/docs/system/i386/nitro-enclave.rst +++ b/docs/system/i386/nitro-enclave.rst @@ -13,7 +13,7 @@ the enclave VM gets a dynamic CID. Enclaves use an EIF (`Enclave Image Format`_) file which contains the necessary kernel, cmdline and ramdisk(s) to boot. In QEMU, ``nitro-enclave`` is a machine type based on ``microvm`` similar to how -AWS nitro enclaves are based on `Firecracker`_ microvm. This is useful for +AWS nitro enclaves look like a `Firecracker`_ microvm. This is useful for local testing of EIF files using QEMU instead of running real AWS Nitro Enclaves which can be difficult for debugging due to its roots in security. The vsock device emulation is done using vhost-user-vsock which means another process that From b2d4e9f3b8437359e063431d991d4d4eb1ef7d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 15:01:52 +0100 Subject: [PATCH 0973/2892] hw/misc/vmcoreinfo: Rename VMCOREINFO_DEVICE -> TYPE_VMCOREINFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the assumed QOM type definition style, prefixing with 'TYPE_', and dropping the '_DEVICE' suffix which doesn't add any value. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20250102132624.53443-1-philmd@linaro.org> --- hw/misc/vmcoreinfo.c | 6 +++--- include/hw/misc/vmcoreinfo.h | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index b1fcc22e92..145f13a65c 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -47,13 +47,13 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) */ if (!vmcoreinfo_find()) { error_setg(errp, "at most one %s device is permitted", - VMCOREINFO_DEVICE); + TYPE_VMCOREINFO); return; } if (!fw_cfg || !fw_cfg->dma_enabled) { error_setg(errp, "%s device requires fw_cfg with DMA", - VMCOREINFO_DEVICE); + TYPE_VMCOREINFO); return; } @@ -95,7 +95,7 @@ static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data) static const TypeInfo vmcoreinfo_types[] = { { - .name = VMCOREINFO_DEVICE, + .name = TYPE_VMCOREINFO, .parent = TYPE_DEVICE, .instance_size = sizeof(VMCoreInfoState), .class_init = vmcoreinfo_device_class_init, diff --git a/include/hw/misc/vmcoreinfo.h b/include/hw/misc/vmcoreinfo.h index 0b7b55d400..1aa4477163 100644 --- a/include/hw/misc/vmcoreinfo.h +++ b/include/hw/misc/vmcoreinfo.h @@ -16,10 +16,9 @@ #include "standard-headers/linux/qemu_fw_cfg.h" #include "qom/object.h" -#define VMCOREINFO_DEVICE "vmcoreinfo" +#define TYPE_VMCOREINFO "vmcoreinfo" typedef struct VMCoreInfoState VMCoreInfoState; -DECLARE_INSTANCE_CHECKER(VMCoreInfoState, VMCOREINFO, - VMCOREINFO_DEVICE) +DECLARE_INSTANCE_CHECKER(VMCoreInfoState, VMCOREINFO, TYPE_VMCOREINFO) typedef struct fw_cfg_vmcoreinfo FWCfgVMCoreInfo; @@ -33,7 +32,7 @@ struct VMCoreInfoState { /* returns NULL unless there is exactly one device */ static inline VMCoreInfoState *vmcoreinfo_find(void) { - Object *o = object_resolve_path_type("", VMCOREINFO_DEVICE, NULL); + Object *o = object_resolve_path_type("", TYPE_VMCOREINFO, NULL); return o ? VMCOREINFO(o) : NULL; } From b7700c9fb1c052e3f2c503c535bccdb6dd70dd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Dec 2024 16:19:35 +0100 Subject: [PATCH 0974/2892] hw/misc/vmcoreinfo: Convert to three-phase reset interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20241219153857.57450-6-philmd@linaro.org> --- hw/misc/vmcoreinfo.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index 145f13a65c..b0145fa504 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -26,9 +26,9 @@ static void fw_cfg_vmci_write(void *opaque, off_t offset, size_t len) && s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE; } -static void vmcoreinfo_reset(void *opaque) +static void vmcoreinfo_reset_hold(Object *obj, ResetType type) { - VMCoreInfoState *s = opaque; + VMCoreInfoState *s = VMCOREINFO(obj); s->has_vmcoreinfo = false; memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo)); @@ -65,7 +65,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) * This device requires to register a global reset because it is * not plugged to a bus (which, as its QOM parent, would reset it). */ - qemu_register_reset(vmcoreinfo_reset, s); + qemu_register_resettable(OBJECT(s)); vmcoreinfo_state = s; } @@ -86,11 +86,13 @@ static const VMStateDescription vmstate_vmcoreinfo = { static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_vmcoreinfo; dc->realize = vmcoreinfo_realize; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + rc->phases.hold = vmcoreinfo_reset_hold; } static const TypeInfo vmcoreinfo_types[] = { From c407eef162f765dd83d45e048585731be41a66fc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 9 Jan 2025 15:29:46 +0900 Subject: [PATCH 0975/2892] hw/pci: Rename has_power to enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The renamed state will not only represent powering state of PFs, but also represent SR-IOV VF enablement in the future. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250109-reuse-v19-1-f541e82ca5f7@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci/pci.c | 17 +++++++++++------ hw/pci/pci_host.c | 4 ++-- include/hw/pci/pci.h | 1 + include/hw/pci/pci_device.h | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 78907527f2..2afa423925 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1598,7 +1598,7 @@ static void pci_update_mappings(PCIDevice *d) continue; new_addr = pci_bar_address(d, i, r->type, r->size); - if (!d->has_power) { + if (!d->enabled) { new_addr = PCI_BAR_UNMAPPED; } @@ -1686,7 +1686,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int pci_update_irq_disabled(d, was_irq_disabled); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); + & PCI_COMMAND_MASTER) && d->enabled); } msi_write_config(d, addr, val_in, l); @@ -2963,16 +2963,21 @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) void pci_set_power(PCIDevice *d, bool state) { - if (d->has_power == state) { + pci_set_enabled(d, state); +} + +void pci_set_enabled(PCIDevice *d, bool state) +{ + if (d->enabled == state) { return; } - d->has_power = state; + d->enabled = state; pci_update_mappings(d); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); - if (!d->has_power) { + & PCI_COMMAND_MASTER) && d->enabled); + if (!d->enabled) { pci_device_reset(d); } } diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 4510890dfc..80f91f409f 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -86,7 +86,7 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return; } @@ -111,7 +111,7 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return ~0x0; } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index cefeb388bd..4002bbeebd 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -674,6 +674,7 @@ static inline void pci_irq_deassert(PCIDevice *pci_dev) } MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); +void pci_set_enabled(PCIDevice *pci_dev, bool state); void pci_set_power(PCIDevice *pci_dev, bool state); #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index 16ea7f4c19..add208edfa 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -57,7 +57,7 @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIDevice { DeviceState qdev; bool partially_hotplugged; - bool has_power; + bool enabled; /* PCI config space */ uint8_t *config; From 4572dacc33e232a7c951ba7ba7a20887fad29e71 Mon Sep 17 00:00:00 2001 From: Keoseong Park Date: Tue, 7 Jan 2025 17:43:56 +0900 Subject: [PATCH 0976/2892] hw/ufs: Adjust value to match CPU's endian format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ufs_write_attr_value(), the value parameter is handled in the CPU's endian format but provided in big-endian format by the caller. Thus, it is converted to the CPU's endian format. The related test code is also fixed to reflect this change. Fixes: 7c85332a2b3e ("hw/ufs: minor bug fixes related to ufs-test") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Keoseong Park Reviewed-by: Jeuk Kim Message-ID: <20250107084356epcms2p2af4d86432174d76ea57336933e46b4c3@epcms2p2> Signed-off-by: Philippe Mathieu-Daudé --- hw/ufs/ufs.c | 2 +- tests/qtest/ufs-test.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 8d26d13791..428fe927ad 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1164,7 +1164,7 @@ static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op) value = ufs_read_attr_value(u, idn); ret = UFS_QUERY_RESULT_SUCCESS; } else { - value = req->req_upiu.qr.value; + value = be32_to_cpu(req->req_upiu.qr.value); ret = ufs_write_attr_value(u, idn, value); } req->rsp_upiu.qr.value = cpu_to_be32(value); diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 60199abbee..1f860b41c0 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -145,7 +145,7 @@ static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, req_upiu.qr.idn = idn; req_upiu.qr.index = index; req_upiu.qr.selector = selector; - req_upiu.qr.value = attr_value; + req_upiu.qr.value = cpu_to_be32(attr_value); req_upiu.qr.length = UFS_QUERY_DESC_MAX_SIZE; qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, sizeof(req_upiu)); From 5df50b8e97377d2468bd8759ec3275e747a147bb Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 Jan 2025 10:25:25 +0100 Subject: [PATCH 0977/2892] hw/sd/sdhci: Set SDHC_NIS_DMA bit when appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In U-Boot, the fsl_esdhc[_imx] driver waits for both "transmit completed" and "DMA" bits in esdhc_send_cmd_common() by means of DATA_COMPLETE constant. QEMU currently misses to set the DMA bit which causes the driver to loop forever. Fix that by setting the DMA bit if enabled when doing DMA block transfers. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250108092538.11474-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sdhci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 299cd4bc1b..a958c11497 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -665,12 +665,13 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) } } + if (s->norintstsen & SDHC_NISEN_DMA) { + s->norintsts |= SDHC_NIS_DMA; + } + if (s->blkcnt == 0) { sdhci_end_transfer(s); } else { - if (s->norintstsen & SDHC_NISEN_DMA) { - s->norintsts |= SDHC_NIS_DMA; - } sdhci_update_irq(s); } } @@ -691,6 +692,10 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) } s->blkcnt--; + if (s->norintstsen & SDHC_NISEN_DMA) { + s->norintsts |= SDHC_NIS_DMA; + } + sdhci_end_transfer(s); } From 14b1086f92ae8d45fc1e66c837ddc7da79d93f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Jan 2025 13:12:37 +0100 Subject: [PATCH 0978/2892] hw/sd/sdhci: Factor sdhci_sdma_transfer() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor sdhci_sdma_transfer() out of sdhci_data_transfer(). Re-use it in sdhci_write(), so we don't try to run multi block transfer for a single block. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bernhard Beschow Message-Id: <20250109122029.22780-1-philmd@linaro.org> --- hw/sd/sdhci.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index a958c11497..318587ff57 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -699,6 +699,15 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) sdhci_end_transfer(s); } +static void sdhci_sdma_transfer(SDHCIState *s) +{ + if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { + sdhci_sdma_transfer_single_block(s); + } else { + sdhci_sdma_transfer_multi_blocks(s); + } +} + typedef struct ADMADescr { hwaddr addr; uint16_t length; @@ -930,12 +939,7 @@ static void sdhci_data_transfer(void *opaque) if (s->trnmod & SDHC_TRNS_DMA) { switch (SDHC_DMA_TYPE(s->hostctl1)) { case SDHC_CTRL_SDMA: - if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { - sdhci_sdma_transfer_single_block(s); - } else { - sdhci_sdma_transfer_multi_blocks(s); - } - + sdhci_sdma_transfer(s); break; case SDHC_CTRL_ADMA1_32: if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) { @@ -1179,11 +1183,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) if (!(mask & 0xFF000000) && s->blkcnt && (s->blksize & BLOCK_SIZE_MASK) && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { - if (s->trnmod & SDHC_TRNS_MULTI) { - sdhci_sdma_transfer_multi_blocks(s); - } else { - sdhci_sdma_transfer_single_block(s); - } + sdhci_sdma_transfer(s); } } break; From d25202fe836805d7e28dea5d11e39e19a7801780 Mon Sep 17 00:00:00 2001 From: Nikita Shubin Date: Fri, 20 Dec 2024 14:17:56 +0300 Subject: [PATCH 0979/2892] hw/char/stm32f2xx_usart: replace print with trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop debug printing macros and replace them with according trace functions. Signed-off-by: Nikita Shubin Reviewed-by: Alistair Francis Message-ID: <20241220111756.16511-1-nikita.shubin@maquefel.me> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/stm32f2xx_usart.c | 49 ++++++++++++++++++--------------------- hw/char/trace-events | 6 +++++ 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index ebcc510f4e..87882daa71 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -30,17 +30,7 @@ #include "qemu/log.h" #include "qemu/module.h" -#ifndef STM_USART_ERR_DEBUG -#define STM_USART_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do { \ - if (STM_USART_ERR_DEBUG >= lvl) { \ - qemu_log("%s: " fmt, __func__, ## args); \ - } \ -} while (0) - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) +#include "trace.h" static int stm32f2xx_usart_can_receive(void *opaque) { @@ -67,10 +57,11 @@ static void stm32f2xx_update_irq(STM32F2XXUsartState *s) static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) { STM32F2XXUsartState *s = opaque; + DeviceState *d = DEVICE(s); if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) { /* USART not enabled - drop the chars */ - DB_PRINT("Dropping the chars\n"); + trace_stm32f2xx_usart_drop(d->id); return; } @@ -79,7 +70,7 @@ static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) stm32f2xx_update_irq(s); - DB_PRINT("Receiving: %c\n", s->usart_dr); + trace_stm32f2xx_usart_receive(d->id, *buf); } static void stm32f2xx_usart_reset(DeviceState *dev) @@ -101,49 +92,55 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, unsigned int size) { STM32F2XXUsartState *s = opaque; - uint64_t retvalue; - - DB_PRINT("Read 0x%"HWADDR_PRIx"\n", addr); + DeviceState *d = DEVICE(s); + uint64_t retvalue = 0; switch (addr) { case USART_SR: retvalue = s->usart_sr; qemu_chr_fe_accept_input(&s->chr); - return retvalue; + break; case USART_DR: - DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); retvalue = s->usart_dr & 0x3FF; s->usart_sr &= ~USART_SR_RXNE; qemu_chr_fe_accept_input(&s->chr); stm32f2xx_update_irq(s); - return retvalue; + break; case USART_BRR: - return s->usart_brr; + retvalue = s->usart_brr; + break; case USART_CR1: - return s->usart_cr1; + retvalue = s->usart_cr1; + break; case USART_CR2: - return s->usart_cr2; + retvalue = s->usart_cr2; + break; case USART_CR3: - return s->usart_cr3; + retvalue = s->usart_cr3; + break; case USART_GTPR: - return s->usart_gtpr; + retvalue = s->usart_gtpr; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); return 0; } - return 0; + trace_stm32f2xx_usart_read(d->id, size, addr, retvalue); + + return retvalue; } static void stm32f2xx_usart_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { STM32F2XXUsartState *s = opaque; + DeviceState *d = DEVICE(s); uint32_t value = val64; unsigned char ch; - DB_PRINT("Write 0x%" PRIx32 ", 0x%"HWADDR_PRIx"\n", value, addr); + trace_stm32f2xx_usart_write(d->id, size, addr, val64); switch (addr) { case USART_SR: diff --git a/hw/char/trace-events b/hw/char/trace-events index 59e1f734a7..140b994fd4 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -125,3 +125,9 @@ xen_console_unrealize(unsigned int idx) "idx %u" xen_console_realize(unsigned int idx, const char *chrdev) "idx %u chrdev %s" xen_console_device_create(unsigned int idx) "idx %u" xen_console_device_destroy(unsigned int idx) "idx %u" + +# stm32f2xx_usart.c +stm32f2xx_usart_read(char *id, unsigned size, uint64_t ofs, uint64_t val) " %s size %d ofs 0x%02" PRIx64 " -> 0x%02" PRIx64 +stm32f2xx_usart_write(char *id, unsigned size, uint64_t ofs, uint64_t val) "%s size %d ofs 0x%02" PRIx64 " <- 0x%02" PRIx64 +stm32f2xx_usart_drop(char *id) " %s dropping the chars" +stm32f2xx_usart_receive(char *id, uint8_t chr) " %s receiving '%c'" From 2ee8c2ccb5a5bb2295b8a17e307efbe35539d289 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 Jan 2025 10:25:34 +0100 Subject: [PATCH 0980/2892] hw/timer/imx_gpt: Remove unused define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250108092538.11474-11-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/imx_gpt.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 2663a9d9ef..11eca9fa4d 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -20,10 +20,6 @@ #include "qemu/log.h" #include "trace.h" -#ifndef DEBUG_IMX_GPT -#define DEBUG_IMX_GPT 0 -#endif - static const char *imx_gpt_reg_name(uint32_t reg) { switch (reg) { From bbaf7a0d4c9f653edd085c06bc2a343e356d9b6a Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 Jan 2025 10:25:35 +0100 Subject: [PATCH 0981/2892] tests/qtest/libqos: Reuse TYPE_IMX_I2C define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Fabiano Rosas Message-ID: <20250108092538.11474-12-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/qtest/libqos/arm-imx25-pdk-machine.c | 5 +++-- tests/qtest/libqos/i2c-imx.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c index 8fe128fae8..2d8b754343 100644 --- a/tests/qtest/libqos/arm-imx25-pdk-machine.c +++ b/tests/qtest/libqos/arm-imx25-pdk-machine.c @@ -23,6 +23,7 @@ #include "libqos-malloc.h" #include "qgraph.h" #include "i2c.h" +#include "hw/i2c/imx_i2c.h" #define ARM_PAGE_SIZE 4096 #define IMX25_PDK_RAM_START 0x80000000 @@ -50,7 +51,7 @@ static void *imx25_pdk_get_driver(void *object, const char *interface) static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device) { QIMX25PDKMachine *machine = obj; - if (!g_strcmp0(device, "imx.i2c")) { + if (!g_strcmp0(device, TYPE_IMX_I2C)) { return &machine->i2c_1.obj; } @@ -86,7 +87,7 @@ static void imx25_pdk_register_nodes(void) .extra_device_opts = "bus=i2c-bus.0" }; qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk); - qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL); + qos_node_contains("arm/imx25-pdk", TYPE_IMX_I2C, &edge, NULL); } libqos_init(imx25_pdk_register_nodes); diff --git a/tests/qtest/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c index 710cb926d6..6d868e4cc4 100644 --- a/tests/qtest/libqos/i2c-imx.c +++ b/tests/qtest/libqos/i2c-imx.c @@ -209,8 +209,8 @@ void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr) static void imx_i2c_register_nodes(void) { - qos_node_create_driver("imx.i2c", NULL); - qos_node_produces("imx.i2c", "i2c-bus"); + qos_node_create_driver(TYPE_IMX_I2C, NULL); + qos_node_produces(TYPE_IMX_I2C, "i2c-bus"); } libqos_init(imx_i2c_register_nodes); From 2eabc49809b16317094b005320efba8e82b885db Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 Jan 2025 10:25:37 +0100 Subject: [PATCH 0982/2892] hw/misc/imx6_src: Convert DPRINTF() to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250108092538.11474-14-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/imx6_src.c | 23 +++++------------------ hw/misc/trace-events | 6 ++++++ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c index dc6a2b92ba..06cc46292e 100644 --- a/hw/misc/imx6_src.c +++ b/hw/misc/imx6_src.c @@ -17,18 +17,7 @@ #include "qemu/module.h" #include "target/arm/arm-powerctl.h" #include "hw/core/cpu.h" - -#ifndef DEBUG_IMX6_SRC -#define DEBUG_IMX6_SRC 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX6_SRC) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_SRC, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx6_src_reg_name(uint32_t reg) { @@ -87,7 +76,7 @@ static void imx6_src_reset(DeviceState *dev) { IMX6SRCState *s = IMX6_SRC(dev); - DPRINTF("\n"); + trace_imx6_src_reset(); memset(s->regs, 0, sizeof(s->regs)); @@ -111,7 +100,7 @@ static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size) } - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_src_reg_name(index), value); + trace_imx6_src_read(imx6_src_reg_name(index), value); return value; } @@ -134,8 +123,7 @@ static void imx6_clear_reset_bit(CPUState *cpu, run_on_cpu_data data) assert(bql_locked()); s->regs[SRC_SCR] = deposit32(s->regs[SRC_SCR], ri->reset_bit, 1, 0); - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", - imx6_src_reg_name(SRC_SCR), s->regs[SRC_SCR]); + trace_imx6_clear_reset_bit(imx6_src_reg_name(SRC_SCR), s->regs[SRC_SCR]); g_free(ri); } @@ -173,8 +161,7 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value, return; } - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_src_reg_name(index), - (uint32_t)current_value); + trace_imx6_src_write(imx6_src_reg_name(index), value); change_mask = s->regs[index] ^ (uint32_t)current_value; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 0f5d2b5666..cf1abe6928 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -253,6 +253,12 @@ ccm_clock_freq(uint32_t clock, uint32_t freq) "(Clock = %d) = %d" ccm_read_reg(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 ccm_write_reg(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 +# imx6_src.c +imx6_src_read(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_src_write(const char *reg_name, uint64_t value) "reg[%s] <= 0x%" PRIx64 +imx6_clear_reset_bit(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 +imx6_src_reset(void) "" + # imx7_src.c imx7_src_read(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 imx7_src_write(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 From 1bada3c94c7d6a3ebb731eb23a6e4454a921c4e0 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 11 Jan 2025 19:37:06 +0100 Subject: [PATCH 0983/2892] hw/char/imx_serial: Turn some DPRINTF() statements into trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Message-ID: <20250111183711.2338-9-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/imx_serial.c | 58 +++++++++++++++++++++++++++++--------------- hw/char/trace-events | 5 ++++ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 12705a1337..7c353fde50 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -27,6 +27,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/fifo32.h" +#include "trace.h" #ifndef DEBUG_IMX_UART #define DEBUG_IMX_UART 0 @@ -184,10 +185,10 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, unsigned size) { IMXSerialState *s = (IMXSerialState *)opaque; + Chardev *chr = qemu_chr_fe_get_driver(&s->chr); uint32_t c, rx_used; uint8_t rxtl = s->ufcr & TL_MASK; - - DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset); + uint64_t value; switch (offset >> 2) { case 0x0: /* URXD */ @@ -208,49 +209,67 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, imx_serial_rx_fifo_ageing_timer_restart(s); qemu_chr_fe_accept_input(&s->chr); } - return c; + value = c; + break; case 0x20: /* UCR1 */ - return s->ucr1; + value = s->ucr1; + break; case 0x21: /* UCR2 */ - return s->ucr2; + value = s->ucr2; + break; case 0x25: /* USR1 */ - return s->usr1; + value = s->usr1; + break; case 0x26: /* USR2 */ - return s->usr2; + value = s->usr2; + break; case 0x2A: /* BRM Modulator */ - return s->ubmr; + value = s->ubmr; + break; case 0x2B: /* Baud Rate Count */ - return s->ubrc; + value = s->ubrc; + break; case 0x2d: /* Test register */ - return s->uts1; + value = s->uts1; + break; case 0x24: /* UFCR */ - return s->ufcr; + value = s->ufcr; + break; case 0x2c: - return s->onems; + value = s->onems; + break; case 0x22: /* UCR3 */ - return s->ucr3; + value = s->ucr3; + break; case 0x23: /* UCR4 */ - return s->ucr4; + value = s->ucr4; + break; case 0x29: /* BRM Incremental */ - return 0x0; /* TODO */ + value = 0x0; /* TODO */ + break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); - return 0; + value = 0; + break; } + + trace_imx_serial_read(chr ? chr->label : "NODEV", offset, value); + + return value; } static void imx_serial_write(void *opaque, hwaddr offset, @@ -260,8 +279,7 @@ static void imx_serial_write(void *opaque, hwaddr offset, Chardev *chr = qemu_chr_fe_get_driver(&s->chr); unsigned char ch; - DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n", - offset, (unsigned int)value, chr ? chr->label : "NODEV"); + trace_imx_serial_write(chr ? chr->label : "NODEV", offset, value); switch (offset >> 2) { case 0x10: /* UTXD */ @@ -373,9 +391,11 @@ static int imx_can_receive(void *opaque) static void imx_put_data(void *opaque, uint32_t value) { IMXSerialState *s = (IMXSerialState *)opaque; + Chardev *chr = qemu_chr_fe_get_driver(&s->chr); uint8_t rxtl = s->ufcr & TL_MASK; - DPRINTF("received char\n"); + trace_imx_serial_put_data(chr ? chr->label : "NODEV", value); + imx_serial_rx_fifo_push(s, value); if (fifo32_num_used(&s->rx_fifo) >= rxtl) { s->usr1 |= USR1_RRDY; diff --git a/hw/char/trace-events b/hw/char/trace-events index 140b994fd4..3ee7cfcdff 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -52,6 +52,11 @@ escc_sunkbd_event_out(int ch) "Translated keycode 0x%2.2x" escc_kbd_command(int val) "Command %d" escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=0x%01x" +# imx_serial.c +imx_serial_read(const char *chrname, uint64_t addr, uint64_t value) "%s:[0x%03" PRIu64 "] -> 0x%08" PRIx64 +imx_serial_write(const char *chrname, uint64_t addr, uint64_t value) "%s:[0x%03" PRIu64 "] <- 0x%08" PRIx64 +imx_serial_put_data(const char *chrname, uint32_t value) "%s: 0x%" PRIx32 + # pl011.c pl011_irq_state(int level) "irq state %d" pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" From e589c0ea9c991094a85dbd7bee24e02e7d272310 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 11 Jan 2025 19:37:09 +0100 Subject: [PATCH 0984/2892] hw/i2c/imx_i2c: Convert DPRINTF() to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also print the QOM canonical path when tracing which allows for distinguishing the many instances a typical i.MX SoC has. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Message-ID: <20250111183711.2338-12-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/imx_i2c.c | 21 +++++---------------- hw/i2c/trace-events | 5 +++++ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index c565fd5b8a..d62213b9e0 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -25,18 +25,7 @@ #include "hw/i2c/i2c.h" #include "qemu/log.h" #include "qemu/module.h" - -#ifndef DEBUG_IMX_I2C -#define DEBUG_IMX_I2C 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_I2C) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_I2C, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx_i2c_get_regname(unsigned offset) { @@ -152,8 +141,8 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, break; } - DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n", - imx_i2c_get_regname(offset), offset, value); + trace_imx_i2c_read(DEVICE(s)->canonical_path, imx_i2c_get_regname(offset), + offset, value); return (uint64_t)value; } @@ -163,8 +152,8 @@ static void imx_i2c_write(void *opaque, hwaddr offset, { IMXI2CState *s = IMX_I2C(opaque); - DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n", - imx_i2c_get_regname(offset), offset, (int)value); + trace_imx_i2c_read(DEVICE(s)->canonical_path, imx_i2c_get_regname(offset), + offset, value); value &= 0xff; diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index f708a7ace1..1ad0e95c0e 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -56,3 +56,8 @@ npcm7xx_smbus_recv_fifo(const char *id, uint8_t received, uint8_t expected) "%s pca954x_write_bytes(uint8_t value) "PCA954X write data: 0x%02x" pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x" + +# imx_i2c.c + +imx_i2c_read(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] -> 0x%02" PRIx64 +imx_i2c_write(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] <- 0x%02" PRIx64 From e3778c8499add463fa2c62b0251a4c9879f37e04 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 11 Jan 2025 19:37:11 +0100 Subject: [PATCH 0985/2892] hw/gpio/imx_gpio: Turn DPRINTF() into trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it add a trace event for input GPIO events. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Message-ID: <20250111183711.2338-14-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/gpio/imx_gpio.c | 18 +++++++----------- hw/gpio/trace-events | 5 +++++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index 898f80f8c8..549a281ed7 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -24,6 +24,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" +#include "trace.h" #ifndef DEBUG_IMX_GPIO #define DEBUG_IMX_GPIO 0 @@ -34,14 +35,6 @@ typedef enum IMXGPIOLevel { IMX_GPIO_LEVEL_HIGH = 1, } IMXGPIOLevel; -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_GPIO) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPIO, \ - __func__, ##args); \ - } \ - } while (0) - static const char *imx_gpio_reg_name(uint32_t reg) { switch (reg) { @@ -111,6 +104,8 @@ static void imx_gpio_set(void *opaque, int line, int level) IMXGPIOState *s = IMX_GPIO(opaque); IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW; + trace_imx_gpio_set(DEVICE(s)->canonical_path, line, imx_level); + imx_gpio_set_int_line(s, line, imx_level); /* this is an input signal, so set PSR */ @@ -200,7 +195,8 @@ static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size) break; } - DPRINTF("(%s) = 0x%" PRIx32 "\n", imx_gpio_reg_name(offset), reg_value); + trace_imx_gpio_read(DEVICE(s)->canonical_path, imx_gpio_reg_name(offset), + reg_value); return reg_value; } @@ -210,8 +206,8 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, { IMXGPIOState *s = IMX_GPIO(opaque); - DPRINTF("(%s, value = 0x%" PRIx32 ")\n", imx_gpio_reg_name(offset), - (uint32_t)value); + trace_imx_gpio_write(DEVICE(s)->canonical_path, imx_gpio_reg_name(offset), + value); switch (offset) { case DR_ADDR: diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index b91cc7e9a4..cea896b28f 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -1,5 +1,10 @@ # See docs/devel/tracing.rst for syntax documentation. +# imx_gpio.c +imx_gpio_read(const char *id, const char *reg, uint32_t value) "%s:[%s] -> 0x%" PRIx32 +imx_gpio_write(const char *id, const char *reg, uint32_t value) "%s:[%s] <- 0x%" PRIx32 +imx_gpio_set(const char *id, int line, int level) "%s:[%d] <- %d" + # npcm7xx_gpio.c npcm7xx_gpio_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 npcm7xx_gpio_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 From a87077316ed2f1c1c8ba8faf05feed9dbf0f2fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Jan 2025 10:59:31 +0100 Subject: [PATCH 0986/2892] tests/qtest/boot-serial-test: Correct HPPA machine name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 7df6f751176 ("hw/hppa: Split out machine creation") renamed the 'hppa' machine as 'B160L', but forgot to update the boot serial test, which ended being skipped. Cc: qemu-stable@nongnu.org Fixes: 7df6f751176 ("hw/hppa: Split out machine creation") Reported-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-Id: <20250102100340.43014-2-philmd@linaro.org> --- tests/qtest/boot-serial-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index ffa9e780ad..c924cf94fc 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -190,7 +190,7 @@ static const testdef_t tests[] = { sizeof(kernel_plml605), kernel_plml605 }, { "arm", "raspi2b", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, /* For hppa, force bios to output to serial by disabling graphics. */ - { "hppa", "hppa", "-vga none", "SeaBIOS wants SYSTEM HALT" }, + { "hppa", "B160L", "-vga none", "SeaBIOS wants SYSTEM HALT" }, { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, From e4a407d2b41de8a35e2a1ecc0b9f22178ce71577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 31 Dec 2024 19:58:37 +0100 Subject: [PATCH 0987/2892] tests: Add functional tests for HPPA machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add quick firmware boot tests (less than 1sec) for the B160L (32-bit) and C3700 (64-bit) HPPA machines: $ make check-functional-hppa ... 4/4 qemu:func-quick+func-hppa / func-hppa-hppa_seabios OK 0.22s 2 subtests passed Remove the duplicated B160L test in qtest/boot-serial-test.c. Suggested-by: Helge Deller Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Helge Deller Tested-by: Helge Deller Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250102100340.43014-3-philmd@linaro.org> --- MAINTAINERS | 1 + tests/functional/meson.build | 4 +++ tests/functional/test_hppa_seabios.py | 35 +++++++++++++++++++++++++++ tests/qtest/boot-serial-test.c | 2 -- tests/qtest/meson.build | 2 +- 5 files changed, 41 insertions(+), 3 deletions(-) create mode 100755 tests/functional/test_hppa_seabios.py diff --git a/MAINTAINERS b/MAINTAINERS index c1d954c9de..d26d497a38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1203,6 +1203,7 @@ F: include/hw/pci-host/astro.h F: include/hw/pci-host/dino.h F: pc-bios/hppa-firmware.img F: roms/seabios-hppa/ +F: tests/functional/test_hppa_seabios.py LoongArch Machines ------------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index bd3d903cfc..cf80924ddc 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -108,6 +108,10 @@ tests_avr_system_thorough = [ 'avr_mega2560', ] +tests_hppa_system_quick = [ + 'hppa_seabios', +] + tests_i386_system_thorough = [ 'i386_tuxrun', ] diff --git a/tests/functional/test_hppa_seabios.py b/tests/functional/test_hppa_seabios.py new file mode 100755 index 0000000000..a44d1a3eeb --- /dev/null +++ b/tests/functional/test_hppa_seabios.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# SeaBIOS boot test for HPPA machines +# +# Copyright (c) 2024 Linaro, Ltd +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern + +class HppaSeabios(QemuSystemTest): + + timeout = 5 + MACH_BITS = {'B160L': 32, 'C3700': 64} + + def boot_seabios(self): + mach = self.machine + bits = self.MACH_BITS[mach] + self.vm.set_console() + self.vm.launch() + self.machine + wait_for_console_pattern(self, f'SeaBIOS PA-RISC {bits}-bit Firmware') + wait_for_console_pattern(self, f'Emulated machine: HP {mach} ({bits}-bit') + + def test_hppa_32(self): + self.set_machine('B160L') + self.boot_seabios() + + def test_hppa_64(self): + self.set_machine('C3700') + self.boot_seabios() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index c924cf94fc..a05d26ee99 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -189,8 +189,6 @@ static const testdef_t tests[] = { { "microblazeel", "petalogix-ml605", "", "TT", sizeof(kernel_plml605), kernel_plml605 }, { "arm", "raspi2b", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, - /* For hppa, force bios to output to serial by disabling graphics. */ - { "hppa", "B160L", "-vga none", "SeaBIOS wants SYSTEM HALT" }, { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index f75c1057a4..bf4d5c268b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -140,7 +140,7 @@ qtests_alpha = ['boot-serial-test'] + \ qtests_avr = [ 'boot-serial-test' ] -qtests_hppa = ['boot-serial-test'] + \ +qtests_hppa = \ qtests_filter + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) From f4f4173188249d33a3ec4a0c910c168c9181ac9d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 30 Dec 2024 12:46:47 +0100 Subject: [PATCH 0988/2892] target/hppa: Convert hppa_cpu_init() to ResetHold handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hppa_cpu_initfn() is called once when a HPPA CPU instance is initialized, but it sets fields which should be set each time a CPU resets. Rename it as a reset handler, having it matching the ResettablePhases::hold() signature, and register it as ResettableClass handler. Since on reset the CPU registers and TLB entries are expected to be zero, add a memset() call clearing CPUHPPAState up to the &end_reset_fields marker. Signed-off-by: Helge Deller Co-developed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241231190620.24442-3-philmd@linaro.org> --- target/hppa/cpu.c | 14 ++++++++++++-- target/hppa/cpu.h | 5 +++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 47d0160955..d784bcdd60 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -193,13 +193,20 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) tcg_cflags_set(cs, CF_PCREL); } -static void hppa_cpu_initfn(Object *obj) +static void hppa_cpu_reset_hold(Object *obj, ResetType type) { + HPPACPUClass *scc = HPPA_CPU_GET_CLASS(obj); CPUState *cs = CPU(obj); HPPACPU *cpu = HPPA_CPU(obj); CPUHPPAState *env = &cpu->env; + if (scc->parent_phases.hold) { + scc->parent_phases.hold(obj, type); + } cs->exception_index = -1; + + memset(env, 0, offsetof(CPUHPPAState, end_reset_fields)); + cpu_hppa_loaded_fr0(env); cpu_hppa_put_psw(env, PSW_W); } @@ -242,10 +249,14 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); HPPACPUClass *acc = HPPA_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, hppa_cpu_realizefn, &acc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, hppa_cpu_reset_hold, NULL, + &acc->parent_phases); + cc->class_by_name = hppa_cpu_class_by_name; cc->has_work = hppa_cpu_has_work; cc->mmu_index = hppa_cpu_mmu_index; @@ -269,7 +280,6 @@ static const TypeInfo hppa_cpu_type_infos[] = { .parent = TYPE_CPU, .instance_size = sizeof(HPPACPU), .instance_align = __alignof(HPPACPU), - .instance_init = hppa_cpu_initfn, .abstract = false, .class_size = sizeof(HPPACPUClass), .class_init = hppa_cpu_class_init, diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 22a6510e08..c1d69c1a83 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -263,6 +263,9 @@ typedef struct CPUArchState { IntervalTreeRoot tlb_root; HPPATLBEntry tlb[HPPA_TLB_ENTRIES]; + + /* Fields up to this point are cleared by a CPU reset */ + struct {} end_reset_fields; } CPUHPPAState; /** @@ -281,6 +284,7 @@ struct ArchCPU { /** * HPPACPUClass: * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. * * An HPPA CPU model. */ @@ -288,6 +292,7 @@ struct HPPACPUClass { CPUClass parent_class; DeviceRealize parent_realize; + ResettablePhases parent_phases; }; #include "exec/cpu-all.h" From 20f7b890173be6cd38dabad8122b8b2fe51d0255 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 30 Dec 2024 10:53:25 +0100 Subject: [PATCH 0989/2892] hw/hppa: Reset vCPUs calling resettable_reset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than manually (and incompletely) resetting vCPUs, call resettable_reset() which will fully reset the vCPUs. Remove redundant assignations. Signed-off-by: Helge Deller Co-developed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241231190620.24442-4-philmd@linaro.org> --- hw/hppa/machine.c | 6 +++--- target/hppa/cpu.c | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 65259308e2..8230f43e41 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -655,12 +655,12 @@ static void hppa_machine_reset(MachineState *ms, ResetType type) for (i = 0; i < smp_cpus; i++) { CPUState *cs = CPU(cpu[i]); + /* reset CPU */ + resettable_reset(OBJECT(cs), RESET_TYPE_COLD); + cpu_set_pc(cs, firmware_entry); cpu[i]->env.psw = PSW_Q; cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000; - - cs->exception_index = -1; - cs->halted = 0; } /* already initialized by machine_hppa_init()? */ diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index d784bcdd60..41538d39d6 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -204,6 +204,7 @@ static void hppa_cpu_reset_hold(Object *obj, ResetType type) scc->parent_phases.hold(obj, type); } cs->exception_index = -1; + cs->halted = 0; memset(env, 0, offsetof(CPUHPPAState, end_reset_fields)); From 3d66ec208ca75329b61a4e9c4a58462ed5948504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Dec 2024 16:11:09 +0100 Subject: [PATCH 0990/2892] target/hppa: Only set PSW 'M' bit on reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On reset: "All PSW bits except the M bit is reset. The M bit is set." Commit 1a19da0da44 ("target/hppa: Fill in hppa_cpu_do_interrupt / hppa_cpu_exec_interrupt") inadvertently set the W bit at RESET, remove it and set the M bit. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Helge Deller Message-Id: <20241231190620.24442-5-philmd@linaro.org> --- target/hppa/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 41538d39d6..dbd4684284 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -209,7 +209,7 @@ static void hppa_cpu_reset_hold(Object *obj, ResetType type) memset(env, 0, offsetof(CPUHPPAState, end_reset_fields)); cpu_hppa_loaded_fr0(env); - cpu_hppa_put_psw(env, PSW_W); + cpu_hppa_put_psw(env, PSW_M); } static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model) From 46f7be06c8e568ec5d5c4aa07c81b42fc0fc863a Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 30 Dec 2024 12:47:52 +0100 Subject: [PATCH 0991/2892] target/hppa: Set PC on vCPU reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On reset: "The CPU begins fetching instructions from address 0xf0000004. This address is in PDC space." Switch vCPUs to 32-bit mode (PSW_W bit is not set) and start execution at address 0xf0000004. Signed-off-by: Helge Deller Co-developed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241231190620.24442-6-philmd@linaro.org> --- target/hppa/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index dbd4684284..7278b7ca6b 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -205,6 +205,7 @@ static void hppa_cpu_reset_hold(Object *obj, ResetType type) } cs->exception_index = -1; cs->halted = 0; + cpu_set_pc(cs, 0xf0000004); memset(env, 0, offsetof(CPUHPPAState, end_reset_fields)); From 5c27cbd7b2ab825528086c0bd10029556ab88fd8 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 30 Dec 2024 00:41:54 +0100 Subject: [PATCH 0992/2892] target/hppa: Speed up hppa_is_pa20() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although the hppa_is_pa20() helper is costly due to string comparisons in object_dynamic_cast(), it is called quite often during memory lookups and at each start of a block of instruction translations. Speed hppa_is_pa20() up by calling object_dynamic_cast() only once at CPU creation and store the result in the is_pa20 of struct CPUArchState. Signed-off-by: Helge Deller Co-developed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20241231190620.24442-7-philmd@linaro.org> --- target/hppa/cpu.c | 8 ++++++++ target/hppa/cpu.h | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 7278b7ca6b..b0bc9d35e4 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -193,6 +193,13 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) tcg_cflags_set(cs, CF_PCREL); } +static void hppa_cpu_initfn(Object *obj) +{ + CPUHPPAState *env = cpu_env(CPU(obj)); + + env->is_pa20 = !!object_dynamic_cast(obj, TYPE_HPPA64_CPU); +} + static void hppa_cpu_reset_hold(Object *obj, ResetType type) { HPPACPUClass *scc = HPPA_CPU_GET_CLASS(obj); @@ -282,6 +289,7 @@ static const TypeInfo hppa_cpu_type_infos[] = { .parent = TYPE_CPU, .instance_size = sizeof(HPPACPU), .instance_align = __alignof(HPPACPU), + .instance_init = hppa_cpu_initfn, .abstract = false, .class_size = sizeof(HPPACPUClass), .class_init = hppa_cpu_class_init, diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index c1d69c1a83..083d4f5a56 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -266,6 +266,8 @@ typedef struct CPUArchState { /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; + + bool is_pa20; } CPUHPPAState; /** @@ -297,9 +299,9 @@ struct HPPACPUClass { #include "exec/cpu-all.h" -static inline bool hppa_is_pa20(CPUHPPAState *env) +static inline bool hppa_is_pa20(const CPUHPPAState *env) { - return object_dynamic_cast(OBJECT(env_cpu(env)), TYPE_HPPA64_CPU) != NULL; + return env->is_pa20; } static inline int HPPA_BTLB_ENTRIES(CPUHPPAState *env) From c48cc87ba15b3f7e327f6c6fab9eec7fe7421136 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 3 Jan 2025 14:45:14 +0800 Subject: [PATCH 0993/2892] hw/loongarch/virt: Checkpatch cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code cleanup with directory hw/loongarch/, removing errors from command "scripts/checkpatch.pl hw/loongarch/*" Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250103064514.2660438-1-maobibo@loongson.cn> Signed-off-by: Philippe Mathieu-Daudé --- hw/loongarch/acpi-build.c | 3 ++- hw/loongarch/boot.c | 4 ++-- hw/loongarch/virt.c | 8 +++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 9eb5fb68bf..fdd62acf7e 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -456,8 +456,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); - for (i = 0; i < VIRT_UART_COUNT; i++) + for (i = 0; i < VIRT_UART_COUNT; i++) { build_uart_device_aml(dsdt, i); + } build_pci_device_aml(dsdt, lvms); build_la_ged_aml(dsdt, machine); build_flash_aml(dsdt, lvms); diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 241c0eef1f..bd8763c61c 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -292,7 +292,7 @@ static void reset_load_elf(void *opaque) cpu_reset(CPU(cpu)); if (env->load_elf) { - if (cpu == LOONGARCH_CPU(first_cpu)) { + if (cpu == LOONGARCH_CPU(first_cpu)) { env->gpr[4] = env->boot_info->a0; env->gpr[5] = env->boot_info->a1; env->gpr[6] = env->boot_info->a2; @@ -354,7 +354,7 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) if (info->kernel_filename) { kernel_addr = load_kernel_info(info); } else { - if(!qtest_enabled()) { + if (!qtest_enabled()) { warn_report("No kernel provided, booting from flash drive."); } } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index df56d75a6e..db37ed6a71 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -331,8 +331,9 @@ static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a"); qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); - if (chosen) + if (chosen) { qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + } qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4); qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", *pch_pic_phandle); @@ -815,7 +816,7 @@ static void virt_devices_init(DeviceState *pch_pic, * Create uart fdt node in reverse order so that they appear * in the finished device tree lowest address first */ - for (i = VIRT_UART_COUNT; i --> 0;) { + for (i = VIRT_UART_COUNT; i-- > 0;) { hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE; int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE; serial_mm_init(get_system_memory(), base, 0, @@ -1175,8 +1176,9 @@ static void fw_cfg_add_memory(MachineState *ms) size = ram_size - numa_info[0].node_mem; } - if (size) + if (size) { memmap_add_entry(base, size, 1); + } } static void virt_init(MachineState *machine) From 78b0c15a563ac4be5afb0375602ca0a3adc6c442 Mon Sep 17 00:00:00 2001 From: Gabriel Barrantes Date: Sat, 28 Dec 2024 01:16:57 +0000 Subject: [PATCH 0994/2892] backends/cryptodev-vhost-user: Fix local_error leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not propagate error to the upper, directly output the error to avoid leaks. Fixes: 2fda101de07 ("virtio-crypto: Support asynchronous mode") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2714 Signed-off-by: Gabriel Barrantes Reviewed-by: zhenwei pi Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- backends/cryptodev-vhost-user.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index 43efdf9747..3295c6198a 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -281,8 +281,7 @@ static int cryptodev_vhost_user_create_session( break; default: - error_setg(&local_error, "Unsupported opcode :%" PRIu32 "", - sess_info->op_code); + error_report("Unsupported opcode :%" PRIu32 "", sess_info->op_code); return -VIRTIO_CRYPTO_NOTSUPP; } From bb5b7fced6b5d3334ab20702fc846e47bb1fb731 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Fri, 13 Dec 2024 17:06:14 +0100 Subject: [PATCH 0995/2892] hw/usb/hcd-xhci-pci: Use modulo to select MSI vector as per spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU would crash with a failed assertion if the XHCI controller attempted to raise the interrupt on an interrupter corresponding to a MSI vector with a higher index than the highest configured for the device by the guest driver. This behaviour is correct on the MSI/PCI side: per PCI 3.0 spec, devices must ensure they do not send MSI notifications for vectors beyond the range of those allocated by the system/driver software. Unlike MSI-X, there is no generic way for handling aliasing in the case of fewer allocated vectors than requested, so the specifics are up to device implementors. (Section 6.8.3.4. "Sending Messages") It turns out the XHCI spec (Implementation Note in section 4.17, "Interrupters") requires that the host controller signal the MSI vector with the number computed by taking the interrupter number modulo the number of enabled MSI vectors. This change introduces that modulo calculation, fixing the failed assertion. This makes the device work correctly in MSI mode with macOS's XHCI driver, which only allocates a single vector. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250112210056.16658-2-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index a069b42338..49642aab58 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -74,6 +74,7 @@ static bool xhci_pci_intr_raise(XHCIState *xhci, int n, bool level) } if (msi_enabled(pci_dev) && level) { + n %= msi_nr_vectors_allocated(pci_dev); msi_notify(pci_dev, n); return true; } From ef82ab692424c7fb42c3aeb6b65db68eade6213a Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Fri, 27 Dec 2024 13:13:33 +0100 Subject: [PATCH 0996/2892] hw/usb/hcd-xhci-pci: Use event ring 0 if mapping unsupported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XHCI specification, section 4.17.1 specifies that "If the Number of Interrupters (MaxIntrs) field is greater than 1, then Interrupter Mapping shall be supported." and "If Interrupter Mapping is not supported, the Interrupter Target field shall be ignored by the xHC and all Events targeted at Interrupter 0." QEMU's XHCI device has so far not specially addressed this case, so we add a check to xhci_event() to redirect to event ring and interrupt 0 if mapping is disabled. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241227121336.25838-4-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 7dc0994c89..00d5bc3779 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -644,6 +644,10 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) dma_addr_t erdp; unsigned int dp_idx; + if (xhci->numintrs == 1) { + v = 0; + } + if (v >= xhci->numintrs) { DPRINTF("intr nr out of range (%d >= %d)\n", v, xhci->numintrs); return; From e555abceec0c7f958d553a39ce4ac79239a34537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 19:07:05 +0100 Subject: [PATCH 0997/2892] hw/tricore/triboard: Remove unnecessary use of &first_cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit triboard_machine_init() has access to the single CPU via: TriBoardMachineState { TC27XSoCState { TriCoreCPU cpu; ... } tc27x_soc; } ms; Pass it as argument to tricore_load_kernel() so we can remove the &first_cpu global use. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Reviewed-by: Bastian Koppelmann Message-Id: <20250110180909.83165-1-philmd@linaro.org> --- hw/tricore/triboard.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index 4dba0259cd..9cc8d282ff 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -31,11 +31,10 @@ #include "hw/tricore/triboard.h" #include "hw/tricore/tc27x_soc.h" -static void tricore_load_kernel(const char *kernel_filename) +static void tricore_load_kernel(TriCoreCPU *cpu, const char *kernel_filename) { uint64_t entry; long kernel_size; - TriCoreCPU *cpu; CPUTriCoreState *env; kernel_size = load_elf(kernel_filename, NULL, @@ -46,7 +45,6 @@ static void tricore_load_kernel(const char *kernel_filename) error_report("no kernel file '%s'", kernel_filename); exit(1); } - cpu = TRICORE_CPU(first_cpu); env = &cpu->env; env->PC = entry; } @@ -62,7 +60,7 @@ static void triboard_machine_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&ms->tc27x_soc), &error_fatal); if (machine->kernel_filename) { - tricore_load_kernel(machine->kernel_filename); + tricore_load_kernel(&ms->tc27x_soc.cpu, machine->kernel_filename); } } From a18ed70625e7360cafbd810bf35c6bc0abf005fb Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Wed, 18 Dec 2024 13:30:55 +0100 Subject: [PATCH 0998/2892] MAINTAINERS: remove myself from sbsa-ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am ending my time with Linaro and do not have plans to continue working on SBSA Reference Platform anymore. Signed-off-by: Marcin Juszkiewicz Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Leif Lindholm Message-ID: <20241218123055.11220-1-marcin.juszkiewicz@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index d26d497a38..026890ddc0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -923,7 +923,6 @@ SBSA-REF M: Radoslaw Biernacki M: Peter Maydell R: Leif Lindholm -R: Marcin Juszkiewicz L: qemu-arm@nongnu.org S: Maintained F: hw/arm/sbsa-ref.c From b44314abca6fe22d21795eaeaf280d504183cb11 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 7 Jan 2025 01:56:39 +0000 Subject: [PATCH 0999/2892] MAINTAINERS: Add me as the maintainer for ivshmem-flat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add me as the maintainer for the ivshmem-flat device. Signed-off-by: Gustavo Romero Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250107015639.27648-1-gustavo.romero@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 026890ddc0..ce42b95e71 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2786,6 +2786,13 @@ F: hw/hyperv/hv-balloon*.h F: include/hw/hyperv/dynmem-proto.h F: include/hw/hyperv/hv-balloon.h +ivshmem-flat +M: Gustavo Romero +S: Maintained +F: hw/misc/ivshmem-flat.c +F: include/hw/misc/ivshmem-flat.h +F: docs/system/devices/ivshmem-flat.rst + Subsystems ---------- Overall Audio backends From 07340820e63460e8b859c1ce0568c31226d31311 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 11 Jan 2025 15:42:36 +0900 Subject: [PATCH 1000/2892] MAINTAINERS: Update path to coreaudio.m MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 8b46d7e2dc8e ("audio: Rename coreaudio extension to use Objective-C compiler") renamed coreaudio.c to coreaudio.m. Signed-off-by: Akihiko Odaki Reviewed-by: Christian Schoenebeck Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250111-maintainers-v1-1-faebe6ef0fec@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index ce42b95e71..8b9d9a7cac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2801,7 +2801,7 @@ M: Marc-André Lureau S: Odd Fixes F: audio/ X: audio/alsaaudio.c -X: audio/coreaudio.c +X: audio/coreaudio.m X: audio/dsound* X: audio/jackaudio.c X: audio/ossaudio.c @@ -2823,7 +2823,7 @@ M: Philippe Mathieu-Daudé R: Christian Schoenebeck R: Akihiko Odaki S: Odd Fixes -F: audio/coreaudio.c +F: audio/coreaudio.m DSound Audio backend M: Gerd Hoffmann From 838cf72b5d2cd875897d8bdfea4b23f6d9fdc602 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Sun, 22 Dec 2024 16:53:41 +0000 Subject: [PATCH 1001/2892] Add a b4 configuration file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit b4 [1] is a convenient tool to manage patch series with mailing list working flow. Add a project default config file to match QEMU's mailing list conventions as well as adopting differences on scripting. Examples of b4: ``` $ b4 prep --check Checking patches using: scripts/checkpatch.pl -q --terse --no-summary --mailback - --- Signed-off-by: Jiaxun Yang --- Changes in v2: - Add lore masks (philmd) from: https://lore.kernel.org/qemu-devel/20241224135054.10243-1-philmd@linaro.org/ - Link to v1: https://lore.kernel.org/r/20241222-b4-config-v1-1-b3667beb30a4@flygoat.com --- ● cc5a4c890fed: Add a b4 configuration file ● checkpatch.pl: 27: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? --- Success: 0, Warning: 1, Error: 0 ``` ``` $ b4 prep -c Will collect To: addresses using echo Will collect Cc: addresses using get_maintainer.pl Collecting To/Cc addresses + To: qemu-devel@nongnu.org --- You can trim/expand this list with: b4 prep --edit-cover Invoking git-filter-repo to update the cover letter. New history written in 0.02 seconds... Completely finished after 0.06 seconds ``` [1]: https://b4.docs.kernel.org/ Co-developed-by: Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Jiaxun Yang Message-ID: <20250102-b4-config-v2-1-cc7299e399bb@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- .b4-config | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .b4-config diff --git a/.b4-config b/.b4-config new file mode 100644 index 0000000000..4b9b2fe290 --- /dev/null +++ b/.b4-config @@ -0,0 +1,14 @@ +# +# Common b4 settings that can be used to send patches to QEMU upstream. +# https://b4.docs.kernel.org/ +# + +[b4] + send-series-to = qemu-devel@nongnu.org + send-auto-to-cmd = echo + send-auto-cc-cmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback + am-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback - + prep-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback - + searchmask = https://lore.kernel.org/qemu-devel/?x=m&t=1&q=%s + linkmask = https://lore.kernel.org/qemu-devel/%s + linktrailermask = Message-ID: <%s> From dd291171740871a84c183d886f70b8d2e6a68d09 Mon Sep 17 00:00:00 2001 From: Miao Hao Date: Tue, 14 Jan 2025 17:54:44 +0800 Subject: [PATCH 1002/2892] target/loongarch: Add page table walker support for debugger usage When dump memory content with gva address, software page table walker is necessary to get responding gpa address. Here page table walker is added for debugger usage. Signed-off-by: Miao Hao Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu_helper.c | 94 +++++++++++++++++++++++++++++-- target/loongarch/internals.h | 4 +- target/loongarch/tcg/tlb_helper.c | 4 +- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 580362ac3e..930466ca48 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -141,9 +141,85 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, return false; } +static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address) +{ + CPUState *cs = env_cpu(env); + target_ulong index, phys; + uint64_t dir_base, dir_width; + uint64_t base; + int level; + + if ((address >> 63) & 0x1) { + base = env->CSR_PGDH; + } else { + base = env->CSR_PGDL; + } + base &= TARGET_PHYS_MASK; + + for (level = 4; level > 0; level--) { + get_dir_base_width(env, &dir_base, &dir_width, level); + + if (dir_width == 0) { + continue; + } + + /* get next level page directory */ + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + base = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* base is a huge pte */ + break; + } + } + + /* pte */ + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* Huge Page. base is pte */ + base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); + base = FIELD_DP64(base, TLBENTRY, HUGE, 0); + if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { + base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); + base = FIELD_DP64(base, TLBENTRY, G, 1); + } + } else { + /* Normal Page. base points to pte */ + get_dir_base_width(env, &dir_base, &dir_width, 0); + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + base = ldq_phys(cs->as, phys); + } + + /* TODO: check plv and other bits? */ + + /* base is pte, in normal pte format */ + if (!FIELD_EX64(base, TLBENTRY, V)) { + return TLBRET_NOMATCH; + } + + if (!FIELD_EX64(base, TLBENTRY, D)) { + *prot = PAGE_READ; + } else { + *prot = PAGE_READ | PAGE_WRITE; + } + + /* get TARGET_PAGE_SIZE aligned physical address */ + base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1); + /* mask RPLV, NX, NR bits */ + base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0); + base = FIELD_DP64(base, TLBENTRY_64, NX, 0); + base = FIELD_DP64(base, TLBENTRY_64, NR, 0); + /* mask other attribute bits */ + *physical = base & TARGET_PAGE_MASK; + + return 0; +} + static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, + int is_debug) { int index, match; @@ -151,6 +227,13 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, if (match) { return loongarch_map_tlb_entry(env, physical, prot, address, access_type, index, mmu_idx); + } else if (is_debug) { + /* + * For debugger memory access, we want to do the map when there is a + * legal mapping, even if the mapping is not yet in TLB. return 0 if + * there is a valid map, else none zero. + */ + return loongarch_page_table_walker(env, physical, prot, address); } return TLBRET_NOMATCH; @@ -158,7 +241,8 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, #else static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, + int is_debug) { return TLBRET_NOMATCH; } @@ -178,7 +262,7 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, int get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, int is_debug) { int user_mode = mmu_idx == MMU_USER_IDX; int kernel_mode = mmu_idx == MMU_KERNEL_IDX; @@ -222,7 +306,7 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, /* Mapped address */ return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx); + access_type, mmu_idx, is_debug); } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) @@ -232,7 +316,7 @@ hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(cs, false)) != 0) { + cpu_mmu_index(cs, false), 1) != 0) { return -1; } return phys_addr; diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index ad9cf4fc7a..7b254c5f49 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -56,7 +56,9 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, int *index); int get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx); + MMUAccessType access_type, int mmu_idx, int is_debug); +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #ifdef CONFIG_TCG diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 8c61fe728c..a323606e5a 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -18,7 +18,7 @@ #include "exec/log.h" #include "cpu-csr.h" -static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level) { switch (level) { @@ -485,7 +485,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, /* Data access */ ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx); + access_type, mmu_idx, 0); if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK, From 59c54c1ceb1d84cb48d27a5b26d6f21cb76ee9e1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:13 +0800 Subject: [PATCH 1003/2892] hw/intc/loongarch_ipi: Implement realize interface Add realize interface for loongarch ipi device. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 19 +++++++++++++++++++ include/hw/intc/loongarch_ipi.h | 1 + 2 files changed, 20 insertions(+) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 2ae1a42c46..4e2f9acddf 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "hw/boards.h" +#include "qapi/error.h" #include "hw/intc/loongarch_ipi.h" #include "target/loongarch/cpu.h" @@ -49,10 +50,26 @@ static CPUState *loongarch_cpu_by_arch_id(int64_t arch_id) return NULL; } +static void loongarch_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev); + Error *local_err = NULL; + + lic->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_parent_realize(dc, loongarch_ipi_realize, + &lic->parent_realize); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; } @@ -61,6 +78,8 @@ static const TypeInfo loongarch_ipi_types[] = { { .name = TYPE_LOONGARCH_IPI, .parent = TYPE_LOONGSON_IPI_COMMON, + .instance_size = sizeof(LoongarchIPIState), + .class_size = sizeof(LoongarchIPIClass), .class_init = loongarch_ipi_class_init, } }; diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 276b3040a3..923bf21ecb 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -20,6 +20,7 @@ struct LoongarchIPIState { struct LoongarchIPIClass { LoongsonIPICommonClass parent_class; + DeviceRealize parent_realize; }; #endif From 5b82177addba2487c3c0e1b1974c0076a5a36342 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:14 +0800 Subject: [PATCH 1004/2892] hw/intc/loongson_ipi: Remove num_cpu from loongson_ipi_common With mips64 loongson ipi, num_cpu property is used. With loongarch ipi, num_cpu can be acquired from possible_cpu_arch_ids. Here remove num_cpu setting from loongson_ipi_common, and this piece of code is put into loongson and loongarch ipi separately. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 13 +++++++++++++ hw/intc/loongson_ipi.c | 14 +++++++++++++- hw/intc/loongson_ipi_common.c | 14 -------------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 4e2f9acddf..e6126e4fbc 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -52,14 +52,27 @@ static CPUState *loongarch_cpu_by_arch_id(int64_t arch_id) static void loongarch_ipi_realize(DeviceState *dev, Error **errp) { + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev); LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev); Error *local_err = NULL; + int i; lic->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } + + if (lics->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } + + lics->cpu = g_new0(IPICore, lics->num_cpu); + for (i = 0; i < lics->num_cpu; i++) { + lics->cpu[i].ipi = lics; + qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1); + } } static void loongarch_ipi_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 4e08f03510..1ed39b90ea 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -36,6 +36,7 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) LoongsonIPIClass *lic = LOONGSON_IPI_GET_CLASS(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; + int i; lic->parent_realize(dev, &local_err); if (local_err) { @@ -43,8 +44,19 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) return; } + if (sc->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } + + sc->cpu = g_new0(IPICore, sc->num_cpu); + for (i = 0; i < sc->num_cpu; i++) { + sc->cpu[i].ipi = sc; + qdev_init_gpio_out(dev, &sc->cpu[i].irq, 1); + } + s->ipi_mmio_mem = g_new0(MemoryRegion, sc->num_cpu); - for (unsigned i = 0; i < sc->num_cpu; i++) { + for (i = 0; i < sc->num_cpu; i++) { g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); memory_region_init_io(&s->ipi_mmio_mem[i], OBJECT(dev), diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index 9a081565f5..5d46679ea1 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -10,7 +10,6 @@ #include "hw/intc/loongson_ipi_common.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "qapi/error.h" #include "qemu/log.h" #include "migration/vmstate.h" #include "trace.h" @@ -253,12 +252,6 @@ static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) { LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - if (s->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); - return; - } memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongson_ipi_iocsr_ops, @@ -273,13 +266,6 @@ static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) &loongson_ipi64_ops, s, "loongson_ipi64_iocsr", 0x118); sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); - - s->cpu = g_new0(IPICore, s->num_cpu); - for (i = 0; i < s->num_cpu; i++) { - s->cpu[i].ipi = s; - - qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); - } } static void loongson_ipi_common_unrealize(DeviceState *dev) From ce78dacf7e9b22dcc121dca17b1a3bcd93751680 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:15 +0800 Subject: [PATCH 1005/2892] hw/intc/loongson_ipi: Remove property num_cpu from loongson_ipi_common With mips64 loongson ipi, num_cpu property is used. With loongarch ipi, num_cpu can be acquired from possible_cpu_arch_ids. Here remove property num_cpu from loongson_ipi_common, and put it into loongson and loongarch ipi separately. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 6 ++++++ hw/intc/loongson_ipi.c | 6 ++++++ hw/intc/loongson_ipi_common.c | 6 ------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index e6126e4fbc..9c7636c4d6 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -9,6 +9,7 @@ #include "hw/boards.h" #include "qapi/error.h" #include "hw/intc/loongarch_ipi.h" +#include "hw/qdev-properties.h" #include "target/loongarch/cpu.h" static AddressSpace *get_iocsr_as(CPUState *cpu) @@ -75,6 +76,10 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) } } +static const Property loongarch_ipi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), +}; + static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); @@ -83,6 +88,7 @@ static void loongarch_ipi_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, loongarch_ipi_realize, &lic->parent_realize); + device_class_set_props(dc, loongarch_ipi_properties); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; } diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 1ed39b90ea..29e92d48fd 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "hw/intc/loongson_ipi.h" +#include "hw/qdev-properties.h" #include "qapi/error.h" #include "target/mips/cpu.h" @@ -75,6 +76,10 @@ static void loongson_ipi_unrealize(DeviceState *dev) k->parent_unrealize(dev); } +static const Property loongson_ipi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), +}; + static void loongson_ipi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -85,6 +90,7 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) &lic->parent_realize); device_class_set_parent_unrealize(dc, loongson_ipi_unrealize, &lic->parent_unrealize); + device_class_set_props(dc, loongson_ipi_properties); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = cpu_by_arch_id; } diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index 5d46679ea1..363cddc54c 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -9,7 +9,6 @@ #include "hw/sysbus.h" #include "hw/intc/loongson_ipi_common.h" #include "hw/irq.h" -#include "hw/qdev-properties.h" #include "qemu/log.h" #include "migration/vmstate.h" #include "trace.h" @@ -301,10 +300,6 @@ static const VMStateDescription vmstate_loongson_ipi_common = { } }; -static const Property ipi_common_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), -}; - static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -314,7 +309,6 @@ static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) &licc->parent_realize); device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize, &licc->parent_unrealize); - device_class_set_props(dc, ipi_common_properties); dc->vmsd = &vmstate_loongson_ipi_common; } From 14dc02b56a3d4434401ad92415cbec3e30ff3fa5 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:16 +0800 Subject: [PATCH 1006/2892] hw/intc/loongarch_ipi: Get cpu number from possible_cpu_arch_ids Supported CPU number can be acquired from function possible_cpu_arch_ids(), cpu-num property is not necessary and can be removed. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 13 ++++++++----- include/hw/intc/loongson_ipi_common.h | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 9c7636c4d6..49b4595d90 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -55,6 +55,9 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) { LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev); LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev); + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *id_list; Error *local_err = NULL; int i; @@ -64,13 +67,13 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) return; } - if (lics->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); - return; - } - + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + lics->num_cpu = id_list->len; lics->cpu = g_new0(IPICore, lics->num_cpu); for (i = 0; i < lics->num_cpu; i++) { + lics->cpu[i].arch_id = id_list->cpus[i].arch_id; + lics->cpu[i].cpu = CPU(id_list->cpus[i].cpu); lics->cpu[i].ipi = lics; qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1); } diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index df9d9c5168..4192f3d548 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -27,6 +27,8 @@ typedef struct IPICore { /* 64bit buf divide into 2 32-bit buf */ uint32_t buf[IPI_MBX_NUM * 2]; qemu_irq irq; + uint64_t arch_id; + CPUState *cpu; } IPICore; struct LoongsonIPICommonState { From 1b3aa347044966a69e16a821eb44fbc16d0d58c9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:17 +0800 Subject: [PATCH 1007/2892] hw/intc/loongarch_ipi: Remove property num-cpu Since cpu number can be acquired from possible_cpu_arch_ids(), num-cpu property is not necessary. Here remove num-cpu property for object TYPE_LOONGARCH_IPI object. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 5 ----- hw/loongarch/virt.c | 1 - 2 files changed, 6 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 49b4595d90..41d9625dcb 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -79,10 +79,6 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) } } -static const Property loongarch_ipi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), -}; - static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); @@ -91,7 +87,6 @@ static void loongarch_ipi_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, loongarch_ipi_realize, &lic->parent_realize); - device_class_set_props(dc, loongarch_ipi_properties); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index db37ed6a71..63fa0f4e32 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -899,7 +899,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Create IPI device */ ipi = qdev_new(TYPE_LOONGARCH_IPI); - qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); /* IPI iocsr memory region */ From 999b112d90be8404547eec0793f8d7c0b5d2a547 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:18 +0800 Subject: [PATCH 1008/2892] hw/intc/loongson_ipi: Add more input parameter for cpu_by_arch_id Add logic cpu index input parameter for function cpu_by_arch_id, CPUState::cpu_index is logic cpu slot index for possible_cpus. At the same time it is logic index with LoongsonIPICommonState::IPICore, here hide access for CPUState::cpu_index directly, it comes from function cpu_by_arch_id(). Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 19 +++++++++++++++---- hw/intc/loongson_ipi.c | 23 ++++++++++++++++++++++- hw/intc/loongson_ipi_common.c | 21 ++++++++++++--------- include/hw/intc/loongson_ipi_common.h | 3 ++- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 41d9625dcb..515549e8a5 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -38,17 +38,28 @@ static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) return found_cpu; } -static CPUState *loongarch_cpu_by_arch_id(int64_t arch_id) +static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics, + int64_t arch_id, int *index, CPUState **pcs) { MachineState *machine = MACHINE(qdev_get_machine()); CPUArchId *archid; + CPUState *cs; archid = find_cpu_by_archid(machine, arch_id); - if (archid) { - return CPU(archid->cpu); + if (archid && archid->cpu) { + cs = archid->cpu; + if (index) { + *index = cs->cpu_index; + } + + if (pcs) { + *pcs = cs; + } + + return MEMTX_OK; } - return NULL; + return MEMTX_ERROR; } static void loongarch_ipi_realize(DeviceState *dev, Error **errp) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 29e92d48fd..d2268a27f8 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -20,6 +20,27 @@ static AddressSpace *get_iocsr_as(CPUState *cpu) return NULL; } +static int loongson_cpu_by_arch_id(LoongsonIPICommonState *lics, + int64_t arch_id, int *index, CPUState **pcs) +{ + CPUState *cs; + + cs = cpu_by_arch_id(arch_id); + if (cs == NULL) { + return MEMTX_ERROR; + } + + if (index) { + *index = cs->cpu_index; + } + + if (pcs) { + *pcs = cs; + } + + return MEMTX_OK; +} + static const MemoryRegionOps loongson_ipi_core_ops = { .read_with_attrs = loongson_ipi_core_readl, .write_with_attrs = loongson_ipi_core_writel, @@ -92,7 +113,7 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) &lic->parent_unrealize); device_class_set_props(dc, loongson_ipi_properties); licc->get_iocsr_as = get_iocsr_as; - licc->cpu_by_arch_id = cpu_by_arch_id; + licc->cpu_by_arch_id = loongson_cpu_by_arch_id; } static const TypeInfo loongson_ipi_types[] = { diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index 363cddc54c..f5ab5024c0 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -103,16 +103,17 @@ static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint32_t cpuid; hwaddr addr; CPUState *cs; + int cpu, ret; cpuid = extract32(val, 16, 10); - cs = licc->cpu_by_arch_id(cpuid); - if (cs == NULL) { + ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); + if (ret != MEMTX_OK) { return MEMTX_DECODE_ERROR; } /* override requester_id */ addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); - attrs.requester_id = cs->cpu_index; + attrs.requester_id = cpu; return send_ipi_data(ipi, cs, val, addr, attrs); } @@ -123,16 +124,17 @@ static MemTxResult any_send(LoongsonIPICommonState *ipi, uint32_t cpuid; hwaddr addr; CPUState *cs; + int cpu, ret; cpuid = extract32(val, 16, 10); - cs = licc->cpu_by_arch_id(cpuid); - if (cs == NULL) { + ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); + if (ret != MEMTX_OK) { return MEMTX_DECODE_ERROR; } /* override requester_id */ addr = val & 0xffff; - attrs.requester_id = cs->cpu_index; + attrs.requester_id = cpu; return send_ipi_data(ipi, cs, val, addr, attrs); } @@ -146,6 +148,7 @@ MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, uint32_t cpuid; uint8_t vector; CPUState *cs; + int cpu, ret; addr &= 0xff; trace_loongson_ipi_write(size, (uint64_t)addr, val); @@ -176,11 +179,11 @@ MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, cpuid = extract32(val, 16, 10); /* IPI status vector */ vector = extract8(val, 0, 5); - cs = licc->cpu_by_arch_id(cpuid); - if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { + ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); + if (ret != MEMTX_OK || cpu >= ipi->num_cpu) { return MEMTX_DECODE_ERROR; } - loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF, + loongson_ipi_core_writel(&ipi->cpu[cpu], CORE_SET_OFF, BIT(vector), 4, attrs); break; default: diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index 4192f3d548..b587f9c571 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -46,7 +46,8 @@ struct LoongsonIPICommonClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; AddressSpace *(*get_iocsr_as)(CPUState *cpu); - CPUState *(*cpu_by_arch_id)(int64_t id); + int (*cpu_by_arch_id)(LoongsonIPICommonState *lics, int64_t id, + int *index, CPUState **pcs); }; MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, From bb81f237401b5f89f6bba21d9d4f50e0073372a6 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 7 Jan 2025 11:08:19 +0800 Subject: [PATCH 1009/2892] hw/intc/loongarch_ipi: Use alternative implemation for cpu_by_arch_id There is arch_id and CPUState pointer in IPICore object. With function cpu_by_arch_id() it can be implemented by parsing IPICore array inside, rather than possible_cpus array. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 515549e8a5..5376f1e084 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -17,43 +17,29 @@ static AddressSpace *get_iocsr_as(CPUState *cpu) return LOONGARCH_CPU(cpu)->env.address_space_iocsr; } -static int archid_cmp(const void *a, const void *b) +static int loongarch_ipi_cmp(const void *a, const void *b) { - CPUArchId *archid_a = (CPUArchId *)a; - CPUArchId *archid_b = (CPUArchId *)b; + IPICore *ipi_a = (IPICore *)a; + IPICore *ipi_b = (IPICore *)b; - return archid_a->arch_id - archid_b->arch_id; -} - -static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) -{ - CPUArchId apic_id, *found_cpu; - - apic_id.arch_id = id; - found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, - ms->possible_cpus->len, - sizeof(*ms->possible_cpus->cpus), - archid_cmp); - - return found_cpu; + return ipi_a->arch_id - ipi_b->arch_id; } static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics, int64_t arch_id, int *index, CPUState **pcs) { - MachineState *machine = MACHINE(qdev_get_machine()); - CPUArchId *archid; - CPUState *cs; + IPICore ipi, *found; - archid = find_cpu_by_archid(machine, arch_id); - if (archid && archid->cpu) { - cs = archid->cpu; + ipi.arch_id = arch_id; + found = bsearch(&ipi, lics->cpu, lics->num_cpu, sizeof(IPICore), + loongarch_ipi_cmp); + if (found && found->cpu) { if (index) { - *index = cs->cpu_index; + *index = found - lics->cpu; } if (pcs) { - *pcs = cs; + *pcs = found->cpu; } return MEMTX_OK; From e6cdeee95990a2c6f5d6873d3afb3c90518aed5c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 10:35:30 +0100 Subject: [PATCH 1010/2892] hw/xen: Add xs_node_read() helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This returns the full contents of the node, having created the node path from the printf-style format string provided in its arguments. This will save various callers from having to do so for themselves (and from using xs_node_scanf() with the non-portable %ms format string. Signed-off-by: David Woodhouse [remove double newline and constify trace parameters] Signed-off-by: Roger Pau Monné Reviewed-by: Anthony PERARD --- hw/xen/trace-events | 1 + hw/xen/xen-bus-helper.c | 22 ++++++++++++++++++++++ include/hw/xen/xen-bus-helper.h | 9 +++++++++ 3 files changed, 32 insertions(+) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index a07fe41c6d..461dee7b23 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -39,6 +39,7 @@ xs_node_create(const char *node) "%s" xs_node_destroy(const char *node) "%s" xs_node_vprintf(char *path, char *value) "%s %s" xs_node_vscanf(char *path, char *value) "%s %s" +xs_node_read(const char *path, const char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c index b2b2cc9c5d..22fd2f6c1a 100644 --- a/hw/xen/xen-bus-helper.c +++ b/hw/xen/xen-bus-helper.c @@ -142,6 +142,28 @@ int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, return rc; } +char *xs_node_read(struct qemu_xs_handle *h, xs_transaction_t tid, + unsigned int *len, Error **errp, + const char *path_fmt, ...) +{ + char *path, *value; + va_list ap; + + va_start(ap, path_fmt); + path = g_strdup_vprintf(path_fmt, ap); + va_end(ap); + + value = qemu_xen_xs_read(h, tid, path, len); + trace_xs_node_read(path, value); + if (!value) { + error_setg_errno(errp, errno, "failed to read from '%s'", path); + } + + g_free(path); + + return value; +} + struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, const char *key, xs_watch_fn fn, void *opaque, Error **errp) diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h index d8dcc2f010..e9911115b3 100644 --- a/include/hw/xen/xen-bus-helper.h +++ b/include/hw/xen/xen-bus-helper.h @@ -38,6 +38,15 @@ int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *fmt, ...) G_GNUC_SCANF(6, 7); +/* + * Unlike other functions here, the printf-formatted path_fmt is for + * the XenStore path, not the contents of the node. + */ +char *xs_node_read(struct qemu_xs_handle *h, xs_transaction_t tid, + unsigned int *len, Error **errp, + const char *path_fmt, ...) + G_GNUC_PRINTF(5, 6); + /* Watch node/key unless node is empty, in which case watch key */ struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, const char *key, xs_watch_fn fn, From 7a0b74d8716836f1206c5dfd778984c5d6eee46b Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Fri, 10 Jan 2025 10:35:31 +0100 Subject: [PATCH 1011/2892] xen: do not use '%ms' scanf specifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'm' parameter used to request auto-allocation of the destination variable is not supported on FreeBSD, and as such leads to failures to parse. What's more, the current usage of '%ms' with xs_node_scanf() is pointless, as it just leads to a double allocation of the same string. Instead use xs_node_read() to read the whole xenstore node. Fixes: a783f8ad4ec9 ('xen: add a mechanism to automatically create XenDevice-s...') Fixes: 9b7737469080 ('hw/xen: update Xen console to XenDevice model') Signed-off-by: Roger Pau Monné Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD --- hw/block/xen-block.c | 3 ++- hw/char/xen_console.c | 6 ++++-- hw/xen/xen-bus.c | 14 ++++++++++++-- include/hw/xen/xen-bus.h | 1 + 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 306d38927c..034a18b70e 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -239,7 +239,8 @@ static void xen_block_connect(XenDevice *xendev, Error **errp) return; } - if (xen_device_frontend_scanf(xendev, "protocol", "%ms", &str) != 1) { + str = xen_device_frontend_read(xendev, "protocol"); + if (!str) { /* x86 defaults to the 32-bit protocol even for 64-bit guests. */ if (object_dynamic_cast(OBJECT(qdev_get_machine()), "x86-machine")) { protocol = BLKIF_PROTOCOL_X86_32; diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index ef0c2912ef..cb39b21504 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -550,7 +550,8 @@ static void xen_console_device_create(XenBackendInstance *backend, goto fail; } - if (xs_node_scanf(xsh, XBT_NULL, fe, "type", errp, "%ms", &type) != 1) { + type = xs_node_read(xsh, XBT_NULL, NULL, errp, "%s/%s", fe, "type"); + if (!type) { error_prepend(errp, "failed to read console device type: "); goto fail; } @@ -568,7 +569,8 @@ static void xen_console_device_create(XenBackendInstance *backend, snprintf(label, sizeof(label), "xencons%ld", number); - if (xs_node_scanf(xsh, XBT_NULL, fe, "output", NULL, "%ms", &output) == 1) { + output = xs_node_read(xsh, XBT_NULL, NULL, NULL, "%s/%s", fe, "output"); + if (output) { /* * FIXME: sure we want to support implicit * muxed monitors here? diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index adfc4efad0..feeb612681 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -156,8 +156,8 @@ again: !strcmp(key[i], "hotplug-status")) continue; - if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", - &val) == 1) { + val = xs_node_read(xenbus->xsh, tid, NULL, NULL, "%s/%s", path, key[i]); + if (val) { qdict_put_str(opts, key[i], val); free(val); } @@ -650,6 +650,16 @@ int xen_device_frontend_scanf(XenDevice *xendev, const char *key, return rc; } +char *xen_device_frontend_read(XenDevice *xendev, const char *key) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + + g_assert(xenbus->xsh); + + return xs_node_read(xenbus->xsh, XBT_NULL, NULL, NULL, "%s/%s", + xendev->frontend_path, key); +} + static void xen_device_frontend_set_state(XenDevice *xendev, enum xenbus_state state, bool publish) diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 38d40afa37..2adb2af839 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -91,6 +91,7 @@ void xen_device_frontend_printf(XenDevice *xendev, const char *key, int xen_device_frontend_scanf(XenDevice *xendev, const char *key, const char *fmt, ...) G_GNUC_SCANF(3, 4); +char *xen_device_frontend_read(XenDevice *xendev, const char *key); void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, Error **errp); From 76f26e46ac577421e148adcedabfa12d4200ff3e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:37:48 +0000 Subject: [PATCH 1012/2892] hw/xen: Use xs_node_read() from xs_node_vscanf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce some duplication. Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/xen/trace-events | 1 - hw/xen/xen-bus-helper.c | 15 ++++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 461dee7b23..b67942d07b 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -38,7 +38,6 @@ xen_device_remove_watch(const char *type, char *name, const char *node, const ch xs_node_create(const char *node) "%s" xs_node_destroy(const char *node) "%s" xs_node_vprintf(char *path, char *value) "%s %s" -xs_node_vscanf(char *path, char *value) "%s %s" xs_node_read(const char *path, const char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c index 22fd2f6c1a..288fad422b 100644 --- a/hw/xen/xen-bus-helper.c +++ b/hw/xen/xen-bus-helper.c @@ -105,25 +105,22 @@ int xs_node_vscanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, va_list ap) { - char *path, *value; + char *value; int rc; - path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : - g_strdup(key); - value = qemu_xen_xs_read(h, tid, path, NULL); - - trace_xs_node_vscanf(path, value); + if (node && strlen(node) != 0) { + value = xs_node_read(h, tid, NULL, errp, "%s/%s", node, key); + } else { + value = xs_node_read(h, tid, NULL, errp, "%s", key); + } if (value) { rc = vsscanf(value, fmt, ap); } else { - error_setg_errno(errp, errno, "failed to read from '%s'", - path); rc = EOF; } free(value); - g_free(path); return rc; } From b34729aca2f18a7c277bd2903b932add21cf7796 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:07:16 +0000 Subject: [PATCH 1013/2892] hw/xen: Use xs_node_read() from xen_console_get_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that xs_node_read() can construct a node path, no need to open-code it. Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/char/xen_console.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index cb39b21504..e61902461b 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -367,28 +367,28 @@ static char *xen_console_get_name(XenDevice *xendev, Error **errp) if (con->dev == -1) { XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - char fe_path[XENSTORE_ABS_PATH_MAX + 1]; int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + Error *local_err = NULL; char *value; /* Theoretically we could go up to INT_MAX here but that's overkill */ while (idx < 100) { if (!idx) { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/console", xendev->frontend_id); + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/console", + xendev->frontend_id); } else { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/device/console/%u", - xendev->frontend_id, idx); + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/device/console/%u", + xendev->frontend_id, idx); } - value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); if (!value) { if (errno == ENOENT) { con->dev = idx; + error_free(local_err); goto found; } - error_setg(errp, "cannot read %s: %s", fe_path, - strerror(errno)); + error_propagate(errp, local_err); return NULL; } free(value); From e4e113ecc2d2e4d1bc9d3a70bf01bba4b86f845c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:12:36 +0000 Subject: [PATCH 1014/2892] hw/xen: Use xs_node_read() from xen_netdev_get_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that xs_node_read() can construct a node path, no need to open-code it. Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/net/xen_nic.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 97ebd9fa30..5410039490 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -510,23 +510,22 @@ static char *xen_netdev_get_name(XenDevice *xendev, Error **errp) if (netdev->dev == -1) { XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - char fe_path[XENSTORE_ABS_PATH_MAX + 1]; int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + Error *local_err = NULL; char *value; /* Theoretically we could go up to INT_MAX here but that's overkill */ while (idx < 100) { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/device/vif/%u", - xendev->frontend_id, idx); - value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/device/vif/%u", + xendev->frontend_id, idx); if (!value) { if (errno == ENOENT) { netdev->dev = idx; + error_free(local_err); goto found; } - error_setg(errp, "cannot read %s: %s", fe_path, - strerror(errno)); + error_propagate(errp, local_err); return NULL; } free(value); From cd414c3f566fdbd98778c1c9a497428a80cd7fdd Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:18:13 +0000 Subject: [PATCH 1015/2892] hw/xen: Use xs_node_read() from xenstore_read_str() instead of open-coding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/xen/xen_pvdev.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index c5ad71e8dc..c9143ba259 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -22,6 +22,7 @@ #include "qemu/main-loop.h" #include "hw/qdev-core.h" #include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus-helper.h" #include "hw/xen/xen_pvdev.h" /* private */ @@ -81,12 +82,9 @@ int xenstore_write_str(const char *base, const char *node, const char *val) char *xenstore_read_str(const char *base, const char *node) { - char abspath[XEN_BUFSIZE]; - unsigned int len; char *str, *ret = NULL; - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - str = qemu_xen_xs_read(xenstore, 0, abspath, &len); + str = xs_node_read(xenstore, 0, NULL, NULL, "%s/%s", base, node); if (str != NULL) { /* move to qemu-allocated memory to make sure * callers can safely g_free() stuff. */ From 8b44a3e39f36540818d99ef8cf79e64bba1ed9c3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 15 Jan 2025 15:46:06 +0000 Subject: [PATCH 1016/2892] hw/xen: Fix errp handling in xen_console When attempting to read the 'output' node, interpret any error *other* than ENOENT as a fatal error. For ENOENT, fall back to serial_hd() to find a character device, or create a null device. Do not attempt to prepend to errp when serial_hd() fails; the error isn't relevant (and prior to this change, wasn't set anyway). Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD --- hw/char/xen_console.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index e61902461b..d03c188d1d 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -569,7 +569,7 @@ static void xen_console_device_create(XenBackendInstance *backend, snprintf(label, sizeof(label), "xencons%ld", number); - output = xs_node_read(xsh, XBT_NULL, NULL, NULL, "%s/%s", fe, "output"); + output = xs_node_read(xsh, XBT_NULL, NULL, errp, "%s/%s", fe, "output"); if (output) { /* * FIXME: sure we want to support implicit @@ -581,19 +581,27 @@ static void xen_console_device_create(XenBackendInstance *backend, output); goto fail; } - } else if (number) { - cd = serial_hd(number); - if (!cd) { - error_prepend(errp, "console: No serial device #%ld found: ", - number); - goto fail; - } + } else if (errno != ENOENT) { + error_prepend(errp, "console: No valid chardev found: "); + goto fail; } else { - /* No 'output' node on primary console: use null. */ - cd = qemu_chr_new(label, "null", NULL); - if (!cd) { - error_setg(errp, "console: failed to create null device"); - goto fail; + error_free(*errp); + *errp = NULL; + + if (number) { + cd = serial_hd(number); + if (!cd) { + error_setg(errp, "console: No serial device #%ld found", + number); + goto fail; + } + } else { + /* No 'output' node on primary console: use null. */ + cd = qemu_chr_new(label, "null", NULL); + if (!cd) { + error_setg(errp, "console: failed to create null device"); + goto fail; + } } } From e7bc0204e57836b3df611b73d2decc56ed698c4a Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Sun, 12 Jan 2025 22:26:09 +0100 Subject: [PATCH 1017/2892] system/runstate: Fix regression, clarify BQL status of exit notifiers By changing the way the main QEMU event loop is invoked, I inadvertently changed the BQL status of exit notifiers: some of them implicitly assumed they would be called with the BQL held; the BQL is however not held during the exit(status) call in qemu_default_main(). Instead of attempting to ensuring we always call exit() from the BQL - including any transitive calls - this change adds a BQL lock guard to qemu_run_exit_notifiers, ensuring the BQL will always be held in the exit notifiers. Additionally, the BQL promise is now documented at the qemu_{add,remove}_exit_notifier() declarations. Fixes: f5ab12caba4f ("ui & main loop: Redesign of system-specific main thread event handling") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2771 Reported-by: David Woodhouse Signed-off-by: Phil Dennis-Jordan Tested-by: David Woodhouse Signed-off-by: David Woodhouse --- include/system/system.h | 1 + system/runstate.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/system/system.h b/include/system/system.h index 5364ad4f27..0cbb43ec30 100644 --- a/include/system/system.h +++ b/include/system/system.h @@ -15,6 +15,7 @@ extern bool qemu_uuid_set; const char *qemu_get_vm_name(void); +/* Exit notifiers will run with BQL held. */ void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); diff --git a/system/runstate.c b/system/runstate.c index 3a8fe866bc..272801d307 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -850,6 +850,7 @@ void qemu_remove_exit_notifier(Notifier *notify) static void qemu_run_exit_notifiers(void) { + BQL_LOCK_GUARD(); notifier_list_notify(&exit_notifiers, NULL); } From 1e77a4a32f8b7b6699a2f8b1f98e8fada902ba1f Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Mon, 7 Oct 2024 09:00:11 +0200 Subject: [PATCH 1018/2892] virtio-gpu: Add definition for resource_uuid feature Add the VIRTIO_GPU_F_RESOURCE_UUID feature to enable the assignment of resources UUIDs for export to other virtio devices. Signed-off-by: Dorinda Bassey Message-Id: <20241007070013.3350752-1-dbassey@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/display/vhost-user-gpu.c | 8 ++++++++ hw/display/virtio-gpu-base.c | 3 +++ include/hw/virtio/virtio-gpu.h | 3 +++ 3 files changed, 14 insertions(+) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 12d5c37ee5..2aed6243f6 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -631,6 +631,14 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) error_report("EDID requested but the backend doesn't support it."); g->parent_obj.conf.flags &= ~(1 << VIRTIO_GPU_FLAG_EDID_ENABLED); } + if (virtio_has_feature(g->vhost->dev.features, + VIRTIO_GPU_F_RESOURCE_UUID)) { + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED; + } + if (virtio_has_feature(g->vhost->dev.features, + VIRTIO_GPU_F_RESOURCE_UUID)) { + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED; + } if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) { return; diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 4fc7ef8896..7827536ac4 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -235,6 +235,9 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features, if (virtio_gpu_context_init_enabled(g->conf)) { features |= (1 << VIRTIO_GPU_F_CONTEXT_INIT); } + if (virtio_gpu_resource_uuid_enabled(g->conf)) { + features |= (1 << VIRTIO_GPU_F_RESOURCE_UUID); + } return features; } diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index bd93672185..a42957c4e2 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -98,6 +98,7 @@ enum virtio_gpu_base_conf_flags { VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED, VIRTIO_GPU_FLAG_RUTABAGA_ENABLED, VIRTIO_GPU_FLAG_VENUS_ENABLED, + VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED, }; #define virtio_gpu_virgl_enabled(_cfg) \ @@ -114,6 +115,8 @@ enum virtio_gpu_base_conf_flags { (_cfg.flags & (1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED)) #define virtio_gpu_rutabaga_enabled(_cfg) \ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED)) +#define virtio_gpu_resource_uuid_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED)) #define virtio_gpu_hostmem_enabled(_cfg) \ (_cfg.hostmem > 0) #define virtio_gpu_venus_enabled(_cfg) \ From 694632fd44987cc4618612a38ad151047524a590 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 3 Dec 2024 13:19:28 +0100 Subject: [PATCH 1019/2892] pci: ensure valid link status bits for downstream ports PCI hotplug for downstream endpoints on arm fails because Linux' PCIe hotplug driver doesn't like the QEMU provided LNKSTA: pcieport 0000:08:01.0: pciehp: Slot(2): Card present pcieport 0000:08:01.0: pciehp: Slot(2): Link Up pcieport 0000:08:01.0: pciehp: Slot(2): Cannot train link: status 0x2000 There's 2 cases where LNKSTA isn't setup properly: * the downstream device has no express capability * max link width of the bridge is 0 Move the sanity checks added via 88c869198aa63 ("pci: Sanity test minimum downstream LNKSTA") outside of the branch to make sure downstream ports always have a valid LNKSTA. Signed-off-by: Sebastian Ott Tested-by: Zhenyu Zhang Message-Id: <20241203121928.14861-1-sebott@redhat.com> Reviewed-by: Alex Williamson Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 0b455c8654..1b12db6fa2 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1113,18 +1113,22 @@ void pcie_sync_bridge_lnk(PCIDevice *bridge_dev) if ((lnksta & PCI_EXP_LNKSTA_NLW) > (lnkcap & PCI_EXP_LNKCAP_MLW)) { lnksta &= ~PCI_EXP_LNKSTA_NLW; lnksta |= lnkcap & PCI_EXP_LNKCAP_MLW; - } else if (!(lnksta & PCI_EXP_LNKSTA_NLW)) { - lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1); } if ((lnksta & PCI_EXP_LNKSTA_CLS) > (lnkcap & PCI_EXP_LNKCAP_SLS)) { lnksta &= ~PCI_EXP_LNKSTA_CLS; lnksta |= lnkcap & PCI_EXP_LNKCAP_SLS; - } else if (!(lnksta & PCI_EXP_LNKSTA_CLS)) { - lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT); } } + if (!(lnksta & PCI_EXP_LNKSTA_NLW)) { + lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1); + } + + if (!(lnksta & PCI_EXP_LNKSTA_CLS)) { + lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT); + } + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, lnksta & From e043be2290ffdd666ad2ab35d2341c137b21a3b4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 10 Dec 2024 17:39:43 +0100 Subject: [PATCH 1020/2892] tests: acpi: whitelist expected blobs Signed-off-by: Igor Mammedov Message-Id: <20241210163945.3422623-2-imammedo@redhat.com> Tested-by: Eric Mackay Acked-by: Ani Sinha Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..a1047913af 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,43 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/x86/pc/DSDT", +"tests/data/acpi/x86/pc/DSDT.acpierst", +"tests/data/acpi/x86/pc/DSDT.acpihmat", +"tests/data/acpi/x86/pc/DSDT.bridge", +"tests/data/acpi/x86/pc/DSDT.cphp", +"tests/data/acpi/x86/pc/DSDT.dimmpxm", +"tests/data/acpi/x86/pc/DSDT.hpbridge", +"tests/data/acpi/x86/pc/DSDT.hpbrroot", +"tests/data/acpi/x86/pc/DSDT.ipmikcs", +"tests/data/acpi/x86/pc/DSDT.memhp", +"tests/data/acpi/x86/pc/DSDT.nohpet", +"tests/data/acpi/x86/pc/DSDT.numamem", +"tests/data/acpi/x86/pc/DSDT.roothp", +"tests/data/acpi/x86/q35/DSDT", +"tests/data/acpi/x86/q35/DSDT.acpierst", +"tests/data/acpi/x86/q35/DSDT.acpihmat", +"tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x", +"tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator", +"tests/data/acpi/x86/q35/DSDT.applesmc", +"tests/data/acpi/x86/q35/DSDT.bridge", +"tests/data/acpi/x86/q35/DSDT.core-count", +"tests/data/acpi/x86/q35/DSDT.core-count2", +"tests/data/acpi/x86/q35/DSDT.cphp", +"tests/data/acpi/x86/q35/DSDT.cxl", +"tests/data/acpi/x86/q35/DSDT.dimmpxm", +"tests/data/acpi/x86/q35/DSDT.ipmibt", +"tests/data/acpi/x86/q35/DSDT.ipmismbus", +"tests/data/acpi/x86/q35/DSDT.ivrs", +"tests/data/acpi/x86/q35/DSDT.memhp", +"tests/data/acpi/x86/q35/DSDT.mmio64", +"tests/data/acpi/x86/q35/DSDT.multi-bridge", +"tests/data/acpi/x86/q35/DSDT.noacpihp", +"tests/data/acpi/x86/q35/DSDT.nohpet", +"tests/data/acpi/x86/q35/DSDT.numamem", +"tests/data/acpi/x86/q35/DSDT.pvpanic-isa", +"tests/data/acpi/x86/q35/DSDT.thread-count", +"tests/data/acpi/x86/q35/DSDT.thread-count2", +"tests/data/acpi/x86/q35/DSDT.tis.tpm12", +"tests/data/acpi/x86/q35/DSDT.tis.tpm2", +"tests/data/acpi/x86/q35/DSDT.type4-count", +"tests/data/acpi/x86/q35/DSDT.viot", +"tests/data/acpi/x86/q35/DSDT.xapic", From 8aa35bebeeaed19ae57afbc3e110b8e7fe8587d0 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 10 Dec 2024 17:39:44 +0100 Subject: [PATCH 1021/2892] cpuhp: make sure that remove events are handled within the same SCI CPU_SCAN_METHOD was processing insert events first and only if insert event was not present then it would check remove event. Normally it's not an issue as it doesn't make much sense tho hotplug and immediately unplug it. In this corner case, which can be reproduced with: qemu -smp 1,maxcpus=2 -cpu host -monitor stdio \ -drive if=pflash,format=raw,readonly,file=edk2-x86_64-code.fd * boot till GRUB prompt and pause guest (either via monitor or stop GRUB from automatic boot) * at monitor prompt add CPU: device_add host-x86_64-cpu,socket-id=0,core-id=1,thread-id=0,id=foo * let guest OS boot completely, and unplug CPU from monitor prompt: device_del foo which triggers GPE event that leads to CPU_SCAN_METHOD on guest side as result of above cpu 'foo' will not be hotunplugged, since QEMU sees insert event and ignores remove event (leaving it in pending state) for the GPE event. Any follow up CPU hotplug/unplug action from QEMU side will handle previously ignored event, so as workaround user can repeat device_del. Fix this corner-case by queuing remove events independently from insert events, aka the same way as we do with insert events. And then go over remove queue to send eject notify events to OSPM within the same GPE event. PS: Process remove queue after the cpu add queue has been processed 1st to ensure that OSPM gets hotadd evets after hotremove ones. PS2: Case where it's still borken happens when guest OS is Linux and device_del happens before guest OS initializes ACPI subsystem. Culprit in this case though is the guest kernel, which mangles GPE.sts (by clearing them up) and thus pending SCI turns to NOP leaving insert/remove events in pending state. That is the guest bug and should be fixed there. Signed-off-by: Igor Mammedov Reported-by: Eric Mackay Message-Id: <20241210163945.3422623-3-imammedo@redhat.com> Tested-by: Eric Mackay Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/cpu.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 9d530a24da..f70a2c045e 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -327,6 +327,7 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_EJECT_METHOD "CEJ0" #define CPU_OST_METHOD "COST" #define CPU_ADDED_LIST "CNEW" +#define CPU_EJ_LIST "CEJL" #define CPU_ENABLED "CPEN" #define CPU_SELECTOR "CSEL" @@ -488,7 +489,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, method = aml_method(CPU_SCAN_METHOD, 0, AML_SERIALIZED); { const uint8_t max_cpus_per_pass = 255; - Aml *else_ctx; Aml *while_ctx, *while_ctx2; Aml *has_event = aml_local(0); Aml *dev_chk = aml_int(1); @@ -499,6 +499,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, Aml *uid = aml_local(3); Aml *has_job = aml_local(4); Aml *new_cpus = aml_name(CPU_ADDED_LIST); + Aml *ej_cpus = aml_name(CPU_EJ_LIST); + Aml *num_ej_cpus = aml_local(5); aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); @@ -513,6 +515,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, */ aml_append(method, aml_name_decl(CPU_ADDED_LIST, aml_package(max_cpus_per_pass))); + aml_append(method, aml_name_decl(CPU_EJ_LIST, + aml_package(max_cpus_per_pass))); aml_append(method, aml_store(zero, uid)); aml_append(method, aml_store(one, has_job)); @@ -527,6 +531,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(while_ctx2, aml_store(one, has_event)); aml_append(while_ctx2, aml_store(zero, num_added_cpus)); + aml_append(while_ctx2, aml_store(zero, num_ej_cpus)); /* * Scan CPUs, till there are CPUs with events or @@ -559,8 +564,10 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, * if CPU_ADDED_LIST is full, exit inner loop and process * collected CPUs */ - ifctx = aml_if( - aml_equal(num_added_cpus, aml_int(max_cpus_per_pass))); + ifctx = aml_if(aml_lor( + aml_equal(num_added_cpus, aml_int(max_cpus_per_pass)), + aml_equal(num_ej_cpus, aml_int(max_cpus_per_pass)) + )); { aml_append(ifctx, aml_store(one, has_job)); aml_append(ifctx, aml_break()); @@ -577,16 +584,16 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(ifctx, aml_store(one, has_event)); } aml_append(while_ctx, ifctx); - else_ctx = aml_else(); + ifctx = aml_if(aml_equal(rm_evt, one)); { - aml_append(ifctx, - aml_call2(CPU_NOTIFY_METHOD, uid, eject_req)); - aml_append(ifctx, aml_store(one, rm_evt)); + /* cache to be removed CPUs to Notify later */ + aml_append(ifctx, aml_store(uid, + aml_index(ej_cpus, num_ej_cpus))); + aml_append(ifctx, aml_increment(num_ej_cpus)); aml_append(ifctx, aml_store(one, has_event)); } - aml_append(else_ctx, ifctx); - aml_append(while_ctx, else_ctx); + aml_append(while_ctx, ifctx); aml_append(while_ctx, aml_increment(uid)); } aml_append(while_ctx2, while_ctx); @@ -620,6 +627,24 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(while_ctx, aml_increment(cpu_idx)); } aml_append(while_ctx2, while_ctx); + + /* + * Notify OSPM about to be removed CPUs and clear remove flag + */ + aml_append(while_ctx2, aml_store(zero, cpu_idx)); + while_ctx = aml_while(aml_lless(cpu_idx, num_ej_cpus)); + { + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(ej_cpus, cpu_idx)), + uid)); + aml_append(while_ctx, + aml_call2(CPU_NOTIFY_METHOD, uid, eject_req)); + aml_append(while_ctx, aml_store(uid, cpu_selector)); + aml_append(while_ctx, aml_store(one, rm_evt)); + aml_append(while_ctx, aml_increment(cpu_idx)); + } + aml_append(while_ctx2, while_ctx); + /* * If another batch is needed, then it will resume scanning * exactly at -- and not after -- the last CPU that's currently From 9ccb69df554a5204077cda101b7bcf0f19544553 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 10 Dec 2024 17:39:45 +0100 Subject: [PATCH 1022/2892] tests: acpi: update expected blobs previous patch has changed cpu hotplug AML, expected diff: @@ -2942,6 +2942,7 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) { Acquire (\_SB.PCI0.PRES.CPLK, 0xFFFF) Name (CNEW, Package (0xFF) {}) + Name (CEJL, Package (0xFF) {}) Local3 = Zero Local4 = One While ((Local4 == One)) @@ -2949,6 +2950,7 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) Local4 = Zero Local0 = One Local1 = Zero + Local5 = Zero While (((Local0 == One) && (Local3 < One))) { Local0 = Zero @@ -2959,7 +2961,7 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) Break } - If ((Local1 == 0xFF)) + If (((Local1 == 0xFF) || (Local5 == 0xFF))) { Local4 = One Break @@ -2972,10 +2974,11 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) Local1++ Local0 = One } - ElseIf ((\_SB.PCI0.PRES.CRMV == One)) + + If ((\_SB.PCI0.PRES.CRMV == One)) { - CTFY (Local3, 0x03) - \_SB.PCI0.PRES.CRMV = One + CEJL [Local5] = Local3 + Local5++ Local0 = One } @@ -2992,6 +2995,16 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) \_SB.PCI0.PRES.CINS = One Local2++ } + + Local2 = Zero + While ((Local2 < Local5)) + { + Local3 = DerefOf (CEJL [Local2]) + CTFY (Local3, 0x03) + \_SB.PCI0.PRES.CSEL = Local3 + \_SB.PCI0.PRES.CRMV = One + Local2++ + } } Release (\_SB.PCI0.PRES.CPLK) Signed-off-by: Igor Mammedov Message-Id: <20241210163945.3422623-4-imammedo@redhat.com> Tested-by: Eric Mackay Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/x86/pc/DSDT | Bin 8526 -> 8593 bytes tests/data/acpi/x86/pc/DSDT.acpierst | Bin 8437 -> 8504 bytes tests/data/acpi/x86/pc/DSDT.acpihmat | Bin 9851 -> 9918 bytes tests/data/acpi/x86/pc/DSDT.bridge | Bin 15397 -> 15464 bytes tests/data/acpi/x86/pc/DSDT.cphp | Bin 8990 -> 9057 bytes tests/data/acpi/x86/pc/DSDT.dimmpxm | Bin 10180 -> 10247 bytes tests/data/acpi/x86/pc/DSDT.hpbridge | Bin 8477 -> 8544 bytes tests/data/acpi/x86/pc/DSDT.hpbrroot | Bin 5033 -> 5100 bytes tests/data/acpi/x86/pc/DSDT.ipmikcs | Bin 8598 -> 8665 bytes tests/data/acpi/x86/pc/DSDT.memhp | Bin 9885 -> 9952 bytes tests/data/acpi/x86/pc/DSDT.nohpet | Bin 8384 -> 8451 bytes tests/data/acpi/x86/pc/DSDT.numamem | Bin 8532 -> 8599 bytes tests/data/acpi/x86/pc/DSDT.roothp | Bin 12319 -> 12386 bytes tests/data/acpi/x86/q35/DSDT | Bin 8355 -> 8422 bytes tests/data/acpi/x86/q35/DSDT.acpierst | Bin 8372 -> 8439 bytes tests/data/acpi/x86/q35/DSDT.acpihmat | Bin 9680 -> 9747 bytes .../data/acpi/x86/q35/DSDT.acpihmat-generic-x | Bin 12565 -> 12632 bytes .../acpi/x86/q35/DSDT.acpihmat-noinitiator | Bin 8634 -> 8701 bytes tests/data/acpi/x86/q35/DSDT.applesmc | Bin 8401 -> 8468 bytes tests/data/acpi/x86/q35/DSDT.bridge | Bin 11968 -> 12035 bytes tests/data/acpi/x86/q35/DSDT.core-count | Bin 12913 -> 12980 bytes tests/data/acpi/x86/q35/DSDT.core-count2 | Bin 33770 -> 33837 bytes tests/data/acpi/x86/q35/DSDT.cphp | Bin 8819 -> 8886 bytes tests/data/acpi/x86/q35/DSDT.cxl | Bin 13146 -> 13213 bytes tests/data/acpi/x86/q35/DSDT.dimmpxm | Bin 10009 -> 10076 bytes tests/data/acpi/x86/q35/DSDT.ipmibt | Bin 8430 -> 8497 bytes tests/data/acpi/x86/q35/DSDT.ipmismbus | Bin 8443 -> 8510 bytes tests/data/acpi/x86/q35/DSDT.ivrs | Bin 8372 -> 8439 bytes tests/data/acpi/x86/q35/DSDT.memhp | Bin 9714 -> 9781 bytes tests/data/acpi/x86/q35/DSDT.mmio64 | Bin 9485 -> 9552 bytes tests/data/acpi/x86/q35/DSDT.multi-bridge | Bin 13208 -> 13275 bytes tests/data/acpi/x86/q35/DSDT.noacpihp | Bin 8235 -> 8302 bytes tests/data/acpi/x86/q35/DSDT.nohpet | Bin 8213 -> 8280 bytes tests/data/acpi/x86/q35/DSDT.numamem | Bin 8361 -> 8428 bytes tests/data/acpi/x86/q35/DSDT.pvpanic-isa | Bin 8456 -> 8523 bytes tests/data/acpi/x86/q35/DSDT.thread-count | Bin 12913 -> 12980 bytes tests/data/acpi/x86/q35/DSDT.thread-count2 | Bin 33770 -> 33837 bytes tests/data/acpi/x86/q35/DSDT.tis.tpm12 | Bin 8961 -> 9028 bytes tests/data/acpi/x86/q35/DSDT.tis.tpm2 | Bin 8987 -> 9054 bytes tests/data/acpi/x86/q35/DSDT.type4-count | Bin 18589 -> 18656 bytes tests/data/acpi/x86/q35/DSDT.viot | Bin 14612 -> 14679 bytes tests/data/acpi/x86/q35/DSDT.xapic | Bin 35718 -> 35785 bytes tests/qtest/bios-tables-test-allowed-diff.h | 42 ------------------ 43 files changed, 42 deletions(-) diff --git a/tests/data/acpi/x86/pc/DSDT b/tests/data/acpi/x86/pc/DSDT index 8b8235fe79e2fa08a6f840c8479edb75f5a047b9..60d50b088a362556fd54395cb15364d6c0936be5 100644 GIT binary patch delta 191 zcmX@-G|`#MCDLNex$(#6mIrt_{PUQMO zIdw7aXmPleqb`EiiOy**qJduH&*yh{(#ex8Vk2-Y# delta 140 zcmV;70CWG5L(W19L{mgmP9XpQ0co)cxeNkNK9j`^umVjplXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5OWbzU?~#- diff --git a/tests/data/acpi/x86/pc/DSDT.acpierst b/tests/data/acpi/x86/pc/DSDT.acpierst index 06829b9c6c6d726d955dc7c99bc9f42448e22aeb..4c434c25c0b1602f22128e352781df498fa69ddf 100644 GIT binary patch delta 191 zcmezBxWkFdCDZ1Zh?DM0|M$vRg6 delta 140 zcmV;70CWGiLiIrkL{mgm^&kKM0kW|QxeNkNK9j`^umVjplXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5E>Em)hYD= diff --git a/tests/data/acpi/x86/pc/DSDT.acpihmat b/tests/data/acpi/x86/pc/DSDT.acpihmat index 2fe355ebdbb858fa9247d09112e21712e3eddc45..61b7d5caa55c44dbf69d649110c6b14bb4c3fdf5 100644 GIT binary patch delta 175 zcmezEv(J~yCDvQVJLo3K$X#7*ZGcb5GvO=f}Y}adINp|H-M7`S=}_7N|^) z(PxPdc8U*h_B03xat(G4@(p7wNbUfcmda3?I=PwOPo{t&X^|7l)TGpcX0SvOL$Y&- YTVyg9b3yWCM?O_%pr*~|`PYa60N6n`+5i9m delta 117 zcmV-*0E+*Z1Zh?FC75!n>zpi delta 140 zcmV;70CWH7c%^s>L{mgmB|HEC0e7(qxeNkNK9j`^umVjplXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5K1hi?{ulVG1 zd>qOg&aPfQLQMY)7?KMZQx-XhO-^AfU`Q!oOekPTEMQ1omGh zPPXTFU|ygydGbPjVIIbUkq;Fd oLsUjtV+sRs0bDNxUsFO~P(w*DP*O!xLsCsvld%s@vqcab6Skcv^Z)<= diff --git a/tests/data/acpi/x86/pc/DSDT.dimmpxm b/tests/data/acpi/x86/pc/DSDT.dimmpxm index 205219b99d903555125c4b07fc047c42993eb338..5b6471c8db9003b39bf5e20af34061f3e71cdbd5 100644 GIT binary patch delta 173 zcmX@&-yXo_66_MfuED^-7{8J0EDw`+$mE+m8<;#jH+Sg6NE z^uK^1xqvZck%QRe6vhIElmf>AO`El?~oSew@e{$+%D}D#11uBzc z^jYGAo#F$WJq-eaT!Wp1e8U(Ek~=`Ar81PJPF~ONCsn|Zw8)8NYEo)JGgu;tA=x>^ WEi##ldGbYmb!MQJ&A<6iiUR=B8#YG( delta 141 zcmZni{HU?q4@&S$uasY@xe~<0nVNV0YR?8 e&OyFmjLso$k;z=l1&mlEC$Hi6+w354TpR%WuPVd< diff --git a/tests/data/acpi/x86/pc/DSDT.hpbridge b/tests/data/acpi/x86/pc/DSDT.hpbridge index 8fa8b519ec65bd5099c45f4e1c85b11b47a23845..67fe28699fbb261cfc7a52b2291f9965ab93c6a8 100644 GIT binary patch delta 191 zcmbR1^uUSBCDZ1Zh?CqV$0Q#xb- delta 140 zcmV;70CWG~LY+biL{mgm9U%Y!0iCf5xeNkNK9j`^umVjplXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5JC}S!YKj( diff --git a/tests/data/acpi/x86/pc/DSDT.hpbrroot b/tests/data/acpi/x86/pc/DSDT.hpbrroot index 01719462a72fd6d40ce433dac601e4b94eae574c..077a4cc988dc417a1bc9317dddd2dbd96ff1ff50 100644 GIT binary patch delta 195 zcmZ3f{zje4CD9Bw8j=gAe^8<-p{CTsEhW%rbF4tDnAn0$dpT${t$ z&ox|#=|6|FtCtUuE?`J5U`$!$FFHAev4A0^fH9$fA+dlVb&((U2JCu{NiWp@*E4tDnAn0$dpT!F*c z&ox|#>3;!3asgw?A{U{_DU1aSDFuuP1q_Lce0U}o@cJ@yPEMSBl-I#@q4@&S$uasY u@xe~<0nVNV0YR?8&OyFmjLso$k;z=l1&mlEC#&=MO%~)8+kBBvniT*|~5 diff --git a/tests/data/acpi/x86/pc/DSDT.ipmikcs b/tests/data/acpi/x86/pc/DSDT.ipmikcs index 0ca664688b16baa3a06b8440181de4f17511c6b0..9b2e81a7bcefb5c0e2dfbd2bbc5b6ea501f86306 100644 GIT binary patch delta 191 zcmbQ{eAAiBCD*>_hQtDf)J1;WlQ;4Ca_~)@oXGWm za_VFzeg~xmDwAXMS>l78;scyL4FZB(gPntX!x#&aJ3ywTGL)uHF6Z}?Dqu)jJ>>T12nassJc_zO)D^N>m(&RWkvCX&ncMAdl7dShT delta 140 zcmV;70CWG@LzY7dL{mgmmLUKD0p+m@xeNkNK9j`^umVjplXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5W5k-i7GGv diff --git a/tests/data/acpi/x86/pc/DSDT.memhp b/tests/data/acpi/x86/pc/DSDT.memhp index 03ff464ba4e72082fce0921815cfc09ca20b561a..9c66ccf150af1622d1b788a1ae04a6e5136cff9e 100644 GIT binary patch delta 191 zcmbR1`@omWCDLNex$(#6mIrt_{PUQMO zIdw7aXmPleqb`EiiOy**qJduH&*yh{(hr|HPnL985 delta 140 zcmV;70CWG~O`S~&L{mgmohASP0b8*OxeNkDKa<4_umVFglXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5W*IV{wdA? diff --git a/tests/data/acpi/x86/pc/DSDT.nohpet b/tests/data/acpi/x86/pc/DSDT.nohpet index b081030f0ed171e52b13e28cfdc8770a04c2806e..28dbd8d8949d1421da9312cf0440d7ae3b64916e 100644 GIT binary patch delta 195 zcmX@$*zCmR66_MftjNH?$gz=2n}^BCd9pdr1||oK$uD{SvU^H72Rr+5Oz!6u*XD5c za}5__`p@C)>g5BZ3mB3M7*iJci%w2qEMQ0}U`!}rNGxDTUF63-S%uG+gKy&GM6Umn zQzw`3IVdeqnH;0f5+CdoAK>h15D?@V>>T79##oTt0WvL>p)_^!OFloT0*0hTPApTC qQVW{F5=jin&LM7*$z05n_4vdl@8K0>1!^lzntXv*Y_l(au^<4GLppx| delta 157 zcmZp6I^f9V66_LkK!JgQ(Rd@5HV>1(-DGo~4NShqlV9@uWp@*E4tDnAnB31RuE62! z=Nc}=^uK^1xqvZck&Dpe6vhIElmf*>_hQtDf)J1;WlQ;4Ca_~)@oXGWm za_VFzeg~xmDwAXMS>l78;scyL4FZB(gPntX!x#&aJ3ywTGL)uHF6Z}?Dqu)jJ>>T12nassJc_zO)D^N>m(&RWkvCX&nD+K}fZ963Z delta 140 zcmV;70CWGBL)1bFL{mgmR3QKW0TZzbxeNkDKa<4_umVFglXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5P1LNex$(#6mIrt_{PUQMO zIdw7aXmPleqb`EiiOy**qJduH&*yh{(E(!n-m^&T- delta 140 zcmV;70CWH1V4q+LL{mgmA20v_0rjy8xeNkNK9j`^umVjplXngM2Sye{Q$tP&lN}Bh zAP7TFMOP96|8M|fZ~$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Imhz4;7Pi4^ER_4i>W>5JVulO)0zp diff --git a/tests/data/acpi/x86/q35/DSDT b/tests/data/acpi/x86/q35/DSDT index fb89ae0ac6d4346e33156e9e4d3718698a0a1a8e..51ad37a351bffae8fbc9ba17f72c25ef61822f59 100644 GIT binary patch delta 180 zcmZ4N_{@>ZCDUq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRveH7+b|#D diff --git a/tests/data/acpi/x86/q35/DSDT.acpierst b/tests/data/acpi/x86/q35/DSDT.acpierst index 46fd25400b7c00ee9149ddb64cb5d5bd73f6a82b..dbd4f858354df0f4c050fd0b914581154f340ee8 100644 GIT binary patch delta 180 zcmdnu_}!7qCD*Tg0oSn#2N?oSK9ds}umVjplf@bR0!9{-#2O?RZ~$X) z0c4^?5|d;BZ~$a*0bpfHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRve)OE4?7a diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat b/tests/data/acpi/x86/q35/DSDT.acpihmat index 61c5bd52a42242e85090934e8e45bf01642609d6..952752e30e9dfc9e2085e8fceaa0740dda6db89c 100644 GIT binary patch delta 159 zcmccMJ=urLCD!@#ID>c1b2j&&k4)8<_lUHy@ScU}W-=ntWbLMVZ6d)yqeS z>3;!3asgw?A_uX_DU1aSDFuuP1q_J=45^F!xhHc=`*HA1oSew@e{$;N7-^Ei##lxgdFRzmzI-kZ;&# IS((k80E*WzCIA2c delta 141 zcmbR2bHSU-CD!N}w;Hu=1iibMfJasgw? zB3GfwDU1aSDFuuP1q_Lce0e5|N&7K#PEMShBkf?i(0qaE$Yd_&0!A#7lg~-}ZBCNe$O!;@8!4;+ diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x b/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x index 497706c9742a9ea5396d6c9c4cc1cc2a4a530339..e95258cbd8681103a642f8973bd1ac9ef229cff7 100644 GIT binary patch delta 173 zcmbQ5bR&t&CDfOmyCjpp-(+FQ4NShyn~zG0Ffw^bO};IqqRip!>g6NE z^uK^1xqvZck%QRe6vhIElmf^zn{W$n0PEO?dKRI=BhO~py0+q=z z`YiFmPVoWGo(2IyuEEYhzF~|7$sHimQW;89C*PI!lPX|HTI9qsH7T{A87z^+kn9}d V7MaY&Jo&%0Ix|qqW=)wlA^>)AH8}tP delta 141 zcmcbSG&PCKCDkajR#XuiO7a*RGpe6Uk|fU~DTK#*&& ebC7QsqjQK`WHJ|X0V5X4$+x8aHs{H_5&-}%Gbwuj diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator index 3aaa2bbdf54a0d0cade14421e84c6ec5a42f96fa..ba2a7d0004be7cd7220716dc7e8594be87197b98 100644 GIT binary patch delta 200 zcmdnx{MVVwCD=Psgp~j9h4TROpeiKi4S&)4{-K02nccwb`J6lV=PGS0GXD`P?|dVrL><^0YlOv uCzh#6sRhkoi6n+(=McBZWG?2(deUN(cS{Mf0=1PUO+GIrw%J!EmlXghfj&V1 delta 162 zcmezCyvv!(CDHmKYXFu0)A*TNY49NwIDT`c%CZ{kKFr*YPCKNCvF7oA>tS9Zq%sDx6a*MQs=|b}b zrjujzS>l78;scyL4FZB(gPntX!x)`I+#-{?mUq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvf+r*iaw} diff --git a/tests/data/acpi/x86/q35/DSDT.bridge b/tests/data/acpi/x86/q35/DSDT.bridge index d9938dba8fa5d405f7696c0dbdc24f3ae42ec934..1939fda2507cde6fcb6f7a093897f9bd2cb987ef 100644 GIT binary patch delta 180 zcmX>Q+Z@N`66_Mftk1x}sIifYU6RSkd9twN1||oK$wwvsvU^H72Rr+5Og4}apZq|I zLz%3;!3asgw?B7f1zDU1aSDFuuP1q_J=45^F!xF<>ba_~)@oXGWma_Zy& zX$PeRDwAXMS>l78;scyL4FZB(gPntX!x#&aJ3xk|GL)uHJ}T`eRltz6$cbfYQffgn cSR#ob**U~5GMS5c@^fi*W}ueMf->uc0FDnfXaE2J delta 109 zcmV-z0FwWMUcg-nL{mgmz%Bp)0aLLG2N?oSK9ds}umVjplf@bR0!9{-#2O?RZ~$X) z0c4^?5|d;BZ~$a*0bpfHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvfJo@Ma)J diff --git a/tests/data/acpi/x86/q35/DSDT.core-count b/tests/data/acpi/x86/q35/DSDT.core-count index a24b04cbdbf09383b933a42a2a15182545543a87..41c0832ab5041ff5361598813ec28fe7442b191b 100644 GIT binary patch delta 168 zcmeyEvL%(vCD>}qURltz6$cbfYQffgnSR#ob**U~5GMS5c Ra+;hvbC7S?<_U5Sg#j0XG*JKm delta 134 zcmV;10D1qkW$|PRL{mgmaWVh^0k5$N2N?p5TayzRu>y)vv&I>E0|!SILsLUe2$O&u z7n5Ec2pDhxV{idvqD2yuWC3shWN-msZ~$SVO$?KX9ZmxYlVOwJ9Y6%3H=s3>As!VR oLsUjtV+sRs0bDNxUsFO~P(w*DP*O!xLsCsvlR+L%v(+Bo6Qh?Xg#Z8m diff --git a/tests/data/acpi/x86/q35/DSDT.core-count2 b/tests/data/acpi/x86/q35/DSDT.core-count2 index 3a0cb8c581c8cc630a2ec21712b7f8b75fcad1c8..153b45f0f7443d25cecc2a752fb6dbd921160e78 100644 GIT binary patch delta 160 zcmaFW&a}3JiOVI}B}BJ{fr0VbMlNDW6B~&vB@cn1q>+#j0pt{i3JR)iyS6vst0iJO`M#_^?!2eWMuM^nk22F%;D_n*>_hQtDf)J6W>lWnB^IQS+`PUQMOIdyWcv;)%umB|;S zMY#)-J3zWq8A?+pGs*bL6fh($a$=d9lv>aXmPleqb`EiiOy*)PNS=H^N|iatH*B+? Hj4B%dq#Q5J delta 124 zcmV-?0E7RwMe{@oL{mgmb0Po$0h_T32N?oSTayzRumVj`v&9)X0Rl%Bk{cu#Z~$X) z0c4^@5|d;BZ~$a*0bpmkO;(ft8&0#693uw7dL)Se diff --git a/tests/data/acpi/x86/q35/DSDT.cxl b/tests/data/acpi/x86/q35/DSDT.cxl index 3c34d4dcab16783abe65f6fa5e64eb69d40795fb..0f1ccdfcc3ffbf151c172015cc4bf18bc4ead218 100644 GIT binary patch delta 180 zcmcbWHaDHiCDUq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvbqc@Dv~7 diff --git a/tests/data/acpi/x86/q35/DSDT.dimmpxm b/tests/data/acpi/x86/q35/DSDT.dimmpxm index 228374b55bd544116e359f659e546fc66cf8a895..eb5b6e9f52107d9c95e38e94a67a6b5001beafc1 100644 GIT binary patch delta 173 zcmbQ~cgK&*CDg6NE z^uK^1xqvZck%QRe6vhIElmf_0v{W$n0PEO?dKRI=BhqQyz0+q=z z`YiFmPVoWGo(2IyuEEYhzF~|7$sHimQW;89C;yf9lPX|HTI9qsH7T{A87z^+kn9}d V7MaY&JXu~wof)WQv!~1jE&ziMG`Ro( delta 141 zcmccPH`9;HCDNE4v?P;9z+`pF4NPvXn{P@gFfzG|P5v#VB2mDQT)>#J z$W>@^3S$97N&#a+0Yl;;KKX$Z zhcbt=tCx=u)Bgg7vQVJLo3K$X#7*ZGcaZi%=<=~q*Ig#uCMBkrT_*q|}0D dutX9=vU7-AWHJ}?fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvg;|6`>%) diff --git a/tests/data/acpi/x86/q35/DSDT.ipmismbus b/tests/data/acpi/x86/q35/DSDT.ipmismbus index e5d6811bee1233d74236453c49060390d74d4416..d04d215a1d0fbc77739084d100a35af47a1c1a62 100644 GIT binary patch delta 180 zcmezExX+2pCDUq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvhRAPqrav diff --git a/tests/data/acpi/x86/q35/DSDT.ivrs b/tests/data/acpi/x86/q35/DSDT.ivrs index 46fd25400b7c00ee9149ddb64cb5d5bd73f6a82b..dbd4f858354df0f4c050fd0b914581154f340ee8 100644 GIT binary patch delta 180 zcmdnu_}!7qCD*Tg0oSn#2N?oSK9ds}umVjplf@bR0!9{-#2O?RZ~$X) z0c4^?5|d;BZ~$a*0bpfHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRve)OE4?7a diff --git a/tests/data/acpi/x86/q35/DSDT.memhp b/tests/data/acpi/x86/q35/DSDT.memhp index 5ce081187a578ba7145a9ba20d30be36c13b7663..f73ade9bf6e4545f9912ed654a282884a54cec79 100644 GIT binary patch delta 180 zcmez5z14@yCDdw@xewec1b1==gGp78<^ZICLfjj%kC-V9PI4JG1)*yeDVV+ z4rLB!S1%tSrvC*D$pwrli~L0=r!W>Uq!chF6fh(fFr+T> delta 109 zcmV-z0FwW;O!7+#L{mgm@+ANO0V%Nx2N?oIKa&#~umVFglf@bR0!9{-#2O?RZ~$X) z0c4^?5|d;BZ~$a*0bpfHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvh0624*0f diff --git a/tests/data/acpi/x86/q35/DSDT.mmio64 b/tests/data/acpi/x86/q35/DSDT.mmio64 index bdf36c4d575bfc4eb2eac3f00c9b7b4270f88677..f0ddb4c83cdc9afdf4f289a66ed6bf0d630fd623 100644 GIT binary patch delta 180 zcmeD6y5Pm-66_KZpvu6&xNsvEyCjo`^JHPk4NPtplaEUNW%rbF4tDnAm~0>;KKX$Z zhcbt=tCx=u)Bgg7vQVJLo3K$X#7*ZGcaZi%=<=~q*Ig#uCMBkrT_*q|}0D dutX9=vU7-AWHJ}?fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvh{W)LtM& diff --git a/tests/data/acpi/x86/q35/DSDT.multi-bridge b/tests/data/acpi/x86/q35/DSDT.multi-bridge index 1db43a69e4c2affd8bd678bbef4d3c228380288e..3ad19e3f5e480db1c449b838c83833f7665186cd 100644 GIT binary patch delta 180 zcmbP{emkAZCDUq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvd*F{DB}m diff --git a/tests/data/acpi/x86/q35/DSDT.noacpihp b/tests/data/acpi/x86/q35/DSDT.noacpihp index 8bc16887e1c963c61aaecf71712a09c0554f6d67..9f7261d1b06bbf5d8a3e5a7a46b247a2a21eb544 100644 GIT binary patch delta 206 zcmZ4O@XmqDCDreFm{7owSiq3F$d7ySW=UUuzKN3) zx&BX1<@yg)wzOb!ptSI0St&K81uBzc^jYGAo#F$WJq-eaT!Wp1e8U(Ek~=^Kr!thL zPOgyhlPX|HTI9qsH7T{A87z^+kn9}d7MaY&Jb9LsIxA31Y0~6)NwLj$r2JU{in&3W delta 161 zcmaFou-bvkCDV3(Xgp yPL9!Mi4S&)4{-K02nccwb`J6lV{{I2i%jNXE?~qWIk{BIZ*sh(*k*ZYe^vm=gF-S8<-p{CYwwCW%rbF4tDnAn0!}KT${t$ z&ox|#=|6|FtCtUuE?`J5U`$!$FFHAev4A0^fH9$fA+dlVb&((U$vpf!_R uF9csxLSIlrNia}SMN>mkO;!OzR7P223Img|8x@l@8%~oR8WywO8+rsM6)9){ diff --git a/tests/data/acpi/x86/q35/DSDT.numamem b/tests/data/acpi/x86/q35/DSDT.numamem index ba6669437e65952f24516ded954b33fe54bdedfb..2867f5b44498d788fc0effd0bf616317821be88e 100644 GIT binary patch delta 180 zcmZ4K_{NdTCDfHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRveZD8^j=y diff --git a/tests/data/acpi/x86/q35/DSDT.pvpanic-isa b/tests/data/acpi/x86/q35/DSDT.pvpanic-isa index 6ad42873e91c80cef5a42224cb4d31936dad59b4..02cc07f010f880684216ba8925c8f3f55cfd80aa 100644 GIT binary patch delta 180 zcmeBhI_<>e66_M;KKX$Z zhcbt=tCx=u)Bgg7vQVJLo3K$X#7*ZGcaZi%=<=~q*Ig#uCMBkrT_*q|}0D dutX9=vU7-AWHJ}?fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvh&N%k3a4 diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count b/tests/data/acpi/x86/q35/DSDT.thread-count index a24b04cbdbf09383b933a42a2a15182545543a87..41c0832ab5041ff5361598813ec28fe7442b191b 100644 GIT binary patch delta 168 zcmeyEvL%(vCD>}qURltz6$cbfYQffgnSR#ob**U~5GMS5c Ra+;hvbC7S?<_U5Sg#j0XG*JKm delta 134 zcmV;10D1qkW$|PRL{mgmaWVh^0k5$N2N?p5TayzRu>y)vv&I>E0|!SILsLUe2$O&u z7n5Ec2pDhxV{idvqD2yuWC3shWN-msZ~$SVO$?KX9ZmxYlVOwJ9Y6%3H=s3>As!VR oLsUjtV+sRs0bDNxUsFO~P(w*DP*O!xLsCsvlR+L%v(+Bo6Qh?Xg#Z8m diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count2 b/tests/data/acpi/x86/q35/DSDT.thread-count2 index 3a0cb8c581c8cc630a2ec21712b7f8b75fcad1c8..153b45f0f7443d25cecc2a752fb6dbd921160e78 100644 GIT binary patch delta 160 zcmaFW&a}3JiOVI}B}BJ{fr0VbMlNDW6B~&vB@cn1q>+#j0pt{i3JR)iyS6vst0iJO`M#_^?!2e3;!3asgw?B7f1zDU1aSDFuuP1q_J=45^F!xF<>ba_~)@oXGWma_Zy& zX$PeRDwAXMS>l78;scyL4FZB(gPntX!x#&aJ3xk|GL)uHJ}T`eRltz6$cbfYQffgn cSR#ob**U~5GMS5c@^fi*W}ueMf-)c20H4n`x&QzG delta 109 zcmV-z0FwX2MuA2OL{mgm0V4na0cWua2N?oSK9ds}umVjplf@bR0!9{-#2O?RZ~$X) z0c4^?5|d;BZ~$a*0bpfHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvhjImmMFN diff --git a/tests/data/acpi/x86/q35/DSDT.tis.tpm2 b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 index a09253042ce4a715922027245de8a2ab7449c5b7..b05563deedc65df50f35b2399862d9ee8d4d1e0e 100644 GIT binary patch delta 180 zcmbR3cF&E=CDUq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvZWin{Xbx diff --git a/tests/data/acpi/x86/q35/DSDT.type4-count b/tests/data/acpi/x86/q35/DSDT.type4-count index edc23198cdb47a981bcbc82bc8e392b815abb554..00807e7fd4d758bc2ab9c69ac8869cf6864399f7 100644 GIT binary patch delta 200 zcmbO`k@3MqMlP3Nmyib@3=E8mHgd5`GW8sqEG)T^sdM}0W0I5D*uA8jgPr|2CNnCC zYjZgJxrPfd{pWCY_3{DI1q{grj46v8#3rXO7BHj~FeVf*Bo;8FF7oG|JWaumgKy&G zM6UmnQzt)Ha8O#HGC4+{B|g|GKET=2ARx#!*g42IjIkiO17un%Luu;d7)3v+0*0hT wPApTCQVW{F5=jin&LM7*$z05n7b=KN)>9B<2Wl%#idLVTs35#~zv4YL0HE7HJpcdz delta 163 zcmaDbk#X)sMlP3Nmyo$03=E97H*&E{GIj2oEG)T^scrM-W0I5D*xkjPgPr|2CNnCC zD{wgbxrPfd{V!lhE?`Vq(KT@%~0Gc1b2D=gGp78<-p{CLfjj%kC-V9PI4JG1)*yeDVV+ z4rLB!S1%tSrvC*D$pwrli~L0=r!W>Uq!chF6fh(fFr+T>fHItJ;91{ouLsUjtV+sS4 PKO7a4!W&MrRvZBxw>BRO diff --git a/tests/data/acpi/x86/q35/DSDT.xapic b/tests/data/acpi/x86/q35/DSDT.xapic index d4acd851c62c956436a436f9fa6d08fc5f370fa7..227d421f16ed1824a87e8a91da734828f8b48cbf 100644 GIT binary patch delta 195 zcmZph&UA7*6PHV{OUTJ?1_s7U8@bpenOZ+i7M9$|)bwogG091M?A}t&!OnghlNmL{ zwK$ypT*HN!{&P6Hdien90*2%Q#*{^lVv|!C3m8%g7!wK@5(^ko7dcFxsu94!H*slV4~!C@oN#9HY+?AM6w#;OuD-5ab%{9ON6uSdiQSGAxy$G<9;Urk_*+L((EA rmZ?dp1XQ>Sgf}11JeCLm_4q#( delta 162 zcmX>(ovCd)6PHV{OGsNc0|VpRja=-KOigbm3rlWf>VLTTnB*irb`LS=Ym1>}e1X Date: Thu, 12 Dec 2024 16:37:38 +0800 Subject: [PATCH 1023/2892] intel_iommu: Use the latest fault reasons defined by spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec revision 3.0 or above defines more detailed fault reasons for scalable mode. So introduce them into emulation code, see spec section 7.1.2 for details. Note spec revision has no relation with VERSION register, Guest kernel should not use that register to judge what features are supported. Instead cap/ecap bits should be checked. Signed-off-by: Yu Zhang Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Reviewed-by: Yi Liu Acked-by: Jason Wang Message-Id: <20241212083757.605022-2-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 25 ++++++++++++++++--------- hw/i386/intel_iommu_internal.h | 9 ++++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index a8c275f9ce..0ab1676d5f 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -796,7 +796,7 @@ static int vtd_get_pdire_from_pdir_table(dma_addr_t pasid_dir_base, addr = pasid_dir_base + index * entry_size; if (dma_memory_read(&address_space_memory, addr, pdire, entry_size, MEMTXATTRS_UNSPECIFIED)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_DIR_ACCESS_ERR; } pdire->val = le64_to_cpu(pdire->val); @@ -814,6 +814,7 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, dma_addr_t addr, VTDPASIDEntry *pe) { + uint8_t pgtt; uint32_t index; dma_addr_t entry_size; X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); @@ -823,7 +824,7 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, addr = addr + index * entry_size; if (dma_memory_read(&address_space_memory, addr, pe, entry_size, MEMTXATTRS_UNSPECIFIED)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_TABLE_ACCESS_ERR; } for (size_t i = 0; i < ARRAY_SIZE(pe->val); i++) { pe->val[i] = le64_to_cpu(pe->val[i]); @@ -831,11 +832,13 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, /* Do translation type check */ if (!vtd_pe_type_check(x86_iommu, pe)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_TABLE_ENTRY_INV; } - if (!vtd_is_level_supported(s, VTD_PE_GET_LEVEL(pe))) { - return -VTD_FR_PASID_TABLE_INV; + pgtt = VTD_PE_GET_TYPE(pe); + if (pgtt == VTD_SM_PASID_ENTRY_SLT && + !vtd_is_level_supported(s, VTD_PE_GET_LEVEL(pe))) { + return -VTD_FR_PASID_TABLE_ENTRY_INV; } return 0; @@ -876,7 +879,7 @@ static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s, } if (!vtd_pdire_present(&pdire)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_DIR_ENTRY_P; } ret = vtd_get_pe_from_pdire(s, pasid, &pdire, pe); @@ -885,7 +888,7 @@ static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s, } if (!vtd_pe_present(pe)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_ENTRY_P; } return 0; @@ -938,7 +941,7 @@ static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s, } if (!vtd_pdire_present(&pdire)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_DIR_ENTRY_P; } /* @@ -1795,7 +1798,11 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_ROOT_ENTRY_RSVD] = false, [VTD_FR_PAGING_ENTRY_RSVD] = true, [VTD_FR_CONTEXT_ENTRY_TT] = true, - [VTD_FR_PASID_TABLE_INV] = false, + [VTD_FR_PASID_DIR_ACCESS_ERR] = false, + [VTD_FR_PASID_DIR_ENTRY_P] = true, + [VTD_FR_PASID_TABLE_ACCESS_ERR] = false, + [VTD_FR_PASID_ENTRY_P] = true, + [VTD_FR_PASID_TABLE_ENTRY_INV] = true, [VTD_FR_SM_INTERRUPT_ADDR] = true, [VTD_FR_MAX] = false, }; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 4323fc5d6d..a987023692 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -311,7 +311,14 @@ typedef enum VTDFaultReason { * request while disabled */ VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */ - VTD_FR_PASID_TABLE_INV = 0x58, /*Invalid PASID table entry */ + /* PASID directory entry access failure */ + VTD_FR_PASID_DIR_ACCESS_ERR = 0x50, + /* The Present(P) field of pasid directory entry is 0 */ + VTD_FR_PASID_DIR_ENTRY_P = 0x51, + VTD_FR_PASID_TABLE_ACCESS_ERR = 0x58, /* PASID table entry access failure */ + /* The Present(P) field of pasid table entry is 0 */ + VTD_FR_PASID_ENTRY_P = 0x59, + VTD_FR_PASID_TABLE_ENTRY_INV = 0x5b, /*Invalid PASID table entry */ /* Output address in the interrupt address range for scalable mode */ VTD_FR_SM_INTERRUPT_ADDR = 0x87, From b291dae33d1dab48670b86807a71cb1cbadf39aa Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:39 +0800 Subject: [PATCH 1024/2892] intel_iommu: Make pasid entry type check accurate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When guest configures Nested Translation(011b) or First-stage Translation only (001b), type check passed unaccurately. Fails the type check in those cases as their simulation isn't supported yet. Fixes: fb43cf739e1 ("intel_iommu: scalable mode emulation") Suggested-by: Yi Liu Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Reviewed-by: Yi Liu Acked-by: Jason Wang Message-Id: <20241212083757.605022-3-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0ab1676d5f..bd639b7ff7 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -759,20 +759,16 @@ static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu, VTDPASIDEntry *pe) { switch (VTD_PE_GET_TYPE(pe)) { - case VTD_SM_PASID_ENTRY_FLT: case VTD_SM_PASID_ENTRY_SLT: - case VTD_SM_PASID_ENTRY_NESTED: - break; + return true; case VTD_SM_PASID_ENTRY_PT: - if (!x86_iommu->pt_supported) { - return false; - } - break; + return x86_iommu->pt_supported; + case VTD_SM_PASID_ENTRY_FLT: + case VTD_SM_PASID_ENTRY_NESTED: default: /* Unknown type */ return false; } - return true; } static inline bool vtd_pdire_present(VTDPASIDDirEntry *pdire) From 791346f93d2aa3b7eaebf4a12f4b7c558e94ff6b Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:40 +0800 Subject: [PATCH 1025/2892] intel_iommu: Add a placeholder variable for scalable mode stage-1 translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an new element flts in IntelIOMMUState to mark stage-1 translation support in scalable mode, this element will be exposed as an intel_iommu property x-flts finally. For now, it's only a placehholder and used for address width compatibility check and block host device passthrough until nesting is supported. Signed-off-by: Yi Liu Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Reviewed-by: Clément Mathieu--Drif Reviewed-by: Yi Liu Message-Id: <20241212083757.605022-4-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 23 ++++++++++++++++++----- include/hw/i386/intel_iommu.h | 1 + 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index bd639b7ff7..d0c1d73974 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3917,7 +3917,13 @@ static bool vtd_check_hiod(IntelIOMMUState *s, HostIOMMUDevice *hiod, return false; } - return true; + if (!s->flts) { + /* All checks requested by VTD stage-2 translation pass */ + return true; + } + + error_setg(errp, "host device is uncompatible with stage-1 translation"); + return false; } static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, @@ -4307,14 +4313,21 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) } } - /* Currently only address widths supported are 39 and 48 bits */ - if ((s->aw_bits != VTD_HOST_AW_39BIT) && - (s->aw_bits != VTD_HOST_AW_48BIT)) { - error_setg(errp, "Supported values for aw-bits are: %d, %d", + if (!s->flts && s->aw_bits != VTD_HOST_AW_39BIT && + s->aw_bits != VTD_HOST_AW_48BIT) { + error_setg(errp, "%s: supported values for aw-bits are: %d, %d", + s->scalable_mode ? "Scalable mode(flts=off)" : "Legacy mode", VTD_HOST_AW_39BIT, VTD_HOST_AW_48BIT); return false; } + if (s->flts && s->aw_bits != VTD_HOST_AW_48BIT) { + error_setg(errp, + "Scalable mode(flts=on): supported value for aw-bits is: %d", + VTD_HOST_AW_48BIT); + return false; + } + if (s->scalable_mode && !s->dma_drain) { error_setg(errp, "Need to set dma_drain for scalable mode"); return false; diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index d372cd396b..b19f3004f0 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -262,6 +262,7 @@ struct IntelIOMMUState { bool caching_mode; /* RO - is cap CM enabled? */ bool scalable_mode; /* RO - is Scalable Mode supported? */ + bool flts; /* RO - is stage-1 translation supported? */ bool snoop_control; /* RO - is SNP filed supported? */ dma_addr_t root; /* Current root table pointer */ From ad0a7f1e1edbe841d4285a6b56f071eb3de9c86c Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:41 +0800 Subject: [PATCH 1026/2892] intel_iommu: Flush stage-2 cache in PASID-selective PASID-based iotlb invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per VT-d spec 4.1, 6.5.2.4, "Table 21. PASID-based-IOTLB Invalidation", PADID-selective PASID-based iotlb invalidation will flush stage-2 iotlb entries with matching domain id and pasid. With stage-1 translation introduced, guest could send PASID-selective PASID-based iotlb invalidation to flush either stage-1 or stage-2 entries. By this chance, remove old IOTLB related definitions which were unused. Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Acked-by: Jason Wang Message-Id: <20241212083757.605022-5-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 85 +++++++++++++++++++++++++++++++++- hw/i386/intel_iommu_internal.h | 14 ++++-- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index d0c1d73974..bb1f43c4b3 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2692,6 +2692,83 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) return true; } +static gboolean vtd_hash_remove_by_pasid(gpointer key, gpointer value, + gpointer user_data) +{ + VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value; + VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; + + return ((entry->domain_id == info->domain_id) && + (entry->pasid == info->pasid)); +} + +static void vtd_piotlb_pasid_invalidate(IntelIOMMUState *s, + uint16_t domain_id, uint32_t pasid) +{ + VTDIOTLBPageInvInfo info; + VTDAddressSpace *vtd_as; + VTDContextEntry ce; + + info.domain_id = domain_id; + info.pasid = pasid; + + vtd_iommu_lock(s); + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_pasid, + &info); + vtd_iommu_unlock(s); + + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce) && + domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) { + uint32_t rid2pasid = VTD_CE_GET_RID2PASID(&ce); + + if ((vtd_as->pasid != PCI_NO_PASID || pasid != rid2pasid) && + vtd_as->pasid != pasid) { + continue; + } + + if (!s->flts) { + vtd_address_space_sync(vtd_as); + } + } + } +} + +static bool vtd_process_piotlb_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + uint16_t domain_id; + uint32_t pasid; + uint64_t mask[4] = {VTD_INV_DESC_PIOTLB_RSVD_VAL0, + VTD_INV_DESC_PIOTLB_RSVD_VAL1, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, true, + __func__, "piotlb inv")) { + return false; + } + + domain_id = VTD_INV_DESC_PIOTLB_DID(inv_desc->val[0]); + pasid = VTD_INV_DESC_PIOTLB_PASID(inv_desc->val[0]); + switch (inv_desc->val[0] & VTD_INV_DESC_PIOTLB_G) { + case VTD_INV_DESC_PIOTLB_ALL_IN_PASID: + vtd_piotlb_pasid_invalidate(s, domain_id, pasid); + break; + + case VTD_INV_DESC_PIOTLB_PSI_IN_PASID: + break; + + default: + error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (type mismatch: 0x%llx)", + __func__, inv_desc->val[1], inv_desc->val[0], + inv_desc->val[0] & VTD_INV_DESC_IOTLB_G); + return false; + } + return true; +} + static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { @@ -2810,6 +2887,13 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + case VTD_INV_DESC_PIOTLB: + trace_vtd_inv_desc("p-iotlb", inv_desc.val[1], inv_desc.val[0]); + if (!vtd_process_piotlb_desc(s, &inv_desc)) { + return false; + } + break; + case VTD_INV_DESC_WAIT: trace_vtd_inv_desc("wait", inv_desc.hi, inv_desc.lo); if (!vtd_process_wait_desc(s, &inv_desc)) { @@ -2837,7 +2921,6 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) * iommu driver) work, just return true is enough so far. */ case VTD_INV_DESC_PC: - case VTD_INV_DESC_PIOTLB: if (s->scalable_mode) { break; } diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index a987023692..48019e2005 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -404,11 +404,6 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL) #define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000f100ULL #define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL -#define VTD_INV_DESC_IOTLB_PASID_PASID (2ULL << 4) -#define VTD_INV_DESC_IOTLB_PASID_PAGE (3ULL << 4) -#define VTD_INV_DESC_IOTLB_PASID(val) (((val) >> 32) & VTD_PASID_ID_MASK) -#define VTD_INV_DESC_IOTLB_PASID_RSVD_LO 0xfff00000000001c0ULL -#define VTD_INV_DESC_IOTLB_PASID_RSVD_HI 0xf80ULL /* Mask for Device IOTLB Invalidate Descriptor */ #define VTD_INV_DESC_DEVICE_IOTLB_ADDR(val) ((val) & 0xfffffffffffff000ULL) @@ -443,6 +438,15 @@ typedef union VTDInvDesc VTDInvDesc; (0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \ (0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM)) +/* Masks for PIOTLB Invalidate Descriptor */ +#define VTD_INV_DESC_PIOTLB_G (3ULL << 4) +#define VTD_INV_DESC_PIOTLB_ALL_IN_PASID (2ULL << 4) +#define VTD_INV_DESC_PIOTLB_PSI_IN_PASID (3ULL << 4) +#define VTD_INV_DESC_PIOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) +#define VTD_INV_DESC_PIOTLB_PASID(val) (((val) >> 32) & 0xfffffULL) +#define VTD_INV_DESC_PIOTLB_RSVD_VAL0 0xfff000000000f1c0ULL +#define VTD_INV_DESC_PIOTLB_RSVD_VAL1 0xf80ULL + /* Information about page-selective IOTLB invalidate */ struct VTDIOTLBPageInvInfo { uint16_t domain_id; From eda4c9b5b3c46f8d4d6a628cc7a588f187f30050 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Thu, 12 Dec 2024 16:37:42 +0800 Subject: [PATCH 1027/2892] intel_iommu: Rename slpte to pte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because we will support both FST(a.k.a, FLT) and SST(a.k.a, SLT) translation, rename variable and functions from slpte to pte whenever possible. But some are SST only, they are renamed with sl_ prefix. Signed-off-by: Yi Liu Co-developed-by: Clément Mathieu--Drif Signed-off-by: Clément Mathieu--Drif Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Reviewed-by: Yi Liu Message-Id: <20241212083757.605022-6-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 129 +++++++++++++++++---------------- hw/i386/intel_iommu_internal.h | 24 +++--- include/hw/i386/intel_iommu.h | 2 +- 3 files changed, 78 insertions(+), 77 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index bb1f43c4b3..dfac5982d6 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -48,7 +48,8 @@ /* pe operations */ #define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT) -#define VTD_PE_GET_LEVEL(pe) (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) +#define VTD_PE_GET_SL_LEVEL(pe) \ + (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) /* * PCI bus number (or SID) is not reliable since the device is usaully @@ -284,15 +285,15 @@ static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, } /* The shift of an addr for a certain level of paging structure */ -static inline uint32_t vtd_slpt_level_shift(uint32_t level) +static inline uint32_t vtd_pt_level_shift(uint32_t level) { assert(level != 0); - return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_SL_LEVEL_BITS; + return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_LEVEL_BITS; } -static inline uint64_t vtd_slpt_level_page_mask(uint32_t level) +static inline uint64_t vtd_pt_level_page_mask(uint32_t level) { - return ~((1ULL << vtd_slpt_level_shift(level)) - 1); + return ~((1ULL << vtd_pt_level_shift(level)) - 1); } static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, @@ -349,7 +350,7 @@ static void vtd_reset_caches(IntelIOMMUState *s) static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) { - return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; + return (addr & vtd_pt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; } /* Must be called with IOMMU lock held */ @@ -360,7 +361,7 @@ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, VTDIOTLBEntry *entry; unsigned level; - for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) { + for (level = VTD_PT_LEVEL; level < VTD_PML4_LEVEL; level++) { key.gfn = vtd_get_iotlb_gfn(addr, level); key.level = level; key.sid = source_id; @@ -377,7 +378,7 @@ out: /* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, - uint16_t domain_id, hwaddr addr, uint64_t slpte, + uint16_t domain_id, hwaddr addr, uint64_t pte, uint8_t access_flags, uint32_t level, uint32_t pasid) { @@ -385,7 +386,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, struct vtd_iotlb_key *key = g_malloc(sizeof(*key)); uint64_t gfn = vtd_get_iotlb_gfn(addr, level); - trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); + trace_vtd_iotlb_page_update(source_id, addr, pte, domain_id); if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { trace_vtd_iotlb_reset("iotlb exceeds size limit"); vtd_reset_iotlb_locked(s); @@ -393,9 +394,9 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, entry->gfn = gfn; entry->domain_id = domain_id; - entry->slpte = slpte; + entry->pte = pte; entry->access_flags = access_flags; - entry->mask = vtd_slpt_level_page_mask(level); + entry->mask = vtd_pt_level_page_mask(level); entry->pasid = pasid; key->gfn = gfn; @@ -710,32 +711,32 @@ static inline dma_addr_t vtd_ce_get_slpt_base(VTDContextEntry *ce) return ce->lo & VTD_CONTEXT_ENTRY_SLPTPTR; } -static inline uint64_t vtd_get_slpte_addr(uint64_t slpte, uint8_t aw) +static inline uint64_t vtd_get_pte_addr(uint64_t pte, uint8_t aw) { - return slpte & VTD_SL_PT_BASE_ADDR_MASK(aw); + return pte & VTD_PT_BASE_ADDR_MASK(aw); } /* Whether the pte indicates the address of the page frame */ -static inline bool vtd_is_last_slpte(uint64_t slpte, uint32_t level) +static inline bool vtd_is_last_pte(uint64_t pte, uint32_t level) { - return level == VTD_SL_PT_LEVEL || (slpte & VTD_SL_PT_PAGE_SIZE_MASK); + return level == VTD_PT_LEVEL || (pte & VTD_PT_PAGE_SIZE_MASK); } -/* Get the content of a spte located in @base_addr[@index] */ -static uint64_t vtd_get_slpte(dma_addr_t base_addr, uint32_t index) +/* Get the content of a pte located in @base_addr[@index] */ +static uint64_t vtd_get_pte(dma_addr_t base_addr, uint32_t index) { - uint64_t slpte; + uint64_t pte; - assert(index < VTD_SL_PT_ENTRY_NR); + assert(index < VTD_PT_ENTRY_NR); if (dma_memory_read(&address_space_memory, - base_addr + index * sizeof(slpte), - &slpte, sizeof(slpte), MEMTXATTRS_UNSPECIFIED)) { - slpte = (uint64_t)-1; - return slpte; + base_addr + index * sizeof(pte), + &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED)) { + pte = (uint64_t)-1; + return pte; } - slpte = le64_to_cpu(slpte); - return slpte; + pte = le64_to_cpu(pte); + return pte; } /* Given an iova and the level of paging structure, return the offset @@ -743,12 +744,12 @@ static uint64_t vtd_get_slpte(dma_addr_t base_addr, uint32_t index) */ static inline uint32_t vtd_iova_level_offset(uint64_t iova, uint32_t level) { - return (iova >> vtd_slpt_level_shift(level)) & - ((1ULL << VTD_SL_LEVEL_BITS) - 1); + return (iova >> vtd_pt_level_shift(level)) & + ((1ULL << VTD_LEVEL_BITS) - 1); } /* Check Capability Register to see if the @level of page-table is supported */ -static inline bool vtd_is_level_supported(IntelIOMMUState *s, uint32_t level) +static inline bool vtd_is_sl_level_supported(IntelIOMMUState *s, uint32_t level) { return VTD_CAP_SAGAW_MASK & s->cap & (1ULL << (level - 2 + VTD_CAP_SAGAW_SHIFT)); @@ -833,7 +834,7 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, pgtt = VTD_PE_GET_TYPE(pe); if (pgtt == VTD_SM_PASID_ENTRY_SLT && - !vtd_is_level_supported(s, VTD_PE_GET_LEVEL(pe))) { + !vtd_is_sl_level_supported(s, VTD_PE_GET_SL_LEVEL(pe))) { return -VTD_FR_PASID_TABLE_ENTRY_INV; } @@ -972,7 +973,7 @@ static uint32_t vtd_get_iova_level(IntelIOMMUState *s, if (s->root_scalable) { vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); - return VTD_PE_GET_LEVEL(&pe); + return VTD_PE_GET_SL_LEVEL(&pe); } return vtd_ce_get_level(ce); @@ -1040,9 +1041,9 @@ static inline uint64_t vtd_iova_limit(IntelIOMMUState *s, } /* Return true if IOVA passes range check, otherwise false. */ -static inline bool vtd_iova_range_check(IntelIOMMUState *s, - uint64_t iova, VTDContextEntry *ce, - uint8_t aw, uint32_t pasid) +static inline bool vtd_iova_sl_range_check(IntelIOMMUState *s, + uint64_t iova, VTDContextEntry *ce, + uint8_t aw, uint32_t pasid) { /* * Check if @iova is above 2^X-1, where X is the minimum of MGAW @@ -1083,17 +1084,17 @@ static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level) /* * We should have caught a guest-mis-programmed level earlier, - * via vtd_is_level_supported. + * via vtd_is_sl_level_supported. */ assert(level < VTD_SPTE_RSVD_LEN); /* - * Zero level doesn't exist. The smallest level is VTD_SL_PT_LEVEL=1 and - * checked by vtd_is_last_slpte(). + * Zero level doesn't exist. The smallest level is VTD_PT_LEVEL=1 and + * checked by vtd_is_last_pte(). */ assert(level); - if ((level == VTD_SL_PD_LEVEL || level == VTD_SL_PDP_LEVEL) && - (slpte & VTD_SL_PT_PAGE_SIZE_MASK)) { + if ((level == VTD_PD_LEVEL || level == VTD_PDP_LEVEL) && + (slpte & VTD_PT_PAGE_SIZE_MASK)) { /* large page */ rsvd_mask = vtd_spte_rsvd_large[level]; } else { @@ -1119,7 +1120,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, uint64_t access_right_check; uint64_t xlat, size; - if (!vtd_iova_range_check(s, iova, ce, aw_bits, pasid)) { + if (!vtd_iova_sl_range_check(s, iova, ce, aw_bits, pasid)) { error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 "," "pasid=0x%" PRIx32 ")", __func__, iova, pasid); return -VTD_FR_ADDR_BEYOND_MGAW; @@ -1130,7 +1131,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, while (true) { offset = vtd_iova_level_offset(iova, level); - slpte = vtd_get_slpte(addr, offset); + slpte = vtd_get_pte(addr, offset); if (slpte == (uint64_t)-1) { error_report_once("%s: detected read error on DMAR slpte " @@ -1161,17 +1162,17 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, return -VTD_FR_PAGING_ENTRY_RSVD; } - if (vtd_is_last_slpte(slpte, level)) { + if (vtd_is_last_pte(slpte, level)) { *slptep = slpte; *slpte_level = level; break; } - addr = vtd_get_slpte_addr(slpte, aw_bits); + addr = vtd_get_pte_addr(slpte, aw_bits); level--; } - xlat = vtd_get_slpte_addr(*slptep, aw_bits); - size = ~vtd_slpt_level_page_mask(level) + 1; + xlat = vtd_get_pte_addr(*slptep, aw_bits); + size = ~vtd_pt_level_page_mask(level) + 1; /* * From VT-d spec 3.14: Untranslated requests and translation @@ -1322,14 +1323,14 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, trace_vtd_page_walk_level(addr, level, start, end); - subpage_size = 1ULL << vtd_slpt_level_shift(level); - subpage_mask = vtd_slpt_level_page_mask(level); + subpage_size = 1ULL << vtd_pt_level_shift(level); + subpage_mask = vtd_pt_level_page_mask(level); while (iova < end) { iova_next = (iova & subpage_mask) + subpage_size; offset = vtd_iova_level_offset(iova, level); - slpte = vtd_get_slpte(addr, offset); + slpte = vtd_get_pte(addr, offset); if (slpte == (uint64_t)-1) { trace_vtd_page_walk_skip_read(iova, iova_next); @@ -1352,12 +1353,12 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; - if (!vtd_is_last_slpte(slpte, level) && entry_valid) { + if (!vtd_is_last_pte(slpte, level) && entry_valid) { /* * This is a valid PDE (or even bigger than PDE). We need * to walk one further level. */ - ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + ret = vtd_page_walk_level(vtd_get_pte_addr(slpte, info->aw), iova, MIN(iova_next, end), level - 1, read_cur, write_cur, info); } else { @@ -1374,7 +1375,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, event.entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); event.entry.addr_mask = ~subpage_mask; /* NOTE: this is only meaningful if entry_valid == true */ - event.entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + event.entry.translated_addr = vtd_get_pte_addr(slpte, info->aw); event.type = event.entry.perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP; ret = vtd_page_walk_one(&event, info); @@ -1408,11 +1409,11 @@ static int vtd_page_walk(IntelIOMMUState *s, VTDContextEntry *ce, dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); uint32_t level = vtd_get_iova_level(s, ce, pasid); - if (!vtd_iova_range_check(s, start, ce, info->aw, pasid)) { + if (!vtd_iova_sl_range_check(s, start, ce, info->aw, pasid)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(s, end, ce, info->aw, pasid)) { + if (!vtd_iova_sl_range_check(s, end, ce, info->aw, pasid)) { /* Fix end so that it reaches the maximum */ end = vtd_iova_limit(s, ce, info->aw, pasid); } @@ -1527,7 +1528,7 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, /* Check if the programming of context-entry is valid */ if (!s->root_scalable && - !vtd_is_level_supported(s, vtd_ce_get_level(ce))) { + !vtd_is_sl_level_supported(s, vtd_ce_get_level(ce))) { error_report_once("%s: invalid context entry: hi=%"PRIx64 ", lo=%"PRIx64" (level %d not supported)", __func__, ce->hi, ce->lo, @@ -1897,7 +1898,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry; - uint64_t slpte, page_mask; + uint64_t pte, page_mask; uint32_t level, pasid = vtd_as->pasid; uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn); int ret_fr; @@ -1918,13 +1919,13 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, cc_entry = &vtd_as->context_cache_entry; - /* Try to fetch slpte form IOTLB, we don't need RID2PASID logic */ + /* Try to fetch pte from IOTLB, we don't need RID2PASID logic */ if (!rid2pasid) { iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr); if (iotlb_entry) { - trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, + trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->pte, iotlb_entry->domain_id); - slpte = iotlb_entry->slpte; + pte = iotlb_entry->pte; access_flags = iotlb_entry->access_flags; page_mask = iotlb_entry->mask; goto out; @@ -1996,20 +1997,20 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, return true; } - /* Try to fetch slpte form IOTLB for RID2PASID slow path */ + /* Try to fetch pte from IOTLB for RID2PASID slow path */ if (rid2pasid) { iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr); if (iotlb_entry) { - trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, + trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->pte, iotlb_entry->domain_id); - slpte = iotlb_entry->slpte; + pte = iotlb_entry->pte; access_flags = iotlb_entry->access_flags; page_mask = iotlb_entry->mask; goto out; } } - ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &slpte, &level, + ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level, &reads, &writes, s->aw_bits, pasid); if (ret_fr) { vtd_report_fault(s, -ret_fr, is_fpd_set, source_id, @@ -2017,14 +2018,14 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, goto error; } - page_mask = vtd_slpt_level_page_mask(level); + page_mask = vtd_pt_level_page_mask(level); access_flags = IOMMU_ACCESS_FLAG(reads, writes); vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce, pasid), - addr, slpte, access_flags, level, pasid); + addr, pte, access_flags, level, pasid); out: vtd_iommu_unlock(s); entry->iova = addr & page_mask; - entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; + entry->translated_addr = vtd_get_pte_addr(pte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; entry->perm = access_flags; return true; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 48019e2005..e810b0071f 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -533,24 +533,24 @@ typedef struct VTDRootEntry VTDRootEntry; /* Second Level Page Translation Pointer*/ #define VTD_SM_PASID_ENTRY_SLPTPTR (~0xfffULL) -/* Paging Structure common */ -#define VTD_SL_PT_PAGE_SIZE_MASK (1ULL << 7) -/* Bits to decide the offset for each level */ -#define VTD_SL_LEVEL_BITS 9 - /* Second Level Paging Structure */ -#define VTD_SL_PML4_LEVEL 4 -#define VTD_SL_PDP_LEVEL 3 -#define VTD_SL_PD_LEVEL 2 -#define VTD_SL_PT_LEVEL 1 -#define VTD_SL_PT_ENTRY_NR 512 - /* Masks for Second Level Paging Entry */ #define VTD_SL_RW_MASK 3ULL #define VTD_SL_R 1ULL #define VTD_SL_W (1ULL << 1) -#define VTD_SL_PT_BASE_ADDR_MASK(aw) (~(VTD_PAGE_SIZE - 1) & VTD_HAW_MASK(aw)) #define VTD_SL_IGN_COM 0xbff0000000000000ULL #define VTD_SL_TM (1ULL << 62) +/* Common for both First Level and Second Level */ +#define VTD_PML4_LEVEL 4 +#define VTD_PDP_LEVEL 3 +#define VTD_PD_LEVEL 2 +#define VTD_PT_LEVEL 1 +#define VTD_PT_ENTRY_NR 512 +#define VTD_PT_PAGE_SIZE_MASK (1ULL << 7) +#define VTD_PT_BASE_ADDR_MASK(aw) (~(VTD_PAGE_SIZE - 1) & VTD_HAW_MASK(aw)) + +/* Bits to decide the offset for each level */ +#define VTD_LEVEL_BITS 9 + #endif diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index b19f3004f0..f44f3eb63a 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -152,7 +152,7 @@ struct VTDIOTLBEntry { uint64_t gfn; uint16_t domain_id; uint32_t pasid; - uint64_t slpte; + uint64_t pte; uint64_t mask; uint8_t access_flags; }; From eb9da9d2632839c386ecbfc50f78032c9f3a75a4 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Thu, 12 Dec 2024 16:37:43 +0800 Subject: [PATCH 1028/2892] intel_iommu: Implement stage-1 translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds stage-1 page table walking to support stage-1 only translation in scalable mode. Signed-off-by: Yi Liu Co-developed-by: Clément Mathieu--Drif Signed-off-by: Clément Mathieu--Drif Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Message-Id: <20241212083757.605022-7-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 158 ++++++++++++++++++++++++++++++++- hw/i386/intel_iommu_internal.h | 34 +++++++ 2 files changed, 188 insertions(+), 4 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index dfac5982d6..bd6de71c02 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -48,6 +48,8 @@ /* pe operations */ #define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT) +#define VTD_PE_GET_FL_LEVEL(pe) \ + (4 + (((pe)->val[2] >> 2) & VTD_SM_PASID_ENTRY_FLPM)) #define VTD_PE_GET_SL_LEVEL(pe) \ (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) @@ -755,6 +757,11 @@ static inline bool vtd_is_sl_level_supported(IntelIOMMUState *s, uint32_t level) (1ULL << (level - 2 + VTD_CAP_SAGAW_SHIFT)); } +static inline bool vtd_is_fl_level_supported(IntelIOMMUState *s, uint32_t level) +{ + return level == VTD_PML4_LEVEL; +} + /* Return true if check passed, otherwise false */ static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu, VTDPASIDEntry *pe) @@ -838,6 +845,11 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, return -VTD_FR_PASID_TABLE_ENTRY_INV; } + if (pgtt == VTD_SM_PASID_ENTRY_FLT && + !vtd_is_fl_level_supported(s, VTD_PE_GET_FL_LEVEL(pe))) { + return -VTD_FR_PASID_TABLE_ENTRY_INV; + } + return 0; } @@ -973,7 +985,11 @@ static uint32_t vtd_get_iova_level(IntelIOMMUState *s, if (s->root_scalable) { vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); - return VTD_PE_GET_SL_LEVEL(&pe); + if (s->flts) { + return VTD_PE_GET_FL_LEVEL(&pe); + } else { + return VTD_PE_GET_SL_LEVEL(&pe); + } } return vtd_ce_get_level(ce); @@ -1060,7 +1076,11 @@ static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s, if (s->root_scalable) { vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); - return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR; + if (s->flts) { + return pe.val[2] & VTD_SM_PASID_ENTRY_FLPTPTR; + } else { + return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR; + } } return vtd_ce_get_slpt_base(ce); @@ -1800,6 +1820,12 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_PASID_TABLE_ACCESS_ERR] = false, [VTD_FR_PASID_ENTRY_P] = true, [VTD_FR_PASID_TABLE_ENTRY_INV] = true, + [VTD_FR_FS_PAGING_ENTRY_INV] = true, + [VTD_FR_FS_PAGING_ENTRY_P] = true, + [VTD_FR_FS_PAGING_ENTRY_RSVD] = true, + [VTD_FR_PASID_ENTRY_FSPTPTR_INV] = true, + [VTD_FR_FS_PAGING_ENTRY_US] = true, + [VTD_FR_SM_WRITE] = true, [VTD_FR_SM_INTERRUPT_ADDR] = true, [VTD_FR_MAX] = false, }; @@ -1862,6 +1888,113 @@ out: trace_vtd_pt_enable_fast_path(source_id, success); } +/* + * Rsvd field masks for fpte: + * vtd_fpte_rsvd 4k pages + * vtd_fpte_rsvd_large large pages + * + * We support only 4-level page tables. + */ +#define VTD_FPTE_RSVD_LEN 5 +static uint64_t vtd_fpte_rsvd[VTD_FPTE_RSVD_LEN]; +static uint64_t vtd_fpte_rsvd_large[VTD_FPTE_RSVD_LEN]; + +static bool vtd_flpte_nonzero_rsvd(uint64_t flpte, uint32_t level) +{ + uint64_t rsvd_mask; + + /* + * We should have caught a guest-mis-programmed level earlier, + * via vtd_is_fl_level_supported. + */ + assert(level < VTD_FPTE_RSVD_LEN); + /* + * Zero level doesn't exist. The smallest level is VTD_PT_LEVEL=1 and + * checked by vtd_is_last_pte(). + */ + assert(level); + + if ((level == VTD_PD_LEVEL || level == VTD_PDP_LEVEL) && + (flpte & VTD_PT_PAGE_SIZE_MASK)) { + /* large page */ + rsvd_mask = vtd_fpte_rsvd_large[level]; + } else { + rsvd_mask = vtd_fpte_rsvd[level]; + } + + return flpte & rsvd_mask; +} + +static inline bool vtd_flpte_present(uint64_t flpte) +{ + return !!(flpte & VTD_FL_P); +} + +/* + * Given the @iova, get relevant @flptep. @flpte_level will be the last level + * of the translation, can be used for deciding the size of large page. + */ +static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, + uint64_t iova, bool is_write, + uint64_t *flptep, uint32_t *flpte_level, + bool *reads, bool *writes, uint8_t aw_bits, + uint32_t pasid) +{ + dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); + uint32_t level = vtd_get_iova_level(s, ce, pasid); + uint32_t offset; + uint64_t flpte; + + while (true) { + offset = vtd_iova_level_offset(iova, level); + flpte = vtd_get_pte(addr, offset); + + if (flpte == (uint64_t)-1) { + if (level == vtd_get_iova_level(s, ce, pasid)) { + /* Invalid programming of pasid-entry */ + return -VTD_FR_PASID_ENTRY_FSPTPTR_INV; + } else { + return -VTD_FR_FS_PAGING_ENTRY_INV; + } + } + + if (!vtd_flpte_present(flpte)) { + *reads = false; + *writes = false; + return -VTD_FR_FS_PAGING_ENTRY_P; + } + + /* No emulated device supports supervisor privilege request yet */ + if (!(flpte & VTD_FL_US)) { + *reads = false; + *writes = false; + return -VTD_FR_FS_PAGING_ENTRY_US; + } + + *reads = true; + *writes = (*writes) && (flpte & VTD_FL_RW); + if (is_write && !(flpte & VTD_FL_RW)) { + return -VTD_FR_SM_WRITE; + } + if (vtd_flpte_nonzero_rsvd(flpte, level)) { + error_report_once("%s: detected flpte reserved non-zero " + "iova=0x%" PRIx64 ", level=0x%" PRIx32 + "flpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")", + __func__, iova, level, flpte, pasid); + return -VTD_FR_FS_PAGING_ENTRY_RSVD; + } + + if (vtd_is_last_pte(flpte, level)) { + *flptep = flpte; + *flpte_level = level; + return 0; + } + + addr = vtd_get_pte_addr(flpte, aw_bits); + level--; + } +} + static void vtd_report_fault(IntelIOMMUState *s, int err, bool is_fpd_set, uint16_t source_id, @@ -2010,8 +2143,13 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, } } - ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level, - &reads, &writes, s->aw_bits, pasid); + if (s->flts && s->root_scalable) { + ret_fr = vtd_iova_to_flpte(s, &ce, addr, is_write, &pte, &level, + &reads, &writes, s->aw_bits, pasid); + } else { + ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level, + &reads, &writes, s->aw_bits, pasid); + } if (ret_fr) { vtd_report_fault(s, -ret_fr, is_fpd_set, source_id, addr, is_write, pasid != PCI_NO_PASID, pasid); @@ -4286,6 +4424,18 @@ static void vtd_init(IntelIOMMUState *s) vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits, x86_iommu->dt_supported && s->stale_tm); + /* + * Rsvd field masks for fpte + */ + vtd_fpte_rsvd[0] = ~0ULL; + vtd_fpte_rsvd[1] = VTD_FPTE_PAGE_L1_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd[2] = VTD_FPTE_PAGE_L2_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd[3] = VTD_FPTE_PAGE_L3_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd[4] = VTD_FPTE_PAGE_L4_RSVD_MASK(s->aw_bits); + + vtd_fpte_rsvd_large[2] = VTD_FPTE_LPAGE_L2_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd_large[3] = VTD_FPTE_LPAGE_L3_RSVD_MASK(s->aw_bits); + if (s->scalable_mode || s->snoop_control) { vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP; vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index e810b0071f..86d3354198 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -320,6 +320,15 @@ typedef enum VTDFaultReason { VTD_FR_PASID_ENTRY_P = 0x59, VTD_FR_PASID_TABLE_ENTRY_INV = 0x5b, /*Invalid PASID table entry */ + /* Fail to access a first-level paging entry (not FS_PML4E) */ + VTD_FR_FS_PAGING_ENTRY_INV = 0x70, + VTD_FR_FS_PAGING_ENTRY_P = 0x71, + /* Non-zero reserved field in present first-stage paging entry */ + VTD_FR_FS_PAGING_ENTRY_RSVD = 0x72, + VTD_FR_PASID_ENTRY_FSPTPTR_INV = 0x73, /* Invalid FSPTPTR in PASID entry */ + VTD_FR_FS_PAGING_ENTRY_US = 0x81, /* Privilege violation */ + VTD_FR_SM_WRITE = 0x85, /* No write permission */ + /* Output address in the interrupt address range for scalable mode */ VTD_FR_SM_INTERRUPT_ADDR = 0x87, VTD_FR_MAX, /* Guard */ @@ -438,6 +447,22 @@ typedef union VTDInvDesc VTDInvDesc; (0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \ (0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM)) +/* Rsvd field masks for fpte */ +#define VTD_FS_UPPER_IGNORED 0xfff0000000000000ULL +#define VTD_FPTE_PAGE_L1_RSVD_MASK(aw) \ + (~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_PAGE_L2_RSVD_MASK(aw) \ + (~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_PAGE_L3_RSVD_MASK(aw) \ + (~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_PAGE_L4_RSVD_MASK(aw) \ + (0x80ULL | ~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) + +#define VTD_FPTE_LPAGE_L2_RSVD_MASK(aw) \ + (0x1fe000ULL | ~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_LPAGE_L3_RSVD_MASK(aw) \ + (0x3fffe000ULL | ~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) + /* Masks for PIOTLB Invalidate Descriptor */ #define VTD_INV_DESC_PIOTLB_G (3ULL << 4) #define VTD_INV_DESC_PIOTLB_ALL_IN_PASID (2ULL << 4) @@ -530,6 +555,15 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_SM_PASID_ENTRY_AW 7ULL /* Adjusted guest-address-width */ #define VTD_SM_PASID_ENTRY_DID(val) ((val) & VTD_DOMAIN_ID_MASK) +#define VTD_SM_PASID_ENTRY_FLPM 3ULL +#define VTD_SM_PASID_ENTRY_FLPTPTR (~0xfffULL) + +/* First Level Paging Structure */ +/* Masks for First Level Paging Entry */ +#define VTD_FL_P 1ULL +#define VTD_FL_RW (1ULL << 1) +#define VTD_FL_US (1ULL << 2) + /* Second Level Page Translation Pointer*/ #define VTD_SM_PASID_ENTRY_SLPTPTR (~0xfffULL) From 305e469b7188e5f1a896c40853d84fa158ee6ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= Date: Thu, 12 Dec 2024 16:37:44 +0800 Subject: [PATCH 1029/2892] intel_iommu: Check if the input address is canonical MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage-1 translation must fail if the address to translate is not canonical. Signed-off-by: Clément Mathieu--Drif Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Reviewed-by: Yi Liu Message-Id: <20241212083757.605022-8-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 23 +++++++++++++++++++++++ hw/i386/intel_iommu_internal.h | 1 + 2 files changed, 24 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index bd6de71c02..3959fe44c7 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1824,6 +1824,7 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_FS_PAGING_ENTRY_P] = true, [VTD_FR_FS_PAGING_ENTRY_RSVD] = true, [VTD_FR_PASID_ENTRY_FSPTPTR_INV] = true, + [VTD_FR_FS_NON_CANONICAL] = true, [VTD_FR_FS_PAGING_ENTRY_US] = true, [VTD_FR_SM_WRITE] = true, [VTD_FR_SM_INTERRUPT_ADDR] = true, @@ -1930,6 +1931,22 @@ static inline bool vtd_flpte_present(uint64_t flpte) return !!(flpte & VTD_FL_P); } +/* Return true if IOVA is canonical, otherwise false. */ +static bool vtd_iova_fl_check_canonical(IntelIOMMUState *s, uint64_t iova, + VTDContextEntry *ce, uint32_t pasid) +{ + uint64_t iova_limit = vtd_iova_limit(s, ce, s->aw_bits, pasid); + uint64_t upper_bits_mask = ~(iova_limit - 1); + uint64_t upper_bits = iova & upper_bits_mask; + bool msb = ((iova & (iova_limit >> 1)) != 0); + + if (msb) { + return upper_bits == upper_bits_mask; + } else { + return !upper_bits; + } +} + /* * Given the @iova, get relevant @flptep. @flpte_level will be the last level * of the translation, can be used for deciding the size of large page. @@ -1945,6 +1962,12 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, uint32_t offset; uint64_t flpte; + if (!vtd_iova_fl_check_canonical(s, iova, ce, pasid)) { + error_report_once("%s: detected non canonical IOVA (iova=0x%" PRIx64 "," + "pasid=0x%" PRIx32 ")", __func__, iova, pasid); + return -VTD_FR_FS_NON_CANONICAL; + } + while (true) { offset = vtd_iova_level_offset(iova, level); flpte = vtd_get_pte(addr, offset); diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 86d3354198..3e7365dfff 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -326,6 +326,7 @@ typedef enum VTDFaultReason { /* Non-zero reserved field in present first-stage paging entry */ VTD_FR_FS_PAGING_ENTRY_RSVD = 0x72, VTD_FR_PASID_ENTRY_FSPTPTR_INV = 0x73, /* Invalid FSPTPTR in PASID entry */ + VTD_FR_FS_NON_CANONICAL = 0x80, /* SNG.1 : Address for FS not canonical.*/ VTD_FR_FS_PAGING_ENTRY_US = 0x81, /* Privilege violation */ VTD_FR_SM_WRITE = 0x85, /* No write permission */ From fed51ee5e02d8779ccbdce555e556c4ada321281 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:45 +0800 Subject: [PATCH 1030/2892] intel_iommu: Check stage-1 translation result with interrupt range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per VT-d spec 4.1 section 3.15, "Untranslated requests and translation requests that result in an address in the interrupt range will be blocked with condition code LGN.4 or SGN.8." This applies to both stage-1 and stage-2 IOMMU page table, move the check from vtd_iova_to_slpte() to vtd_do_iommu_translate() so stage-1 page table could also be checked. By this chance, update the comment with correct section number. Suggested-by: Yi Liu Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Acked-by: Jason Wang Message-Id: <20241212083757.605022-9-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 48 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 3959fe44c7..d53ce01e82 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1138,7 +1138,6 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, uint32_t offset; uint64_t slpte; uint64_t access_right_check; - uint64_t xlat, size; if (!vtd_iova_sl_range_check(s, iova, ce, aw_bits, pasid)) { error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 "," @@ -1191,28 +1190,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, level--; } - xlat = vtd_get_pte_addr(*slptep, aw_bits); - size = ~vtd_pt_level_page_mask(level) + 1; - - /* - * From VT-d spec 3.14: Untranslated requests and translation - * requests that result in an address in the interrupt range will be - * blocked with condition code LGN.4 or SGN.8. - */ - if ((xlat > VTD_INTERRUPT_ADDR_LAST || - xlat + size - 1 < VTD_INTERRUPT_ADDR_FIRST)) { - return 0; - } else { - error_report_once("%s: xlat address is in interrupt range " - "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " - "slpte=0x%" PRIx64 ", write=%d, " - "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", " - "pasid=0x%" PRIx32 ")", - __func__, iova, level, slpte, is_write, - xlat, size, pasid); - return s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR : - -VTD_FR_INTERRUPT_ADDR; - } + return 0; } typedef int (*vtd_page_walk_hook)(const IOMMUTLBEvent *event, void *private); @@ -2064,6 +2042,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t access_flags; bool rid2pasid = (pasid == PCI_NO_PASID) && s->root_scalable; VTDIOTLBEntry *iotlb_entry; + uint64_t xlat, size; /* * We have standalone memory region for interrupt addresses, we @@ -2173,6 +2152,29 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level, &reads, &writes, s->aw_bits, pasid); } + if (!ret_fr) { + xlat = vtd_get_pte_addr(pte, s->aw_bits); + size = ~vtd_pt_level_page_mask(level) + 1; + + /* + * Per VT-d spec 4.1 section 3.15: Untranslated requests and translation + * requests that result in an address in the interrupt range will be + * blocked with condition code LGN.4 or SGN.8. + */ + if ((xlat <= VTD_INTERRUPT_ADDR_LAST && + xlat + size - 1 >= VTD_INTERRUPT_ADDR_FIRST)) { + error_report_once("%s: xlat address is in interrupt range " + "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " + "pte=0x%" PRIx64 ", write=%d, " + "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", " + "pasid=0x%" PRIx32 ")", + __func__, addr, level, pte, is_write, + xlat, size, pasid); + ret_fr = s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR : + -VTD_FR_INTERRUPT_ADDR; + } + } + if (ret_fr) { vtd_report_fault(s, -ret_fr, is_fpd_set, source_id, addr, is_write, pasid != PCI_NO_PASID, pasid); From 65c4f0999991f6321d6a369fa56c81c57c5b87ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= Date: Thu, 12 Dec 2024 16:37:46 +0800 Subject: [PATCH 1031/2892] intel_iommu: Set accessed and dirty bits during stage-1 translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Mathieu--Drif Signed-off-by: Zhenzhong Duan Reviewed-by: Yi Liu Acked-by: Jason Wang Message-Id: <20241212083757.605022-10-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 25 ++++++++++++++++++++++++- hw/i386/intel_iommu_internal.h | 3 +++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index d53ce01e82..0aeb0dbde9 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1806,6 +1806,7 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_FS_PAGING_ENTRY_US] = true, [VTD_FR_SM_WRITE] = true, [VTD_FR_SM_INTERRUPT_ADDR] = true, + [VTD_FR_FS_BIT_UPDATE_FAILED] = true, [VTD_FR_MAX] = false, }; @@ -1925,6 +1926,20 @@ static bool vtd_iova_fl_check_canonical(IntelIOMMUState *s, uint64_t iova, } } +static MemTxResult vtd_set_flag_in_pte(dma_addr_t base_addr, uint32_t index, + uint64_t pte, uint64_t flag) +{ + if (pte & flag) { + return MEMTX_OK; + } + pte |= flag; + pte = cpu_to_le64(pte); + return dma_memory_write(&address_space_memory, + base_addr + index * sizeof(pte), + &pte, sizeof(pte), + MEMTXATTRS_UNSPECIFIED); +} + /* * Given the @iova, get relevant @flptep. @flpte_level will be the last level * of the translation, can be used for deciding the size of large page. @@ -1938,7 +1953,7 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); uint32_t level = vtd_get_iova_level(s, ce, pasid); uint32_t offset; - uint64_t flpte; + uint64_t flpte, flag_ad = VTD_FL_A; if (!vtd_iova_fl_check_canonical(s, iova, ce, pasid)) { error_report_once("%s: detected non canonical IOVA (iova=0x%" PRIx64 "," @@ -1985,6 +2000,14 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, return -VTD_FR_FS_PAGING_ENTRY_RSVD; } + if (vtd_is_last_pte(flpte, level) && is_write) { + flag_ad |= VTD_FL_D; + } + + if (vtd_set_flag_in_pte(addr, offset, flpte, flag_ad) != MEMTX_OK) { + return -VTD_FR_FS_BIT_UPDATE_FAILED; + } + if (vtd_is_last_pte(flpte, level)) { *flptep = flpte; *flpte_level = level; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 3e7365dfff..22dd3faf0c 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -332,6 +332,7 @@ typedef enum VTDFaultReason { /* Output address in the interrupt address range for scalable mode */ VTD_FR_SM_INTERRUPT_ADDR = 0x87, + VTD_FR_FS_BIT_UPDATE_FAILED = 0x91, /* SFS.10 */ VTD_FR_MAX, /* Guard */ } VTDFaultReason; @@ -564,6 +565,8 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_FL_P 1ULL #define VTD_FL_RW (1ULL << 1) #define VTD_FL_US (1ULL << 2) +#define VTD_FL_A (1ULL << 5) +#define VTD_FL_D (1ULL << 6) /* Second Level Page Translation Pointer*/ #define VTD_SM_PASID_ENTRY_SLPTPTR (~0xfffULL) From 16d4e418e98feeb53f28d17eeffb198fe0fd6f22 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:47 +0800 Subject: [PATCH 1032/2892] intel_iommu: Flush stage-1 cache in iotlb invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to spec, Page-Selective-within-Domain Invalidation (11b): 1. IOTLB entries caching second-stage mappings (PGTT=010b) or pass-through (PGTT=100b) mappings associated with the specified domain-id and the input-address range are invalidated. 2. IOTLB entries caching first-stage (PGTT=001b) or nested (PGTT=011b) mapping associated with specified domain-id are invalidated. So per spec definition the Page-Selective-within-Domain Invalidation needs to flush first stage and nested cached IOTLB entries as well. We don't support nested yet and pass-through mapping is never cached, so what in iotlb cache are only first-stage and second-stage mappings. Add a tag pgtt in VTDIOTLBEntry to mark PGTT type of the mapping and invalidate entries based on PGTT type. Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Acked-by: Jason Wang Reviewed-by: Yi Liu Message-Id: <20241212083757.605022-11-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 27 +++++++++++++++++++++------ include/hw/i386/intel_iommu.h | 1 + 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0aeb0dbde9..95f344eb46 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -305,9 +305,21 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask; uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K; - return (entry->domain_id == info->domain_id) && - (((entry->gfn & info->mask) == gfn) || - (entry->gfn == gfn_tlb)); + + if (entry->domain_id != info->domain_id) { + return false; + } + + /* + * According to spec, IOTLB entries caching first-stage (PGTT=001b) or + * nested (PGTT=011b) mapping associated with specified domain-id are + * invalidated. Nested isn't supported yet, so only need to check 001b. + */ + if (entry->pgtt == VTD_SM_PASID_ENTRY_FLT) { + return true; + } + + return (entry->gfn & info->mask) == gfn || entry->gfn == gfn_tlb; } /* Reset all the gen of VTDAddressSpace to zero and set the gen of @@ -382,7 +394,7 @@ out: static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t pte, uint8_t access_flags, uint32_t level, - uint32_t pasid) + uint32_t pasid, uint8_t pgtt) { VTDIOTLBEntry *entry = g_malloc(sizeof(*entry)); struct vtd_iotlb_key *key = g_malloc(sizeof(*key)); @@ -400,6 +412,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, entry->access_flags = access_flags; entry->mask = vtd_pt_level_page_mask(level); entry->pasid = pasid; + entry->pgtt = pgtt; key->gfn = gfn; key->sid = source_id; @@ -2062,7 +2075,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, bool is_fpd_set = false; bool reads = true; bool writes = true; - uint8_t access_flags; + uint8_t access_flags, pgtt; bool rid2pasid = (pasid == PCI_NO_PASID) && s->root_scalable; VTDIOTLBEntry *iotlb_entry; uint64_t xlat, size; @@ -2171,9 +2184,11 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, if (s->flts && s->root_scalable) { ret_fr = vtd_iova_to_flpte(s, &ce, addr, is_write, &pte, &level, &reads, &writes, s->aw_bits, pasid); + pgtt = VTD_SM_PASID_ENTRY_FLT; } else { ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level, &reads, &writes, s->aw_bits, pasid); + pgtt = VTD_SM_PASID_ENTRY_SLT; } if (!ret_fr) { xlat = vtd_get_pte_addr(pte, s->aw_bits); @@ -2207,7 +2222,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, page_mask = vtd_pt_level_page_mask(level); access_flags = IOMMU_ACCESS_FLAG(reads, writes); vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce, pasid), - addr, pte, access_flags, level, pasid); + addr, pte, access_flags, level, pasid, pgtt); out: vtd_iommu_unlock(s); entry->iova = addr & page_mask; diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index f44f3eb63a..a434c2489c 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -155,6 +155,7 @@ struct VTDIOTLBEntry { uint64_t pte; uint64_t mask; uint8_t access_flags; + uint8_t pgtt; }; /* VT-d Source-ID Qualifier types */ From 6ebe6cf2a0663f46f94a69eca5296f608da12f2e Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:48 +0800 Subject: [PATCH 1033/2892] intel_iommu: Process PASID-based iotlb invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PASID-based iotlb (piotlb) is used during walking Intel VT-d stage-1 page table. This emulates the stage-1 page table iotlb invalidation requested by a PASID-based IOTLB Invalidate Descriptor (P_IOTLB). Signed-off-by: Yi Liu Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Acked-by: Jason Wang Reviewed-by: Yi Liu Message-Id: <20241212083757.605022-12-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 43 ++++++++++++++++++++++++++++++++++ hw/i386/intel_iommu_internal.h | 3 +++ 2 files changed, 46 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 95f344eb46..c45a486bf8 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -322,6 +322,28 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, return (entry->gfn & info->mask) == gfn || entry->gfn == gfn_tlb; } +static gboolean vtd_hash_remove_by_page_piotlb(gpointer key, gpointer value, + gpointer user_data) +{ + VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value; + VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; + uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask; + uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K; + + /* + * According to spec, PASID-based-IOTLB Invalidation in page granularity + * doesn't invalidate IOTLB entries caching second-stage (PGTT=010b) + * or pass-through (PGTT=100b) mappings. Nested isn't supported yet, + * so only need to check first-stage (PGTT=001b) mappings. + */ + if (entry->pgtt != VTD_SM_PASID_ENTRY_FLT) { + return false; + } + + return entry->domain_id == info->domain_id && entry->pasid == info->pasid && + ((entry->gfn & info->mask) == gfn || entry->gfn == gfn_tlb); +} + /* Reset all the gen of VTDAddressSpace to zero and set the gen of * IntelIOMMUState to 1. Must be called with IOMMU lock held. */ @@ -2937,11 +2959,29 @@ static void vtd_piotlb_pasid_invalidate(IntelIOMMUState *s, } } +static void vtd_piotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, + uint32_t pasid, hwaddr addr, uint8_t am) +{ + VTDIOTLBPageInvInfo info; + + info.domain_id = domain_id; + info.pasid = pasid; + info.addr = addr; + info.mask = ~((1 << am) - 1); + + vtd_iommu_lock(s); + g_hash_table_foreach_remove(s->iotlb, + vtd_hash_remove_by_page_piotlb, &info); + vtd_iommu_unlock(s); +} + static bool vtd_process_piotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { uint16_t domain_id; uint32_t pasid; + hwaddr addr; + uint8_t am; uint64_t mask[4] = {VTD_INV_DESC_PIOTLB_RSVD_VAL0, VTD_INV_DESC_PIOTLB_RSVD_VAL1, VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; @@ -2959,6 +2999,9 @@ static bool vtd_process_piotlb_desc(IntelIOMMUState *s, break; case VTD_INV_DESC_PIOTLB_PSI_IN_PASID: + am = VTD_INV_DESC_PIOTLB_AM(inv_desc->val[1]); + addr = (hwaddr) VTD_INV_DESC_PIOTLB_ADDR(inv_desc->val[1]); + vtd_piotlb_page_invalidate(s, domain_id, pasid, addr, am); break; default: diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 22dd3faf0c..5e4e563e62 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -471,6 +471,9 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_PIOTLB_PSI_IN_PASID (3ULL << 4) #define VTD_INV_DESC_PIOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) #define VTD_INV_DESC_PIOTLB_PASID(val) (((val) >> 32) & 0xfffffULL) +#define VTD_INV_DESC_PIOTLB_AM(val) ((val) & 0x3fULL) +#define VTD_INV_DESC_PIOTLB_IH(val) (((val) >> 6) & 0x1) +#define VTD_INV_DESC_PIOTLB_ADDR(val) ((val) & ~0xfffULL) #define VTD_INV_DESC_PIOTLB_RSVD_VAL0 0xfff000000000f1c0ULL #define VTD_INV_DESC_PIOTLB_RSVD_VAL1 0xf80ULL From 1075645d430e291404d19c88fc80d989669fa4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= Date: Thu, 12 Dec 2024 16:37:49 +0800 Subject: [PATCH 1034/2892] intel_iommu: Add an internal API to find an address space with PASID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used to implement the device IOTLB invalidation Signed-off-by: Clément Mathieu--Drif Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Reviewed-by: Yi Liu Message-Id: <20241212083757.605022-13-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index c45a486bf8..9aa807593e 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -70,6 +70,11 @@ struct vtd_hiod_key { uint8_t devfn; }; +struct vtd_as_raw_key { + uint16_t sid; + uint32_t pasid; +}; + struct vtd_iotlb_key { uint64_t gfn; uint32_t pasid; @@ -1859,29 +1864,32 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) return VTD_INTERRUPT_ADDR_FIRST <= addr && addr <= VTD_INTERRUPT_ADDR_LAST; } -static gboolean vtd_find_as_by_sid(gpointer key, gpointer value, - gpointer user_data) +static gboolean vtd_find_as_by_sid_and_pasid(gpointer key, gpointer value, + gpointer user_data) { struct vtd_as_key *as_key = (struct vtd_as_key *)key; - uint16_t target_sid = *(uint16_t *)user_data; + struct vtd_as_raw_key *target = (struct vtd_as_raw_key *)user_data; uint16_t sid = PCI_BUILD_BDF(pci_bus_num(as_key->bus), as_key->devfn); - return sid == target_sid; + + return (as_key->pasid == target->pasid) && (sid == target->sid); +} + +static VTDAddressSpace *vtd_get_as_by_sid_and_pasid(IntelIOMMUState *s, + uint16_t sid, + uint32_t pasid) +{ + struct vtd_as_raw_key key = { + .sid = sid, + .pasid = pasid + }; + + return g_hash_table_find(s->vtd_address_spaces, + vtd_find_as_by_sid_and_pasid, &key); } static VTDAddressSpace *vtd_get_as_by_sid(IntelIOMMUState *s, uint16_t sid) { - uint8_t bus_num = PCI_BUS_NUM(sid); - VTDAddressSpace *vtd_as = s->vtd_as_cache[bus_num]; - - if (vtd_as && - (sid == PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn))) { - return vtd_as; - } - - vtd_as = g_hash_table_find(s->vtd_address_spaces, vtd_find_as_by_sid, &sid); - s->vtd_as_cache[bus_num] = vtd_as; - - return vtd_as; + return vtd_get_as_by_sid_and_pasid(s, sid, PCI_NO_PASID); } static void vtd_pt_enable_fast_path(IntelIOMMUState *s, uint16_t source_id) From dd8200503e230a4e32f930f5a57556b04651c16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= Date: Thu, 12 Dec 2024 16:37:50 +0800 Subject: [PATCH 1035/2892] intel_iommu: Add support for PASID-based device IOTLB invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Mathieu--Drif Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Message-Id: <20241212083757.605022-14-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 50 ++++++++++++++++++++++++++++++++++ hw/i386/intel_iommu_internal.h | 11 ++++++++ 2 files changed, 61 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 9aa807593e..5634a37a74 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3075,6 +3075,49 @@ static void do_invalidate_device_tlb(VTDAddressSpace *vtd_dev_as, memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); } +static bool vtd_process_device_piotlb_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + uint16_t sid; + VTDAddressSpace *vtd_dev_as; + bool size; + bool global; + hwaddr addr; + uint32_t pasid; + uint64_t mask[4] = {VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL0, + VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL1, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, true, + __func__, "device piotlb inv")) { + return false; + } + + global = VTD_INV_DESC_PASID_DEVICE_IOTLB_GLOBAL(inv_desc->hi); + size = VTD_INV_DESC_PASID_DEVICE_IOTLB_SIZE(inv_desc->hi); + addr = VTD_INV_DESC_PASID_DEVICE_IOTLB_ADDR(inv_desc->hi); + sid = VTD_INV_DESC_PASID_DEVICE_IOTLB_SID(inv_desc->lo); + if (global) { + QLIST_FOREACH(vtd_dev_as, &s->vtd_as_with_notifiers, next) { + if ((vtd_dev_as->pasid != PCI_NO_PASID) && + (PCI_BUILD_BDF(pci_bus_num(vtd_dev_as->bus), + vtd_dev_as->devfn) == sid)) { + do_invalidate_device_tlb(vtd_dev_as, size, addr); + } + } + } else { + pasid = VTD_INV_DESC_PASID_DEVICE_IOTLB_PASID(inv_desc->lo); + vtd_dev_as = vtd_get_as_by_sid_and_pasid(s, sid, pasid); + if (!vtd_dev_as) { + return true; + } + + do_invalidate_device_tlb(vtd_dev_as, size, addr); + } + + return true; +} + static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { @@ -3161,6 +3204,13 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + case VTD_INV_DESC_DEV_PIOTLB: + trace_vtd_inv_desc("device-piotlb", inv_desc.hi, inv_desc.lo); + if (!vtd_process_device_piotlb_desc(s, &inv_desc)) { + return false; + } + break; + case VTD_INV_DESC_DEVICE: trace_vtd_inv_desc("device", inv_desc.hi, inv_desc.lo); if (!vtd_process_device_iotlb_desc(s, &inv_desc)) { diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 5e4e563e62..2c977aa7da 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -385,6 +385,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_WAIT 0x5 /* Invalidation Wait Descriptor */ #define VTD_INV_DESC_PIOTLB 0x6 /* PASID-IOTLB Invalidate Desc */ #define VTD_INV_DESC_PC 0x7 /* PASID-cache Invalidate Desc */ +#define VTD_INV_DESC_DEV_PIOTLB 0x8 /* PASID-based-DIOTLB inv_desc*/ #define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */ /* Masks for Invalidation Wait Descriptor*/ @@ -426,6 +427,16 @@ typedef union VTDInvDesc VTDInvDesc; /* Masks for Interrupt Entry Invalidate Descriptor */ #define VTD_INV_DESC_IEC_RSVD 0xffff000007fff1e0ULL +/* Masks for PASID based Device IOTLB Invalidate Descriptor */ +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_ADDR(val) ((val) & \ + 0xfffffffffffff000ULL) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_SIZE(val) ((val >> 11) & 0x1) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_GLOBAL(val) ((val) & 0x1) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_SID(val) (((val) >> 16) & 0xffffULL) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_PASID(val) ((val >> 32) & 0xfffffULL) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL0 0xfff000000000f000ULL +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL1 0x7feULL + /* Rsvd field masks for spte */ #define VTD_SPTE_SNP 0x800ULL From 1ab93575bdcb2972c99a174a957b598f7457a899 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:51 +0800 Subject: [PATCH 1036/2892] intel_iommu: piotlb invalidation should notify unmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is used by some emulated devices which caches address translation result. When piotlb invalidation issued in guest, those caches should be refreshed. There is already a similar implementation in iotlb invalidation. So update vtd_iotlb_page_invalidate_notify() to make it work also for piotlb invalidation. For device that does not implement ATS capability or disable it but still caches the translation result, it is better to implement ATS cap or enable it if there is need to cache the translation result. Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Reviewed-by: Clément Mathieu--Drif Message-Id: <20241212083757.605022-15-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 5634a37a74..7d4b99523d 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2450,8 +2450,13 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) } } +/* + * There is no pasid field in iotlb invalidation descriptor, so PCI_NO_PASID + * is passed as parameter. Piotlb invalidation supports pasid, pasid in its + * descriptor is passed which should not be PCI_NO_PASID. + */ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, - uint16_t domain_id, hwaddr addr, + uint16_t domain_id, hwaddr addr, uint8_t am, uint32_t pasid) { VTDAddressSpace *vtd_as; @@ -2460,19 +2465,37 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, hwaddr size = (1 << am) * VTD_PAGE_SIZE; QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { - if (pasid != PCI_NO_PASID && pasid != vtd_as->pasid) { - continue; - } ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) { + uint32_t rid2pasid = PCI_NO_PASID; + + if (s->root_scalable) { + rid2pasid = VTD_CE_GET_RID2PASID(&ce); + } + + /* + * In legacy mode, vtd_as->pasid == pasid is always true. + * In scalable mode, for vtd address space backing a PCI + * device without pasid, needs to compare pasid with + * rid2pasid of this device. + */ + if (!(vtd_as->pasid == pasid || + (vtd_as->pasid == PCI_NO_PASID && pasid == rid2pasid))) { + continue; + } + if (vtd_as_has_map_notifier(vtd_as)) { /* - * As long as we have MAP notifications registered in - * any of our IOMMU notifiers, we need to sync the - * shadow page table. + * When stage-1 translation is off, as long as we have MAP + * notifications registered in any of our IOMMU notifiers, + * we need to sync the shadow page table. Otherwise VFIO + * device attaches to nested page table instead of shadow + * page table, so no need to sync. */ - vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + if (!s->flts || !s->root_scalable) { + vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + } } else { /* * For UNMAP-only notifiers, we don't need to walk the @@ -2960,7 +2983,7 @@ static void vtd_piotlb_pasid_invalidate(IntelIOMMUState *s, continue; } - if (!s->flts) { + if (!s->flts || !vtd_as_has_map_notifier(vtd_as)) { vtd_address_space_sync(vtd_as); } } @@ -2981,6 +3004,8 @@ static void vtd_piotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page_piotlb, &info); vtd_iommu_unlock(s); + + vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am, pasid); } static bool vtd_process_piotlb_desc(IntelIOMMUState *s, From 9609d7101867819086516c7df845337dcee1cf08 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:52 +0800 Subject: [PATCH 1037/2892] tests/acpi: q35: allow DMAR acpi table changes Signed-off-by: Zhenzhong Duan Acked-by: Jason Wang Message-Id: <20241212083757.605022-16-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..46f80be9ca 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/x86/q35/DMAR.dmar", From ddd84fd0c1f8f62e8384591f7203aaabb935199a Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:53 +0800 Subject: [PATCH 1038/2892] intel_iommu: Set default aw_bits to 48 starting from QEMU 9.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to VTD spec, stage-1 page table could support 4-level and 5-level paging. However, 5-level paging translation emulation is unsupported yet. That means the only supported value for aw_bits is 48. So default aw_bits to 48 when stage-1 translation is turned on. For legacy and scalable modes, 48 is the default choice for modern OS when both 48 and 39 are supported. So it makes sense to set default to 48 for these two modes too starting from QEMU 9.2. Use pc_compat_9_1 to handle the compatibility for machines before 9.2. Suggested-by: Jason Wang Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Reviewed-by: Yi Liu Acked-by: Jason Wang Message-Id: <20241212083757.605022-17-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 1 + include/hw/i386/intel_iommu.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 9334b033f6..b46975c8a4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -86,6 +86,7 @@ GlobalProperty pc_compat_9_1[] = { { "ICH9-LPC", "x-smi-swsmi-timer", "off" }, { "ICH9-LPC", "x-smi-periodic-timer", "off" }, { TYPE_INTEL_IOMMU_DEVICE, "stale-tm", "on" }, + { TYPE_INTEL_IOMMU_DEVICE, "aw-bits", "39" }, }; const size_t pc_compat_9_1_len = G_N_ELEMENTS(pc_compat_9_1); diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index a434c2489c..72428fefa4 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -45,7 +45,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(IntelIOMMUState, INTEL_IOMMU_DEVICE) #define DMAR_REG_SIZE 0x230 #define VTD_HOST_AW_39BIT 39 #define VTD_HOST_AW_48BIT 48 -#define VTD_HOST_ADDRESS_WIDTH VTD_HOST_AW_39BIT +#define VTD_HOST_ADDRESS_WIDTH VTD_HOST_AW_48BIT #define VTD_HAW_MASK(aw) ((1ULL << (aw)) - 1) #define DMAR_REPORT_F_INTR (1) From 81ab964f21620db32558277f220eb0d803c14109 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:54 +0800 Subject: [PATCH 1039/2892] tests/acpi: q35: Update host address width in DMAR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences: @@ -1,39 +1,39 @@ /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/x86/q35/DMAR.dmar, Mon Nov 11 15:31:18 2024 + * Disassembly of /tmp/aml-SPJ4W2, Mon Nov 11 15:31:18 2024 * * ACPI Data Table [DMAR] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "DMAR" [DMA Remapping table] [004h 0004 4] Table Length : 00000078 [008h 0008 1] Revision : 01 -[009h 0009 1] Checksum : 15 +[009h 0009 1] Checksum : 0C [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 1] Host Address Width : 26 +[024h 0036 1] Host Address Width : 2F [025h 0037 1] Flags : 01 [026h 0038 10] Reserved : 00 00 00 00 00 00 00 00 00 00 [030h 0048 2] Subtable Type : 0000 [Hardware Unit Definition] [032h 0050 2] Length : 0040 [034h 0052 1] Flags : 00 [035h 0053 1] Reserved : 00 [036h 0054 2] PCI Segment Number : 0000 [038h 0056 8] Register Base Address : 00000000FED90000 [040h 0064 1] Device Scope Type : 03 [IOAPIC Device] [041h 0065 1] Entry Length : 08 [042h 0066 2] Reserved : 0000 [044h 0068 1] Enumeration ID : 00 [045h 0069 1] PCI Bus Number : FF Signed-off-by: Zhenzhong Duan Acked-by: Clément Mathieu--Drif Message-Id: <20241212083757.605022-18-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/x86/q35/DMAR.dmar | Bin 120 -> 120 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/x86/q35/DMAR.dmar b/tests/data/acpi/x86/q35/DMAR.dmar index 0dca6e68ad8a8ca5b981bcfbc745385a63e9f216..0c05976715c6f2f6ec46ef6d37790f86a392b5ea 100644 GIT binary patch delta 21 ccmb=Z;BxVG460yYU|{5#$R)+7KT$Op05(qqk^lez delta 21 ccmb=Z;BxVG460yYU|Sg05*ICk^lez diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 46f80be9ca..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/x86/q35/DMAR.dmar", From aa68a9fbdb81c47c2a48a3199559df470c3d9eba Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:55 +0800 Subject: [PATCH 1040/2892] intel_iommu: Introduce a property x-flts for stage-1 translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intel VT-d 3.0 introduces scalable mode, and it has a bunch of capabilities related to scalable mode translation, thus there are multiple combinations. This vIOMMU implementation wants to simplify it with a new property "x-flts". When turned on in scalable mode, stage-1 translation is supported. When turned on in legacy mode, throw out error. With stage-1 translation support exposed to user, also accurate the pasid entry check in vtd_pe_type_check(). Suggested-by: Jason Wang Signed-off-by: Yi Liu Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Message-Id: <20241212083757.605022-19-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 28 +++++++++++++++++++--------- hw/i386/intel_iommu_internal.h | 2 ++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 7d4b99523d..0111186f7a 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -803,16 +803,18 @@ static inline bool vtd_is_fl_level_supported(IntelIOMMUState *s, uint32_t level) } /* Return true if check passed, otherwise false */ -static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu, - VTDPASIDEntry *pe) +static inline bool vtd_pe_type_check(IntelIOMMUState *s, VTDPASIDEntry *pe) { switch (VTD_PE_GET_TYPE(pe)) { - case VTD_SM_PASID_ENTRY_SLT: - return true; - case VTD_SM_PASID_ENTRY_PT: - return x86_iommu->pt_supported; case VTD_SM_PASID_ENTRY_FLT: + return !!(s->ecap & VTD_ECAP_FLTS); + case VTD_SM_PASID_ENTRY_SLT: + return !!(s->ecap & VTD_ECAP_SLTS); case VTD_SM_PASID_ENTRY_NESTED: + /* Not support NESTED page table type yet */ + return false; + case VTD_SM_PASID_ENTRY_PT: + return !!(s->ecap & VTD_ECAP_PT); default: /* Unknown type */ return false; @@ -861,7 +863,6 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, uint8_t pgtt; uint32_t index; dma_addr_t entry_size; - X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); index = VTD_PASID_TABLE_INDEX(pasid); entry_size = VTD_PASID_ENTRY_SIZE; @@ -875,7 +876,7 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, } /* Do translation type check */ - if (!vtd_pe_type_check(x86_iommu, pe)) { + if (!vtd_pe_type_check(s, pe)) { return -VTD_FR_PASID_TABLE_ENTRY_INV; } @@ -3827,6 +3828,7 @@ static const Property vtd_properties[] = { VTD_HOST_ADDRESS_WIDTH), DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE), DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE), + DEFINE_PROP_BOOL("x-flts", IntelIOMMUState, flts, FALSE), DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false), DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false), DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), @@ -4557,7 +4559,10 @@ static void vtd_cap_init(IntelIOMMUState *s) } /* TODO: read cap/ecap from host to decide which cap to be exposed. */ - if (s->scalable_mode) { + if (s->flts) { + s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_FLTS; + s->cap |= VTD_CAP_FS1GP; + } else if (s->scalable_mode) { s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_SRS | VTD_ECAP_SLTS; } @@ -4736,6 +4741,11 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) } } + if (!s->scalable_mode && s->flts) { + error_setg(errp, "x-flts is only available in scalable mode"); + return false; + } + if (!s->flts && s->aw_bits != VTD_HOST_AW_39BIT && s->aw_bits != VTD_HOST_AW_48BIT) { error_setg(errp, "%s: supported values for aw-bits are: %d, %d", diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 2c977aa7da..e8b211e8b0 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -195,6 +195,7 @@ #define VTD_ECAP_PASID (1ULL << 40) #define VTD_ECAP_SMTS (1ULL << 43) #define VTD_ECAP_SLTS (1ULL << 46) +#define VTD_ECAP_FLTS (1ULL << 47) /* CAP_REG */ /* (offset >> 4) << 24 */ @@ -211,6 +212,7 @@ #define VTD_CAP_SLLPS ((1ULL << 34) | (1ULL << 35)) #define VTD_CAP_DRAIN_WRITE (1ULL << 54) #define VTD_CAP_DRAIN_READ (1ULL << 55) +#define VTD_CAP_FS1GP (1ULL << 56) #define VTD_CAP_DRAIN (VTD_CAP_DRAIN_READ | VTD_CAP_DRAIN_WRITE) #define VTD_CAP_CM (1ULL << 7) #define VTD_PASID_ID_SHIFT 20 From d9d32478ed4543539322761c19a73edf5d0be059 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:56 +0800 Subject: [PATCH 1041/2892] intel_iommu: Introduce a property to control FS1GP cap bit setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives user flexibility to turn off FS1GP for debug purpose. It is also useful for future nesting feature. When host IOMMU doesn't support FS1GP but vIOMMU does, nested page table on host side works after turning FS1GP off in vIOMMU. This property has no effect when vIOMMU is in legacy mode or x-flts=off in scalable modme. Signed-off-by: Zhenzhong Duan Reviewed-by: Clément Mathieu--Drif Reviewed-by: Yi Liu Acked-by: Jason Wang Message-Id: <20241212083757.605022-20-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 5 ++++- include/hw/i386/intel_iommu.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0111186f7a..f366c223d0 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3834,6 +3834,7 @@ static const Property vtd_properties[] = { DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true), DEFINE_PROP_BOOL("stale-tm", IntelIOMMUState, stale_tm, false), + DEFINE_PROP_BOOL("fs1gp", IntelIOMMUState, fs1gp, true), }; /* Read IRTE entry with specific index */ @@ -4561,7 +4562,9 @@ static void vtd_cap_init(IntelIOMMUState *s) /* TODO: read cap/ecap from host to decide which cap to be exposed. */ if (s->flts) { s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_FLTS; - s->cap |= VTD_CAP_FS1GP; + if (s->fs1gp) { + s->cap |= VTD_CAP_FS1GP; + } } else if (s->scalable_mode) { s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_SRS | VTD_ECAP_SLTS; } diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 72428fefa4..9e92bffd5a 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -307,6 +307,7 @@ struct IntelIOMMUState { bool dma_drain; /* Whether DMA r/w draining enabled */ bool dma_translation; /* Whether DMA translation supported */ bool pasid; /* Whether to support PASID */ + bool fs1gp; /* First Stage 1-GByte Page Support */ /* Transient Mapping, Reserved(0) since VTD spec revision 3.2 */ bool stale_tm; From 2c746dfe1c69896b0e434d37efe014654f0a3a65 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 12 Dec 2024 16:37:57 +0800 Subject: [PATCH 1042/2892] tests/qtest: Add intel-iommu test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the framework to test the intel-iommu device. Currently only tested cap/ecap bits correctness when x-flts=on in scalable mode. Also tested cap/ecap bits consistency before and after system reset. Signed-off-by: Zhenzhong Duan Acked-by: Thomas Huth Reviewed-by: Clément Mathieu--Drif Acked-by: Jason Wang Message-Id: <20241212083757.605022-21-zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + include/hw/i386/intel_iommu.h | 1 + tests/qtest/intel-iommu-test.c | 64 ++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 4 files changed, 67 insertions(+) create mode 100644 tests/qtest/intel-iommu-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 8b9d9a7cac..a928ce3e41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3711,6 +3711,7 @@ F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h F: include/hw/i386/intel_iommu.h F: tests/functional/test_intel_iommu.py +F: tests/qtest/intel-iommu-test.c AMD-Vi Emulation S: Orphan diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 9e92bffd5a..e95477e855 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -47,6 +47,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(IntelIOMMUState, INTEL_IOMMU_DEVICE) #define VTD_HOST_AW_48BIT 48 #define VTD_HOST_ADDRESS_WIDTH VTD_HOST_AW_48BIT #define VTD_HAW_MASK(aw) ((1ULL << (aw)) - 1) +#define VTD_MGAW_FROM_CAP(cap) ((cap >> 16) & 0x3fULL) #define DMAR_REPORT_F_INTR (1) diff --git a/tests/qtest/intel-iommu-test.c b/tests/qtest/intel-iommu-test.c new file mode 100644 index 0000000000..c521b3796e --- /dev/null +++ b/tests/qtest/intel-iommu-test.c @@ -0,0 +1,64 @@ +/* + * QTest testcase for intel-iommu + * + * Copyright (c) 2024 Intel, Inc. + * + * Author: Zhenzhong Duan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/i386/intel_iommu_internal.h" + +#define CAP_STAGE_1_FIXED1 (VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | \ + VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS) +#define ECAP_STAGE_1_FIXED1 (VTD_ECAP_QI | VTD_ECAP_IR | VTD_ECAP_IRO | \ + VTD_ECAP_MHMV | VTD_ECAP_SMTS | VTD_ECAP_FLTS) + +static inline uint64_t vtd_reg_readq(QTestState *s, uint64_t offset) +{ + return qtest_readq(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset); +} + +static void test_intel_iommu_stage_1(void) +{ + uint8_t init_csr[DMAR_REG_SIZE]; /* register values */ + uint8_t post_reset_csr[DMAR_REG_SIZE]; /* register values */ + uint64_t cap, ecap, tmp; + QTestState *s; + + s = qtest_init("-M q35 -device intel-iommu,x-scalable-mode=on,x-flts=on"); + + cap = vtd_reg_readq(s, DMAR_CAP_REG); + g_assert((cap & CAP_STAGE_1_FIXED1) == CAP_STAGE_1_FIXED1); + + tmp = cap & VTD_CAP_SAGAW_MASK; + g_assert(tmp == (VTD_CAP_SAGAW_39bit | VTD_CAP_SAGAW_48bit)); + + tmp = VTD_MGAW_FROM_CAP(cap); + g_assert(tmp == VTD_HOST_AW_48BIT - 1); + + ecap = vtd_reg_readq(s, DMAR_ECAP_REG); + g_assert((ecap & ECAP_STAGE_1_FIXED1) == ECAP_STAGE_1_FIXED1); + + qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, init_csr, DMAR_REG_SIZE); + + qobject_unref(qtest_qmp(s, "{ 'execute': 'system_reset' }")); + qtest_qmp_eventwait(s, "RESET"); + + qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, post_reset_csr, DMAR_REG_SIZE); + /* Ensure registers are consistent after hard reset */ + g_assert(!memcmp(init_csr, post_reset_csr, DMAR_REG_SIZE)); + + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/q35/intel-iommu/stage-1", test_intel_iommu_stage_1); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index bf4d5c268b..edd53ec995 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -93,6 +93,7 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \ (host_os != 'windows' and \ config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ (config_all_devices.has_key('CONFIG_PCIE_PORT') and \ From 42e2a7a0ab23784e44fcb18369e06067abc89305 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 12 Dec 2024 22:04:02 +1000 Subject: [PATCH 1043/2892] pci/msix: Fix msix pba read vector poll end calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The end vector calculation has a bug that results in polling fewer than required vectors when reading at a non-zero offset in PBA memory. Fixes: bbef882cc193 ("msi: add API to get notified about pending bit poll") Signed-off-by: Nicholas Piggin Message-Id: <20241212120402.1475053-1-npiggin@gmail.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/msix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/pci/msix.c b/hw/pci/msix.c index d8a55a6474..57ec7084a4 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -250,7 +250,7 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, PCIDevice *dev = opaque; if (dev->msix_vector_poll_notifier) { unsigned vector_start = addr * 8; - unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr); + unsigned vector_end = MIN((addr + size) * 8, dev->msix_entries_nr); dev->msix_vector_poll_notifier(dev, vector_start, vector_end); } From 239c3f7ed44d8b82a682a1c2e9e8c3355c10d321 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:17 +0100 Subject: [PATCH 1044/2892] acpi/ghes: get rid of ACPI_HEST_SRC_ID_RESERVED This is just duplicating ACPI_GHES_ERROR_SOURCE_COUNT, which has a better name. So, drop the duplication. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <9012bf4c9630adf15a22af3c88fda8270916887b.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 7 ++----- include/hw/acpi/ghes.h | 3 ++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index e9511d9b8f..dc217694de 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -34,9 +34,6 @@ /* The max size in bytes for one error block */ #define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB) -/* Now only support ARMv8 SEA notification type error source */ -#define ACPI_GHES_ERROR_SOURCE_COUNT 1 - /* Generic Hardware Error Source version 2 */ #define ACPI_GHES_SOURCE_GENERIC_ERROR_V2 10 @@ -396,7 +393,7 @@ int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) AcpiGedState *acpi_ged_state; AcpiGhesState *ags; - assert(source_id < ACPI_HEST_SRC_ID_RESERVED); + assert(source_id < ACPI_GHES_ERROR_SOURCE_COUNT); acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED, NULL)); @@ -407,7 +404,7 @@ int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) if (physical_address) { - if (source_id < ACPI_HEST_SRC_ID_RESERVED) { + if (source_id < ACPI_GHES_ERROR_SOURCE_COUNT) { start_addr += source_id * sizeof(uint64_t); } diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 674f6958e9..59e3b8fb24 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -59,7 +59,8 @@ enum AcpiGhesNotifyType { enum { ACPI_HEST_SRC_ID_SEA = 0, /* future ids go here */ - ACPI_HEST_SRC_ID_RESERVED, + + ACPI_GHES_ERROR_SOURCE_COUNT }; typedef struct AcpiGhesState { From 872b69f21fe34f133982e02d04e33425d761d33b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:18 +0100 Subject: [PATCH 1045/2892] acpi/ghes: simplify acpi_ghes_record_errors() code Reduce the ident of the function and prepares it for the next changes. No functional changes. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <19af4188535217213486d169e0501e592bc78a95.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 56 ++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index dc217694de..e66f3be150 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -402,40 +402,42 @@ int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) start_addr = le64_to_cpu(ags->ghes_addr_le); - if (physical_address) { + if (!physical_address) { + return -1; + } - if (source_id < ACPI_GHES_ERROR_SOURCE_COUNT) { - start_addr += source_id * sizeof(uint64_t); - } + if (source_id < ACPI_GHES_ERROR_SOURCE_COUNT) { + start_addr += source_id * sizeof(uint64_t); + } - cpu_physical_memory_read(start_addr, &error_block_addr, - sizeof(error_block_addr)); + cpu_physical_memory_read(start_addr, &error_block_addr, + sizeof(error_block_addr)); - error_block_addr = le64_to_cpu(error_block_addr); + error_block_addr = le64_to_cpu(error_block_addr); - read_ack_register_addr = start_addr + - ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); + read_ack_register_addr = start_addr + + ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); - cpu_physical_memory_read(read_ack_register_addr, - &read_ack_register, sizeof(read_ack_register)); + cpu_physical_memory_read(read_ack_register_addr, + &read_ack_register, sizeof(read_ack_register)); - /* zero means OSPM does not acknowledge the error */ - if (!read_ack_register) { - error_report("OSPM does not acknowledge previous error," - " so can not record CPER for current error anymore"); - } else if (error_block_addr) { - read_ack_register = cpu_to_le64(0); - /* - * Clear the Read Ack Register, OSPM will write it to 1 when - * it acknowledges this error. - */ - cpu_physical_memory_write(read_ack_register_addr, - &read_ack_register, sizeof(uint64_t)); + /* zero means OSPM does not acknowledge the error */ + if (!read_ack_register) { + error_report("OSPM does not acknowledge previous error," + " so can not record CPER for current error anymore"); + } else if (error_block_addr) { + read_ack_register = cpu_to_le64(0); + /* + * Clear the Read Ack Register, OSPM will write it to 1 when + * it acknowledges this error. + */ + cpu_physical_memory_write(read_ack_register_addr, + &read_ack_register, sizeof(uint64_t)); - ret = acpi_ghes_record_mem_error(error_block_addr, - physical_address); - } else - error_report("can not find Generic Error Status Block"); + ret = acpi_ghes_record_mem_error(error_block_addr, + physical_address); + } else { + error_report("can not find Generic Error Status Block"); } return ret; From 606a42c4c1d46b31a95cab09a7033e7f22f9ed3d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:19 +0100 Subject: [PATCH 1046/2892] acpi/ghes: simplify the per-arch caller to build HEST table The GHES driver requires not only a HEST table, but also a separate firmware file to store Error Structure records. It can't do one without the other. Simplify the caller logic for it to require one function. No functional changes. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <9584bb8953385e165681d5d185c503f8df8ef42f.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 7 +++++-- hw/arm/virt-acpi-build.c | 5 ++--- include/hw/acpi/ghes.h | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index e66f3be150..4a6c45bcb4 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -233,7 +233,7 @@ static int acpi_ghes_record_mem_error(uint64_t error_block_address, * Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs. * See docs/specs/acpi_hest_ghes.rst for blobs format. */ -void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) +static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) { int i, error_status_block_offset; @@ -356,12 +356,15 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) } /* Build Hardware Error Source Table */ -void acpi_build_hest(GArray *table_data, BIOSLinker *linker, +void acpi_build_hest(GArray *table_data, GArray *hardware_errors, + BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { AcpiTable table = { .sig = "HEST", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; + build_ghes_error_table(hardware_errors, linker); + acpi_table_begin(&table, table_data); /* Error Source Count */ diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index c9b13057a3..3ac8f8e178 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -946,10 +946,9 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) build_dbg2(tables_blob, tables->linker, vms); if (vms->ras) { - build_ghes_error_table(tables->hardware_errors, tables->linker); acpi_add_table(table_offsets, tables_blob); - acpi_build_hest(tables_blob, tables->linker, vms->oem_id, - vms->oem_table_id); + acpi_build_hest(tables_blob, tables->hardware_errors, tables->linker, + vms->oem_id, vms->oem_table_id); } if (ms->numa_state->num_nodes > 0) { diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 59e3b8fb24..20016c226d 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -68,8 +68,8 @@ typedef struct AcpiGhesState { bool present; /* True if GHES is present at all on this board */ } AcpiGhesState; -void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker); -void acpi_build_hest(GArray *table_data, BIOSLinker *linker, +void acpi_build_hest(GArray *table_data, GArray *hardware_errors, + BIOSLinker *linker, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); From a85a3b729b3c31d8cc878017308997a8112655b2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:20 +0100 Subject: [PATCH 1047/2892] acpi/ghes: better handle source_id and notification GHES has two fields that are stored on HEST error source blocks associated with notifications: - notification type, which is a number defined at the ACPI spec containing several arch-specific synchronous and assynchronous types; - source id, which is a HW/FW defined number, used to distinguish between different implemented sources. There could be several sources with the same notification type, which is dependent of the way each architecture maps notifications. Right now, build_ghes_v2() hardcodes a 1:1 mapping between such fields. Move it to two independent parameters, allowing the caller function to fill both. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <133ff72ea1041fed7dbcf97b7a2b0f4dfacde31a.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 4a6c45bcb4..29cd7e4d81 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -284,9 +284,13 @@ static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) } /* Build Generic Hardware Error Source version 2 (GHESv2) */ -static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) +static void build_ghes_v2(GArray *table_data, + BIOSLinker *linker, + enum AcpiGhesNotifyType notify, + uint16_t source_id) { uint64_t address_offset; + /* * Type: * Generic Hardware Error Source version 2(GHESv2 - Type 10) @@ -316,18 +320,8 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) address_offset + GAS_ADDR_OFFSET, sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, source_id * sizeof(uint64_t)); - switch (source_id) { - case ACPI_HEST_SRC_ID_SEA: - /* - * Notification Structure - * Now only enable ARMv8 SEA notification type - */ - build_ghes_hw_error_notification(table_data, ACPI_GHES_NOTIFY_SEA); - break; - default: - error_report("Not support this error source"); - abort(); - } + /* Notification Structure */ + build_ghes_hw_error_notification(table_data, notify); /* Error Status Block Length */ build_append_int_noprefix(table_data, ACPI_GHES_MAX_RAW_DATA_LENGTH, 4); @@ -369,7 +363,8 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, /* Error Source Count */ build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4); - build_ghes_v2(table_data, ACPI_HEST_SRC_ID_SEA, linker); + build_ghes_v2(table_data, linker, + ACPI_GHES_NOTIFY_SEA, ACPI_HEST_SRC_ID_SEA); acpi_table_end(linker, &table); } From 5eb07a4ff067053b4d6bf55d2d614b65b00a476b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:21 +0100 Subject: [PATCH 1048/2892] acpi/ghes: Fix acpi_ghes_record_errors() argument Align the header file with the actual implementation of this function, as the first argument is source ID and not notification type. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/acpi/ghes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 20016c226d..50e3a25ea3 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -73,7 +73,7 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); -int acpi_ghes_record_errors(uint8_t notify, uint64_t error_physical_addr); +int acpi_ghes_record_errors(uint8_t source_id, uint64_t error_physical_addr); /** * acpi_ghes_present: Report whether ACPI GHES table is present From 4ffedca347c458db17c464f9329222403fe54f22 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:22 +0100 Subject: [PATCH 1049/2892] acpi/ghes: Remove a duplicated out of bounds check acpi_ghes_record_errors() has an assert() at the beginning to ensure that source_id will be lower than ACPI_GHES_ERROR_SOURCE_COUNT. Remove a duplicated check. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 29cd7e4d81..5f67322bf0 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -404,9 +404,7 @@ int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) return -1; } - if (source_id < ACPI_GHES_ERROR_SOURCE_COUNT) { - start_addr += source_id * sizeof(uint64_t); - } + start_addr += source_id * sizeof(uint64_t); cpu_physical_memory_read(start_addr, &error_block_addr, sizeof(error_block_addr)); From 26e0893e420b34677e6e904a06edf55331bd937c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:23 +0100 Subject: [PATCH 1050/2892] acpi/ghes: Change the type for source_id As described at: ACPI 6.5 spec at: 18.3.2. ACPI Error Source In particular at GHES/GHESv2 table: Table 18.10 Generic Hardware Error Source Structure HEST source ID is actually a 16-bit value. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <0e83ba548c1aedd1299fe387b94db78986590a34.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes-stub.c | 2 +- hw/acpi/ghes.c | 2 +- include/hw/acpi/ghes.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c index c315de1802..2b64cbd281 100644 --- a/hw/acpi/ghes-stub.c +++ b/hw/acpi/ghes-stub.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/acpi/ghes.h" -int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) +int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) { return -1; } diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 5f67322bf0..edc74c38bf 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -383,7 +383,7 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, ags->present = true; } -int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) +int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) { uint64_t error_block_addr, read_ack_register_addr, read_ack_register = 0; uint64_t start_addr; diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 50e3a25ea3..9295e46be2 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -73,7 +73,7 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); -int acpi_ghes_record_errors(uint8_t source_id, uint64_t error_physical_addr); +int acpi_ghes_record_errors(uint16_t source_id, uint64_t error_physical_addr); /** * acpi_ghes_present: Report whether ACPI GHES table is present From 2e223c5ec1146b61163d3372ac629d9240d57cb1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:24 +0100 Subject: [PATCH 1051/2892] acpi/ghes: don't check if physical_address is not zero The 'physical_address' value is a faulty page. As such, 0 is as valid as any other value. Suggested-by: Igor Mammedov Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index edc74c38bf..a3dffd78b0 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -400,10 +400,6 @@ int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) start_addr = le64_to_cpu(ags->ghes_addr_le); - if (!physical_address) { - return -1; - } - start_addr += source_id * sizeof(uint64_t); cpu_physical_memory_read(start_addr, &error_block_addr, From 48b0dcdd67d3fafbd07f6298257259dec4f541ce Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:25 +0100 Subject: [PATCH 1052/2892] acpi/ghes: make the GHES record generation more generic Split the code into separate functions to allow using the common CPER filling code by different error sources. The generic code was moved to ghes_record_cper_errors(), and ghes_gen_err_data_uncorrectable_recoverable() now contains only a logic to fill the Generic Error Data part of the record, as described at: ACPI 6.2: 18.3.2.7.1 Generic Error Data The remaining code to generate a memory error now belongs to acpi_ghes_record_errors() function. A further patch will give it a better name. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <68d9f787d8c4fc8d1dbc227d6902fe801e42dea9.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 120 +++++++++++++++++++++++------------------ include/hw/acpi/ghes.h | 3 ++ 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index a3dffd78b0..6f40cd35a9 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -181,51 +181,24 @@ static void acpi_ghes_build_append_mem_cper(GArray *table, build_append_int_noprefix(table, 0, 7); } -static int acpi_ghes_record_mem_error(uint64_t error_block_address, - uint64_t error_physical_addr) +static void +ghes_gen_err_data_uncorrectable_recoverable(GArray *block, + const uint8_t *section_type, + int data_length) { - GArray *block; - - /* Memory Error Section Type */ - const uint8_t uefi_cper_mem_sec[] = - UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \ - 0xED, 0x7C, 0x83, 0xB1); - /* invalid fru id: ACPI 4.0: 17.3.2.6.1 Generic Error Data, * Table 17-13 Generic Error Data Entry */ QemuUUID fru_id = {}; - uint32_t data_length; - - block = g_array_new(false, true /* clear */, 1); - - /* This is the length if adding a new generic error data entry*/ - data_length = ACPI_GHES_DATA_LENGTH + ACPI_GHES_MEM_CPER_LENGTH; - /* - * It should not run out of the preallocated memory if adding a new generic - * error data entry - */ - assert((data_length + ACPI_GHES_GESB_SIZE) <= - ACPI_GHES_MAX_RAW_DATA_LENGTH); /* Build the new generic error status block header */ acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE, 0, 0, data_length, ACPI_CPER_SEV_RECOVERABLE); /* Build this new generic error data entry header */ - acpi_ghes_generic_error_data(block, uefi_cper_mem_sec, + acpi_ghes_generic_error_data(block, section_type, ACPI_CPER_SEV_RECOVERABLE, 0, 0, ACPI_GHES_MEM_CPER_LENGTH, fru_id, 0); - - /* Build the memory section CPER for above new generic error data entry */ - acpi_ghes_build_append_mem_cper(block, error_physical_addr); - - /* Write the generic error data entry into guest memory */ - cpu_physical_memory_write(error_block_address, block->data, block->len); - - g_array_free(block, true); - - return 0; } /* @@ -383,15 +356,18 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, ags->present = true; } -int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) +void ghes_record_cper_errors(const void *cper, size_t len, + uint16_t source_id, Error **errp) { uint64_t error_block_addr, read_ack_register_addr, read_ack_register = 0; uint64_t start_addr; - bool ret = -1; AcpiGedState *acpi_ged_state; AcpiGhesState *ags; - assert(source_id < ACPI_GHES_ERROR_SOURCE_COUNT); + if (len > ACPI_GHES_MAX_RAW_DATA_LENGTH) { + error_setg(errp, "GHES CPER record is too big: %zd", len); + return; + } acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED, NULL)); @@ -406,6 +382,10 @@ int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) sizeof(error_block_addr)); error_block_addr = le64_to_cpu(error_block_addr); + if (!error_block_addr) { + error_setg(errp, "can not find Generic Error Status Block"); + return; + } read_ack_register_addr = start_addr + ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); @@ -415,24 +395,62 @@ int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) /* zero means OSPM does not acknowledge the error */ if (!read_ack_register) { - error_report("OSPM does not acknowledge previous error," - " so can not record CPER for current error anymore"); - } else if (error_block_addr) { - read_ack_register = cpu_to_le64(0); - /* - * Clear the Read Ack Register, OSPM will write it to 1 when - * it acknowledges this error. - */ - cpu_physical_memory_write(read_ack_register_addr, - &read_ack_register, sizeof(uint64_t)); - - ret = acpi_ghes_record_mem_error(error_block_addr, - physical_address); - } else { - error_report("can not find Generic Error Status Block"); + error_setg(errp, + "OSPM does not acknowledge previous error," + " so can not record CPER for current error anymore"); + return; } - return ret; + read_ack_register = cpu_to_le64(0); + /* + * Clear the Read Ack Register, OSPM will write 1 to this register when + * it acknowledges the error. + */ + cpu_physical_memory_write(read_ack_register_addr, + &read_ack_register, sizeof(uint64_t)); + + /* Write the generic error data entry into guest memory */ + cpu_physical_memory_write(error_block_addr, cper, len); + + return; +} + +int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) +{ + /* Memory Error Section Type */ + const uint8_t guid[] = + UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \ + 0xED, 0x7C, 0x83, 0xB1); + Error *errp = NULL; + int data_length; + GArray *block; + + block = g_array_new(false, true /* clear */, 1); + + data_length = ACPI_GHES_DATA_LENGTH + ACPI_GHES_MEM_CPER_LENGTH; + /* + * It should not run out of the preallocated memory if adding a new generic + * error data entry + */ + assert((data_length + ACPI_GHES_GESB_SIZE) <= + ACPI_GHES_MAX_RAW_DATA_LENGTH); + + ghes_gen_err_data_uncorrectable_recoverable(block, guid, data_length); + + /* Build the memory section CPER for above new generic error data entry */ + acpi_ghes_build_append_mem_cper(block, physical_address); + + /* Report the error */ + ghes_record_cper_errors(block->data, block->len, source_id, &errp); + + g_array_free(block, true); + + if (errp) { + error_report_err(errp); + return -1; + } + + return 0; } bool acpi_ghes_present(void) diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 9295e46be2..8859346af5 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -23,6 +23,7 @@ #define ACPI_GHES_H #include "hw/acpi/bios-linker-loader.h" +#include "qapi/error.h" /* * Values for Hardware Error Notification Type field @@ -73,6 +74,8 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); +void ghes_record_cper_errors(const void *cper, size_t len, + uint16_t source_id, Error **errp); int acpi_ghes_record_errors(uint16_t source_id, uint64_t error_physical_addr); /** From d32028a54000db671eb2d0b6b28bbf15acc2e5f9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:26 +0100 Subject: [PATCH 1053/2892] acpi/ghes: better name GHES memory error function The current function used to generate GHES data is specific for memory errors. Give a better name for it, as we now have a generic function as well. Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Signed-off-by: Mauro Carvalho Chehab Message-Id: <35b59121129d5e99cb5062cc3d775594bbb0905b.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes-stub.c | 2 +- hw/acpi/ghes.c | 2 +- include/hw/acpi/ghes.h | 4 ++-- target/arm/kvm.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c index 2b64cbd281..7cec1812da 100644 --- a/hw/acpi/ghes-stub.c +++ b/hw/acpi/ghes-stub.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/acpi/ghes.h" -int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) +int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) { return -1; } diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 6f40cd35a9..66bd98337a 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -415,7 +415,7 @@ void ghes_record_cper_errors(const void *cper, size_t len, return; } -int acpi_ghes_record_errors(uint16_t source_id, uint64_t physical_address) +int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) { /* Memory Error Section Type */ const uint8_t guid[] = diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 8859346af5..21666a4bcc 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -74,15 +74,15 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); +int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr); void ghes_record_cper_errors(const void *cper, size_t len, uint16_t source_id, Error **errp); -int acpi_ghes_record_errors(uint16_t source_id, uint64_t error_physical_addr); /** * acpi_ghes_present: Report whether ACPI GHES table is present * * Returns: true if the system has an ACPI GHES table and it is - * safe to call acpi_ghes_record_errors() to record a memory error. + * safe to call acpi_ghes_memory_errors() to record a memory error. */ bool acpi_ghes_present(void); #endif diff --git a/target/arm/kvm.c b/target/arm/kvm.c index a9444a2c7a..da30bdbb23 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2387,7 +2387,7 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) */ if (code == BUS_MCEERR_AR) { kvm_cpu_synchronize_state(c); - if (!acpi_ghes_record_errors(ACPI_HEST_SRC_ID_SEA, paddr)) { + if (!acpi_ghes_memory_errors(ACPI_HEST_SRC_ID_SEA, paddr)) { kvm_inject_arm_sea(c); } else { error_report("failed to record the error"); From 1acc8d4e647e3d9fc45cc43c216e784e47a74809 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:27 +0100 Subject: [PATCH 1054/2892] acpi/ghes: don't crash QEMU if ghes GED is not found Make error handling within ghes_record_cper_errors() consistent, i.e. instead abort just print a error in case ghes GED is not found. Reviewed-by: Jonathan Cameron Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 66bd98337a..6843ddf64b 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -371,7 +371,10 @@ void ghes_record_cper_errors(const void *cper, size_t len, acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED, NULL)); - g_assert(acpi_ged_state); + if (!acpi_ged_state) { + error_setg(errp, "Can't find ACPI_GED object"); + return; + } ags = &acpi_ged_state->ghes_state; start_addr = le64_to_cpu(ags->ghes_addr_le); From 4651745dfc8d384bd260969c23d8423741462eac Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:28 +0100 Subject: [PATCH 1055/2892] acpi/ghes: rename etc/hardware_error file macros Now that we have also have a file to store HEST data location, which is part of GHES, better name the file where CPER records are stored. No functional changes. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 6843ddf64b..3f94a5542b 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -28,8 +28,8 @@ #include "hw/nvram/fw_cfg.h" #include "qemu/uuid.h" -#define ACPI_GHES_ERRORS_FW_CFG_FILE "etc/hardware_errors" -#define ACPI_GHES_DATA_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" +#define ACPI_HW_ERROR_FW_CFG_FILE "etc/hardware_errors" +#define ACPI_HW_ERROR_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" /* The max size in bytes for one error block */ #define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB) @@ -234,7 +234,7 @@ static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) ACPI_GHES_MAX_RAW_DATA_LENGTH * ACPI_GHES_ERROR_SOURCE_COUNT); /* Tell guest firmware to place hardware_errors blob into RAM */ - bios_linker_loader_alloc(linker, ACPI_GHES_ERRORS_FW_CFG_FILE, + bios_linker_loader_alloc(linker, ACPI_HW_ERROR_FW_CFG_FILE, hardware_errors, sizeof(uint64_t), false); for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { @@ -243,17 +243,21 @@ static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) * corresponding "Generic Error Status Block" */ bios_linker_loader_add_pointer(linker, - ACPI_GHES_ERRORS_FW_CFG_FILE, sizeof(uint64_t) * i, - sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, - error_status_block_offset + i * ACPI_GHES_MAX_RAW_DATA_LENGTH); + ACPI_HW_ERROR_FW_CFG_FILE, + sizeof(uint64_t) * i, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, + error_status_block_offset + + i * ACPI_GHES_MAX_RAW_DATA_LENGTH); } /* * tell firmware to write hardware_errors GPA into * hardware_errors_addr fw_cfg, once the former has been initialized. */ - bios_linker_loader_write_pointer(linker, ACPI_GHES_DATA_ADDR_FW_CFG_FILE, - 0, sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, 0); + bios_linker_loader_write_pointer(linker, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, 0, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, 0); } /* Build Generic Hardware Error Source version 2 (GHESv2) */ @@ -290,8 +294,10 @@ static void build_ghes_v2(GArray *table_data, build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0x40, 0, 4 /* QWord access */, 0); bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - address_offset + GAS_ADDR_OFFSET, sizeof(uint64_t), - ACPI_GHES_ERRORS_FW_CFG_FILE, source_id * sizeof(uint64_t)); + address_offset + GAS_ADDR_OFFSET, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, + source_id * sizeof(uint64_t)); /* Notification Structure */ build_ghes_hw_error_notification(table_data, notify); @@ -308,9 +314,11 @@ static void build_ghes_v2(GArray *table_data, build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0x40, 0, 4 /* QWord access */, 0); bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - address_offset + GAS_ADDR_OFFSET, - sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, - (ACPI_GHES_ERROR_SOURCE_COUNT + source_id) * sizeof(uint64_t)); + address_offset + GAS_ADDR_OFFSET, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, + (ACPI_GHES_ERROR_SOURCE_COUNT + source_id) + * sizeof(uint64_t)); /* * Read Ack Preserve field @@ -346,11 +354,11 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, GArray *hardware_error) { /* Create a read-only fw_cfg file for GHES */ - fw_cfg_add_file(s, ACPI_GHES_ERRORS_FW_CFG_FILE, hardware_error->data, + fw_cfg_add_file(s, ACPI_HW_ERROR_FW_CFG_FILE, hardware_error->data, hardware_error->len); /* Create a read-write fw_cfg file for Address */ - fw_cfg_add_file_callback(s, ACPI_GHES_DATA_ADDR_FW_CFG_FILE, NULL, NULL, + fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, NULL, &(ags->ghes_addr_le), sizeof(ags->ghes_addr_le), false); ags->present = true; From 652f6d86cbb60e193edc2510c365b75229734ccf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:29 +0100 Subject: [PATCH 1056/2892] acpi/ghes: better name the offset of the hardware error firmware The hardware error firmware is where HEST error structures are stored. Those can be GHESv2, but they can also be other types. Better name the location of the hardware error. No functional changes. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 4 ++-- hw/acpi/ghes.c | 4 ++-- include/hw/acpi/ghes.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 58540c0aaf..c85d97ca37 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -363,7 +363,7 @@ static const VMStateDescription vmstate_ghes = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT64(ghes_addr_le, AcpiGhesState), + VMSTATE_UINT64(hw_error_le, AcpiGhesState), VMSTATE_END_OF_LIST() }, }; @@ -371,7 +371,7 @@ static const VMStateDescription vmstate_ghes = { static bool ghes_needed(void *opaque) { AcpiGedState *s = opaque; - return s->ghes_state.ghes_addr_le; + return s->ghes_state.hw_error_le; } static const VMStateDescription vmstate_ghes_state = { diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 3f94a5542b..983e28505a 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -359,7 +359,7 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, /* Create a read-write fw_cfg file for Address */ fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, - NULL, &(ags->ghes_addr_le), sizeof(ags->ghes_addr_le), false); + NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false); ags->present = true; } @@ -385,7 +385,7 @@ void ghes_record_cper_errors(const void *cper, size_t len, } ags = &acpi_ged_state->ghes_state; - start_addr = le64_to_cpu(ags->ghes_addr_le); + start_addr = le64_to_cpu(ags->hw_error_le); start_addr += source_id * sizeof(uint64_t); diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 21666a4bcc..39619a2457 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -65,7 +65,7 @@ enum { }; typedef struct AcpiGhesState { - uint64_t ghes_addr_le; + uint64_t hw_error_le; bool present; /* True if GHES is present at all on this board */ } AcpiGhesState; From 1cd59b8981ce234c1d790111afce4a32218a88dd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:30 +0100 Subject: [PATCH 1057/2892] acpi/ghes: move offset calculus to a separate function Currently, CPER address location is calculated as an offset of the hardware_errors table. It is also badly named, as the offset actually used is the address where the CPER data starts, and not the beginning of the error source. Move the logic which calculates such offset to a separate function, in preparation for a patch that will be changing the logic to calculate it from the HEST table. While here, properly name the variable which stores the cper address. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Message-Id: <60fdd1bf379ba1db3099710868802aa49a27febb.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index 983e28505a..ddb576b940 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -364,10 +364,37 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, ags->present = true; } +static void get_hw_error_offsets(uint64_t ghes_addr, + uint64_t *cper_addr, + uint64_t *read_ack_register_addr) +{ + if (!ghes_addr) { + return; + } + + /* + * non-HEST version supports only one source, so no need to change + * the start offset based on the source ID. Also, we can't validate + * the source ID, as it is stored inside the HEST table. + */ + + cpu_physical_memory_read(ghes_addr, cper_addr, + sizeof(*cper_addr)); + + *cper_addr = le64_to_cpu(*cper_addr); + + /* + * As the current version supports only one source, the ack offset is + * just sizeof(uint64_t). + */ + *read_ack_register_addr = ghes_addr + + ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); +} + void ghes_record_cper_errors(const void *cper, size_t len, uint16_t source_id, Error **errp) { - uint64_t error_block_addr, read_ack_register_addr, read_ack_register = 0; + uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register; uint64_t start_addr; AcpiGedState *acpi_ged_state; AcpiGhesState *ags; @@ -389,18 +416,13 @@ void ghes_record_cper_errors(const void *cper, size_t len, start_addr += source_id * sizeof(uint64_t); - cpu_physical_memory_read(start_addr, &error_block_addr, - sizeof(error_block_addr)); + get_hw_error_offsets(start_addr, &cper_addr, &read_ack_register_addr); - error_block_addr = le64_to_cpu(error_block_addr); - if (!error_block_addr) { + if (!cper_addr) { error_setg(errp, "can not find Generic Error Status Block"); return; } - read_ack_register_addr = start_addr + - ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); - cpu_physical_memory_read(read_ack_register_addr, &read_ack_register, sizeof(read_ack_register)); @@ -421,7 +443,7 @@ void ghes_record_cper_errors(const void *cper, size_t len, &read_ack_register, sizeof(uint64_t)); /* Write the generic error data entry into guest memory */ - cpu_physical_memory_write(error_block_addr, cper, len); + cpu_physical_memory_write(cper_addr, cper, len); return; } From 47935fc1e56f02c892d186ef89be7c923f73c89b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:31 +0100 Subject: [PATCH 1058/2892] acpi/ghes: Change ghes fill logic to work with only one source Extending to multiple sources require a BIOS pointer to the beginning of the HEST table, which in turn requires a backward-compatible code. So, the current code supports only one source. Ensure that and simplify the code. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <66bddd42a64c8515ad98b9975d953b4a70ffcc6d.1736945236.git.mchehab+huawei@kernel.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index ddb576b940..b709c177cd 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -387,15 +387,13 @@ static void get_hw_error_offsets(uint64_t ghes_addr, * As the current version supports only one source, the ack offset is * just sizeof(uint64_t). */ - *read_ack_register_addr = ghes_addr + - ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); + *read_ack_register_addr = ghes_addr + sizeof(uint64_t); } void ghes_record_cper_errors(const void *cper, size_t len, uint16_t source_id, Error **errp) { uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register; - uint64_t start_addr; AcpiGedState *acpi_ged_state; AcpiGhesState *ags; @@ -412,11 +410,9 @@ void ghes_record_cper_errors(const void *cper, size_t len, } ags = &acpi_ged_state->ghes_state; - start_addr = le64_to_cpu(ags->hw_error_le); - - start_addr += source_id * sizeof(uint64_t); - - get_hw_error_offsets(start_addr, &cper_addr, &read_ack_register_addr); + assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1); + get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), + &cper_addr, &read_ack_register_addr); if (!cper_addr) { error_setg(errp, "can not find Generic Error Status Block"); From 84c146758d79b3689b6c9c7815b6bfbb70ba06b0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2025 13:50:32 +0100 Subject: [PATCH 1059/2892] docs: acpi_hest_ghes: fix documentation for CPER size While the spec defines a CPER size of 4KiB for each record, currently it is set to 1KiB. Fix the documentation and add a pointer to the macro name there, as this may help to keep it updated. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/specs/acpi_hest_ghes.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/specs/acpi_hest_ghes.rst b/docs/specs/acpi_hest_ghes.rst index 68f1fbe0a4..c3e9f8d9a7 100644 --- a/docs/specs/acpi_hest_ghes.rst +++ b/docs/specs/acpi_hest_ghes.rst @@ -67,8 +67,10 @@ Design Details (3) The address registers table contains N Error Block Address entries and N Read Ack Register entries. The size for each entry is 8-byte. The Error Status Data Block table contains N Error Status Data Block - entries. The size for each entry is 4096(0x1000) bytes. The total size - for the "etc/hardware_errors" fw_cfg blob is (N * 8 * 2 + N * 4096) bytes. + entries. The size for each entry is defined at the source code as + ACPI_GHES_MAX_RAW_DATA_LENGTH (currently 1024 bytes). The total size + for the "etc/hardware_errors" fw_cfg blob is + (N * 8 * 2 + N * ACPI_GHES_MAX_RAW_DATA_LENGTH) bytes. N is the number of the kinds of hardware error sources. (4) QEMU generates the ACPI linker/loader script for the firmware. The From 1ad32644fe4c9fb25086be15a66dde1d55d3410f Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 15 Jan 2025 13:53:40 +0100 Subject: [PATCH 1060/2892] tests: acpi: whitelist expected blobs Signed-off-by: Igor Mammedov Message-Id: <20250115125342.3883374-2-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..085dfa9ff4 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,41 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/x86/pc/DSDT", +"tests/data/acpi/x86/pc/DSDT.acpierst", +"tests/data/acpi/x86/pc/DSDT.acpihmat", +"tests/data/acpi/x86/pc/DSDT.bridge", +"tests/data/acpi/x86/pc/DSDT.cphp", +"tests/data/acpi/x86/pc/DSDT.dimmpxm", +"tests/data/acpi/x86/pc/DSDT.hpbridge", +"tests/data/acpi/x86/pc/DSDT.ipmikcs", +"tests/data/acpi/x86/pc/DSDT.memhp", +"tests/data/acpi/x86/pc/DSDT.nohpet", +"tests/data/acpi/x86/pc/DSDT.numamem", +"tests/data/acpi/x86/pc/DSDT.roothp", +"tests/data/acpi/x86/q35/DSDT", +"tests/data/acpi/x86/q35/DSDT.acpierst", +"tests/data/acpi/x86/q35/DSDT.acpihmat", +"tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x", +"tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator", +"tests/data/acpi/x86/q35/DSDT.applesmc", +"tests/data/acpi/x86/q35/DSDT.bridge", +"tests/data/acpi/x86/q35/DSDT.core-count", +"tests/data/acpi/x86/q35/DSDT.core-count2", +"tests/data/acpi/x86/q35/DSDT.cphp", +"tests/data/acpi/x86/q35/DSDT.cxl", +"tests/data/acpi/x86/q35/DSDT.dimmpxm", +"tests/data/acpi/x86/q35/DSDT.ipmibt", +"tests/data/acpi/x86/q35/DSDT.ipmismbus", +"tests/data/acpi/x86/q35/DSDT.ivrs", +"tests/data/acpi/x86/q35/DSDT.memhp", +"tests/data/acpi/x86/q35/DSDT.mmio64", +"tests/data/acpi/x86/q35/DSDT.multi-bridge", +"tests/data/acpi/x86/q35/DSDT.nohpet", +"tests/data/acpi/x86/q35/DSDT.numamem", +"tests/data/acpi/x86/q35/DSDT.pvpanic-isa", +"tests/data/acpi/x86/q35/DSDT.thread-count", +"tests/data/acpi/x86/q35/DSDT.thread-count2", +"tests/data/acpi/x86/q35/DSDT.tis.tpm12", +"tests/data/acpi/x86/q35/DSDT.tis.tpm2", +"tests/data/acpi/x86/q35/DSDT.type4-count", +"tests/data/acpi/x86/q35/DSDT.viot", +"tests/data/acpi/x86/q35/DSDT.xapic", From 0b053391985abcc40b16ac8fc4a7f6588d1d95c1 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 15 Jan 2025 13:53:41 +0100 Subject: [PATCH 1061/2892] pci: acpi: Windows 'PCI Label Id' bug workaround Current versions of Windows call _DSM(func=7) regardless of whether it is supported or not. It leads to NICs having bogus 'PCI Label Id = 0', where none should be set at all. Also presence of 'PCI Label Id' triggers another Windows bug on localized versions that leads to hangs. The later bug is fixed in latest updates for 'Windows Server' but not in consumer versions of Windows (and there is no plans to fix it as far as I'm aware). Given it's easy, implement Microsoft suggested workaround (return invalid Package) so that affected Windows versions could boot on QEMU. This would effectvely remove bogus 'PCI Label Id's on NICs, but MS teem confirmed that flipping 'PCI Label Id' should not change 'Network Connection' ennumeration, so it should be safe for QEMU to change _DSM without any compat code. Smoke tested with WinXP and WS2022 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/774 Signed-off-by: Igor Mammedov Message-Id: <20250115125342.3883374-3-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 733b8f0851..1311a0d4f3 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -654,6 +654,7 @@ static Aml *aml_pci_pdsm(void) Aml *acpi_index = aml_local(2); Aml *zero = aml_int(0); Aml *one = aml_int(1); + Aml *not_supp = aml_int(0xFFFFFFFF); Aml *func = aml_arg(2); Aml *params = aml_arg(4); Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); @@ -678,7 +679,7 @@ static Aml *aml_pci_pdsm(void) */ ifctx1 = aml_if(aml_lnot( aml_or(aml_equal(acpi_index, zero), - aml_equal(acpi_index, aml_int(0xFFFFFFFF)), NULL) + aml_equal(acpi_index, not_supp), NULL) )); { /* have supported functions */ @@ -704,18 +705,30 @@ static Aml *aml_pci_pdsm(void) { Aml *pkg = aml_package(2); - aml_append(pkg, zero); - /* - * optional, if not impl. should return null string - */ - aml_append(pkg, aml_string("%s", "")); - aml_append(ifctx, aml_store(pkg, ret)); - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + aml_append(ifctx, aml_store(pkg, ret)); /* - * update acpi-index to actual value + * Windows calls func=7 without checking if it's available, + * as workaround Microsoft has suggested to return invalid for func7 + * Package, so return 2 elements package but only initialize elements + * when acpi_index is supported and leave them uninitialized, which + * leads elements to being Uninitialized ObjectType and should trip + * Windows into discarding result as an unexpected and prevent setting + * bogus 'PCI Label' on the device. */ - aml_append(ifctx, aml_store(acpi_index, aml_index(ret, zero))); + ifctx1 = aml_if(aml_lnot(aml_lor( + aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp) + ))); + { + aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); + /* + * optional, if not impl. should return null string + */ + aml_append(ifctx1, aml_store(aml_string("%s", ""), + aml_index(ret, one))); + } + aml_append(ifctx, ifctx1); + aml_append(ifctx, aml_return(ret)); } From 9fb1c9a1bb26e111ee5fa5538070cd684de14c08 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 15 Jan 2025 13:53:42 +0100 Subject: [PATCH 1062/2892] tests: acpi: update expected blobs _DSM function 7 AML should have followig change: If ((Arg2 == 0x07)) { - Local0 = Package (0x02) - { - Zero, - "" - } Local2 = AIDX (DerefOf (Arg4 [Zero]), DerefOf (Arg4 [One] )) - Local0 [Zero] = Local2 + Local0 = Package (0x02) {} + If (!((Local2 == Zero) || (Local2 == 0xFFFFFFFF))) + { + Local0 [Zero] = Local2 + Local0 [One] = "" + } + Return (Local0) } } Signed-off-by: Igor Mammedov Message-Id: <20250115125342.3883374-4-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/x86/pc/DSDT | Bin 8593 -> 8611 bytes tests/data/acpi/x86/pc/DSDT.acpierst | Bin 8504 -> 8522 bytes tests/data/acpi/x86/pc/DSDT.acpihmat | Bin 9918 -> 9936 bytes tests/data/acpi/x86/pc/DSDT.bridge | Bin 15464 -> 15482 bytes tests/data/acpi/x86/pc/DSDT.cphp | Bin 9057 -> 9075 bytes tests/data/acpi/x86/pc/DSDT.dimmpxm | Bin 10247 -> 10265 bytes tests/data/acpi/x86/pc/DSDT.hpbridge | Bin 8544 -> 8562 bytes tests/data/acpi/x86/pc/DSDT.ipmikcs | Bin 8665 -> 8683 bytes tests/data/acpi/x86/pc/DSDT.memhp | Bin 9952 -> 9970 bytes tests/data/acpi/x86/pc/DSDT.nohpet | Bin 8451 -> 8469 bytes tests/data/acpi/x86/pc/DSDT.numamem | Bin 8599 -> 8617 bytes tests/data/acpi/x86/pc/DSDT.roothp | Bin 12386 -> 12404 bytes tests/data/acpi/x86/q35/DSDT | Bin 8422 -> 8440 bytes tests/data/acpi/x86/q35/DSDT.acpierst | Bin 8439 -> 8457 bytes tests/data/acpi/x86/q35/DSDT.acpihmat | Bin 9747 -> 9765 bytes .../data/acpi/x86/q35/DSDT.acpihmat-generic-x | Bin 12632 -> 12650 bytes .../acpi/x86/q35/DSDT.acpihmat-noinitiator | Bin 8701 -> 8719 bytes tests/data/acpi/x86/q35/DSDT.applesmc | Bin 8468 -> 8486 bytes tests/data/acpi/x86/q35/DSDT.bridge | Bin 12035 -> 12053 bytes tests/data/acpi/x86/q35/DSDT.core-count | Bin 12980 -> 12998 bytes tests/data/acpi/x86/q35/DSDT.core-count2 | Bin 33837 -> 33855 bytes tests/data/acpi/x86/q35/DSDT.cphp | Bin 8886 -> 8904 bytes tests/data/acpi/x86/q35/DSDT.cxl | Bin 13213 -> 13231 bytes tests/data/acpi/x86/q35/DSDT.dimmpxm | Bin 10076 -> 10094 bytes tests/data/acpi/x86/q35/DSDT.ipmibt | Bin 8497 -> 8515 bytes tests/data/acpi/x86/q35/DSDT.ipmismbus | Bin 8510 -> 8528 bytes tests/data/acpi/x86/q35/DSDT.ivrs | Bin 8439 -> 8457 bytes tests/data/acpi/x86/q35/DSDT.memhp | Bin 9781 -> 9799 bytes tests/data/acpi/x86/q35/DSDT.mmio64 | Bin 9552 -> 9570 bytes tests/data/acpi/x86/q35/DSDT.multi-bridge | Bin 13275 -> 13293 bytes tests/data/acpi/x86/q35/DSDT.nohpet | Bin 8280 -> 8298 bytes tests/data/acpi/x86/q35/DSDT.numamem | Bin 8428 -> 8446 bytes tests/data/acpi/x86/q35/DSDT.pvpanic-isa | Bin 8523 -> 8541 bytes tests/data/acpi/x86/q35/DSDT.thread-count | Bin 12980 -> 12998 bytes tests/data/acpi/x86/q35/DSDT.thread-count2 | Bin 33837 -> 33855 bytes tests/data/acpi/x86/q35/DSDT.tis.tpm12 | Bin 9028 -> 9046 bytes tests/data/acpi/x86/q35/DSDT.tis.tpm2 | Bin 9054 -> 9072 bytes tests/data/acpi/x86/q35/DSDT.type4-count | Bin 18656 -> 18674 bytes tests/data/acpi/x86/q35/DSDT.viot | Bin 14679 -> 14697 bytes tests/data/acpi/x86/q35/DSDT.xapic | Bin 35785 -> 35803 bytes tests/qtest/bios-tables-test-allowed-diff.h | 40 ------------------ 41 files changed, 40 deletions(-) diff --git a/tests/data/acpi/x86/pc/DSDT b/tests/data/acpi/x86/pc/DSDT index 60d50b088a362556fd54395cb15364d6c0936be5..4beb5194b84a711fcb52e3e52cc2096497d18442 100644 GIT binary patch delta 89 zcmbQ}yx5t`CD3-R delta 71 zcmZ4NJkgoUCDk-)%Ez{}8)z_{6%iB%o|3(g%c delta 71 zcmX@*w8M$ZCDJ%>k diff --git a/tests/data/acpi/x86/pc/DSDT.acpihmat b/tests/data/acpi/x86/pc/DSDT.acpihmat index 61b7d5caa55c44dbf69d649110c6b14bb4c3fdf5..d081db26d7ba504b3344fad130d5812419291ac0 100644 GIT binary patch delta 89 zcmdnzd%>5>CDf|gg_5vYRCI()HgaSuT amx$($90mp;!N`zQkkpaDu-TQVTLl0-@)g_w diff --git a/tests/data/acpi/x86/pc/DSDT.bridge b/tests/data/acpi/x86/pc/DSDT.bridge index d43e148bed19160f39d88ccf3364544150a3f87f..e16897dc5f0fbb3f7b4de8db913884046246cc3b 100644 GIT binary patch delta 89 zcmaD+@vDN%CDk-)%Ez{}8)z_{6%Nly^~MG+nf delta 71 zcmezD_Rx*XCDIz366_KpslmX&C@_)h0h6ET#5X&bT(~ALW6U-&pPa?TUf}5I64Bg|!@vL} r7#WfZgqWBT7D!E+I5~-7auUyfASg)cNMK+n;AQAYVBBoXbVU^a+*loK delta 71 zcmbOk&>q0$66_MfuED^-7(bEg0h5oX&boH!>hW6V}kot(wRULeHE#K6mtP~hn4 a64Bg|!@vL}7#WfZk~$I?HoG!iQ3U|fGZbt9 diff --git a/tests/data/acpi/x86/pc/DSDT.hpbridge b/tests/data/acpi/x86/pc/DSDT.hpbridge index 67fe28699fbb261cfc7a52b2291f9965ab93c6a8..0eafe5fbf3d73719c9c3e6e26371863bfb44ed2f 100644 GIT binary patch delta 89 zcmaFh^vQ|KCDk-)%Ez{}8)z_{6%Nmm{KEf|gg_5vYRCI()HgaSuT amx$($90mp;!N`zQkkpaDu-TPqnLGeJV-@KD diff --git a/tests/data/acpi/x86/pc/DSDT.memhp b/tests/data/acpi/x86/pc/DSDT.memhp index 9c66ccf150af1622d1b788a1ae04a6e5136cff9e..e3b49757cb7abd7536ee89a6824967d2cb2485cf 100644 GIT binary patch delta 89 zcmaFh`^lHfCDnk1QX1 delta 71 zcmez5`@omWCDr~=xN{=c113MwiEnl=xo}Nh#+YqlJ~@kvy};4aC8D__hk*e| rFft?+2r)4wERdQsadHyFk-)%Ez{}8)z_{6%>4F>p{vRFN delta 71 zcmbR0)a=CN66_MftjNH?$T5-Y0h5oX&boH!>hW6V}kot(wRULeHE#K6mtP~hn4 a64Bg|!@vL}7#WfZk~$I?HoG!ikOKg+W)s^0 diff --git a/tests/data/acpi/x86/pc/DSDT.numamem b/tests/data/acpi/x86/pc/DSDT.numamem index e256bbce790152f045247db631d9f1da81f90499..9bfbfc28213713c208dfc38a85abb46fb190871d 100644 GIT binary patch delta 89 zcmbR4ywaJ=CDf|gg_5vYRCI()HgaSuT amx$($90mp;!N`zQkkpaDu-TOk-)%Ez{}8)z_{6%NnZ~DVZCD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dWBw!y01)vM(EtDd diff --git a/tests/data/acpi/x86/q35/DSDT.acpierst b/tests/data/acpi/x86/q35/DSDT.acpierst index dbd4f858354df0f4c050fd0b914581154f340ee8..072a3fe2cd17dfe06658dfd82588f69787810114 100644 GIT binary patch delta 91 zcmezF*y+UO66_MfsmQ><7`u^cAtRHY=;XDGJD6O!CZ{uHo0w0|;$kmw^mK`6?#N+a t01}K0Nd-boObH95CQY22#4tID=RXh>By}V(Fck1IbR;lt{=&p82LS2(9X0>} delta 73 zcmeBl`tHc(66_N4U4emtaqUK~g^Wx-B9qrL?qG7_oSe>-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV`i2E03D1JHvj+t diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat b/tests/data/acpi/x86/q35/DSDT.acpihmat index 952752e30e9dfc9e2085e8fceaa0740dda6db89c..2a4f2fc1d5c5649673353186e67ff5b5e59e8d53 100644 GIT binary patch delta 91 zcmbR2v($&nCDFW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{Dnzg835}29by0g delta 73 zcmZ4LGuemBCD!@#IFXg^Wx-B9qrL?qG7_oSe>-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dW0qG20N%P2V*mgE diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x b/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x index e95258cbd8681103a642f8973bd1ac9ef229cff7..7911c058bba5005d318b8db8d6da5c1ee381b0f1 100644 GIT binary patch delta 91 zcmcbS^eTzVCDFW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{Dmn+4*)5r9$f$c delta 73 zcmaErbR&t&CDfQ+LPjPZk;!WrcQ83|PEKdaR#Kgu#l>DA#LC3L%aBmu c=;;#C+>yh;03;Y0k_wVK5*RkKF{kJO01;jkUH||9 diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator index ba2a7d0004be7cd7220716dc7e8594be87197b98..580b4a456a20fc0cc0a832eaf74193b46d8ae8b1 100644 GIT binary patch delta 91 zcmezC-0#BW66_Mfuf)K>XtI%OAtRHY=;XDGJD6O!CZ{uHo0w0|;$kmw^mK`6?#N+a t01}K0Nd-boObH95CQY22#4tID=RXh>By}V(Fck1IbR;lt{=&p54*=zj9V-9; delta 73 zcmeBo`RmN(66_N4SCN5%v1KFILPjPZk;!WrcQ83|PEKdaR#Kgu#l>DA#LC3L%aBmu c=;;#C+>yh;03;Y0k_wVK5*RkKF>}fT02*NwEC2ui diff --git a/tests/data/acpi/x86/q35/DSDT.applesmc b/tests/data/acpi/x86/q35/DSDT.applesmc index b6cb840953ea539092f601e08b7122fc999b3e1b..5e8220e38d6f88b103f6eb3eb7c78dfa466882dc 100644 GIT binary patch delta 91 zcmbQ@w9JXiCDx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3sYR902mp9eV%( delta 73 zcmZ4HG{uR_CD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV^)v@0OWxad;kCd diff --git a/tests/data/acpi/x86/q35/DSDT.bridge b/tests/data/acpi/x86/q35/DSDT.bridge index 1939fda2507cde6fcb6f7a093897f9bd2cb987ef..ee039453af1071e00a81ee7b37cf8f417f524257 100644 GIT binary patch delta 91 zcmZpUn;OUE66_Kps?Wf{cylAyLPjP((aCEWcQCndO-^UZHZh-^#l>FW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{Dp~M2LJ|z9k&1g delta 73 zcmbOl*Br;?66_Mftk1x}sIif2AtRHI$mF$*JD8j}C#N%IE2&P-;$kllVr63BWk@J+ b^mK`6?#N+a01}K0Nd-wA2@IRrnE7=8xVsX# diff --git a/tests/data/acpi/x86/q35/DSDT.core-count b/tests/data/acpi/x86/q35/DSDT.core-count index 41c0832ab5041ff5361598813ec28fe7442b191b..7ebfceeb66460d0ad98471924ce224b7153e87ef 100644 GIT binary patch delta 91 zcmdmzdMuU8CDx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3utleE>Jw9_#=B delta 73 zcmX?>x+Rs%CD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV?Lk{02~bz?EnA( diff --git a/tests/data/acpi/x86/q35/DSDT.core-count2 b/tests/data/acpi/x86/q35/DSDT.core-count2 index 153b45f0f7443d25cecc2a752fb6dbd921160e78..d0394558a1faa0b4ba43abab66d474d96b477ff3 100644 GIT binary patch delta 93 zcmZ46!L+}FiOVI}CB(jkfq}7oBiBMkCO^^1YZ-Sixo}NRXUaA)pPa?TUf}5I64Bg| v!@vL}7#WfZgqWBT7D!E+I5~-7auUyfASg)cNMK+n;AQAYVBGwL$+8guRx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3ut_c>o;=9*+P3 delta 73 zcmX@%y3Li#CD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV?HDg00znwkN^Mx diff --git a/tests/data/acpi/x86/q35/DSDT.cxl b/tests/data/acpi/x86/q35/DSDT.cxl index 0f1ccdfcc3ffbf151c172015cc4bf18bc4ead218..20843549f54af1cb0e6017c4cfff7463318d9eb7 100644 GIT binary patch delta 91 zcmbQ6zCNAHCDW3mKXGL?^Fh+`;6+H94Ir+r)fw78iSgqo+$mb4LyX t1CU^3NGcFwVoF#bHEH7HB!-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV_sx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3qBq3IJ9f9`FDF delta 73 zcmaFocgK&*CDDA#LC3L%aBmu c=;;#C+>yh;03;Y0k_wVK5*RkKF=wa%0QdwG@c;k- diff --git a/tests/data/acpi/x86/q35/DSDT.ipmibt b/tests/data/acpi/x86/q35/DSDT.ipmibt index 524fc9f4ee09fd7a5bec62818fd87b6ec300dee8..4066a76d26aa380dfbecc58aa3f83ab5db2baadb 100644 GIT binary patch delta 91 zcmdn!bl8c@CDx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3sY+8~`Ob9y0&{ delta 73 zcmX@?w9$#nCDDA#LC3L%aBmu c=;;#C+>yh;03;Y0k_wVK5*RkKG26-k0MPdnGynhq diff --git a/tests/data/acpi/x86/q35/DSDT.ipmismbus b/tests/data/acpi/x86/q35/DSDT.ipmismbus index d04d215a1d0fbc77739084d100a35af47a1c1a62..6d0b6b95c2a9fd01befc37b26650781ee1562e2a 100644 GIT binary patch delta 91 zcmdnzbis+sCD$LPjP((aCEWcQCndO-^UZHZh-^#l>FW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{DsL&4gd$99oPT> delta 73 zcmccMw9kplCD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dWA>5*0QIaB*#H0l diff --git a/tests/data/acpi/x86/q35/DSDT.ivrs b/tests/data/acpi/x86/q35/DSDT.ivrs index dbd4f858354df0f4c050fd0b914581154f340ee8..072a3fe2cd17dfe06658dfd82588f69787810114 100644 GIT binary patch delta 91 zcmezF*y+UO66_MfsmQ><7`u^cAtRHY=;XDGJD6O!CZ{uHo0w0|;$kmw^mK`6?#N+a t01}K0Nd-boObH95CQY22#4tID=RXh>By}V(Fck1IbR;lt{=&p82LS2(9X0>} delta 73 zcmeBl`tHc(66_N4U4emtaqUK~g^Wx-B9qrL?qG7_oSe>-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV`i2E03D1JHvj+t diff --git a/tests/data/acpi/x86/q35/DSDT.memhp b/tests/data/acpi/x86/q35/DSDT.memhp index f73ade9bf6e4545f9912ed654a282884a54cec79..4f2f9bcfceff076490cc49b8286380295a340004 100644 GIT binary patch delta 91 zcmdn$bKHl^CDx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3sYyG5{3^9svLV delta 73 zcmX@^v(<;oCDdw@xex}g^Wx-B9qrL?qG7_oSe>-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV|G*q0RQV00ssI2 diff --git a/tests/data/acpi/x86/q35/DSDT.mmio64 b/tests/data/acpi/x86/q35/DSDT.mmio64 index f0ddb4c83cdc9afdf4f289a66ed6bf0d630fd623..0fb6aab16f1bd79f3c0790cc9f644f7e52ac37b1 100644 GIT binary patch delta 91 zcmccM^~j6MCDFW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{Dmn-2>={69w-0+ delta 73 zcmaFlb-|0vCD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV~$Y*00uu4DF6Tf diff --git a/tests/data/acpi/x86/q35/DSDT.multi-bridge b/tests/data/acpi/x86/q35/DSDT.multi-bridge index 3ad19e3f5e480db1c449b838c83833f7665186cd..f6afa6d96d2525d512cc46f17439f7a49962b730 100644 GIT binary patch delta 91 zcmcbe{x+SN93mKXGL?^Fh+`;6+H94Ir+r)fw78iSgqo+$mb4LyX t1CU^3NGcFwVoF#bHEH7HB! delta 73 zcmaExemkAZCD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV}5P`04A0dEC2ui diff --git a/tests/data/acpi/x86/q35/DSDT.nohpet b/tests/data/acpi/x86/q35/DSDT.nohpet index c089b5877a0f4d808abd4d8d9396ee7d2a9a78e5..99ad629c9171ff6ab346d6b4c519e77ca23e5b1c 100644 GIT binary patch delta 91 zcmccN@XCS9CDFW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{Dmn+762>R9$x?e delta 73 zcmaFmaKnMiCD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV@{C;01`SBU;qFB diff --git a/tests/data/acpi/x86/q35/DSDT.numamem b/tests/data/acpi/x86/q35/DSDT.numamem index 2867f5b44498d788fc0effd0bf616317821be88e..fd1d8a79d3d9b071c8796e5e99b76698a9a8d29c 100644 GIT binary patch delta 91 zcmaFk_|K8cCDx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3utzSpZnlA9Mf! delta 73 zcmez8_{NdTCDFW=;;#C+>yh; t03;Y0k_v>Fm=YF9O`144iD7aQ&wn5&Na{#nU?|{a=tyAP{Dmo84geWE9vT1u delta 73 zcmccXblQo_CD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV-A-C00W;C8vpx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3utleE>Jw9_#=B delta 73 zcmX?>x+Rs%CD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV?Lk{02~bz?EnA( diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count2 b/tests/data/acpi/x86/q35/DSDT.thread-count2 index 153b45f0f7443d25cecc2a752fb6dbd921160e78..d0394558a1faa0b4ba43abab66d474d96b477ff3 100644 GIT binary patch delta 93 zcmZ46!L+}FiOVI}CB(jkfq}7oBiBMkCO^^1YZ-Sixo}NRXUaA)pPa?TUf}5I64Bg| v!@vL}7#WfZgqWBT7D!E+I5~-7auUyfASg)cNMK+n;AQAYVBGwL$+8guRx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3qBk0suQ!9*6({ delta 73 zcmccScEpX#CDDA#LC3L%aBmu c=;;#C+>yh;03;Y0k_wVK5*RkKF$X9B0OFJri2wiq diff --git a/tests/data/acpi/x86/q35/DSDT.tis.tpm2 b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 index b05563deedc65df50f35b2399862d9ee8d4d1e0e..5c975d2162d0bfee5a3a089e79b5ba038f82b7ef 100644 GIT binary patch delta 91 zcmccT_Q8$ICDx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3qB)0sui69;yHU delta 73 zcmez1cF&E=CD-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dW6n|l03y{Cs{jB1 diff --git a/tests/data/acpi/x86/q35/DSDT.type4-count b/tests/data/acpi/x86/q35/DSDT.type4-count index 00807e7fd4d758bc2ab9c69ac8869cf6864399f7..3194a82b8b4f66aff1ecf7d2d60b4890181fc600 100644 GIT binary patch delta 93 zcmaDbk@3?+MlP3Nmyk~$3=E9H8@U!TGWm&4Udy;*!sObomX e2?dUxE)mTgISdRyf{`JqAgLpPVKW=^8&?2n#1=pR diff --git a/tests/data/acpi/x86/q35/DSDT.viot b/tests/data/acpi/x86/q35/DSDT.viot index c3d83e67660ee3fd59f6fae6242270bed4a567f1..129d43e1e561be3fd7cd71406829ab81d0a8aba0 100644 GIT binary patch delta 91 zcmca!^sx t0Z1@1BozoTF(oXJnly2862s&qp8r5lkkpaDz)--;(2>Bn`3qCB830&J9{K

(KT@%~1xg^Wx-B9qrL?qG7_oSe>-t)x0Ri;KNLh?R+fmm#6R c(bFZOxg&>x0Z1@1Bo!oeBrt4dV@@^$05x|M`v3p{ diff --git a/tests/data/acpi/x86/q35/DSDT.xapic b/tests/data/acpi/x86/q35/DSDT.xapic index 227d421f16ed1824a87e8a91da734828f8b48cbf..b37ab591110d1c8201575ad6bba83449d7b90b21 100644 GIT binary patch delta 93 zcmX>(o$2;;CN7s?myp}t3=E9T8@U!TGWm&4Udy;*!sObomX e2?dUxE)mTgISdRyf{`JqAgLpPVKW=^)eZo5W)`Rb diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 085dfa9ff4..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,41 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/x86/pc/DSDT", -"tests/data/acpi/x86/pc/DSDT.acpierst", -"tests/data/acpi/x86/pc/DSDT.acpihmat", -"tests/data/acpi/x86/pc/DSDT.bridge", -"tests/data/acpi/x86/pc/DSDT.cphp", -"tests/data/acpi/x86/pc/DSDT.dimmpxm", -"tests/data/acpi/x86/pc/DSDT.hpbridge", -"tests/data/acpi/x86/pc/DSDT.ipmikcs", -"tests/data/acpi/x86/pc/DSDT.memhp", -"tests/data/acpi/x86/pc/DSDT.nohpet", -"tests/data/acpi/x86/pc/DSDT.numamem", -"tests/data/acpi/x86/pc/DSDT.roothp", -"tests/data/acpi/x86/q35/DSDT", -"tests/data/acpi/x86/q35/DSDT.acpierst", -"tests/data/acpi/x86/q35/DSDT.acpihmat", -"tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x", -"tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator", -"tests/data/acpi/x86/q35/DSDT.applesmc", -"tests/data/acpi/x86/q35/DSDT.bridge", -"tests/data/acpi/x86/q35/DSDT.core-count", -"tests/data/acpi/x86/q35/DSDT.core-count2", -"tests/data/acpi/x86/q35/DSDT.cphp", -"tests/data/acpi/x86/q35/DSDT.cxl", -"tests/data/acpi/x86/q35/DSDT.dimmpxm", -"tests/data/acpi/x86/q35/DSDT.ipmibt", -"tests/data/acpi/x86/q35/DSDT.ipmismbus", -"tests/data/acpi/x86/q35/DSDT.ivrs", -"tests/data/acpi/x86/q35/DSDT.memhp", -"tests/data/acpi/x86/q35/DSDT.mmio64", -"tests/data/acpi/x86/q35/DSDT.multi-bridge", -"tests/data/acpi/x86/q35/DSDT.nohpet", -"tests/data/acpi/x86/q35/DSDT.numamem", -"tests/data/acpi/x86/q35/DSDT.pvpanic-isa", -"tests/data/acpi/x86/q35/DSDT.thread-count", -"tests/data/acpi/x86/q35/DSDT.thread-count2", -"tests/data/acpi/x86/q35/DSDT.tis.tpm12", -"tests/data/acpi/x86/q35/DSDT.tis.tpm2", -"tests/data/acpi/x86/q35/DSDT.type4-count", -"tests/data/acpi/x86/q35/DSDT.viot", -"tests/data/acpi/x86/q35/DSDT.xapic", From 1ce979e7269a34d19ea1a65808df014d8b2acbf6 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 15 Jan 2025 15:58:34 +0800 Subject: [PATCH 1063/2892] hw/cxl: Fix msix_notify: Assertion `vector < dev->msix_entries_nr` This assertion always happens when we sanitize the CXL memory device. $ echo 1 > /sys/bus/cxl/devices/mem0/security/sanitize It is incorrect to register an MSIX number beyond the device's capability. Increase the device's MSIX number to cover the mailbox msix number(9). Fixes: 43efb0bfad2b ("hw/cxl/mbox: Wire up interrupts for background completion") Signed-off-by: Li Zhijian Message-Id: <20250115075834.167504-1-lizhijian@fujitsu.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index bd7652740f..0ae1704a34 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -843,7 +843,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) ComponentRegisters *regs = &cxl_cstate->crb; MemoryRegion *mr = ®s->component_registers; uint8_t *pci_conf = pci_dev->config; - unsigned short msix_num = 6; + unsigned short msix_num = 10; int i, rc; uint16_t count; From 3f65357313e0f928e0bd3ff868b705855d0405bc Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 15 Jan 2025 14:50:43 +0100 Subject: [PATCH 1064/2892] vhost: Add stubs for the migration state transfer interface Migration state transfer interface is only used by vhost-user-fs, so the interface needs to be defined only when vhost is built. But I need to use this interface with virtio-net and vhost is not always enabled, and to avoid undefined reference error during build, define stub functions for vhost_supports_device_state(), vhost_save_backend_state() and vhost_load_backend_state(). Cc: Hanna Czenczek Signed-off-by: Laurent Vivier Message-Id: <20250115135044.799698-2-lvivier@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/vhost.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 461c168c37..a9469d50bc 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -365,7 +365,14 @@ static inline int vhost_reset_device(struct vhost_dev *hdev) * Returns true if the device supports these commands, and false if it * does not. */ +#ifdef CONFIG_VHOST bool vhost_supports_device_state(struct vhost_dev *dev); +#else +static inline bool vhost_supports_device_state(struct vhost_dev *dev) +{ + return false; +} +#endif /** * vhost_set_device_state_fd(): Begin transfer of internal state from/to @@ -448,7 +455,15 @@ int vhost_check_device_state(struct vhost_dev *dev, Error **errp); * * Returns 0 on success, and -errno otherwise. */ +#ifdef CONFIG_VHOST int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); +#else +static inline int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, + Error **errp) +{ + return -ENOSYS; +} +#endif /** * vhost_load_backend_state(): High-level function to load a vhost @@ -465,6 +480,14 @@ int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); * * Returns 0 on success, and -errno otherwise. */ +#ifdef CONFIG_VHOST int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); +#else +static inline int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, + Error **errp) +{ + return -ENOSYS; +} +#endif #endif From 60f543ad917fad731e39ff8ce2ca83b9a9cc9d90 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 15 Jan 2025 14:50:44 +0100 Subject: [PATCH 1065/2892] virtio-net: vhost-user: Implement internal migration Add support of VHOST_USER_PROTOCOL_F_DEVICE_STATE in virtio-net with vhost-user backend. Cc: Hanna Czenczek Signed-off-by: Laurent Vivier Message-Id: <20250115135044.799698-3-lvivier@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 135 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 112 insertions(+), 23 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 06f096abf6..85e14b788c 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3337,6 +3337,117 @@ static const VMStateDescription vmstate_virtio_net_rss = { }, }; +static struct vhost_dev *virtio_net_get_vhost(VirtIODevice *vdev) +{ + VirtIONet *n = VIRTIO_NET(vdev); + NetClientState *nc; + struct vhost_net *net; + + if (!n->nic) { + return NULL; + } + + nc = qemu_get_queue(n->nic); + if (!nc) { + return NULL; + } + + net = get_vhost_net(nc->peer); + if (!net) { + return NULL; + } + + return &net->dev; +} + +static int vhost_user_net_save_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, + JSONWriter *vmdesc) +{ + VirtIONet *n = pv; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + struct vhost_dev *vhdev; + Error *local_error = NULL; + int ret; + + vhdev = virtio_net_get_vhost(vdev); + if (vhdev == NULL) { + error_reportf_err(local_error, + "Error getting vhost back-end of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return -1; + } + + ret = vhost_save_backend_state(vhdev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error saving back-end state of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return ret; + } + + return 0; +} + +static int vhost_user_net_load_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VirtIONet *n = pv; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + struct vhost_dev *vhdev; + Error *local_error = NULL; + int ret; + + vhdev = virtio_net_get_vhost(vdev); + if (vhdev == NULL) { + error_reportf_err(local_error, + "Error getting vhost back-end of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return -1; + } + + ret = vhost_load_backend_state(vhdev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error loading back-end state of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return ret; + } + + return 0; +} + +static bool vhost_user_net_is_internal_migration(void *opaque) +{ + VirtIONet *n = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + struct vhost_dev *vhdev; + + vhdev = virtio_net_get_vhost(vdev); + if (vhdev == NULL) { + return false; + } + + return vhost_supports_device_state(vhdev); +} + +static const VMStateDescription vhost_user_net_backend_state = { + .name = "virtio-net-device/backend", + .version_id = 0, + .needed = vhost_user_net_is_internal_migration, + .fields = (const VMStateField[]) { + { + .name = "backend", + .info = &(const VMStateInfo) { + .name = "virtio-net vhost-user backend state", + .get = vhost_user_net_load_state, + .put = vhost_user_net_save_state, + }, + }, + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio_net_device = { .name = "virtio-net-device", .version_id = VIRTIO_NET_VM_VERSION, @@ -3389,6 +3500,7 @@ static const VMStateDescription vmstate_virtio_net_device = { }, .subsections = (const VMStateDescription * const []) { &vmstate_virtio_net_rss, + &vhost_user_net_backend_state, NULL } }; @@ -3950,29 +4062,6 @@ static bool dev_unplug_pending(void *opaque) return vdc->primary_unplug_pending(dev); } -static struct vhost_dev *virtio_net_get_vhost(VirtIODevice *vdev) -{ - VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc; - struct vhost_net *net; - - if (!n->nic) { - return NULL; - } - - nc = qemu_get_queue(n->nic); - if (!nc) { - return NULL; - } - - net = get_vhost_net(nc->peer); - if (!net) { - return NULL; - } - - return &net->dev; -} - static const VMStateDescription vmstate_virtio_net = { .name = "virtio-net", .minimum_version_id = VIRTIO_NET_VM_VERSION, From 3634039b93cc51816263e0cb5ba32e1b61142d5d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 7 Jan 2025 16:28:16 +0000 Subject: [PATCH 1066/2892] hw/acpi: Add vmclock device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vmclock device addresses the problem of live migration with precision clocks. The tolerances of a hardware counter (e.g. TSC) are typically around ±50PPM. A guest will use NTP/PTP/PPS to discipline that counter against an external source of 'real' time, and track the precise frequency of the counter as it changes with environmental conditions. When a guest is live migrated, anything it knows about the frequency of the underlying counter becomes invalid. It may move from a host where the counter running at -50PPM of its nominal frequency, to a host where it runs at +50PPM. There will also be a step change in the value of the counter, as the correctness of its absolute value at migration is limited by the accuracy of the source and destination host's time synchronization. The device exposes a shared memory region to guests, which can be mapped all the way to userspace. In the first phase, this merely advertises a 'disruption_marker', which indicates that the guest should throw away any NTP synchronization it thinks it has, and start again. Because the region can be exposed all the way to userspace, applications can still use time from a fast vDSO 'system call', and check the disruption marker to be sure that their timestamp is indeed truthful. The structure also allows for the precise time, as known by the host, to be exposed directly to guests so that they don't have to wait for NTP to resync from scratch. The values and fields are based on the nascent virtio-rtc specification, and the intent is that a version (hopefully precisely this version) of this structure will be included as an optional part of that spec. In the meantime, a simple ACPI device along the lines of VMGENID is perfectly sufficient and is compatible with what's being shipped in certain commercial hypervisors. Linux guest support was merged into the 6.13-rc1 kernel: https://git.kernel.org/torvalds/c/205032724226 Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant Message-Id: <07fd5e2f529098ad4d7cab1423fe9f4a03a9cc14.camel@infradead.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/Kconfig | 5 + hw/acpi/meson.build | 1 + hw/acpi/vmclock.c | 179 ++++++++++++++++++ hw/i386/Kconfig | 1 + hw/i386/acpi-build.c | 10 +- include/hw/acpi/vmclock.h | 34 ++++ include/standard-headers/linux/vmclock-abi.h | 182 +++++++++++++++++++ scripts/update-linux-headers.sh | 1 + 8 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 hw/acpi/vmclock.c create mode 100644 include/hw/acpi/vmclock.h create mode 100644 include/standard-headers/linux/vmclock-abi.h diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index e07d3204eb..1d4e9f0845 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -60,6 +60,11 @@ config ACPI_VMGENID default y depends on PC +config ACPI_VMCLOCK + bool + default y + depends on PC + config ACPI_VIOT bool depends on ACPI diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index c8854f4d48..73f02b9691 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -15,6 +15,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_NVDIMM', if_false: files('acpi-nvdimm-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCI', if_true: files('pci.c')) acpi_ss.add(when: 'CONFIG_ACPI_CXL', if_true: files('cxl.c'), if_false: files('cxl-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_VMGENID', if_true: files('vmgenid.c')) +acpi_ss.add(when: 'CONFIG_ACPI_VMCLOCK', if_true: files('vmclock.c')) acpi_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device.c')) acpi_ss.add(when: 'CONFIG_ACPI_HMAT', if_true: files('hmat.c')) acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files('ghes-stub.c')) diff --git a/hw/acpi/vmclock.c b/hw/acpi/vmclock.c new file mode 100644 index 0000000000..7387e5c9ca --- /dev/null +++ b/hw/acpi/vmclock.c @@ -0,0 +1,179 @@ +/* + * Virtual Machine Clock Device + * + * Copyright © 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/i386/e820_memory_layout.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/vmclock.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "migration/vmstate.h" +#include "system/reset.h" + +#include "standard-headers/linux/vmclock-abi.h" + +void vmclock_build_acpi(VmclockState *vms, GArray *table_data, + BIOSLinker *linker, const char *oem_id) +{ + Aml *ssdt, *dev, *scope, *crs; + AcpiTable table = { .sig = "SSDT", .rev = 1, + .oem_id = oem_id, .oem_table_id = "VMCLOCK" }; + + /* Put VMCLOCK into a separate SSDT table */ + acpi_table_begin(&table, table_data); + ssdt = init_aml_allocator(); + + scope = aml_scope("\\_SB"); + dev = aml_device("VCLK"); + aml_append(dev, aml_name_decl("_HID", aml_string("AMZNC10C"))); + aml_append(dev, aml_name_decl("_CID", aml_string("VMCLOCK"))); + aml_append(dev, aml_name_decl("_DDN", aml_string("VMCLOCK"))); + + /* Simple status method */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xf))); + + crs = aml_resource_template(); + aml_append(crs, aml_qword_memory(AML_POS_DECODE, + AML_MIN_FIXED, AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_ONLY, + 0xffffffffffffffffULL, + vms->physaddr, + vms->physaddr + VMCLOCK_SIZE - 1, + 0, VMCLOCK_SIZE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + aml_append(ssdt, scope); + + g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); + acpi_table_end(linker, &table); + free_aml_allocator(); +} + +static void vmclock_update_guest(VmclockState *vms) +{ + uint64_t disruption_marker; + uint32_t seq_count; + + if (!vms->clk) { + return; + } + + seq_count = le32_to_cpu(vms->clk->seq_count) | 1; + vms->clk->seq_count = cpu_to_le32(seq_count); + /* These barriers pair with read barriers in the guest */ + smp_wmb(); + + disruption_marker = le64_to_cpu(vms->clk->disruption_marker); + disruption_marker++; + vms->clk->disruption_marker = cpu_to_le64(disruption_marker); + + /* These barriers pair with read barriers in the guest */ + smp_wmb(); + vms->clk->seq_count = cpu_to_le32(seq_count + 1); +} + +/* + * After restoring an image, we need to update the guest memory to notify + * it of clock disruption. + */ +static int vmclock_post_load(void *opaque, int version_id) +{ + VmclockState *vms = opaque; + + vmclock_update_guest(vms); + return 0; +} + +static const VMStateDescription vmstate_vmclock = { + .name = "vmclock", + .version_id = 1, + .minimum_version_id = 1, + .post_load = vmclock_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(physaddr, VmclockState), + VMSTATE_END_OF_LIST() + }, +}; + +static void vmclock_handle_reset(void *opaque) +{ + VmclockState *vms = VMCLOCK(opaque); + + if (!memory_region_is_mapped(&vms->clk_page)) { + memory_region_add_subregion_overlap(get_system_memory(), + vms->physaddr, + &vms->clk_page, 0); + } +} + +static void vmclock_realize(DeviceState *dev, Error **errp) +{ + VmclockState *vms = VMCLOCK(dev); + + /* + * Given that this function is executing, there is at least one VMCLOCK + * device. Check if there are several. + */ + if (!find_vmclock_dev()) { + error_setg(errp, "at most one %s device is permitted", TYPE_VMCLOCK); + return; + } + + vms->physaddr = VMCLOCK_ADDR; + + e820_add_entry(vms->physaddr, VMCLOCK_SIZE, E820_RESERVED); + + memory_region_init_ram(&vms->clk_page, OBJECT(dev), "vmclock_page", + VMCLOCK_SIZE, &error_abort); + memory_region_set_enabled(&vms->clk_page, true); + vms->clk = memory_region_get_ram_ptr(&vms->clk_page); + memset(vms->clk, 0, VMCLOCK_SIZE); + + vms->clk->magic = cpu_to_le32(VMCLOCK_MAGIC); + vms->clk->size = cpu_to_le16(VMCLOCK_SIZE); + vms->clk->version = cpu_to_le16(1); + + /* These are all zero and thus default, but be explicit */ + vms->clk->clock_status = VMCLOCK_STATUS_UNKNOWN; + vms->clk->counter_id = VMCLOCK_COUNTER_INVALID; + + qemu_register_reset(vmclock_handle_reset, vms); + + vmclock_update_guest(vms); +} + +static void vmclock_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_vmclock; + dc->realize = vmclock_realize; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo vmclock_device_info = { + .name = TYPE_VMCLOCK, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VmclockState), + .class_init = vmclock_device_class_init, +}; + +static void vmclock_register_types(void) +{ + type_register_static(&vmclock_device_info); +} + +type_init(vmclock_register_types) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 32818480d2..d34ce07b21 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -43,6 +43,7 @@ config PC select SERIAL_ISA select ACPI_PCI select ACPI_VMGENID + select ACPI_VMCLOCK select VIRTIO_PMEM_SUPPORTED select VIRTIO_MEM_SUPPORTED select HV_BALLOON_SUPPORTED diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1311a0d4f3..53b7306b43 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -43,6 +43,7 @@ #include "system/tpm.h" #include "hw/acpi/tpm.h" #include "hw/acpi/vmgenid.h" +#include "hw/acpi/vmclock.h" #include "hw/acpi/erst.h" #include "hw/acpi/piix4.h" #include "system/tpm_backend.h" @@ -2445,7 +2446,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) uint8_t *u; GArray *tables_blob = tables->table_data; AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL }; - Object *vmgenid_dev; + Object *vmgenid_dev, *vmclock_dev; char *oem_id; char *oem_table_id; @@ -2518,6 +2519,13 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) tables->vmgenid, tables->linker, x86ms->oem_id); } + vmclock_dev = find_vmclock_dev(); + if (vmclock_dev) { + acpi_add_table(table_offsets, tables_blob); + vmclock_build_acpi(VMCLOCK(vmclock_dev), tables_blob, tables->linker, + x86ms->oem_id); + } + if (misc.has_hpet) { acpi_add_table(table_offsets, tables_blob); build_hpet(tables_blob, tables->linker, x86ms->oem_id, diff --git a/include/hw/acpi/vmclock.h b/include/hw/acpi/vmclock.h new file mode 100644 index 0000000000..5605605812 --- /dev/null +++ b/include/hw/acpi/vmclock.h @@ -0,0 +1,34 @@ +#ifndef ACPI_VMCLOCK_H +#define ACPI_VMCLOCK_H + +#include "hw/acpi/bios-linker-loader.h" +#include "hw/qdev-core.h" +#include "qemu/uuid.h" +#include "qom/object.h" + +#define TYPE_VMCLOCK "vmclock" + +#define VMCLOCK_ADDR 0xfeffb000 +#define VMCLOCK_SIZE 0x1000 + +OBJECT_DECLARE_SIMPLE_TYPE(VmclockState, VMCLOCK) + +struct vmclock_abi; + +struct VmclockState { + DeviceState parent_obj; + MemoryRegion clk_page; + uint64_t physaddr; + struct vmclock_abi *clk; +}; + +/* returns NULL unless there is exactly one device */ +static inline Object *find_vmclock_dev(void) +{ + return object_resolve_path_type("", TYPE_VMCLOCK, NULL); +} + +void vmclock_build_acpi(VmclockState *vms, GArray *table_data, + BIOSLinker *linker, const char *oem_id); + +#endif diff --git a/include/standard-headers/linux/vmclock-abi.h b/include/standard-headers/linux/vmclock-abi.h new file mode 100644 index 0000000000..15b0316cb4 --- /dev/null +++ b/include/standard-headers/linux/vmclock-abi.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ + +/* + * This structure provides a vDSO-style clock to VM guests, exposing the + * relationship (or lack thereof) between the CPU clock (TSC, timebase, arch + * counter, etc.) and real time. It is designed to address the problem of + * live migration, which other clock enlightenments do not. + * + * When a guest is live migrated, this affects the clock in two ways. + * + * First, even between identical hosts the actual frequency of the underlying + * counter will change within the tolerances of its specification (typically + * ±50PPM, or 4 seconds a day). This frequency also varies over time on the + * same host, but can be tracked by NTP as it generally varies slowly. With + * live migration there is a step change in the frequency, with no warning. + * + * Second, there may be a step change in the value of the counter itself, as + * its accuracy is limited by the precision of the NTP synchronization on the + * source and destination hosts. + * + * So any calibration (NTP, PTP, etc.) which the guest has done on the source + * host before migration is invalid, and needs to be redone on the new host. + * + * In its most basic mode, this structure provides only an indication to the + * guest that live migration has occurred. This allows the guest to know that + * its clock is invalid and take remedial action. For applications that need + * reliable accurate timestamps (e.g. distributed databases), the structure + * can be mapped all the way to userspace. This allows the application to see + * directly for itself that the clock is disrupted and take appropriate + * action, even when using a vDSO-style method to get the time instead of a + * system call. + * + * In its more advanced mode. this structure can also be used to expose the + * precise relationship of the CPU counter to real time, as calibrated by the + * host. This means that userspace applications can have accurate time + * immediately after live migration, rather than having to pause operations + * and wait for NTP to recover. This mode does, of course, rely on the + * counter being reliable and consistent across CPUs. + * + * Note that this must be true UTC, never with smeared leap seconds. If a + * guest wishes to construct a smeared clock, it can do so. Presenting a + * smeared clock through this interface would be problematic because it + * actually messes with the apparent counter *period*. A linear smearing + * of 1 ms per second would effectively tweak the counter period by 1000PPM + * at the start/end of the smearing period, while a sinusoidal smear would + * basically be impossible to represent. + * + * This structure is offered with the intent that it be adopted into the + * nascent virtio-rtc standard, as a virtio-rtc that does not address the live + * migration problem seems a little less than fit for purpose. For that + * reason, certain fields use precisely the same numeric definitions as in + * the virtio-rtc proposal. The structure can also be exposed through an ACPI + * device with the CID "VMCLOCK", modelled on the "VMGENID" device except for + * the fact that it uses a real _CRS to convey the address of the structure + * (which should be a full page, to allow for mapping directly to userspace). + */ + +#ifndef __VMCLOCK_ABI_H__ +#define __VMCLOCK_ABI_H__ + +#include "standard-headers/linux/types.h" + +struct vmclock_abi { + /* CONSTANT FIELDS */ + uint32_t magic; +#define VMCLOCK_MAGIC 0x4b4c4356 /* "VCLK" */ + uint32_t size; /* Size of region containing this structure */ + uint16_t version; /* 1 */ + uint8_t counter_id; /* Matches VIRTIO_RTC_COUNTER_xxx except INVALID */ +#define VMCLOCK_COUNTER_ARM_VCNT 0 +#define VMCLOCK_COUNTER_X86_TSC 1 +#define VMCLOCK_COUNTER_INVALID 0xff + uint8_t time_type; /* Matches VIRTIO_RTC_TYPE_xxx */ +#define VMCLOCK_TIME_UTC 0 /* Since 1970-01-01 00:00:00z */ +#define VMCLOCK_TIME_TAI 1 /* Since 1970-01-01 00:00:00z */ +#define VMCLOCK_TIME_MONOTONIC 2 /* Since undefined epoch */ +#define VMCLOCK_TIME_INVALID_SMEARED 3 /* Not supported */ +#define VMCLOCK_TIME_INVALID_MAYBE_SMEARED 4 /* Not supported */ + + /* NON-CONSTANT FIELDS PROTECTED BY SEQCOUNT LOCK */ + uint32_t seq_count; /* Low bit means an update is in progress */ + /* + * This field changes to another non-repeating value when the CPU + * counter is disrupted, for example on live migration. This lets + * the guest know that it should discard any calibration it has + * performed of the counter against external sources (NTP/PTP/etc.). + */ + uint64_t disruption_marker; + uint64_t flags; + /* Indicates that the tai_offset_sec field is valid */ +#define VMCLOCK_FLAG_TAI_OFFSET_VALID (1 << 0) + /* + * Optionally used to notify guests of pending maintenance events. + * A guest which provides latency-sensitive services may wish to + * remove itself from service if an event is coming up. Two flags + * indicate the approximate imminence of the event. + */ +#define VMCLOCK_FLAG_DISRUPTION_SOON (1 << 1) /* About a day */ +#define VMCLOCK_FLAG_DISRUPTION_IMMINENT (1 << 2) /* About an hour */ +#define VMCLOCK_FLAG_PERIOD_ESTERROR_VALID (1 << 3) +#define VMCLOCK_FLAG_PERIOD_MAXERROR_VALID (1 << 4) +#define VMCLOCK_FLAG_TIME_ESTERROR_VALID (1 << 5) +#define VMCLOCK_FLAG_TIME_MAXERROR_VALID (1 << 6) + /* + * If the MONOTONIC flag is set then (other than leap seconds) it is + * guaranteed that the time calculated according this structure at + * any given moment shall never appear to be later than the time + * calculated via the structure at any *later* moment. + * + * In particular, a timestamp based on a counter reading taken + * immediately after setting the low bit of seq_count (and the + * associated memory barrier), using the previously-valid time and + * period fields, shall never be later than a timestamp based on + * a counter reading taken immediately before *clearing* the low + * bit again after the update, using the about-to-be-valid fields. + */ +#define VMCLOCK_FLAG_TIME_MONOTONIC (1 << 7) + + uint8_t pad[2]; + uint8_t clock_status; +#define VMCLOCK_STATUS_UNKNOWN 0 +#define VMCLOCK_STATUS_INITIALIZING 1 +#define VMCLOCK_STATUS_SYNCHRONIZED 2 +#define VMCLOCK_STATUS_FREERUNNING 3 +#define VMCLOCK_STATUS_UNRELIABLE 4 + + /* + * The time exposed through this device is never smeared. This field + * corresponds to the 'subtype' field in virtio-rtc, which indicates + * the smearing method. However in this case it provides a *hint* to + * the guest operating system, such that *if* the guest OS wants to + * provide its users with an alternative clock which does not follow + * UTC, it may do so in a fashion consistent with the other systems + * in the nearby environment. + */ + uint8_t leap_second_smearing_hint; /* Matches VIRTIO_RTC_SUBTYPE_xxx */ +#define VMCLOCK_SMEARING_STRICT 0 +#define VMCLOCK_SMEARING_NOON_LINEAR 1 +#define VMCLOCK_SMEARING_UTC_SLS 2 + uint16_t tai_offset_sec; /* Actually two's complement signed */ + uint8_t leap_indicator; + /* + * This field is based on the VIRTIO_RTC_LEAP_xxx values as defined + * in the current draft of virtio-rtc, but since smearing cannot be + * used with the shared memory device, some values are not used. + * + * The _POST_POS and _POST_NEG values allow the guest to perform + * its own smearing during the day or so after a leap second when + * such smearing may need to continue being applied for a leap + * second which is now theoretically "historical". + */ +#define VMCLOCK_LEAP_NONE 0x00 /* No known nearby leap second */ +#define VMCLOCK_LEAP_PRE_POS 0x01 /* Positive leap second at EOM */ +#define VMCLOCK_LEAP_PRE_NEG 0x02 /* Negative leap second at EOM */ +#define VMCLOCK_LEAP_POS 0x03 /* Set during 23:59:60 second */ +#define VMCLOCK_LEAP_POST_POS 0x04 +#define VMCLOCK_LEAP_POST_NEG 0x05 + + /* Bit shift for counter_period_frac_sec and its error rate */ + uint8_t counter_period_shift; + /* + * Paired values of counter and UTC at a given point in time. + */ + uint64_t counter_value; + /* + * Counter period, and error margin of same. The unit of these + * fields is 1/2^(64 + counter_period_shift) of a second. + */ + uint64_t counter_period_frac_sec; + uint64_t counter_period_esterror_rate_frac_sec; + uint64_t counter_period_maxerror_rate_frac_sec; + + /* + * Time according to time_type field above. + */ + uint64_t time_sec; /* Seconds since time_type epoch */ + uint64_t time_frac_sec; /* Units of 1/2^64 of a second */ + uint64_t time_esterror_nanosec; + uint64_t time_maxerror_nanosec; +}; + +#endif /* __VMCLOCK_ABI_H__ */ diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 99a8d9fa4c..8913e4fb99 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -258,6 +258,7 @@ for i in "$hdrdir"/include/linux/*virtio*.h \ "$hdrdir/include/linux/kernel.h" \ "$hdrdir/include/linux/kvm_para.h" \ "$hdrdir/include/linux/vhost_types.h" \ + "$hdrdir/include/linux/vmclock-abi.h" \ "$hdrdir/include/linux/sysinfo.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done From f65f3ebfaddbbf679c01534d55600525a3cb330c Mon Sep 17 00:00:00 2001 From: LIU Zhiwei Date: Fri, 6 Dec 2024 11:24:11 +0800 Subject: [PATCH 1067/2892] disas/riscv: Guard dec->cfg dereference for host disassemble For riscv host, it will set dec->cfg to zero. Thus we shuld guard the dec->cfg deference for riscv host disassemble. And in general, we should only use dec->cfg for target in three cases: 1) For not incompatible encodings, such as zcmp/zcmt/zfinx. 2) For maybe-ops encodings, they are better to be disassembled to the "real" extensions, such as zicfiss. The guard of dec->zimop and dec->zcmop is for comment and avoid check for every extension that encoded in maybe-ops area. 3) For custom encodings, we have to use dec->cfg to disassemble custom encodings using the same encoding area. Signed-off-by: LIU Zhiwei Suggested-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20241206032411.52528-1-zhiwei_liu@linux.alibaba.com> --- disas/riscv.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 9c1e332dde..4075ed6bfe 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2611,7 +2611,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 2: op = rv_op_c_li; break; case 3: - if (dec->cfg->ext_zcmop) { + if (dec->cfg && dec->cfg->ext_zcmop) { if ((((inst >> 2) & 0b111111) == 0b100000) && (((inst >> 11) & 0b11) == 0b0)) { unsigned int cmop_code = 0; @@ -2712,7 +2712,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) op = rv_op_c_sqsp; } else { op = rv_op_c_fsdsp; - if (dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { + if (dec->cfg && dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { switch ((inst >> 8) & 0b01111) { case 8: if (((inst >> 4) & 0b01111) >= 4) { @@ -2738,7 +2738,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } else { switch ((inst >> 10) & 0b011) { case 0: - if (!dec->cfg->ext_zcmt) { + if (dec->cfg && !dec->cfg->ext_zcmt) { break; } if (((inst >> 2) & 0xFF) >= 32) { @@ -2748,7 +2748,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - if (!dec->cfg->ext_zcmp) { + if (dec->cfg && !dec->cfg->ext_zcmp) { break; } switch ((inst >> 5) & 0b011) { @@ -2956,7 +2956,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 5: op = rv_op_auipc; - if (dec->cfg->ext_zicfilp && + if (dec->cfg && dec->cfg->ext_zicfilp && (((inst >> 7) & 0b11111) == 0b00000)) { op = rv_op_lpad; } @@ -4058,7 +4058,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 2: op = rv_op_csrrs; break; case 3: op = rv_op_csrrc; break; case 4: - if (dec->cfg->ext_zimop) { + if (dec->cfg && dec->cfg->ext_zimop) { int imm_mop5, imm_mop3, reg_num; if ((extract32(inst, 22, 10) & 0b1011001111) == 0b1000000111) { @@ -5112,28 +5112,28 @@ static GString *format_inst(size_t tab, rv_decode *dec) g_string_append(buf, rv_ireg_name_sym[dec->rs2]); break; case '3': - if (dec->cfg->ext_zfinx) { + if (dec->cfg && dec->cfg->ext_zfinx) { g_string_append(buf, rv_ireg_name_sym[dec->rd]); } else { g_string_append(buf, rv_freg_name_sym[dec->rd]); } break; case '4': - if (dec->cfg->ext_zfinx) { + if (dec->cfg && dec->cfg->ext_zfinx) { g_string_append(buf, rv_ireg_name_sym[dec->rs1]); } else { g_string_append(buf, rv_freg_name_sym[dec->rs1]); } break; case '5': - if (dec->cfg->ext_zfinx) { + if (dec->cfg && dec->cfg->ext_zfinx) { g_string_append(buf, rv_ireg_name_sym[dec->rs2]); } else { g_string_append(buf, rv_freg_name_sym[dec->rs2]); } break; case '6': - if (dec->cfg->ext_zfinx) { + if (dec->cfg && dec->cfg->ext_zfinx) { g_string_append(buf, rv_ireg_name_sym[dec->rs3]); } else { g_string_append(buf, rv_freg_name_sym[dec->rs3]); @@ -5439,7 +5439,8 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, const rv_opcode_data *opcode_data = decoders[i].opcode_data; void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func; - if (guard_func(cfg)) { + /* always_true_p don't dereference cfg */ + if (((i == 0) || cfg) && guard_func(cfg)) { dec.opcode_data = opcode_data; decode_func(&dec, isa); if (dec.op != rv_op_illegal) From 125f97925d69aad22cf766aa1f7eac63707800d8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 16:43:58 -0800 Subject: [PATCH 1068/2892] tcg: Move call abi parameters from tcg-target.h to tcg-target.c.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These defines are not required outside of tcg/tcg.c, which includes tcg-target.c.inc before use. Reduces the exported symbol set of tcg-target.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 13 +++++++++++++ tcg/aarch64/tcg-target.h | 13 ------------- tcg/arm/tcg-target.c.inc | 8 ++++++++ tcg/arm/tcg-target.h | 8 -------- tcg/i386/tcg-target.c.inc | 20 ++++++++++++++++++++ tcg/i386/tcg-target.h | 20 -------------------- tcg/loongarch64/tcg-target.c.inc | 9 +++++++++ tcg/loongarch64/tcg-target.h | 9 --------- tcg/mips/tcg-target.c.inc | 14 ++++++++++++++ tcg/mips/tcg-target.h | 14 -------------- tcg/riscv/tcg-target.c.inc | 9 +++++++++ tcg/riscv/tcg-target.h | 9 --------- tcg/s390x/tcg-target.c.inc | 8 ++++++++ tcg/s390x/tcg-target.h | 8 -------- tcg/sparc64/tcg-target.c.inc | 10 ++++++++++ tcg/sparc64/tcg-target.h | 11 ----------- tcg/tci/tcg-target.c.inc | 14 ++++++++++++++ tcg/tci/tcg-target.h | 14 -------------- 18 files changed, 105 insertions(+), 106 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index ffa8a3e519..0b018d3247 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -14,6 +14,19 @@ #include "../tcg-pool.c.inc" #include "qemu/bitops.h" +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#ifdef CONFIG_DARWIN +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#else +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN +#endif +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + /* We're going to re-use TCGType in setting of the SF bit, which controls the size of the operation performed. If we know the values match, it makes things much cleaner. */ diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 8bd9e6a5eb..cb24c0d276 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -49,19 +49,6 @@ typedef enum { #define TCG_TARGET_NB_REGS 64 -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#ifdef CONFIG_DARWIN -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -#else -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN -#endif -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL - #define have_lse (cpuinfo & CPUINFO_LSE) #define have_lse2 (cpuinfo & CPUINFO_LSE2) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 56072d89a2..f0674f23a5 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -35,6 +35,14 @@ bool use_idiv_instructions; bool use_neon_instructions; #endif +/* Used for function call generation. */ +#define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index fb7261499b..8abf15aef4 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -85,14 +85,6 @@ extern bool use_idiv_instructions; extern bool use_neon_instructions; #endif -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 8 -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF - /* optional instructions */ #define TCG_TARGET_HAS_ext8s_i32 1 #define TCG_TARGET_HAS_ext16s_i32 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 1bf50f1f62..aaf6107284 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -25,6 +25,26 @@ #include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_TARGET_STACK_ALIGN 16 +#if defined(_WIN64) +#define TCG_TARGET_CALL_STACK_OFFSET 32 +#else +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#endif +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#if defined(_WIN64) +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_VEC +#elif TCG_TARGET_REG_BITS == 64 +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL +#else +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF +#endif + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #if TCG_TARGET_REG_BITS == 64 diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index c68ac023d8..c81d509f1c 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -90,26 +90,6 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_ESP } TCGReg; -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 16 -#if defined(_WIN64) -#define TCG_TARGET_CALL_STACK_OFFSET 32 -#else -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#endif -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#if defined(_WIN64) -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_VEC -#elif TCG_TARGET_REG_BITS == 64 -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL -#else -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF -#endif - #define have_bmi1 (cpuinfo & CPUINFO_BMI1) #define have_popcnt (cpuinfo & CPUINFO_POPCNT) #define have_avx1 (cpuinfo & CPUINFO_AVX1) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 973601aec3..8ae561bfc0 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -32,6 +32,15 @@ #include "../tcg-ldst.c.inc" #include +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "zero", diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 58bd7d258e..7811530c8a 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -87,15 +87,6 @@ typedef enum { TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL - /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_div_i32 1 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 3b5b5c6d5b..ed41cd7f1b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -27,6 +27,20 @@ #include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" +/* used for function call generation */ +#define TCG_TARGET_STACK_ALIGN 16 +#if _MIPS_SIM == _ABIO32 +# define TCG_TARGET_CALL_STACK_OFFSET 16 +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF +#else +# define TCG_TARGET_CALL_STACK_OFFSET 0 +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL +#endif +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN + #if TCG_TARGET_REG_BITS == 32 # define LO_OFF (HOST_BIG_ENDIAN * 4) # define HI_OFF (4 - LO_OFF) diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index a996aa171d..d9b9f6a965 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -70,20 +70,6 @@ typedef enum { TCG_AREG0 = TCG_REG_S8, } TCGReg; -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 16 -#if _MIPS_SIM == _ABIO32 -# define TCG_TARGET_CALL_STACK_OFFSET 16 -# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF -#else -# define TCG_TARGET_CALL_STACK_OFFSET 0 -# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL -#endif -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN - /* MOVN/MOVZ instructions detection */ #if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 96f9a7e348..34402fee2a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -30,6 +30,15 @@ #include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 334c37cbe6..d23306738a 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -59,15 +59,6 @@ typedef enum { TCG_REG_TMP2 = TCG_REG_T4, } TCGReg; -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL - /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_div_i32 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 27bccc14e5..b1188525b2 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -28,6 +28,14 @@ #include "../tcg-pool.c.inc" #include "elf.h" +/* Used for function call generation. */ +#define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_STACK_OFFSET 160 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF + #define TCG_CT_CONST_S16 (1 << 8) #define TCG_CT_CONST_S32 (1 << 9) #define TCG_CT_CONST_U32 (1 << 10) diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 86aeca166f..7e0bf687b9 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -165,14 +165,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_cmpsel_vec 1 #define TCG_TARGET_HAS_tst_vec 0 -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 8 -#define TCG_TARGET_CALL_STACK_OFFSET 160 -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF - #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 32f9ec24b5..c9d105c35a 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -30,6 +30,16 @@ #include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK TCG_REG_O6 +#define TCG_TARGET_STACK_BIAS 2047 +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET (128 + 6 * 8 + TCG_TARGET_STACK_BIAS) +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%g0", diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index a18906a14e..0705308951 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -64,17 +64,6 @@ typedef enum { TCG_REG_I7, } TCGReg; -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_O6 - -#define TCG_TARGET_STACK_BIAS 2047 -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET (128 + 6*8 + TCG_TARGET_STACK_BIAS) -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL - #if defined(__VIS__) && __VIS__ >= 0x300 #define use_vis3_instructions 1 #else diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index c740864b96..e6c97e8153 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -24,6 +24,20 @@ #include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_STACK_ALIGN 8 +#if TCG_TARGET_REG_BITS == 32 +# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN +#else +# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#endif +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) { switch (op) { diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index a076f401d2..d7650343a3 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -146,20 +146,6 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_R15, } TCGReg; -/* Used for function call generation. */ -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#define TCG_TARGET_STACK_ALIGN 8 -#if TCG_TARGET_REG_BITS == 32 -# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EVEN -# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN -#else -# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -#endif -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL - #define HAVE_TCG_QEMU_TB_EXEC #define TCG_TARGET_NEED_POOL_LABELS From 4d8722183932d9502e405ae86b1889e1d8a475e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 Jan 2025 19:43:06 -0800 Subject: [PATCH 1069/2892] tcg: Replace TCGOP_VECL with TCGOP_TYPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the replacement, drop the TCGType - TCG_TYPE_V64 adjustment, except for the call to tcg_out_vec_op. Pass type to tcg_gen_op[1-6], so that all integer opcodes gain the type. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 15 +++--- include/tcg/tcg.h | 2 +- tcg/optimize.c | 10 +--- tcg/tcg-internal.h | 13 ++--- tcg/tcg-op-ldst.c | 26 ++++++---- tcg/tcg-op-vec.c | 8 +-- tcg/tcg-op.c | 113 +++++++++++++++++++++++------------------ tcg/tcg.c | 11 ++-- 8 files changed, 105 insertions(+), 93 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index d46b625e0e..6608a29376 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -712,10 +712,9 @@ QEMU specific operations Host vector operations ---------------------- -All of the vector ops have two parameters, ``TCGOP_VECL`` & ``TCGOP_VECE``. -The former specifies the length of the vector in log2 64-bit units; the -latter specifies the length of the element (if applicable) in log2 8-bit units. -E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. +All of the vector ops have two parameters, ``TCGOP_TYPE`` & ``TCGOP_VECE``. +The former specifies the length of the vector as a TCGType; the latter +specifies the length of the element (if applicable) in log2 8-bit units. .. list-table:: @@ -729,7 +728,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. * - dup_vec *v0*, *r1* - - | Duplicate the low N bits of *r1* into VECL/VECE copies across *v0*. + - | Duplicate the low N bits of *r1* into TYPE/VECE copies across *v0*. * - dupi_vec *v0*, *c* @@ -738,7 +737,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. * - dup2_vec *v0*, *r1*, *r2* - - | Duplicate *r2*:*r1* into VECL/64 copies across *v0*. This opcode is + - | Duplicate *r2*:*r1* into TYPE/64 copies across *v0*. This opcode is only present for 32-bit hosts. * - add_vec *v0*, *v1*, *v2* @@ -810,7 +809,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. .. code-block:: c - for (i = 0; i < VECL/VECE; ++i) { + for (i = 0; i < TYPE/VECE; ++i) { v0[i] = v1[i] << s2; } @@ -832,7 +831,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. .. code-block:: c - for (i = 0; i < VECL/VECE; ++i) { + for (i = 0; i < TYPE/VECE; ++i) { v0[i] = v1[i] << v2[i]; } diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index a77ed12b9d..fe053296ac 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -439,7 +439,7 @@ struct TCGOp { #define TCGOP_CALLI(X) (X)->param1 #define TCGOP_CALLO(X) (X)->param2 -#define TCGOP_VECL(X) (X)->param1 +#define TCGOP_TYPE(X) (X)->param1 #define TCGOP_VECE(X) (X)->param2 /* Make sure operands fit in the bitfields above. */ diff --git a/tcg/optimize.c b/tcg/optimize.c index c23f0d1392..6823569ee2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -370,7 +370,7 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) case TCG_TYPE_V64: case TCG_TYPE_V128: case TCG_TYPE_V256: - /* TCGOP_VECL and TCGOP_VECE remain unchanged. */ + /* TCGOP_TYPE and TCGOP_VECE remain unchanged. */ new_op = INDEX_op_mov_vec; break; default: @@ -2866,13 +2866,7 @@ void tcg_optimize(TCGContext *s) copy_propagate(&ctx, op, def->nb_oargs, def->nb_iargs); /* Pre-compute the type of the operation. */ - if (def->flags & TCG_OPF_VECTOR) { - ctx.type = TCG_TYPE_V64 + TCGOP_VECL(op); - } else if (def->flags & TCG_OPF_64BIT) { - ctx.type = TCG_TYPE_I64; - } else { - ctx.type = TCG_TYPE_I32; - } + ctx.type = TCGOP_TYPE(op); /* * Process each opcode. diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index 8099248076..072b36d85c 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -92,12 +92,13 @@ TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind); */ TCGTemp *tcg_constant_internal(TCGType type, int64_t val); -TCGOp *tcg_gen_op1(TCGOpcode, TCGArg); -TCGOp *tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); -TCGOp *tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); -TCGOp *tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); -TCGOp *tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); -TCGOp *tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op1(TCGOpcode, TCGType, TCGArg); +TCGOp *tcg_gen_op2(TCGOpcode, TCGType, TCGArg, TCGArg); +TCGOp *tcg_gen_op3(TCGOpcode, TCGType, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op4(TCGOpcode, TCGType, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op5(TCGOpcode, TCGType, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op6(TCGOpcode, TCGType, TCGArg, TCGArg, + TCGArg, TCGArg, TCGArg, TCGArg); void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index a318011229..0d8fe3b4f5 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -87,14 +87,15 @@ static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) return op; } -static void gen_ldst(TCGOpcode opc, TCGTemp *vl, TCGTemp *vh, +static void gen_ldst(TCGOpcode opc, TCGType type, TCGTemp *vl, TCGTemp *vh, TCGTemp *addr, MemOpIdx oi) { if (TCG_TARGET_REG_BITS == 64 || tcg_ctx->addr_type == TCG_TYPE_I32) { if (vh) { - tcg_gen_op4(opc, temp_arg(vl), temp_arg(vh), temp_arg(addr), oi); + tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), + temp_arg(addr), oi); } else { - tcg_gen_op3(opc, temp_arg(vl), temp_arg(addr), oi); + tcg_gen_op3(opc, type, temp_arg(vl), temp_arg(addr), oi); } } else { /* See TCGV_LOW/HIGH. */ @@ -102,10 +103,11 @@ static void gen_ldst(TCGOpcode opc, TCGTemp *vl, TCGTemp *vh, TCGTemp *ah = addr + !HOST_BIG_ENDIAN; if (vh) { - tcg_gen_op5(opc, temp_arg(vl), temp_arg(vh), + tcg_gen_op5(opc, type, temp_arg(vl), temp_arg(vh), temp_arg(al), temp_arg(ah), oi); } else { - tcg_gen_op4(opc, temp_arg(vl), temp_arg(al), temp_arg(ah), oi); + tcg_gen_op4(opc, type, temp_arg(vl), + temp_arg(al), temp_arg(ah), oi); } } } @@ -115,9 +117,9 @@ static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) if (TCG_TARGET_REG_BITS == 32) { TCGTemp *vl = tcgv_i32_temp(TCGV_LOW(v)); TCGTemp *vh = tcgv_i32_temp(TCGV_HIGH(v)); - gen_ldst(opc, vl, vh, addr, oi); + gen_ldst(opc, TCG_TYPE_I64, vl, vh, addr, oi); } else { - gen_ldst(opc, tcgv_i64_temp(v), NULL, addr, oi); + gen_ldst(opc, TCG_TYPE_I64, tcgv_i64_temp(v), NULL, addr, oi); } } @@ -250,7 +252,7 @@ static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, } else { opc = INDEX_op_qemu_ld_a64_i32; } - gen_ldst(opc, tcgv_i32_temp(val), NULL, addr, oi); + gen_ldst(opc, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr, oi); plugin_gen_mem_callbacks_i32(val, copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); @@ -319,7 +321,7 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, opc = INDEX_op_qemu_st_a64_i32; } } - gen_ldst(opc, tcgv_i32_temp(val), NULL, addr, oi); + gen_ldst(opc, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr, oi); plugin_gen_mem_callbacks_i32(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { @@ -590,7 +592,8 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, } else { opc = INDEX_op_qemu_ld_a64_i128; } - gen_ldst(opc, tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); + gen_ldst(opc, TCG_TYPE_I128, tcgv_i64_temp(lo), + tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_gen_bswap64_i64(lo, lo); @@ -710,7 +713,8 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, } else { opc = INDEX_op_qemu_st_a64_i128; } - gen_ldst(opc, tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); + gen_ldst(opc, TCG_TYPE_I128, tcgv_i64_temp(lo), + tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_temp_free_i64(lo); diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index d4bb4aee74..364cd089df 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -143,7 +143,7 @@ bool tcg_can_emit_vecop_list(const TCGOpcode *list, void vec_gen_2(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a) { TCGOp *op = tcg_emit_op(opc, 2); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -153,7 +153,7 @@ void vec_gen_3(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b) { TCGOp *op = tcg_emit_op(opc, 3); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -164,7 +164,7 @@ void vec_gen_4(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c) { TCGOp *op = tcg_emit_op(opc, 4); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -176,7 +176,7 @@ void vec_gen_6(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c, TCGArg d, TCGArg e) { TCGOp *op = tcg_emit_op(opc, 6); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 4a7e705367..872fb22ef8 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -37,34 +37,39 @@ */ #define NI __attribute__((noinline)) -TCGOp * NI tcg_gen_op1(TCGOpcode opc, TCGArg a1) +TCGOp * NI tcg_gen_op1(TCGOpcode opc, TCGType type, TCGArg a1) { TCGOp *op = tcg_emit_op(opc, 1); + TCGOP_TYPE(op) = type; op->args[0] = a1; return op; } -TCGOp * NI tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) +TCGOp * NI tcg_gen_op2(TCGOpcode opc, TCGType type, TCGArg a1, TCGArg a2) { TCGOp *op = tcg_emit_op(opc, 2); + TCGOP_TYPE(op) = type; op->args[0] = a1; op->args[1] = a2; return op; } -TCGOp * NI tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) +TCGOp * NI tcg_gen_op3(TCGOpcode opc, TCGType type, TCGArg a1, + TCGArg a2, TCGArg a3) { TCGOp *op = tcg_emit_op(opc, 3); + TCGOP_TYPE(op) = type; op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; return op; } -TCGOp * NI tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, +TCGOp * NI tcg_gen_op4(TCGOpcode opc, TCGType type, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) { TCGOp *op = tcg_emit_op(opc, 4); + TCGOP_TYPE(op) = type; op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; @@ -72,10 +77,11 @@ TCGOp * NI tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, return op; } -TCGOp * NI tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, +TCGOp * NI tcg_gen_op5(TCGOpcode opc, TCGType type, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4, TCGArg a5) { TCGOp *op = tcg_emit_op(opc, 5); + TCGOP_TYPE(op) = type; op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; @@ -84,10 +90,11 @@ TCGOp * NI tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, return op; } -TCGOp * NI tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5, TCGArg a6) +TCGOp * NI tcg_gen_op6(TCGOpcode opc, TCGType type, TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4, TCGArg a5, TCGArg a6) { TCGOp *op = tcg_emit_op(opc, 6); + TCGOP_TYPE(op) = type; op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; @@ -107,132 +114,138 @@ TCGOp * NI tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, # define DNI #endif -static void DNI tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) +static void DNI tcg_gen_op1_i32(TCGOpcode opc, TCGType type, TCGv_i32 a1) { - tcg_gen_op1(opc, tcgv_i32_arg(a1)); + tcg_gen_op1(opc, type, tcgv_i32_arg(a1)); } -static void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) +static void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGType type, TCGv_i64 a1) { - tcg_gen_op1(opc, tcgv_i64_arg(a1)); + tcg_gen_op1(opc, type, tcgv_i64_arg(a1)); } -static TCGOp * DNI tcg_gen_op1i(TCGOpcode opc, TCGArg a1) +static TCGOp * DNI tcg_gen_op1i(TCGOpcode opc, TCGType type, TCGArg a1) { - return tcg_gen_op1(opc, a1); + return tcg_gen_op1(opc, type, a1); } static void DNI tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) { - tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); + tcg_gen_op2(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); } static void DNI tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) { - tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); + tcg_gen_op2(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); } static void DNI tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3) { - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); + tcg_gen_op3(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), + tcgv_i32_arg(a2), tcgv_i32_arg(a3)); } static void DNI tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3) { - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); + tcg_gen_op3(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), + tcgv_i64_arg(a2), tcgv_i64_arg(a3)); } static void DNI tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGArg a3) { - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); + tcg_gen_op3(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); } static void DNI tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGArg a3) { - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); + tcg_gen_op3(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); } static void DNI tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, TCGv_ptr base, TCGArg offset) { - tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); + tcg_gen_op3(opc, TCG_TYPE_I32, tcgv_i32_arg(val), + tcgv_ptr_arg(base), offset); } static void DNI tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, TCGv_ptr base, TCGArg offset) { - tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); + tcg_gen_op3(opc, TCG_TYPE_I64, tcgv_i64_arg(val), + tcgv_ptr_arg(base), offset); } static void DNI tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4) { - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcg_gen_op4(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4)); } static void DNI tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, TCGv_i64 a4) { - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcg_gen_op4(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4)); } static void DNI tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGArg a4) { - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcg_gen_op4(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), a4); } static void DNI tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, TCGArg a4) { - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcg_gen_op4(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), a4); } static TCGOp * DNI tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGArg a3, TCGArg a4) { - return tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); + return tcg_gen_op4(opc, TCG_TYPE_I32, + tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); } static TCGOp * DNI tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGArg a3, TCGArg a4) { - return tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); + return tcg_gen_op4(opc, TCG_TYPE_I64, + tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); } static void DNI tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) { - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcg_gen_op5(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); } static void DNI tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) { - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcg_gen_op5(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); } static void DNI tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGArg a4, TCGArg a5) { - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcg_gen_op5(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), a4, a5); } static void DNI tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, TCGArg a4, TCGArg a5) { - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcg_gen_op5(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), a4, a5); } @@ -240,7 +253,7 @@ static void DNI tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5, TCGv_i32 a6) { - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcg_gen_op6(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), tcgv_i32_arg(a6)); } @@ -249,7 +262,7 @@ static void DNI tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5, TCGv_i64 a6) { - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcg_gen_op6(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), tcgv_i64_arg(a6)); } @@ -258,7 +271,7 @@ static void DNI tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5, TCGArg a6) { - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcg_gen_op6(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); } @@ -266,7 +279,7 @@ static void DNI tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5, TCGArg a6) { - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcg_gen_op6(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); } @@ -274,7 +287,7 @@ static TCGOp * DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4, TCGArg a5, TCGArg a6) { - return tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + return tcg_gen_op6(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); } @@ -283,7 +296,7 @@ static TCGOp * DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, void gen_set_label(TCGLabel *l) { l->present = 1; - tcg_gen_op1(INDEX_op_set_label, label_arg(l)); + tcg_gen_op1(INDEX_op_set_label, 0, label_arg(l)); } static void add_as_label_use(TCGLabel *l, TCGOp *op) @@ -296,7 +309,7 @@ static void add_as_label_use(TCGLabel *l, TCGOp *op) void tcg_gen_br(TCGLabel *l) { - add_as_label_use(l, tcg_gen_op1(INDEX_op_br, label_arg(l))); + add_as_label_use(l, tcg_gen_op1(INDEX_op_br, 0, label_arg(l))); } void tcg_gen_mb(TCGBar mb_type) @@ -314,25 +327,25 @@ void tcg_gen_mb(TCGBar mb_type) #endif if (parallel) { - tcg_gen_op1(INDEX_op_mb, mb_type); + tcg_gen_op1(INDEX_op_mb, 0, mb_type); } } void tcg_gen_plugin_cb(unsigned from) { - tcg_gen_op1(INDEX_op_plugin_cb, from); + tcg_gen_op1(INDEX_op_plugin_cb, 0, from); } void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) { - tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo); + tcg_gen_op2(INDEX_op_plugin_mem_cb, 0, tcgv_i64_arg(addr), meminfo); } /* 32 bit ops */ void tcg_gen_discard_i32(TCGv_i32 arg) { - tcg_gen_op1_i32(INDEX_op_discard, arg); + tcg_gen_op1_i32(INDEX_op_discard, TCG_TYPE_I32, arg); } void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) @@ -1467,7 +1480,7 @@ void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_discard_i64(TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op1_i64(INDEX_op_discard, arg); + tcg_gen_op1_i64(INDEX_op_discard, TCG_TYPE_I64, arg); } else { tcg_gen_discard_i32(TCGV_LOW(arg)); tcg_gen_discard_i32(TCGV_HIGH(arg)); @@ -3156,7 +3169,7 @@ void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_mov_i32(ret, TCGV_LOW(arg)); } else if (TCG_TARGET_HAS_extr_i64_i32) { - tcg_gen_op2(INDEX_op_extrl_i64_i32, + tcg_gen_op2(INDEX_op_extrl_i64_i32, TCG_TYPE_I32, tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } else { tcg_gen_mov_i32(ret, (TCGv_i32)arg); @@ -3168,7 +3181,7 @@ void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_mov_i32(ret, TCGV_HIGH(arg)); } else if (TCG_TARGET_HAS_extr_i64_i32) { - tcg_gen_op2(INDEX_op_extrh_i64_i32, + tcg_gen_op2(INDEX_op_extrh_i64_i32, TCG_TYPE_I32, tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } else { TCGv_i64 t = tcg_temp_ebb_new_i64(); @@ -3184,7 +3197,7 @@ void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) tcg_gen_mov_i32(TCGV_LOW(ret), arg); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } else { - tcg_gen_op2(INDEX_op_extu_i32_i64, + tcg_gen_op2(INDEX_op_extu_i32_i64, TCG_TYPE_I64, tcgv_i64_arg(ret), tcgv_i32_arg(arg)); } } @@ -3195,7 +3208,7 @@ void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) tcg_gen_mov_i32(TCGV_LOW(ret), arg); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); } else { - tcg_gen_op2(INDEX_op_ext_i32_i64, + tcg_gen_op2(INDEX_op_ext_i32_i64, TCG_TYPE_I64, tcgv_i64_arg(ret), tcgv_i32_arg(arg)); } } @@ -3320,7 +3333,7 @@ void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx) tcg_debug_assert(idx == TB_EXIT_REQUESTED); } - tcg_gen_op1i(INDEX_op_exit_tb, val); + tcg_gen_op1i(INDEX_op_exit_tb, 0, val); } void tcg_gen_goto_tb(unsigned idx) @@ -3335,7 +3348,7 @@ void tcg_gen_goto_tb(unsigned idx) tcg_ctx->goto_tb_issue_mask |= 1 << idx; #endif plugin_gen_disable_mem_helpers(); - tcg_gen_op1i(INDEX_op_goto_tb, idx); + tcg_gen_op1i(INDEX_op_goto_tb, 0, idx); } void tcg_gen_lookup_and_goto_ptr(void) @@ -3350,6 +3363,6 @@ void tcg_gen_lookup_and_goto_ptr(void) plugin_gen_disable_mem_helpers(); ptr = tcg_temp_ebb_new_ptr(); gen_helper_lookup_tb_ptr(ptr, tcg_env); - tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr)); + tcg_gen_op1i(INDEX_op_goto_ptr, TCG_TYPE_PTR, tcgv_ptr_arg(ptr)); tcg_temp_free_ptr(ptr); } diff --git a/tcg/tcg.c b/tcg/tcg.c index 4578b185be..6838ecdefc 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2616,7 +2616,8 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) nb_cargs = def->nb_cargs; if (def->flags & TCG_OPF_VECTOR) { - col += ne_fprintf(f, "v%d,e%d,", 64 << TCGOP_VECL(op), + col += ne_fprintf(f, "v%d,e%d,", + 8 * tcg_type_size(TCGOP_TYPE(op)), 8 << TCGOP_VECE(op)); } @@ -4709,7 +4710,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) itype = its->type; vece = TCGOP_VECE(op); - vtype = TCGOP_VECL(op) + TCG_TYPE_V64; + vtype = TCGOP_TYPE(op); if (its->val_type == TEMP_VAL_CONST) { /* Propagate constant via movi -> dupi. */ @@ -5176,8 +5177,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; default: if (def->flags & TCG_OPF_VECTOR) { - tcg_out_vec_op(s, op->opc, TCGOP_VECL(op), TCGOP_VECE(op), - new_args, const_args); + tcg_out_vec_op(s, op->opc, TCGOP_TYPE(op) - TCG_TYPE_V64, + TCGOP_VECE(op), new_args, const_args); } else { tcg_out_op(s, op->opc, new_args, const_args); } @@ -5203,7 +5204,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) { const TCGLifeData arg_life = op->life; TCGTemp *ots, *itsl, *itsh; - TCGType vtype = TCGOP_VECL(op) + TCG_TYPE_V64; + TCGType vtype = TCGOP_TYPE(op); /* This opcode is only valid for 32-bit hosts, for 64-bit elements. */ tcg_debug_assert(TCG_TARGET_REG_BITS == 32); From efefb9cb89f0db957a7b9775b89dc96c28da4a40 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 13:32:11 -0800 Subject: [PATCH 1070/2892] tcg: Move tcg_op_insert_{after,before} decls to tcg-internal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are not particularly useful outside of optimization passes. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 4 ---- tcg/tcg-internal.h | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index fe053296ac..5d96cdfc30 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -854,10 +854,6 @@ void tcg_gen_call7(void *func, TCGHelperInfo *, TCGTemp *ret, TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs); void tcg_op_remove(TCGContext *s, TCGOp *op); -TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, - TCGOpcode opc, unsigned nargs); -TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, - TCGOpcode opc, unsigned nargs); /** * tcg_remove_ops_after: diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index 072b36d85c..a648ee7a0e 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -106,4 +106,9 @@ void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); void vec_gen_6(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c, TCGArg d, TCGArg e); +TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, + TCGOpcode opc, unsigned nargs); +TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, + TCGOpcode opc, unsigned nargs); + #endif /* TCG_INTERNAL_H */ From fb744ece3a782c1dbe0331f312c7ea655c14c52f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 13:36:02 -0800 Subject: [PATCH 1071/2892] tcg: Copy TCGOP_TYPE in tcg_op_insert_{after,before} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify use within the optimizers by defaulting the new opcode to the same type as the old opcode. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tcg/tcg.c b/tcg/tcg.c index 6838ecdefc..f2bbff8079 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3249,6 +3249,8 @@ TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, TCGOpcode opc, unsigned nargs) { TCGOp *new_op = tcg_op_alloc(opc, nargs); + + TCGOP_TYPE(new_op) = TCGOP_TYPE(old_op); QTAILQ_INSERT_BEFORE(old_op, new_op, link); return new_op; } @@ -3257,6 +3259,8 @@ TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, TCGOpcode opc, unsigned nargs) { TCGOp *new_op = tcg_op_alloc(opc, nargs); + + TCGOP_TYPE(new_op) = TCGOP_TYPE(old_op); QTAILQ_INSERT_AFTER(&s->ops, old_op, new_op, link); return new_op; } From 2ccf871ea5e2b9164123529ae788df5d1360e036 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 06:38:08 -0800 Subject: [PATCH 1072/2892] tcg: Add TCGOP_FLAGS To be used by some integer operations instead of, or in addition to, a trailing constant argument. Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 5d96cdfc30..238c55c9ac 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -440,6 +440,7 @@ struct TCGOp { #define TCGOP_CALLO(X) (X)->param2 #define TCGOP_TYPE(X) (X)->param1 +#define TCGOP_FLAGS(X) (X)->param2 #define TCGOP_VECE(X) (X)->param2 /* Make sure operands fit in the bitfields above. */ From 771a5925e8142550d9469b1ed394b63bab021b15 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 16:51:36 -0800 Subject: [PATCH 1073/2892] tcg: Add type and flags arguments to tcg_op_supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 7 ++++++- tcg/tcg.c | 11 +++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 238c55c9ac..ac0a080b15 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -834,7 +834,12 @@ typedef struct TCGTargetOpDef { const char *args_ct_str[TCG_MAX_OP_ARGS]; } TCGTargetOpDef; -bool tcg_op_supported(TCGOpcode op); +/* + * tcg_op_supported: + * Query if @op, for @type and @flags, is supported by the host + * on which we are currently executing. + */ +bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags); void tcg_gen_call0(void *func, TCGHelperInfo *, TCGTemp *ret); void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *); diff --git a/tcg/tcg.c b/tcg/tcg.c index f2bbff8079..43293ca255 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1931,9 +1931,11 @@ TCGTemp *tcgv_i32_temp(TCGv_i32 v) } #endif /* CONFIG_DEBUG_TCG */ -/* Return true if OP may appear in the opcode stream. - Test the runtime variable that controls each opcode. */ -bool tcg_op_supported(TCGOpcode op) +/* + * Return true if OP may appear in the opcode stream with TYPE. + * Test the runtime variable that controls each opcode. + */ +bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) { const bool have_vec = TCG_TARGET_HAS_v64 | TCG_TARGET_HAS_v128 | TCG_TARGET_HAS_v256; @@ -6243,7 +6245,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) /* fall through */ default: /* Sanity check that we've not introduced any unhandled opcodes. */ - tcg_debug_assert(tcg_op_supported(opc)); + tcg_debug_assert(tcg_op_supported(opc, TCGOP_TYPE(op), + TCGOP_FLAGS(op))); /* Note: in order to speed up the code, it would be much faster to have specialized register allocator functions for some common argument patterns */ From 09246b1797a739c1982353eea5272735b411645d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:07:10 -0800 Subject: [PATCH 1074/2892] target/arm: Do not test TCG_TARGET_HAS_bitsel_vec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rely on tcg-op-vec.c to expand the opcode if missing. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/translate-sve.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 49d32fabc9..732453db6f 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -596,14 +596,8 @@ static void gen_bsl1n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) static void gen_bsl1n_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m, TCGv_vec k) { - if (TCG_TARGET_HAS_bitsel_vec) { - tcg_gen_not_vec(vece, n, n); - tcg_gen_bitsel_vec(vece, d, k, n, m); - } else { - tcg_gen_andc_vec(vece, n, k, n); - tcg_gen_andc_vec(vece, m, m, k); - tcg_gen_or_vec(vece, d, n, m); - } + tcg_gen_not_vec(vece, n, n); + tcg_gen_bitsel_vec(vece, d, k, n, m); } static void gen_bsl1n(unsigned vece, uint32_t d, uint32_t n, uint32_t m, @@ -640,14 +634,8 @@ static void gen_bsl2n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) static void gen_bsl2n_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m, TCGv_vec k) { - if (TCG_TARGET_HAS_bitsel_vec) { - tcg_gen_not_vec(vece, m, m); - tcg_gen_bitsel_vec(vece, d, k, n, m); - } else { - tcg_gen_and_vec(vece, n, n, k); - tcg_gen_or_vec(vece, m, m, k); - tcg_gen_orc_vec(vece, d, n, m); - } + tcg_gen_not_vec(vece, m, m); + tcg_gen_bitsel_vec(vece, d, k, n, m); } static void gen_bsl2n(unsigned vece, uint32_t d, uint32_t n, uint32_t m, From 3a4fb570135d334af00313755addd0fc5fdf022d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:08:31 -0800 Subject: [PATCH 1075/2892] target/arm: Use tcg_op_supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not reference TCG_TARGET_HAS_* directly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 10 ++++++---- target/arm/tcg/translate-sve.c | 2 +- target/arm/tcg/translate.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 7c65fc3a3b..bd814849c1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8219,6 +8219,7 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) TCGv_i64 tcg_rn, tcg_y; DisasCompare c; unsigned nzcv; + bool has_andc; /* Set T0 = !COND. */ arm_test_cc(&c, a->cond); @@ -8249,17 +8250,18 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); nzcv = a->nzcv; + has_andc = tcg_op_supported(INDEX_op_andc_i32, TCG_TYPE_I32, 0); if (nzcv & 8) { /* N */ tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); } else { - if (TCG_TARGET_HAS_andc_i32) { + if (has_andc) { tcg_gen_andc_i32(cpu_NF, cpu_NF, tcg_t1); } else { tcg_gen_and_i32(cpu_NF, cpu_NF, tcg_t2); } } if (nzcv & 4) { /* Z */ - if (TCG_TARGET_HAS_andc_i32) { + if (has_andc) { tcg_gen_andc_i32(cpu_ZF, cpu_ZF, tcg_t1); } else { tcg_gen_and_i32(cpu_ZF, cpu_ZF, tcg_t2); @@ -8270,7 +8272,7 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) if (nzcv & 2) { /* C */ tcg_gen_or_i32(cpu_CF, cpu_CF, tcg_t0); } else { - if (TCG_TARGET_HAS_andc_i32) { + if (has_andc) { tcg_gen_andc_i32(cpu_CF, cpu_CF, tcg_t1); } else { tcg_gen_and_i32(cpu_CF, cpu_CF, tcg_t2); @@ -8279,7 +8281,7 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) if (nzcv & 1) { /* V */ tcg_gen_or_i32(cpu_VF, cpu_VF, tcg_t1); } else { - if (TCG_TARGET_HAS_andc_i32) { + if (has_andc) { tcg_gen_andc_i32(cpu_VF, cpu_VF, tcg_t1); } else { tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 732453db6f..e303196592 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -622,7 +622,7 @@ static void gen_bsl2n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) * = | ~(m | k) */ tcg_gen_and_i64(n, n, k); - if (TCG_TARGET_HAS_orc_i64) { + if (tcg_op_supported(INDEX_op_orc_i64, TCG_TYPE_I64, 0)) { tcg_gen_or_i64(m, m, k); tcg_gen_orc_i64(d, n, m); } else { diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index c16b59ab88..68ac393415 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -493,7 +493,7 @@ static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) static void gen_adc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) { TCGv_i32 tmp = tcg_temp_new_i32(); - if (TCG_TARGET_HAS_add2_i32) { + if (tcg_op_supported(INDEX_op_add2_i32, TCG_TYPE_I32, 0)) { tcg_gen_movi_i32(tmp, 0); tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, cpu_CF, tmp); tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1, tmp); From 80a3a9423a86cc5dae6c6e1794328465d105d73c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:15:28 -0800 Subject: [PATCH 1076/2892] target/tricore: Use tcg_op_supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not reference TCG_TARGET_HAS_* directly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/tricore/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 0ef3743f3e..6819b77668 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3980,7 +3980,7 @@ static void decode_bit_andacc(DisasContext *ctx) pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_and_tl); break; case OPC2_32_BIT_AND_NOR_T: - if (TCG_TARGET_HAS_andc_i32) { + if (tcg_op_supported(INDEX_op_andc_i32, TCG_TYPE_I32, 0)) { gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_andc_tl); } else { @@ -4113,7 +4113,7 @@ static void decode_bit_orand(DisasContext *ctx) pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_or_tl); break; case OPC2_32_BIT_OR_NOR_T: - if (TCG_TARGET_HAS_orc_i32) { + if (tcg_op_supported(INDEX_op_orc_i32, TCG_TYPE_I32, 0)) { gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_orc_tl); } else { From 0e4c6424d639b1e2e2b780a2692d47491b7260ae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:32:06 -0800 Subject: [PATCH 1077/2892] tcg: Add tcg_op_deposit_valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 6 ++++++ tcg/tcg.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index ac0a080b15..63f7eb3adf 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -840,6 +840,12 @@ typedef struct TCGTargetOpDef { * on which we are currently executing. */ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags); +/* + * tcg_op_deposit_valid: + * Query if a deposit into (ofs, len) is supported for @type by + * the host on which we are currently executing. + */ +bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len); void tcg_gen_call0(void *func, TCGHelperInfo *, TCGTemp *ret); void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *); diff --git a/tcg/tcg.c b/tcg/tcg.c index 43293ca255..6b318873ca 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2238,6 +2238,27 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) } } +bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len) +{ + tcg_debug_assert(len > 0); + switch (type) { + case TCG_TYPE_I32: + tcg_debug_assert(ofs < 32); + tcg_debug_assert(len <= 32); + tcg_debug_assert(ofs + len <= 32); + return TCG_TARGET_HAS_deposit_i32 && + TCG_TARGET_deposit_i32_valid(ofs, len); + case TCG_TYPE_I64: + tcg_debug_assert(ofs < 64); + tcg_debug_assert(len <= 64); + tcg_debug_assert(ofs + len <= 64); + return TCG_TARGET_HAS_deposit_i64 && + TCG_TARGET_deposit_i64_valid(ofs, len); + default: + g_assert_not_reached(); + } +} + static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); static void tcg_gen_callN(void *func, TCGHelperInfo *info, From 20fab3c2106eafc4bb306d5d2c721fcacb5b10c6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:25:36 -0800 Subject: [PATCH 1078/2892] target/i386: Remove TCG_TARGET_extract_tl_valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro is unused. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/emit.c.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 785ff63f2a..ab416627b7 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -26,11 +26,9 @@ #ifdef TARGET_X86_64 #define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 #define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i64_valid -#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i64_valid #else #define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 #define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i32_valid -#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i32_valid #endif #define MMX_OFFSET(reg) \ From 34220513bbb88bc2fc53470a83c73c828377570f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:34:25 -0800 Subject: [PATCH 1079/2892] target/i386: Use tcg_op_deposit_valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid direct usage of TCG_TARGET_deposit_*_valid. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/emit.c.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index ab416627b7..a2b940a5c3 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -25,10 +25,8 @@ */ #ifdef TARGET_X86_64 #define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 -#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i64_valid #else #define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 -#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i32_valid #endif #define MMX_OFFSET(reg) \ @@ -3449,7 +3447,7 @@ static void gen_RCL(DisasContext *s, X86DecodedInsn *decode) } /* Compute high part, including incoming carry. */ - if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) { + if (!have_1bit_cin || tcg_op_deposit_valid(TCG_TYPE_TL, 1, TARGET_LONG_BITS - 1)) { /* high = (T0 << 1) | cin */ TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src; tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1); @@ -3501,7 +3499,7 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) } /* Save incoming carry into high, it will be shifted later. */ - if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) { + if (!have_1bit_cin || tcg_op_deposit_valid(TCG_TYPE_TL, 1, TARGET_LONG_BITS - 1)) { TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src; tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1); } else { From a4ca7f4a3e2467ce8378fbf8ff2379f29a8a0724 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 17:11:24 -0800 Subject: [PATCH 1080/2892] target/i386: Use tcg_op_supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not reference TCG_TARGET_HAS_* directly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/emit.c.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index a2b940a5c3..f305640182 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -24,9 +24,9 @@ * The exact opcode to check depends on 32- vs. 64-bit. */ #ifdef TARGET_X86_64 -#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 +#define INDEX_op_extract2_tl INDEX_op_extract2_i64 #else -#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 +#define INDEX_op_extract2_tl INDEX_op_extract2_i32 #endif #define MMX_OFFSET(reg) \ @@ -2993,7 +2993,7 @@ static void gen_PMOVMSKB(DisasContext *s, X86DecodedInsn *decode) tcg_gen_ld8u_tl(s->T0, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); while (vec_len > 8) { vec_len -= 8; - if (TCG_TARGET_HAS_extract2_tl) { + if (tcg_op_supported(INDEX_op_extract2_tl, TCG_TYPE_TL, 0)) { /* * Load the next byte of the result into the high byte of T. * TCG does a similar expansion of deposit to shl+extract2; by From a417ef835058995d09a9d2e58c04c4bf640563e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 21:43:22 -0800 Subject: [PATCH 1081/2892] tcg: Remove TCG_TARGET_NEED_LDST_LABELS and TCG_TARGET_NEED_POOL_LABELS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make these features unconditional, as they're used by most tcg backends anyway. Merge tcg-ldst.c.inc and tcg-pool.c.inc into tcg.c and mark some of the functions unused, so that when the features are not used we won't get Werrors. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 4 - tcg/aarch64/tcg-target.c.inc | 2 - tcg/aarch64/tcg-target.h | 2 - tcg/arm/tcg-target.c.inc | 2 - tcg/arm/tcg-target.h | 2 - tcg/i386/tcg-target.c.inc | 3 - tcg/i386/tcg-target.h | 2 - tcg/loongarch64/tcg-target.c.inc | 9 +- tcg/loongarch64/tcg-target.h | 2 - tcg/mips/tcg-target.c.inc | 3 - tcg/mips/tcg-target.h | 2 - tcg/ppc/tcg-target.c.inc | 2 - tcg/ppc/tcg-target.h | 2 - tcg/riscv/tcg-target.c.inc | 3 - tcg/riscv/tcg-target.h | 3 - tcg/s390x/tcg-target.c.inc | 2 - tcg/s390x/tcg-target.h | 2 - tcg/sparc64/tcg-target.c.inc | 3 - tcg/sparc64/tcg-target.h | 2 - tcg/tcg-ldst.c.inc | 65 ---------- tcg/tcg-pool.c.inc | 162 ------------------------ tcg/tcg.c | 211 +++++++++++++++++++++++++++++-- tcg/tci/tcg-target.c.inc | 12 +- 23 files changed, 216 insertions(+), 286 deletions(-) delete mode 100644 tcg/tcg-ldst.c.inc delete mode 100644 tcg/tcg-pool.c.inc diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 63f7eb3adf..2671321cb5 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -498,12 +498,8 @@ struct TCGContext { CPUState *cpu; /* *_trans */ /* These structures are private to tcg-target.c.inc. */ -#ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_HEAD(, TCGLabelQemuLdst) ldst_labels; -#endif -#ifdef TCG_TARGET_NEED_POOL_LABELS struct TCGLabelPoolData *pool_labels; -#endif TCGLabel *exitreq_label; diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 0b018d3247..d77d305f30 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -10,8 +10,6 @@ * 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" /* Used for function call generation. */ diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index cb24c0d276..d8ca52d32d 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -157,7 +157,5 @@ typedef enum { #define TCG_TARGET_HAS_tst_vec 1 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif /* AARCH64_TCG_TARGET_H */ diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index f0674f23a5..90ac80077f 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -23,8 +23,6 @@ */ #include "elf.h" -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" int arm_arch = __ARM_ARCH; diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 8abf15aef4..7f6e24f861 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -145,7 +145,5 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_tst_vec 1 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index aaf6107284..167228a781 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -22,9 +22,6 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" - /* Used for function call generation. */ #define TCG_TARGET_STACK_ALIGN 16 #if defined(_WIN64) diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index c81d509f1c..9961d8e757 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -227,7 +227,5 @@ typedef enum { #include "tcg/tcg-mo.h" #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 8ae561bfc0..a273e7fce5 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -29,7 +29,6 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" #include /* used for function call generation */ @@ -2465,6 +2464,14 @@ static void tcg_out_tb_start(TCGContext *s) /* nothing to do */ } +static void tcg_out_nop_fill(tcg_insn_unit *p, int count) +{ + for (int i = 0; i < count; ++i) { + /* Canonical nop is andi r0,r0,0 */ + p[i] = OPC_ANDI; + } +} + static void tcg_target_init(TCGContext *s) { unsigned long hwcap = qemu_getauxval(AT_HWCAP); diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 7811530c8a..3bc9aafaf2 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -189,6 +189,4 @@ typedef enum { #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS - #endif /* LOONGARCH_TCG_TARGET_H */ diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index ed41cd7f1b..8857398893 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -24,9 +24,6 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" - /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 16 #if _MIPS_SIM == _ABIO32 diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index d9b9f6a965..db60eb7c1b 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -183,7 +183,5 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_tst 0 #define TCG_TARGET_DEFAULT_MO 0 -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 9a11c26fd3..94997b126f 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -23,8 +23,6 @@ */ #include "elf.h" -#include "../tcg-pool.c.inc" -#include "../tcg-ldst.c.inc" /* * Standardize on the _CALL_FOO symbols used by GCC: diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 0b2171d38c..8291e0127d 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -176,7 +176,5 @@ typedef enum { #define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 34402fee2a..7d1bba100a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -27,9 +27,6 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" - /* Used for function call generation. */ #define TCG_REG_CALL_STACK TCG_REG_SP #define TCG_TARGET_STACK_ALIGN 16 diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index d23306738a..bfaa99ccdd 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -161,7 +161,4 @@ typedef enum { #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS - #endif diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index b1188525b2..fdf57c0b07 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -24,8 +24,6 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" #include "elf.h" /* Used for function call generation. */ diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 7e0bf687b9..223d3f6ca1 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -166,7 +166,5 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index c9d105c35a..fe3e727399 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -27,9 +27,6 @@ #error "unsupported code generation mode" #endif -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" - /* Used for function call generation. */ #define TCG_REG_CALL_STACK TCG_REG_O6 #define TCG_TARGET_STACK_BIAS 2047 diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 0705308951..b560d43ed5 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -143,7 +143,5 @@ extern bool use_vis3_instructions; #define TCG_AREG0 TCG_REG_I0 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/tcg-ldst.c.inc b/tcg/tcg-ldst.c.inc deleted file mode 100644 index ffada04af0..0000000000 --- a/tcg/tcg-ldst.c.inc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * TCG Backend Data: load-store optimization only. - * - * 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. - */ - -/* - * Generate TB finalization at the end of block - */ - -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l); -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l); - -static int tcg_out_ldst_finalize(TCGContext *s) -{ - TCGLabelQemuLdst *lb; - - /* qemu_ld/st slow paths */ - QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { - if (lb->is_ld - ? !tcg_out_qemu_ld_slow_path(s, lb) - : !tcg_out_qemu_st_slow_path(s, lb)) { - return -2; - } - - /* Test for (pending) buffer overflow. The assumption is that any - one operation beginning below the high water mark cannot overrun - the buffer completely. Thus we can test for overflow after - generating code without having to check during generation. */ - if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { - return -1; - } - } - return 0; -} - -/* - * Allocate a new TCGLabelQemuLdst entry. - */ - -static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) -{ - TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - - memset(l, 0, sizeof(*l)); - QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); - - return l; -} diff --git a/tcg/tcg-pool.c.inc b/tcg/tcg-pool.c.inc deleted file mode 100644 index 90c2e63b7f..0000000000 --- a/tcg/tcg-pool.c.inc +++ /dev/null @@ -1,162 +0,0 @@ -/* - * TCG Backend Data: constant pool. - * - * 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. - */ - -typedef struct TCGLabelPoolData { - struct TCGLabelPoolData *next; - tcg_insn_unit *label; - intptr_t addend; - int rtype; - unsigned nlong; - tcg_target_ulong data[]; -} TCGLabelPoolData; - - -static TCGLabelPoolData *new_pool_alloc(TCGContext *s, int nlong, int rtype, - tcg_insn_unit *label, intptr_t addend) -{ - TCGLabelPoolData *n = tcg_malloc(sizeof(TCGLabelPoolData) - + sizeof(tcg_target_ulong) * nlong); - - n->label = label; - n->addend = addend; - n->rtype = rtype; - n->nlong = nlong; - return n; -} - -static void new_pool_insert(TCGContext *s, TCGLabelPoolData *n) -{ - TCGLabelPoolData *i, **pp; - int nlong = n->nlong; - - /* Insertion sort on the pool. */ - for (pp = &s->pool_labels; (i = *pp) != NULL; pp = &i->next) { - if (nlong > i->nlong) { - break; - } - if (nlong < i->nlong) { - continue; - } - if (memcmp(n->data, i->data, sizeof(tcg_target_ulong) * nlong) >= 0) { - break; - } - } - n->next = *pp; - *pp = n; -} - -/* The "usual" for generic integer code. */ -static inline void new_pool_label(TCGContext *s, tcg_target_ulong d, int rtype, - tcg_insn_unit *label, intptr_t addend) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 1, rtype, label, addend); - n->data[0] = d; - new_pool_insert(s, n); -} - -/* For v64 or v128, depending on the host. */ -static inline void new_pool_l2(TCGContext *s, int rtype, tcg_insn_unit *label, - intptr_t addend, tcg_target_ulong d0, - tcg_target_ulong d1) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 2, rtype, label, addend); - n->data[0] = d0; - n->data[1] = d1; - new_pool_insert(s, n); -} - -/* For v128 or v256, depending on the host. */ -static inline void new_pool_l4(TCGContext *s, int rtype, tcg_insn_unit *label, - intptr_t addend, tcg_target_ulong d0, - tcg_target_ulong d1, tcg_target_ulong d2, - tcg_target_ulong d3) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 4, rtype, label, addend); - n->data[0] = d0; - n->data[1] = d1; - n->data[2] = d2; - n->data[3] = d3; - new_pool_insert(s, n); -} - -/* For v256, for 32-bit host. */ -static inline void new_pool_l8(TCGContext *s, int rtype, tcg_insn_unit *label, - intptr_t addend, tcg_target_ulong d0, - tcg_target_ulong d1, tcg_target_ulong d2, - tcg_target_ulong d3, tcg_target_ulong d4, - tcg_target_ulong d5, tcg_target_ulong d6, - tcg_target_ulong d7) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 8, rtype, label, addend); - n->data[0] = d0; - n->data[1] = d1; - n->data[2] = d2; - n->data[3] = d3; - n->data[4] = d4; - n->data[5] = d5; - n->data[6] = d6; - n->data[7] = d7; - new_pool_insert(s, n); -} - -/* To be provided by cpu/tcg-target.c.inc. */ -static void tcg_out_nop_fill(tcg_insn_unit *p, int count); - -static int tcg_out_pool_finalize(TCGContext *s) -{ - TCGLabelPoolData *p = s->pool_labels; - TCGLabelPoolData *l = NULL; - void *a; - - if (p == NULL) { - return 0; - } - - /* ??? Round up to qemu_icache_linesize, but then do not round - again when allocating the next TranslationBlock structure. */ - a = (void *)ROUND_UP((uintptr_t)s->code_ptr, - sizeof(tcg_target_ulong) * p->nlong); - tcg_out_nop_fill(s->code_ptr, (tcg_insn_unit *)a - s->code_ptr); - s->data_gen_ptr = a; - - for (; p != NULL; p = p->next) { - size_t size = sizeof(tcg_target_ulong) * p->nlong; - uintptr_t value; - - if (!l || l->nlong != p->nlong || memcmp(l->data, p->data, size)) { - if (unlikely(a > s->code_gen_highwater)) { - return -1; - } - memcpy(a, p->data, size); - a += size; - l = p; - } - - value = (uintptr_t)tcg_splitwx_to_rx(a) - size; - if (!patch_reloc(p->label, p->rtype, value, p->addend)) { - return -2; - } - } - - s->code_ptr = a; - return 0; -} diff --git a/tcg/tcg.c b/tcg/tcg.c index 6b318873ca..7f5d014973 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -66,6 +66,11 @@ static void tcg_target_init(TCGContext *s); static void tcg_target_qemu_prologue(TCGContext *s); static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend); +static void tcg_out_nop_fill(tcg_insn_unit *p, int count); + +typedef struct TCGLabelQemuLdst TCGLabelQemuLdst; +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l); +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l); /* The CIE and FDE header definitions will be common to all hosts. */ typedef struct { @@ -90,7 +95,7 @@ typedef struct QEMU_PACKED { DebugFrameFDEHeader fde; } DebugFrameHeader; -typedef struct TCGLabelQemuLdst { +struct TCGLabelQemuLdst { bool is_ld; /* qemu_ld: true, qemu_st: false */ MemOpIdx oi; TCGType type; /* result type of a load */ @@ -101,7 +106,7 @@ typedef struct TCGLabelQemuLdst { const tcg_insn_unit *raddr; /* addr of the next IR of qemu_ld/st IR */ tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ QSIMPLEQ_ENTRY(TCGLabelQemuLdst) next; -} TCGLabelQemuLdst; +}; static void tcg_register_jit_int(const void *buf, size_t size, const void *debug_frame, @@ -175,9 +180,6 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot); static bool tcg_target_const_match(int64_t val, int ct, TCGType type, TCGCond cond, int vece); -#ifdef TCG_TARGET_NEED_LDST_LABELS -static int tcg_out_ldst_finalize(TCGContext *s); -#endif #ifndef CONFIG_USER_ONLY #define guest_base ({ qemu_build_not_reached(); (uintptr_t)0; }) @@ -634,6 +636,197 @@ static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, } } +/* + * Allocate a new TCGLabelQemuLdst entry. + */ + +__attribute__((unused)) +static TCGLabelQemuLdst *new_ldst_label(TCGContext *s) +{ + TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); + + memset(l, 0, sizeof(*l)); + QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); + + return l; +} + +/* + * Allocate new constant pool entries. + */ + +typedef struct TCGLabelPoolData { + struct TCGLabelPoolData *next; + tcg_insn_unit *label; + intptr_t addend; + int rtype; + unsigned nlong; + tcg_target_ulong data[]; +} TCGLabelPoolData; + +static TCGLabelPoolData *new_pool_alloc(TCGContext *s, int nlong, int rtype, + tcg_insn_unit *label, intptr_t addend) +{ + TCGLabelPoolData *n = tcg_malloc(sizeof(TCGLabelPoolData) + + sizeof(tcg_target_ulong) * nlong); + + n->label = label; + n->addend = addend; + n->rtype = rtype; + n->nlong = nlong; + return n; +} + +static void new_pool_insert(TCGContext *s, TCGLabelPoolData *n) +{ + TCGLabelPoolData *i, **pp; + int nlong = n->nlong; + + /* Insertion sort on the pool. */ + for (pp = &s->pool_labels; (i = *pp) != NULL; pp = &i->next) { + if (nlong > i->nlong) { + break; + } + if (nlong < i->nlong) { + continue; + } + if (memcmp(n->data, i->data, sizeof(tcg_target_ulong) * nlong) >= 0) { + break; + } + } + n->next = *pp; + *pp = n; +} + +/* The "usual" for generic integer code. */ +__attribute__((unused)) +static void new_pool_label(TCGContext *s, tcg_target_ulong d, int rtype, + tcg_insn_unit *label, intptr_t addend) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 1, rtype, label, addend); + n->data[0] = d; + new_pool_insert(s, n); +} + +/* For v64 or v128, depending on the host. */ +__attribute__((unused)) +static void new_pool_l2(TCGContext *s, int rtype, tcg_insn_unit *label, + intptr_t addend, tcg_target_ulong d0, + tcg_target_ulong d1) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 2, rtype, label, addend); + n->data[0] = d0; + n->data[1] = d1; + new_pool_insert(s, n); +} + +/* For v128 or v256, depending on the host. */ +__attribute__((unused)) +static void new_pool_l4(TCGContext *s, int rtype, tcg_insn_unit *label, + intptr_t addend, tcg_target_ulong d0, + tcg_target_ulong d1, tcg_target_ulong d2, + tcg_target_ulong d3) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 4, rtype, label, addend); + n->data[0] = d0; + n->data[1] = d1; + n->data[2] = d2; + n->data[3] = d3; + new_pool_insert(s, n); +} + +/* For v256, for 32-bit host. */ +__attribute__((unused)) +static void new_pool_l8(TCGContext *s, int rtype, tcg_insn_unit *label, + intptr_t addend, tcg_target_ulong d0, + tcg_target_ulong d1, tcg_target_ulong d2, + tcg_target_ulong d3, tcg_target_ulong d4, + tcg_target_ulong d5, tcg_target_ulong d6, + tcg_target_ulong d7) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 8, rtype, label, addend); + n->data[0] = d0; + n->data[1] = d1; + n->data[2] = d2; + n->data[3] = d3; + n->data[4] = d4; + n->data[5] = d5; + n->data[6] = d6; + n->data[7] = d7; + new_pool_insert(s, n); +} + +/* + * Generate TB finalization at the end of block + */ + +static int tcg_out_ldst_finalize(TCGContext *s) +{ + TCGLabelQemuLdst *lb; + + /* qemu_ld/st slow paths */ + QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { + if (lb->is_ld + ? !tcg_out_qemu_ld_slow_path(s, lb) + : !tcg_out_qemu_st_slow_path(s, lb)) { + return -2; + } + + /* + * Test for (pending) buffer overflow. The assumption is that any + * one operation beginning below the high water mark cannot overrun + * the buffer completely. Thus we can test for overflow after + * generating code without having to check during generation. + */ + if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { + return -1; + } + } + return 0; +} + +static int tcg_out_pool_finalize(TCGContext *s) +{ + TCGLabelPoolData *p = s->pool_labels; + TCGLabelPoolData *l = NULL; + void *a; + + if (p == NULL) { + return 0; + } + + /* + * ??? Round up to qemu_icache_linesize, but then do not round + * again when allocating the next TranslationBlock structure. + */ + a = (void *)ROUND_UP((uintptr_t)s->code_ptr, + sizeof(tcg_target_ulong) * p->nlong); + tcg_out_nop_fill(s->code_ptr, (tcg_insn_unit *)a - s->code_ptr); + s->data_gen_ptr = a; + + for (; p != NULL; p = p->next) { + size_t size = sizeof(tcg_target_ulong) * p->nlong; + uintptr_t value; + + if (!l || l->nlong != p->nlong || memcmp(l->data, p->data, size)) { + if (unlikely(a > s->code_gen_highwater)) { + return -1; + } + memcpy(a, p->data, size); + a += size; + l = p; + } + + value = (uintptr_t)tcg_splitwx_to_rx(a) - size; + if (!patch_reloc(p->label, p->rtype, value, p->addend)) { + return -2; + } + } + + s->code_ptr = a; + return 0; +} + #define C_PFX1(P, A) P##A #define C_PFX2(P, A, B) P##A##_##B #define C_PFX3(P, A, B, C) P##A##_##B##_##C @@ -6204,12 +6397,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) s->code_ptr = s->code_buf; s->data_gen_ptr = NULL; -#ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_INIT(&s->ldst_labels); -#endif -#ifdef TCG_TARGET_NEED_POOL_LABELS s->pool_labels = NULL; -#endif start_words = s->insn_start_words; s->gen_insn_data = @@ -6290,18 +6479,14 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); /* Generate TB finalization at the end of block */ -#ifdef TCG_TARGET_NEED_LDST_LABELS i = tcg_out_ldst_finalize(s); if (i < 0) { return i; } -#endif -#ifdef TCG_TARGET_NEED_POOL_LABELS i = tcg_out_pool_finalize(s); if (i < 0) { return i; } -#endif if (!tcg_resolve_relocs(s)) { return -2; } diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index e6c97e8153..5f88ca0537 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -22,8 +22,6 @@ * THE SOFTWARE. */ -#include "../tcg-pool.c.inc" - /* Used for function call generation. */ #define TCG_TARGET_CALL_STACK_OFFSET 0 #define TCG_TARGET_STACK_ALIGN 8 @@ -979,3 +977,13 @@ bool tcg_target_has_memory_bswap(MemOp memop) { return true; } + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + g_assert_not_reached(); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + g_assert_not_reached(); +} From 76da0a9c83e65634cb24502e642a194177a24853 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 22:06:54 -0800 Subject: [PATCH 1082/2892] tcg: Rename tcg-target.opc.h to tcg-target-opc.h.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition, add empty files for mips, sparc64 and tci. Make the include unconditional within tcg-opc.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 4 +--- tcg/aarch64/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/arm/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/i386/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/loongarch64/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/mips/tcg-target-opc.h.inc | 1 + tcg/ppc/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/riscv/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/s390x/{tcg-target.opc.h => tcg-target-opc.h.inc} | 0 tcg/sparc64/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target-opc.h.inc | 1 + 11 files changed, 4 insertions(+), 3 deletions(-) rename tcg/aarch64/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) rename tcg/arm/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) rename tcg/i386/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) rename tcg/loongarch64/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) create mode 100644 tcg/mips/tcg-target-opc.h.inc rename tcg/ppc/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) rename tcg/riscv/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) rename tcg/s390x/{tcg-target.opc.h => tcg-target-opc.h.inc} (100%) create mode 100644 tcg/sparc64/tcg-target-opc.h.inc create mode 100644 tcg/tci/tcg-target-opc.h.inc diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 546eb49c11..93622f3f6b 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -301,9 +301,7 @@ DEF(cmpsel_vec, 1, 4, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_cmpsel_vec)) DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT) -#if TCG_TARGET_MAYBE_vec -#include "tcg-target.opc.h" -#endif +#include "tcg-target-opc.h.inc" #ifdef TCG_TARGET_INTERPRETER /* These opcodes are only for use between the tci generator and interpreter. */ diff --git a/tcg/aarch64/tcg-target.opc.h b/tcg/aarch64/tcg-target-opc.h.inc similarity index 100% rename from tcg/aarch64/tcg-target.opc.h rename to tcg/aarch64/tcg-target-opc.h.inc diff --git a/tcg/arm/tcg-target.opc.h b/tcg/arm/tcg-target-opc.h.inc similarity index 100% rename from tcg/arm/tcg-target.opc.h rename to tcg/arm/tcg-target-opc.h.inc diff --git a/tcg/i386/tcg-target.opc.h b/tcg/i386/tcg-target-opc.h.inc similarity index 100% rename from tcg/i386/tcg-target.opc.h rename to tcg/i386/tcg-target-opc.h.inc diff --git a/tcg/loongarch64/tcg-target.opc.h b/tcg/loongarch64/tcg-target-opc.h.inc similarity index 100% rename from tcg/loongarch64/tcg-target.opc.h rename to tcg/loongarch64/tcg-target-opc.h.inc diff --git a/tcg/mips/tcg-target-opc.h.inc b/tcg/mips/tcg-target-opc.h.inc new file mode 100644 index 0000000000..84e777bfe5 --- /dev/null +++ b/tcg/mips/tcg-target-opc.h.inc @@ -0,0 +1 @@ +/* No target specific opcodes. */ diff --git a/tcg/ppc/tcg-target.opc.h b/tcg/ppc/tcg-target-opc.h.inc similarity index 100% rename from tcg/ppc/tcg-target.opc.h rename to tcg/ppc/tcg-target-opc.h.inc diff --git a/tcg/riscv/tcg-target.opc.h b/tcg/riscv/tcg-target-opc.h.inc similarity index 100% rename from tcg/riscv/tcg-target.opc.h rename to tcg/riscv/tcg-target-opc.h.inc diff --git a/tcg/s390x/tcg-target.opc.h b/tcg/s390x/tcg-target-opc.h.inc similarity index 100% rename from tcg/s390x/tcg-target.opc.h rename to tcg/s390x/tcg-target-opc.h.inc diff --git a/tcg/sparc64/tcg-target-opc.h.inc b/tcg/sparc64/tcg-target-opc.h.inc new file mode 100644 index 0000000000..84e777bfe5 --- /dev/null +++ b/tcg/sparc64/tcg-target-opc.h.inc @@ -0,0 +1 @@ +/* No target specific opcodes. */ diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc new file mode 100644 index 0000000000..84e777bfe5 --- /dev/null +++ b/tcg/tci/tcg-target-opc.h.inc @@ -0,0 +1 @@ +/* No target specific opcodes. */ From 87431dd6a94e757858d131279040aec17a444eec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Dec 2024 13:13:56 -0800 Subject: [PATCH 1083/2892] tcg/tci: Move TCI specific opcodes to tcg-target-opc.h.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that tcg-target-opc.h.inc is unconditional, we can move these out of the generic header. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 6 ------ tcg/tci/tcg-target-opc.h.inc | 5 ++++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 93622f3f6b..14aff6e7f9 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -303,12 +303,6 @@ DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT) #include "tcg-target-opc.h.inc" -#ifdef TCG_TARGET_INTERPRETER -/* These opcodes are only for use between the tci generator and interpreter. */ -DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) -DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) -#endif - #undef DATA64_ARGS #undef IMPL #undef IMPL64 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 84e777bfe5..ecc8c4e55e 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -1 +1,4 @@ -/* No target specific opcodes. */ +/* SPDX-License-Identifier: MIT */ +/* These opcodes for use between the tci generator and interpreter. */ +DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) From 7d3e705a96a980d80fed00de7ab4afa368a22f3f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Dec 2024 22:36:59 -0800 Subject: [PATCH 1084/2892] tcg: Move fallback tcg_can_emit_vec_op out of line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't reference TCG_TARGET_MAYBE_vec in a public header. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 7 ------- tcg/tcg.c | 4 ++++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 2671321cb5..fc379bb122 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -1020,17 +1020,10 @@ extern tcg_prologue_fn *tcg_qemu_tb_exec; void tcg_register_jit(const void *buf, size_t buf_size); -#if TCG_TARGET_MAYBE_vec /* Return zero if the tuple (opc, type, vece) is unsupportable; return > 0 if it is directly supportable; return < 0 if we must call tcg_expand_vec_op. */ int tcg_can_emit_vec_op(TCGOpcode, TCGType, unsigned); -#else -static inline int tcg_can_emit_vec_op(TCGOpcode o, TCGType t, unsigned ve) -{ - return 0; -} -#endif /* Expand the tuple (opc, type, vece) on the given arguments. */ void tcg_expand_vec_op(TCGOpcode, TCGType, unsigned, TCGArg, ...); diff --git a/tcg/tcg.c b/tcg/tcg.c index 7f5d014973..505e43c128 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -170,6 +170,10 @@ static inline void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, { g_assert_not_reached(); } +int tcg_can_emit_vec_op(TCGOpcode o, TCGType t, unsigned ve) +{ + return 0; +} #endif static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2); From 3bff1625f7a4c2be96aedc76b270a1d67e4e6dcc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:42 +0100 Subject: [PATCH 1085/2892] tcg/ppc: Remove TCGPowerISA enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Left-over from commit 623d7e3551a ("util: Add cpuinfo-ppc.c"). Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-2-philmd@linaro.org> --- tcg/ppc/tcg-target.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 8291e0127d..4fa4a30de4 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -55,14 +55,6 @@ typedef enum { TCG_AREG0 = TCG_REG_R27 } TCGReg; -typedef enum { - tcg_isa_base, - tcg_isa_2_06, - tcg_isa_2_07, - tcg_isa_3_00, - tcg_isa_3_10, -} TCGPowerISA; - #define have_isa_2_06 (cpuinfo & CPUINFO_V2_06) #define have_isa_2_07 (cpuinfo & CPUINFO_V2_07) #define have_isa_3_00 (cpuinfo & CPUINFO_V3_0) From fd4841c0433c1b433f9b3f6ddddc3e653997b26e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:43 +0100 Subject: [PATCH 1086/2892] tcg: Extract default TCG_TARGET_HAS_foo definitions to 'tcg-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-3-philmd@linaro.org> --- include/tcg/tcg.h | 105 +----------------------------------------- tcg/tcg-has.h | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 104 deletions(-) create mode 100644 tcg/tcg-has.h diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index fc379bb122..4352ec012f 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -64,110 +64,7 @@ typedef uint64_t TCGRegSet; #error unsupported #endif -#if TCG_TARGET_REG_BITS == 32 -/* Turn some undef macros into false macros. */ -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 0 -#define TCG_TARGET_HAS_rem_i64 0 -#define TCG_TARGET_HAS_div2_i64 0 -#define TCG_TARGET_HAS_rot_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 0 -#define TCG_TARGET_HAS_ext16s_i64 0 -#define TCG_TARGET_HAS_ext32s_i64 0 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 -#define TCG_TARGET_HAS_bswap16_i64 0 -#define TCG_TARGET_HAS_bswap32_i64 0 -#define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_andc_i64 0 -#define TCG_TARGET_HAS_orc_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 0 -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 -/* Turn some undef macros into true macros. */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#endif - -#ifndef TCG_TARGET_deposit_i32_valid -#define TCG_TARGET_deposit_i32_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_deposit_i64_valid -#define TCG_TARGET_deposit_i64_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_extract_i32_valid -#define TCG_TARGET_extract_i32_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_extract_i64_valid -#define TCG_TARGET_extract_i64_valid(ofs, len) 1 -#endif - -/* Only one of DIV or DIV2 should be defined. */ -#if defined(TCG_TARGET_HAS_div_i32) -#define TCG_TARGET_HAS_div2_i32 0 -#elif defined(TCG_TARGET_HAS_div2_i32) -#define TCG_TARGET_HAS_div_i32 0 -#define TCG_TARGET_HAS_rem_i32 0 -#endif -#if defined(TCG_TARGET_HAS_div_i64) -#define TCG_TARGET_HAS_div2_i64 0 -#elif defined(TCG_TARGET_HAS_div2_i64) -#define TCG_TARGET_HAS_div_i64 0 -#define TCG_TARGET_HAS_rem_i64 0 -#endif - -#if !defined(TCG_TARGET_HAS_v64) \ - && !defined(TCG_TARGET_HAS_v128) \ - && !defined(TCG_TARGET_HAS_v256) -#define TCG_TARGET_MAYBE_vec 0 -#define TCG_TARGET_HAS_abs_vec 0 -#define TCG_TARGET_HAS_neg_vec 0 -#define TCG_TARGET_HAS_not_vec 0 -#define TCG_TARGET_HAS_andc_vec 0 -#define TCG_TARGET_HAS_orc_vec 0 -#define TCG_TARGET_HAS_nand_vec 0 -#define TCG_TARGET_HAS_nor_vec 0 -#define TCG_TARGET_HAS_eqv_vec 0 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#define TCG_TARGET_HAS_shi_vec 0 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 0 -#define TCG_TARGET_HAS_mul_vec 0 -#define TCG_TARGET_HAS_sat_vec 0 -#define TCG_TARGET_HAS_minmax_vec 0 -#define TCG_TARGET_HAS_bitsel_vec 0 -#define TCG_TARGET_HAS_cmpsel_vec 0 -#define TCG_TARGET_HAS_tst_vec 0 -#else -#define TCG_TARGET_MAYBE_vec 1 -#endif -#ifndef TCG_TARGET_HAS_v64 -#define TCG_TARGET_HAS_v64 0 -#endif -#ifndef TCG_TARGET_HAS_v128 -#define TCG_TARGET_HAS_v128 0 -#endif -#ifndef TCG_TARGET_HAS_v256 -#define TCG_TARGET_HAS_v256 0 -#endif +#include "tcg/tcg-has.h" typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h new file mode 100644 index 0000000000..c09ce13389 --- /dev/null +++ b/tcg/tcg-has.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2024 Linaro, Ltd. + */ + +#ifndef TCG_HAS_H +#define TCG_HAS_H + +#if TCG_TARGET_REG_BITS == 32 +/* Turn some undef macros into false macros. */ +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_div_i64 0 +#define TCG_TARGET_HAS_rem_i64 0 +#define TCG_TARGET_HAS_div2_i64 0 +#define TCG_TARGET_HAS_rot_i64 0 +#define TCG_TARGET_HAS_ext8s_i64 0 +#define TCG_TARGET_HAS_ext16s_i64 0 +#define TCG_TARGET_HAS_ext32s_i64 0 +#define TCG_TARGET_HAS_ext8u_i64 0 +#define TCG_TARGET_HAS_ext16u_i64 0 +#define TCG_TARGET_HAS_ext32u_i64 0 +#define TCG_TARGET_HAS_bswap16_i64 0 +#define TCG_TARGET_HAS_bswap32_i64 0 +#define TCG_TARGET_HAS_bswap64_i64 0 +#define TCG_TARGET_HAS_not_i64 0 +#define TCG_TARGET_HAS_andc_i64 0 +#define TCG_TARGET_HAS_orc_i64 0 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 0 +#define TCG_TARGET_HAS_ctz_i64 0 +#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_deposit_i64 0 +#define TCG_TARGET_HAS_extract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 0 +#define TCG_TARGET_HAS_mulsh_i64 0 +/* Turn some undef macros into true macros. */ +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#endif + +#ifndef TCG_TARGET_deposit_i32_valid +#define TCG_TARGET_deposit_i32_valid(ofs, len) 1 +#endif +#ifndef TCG_TARGET_deposit_i64_valid +#define TCG_TARGET_deposit_i64_valid(ofs, len) 1 +#endif +#ifndef TCG_TARGET_extract_i32_valid +#define TCG_TARGET_extract_i32_valid(ofs, len) 1 +#endif +#ifndef TCG_TARGET_extract_i64_valid +#define TCG_TARGET_extract_i64_valid(ofs, len) 1 +#endif + +/* Only one of DIV or DIV2 should be defined. */ +#if defined(TCG_TARGET_HAS_div_i32) +#define TCG_TARGET_HAS_div2_i32 0 +#elif defined(TCG_TARGET_HAS_div2_i32) +#define TCG_TARGET_HAS_div_i32 0 +#define TCG_TARGET_HAS_rem_i32 0 +#endif +#if defined(TCG_TARGET_HAS_div_i64) +#define TCG_TARGET_HAS_div2_i64 0 +#elif defined(TCG_TARGET_HAS_div2_i64) +#define TCG_TARGET_HAS_div_i64 0 +#define TCG_TARGET_HAS_rem_i64 0 +#endif + +#if !defined(TCG_TARGET_HAS_v64) \ + && !defined(TCG_TARGET_HAS_v128) \ + && !defined(TCG_TARGET_HAS_v256) +#define TCG_TARGET_MAYBE_vec 0 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_neg_vec 0 +#define TCG_TARGET_HAS_not_vec 0 +#define TCG_TARGET_HAS_andc_vec 0 +#define TCG_TARGET_HAS_orc_vec 0 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_shi_vec 0 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 0 +#define TCG_TARGET_HAS_mul_vec 0 +#define TCG_TARGET_HAS_sat_vec 0 +#define TCG_TARGET_HAS_minmax_vec 0 +#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 +#else +#define TCG_TARGET_MAYBE_vec 1 +#endif +#ifndef TCG_TARGET_HAS_v64 +#define TCG_TARGET_HAS_v64 0 +#endif +#ifndef TCG_TARGET_HAS_v128 +#define TCG_TARGET_HAS_v128 0 +#endif +#ifndef TCG_TARGET_HAS_v256 +#define TCG_TARGET_HAS_v256 0 +#endif + +#endif From e03cf27df9169867b33680261eb4162232db2cba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:44 +0100 Subject: [PATCH 1087/2892] tcg/aarch64: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-4-philmd@linaro.org> --- tcg/aarch64/tcg-target-has.h | 119 +++++++++++++++++++++++++++++++++++ tcg/aarch64/tcg-target.h | 109 +------------------------------- 2 files changed, 120 insertions(+), 108 deletions(-) create mode 100644 tcg/aarch64/tcg-target-has.h diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h new file mode 100644 index 0000000000..0e79e01266 --- /dev/null +++ b/tcg/aarch64/tcg-target-has.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Define target-specific opcode support + * Copyright (c) 2013 Huawei Technologies Duesseldorf GmbH + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +#define have_lse (cpuinfo & CPUINFO_LSE) +#define have_lse2 (cpuinfo & CPUINFO_LSE2) + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_orc_i32 1 +#define TCG_TARGET_HAS_eqv_i32 1 +#define TCG_TARGET_HAS_nand_i32 0 +#define TCG_TARGET_HAS_nor_i32 0 +#define TCG_TARGET_HAS_clz_i32 1 +#define TCG_TARGET_HAS_ctz_i32 1 +#define TCG_TARGET_HAS_ctpop_i32 0 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 +#define TCG_TARGET_HAS_extract2_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 1 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 1 +#define TCG_TARGET_HAS_ctz_i64 1 +#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 1 +#define TCG_TARGET_HAS_extract2_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 + +/* + * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, + * which requires writable pages. We must defer to the helper for user-only, + * but in system mode all ram is writable for the host. + */ +#ifdef CONFIG_USER_ONLY +#define TCG_TARGET_HAS_qemu_ldst_i128 have_lse2 +#else +#define TCG_TARGET_HAS_qemu_ldst_i128 1 +#endif + +#define TCG_TARGET_HAS_tst 1 + +#define TCG_TARGET_HAS_v64 1 +#define TCG_TARGET_HAS_v128 1 +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec 1 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 1 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 1 +#define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 + +#endif diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index d8ca52d32d..9a682e51a4 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -13,8 +13,6 @@ #ifndef AARCH64_TCG_TARGET_H #define AARCH64_TCG_TARGET_H -#include "host/cpuinfo.h" - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -49,112 +47,7 @@ typedef enum { #define TCG_TARGET_NB_REGS 64 -#define have_lse (cpuinfo & CPUINFO_LSE) -#define have_lse2 (cpuinfo & CPUINFO_LSE2) - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 -#define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 -#define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 - -/* - * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, - * which requires writable pages. We must defer to the helper for user-only, - * but in system mode all ram is writable for the host. - */ -#ifdef CONFIG_USER_ONLY -#define TCG_TARGET_HAS_qemu_ldst_i128 have_lse2 -#else -#define TCG_TARGET_HAS_qemu_ldst_i128 1 -#endif - -#define TCG_TARGET_HAS_tst 1 - -#define TCG_TARGET_HAS_v64 1 -#define TCG_TARGET_HAS_v128 1 -#define TCG_TARGET_HAS_v256 0 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec 1 -#define TCG_TARGET_HAS_nand_vec 0 -#define TCG_TARGET_HAS_nor_vec 0 -#define TCG_TARGET_HAS_eqv_vec 0 -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec 1 -#define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 1 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 -#define TCG_TARGET_HAS_tst_vec 1 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO (0) From d3f4d0dc9cd3050f46db3e429d483cc4b3e4798d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:45 +0100 Subject: [PATCH 1088/2892] tcg/arm: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-5-philmd@linaro.org> --- tcg/arm/tcg-target-has.h | 85 ++++++++++++++++++++++++++++++++++++++++ tcg/arm/tcg-target.h | 74 +--------------------------------- 2 files changed, 86 insertions(+), 73 deletions(-) create mode 100644 tcg/arm/tcg-target-has.h diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h new file mode 100644 index 0000000000..316185500d --- /dev/null +++ b/tcg/arm/tcg-target-has.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + * Copyright (c) 2008 Andrzej Zaborowski + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +extern int arm_arch; + +#define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) + +#ifdef __ARM_ARCH_EXT_IDIV__ +#define use_idiv_instructions 1 +#else +extern bool use_idiv_instructions; +#endif +#ifdef __ARM_NEON__ +#define use_neon_instructions 1 +#else +extern bool use_neon_instructions; +#endif + +/* optional instructions */ +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 0 /* and r0, r1, #0xff */ +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_orc_i32 0 +#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 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 +#define TCG_TARGET_HAS_extract_i32 use_armv7_instructions +#define TCG_TARGET_HAS_sextract_i32 use_armv7_instructions +#define TCG_TARGET_HAS_extract2_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_muls2_i32 1 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_div_i32 use_idiv_instructions +#define TCG_TARGET_HAS_rem_i32 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + +#define TCG_TARGET_HAS_v64 use_neon_instructions +#define TCG_TARGET_HAS_v128 use_neon_instructions +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec 1 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 1 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 0 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 1 +#define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 + +#endif diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 7f6e24f861..e114f7ddf4 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -26,10 +26,6 @@ #ifndef ARM_TCG_TARGET_H #define ARM_TCG_TARGET_H -extern int arm_arch; - -#define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX @@ -74,75 +70,7 @@ typedef enum { #define TCG_TARGET_NB_REGS 32 -#ifdef __ARM_ARCH_EXT_IDIV__ -#define use_idiv_instructions 1 -#else -extern bool use_idiv_instructions; -#endif -#ifdef __ARM_NEON__ -#define use_neon_instructions 1 -#else -extern bool use_neon_instructions; -#endif - -/* optional instructions */ -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 0 /* and r0, r1, #0xff */ -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 0 -#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 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 -#define TCG_TARGET_HAS_extract_i32 use_armv7_instructions -#define TCG_TARGET_HAS_sextract_i32 use_armv7_instructions -#define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_div_i32 use_idiv_instructions -#define TCG_TARGET_HAS_rem_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_HAS_tst 1 - -#define TCG_TARGET_HAS_v64 use_neon_instructions -#define TCG_TARGET_HAS_v128 use_neon_instructions -#define TCG_TARGET_HAS_v256 0 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec 1 -#define TCG_TARGET_HAS_nand_vec 0 -#define TCG_TARGET_HAS_nor_vec 0 -#define TCG_TARGET_HAS_eqv_vec 0 -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec 1 -#define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 0 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 -#define TCG_TARGET_HAS_tst_vec 1 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO (0) From 8c033f243141e9b50a93308f871f7f0b53fdc379 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:46 +0100 Subject: [PATCH 1089/2892] tcg/i386: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-6-philmd@linaro.org> --- tcg/i386/tcg-target-has.h | 139 ++++++++++++++++++++++++++++++++++++++ tcg/i386/tcg-target.h | 129 +---------------------------------- 2 files changed, 140 insertions(+), 128 deletions(-) create mode 100644 tcg/i386/tcg-target-has.h diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h new file mode 100644 index 0000000000..3ea2eab807 --- /dev/null +++ b/tcg/i386/tcg-target-has.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +#define have_bmi1 (cpuinfo & CPUINFO_BMI1) +#define have_popcnt (cpuinfo & CPUINFO_POPCNT) +#define have_avx1 (cpuinfo & CPUINFO_AVX1) +#define have_avx2 (cpuinfo & CPUINFO_AVX2) +#define have_movbe (cpuinfo & CPUINFO_MOVBE) + +/* + * There are interesting instructions in AVX512, so long as we have AVX512VL, + * which indicates support for EVEX on sizes smaller than 512 bits. + */ +#define have_avx512vl ((cpuinfo & CPUINFO_AVX512VL) && \ + (cpuinfo & CPUINFO_AVX512F)) +#define have_avx512bw ((cpuinfo & CPUINFO_AVX512BW) && have_avx512vl) +#define have_avx512dq ((cpuinfo & CPUINFO_AVX512DQ) && have_avx512vl) +#define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) + +/* optional instructions */ +#define TCG_TARGET_HAS_div2_i32 1 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_andc_i32 have_bmi1 +#define TCG_TARGET_HAS_orc_i32 0 +#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 1 +#define TCG_TARGET_HAS_ctz_i32 1 +#define TCG_TARGET_HAS_ctpop_i32 have_popcnt +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 +#define TCG_TARGET_HAS_extract2_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_muls2_i32 1 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 + +#if TCG_TARGET_REG_BITS == 64 +/* Keep 32-bit values zero-extended in a register. */ +#define TCG_TARGET_HAS_extr_i64_i32 1 +#define TCG_TARGET_HAS_div2_i64 1 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_andc_i64 have_bmi1 +#define TCG_TARGET_HAS_orc_i64 0 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 1 +#define TCG_TARGET_HAS_ctz_i64 1 +#define TCG_TARGET_HAS_ctpop_i64 have_popcnt +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 1 +#define TCG_TARGET_HAS_muls2_i64 1 +#define TCG_TARGET_HAS_muluh_i64 0 +#define TCG_TARGET_HAS_mulsh_i64 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 +#else +#define TCG_TARGET_HAS_qemu_st8_i32 1 +#endif + +#define TCG_TARGET_HAS_qemu_ldst_i128 \ + (TCG_TARGET_REG_BITS == 64 && (cpuinfo & CPUINFO_ATOMIC_VMOVDQA)) + +#define TCG_TARGET_HAS_tst 1 + +/* We do not support older SSE systems, only beginning with AVX1. */ +#define TCG_TARGET_HAS_v64 have_avx1 +#define TCG_TARGET_HAS_v128 have_avx1 +#define TCG_TARGET_HAS_v256 have_avx2 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec have_avx512vl +#define TCG_TARGET_HAS_nand_vec have_avx512vl +#define TCG_TARGET_HAS_nor_vec have_avx512vl +#define TCG_TARGET_HAS_eqv_vec have_avx512vl +#define TCG_TARGET_HAS_not_vec have_avx512vl +#define TCG_TARGET_HAS_neg_vec 0 +#define TCG_TARGET_HAS_abs_vec 1 +#define TCG_TARGET_HAS_roti_vec have_avx512vl +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec have_avx512vl +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec have_avx2 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec have_avx512vl +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec have_avx512bw + +#define TCG_TARGET_deposit_i32_valid(ofs, len) \ + (((ofs) == 0 && ((len) == 8 || (len) == 16)) || \ + (TCG_TARGET_REG_BITS == 32 && (ofs) == 8 && (len) == 8)) +#define TCG_TARGET_deposit_i64_valid TCG_TARGET_deposit_i32_valid + +/* Check for the possibility of high-byte extraction and, for 64-bit, + zero-extending 32-bit right-shift. */ +#define TCG_TARGET_extract_i32_valid(ofs, len) ((ofs) == 8 && (len) == 8) +#define TCG_TARGET_extract_i64_valid(ofs, len) \ + (((ofs) == 8 && (len) == 8) || ((ofs) + (len)) == 32) + +#endif diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 9961d8e757..a1dfdeb28d 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -25,8 +25,6 @@ #ifndef I386_TCG_TARGET_H #define I386_TCG_TARGET_H -#include "host/cpuinfo.h" - #define TCG_TARGET_INSN_UNIT_SIZE 1 #ifdef __x86_64__ @@ -90,132 +88,7 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_ESP } TCGReg; -#define have_bmi1 (cpuinfo & CPUINFO_BMI1) -#define have_popcnt (cpuinfo & CPUINFO_POPCNT) -#define have_avx1 (cpuinfo & CPUINFO_AVX1) -#define have_avx2 (cpuinfo & CPUINFO_AVX2) -#define have_movbe (cpuinfo & CPUINFO_MOVBE) - -/* - * There are interesting instructions in AVX512, so long as we have AVX512VL, - * which indicates support for EVEX on sizes smaller than 512 bits. - */ -#define have_avx512vl ((cpuinfo & CPUINFO_AVX512VL) && \ - (cpuinfo & CPUINFO_AVX512F)) -#define have_avx512bw ((cpuinfo & CPUINFO_AVX512BW) && have_avx512vl) -#define have_avx512dq ((cpuinfo & CPUINFO_AVX512DQ) && have_avx512vl) -#define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) - -/* optional instructions */ -#define TCG_TARGET_HAS_div2_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 have_bmi1 -#define TCG_TARGET_HAS_orc_i32 0 -#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 1 -#define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 have_popcnt -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 -#define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -/* Keep 32-bit values zero-extended in a register. */ -#define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_div2_i64 1 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 have_bmi1 -#define TCG_TARGET_HAS_orc_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 have_popcnt -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muls2_i64 1 -#define TCG_TARGET_HAS_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 -#else -#define TCG_TARGET_HAS_qemu_st8_i32 1 -#endif - -#define TCG_TARGET_HAS_qemu_ldst_i128 \ - (TCG_TARGET_REG_BITS == 64 && (cpuinfo & CPUINFO_ATOMIC_VMOVDQA)) - -#define TCG_TARGET_HAS_tst 1 - -/* We do not support older SSE systems, only beginning with AVX1. */ -#define TCG_TARGET_HAS_v64 have_avx1 -#define TCG_TARGET_HAS_v128 have_avx1 -#define TCG_TARGET_HAS_v256 have_avx2 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec have_avx512vl -#define TCG_TARGET_HAS_nand_vec have_avx512vl -#define TCG_TARGET_HAS_nor_vec have_avx512vl -#define TCG_TARGET_HAS_eqv_vec have_avx512vl -#define TCG_TARGET_HAS_not_vec have_avx512vl -#define TCG_TARGET_HAS_neg_vec 0 -#define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec have_avx512vl -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec have_avx512vl -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 1 -#define TCG_TARGET_HAS_shv_vec have_avx2 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec have_avx512vl -#define TCG_TARGET_HAS_cmpsel_vec 1 -#define TCG_TARGET_HAS_tst_vec have_avx512bw - -#define TCG_TARGET_deposit_i32_valid(ofs, len) \ - (((ofs) == 0 && ((len) == 8 || (len) == 16)) || \ - (TCG_TARGET_REG_BITS == 32 && (ofs) == 8 && (len) == 8)) -#define TCG_TARGET_deposit_i64_valid TCG_TARGET_deposit_i32_valid - -/* Check for the possibility of high-byte extraction and, for 64-bit, - zero-extending 32-bit right-shift. */ -#define TCG_TARGET_extract_i32_valid(ofs, len) ((ofs) == 8 && (len) == 8) -#define TCG_TARGET_extract_i64_valid(ofs, len) \ - (((ofs) == 8 && (len) == 8) || ((ofs) + (len)) == 32) +#include "tcg-target-has.h" /* This defines the natural memory order supported by this * architecture before guarantees made by various barrier From 0a16d036154ecafe68db336309caed3add00fd70 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:47 +0100 Subject: [PATCH 1090/2892] tcg/loongarch64: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-7-philmd@linaro.org> --- tcg/loongarch64/tcg-target-has.h | 113 +++++++++++++++++++++++++++++++ tcg/loongarch64/tcg-target.h | 102 +--------------------------- 2 files changed, 114 insertions(+), 101 deletions(-) create mode 100644 tcg/loongarch64/tcg-target-has.h diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h new file mode 100644 index 0000000000..e4333c36c6 --- /dev/null +++ b/tcg/loongarch64/tcg-target-has.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2021 WANG Xuerui + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +/* optional instructions */ +#define TCG_TARGET_HAS_negsetcond_i32 0 +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_div2_i32 0 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 1 +#define TCG_TARGET_HAS_mulsh_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_orc_i32 1 +#define TCG_TARGET_HAS_eqv_i32 0 +#define TCG_TARGET_HAS_nand_i32 0 +#define TCG_TARGET_HAS_nor_i32 1 +#define TCG_TARGET_HAS_clz_i32 1 +#define TCG_TARGET_HAS_ctz_i32 1 +#define TCG_TARGET_HAS_ctpop_i32 0 +#define TCG_TARGET_HAS_brcond2 0 +#define TCG_TARGET_HAS_setcond2 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +/* 64-bit operations */ +#define TCG_TARGET_HAS_negsetcond_i64 0 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 1 +#define TCG_TARGET_HAS_div2_i64 0 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_extr_i64_i32 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 1 +#define TCG_TARGET_HAS_clz_i64 1 +#define TCG_TARGET_HAS_ctz_i64 1 +#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 + +#define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) + +#define TCG_TARGET_HAS_tst 0 + +#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_LSX) +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) +#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_LASX) + +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec 1 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 1 +#define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 + + +#endif diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 3bc9aafaf2..a3a6130720 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -29,8 +29,6 @@ #ifndef LOONGARCH_TCG_TARGET_H #define LOONGARCH_TCG_TARGET_H -#include "host/cpuinfo.h" - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 64 @@ -87,105 +85,7 @@ typedef enum { TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; -/* optional instructions */ -#define TCG_TARGET_HAS_negsetcond_i32 0 -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_div2_i32 0 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#define TCG_TARGET_HAS_eqv_i32 0 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_brcond2 0 -#define TCG_TARGET_HAS_setcond2 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -/* 64-bit operations */ -#define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_div2_i64 0 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 - -#define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) - -#define TCG_TARGET_HAS_tst 0 - -#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_LSX) -#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) -#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_LASX) - -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec 1 -#define TCG_TARGET_HAS_abs_vec 0 -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec 1 -#define TCG_TARGET_HAS_nand_vec 0 -#define TCG_TARGET_HAS_nor_vec 1 -#define TCG_TARGET_HAS_eqv_vec 0 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 1 -#define TCG_TARGET_HAS_roti_vec 1 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 -#define TCG_TARGET_HAS_tst_vec 0 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO (0) From f975a3134d67d53e6409392e07d8cc73aebfc512 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:48 +0100 Subject: [PATCH 1091/2892] tcg/mips: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-8-philmd@linaro.org> --- tcg/mips/tcg-target-has.h | 122 ++++++++++++++++++++++++++++++++++++++ tcg/mips/tcg-target.h | 112 +--------------------------------- 2 files changed, 123 insertions(+), 111 deletions(-) create mode 100644 tcg/mips/tcg-target-has.h diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h new file mode 100644 index 0000000000..5dbc63cef6 --- /dev/null +++ b/tcg/mips/tcg-target-has.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008-2009 Arnaud Patard + * Copyright (c) 2009 Aurelien Jarno + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +/* MOVN/MOVZ instructions detection */ +#if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ + defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ + defined(_MIPS_ARCH_MIPS4) +#define use_movnz_instructions 1 +#else +extern bool use_movnz_instructions; +#endif + +/* MIPS32 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 1) +#define use_mips32_instructions 1 +#else +extern bool use_mips32_instructions; +#endif + +/* MIPS32R2 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) +#define use_mips32r2_instructions 1 +#else +extern bool use_mips32r2_instructions; +#endif + +/* MIPS32R6 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 6) +#define use_mips32r6_instructions 1 +#else +#define use_mips32r6_instructions 0 +#endif + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_nor_i32 1 +#define TCG_TARGET_HAS_andc_i32 0 +#define TCG_TARGET_HAS_orc_i32 0 +#define TCG_TARGET_HAS_eqv_i32 0 +#define TCG_TARGET_HAS_nand_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) +#define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) +#define TCG_TARGET_HAS_muluh_i32 1 +#define TCG_TARGET_HAS_mulsh_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 0 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 1 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_nor_i64 1 +#define TCG_TARGET_HAS_andc_i64 0 +#define TCG_TARGET_HAS_orc_i64 0 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) +#define TCG_TARGET_HAS_muls2_i64 (!use_mips32r6_instructions) +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 0 +#endif + +/* optional instructions detected at runtime */ +#define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_clz_i32 use_mips32r2_instructions +#define TCG_TARGET_HAS_ctz_i32 0 +#define TCG_TARGET_HAS_ctpop_i32 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_bswap16_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_bswap32_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_bswap64_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_extract_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_rot_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_clz_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_ctz_i64 0 +#define TCG_TARGET_HAS_ctpop_i64 0 +#endif + +/* optional instructions automatically implemented */ +#define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */ +#define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */ + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_ext8u_i64 0 /* andi rt, rs, 0xff */ +#define TCG_TARGET_HAS_ext16u_i64 0 /* andi rt, rs, 0xffff */ +#endif + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 +#define TCG_TARGET_HAS_tst 0 + +#endif diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index db60eb7c1b..a34765b389 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -70,117 +70,7 @@ typedef enum { TCG_AREG0 = TCG_REG_S8, } TCGReg; -/* MOVN/MOVZ instructions detection */ -#if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ - defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ - defined(_MIPS_ARCH_MIPS4) -#define use_movnz_instructions 1 -#else -extern bool use_movnz_instructions; -#endif - -/* MIPS32 instruction set detection */ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 1) -#define use_mips32_instructions 1 -#else -extern bool use_mips32_instructions; -#endif - -/* MIPS32R2 instruction set detection */ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) -#define use_mips32r2_instructions 1 -#else -extern bool use_mips32r2_instructions; -#endif - -/* MIPS32R6 instruction set detection */ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 6) -#define use_mips32r6_instructions 1 -#else -#define use_mips32r6_instructions 0 -#endif - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_andc_i32 0 -#define TCG_TARGET_HAS_orc_i32 0 -#define TCG_TARGET_HAS_eqv_i32 0 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muluh_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_andc_i64 0 -#define TCG_TARGET_HAS_orc_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muls2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 0 -#endif - -/* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_clz_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_bswap16_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_bswap32_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_bswap64_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_rot_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_clz_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#endif - -/* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */ -#define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */ - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_ext8u_i64 0 /* andi rt, rs, 0xff */ -#define TCG_TARGET_HAS_ext16u_i64 0 /* andi rt, rs, 0xffff */ -#endif - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_HAS_tst 0 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO 0 From 5f593d5b16fcbadbca8929d891a2d65144156f91 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:49 +0100 Subject: [PATCH 1092/2892] tcg/ppc: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-9-philmd@linaro.org> --- tcg/ppc/tcg-target-has.h | 124 +++++++++++++++++++++++++++++++++++++++ tcg/ppc/tcg-target.h | 114 +---------------------------------- 2 files changed, 125 insertions(+), 113 deletions(-) create mode 100644 tcg/ppc/tcg-target-has.h diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h new file mode 100644 index 0000000000..a6c7cdba5d --- /dev/null +++ b/tcg/ppc/tcg-target-has.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +#define have_isa_2_06 (cpuinfo & CPUINFO_V2_06) +#define have_isa_2_07 (cpuinfo & CPUINFO_V2_07) +#define have_isa_3_00 (cpuinfo & CPUINFO_V3_0) +#define have_isa_3_10 (cpuinfo & CPUINFO_V3_1) +#define have_altivec (cpuinfo & CPUINFO_ALTIVEC) +#define have_vsx (cpuinfo & CPUINFO_VSX) + +/* optional instructions automatically implemented */ +#define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ +#define TCG_TARGET_HAS_ext16u_i32 0 + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 have_isa_3_00 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_orc_i32 1 +#define TCG_TARGET_HAS_eqv_i32 1 +#define TCG_TARGET_HAS_nand_i32 1 +#define TCG_TARGET_HAS_nor_i32 1 +#define TCG_TARGET_HAS_clz_i32 1 +#define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 +#define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 1 +#define TCG_TARGET_HAS_mulsh_i32 1 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 have_isa_3_00 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 0 +#define TCG_TARGET_HAS_ext16u_i64 0 +#define TCG_TARGET_HAS_ext32u_i64 0 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 1 +#define TCG_TARGET_HAS_nand_i64 1 +#define TCG_TARGET_HAS_nor_i64 1 +#define TCG_TARGET_HAS_clz_i64 1 +#define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 +#define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 +#endif + +#define TCG_TARGET_HAS_qemu_ldst_i128 \ + (TCG_TARGET_REG_BITS == 64 && have_isa_2_07) + +#define TCG_TARGET_HAS_tst 1 + +/* + * While technically Altivec could support V64, it has no 64-bit store + * instruction and substituting two 32-bit stores makes the generated + * code quite large. + */ +#define TCG_TARGET_HAS_v64 have_vsx +#define TCG_TARGET_HAS_v128 have_altivec +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec have_isa_2_07 +#define TCG_TARGET_HAS_nand_vec have_isa_2_07 +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec have_isa_2_07 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec have_isa_3_00 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 0 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec have_vsx +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec 0 + +#endif diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 4fa4a30de4..fa2cc28183 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -25,8 +25,6 @@ #ifndef PPC_TCG_TARGET_H #define PPC_TCG_TARGET_H -#include "host/cpuinfo.h" - #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) #define TCG_TARGET_NB_REGS 64 @@ -55,117 +53,7 @@ typedef enum { TCG_AREG0 = TCG_REG_R27 } TCGReg; -#define have_isa_2_06 (cpuinfo & CPUINFO_V2_06) -#define have_isa_2_07 (cpuinfo & CPUINFO_V2_07) -#define have_isa_3_00 (cpuinfo & CPUINFO_V3_0) -#define have_isa_3_10 (cpuinfo & CPUINFO_V3_1) -#define have_altivec (cpuinfo & CPUINFO_ALTIVEC) -#define have_vsx (cpuinfo & CPUINFO_VSX) - -/* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ -#define TCG_TARGET_HAS_ext16u_i32 0 - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 have_isa_3_00 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 -#define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 1 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 have_isa_3_00 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 -#define TCG_TARGET_HAS_nand_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 -#define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 -#endif - -#define TCG_TARGET_HAS_qemu_ldst_i128 \ - (TCG_TARGET_REG_BITS == 64 && have_isa_2_07) - -#define TCG_TARGET_HAS_tst 1 - -/* - * While technically Altivec could support V64, it has no 64-bit store - * instruction and substituting two 32-bit stores makes the generated - * code quite large. - */ -#define TCG_TARGET_HAS_v64 have_vsx -#define TCG_TARGET_HAS_v128 have_altivec -#define TCG_TARGET_HAS_v256 0 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec have_isa_2_07 -#define TCG_TARGET_HAS_nand_vec have_isa_2_07 -#define TCG_TARGET_HAS_nor_vec 1 -#define TCG_TARGET_HAS_eqv_vec have_isa_2_07 -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec have_isa_3_00 -#define TCG_TARGET_HAS_abs_vec 0 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 1 -#define TCG_TARGET_HAS_shi_vec 0 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 1 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec have_vsx -#define TCG_TARGET_HAS_cmpsel_vec 1 -#define TCG_TARGET_HAS_tst_vec 0 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO (0) From 0242532b45df877ac986b57348a536c1b76088c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:50 +0100 Subject: [PATCH 1093/2892] tcg/riscv: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-10-philmd@linaro.org> --- tcg/riscv/tcg-target-has.h | 112 +++++++++++++++++++++++++++++++++++++ tcg/riscv/tcg-target.h | 102 +-------------------------------- 2 files changed, 113 insertions(+), 101 deletions(-) create mode 100644 tcg/riscv/tcg-target-has.h diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h new file mode 100644 index 0000000000..ddc759a533 --- /dev/null +++ b/tcg/riscv/tcg-target-has.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2018 SiFive, Inc + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +/* optional instructions */ +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_div2_i32 0 +#define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_deposit_i32 0 +#define TCG_TARGET_HAS_extract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_andc_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_orc_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_nand_i32 0 +#define TCG_TARGET_HAS_nor_i32 0 +#define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_brcond2 1 +#define TCG_TARGET_HAS_setcond2 1 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#define TCG_TARGET_HAS_negsetcond_i64 1 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 1 +#define TCG_TARGET_HAS_div2_i64 0 +#define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_deposit_i64 0 +#define TCG_TARGET_HAS_extract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_extr_i64_i32 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_andc_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_orc_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 0 + +/* vector instructions */ +#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_andc_vec 0 +#define TCG_TARGET_HAS_orc_vec 0 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 1 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_cmpsel_vec 1 + +#define TCG_TARGET_HAS_tst_vec 0 + +#endif diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index bfaa99ccdd..c710321bdb 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -25,8 +25,6 @@ #ifndef RISCV_TCG_TARGET_H #define RISCV_TCG_TARGET_H -#include "host/cpuinfo.h" - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 64 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -59,105 +57,7 @@ typedef enum { TCG_REG_TMP2 = TCG_REG_T4, } TCGReg; -/* optional instructions */ -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_div2_i32 0 -#define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_extract_i32 0 -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_orc_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_brcond2 1 -#define TCG_TARGET_HAS_setcond2 1 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_div2_i64 0 -#define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_orc_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_HAS_tst 0 - -/* vector instructions */ -#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_ZVE64X) -#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_ZVE64X) -#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_ZVE64X) -#define TCG_TARGET_HAS_andc_vec 0 -#define TCG_TARGET_HAS_orc_vec 0 -#define TCG_TARGET_HAS_nand_vec 0 -#define TCG_TARGET_HAS_nor_vec 0 -#define TCG_TARGET_HAS_eqv_vec 0 -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec 1 -#define TCG_TARGET_HAS_abs_vec 0 -#define TCG_TARGET_HAS_roti_vec 1 -#define TCG_TARGET_HAS_rots_vec 1 -#define TCG_TARGET_HAS_rotv_vec 1 -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 1 -#define TCG_TARGET_HAS_shv_vec 1 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 0 -#define TCG_TARGET_HAS_cmpsel_vec 1 - -#define TCG_TARGET_HAS_tst_vec 0 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO (0) From 44c9e94bcccb6e4d7c3fb9a184830577c923907a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:51 +0100 Subject: [PATCH 1094/2892] tcg/s390x: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-11-philmd@linaro.org> --- tcg/s390x/tcg-target-has.h | 124 +++++++++++++++++++++++++++++++++++++ tcg/s390x/tcg-target.h | 114 +--------------------------------- 2 files changed, 125 insertions(+), 113 deletions(-) create mode 100644 tcg/s390x/tcg-target-has.h diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h new file mode 100644 index 0000000000..4992d74f12 --- /dev/null +++ b/tcg/s390x/tcg-target-has.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2009 Ulrich Hecht + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +/* Facilities required for proper operation; checked at startup. */ + +#define FACILITY_ZARCH_ACTIVE 2 +#define FACILITY_LONG_DISP 18 +#define FACILITY_EXT_IMM 21 +#define FACILITY_GEN_INST_EXT 34 +#define FACILITY_45 45 + +/* Facilities that are checked at runtime. */ + +#define FACILITY_LOAD_ON_COND2 53 +#define FACILITY_MISC_INSN_EXT2 58 +#define FACILITY_MISC_INSN_EXT3 61 +#define FACILITY_VECTOR 129 +#define FACILITY_VECTOR_ENH1 135 + +extern uint64_t s390_facilities[3]; + +#define HAVE_FACILITY(X) \ + ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) + +/* optional instructions */ +#define TCG_TARGET_HAS_div2_i32 1 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_andc_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_orc_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_clz_i32 0 +#define TCG_TARGET_HAS_ctz_i32 0 +#define TCG_TARGET_HAS_ctpop_i32 1 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#define TCG_TARGET_HAS_div2_i64 1 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_andc_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_orc_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_clz_i64 1 +#define TCG_TARGET_HAS_ctz_i64 0 +#define TCG_TARGET_HAS_ctpop_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 1 +#define TCG_TARGET_HAS_muls2_i64 HAVE_FACILITY(MISC_INSN_EXT2) +#define TCG_TARGET_HAS_muluh_i64 0 +#define TCG_TARGET_HAS_mulsh_i64 0 + +#define TCG_TARGET_HAS_qemu_ldst_i128 1 + +#define TCG_TARGET_HAS_tst 1 + +#define TCG_TARGET_HAS_v64 HAVE_FACILITY(VECTOR) +#define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_nand_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 1 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 1 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 0 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 1 +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec 0 + +#endif diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 223d3f6ca1..220ed68b1f 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -51,119 +51,7 @@ typedef enum TCGReg { #define TCG_TARGET_NB_REGS 64 -/* Facilities required for proper operation; checked at startup. */ - -#define FACILITY_ZARCH_ACTIVE 2 -#define FACILITY_LONG_DISP 18 -#define FACILITY_EXT_IMM 21 -#define FACILITY_GEN_INST_EXT 34 -#define FACILITY_45 45 - -/* Facilities that are checked at runtime. */ - -#define FACILITY_LOAD_ON_COND2 53 -#define FACILITY_MISC_INSN_EXT2 58 -#define FACILITY_MISC_INSN_EXT3 61 -#define FACILITY_VECTOR 129 -#define FACILITY_VECTOR_ENH1 135 - -extern uint64_t s390_facilities[3]; - -#define HAVE_FACILITY(X) \ - ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) - -/* optional instructions */ -#define TCG_TARGET_HAS_div2_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_andc_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_orc_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_clz_i32 0 -#define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_div2_i64 1 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_andc_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_orc_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muls2_i64 HAVE_FACILITY(MISC_INSN_EXT2) -#define TCG_TARGET_HAS_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 - -#define TCG_TARGET_HAS_qemu_ldst_i128 1 - -#define TCG_TARGET_HAS_tst 1 - -#define TCG_TARGET_HAS_v64 HAVE_FACILITY(VECTOR) -#define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) -#define TCG_TARGET_HAS_v256 0 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec HAVE_FACILITY(VECTOR_ENH1) -#define TCG_TARGET_HAS_nand_vec HAVE_FACILITY(VECTOR_ENH1) -#define TCG_TARGET_HAS_nor_vec 1 -#define TCG_TARGET_HAS_eqv_vec HAVE_FACILITY(VECTOR_ENH1) -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec 1 -#define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec 1 -#define TCG_TARGET_HAS_rots_vec 1 -#define TCG_TARGET_HAS_rotv_vec 1 -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 1 -#define TCG_TARGET_HAS_shv_vec 1 -#define TCG_TARGET_HAS_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 0 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 1 -#define TCG_TARGET_HAS_tst_vec 0 +#include "tcg-target-has.h" #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) From 66ba44cfbf60cb009435777a226f7c6cf248d55f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:52 +0100 Subject: [PATCH 1095/2892] tcg/sparc64: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-12-philmd@linaro.org> --- tcg/sparc64/tcg-target-has.h | 86 ++++++++++++++++++++++++++++++++++++ tcg/sparc64/tcg-target.h | 78 +------------------------------- 2 files changed, 88 insertions(+), 76 deletions(-) create mode 100644 tcg/sparc64/tcg-target-has.h diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h new file mode 100644 index 0000000000..d9ca14cc3d --- /dev/null +++ b/tcg/sparc64/tcg-target-has.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#if defined(__VIS__) && __VIS__ >= 0x300 +#define use_vis3_instructions 1 +#else +extern bool use_vis3_instructions; +#endif + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 0 +#define TCG_TARGET_HAS_rot_i32 0 +#define TCG_TARGET_HAS_ext8s_i32 0 +#define TCG_TARGET_HAS_ext16s_i32 0 +#define TCG_TARGET_HAS_ext8u_i32 0 +#define TCG_TARGET_HAS_ext16u_i32 0 +#define TCG_TARGET_HAS_bswap16_i32 0 +#define TCG_TARGET_HAS_bswap32_i32 0 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_orc_i32 1 +#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 0 +#define TCG_TARGET_HAS_ctz_i32 0 +#define TCG_TARGET_HAS_ctpop_i32 0 +#define TCG_TARGET_HAS_deposit_i32 0 +#define TCG_TARGET_HAS_extract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_muls2_i32 1 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 0 +#define TCG_TARGET_HAS_rot_i64 0 +#define TCG_TARGET_HAS_ext8s_i64 0 +#define TCG_TARGET_HAS_ext16s_i64 0 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 0 +#define TCG_TARGET_HAS_ext16u_i64 0 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 0 +#define TCG_TARGET_HAS_bswap32_i64 0 +#define TCG_TARGET_HAS_bswap64_i64 0 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 0 +#define TCG_TARGET_HAS_ctz_i64 0 +#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_deposit_i64 0 +#define TCG_TARGET_HAS_extract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 use_vis3_instructions +#define TCG_TARGET_HAS_mulsh_i64 0 + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + +#endif diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index b560d43ed5..1462144631 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -64,84 +64,10 @@ typedef enum { TCG_REG_I7, } TCGReg; -#if defined(__VIS__) && __VIS__ >= 0x300 -#define use_vis3_instructions 1 -#else -extern bool use_vis3_instructions; -#endif - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 0 -#define TCG_TARGET_HAS_rot_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 0 -#define TCG_TARGET_HAS_ext16s_i32 0 -#define TCG_TARGET_HAS_ext8u_i32 0 -#define TCG_TARGET_HAS_ext16u_i32 0 -#define TCG_TARGET_HAS_bswap16_i32 0 -#define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#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 0 -#define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_extract_i32 0 -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 0 -#define TCG_TARGET_HAS_rot_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 0 -#define TCG_TARGET_HAS_ext16s_i64 0 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 0 -#define TCG_TARGET_HAS_bswap32_i64 0 -#define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 0 -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 use_vis3_instructions -#define TCG_TARGET_HAS_mulsh_i64 0 - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_HAS_tst 1 - #define TCG_AREG0 TCG_REG_I0 +#include "tcg-target-has.h" + #define TCG_TARGET_DEFAULT_MO (0) #endif From 8ae72b38b504ac450bf12025f5242cc5a4cb39b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:53 +0100 Subject: [PATCH 1096/2892] tcg/tci: Extract TCG_TARGET_HAS_foo defs to 'tcg-target-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-13-philmd@linaro.org> --- tcg/tci/tcg-target-has.h | 83 ++++++++++++++++++++++++++++++++++++++++ tcg/tci/tcg-target.h | 75 +----------------------------------- 2 files changed, 84 insertions(+), 74 deletions(-) create mode 100644 tcg/tci/tcg-target-has.h diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h new file mode 100644 index 0000000000..3397403910 --- /dev/null +++ b/tcg/tci/tcg-target-has.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2009, 2011 Stefan Weil + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_eqv_i32 1 +#define TCG_TARGET_HAS_nand_i32 1 +#define TCG_TARGET_HAS_nor_i32 1 +#define TCG_TARGET_HAS_clz_i32 1 +#define TCG_TARGET_HAS_ctz_i32 1 +#define TCG_TARGET_HAS_ctpop_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_orc_i32 1 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 0 +#define TCG_TARGET_HAS_muls2_i32 1 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_qemu_st8_i32 0 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 1 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 1 +#define TCG_TARGET_HAS_nand_i64 1 +#define TCG_TARGET_HAS_nor_i64 1 +#define TCG_TARGET_HAS_clz_i64 1 +#define TCG_TARGET_HAS_ctz_i64 1 +#define TCG_TARGET_HAS_ctpop_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 0 +#define TCG_TARGET_HAS_muls2_i64 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 1 +#define TCG_TARGET_HAS_muluh_i64 0 +#define TCG_TARGET_HAS_mulsh_i64 0 +#else +#define TCG_TARGET_HAS_mulu2_i32 1 +#endif /* TCG_TARGET_REG_BITS == 64 */ + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + +#endif diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index d7650343a3..899d9861a6 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -44,80 +44,7 @@ #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) -/* Optional instructions. */ - -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 0 -#define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 -#define TCG_TARGET_HAS_nand_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_muls2_i64 1 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 -#else -#define TCG_TARGET_HAS_mulu2_i32 1 -#endif /* TCG_TARGET_REG_BITS == 64 */ - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_HAS_tst 1 +#include "tcg-target-has.h" /* Number of registers available. */ #define TCG_TARGET_NB_REGS 16 From f15d00a4c180ffd303dc9272f34a26a185f741f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:54 +0100 Subject: [PATCH 1097/2892] tcg: Include 'tcg-target-has.h' once in 'tcg-has.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-14-philmd@linaro.org> --- tcg/aarch64/tcg-target.h | 2 -- tcg/arm/tcg-target.h | 2 -- tcg/i386/tcg-target.h | 2 -- tcg/loongarch64/tcg-target.h | 2 -- tcg/mips/tcg-target.h | 2 -- tcg/ppc/tcg-target.h | 2 -- tcg/riscv/tcg-target.h | 2 -- tcg/s390x/tcg-target.h | 2 -- tcg/sparc64/tcg-target.h | 2 -- tcg/tcg-has.h | 2 ++ tcg/tci/tcg-target.h | 2 -- 11 files changed, 2 insertions(+), 20 deletions(-) diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 9a682e51a4..1ef8b2e300 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -47,8 +47,6 @@ typedef enum { #define TCG_TARGET_NB_REGS 64 -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (0) #endif /* AARCH64_TCG_TARGET_H */ diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index e114f7ddf4..21563e00f9 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -70,8 +70,6 @@ typedef enum { #define TCG_TARGET_NB_REGS 32 -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (0) #endif diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index a1dfdeb28d..e6d7fd526e 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -88,8 +88,6 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_ESP } TCGReg; -#include "tcg-target-has.h" - /* This defines the natural memory order supported by this * architecture before guarantees made by various barrier * instructions. diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index a3a6130720..0432a4ebbd 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -85,8 +85,6 @@ typedef enum { TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (0) #endif /* LOONGARCH_TCG_TARGET_H */ diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index a34765b389..210044ca12 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -70,8 +70,6 @@ typedef enum { TCG_AREG0 = TCG_REG_S8, } TCGReg; -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO 0 #endif diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index fa2cc28183..0bc13d7363 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -53,8 +53,6 @@ typedef enum { TCG_AREG0 = TCG_REG_R27 } TCGReg; -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (0) #endif diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index c710321bdb..4c40662402 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -57,8 +57,6 @@ typedef enum { TCG_REG_TMP2 = TCG_REG_T4, } TCGReg; -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (0) #endif diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 220ed68b1f..f790b77075 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -51,8 +51,6 @@ typedef enum TCGReg { #define TCG_TARGET_NB_REGS 64 -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) #endif diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 1462144631..5ecca5586b 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -66,8 +66,6 @@ typedef enum { #define TCG_AREG0 TCG_REG_I0 -#include "tcg-target-has.h" - #define TCG_TARGET_DEFAULT_MO (0) #endif diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index c09ce13389..65b6a0b0cf 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -7,6 +7,8 @@ #ifndef TCG_HAS_H #define TCG_HAS_H +#include "tcg-target-has.h" + #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 899d9861a6..fea92f7848 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -44,8 +44,6 @@ #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) -#include "tcg-target-has.h" - /* Number of registers available. */ #define TCG_TARGET_NB_REGS 16 From 93280b67381148d6b8b25f54f32901f868987c84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 22:51:55 +0100 Subject: [PATCH 1098/2892] tcg: Only include 'tcg-has.h' when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCG_TARGET_HAS_* definitions don't need to be exposed by "tcg/tcg.h". Only include 'tcg-has.h' when necessary. Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250108215156.8731-15-philmd@linaro.org> --- include/tcg/tcg.h | 2 -- tcg/optimize.c | 1 + tcg/tcg-common.c | 1 + tcg/tcg-op-gvec.c | 1 + tcg/tcg-op-ldst.c | 2 +- tcg/tcg-op-vec.c | 1 + tcg/tcg-op.c | 2 +- tcg/tcg.c | 1 + tcg/tci.c | 1 + 9 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 4352ec012f..e5fa69d20b 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -64,8 +64,6 @@ typedef uint64_t TCGRegSet; #error unsupported #endif -#include "tcg/tcg-has.h" - typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg/tcg-opc.h" diff --git a/tcg/optimize.c b/tcg/optimize.c index 6823569ee2..c363c5c04b 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -28,6 +28,7 @@ #include "qemu/interval-tree.h" #include "tcg/tcg-op-common.h" #include "tcg-internal.h" +#include "tcg-has.h" #define CASE_OP_32_64(x) \ glue(glue(case INDEX_op_, x), _i32): \ diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index 35e7616ae9..fadc33c3d1 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" +#include "tcg-has.h" TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 97e4df221a..d32a4f146d 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -23,6 +23,7 @@ #include "tcg/tcg-op-common.h" #include "tcg/tcg-op-gvec-common.h" #include "tcg/tcg-gvec-desc.h" +#include "tcg-has.h" #define MAX_UNROLL 4 diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 0d8fe3b4f5..ec3ef4dcb4 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -30,7 +30,7 @@ #include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" - +#include "tcg-has.h" static void check_max_alignment(unsigned a_bits) { diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 364cd089df..893d68e7d8 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -23,6 +23,7 @@ #include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" #include "tcg-internal.h" +#include "tcg-has.h" /* * Vector optional opcode tracking. diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 872fb22ef8..ab5ccd8dcb 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -29,7 +29,7 @@ #include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" - +#include "tcg-has.h" /* * Encourage the compiler to tail-call to a function, rather than inlining. diff --git a/tcg/tcg.c b/tcg/tcg.c index 505e43c128..3576299a1c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -56,6 +56,7 @@ #include "tcg/tcg-temp-internal.h" #include "tcg-internal.h" #include "tcg/perf.h" +#include "tcg-has.h" #ifdef CONFIG_USER_ONLY #include "user/guest-base.h" #endif diff --git a/tcg/tci.c b/tcg/tci.c index 3eb95e20b6..39a68db287 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -22,6 +22,7 @@ #include "tcg/helper-info.h" #include "tcg/tcg-ldst.h" #include "disas/dis-asm.h" +#include "tcg-has.h" #include From 12f06532c86467c2efac13f6cd630fa5c6f7bda8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 01:32:30 -0800 Subject: [PATCH 1099/2892] tcg: Split out tcg-target-mo.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/internal-target.h | 1 + tcg/aarch64/tcg-target-mo.h | 12 ++++++++++++ tcg/aarch64/tcg-target.h | 2 -- tcg/arm/tcg-target-mo.h | 13 +++++++++++++ tcg/arm/tcg-target.h | 2 -- tcg/i386/tcg-target-mo.h | 19 +++++++++++++++++++ tcg/i386/tcg-target.h | 11 ----------- tcg/loongarch64/tcg-target-mo.h | 12 ++++++++++++ tcg/loongarch64/tcg-target.h | 2 -- tcg/mips/tcg-target-mo.h | 13 +++++++++++++ tcg/mips/tcg-target.h | 2 -- tcg/ppc/tcg-target-mo.h | 12 ++++++++++++ tcg/ppc/tcg-target.h | 2 -- tcg/riscv/tcg-target-mo.h | 12 ++++++++++++ tcg/riscv/tcg-target.h | 2 -- tcg/s390x/tcg-target-mo.h | 12 ++++++++++++ tcg/s390x/tcg-target.h | 2 -- tcg/sparc64/tcg-target-mo.h | 12 ++++++++++++ tcg/sparc64/tcg-target.h | 2 -- tcg/tcg-op-ldst.c | 1 + tcg/tci/tcg-target-mo.h | 17 +++++++++++++++++ tcg/tci/tcg-target.h | 5 ----- 22 files changed, 136 insertions(+), 32 deletions(-) create mode 100644 tcg/aarch64/tcg-target-mo.h create mode 100644 tcg/arm/tcg-target-mo.h create mode 100644 tcg/i386/tcg-target-mo.h create mode 100644 tcg/loongarch64/tcg-target-mo.h create mode 100644 tcg/mips/tcg-target-mo.h create mode 100644 tcg/ppc/tcg-target-mo.h create mode 100644 tcg/riscv/tcg-target-mo.h create mode 100644 tcg/s390x/tcg-target-mo.h create mode 100644 tcg/sparc64/tcg-target-mo.h create mode 100644 tcg/tci/tcg-target-mo.h diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index a664be02cc..2cdf11c905 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -12,6 +12,7 @@ #include "exec/exec-all.h" #include "exec/translation-block.h" #include "tb-internal.h" +#include "tcg-target-mo.h" /* * Access to the various translations structures need to be serialised diff --git a/tcg/aarch64/tcg-target-mo.h b/tcg/aarch64/tcg-target-mo.h new file mode 100644 index 0000000000..e8e8923014 --- /dev/null +++ b/tcg/aarch64/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Define target-specific memory model + * Copyright (c) 2013 Huawei Technologies Duesseldorf GmbH + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 1ef8b2e300..0dd6e1f069 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -47,6 +47,4 @@ typedef enum { #define TCG_TARGET_NB_REGS 64 -#define TCG_TARGET_DEFAULT_MO (0) - #endif /* AARCH64_TCG_TARGET_H */ diff --git a/tcg/arm/tcg-target-mo.h b/tcg/arm/tcg-target-mo.h new file mode 100644 index 0000000000..12542dfd1c --- /dev/null +++ b/tcg/arm/tcg-target-mo.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + * Copyright (c) 2008 Andrzej Zaborowski + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 21563e00f9..4f9f877121 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -70,6 +70,4 @@ typedef enum { #define TCG_TARGET_NB_REGS 32 -#define TCG_TARGET_DEFAULT_MO (0) - #endif diff --git a/tcg/i386/tcg-target-mo.h b/tcg/i386/tcg-target-mo.h new file mode 100644 index 0000000000..7567dc7248 --- /dev/null +++ b/tcg/i386/tcg-target-mo.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +/* + * This defines the natural memory order supported by this architecture + * before guarantees made by various barrier instructions. + * + * The x86 has a pretty strong memory ordering which only really + * allows for some stores to be re-ordered after loads. + */ +#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + +#endif diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index e6d7fd526e..3cbdfbca52 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -88,15 +88,4 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_ESP } TCGReg; -/* This defines the natural memory order supported by this - * architecture before guarantees made by various barrier - * instructions. - * - * The x86 has a pretty strong memory ordering which only really - * allows for some stores to be re-ordered after loads. - */ -#include "tcg/tcg-mo.h" - -#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - #endif diff --git a/tcg/loongarch64/tcg-target-mo.h b/tcg/loongarch64/tcg-target-mo.h new file mode 100644 index 0000000000..d35506957f --- /dev/null +++ b/tcg/loongarch64/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2021 WANG Xuerui + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 0432a4ebbd..8533284631 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -85,6 +85,4 @@ typedef enum { TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; -#define TCG_TARGET_DEFAULT_MO (0) - #endif /* LOONGARCH_TCG_TARGET_H */ diff --git a/tcg/mips/tcg-target-mo.h b/tcg/mips/tcg-target-mo.h new file mode 100644 index 0000000000..50cefc222d --- /dev/null +++ b/tcg/mips/tcg-target-mo.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008-2009 Arnaud Patard + * Copyright (c) 2009 Aurelien Jarno + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 210044ca12..3090acc4f5 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -70,6 +70,4 @@ typedef enum { TCG_AREG0 = TCG_REG_S8, } TCGReg; -#define TCG_TARGET_DEFAULT_MO 0 - #endif diff --git a/tcg/ppc/tcg-target-mo.h b/tcg/ppc/tcg-target-mo.h new file mode 100644 index 0000000000..98bfe03b7a --- /dev/null +++ b/tcg/ppc/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 0bc13d7363..5607634e99 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -53,6 +53,4 @@ typedef enum { TCG_AREG0 = TCG_REG_R27 } TCGReg; -#define TCG_TARGET_DEFAULT_MO (0) - #endif diff --git a/tcg/riscv/tcg-target-mo.h b/tcg/riscv/tcg-target-mo.h new file mode 100644 index 0000000000..691b5d0da8 --- /dev/null +++ b/tcg/riscv/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2018 SiFive, Inc + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 4c40662402..db5f3d8b72 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -57,6 +57,4 @@ typedef enum { TCG_REG_TMP2 = TCG_REG_T4, } TCGReg; -#define TCG_TARGET_DEFAULT_MO (0) - #endif diff --git a/tcg/s390x/tcg-target-mo.h b/tcg/s390x/tcg-target-mo.h new file mode 100644 index 0000000000..962295ed51 --- /dev/null +++ b/tcg/s390x/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2009 Ulrich Hecht + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + +#endif diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index f790b77075..0ef5a6d3dd 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -51,6 +51,4 @@ typedef enum TCGReg { #define TCG_TARGET_NB_REGS 64 -#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - #endif diff --git a/tcg/sparc64/tcg-target-mo.h b/tcg/sparc64/tcg-target-mo.h new file mode 100644 index 0000000000..98bfe03b7a --- /dev/null +++ b/tcg/sparc64/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 5ecca5586b..f7d75d5806 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -66,6 +66,4 @@ typedef enum { #define TCG_AREG0 TCG_REG_I0 -#define TCG_TARGET_DEFAULT_MO (0) - #endif diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index ec3ef4dcb4..77271e0193 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -31,6 +31,7 @@ #include "exec/plugin-gen.h" #include "tcg-internal.h" #include "tcg-has.h" +#include "tcg-target-mo.h" static void check_max_alignment(unsigned a_bits) { diff --git a/tcg/tci/tcg-target-mo.h b/tcg/tci/tcg-target-mo.h new file mode 100644 index 0000000000..779872e39a --- /dev/null +++ b/tcg/tci/tcg-target-mo.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2009, 2011 Stefan Weil + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +/* + * We could notice __i386__ or __s390x__ and reduce the barriers depending + * on the host. But if you want performance, you use the normal backend. + * We prefer consistency across hosts on this. + */ +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index fea92f7848..a9ca493d20 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -74,9 +74,4 @@ typedef enum { #define HAVE_TCG_QEMU_TB_EXEC #define TCG_TARGET_NEED_POOL_LABELS -/* We could notice __i386__ or __s390x__ and reduce the barriers depending - on the host. But if you want performance, you use the normal backend. - We prefer consistency across hosts on this. */ -#define TCG_TARGET_DEFAULT_MO (0) - #endif /* TCG_TARGET_H */ From da43e5e6ba64fbe50d6437719470d57874939542 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Dec 2024 14:30:01 -0800 Subject: [PATCH 1100/2892] tcg: Use C_NotImplemented in tcg_target_op_def MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return C_NotImplemented instead of asserting for opcodes not implemented by the backend. For now, the assertion moves to process_op_defs. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 2 +- tcg/arm/tcg-target.c.inc | 2 +- tcg/i386/tcg-target.c.inc | 2 +- tcg/loongarch64/tcg-target.c.inc | 2 +- tcg/mips/tcg-target.c.inc | 2 +- tcg/ppc/tcg-target.c.inc | 2 +- tcg/riscv/tcg-target.c.inc | 2 +- tcg/s390x/tcg-target.c.inc | 2 +- tcg/sparc64/tcg-target.c.inc | 2 +- tcg/tcg.c | 10 ++++++---- tcg/tci/tcg-target.c.inc | 2 +- 11 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index d77d305f30..d1e08def60 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -3158,7 +3158,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I2(w, 0, w); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 90ac80077f..b4cd36a9b8 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2260,7 +2260,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_bitsel_vec: return C_O1_I3(w, w, w, w); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 167228a781..813c12ca0e 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3885,7 +3885,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I4(x, x, x, xO, x); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index a273e7fce5..686b94ccda 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -2391,7 +2391,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I3(w, w, w, w); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 8857398893..199bd97c0e 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2292,7 +2292,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) : C_O0_I4(rZ, rZ, r, r)); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 94997b126f..3ce4fa1db6 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -4354,7 +4354,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I4(v, v, v, vZM, v); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 7d1bba100a..51cd7e7586 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2767,7 +2767,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_cmpsel_vec: return C_O1_I4(v, v, vL, vK, vK); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index fdf57c0b07..98925b1d5d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3427,7 +3427,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) : C_O1_I4(v, v, v, vZ, v)); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index fe3e727399..1201607722 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1627,7 +1627,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I2(r, r, r); default: - g_assert_not_reached(); + return C_NotImplemented; } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 3576299a1c..05bb464940 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -862,6 +862,7 @@ static int tcg_out_pool_finalize(TCGContext *s) #define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_n1_o1_i4_, O1, O2, I1, I2, I3, I4), typedef enum { + C_NotImplemented = -1, #include "tcg-target-con-set.h" } TCGConstraintSetIndex; @@ -3176,6 +3177,7 @@ static void process_op_defs(TCGContext *s) const TCGTargetOpDef *tdefs; bool saw_alias_pair = false; int i, o, i2, o2, nb_args; + TCGConstraintSetIndex con_set; if (def->flags & TCG_OPF_NOT_PRESENT) { continue; @@ -3188,11 +3190,11 @@ static void process_op_defs(TCGContext *s) /* * Macro magic should make it impossible, but double-check that - * the array index is in range. Since the signness of an enum - * is implementation defined, force the result to unsigned. + * the array index is in range. At the same time, double-check + * that the opcode is implemented, i.e. not C_NotImplemented. */ - unsigned con_set = tcg_target_op_def(op); - tcg_debug_assert(con_set < ARRAY_SIZE(constraint_sets)); + con_set = tcg_target_op_def(op); + tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); tdefs = &constraint_sets[con_set]; for (i = 0; i < nb_args; i++) { diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 5f88ca0537..74b649c902 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -186,7 +186,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I4(r, r, r, r); default: - g_assert_not_reached(); + return C_NotImplemented; } } From f44824cc4dcdc993d60639576d706ecf5996ca5a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Jan 2025 13:59:39 -0800 Subject: [PATCH 1101/2892] tcg: Change have_vec to has_type in tcg_op_supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test each vector type, not just lumping them all together. Add tests for I32 (always true) and I64 (64-bit hosts). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 66 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 05bb464940..915a181596 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2136,8 +2136,28 @@ TCGTemp *tcgv_i32_temp(TCGv_i32 v) */ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) { - const bool have_vec - = TCG_TARGET_HAS_v64 | TCG_TARGET_HAS_v128 | TCG_TARGET_HAS_v256; + bool has_type; + + switch (type) { + case TCG_TYPE_I32: + has_type = true; + break; + case TCG_TYPE_I64: + has_type = TCG_TARGET_REG_BITS == 64; + break; + case TCG_TYPE_V64: + has_type = TCG_TARGET_HAS_v64; + break; + case TCG_TYPE_V128: + has_type = TCG_TARGET_HAS_v128; + break; + case TCG_TYPE_V256: + has_type = TCG_TARGET_HAS_v256; + break; + default: + has_type = false; + break; + } switch (op) { case INDEX_op_discard: @@ -2376,60 +2396,60 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_or_vec: case INDEX_op_xor_vec: case INDEX_op_cmp_vec: - return have_vec; + return has_type; case INDEX_op_dup2_vec: - return have_vec && TCG_TARGET_REG_BITS == 32; + return has_type && TCG_TARGET_REG_BITS == 32; case INDEX_op_not_vec: - return have_vec && TCG_TARGET_HAS_not_vec; + return has_type && TCG_TARGET_HAS_not_vec; case INDEX_op_neg_vec: - return have_vec && TCG_TARGET_HAS_neg_vec; + return has_type && TCG_TARGET_HAS_neg_vec; case INDEX_op_abs_vec: - return have_vec && TCG_TARGET_HAS_abs_vec; + return has_type && TCG_TARGET_HAS_abs_vec; case INDEX_op_andc_vec: - return have_vec && TCG_TARGET_HAS_andc_vec; + return has_type && TCG_TARGET_HAS_andc_vec; case INDEX_op_orc_vec: - return have_vec && TCG_TARGET_HAS_orc_vec; + return has_type && TCG_TARGET_HAS_orc_vec; case INDEX_op_nand_vec: - return have_vec && TCG_TARGET_HAS_nand_vec; + return has_type && TCG_TARGET_HAS_nand_vec; case INDEX_op_nor_vec: - return have_vec && TCG_TARGET_HAS_nor_vec; + return has_type && TCG_TARGET_HAS_nor_vec; case INDEX_op_eqv_vec: - return have_vec && TCG_TARGET_HAS_eqv_vec; + return has_type && TCG_TARGET_HAS_eqv_vec; case INDEX_op_mul_vec: - return have_vec && TCG_TARGET_HAS_mul_vec; + return has_type && TCG_TARGET_HAS_mul_vec; case INDEX_op_shli_vec: case INDEX_op_shri_vec: case INDEX_op_sari_vec: - return have_vec && TCG_TARGET_HAS_shi_vec; + return has_type && TCG_TARGET_HAS_shi_vec; case INDEX_op_shls_vec: case INDEX_op_shrs_vec: case INDEX_op_sars_vec: - return have_vec && TCG_TARGET_HAS_shs_vec; + return has_type && TCG_TARGET_HAS_shs_vec; case INDEX_op_shlv_vec: case INDEX_op_shrv_vec: case INDEX_op_sarv_vec: - return have_vec && TCG_TARGET_HAS_shv_vec; + return has_type && TCG_TARGET_HAS_shv_vec; case INDEX_op_rotli_vec: - return have_vec && TCG_TARGET_HAS_roti_vec; + return has_type && TCG_TARGET_HAS_roti_vec; case INDEX_op_rotls_vec: - return have_vec && TCG_TARGET_HAS_rots_vec; + return has_type && TCG_TARGET_HAS_rots_vec; case INDEX_op_rotlv_vec: case INDEX_op_rotrv_vec: - return have_vec && TCG_TARGET_HAS_rotv_vec; + return has_type && TCG_TARGET_HAS_rotv_vec; case INDEX_op_ssadd_vec: case INDEX_op_usadd_vec: case INDEX_op_sssub_vec: case INDEX_op_ussub_vec: - return have_vec && TCG_TARGET_HAS_sat_vec; + return has_type && TCG_TARGET_HAS_sat_vec; case INDEX_op_smin_vec: case INDEX_op_umin_vec: case INDEX_op_smax_vec: case INDEX_op_umax_vec: - return have_vec && TCG_TARGET_HAS_minmax_vec; + return has_type && TCG_TARGET_HAS_minmax_vec; case INDEX_op_bitsel_vec: - return have_vec && TCG_TARGET_HAS_bitsel_vec; + return has_type && TCG_TARGET_HAS_bitsel_vec; case INDEX_op_cmpsel_vec: - return have_vec && TCG_TARGET_HAS_cmpsel_vec; + return has_type && TCG_TARGET_HAS_cmpsel_vec; default: tcg_debug_assert(op > INDEX_op_last_generic && op < NB_OPS); From 3e80824e8bffef2d1dd27165a2cf48e8c034ed10 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Dec 2024 11:40:42 -0800 Subject: [PATCH 1102/2892] tcg: Reorg process_op_defs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Process each TCGConstraintSetIndex first. Allocate TCGArgConstraint arrays based on those. Only afterward process the TCGOpcodes and share those TCGArgConstraint arrays. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 7 +- tcg/tcg.c | 272 +++++++++++++++++++++++----------------------- 2 files changed, 136 insertions(+), 143 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index e5fa69d20b..a02de82508 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -714,17 +714,12 @@ typedef struct TCGOpDef { const char *name; uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; uint8_t flags; - TCGArgConstraint *args_ct; + const TCGArgConstraint *args_ct; } TCGOpDef; extern TCGOpDef tcg_op_defs[]; extern const size_t tcg_op_defs_max; -typedef struct TCGTargetOpDef { - TCGOpcode op; - const char *args_ct_str[TCG_MAX_OP_ARGS]; -} TCGTargetOpDef; - /* * tcg_op_supported: * Query if @op, for @type and @flags, is supported by the host diff --git a/tcg/tcg.c b/tcg/tcg.c index 915a181596..d5ab0abe9d 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -887,31 +887,35 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); /* Put all of the constraint sets into an array, indexed by the enum. */ -#define C_O0_I1(I1) { .args_ct_str = { #I1 } }, -#define C_O0_I2(I1, I2) { .args_ct_str = { #I1, #I2 } }, -#define C_O0_I3(I1, I2, I3) { .args_ct_str = { #I1, #I2, #I3 } }, -#define C_O0_I4(I1, I2, I3, I4) { .args_ct_str = { #I1, #I2, #I3, #I4 } }, +typedef struct TCGConstraintSet { + uint8_t nb_oargs, nb_iargs; + const char *args_ct_str[TCG_MAX_OP_ARGS]; +} TCGConstraintSet; -#define C_O1_I1(O1, I1) { .args_ct_str = { #O1, #I1 } }, -#define C_O1_I2(O1, I1, I2) { .args_ct_str = { #O1, #I1, #I2 } }, -#define C_O1_I3(O1, I1, I2, I3) { .args_ct_str = { #O1, #I1, #I2, #I3 } }, -#define C_O1_I4(O1, I1, I2, I3, I4) { .args_ct_str = { #O1, #I1, #I2, #I3, #I4 } }, +#define C_O0_I1(I1) { 0, 1, { #I1 } }, +#define C_O0_I2(I1, I2) { 0, 2, { #I1, #I2 } }, +#define C_O0_I3(I1, I2, I3) { 0, 3, { #I1, #I2, #I3 } }, +#define C_O0_I4(I1, I2, I3, I4) { 0, 4, { #I1, #I2, #I3, #I4 } }, -#define C_N1_I2(O1, I1, I2) { .args_ct_str = { "&" #O1, #I1, #I2 } }, -#define C_N1O1_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, #O2, #I1 } }, -#define C_N2_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, "&" #O2, #I1 } }, +#define C_O1_I1(O1, I1) { 1, 1, { #O1, #I1 } }, +#define C_O1_I2(O1, I1, I2) { 1, 2, { #O1, #I1, #I2 } }, +#define C_O1_I3(O1, I1, I2, I3) { 1, 3, { #O1, #I1, #I2, #I3 } }, +#define C_O1_I4(O1, I1, I2, I3, I4) { 1, 4, { #O1, #I1, #I2, #I3, #I4 } }, -#define C_O2_I1(O1, O2, I1) { .args_ct_str = { #O1, #O2, #I1 } }, -#define C_O2_I2(O1, O2, I1, I2) { .args_ct_str = { #O1, #O2, #I1, #I2 } }, -#define C_O2_I3(O1, O2, I1, I2, I3) { .args_ct_str = { #O1, #O2, #I1, #I2, #I3 } }, -#define C_O2_I4(O1, O2, I1, I2, I3, I4) { .args_ct_str = { #O1, #O2, #I1, #I2, #I3, #I4 } }, -#define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) { .args_ct_str = { "&" #O1, #O2, #I1, #I2, #I3, #I4 } }, +#define C_N1_I2(O1, I1, I2) { 1, 2, { "&" #O1, #I1, #I2 } }, +#define C_N1O1_I1(O1, O2, I1) { 2, 1, { "&" #O1, #O2, #I1 } }, +#define C_N2_I1(O1, O2, I1) { 2, 1, { "&" #O1, "&" #O2, #I1 } }, -static const TCGTargetOpDef constraint_sets[] = { +#define C_O2_I1(O1, O2, I1) { 2, 1, { #O1, #O2, #I1 } }, +#define C_O2_I2(O1, O2, I1, I2) { 2, 2, { #O1, #O2, #I1, #I2 } }, +#define C_O2_I3(O1, O2, I1, I2, I3) { 2, 3, { #O1, #O2, #I1, #I2, #I3 } }, +#define C_O2_I4(O1, O2, I1, I2, I3, I4) { 2, 4, { #O1, #O2, #I1, #I2, #I3, #I4 } }, +#define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) { 2, 4, { "&" #O1, #O2, #I1, #I2, #I3, #I4 } }, + +static const TCGConstraintSet constraint_sets[] = { #include "tcg-target-con-set.h" }; - #undef C_O0_I1 #undef C_O0_I2 #undef C_O0_I3 @@ -1499,32 +1503,12 @@ static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type, static void tcg_context_init(unsigned max_cpus) { TCGContext *s = &tcg_init_ctx; - int op, total_args, n, i; - TCGOpDef *def; - TCGArgConstraint *args_ct; + int n, i; TCGTemp *ts; memset(s, 0, sizeof(*s)); s->nb_globals = 0; - /* Count total number of arguments and allocate the corresponding - space */ - total_args = 0; - for(op = 0; op < NB_OPS; op++) { - def = &tcg_op_defs[op]; - n = def->nb_iargs + def->nb_oargs; - total_args += n; - } - - args_ct = g_new0(TCGArgConstraint, total_args); - - for(op = 0; op < NB_OPS; op++) { - def = &tcg_op_defs[op]; - def->args_ct = args_ct; - n = def->nb_iargs + def->nb_oargs; - args_ct += n; - } - init_call_layout(&info_helper_ld32_mmu); init_call_layout(&info_helper_ld64_mmu); init_call_layout(&info_helper_ld128_mmu); @@ -3132,10 +3116,12 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } /* we give more priority to constraints with less registers */ -static int get_constraint_priority(const TCGOpDef *def, int k) +static int get_constraint_priority(const TCGArgConstraint *arg_ct, int k) { - const TCGArgConstraint *arg_ct = &def->args_ct[k]; - int n = ctpop64(arg_ct->regs); + int n; + + arg_ct += k; + n = ctpop64(arg_ct->regs); /* * Sort constraints of a single register first, which includes output @@ -3164,10 +3150,9 @@ static int get_constraint_priority(const TCGOpDef *def, int k) } /* sort from highest priority to lowest */ -static void sort_constraints(TCGOpDef *def, int start, int n) +static void sort_constraints(TCGArgConstraint *a, int start, int n) { int i, j; - TCGArgConstraint *a = def->args_ct; for (i = 0; i < n; i++) { a[start + i].sort_index = start + i; @@ -3177,8 +3162,8 @@ static void sort_constraints(TCGOpDef *def, int start, int n) } for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { - int p1 = get_constraint_priority(def, a[start + i].sort_index); - int p2 = get_constraint_priority(def, a[start + j].sort_index); + int p1 = get_constraint_priority(a, a[start + i].sort_index); + int p2 = get_constraint_priority(a, a[start + j].sort_index); if (p1 < p2) { int tmp = a[start + i].sort_index; a[start + i].sort_index = a[start + j].sort_index; @@ -3188,57 +3173,39 @@ static void sort_constraints(TCGOpDef *def, int start, int n) } } +static const TCGArgConstraint empty_cts[TCG_MAX_OP_ARGS]; +static TCGArgConstraint all_cts[ARRAY_SIZE(constraint_sets)][TCG_MAX_OP_ARGS]; + static void process_op_defs(TCGContext *s) { - TCGOpcode op; - - for (op = 0; op < NB_OPS; op++) { - TCGOpDef *def = &tcg_op_defs[op]; - const TCGTargetOpDef *tdefs; + for (size_t c = 0; c < ARRAY_SIZE(constraint_sets); ++c) { + const TCGConstraintSet *tdefs = &constraint_sets[c]; + TCGArgConstraint *args_ct = all_cts[c]; + int nb_oargs = tdefs->nb_oargs; + int nb_iargs = tdefs->nb_iargs; + int nb_args = nb_oargs + nb_iargs; bool saw_alias_pair = false; - int i, o, i2, o2, nb_args; - TCGConstraintSetIndex con_set; - if (def->flags & TCG_OPF_NOT_PRESENT) { - continue; - } - - nb_args = def->nb_iargs + def->nb_oargs; - if (nb_args == 0) { - continue; - } - - /* - * Macro magic should make it impossible, but double-check that - * the array index is in range. At the same time, double-check - * that the opcode is implemented, i.e. not C_NotImplemented. - */ - con_set = tcg_target_op_def(op); - tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); - tdefs = &constraint_sets[con_set]; - - for (i = 0; i < nb_args; i++) { + for (int i = 0; i < nb_args; i++) { const char *ct_str = tdefs->args_ct_str[i]; - bool input_p = i >= def->nb_oargs; - - /* Incomplete TCGTargetOpDef entry. */ - tcg_debug_assert(ct_str != NULL); + bool input_p = i >= nb_oargs; + int o; switch (*ct_str) { case '0' ... '9': o = *ct_str - '0'; tcg_debug_assert(input_p); - tcg_debug_assert(o < def->nb_oargs); - tcg_debug_assert(def->args_ct[o].regs != 0); - tcg_debug_assert(!def->args_ct[o].oalias); - def->args_ct[i] = def->args_ct[o]; + tcg_debug_assert(o < nb_oargs); + tcg_debug_assert(args_ct[o].regs != 0); + tcg_debug_assert(!args_ct[o].oalias); + args_ct[i] = args_ct[o]; /* The output sets oalias. */ - def->args_ct[o].oalias = 1; - def->args_ct[o].alias_index = i; + args_ct[o].oalias = 1; + args_ct[o].alias_index = i; /* The input sets ialias. */ - def->args_ct[i].ialias = 1; - def->args_ct[i].alias_index = o; - if (def->args_ct[i].pair) { + args_ct[i].ialias = 1; + args_ct[i].alias_index = o; + if (args_ct[i].pair) { saw_alias_pair = true; } tcg_debug_assert(ct_str[1] == '\0'); @@ -3246,41 +3213,41 @@ static void process_op_defs(TCGContext *s) case '&': tcg_debug_assert(!input_p); - def->args_ct[i].newreg = true; + args_ct[i].newreg = true; ct_str++; break; case 'p': /* plus */ /* Allocate to the register after the previous. */ - tcg_debug_assert(i > (input_p ? def->nb_oargs : 0)); + tcg_debug_assert(i > (input_p ? nb_oargs : 0)); o = i - 1; - tcg_debug_assert(!def->args_ct[o].pair); - tcg_debug_assert(!def->args_ct[o].ct); - def->args_ct[i] = (TCGArgConstraint){ + tcg_debug_assert(!args_ct[o].pair); + tcg_debug_assert(!args_ct[o].ct); + args_ct[i] = (TCGArgConstraint){ .pair = 2, .pair_index = o, - .regs = def->args_ct[o].regs << 1, - .newreg = def->args_ct[o].newreg, + .regs = args_ct[o].regs << 1, + .newreg = args_ct[o].newreg, }; - def->args_ct[o].pair = 1; - def->args_ct[o].pair_index = i; + args_ct[o].pair = 1; + args_ct[o].pair_index = i; tcg_debug_assert(ct_str[1] == '\0'); continue; case 'm': /* minus */ /* Allocate to the register before the previous. */ - tcg_debug_assert(i > (input_p ? def->nb_oargs : 0)); + tcg_debug_assert(i > (input_p ? nb_oargs : 0)); o = i - 1; - tcg_debug_assert(!def->args_ct[o].pair); - tcg_debug_assert(!def->args_ct[o].ct); - def->args_ct[i] = (TCGArgConstraint){ + tcg_debug_assert(!args_ct[o].pair); + tcg_debug_assert(!args_ct[o].ct); + args_ct[i] = (TCGArgConstraint){ .pair = 1, .pair_index = o, - .regs = def->args_ct[o].regs >> 1, - .newreg = def->args_ct[o].newreg, + .regs = args_ct[o].regs >> 1, + .newreg = args_ct[o].newreg, }; - def->args_ct[o].pair = 2; - def->args_ct[o].pair_index = i; + args_ct[o].pair = 2; + args_ct[o].pair_index = i; tcg_debug_assert(ct_str[1] == '\0'); continue; } @@ -3288,16 +3255,16 @@ static void process_op_defs(TCGContext *s) do { switch (*ct_str) { case 'i': - def->args_ct[i].ct |= TCG_CT_CONST; + args_ct[i].ct |= TCG_CT_CONST; break; /* Include all of the target-specific constraints. */ #undef CONST #define CONST(CASE, MASK) \ - case CASE: def->args_ct[i].ct |= MASK; break; + case CASE: args_ct[i].ct |= MASK; break; #define REGS(CASE, MASK) \ - case CASE: def->args_ct[i].regs |= MASK; break; + case CASE: args_ct[i].regs |= MASK; break; #include "tcg-target-con-str.h" @@ -3308,15 +3275,12 @@ static void process_op_defs(TCGContext *s) case '&': case 'p': case 'm': - /* Typo in TCGTargetOpDef constraint. */ + /* Typo in TCGConstraintSet constraint. */ g_assert_not_reached(); } } while (*++ct_str != '\0'); } - /* TCGTargetOpDef entry with too much information? */ - tcg_debug_assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL); - /* * Fix up output pairs that are aliased with inputs. * When we created the alias, we copied pair from the output. @@ -3337,51 +3301,53 @@ static void process_op_defs(TCGContext *s) * first output to pair=3, and the pair_index'es to match. */ if (saw_alias_pair) { - for (i = def->nb_oargs; i < nb_args; i++) { + for (int i = nb_oargs; i < nb_args; i++) { + int o, o2, i2; + /* * Since [0-9pm] must be alone in the constraint string, * the only way they can both be set is if the pair comes * from the output alias. */ - if (!def->args_ct[i].ialias) { + if (!args_ct[i].ialias) { continue; } - switch (def->args_ct[i].pair) { + switch (args_ct[i].pair) { case 0: break; case 1: - o = def->args_ct[i].alias_index; - o2 = def->args_ct[o].pair_index; - tcg_debug_assert(def->args_ct[o].pair == 1); - tcg_debug_assert(def->args_ct[o2].pair == 2); - if (def->args_ct[o2].oalias) { + o = args_ct[i].alias_index; + o2 = args_ct[o].pair_index; + tcg_debug_assert(args_ct[o].pair == 1); + tcg_debug_assert(args_ct[o2].pair == 2); + if (args_ct[o2].oalias) { /* Case 1a */ - i2 = def->args_ct[o2].alias_index; - tcg_debug_assert(def->args_ct[i2].pair == 2); - def->args_ct[i2].pair_index = i; - def->args_ct[i].pair_index = i2; + i2 = args_ct[o2].alias_index; + tcg_debug_assert(args_ct[i2].pair == 2); + args_ct[i2].pair_index = i; + args_ct[i].pair_index = i2; } else { /* Case 1b */ - def->args_ct[i].pair_index = i; + args_ct[i].pair_index = i; } break; case 2: - o = def->args_ct[i].alias_index; - o2 = def->args_ct[o].pair_index; - tcg_debug_assert(def->args_ct[o].pair == 2); - tcg_debug_assert(def->args_ct[o2].pair == 1); - if (def->args_ct[o2].oalias) { + o = args_ct[i].alias_index; + o2 = args_ct[o].pair_index; + tcg_debug_assert(args_ct[o].pair == 2); + tcg_debug_assert(args_ct[o2].pair == 1); + if (args_ct[o2].oalias) { /* Case 1a */ - i2 = def->args_ct[o2].alias_index; - tcg_debug_assert(def->args_ct[i2].pair == 1); - def->args_ct[i2].pair_index = i; - def->args_ct[i].pair_index = i2; + i2 = args_ct[o2].alias_index; + tcg_debug_assert(args_ct[i2].pair == 1); + args_ct[i2].pair_index = i; + args_ct[i].pair_index = i2; } else { /* Case 2 */ - def->args_ct[i].pair = 3; - def->args_ct[o2].pair = 3; - def->args_ct[i].pair_index = o2; - def->args_ct[o2].pair_index = i; + args_ct[i].pair = 3; + args_ct[o2].pair = 3; + args_ct[i].pair_index = o2; + args_ct[o2].pair_index = i; } break; default: @@ -3391,8 +3357,40 @@ static void process_op_defs(TCGContext *s) } /* sort the constraints (XXX: this is just an heuristic) */ - sort_constraints(def, 0, def->nb_oargs); - sort_constraints(def, def->nb_oargs, def->nb_iargs); + sort_constraints(args_ct, 0, nb_oargs); + sort_constraints(args_ct, nb_oargs, nb_iargs); + } + + for (TCGOpcode op = 0; op < NB_OPS; op++) { + TCGOpDef *def = &tcg_op_defs[op]; + const TCGConstraintSet *tdefs; + TCGConstraintSetIndex con_set; + int nb_args; + + nb_args = def->nb_iargs + def->nb_oargs; + if (nb_args == 0) { + continue; + } + + if (def->flags & TCG_OPF_NOT_PRESENT) { + def->args_ct = empty_cts; + continue; + } + + /* + * Macro magic should make it impossible, but double-check that + * the array index is in range. At the same time, double-check + * that the opcode is implemented, i.e. not C_NotImplemented. + */ + con_set = tcg_target_op_def(op); + tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); + + /* The constraint arguments must match TCGOpcode arguments. */ + tdefs = &constraint_sets[con_set]; + tcg_debug_assert(tdefs->nb_oargs == def->nb_oargs); + tcg_debug_assert(tdefs->nb_iargs == def->nb_iargs); + + def->args_ct = all_cts[con_set]; } } From 501fb3da3fd60538214218cfbccc1abf45a047c2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 10:34:34 -0800 Subject: [PATCH 1103/2892] tcg: Remove args_ct from TCGOpDef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new function, opcode_args_ct, to look up the argument set for an opcode. We lose the ability to assert the correctness of the map from TCGOpcode to constraint sets at startup, but we can still validate at runtime upon lookup. Rename process_op_defs to process_constraint_sets, as it now does nothing to TCGOpDef. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 1 - tcg/tcg-common.c | 2 +- tcg/tcg.c | 82 ++++++++++++++++++++++------------------------- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index a02de82508..e28894c57b 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -714,7 +714,6 @@ typedef struct TCGOpDef { const char *name; uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; uint8_t flags; - const TCGArgConstraint *args_ct; } TCGOpDef; extern TCGOpDef tcg_op_defs[]; diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index fadc33c3d1..0f30e5b3ec 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -28,7 +28,7 @@ TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ - { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, NULL }, + { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg/tcg-opc.h" #undef DEF }; diff --git a/tcg/tcg.c b/tcg/tcg.c index d5ab0abe9d..df7c4dab88 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1496,7 +1496,7 @@ static void init_call_layout(TCGHelperInfo *info) } static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)]; -static void process_op_defs(TCGContext *s); +static void process_constraint_sets(void); static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type, TCGReg reg, const char *name); @@ -1517,7 +1517,7 @@ static void tcg_context_init(unsigned max_cpus) init_call_layout(&info_helper_st128_mmu); tcg_target_init(s); - process_op_defs(s); + process_constraint_sets(); /* Reverse the order of the saved registers, assuming they're all at the start of tcg_target_reg_alloc_order. */ @@ -3176,7 +3176,7 @@ static void sort_constraints(TCGArgConstraint *a, int start, int n) static const TCGArgConstraint empty_cts[TCG_MAX_OP_ARGS]; static TCGArgConstraint all_cts[ARRAY_SIZE(constraint_sets)][TCG_MAX_OP_ARGS]; -static void process_op_defs(TCGContext *s) +static void process_constraint_sets(void) { for (size_t c = 0; c < ARRAY_SIZE(constraint_sets); ++c) { const TCGConstraintSet *tdefs = &constraint_sets[c]; @@ -3360,38 +3360,28 @@ static void process_op_defs(TCGContext *s) sort_constraints(args_ct, 0, nb_oargs); sort_constraints(args_ct, nb_oargs, nb_iargs); } +} - for (TCGOpcode op = 0; op < NB_OPS; op++) { - TCGOpDef *def = &tcg_op_defs[op]; - const TCGConstraintSet *tdefs; - TCGConstraintSetIndex con_set; - int nb_args; +static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) +{ + TCGOpDef *def = &tcg_op_defs[op->opc]; + TCGConstraintSetIndex con_set; - nb_args = def->nb_iargs + def->nb_oargs; - if (nb_args == 0) { - continue; - } - - if (def->flags & TCG_OPF_NOT_PRESENT) { - def->args_ct = empty_cts; - continue; - } - - /* - * Macro magic should make it impossible, but double-check that - * the array index is in range. At the same time, double-check - * that the opcode is implemented, i.e. not C_NotImplemented. - */ - con_set = tcg_target_op_def(op); - tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); - - /* The constraint arguments must match TCGOpcode arguments. */ - tdefs = &constraint_sets[con_set]; - tcg_debug_assert(tdefs->nb_oargs == def->nb_oargs); - tcg_debug_assert(tdefs->nb_iargs == def->nb_iargs); - - def->args_ct = all_cts[con_set]; + if (def->nb_iargs + def->nb_oargs == 0) { + return NULL; } + if (def->flags & TCG_OPF_NOT_PRESENT) { + return empty_cts; + } + + con_set = tcg_target_op_def(op->opc); + tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); + + /* The constraint arguments must match TCGOpcode arguments. */ + tcg_debug_assert(constraint_sets[con_set].nb_oargs == def->nb_oargs); + tcg_debug_assert(constraint_sets[con_set].nb_iargs == def->nb_iargs); + + return all_cts[con_set]; } static void remove_label_use(TCGOp *op, int idx) @@ -3864,6 +3854,7 @@ liveness_pass_1(TCGContext *s) TCGTemp *ts; TCGOpcode opc = op->opc; const TCGOpDef *def = &tcg_op_defs[opc]; + const TCGArgConstraint *args_ct; switch (opc) { case INDEX_op_call: @@ -4153,8 +4144,9 @@ liveness_pass_1(TCGContext *s) break; default: + args_ct = opcode_args_ct(op); for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - const TCGArgConstraint *ct = &def->args_ct[i]; + const TCGArgConstraint *ct = &args_ct[i]; TCGRegSet set, *pset; ts = arg_temp(op->args[i]); @@ -4941,6 +4933,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) { const TCGLifeData arg_life = op->life; TCGRegSet dup_out_regs, dup_in_regs; + const TCGArgConstraint *dup_args_ct; TCGTemp *its, *ots; TCGType itype, vtype; unsigned vece; @@ -4967,8 +4960,9 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) return; } - dup_out_regs = tcg_op_defs[INDEX_op_dup_vec].args_ct[0].regs; - dup_in_regs = tcg_op_defs[INDEX_op_dup_vec].args_ct[1].regs; + dup_args_ct = opcode_args_ct(op); + dup_out_regs = dup_args_ct[0].regs; + dup_in_regs = dup_args_ct[1].regs; /* Allocate the output register now. */ if (ots->val_type != TEMP_VAL_REG) { @@ -5054,6 +5048,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) int i, k, nb_iargs, nb_oargs; TCGReg reg; TCGArg arg; + const TCGArgConstraint *args_ct; const TCGArgConstraint *arg_ct; TCGTemp *ts; TCGArg new_args[TCG_MAX_OP_ARGS]; @@ -5098,6 +5093,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; } + args_ct = opcode_args_ct(op); + /* satisfy input constraints */ for (k = 0; k < nb_iargs; k++) { TCGRegSet i_preferred_regs, i_required_regs; @@ -5105,9 +5102,9 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) TCGTemp *ts2; int i1, i2; - i = def->args_ct[nb_oargs + k].sort_index; + i = args_ct[nb_oargs + k].sort_index; arg = op->args[i]; - arg_ct = &def->args_ct[i]; + arg_ct = &args_ct[i]; ts = arg_temp(arg); if (ts->val_type == TEMP_VAL_CONST @@ -5137,7 +5134,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) * register and move it. */ if (temp_readonly(ts) || !IS_DEAD_ARG(i) - || def->args_ct[arg_ct->alias_index].newreg) { + || args_ct[arg_ct->alias_index].newreg) { allocate_new_reg = true; } else if (ts->val_type == TEMP_VAL_REG) { /* @@ -5322,10 +5319,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } /* satisfy the output constraints */ - for(k = 0; k < nb_oargs; k++) { - i = def->args_ct[k].sort_index; + for (k = 0; k < nb_oargs; k++) { + i = args_ct[k].sort_index; arg = op->args[i]; - arg_ct = &def->args_ct[i]; + arg_ct = &args_ct[i]; ts = arg_temp(arg); /* ENV should not be modified. */ @@ -5465,8 +5462,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) /* Allocate the output register now. */ if (ots->val_type != TEMP_VAL_REG) { TCGRegSet allocated_regs = s->reserved_regs; - TCGRegSet dup_out_regs = - tcg_op_defs[INDEX_op_dup_vec].args_ct[0].regs; + TCGRegSet dup_out_regs = opcode_args_ct(op)[0].regs; TCGReg oreg; /* Make sure to not spill the input registers. */ From ed1a653bad46752c4c4ceb2ada774dff7cd79e81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Dec 2024 12:53:22 -0800 Subject: [PATCH 1104/2892] tcg: Constify tcg_op_defs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we're no longer assigning to TCGOpDef.args_ct, we can make the array constant. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 2 +- tcg/tcg-common.c | 2 +- tcg/tcg.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index e28894c57b..a4630e44bc 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -716,7 +716,7 @@ typedef struct TCGOpDef { uint8_t flags; } TCGOpDef; -extern TCGOpDef tcg_op_defs[]; +extern const TCGOpDef tcg_op_defs[]; extern const size_t tcg_op_defs_max; /* diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index 0f30e5b3ec..e98b3e5fdd 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -26,7 +26,7 @@ #include "tcg/tcg.h" #include "tcg-has.h" -TCGOpDef tcg_op_defs[] = { +const TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg/tcg-opc.h" diff --git a/tcg/tcg.c b/tcg/tcg.c index df7c4dab88..32975fe298 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3364,7 +3364,7 @@ static void process_constraint_sets(void) static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) { - TCGOpDef *def = &tcg_op_defs[op->opc]; + const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGConstraintSetIndex con_set; if (def->nb_iargs + def->nb_oargs == 0) { From b277cdd20baf6d8579aa5a874f713f9e19833c9f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 11:01:53 -0800 Subject: [PATCH 1105/2892] tcg: Validate op supported in opcode_args_ct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should have checked that the op is supported before emitting it. The backend cannot be expected to have a constraint set for unsupported ops. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tcg/tcg.c b/tcg/tcg.c index 32975fe298..b9119a1ec7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3367,6 +3367,10 @@ static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGConstraintSetIndex con_set; +#ifdef CONFIG_DEBUG_TCG + assert(tcg_op_supported(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op))); +#endif + if (def->nb_iargs + def->nb_oargs == 0) { return NULL; } From 07ffd5b219681d9f6eb6bef9f66152bb88b23106 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 11:08:44 -0800 Subject: [PATCH 1106/2892] tcg: Add TCG_OPF_NOT_PRESENT to opcodes without inputs or outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The br, mb, goto_tb and exit_tb opcodes do not have register operands, only constants, flags, or labels. Remove the special case in opcode_args_ct by including TCG_OPF_NOT_PRESENT in the flags for these opcodes. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 8 ++++---- tcg/tcg.c | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 14aff6e7f9..724e7a9de8 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -33,7 +33,7 @@ DEF(set_label, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) /* variable number of parameters */ DEF(call, 0, 0, 3, TCG_OPF_CALL_CLOBBER | TCG_OPF_NOT_PRESENT) -DEF(br, 0, 0, 1, TCG_OPF_BB_END) +DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) #define IMPL(X) (__builtin_constant_p(X) && (X) <= 0 ? TCG_OPF_NOT_PRESENT : 0) #if TCG_TARGET_REG_BITS == 32 @@ -42,7 +42,7 @@ DEF(br, 0, 0, 1, TCG_OPF_BB_END) # define IMPL64 TCG_OPF_64BIT #endif -DEF(mb, 0, 0, 1, 0) +DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(setcond_i32, 1, 2, 1, 0) @@ -193,8 +193,8 @@ DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64)) /* There are tcg_ctx->insn_start_words here, not just one. */ DEF(insn_start, 0, 0, DATA64_ARGS, TCG_OPF_NOT_PRESENT) -DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) -DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) +DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) +DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tcg.c b/tcg/tcg.c index b9119a1ec7..e9ecdc5eeb 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3371,9 +3371,6 @@ static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) assert(tcg_op_supported(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op))); #endif - if (def->nb_iargs + def->nb_oargs == 0) { - return NULL; - } if (def->flags & TCG_OPF_NOT_PRESENT) { return empty_cts; } From 6323b3639cfd362abcd1f29c52455ae550d5ac99 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 11:19:19 -0800 Subject: [PATCH 1107/2892] tcg: Pass type and flags to tcg_target_op_def MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the backend to make constraint choices based on more parameters. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 3 ++- tcg/arm/tcg-target.c.inc | 3 ++- tcg/i386/tcg-target.c.inc | 3 ++- tcg/loongarch64/tcg-target.c.inc | 3 ++- tcg/mips/tcg-target.c.inc | 3 ++- tcg/ppc/tcg-target.c.inc | 3 ++- tcg/riscv/tcg-target.c.inc | 3 ++- tcg/s390x/tcg-target.c.inc | 3 ++- tcg/sparc64/tcg-target.c.inc | 3 ++- tcg/tcg.c | 4 ++-- tcg/tci/tcg-target.c.inc | 3 ++- 11 files changed, 22 insertions(+), 12 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index d1e08def60..9116f85667 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2962,7 +2962,8 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index b4cd36a9b8..182cac1a8a 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2124,7 +2124,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 813c12ca0e..64826c7419 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3627,7 +3627,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 686b94ccda..2f0d4d01ff 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -2191,7 +2191,8 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, g_assert_not_reached(); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 199bd97c0e..b89b279a0e 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2151,7 +2151,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 3ce4fa1db6..1e7b8a204c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -4140,7 +4140,8 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, va_end(va); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 51cd7e7586..432a2fe26f 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2583,7 +2583,8 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 98925b1d5d..e09a726ecf 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3207,7 +3207,8 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, va_end(va); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 1201607722..48de490120 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1532,7 +1532,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: diff --git a/tcg/tcg.c b/tcg/tcg.c index e9ecdc5eeb..83356d932d 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -866,7 +866,7 @@ typedef enum { #include "tcg-target-con-set.h" } TCGConstraintSetIndex; -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); +static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode, TCGType, unsigned); #undef C_O0_I1 #undef C_O0_I2 @@ -3375,7 +3375,7 @@ static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) return empty_cts; } - con_set = tcg_target_op_def(op->opc); + con_set = tcg_target_op_def(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op)); tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); /* The constraint arguments must match TCGOpcode arguments. */ diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 74b649c902..662acbdcb6 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -36,7 +36,8 @@ #endif #define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { case INDEX_op_goto_ptr: From 4e350091a2b87070967c85f9caeefeb85c4d2e8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 28 Dec 2024 14:35:59 -0800 Subject: [PATCH 1108/2892] tcg: Add TCGType argument to tcg_out_op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass TCGOp.type to the output function. For aarch64 and tci, use this instead of testing TCG_OPF_64BIT. For s390x, use this instead of testing INDEX_op_deposit_i64. For i386, use this to initialize rexw. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 6 +----- tcg/arm/tcg-target.c.inc | 2 +- tcg/i386/tcg-target.c.inc | 10 +++++----- tcg/loongarch64/tcg-target.c.inc | 2 +- tcg/mips/tcg-target.c.inc | 2 +- tcg/ppc/tcg-target.c.inc | 2 +- tcg/riscv/tcg-target.c.inc | 2 +- tcg/s390x/tcg-target.c.inc | 7 +++---- tcg/sparc64/tcg-target.c.inc | 2 +- tcg/tcg.c | 4 ++-- tcg/tci/tcg-target.c.inc | 4 ++-- 11 files changed, 19 insertions(+), 24 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 9116f85667..ede6f47235 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2115,14 +2115,10 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - /* 99% of the time, we can signal the use of extension registers - by looking to see if the opcode handles 64-bit data. */ - TCGType ext = (tcg_op_defs[opc].flags & TCG_OPF_64BIT) != 0; - /* Hoist the loads of the most common arguments. */ TCGArg a0 = args[0]; TCGArg a1 = args[1]; diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 182cac1a8a..9cfb733a14 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1805,7 +1805,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 64826c7419..8d1057cdb3 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2612,17 +2612,16 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* no need to flush icache explicitly */ } -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int c, const_a2, vexop, rexw = 0; + int c, const_a2, vexop, rexw; #if TCG_TARGET_REG_BITS == 64 # define OP_32_64(x) \ case glue(glue(INDEX_op_, x), _i64): \ - rexw = P_REXW; /* FALLTHRU */ \ case glue(glue(INDEX_op_, x), _i32) #else # define OP_32_64(x) \ @@ -2634,6 +2633,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, a1 = args[1]; a2 = args[2]; const_a2 = const_args[2]; + rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; switch (opc) { case INDEX_op_goto_ptr: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 2f0d4d01ff..3dff29facb 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1278,7 +1278,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index b89b279a0e..b31b8f0007 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1678,7 +1678,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* Always indirect, nothing to do */ } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 1e7b8a204c..9205ac99e9 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2940,7 +2940,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 432a2fe26f..e381ba4e77 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1960,7 +1960,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index e09a726ecf..fc7d986e68 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2117,9 +2117,9 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, case glue(glue(INDEX_op_,x),_i32): \ case glue(glue(INDEX_op_,x),_i64) -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) { S390Opcode op, op2; TCGArg a0, a1, a2; @@ -2713,7 +2713,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, /* Since we can't support "0Z" as a constraint, we allow a1 in any register. Fix things up as if a matching constraint. */ if (a0 != a1) { - TCGType type = (opc == INDEX_op_deposit_i64); if (a0 == a2) { tcg_out_mov(s, type, TCG_TMP0, a2); a2 = TCG_TMP0; diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 48de490120..afc778fae7 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1288,7 +1288,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, { } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { diff --git a/tcg/tcg.c b/tcg/tcg.c index 83356d932d..9b54a8bec8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -134,7 +134,7 @@ static void tcg_out_addi_ptr(TCGContext *s, TCGReg, TCGReg, tcg_target_long); static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg); static void tcg_out_goto_tb(TCGContext *s, int which); -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]); #if TCG_TARGET_MAYBE_vec @@ -5423,7 +5423,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) tcg_out_vec_op(s, op->opc, TCGOP_TYPE(op) - TCG_TYPE_V64, TCGOP_VECE(op), new_args, const_args); } else { - tcg_out_op(s, op->opc, new_args, const_args); + tcg_out_op(s, op->opc, TCGOP_TYPE(op), new_args, const_args); } break; } diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 662acbdcb6..88cecbd62f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -708,7 +708,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* Always indirect, nothing to do */ } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { @@ -790,7 +790,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, CASE_32_64(sextract) /* Optional (TCG_TARGET_HAS_sextract_*). */ { TCGArg pos = args[2], len = args[3]; - TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32; + TCGArg max = type == TCG_TYPE_I32 ? 32 : 64; tcg_debug_assert(pos < max); tcg_debug_assert(pos + len <= max); From 931bac71fc1f877168d14e718a503c271713ed6e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 11:37:32 -0800 Subject: [PATCH 1109/2892] tcg: Remove TCG_OPF_64BIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag is no longer used. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 22 +++++++++++----------- include/tcg/tcg.h | 2 -- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 724e7a9de8..eb17a21f21 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -37,9 +37,9 @@ DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) #define IMPL(X) (__builtin_constant_p(X) && (X) <= 0 ? TCG_OPF_NOT_PRESENT : 0) #if TCG_TARGET_REG_BITS == 32 -# define IMPL64 TCG_OPF_64BIT | TCG_OPF_NOT_PRESENT +# define IMPL64 TCG_OPF_NOT_PRESENT #else -# define IMPL64 TCG_OPF_64BIT +# define IMPL64 0 #endif DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) @@ -110,7 +110,7 @@ DEF(clz_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_clz_i32)) DEF(ctz_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_ctz_i32)) DEF(ctpop_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ctpop_i32)) -DEF(mov_i64, 1, 1, 0, TCG_OPF_64BIT | TCG_OPF_NOT_PRESENT) +DEF(mov_i64, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(setcond_i64, 1, 2, 1, IMPL64) DEF(negsetcond_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_negsetcond_i64)) DEF(movcond_i64, 1, 4, 1, IMPL64) @@ -206,18 +206,18 @@ DEF(qemu_ld_a32_i32, 1, 1, 1, DEF(qemu_st_a32_i32, 0, 1 + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_ld_a32_i64, DATA64_ARGS, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_st_a32_i64, 0, DATA64_ARGS + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_ld_a64_i32, 1, DATA64_ARGS, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_st_a64_i32, 0, 1 + DATA64_ARGS, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_ld_a64_i64, DATA64_ARGS, DATA64_ARGS, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_st_a64_i64, 0, DATA64_ARGS + DATA64_ARGS, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Only used by i386 to cope with stupid register constraints. */ DEF(qemu_st8_a32_i32, 0, 1 + 1, 1, @@ -229,16 +229,16 @@ DEF(qemu_st8_a64_i32, 0, 1 + DATA64_ARGS, 1, /* Only for 64-bit hosts at the moment. */ DEF(qemu_ld_a32_i128, 2, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) DEF(qemu_ld_a64_i128, 2, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) DEF(qemu_st_a32_i128, 0, 3, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) DEF(qemu_st_a64_i128, 0, 3, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) /* Host vector support. */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index a4630e44bc..e7ddf979f6 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -699,8 +699,6 @@ enum { /* Instruction has side effects: it cannot be removed if its outputs are not used, and might trigger exceptions. */ TCG_OPF_SIDE_EFFECTS = 0x08, - /* Instruction operands are 64-bits (otherwise 32-bits). */ - TCG_OPF_64BIT = 0x10, /* Instruction is optional and not implemented by the host, or insn is generic and should not be implemented by the host. */ TCG_OPF_NOT_PRESENT = 0x20, From 76187b4f57eb3931352e7f3e1c86d8d49545e396 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 11:44:57 -0800 Subject: [PATCH 1110/2892] tcg: Drop implementation checks from tcg-opc.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we use a functional interface to query whether the opcode is supported, we can drop the TCG_OPF_NOT_PRESENT bit mapping from TCG_TARGET_HAS_foo in tcg-opc.h Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 306 +++++++++++++++++++----------------------- 1 file changed, 141 insertions(+), 165 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index eb17a21f21..559f5971e6 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -35,18 +35,11 @@ DEF(call, 0, 0, 3, TCG_OPF_CALL_CLOBBER | TCG_OPF_NOT_PRESENT) DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) -#define IMPL(X) (__builtin_constant_p(X) && (X) <= 0 ? TCG_OPF_NOT_PRESENT : 0) -#if TCG_TARGET_REG_BITS == 32 -# define IMPL64 TCG_OPF_NOT_PRESENT -#else -# define IMPL64 0 -#endif - DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(setcond_i32, 1, 2, 1, 0) -DEF(negsetcond_i32, 1, 2, 1, IMPL(TCG_TARGET_HAS_negsetcond_i32)) +DEF(negsetcond_i32, 1, 2, 1, 0) DEF(movcond_i32, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i32, 1, 1, 1, 0) @@ -61,12 +54,12 @@ DEF(st_i32, 0, 2, 1, 0) DEF(add_i32, 1, 2, 0, 0) DEF(sub_i32, 1, 2, 0, 0) DEF(mul_i32, 1, 2, 0, 0) -DEF(div_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_div_i32)) -DEF(divu_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_div_i32)) -DEF(rem_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rem_i32)) -DEF(remu_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rem_i32)) -DEF(div2_i32, 2, 3, 0, IMPL(TCG_TARGET_HAS_div2_i32)) -DEF(divu2_i32, 2, 3, 0, IMPL(TCG_TARGET_HAS_div2_i32)) +DEF(div_i32, 1, 2, 0, 0) +DEF(divu_i32, 1, 2, 0, 0) +DEF(rem_i32, 1, 2, 0, 0) +DEF(remu_i32, 1, 2, 0, 0) +DEF(div2_i32, 2, 3, 0, 0) +DEF(divu2_i32, 2, 3, 0, 0) DEF(and_i32, 1, 2, 0, 0) DEF(or_i32, 1, 2, 0, 0) DEF(xor_i32, 1, 2, 0, 0) @@ -74,119 +67,114 @@ DEF(xor_i32, 1, 2, 0, 0) DEF(shl_i32, 1, 2, 0, 0) DEF(shr_i32, 1, 2, 0, 0) DEF(sar_i32, 1, 2, 0, 0) -DEF(rotl_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rot_i32)) -DEF(rotr_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rot_i32)) -DEF(deposit_i32, 1, 2, 2, IMPL(TCG_TARGET_HAS_deposit_i32)) -DEF(extract_i32, 1, 1, 2, IMPL(TCG_TARGET_HAS_extract_i32)) -DEF(sextract_i32, 1, 1, 2, IMPL(TCG_TARGET_HAS_sextract_i32)) -DEF(extract2_i32, 1, 2, 1, IMPL(TCG_TARGET_HAS_extract2_i32)) +DEF(rotl_i32, 1, 2, 0, 0) +DEF(rotr_i32, 1, 2, 0, 0) +DEF(deposit_i32, 1, 2, 2, 0) +DEF(extract_i32, 1, 1, 2, 0) +DEF(sextract_i32, 1, 1, 2, 0) +DEF(extract2_i32, 1, 2, 1, 0) DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) -DEF(add2_i32, 2, 4, 0, IMPL(TCG_TARGET_HAS_add2_i32)) -DEF(sub2_i32, 2, 4, 0, IMPL(TCG_TARGET_HAS_sub2_i32)) -DEF(mulu2_i32, 2, 2, 0, IMPL(TCG_TARGET_HAS_mulu2_i32)) -DEF(muls2_i32, 2, 2, 0, IMPL(TCG_TARGET_HAS_muls2_i32)) -DEF(muluh_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_muluh_i32)) -DEF(mulsh_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_mulsh_i32)) -DEF(brcond2_i32, 0, 4, 2, - TCG_OPF_BB_END | TCG_OPF_COND_BRANCH | IMPL(TCG_TARGET_REG_BITS == 32)) -DEF(setcond2_i32, 1, 4, 1, IMPL(TCG_TARGET_REG_BITS == 32)) +DEF(add2_i32, 2, 4, 0, 0) +DEF(sub2_i32, 2, 4, 0, 0) +DEF(mulu2_i32, 2, 2, 0, 0) +DEF(muls2_i32, 2, 2, 0, 0) +DEF(muluh_i32, 1, 2, 0, 0) +DEF(mulsh_i32, 1, 2, 0, 0) +DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) +DEF(setcond2_i32, 1, 4, 1, 0) -DEF(ext8s_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext8s_i32)) -DEF(ext16s_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext16s_i32)) -DEF(ext8u_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext8u_i32)) -DEF(ext16u_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext16u_i32)) -DEF(bswap16_i32, 1, 1, 1, IMPL(TCG_TARGET_HAS_bswap16_i32)) -DEF(bswap32_i32, 1, 1, 1, IMPL(TCG_TARGET_HAS_bswap32_i32)) -DEF(not_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_not_i32)) +DEF(ext8s_i32, 1, 1, 0, 0) +DEF(ext16s_i32, 1, 1, 0, 0) +DEF(ext8u_i32, 1, 1, 0, 0) +DEF(ext16u_i32, 1, 1, 0, 0) +DEF(bswap16_i32, 1, 1, 1, 0) +DEF(bswap32_i32, 1, 1, 1, 0) +DEF(not_i32, 1, 1, 0, 0) DEF(neg_i32, 1, 1, 0, 0) -DEF(andc_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_andc_i32)) -DEF(orc_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_orc_i32)) -DEF(eqv_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_eqv_i32)) -DEF(nand_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_nand_i32)) -DEF(nor_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_nor_i32)) -DEF(clz_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_clz_i32)) -DEF(ctz_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_ctz_i32)) -DEF(ctpop_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ctpop_i32)) +DEF(andc_i32, 1, 2, 0, 0) +DEF(orc_i32, 1, 2, 0, 0) +DEF(eqv_i32, 1, 2, 0, 0) +DEF(nand_i32, 1, 2, 0, 0) +DEF(nor_i32, 1, 2, 0, 0) +DEF(clz_i32, 1, 2, 0, 0) +DEF(ctz_i32, 1, 2, 0, 0) +DEF(ctpop_i32, 1, 1, 0, 0) DEF(mov_i64, 1, 1, 0, TCG_OPF_NOT_PRESENT) -DEF(setcond_i64, 1, 2, 1, IMPL64) -DEF(negsetcond_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_negsetcond_i64)) -DEF(movcond_i64, 1, 4, 1, IMPL64) +DEF(setcond_i64, 1, 2, 1, 0) +DEF(negsetcond_i64, 1, 2, 1, 0) +DEF(movcond_i64, 1, 4, 1, 0) /* load/store */ -DEF(ld8u_i64, 1, 1, 1, IMPL64) -DEF(ld8s_i64, 1, 1, 1, IMPL64) -DEF(ld16u_i64, 1, 1, 1, IMPL64) -DEF(ld16s_i64, 1, 1, 1, IMPL64) -DEF(ld32u_i64, 1, 1, 1, IMPL64) -DEF(ld32s_i64, 1, 1, 1, IMPL64) -DEF(ld_i64, 1, 1, 1, IMPL64) -DEF(st8_i64, 0, 2, 1, IMPL64) -DEF(st16_i64, 0, 2, 1, IMPL64) -DEF(st32_i64, 0, 2, 1, IMPL64) -DEF(st_i64, 0, 2, 1, IMPL64) +DEF(ld8u_i64, 1, 1, 1, 0) +DEF(ld8s_i64, 1, 1, 1, 0) +DEF(ld16u_i64, 1, 1, 1, 0) +DEF(ld16s_i64, 1, 1, 1, 0) +DEF(ld32u_i64, 1, 1, 1, 0) +DEF(ld32s_i64, 1, 1, 1, 0) +DEF(ld_i64, 1, 1, 1, 0) +DEF(st8_i64, 0, 2, 1, 0) +DEF(st16_i64, 0, 2, 1, 0) +DEF(st32_i64, 0, 2, 1, 0) +DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(add_i64, 1, 2, 0, IMPL64) -DEF(sub_i64, 1, 2, 0, IMPL64) -DEF(mul_i64, 1, 2, 0, IMPL64) -DEF(div_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div_i64)) -DEF(divu_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div_i64)) -DEF(rem_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rem_i64)) -DEF(remu_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rem_i64)) -DEF(div2_i64, 2, 3, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div2_i64)) -DEF(divu2_i64, 2, 3, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div2_i64)) -DEF(and_i64, 1, 2, 0, IMPL64) -DEF(or_i64, 1, 2, 0, IMPL64) -DEF(xor_i64, 1, 2, 0, IMPL64) +DEF(add_i64, 1, 2, 0, 0) +DEF(sub_i64, 1, 2, 0, 0) +DEF(mul_i64, 1, 2, 0, 0) +DEF(div_i64, 1, 2, 0, 0) +DEF(divu_i64, 1, 2, 0, 0) +DEF(rem_i64, 1, 2, 0, 0) +DEF(remu_i64, 1, 2, 0, 0) +DEF(div2_i64, 2, 3, 0, 0) +DEF(divu2_i64, 2, 3, 0, 0) +DEF(and_i64, 1, 2, 0, 0) +DEF(or_i64, 1, 2, 0, 0) +DEF(xor_i64, 1, 2, 0, 0) /* shifts/rotates */ -DEF(shl_i64, 1, 2, 0, IMPL64) -DEF(shr_i64, 1, 2, 0, IMPL64) -DEF(sar_i64, 1, 2, 0, IMPL64) -DEF(rotl_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64)) -DEF(rotr_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64)) -DEF(deposit_i64, 1, 2, 2, IMPL64 | IMPL(TCG_TARGET_HAS_deposit_i64)) -DEF(extract_i64, 1, 1, 2, IMPL64 | IMPL(TCG_TARGET_HAS_extract_i64)) -DEF(sextract_i64, 1, 1, 2, IMPL64 | IMPL(TCG_TARGET_HAS_sextract_i64)) -DEF(extract2_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_extract2_i64)) +DEF(shl_i64, 1, 2, 0, 0) +DEF(shr_i64, 1, 2, 0, 0) +DEF(sar_i64, 1, 2, 0, 0) +DEF(rotl_i64, 1, 2, 0, 0) +DEF(rotr_i64, 1, 2, 0, 0) +DEF(deposit_i64, 1, 2, 2, 0) +DEF(extract_i64, 1, 1, 2, 0) +DEF(sextract_i64, 1, 1, 2, 0) +DEF(extract2_i64, 1, 2, 1, 0) /* size changing ops */ -DEF(ext_i32_i64, 1, 1, 0, IMPL64) -DEF(extu_i32_i64, 1, 1, 0, IMPL64) -DEF(extrl_i64_i32, 1, 1, 0, - IMPL(TCG_TARGET_HAS_extr_i64_i32) - | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) -DEF(extrh_i64_i32, 1, 1, 0, - IMPL(TCG_TARGET_HAS_extr_i64_i32) - | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) +DEF(ext_i32_i64, 1, 1, 0, 0) +DEF(extu_i32_i64, 1, 1, 0, 0) +DEF(extrl_i64_i32, 1, 1, 0, 0) +DEF(extrh_i64_i32, 1, 1, 0, 0) -DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH | IMPL64) -DEF(ext8s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext8s_i64)) -DEF(ext16s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext16s_i64)) -DEF(ext32s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext32s_i64)) -DEF(ext8u_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext8u_i64)) -DEF(ext16u_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext16u_i64)) -DEF(ext32u_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext32u_i64)) -DEF(bswap16_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap16_i64)) -DEF(bswap32_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap32_i64)) -DEF(bswap64_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap64_i64)) -DEF(not_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_not_i64)) -DEF(neg_i64, 1, 1, 0, IMPL64) -DEF(andc_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_andc_i64)) -DEF(orc_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_orc_i64)) -DEF(eqv_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_eqv_i64)) -DEF(nand_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_nand_i64)) -DEF(nor_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_nor_i64)) -DEF(clz_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_clz_i64)) -DEF(ctz_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ctz_i64)) -DEF(ctpop_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ctpop_i64)) +DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) +DEF(ext8s_i64, 1, 1, 0, 0) +DEF(ext16s_i64, 1, 1, 0, 0) +DEF(ext32s_i64, 1, 1, 0, 0) +DEF(ext8u_i64, 1, 1, 0, 0) +DEF(ext16u_i64, 1, 1, 0, 0) +DEF(ext32u_i64, 1, 1, 0, 0) +DEF(bswap16_i64, 1, 1, 1, 0) +DEF(bswap32_i64, 1, 1, 1, 0) +DEF(bswap64_i64, 1, 1, 1, 0) +DEF(not_i64, 1, 1, 0, 0) +DEF(neg_i64, 1, 1, 0, 0) +DEF(andc_i64, 1, 2, 0, 0) +DEF(orc_i64, 1, 2, 0, 0) +DEF(eqv_i64, 1, 2, 0, 0) +DEF(nand_i64, 1, 2, 0, 0) +DEF(nor_i64, 1, 2, 0, 0) +DEF(clz_i64, 1, 2, 0, 0) +DEF(ctz_i64, 1, 2, 0, 0) +DEF(ctpop_i64, 1, 1, 0, 0) -DEF(add2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_add2_i64)) -DEF(sub2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_sub2_i64)) -DEF(mulu2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulu2_i64)) -DEF(muls2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muls2_i64)) -DEF(muluh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muluh_i64)) -DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64)) +DEF(add2_i64, 2, 4, 0, 0) +DEF(sub2_i64, 2, 4, 0, 0) +DEF(mulu2_i64, 2, 2, 0, 0) +DEF(muls2_i64, 2, 2, 0, 0) +DEF(muluh_i64, 1, 2, 0, 0) +DEF(mulsh_i64, 1, 2, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) @@ -221,34 +209,24 @@ DEF(qemu_st_a64_i64, 0, DATA64_ARGS + DATA64_ARGS, 1, /* Only used by i386 to cope with stupid register constraints. */ DEF(qemu_st8_a32_i32, 0, 1 + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | - IMPL(TCG_TARGET_HAS_qemu_st8_i32)) + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_st8_a64_i32, 0, 1 + DATA64_ARGS, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | - IMPL(TCG_TARGET_HAS_qemu_st8_i32)) + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Only for 64-bit hosts at the moment. */ -DEF(qemu_ld_a32_i128, 2, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | - IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) -DEF(qemu_ld_a64_i128, 2, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | - IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) -DEF(qemu_st_a32_i128, 0, 3, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | - IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) -DEF(qemu_st_a64_i128, 0, 3, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | - IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) +DEF(qemu_ld_a32_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld_a64_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st_a32_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st_a64_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Host vector support. */ -#define IMPLVEC TCG_OPF_VECTOR | IMPL(TCG_TARGET_MAYBE_vec) +#define IMPLVEC TCG_OPF_VECTOR DEF(mov_vec, 1, 1, 0, TCG_OPF_VECTOR | TCG_OPF_NOT_PRESENT) DEF(dup_vec, 1, 1, 0, IMPLVEC) -DEF(dup2_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_REG_BITS == 32)) +DEF(dup2_vec, 1, 2, 0, IMPLVEC) DEF(ld_vec, 1, 1, 1, IMPLVEC) DEF(st_vec, 0, 2, 1, IMPLVEC) @@ -256,55 +234,53 @@ DEF(dupm_vec, 1, 1, 1, IMPLVEC) DEF(add_vec, 1, 2, 0, IMPLVEC) DEF(sub_vec, 1, 2, 0, IMPLVEC) -DEF(mul_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_mul_vec)) -DEF(neg_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_neg_vec)) -DEF(abs_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_abs_vec)) -DEF(ssadd_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(usadd_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(sssub_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(ussub_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(smin_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) -DEF(umin_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) -DEF(smax_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) -DEF(umax_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) +DEF(mul_vec, 1, 2, 0, IMPLVEC) +DEF(neg_vec, 1, 1, 0, IMPLVEC) +DEF(abs_vec, 1, 1, 0, IMPLVEC) +DEF(ssadd_vec, 1, 2, 0, IMPLVEC) +DEF(usadd_vec, 1, 2, 0, IMPLVEC) +DEF(sssub_vec, 1, 2, 0, IMPLVEC) +DEF(ussub_vec, 1, 2, 0, IMPLVEC) +DEF(smin_vec, 1, 2, 0, IMPLVEC) +DEF(umin_vec, 1, 2, 0, IMPLVEC) +DEF(smax_vec, 1, 2, 0, IMPLVEC) +DEF(umax_vec, 1, 2, 0, IMPLVEC) DEF(and_vec, 1, 2, 0, IMPLVEC) DEF(or_vec, 1, 2, 0, IMPLVEC) DEF(xor_vec, 1, 2, 0, IMPLVEC) -DEF(andc_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_andc_vec)) -DEF(orc_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_orc_vec)) -DEF(nand_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_nand_vec)) -DEF(nor_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_nor_vec)) -DEF(eqv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_eqv_vec)) -DEF(not_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_not_vec)) +DEF(andc_vec, 1, 2, 0, IMPLVEC) +DEF(orc_vec, 1, 2, 0, IMPLVEC) +DEF(nand_vec, 1, 2, 0, IMPLVEC) +DEF(nor_vec, 1, 2, 0, IMPLVEC) +DEF(eqv_vec, 1, 2, 0, IMPLVEC) +DEF(not_vec, 1, 1, 0, IMPLVEC) -DEF(shli_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) -DEF(shri_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) -DEF(sari_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) -DEF(rotli_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_roti_vec)) +DEF(shli_vec, 1, 1, 1, IMPLVEC) +DEF(shri_vec, 1, 1, 1, IMPLVEC) +DEF(sari_vec, 1, 1, 1, IMPLVEC) +DEF(rotli_vec, 1, 1, 1, IMPLVEC) -DEF(shls_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shs_vec)) -DEF(shrs_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shs_vec)) -DEF(sars_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shs_vec)) -DEF(rotls_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_rots_vec)) +DEF(shls_vec, 1, 2, 0, IMPLVEC) +DEF(shrs_vec, 1, 2, 0, IMPLVEC) +DEF(sars_vec, 1, 2, 0, IMPLVEC) +DEF(rotls_vec, 1, 2, 0, IMPLVEC) -DEF(shlv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shv_vec)) -DEF(shrv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shv_vec)) -DEF(sarv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shv_vec)) -DEF(rotlv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_rotv_vec)) -DEF(rotrv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_rotv_vec)) +DEF(shlv_vec, 1, 2, 0, IMPLVEC) +DEF(shrv_vec, 1, 2, 0, IMPLVEC) +DEF(sarv_vec, 1, 2, 0, IMPLVEC) +DEF(rotlv_vec, 1, 2, 0, IMPLVEC) +DEF(rotrv_vec, 1, 2, 0, IMPLVEC) DEF(cmp_vec, 1, 2, 1, IMPLVEC) -DEF(bitsel_vec, 1, 3, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_bitsel_vec)) -DEF(cmpsel_vec, 1, 4, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_cmpsel_vec)) +DEF(bitsel_vec, 1, 3, 0, IMPLVEC) +DEF(cmpsel_vec, 1, 4, 1, IMPLVEC) DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT) #include "tcg-target-opc.h.inc" #undef DATA64_ARGS -#undef IMPL -#undef IMPL64 #undef IMPLVEC #undef DEF From f9af66f6dc0fb5f6c570e99df318d997d5510f7f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 11:48:05 -0800 Subject: [PATCH 1111/2892] tcg: Replace IMPLVEC with TCG_OPF_VECTOR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now a direct replacement. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 89 +++++++++++++++----------------- tcg/aarch64/tcg-target-opc.h.inc | 4 +- tcg/arm/tcg-target-opc.h.inc | 6 +-- tcg/i386/tcg-target-opc.h.inc | 22 ++++---- tcg/ppc/tcg-target-opc.h.inc | 12 ++--- tcg/s390x/tcg-target-opc.h.inc | 6 +-- 6 files changed, 68 insertions(+), 71 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 559f5971e6..9383e295f4 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -221,66 +221,63 @@ DEF(qemu_st_a64_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Host vector support. */ -#define IMPLVEC TCG_OPF_VECTOR - DEF(mov_vec, 1, 1, 0, TCG_OPF_VECTOR | TCG_OPF_NOT_PRESENT) -DEF(dup_vec, 1, 1, 0, IMPLVEC) -DEF(dup2_vec, 1, 2, 0, IMPLVEC) +DEF(dup_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(dup2_vec, 1, 2, 0, TCG_OPF_VECTOR) -DEF(ld_vec, 1, 1, 1, IMPLVEC) -DEF(st_vec, 0, 2, 1, IMPLVEC) -DEF(dupm_vec, 1, 1, 1, IMPLVEC) +DEF(ld_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(st_vec, 0, 2, 1, TCG_OPF_VECTOR) +DEF(dupm_vec, 1, 1, 1, TCG_OPF_VECTOR) -DEF(add_vec, 1, 2, 0, IMPLVEC) -DEF(sub_vec, 1, 2, 0, IMPLVEC) -DEF(mul_vec, 1, 2, 0, IMPLVEC) -DEF(neg_vec, 1, 1, 0, IMPLVEC) -DEF(abs_vec, 1, 1, 0, IMPLVEC) -DEF(ssadd_vec, 1, 2, 0, IMPLVEC) -DEF(usadd_vec, 1, 2, 0, IMPLVEC) -DEF(sssub_vec, 1, 2, 0, IMPLVEC) -DEF(ussub_vec, 1, 2, 0, IMPLVEC) -DEF(smin_vec, 1, 2, 0, IMPLVEC) -DEF(umin_vec, 1, 2, 0, IMPLVEC) -DEF(smax_vec, 1, 2, 0, IMPLVEC) -DEF(umax_vec, 1, 2, 0, IMPLVEC) +DEF(add_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sub_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(mul_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(neg_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(abs_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(ssadd_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(usadd_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sssub_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ussub_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(smin_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(umin_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(smax_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(umax_vec, 1, 2, 0, TCG_OPF_VECTOR) -DEF(and_vec, 1, 2, 0, IMPLVEC) -DEF(or_vec, 1, 2, 0, IMPLVEC) -DEF(xor_vec, 1, 2, 0, IMPLVEC) -DEF(andc_vec, 1, 2, 0, IMPLVEC) -DEF(orc_vec, 1, 2, 0, IMPLVEC) -DEF(nand_vec, 1, 2, 0, IMPLVEC) -DEF(nor_vec, 1, 2, 0, IMPLVEC) -DEF(eqv_vec, 1, 2, 0, IMPLVEC) -DEF(not_vec, 1, 1, 0, IMPLVEC) +DEF(and_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(or_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(xor_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(andc_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(orc_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(nand_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(nor_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(eqv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(not_vec, 1, 1, 0, TCG_OPF_VECTOR) -DEF(shli_vec, 1, 1, 1, IMPLVEC) -DEF(shri_vec, 1, 1, 1, IMPLVEC) -DEF(sari_vec, 1, 1, 1, IMPLVEC) -DEF(rotli_vec, 1, 1, 1, IMPLVEC) +DEF(shli_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(shri_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(sari_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(rotli_vec, 1, 1, 1, TCG_OPF_VECTOR) -DEF(shls_vec, 1, 2, 0, IMPLVEC) -DEF(shrs_vec, 1, 2, 0, IMPLVEC) -DEF(sars_vec, 1, 2, 0, IMPLVEC) -DEF(rotls_vec, 1, 2, 0, IMPLVEC) +DEF(shls_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(shrs_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sars_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(rotls_vec, 1, 2, 0, TCG_OPF_VECTOR) -DEF(shlv_vec, 1, 2, 0, IMPLVEC) -DEF(shrv_vec, 1, 2, 0, IMPLVEC) -DEF(sarv_vec, 1, 2, 0, IMPLVEC) -DEF(rotlv_vec, 1, 2, 0, IMPLVEC) -DEF(rotrv_vec, 1, 2, 0, IMPLVEC) +DEF(shlv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(shrv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sarv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(rotlv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(rotrv_vec, 1, 2, 0, TCG_OPF_VECTOR) -DEF(cmp_vec, 1, 2, 1, IMPLVEC) +DEF(cmp_vec, 1, 2, 1, TCG_OPF_VECTOR) -DEF(bitsel_vec, 1, 3, 0, IMPLVEC) -DEF(cmpsel_vec, 1, 4, 1, IMPLVEC) +DEF(bitsel_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(cmpsel_vec, 1, 4, 1, TCG_OPF_VECTOR) DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT) #include "tcg-target-opc.h.inc" #undef DATA64_ARGS -#undef IMPLVEC #undef DEF diff --git a/tcg/aarch64/tcg-target-opc.h.inc b/tcg/aarch64/tcg-target-opc.h.inc index bce30accd9..5382315c41 100644 --- a/tcg/aarch64/tcg-target-opc.h.inc +++ b/tcg/aarch64/tcg-target-opc.h.inc @@ -11,5 +11,5 @@ * consider these to be UNSPEC with names. */ -DEF(aa64_sshl_vec, 1, 2, 0, IMPLVEC) -DEF(aa64_sli_vec, 1, 2, 1, IMPLVEC) +DEF(aa64_sshl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(aa64_sli_vec, 1, 2, 1, TCG_OPF_VECTOR) diff --git a/tcg/arm/tcg-target-opc.h.inc b/tcg/arm/tcg-target-opc.h.inc index d38af9a808..70394e0282 100644 --- a/tcg/arm/tcg-target-opc.h.inc +++ b/tcg/arm/tcg-target-opc.h.inc @@ -11,6 +11,6 @@ * consider these to be UNSPEC with names. */ -DEF(arm_sli_vec, 1, 2, 1, IMPLVEC) -DEF(arm_sshl_vec, 1, 2, 0, IMPLVEC) -DEF(arm_ushl_vec, 1, 2, 0, IMPLVEC) +DEF(arm_sli_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(arm_sshl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(arm_ushl_vec, 1, 2, 0, TCG_OPF_VECTOR) diff --git a/tcg/i386/tcg-target-opc.h.inc b/tcg/i386/tcg-target-opc.h.inc index 4ffc084bda..8cc0dbaeaf 100644 --- a/tcg/i386/tcg-target-opc.h.inc +++ b/tcg/i386/tcg-target-opc.h.inc @@ -24,14 +24,14 @@ * consider these to be UNSPEC with names. */ -DEF(x86_shufps_vec, 1, 2, 1, IMPLVEC) -DEF(x86_blend_vec, 1, 2, 1, IMPLVEC) -DEF(x86_packss_vec, 1, 2, 0, IMPLVEC) -DEF(x86_packus_vec, 1, 2, 0, IMPLVEC) -DEF(x86_psrldq_vec, 1, 1, 1, IMPLVEC) -DEF(x86_vperm2i128_vec, 1, 2, 1, IMPLVEC) -DEF(x86_punpckl_vec, 1, 2, 0, IMPLVEC) -DEF(x86_punpckh_vec, 1, 2, 0, IMPLVEC) -DEF(x86_vpshldi_vec, 1, 2, 1, IMPLVEC) -DEF(x86_vpshldv_vec, 1, 3, 0, IMPLVEC) -DEF(x86_vpshrdv_vec, 1, 3, 0, IMPLVEC) +DEF(x86_shufps_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_blend_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_packss_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_packus_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_psrldq_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(x86_vperm2i128_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_punpckl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_punpckh_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_vpshldi_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_vpshldv_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(x86_vpshrdv_vec, 1, 3, 0, TCG_OPF_VECTOR) diff --git a/tcg/ppc/tcg-target-opc.h.inc b/tcg/ppc/tcg-target-opc.h.inc index db514403c3..c3635831b5 100644 --- a/tcg/ppc/tcg-target-opc.h.inc +++ b/tcg/ppc/tcg-target-opc.h.inc @@ -24,9 +24,9 @@ * consider these to be UNSPEC with names. */ -DEF(ppc_mrgh_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_mrgl_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_msum_vec, 1, 3, 0, IMPLVEC) -DEF(ppc_muleu_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_mulou_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_pkum_vec, 1, 2, 0, IMPLVEC) +DEF(ppc_mrgh_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_mrgl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_msum_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(ppc_muleu_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_mulou_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_pkum_vec, 1, 2, 0, TCG_OPF_VECTOR) diff --git a/tcg/s390x/tcg-target-opc.h.inc b/tcg/s390x/tcg-target-opc.h.inc index 0eb2350fb3..61237b39cd 100644 --- a/tcg/s390x/tcg-target-opc.h.inc +++ b/tcg/s390x/tcg-target-opc.h.inc @@ -10,6 +10,6 @@ * emitted by tcg_expand_vec_op. For those familiar with GCC internals, * consider these to be UNSPEC with names. */ -DEF(s390_vuph_vec, 1, 1, 0, IMPLVEC) -DEF(s390_vupl_vec, 1, 1, 0, IMPLVEC) -DEF(s390_vpks_vec, 1, 2, 0, IMPLVEC) +DEF(s390_vuph_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(s390_vupl_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(s390_vpks_vec, 1, 2, 0, TCG_OPF_VECTOR) From 72912ac7365e737d9c9755437345efb47363db26 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 13:34:21 -0800 Subject: [PATCH 1112/2892] tcg/mips: Expand bswap unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We always provide bswap subroutines, whether they are optimized using mips32r2 when available or not. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target-has.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 5dbc63cef6..d3d874ffd1 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -51,6 +51,7 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 @@ -78,7 +79,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_sextract_i32 0 @@ -92,9 +92,9 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_bswap16_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_bswap32_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_bswap64_i64 use_mips32r2_instructions +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_extract_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_sextract_i64 0 From ad76017e51731ef84e98bd25b922fe14e656dd8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Dec 2024 13:06:45 -0800 Subject: [PATCH 1113/2892] tcg/i386: Handle all 8-bit extensions for i686 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we generalize {s}extract_i32, we'll lose the specific register constraints on ext8u and ext8s. It's just as easy to emit a couple of insns instead. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 8d1057cdb3..ed064c38d4 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1329,16 +1329,31 @@ static inline void tcg_out_rolw_8(TCGContext *s, int reg) static void tcg_out_ext8u(TCGContext *s, TCGReg dest, TCGReg src) { - /* movzbl */ - tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); + if (TCG_TARGET_REG_BITS == 32 && src >= 4) { + tcg_out_mov(s, TCG_TYPE_I32, dest, src); + if (dest >= 4) { + tcg_out_modrm(s, OPC_ARITH_EvIz, ARITH_AND, dest); + tcg_out32(s, 0xff); + return; + } + src = dest; + } tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); } static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; - /* movsbl */ - tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); + + if (TCG_TARGET_REG_BITS == 32 && src >= 4) { + tcg_out_mov(s, TCG_TYPE_I32, dest, src); + if (dest >= 4) { + tcg_out_shifti(s, SHIFT_SHL, dest, 24); + tcg_out_shifti(s, SHIFT_SAR, dest, 24); + return; + } + src = dest; + } tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); } From 4bce752c1a4aad3bfba9b6447dd200fade242aed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 18:55:45 -0800 Subject: [PATCH 1114/2892] tcg/i386: Fold the ext{8,16,32}[us] cases into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept byte and word extensions with the extract opcodes. This is preparatory to removing the specialized extracts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target-has.h | 49 +++++++++++++++++++++++++++---- tcg/i386/tcg-target.c.inc | 62 +++++++++++++++++++++++++++++---------- tcg/optimize.c | 8 +++-- tcg/tcg-has.h | 12 +++++--- tcg/tcg-op.c | 12 +++----- 5 files changed, 107 insertions(+), 36 deletions(-) diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 3ea2eab807..ad69f957a7 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -80,7 +80,7 @@ #define TCG_TARGET_HAS_ctpop_i64 have_popcnt #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -130,10 +130,47 @@ (TCG_TARGET_REG_BITS == 32 && (ofs) == 8 && (len) == 8)) #define TCG_TARGET_deposit_i64_valid TCG_TARGET_deposit_i32_valid -/* Check for the possibility of high-byte extraction and, for 64-bit, - zero-extending 32-bit right-shift. */ -#define TCG_TARGET_extract_i32_valid(ofs, len) ((ofs) == 8 && (len) == 8) -#define TCG_TARGET_extract_i64_valid(ofs, len) \ - (((ofs) == 8 && (len) == 8) || ((ofs) + (len)) == 32) +/* + * Check for the possibility of low byte/word extraction, high-byte extraction + * and zero-extending 32-bit right-shift. + * + * We cannot sign-extend from high byte to 64-bits without using the + * REX prefix that explicitly excludes access to the high-byte registers. + */ +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + switch (ofs) { + case 0: + switch (len) { + case 8: + case 16: + return true; + case 32: + return type == TCG_TYPE_I64; + } + return false; + case 8: + return len == 8 && type == TCG_TYPE_I32; + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +static inline bool +tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + switch (ofs) { + case 0: + return len == 8 || len == 16; + case 8: + return len == 8; + } + return false; +} +#define TCG_TARGET_extract_valid tcg_target_extract_valid #endif diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index ed064c38d4..2cac151331 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3035,6 +3035,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_extract_i64: if (a2 + args[3] == 32) { + if (a2 == 0) { + tcg_out_ext32u(s, a0, a1); + break; + } /* This is a 32-bit zero-extending right shift. */ tcg_out_mov(s, TCG_TYPE_I32, a0, a1); tcg_out_shifti(s, SHIFT_SHR, a0, a2); @@ -3042,28 +3046,53 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } /* FALLTHRU */ case INDEX_op_extract_i32: - /* On the off-chance that we can use the high-byte registers. - Otherwise we emit the same ext16 + shift pattern that we - would have gotten from the normal tcg-op.c expansion. */ - tcg_debug_assert(a2 == 8 && args[3] == 8); - if (a1 < 4 && a0 < 8) { - tcg_out_modrm(s, OPC_MOVZBL, a0, a1 + 4); - } else { + if (a2 == 0 && args[3] == 8) { + tcg_out_ext8u(s, a0, a1); + } else if (a2 == 0 && args[3] == 16) { tcg_out_ext16u(s, a0, a1); - tcg_out_shifti(s, SHIFT_SHR, a0, 8); + } else if (a2 == 8 && args[3] == 8) { + /* + * On the off-chance that we can use the high-byte registers. + * Otherwise we emit the same ext16 + shift pattern that we + * would have gotten from the normal tcg-op.c expansion. + */ + if (a1 < 4 && a0 < 8) { + tcg_out_modrm(s, OPC_MOVZBL, a0, a1 + 4); + } else { + tcg_out_ext16u(s, a0, a1); + tcg_out_shifti(s, SHIFT_SHR, a0, 8); + } + } else { + g_assert_not_reached(); + } + break; + + case INDEX_op_sextract_i64: + if (a2 == 0 && args[3] == 8) { + tcg_out_ext8s(s, TCG_TYPE_I64, a0, a1); + } else if (a2 == 0 && args[3] == 16) { + tcg_out_ext16s(s, TCG_TYPE_I64, a0, a1); + } else if (a2 == 0 && args[3] == 32) { + tcg_out_ext32s(s, a0, a1); + } else { + g_assert_not_reached(); } break; case INDEX_op_sextract_i32: - /* We don't implement sextract_i64, as we cannot sign-extend to - 64-bits without using the REX prefix that explicitly excludes - access to the high-byte registers. */ - tcg_debug_assert(a2 == 8 && args[3] == 8); - if (a1 < 4 && a0 < 8) { - tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); - } else { + if (a2 == 0 && args[3] == 8) { + tcg_out_ext8s(s, TCG_TYPE_I32, a0, a1); + } else if (a2 == 0 && args[3] == 16) { tcg_out_ext16s(s, TCG_TYPE_I32, a0, a1); - tcg_out_shifti(s, SHIFT_SAR, a0, 8); + } else if (a2 == 8 && args[3] == 8) { + if (a1 < 4 && a0 < 8) { + tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); + } else { + tcg_out_ext16s(s, TCG_TYPE_I32, a0, a1); + tcg_out_shifti(s, SHIFT_SAR, a0, 8); + } + } else { + g_assert_not_reached(); } break; @@ -3746,6 +3775,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i32: case INDEX_op_extract_i64: case INDEX_op_sextract_i32: + case INDEX_op_sextract_i64: case INDEX_op_ctpop_i32: case INDEX_op_ctpop_i64: return C_O1_I1(r, r); diff --git a/tcg/optimize.c b/tcg/optimize.c index c363c5c04b..cd8ad712c4 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2362,8 +2362,10 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) xor_opc = INDEX_op_xor_i32; shr_opc = INDEX_op_shr_i32; neg_opc = INDEX_op_neg_i32; - if (TCG_TARGET_extract_i32_valid(sh, 1)) { + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, sh, 1)) { uext_opc = TCG_TARGET_HAS_extract_i32 ? INDEX_op_extract_i32 : 0; + } + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, sh, 1)) { sext_opc = TCG_TARGET_HAS_sextract_i32 ? INDEX_op_sextract_i32 : 0; } break; @@ -2373,8 +2375,10 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) xor_opc = INDEX_op_xor_i64; shr_opc = INDEX_op_shr_i64; neg_opc = INDEX_op_neg_i64; - if (TCG_TARGET_extract_i64_valid(sh, 1)) { + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, sh, 1)) { uext_opc = TCG_TARGET_HAS_extract_i64 ? INDEX_op_extract_i64 : 0; + } + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, sh, 1)) { sext_opc = TCG_TARGET_HAS_sextract_i64 ? INDEX_op_sextract_i64 : 0; } break; diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 65b6a0b0cf..8ed35be8c3 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -56,11 +56,15 @@ #ifndef TCG_TARGET_deposit_i64_valid #define TCG_TARGET_deposit_i64_valid(ofs, len) 1 #endif -#ifndef TCG_TARGET_extract_i32_valid -#define TCG_TARGET_extract_i32_valid(ofs, len) 1 +#ifndef TCG_TARGET_extract_valid +#define TCG_TARGET_extract_valid(type, ofs, len) \ + ((type) == TCG_TYPE_I32 ? TCG_TARGET_HAS_extract_i32 \ + : TCG_TARGET_HAS_extract_i64) #endif -#ifndef TCG_TARGET_extract_i64_valid -#define TCG_TARGET_extract_i64_valid(ofs, len) 1 +#ifndef TCG_TARGET_sextract_valid +#define TCG_TARGET_sextract_valid(type, ofs, len) \ + ((type) == TCG_TYPE_I32 ? TCG_TARGET_HAS_sextract_i32 \ + : TCG_TARGET_HAS_sextract_i64) #endif /* Only one of DIV or DIV2 should be defined. */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ab5ccd8dcb..d813a7f44e 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1014,8 +1014,7 @@ void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, return; } - if (TCG_TARGET_HAS_extract_i32 - && TCG_TARGET_extract_i32_valid(ofs, len)) { + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, ofs, len)) { tcg_gen_op4ii_i32(INDEX_op_extract_i32, ret, arg, ofs, len); return; } @@ -1077,8 +1076,7 @@ void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, } } - if (TCG_TARGET_HAS_sextract_i32 - && TCG_TARGET_extract_i32_valid(ofs, len)) { + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, ofs, len)) { tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, arg, ofs, len); return; } @@ -2811,8 +2809,7 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, goto do_shift_and; } - if (TCG_TARGET_HAS_extract_i64 - && TCG_TARGET_extract_i64_valid(ofs, len)) { + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, ofs, len)) { tcg_gen_op4ii_i64(INDEX_op_extract_i64, ret, arg, ofs, len); return; } @@ -2917,8 +2914,7 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, return; } - if (TCG_TARGET_HAS_sextract_i64 - && TCG_TARGET_extract_i64_valid(ofs, len)) { + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, ofs, len)) { tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, arg, ofs, len); return; } From 42ace08607669007e47d7e73aaaeb74948b0c5f2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 22:56:31 -0800 Subject: [PATCH 1115/2892] tcg/aarch64: Provide TCG_TARGET_{s}extract_valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivially mirrors TCG_TARGET_HAS_{s}extract_*. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 0e79e01266..26ce65b6a5 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -116,4 +116,7 @@ #define TCG_TARGET_HAS_cmpsel_vec 0 #define TCG_TARGET_HAS_tst_vec 1 +#define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_sextract_valid(type, ofs, len) 1 + #endif From 936fc0a96ed02e0d996ea58b05daf983b1cb3041 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Dec 2024 20:15:34 -0800 Subject: [PATCH 1116/2892] tcg/aarch64: Expand extract with offset 0 with andi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're about to change canonicalization of masks as extract instead of and. Retain the andi expansion here. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index ede6f47235..66eb4b73b5 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2450,7 +2450,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_extract_i64: case INDEX_op_extract_i32: - tcg_out_ubfm(s, ext, a0, a1, a2, a2 + args[3] - 1); + if (a2 == 0) { + uint64_t mask = MAKE_64BIT_MASK(0, args[3]); + tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, mask); + } else { + tcg_out_ubfm(s, ext, a0, a1, a2, a2 + args[3] - 1); + } break; case INDEX_op_sextract_i64: From 802ef65b5f8dccbcabb7960bee9993ec65f95ab6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 20:22:45 -0800 Subject: [PATCH 1117/2892] tcg/arm: Add full [US]XT[BH] into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The armv6 uxt and sxt opcodes have a 2-bit rotate field which supports extractions from ofs = {0,8,16,24}. Special case ofs = 0, len <= 8 as AND. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/arm/tcg-target-has.h | 21 ++++++++++++++-- tcg/arm/tcg-target.c.inc | 54 +++++++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 316185500d..d9f3311102 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -41,8 +41,8 @@ extern bool use_neon_instructions; #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 -#define TCG_TARGET_HAS_extract_i32 use_armv7_instructions -#define TCG_TARGET_HAS_sextract_i32 use_armv7_instructions +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 @@ -82,4 +82,21 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_cmpsel_vec 0 #define TCG_TARGET_HAS_tst_vec 1 +static inline bool +tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (use_armv7_instructions) { + return true; /* SBFX or UBFX */ + } + switch (len) { + case 8: /* SXTB or UXTB */ + case 16: /* SXTH or UXTH */ + return (ofs % 8) == 0; + } + return false; +} + +#define TCG_TARGET_extract_valid tcg_target_extract_valid +#define TCG_TARGET_sextract_valid tcg_target_extract_valid + #endif diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 9cfb733a14..12dad7307f 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1036,19 +1036,61 @@ static void tcg_out_deposit(TCGContext *s, ARMCond cond, TCGReg rd, static void tcg_out_extract(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int ofs, int len) { - /* ubfx */ - tcg_out32(s, 0x07e00050 | (cond << 28) | (rd << 12) | rn - | (ofs << 7) | ((len - 1) << 16)); + /* According to gcc, AND can be faster. */ + if (ofs == 0 && len <= 8) { + tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, + encode_imm_nofail((1 << len) - 1)); + return; + } + + if (use_armv7_instructions) { + /* ubfx */ + tcg_out32(s, 0x07e00050 | (cond << 28) | (rd << 12) | rn + | (ofs << 7) | ((len - 1) << 16)); + return; + } + + assert(ofs % 8 == 0); + switch (len) { + case 8: + /* uxtb */ + tcg_out32(s, 0x06ef0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + break; + case 16: + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + break; + default: + g_assert_not_reached(); + } } static void tcg_out_sextract(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int ofs, int len) { - /* sbfx */ - tcg_out32(s, 0x07a00050 | (cond << 28) | (rd << 12) | rn - | (ofs << 7) | ((len - 1) << 16)); + if (use_armv7_instructions) { + /* sbfx */ + tcg_out32(s, 0x07a00050 | (cond << 28) | (rd << 12) | rn + | (ofs << 7) | ((len - 1) << 16)); + return; + } + + assert(ofs % 8 == 0); + switch (len) { + case 8: + /* sxtb */ + tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + break; + case 16: + /* sxth */ + tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + break; + default: + g_assert_not_reached(); + } } + static void tcg_out_ld32u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int32_t offset) { From 0c44a4d3b647240aa9485e648fe5f63bed5e4820 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 20:38:06 -0800 Subject: [PATCH 1118/2892] tcg/loongarch64: Fold the ext{8,16,32}[us] cases into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept byte and word extensions with the extract opcodes. This is preparatory to removing the specialized extracts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target-has.h | 15 ++++++++++++-- tcg/loongarch64/tcg-target.c.inc | 34 ++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index e4333c36c6..ac7d2fcdf9 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -17,7 +17,7 @@ #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 @@ -52,7 +52,7 @@ #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 @@ -109,5 +109,16 @@ #define TCG_TARGET_HAS_cmpsel_vec 0 #define TCG_TARGET_HAS_tst_vec 0 +#define TCG_TARGET_extract_valid(type, ofs, len) 1 + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + return ofs == 0 && (len == 8 || len == 16); +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid #endif diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 3dff29facb..cebe8dd354 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1375,10 +1375,38 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_extract_i32: - tcg_out_opc_bstrpick_w(s, a0, a1, a2, a2 + args[3] - 1); + if (a2 == 0 && args[3] <= 12) { + tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); + } else { + tcg_out_opc_bstrpick_w(s, a0, a1, a2, a2 + args[3] - 1); + } break; case INDEX_op_extract_i64: - tcg_out_opc_bstrpick_d(s, a0, a1, a2, a2 + args[3] - 1); + if (a2 == 0 && args[3] <= 12) { + tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); + } else { + tcg_out_opc_bstrpick_d(s, a0, a1, a2, a2 + args[3] - 1); + } + break; + + case INDEX_op_sextract_i64: + if (a2 + args[3] == 32) { + if (a2 == 0) { + tcg_out_ext32s(s, a0, a1); + } else { + tcg_out_opc_srai_w(s, a0, a1, a2); + } + break; + } + /* FALLTHRU */ + case INDEX_op_sextract_i32: + if (a2 == 0 && args[3] == 8) { + tcg_out_ext8s(s, TCG_TYPE_REG, a0, a1); + } else if (a2 == 0 && args[3] == 16) { + tcg_out_ext16s(s, TCG_TYPE_REG, a0, a1); + } else { + g_assert_not_reached(); + } break; case INDEX_op_deposit_i32: @@ -2243,6 +2271,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_not_i64: case INDEX_op_extract_i32: case INDEX_op_extract_i64: + case INDEX_op_sextract_i32: + case INDEX_op_sextract_i64: case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: From 791d03047bb814cc5f938f2b1f59115ef7f63344 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 21:06:30 -0800 Subject: [PATCH 1119/2892] tcg/mips: Fold the ext{8,16,32}[us] cases into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept AND, ext32u, ext32s extensions with the extract opcodes. This is preparatory to removing the specialized extracts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target-has.h | 26 ++++++++++++++++++++++---- tcg/mips/tcg-target.c.inc | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index d3d874ffd1..e7914cc970 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -80,8 +80,8 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions @@ -96,8 +96,8 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions @@ -119,4 +119,22 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 0 +#define TCG_TARGET_extract_valid(type, ofs, len) use_mips32r2_instructions + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + case 16: + return use_mips32r2_instructions; + case 32: + return type == TCG_TYPE_I64; + } + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + #endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index b31b8f0007..99f6ef6c76 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2041,12 +2041,37 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_bf64(s, OPC_DINS, OPC_DINSM, OPC_DINSU, a0, a2, args[3] + args[4] - 1, args[3]); break; + case INDEX_op_extract_i32: - tcg_out_opc_bf(s, OPC_EXT, a0, a1, args[3] - 1, a2); + if (a2 == 0 && args[3] <= 16) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, (1 << args[3]) - 1); + } else { + tcg_out_opc_bf(s, OPC_EXT, a0, a1, args[3] - 1, a2); + } break; case INDEX_op_extract_i64: - tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, a0, a1, - args[3] - 1, a2); + if (a2 == 0 && args[3] <= 16) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, (1 << args[3]) - 1); + } else { + tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, + a0, a1, args[3] - 1, a2); + } + break; + + case INDEX_op_sextract_i64: + if (a2 == 0 && args[3] == 32) { + tcg_out_ext32s(s, a0, a1); + break; + } + /* FALLTHRU */ + case INDEX_op_sextract_i32: + if (a2 == 0 && args[3] == 8) { + tcg_out_ext8s(s, TCG_TYPE_REG, a0, a1); + } else if (a2 == 0 && args[3] == 16) { + tcg_out_ext16s(s, TCG_TYPE_REG, a0, a1); + } else { + g_assert_not_reached(); + } break; case INDEX_op_brcond_i32: @@ -2170,6 +2195,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext8s_i32: case INDEX_op_ext16s_i32: case INDEX_op_extract_i32: + case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -2191,6 +2217,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_extract_i64: + case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: From 94d593941b68828a42df6d7af20fc4bb0e720df5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 21:44:35 -0800 Subject: [PATCH 1120/2892] tcg/ppc: Fold the ext{8,16,32}[us] cases into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept byte and word extensions with the extract opcodes. This is preparatory to removing the specialized extracts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target-has.h | 16 ++++++++++++++-- tcg/ppc/tcg-target.c.inc | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index a6c7cdba5d..d087189a77 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -39,7 +39,7 @@ #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 @@ -75,7 +75,7 @@ #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -121,4 +121,16 @@ #define TCG_TARGET_HAS_cmpsel_vec 1 #define TCG_TARGET_HAS_tst_vec 0 +#define TCG_TARGET_extract_valid(type, ofs, len) 1 + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + return ofs == 0 && (len == 8 || len == 16); +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + #endif diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 9205ac99e9..6e711cd53f 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3430,13 +3430,41 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_extract_i32: + if (args[2] == 0 && args[3] <= 16) { + tcg_out32(s, ANDI | SAI(args[1], args[0], (1 << args[3]) - 1)); + break; + } tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 32 - args[3], 31); break; case INDEX_op_extract_i64: + if (args[2] == 0 && args[3] <= 16) { + tcg_out32(s, ANDI | SAI(args[1], args[0], (1 << args[3]) - 1)); + break; + } tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 64 - args[3]); break; + case INDEX_op_sextract_i64: + if (args[2] + args[3] == 32) { + if (args[2] == 0) { + tcg_out_ext32s(s, args[0], args[1]); + } else { + tcg_out_sari32(s, args[0], args[1], args[2]); + } + break; + } + /* FALLTHRU */ + case INDEX_op_sextract_i32: + if (args[2] == 0 && args[3] == 8) { + tcg_out_ext8s(s, TCG_TYPE_I32, args[0], args[1]); + } else if (args[2] == 0 && args[3] == 16) { + tcg_out_ext16s(s, TCG_TYPE_I32, args[0], args[1]); + } else { + g_assert_not_reached(); + } + break; + case INDEX_op_movcond_i32: tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], args[3], args[4], const_args[2]); @@ -4160,6 +4188,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: + case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -4179,6 +4208,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_extract_i64: + case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: From 841e2c5257102c738e8578eb0ce38d3de830ea4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Dec 2024 00:17:20 -0800 Subject: [PATCH 1121/2892] tcg/riscv64: Fold the ext{8,16,32}[us] cases into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept byte and word extensions with the extract opcodes. This is preparatory to removing the specialized extracts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target-has.h | 39 ++++++++++++++++++++++++++++++++++---- tcg/riscv/tcg-target.c.inc | 34 +++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index ddc759a533..5bf62c7c7f 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -16,8 +16,8 @@ #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_extract_i32 0 -#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -50,8 +50,8 @@ #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 @@ -109,4 +109,35 @@ #define TCG_TARGET_HAS_tst_vec 0 +static inline bool +tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 16: + return cpuinfo & CPUINFO_ZBB; + case 32: + return (cpuinfo & CPUINFO_ZBA) && type == TCG_TYPE_I64; + } + } + return false; +} +#define TCG_TARGET_extract_valid tcg_target_extract_valid + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + case 16: + return cpuinfo & CPUINFO_ZBB; + case 32: + return type == TCG_TYPE_I64; + } + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + #endif diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index e381ba4e77..fc93900c6d 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2343,6 +2343,36 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, a0); break; + case INDEX_op_extract_i64: + if (a2 == 0 && args[3] == 32) { + tcg_out_ext32u(s, a0, a1); + break; + } + /* FALLTHRU */ + case INDEX_op_extract_i32: + if (a2 == 0 && args[3] == 16) { + tcg_out_ext16u(s, a0, a1); + } else { + g_assert_not_reached(); + } + break; + + case INDEX_op_sextract_i64: + if (a2 == 0 && args[3] == 32) { + tcg_out_ext32s(s, a0, a1); + break; + } + /* FALLTHRU */ + case INDEX_op_sextract_i32: + if (a2 == 0 && args[3] == 8) { + tcg_out_ext8s(s, TCG_TYPE_REG, a0, a1); + } else if (a2 == 0 && args[3] == 16) { + tcg_out_ext16s(s, TCG_TYPE_REG, a0, a1); + } else { + g_assert_not_reached(); + } + break; + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ @@ -2620,6 +2650,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: + case INDEX_op_extract_i32: + case INDEX_op_extract_i64: + case INDEX_op_sextract_i32: + case INDEX_op_sextract_i64: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_bswap16_i64: From fa65f13555e121566c9105f252c72a3b63f1ecea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Dec 2024 20:52:12 -0800 Subject: [PATCH 1122/2892] tcg/riscv: Use SRAIW, SRLIW for {s}extract_i64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracts which abut bit 32 may use 32-bit shifts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target-has.h | 24 +++++++----------------- tcg/riscv/tcg-target.c.inc | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 5bf62c7c7f..e890546c3a 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -112,31 +112,21 @@ static inline bool tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) { - if (ofs == 0) { - switch (len) { - case 16: - return cpuinfo & CPUINFO_ZBB; - case 32: - return (cpuinfo & CPUINFO_ZBA) && type == TCG_TYPE_I64; - } + if (type == TCG_TYPE_I64 && ofs + len == 32) { + /* ofs > 0 uses SRLIW; ofs == 0 uses add.uw. */ + return ofs || (cpuinfo & CPUINFO_ZBA); } - return false; + return (cpuinfo & CPUINFO_ZBB) && ofs == 0 && len == 16; } #define TCG_TARGET_extract_valid tcg_target_extract_valid static inline bool tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) { - if (ofs == 0) { - switch (len) { - case 8: - case 16: - return cpuinfo & CPUINFO_ZBB; - case 32: - return type == TCG_TYPE_I64; - } + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; } - return false; + return (cpuinfo & CPUINFO_ZBB) && ofs == 0 && (len == 8 || len == 16); } #define TCG_TARGET_sextract_valid tcg_target_sextract_valid diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index fc93900c6d..4f6e18f59e 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2344,8 +2344,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_extract_i64: - if (a2 == 0 && args[3] == 32) { - tcg_out_ext32u(s, a0, a1); + if (a2 + args[3] == 32) { + if (a2 == 0) { + tcg_out_ext32u(s, a0, a1); + } else { + tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2); + } break; } /* FALLTHRU */ @@ -2358,8 +2362,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_sextract_i64: - if (a2 == 0 && args[3] == 32) { - tcg_out_ext32s(s, a0, a1); + if (a2 + args[3] == 32) { + if (a2 == 0) { + tcg_out_ext32s(s, a0, a1); + } else { + tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2); + } break; } /* FALLTHRU */ From 42103c4ce73d7c26c48cc48f29e694700b09a937 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 22:26:42 -0800 Subject: [PATCH 1123/2892] tcg/s390x: Fold the ext{8,16,32}[us] cases into {s}extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept byte and word extensions with the extract opcodes. This is preparatory to removing the specialized extracts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-has.h | 22 ++++++++++++++++++++-- tcg/s390x/tcg-target.c.inc | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 4992d74f12..72b57407d4 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -48,7 +48,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -82,7 +82,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -121,4 +121,22 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_cmpsel_vec 1 #define TCG_TARGET_HAS_tst_vec 0 +#define TCG_TARGET_extract_valid(type, ofs, len) 1 + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + case 16: + return true; + case 32: + return type == TCG_TYPE_I64; + } + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + #endif diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index fc7d986e68..dc7722dc31 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1572,9 +1572,41 @@ static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, static void tgen_extract(TCGContext *s, TCGReg dest, TCGReg src, int ofs, int len) { + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8u(s, dest, src); + return; + case 16: + tcg_out_ext16u(s, dest, src); + return; + case 32: + tcg_out_ext32u(s, dest, src); + return; + } + } tcg_out_risbg(s, dest, src, 64 - len, 63, 64 - ofs, 1); } +static void tgen_sextract(TCGContext *s, TCGReg dest, TCGReg src, + int ofs, int len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, TCG_TYPE_REG, dest, src); + return; + case 16: + tcg_out_ext16s(s, TCG_TYPE_REG, dest, src); + return; + case 32: + tcg_out_ext32s(s, dest, src); + return; + } + } + g_assert_not_reached(); +} + static void tgen_gotoi(TCGContext *s, int cc, const tcg_insn_unit *dest) { ptrdiff_t off = tcg_pcrel_diff(s, dest) >> 1; @@ -2726,6 +2758,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(extract): tgen_extract(s, args[0], args[1], args[2], args[3]); break; + OP_32_64(sextract): + tgen_sextract(s, args[0], args[1], args[2], args[3]); + break; case INDEX_op_clz_i64: tgen_clz(s, args[0], args[1], args[2], const_args[2]); @@ -3325,6 +3360,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: case INDEX_op_extract_i64: + case INDEX_op_sextract_i32: + case INDEX_op_sextract_i64: case INDEX_op_ctpop_i32: case INDEX_op_ctpop_i64: return C_O1_I1(r, r); From 3dc7e1db22b8e2f058fca36fa1ab7c78ebf1a389 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Dec 2024 22:37:13 -0800 Subject: [PATCH 1124/2892] tcg/sparc64: Use SRA, SRL for {s}extract_i64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracts which abut bit 32 may use 32-bit shifts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target-has.h | 13 +++++++++---- tcg/sparc64/tcg-target.c.inc | 11 +++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index d9ca14cc3d..d3ec569592 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -33,8 +33,8 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_extract_i32 0 -#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract_i32 1 +#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -68,8 +68,8 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract_i64 1 +#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -83,4 +83,9 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_tst 1 +#define TCG_TARGET_extract_valid(type, ofs, len) \ + ((type) == TCG_TYPE_I64 && (ofs) + (len) == 32) + +#define TCG_TARGET_sextract_valid TCG_TARGET_extract_valid + #endif diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index afc778fae7..733cb51651 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1510,6 +1510,15 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, a0); break; + case INDEX_op_extract_i64: + tcg_debug_assert(a2 + args[3] == 32); + tcg_out_arithi(s, a0, a1, a2, SHIFT_SRL); + break; + case INDEX_op_sextract_i64: + tcg_debug_assert(a2 + args[3] == 32); + tcg_out_arithi(s, a0, a1, a2, SHIFT_SRA); + break; + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ @@ -1559,6 +1568,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: + case INDEX_op_extract_i64: + case INDEX_op_sextract_i64: case INDEX_op_qemu_ld_a32_i32: case INDEX_op_qemu_ld_a64_i32: case INDEX_op_qemu_ld_a32_i64: From d9336b727ffd3430254663c8a37850242f6f07ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Dec 2024 00:23:18 -0800 Subject: [PATCH 1125/2892] tcg/tci: Provide TCG_TARGET_{s}extract_valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivially mirrors TCG_TARGET_HAS_{s}extract_*. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tci/tcg-target-has.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 3397403910..2f45ad614f 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -80,4 +80,7 @@ #define TCG_TARGET_HAS_tst 1 +#define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_sextract_valid(type, ofs, len) 1 + #endif From 41736e7ce1f810594abad540c48f8390228a4d81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 28 Dec 2024 14:44:56 -0800 Subject: [PATCH 1126/2892] tcg/tci: Remove assertions for deposit and extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have these assertions during opcode creation. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tci/tcg-target.c.inc | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 88cecbd62f..8dedddce5f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -775,28 +775,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; CASE_32_64(deposit) /* Optional (TCG_TARGET_HAS_deposit_*). */ - { - TCGArg pos = args[3], len = args[4]; - TCGArg max = opc == INDEX_op_deposit_i32 ? 32 : 64; - - tcg_debug_assert(pos < max); - tcg_debug_assert(pos + len <= max); - - tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], pos, len); - } + tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], args[3], args[4]); break; CASE_32_64(extract) /* Optional (TCG_TARGET_HAS_extract_*). */ CASE_32_64(sextract) /* Optional (TCG_TARGET_HAS_sextract_*). */ - { - TCGArg pos = args[2], len = args[3]; - TCGArg max = type == TCG_TYPE_I32 ? 32 : 64; - - tcg_debug_assert(pos < max); - tcg_debug_assert(pos + len <= max); - - tcg_out_op_rrbb(s, opc, args[0], args[1], pos, len); - } + tcg_out_op_rrbb(s, opc, args[0], args[1], args[2], args[3]); break; CASE_32_64(brcond) From c334de110ea93108feeddce11bd302146d1520f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Dec 2024 00:43:19 -0800 Subject: [PATCH 1127/2892] tcg: Remove TCG_TARGET_HAS_{s}extract_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make extract and sextract "unconditional" in the sense that the opcodes are always present. Rely instead on TCG_TARGET_HAS_{s}extract_valid, now always defined. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 4 ---- tcg/arm/tcg-target-has.h | 2 -- tcg/i386/tcg-target-has.h | 4 ---- tcg/loongarch64/tcg-target-has.h | 4 ---- tcg/mips/tcg-target-has.h | 4 ---- tcg/optimize.c | 8 ++++---- tcg/ppc/tcg-target-has.h | 4 ---- tcg/riscv/tcg-target-has.h | 4 ---- tcg/s390x/tcg-target-has.h | 4 ---- tcg/sparc64/tcg-target-has.h | 4 ---- tcg/tcg-has.h | 12 ------------ tcg/tcg.c | 12 ++++-------- tcg/tci.c | 8 -------- tcg/tci/tcg-target-has.h | 4 ---- 14 files changed, 8 insertions(+), 70 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 26ce65b6a5..43eaa2287c 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -32,8 +32,6 @@ #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -67,8 +65,6 @@ #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index d9f3311102..7152dd6f5e 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -41,8 +41,6 @@ extern bool use_neon_instructions; #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 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index ad69f957a7..833ccc411a 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -44,8 +44,6 @@ #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 have_popcnt #define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -79,8 +77,6 @@ #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 have_popcnt #define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index ac7d2fcdf9..a15ab9cc28 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -16,8 +16,6 @@ #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 @@ -51,8 +49,6 @@ #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index e7914cc970..1bc14f65dd 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -80,8 +80,6 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions @@ -96,8 +94,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions diff --git a/tcg/optimize.c b/tcg/optimize.c index cd8ad712c4..8c6303e3af 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2363,10 +2363,10 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) shr_opc = INDEX_op_shr_i32; neg_opc = INDEX_op_neg_i32; if (TCG_TARGET_extract_valid(TCG_TYPE_I32, sh, 1)) { - uext_opc = TCG_TARGET_HAS_extract_i32 ? INDEX_op_extract_i32 : 0; + uext_opc = INDEX_op_extract_i32; } if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, sh, 1)) { - sext_opc = TCG_TARGET_HAS_sextract_i32 ? INDEX_op_sextract_i32 : 0; + sext_opc = INDEX_op_sextract_i32; } break; case TCG_TYPE_I64: @@ -2376,10 +2376,10 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) shr_opc = INDEX_op_shr_i64; neg_opc = INDEX_op_neg_i64; if (TCG_TARGET_extract_valid(TCG_TYPE_I64, sh, 1)) { - uext_opc = TCG_TARGET_HAS_extract_i64 ? INDEX_op_extract_i64 : 0; + uext_opc = INDEX_op_extract_i64; } if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, sh, 1)) { - sext_opc = TCG_TARGET_HAS_sextract_i64 ? INDEX_op_sextract_i64 : 0; + sext_opc = INDEX_op_sextract_i64; } break; default: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index d087189a77..fa9275264c 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -38,8 +38,6 @@ #define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 #define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 @@ -74,8 +72,6 @@ #define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 #define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index e890546c3a..5a39720ea9 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -16,8 +16,6 @@ #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -50,8 +48,6 @@ #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 72b57407d4..f3965c7df5 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -47,8 +47,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -81,8 +79,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index d3ec569592..4a621313b7 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -33,8 +33,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -68,8 +66,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 8ed35be8c3..c93a98fb86 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -35,8 +35,6 @@ #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 @@ -56,16 +54,6 @@ #ifndef TCG_TARGET_deposit_i64_valid #define TCG_TARGET_deposit_i64_valid(ofs, len) 1 #endif -#ifndef TCG_TARGET_extract_valid -#define TCG_TARGET_extract_valid(type, ofs, len) \ - ((type) == TCG_TYPE_I32 ? TCG_TARGET_HAS_extract_i32 \ - : TCG_TARGET_HAS_extract_i64) -#endif -#ifndef TCG_TARGET_sextract_valid -#define TCG_TARGET_sextract_valid(type, ofs, len) \ - ((type) == TCG_TYPE_I32 ? TCG_TARGET_HAS_sextract_i32 \ - : TCG_TARGET_HAS_sextract_i64) -#endif /* Only one of DIV or DIV2 should be defined. */ #if defined(TCG_TARGET_HAS_div_i32) diff --git a/tcg/tcg.c b/tcg/tcg.c index 9b54a8bec8..c584ca034f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2195,6 +2195,8 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: + case INDEX_op_extract_i32: + case INDEX_op_sextract_i32: return true; case INDEX_op_negsetcond_i32: @@ -2213,10 +2215,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_rot_i32; case INDEX_op_deposit_i32: return TCG_TARGET_HAS_deposit_i32; - case INDEX_op_extract_i32: - return TCG_TARGET_HAS_extract_i32; - case INDEX_op_sextract_i32: - return TCG_TARGET_HAS_sextract_i32; case INDEX_op_extract2_i32: return TCG_TARGET_HAS_extract2_i32; case INDEX_op_add2_i32: @@ -2293,6 +2291,8 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sar_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: + case INDEX_op_extract_i64: + case INDEX_op_sextract_i64: return TCG_TARGET_REG_BITS == 64; case INDEX_op_negsetcond_i64: @@ -2311,10 +2311,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_rot_i64; case INDEX_op_deposit_i64: return TCG_TARGET_HAS_deposit_i64; - case INDEX_op_extract_i64: - return TCG_TARGET_HAS_extract_i64; - case INDEX_op_sextract_i64: - return TCG_TARGET_HAS_sextract_i64; case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; case INDEX_op_extrl_i64_i32: diff --git a/tcg/tci.c b/tcg/tci.c index 39a68db287..30d912d75d 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -657,18 +657,14 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); break; #endif -#if TCG_TARGET_HAS_extract_i32 case INDEX_op_extract_i32: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = extract32(regs[r1], pos, len); break; -#endif -#if TCG_TARGET_HAS_sextract_i32 case INDEX_op_sextract_i32: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = sextract32(regs[r1], pos, len); break; -#endif case INDEX_op_brcond_i32: tci_args_rl(insn, tb_ptr, &r0, &ptr); if ((uint32_t)regs[r0]) { @@ -868,18 +864,14 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); break; #endif -#if TCG_TARGET_HAS_extract_i64 case INDEX_op_extract_i64: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = extract64(regs[r1], pos, len); break; -#endif -#if TCG_TARGET_HAS_sextract_i64 case INDEX_op_sextract_i64: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = sextract64(regs[r1], pos, len); break; -#endif case INDEX_op_brcond_i64: tci_args_rl(insn, tb_ptr, &r0, &ptr); if (regs[r0]) { diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 2f45ad614f..7a176b1fe5 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -17,8 +17,6 @@ #define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 @@ -41,8 +39,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 From 6482e9d2a40101895ae73c72466ccf4bcbee51bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Dec 2024 20:10:03 -0800 Subject: [PATCH 1128/2892] tcg: Remove TCG_TARGET_HAS_deposit_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make deposit "unconditional" in the sense that the opcode is always present. Rely instead on TCG_TARGET_deposit_valid, now always defined. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 3 +-- tcg/arm/tcg-target-has.h | 2 +- tcg/i386/tcg-target-has.h | 5 +---- tcg/loongarch64/tcg-target-has.h | 3 +-- tcg/mips/tcg-target-has.h | 3 +-- tcg/ppc/tcg-target-has.h | 3 +-- tcg/riscv/tcg-target-has.h | 4 ++-- tcg/s390x/tcg-target-has.h | 3 +-- tcg/sparc64/tcg-target-has.h | 4 ++-- tcg/tcg-has.h | 8 -------- tcg/tcg-op.c | 22 +++++++++++----------- tcg/tcg.c | 31 +++++++++++-------------------- tcg/tci.c | 4 ---- tcg/tci/tcg-target-has.h | 3 +-- tcg/tci/tcg-target.c.inc | 2 +- 15 files changed, 35 insertions(+), 65 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 43eaa2287c..39f01c14cd 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -31,7 +31,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -64,7 +63,6 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -114,5 +112,6 @@ #define TCG_TARGET_extract_valid(type, ofs, len) 1 #define TCG_TARGET_sextract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 #endif diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 7152dd6f5e..e3510a8f7a 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -40,7 +40,6 @@ extern bool use_neon_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 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 @@ -96,5 +95,6 @@ tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) #define TCG_TARGET_extract_valid tcg_target_extract_valid #define TCG_TARGET_sextract_valid tcg_target_extract_valid +#define TCG_TARGET_deposit_valid(type, ofs, len) use_armv7_instructions #endif diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 833ccc411a..63768ff058 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -43,7 +43,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 have_popcnt -#define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -76,7 +75,6 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 have_popcnt -#define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -121,10 +119,9 @@ #define TCG_TARGET_HAS_cmpsel_vec 1 #define TCG_TARGET_HAS_tst_vec have_avx512bw -#define TCG_TARGET_deposit_i32_valid(ofs, len) \ +#define TCG_TARGET_deposit_valid(type, ofs, len) \ (((ofs) == 0 && ((len) == 8 || (len) == 16)) || \ (TCG_TARGET_REG_BITS == 32 && (ofs) == 8 && (len) == 8)) -#define TCG_TARGET_deposit_i64_valid TCG_TARGET_deposit_i32_valid /* * Check for the possibility of low byte/word extraction, high-byte extraction diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index a15ab9cc28..ac88522eef 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 @@ -48,7 +47,6 @@ #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 @@ -106,6 +104,7 @@ #define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 static inline bool tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 1bc14f65dd..df6960fe9a 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -79,7 +79,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions @@ -93,7 +92,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions @@ -116,6 +114,7 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_tst 0 #define TCG_TARGET_extract_valid(type, ofs, len) use_mips32r2_instructions +#define TCG_TARGET_deposit_valid(type, ofs, len) use_mips32r2_instructions static inline bool tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index fa9275264c..6db91f78ce 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -37,7 +37,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 -#define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 @@ -71,7 +70,6 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 -#define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -118,6 +116,7 @@ #define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 static inline bool tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 5a39720ea9..0f9cc04f8c 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -47,7 +46,6 @@ #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 @@ -126,4 +124,6 @@ tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) } #define TCG_TARGET_sextract_valid tcg_target_sextract_valid +#define TCG_TARGET_deposit_valid(type, ofs, len) 0 + #endif diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index f3965c7df5..e99e671642 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -46,7 +46,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -78,7 +77,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -118,6 +116,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 static inline bool tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 4a621313b7..2f46df8c61 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -32,7 +32,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -65,7 +64,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 @@ -84,4 +82,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_sextract_valid TCG_TARGET_extract_valid +#define TCG_TARGET_deposit_valid(type, ofs, len) 0 + #endif diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index c93a98fb86..418e4673eb 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -34,7 +34,6 @@ #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 @@ -48,13 +47,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #endif -#ifndef TCG_TARGET_deposit_i32_valid -#define TCG_TARGET_deposit_i32_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_deposit_i64_valid -#define TCG_TARGET_deposit_i64_valid(ofs, len) 1 -#endif - /* Only one of DIV or DIV2 should be defined. */ #if defined(TCG_TARGET_HAS_div_i32) #define TCG_TARGET_HAS_div2_i32 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index d813a7f44e..fec6d678a2 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -906,7 +906,7 @@ void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, tcg_gen_mov_i32(ret, arg2); return; } - if (TCG_TARGET_HAS_deposit_i32 && TCG_TARGET_deposit_i32_valid(ofs, len)) { + if (TCG_TARGET_deposit_valid(TCG_TYPE_I32, ofs, len)) { tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); return; } @@ -951,8 +951,7 @@ void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_shli_i32(ret, arg, ofs); } else if (ofs == 0) { tcg_gen_andi_i32(ret, arg, (1u << len) - 1); - } else if (TCG_TARGET_HAS_deposit_i32 - && TCG_TARGET_deposit_i32_valid(ofs, len)) { + } else if (TCG_TARGET_deposit_valid(TCG_TYPE_I32, ofs, len)) { TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, zero, arg, ofs, len); } else { @@ -2642,12 +2641,13 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, tcg_gen_mov_i64(ret, arg2); return; } - if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(ofs, len)) { - tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); - return; - } - if (TCG_TARGET_REG_BITS == 32) { + if (TCG_TARGET_REG_BITS == 64) { + if (TCG_TARGET_deposit_valid(TCG_TYPE_I64, ofs, len)) { + tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); + return; + } + } else { if (ofs >= 32) { tcg_gen_deposit_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_LOW(arg2), ofs - 32, len); @@ -2702,8 +2702,8 @@ void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_shli_i64(ret, arg, ofs); } else if (ofs == 0) { tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); - } else if (TCG_TARGET_HAS_deposit_i64 - && TCG_TARGET_deposit_i64_valid(ofs, len)) { + } else if (TCG_TARGET_REG_BITS == 64 && + TCG_TARGET_deposit_valid(TCG_TYPE_I64, ofs, len)) { TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, zero, arg, ofs, len); } else { @@ -3226,7 +3226,7 @@ void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high) tcg_gen_extu_i32_i64(dest, low); /* If deposit is available, use it. Otherwise use the extra knowledge that we have of the zero-extensions above. */ - if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(32, 32)) { + if (TCG_TARGET_deposit_valid(TCG_TYPE_I64, 32, 32)) { tcg_gen_deposit_i64(dest, dest, tmp, 32, 32); } else { tcg_gen_shli_i64(tmp, tmp, 32); diff --git a/tcg/tcg.c b/tcg/tcg.c index c584ca034f..43b6712286 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2197,6 +2197,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sar_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: + case INDEX_op_deposit_i32: return true; case INDEX_op_negsetcond_i32: @@ -2213,8 +2214,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: return TCG_TARGET_HAS_rot_i32; - case INDEX_op_deposit_i32: - return TCG_TARGET_HAS_deposit_i32; case INDEX_op_extract2_i32: return TCG_TARGET_HAS_extract2_i32; case INDEX_op_add2_i32: @@ -2293,6 +2292,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extract_i64: case INDEX_op_sextract_i64: + case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; case INDEX_op_negsetcond_i64: @@ -2309,8 +2309,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: return TCG_TARGET_HAS_rot_i64; - case INDEX_op_deposit_i64: - return TCG_TARGET_HAS_deposit_i64; case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; case INDEX_op_extrl_i64_i32: @@ -2439,23 +2437,16 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len) { + unsigned width; + + tcg_debug_assert(type == TCG_TYPE_I32 || type == TCG_TYPE_I64); + width = (type == TCG_TYPE_I32 ? 32 : 64); + + tcg_debug_assert(ofs < width); tcg_debug_assert(len > 0); - switch (type) { - case TCG_TYPE_I32: - tcg_debug_assert(ofs < 32); - tcg_debug_assert(len <= 32); - tcg_debug_assert(ofs + len <= 32); - return TCG_TARGET_HAS_deposit_i32 && - TCG_TARGET_deposit_i32_valid(ofs, len); - case TCG_TYPE_I64: - tcg_debug_assert(ofs < 64); - tcg_debug_assert(len <= 64); - tcg_debug_assert(ofs + len <= 64); - return TCG_TARGET_HAS_deposit_i64 && - TCG_TARGET_deposit_i64_valid(ofs, len); - default: - g_assert_not_reached(); - } + tcg_debug_assert(len <= width - ofs); + + return TCG_TARGET_deposit_valid(type, ofs, len); } static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); diff --git a/tcg/tci.c b/tcg/tci.c index 30d912d75d..8c1c53424d 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -651,12 +651,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ror32(regs[r1], regs[r2] & 31); break; #endif -#if TCG_TARGET_HAS_deposit_i32 case INDEX_op_deposit_i32: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); break; -#endif case INDEX_op_extract_i32: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = extract32(regs[r1], pos, len); @@ -858,12 +856,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ror64(regs[r1], regs[r2] & 63); break; #endif -#if TCG_TARGET_HAS_deposit_i64 case INDEX_op_deposit_i64: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); break; -#endif case INDEX_op_extract_i64: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = extract64(regs[r1], pos, len); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 7a176b1fe5..c8785ca8dc 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -16,7 +16,6 @@ #define TCG_TARGET_HAS_ext8u_i32 1 #define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 @@ -38,7 +37,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 @@ -78,5 +76,6 @@ #define TCG_TARGET_extract_valid(type, ofs, len) 1 #define TCG_TARGET_sextract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 #endif diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 8dedddce5f..d6c77325a3 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -774,7 +774,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrr(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(deposit) /* Optional (TCG_TARGET_HAS_deposit_*). */ + CASE_32_64(deposit) tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], args[3], args[4]); break; From 2c4815590f9ea399bc87f727dd1c883b0d701bb8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 Jan 2025 10:16:00 -0800 Subject: [PATCH 1129/2892] util/cpuinfo-riscv: Detect Zbs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20250102181601.1421059-2-richard.henderson@linaro.org> --- host/include/riscv/host/cpuinfo.h | 5 +++-- util/cpuinfo-riscv.c | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/host/include/riscv/host/cpuinfo.h b/host/include/riscv/host/cpuinfo.h index cdc784e7b6..b2b53dbf62 100644 --- a/host/include/riscv/host/cpuinfo.h +++ b/host/include/riscv/host/cpuinfo.h @@ -9,8 +9,9 @@ #define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ #define CPUINFO_ZBA (1u << 1) #define CPUINFO_ZBB (1u << 2) -#define CPUINFO_ZICOND (1u << 3) -#define CPUINFO_ZVE64X (1u << 4) +#define CPUINFO_ZBS (1u << 3) +#define CPUINFO_ZICOND (1u << 4) +#define CPUINFO_ZVE64X (1u << 5) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c index 971c924012..0291b7218a 100644 --- a/util/cpuinfo-riscv.c +++ b/util/cpuinfo-riscv.c @@ -36,7 +36,8 @@ static void sigill_handler(int signo, siginfo_t *si, void *data) /* Called both as constructor and (possibly) via other constructors. */ unsigned __attribute__((constructor)) cpuinfo_init(void) { - unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND | CPUINFO_ZVE64X; + unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS + | CPUINFO_ZICOND | CPUINFO_ZVE64X; unsigned info = cpuinfo; if (info) { @@ -50,6 +51,9 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) #if defined(__riscv_arch_test) && defined(__riscv_zbb) info |= CPUINFO_ZBB; #endif +#if defined(__riscv_arch_test) && defined(__riscv_zbs) + info |= CPUINFO_ZBS; +#endif #if defined(__riscv_arch_test) && defined(__riscv_zicond) info |= CPUINFO_ZICOND; #endif @@ -71,7 +75,8 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) && pair.key >= 0) { info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0; info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0; - left &= ~(CPUINFO_ZBA | CPUINFO_ZBB); + info |= pair.value & RISCV_HWPROBE_EXT_ZBS ? CPUINFO_ZBS : 0; + left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS); #ifdef RISCV_HWPROBE_EXT_ZICOND info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; left &= ~CPUINFO_ZICOND; @@ -117,6 +122,15 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) left &= ~CPUINFO_ZBB; } + if (left & CPUINFO_ZBS) { + /* Probe for Zbs: bext zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 5, 0x24, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBS; + left &= ~CPUINFO_ZBS; + } + if (left & CPUINFO_ZICOND) { /* Probe for Zicond: czero.eqz zero,zero,zero. */ got_sigill = 0; From ee97eef290cde22c39b8715802bb3b0ca4555be3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 Jan 2025 10:16:01 -0800 Subject: [PATCH 1130/2892] tcg/riscv: Use BEXTI for single-bit extractions Acked-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20250102181601.1421059-3-richard.henderson@linaro.org> --- tcg/riscv/tcg-target-has.h | 8 +++++++- tcg/riscv/tcg-target.c.inc | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 0f9cc04f8c..f35f9b31f5 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -110,7 +110,13 @@ tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) /* ofs > 0 uses SRLIW; ofs == 0 uses add.uw. */ return ofs || (cpuinfo & CPUINFO_ZBA); } - return (cpuinfo & CPUINFO_ZBB) && ofs == 0 && len == 16; + switch (len) { + case 1: + return (cpuinfo & CPUINFO_ZBS) && ofs != 0; + case 16: + return (cpuinfo & CPUINFO_ZBB) && ofs == 0; + } + return false; } #define TCG_TARGET_extract_valid tcg_target_extract_valid diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 4f6e18f59e..61dc310c1a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -163,6 +163,7 @@ typedef enum { OPC_ANDI = 0x7013, OPC_AUIPC = 0x17, OPC_BEQ = 0x63, + OPC_BEXTI = 0x48005013, OPC_BGE = 0x5063, OPC_BGEU = 0x7063, OPC_BLT = 0x4063, @@ -2354,9 +2355,15 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } /* FALLTHRU */ case INDEX_op_extract_i32: - if (a2 == 0 && args[3] == 16) { + switch (args[3]) { + case 1: + tcg_out_opc_imm(s, OPC_BEXTI, a0, a1, a2); + break; + case 16: + tcg_debug_assert(a2 == 0); tcg_out_ext16u(s, a0, a1); - } else { + break; + default: g_assert_not_reached(); } break; From 8095f652f237df90b7b01e025cbcebe95065541c Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 9 Jan 2025 04:26:15 +0100 Subject: [PATCH 1131/2892] linux-user: Add missing /proc/cpuinfo fields for sparc Add some missing fields which may be parsed by userspace applications. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: --- linux-user/sparc/target_proc.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/linux-user/sparc/target_proc.h b/linux-user/sparc/target_proc.h index 3bb3134a47..744fa10730 100644 --- a/linux-user/sparc/target_proc.h +++ b/linux-user/sparc/target_proc.h @@ -8,7 +8,25 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) { - dprintf(fd, "type\t\t: sun4u\n"); + int i, num_cpus; + const char *cpu_type; + + num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if (cpu_env->def.features & CPU_FEATURE_HYPV) { + cpu_type = "sun4v"; + } else { + cpu_type = "sun4u"; + } + + dprintf(fd, "cpu\t\t: %s (QEMU)\n", cpu_env->def.name); + dprintf(fd, "type\t\t: %s\n", cpu_type); + dprintf(fd, "ncpus probed\t: %d\n", num_cpus); + dprintf(fd, "ncpus active\t: %d\n", num_cpus); + dprintf(fd, "State:\n"); + for (i = 0; i < num_cpus; i++) { + dprintf(fd, "CPU%d:\t\t: online\n", i); + } + return 0; } #define HAVE_ARCH_PROC_CPUINFO From 552260aeae26edebb1d660dae1e0c76fa234364b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:30 +0000 Subject: [PATCH 1132/2892] semihosting: add guest_error logging for failed opens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This usually indicates the semihosting call was expecting to find something but didn't. Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-2-alex.bennee@linaro.org> --- semihosting/syscalls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index c40348f996..f6451d9bb0 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "gdbstub/syscalls.h" #include "semihosting/guestfd.h" @@ -287,6 +288,7 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, ret = open(p, host_flags, mode); if (ret < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to open %s\n", __func__, p); complete(cs, -1, errno); } else { int guestfd = alloc_guestfd(); From 23482ccd6bff4398643a90ca1c890f91f003e2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 16:02:31 +0000 Subject: [PATCH 1133/2892] semihosting/uaccess: Briefly document returned values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since it is not obvious the get/put_user*() methods can return an error, add brief docstrings about it. Also remind to use *unlock_user() when appropriate. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241212115413.42109-1-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-3-alex.bennee@linaro.org> --- include/semihosting/uaccess.h | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h index c2fa5a655d..6bc90b12d6 100644 --- a/include/semihosting/uaccess.h +++ b/include/semihosting/uaccess.h @@ -19,41 +19,96 @@ #include "exec/tswap.h" #include "exec/page-protection.h" +/** + * get_user_u64: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_u64(val, addr) \ ({ uint64_t val_ = 0; \ int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ &val_, sizeof(val_), 0); \ (val) = tswap64(val_); ret_; }) +/** + * get_user_u32: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_u32(val, addr) \ ({ uint32_t val_ = 0; \ int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ &val_, sizeof(val_), 0); \ (val) = tswap32(val_); ret_; }) +/** + * get_user_u8: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_u8(val, addr) \ ({ uint8_t val_ = 0; \ int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ &val_, sizeof(val_), 0); \ (val) = val_; ret_; }) +/** + * get_user_ual: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_ual(arg, p) get_user_u32(arg, p) +/** + * put_user_u64: + * + * Returns: 0 on success, -1 on error. + */ #define put_user_u64(val, addr) \ ({ uint64_t val_ = tswap64(val); \ cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) +/** + * put_user_u32: + * + * Returns: 0 on success, -1 on error. + */ #define put_user_u32(val, addr) \ ({ uint32_t val_ = tswap32(val); \ cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) +/** + * put_user_ual: + * + * Returns: 0 on success, -1 on error. + */ #define put_user_ual(arg, p) put_user_u32(arg, p) +/** + * uaccess_lock_user: + * + * The returned pointer should be freed using uaccess_unlock_user(). + */ void *uaccess_lock_user(CPUArchState *env, target_ulong addr, target_ulong len, bool copy); +/** + * lock_user: + * + * The returned pointer should be freed using unlock_user(). + */ #define lock_user(type, p, len, copy) uaccess_lock_user(env, p, len, copy) +/** + * uaccess_lock_user_string: + * + * The returned string should be freed using uaccess_unlock_user(). + */ char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr); +/** + * uaccess_lock_user_string: + * + * The returned string should be freed using unlock_user(). + */ #define lock_user_string(p) uaccess_lock_user_string(env, p) void uaccess_unlock_user(CPUArchState *env, void *p, From 056c4059e8a64d006dc274ab8279e06e47e2920d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 16:02:32 +0000 Subject: [PATCH 1134/2892] semihosting/syscalls: Include missing 'exec/cpu-defs.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit target_ulong is defined in each target "cpu-param.h", itself included by "exec/cpu-defs.h". Include the latter in order to avoid when refactoring: include/semihosting/syscalls.h:26:24: error: unknown type name 'target_ulong' 26 | target_ulong fname, target_ulong fname_len, | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250103171037.11265-2-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-4-alex.bennee@linaro.org> --- include/semihosting/syscalls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index b5937c619a..6627c45fb2 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -9,6 +9,7 @@ #ifndef SEMIHOSTING_SYSCALLS_H #define SEMIHOSTING_SYSCALLS_H +#include "exec/cpu-defs.h" #include "gdbstub/syscalls.h" /* From d2f28a0ce8d2e09c0bc9c323b492d2ee70bbdc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 16:02:33 +0000 Subject: [PATCH 1135/2892] semihosting/uaccess: Include missing 'exec/cpu-all.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TLB_INVALID_MASK is defined in "exec/cpu-all.h". Include it in order to avoid when refactoring: ../semihosting/uaccess.c:41:21: error: use of undeclared identifier 'TLB_INVALID_MASK' 41 | if (flags & TLB_INVALID_MASK) { | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250103171037.11265-3-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-5-alex.bennee@linaro.org> --- semihosting/uaccess.c | 1 + 1 file changed, 1 insertion(+) diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index dc587d73bc..382a366ce3 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "exec/cpu-all.h" #include "exec/exec-all.h" #include "semihosting/uaccess.h" From 847343cfbf80bd221f42595a0038a8d5e7ab7088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 16:02:34 +0000 Subject: [PATCH 1136/2892] semihosting/arm-compat: Include missing 'cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARM semihosting implementations in "common-semi-target.h" must de-reference the target CPUArchState, which is declared in each target "cpu.h" header. Include it in order to avoid when refactoring: In file included from ../../semihosting/arm-compat-semi.c:169: ../target/riscv/common-semi-target.h:16:5: error: use of undeclared identifier 'RISCVCPU' 16 | RISCVCPU *cpu = RISCV_CPU(cs); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250103171037.11265-4-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-6-alex.bennee@linaro.org> --- semihosting/arm-compat-semi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index d78c6428b9..86e5260e50 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -166,6 +166,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif +#include "cpu.h" #include "common-semi-target.h" /* From 57792106562417ba03a1ac0f2a5afc3eb63c5d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 16:02:35 +0000 Subject: [PATCH 1137/2892] semihosting/console: Avoid including 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPUState structure is declared in "hw/core/cpu.h", the EXCP_HALTED definition in "exec/cpu-common.h". Both headers are indirectly include by "cpu.h". In order to remove "cpu.h" from "semihosting/console.h", explicitly include them in console.c, otherwise we'd get: ../semihosting/console.c:88:11: error: incomplete definition of type 'struct CPUState' 88 | cs->exception_index = EXCP_HALTED; | ~~^ ../semihosting/console.c:88:31: error: use of undeclared identifier 'EXCP_HALTED' 88 | cs->exception_index = EXCP_HALTED; | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250103171037.11265-5-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-7-alex.bennee@linaro.org> --- include/semihosting/console.h | 2 -- semihosting/console.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index bd78e5f03f..1c12e178ee 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -9,8 +9,6 @@ #ifndef SEMIHOST_CONSOLE_H #define SEMIHOST_CONSOLE_H -#include "cpu.h" - /** * qemu_semihosting_console_read: * @cs: CPUState diff --git a/semihosting/console.c b/semihosting/console.c index 60102bbab6..c3683a1566 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -18,14 +18,15 @@ #include "qemu/osdep.h" #include "semihosting/semihost.h" #include "semihosting/console.h" +#include "exec/cpu-common.h" #include "exec/gdbstub.h" -#include "exec/exec-all.h" #include "qemu/log.h" #include "chardev/char.h" #include "chardev/char-fe.h" #include "qemu/main-loop.h" #include "qapi/error.h" #include "qemu/fifo8.h" +#include "hw/core/cpu.h" /* Access to this structure is protected by the BQL */ typedef struct SemihostingConsole { From bb0c5be8e907511c7f05f45e820c548ceef25b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 16:02:36 +0000 Subject: [PATCH 1138/2892] semihosting/meson: Build config.o and console.o once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit config.c and console.c don't use any target specific headers anymore, move them from specific_ss[] to system_ss[] so they are built once, but will also be linked once, removing global symbol clash in a single QEMU binary. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250103171037.11265-6-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-8-alex.bennee@linaro.org> --- semihosting/meson.build | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/semihosting/meson.build b/semihosting/meson.build index 34933e5a19..86f5004bed 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -4,13 +4,16 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( )) specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( - 'config.c', - 'console.c', 'uaccess.c', )) common_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_false: files('stubs-all.c')) -system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_false: files('stubs-system.c')) +system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_true: files( + 'config.c', + 'console.c', +), if_false: files( + 'stubs-system.c', +)) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], if_true: files('arm-compat-semi.c')) From 77e911d0c76e91f1566afb9e76f05aee50f08e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:37 +0000 Subject: [PATCH 1139/2892] system/vl: more error exit into config enumeration code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of the failures to configure devices will result in QEMU exiting with an error code. In preparation for passing Error * down the chain re-name the iterator to foreach_device_config_or_exit and exit using &error_fatal instead of returning a failure indication. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-9-alex.bennee@linaro.org> --- system/vl.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/system/vl.c b/system/vl.c index be029c52ef..22c1444da4 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1307,7 +1307,15 @@ static void add_device_config(int type, const char *cmdline) QTAILQ_INSERT_TAIL(&device_configs, conf, next); } -static int foreach_device_config(int type, int (*func)(const char *cmdline)) +/** + * foreach_device_config_or_exit(): process per-device configs + * @type: device_config type + * @func: device specific config function, returning pass/fail + * + * Any failure is fatal and we exit with an error message. + */ +static void foreach_device_config_or_exit(int type, + int (*func)(const char *cmdline)) { struct device_config *conf; int rc; @@ -1319,10 +1327,10 @@ static int foreach_device_config(int type, int (*func)(const char *cmdline)) rc = func(conf->cmdline); loc_pop(&conf->loc); if (rc) { - return rc; + error_setg(&error_fatal, "failed to configure: %s", conf->cmdline); + exit(1); } } - return 0; } static void qemu_disable_default_devices(void) @@ -2044,12 +2052,9 @@ static void qemu_create_late_backends(void) qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, &error_fatal); - if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) - exit(1); - if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) - exit(1); - if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) - exit(1); + foreach_device_config_or_exit(DEV_SERIAL, serial_parse); + foreach_device_config_or_exit(DEV_PARALLEL, parallel_parse); + foreach_device_config_or_exit(DEV_DEBUGCON, debugcon_parse); /* now chardevs have been created we may have semihosting to connect */ qemu_semihosting_chardev_init(); @@ -2667,8 +2672,7 @@ static void qemu_create_cli_devices(void) /* init USB devices */ if (machine_usb(current_machine)) { - if (foreach_device_config(DEV_USB, usb_parse) < 0) - exit(1); + foreach_device_config_or_exit(DEV_USB, usb_parse); } /* init generic devices */ @@ -2715,10 +2719,8 @@ static bool qemu_machine_creation_done(Error **errp) exit(1); } - if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { - error_setg(errp, "could not start gdbserver"); - return false; - } + foreach_device_config_or_exit(DEV_GDB, gdbserver_start); + if (!vga_interface_created && !default_vga && vga_interface_type != VGA_NONE) { warn_report("A -vga option was passed but this machine " From 05cdd648a846bd60e300fcfa1eabf8f20e589cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:38 +0000 Subject: [PATCH 1140/2892] system: squash usb_parse into a single function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need to wrap usb_device_add as usb_parse is already gated with an if (machine_usb(current_machine)) check. Instead just assert and directly fail if usbdevice_create returns NULL. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-10-alex.bennee@linaro.org> --- system/vl.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/system/vl.c b/system/vl.c index 22c1444da4..02795c5135 100644 --- a/system/vl.c +++ b/system/vl.c @@ -811,29 +811,15 @@ static void configure_msg(QemuOpts *opts) /***********************************************************/ /* USB devices */ -static int usb_device_add(const char *devname) -{ - USBDevice *dev = NULL; - - if (!machine_usb(current_machine)) { - return -1; - } - - dev = usbdevice_create(devname); - if (!dev) - return -1; - - return 0; -} - static int usb_parse(const char *cmdline) { - int r; - r = usb_device_add(cmdline); - if (r < 0) { + g_assert(machine_usb(current_machine)); + + if (!usbdevice_create(cmdline)) { error_report("could not add USB device '%s'", cmdline); + return -1; } - return r; + return 0; } /***********************************************************/ From c0e6b8b798bee5d8772ca8db19638ec89b47c946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:39 +0000 Subject: [PATCH 1141/2892] system: propagate Error to gdbserver_start (and other device setups) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This started as a clean-up to properly pass a Error handler to the gdbserver_start so we could do the right thing for command line and HMP invocations. Now that we have cleaned up foreach_device_config_or_exit() in earlier patches we can further simplify by it by passing &error_fatal instead of checking the return value. Having a return value is still useful for HMP though so tweak the return to use a simple bool instead. Reviewed-by: Pierrick Bouvier Acked-by: Ilya Leoshkevich Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-11-alex.bennee@linaro.org> --- bsd-user/main.c | 2 +- gdbstub/system.c | 22 +++++++++------- gdbstub/user.c | 22 +++++++++------- include/exec/gdbstub.h | 8 +++++- linux-user/main.c | 6 +---- monitor/hmp-cmds.c | 2 +- system/vl.c | 59 ++++++++++++++++++++---------------------- 7 files changed, 62 insertions(+), 59 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 0a5bc57836..b2f6a9be2f 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -628,7 +628,7 @@ int main(int argc, char **argv) target_cpu_init(env, regs); if (gdbstub) { - gdbserver_start(gdbstub); + gdbserver_start(gdbstub, &error_fatal); gdb_handlesig(cpu, 0, NULL, NULL, 0); } cpu_loop(env); diff --git a/gdbstub/system.c b/gdbstub/system.c index 2d9fdff2fe..8ce79fa88c 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -330,26 +330,27 @@ static void create_processes(GDBState *s) gdb_create_default_process(s); } -int gdbserver_start(const char *device) +bool gdbserver_start(const char *device, Error **errp) { Chardev *chr = NULL; Chardev *mon_chr; g_autoptr(GString) cs = g_string_new(device); if (!first_cpu) { - error_report("gdbstub: meaningless to attach gdb to a " - "machine without any CPU."); - return -1; + error_setg(errp, "gdbstub: meaningless to attach gdb to a " + "machine without any CPU."); + return false; } if (!gdb_supports_guest_debug()) { - error_report("gdbstub: current accelerator doesn't " - "support guest debugging"); - return -1; + error_setg(errp, "gdbstub: current accelerator doesn't " + "support guest debugging"); + return false; } if (cs->len == 0) { - return -1; + error_setg(errp, "gdbstub: missing connection string"); + return false; } trace_gdbstub_op_start(cs->str); @@ -374,7 +375,8 @@ int gdbserver_start(const char *device) */ chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); if (!chr) { - return -1; + error_setg(errp, "gdbstub: couldn't create chardev"); + return false; } } @@ -406,7 +408,7 @@ int gdbserver_start(const char *device) gdbserver_system_state.mon_chr = mon_chr; gdb_syscall_reset(); - return 0; + return true; } static void register_types(void) diff --git a/gdbstub/user.c b/gdbstub/user.c index 0b4bfa9c48..c2bdfc3d49 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -13,6 +13,7 @@ #include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/sockets.h" +#include "qapi/error.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/gdbstub.h" @@ -372,14 +373,14 @@ static bool gdb_accept_tcp(int gdb_fd) return true; } -static int gdbserver_open_port(int port) +static int gdbserver_open_port(int port, Error **errp) { struct sockaddr_in sockaddr; int fd, ret; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { - perror("socket"); + error_setg_errno(errp, errno, "Failed to create socket"); return -1; } qemu_set_cloexec(fd); @@ -391,13 +392,13 @@ static int gdbserver_open_port(int port) sockaddr.sin_addr.s_addr = 0; ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { - perror("bind"); + error_setg_errno(errp, errno, "Failed to bind socket"); close(fd); return -1; } ret = listen(fd, 1); if (ret < 0) { - perror("listen"); + error_setg_errno(errp, errno, "Failed to listen to socket"); close(fd); return -1; } @@ -405,31 +406,32 @@ static int gdbserver_open_port(int port) return fd; } -int gdbserver_start(const char *port_or_path) +bool gdbserver_start(const char *port_or_path, Error **errp) { int port = g_ascii_strtoull(port_or_path, NULL, 10); int gdb_fd; if (port > 0) { - gdb_fd = gdbserver_open_port(port); + gdb_fd = gdbserver_open_port(port, errp); } else { gdb_fd = gdbserver_open_socket(port_or_path); } if (gdb_fd < 0) { - return -1; + return false; } if (port > 0 && gdb_accept_tcp(gdb_fd)) { - return 0; + return true; } else if (gdb_accept_socket(gdb_fd)) { gdbserver_user_state.socket_path = g_strdup(port_or_path); - return 0; + return true; } /* gone wrong */ close(gdb_fd); - return -1; + error_setg(errp, "gdbstub: failed to accept connection"); + return false; } void gdbserver_fork_start(void) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index d73f424f56..0675b0b646 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -49,12 +49,18 @@ void gdb_unregister_coprocessor_all(CPUState *cpu); /** * gdbserver_start: start the gdb server * @port_or_device: connection spec for gdb + * @errp: error handle * * For CONFIG_USER this is either a tcp port or a path to a fifo. For * system emulation you can use a full chardev spec for your gdbserver * port. + * + * The error handle should be either &error_fatal (for start-up) or + * &error_warn (for QMP/HMP initiated sessions). + * + * Returns true when server successfully started. */ -int gdbserver_start(const char *port_or_device); +bool gdbserver_start(const char *port_or_device, Error **errp); /** * gdb_feature_builder_init() - Initialize GDBFeatureBuilder. diff --git a/linux-user/main.c b/linux-user/main.c index b97634a32d..7198fa0986 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1023,11 +1023,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); if (gdbstub) { - if (gdbserver_start(gdbstub) < 0) { - fprintf(stderr, "qemu: could not open gdbserver on %s\n", - gdbstub); - exit(EXIT_FAILURE); - } + gdbserver_start(gdbstub, &error_fatal); gdb_handlesig(cpu, 0, NULL, NULL, 0); } diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 80b2e5ff9f..0aa22e1ae2 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -285,7 +285,7 @@ void hmp_gdbserver(Monitor *mon, const QDict *qdict) device = "tcp::" DEFAULT_GDBSTUB_PORT; } - if (gdbserver_start(device) < 0) { + if (!gdbserver_start(device, &error_warn)) { monitor_printf(mon, "Could not open gdbserver on device '%s'\n", device); } else if (strcmp(device, "none") == 0) { diff --git a/system/vl.c b/system/vl.c index 02795c5135..c567826718 100644 --- a/system/vl.c +++ b/system/vl.c @@ -811,15 +811,15 @@ static void configure_msg(QemuOpts *opts) /***********************************************************/ /* USB devices */ -static int usb_parse(const char *cmdline) +static bool usb_parse(const char *cmdline, Error **errp) { g_assert(machine_usb(current_machine)); if (!usbdevice_create(cmdline)) { - error_report("could not add USB device '%s'", cmdline); - return -1; + error_setg(errp, "could not add USB device '%s'", cmdline); + return false; } - return 0; + return true; } /***********************************************************/ @@ -1298,24 +1298,21 @@ static void add_device_config(int type, const char *cmdline) * @type: device_config type * @func: device specific config function, returning pass/fail * - * Any failure is fatal and we exit with an error message. + * @func is called with the &error_fatal handler so device specific + * error messages can be reported on failure. */ static void foreach_device_config_or_exit(int type, - int (*func)(const char *cmdline)) + bool (*func)(const char *cmdline, + Error **errp)) { struct device_config *conf; - int rc; QTAILQ_FOREACH(conf, &device_configs, next) { if (conf->type != type) continue; loc_push_restore(&conf->loc); - rc = func(conf->cmdline); + func(conf->cmdline, &error_fatal); loc_pop(&conf->loc); - if (rc) { - error_setg(&error_fatal, "failed to configure: %s", conf->cmdline); - exit(1); - } } } @@ -1446,7 +1443,7 @@ static void qemu_create_default_devices(void) } } -static int serial_parse(const char *devname) +static bool serial_parse(const char *devname, Error **errp) { int index = num_serial_hds; @@ -1461,13 +1458,13 @@ static int serial_parse(const char *devname) serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!serial_hds[index]) { - error_report("could not connect serial device" - " to character backend '%s'", devname); - return -1; + error_setg(errp, "could not connect serial device" + " to character backend '%s'", devname); + return false; } } num_serial_hds++; - return 0; + return true; } Chardev *serial_hd(int i) @@ -1479,44 +1476,44 @@ Chardev *serial_hd(int i) return NULL; } -static int parallel_parse(const char *devname) +static bool parallel_parse(const char *devname, Error **errp) { static int index = 0; char label[32]; if (strcmp(devname, "none") == 0) - return 0; + return true; if (index == MAX_PARALLEL_PORTS) { - error_report("too many parallel ports"); - exit(1); + error_setg(errp, "too many parallel ports"); + return false; } snprintf(label, sizeof(label), "parallel%d", index); parallel_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!parallel_hds[index]) { - error_report("could not connect parallel device" - " to character backend '%s'", devname); - return -1; + error_setg(errp, "could not connect parallel device" + " to character backend '%s'", devname); + return false; } index++; - return 0; + return true; } -static int debugcon_parse(const char *devname) +static bool debugcon_parse(const char *devname, Error **errp) { QemuOpts *opts; if (!qemu_chr_new_mux_mon("debugcon", devname, NULL)) { - error_report("invalid character backend '%s'", devname); - exit(1); + error_setg(errp, "invalid character backend '%s'", devname); + return false; } opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); if (!opts) { - error_report("already have a debugcon device"); - exit(1); + error_setg(errp, "already have a debugcon device"); + return false; } qemu_opt_set(opts, "driver", "isa-debugcon", &error_abort); qemu_opt_set(opts, "chardev", "debugcon", &error_abort); - return 0; + return true; } static gint machine_class_cmp(gconstpointer a, gconstpointer b) From c7c430065a5e240bd206b8edb4949256fb528299 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:40 +0000 Subject: [PATCH 1142/2892] tests/tcg/plugins/insn: remove unused callback parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-12-alex.bennee@linaro.org> --- tests/tcg/plugins/insn.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/tcg/plugins/insn.c b/tests/tcg/plugins/insn.c index baf2d07205..0c723cb9ed 100644 --- a/tests/tcg/plugins/insn.c +++ b/tests/tcg/plugins/insn.c @@ -150,10 +150,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); } else { - uint64_t vaddr = qemu_plugin_insn_vaddr(insn); qemu_plugin_register_vcpu_insn_exec_cb( - insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, - GUINT_TO_POINTER(vaddr)); + insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, NULL); } if (do_size) { From d0737068e11cc647c85918a0c57f00da27ec14b3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:41 +0000 Subject: [PATCH 1143/2892] contrib/plugins/howvec: ensure we don't regress if this plugin is extended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-13-alex.bennee@linaro.org> --- contrib/plugins/howvec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 9be67f7453..2aa9029c3f 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -253,6 +253,8 @@ static struct qemu_plugin_scoreboard *find_counter( int i; uint64_t *cnt = NULL; uint32_t opcode = 0; + /* if opcode is greater than 32 bits, we should refactor insn hash table. */ + G_STATIC_ASSERT(sizeof(opcode) == sizeof(uint32_t)); InsnClassExecCount *class = NULL; /* @@ -284,7 +286,7 @@ static struct qemu_plugin_scoreboard *find_counter( g_mutex_lock(&lock); icount = (InsnExecCount *) g_hash_table_lookup(insns, - GUINT_TO_POINTER(opcode)); + (gpointer)(intptr_t) opcode); if (!icount) { icount = g_new0(InsnExecCount, 1); @@ -295,8 +297,7 @@ static struct qemu_plugin_scoreboard *find_counter( qemu_plugin_scoreboard_new(sizeof(uint64_t)); icount->count = qemu_plugin_scoreboard_u64(score); - g_hash_table_insert(insns, GUINT_TO_POINTER(opcode), - (gpointer) icount); + g_hash_table_insert(insns, (gpointer)(intptr_t) opcode, icount); } g_mutex_unlock(&lock); From b2a3ebb72c30e649cac9670ac81770a297271d0f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:42 +0000 Subject: [PATCH 1144/2892] tests/tcg/plugins/syscall: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-4-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-14-alex.bennee@linaro.org> --- tests/tcg/plugins/syscall.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c index ff452178b1..47aad55fc1 100644 --- a/tests/tcg/plugins/syscall.c +++ b/tests/tcg/plugins/syscall.c @@ -76,12 +76,12 @@ static int64_t write_sysno = -1; static SyscallStats *get_or_create_entry(int64_t num) { SyscallStats *entry = - (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num)); + (SyscallStats *) g_hash_table_lookup(statistics, &num); if (!entry) { entry = g_new0(SyscallStats, 1); entry->num = num; - g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry); + g_hash_table_insert(statistics, &entry->num, entry); } return entry; @@ -232,7 +232,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } if (!do_print) { - statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); + statistics = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); } if (do_log_writes) { From 376bc151c7e0f535a783e72fc75dbbb07d0594b4 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:43 +0000 Subject: [PATCH 1145/2892] tests/tcg/plugins/mem: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-5-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-15-alex.bennee@linaro.org> --- tests/tcg/plugins/mem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index b0fa8a9f27..d87d6628e0 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -135,14 +135,14 @@ static void update_region_info(uint64_t region, uint64_t offset, g_assert(offset + size <= region_size); g_mutex_lock(&lock); - ri = (RegionInfo *) g_hash_table_lookup(regions, GUINT_TO_POINTER(region)); + ri = (RegionInfo *) g_hash_table_lookup(regions, ®ion); if (!ri) { ri = g_new0(RegionInfo, 1); ri->region_address = region; ri->data = g_malloc0(region_size); ri->seen_all = true; - g_hash_table_insert(regions, GUINT_TO_POINTER(region), (gpointer) ri); + g_hash_table_insert(regions, &ri->region_address, ri); } if (is_store) { @@ -392,7 +392,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, if (do_region_summary) { region_mask = (region_size - 1); - regions = g_hash_table_new(NULL, g_direct_equal); + regions = g_hash_table_new(g_int64_hash, g_int64_equal); } counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); From 03be743f4f9fbcad2a80e89157d3255c3d3774f3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:44 +0000 Subject: [PATCH 1146/2892] contrib/plugins/stoptrigger: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241217224306.2900490-6-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-16-alex.bennee@linaro.org> --- contrib/plugins/stoptrigger.c | 48 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/contrib/plugins/stoptrigger.c b/contrib/plugins/stoptrigger.c index 03ee22f4c6..b3a6ed66a7 100644 --- a/contrib/plugins/stoptrigger.c +++ b/contrib/plugins/stoptrigger.c @@ -21,9 +21,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; /* Scoreboard to track executed instructions count */ typedef struct { uint64_t insn_count; + uint64_t current_pc; } InstructionsCount; static struct qemu_plugin_scoreboard *insn_count_sb; static qemu_plugin_u64 insn_count; +static qemu_plugin_u64 current_pc; static uint64_t icount; static int icount_exit_code; @@ -34,6 +36,11 @@ static bool exit_on_address; /* Map trigger addresses to exit code */ static GHashTable *addrs_ht; +typedef struct { + uint64_t exit_addr; + int exit_code; +} ExitInfo; + static void exit_emulation(int return_code, char *message) { qemu_plugin_outs(message); @@ -43,23 +50,18 @@ static void exit_emulation(int return_code, char *message) static void exit_icount_reached(unsigned int cpu_index, void *udata) { - uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + uint64_t insn_vaddr = qemu_plugin_u64_get(current_pc, cpu_index); char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n", insn_vaddr); - exit_emulation(icount_exit_code, msg); } static void exit_address_reached(unsigned int cpu_index, void *udata) { - uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); - char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr); - int exit_code; - - exit_code = GPOINTER_TO_INT( - g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr))); - - exit_emulation(exit_code, msg); + ExitInfo *ei = udata; + g_assert(ei); + char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", ei->exit_addr); + exit_emulation(ei->exit_code, msg); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -67,23 +69,25 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) size_t tb_n = qemu_plugin_tb_n_insns(tb); for (size_t i = 0; i < tb_n; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn)); + uint64_t insn_vaddr = qemu_plugin_insn_vaddr(insn); if (exit_on_icount) { /* Increment and check scoreboard for each instruction */ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_STORE_U64, current_pc, insn_vaddr); qemu_plugin_register_vcpu_insn_exec_cond_cb( insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS, - QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr); + QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, NULL); } if (exit_on_address) { - if (g_hash_table_contains(addrs_ht, insn_vaddr)) { + ExitInfo *ei = g_hash_table_lookup(addrs_ht, &insn_vaddr); + if (ei) { /* Exit triggered by address */ qemu_plugin_register_vcpu_insn_exec_cb( - insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, - insn_vaddr); + insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, ei); } } } @@ -99,11 +103,13 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - addrs_ht = g_hash_table_new(NULL, g_direct_equal); + addrs_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount)); insn_count = qemu_plugin_scoreboard_u64_in_struct( insn_count_sb, InstructionsCount, insn_count); + current_pc = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, current_pc); for (int i = 0; i < argc; i++) { char *opt = argv[i]; @@ -124,13 +130,13 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, exit_on_icount = true; } else if (g_strcmp0(tokens[0], "addr") == 0) { g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2); - uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); - int exit_code = 0; + ExitInfo *ei = g_malloc(sizeof(ExitInfo)); + ei->exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); + ei->exit_code = 0; if (addr_tokens[1]) { - exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); + ei->exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); } - g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr), - GINT_TO_POINTER(exit_code)); + g_hash_table_insert(addrs_ht, &ei->exit_addr, ei); exit_on_address = true; } else { fprintf(stderr, "option parsing failed: %s\n", opt); From aa47f448b5e42184f7b99cf8139646dd0f362e0d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:45 +0000 Subject: [PATCH 1147/2892] contrib/plugins/cache: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241217224306.2900490-7-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-17-alex.bennee@linaro.org> --- contrib/plugins/cache.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index 512ef6776b..7baff86860 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -208,7 +208,7 @@ static int fifo_get_first_block(Cache *cache, int set) static void fifo_update_on_miss(Cache *cache, int set, int blk_idx) { GQueue *q = cache->sets[set].fifo_queue; - g_queue_push_head(q, GINT_TO_POINTER(blk_idx)); + g_queue_push_head(q, (gpointer)(intptr_t) blk_idx); } static void fifo_destroy(Cache *cache) @@ -471,13 +471,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) n_insns = qemu_plugin_tb_n_insns(tb); for (i = 0; i < n_insns; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - uint64_t effective_addr; - - if (sys) { - effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn); - } else { - effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn); - } + uint64_t effective_addr = sys ? (uintptr_t) qemu_plugin_insn_haddr(insn) : + qemu_plugin_insn_vaddr(insn); /* * Instructions might get translated multiple times, we do not create @@ -485,14 +480,13 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * entry from the hash table and register it for the callback again. */ g_mutex_lock(&hashtable_lock); - data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr)); + data = g_hash_table_lookup(miss_ht, &effective_addr); if (data == NULL) { data = g_new0(InsnData, 1); data->disas_str = qemu_plugin_insn_disas(insn); data->symbol = qemu_plugin_insn_symbol(insn); data->addr = effective_addr; - g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr), - (gpointer) data); + g_hash_table_insert(miss_ht, &data->addr, data); } g_mutex_unlock(&hashtable_lock); @@ -853,7 +847,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); - miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free); + miss_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, insn_free); return 0; } From 2fb2aa0bb0ed173a84d756d214c3bb4f1e9dc3c7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:46 +0000 Subject: [PATCH 1148/2892] contrib/plugins/hotblocks: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20241217224306.2900490-8-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-18-alex.bennee@linaro.org> --- contrib/plugins/hotblocks.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/contrib/plugins/hotblocks.c b/contrib/plugins/hotblocks.c index 02bc5078bd..f12bfb7a26 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -29,7 +29,7 @@ static guint64 limit = 20; * * The internals of the TCG are not exposed to plugins so we can only * get the starting PC for each block. We cheat this slightly by - * xor'ing the number of instructions to the hash to help + * checking the number of instructions as well to help * differentiate. */ typedef struct { @@ -50,6 +50,20 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) return count_a > count_b ? -1 : 1; } +static guint exec_count_hash(gconstpointer v) +{ + const ExecCount *e = v; + return e->start_addr ^ e->insns; +} + +static gboolean exec_count_equal(gconstpointer v1, gconstpointer v2) +{ + const ExecCount *ea = v1; + const ExecCount *eb = v2; + return (ea->start_addr == eb->start_addr) && + (ea->insns == eb->insns); +} + static void exec_count_free(gpointer key, gpointer value, gpointer user_data) { ExecCount *cnt = value; @@ -91,7 +105,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { - hotblocks = g_hash_table_new(NULL, g_direct_equal); + hotblocks = g_hash_table_new(exec_count_hash, exec_count_equal); } static void vcpu_tb_exec(unsigned int cpu_index, void *udata) @@ -111,10 +125,15 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) ExecCount *cnt; uint64_t pc = qemu_plugin_tb_vaddr(tb); size_t insns = qemu_plugin_tb_n_insns(tb); - uint64_t hash = pc ^ insns; g_mutex_lock(&lock); - cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); + { + ExecCount e; + e.start_addr = pc; + e.insns = insns; + cnt = (ExecCount *) g_hash_table_lookup(hotblocks, &e); + } + if (cnt) { cnt->trans_count++; } else { @@ -123,7 +142,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) cnt->trans_count = 1; cnt->insns = insns; cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); - g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt); + g_hash_table_insert(hotblocks, cnt, cnt); } g_mutex_unlock(&lock); From a5555b254820b57ed978f546413a70ddb794c472 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:47 +0000 Subject: [PATCH 1149/2892] contrib/plugins/cflow: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-9-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-19-alex.bennee@linaro.org> --- contrib/plugins/cflow.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/contrib/plugins/cflow.c b/contrib/plugins/cflow.c index b39974d1cf..930ecb46fc 100644 --- a/contrib/plugins/cflow.c +++ b/contrib/plugins/cflow.c @@ -76,6 +76,8 @@ typedef struct { /* We use this to track the current execution state */ typedef struct { + /* address of current translated block */ + uint64_t tb_pc; /* address of end of block */ uint64_t end_block; /* next pc after end of block */ @@ -85,6 +87,7 @@ typedef struct { } VCPUScoreBoard; /* descriptors for accessing the above scoreboard */ +static qemu_plugin_u64 tb_pc; static qemu_plugin_u64 end_block; static qemu_plugin_u64 pc_after_block; static qemu_plugin_u64 last_pc; @@ -189,10 +192,11 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { g_mutex_init(&node_lock); - nodes = g_hash_table_new(NULL, g_direct_equal); + nodes = g_hash_table_new(g_int64_hash, g_int64_equal); state = qemu_plugin_scoreboard_new(sizeof(VCPUScoreBoard)); /* score board declarations */ + tb_pc = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, tb_pc); end_block = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, end_block); pc_after_block = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, @@ -215,10 +219,10 @@ static NodeData *fetch_node(uint64_t addr, bool create_if_not_found) NodeData *node = NULL; g_mutex_lock(&node_lock); - node = (NodeData *) g_hash_table_lookup(nodes, (gconstpointer) addr); + node = (NodeData *) g_hash_table_lookup(nodes, &addr); if (!node && create_if_not_found) { node = create_node(addr); - g_hash_table_insert(nodes, (gpointer) addr, (gpointer) node); + g_hash_table_insert(nodes, &node->addr, node); } g_mutex_unlock(&node_lock); return node; @@ -234,7 +238,7 @@ static void vcpu_tb_branched_exec(unsigned int cpu_index, void *udata) uint64_t lpc = qemu_plugin_u64_get(last_pc, cpu_index); uint64_t ebpc = qemu_plugin_u64_get(end_block, cpu_index); uint64_t npc = qemu_plugin_u64_get(pc_after_block, cpu_index); - uint64_t pc = GPOINTER_TO_UINT(udata); + uint64_t pc = qemu_plugin_u64_get(tb_pc, cpu_index); /* return early for address 0 */ if (!lpc) { @@ -305,10 +309,11 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * handle both early block exits and normal branches in the * callback if we hit it. */ - gpointer udata = GUINT_TO_POINTER(pc); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_STORE_U64, tb_pc, pc); qemu_plugin_register_vcpu_tb_exec_cond_cb( tb, vcpu_tb_branched_exec, QEMU_PLUGIN_CB_NO_REGS, - QEMU_PLUGIN_COND_NE, pc_after_block, pc, udata); + QEMU_PLUGIN_COND_NE, pc_after_block, pc, NULL); /* * Now we can set start/end for this block so the next block can From cab85a63e0c0f0ae3f2c8d0b9dc2770c5d21cf81 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:48 +0000 Subject: [PATCH 1150/2892] contrib/plugins/hwprofile: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-10-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-20-alex.bennee@linaro.org> --- contrib/plugins/hwprofile.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c index 739ac0c66b..2a4cbc47d4 100644 --- a/contrib/plugins/hwprofile.c +++ b/contrib/plugins/hwprofile.c @@ -43,6 +43,8 @@ typedef struct { static GMutex lock; static GHashTable *devices; +static struct qemu_plugin_scoreboard *source_pc_scoreboard; +static qemu_plugin_u64 source_pc; /* track the access pattern to a piece of HW */ static bool pattern; @@ -159,7 +161,7 @@ static DeviceCounts *new_count(const char *name, uint64_t base) count->name = name; count->base = base; if (pattern || source) { - count->detail = g_hash_table_new(NULL, NULL); + count->detail = g_hash_table_new(g_int64_hash, g_int64_equal); } g_hash_table_insert(devices, (gpointer) name, count); return count; @@ -169,7 +171,7 @@ static IOLocationCounts *new_location(GHashTable *table, uint64_t off_or_pc) { IOLocationCounts *loc = g_new0(IOLocationCounts, 1); loc->off_or_pc = off_or_pc; - g_hash_table_insert(table, (gpointer) off_or_pc, loc); + g_hash_table_insert(table, &loc->off_or_pc, loc); return loc; } @@ -224,12 +226,12 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, /* either track offsets or source of access */ if (source) { - off = (uint64_t) udata; + off = qemu_plugin_u64_get(source_pc, cpu_index); } if (pattern || source) { IOLocationCounts *io_count = g_hash_table_lookup(counts->detail, - (gpointer) off); + &off); if (!io_count) { io_count = new_location(counts->detail, off); } @@ -247,10 +249,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) for (i = 0; i < n; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - gpointer udata = (gpointer) (source ? qemu_plugin_insn_vaddr(insn) : 0); + if (source) { + uint64_t pc = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, rw, QEMU_PLUGIN_INLINE_STORE_U64, + source_pc, pc); + } qemu_plugin_register_vcpu_mem_cb(insn, vcpu_haddr, - QEMU_PLUGIN_CB_NO_REGS, - rw, udata); + QEMU_PLUGIN_CB_NO_REGS, rw, NULL); } } @@ -306,10 +312,9 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, return -1; } - /* Just warn about overflow */ - if (info->system.smp_vcpus > 64 || - info->system.max_vcpus > 64) { - fprintf(stderr, "hwprofile: can only track up to 64 CPUs\n"); + if (source) { + source_pc_scoreboard = qemu_plugin_scoreboard_new(sizeof(uint64_t)); + source_pc = qemu_plugin_scoreboard_u64(source_pc_scoreboard); } plugin_init(); From 645bf0601215979804264f593300644692adcd15 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:49 +0000 Subject: [PATCH 1151/2892] contrib/plugins/hotpages: fix 32-bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-11-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-21-alex.bennee@linaro.org> --- contrib/plugins/hotpages.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/plugins/hotpages.c b/contrib/plugins/hotpages.c index 8316ae50c7..c6e6493719 100644 --- a/contrib/plugins/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -103,7 +103,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { page_mask = (page_size - 1); - pages = g_hash_table_new(NULL, g_direct_equal); + pages = g_hash_table_new(g_int64_hash, g_int64_equal); } static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, @@ -130,12 +130,12 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, page &= ~page_mask; g_mutex_lock(&lock); - count = (PageCounters *) g_hash_table_lookup(pages, GUINT_TO_POINTER(page)); + count = (PageCounters *) g_hash_table_lookup(pages, &page); if (!count) { count = g_new0(PageCounters, 1); count->page_address = page; - g_hash_table_insert(pages, GUINT_TO_POINTER(page), (gpointer) count); + g_hash_table_insert(pages, &count->page_address, count); } if (qemu_plugin_mem_is_store(meminfo)) { count->writes++; From db7a06ade11eb380aeef0b7c204b699878bdd799 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:50 +0000 Subject: [PATCH 1152/2892] configure: reenable plugins by default for 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241217224306.2900490-12-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-22-alex.bennee@linaro.org> --- configure | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/configure b/configure index 18336376bf..02f1dd2311 100755 --- a/configure +++ b/configure @@ -528,25 +528,6 @@ case "$cpu" in ;; esac -# Now we have our CPU_CFLAGS we can check if we are targeting a 32 or -# 64 bit host. - -check_64bit_host() { -cat > $TMPC < Date: Thu, 16 Jan 2025 16:02:51 +0000 Subject: [PATCH 1153/2892] accel/tcg: also suppress asynchronous IRQs for cpu_io_recompile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While it would be technically correct to allow an IRQ to happen (as the offending instruction never really completed) it messes up instrumentation. We already take care to only use memory instrumentation on the block, we should also suppress IRQs. Reviewed-by: Pierrick Bouvier Reviewed-by: Julian Ganz Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-23-alex.bennee@linaro.org> --- accel/tcg/translate-all.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 453eb20ec9..d56ca13cdd 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -633,9 +633,10 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * Exit the loop and potentially generate a new TB executing the * just the I/O insns. We also limit instrumentation to memory * operations only (which execute after completion) so we don't - * double instrument the instruction. + * double instrument the instruction. Also don't let an IRQ sneak + * in before we execute it. */ - cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | n; + cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_NOIRQ | n; if (qemu_loglevel_mask(CPU_LOG_EXEC)) { vaddr pc = cpu->cc->get_pc(cpu); From 8f5a4cfc7ed9e06e07fdd8e8fdf50ef3ea783f63 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:52 +0000 Subject: [PATCH 1154/2892] win32: remove usage of attribute gcc_struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This attribute is not recognized by clang. An investigation has been performed to ensure this attribute has no effect on layout of structures we use in QEMU [1], so it's safe to remove now. In the future, we'll forbid introducing new bitfields in packed struct, as they are the one potentially impacted by this change. [1] https://lore.kernel.org/qemu-devel/66c346de-7e20-4831-b3eb-1cda83240af9@linaro.org/ Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Acked-by: Stefano Garzarella Signed-off-by: Pierrick Bouvier Acked-by: Michael S. Tsirkin Tested-by: Stefan Weil Tested-by: Philippe Mathieu-Daudé Message-Id: <20250110203401.178532-2-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-24-alex.bennee@linaro.org> --- include/qemu/compiler.h | 7 +------ meson.build | 5 ----- scripts/cocci-macro-file.h | 6 +----- subprojects/libvhost-user/libvhost-user.h | 6 +----- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index c06954ccb4..d904408e5e 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -22,12 +22,7 @@ #define QEMU_EXTERN_C extern #endif -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define QEMU_PACKED __attribute__((gcc_struct, packed)) -#else -# define QEMU_PACKED __attribute__((packed)) -#endif - +#define QEMU_PACKED __attribute__((packed)) #define QEMU_ALIGNED(X) __attribute__((aligned(X))) #ifndef glue diff --git a/meson.build b/meson.build index d06f59095c..da279cc112 100644 --- a/meson.build +++ b/meson.build @@ -377,11 +377,6 @@ elif host_os == 'sunos' qemu_common_flags += '-D__EXTENSIONS__' elif host_os == 'haiku' qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC'] -elif host_os == 'windows' - if not compiler.compiles('struct x { int y; } __attribute__((gcc_struct));', - args: '-Werror') - error('Your compiler does not support __attribute__((gcc_struct)) - please use GCC instead of Clang') - endif endif # Choose instruction set (currently x86-only) diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h index d247a5086e..c64831d540 100644 --- a/scripts/cocci-macro-file.h +++ b/scripts/cocci-macro-file.h @@ -23,11 +23,7 @@ #define G_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #define G_GNUC_NULL_TERMINATED __attribute__((sentinel)) -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define QEMU_PACKED __attribute__((gcc_struct, packed)) -#else -# define QEMU_PACKED __attribute__((packed)) -#endif +#define QEMU_PACKED __attribute__((packed)) #define cat(x,y) x ## y #define cat2(x,y) cat(x,y) diff --git a/subprojects/libvhost-user/libvhost-user.h b/subprojects/libvhost-user/libvhost-user.h index deb40e77b3..2ffc58c11b 100644 --- a/subprojects/libvhost-user/libvhost-user.h +++ b/subprojects/libvhost-user/libvhost-user.h @@ -186,11 +186,7 @@ typedef struct VhostUserShared { unsigned char uuid[UUID_LEN]; } VhostUserShared; -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define VU_PACKED __attribute__((gcc_struct, packed)) -#else -# define VU_PACKED __attribute__((packed)) -#endif +#define VU_PACKED __attribute__((packed)) typedef struct VhostUserMsg { int request; From ecbf3567e217bc7de320bfe165c8ce72eea51b2c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:53 +0000 Subject: [PATCH 1155/2892] docs/devel/style: add a section about bitfield, and disallow them for packed structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Tested-by: Stefan Weil Tested-by: Philippe Mathieu-Daudé Message-Id: <20250110203401.178532-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-25-alex.bennee@linaro.org> --- docs/devel/style.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/devel/style.rst b/docs/devel/style.rst index 2f68b50079..d025933808 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -416,6 +416,26 @@ definitions instead of typedefs in headers and function prototypes; this avoids problems with duplicated typedefs and reduces the need to include headers from other headers. +Bitfields +--------- + +C bitfields can be a cause of non-portability issues, especially under windows +where `MSVC has a different way to lay them out than GCC +`_, or where +endianness matters. + +For this reason, we disallow usage of bitfields in packed structures and in any +structures which are supposed to exactly match a specific layout in guest +memory. Some existing code may use it, and we carefully ensured the layout was +the one expected. + +We also suggest avoiding bitfields even in structures where the exact +layout does not matter, unless you can show that they provide a significant +usability benefit. + +We encourage the usage of ``include/hw/registerfields.h`` as a safe replacement +for bitfields. + Reserved namespaces in C and POSIX ---------------------------------- From 923710b6d5b21d9b3fcecc7e6719cfa5a53de268 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:02:54 +0000 Subject: [PATCH 1156/2892] plugins: enable linking with clang/lld MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows uses a special mechanism to enable plugins to work (DLL delay loading). Option for lld is different than ld. MSYS2 clang based environment use lld by default, so restricting to this config on Windows is safe, and will avoid false bug reports. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Tested-by: Stefan Weil Tested-by: Philippe Mathieu-Daudé Message-Id: <20250110203401.178532-4-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-26-alex.bennee@linaro.org> --- contrib/plugins/meson.build | 2 +- meson.build | 5 +++++ plugins/meson.build | 24 ++++++++++++++++++++---- tests/tcg/plugins/meson.build | 3 +-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index 63a32c2b4f..484b9a808c 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -12,7 +12,7 @@ if get_option('plugins') t += shared_module(i, files(i + '.c') + 'win32_linker.c', include_directories: '../../include/qemu', link_depends: [win32_qemu_plugin_api_lib], - link_args: ['-Lplugins', '-lqemu_plugin_api'], + link_args: win32_qemu_plugin_api_link_flags, dependencies: glib) else t += shared_module(i, files(i + '.c'), diff --git a/meson.build b/meson.build index da279cc112..15a066043b 100644 --- a/meson.build +++ b/meson.build @@ -377,6 +377,11 @@ elif host_os == 'sunos' qemu_common_flags += '-D__EXTENSIONS__' elif host_os == 'haiku' qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC'] +elif host_os == 'windows' + # plugins use delaylib, and clang needs to be used with lld to make it work. + if compiler.get_id() == 'clang' and compiler.get_linker_id() != 'ld.lld' + error('On windows, you need to use lld with clang - use msys2 clang64/clangarm64 env') + endif endif # Choose instruction set (currently x86-only) diff --git a/plugins/meson.build b/plugins/meson.build index 98542e926f..d60be2a4d6 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -17,14 +17,15 @@ if not enable_modules capture: true, command: ['sed', '-ne', 's/^[[:space:]]*\\(qemu_.*\\);/_\\1/p', '@INPUT@']) emulator_link_args += ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols'] + elif host_os == 'windows' and meson.get_compiler('c').get_id() == 'clang' + # LLVM/lld does not support exporting specific symbols. However, it works + # out of the box with dllexport/dllimport attribute we set in the code. else emulator_link_args += ['-Xlinker', '--dynamic-list=' + qemu_plugin_symbols.full_path()] endif endif if host_os == 'windows' - dlltool = find_program('dlltool', required: true) - # Generate a .lib file for plugins to link against. # First, create a .def file listing all the symbols a plugin should expect to have # available in qemu @@ -33,12 +34,27 @@ if host_os == 'windows' output: 'qemu_plugin_api.def', capture: true, command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) + # then use dlltool to assemble a delaylib. + # The delaylib will have an "imaginary" name (qemu.exe), that is used by the + # linker file we add with plugins (win32_linker.c) to identify that we want + # to find missing symbols in current program. + win32_qemu_plugin_api_link_flags = ['-Lplugins', '-lqemu_plugin_api'] + if meson.get_compiler('c').get_id() == 'clang' + # With LLVM/lld, delaylib is specified at link time (-delayload) + dlltool = find_program('llvm-dlltool', required: true) + dlltool_cmd = [dlltool, '-d', '@INPUT@', '-l', '@OUTPUT@', '-D', 'qemu.exe'] + win32_qemu_plugin_api_link_flags += ['-Wl,-delayload=qemu.exe'] + else + # With gcc/ld, delay lib is built with a specific delay parameter. + dlltool = find_program('dlltool', required: true) + dlltool_cmd = [dlltool, '--input-def', '@INPUT@', + '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + endif win32_qemu_plugin_api_lib = configure_file( input: win32_plugin_def, output: 'libqemu_plugin_api.a', - command: [dlltool, '--input-def', '@INPUT@', - '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + command: dlltool_cmd ) endif specific_ss.add(files( diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index f847849b1b..87a17d67bd 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -5,9 +5,8 @@ if get_option('plugins') t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', link_depends: [win32_qemu_plugin_api_lib], - link_args: ['-Lplugins', '-lqemu_plugin_api'], + link_args: win32_qemu_plugin_api_link_flags, dependencies: glib) - else t += shared_module(i, files(i + '.c'), include_directories: '../../../include/qemu', From b165ee1916b716043d452cae233fae9175ca5846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:55 +0000 Subject: [PATCH 1157/2892] plugins: fix kdoc annotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is qemu_plugin_mem_get_value() Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-27-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 0fba36ae02..3a850aa216 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -583,7 +583,7 @@ QEMU_PLUGIN_API bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info); /** - * qemu_plugin_mem_get_mem_value() - return last value loaded/stored + * qemu_plugin_mem_get_value() - return last value loaded/stored * @info: opaque memory transaction handle * * Returns: memory value From c08f9d8dec26133e86e1420092742632e58a1d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:56 +0000 Subject: [PATCH 1158/2892] editorconfig: update for perl scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have two types of perl scripts in the tree. The ones from the kernel are mostly tab based where as scripts we have written ourselves use 4 space indentation. Attempt to codify that in our .editorconfig Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-28-alex.bennee@linaro.org> --- .editorconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.editorconfig b/.editorconfig index 7303759ed7..a04cb9054c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -47,3 +47,16 @@ emacs_mode = glsl [*.json] indent_style = space emacs_mode = python + +# by default follow QEMU's style +[*.pl] +indent_style = space +indent_size = 4 +emacs_mode = perl + +# but user kernel "style" for imported scripts +[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] +indent_style = tab +indent_size = 8 +emacs_mode = perl + From 64965b4b30e6634ce874156ae94d336bfb0fdfd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:57 +0000 Subject: [PATCH 1159/2892] tests/qtest: fix some copy and paste errors in kdoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of copy and paste kdoc comments are referring to the wrong definition. Fix those cases. Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-29-alex.bennee@linaro.org> --- tests/qtest/libqos/qgraph.h | 2 +- tests/qtest/libqtest.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h index 1b5de02e7b..81fbfdd0e2 100644 --- a/tests/qtest/libqos/qgraph.h +++ b/tests/qtest/libqos/qgraph.h @@ -355,7 +355,7 @@ void qos_object_start_hw(QOSGraphObject *obj); QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); /** - * qos_machine_new(): instantiate a new driver node + * qos_driver_new(): instantiate a new driver node * @node: A driver node to be instantiated * @parent: A #QOSGraphObject to be consumed by the new driver node * @alloc: An allocator to be used by the new driver node. diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index f23d80e9e5..fa08c7eca5 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -365,7 +365,7 @@ QDict *qtest_qmp_event_ref(QTestState *s, const char *event); char *qtest_hmp(QTestState *s, const char *fmt, ...) G_GNUC_PRINTF(2, 3); /** - * qtest_hmpv: + * qtest_vhmp: * @s: #QTestState instance to operate on. * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). * @ap: HMP command arguments @@ -904,7 +904,7 @@ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) #ifndef _WIN32 /** - * qtest_qmp_fd_assert_success_ref: + * qtest_qmp_fds_assert_success_ref: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send @@ -921,7 +921,7 @@ QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, G_GNUC_PRINTF(4, 5); /** - * qtest_qmp_fd_assert_success: + * qtest_qmp_fds_assert_success: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send From 69f11e473060de0e704ea0dfda13cdfd1827fc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:58 +0000 Subject: [PATCH 1160/2892] include/exec: fix some copy and paste errors in kdoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of copy and paste kdoc comments are referring to the wrong definition. Fix those cases. Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-30-alex.bennee@linaro.org> --- include/exec/memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 9458e2801d..605687befa 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1194,7 +1194,7 @@ static inline bool MemoryRegionSection_eq(MemoryRegionSection *a, MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s); /** - * memory_region_section_new_copy: Free a copied memory region section + * memory_region_section_free_copy: Free a copied memory region section * * Free a copy of a memory section created via memory_region_section_new_copy(). * properly dropping references on all relevant members. @@ -2510,7 +2510,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, void memory_global_dirty_log_sync(bool last_stage); /** - * memory_global_dirty_log_sync: synchronize the dirty log for all memory + * memory_global_after_dirty_log_sync: synchronize the dirty log for all memory * * Synchronizes the vCPUs with a thread that is reading the dirty bitmap. * This function must be called after the dirty log bitmap is cleared, and From 2012375d1874cac8b1c0f68b84d4b21be02186d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:02:59 +0000 Subject: [PATCH 1161/2892] include/exec: remove warning_printed from MemoryRegion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since d197063fcf9 (memory: move unassigned_mem_ops to memory.c) this field is unused. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-31-alex.bennee@linaro.org> --- include/exec/memory.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 605687befa..3ee1901b52 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -784,7 +784,6 @@ struct MemoryRegion { bool terminates; bool ram_device; bool enabled; - bool warning_printed; /* For reservations */ uint8_t vga_logging_count; MemoryRegion *alias; hwaddr alias_offset; From 7b2c98854cf931cc2a090ead5cdc5c1bed4e9f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 16 Jan 2025 16:03:00 +0000 Subject: [PATCH 1162/2892] docs/sphinx: include kernel-doc script as a dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we update the script we should rebuild the docs. Otherwise breaking changes made to the kdoc script don't become apparent until later. Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-32-alex.bennee@linaro.org> --- docs/sphinx/depfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index e74be6af98..d3c774d28b 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -31,6 +31,9 @@ def get_infiles(env): for path in Path(static_path).rglob('*'): yield str(path) + # also include kdoc script + yield str(env.config.kerneldoc_bin[1]) + def write_depfile(app, exception): if exception: From f4ac443efd8228a0cdb3f687f574c81859674d46 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:03:01 +0000 Subject: [PATCH 1163/2892] docs/devel: add git-publish for patch submitting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241209183104.365796-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-33-alex.bennee@linaro.org> --- docs/devel/submitting-a-patch.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index 03b2ac298a..69df7682c5 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -235,6 +235,31 @@ to another list.) ``git send-email`` (`step-by-step setup guide works best for delivering the patch without mangling it, but attachments can be used as a last resort on a first-time submission. +.. _use_git_publish: + +Use git-publish +~~~~~~~~~~~~~~~ + +If you already configured git send-email, you can simply use `git-publish +`__ to send series. + +:: + + $ git checkout master -b my-feature + $ # work on new commits, add your 'Signed-off-by' lines to each + $ git publish + $ ... more work, rebase on master, ... + $ git publish # will send a v2 + +Each time you post a series, git-publish will create a local tag with the format +``-v`` to record the patch series. + +When sending patch emails, 'git publish' will consult the output of +'scripts/get_maintainers.pl' and automatically CC anyone listed as maintainers +of the affected code. Generally you should accept the suggested CC list, but +there may sometimes be scenarios where it is appropriate to cut it down (eg on +certain large tree-wide cleanups), or augment it with other interested people. + .. _if_you_cannot_send_patch_emails: If you cannot send patch emails From ca494c9be4dbe4144de6f9433beb00d0f6cbc15d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:03:02 +0000 Subject: [PATCH 1164/2892] docs/devel: add b4 for patch retrieval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241209183104.365796-4-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-34-alex.bennee@linaro.org> --- docs/devel/submitting-a-patch.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index 69df7682c5..65c64078cb 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -433,6 +433,20 @@ For more details on how QEMU's stable process works, refer to the .. _participating_in_code_review: +Retrieve an existing series +--------------------------- + +If you want to apply an existing series on top of your tree, you can simply use +`b4 `__. + +:: + + b4 shazam $msg-id + +The message id is related to the patch series that has been sent to the mailing +list. You need to retrieve the "Message-Id:" header from one of the patches. Any +of them can be used and b4 will apply the whole series. + Participating in Code Review ---------------------------- From 75dbfbad68461cd48bf283964bcf319aaa11570a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:03:03 +0000 Subject: [PATCH 1165/2892] docs/devel: add information on how to setup build environments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MacOS and Linux are straightforward, but Windows needs a bit more details. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241209183104.365796-5-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-35-alex.bennee@linaro.org> --- MAINTAINERS | 3 +- docs/about/build-platforms.rst | 4 +- docs/devel/build-environment.rst | 118 +++++++++++++++++++++++++++++++ docs/devel/index-build.rst | 1 + 4 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 docs/devel/build-environment.rst diff --git a/MAINTAINERS b/MAINTAINERS index a928ce3e41..f744896f89 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -72,9 +72,10 @@ R: Markus Armbruster R: Philippe Mathieu-Daudé W: https://www.qemu.org/docs/master/devel/index.html S: Odd Fixes -F: docs/devel/style.rst +F: docs/devel/build-environment.rst F: docs/devel/code-of-conduct.rst F: docs/devel/conflict-resolution.rst +F: docs/devel/style.rst F: docs/devel/submitting-a-patch.rst F: docs/devel/submitting-a-pull-request.rst diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index d8b0445157..482b09819c 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -29,6 +29,9 @@ The `Repology`_ site is a useful resource to identify currently shipped versions of software in various operating systems, though it does not cover all distros listed below. +You can find how to install build dependencies for different systems on the +:ref:`setup-build-env` page. + Supported host architectures ---------------------------- @@ -130,7 +133,6 @@ Optional build dependencies cross compilation using ``docker`` or ``podman``, or to use pre-built binaries distributed with QEMU. - Windows ------- diff --git a/docs/devel/build-environment.rst b/docs/devel/build-environment.rst new file mode 100644 index 0000000000..f133ef2e01 --- /dev/null +++ b/docs/devel/build-environment.rst @@ -0,0 +1,118 @@ + +.. _setup-build-env: + +Setup build environment +======================= + +QEMU uses a lot of dependencies on the host system. glib2 is used everywhere in +the code base, and most of the other dependencies are optional. + +We present here simple instructions to enable native builds on most popular +systems. + +You can find additional instructions on `QEMU wiki `_: + +- `Linux `_ +- `MacOS `_ +- `Windows `_ +- `BSD `_ + +Note: Installing dependencies using your package manager build dependencies may +miss out on deps that have been newly introduced in qemu.git. In more, it misses +deps the distribution has decided to exclude. + +Linux +----- + +Fedora +++++++ + +:: + + sudo dnf update && sudo dnf builddep qemu + +Debian/Ubuntu ++++++++++++++ + +You first need to enable `Sources List `_. +Then, use apt to install dependencies: + +:: + + sudo apt update && sudo apt build-dep qemu + +MacOS +----- + +You first need to install `Homebrew `_. Then, use it to +install dependencies: + +:: + + brew update && brew install $(brew deps --include-build qemu) + +Windows +------- + +You first need to install `MSYS2 `_. +MSYS2 offers `different environments `_. +x86_64 environments are based on GCC, while aarch64 is based on Clang. + +We recommend to use MINGW64 for windows-x86_64 and CLANGARM64 for windows-aarch64 +(only available on windows-aarch64 hosts). + +Then, you can open a windows shell, and enter msys2 env using: + +:: + + c:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 + # Replace -ucrt64 by -clangarm64 or -ucrt64 for other environments. + +MSYS2 package manager does not offer a built-in way to install build +dependencies. You can start with this list of packages using pacman: + +Note: Dependencies need to be installed again if you use a different MSYS2 +environment. + +:: + + # update MSYS2 itself, you need to reopen your shell at the end. + pacman -Syu + pacman -S \ + base-devel binutils bison diffutils flex git grep make sed \ + ${MINGW_PACKAGE_PREFIX}-toolchain \ + ${MINGW_PACKAGE_PREFIX}-glib2 \ + ${MINGW_PACKAGE_PREFIX}-gtk3 \ + ${MINGW_PACKAGE_PREFIX}-libnfs \ + ${MINGW_PACKAGE_PREFIX}-libssh \ + ${MINGW_PACKAGE_PREFIX}-ninja \ + ${MINGW_PACKAGE_PREFIX}-pixman \ + ${MINGW_PACKAGE_PREFIX}-pkgconf \ + ${MINGW_PACKAGE_PREFIX}-python \ + ${MINGW_PACKAGE_PREFIX}-SDL2 \ + ${MINGW_PACKAGE_PREFIX}-zstd + +If you want to install all dependencies, it's possible to use recipe used to +build QEMU in MSYS2 itself. + +:: + + pacman -S wget + wget https://raw.githubusercontent.com/msys2/MINGW-packages/refs/heads/master/mingw-w64-qemu/PKGBUILD + # Some packages may be missing for your environment, installation will still + # be done though. + makepkg -s PKGBUILD || true + +Build on windows-aarch64 +++++++++++++++++++++++++ + +When trying to cross compile meson for x86_64 using UCRT64 or MINGW64 env, +configure will run into an error because the cpu detected is not correct. + +Meson detects x86_64 processes emulated, so you need to manually set the cpu, +and force a cross compilation (with empty prefix). + +:: + + ./configure --cpu=x86_64 --cross-prefix= + diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 0023953be3..0745c81a26 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -8,6 +8,7 @@ some of the basics if you are adding new files and targets to the build. :maxdepth: 3 build-system + build-environment kconfig docs qapi-code-gen From 7f6314427e78283f84e6f1b425a122b260a6ac50 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:03:04 +0000 Subject: [PATCH 1166/2892] docs/devel: add a codebase section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Present the various parts of QEMU and organization of codebase. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241209183104.365796-6-pierrick.bouvier@linaro.org> [AJB: tweak commit summary, update MAINTAINERS] Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-36-alex.bennee@linaro.org> --- MAINTAINERS | 1 + docs/about/emulation.rst | 2 + docs/devel/codebase.rst | 220 +++++++++++++++++++++++++ docs/devel/decodetree.rst | 2 + docs/devel/ebpf_rss.rst | 2 + docs/devel/index-internals.rst | 2 + docs/devel/index.rst | 1 + docs/devel/migration/main.rst | 2 + docs/devel/qapi-code-gen.rst | 1 + docs/devel/testing/main.rst | 9 +- docs/devel/testing/qtest.rst | 2 + docs/index.rst | 2 + docs/interop/qemu-ga.rst | 2 + docs/system/qemu-block-drivers.rst.inc | 2 + docs/tools/qemu-storage-daemon.rst | 2 + docs/user/main.rst | 6 + 16 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 docs/devel/codebase.rst diff --git a/MAINTAINERS b/MAINTAINERS index f744896f89..4c86c81f08 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -74,6 +74,7 @@ W: https://www.qemu.org/docs/master/devel/index.html S: Odd Fixes F: docs/devel/build-environment.rst F: docs/devel/code-of-conduct.rst +F: docs/devel/codebase.rst F: docs/devel/conflict-resolution.rst F: docs/devel/style.rst F: docs/devel/submitting-a-patch.rst diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 3028d5fff7..3bc3579434 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -176,6 +176,8 @@ for that architecture. - System - Tensilica ISS SIMCALL +.. _tcg-plugins: + TCG Plugins ----------- diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst new file mode 100644 index 0000000000..4039875ee0 --- /dev/null +++ b/docs/devel/codebase.rst @@ -0,0 +1,220 @@ +======== +Codebase +======== + +This section presents the various parts of QEMU and how the codebase is +organized. + +Beyond giving succint descriptions, the goal is to offer links to various +parts of the documentation/codebase. + +Subsystems +---------- + +An exhaustive list of subsystems and associated files can be found in the +`MAINTAINERS `_ +file. + +Some of the main QEMU subsystems are: + +- `Accelerators` +- Block devices and `disk images` support +- `CI` and `Tests` +- `Devices` & Board models +- `Documentation ` +- `GDB support` +- `Migration` +- `Monitor` +- :ref:`QOM (QEMU Object Model)` +- `System mode` +- :ref:`TCG (Tiny Code Generator)` +- `User mode` (`Linux` & `BSD`) +- User Interfaces + +More documentation on QEMU subsystems can be found on :ref:`internal-subsystem` +page. + +The Grand tour +-------------- + +We present briefly here what every folder in the top directory of the codebase +contains. Hop on! + +The folder name links here will take you to that folder in our gitlab +repository. Other links will take you to more detailed documentation for that +subsystem, where we have it. Unfortunately not every subsystem has documentation +yet, so sometimes the source code is all you have. + +* `accel `_: + Infrastructure and architecture agnostic code related to the various + `accelerators ` supported by QEMU + (TCG, KVM, hvf, whpx, xen, nvmm). + Contains interfaces for operations that will be implemented per + `target `_. +* `audio `_: + Audio (host) support. +* `authz `_: + `QEMU Authorization framework`. +* `backends `_: + Various backends that are used to access resources on the host (e.g. for + random number generation, memory backing or cryptographic functions). +* `block `_: + Block devices and `image formats` implementation. +* `bsd-user `_: + `BSD User mode`. +* build: Where the code built goes by default. You can tell the QEMU build + system to put the built code anywhere else you like. +* `chardev `_: + Various backends used by char devices. +* `common-user `_: + User-mode assembly code for dealing with signals occuring during syscalls. +* `configs `_: + Makefiles defining configurations to build QEMU. +* `contrib `_: + Community contributed devices/plugins/tools. +* `crypto `_: + Cryptographic algorithms used in QEMU. +* `disas `_: + Disassembly functions used by QEMU target code. +* `docs `_: + QEMU Documentation. +* `dump `_: + Code to dump memory of a running VM. +* `ebpf `_: + eBPF program support in QEMU. `virtio-net RSS` uses it. +* `fpu `_: + Floating-point software emulation. +* `fsdev `_: + `VirtFS `_ support. +* `gdbstub `_: + `GDB ` support. +* `gdb-xml `_: + Set of XML files describing architectures and used by `gdbstub `. +* `host `_: + Various architecture specific header files (crypto, atomic, memory + operations). +* `linux-headers `_: + A subset of headers imported from Linux kernel and used for implementing + KVM support and user-mode. +* `linux-user `_: + `User mode ` implementation. Contains one folder per target + architecture. +* `.gitlab-ci.d `_: + `CI ` yaml and scripts. +* `include `_: + All headers associated to different subsystems in QEMU. The hierachy used + mirrors source code organization and naming. +* `hw `_: + `Devices ` and boards emulation. Devices are categorized by + type/protocol/architecture and located in associated subfolder. +* `io `_: + QEMU `I/O channels `_. +* `libdecnumber `_: + Import of gcc library, used to implement decimal number arithmetic. +* `migration `__: + `Migration framework `. +* `monitor `_: + `Monitor ` implementation (HMP & QMP). +* `nbd `_: + QEMU `NBD (Network Block Device) ` server. +* `net `_: + Network (host) support. +* `pc-bios `_: + Contains pre-built firmware binaries and boot images, ready to use in + QEMU without compilation. +* `plugins `_: + :ref:`TCG plugins ` core implementation. Plugins can be found in + `tests `__ + and `contrib `__ + folders. +* `po `_: + Translation files. +* `python `_: + Python part of our build/test system. +* `qapi `_: + `QAPI ` implementation. +* `qobject `_: + QEMU Object implementation. +* `qga `_: + QEMU `Guest agent ` implementation. +* `qom `_: + QEMU :ref:`Object model ` implementation, with monitor associated commands. +* `replay `_: + QEMU :ref:`Record/replay ` implementation. +* `roms `_: + Contains source code for various firmware and ROMs, which can be compiled if + custom or updated versions are needed. +* `rust `_: + Rust integration in QEMU. It contains the new interfaces defined and + associated devices using it. +* `scripts `_: + Collection of scripts used in build and test systems, and various + tools for QEMU codebase and execution traces. +* `scsi `_: + Code related to SCSI support, used by SCSI devices. +* `semihosting `_: + QEMU `Semihosting ` implementation. +* `stats `_: + `Monitor ` stats commands implementation. +* `storage-daemon `_: + QEMU `Storage daemon ` implementation. +* `stubs `_: + Various stubs (empty functions) used to compile QEMU with specific + configurations. +* `subprojects `_: + QEMU submodules used by QEMU build system. +* `system `_: + QEMU `system mode ` implementation (cpu, mmu, boot support). +* `target `_: + Contains code for all target architectures supported (one subfolder + per arch). For every architecture, you can find accelerator specific + implementations. +* `tcg `_: + :ref:`TCG ` related code. + Contains one subfolder per host supported architecture. +* `tests `_: + QEMU `test ` suite + + - `avocado `_: + Functional tests booting full VM using `Avocado framework `. + Those tests will be transformed and moved into + `tests/functional `_ + in the future. + - `data `_: + Data for various tests. + - `decode `_: + Testsuite for :ref:`decodetree ` implementation. + - `docker `_: + Code and scripts to create `containers ` used in `CI `. + - `fp `_: + QEMU testsuite for soft float implementation. + - `functional `_: + `Functional tests ` (full VM boot). + - `lcitool `_: + Generate dockerfiles for CI containers. + - `migration `_: + Test scripts and data for `Migration framework `. + - `multiboot `_: + Test multiboot functionality for x86_64/i386. + - `qapi-schema `_: + Test scripts and data for `QAPI `. + - `qemu-iotests `_: + `Disk image and block tests `. + - `qtest `_: + `Device emulation testing `. + - `tcg `__: + `TCG related tests `. Contains code per architecture + (subfolder) and multiarch tests as well. + - `tsan `_: + `Suppressions ` for thread sanitizer. + - `uefi-test-tools `_: + Test tool for UEFI support. + - `unit `_: + QEMU `Unit tests `. +* `trace `_: + :ref:`Tracing framework `. Used to print information associated to various + events during execution. +* `ui `_: + QEMU User interfaces. +* `util `_: + Utility code used by other parts of QEMU. diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst index e3392aa705..98ad33a487 100644 --- a/docs/devel/decodetree.rst +++ b/docs/devel/decodetree.rst @@ -1,3 +1,5 @@ +.. _decodetree: + ======================== Decodetree Specification ======================== diff --git a/docs/devel/ebpf_rss.rst b/docs/devel/ebpf_rss.rst index 4a68682b31..ed5d33767b 100644 --- a/docs/devel/ebpf_rss.rst +++ b/docs/devel/ebpf_rss.rst @@ -1,3 +1,5 @@ +.. _ebpf-rss: + =========================== eBPF RSS virtio-net support =========================== diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index ab9fbc4482..bca597c658 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -1,3 +1,5 @@ +.. _internal-subsystem: + Internal Subsystem Information ------------------------------ diff --git a/docs/devel/index.rst b/docs/devel/index.rst index a53f1bfda5..29f032d6a8 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -35,3 +35,4 @@ the :ref:`tcg_internals`. index-api index-internals index-tcg + codebase diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index c2857fc244..cdd4f4a6d7 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -1,3 +1,5 @@ +.. _migration: + =================== Migration framework =================== diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 583207a8ec..3e26d2d104 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -9,6 +9,7 @@ How to use the QAPI code generator This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _qapi: Introduction ============ diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 91f4dc61fb..9869bcf034 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -39,6 +39,8 @@ Before running tests, it is best to build QEMU programs first. Some tests expect the executables to exist and will fail with obscure messages if they cannot find them. +.. _unit-tests: + Unit tests ~~~~~~~~~~ @@ -126,6 +128,8 @@ successfully on various hosts. The following list shows some best practices: #ifdef in the codes. If the whole test suite cannot run on Windows, disable the build in the meson.build file. +.. _qapi-tests: + QAPI schema tests ~~~~~~~~~~~~~~~~~ @@ -160,6 +164,8 @@ check-block are in the "auto" group). See the "QEMU iotests" section below for more information. +.. _qemu-iotests: + QEMU iotests ------------ @@ -679,6 +685,8 @@ The above exitcode=0 has TSan continue without error if any warnings are found. This allows for running the test and then checking the warnings afterwards. If you want TSan to stop and exit with error on warnings, use exitcode=66. +.. _tsan-suppressions: + TSan Suppressions ~~~~~~~~~~~~~~~~~ Keep in mind that for any data race warning, although there might be a data race @@ -901,7 +909,6 @@ You can run the avocado tests simply by executing: See :ref:`checkavocado-ref` for more details. - .. _checktcg-ref: Testing with "make check-tcg" diff --git a/docs/devel/testing/qtest.rst b/docs/devel/testing/qtest.rst index c5b8546b3e..73ef7702b7 100644 --- a/docs/devel/testing/qtest.rst +++ b/docs/devel/testing/qtest.rst @@ -1,3 +1,5 @@ +.. _qtest: + ======================================== QTest Device Emulation Testing Framework ======================================== diff --git a/docs/index.rst b/docs/index.rst index 0b9ee9901d..78285ebd6a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _documentation-root: + ================================ Welcome to QEMU's documentation! ================================ diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 11f7bae460..d16cc1b9f0 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -1,3 +1,5 @@ +.. _qemu-ga: + QEMU Guest Agent ================ diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc index 384e95ba76..cfe1acb78a 100644 --- a/docs/system/qemu-block-drivers.rst.inc +++ b/docs/system/qemu-block-drivers.rst.inc @@ -500,6 +500,8 @@ What you should *never* do: - expect it to work when loadvm'ing - write to the FAT directory on the host system while accessing it with the guest system +.. _nbd: + NBD access ~~~~~~~~~~ diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index ea00149a63..35ab2d7807 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -1,3 +1,5 @@ +.. _storage-daemon: + =================== QEMU Storage Daemon =================== diff --git a/docs/user/main.rst b/docs/user/main.rst index 7a126ee809..80a77f0a0c 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -1,3 +1,5 @@ +.. _user-mode: + QEMU User space emulator ======================== @@ -42,6 +44,8 @@ QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the emulator. +.. _linux-user-mode: + Linux User space emulator ------------------------- @@ -175,6 +179,8 @@ Other binaries * ``qemu-sparc64`` can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). +.. _bsd-user-mode: + BSD User space emulator ----------------------- From a4340e7c522e3f20abeac061a5a8b319f715c1d0 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 16 Jan 2025 16:03:05 +0000 Subject: [PATCH 1167/2892] docs: add a glossary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20241209183104.365796-7-pierrick.bouvier@linaro.org> [AJB: update MAINTAINERS] Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-37-alex.bennee@linaro.org> --- MAINTAINERS | 1 + docs/devel/control-flow-integrity.rst | 2 + docs/devel/multi-thread-tcg.rst | 2 + docs/glossary.rst | 280 ++++++++++++++++++++++++++ docs/index.rst | 1 + docs/system/arm/virt.rst | 2 + docs/system/images.rst | 2 + docs/tools/qemu-nbd.rst | 2 + 8 files changed, 292 insertions(+) create mode 100644 docs/glossary.rst diff --git a/MAINTAINERS b/MAINTAINERS index 4c86c81f08..846b81e3ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -79,6 +79,7 @@ F: docs/devel/conflict-resolution.rst F: docs/devel/style.rst F: docs/devel/submitting-a-patch.rst F: docs/devel/submitting-a-pull-request.rst +F: docs/glossary.rst Responsible Disclosure, Reporting Security Issues ------------------------------------------------- diff --git a/docs/devel/control-flow-integrity.rst b/docs/devel/control-flow-integrity.rst index e6b73a4fe1..3d5702fa4c 100644 --- a/docs/devel/control-flow-integrity.rst +++ b/docs/devel/control-flow-integrity.rst @@ -1,3 +1,5 @@ +.. _cfi: + ============================ Control-Flow Integrity (CFI) ============================ diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index d706c27ea7..7fd0a07633 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -4,6 +4,8 @@ This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _mttcg: + ================== Multi-threaded TCG ================== diff --git a/docs/glossary.rst b/docs/glossary.rst new file mode 100644 index 0000000000..693d9855dd --- /dev/null +++ b/docs/glossary.rst @@ -0,0 +1,280 @@ +.. _Glossary: + +-------- +Glossary +-------- + +This section of the manual presents brief definitions of acronyms and terms used +by QEMU developers. + +Accelerator +----------- + +A specific API used to accelerate execution of guest instructions. It can be +hardware-based, through a virtualization API provided by the host OS (kvm, hvf, +whpx, ...), or software-based (tcg). See this description of `supported +accelerators`. + +Board +----- + +Another name for :ref:`machine`. + +Block +----- + +Block drivers are the available `disk formats and front-ends +` available, and block devices `(see Block device section on +options page)` are using them to implement disks for a +virtual machine. + +CFI +--- + +Control Flow Integrity is a hardening technique used to prevent exploits +targeting QEMU by detecting unexpected branches during execution. QEMU `actively +supports` being compiled with CFI enabled. + +Device +------ + +In QEMU, a device is a piece of hardware visible to the guest. Examples include +UARTs, PCI controllers, PCI cards, VGA controllers, and many more. + +QEMU is able to emulate a CPU, and all the hardware interacting with it, +including `many devices`. When QEMU runs a virtual machine +using a hardware-based accelerator, it is responsible for emulating, using +software, all devices. + +EDK2 +---- + +EDK2, as known as `TianoCore `_, is an open source +implementation of UEFI standard. QEMU virtual machines that boot a UEFI firmware +usually use EDK2. + +gdbstub +------- + +QEMU implements a `gdb server `, allowing gdb to attach to it and +debug a running virtual machine, or a program in user-mode. This allows +debugging the guest code that is running inside QEMU. + +glib2 +----- + +`GLib2 `_ is one of the most important libraries we +are using through the codebase. It provides many data structures, macros, string +and thread utilities and portable functions across different OS. It's required +to build QEMU. + +Guest agent +----------- + +The `QEMU Guest Agent ` is a daemon intended to be run within virtual +machines. It provides various services to help QEMU to interact with it. + +.. _guest: + +Guest +----- + +Guest is the architecture of the virtual machine, which is emulated. +See also :ref:`host`. + +Sometimes this is called the :ref:`target` architecture, but that term +can be ambiguous. + +.. _host: + +Host +---- + +Host is the architecture on which QEMU is running on, which is native. +See also :ref:`guest`. + +Hypervisor +---------- + +The formal definition of an hypervisor is a program or API than can be used to +manage a virtual machine. QEMU is a virtualizer, that interacts with various +hypervisors. + +In the context of QEMU, an hypervisor is an API, provided by the Host OS, +allowing to execute virtual machines. Linux implementation is KVM (and supports +Xen as well). For MacOS, it's HVF. Windows defines WHPX. And NetBSD provides +NVMM. + +.. _machine: + +Machine +------- + +QEMU's system emulation models many different types of hardware. A machine model +(sometimes called a board model) is the model of a complete virtual system with +RAM, one or more CPUs, and various devices. It can be selected with the option +``-machine`` of qemu-system. Our machine models can be found on this `page +`. + +Migration +--------- + +QEMU can save and restore the execution of a virtual machine between different +host systems. This is provided by the `Migration framework`. + +NBD +--- + +The `QEMU Network Block Device server ` is a tool that can be used to +mount and access QEMU images, providing functionality similar to a loop device. + +Mailing List +------------ + +This is `where `_ all the +development happens! Changes are posted as series, that all developers can +review and share feedback for. + +For reporting issues, our `GitLab +`_ tracker is the best place. + +.. _softmmu: + +MMU / softmmu +------------- + +The Memory Management Unit is responsible for translating virtual addresses to +physical addresses and managing memory protection. QEMU system mode is named +"softmmu" precisely because it implements this in software, including a TLB +(Translation lookaside buffer), for the guest virtual machine. + +QEMU user-mode does not implement a full software MMU, but "simply" translates +virtual addresses by adding a specific offset, and relying on host MMU/OS +instead. + +Monitor / QMP / HMP +------------------- + +The `QEMU Monitor ` is a text interface which can be used to interact +with a running virtual machine. + +QMP stands for QEMU Monitor Protocol and is a json based interface. +HMP stands for Human Monitor Protocol and is a set of text commands available +for users who prefer natural language to json. + +MTTCG +----- + +Multiple CPU support was first implemented using a round-robin algorithm +running on a single thread. Later on, `Multi-threaded TCG ` was developed +to benefit from multiple cores to speed up execution. + +Plugins +------- + +`TCG Plugins ` is an API used to instrument guest code, in system +and user mode. The end goal is to have a similar set of functionality compared +to `DynamoRIO `_ or `valgrind `_. + +One key advantage of QEMU plugins is that they can be used to perform +architecture agnostic instrumentation. + +Patchew +------- + +`Patchew `_ is a website that tracks patches on the +Mailing List. + +PR +-- + +Once a series is reviewed and accepted by a subsystem maintainer, it will be +included in a PR (Pull Request) that the project maintainer will merge into QEMU +main branch, after running tests. + +The QEMU project doesn't currently expect most developers to directly submit +pull requests. + +QCOW2 +----- + +QEMU Copy On Write is a disk format developed by QEMU. It provides transparent +compression, automatic extension, and many other advantages over a raw image. + +qcow2 is the recommended format to use. + +QEMU +---- + +`QEMU (Quick Emulator) `_ is a generic and open source +machine emulator and virtualizer. + +QOM +--- + +`QEMU Object Model ` is an object oriented API used to define various +devices and hardware in the QEMU codebase. + +Record/replay +------------- + +`Record/replay ` is a feature of QEMU allowing to have a deterministic +and reproducible execution of a virtual machine. + +Rust +---- + +`A new programming language `_, memory safe by +default. There is a work in progress to integrate it in QEMU codebase for +various subsystems. + +System mode +----------- + +QEMU System mode provides a virtual model of an entire machine (CPU, memory and +emulated devices) to run a guest OS. In this mode the CPU may be fully emulated, +or it may work with a hypervisor such as KVM, Xen or Hypervisor.Framework to +allow the guest to run directly on the host CPU. + +QEMU System mode is called :ref:`softmmu ` as well. + +.. _target: + +Target +------ + +The term "target" can be ambiguous. In most places in QEMU it is used as a +synonym for :ref:`guest`. For example the code for emulating Arm CPUs is in +``target/arm/``. However in the :ref:`TCG subsystem ` "target" refers to the +architecture which QEMU is running on, i.e. the :ref:`host`. + +TCG +--- + +TCG is the QEMU `Tiny Code Generator `. It is the JIT (just-in-time) +compiler we use to emulate a guest CPU in software. + +It is one of the accelerators supported by QEMU, and supports a lot of +guest/host architectures. + +User mode +--------- + +QEMU User mode can launch processes compiled for one CPU on another CPU. In this +mode the CPU is always emulated. In this mode, QEMU translate system calls from +guest to host kernel. It is available for Linux and BSD. + +VirtIO +------ + +VirtIO is an open standard used to define and implement virtual devices with a +minimal overhead, defining a set of data structures and hypercalls (similar to +system calls, but targeting an hypervisor, which happens to be QEMU in our +case). It's designed to be more efficient than emulating a real device, by +minimizing the amount of interactions between a guest VM and its hypervisor. + +vhost-user +---------- + +`Vhost-user ` is an interface used to implement VirtIO devices +outside of QEMU itself. diff --git a/docs/index.rst b/docs/index.rst index 78285ebd6a..5665de85ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,3 +20,4 @@ Welcome to QEMU's documentation! interop/index specs/index devel/index + glossary diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 766a7455f0..0c9c2ce035 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -1,3 +1,5 @@ +.. _arm-virt: + 'virt' generic virtual platform (``virt``) ========================================== diff --git a/docs/system/images.rst b/docs/system/images.rst index d000bd6b6f..a5551173c9 100644 --- a/docs/system/images.rst +++ b/docs/system/images.rst @@ -82,4 +82,6 @@ VM snapshots currently have the following known limitations: - A few device drivers still have incomplete snapshot support so their state is not saved or restored properly (in particular USB). +.. _block-drivers: + .. include:: qemu-block-drivers.rst.inc diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 329f44d989..4f21b7904a 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -1,3 +1,5 @@ +.. _qemu-nbd: + ===================================== QEMU Disk Network Block Device Server ===================================== From b9eab5efc1a631b476656859beb8eaaa895eb202 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 16 Jan 2025 16:03:06 +0000 Subject: [PATCH 1168/2892] scripts/nsis.py: Run dependency check for each DLL file only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each DLL should only be checked once for dependencies, but several hundred (781 in my test) unneeded checks were done. Now the script is significantly faster (16 s in my build). Signed-off-by: Stefan Weil Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250111215244.1680931-1-sw@weilnetz.de> Signed-off-by: Alex Bennée Message-Id: <20250116160306.1709518-38-alex.bennee@linaro.org> --- scripts/nsis.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/nsis.py b/scripts/nsis.py index 03ed7608a2..af4e064819 100644 --- a/scripts/nsis.py +++ b/scripts/nsis.py @@ -37,10 +37,10 @@ def find_deps(exe_or_dll, search_path, analyzed_deps): analyzed_deps.add(dep) # locate the dll dependencies recursively - rdeps = find_deps(dll, search_path, analyzed_deps) + analyzed_deps, rdeps = find_deps(dll, search_path, analyzed_deps) deps.extend(rdeps) - return deps + return analyzed_deps, deps def main(): parser = argparse.ArgumentParser(description="QEMU NSIS build helper.") @@ -92,18 +92,18 @@ def main(): dlldir = os.path.join(destdir + prefix, "dll") os.mkdir(dlldir) + analyzed_deps = set() for exe in glob.glob(os.path.join(destdir + prefix, "*.exe")): signcode(exe) # find all dll dependencies - deps = set(find_deps(exe, search_path, set())) + analyzed_deps, deps = find_deps(exe, search_path, analyzed_deps) + deps = set(deps) deps.remove(exe) # copy all dlls to the DLLDIR for dep in deps: dllfile = os.path.join(dlldir, os.path.basename(dep)) - if (os.path.exists(dllfile)): - continue print("Copying '%s' to '%s'" % (dep, dllfile)) shutil.copy(dep, dllfile) From 1addf57177a5646f86ede4eee385932b0214ab72 Mon Sep 17 00:00:00 2001 From: Ivan Klokov Date: Thu, 9 Jan 2025 12:10:43 +0300 Subject: [PATCH 1169/2892] target/riscv: Add RISC-V CSR qtest support The RISC-V architecture supports the creation of custom CSR-mapped devices. It would be convenient to test them in the same way as MMIO-mapped devices. To do this, a new call has been added to read/write CSR registers. Signed-off-by: Ivan Klokov Acked-by: Fabiano Rosas Reviewed-by: Daniel Henrique Barboza Signed-off-by: Fabiano Rosas --- hw/riscv/riscv_hart.c | 55 ++++++++++++++++++++++++++++++++++++++++++ tests/qtest/libqtest.c | 27 +++++++++++++++++++++ tests/qtest/libqtest.h | 14 +++++++++++ 3 files changed, 96 insertions(+) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index bc9ffdd2d4..5d64271718 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -22,6 +22,8 @@ #include "qapi/error.h" #include "qemu/module.h" #include "system/reset.h" +#include "system/qtest.h" +#include "qemu/cutils.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" #include "hw/qdev-properties.h" @@ -41,6 +43,55 @@ static void riscv_harts_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); } +#ifndef CONFIG_USER_ONLY +static void csr_call(char *cmd, uint64_t cpu_num, int csrno, uint64_t *val) +{ + RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(cpu_num)); + CPURISCVState *env = &cpu->env; + + int ret = RISCV_EXCP_NONE; + if (strcmp(cmd, "get_csr") == 0) { + ret = riscv_csrr(env, csrno, (target_ulong *)val); + } else if (strcmp(cmd, "set_csr") == 0) { + ret = riscv_csrrw(env, csrno, NULL, *(target_ulong *)val, + MAKE_64BIT_MASK(0, TARGET_LONG_BITS)); + } + + g_assert(ret == RISCV_EXCP_NONE); +} + +static bool csr_qtest_callback(CharBackend *chr, gchar **words) +{ + if (strcmp(words[0], "csr") == 0) { + + uint64_t cpu; + uint64_t val; + int rc, csr; + + rc = qemu_strtou64(words[2], NULL, 0, &cpu); + g_assert(rc == 0); + rc = qemu_strtoi(words[3], NULL, 0, &csr); + g_assert(rc == 0); + rc = qemu_strtou64(words[4], NULL, 0, &val); + g_assert(rc == 0); + csr_call(words[1], cpu, csr, &val); + + qtest_send_prefix(chr); + qtest_sendf(chr, "OK 0 "TARGET_FMT_lx"\n", (target_ulong)val); + + return true; + } + + return false; +} + +static void riscv_cpu_register_csr_qtest_callback(void) +{ + static GOnce once; + g_once(&once, (GThreadFunc)qtest_set_command_cb, csr_qtest_callback); +} +#endif + static bool riscv_hart_realize(RISCVHartArrayState *s, int idx, char *cpu_type, Error **errp) { @@ -58,6 +109,10 @@ static void riscv_harts_realize(DeviceState *dev, Error **errp) s->harts = g_new0(RISCVCPU, s->num_harts); +#ifndef CONFIG_USER_ONLY + riscv_cpu_register_csr_qtest_callback(); +#endif + for (n = 0; n < s->num_harts; n++) { if (!riscv_hart_realize(s, n, s->cpu_type, errp)) { return; diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 8de5f1fde3..4bc9643aad 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1218,6 +1218,33 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name, return 0; } +static void qtest_rsp_csr(QTestState *s, uint64_t *val) +{ + gchar **args; + uint64_t ret; + int rc; + + args = qtest_rsp_args(s, 3); + + rc = qemu_strtou64(args[1], NULL, 16, &ret); + g_assert(rc == 0); + rc = qemu_strtou64(args[2], NULL, 16, val); + g_assert(rc == 0); + + g_strfreev(args); +} + +uint64_t qtest_csr_call(QTestState *s, const char *name, + uint64_t cpu, int csr, + uint64_t *val) +{ + qtest_sendf(s, "csr %s 0x%"PRIx64" %d 0x%"PRIx64"\n", + name, cpu, csr, *val); + + qtest_rsp_csr(s, val); + return 0; +} + void qtest_add_func(const char *str, void (*fn)(void)) { gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index f23d80e9e5..d771f29d63 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -600,6 +600,20 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name, uint32_t nargs, uint64_t args, uint32_t nret, uint64_t ret); +/** + * qtest_csr_call: + * @s: #QTestState instance to operate on. + * @name: name of the command to call. + * @cpu: hart number. + * @csr: CSR number. + * @val: Value for reading/writing. + * + * Call an RISC-V CSR read/write function + */ +uint64_t qtest_csr_call(QTestState *s, const char *name, + uint64_t cpu, int csr, + uint64_t *val); + /** * qtest_bufread: * @s: #QTestState instance to operate on. From b4a91c5e710e42d95cca891496a6047de56aa535 Mon Sep 17 00:00:00 2001 From: Ivan Klokov Date: Thu, 9 Jan 2025 12:10:44 +0300 Subject: [PATCH 1170/2892] tests/qtest: QTest example for RISC-V CSR register Added demo for reading CSR register from qtest environment. Signed-off-by: Ivan Klokov Reviewed-by: Fabiano Rosas Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Fabiano Rosas --- tests/qtest/meson.build | 2 +- tests/qtest/riscv-csr-test.c | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/riscv-csr-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index edd53ec995..94b28e5a53 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -274,7 +274,7 @@ qtests_s390x = \ qtests_riscv32 = \ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) -qtests_riscv64 = \ +qtests_riscv64 = ['riscv-csr-test'] + \ (unpack_edk2_blobs ? ['bios-tables-test'] : []) qos_test_ss = ss.source_set() diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c new file mode 100644 index 0000000000..ff5c29e6c6 --- /dev/null +++ b/tests/qtest/riscv-csr-test.c @@ -0,0 +1,56 @@ +/* + * QTest testcase for RISC-V CSRs + * + * Copyright (c) 2024 Syntacore. + * + * 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. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define CSR_MVENDORID 0xf11 +#define CSR_MISELECT 0x350 + +static void run_test_csr(void) +{ + uint64_t res; + uint64_t val = 0; + + QTestState *qts = qtest_init("-machine virt -cpu veyron-v1"); + + res = qtest_csr_call(qts, "get_csr", 0, CSR_MVENDORID, &val); + + g_assert_cmpint(res, ==, 0); + g_assert_cmpint(val, ==, 0x61f); + + val = 0xff; + res = qtest_csr_call(qts, "set_csr", 0, CSR_MISELECT, &val); + + g_assert_cmpint(res, ==, 0); + + val = 0; + res = qtest_csr_call(qts, "get_csr", 0, CSR_MISELECT, &val); + + g_assert_cmpint(res, ==, 0); + g_assert_cmpint(val, ==, 0xff); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/cpu/csr", run_test_csr); + + return g_test_run(); +} From 99baa5d921e7414c83444c379510c6aad5c023a7 Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Tue, 7 Jan 2025 17:31:53 +0100 Subject: [PATCH 1171/2892] tests/qtest: Introduce qtest_init_with_env_and_capabilities() This patch adds a new version of qtest_init_with_env() that allows specifying QMP capabilities that should be enabled during handshake. This is useful for example if a test needs out-of-band execution of QMP commands, it can initialize with the oob capability. Signed-off-by: Juraj Marcin Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 18 ++++++++++++++++-- tests/qtest/libqtest.h | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 4bc9643aad..a1e105f27f 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -543,7 +543,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); } -QTestState *qtest_init_with_env(const char *var, const char *extra_args) +QTestState *qtest_init_with_env_and_capabilities(const char *var, + const char *extra_args, + QList *capabilities) { QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); QDict *greeting; @@ -551,11 +553,23 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args) /* Read the QMP greeting and then do the handshake */ greeting = qtest_qmp_receive(s); qobject_unref(greeting); - qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }")); + if (capabilities) { + qtest_qmp_assert_success(s, + "{ 'execute': 'qmp_capabilities', " + "'arguments': { 'enable': %p } }", + qobject_ref(capabilities)); + } else { + qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }"); + } return s; } +QTestState *qtest_init_with_env(const char *var, const char *extra_args) +{ + return qtest_init_with_env_and_capabilities(var, extra_args, NULL); +} + QTestState *qtest_init(const char *extra_args) { return qtest_init_with_env(NULL, extra_args); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index d771f29d63..8f3bde5d16 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -19,6 +19,7 @@ #include "qapi/qmp/qobject.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" #include "libqmp.h" typedef struct QTestState QTestState; @@ -68,6 +69,22 @@ QTestState *qtest_init(const char *extra_args); */ QTestState *qtest_init_with_env(const char *var, const char *extra_args); +/** + * qtest_init_with_env_and_capabilities: + * @var: Environment variable from where to take the QEMU binary + * @extra_args: Other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. + * @capabilities: list of QMP capabilities (strings) to enable + * + * Like qtest_init_with_env(), but enable specified capabilities during + * hadshake. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init_with_env_and_capabilities(const char *var, + const char *extra_args, + QList *capabilities); + /** * qtest_init_without_qmp_handshake: * @extra_args: other arguments to pass to QEMU. CAUTION: these From 3dec966f2798ebee41fc82fe4d1036d907ec51a4 Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Tue, 7 Jan 2025 17:31:54 +0100 Subject: [PATCH 1172/2892] tests/qtest/migration: Use out-of-band execution for migrate-recover In real use cases, the migrate-recover command requires out-of-band execution, because the thread processing normal commands is blocked by a page fault in the guest memory. With this change, the tests will be closer to real use cases and could help detect regressions and other bugs in migration recovery. Signed-off-by: Juraj Marcin Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 23 +++++++++++++++++++++-- tests/qtest/migration/framework.h | 2 ++ tests/qtest/migration/migration-qmp.c | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 47ce07856e..4550cda129 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -194,6 +194,16 @@ static void cleanup(const char *filename) unlink(path); } +static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) +{ + QList *capabilities = qlist_new(); + + if (args->oob) { + qlist_append_str(capabilities, "oob"); + } + return capabilities; +} + int migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { @@ -210,6 +220,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, const char *machine_alias, *machine_opts = ""; g_autofree char *machine = NULL; const char *bootpath; + g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { @@ -314,7 +325,8 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, args->opts_source ? args->opts_source : "", ignore_stderr); if (!args->only_target) { - *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); + *from = qtest_init_with_env_and_capabilities(QEMU_ENV_SRC, cmd_source, + capabilities); qtest_qmp_set_event_callback(*from, migrate_watch_for_events, &src_state); @@ -334,7 +346,8 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); - *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); + *to = qtest_init_with_env_and_capabilities(QEMU_ENV_DST, cmd_target, + capabilities); qtest_qmp_set_event_callback(*to, migrate_watch_for_events, &dst_state); @@ -601,6 +614,12 @@ void test_postcopy_recovery_common(MigrateCommon *args) QTestState *from, *to; g_autofree char *uri = NULL; + /* + * Always enable OOB QMP capability for recovery tests, migrate-recover is + * executed out-of-band + */ + args->start.oob = true; + /* Always hide errors for postcopy recover tests since they're expected */ args->start.hide_stderr = true; diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index e9fc4ec363..7991ee56b6 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -109,6 +109,8 @@ typedef struct { const char *opts_target; /* suspend the src before migrating to dest. */ bool suspend_me; + /* enable OOB QMP capability */ + bool oob; } MigrateStart; typedef enum PostcopyRecoveryFailStage { diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 71b14b51b2..9431d2beda 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -464,7 +464,7 @@ void migrate_continue(QTestState *who, const char *state) void migrate_recover(QTestState *who, const char *uri) { qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-recover', " + "{ 'exec-oob': 'migrate-recover', " " 'id': 'recover-cmd', " " 'arguments': { 'uri': %s } }", uri); From aa601bd4f1208d85906f7778679c57d91cef6c70 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 17 Jan 2025 11:27:36 +0100 Subject: [PATCH 1173/2892] tests/qtest/test-x86-cpuid-compat: Remove tests related to pc-i440fx-2.3 The pc-i440fx-2.3 machine type has been removed in commit 46a2bd5257 ("hw/i386/pc: Remove deprecated pc-i440fx-2.3 machine") already, so these tests are just dead code by now. Signed-off-by: Thomas Huth Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250117102738.59714-2-thuth@redhat.com Signed-off-by: Fabiano Rosas --- tests/qtest/test-x86-cpuid-compat.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index b9e7e5ef7b..9cbc8b7ae9 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -357,19 +357,6 @@ int main(int argc, char **argv) "486", "xstore=on", "pc-i440fx-2.7", "xlevel2", 0); } - /* - * QEMU 2.3.0 had auto-level enabled for CPUID[7], already, - * and the compat code that sets default level shouldn't - * disable the auto-level=7 code: - */ - if (qtest_has_machine("pc-i440fx-2.3")) { - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", - "Penryn", NULL, "pc-i440fx-2.3", - "level", 4); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", - "Penryn", "erms=on", "pc-i440fx-2.3", - "level", 7); - } if (qtest_has_machine("pc-i440fx-2.9")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", "Conroe", NULL, "pc-i440fx-2.9", @@ -384,11 +371,6 @@ int main(int argc, char **argv) * code on old machine-types. Just check that the compat code * is working correctly: */ - if (qtest_has_machine("pc-i440fx-2.3")) { - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", - "SandyBridge", NULL, "pc-i440fx-2.3", - "xlevel", 0x8000000a); - } if (qtest_has_machine("pc-i440fx-2.4")) { add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", "SandyBridge", NULL, "pc-i440fx-2.4", From d3203d5a0cff5a7064fe52b99510835e6b7eb03b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 16 Jan 2025 22:31:32 +0100 Subject: [PATCH 1174/2892] tcg: Document tb_lookup() and tcg_tb_lookup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These similarly named functions serve different purposes; add docstrings to highlight them. Suggested-by: Alex Bennée Signed-off-by: Ilya Leoshkevich Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250116213214.5695-1-iii@linux.ibm.com> --- accel/tcg/cpu-exec.c | 15 ++++++++++++++- include/tcg/tcg.h | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index d48b82a932..8b773d8847 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -249,7 +249,20 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc, return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); } -/* Might cause an exception, so have a longjmp destination ready */ +/** + * tb_lookup: + * @cpu: CPU that will execute the returned translation block + * @pc: guest PC + * @cs_base: arch-specific value associated with translation block + * @flags: arch-specific translation block flags + * @cflags: CF_* flags + * + * Look up a translation block inside the QHT using @pc, @cs_base, @flags and + * @cflags. Uses @cpu's tb_jmp_cache. Might cause an exception, so have a + * longjmp destination ready. + * + * Returns: an existing translation block or NULL. + */ static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc, uint64_t cs_base, uint32_t flags, uint32_t cflags) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index e7ddf979f6..1d1d668f52 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -638,10 +638,51 @@ void tcg_region_reset_all(void); size_t tcg_code_size(void); size_t tcg_code_capacity(void); +/** + * tcg_tb_insert: + * @tb: translation block to insert + * + * Insert @tb into the region trees. + */ void tcg_tb_insert(TranslationBlock *tb); + +/** + * tcg_tb_remove: + * @tb: translation block to remove + * + * Remove @tb from the region trees. + */ void tcg_tb_remove(TranslationBlock *tb); + +/** + * tcg_tb_lookup: + * @tc_ptr: host PC to look up + * + * Look up a translation block inside the region trees by @tc_ptr. This is + * useful for exception handling, but must not be used for the purposes of + * executing the returned translation block. See struct tb_tc for more + * information. + * + * Returns: a translation block previously inserted into the region trees, + * such that @tc_ptr points anywhere inside the code generated for it, or + * NULL. + */ TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); + +/** + * tcg_tb_foreach: + * @func: callback + * @user_data: opaque value to pass to @callback + * + * Call @func for each translation block inserted into the region trees. + */ void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); + +/** + * tcg_nb_tbs: + * + * Returns: the number of translation blocks inserted into the region trees. + */ size_t tcg_nb_tbs(void); /* user-mode: Called with mmap_lock held. */ From 5313b1aaaccca99b2fe5a03fc6b3bfad65597b22 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 16 Jan 2025 22:31:33 +0100 Subject: [PATCH 1175/2892] accel/tcg: Call tcg_tb_insert() for one-insn TBs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently one-insn TBs created from I/O memory are not added to region_trees. Therefore, when they generate exceptions, they are not handled by cpu_restore_state_from_tb(). For x86 this is not a problem, because x86_restore_state_to_opc() only restores pc and cc, which already have the correct values if the first TB instruction causes an exception. However, on several other architectures, restore_state_to_opc() is not stricly limited to state restoration and affects some exception-related registers, where guests can notice incorrect values, for example: - arm's exception.syndrome; - hppa's unwind_breg; - riscv's excp_uw2; - s390x's int_pgm_ilen. Fix by always calling tcg_tb_insert(). This may increase the size of region_trees, but tcg_region_reset_all() clears it once code_gen_buffer fills up, so it will not grow uncontrollably. Do not call tb_link_page(), which would add such TBs to the QHT, to prevent tb_lookup() from finding them. These TBs are single-use, since subsequent reads from I/O memory may return different values; they are not removed from code_gen_buffer only in order to keep things simple. Co-developed-by: Nina Schoetterl-Glausch Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Message-ID: <20250116213214.5695-2-iii@linux.ibm.com> --- accel/tcg/translate-all.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 453eb20ec9..7ec1c53f24 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -531,16 +531,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb_reset_jump(tb, 1); } - /* - * If the TB is not associated with a physical RAM page then it must be - * a temporary one-insn TB, and we have nothing left to do. Return early - * before attempting to link to other TBs or add to the lookup table. - */ - if (tb_page_addr0(tb) == -1) { - assert_no_pages_locked(); - return tb; - } - /* * Insert TB into the corresponding region tree before publishing it * through QHT. Otherwise rewinding happened in the TB might fail to @@ -548,6 +538,25 @@ TranslationBlock *tb_gen_code(CPUState *cpu, */ tcg_tb_insert(tb); + /* + * If the TB is not associated with a physical RAM page then it must be + * a temporary one-insn TB. + * + * Such TBs must be added to region trees in order to make sure that + * restore_state_to_opc() - which on some architectures is not limited to + * rewinding, but also affects exception handling! - is called when such a + * TB causes an exception. + * + * At the same time, temporary one-insn TBs must be executed at most once, + * because subsequent reads from, e.g., I/O memory may return different + * values. So return early before attempting to link to other TBs or add + * to the QHT. + */ + if (tb_page_addr0(tb) == -1) { + assert_no_pages_locked(); + return tb; + } + /* * No explicit memory barrier is required -- tb_link_page() makes the * TB visible in a consistent state. From db1649823d4f27b924a5aa5f9e0111457accb798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 22:43:58 +0100 Subject: [PATCH 1176/2892] softfloat: Constify helpers returning float_status field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These helpers don't alter float_status. Make it const. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250116214359.67295-1-philmd@linaro.org> --- include/fpu/softfloat-helpers.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index dceee23c82..4cb30a4822 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -124,58 +124,61 @@ static inline void set_no_signaling_nans(bool val, float_status *status) status->no_signaling_nans = val; } -static inline bool get_float_detect_tininess(float_status *status) +static inline bool get_float_detect_tininess(const float_status *status) { return status->tininess_before_rounding; } -static inline FloatRoundMode get_float_rounding_mode(float_status *status) +static inline FloatRoundMode get_float_rounding_mode(const float_status *status) { return status->float_rounding_mode; } -static inline int get_float_exception_flags(float_status *status) +static inline int get_float_exception_flags(const float_status *status) { return status->float_exception_flags; } static inline FloatX80RoundPrec -get_floatx80_rounding_precision(float_status *status) +get_floatx80_rounding_precision(const float_status *status) { return status->floatx80_rounding_precision; } -static inline Float2NaNPropRule get_float_2nan_prop_rule(float_status *status) +static inline Float2NaNPropRule +get_float_2nan_prop_rule(const float_status *status) { return status->float_2nan_prop_rule; } -static inline Float3NaNPropRule get_float_3nan_prop_rule(float_status *status) +static inline Float3NaNPropRule +get_float_3nan_prop_rule(const float_status *status) { return status->float_3nan_prop_rule; } -static inline FloatInfZeroNaNRule get_float_infzeronan_rule(float_status *status) +static inline FloatInfZeroNaNRule +get_float_infzeronan_rule(const float_status *status) { return status->float_infzeronan_rule; } -static inline uint8_t get_float_default_nan_pattern(float_status *status) +static inline uint8_t get_float_default_nan_pattern(const float_status *status) { return status->default_nan_pattern; } -static inline bool get_flush_to_zero(float_status *status) +static inline bool get_flush_to_zero(const float_status *status) { return status->flush_to_zero; } -static inline bool get_flush_inputs_to_zero(float_status *status) +static inline bool get_flush_inputs_to_zero(const float_status *status) { return status->flush_inputs_to_zero; } -static inline bool get_default_nan_mode(float_status *status) +static inline bool get_default_nan_mode(const float_status *status) { return status->default_nan_mode; } From ffd23ae2a6374c9754f33f992874722735cc7f7c Mon Sep 17 00:00:00 2001 From: Craig Blackmore Date: Wed, 18 Dec 2024 14:23:52 +0000 Subject: [PATCH 1177/2892] target/riscv: rvv: fix typo in vext continuous ldst function names Replace `continus` with `continuous`. Signed-off-by: Craig Blackmore Reviewed-by: Daniel Henrique Barboza Reviewed-by: Max Chou Reviewed-by: Richard Henderson Message-ID: <20241218142353.1027938-2-craig.blackmore@embecosm.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index a85dd1d200..0f57e48cc5 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -195,7 +195,7 @@ GEN_VEXT_ST_ELEM(ste_w, uint32_t, H4, stl) GEN_VEXT_ST_ELEM(ste_d, uint64_t, H8, stq) static inline QEMU_ALWAYS_INLINE void -vext_continus_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb, +vext_continuous_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb, void *vd, uint32_t evl, target_ulong addr, uint32_t reg_start, uintptr_t ra, uint32_t esz, bool is_load) @@ -207,7 +207,7 @@ vext_continus_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb, } static inline QEMU_ALWAYS_INLINE void -vext_continus_ldst_host(CPURISCVState *env, vext_ldst_elem_fn_host *ldst_host, +vext_continuous_ldst_host(CPURISCVState *env, vext_ldst_elem_fn_host *ldst_host, void *vd, uint32_t evl, uint32_t reg_start, void *host, uint32_t esz, bool is_load) { @@ -342,8 +342,8 @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, if (flags == 0) { if (nf == 1) { - vext_continus_ldst_host(env, ldst_host, vd, evl, env->vstart, host, - esz, is_load); + vext_continuous_ldst_host(env, ldst_host, vd, evl, env->vstart, + host, esz, is_load); } else { for (i = env->vstart; i < evl; ++i) { k = 0; @@ -357,7 +357,7 @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, env->vstart += elems; } else { if (nf == 1) { - vext_continus_ldst_tlb(env, ldst_tlb, vd, evl, addr, env->vstart, + vext_continuous_ldst_tlb(env, ldst_tlb, vd, evl, addr, env->vstart, ra, esz, is_load); } else { /* load bytes from guest memory */ From d4ce7ef4b3b867e4d369f6024cf5f217f7bc2202 Mon Sep 17 00:00:00 2001 From: Craig Blackmore Date: Wed, 18 Dec 2024 14:23:53 +0000 Subject: [PATCH 1178/2892] target/riscv: rvv: speed up small unit-stride loads and stores Calling `vext_continuous_ldst_tlb` for load/stores up to 6 bytes significantly improves performance. Co-authored-by: Helene CHELIN Co-authored-by: Paolo Savini Co-authored-by: Craig Blackmore Signed-off-by: Helene CHELIN Signed-off-by: Paolo Savini Signed-off-by: Craig Blackmore Reviewed-by: Daniel Henrique Barboza Reviewed-by: Richard Henderson Message-ID: <20241218142353.1027938-3-craig.blackmore@embecosm.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 0f57e48cc5..ead3ec5194 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -393,6 +393,22 @@ vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, return; } +#if defined(CONFIG_USER_ONLY) + /* + * For data sizes <= 6 bytes we get better performance by simply calling + * vext_continuous_ldst_tlb + */ + if (nf == 1 && (evl << log2_esz) <= 6) { + addr = base + (env->vstart << log2_esz); + vext_continuous_ldst_tlb(env, ldst_tlb, vd, evl, addr, env->vstart, ra, + esz, is_load); + + env->vstart = 0; + vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems); + return; + } +#endif + /* Calculate the page range of first page */ addr = base + ((env->vstart * nf) << log2_esz); page_split = -(addr | TARGET_PAGE_MASK); From e9952b3631b97f35d06052e0f3ec7ce812c9b539 Mon Sep 17 00:00:00 2001 From: Yanfeng Liu Date: Mon, 16 Dec 2024 05:36:35 +0800 Subject: [PATCH 1179/2892] riscv/gdbstub: add V bit to priv reg This adds virtualization mode (V bit) as bit(2) of register `priv` per RiscV debug spec v1.0.0-rc4. Checked with gdb-multiarch v12.1. Note that GDB may display `INVALID` tag for `priv` reg when V bit is set, this doesn't affect actual access to the bit though. Signed-off-by: Yanfeng Liu Reviewed-by: Alistair Francis Message-ID: Signed-off-by: Alistair Francis --- target/riscv/gdbstub.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index c07df972f1..18e88f416a 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -213,7 +213,10 @@ static int riscv_gdb_get_virtual(CPUState *cs, GByteArray *buf, int n) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - return gdb_get_regl(buf, env->priv); + /* Per RiscV debug spec v1.0.0 rc4 */ + target_ulong vbit = (env->virt_enabled) ? BIT(2) : 0; + + return gdb_get_regl(buf, env->priv | vbit); #endif } return 0; @@ -226,10 +229,22 @@ static int riscv_gdb_set_virtual(CPUState *cs, uint8_t *mem_buf, int n) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - env->priv = ldtul_p(mem_buf) & 0x3; - if (env->priv == PRV_RESERVED) { - env->priv = PRV_S; + target_ulong new_priv = ldtul_p(mem_buf) & 0x3; + bool new_virt = 0; + + if (new_priv == PRV_RESERVED) { + new_priv = PRV_S; } + + if (new_priv != PRV_M) { + new_virt = (ldtul_p(mem_buf) & BIT(2)) >> 2; + } + + if (riscv_has_ext(env, RVH) && new_virt != env->virt_enabled) { + riscv_cpu_swap_hypervisor_regs(env); + } + + riscv_cpu_set_mode(env, new_priv, new_virt); #endif return sizeof(target_ulong); } From 3739732e755d84859fc2278ae3fff7d3869507b5 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Dec 2024 08:40:20 -0300 Subject: [PATCH 1180/2892] target/riscv: add shcounterenw shcounterenw is defined in RVA22 as: "For any hpmcounter that is not read-only zero, the corresponding bit in hcounteren must be writable." This is always true in TCG so let's claim support for it. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 332 -> 346 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b8d5120106..07bcf96e86 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -183,6 +183,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), + ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 4f231735abad925435c3cd052e6641b1b4187278..460808d017baef93ccdd8fd8d1d4722edefd3b86 100644 GIT binary patch delta 55 zcmX@Zbc=~A$iq1#ijjeV(RCu10qYM2Muztj?N@PUB=9N!;ZarC%QJKR4 IsLBe60nM=wbpQYW delta 43 wcmcb`bcTs5$iq3rhmnDSk#8cG0qZLUMutZd?N?3wW;xk_QHetdD5C?!0RK1&R{#J2 From 8d6855ac7ef797f433c5b75f33e3be8f306eaa37 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Dec 2024 08:40:21 -0300 Subject: [PATCH 1181/2892] target/riscv: add shvstvala shvstvala is defined in RVA22 as: "vstval must be written in all cases described above for stval." By "cases describe above" the doc refer to the description of sstvala: "stval must be written with the faulting virtual address for load, store, and instruction page-fault, access-fault, and misaligned exceptions, and for breakpoint exceptions other than those caused by execution of the EBREAK or C.EBREAK instructions. For virtual-instruction and illegal-instruction exceptions, stval must be written with the faulting instruction." We already have sstvala, and our vstval follows the same rules as stval, so we can claim to support shvstvala too. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 346 -> 356 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 07bcf96e86..4f76efc298 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -184,6 +184,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 460808d017baef93ccdd8fd8d1d4722edefd3b86..15b82b5bb1cf24cf501e74cb82682742e0041ea6 100644 GIT binary patch delta 48 zcmcb`^n{5k$iq1#g^_`Q@%ltA16C$RMuwjg?YDDelogkhCFUef{1V4u1(bFHVgOlv B4Ql`Z delta 39 scmaFDbc=~A$iq1#ijjeV(RCu10qYM2Muztj?YB>2jNvcz>% From e306fff7f83285a385c29927833b1633e51d431b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Dec 2024 08:40:22 -0300 Subject: [PATCH 1182/2892] target/riscv: add shtvala shtvala is described in RVA22 as: "htval must be written with the faulting guest physical address in all circumstances permitted by the ISA." This is the case since commit 3067553993, so claim support for shtvala. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 356 -> 364 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4f76efc298..fe5f7b572f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -184,6 +184,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 15b82b5bb1cf24cf501e74cb82682742e0041ea6..065f894010272e7f27834b2c8d5d5fb0c21066a1 100644 GIT binary patch delta 46 zcmaFD^oEHm$iq1#hmnDSF=ir{0UH-1BLgGjM2GFHC1r^@i4(tMa5w=aeSjDM5FiTU delta 40 tcmaFE^n{5k$iq1#g^_`Q@%ltA12!f`MuwjZ6CJis{GZBU1r&AxVgTjp3LpRg From 73afe5c2f930b0ca86f7e8a43d501aa1908924ed Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Dec 2024 08:40:23 -0300 Subject: [PATCH 1183/2892] target/riscv: add shvstvecd shvstvecd is defined in RVA22 as: "vstvec.MODE must be capable of holding the value 0 (Direct). When vstvec.MODE=Direct, vstvec.BASE must be capable of holding any valid four-byte-aligned address." This is always true for TCG so let's claim support for it. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 364 -> 374 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fe5f7b572f..20cbb6b2f4 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -186,6 +186,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 065f894010272e7f27834b2c8d5d5fb0c21066a1..2c7dc6c9ab8d8da9c30ad34294ef28427a4f8f1a 100644 GIT binary patch delta 49 zcmaFE^o@xt$iq3LjFEwX@#91;16C16Mh5PQ_9r Date: Wed, 18 Dec 2024 08:40:24 -0300 Subject: [PATCH 1184/2892] target/riscv: add shvsatpa shvsatpa is defined in RVA22 as: "All translation modes supported in satp must be supported in vsatp." This is always true in TCG so let's claim support for it. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 374 -> 382 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 20cbb6b2f4..2f58eeb689 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -185,6 +185,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shvsatpa, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 2c7dc6c9ab8d8da9c30ad34294ef28427a4f8f1a..fcd9c95a6ae6e6977e5d9c33a39785269a28aa58 100644 GIT binary patch delta 47 zcmeyy^pA-v$iq3Lj*)?Z@&80F16CPEMh20I_J^1gOA03b4w&r2sKgNhl#K&o07Z=q ADF6Tf delta 44 xcmeyz^o@xt$iq3LjFEwX@#91;16C16Mh5PQ_J<~OF?vl7W>n_z0ZN4dF#rbF3BCXT From 2fedb6b1835cc83a168a920dd39dbd3cd834c254 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Dec 2024 08:40:25 -0300 Subject: [PATCH 1185/2892] target/riscv: add shgatpa shgatpa is defined in RVA22 as: "For each supported virtual memory scheme SvNN supported in satp, the corresponding hgatp SvNNx4 mode must be supported. The hgatp mode Bare must also be supported." Claim support for shgatpa since this is always true for TCG. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 382 -> 390 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2f58eeb689..3e138572d4 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -184,6 +184,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shgatpa, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvsatpa, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index fcd9c95a6ae6e6977e5d9c33a39785269a28aa58..695022d56c4ac16607d4c622955ad339fbbfe997 100644 GIT binary patch delta 45 ycmeyz)W*yel|HIAJ1}0jmllBZJID`yH(5i6sSz6TdWZ!~o^efEWM|9t&Im delta 39 scmZo;{>Q`>6I_pbMdN@N0MP#lnE(I) From f4df21e07f126eab24adf505cb33db0c94968cab Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Dec 2024 08:40:26 -0300 Subject: [PATCH 1186/2892] target/riscv/tcg: add sha 'sha' is the augmented hypervisor extension, defined in RVA22 as a set of the following extensions: - RVH - Ssstateen - Shcounterenw (always present) - Shvstvala (always present) - Shtvala (always present) - Shvstvecd (always present) - Shvsatpa (always present) - Shgatpa (always present) We can claim support for 'sha' by checking if we have RVH and ssstateen. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20241218114026.1652352-10-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 8 ++++++++ 3 files changed, 11 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3e138572d4..954425081d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -184,6 +184,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha), ISA_EXT_DATA_ENTRY(shgatpa, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvsatpa, PRIV_VERSION_1_12_0, has_priv_1_12), @@ -1714,6 +1715,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), + MULTI_EXT_CFG_BOOL("sha", ext_sha, true), { }, }; diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index a1457ab4f4..fe0c4173d2 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -141,6 +141,7 @@ struct RISCVCPUConfig { bool ext_svade; bool ext_zic64b; bool ext_ssstateen; + bool ext_sha; /* * Always 'true' booleans for named features diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 8b89c99c0f..e03b409248 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -212,6 +212,11 @@ static void riscv_cpu_enable_named_feat(RISCVCPU *cpu, uint32_t feat_offset) cpu->cfg.cbop_blocksize = 64; cpu->cfg.cboz_blocksize = 64; break; + case CPU_CFG_OFFSET(ext_sha): + if (!cpu_misa_ext_is_user_set(RVH)) { + riscv_cpu_write_misa_bit(cpu, RVH, true); + } + /* fallthrough */ case CPU_CFG_OFFSET(ext_ssstateen): cpu->cfg.ext_smstateen = true; break; @@ -352,6 +357,9 @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.cboz_blocksize == 64; cpu->cfg.ext_ssstateen = cpu->cfg.ext_smstateen; + + cpu->cfg.ext_sha = riscv_has_ext(&cpu->env, RVH) && + cpu->cfg.ext_ssstateen; } static void riscv_cpu_validate_g(RISCVCPU *cpu) From e2dca2dc5abfba504d4ac9222673a9edbc1c1266 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Jan 2025 14:37:33 -0300 Subject: [PATCH 1187/2892] target/riscv: use RISCVException enum in exception helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do a cosmetic change in riscv_raise_exception() to change 'exception' type from uint32_t to RISCVException, making it a bit clear that the arg is directly correlated to the RISCVException enum. As a side effect, change 'excp' type from int to RISCVException in generate_exception() to guarantee that all callers of riscv_raise_exception() will use the enum. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250106173734.412353-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 3 ++- target/riscv/op_helper.c | 3 ++- target/riscv/translate.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 252fdb8672..3d9c404254 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -606,7 +606,8 @@ void riscv_translate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); G_NORETURN void riscv_raise_exception(CPURISCVState *env, - uint32_t exception, uintptr_t pc); + RISCVException exception, + uintptr_t pc); target_ulong riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index eddedacf4b..29c104bc23 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -27,7 +27,8 @@ /* Exceptions processing helpers */ G_NORETURN void riscv_raise_exception(CPURISCVState *env, - uint32_t exception, uintptr_t pc) + RISCVException exception, + uintptr_t pc) { CPUState *cs = env_cpu(env); cs->exception_index = exception; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index a992d4f3c6..f46d76c785 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -245,7 +245,7 @@ static void gen_update_pc(DisasContext *ctx, target_long diff) ctx->pc_save = ctx->base.pc_next + diff; } -static void generate_exception(DisasContext *ctx, int excp) +static void generate_exception(DisasContext *ctx, RISCVException excp) { gen_update_pc(ctx, 0); gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); From 8f1a1289429b3bcb9709c0cfef2006d759e2936b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Jan 2025 14:37:34 -0300 Subject: [PATCH 1188/2892] target/riscv: add trace in riscv_raise_exception() When using system mode we can get the CPU traps being taken via the 'riscv_trap' trace or the "-d int" qemu log. User mode does not a way of logging/showing exceptions to users. Add a trace in riscv_raise_exception() to allow qemu-riscv(32/64) users to check all exceptions being thrown. This is particularly useful to help identifying insns that are throwing SIGILLs. As it is today we need to debug their binaries to identify where the illegal insns are: $ ~/work/qemu/build/qemu-riscv64 -cpu rv64 ./foo.out Illegal instruction (core dumped) After this change users can capture the trace and use EPC to pinpoint the insn: $ ~/work/qemu/build/qemu-riscv64 -cpu rv64 -trace riscv_exception ./foo.out riscv_exception 8 (user_ecall) on epc 0x17cd2 riscv_exception 8 (user_ecall) on epc 0x17cda riscv_exception 8 (user_ecall) on epc 0x17622 (...) riscv_exception 2 (illegal_instruction) on epc 0x1053a Illegal instruction (core dumped) Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106173734.412353-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/op_helper.c | 6 ++++++ target/riscv/trace-events | 3 +++ 2 files changed, 9 insertions(+) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 29c104bc23..29de8eb43d 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" +#include "trace.h" /* Exceptions processing helpers */ G_NORETURN void riscv_raise_exception(CPURISCVState *env, @@ -31,6 +32,11 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, uintptr_t pc) { CPUState *cs = env_cpu(env); + + trace_riscv_exception(exception, + riscv_cpu_get_trap_name(exception, false), + env->pc); + cs->exception_index = exception; cpu_loop_exit_restore(cs, pc); } diff --git a/target/riscv/trace-events b/target/riscv/trace-events index 49ec4d3b7d..93837f82a1 100644 --- a/target/riscv/trace-events +++ b/target/riscv/trace-events @@ -9,3 +9,6 @@ pmpaddr_csr_write(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" mseccfg_csr_read(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": read mseccfg, val: 0x%" PRIx64 mseccfg_csr_write(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": write mseccfg, val: 0x%" PRIx64 + +# op_helper.c +riscv_exception(uint32_t exception, const char *desc, uint64_t epc) "%u (%s) on epc 0x%"PRIx64"" From 37089cb8ad3e0ffd552a68101e42697eb9dcd48a Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:40 +0300 Subject: [PATCH 1189/2892] target/riscv: Remove obsolete pointer masking extension code. Zjpm extension is finally ratified. And it's much simplier compared to the experimental one. The newer version doesn't allow to specify custom mask or base for pointer masking. Instead it allows only certain options for masking top bits. Signed-off-by: Alexey Baturo Acked-by: Alistair Francis Message-ID: <20250106102346.1100149-2-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 13 +- target/riscv/cpu.h | 33 +--- target/riscv/cpu_bits.h | 87 ---------- target/riscv/cpu_helper.c | 52 ------ target/riscv/csr.c | 326 ----------------------------------- target/riscv/machine.c | 17 +- target/riscv/tcg/tcg-cpu.c | 5 +- target/riscv/translate.c | 28 +-- target/riscv/vector_helper.c | 2 +- 9 files changed, 19 insertions(+), 544 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 954425081d..99588e219e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -42,7 +42,7 @@ /* RISC-V CPU definitions */ static const char riscv_single_letter_exts[] = "IEMAFDQCBPVH"; const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, - RVC, RVS, RVU, RVH, RVJ, RVG, RVB, 0}; + RVC, RVS, RVU, RVH, RVG, RVB, 0}; /* * From vector_helper.c @@ -896,13 +896,6 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CSR_MSCRATCH, CSR_SSCRATCH, CSR_SATP, - CSR_MMTE, - CSR_UPMBASE, - CSR_UPMMASK, - CSR_SPMBASE, - CSR_SPMMASK, - CSR_MPMBASE, - CSR_MPMMASK, }; for (i = 0; i < ARRAY_SIZE(dump_csrs); ++i) { @@ -1088,8 +1081,6 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) } i++; } - /* mmte is supposed to have pm.current hardwired to 1 */ - env->mmte |= (EXT_STATUS_INITIAL | MMTE_M_PM_CURRENT); /* * Bits 10, 6, 2 and 12 of mideleg are read only 1 when the Hypervisor @@ -1121,7 +1112,6 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) env->ssp = 0; env->xl = riscv_cpu_mxl(env); - riscv_cpu_update_mask(env); cs->exception_index = RISCV_EXCP_NONE; env->load_res = -1; set_default_nan_mode(1, &env->fp_status); @@ -1511,7 +1501,6 @@ static const MISAExtInfo misa_ext_info_arr[] = { MISA_EXT_INFO(RVS, "s", "Supervisor-level instructions"), MISA_EXT_INFO(RVU, "u", "User-level instructions"), MISA_EXT_INFO(RVH, "h", "Hypervisor"), - MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"), MISA_EXT_INFO(RVV, "v", "Vector operations"), MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"), MISA_EXT_INFO(RVB, "b", "Bit manipulation (Zba_Zbb_Zbs)") diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 3d9c404254..c78e97af50 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -71,7 +71,6 @@ typedef struct CPUArchState CPURISCVState; #define RVS RV('S') #define RVU RV('U') #define RVH RV('H') -#define RVJ RV('J') #define RVG RV('G') #define RVB RV('B') @@ -451,24 +450,11 @@ struct CPUArchState { /* True if in debugger mode. */ bool debugger; - /* - * CSRs for PointerMasking extension - */ - target_ulong mmte; - target_ulong mpmmask; - target_ulong mpmbase; - target_ulong spmmask; - target_ulong spmbase; - target_ulong upmmask; - target_ulong upmbase; - uint64_t mstateen[SMSTATEEN_MAX_COUNT]; uint64_t hstateen[SMSTATEEN_MAX_COUNT]; uint64_t sstateen[SMSTATEEN_MAX_COUNT]; uint64_t henvcfg; #endif - target_ulong cur_pmmask; - target_ulong cur_pmbase; /* Fields from here on are preserved across CPU reset. */ QEMUTimer *stimer; /* Internal timer for S-mode interrupt */ @@ -628,19 +614,19 @@ FIELD(TB_FLAGS, XL, 16, 2) /* If PointerMasking should be applied */ FIELD(TB_FLAGS, PM_MASK_ENABLED, 18, 1) FIELD(TB_FLAGS, PM_BASE_ENABLED, 19, 1) -FIELD(TB_FLAGS, VTA, 20, 1) -FIELD(TB_FLAGS, VMA, 21, 1) +FIELD(TB_FLAGS, VTA, 18, 1) +FIELD(TB_FLAGS, VMA, 19, 1) /* Native debug itrigger */ -FIELD(TB_FLAGS, ITRIGGER, 22, 1) +FIELD(TB_FLAGS, ITRIGGER, 20, 1) /* Virtual mode enabled */ -FIELD(TB_FLAGS, VIRT_ENABLED, 23, 1) -FIELD(TB_FLAGS, PRIV, 24, 2) -FIELD(TB_FLAGS, AXL, 26, 2) +FIELD(TB_FLAGS, VIRT_ENABLED, 21, 1) +FIELD(TB_FLAGS, PRIV, 22, 2) +FIELD(TB_FLAGS, AXL, 24, 2) /* zicfilp needs a TB flag to track indirect branches */ -FIELD(TB_FLAGS, FCFI_ENABLED, 28, 1) -FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 29, 1) +FIELD(TB_FLAGS, FCFI_ENABLED, 26, 1) +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1) /* zicfiss needs a TB flag so that correct TB is located based on tb flags */ -FIELD(TB_FLAGS, BCFI_ENABLED, 30, 1) +FIELD(TB_FLAGS, BCFI_ENABLED, 28, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) @@ -776,7 +762,6 @@ static inline uint32_t vext_get_vlmax(uint32_t vlenb, uint32_t vsew, void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, uint64_t *cs_base, uint32_t *pflags); -void riscv_cpu_update_mask(CPURISCVState *env); bool riscv_cpu_is_32bit(RISCVCPU *cpu); RISCVException riscv_csrr(CPURISCVState *env, int csrno, diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index fe4e34c64a..c5b3de6469 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -497,37 +497,6 @@ #define CSR_MHPMCOUNTER30H 0xb9e #define CSR_MHPMCOUNTER31H 0xb9f -/* - * User PointerMasking registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_UMTE 0x4c0 -#define CSR_UPMMASK 0x4c1 -#define CSR_UPMBASE 0x4c2 - -/* - * Machine PointerMasking registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_MMTE 0x3c0 -#define CSR_MPMMASK 0x3c1 -#define CSR_MPMBASE 0x3c2 - -/* - * Supervisor PointerMaster registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_SMTE 0x1c0 -#define CSR_SPMMASK 0x1c1 -#define CSR_SPMBASE 0x1c2 - -/* - * Hypervisor PointerMaster registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_VSMTE 0x2c0 -#define CSR_VSPMMASK 0x2c1 -#define CSR_VSPMBASE 0x2c2 #define CSR_SCOUNTOVF 0xda0 /* Crypto Extension */ @@ -759,11 +728,6 @@ typedef enum RISCVException { #define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) #define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) -/* General PointerMasking CSR bits */ -#define PM_ENABLE 0x00000001ULL -#define PM_CURRENT 0x00000002ULL -#define PM_INSN 0x00000004ULL - /* Execution environment configuration bits */ #define MENVCFG_FIOM BIT(0) #define MENVCFG_LPE BIT(2) /* zicfilp */ @@ -803,57 +767,6 @@ typedef enum RISCVException { #define HENVCFGH_PBMTE MENVCFGH_PBMTE #define HENVCFGH_STCE MENVCFGH_STCE -/* Offsets for every pair of control bits per each priv level */ -#define XS_OFFSET 0ULL -#define U_OFFSET 2ULL -#define S_OFFSET 5ULL -#define M_OFFSET 8ULL - -#define PM_XS_BITS (EXT_STATUS_MASK << XS_OFFSET) -#define U_PM_ENABLE (PM_ENABLE << U_OFFSET) -#define U_PM_CURRENT (PM_CURRENT << U_OFFSET) -#define U_PM_INSN (PM_INSN << U_OFFSET) -#define S_PM_ENABLE (PM_ENABLE << S_OFFSET) -#define S_PM_CURRENT (PM_CURRENT << S_OFFSET) -#define S_PM_INSN (PM_INSN << S_OFFSET) -#define M_PM_ENABLE (PM_ENABLE << M_OFFSET) -#define M_PM_CURRENT (PM_CURRENT << M_OFFSET) -#define M_PM_INSN (PM_INSN << M_OFFSET) - -/* mmte CSR bits */ -#define MMTE_PM_XS_BITS PM_XS_BITS -#define MMTE_U_PM_ENABLE U_PM_ENABLE -#define MMTE_U_PM_CURRENT U_PM_CURRENT -#define MMTE_U_PM_INSN U_PM_INSN -#define MMTE_S_PM_ENABLE S_PM_ENABLE -#define MMTE_S_PM_CURRENT S_PM_CURRENT -#define MMTE_S_PM_INSN S_PM_INSN -#define MMTE_M_PM_ENABLE M_PM_ENABLE -#define MMTE_M_PM_CURRENT M_PM_CURRENT -#define MMTE_M_PM_INSN M_PM_INSN -#define MMTE_MASK (MMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | MMTE_U_PM_INSN | \ - MMTE_S_PM_ENABLE | MMTE_S_PM_CURRENT | MMTE_S_PM_INSN | \ - MMTE_M_PM_ENABLE | MMTE_M_PM_CURRENT | MMTE_M_PM_INSN | \ - MMTE_PM_XS_BITS) - -/* (v)smte CSR bits */ -#define SMTE_PM_XS_BITS PM_XS_BITS -#define SMTE_U_PM_ENABLE U_PM_ENABLE -#define SMTE_U_PM_CURRENT U_PM_CURRENT -#define SMTE_U_PM_INSN U_PM_INSN -#define SMTE_S_PM_ENABLE S_PM_ENABLE -#define SMTE_S_PM_CURRENT S_PM_CURRENT -#define SMTE_S_PM_INSN S_PM_INSN -#define SMTE_MASK (SMTE_U_PM_ENABLE | SMTE_U_PM_CURRENT | SMTE_U_PM_INSN | \ - SMTE_S_PM_ENABLE | SMTE_S_PM_CURRENT | SMTE_S_PM_INSN | \ - SMTE_PM_XS_BITS) - -/* umte CSR bits */ -#define UMTE_U_PM_ENABLE U_PM_ENABLE -#define UMTE_U_PM_CURRENT U_PM_CURRENT -#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 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f62b21e182..8c1969294f 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -210,61 +210,10 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); - if (env->cur_pmmask != 0) { - flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1); - } - if (env->cur_pmbase != 0) { - flags = FIELD_DP32(flags, TB_FLAGS, PM_BASE_ENABLED, 1); - } *pflags = flags; } -void riscv_cpu_update_mask(CPURISCVState *env) -{ - target_ulong mask = 0, base = 0; - RISCVMXL xl = env->xl; - /* - * TODO: Current RVJ spec does not specify - * how the extension interacts with XLEN. - */ -#ifndef CONFIG_USER_ONLY - int mode = cpu_address_mode(env); - xl = cpu_get_xl(env, mode); - if (riscv_has_ext(env, RVJ)) { - switch (mode) { - case PRV_M: - if (env->mmte & M_PM_ENABLE) { - mask = env->mpmmask; - base = env->mpmbase; - } - break; - case PRV_S: - if (env->mmte & S_PM_ENABLE) { - mask = env->spmmask; - base = env->spmbase; - } - break; - case PRV_U: - if (env->mmte & U_PM_ENABLE) { - mask = env->upmmask; - base = env->upmbase; - } - break; - default: - g_assert_not_reached(); - } - } -#endif - if (xl == MXL_RV32) { - env->cur_pmmask = mask & UINT32_MAX; - env->cur_pmbase = base & UINT32_MAX; - } else { - env->cur_pmmask = mask; - env->cur_pmbase = base; - } -} - #ifndef CONFIG_USER_ONLY /* @@ -786,7 +735,6 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) /* tlb_flush is unnecessary as mode is contained in mmu_idx */ env->priv = newpriv; env->xl = cpu_recompute_xl(env); - riscv_cpu_update_mask(env); /* * Clear the load reservation - otherwise a reservation placed in one diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 381cda81f8..48abcab487 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -531,16 +531,6 @@ static RISCVException hgatp(CPURISCVState *env, int csrno) return hmode(env, csrno); } -/* Checks if PointerMasking registers could be accessed */ -static RISCVException pointer_masking(CPURISCVState *env, int csrno) -{ - /* Check if j-ext is present */ - if (riscv_has_ext(env, RVJ)) { - return RISCV_EXCP_NONE; - } - return RISCV_EXCP_ILLEGAL_INST; -} - static RISCVException aia_hmode(CPURISCVState *env, int csrno) { if (!riscv_cpu_cfg(env)->ext_ssaia) { @@ -1648,7 +1638,6 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, env->xl = cpu_recompute_xl(env); } - riscv_cpu_update_mask(env); return RISCV_EXCP_NONE; } @@ -4358,302 +4347,6 @@ static RISCVException write_mcontext(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -/* - * Functions to access Pointer Masking feature registers - * We have to check if current priv lvl could modify - * csr in given mode - */ -static bool check_pm_current_disabled(CPURISCVState *env, int csrno) -{ - int csr_priv = get_field(csrno, 0x300); - int pm_current; - - if (env->debugger) { - return false; - } - /* - * If priv lvls differ that means we're accessing csr from higher priv lvl, - * so allow the access - */ - if (env->priv != csr_priv) { - return false; - } - switch (env->priv) { - case PRV_M: - pm_current = get_field(env->mmte, M_PM_CURRENT); - break; - case PRV_S: - pm_current = get_field(env->mmte, S_PM_CURRENT); - break; - case PRV_U: - pm_current = get_field(env->mmte, U_PM_CURRENT); - break; - default: - g_assert_not_reached(); - } - /* It's same priv lvl, so we allow to modify csr only if pm.current==1 */ - return !pm_current; -} - -static RISCVException read_mmte(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mmte & MMTE_MASK; - return RISCV_EXCP_NONE; -} - -static RISCVException write_mmte(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - target_ulong wpri_val = val & MMTE_MASK; - - if (val != wpri_val) { - qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" - TARGET_FMT_lx "\n", "MMTE: WPRI violation written 0x", - val, "vs expected 0x", wpri_val); - } - /* for machine mode pm.current is hardwired to 1 */ - wpri_val |= MMTE_M_PM_CURRENT; - - /* hardwiring pm.instruction bit to 0, since it's not supported yet */ - wpri_val &= ~(MMTE_M_PM_INSN | MMTE_S_PM_INSN | MMTE_U_PM_INSN); - env->mmte = wpri_val | EXT_STATUS_DIRTY; - riscv_cpu_update_mask(env); - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_smte(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mmte & SMTE_MASK; - return RISCV_EXCP_NONE; -} - -static RISCVException write_smte(CPURISCVState *env, int csrno, - target_ulong val) -{ - target_ulong wpri_val = val & SMTE_MASK; - - if (val != wpri_val) { - qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" - TARGET_FMT_lx "\n", "SMTE: WPRI violation written 0x", - val, "vs expected 0x", wpri_val); - } - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - - wpri_val |= (env->mmte & ~SMTE_MASK); - write_mmte(env, csrno, wpri_val); - return RISCV_EXCP_NONE; -} - -static RISCVException read_umte(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mmte & UMTE_MASK; - return RISCV_EXCP_NONE; -} - -static RISCVException write_umte(CPURISCVState *env, int csrno, - target_ulong val) -{ - target_ulong wpri_val = val & UMTE_MASK; - - if (val != wpri_val) { - qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" - TARGET_FMT_lx "\n", "UMTE: WPRI violation written 0x", - val, "vs expected 0x", wpri_val); - } - - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - - wpri_val |= (env->mmte & ~UMTE_MASK); - write_mmte(env, csrno, wpri_val); - return RISCV_EXCP_NONE; -} - -static RISCVException read_mpmmask(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mpmmask; - return RISCV_EXCP_NONE; -} - -static RISCVException write_mpmmask(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - - env->mpmmask = val; - if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { - env->cur_pmmask = val; - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_spmmask(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->spmmask; - return RISCV_EXCP_NONE; -} - -static RISCVException write_spmmask(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->spmmask = val; - if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) { - env->cur_pmmask = val; - if (cpu_get_xl(env, PRV_S) == MXL_RV32) { - env->cur_pmmask &= UINT32_MAX; - } - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_upmmask(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->upmmask; - return RISCV_EXCP_NONE; -} - -static RISCVException write_upmmask(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->upmmask = val; - if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) { - env->cur_pmmask = val; - if (cpu_get_xl(env, PRV_U) == MXL_RV32) { - env->cur_pmmask &= UINT32_MAX; - } - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_mpmbase(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mpmbase; - return RISCV_EXCP_NONE; -} - -static RISCVException write_mpmbase(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - - env->mpmbase = val; - if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { - env->cur_pmbase = val; - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_spmbase(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->spmbase; - return RISCV_EXCP_NONE; -} - -static RISCVException write_spmbase(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->spmbase = val; - if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) { - env->cur_pmbase = val; - if (cpu_get_xl(env, PRV_S) == MXL_RV32) { - env->cur_pmbase &= UINT32_MAX; - } - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_upmbase(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->upmbase; - return RISCV_EXCP_NONE; -} - -static RISCVException write_upmbase(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->upmbase = val; - if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) { - env->cur_pmbase = val; - if (cpu_get_xl(env, PRV_U) == MXL_RV32) { - env->cur_pmbase &= UINT32_MAX; - } - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - #endif /* Crypto Extension */ @@ -5323,25 +5016,6 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_TINFO] = { "tinfo", debug, read_tinfo, write_ignore }, [CSR_MCONTEXT] = { "mcontext", debug, read_mcontext, write_mcontext }, - /* User Pointer Masking */ - [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, - [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, - write_upmmask }, - [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, - write_upmbase }, - /* Machine Pointer Masking */ - [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, - [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, - write_mpmmask }, - [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, - write_mpmbase }, - /* Supervisor Pointer Masking */ - [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, - [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, - write_spmmask }, - [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, - write_spmbase }, - /* Performance Counters */ [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter }, [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index b2e1f2503c..d81621010d 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -152,25 +152,15 @@ static const VMStateDescription vmstate_vector = { static bool pointermasking_needed(void *opaque) { - RISCVCPU *cpu = opaque; - CPURISCVState *env = &cpu->env; - - return riscv_has_ext(env, RVJ); + return false; } static const VMStateDescription vmstate_pointermasking = { .name = "cpu/pointer_masking", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = pointermasking_needed, .fields = (const VMStateField[]) { - VMSTATE_UINTTL(env.mmte, RISCVCPU), - VMSTATE_UINTTL(env.mpmmask, RISCVCPU), - VMSTATE_UINTTL(env.mpmbase, RISCVCPU), - VMSTATE_UINTTL(env.spmmask, RISCVCPU), - VMSTATE_UINTTL(env.spmbase, RISCVCPU), - VMSTATE_UINTTL(env.upmmask, RISCVCPU), - VMSTATE_UINTTL(env.upmbase, RISCVCPU), VMSTATE_END_OF_LIST() } @@ -266,7 +256,6 @@ static int riscv_cpu_post_load(void *opaque, int version_id) CPURISCVState *env = &cpu->env; env->xl = cpu_recompute_xl(env); - riscv_cpu_update_mask(env); return 0; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index e03b409248..7f7283d52a 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1115,7 +1115,6 @@ static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = { MISA_CFG(RVS, true), MISA_CFG(RVU, true), MISA_CFG(RVH, true), - MISA_CFG(RVJ, false), MISA_CFG(RVV, false), MISA_CFG(RVG, false), MISA_CFG(RVB, false), @@ -1402,8 +1401,8 @@ static void riscv_init_max_cpu_extensions(Object *obj) CPURISCVState *env = &cpu->env; const RISCVCPUMultiExtConfig *prop; - /* Enable RVG, RVJ and RVV that are disabled by default */ - riscv_cpu_set_misa_ext(env, env->misa_ext | RVB | RVG | RVJ | RVV); + /* Enable RVG and RVV that are disabled by default */ + riscv_cpu_set_misa_ext(env, env->misa_ext | RVB | RVG | RVV); for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { isa_ext_update_enabled(cpu, prop->offset, true); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index f46d76c785..7406a43b9f 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -42,9 +42,6 @@ static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ static TCGv load_res; static TCGv load_val; -/* globals for PM CSRs */ -static TCGv pm_mask; -static TCGv pm_base; /* * If an operation is being performed on less than TARGET_LONG_BITS, @@ -106,9 +103,6 @@ typedef struct DisasContext { bool vl_eq_vlmax; CPUState *cs; TCGv zero; - /* PointerMasking extension */ - bool pm_mask_enabled; - bool pm_base_enabled; /* Ztso */ bool ztso; /* Use icount trigger for native debug */ @@ -592,14 +586,9 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm) TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); tcg_gen_addi_tl(addr, src1, imm); - if (ctx->pm_mask_enabled) { - tcg_gen_andc_tl(addr, addr, pm_mask); - } else if (get_address_xl(ctx) == MXL_RV32) { + if (get_address_xl(ctx) == MXL_RV32) { tcg_gen_ext32u_tl(addr, addr); } - if (ctx->pm_base_enabled) { - tcg_gen_or_tl(addr, addr, pm_base); - } return addr; } @@ -611,14 +600,10 @@ static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); tcg_gen_add_tl(addr, src1, offs); - if (ctx->pm_mask_enabled) { - tcg_gen_andc_tl(addr, addr, pm_mask); - } else if (get_xl(ctx) == MXL_RV32) { + if (get_xl(ctx) == MXL_RV32) { tcg_gen_ext32u_tl(addr, addr); } - if (ctx->pm_base_enabled) { - tcg_gen_or_tl(addr, addr, pm_base); - } + return addr; } @@ -1246,8 +1231,6 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); ctx->cs = cs; - ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED); - ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED); ctx->ztso = cpu->cfg.ext_ztso; ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); ctx->bcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, BCFI_ENABLED); @@ -1386,9 +1369,4 @@ void riscv_translate_init(void) "load_res"); load_val = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_val), "load_val"); - /* Assign PM CSRs to tcg globals */ - pm_mask = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, cur_pmmask), - "pmmask"); - pm_base = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, cur_pmbase), - "pmbase"); } diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index ead3ec5194..cf5dd7f2e1 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -107,7 +107,7 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) { - return (addr & ~env->cur_pmmask) | env->cur_pmbase; + return addr; } /* From 33ca99a111b4cb6af7f6f907ad685b04ff05892a Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:41 +0300 Subject: [PATCH 1190/2892] target/riscv: Add new CSR fields for S{sn, mn, m}pm extensions as part of Zjpm v1.0 Signed-off-by: Alexey Baturo Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106102346.1100149-3-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 8 ++++++++ target/riscv/cpu_bits.h | 4 ++++ target/riscv/cpu_cfg.h | 3 +++ target/riscv/csr.c | 33 +++++++++++++++++++++++++++++++-- target/riscv/pmp.c | 14 +++++++++++--- target/riscv/pmp.h | 1 + 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c78e97af50..ad33e96ddf 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -128,6 +128,14 @@ typedef enum { EXT_STATUS_DIRTY, } RISCVExtStatus; +/* Enum holds PMM field values for Zjpm v1.0 extension */ +typedef enum { + PMM_FIELD_DISABLED = 0, + PMM_FIELD_RESERVED = 1, + PMM_FIELD_PMLEN7 = 2, + PMM_FIELD_PMLEN16 = 3, +} RISCVPmPmm; + typedef struct riscv_cpu_implied_exts_rule { #ifndef CONFIG_USER_ONLY /* diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index c5b3de6469..797dd6985b 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -575,6 +575,7 @@ typedef enum { #define HSTATUS_VTSR 0x00400000 #define HSTATUS_HUKTE 0x01000000 #define HSTATUS_VSXL 0x300000000 +#define HSTATUS_HUPMM 0x3000000000000 #define HSTATUS32_WPRI 0xFF8FF87E #define HSTATUS64_WPRI 0xFFFFFFFFFF8FF87EULL @@ -735,6 +736,7 @@ typedef enum RISCVException { #define MENVCFG_CBIE (3UL << 4) #define MENVCFG_CBCFE BIT(6) #define MENVCFG_CBZE BIT(7) +#define MENVCFG_PMM (3ULL << 32) #define MENVCFG_ADUE (1ULL << 61) #define MENVCFG_PBMTE (1ULL << 62) #define MENVCFG_STCE (1ULL << 63) @@ -751,6 +753,7 @@ typedef enum RISCVException { #define SENVCFG_CBCFE MENVCFG_CBCFE #define SENVCFG_CBZE MENVCFG_CBZE #define SENVCFG_UKTE BIT(8) +#define SENVCFG_PMM MENVCFG_PMM #define HENVCFG_FIOM MENVCFG_FIOM #define HENVCFG_LPE MENVCFG_LPE @@ -758,6 +761,7 @@ typedef enum RISCVException { #define HENVCFG_CBIE MENVCFG_CBIE #define HENVCFG_CBCFE MENVCFG_CBCFE #define HENVCFG_CBZE MENVCFG_CBZE +#define HENVCFG_PMM MENVCFG_PMM #define HENVCFG_ADUE MENVCFG_ADUE #define HENVCFG_PBMTE MENVCFG_PBMTE #define HENVCFG_STCE MENVCFG_STCE diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index fe0c4173d2..a36d3fada3 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -129,6 +129,9 @@ struct RISCVCPUConfig { bool ext_ssaia; bool ext_sscofpmf; bool ext_smepmp; + bool ext_ssnpm; + bool ext_smnpm; + bool ext_smmpm; bool rvv_ta_all_1s; bool rvv_ma_all_1s; bool rvv_vl_half_avl; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 48abcab487..6b8cef52fe 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -575,6 +575,9 @@ static RISCVException have_mseccfg(CPURISCVState *env, int csrno) if (riscv_cpu_cfg(env)->ext_zkr) { return RISCV_EXCP_NONE; } + if (riscv_cpu_cfg(env)->ext_smmpm) { + return RISCV_EXCP_NONE; + } return RISCV_EXCP_ILLEGAL_INST; } @@ -2379,6 +2382,12 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, if (env_archcpu(env)->cfg.ext_zicfiss) { mask |= MENVCFG_SSE; } + + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (env_archcpu(env)->cfg.ext_smnpm && + get_field(val, MENVCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= MENVCFG_PMM; + } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); @@ -2425,6 +2434,12 @@ static RISCVException write_senvcfg(CPURISCVState *env, int csrno, { uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; RISCVException ret; + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (env_archcpu(env)->cfg.ext_ssnpm && + riscv_cpu_mxl(env) == MXL_RV64 && + get_field(val, SENVCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= SENVCFG_PMM; + } ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); if (ret != RISCV_EXCP_NONE) { @@ -2493,6 +2508,12 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, get_field(env->menvcfg, MENVCFG_SSE)) { mask |= HENVCFG_SSE; } + + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (env_archcpu(env)->cfg.ext_ssnpm && + get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= HENVCFG_PMM; + } } env->henvcfg = (env->henvcfg & ~mask) | (val & mask); @@ -3529,10 +3550,18 @@ static RISCVException read_hstatus(CPURISCVState *env, int csrno, static RISCVException write_hstatus(CPURISCVState *env, int csrno, target_ulong val) { + uint64_t mask = (target_ulong)-1; if (!env_archcpu(env)->cfg.ext_svukte) { - val = val & (~HSTATUS_HUKTE); + mask &= ~HSTATUS_HUKTE; } - env->hstatus = val; + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (!env_archcpu(env)->cfg.ext_ssnpm || + riscv_cpu_mxl(env) != MXL_RV64 || + get_field(val, HSTATUS_HUPMM) == PMM_FIELD_RESERVED) { + mask &= ~HSTATUS_HUPMM; + } + env->hstatus = (env->hstatus & ~mask) | (val & mask); + if (riscv_cpu_mxl(env) != MXL_RV32 && get_field(val, HSTATUS_VSXL) != 2) { qemu_log_mask(LOG_UNIMP, "QEMU does not support mixed HSXLEN options."); diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index a1b36664fc..a185c246d6 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -575,6 +575,13 @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) void mseccfg_csr_write(CPURISCVState *env, target_ulong val) { int i; + uint64_t mask = MSECCFG_MMWP | MSECCFG_MML; + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (riscv_cpu_cfg(env)->ext_smmpm && + riscv_cpu_mxl(env) == MXL_RV64 && + get_field(val, MSECCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= MSECCFG_PMM; + } trace_mseccfg_csr_write(env->mhartid, val); @@ -590,12 +597,13 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) if (riscv_cpu_cfg(env)->ext_smepmp) { /* Sticky bits */ - val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); - if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) { + val |= (env->mseccfg & mask); + if ((val ^ env->mseccfg) & mask) { tlb_flush(env_cpu(env)); } } else { - val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB); + mask |= MSECCFG_RLB; + val &= ~(mask); } /* M-mode forward cfi to be enabled if cfi extension is implemented */ diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index e0530a17a3..271cf24169 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -46,6 +46,7 @@ typedef enum { MSECCFG_USEED = 1 << 8, MSECCFG_SSEED = 1 << 9, MSECCFG_MLPE = 1 << 10, + MSECCFG_PMM = 3ULL << 32, } mseccfg_field_t; typedef struct { From 3d1c5c08855de3a17fa91777260f4d1867f39bdf Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:42 +0300 Subject: [PATCH 1191/2892] target/riscv: Add helper functions to calculate current number of masked bits for pointer masking Signed-off-by: Alexey Baturo Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106102346.1100149-4-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 5 +++ target/riscv/cpu_helper.c | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ad33e96ddf..5c85e8b28d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -772,8 +772,13 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, bool riscv_cpu_is_32bit(RISCVCPU *cpu); +bool riscv_cpu_virt_mem_enabled(CPURISCVState *env); +RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env); +uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm); + RISCVException riscv_csrr(CPURISCVState *env, int csrno, target_ulong *ret_value); + RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8c1969294f..0e030d4ecb 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -214,6 +214,84 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, *pflags = flags; } +RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env) +{ +#ifndef CONFIG_USER_ONLY + int priv_mode = cpu_address_mode(env); + + if (get_field(env->mstatus, MSTATUS_MPRV) && + get_field(env->mstatus, MSTATUS_MXR)) { + return PMM_FIELD_DISABLED; + } + + /* Get current PMM field */ + switch (priv_mode) { + case PRV_M: + if (riscv_cpu_cfg(env)->ext_smmpm) { + return get_field(env->mseccfg, MSECCFG_PMM); + } + break; + case PRV_S: + if (riscv_cpu_cfg(env)->ext_smnpm) { + if (get_field(env->mstatus, MSTATUS_MPV)) { + return get_field(env->henvcfg, HENVCFG_PMM); + } else { + return get_field(env->menvcfg, MENVCFG_PMM); + } + } + break; + case PRV_U: + if (riscv_has_ext(env, RVS)) { + if (riscv_cpu_cfg(env)->ext_ssnpm) { + return get_field(env->senvcfg, SENVCFG_PMM); + } + } else { + if (riscv_cpu_cfg(env)->ext_smnpm) { + return get_field(env->menvcfg, MENVCFG_PMM); + } + } + break; + default: + g_assert_not_reached(); + } + return PMM_FIELD_DISABLED; +#else + return PMM_FIELD_DISABLED; +#endif +} + +bool riscv_cpu_virt_mem_enabled(CPURISCVState *env) +{ +#ifndef CONFIG_USER_ONLY + int satp_mode = 0; + int priv_mode = cpu_address_mode(env); + + if (riscv_cpu_mxl(env) == MXL_RV32) { + satp_mode = get_field(env->satp, SATP32_MODE); + } else { + satp_mode = get_field(env->satp, SATP64_MODE); + } + + return ((satp_mode != VM_1_10_MBARE) && (priv_mode != PRV_M)); +#else + return false; +#endif +} + +uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm) +{ + switch (pmm) { + case PMM_FIELD_DISABLED: + return 0; + case PMM_FIELD_PMLEN7: + return 7; + case PMM_FIELD_PMLEN16: + return 16; + default: + g_assert_not_reached(); + } +} + #ifndef CONFIG_USER_ONLY /* From 6ec718e352c9694e2caed5977c847a5e9bbbe11e Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:43 +0300 Subject: [PATCH 1192/2892] target/riscv: Add pointer masking tb flags Signed-off-by: Alexey Baturo Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-ID: <20250106102346.1100149-5-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 3 +++ target/riscv/cpu_helper.c | 3 +++ target/riscv/translate.c | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5c85e8b28d..f22e43c662 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -635,6 +635,9 @@ FIELD(TB_FLAGS, FCFI_ENABLED, 26, 1) FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1) /* zicfiss needs a TB flag so that correct TB is located based on tb flags */ FIELD(TB_FLAGS, BCFI_ENABLED, 28, 1) +/* If pointer masking should be applied and address sign extended */ +FIELD(TB_FLAGS, PM_PMM, 29, 2) +FIELD(TB_FLAGS, PM_SIGNEXTEND, 31, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 0e030d4ecb..8728541b99 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -126,6 +126,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, RISCVCPU *cpu = env_archcpu(env); RISCVExtStatus fs, vs; uint32_t flags = 0; + bool pm_signext = riscv_cpu_virt_mem_enabled(env); *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; *cs_base = 0; @@ -210,6 +211,8 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); + flags = FIELD_DP32(flags, TB_FLAGS, PM_PMM, riscv_pm_get_pmm(env)); + flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext); *pflags = flags; } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 7406a43b9f..26350b2826 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -103,6 +103,9 @@ typedef struct DisasContext { bool vl_eq_vlmax; CPUState *cs; TCGv zero; + /* actual address width */ + uint8_t addr_xl; + bool addr_signed; /* Ztso */ bool ztso; /* Use icount trigger for native debug */ @@ -1231,6 +1234,8 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); ctx->cs = cs; + ctx->addr_xl = 0; + ctx->addr_signed = false; ctx->ztso = cpu->cfg.ext_ztso; ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); ctx->bcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, BCFI_ENABLED); From 4d501a7a7fb4a9a4ac17d031225bafe7f4f75583 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:44 +0300 Subject: [PATCH 1193/2892] target/riscv: Update address modify functions to take into account pointer masking Signed-off-by: Alexey Baturo Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-ID: <20250106102346.1100149-6-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 22 ++++++++++++++++------ target/riscv/vector_helper.c | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 26350b2826..698b74f7a8 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -589,8 +589,10 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm) TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); tcg_gen_addi_tl(addr, src1, imm); - if (get_address_xl(ctx) == MXL_RV32) { - tcg_gen_ext32u_tl(addr, addr); + if (ctx->addr_signed) { + tcg_gen_sextract_tl(addr, addr, 0, ctx->addr_xl); + } else { + tcg_gen_extract_tl(addr, addr, 0, ctx->addr_xl); } return addr; @@ -603,8 +605,10 @@ static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); tcg_gen_add_tl(addr, src1, offs); - if (get_xl(ctx) == MXL_RV32) { - tcg_gen_ext32u_tl(addr, addr); + if (ctx->addr_signed) { + tcg_gen_sextract_tl(addr, addr, 0, ctx->addr_xl); + } else { + tcg_gen_extract_tl(addr, addr, 0, ctx->addr_xl); } return addr; @@ -1234,8 +1238,14 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); ctx->cs = cs; - ctx->addr_xl = 0; - ctx->addr_signed = false; + if (get_xl(ctx) == MXL_RV32) { + ctx->addr_xl = 32; + ctx->addr_signed = false; + } else { + int pm_pmm = FIELD_EX32(tb_flags, TB_FLAGS, PM_PMM); + ctx->addr_xl = 64 - riscv_pm_get_pmlen(pm_pmm); + ctx->addr_signed = FIELD_EX32(tb_flags, TB_FLAGS, PM_SIGNEXTEND); + } ctx->ztso = cpu->cfg.ext_ztso; ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); ctx->bcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, BCFI_ENABLED); diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index cf5dd7f2e1..0eea124b66 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -107,6 +107,22 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) { + if (riscv_cpu_mxl(env) == MXL_RV32) { + return addr; + } + RISCVPmPmm pmm = riscv_pm_get_pmm(env); + if (pmm == PMM_FIELD_DISABLED) { + return addr; + } + int pmlen = riscv_pm_get_pmlen(pmm); + bool signext = riscv_cpu_virt_mem_enabled(env); + addr = addr << pmlen; + /* sign/zero extend masked address by N-1 bit */ + if (signext) { + addr = (target_long)addr >> pmlen; + } else { + addr = addr >> pmlen; + } return addr; } From 4d1600934a6c0fb617ffd4852fd54dfdd3eda35b Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:45 +0300 Subject: [PATCH 1194/2892] target/riscv: Apply pointer masking for virtualized memory accesses Signed-off-by: Alexey Baturo Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106102346.1100149-7-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 19 +++++++++++++ target/riscv/internals.h | 54 ++++++++++++++++++++++++++++++++++++ target/riscv/op_helper.c | 16 +++++------ target/riscv/vector_helper.c | 21 -------------- 5 files changed, 82 insertions(+), 29 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f22e43c662..5e7152200f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -777,6 +777,7 @@ bool riscv_cpu_is_32bit(RISCVCPU *cpu); bool riscv_cpu_virt_mem_enabled(CPURISCVState *env); RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env); +RISCVPmPmm riscv_pm_get_virt_pmm(CPURISCVState *env); uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm); RISCVException riscv_csrr(CPURISCVState *env, int csrno, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8728541b99..2e307e4ea5 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -263,6 +263,25 @@ RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env) #endif } +RISCVPmPmm riscv_pm_get_virt_pmm(CPURISCVState *env) +{ +#ifndef CONFIG_USER_ONLY + int priv_mode = cpu_address_mode(env); + + if (priv_mode == PRV_U) { + return get_field(env->hstatus, HSTATUS_HUPMM); + } else { + if (get_field(env->hstatus, HSTATUS_SPVP)) { + return get_field(env->henvcfg, HENVCFG_PMM); + } else { + return get_field(env->senvcfg, SENVCFG_PMM); + } + } +#else + return PMM_FIELD_DISABLED; +#endif +} + bool riscv_cpu_virt_mem_enabled(CPURISCVState *env) { #ifndef CONFIG_USER_ONLY diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 76934eaa7b..67291933f8 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -145,4 +145,58 @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) /* Our implementation of CPUClass::has_work */ bool riscv_cpu_has_work(CPUState *cs); +/* Zjpm addr masking routine */ +static inline target_ulong adjust_addr_body(CPURISCVState *env, + target_ulong addr, + bool is_virt_addr) +{ + RISCVPmPmm pmm = PMM_FIELD_DISABLED; + uint32_t pmlen = 0; + bool signext = false; + + /* do nothing for rv32 mode */ + if (riscv_cpu_mxl(env) == MXL_RV32) { + return addr; + } + + /* get pmm field depending on whether addr is */ + if (is_virt_addr) { + pmm = riscv_pm_get_virt_pmm(env); + } else { + pmm = riscv_pm_get_pmm(env); + } + + /* if pointer masking is disabled, return original addr */ + if (pmm == PMM_FIELD_DISABLED) { + return addr; + } + + if (!is_virt_addr) { + signext = riscv_cpu_virt_mem_enabled(env); + } + addr = addr << pmlen; + pmlen = riscv_pm_get_pmlen(pmm); + + /* sign/zero extend masked address by N-1 bit */ + if (signext) { + addr = (target_long)addr >> pmlen; + } else { + addr = addr >> pmlen; + } + + return addr; +} + +static inline target_ulong adjust_addr(CPURISCVState *env, + target_ulong addr) +{ + return adjust_addr_body(env, addr, false); +} + +static inline target_ulong adjust_addr_virt(CPURISCVState *env, + target_ulong addr) +{ + return adjust_addr_body(env, addr, true); +} + #endif diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 29de8eb43d..952ef8b3ec 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -479,7 +479,7 @@ target_ulong helper_hyp_hlv_bu(CPURISCVState *env, target_ulong addr) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - return cpu_ldb_mmu(env, addr, oi, ra); + return cpu_ldb_mmu(env, adjust_addr_virt(env, addr), oi, ra); } target_ulong helper_hyp_hlv_hu(CPURISCVState *env, target_ulong addr) @@ -488,7 +488,7 @@ target_ulong helper_hyp_hlv_hu(CPURISCVState *env, target_ulong addr) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_TEUW, mmu_idx); - return cpu_ldw_mmu(env, addr, oi, ra); + return cpu_ldw_mmu(env, adjust_addr_virt(env, addr), oi, ra); } target_ulong helper_hyp_hlv_wu(CPURISCVState *env, target_ulong addr) @@ -497,7 +497,7 @@ target_ulong helper_hyp_hlv_wu(CPURISCVState *env, target_ulong addr) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_TEUL, mmu_idx); - return cpu_ldl_mmu(env, addr, oi, ra); + return cpu_ldl_mmu(env, adjust_addr_virt(env, addr), oi, ra); } target_ulong helper_hyp_hlv_d(CPURISCVState *env, target_ulong addr) @@ -506,7 +506,7 @@ target_ulong helper_hyp_hlv_d(CPURISCVState *env, target_ulong addr) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_TEUQ, mmu_idx); - return cpu_ldq_mmu(env, addr, oi, ra); + return cpu_ldq_mmu(env, adjust_addr_virt(env, addr), oi, ra); } void helper_hyp_hsv_b(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -515,7 +515,7 @@ void helper_hyp_hsv_b(CPURISCVState *env, target_ulong addr, target_ulong val) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - cpu_stb_mmu(env, addr, val, oi, ra); + cpu_stb_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } void helper_hyp_hsv_h(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -524,7 +524,7 @@ void helper_hyp_hsv_h(CPURISCVState *env, target_ulong addr, target_ulong val) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_TEUW, mmu_idx); - cpu_stw_mmu(env, addr, val, oi, ra); + cpu_stw_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } void helper_hyp_hsv_w(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -533,7 +533,7 @@ void helper_hyp_hsv_w(CPURISCVState *env, target_ulong addr, target_ulong val) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_TEUL, mmu_idx); - cpu_stl_mmu(env, addr, val, oi, ra); + cpu_stl_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } void helper_hyp_hsv_d(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -542,7 +542,7 @@ void helper_hyp_hsv_d(CPURISCVState *env, target_ulong addr, target_ulong val) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_TEUQ, mmu_idx); - cpu_stq_mmu(env, addr, val, oi, ra); + cpu_stq_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } /* diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 0eea124b66..5386e3b97c 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -105,27 +105,6 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) return scale < 0 ? vlenb >> -scale : vlenb << scale; } -static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) -{ - if (riscv_cpu_mxl(env) == MXL_RV32) { - return addr; - } - RISCVPmPmm pmm = riscv_pm_get_pmm(env); - if (pmm == PMM_FIELD_DISABLED) { - return addr; - } - int pmlen = riscv_pm_get_pmlen(pmm); - bool signext = riscv_cpu_virt_mem_enabled(env); - addr = addr << pmlen; - /* sign/zero extend masked address by N-1 bit */ - if (signext) { - addr = (target_long)addr >> pmlen; - } else { - addr = addr >> pmlen; - } - return addr; -} - /* * This function checks watchpoint before real load operation. * From e00e2749ce6194e2757a850e13dc4c337bf3e3d0 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 6 Jan 2025 13:23:46 +0300 Subject: [PATCH 1195/2892] target/riscv: Enable updates for pointer masking variables and thus enable pointer masking extension Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Message-ID: <20250106102346.1100149-8-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 99588e219e..d9eb2c04c3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -193,11 +193,14 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), + ISA_EXT_DATA_ENTRY(smmpm, PRIV_VERSION_1_13_0, ext_smmpm), + ISA_EXT_DATA_ENTRY(smnpm, PRIV_VERSION_1_13_0, ext_smnpm), ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm), ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), @@ -1595,9 +1598,12 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zvfh", ext_zvfh, false), MULTI_EXT_CFG_BOOL("zvfhmin", ext_zvfhmin, false), MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true), + MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false), MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), + MULTI_EXT_CFG_BOOL("smmpm", ext_smmpm, false), + MULTI_EXT_CFG_BOOL("smnpm", ext_smnpm, false), MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), MULTI_EXT_CFG_BOOL("ssaia", ext_ssaia, false), MULTI_EXT_CFG_BOOL("svade", ext_svade, false), From 36de64b74cc56e95b082e2e26522a22ec75a7c8d Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Mon, 6 Jan 2025 13:43:31 +0800 Subject: [PATCH 1196/2892] target/riscv: Add 'ext_smrnmi' in the RISCVCPUConfig The boolean variable 'ext_smrnmi' is used to determine whether the Smrnmi extension exists. Signed-off-by: Frank Chang Signed-off-by: Tommy Wu Reviewed-by: Alistair Francis Message-ID: <20250106054336.1878291-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index a36d3fada3..ee7c908710 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -129,6 +129,7 @@ struct RISCVCPUConfig { bool ext_ssaia; bool ext_sscofpmf; bool ext_smepmp; + bool ext_smrnmi; bool ext_ssnpm; bool ext_smnpm; bool ext_smmpm; From 5db557f82bff480437275d4cc9e0b5463bc04484 Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Mon, 6 Jan 2025 13:43:32 +0800 Subject: [PATCH 1197/2892] target/riscv: Add Smrnmi CSRs The Smrnmi extension adds the 'mnscratch', 'mnepc', 'mncause', 'mnstatus' CSRs. Signed-off-by: Frank Chang Signed-off-by: Tommy Wu Reviewed-by: Alistair Francis Message-ID: <20250106054336.1878291-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 5 +++ target/riscv/cpu.h | 7 ++++ target/riscv/cpu_bits.h | 11 ++++++ target/riscv/csr.c | 82 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d9eb2c04c3..66193cd2f6 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1127,6 +1127,11 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) riscv_trigger_reset_hold(env); } + if (cpu->cfg.ext_smrnmi) { + env->rnmip = 0; + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false); + } + if (kvm_enabled()) { kvm_riscv_reset_vcpu(cpu); } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5e7152200f..5eaf9da1f7 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -480,6 +480,13 @@ struct CPUArchState { uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; #endif /* CONFIG_KVM */ + + /* RNMI */ + target_ulong mnscratch; + target_ulong mnepc; + target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */ + target_ulong mnstatus; + target_ulong rnmip; }; /* diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 797dd6985b..ba6fc546c4 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -353,6 +353,12 @@ #define CSR_PMPADDR14 0x3be #define CSR_PMPADDR15 0x3bf +/* RNMI */ +#define CSR_MNSCRATCH 0x740 +#define CSR_MNEPC 0x741 +#define CSR_MNCAUSE 0x742 +#define CSR_MNSTATUS 0x744 + /* Debug/Trace Registers (shared with Debug Mode) */ #define CSR_TSELECT 0x7a0 #define CSR_TDATA1 0x7a1 @@ -604,6 +610,11 @@ typedef enum { #define SATP64_ASID 0x0FFFF00000000000ULL #define SATP64_PPN 0x00000FFFFFFFFFFFULL +/* RNMI mnstatus CSR mask */ +#define MNSTATUS_NMIE 0x00000008 +#define MNSTATUS_MNPV 0x00000080 +#define MNSTATUS_MNPP 0x00001800 + /* VM modes (satp.mode) privileged ISA 1.10 */ #define VM_1_10_MBARE 0 #define VM_1_10_SV32 1 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 6b8cef52fe..af9766759a 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -590,6 +590,17 @@ static RISCVException debug(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } + +static RISCVException rnmi(CPURISCVState *env, int csrno) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (cpu->cfg.ext_smrnmi) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} #endif static RISCVException seed(CPURISCVState *env, int csrno) @@ -4376,6 +4387,67 @@ static RISCVException write_mcontext(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_mnscratch(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mnscratch; + return RISCV_EXCP_NONE; +} + +static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mnscratch = val; + return RISCV_EXCP_NONE; +} + +static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mnepc; + return RISCV_EXCP_NONE; +} + +static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mnepc = val; + return RISCV_EXCP_NONE; +} + +static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mncause; + return RISCV_EXCP_NONE; +} + +static int write_mncause(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mncause = val; + return RISCV_EXCP_NONE; +} + +static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mnstatus; + return RISCV_EXCP_NONE; +} + +static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong mask = (MNSTATUS_NMIE | MNSTATUS_MNPP); + + if (riscv_has_ext(env, RVH)) { + /* Flush tlb on mnstatus fields that affect VM. */ + if ((val ^ env->mnstatus) & MNSTATUS_MNPV) { + tlb_flush(env_cpu(env)); + } + + mask |= MNSTATUS_MNPV; + } + + /* mnstatus.mnie can only be cleared by hardware. */ + env->mnstatus = (env->mnstatus & MNSTATUS_NMIE) | (val & mask); + return RISCV_EXCP_NONE; +} + #endif /* Crypto Extension */ @@ -4883,6 +4955,16 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_sstateen_1_3, .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* RNMI */ + [CSR_MNSCRATCH] = { "mnscratch", rnmi, read_mnscratch, write_mnscratch, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MNEPC] = { "mnepc", rnmi, read_mnepc, write_mnepc, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MNCAUSE] = { "mncause", rnmi, read_mncause, write_mncause, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MNSTATUS] = { "mnstatus", rnmi, read_mnstatus, write_mnstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor Trap Setup */ [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, read_sstatus_i128 }, From c1149f69ab711bf6ccdc1da492f5be47f1ebf67e Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Mon, 6 Jan 2025 13:43:33 +0800 Subject: [PATCH 1198/2892] target/riscv: Handle Smrnmi interrupt and exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the RNMI interrupt trap handler address is implementation defined. We add the 'rnmi-interrupt-vector' and 'rnmi-exception-vector' as the property of the harts. It’s very easy for users to set the address based on their expectation. This patch also adds the functionality to handle the RNMI signals. Signed-off-by: Frank Chang Signed-off-by: Tommy Wu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106054336.1878291-4-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv_hart.c | 41 ++++++++++++++++ include/hw/riscv/riscv_hart.h | 4 ++ target/riscv/cpu.c | 11 +++++ target/riscv/cpu.h | 3 ++ target/riscv/cpu_bits.h | 12 +++++ target/riscv/cpu_helper.c | 88 ++++++++++++++++++++++++++++++++--- 6 files changed, 152 insertions(+), 7 deletions(-) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index bc9ffdd2d4..62b7c44350 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -26,6 +26,7 @@ #include "target/riscv/cpu.h" #include "hw/qdev-properties.h" #include "hw/riscv/riscv_hart.h" +#include "qemu/error-report.h" static const Property riscv_harts_props[] = { DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1), @@ -33,6 +34,23 @@ static const Property riscv_harts_props[] = { DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec, DEFAULT_RSTVEC), + + /* + * Smrnmi implementation-defined interrupt and exception trap handlers. + * + * When an RNMI interrupt is detected, the hart then enters M-mode and + * jumps to the address defined by "rnmi-interrupt-vector". + * + * When the hart encounters an exception while executing in M-mode with + * the mnstatus.NMIE bit clear, the hart then jumps to the address + * defined by "rnmi-exception-vector". + */ + DEFINE_PROP_ARRAY("rnmi-interrupt-vector", RISCVHartArrayState, + num_rnmi_irqvec, rnmi_irqvec, qdev_prop_uint64, + uint64_t), + DEFINE_PROP_ARRAY("rnmi-exception-vector", RISCVHartArrayState, + num_rnmi_excpvec, rnmi_excpvec, qdev_prop_uint64, + uint64_t), }; static void riscv_harts_cpu_reset(void *opaque) @@ -46,6 +64,29 @@ static bool riscv_hart_realize(RISCVHartArrayState *s, int idx, { object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type); qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec); + + if (s->harts[idx].cfg.ext_smrnmi) { + if (idx < s->num_rnmi_irqvec) { + qdev_prop_set_uint64(DEVICE(&s->harts[idx]), + "rnmi-interrupt-vector", s->rnmi_irqvec[idx]); + } + + if (idx < s->num_rnmi_excpvec) { + qdev_prop_set_uint64(DEVICE(&s->harts[idx]), + "rnmi-exception-vector", s->rnmi_excpvec[idx]); + } + } else { + if (s->num_rnmi_irqvec > 0) { + warn_report_once("rnmi-interrupt-vector property is ignored " + "because Smrnmi extension is not enabled."); + } + + if (s->num_rnmi_excpvec > 0) { + warn_report_once("rnmi-exception-vector property is ignored " + "because Smrnmi extension is not enabled."); + } + } + s->harts[idx].env.mhartid = s->hartid_base + idx; qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]); return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp); diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index 912b4a2682..a6ed73a195 100644 --- a/include/hw/riscv/riscv_hart.h +++ b/include/hw/riscv/riscv_hart.h @@ -38,6 +38,10 @@ struct RISCVHartArrayState { uint32_t hartid_base; char *cpu_type; uint64_t resetvec; + uint32_t num_rnmi_irqvec; + uint64_t *rnmi_irqvec; + uint32_t num_rnmi_excpvec; + uint64_t *rnmi_excpvec; RISCVCPU *harts; }; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 66193cd2f6..eb06d06628 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1412,6 +1412,11 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) g_assert_not_reached(); } } + +static void riscv_cpu_set_nmi(void *opaque, int irq, int level) +{ + riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level); +} #endif /* CONFIG_USER_ONLY */ static bool riscv_cpu_is_dynamic(Object *cpu_obj) @@ -1435,6 +1440,8 @@ static void riscv_cpu_init(Object *obj) #ifndef CONFIG_USER_ONLY qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq, IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); + qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi, + "riscv.cpu.rnmi", RNMI_MAX); #endif /* CONFIG_USER_ONLY */ general_user_opts = g_hash_table_new(g_str_hash, g_str_equal); @@ -2793,6 +2800,10 @@ static const Property riscv_cpu_properties[] = { #ifndef CONFIG_USER_ONLY DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC), + DEFINE_PROP_UINT64("rnmi-interrupt-vector", RISCVCPU, env.rnmi_irqvec, + DEFAULT_RNMI_IRQVEC), + DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec, + DEFAULT_RNMI_EXCPVEC), #endif DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5eaf9da1f7..08215efb09 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -487,6 +487,8 @@ struct CPUArchState { target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */ target_ulong mnstatus; target_ulong rnmip; + uint64_t rnmi_irqvec; + uint64_t rnmi_excpvec; }; /* @@ -585,6 +587,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value); +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level); void riscv_cpu_interrupt(CPURISCVState *env); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index ba6fc546c4..32525f00d6 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -650,6 +650,12 @@ typedef enum { /* Default Reset Vector address */ #define DEFAULT_RSTVEC 0x1000 +/* Default RNMI Interrupt Vector address */ +#define DEFAULT_RNMI_IRQVEC 0x0 + +/* Default RNMI Exception Vector address */ +#define DEFAULT_RNMI_EXCPVEC 0x0 + /* Exception causes */ typedef enum RISCVException { RISCV_EXCP_NONE = -1, /* sentinel value */ @@ -704,6 +710,9 @@ typedef enum RISCVException { /* -1 is due to bit zero of hgeip and hgeie being ROZ. */ #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) +/* RNMI causes */ +#define RNMI_MAX 16 + /* mip masks */ #define MIP_USIP (1 << IRQ_U_SOFT) #define MIP_SSIP (1 << IRQ_S_SOFT) @@ -889,6 +898,9 @@ typedef enum RISCVException { #define MHPMEVENT_IDX_MASK 0xFFFFF #define MHPMEVENT_SSCOF_RESVD 16 +/* RISC-V-specific interrupt pending bits. */ +#define CPU_INTERRUPT_RNMI CPU_INTERRUPT_TGT_EXT_0 + /* JVT CSR bits */ #define JVT_MODE 0x3F #define JVT_BASE (~0x3F) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 2e307e4ea5..4c70db6def 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -554,6 +554,18 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) uint64_t vsbits, irq_delegated; int virq; + /* Priority: RNMI > Other interrupt. */ + if (riscv_cpu_cfg(env)->ext_smrnmi) { + /* If mnstatus.NMIE == 0, all interrupts are disabled. */ + if (!get_field(env->mnstatus, MNSTATUS_NMIE)) { + return RISCV_EXCP_NONE; + } + + if (env->rnmip) { + return ctz64(env->rnmip); /* since non-zero */ + } + } + /* Determine interrupt enable state of all privilege modes */ if (env->virt_enabled) { mie = 1; @@ -616,7 +628,9 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - if (interrupt_request & CPU_INTERRUPT_HARD) { + uint32_t mask = CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI; + + if (interrupt_request & mask) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; int interruptno = riscv_cpu_local_irq_pending(env); @@ -748,6 +762,30 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) env->geilen = geilen; } +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level) +{ + CPURISCVState *env = &cpu->env; + CPUState *cs = CPU(cpu); + bool release_lock = false; + + if (!bql_locked()) { + release_lock = true; + bql_lock(); + } + + if (level) { + env->rnmip |= 1 << irq; + cpu_interrupt(cs, CPU_INTERRUPT_RNMI); + } else { + env->rnmip &= ~(1 << irq); + cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI); + } + + if (release_lock) { + bql_unlock(); + } +} + int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) { CPURISCVState *env = &cpu->env; @@ -1897,6 +1935,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) bool write_gva = false; bool always_storeamo = (env->excp_uw2 & RISCV_UW2_ALWAYS_STORE_AMO); uint64_t s; + int mode; /* * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide @@ -1914,7 +1953,24 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong htval = 0; target_ulong mtval2 = 0; int sxlen = 0; - int mxlen = 0; + int mxlen = 16 << riscv_cpu_mxl(env); + bool nnmi_excep = false; + + if (cpu->cfg.ext_smrnmi && env->rnmip && async) { + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV, + env->virt_enabled); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP, + env->priv); + env->mncause = cause | ((target_ulong)1U << (mxlen - 1)); + env->mnepc = env->pc; + env->pc = env->rnmi_irqvec; + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_mode(env, PRV_M, false); + + return; + } if (!async) { /* set tval to badaddr for traps with address information */ @@ -2008,8 +2064,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) __func__, env->mhartid, async, cause, env->pc, tval, riscv_cpu_get_trap_name(cause, async)); - if (env->priv <= PRV_S && cause < 64 && - (((deleg >> cause) & 1) || s_injected || vs_injected)) { + mode = env->priv <= PRV_S && cause < 64 && + (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M; + + if (mode == PRV_S) { /* handle the trap in S-mode */ /* save elp status */ if (cpu_get_fcfien(env)) { @@ -2064,6 +2122,14 @@ void riscv_cpu_do_interrupt(CPUState *cs) ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S, virt); } else { + /* + * If the hart encounters an exception while executing in M-mode + * with the mnstatus.NMIE bit clear, the exception is an RNMI exception. + */ + nnmi_excep = cpu->cfg.ext_smrnmi && + !get_field(env->mnstatus, MNSTATUS_NMIE) && + !async; + /* handle the trap in M-mode */ /* save elp status */ if (cpu_get_fcfien(env)) { @@ -2091,14 +2157,22 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); env->mstatus = s; - mxlen = 16 << riscv_cpu_mxl(env); env->mcause = cause | ((target_ulong)async << (mxlen - 1)); env->mepc = env->pc; env->mtval = tval; env->mtval2 = mtval2; env->mtinst = tinst; - env->pc = (env->mtvec >> 2 << 2) + - ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); + + /* + * For RNMI exception, program counter is set to the RNMI exception + * trap handler address. + */ + if (nnmi_excep) { + env->pc = env->rnmi_excpvec; + } else { + env->pc = (env->mtvec >> 2 << 2) + + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); + } riscv_cpu_set_mode(env, PRV_M, virt); } From 3157a553ec6b9a52ad0aa6b52cca27d3a964167e Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Mon, 6 Jan 2025 13:43:34 +0800 Subject: [PATCH 1199/2892] target/riscv: Add Smrnmi mnret instruction This patch adds a new instruction 'mnret'. 'mnret' is an M-mode-only instruction that uses the values in `mnepc` and `mnstatus` to return to the program counter, privilege mode, and virtualization mode of the interrupted context. Signed-off-by: Frank Chang Signed-off-by: Tommy Wu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106054336.1878291-5-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/helper.h | 1 + target/riscv/insn32.decode | 3 ++ .../riscv/insn_trans/trans_privileged.c.inc | 20 +++++++++ target/riscv/op_helper.c | 45 ++++++++++++++++--- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 451261ce5a..16ea240d26 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) +DEF_HELPER_1(mnret, tl, env) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wrs_nto, void, env) DEF_HELPER_1(tlb_flush, void, env) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index e9139ec1b9..942c434c6e 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -121,6 +121,9 @@ wfi 0001000 00101 00000 000 00000 1110011 sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm +# *** NMI *** +mnret 0111000 00010 00000 000 00000 1110011 + # *** RV32I Base Instruction Set *** lui .................... ..... 0110111 @u { diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index ecd3b8b2c9..73f940d406 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -18,6 +18,12 @@ * this program. If not, see . */ +#define REQUIRE_SMRNMI(ctx) do { \ + if (!ctx->cfg_ptr->ext_smrnmi) { \ + return false; \ + } \ +} while (0) + static bool trans_ecall(DisasContext *ctx, arg_ecall *a) { /* always generates U-level ECALL, fixed in do_interrupt handler */ @@ -106,6 +112,20 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) #endif } +static bool trans_mnret(DisasContext *ctx, arg_mnret *a) +{ +#ifndef CONFIG_USER_ONLY + REQUIRE_SMRNMI(ctx); + decode_save_opc(ctx, 0); + gen_helper_mnret(cpu_pc, tcg_env); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; + return true; +#else + return false; +#endif +} + static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 952ef8b3ec..bb022d89e2 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -328,24 +328,30 @@ target_ulong helper_sret(CPURISCVState *env) return retpc; } -target_ulong helper_mret(CPURISCVState *env) +static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, + target_ulong prev_priv) { if (!(env->priv >= PRV_M)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - target_ulong retpc = env->mepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } - uint64_t mstatus = env->mstatus; - target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); - if (riscv_cpu_cfg(env)->pmp && !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); } +} + +target_ulong helper_mret(CPURISCVState *env) +{ + target_ulong retpc = env->mepc; + uint64_t mstatus = env->mstatus; + target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + + check_ret_from_m_mode(env, retpc, prev_priv); target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && (prev_priv != PRV_M); @@ -377,6 +383,35 @@ target_ulong helper_mret(CPURISCVState *env) return retpc; } +target_ulong helper_mnret(CPURISCVState *env) +{ + target_ulong retpc = env->mnepc; + target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP); + target_ulong prev_virt; + + check_ret_from_m_mode(env, retpc, prev_priv); + + prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) && + (prev_priv != PRV_M); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true); + + /* + * If MNRET changes the privilege mode to a mode + * less privileged than M, it also sets mstatus.MPRV to 0. + */ + if (prev_priv < PRV_M) { + env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false); + } + + if (riscv_has_ext(env, RVH) && prev_virt) { + riscv_cpu_swap_hypervisor_regs(env); + } + + riscv_cpu_set_mode(env, prev_priv, prev_virt); + + return retpc; +} + void helper_wfi(CPURISCVState *env) { CPUState *cs = env_cpu(env); From f9653d4eb2ccaf6fe140e38fb1027a9e829d4062 Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Mon, 6 Jan 2025 13:43:35 +0800 Subject: [PATCH 1200/2892] target/riscv: Add Smrnmi cpu extension This adds the properties for ISA extension Smrnmi. Also, when Smrnmi is present, the firmware (e.g., OpenSBI) must set mnstatus.NMIE to 1 before enabling any interrupts. Otherwise, all interrupts will be disabled. Since our current OpenSBI does not support Smrnmi yet, let's disable Smrnmi for the 'max' type CPU for now. We can re-enable it once OpenSBI includes proper support for it. Signed-off-by: Frank Chang Signed-off-by: Tommy Wu Signed-off-by: Daniel Henrique Barboza Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106054336.1878291-6-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/tcg/tcg-cpu.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index eb06d06628..dace670e5e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -193,6 +193,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), + ISA_EXT_DATA_ENTRY(smrnmi, PRIV_VERSION_1_12_0, ext_smrnmi), ISA_EXT_DATA_ENTRY(smmpm, PRIV_VERSION_1_13_0, ext_smmpm), ISA_EXT_DATA_ENTRY(smnpm, PRIV_VERSION_1_13_0, ext_smnpm), ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), @@ -1614,6 +1615,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), + MULTI_EXT_CFG_BOOL("smrnmi", ext_smrnmi, false), MULTI_EXT_CFG_BOOL("smmpm", ext_smmpm, false), MULTI_EXT_CFG_BOOL("smnpm", ext_smnpm, false), MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 7f7283d52a..f94aa9f29e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1430,6 +1430,15 @@ static void riscv_init_max_cpu_extensions(Object *obj) if (env->misa_mxl != MXL_RV32) { isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false); } + + /* + * ext_smrnmi requires OpenSBI changes that our current + * image does not have. Disable it for now. + */ + if (cpu->cfg.ext_smrnmi) { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smrnmi), false); + qemu_log("Smrnmi is disabled in the 'max' type CPU\n"); + } } static bool riscv_cpu_has_max_extensions(Object *cpu_obj) From 0266fd8b56a4de8180cda9b2064ed2e58d17b3d9 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 6 Jan 2025 13:43:36 +0800 Subject: [PATCH 1201/2892] target/riscv: Add Zicfilp support for Smrnmi Zicfilp extension introduces the MNPELP (bit 9) in mnstatus. The MNPELP field holds the previous ELP. When a RNMI trap is delivered, the MNPELP is set to ELP and ELP set to NO_LP_EXPECTED. Upon a mnret, if the mnstatus.MNPP holds the value y, then ELP is set to the value of MNPELP if yLPE is 1; otherwise, it is set to NO_LP_EXPECTED. Signed-off-by: Frank Chang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250106054336.1878291-7-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 1 + target/riscv/cpu_helper.c | 11 ++++++++++- target/riscv/op_helper.c | 9 +++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 32525f00d6..d51f3d8cef 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -613,6 +613,7 @@ typedef enum { /* RNMI mnstatus CSR mask */ #define MNSTATUS_NMIE 0x00000008 #define MNSTATUS_MNPV 0x00000080 +#define MNSTATUS_MNPELP 0x00000200 #define MNSTATUS_MNPP 0x00001800 /* VM modes (satp.mode) privileged ISA 1.10 */ diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 4c70db6def..3318ce440d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1966,6 +1966,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mnepc = env->pc; env->pc = env->rnmi_irqvec; + if (cpu_get_fcfien(env)) { + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, env->elp); + } + /* Trapping to M mode, virt is disabled */ riscv_cpu_set_mode(env, PRV_M, false); @@ -2133,7 +2137,12 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* handle the trap in M-mode */ /* save elp status */ if (cpu_get_fcfien(env)) { - env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp); + if (nnmi_excep) { + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, + env->elp); + } else { + env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp); + } } if (riscv_has_ext(env, RVH)) { diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index bb022d89e2..c825336519 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -409,6 +409,15 @@ target_ulong helper_mnret(CPURISCVState *env) riscv_cpu_set_mode(env, prev_priv, prev_virt); + /* + * If forward cfi enabled for new priv, restore elp status + * and clear mnpelp in mnstatus + */ + if (cpu_get_fcfien(env)) { + env->elp = get_field(env->mnstatus, MNSTATUS_MNPELP); + } + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, 0); + return retpc; } From 7703a1d1e6479084d58ee3106a3c8a72ed7357eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jan 2025 00:13:43 +0100 Subject: [PATCH 1202/2892] target/riscv: Have kvm_riscv_get_timebase_frequency() take RISCVCPU cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep kvm_riscv_get_timebase_frequency() prototype aligned with the other ones declared in "kvm_riscv.h", have it take a RISCVCPU cpu as argument. Include "target/riscv/cpu-qom.h" which declares the RISCVCPU typedef. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20250112231344.34632-2-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- target/riscv/kvm/kvm-cpu.c | 4 ++-- target/riscv/kvm/kvm_riscv.h | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2bc5a9dd98..9e8876be29 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -750,7 +750,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_add_subnode(ms->fdt, "/cpus"); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "timebase-frequency", kvm_enabled() ? - kvm_riscv_get_timebase_frequency(first_cpu) : + kvm_riscv_get_timebase_frequency(RISCV_CPU(first_cpu)) : RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 11278ea778..23ce779359 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -758,11 +758,11 @@ static void kvm_riscv_put_regs_timer(CPUState *cs) env->kvm_timer_dirty = false; } -uint64_t kvm_riscv_get_timebase_frequency(CPUState *cs) +uint64_t kvm_riscv_get_timebase_frequency(RISCVCPU *cpu) { uint64_t reg; - KVM_RISCV_GET_TIMER(cs, frequency, reg); + KVM_RISCV_GET_TIMER(CPU(cpu), frequency, reg); return reg; } diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h index 5851898868..b2bcd1041f 100644 --- a/target/riscv/kvm/kvm_riscv.h +++ b/target/riscv/kvm/kvm_riscv.h @@ -19,6 +19,8 @@ #ifndef QEMU_KVM_RISCV_H #define QEMU_KVM_RISCV_H +#include "target/riscv/cpu-qom.h" + void kvm_riscv_reset_vcpu(RISCVCPU *cpu); void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level); void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, @@ -28,6 +30,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, void riscv_kvm_aplic_request(void *opaque, int irq, int level); int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state); void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp); -uint64_t kvm_riscv_get_timebase_frequency(CPUState *cs); +uint64_t kvm_riscv_get_timebase_frequency(RISCVCPU *cpu); #endif From cb938a0a24bc911894b6fec1429e2e0cc8b2f948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jan 2025 00:13:44 +0100 Subject: [PATCH 1203/2892] hw/riscv/virt: Remove unnecessary use of &first_cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virt_machine_init() creates the HARTs vCPUs, then later virt_machine_done() calls create_fdt_sockets(), so the latter has access to the first vCPU via: RISCVVirtState { RISCVHartArrayState { RISCVCPU *harts; ... } soc[VIRT_SOCKETS_MAX]; ... } s; Directly use that instead of the &first_cpu global. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20250112231344.34632-3-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 9e8876be29..241389d72f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -750,7 +750,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_add_subnode(ms->fdt, "/cpus"); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "timebase-frequency", kvm_enabled() ? - kvm_riscv_get_timebase_frequency(RISCV_CPU(first_cpu)) : + kvm_riscv_get_timebase_frequency(&s->soc->harts[0]) : RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); From 51c4f3e982daa1d2fffa63e0b73565c948d26d2b Mon Sep 17 00:00:00 2001 From: Kaiwen Xue Date: Fri, 10 Jan 2025 00:21:29 -0800 Subject: [PATCH 1204/2892] target/riscv: Add properties for Indirect CSR Access extension This adds the properties for sxcsrind. Definitions of new registers and implementations will come with future patches. Signed-off-by: Kaiwen Xue Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-1-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index dace670e5e..4f5772ae5b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -192,6 +192,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), + ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_13_0, ext_smcsrind), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), ISA_EXT_DATA_ENTRY(smrnmi, PRIV_VERSION_1_12_0, ext_smrnmi), ISA_EXT_DATA_ENTRY(smmpm, PRIV_VERSION_1_13_0, ext_smmpm), @@ -201,6 +202,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind), ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm), ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index ee7c908710..4fe2144ec7 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -79,6 +79,8 @@ struct RISCVCPUConfig { bool ext_smstateen; bool ext_sstc; bool ext_smcntrpmf; + bool ext_smcsrind; + bool ext_sscsrind; bool ext_svadu; bool ext_svinval; bool ext_svnapot; From dc0280723dfc64d90e94155985853691d5ab9276 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue Date: Fri, 10 Jan 2025 00:21:30 -0800 Subject: [PATCH 1205/2892] target/riscv: Decouple AIA processing from xiselect and xireg Since xiselect and xireg also will be of use in sxcsrind, AIA should have its own separated interface when those CSRs are accessed. Signed-off-by: Kaiwen Xue Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-2-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 165 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 26 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index af9766759a..123e9fd2bd 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -29,6 +29,7 @@ #include "system/cpu-timers.h" #include "qemu/guest-random.h" #include "qapi/error.h" +#include /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) @@ -305,6 +306,15 @@ static RISCVException aia_any32(CPURISCVState *env, int csrno) return any32(env, csrno); } +static RISCVException csrind_or_aia_any(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smaia && !riscv_cpu_cfg(env)->ext_smcsrind) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return any(env, csrno); +} + static RISCVException smode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVS)) { @@ -341,6 +351,30 @@ static RISCVException aia_smode32(CPURISCVState *env, int csrno) return smode32(env, csrno); } +static bool csrind_extensions_present(CPURISCVState *env) +{ + return riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind; +} + +static bool aia_extensions_present(CPURISCVState *env) +{ + return riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_ssaia; +} + +static bool csrind_or_aia_extensions_present(CPURISCVState *env) +{ + return csrind_extensions_present(env) || aia_extensions_present(env); +} + +static RISCVException csrind_or_aia_smode(CPURISCVState *env, int csrno) +{ + if (!csrind_or_aia_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + static RISCVException hmode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVH)) { @@ -360,6 +394,15 @@ static RISCVException hmode32(CPURISCVState *env, int csrno) } +static RISCVException csrind_or_aia_hmode(CPURISCVState *env, int csrno) +{ + if (!csrind_or_aia_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode(env, csrno); +} + static RISCVException umode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVU)) { @@ -1969,6 +2012,22 @@ static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) }; } +static int csrind_xlate_vs_csrno(CPURISCVState *env, int csrno) +{ + if (!env->virt_enabled) { + return csrno; + } + + switch (csrno) { + case CSR_SISELECT: + return CSR_VSISELECT; + case CSR_SIREG: + return CSR_VSIREG; + default: + return csrno; + }; +} + static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) @@ -1976,7 +2035,7 @@ static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *iselect; /* Translate CSR number for VS-mode */ - csrno = aia_xlate_vs_csrno(env, csrno); + csrno = csrind_xlate_vs_csrno(env, csrno); /* Find the iselect CSR based on CSR number */ switch (csrno) { @@ -2005,6 +2064,12 @@ static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static bool xiselect_aia_range(target_ulong isel) +{ + return (ISELECT_IPRIO0 <= isel && isel <= ISELECT_IPRIO15) || + (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST); +} + static int rmw_iprio(target_ulong xlen, target_ulong iselect, uint8_t *iprio, target_ulong *val, target_ulong new_val, @@ -2050,45 +2115,44 @@ static int rmw_iprio(target_ulong xlen, return 0; } -static RISCVException rmw_xireg(CPURISCVState *env, int csrno, - target_ulong *val, target_ulong new_val, - target_ulong wr_mask) +static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) { - bool virt, isel_reserved; - uint8_t *iprio; + bool virt = false, isel_reserved = false; int ret = -EINVAL; - target_ulong priv, isel, vgein; + uint8_t *iprio; + 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; - isel_reserved = false; + /* VS-mode CSR number passed in has already been translated */ switch (csrno) { case CSR_MIREG: + if (!riscv_cpu_cfg(env)->ext_smaia) { + goto done; + } iprio = env->miprio; - isel = env->miselect; priv = PRV_M; break; case CSR_SIREG: - if (env->priv == PRV_S && env->mvien & MIP_SEIP && + if (!riscv_cpu_cfg(env)->ext_ssaia || + (env->priv == PRV_S && env->mvien & MIP_SEIP && env->siselect >= ISELECT_IMSIC_EIDELIVERY && - env->siselect <= ISELECT_IMSIC_EIE63) { + env->siselect <= ISELECT_IMSIC_EIE63)) { goto done; } iprio = env->siprio; - isel = env->siselect; priv = PRV_S; break; case CSR_VSIREG: + if (!riscv_cpu_cfg(env)->ext_ssaia) { + goto done; + } iprio = env->hviprio; - isel = env->vsiselect; priv = PRV_S; virt = true; break; default: - goto done; + goto done; }; /* Find the selected guest interrupt file */ @@ -2119,10 +2183,54 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, } done: + /* + * If AIA is not enabled, illegal instruction exception is always + * returned regardless of whether we are in VS-mode or not + */ if (ret) { return (env->virt_enabled && virt && !isel_reserved) ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_xireg(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + bool virt = false; + int ret = -EINVAL; + target_ulong isel; + + /* Translate CSR number for VS-mode */ + csrno = csrind_xlate_vs_csrno(env, csrno); + + /* Decode register details from CSR number */ + switch (csrno) { + case CSR_MIREG: + isel = env->miselect; + break; + case CSR_SIREG: + isel = env->siselect; + break; + case CSR_VSIREG: + isel = env->vsiselect; + virt = true; + break; + default: + goto done; + }; + + if (xiselect_aia_range(isel)) { + return rmw_xireg_aia(env, csrno, isel, val, new_val, wr_mask); + } + +done: + if (ret) { + return (env->virt_enabled && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } return RISCV_EXCP_NONE; } @@ -4866,8 +4974,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [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 }, + [CSR_MISELECT] = { "miselect", csrind_or_aia_any, NULL, NULL, + rmw_xiselect }, + [CSR_MIREG] = { "mireg", csrind_or_aia_any, NULL, NULL, + rmw_xireg }, /* Machine-Level Interrupts (AIA) */ [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, @@ -4995,8 +5105,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SATP] = { "satp", satp, 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 }, + [CSR_SISELECT] = { "siselect", csrind_or_aia_smode, NULL, NULL, + rmw_xiselect }, + [CSR_SIREG] = { "sireg", csrind_or_aia_smode, NULL, NULL, + rmw_xireg }, /* Supervisor-Level Interrupts (AIA) */ [CSR_STOPEI] = { "stopei", aia_smode, NULL, NULL, rmw_xtopei }, @@ -5075,9 +5187,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* * 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 }, + [CSR_VSISELECT] = { "vsiselect", csrind_or_aia_hmode, NULL, NULL, + rmw_xiselect }, + [CSR_VSIREG] = { "vsireg", csrind_or_aia_hmode, NULL, NULL, + rmw_xireg }, /* VS-Level Interrupts (H-extension with AIA) */ [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, From dbcb6e1ccf3f25292a8700bb18997a4411fad82f Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 10 Jan 2025 00:21:31 -0800 Subject: [PATCH 1206/2892] target/riscv: Enable S*stateen bits for AIA As per the ratified AIA spec v1.0, three stateen bits control AIA CSR access. Bit 60 controls the indirect CSRs Bit 59 controls the most AIA CSR state Bit 58 controls the IMSIC state such as stopei and vstopei Enable the corresponding bits in [m|h]stateen and enable corresponding checks in the CSR accessor functions. Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-3-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 85 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 123e9fd2bd..7f4348fe86 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -335,19 +335,42 @@ static RISCVException smode32(CPURISCVState *env, int csrno) static RISCVException aia_smode(CPURISCVState *env, int csrno) { + int ret; + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } + if (csrno == CSR_STOPEI) { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_IMSIC); + } else { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + } + + if (ret != RISCV_EXCP_NONE) { + return ret; + } + return smode(env, csrno); } static RISCVException aia_smode32(CPURISCVState *env, int csrno) { + int ret; + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + if (ret != RISCV_EXCP_NONE) { + return ret; + } + return smode32(env, csrno); } @@ -576,15 +599,38 @@ static RISCVException hgatp(CPURISCVState *env, int csrno) static RISCVException aia_hmode(CPURISCVState *env, int csrno) { + int ret; + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } - return hmode(env, csrno); + if (csrno == CSR_VSTOPEI) { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_IMSIC); + } else { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + } + + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + return hmode(env, csrno); } static RISCVException aia_hmode32(CPURISCVState *env, int csrno) { + int ret; + + if (!riscv_cpu_cfg(env)->ext_ssaia) { + return RISCV_EXCP_ILLEGAL_INST; + } + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -2033,6 +2079,12 @@ static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, target_ulong wr_mask) { target_ulong *iselect; + int ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } /* Translate CSR number for VS-mode */ csrno = csrind_xlate_vs_csrno(env, csrno); @@ -2203,6 +2255,11 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, int ret = -EINVAL; target_ulong isel; + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + /* Translate CSR number for VS-mode */ csrno = csrind_xlate_vs_csrno(env, csrno); @@ -2703,6 +2760,19 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_P1P13; } + if (riscv_cpu_cfg(env)->ext_smaia) { + wr_mask |= SMSTATEEN0_SVSLCT; + } + + /* + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is + * implemented. However, that information is with MachineState and we can't + * figure that out in csr.c. Just enable if Smaia is available. + */ + if (riscv_cpu_cfg(env)->ext_smaia) { + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC); + } + return write_mstateen(env, csrno, wr_mask, new_val); } @@ -2783,6 +2853,19 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_FCSR; } + if (riscv_cpu_cfg(env)->ext_ssaia) { + wr_mask |= SMSTATEEN0_SVSLCT; + } + + /* + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is + * implemented. However, that information is with MachineState and we can't + * figure that out in csr.c. Just enable if Ssaia is available. + */ + if (riscv_cpu_cfg(env)->ext_ssaia) { + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC); + } + return write_hstateen(env, csrno, wr_mask, new_val); } From 5e33a20827150345350bede07e26a1bae320e682 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue Date: Fri, 10 Jan 2025 00:21:32 -0800 Subject: [PATCH 1207/2892] target/riscv: Support generic CSR indirect access This adds the indirect access registers required by sscsrind/smcsrind and the operations on them. Note that xiselect and xireg are used for both AIA and sxcsrind, and the behavior of accessing them depends on whether each extension is enabled and the value stored in xiselect. Co-developed-by: Atish Patra Signed-off-by: Kaiwen Xue Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-4-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 28 +++++++- target/riscv/csr.c | 144 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 166 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index d51f3d8cef..6b1446fb7e 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -173,6 +173,13 @@ #define CSR_MISELECT 0x350 #define CSR_MIREG 0x351 +/* Machine Indirect Register Alias */ +#define CSR_MIREG2 0x352 +#define CSR_MIREG3 0x353 +#define CSR_MIREG4 0x355 +#define CSR_MIREG5 0x356 +#define CSR_MIREG6 0x357 + /* Machine-Level Interrupts (AIA) */ #define CSR_MTOPEI 0x35c #define CSR_MTOPI 0xfb0 @@ -222,6 +229,13 @@ #define CSR_SISELECT 0x150 #define CSR_SIREG 0x151 +/* Supervisor Indirect Register Alias */ +#define CSR_SIREG2 0x152 +#define CSR_SIREG3 0x153 +#define CSR_SIREG4 0x155 +#define CSR_SIREG5 0x156 +#define CSR_SIREG6 0x157 + /* Supervisor-Level Interrupts (AIA) */ #define CSR_STOPEI 0x15c #define CSR_STOPI 0xdb0 @@ -288,6 +302,13 @@ #define CSR_VSISELECT 0x250 #define CSR_VSIREG 0x251 +/* Virtual Supervisor Indirect Alias */ +#define CSR_VSIREG2 0x252 +#define CSR_VSIREG3 0x253 +#define CSR_VSIREG4 0x255 +#define CSR_VSIREG5 0x256 +#define CSR_VSIREG6 0x257 + /* VS-Level Interrupts (H-extension with AIA) */ #define CSR_VSTOPEI 0x25c #define CSR_VSTOPI 0xeb0 @@ -803,10 +824,13 @@ typedef enum RISCVException { #define ISELECT_IMSIC_EIE63 0xff #define ISELECT_IMSIC_FIRST ISELECT_IMSIC_EIDELIVERY #define ISELECT_IMSIC_LAST ISELECT_IMSIC_EIE63 -#define ISELECT_MASK 0x1ff +#define ISELECT_MASK_AIA 0x1ff + +/* MISELECT, SISELECT, and VSISELECT bits (AIA) */ +#define ISELECT_MASK_SXCSRIND 0xfff /* Dummy [M|S|VS]ISELECT value for emulating [M|S|VS]TOPEI CSRs */ -#define ISELECT_IMSIC_TOPEI (ISELECT_MASK + 1) +#define ISELECT_IMSIC_TOPEI (ISELECT_MASK_AIA + 1) /* IMSIC bits (AIA) */ #define IMSIC_TOPEI_IID_SHIFT 16 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 7f4348fe86..49648ddc95 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -306,6 +306,15 @@ static RISCVException aia_any32(CPURISCVState *env, int csrno) return any32(env, csrno); } +static RISCVException csrind_any(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smcsrind) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + static RISCVException csrind_or_aia_any(CPURISCVState *env, int csrno) { if (!riscv_cpu_cfg(env)->ext_smaia && !riscv_cpu_cfg(env)->ext_smcsrind) { @@ -389,6 +398,15 @@ static bool csrind_or_aia_extensions_present(CPURISCVState *env) return csrind_extensions_present(env) || aia_extensions_present(env); } +static RISCVException csrind_smode(CPURISCVState *env, int csrno) +{ + if (!csrind_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + static RISCVException csrind_or_aia_smode(CPURISCVState *env, int csrno) { if (!csrind_or_aia_extensions_present(env)) { @@ -417,6 +435,15 @@ static RISCVException hmode32(CPURISCVState *env, int csrno) } +static RISCVException csrind_hmode(CPURISCVState *env, int csrno) +{ + if (!csrind_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode(env, csrno); +} + static RISCVException csrind_or_aia_hmode(CPURISCVState *env, int csrno) { if (!csrind_or_aia_extensions_present(env)) { @@ -2068,7 +2095,12 @@ static int csrind_xlate_vs_csrno(CPURISCVState *env, int csrno) case CSR_SISELECT: return CSR_VSISELECT; case CSR_SIREG: - return CSR_VSIREG; + case CSR_SIREG2: + case CSR_SIREG3: + case CSR_SIREG4: + case CSR_SIREG5: + case CSR_SIREG6: + return CSR_VSIREG + (csrno - CSR_SIREG); default: return csrno; }; @@ -2108,7 +2140,12 @@ static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, *val = *iselect; } - wr_mask &= ISELECT_MASK; + if (riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind) { + wr_mask &= ISELECT_MASK_SXCSRIND; + } else { + wr_mask &= ISELECT_MASK_AIA; + } + if (wr_mask) { *iselect = (*iselect & ~wr_mask) | (new_val & wr_mask); } @@ -2247,6 +2284,56 @@ done: return RISCV_EXCP_NONE; } +/* + * rmw_xireg_csrind: Perform indirect access to xireg and xireg2-xireg6 + * + * Perform indirect access to xireg and xireg2-xireg6. + * This is a generic interface for all xireg CSRs. Apart from AIA, all other + * extension using csrind should be implemented here. + */ +static int rmw_xireg_csrind(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + return -EINVAL; +} + +static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + bool virt = false; + int ret = -EINVAL; + target_ulong isel; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Translate CSR number for VS-mode */ + csrno = csrind_xlate_vs_csrno(env, csrno); + + if (CSR_MIREG <= csrno && csrno <= CSR_MIREG6 && + csrno != CSR_MIREG4 - 1) { + isel = env->miselect; + } else if (CSR_SIREG <= csrno && csrno <= CSR_SIREG6 && + csrno != CSR_SIREG4 - 1) { + isel = env->siselect; + } else if (CSR_VSIREG <= csrno && csrno <= CSR_VSIREG6 && + csrno != CSR_VSIREG4 - 1) { + isel = env->vsiselect; + virt = true; + } else { + goto done; + } + + return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); + +done: + return (env->virt_enabled && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; +} + static RISCVException rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) @@ -2279,8 +2366,21 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, goto done; }; + /* + * Use the xiselect range to determine actual op on xireg. + * + * Since we only checked the existence of AIA or Indirect Access in the + * predicate, we should check the existence of the exact extension when + * we get to a specific range and return illegal instruction exception even + * in VS-mode. + */ if (xiselect_aia_range(isel)) { return rmw_xireg_aia(env, csrno, isel, val, new_val, wr_mask); + } else if (riscv_cpu_cfg(env)->ext_smcsrind || + riscv_cpu_cfg(env)->ext_sscsrind) { + return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); + } else { + return RISCV_EXCP_ILLEGAL_INST; } done: @@ -2760,7 +2860,7 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_P1P13; } - if (riscv_cpu_cfg(env)->ext_smaia) { + if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) { wr_mask |= SMSTATEEN0_SVSLCT; } @@ -2853,7 +2953,7 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_FCSR; } - if (riscv_cpu_cfg(env)->ext_ssaia) { + if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) { wr_mask |= SMSTATEEN0_SVSLCT; } @@ -5062,6 +5162,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIREG] = { "mireg", csrind_or_aia_any, NULL, NULL, rmw_xireg }, + /* Machine Indirect Register Alias */ + [CSR_MIREG2] = { "mireg2", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG3] = { "mireg3", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG4] = { "mireg4", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG5] = { "mireg5", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG6] = { "mireg6", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Machine-Level Interrupts (AIA) */ [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, @@ -5193,6 +5305,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SIREG] = { "sireg", csrind_or_aia_smode, NULL, NULL, rmw_xireg }, + /* Supervisor Indirect Register Alias */ + [CSR_SIREG2] = { "sireg2", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG3] = { "sireg3", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG4] = { "sireg4", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG5] = { "sireg5", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG6] = { "sireg6", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor-Level Interrupts (AIA) */ [CSR_STOPEI] = { "stopei", aia_smode, NULL, NULL, rmw_xtopei }, [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, @@ -5275,6 +5399,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIREG] = { "vsireg", csrind_or_aia_hmode, NULL, NULL, rmw_xireg }, + /* Virtual Supervisor Indirect Alias */ + [CSR_VSIREG2] = { "vsireg2", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG3] = { "vsireg3", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG4] = { "vsireg4", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG5] = { "vsireg5", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG6] = { "vsireg6", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* VS-Level Interrupts (H-extension with AIA) */ [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, From f2548886b3dff228b82e91808553616c4b8d14a8 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 10 Jan 2025 00:21:33 -0800 Subject: [PATCH 1208/2892] target/riscv: Add properties for counter delegation ISA extensions This adds the properties for counter delegation ISA extensions (Smcdeleg/Ssccfg). Definitions of new registers and and implementation will come in the next set of patches. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-5-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4f5772ae5b..da40f68715 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -191,6 +191,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), + ISA_EXT_DATA_ENTRY(smcdeleg, PRIV_VERSION_1_13_0, ext_smcdeleg), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_13_0, ext_smcsrind), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), @@ -199,6 +200,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(smnpm, PRIV_VERSION_1_13_0, ext_smnpm), ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), + ISA_EXT_DATA_ENTRY(ssccfg, PRIV_VERSION_1_13_0, ext_ssccfg), ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 4fe2144ec7..561f5119b6 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -78,6 +78,8 @@ struct RISCVCPUConfig { bool ext_ztso; bool ext_smstateen; bool ext_sstc; + bool ext_smcdeleg; + bool ext_ssccfg; bool ext_smcntrpmf; bool ext_smcsrind; bool ext_sscsrind; From e84af935607e3df409cbf0854bc4f4a1b828ce76 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue Date: Fri, 10 Jan 2025 00:21:34 -0800 Subject: [PATCH 1209/2892] target/riscv: Add counter delegation definitions This adds definitions for counter delegation, including the new scountinhibit register and the mstateen.CD bit. Signed-off-by: Kaiwen Xue Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-6-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 1 + target/riscv/cpu_bits.h | 8 +++++++- target/riscv/machine.c | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 08215efb09..a936300103 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -392,6 +392,7 @@ struct CPUArchState { uint32_t scounteren; uint32_t mcounteren; + uint32_t scountinhibit; uint32_t mcountinhibit; /* PMU cycle & instret privilege mode filtering */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 6b1446fb7e..73f7d37d80 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -210,6 +210,9 @@ #define CSR_SSTATEEN2 0x10E #define CSR_SSTATEEN3 0x10F +/* Supervisor Counter Delegation */ +#define CSR_SCOUNTINHIBIT 0x120 + /* Supervisor Trap Handling */ #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 @@ -779,6 +782,7 @@ typedef enum RISCVException { #define MENVCFG_CBCFE BIT(6) #define MENVCFG_CBZE BIT(7) #define MENVCFG_PMM (3ULL << 32) +#define MENVCFG_CDE (1ULL << 60) #define MENVCFG_ADUE (1ULL << 61) #define MENVCFG_PBMTE (1ULL << 62) #define MENVCFG_STCE (1ULL << 63) @@ -826,7 +830,9 @@ typedef enum RISCVException { #define ISELECT_IMSIC_LAST ISELECT_IMSIC_EIE63 #define ISELECT_MASK_AIA 0x1ff -/* MISELECT, SISELECT, and VSISELECT bits (AIA) */ +/* [M|S|VS]SELCT value for Indirect CSR Access Extension */ +#define ISELECT_CD_FIRST 0x40 +#define ISELECT_CD_LAST 0x5f #define ISELECT_MASK_SXCSRIND 0xfff /* Dummy [M|S|VS]ISELECT value for emulating [M|S|VS]TOPEI CSRs */ diff --git a/target/riscv/machine.c b/target/riscv/machine.c index d81621010d..d8445244ab 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -423,6 +423,7 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.siselect, RISCVCPU), VMSTATE_UINT32(env.scounteren, RISCVCPU), VMSTATE_UINT32(env.mcounteren, RISCVCPU), + VMSTATE_UINT32(env.scountinhibit, RISCVCPU), VMSTATE_UINT32(env.mcountinhibit, RISCVCPU), VMSTATE_STRUCT_ARRAY(env.pmu_ctrs, RISCVCPU, RV_MAX_MHPMCOUNTERS, 0, vmstate_pmu_ctr_state, PMUCTRState), From b6504cd0d1ddb766410a951dc9f5bb63059d8eb6 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue Date: Fri, 10 Jan 2025 00:21:35 -0800 Subject: [PATCH 1210/2892] target/riscv: Add select value range check for counter delegation This adds checks in ops performed on xireg and xireg2-xireg6 so that the counter delegation function will receive a valid xiselect value with the proper extensions enabled. Co-developed-by: Atish Patra Signed-off-by: Kaiwen Xue Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-7-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 49648ddc95..df748dffa3 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2159,6 +2159,11 @@ static bool xiselect_aia_range(target_ulong isel) (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST); } +static bool xiselect_cd_range(target_ulong isel) +{ + return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST); +} + static int rmw_iprio(target_ulong xlen, target_ulong iselect, uint8_t *iprio, target_ulong *val, target_ulong new_val, @@ -2284,6 +2289,17 @@ done: return RISCV_EXCP_NONE; } +static int rmw_xireg_cd(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + if (!riscv_cpu_cfg(env)->ext_smcdeleg) { + return RISCV_EXCP_ILLEGAL_INST; + } + /* TODO: Implement the functionality later */ + return RISCV_EXCP_NONE; +} + /* * rmw_xireg_csrind: Perform indirect access to xireg and xireg2-xireg6 * @@ -2295,7 +2311,25 @@ static int rmw_xireg_csrind(CPURISCVState *env, int csrno, target_ulong isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - return -EINVAL; + int ret = -EINVAL; + bool virt = csrno == CSR_VSIREG ? true : false; + + if (xiselect_cd_range(isel)) { + ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask); + } else { + /* + * As per the specification, access to unimplented region is undefined + * but recommendation is to raise illegal instruction exception. + */ + return RISCV_EXCP_ILLEGAL_INST; + } + + if (ret) { + return (env->virt_enabled && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; } static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val, From 6247dc2ef70b512523d4495e8ea8349ccd6b0ef0 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue Date: Fri, 10 Jan 2025 00:21:36 -0800 Subject: [PATCH 1211/2892] target/riscv: Add counter delegation/configuration support The Smcdeleg/Ssccfg adds the support for counter delegation via S*indcsr and Ssccfg. It also adds a new shadow CSR scountinhibit and menvcfg enable bit (CDE) to enable this extension and scountovf virtualization. Signed-off-by: Kaiwen Xue Co-developed-by: Atish Patra Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-8-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 304 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 292 insertions(+), 12 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index df748dffa3..eddcf5a5d0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -383,6 +383,21 @@ static RISCVException aia_smode32(CPURISCVState *env, int csrno) return smode32(env, csrno); } +static RISCVException scountinhibit_pred(CPURISCVState *env, int csrno) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_ssccfg || !cpu->cfg.ext_smcdeleg) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + + return smode(env, csrno); +} + static bool csrind_extensions_present(CPURISCVState *env) { return riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind; @@ -1224,10 +1239,9 @@ done: return result; } -static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException riscv_pmu_write_ctr(CPURISCVState *env, target_ulong val, + uint32_t ctr_idx) { - int ctr_idx = csrno - CSR_MCYCLE; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; uint64_t mhpmctr_val = val; @@ -1252,10 +1266,9 @@ static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException riscv_pmu_write_ctrh(CPURISCVState *env, target_ulong val, + uint32_t ctr_idx) { - int ctr_idx = csrno - CSR_MCYCLEH; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; uint64_t mhpmctr_val = counter->mhpmcounter_val; uint64_t mhpmctrh_val = val; @@ -1277,6 +1290,20 @@ static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) +{ + int ctr_idx = csrno - CSR_MCYCLE; + + return riscv_pmu_write_ctr(env, val, ctr_idx); +} + +static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) +{ + int ctr_idx = csrno - CSR_MCYCLEH; + + return riscv_pmu_write_ctrh(env, val, ctr_idx); +} + RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, bool upper_half, uint32_t ctr_idx) { @@ -1342,6 +1369,167 @@ static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, return riscv_pmu_read_ctr(env, val, true, ctr_index); } +static int rmw_cd_mhpmcounter(CPURISCVState *env, int ctr_idx, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + riscv_pmu_read_ctr(env, val, false, ctr_idx); + } else if (wr_mask) { + riscv_pmu_write_ctr(env, new_val, ctr_idx); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_mhpmcounterh(CPURISCVState *env, int ctr_idx, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + riscv_pmu_read_ctr(env, val, true, ctr_idx); + } else if (wr_mask) { + riscv_pmu_write_ctrh(env, new_val, ctr_idx); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_mhpmevent(CPURISCVState *env, int evt_index, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + uint64_t mhpmevt_val = new_val; + + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + *val = env->mhpmevent_val[evt_index]; + if (riscv_cpu_cfg(env)->ext_sscofpmf) { + *val &= ~MHPMEVENT_BIT_MINH; + } + } else if (wr_mask) { + wr_mask &= ~MHPMEVENT_BIT_MINH; + mhpmevt_val = (new_val & wr_mask) | + (env->mhpmevent_val[evt_index] & ~wr_mask); + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevt_val = mhpmevt_val | + ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + } + env->mhpmevent_val[evt_index] = mhpmevt_val; + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_mhpmeventh(CPURISCVState *env, int evt_index, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + uint64_t mhpmevth_val; + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + *val = env->mhpmeventh_val[evt_index]; + if (riscv_cpu_cfg(env)->ext_sscofpmf) { + *val &= ~MHPMEVENTH_BIT_MINH; + } + } else if (wr_mask) { + wr_mask &= ~MHPMEVENTH_BIT_MINH; + env->mhpmeventh_val[evt_index] = + (new_val & wr_mask) | (env->mhpmeventh_val[evt_index] & ~wr_mask); + mhpmevth_val = env->mhpmeventh_val[evt_index]; + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_ctr_cfg(CPURISCVState *env, int cfg_index, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + switch (cfg_index) { + case 0: /* CYCLECFG */ + if (wr_mask) { + wr_mask &= ~MCYCLECFG_BIT_MINH; + env->mcyclecfg = (new_val & wr_mask) | (env->mcyclecfg & ~wr_mask); + } else { + *val = env->mcyclecfg &= ~MHPMEVENTH_BIT_MINH; + } + break; + case 2: /* INSTRETCFG */ + if (wr_mask) { + wr_mask &= ~MINSTRETCFG_BIT_MINH; + env->minstretcfg = (new_val & wr_mask) | + (env->minstretcfg & ~wr_mask); + } else { + *val = env->minstretcfg &= ~MHPMEVENTH_BIT_MINH; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int rmw_cd_ctr_cfgh(CPURISCVState *env, int cfg_index, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + switch (cfg_index) { + case 0: /* CYCLECFGH */ + if (wr_mask) { + wr_mask &= ~MCYCLECFGH_BIT_MINH; + env->mcyclecfgh = (new_val & wr_mask) | + (env->mcyclecfgh & ~wr_mask); + } else { + *val = env->mcyclecfgh; + } + break; + case 2: /* INSTRETCFGH */ + if (wr_mask) { + wr_mask &= ~MINSTRETCFGH_BIT_MINH; + env->minstretcfgh = (new_val & wr_mask) | + (env->minstretcfgh & ~wr_mask); + } else { + *val = env->minstretcfgh; + } + break; + default: + return -EINVAL; + } + return 0; +} + + static RISCVException read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1351,6 +1539,14 @@ static RISCVException read_scountovf(CPURISCVState *env, int csrno, target_ulong *mhpm_evt_val; uint64_t of_bit_mask; + /* Virtualize scountovf for counter delegation */ + if (riscv_cpu_cfg(env)->ext_sscofpmf && + riscv_cpu_cfg(env)->ext_ssccfg && + get_field(env->menvcfg, MENVCFG_CDE) && + env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + if (riscv_cpu_mxl(env) == MXL_RV32) { mhpm_evt_val = env->mhpmeventh_val; of_bit_mask = MHPMEVENTH_BIT_OF; @@ -2293,11 +2489,72 @@ static int rmw_xireg_cd(CPURISCVState *env, int csrno, target_ulong isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - if (!riscv_cpu_cfg(env)->ext_smcdeleg) { - return RISCV_EXCP_ILLEGAL_INST; + int ret = -EINVAL; + int ctr_index = isel - ISELECT_CD_FIRST; + int isel_hpm_start = ISELECT_CD_FIRST + 3; + + if (!riscv_cpu_cfg(env)->ext_smcdeleg || !riscv_cpu_cfg(env)->ext_ssccfg) { + ret = RISCV_EXCP_ILLEGAL_INST; + goto done; } - /* TODO: Implement the functionality later */ - return RISCV_EXCP_NONE; + + /* Invalid siselect value for reserved */ + if (ctr_index == 1) { + goto done; + } + + /* sireg4 and sireg5 provides access RV32 only CSRs */ + if (((csrno == CSR_SIREG5) || (csrno == CSR_SIREG4)) && + (riscv_cpu_mxl(env) != MXL_RV32)) { + ret = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + + /* Check Sscofpmf dependancy */ + if (!riscv_cpu_cfg(env)->ext_sscofpmf && csrno == CSR_SIREG5 && + (isel_hpm_start <= isel && isel <= ISELECT_CD_LAST)) { + goto done; + } + + /* Check smcntrpmf dependancy */ + if (!riscv_cpu_cfg(env)->ext_smcntrpmf && + (csrno == CSR_SIREG2 || csrno == CSR_SIREG5) && + (ISELECT_CD_FIRST <= isel && isel < isel_hpm_start)) { + goto done; + } + + if (!get_field(env->mcounteren, BIT(ctr_index)) || + !get_field(env->menvcfg, MENVCFG_CDE)) { + goto done; + } + + switch (csrno) { + case CSR_SIREG: + ret = rmw_cd_mhpmcounter(env, ctr_index, val, new_val, wr_mask); + break; + case CSR_SIREG4: + ret = rmw_cd_mhpmcounterh(env, ctr_index, val, new_val, wr_mask); + break; + case CSR_SIREG2: + if (ctr_index <= 2) { + ret = rmw_cd_ctr_cfg(env, ctr_index, val, new_val, wr_mask); + } else { + ret = rmw_cd_mhpmevent(env, ctr_index, val, new_val, wr_mask); + } + break; + case CSR_SIREG5: + if (ctr_index <= 2) { + ret = rmw_cd_ctr_cfgh(env, ctr_index, val, new_val, wr_mask); + } else { + ret = rmw_cd_mhpmeventh(env, ctr_index, val, new_val, wr_mask); + } + break; + default: + goto done; + } + +done: + return ret; } /* @@ -2576,6 +2833,21 @@ static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_scountinhibit(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* S-mode can only access the bits delegated by M-mode */ + *val = env->mcountinhibit & env->mcounteren; + return RISCV_EXCP_NONE; +} + +static RISCVException write_scountinhibit(CPURISCVState *env, int csrno, + target_ulong val) +{ + write_mcountinhibit(env, csrno, val & env->mcounteren); + return RISCV_EXCP_NONE; +} + static RISCVException read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) { @@ -2678,11 +2950,13 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, target_ulong val) { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); - uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE; + uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | + MENVCFG_CBZE | MENVCFG_CDE; if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | + (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | (cfg->ext_svadu ? MENVCFG_ADUE : 0); if (env_archcpu(env)->cfg.ext_zicfilp) { @@ -2717,7 +2991,8 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | - (cfg->ext_svadu ? MENVCFG_ADUE : 0); + (cfg->ext_svadu ? MENVCFG_ADUE : 0) | + (cfg->ext_smcdeleg ? MENVCFG_CDE : 0); uint64_t valh = (uint64_t)val << 32; env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); @@ -5304,6 +5579,11 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MNSTATUS] = { "mnstatus", rnmi, read_mnstatus, write_mnstatus, .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor Counter Delegation */ + [CSR_SCOUNTINHIBIT] = {"scountinhibit", scountinhibit_pred, + read_scountinhibit, write_scountinhibit, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor Trap Setup */ [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, read_sstatus_i128 }, From 04ff272d588695a2a4c328347e767b24fa241408 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 10 Jan 2025 00:21:37 -0800 Subject: [PATCH 1212/2892] target/riscv: Invoke pmu init after feature enable The dependant ISA features are enabled at the end of cpu_realize in finalize_features. Thus, PMU init should be invoked after that only. Move the init invocation to riscv_tcg_cpu_finalize_features. Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-9-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index f94aa9f29e..48be24bbbe 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -963,6 +963,20 @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) error_propagate(errp, local_err); return; } +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.pmu_mask) { + riscv_pmu_init(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + if (cpu->cfg.ext_sscofpmf) { + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_pmu_timer_cb, cpu); + } + } +#endif } void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu) @@ -1010,7 +1024,6 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) #ifndef CONFIG_USER_ONLY CPURISCVState *env = &cpu->env; - Error *local_err = NULL; tcg_cflags_set(CPU(cs), CF_PCREL); @@ -1018,19 +1031,6 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) riscv_timer_init(cpu); } - if (cpu->cfg.pmu_mask) { - riscv_pmu_init(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return false; - } - - if (cpu->cfg.ext_sscofpmf) { - cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - riscv_pmu_timer_cb, cpu); - } - } - /* With H-Ext, VSSIP, VSTIP, VSEIP and SGEIP are hardwired to one. */ if (riscv_has_ext(env, RVH)) { env->mideleg = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | MIP_SGEIP; From 2a754d6957e70889e7208f4d2d6bdb9714508c9b Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 10 Jan 2025 00:21:38 -0800 Subject: [PATCH 1213/2892] target/riscv: Add implied rule for counter delegation extensions The counter delegation/configuration extensions depend on the following extensions. 1. Smcdeleg - To enable counter delegation from M to S 2. S[m|s]csrind - To enable indirect access CSRs Add an implied rule so that these extensions are enabled by default if the sscfg extension is enabled. Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-10-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index da40f68715..671fc3d1c1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2760,6 +2760,16 @@ static RISCVCPUImpliedExtsRule ZVKSG_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule SSCFG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_ssccfg), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_smcsrind), CPU_CFG_OFFSET(ext_sscsrind), + CPU_CFG_OFFSET(ext_smcdeleg), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, &RVM_IMPLIED, &RVV_IMPLIED, NULL @@ -2777,7 +2787,7 @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { &ZVE64X_IMPLIED, &ZVFBFMIN_IMPLIED, &ZVFBFWMA_IMPLIED, &ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED, &ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED, - &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, + &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, &SSCFG_IMPLIED, NULL }; From fdb7bce43f9008d83e1edfd260a8165119b61ca5 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 10 Jan 2025 00:21:39 -0800 Subject: [PATCH 1214/2892] target/riscv: Add configuration for S[m|s]csrind, Smcdeleg/Ssccfg Add configuration options so that they can be enabled/disabld from qemu commandline. Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Signed-off-by: Atish Patra Message-ID: <20250110-counter_delegation-v5-11-e83d797ae294@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 671fc3d1c1..fe470f646d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1587,6 +1587,10 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { /* Defaults for standard extensions */ MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false), + MULTI_EXT_CFG_BOOL("smcsrind", ext_smcsrind, false), + MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false), + MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false), + MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false), MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), MULTI_EXT_CFG_BOOL("zicfilp", ext_zicfilp, false), MULTI_EXT_CFG_BOOL("zicfiss", ext_zicfiss, false), From 507957eb2acfd321646c98bc853d6c8bafe628d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:32 +0100 Subject: [PATCH 1215/2892] target/riscv: Fix henvcfg potentially containing stale bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the current implementation, if we had the following scenario: - Set bit x in menvcfg - Set bit x in henvcfg - Clear bit x in menvcfg then, the internal variable env->henvcfg would still contain bit x due to both a wrong menvcfg mask used in write_henvcfg() as well as a missing update of henvcfg upon menvcfg update. This can lead to some wrong interpretation of the context. In order to update henvcfg upon menvcfg writing, call write_henvcfg() after writing menvcfg. Clearing henvcfg upon writing the new value is also needed in write_henvcfg() as well as clearing henvcfg upper part when writing it with write_henvcfgh(). Signed-off-by: Clément Léger Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-2-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index eddcf5a5d0..279293b86d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2946,6 +2946,8 @@ static RISCVException read_menvcfg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException write_henvcfg(CPURISCVState *env, int csrno, + target_ulong val); static RISCVException write_menvcfg(CPURISCVState *env, int csrno, target_ulong val) { @@ -2974,6 +2976,7 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); + write_henvcfg(env, CSR_HENVCFG, env->henvcfg); return RISCV_EXCP_NONE; } @@ -2985,6 +2988,8 @@ static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, + target_ulong val); static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, target_ulong val) { @@ -2996,6 +3001,7 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, uint64_t valh = (uint64_t)val << 32; env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32); return RISCV_EXCP_NONE; } @@ -3101,7 +3107,7 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, } } - env->henvcfg = (env->henvcfg & ~mask) | (val & mask); + env->henvcfg = val & mask; return RISCV_EXCP_NONE; } @@ -3134,7 +3140,7 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, return ret; } - env->henvcfg = (env->henvcfg & ~mask) | (valh & mask); + env->henvcfg = (env->henvcfg & 0xFFFFFFFF) | (valh & mask); return RISCV_EXCP_NONE; } From 0aadf8162a77a03c79e35e76e16b99cd18ef7916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:33 +0100 Subject: [PATCH 1216/2892] target/riscv: Add Ssdbltrp CSRs handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ext_ssdbltrp in RISCVCPUConfig and implement MSTATUS.SDT, {H|M}ENVCFG.DTE and modify the availability of MTVAL2 based on the presence of the Ssdbltrp ISA extension. Signed-off-by: Clément Léger Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-3-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 1 + target/riscv/cpu_bits.h | 6 ++++ target/riscv/cpu_cfg.h | 1 + target/riscv/cpu_helper.c | 17 ++++++++++ target/riscv/csr.c | 71 ++++++++++++++++++++++++++++++++------- 5 files changed, 84 insertions(+), 12 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index a936300103..97713681cb 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -564,6 +564,7 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); int riscv_env_mmu_index(CPURISCVState *env, bool ifetch); bool cpu_get_fcfien(CPURISCVState *env); bool cpu_get_bcfien(CPURISCVState *env); +bool riscv_env_smode_dbltrp_enabled(CPURISCVState *env, bool virt); G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 73f7d37d80..0a56163d73 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -555,6 +555,7 @@ #define MSTATUS_TW 0x00200000 /* since: priv-1.10 */ #define MSTATUS_TSR 0x00400000 /* since: priv-1.10 */ #define MSTATUS_SPELP 0x00800000 /* zicfilp */ +#define MSTATUS_SDT 0x01000000 #define MSTATUS_MPELP 0x020000000000 /* zicfilp */ #define MSTATUS_GVA 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL @@ -587,6 +588,7 @@ typedef enum { #define SSTATUS_SUM 0x00040000 /* since: priv-1.10 */ #define SSTATUS_MXR 0x00080000 #define SSTATUS_SPELP MSTATUS_SPELP /* zicfilp */ +#define SSTATUS_SDT MSTATUS_SDT #define SSTATUS64_UXL 0x0000000300000000ULL @@ -782,12 +784,14 @@ typedef enum RISCVException { #define MENVCFG_CBCFE BIT(6) #define MENVCFG_CBZE BIT(7) #define MENVCFG_PMM (3ULL << 32) +#define MENVCFG_DTE (1ULL << 59) #define MENVCFG_CDE (1ULL << 60) #define MENVCFG_ADUE (1ULL << 61) #define MENVCFG_PBMTE (1ULL << 62) #define MENVCFG_STCE (1ULL << 63) /* For RV32 */ +#define MENVCFGH_DTE BIT(27) #define MENVCFGH_ADUE BIT(29) #define MENVCFGH_PBMTE BIT(30) #define MENVCFGH_STCE BIT(31) @@ -808,11 +812,13 @@ typedef enum RISCVException { #define HENVCFG_CBCFE MENVCFG_CBCFE #define HENVCFG_CBZE MENVCFG_CBZE #define HENVCFG_PMM MENVCFG_PMM +#define HENVCFG_DTE MENVCFG_DTE #define HENVCFG_ADUE MENVCFG_ADUE #define HENVCFG_PBMTE MENVCFG_PBMTE #define HENVCFG_STCE MENVCFG_STCE /* For RV32 */ +#define HENVCFGH_DTE MENVCFGH_DTE #define HENVCFGH_ADUE MENVCFGH_ADUE #define HENVCFGH_PBMTE MENVCFGH_PBMTE #define HENVCFGH_STCE MENVCFGH_STCE diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 561f5119b6..20e11a5bdd 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -83,6 +83,7 @@ struct RISCVCPUConfig { bool ext_smcntrpmf; bool ext_smcsrind; bool ext_sscsrind; + bool ext_ssdbltrp; bool ext_svadu; bool ext_svinval; bool ext_svnapot; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 3318ce440d..1eac0a0062 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -120,6 +120,19 @@ bool cpu_get_bcfien(CPURISCVState *env) } } +bool riscv_env_smode_dbltrp_enabled(CPURISCVState *env, bool virt) +{ +#ifdef CONFIG_USER_ONLY + return false; +#else + if (virt) { + return (env->henvcfg & HENVCFG_DTE) != 0; + } else { + return (env->menvcfg & MENVCFG_DTE) != 0; + } +#endif +} + void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, uint64_t *cs_base, uint32_t *pflags) { @@ -691,6 +704,10 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) g_assert(riscv_has_ext(env, RVH)); + if (riscv_env_smode_dbltrp_enabled(env, current_virt)) { + mstatus_mask |= MSTATUS_SDT; + } + if (current_virt) { /* Current V=1 and we are about to change to V=0 */ env->vsstatus = env->mstatus & mstatus_mask; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 279293b86d..4aded3f00c 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -680,6 +680,15 @@ static RISCVException aia_hmode32(CPURISCVState *env, int csrno) return hmode32(env, csrno); } +static RISCVException dbltrp_hmode(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + return RISCV_EXCP_NONE; + } + + return hmode(env, csrno); +} + static RISCVException pmp(CPURISCVState *env, int csrno) { if (riscv_cpu_cfg(env)->pmp) { @@ -1938,6 +1947,13 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mask |= MSTATUS_VS; } + if (riscv_env_smode_dbltrp_enabled(env, env->virt_enabled)) { + mask |= MSTATUS_SDT; + if ((val & MSTATUS_SDT) != 0) { + val &= ~MSTATUS_SIE; + } + } + if (xl != MXL_RV32 || env->debugger) { if (riscv_has_ext(env, RVH)) { mask |= MSTATUS_MPV | MSTATUS_GVA; @@ -2959,7 +2975,8 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | - (cfg->ext_svadu ? MENVCFG_ADUE : 0); + (cfg->ext_svadu ? MENVCFG_ADUE : 0) | + (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= MENVCFG_LPE; @@ -2973,6 +2990,10 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, if (env_archcpu(env)->cfg.ext_smnpm && get_field(val, MENVCFG_PMM) != PMM_FIELD_RESERVED) { mask |= MENVCFG_PMM; + } + + if ((val & MENVCFG_DTE) == 0) { + env->mstatus &= ~MSTATUS_SDT; } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); @@ -2997,9 +3018,14 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, uint64_t mask = (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | (cfg->ext_svadu ? MENVCFG_ADUE : 0) | - (cfg->ext_smcdeleg ? MENVCFG_CDE : 0); + (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | + (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); uint64_t valh = (uint64_t)val << 32; + if ((valh & MENVCFG_DTE) == 0) { + env->mstatus &= ~MSTATUS_SDT; + } + env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32); @@ -3070,9 +3096,10 @@ static RISCVException read_henvcfg(CPURISCVState *env, int csrno, * henvcfg.pbmte is read_only 0 when menvcfg.pbmte = 0 * henvcfg.stce is read_only 0 when menvcfg.stce = 0 * henvcfg.adue is read_only 0 when menvcfg.adue = 0 + * henvcfg.dte is read_only 0 when menvcfg.dte = 0 */ - *val = env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE) | - env->menvcfg); + *val = env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | + HENVCFG_DTE) | env->menvcfg); return RISCV_EXCP_NONE; } @@ -3088,7 +3115,8 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, } if (riscv_cpu_mxl(env) == MXL_RV64) { - mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE); + mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | + HENVCFG_DTE); if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= HENVCFG_LPE; @@ -3108,6 +3136,9 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, } env->henvcfg = val & mask; + if ((env->henvcfg & HENVCFG_DTE) == 0) { + env->vsstatus &= ~MSTATUS_SDT; + } return RISCV_EXCP_NONE; } @@ -3122,8 +3153,8 @@ static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, return ret; } - *val = (env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE) | - env->menvcfg)) >> 32; + *val = (env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | + HENVCFG_DTE) | env->menvcfg)) >> 32; return RISCV_EXCP_NONE; } @@ -3131,7 +3162,7 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, target_ulong val) { uint64_t mask = env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | - HENVCFG_ADUE); + HENVCFG_ADUE | HENVCFG_DTE); uint64_t valh = (uint64_t)val << 32; RISCVException ret; @@ -3139,8 +3170,10 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, if (ret != RISCV_EXCP_NONE) { return ret; } - env->henvcfg = (env->henvcfg & 0xFFFFFFFF) | (valh & mask); + if ((env->henvcfg & HENVCFG_DTE) == 0) { + env->vsstatus &= ~MSTATUS_SDT; + } return RISCV_EXCP_NONE; } @@ -3594,6 +3627,9 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, if (env->xl != MXL_RV32 || env->debugger) { mask |= SSTATUS64_UXL; } + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mask |= SSTATUS_SDT; + } if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= SSTATUS_SPELP; @@ -3614,7 +3650,9 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= SSTATUS_SPELP; } - + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mask |= SSTATUS_SDT; + } /* TODO: Use SXL not MXL. */ *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; @@ -3634,7 +3672,9 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno, if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= SSTATUS_SPELP; } - + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mask |= SSTATUS_SDT; + } target_ulong newval = (env->mstatus & ~mask) | (val & mask); return write_mstatus(env, CSR_MSTATUS, newval); } @@ -4751,6 +4791,13 @@ static RISCVException write_vsstatus(CPURISCVState *env, int csrno, if ((val & VSSTATUS64_UXL) == 0) { mask &= ~VSSTATUS64_UXL; } + if ((env->henvcfg & HENVCFG_DTE)) { + if ((val & SSTATUS_SDT) != 0) { + val &= ~SSTATUS_SIE; + } + } else { + val &= ~SSTATUS_SDT; + } env->vsstatus = (env->vsstatus & ~mask) | (uint64_t)val; return RISCV_EXCP_NONE; } @@ -5698,7 +5745,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, + [CSR_MTVAL2] = { "mtval2", dbltrp_hmode, read_mtval2, write_mtval2, .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, .min_priv_ver = PRIV_VERSION_1_12_0 }, From 72d71d87327f47dc878683f4ff6a21472d5dcfdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:34 +0100 Subject: [PATCH 1217/2892] target/riscv: Implement Ssdbltrp sret, mret and mnret behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the Ssdbltrp extension is enabled, SSTATUS.SDT field is cleared when executing sret. When executing mret/mnret, SSTATUS.SDT is cleared when returning to U, VS or VU and VSSTATUS.SDT is cleared when returning to VU from HS. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-4-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/op_helper.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index c825336519..59c4bf28ed 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -294,6 +294,18 @@ target_ulong helper_sret(CPURISCVState *env) get_field(mstatus, MSTATUS_SPIE)); mstatus = set_field(mstatus, MSTATUS_SPIE, 1); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + if (riscv_has_ext(env, RVH)) { + target_ulong prev_vu = get_field(env->hstatus, HSTATUS_SPV) && + prev_priv == PRV_U; + /* Returning to VU from HS, vsstatus.sdt = 0 */ + if (!env->virt_enabled && prev_vu) { + env->vsstatus = set_field(env->vsstatus, MSTATUS_SDT, 0); + } + } + mstatus = set_field(mstatus, MSTATUS_SDT, 0); + } if (env->priv_ver >= PRIV_VERSION_1_12_0) { mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } @@ -304,7 +316,6 @@ target_ulong helper_sret(CPURISCVState *env) target_ulong hstatus = env->hstatus; prev_virt = get_field(hstatus, HSTATUS_SPV); - hstatus = set_field(hstatus, HSTATUS_SPV, 0); env->hstatus = hstatus; @@ -344,6 +355,22 @@ static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); } } +static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, + target_ulong prev_priv, + target_ulong prev_virt) +{ + /* If returning to U, VS or VU, sstatus.sdt = 0 */ + if (prev_priv == PRV_U || (prev_virt && + (prev_priv == PRV_S || prev_priv == PRV_U))) { + mstatus = set_field(mstatus, MSTATUS_SDT, 0); + /* If returning to VU, vsstatus.sdt = 0 */ + if (prev_virt && prev_priv == PRV_U) { + env->vsstatus = set_field(env->vsstatus, MSTATUS_SDT, 0); + } + } + + return mstatus; +} target_ulong helper_mret(CPURISCVState *env) { @@ -361,6 +388,9 @@ target_ulong helper_mret(CPURISCVState *env) mstatus = set_field(mstatus, MSTATUS_MPP, riscv_has_ext(env, RVU) ? PRV_U : PRV_M); mstatus = set_field(mstatus, MSTATUS_MPV, 0); + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mstatus = ssdbltrp_mxret(env, mstatus, prev_priv, prev_virt); + } if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } @@ -402,6 +432,9 @@ target_ulong helper_mnret(CPURISCVState *env) if (prev_priv < PRV_M) { env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false); } + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + env->mstatus = ssdbltrp_mxret(env, env->mstatus, prev_priv, prev_virt); + } if (riscv_has_ext(env, RVH) && prev_virt) { riscv_cpu_swap_hypervisor_regs(env); From 967760f62c7ca5ab6194131f4ff152b37f2d7017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:35 +0100 Subject: [PATCH 1218/2892] target/riscv: Implement Ssdbltrp exception handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the Ssdbltrp ISA extension is enabled, if a trap happens in S-mode while SSTATUS.SDT isn't cleared, generate a double trap exception to M-mode. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-5-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- target/riscv/cpu_bits.h | 1 + target/riscv/cpu_helper.c | 42 ++++++++++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fe470f646d..5540eb7f63 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -303,7 +303,7 @@ static const char * const riscv_excp_names[] = { "load_page_fault", "reserved", "store_page_fault", - "reserved", + "double_trap", "reserved", "reserved", "reserved", diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 0a56163d73..a3acda4bc8 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -701,6 +701,7 @@ typedef enum RISCVException { RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ + RISCV_EXCP_DOUBLE_TRAP = 0x10, RISCV_EXCP_SW_CHECK = 0x12, /* since: priv-1.13.0 */ RISCV_EXCP_HW_ERR = 0x13, /* since: priv-1.13.0 */ RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 1eac0a0062..539ba327e7 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1951,6 +1951,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) bool virt = env->virt_enabled; bool write_gva = false; bool always_storeamo = (env->excp_uw2 & RISCV_UW2_ALWAYS_STORE_AMO); + bool vsmode_exc; uint64_t s; int mode; @@ -1965,6 +1966,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) !(env->mip & (1ULL << cause)); bool vs_injected = env->hvip & (1ULL << cause) & env->hvien && !(env->mip & (1ULL << cause)); + bool smode_double_trap = false; + uint64_t hdeleg = async ? env->hideleg : env->hedeleg; target_ulong tval = 0; target_ulong tinst = 0; target_ulong htval = 0; @@ -2088,6 +2091,30 @@ void riscv_cpu_do_interrupt(CPUState *cs) mode = env->priv <= PRV_S && cause < 64 && (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M; + vsmode_exc = env->virt_enabled && (((hdeleg >> cause) & 1) || vs_injected); + /* + * Check double trap condition only if already in S-mode and targeting + * S-mode + */ + if (cpu->cfg.ext_ssdbltrp && env->priv == PRV_S && mode == PRV_S) { + bool dte = (env->menvcfg & MENVCFG_DTE) != 0; + bool sdt = (env->mstatus & MSTATUS_SDT) != 0; + /* In VS or HS */ + if (riscv_has_ext(env, RVH)) { + if (vsmode_exc) { + /* VS -> VS, use henvcfg instead of menvcfg*/ + dte = (env->henvcfg & HENVCFG_DTE) != 0; + } else if (env->virt_enabled) { + /* VS -> HS, use mstatus_hs */ + sdt = (env->mstatus_hs & MSTATUS_SDT) != 0; + } + } + smode_double_trap = dte && sdt; + if (smode_double_trap) { + mode = PRV_M; + } + } + if (mode == PRV_S) { /* handle the trap in S-mode */ /* save elp status */ @@ -2096,10 +2123,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) } if (riscv_has_ext(env, RVH)) { - uint64_t hdeleg = async ? env->hideleg : env->hedeleg; - - if (env->virt_enabled && - (((hdeleg >> cause) & 1) || vs_injected)) { + if (vsmode_exc) { /* Trap to VS mode */ /* * See if we need to adjust cause. Yes if its VS mode interrupt @@ -2132,6 +2156,9 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); + if (riscv_env_smode_dbltrp_enabled(env, virt)) { + s = set_field(s, MSTATUS_SDT, 1); + } env->mstatus = s; sxlen = 16 << riscv_cpu_sxl(env); env->scause = cause | ((target_ulong)async << (sxlen - 1)); @@ -2184,9 +2211,14 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_MIE, 0); env->mstatus = s; env->mcause = cause | ((target_ulong)async << (mxlen - 1)); + if (smode_double_trap) { + env->mtval2 = env->mcause; + env->mcause = RISCV_EXCP_DOUBLE_TRAP; + } else { + env->mtval2 = mtval2; + } env->mepc = env->pc; env->mtval = tval; - env->mtval2 = mtval2; env->mtinst = tinst; /* From b0edcbe755e88f969a5e201c093bad453ba4a13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:36 +0100 Subject: [PATCH 1219/2892] target/riscv: Add Ssdbltrp ISA extension enable switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the switch to enable the Ssdbltrp ISA extension. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-6-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5540eb7f63..9e1ce0e1f1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -205,6 +205,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind), + ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp), ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm), ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), @@ -1628,6 +1629,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("smnpm", ext_smnpm, false), MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), MULTI_EXT_CFG_BOOL("ssaia", ext_ssaia, false), + MULTI_EXT_CFG_BOOL("ssdbltrp", ext_ssdbltrp, false), MULTI_EXT_CFG_BOOL("svade", ext_svade, false), MULTI_EXT_CFG_BOOL("svadu", ext_svadu, true), MULTI_EXT_CFG_BOOL("svinval", ext_svinval, false), From d2e92f1c6d4441221e3ae07dd24613d479b310dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:37 +0100 Subject: [PATCH 1220/2892] target/riscv: Add Smdbltrp CSRs handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `ext_smdbltrp`in RISCVCPUConfig and implement MSTATUS.MDT behavior. Also set MDT to 1 at reset according to the specification. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-7-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +++ target/riscv/cpu_bits.h | 1 + target/riscv/cpu_cfg.h | 1 + target/riscv/csr.c | 13 +++++++++++++ 4 files changed, 18 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9e1ce0e1f1..e3ed11b0fd 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1064,6 +1064,9 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) env->mstatus_hs = set_field(env->mstatus_hs, MSTATUS64_UXL, env->misa_mxl); } + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 1); + } } env->mcause = 0; env->miclaim = MIP_SGEIP; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index a3acda4bc8..f97c48a394 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -559,6 +559,7 @@ #define MSTATUS_MPELP 0x020000000000 /* zicfilp */ #define MSTATUS_GVA 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL +#define MSTATUS_MDT 0x40000000000ULL /* Smdbltrp extension */ #define MSTATUS64_UXL 0x0000000300000000ULL #define MSTATUS64_SXL 0x0000000C00000000ULL diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 20e11a5bdd..aef896ba00 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -84,6 +84,7 @@ struct RISCVCPUConfig { bool ext_smcsrind; bool ext_sscsrind; bool ext_ssdbltrp; + bool ext_smdbltrp; bool ext_svadu; bool ext_svinval; bool ext_svnapot; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4aded3f00c..afb7544f07 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1954,6 +1954,13 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } } + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + mask |= MSTATUS_MDT; + if ((val & MSTATUS_MDT) != 0) { + val &= ~MSTATUS_MIE; + } + } + if (xl != MXL_RV32 || env->debugger) { if (riscv_has_ext(env, RVH)) { mask |= MSTATUS_MPV | MSTATUS_GVA; @@ -1996,6 +2003,12 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, uint64_t valh = (uint64_t)val << 32; uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + mask |= MSTATUS_MDT; + if ((valh & MSTATUS_MDT) != 0) { + mask |= MSTATUS_MIE; + } + } env->mstatus = (env->mstatus & ~mask) | (valh & mask); return RISCV_EXCP_NONE; From f2efb6e793094425ca0b9ee6bafafd7e11eaa666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:38 +0100 Subject: [PATCH 1221/2892] target/riscv: Implement Smdbltrp sret, mret and mnret behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the Ssdbltrp extension is enabled, SSTATUS.MDT field is cleared when executing sret if executed in M-mode. When executing mret/mnret, SSTATUS.MDT is cleared. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-8-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/op_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 59c4bf28ed..ce1256f439 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -306,6 +306,9 @@ target_ulong helper_sret(CPURISCVState *env) } mstatus = set_field(mstatus, MSTATUS_SDT, 0); } + if (riscv_cpu_cfg(env)->ext_smdbltrp && env->priv >= PRV_M) { + mstatus = set_field(mstatus, MSTATUS_MDT, 0); + } if (env->priv_ver >= PRIV_VERSION_1_12_0) { mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } @@ -391,6 +394,9 @@ target_ulong helper_mret(CPURISCVState *env) if (riscv_cpu_cfg(env)->ext_ssdbltrp) { mstatus = ssdbltrp_mxret(env, mstatus, prev_priv, prev_virt); } + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + mstatus = set_field(mstatus, MSTATUS_MDT, 0); + } if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } @@ -436,6 +442,12 @@ target_ulong helper_mnret(CPURISCVState *env) env->mstatus = ssdbltrp_mxret(env, env->mstatus, prev_priv, prev_virt); } + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + if (prev_priv < PRV_M) { + env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 0); + } + } + if (riscv_has_ext(env, RVH) && prev_virt) { riscv_cpu_swap_hypervisor_regs(env); } From 00af7d53601b70f1353dabd1c87ffa260aafd27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Fri, 10 Jan 2025 13:54:39 +0100 Subject: [PATCH 1222/2892] target/riscv: Implement Smdbltrp behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the Smsdbltrp ISA extension is enabled, if a trap happens while MSTATUS.MDT is already set, it will trigger an abort or an NMI is the Smrnmi extension is available. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20250110125441.3208676-9-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 57 ++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 539ba327e7..e1dfc4ecbf 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1938,6 +1938,24 @@ static target_ulong promote_load_fault(target_ulong orig_cause) /* if no promotion, return original cause */ return orig_cause; } + +static void riscv_do_nmi(CPURISCVState *env, target_ulong cause, bool virt) +{ + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV, virt); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP, env->priv); + env->mncause = cause; + env->mnepc = env->pc; + env->pc = env->rnmi_irqvec; + + if (cpu_get_fcfien(env)) { + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, env->elp); + } + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_mode(env, PRV_M, false); +} + /* * Handle Traps * @@ -1977,22 +1995,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) bool nnmi_excep = false; if (cpu->cfg.ext_smrnmi && env->rnmip && async) { - env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false); - env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV, - env->virt_enabled); - env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP, - env->priv); - env->mncause = cause | ((target_ulong)1U << (mxlen - 1)); - env->mnepc = env->pc; - env->pc = env->rnmi_irqvec; - - if (cpu_get_fcfien(env)) { - env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, env->elp); - } - - /* Trapping to M mode, virt is disabled */ - riscv_cpu_set_mode(env, PRV_M, false); - + riscv_do_nmi(env, cause | ((target_ulong)1U << (mxlen - 1)), + env->virt_enabled); return; } @@ -2204,11 +2208,32 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* Trapping to M mode, virt is disabled */ virt = false; } + /* + * If the hart encounters an exception while executing in M-mode, + * with the mnstatus.NMIE bit clear, the program counter is set to + * the RNMI exception trap handler address. + */ + nnmi_excep = cpu->cfg.ext_smrnmi && + !get_field(env->mnstatus, MNSTATUS_NMIE) && + !async; s = env->mstatus; s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE)); s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); + if (cpu->cfg.ext_smdbltrp) { + if (env->mstatus & MSTATUS_MDT) { + assert(env->priv == PRV_M); + if (!cpu->cfg.ext_smrnmi || nnmi_excep) { + cpu_abort(CPU(cpu), "M-mode double trap\n"); + } else { + riscv_do_nmi(env, cause, false); + return; + } + } + + s = set_field(s, MSTATUS_MDT, 1); + } env->mstatus = s; env->mcause = cause | ((target_ulong)async << (mxlen - 1)); if (smode_double_trap) { From 2d8e8259287ced7c689a7c7fad67ad2a417e477c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Thu, 16 Jan 2025 14:15:36 +0100 Subject: [PATCH 1223/2892] target/riscv: Add Smdbltrp ISA extension enable switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the switch to enable the Smdbltrp ISA extension and disable it for the max cpu. Indeed, OpenSBI when Smdbltrp is present, M-mode double trap is enabled by default and MSTATUS.MDT needs to be cleared to avoid taking a double trap. OpenSBI does not currently support it so disable it for the max cpu to avoid breaking regression tests. Signed-off-by: Clément Léger Reviewed-by: Daniel Henrique Barboza Message-ID: <20250116131539.2475785-1-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/tcg/tcg-cpu.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e3ed11b0fd..bddf1ba75e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -194,6 +194,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(smcdeleg, PRIV_VERSION_1_13_0, ext_smcdeleg), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_13_0, ext_smcsrind), + ISA_EXT_DATA_ENTRY(smdbltrp, PRIV_VERSION_1_13_0, ext_smdbltrp), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), ISA_EXT_DATA_ENTRY(smrnmi, PRIV_VERSION_1_12_0, ext_smrnmi), ISA_EXT_DATA_ENTRY(smmpm, PRIV_VERSION_1_13_0, ext_smmpm), @@ -1626,6 +1627,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false), MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), + MULTI_EXT_CFG_BOOL("smdbltrp", ext_smdbltrp, false), MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), MULTI_EXT_CFG_BOOL("smrnmi", ext_smrnmi, false), MULTI_EXT_CFG_BOOL("smmpm", ext_smmpm, false), diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 48be24bbbe..0a137281de 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1439,6 +1439,16 @@ static void riscv_init_max_cpu_extensions(Object *obj) isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smrnmi), false); qemu_log("Smrnmi is disabled in the 'max' type CPU\n"); } + + /* + * ext_smdbltrp requires the firmware to clear MSTATUS.MDT on startup to + * avoid generating a double trap. OpenSBI does not currently support it, + * disable it for now. + */ + if (cpu->cfg.ext_smdbltrp) { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smdbltrp), false); + qemu_log("Smdbltrp is disabled in the 'max' type CPU\n"); + } } static bool riscv_cpu_has_max_extensions(Object *cpu_obj) From fa622855eaaca8b543e19cf7ba8ab0304a1e4b84 Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Fri, 8 Nov 2024 19:01:47 +0800 Subject: [PATCH 1224/2892] hw/riscv/riscv-iommu.c: Introduce a translation tag for the page table cache This commit introduces a translation tag to avoid invalidating an entry that should not be invalidated when IOMMU executes invalidation commands. E.g. IOTINVAL.VMA with GV=0, AV=0, PSCV=1 invalidates both a mapping of single stage translation and a mapping of nested translation with the same PSCID, but only the former one should be invalidated. Signed-off-by: Jason Chien Reviewed-by: Daniel Henrique Barboza Message-ID: <20241108110147.11178-1-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 211 ++++++++++++++++++++++++++++++----------- 1 file changed, 156 insertions(+), 55 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 8bf920deab..e7568ca227 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -64,8 +64,16 @@ struct RISCVIOMMUContext { uint64_t msiptp; /* MSI redirection page table pointer */ }; +typedef enum RISCVIOMMUTransTag { + RISCV_IOMMU_TRANS_TAG_BY, /* Bypass */ + RISCV_IOMMU_TRANS_TAG_SS, /* Single Stage */ + RISCV_IOMMU_TRANS_TAG_VG, /* G-stage only */ + RISCV_IOMMU_TRANS_TAG_VN, /* Nested translation */ +} RISCVIOMMUTransTag; + /* Address translation cache entry */ struct RISCVIOMMUEntry { + RISCVIOMMUTransTag tag; /* Translation Tag */ uint64_t iova:44; /* IOVA Page Number */ uint64_t pscid:20; /* Process Soft-Context identifier */ uint64_t phys:44; /* Physical Page Number */ @@ -1227,7 +1235,7 @@ static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2) RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1; RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2; return t1->gscid == t2->gscid && t1->pscid == t2->pscid && - t1->iova == t2->iova; + t1->iova == t2->iova && t1->tag == t2->tag; } static guint riscv_iommu_iot_hash(gconstpointer v) @@ -1236,67 +1244,115 @@ static guint riscv_iommu_iot_hash(gconstpointer v) return (guint)t->iova; } -/* GV: 1 PSCV: 1 AV: 1 */ +/* GV: 0 AV: 0 PSCV: 0 GVMA: 0 */ +/* GV: 0 AV: 0 GVMA: 1 */ +static +void riscv_iommu_iot_inval_all(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 AV: 0 PSCV: 1 GVMA: 0 */ +static +void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->pscid == arg->pscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 AV: 1 PSCV: 0 GVMA: 0 */ +static +void riscv_iommu_iot_inval_iova(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->iova == arg->iova) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 AV: 1 PSCV: 1 GVMA: 0 */ static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value, gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->gscid == arg->gscid && + if (iot->tag == arg->tag && iot->pscid == arg->pscid && iot->iova == arg->iova) { iot->perm = IOMMU_NONE; } } -/* GV: 1 PSCV: 1 AV: 0 */ -static void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, - gpointer data) +/* GV: 1 AV: 0 PSCV: 0 GVMA: 0 */ +/* GV: 1 AV: 0 GVMA: 1 */ +static +void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->gscid == arg->gscid && + if (iot->tag == arg->tag && + iot->gscid == arg->gscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 AV: 0 PSCV: 1 GVMA: 0 */ +static void riscv_iommu_iot_inval_gscid_pscid(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->gscid == arg->gscid && iot->pscid == arg->pscid) { iot->perm = IOMMU_NONE; } } -/* GV: 1 GVMA: 1 */ -static void riscv_iommu_iot_inval_gscid_gpa(gpointer key, gpointer value, - gpointer data) +/* GV: 1 AV: 1 PSCV: 0 GVMA: 0 */ +/* GV: 1 AV: 1 GVMA: 1 */ +static void riscv_iommu_iot_inval_gscid_iova(gpointer key, gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->gscid == arg->gscid) { - /* simplified cache, no GPA matching */ + if (iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->iova == arg->iova) { iot->perm = IOMMU_NONE; } } -/* GV: 1 GVMA: 0 */ -static void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, - gpointer data) +/* GV: 1 AV: 1 PSCV: 1 GVMA: 0 */ +static void riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->gscid == arg->gscid) { + if (iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->pscid == arg->pscid && + iot->iova == arg->iova) { iot->perm = IOMMU_NONE; } } -/* GV: 0 */ -static void riscv_iommu_iot_inval_all(gpointer key, gpointer value, - gpointer data) -{ - RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; - iot->perm = IOMMU_NONE; -} - /* caller should keep ref-count for iot_cache object */ static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx, - GHashTable *iot_cache, hwaddr iova) + GHashTable *iot_cache, hwaddr iova, RISCVIOMMUTransTag transtag) { RISCVIOMMUEntry key = { + .tag = transtag, .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID), .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID), .iova = PPN_DOWN(iova), @@ -1322,10 +1378,11 @@ static void riscv_iommu_iot_update(RISCVIOMMUState *s, } static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, - uint32_t gscid, uint32_t pscid, hwaddr iova) + uint32_t gscid, uint32_t pscid, hwaddr iova, RISCVIOMMUTransTag transtag) { GHashTable *iot_cache; RISCVIOMMUEntry key = { + .tag = transtag, .gscid = gscid, .pscid = pscid, .iova = PPN_DOWN(iova), @@ -1336,9 +1393,24 @@ static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, g_hash_table_unref(iot_cache); } +static RISCVIOMMUTransTag riscv_iommu_get_transtag(RISCVIOMMUContext *ctx) +{ + uint64_t satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); + uint64_t gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + + if (satp == RISCV_IOMMU_DC_FSC_MODE_BARE) { + return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ? + RISCV_IOMMU_TRANS_TAG_BY : RISCV_IOMMU_TRANS_TAG_VG; + } else { + return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ? + RISCV_IOMMU_TRANS_TAG_SS : RISCV_IOMMU_TRANS_TAG_VN; + } +} + static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, IOMMUTLBEntry *iotlb, bool enable_cache) { + RISCVIOMMUTransTag transtag = riscv_iommu_get_transtag(ctx); RISCVIOMMUEntry *iot; IOMMUAccessFlags perm; bool enable_pid; @@ -1364,7 +1436,7 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } } - iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova); + iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova, transtag); perm = iot ? iot->perm : IOMMU_NONE; if (perm != IOMMU_NONE) { iotlb->translated_addr = PPN_PHYS(iot->phys); @@ -1395,6 +1467,7 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); iot->perm = iotlb->perm; + iot->tag = transtag; riscv_iommu_iot_update(s, iot_cache, iot); } @@ -1602,44 +1675,72 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA, RISCV_IOMMU_CMD_IOTINVAL_OPCODE): - if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) { + { + bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV); + bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV); + bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV); + uint32_t gscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_GSCID); + uint32_t pscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_PSCID); + hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK; + + if (pscv) { /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */ goto cmd_ill; - } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) { - /* invalidate all cache mappings */ - func = riscv_iommu_iot_inval_all; - } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) { - /* invalidate cache matching GSCID */ - func = riscv_iommu_iot_inval_gscid; - } else { - /* invalidate cache matching GSCID and ADDR (GPA) */ - func = riscv_iommu_iot_inval_gscid_gpa; } - riscv_iommu_iot_inval(s, func, - get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0, - cmd.dword1 << 2 & TARGET_PAGE_MASK); + + func = riscv_iommu_iot_inval_all; + + if (gv) { + func = (av) ? riscv_iommu_iot_inval_gscid_iova : + riscv_iommu_iot_inval_gscid; + } + + riscv_iommu_iot_inval( + s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VG); + + riscv_iommu_iot_inval( + s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VN); break; + } case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA, RISCV_IOMMU_CMD_IOTINVAL_OPCODE): - if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) { - /* invalidate all cache mappings, simplified model */ - func = riscv_iommu_iot_inval_all; - } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) { - /* invalidate cache matching GSCID, simplified model */ - func = riscv_iommu_iot_inval_gscid; - } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) { - /* invalidate cache matching GSCID and PSCID */ - func = riscv_iommu_iot_inval_pscid; + { + bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV); + bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV); + bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV); + uint32_t gscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_GSCID); + uint32_t pscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_PSCID); + hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK; + RISCVIOMMUTransTag transtag; + + if (gv) { + transtag = RISCV_IOMMU_TRANS_TAG_VN; + if (pscv) { + func = (av) ? riscv_iommu_iot_inval_gscid_pscid_iova : + riscv_iommu_iot_inval_gscid_pscid; + } else { + func = (av) ? riscv_iommu_iot_inval_gscid_iova : + riscv_iommu_iot_inval_gscid; + } } else { - /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */ - func = riscv_iommu_iot_inval_pscid_iova; + transtag = RISCV_IOMMU_TRANS_TAG_SS; + if (pscv) { + func = (av) ? riscv_iommu_iot_inval_pscid_iova : + riscv_iommu_iot_inval_pscid; + } else { + func = (av) ? riscv_iommu_iot_inval_iova : + riscv_iommu_iot_inval_all; + } } - riscv_iommu_iot_inval(s, func, - get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), - get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID), - cmd.dword1 << 2 & TARGET_PAGE_MASK); + + riscv_iommu_iot_inval(s, func, gscid, pscid, iova, transtag); break; + } case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT, RISCV_IOMMU_CMD_IODIR_OPCODE): From 941f76e2930c2d57e22692e4163d8797529d0491 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 13 Jan 2025 22:44:09 +0300 Subject: [PATCH 1225/2892] target/riscv: Support Supm and Sspm as part of Zjpm v1.0 The Zjpm v1.0 spec states there should be Supm and Sspm extensions that are used in profile specification. Enabling Supm extension enables both Ssnpm and Smnpm, while Sspm enables only Smnpm. Signed-off-by: Alexey Baturo Reviewed-by: Daniel Henrique Barboza Message-ID: <20250113194410.1307494-1-baturo.alexey@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 23 +++++++++++++++++++++++ target/riscv/cpu_cfg.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index bddf1ba75e..3d4bd157d2 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -208,10 +208,12 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind), ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp), ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm), + ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm), ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm), ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu), ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), @@ -1625,6 +1627,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zvfhmin", ext_zvfhmin, false), MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true), MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false), + MULTI_EXT_CFG_BOOL("sspm", ext_sspm, false), + MULTI_EXT_CFG_BOOL("supm", ext_supm, false), MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), MULTI_EXT_CFG_BOOL("smdbltrp", ext_smdbltrp, false), @@ -2781,6 +2785,24 @@ static RISCVCPUImpliedExtsRule SSCFG_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule SUPM_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_supm), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_ssnpm), CPU_CFG_OFFSET(ext_smnpm), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule SSPM_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_sspm), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_smnpm), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, &RVM_IMPLIED, &RVV_IMPLIED, NULL @@ -2799,6 +2821,7 @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { &ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED, &ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED, &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, &SSCFG_IMPLIED, + &SUPM_IMPLIED, &SSPM_IMPLIED, NULL }; diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index aef896ba00..b410b1e603 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -139,6 +139,8 @@ struct RISCVCPUConfig { bool ext_ssnpm; bool ext_smnpm; bool ext_smmpm; + bool ext_sspm; + bool ext_supm; bool rvv_ta_all_1s; bool rvv_ma_all_1s; bool rvv_vl_half_avl; From f04cac4f8f254931f2af9d059b2175769e576afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 23:36:09 +0100 Subject: [PATCH 1226/2892] hw/char/riscv_htif: Convert HTIF_DEBUG() to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20250116223609.81594-1-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/char/riscv_htif.c | 15 +++------------ hw/char/trace-events | 4 ++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 11a0e1a7b7..ec5db5a597 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -32,14 +32,7 @@ #include "exec/tswap.h" #include "system/dma.h" #include "system/runstate.h" - -#define RISCV_DEBUG_HTIF 0 -#define HTIF_DEBUG(fmt, ...) \ - do { \ - if (RISCV_DEBUG_HTIF) { \ - qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ - } \ - } while (0) +#include "trace.h" #define HTIF_DEV_SHIFT 56 #define HTIF_CMD_SHIFT 48 @@ -159,8 +152,7 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; int resp = 0; - HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 - " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload); + trace_htif_uart_write_to_host(device, cmd, payload); /* * Currently, there is a fixed mapping of devices: @@ -251,8 +243,7 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) } } else { qemu_log("HTIF unknown device or command\n"); - HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 - " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); + trace_htif_uart_unknown_device_command(device, cmd, payload); } /* * Latest bbl does not set fromhost to 0 if there is a value in tohost. diff --git a/hw/char/trace-events b/hw/char/trace-events index 3ee7cfcdff..b2e3d25ae3 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -136,3 +136,7 @@ stm32f2xx_usart_read(char *id, unsigned size, uint64_t ofs, uint64_t val) " %s s stm32f2xx_usart_write(char *id, unsigned size, uint64_t ofs, uint64_t val) "%s size %d ofs 0x%02" PRIx64 " <- 0x%02" PRIx64 stm32f2xx_usart_drop(char *id) " %s dropping the chars" stm32f2xx_usart_receive(char *id, uint8_t chr) " %s receiving '%c'" + +# riscv_htif.c +htif_uart_write_to_host(uint8_t device, uint8_t cmd, uint64_t payload) "device: %u cmd: %02u payload: %016" PRIx64 +htif_uart_unknown_device_command(uint8_t device, uint8_t cmd, uint64_t payload) "device: %u cmd: %02u payload: %016" PRIx64 From 0a8b4fd59fdec7edc5c569d5a30c96a5d456e584 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 Jan 2025 09:25:15 +0100 Subject: [PATCH 1227/2892] tests/functional: Convert the kvm_xen_guest avocado test Use the serial console to execute the commands in the guest instead of using ssh since we don't have ssh support in the functional framework yet. Acked-by: David Woodhouse Message-ID: <20250113082516.57894-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/functional/meson.build | 2 + .../test_x86_64_kvm_xen.py} | 89 +++++++++++-------- 3 files changed, 55 insertions(+), 38 deletions(-) rename tests/{avocado/kvm_xen_guest.py => functional/test_x86_64_kvm_xen.py} (61%) mode change 100644 => 100755 diff --git a/MAINTAINERS b/MAINTAINERS index 846b81e3ec..94834b7876 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -489,7 +489,7 @@ S: Supported F: include/system/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* -F: tests/avocado/kvm_xen_guest.py +F: tests/functional/test_x86_64_kvm_xen.py Guest CPU Cores (other accelerators) ------------------------------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index cf80924ddc..b7719ab85f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -44,6 +44,7 @@ test_timeouts = { 'riscv64_tuxrun' : 120, 's390x_ccw_virtio' : 420, 'sh4_tuxrun' : 240, + 'x86_64_kvm_xen' : 180, } tests_generic_system = [ @@ -244,6 +245,7 @@ tests_x86_64_system_thorough = [ 'netdev_ethtool', 'virtio_gpu', 'x86_64_hotplug_cpu', + 'x86_64_kvm_xen', 'x86_64_tuxrun', ] diff --git a/tests/avocado/kvm_xen_guest.py b/tests/functional/test_x86_64_kvm_xen.py old mode 100644 new mode 100755 similarity index 61% rename from tests/avocado/kvm_xen_guest.py rename to tests/functional/test_x86_64_kvm_xen.py index f8cb458d5d..0298c96c2e --- a/tests/avocado/kvm_xen_guest.py +++ b/tests/functional/test_x86_64_kvm_xen.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # KVM Xen guest functional tests # # Copyright © 2021 Red Hat, Inc. @@ -13,19 +15,12 @@ import os from qemu.machine import machine -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern -class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - :avocado: tags=kvm_xen_guest - """ +class KVMXenGuest(QemuSystemTest): - KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0' + KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0 quiet' kernel_path = None kernel_params = None @@ -33,14 +28,15 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): # Fetch assets from the kvm-xen-guest subdir of my shared test # images directory on fileserver.linaro.org where you can find # build instructions for how they where assembled. - def get_asset(self, name, sha1): - base_url = ('https://fileserver.linaro.org/s/' - 'kE4nCFLdQcoBF9t/download?' - 'path=%2Fkvm-xen-guest&files=' ) - url = base_url + name - # use explicit name rather than failing to neatly parse the - # URL into a unique one - return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=bzImage'), + 'ec0ad7bb8c33c5982baee0a75505fe7dbf29d3ff5d44258204d6307c6fe0132a') + + ASSET_ROOTFS = Asset( + ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=rootfs.ext4'), + 'b11045d649006c649c184e93339aaa41a8fe20a1a86620af70323252eb29e40b') def common_vm_setup(self): # We also catch lack of KVM_XEN support if we fail to launch @@ -51,10 +47,8 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.vm.add_args("-accel", "kvm,xen-version=0x4000a,kernel-irqchip=split") self.vm.add_args("-smp", "2") - self.kernel_path = self.get_asset("bzImage", - "367962983d0d32109998a70b45dcee4672d0b045") - self.rootfs = self.get_asset("rootfs.ext4", - "f1478401ea4b3fa2ea196396be44315bab2bb5e4") + self.kernel_path = self.ASSET_KERNEL.fetch() + self.rootfs = self.ASSET_ROOTFS.fetch() def run_and_check(self): self.vm.add_args('-kernel', self.kernel_path, @@ -68,10 +62,10 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.vm.launch() except machine.VMLaunchFailure as e: if "Xen HVM guest support not present" in e.output: - self.cancel("KVM Xen support is not present " - "(need v5.12+ kernel with CONFIG_KVM_XEN)") + self.skipTest("KVM Xen support is not present " + "(need v5.12+ kernel with CONFIG_KVM_XEN)") elif "Property 'kvm-accel.xen-version' not found" in e.output: - self.cancel("QEMU not built with CONFIG_XEN_EMU support") + self.skipTest("QEMU not built with CONFIG_XEN_EMU support") else: raise e @@ -79,10 +73,11 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): console_pattern = 'Starting dropbear sshd: OK' wait_for_console_pattern(self, console_pattern, 'Oops') self.log.info('sshd ready') - self.ssh_connect('root', '', False) - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dmesg | grep -e "Grant table initialized"') + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', 'xen') + exec_command_and_wait_for_pattern(self, 'dmesg | grep "Grant table"', + 'Grant table initialized') + wait_for_console_pattern(self, '#', 'Oops') def test_kvm_xen_guest(self): """ @@ -94,7 +89,9 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks') self.run_and_check() - self.ssh_command('grep xen-pirq.*msi /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq.*msi /proc/interrupts', + 'virtio0-output') def test_kvm_xen_guest_nomsi(self): """ @@ -106,7 +103,9 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks pci=nomsi') self.run_and_check() - self.ssh_command('grep xen-pirq.* /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq.* /proc/interrupts', + 'virtio0') def test_kvm_xen_guest_noapic_nomsi(self): """ @@ -118,7 +117,9 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks noapic pci=nomsi') self.run_and_check() - self.ssh_command('grep xen-pirq /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq /proc/interrupts', + 'virtio0') def test_kvm_xen_guest_vapic(self): """ @@ -130,8 +131,13 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks') self.run_and_check() - self.ssh_command('grep xen-pirq /proc/interrupts') - self.ssh_command('grep PCI-MSI /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq /proc/interrupts', + 'acpi') + wait_for_console_pattern(self, '#') + exec_command_and_wait_for_pattern(self, + 'grep PCI-MSI /proc/interrupts', + 'virtio0-output') def test_kvm_xen_guest_novector(self): """ @@ -143,7 +149,9 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): ' xen_emul_unplug=ide-disks' + ' xen_no_vector_callback') self.run_and_check() - self.ssh_command('grep xen-platform-pci /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'fasteoi') def test_kvm_xen_guest_novector_nomsi(self): """ @@ -156,7 +164,9 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): ' xen_emul_unplug=ide-disks pci=nomsi' + ' xen_no_vector_callback') self.run_and_check() - self.ssh_command('grep xen-platform-pci /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'IO-APIC') def test_kvm_xen_guest_novector_noapic(self): """ @@ -168,4 +178,9 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): ' xen_emul_unplug=ide-disks' + ' xen_no_vector_callback noapic') self.run_and_check() - self.ssh_command('grep xen-platform-pci /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'XT-PIC') + +if __name__ == '__main__': + QemuSystemTest.main() From b94893ab0f68c08c9d4203c119c823da27b5ff04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 6 Jan 2025 06:50:24 +0100 Subject: [PATCH 1228/2892] MAINTAINERS: Remove myself as Avocado Framework reviewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While I was very enthusiastic when Avocado was presented to the QEMU community and pushed forward to have it integrated, time passed and I lost interest. Be honest, remove my R: tag to not give fake expectation I'd review patches related to Avocado anymore. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20250106055024.70139-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 94834b7876..7be3d8f431 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4207,7 +4207,6 @@ F: tests/tcg/Makefile.target Integration Testing with the Avocado framework W: https://trello.com/b/6Qi1pxVn/avocado-qemu R: Cleber Rosa -R: Philippe Mathieu-Daudé S: Odd Fixes F: tests/avocado/ From 145f12ea885c8fcfbe2d0ac5230630f071b5a9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 9 Jan 2025 09:37:46 +0000 Subject: [PATCH 1229/2892] crypto: fix bogus error benchmarking pbkdf on fast machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're seeing periodic reports of errors like: $ qemu-img create -f luks --object secret,data=123456,id=sec0 \ -o key-secret=sec0 luks-info.img 1M Formatting 'luks-info.img', fmt=luks size=1048576 key-secret=sec0 qemu-img: luks-info.img: Unable to get accurate CPU usage This error message comes from a recent attempt to workaround a kernel bug with measuring rusage in long running processes: commit c72cab5ad9f849bbcfcf4be7952b8b8946cc626e Author: Tiago Pasqualini Date: Wed Sep 4 20:52:30 2024 -0300 crypto: run qcrypto_pbkdf2_count_iters in a new thread Unfortunately this has a subtle bug on machines which are very fast. On the first time around the loop, the 'iterations' value is quite small (1 << 15), and so will run quite fast. Testing has shown that some machines can complete this benchmarking task in as little as 7 milliseconds. Unfortunately the 'getrusage' data is not updated at the time of the 'getrusage' call, it is done asynchronously by the scheduler. The 7 millisecond completion time for the benchmark is short enough that 'getrusage' sometimes reports 0 accumulated execution time. As a result the 'delay_ms == 0' sanity check in the above commit is triggering non-deterministically on such machines. The benchmarking loop intended to run multiple times, increasing the 'iterations' value until the benchmark ran for > 500 ms, but the sanity check doesn't allow this to happen. To fix it, we keep a loop counter and only run the sanity check after we've been around the loop more than 5 times. At that point the 'iterations' value is high enough that even with infrequent updates of 'getrusage' accounting data on fast machines, we should see a non-zero value. Fixes: https://lore.kernel.org/qemu-devel/ffe542bb-310c-4616-b0ca-13182f849fd1@redhat.com/ Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2336437 Reported-by: Thomas Huth Reported-by: Richard W.M. Jones Tested-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20250109093746.1216300-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- crypto/pbkdf.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crypto/pbkdf.c b/crypto/pbkdf.c index 0dd7c3aeaa..2989fc0a40 100644 --- a/crypto/pbkdf.c +++ b/crypto/pbkdf.c @@ -107,7 +107,7 @@ static void *threaded_qcrypto_pbkdf2_count_iters(void *data) size_t nsalt = iters_data->nsalt; size_t nout = iters_data->nout; Error **errp = iters_data->errp; - + size_t scaled = 0; uint64_t ret = -1; g_autofree uint8_t *out = g_new(uint8_t, nout); uint64_t iterations = (1 << 15); @@ -131,7 +131,17 @@ static void *threaded_qcrypto_pbkdf2_count_iters(void *data) delta_ms = end_ms - start_ms; - if (delta_ms == 0) { /* sanity check */ + /* + * For very small 'iterations' values, CPU (or crypto + * accelerator) might be fast enough that the scheduler + * hasn't incremented getrusage() data, or incremented + * it by a very small amount, resulting in delta_ms == 0. + * Once we've scaled 'iterations' x10, 5 times, we really + * should be seeing delta_ms != 0, so sanity check at + * that point. + */ + if (scaled > 5 && + delta_ms == 0) { /* sanity check */ error_setg(errp, "Unable to get accurate CPU usage"); goto cleanup; } else if (delta_ms > 500) { @@ -141,6 +151,7 @@ static void *threaded_qcrypto_pbkdf2_count_iters(void *data) } else { iterations = (iterations * 1000 / delta_ms); } + scaled++; } iterations = iterations * 1000 / delta_ms; From 807830e809bc684dc3344e78dd32f0cb6e5c6c37 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 20 Jan 2025 08:07:05 +0100 Subject: [PATCH 1230/2892] hw/s390x: Fix crash that occurs when inspecting older versioned machines types qemu-system-s390x currently crashes when trying to inspect older machines types, for example: $ echo '{ "execute": "qmp_capabilities" } { "execute": "qom-list-properties","arguments": { "typename": "s390-ccw-virtio-3.0-machine"}}' \ | ./qemu-system-s390x -qmp stdio -no-shutdown {"QMP": {"version": {"qemu": {"micro": 50, "minor": 2, "major": 9}, "package": "v9.2.0-1071-g81e97df3e7"}, "capabilities": ["oob"]}} {"return": {}} ** Bail out! ERROR:../target/s390x/cpu_models.c:832:s390_set_qemu_cpu_model: assertion failed: (QTAILQ_EMPTY_RCU(&cpus_queue)) Aborted (core dumped) The problem is that the versioned s390-ccw-virtio machine types use instance_init() to set global state that should be initialized before the CPUs get instantiated. But instance_init() is not called only for the machine that is finally used, it is also called for temporary instances of objects that are e.g. just created for introspection. That means that those instance_init() functions can also be called while a machine (and its CPUs) is already created, which triggers the assertion in cpu_models.c. So we must not use instance_init() for setting global state, but use the machine->init() function instead, which is really only called once when the machine comes to life. Fixes: 3b00f702c2 ("s390x/cpumodel: add zpci, aen and ais facilities") Message-ID: <20250120085059.239345-1-thuth@redhat.com> Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 38aeba14ee..3af613d4e9 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -782,7 +782,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; - mc->init = ccw_init; mc->reset = s390_machine_reset; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; @@ -852,6 +851,12 @@ static const TypeInfo ccw_machine_info = { }; #define DEFINE_CCW_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(mach_init, ccw, __VA_ARGS__)(MachineState *mach) \ + { \ + current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(mach)); \ + MACHINE_VER_SYM(instance_options, ccw, __VA_ARGS__)(mach); \ + ccw_init(mach); \ + } \ static void MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__)( \ ObjectClass *oc, \ void *data) \ @@ -859,24 +864,18 @@ static const TypeInfo ccw_machine_info = { MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(class_options, ccw, __VA_ARGS__)(mc); \ mc->desc = "Virtual s390x machine (version " MACHINE_VER_STR(__VA_ARGS__) ")"; \ + mc->init = MACHINE_VER_SYM(mach_init, ccw, __VA_ARGS__); \ MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "s390-ccw-virtio"; \ mc->is_default = true; \ } \ } \ - static void MACHINE_VER_SYM(instance_init, ccw, __VA_ARGS__)(Object *obj) \ - { \ - MachineState *machine = MACHINE(obj); \ - current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ - MACHINE_VER_SYM(instance_options, ccw, __VA_ARGS__)(machine); \ - } \ static const TypeInfo MACHINE_VER_SYM(info, ccw, __VA_ARGS__) = \ { \ .name = MACHINE_VER_TYPE_NAME("s390-ccw-virtio", __VA_ARGS__), \ .parent = TYPE_S390_CCW_MACHINE, \ .class_init = MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__), \ - .instance_init = MACHINE_VER_SYM(instance_init, ccw, __VA_ARGS__), \ }; \ static void MACHINE_VER_SYM(register, ccw, __VA_ARGS__)(void) \ { \ From 3936d0556383829b8db9518aed8badfed6513953 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 Jan 2025 12:58:24 +0100 Subject: [PATCH 1231/2892] pc-bios/s390-ccw/virtio: Add a function to reset a virtio device To be able to properly silence a virtio device after using it, we need a global function to reset the device. Reviewed-by: Jared Rossi Reviewed-by: Eric Farman Tested-by: Jared Rossi Message-ID: <20250116115826.192047-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/virtio.c | 7 ++++++- pc-bios/s390-ccw/virtio.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 8b5a370bb3..cd6c99c7e3 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -217,6 +217,11 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) return 0; } +int virtio_reset(VDev *vdev) +{ + return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); +} + int virtio_setup_ccw(VDev *vdev) { int i, cfg_size = 0; @@ -235,7 +240,7 @@ int virtio_setup_ccw(VDev *vdev) vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ vdev->guessed_disk_nature = VIRTIO_GDN_NONE; - run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); + virtio_reset(vdev); status = VIRTIO_CONFIG_S_ACKNOWLEDGE; if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 9faf3986b1..f13fa6f5fe 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -274,6 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags); int vr_poll(VRing *vr); int vring_wait_reply(void); int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd); +int virtio_reset(VDev *vdev); int virtio_setup_ccw(VDev *vdev); int virtio_net_init(void *mac_addr); From 68c95ed1db070f7545e487e742715f01a545aab0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 Jan 2025 12:58:25 +0100 Subject: [PATCH 1232/2892] pc-bios/s390-ccw: Fix boot problem with virtio-net devices When we are trying to boot from virtio-net devices, the s390-ccw bios currently leaves the virtio-net device enabled after using it. That means that the receiving virt queues will continue to happily write incoming network packets into memory. This can corrupt data of the following boot process. For example, if you set up a second guest on a virtual network and create a lot of broadcast traffic there, e.g. with: ping -i 0.02 -s 1400 -b 192.168.1.255 and then you try to boot a guest with two boot devices, a network device first (which should not be bootable) and e.g. a bootable SCSI CD second, then this guest will fail to load the kernel from the CD image: $ qemu-system-s390x -m 2G -nographic -device virtio-scsi-ccw \ -netdev tap,id=net0 -device virtio-net-ccw,netdev=net0,bootindex=1 \ -drive if=none,file=test.iso,format=raw,id=cd1 \ -device scsi-cd,drive=cd1,bootindex=2 LOADPARM=[ ] Network boot device detected Network boot starting... Using MAC address: 52:54:00:12:34:56 Requesting information via DHCP: done Using IPv4 address: 192.168.1.76 Using TFTP server: 192.168.1.1 Trying pxelinux.cfg files... TFTP error: ICMP ERROR "port unreachable" Receiving data: 0 KBytes Repeating TFTP read request... TFTP error: ICMP ERROR "port unreachable" Failed to load OS from network. Failed to IPL from this network! LOADPARM=[ ] Using virtio-scsi. ! virtio-scsi:setup:inquiry: response VS RESP=ff ! ERROR: No suitable device for IPL. Halting... We really have to shut up the virtio-net devices after we're not using it anymore. The easiest way to do this is to simply reset the device, so let's do that now. Reviewed-by: Jared Rossi Reviewed-by: Eric Farman Tested-by: Jared Rossi Message-ID: <20250116115826.192047-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netmain.c | 33 +++++++++++++++++++++++---------- pc-bios/s390-ccw/virtio-net.c | 5 +++++ pc-bios/s390-ccw/virtio.h | 1 + 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index e46e470db4..335ea9b63e 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -153,19 +153,10 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) return rc; } -static int net_init(filename_ip_t *fn_ip) +static int net_init_ip(filename_ip_t *fn_ip) { int rc; - memset(fn_ip, 0, sizeof(filename_ip_t)); - - rc = virtio_net_init(mac); - if (rc < 0) { - puts("Could not initialize network device"); - return -101; - } - fn_ip->fd = rc; - printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -221,11 +212,33 @@ static int net_init(filename_ip_t *fn_ip) return rc; } +static int net_init(filename_ip_t *fn_ip) +{ + int rc; + + memset(fn_ip, 0, sizeof(filename_ip_t)); + + rc = virtio_net_init(mac); + if (rc < 0) { + puts("Could not initialize network device"); + return -101; + } + fn_ip->fd = rc; + + rc = net_init_ip(fn_ip); + if (rc < 0) { + virtio_net_deinit(); + } + + return rc; +} + static void net_release(filename_ip_t *fn_ip) { if (fn_ip->ip_version == 4) { dhcp_send_release(fn_ip->fd); } + virtio_net_deinit(); } /** diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c index 578c89d0c5..301445bf97 100644 --- a/pc-bios/s390-ccw/virtio-net.c +++ b/pc-bios/s390-ccw/virtio-net.c @@ -140,3 +140,8 @@ int recv(int fd, void *buf, int maxlen, int flags) return len; } + +void virtio_net_deinit(void) +{ + virtio_reset(virtio_get_device()); +} diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index f13fa6f5fe..5c5e808a50 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -278,5 +278,6 @@ int virtio_reset(VDev *vdev); int virtio_setup_ccw(VDev *vdev); int virtio_net_init(void *mac_addr); +void virtio_net_deinit(void); #endif /* VIRTIO_H */ From bbfa7f8558d5346b6884108ad50df3517fe17358 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 Jan 2025 12:58:26 +0100 Subject: [PATCH 1233/2892] pc-bios/s390-ccw/netmain: Fix error messages with regards to the TFTP server The code in net_init_ip() currently bails out early if "rc" is less than 0, so the if-statements that check for negative "rc" codes to print out some specific error messages with regards to the TFTP server are never reached. Move them earlier to bring that dead code back to life. Reviewed-by: Jared Rossi Reviewed-by: Eric Farman Tested-by: Jared Rossi Message-ID: <20250116115826.192047-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netmain.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 335ea9b63e..719a547ada 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -168,6 +168,14 @@ static int net_init_ip(filename_ip_t *fn_ip) if (fn_ip->ip_version == 4) { set_ipv4_address(fn_ip->own_ip); } + } else if (rc == -2) { + printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); + return -102; + } else if (rc == -4 || rc == -3) { + puts("Can't obtain TFTP server IP address"); + return -107; } else { puts("Could not get IP address"); return -101; @@ -183,17 +191,6 @@ static int net_init_ip(filename_ip_t *fn_ip) printf(" Using IPv6 address: %s\n", ip6_str); } - if (rc == -2) { - printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", - (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, - (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); - return -102; - } - if (rc == -4 || rc == -3) { - puts("Can't obtain TFTP server IP address"); - return -107; - } - printf(" Using TFTP server: "); if (fn_ip->ip_version == 4) { printf("%d.%d.%d.%d\n", From 64fa0de46ee3cc972af5d3ce8c5dc0db8198cd2b Mon Sep 17 00:00:00 2001 From: Jared Rossi Date: Fri, 17 Jan 2025 16:22:35 -0500 Subject: [PATCH 1234/2892] pc-bios/s390-ccw: Abort IPL on invalid loadparm Because the loadparm specifies an exact kernel the user wants to boot, if the loadparm is invalid it must represent a misconfiguration of the guest. Thus we should abort the IPL immediately, without attempting to use other devices, to avoid booting into an unintended guest image. Signed-off-by: Jared Rossi Message-ID: <20250117212235.1324063-2-jrossi@linux.ibm.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 56f2f75640..0f8baa0198 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -336,8 +336,7 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr, debug_print_int("loadparm", loadparm); if (loadparm >= MAX_BOOT_ENTRIES) { - puts("loadparm value greater than max number of boot entries allowed"); - return -EINVAL; + panic("loadparm value greater than max number of boot entries allowed"); } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -348,8 +347,8 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr, block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl); if (block_nr == NULL_BLOCK_NR) { - puts("Cannot find Boot Map Table Entry"); - return -EIO; + printf("The requested boot entry (%d) is invalid\n", loadparm); + panic("Invalid loadparm"); } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -792,8 +791,12 @@ static int ipl_scsi(void) debug_print_int("loadparm", loadparm); if (loadparm >= MAX_BOOT_ENTRIES) { - puts("loadparm value greater than max number of boot entries allowed"); - return -EINVAL; + panic("loadparm value greater than max number of boot entries allowed"); + } + + if (!valid_entries[loadparm]) { + printf("The requested boot entry (%d) is invalid\n", loadparm); + panic("Invalid loadparm"); } return zipl_run(&prog_table->entry[loadparm].scsi); From 9744ceb94bf963ed42685c7d28024a77de2fbe78 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 20 Jan 2025 16:28:28 +0100 Subject: [PATCH 1235/2892] pc-bios: Update the s390 bios images with the recent changes Fix the problem with the non-quiesced virtio-net device and make sure to abort the boot process if the user specified a wrong loadparm parameter. Signed-off-by: Thomas Huth --- pc-bios/s390-ccw.img | Bin 79608 -> 96000 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index 0cbedf0fa65fb87a9e19b3bcf0c4b81002801b5c..47240f0a74eec17b0da3ad334157d1b0e62632b4 100644 GIT binary patch literal 96000 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 Dgs*5J literal 79608 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%0AH+Q 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 Date: Fri, 17 Jan 2025 13:29:17 +0100 Subject: [PATCH 1236/2892] rust: pl011: fix repr(C) for PL011Class Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 994c2fc059..65a1234b9f 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -123,6 +123,7 @@ pub struct PL011State { qom_isa!(PL011State : SysBusDevice, DeviceState, Object); +#[repr(C)] pub struct PL011Class { parent_class: ::Class, /// The byte string that identifies the device. From 6ace2d5163bbc0b38d9982e04f3a4199c5fef315 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:00 +0100 Subject: [PATCH 1237/2892] target/i386: inline gen_jcc into sole caller The code of gen_Jcc is very similar to gen_LOOP* and gen_JCXZ, but this is hidden by gen_jcc. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-2-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 5 ++++- target/i386/tcg/translate.c | 8 -------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index c4cc5f48d8..a193d32ca7 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2297,8 +2297,11 @@ static void gen_IRET(DisasContext *s, X86DecodedInsn *decode) static void gen_Jcc(DisasContext *s, X86DecodedInsn *decode) { + TCGLabel *taken = gen_new_label(); + gen_bnd_jmp(s); - gen_jcc(s, decode->b & 0xf, decode->immediate); + gen_jcc1(s, decode->b & 0xf, taken); + gen_conditional_jump_labels(s, decode->immediate, NULL, taken); } static void gen_JCXZ(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index dbc9d637c4..3b68441a56 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1847,14 +1847,6 @@ static void gen_conditional_jump_labels(DisasContext *s, target_long diff, gen_jmp_rel(s, s->dflag, diff, 0); } -static void gen_jcc(DisasContext *s, int b, int diff) -{ - TCGLabel *l1 = gen_new_label(); - - gen_jcc1(s, b, l1); - gen_conditional_jump_labels(s, diff, NULL, l1); -} - static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) { CCPrepare cc = gen_prepare_cc(s, b, NULL); From e604be4fb4ed1abe5286f8f4145701bf3fc15b97 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:01 +0100 Subject: [PATCH 1238/2892] target/i386: remove trailing 1 from gen_{j, cmov, set}cc1 This is not needed anymore now that gen_jcc has been eliminated (merged into the similarly-named gen_Jcc, where the uppercase letter gives away that it is an emission function). Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-3-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 10 +++++----- target/i386/tcg/translate.c | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index a193d32ca7..861f0fb70f 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1634,7 +1634,7 @@ static void gen_CMC(DisasContext *s, X86DecodedInsn *decode) static void gen_CMOVcc(DisasContext *s, X86DecodedInsn *decode) { - gen_cmovcc1(s, decode->b & 0xf, s->T0, s->T1); + gen_cmovcc(s, decode->b & 0xf, s->T0, s->T1); } static void gen_CMPccXADD(DisasContext *s, X86DecodedInsn *decode) @@ -2300,7 +2300,7 @@ static void gen_Jcc(DisasContext *s, X86DecodedInsn *decode) TCGLabel *taken = gen_new_label(); gen_bnd_jmp(s); - gen_jcc1(s, decode->b & 0xf, taken); + gen_jcc(s, decode->b & 0xf, taken); gen_conditional_jump_labels(s, decode->immediate, NULL, taken); } @@ -2451,7 +2451,7 @@ static void gen_LOOPE(DisasContext *s, X86DecodedInsn *decode) gen_update_cc_op(s); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); gen_op_jz_ecx(s, not_taken); - gen_jcc1(s, (JCC_Z << 1), taken); /* jz taken */ + gen_jcc(s, (JCC_Z << 1), taken); /* jz taken */ gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); } @@ -2463,7 +2463,7 @@ static void gen_LOOPNE(DisasContext *s, X86DecodedInsn *decode) gen_update_cc_op(s); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); gen_op_jz_ecx(s, not_taken); - gen_jcc1(s, (JCC_Z << 1) | 1, taken); /* jnz taken */ + gen_jcc(s, (JCC_Z << 1) | 1, taken); /* jnz taken */ gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); } @@ -3888,7 +3888,7 @@ static void gen_SCAS(DisasContext *s, X86DecodedInsn *decode) static void gen_SETcc(DisasContext *s, X86DecodedInsn *decode) { - gen_setcc1(s, decode->b & 0xf, s->T0); + gen_setcc(s, decode->b & 0xf, s->T0); } static void gen_SFENCE(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3b68441a56..a2101b5615 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1148,7 +1148,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) return cc; } -static void gen_setcc1(DisasContext *s, int b, TCGv reg) +static void gen_setcc(DisasContext *s, int b, TCGv reg) { CCPrepare cc = gen_prepare_cc(s, b, reg); @@ -1170,12 +1170,12 @@ static void gen_setcc1(DisasContext *s, int b, TCGv reg) static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) { - gen_setcc1(s, JCC_B << 1, reg); + gen_setcc(s, JCC_B << 1, reg); } /* generate a conditional jump to label 'l1' according to jump opcode value 'b'. In the fast case, T0 is guaranteed not to be used. */ -static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) +static inline void gen_jcc_noeob(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, NULL); @@ -1190,7 +1190,7 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) value 'b'. In the fast case, T0 is guaranteed not to be used. One or both of the branches will call gen_jmp_rel, so ensure cc_op is clean. */ -static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) +static inline void gen_jcc(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, NULL); @@ -1337,7 +1337,7 @@ static void gen_repz_nz(DisasContext *s, MemOp ot, l2 = gen_jz_ecx_string(s); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); + gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), l2); if (s->repz_opt) { gen_op_jz_ecx(s, l2); } @@ -1847,7 +1847,7 @@ static void gen_conditional_jump_labels(DisasContext *s, target_long diff, gen_jmp_rel(s, s->dflag, diff, 0); } -static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) +static void gen_cmovcc(DisasContext *s, int b, TCGv dest, TCGv src) { CCPrepare cc = gen_prepare_cc(s, b, NULL); @@ -2856,7 +2856,7 @@ static void gen_x87(DisasContext *s, X86DecodedInsn *decode) } op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); l1 = gen_new_label(); - gen_jcc1_noeob(s, op1, l1); + gen_jcc_noeob(s, op1, l1); gen_helper_fmov_ST0_STN(tcg_env, tcg_constant_i32(opreg)); gen_set_label(l1); From b519556f58dcb548f295c5cbbf91617377c5c564 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:02 +0100 Subject: [PATCH 1239/2892] target/i386: unify REP and REPZ/REPNZ generation It only differs in a single call to gen_jcc, so use a "bool" argument to distinguish the two cases; do not duplicate code. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-4-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index a2101b5615..877b584611 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1310,14 +1310,18 @@ static void gen_outs(DisasContext *s, MemOp ot) gen_bpt_io(s, s->tmp2_i32, ot); } -/* Generate jumps to current or next instruction */ -static void gen_repz(DisasContext *s, MemOp ot, - void (*fn)(DisasContext *s, MemOp ot)) +static void do_gen_rep(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot), + bool is_repz_nz) { TCGLabel *l2; l2 = gen_jz_ecx_string(s); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + if (is_repz_nz) { + int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; + gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), l2); + } /* * A loop would cause two single step exceptions if ECX = 1 * before rep string_insn @@ -1325,28 +1329,25 @@ static void gen_repz(DisasContext *s, MemOp ot, if (s->repz_opt) { gen_op_jz_ecx(s, l2); } + /* + * For CMPS/SCAS there is no need to set CC_OP_DYNAMIC: only one iteration + * is done at a time, so the translation block ends unconditionally after + * this instruction and there is no control flow junction. + */ gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } +static void gen_repz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot)) + +{ + do_gen_rep(s, ot, fn, false); +} + static void gen_repz_nz(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot)) { - TCGLabel *l2; - int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; - - l2 = gen_jz_ecx_string(s); - fn(s, ot); - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), l2); - if (s->repz_opt) { - gen_op_jz_ecx(s, l2); - } - /* - * Only one iteration is done at a time, so the translation - * block ends unconditionally after this instruction and there - * is no control flow junction - no need to set CC_OP_DYNAMIC. - */ - gen_jmp_rel_csize(s, -cur_insn_len(s), 0); + do_gen_rep(s, ot, fn, true); } static void gen_helper_fp_arith_ST0_FT0(int op) From d8d552d4591257368633831953a190b868e5f566 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:03 +0100 Subject: [PATCH 1240/2892] target/i386: unify choice between single and repeated string instructions The same "if" is present in all generator functions for string instructions. Push it inside gen_repz() and gen_repz_nz() instead. Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241215090613.89588-5-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 42 +++++++------------------------------ target/i386/tcg/translate.c | 12 +++++++++-- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 861f0fb70f..3a28b0cb31 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1743,11 +1743,7 @@ static void gen_CMPccXADD(DisasContext *s, X86DecodedInsn *decode) static void gen_CMPS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_nz(s, ot, gen_cmps); - } else { - gen_cmps(s, ot); - } + gen_repz_nz(s, ot, gen_cmps); } static void gen_CMPXCHG(DisasContext *s, X86DecodedInsn *decode) @@ -2238,11 +2234,7 @@ static void gen_INS(DisasContext *s, X86DecodedInsn *decode) } translator_io_start(&s->base); - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz(s, ot, gen_ins); - } else { - gen_ins(s, ot); - } + gen_repz(s, ot, gen_ins); } static void gen_INSERTQ_i(DisasContext *s, X86DecodedInsn *decode) @@ -2426,11 +2418,7 @@ static void gen_LGS(DisasContext *s, X86DecodedInsn *decode) static void gen_LODS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz(s, ot, gen_lods); - } else { - gen_lods(s, ot); - } + gen_repz(s, ot, gen_lods); } static void gen_LOOP(DisasContext *s, X86DecodedInsn *decode) @@ -2628,11 +2616,7 @@ static void gen_MOVq_dq(DisasContext *s, X86DecodedInsn *decode) static void gen_MOVS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz(s, ot, gen_movs); - } else { - gen_movs(s, ot); - } + gen_repz(s, ot, gen_movs); } static void gen_MUL(DisasContext *s, X86DecodedInsn *decode) @@ -2794,11 +2778,7 @@ static void gen_OUTS(DisasContext *s, X86DecodedInsn *decode) } translator_io_start(&s->base); - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz(s, ot, gen_outs); - } else { - gen_outs(s, ot); - } + gen_repz(s, ot, gen_outs); } static void gen_PALIGNR(DisasContext *s, X86DecodedInsn *decode) @@ -3879,11 +3859,7 @@ static void gen_SBB(DisasContext *s, X86DecodedInsn *decode) static void gen_SCAS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_nz(s, ot, gen_scas); - } else { - gen_scas(s, ot); - } + gen_repz_nz(s, ot, gen_scas); } static void gen_SETcc(DisasContext *s, X86DecodedInsn *decode) @@ -4089,11 +4065,7 @@ static void gen_STMXCSR(DisasContext *s, X86DecodedInsn *decode) static void gen_STOS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz(s, ot, gen_stos); - } else { - gen_stos(s, ot); - } + gen_repz(s, ot, gen_stos); } static void gen_SUB(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 877b584611..3e46be8d78 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1341,13 +1341,21 @@ static void gen_repz(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot)) { - do_gen_rep(s, ot, fn, false); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + do_gen_rep(s, ot, fn, false); + } else { + fn(s, ot); + } } static void gen_repz_nz(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot)) { - do_gen_rep(s, ot, fn, true); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + do_gen_rep(s, ot, fn, true); + } else { + fn(s, ot); + } } static void gen_helper_fp_arith_ST0_FT0(int op) From 0eb7046e1bbe83468169a74b1886fa9c2605ffa7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:04 +0100 Subject: [PATCH 1241/2892] target/i386: reorganize ops emitted by do_gen_rep, drop repz_opt The condition for optimizing repeat instruction is more or less the opposite of what you imagine: almost always the string instruction was _not_ optimized and optimizing the loop relied on goto_tb. This is obviously not great for performance, due to the cost of the exit-to-main-loop check, but also wrong. In fact, after expanding dc->jmp_opt and simplifying "!!x" to "x", the condition for looping used to be: ((cflags & CF_NO_GOTO_TB) || (flags & (HF_RF_MASK | HF_TF_MASK | HF_INHIBIT_IRQ_MASK))) && !(cflags & CF_USE_ICOUNT) In other words, setting aside RF (it requires special handling for REP instructions and it was completely missing), repeat instruction were being optimized if TF or inhibit IRQ flags were set. This is certainly wrong for TF, because string instructions trap after every execution, and probably for interrupt shadow too. Get rid of repz_opt completely. The next patches will reintroduce the optimization, applying it in the common case instead of the unlikely and wrong one. While at it, place the CX/ECX/RCX=0 case is at the end of the function, which saves a label and is clearer when reading the generated ops. For clarity, mark the cc_op explicitly as DYNAMIC even if at the end of the translation block; the cc_op can come from either the previous instruction or the string instruction, and currently we rely on a gen_update_cc_op() that is hidden in the bowels of gen_jcc() to spill cc_op and mark it clean. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-6-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 60 ++++++++----------------------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3e46be8d78..ee53623439 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -113,7 +113,6 @@ typedef struct DisasContext { #endif bool vex_w; /* used by AVX even on 32-bit processors */ bool jmp_opt; /* use direct block chaining for direct jumps */ - bool repz_opt; /* optimize jumps within repz instructions */ bool cc_op_dirty; CCOp cc_op; /* current CC operation */ @@ -1206,23 +1205,6 @@ static inline void gen_jcc(DisasContext *s, int b, TCGLabel *l1) } } -/* XXX: does not work with gdbstub "ice" single step - not a - serious problem. The caller can jump to the returned label - to stop the REP but, if the flags have changed, it has to call - gen_update_cc_op before doing so. */ -static TCGLabel *gen_jz_ecx_string(DisasContext *s) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - - gen_update_cc_op(s); - gen_op_jnz_ecx(s, l1); - gen_set_label(l2); - gen_jmp_rel_csize(s, 0, 1); - gen_set_label(l1); - return l2; -} - static void gen_stos(DisasContext *s, MemOp ot) { gen_string_movl_A0_EDI(s); @@ -1314,27 +1296,25 @@ static void do_gen_rep(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot), bool is_repz_nz) { - TCGLabel *l2; - l2 = gen_jz_ecx_string(s); + TCGLabel *done = gen_new_label(); + + gen_update_cc_op(s); + gen_op_jz_ecx(s, done); + fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); if (is_repz_nz) { int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; - gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), l2); + gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), done); } - /* - * A loop would cause two single step exceptions if ECX = 1 - * before rep string_insn - */ - if (s->repz_opt) { - gen_op_jz_ecx(s, l2); - } - /* - * For CMPS/SCAS there is no need to set CC_OP_DYNAMIC: only one iteration - * is done at a time, so the translation block ends unconditionally after - * this instruction and there is no control flow junction. - */ + + /* Go to the main loop but reenter the same instruction. */ gen_jmp_rel_csize(s, -cur_insn_len(s), 0); + + /* CX/ECX/RCX is zero, or REPZ/REPNZ broke the repetition. */ + gen_set_label(done); + set_cc_op(s, CC_OP_DYNAMIC); + gen_jmp_rel_csize(s, 0, 1); } static void gen_repz(DisasContext *s, MemOp ot, @@ -3665,20 +3645,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; dc->jmp_opt = !((cflags & CF_NO_GOTO_TB) || (flags & (HF_RF_MASK | HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); - /* - * If jmp_opt, we want to handle each string instruction individually. - * For icount also disable repz optimization so that each iteration - * is accounted separately. - * - * FIXME: this is messy; it makes REP string instructions a lot less - * efficient than they should be and it gets in the way of correct - * handling of RF (interrupts or traps arriving after any iteration - * of a repeated string instruction but the last should set RF to 1). - * Perhaps it would be more efficient if REP string instructions were - * always at the beginning of the TB, or even their own TB? That - * would even allow accounting up to 64k iterations at once for icount. - */ - dc->repz_opt = !dc->jmp_opt && !(cflags & CF_USE_ICOUNT); dc->T0 = tcg_temp_new(); dc->T1 = tcg_temp_new(); From 4d7704ebc59a1f52d6ab65e5fff8e3160c1f4d79 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:05 +0100 Subject: [PATCH 1242/2892] target/i386: tcg: move gen_set/reset_* earlier in the file Allow using them in the code that translates REP/REPZ, without forward declarations. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-7-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 80 ++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ee53623439..6347de446a 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -725,6 +725,46 @@ static inline void gen_op_jnz_ecx(DisasContext *s, TCGLabel *label1) gen_op_j_ecx(s, TCG_COND_NE, label1); } +static void gen_set_hflag(DisasContext *s, uint32_t mask) +{ + if ((s->flags & mask) == 0) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + tcg_gen_ori_i32(t, t, mask); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + s->flags |= mask; + } +} + +static void gen_reset_hflag(DisasContext *s, uint32_t mask) +{ + if (s->flags & mask) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + tcg_gen_andi_i32(t, t, ~mask); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + s->flags &= ~mask; + } +} + +static void gen_set_eflags(DisasContext *s, target_ulong mask) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); + tcg_gen_ori_tl(t, t, mask); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); +} + +static void gen_reset_eflags(DisasContext *s, target_ulong mask) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); + tcg_gen_andi_tl(t, t, ~mask); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); +} + static void gen_helper_in_func(MemOp ot, TCGv v, TCGv_i32 n) { switch (ot) { @@ -2084,46 +2124,6 @@ static void gen_interrupt(DisasContext *s, uint8_t intno) s->base.is_jmp = DISAS_NORETURN; } -static void gen_set_hflag(DisasContext *s, uint32_t mask) -{ - if ((s->flags & mask) == 0) { - TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); - tcg_gen_ori_i32(t, t, mask); - tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); - s->flags |= mask; - } -} - -static void gen_reset_hflag(DisasContext *s, uint32_t mask) -{ - if (s->flags & mask) { - TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); - tcg_gen_andi_i32(t, t, ~mask); - tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); - s->flags &= ~mask; - } -} - -static void gen_set_eflags(DisasContext *s, target_ulong mask) -{ - TCGv t = tcg_temp_new(); - - tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); - tcg_gen_ori_tl(t, t, mask); - tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); -} - -static void gen_reset_eflags(DisasContext *s, target_ulong mask) -{ - TCGv t = tcg_temp_new(); - - tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); - tcg_gen_andi_tl(t, t, ~mask); - tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); -} - /* Clear BND registers during legacy branches. */ static void gen_bnd_jmp(DisasContext *s) { From 0d82d9e84644ecee3e626bdf204e9847ffe10bce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:06 +0100 Subject: [PATCH 1243/2892] target/i386: fix RF handling for string instructions RF must be set on traps and interrupts from a string instruction, except if they occur after the last iteration. Ensure it is set before giving the main loop a chance to execute. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-8-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 6347de446a..141295742a 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1337,6 +1337,14 @@ static void do_gen_rep(DisasContext *s, MemOp ot, bool is_repz_nz) { TCGLabel *done = gen_new_label(); + bool had_rf = s->flags & HF_RF_MASK; + + /* + * Even if EFLAGS.RF was set on entry (such as if we're on the second or + * later iteration and an exception or interrupt happened), force gen_eob() + * not to clear the flag. We do that ourselves after the last iteration. + */ + s->flags &= ~HF_RF_MASK; gen_update_cc_op(s); gen_op_jz_ecx(s, done); @@ -1348,12 +1356,24 @@ static void do_gen_rep(DisasContext *s, MemOp ot, gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), done); } + /* + * Traps or interrupts set RF_MASK if they happen after any iteration + * but the last. Set it here before giving the main loop a chance to + * execute. (For faults, seg_helper.c sets the flag as usual). + */ + if (!had_rf) { + gen_set_eflags(s, RF_MASK); + } + /* Go to the main loop but reenter the same instruction. */ gen_jmp_rel_csize(s, -cur_insn_len(s), 0); /* CX/ECX/RCX is zero, or REPZ/REPNZ broke the repetition. */ gen_set_label(done); set_cc_op(s, CC_OP_DYNAMIC); + if (had_rf) { + gen_reset_eflags(s, RF_MASK); + } gen_jmp_rel_csize(s, 0, 1); } @@ -2158,7 +2178,7 @@ gen_eob(DisasContext *s, int mode) gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); } - if (s->base.tb->flags & HF_RF_MASK) { + if (s->flags & HF_RF_MASK) { gen_reset_eflags(s, RF_MASK); } if (mode == DISAS_EOB_RECHECK_TF) { From 6986cf003226ddf7e5af36a9f4f033cb16c8636c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:07 +0100 Subject: [PATCH 1244/2892] target/i386: make cc_op handling more explicit for repeated string instructions. Since the cost of gen_update_cc_op() must be paid anyway, it's easier to place them manually and not rely on spilling that is buried under multiple levels of function calls. While at it, clarify the circumstances in which the gen_update_cc_op() is needed, and why it is not for REPxx SCAS and REPxx CMPS. And since cc_op will have been spilled at the point of a fault, just make the whole insn CC_OP_DYNAMIC. Once repz_opt is reintroduced, a fault could happen either before or after the first execution of CMPS/SCAS, and CC_OP_DYNAMIC sidesteps the complicated matter of what x86_restore_state_to_opc would do. Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241215090613.89588-9-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 141295742a..8bc91c3de3 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1234,8 +1234,9 @@ static inline void gen_jcc(DisasContext *s, int b, TCGLabel *l1) CCPrepare cc = gen_prepare_cc(s, b, NULL); /* - * Note that this must be _after_ gen_prepare_cc, because it - * can change the cc_op from CC_OP_DYNAMIC to CC_OP_EFLAGS! + * Note that this must be _after_ gen_prepare_cc, because it can change + * the cc_op to CC_OP_EFLAGS (because it's CC_OP_DYNAMIC or because + * it's cheaper to just compute the flags)! */ gen_update_cc_op(s); if (cc.use_reg2) { @@ -1346,14 +1347,31 @@ static void do_gen_rep(DisasContext *s, MemOp ot, */ s->flags &= ~HF_RF_MASK; + /* + * For CMPS/SCAS, the CC_OP after a memory fault could come from either + * the previous instruction or the string instruction; but because we + * arrange to keep CC_OP up to date all the time, just mark the whole + * insn as CC_OP_DYNAMIC. + * + * It's not a problem to do this even for instructions that do not + * modify the flags, so do it unconditionally. + */ gen_update_cc_op(s); + tcg_set_insn_start_param(s->base.insn_start, 1, CC_OP_DYNAMIC); + + /* Any iteration at all? */ gen_op_jz_ecx(s, done); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_update_cc_op(s); + + /* Leave if REP condition fails. */ if (is_repz_nz) { int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; - gen_jcc(s, (JCC_Z << 1) | (nz ^ 1), done); + gen_jcc_noeob(s, (JCC_Z << 1) | (nz ^ 1), done); + /* gen_prepare_eflags_z never changes cc_op. */ + assert(!s->cc_op_dirty); } /* From 365811602572054b1c1173b19e8fd28689d827d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:08 +0100 Subject: [PATCH 1245/2892] target/i386: do not use gen_op_jz_ecx for repeated string operations Explicitly generate a TSTEQ branch (which is optimized to NE x,0 if possible). This does not make much sense yet, but later we will add more checks and some will use a temporary to check on the decremented value of CX/ECX/RCX; it will be clearer for all checks to share the same logic using TSTEQ(reg, cx_mask). Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-10-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 8bc91c3de3..7a3caf8b99 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1338,6 +1338,7 @@ static void do_gen_rep(DisasContext *s, MemOp ot, bool is_repz_nz) { TCGLabel *done = gen_new_label(); + target_ulong cx_mask = MAKE_64BIT_MASK(0, 8 << s->aflag); bool had_rf = s->flags & HF_RF_MASK; /* @@ -1360,7 +1361,7 @@ static void do_gen_rep(DisasContext *s, MemOp ot, tcg_set_insn_start_param(s->base.insn_start, 1, CC_OP_DYNAMIC); /* Any iteration at all? */ - gen_op_jz_ecx(s, done); + tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cpu_regs[R_ECX], cx_mask, done); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); From 0360b781870a628379de20e03305c4e62dbdcca4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:09 +0100 Subject: [PATCH 1246/2892] target/i386: optimize CX handling in repeated string operations In a repeated string operation, CX/ECX will be decremented until it is 0 but never underflow. Use this observation to avoid a deposit or zero-extend operation if the address size of the operation is smaller than MO_TL. As in the previous patch, the patch is structured to include some preparatory work for subsequent changes. In particular, introducing cx_next prepares for when ECX will be decremented *before* calling fn(s, ot), and therefore cannot yet be written back to cpu_regs. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-11-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 7a3caf8b99..0a8f3c8951 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1339,6 +1339,7 @@ static void do_gen_rep(DisasContext *s, MemOp ot, { TCGLabel *done = gen_new_label(); target_ulong cx_mask = MAKE_64BIT_MASK(0, 8 << s->aflag); + TCGv cx_next = tcg_temp_new(); bool had_rf = s->flags & HF_RF_MASK; /* @@ -1364,7 +1365,19 @@ static void do_gen_rep(DisasContext *s, MemOp ot, tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cpu_regs[R_ECX], cx_mask, done); fn(s, ot); - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + + tcg_gen_subi_tl(cx_next, cpu_regs[R_ECX], 1); + + /* + * Write back cx_next to CX/ECX/RCX. There can be no carry, so zero + * extend if needed but do not do expensive deposit operations. + */ +#ifdef TARGET_X86_64 + if (s->aflag == MO_32) { + tcg_gen_ext32u_tl(cx_next, cx_next); + } +#endif + tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); gen_update_cc_op(s); /* Leave if REP condition fails. */ From 456709db50f424d112bc5f07260fdc51555f3a24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:10 +0100 Subject: [PATCH 1247/2892] target/i386: execute multiple REP/REPZ iterations without leaving TB Use a TCG loop so that it is not necessary to go through the setup steps of REP and through the I/O check on every iteration. Interestingly, this is not a particularly effective optimization on its own, though it avoids the cost of correct RF emulation that was added in the previous patch. The main benefit lies in allowing the hoisting of loop invariants outside the loop, which will happen separately. The loop exits when the low 16 bits of CX/ECX/RCX are zero (so generally speaking the string operation runs in 65536 iteration batches) to give the main loop an opportunity to pick up interrupts. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-12-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 55 +++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 0a8f3c8951..991baf5d82 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1333,13 +1333,28 @@ static void gen_outs(DisasContext *s, MemOp ot) gen_bpt_io(s, s->tmp2_i32, ot); } +#define REP_MAX 65535 + static void do_gen_rep(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot), bool is_repz_nz) { + TCGLabel *last = gen_new_label(); + TCGLabel *loop = gen_new_label(); TCGLabel *done = gen_new_label(); + target_ulong cx_mask = MAKE_64BIT_MASK(0, 8 << s->aflag); TCGv cx_next = tcg_temp_new(); + + /* + * Check if we must translate a single iteration only. Normally, HF_RF_MASK + * would also limit translation blocks to one instruction, so that gen_eob + * can reset the flag; here however RF is set throughout the repetition, so + * we can plow through until CX/ECX/RCX is zero. + */ + bool can_loop = + (!(tb_cflags(s->base.tb) & (CF_USE_ICOUNT | CF_SINGLE_STEP)) + && !(s->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); bool had_rf = s->flags & HF_RF_MASK; /* @@ -1364,19 +1379,29 @@ static void do_gen_rep(DisasContext *s, MemOp ot, /* Any iteration at all? */ tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cpu_regs[R_ECX], cx_mask, done); - fn(s, ot); - - tcg_gen_subi_tl(cx_next, cpu_regs[R_ECX], 1); - /* - * Write back cx_next to CX/ECX/RCX. There can be no carry, so zero - * extend if needed but do not do expensive deposit operations. + * From now on we operate on the value of CX/ECX/RCX that will be written + * back, which is stored in cx_next. There can be no carry, so we can zero + * extend here if needed and not do any expensive deposit operations later. */ + tcg_gen_subi_tl(cx_next, cpu_regs[R_ECX], 1); #ifdef TARGET_X86_64 if (s->aflag == MO_32) { tcg_gen_ext32u_tl(cx_next, cx_next); + cx_mask = ~0; } #endif + + /* + * The last iteration is handled outside the loop, so that cx_next + * can never underflow. + */ + if (can_loop) { + tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cx_next, cx_mask, last); + } + + gen_set_label(loop); + fn(s, ot); tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); gen_update_cc_op(s); @@ -1388,6 +1413,12 @@ static void do_gen_rep(DisasContext *s, MemOp ot, assert(!s->cc_op_dirty); } + if (can_loop) { + tcg_gen_subi_tl(cx_next, cx_next, 1); + tcg_gen_brcondi_tl(TCG_COND_TSTNE, cx_next, REP_MAX, loop); + tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cx_next, cx_mask, last); + } + /* * Traps or interrupts set RF_MASK if they happen after any iteration * but the last. Set it here before giving the main loop a chance to @@ -1400,6 +1431,18 @@ static void do_gen_rep(DisasContext *s, MemOp ot, /* Go to the main loop but reenter the same instruction. */ gen_jmp_rel_csize(s, -cur_insn_len(s), 0); + if (can_loop) { + /* + * The last iteration needs no conditional jump, even if is_repz_nz, + * because the repeats are ending anyway. + */ + gen_set_label(last); + set_cc_op(s, CC_OP_DYNAMIC); + fn(s, ot); + tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); + gen_update_cc_op(s); + } + /* CX/ECX/RCX is zero, or REPZ/REPNZ broke the repetition. */ gen_set_label(done); set_cc_op(s, CC_OP_DYNAMIC); From 4f094e27f3ad2a35e305cb26a2926864815b6ac6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:11 +0100 Subject: [PATCH 1248/2892] target/i386: pull computation of string update value out of loop This is a common operation that is executed many times in rep movs or rep stos loops. It can improve performance by several percentage points. Signed-off-by: Paolo Bonzini Link: https://lore.kernel.org/r/20241215090613.89588-13-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 54 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 991baf5d82..9f4d3ebbd9 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -832,16 +832,13 @@ static bool gen_check_io(DisasContext *s, MemOp ot, TCGv_i32 port, #endif } -static void gen_movs(DisasContext *s, MemOp ot) +static void gen_movs(DisasContext *s, MemOp ot, TCGv dshift) { - TCGv dshift; - gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); gen_string_movl_A0_EDI(s); gen_op_st_v(s, ot, s->T0, s->A0); - dshift = gen_compute_Dshift(s, ot); gen_op_add_reg(s, s->aflag, R_ESI, dshift); gen_op_add_reg(s, s->aflag, R_EDI, dshift); } @@ -1246,22 +1243,22 @@ static inline void gen_jcc(DisasContext *s, int b, TCGLabel *l1) } } -static void gen_stos(DisasContext *s, MemOp ot) +static void gen_stos(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_EDI(s); gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_add_reg(s, s->aflag, R_EDI, gen_compute_Dshift(s, ot)); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); } -static void gen_lods(DisasContext *s, MemOp ot) +static void gen_lods(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); gen_op_mov_reg_v(s, ot, R_EAX, s->T0); - gen_op_add_reg(s, s->aflag, R_ESI, gen_compute_Dshift(s, ot)); + gen_op_add_reg(s, s->aflag, R_ESI, dshift); } -static void gen_scas(DisasContext *s, MemOp ot) +static void gen_scas(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_EDI(s); gen_op_ld_v(s, ot, s->T1, s->A0); @@ -1270,13 +1267,11 @@ static void gen_scas(DisasContext *s, MemOp ot) tcg_gen_sub_tl(cpu_cc_dst, s->T0, s->T1); set_cc_op(s, CC_OP_SUBB + ot); - gen_op_add_reg(s, s->aflag, R_EDI, gen_compute_Dshift(s, ot)); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); } -static void gen_cmps(DisasContext *s, MemOp ot) +static void gen_cmps(DisasContext *s, MemOp ot, TCGv dshift) { - TCGv dshift; - gen_string_movl_A0_EDI(s); gen_op_ld_v(s, ot, s->T1, s->A0); gen_string_movl_A0_ESI(s); @@ -1286,7 +1281,6 @@ static void gen_cmps(DisasContext *s, MemOp ot) tcg_gen_sub_tl(cpu_cc_dst, s->T0, s->T1); set_cc_op(s, CC_OP_SUBB + ot); - dshift = gen_compute_Dshift(s, ot); gen_op_add_reg(s, s->aflag, R_ESI, dshift); gen_op_add_reg(s, s->aflag, R_EDI, dshift); } @@ -1305,7 +1299,7 @@ static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) } } -static void gen_ins(DisasContext *s, MemOp ot) +static void gen_ins(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_EDI(s); /* Note: we must do this dummy write first to be restartable in @@ -1316,11 +1310,11 @@ static void gen_ins(DisasContext *s, MemOp ot) tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); gen_helper_in_func(ot, s->T0, s->tmp2_i32); gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_add_reg(s, s->aflag, R_EDI, gen_compute_Dshift(s, ot)); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); gen_bpt_io(s, s->tmp2_i32, ot); } -static void gen_outs(DisasContext *s, MemOp ot) +static void gen_outs(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); @@ -1329,14 +1323,14 @@ static void gen_outs(DisasContext *s, MemOp ot) tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T0); gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_op_add_reg(s, s->aflag, R_ESI, gen_compute_Dshift(s, ot)); + gen_op_add_reg(s, s->aflag, R_ESI, dshift); gen_bpt_io(s, s->tmp2_i32, ot); } #define REP_MAX 65535 -static void do_gen_rep(DisasContext *s, MemOp ot, - void (*fn)(DisasContext *s, MemOp ot), +static void do_gen_rep(DisasContext *s, MemOp ot, TCGv dshift, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift), bool is_repz_nz) { TCGLabel *last = gen_new_label(); @@ -1401,7 +1395,7 @@ static void do_gen_rep(DisasContext *s, MemOp ot, } gen_set_label(loop); - fn(s, ot); + fn(s, ot, dshift); tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); gen_update_cc_op(s); @@ -1438,7 +1432,7 @@ static void do_gen_rep(DisasContext *s, MemOp ot, */ gen_set_label(last); set_cc_op(s, CC_OP_DYNAMIC); - fn(s, ot); + fn(s, ot, dshift); tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); gen_update_cc_op(s); } @@ -1453,23 +1447,27 @@ static void do_gen_rep(DisasContext *s, MemOp ot, } static void gen_repz(DisasContext *s, MemOp ot, - void (*fn)(DisasContext *s, MemOp ot)) + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) { + TCGv dshift = gen_compute_Dshift(s, ot); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - do_gen_rep(s, ot, fn, false); + do_gen_rep(s, ot, dshift, fn, false); } else { - fn(s, ot); + fn(s, ot, dshift); } } static void gen_repz_nz(DisasContext *s, MemOp ot, - void (*fn)(DisasContext *s, MemOp ot)) + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) { + TCGv dshift = gen_compute_Dshift(s, ot); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - do_gen_rep(s, ot, fn, true); + do_gen_rep(s, ot, dshift, fn, true); } else { - fn(s, ot); + fn(s, ot, dshift); } } From 82290c76476021c647824f816d8ccfbbfb773b2e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jan 2025 11:35:45 +0100 Subject: [PATCH 1249/2892] target/i386: extract common bits of gen_repz/gen_repz_nz Now that everything has been cleaned up, look at DF and prefixes in a single function, and call that one from gen_repz and gen_repz_nz. Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 9f4d3ebbd9..9b2fde5eb2 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -688,14 +688,6 @@ static inline void gen_string_movl_A0_EDI(DisasContext *s) gen_lea_v_seg(s, cpu_regs[R_EDI], R_ES, -1); } -static inline TCGv gen_compute_Dshift(DisasContext *s, MemOp ot) -{ - TCGv dshift = tcg_temp_new(); - tcg_gen_ld32s_tl(dshift, tcg_env, offsetof(CPUX86State, df)); - tcg_gen_shli_tl(dshift, dshift, ot); - return dshift; -}; - static TCGv gen_ext_tl(TCGv dst, TCGv src, MemOp size, bool sign) { if (size == MO_TL) { @@ -1446,29 +1438,31 @@ static void do_gen_rep(DisasContext *s, MemOp ot, TCGv dshift, gen_jmp_rel_csize(s, 0, 1); } -static void gen_repz(DisasContext *s, MemOp ot, - void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) - +static void do_gen_string(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift), + bool is_repz_nz) { - TCGv dshift = gen_compute_Dshift(s, ot); + TCGv dshift = tcg_temp_new(); + tcg_gen_ld32s_tl(dshift, tcg_env, offsetof(CPUX86State, df)); + tcg_gen_shli_tl(dshift, dshift, ot); if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - do_gen_rep(s, ot, dshift, fn, false); + do_gen_rep(s, ot, dshift, fn, is_repz_nz); } else { fn(s, ot, dshift); } } +static void gen_repz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) +{ + do_gen_string(s, ot, fn, false); +} + static void gen_repz_nz(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) { - TCGv dshift = gen_compute_Dshift(s, ot); - - if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - do_gen_rep(s, ot, dshift, fn, true); - } else { - fn(s, ot, dshift); - } + do_gen_string(s, ot, fn, true); } static void gen_helper_fp_arith_ST0_FT0(int op) From 22063f03a7626c77d7a4546b90fd27badd504269 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Dec 2024 10:06:12 +0100 Subject: [PATCH 1250/2892] target/i386: avoid using s->tmp0 for add to implicit registers For updates to implicit registers (RCX in LOOP instructions, RSI or RDI in string instructions, or the stack pointer) do the add directly using the registers (with no temporary) if 32-bit or 64-bit, or use a temporary created for the occasion if 16-bit. This is more efficient and removes move instructions for the MO_TL case. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20241215090613.89588-14-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 9b2fde5eb2..a8935f487a 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -505,17 +505,24 @@ static inline void gen_op_jmp_v(DisasContext *s, TCGv dest) s->pc_save = -1; } +static inline void gen_op_add_reg(DisasContext *s, MemOp size, int reg, TCGv val) +{ + /* Using cpu_regs[reg] does not work for xH registers. */ + assert(size >= MO_16); + if (size == MO_16) { + TCGv temp = tcg_temp_new(); + tcg_gen_add_tl(temp, cpu_regs[reg], val); + gen_op_mov_reg_v(s, size, reg, temp); + } else { + tcg_gen_add_tl(cpu_regs[reg], cpu_regs[reg], val); + tcg_gen_ext_tl(cpu_regs[reg], cpu_regs[reg], size); + } +} + static inline void gen_op_add_reg_im(DisasContext *s, MemOp size, int reg, int32_t val) { - tcg_gen_addi_tl(s->tmp0, cpu_regs[reg], val); - gen_op_mov_reg_v(s, size, reg, s->tmp0); -} - -static inline void gen_op_add_reg(DisasContext *s, MemOp size, int reg, TCGv val) -{ - tcg_gen_add_tl(s->tmp0, cpu_regs[reg], val); - gen_op_mov_reg_v(s, size, reg, s->tmp0); + gen_op_add_reg(s, size, reg, tcg_constant_tl(val)); } static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) From c597ff5339a9918b00d9f4160126db0ac2a423cc Mon Sep 17 00:00:00 2001 From: Tao Su Date: Tue, 21 Jan 2025 10:06:47 +0800 Subject: [PATCH 1251/2892] target/i386: Introduce SierraForest-v2 model Update SierraForest CPU model to add LAM, 4 bits indicating certain bits of IA32_SPEC_CTR are supported(intel-psfd, ipred-ctrl, rrsba-ctrl, bhi-ctrl) and the missing features(ss, tsc-adjust, cldemote, movdiri, movdir64b) Also add GDS-NO and RFDS-NO to indicate the related vulnerabilities are mitigated in stepping 3. Tested-by: Xuelian Guo Signed-off-by: Tao Su Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250121020650.1899618-2-tao1.su@linux.intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1b9c11022c..6db8d6c9ba 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4549,6 +4549,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { .model_id = "Intel Xeon Processor (SierraForest)", .versions = (X86CPUVersionDefinition[]) { { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "ss", "on" }, + { "tsc-adjust", "on" }, + { "cldemote", "on" }, + { "movdiri", "on" }, + { "movdir64b", "on" }, + { "gds-no", "on" }, + { "rfds-no", "on" }, + { "lam", "on" }, + { "intel-psfd", "on"}, + { "ipred-ctrl", "on"}, + { "rrsba-ctrl", "on"}, + { "bhi-ctrl", "on"}, + { "stepping", "3" }, + { /* end of list */ } + } + }, { /* end of list */ }, }, }, From b611931d4f70b9a3e49e39c405c63b3b5e9c0df1 Mon Sep 17 00:00:00 2001 From: Tao Su Date: Tue, 21 Jan 2025 10:06:48 +0800 Subject: [PATCH 1252/2892] target/i386: Export BHI_NO bit to guests Branch History Injection (BHI) is a CPU side-channel vulnerability, where an attacker may manipulate branch history before transitioning from user to supervisor mode or from VMX non-root/guest to root mode. CPUs that set BHI_NO bit in MSR IA32_ARCH_CAPABILITIES to indicate no additional mitigation is required to prevent BHI. Make BHI_NO bit available to guests. Tested-by: Xuelian Guo Signed-off-by: Tao Su Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250121020650.1899618-3-tao1.su@linux.intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6db8d6c9ba..33fb27a611 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1364,7 +1364,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "taa-no", NULL, NULL, NULL, NULL, "sbdr-ssdp-no", "fbsdp-no", "psdp-no", NULL, "fb-clear", NULL, NULL, - NULL, NULL, NULL, NULL, + "bhi-no", NULL, NULL, NULL, "pbrsb-no", NULL, "gds-no", "rfds-no", "rfds-clear", NULL, NULL, NULL, }, From 56e84d898f17606b5d88778726466540af96b234 Mon Sep 17 00:00:00 2001 From: Tao Su Date: Tue, 21 Jan 2025 10:06:49 +0800 Subject: [PATCH 1253/2892] target/i386: Add new CPU model ClearwaterForest According to table 1-2 in Intel Architecture Instruction Set Extensions and Future Features (rev 056) [1], ClearwaterForest has the following new features which have already been virtualized: - AVX-VNNI-INT16 CPUID.(EAX=7,ECX=1):EDX[bit 10] - SHA512 CPUID.(EAX=7,ECX=1):EAX[bit 0] - SM3 CPUID.(EAX=7,ECX=1):EAX[bit 1] - SM4 CPUID.(EAX=7,ECX=1):EAX[bit 2] Add above features to new CPU model ClearwaterForest. Comparing with SierraForest, ClearwaterForest bare-metal contains all features of SierraForest-v2 CPU model and adds: - PREFETCHI CPUID.(EAX=7,ECX=1):EDX[bit 14] - DDPD_U CPUID.(EAX=7,ECX=2):EDX[bit 3] - BHI_NO IA32_ARCH_CAPABILITIES[bit 20] Add above and all features of SierraForest-v2 CPU model to new CPU model ClearwaterForest. [1] https://cdrdv2.intel.com/v1/dl/getContent/671368 Tested-by: Xuelian Guo Signed-off-by: Tao Su Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250121020650.1899618-4-tao1.su@linux.intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++ target/i386/cpu.h | 33 +++++++++--- 2 files changed, 162 insertions(+), 6 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 33fb27a611..b5dd60d281 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4571,6 +4571,141 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ }, }, }, + { + .name = "ClearwaterForest", + .level = 0x23, + .xlevel = 0x80000008, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 221, + .stepping = 0, + /* + * please keep the ascending order so that we can have a clear view of + * bit position of each feature. + */ + .features[FEAT_1_EDX] = + CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC | + CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | + CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | + CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX | CPUID_FXSR | + CPUID_SSE | CPUID_SSE2 | CPUID_SS, + .features[FEAT_1_ECX] = + CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 | + CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | + CPUID_EXT_POPCNT | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_AES | + CPUID_EXT_XSAVE | CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_WBNOINVD, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_TSC_ADJUST | + CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | + CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_SHA_NI, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_BUS_LOCK_DETECT | + CPUID_7_0_ECX_CLDEMOTE | CPUID_7_0_ECX_MOVDIRI | + CPUID_7_0_ECX_MOVDIR64B, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_SERIALIZE | + CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_ARCH_CAPABILITIES | + CPUID_7_0_EDX_SPEC_CTRL_SSBD, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_IBRS_ALL | + MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | MSR_ARCH_CAP_MDS_NO | + MSR_ARCH_CAP_PSCHANGE_MC_NO | MSR_ARCH_CAP_SBDR_SSDP_NO | + MSR_ARCH_CAP_FBSDP_NO | MSR_ARCH_CAP_PSDP_NO | + MSR_ARCH_CAP_BHI_NO | MSR_ARCH_CAP_PBRSB_NO | + MSR_ARCH_CAP_GDS_NO | MSR_ARCH_CAP_RFDS_NO, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_SHA512 | CPUID_7_1_EAX_SM3 | CPUID_7_1_EAX_SM4 | + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_CMPCCXADD | + CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_AVX_IFMA | + CPUID_7_1_EAX_LAM, + .features[FEAT_7_1_EDX] = + CPUID_7_1_EDX_AVX_VNNI_INT8 | CPUID_7_1_EDX_AVX_NE_CONVERT | + CPUID_7_1_EDX_AVX_VNNI_INT16 | CPUID_7_1_EDX_PREFETCHITI, + .features[FEAT_7_2_EDX] = + CPUID_7_2_EDX_PSFD | CPUID_7_2_EDX_IPRED_CTRL | + CPUID_7_2_EDX_RRSBA_CTRL | CPUID_7_2_EDX_DDPD_U | + CPUID_7_2_EDX_BHI_CTRL | CPUID_7_2_EDX_MCDT_NO, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | VMX_VM_ENTRY_LOAD_IA32_EFER, + .features[FEAT_VMX_EPT_VPID_CAPS] = + MSR_VMX_EPT_EXECONLY | MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | + MSR_VMX_EPT_INVVPID_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_VMWRITE_VMEXIT, + .features[FEAT_VMX_PINBASED_CTLS] = + VMX_PIN_BASED_EXT_INTR_MASK | VMX_PIN_BASED_NMI_EXITING | + VMX_PIN_BASED_VIRTUAL_NMIS | VMX_PIN_BASED_VMX_PREEMPTION_TIMER | + VMX_PIN_BASED_POSTED_INTR, + .features[FEAT_VMX_PROCBASED_CTLS] = + VMX_CPU_BASED_VIRTUAL_INTR_PENDING | + VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_INVLPG_EXITING | VMX_CPU_BASED_MWAIT_EXITING | + VMX_CPU_BASED_RDPMC_EXITING | VMX_CPU_BASED_RDTSC_EXITING | + VMX_CPU_BASED_CR3_LOAD_EXITING | VMX_CPU_BASED_CR3_STORE_EXITING | + VMX_CPU_BASED_CR8_LOAD_EXITING | VMX_CPU_BASED_CR8_STORE_EXITING | + VMX_CPU_BASED_TPR_SHADOW | VMX_CPU_BASED_VIRTUAL_NMI_PENDING | + VMX_CPU_BASED_MOV_DR_EXITING | VMX_CPU_BASED_UNCOND_IO_EXITING | + VMX_CPU_BASED_USE_IO_BITMAPS | VMX_CPU_BASED_MONITOR_TRAP_FLAG | + VMX_CPU_BASED_USE_MSR_BITMAPS | VMX_CPU_BASED_MONITOR_EXITING | + VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + VMX_SECONDARY_EXEC_ENABLE_EPT | VMX_SECONDARY_EXEC_DESC | + VMX_SECONDARY_EXEC_RDTSCP | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + VMX_SECONDARY_EXEC_ENABLE_VPID | VMX_SECONDARY_EXEC_WBINVD_EXITING | + VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | + VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | + VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | + VMX_SECONDARY_EXEC_RDRAND_EXITING | + VMX_SECONDARY_EXEC_ENABLE_INVPCID | + VMX_SECONDARY_EXEC_ENABLE_VMFUNC | VMX_SECONDARY_EXEC_SHADOW_VMCS | + VMX_SECONDARY_EXEC_RDSEED_EXITING | VMX_SECONDARY_EXEC_ENABLE_PML | + VMX_SECONDARY_EXEC_XSAVES, + .features[FEAT_VMX_VMFUNC] = + MSR_VMX_VMFUNC_EPT_SWITCHING, + .model_id = "Intel Xeon Processor (ClearwaterForest)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { /* end of list */ }, + }, + }, { .name = "Denverton", .level = 21, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index b26e25ba15..c67b42d34f 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -951,6 +951,12 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* Speculative Store Bypass Disable */ #define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) +/* SHA512 Instruction */ +#define CPUID_7_1_EAX_SHA512 (1U << 0) +/* SM3 Instruction */ +#define CPUID_7_1_EAX_SM3 (1U << 1) +/* SM4 Instruction */ +#define CPUID_7_1_EAX_SM4 (1U << 2) /* AVX VNNI Instruction */ #define CPUID_7_1_EAX_AVX_VNNI (1U << 4) /* AVX512 BFloat16 Instruction */ @@ -963,6 +969,12 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_7_1_EAX_FSRS (1U << 11) /* Fast Short REP CMPS/SCAS */ #define CPUID_7_1_EAX_FSRC (1U << 12) +/* Flexible return and event delivery (FRED) */ +#define CPUID_7_1_EAX_FRED (1U << 17) +/* Load into IA32_KERNEL_GS_BASE (LKGS) */ +#define CPUID_7_1_EAX_LKGS (1U << 18) +/* Non-Serializing Write to Model Specific Register (WRMSRNS) */ +#define CPUID_7_1_EAX_WRMSRNS (1U << 19) /* Support Tile Computational Operations on FP16 Numbers */ #define CPUID_7_1_EAX_AMX_FP16 (1U << 21) /* Support for VPMADD52[H,L]UQ */ @@ -976,17 +988,23 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_7_1_EDX_AVX_NE_CONVERT (1U << 5) /* AMX COMPLEX Instructions */ #define CPUID_7_1_EDX_AMX_COMPLEX (1U << 8) +/* AVX-VNNI-INT16 Instructions */ +#define CPUID_7_1_EDX_AVX_VNNI_INT16 (1U << 10) /* PREFETCHIT0/1 Instructions */ #define CPUID_7_1_EDX_PREFETCHITI (1U << 14) /* Support for Advanced Vector Extensions 10 */ #define CPUID_7_1_EDX_AVX10 (1U << 19) -/* Flexible return and event delivery (FRED) */ -#define CPUID_7_1_EAX_FRED (1U << 17) -/* Load into IA32_KERNEL_GS_BASE (LKGS) */ -#define CPUID_7_1_EAX_LKGS (1U << 18) -/* Non-Serializing Write to Model Specific Register (WRMSRNS) */ -#define CPUID_7_1_EAX_WRMSRNS (1U << 19) +/* Indicate bit 7 of the IA32_SPEC_CTRL MSR is supported */ +#define CPUID_7_2_EDX_PSFD (1U << 0) +/* Indicate bits 3 and 4 of the IA32_SPEC_CTRL MSR are supported */ +#define CPUID_7_2_EDX_IPRED_CTRL (1U << 1) +/* Indicate bits 5 and 6 of the IA32_SPEC_CTRL MSR are supported */ +#define CPUID_7_2_EDX_RRSBA_CTRL (1U << 2) +/* Indicate bit 8 of the IA32_SPEC_CTRL MSR is supported */ +#define CPUID_7_2_EDX_DDPD_U (1U << 3) +/* Indicate bit 10 of the IA32_SPEC_CTRL MSR is supported */ +#define CPUID_7_2_EDX_BHI_CTRL (1U << 4) /* Do not exhibit MXCSR Configuration Dependent Timing (MCDT) behavior */ #define CPUID_7_2_EDX_MCDT_NO (1U << 5) @@ -1144,7 +1162,10 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define MSR_ARCH_CAP_FBSDP_NO (1U << 14) #define MSR_ARCH_CAP_PSDP_NO (1U << 15) #define MSR_ARCH_CAP_FB_CLEAR (1U << 17) +#define MSR_ARCH_CAP_BHI_NO (1U << 20) #define MSR_ARCH_CAP_PBRSB_NO (1U << 24) +#define MSR_ARCH_CAP_GDS_NO (1U << 26) +#define MSR_ARCH_CAP_RFDS_NO (1U << 27) #define MSR_CORE_CAP_SPLIT_LOCK_DETECT (1U << 5) From 0a6dec6d11e5e392dcd6299548bf1514f1201707 Mon Sep 17 00:00:00 2001 From: Tao Su Date: Tue, 21 Jan 2025 10:06:50 +0800 Subject: [PATCH 1254/2892] docs: Add GNR, SRF and CWF CPU models Update GraniteRapids, SierraForest and ClearwaterForest CPU models in section "Preferred CPU models for Intel x86 hosts". Also introduce bhi-no, gds-no and rfds-no in doc. Suggested-by: Zhao Liu Signed-off-by: Tao Su Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250121020650.1899618-5-tao1.su@linux.intel.com Signed-off-by: Paolo Bonzini --- docs/system/cpu-models-x86.rst.inc | 50 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/docs/system/cpu-models-x86.rst.inc b/docs/system/cpu-models-x86.rst.inc index ba27b5683f..6a770ca835 100644 --- a/docs/system/cpu-models-x86.rst.inc +++ b/docs/system/cpu-models-x86.rst.inc @@ -71,6 +71,16 @@ mixture of host CPU models between machines, if live migration compatibility is required, use the newest CPU model that is compatible across all desired hosts. +``ClearwaterForest`` + Intel Xeon Processor (ClearwaterForest, 2025) + +``SierraForest``, ``SierraForest-v2`` + Intel Xeon Processor (SierraForest, 2024), SierraForest-v2 mitigates + the GDS and RFDS vulnerabilities with stepping 3. + +``GraniteRapids``, ``GraniteRapids-v2`` + Intel Xeon Processor (GraniteRapids, 2024) + ``Cascadelake-Server``, ``Cascadelake-Server-noTSX`` Intel Xeon Processor (Cascade Lake, 2019), with "stepping" levels 6 or 7 only. (The Cascade Lake Xeon processor with *stepping 5 is @@ -181,7 +191,7 @@ features are included if using "Host passthrough" or "Host model". CVE-2018-12127, [MSBDS] CVE-2018-12126). This is an MSR (Model-Specific Register) feature rather than a CPUID feature, - so it will not appear in the Linux ``/proc/cpuinfo`` in the host or + therefore it will not appear in the Linux ``/proc/cpuinfo`` in the host or guest. Instead, the host kernel uses it to populate the MDS vulnerability file in ``sysfs``. @@ -189,10 +199,10 @@ features are included if using "Host passthrough" or "Host model". affected} in the ``/sys/devices/system/cpu/vulnerabilities/mds`` file. ``taa-no`` - Recommended to inform that the guest that the host is ``not`` + Recommended to inform the guest that the host is ``not`` vulnerable to CVE-2019-11135, TSX Asynchronous Abort (TAA). - This too is an MSR feature, so it does not show up in the Linux + This is also an MSR feature, therefore it does not show up in the Linux ``/proc/cpuinfo`` in the host or guest. It should only be enabled for VMs if the host reports ``Not affected`` @@ -214,7 +224,7 @@ features are included if using "Host passthrough" or "Host model". By disabling TSX, KVM-based guests can avoid paying the price of mitigating TSX-based attacks. - Note that ``tsx-ctrl`` too is an MSR feature, so it does not show + Note that ``tsx-ctrl`` is also an MSR feature, therefore it does not show up in the Linux ``/proc/cpuinfo`` in the host or guest. To validate that Intel TSX is indeed disabled for the guest, there are @@ -223,6 +233,38 @@ features are included if using "Host passthrough" or "Host model". ``/sys/devices/system/cpu/vulnerabilities/tsx_async_abort`` file in the guest should report ``Mitigation: TSX disabled``. +``bhi-no`` + Recommended to inform the guest that the host is ``not`` + vulnerable to CVE-2022-0001, Branch History Injection (BHI). + + This is also an MSR feature, therefore it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports + ``BHI: Not affected`` in the + ``/sys/devices/system/cpu/vulnerabilities/spectre_v2`` file. + +``gds-no`` + Recommended to inform the guest that the host is ``not`` + vulnerable to CVE-2022-40982, Gather Data Sampling (GDS). + + This is also an MSR feature, therefore it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports ``Not affected`` + in the ``/sys/devices/system/cpu/vulnerabilities/gather_data_sampling`` + file. + +``rfds-no`` + Recommended to inform the guest that the host is ``not`` + vulnerable to CVE-2023-28746, Register File Data Sampling (RFDS). + + This is also an MSR feature, therefore it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports ``Not affected`` + in the ``/sys/devices/system/cpu/vulnerabilities/reg_file_data_sampling`` + file. Preferred CPU models for AMD x86 hosts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8113dbbcdaee05f319a7e48272416d918cb2b04a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 21 Jan 2025 23:43:18 +0800 Subject: [PATCH 1255/2892] stub: Fix build failure with --enable-user --disable-system --enable-tools Configuring "--enable-user --disable-system --enable-tools" causes the build failure with the following information: /usr/bin/ld: libhwcore.a.p/hw_core_qdev.c.o: in function `device_finalize': /qemu/build/../hw/core/qdev.c:688: undefined reference to `qapi_event_send_device_deleted' collect2: error: ld returned 1 exit status To fix the above issue, add qdev.c stub when build with `have_tools`. With this fix, QEMU could be successfully built in the following cases: --enable-user --disable-system --enable-tools --enable-user --disable-system --disable-tools --enable-user --disable-system Cc: qemu-stable@nongnu.org Fixes: 388b849fb6c3 ("stubs: avoid duplicate symbols in libqemuutil.a") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2766 Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250121154318.214680-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- stubs/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/meson.build b/stubs/meson.build index e91614a874..a8b3aeb564 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -57,8 +57,8 @@ if have_user stub_ss.add(files('cpu-synchronize-state.c')) # Stubs for QAPI events. Those can always be included in the build, but - # they are not built at all for --disable-system --disable-tools builds. - if not (have_system or have_tools) + # they are not built at all for --disable-system builds. + if not have_system stub_ss.add(files('qdev.c')) endif endif From 0f9eb0ff2b25787be62fceb036dba7c3f54fde2d Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 21 Jan 2025 22:04:56 +0800 Subject: [PATCH 1256/2892] rust/qdev: Make REALIZE safe A safe REALIZE accepts immutable reference. Since current PL011's realize() only calls a char binding function ( qemu_chr_fe_set_handlers), it is possible to convert mutable reference (&mut self) to immutable reference (&self), which only needs to convert the pointers passed to C to mutable pointers. Thus, make REALIZE accept immutable reference. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250121140457.84631-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 10 +++++----- rust/qemu-api/src/qdev.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 65a1234b9f..a0e0fbdd9d 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,7 +2,7 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of_mut, NonNull}; +use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, os::raw::{c_int, c_uint, c_void}, @@ -156,7 +156,7 @@ impl DeviceImpl for PL011State { fn vmsd() -> Option<&'static VMStateDescription> { Some(&device_class::VMSTATE_PL011) } - const REALIZE: Option = Some(Self::realize); + const REALIZE: Option = Some(Self::realize); const RESET: Option = Some(Self::reset); } @@ -439,17 +439,17 @@ impl PL011State { self.read_trigger = 1; } - pub fn realize(&mut self) { + pub fn realize(&self) { // SAFETY: self.char_backend has the correct size and alignment for a // CharBackend object, and its callbacks are of the correct types. unsafe { qemu_chr_fe_set_handlers( - addr_of_mut!(self.char_backend), + addr_of!(self.char_backend) as *mut CharBackend, Some(pl011_can_receive), Some(pl011_receive), Some(pl011_event), None, - addr_of_mut!(*self).cast::(), + addr_of!(*self).cast::() as *mut c_void, core::ptr::null_mut(), true, ); diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 686054e737..a5121e31a3 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -23,7 +23,7 @@ pub trait DeviceImpl { /// /// If not `None`, the parent class's `realize` method is overridden /// with the function pointed to by `REALIZE`. - const REALIZE: Option = None; + const REALIZE: Option = None; /// If not `None`, the parent class's `reset` method is overridden /// with the function pointed to by `RESET`. From 06a1cfb5550a090b63c81cf5f44d2558010a8ed7 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 21 Jan 2025 22:04:57 +0800 Subject: [PATCH 1257/2892] rust/pl011: Avoid bindings::* List all the necessary bindings to better identify gaps in rust/qapi. And include the bindings wrapped by rust/qapi instead mapping the raw bindings directly. Inspired-by: Paolo Bonzini Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250121140457.84631-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index a0e0fbdd9d..4f1080ff19 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,12 +9,19 @@ use std::{ }; use qemu_api::{ - bindings::{self, *}, + bindings::{ + error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_new, + qdev_prop_set_chr, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, + qemu_irq, sysbus_connect_irq, sysbus_mmio_map, sysbus_realize_and_unref, CharBackend, + Chardev, Clock, ClockEvent, MemoryRegion, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, + }, c_str, irq::InterruptSource, prelude::*, - qdev::DeviceImpl, + qdev::{DeviceImpl, DeviceState, Property}, qom::{ClassInitImpl, ObjectImpl, ParentField}, + sysbus::{SysBusDevice, SysBusDeviceClass}, + vmstate::VMStateDescription, }; use crate::{ @@ -494,7 +501,7 @@ impl PL011State { } pub fn event(&mut self, event: QEMUChrEvent) { - if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { + if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { self.put_fifo(registers::Data::BREAK.into()); } } From 5014e33b1e00d330f13df33c09a3932ac88f8d94 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 21 Jan 2025 23:13:21 +0800 Subject: [PATCH 1258/2892] memattrs: Convert unspecified member to bool Convert `unspecified` member of MemTxAttrs from bit field to bool, so that bindgen could generate more ergonomic Rust binding with bool type. As a result, MemTxAttrs needs to be expanded from 4 bytes to 8 bytes. Therefore, move `unspecified` to after the bit fields and add reserved members to ensure that the whole structure is packed into 8 bytes. Suggested-by: Richard Henderson Suggested-by: Paolo Bonzini Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250121151322.171832-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- include/exec/memattrs.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index e27c18f3dc..4fde4eee84 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -23,12 +23,6 @@ * different semantics. */ typedef struct MemTxAttrs { - /* Bus masters which don't specify any attributes will get this - * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can - * distinguish "all attributes deliberately clear" from - * "didn't specify" if necessary. - */ - unsigned int unspecified:1; /* * ARM/AMBA: TrustZone Secure access * x86: System Management Mode access @@ -57,6 +51,17 @@ typedef struct MemTxAttrs { * PID (PCI PASID) support: Limited to 8 bits process identifier. */ unsigned int pid:8; + + /* + * Bus masters which don't specify any attributes will get this + * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can + * distinguish "all attributes deliberately clear" from + * "didn't specify" if necessary. + */ + bool unspecified; + + uint8_t _reserved1; + uint16_t _reserved2; } MemTxAttrs; /* Bus masters which don't specify any attributes will get this, @@ -64,7 +69,7 @@ typedef struct MemTxAttrs { * (so that we can distinguish "all attributes deliberately clear" * from "didn't specify" if necessary). */ -#define MEMTXATTRS_UNSPECIFIED ((MemTxAttrs) { .unspecified = 1 }) +#define MEMTXATTRS_UNSPECIFIED ((MemTxAttrs) { .unspecified = true }) /* New-style MMIO accessors can indicate that the transaction failed. * A zero (MEMTX_OK) response means success; anything else is a failure From 57f9d9c84a9112d534fa90f2a6dad74bd71150b6 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 21 Jan 2025 23:13:22 +0800 Subject: [PATCH 1259/2892] memattrs: Check the size of MemTxAttrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure MemTxAttrs is packed into 8 bytes and does not exceed 8 bytes. Suggested-by: Philippe Mathieu-Daudà Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250121151322.171832-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- include/exec/memattrs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 4fde4eee84..060b7e7131 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -64,6 +64,8 @@ typedef struct MemTxAttrs { uint16_t _reserved2; } MemTxAttrs; +QEMU_BUILD_BUG_ON(sizeof(MemTxAttrs) > 8); + /* Bus masters which don't specify any attributes will get this, * which has all attribute bits clear except the topmost one * (so that we can distinguish "all attributes deliberately clear" From 0d43ddae35a29d1822ec3f35a31bfe7c91618ef4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 8 Dec 2024 12:16:56 +0100 Subject: [PATCH 1260/2892] rust: vmstate: add new type safe implementation The existing translation of the C macros for vmstate does not make any attempt to type-check vmstate declarations against the struct, so introduce a new system that computes VMStateField based on the actual struct declaration. Macros do not have full access to the type system, therefore a full implementation of this scheme requires a helper trait to analyze the type and produce a VMStateField from it; a macro "vmstate_of!" accepts arguments similar to "offset_of!" and tricks the compiler into looking up the trait for the right type. The patch introduces not just vmstate_of!, but also the slightly too clever enabling macro call_func_with_field!. The particular trick used here was proposed on the users.rust-lang.org forum, so I take no merit and all the blame. Introduce the trait and some functions to access it; the actual implementation comes later. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/prelude.rs | 2 + rust/qemu-api/src/vmstate.rs | 113 +++++++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 4ea70b9c82..2dc86e19b2 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -18,3 +18,5 @@ pub use crate::qom::ObjectType; pub use crate::qom_isa; pub use crate::sysbus::SysBusDeviceMethods; + +pub use crate::vmstate::VMState; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 63c897abcd..b839a7d6b7 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -4,13 +4,114 @@ //! Helper macros to declare migration state for device models. //! -//! Some macros are direct equivalents to the C macros declared in -//! `include/migration/vmstate.h` while -//! [`vmstate_subsections`](crate::vmstate_subsections) and -//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when -//! declaring a device model state struct. +//! This module includes three families of macros: +//! +//! * [`vmstate_unused!`](crate::vmstate_unused) and +//! [`vmstate_of!`](crate::vmstate_of), which are used to express the +//! migration format for a struct. This is based on the [`VMState`] trait, +//! which is defined by all migrateable types. +//! +//! * helper macros to declare a device model state struct, in particular +//! [`vmstate_subsections`](crate::vmstate_subsections) and +//! [`vmstate_fields`](crate::vmstate_fields). +//! +//! * direct equivalents to the C macros declared in +//! `include/migration/vmstate.h`. These are not type-safe and should not be +//! used if the equivalent functionality is available with `vmstate_of!`. -pub use crate::bindings::VMStateDescription; +use core::marker::PhantomData; + +pub use crate::bindings::{VMStateDescription, VMStateField}; + +/// This macro is used to call a function with a generic argument bound +/// to the type of a field. The function must take a +/// [`PhantomData`]`` argument; `T` is the type of +/// field `$field` in the `$typ` type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::call_func_with_field; +/// # use core::marker::PhantomData; +/// const fn size_of_field(_: PhantomData) -> usize { +/// std::mem::size_of::() +/// } +/// +/// struct Foo { +/// x: u16, +/// }; +/// // calls size_of_field::() +/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); +/// ``` +#[macro_export] +macro_rules! call_func_with_field { + // Based on the answer by user steffahn (Frank Steffahn) at + // https://users.rust-lang.org/t/inferring-type-of-field/122857 + // and used under MIT license + ($func:expr, $typ:ty, $($field:tt).+) => { + $func(loop { + #![allow(unreachable_code)] + const fn phantom__(_: &T) -> ::core::marker::PhantomData { ::core::marker::PhantomData } + // Unreachable code is exempt from checks on uninitialized values. + // Use that trick to infer the type of this PhantomData. + break ::core::marker::PhantomData; + break phantom__(&{ let value__: $typ; value__.$($field).+ }); + }) + }; +} + +/// A trait for types that can be included in a device's migration stream. It +/// provides the base contents of a `VMStateField` (minus the name and offset). +/// +/// # Safety +/// +/// The contents of this trait go straight into structs that are parsed by C +/// code and used to introspect into other structs. Be careful. +pub unsafe trait VMState { + /// The base contents of a `VMStateField` (minus the name and offset) for + /// the type that is implementing the trait. + const BASE: VMStateField; +} + +/// Internal utility function to retrieve a type's `VMStateField`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_base(_: PhantomData) -> VMStateField { + T::BASE +} + +/// Return the `VMStateField` for a field of a struct. The field must be +/// visible in the current scope. +/// +/// In order to support other types, the trait `VMState` must be implemented +/// for them. +#[macro_export] +macro_rules! vmstate_of { + ($struct_name:ty, $field_name:ident $(,)?) => { + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: $crate::offset_of!($struct_name, $field_name), + // Compute most of the VMStateField from the type of the field. + ..$crate::call_func_with_field!( + $crate::vmstate::vmstate_base, + $struct_name, + $field_name + ) + } + }; +} + +// Add a couple builder-style methods to VMStateField, allowing +// easy derivation of VMStateField constants from other types. +impl VMStateField { + #[must_use] + pub const fn with_version_id(mut self, version_id: i32) -> Self { + assert!(version_id >= 0); + self.version_id = version_id; + self + } +} #[doc(alias = "VMSTATE_UNUSED_BUFFER")] #[macro_export] From 80aa3045bd42bec287d1f9bcc94be32a4c1b582e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 29 Dec 2024 12:29:45 +0100 Subject: [PATCH 1261/2892] rust: vmstate: implement VMState for non-leaf types Arrays, pointers and cells use a VMStateField that is based on that for the inner type. The implementation therefore delegates to the VMState implementation of the inner type. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 79 +++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index b839a7d6b7..211c3d096b 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -19,8 +19,9 @@ //! `include/migration/vmstate.h`. These are not type-safe and should not be //! used if the equivalent functionality is available with `vmstate_of!`. -use core::marker::PhantomData; +use core::{marker::PhantomData, mem, ptr::NonNull}; +use crate::bindings::VMStateFlags; pub use crate::bindings::{VMStateDescription, VMStateField}; /// This macro is used to call a function with a generic argument bound @@ -102,6 +103,15 @@ macro_rules! vmstate_of { }; } +impl VMStateFlags { + const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( + VMStateFlags::VMS_VARRAY_INT32.0 + | VMStateFlags::VMS_VARRAY_UINT8.0 + | VMStateFlags::VMS_VARRAY_UINT16.0 + | VMStateFlags::VMS_VARRAY_UINT32.0, + ); +} + // Add a couple builder-style methods to VMStateField, allowing // easy derivation of VMStateField constants from other types. impl VMStateField { @@ -111,6 +121,73 @@ impl VMStateField { self.version_id = version_id; self } + + #[must_use] + pub const fn with_array_flag(mut self, num: usize) -> Self { + assert!(num <= 0x7FFF_FFFFusize); + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); + assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); + if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); + } + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); + self.num = num as i32; + self + } + + #[must_use] + pub const fn with_pointer_flag(mut self) -> Self { + assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); + self + } +} + +// Transparent wrappers: just use the internal type + +macro_rules! impl_vmstate_transparent { + ($type:ty where $base:tt: VMState $($where:tt)*) => { + unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const BASE: VMStateField = VMStateField { + size: mem::size_of::<$type>(), + ..<$base as VMState>::BASE + }; + } + }; +} + +impl_vmstate_transparent!(std::cell::Cell where T: VMState); +impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); +impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); +impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); + +// Pointer types using the underlying type's VMState plus VMS_POINTER +// Note that references are not supported, though references to cells +// could be allowed. + +macro_rules! impl_vmstate_pointer { + ($type:ty where $base:tt: VMState $($where:tt)*) => { + unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); + } + }; +} + +impl_vmstate_pointer!(*const T where T: VMState); +impl_vmstate_pointer!(*mut T where T: VMState); +impl_vmstate_pointer!(NonNull where T: VMState); + +// Unlike C pointers, Box is always non-null therefore there is no need +// to specify VMS_ALLOC. +impl_vmstate_pointer!(Box where T: VMState); + +// Arrays using the underlying type's VMState plus +// VMS_ARRAY/VMS_ARRAY_OF_POINTER + +unsafe impl VMState for [T; N] { + const BASE: VMStateField = ::BASE.with_array_flag(N); } #[doc(alias = "VMSTATE_UNUSED_BUFFER")] From 5b024b4e73f180402fde8485e8d4a51383592940 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 18:05:23 +0100 Subject: [PATCH 1262/2892] rust: vmstate: add varray support to vmstate_of! Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 42 ++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 211c3d096b..2b14d4839d 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -72,6 +72,15 @@ pub unsafe trait VMState { /// The base contents of a `VMStateField` (minus the name and offset) for /// the type that is implementing the trait. const BASE: VMStateField; + + /// A flag that is added to another field's `VMStateField` to specify the + /// length's type in a variable-sized array. If this is not a supported + /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it + /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a + /// compile-time error. + const VARRAY_FLAG: VMStateFlags = { + panic!("invalid type for variable-sized array"); + }; } /// Internal utility function to retrieve a type's `VMStateField`; @@ -80,6 +89,13 @@ pub const fn vmstate_base(_: PhantomData) -> VMStateField { T::BASE } +/// Internal utility function to retrieve a type's `VMStateFlags` when it +/// is used as the element count of a `VMSTATE_VARRAY`; used by +/// [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags { + T::VARRAY_FLAG +} + /// Return the `VMStateField` for a field of a struct. The field must be /// visible in the current scope. /// @@ -87,18 +103,23 @@ pub const fn vmstate_base(_: PhantomData) -> VMStateField { /// for them. #[macro_export] macro_rules! vmstate_of { - ($struct_name:ty, $field_name:ident $(,)?) => { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, offset: $crate::offset_of!($struct_name, $field_name), // Compute most of the VMStateField from the type of the field. + $(.num_offset: $crate::offset_of!($struct_name, $num),)? ..$crate::call_func_with_field!( $crate::vmstate::vmstate_base, $struct_name, $field_name - ) + )$(.with_varray_flag($crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num)) + $(.with_varray_multiply($factor))?)? } }; } @@ -143,6 +164,22 @@ impl VMStateField { self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); self } + + #[must_use] + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); + self.flags = VMStateFlags(self.flags.0 | flag.0); + self + } + + #[must_use] + pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { + assert!(num <= 0x7FFF_FFFFu32); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); + self.num = num as i32; + self + } } // Transparent wrappers: just use the internal type @@ -154,6 +191,7 @@ macro_rules! impl_vmstate_transparent { size: mem::size_of::<$type>(), ..<$base as VMState>::BASE }; + const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG; } }; } From 2537f8309885013c4b04ae7b2888591ba0cb6ca7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 29 Dec 2024 12:15:36 +0100 Subject: [PATCH 1263/2892] rust: vmstate: implement Zeroable for VMStateField This shortens a bit the constants. Do not bother using it in the vmstate macros since most of them will go away soon. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 18 +++--------------- rust/qemu-api/src/zeroable.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 2b14d4839d..7652930aff 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -21,8 +21,8 @@ use core::{marker::PhantomData, mem, ptr::NonNull}; -use crate::bindings::VMStateFlags; pub use crate::bindings::{VMStateDescription, VMStateField}; +use crate::bindings::VMStateFlags; /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a @@ -503,20 +503,8 @@ macro_rules! vmstate_fields { static _FIELDS: &[$crate::bindings::VMStateField] = &[ $($field),*, $crate::bindings::VMStateField { - name: ::core::ptr::null(), - err_hint: ::core::ptr::null(), - offset: 0, - size: 0, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: ::core::ptr::null(), - flags: VMStateFlags::VMS_END, - vmsd: ::core::ptr::null(), - version_id: 0, - struct_version_id: 0, - field_exists: None, + flags: $crate::bindings::VMStateFlags::VMS_END, + ..$crate::zeroable::Zeroable::ZERO } ]; _FIELDS.as_ptr() diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 6125aeed8b..57cac96de0 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -49,6 +49,37 @@ unsafe impl Zeroable for crate::bindings::Property { }; } +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for crate::bindings::VMStateFlags { + fn default() -> Self { + Self(0) + } +} + +unsafe impl Zeroable for crate::bindings::VMStateFlags { + const ZERO: Self = Self(0); +} + +unsafe impl Zeroable for crate::bindings::VMStateField { + const ZERO: Self = Self { + name: ptr::null(), + err_hint: ptr::null(), + offset: 0, + size: 0, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: ptr::null(), + flags: Zeroable::ZERO, + vmsd: ptr::null(), + version_id: 0, + struct_version_id: 0, + field_exists: None, + }; +} + unsafe impl Zeroable for crate::bindings::VMStateDescription { const ZERO: Self = Self { name: ptr::null(), From f2cb78bdbe5f9ff61366beb216971a8502456c3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 29 Dec 2024 11:59:34 +0100 Subject: [PATCH 1264/2892] rust: vmstate: implement VMState for scalar types Scalar types are those that have their own VMStateInfo. This poses a problem in that references to VMStateInfo can only be included in associated consts starting with Rust 1.83.0, when the const_refs_static was stabilized. Removing the requirement is done by placing a limited list of VMStateInfos in an enum, and going from enum to &VMStateInfo only when building the VMStateField. The same thing cannot be done with VMS_STRUCT because the set of VMStateDescriptions extends to structs defined by the devices. Therefore, structs and cells cannot yet use vmstate_of!. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 128 ++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 7652930aff..a262c315da 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -22,7 +22,10 @@ use core::{marker::PhantomData, mem, ptr::NonNull}; pub use crate::bindings::{VMStateDescription, VMStateField}; -use crate::bindings::VMStateFlags; +use crate::{ + bindings::{self, VMStateFlags}, + zeroable::Zeroable, +}; /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a @@ -61,6 +64,70 @@ macro_rules! call_func_with_field { }; } +/// Workaround for lack of `const_refs_static`: references to global variables +/// can be included in a `static`, but not in a `const`; unfortunately, this +/// is exactly what would go in the `VMStateField`'s `info` member. +/// +/// This enum contains the contents of the `VMStateField`'s `info` member, +/// but as an `enum` instead of a pointer. +#[allow(non_camel_case_types)] +pub enum VMStateFieldType { + null, + vmstate_info_bool, + vmstate_info_int8, + vmstate_info_int16, + vmstate_info_int32, + vmstate_info_int64, + vmstate_info_uint8, + vmstate_info_uint16, + vmstate_info_uint32, + vmstate_info_uint64, + vmstate_info_timer, +} + +/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType` +/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`. +#[macro_export] +macro_rules! info_enum_to_ref { + ($e:expr) => { + unsafe { + match $e { + $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(), + $crate::vmstate::VMStateFieldType::vmstate_info_bool => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int8 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int16 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int32 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int64 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64) + } + $crate::vmstate::VMStateFieldType::vmstate_info_timer => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer) + } + } + } + }; +} + /// A trait for types that can be included in a device's migration stream. It /// provides the base contents of a `VMStateField` (minus the name and offset). /// @@ -69,6 +136,12 @@ macro_rules! call_func_with_field { /// The contents of this trait go straight into structs that are parsed by C /// code and used to introspect into other structs. Be careful. pub unsafe trait VMState { + /// The `info` member of a `VMStateField` is a pointer and as such cannot + /// yet be included in the [`BASE`](VMState::BASE) associated constant; + /// this is only allowed by Rust 1.83.0 and newer. For now, include the + /// member as an enum which is stored in a separate constant. + const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null; + /// The base contents of a `VMStateField` (minus the name and offset) for /// the type that is implementing the trait. const BASE: VMStateField; @@ -83,6 +156,12 @@ pub unsafe trait VMState { }; } +/// Internal utility function to retrieve a type's `VMStateFieldType`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_scalar_type(_: PhantomData) -> VMStateFieldType { + T::SCALAR_TYPE +} + /// Internal utility function to retrieve a type's `VMStateField`; /// used by [`vmstate_of!`](crate::vmstate_of). pub const fn vmstate_base(_: PhantomData) -> VMStateField { @@ -99,6 +178,15 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// Return the `VMStateField` for a field of a struct. The field must be /// visible in the current scope. /// +/// Only a limited set of types is supported out of the box: +/// * scalar types (integer and `bool`) +/// * the C struct `QEMUTimer` +/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, +/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) +/// * a raw pointer to any of the above +/// * a `NonNull` pointer or a `Box` for any of the above +/// * an array of any of the above +/// /// In order to support other types, the trait `VMState` must be implemented /// for them. #[macro_export] @@ -109,8 +197,14 @@ macro_rules! vmstate_of { .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, offset: $crate::offset_of!($struct_name, $field_name), - // Compute most of the VMStateField from the type of the field. $(.num_offset: $crate::offset_of!($struct_name, $num),)? + // The calls to `call_func_with_field!` are the magic that + // computes most of the VMStateField from the type of the field. + info: $crate::info_enum_to_ref!($crate::call_func_with_field!( + $crate::vmstate::vmstate_scalar_type, + $struct_name, + $field_name + )), ..$crate::call_func_with_field!( $crate::vmstate::vmstate_base, $struct_name, @@ -187,6 +281,7 @@ impl VMStateField { macro_rules! impl_vmstate_transparent { ($type:ty where $base:tt: VMState $($where:tt)*) => { unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE; const BASE: VMStateField = VMStateField { size: mem::size_of::<$type>(), ..<$base as VMState>::BASE @@ -201,6 +296,33 @@ impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); +// Scalar types using predefined VMStateInfos + +macro_rules! impl_vmstate_scalar { + ($info:ident, $type:ty$(, $varray_flag:ident)?) => { + unsafe impl VMState for $type { + const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info; + const BASE: VMStateField = VMStateField { + size: mem::size_of::<$type>(), + flags: VMStateFlags::VMS_SINGLE, + ..Zeroable::ZERO + }; + $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? + } + }; +} + +impl_vmstate_scalar!(vmstate_info_bool, bool); +impl_vmstate_scalar!(vmstate_info_int8, i8); +impl_vmstate_scalar!(vmstate_info_int16, i16); +impl_vmstate_scalar!(vmstate_info_int32, i32); +impl_vmstate_scalar!(vmstate_info_int64, i64); +impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); +impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); +impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); +impl_vmstate_scalar!(vmstate_info_uint64, u64); +impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer); + // Pointer types using the underlying type's VMState plus VMS_POINTER // Note that references are not supported, though references to cells // could be allowed. @@ -208,6 +330,7 @@ impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); macro_rules! impl_vmstate_pointer { ($type:ty where $base:tt: VMState $($where:tt)*) => { unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const SCALAR_TYPE: VMStateFieldType = ::SCALAR_TYPE; const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); } }; @@ -225,6 +348,7 @@ impl_vmstate_pointer!(Box where T: VMState); // VMS_ARRAY/VMS_ARRAY_OF_POINTER unsafe impl VMState for [T; N] { + const SCALAR_TYPE: VMStateFieldType = ::SCALAR_TYPE; const BASE: VMStateField = ::BASE.with_array_flag(N); } From 00f89716a8858f6b9274dd4067740fb40212e88b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 21 Dec 2024 13:42:41 +0100 Subject: [PATCH 1265/2892] rust: vmstate: add public utility macros to implement VMState Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 61 ++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index a262c315da..9ac699b73b 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -4,13 +4,18 @@ //! Helper macros to declare migration state for device models. //! -//! This module includes three families of macros: +//! This module includes four families of macros: //! //! * [`vmstate_unused!`](crate::vmstate_unused) and //! [`vmstate_of!`](crate::vmstate_of), which are used to express the //! migration format for a struct. This is based on the [`VMState`] trait, //! which is defined by all migrateable types. //! +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and +//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with +//! the definition of the [`VMState`] trait (respectively for transparent +//! structs and for `bilge`-defined types) +//! //! * helper macros to declare a device model state struct, in particular //! [`vmstate_subsections`](crate::vmstate_subsections) and //! [`vmstate_fields`](crate::vmstate_fields). @@ -134,7 +139,9 @@ macro_rules! info_enum_to_ref { /// # Safety /// /// The contents of this trait go straight into structs that are parsed by C -/// code and used to introspect into other structs. Be careful. +/// code and used to introspect into other structs. Generally, you don't need +/// to implement it except via macros that do it for you, such as +/// `impl_vmstate_bitsized!`. pub unsafe trait VMState { /// The `info` member of a `VMStateField` is a pointer and as such cannot /// yet be included in the [`BASE`](VMState::BASE) associated constant; @@ -188,7 +195,9 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented -/// for them. +/// for them. The macros +/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized) +/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. #[macro_export] macro_rules! vmstate_of { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { @@ -276,6 +285,32 @@ impl VMStateField { } } +/// This macro can be used (by just passing it a type) to forward the `VMState` +/// trait to the first field of a tuple. This is a workaround for lack of +/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::vmstate::impl_vmstate_forward; +/// pub struct Fifo([u8; 16]); +/// impl_vmstate_forward!(Fifo); +/// ``` +#[macro_export] +macro_rules! impl_vmstate_forward { + // This is similar to impl_vmstate_transparent below, but it + // uses the same trick as vmstate_of! to obtain the type of + // the first field of the tuple + ($tuple:ty) => { + unsafe impl $crate::vmstate::VMState for $tuple { + const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = + $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0); + const BASE: $crate::bindings::VMStateField = + $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); + } + }; +} + // Transparent wrappers: just use the internal type macro_rules! impl_vmstate_transparent { @@ -296,6 +331,26 @@ impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); +#[macro_export] +macro_rules! impl_vmstate_bitsized { + ($type:ty) => { + unsafe impl $crate::vmstate::VMState for $type { + const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::SCALAR_TYPE; + const BASE: $crate::bindings::VMStateField = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::BASE; + const VARRAY_FLAG: $crate::bindings::VMStateFlags = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::VARRAY_FLAG; + } + }; +} + // Scalar types using predefined VMStateInfos macro_rules! impl_vmstate_scalar { From 9a2ba4882d320a650b4f98f92b49bb45956d227e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 21 Dec 2024 16:28:29 +0100 Subject: [PATCH 1266/2892] rust: qemu_api: add vmstate_struct It is not type safe, but it's the best that can be done without const_refs_static. It can also be used with BqlCell and BqlRefCell. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 9ac699b73b..d3a9cffdf3 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -628,6 +628,39 @@ macro_rules! vmstate_array_of_pointer_to_struct { }}; } +// FIXME: including the `vmsd` field in a `const` is not possible without +// the const_refs_static feature (stabilized in Rust 1.83.0). Without it, +// it is not possible to use VMS_STRUCT in a transparent manner using +// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe, +// VMSTATE_STRUCT includes $type only for documentation purposes; it +// is checked against $field_name and $struct_name, but not against $vmsd +// which is what really would matter. +#[doc(alias = "VMSTATE_STRUCT")] +#[macro_export] +macro_rules! vmstate_struct { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => { + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + $(.num_offset: $crate::offset_of!($struct_name, $num),)? + offset: { + $crate::assert_field_type!($struct_name, $field_name, $type); + $crate::offset_of!($struct_name, $field_name) + }, + size: ::core::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, + vmsd: unsafe { $vmsd }, + ..$crate::zeroable::Zeroable::ZERO $( + .with_varray_flag($crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num)) + $(.with_varray_multiply($factor))?)? + } + }; +} + #[doc(alias = "VMSTATE_CLOCK_V")] #[macro_export] macro_rules! vmstate_clock_v { From b800a3132194014928cfbf9d79062da77ea70fee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 8 Dec 2024 12:19:05 +0100 Subject: [PATCH 1267/2892] rust: pl011: switch vmstate to new-style macros Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 3 ++- rust/hw/char/pl011/src/device_class.rs | 36 +++++++++++++------------- rust/hw/char/pl011/src/lib.rs | 6 +++++ 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 4f1080ff19..a1a522fdcd 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -15,7 +15,7 @@ use qemu_api::{ qemu_irq, sysbus_connect_irq, sysbus_mmio_map, sysbus_realize_and_unref, CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, }, - c_str, + c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, qdev::{DeviceImpl, DeviceState, Property}, @@ -61,6 +61,7 @@ impl DeviceId { #[repr(transparent)] #[derive(Debug, Default)] pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); +impl_vmstate_forward!(Fifo); impl Fifo { const fn len(&self) -> u32 { diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 7f3ca89507..e0d3532e95 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,11 +6,11 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_subsections, vmstate_uint32, - vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, + bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_of, vmstate_subsections, + vmstate_unused, zeroable::Zeroable, }; -use crate::device::{PL011State, PL011_FIFO_DEPTH}; +use crate::device::PL011State; extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { unsafe { @@ -52,21 +52,21 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { post_load: Some(pl011_post_load), fields: vmstate_fields! { vmstate_unused!(core::mem::size_of::()), - vmstate_uint32!(flags, PL011State), - vmstate_uint32!(line_control, PL011State), - vmstate_uint32!(receive_status_error_clear, PL011State), - vmstate_uint32!(control, PL011State), - vmstate_uint32!(dmacr, PL011State), - vmstate_uint32!(int_enabled, PL011State), - vmstate_uint32!(int_level, PL011State), - vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), - vmstate_uint32!(ilpr, PL011State), - vmstate_uint32!(ibrd, PL011State), - vmstate_uint32!(fbrd, PL011State), - vmstate_uint32!(ifl, PL011State), - vmstate_uint32!(read_pos, PL011State), - vmstate_uint32!(read_count, PL011State), - vmstate_uint32!(read_trigger, PL011State), + vmstate_of!(PL011State, flags), + vmstate_of!(PL011State, line_control), + vmstate_of!(PL011State, receive_status_error_clear), + vmstate_of!(PL011State, control), + vmstate_of!(PL011State, dmacr), + vmstate_of!(PL011State, int_enabled), + vmstate_of!(PL011State, int_level), + vmstate_of!(PL011State, read_fifo), + vmstate_of!(PL011State, ilpr), + vmstate_of!(PL011State, ibrd), + vmstate_of!(PL011State, fbrd), + vmstate_of!(PL011State, ifl), + vmstate_of!(PL011State, read_pos), + vmstate_of!(PL011State, read_count), + vmstate_of!(PL011State, read_trigger), }, subsections: vmstate_subsections! { VMSTATE_PL011_CLOCK diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 0a89d393e0..f30f9850ad 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -106,6 +106,7 @@ pub mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. use bilge::prelude::*; + use qemu_api::impl_vmstate_bitsized; /// Receive Status Register / Data Register common error bits /// @@ -172,6 +173,7 @@ pub mod registers { pub errors: Errors, _reserved: u16, } + impl_vmstate_bitsized!(Data); impl Data { // bilge is not very const-friendly, unfortunately @@ -208,6 +210,7 @@ pub mod registers { pub errors: Errors, _reserved_unpredictable: u24, } + impl_vmstate_bitsized!(ReceiveStatusErrorClear); impl ReceiveStatusErrorClear { pub fn set_from_data(&mut self, data: Data) { @@ -280,6 +283,7 @@ pub mod registers { pub ring_indicator: bool, _reserved_zero_no_modify: u23, } + impl_vmstate_bitsized!(Flags); impl Flags { pub fn reset(&mut self) { @@ -354,6 +358,7 @@ pub mod registers { /// 31:8 - Reserved, do not modify, read as zero. _reserved_zero_no_modify: u24, } + impl_vmstate_bitsized!(LineControl); impl LineControl { pub fn reset(&mut self) { @@ -498,6 +503,7 @@ pub mod registers { /// 31:16 - Reserved, do not modify, read as zero. _reserved_zero_no_modify2: u16, } + impl_vmstate_bitsized!(Control); impl Control { pub fn reset(&mut self) { From 9d4899496b555751c8ea4155d6da4fc3dbd7edae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Jan 2025 10:29:27 +0100 Subject: [PATCH 1268/2892] rust: vmstate: remove translation of C vmstate macros Keep vmstate_clock!; because it uses a field of type VMStateDescription, it cannot be converted to the VMState trait without access to the const_refs_static feature. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 274 +++-------------------------------- 1 file changed, 23 insertions(+), 251 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index d3a9cffdf3..120933e60d 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -21,8 +21,8 @@ //! [`vmstate_fields`](crate::vmstate_fields). //! //! * direct equivalents to the C macros declared in -//! `include/migration/vmstate.h`. These are not type-safe and should not be -//! used if the equivalent functionality is available with `vmstate_of!`. +//! `include/migration/vmstate.h`. These are not type-safe and only provide +//! functionality that is missing from `vmstate_of!`. use core::{marker::PhantomData, mem, ptr::NonNull}; @@ -407,223 +407,16 @@ unsafe impl VMState for [T; N] { const BASE: VMStateField = ::BASE.with_array_flag(N); } -#[doc(alias = "VMSTATE_UNUSED_BUFFER")] -#[macro_export] -macro_rules! vmstate_unused_buffer { - ($field_exists_fn:expr, $version_id:expr, $size:expr) => {{ - $crate::bindings::VMStateField { - name: c_str!("unused").as_ptr(), - err_hint: ::core::ptr::null(), - offset: 0, - size: $size, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, - flags: VMStateFlags::VMS_BUFFER, - vmsd: ::core::ptr::null(), - version_id: $version_id, - struct_version_id: 0, - field_exists: $field_exists_fn, - } - }}; -} - -#[doc(alias = "VMSTATE_UNUSED_V")] -#[macro_export] -macro_rules! vmstate_unused_v { - ($version_id:expr, $size:expr) => {{ - $crate::vmstate_unused_buffer!(None, $version_id, $size) - }}; -} - #[doc(alias = "VMSTATE_UNUSED")] #[macro_export] macro_rules! vmstate_unused { ($size:expr) => {{ - $crate::vmstate_unused_v!(0, $size) - }}; -} - -#[doc(alias = "VMSTATE_SINGLE_TEST")] -#[macro_export] -macro_rules! vmstate_single_test { - ($field_name:ident, $struct_name:ty, $field_exists_fn:expr, $version_id:expr, $info:expr, $size:expr) => {{ $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - err_hint: ::core::ptr::null(), - offset: $crate::offset_of!($struct_name, $field_name), + name: $crate::c_str!("unused").as_ptr(), size: $size, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: unsafe { $info }, - flags: VMStateFlags::VMS_SINGLE, - vmsd: ::core::ptr::null(), - version_id: $version_id, - struct_version_id: 0, - field_exists: $field_exists_fn, - } - }}; -} - -#[doc(alias = "VMSTATE_SINGLE")] -#[macro_export] -macro_rules! vmstate_single { - ($field_name:ident, $struct_name:ty, $version_id:expr, $info:expr, $size:expr) => {{ - $crate::vmstate_single_test!($field_name, $struct_name, None, $version_id, $info, $size) - }}; -} - -#[doc(alias = "VMSTATE_UINT32_V")] -#[macro_export] -macro_rules! vmstate_uint32_v { - ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ - $crate::vmstate_single!( - $field_name, - $struct_name, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32), - ::core::mem::size_of::() - ) - }}; -} - -#[doc(alias = "VMSTATE_UINT32")] -#[macro_export] -macro_rules! vmstate_uint32 { - ($field_name:ident, $struct_name:ty) => {{ - $crate::vmstate_uint32_v!($field_name, $struct_name, 0) - }}; -} - -#[doc(alias = "VMSTATE_ARRAY")] -#[macro_export] -macro_rules! vmstate_array { - ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr, $info:expr, $size:expr) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - err_hint: ::core::ptr::null(), - offset: $crate::offset_of!($struct_name, $field_name), - size: $size, - start: 0, - num: $length as _, - num_offset: 0, - size_offset: 0, - info: unsafe { $info }, - flags: VMStateFlags::VMS_ARRAY, - vmsd: ::core::ptr::null(), - version_id: $version_id, - struct_version_id: 0, - field_exists: None, - } - }}; -} - -#[doc(alias = "VMSTATE_UINT32_ARRAY_V")] -#[macro_export] -macro_rules! vmstate_uint32_array_v { - ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr) => {{ - $crate::vmstate_array!( - $field_name, - $struct_name, - $length, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32), - ::core::mem::size_of::() - ) - }}; -} - -#[doc(alias = "VMSTATE_UINT32_ARRAY")] -#[macro_export] -macro_rules! vmstate_uint32_array { - ($field_name:ident, $struct_name:ty, $length:expr) => {{ - $crate::vmstate_uint32_array_v!($field_name, $struct_name, $length, 0) - }}; -} - -#[doc(alias = "VMSTATE_STRUCT_POINTER_V")] -#[macro_export] -macro_rules! vmstate_struct_pointer_v { - ($field_name:ident, $struct_name:ty, $version_id:expr, $vmsd:expr, $type:ty) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - err_hint: ::core::ptr::null(), - offset: $crate::offset_of!($struct_name, $field_name), - size: ::core::mem::size_of::<*const $type>(), - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: ::core::ptr::null(), - flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), - vmsd: unsafe { $vmsd }, - version_id: $version_id, - struct_version_id: 0, - field_exists: None, - } - }}; -} - -#[doc(alias = "VMSTATE_ARRAY_OF_POINTER")] -#[macro_export] -macro_rules! vmstate_array_of_pointer { - ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $info:expr, $type:ty) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - version_id: $version_id, - num: $num as _, - info: unsafe { $info }, - size: ::core::mem::size_of::<*const $type>(), - flags: VMStateFlags(VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0), - offset: $crate::offset_of!($struct_name, $field_name), - err_hint: ::core::ptr::null(), - start: 0, - num_offset: 0, - size_offset: 0, - vmsd: ::core::ptr::null(), - struct_version_id: 0, - field_exists: None, - } - }}; -} - -#[doc(alias = "VMSTATE_ARRAY_OF_POINTER_TO_STRUCT")] -#[macro_export] -macro_rules! vmstate_array_of_pointer_to_struct { - ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $vmsd:expr, $type:ty) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - version_id: $version_id, - num: $num as _, - vmsd: unsafe { $vmsd }, - size: ::core::mem::size_of::<*const $type>(), - flags: VMStateFlags( - VMStateFlags::VMS_ARRAY.0 - | VMStateFlags::VMS_STRUCT.0 - | VMStateFlags::VMS_ARRAY_OF_POINTER.0, - ), - offset: $crate::offset_of!($struct_name, $field_name), - err_hint: ::core::ptr::null(), - start: 0, - num_offset: 0, - size_offset: 0, - vmsd: ::core::ptr::null(), - struct_version_id: 0, - field_exists: None, + info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, + flags: $crate::bindings::VMStateFlags::VMS_BUFFER, + ..$crate::zeroable::Zeroable::ZERO } }}; } @@ -661,48 +454,27 @@ macro_rules! vmstate_struct { }; } -#[doc(alias = "VMSTATE_CLOCK_V")] -#[macro_export] -macro_rules! vmstate_clock_v { - ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ - $crate::vmstate_struct_pointer_v!( - $field_name, - $struct_name, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_clock), - $crate::bindings::Clock - ) - }}; -} - #[doc(alias = "VMSTATE_CLOCK")] #[macro_export] macro_rules! vmstate_clock { ($field_name:ident, $struct_name:ty) => {{ - $crate::vmstate_clock_v!($field_name, $struct_name, 0) - }}; -} - -#[doc(alias = "VMSTATE_ARRAY_CLOCK_V")] -#[macro_export] -macro_rules! vmstate_array_clock_v { - ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr) => {{ - $crate::vmstate_array_of_pointer_to_struct!( - $field_name, - $struct_name, - $num, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_clock), - $crate::bindings::Clock - ) - }}; -} - -#[doc(alias = "VMSTATE_ARRAY_CLOCK")] -#[macro_export] -macro_rules! vmstate_array_clock { - ($field_name:ident, $struct_name:ty, $num:expr) => {{ - $crate::vmstate_array_clock_v!($field_name, $struct_name, $name, 0) + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: { + $crate::assert_field_type!( + $struct_name, + $field_name, + core::ptr::NonNull<$crate::bindings::Clock> + ); + $crate::offset_of!($struct_name, $field_name) + }, + size: ::core::mem::size_of::<*const $crate::bindings::Clock>(), + flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), + vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, + ..$crate::zeroable::Zeroable::ZERO + } }}; } From 24f0e8d818b931758b6dc47f973a6b1b80ecee1f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Jan 2025 10:30:41 +0100 Subject: [PATCH 1269/2892] rust: vmstate: make order of parameters consistent in vmstate_clock Place struct_name before field_name, similar to offset_of. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device_class.rs | 2 +- rust/qemu-api/src/vmstate.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index e0d3532e95..b052d98803 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -27,7 +27,7 @@ pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { minimum_version_id: 1, needed: Some(pl011_clock_needed), fields: vmstate_fields! { - vmstate_clock!(clock, PL011State), + vmstate_clock!(PL011State, clock), }, ..Zeroable::ZERO }; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 120933e60d..6ac432cf52 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -457,7 +457,7 @@ macro_rules! vmstate_struct { #[doc(alias = "VMSTATE_CLOCK")] #[macro_export] macro_rules! vmstate_clock { - ($field_name:ident, $struct_name:ty) => {{ + ($struct_name:ty, $field_name:ident) => {{ $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() From 7d0520398f7f58214cf5242b34c1b46efa2fcf4f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 Jan 2025 11:25:22 +0100 Subject: [PATCH 1270/2892] rust: prefer NonNull::new to assertions Do not use new_unchecked; the effect is the same, but the code is easier to read and unsafe regions become smaller. Likewise, NonNull::new can be used instead of assertion and followed by as_ref() or as_mut() instead of dereferencing the pointer. Suggested-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 17 +++++------------ rust/hw/char/pl011/src/device_class.rs | 23 +++++++++-------------- rust/hw/char/pl011/src/memory_ops.rs | 9 +++------ rust/qemu-api/src/qdev.rs | 12 +++++------- rust/qemu-api/src/qom.rs | 21 +++++++++++++-------- 5 files changed, 35 insertions(+), 47 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index a1a522fdcd..c0b53f2515 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -593,11 +593,8 @@ pub const IRQMASK: [u32; 6] = [ /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::()); - state.as_ref().can_receive().into() - } + let state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_ref().can_receive().into() } } /// # Safety @@ -608,9 +605,8 @@ pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { /// /// The buffer and size arguments must also be valid. pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { + let mut state = NonNull::new(opaque).unwrap().cast::(); unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); if state.as_ref().loopback_enabled() { return; } @@ -627,11 +623,8 @@ pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - state.as_mut().event(event) - } + let mut state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_mut().event(event) } } /// # Safety diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index b052d98803..6fa14ca0f9 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -12,12 +12,10 @@ use qemu_api::{ use crate::device::PL011State; +#[allow(clippy::missing_const_for_fn)] extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::()); - state.as_ref().migrate_clock - } + let state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_ref().migrate_clock } } /// Migration subsection for [`PL011State`] clock. @@ -33,15 +31,12 @@ pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { }; extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - let result = state.as_mut().post_load(version_id as u32); - if result.is_err() { - -1 - } else { - 0 - } + let mut state = NonNull::new(opaque).unwrap().cast::(); + let result = unsafe { state.as_mut().post_load(version_id as u32) }; + if result.is_err() { + -1 + } else { + 0 } } diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs index c4e8599ba4..a286003d13 100644 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -25,7 +25,7 @@ pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { assert!(!opaque.is_null()); - let mut state = unsafe { NonNull::new_unchecked(opaque.cast::()) }; + let mut state = NonNull::new(opaque).unwrap().cast::(); let val = unsafe { state.as_mut().read(addr, size) }; match val { std::ops::ControlFlow::Break(val) => val, @@ -43,9 +43,6 @@ unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) } unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { - unsafe { - assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - state.as_mut().write(addr, data) - } + let mut state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_mut().write(addr, data) } } diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index a5121e31a3..42429903aa 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -4,7 +4,7 @@ //! Bindings to create devices and access device functionality from Rust. -use std::ffi::CStr; +use std::{ffi::CStr, ptr::NonNull}; pub use bindings::{DeviceClass, DeviceState, Property}; @@ -55,9 +55,8 @@ pub trait DeviceImpl { /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp: *mut *mut Error) { - assert!(!dev.is_null()); - let state = dev.cast::(); - T::REALIZE.unwrap()(unsafe { &mut *state }); + let state = NonNull::new(dev).unwrap().cast::(); + T::REALIZE.unwrap()(unsafe { state.as_ref() }); } /// # Safety @@ -66,9 +65,8 @@ unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { - assert!(!dev.is_null()); - let state = dev.cast::(); - T::RESET.unwrap()(unsafe { &mut *state }); + let mut state = NonNull::new(dev).unwrap().cast::(); + T::RESET.unwrap()(unsafe { state.as_mut() }); } impl ClassInitImpl for T diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 97901fb908..f50ee371aa 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -58,6 +58,7 @@ use std::{ fmt, ops::{Deref, DerefMut}, os::raw::c_void, + ptr::NonNull, }; pub use bindings::{Object, ObjectClass}; @@ -153,27 +154,34 @@ impl fmt::Display for ParentField { } unsafe extern "C" fn rust_instance_init(obj: *mut Object) { + let mut state = NonNull::new(obj).unwrap().cast::(); // SAFETY: obj is an instance of T, since rust_instance_init // is called from QOM core as the instance_init function // for class T - unsafe { T::INSTANCE_INIT.unwrap()(&mut *obj.cast::()) } + unsafe { + T::INSTANCE_INIT.unwrap()(state.as_mut()); + } } unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { + let state = NonNull::new(obj).unwrap().cast::(); // SAFETY: obj is an instance of T, since rust_instance_post_init // is called from QOM core as the instance_post_init function // for class T - T::INSTANCE_POST_INIT.unwrap()(unsafe { &*obj.cast::() }) + T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); } unsafe extern "C" fn rust_class_init>( klass: *mut ObjectClass, _data: *mut c_void, ) { + let mut klass = NonNull::new(klass) + .unwrap() + .cast::<::Class>(); // SAFETY: klass is a T::Class, since rust_class_init // is called from QOM core as the class_init function // for class T - T::class_init(unsafe { &mut *klass.cast::() }) + T::class_init(unsafe { klass.as_mut() }) } unsafe extern "C" fn drop_object(obj: *mut Object) { @@ -581,11 +589,8 @@ pub trait ClassInitImpl { /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_unparent_fn(dev: *mut Object) { - unsafe { - assert!(!dev.is_null()); - let state = core::ptr::NonNull::new_unchecked(dev.cast::()); - T::UNPARENT.unwrap()(state.as_ref()); - } + let state = NonNull::new(dev).unwrap().cast::(); + T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } impl ClassInitImpl for T From efe5719c64c7fd7e85f65dc378de1ec3776ef3ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 12 Nov 2024 21:00:08 +0100 Subject: [PATCH 1271/2892] rust: pl011: remove unnecessary "extern crate" Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index f30f9850ad..d10f0805aa 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -25,10 +25,6 @@ #![allow(clippy::upper_case_acronyms)] #![allow(clippy::result_unit_err)] -extern crate bilge; -extern crate bilge_impl; -extern crate qemu_api; - use qemu_api::c_str; pub mod device; From d1f27ae9ca1c87268b97741c0a2560baa7be4c8b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 14 Nov 2024 17:46:43 +0100 Subject: [PATCH 1272/2892] rust: pl011: hide unnecessarily "pub" items from outside pl011::device The only public interfaces for pl011 are TYPE_PL011 and pl011_create. Remove pub from everything else. Note: the "allow(dead_code)" is removed later. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/char/pl011/src/device_class.rs | 2 +- rust/hw/char/pl011/src/lib.rs | 13 ++++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c0b53f2515..c8496eeb1b 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -573,7 +573,7 @@ impl PL011State { } /// Which bits in the interrupt status matter for each outbound IRQ line ? -pub const IRQMASK: [u32; 6] = [ +const IRQMASK: [u32; 6] = [ /* combined IRQ */ Interrupt::E | Interrupt::MS diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 6fa14ca0f9..2336a76872 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -19,7 +19,7 @@ extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { } /// Migration subsection for [`PL011State`] clock. -pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { +static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { name: c_str!("pl011/clock").as_ptr(), version_id: 1, minimum_version_id: 1, diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index d10f0805aa..2baacba230 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -27,9 +27,11 @@ use qemu_api::c_str; -pub mod device; -pub mod device_class; -pub mod memory_ops; +mod device; +mod device_class; +mod memory_ops; + +pub use device::pl011_create; pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011"); pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); @@ -42,7 +44,7 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); #[allow(non_camel_case_types)] #[repr(u64)] #[derive(Debug, qemu_api_macros::TryInto)] -pub enum RegisterOffset { +enum RegisterOffset { /// Data Register /// /// A write to this register initiates the actual data transmission @@ -98,7 +100,8 @@ pub enum RegisterOffset { //Reserved = 0x04C, } -pub mod registers { +#[allow(dead_code)] +mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. use bilge::prelude::*; From 90f73c2d7fd006ef4cfb12d4146cae07afa861c1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 13 Jan 2025 11:28:18 +0800 Subject: [PATCH 1273/2892] target/loongarch: Add dynamic function access with CSR register With CSR register, dynamic function access is used for CSR register access in TCG mode, so that csr info can be used by other modules. Signed-off-by: Bibo Mao --- .../tcg/insn_trans/trans_privileged.c.inc | 37 +++++++++++++++++-- target/loongarch/tcg/tcg_loongarch.h | 12 ++++++ target/loongarch/tcg/translate.c | 5 +++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 target/loongarch/tcg/tcg_loongarch.h diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 30f9b83fb2..96958bd6c1 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -76,7 +76,7 @@ enum { #define CSR_OFF(NAME) \ CSR_OFF_FLAGS(NAME, 0) -static const CSRInfo csr_info[] = { +static CSRInfo csr_info[] = { CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), CSR_OFF(PRMD), CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), @@ -160,9 +160,9 @@ static bool check_plv(DisasContext *ctx) return false; } -static const CSRInfo *get_csr(unsigned csr_num) +static CSRInfo *get_csr(unsigned csr_num) { - const CSRInfo *csr; + CSRInfo *csr; if (csr_num >= ARRAY_SIZE(csr_info)) { return NULL; @@ -174,6 +174,37 @@ static const CSRInfo *get_csr(unsigned csr_num) return csr; } +static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn, + GenCSRWrite writefn) +{ + CSRInfo *csr; + + csr = get_csr(csr_num); + if (!csr) { + return false; + } + + csr->readfn = readfn; + csr->writefn = writefn; + return true; +} + +#define SET_CSR_FUNC(NAME, read, write) \ + set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write) + +void loongarch_csr_translate_init(void) +{ + SET_CSR_FUNC(ESTAT, NULL, gen_helper_csrwr_estat); + SET_CSR_FUNC(ASID, NULL, gen_helper_csrwr_asid); + SET_CSR_FUNC(PGD, gen_helper_csrrd_pgd, NULL); + SET_CSR_FUNC(PWCL, NULL, gen_helper_csrwr_pwcl); + SET_CSR_FUNC(CPUID, gen_helper_csrrd_cpuid, NULL); + SET_CSR_FUNC(TCFG, NULL, gen_helper_csrwr_tcfg); + SET_CSR_FUNC(TVAL, gen_helper_csrrd_tval, NULL); + SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr); +} +#undef SET_CSR_FUNC + static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) { if ((csr->flags & CSRFL_READONLY) && write) { diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h new file mode 100644 index 0000000000..da2539e995 --- /dev/null +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch TCG interface + * + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H +#define TARGET_LOONGARCH_TCG_LOONGARCH_H + +void loongarch_csr_translate_init(void); + +#endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index 68be999410..3480f54c71 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -16,6 +16,7 @@ #include "exec/log.h" #include "qemu/qemu-print.h" #include "fpu/softfloat.h" +#include "tcg_loongarch.h" #include "translate.h" #include "internals.h" #include "vec.h" @@ -358,4 +359,8 @@ void loongarch_translate_init(void) offsetof(CPULoongArchState, lladdr), "lladdr"); cpu_llval = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, llval), "llval"); + +#ifndef CONFIG_USER_ONLY + loongarch_csr_translate_init(); +#endif } From 3156b1c1e9eb0954c46346595e6b40af13114fd4 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 13 Jan 2025 11:43:44 +0800 Subject: [PATCH 1274/2892] target/loongarch: Remove static CSR function setting Since CSR function setting is done dynamically in TCG mode, remove static CSR function setting here. Signed-off-by: Bibo Mao --- .../tcg/insn_trans/trans_privileged.c.inc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 96958bd6c1..b90e14cd2a 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -82,7 +82,7 @@ static CSRInfo csr_info[] = { CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), CSR_OFF_FLAGS(MISC, CSRFL_READONLY), CSR_OFF(ECFG), - CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat), + CSR_OFF_FLAGS(ESTAT, CSRFL_EXITTB), CSR_OFF(ERA), CSR_OFF(BADV), CSR_OFF_FLAGS(BADI, CSRFL_READONLY), @@ -91,15 +91,15 @@ static CSRInfo csr_info[] = { CSR_OFF(TLBEHI), CSR_OFF(TLBELO0), CSR_OFF(TLBELO1), - CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid), + CSR_OFF_FLAGS(ASID, CSRFL_EXITTB), CSR_OFF(PGDL), CSR_OFF(PGDH), - CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL), - CSR_OFF_FUNCS(PWCL, 0, NULL, gen_helper_csrwr_pwcl), + CSR_OFF_FLAGS(PGD, CSRFL_READONLY), + CSR_OFF(PWCL), CSR_OFF(PWCH), CSR_OFF(STLBPS), CSR_OFF(RVACFG), - CSR_OFF_FUNCS(CPUID, CSRFL_READONLY, gen_helper_csrrd_cpuid, NULL), + CSR_OFF_FLAGS(CPUID, CSRFL_READONLY), CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), @@ -120,10 +120,10 @@ static CSRInfo csr_info[] = { CSR_OFF_ARRAY(SAVE, 14), CSR_OFF_ARRAY(SAVE, 15), CSR_OFF(TID), - CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg), - CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL), + CSR_OFF_FLAGS(TCFG, CSRFL_IO), + CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO), CSR_OFF(CNTC), - CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr), + CSR_OFF_FLAGS(TICLR, CSRFL_IO), CSR_OFF(LLBCTL), CSR_OFF(IMPCTL1), CSR_OFF(IMPCTL2), From 75b2c5da94fc9f94370438d27c179abcc8f424be Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 22 Jan 2025 15:13:41 +0800 Subject: [PATCH 1275/2892] target/loongarch: Add generic csr function type Parameter type TCGv and TCGv_ptr for function GenCSRRead and GenCSRWrite is not used in non-TCG mode. Generic csr function type is added here with parameter void type, so that it passes to compile with non-TCG mode. Signed-off-by: Bibo Mao --- .../tcg/insn_trans/trans_privileged.c.inc | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index b90e14cd2a..0513cac577 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -44,12 +44,13 @@ GEN_FALSE_TRANS(idle) typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); +typedef void (*GenCSRFunc)(void); typedef struct { int offset; int flags; - GenCSRRead readfn; - GenCSRWrite writefn; + GenCSRFunc readfn; + GenCSRFunc writefn; } CSRInfo; enum { @@ -184,8 +185,8 @@ static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn, return false; } - csr->readfn = readfn; - csr->writefn = writefn; + csr->readfn = (GenCSRFunc)readfn; + csr->writefn = (GenCSRFunc)writefn; return true; } @@ -222,6 +223,7 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) { TCGv dest; const CSRInfo *csr; + GenCSRRead readfn; if (check_plv(ctx)) { return false; @@ -233,8 +235,9 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) } else { check_csr_flags(ctx, csr, false); dest = gpr_dst(ctx, a->rd, EXT_NONE); - if (csr->readfn) { - csr->readfn(dest, tcg_env); + readfn = (GenCSRRead)csr->readfn; + if (readfn) { + readfn(dest, tcg_env); } else { tcg_gen_ld_tl(dest, tcg_env, csr->offset); } @@ -247,6 +250,7 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) { TCGv dest, src1; const CSRInfo *csr; + GenCSRWrite writefn; if (check_plv(ctx)) { return false; @@ -262,9 +266,10 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) return false; } src1 = gpr_src(ctx, a->rd, EXT_NONE); - if (csr->writefn) { + writefn = (GenCSRWrite)csr->writefn; + if (writefn) { dest = gpr_dst(ctx, a->rd, EXT_NONE); - csr->writefn(dest, tcg_env, src1); + writefn(dest, tcg_env, src1); } else { dest = tcg_temp_new(); tcg_gen_ld_tl(dest, tcg_env, csr->offset); @@ -278,6 +283,7 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) { TCGv src1, mask, oldv, newv, temp; const CSRInfo *csr; + GenCSRWrite writefn; if (check_plv(ctx)) { return false; @@ -308,8 +314,9 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) tcg_gen_andc_tl(temp, oldv, mask); tcg_gen_or_tl(newv, newv, temp); - if (csr->writefn) { - csr->writefn(oldv, tcg_env, newv); + writefn = (GenCSRWrite)csr->writefn; + if (writefn) { + writefn(oldv, tcg_env, newv); } else { tcg_gen_st_tl(newv, tcg_env, csr->offset); } From d03114ea20e043278306f168bcf306a6605ed4a5 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 22 Jan 2025 15:21:01 +0800 Subject: [PATCH 1276/2892] target/loongarch: Add common header file for CSR registers Common header file csr.h is added here, it can be used by both TCG mode and kvm mode. Signed-off-by: Bibo Mao --- target/loongarch/csr.h | 25 +++++++++++++++++++ .../tcg/insn_trans/trans_privileged.c.inc | 16 +----------- 2 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 target/loongarch/csr.h diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h new file mode 100644 index 0000000000..20d4bf5dc7 --- /dev/null +++ b/target/loongarch/csr.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ + +#ifndef TARGET_LOONGARCH_CSR_H +#define TARGET_LOONGARCH_CSR_H + +#include "cpu-csr.h" + +typedef void (*GenCSRFunc)(void); +enum { + CSRFL_READONLY = (1 << 0), + CSRFL_EXITTB = (1 << 1), + CSRFL_IO = (1 << 2), +}; + +typedef struct { + int offset; + int flags; + GenCSRFunc readfn; + GenCSRFunc writefn; +} CSRInfo; + +#endif /* TARGET_LOONGARCH_CSR_H */ diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 0513cac577..87506ec0dc 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -5,7 +5,7 @@ * LoongArch translation routines for the privileged instructions. */ -#include "cpu-csr.h" +#include "csr.h" #ifdef CONFIG_USER_ONLY @@ -44,20 +44,6 @@ GEN_FALSE_TRANS(idle) typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); -typedef void (*GenCSRFunc)(void); - -typedef struct { - int offset; - int flags; - GenCSRFunc readfn; - GenCSRFunc writefn; -} CSRInfo; - -enum { - CSRFL_READONLY = (1 << 0), - CSRFL_EXITTB = (1 << 1), - CSRFL_IO = (1 << 2), -}; #define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ [LOONGARCH_CSR_##NAME] = { \ From cb6fa4142f883684c7689944020aa95e422e0578 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 16 Jan 2025 14:22:19 +0800 Subject: [PATCH 1277/2892] target/loongarch: Add common source file for CSR register Common source file csr.c is added here, it can be used by both TCG mode and kvm mode. The common code is removed from file tcg/insn_trans/trans_privileged.c.inc to csrc.c Signed-off-by: Bibo Mao --- target/loongarch/csr.c | 114 ++++++++++++++++++ target/loongarch/csr.h | 1 + target/loongarch/meson.build | 1 + .../tcg/insn_trans/trans_privileged.c.inc | 107 ---------------- 4 files changed, 116 insertions(+), 107 deletions(-) create mode 100644 target/loongarch/csr.c diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c new file mode 100644 index 0000000000..62c1815bfb --- /dev/null +++ b/target/loongarch/csr.c @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#include +#include "qemu/osdep.h" +#include "cpu.h" +#include "csr.h" + +#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ + [LOONGARCH_CSR_##NAME] = { \ + .offset = offsetof(CPULoongArchState, CSR_##NAME), \ + .flags = FL, .readfn = RD, .writefn = WR \ + } + +#define CSR_OFF_ARRAY(NAME, N) \ + [LOONGARCH_CSR_##NAME(N)] = { \ + .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ + .flags = 0, .readfn = NULL, .writefn = NULL \ + } + +#define CSR_OFF_FLAGS(NAME, FL) CSR_OFF_FUNCS(NAME, FL, NULL, NULL) +#define CSR_OFF(NAME) CSR_OFF_FLAGS(NAME, 0) + +static CSRInfo csr_info[] = { + CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), + CSR_OFF(PRMD), + CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), + CSR_OFF_FLAGS(MISC, CSRFL_READONLY), + CSR_OFF(ECFG), + CSR_OFF_FLAGS(ESTAT, CSRFL_EXITTB), + CSR_OFF(ERA), + CSR_OFF(BADV), + CSR_OFF_FLAGS(BADI, CSRFL_READONLY), + CSR_OFF(EENTRY), + CSR_OFF(TLBIDX), + CSR_OFF(TLBEHI), + CSR_OFF(TLBELO0), + CSR_OFF(TLBELO1), + CSR_OFF_FLAGS(ASID, CSRFL_EXITTB), + CSR_OFF(PGDL), + CSR_OFF(PGDH), + CSR_OFF_FLAGS(PGD, CSRFL_READONLY), + CSR_OFF(PWCL), + CSR_OFF(PWCH), + CSR_OFF(STLBPS), + CSR_OFF(RVACFG), + CSR_OFF_FLAGS(CPUID, CSRFL_READONLY), + CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), + CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), + CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), + CSR_OFF_ARRAY(SAVE, 0), + CSR_OFF_ARRAY(SAVE, 1), + CSR_OFF_ARRAY(SAVE, 2), + CSR_OFF_ARRAY(SAVE, 3), + CSR_OFF_ARRAY(SAVE, 4), + CSR_OFF_ARRAY(SAVE, 5), + CSR_OFF_ARRAY(SAVE, 6), + CSR_OFF_ARRAY(SAVE, 7), + CSR_OFF_ARRAY(SAVE, 8), + CSR_OFF_ARRAY(SAVE, 9), + CSR_OFF_ARRAY(SAVE, 10), + CSR_OFF_ARRAY(SAVE, 11), + CSR_OFF_ARRAY(SAVE, 12), + CSR_OFF_ARRAY(SAVE, 13), + CSR_OFF_ARRAY(SAVE, 14), + CSR_OFF_ARRAY(SAVE, 15), + CSR_OFF(TID), + CSR_OFF_FLAGS(TCFG, CSRFL_IO), + CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO), + CSR_OFF(CNTC), + CSR_OFF_FLAGS(TICLR, CSRFL_IO), + CSR_OFF(LLBCTL), + CSR_OFF(IMPCTL1), + CSR_OFF(IMPCTL2), + CSR_OFF(TLBRENTRY), + CSR_OFF(TLBRBADV), + CSR_OFF(TLBRERA), + CSR_OFF(TLBRSAVE), + CSR_OFF(TLBRELO0), + CSR_OFF(TLBRELO1), + CSR_OFF(TLBREHI), + CSR_OFF(TLBRPRMD), + CSR_OFF(MERRCTL), + CSR_OFF(MERRINFO1), + CSR_OFF(MERRINFO2), + CSR_OFF(MERRENTRY), + CSR_OFF(MERRERA), + CSR_OFF(MERRSAVE), + CSR_OFF(CTAG), + CSR_OFF_ARRAY(DMW, 0), + CSR_OFF_ARRAY(DMW, 1), + CSR_OFF_ARRAY(DMW, 2), + CSR_OFF_ARRAY(DMW, 3), + CSR_OFF(DBG), + CSR_OFF(DERA), + CSR_OFF(DSAVE), +}; + +CSRInfo *get_csr(unsigned int csr_num) +{ + CSRInfo *csr; + + if (csr_num >= ARRAY_SIZE(csr_info)) { + return NULL; + } + + csr = &csr_info[csr_num]; + if (csr->offset == 0) { + return NULL; + } + + return csr; +} diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h index 20d4bf5dc7..caad832545 100644 --- a/target/loongarch/csr.h +++ b/target/loongarch/csr.h @@ -22,4 +22,5 @@ typedef struct { GenCSRFunc writefn; } CSRInfo; +CSRInfo *get_csr(unsigned int csr_num); #endif /* TARGET_LOONGARCH_CSR_H */ diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build index 7817318287..20bd3e2f0a 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -10,6 +10,7 @@ loongarch_system_ss = ss.source_set() loongarch_system_ss.add(files( 'arch_dump.c', 'cpu_helper.c', + 'csr.c', 'loongarch-qmp-cmds.c', 'machine.c', )) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 87506ec0dc..3afa23af79 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -45,99 +45,6 @@ GEN_FALSE_TRANS(idle) typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); -#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ - [LOONGARCH_CSR_##NAME] = { \ - .offset = offsetof(CPULoongArchState, CSR_##NAME), \ - .flags = FL, .readfn = RD, .writefn = WR \ - } - -#define CSR_OFF_ARRAY(NAME, N) \ - [LOONGARCH_CSR_##NAME(N)] = { \ - .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ - .flags = 0, .readfn = NULL, .writefn = NULL \ - } - -#define CSR_OFF_FLAGS(NAME, FL) \ - CSR_OFF_FUNCS(NAME, FL, NULL, NULL) - -#define CSR_OFF(NAME) \ - CSR_OFF_FLAGS(NAME, 0) - -static CSRInfo csr_info[] = { - CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), - CSR_OFF(PRMD), - CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), - CSR_OFF_FLAGS(MISC, CSRFL_READONLY), - CSR_OFF(ECFG), - CSR_OFF_FLAGS(ESTAT, CSRFL_EXITTB), - CSR_OFF(ERA), - CSR_OFF(BADV), - CSR_OFF_FLAGS(BADI, CSRFL_READONLY), - CSR_OFF(EENTRY), - CSR_OFF(TLBIDX), - CSR_OFF(TLBEHI), - CSR_OFF(TLBELO0), - CSR_OFF(TLBELO1), - CSR_OFF_FLAGS(ASID, CSRFL_EXITTB), - CSR_OFF(PGDL), - CSR_OFF(PGDH), - CSR_OFF_FLAGS(PGD, CSRFL_READONLY), - CSR_OFF(PWCL), - CSR_OFF(PWCH), - CSR_OFF(STLBPS), - CSR_OFF(RVACFG), - CSR_OFF_FLAGS(CPUID, CSRFL_READONLY), - CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), - CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), - CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), - CSR_OFF_ARRAY(SAVE, 0), - CSR_OFF_ARRAY(SAVE, 1), - CSR_OFF_ARRAY(SAVE, 2), - CSR_OFF_ARRAY(SAVE, 3), - CSR_OFF_ARRAY(SAVE, 4), - CSR_OFF_ARRAY(SAVE, 5), - CSR_OFF_ARRAY(SAVE, 6), - CSR_OFF_ARRAY(SAVE, 7), - CSR_OFF_ARRAY(SAVE, 8), - CSR_OFF_ARRAY(SAVE, 9), - CSR_OFF_ARRAY(SAVE, 10), - CSR_OFF_ARRAY(SAVE, 11), - CSR_OFF_ARRAY(SAVE, 12), - CSR_OFF_ARRAY(SAVE, 13), - CSR_OFF_ARRAY(SAVE, 14), - CSR_OFF_ARRAY(SAVE, 15), - CSR_OFF(TID), - CSR_OFF_FLAGS(TCFG, CSRFL_IO), - CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO), - CSR_OFF(CNTC), - CSR_OFF_FLAGS(TICLR, CSRFL_IO), - CSR_OFF(LLBCTL), - CSR_OFF(IMPCTL1), - CSR_OFF(IMPCTL2), - CSR_OFF(TLBRENTRY), - CSR_OFF(TLBRBADV), - CSR_OFF(TLBRERA), - CSR_OFF(TLBRSAVE), - CSR_OFF(TLBRELO0), - CSR_OFF(TLBRELO1), - CSR_OFF(TLBREHI), - CSR_OFF(TLBRPRMD), - CSR_OFF(MERRCTL), - CSR_OFF(MERRINFO1), - CSR_OFF(MERRINFO2), - CSR_OFF(MERRENTRY), - CSR_OFF(MERRERA), - CSR_OFF(MERRSAVE), - CSR_OFF(CTAG), - CSR_OFF_ARRAY(DMW, 0), - CSR_OFF_ARRAY(DMW, 1), - CSR_OFF_ARRAY(DMW, 2), - CSR_OFF_ARRAY(DMW, 3), - CSR_OFF(DBG), - CSR_OFF(DERA), - CSR_OFF(DSAVE), -}; - static bool check_plv(DisasContext *ctx) { if (ctx->plv == MMU_PLV_USER) { @@ -147,20 +54,6 @@ static bool check_plv(DisasContext *ctx) return false; } -static CSRInfo *get_csr(unsigned csr_num) -{ - CSRInfo *csr; - - if (csr_num >= ARRAY_SIZE(csr_info)) { - return NULL; - } - csr = &csr_info[csr_num]; - if (csr->offset == 0) { - return NULL; - } - return csr; -} - static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn, GenCSRWrite writefn) { From b5b13eb712e73545c4323d497ef1fabf7e63c360 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 16 Jan 2025 19:09:25 +0800 Subject: [PATCH 1278/2892] target/loongarch: Set unused flag with CSR registers On LA464, some CSR registers are not used such as CSR_SAVE8 - CSR_SAVE15, also CSR registers relative with MCE is not used now. Flag CSRFL_UNUSED is added for these registers, so that it will not dumped. In order to keep compatiblity, these CSR registers are not removed since it is used in vmstate already. Signed-off-by: Bibo Mao --- target/loongarch/cpu.c | 30 +++++++++++++++++++++++++++++- target/loongarch/csr.c | 13 +++++++++++++ target/loongarch/csr.h | 2 ++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index d611a60470..a744010332 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -19,7 +19,7 @@ #include "cpu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" -#include "cpu-csr.h" +#include "csr.h" #ifndef CONFIG_USER_ONLY #include "system/reset.h" #endif @@ -375,6 +375,33 @@ static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch) return MMU_DA_IDX; } +static void loongarch_la464_init_csr(Object *obj) +{ +#ifndef CONFIG_USER_ONLY + static bool initialized; + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + CPULoongArchState *env = &cpu->env; + int i, num; + + if (!initialized) { + initialized = true; + num = FIELD_EX64(env->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM); + for (i = num; i < 16; i++) { + set_csr_flag(LOONGARCH_CSR_SAVE(i), CSRFL_UNUSED); + } + set_csr_flag(LOONGARCH_CSR_IMPCTL1, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_IMPCTL2, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRCTL, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRINFO1, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRINFO2, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRENTRY, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRERA, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRSAVE, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_CTAG, CSRFL_UNUSED); + } +#endif +} + static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); @@ -470,6 +497,7 @@ static void loongarch_la464_initfn(Object *obj) env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); + loongarch_la464_init_csr(obj); loongarch_cpu_post_init(obj); } diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c index 62c1815bfb..87bd24e8cd 100644 --- a/target/loongarch/csr.c +++ b/target/loongarch/csr.c @@ -112,3 +112,16 @@ CSRInfo *get_csr(unsigned int csr_num) return csr; } + +bool set_csr_flag(unsigned int csr_num, int flag) +{ + CSRInfo *csr; + + csr = get_csr(csr_num); + if (!csr) { + return false; + } + + csr->flags |= flag; + return true; +} diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h index caad832545..deb1aacc33 100644 --- a/target/loongarch/csr.h +++ b/target/loongarch/csr.h @@ -13,6 +13,7 @@ enum { CSRFL_READONLY = (1 << 0), CSRFL_EXITTB = (1 << 1), CSRFL_IO = (1 << 2), + CSRFL_UNUSED = (1 << 3), }; typedef struct { @@ -23,4 +24,5 @@ typedef struct { } CSRInfo; CSRInfo *get_csr(unsigned int csr_num); +bool set_csr_flag(unsigned int csr_num, int flag); #endif /* TARGET_LOONGARCH_CSR_H */ From 3215fe8528de45a1794f0314623cc10bd8e8e19f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 16 Jan 2025 19:21:31 +0800 Subject: [PATCH 1279/2892] target/loongarch: Dump all generic CSR registers CSR registers is import system control registers, it had better dump all CSR registers when VM is running in system mode. Here is dump output example of CSR registers: CSR000: CRMD b4 PRMD 4 EUEN 0 MISC 0 CSR004: ECFG 71c1c ESTAT 0 ERA 9000000002c31300 BADV 12022c0e0 CSR008: BADI 2b0000 CSR012: EENTRY 90000000046b0000 CSR016: TLBIDX ffffffff8e000228 TLBEHI 120228000 TLBELO0 400000016f19001f TLBELO1 400000016f1a401f CSR024: ASID a0004 PGDL 90000001016f0000 PGDH 9000000004680000 PGD 0 CSR028: PWCL 5e56e PWCH 2e4 STLBPS e RVACFG 0 CSR032: CPUID 0 PRCFG1 72f8 PRCFG2 3ffff000 PRCFG3 8073f2 CSR048: SAVE0 0 SAVE1 af9c SAVE2 12010d6a8 SAVE3 8300000 CSR052: SAVE4 0 SAVE5 0 SAVE6 0 SAVE7 0 CSR064: TID 0 TCFG 8f0ca15 TVAL 4cefd8b CNTC fffffffffe688aaa CSR068: TICLR 0 CSR096: LLBCTL 1 CSR136: TLBRENTRY 46ba000 TLBRBADV ffff8000130d81e2 TLBRERA 9000000003585cb8 TLBRSAVE ffff8000130d81e0 CSR140: TLBRELO0 1fe00043 TLBRELO1 40 TLBREHI ffff8000130d800e TLBRPRMD 0 CSR384: DMW0 8000000000000001 DMW1 9000000000000011 DMW2 0 DMW3 0 Signed-off-by: Bibo Mao --- target/loongarch/cpu.c | 66 ++++++++++++++++++++++++++++++++---------- target/loongarch/csr.c | 2 ++ target/loongarch/csr.h | 1 + 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index a744010332..e91f4a5239 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -793,6 +793,54 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) return oc; } +static void loongarch_cpu_dump_csr(CPUState *cs, FILE *f) +{ +#ifndef CONFIG_USER_ONLY + CPULoongArchState *env = cpu_env(cs); + CSRInfo *csr_info; + int64_t *addr; + int i, j, len, col = 0; + + qemu_fprintf(f, "\n"); + + /* Dump all generic CSR register */ + for (i = 0; i < LOONGARCH_CSR_DBG; i++) { + csr_info = get_csr(i); + if (!csr_info || (csr_info->flags & CSRFL_UNUSED)) { + if (i == (col + 3)) { + qemu_fprintf(f, "\n"); + } + + continue; + } + + if ((i > (col + 3)) || (i == col)) { + col = i & ~3; + qemu_fprintf(f, " CSR%03d:", col); + } + + addr = (void *)env + csr_info->offset; + qemu_fprintf(f, " %s ", csr_info->name); + len = strlen(csr_info->name); + for (; len < 6; len++) { + qemu_fprintf(f, " "); + } + + qemu_fprintf(f, "%" PRIx64, *addr); + j = find_last_bit((void *)addr, BITS_PER_LONG) & (BITS_PER_LONG - 1); + len += j / 4 + 1; + for (; len < 22; len++) { + qemu_fprintf(f, " "); + } + + if (i == (col + 3)) { + qemu_fprintf(f, "\n"); + } + } + qemu_fprintf(f, "\n"); +#endif +} + static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) { CPULoongArchState *env = cpu_env(cs); @@ -812,22 +860,8 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } - qemu_fprintf(f, "CRMD=%016" PRIx64 "\n", env->CSR_CRMD); - qemu_fprintf(f, "PRMD=%016" PRIx64 "\n", env->CSR_PRMD); - qemu_fprintf(f, "EUEN=%016" PRIx64 "\n", env->CSR_EUEN); - qemu_fprintf(f, "ESTAT=%016" PRIx64 "\n", env->CSR_ESTAT); - qemu_fprintf(f, "ERA=%016" PRIx64 "\n", env->CSR_ERA); - qemu_fprintf(f, "BADV=%016" PRIx64 "\n", env->CSR_BADV); - qemu_fprintf(f, "BADI=%016" PRIx64 "\n", env->CSR_BADI); - qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY); - qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 "," - " PRCFG3=%016" PRIx64 "\n", - env->CSR_PRCFG1, env->CSR_PRCFG2, env->CSR_PRCFG3); - qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY); - qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV); - qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA); - qemu_fprintf(f, "TCFG=%016" PRIx64 "\n", env->CSR_TCFG); - qemu_fprintf(f, "TVAL=%016" PRIx64 "\n", env->CSR_TVAL); + /* csr */ + loongarch_cpu_dump_csr(cs, f); /* fpr */ if (flags & CPU_DUMP_FPU) { diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c index 87bd24e8cd..7ea0a30450 100644 --- a/target/loongarch/csr.c +++ b/target/loongarch/csr.c @@ -9,12 +9,14 @@ #define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ [LOONGARCH_CSR_##NAME] = { \ + .name = (stringify(NAME)), \ .offset = offsetof(CPULoongArchState, CSR_##NAME), \ .flags = FL, .readfn = RD, .writefn = WR \ } #define CSR_OFF_ARRAY(NAME, N) \ [LOONGARCH_CSR_##NAME(N)] = { \ + .name = (stringify(NAME##N)), \ .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ .flags = 0, .readfn = NULL, .writefn = NULL \ } diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h index deb1aacc33..81a656baae 100644 --- a/target/loongarch/csr.h +++ b/target/loongarch/csr.h @@ -17,6 +17,7 @@ enum { }; typedef struct { + const char *name; int offset; int flags; GenCSRFunc readfn; From 5f01c60879f0a1dd540c2c25757a57908847e055 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 20 Jan 2025 22:20:41 +0100 Subject: [PATCH 1280/2892] linux-user: netlink: Add missing IFA_PROTO to host_to_target_data_addr_rtattr() Fix this warning: Unknown host IFA type: 11 While adding IFA_PROTO, convert all IFA_XXX values over to QEMU_IFA_XXX values to avoid a build failure on Ubuntu 22.04 (kernel v5.18 which does not know IFA_PROTO yet). Signed-off-by: Helge Deller Reviewed-by: Laurent Vivier --- linux-user/fd-trans.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index c04a97c73a..2e714c8e56 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -31,6 +31,22 @@ #include "fd-trans.h" #include "signal-common.h" +enum { + QEMU_IFA_UNSPEC, + QEMU_IFA_ADDRESS, + QEMU_IFA_LOCAL, + QEMU_IFA_LABEL, + QEMU_IFA_BROADCAST, + QEMU_IFA_ANYCAST, + QEMU_IFA_CACHEINFO, + QEMU_IFA_MULTICAST, + QEMU_IFA_FLAGS, + QEMU_IFA_RT_PRIORITY, + QEMU_IFA_TARGET_NETNSID, + QEMU_IFA_PROTO, + QEMU__IFA__MAX, +}; + enum { QEMU_IFLA_BR_UNSPEC, QEMU_IFLA_BR_FORWARD_DELAY, @@ -1138,20 +1154,21 @@ static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr) switch (rtattr->rta_type) { /* binary: depends on family type */ - case IFA_ADDRESS: - case IFA_LOCAL: + case QEMU_IFA_ADDRESS: + case QEMU_IFA_LOCAL: + case QEMU_IFA_PROTO: break; /* string */ - case IFA_LABEL: + case QEMU_IFA_LABEL: break; /* u32 */ - case IFA_FLAGS: - case IFA_BROADCAST: + case QEMU_IFA_FLAGS: + case QEMU_IFA_BROADCAST: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; /* struct ifa_cacheinfo */ - case IFA_CACHEINFO: + case QEMU_IFA_CACHEINFO: ci = RTA_DATA(rtattr); ci->ifa_prefered = tswap32(ci->ifa_prefered); ci->ifa_valid = tswap32(ci->ifa_valid); @@ -1398,8 +1415,8 @@ static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) { switch (rtattr->rta_type) { /* binary: depends on family type */ - case IFA_LOCAL: - case IFA_ADDRESS: + case QEMU_IFA_LOCAL: + case QEMU_IFA_ADDRESS: break; default: qemu_log_mask(LOG_UNIMP, "Unknown target IFA type: %d\n", From b97f8d1fa5f783d1ff436b52b29612ecb8793f93 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 29 Nov 2024 09:53:23 +0100 Subject: [PATCH 1281/2892] linux-user: Use unique error messages for cmsg parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using the same error message for two different code paths as it complicates determining the one which actually triggered. Signed-off-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 78c7c0b34e..a157abc40c 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1827,7 +1827,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, *dst = tswap32(*dst); } } else { - qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", + qemu_log_mask(LOG_UNIMP, "Unsupported target ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(data, target_data, len); } @@ -2049,7 +2049,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, default: unimplemented: - qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", + qemu_log_mask(LOG_UNIMP, "Unsupported host ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(target_data, data, MIN(len, tgt_len)); if (tgt_len > len) { From 017fc6620f2513c1d0217289c48be4c51e0167a7 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sun, 19 Jan 2025 03:20:03 +0100 Subject: [PATCH 1282/2892] linux-user: netlink: Add IP_PKTINFO cmsg parsing Fixes those warnings: Unsupported host ancillary data: 0/8 Signed-off-by: Helge Deller Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 10 ++++++++++ linux-user/syscall_defs.h | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a157abc40c..df8609b4d8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1998,6 +1998,16 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, (void *) &errh->offender, sizeof(errh->offender)); break; } + case IP_PKTINFO: + { + struct in_pktinfo *pkti = data; + struct target_in_pktinfo *target_pi = target_data; + + __put_user(pkti->ipi_ifindex, &target_pi->ipi_ifindex); + target_pi->ipi_spec_dst.s_addr = pkti->ipi_spec_dst.s_addr; + target_pi->ipi_addr.s_addr = pkti->ipi_addr.s_addr; + break; + } default: goto unimplemented; } diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index faad9147c9..86d773add7 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2622,6 +2622,12 @@ struct target_ucred { abi_uint gid; }; +struct target_in_pktinfo { + abi_int ipi_ifindex; + struct target_in_addr ipi_spec_dst; + struct target_in_addr ipi_addr; +}; + typedef abi_int target_timer_t; #define TARGET_SIGEV_MAX_SIZE 64 From f65464ce6d00fdec726dbd9f8c8c543c3feb2924 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sun, 19 Jan 2025 05:26:10 +0100 Subject: [PATCH 1283/2892] linux-user: netlink: Add emulation of IP_MULTICAST_IF Add IP_MULTICAST_IF and share the code with IP_ADD_MEMBERSHIP / IP_DROP_MEMBERSHIP. Sharing the code makes sense, because the manpage of ip(7) says: IP_MULTICAST_IF (since Linux 1.2) Set the local device for a multicast socket. The argument for setsockopt(2) is an ip_mreqn or (since Linux 3.5) ip_mreq structure similar to IP_ADD_MEMBERSHIP, or an in_addr structure. (The kernel determines which structure is being passed based on the size passed in optlen.) For getsockopt(2), the argument is an in_addr structure. Signed-off-by: Helge Deller Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index df8609b4d8..6ee02383da 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2130,16 +2130,23 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, } ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); break; + case IP_MULTICAST_IF: case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: { struct ip_mreqn ip_mreq; struct target_ip_mreqn *target_smreqn; + int min_size; QEMU_BUILD_BUG_ON(sizeof(struct ip_mreq) != sizeof(struct target_ip_mreq)); - if (optlen < sizeof (struct target_ip_mreq) || + if (optname == IP_MULTICAST_IF) { + min_size = sizeof(struct in_addr); + } else { + min_size = sizeof(struct target_ip_mreq); + } + if (optlen < min_size || optlen > sizeof (struct target_ip_mreqn)) { return -TARGET_EINVAL; } @@ -2149,13 +2156,14 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, return -TARGET_EFAULT; } ip_mreq.imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; - ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; - if (optlen == sizeof(struct target_ip_mreqn)) { - ip_mreq.imr_ifindex = tswapal(target_smreqn->imr_ifindex); - optlen = sizeof(struct ip_mreqn); + if (optlen >= sizeof(struct target_ip_mreq)) { + ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; + if (optlen >= sizeof(struct target_ip_mreqn)) { + __put_user(target_smreqn->imr_ifindex, &ip_mreq.imr_ifindex); + optlen = sizeof(struct ip_mreqn); + } } unlock_user(target_smreqn, optval_addr, 0); - ret = get_errno(setsockopt(sockfd, level, optname, &ip_mreq, optlen)); break; } From cc9a83155dde604020b4b4b4ad2926d5924b1375 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 20 Jan 2025 22:22:31 +0100 Subject: [PATCH 1284/2892] linux-user: netlink: add netlink neighbour emulation Fixes various warnings in the testsuite while building gupnp: gssdp-net-DEBUG: Failed to send netlink message: Operation not supported gupnp-context-DEBUG: Mismatch between host header and host IP (example.com, expected: 127.0.0.1) gupnp-context-DEBUG: Mismatch between host header and host port (80, expected 4711) gupnp-context-DEBUG: Mismatch between host header and host IP (192.168.1.2, expected: 127.0.0.1) gupnp-context-DEBUG: Mismatch between host header and host IP (fe80::01, expected: 127.0.0.1) gupnp-context-DEBUG: Mismatch between host header and host port (80, expected 4711) gupnp-context-DEBUG: Failed to parse HOST header from request: Invalid IPv6 address ?[fe80::01%1]? in URI gupnp-context-DEBUG: Failed to parse HOST header from request: Invalid IPv6 address ?[fe80::01%eth0]? in URI gupnp-context-DEBUG: Failed to parse HOST header from request: Could not parse port ?:1? in URI gupnp-context-DEBUG: Mismatch between host header and host IP (example.com, expected: ::1) gupnp-context-DEBUG: Mismatch between host header and host port (80, expected 4711) gupnp-context-DEBUG: Mismatch between host header and host IP (example.com, expected: ::1) gupnp-context-DEBUG: Mismatch between host header and host port (80, expected 4711) gupnp-context-DEBUG: Mismatch between host header and host IP (example.com, expected: ::1) Signed-off-by: Helge Deller Reviewed-by: Laurent Vivier --- linux-user/fd-trans.c | 100 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index 2e714c8e56..621e2248b4 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -25,12 +25,16 @@ #ifdef CONFIG_RTNETLINK #include #include +#include #endif #include "qemu.h" #include "user-internals.h" #include "fd-trans.h" #include "signal-common.h" +#define NDM_RTA(r) ((struct rtattr*)(((char*)(r)) + \ + NLMSG_ALIGN(sizeof(struct ndmsg)))) + enum { QEMU_IFA_UNSPEC, QEMU_IFA_ADDRESS, @@ -1226,6 +1230,35 @@ static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr) return 0; } +static abi_long host_to_target_data_neigh_rtattr(struct rtattr *rtattr) +{ + struct nda_cacheinfo *ndac; + uint32_t *u32; + + switch (rtattr->rta_type) { + case NDA_UNSPEC: + case NDA_DST: + case NDA_LLADDR: + break; + case NDA_PROBES: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + case NDA_CACHEINFO: + ndac = RTA_DATA(rtattr); + ndac->ndm_confirmed = tswap32(ndac->ndm_confirmed); + ndac->ndm_used = tswap32(ndac->ndm_used); + ndac->ndm_updated = tswap32(ndac->ndm_updated); + ndac->ndm_refcnt = tswap32(ndac->ndm_refcnt); + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown host to target NEIGH type: %d\n", + rtattr->rta_type); + break; + } + return 0; +} + static abi_long host_to_target_link_rtattr(struct rtattr *rtattr, uint32_t rtattr_len) { @@ -1247,12 +1280,20 @@ static abi_long host_to_target_route_rtattr(struct rtattr *rtattr, host_to_target_data_route_rtattr); } +static abi_long host_to_target_neigh_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + return host_to_target_for_each_rtattr(rtattr, rtattr_len, + host_to_target_data_neigh_rtattr); +} + static abi_long host_to_target_data_route(struct nlmsghdr *nlh) { uint32_t nlmsg_len; struct ifinfomsg *ifi; struct ifaddrmsg *ifa; struct rtmsg *rtm; + struct ndmsg *ndm; nlmsg_len = nlh->nlmsg_len; switch (nlh->nlmsg_type) { @@ -1279,6 +1320,17 @@ static abi_long host_to_target_data_route(struct nlmsghdr *nlh) nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); } break; + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + case RTM_GETNEIGH: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ndm))) { + ndm = NLMSG_DATA(nlh); + ndm->ndm_ifindex = tswap32(ndm->ndm_ifindex); + ndm->ndm_state = tswap16(ndm->ndm_state); + host_to_target_neigh_rtattr(NDM_RTA(ndm), + nlmsg_len - NLMSG_LENGTH(sizeof(*ndm))); + } + break; case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: @@ -1426,6 +1478,35 @@ static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) return 0; } +static abi_long target_to_host_data_neigh_rtattr(struct rtattr *rtattr) +{ + struct nda_cacheinfo *ndac; + uint32_t *u32; + + switch (rtattr->rta_type) { + case NDA_UNSPEC: + case NDA_DST: + case NDA_LLADDR: + break; + case NDA_PROBES: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + case NDA_CACHEINFO: + ndac = RTA_DATA(rtattr); + ndac->ndm_confirmed = tswap32(ndac->ndm_confirmed); + ndac->ndm_used = tswap32(ndac->ndm_used); + ndac->ndm_updated = tswap32(ndac->ndm_updated); + ndac->ndm_refcnt = tswap32(ndac->ndm_refcnt); + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown target NEIGH type: %d\n", + rtattr->rta_type); + break; + } + return 0; +} + static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -1464,6 +1545,13 @@ static void target_to_host_addr_rtattr(struct rtattr *rtattr, target_to_host_data_addr_rtattr); } +static void target_to_host_neigh_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + target_to_host_for_each_rtattr(rtattr, rtattr_len, + target_to_host_data_neigh_rtattr); +} + static void target_to_host_route_rtattr(struct rtattr *rtattr, uint32_t rtattr_len) { @@ -1476,6 +1564,7 @@ static abi_long target_to_host_data_route(struct nlmsghdr *nlh) struct ifinfomsg *ifi; struct ifaddrmsg *ifa; struct rtmsg *rtm; + struct ndmsg *ndm; switch (nlh->nlmsg_type) { case RTM_NEWLINK: @@ -1502,6 +1591,17 @@ static abi_long target_to_host_data_route(struct nlmsghdr *nlh) NLMSG_LENGTH(sizeof(*ifa))); } break; + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + case RTM_GETNEIGH: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ndm))) { + ndm = NLMSG_DATA(nlh); + ndm->ndm_ifindex = tswap32(ndm->ndm_ifindex); + ndm->ndm_state = tswap16(ndm->ndm_state); + target_to_host_neigh_rtattr(NDM_RTA(ndm), nlh->nlmsg_len - + NLMSG_LENGTH(sizeof(*ndm))); + } + break; case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: From 3719acc273865744b885ad9bcb141b4496c31887 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 29 Nov 2024 10:11:52 +0100 Subject: [PATCH 1285/2892] linux-user: netlink: Add missing QEMU_IFLA entries This fixes the following qemu warnings when building debian gupnp package: Unknown host QEMU_IFLA type: 61 Unknown host QEMU_IFLA type: 58 Unknown host QEMU_IFLA type: 59 Unknown host QEMU_IFLA type: 60 Unknown host QEMU_IFLA type: 32820 QEMU_IFLA type 32820 is actually NLA_NESTED | QEMU_IFLA_PROP_LIST (a nested entry), which is why rta_type needs to be masked with NLA_TYPE_MASK. Signed-off-by: Helge Deller Reviewed-by: Laurent Vivier --- linux-user/fd-trans.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index 621e2248b4..f83d1f79d5 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -161,6 +161,14 @@ enum { QEMU_IFLA_PROTO_DOWN_REASON, QEMU_IFLA_PARENT_DEV_NAME, QEMU_IFLA_PARENT_DEV_BUS_NAME, + QEMU_IFLA_GRO_MAX_SIZE, + QEMU_IFLA_TSO_MAX_SIZE, + QEMU_IFLA_TSO_MAX_SEGS, + QEMU_IFLA_ALLMULTI, + QEMU_IFLA_DEVLINK_PORT, + QEMU_IFLA_GSO_IPV4_MAX_SIZE, + QEMU_IFLA_GRO_IPV4_MAX_SIZE, + QEMU_IFLA_DPLL_PIN, QEMU___IFLA_MAX }; @@ -1002,6 +1010,22 @@ static abi_long host_to_target_data_vfinfo_nlattr(struct nlattr *nlattr, return 0; } +static abi_long host_to_target_data_prop_nlattr(struct nlattr *nlattr, + void *context) +{ + switch (nlattr->nla_type) { + /* string */ + case QEMU_IFLA_ALT_IFNAME: + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown host PROP type: %d\n", + nlattr->nla_type); + break; + } + return 0; +} + + static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -1010,7 +1034,7 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) struct rtnl_link_ifmap *map; struct linkinfo_context li_context; - switch (rtattr->rta_type) { + switch (rtattr->rta_type & NLA_TYPE_MASK) { /* binary stream */ case QEMU_IFLA_ADDRESS: case QEMU_IFLA_BROADCAST: @@ -1048,6 +1072,12 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) case QEMU_IFLA_CARRIER_DOWN_COUNT: case QEMU_IFLA_MIN_MTU: case QEMU_IFLA_MAX_MTU: + case QEMU_IFLA_GRO_MAX_SIZE: + case QEMU_IFLA_TSO_MAX_SIZE: + case QEMU_IFLA_TSO_MAX_SEGS: + case QEMU_IFLA_ALLMULTI: + case QEMU_IFLA_GSO_IPV4_MAX_SIZE: + case QEMU_IFLA_GRO_IPV4_MAX_SIZE: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; @@ -1143,6 +1173,10 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, NULL, host_to_target_data_vfinfo_nlattr); + case QEMU_IFLA_PROP_LIST: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_prop_nlattr); default: qemu_log_mask(LOG_UNIMP, "Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); From d0ad4118abb1434d6788d9ac5a1612103f88d360 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 22 Jan 2025 15:00:53 +0100 Subject: [PATCH 1286/2892] hw/hppa: Support up to 256 GiB RAM on 64-bit machines Allow up to 256 GB RAM, which is the maximum a rp8440 machine (the very last 64-bit PA-RISC machine) physically supports. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- hw/hppa/hppa_hardware.h | 2 ++ hw/hppa/machine.c | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index a9be7bb851..a276240967 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -49,4 +49,6 @@ #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) */ +#define RAM_MAP_HIGH 0x0100000000 /* memory above 3.75 GB is mapped here */ + #endif diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 8230f43e41..4bcc66cd6f 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -283,16 +283,13 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type)); } - /* - * For now, treat address layout as if PSW_W is clear. - * TODO: create a proper hppa64 board model and load elf64 firmware. - */ + /* Initialize memory */ if (hppa_is_pa20(&cpu[0]->env)) { translate = translate_pa20; - ram_max = 0xf0000000; /* 3.75 GB (limited by 32-bit firmware) */ + ram_max = 256 * GiB; /* like HP rp8440 */ } else { translate = translate_pa10; - ram_max = 0xf0000000; /* 3.75 GB (32-bit CPU) */ + ram_max = FIRMWARE_START; /* 3.75 GB (32-bit CPU) */ } soft_power_reg = translate(NULL, HPA_POWER_BUTTON); @@ -320,7 +317,22 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) info_report("Max RAM size limited to %" PRIu64 " MB", ram_max / MiB); machine->ram_size = ram_max; } - memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + if (machine->ram_size <= FIRMWARE_START) { + /* contiguous memory up to 3.75 GB RAM */ + memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + } else { + /* non-contiguous: Memory above 3.75 GB is mapped at RAM_MAP_HIGH */ + MemoryRegion *mem_region; + mem_region = g_new(MemoryRegion, 2); + memory_region_init_alias(&mem_region[0], &addr_space->parent_obj, + "LowMem", machine->ram, 0, FIRMWARE_START); + memory_region_init_alias(&mem_region[1], &addr_space->parent_obj, + "HighMem", machine->ram, FIRMWARE_START, + machine->ram_size - FIRMWARE_START); + memory_region_add_subregion_overlap(addr_space, 0, &mem_region[0], -1); + memory_region_add_subregion_overlap(addr_space, RAM_MAP_HIGH, + &mem_region[1], -1); + } return translate; } From c656f293dfe31661e4252d78cfa6cab6372ad5ca Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 22 Jan 2025 17:15:00 +0100 Subject: [PATCH 1287/2892] hw/hppa: Fix booting Linux kernel with initrd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 20f7b890173b ("hw/hppa: Reset vCPUs calling resettable_reset()") broke booting the Linux kernel with initrd which may have been provided on the command line. The problem is, that the mentioned commit zeroes out initial registers which were preset with addresses for the Linux kernel and initrd. Fix it by adding proper variables which are set shortly before starting the firmware. Signed-off-by: Helge Deller Fixes: 20f7b890173b ("hw/hppa: Reset vCPUs calling resettable_reset()") Cc: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/hppa/machine.c | 48 +++++++++++++++++++---------------------------- target/hppa/cpu.h | 4 ++++ 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 4bcc66cd6f..0dd1908214 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -356,7 +356,6 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, uint64_t kernel_entry = 0, kernel_low, kernel_high; MemoryRegion *addr_space = get_system_memory(); MemoryRegion *rom_region; - unsigned int smp_cpus = machine->smp.cpus; SysBusDevice *s; /* SCSI disk setup. */ @@ -482,8 +481,8 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, kernel_low, kernel_high, kernel_entry, size / KiB); if (kernel_cmdline) { - cpu[0]->env.gr[24] = 0x4000; - pstrcpy_targphys("cmdline", cpu[0]->env.gr[24], + cpu[0]->env.cmdline_or_bootorder = 0x4000; + pstrcpy_targphys("cmdline", cpu[0]->env.cmdline_or_bootorder, TARGET_PAGE_SIZE, kernel_cmdline); } @@ -513,32 +512,22 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, } load_image_targphys(initrd_filename, initrd_base, initrd_size); - cpu[0]->env.gr[23] = initrd_base; - cpu[0]->env.gr[22] = initrd_base + initrd_size; + cpu[0]->env.initrd_base = initrd_base; + cpu[0]->env.initrd_end = initrd_base + initrd_size; } } if (!kernel_entry) { /* When booting via firmware, tell firmware if we want interactive - * mode (kernel_entry=1), and to boot from CD (gr[24]='d') - * or hard disc * (gr[24]='c'). + * mode (kernel_entry=1), and to boot from CD (cmdline_or_bootorder='d') + * or hard disc (cmdline_or_bootorder='c'). */ kernel_entry = machine->boot_config.has_menu ? machine->boot_config.menu : 0; - cpu[0]->env.gr[24] = machine->boot_config.order[0]; + cpu[0]->env.cmdline_or_bootorder = machine->boot_config.order[0]; } - /* We jump to the firmware entry routine and pass the - * various parameters in registers. After firmware initialization, - * firmware will start the Linux kernel with ramdisk and cmdline. - */ - cpu[0]->env.gr[26] = machine->ram_size; - cpu[0]->env.gr[25] = kernel_entry; - - /* tell firmware how many SMP CPUs to present in inventory table */ - cpu[0]->env.gr[21] = smp_cpus; - - /* tell firmware fw_cfg port */ - cpu[0]->env.gr[19] = FW_CFG_IO_BASE; + /* Keep initial kernel_entry for first boot */ + cpu[0]->env.kernel_entry = kernel_entry; } /* @@ -675,18 +664,19 @@ static void hppa_machine_reset(MachineState *ms, ResetType type) cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000; } - /* already initialized by machine_hppa_init()? */ - if (cpu[0]->env.gr[26] == ms->ram_size) { - return; - } - cpu[0]->env.gr[26] = ms->ram_size; - cpu[0]->env.gr[25] = 0; /* no firmware boot menu */ - cpu[0]->env.gr[24] = 'c'; - /* gr22/gr23 unused, no initrd while reboot. */ + cpu[0]->env.gr[25] = cpu[0]->env.kernel_entry; + cpu[0]->env.gr[24] = cpu[0]->env.cmdline_or_bootorder; + cpu[0]->env.gr[23] = cpu[0]->env.initrd_base; + cpu[0]->env.gr[22] = cpu[0]->env.initrd_end; cpu[0]->env.gr[21] = smp_cpus; - /* tell firmware fw_cfg port */ cpu[0]->env.gr[19] = FW_CFG_IO_BASE; + + /* reset static fields to avoid starting Linux kernel & initrd on reboot */ + cpu[0]->env.kernel_entry = 0; + cpu[0]->env.initrd_base = 0; + cpu[0]->env.initrd_end = 0; + cpu[0]->env.cmdline_or_bootorder = 'c'; } static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 083d4f5a56..beea42d105 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -268,6 +268,10 @@ typedef struct CPUArchState { struct {} end_reset_fields; bool is_pa20; + + target_ulong kernel_entry; /* Linux kernel was loaded here */ + target_ulong cmdline_or_bootorder; + target_ulong initrd_base, initrd_end; } CPUHPPAState; /** From 8a139ae719616d85d835528a35f41eb23bfa54c7 Mon Sep 17 00:00:00 2001 From: Kenneth Jia Date: Thu, 12 Dec 2024 20:42:04 +0800 Subject: [PATCH 1288/2892] hw/arm/aspeed: fix connect_serial_hds_to_uarts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the loop, we need ignore the index increase when uart == uart_chosen We should increase the index only after we allocate a serial. Signed-off-by: Kenneth Jia Fixes: d2b3eaefb4d7 ("aspeed: Refactor UART init for multi-SoC machines") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/5f9b0c53f1644922ba85522046e92f4c@asus.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a18d4ed1fb..2662465ada 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -364,11 +364,11 @@ static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; aspeed_soc_uart_set_chr(s, uart_chosen, serial_hd(0)); - for (int i = 1, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + for (int i = 1, uart = sc->uarts_base; i < sc->uarts_num; uart++) { if (uart == uart_chosen) { continue; } - aspeed_soc_uart_set_chr(s, uart, serial_hd(i)); + aspeed_soc_uart_set_chr(s, uart, serial_hd(i++)); } } From 134d9e5c0c4ae2fe64817a185730ec8b7835d573 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 14 Nov 2024 17:48:38 +0800 Subject: [PATCH 1289/2892] hw/sd/sdhci: Introduce a new Write Protected pin inverted property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Write Protect pin of SDHCI model is default active low to match the SDHCI spec. So, write enable the bit 19 should be 1 and write protected the bit 19 should be 0 at the Present State Register (0x24). However, some boards are design Write Protected pin active high. In other words, write enable the bit 19 should be 0 and write protected the bit 19 should be 1 at the Present State Register (0x24). To support it, introduces a new "wp-inverted" property and set it false by default. Signed-off-by: Jamin Lin Acked-by: Cédric Le Goater Acked-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20241114094839.4128404-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/sd/sdhci.c | 6 ++++++ include/hw/sd/sdhci.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 318587ff57..99dd4a4e95 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -274,6 +274,10 @@ static void sdhci_set_readonly(DeviceState *dev, bool level) { SDHCIState *s = (SDHCIState *)dev; + if (s->wp_inverted) { + level = !level; + } + if (level) { s->prnsts &= ~SDHC_WRITE_PROTECT; } else { @@ -1555,6 +1559,8 @@ static const Property sdhci_sysbus_properties[] = { false), DEFINE_PROP_LINK("dma", SDHCIState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_BOOL("wp-inverted", SDHCIState, + wp_inverted, false), }; static void sdhci_sysbus_init(Object *obj) diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index 6cd2822f1d..38c08e2859 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -100,6 +100,11 @@ struct SDHCIState { uint8_t sd_spec_version; uint8_t uhs_mode; uint8_t vendor; /* For vendor specific functionality */ + /* + * Write Protect pin default active low for detecting SD card + * to be protected. Set wp_inverted to invert the signal. + */ + bool wp_inverted; }; typedef struct SDHCIState SDHCIState; From bf8a471a38774800d77f58949bcaea4ca26390a7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 14 Nov 2024 17:48:39 +0800 Subject: [PATCH 1290/2892] hw/arm/aspeed: Invert sdhci write protected pin for AST2600 EVB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Write Protect pin of SDHCI model is default active low to match the SDHCI spec. So, write enable the bit 19 should be 1 and write protected the bit 19 should be 0 at the Present State Register (0x24). According to the design of AST2600 EVB, the Write Protected pin is active high by default. To support it, introduces a new "sdhci_wp_inverted" property in ASPEED MACHINE State and set it true for AST2600 EVB and set "wp_inverted" property true of sdhci-generic model. Signed-off-by: Jamin Lin Reviewed-by: Andrew Jeffery Acked-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20241114094839.4128404-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 7 +++++++ include/hw/arm/aspeed.h | 1 + 2 files changed, 8 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 2662465ada..53a859a6e4 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -409,6 +409,12 @@ static void aspeed_machine_init(MachineState *machine) OBJECT(get_system_memory()), &error_abort); object_property_set_link(OBJECT(bmc->soc), "dram", OBJECT(machine->ram), &error_abort); + if (amc->sdhci_wp_inverted) { + for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { + object_property_set_bool(OBJECT(&bmc->soc->sdhci.slots[i]), + "wp-inverted", true, &error_abort); + } + } if (machine->kernel_filename) { /* * When booting with a -kernel command line there is no u-boot @@ -1415,6 +1421,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON | ASPEED_MAC3_ON; + amc->sdhci_wp_inverted = true; amc->i2c_init = ast2600_evb_i2c_init; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index cbeacb214c..9cae45a1c9 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -39,6 +39,7 @@ struct AspeedMachineClass { uint32_t macs_mask; void (*i2c_init)(AspeedMachineState *bmc); uint32_t uart_default; + bool sdhci_wp_inverted; }; From ef2385bb3797a594c85c14452d89a2d884d1df44 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jan 2025 14:44:53 +0800 Subject: [PATCH 1291/2892] hw/timer/aspeed: Refactor Timer Callbacks for SoC-Specific Implementations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The register set have a significant change in AST2700. The TMC00-TMC3C are used for TIMER0 and TMC40-TMC7C are used for TIMER1. In additional, TMC20-TMC3C and TMC60-TMC7C are reserved registers for TIMER0 and TIMER1, respectively. Besides, each TIMER has their own control and interrupt status register. In other words, users are able to set control and interrupt status for TIMER0 in one register. Both aspeed_timer_read and aspeed_timer_write callback functions are not compatible AST2700. Introduce common read and write functions for ASPEED timers. Modify the aspeed_timer_read and aspeed_timer_write functions to delegate to SoC-specific callbacks first. Update the AST2400, AST2500, AST2600 and AST1030 specific read and write functions to call the common implementations for common register accesses. This refactoring improves the organization of call delegation and prepares the codebase for future SoC-specific specializations, such as the AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20250113064455.1660564-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/timer/aspeed_timer.c | 55 ++++++++++++++++++++++++++++++----------- hw/timer/trace-events | 2 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 4868651ad4..24ba40cbe9 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -239,9 +239,8 @@ static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) return value; } -static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) +static uint64_t aspeed_timer_read_common(AspeedTimerCtrlState *s, hwaddr offset) { - AspeedTimerCtrlState *s = opaque; const int reg = (offset & 0xf) / 4; uint64_t value; @@ -256,10 +255,11 @@ static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg); break; default: - value = ASPEED_TIMER_GET_CLASS(s)->read(s, offset); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; break; } - trace_aspeed_timer_read(offset, size, value); return value; } @@ -431,12 +431,11 @@ static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value) trace_aspeed_timer_set_ctrl2(value); } -static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) +static void aspeed_timer_write_common(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) { const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); const int reg = (offset & 0xf) / 4; - AspeedTimerCtrlState *s = opaque; switch (offset) { /* Control Registers */ @@ -451,11 +450,25 @@ static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv); break; default: - ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); break; } } +static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedTimerCtrlState *s = ASPEED_TIMER(opaque); + return ASPEED_TIMER_GET_CLASS(s)->read(s, offset); +} + +static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AspeedTimerCtrlState *s = ASPEED_TIMER(opaque); + ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value); +} + static const MemoryRegionOps aspeed_timer_ops = { .read = aspeed_timer_read, .write = aspeed_timer_write, @@ -475,12 +488,15 @@ static uint64_t aspeed_2400_timer_read(AspeedTimerCtrlState *s, hwaddr offset) break; case 0x38: case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); value = 0; break; + default: + value = aspeed_timer_read_common(s, offset); + break; } + trace_aspeed_timer_read(offset, value); return value; } @@ -495,10 +511,12 @@ static void aspeed_2400_timer_write(AspeedTimerCtrlState *s, hwaddr offset, break; case 0x38: case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); break; + default: + aspeed_timer_write_common(s, offset, value); + break; } } @@ -514,12 +532,15 @@ static uint64_t aspeed_2500_timer_read(AspeedTimerCtrlState *s, hwaddr offset) value = s->ctrl3 & BIT(0); break; case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); value = 0; break; + default: + value = aspeed_timer_read_common(s, offset); + break; } + trace_aspeed_timer_read(offset, value); return value; } @@ -548,8 +569,7 @@ static void aspeed_2500_timer_write(AspeedTimerCtrlState *s, hwaddr offset, break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, offset); + aspeed_timer_write_common(s, offset, value); break; } } @@ -564,12 +584,15 @@ static uint64_t aspeed_2600_timer_read(AspeedTimerCtrlState *s, hwaddr offset) break; case 0x38: case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); value = 0; break; + default: + value = aspeed_timer_read_common(s, offset); + break; } + trace_aspeed_timer_read(offset, value); return value; } @@ -586,10 +609,12 @@ static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset, aspeed_timer_set_ctrl(s, s->ctrl & ~tv); break; case 0x38: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); break; + default: + aspeed_timer_write_common(s, offset, value); + break; } } diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 5cfc369fba..c5b6db49f5 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -31,7 +31,7 @@ aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32 aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32 -aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64 +aspeed_timer_read(uint64_t offset, uint64_t value) "From 0x%" PRIx64 ": 0x%" PRIx64 # armv7m_systick.c systick_reload(void) "systick reload" From 8bc691bed881b37079405984334a59e0b6abba01 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jan 2025 14:44:54 +0800 Subject: [PATCH 1292/2892] hw/timer/aspeed: Add AST2700 Support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timer controller include 8 sets of 32-bit decrement counters, based on either PCLK or 1MHZ clock and the design of timer controller between AST2600 and AST2700 are almost the same. TIMER0 – TIMER7 has their own individual control and interrupt status register. In other words, users are able to set timer control in register TMC10 with different TIMER base address and clear timer control and interrupt status in register TMC14 with different TIMER base address. Introduce new "aspeed_2700_timer_read" and "aspeed_2700_timer_write" callback functions and a new ast2700 class to support AST2700. The base address of TIMER0 to TIMER7 as following. Base Address of Timer 0 = 0x12C1_0000 Base Address of Timer 1 = 0x12C1_0040 Base Address of Timer 2 = 0x12C1_0080 Base Address of Timer 3 = 0x12C1_00C0 Base Address of Timer 4 = 0x12C1_0100 Base Address of Timer 5 = 0x12C1_0140 Base Address of Timer 6 = 0x12C1_0180 Base Address of Timer 7 = 0x12C1_01C0 The register address space of each TIMER is "0x40" , and uses the following formula to get the index and register of each TIMER. timer_index = offset >> 6; timer_offset = offset & 0x3f; The TMC010 is a counter control set and interrupt status register. Write "1" to TMC10[3:0] will set the specific bits to "1". Introduce a new "aspeed_2700_timer_set_ctrl" function to handle this register behavior. The TMC014 is a counter control clear and interrupt status register, to clear the specific bits to "0", it should write "1" to TMC14[3:0] on the same bit position. Introduce a new "aspeed_2700_timer_clear_ctrl" function to handle this register behavior. TMC014 does not support read operation. Signed-off-by: Jamin Lin Acked-by: Cédric Le Goater Link: https://lore.kernel.org/r/20250113064455.1660564-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/timer/aspeed_timer.c | 208 ++++++++++++++++++++++++++++++++ include/hw/timer/aspeed_timer.h | 1 + 2 files changed, 209 insertions(+) diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 24ba40cbe9..ecda49574e 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -618,6 +618,197 @@ static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset, } } +static void aspeed_2700_timer_set_ctrl(AspeedTimerCtrlState *s, int index, + uint32_t reg) +{ + const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt); + const uint8_t external_clock_mask = BIT(op_external_clock); + const uint8_t pulse_enable_mask = BIT(op_pulse_enable); + const uint8_t enable_mask = BIT(op_enable); + AspeedTimer *t; + uint8_t t_old; + uint8_t t_new; + int shift; + + /* + * Only 1 will set the specific bits to 1 + * Handle a dependency between the 'enable' and remaining three + * configuration bits - i.e. if more than one bit in the control set has + * set, including the 'enable' bit, perform configuration and then + * enable the timer. + * Interrupt Status bit should not be set. + */ + + t = &s->timers[index]; + shift = index * TIMER_CTRL_BITS; + + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK; + t_new = reg & TIMER_CTRL_MASK; + + if (!(t_old & external_clock_mask) && + (t_new & external_clock_mask)) { + aspeed_timer_ctrl_external_clock(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 1); + } + + if (!(t_old & overflow_interrupt_mask) && + (t_new & overflow_interrupt_mask)) { + aspeed_timer_ctrl_overflow_interrupt(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 1); + } + + + if (!(t_old & pulse_enable_mask) && + (t_new & pulse_enable_mask)) { + aspeed_timer_ctrl_pulse_enable(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 1); + } + + /* If we are enabling, do so last */ + if (!(t_old & enable_mask) && + (t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 1); + } +} + +static void aspeed_2700_timer_clear_ctrl(AspeedTimerCtrlState *s, int index, + uint32_t reg) +{ + const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt); + const uint8_t external_clock_mask = BIT(op_external_clock); + const uint8_t pulse_enable_mask = BIT(op_pulse_enable); + const uint8_t enable_mask = BIT(op_enable); + AspeedTimer *t; + uint8_t t_old; + uint8_t t_new; + int shift; + + /* + * Only 1 will clear the specific bits to 0 + * Handle a dependency between the 'enable' and remaining three + * configuration bits - i.e. if more than one bit in the control set has + * clear, including the 'enable' bit, then disable the timer and perform + * configuration + */ + + t = &s->timers[index]; + shift = index * TIMER_CTRL_BITS; + + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK; + t_new = reg & TIMER_CTRL_MASK; + + /* If we are disabling, do so first */ + if ((t_old & enable_mask) && + (t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 0); + } + + if ((t_old & external_clock_mask) && + (t_new & external_clock_mask)) { + aspeed_timer_ctrl_external_clock(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 0); + } + + if ((t_old & overflow_interrupt_mask) && + (t_new & overflow_interrupt_mask)) { + aspeed_timer_ctrl_overflow_interrupt(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 0); + } + + if ((t_old & pulse_enable_mask) && + (t_new & pulse_enable_mask)) { + aspeed_timer_ctrl_pulse_enable(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 0); + } + + /* Clear interrupt status */ + if (reg & 0x10000) { + s->irq_sts = deposit32(s->irq_sts, index, 1, 0); + } +} + +static uint64_t aspeed_2700_timer_read(AspeedTimerCtrlState *s, hwaddr offset) +{ + uint32_t timer_offset = offset & 0x3f; + int timer_index = offset >> 6; + uint64_t value = 0; + + if (timer_index >= ASPEED_TIMER_NR_TIMERS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%" PRIx64 " out of bounds\n", + __func__, offset); + return 0; + } + + switch (timer_offset) { + /* + * Counter Status + * Counter Reload + * Counter First Matching + * Counter Second Matching + */ + case 0x00 ... 0x0C: + value = aspeed_timer_get_value(&s->timers[timer_index], + timer_offset >> 2); + break; + /* Counter Control and Interrupt Status */ + case 0x10: + value = deposit64(value, 0, 4, + extract32(s->ctrl, timer_index * 4, 4)); + value = deposit64(value, 16, 1, + extract32(s->irq_sts, timer_index, 1)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" + PRIx64"\n", __func__, offset); + value = 0; + break; + } + trace_aspeed_timer_read(offset, value); + return value; +} + +static void aspeed_2700_timer_write(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) +{ + const uint32_t timer_value = (uint32_t)(value & 0xFFFFFFFF); + uint32_t timer_offset = offset & 0x3f; + int timer_index = offset >> 6; + + if (timer_index >= ASPEED_TIMER_NR_TIMERS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%" PRIx64 " out of bounds\n", + __func__, offset); + } + + switch (timer_offset) { + /* + * Counter Status + * Counter Reload + * Counter First Matching + * Counter Second Matching + */ + case 0x00 ... 0x0C: + aspeed_timer_set_value(s, timer_index, timer_offset >> 2, + timer_value); + break; + /* Counter Control Set and Interrupt Status */ + case 0x10: + aspeed_2700_timer_set_ctrl(s, timer_index, timer_value); + break; + /* Counter Control Clear and Interrupr Status */ + case 0x14: + aspeed_2700_timer_clear_ctrl(s, timer_index, timer_value); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" + PRIx64"\n", __func__, offset); + break; + } +} + static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id) { AspeedTimer *t = &s->timers[id]; @@ -788,6 +979,22 @@ static const TypeInfo aspeed_1030_timer_info = { .class_init = aspeed_1030_timer_class_init, }; +static void aspeed_2700_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); + + dc->desc = "ASPEED 2700 Timer"; + awc->read = aspeed_2700_timer_read; + awc->write = aspeed_2700_timer_write; +} + +static const TypeInfo aspeed_2700_timer_info = { + .name = TYPE_ASPEED_2700_TIMER, + .parent = TYPE_ASPEED_TIMER, + .class_init = aspeed_2700_timer_class_init, +}; + static void aspeed_timer_register_types(void) { type_register_static(&aspeed_timer_info); @@ -795,6 +1002,7 @@ static void aspeed_timer_register_types(void) type_register_static(&aspeed_2500_timer_info); type_register_static(&aspeed_2600_timer_info); type_register_static(&aspeed_1030_timer_info); + type_register_static(&aspeed_2700_timer_info); } type_init(aspeed_timer_register_types) diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index 07dc6b6f2c..767cae4b05 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -32,6 +32,7 @@ OBJECT_DECLARE_TYPE(AspeedTimerCtrlState, AspeedTimerClass, ASPEED_TIMER) #define TYPE_ASPEED_2500_TIMER TYPE_ASPEED_TIMER "-ast2500" #define TYPE_ASPEED_2600_TIMER TYPE_ASPEED_TIMER "-ast2600" #define TYPE_ASPEED_1030_TIMER TYPE_ASPEED_TIMER "-ast1030" +#define TYPE_ASPEED_2700_TIMER TYPE_ASPEED_TIMER "-ast2700" #define ASPEED_TIMER_NR_TIMERS 8 From 9cdca151f32eb9840aa5a1b3ba01a5b533d27686 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jan 2025 14:44:55 +0800 Subject: [PATCH 1293/2892] aspeed/soc: Support Timer for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Timer model for AST2700 Timer support. The Timer controller include 8 sets of 32-bit decrement counters. The base address of TIMER0 to TIMER7 as following. Base Address of Timer 0 = 0x12C1_0000 Base Address of Timer 1 = 0x12C1_0040 Base Address of Timer 2 = 0x12C1_0080 Base Address of Timer 3 = 0x12C1_00C0 Base Address of Timer 4 = 0x12C1_0100 Base Address of Timer 5 = 0x12C1_0140 Base Address of Timer 6 = 0x12C1_0180 Base Address of Timer 7 = 0x12C1_01C0 The interrupt of TIMER0 to TIMER7 as following. GICINT16 = TIMER 0 interrupt GICINT17 = TIMER 1 interrupt GICINT18 = TIMER 2 interrupt GICINT19 = TIMER 3 interrupt GICINT20 = TIMER 4 interrupt GICINT21 = TIMER 5 interrupt GICINT22 = TIMER 6 interrupt GICINT23 = TIMER 7 interrupt Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20250113064455.1660564-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index fee3755837..4114e15ddd 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -66,6 +66,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_GPIO] = 0x14C0B000, [ASPEED_DEV_RTC] = 0x12C0F000, [ASPEED_DEV_SDHCI] = 0x14080000, + [ASPEED_DEV_TIMER1] = 0x12C10000, }; #define AST2700_MAX_IRQ 256 @@ -397,6 +398,9 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "emmc-controller.sdhci", &s->emmc.slots[0], TYPE_SYSBUS_SDHCI); + + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); + object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); } /* @@ -716,6 +720,19 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); + /* Timer */ + object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, + sc->memmap[ASPEED_DEV_TIMER1]); + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); From a03e1382598573eef351c48509ca7abd3ab610e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 Jan 2025 08:09:07 +0100 Subject: [PATCH 1294/2892] test/functional: Update the Aspeed aarch64 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumped SDK version to v09.03. v09.04 is available but not yet supported in QEMU. Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20250122070909.1138598-8-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 141d863859..9595498ace 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -27,14 +27,14 @@ class AST2x00MachineSDK(QemuSystemTest): wait_for_console_pattern(self, '## Loading kernel from FIT Image') wait_for_console_pattern(self, 'Starting kernel ...') - ASSET_SDK_V902_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.02/ast2700-default-obmc.tar.gz', - 'ac969c2602f4e6bdb69562ff466b89ae3fe1d86e1f6797bb7969d787f82116a7') + ASSET_SDK_V903_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.03/ast2700-default-obmc.tar.gz', + '91225f50d255e2905ba8d8e0c80b71b9d157c3609770c7a740cd786370d85a77') - def test_aarch64_ast2700_evb_sdk_v09_02(self): + def test_aarch64_ast2700_evb_sdk_v09_03(self): self.set_machine('ast2700-evb') - self.archive_extract(self.ASSET_SDK_V902_AST2700) + self.archive_extract(self.ASSET_SDK_V903_AST2700) num_cpu = 4 uboot_size = os.path.getsize(self.scratch_file('ast2700-default', From f7ae9612fb98d505a44ac24872f6b92e87687813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 Jan 2025 08:09:08 +0100 Subject: [PATCH 1295/2892] test/functional: Update buildroot images to 2024.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main changes compared to upstream 2024.11 buildroot are - bumped Linux to version 6.11.11 with a custom config - changed U-Boot to OpenBMC branch for more support - included extra target packages See branch [1] for more details. There is a slight output change when powering off the machine, the console now contains : reboot: Power off not available: System halted Adjust accordingly the expect string in do_test_arm_aspeed_buildroot_poweroff(). [1] https://github.com/legoater/buildroot/commits/aspeed-2024.11 Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20250122070909.1138598-9-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 2 +- tests/functional/test_arm_aspeed_ast2500.py | 8 ++++---- tests/functional/test_arm_aspeed_ast2600.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index 62f50bab7a..b52358bb8c 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -42,7 +42,7 @@ class AspeedTest(LinuxKernelTest): def do_test_arm_aspeed_buildroot_poweroff(self): exec_command_and_wait_for_pattern(self, 'poweroff', - 'reboot: System halted'); + 'System halted'); def do_test_arm_aspeed_sdk_start(self, image): self.require_netdev('user') diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py index 743fc46eb2..1ffba6c995 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -11,15 +11,15 @@ from qemu_test import exec_command_and_wait_for_pattern class AST2500Machine(AspeedTest): - ASSET_BR2_202311_AST2500_FLASH = Asset( + ASSET_BR2_202411_AST2500_FLASH = Asset( ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2500-evb/buildroot-2023.11/flash.img'), - 'c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f') + 'images/ast2500-evb/buildroot-2024.11/flash.img'), + '641e6906c18c0f19a2aeb48099d66d4771929c361001d554d0d45c667413e13a') def test_arm_ast2500_evb_buildroot(self): self.set_machine('ast2500-evb') - image_path = self.ASSET_BR2_202311_AST2500_FLASH.fetch() + image_path = self.ASSET_BR2_202411_AST2500_FLASH.fetch() self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py index 21640123ee..6ae4ed636a 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -16,15 +16,15 @@ from qemu_test import exec_command_and_wait_for_pattern, skipIfMissingCommands class AST2600Machine(AspeedTest): - ASSET_BR2_202311_AST2600_FLASH = Asset( + ASSET_BR2_202411_AST2600_FLASH = Asset( ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2600-evb/buildroot-2023.11/flash.img'), - 'b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68') + 'images/ast2600-evb/buildroot-2024.11/flash.img'), + '4bb2f3dfdea31199b51d66b42f686dc5374c144a7346fdc650194a5578b73609') def test_arm_ast2600_evb_buildroot(self): self.set_machine('ast2600-evb') - image_path = self.ASSET_BR2_202311_AST2600_FLASH.fetch() + image_path = self.ASSET_BR2_202411_AST2600_FLASH.fetch() self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); From e90858464ab90e0e6aaede7f3c9d4750232b9755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 Jan 2025 08:09:09 +0100 Subject: [PATCH 1296/2892] aspeed: Create sd devices only when defaults are enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the -nodefaults option is set, sd devices should not be automatically created by the machine. Instead they should be defined on the command line. Note that it is not currently possible to define which bus an "sd-card" device is attached to: -blockdev node-name=drive0,driver=file,filename=/path/to/file.img \ -device sd-card,drive=drive0,id=sd0 and the first bus named "sd-bus" will be used. Reviewed-by: Jamin Lin Acked-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250122070909.1138598-10-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 53a859a6e4..d9418e2b9f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -456,14 +456,14 @@ static void aspeed_machine_init(MachineState *machine) amc->i2c_init(bmc); } - for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { + for (i = 0; i < bmc->soc->sdhci.num_slots && defaults_enabled(); i++) { sdhci_attach_drive(&bmc->soc->sdhci.slots[i], drive_get(IF_SD, 0, i), false, false); } boot_emmc = sc->boot_from_emmc(bmc->soc); - if (bmc->soc->emmc.num_slots) { + if (bmc->soc->emmc.num_slots && defaults_enabled()) { emmc0 = drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots); sdhci_attach_drive(&bmc->soc->emmc.slots[0], emmc0, true, boot_emmc); } From 668f29e1713b879fa32af213cc2820ea589034d0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 24 Jan 2025 11:02:48 +0800 Subject: [PATCH 1297/2892] aspeed/wdt: Fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250124030249.1706996-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/watchdog/wdt_aspeed.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 81f5c5189a..22e94e7b9c 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -278,7 +278,8 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); - /* FIXME: This setting should be derived from the SCU hw strapping + /* + * FIXME: This setting should be derived from the SCU hw strapping * register SCU70 */ s->pclk_freq = PCLK_HZ; From a22acbb252fe308416aafae8069beaf039578f14 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 24 Jan 2025 11:02:49 +0800 Subject: [PATCH 1298/2892] aspeed/wdt: Support software reset mode for AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the AST2400 and AST2500 platforms, the system can only be reset by enabling the WDT (Watchdog Timer) and waiting for the WDT timeout. However, starting from the AST2600 platform, the reset event can be triggered directly and intentionally by software, without relying on the WDT timeout. This mechanism, referred to as "software restart", is implemented in hardware. When using the software restart mechanism, the WDT counter is not enabled. To trigger a reset generation in software mode, write 0xAEEDF123 to register 0x24 and software mode reset only support SOC reset mode. A new function, "aspeed_wdt_is_soc_reset_mode", is introduced to determine whether the SoC reset mode is active. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250124030249.1706996-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/watchdog/wdt_aspeed.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 22e94e7b9c..d94b83c109 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -51,11 +51,20 @@ #define WDT_TIMEOUT_CLEAR (0x14 / 4) #define WDT_RESTART_MAGIC 0x4755 +#define WDT_SW_RESET_ENABLE 0xAEEDF123 #define AST2600_SCU_RESET_CONTROL1 (0x40 / 4) #define SCU_RESET_CONTROL1 (0x04 / 4) #define SCU_RESET_SDRAM BIT(0) +static bool aspeed_wdt_is_soc_reset_mode(const AspeedWDTState *s) +{ + uint32_t mode; + + mode = extract32(s->regs[WDT_CTRL], 5, 2); + return (mode == WDT_CTRL_RESET_MODE_SOC); +} + static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) { return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; @@ -199,13 +208,18 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, case WDT_TIMEOUT_STATUS: case WDT_TIMEOUT_CLEAR: case WDT_RESET_MASK2: - case WDT_SW_RESET_CTRL: case WDT_SW_RESET_MASK1: case WDT_SW_RESET_MASK2: qemu_log_mask(LOG_UNIMP, "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); break; + case WDT_SW_RESET_CTRL: + if (aspeed_wdt_is_soc_reset_mode(s) && + (data == WDT_SW_RESET_ENABLE)) { + watchdog_perform_action(); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", From 8b7ccc6ad10cd4a107b4627e9a5606d757607ff2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 24 Jan 2025 18:45:07 +0100 Subject: [PATCH 1299/2892] docs/system/arm/aspeed: Remove tacoma-bmc from the documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tacoma-bmc machine has recently been removed, so let's remove it from the documentation now, too. Fixes: 2b1b66e01f ("arm: Remove tacoma-bmc machine") Signed-off-by: Thomas Huth Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250124174507.27348-1-thuth@redhat.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index fa4aa28eef..97fd6a0e7f 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,5 +1,5 @@ -Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``tacoma-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) -================================================================================================================================================================================================================================================================================================================================================================================================================================== +Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +================================================================================================================================================================================================================================================================================================================================================================================================================== The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the From 13f113e46944e735cc1d9fadd62d61d8ead7856e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 23:56:12 +0100 Subject: [PATCH 1300/2892] hw/arm/nrf51: Rename ARMv7MState 'cpu' -> 'armv7m' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ARMv7MState object is not simply a CPU, it also contains the NVIC, SysTick timer, and various MemoryRegions. Rename the field as 'armv7m', like other Cortex-M boards. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20250112225614.33723-2-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 18 +++++++++--------- include/hw/arm/nrf51_soc.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 37dd4cf5f4..dee06ab565 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -76,16 +76,16 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) } /* This clock doesn't need migration because it is fixed-frequency */ clock_set_hz(s->sysclk, HCLK_FRQ); - qdev_connect_clock_in(DEVICE(&s->cpu), "cpuclk", s->sysclk); + qdev_connect_clock_in(DEVICE(&s->armv7m), "cpuclk", s->sysclk); /* * This SoC has no systick device, so don't connect refclk. * TODO: model the lack of systick (currently the armv7m object * will always provide one). */ - object_property_set_link(OBJECT(&s->cpu), "memory", OBJECT(&s->container), + object_property_set_link(OBJECT(&s->armv7m), "memory", OBJECT(&s->container), &error_abort); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpu), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { return; } @@ -104,7 +104,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart), 0); memory_region_add_subregion_overlap(&s->container, NRF51_UART_BASE, mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), + qdev_get_gpio_in(DEVICE(&s->armv7m), BASE_TO_IRQ(NRF51_UART_BASE))); /* RNG */ @@ -115,7 +115,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0); memory_region_add_subregion_overlap(&s->container, NRF51_RNG_BASE, mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rng), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), + qdev_get_gpio_in(DEVICE(&s->armv7m), BASE_TO_IRQ(NRF51_RNG_BASE))); /* UICR, FICR, NVMC, FLASH */ @@ -161,7 +161,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer[i]), 0, base_addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer[i]), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), + qdev_get_gpio_in(DEVICE(&s->armv7m), BASE_TO_IRQ(base_addr))); } @@ -185,10 +185,10 @@ static void nrf51_soc_init(Object *obj) memory_region_init(&s->container, obj, "nrf51-container", UINT64_MAX); - object_initialize_child(OBJECT(s), "armv6m", &s->cpu, TYPE_ARMV7M); - qdev_prop_set_string(DEVICE(&s->cpu), "cpu-type", + object_initialize_child(OBJECT(s), "armv6m", &s->armv7m, TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", ARM_CPU_TYPE_NAME("cortex-m0")); - qdev_prop_set_uint32(DEVICE(&s->cpu), "num-irq", 32); + qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", 32); object_initialize_child(obj, "uart", &s->uart, TYPE_NRF51_UART); object_property_add_alias(obj, "serial0", OBJECT(&s->uart), "chardev"); diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index e52a56e75e..f88ab1b7d3 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -30,7 +30,7 @@ struct NRF51State { SysBusDevice parent_obj; /*< public >*/ - ARMv7MState cpu; + ARMv7MState armv7m; NRF51UARTState uart; NRF51RNGState rng; From 19266bec2770f40baa6f17d9a7d84d0fd9211a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 23:56:13 +0100 Subject: [PATCH 1301/2892] hw/arm/stellaris: Add 'armv7m' local variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the TYPE_ARMV7M object forward its NVIC interrupt lines, it is somehow misleading to name it 'nvic'. Add the 'armv7m' local variable for clarity, but also keep the 'nvic' variable behaving like before when used for wiring IRQ lines. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20250112225614.33723-3-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 1bba96df14..7303e096ef 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1031,7 +1031,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) */ Object *soc_container; - DeviceState *gpio_dev[7], *nvic; + DeviceState *gpio_dev[7], *armv7m, *nvic; qemu_irq gpio_in[7][8]; qemu_irq gpio_out[7][8]; qemu_irq adc; @@ -1095,19 +1095,20 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) qdev_prop_set_uint32(ssys_dev, "dc4", board->dc4); sysbus_realize_and_unref(SYS_BUS_DEVICE(ssys_dev), &error_fatal); - nvic = qdev_new(TYPE_ARMV7M); - object_property_add_child(soc_container, "v7m", OBJECT(nvic)); - qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); - qdev_prop_set_uint8(nvic, "num-prio-bits", NUM_PRIO_BITS); - qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); - qdev_prop_set_bit(nvic, "enable-bitband", true); - qdev_connect_clock_in(nvic, "cpuclk", + armv7m = qdev_new(TYPE_ARMV7M); + object_property_add_child(soc_container, "v7m", OBJECT(armv7m)); + qdev_prop_set_uint32(armv7m, "num-irq", NUM_IRQ_LINES); + qdev_prop_set_uint8(armv7m, "num-prio-bits", NUM_PRIO_BITS); + qdev_prop_set_string(armv7m, "cpu-type", ms->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", qdev_get_clock_out(ssys_dev, "SYSCLK")); /* This SoC does not connect the systick reference clock */ - object_property_set_link(OBJECT(nvic), "memory", + object_property_set_link(OBJECT(armv7m), "memory", OBJECT(get_system_memory()), &error_abort); /* This will exit with an error if the user passed us a bad cpu_type */ - sysbus_realize_and_unref(SYS_BUS_DEVICE(nvic), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(armv7m), &error_fatal); + nvic = armv7m; /* Now we can wire up the IRQ and MMIO of the system registers */ sysbus_mmio_map(SYS_BUS_DEVICE(ssys_dev), 0, 0x400fe000); From deeb9969357d377110ac61fe3c6482c7303313ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 23:56:14 +0100 Subject: [PATCH 1302/2892] hw/arm/v7m: Remove use of &first_cpu in machine_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When instanciating the machine model, the machine_init() implementations usually create the CPUs, so have access to its first CPU. Use that rather then the &first_cpu global. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Samuel Tardieu Message-id: 20250112225614.33723-4-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/b-l475e-iot01a.c | 2 +- hw/arm/microbit.c | 2 +- hw/arm/mps2-tz.c | 2 +- hw/arm/mps2.c | 2 +- hw/arm/msf2-som.c | 2 +- hw/arm/musca.c | 2 +- hw/arm/netduino2.c | 2 +- hw/arm/netduinoplus2.c | 2 +- hw/arm/olimex-stm32-h405.c | 2 +- hw/arm/stellaris.c | 2 +- hw/arm/stm32vldiscovery.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index 5002a40f06..c9a5209216 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -82,7 +82,7 @@ static void bl475e_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); sc = STM32L4X5_SOC_GET_CLASS(&s->soc); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0, + armv7m_load_kernel(s->soc.armv7m.cpu, machine->kernel_filename, 0, sc->flash_size); if (object_class_by_name(TYPE_DM163)) { diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 374fbcb361..3f56fb45ce 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -56,7 +56,7 @@ static void microbit_init(MachineState *machine) memory_region_add_subregion_overlap(&s->nrf51.container, NRF51_TWI_BASE, mr, -1); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(s->nrf51.armv7m.cpu, machine->kernel_filename, 0, s->nrf51.flash_size); } diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 0136e419bf..d3a9f1b03a 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1211,7 +1211,7 @@ static void mps2tz_common_init(MachineState *machine) mms->remap_irq); } - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(mms->iotkit.armv7m[0].cpu, machine->kernel_filename, 0, boot_ram_size(mms)); } diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index efb3500742..56b2af40f1 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -460,7 +460,7 @@ static void mps2_common_init(MachineState *machine) qdev_get_gpio_in(armv7m, mmc->fpga_type == FPGA_AN511 ? 47 : 13)); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(mms->armv7m.cpu, machine->kernel_filename, 0, 0x400000); } diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 5c415abe85..9b20f1e2c9 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -92,7 +92,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) cs_line = qdev_get_gpio_in_named(spi_flash, SSI_GPIO_CS, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&soc->spi[0]), 1, cs_line); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(soc->armv7m.cpu, machine->kernel_filename, 0, soc->envm_size); } diff --git a/hw/arm/musca.c b/hw/arm/musca.c index 3c3b534cb7..e9c092abc3 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -590,7 +590,7 @@ static void musca_init(MachineState *machine) "cfg_sec_resp", 0)); } - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(mms->sse.armv7m[0].cpu, machine->kernel_filename, 0, 0x2000000); } diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index 8b1a9a2437..df793c77fe 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -48,7 +48,7 @@ static void netduino2_init(MachineState *machine) qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(STM32F205_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index bccd100354..81b6334cf7 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -48,7 +48,7 @@ static void netduinoplus2_init(MachineState *machine) qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), + armv7m_load_kernel(STM32F405_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } diff --git a/hw/arm/olimex-stm32-h405.c b/hw/arm/olimex-stm32-h405.c index 4ad7b043be..1f15620f9f 100644 --- a/hw/arm/olimex-stm32-h405.c +++ b/hw/arm/olimex-stm32-h405.c @@ -51,7 +51,7 @@ static void olimex_stm32_h405_init(MachineState *machine) qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), + armv7m_load_kernel(STM32F405_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 7303e096ef..284980ad4b 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1366,7 +1366,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) create_unimplemented_device("hibernation", 0x400fc000, 0x1000); create_unimplemented_device("flash-control", 0x400fd000, 0x1000); - armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, 0, flash_size); + armv7m_load_kernel(ARMV7M(armv7m)->cpu, ms->kernel_filename, 0, flash_size); } /* FIXME: Figure out how to generate these from stellaris_boards. */ diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index cc41935160..e6c1f5b8d7 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -51,7 +51,7 @@ static void stm32vldiscovery_init(MachineState *machine) qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), + armv7m_load_kernel(STM32F100_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } From b6cd77fbddf020d4dad23476b286fbeb22d4c334 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 11 Jan 2025 19:37:00 +0100 Subject: [PATCH 1303/2892] hw/char/imx_serial: Fix reset value of UFCR register The value of the UCFR register is respected when echoing characters to the terminal, but its reset value is reserved. Fix the reset value to the one documented in the datasheet. While at it move the related attribute out of the section of unimplemented registers since its value is actually respected. Signed-off-by: Bernhard Beschow Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/imx_serial.c | 1 + include/hw/char/imx_serial.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 7c353fde50..cb6761d40e 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -160,6 +160,7 @@ static void imx_serial_reset(IMXSerialState *s) s->ucr3 = 0x700; s->ubmr = 0; s->ubrc = 4; + s->ufcr = BIT(11) | BIT(0); fifo32_reset(&s->rx_fifo); timer_del(&s->ageing_timer); diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h index 65f0e97c76..90ba3ff18c 100644 --- a/include/hw/char/imx_serial.h +++ b/include/hw/char/imx_serial.h @@ -109,13 +109,13 @@ struct IMXSerialState { uint32_t ucr1; uint32_t ucr2; uint32_t uts1; + uint32_t ufcr; /* * The registers below are implemented just so that the * guest OS sees what it has written */ uint32_t onems; - uint32_t ufcr; uint32_t ubmr; uint32_t ubrc; uint32_t ucr3; From a451cc11c47903242123def2717a86a56e5fb390 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 11 Jan 2025 19:37:01 +0100 Subject: [PATCH 1304/2892] hw/char/imx_serial: Update all state before restarting ageing timer Fixes characters to be "echoed" after each keystroke rather than after every other since imx_serial_rx_fifo_ageing_timer_restart() would see ~UTS1_RXEMPTY only after every other keystroke. Signed-off-by: Bernhard Beschow Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/imx_serial.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index cb6761d40e..38b4865157 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -401,14 +401,14 @@ static void imx_put_data(void *opaque, uint32_t value) if (fifo32_num_used(&s->rx_fifo) >= rxtl) { s->usr1 |= USR1_RRDY; } - - imx_serial_rx_fifo_ageing_timer_restart(s); - s->usr2 |= USR2_RDR; s->uts1 &= ~UTS1_RXEMPTY; if (value & URXD_BRK) { s->usr2 |= USR2_BRCD; } + + imx_serial_rx_fifo_ageing_timer_restart(s); + imx_update(s); } From 1b326f278d05543425a71165a66b925922e532ac Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 11 Jan 2025 19:37:02 +0100 Subject: [PATCH 1305/2892] hw/pci-host/designware: Expose MSI IRQ Fixes INTD and MSI interrupts poking the same IRQ line without keeping track of each other's IRQ level. Furthermore, SoCs such as the i.MX 8M Plus don't share the MSI IRQ with the INTx lines, so expose it as a dedicated pin. Signed-off-by: Bernhard Beschow Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 2 ++ hw/arm/fsl-imx6.c | 13 ++++++++++++- hw/arm/fsl-imx7.c | 13 ++++++++++++- hw/pci-host/designware.c | 7 +++---- include/hw/arm/fsl-imx6.h | 4 +++- include/hw/arm/fsl-imx7.h | 4 +++- include/hw/pci-host/designware.h | 1 + 7 files changed, 36 insertions(+), 8 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index e779b5af95..256013ca80 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -516,6 +516,7 @@ config FSL_IMX6 select PL310 # cache controller select PCI_EXPRESS_DESIGNWARE select SDHCI + select OR_IRQ config ASPEED_SOC bool @@ -573,6 +574,7 @@ config FSL_IMX7 select WDT_IMX2 select PCI_EXPRESS_DESIGNWARE select SDHCI + select OR_IRQ select UNIMP config ARM_SMMUV3 diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index ac8c66e242..88b9ccff49 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -106,6 +106,8 @@ static void fsl_imx6_init(Object *obj) object_initialize_child(obj, "eth", &s->eth, TYPE_IMX_ENET); object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie4-msi-irq", &s->pcie4_msi_irq, + TYPE_OR_IRQ); } static void fsl_imx6_realize(DeviceState *dev, Error **errp) @@ -435,14 +437,23 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->pcie), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX6_PCIe_REG_ADDR); + object_property_set_int(OBJECT(&s->pcie4_msi_irq), "num-lines", 2, + &error_abort); + qdev_realize(DEVICE(&s->pcie4_msi_irq), NULL, &error_abort); + + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE4_MSI_IRQ); + qdev_connect_gpio_out(DEVICE(&s->pcie4_msi_irq), 0, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE1_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq); irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE2_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq); irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE3_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE4_IRQ); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 1); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, irq); /* * PCIe PHY diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 05e3389fbe..004bf49937 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -150,6 +150,8 @@ static void fsl_imx7_init(Object *obj) * PCIE */ object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie4-msi-irq", &s->pcie4_msi_irq, + TYPE_OR_IRQ); /* * USBs @@ -597,14 +599,23 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->pcie), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR); + object_property_set_int(OBJECT(&s->pcie4_msi_irq), "num-lines", 2, + &error_abort); + qdev_realize(DEVICE(&s->pcie4_msi_irq), NULL, &error_abort); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_MSI_IRQ); + qdev_connect_gpio_out(DEVICE(&s->pcie4_msi_irq), 0, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq); irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq); irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 1); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, irq); /* * USBs diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index c3fc37b904..3e8c36e6a7 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -55,8 +55,6 @@ #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C -#define DESIGNWARE_PCIE_IRQ_MSI 3 - static DesignwarePCIEHost * designware_pcie_root_to_host(DesignwarePCIERoot *root) { @@ -90,7 +88,7 @@ static void designware_pcie_root_msi_write(void *opaque, hwaddr addr, root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable; if (root->msi.intr[0].status & ~root->msi.intr[0].mask) { - qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 1); + qemu_set_irq(host->pci.msi, 1); } } @@ -335,7 +333,7 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, case DESIGNWARE_PCIE_MSI_INTR0_STATUS: root->msi.intr[0].status ^= val; if (!root->msi.intr[0].status) { - qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 0); + qemu_set_irq(host->pci.msi, 0); } break; @@ -680,6 +678,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) { sysbus_init_irq(sbd, &s->pci.irqs[i]); } + sysbus_init_irq(sbd, &s->pci.msi); memory_region_init_io(&s->mmio, OBJECT(s), diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 61c593ffd2..9da32fc189 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -33,6 +33,7 @@ #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" #include "hw/pci-host/designware.h" +#include "hw/or-irq.h" #include "exec/memory.h" #include "cpu.h" #include "qom/object.h" @@ -73,6 +74,7 @@ struct FslIMX6State { ChipideaState usb[FSL_IMX6_NUM_USBS]; IMXFECState eth; DesignwarePCIEHost pcie; + OrIRQState pcie4_msi_irq; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -457,7 +459,7 @@ struct FslIMX6State { #define FSL_IMX6_PCIE1_IRQ 120 #define FSL_IMX6_PCIE2_IRQ 121 #define FSL_IMX6_PCIE3_IRQ 122 -#define FSL_IMX6_PCIE4_IRQ 123 +#define FSL_IMX6_PCIE4_MSI_IRQ 123 #define FSL_IMX6_DCIC1_IRQ 124 #define FSL_IMX6_DCIC2_IRQ 125 #define FSL_IMX6_MLB150_HIGH_IRQ 126 diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h index 411fa1c2e3..aa7818c499 100644 --- a/include/hw/arm/fsl-imx7.h +++ b/include/hw/arm/fsl-imx7.h @@ -36,6 +36,7 @@ #include "hw/net/imx_fec.h" #include "hw/pci-host/designware.h" #include "hw/usb/chipidea.h" +#include "hw/or-irq.h" #include "cpu.h" #include "qom/object.h" #include "qemu/units.h" @@ -85,6 +86,7 @@ struct FslIMX7State { IMX7GPRState gpr; ChipideaState usb[FSL_IMX7_NUM_USBS]; DesignwarePCIEHost pcie; + OrIRQState pcie4_msi_irq; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -428,7 +430,7 @@ enum FslIMX7IRQs { FSL_IMX7_PCI_INTA_IRQ = 125, FSL_IMX7_PCI_INTB_IRQ = 124, FSL_IMX7_PCI_INTC_IRQ = 123, - FSL_IMX7_PCI_INTD_IRQ = 122, + FSL_IMX7_PCI_INTD_MSI_IRQ = 122, FSL_IMX7_UART7_IRQ = 126, diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h index c484e377a8..bf8b278978 100644 --- a/include/hw/pci-host/designware.h +++ b/include/hw/pci-host/designware.h @@ -86,6 +86,7 @@ struct DesignwarePCIEHost { MemoryRegion io; qemu_irq irqs[4]; + qemu_irq msi; } pci; MemoryRegion mmio; From 82634b58bc2dc32489cdf8e04af5b61bea4efb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 17:01:56 +0100 Subject: [PATCH 1306/2892] hw/arm/stellaris: Link each board schematic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Board schematic is useful to corroborate GPIOs/IRQs wiring. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20250110160204.74997-2-philmd@linaro.org [PMM: Use https:// URLs] Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 284980ad4b..3f525d2dcf 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1380,6 +1380,10 @@ static void lm3s6965evb_init(MachineState *machine) stellaris_init(machine, &stellaris_boards[1]); } +/* + * Stellaris LM3S811 Evaluation Board Schematics: + * https://www.ti.com/lit/ug/symlink/spmu030.pdf + */ static void lm3s811evb_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1396,6 +1400,10 @@ static const TypeInfo lm3s811evb_type = { .class_init = lm3s811evb_class_init, }; +/* + * Stellaris: LM3S6965 Evaluation Board Schematics: + * https://www.ti.com/lit/ug/symlink/spmu029.pdf + */ static void lm3s6965evb_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); From 87409ea90408938d55a37652968f2855954bada1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 17:01:57 +0100 Subject: [PATCH 1307/2892] hw/arm/stellaris: Constify read-only arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20250110160204.74997-3-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 3f525d2dcf..42498cc1e5 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -101,7 +101,7 @@ static void ssys_update(ssys_state *s) qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0); } -static uint32_t pllcfg_sandstorm[16] = { +static const uint32_t pllcfg_sandstorm[16] = { 0x31c0, /* 1 Mhz */ 0x1ae0, /* 1.8432 Mhz */ 0x18c0, /* 2 Mhz */ @@ -120,7 +120,7 @@ static uint32_t pllcfg_sandstorm[16] = { 0x585b /* 8.192 Mhz */ }; -static uint32_t pllcfg_fury[16] = { +static const uint32_t pllcfg_fury[16] = { 0x3200, /* 1 Mhz */ 0x1b20, /* 1.8432 Mhz */ 0x1900, /* 2 Mhz */ @@ -964,7 +964,7 @@ static void stellaris_adc_init(Object *obj) } /* Board init. */ -static stellaris_board_info stellaris_boards[] = { +static const stellaris_board_info stellaris_boards[] = { { "LM3S811EVB", 0, 0x0032000e, From 00bc529d95505f3bfcc2792f92d403da4aebc68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 17:01:58 +0100 Subject: [PATCH 1308/2892] hw/arm/stellaris: Remove incorrect unimplemented i2c-0 at 0x40002000 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is nothing mapped at 0x40002000. I2C#0 is already mapped at 0x40021000. Remove the invalid mapping added in commits aecfbbc97a2 & 394c8bbfb7a. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20250110160204.74997-4-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 42498cc1e5..4df02138ac 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1001,7 +1001,6 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf * * 40000000 wdtimer - * 40002000 i2c (unimplemented) * 40004000 GPIO * 40005000 GPIO * 40006000 GPIO @@ -1357,7 +1356,6 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) /* Add dummy regions for the devices we don't implement yet, * so guest accesses don't cause unlogged crashes. */ - create_unimplemented_device("i2c-0", 0x40002000, 0x1000); create_unimplemented_device("i2c-2", 0x40021000, 0x1000); create_unimplemented_device("PWM", 0x40028000, 0x1000); create_unimplemented_device("QEI-0", 0x4002c000, 0x1000); From 7330c1c5c61acd8172ff218dd9e169cd2a4fa87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 17:01:59 +0100 Subject: [PATCH 1309/2892] hw/arm/stellaris: Replace magic numbers by definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add definitions for the number of controllers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20250110160204.74997-5-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 4df02138ac..dd342b17d2 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -49,6 +49,11 @@ #define NUM_IRQ_LINES 64 #define NUM_PRIO_BITS 3 +#define NUM_GPIO 7 +#define NUM_UART 4 +#define NUM_GPTM 4 +#define NUM_I2C 2 + typedef const struct { const char *name; uint32_t did0; @@ -989,12 +994,12 @@ static const stellaris_board_info stellaris_boards[] = { static void stellaris_init(MachineState *ms, stellaris_board_info *board) { - static const int uart_irq[] = {5, 6, 33, 34}; - static const int timer_irq[] = {19, 21, 23, 35}; - static const uint32_t gpio_addr[7] = + static const int uart_irq[NUM_UART] = {5, 6, 33, 34}; + static const int timer_irq[NUM_GPTM] = {19, 21, 23, 35}; + static const uint32_t gpio_addr[NUM_GPIO] = { 0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40024000, 0x40025000, 0x40026000}; - static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; + static const int gpio_irq[NUM_GPIO] = {0, 1, 2, 3, 4, 30, 31}; /* Memory map of SoC devices, from * Stellaris LM3S6965 Microcontroller Data Sheet (rev I) @@ -1030,9 +1035,9 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) */ Object *soc_container; - DeviceState *gpio_dev[7], *armv7m, *nvic; - qemu_irq gpio_in[7][8]; - qemu_irq gpio_out[7][8]; + DeviceState *gpio_dev[NUM_GPIO], *armv7m, *nvic; + qemu_irq gpio_in[NUM_GPIO][8]; + qemu_irq gpio_out[NUM_GPIO][8]; qemu_irq adc; int sram_size; int flash_size; @@ -1124,7 +1129,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } else { adc = NULL; } - for (i = 0; i < 4; i++) { + for (i = 0; i < NUM_GPTM; i++) { if (board->dc2 & (0x10000 << i)) { SysBusDevice *sbd; @@ -1158,7 +1163,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } - for (i = 0; i < 7; i++) { + for (i = 0; i < NUM_GPIO; i++) { if (board->dc4 & (1 << i)) { gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], qdev_get_gpio_in(nvic, @@ -1179,7 +1184,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } } - for (i = 0; i < 4; i++) { + for (i = 0; i < NUM_UART; i++) { if (board->dc2 & (1 << i)) { SysBusDevice *sbd; From b7c55f596cc8b6cd85aeed08846da677e7261b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 17:02:00 +0100 Subject: [PATCH 1310/2892] hw/arm/stellaris: Use DEVCAP macro to access DeviceCapability registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add definitions (DCx_periph) for the DeviceCapability bits, replace direct bitmask checks with the DEV_CAP() macro, which use the extract/deposit API. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20250110160204.74997-6-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index dd342b17d2..82f935cb32 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "qapi/error.h" #include "hw/core/split-irq.h" #include "hw/sysbus.h" @@ -54,6 +55,26 @@ #define NUM_GPTM 4 #define NUM_I2C 2 +/* + * See Stellaris Data Sheet chapter 5.2.5 "System Control", + * Register 13 .. 17: Device Capabilities 0 .. 4 (DC0 .. DC4). + */ +#define DC1_WDT 3 +#define DC1_HIB 6 +#define DC1_MPU 7 +#define DC1_ADC 16 +#define DC1_PWM 20 +#define DC2_UART(n) (n) +#define DC2_SSI 4 +#define DC2_QEI(n) (8 + n) +#define DC2_I2C(n) (12 + 2 * n) +#define DC2_GPTM(n) (16 + n) +#define DC2_COMP(n) (24 + n) +#define DC4_GPIO(n) (n) +#define DC4_EMAC 28 + +#define DEV_CAP(_dc, _cap) extract32(board->dc##_dc, DC##_dc##_##_cap, 1) + typedef const struct { const char *name; uint32_t did0; @@ -1118,7 +1139,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sysbus_mmio_map(SYS_BUS_DEVICE(ssys_dev), 0, 0x400fe000); sysbus_connect_irq(SYS_BUS_DEVICE(ssys_dev), 0, qdev_get_gpio_in(nvic, 28)); - if (board->dc1 & (1 << 16)) { + if (DEV_CAP(1, ADC)) { dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000, qdev_get_gpio_in(nvic, 14), qdev_get_gpio_in(nvic, 15), @@ -1130,7 +1151,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) adc = NULL; } for (i = 0; i < NUM_GPTM; i++) { - if (board->dc2 & (0x10000 << i)) { + if (DEV_CAP(2, GPTM(i))) { SysBusDevice *sbd; dev = qdev_new(TYPE_STELLARIS_GPTM); @@ -1147,7 +1168,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } } - if (board->dc1 & (1 << 3)) { /* watchdog present */ + if (DEV_CAP(1, WDT)) { dev = qdev_new(TYPE_LUMINARY_WATCHDOG); object_property_add_child(soc_container, "wdg", OBJECT(dev)); qdev_connect_clock_in(dev, "WDOGCLK", @@ -1164,7 +1185,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) for (i = 0; i < NUM_GPIO; i++) { - if (board->dc4 & (1 << i)) { + if (DEV_CAP(4, GPIO(i))) { gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], qdev_get_gpio_in(nvic, gpio_irq[i])); @@ -1175,7 +1196,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } } - if (board->dc2 & (1 << 12)) { + if (DEV_CAP(2, I2C(0))) { dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, qdev_get_gpio_in(nvic, 8)); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); @@ -1185,7 +1206,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } for (i = 0; i < NUM_UART; i++) { - if (board->dc2 & (1 << i)) { + if (DEV_CAP(2, UART(i))) { SysBusDevice *sbd; dev = qdev_new("pl011_luminary"); @@ -1197,7 +1218,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, uart_irq[i])); } } - if (board->dc2 & (1 << 4)) { + if (DEV_CAP(2, SSI)) { dev = sysbus_create_simple("pl022", 0x40008000, qdev_get_gpio_in(nvic, 7)); if (board->peripherals & BP_OLED_SSI) { @@ -1306,7 +1327,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) qemu_irq_raise(gpio_out[GPIO_D][0]); } } - if (board->dc4 & (1 << 28)) { + if (DEV_CAP(4, EMAC)) { DeviceState *enet; enet = qdev_new("stellaris_enet"); From 3499f7e356e7ce54fa65fd0e610625199de2c42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Jan 2025 17:02:01 +0100 Subject: [PATCH 1311/2892] hw/arm/stellaris: Map both I2C controllers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are 2 I2C controllers, map them both, removing the unimplemented one. Keep the OLED controller on the first I2C bus. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20250110160204.74997-7-philmd@linaro.org [PMM: tweak to appease maybe-use-uninitialized warning] Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 82f935cb32..c3c3fd0410 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1021,6 +1021,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) { 0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40024000, 0x40025000, 0x40026000}; static const int gpio_irq[NUM_GPIO] = {0, 1, 2, 3, 4, 30, 31}; + static const uint32_t i2c_addr[NUM_I2C] = {0x40020000, 0x40021000}; + static const int i2c_irq[NUM_I2C] = {8, 37}; /* Memory map of SoC devices, from * Stellaris LM3S6965 Microcontroller Data Sheet (rev I) @@ -1062,7 +1064,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) qemu_irq adc; int sram_size; int flash_size; - I2CBus *i2c; + DeviceState *i2c_dev[NUM_I2C] = { }; DeviceState *dev; DeviceState *ssys_dev; int i; @@ -1196,14 +1198,18 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } } - if (DEV_CAP(2, I2C(0))) { - dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, - qdev_get_gpio_in(nvic, 8)); - i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); - if (board->peripherals & BP_OLED_I2C) { - i2c_slave_create_simple(i2c, "ssd0303", 0x3d); + for (i = 0; i < NUM_I2C; i++) { + if (DEV_CAP(2, I2C(i))) { + i2c_dev[i] = sysbus_create_simple(TYPE_STELLARIS_I2C, i2c_addr[i], + qdev_get_gpio_in(nvic, + i2c_irq[i])); } } + if (board->peripherals & BP_OLED_I2C) { + I2CBus *bus = (I2CBus *)qdev_get_child_bus(i2c_dev[0], "i2c"); + + i2c_slave_create_simple(bus, "ssd0303", 0x3d); + } for (i = 0; i < NUM_UART; i++) { if (DEV_CAP(2, UART(i))) { @@ -1382,7 +1388,6 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) /* Add dummy regions for the devices we don't implement yet, * so guest accesses don't cause unlogged crashes. */ - create_unimplemented_device("i2c-2", 0x40021000, 0x1000); create_unimplemented_device("PWM", 0x40028000, 0x1000); create_unimplemented_device("QEI-0", 0x4002c000, 0x1000); create_unimplemented_device("QEI-1", 0x4002d000, 0x1000); From 6d314cc04544969bd83521a315312702b8c166d1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Dec 2024 13:09:08 +0100 Subject: [PATCH 1312/2892] rust: pl011: extract conversion to RegisterOffset As an added bonus, this also makes the new function return u32 instead of u64, thus factoring some casts into a single place. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 137 ++++++++++++++++++------------- rust/hw/char/pl011/src/lib.rs | 2 +- 2 files changed, 79 insertions(+), 60 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c8496eeb1b..64e7234f62 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,6 +5,7 @@ use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, + ops::ControlFlow, os::raw::{c_int, c_uint, c_void}, }; @@ -222,19 +223,11 @@ impl PL011State { } } - pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow { + fn regs_read(&mut self, offset: RegisterOffset) -> ControlFlow { use RegisterOffset::*; - let value = match RegisterOffset::try_from(offset) { - Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - let device_id = self.get_class().device_id; - u32::from(device_id[(offset - 0xfe0) >> 2]) - } - Err(_) => { - // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); - 0 - } - Ok(DR) => { + ControlFlow::Break(match offset { + DR => { self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { @@ -251,69 +244,53 @@ impl PL011State { self.receive_status_error_clear.set_from_data(c); self.update(); // Must call qemu_chr_fe_accept_input, so return Continue: - let c = u32::from(c); - return std::ops::ControlFlow::Continue(u64::from(c)); + return ControlFlow::Continue(u32::from(c)); } - Ok(RSR) => u32::from(self.receive_status_error_clear), - Ok(FR) => u32::from(self.flags), - Ok(FBRD) => self.fbrd, - Ok(ILPR) => self.ilpr, - Ok(IBRD) => self.ibrd, - Ok(LCR_H) => u32::from(self.line_control), - Ok(CR) => u32::from(self.control), - Ok(FLS) => self.ifl, - Ok(IMSC) => self.int_enabled, - Ok(RIS) => self.int_level, - Ok(MIS) => self.int_level & self.int_enabled, - Ok(ICR) => { + RSR => u32::from(self.receive_status_error_clear), + FR => u32::from(self.flags), + FBRD => self.fbrd, + ILPR => self.ilpr, + IBRD => self.ibrd, + LCR_H => u32::from(self.line_control), + CR => u32::from(self.control), + FLS => self.ifl, + IMSC => self.int_enabled, + RIS => self.int_level, + MIS => self.int_level & self.int_enabled, + ICR => { // "The UARTICR Register is the interrupt clear register and is write-only" // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR 0 } - Ok(DMACR) => self.dmacr, - }; - std::ops::ControlFlow::Break(value.into()) + DMACR => self.dmacr, + }) } - pub fn write(&mut self, offset: hwaddr, value: u64) { + fn regs_write(&mut self, offset: RegisterOffset, value: u32) { // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; - let value: u32 = value as u32; - match RegisterOffset::try_from(offset) { - Err(_bad_offset) => { - eprintln!("write bad offset {offset} value {value}"); - } - Ok(DR) => { - // ??? Check if transmitter is enabled. - let ch: u8 = value as u8; - // XXX this blocks entire thread. Rewrite to use - // qemu_chr_fe_write and background I/O callbacks - - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - unsafe { - qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1); - } + match offset { + DR => { self.loopback_tx(value); self.int_level |= registers::INT_TX; self.update(); } - Ok(RSR) => { - self.receive_status_error_clear.reset(); + RSR => { + self.receive_status_error_clear = 0.into(); } - Ok(FR) => { + FR => { // flag writes are ignored } - Ok(ILPR) => { + ILPR => { self.ilpr = value; } - Ok(IBRD) => { + IBRD => { self.ibrd = value; } - Ok(FBRD) => { + FBRD => { self.fbrd = value; } - Ok(LCR_H) => { + LCR_H => { let new_val: registers::LineControl = value.into(); // Reset the FIFO state on FIFO enable or disable if self.line_control.fifos_enabled() != new_val.fifos_enabled() { @@ -336,26 +313,26 @@ impl PL011State { self.line_control = new_val; self.set_read_trigger(); } - Ok(CR) => { + CR => { // ??? Need to implement the enable bit. self.control = value.into(); self.loopback_mdmctrl(); } - Ok(FLS) => { + FLS => { self.ifl = value; self.set_read_trigger(); } - Ok(IMSC) => { + IMSC => { self.int_enabled = value; self.update(); } - Ok(RIS) => {} - Ok(MIS) => {} - Ok(ICR) => { + RIS => {} + MIS => {} + ICR => { self.int_level &= !value; self.update(); } - Ok(DMACR) => { + DMACR => { self.dmacr = value; if value & 3 > 0 { // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); @@ -570,6 +547,48 @@ impl PL011State { Ok(()) } + + pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow { + match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { + let device_id = self.get_class().device_id; + ControlFlow::Break(u64::from(device_id[(offset - 0xfe0) >> 2])) + } + Err(_) => { + // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + ControlFlow::Break(0) + } + Ok(field) => { + let result = self.regs_read(field); + match result { + ControlFlow::Break(value) => ControlFlow::Break(value.into()), + ControlFlow::Continue(value) => ControlFlow::Continue(value.into()), + } + } + } + } + + pub fn write(&mut self, offset: hwaddr, value: u64) { + if let Ok(field) = RegisterOffset::try_from(offset) { + // qemu_chr_fe_write_all() calls into the can_receive + // callback, so handle writes before entering PL011Registers. + if field == RegisterOffset::DR { + // ??? Check if transmitter is enabled. + let ch: u8 = value as u8; + // SAFETY: char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + // XXX this blocks entire thread. Rewrite to use + // qemu_chr_fe_write and background I/O callbacks + unsafe { + qemu_chr_fe_write_all(&mut self.char_backend, &ch, 1); + } + } + + self.regs_write(field, value as u32); + } else { + eprintln!("write bad offset {offset} value {value}"); + } + } } /// Which bits in the interrupt status matter for each outbound IRQ line ? diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 2baacba230..a35fff8d44 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -43,7 +43,7 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, qemu_api_macros::TryInto)] +#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] enum RegisterOffset { /// Data Register /// From 137612772e300a386f0f0c31486eae7d1008a68c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 6 Dec 2024 19:00:21 +0100 Subject: [PATCH 1313/2892] rust: pl011: extract CharBackend receive logic into a separate function Prepare for moving all references to the registers and the FIFO into a separate struct. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 64e7234f62..b14dcabdac 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -6,7 +6,7 @@ use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, ops::ControlFlow, - os::raw::{c_int, c_uint, c_void}, + os::raw::{c_int, c_void}, }; use qemu_api::{ @@ -478,6 +478,12 @@ impl PL011State { self.read_count < self.fifo_depth() } + pub fn receive(&mut self, ch: u32) { + if !self.loopback_enabled() { + self.put_fifo(ch) + } + } + pub fn event(&mut self, event: QEMUChrEvent) { if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { self.put_fifo(registers::Data::BREAK.into()); @@ -503,7 +509,7 @@ impl PL011State { 1 } - pub fn put_fifo(&mut self, value: c_uint) { + pub fn put_fifo(&mut self, value: u32) { let depth = self.fifo_depth(); assert!(depth > 0); let slot = (self.read_pos + self.read_count) & (depth - 1); @@ -626,12 +632,9 @@ pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { let mut state = NonNull::new(opaque).unwrap().cast::(); unsafe { - if state.as_ref().loopback_enabled() { - return; - } if size > 0 { debug_assert!(!buf.is_null()); - state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) + state.as_mut().receive(u32::from(buf.read_volatile())); } } } From ab6b6a8a55b5434b77dc229f86179c8d3ca55873 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 Jan 2025 00:26:04 +0100 Subject: [PATCH 1314/2892] rust: pl011: pull interrupt updates out of read/write ops qemu_irqs are not part of the vmstate, therefore they will remain in PL011State. Update them if needed after regs_read()/regs_write(). Apply #[must_use] to functions that return whether the interrupt state could have changed, so that it's harder to forget the call to update(). Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 84 ++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index b14dcabdac..0acb36b94e 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -242,7 +242,6 @@ impl PL011State { } // Update error bits. self.receive_status_error_clear.set_from_data(c); - self.update(); // Must call qemu_chr_fe_accept_input, so return Continue: return ControlFlow::Continue(u32::from(c)); } @@ -266,14 +265,15 @@ impl PL011State { }) } - fn regs_write(&mut self, offset: RegisterOffset, value: u32) { + fn regs_write(&mut self, offset: RegisterOffset, value: u32) -> bool { // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; match offset { DR => { - self.loopback_tx(value); + // interrupts always checked + let _ = self.loopback_tx(value); self.int_level |= registers::INT_TX; - self.update(); + return true; } RSR => { self.receive_status_error_clear = 0.into(); @@ -297,7 +297,7 @@ impl PL011State { self.reset_rx_fifo(); self.reset_tx_fifo(); } - if self.line_control.send_break() ^ new_val.send_break() { + let update = (self.line_control.send_break() != new_val.send_break()) && { let mut break_enable: c_int = new_val.send_break().into(); // SAFETY: self.char_backend is a valid CharBackend instance after it's been // initialized in realize(). @@ -308,15 +308,16 @@ impl PL011State { addr_of_mut!(break_enable).cast::(), ); } - self.loopback_break(break_enable > 0); - } + self.loopback_break(break_enable > 0) + }; self.line_control = new_val; self.set_read_trigger(); + return update; } CR => { // ??? Need to implement the enable bit. self.control = value.into(); - self.loopback_mdmctrl(); + return self.loopback_mdmctrl(); } FLS => { self.ifl = value; @@ -324,13 +325,13 @@ impl PL011State { } IMSC => { self.int_enabled = value; - self.update(); + return true; } RIS => {} MIS => {} ICR => { self.int_level &= !value; - self.update(); + return true; } DMACR => { self.dmacr = value; @@ -340,14 +341,12 @@ impl PL011State { } } } + false } #[inline] - fn loopback_tx(&mut self, value: u32) { - if !self.loopback_enabled() { - return; - } - + #[must_use] + fn loopback_tx(&mut self, value: u32) -> bool { // Caveat: // // In real hardware, TX loopback happens at the serial-bit level @@ -365,12 +364,13 @@ impl PL011State { // hardware flow-control is enabled. // // For simplicity, the above described is not emulated. - self.put_fifo(value); + self.loopback_enabled() && self.put_fifo(value) } - fn loopback_mdmctrl(&mut self) { + #[must_use] + fn loopback_mdmctrl(&mut self) -> bool { if !self.loopback_enabled() { - return; + return false; } /* @@ -411,13 +411,11 @@ impl PL011State { il |= Interrupt::RI as u32; } self.int_level = il; - self.update(); + true } - fn loopback_break(&mut self, enable: bool) { - if enable { - self.loopback_tx(registers::Data::BREAK.into()); - } + fn loopback_break(&mut self, enable: bool) -> bool { + enable && self.loopback_tx(registers::Data::BREAK.into()) } fn set_read_trigger(&mut self) { @@ -479,14 +477,17 @@ impl PL011State { } pub fn receive(&mut self, ch: u32) { - if !self.loopback_enabled() { - self.put_fifo(ch) + if !self.loopback_enabled() && self.put_fifo(ch) { + self.update(); } } pub fn event(&mut self, event: QEMUChrEvent) { if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { - self.put_fifo(registers::Data::BREAK.into()); + let update = self.put_fifo(registers::Data::BREAK.into()); + if update { + self.update(); + } } } @@ -509,7 +510,8 @@ impl PL011State { 1 } - pub fn put_fifo(&mut self, value: u32) { + #[must_use] + pub fn put_fifo(&mut self, value: u32) -> bool { let depth = self.fifo_depth(); assert!(depth > 0); let slot = (self.read_pos + self.read_count) & (depth - 1); @@ -522,8 +524,9 @@ impl PL011State { if self.read_count == self.read_trigger { self.int_level |= registers::INT_RX; - self.update(); + return true; } + false } pub fn update(&self) { @@ -555,7 +558,8 @@ impl PL011State { } pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow { - match RegisterOffset::try_from(offset) { + let mut update_irq = false; + let result = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { let device_id = self.get_class().device_id; ControlFlow::Break(u64::from(device_id[(offset - 0xfe0) >> 2])) @@ -564,17 +568,22 @@ impl PL011State { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); ControlFlow::Break(0) } - Ok(field) => { - let result = self.regs_read(field); - match result { - ControlFlow::Break(value) => ControlFlow::Break(value.into()), - ControlFlow::Continue(value) => ControlFlow::Continue(value.into()), + Ok(field) => match self.regs_read(field) { + ControlFlow::Break(value) => ControlFlow::Break(value.into()), + ControlFlow::Continue(value) => { + update_irq = true; + ControlFlow::Continue(value.into()) } - } + }, + }; + if update_irq { + self.update(); } + result } pub fn write(&mut self, offset: hwaddr, value: u64) { + let mut update_irq = false; if let Ok(field) = RegisterOffset::try_from(offset) { // qemu_chr_fe_write_all() calls into the can_receive // callback, so handle writes before entering PL011Registers. @@ -590,10 +599,13 @@ impl PL011State { } } - self.regs_write(field, value as u32); + update_irq = self.regs_write(field, value as u32); } else { eprintln!("write bad offset {offset} value {value}"); } + if update_irq { + self.update(); + } } } From 49bfe63f297f71c5d7e1578a8b69953430b7b532 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 Jan 2025 00:26:56 +0100 Subject: [PATCH 1315/2892] rust: pl011: extract PL011Registers Pull all the mutable fields of PL011State into a separate struct. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 247 ++++++++++++++----------- rust/hw/char/pl011/src/device_class.rs | 46 +++-- 2 files changed, 166 insertions(+), 127 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 0acb36b94e..91c71d0989 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -85,11 +85,8 @@ impl std::ops::Index for Fifo { } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] -/// PL011 Device Model in QEMU -pub struct PL011State { - pub parent_obj: ParentField, - pub iomem: MemoryRegion, +#[derive(Debug, Default, qemu_api_macros::offsets)] +pub struct PL011Registers { #[doc(alias = "fr")] pub flags: registers::Flags, #[doc(alias = "lcr")] @@ -109,8 +106,17 @@ pub struct PL011State { pub read_pos: u32, pub read_count: u32, pub read_trigger: u32, +} + +#[repr(C)] +#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] +/// PL011 Device Model in QEMU +pub struct PL011State { + pub parent_obj: ParentField, + pub iomem: MemoryRegion, #[doc(alias = "chr")] pub char_backend: CharBackend, + pub regs: PL011Registers, /// QEMU interrupts /// /// ```text @@ -169,61 +175,8 @@ impl DeviceImpl for PL011State { const RESET: Option = Some(Self::reset); } -impl PL011State { - /// Initializes a pre-allocated, unitialized instance of `PL011State`. - /// - /// # Safety - /// - /// `self` must point to a correctly sized and aligned location for the - /// `PL011State` type. It must not be called more than once on the same - /// location/instance. All its fields are expected to hold unitialized - /// values with the sole exception of `parent_obj`. - unsafe fn init(&mut self) { - const CLK_NAME: &CStr = c_str!("clk"); - - // SAFETY: - // - // self and self.iomem are guaranteed to be valid at this point since callers - // must make sure the `self` reference is valid. - unsafe { - memory_region_init_io( - addr_of_mut!(self.iomem), - addr_of_mut!(*self).cast::(), - &PL011_OPS, - addr_of_mut!(*self).cast::(), - Self::TYPE_NAME.as_ptr(), - 0x1000, - ); - } - - // SAFETY: - // - // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, - // we can overwrite the undefined value without side effects. This is - // safe since all PL011State instances are created by QOM code which - // calls this function to initialize the fields; therefore no code is - // able to access an invalid self.clock value. - unsafe { - let dev: &mut DeviceState = self.upcast_mut(); - self.clock = NonNull::new(qdev_init_clock_in( - dev, - CLK_NAME.as_ptr(), - None, /* pl011_clock_update */ - addr_of_mut!(*self).cast::(), - ClockEvent::ClockUpdate.0, - )) - .unwrap(); - } - } - - fn post_init(&self) { - self.init_mmio(&self.iomem); - for irq in self.interrupts.iter() { - self.init_irq(irq); - } - } - - fn regs_read(&mut self, offset: RegisterOffset) -> ControlFlow { +impl PL011Registers { + pub(self) fn read(&mut self, offset: RegisterOffset) -> ControlFlow { use RegisterOffset::*; ControlFlow::Break(match offset { @@ -265,7 +218,12 @@ impl PL011State { }) } - fn regs_write(&mut self, offset: RegisterOffset, value: u32) -> bool { + pub(self) fn write( + &mut self, + offset: RegisterOffset, + value: u32, + char_backend: *mut CharBackend, + ) -> bool { // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; match offset { @@ -303,7 +261,7 @@ impl PL011State { // initialized in realize(). unsafe { qemu_chr_fe_ioctl( - addr_of_mut!(self.char_backend), + char_backend, CHR_IOCTL_SERIAL_SET_BREAK as i32, addr_of_mut!(break_enable).cast::(), ); @@ -422,23 +380,6 @@ impl PL011State { self.read_trigger = 1; } - pub fn realize(&self) { - // SAFETY: self.char_backend has the correct size and alignment for a - // CharBackend object, and its callbacks are of the correct types. - unsafe { - qemu_chr_fe_set_handlers( - addr_of!(self.char_backend) as *mut CharBackend, - Some(pl011_can_receive), - Some(pl011_receive), - Some(pl011_event), - None, - addr_of!(*self).cast::() as *mut c_void, - core::ptr::null_mut(), - true, - ); - } - } - pub fn reset(&mut self) { self.line_control.reset(); self.receive_status_error_clear.reset(); @@ -471,26 +412,6 @@ impl PL011State { self.flags.set_transmit_fifo_empty(true); } - pub fn can_receive(&self) -> bool { - // trace_pl011_can_receive(s->lcr, s->read_count, r); - self.read_count < self.fifo_depth() - } - - pub fn receive(&mut self, ch: u32) { - if !self.loopback_enabled() && self.put_fifo(ch) { - self.update(); - } - } - - pub fn event(&mut self, event: QEMUChrEvent) { - if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { - let update = self.put_fifo(registers::Data::BREAK.into()); - if update { - self.update(); - } - } - } - #[inline] pub fn fifo_enabled(&self) -> bool { self.line_control.fifos_enabled() == registers::Mode::FIFO @@ -529,14 +450,7 @@ impl PL011State { false } - pub fn update(&self) { - let flags = self.int_level & self.int_enabled; - for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - irq.set(flags & i != 0); - } - } - - pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { + pub fn post_load(&mut self) -> Result<(), ()> { /* Sanity-check input state */ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { return Err(()); @@ -556,6 +470,63 @@ impl PL011State { Ok(()) } +} + +impl PL011State { + /// Initializes a pre-allocated, unitialized instance of `PL011State`. + /// + /// # Safety + /// + /// `self` must point to a correctly sized and aligned location for the + /// `PL011State` type. It must not be called more than once on the same + /// location/instance. All its fields are expected to hold unitialized + /// values with the sole exception of `parent_obj`. + unsafe fn init(&mut self) { + const CLK_NAME: &CStr = c_str!("clk"); + + // SAFETY: + // + // self and self.iomem are guaranteed to be valid at this point since callers + // must make sure the `self` reference is valid. + unsafe { + memory_region_init_io( + addr_of_mut!(self.iomem), + addr_of_mut!(*self).cast::(), + &PL011_OPS, + addr_of_mut!(*self).cast::(), + Self::TYPE_NAME.as_ptr(), + 0x1000, + ); + } + + self.regs = Default::default(); + + // SAFETY: + // + // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, + // we can overwrite the undefined value without side effects. This is + // safe since all PL011State instances are created by QOM code which + // calls this function to initialize the fields; therefore no code is + // able to access an invalid self.clock value. + unsafe { + let dev: &mut DeviceState = self.upcast_mut(); + self.clock = NonNull::new(qdev_init_clock_in( + dev, + CLK_NAME.as_ptr(), + None, /* pl011_clock_update */ + addr_of_mut!(*self).cast::(), + ClockEvent::ClockUpdate.0, + )) + .unwrap(); + } + } + + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.interrupts.iter() { + self.init_irq(irq); + } + } pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow { let mut update_irq = false; @@ -568,7 +539,7 @@ impl PL011State { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); ControlFlow::Break(0) } - Ok(field) => match self.regs_read(field) { + Ok(field) => match self.regs.read(field) { ControlFlow::Break(value) => ControlFlow::Break(value.into()), ControlFlow::Continue(value) => { update_irq = true; @@ -599,7 +570,7 @@ impl PL011State { } } - update_irq = self.regs_write(field, value as u32); + update_irq = self.regs.write(field, value as u32, &mut self.char_backend); } else { eprintln!("write bad offset {offset} value {value}"); } @@ -607,6 +578,64 @@ impl PL011State { self.update(); } } + + pub fn can_receive(&self) -> bool { + // trace_pl011_can_receive(s->lcr, s->read_count, r); + let regs = &self.regs; + regs.read_count < regs.fifo_depth() + } + + pub fn receive(&mut self, ch: u32) { + let regs = &mut self.regs; + let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch); + if update_irq { + self.update(); + } + } + + pub fn event(&mut self, event: QEMUChrEvent) { + let mut update_irq = false; + let regs = &mut self.regs; + if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() { + update_irq = regs.put_fifo(registers::Data::BREAK.into()); + } + if update_irq { + self.update() + } + } + + pub fn realize(&self) { + // SAFETY: self.char_backend has the correct size and alignment for a + // CharBackend object, and its callbacks are of the correct types. + unsafe { + qemu_chr_fe_set_handlers( + addr_of!(self.char_backend) as *mut CharBackend, + Some(pl011_can_receive), + Some(pl011_receive), + Some(pl011_event), + None, + addr_of!(*self).cast::() as *mut c_void, + core::ptr::null_mut(), + true, + ); + } + } + + pub fn reset(&mut self) { + self.regs.reset(); + } + + pub fn update(&self) { + let regs = &self.regs; + let flags = regs.int_level & regs.int_enabled; + for (irq, i) in self.interrupts.iter().zip(IRQMASK) { + irq.set(flags & i != 0); + } + } + + pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { + self.regs.post_load() + } } /// Which bits in the interrupt status matter for each outbound IRQ line ? diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 2336a76872..d94b98de7b 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,11 +6,11 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_of, vmstate_subsections, - vmstate_unused, zeroable::Zeroable, + bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, + vmstate_subsections, vmstate_unused, zeroable::Zeroable, }; -use crate::device::PL011State; +use crate::device::{PL011Registers, PL011State}; #[allow(clippy::missing_const_for_fn)] extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { @@ -40,6 +40,30 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { } } +static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { + name: c_str!("pl011/regs").as_ptr(), + version_id: 2, + minimum_version_id: 2, + fields: vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }, + ..Zeroable::ZERO +}; + pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { name: c_str!("pl011").as_ptr(), version_id: 2, @@ -47,21 +71,7 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { post_load: Some(pl011_post_load), fields: vmstate_fields! { vmstate_unused!(core::mem::size_of::()), - vmstate_of!(PL011State, flags), - vmstate_of!(PL011State, line_control), - vmstate_of!(PL011State, receive_status_error_clear), - vmstate_of!(PL011State, control), - vmstate_of!(PL011State, dmacr), - vmstate_of!(PL011State, int_enabled), - vmstate_of!(PL011State, int_level), - vmstate_of!(PL011State, read_fifo), - vmstate_of!(PL011State, ilpr), - vmstate_of!(PL011State, ibrd), - vmstate_of!(PL011State, fbrd), - vmstate_of!(PL011State, ifl), - vmstate_of!(PL011State, read_pos), - vmstate_of!(PL011State, read_count), - vmstate_of!(PL011State, read_trigger), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, PL011Registers), }, subsections: vmstate_subsections! { VMSTATE_PL011_CLOCK From a1ab4eed8d37e4afb78367d766edeadfdb489027 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 Jan 2025 00:28:09 +0100 Subject: [PATCH 1316/2892] rust: pl011: wrap registers with BqlRefCell This is a step towards making memory ops use a shared reference to the device type; it's not yet possible due to the calls to character device functions. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 46 ++++++++++++++++---------- rust/hw/char/pl011/src/device_class.rs | 8 ++--- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 91c71d0989..861b8645b7 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -109,14 +109,14 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] +#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField, pub iomem: MemoryRegion, #[doc(alias = "chr")] pub char_backend: CharBackend, - pub regs: PL011Registers, + pub regs: BqlRefCell, /// QEMU interrupts /// /// ```text @@ -528,6 +528,7 @@ impl PL011State { } } + #[allow(clippy::needless_pass_by_ref_mut)] pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow { let mut update_irq = false; let result = match RegisterOffset::try_from(offset) { @@ -539,7 +540,7 @@ impl PL011State { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); ControlFlow::Break(0) } - Ok(field) => match self.regs.read(field) { + Ok(field) => match self.regs.borrow_mut().read(field) { ControlFlow::Break(value) => ControlFlow::Break(value.into()), ControlFlow::Continue(value) => { update_irq = true; @@ -570,7 +571,10 @@ impl PL011State { } } - update_irq = self.regs.write(field, value as u32, &mut self.char_backend); + update_irq = self + .regs + .borrow_mut() + .write(field, value as u32, &mut self.char_backend); } else { eprintln!("write bad offset {offset} value {value}"); } @@ -581,24 +585,30 @@ impl PL011State { pub fn can_receive(&self) -> bool { // trace_pl011_can_receive(s->lcr, s->read_count, r); - let regs = &self.regs; + let regs = self.regs.borrow(); regs.read_count < regs.fifo_depth() } - pub fn receive(&mut self, ch: u32) { - let regs = &mut self.regs; + pub fn receive(&self, ch: u32) { + let mut regs = self.regs.borrow_mut(); let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch); + // Release the BqlRefCell before calling self.update() + drop(regs); + if update_irq { self.update(); } } - pub fn event(&mut self, event: QEMUChrEvent) { + pub fn event(&self, event: QEMUChrEvent) { let mut update_irq = false; - let regs = &mut self.regs; + let mut regs = self.regs.borrow_mut(); if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() { update_irq = regs.put_fifo(registers::Data::BREAK.into()); } + // Release the BqlRefCell before calling self.update() + drop(regs); + if update_irq { self.update() } @@ -622,19 +632,19 @@ impl PL011State { } pub fn reset(&mut self) { - self.regs.reset(); + self.regs.borrow_mut().reset(); } pub fn update(&self) { - let regs = &self.regs; + let regs = self.regs.borrow(); let flags = regs.int_level & regs.int_enabled; for (irq, i) in self.interrupts.iter().zip(IRQMASK) { irq.set(flags & i != 0); } } - pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { - self.regs.post_load() + pub fn post_load(&self, _version_id: u32) -> Result<(), ()> { + self.regs.borrow_mut().post_load() } } @@ -671,11 +681,11 @@ pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { /// /// The buffer and size arguments must also be valid. pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { - let mut state = NonNull::new(opaque).unwrap().cast::(); + let state = NonNull::new(opaque).unwrap().cast::(); unsafe { if size > 0 { debug_assert!(!buf.is_null()); - state.as_mut().receive(u32::from(buf.read_volatile())); + state.as_ref().receive(u32::from(buf.read_volatile())); } } } @@ -686,8 +696,8 @@ pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { - let mut state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_mut().event(event) } + let state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_ref().event(event) } } /// # Safety @@ -712,7 +722,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField, diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index d94b98de7b..8a157a663f 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,7 +6,7 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, + bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, zeroable::Zeroable, }; @@ -31,8 +31,8 @@ static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { }; extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - let mut state = NonNull::new(opaque).unwrap().cast::(); - let result = unsafe { state.as_mut().post_load(version_id as u32) }; + let state = NonNull::new(opaque).unwrap().cast::(); + let result = unsafe { state.as_ref().post_load(version_id as u32) }; if result.is_err() { -1 } else { @@ -71,7 +71,7 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { post_load: Some(pl011_post_load), fields: vmstate_fields! { vmstate_unused!(core::mem::size_of::()), - vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, PL011Registers), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), }, subsections: vmstate_subsections! { VMSTATE_PL011_CLOCK From c44818a5fdbcca9a4e3474be70f8a2615e19922b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Dec 2024 17:28:26 +0100 Subject: [PATCH 1317/2892] rust: pl011: remove duplicate definitions Unify the "Interrupt" enum and the "INT_*" constants with a struct that contains the bits. The "int_level" and "int_enabled" fields could use a crate such as "bitflags". Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 32 ++++++++++------------ rust/hw/char/pl011/src/lib.rs | 46 +++++++++++--------------------- 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 861b8645b7..6c47d3045a 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -191,7 +191,7 @@ impl PL011Registers { self.flags.set_receive_fifo_empty(true); } if self.read_count + 1 == self.read_trigger { - self.int_level &= !registers::INT_RX; + self.int_level &= !Interrupt::RX.0; } // Update error bits. self.receive_status_error_clear.set_from_data(c); @@ -230,7 +230,7 @@ impl PL011Registers { DR => { // interrupts always checked let _ = self.loopback_tx(value); - self.int_level |= registers::INT_TX; + self.int_level |= Interrupt::TX.0; return true; } RSR => { @@ -354,19 +354,19 @@ impl PL011Registers { // Change interrupts based on updated FR let mut il = self.int_level; - il &= !Interrupt::MS; + il &= !Interrupt::MS.0; if self.flags.data_set_ready() { - il |= Interrupt::DSR as u32; + il |= Interrupt::DSR.0; } if self.flags.data_carrier_detect() { - il |= Interrupt::DCD as u32; + il |= Interrupt::DCD.0; } if self.flags.clear_to_send() { - il |= Interrupt::CTS as u32; + il |= Interrupt::CTS.0; } if self.flags.ring_indicator() { - il |= Interrupt::RI as u32; + il |= Interrupt::RI.0; } self.int_level = il; true @@ -444,7 +444,7 @@ impl PL011Registers { } if self.read_count == self.read_trigger { - self.int_level |= registers::INT_RX; + self.int_level |= Interrupt::RX.0; return true; } false @@ -651,16 +651,12 @@ impl PL011State { /// Which bits in the interrupt status matter for each outbound IRQ line ? const IRQMASK: [u32; 6] = [ /* combined IRQ */ - Interrupt::E - | Interrupt::MS - | Interrupt::RT as u32 - | Interrupt::TX as u32 - | Interrupt::RX as u32, - Interrupt::RX as u32, - Interrupt::TX as u32, - Interrupt::RT as u32, - Interrupt::MS, - Interrupt::E, + Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0, + Interrupt::RX.0, + Interrupt::TX.0, + Interrupt::RT.0, + Interrupt::MS.0, + Interrupt::E.0, ]; /// # Safety diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index a35fff8d44..e2df4586bc 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -100,7 +100,6 @@ enum RegisterOffset { //Reserved = 0x04C, } -#[allow(dead_code)] mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. @@ -521,38 +520,23 @@ mod registers { } /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC - pub const INT_OE: u32 = 1 << 10; - pub const INT_BE: u32 = 1 << 9; - pub const INT_PE: u32 = 1 << 8; - pub const INT_FE: u32 = 1 << 7; - pub const INT_RT: u32 = 1 << 6; - pub const INT_TX: u32 = 1 << 5; - pub const INT_RX: u32 = 1 << 4; - pub const INT_DSR: u32 = 1 << 3; - pub const INT_DCD: u32 = 1 << 2; - pub const INT_CTS: u32 = 1 << 1; - pub const INT_RI: u32 = 1 << 0; - pub const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; - - #[repr(u32)] - pub enum Interrupt { - OE = 1 << 10, - BE = 1 << 9, - PE = 1 << 8, - FE = 1 << 7, - RT = 1 << 6, - TX = 1 << 5, - RX = 1 << 4, - DSR = 1 << 3, - DCD = 1 << 2, - CTS = 1 << 1, - RI = 1 << 0, - } + pub struct Interrupt(pub u32); impl Interrupt { - pub const E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + pub const OE: Self = Self(1 << 10); + pub const BE: Self = Self(1 << 9); + pub const PE: Self = Self(1 << 8); + pub const FE: Self = Self(1 << 7); + pub const RT: Self = Self(1 << 6); + pub const TX: Self = Self(1 << 5); + pub const RX: Self = Self(1 << 4); + pub const DSR: Self = Self(1 << 3); + pub const DCD: Self = Self(1 << 2); + pub const CTS: Self = Self(1 << 1); + pub const RI: Self = Self(1 << 0); + + pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); + pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0); } } From b3a29b3dc0d3f1e0f177b2be3edeb0d74c061b15 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 17:56:00 +0100 Subject: [PATCH 1318/2892] rust: pl011: pull device-specific code out of MemoryRegionOps callbacks read() can now return a simple u64. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 23 +++++++++++++---------- rust/hw/char/pl011/src/memory_ops.rs | 18 ++---------------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 6c47d3045a..945200f046 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -12,9 +12,10 @@ use std::{ use qemu_api::{ bindings::{ error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_new, - qdev_prop_set_chr, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, - qemu_irq, sysbus_connect_irq, sysbus_mmio_map, sysbus_realize_and_unref, CharBackend, - Chardev, Clock, ClockEvent, MemoryRegion, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, + qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, + qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, + sysbus_realize_and_unref, CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, + QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, }, c_str, impl_vmstate_forward, irq::InterruptSource, @@ -528,30 +529,32 @@ impl PL011State { } } - #[allow(clippy::needless_pass_by_ref_mut)] - pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow { + pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { let mut update_irq = false; let result = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { let device_id = self.get_class().device_id; - ControlFlow::Break(u64::from(device_id[(offset - 0xfe0) >> 2])) + u32::from(device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); - ControlFlow::Break(0) + 0 } Ok(field) => match self.regs.borrow_mut().read(field) { - ControlFlow::Break(value) => ControlFlow::Break(value.into()), + ControlFlow::Break(value) => value, ControlFlow::Continue(value) => { update_irq = true; - ControlFlow::Continue(value.into()) + value } }, }; if update_irq { self.update(); + unsafe { + qemu_chr_fe_accept_input(&mut self.char_backend); + } } - result + result.into() } pub fn write(&mut self, offset: hwaddr, value: u64) { diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs index a286003d13..432d326389 100644 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -24,25 +24,11 @@ pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { }; unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { - assert!(!opaque.is_null()); let mut state = NonNull::new(opaque).unwrap().cast::(); - let val = unsafe { state.as_mut().read(addr, size) }; - match val { - std::ops::ControlFlow::Break(val) => val, - std::ops::ControlFlow::Continue(val) => { - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; - unsafe { - qemu_chr_fe_accept_input(cb_ptr); - } - - val - } - } + unsafe { state.as_mut() }.read(addr, size) } unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { let mut state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_mut().write(addr, data) } + unsafe { state.as_mut() }.write(addr, data); } From 20bcc96f458dafb9fcf84e240545c8136ac7443f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 18:13:30 +0100 Subject: [PATCH 1319/2892] rust: pl011: drop use of ControlFlow It is a poor match for what the code is doing, anyway. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 39 +++++++++++++++----------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 945200f046..bb6a6e4daf 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,7 +5,6 @@ use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, - ops::ControlFlow, os::raw::{c_int, c_void}, }; @@ -177,10 +176,11 @@ impl DeviceImpl for PL011State { } impl PL011Registers { - pub(self) fn read(&mut self, offset: RegisterOffset) -> ControlFlow { + pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { use RegisterOffset::*; - ControlFlow::Break(match offset { + let mut update = false; + let result = match offset { DR => { self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; @@ -196,8 +196,9 @@ impl PL011Registers { } // Update error bits. self.receive_status_error_clear.set_from_data(c); - // Must call qemu_chr_fe_accept_input, so return Continue: - return ControlFlow::Continue(u32::from(c)); + // Must call qemu_chr_fe_accept_input + update = true; + u32::from(c) } RSR => u32::from(self.receive_status_error_clear), FR => u32::from(self.flags), @@ -216,7 +217,8 @@ impl PL011Registers { 0 } DMACR => self.dmacr, - }) + }; + (update, result) } pub(self) fn write( @@ -530,31 +532,26 @@ impl PL011State { } pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { - let mut update_irq = false; - let result = match RegisterOffset::try_from(offset) { + match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { let device_id = self.get_class().device_id; - u32::from(device_id[(offset - 0xfe0) >> 2]) + u64::from(device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); 0 } - Ok(field) => match self.regs.borrow_mut().read(field) { - ControlFlow::Break(value) => value, - ControlFlow::Continue(value) => { - update_irq = true; - value + Ok(field) => { + let (update_irq, result) = self.regs.borrow_mut().read(field); + if update_irq { + self.update(); + unsafe { + qemu_chr_fe_accept_input(&mut self.char_backend); + } } - }, - }; - if update_irq { - self.update(); - unsafe { - qemu_chr_fe_accept_input(&mut self.char_backend); + result.into() } } - result.into() } pub fn write(&mut self, offset: hwaddr, value: u64) { From af7edb1d326de0af565b48c663163c7e5050e03c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Dec 2024 12:40:18 +0100 Subject: [PATCH 1320/2892] rust: qdev: make reset take a shared reference Because register reset is within a borrow_mut() call, reset does not need anymore a mut reference to the PL011State. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 ++-- rust/qemu-api/src/qdev.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bb6a6e4daf..8050ede9c8 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -172,7 +172,7 @@ impl DeviceImpl for PL011State { Some(&device_class::VMSTATE_PL011) } const REALIZE: Option = Some(Self::realize); - const RESET: Option = Some(Self::reset); + const RESET: Option = Some(Self::reset); } impl PL011Registers { @@ -631,7 +631,7 @@ impl PL011State { } } - pub fn reset(&mut self) { + pub fn reset(&self) { self.regs.borrow_mut().reset(); } diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 42429903aa..f4c75c752f 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -30,7 +30,7 @@ pub trait DeviceImpl { /// /// Rust does not yet support the three-phase reset protocol; this is /// usually okay for leaf classes. - const RESET: Option = None; + const RESET: Option = None; /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants From aaf3778baaa6408460ec6e6636babbdf0b92c101 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 23 Jan 2025 19:07:28 +0100 Subject: [PATCH 1321/2892] rust/zeroable: Implement Zeroable with const_zero macro The `const_zero` crate provides a nice macro to zero type-specific constants, which doesn't need to enumerates the fields one by one. Introduce the `const_zero` macro to QEMU (along with its documentation), and use it to simplify the implementation of `Zeroable` trait. Suggested-by: Paolo Bonzini Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250123163143.679841-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/zeroable.rs | 137 +++++++++++++++------------------- 1 file changed, 61 insertions(+), 76 deletions(-) diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 57cac96de0..7b04947cb6 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -use std::ptr; - /// Encapsulates the requirement that /// `MaybeUninit::::zeroed().assume_init()` does not cause undefined /// behavior. This trait in principle could be implemented as just: /// /// ``` -/// pub unsafe trait Zeroable: Default { +/// pub unsafe trait Zeroable { /// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; /// } /// ``` @@ -29,23 +27,61 @@ pub unsafe trait Zeroable: Default { const ZERO: Self; } -unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 { - const ZERO: Self = Self { i: 0 }; +/// A macro that acts similarly to [`core::mem::zeroed()`], only is const +/// +/// ## Safety +/// +/// Similar to `core::mem::zeroed()`, except this zeroes padding bits. Zeroed +/// padding usually isn't relevant to safety, but might be if a C union is used. +/// +/// Just like for `core::mem::zeroed()`, an all zero byte pattern might not +/// be a valid value for a type, as is the case for references `&T` and `&mut +/// T`. Reference types trigger a (denied by default) lint and cause immediate +/// undefined behavior if the lint is ignored +/// +/// ```rust compile_fail +/// use const_zero::const_zero; +/// // error: any use of this value will cause an error +/// // note: `#[deny(const_err)]` on by default +/// const STR: &str = unsafe{const_zero!(&'static str)}; +/// ``` +/// +/// `const_zero` does not work on unsized types: +/// +/// ```rust compile_fail +/// use const_zero::const_zero; +/// // error[E0277]: the size for values of type `[u8]` cannot be known at compilation time +/// const BYTES: [u8] = unsafe{const_zero!([u8])}; +/// ``` +/// ## Differences with `core::mem::zeroed` +/// +/// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't +macro_rules! const_zero { + // This macro to produce a type-generic zero constant is taken from the + // const_zero crate (v0.1.1): + // + // https://docs.rs/const-zero/latest/src/const_zero/lib.rs.html + // + // and used under MIT license + ($type_:ty) => {{ + const TYPE_SIZE: ::core::primitive::usize = ::core::mem::size_of::<$type_>(); + union TypeAsBytes { + bytes: [::core::primitive::u8; TYPE_SIZE], + inner: ::core::mem::ManuallyDrop<$type_>, + } + const ZERO_BYTES: TypeAsBytes = TypeAsBytes { + bytes: [0; TYPE_SIZE], + }; + ::core::mem::ManuallyDrop::<$type_>::into_inner(ZERO_BYTES.inner) + }}; } -unsafe impl Zeroable for crate::bindings::Property { - const ZERO: Self = Self { - name: ptr::null(), - info: ptr::null(), - offset: 0, - bitnr: 0, - bitmask: 0, - set_default: false, - defval: Zeroable::ZERO, - arrayoffset: 0, - arrayinfo: ptr::null(), - arrayfieldsize: 0, - link_type: ptr::null(), +/// A wrapper to implement the `Zeroable` trait through the `const_zero` macro. +macro_rules! impl_zeroable { + ($type:ty) => { + unsafe impl Zeroable for $type { + const ZERO: Self = unsafe { const_zero!($type) }; + } }; } @@ -57,61 +93,10 @@ impl Default for crate::bindings::VMStateFlags { } } -unsafe impl Zeroable for crate::bindings::VMStateFlags { - const ZERO: Self = Self(0); -} - -unsafe impl Zeroable for crate::bindings::VMStateField { - const ZERO: Self = Self { - name: ptr::null(), - err_hint: ptr::null(), - offset: 0, - size: 0, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: ptr::null(), - flags: Zeroable::ZERO, - vmsd: ptr::null(), - version_id: 0, - struct_version_id: 0, - field_exists: None, - }; -} - -unsafe impl Zeroable for crate::bindings::VMStateDescription { - const ZERO: Self = Self { - name: ptr::null(), - unmigratable: false, - early_setup: false, - version_id: 0, - minimum_version_id: 0, - priority: crate::bindings::MigrationPriority::MIG_PRI_DEFAULT, - pre_load: None, - post_load: None, - pre_save: None, - post_save: None, - needed: None, - dev_unplug_pending: None, - fields: ptr::null(), - subsections: ptr::null(), - }; -} - -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 { - const ZERO: Self = Self { - min_access_size: 0, - max_access_size: 0, - unaligned: false, - accepts: None, - }; -} - -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 { - const ZERO: Self = Self { - min_access_size: 0, - max_access_size: 0, - unaligned: false, - }; -} +impl_zeroable!(crate::bindings::Property__bindgen_ty_1); +impl_zeroable!(crate::bindings::Property); +impl_zeroable!(crate::bindings::VMStateFlags); +impl_zeroable!(crate::bindings::VMStateField); +impl_zeroable!(crate::bindings::VMStateDescription); +impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1); +impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2); From d28ece2487fb13f93fcd7eb870cdc64412027c34 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 17 Jan 2025 11:59:55 +0100 Subject: [PATCH 1322/2892] rust: qemu-api: add sub-subclass to the integration tests Signed-off-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/tests.rs | 56 ++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 526c3f4f8e..5c3e75ed3d 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -14,8 +14,8 @@ use qemu_api::{ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceImpl, DeviceState, Property}, - qom::{ObjectImpl, ParentField}, + qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, + qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -37,6 +37,10 @@ pub struct DummyState { qom_isa!(DummyState: Object, DeviceState); +pub struct DummyClass { + parent_class: ::Class, +} + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -49,7 +53,7 @@ declare_properties! { } unsafe impl ObjectType for DummyState { - type Class = ::Class; + type Class = DummyClass; const TYPE_NAME: &'static CStr = c_str!("dummy"); } @@ -67,6 +71,51 @@ impl DeviceImpl for DummyState { } } +// `impl ClassInitImpl for T` doesn't work since it violates +// orphan rule. +impl ClassInitImpl for DummyState { + fn class_init(klass: &mut DummyClass) { + >::class_init(&mut klass.parent_class); + } +} + +#[derive(qemu_api_macros::offsets)] +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct DummyChildState { + parent: ParentField, +} + +qom_isa!(DummyChildState: Object, DeviceState, DummyState); + +pub struct DummyChildClass { + parent_class: ::Class, +} + +unsafe impl ObjectType for DummyChildState { + type Class = DummyChildClass; + const TYPE_NAME: &'static CStr = c_str!("dummy_child"); +} + +impl ObjectImpl for DummyChildState { + type ParentType = DummyState; + const ABSTRACT: bool = false; +} + +impl DeviceImpl for DummyChildState {} + +impl ClassInitImpl for DummyChildState { + fn class_init(klass: &mut DummyClass) { + >::class_init(&mut klass.parent_class); + } +} + +impl ClassInitImpl for DummyChildState { + fn class_init(klass: &mut DummyChildClass) { + >::class_init(&mut klass.parent_class); + } +} + fn init_qom() { static ONCE: BqlCell = BqlCell::new(false); @@ -85,6 +134,7 @@ fn test_object_new() { init_qom(); unsafe { object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); + object_unref(object_new(DummyChildState::TYPE_NAME.as_ptr()).cast()); } } From cb5f6ca8af0f1dd2f55008855e542ec33479e929 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 24 Jan 2025 11:17:09 +0100 Subject: [PATCH 1323/2892] tests/functional: Add a test for the arm microbit machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't have any functional tests for this machine yet, thus let's add a test with a MicroPython binary that is available online (thanks to Joel Stanley for providing it, see: https://www.mail-archive.com/qemu-devel@nongnu.org/msg606064.html ). Signed-off-by: Thomas Huth Reviewed-by: Alex Bennée Message-id: 20250124101709.1591761-1-thuth@redhat.com Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_arm_microbit.py | 31 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100755 tests/functional/test_arm_microbit.py diff --git a/MAINTAINERS b/MAINTAINERS index 7be3d8f431..bb96a00db0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1157,6 +1157,7 @@ F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c +F: tests/functional/test_arm_microbit.py F: docs/system/arm/nrf.rst ARM PL011 Rust device diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b7719ab85f..b62f714220 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -91,6 +91,7 @@ tests_arm_system_thorough = [ 'arm_cubieboard', 'arm_emcraft_sf2', 'arm_integratorcp', + 'arm_microbit', 'arm_orangepi', 'arm_quanta_gsj', 'arm_raspi2', diff --git a/tests/functional/test_arm_microbit.py b/tests/functional/test_arm_microbit.py new file mode 100755 index 0000000000..68ea4e73d6 --- /dev/null +++ b/tests/functional/test_arm_microbit.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright 2025, The QEMU Project Developers. +# +# A functional test that runs MicroPython on the arm microbit machine. + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class MicrobitMachine(QemuSystemTest): + + ASSET_MICRO = Asset('https://ozlabs.org/~joel/microbit-micropython.hex', + '021641f93dfb11767d4978dbb3ca7f475d1b13c69e7f4aec3382f212636bffd6') + + def test_arm_microbit(self): + self.set_machine('microbit') + + micropython = self.ASSET_MICRO.fetch() + self.vm.set_console() + self.vm.add_args('-device', f'loader,file={micropython}') + self.vm.launch() + wait_for_console_pattern(self, 'Type "help()" for more information.') + exec_command_and_wait_for_pattern(self, 'import machine as mch', '>>>') + exec_command_and_wait_for_pattern(self, 'mch.reset()', 'MicroPython') + wait_for_console_pattern(self, '>>>') + +if __name__ == '__main__': + QemuSystemTest.main() From 1edc3d43f20df0d04f8d00b906ba19fed37512a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:23 +0000 Subject: [PATCH 1324/2892] target/arm: arm_reset_sve_state() should set FPSR, not FPCR The pseudocode ResetSVEState() does: FPSR = ZeroExtend(0x0800009f<31:0>, 64); but QEMU's arm_reset_sve_state() called vfp_set_fpcr() by accident. Before the advent of FEAT_AFP, this was only setting a collection of RES0 bits, which vfp_set_fpsr() would then ignore, so the only effect was that we didn't actually set the FPSR the way we are supposed to do. Once FEAT_AFP is implemented, setting the bottom bits of FPSR will change the floating point behaviour. Call vfp_set_fpsr(), as we ought to. (Note for stable backports: commit 7f2a01e7368f9 moved this function from sme_helper.c to helper.c, but it had the same bug before the move too.) Cc: qemu-stable@nongnu.org Fixes: f84734b87461 ("target/arm: Implement SMSTART, SMSTOP") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-4-peter.maydell@linaro.org --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6399767851..40bdfc851a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6413,7 +6413,7 @@ static void arm_reset_sve_state(CPUARMState *env) memset(env->vfp.zregs, 0, sizeof(env->vfp.zregs)); /* Recall that FFR is stored as pregs[16]. */ memset(env->vfp.pregs, 0, sizeof(env->vfp.pregs)); - vfp_set_fpcr(env, 0x0800009f); + vfp_set_fpsr(env, 0x0800009f); } void aarch64_set_svcr(CPUARMState *env, uint64_t new, uint64_t mask) From f10dee833f5b810d7c5ac036e3b5937d388f7b3b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:24 +0000 Subject: [PATCH 1325/2892] target/arm: Use FPSR_ constants in vfp_exceptbits_from_host() Use the FPSR_ named constants in vfp_exceptbits_from_host(), rather than hardcoded magic numbers. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-5-peter.maydell@linaro.org --- target/arm/vfp_helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index fc20a56753..fcc9e5d382 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -39,22 +39,22 @@ static inline int vfp_exceptbits_from_host(int host_bits) int target_bits = 0; if (host_bits & float_flag_invalid) { - target_bits |= 1; + target_bits |= FPSR_IOC; } if (host_bits & float_flag_divbyzero) { - target_bits |= 2; + target_bits |= FPSR_DZC; } if (host_bits & float_flag_overflow) { - target_bits |= 4; + target_bits |= FPSR_OFC; } if (host_bits & (float_flag_underflow | float_flag_output_denormal)) { - target_bits |= 8; + target_bits |= FPSR_UFC; } if (host_bits & float_flag_inexact) { - target_bits |= 0x10; + target_bits |= FPSR_IXC; } if (host_bits & float_flag_input_denormal) { - target_bits |= 0x80; + target_bits |= FPSR_IDC; } return target_bits; } From eda8d53083956f31c2ffe4ae62bb5883eda84be5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:25 +0000 Subject: [PATCH 1326/2892] target/arm: Use uint32_t in vfp_exceptbits_from_host() In vfp_exceptbits_from_host(), we accumulate the FPSR flags in an "int", and our return type is also "int". However, the only callsite returns the same information as a uint32_t, and more generally we handle FPSR values in the code as uint32_t, not int. Bring this function in to line with that convention. There is no behaviour change because none of the FPSR bits we set in this function are bit 31. The input argument to the function remains 'int' because that is the return type of the softfloat get_float_exception_flags(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-6-peter.maydell@linaro.org --- target/arm/vfp_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index fcc9e5d382..afc41420eb 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -34,9 +34,9 @@ #ifdef CONFIG_TCG /* Convert host exception flags to vfp form. */ -static inline int vfp_exceptbits_from_host(int host_bits) +static inline uint32_t vfp_exceptbits_from_host(int host_bits) { - int target_bits = 0; + uint32_t target_bits = 0; if (host_bits & float_flag_invalid) { target_bits |= FPSR_IOC; From 2208cb46e60a825768b0d6aad1bd809f7b235bd1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:26 +0000 Subject: [PATCH 1327/2892] target/arm: Define new fp_status_a32 and fp_status_a64 We want to split the existing fp_status in the Arm CPUState into separate float_status fields for AArch32 and AArch64. (This is because new control bits defined by FEAT_AFP only have an effect for AArch64, not AArch32.) To make this split we will: * define new fp_status_a32 and fp_status_a64 which have identical behaviour to the existing fp_status * move existing uses of fp_status to fp_status_a32 or fp_status_a64 as appropriate * delete the old fp_status when it has no uses left In this patch we add the new float_status fields. We will also need to split fp_status_f16, but we will do that as a separate series of patches. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-7-peter.maydell@linaro.org --- target/arm/cpu.c | 2 ++ target/arm/cpu.h | 4 ++++ target/arm/tcg/translate.h | 12 ++++++++++++ target/arm/vfp_helper.c | 12 ++++++++++++ 4 files changed, 30 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index dc0231233a..8bdd535db9 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -573,6 +573,8 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) set_default_nan_mode(1, &env->vfp.standard_fp_status); set_default_nan_mode(1, &env->vfp.standard_fp_status_f16); arm_set_default_fp_behaviours(&env->vfp.fp_status); + arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); + arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 9a6e8e589c..337c538374 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -634,6 +634,8 @@ typedef struct CPUArchState { /* There are a number of distinct float control structures: * * fp_status: is the "normal" fp status. + * fp_status_a32: is the "normal" fp status for AArch32 insns + * fp_status_a64: is the "normal" fp status for AArch64 insns * fp_status_fp16: used for half-precision calculations * standard_fp_status : the ARM "Standard FPSCR Value" * standard_fp_status_fp16 : used for half-precision @@ -659,6 +661,8 @@ typedef struct CPUArchState { * an explicit FPSCR read. */ float_status fp_status; + float_status fp_status_a32; + float_status fp_status_a64; float_status fp_status_f16; float_status standard_fp_status; float_status standard_fp_status_f16; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 2d37d7c9f2..c8414d94d5 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -671,6 +671,8 @@ static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb) */ typedef enum ARMFPStatusFlavour { FPST_FPCR, + FPST_A32, + FPST_A64, FPST_FPCR_F16, FPST_STD, FPST_STD_F16, @@ -686,6 +688,10 @@ typedef enum ARMFPStatusFlavour { * * FPST_FPCR * for non-FP16 operations controlled by the FPCR + * FPST_A32 + * for AArch32 non-FP16 operations controlled by the FPCR + * FPST_A64 + * for AArch64 non-FP16 operations controlled by the FPCR * FPST_FPCR_F16 * for operations controlled by the FPCR where FPCR.FZ16 is to be used * FPST_STD @@ -702,6 +708,12 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) case FPST_FPCR: offset = offsetof(CPUARMState, vfp.fp_status); break; + case FPST_A32: + offset = offsetof(CPUARMState, vfp.fp_status_a32); + break; + case FPST_A64: + offset = offsetof(CPUARMState, vfp.fp_status_a64); + break; case FPST_FPCR_F16: offset = offsetof(CPUARMState, vfp.fp_status_f16); break; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index afc41420eb..7475f97e0c 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -64,6 +64,8 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) uint32_t i; i = get_float_exception_flags(&env->vfp.fp_status); + i |= get_float_exception_flags(&env->vfp.fp_status_a32); + i |= get_float_exception_flags(&env->vfp.fp_status_a64); i |= get_float_exception_flags(&env->vfp.standard_fp_status); /* FZ16 does not generate an input denormal exception. */ i |= (get_float_exception_flags(&env->vfp.fp_status_f16) @@ -81,6 +83,8 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) * be the architecturally up-to-date exception flag information first. */ set_float_exception_flags(0, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.fp_status_a32); + set_float_exception_flags(0, &env->vfp.fp_status_a64); set_float_exception_flags(0, &env->vfp.fp_status_f16); set_float_exception_flags(0, &env->vfp.standard_fp_status); set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); @@ -109,6 +113,8 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) break; } set_float_rounding_mode(i, &env->vfp.fp_status); + set_float_rounding_mode(i, &env->vfp.fp_status_a32); + set_float_rounding_mode(i, &env->vfp.fp_status_a64); set_float_rounding_mode(i, &env->vfp.fp_status_f16); } if (changed & FPCR_FZ16) { @@ -122,10 +128,16 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) bool ftz_enabled = val & FPCR_FZ; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a32); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a32); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a64); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a64); } if (changed & FPCR_DN) { bool dnan_enabled = val & FPCR_DN; set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); } } From 57bd2f30ff50642dc32b3b2a4232054f3b6d664e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:27 +0000 Subject: [PATCH 1328/2892] target/arm: Use vfp.fp_status_a64 in A64-only helper functions Switch from vfp.fp_status to vfp.fp_status_a64 for helpers which: * directly reference an fp_status field * are called only from the A64 decoder * are not called inside a set_rmode/restore_rmode sequence Signed-off-by: Peter Maydell Message-id: 20250124162836.2332150-8-peter.maydell@linaro.org Reviewed-by: Richard Henderson --- target/arm/tcg/sme_helper.c | 2 +- target/arm/tcg/vec_helper.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index a0e6b4a41e..2aad00d3ad 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1044,7 +1044,7 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, * round-to-odd -- see above. */ fpst_f16 = env->vfp.fp_status_f16; - fpst_std = env->vfp.fp_status; + fpst_std = env->vfp.fp_status_a64; set_default_nan_mode(true, &fpst_std); set_default_nan_mode(true, &fpst_f16); fpst_odd = fpst_std; diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index e3083c6e84..011726a72d 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2066,7 +2066,7 @@ void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { - do_fmlal(vd, vn, vm, &env->vfp.fp_status, desc, + do_fmlal(vd, vn, vm, &env->vfp.fp_status_a64, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } @@ -2076,7 +2076,7 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, intptr_t i, oprsz = simd_oprsz(desc); uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - float_status *status = &env->vfp.fp_status; + float_status *status = &env->vfp.fp_status_a64; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); for (i = 0; i < oprsz; i += sizeof(float32)) { @@ -2128,7 +2128,7 @@ void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { - do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc, + do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status_a64, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } @@ -2139,7 +2139,7 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); - float_status *status = &env->vfp.fp_status; + float_status *status = &env->vfp.fp_status_a64; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); for (i = 0; i < oprsz; i += 16) { From 75df4e86097062ce3f0926cf2c4afd837edfb286 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 11:40:13 +0000 Subject: [PATCH 1329/2892] target/arm: Use fp_status_a64 or fp_status_a32 in is_ebf() In is_ebf(), we might be called for A64 or A32, but we have the CPUARMState* so we can select fp_status_a64 or fp_status_a32 accordingly. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/vec_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 011726a72d..2ba1f7cb32 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2808,7 +2808,7 @@ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) */ bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; - *statusp = env->vfp.fp_status; + *statusp = is_a64(env) ? env->vfp.fp_status_a64 : env->vfp.fp_status_a32; set_default_nan_mode(true, statusp); if (ebf) { From 1069d8ab30cb8c06cb093b06b05f571f64248d0d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:28 +0000 Subject: [PATCH 1330/2892] target/arm: Use fp_status_a32 in vjvct helper Use fp_status_a32 in the vjcvt helper function; this is called only from the A32/T32 decoder and is not used inside a set_rmode/restore_rmode sequence. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-9-peter.maydell@linaro.org --- target/arm/vfp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 7475f97e0c..0671ba3a88 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -1144,7 +1144,7 @@ uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) { - uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status); + uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status_a32); uint32_t result = pair; uint32_t z = (pair >> 32) == 0; From d1ce6db3b1dcaa51d48def07f5615e6655ec4550 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:29 +0000 Subject: [PATCH 1331/2892] target/arm: Use fp_status_a32 in vfp_cmp helpers The helpers vfp_cmps, vfp_cmpes, vfp_cmpd, vfp_cmped are used only from the A32 decoder; the A64 decoder uses separate vfp_cmps_a64 etc helpers (because for A64 we update the main NZCV flags and for A32 we update the FPSCR NZCV flags). So we can make these helpers use the fp_status_a32 field instead of fp_status. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-10-peter.maydell@linaro.org --- target/arm/vfp_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 0671ba3a88..034f26e5da 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -373,8 +373,8 @@ void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ FLOATTYPE ## _compare(a, b, &env->vfp.FPST)); \ } DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status_f16) -DO_VFP_cmp(s, float32, float32, fp_status) -DO_VFP_cmp(d, float64, float64, fp_status) +DO_VFP_cmp(s, float32, float32, fp_status_a32) +DO_VFP_cmp(d, float64, float64, fp_status_a32) #undef DO_VFP_cmp /* Integer to float and float to integer conversions */ From 961a8b3fb81ffd74218e44397fb14854b9793194 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:30 +0000 Subject: [PATCH 1332/2892] target/arm: Use FPST_A32 in A32 decoder In the A32 decoder, use FPST_A32 rather than FPST_FPCR. By doing an automated conversion of the whole file we avoid possibly using more than one fpst value in a set_rmode/op/restore_rmode sequence. Patch created with perl -p -i -e 's/FPST_FPCR(?!_)/FPST_A32/g' target/arm/tcg/translate-vfp.c Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-11-peter.maydell@linaro.org --- target/arm/tcg/translate-vfp.c | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 3cbe9a7418..c82f41234c 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -462,7 +462,7 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) if (sz == 1) { fpst = fpstatus_ptr(FPST_FPCR_F16); } else { - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); } tcg_rmode = gen_set_rmode(rounding, fpst); @@ -529,7 +529,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) if (sz == 1) { fpst = fpstatus_ptr(FPST_FPCR_F16); } else { - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); } tcg_shift = tcg_constant_i32(0); @@ -1398,7 +1398,7 @@ static bool do_vfp_3op_sp(DisasContext *s, VFPGen3OpSPFn *fn, f0 = tcg_temp_new_i32(); f1 = tcg_temp_new_i32(); fd = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vfp_load_reg32(f0, vn); vfp_load_reg32(f1, vm); @@ -1517,7 +1517,7 @@ static bool do_vfp_3op_dp(DisasContext *s, VFPGen3OpDPFn *fn, f0 = tcg_temp_new_i64(); f1 = tcg_temp_new_i64(); fd = tcg_temp_new_i64(); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vfp_load_reg64(f0, vn); vfp_load_reg64(f1, vm); @@ -2181,7 +2181,7 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) /* VFNMA, VFNMS */ gen_vfp_negs(vd, vd); } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_vfp_muladds(vd, vn, vm, vd, fpst); vfp_store_reg32(vd, a->vd); return true; @@ -2246,7 +2246,7 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) /* VFNMA, VFNMS */ gen_vfp_negd(vd, vd); } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_vfp_muladdd(vd, vn, vm, vd, fpst); vfp_store_reg64(vd, a->vd); return true; @@ -2429,12 +2429,12 @@ static void gen_VSQRT_hp(TCGv_i32 vd, TCGv_i32 vm) static void gen_VSQRT_sp(TCGv_i32 vd, TCGv_i32 vm) { - gen_helper_vfp_sqrts(vd, vm, fpstatus_ptr(FPST_FPCR)); + gen_helper_vfp_sqrts(vd, vm, fpstatus_ptr(FPST_A32)); } static void gen_VSQRT_dp(TCGv_i64 vd, TCGv_i64 vm) { - gen_helper_vfp_sqrtd(vd, vm, fpstatus_ptr(FPST_FPCR)); + gen_helper_vfp_sqrtd(vd, vm, fpstatus_ptr(FPST_A32)); } DO_VFP_2OP(VSQRT, hp, gen_VSQRT_hp, aa32_fp16_arith) @@ -2565,7 +2565,7 @@ static bool trans_VCVT_f32_f16(DisasContext *s, arg_VCVT_f32_f16 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); /* The T bit tells us if we want the low or high 16 bits of Vm */ @@ -2599,7 +2599,7 @@ static bool trans_VCVT_f64_f16(DisasContext *s, arg_VCVT_f64_f16 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); /* The T bit tells us if we want the low or high 16 bits of Vm */ @@ -2623,7 +2623,7 @@ static bool trans_VCVT_b16_f32(DisasContext *s, arg_VCVT_b16_f32 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); @@ -2646,7 +2646,7 @@ static bool trans_VCVT_f16_f32(DisasContext *s, arg_VCVT_f16_f32 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); @@ -2680,7 +2680,7 @@ static bool trans_VCVT_f16_f64(DisasContext *s, arg_VCVT_f16_f64 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); vm = tcg_temp_new_i64(); @@ -2727,7 +2727,7 @@ static bool trans_VRINTR_sp(DisasContext *s, arg_VRINTR_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rints(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2757,7 +2757,7 @@ static bool trans_VRINTR_dp(DisasContext *s, arg_VRINTR_dp *a) tmp = tcg_temp_new_i64(); vfp_load_reg64(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rintd(tmp, tmp, fpst); vfp_store_reg64(tmp, a->vd); return true; @@ -2803,7 +2803,7 @@ static bool trans_VRINTZ_sp(DisasContext *s, arg_VRINTZ_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rints(tmp, tmp, fpst); gen_restore_rmode(tcg_rmode, fpst); @@ -2836,7 +2836,7 @@ static bool trans_VRINTZ_dp(DisasContext *s, arg_VRINTZ_dp *a) tmp = tcg_temp_new_i64(); vfp_load_reg64(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rintd(tmp, tmp, fpst); gen_restore_rmode(tcg_rmode, fpst); @@ -2880,7 +2880,7 @@ static bool trans_VRINTX_sp(DisasContext *s, arg_VRINTX_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rints_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2910,7 +2910,7 @@ static bool trans_VRINTX_dp(DisasContext *s, arg_VRINTX_dp *a) tmp = tcg_temp_new_i64(); vfp_load_reg64(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rintd_exact(tmp, tmp, fpst); vfp_store_reg64(tmp, a->vd); return true; @@ -2937,7 +2937,7 @@ static bool trans_VCVT_sp(DisasContext *s, arg_VCVT_sp *a) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i64(); vfp_load_reg32(vm, a->vm); - gen_helper_vfp_fcvtds(vd, vm, fpstatus_ptr(FPST_FPCR)); + gen_helper_vfp_fcvtds(vd, vm, fpstatus_ptr(FPST_A32)); vfp_store_reg64(vd, a->vd); return true; } @@ -2963,7 +2963,7 @@ static bool trans_VCVT_dp(DisasContext *s, arg_VCVT_dp *a) vd = tcg_temp_new_i32(); vm = tcg_temp_new_i64(); vfp_load_reg64(vm, a->vm); - gen_helper_vfp_fcvtsd(vd, vm, fpstatus_ptr(FPST_FPCR)); + gen_helper_vfp_fcvtsd(vd, vm, fpstatus_ptr(FPST_A32)); vfp_store_reg32(vd, a->vd); return true; } @@ -3010,7 +3010,7 @@ static bool trans_VCVT_int_sp(DisasContext *s, arg_VCVT_int_sp *a) vm = tcg_temp_new_i32(); vfp_load_reg32(vm, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); if (a->s) { /* i32 -> f32 */ gen_helper_vfp_sitos(vm, vm, fpst); @@ -3044,7 +3044,7 @@ static bool trans_VCVT_int_dp(DisasContext *s, arg_VCVT_int_dp *a) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i64(); vfp_load_reg32(vm, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); if (a->s) { /* i32 -> f64 */ gen_helper_vfp_sitod(vd, vm, fpst); @@ -3161,7 +3161,7 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) vd = tcg_temp_new_i32(); vfp_load_reg32(vd, a->vd); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ @@ -3223,7 +3223,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) vd = tcg_temp_new_i64(); vfp_load_reg64(vd, a->vd); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ @@ -3307,7 +3307,7 @@ static bool trans_VCVT_sp_int(DisasContext *s, arg_VCVT_sp_int *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vm = tcg_temp_new_i32(); vfp_load_reg32(vm, a->vm); @@ -3347,7 +3347,7 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vm = tcg_temp_new_i64(); vd = tcg_temp_new_i32(); vfp_load_reg64(vm, a->vm); From e107a7a54e51b4f93e2fa8cff46ac5beb6cafafe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:31 +0000 Subject: [PATCH 1333/2892] target/arm: Use FPST_A64 in A64 decoder In the A64 decoder, use FPST_A64 rather than FPST_FPCR. By doing an automated conversion of the whole file we avoid possibly using more than one fpst value in a set_rmode/op/restore_rmode sequence. Patch created with perl -p -i -e 's/FPST_FPCR(?!_)/FPST_A64/g' target/arm/tcg/translate-{a64,sve,sme}.c Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-12-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 70 +++++++++++----------- target/arm/tcg/translate-sme.c | 4 +- target/arm/tcg/translate-sve.c | 106 ++++++++++++++++----------------- 3 files changed, 90 insertions(+), 90 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bd814849c1..d9c0071367 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -726,7 +726,7 @@ static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, int rm, bool is_fp16, int data, gen_helper_gvec_3_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_A64); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), fpst, @@ -768,7 +768,7 @@ static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, int rm, int ra, bool is_fp16, int data, gen_helper_gvec_4_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_A64); tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), @@ -5043,7 +5043,7 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) if (fp_access_check(s)) { TCGv_i64 t0 = read_fp_dreg(s, a->rn); TCGv_i64 t1 = read_fp_dreg(s, a->rm); - f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); write_fp_dreg(s, a->rd, t0); } break; @@ -5051,7 +5051,7 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) if (fp_access_check(s)) { TCGv_i32 t0 = read_fp_sreg(s, a->rn); TCGv_i32 t1 = read_fp_sreg(s, a->rm); - f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); write_fp_sreg(s, a->rd, t0); } break; @@ -5243,9 +5243,9 @@ static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, TCGv_i64 t0 = read_fp_dreg(s, a->rn); TCGv_i64 t1 = tcg_constant_i64(0); if (swap) { - f->gen_d(t0, t1, t0, fpstatus_ptr(FPST_FPCR)); + f->gen_d(t0, t1, t0, fpstatus_ptr(FPST_A64)); } else { - f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); } write_fp_dreg(s, a->rd, t0); } @@ -5255,9 +5255,9 @@ static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, TCGv_i32 t0 = read_fp_sreg(s, a->rn); TCGv_i32 t1 = tcg_constant_i32(0); if (swap) { - f->gen_s(t0, t1, t0, fpstatus_ptr(FPST_FPCR)); + f->gen_s(t0, t1, t0, fpstatus_ptr(FPST_A64)); } else { - f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); } write_fp_sreg(s, a->rd, t0); } @@ -6207,7 +6207,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) TCGv_i64 t1 = tcg_temp_new_i64(); read_vec_element(s, t1, a->rm, a->idx, MO_64); - f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); write_fp_dreg(s, a->rd, t0); } break; @@ -6217,7 +6217,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) TCGv_i32 t1 = tcg_temp_new_i32(); read_vec_element_i32(s, t1, a->rm, a->idx, MO_32); - f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); write_fp_sreg(s, a->rd, t0); } break; @@ -6256,7 +6256,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) if (neg) { gen_vfp_negd(t1, t1); } - gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR)); + gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); write_fp_dreg(s, a->rd, t0); } break; @@ -6270,7 +6270,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) if (neg) { gen_vfp_negs(t1, t1); } - gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR)); + gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); write_fp_sreg(s, a->rd, t0); } break; @@ -6601,7 +6601,7 @@ static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) read_vec_element(s, t0, a->rn, 0, MO_64); read_vec_element(s, t1, a->rn, 1, MO_64); - f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); write_fp_dreg(s, a->rd, t0); } break; @@ -6612,7 +6612,7 @@ static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) read_vec_element_i32(s, t0, a->rn, 0, MO_32); read_vec_element_i32(s, t1, a->rn, 1, MO_32); - f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); write_fp_sreg(s, a->rd, t0); } break; @@ -6762,7 +6762,7 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) if (neg_n) { gen_vfp_negd(tn, tn); } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_muladdd(ta, tn, tm, ta, fpst); write_fp_dreg(s, a->rd, ta); } @@ -6780,7 +6780,7 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) if (neg_n) { gen_vfp_negs(tn, tn); } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_muladds(ta, tn, tm, ta, fpst); write_fp_sreg(s, a->rd, ta); } @@ -6895,7 +6895,7 @@ static bool do_fp_reduction(DisasContext *s, arg_qrr_e *a, if (fp_access_check(s)) { MemOp esz = a->esz; int elts = (a->q ? 16 : 8) >> esz; - TCGv_ptr fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); TCGv_i32 res = do_reduction_op(s, a->rn, esz, 0, elts, fpst, fn); write_fp_sreg(s, a->rd, res); } @@ -6939,7 +6939,7 @@ static void handle_fp_compare(DisasContext *s, int size, bool cmp_with_zero, bool signal_all_nans) { TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_A64); if (size == MO_64) { TCGv_i64 tcg_vn, tcg_vm; @@ -8407,7 +8407,7 @@ static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, return check == 0; } - fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); if (rmode >= 0) { tcg_rmode = gen_set_rmode(rmode, fpst); } @@ -8513,7 +8513,7 @@ static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tcg_rn = read_fp_sreg(s, a->rn); TCGv_i64 tcg_rd = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, fpst); write_fp_dreg(s, a->rd, tcg_rd); @@ -8526,7 +8526,7 @@ static bool trans_FCVT_s_hs(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tmp = read_fp_sreg(s, a->rn); TCGv_i32 ahp = get_ahp_flag(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); /* write_fp_sreg is OK here because top half of result is zero */ @@ -8540,7 +8540,7 @@ static bool trans_FCVT_s_sd(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); TCGv_i32 tcg_rd = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, fpst); write_fp_sreg(s, a->rd, tcg_rd); @@ -8554,7 +8554,7 @@ static bool trans_FCVT_s_hd(DisasContext *s, arg_rr *a) TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); TCGv_i32 tcg_rd = tcg_temp_new_i32(); TCGv_i32 ahp = get_ahp_flag(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ @@ -8568,7 +8568,7 @@ static bool trans_FCVT_s_sh(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); TCGv_i32 tcg_rd = tcg_temp_new_i32(); - TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64); TCGv_i32 tcg_ahp = get_ahp_flag(); gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); @@ -8582,7 +8582,7 @@ static bool trans_FCVT_s_dh(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); TCGv_i64 tcg_rd = tcg_temp_new_i64(); - TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64); TCGv_i32 tcg_ahp = get_ahp_flag(); gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); @@ -8598,7 +8598,7 @@ static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, TCGv_i32 tcg_shift, tcg_single; TCGv_i64 tcg_double; - tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); tcg_shift = tcg_constant_i32(shift); switch (esz) { @@ -8693,7 +8693,7 @@ static void do_fcvt_scalar(DisasContext *s, MemOp out, MemOp esz, TCGv_ptr tcg_fpstatus; TCGv_i32 tcg_shift, tcg_rmode, tcg_single; - tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); tcg_shift = tcg_constant_i32(shift); tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); @@ -8857,7 +8857,7 @@ static bool trans_FJCVTZS(DisasContext *s, arg_FJCVTZS *a) } if (fp_access_check(s)) { TCGv_i64 t = read_fp_dreg(s, a->rn); - TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpstatus = fpstatus_ptr(FPST_A64); gen_helper_fjcvtzs(t, t, fpstatus); @@ -9115,7 +9115,7 @@ static void gen_fcvtxn_sd(TCGv_i64 d, TCGv_i64 n) * with von Neumann rounding (round to odd) */ TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_fcvtx_f64_to_f32(tmp, n, fpstatus_ptr(FPST_FPCR)); + gen_helper_fcvtx_f64_to_f32(tmp, n, fpstatus_ptr(FPST_A64)); tcg_gen_extu_i32_i64(d, tmp); } @@ -9208,7 +9208,7 @@ static void gen_fcvtn_hs(TCGv_i64 d, TCGv_i64 n) { TCGv_i32 tcg_lo = tcg_temp_new_i32(); TCGv_i32 tcg_hi = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); TCGv_i32 ahp = get_ahp_flag(); tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, n); @@ -9221,7 +9221,7 @@ static void gen_fcvtn_hs(TCGv_i64 d, TCGv_i64 n) static void gen_fcvtn_sd(TCGv_i64 d, TCGv_i64 n) { TCGv_i32 tmp = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvtsd(tmp, n, fpst); tcg_gen_extu_i32_i64(d, tmp); @@ -9237,7 +9237,7 @@ TRANS(FCVTXN_v, do_2misc_narrow_vector, a, f_scalar_fcvtxn) static void gen_bfcvtn_hs(TCGv_i64 d, TCGv_i64 n) { - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_bfcvt_pair(tmp, n, fpst); tcg_gen_extu_i32_i64(d, tmp); @@ -9312,7 +9312,7 @@ static bool do_fp1_vector(DisasContext *s, arg_qrr_e *a, return check == 0; } - fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); if (rmode >= 0) { tcg_rmode = gen_set_rmode(rmode, fpst); } @@ -9372,7 +9372,7 @@ static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, return check == 0; } - fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), fpst, is_q ? 16 : 8, vec_full_reg_size(s), @@ -9511,7 +9511,7 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A64); if (a->esz == MO_64) { /* 32 -> 64 bit fp conversion */ TCGv_i64 tcg_res[2]; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 01ece57016..fcbb350016 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -358,9 +358,9 @@ static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz, TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_fmopa_h) TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, - MO_32, FPST_FPCR, gen_helper_sme_fmopa_s) + MO_32, FPST_A64, gen_helper_sme_fmopa_s) TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, - MO_64, FPST_FPCR, gen_helper_sme_fmopa_d) + MO_64, FPST_A64, gen_helper_sme_fmopa_d) TRANS_FEAT(BFMOPA, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_bfmopa) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index e303196592..d32a67331d 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -141,7 +141,7 @@ static bool gen_gvec_fpst_arg_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, arg_rr_esz *a, int data) { return gen_gvec_fpst_zz(s, fn, a->rd, a->rn, data, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); } /* Invoke an out-of-line helper on 3 Zregs. */ @@ -191,7 +191,7 @@ static bool gen_gvec_fpst_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, arg_rrr_esz *a, int data) { return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); } /* Invoke an out-of-line helper on 4 Zregs. */ @@ -397,7 +397,7 @@ static bool gen_gvec_fpst_arg_zpzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, arg_rprr_esz *a) { return gen_gvec_fpst_zzzp(s, fn, a->rd, a->rn, a->rm, a->pg, 0, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); } /* Invoke a vector expander on two Zregs and an immediate. */ @@ -3517,7 +3517,7 @@ static bool do_FMLA_zzxz(DisasContext *s, arg_rrxr_esz *a, bool sub) }; return gen_gvec_fpst_zzzz(s, fns[a->esz], a->rd, a->rn, a->rm, a->ra, (a->index << 1) | sub, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); } TRANS_FEAT(FMLA_zzxz, aa64_sve, do_FMLA_zzxz, a, false) @@ -3533,7 +3533,7 @@ static gen_helper_gvec_3_ptr * const fmul_idx_fns[4] = { }; TRANS_FEAT(FMUL_zzx, aa64_sve, gen_gvec_fpst_zzz, fmul_idx_fns[a->esz], a->rd, a->rn, a->rm, a->index, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) /* *** SVE Floating Point Fast Reduction Group @@ -3566,7 +3566,7 @@ static bool do_reduce(DisasContext *s, arg_rpr_esz *a, tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, a->rn)); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); - status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); fn(temp, t_zn, t_pg, status, t_desc); @@ -3618,7 +3618,7 @@ static bool do_ppz_fp(DisasContext *s, arg_rpr_esz *a, if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); TCGv_ptr status = - fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); tcg_gen_gvec_3_ptr(pred_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), @@ -3654,7 +3654,7 @@ static gen_helper_gvec_3_ptr * const ftmad_fns[4] = { }; TRANS_FEAT_NONSTREAMING(FTMAD, aa64_sve, gen_gvec_fpst_zzz, ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) /* *** SVE Floating Point Accumulating Reduction Group @@ -3687,7 +3687,7 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) t_pg = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_rm, tcg_env, vec_full_reg_offset(s, a->rm)); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); - t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); t_desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fns[a->esz - 1](t_val, t_val, t_rm, t_pg, t_fpst, t_desc); @@ -3762,7 +3762,7 @@ static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, zn)); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); - status = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + status = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_A64); desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fn(t_zd, t_zn, t_pg, scalar, status, desc); } @@ -3814,7 +3814,7 @@ static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, } if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); - TCGv_ptr status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); tcg_gen_gvec_4_ptr(pred_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), vec_full_reg_offset(s, a->rm), @@ -3847,7 +3847,7 @@ static gen_helper_gvec_4_ptr * const fcadd_fns[] = { }; TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], a->rd, a->rn, a->rm, a->pg, a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) #define DO_FMLA(NAME, name) \ static gen_helper_gvec_5_ptr * const name##_fns[4] = { \ @@ -3856,7 +3856,7 @@ TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], }; \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, name##_fns[a->esz], \ a->rd, a->rn, a->rm, a->ra, a->pg, 0, \ - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) DO_FMLA(FMLA_zpzzz, fmla_zpzzz) DO_FMLA(FMLS_zpzzz, fmls_zpzzz) @@ -3871,35 +3871,35 @@ static gen_helper_gvec_5_ptr * const fcmla_fns[4] = { }; TRANS_FEAT(FCMLA_zpzzz, aa64_sve, gen_gvec_fpst_zzzzp, fcmla_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->pg, a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) static gen_helper_gvec_4_ptr * const fcmla_idx_fns[4] = { NULL, gen_helper_gvec_fcmlah_idx, gen_helper_gvec_fcmlas_idx, NULL }; TRANS_FEAT(FCMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, fcmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index * 4 + a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) /* *** SVE Floating Point Unary Operations Predicated Group */ TRANS_FEAT(FCVT_sh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_sh, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_sh, a, 0, FPST_A64) TRANS_FEAT(FCVT_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_hs, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_hs, a, 0, FPST_A64) TRANS_FEAT(BFCVT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, - gen_helper_sve_bfcvt, a, 0, FPST_FPCR) + gen_helper_sve_bfcvt, a, 0, FPST_A64) TRANS_FEAT(FCVT_dh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_dh, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_dh, a, 0, FPST_A64) TRANS_FEAT(FCVT_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_hd, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_hd, a, 0, FPST_A64) TRANS_FEAT(FCVT_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_ds, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_ds, a, 0, FPST_A64) TRANS_FEAT(FCVT_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_sd, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_hh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_hh, a, 0, FPST_FPCR_F16) @@ -3915,22 +3915,22 @@ TRANS_FEAT(FCVTZU_hd, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzu_hd, a, 0, FPST_FPCR_F16) TRANS_FEAT(FCVTZS_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_ss, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_ss, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_ss, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_ss, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_sd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_sd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_ds, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_ds, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_ds, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_ds, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_dd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_dd, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_dd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_dd, a, 0, FPST_A64) static gen_helper_gvec_3_ptr * const frint_fns[] = { NULL, @@ -3939,7 +3939,7 @@ static gen_helper_gvec_3_ptr * const frint_fns[] = { gen_helper_sve_frint_d }; TRANS_FEAT(FRINTI, aa64_sve, gen_gvec_fpst_arg_zpz, frint_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const frintx_fns[] = { NULL, @@ -3948,7 +3948,7 @@ static gen_helper_gvec_3_ptr * const frintx_fns[] = { gen_helper_sve_frintx_d }; TRANS_FEAT(FRINTX, aa64_sve, gen_gvec_fpst_arg_zpz, frintx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, ARMFPRounding mode, gen_helper_gvec_3_ptr *fn) @@ -3965,7 +3965,7 @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, } vsz = vec_full_reg_size(s); - status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); tmode = gen_set_rmode(mode, status); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), @@ -3993,14 +3993,14 @@ static gen_helper_gvec_3_ptr * const frecpx_fns[] = { gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, }; TRANS_FEAT(FRECPX, aa64_sve, gen_gvec_fpst_arg_zpz, frecpx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { NULL, gen_helper_sve_fsqrt_h, gen_helper_sve_fsqrt_s, gen_helper_sve_fsqrt_d, }; TRANS_FEAT(FSQRT, aa64_sve, gen_gvec_fpst_arg_zpz, fsqrt_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) TRANS_FEAT(SCVTF_hh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_hh, a, 0, FPST_FPCR_F16) @@ -4010,14 +4010,14 @@ TRANS_FEAT(SCVTF_dh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_dh, a, 0, FPST_FPCR_F16) TRANS_FEAT(SCVTF_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_ss, a, 0, FPST_FPCR) + gen_helper_sve_scvt_ss, a, 0, FPST_A64) TRANS_FEAT(SCVTF_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_ds, a, 0, FPST_FPCR) + gen_helper_sve_scvt_ds, a, 0, FPST_A64) TRANS_FEAT(SCVTF_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_sd, a, 0, FPST_FPCR) + gen_helper_sve_scvt_sd, a, 0, FPST_A64) TRANS_FEAT(SCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_dd, a, 0, FPST_FPCR) + gen_helper_sve_scvt_dd, a, 0, FPST_A64) TRANS_FEAT(UCVTF_hh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_hh, a, 0, FPST_FPCR_F16) @@ -4027,14 +4027,14 @@ TRANS_FEAT(UCVTF_dh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_dh, a, 0, FPST_FPCR_F16) TRANS_FEAT(UCVTF_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_ss, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_ss, a, 0, FPST_A64) TRANS_FEAT(UCVTF_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_ds, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_ds, a, 0, FPST_A64) TRANS_FEAT(UCVTF_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_sd, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_sd, a, 0, FPST_A64) TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_dd, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_dd, a, 0, FPST_A64) /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group @@ -6916,10 +6916,10 @@ DO_ZPZZ_FP(FMINP, aa64_sve2, sve2_fminp_zpzz) TRANS_FEAT_NONSTREAMING(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_s, a->rd, a->rn, a->rm, a->ra, - 0, FPST_FPCR) + 0, FPST_A64) TRANS_FEAT_NONSTREAMING(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_d, a->rd, a->rn, a->rm, a->ra, - 0, FPST_FPCR) + 0, FPST_A64) static gen_helper_gvec_4 * const sqdmlal_zzzw_fns[] = { NULL, gen_helper_sve2_sqdmlal_zzzw_h, @@ -7035,17 +7035,17 @@ TRANS_FEAT_NONSTREAMING(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz, gen_gvec_rax1, a) TRANS_FEAT(FCVTNT_sh, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtnt_sh, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtnt_sh, a, 0, FPST_A64) TRANS_FEAT(FCVTNT_ds, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtnt_ds, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtnt_ds, a, 0, FPST_A64) TRANS_FEAT(BFCVTNT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, - gen_helper_sve_bfcvtnt, a, 0, FPST_FPCR) + gen_helper_sve_bfcvtnt, a, 0, FPST_A64) TRANS_FEAT(FCVTLT_hs, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtlt_hs, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtlt_hs, a, 0, FPST_A64) TRANS_FEAT(FCVTLT_sd, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtlt_sd, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtlt_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTX_ds, aa64_sve2, do_frint_mode, a, FPROUNDING_ODD, gen_helper_sve_fcvt_ds) @@ -7057,7 +7057,7 @@ static gen_helper_gvec_3_ptr * const flogb_fns[] = { gen_helper_flogb_s, gen_helper_flogb_d }; TRANS_FEAT(FLOGB, aa64_sve2, gen_gvec_fpst_arg_zpz, flogb_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) static bool do_FMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sub, bool sel) { @@ -7101,7 +7101,7 @@ TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_env_arg_zzzz, static bool do_BFMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) { return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlal, - a->rd, a->rn, a->rm, a->ra, sel, FPST_FPCR); + a->rd, a->rn, a->rm, a->ra, sel, FPST_A64); } TRANS_FEAT(BFMLALB_zzzw, aa64_sve_bf16, do_BFMLAL_zzzw, a, false) @@ -7111,7 +7111,7 @@ static bool do_BFMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) { return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlal_idx, a->rd, a->rn, a->rm, a->ra, - (a->index << 1) | sel, FPST_FPCR); + (a->index << 1) | sel, FPST_A64); } TRANS_FEAT(BFMLALB_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, false) From 2aa9656ebc26ea73c0cdb5c67409de1b9ef303c8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:32 +0000 Subject: [PATCH 1334/2892] target/arm: Remove now-unused vfp.fp_status and FPST_FPCR Now we have moved all the uses of vfp.fp_status and FPST_FPCR to either the A32 or A64 fields, we can remove these. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-13-peter.maydell@linaro.org --- target/arm/cpu.c | 1 - target/arm/cpu.h | 2 -- target/arm/tcg/translate.h | 6 ------ target/arm/vfp_helper.c | 8 +------- 4 files changed, 1 insertion(+), 16 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 8bdd535db9..a2b9bd3fb9 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -572,7 +572,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); set_default_nan_mode(1, &env->vfp.standard_fp_status); set_default_nan_mode(1, &env->vfp.standard_fp_status_f16); - arm_set_default_fp_behaviours(&env->vfp.fp_status); arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 337c538374..7b967bbd1d 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -633,7 +633,6 @@ typedef struct CPUArchState { /* There are a number of distinct float control structures: * - * fp_status: is the "normal" fp status. * fp_status_a32: is the "normal" fp status for AArch32 insns * fp_status_a64: is the "normal" fp status for AArch64 insns * fp_status_fp16: used for half-precision calculations @@ -660,7 +659,6 @@ typedef struct CPUArchState { * only thing which needs to read the exception flags being * an explicit FPSCR read. */ - float_status fp_status; float_status fp_status_a32; float_status fp_status_a64; float_status fp_status_f16; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index c8414d94d5..fca68b7c4c 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -670,7 +670,6 @@ static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb) * Enum for argument to fpstatus_ptr(). */ typedef enum ARMFPStatusFlavour { - FPST_FPCR, FPST_A32, FPST_A64, FPST_FPCR_F16, @@ -686,8 +685,6 @@ typedef enum ARMFPStatusFlavour { * been set up to point to the requested field in the CPU state struct. * The options are: * - * FPST_FPCR - * for non-FP16 operations controlled by the FPCR * FPST_A32 * for AArch32 non-FP16 operations controlled by the FPCR * FPST_A64 @@ -705,9 +702,6 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) int offset; switch (flavour) { - case FPST_FPCR: - offset = offsetof(CPUARMState, vfp.fp_status); - break; case FPST_A32: offset = offsetof(CPUARMState, vfp.fp_status_a32); break; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 034f26e5da..9fee6265f2 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -61,9 +61,8 @@ static inline uint32_t vfp_exceptbits_from_host(int host_bits) static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { - uint32_t i; + uint32_t i = 0; - i = get_float_exception_flags(&env->vfp.fp_status); i |= get_float_exception_flags(&env->vfp.fp_status_a32); i |= get_float_exception_flags(&env->vfp.fp_status_a64); i |= get_float_exception_flags(&env->vfp.standard_fp_status); @@ -82,7 +81,6 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) * values. The caller should have arranged for env->vfp.fpsr to * be the architecturally up-to-date exception flag information first. */ - set_float_exception_flags(0, &env->vfp.fp_status); set_float_exception_flags(0, &env->vfp.fp_status_a32); set_float_exception_flags(0, &env->vfp.fp_status_a64); set_float_exception_flags(0, &env->vfp.fp_status_f16); @@ -112,7 +110,6 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) i = float_round_to_zero; break; } - set_float_rounding_mode(i, &env->vfp.fp_status); set_float_rounding_mode(i, &env->vfp.fp_status_a32); set_float_rounding_mode(i, &env->vfp.fp_status_a64); set_float_rounding_mode(i, &env->vfp.fp_status_f16); @@ -126,8 +123,6 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a32); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a32); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a64); @@ -135,7 +130,6 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } if (changed & FPCR_DN) { bool dnan_enabled = val & FPCR_DN; - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); From 5f4ed6da85ff4abeb89bf9e6ad9481b82c5db0b0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:33 +0000 Subject: [PATCH 1335/2892] target/arm: Define new fp_status_f16_a32 and fp_status_f16_a64 As the first part of splitting the existing fp_status_f16 into separate float_status fields for AArch32 and AArch64 (so that we can make FEAT_AFP control bits apply only for AArch64), define the two new fp_status_f16_a32 and fp_status_f16_a64 fields, but don't use them yet. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-14-peter.maydell@linaro.org --- target/arm/cpu.c | 2 ++ target/arm/cpu.h | 4 ++++ target/arm/tcg/translate.h | 12 ++++++++++++ target/arm/vfp_helper.c | 14 ++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a2b9bd3fb9..ff8514edc6 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -576,6 +576,8 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16); + arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); + arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16); #ifndef CONFIG_USER_ONLY diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 7b967bbd1d..be409c5c76 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -636,6 +636,8 @@ typedef struct CPUArchState { * fp_status_a32: is the "normal" fp status for AArch32 insns * fp_status_a64: is the "normal" fp status for AArch64 insns * fp_status_fp16: used for half-precision calculations + * fp_status_fp16_a32: used for AArch32 half-precision calculations + * fp_status_fp16_a64: used for AArch64 half-precision calculations * standard_fp_status : the ARM "Standard FPSCR Value" * standard_fp_status_fp16 : used for half-precision * calculations with the ARM "Standard FPSCR Value" @@ -662,6 +664,8 @@ typedef struct CPUArchState { float_status fp_status_a32; float_status fp_status_a64; float_status fp_status_f16; + float_status fp_status_f16_a32; + float_status fp_status_f16_a64; float_status standard_fp_status; float_status standard_fp_status_f16; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index fca68b7c4c..d84c6d74aa 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -673,6 +673,8 @@ typedef enum ARMFPStatusFlavour { FPST_A32, FPST_A64, FPST_FPCR_F16, + FPST_A32_F16, + FPST_A64_F16, FPST_STD, FPST_STD_F16, } ARMFPStatusFlavour; @@ -691,6 +693,10 @@ typedef enum ARMFPStatusFlavour { * for AArch64 non-FP16 operations controlled by the FPCR * FPST_FPCR_F16 * for operations controlled by the FPCR where FPCR.FZ16 is to be used + * FPST_A32_F16 + * for AArch32 operations controlled by the FPCR where FPCR.FZ16 is to be used + * FPST_A64_F16 + * for AArch64 operations controlled by the FPCR where FPCR.FZ16 is to be used * FPST_STD * for A32/T32 Neon operations using the "standard FPSCR value" * FPST_STD_F16 @@ -711,6 +717,12 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) case FPST_FPCR_F16: offset = offsetof(CPUARMState, vfp.fp_status_f16); break; + case FPST_A32_F16: + offset = offsetof(CPUARMState, vfp.fp_status_f16_a32); + break; + case FPST_A64_F16: + offset = offsetof(CPUARMState, vfp.fp_status_f16_a64); + break; case FPST_STD: offset = offsetof(CPUARMState, vfp.standard_fp_status); break; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 9fee6265f2..45f9dfc886 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -69,6 +69,10 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) /* FZ16 does not generate an input denormal exception. */ i |= (get_float_exception_flags(&env->vfp.fp_status_f16) & ~float_flag_input_denormal); + i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) + & ~float_flag_input_denormal); + i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) + & ~float_flag_input_denormal); i |= (get_float_exception_flags(&env->vfp.standard_fp_status_f16) & ~float_flag_input_denormal); return vfp_exceptbits_from_host(i); @@ -84,6 +88,8 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status_a32); set_float_exception_flags(0, &env->vfp.fp_status_a64); set_float_exception_flags(0, &env->vfp.fp_status_f16); + set_float_exception_flags(0, &env->vfp.fp_status_f16_a32); + set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); set_float_exception_flags(0, &env->vfp.standard_fp_status); set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); } @@ -113,12 +119,18 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_float_rounding_mode(i, &env->vfp.fp_status_a32); set_float_rounding_mode(i, &env->vfp.fp_status_a64); set_float_rounding_mode(i, &env->vfp.fp_status_f16); + set_float_rounding_mode(i, &env->vfp.fp_status_f16_a32); + set_float_rounding_mode(i, &env->vfp.fp_status_f16_a64); } if (changed & FPCR_FZ16) { bool ftz_enabled = val & FPCR_FZ16; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); } if (changed & FPCR_FZ) { @@ -133,6 +145,8 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); } } From 85fffc1085c0b2623aa615cfdfb6b4d84694cfa3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:34 +0000 Subject: [PATCH 1336/2892] target/arm: Use fp_status_f16_a32 in AArch32-only helpers We directly use fp_status_f16 in a handful of helpers that are AArch32-specific; switch to fp_status_f16_a32 for these. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-15-peter.maydell@linaro.org --- target/arm/tcg/vec_helper.c | 4 ++-- target/arm/vfp_helper.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 2ba1f7cb32..1b1deda942 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2060,7 +2060,7 @@ void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); + get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, @@ -2122,7 +2122,7 @@ void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); + get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 45f9dfc886..f3aa80bbfb 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -380,7 +380,7 @@ void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ softfloat_to_vfp_compare(env, \ FLOATTYPE ## _compare(a, b, &env->vfp.FPST)); \ } -DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status_f16) +DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status_f16_a32) DO_VFP_cmp(s, float32, float32, fp_status_a32) DO_VFP_cmp(d, float64, float64, fp_status_a32) #undef DO_VFP_cmp From e4b3c388f96c1e4d18e276db6a9963bcb8cb98fb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:35 +0000 Subject: [PATCH 1337/2892] target/arm: Use fp_status_f16_a64 in AArch64-only helpers We directly use fp_status_f16 in a handful of helpers that are AArch64-specific; switch to fp_status_f16_a64 for these. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-16-peter.maydell@linaro.org --- target/arm/tcg/sme_helper.c | 4 ++-- target/arm/tcg/vec_helper.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 2aad00d3ad..727c085f37 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1038,12 +1038,12 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, float_status fpst_odd, fpst_std, fpst_f16; /* - * Make copies of fp_status and fp_status_f16, because this operation + * Make copies of the fp status fields we use, because this operation * does not update the cumulative fp exception status. It also * produces default NaNs. We also need a second copy of fp_status with * round-to-odd -- see above. */ - fpst_f16 = env->vfp.fp_status_f16; + fpst_f16 = env->vfp.fp_status_f16_a64; fpst_std = env->vfp.fp_status_a64; set_default_nan_mode(true, &fpst_std); set_default_nan_mode(true, &fpst_f16); diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 1b1deda942..7330b373c3 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2067,7 +2067,7 @@ void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { do_fmlal(vd, vn, vm, &env->vfp.fp_status_a64, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); + get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64)); } void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, @@ -2077,7 +2077,7 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); float_status *status = &env->vfp.fp_status_a64; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); + bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64); for (i = 0; i < oprsz; i += sizeof(float32)) { float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negn; @@ -2129,7 +2129,7 @@ void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status_a64, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); + get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64)); } void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, @@ -2140,7 +2140,7 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); float_status *status = &env->vfp.fp_status_a64; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); + bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64); for (i = 0; i < oprsz; i += 16) { float16 mm_16 = *(float16 *)(vm + i + idx); From e935710bc8c76c1a7a665da10fa2d5e97ea94ee1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:36 +0000 Subject: [PATCH 1338/2892] target/arm: Use FPST_A32_F16 in A32 decoder In the A32 decoder, use FPST_A32_F16 rather than FPST_FPCR_F16. By doing an automated conversion of the whole file we avoid possibly using more than one fpst value in a set_rmode/op/restore_rmode sequence. Patch created with perl -p -i -e 's/FPST_FPCR_F16(?!_)/FPST_A32_F16/g' target/arm/tcg/translate-vfp.c Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-17-peter.maydell@linaro.org --- target/arm/tcg/translate-vfp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index c82f41234c..8d9d1ab877 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -460,7 +460,7 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) } if (sz == 1) { - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); } else { fpst = fpstatus_ptr(FPST_A32); } @@ -527,7 +527,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } if (sz == 1) { - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); } else { fpst = fpstatus_ptr(FPST_A32); } @@ -1433,7 +1433,7 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, /* * Do a half-precision operation. Functionally this is * the same as do_vfp_3op_sp(), except: - * - it uses the FPST_FPCR_F16 + * - it uses the FPST_A32_F16 * - it doesn't need the VFP vector handling (fp16 is a * v8 feature, and in v8 VFP vectors don't exist) * - it does the aa32_fp16_arith feature test @@ -1456,7 +1456,7 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, f0 = tcg_temp_new_i32(); f1 = tcg_temp_new_i32(); fd = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); vfp_load_reg16(f0, vn); vfp_load_reg16(f1, vm); @@ -2122,7 +2122,7 @@ static bool do_vfm_hp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) /* VFNMA, VFNMS */ gen_vfp_negh(vd, vd); } - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); gen_helper_vfp_muladdh(vd, vn, vm, vd, fpst); vfp_store_reg32(vd, a->vd); return true; @@ -2424,7 +2424,7 @@ DO_VFP_2OP(VNEG, dp, gen_vfp_negd, aa32_fpdp_v2) static void gen_VSQRT_hp(TCGv_i32 vd, TCGv_i32 vm) { - gen_helper_vfp_sqrth(vd, vm, fpstatus_ptr(FPST_FPCR_F16)); + gen_helper_vfp_sqrth(vd, vm, fpstatus_ptr(FPST_A32_F16)); } static void gen_VSQRT_sp(TCGv_i32 vd, TCGv_i32 vm) @@ -2706,7 +2706,7 @@ static bool trans_VRINTR_hp(DisasContext *s, arg_VRINTR_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg16(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); gen_helper_rinth(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2779,7 +2779,7 @@ static bool trans_VRINTZ_hp(DisasContext *s, arg_VRINTZ_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg16(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rinth(tmp, tmp, fpst); gen_restore_rmode(tcg_rmode, fpst); @@ -2859,7 +2859,7 @@ static bool trans_VRINTX_hp(DisasContext *s, arg_VRINTX_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg16(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); gen_helper_rinth_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2983,7 +2983,7 @@ static bool trans_VCVT_int_hp(DisasContext *s, arg_VCVT_int_sp *a) vm = tcg_temp_new_i32(); vfp_load_reg32(vm, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); if (a->s) { /* i32 -> f16 */ gen_helper_vfp_sitoh(vm, vm, fpst); @@ -3105,7 +3105,7 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) vd = tcg_temp_new_i32(); vfp_load_reg32(vd, a->vd); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ @@ -3273,7 +3273,7 @@ static bool trans_VCVT_hp_int(DisasContext *s, arg_VCVT_sp_int *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); vm = tcg_temp_new_i32(); vfp_load_reg16(vm, a->vm); From 230c2bd3f2882e007abc80d513b86770adb7b0e5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:37 +0000 Subject: [PATCH 1339/2892] target/arm: Use FPST_A64_F16 in A64 decoder In the A32 decoder, use FPST_A64_F16 rather than FPST_FPCR_F16. By doing an automated conversion of the whole file we avoid possibly using more than one fpst value in a set_rmode/op/restore_rmode sequence. Patch created with perl -p -i -e 's/FPST_FPCR_F16(?!_)/FPST_A64_F16/g' target/arm/tcg/translate-{a64,sve,sme}.c Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-18-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 32 ++++++++--------- target/arm/tcg/translate-sve.c | 66 +++++++++++++++++----------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d9c0071367..2b8b253479 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -726,7 +726,7 @@ static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, int rm, bool is_fp16, int data, gen_helper_gvec_3_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_A64); + TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), fpst, @@ -768,7 +768,7 @@ static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, int rm, int ra, bool is_fp16, int data, gen_helper_gvec_4_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_A64); + TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), @@ -5062,7 +5062,7 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) if (fp_access_check(s)) { TCGv_i32 t0 = read_fp_hreg(s, a->rn); TCGv_i32 t1 = read_fp_hreg(s, a->rm); - f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); write_fp_sreg(s, a->rd, t0); } break; @@ -5270,9 +5270,9 @@ static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, TCGv_i32 t0 = read_fp_hreg(s, a->rn); TCGv_i32 t1 = tcg_constant_i32(0); if (swap) { - f->gen_h(t0, t1, t0, fpstatus_ptr(FPST_FPCR_F16)); + f->gen_h(t0, t1, t0, fpstatus_ptr(FPST_A64_F16)); } else { - f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); } write_fp_sreg(s, a->rd, t0); } @@ -6230,7 +6230,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) TCGv_i32 t1 = tcg_temp_new_i32(); read_vec_element_i32(s, t1, a->rm, a->idx, MO_16); - f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); write_fp_sreg(s, a->rd, t0); } break; @@ -6288,7 +6288,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) gen_vfp_negh(t1, t1); } gen_helper_advsimd_muladdh(t0, t1, t2, t0, - fpstatus_ptr(FPST_FPCR_F16)); + fpstatus_ptr(FPST_A64_F16)); write_fp_sreg(s, a->rd, t0); } break; @@ -6626,7 +6626,7 @@ static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) read_vec_element_i32(s, t0, a->rn, 0, MO_16); read_vec_element_i32(s, t1, a->rn, 1, MO_16); - f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); write_fp_sreg(s, a->rd, t0); } break; @@ -6801,7 +6801,7 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) if (neg_n) { gen_vfp_negh(tn, tn); } - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A64_F16); gen_helper_advsimd_muladdh(ta, tn, tm, ta, fpst); write_fp_sreg(s, a->rd, ta); } @@ -6895,7 +6895,7 @@ static bool do_fp_reduction(DisasContext *s, arg_qrr_e *a, if (fp_access_check(s)) { MemOp esz = a->esz; int elts = (a->q ? 16 : 8) >> esz; - TCGv_ptr fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + TCGv_ptr fpst = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); TCGv_i32 res = do_reduction_op(s, a->rn, esz, 0, elts, fpst, fn); write_fp_sreg(s, a->rd, res); } @@ -6939,7 +6939,7 @@ static void handle_fp_compare(DisasContext *s, int size, bool cmp_with_zero, bool signal_all_nans) { TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_A64); + TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_A64_F16 : FPST_A64); if (size == MO_64) { TCGv_i64 tcg_vn, tcg_vm; @@ -8407,7 +8407,7 @@ static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, return check == 0; } - fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); if (rmode >= 0) { tcg_rmode = gen_set_rmode(rmode, fpst); } @@ -8598,7 +8598,7 @@ static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, TCGv_i32 tcg_shift, tcg_single; TCGv_i64 tcg_double; - tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_shift = tcg_constant_i32(shift); switch (esz) { @@ -8693,7 +8693,7 @@ static void do_fcvt_scalar(DisasContext *s, MemOp out, MemOp esz, TCGv_ptr tcg_fpstatus; TCGv_i32 tcg_shift, tcg_rmode, tcg_single; - tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_shift = tcg_constant_i32(shift); tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); @@ -9312,7 +9312,7 @@ static bool do_fp1_vector(DisasContext *s, arg_qrr_e *a, return check == 0; } - fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); if (rmode >= 0) { tcg_rmode = gen_set_rmode(rmode, fpst); } @@ -9372,7 +9372,7 @@ static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, return check == 0; } - fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + fpst = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), fpst, is_q ? 16 : 8, vec_full_reg_size(s), diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index d32a67331d..a7dbea5acd 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -141,7 +141,7 @@ static bool gen_gvec_fpst_arg_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, arg_rr_esz *a, int data) { return gen_gvec_fpst_zz(s, fn, a->rd, a->rn, data, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } /* Invoke an out-of-line helper on 3 Zregs. */ @@ -191,7 +191,7 @@ static bool gen_gvec_fpst_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, arg_rrr_esz *a, int data) { return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } /* Invoke an out-of-line helper on 4 Zregs. */ @@ -397,7 +397,7 @@ static bool gen_gvec_fpst_arg_zpzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, arg_rprr_esz *a) { return gen_gvec_fpst_zzzp(s, fn, a->rd, a->rn, a->rm, a->pg, 0, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } /* Invoke a vector expander on two Zregs and an immediate. */ @@ -3517,7 +3517,7 @@ static bool do_FMLA_zzxz(DisasContext *s, arg_rrxr_esz *a, bool sub) }; return gen_gvec_fpst_zzzz(s, fns[a->esz], a->rd, a->rn, a->rm, a->ra, (a->index << 1) | sub, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } TRANS_FEAT(FMLA_zzxz, aa64_sve, do_FMLA_zzxz, a, false) @@ -3533,7 +3533,7 @@ static gen_helper_gvec_3_ptr * const fmul_idx_fns[4] = { }; TRANS_FEAT(FMUL_zzx, aa64_sve, gen_gvec_fpst_zzz, fmul_idx_fns[a->esz], a->rd, a->rn, a->rm, a->index, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Fast Reduction Group @@ -3566,7 +3566,7 @@ static bool do_reduce(DisasContext *s, arg_rpr_esz *a, tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, a->rn)); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); - status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + status = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); fn(temp, t_zn, t_pg, status, t_desc); @@ -3618,7 +3618,7 @@ static bool do_ppz_fp(DisasContext *s, arg_rpr_esz *a, if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); TCGv_ptr status = - fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_3_ptr(pred_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), @@ -3654,7 +3654,7 @@ static gen_helper_gvec_3_ptr * const ftmad_fns[4] = { }; TRANS_FEAT_NONSTREAMING(FTMAD, aa64_sve, gen_gvec_fpst_zzz, ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Accumulating Reduction Group @@ -3687,7 +3687,7 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) t_pg = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_rm, tcg_env, vec_full_reg_offset(s, a->rm)); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); - t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); t_desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fns[a->esz - 1](t_val, t_val, t_rm, t_pg, t_fpst, t_desc); @@ -3762,7 +3762,7 @@ static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, zn)); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); - status = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_A64); + status = fpstatus_ptr(is_fp16 ? FPST_A64_F16 : FPST_A64); desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fn(t_zd, t_zn, t_pg, scalar, status, desc); } @@ -3814,7 +3814,7 @@ static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, } if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); - TCGv_ptr status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + TCGv_ptr status = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_4_ptr(pred_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), vec_full_reg_offset(s, a->rm), @@ -3847,7 +3847,7 @@ static gen_helper_gvec_4_ptr * const fcadd_fns[] = { }; TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], a->rd, a->rn, a->rm, a->pg, a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) #define DO_FMLA(NAME, name) \ static gen_helper_gvec_5_ptr * const name##_fns[4] = { \ @@ -3856,7 +3856,7 @@ TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], }; \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, name##_fns[a->esz], \ a->rd, a->rn, a->rm, a->ra, a->pg, 0, \ - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) DO_FMLA(FMLA_zpzzz, fmla_zpzzz) DO_FMLA(FMLS_zpzzz, fmls_zpzzz) @@ -3871,14 +3871,14 @@ static gen_helper_gvec_5_ptr * const fcmla_fns[4] = { }; TRANS_FEAT(FCMLA_zpzzz, aa64_sve, gen_gvec_fpst_zzzzp, fcmla_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->pg, a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_4_ptr * const fcmla_idx_fns[4] = { NULL, gen_helper_gvec_fcmlah_idx, gen_helper_gvec_fcmlas_idx, NULL }; TRANS_FEAT(FCMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, fcmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index * 4 + a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Unary Operations Predicated Group @@ -3902,17 +3902,17 @@ TRANS_FEAT(FCVT_sd, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzs_hh, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZU_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzu_hh, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZS_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_hs, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzs_hs, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZU_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_hs, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzu_hs, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZS_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_hd, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzs_hd, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZU_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_hd, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzu_hd, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZS_ss, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvtzs_ss, a, 0, FPST_A64) @@ -3939,7 +3939,7 @@ static gen_helper_gvec_3_ptr * const frint_fns[] = { gen_helper_sve_frint_d }; TRANS_FEAT(FRINTI, aa64_sve, gen_gvec_fpst_arg_zpz, frint_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const frintx_fns[] = { NULL, @@ -3948,7 +3948,7 @@ static gen_helper_gvec_3_ptr * const frintx_fns[] = { gen_helper_sve_frintx_d }; TRANS_FEAT(FRINTX, aa64_sve, gen_gvec_fpst_arg_zpz, frintx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, ARMFPRounding mode, gen_helper_gvec_3_ptr *fn) @@ -3965,7 +3965,7 @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, } vsz = vec_full_reg_size(s); - status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64); + status = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); tmode = gen_set_rmode(mode, status); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), @@ -3993,21 +3993,21 @@ static gen_helper_gvec_3_ptr * const frecpx_fns[] = { gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, }; TRANS_FEAT(FRECPX, aa64_sve, gen_gvec_fpst_arg_zpz, frecpx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { NULL, gen_helper_sve_fsqrt_h, gen_helper_sve_fsqrt_s, gen_helper_sve_fsqrt_d, }; TRANS_FEAT(FSQRT, aa64_sve, gen_gvec_fpst_arg_zpz, fsqrt_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) TRANS_FEAT(SCVTF_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_scvt_hh, a, 0, FPST_A64_F16) TRANS_FEAT(SCVTF_sh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_sh, a, 0, FPST_FPCR_F16) + gen_helper_sve_scvt_sh, a, 0, FPST_A64_F16) TRANS_FEAT(SCVTF_dh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_dh, a, 0, FPST_FPCR_F16) + gen_helper_sve_scvt_dh, a, 0, FPST_A64_F16) TRANS_FEAT(SCVTF_ss, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_ss, a, 0, FPST_A64) @@ -4020,11 +4020,11 @@ TRANS_FEAT(SCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_scvt_dd, a, 0, FPST_A64) TRANS_FEAT(UCVTF_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_ucvt_hh, a, 0, FPST_A64_F16) TRANS_FEAT(UCVTF_sh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_sh, a, 0, FPST_FPCR_F16) + gen_helper_sve_ucvt_sh, a, 0, FPST_A64_F16) TRANS_FEAT(UCVTF_dh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_dh, a, 0, FPST_FPCR_F16) + gen_helper_sve_ucvt_dh, a, 0, FPST_A64_F16) TRANS_FEAT(UCVTF_ss, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_ucvt_ss, a, 0, FPST_A64) @@ -7057,7 +7057,7 @@ static gen_helper_gvec_3_ptr * const flogb_fns[] = { gen_helper_flogb_s, gen_helper_flogb_d }; TRANS_FEAT(FLOGB, aa64_sve2, gen_gvec_fpst_arg_zpz, flogb_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_A64) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static bool do_FMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sub, bool sel) { From 3847b5b1fbb2d6d4ab1dffd8842ffc3d0c3ddb37 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:38 +0000 Subject: [PATCH 1340/2892] target/arm: Remove now-unused vfp.fp_status_f16 and FPST_FPCR_F16 Now we have moved all the uses of vfp.fp_status_f16 and FPST_FPCR_F16 to the new A32 or A64 fields, we can remove these. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-19-peter.maydell@linaro.org --- target/arm/cpu.c | 1 - target/arm/cpu.h | 2 -- target/arm/tcg/translate.h | 6 ------ target/arm/vfp_helper.c | 7 ------- 4 files changed, 16 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ff8514edc6..7a83b9ee34 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -575,7 +575,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); - arm_set_default_fp_behaviours(&env->vfp.fp_status_f16); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index be409c5c76..2213c27734 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -635,7 +635,6 @@ typedef struct CPUArchState { * * fp_status_a32: is the "normal" fp status for AArch32 insns * fp_status_a64: is the "normal" fp status for AArch64 insns - * fp_status_fp16: used for half-precision calculations * fp_status_fp16_a32: used for AArch32 half-precision calculations * fp_status_fp16_a64: used for AArch64 half-precision calculations * standard_fp_status : the ARM "Standard FPSCR Value" @@ -663,7 +662,6 @@ typedef struct CPUArchState { */ float_status fp_status_a32; float_status fp_status_a64; - float_status fp_status_f16; float_status fp_status_f16_a32; float_status fp_status_f16_a64; float_status standard_fp_status; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index d84c6d74aa..084ee63d99 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -672,7 +672,6 @@ static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb) typedef enum ARMFPStatusFlavour { FPST_A32, FPST_A64, - FPST_FPCR_F16, FPST_A32_F16, FPST_A64_F16, FPST_STD, @@ -691,8 +690,6 @@ typedef enum ARMFPStatusFlavour { * for AArch32 non-FP16 operations controlled by the FPCR * FPST_A64 * for AArch64 non-FP16 operations controlled by the FPCR - * FPST_FPCR_F16 - * for operations controlled by the FPCR where FPCR.FZ16 is to be used * FPST_A32_F16 * for AArch32 operations controlled by the FPCR where FPCR.FZ16 is to be used * FPST_A64_F16 @@ -714,9 +711,6 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) case FPST_A64: offset = offsetof(CPUARMState, vfp.fp_status_a64); break; - case FPST_FPCR_F16: - offset = offsetof(CPUARMState, vfp.fp_status_f16); - break; case FPST_A32_F16: offset = offsetof(CPUARMState, vfp.fp_status_f16_a32); break; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index f3aa80bbfb..3ed69d7369 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -67,8 +67,6 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) i |= get_float_exception_flags(&env->vfp.fp_status_a64); i |= get_float_exception_flags(&env->vfp.standard_fp_status); /* FZ16 does not generate an input denormal exception. */ - i |= (get_float_exception_flags(&env->vfp.fp_status_f16) - & ~float_flag_input_denormal); i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) & ~float_flag_input_denormal); i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) @@ -87,7 +85,6 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) */ set_float_exception_flags(0, &env->vfp.fp_status_a32); set_float_exception_flags(0, &env->vfp.fp_status_a64); - set_float_exception_flags(0, &env->vfp.fp_status_f16); set_float_exception_flags(0, &env->vfp.fp_status_f16_a32); set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); set_float_exception_flags(0, &env->vfp.standard_fp_status); @@ -118,17 +115,14 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } set_float_rounding_mode(i, &env->vfp.fp_status_a32); set_float_rounding_mode(i, &env->vfp.fp_status_a64); - set_float_rounding_mode(i, &env->vfp.fp_status_f16); set_float_rounding_mode(i, &env->vfp.fp_status_f16_a32); set_float_rounding_mode(i, &env->vfp.fp_status_f16_a64); } if (changed & FPCR_FZ16) { bool ftz_enabled = val & FPCR_FZ16; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); @@ -144,7 +138,6 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) bool dnan_enabled = val & FPCR_DN; set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); } From 584b7aec817ea2c2d24cbc92588363caee8667ab Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:39 +0000 Subject: [PATCH 1341/2892] fpu: Rename float_flag_input_denormal to float_flag_input_denormal_flushed Our float_flag_input_denormal exception flag is set when the fpu code flushes an input denormal to zero. This is what many guest architectures (eg classic Arm behaviour) require, but it is not the only donarmal-related reason we might want to set an exception flag. The x86 behaviour (which we do not currently model correctly) wants to see an exception flag when a denormal input is *not* flushed to zero and is actually used in an arithmetic operation. Arm's FEAT_AFP also wants these semantics. Rename float_flag_input_denormal to float_flag_input_denormal_flushed to make it clearer when it is set and to allow us to add a new float_flag_input_denormal_used next to it for the x86/FEAT_AFP semantics. Commit created with for f in `git grep -l float_flag_input_denormal`; do sed -i -e 's/float_flag_input_denormal/float_flag_input_denormal_flushed/' $f; done and manual editing of softfloat-types.h and softfloat.c to clean up the indentation afterwards and to fix a comment which wasn't using the full name of the flag. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-20-peter.maydell@linaro.org --- fpu/softfloat-parts.c.inc | 2 +- fpu/softfloat.c | 4 ++-- include/fpu/softfloat-types.h | 5 +++-- target/arm/tcg/sve_helper.c | 6 +++--- target/arm/vfp_helper.c | 10 +++++----- target/i386/tcg/fpu_helper.c | 6 +++--- target/mips/tcg/msa_helper.c | 2 +- target/rx/op_helper.c | 2 +- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index ebde42992f..b3d693eed0 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -198,7 +198,7 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status, if (likely(frac_eqz(p))) { p->cls = float_class_zero; } else if (status->flush_inputs_to_zero) { - float_raise(float_flag_input_denormal, status); + float_raise(float_flag_input_denormal_flushed, status); p->cls = float_class_zero; frac_clear(p); } else { diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 8d75d66817..648050be6f 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -132,7 +132,7 @@ this code that are retained. if (unlikely(soft_t ## _is_denormal(*a))) { \ *a = soft_t ## _set_sign(soft_t ## _zero, \ soft_t ## _is_neg(*a)); \ - float_raise(float_flag_input_denormal, s); \ + float_raise(float_flag_input_denormal_flushed, s); \ } \ } @@ -4848,7 +4848,7 @@ float128 float128_silence_nan(float128 a, float_status *status) static bool parts_squash_denormal(FloatParts64 p, float_status *status) { if (p.exp == 0 && p.frac != 0) { - float_raise(float_flag_input_denormal, status); + float_raise(float_flag_input_denormal_flushed, status); return true; } diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 9d37cdfaa8..24cd290a1d 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -154,7 +154,8 @@ enum { float_flag_overflow = 0x0004, float_flag_underflow = 0x0008, float_flag_inexact = 0x0010, - float_flag_input_denormal = 0x0020, + /* We flushed an input denormal to 0 (because of flush_inputs_to_zero) */ + float_flag_input_denormal_flushed = 0x0020, float_flag_output_denormal = 0x0040, float_flag_invalid_isi = 0x0080, /* inf - inf */ float_flag_invalid_imz = 0x0100, /* inf * 0 */ @@ -302,7 +303,7 @@ typedef struct float_status { bool tininess_before_rounding; /* should denormalised results go to zero and set the inexact flag? */ bool flush_to_zero; - /* should denormalised inputs go to zero and set the input_denormal flag? */ + /* should denormalised inputs go to zero and set input_denormal_flushed? */ bool flush_inputs_to_zero; bool default_nan_mode; /* diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index d0865dece3..9837c5bc7a 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4658,7 +4658,7 @@ static int16_t do_float16_logb_as_int(float16 a, float_status *s) return -15 - clz32(frac); } /* flush to zero */ - float_raise(float_flag_input_denormal, s); + float_raise(float_flag_input_denormal_flushed, s); } } else if (unlikely(exp == 0x1f)) { if (frac == 0) { @@ -4686,7 +4686,7 @@ static int32_t do_float32_logb_as_int(float32 a, float_status *s) return -127 - clz32(frac); } /* flush to zero */ - float_raise(float_flag_input_denormal, s); + float_raise(float_flag_input_denormal_flushed, s); } } else if (unlikely(exp == 0xff)) { if (frac == 0) { @@ -4714,7 +4714,7 @@ static int64_t do_float64_logb_as_int(float64 a, float_status *s) return -1023 - clz64(frac); } /* flush to zero */ - float_raise(float_flag_input_denormal, s); + float_raise(float_flag_input_denormal_flushed, s); } } else if (unlikely(exp == 0x7ff)) { if (frac == 0) { diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 3ed69d7369..444702a460 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -53,7 +53,7 @@ static inline uint32_t vfp_exceptbits_from_host(int host_bits) if (host_bits & float_flag_inexact) { target_bits |= FPSR_IXC; } - if (host_bits & float_flag_input_denormal) { + if (host_bits & float_flag_input_denormal_flushed) { target_bits |= FPSR_IDC; } return target_bits; @@ -68,11 +68,11 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) i |= get_float_exception_flags(&env->vfp.standard_fp_status); /* FZ16 does not generate an input denormal exception. */ i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) - & ~float_flag_input_denormal); + & ~float_flag_input_denormal_flushed); i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) - & ~float_flag_input_denormal); + & ~float_flag_input_denormal_flushed); i |= (get_float_exception_flags(&env->vfp.standard_fp_status_f16) - & ~float_flag_input_denormal); + & ~float_flag_input_denormal_flushed); return vfp_exceptbits_from_host(i); } @@ -1133,7 +1133,7 @@ uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) /* Normal inexact, denormal with flush-to-zero, or overflow or NaN */ inexact = e_new & (float_flag_inexact | - float_flag_input_denormal | + float_flag_input_denormal_flushed | float_flag_invalid); /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index d0a1e2f3c8..9c33ac7898 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -204,7 +204,7 @@ static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) (new_flags & float_flag_overflow ? FPUS_OE : 0) | (new_flags & float_flag_underflow ? FPUS_UE : 0) | (new_flags & float_flag_inexact ? FPUS_PE : 0) | - (new_flags & float_flag_input_denormal ? FPUS_DE : 0))); + (new_flags & float_flag_input_denormal_flushed ? FPUS_DE : 0))); } static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) @@ -1829,7 +1829,7 @@ void helper_fxtract(CPUX86State *env) int shift = clz64(temp.l.lower); temp.l.lower <<= shift; expdif = 1 - EXPBIAS - shift; - float_raise(float_flag_input_denormal, &env->fp_status); + float_raise(float_flag_input_denormal_flushed, &env->fp_status); } else { expdif = EXPD(temp) - EXPBIAS; } @@ -3258,7 +3258,7 @@ void update_mxcsr_from_sse_status(CPUX86State *env) uint8_t flags = get_float_exception_flags(&env->sse_status); /* * The MXCSR denormal flag has opposite semantics to - * float_flag_input_denormal (the softfloat code sets that flag + * float_flag_input_denormal_flushed (the softfloat code sets that flag * only when flushing input denormals to zero, but SSE sets it * only when not flushing them to zero), so is not converted * here. diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 1d40383ca4..aeab6a1d8b 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -6231,7 +6231,7 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; /* Set Inexact (I) when flushing inputs to zero */ - if ((ieee_exception_flags & float_flag_input_denormal) && + if ((ieee_exception_flags & float_flag_input_denormal_flushed) && (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { if (action & CLEAR_IS_INEXACT) { mips_exception_flags &= ~FP_INEXACT; diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index 691a12b2be..59dd1ae612 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -99,7 +99,7 @@ static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr) if (xcpt & float_flag_inexact) { SET_FPSW(X); } - if ((xcpt & (float_flag_input_denormal + if ((xcpt & (float_flag_input_denormal_flushed | float_flag_output_denormal)) && !FIELD_EX32(env->fpsw, FPSW, DN)) { env->fpsw = FIELD_DP32(env->fpsw, FPSW, CE, 1); From 7af64d103d2d81e4b1a9a7c92a803abb2bcd6d91 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:40 +0000 Subject: [PATCH 1342/2892] fpu: Rename float_flag_output_denormal to float_flag_output_denormal_flushed Our float_flag_output_denormal exception flag is set when the fpu code flushes an output denormal to zero. Rename it to float_flag_output_denormal_flushed: * this keeps it parallel with the flag for flushing input denormals, which we just renamed * it makes it clearer that it doesn't mean "set when the output is a denormal" Commit created with for f in `git grep -l float_flag_output_denormal`; do sed -i -e 's/float_flag_output_denormal/float_flag_output_denormal_flushed/' $f; done Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-21-peter.maydell@linaro.org --- fpu/softfloat-parts.c.inc | 2 +- fpu/softfloat.c | 2 +- include/fpu/softfloat-types.h | 3 ++- target/arm/vfp_helper.c | 2 +- target/i386/tcg/fpu_helper.c | 2 +- target/m68k/fpu_helper.c | 2 +- target/mips/tcg/msa_helper.c | 2 +- target/rx/op_helper.c | 2 +- target/tricore/fpu_helper.c | 6 +++--- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index b3d693eed0..fee05d0a86 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -334,7 +334,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, } frac_shr(p, frac_shift); } else if (s->flush_to_zero) { - flags |= float_flag_output_denormal; + flags |= float_flag_output_denormal_flushed; p->cls = float_class_zero; exp = 0; frac_clear(p); diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 648050be6f..26f3a8dc87 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -5017,7 +5017,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign, } if ( zExp <= 0 ) { if (status->flush_to_zero) { - float_raise(float_flag_output_denormal, status); + float_raise(float_flag_output_denormal_flushed, status); return packFloatx80(zSign, 0, 0); } isTiny = status->tininess_before_rounding diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 24cd290a1d..c35fdaa5ae 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -156,7 +156,8 @@ enum { float_flag_inexact = 0x0010, /* We flushed an input denormal to 0 (because of flush_inputs_to_zero) */ float_flag_input_denormal_flushed = 0x0020, - float_flag_output_denormal = 0x0040, + /* We flushed an output denormal to 0 (because of flush_to_zero) */ + float_flag_output_denormal_flushed = 0x0040, float_flag_invalid_isi = 0x0080, /* inf - inf */ float_flag_invalid_imz = 0x0100, /* inf * 0 */ float_flag_invalid_idi = 0x0200, /* inf / inf */ diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 444702a460..3c8f3e6588 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -47,7 +47,7 @@ static inline uint32_t vfp_exceptbits_from_host(int host_bits) if (host_bits & float_flag_overflow) { target_bits |= FPSR_OFC; } - if (host_bits & (float_flag_underflow | float_flag_output_denormal)) { + if (host_bits & (float_flag_underflow | float_flag_output_denormal_flushed)) { target_bits |= FPSR_UFC; } if (host_bits & float_flag_inexact) { diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 9c33ac7898..3d764bc138 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -3268,7 +3268,7 @@ void update_mxcsr_from_sse_status(CPUX86State *env) (flags & float_flag_overflow ? FPUS_OE : 0) | (flags & float_flag_underflow ? FPUS_UE : 0) | (flags & float_flag_inexact ? FPUS_PE : 0) | - (flags & float_flag_output_denormal ? FPUS_UE | FPUS_PE : + (flags & float_flag_output_denormal_flushed ? FPUS_UE | FPUS_PE : 0)); } diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index e3f4a18850..339b73ad7d 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -175,7 +175,7 @@ static int cpu_m68k_exceptbits_from_host(int host_bits) if (host_bits & float_flag_overflow) { target_bits |= 0x40; } - if (host_bits & (float_flag_underflow | float_flag_output_denormal)) { + if (host_bits & (float_flag_underflow | float_flag_output_denormal_flushed)) { target_bits |= 0x20; } if (host_bits & float_flag_divbyzero) { diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index aeab6a1d8b..ec38d9fde5 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -6241,7 +6241,7 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) } /* Set Inexact (I) and Underflow (U) when flushing outputs to zero */ - if ((ieee_exception_flags & float_flag_output_denormal) && + if ((ieee_exception_flags & float_flag_output_denormal_flushed) && (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { mips_exception_flags |= FP_INEXACT; if (action & CLEAR_FS_UNDERFLOW) { diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index 59dd1ae612..b3ed822dd1 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -100,7 +100,7 @@ static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr) SET_FPSW(X); } if ((xcpt & (float_flag_input_denormal_flushed - | float_flag_output_denormal)) + | float_flag_output_denormal_flushed)) && !FIELD_EX32(env->fpsw, FPSW, DN)) { env->fpsw = FIELD_DP32(env->fpsw, FPSW, CE, 1); } diff --git a/target/tricore/fpu_helper.c b/target/tricore/fpu_helper.c index 5d38aea143..1b72dcc5f5 100644 --- a/target/tricore/fpu_helper.c +++ b/target/tricore/fpu_helper.c @@ -43,7 +43,7 @@ static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) & (float_flag_invalid | float_flag_overflow | float_flag_underflow - | float_flag_output_denormal + | float_flag_output_denormal_flushed | float_flag_divbyzero | float_flag_inexact); } @@ -99,7 +99,7 @@ static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) some_excp = 1; } - if (flags & float_flag_underflow || flags & float_flag_output_denormal) { + if (flags & float_flag_underflow || flags & float_flag_output_denormal_flushed) { env->FPU_FU = 1 << 31; some_excp = 1; } @@ -109,7 +109,7 @@ static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) some_excp = 1; } - if (flags & float_flag_inexact || flags & float_flag_output_denormal) { + if (flags & float_flag_inexact || flags & float_flag_output_denormal_flushed) { env->PSW |= 1 << 26; some_excp = 1; } From 1c49280f023e87e1c93c136e2b1b435e26f7c332 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:41 +0000 Subject: [PATCH 1343/2892] fpu: Fix a comment in softfloat-types.h In softfloat-types.h a comment documents that if the float_status field flush_to_zero is set then we flush denormalised results to 0 and set the inexact flag. This isn't correct: the status flag that we set when flush_to_zero causes us to flush an output to zero is float_flag_output_denormal_flushed. Correct the comment. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-22-peter.maydell@linaro.org --- include/fpu/softfloat-types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index c35fdaa5ae..616c290145 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -302,7 +302,7 @@ typedef struct float_status { Float3NaNPropRule float_3nan_prop_rule; FloatInfZeroNaNRule float_infzeronan_rule; bool tininess_before_rounding; - /* should denormalised results go to zero and set the inexact flag? */ + /* should denormalised results go to zero and set output_denormal_flushed? */ bool flush_to_zero; /* should denormalised inputs go to zero and set input_denormal_flushed? */ bool flush_inputs_to_zero; From d168a081479c8f90fa99949111c93bcb3a342348 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:45 +0000 Subject: [PATCH 1344/2892] target/arm: Remove redundant advsimd float16 helpers The advsimd_addh etc helpers defined in helper-a64.c are identical to the vfp_addh etc helpers defined in helper-vfp.c: both take two float16 inputs (in a uint32_t type) plus a float_status* and are simple wrappers around the softfloat float16_* functions. (The duplication seems to be a historical accident: we added the advsimd helpers in 2018 as part of the A64 implementation, and at that time there was no f16 emulation in A32. Then later we added the A32 f16 handling by extending the existing VFP helper macros to generate f16 versions as well as f32 and f64, and didn't realise we could clean things up.) Remove the now-unnecessary advsimd helpers and make the places that generated calls to them use the vfp helpers instead. Many of the helper functions were already unused. (The remaining advsimd_ helpers are those which don't have vfp versions.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-26-peter.maydell@linaro.org --- target/arm/tcg/helper-a64.c | 9 --------- target/arm/tcg/helper-a64.h | 8 -------- target/arm/tcg/translate-a64.c | 16 ++++++++-------- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 3b226daee7..05036089dd 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -439,15 +439,6 @@ uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, float_status *fpst) \ return float16_ ## name(a, b, fpst); \ } -ADVSIMD_HALFOP(add) -ADVSIMD_HALFOP(sub) -ADVSIMD_HALFOP(mul) -ADVSIMD_HALFOP(div) -ADVSIMD_HALFOP(min) -ADVSIMD_HALFOP(max) -ADVSIMD_HALFOP(minnum) -ADVSIMD_HALFOP(maxnum) - #define ADVSIMD_TWOHALFOP(name) \ uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, \ float_status *fpst) \ diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 0c120bf388..bac12fbe55 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -47,14 +47,6 @@ DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, fpst) DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, fpst) DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) -DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) -DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) -DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) -DEF_HELPER_FLAGS_3(advsimd_minnumh, TCG_CALL_NO_RWG, f16, f16, f16, fpst) -DEF_HELPER_3(advsimd_addh, f16, f16, f16, fpst) -DEF_HELPER_3(advsimd_subh, f16, f16, f16, fpst) -DEF_HELPER_3(advsimd_mulh, f16, f16, f16, fpst) -DEF_HELPER_3(advsimd_divh, f16, f16, f16, fpst) DEF_HELPER_3(advsimd_ceq_f16, i32, f16, f16, fpst) DEF_HELPER_3(advsimd_cge_f16, i32, f16, f16, fpst) DEF_HELPER_3(advsimd_cgt_f16, i32, f16, f16, fpst) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2b8b253479..703f265f20 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5101,28 +5101,28 @@ static const FPScalar f_scalar_fmul = { TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul) static const FPScalar f_scalar_fmax = { - gen_helper_advsimd_maxh, + gen_helper_vfp_maxh, gen_helper_vfp_maxs, gen_helper_vfp_maxd, }; TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax) static const FPScalar f_scalar_fmin = { - gen_helper_advsimd_minh, + gen_helper_vfp_minh, gen_helper_vfp_mins, gen_helper_vfp_mind, }; TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin) static const FPScalar f_scalar_fmaxnm = { - gen_helper_advsimd_maxnumh, + gen_helper_vfp_maxnumh, gen_helper_vfp_maxnums, gen_helper_vfp_maxnumd, }; TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm) static const FPScalar f_scalar_fminnm = { - gen_helper_advsimd_minnumh, + gen_helper_vfp_minnumh, gen_helper_vfp_minnums, gen_helper_vfp_minnumd, }; @@ -6902,10 +6902,10 @@ static bool do_fp_reduction(DisasContext *s, arg_qrr_e *a, return true; } -TRANS_FEAT(FMAXNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_maxnumh) -TRANS_FEAT(FMINNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_minnumh) -TRANS_FEAT(FMAXV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_maxh) -TRANS_FEAT(FMINV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_minh) +TRANS_FEAT(FMAXNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_maxnumh) +TRANS_FEAT(FMINNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_minnumh) +TRANS_FEAT(FMAXV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_maxh) +TRANS_FEAT(FMINV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_minh) TRANS(FMAXNMV_s, do_fp_reduction, a, gen_helper_vfp_maxnums) TRANS(FMINNMV_s, do_fp_reduction, a, gen_helper_vfp_minnums) From e07b48995aaae22fb8aa582b0a53633734bbea92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 24 Jan 2025 16:27:46 +0000 Subject: [PATCH 1345/2892] target/arm: Use FPST_A64_F16 for halfprec-to-other conversions We should be using the F16-specific float_status for conversions from half-precision, because halfprec inputs never set Input Denormal. Without FEAT_AHP, using the wrong fpst here had no effect, because the only difference between the A64_F16 and A64 fpst is its handling of flush-to-zero on input and output, and the helper functions vfp_fcvt_f16_to_* and vfp_fcvt_*_to_f16 all explicitly squash the relevant flushing flags, and flush_inputs_to_zero was the only way that IDC could be set. With FEAT_AHP, the FPCR.AH=1 behaviour sets IDC for input_denormal_used, which we will only ignore in vfp_get_fpsr_from_host() for the A64_F16 fpst; so it matters that we use that one for f16 inputs (and the normal one for single/double to f16 conversions). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250124162836.2332150-27-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 9 ++++++--- target/arm/tcg/translate-sve.c | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 703f265f20..0b76a2cdb7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8568,7 +8568,7 @@ static bool trans_FCVT_s_sh(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); TCGv_i32 tcg_rd = tcg_temp_new_i32(); - TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64_F16); TCGv_i32 tcg_ahp = get_ahp_flag(); gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); @@ -8582,7 +8582,7 @@ static bool trans_FCVT_s_dh(DisasContext *s, arg_rr *a) if (fp_access_check(s)) { TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); TCGv_i64 tcg_rd = tcg_temp_new_i64(); - TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64_F16); TCGv_i32 tcg_ahp = get_ahp_flag(); gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); @@ -9511,13 +9511,14 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) return true; } - fpst = fpstatus_ptr(FPST_A64); if (a->esz == MO_64) { /* 32 -> 64 bit fp conversion */ TCGv_i64 tcg_res[2]; TCGv_i32 tcg_op = tcg_temp_new_i32(); int srcelt = a->q ? 2 : 0; + fpst = fpstatus_ptr(FPST_A64); + for (pass = 0; pass < 2; pass++) { tcg_res[pass] = tcg_temp_new_i64(); read_vec_element_i32(s, tcg_op, a->rn, srcelt + pass, MO_32); @@ -9532,6 +9533,8 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) TCGv_i32 tcg_res[4]; TCGv_i32 ahp = get_ahp_flag(); + fpst = fpstatus_ptr(FPST_A64_F16); + for (pass = 0; pass < 4; pass++) { tcg_res[pass] = tcg_temp_new_i32(); read_vec_element_i32(s, tcg_res[pass], a->rn, srcelt + pass, MO_16); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index a7dbea5acd..e1788330aa 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3887,7 +3887,7 @@ TRANS_FEAT(FCMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, fcmla_idx_fns[a->esz], TRANS_FEAT(FCVT_sh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_sh, a, 0, FPST_A64) TRANS_FEAT(FCVT_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_hs, a, 0, FPST_A64) + gen_helper_sve_fcvt_hs, a, 0, FPST_A64_F16) TRANS_FEAT(BFCVT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, gen_helper_sve_bfcvt, a, 0, FPST_A64) @@ -3895,7 +3895,7 @@ TRANS_FEAT(BFCVT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, TRANS_FEAT(FCVT_dh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_dh, a, 0, FPST_A64) TRANS_FEAT(FCVT_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_hd, a, 0, FPST_A64) + gen_helper_sve_fcvt_hd, a, 0, FPST_A64_F16) TRANS_FEAT(FCVT_ds, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_ds, a, 0, FPST_A64) TRANS_FEAT(FCVT_sd, aa64_sve, gen_gvec_fpst_arg_zpz, From 664280abddcb3cacc9c6204706bb739fcc1316f7 Mon Sep 17 00:00:00 2001 From: Hongren Zheng Date: Mon, 13 Jan 2025 17:38:56 +0800 Subject: [PATCH 1346/2892] hw/usb/canokey: Fix buffer overflow for OUT packet When USBPacket in OUT direction has larger payload than the ep_out_buffer (of size 512), a buffer overflow would occur. It could be fixed by limiting the size of usb_packet_copy to be at most buffer size. Further optimization gets rid of the ep_out_buffer and directly uses ep_out as the target buffer. This is reported by a security researcher who artificially constructed an OUT packet of size 2047. The report has gone through the QEMU security process, and as this device is for testing purpose and no deployment of it in virtualization environment is observed, it is triaged not to be a security bug. Cc: qemu-stable@nongnu.org Fixes: d7d34918551dc48 ("hw/usb: Add CanoKey Implementation") Reported-by: Juan Jose Lopez Jaimez Signed-off-by: Hongren Zheng Message-id: Z4TfMOrZz6IQYl_h@Sun Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/usb/canokey.c | 6 +++--- hw/usb/canokey.h | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c index fae212f053..e2d66179e0 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -197,8 +197,8 @@ static void canokey_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_OUT: trace_canokey_handle_data_out(ep_out, p->iov.size); - usb_packet_copy(p, key->ep_out_buffer[ep_out], p->iov.size); out_pos = 0; + /* segment packet into (possibly multiple) ep_out */ while (out_pos != p->iov.size) { /* * key->ep_out[ep_out] set by prepare_receive @@ -207,8 +207,8 @@ static void canokey_handle_data(USBDevice *dev, USBPacket *p) * to be the buffer length */ out_len = MIN(p->iov.size - out_pos, key->ep_out_size[ep_out]); - memcpy(key->ep_out[ep_out], - key->ep_out_buffer[ep_out] + out_pos, out_len); + /* usb_packet_copy would update the pos offset internally */ + usb_packet_copy(p, key->ep_out[ep_out], out_len); out_pos += out_len; /* update ep_out_size to actual len */ key->ep_out_size[ep_out] = out_len; diff --git a/hw/usb/canokey.h b/hw/usb/canokey.h index e528889d33..1b60d73485 100644 --- a/hw/usb/canokey.h +++ b/hw/usb/canokey.h @@ -24,8 +24,6 @@ #define CANOKEY_EP_NUM 3 /* BULK/INTR IN can be up to 1352 bytes, e.g. get key info */ #define CANOKEY_EP_IN_BUFFER_SIZE 2048 -/* BULK OUT can be up to 270 bytes, e.g. PIV import cert */ -#define CANOKEY_EP_OUT_BUFFER_SIZE 512 typedef enum { CANOKEY_EP_IN_WAIT, @@ -59,8 +57,6 @@ typedef struct CanoKeyState { /* OUT pointer to canokey recv buffer */ uint8_t *ep_out[CANOKEY_EP_NUM]; uint32_t ep_out_size[CANOKEY_EP_NUM]; - /* For large BULK OUT, multiple write to ep_out is needed */ - uint8_t ep_out_buffer[CANOKEY_EP_NUM][CANOKEY_EP_OUT_BUFFER_SIZE]; /* Properties */ char *file; /* canokey-file */ From 3b36ee720288ba17962a17b305243ea34100e1f3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 28 Jan 2025 17:06:11 +0100 Subject: [PATCH 1347/2892] gitlab-ci: include full Rust backtraces in test runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest-template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 39da7698b0..4cc1923931 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -63,6 +63,7 @@ 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 From ed19620846cfe0206d88fd37522a79de96ebddc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 14 Jan 2025 14:48:11 +0400 Subject: [PATCH 1348/2892] migration: fix -Werror=maybe-uninitialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../migration/savevm.c: In function ‘qemu_savevm_state_complete_precopy_non_iterable’: ../migration/savevm.c:1560:20: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialized] 1560 | return ret; | ^~~ Cc: Peter Xu Signed-off-by: Marc-André Lureau Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250114104811.2612846-1-marcandre.lureau@redhat.com> Signed-off-by: Fabiano Rosas --- migration/savevm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/savevm.c b/migration/savevm.c index c929da1ca5..6e56d4cf1d 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1557,7 +1557,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, migrate_set_error(ms, local_err); error_report_err(local_err); qemu_file_set_error(f, -EFAULT); - return ret; + return -1; } } if (!in_postcopy) { From 57ad6ab804cd24d6dd4a08f40f83081c393ee0b9 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:27 -0800 Subject: [PATCH 1349/2892] backends/hostmem-shm: factor out allocation of "anonymous shared memory with an fd" Let's factor it out so we can reuse it. Signed-off-by: David Hildenbrand Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- backends/hostmem-shm.c | 45 ++++-------------------------------- include/qemu/osdep.h | 1 + meson.build | 8 +++++-- util/oslib-posix.c | 52 ++++++++++++++++++++++++++++++++++++++++++ util/oslib-win32.c | 6 +++++ 5 files changed, 69 insertions(+), 43 deletions(-) diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c index 5551ba78a6..fabee41f2c 100644 --- a/backends/hostmem-shm.c +++ b/backends/hostmem-shm.c @@ -25,11 +25,9 @@ struct HostMemoryBackendShm { static bool shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { - g_autoptr(GString) shm_name = g_string_new(NULL); g_autofree char *backend_name = NULL; uint32_t ram_flags; - int fd, oflag; - mode_t mode; + int fd; if (!backend->size) { error_setg(errp, "can't create shm backend with size 0"); @@ -41,48 +39,13 @@ shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return false; } - /* - * Let's use `mode = 0` because we don't want other processes to open our - * memory unless we share the file descriptor with them. - */ - mode = 0; - oflag = O_RDWR | O_CREAT | O_EXCL; - backend_name = host_memory_backend_get_name(backend); - - /* - * Some operating systems allow creating anonymous POSIX shared memory - * objects (e.g. FreeBSD provides the SHM_ANON constant), but this is not - * defined by POSIX, so let's create a unique name. - * - * From Linux's shm_open(3) man-page: - * For portable use, a shared memory object should be identified - * by a name of the form /somename;" - */ - g_string_printf(shm_name, "/qemu-" FMT_pid "-shm-%s", getpid(), - backend_name); - - fd = shm_open(shm_name->str, oflag, mode); + fd = qemu_shm_alloc(backend->size, errp); if (fd < 0) { - error_setg_errno(errp, errno, - "failed to create POSIX shared memory"); - return false; - } - - /* - * We have the file descriptor, so we no longer need to expose the - * POSIX shared memory object. However it will remain allocated as long as - * there are file descriptors pointing to it. - */ - shm_unlink(shm_name->str); - - if (ftruncate(fd, backend->size) == -1) { - error_setg_errno(errp, errno, - "failed to resize POSIX shared memory to %" PRIu64, - backend->size); - close(fd); return false; } + /* Let's do the same as memory-backend-ram,share=on would do. */ + backend_name = host_memory_backend_get_name(backend); ram_flags = RAM_SHARED; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index b94fb5fab8..112ebdff21 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -509,6 +509,7 @@ int qemu_daemon(int nochdir, int noclose); void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, bool noreserve); void qemu_anon_ram_free(void *ptr, size_t size); +int qemu_shm_alloc(size_t size, Error **errp); #ifdef _WIN32 #define HAVE_CHARDEV_SERIAL 1 diff --git a/meson.build b/meson.build index 15a066043b..2c9ac9cfe1 100644 --- a/meson.build +++ b/meson.build @@ -3696,9 +3696,13 @@ libqemuutil = static_library('qemuutil', build_by_default: false, sources: util_ss.sources() + stub_ss.sources() + genh, dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc]) +qemuutil_deps = [event_loop_base] +if host_os != 'windows' + qemuutil_deps += [rt] +endif qemuutil = declare_dependency(link_with: libqemuutil, sources: genh + version_res, - dependencies: [event_loop_base]) + dependencies: qemuutil_deps) if have_system or have_user decodetree = generator(find_program('scripts/decodetree.py'), @@ -4357,7 +4361,7 @@ if have_tools subdir('contrib/elf2dmp') executable('qemu-edid', files('qemu-edid.c', 'hw/display/edid-generate.c'), - dependencies: qemuutil, + dependencies: [qemuutil, rt], install: true) if have_vhost_user diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 7a542cb50b..2bb34dade3 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -931,3 +931,55 @@ void qemu_close_all_open_fd(const int *skip, unsigned int nskip) qemu_close_all_open_fd_fallback(skip, nskip, open_max); } } + +int qemu_shm_alloc(size_t size, Error **errp) +{ + g_autoptr(GString) shm_name = g_string_new(NULL); + int fd, oflag, cur_sequence; + static int sequence; + mode_t mode; + + cur_sequence = qatomic_fetch_inc(&sequence); + + /* + * Let's use `mode = 0` because we don't want other processes to open our + * memory unless we share the file descriptor with them. + */ + mode = 0; + oflag = O_RDWR | O_CREAT | O_EXCL; + + /* + * Some operating systems allow creating anonymous POSIX shared memory + * objects (e.g. FreeBSD provides the SHM_ANON constant), but this is not + * defined by POSIX, so let's create a unique name. + * + * From Linux's shm_open(3) man-page: + * For portable use, a shared memory object should be identified + * by a name of the form /somename;" + */ + g_string_printf(shm_name, "/qemu-" FMT_pid "-shm-%d", getpid(), + cur_sequence); + + fd = shm_open(shm_name->str, oflag, mode); + if (fd < 0) { + error_setg_errno(errp, errno, + "failed to create POSIX shared memory"); + return -1; + } + + /* + * We have the file descriptor, so we no longer need to expose the + * POSIX shared memory object. However it will remain allocated as long as + * there are file descriptors pointing to it. + */ + shm_unlink(shm_name->str); + + if (ftruncate(fd, size) == -1) { + error_setg_errno(errp, errno, + "failed to resize POSIX shared memory to %zu", size); + close(fd); + return -1; + } + + return fd; +} diff --git a/util/oslib-win32.c b/util/oslib-win32.c index b623830d62..b7351634ec 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -877,3 +877,9 @@ void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp) } CloseHandle(h); } + +int qemu_shm_alloc(size_t size, Error **errp) +{ + error_setg(errp, "Shared memory is not supported."); + return -1; +} From 719168fba7c3215cc996dcfd32a6e5e9c7b8eee0 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:28 -0800 Subject: [PATCH 1350/2892] physmem: fix qemu_ram_alloc_from_fd size calculation qemu_ram_alloc_from_fd allocates space if file_size == 0. If non-zero, it uses the existing space and verifies it is large enough, but the verification was broken when the offset parameter was introduced. As a result, a file smaller than offset passes the verification and causes errors later. Fix that, and update the error message to include offset. Peter provides this concise reproducer: $ touch ramfile $ truncate -s 64M ramfile $ ./qemu-system-x86_64 -object memory-backend-file,mem-path=./ramfile,offset=128M,size=128M,id=mem1,prealloc=on qemu-system-x86_64: qemu_prealloc_mem: preallocating memory failed: Bad address With the fix, the error message is: qemu-system-x86_64: mem1 backing store size 0x4000000 is too small for 'size' option 0x8000000 plus 'offset' option 0x8000000 Cc: qemu-stable@nongnu.org Fixes: 4b870dc4d0c0 ("hostmem-file: add offset option") Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Acked-by: David Hildenbrand Link: https://lore.kernel.org/r/1736967650-129648-3-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- system/physmem.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index c76503aea8..792844d5a5 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1970,10 +1970,12 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, size = REAL_HOST_PAGE_ALIGN(size); file_size = get_file_size(fd); - if (file_size > offset && file_size < (offset + size)) { - error_setg(errp, "backing store size 0x%" PRIx64 - " does not match 'size' option 0x" RAM_ADDR_FMT, - file_size, size); + if (file_size && file_size < offset + size) { + error_setg(errp, "%s backing store size 0x%" PRIx64 + " is too small for 'size' option 0x" RAM_ADDR_FMT + " plus 'offset' option 0x%" PRIx64, + memory_region_name(mr), file_size, size, + (uint64_t)offset); return NULL; } From 3ec02148160a8147187fce211d1251af2c4cf9f1 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:29 -0800 Subject: [PATCH 1351/2892] physmem: qemu_ram_alloc_from_fd extensions Extend qemu_ram_alloc_from_fd to support resizable ram, and define qemu_ram_resize_cb to clean up the API. Add a grow parameter to extend the file if necessary. However, if grow is false, a zero-sized file is always extended. Signed-off-by: Steve Sistare Link: https://lore.kernel.org/r/1736967650-129648-4-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- include/exec/ram_addr.h | 13 +++++++++---- system/memory.c | 4 ++-- system/physmem.c | 35 ++++++++++++++++++++--------------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index ff157c1f42..94bb3ccbe4 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -111,23 +111,30 @@ long qemu_maxrampagesize(void); * * Parameters: * @size: the size in bytes of the ram block + * @max_size: the maximum size of the block after resizing * @mr: the memory region where the ram block is + * @resized: callback after calls to qemu_ram_resize * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, * RAM_READONLY_FD, RAM_GUEST_MEMFD * @mem_path or @fd: specify the backing file or device * @offset: Offset into target file + * @grow: extend file if necessary (but an empty file is always extended). * @errp: pointer to Error*, to store an error if it happens * * Return: * On success, return a pointer to the ram block. * On failure, return NULL. */ +typedef void (*qemu_ram_resize_cb)(const char *, uint64_t length, void *host); + RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, const char *mem_path, off_t offset, Error **errp); -RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, +RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, + qemu_ram_resize_cb resized, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, + bool grow, Error **errp); RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, @@ -135,9 +142,7 @@ RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp); RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size, - void (*resized)(const char*, - uint64_t length, - void *host), + qemu_ram_resize_cb resized, MemoryRegion *mr, Error **errp); void qemu_ram_free(RAMBlock *block); diff --git a/system/memory.c b/system/memory.c index b17b5538ff..4c829793a0 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1680,8 +1680,8 @@ bool memory_region_init_ram_from_fd(MemoryRegion *mr, mr->readonly = !!(ram_flags & RAM_READONLY); mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, - &err); + mr->ram_block = qemu_ram_alloc_from_fd(size, size, NULL, mr, ram_flags, fd, + offset, false, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); diff --git a/system/physmem.c b/system/physmem.c index 792844d5a5..4d13761329 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1942,8 +1942,10 @@ out_free: } #ifdef CONFIG_POSIX -RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, +RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, + qemu_ram_resize_cb resized, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, + bool grow, Error **errp) { RAMBlock *new_block; @@ -1953,7 +1955,9 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, /* Just support these ram flags by now. */ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | RAM_PROTECTED | RAM_NAMED_FILE | RAM_READONLY | - RAM_READONLY_FD | RAM_GUEST_MEMFD)) == 0); + RAM_READONLY_FD | RAM_GUEST_MEMFD | + RAM_RESIZEABLE)) == 0); + assert(max_size >= size); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -1968,13 +1972,15 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, size = TARGET_PAGE_ALIGN(size); size = REAL_HOST_PAGE_ALIGN(size); + max_size = TARGET_PAGE_ALIGN(max_size); + max_size = REAL_HOST_PAGE_ALIGN(max_size); file_size = get_file_size(fd); - if (file_size && file_size < offset + size) { + if (file_size && file_size < offset + max_size && !grow) { error_setg(errp, "%s backing store size 0x%" PRIx64 " is too small for 'size' option 0x" RAM_ADDR_FMT " plus 'offset' option 0x%" PRIx64, - memory_region_name(mr), file_size, size, + memory_region_name(mr), file_size, max_size, (uint64_t)offset); return NULL; } @@ -1990,11 +1996,13 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; new_block->used_length = size; - new_block->max_length = size; + new_block->max_length = max_size; + new_block->resized = resized; new_block->flags = ram_flags; new_block->guest_memfd = -1; - new_block->host = file_ram_alloc(new_block, size, fd, !file_size, offset, - errp); + new_block->host = file_ram_alloc(new_block, max_size, fd, + file_size < offset + max_size, + offset, errp); if (!new_block->host) { g_free(new_block); return NULL; @@ -2046,7 +2054,8 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, return NULL; } - block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, errp); + block = qemu_ram_alloc_from_fd(size, size, NULL, mr, ram_flags, fd, offset, + false, errp); if (!block) { if (created) { unlink(mem_path); @@ -2061,9 +2070,7 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, static RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, - void (*resized)(const char*, - uint64_t length, - void *host), + qemu_ram_resize_cb resized, void *host, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { @@ -2115,10 +2122,8 @@ RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, } RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, - void (*resized)(const char*, - uint64_t length, - void *host), - MemoryRegion *mr, Error **errp) + qemu_ram_resize_cb resized, + MemoryRegion *mr, Error **errp) { return qemu_ram_alloc_internal(size, maxsz, resized, NULL, RAM_RESIZEABLE, mr, errp); From 9fb40bb9621df9acb88a8128bee2e0f68631b245 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:30 -0800 Subject: [PATCH 1352/2892] physmem: fd-based shared memory Create MAP_SHARED RAMBlocks by mmap'ing a file descriptor rather than using MAP_ANON, so the memory can be accessed in another process by passing and mmap'ing the fd. This will allow CPR to support memory-backend-ram and memory-backend-shm objects, provided the user creates them with share=on. Use memfd_create if available because it has no constraints. If not, use POSIX shm_open. However, allocation on the opened fd may fail if the shm mount size is too small, even if the system has free memory, so for backwards compatibility fall back to qemu_anon_ram_alloc/MAP_ANON on failure. For backwards compatibility on Windows, always use MAP_ANON. share=on has no purpose there, but the syntax is accepted, and must continue to work. Lastly, quietly fall back to MAP_ANON if the system does not support qemu_ram_alloc_from_fd. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-5-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- system/physmem.c | 57 ++++++++++++++++++++++++++++++++++++++++++++- system/trace-events | 1 + util/memfd.c | 16 ++++++++++--- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 4d13761329..e4355649e9 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -48,6 +48,7 @@ #include "qemu/qemu-print.h" #include "qemu/log.h" #include "qemu/memalign.h" +#include "qemu/memfd.h" #include "exec/memory.h" #include "exec/ioport.h" #include "system/dma.h" @@ -1948,6 +1949,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, bool grow, Error **errp) { + ERRP_GUARD(); RAMBlock *new_block; Error *local_err = NULL; int64_t file_size, file_align; @@ -2068,6 +2070,25 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, } #endif +#ifdef CONFIG_POSIX +/* + * Create MAP_SHARED RAMBlocks by mmap'ing a file descriptor, so it can be + * shared with another process if CPR is being used. Use memfd if available + * because it has no size limits, else use POSIX shm. + */ +static int qemu_ram_get_shared_fd(const char *name, Error **errp) +{ + int fd; + + if (qemu_memfd_check(0)) { + fd = qemu_memfd_create(name, 0, 0, 0, 0, errp); + } else { + fd = qemu_shm_alloc(0, errp); + } + return fd; +} +#endif + static RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, qemu_ram_resize_cb resized, @@ -2081,6 +2102,41 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC | RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); assert(!host ^ (ram_flags & RAM_PREALLOC)); + assert(max_size >= size); + +#ifdef CONFIG_POSIX /* ignore RAM_SHARED for Windows */ + if (!host) { + if (ram_flags & RAM_SHARED) { + const char *name = memory_region_name(mr); + int fd = qemu_ram_get_shared_fd(name, errp); + + if (fd < 0) { + return NULL; + } + + /* Use same alignment as qemu_anon_ram_alloc */ + mr->align = QEMU_VMALLOC_ALIGN; + + /* + * This can fail if the shm mount size is too small, or alloc from + * fd is not supported, but previous QEMU versions that called + * qemu_anon_ram_alloc for anonymous shared memory could have + * succeeded. Quietly fail and fall back. + */ + new_block = qemu_ram_alloc_from_fd(size, max_size, resized, mr, + ram_flags, fd, 0, false, NULL); + if (new_block) { + trace_qemu_ram_alloc_shared(name, new_block->used_length, + new_block->max_length, fd, + new_block->host); + return new_block; + } + + close(fd); + /* fall back to anon allocation */ + } + } +#endif align = qemu_real_host_page_size(); align = MAX(align, TARGET_PAGE_SIZE); @@ -2092,7 +2148,6 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, new_block->resized = resized; new_block->used_length = size; new_block->max_length = max_size; - assert(max_size >= size); new_block->fd = -1; new_block->guest_memfd = -1; new_block->page_size = qemu_real_host_page_size(); diff --git a/system/trace-events b/system/trace-events index 5bbc3fbffa..be12ebfb41 100644 --- a/system/trace-events +++ b/system/trace-events @@ -33,6 +33,7 @@ address_space_map(void *as, uint64_t addr, uint64_t len, bool is_write, uint32_t find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64 find_ram_offset_loop(uint64_t size, uint64_t candidate, uint64_t offset, uint64_t next, uint64_t mingap) "trying size: 0x%" PRIx64 " @ 0x%" PRIx64 ", offset: 0x%" PRIx64" next: 0x%" PRIx64 " mingap: 0x%" PRIx64 ram_block_discard_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d" +qemu_ram_alloc_shared(const char *name, size_t size, size_t max_size, int fd, void *host) "%s size %zu max_size %zu fd %d host %p" # cpus.c vm_stop_flush_all(int ret) "ret %d" diff --git a/util/memfd.c b/util/memfd.c index 8a2e906962..07beab174d 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -194,17 +194,27 @@ bool qemu_memfd_alloc_check(void) /** * qemu_memfd_check(): * - * Check if host supports memfd. + * Check if host supports memfd. Cache the answer for the common case flags=0. */ bool qemu_memfd_check(unsigned int flags) { #ifdef CONFIG_LINUX - int mfd = memfd_create("test", flags | MFD_CLOEXEC); + int mfd; + static int memfd_check = MEMFD_TODO; + if (!flags && memfd_check != MEMFD_TODO) { + return memfd_check; + } + + mfd = memfd_create("test", flags | MFD_CLOEXEC); if (mfd >= 0) { close(mfd); - return true; } + if (!flags) { + memfd_check = (mfd >= 0) ? MEMFD_OK : MEMFD_KO; + } + return (mfd >= 0); + #endif return false; From 6169f1193657d0ba630a2ce33cef639ae918bce4 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:31 -0800 Subject: [PATCH 1353/2892] memory: add RAM_PRIVATE Define the RAM_PRIVATE flag. In RAMBlock creation functions, if MAP_SHARED is 0 in the flags parameter, in a subsequent patch the implementation may still create a shared mapping if other conditions require it. Callers who specifically want a private mapping, eg for objects specified by the user, must pass RAM_PRIVATE. After RAMBlock creation, MAP_SHARED in the block's flags indicates whether the block is shared or private, and MAP_PRIVATE is omitted. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-6-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- backends/hostmem-epc.c | 2 +- backends/hostmem-file.c | 2 +- backends/hostmem-memfd.c | 2 +- backends/hostmem-ram.c | 2 +- include/exec/memory.h | 10 ++++++++++ system/physmem.c | 15 ++++++++++++--- 6 files changed, 26 insertions(+), 7 deletions(-) diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index eb4b95dfd7..1fa2d031e4 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -36,7 +36,7 @@ sgx_epc_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) backend->aligned = true; name = object_get_canonical_path(OBJECT(backend)); - ram_flags = (backend->share ? RAM_SHARED : 0) | RAM_PROTECTED; + ram_flags = (backend->share ? RAM_SHARED : RAM_PRIVATE) | RAM_PROTECTED; return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, backend->size, ram_flags, fd, 0, errp); } diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 46321fda84..691a827819 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -82,7 +82,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) backend->aligned = true; name = host_memory_backend_get_name(backend); - ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags = backend->share ? RAM_SHARED : RAM_PRIVATE; ram_flags |= fb->readonly ? RAM_READONLY_FD : 0; ram_flags |= fb->rom == ON_OFF_AUTO_ON ? RAM_READONLY : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index d4d0620e6c..1672da9e30 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -52,7 +52,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) backend->aligned = true; name = host_memory_backend_get_name(backend); - ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags = backend->share ? RAM_SHARED : RAM_PRIVATE; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index 39aac6bf35..868ae6ca80 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -28,7 +28,7 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) } name = host_memory_backend_get_name(backend); - ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags = backend->share ? RAM_SHARED : RAM_PRIVATE; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; return memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), diff --git a/include/exec/memory.h b/include/exec/memory.h index 3ee1901b52..9f73b59867 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -246,6 +246,16 @@ typedef struct IOMMUTLBEvent { /* RAM can be private that has kvm guest memfd backend */ #define RAM_GUEST_MEMFD (1 << 12) +/* + * In RAMBlock creation functions, if MAP_SHARED is 0 in the flags parameter, + * the implementation may still create a shared mapping if other conditions + * require it. Callers who specifically want a private mapping, eg objects + * specified by the user, must pass RAM_PRIVATE. + * After RAMBlock creation, MAP_SHARED in the block's flags indicates whether + * the block is shared or private, and MAP_PRIVATE is omitted. + */ +#define RAM_PRIVATE (1 << 13) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, diff --git a/system/physmem.c b/system/physmem.c index e4355649e9..03fac0a64f 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1952,7 +1952,11 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, ERRP_GUARD(); RAMBlock *new_block; Error *local_err = NULL; - int64_t file_size, file_align; + int64_t file_size, file_align, share_flags; + + share_flags = ram_flags & (RAM_PRIVATE | RAM_SHARED); + assert(share_flags != (RAM_SHARED | RAM_PRIVATE)); + ram_flags &= ~RAM_PRIVATE; /* Just support these ram flags by now. */ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | @@ -2097,7 +2101,11 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, { RAMBlock *new_block; Error *local_err = NULL; - int align; + int align, share_flags; + + share_flags = ram_flags & (RAM_PRIVATE | RAM_SHARED); + assert(share_flags != (RAM_SHARED | RAM_PRIVATE)); + ram_flags &= ~RAM_PRIVATE; assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC | RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); @@ -2172,7 +2180,8 @@ RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { - assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE | RAM_GUEST_MEMFD | + RAM_PRIVATE)) == 0); return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } From 91792807d11cb30021575cec31fd9dd458efba23 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:32 -0800 Subject: [PATCH 1354/2892] machine: aux-ram-share option Allocate auxilliary guest RAM as an anonymous file that is shareable with an external process. This option applies to memory allocated as a side effect of creating various devices. It does not apply to memory-backend-objects, whether explicitly specified on the command line, or implicitly created by the -m command line option. This option is intended to support new migration modes, in which the memory region can be transferred in place to a new QEMU process, by sending the memfd file descriptor to the process. Memory contents are preserved, and if the mode also transfers device descriptors, then pages that are locked in memory for DMA remain locked. This behavior is a pre-requisite for supporting vfio, vdpa, and iommufd devices with the new modes. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-7-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- hw/core/machine.c | 22 ++++++++++++++++++++++ include/hw/boards.h | 1 + qemu-options.hx | 11 +++++++++++ system/physmem.c | 3 +++ 4 files changed, 37 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index c23b399496..2b11bc4f66 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -457,6 +457,22 @@ static void machine_set_mem_merge(Object *obj, bool value, Error **errp) ms->mem_merge = value; } +#ifdef CONFIG_POSIX +static bool machine_get_aux_ram_share(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->aux_ram_share; +} + +static void machine_set_aux_ram_share(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->aux_ram_share = value; +} +#endif + static bool machine_get_usb(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -1162,6 +1178,12 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "mem-merge", "Enable/disable memory merge support"); +#ifdef CONFIG_POSIX + object_class_property_add_bool(oc, "aux-ram-share", + machine_get_aux_ram_share, + machine_set_aux_ram_share); +#endif + object_class_property_add_bool(oc, "usb", machine_get_usb, machine_set_usb); object_class_property_set_description(oc, "usb", diff --git a/include/hw/boards.h b/include/hw/boards.h index 2ad711e56d..e1f41b2a53 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -410,6 +410,7 @@ struct MachineState { bool enable_graphics; ConfidentialGuestSupport *cgs; HostMemoryBackend *memdev; + bool aux_ram_share; /* * convenience alias to ram_memdev_id backend memory region * or to numa container memory region diff --git a/qemu-options.hx b/qemu-options.hx index 7090d59f6f..90fad31590 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -38,6 +38,9 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " nvdimm=on|off controls NVDIMM support (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n" +#ifdef CONFIG_POSIX + " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" +#endif " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n" " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n", QEMU_ARCH_ALL) @@ -101,6 +104,14 @@ SRST Enables or disables ACPI Heterogeneous Memory Attribute Table (HMAT) support. The default is off. + ``aux-ram-share=on|off`` + Allocate auxiliary guest RAM as an anonymous file that is + shareable with an external process. This option applies to + memory allocated as a side effect of creating various devices. + It does not apply to memory-backend-objects, whether explicitly + specified on the command line, or implicitly created by the -m + command line option. The default is off. + ``memory-backend='id'`` An alternative to legacy ``-mem-path`` and ``mem-prealloc`` options. Allows to use a memory backend as main RAM. diff --git a/system/physmem.c b/system/physmem.c index 03fac0a64f..cb80ce3091 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2114,6 +2114,9 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, #ifdef CONFIG_POSIX /* ignore RAM_SHARED for Windows */ if (!host) { + if (!share_flags && current_machine->aux_ram_share) { + ram_flags |= RAM_SHARED; + } if (ram_flags & RAM_SHARED) { const char *name = memory_region_name(mr); int fd = qemu_ram_get_shared_fd(name, errp); From e7d79011a4b2e7bf8f7a0423164b65656d1619d7 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:33 -0800 Subject: [PATCH 1355/2892] migration: cpr-state CPR must save state that is needed after QEMU is restarted, when devices are realized. Thus the extra state cannot be saved in the migration channel, as objects must already exist before that channel can be loaded. Instead, define auxilliary state structures and vmstate descriptions, not associated with any registered object, and serialize the aux state to a cpr-specific channel in cpr_state_save. Deserialize in cpr_state_load after QEMU restarts, before devices are realized. Provide accessors for clients to register file descriptors for saving. The mechanism for passing the fd's to the new process will be specific to each migration mode, and added in subsequent patches. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-8-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- include/migration/cpr.h | 25 +++++ migration/cpr.c | 198 ++++++++++++++++++++++++++++++++++++++++ migration/meson.build | 1 + migration/migration.c | 1 + migration/trace-events | 7 ++ 5 files changed, 232 insertions(+) create mode 100644 include/migration/cpr.h create mode 100644 migration/cpr.c diff --git a/include/migration/cpr.h b/include/migration/cpr.h new file mode 100644 index 0000000000..d9364f7d1f --- /dev/null +++ b/include/migration/cpr.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021, 2024 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef MIGRATION_CPR_H +#define MIGRATION_CPR_H + +#include "qapi/qapi-types-migration.h" + +#define QEMU_CPR_FILE_MAGIC 0x51435052 +#define QEMU_CPR_FILE_VERSION 0x00000001 + +void cpr_save_fd(const char *name, int id, int fd); +void cpr_delete_fd(const char *name, int id); +int cpr_find_fd(const char *name, int id); + +int cpr_state_save(MigrationChannel *channel, Error **errp); +int cpr_state_load(MigrationChannel *channel, Error **errp); +void cpr_state_close(void); +struct QIOChannel *cpr_state_ioc(void); + +#endif diff --git a/migration/cpr.c b/migration/cpr.c new file mode 100644 index 0000000000..87bcfdb5ff --- /dev/null +++ b/migration/cpr.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021-2024 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "migration/cpr.h" +#include "migration/misc.h" +#include "migration/options.h" +#include "migration/qemu-file.h" +#include "migration/savevm.h" +#include "migration/vmstate.h" +#include "system/runstate.h" +#include "trace.h" + +/*************************************************************************/ +/* cpr state container for all information to be saved. */ + +typedef QLIST_HEAD(CprFdList, CprFd) CprFdList; + +typedef struct CprState { + CprFdList fds; +} CprState; + +static CprState cpr_state; + +/****************************************************************************/ + +typedef struct CprFd { + char *name; + unsigned int namelen; + int id; + int fd; + QLIST_ENTRY(CprFd) next; +} CprFd; + +static const VMStateDescription vmstate_cpr_fd = { + .name = "cpr fd", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(namelen, CprFd), + VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen), + VMSTATE_INT32(id, CprFd), + VMSTATE_INT32(fd, CprFd), + VMSTATE_END_OF_LIST() + } +}; + +void cpr_save_fd(const char *name, int id, int fd) +{ + CprFd *elem = g_new0(CprFd, 1); + + trace_cpr_save_fd(name, id, fd); + elem->name = g_strdup(name); + elem->namelen = strlen(name) + 1; + elem->id = id; + elem->fd = fd; + QLIST_INSERT_HEAD(&cpr_state.fds, elem, next); +} + +static CprFd *find_fd(CprFdList *head, const char *name, int id) +{ + CprFd *elem; + + QLIST_FOREACH(elem, head, next) { + if (!strcmp(elem->name, name) && elem->id == id) { + return elem; + } + } + return NULL; +} + +void cpr_delete_fd(const char *name, int id) +{ + CprFd *elem = find_fd(&cpr_state.fds, name, id); + + if (elem) { + QLIST_REMOVE(elem, next); + g_free(elem->name); + g_free(elem); + } + + trace_cpr_delete_fd(name, id); +} + +int cpr_find_fd(const char *name, int id) +{ + CprFd *elem = find_fd(&cpr_state.fds, name, id); + int fd = elem ? elem->fd : -1; + + trace_cpr_find_fd(name, id, fd); + return fd; +} +/*************************************************************************/ +#define CPR_STATE "CprState" + +static const VMStateDescription vmstate_cpr_state = { + .name = CPR_STATE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next), + VMSTATE_END_OF_LIST() + } +}; +/*************************************************************************/ + +static QEMUFile *cpr_state_file; + +QIOChannel *cpr_state_ioc(void) +{ + return qemu_file_get_ioc(cpr_state_file); +} + +int cpr_state_save(MigrationChannel *channel, Error **errp) +{ + int ret; + QEMUFile *f; + MigMode mode = migrate_mode(); + + trace_cpr_state_save(MigMode_str(mode)); + + /* set f based on mode in a later patch in this series */ + return 0; + + qemu_put_be32(f, QEMU_CPR_FILE_MAGIC); + qemu_put_be32(f, QEMU_CPR_FILE_VERSION); + + ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0); + if (ret) { + error_setg(errp, "vmstate_save_state error %d", ret); + qemu_fclose(f); + return ret; + } + + /* + * Close the socket only partially so we can later detect when the other + * end closes by getting a HUP event. + */ + qemu_fflush(f); + qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE, + NULL); + cpr_state_file = f; + return 0; +} + +int cpr_state_load(MigrationChannel *channel, Error **errp) +{ + int ret; + uint32_t v; + QEMUFile *f; + MigMode mode = 0; + + /* set f and mode based on other parameters later in this patch series */ + return 0; + + trace_cpr_state_load(MigMode_str(mode)); + + v = qemu_get_be32(f); + if (v != QEMU_CPR_FILE_MAGIC) { + error_setg(errp, "Not a migration stream (bad magic %x)", v); + qemu_fclose(f); + return -EINVAL; + } + v = qemu_get_be32(f); + if (v != QEMU_CPR_FILE_VERSION) { + error_setg(errp, "Unsupported migration stream version %d", v); + qemu_fclose(f); + return -ENOTSUP; + } + + ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1); + if (ret) { + error_setg(errp, "vmstate_load_state error %d", ret); + qemu_fclose(f); + return ret; + } + + /* + * Let the caller decide when to close the socket (and generate a HUP event + * for the sending side). + */ + cpr_state_file = f; + + return ret; +} + +void cpr_state_close(void) +{ + if (cpr_state_file) { + qemu_fclose(cpr_state_file); + cpr_state_file = NULL; + } +} diff --git a/migration/meson.build b/migration/meson.build index dac687ee3a..1eb8c96d23 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -14,6 +14,7 @@ system_ss.add(files( 'block-active.c', 'channel.c', 'channel-block.c', + 'cpr.c', 'cpu-throttle.c', 'dirtyrate.c', 'exec.c', diff --git a/migration/migration.c b/migration/migration.c index 2d1da917c7..fce7b22ae8 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -27,6 +27,7 @@ #include "system/cpu-throttle.h" #include "rdma.h" #include "ram.h" +#include "migration/cpr.h" #include "migration/global_state.h" #include "migration/misc.h" #include "migration.h" diff --git a/migration/trace-events b/migration/trace-events index b82a1c5e40..4e3061bc55 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -342,6 +342,13 @@ colo_receive_message(const char *msg) "Receive '%s' message" # colo-failover.c colo_failover_set_state(const char *new_state) "new state %s" +# cpr.c +cpr_save_fd(const char *name, int id, int fd) "%s, id %d, fd %d" +cpr_delete_fd(const char *name, int id) "%s, id %d" +cpr_find_fd(const char *name, int id, int fd) "%s, id %d returns %d" +cpr_state_save(const char *mode) "%s mode" +cpr_state_load(const char *mode) "%s mode" + # block-dirty-bitmap.c send_bitmap_header_enter(void) "" send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uint64_t data_size) "flags: 0x%x, start_sector: %" PRIu64 ", nr_sectors: %" PRIu32 ", data_size: %" PRIu64 From 2b7e9739fad8812d74e5e0ff4433ec25fb461281 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:34 -0800 Subject: [PATCH 1356/2892] physmem: preserve ram blocks for cpr Save the memfd for ramblocks in CPR state, along with a name that uniquely identifies it. The block's idstr is not yet set, so it cannot be used for this purpose. Find the saved memfd in new QEMU when creating a block. If size of a resizable block is larger in new QEMU, extend it via the file_ram_alloc truncate parameter, and the extra space will be usable after a guest reset. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-9-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- system/physmem.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index cb80ce3091..67c9db9daa 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -70,6 +70,7 @@ #include "qemu/pmem.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "qemu/range.h" @@ -1661,6 +1662,18 @@ void qemu_ram_unset_idstr(RAMBlock *block) } } +static char *cpr_name(MemoryRegion *mr) +{ + const char *mr_name = memory_region_name(mr); + g_autofree char *id = mr->dev ? qdev_get_dev_path(mr->dev) : NULL; + + if (id) { + return g_strdup_printf("%s/%s", id, mr_name); + } else { + return g_strdup(mr_name); + } +} + size_t qemu_ram_pagesize(RAMBlock *rb) { return rb->page_size; @@ -2080,15 +2093,25 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, * shared with another process if CPR is being used. Use memfd if available * because it has no size limits, else use POSIX shm. */ -static int qemu_ram_get_shared_fd(const char *name, Error **errp) +static int qemu_ram_get_shared_fd(const char *name, bool *reused, Error **errp) { - int fd; + int fd = cpr_find_fd(name, 0); + + if (fd >= 0) { + *reused = true; + return fd; + } if (qemu_memfd_check(0)) { fd = qemu_memfd_create(name, 0, 0, 0, 0, errp); } else { fd = qemu_shm_alloc(0, errp); } + + if (fd >= 0) { + cpr_save_fd(name, 0, fd); + } + *reused = false; return fd; } #endif @@ -2118,8 +2141,9 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, ram_flags |= RAM_SHARED; } if (ram_flags & RAM_SHARED) { - const char *name = memory_region_name(mr); - int fd = qemu_ram_get_shared_fd(name, errp); + bool reused; + g_autofree char *name = cpr_name(mr); + int fd = qemu_ram_get_shared_fd(name, &reused, errp); if (fd < 0) { return NULL; @@ -2133,9 +2157,14 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, * fd is not supported, but previous QEMU versions that called * qemu_anon_ram_alloc for anonymous shared memory could have * succeeded. Quietly fail and fall back. + * + * After cpr-transfer, new QEMU could create a memory region + * with a larger max size than old, so pass reused to grow the + * region if necessary. The extra space will be usable after a + * guest reset. */ new_block = qemu_ram_alloc_from_fd(size, max_size, resized, mr, - ram_flags, fd, 0, false, NULL); + ram_flags, fd, 0, reused, NULL); if (new_block) { trace_qemu_ram_alloc_shared(name, new_block->used_length, new_block->max_length, fd, @@ -2143,6 +2172,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, return new_block; } + cpr_delete_fd(name, 0); close(fd); /* fall back to anon allocation */ } @@ -2221,6 +2251,8 @@ static void reclaim_ramblock(RAMBlock *block) void qemu_ram_free(RAMBlock *block) { + g_autofree char *name = NULL; + if (!block) { return; } @@ -2231,6 +2263,8 @@ void qemu_ram_free(RAMBlock *block) } qemu_mutex_lock_ramlist(); + name = cpr_name(block->mr); + cpr_delete_fd(name, 0); QLIST_REMOVE_RCU(block, next); ram_list.mru_block = NULL; /* Write list before version */ From 28eaa5957d3e4123e34a913991fea1a5ece41f32 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:35 -0800 Subject: [PATCH 1357/2892] hostmem-memfd: preserve for cpr Preserve memory-backend-memfd memory objects during cpr-transfer. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-10-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- backends/hostmem-memfd.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 1672da9e30..85daa1432c 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -17,6 +17,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "qom/object.h" +#include "migration/cpr.h" OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendMemfd, MEMORY_BACKEND_MEMFD) @@ -33,15 +34,19 @@ static bool memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend); - g_autofree char *name = NULL; + g_autofree char *name = host_memory_backend_get_name(backend); + int fd = cpr_find_fd(name, 0); uint32_t ram_flags; - int fd; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); return false; } + if (fd >= 0) { + goto have_fd; + } + fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, m->hugetlb, m->hugetlbsize, m->seal ? F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0, @@ -49,9 +54,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) if (fd == -1) { return false; } + cpr_save_fd(name, 0, fd); +have_fd: backend->aligned = true; - name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : RAM_PRIVATE; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; From 2ef121688f407ff0fa513d317d4841fb180d6942 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:36 -0800 Subject: [PATCH 1358/2892] hostmem-shm: preserve for cpr Preserve memory-backend-shm memory objects during cpr-transfer. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-11-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- backends/hostmem-shm.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c index fabee41f2c..f67ad2740b 100644 --- a/backends/hostmem-shm.c +++ b/backends/hostmem-shm.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "system/hostmem.h" #include "qapi/error.h" +#include "migration/cpr.h" #define TYPE_MEMORY_BACKEND_SHM "memory-backend-shm" @@ -25,9 +26,9 @@ struct HostMemoryBackendShm { static bool shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { - g_autofree char *backend_name = NULL; + g_autofree char *backend_name = host_memory_backend_get_name(backend); uint32_t ram_flags; - int fd; + int fd = cpr_find_fd(backend_name, 0); if (!backend->size) { error_setg(errp, "can't create shm backend with size 0"); @@ -39,13 +40,18 @@ shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return false; } + if (fd >= 0) { + goto have_fd; + } + fd = qemu_shm_alloc(backend->size, errp); if (fd < 0) { return false; } + cpr_save_fd(backend_name, 0, fd); +have_fd: /* Let's do the same as memory-backend-ram,share=on would do. */ - backend_name = host_memory_backend_get_name(backend); ram_flags = RAM_SHARED; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; From f2374f0fc3180a449857e6e56f20add3e8d2cca8 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:37 -0800 Subject: [PATCH 1359/2892] migration: enhance migrate_uri_parse Export migrate_uri_parse for use outside migration internals, and define a method migrate_is_uri that indicates when migrate_uri_parse should be used. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-12-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- include/migration/misc.h | 7 +++++++ migration/migration.c | 11 +++++++++++ migration/migration.h | 2 -- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index 67f7ef7a0e..c660be8095 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -108,4 +108,11 @@ bool migration_in_bg_snapshot(void); bool migration_block_activate(Error **errp); bool migration_block_inactivate(void); +/* True if @uri starts with a syntactically valid URI prefix */ +bool migrate_is_uri(const char *uri); + +/* Parse @uri and return @channel, returning true on success */ +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp); + #endif diff --git a/migration/migration.c b/migration/migration.c index fce7b22ae8..b5ee98e691 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -14,6 +14,7 @@ */ #include "qemu/osdep.h" +#include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -587,6 +588,16 @@ void migrate_add_address(SocketAddress *address) QAPI_CLONE(SocketAddress, address)); } +bool migrate_is_uri(const char *uri) +{ + while (*uri && *uri != ':') { + if (!qemu_isalpha(*uri++)) { + return false; + } + } + return *uri == ':'; +} + bool migrate_uri_parse(const char *uri, MigrationChannel **channel, Error **errp) { diff --git a/migration/migration.h b/migration/migration.h index 0df2a187af..1d4d4e910d 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -519,8 +519,6 @@ bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList *bbm, Error **errp); void migrate_add_address(SocketAddress *address); -bool migrate_uri_parse(const char *uri, MigrationChannel **channel, - Error **errp); int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); #define qemu_ram_foreach_block \ From 2862b6b92467241e3ed378876cc8dbf2e6a8ea6a Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:38 -0800 Subject: [PATCH 1360/2892] migration: incoming channel Extend the -incoming option to allow an @MigrationChannel to be specified. This allows channels other than 'main' to be described on the command line, which will be needed for CPR. Signed-off-by: Steve Sistare Acked-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-13-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 21 ++++++++++++++++----- qemu-options.hx | 21 +++++++++++++++++++++ system/vl.c | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index b5ee98e691..5f2540fac3 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -695,7 +695,8 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, if (channels) { /* To verify that Migrate channel list has only item */ if (channels->next) { - error_setg(errp, "Channel list has more than one entries"); + error_setg(errp, "Channel list must have only one entry, " + "for type 'main'"); return; } addr = channels->value->addr; @@ -2054,6 +2055,7 @@ void qmp_migrate(const char *uri, bool has_channels, MigrationState *s = migrate_get_current(); g_autoptr(MigrationChannel) channel = NULL; MigrationAddress *addr = NULL; + MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL }; /* * Having preliminary checks for uri and channel @@ -2064,12 +2066,21 @@ void qmp_migrate(const char *uri, bool has_channels, } if (channels) { - /* To verify that Migrate channel list has only item */ - if (channels->next) { - error_setg(errp, "Channel list has more than one entries"); + for ( ; channels; channels = channels->next) { + MigrationChannelType type = channels->value->channel_type; + + if (channelv[type]) { + error_setg(errp, "Channel list has more than one %s entry", + MigrationChannelType_str(type)); + return; + } + channelv[type] = channels->value; + } + addr = channelv[MIGRATION_CHANNEL_TYPE_MAIN]->addr; + if (!addr) { + error_setg(errp, "Channel list has no main entry"); return; } - addr = channels->value->addr; } if (uri) { diff --git a/qemu-options.hx b/qemu-options.hx index 90fad31590..3d1af7325b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4940,10 +4940,18 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ "-incoming exec:cmdline\n" \ " accept incoming migration on given file descriptor\n" \ " or from given external command\n" \ + "-incoming \n" \ + " accept incoming migration on the migration channel\n" \ "-incoming defer\n" \ " wait for the URI to be specified via migrate_incoming\n", QEMU_ARCH_ALL) SRST +The -incoming option specifies the migration channel for an incoming +migration. It may be used multiple times to specify multiple +migration channel types. The channel type is specified in , +or is 'main' for all other forms of -incoming. If multiple -incoming +options are specified for a channel type, the last one takes precedence. + ``-incoming tcp:[host]:port[,to=maxport][,ipv4=on|off][,ipv6=on|off]`` \ ``-incoming rdma:host:port[,ipv4=on|off][,ipv6=on|off]`` @@ -4963,6 +4971,19 @@ SRST Accept incoming migration as an output from specified external command. +``-incoming `` + Accept incoming migration on the migration channel. For the syntax + of , see the QAPI documentation of ``MigrationChannel``. + Examples: + :: + + -incoming '{"channel-type": "main", + "addr": { "transport": "socket", + "type": "unix", + "path": "my.sock" }}' + + -incoming main,addr.transport=socket,addr.type=unix,addr.path=my.sock + ``-incoming defer`` Wait for the URI to be specified via migrate\_incoming. The monitor can be used to change settings (such as migration parameters) prior diff --git a/system/vl.c b/system/vl.c index c567826718..504f05b954 100644 --- a/system/vl.c +++ b/system/vl.c @@ -123,6 +123,7 @@ #include "qapi/qapi-visit-block-core.h" #include "qapi/qapi-visit-compat.h" #include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-visit-migration.h" #include "qapi/qapi-visit-ui.h" #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-commands-migration.h" @@ -159,6 +160,8 @@ typedef struct DeviceOption { static const char *cpu_option; static const char *mem_path; static const char *incoming; +static const char *incoming_str[MIGRATION_CHANNEL_TYPE__MAX]; +static MigrationChannel *incoming_channels[MIGRATION_CHANNEL_TYPE__MAX]; static const char *loadvm; static const char *accelerators; static bool have_custom_ram_size; @@ -1813,6 +1816,30 @@ static void object_option_add_visitor(Visitor *v) QTAILQ_INSERT_TAIL(&object_opts, opt, next); } +static void incoming_option_parse(const char *str) +{ + MigrationChannelType type = MIGRATION_CHANNEL_TYPE_MAIN; + MigrationChannel *channel; + Visitor *v; + + if (!strcmp(str, "defer")) { + channel = NULL; + } else if (migrate_is_uri(str)) { + migrate_uri_parse(str, &channel, &error_fatal); + } else { + v = qobject_input_visitor_new_str(str, "channel-type", &error_fatal); + visit_type_MigrationChannel(v, NULL, &channel, &error_fatal); + visit_free(v); + type = channel->channel_type; + } + + /* New incoming spec replaces the previous */ + qapi_free_MigrationChannel(incoming_channels[type]); + incoming_channels[type] = channel; + incoming_str[type] = str; + incoming = incoming_str[MIGRATION_CHANNEL_TYPE_MAIN]; +} + static void object_option_parse(const char *str) { QemuOpts *opts; @@ -2738,8 +2765,11 @@ void qmp_x_exit_preconfig(Error **errp) if (incoming) { Error *local_err = NULL; if (strcmp(incoming, "defer") != 0) { - qmp_migrate_incoming(incoming, false, NULL, true, true, - &local_err); + g_autofree MigrationChannelList *channels = + g_new0(MigrationChannelList, 1); + + channels->value = incoming_channels[MIGRATION_CHANNEL_TYPE_MAIN]; + qmp_migrate_incoming(NULL, true, channels, true, true, &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); @@ -3458,7 +3488,7 @@ void qemu_init(int argc, char **argv) if (!incoming) { runstate_set(RUN_STATE_INMIGRATE); } - incoming = optarg; + incoming_option_parse(optarg); break; case QEMU_OPTION_only_migratable: only_migratable = 1; From b5779dc7cfbb53583da362da5906733f6cba4c3f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:39 -0800 Subject: [PATCH 1361/2892] migration: SCM_RIGHTS for QEMUFile Define functions to put/get file descriptors to/from a QEMUFile, for qio channels that support SCM_RIGHTS. Maintain ordering such that put(A), put(fd), put(B) followed by get(A), get(fd), get(B) always succeeds. Other get orderings may succeed but are not guaranteed. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-14-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- migration/qemu-file.c | 84 ++++++++++++++++++++++++++++++++++++++++-- migration/qemu-file.h | 2 + migration/trace-events | 2 + 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index b6d2f588bd..1303a5bf58 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -37,6 +37,11 @@ #define IO_BUF_SIZE 32768 #define MAX_IOV_SIZE MIN_CONST(IOV_MAX, 64) +typedef struct FdEntry { + QTAILQ_ENTRY(FdEntry) entry; + int fd; +} FdEntry; + struct QEMUFile { QIOChannel *ioc; bool is_writable; @@ -51,6 +56,9 @@ struct QEMUFile { int last_error; Error *last_error_obj; + + bool can_pass_fd; + QTAILQ_HEAD(, FdEntry) fds; }; /* @@ -109,6 +117,8 @@ static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable) object_ref(ioc); f->ioc = ioc; f->is_writable = is_writable; + f->can_pass_fd = qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS); + QTAILQ_INIT(&f->fds); return f; } @@ -310,6 +320,10 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) int len; int pending; Error *local_error = NULL; + g_autofree int *fds = NULL; + size_t nfd = 0; + int **pfds = f->can_pass_fd ? &fds : NULL; + size_t *pnfd = f->can_pass_fd ? &nfd : NULL; assert(!qemu_file_is_writable(f)); @@ -325,10 +339,9 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) } do { - len = qio_channel_read(f->ioc, - (char *)f->buf + pending, - IO_BUF_SIZE - pending, - &local_error); + struct iovec iov = { f->buf + pending, IO_BUF_SIZE - pending }; + len = qio_channel_readv_full(f->ioc, &iov, 1, pfds, pnfd, 0, + &local_error); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(f->ioc, G_IO_IN); @@ -348,9 +361,66 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) qemu_file_set_error_obj(f, len, local_error); } + for (int i = 0; i < nfd; i++) { + FdEntry *fde = g_new0(FdEntry, 1); + fde->fd = fds[i]; + QTAILQ_INSERT_TAIL(&f->fds, fde, entry); + } + return len; } +int qemu_file_put_fd(QEMUFile *f, int fd) +{ + int ret = 0; + QIOChannel *ioc = qemu_file_get_ioc(f); + Error *err = NULL; + struct iovec iov = { (void *)" ", 1 }; + + /* + * Send a dummy byte so qemu_fill_buffer on the receiving side does not + * fail with a len=0 error. Flush first to maintain ordering wrt other + * data. + */ + + qemu_fflush(f); + if (qio_channel_writev_full(ioc, &iov, 1, &fd, 1, 0, &err) < 1) { + error_report_err(error_copy(err)); + qemu_file_set_error_obj(f, -EIO, err); + ret = -1; + } + trace_qemu_file_put_fd(f->ioc->name, fd, ret); + return ret; +} + +int qemu_file_get_fd(QEMUFile *f) +{ + int fd = -1; + FdEntry *fde; + + if (!f->can_pass_fd) { + Error *err = NULL; + error_setg(&err, "%s does not support fd passing", f->ioc->name); + error_report_err(error_copy(err)); + qemu_file_set_error_obj(f, -EIO, err); + goto out; + } + + /* Force the dummy byte and its fd passenger to appear. */ + qemu_peek_byte(f, 0); + + fde = QTAILQ_FIRST(&f->fds); + if (fde) { + qemu_get_byte(f); /* Drop the dummy byte */ + fd = fde->fd; + QTAILQ_REMOVE(&f->fds, fde, entry); + g_free(fde); + } +out: + trace_qemu_file_get_fd(f->ioc->name, fd); + return fd; +} + /** Closes the file * * Returns negative error value if any error happened on previous operations or @@ -361,11 +431,17 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) */ int qemu_fclose(QEMUFile *f) { + FdEntry *fde, *next; int ret = qemu_fflush(f); int ret2 = qio_channel_close(f->ioc, NULL); if (ret >= 0) { ret = ret2; } + QTAILQ_FOREACH_SAFE(fde, &f->fds, entry, next) { + warn_report("qemu_fclose: received fd %d was never claimed", fde->fd); + close(fde->fd); + g_free(fde); + } g_clear_pointer(&f->ioc, object_unref); error_free(f->last_error_obj); g_free(f); diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 11c2120edd..3e47a20621 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -79,5 +79,7 @@ size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos); QIOChannel *qemu_file_get_ioc(QEMUFile *file); +int qemu_file_put_fd(QEMUFile *f, int fd); +int qemu_file_get_fd(QEMUFile *f); #endif diff --git a/migration/trace-events b/migration/trace-events index 4e3061bc55..abd9cdf2a1 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -88,6 +88,8 @@ put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" # qemu-file.c qemu_file_fclose(void) "" +qemu_file_put_fd(const char *name, int fd, int ret) "ioc %s, fd %d -> status %d" +qemu_file_get_fd(const char *name, int fd) "ioc %s -> fd %d" # ram.c get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" From e3965dc3527c1d03c332d3c10d40d45a1f0f8e14 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:40 -0800 Subject: [PATCH 1362/2892] migration: VMSTATE_FD Define VMSTATE_FD for declaring a file descriptor field in a VMStateDescription. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-15-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- include/migration/vmstate.h | 9 +++++++++ migration/vmstate-types.c | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index f313f2f408..a1dfab4460 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -230,6 +230,7 @@ extern const VMStateInfo vmstate_info_uint8; extern const VMStateInfo vmstate_info_uint16; extern const VMStateInfo vmstate_info_uint32; extern const VMStateInfo vmstate_info_uint64; +extern const VMStateInfo vmstate_info_fd; /** Put this in the stream when migrating a null pointer.*/ #define VMS_NULLPTR_MARKER (0x30U) /* '0' */ @@ -902,6 +903,9 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_UINT64_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint64, uint64_t) +#define VMSTATE_FD_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, vmstate_info_fd, int32_t) + #ifdef CONFIG_LINUX #define VMSTATE_U8_V(_f, _s, _v) \ @@ -936,6 +940,9 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_UINT64(_f, _s) \ VMSTATE_UINT64_V(_f, _s, 0) +#define VMSTATE_FD(_f, _s) \ + VMSTATE_FD_V(_f, _s, 0) + #ifdef CONFIG_LINUX #define VMSTATE_U8(_f, _s) \ @@ -1009,6 +1016,8 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_UINT64_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint64, uint64_t) +#define VMSTATE_FD_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_fd, int32_t) #define VMSTATE_TIMER_PTR_TEST(_f, _s, _test) \ VMSTATE_POINTER_TEST(_f, _s, _test, vmstate_info_timer, QEMUTimer *) diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index d70d573dbd..0319c3568b 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -314,6 +314,29 @@ const VMStateInfo vmstate_info_uint64 = { .put = put_uint64, }; +/* File descriptor communicated via SCM_RIGHTS */ + +static int get_fd(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + int32_t *v = pv; + *v = qemu_file_get_fd(f); + return 0; +} + +static int put_fd(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + int32_t *v = pv; + return qemu_file_put_fd(f, *v); +} + +const VMStateInfo vmstate_info_fd = { + .name = "fd", + .get = get_fd, + .put = put_fd, +}; + static int get_nullptr(QEMUFile *f, void *pv, size_t size, const VMStateField *field) From b3698869f41afe076a37e4cb0477ac2f45d8110f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:41 -0800 Subject: [PATCH 1363/2892] migration: cpr-transfer save and load Add functions to create a QEMUFile based on a unix URI, for saving or loading, for use by cpr-transfer mode to preserve CPR state. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-16-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- include/migration/cpr.h | 3 ++ migration/cpr-transfer.c | 71 ++++++++++++++++++++++++++++++++++++++++ migration/meson.build | 1 + migration/trace-events | 2 ++ 4 files changed, 77 insertions(+) create mode 100644 migration/cpr-transfer.c diff --git a/include/migration/cpr.h b/include/migration/cpr.h index d9364f7d1f..c669b8b8a3 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -22,4 +22,7 @@ int cpr_state_load(MigrationChannel *channel, Error **errp); void cpr_state_close(void); struct QIOChannel *cpr_state_ioc(void); +QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp); +QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp); + #endif diff --git a/migration/cpr-transfer.c b/migration/cpr-transfer.c new file mode 100644 index 0000000000..e1f140359c --- /dev/null +++ b/migration/cpr-transfer.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022, 2024 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "io/channel-file.h" +#include "io/channel-socket.h" +#include "io/net-listener.h" +#include "migration/cpr.h" +#include "migration/migration.h" +#include "migration/savevm.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" +#include "trace.h" + +QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp) +{ + MigrationAddress *addr = channel->addr; + + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET && + addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX) { + + g_autoptr(QIOChannelSocket) sioc = qio_channel_socket_new(); + QIOChannel *ioc = QIO_CHANNEL(sioc); + SocketAddress *saddr = &addr->u.socket; + + if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) { + return NULL; + } + trace_cpr_transfer_output(addr->u.socket.u.q_unix.path); + qio_channel_set_name(ioc, "cpr-out"); + return qemu_file_new_output(ioc); + + } else { + error_setg(errp, "bad cpr channel address; must be unix"); + return NULL; + } +} + +QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp) +{ + MigrationAddress *addr = channel->addr; + + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET && + addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX) { + + g_autoptr(QIOChannelSocket) sioc = NULL; + SocketAddress *saddr = &addr->u.socket; + g_autoptr(QIONetListener) listener = qio_net_listener_new(); + QIOChannel *ioc; + + qio_net_listener_set_name(listener, "cpr-socket-listener"); + if (qio_net_listener_open_sync(listener, saddr, 1, errp) < 0) { + return NULL; + } + + sioc = qio_net_listener_wait_client(listener); + ioc = QIO_CHANNEL(sioc); + trace_cpr_transfer_input(addr->u.socket.u.q_unix.path); + qio_channel_set_name(ioc, "cpr-in"); + return qemu_file_new_input(ioc); + + } else { + error_setg(errp, "bad cpr channel socket type; must be unix"); + return NULL; + } +} diff --git a/migration/meson.build b/migration/meson.build index 1eb8c96d23..d3bfe84d62 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -15,6 +15,7 @@ system_ss.add(files( 'channel.c', 'channel-block.c', 'cpr.c', + 'cpr-transfer.c', 'cpu-throttle.c', 'dirtyrate.c', 'exec.c', diff --git a/migration/trace-events b/migration/trace-events index abd9cdf2a1..e03a914afb 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -350,6 +350,8 @@ cpr_delete_fd(const char *name, int id) "%s, id %d" cpr_find_fd(const char *name, int id, int fd) "%s, id %d returns %d" cpr_state_save(const char *mode) "%s mode" cpr_state_load(const char *mode) "%s mode" +cpr_transfer_input(const char *path) "%s" +cpr_transfer_output(const char *path) "%s" # block-dirty-bitmap.c send_bitmap_header_enter(void) "" From 624e6e654e110d04efd22fe7bb494010c12765f0 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:42 -0800 Subject: [PATCH 1364/2892] migration: cpr-transfer mode Add the cpr-transfer migration mode, which allows the user to transfer a guest to a new QEMU instance on the same host with minimal guest pause time, by preserving guest RAM in place, albeit with new virtual addresses in new QEMU, and by preserving device file descriptors. Pages that were locked in memory for DMA in old QEMU remain locked in new QEMU, because the descriptor of the device that locked them remains open. cpr-transfer preserves memory and devices descriptors by sending them to new QEMU over a unix domain socket using SCM_RIGHTS. Such CPR state cannot be sent over the normal migration channel, because devices and backends are created prior to reading the channel, so this mode sends CPR state over a second "cpr" migration channel. New QEMU reads the cpr channel prior to creating devices or backends. The user specifies the cpr channel in the channel arguments on the outgoing side, and in a second -incoming command-line parameter on the incoming side. The user must start old QEMU with the the '-machine aux-ram-share=on' option, which allows anonymous memory to be transferred in place to the new process by transferring a memory descriptor for each ram block. Memory-backend objects must have the share=on attribute, but memory-backend-epc is not supported. The user starts new QEMU on the same host as old QEMU, with command-line arguments to create the same machine, plus the -incoming option for the main migration channel, like normal live migration. In addition, the user adds a second -incoming option with channel type "cpr". This CPR channel must support file descriptor transfer with SCM_RIGHTS, i.e. it must be a UNIX domain socket. To initiate CPR, the user issues a migrate command to old QEMU, adding a second migration channel of type "cpr" in the channels argument. Old QEMU stops the VM, saves state to the migration channels, and enters the postmigrate state. New QEMU mmap's memory descriptors, and execution resumes. The implementation splits qmp_migrate into start and finish functions. Start sends CPR state to new QEMU, which responds by closing the CPR channel. Old QEMU detects the HUP then calls finish, which connects the main migration channel. In summary, the usage is: qemu-system-$arch -machine aux-ram-share=on ... start new QEMU with "-incoming -incoming " Issue commands to old QEMU: migrate_set_parameter mode cpr-transfer {"execute": "migrate", ... {"channel-type": "main"...}, {"channel-type": "cpr"...} ... } Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Acked-by: Markus Armbruster Link: https://lore.kernel.org/r/1736967650-129648-17-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- include/migration/cpr.h | 5 ++ migration/cpr.c | 36 +++++++++++-- migration/migration.c | 106 +++++++++++++++++++++++++++++++++++++- migration/migration.h | 2 + migration/options.c | 8 ++- migration/ram.c | 2 + migration/vmstate-types.c | 1 + qapi/migration.json | 44 +++++++++++++++- qemu-options.hx | 2 + stubs/vmstate.c | 7 +++ system/vl.c | 7 +++ 11 files changed, 210 insertions(+), 10 deletions(-) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index c669b8b8a3..3a6deb7933 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -10,6 +10,8 @@ #include "qapi/qapi-types-migration.h" +#define MIG_MODE_NONE -1 + #define QEMU_CPR_FILE_MAGIC 0x51435052 #define QEMU_CPR_FILE_VERSION 0x00000001 @@ -17,6 +19,9 @@ void cpr_save_fd(const char *name, int id, int fd); void cpr_delete_fd(const char *name, int id); int cpr_find_fd(const char *name, int id); +MigMode cpr_get_incoming_mode(void); +void cpr_set_incoming_mode(MigMode mode); + int cpr_state_save(MigrationChannel *channel, Error **errp); int cpr_state_load(MigrationChannel *channel, Error **errp); void cpr_state_close(void); diff --git a/migration/cpr.c b/migration/cpr.c index 87bcfdb5ff..584b0b98f7 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -45,7 +45,7 @@ static const VMStateDescription vmstate_cpr_fd = { VMSTATE_UINT32(namelen, CprFd), VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen), VMSTATE_INT32(id, CprFd), - VMSTATE_INT32(fd, CprFd), + VMSTATE_FD(fd, CprFd), VMSTATE_END_OF_LIST() } }; @@ -116,6 +116,18 @@ QIOChannel *cpr_state_ioc(void) return qemu_file_get_ioc(cpr_state_file); } +static MigMode incoming_mode = MIG_MODE_NONE; + +MigMode cpr_get_incoming_mode(void) +{ + return incoming_mode; +} + +void cpr_set_incoming_mode(MigMode mode) +{ + incoming_mode = mode; +} + int cpr_state_save(MigrationChannel *channel, Error **errp) { int ret; @@ -124,8 +136,14 @@ int cpr_state_save(MigrationChannel *channel, Error **errp) trace_cpr_state_save(MigMode_str(mode)); - /* set f based on mode in a later patch in this series */ - return 0; + if (mode == MIG_MODE_CPR_TRANSFER) { + f = cpr_transfer_output(channel, errp); + } else { + return 0; + } + if (!f) { + return -1; + } qemu_put_be32(f, QEMU_CPR_FILE_MAGIC); qemu_put_be32(f, QEMU_CPR_FILE_VERSION); @@ -155,8 +173,16 @@ int cpr_state_load(MigrationChannel *channel, Error **errp) QEMUFile *f; MigMode mode = 0; - /* set f and mode based on other parameters later in this patch series */ - return 0; + if (channel) { + mode = MIG_MODE_CPR_TRANSFER; + cpr_set_incoming_mode(mode); + f = cpr_transfer_input(channel, errp); + } else { + return 0; + } + if (!f) { + return -1; + } trace_cpr_state_load(MigMode_str(mode)); diff --git a/migration/migration.c b/migration/migration.c index 5f2540fac3..88b09914ec 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -77,6 +77,7 @@ static NotifierWithReturnList migration_state_notifiers[] = { NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL), NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT), + NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_TRANSFER), }; /* Messages sent on the return path from destination to source */ @@ -110,6 +111,7 @@ static int migration_maybe_pause(MigrationState *s, static void migrate_fd_cancel(MigrationState *s); static bool close_return_path_on_source(MigrationState *s); static void migration_completion_end(MigrationState *s); +static void migrate_hup_delete(MigrationState *s); static void migration_downtime_start(MigrationState *s) { @@ -220,6 +222,12 @@ migration_channels_and_transport_compatible(MigrationAddress *addr, return false; } + if (migrate_mode() == MIG_MODE_CPR_TRANSFER && + addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + error_setg(errp, "Migration requires streamable transport (eg unix)"); + return false; + } + return true; } @@ -435,6 +443,7 @@ void migration_incoming_state_destroy(void) mis->postcopy_qemufile_dst = NULL; } + cpr_set_incoming_mode(MIG_MODE_NONE); yank_unregister_instance(MIGRATION_YANK_INSTANCE); } @@ -747,6 +756,9 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, } else { error_setg(errp, "unknown migration protocol: %s", uri); } + + /* Close cpr socket to tell source that we are listening */ + cpr_state_close(); } static void process_incoming_migration_bh(void *opaque) @@ -1423,6 +1435,8 @@ static void migrate_fd_cleanup(MigrationState *s) s->vmdesc = NULL; qemu_savevm_state_cleanup(); + cpr_state_close(); + migrate_hup_delete(s); close_return_path_on_source(s); @@ -1534,6 +1548,7 @@ static void migrate_fd_error(MigrationState *s, const Error *error) static void migrate_fd_cancel(MigrationState *s) { int old_state ; + bool setup = (s->state == MIGRATION_STATUS_SETUP); trace_migrate_fd_cancel(); @@ -1568,6 +1583,17 @@ static void migrate_fd_cancel(MigrationState *s) } } } + + /* + * If qmp_migrate_finish has not been called, then there is no path that + * will complete the cancellation. Do it now. + */ + if (setup && !s->to_dst_file) { + migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING, + MIGRATION_STATUS_CANCELLED); + cpr_state_close(); + migrate_hup_delete(s); + } } void migration_add_notifier_mode(NotifierWithReturn *notify, @@ -1665,7 +1691,9 @@ bool migration_thread_is_self(void) bool migrate_mode_is_cpr(MigrationState *s) { - return s->parameters.mode == MIG_MODE_CPR_REBOOT; + MigMode mode = s->parameters.mode; + return mode == MIG_MODE_CPR_REBOOT || + mode == MIG_MODE_CPR_TRANSFER; } int migrate_init(MigrationState *s, Error **errp) @@ -2046,6 +2074,40 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp) return true; } +static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, + Error **errp); + +static void migrate_hup_add(MigrationState *s, QIOChannel *ioc, GSourceFunc cb, + void *opaque) +{ + s->hup_source = qio_channel_create_watch(ioc, G_IO_HUP); + g_source_set_callback(s->hup_source, cb, opaque, NULL); + g_source_attach(s->hup_source, NULL); +} + +static void migrate_hup_delete(MigrationState *s) +{ + if (s->hup_source) { + g_source_destroy(s->hup_source); + g_source_unref(s->hup_source); + s->hup_source = NULL; + } +} + +static gboolean qmp_migrate_finish_cb(QIOChannel *channel, + GIOCondition cond, + void *opaque) +{ + MigrationAddress *addr = opaque; + + qmp_migrate_finish(addr, false, NULL); + + cpr_state_close(); + migrate_hup_delete(migrate_get_current()); + qapi_free_MigrationAddress(addr); + return G_SOURCE_REMOVE; +} + void qmp_migrate(const char *uri, bool has_channels, MigrationChannelList *channels, bool has_detach, bool detach, bool has_resume, bool resume, Error **errp) @@ -2056,6 +2118,7 @@ void qmp_migrate(const char *uri, bool has_channels, g_autoptr(MigrationChannel) channel = NULL; MigrationAddress *addr = NULL; MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL }; + MigrationChannel *cpr_channel = NULL; /* * Having preliminary checks for uri and channel @@ -2076,6 +2139,7 @@ void qmp_migrate(const char *uri, bool has_channels, } channelv[type] = channels->value; } + cpr_channel = channelv[MIGRATION_CHANNEL_TYPE_CPR]; addr = channelv[MIGRATION_CHANNEL_TYPE_MAIN]->addr; if (!addr) { error_setg(errp, "Channel list has no main entry"); @@ -2096,12 +2160,52 @@ void qmp_migrate(const char *uri, bool has_channels, return; } + if (s->parameters.mode == MIG_MODE_CPR_TRANSFER && !cpr_channel) { + error_setg(errp, "missing 'cpr' migration channel"); + return; + } + resume_requested = has_resume && resume; if (!migrate_prepare(s, resume_requested, errp)) { /* Error detected, put into errp */ return; } + if (cpr_state_save(cpr_channel, &local_err)) { + goto out; + } + + /* + * For cpr-transfer, the target may not be listening yet on the migration + * channel, because first it must finish cpr_load_state. The target tells + * us it is listening by closing the cpr-state socket. Wait for that HUP + * event before connecting in qmp_migrate_finish. + * + * The HUP could occur because the target fails while reading CPR state, + * in which case the target will not listen for the incoming migration + * connection, so qmp_migrate_finish will fail to connect, and then recover. + */ + if (s->parameters.mode == MIG_MODE_CPR_TRANSFER) { + migrate_hup_add(s, cpr_state_ioc(), (GSourceFunc)qmp_migrate_finish_cb, + QAPI_CLONE(MigrationAddress, addr)); + + } else { + qmp_migrate_finish(addr, resume_requested, errp); + } + +out: + if (local_err) { + migrate_fd_error(s, local_err); + error_propagate(errp, local_err); + } +} + +static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, + Error **errp) +{ + MigrationState *s = migrate_get_current(); + Error *local_err = NULL; + if (!resume_requested) { if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) { return; diff --git a/migration/migration.h b/migration/migration.h index 1d4d4e910d..fb1b8f99d3 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -468,6 +468,8 @@ struct MigrationState { bool switchover_acked; /* Is this a rdma migration */ bool rdma_migration; + + GSource *hup_source; }; void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, diff --git a/migration/options.c b/migration/options.c index b8d5300326..1ad950e397 100644 --- a/migration/options.c +++ b/migration/options.c @@ -22,6 +22,7 @@ #include "qapi/qmp/qnull.h" #include "system/runstate.h" #include "migration/colo.h" +#include "migration/cpr.h" #include "migration/misc.h" #include "migration.h" #include "migration-stats.h" @@ -745,8 +746,11 @@ uint64_t migrate_max_postcopy_bandwidth(void) MigMode migrate_mode(void) { - MigrationState *s = migrate_get_current(); - MigMode mode = s->parameters.mode; + MigMode mode = cpr_get_incoming_mode(); + + if (mode == MIG_MODE_NONE) { + mode = migrate_get_current()->parameters.mode; + } assert(mode >= 0 && mode < MIG_MODE__MAX); return mode; diff --git a/migration/ram.c b/migration/ram.c index ce28328141..5aace00bf1 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -195,7 +195,9 @@ static bool postcopy_preempt_active(void) bool migrate_ram_is_ignored(RAMBlock *block) { + MigMode mode = migrate_mode(); return !qemu_ram_is_migratable(block) || + mode == MIG_MODE_CPR_TRANSFER || (migrate_ignore_shared() && qemu_ram_is_shared(block) && qemu_ram_is_named_file(block)); } diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index 0319c3568b..741a588b7e 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -15,6 +15,7 @@ #include "qemu-file.h" #include "migration.h" #include "migration/vmstate.h" +#include "migration/client-options.h" #include "qemu/error-report.h" #include "qemu/queue.h" #include "trace.h" diff --git a/qapi/migration.json b/qapi/migration.json index a605dc26db..4679ce9f2a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -614,9 +614,48 @@ # or COLO. # # (since 8.2) +# +# @cpr-transfer: This mode allows the user to transfer a guest to a +# new QEMU instance on the same host with minimal guest pause +# time by preserving guest RAM in place. Devices and their pinned +# pages will also be preserved in a future QEMU release. +# +# The user starts new QEMU on the same host as old QEMU, with +# command-line arguments to create the same machine, plus the +# -incoming option for the main migration channel, like normal +# live migration. In addition, the user adds a second -incoming +# option with channel type "cpr". This CPR channel must support +# file descriptor transfer with SCM_RIGHTS, i.e. it must be a +# UNIX domain socket. +# +# To initiate CPR, the user issues a migrate command to old QEMU, +# adding a second migration channel of type "cpr" in the channels +# argument. Old QEMU stops the VM, saves state to the migration +# channels, and enters the postmigrate state. Execution resumes +# in new QEMU. +# +# New QEMU reads the CPR channel before opening a monitor, hence +# the CPR channel cannot be specified in the list of channels for +# a migrate-incoming command. It may only be specified on the +# command line. +# +# The main channel address cannot be a file type, and for an +# inet socket, the port cannot be 0 (meaning dynamically choose +# a port). +# +# Memory-backend objects must have the share=on attribute, but +# memory-backend-epc is not supported. The VM must be started +# with the '-machine aux-ram-share=on' option. +# +# When using -incoming defer, you must issue the migrate command +# to old QEMU before issuing any monitor commands to new QEMU. +# However, new QEMU does not open and read the migration stream +# until you issue the migrate incoming command. +# +# (since 10.0) ## { 'enum': 'MigMode', - 'data': [ 'normal', 'cpr-reboot' ] } + 'data': [ 'normal', 'cpr-reboot', 'cpr-transfer' ] } ## # @ZeroPageDetection: @@ -1578,11 +1617,12 @@ # The migration channel-type request options. # # @main: Main outbound migration channel. +# @cpr: Checkpoint and restart state channel. # # Since: 8.1 ## { 'enum': 'MigrationChannelType', - 'data': [ 'main' ] } + 'data': [ 'main', 'cpr' ] } ## # @MigrationChannel: diff --git a/qemu-options.hx b/qemu-options.hx index 3d1af7325b..d19bf533d6 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -112,6 +112,8 @@ SRST specified on the command line, or implicitly created by the -m command line option. The default is off. + To use the cpr-transfer migration mode, you must set aux-ram-share=on. + ``memory-backend='id'`` An alternative to legacy ``-mem-path`` and ``mem-prealloc`` options. Allows to use a memory backend as main RAM. diff --git a/stubs/vmstate.c b/stubs/vmstate.c index 8513d9204e..c190762d7c 100644 --- a/stubs/vmstate.c +++ b/stubs/vmstate.c @@ -1,5 +1,7 @@ #include "qemu/osdep.h" #include "migration/vmstate.h" +#include "qapi/qapi-types-migration.h" +#include "migration/client-options.h" int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, @@ -21,3 +23,8 @@ bool vmstate_check_only_migratable(const VMStateDescription *vmsd) { return true; } + +MigMode migrate_mode(void) +{ + return MIG_MODE_NORMAL; +} diff --git a/system/vl.c b/system/vl.c index 504f05b954..db8e604eba 100644 --- a/system/vl.c +++ b/system/vl.c @@ -77,6 +77,7 @@ #include "hw/block/block.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" +#include "migration/cpr.h" #include "migration/misc.h" #include "migration/snapshot.h" #include "system/tpm.h" @@ -3706,6 +3707,12 @@ void qemu_init(int argc, char **argv) qemu_create_machine(machine_opts_dict); + /* + * Load incoming CPR state before any devices are created, because it + * contains file descriptors that are needed in device initialization code. + */ + cpr_state_load(incoming_channels[MIGRATION_CHANNEL_TYPE_CPR], &error_fatal); + suspend_mux_open(); qemu_disable_default_devices(); From fdbfbfc75e9e5ee76693f32d7e67cf04ff804413 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:43 -0800 Subject: [PATCH 1365/2892] migration-test: memory_backend Allow each migration test to define its own memory backend, replacing the standard "-m " specification. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/1736967650-129648-18-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 15 +++++++++++---- tests/qtest/migration/framework.h | 5 +++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 4550cda129..758e14abab 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -221,6 +221,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, g_autofree char *machine = NULL; const char *bootpath; g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + g_autofree char *memory_backend = NULL; if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { @@ -296,6 +297,12 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, memory_size, shmem_path); } + if (args->memory_backend) { + memory_backend = g_strdup_printf(args->memory_backend, memory_size); + } else { + memory_backend = g_strdup_printf("-m %s ", memory_size); + } + if (args->use_dirty_ring) { kvm_opts = ",dirty-ring-size=4096"; } @@ -314,12 +321,12 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " "-machine %s,%s " "-name source,debug-threads=on " - "-m %s " + "%s " "-serial file:%s/src_serial " "%s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, - memory_size, tmpfs, + memory_backend, tmpfs, arch_opts ? arch_opts : "", shmem_opts ? shmem_opts : "", args->opts_source ? args->opts_source : "", @@ -335,13 +342,13 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " "-machine %s,%s " "-name target,debug-threads=on " - "-m %s " + "%s " "-serial file:%s/dest_serial " "-incoming %s " "%s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, - memory_size, tmpfs, uri, + memory_backend, tmpfs, uri, arch_opts ? arch_opts : "", shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 7991ee56b6..dd2db1c000 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -111,6 +111,11 @@ typedef struct { bool suspend_me; /* enable OOB QMP capability */ bool oob; + /* + * Format string for the main memory backend, containing one %s where the + * size is plugged in. If omitted, "-m %s" is used. + */ + const char *memory_backend; } MigrateStart; typedef enum PostcopyRecoveryFailStage { From 903a65120d347d7584726c01072b7e82826459c1 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:44 -0800 Subject: [PATCH 1366/2892] tests/qtest: optimize migrate_set_ports Do not query connection parameters if all port numbers are known. This is more efficient, and also solves a problem for the cpr-transfer test. At the point where cpr-transfer calls migrate_qmp and migrate_set_ports, the monitor is not connected and queries are not allowed. Port=0 is never used for cpr-transfer. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-19-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/migration-util.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 526bed74ea..0ce1413b6c 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -135,25 +135,32 @@ migrate_get_connect_qdict(QTestState *who) void migrate_set_ports(QTestState *to, QList *channel_list) { - QDict *addr; + g_autoptr(QDict) addr = NULL; QListEntry *entry; const char *addr_port = NULL; - addr = migrate_get_connect_qdict(to); - QLIST_FOREACH_ENTRY(channel_list, entry) { QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); QDict *addrdict = qdict_get_qdict(channel, "addr"); - if (qdict_haskey(addrdict, "port") && - qdict_haskey(addr, "port") && - (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { + if (!qdict_haskey(addrdict, "port") || + strcmp(qdict_get_str(addrdict, "port"), "0")) { + continue; + } + + /* + * Fetch addr only if needed, so tests that are not yet connected to + * the monitor do not query it. Such tests cannot use port=0. + */ + if (!addr) { + addr = migrate_get_connect_qdict(to); + } + + if (qdict_haskey(addr, "port")) { addr_port = qdict_get_str(addr, "port"); qdict_put_str(addrdict, "port", addr_port); } } - - qobject_unref(addr); } bool migrate_watch_for_events(QTestState *who, const char *name, From 5357ef823a1f53673eae3e0a9a0d626bc104f8a7 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:45 -0800 Subject: [PATCH 1367/2892] tests/qtest: defer connection Add an option to defer making the connecting to the monitor and qtest sockets when calling qtest_init_with_env. The client makes the connection later by calling qtest_connect and qtest_qmp_handshake. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-20-git-send-email-steven.sistare@oracle.com [plumb capabilities list into qtest_qmp_handshake] Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 101 +++++++++++++++++++----------- tests/qtest/libqtest.h | 24 ++++++- tests/qtest/migration/framework.c | 7 ++- 3 files changed, 91 insertions(+), 41 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index a1e105f27f..fbb51e3e55 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -75,6 +75,8 @@ struct QTestState { int fd; int qmp_fd; + int sock; + int qmpsock; pid_t qemu_pid; /* our child QEMU process */ int wstatus; #ifdef _WIN32 @@ -458,18 +460,19 @@ static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, return s; } +static char *qtest_socket_path(const char *suffix) +{ + return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix); +} + static QTestState *qtest_init_internal(const char *qemu_bin, - const char *extra_args) + const char *extra_args, + bool do_connect) { QTestState *s; int sock, qmpsock, i; - gchar *socket_path; - gchar *qmp_socket_path; - - socket_path = g_strdup_printf("%s/qtest-%d.sock", - g_get_tmp_dir(), getpid()); - qmp_socket_path = g_strdup_printf("%s/qtest-%d.qmp", - g_get_tmp_dir(), getpid()); + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); /* * It's possible that if an earlier test run crashed it might @@ -501,22 +504,19 @@ static QTestState *qtest_init_internal(const char *qemu_bin, qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); qtest_client_set_tx_handler(s, qtest_client_socket_send); - s->fd = socket_accept(sock); - if (s->fd >= 0) { - s->qmp_fd = socket_accept(qmpsock); - } - unlink(socket_path); - unlink(qmp_socket_path); - g_free(socket_path); - g_free(qmp_socket_path); - - g_assert(s->fd >= 0 && s->qmp_fd >= 0); - s->rx = g_string_new(""); for (i = 0; i < MAX_IRQ; i++) { s->irq_level[i] = false; } + s->fd = -1; + s->qmp_fd = -1; + s->sock = sock; + s->qmpsock = qmpsock; + if (do_connect) { + qtest_connect(s); + } + /* * Stopping QEMU for debugging is not supported on Windows. * @@ -531,28 +531,38 @@ static QTestState *qtest_init_internal(const char *qemu_bin, } #endif - /* ask endianness of the target */ - - s->big_endian = qtest_query_target_endianness(s); - return s; } +void qtest_connect(QTestState *s) +{ + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); + + g_assert(s->sock >= 0 && s->qmpsock >= 0); + s->fd = socket_accept(s->sock); + if (s->fd >= 0) { + s->qmp_fd = socket_accept(s->qmpsock); + } + unlink(socket_path); + unlink(qmp_socket_path); + g_assert(s->fd >= 0 && s->qmp_fd >= 0); + s->sock = s->qmpsock = -1; + /* ask endianness of the target */ + s->big_endian = qtest_query_target_endianness(s); +} + QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { - return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); + return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true); } -QTestState *qtest_init_with_env_and_capabilities(const char *var, - const char *extra_args, - QList *capabilities) +void qtest_qmp_handshake(QTestState *s, QList *capabilities) { - QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); - QDict *greeting; - /* Read the QMP greeting and then do the handshake */ - greeting = qtest_qmp_receive(s); + QDict *greeting = qtest_qmp_receive(s); qobject_unref(greeting); + if (capabilities) { qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities', " @@ -561,18 +571,37 @@ QTestState *qtest_init_with_env_and_capabilities(const char *var, } else { qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }"); } +} +QTestState *qtest_init_with_env_and_capabilities(const char *var, + const char *extra_args, + QList *capabilities, + bool do_connect) +{ + QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args, + do_connect); + + if (do_connect) { + qtest_qmp_handshake(s, capabilities); + } else { + /* + * If the connection is delayed, the capabilities must be set + * at that moment. + */ + assert(!capabilities); + } return s; } -QTestState *qtest_init_with_env(const char *var, const char *extra_args) +QTestState *qtest_init_with_env(const char *var, const char *extra_args, + bool do_connect) { - return qtest_init_with_env_and_capabilities(var, extra_args, NULL); + return qtest_init_with_env_and_capabilities(var, extra_args, NULL, true); } QTestState *qtest_init(const char *extra_args) { - return qtest_init_with_env(NULL, extra_args); + return qtest_init_with_env(NULL, extra_args, true); } QTestState *qtest_vinitf(const char *fmt, va_list ap) @@ -1580,7 +1609,7 @@ static struct MachInfo *qtest_get_machines(const char *var) silence_spawn_log = !g_test_verbose(); - qts = qtest_init_with_env(qemu_var, "-machine none"); + qts = qtest_init_with_env(qemu_var, "-machine none", true); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); @@ -1635,7 +1664,7 @@ static struct CpuModel *qtest_get_cpu_models(void) silence_spawn_log = !g_test_verbose(); - qts = qtest_init_with_env(NULL, "-machine none"); + qts = qtest_init_with_env(NULL, "-machine none", true); response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }"); g_assert(response); list = qdict_get_qlist(response, "return"); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index ce88d23eae..29f123e281 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -61,13 +61,15 @@ QTestState *qtest_init(const char *extra_args); * @var: Environment variable from where to take the QEMU binary * @extra_args: Other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. + * @do_connect: connect to qemu monitor and qtest socket. * * Like qtest_init(), but use a different environment variable for the * QEMU binary. * * Returns: #QTestState instance. */ -QTestState *qtest_init_with_env(const char *var, const char *extra_args); +QTestState *qtest_init_with_env(const char *var, const char *extra_args, + bool do_connect); /** * qtest_init_with_env_and_capabilities: @@ -75,6 +77,7 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args); * @extra_args: Other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. * @capabilities: list of QMP capabilities (strings) to enable + * @do_connect: connect to qemu monitor and qtest socket. * * Like qtest_init_with_env(), but enable specified capabilities during * hadshake. @@ -83,7 +86,8 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args); */ QTestState *qtest_init_with_env_and_capabilities(const char *var, const char *extra_args, - QList *capabilities); + QList *capabilities, + bool do_connect); /** * qtest_init_without_qmp_handshake: @@ -94,6 +98,22 @@ QTestState *qtest_init_with_env_and_capabilities(const char *var, */ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); +/** + * qtest_connect + * @s: #QTestState instance to connect + * Connect to qemu monitor and qtest socket, after skipping them in + * qtest_init_with_env. Does not handshake with the monitor. + */ +void qtest_connect(QTestState *s); + +/** + * qtest_qmp_handshake: + * @s: #QTestState instance to operate on. + * @capabilities: list of QMP capabilities (strings) to enable + * Perform handshake after connecting to qemu monitor. + */ +void qtest_qmp_handshake(QTestState *s, QList *capabilities); + /** * qtest_init_with_serial: * @extra_args: other arguments to pass to QEMU. CAUTION: these diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 758e14abab..f7add75ed5 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -196,9 +196,10 @@ static void cleanup(const char *filename) static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) { - QList *capabilities = qlist_new(); + QList *capabilities = NULL; if (args->oob) { + capabilities = qlist_new(); qlist_append_str(capabilities, "oob"); } return capabilities; @@ -333,7 +334,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, ignore_stderr); if (!args->only_target) { *from = qtest_init_with_env_and_capabilities(QEMU_ENV_SRC, cmd_source, - capabilities); + capabilities, true); qtest_qmp_set_event_callback(*from, migrate_watch_for_events, &src_state); @@ -354,7 +355,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, args->opts_target ? args->opts_target : "", ignore_stderr); *to = qtest_init_with_env_and_capabilities(QEMU_ENV_DST, cmd_target, - capabilities); + capabilities, true); qtest_qmp_set_event_callback(*to, migrate_watch_for_events, &dst_state); From f5bac78cd84a8a928ac5f92e4c00772decc30bab Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:46 -0800 Subject: [PATCH 1368/2892] migration-test: defer connection Add an option to defer connection to the target monitor, needed by the cpr-transfer test. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/1736967650-129648-21-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 22 +++++++++++++++++++--- tests/qtest/migration/framework.h | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index f7add75ed5..2611c31c1b 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -223,6 +223,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, const char *bootpath; g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); g_autofree char *memory_backend = NULL; + const char *events; if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { @@ -340,22 +341,30 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, &src_state); } + /* + * If the monitor connection is deferred, enable events on the command line + * so none are missed. This is for testing only, do not set migration + * options like this in general. + */ + events = args->defer_target_connect ? "-global migration.x-events=on" : ""; + cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " "-machine %s,%s " "-name target,debug-threads=on " "%s " "-serial file:%s/dest_serial " "-incoming %s " - "%s %s %s %s", + "%s %s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, memory_backend, tmpfs, uri, + events, arch_opts ? arch_opts : "", shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); *to = qtest_init_with_env_and_capabilities(QEMU_ENV_DST, cmd_target, - capabilities, true); + capabilities, !args->defer_target_connect); qtest_qmp_set_event_callback(*to, migrate_watch_for_events, &dst_state); @@ -373,7 +382,9 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, * to mimic as closer as that. */ migrate_set_capability(*from, "events", true); - migrate_set_capability(*to, "events", true); + if (!args->defer_target_connect) { + migrate_set_capability(*to, "events", true); + } return 0; } @@ -733,6 +744,11 @@ void test_precopy_common(MigrateCommon *args) migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); + if (args->start.defer_target_connect) { + qtest_connect(to); + qtest_qmp_handshake(to, NULL); + } + if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; wait_for_migration_fail(from, allow_active); diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index dd2db1c000..32f3a93632 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -116,6 +116,9 @@ typedef struct { * size is plugged in. If omitted, "-m %s" is used. */ const char *memory_backend; + + /* Do not connect to target monitor and qtest sockets in qtest_init */ + bool defer_target_connect; } MigrateStart; typedef enum PostcopyRecoveryFailStage { From 43ca9d1866b89803490fb69caf5e1d728a112612 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:47 -0800 Subject: [PATCH 1369/2892] tests/qtest: enhance migration channels Change the migrate_qmp and migrate_qmp_fail channels argument to a QObject type so the caller can manipulate the object before passing it to the helper. Define migrate_str_to_channel to aid such manipulation. Add a channels argument to migrate_incoming_qmp. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-22-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/migration/framework.c | 15 ++++++-- tests/qtest/migration/migration-qmp.c | 53 ++++++++++++++++++++++----- tests/qtest/migration/migration-qmp.h | 10 +++-- tests/qtest/migration/misc-tests.c | 9 ++++- tests/qtest/migration/precopy-tests.c | 6 +-- tests/qtest/virtio-net-failover.c | 8 ++-- 6 files changed, 76 insertions(+), 25 deletions(-) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 2611c31c1b..1228bd5bca 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -18,6 +18,8 @@ #include "migration/migration-qmp.h" #include "migration/migration-util.h" #include "ppc-util.h" +#include "qapi/error.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" #include "qemu/module.h" #include "qemu/option.h" @@ -705,6 +707,7 @@ void test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; + QObject *out_channels = NULL; if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -737,12 +740,16 @@ void test_precopy_common(MigrateCommon *args) } } + if (args->connect_channels) { + out_channels = qobject_from_json(args->connect_channels, &error_abort); + } + if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + migrate_qmp_fail(from, args->connect_uri, out_channels, "{}"); goto finish; } - migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); + migrate_qmp(from, to, args->connect_uri, out_channels, "{}"); if (args->start.defer_target_connect) { qtest_connect(to); @@ -892,7 +899,7 @@ void test_file_common(MigrateCommon *args, bool stop_src) * We need to wait for the source to finish before starting the * destination. */ - migrate_incoming_qmp(to, args->connect_uri, "{}"); + migrate_incoming_qmp(to, args->connect_uri, NULL, "{}"); wait_for_migration_complete(to); if (stop_src) { @@ -928,7 +935,7 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); return NULL; } diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 9431d2beda..5610f6d15d 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -15,9 +15,13 @@ #include "migration-qmp.h" #include "migration-util.h" #include "qapi/error.h" +#include "qapi/qapi-types-migration.h" +#include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" /* * Number of seconds we wait when looking for migration @@ -47,8 +51,33 @@ void migration_event_wait(QTestState *s, const char *target) } while (!found); } +/* + * Convert a string representing a single channel to an object. + * @str may be in JSON or dotted keys format. + */ +QObject *migrate_str_to_channel(const char *str) +{ + Visitor *v; + MigrationChannel *channel; + QObject *obj; + + /* Create the channel */ + v = qobject_input_visitor_new_str(str, "channel-type", &error_abort); + visit_type_MigrationChannel(v, NULL, &channel, &error_abort); + visit_free(v); + + /* Create the object */ + v = qobject_output_visitor_new(&obj); + visit_type_MigrationChannel(v, NULL, &channel, &error_abort); + visit_complete(v, &obj); + visit_free(v); + + qapi_free_MigrationChannel(channel); + return obj; +} + void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...) + QObject *channels, const char *fmt, ...) { va_list ap; QDict *args, *err; @@ -64,8 +93,7 @@ void migrate_qmp_fail(QTestState *who, const char *uri, g_assert(!qdict_haskey(args, "channels")); if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - qdict_put_obj(args, "channels", channels_obj); + qdict_put_obj(args, "channels", channels); } err = qtest_qmp_assert_failure_ref( @@ -82,7 +110,7 @@ void migrate_qmp_fail(QTestState *who, const char *uri, * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. */ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...) + QObject *channels, const char *fmt, ...) { va_list ap; QDict *args; @@ -102,10 +130,9 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, g_assert(!qdict_haskey(args, "channels")); if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - QList *channel_list = qobject_to(QList, channels_obj); + QList *channel_list = qobject_to(QList, channels); migrate_set_ports(to, channel_list); - qdict_put_obj(args, "channels", channels_obj); + qdict_put_obj(args, "channels", channels); } qtest_qmp_assert_success(who, @@ -123,7 +150,8 @@ void migrate_set_capability(QTestState *who, const char *capability, capability, value); } -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) +void migrate_incoming_qmp(QTestState *to, const char *uri, QObject *channels, + const char *fmt, ...) { va_list ap; QDict *args, *rsp; @@ -133,7 +161,14 @@ void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) va_end(ap); g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + qdict_put_obj(args, "channels", channels); + } /* This function relies on the event to work, make sure it's enabled */ migrate_set_capability(to, "events", true); diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index caaa78722a..faa8181d91 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -4,17 +4,19 @@ #include "migration-util.h" +QObject *migrate_str_to_channel(const char *str); + G_GNUC_PRINTF(4, 5) void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...); + QObject *channels, const char *fmt, ...); G_GNUC_PRINTF(5, 6) void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...); + QObject *channels, const char *fmt, ...); -G_GNUC_PRINTF(3, 4) +G_GNUC_PRINTF(4, 5) void migrate_incoming_qmp(QTestState *who, const char *uri, - const char *fmt, ...); + QObject *channels, const char *fmt, ...); void migration_event_wait(QTestState *s, const char *target); void migrate_set_capability(QTestState *who, const char *capability, diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index 6173430748..dda3707cf3 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -11,6 +11,8 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qmp/qjson.h" #include "libqtest.h" #include "migration/framework.h" #include "migration/migration-qmp.h" @@ -205,6 +207,7 @@ static void test_validate_uuid_dst_not_set(void) static void do_test_validate_uri_channel(MigrateCommon *args) { QTestState *from, *to; + QObject *channels; if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -217,7 +220,11 @@ static void do_test_validate_uri_channel(MigrateCommon *args) * 'uri' and 'channels' validation is checked even before the migration * starts. */ - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + channels = args->connect_channels ? + qobject_from_json(args->connect_channels, &error_abort) : + NULL; + migrate_qmp_fail(from, args->connect_uri, channels, "{}"); + migrate_end(from, to, false); } diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 23599b29ee..436dbd98e8 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -152,7 +152,7 @@ static void *migrate_hook_start_fd(QTestState *from, close(pair[0]); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "fd:fd-mig", "{}"); + migrate_incoming_qmp(to, "fd:fd-mig", NULL, "{}"); /* Send the 2nd socket to the target */ qtest_qmp_fds_assert_success(from, &pair[1], 1, @@ -479,7 +479,7 @@ static void test_multifd_tcp_cancel(void) migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -518,7 +518,7 @@ static void test_multifd_tcp_cancel(void) migrate_set_capability(to2, "multifd", true); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", NULL, "{}"); migrate_ensure_non_converge(from); diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 08365ffa11..f04573f98c 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -773,7 +773,7 @@ static void test_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); @@ -895,7 +895,7 @@ static void test_off_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); @@ -1022,7 +1022,7 @@ static void test_guest_off_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); @@ -1747,7 +1747,7 @@ static void test_multi_in(gconstpointer opaque) check_one_card(qts, true, "standby1", MAC_STANDBY1); check_one_card(qts, false, "primary1", MAC_PRIMARY1); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); From e6c18b996e6c3a41c9a028f325d293d6740e52b2 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:48 -0800 Subject: [PATCH 1370/2892] tests/qtest: assert qmp connected Assert that qmp_fd is valid when we communicate with the monitor. Suggested-by: Peter Xu Signed-off-by: Steve Sistare Link: https://lore.kernel.org/r/1736967650-129648-23-git-send-email-steven.sistare@oracle.com Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index fbb51e3e55..437b24fa2e 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -811,6 +811,7 @@ QDict *qtest_qmp_receive(QTestState *s) QDict *qtest_qmp_receive_dict(QTestState *s) { + g_assert(s->qmp_fd >= 0); return qmp_fd_receive(s->qmp_fd); } @@ -838,12 +839,14 @@ int qtest_socket_server(const char *socket_path) void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { + g_assert(s->qmp_fd >= 0); qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap); } #endif void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) { + g_assert(s->qmp_fd >= 0); qmp_fd_vsend(s->qmp_fd, fmt, ap); } @@ -904,6 +907,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) { va_list ap; + g_assert(s->qmp_fd >= 0); va_start(ap, fmt); qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); va_end(ap); From 360b5d773be1fcc25579abce0f5d2b6e1922327a Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:49 -0800 Subject: [PATCH 1371/2892] migration-test: cpr-transfer Add a migration test for cpr-transfer mode. Defer the connection to the target monitor, else the test hangs because in cpr-transfer mode QEMU does not listen for monitor connections until we send the migrate command to source QEMU. To test -incoming defer, send a migrate incoming command to the target, after sending the migrate command to the source, as required by cpr-transfer mode. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-24-git-send-email-steven.sistare@oracle.com [only allocate in_channels when needed] Signed-off-by: Fabiano Rosas --- tests/qtest/migration/cpr-tests.c | 62 +++++++++++++++++++++++++++++++ tests/qtest/migration/framework.c | 23 ++++++++++++ tests/qtest/migration/framework.h | 3 ++ 3 files changed, 88 insertions(+) diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 44ce89aa5b..215b0df8c0 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -44,6 +44,62 @@ static void test_mode_reboot(void) test_file_common(&args, true); } +static void *test_mode_transfer_start(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-transfer"); + return NULL; +} + +/* + * cpr-transfer mode cannot use the target monitor prior to starting the + * migration, and cannot connect synchronously to the monitor, so defer + * the target connection. + */ +static void test_mode_transfer_common(bool incoming_defer) +{ + g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs); + g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s", mig_path); + + const char *opts = "-machine aux-ram-share=on -nodefaults"; + g_autofree const char *cpr_channel = g_strdup_printf( + "cpr,addr.transport=socket,addr.type=unix,addr.path=%s", + cpr_path); + g_autofree char *opts_target = g_strdup_printf("-incoming %s %s", + cpr_channel, opts); + + g_autofree char *connect_channels = g_strdup_printf( + "[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'unix'," + " 'path': '%s' } } ]", + mig_path); + + MigrateCommon args = { + .start.opts_source = opts, + .start.opts_target = opts_target, + .start.defer_target_connect = true, + .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s" + " -machine memory-backend=pc.ram", + .listen_uri = incoming_defer ? "defer" : uri, + .connect_channels = connect_channels, + .cpr_channel = cpr_channel, + .start_hook = test_mode_transfer_start, + }; + + test_precopy_common(&args); +} + +static void test_mode_transfer(void) +{ + test_mode_transfer_common(NULL); +} + +static void test_mode_transfer_defer(void) +{ + test_mode_transfer_common(true); +} + void migration_test_add_cpr(MigrationTestEnv *env) { tmpfs = env->tmpfs; @@ -55,4 +111,10 @@ void migration_test_add_cpr(MigrationTestEnv *env) if (getenv("QEMU_TEST_FLAKY_TESTS")) { migration_test_add("/migration/mode/reboot", test_mode_reboot); } + + if (env->has_kvm) { + migration_test_add("/migration/mode/transfer", test_mode_transfer); + migration_test_add("/migration/mode/transfer/defer", + test_mode_transfer_defer); + } } diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 1228bd5bca..de65bfe40d 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -420,6 +420,7 @@ void migrate_end(QTestState *from, QTestState *to, bool test_dest) qtest_quit(to); cleanup("migsocket"); + cleanup("cpr.sock"); cleanup("src_serial"); cleanup("dest_serial"); cleanup(FILE_TEST_FILENAME); @@ -707,8 +708,11 @@ void test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; + QObject *in_channels = NULL; QObject *out_channels = NULL; + g_assert(!args->cpr_channel || args->connect_channels); + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -740,8 +744,24 @@ void test_precopy_common(MigrateCommon *args) } } + /* + * The cpr channel must be included in outgoing channels, but not in + * migrate-incoming channels. + */ if (args->connect_channels) { + if (args->start.defer_target_connect && + !strcmp(args->listen_uri, "defer")) { + in_channels = qobject_from_json(args->connect_channels, + &error_abort); + } out_channels = qobject_from_json(args->connect_channels, &error_abort); + + if (args->cpr_channel) { + QList *channels_list = qobject_to(QList, out_channels); + QObject *obj = migrate_str_to_channel(args->cpr_channel); + + qlist_append(channels_list, obj); + } } if (args->result == MIG_TEST_QMP_ERROR) { @@ -754,6 +774,9 @@ void test_precopy_common(MigrateCommon *args) if (args->start.defer_target_connect) { qtest_connect(to); qtest_qmp_handshake(to, NULL); + if (!strcmp(args->listen_uri, "defer")) { + migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}"); + } } if (args->result != MIG_TEST_SUCCEED) { diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 32f3a93632..cb4a984700 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -154,6 +154,9 @@ typedef struct { */ const char *connect_channels; + /* Optional: the cpr migration channel, in JSON or dotted keys format */ + const char *cpr_channel; + /* Optional: callback to run at start to set migration parameters */ TestMigrateStartHook start_hook; /* Optional: callback to run at finish to cleanup */ From 45c3d6cfbbe1336176a719a9487289ecd951fb99 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 15 Jan 2025 11:00:50 -0800 Subject: [PATCH 1372/2892] migration: cpr-transfer documentation Add documentation for the cpr-transfer migration mode. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/1736967650-129648-25-git-send-email-steven.sistare@oracle.com [add -machine memory-backend=ram0] Signed-off-by: Fabiano Rosas --- docs/devel/migration/CPR.rst | 184 ++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst index 63c36470cf..7897873c86 100644 --- a/docs/devel/migration/CPR.rst +++ b/docs/devel/migration/CPR.rst @@ -5,7 +5,7 @@ CPR is the umbrella name for a set of migration modes in which the VM is migrated to a new QEMU instance on the same host. It is intended for use when the goal is to update host software components that run the VM, such as QEMU or even the host kernel. At this time, -cpr-reboot is the only available mode. +the cpr-reboot and cpr-transfer modes are available. Because QEMU is restarted on the same host, with access to the same local devices, CPR is allowed in certain cases where normal migration @@ -53,7 +53,7 @@ RAM is copied to the migration URI. Outgoing: * Set the migration mode parameter to ``cpr-reboot``. * Set the ``x-ignore-shared`` capability if desired. - * Issue the ``migrate`` command. It is recommended the the URI be a + * Issue the ``migrate`` command. It is recommended the URI be a ``file`` type, but one can use other types such as ``exec``, provided the command captures all the data from the outgoing side, and provides all the data to the incoming side. @@ -145,3 +145,183 @@ Caveats cpr-reboot mode may not be used with postcopy, background-snapshot, or COLO. + +cpr-transfer mode +----------------- + +This mode allows the user to transfer a guest to a new QEMU instance +on the same host with minimal guest pause time, by preserving guest +RAM in place, albeit with new virtual addresses in new QEMU. Devices +and their pinned memory pages will also be preserved in a future QEMU +release. + +The user starts new QEMU on the same host as old QEMU, with command- +line arguments to create the same machine, plus the ``-incoming`` +option for the main migration channel, like normal live migration. +In addition, the user adds a second -incoming option with channel +type ``cpr``. This CPR channel must support file descriptor transfer +with SCM_RIGHTS, i.e. it must be a UNIX domain socket. + +To initiate CPR, the user issues a migrate command to old QEMU, +adding a second migration channel of type ``cpr`` in the channels +argument. Old QEMU stops the VM, saves state to the migration +channels, and enters the postmigrate state. Execution resumes in +new QEMU. + +New QEMU reads the CPR channel before opening a monitor, hence +the CPR channel cannot be specified in the list of channels for a +migrate-incoming command. It may only be specified on the command +line. + +Usage +^^^^^ + +Memory backend objects must have the ``share=on`` attribute. + +The VM must be started with the ``-machine aux-ram-share=on`` +option. This causes implicit RAM blocks (those not described by +a memory-backend object) to be allocated by mmap'ing a memfd. +Examples include VGA and ROM. + +Outgoing: + * Set the migration mode parameter to ``cpr-transfer``. + * Issue the ``migrate`` command, containing a main channel and + a cpr channel. + +Incoming: + * Start new QEMU with two ``-incoming`` options. + * If the VM was running when the outgoing ``migrate`` command was + issued, then QEMU automatically resumes VM execution. + +Caveats +^^^^^^^ + +cpr-transfer mode may not be used with postcopy, background-snapshot, +or COLO. + +memory-backend-epc is not supported. + +The main incoming migration channel address cannot be a file type. + +If the main incoming channel address is an inet socket, then the port +cannot be 0 (meaning dynamically choose a port). + +When using ``-incoming defer``, you must issue the migrate command to +old QEMU before issuing any monitor commands to new QEMU, because new +QEMU blocks waiting to read from the cpr channel before starting its +monitor, and old QEMU does not write to the channel until the migrate +command is issued. However, new QEMU does not open and read the +main migration channel until you issue the migrate incoming command. + +Example 1: incoming channel +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In these examples, we simply restart the same version of QEMU, but +in a real scenario one would start new QEMU on the incoming side. +Note that new QEMU does not print the monitor prompt until old QEMU +has issued the migrate command. The outgoing side uses QMP because +HMP cannot specify a CPR channel. Some QMP responses are omitted for +brevity. + +:: + + Outgoing: Incoming: + + # qemu-kvm -qmp stdio + -object memory-backend-file,id=ram0,size=4G, + mem-path=/dev/shm/ram0,share=on -m 4G + -machine memory-backend=ram0 + -machine aux-ram-share=on + ... + # qemu-kvm -monitor stdio + -incoming tcp:0:44444 + -incoming '{"channel-type": "cpr", + "addr": { "transport": "socket", + "type": "unix", "path": "cpr.sock"}}' + ... + {"execute":"qmp_capabilities"} + + {"execute": "query-status"} + {"return": {"status": "running", + "running": true}} + + {"execute":"migrate-set-parameters", + "arguments":{"mode":"cpr-transfer"}} + + {"execute": "migrate", "arguments": { "channels": [ + {"channel-type": "main", + "addr": { "transport": "socket", "type": "inet", + "host": "0", "port": "44444" }}, + {"channel-type": "cpr", + "addr": { "transport": "socket", "type": "unix", + "path": "cpr.sock" }}]}} + + QEMU 10.0.50 monitor + (qemu) info status + VM status: running + + {"execute": "query-status"} + {"return": {"status": "postmigrate", + "running": false}} + +Example 2: incoming defer +^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example uses ``-incoming defer`` to hot plug a device before +accepting the main migration channel. Again note you must issue the +migrate command to old QEMU before you can issue any monitor +commands to new QEMU. + + +:: + + Outgoing: Incoming: + + # qemu-kvm -monitor stdio + -object memory-backend-file,id=ram0,size=4G, + mem-path=/dev/shm/ram0,share=on -m 4G + -machine memory-backend=ram0 + -machine aux-ram-share=on + ... + # qemu-kvm -monitor stdio + -incoming defer + -incoming '{"channel-type": "cpr", + "addr": { "transport": "socket", + "type": "unix", "path": "cpr.sock"}}' + ... + {"execute":"qmp_capabilities"} + + {"execute": "device_add", + "arguments": {"driver": "pcie-root-port"}} + + {"execute":"migrate-set-parameters", + "arguments":{"mode":"cpr-transfer"}} + + {"execute": "migrate", "arguments": { "channels": [ + {"channel-type": "main", + "addr": { "transport": "socket", "type": "inet", + "host": "0", "port": "44444" }}, + {"channel-type": "cpr", + "addr": { "transport": "socket", "type": "unix", + "path": "cpr.sock" }}]}} + + QEMU 10.0.50 monitor + (qemu) info status + VM status: paused (inmigrate) + (qemu) device_add pcie-root-port + (qemu) migrate_incoming tcp:0:44444 + (qemu) info status + VM status: running + + {"execute": "query-status"} + {"return": {"status": "postmigrate", + "running": false}} + +Futures +^^^^^^^ + +cpr-transfer mode is based on a capability to transfer open file +descriptors from old to new QEMU. In the future, descriptors for +vfio, iommufd, vhost, and char devices could be transferred, +preserving those devices and their kernel state without interruption, +even if they do not explicitly support live migration. From 013c6e1f423c8c3b25e244eaabfbd38d865c6841 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:31 -0500 Subject: [PATCH 1373/2892] migration: Remove postcopy implications in should_send_vmdesc() should_send_vmdesc() has a hack inside (which was not reflected in the function name) in that it tries to detect global postcopy state and that will affect the value to be returned. It's easier to keep the helper simple by only check the suppress-vmdesc property. Then: - On the sender side of its usage, there's already in_postcopy variable that we can use: postcopy doesn't send vmdesc at all, so directly skip everything for postcopy. - On the recv side, when reaching vmdesc processing it must be precopy code already, hence that hack check never used to work anyway. No functional change intended, except a trivial side effect that QEMU source will start to avoid running some JSON helper in postcopy path, but that would only reduce the postcopy blackout window a bit, rather than any other bad side effect. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-2-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/savevm.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 6e56d4cf1d..b8859d367f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1427,8 +1427,8 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) static bool should_send_vmdesc(void) { MachineState *machine = MACHINE(qdev_get_machine()); - bool in_postcopy = migration_in_postcopy(); - return !machine->suppress_vmdesc && !in_postcopy; + + return !machine->suppress_vmdesc; } /* @@ -1563,16 +1563,16 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, if (!in_postcopy) { /* Postcopy stream will still be going */ qemu_put_byte(f, QEMU_VM_EOF); - } - json_writer_end_array(vmdesc); - json_writer_end_object(vmdesc); - vmdesc_len = strlen(json_writer_get(vmdesc)); + json_writer_end_array(vmdesc); + json_writer_end_object(vmdesc); + vmdesc_len = strlen(json_writer_get(vmdesc)); - if (should_send_vmdesc()) { - qemu_put_byte(f, QEMU_VM_VMDESCRIPTION); - qemu_put_be32(f, vmdesc_len); - qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len); + if (should_send_vmdesc()) { + qemu_put_byte(f, QEMU_VM_VMDESCRIPTION); + qemu_put_be32(f, vmdesc_len); + qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len); + } } /* Free it now to detect any inconsistencies. */ @@ -2965,6 +2965,7 @@ int qemu_loadvm_state(QEMUFile *f) return ret; } + /* When reaching here, it must be precopy */ if (ret == 0) { ret = qemu_file_get_error(f); } From a55090db2ac16aa2ee9d25940e11ca5467af661b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:32 -0500 Subject: [PATCH 1374/2892] migration: Do not construct JSON description if suppressed QEMU machine has a property "suppress-vmdesc". When it is enabled, QEMU will stop attaching JSON VM description at the end of the precopy migration stream (postcopy is never affected because postcopy never attach that). However even if it's suppressed by the user, the source QEMU will still construct the JSON descriptions, which is a complete waste of CPU and memory resources. To avoid it, only create the JSON writer object if suppress-vmdesc is not specified. Luckily, vmstate_save() already supports vmdesc==NULL, so only a few spots that are left to be prepared that vmdesc can be NULL now. When at it, move the init / destroy of the JSON writer object to start / end of the migration - the JSON writer object is a sub-struct of migration state, and that looks like the only object that was dynamically allocated / destroyed within migration process. Make it the same as the rest objects that migration uses. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-3-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 9 +++++--- migration/migration.h | 1 + migration/savevm.c | 49 +++++++++++++++++++++++-------------------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 88b09914ec..5c335cc30b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1431,8 +1431,8 @@ static void migrate_fd_cleanup(MigrationState *s) g_free(s->hostname); s->hostname = NULL; - json_writer_free(s->vmdesc); - s->vmdesc = NULL; + + g_clear_pointer(&s->vmdesc, json_writer_free); qemu_savevm_state_cleanup(); cpr_state_close(); @@ -1722,7 +1722,10 @@ int migrate_init(MigrationState *s, Error **errp) s->migration_thread_running = false; error_free(s->error); s->error = NULL; - s->vmdesc = NULL; + + if (should_send_vmdesc()) { + s->vmdesc = json_writer_new(false); + } migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); diff --git a/migration/migration.h b/migration/migration.h index fb1b8f99d3..4c1fafc2b5 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -552,6 +552,7 @@ void migration_bitmap_sync_precopy(bool last_stage); /* migration/block-dirty-bitmap.c */ void dirty_bitmap_mig_init(void); +bool should_send_vmdesc(void); /* migration/block-active.c */ void migration_block_active_setup(bool active); diff --git a/migration/savevm.c b/migration/savevm.c index b8859d367f..cfe9dfaf5c 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1231,8 +1231,7 @@ void qemu_savevm_non_migratable_list(strList **reasons) void qemu_savevm_state_header(QEMUFile *f) { MigrationState *s = migrate_get_current(); - - s->vmdesc = json_writer_new(false); + JSONWriter *vmdesc = s->vmdesc; trace_savevm_state_header(); qemu_put_be32(f, QEMU_VM_FILE_MAGIC); @@ -1241,16 +1240,21 @@ void qemu_savevm_state_header(QEMUFile *f) if (s->send_configuration) { qemu_put_byte(f, QEMU_VM_CONFIGURATION); - /* - * This starts the main json object and is paired with the - * json_writer_end_object in - * qemu_savevm_state_complete_precopy_non_iterable - */ - json_writer_start_object(s->vmdesc, NULL); + if (vmdesc) { + /* + * This starts the main json object and is paired with the + * json_writer_end_object in + * qemu_savevm_state_complete_precopy_non_iterable + */ + json_writer_start_object(vmdesc, NULL); + json_writer_start_object(vmdesc, "configuration"); + } - json_writer_start_object(s->vmdesc, "configuration"); - vmstate_save_state(f, &vmstate_configuration, &savevm_state, s->vmdesc); - json_writer_end_object(s->vmdesc); + vmstate_save_state(f, &vmstate_configuration, &savevm_state, vmdesc); + + if (vmdesc) { + json_writer_end_object(vmdesc); + } } } @@ -1296,16 +1300,19 @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp) { ERRP_GUARD(); MigrationState *ms = migrate_get_current(); + JSONWriter *vmdesc = ms->vmdesc; SaveStateEntry *se; int ret = 0; - json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size()); - json_writer_start_array(ms->vmdesc, "devices"); + if (vmdesc) { + json_writer_int64(vmdesc, "page_size", qemu_target_page_size()); + json_writer_start_array(vmdesc, "devices"); + } trace_savevm_state_setup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->early_setup) { - ret = vmstate_save(f, se, ms->vmdesc, errp); + ret = vmstate_save(f, se, vmdesc, errp); if (ret) { migrate_set_error(ms, *errp); qemu_file_set_error(f, ret); @@ -1424,7 +1431,7 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) return all_finished; } -static bool should_send_vmdesc(void) +bool should_send_vmdesc(void) { MachineState *machine = MACHINE(qdev_get_machine()); @@ -1564,21 +1571,17 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, /* Postcopy stream will still be going */ qemu_put_byte(f, QEMU_VM_EOF); - json_writer_end_array(vmdesc); - json_writer_end_object(vmdesc); - vmdesc_len = strlen(json_writer_get(vmdesc)); + if (vmdesc) { + json_writer_end_array(vmdesc); + json_writer_end_object(vmdesc); + vmdesc_len = strlen(json_writer_get(vmdesc)); - if (should_send_vmdesc()) { qemu_put_byte(f, QEMU_VM_VMDESCRIPTION); qemu_put_be32(f, vmdesc_len); qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len); } } - /* Free it now to detect any inconsistencies. */ - json_writer_free(vmdesc); - ms->vmdesc = NULL; - trace_vmstate_downtime_checkpoint("src-non-iterable-saved"); return 0; From 9cde9b435a7d14ce39331935f4dfea4778400048 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:33 -0500 Subject: [PATCH 1375/2892] migration: Optimize postcopy on downtime by avoiding JSON writer postcopy_start() is the entry function that postcopy is destined to start. It also means QEMU source will not dump VM description, aka, the JSON writer is garbage now. We can leave that to be cleaned up when migration completes, however when with the JSON writer object being present, vmstate_save() will still try to construct the JSON objects for the VM descriptions, even though it'll never be used later if it's postcopy. To save those cycles, release the JSON writer earlier for postcopy. Then vmstate_save() later will be smart enough to skip the JSON object constructions completely. It can logically reduce downtime because all such JSON constructions happen during postcopy blackout. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-4-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 5c335cc30b..a9fe9c2821 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1422,6 +1422,11 @@ void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, } } +static void migration_cleanup_json_writer(MigrationState *s) +{ + g_clear_pointer(&s->vmdesc, json_writer_free); +} + static void migrate_fd_cleanup(MigrationState *s) { MigrationEventType type; @@ -1429,11 +1434,11 @@ static void migrate_fd_cleanup(MigrationState *s) trace_migrate_fd_cleanup(); + migration_cleanup_json_writer(s); + g_free(s->hostname); s->hostname = NULL; - g_clear_pointer(&s->vmdesc, json_writer_free); - qemu_savevm_state_cleanup(); cpr_state_close(); migrate_hup_delete(s); @@ -2628,6 +2633,14 @@ static int postcopy_start(MigrationState *ms, Error **errp) uint64_t bandwidth = migrate_max_postcopy_bandwidth(); int cur_state = MIGRATION_STATUS_ACTIVE; + /* + * Now we're 100% sure to switch to postcopy, so JSON writer won't be + * useful anymore. Free the resources early if it is there. Clearing + * the vmdesc also means any follow up vmstate_save()s will start to + * skip all JSON operations, which can shrink postcopy downtime. + */ + migration_cleanup_json_writer(ms); + if (migrate_postcopy_preempt()) { migration_wait_main_channel(ms); if (postcopy_preempt_establish_channel(ms)) { From 812145fcf7a6d290b69383dc5f25ed00360639d8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:34 -0500 Subject: [PATCH 1376/2892] migration: Avoid two src-downtime-end tracepoints for postcopy Postcopy can trigger this tracepoint twice, while only the 1st one is valid. Avoid triggering the 2nd tracepoint just like what we do with recording the total downtime. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-5-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index a9fe9c2821..07b6b730b7 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -129,9 +129,8 @@ static void migration_downtime_end(MigrationState *s) */ if (!s->downtime) { s->downtime = now - s->downtime_start; + trace_vmstate_downtime_checkpoint("src-downtime-end"); } - - trace_vmstate_downtime_checkpoint("src-downtime-end"); } static bool migration_needs_multiple_sockets(void) From 48221286937e6453524c6fe96dc6f74368c6fb1c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:35 -0500 Subject: [PATCH 1377/2892] migration: Drop inactivate_disk param in qemu_savevm_state_complete* This parameter is only used by one caller, which is the genuine precopy complete path (migration_completion_precopy). The parameter was introduced in a1fbe750fd ("migration: Fix race of image locking between src and dst") to make sure the inactivate will happen before EOF to make sure dest will always be able to activate the disk properly. However there's no limitation on how early we inactivate the disk. For precopy completion path, we can always do that as long as VM is stopped. Move the disk inactivate there, then we can remove this inactivate_disk parameter in the whole call stack, because all the rest users pass in false always. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-6-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 22 ++++++++++++++++------ migration/savevm.c | 27 +++++---------------------- migration/savevm.h | 5 ++--- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 07b6b730b7..d8a6bc12e0 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2682,7 +2682,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) * Cause any non-postcopiable, but iterative devices to * send out their final data. */ - qemu_savevm_state_complete_precopy(ms->to_dst_file, true, false); + qemu_savevm_state_complete_precopy(ms->to_dst_file, true); /* * in Finish migrate and with the io-lock held everything should @@ -2727,7 +2727,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) */ qemu_savevm_send_postcopy_listen(fb); - qemu_savevm_state_complete_precopy(fb, false, false); + qemu_savevm_state_complete_precopy(fb, false); if (migrate_postcopy_ram()) { qemu_savevm_send_ping(fb, 3); } @@ -2859,11 +2859,21 @@ static int migration_completion_precopy(MigrationState *s, goto out_unlock; } + /* Inactivate disks except in COLO */ + if (!migrate_colo()) { + /* + * Inactivate before sending QEMU_VM_EOF so that the + * bdrv_activate_all() on the other end won't fail. + */ + if (!migration_block_inactivate()) { + ret = -EFAULT; + goto out_unlock; + } + } + migration_rate_set(RATE_LIMIT_DISABLED); - /* Inactivate disks except in COLO */ - ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false, - !migrate_colo()); + ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false); out_unlock: bql_unlock(); return ret; @@ -3744,7 +3754,7 @@ static void *bg_migration_thread(void *opaque) * save their state to channel-buffer along with devices. */ cpu_synchronize_all_states(); - if (qemu_savevm_state_complete_precopy_non_iterable(fb, false, false)) { + if (qemu_savevm_state_complete_precopy_non_iterable(fb, false)) { goto fail; } /* diff --git a/migration/savevm.c b/migration/savevm.c index cfe9dfaf5c..5e56a5d9fc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1521,8 +1521,7 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) } int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, - bool in_postcopy, - bool inactivate_disks) + bool in_postcopy) { MigrationState *ms = migrate_get_current(); int64_t start_ts_each, end_ts_each; @@ -1553,20 +1552,6 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, end_ts_each - start_ts_each); } - if (inactivate_disks) { - /* - * Inactivate before sending QEMU_VM_EOF so that the - * bdrv_activate_all() on the other end won't fail. - */ - if (!migration_block_inactivate()) { - error_setg(&local_err, "%s: bdrv_inactivate_all() failed", - __func__); - migrate_set_error(ms, local_err); - error_report_err(local_err); - qemu_file_set_error(f, -EFAULT); - return -1; - } - } if (!in_postcopy) { /* Postcopy stream will still be going */ qemu_put_byte(f, QEMU_VM_EOF); @@ -1587,8 +1572,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, return 0; } -int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, - bool inactivate_disks) +int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) { int ret; Error *local_err = NULL; @@ -1613,8 +1597,7 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, goto flush; } - ret = qemu_savevm_state_complete_precopy_non_iterable(f, in_postcopy, - inactivate_disks); + ret = qemu_savevm_state_complete_precopy_non_iterable(f, in_postcopy); if (ret) { return ret; } @@ -1717,7 +1700,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) ret = qemu_file_get_error(f); if (ret == 0) { - qemu_savevm_state_complete_precopy(f, false, false); + qemu_savevm_state_complete_precopy(f, false); ret = qemu_file_get_error(f); } if (ret != 0) { @@ -1743,7 +1726,7 @@ cleanup: void qemu_savevm_live_state(QEMUFile *f) { /* save QEMU_VM_SECTION_END section */ - qemu_savevm_state_complete_precopy(f, true, false); + qemu_savevm_state_complete_precopy(f, true); qemu_put_byte(f, QEMU_VM_EOF); } diff --git a/migration/savevm.h b/migration/savevm.h index 9ec96a995c..c48a53e95e 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -39,8 +39,7 @@ void qemu_savevm_state_header(QEMUFile *f); int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); void qemu_savevm_state_cleanup(void); void qemu_savevm_state_complete_postcopy(QEMUFile *f); -int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, - bool inactivate_disks); +int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only); void qemu_savevm_state_pending_exact(uint64_t *must_precopy, uint64_t *can_postcopy); void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, @@ -68,6 +67,6 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); int qemu_load_device_state(QEMUFile *f); int qemu_loadvm_approve_switchover(void); int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, - bool in_postcopy, bool inactivate_disks); + bool in_postcopy); #endif From 89011a702f29545a0eac74328cd3c2974ecf2699 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:36 -0500 Subject: [PATCH 1378/2892] migration: Synchronize all CPU states only for non-iterable dump Do one shot cpu sync at qemu_savevm_state_complete_precopy_non_iterable(), instead of coding it separately in two places. Note that in the context of qemu_savevm_state_complete_precopy(), this patch is also an optimization for postcopy path, in that we can avoid sync cpu twice during switchover: before this patch, postcopy_start() invokes twice on qemu_savevm_state_complete_precopy(), each of them will try to sync CPU info. In reality, only one of them would be enough. For background snapshot, there's no intended functional change. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-7-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 6 +----- migration/savevm.c | 5 +++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index d8a6bc12e0..46e30a4814 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3749,11 +3749,7 @@ static void *bg_migration_thread(void *opaque) if (migration_stop_vm(s, RUN_STATE_PAUSED)) { goto fail; } - /* - * Put vCPUs in sync with shadow context structures, then - * save their state to channel-buffer along with devices. - */ - cpu_synchronize_all_states(); + if (qemu_savevm_state_complete_precopy_non_iterable(fb, false)) { goto fail; } diff --git a/migration/savevm.c b/migration/savevm.c index 5e56a5d9fc..92e77ca92b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1531,6 +1531,9 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, Error *local_err = NULL; int ret; + /* Making sure cpu states are synchronized before saving non-iterable */ + cpu_synchronize_all_states(); + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->early_setup) { /* Already saved during qemu_savevm_state_setup(). */ @@ -1584,8 +1587,6 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) trace_savevm_state_complete_precopy(); - cpu_synchronize_all_states(); - if (!in_postcopy || iterable_only) { ret = qemu_savevm_state_complete_precopy_iterable(f, in_postcopy); if (ret) { From 40004007e67df3835fafeadf4f786ba4011a34b2 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:37 -0500 Subject: [PATCH 1379/2892] migration: Adjust postcopy bandwidth during switchover Precopy uses unlimited bandwidth always during switchover, it makes sense because this is so critical and no one would like to throttle bandwidth during the VM blackout. OTOH, postcopy surprisingly didn't do that. There's one line that in the middle of the postcopy switchover it tries to switch to postcopy's specified max-postcopy-bandwidth, but even so it's somewhere in the middle which is strange. This patch brings the two modes to always use unlimited bandwidth for switchover, meanwhile only apply the postcopy max bandwidth after the switchover is completed. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-8-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 46e30a4814..03e3631d5b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2629,7 +2629,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) int ret; QIOChannelBuffer *bioc; QEMUFile *fb; - uint64_t bandwidth = migrate_max_postcopy_bandwidth(); int cur_state = MIGRATION_STATUS_ACTIVE; /* @@ -2678,6 +2677,9 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail; } + /* Switchover phase, switch to unlimited */ + migration_rate_set(RATE_LIMIT_DISABLED); + /* * Cause any non-postcopiable, but iterative devices to * send out their final data. @@ -2694,12 +2696,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) ram_postcopy_send_discard_bitmap(ms); } - /* - * send rest of state - note things that are doing postcopy - * will notice we're in POSTCOPY_ACTIVE and not actually - * wrap their state up here - */ - migration_rate_set(bandwidth); if (migrate_postcopy_ram()) { /* Ping just for debugging, helps line traces up */ qemu_savevm_send_ping(ms->to_dst_file, 2); @@ -2783,6 +2779,12 @@ static int postcopy_start(MigrationState *ms, Error **errp) } trace_postcopy_preempt_enabled(migrate_postcopy_preempt()); + /* + * Now postcopy officially started, switch to postcopy bandwidth that + * user specified. + */ + migration_rate_set(migrate_max_postcopy_bandwidth()); + return ret; fail_closefb: From 1f9b657cae637ec657aa92a5d7616581ada70672 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:38 -0500 Subject: [PATCH 1380/2892] migration: Adjust locking in migration_maybe_pause() In migration_maybe_pause() QEMU may yield BQL before waiting for a semaphore. However it yields the BQL too early, which logically gives it chance for the main thread to quickly take the BQL and modify the state to CANCELLING. To avoid such race condition from happening at all, always update the migration states within the BQL. It'll make sure no concurrent cancellation can ever happen. With that, IIUC there's chance we can remove the extra parameter in migration_maybe_pause() to update active state, but that'll be done separately later. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-9-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 03e3631d5b..4e4bf8ffed 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2828,14 +2828,14 @@ static int migration_maybe_pause(MigrationState *s, * wait for the 'pause_sem' semaphore. */ if (s->state != MIGRATION_STATUS_CANCELLING) { - bql_unlock(); migrate_set_state(&s->state, *current_active_state, MIGRATION_STATUS_PRE_SWITCHOVER); + bql_unlock(); qemu_sem_wait(&s->pause_sem); + bql_lock(); migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, new_state); *current_active_state = new_state; - bql_lock(); } return s->state == new_state ? 0 : -EINVAL; From ec611bd731af7dce318fffc96f57c46e62fb7e16 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:39 -0500 Subject: [PATCH 1381/2892] migration: Drop cached migration state in migration_maybe_pause() I can't see why we must cache the state now after we avoided possible CANCEL race: that's the only thing I can think of that can modify the migration state concurrently with the migration thread itself. Make all the state updates to happen always, then we don't need to cache the state anymore. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-10-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4e4bf8ffed..5a3d0750ec 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -105,9 +105,7 @@ static MigrationIncomingState *current_incoming; static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); -static int migration_maybe_pause(MigrationState *s, - int *current_active_state, - int new_state); +static int migration_maybe_pause(MigrationState *s, int new_state); static void migrate_fd_cancel(MigrationState *s); static bool close_return_path_on_source(MigrationState *s); static void migration_completion_end(MigrationState *s); @@ -2629,7 +2627,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) int ret; QIOChannelBuffer *bioc; QEMUFile *fb; - int cur_state = MIGRATION_STATUS_ACTIVE; /* * Now we're 100% sure to switch to postcopy, so JSON writer won't be @@ -2664,8 +2661,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail; } - ret = migration_maybe_pause(ms, &cur_state, - MIGRATION_STATUS_POSTCOPY_ACTIVE); + ret = migration_maybe_pause(ms, MIGRATION_STATUS_POSTCOPY_ACTIVE); if (ret < 0) { error_setg_errno(errp, -ret, "%s: Failed in migration_maybe_pause()", __func__); @@ -2803,9 +2799,7 @@ fail: * migrate_pause_before_switchover called with the BQL locked * Returns: 0 on success */ -static int migration_maybe_pause(MigrationState *s, - int *current_active_state, - int new_state) +static int migration_maybe_pause(MigrationState *s, int new_state) { if (!migrate_pause_before_switchover()) { return 0; @@ -2828,21 +2822,19 @@ static int migration_maybe_pause(MigrationState *s, * wait for the 'pause_sem' semaphore. */ if (s->state != MIGRATION_STATUS_CANCELLING) { - migrate_set_state(&s->state, *current_active_state, + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_PRE_SWITCHOVER); bql_unlock(); qemu_sem_wait(&s->pause_sem); bql_lock(); migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, new_state); - *current_active_state = new_state; } return s->state == new_state ? 0 : -EINVAL; } -static int migration_completion_precopy(MigrationState *s, - int *current_active_state) +static int migration_completion_precopy(MigrationState *s) { int ret; @@ -2855,8 +2847,7 @@ static int migration_completion_precopy(MigrationState *s, } } - ret = migration_maybe_pause(s, current_active_state, - MIGRATION_STATUS_DEVICE); + ret = migration_maybe_pause(s, MIGRATION_STATUS_DEVICE); if (ret < 0) { goto out_unlock; } @@ -2909,11 +2900,10 @@ static void migration_completion_postcopy(MigrationState *s) static void migration_completion(MigrationState *s) { int ret = 0; - int current_active_state = s->state; Error *local_err = NULL; if (s->state == MIGRATION_STATUS_ACTIVE) { - ret = migration_completion_precopy(s, ¤t_active_state); + ret = migration_completion_precopy(s); } else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { migration_completion_postcopy(s); } else { @@ -2953,8 +2943,7 @@ fail: error_free(local_err); } - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_FAILED); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); } /** From a880ddd8ce387231b9a93fb2964425ea4e6c9728 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:40 -0500 Subject: [PATCH 1382/2892] migration: Take BQL slightly longer in postcopy_start() This paves way for some follow up patch to modify migration states at the end of postcopy_start(), which should better be with the BQL so that there's no way of concurrent cancellation. So we'll do something slightly more with BQL but they're really trivial, hopefully nothing will really chance with this. A side benefit is we can drop another explicit lock() in failure path. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-11-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 5a3d0750ec..4ba6c8912a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2753,8 +2753,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) migration_downtime_end(ms); - bql_unlock(); - if (migrate_postcopy_ram()) { /* * Although this ping is just for debug, it could potentially be @@ -2770,7 +2768,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) ret = qemu_file_get_error(ms->to_dst_file); if (ret) { error_setg_errno(errp, -ret, "postcopy_start: Migration stream error"); - bql_lock(); goto fail; } trace_postcopy_preempt_enabled(migrate_postcopy_preempt()); @@ -2781,6 +2778,8 @@ static int postcopy_start(MigrationState *ms, Error **errp) */ migration_rate_set(migrate_max_postcopy_bandwidth()); + bql_unlock(); + return ret; fail_closefb: From 46b0155ecf3e65ba1497a9eb35e7352c36ed6f31 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:41 -0500 Subject: [PATCH 1383/2892] migration: Notify COMPLETE once for postcopy Postcopy invokes qemu_savevm_state_complete_precopy() twice, that means it'll invoke COMPLETE notify twice.. also twice the tracepoints that marking precopy complete. Move that notification (along with the tracepoint) out to the caller, so that postcopy will only notify once right at the start of switchover phase from precopy. When at it, rename it to suite the file now it locates. For precopy, there should have no functional change except the tracepoint has a name change. For the other two users of qemu_savevm_state_complete_precopy(), namely: qemu_savevm_state() and qemu_savevm_live_state(): the notifier shouldn't matter because they're not precopy at all. Now in these two contexts (aka, "savevm", and "colo") sometimes the precopy notifiers will still be invoked, but that's outside the scope of this patch. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-12-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 15 +++++++++++++++ migration/savevm.c | 7 ------- migration/trace-events | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4ba6c8912a..72802d6133 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -131,6 +131,17 @@ static void migration_downtime_end(MigrationState *s) } } +static void precopy_notify_complete(void) +{ + Error *local_err = NULL; + + if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) { + error_report_err(local_err); + } + + trace_migration_precopy_complete(); +} + static bool migration_needs_multiple_sockets(void) { return migrate_multifd() || migrate_postcopy_preempt(); @@ -2676,6 +2687,8 @@ static int postcopy_start(MigrationState *ms, Error **errp) /* Switchover phase, switch to unlimited */ migration_rate_set(RATE_LIMIT_DISABLED); + precopy_notify_complete(); + /* * Cause any non-postcopiable, but iterative devices to * send out their final data. @@ -2865,6 +2878,8 @@ static int migration_completion_precopy(MigrationState *s) migration_rate_set(RATE_LIMIT_DISABLED); + precopy_notify_complete(); + ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false); out_unlock: bql_unlock(); diff --git a/migration/savevm.c b/migration/savevm.c index 92e77ca92b..9aef2fa3c9 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1578,15 +1578,8 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) { int ret; - Error *local_err = NULL; bool in_postcopy = migration_in_postcopy(); - if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) { - error_report_err(local_err); - } - - trace_savevm_state_complete_precopy(); - if (!in_postcopy || iterable_only) { ret = qemu_savevm_state_complete_precopy_iterable(f, in_postcopy); if (ret) { diff --git a/migration/trace-events b/migration/trace-events index e03a914afb..12b262f8ee 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -44,7 +44,6 @@ savevm_state_resume_prepare(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" savevm_state_cleanup(void) "" -savevm_state_complete_precopy(void) "" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_downtime_save(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 @@ -195,6 +194,7 @@ migrate_transferred(uint64_t transferred, uint64_t time_spent, uint64_t bandwidt process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" process_incoming_migration_co_postcopy_end_main(void) "" postcopy_preempt_enabled(bool value) "%d" +migration_precopy_complete(void) "" # migration-stats migration_transferred_bytes(uint64_t qemu_file, uint64_t multifd, uint64_t rdma) "qemu_file %" PRIu64 " multifd %" PRIu64 " RDMA %" PRIu64 From 15c2ffa0b739fb40e448892ff0d0d49f99263530 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:42 -0500 Subject: [PATCH 1384/2892] migration: Unwrap qemu_savevm_state_complete_precopy() in postcopy Postcopy invokes qemu_savevm_state_complete_precopy() twice for a long time, and that caused way too much confusions. Let's clean this up and make postcopy easier to read. It's actually fairly straightforward: postcopy starts with saving non-postcopiable iterables, then later it saves again with non-iterable only. Move these two calls out makes everything much easier to follow. Otherwise it's very unclear what qemu_savevm_state_complete_precopy() did in either of the calls. No functional change intended. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-13-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 13 +++++++++++-- migration/savevm.c | 1 - migration/savevm.h | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 72802d6133..d29f7448bd 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2693,7 +2693,11 @@ static int postcopy_start(MigrationState *ms, Error **errp) * Cause any non-postcopiable, but iterative devices to * send out their final data. */ - qemu_savevm_state_complete_precopy(ms->to_dst_file, true); + ret = qemu_savevm_state_complete_precopy_iterable(ms->to_dst_file, true); + if (ret) { + error_setg(errp, "Postcopy save non-postcopiable iterables failed"); + goto fail; + } /* * in Finish migrate and with the io-lock held everything should @@ -2732,7 +2736,12 @@ static int postcopy_start(MigrationState *ms, Error **errp) */ qemu_savevm_send_postcopy_listen(fb); - qemu_savevm_state_complete_precopy(fb, false); + ret = qemu_savevm_state_complete_precopy_non_iterable(fb, true); + if (ret) { + error_setg(errp, "Postcopy save non-iterable device states failed"); + goto fail_closefb; + } + if (migrate_postcopy_ram()) { qemu_savevm_send_ping(fb, 3); } diff --git a/migration/savevm.c b/migration/savevm.c index 9aef2fa3c9..0ddc4c8eb5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1477,7 +1477,6 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) qemu_fflush(f); } -static int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { int64_t start_ts_each, end_ts_each; diff --git a/migration/savevm.h b/migration/savevm.h index c48a53e95e..7957460062 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -44,6 +44,7 @@ void qemu_savevm_state_pending_exact(uint64_t *must_precopy, uint64_t *can_postcopy); void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, uint64_t *can_postcopy); +int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy); void qemu_savevm_send_ping(QEMUFile *f, uint32_t value); void qemu_savevm_send_open_return_path(QEMUFile *f); int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); From 455c1963d390ccf3da25f1e8fbf2c9f1ccee27e4 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:43 -0500 Subject: [PATCH 1385/2892] migration: Cleanup qemu_savevm_state_complete_precopy() Now qemu_savevm_state_complete_precopy() is never used in postcopy, clean it up as in_postcopy==false now unconditionally. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-14-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/savevm.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 0ddc4c8eb5..bc375db282 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1577,25 +1577,19 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) { int ret; - bool in_postcopy = migration_in_postcopy(); - if (!in_postcopy || iterable_only) { - ret = qemu_savevm_state_complete_precopy_iterable(f, in_postcopy); + ret = qemu_savevm_state_complete_precopy_iterable(f, false); + if (ret) { + return ret; + } + + if (!iterable_only) { + ret = qemu_savevm_state_complete_precopy_non_iterable(f, false); if (ret) { return ret; } } - if (iterable_only) { - goto flush; - } - - ret = qemu_savevm_state_complete_precopy_non_iterable(f, in_postcopy); - if (ret) { - return ret; - } - -flush: return qemu_fflush(f); } From 48814111366beaad89667224b087e9a0425d1bf6 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:44 -0500 Subject: [PATCH 1386/2892] migration: Always set DEVICE state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEVICE state was introduced back in 2017: https://lore.kernel.org/qemu-devel/20171020090556.18631-1-dgilbert@redhat.com/ Quote from Dave's cover letter, when the pre-switchover phase was enabled, the state transition looks like this: The precopy flow is: active->pre-switchover->device->completed The postcopy flow is: active->pre-switchover->postcopy-active->completed To supplement above, when the cap is not enabled: The precopy flow is: active->completed The postcopy flow is: active->postcopy-active->completed It works for us, though we have some code just to special case these state transitions, so the DEVICE state currently is special only to precopy, and only conditionally. I had a quick discussion with Libvirt developers, it turns out that this may not be necessary. IOW, it seems okay we can have DEVICE state to be generic, so that we don't have over-complicated state machines. It not only helps align all the migration state machine, help cleanup the code path especially on pre-switchover handling (see the patch itself), another side benefit is we can unconditionally have a specific state to mark the switchover phase, which might be helpful for debugging too. This patch makes the DEVICE state to be present always, marking that source QEMU is switching over. Then the state machine will be always as simple as: active-> [pre-switchover->] -> device -> [postcopy-active->] -> complete After the change, no matter whether pre-switchover or postcopy is enabled or not, we always have DEVICE state showing the switchover phase. When pre-switchover enabled, we'll have an extra stage before that. When postcopy is enabled, we'll have an extra stage after that. A few qtests need touch up in QEMU tree for this change: - A few iotest outputs (194, 203, 234, 262, 280) - Teach libqos's migrate() on "device" state Cc: Jiri Denemark Cc: Daniel P. Berrangé Cc: Dr. David Alan Gilbert Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-15-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 86 +++++++++++++++++++++++-------------- qapi/migration.json | 7 ++- tests/qemu-iotests/194.out | 1 + tests/qemu-iotests/203.out | 1 + tests/qemu-iotests/234.out | 2 + tests/qemu-iotests/262.out | 1 + tests/qemu-iotests/280.out | 1 + tests/qtest/libqos/libqos.c | 3 +- 8 files changed, 66 insertions(+), 36 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index d29f7448bd..5302b7b91b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -105,7 +105,7 @@ static MigrationIncomingState *current_incoming; static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); -static int migration_maybe_pause(MigrationState *s, int new_state); +static bool migration_switchover_start(MigrationState *s); static void migrate_fd_cancel(MigrationState *s); static bool close_return_path_on_source(MigrationState *s); static void migration_completion_end(MigrationState *s); @@ -2657,11 +2657,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) } } - if (!migrate_pause_before_switchover()) { - migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE, - MIGRATION_STATUS_POSTCOPY_ACTIVE); - } - trace_postcopy_start(); bql_lock(); trace_postcopy_start_set_run(); @@ -2672,10 +2667,8 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail; } - ret = migration_maybe_pause(ms, MIGRATION_STATUS_POSTCOPY_ACTIVE); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Failed in migration_maybe_pause()", - __func__); + if (!migration_switchover_start(ms)) { + error_setg(errp, "migration_switchover_start() failed"); goto fail; } @@ -2800,6 +2793,10 @@ static int postcopy_start(MigrationState *ms, Error **errp) */ migration_rate_set(migrate_max_postcopy_bandwidth()); + /* Now, switchover looks all fine, switching to postcopy-active */ + migrate_set_state(&ms->state, MIGRATION_STATUS_DEVICE, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + bql_unlock(); return ret; @@ -2816,14 +2813,39 @@ fail: } /** - * migration_maybe_pause: Pause if required to by - * migrate_pause_before_switchover called with the BQL locked - * Returns: 0 on success + * @migration_switchover_start: Start VM switchover procedure + * + * @s: The migration state object pointer + * + * Prepares for the switchover, depending on "pause-before-switchover" + * capability. + * + * If cap set, state machine goes like: + * [postcopy-]active -> pre-switchover -> device + * + * If cap not set: + * [postcopy-]active -> device + * + * Returns: true on success, false on interruptions. */ -static int migration_maybe_pause(MigrationState *s, int new_state) +static bool migration_switchover_start(MigrationState *s) { + /* Concurrent cancellation? Quit */ + if (s->state == MIGRATION_STATUS_CANCELLING) { + return false; + } + + /* + * No matter precopy or postcopy, since we still hold BQL it must not + * change concurrently to CANCELLING, so it must be either ACTIVE or + * POSTCOPY_ACTIVE. + */ + assert(migration_is_active()); + + /* If the pre stage not requested, directly switch to DEVICE */ if (!migrate_pause_before_switchover()) { - return 0; + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_DEVICE); + return true; } /* Since leaving this state is not atomic with posting the semaphore @@ -2836,23 +2858,22 @@ static int migration_maybe_pause(MigrationState *s, int new_state) /* This block intentionally left blank */ } - /* - * If the migration is cancelled when it is in the completion phase, - * the migration state is set to MIGRATION_STATUS_CANCELLING. - * So we don't need to wait a semaphore, otherwise we would always - * wait for the 'pause_sem' semaphore. - */ - if (s->state != MIGRATION_STATUS_CANCELLING) { - migrate_set_state(&s->state, s->state, - MIGRATION_STATUS_PRE_SWITCHOVER); - bql_unlock(); - qemu_sem_wait(&s->pause_sem); - bql_lock(); - migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, - new_state); - } + /* Update [POSTCOPY_]ACTIVE to PRE_SWITCHOVER */ + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_PRE_SWITCHOVER); + bql_unlock(); - return s->state == new_state ? 0 : -EINVAL; + qemu_sem_wait(&s->pause_sem); + + bql_lock(); + /* + * After BQL released and retaken, the state can be CANCELLING if it + * happend during sem_wait().. Only change the state if it's still + * pre-switchover. + */ + migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, + MIGRATION_STATUS_DEVICE); + + return s->state == MIGRATION_STATUS_DEVICE; } static int migration_completion_precopy(MigrationState *s) @@ -2868,8 +2889,7 @@ static int migration_completion_precopy(MigrationState *s) } } - ret = migration_maybe_pause(s, MIGRATION_STATUS_DEVICE); - if (ret < 0) { + if (!migration_switchover_start(s)) { goto out_unlock; } diff --git a/qapi/migration.json b/qapi/migration.json index 4679ce9f2a..43babd1df4 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -158,8 +158,11 @@ # # @pre-switchover: Paused before device serialisation. (since 2.11) # -# @device: During device serialisation when pause-before-switchover is -# enabled (since 2.11) +# @device: During device serialisation (also known as switchover phase). +# Before 9.2, this is only used when (1) in precopy, and (2) when +# pre-switchover capability is enabled. After 10.0, this state will +# always be present for every migration procedure as the switchover +# phase. (since 2.11) # # @wait-unplug: wait for device unplug request by guest OS to be # completed. (since 4.2) diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 376ed1d2e6..6940e809cd 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -14,6 +14,7 @@ Starting migration... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Gracefully ending the `drive-mirror` job on source... {"return": {}} diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out index 9d4abba8c5..8e58705e51 100644 --- a/tests/qemu-iotests/203.out +++ b/tests/qemu-iotests/203.out @@ -8,4 +8,5 @@ Starting migration... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out index ac8b64350c..be3e138b58 100644 --- a/tests/qemu-iotests/234.out +++ b/tests/qemu-iotests/234.out @@ -10,6 +10,7 @@ Starting migration to B... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -27,6 +28,7 @@ Starting migration back to A... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/262.out b/tests/qemu-iotests/262.out index b8a2d3598d..bd7706b84b 100644 --- a/tests/qemu-iotests/262.out +++ b/tests/qemu-iotests/262.out @@ -8,6 +8,7 @@ Starting migration to B... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index 546dbb4a68..37411144ca 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -7,6 +7,7 @@ Enabling migration QMP events on VM... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} VM is now stopped: diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 5c0fa1f7c5..28a0901a0a 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -117,13 +117,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri) g_assert(qdict_haskey(sub, "status")); st = qdict_get_str(sub, "status"); - /* "setup", "active", "completed", "failed", "cancelled" */ + /* "setup", "active", "device", "completed", "failed", "cancelled" */ if (strcmp(st, "completed") == 0) { qobject_unref(rsp); break; } if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0) + || (strcmp(st, "device") == 0) || (strcmp(st, "wait-unplug") == 0)) { qobject_unref(rsp); g_usleep(5000); From 3dde8fdbad7b8bde82cc22180911b9c751bd1979 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:45 -0500 Subject: [PATCH 1387/2892] migration: Merge precopy/postcopy on switchover start Now after all the cleanups, finally we can merge the switchover startup phase into one single function for precopy/postcopy. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-16-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/migration.c | 66 ++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 5302b7b91b..74c50cc72c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -105,7 +105,7 @@ static MigrationIncomingState *current_incoming; static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); -static bool migration_switchover_start(MigrationState *s); +static bool migration_switchover_start(MigrationState *s, Error **errp); static void migrate_fd_cancel(MigrationState *s); static bool close_return_path_on_source(MigrationState *s); static void migration_completion_end(MigrationState *s); @@ -2667,21 +2667,10 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail; } - if (!migration_switchover_start(ms)) { - error_setg(errp, "migration_switchover_start() failed"); + if (!migration_switchover_start(ms, errp)) { goto fail; } - if (!migration_block_inactivate()) { - error_setg(errp, "%s: Failed in bdrv_inactivate_all()", __func__); - goto fail; - } - - /* Switchover phase, switch to unlimited */ - migration_rate_set(RATE_LIMIT_DISABLED); - - precopy_notify_complete(); - /* * Cause any non-postcopiable, but iterative devices to * send out their final data. @@ -2813,7 +2802,7 @@ fail: } /** - * @migration_switchover_start: Start VM switchover procedure + * @migration_switchover_prepare: Start VM switchover procedure * * @s: The migration state object pointer * @@ -2828,7 +2817,7 @@ fail: * * Returns: true on success, false on interruptions. */ -static bool migration_switchover_start(MigrationState *s) +static bool migration_switchover_prepare(MigrationState *s) { /* Concurrent cancellation? Quit */ if (s->state == MIGRATION_STATUS_CANCELLING) { @@ -2876,6 +2865,34 @@ static bool migration_switchover_start(MigrationState *s) return s->state == MIGRATION_STATUS_DEVICE; } +static bool migration_switchover_start(MigrationState *s, Error **errp) +{ + ERRP_GUARD(); + + if (!migration_switchover_prepare(s)) { + error_setg(errp, "Switchover is interrupted"); + return false; + } + + /* Inactivate disks except in COLO */ + if (!migrate_colo()) { + /* + * Inactivate before sending QEMU_VM_EOF so that the + * bdrv_activate_all() on the other end won't fail. + */ + if (!migration_block_inactivate()) { + error_setg(errp, "Block inactivate failed during switchover"); + return false; + } + } + + migration_rate_set(RATE_LIMIT_DISABLED); + + precopy_notify_complete(); + + return true; +} + static int migration_completion_precopy(MigrationState *s) { int ret; @@ -2889,26 +2906,11 @@ static int migration_completion_precopy(MigrationState *s) } } - if (!migration_switchover_start(s)) { + if (!migration_switchover_start(s, NULL)) { + ret = -EFAULT; goto out_unlock; } - /* Inactivate disks except in COLO */ - if (!migrate_colo()) { - /* - * Inactivate before sending QEMU_VM_EOF so that the - * bdrv_activate_all() on the other end won't fail. - */ - if (!migration_block_inactivate()) { - ret = -EFAULT; - goto out_unlock; - } - } - - migration_rate_set(RATE_LIMIT_DISABLED); - - precopy_notify_complete(); - ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false); out_unlock: bql_unlock(); From a10b37c553391ba6875d95a48f0b3ef7bca7edd1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 14 Jan 2025 18:07:46 -0500 Subject: [PATCH 1388/2892] migration: Trivial cleanup on JSON writer of vmstate_save() Two small cleanups in the same section of vmstate_save(): - Check vmdesc before the "mixed null/non-null data in array" logic, to be crystal clear that it's only about the JSON writer, not the vmstate on its own in the migration stream. - Since we have is_null variable now, use that to replace a check. Signed-off-by: Peter Xu Tested-by: Jiri Denemark Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250114230746.3268797-17-peterx@redhat.com Signed-off-by: Fabiano Rosas --- migration/vmstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migration/vmstate.c b/migration/vmstate.c index 82bd005a83..047a52af89 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -459,6 +459,8 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } /* + * This logic only matters when dumping VM Desc. + * * Due to the fake nullptr handling above, if there's mixed * null/non-null data, it doesn't make sense to emit a * compressed array representation spanning the entire array @@ -466,7 +468,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, * vs. nullptr). Search ahead for the next null/non-null element * and start a new compressed array if found. */ - if (field->flags & VMS_ARRAY_OF_POINTER && + if (vmdesc && (field->flags & VMS_ARRAY_OF_POINTER) && is_null != is_prev_null) { is_prev_null = is_null; @@ -504,7 +506,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, written_bytes); /* If we used a fake temp field.. free it now */ - if (inner_field != field) { + if (is_null) { g_clear_pointer((gpointer *)&inner_field, g_free); } From bc38dc2f5f350310724fd7d4f0a09f8c3a4811fa Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Mon, 27 Jan 2025 17:38:21 +0530 Subject: [PATCH 1389/2892] migration: refactor ram_save_target_page functions Refactor ram_save_target_page legacy and multifd functions into one. Other than simplifying it, it frees 'migration_ops' object from usage, so it is expunged. Signed-off-by: Prasad Pandit Reviewed-by: Fabiano Rosas Message-ID: <20250127120823.144949-3-ppandit@redhat.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 67 +++++++++++++------------------------------------ 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 5aace00bf1..6f460fd22d 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -448,13 +448,6 @@ void ram_transferred_add(uint64_t bytes) } } -struct MigrationOps { - int (*ram_save_target_page)(RAMState *rs, PageSearchStatus *pss); -}; -typedef struct MigrationOps MigrationOps; - -MigrationOps *migration_ops; - static int ram_save_host_page_urgent(PageSearchStatus *pss); /* NOTE: page is the PFN not real ram_addr_t. */ @@ -1960,53 +1953,34 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, } /** - * ram_save_target_page_legacy: save one target page - * - * Returns the number of pages written + * ram_save_target_page: save one target page to the precopy thread + * OR to multifd workers. * * @rs: current RAM state * @pss: data about the page we want to send */ -static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss) +static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) { ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; int res; - if (control_save_page(pss, offset, &res)) { - return res; - } - - if (save_zero_page(rs, pss, offset)) { - return 1; - } - - return ram_save_page(rs, pss); -} - -/** - * ram_save_target_page_multifd: send one target page to multifd workers - * - * Returns 1 if the page was queued, -1 otherwise. - * - * @rs: current RAM state - * @pss: data about the page we want to send - */ -static int ram_save_target_page_multifd(RAMState *rs, PageSearchStatus *pss) -{ - RAMBlock *block = pss->block; - ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; - - /* - * While using multifd live migration, we still need to handle zero - * page checking on the migration main thread. - */ - if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) { + if (!migrate_multifd() + || migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) { if (save_zero_page(rs, pss, offset)) { return 1; } } - return ram_save_multifd_page(block, offset); + if (migrate_multifd()) { + RAMBlock *block = pss->block; + return ram_save_multifd_page(block, offset); + } + + if (control_save_page(pss, offset, &res)) { + return res; + } + + return ram_save_page(rs, pss); } /* Should be called before sending a host page */ @@ -2095,7 +2069,7 @@ static int ram_save_host_page_urgent(PageSearchStatus *pss) if (page_dirty) { /* Be strict to return code; it must be 1, or what else? */ - if (migration_ops->ram_save_target_page(rs, pss) != 1) { + if (ram_save_target_page(rs, pss) != 1) { error_report_once("%s: ram_save_target_page failed", __func__); ret = -1; goto out; @@ -2164,7 +2138,7 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) if (preempt_active) { qemu_mutex_unlock(&rs->bitmap_mutex); } - tmppages = migration_ops->ram_save_target_page(rs, pss); + tmppages = ram_save_target_page(rs, pss); if (tmppages >= 0) { pages += tmppages; /* @@ -2362,8 +2336,6 @@ static void ram_save_cleanup(void *opaque) xbzrle_cleanup(); multifd_ram_save_cleanup(); ram_state_cleanup(rsp); - g_free(migration_ops); - migration_ops = NULL; } static void ram_state_reset(RAMState *rs) @@ -3029,13 +3001,8 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) return ret; } - migration_ops = g_malloc0(sizeof(MigrationOps)); - if (migrate_multifd()) { multifd_ram_save_setup(); - migration_ops->ram_save_target_page = ram_save_target_page_multifd; - } else { - migration_ops->ram_save_target_page = ram_save_target_page_legacy; } /* From aca2c48e4d26426c14499fd3d8a2e333ac57d267 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 22 Jan 2025 14:43:13 +0100 Subject: [PATCH 1390/2892] tests/functional/qemu_test/decorators: Fix bad check for imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skipIfMissingImports should use importlib.import_module() for checking whether a module with the name stored in the "impname" variable is available or not, otherwise the code tries to import a module with the name "impname" instead. (This bug hasn't been noticed before since there is another issue with this decorator that will be fixed by the next patch) Suggested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Message-ID: <20250122134315.1448794-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/decorators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index df088bc090..08f58f6b40 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -2,6 +2,7 @@ # # Decorators useful in functional tests +import importlib import os import platform from unittest import skipUnless @@ -97,7 +98,7 @@ def skipIfMissingImports(*args): def has_imports(importlist): for impname in importlist: try: - import impname + importlib.import_module(impname) except ImportError: return False return True From 257de641e091f4f5063298b9d717f3f15f8917f2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 22 Jan 2025 14:43:14 +0100 Subject: [PATCH 1391/2892] tests/functional: Fix broken decorators with lamda functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The decorators that use a lambda function are currently broken and do not properly skip the test if the condition is not met. Using "return skipUnless(lambda: ...)" does not work as expected. To fix it, rewrite the decorators without lambda, it's simpler that way anyway. Reviewed-by: Daniel P. Berrangé Message-ID: <20250122134315.1448794-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/decorators.py | 40 +++++++++++------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index 08f58f6b40..3d9c02fd59 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -17,15 +17,14 @@ Example: @skipIfMissingCommands("mkisofs", "losetup") ''' def skipIfMissingCommands(*args): - def has_cmds(cmdlist): - for cmd in cmdlist: - if not which(cmd): - return False - return True + has_cmds = True + for cmd in args: + if not which(cmd): + has_cmds = False + break - return skipUnless(lambda: has_cmds(args), - 'required command(s) "%s" not installed' % - ", ".join(args)) + return skipUnless(has_cmds, 'required command(s) "%s" not installed' % + ", ".join(args)) ''' Decorator to skip execution of a test if the current @@ -36,9 +35,9 @@ Example @skipIfNotMachine("x86_64", "aarch64") ''' def skipIfNotMachine(*args): - return skipUnless(lambda: platform.machine() in args, - 'not running on one of the required machine(s) "%s"' % - ", ".join(args)) + return skipUnless(platform.machine() in args, + 'not running on one of the required machine(s) "%s"' % + ", ".join(args)) ''' Decorator to skip execution of flaky tests, unless @@ -95,14 +94,13 @@ Example: @skipIfMissingImports("numpy", "cv2") ''' def skipIfMissingImports(*args): - def has_imports(importlist): - for impname in importlist: - try: - importlib.import_module(impname) - except ImportError: - return False - return True + has_imports = True + for impname in args: + try: + importlib.import_module(impname) + except ImportError: + has_imports = False + break - return skipUnless(lambda: has_imports(args), - 'required import(s) "%s" not installed' % - ", ".join(args)) + return skipUnless(has_imports, 'required import(s) "%s" not installed' % + ", ".join(args)) From 092fd6486d78e556c6f3cfb40d5c1855fc0fff32 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Jan 2025 08:43:08 +0100 Subject: [PATCH 1392/2892] tests/functional: Convert the migration avocado test Now that we've got a find_free_port() function in the functional test framework, we can convert the migration test, too. While the original avocado test was only meant to run on aarch64, ppc64 and x86, we can turn this into a more generic test by now and run it on all architectures that have a machine which ships with a working firmware. To avoid overlapping with the migration qtest, we now also test migration on machines that are not covered by the migration qtest yet. Acked-by: Fabiano Rosas Message-ID: <20250103074308.463860-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/migration.py | 135 ----------------------------- tests/functional/meson.build | 32 +++++++ tests/functional/test_migration.py | 100 +++++++++++++++++++++ 4 files changed, 133 insertions(+), 135 deletions(-) delete mode 100644 tests/avocado/migration.py create mode 100755 tests/functional/test_migration.py diff --git a/MAINTAINERS b/MAINTAINERS index bb96a00db0..7b4d84bf5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3461,6 +3461,7 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py +F: tests/functional/test_migration.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration/ F: tests/qtest/migration-* diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py deleted file mode 100644 index be6234b3c2..0000000000 --- a/tests/avocado/migration.py +++ /dev/null @@ -1,135 +0,0 @@ -# Migration test -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Authors: -# Cleber Rosa -# Caio Carrara -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - - -import tempfile -import os - -from avocado_qemu import QemuSystemTest -from avocado import skipUnless - -from avocado.utils.network import ports -from avocado.utils import wait -from avocado.utils.path import find_command - - -class MigrationTest(QemuSystemTest): - """ - :avocado: tags=migration - """ - - timeout = 10 - - @staticmethod - def migration_finished(vm): - return vm.cmd('query-migrate')['status'] in ('completed', 'failed') - - def assert_migration(self, src_vm, dst_vm): - wait.wait_for(self.migration_finished, - timeout=self.timeout, - step=0.1, - args=(src_vm,)) - wait.wait_for(self.migration_finished, - timeout=self.timeout, - step=0.1, - args=(dst_vm,)) - self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed') - self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed') - self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') - self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') - - def do_migrate(self, dest_uri, src_uri=None): - dest_vm = self.get_vm('-incoming', dest_uri) - dest_vm.add_args('-nodefaults') - dest_vm.launch() - if src_uri is None: - src_uri = dest_uri - source_vm = self.get_vm() - source_vm.add_args('-nodefaults') - source_vm.launch() - source_vm.qmp('migrate', uri=src_uri) - self.assert_migration(source_vm, dest_vm) - - def _get_free_port(self): - port = ports.find_free_port() - if port is None: - self.cancel('Failed to find a free port') - return port - - def migration_with_tcp_localhost(self): - dest_uri = 'tcp:localhost:%u' % self._get_free_port() - self.do_migrate(dest_uri) - - def migration_with_unix(self): - with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: - dest_uri = 'unix:%s/qemu-test.sock' % socket_path - self.do_migrate(dest_uri) - - @skipUnless(find_command('nc', default=False), "'nc' command not found") - def migration_with_exec(self): - """The test works for both netcat-traditional and netcat-openbsd packages.""" - free_port = self._get_free_port() - dest_uri = 'exec:nc -l localhost %u' % free_port - src_uri = 'exec:nc localhost %u' % free_port - self.do_migrate(dest_uri, src_uri) - - -@skipUnless('aarch64' in os.uname()[4], "host != target") -class Aarch64(MigrationTest): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - """ - - def test_migration_with_tcp_localhost(self): - self.migration_with_tcp_localhost() - - def test_migration_with_unix(self): - self.migration_with_unix() - - def test_migration_with_exec(self): - self.migration_with_exec() - - -@skipUnless('x86_64' in os.uname()[4], "host != target") -class X86_64(MigrationTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - :avocado: tags=cpu:qemu64 - """ - - def test_migration_with_tcp_localhost(self): - self.migration_with_tcp_localhost() - - def test_migration_with_unix(self): - self.migration_with_unix() - - def test_migration_with_exec(self): - self.migration_with_exec() - - -@skipUnless('ppc64le' in os.uname()[4], "host != target") -class PPC64(MigrationTest): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - def test_migration_with_tcp_localhost(self): - self.migration_with_tcp_localhost() - - def test_migration_with_unix(self): - self.migration_with_unix() - - def test_migration_with_exec(self): - self.migration_with_exec() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b62f714220..3e11b725cb 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -59,6 +59,10 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] +tests_aarch64_system_quick = [ + 'migration', +] + tests_aarch64_system_thorough = [ 'aarch64_aspeed', 'aarch64_raspi3', @@ -74,10 +78,18 @@ tests_aarch64_system_thorough = [ 'multiprocess', ] +tests_alpha_system_quick = [ + 'migration', +] + tests_alpha_system_thorough = [ 'alpha_clipper', ] +tests_arm_system_quick = [ + 'migration', +] + tests_arm_system_thorough = [ 'arm_aspeed_ast1030', 'arm_aspeed_palmetto', @@ -114,6 +126,10 @@ tests_hppa_system_quick = [ 'hppa_seabios', ] +tests_i386_system_quick = [ + 'migration', +] + tests_i386_system_thorough = [ 'i386_tuxrun', ] @@ -163,6 +179,7 @@ tests_or1k_system_thorough = [ ] tests_ppc_system_quick = [ + 'migration', 'ppc_74xx', ] @@ -177,6 +194,10 @@ tests_ppc_system_thorough = [ 'ppc_virtex_ml507', ] +tests_ppc64_system_quick = [ + 'migration', +] + tests_ppc64_system_thorough = [ 'ppc64_e500', 'ppc64_hv', @@ -186,6 +207,7 @@ tests_ppc64_system_thorough = [ ] tests_riscv32_system_quick = [ + 'migration', 'riscv_opensbi', ] @@ -194,6 +216,7 @@ tests_riscv32_system_thorough = [ ] tests_riscv64_system_quick = [ + 'migration', 'riscv_opensbi', ] @@ -220,10 +243,18 @@ tests_sh4eb_system_thorough = [ 'sh4eb_r2d', ] +tests_sparc_system_quick = [ + 'migration', +] + tests_sparc_system_thorough = [ 'sparc_sun4m', ] +tests_sparc64_system_quick = [ + 'migration', +] + tests_sparc64_system_thorough = [ 'sparc64_sun4u', 'sparc64_tuxrun', @@ -232,6 +263,7 @@ tests_sparc64_system_thorough = [ tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', + 'migration', 'pc_cpu_hotplug_props', 'virtio_version', 'x86_cpu_model_versions', diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py new file mode 100755 index 0000000000..44804113cf --- /dev/null +++ b/tests/functional/test_migration.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# +# Migration test +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Authors: +# Cleber Rosa +# Caio Carrara +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +import tempfile +import os +import time + +from qemu_test import QemuSystemTest, skipIfMissingCommands +from qemu_test.ports import Ports + +class MigrationTest(QemuSystemTest): + + timeout = 10 + + @staticmethod + def migration_finished(vm): + return vm.cmd('query-migrate')['status'] in ('completed', 'failed') + + def assert_migration(self, src_vm, dst_vm): + + end = time.monotonic() + self.timeout + while time.monotonic() < end and not self.migration_finished(src_vm): + time.sleep(0.1) + + end = time.monotonic() + self.timeout + while time.monotonic() < end and not self.migration_finished(dst_vm): + time.sleep(0.1) + + self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') + self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') + + def select_machine(self): + target_machine = { + 'aarch64': 'quanta-gsj', + 'alpha': 'clipper', + 'arm': 'npcm750-evb', + 'i386': 'isapc', + 'ppc': 'sam460ex', + 'ppc64': 'mac99', + 'riscv32': 'spike', + 'riscv64': 'virt', + 'sparc': 'SS-4', + 'sparc64': 'sun4u', + 'x86_64': 'microvm', + } + self.set_machine(target_machine[self.arch]) + + def do_migrate(self, dest_uri, src_uri=None): + self.select_machine() + dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu") + dest_vm.add_args('-nodefaults') + dest_vm.launch() + if src_uri is None: + src_uri = dest_uri + source_vm = self.get_vm(name="source-qemu") + source_vm.add_args('-nodefaults') + source_vm.launch() + source_vm.qmp('migrate', uri=src_uri) + self.assert_migration(source_vm, dest_vm) + + def _get_free_port(self, ports): + port = ports.find_free_port() + if port is None: + self.skipTest('Failed to find a free port') + return port + + def test_migration_with_tcp_localhost(self): + with Ports() as ports: + dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports) + self.do_migrate(dest_uri) + + def test_migration_with_unix(self): + with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: + dest_uri = 'unix:%s/qemu-test.sock' % socket_path + self.do_migrate(dest_uri) + + @skipIfMissingCommands('nc') + def test_migration_with_exec(self): + """The test works for both netcat-traditional and netcat-openbsd packages.""" + with Ports() as ports: + free_port = self._get_free_port(ports) + dest_uri = 'exec:nc -l localhost %u' % free_port + src_uri = 'exec:nc localhost %u' % free_port + self.do_migrate(dest_uri, src_uri) + +if __name__ == '__main__': + QemuSystemTest.main() From 156ee8b812689ae22f7f68a7c58e21b9d2cc0fd9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 23 Jan 2025 09:36:25 +0100 Subject: [PATCH 1393/2892] tests/functional: Fix the aarch64_tcg_plugins test Unfortunately, this test had not been added to meson.build, so we did not notice a regression: Looking for 'Kernel panic - not syncing: VFS:' as the indication for the final boot state of the kernel was a bad idea since 'Kernel panic - not syncing' is the default failure message of the LinuxKernelTest class, and since we're now reading the console input byte by byte instead of linewise (see commit cdad03b74f75), the failure now triggers before we fully read the success string. Let's fix this by simply looking for the previous line in the console output instead. Also, replace the call to cancel() - this was only available in the Avocado framework. In the functional framework, we must use skipTest() instead. While we're at it, also fix the TODO here by looking for the exact error and only skip the test if the plugins are not available. Fixes: 3abc545e66 ("tests/functional: Convert the tcg_plugins test") Fixes: cdad03b74f ("tests/functional: rewrite console handling to be bytewise") Message-ID: <20250123083625.1498495-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/meson.build | 1 + tests/functional/test_aarch64_tcg_plugins.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 3e11b725cb..2b2d8953aa 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -72,6 +72,7 @@ tests_aarch64_system_thorough = [ 'aarch64_sbsaref', 'aarch64_sbsaref_alpine', 'aarch64_sbsaref_freebsd', + 'aarch64_tcg_plugins', 'aarch64_tuxrun', 'aarch64_virt', 'aarch64_xlnx_versal', diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py index 01660eb090..7e8beacc83 100755 --- a/tests/functional/test_aarch64_tcg_plugins.py +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -15,6 +15,7 @@ import tempfile import mmap import re +from qemu.machine.machine import VMLaunchFailure from qemu_test import LinuxKernelTest, Asset @@ -43,10 +44,12 @@ class PluginKernelBase(LinuxKernelTest): try: vm.launch() - except: - # TODO: probably fails because plugins not enabled but we - # can't currently probe for the feature. - self.cancel("TCG Plugins not enabled?") + except VMLaunchFailure as excp: + if "plugin interface not enabled in this build" in excp.output: + self.skipTest("TCG plugins not enabled") + else: + self.log.info(f"unhandled launch failure: {excp.output}") + raise excp self.wait_for_console_pattern(console_pattern, vm) # ensure logs are flushed @@ -65,7 +68,7 @@ class PluginKernelNormal(PluginKernelBase): kernel_path = self.ASSET_KERNEL.fetch() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' + console_pattern = 'Please append a correct "root=" boot option' plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", suffix=".log") @@ -91,7 +94,7 @@ class PluginKernelNormal(PluginKernelBase): kernel_path = self.ASSET_KERNEL.fetch() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' + console_pattern = 'Please append a correct "root=" boot option' plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", suffix=".log") From 5e654086246fcad85b820d60364a38e6952ab41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 28 Jan 2025 22:21:45 +0100 Subject: [PATCH 1394/2892] tests/functional: Add a ppc64 mac99 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test sequence boots from disk a mac99 machine in 64-bit mode, in which case the CPU is a PPC 970. The buildroot rootfs is built with config : BR2_powerpc64=y BR2_powerpc_970=y and the kernel with the g5 deconfig. Reviewed-by: Thomas Huth Signed-off-by: Cédric Le Goater Message-ID: <20250128212145.1186617-1-clg@redhat.com> [thuth: Adjusted the comment about '-nographic] Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/functional/meson.build | 2 ++ tests/functional/test_ppc64_mac99.py | 44 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100755 tests/functional/test_ppc64_mac99.py diff --git a/MAINTAINERS b/MAINTAINERS index 7b4d84bf5f..59c3c45f86 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1453,6 +1453,7 @@ F: include/hw/pci-host/uninorth.h F: include/hw/input/adb* F: pc-bios/qemu_vga.ndrv F: tests/functional/test_ppc_mac.py +F: tests/functional/test_ppc64_mac99.py Old World (g3beige) M: Mark Cave-Ayland diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 2b2d8953aa..2e0802144a 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -41,6 +41,7 @@ test_timeouts = { 'ppc64_powernv' : 480, 'ppc64_pseries' : 480, 'ppc64_tuxrun' : 420, + 'ppc64_mac99' : 120, 'riscv64_tuxrun' : 120, 's390x_ccw_virtio' : 420, 'sh4_tuxrun' : 240, @@ -205,6 +206,7 @@ tests_ppc64_system_thorough = [ 'ppc64_powernv', 'ppc64_pseries', 'ppc64_tuxrun', + 'ppc64_mac99', ] tests_riscv32_system_quick = [ diff --git a/tests/functional/test_ppc64_mac99.py b/tests/functional/test_ppc64_mac99.py new file mode 100755 index 0000000000..dfd9c01371 --- /dev/null +++ b/tests/functional/test_ppc64_mac99.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a mac99 machine with a PPC970 CPU +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + +class mac99Test(LinuxKernelTest): + + ASSET_BR2_MAC99_LINUX = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/vmlinux', + 'd59307437e4365f2cced0bbd1b04949f7397b282ef349b7cafd894d74aadfbff') + + ASSET_BR2_MAC99_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/rootfs.ext2', + 'bbd5fd8af62f580bc4e585f326fe584e22856572633a8333178ea6d4ed4955a4') + + def test_ppc64_mac99_buildroot(self): + self.set_machine('mac99') + + linux_path = self.ASSET_BR2_MAC99_LINUX.fetch() + rootfs_path = self.ASSET_BR2_MAC99_ROOTFS.fetch() + + self.vm.set_console() + + # Note: We need '-nographic' to get a serial console + self.vm.add_args('-kernel', linux_path, + '-append', 'root=/dev/sda', + '-drive', f'file={rootfs_path},format=raw', + '-snapshot', '-nographic') + self.vm.launch() + + self.wait_for_console_pattern('>> OpenBIOS') + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('gem 0000:f0:0e.0 eth0: Link is up at 100 Mbps') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'Power down') + +if __name__ == '__main__': + LinuxKernelTest.main() From c8b2deb9214b45349c83e174dfc5edc6d58be434 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 27 Jan 2025 19:41:10 +0100 Subject: [PATCH 1395/2892] tests/functional/test_mips_malta: Fix comment about endianness of the test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test is for the big endian MIPS target, not for the little endian target. Fixes: 79cb4a14cb6 ("tests/functional: Convert mips32eb 4Kc Malta tests") Message-ID: <20250127184112.108122-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- tests/functional/test_mips_malta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index 3b15038d89..eaf372255b 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Functional tests for the little-endian 32-bit MIPS Malta board +# Functional tests for the big-endian 32-bit MIPS Malta board # # Copyright (c) Philippe Mathieu-Daudé # From ba68dd7d475ee2f83e285a21cac3e5e8e8c54bff Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 23 Jan 2025 13:37:53 +0100 Subject: [PATCH 1396/2892] target/s390x: Fix PPNO execution with icount Executing PERFORM RANDOM NUMBER OPERATION makes QEMU exit with "Bad icount read" when using record/replay. This is caused by icount_get_raw_locked() if the current instruction is not the last one in the respective translation block. For the x86_64's rdrand this is resolved by calling translator_io_start(). On s390x one uses IF_IO in order to make this call happen automatically. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Message-ID: <20250123123808.194405-1-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/tcg/insn-data.h.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index e7d61cdec2..ec730ee091 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -1012,7 +1012,7 @@ D(0xb92e, KM, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KM) D(0xb92f, KMC, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KMC) D(0xb929, KMA, RRF_b, MSA8, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KMA) - D(0xb93c, PPNO, RRE, MSA5, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_PPNO) + E(0xb93c, PPNO, RRE, MSA5, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_PPNO, IF_IO) D(0xb93e, KIMD, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KIMD) D(0xb93f, KLMD, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KLMD) From e43ced8be18dda77c229ab09f85136a4d600d40d Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 28 Jan 2025 01:12:42 +0100 Subject: [PATCH 1397/2892] target/s390x: Fix MVC not always invalidating translation blocks Node.js crashes in qemu-system-s390x with random SIGSEGVs / SIGILLs. The v8 JIT used by Node.js can garbage collect and overwrite unused code. Overwriting is performed by WritableJitAllocation::CopyCode(), which ultimately calls memcpy(). For certain sizes, memcpy() uses the MVC instruction. QEMU implements MVC and other similar instructions using helpers. While TCG store ops invalidate affected translation blocks automatically, helpers must do this manually by calling probe_access_flags(). The MVC helper does this using the access_prepare() -> access_prepare_nf() -> s390_probe_access() -> probe_access_flags() call chain. At the last step of this chain, the store size is replaced with 0. This causes the probe_access_flags() -> notdirty_write() -> tb_invalidate_phys_range_fast() chain to miss some translation blocks. When this happens, QEMU executes a mix of old and new code. This quickly leads to either a SIGSEGV or a SIGILL in case the old code ends in the middle of a new instruction. Fix by passing the true size. Reported-by: Berthold Gunreben Cc: Sarah Kriesch Cc: qemu-stable@nongnu.org Closes: https://bugzilla.opensuse.org/show_bug.cgi?id=1235709 Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Reviewed-by: David Hildenbrand Fixes: e2faabee78ff ("accel/tcg: Forward probe size on to notdirty_write") Message-ID: <20250128001338.11474-1-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/tcg/mem_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 32717acb7d..c6ab2901e5 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -149,7 +149,7 @@ static inline int s390_probe_access(CPUArchState *env, target_ulong addr, int mmu_idx, bool nonfault, void **phost, uintptr_t ra) { - int flags = probe_access_flags(env, addr, 0, access_type, mmu_idx, + int flags = probe_access_flags(env, addr, size, access_type, mmu_idx, nonfault, phost, ra); if (unlikely(flags & TLB_INVALID_MASK)) { From b497b0376cb60ae786ec12cffb259641ec1314eb Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 28 Jan 2025 01:12:43 +0100 Subject: [PATCH 1398/2892] tests/tcg/s390x: Test modifying code using the MVC instruction Add a small test to prevent regressions. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Message-ID: <20250128001338.11474-2-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.softmmu-target | 1 + tests/tcg/s390x/mvc-smc.c | 82 +++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 tests/tcg/s390x/mvc-smc.c diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 7adde2fa08..8cd4667c63 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -42,6 +42,7 @@ $(ASM_TESTS): LDFLAGS += -Wl,-T$(LINK_SCRIPT) -Wl,--build-id=none $(ASM_TESTS): $(LINK_SCRIPT) TESTS += $(ASM_TESTS) +MULTIARCH_TESTS += mvc-smc S390X_MULTIARCH_RUNTIME_OBJS = head64.o console.o $(MINILIB_OBJS) $(MULTIARCH_TESTS): $(S390X_MULTIARCH_RUNTIME_OBJS) $(MULTIARCH_TESTS): LDFLAGS += $(S390X_MULTIARCH_RUNTIME_OBJS) diff --git a/tests/tcg/s390x/mvc-smc.c b/tests/tcg/s390x/mvc-smc.c new file mode 100644 index 0000000000..d68f60caa8 --- /dev/null +++ b/tests/tcg/s390x/mvc-smc.c @@ -0,0 +1,82 @@ +/* + * Test modifying code using the MVC instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#define PAGE_SIZE 4096 +#define BR_14_SIZE 2 +#define RWX_OFFSET 2 + +static unsigned char rw[PAGE_SIZE + BR_14_SIZE]; +static unsigned char rwx[RWX_OFFSET + sizeof(rw)] + __attribute__((aligned(PAGE_SIZE))); + +typedef unsigned long (*function_t)(unsigned long); + +static int emit_function(unsigned char *p, int n) +{ + int i = 0, val = 0; + + while (i < n - 2) { + /* aghi %r2,1 */ + p[i++] = 0xa7; + p[i++] = 0x2b; + p[i++] = 0x00; + p[i++] = 0x01; + val++; + } + + /* br %r14 */ + p[i++] = 0x07; + p[i++] = 0xfe; + + return val; +} + +static void memcpy_mvc(void *dest, void *src, unsigned long n) +{ + while (n >= 256) { + asm("mvc 0(256,%[dest]),0(%[src])" + : + : [dest] "a" (dest) + , [src] "a" (src) + : "memory"); + dest += 256; + src += 256; + n -= 256; + } + asm("exrl %[n],0f\n" + "j 1f\n" + "0: mvc 0(1,%[dest]),0(%[src])\n" + "1:" + : + : [dest] "a" (dest) + , [src] "a" (src) + , [n] "a" (n) + : "memory"); +} + +int main(void) +{ + int expected, size; + + /* Create a TB. */ + size = sizeof(rwx) - RWX_OFFSET - 4; + expected = emit_function(rwx + RWX_OFFSET, size); + if (((function_t)(rwx + RWX_OFFSET))(0) != expected) { + return 1; + } + + /* Overwrite the TB. */ + size += 4; + expected = emit_function(rw, size); + memcpy_mvc(rwx + RWX_OFFSET, rw, size); + if (((function_t)(rwx + RWX_OFFSET))(0) != expected) { + return 2; + } + + return 0; +} From a4cda3f5df2e693893aa076403867fda8dec5584 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 24 Jan 2025 12:25:48 +0100 Subject: [PATCH 1399/2892] hw/s390x/s390-virtio-ccw: Fix a record/replay deadlock Booting an s390x VM in record/replay mode hangs due to a deadlock between rr_cpu_thread_fn() and s390_machine_reset(). The former needs the record/replay mutex held by the latter, and the latter waits until the former completes its run_on_cpu() request. Fix by temporarily dropping the record/replay mutex, like it's done in pause_all_vcpus(). Signed-off-by: Ilya Leoshkevich Message-ID: <20250124112625.23050-1-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 3af613d4e9..b069303592 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -48,6 +48,7 @@ #include "kvm/kvm_s390x.h" #include "hw/virtio/virtio-md-pci.h" #include "hw/s390x/virtio-ccw-md.h" +#include "system/replay.h" #include CONFIG_DEVICES static Error *pv_mig_blocker; @@ -454,6 +455,18 @@ static void s390_machine_reset(MachineState *machine, ResetType type) CPUState *cs, *t; S390CPU *cpu; + /* + * Temporarily drop the record/replay mutex to let rr_cpu_thread_fn() + * process the run_on_cpu() requests below. This is safe, because at this + * point one of the following is true: + * - All CPU threads are not running, either because the machine is being + * initialized, or because the guest requested a reset using diag 308. + * There is no risk to desync the record/replay state. + * - A snapshot is about to be loaded. The record/replay state consistency + * is not important. + */ + replay_mutex_unlock(); + /* get the reset parameters, reset them once done */ s390_ipl_get_reset_request(&cs, &reset_type); @@ -533,7 +546,7 @@ static void s390_machine_reset(MachineState *machine, ResetType type) * went wrong. */ s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); - return; + goto out_lock; } run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL); @@ -546,6 +559,15 @@ static void s390_machine_reset(MachineState *machine, ResetType type) run_on_cpu(t, s390_do_cpu_set_diag318, RUN_ON_CPU_HOST_ULONG(0)); } s390_ipl_clear_reset_request(); + +out_lock: + /* + * Re-take the record/replay mutex, temporarily dropping the BQL in order + * to satisfy the ordering requirements. + */ + bql_unlock(); + replay_mutex_lock(); + bql_lock(); } static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev, From fe638ae67bf804aeaeb3360675e88f233eb24604 Mon Sep 17 00:00:00 2001 From: Reza Arbab Date: Wed, 15 Jan 2025 10:14:25 -0600 Subject: [PATCH 1400/2892] virtio-balloon-pci: Allow setting nvectors, so we can use MSI-X Most virtio-pci devices allow MSI-X. Add it to virtio-balloon-pci, but only enable it in new machine types, so we don't break migration of existing machine types between different qemu versions. This copies what was done for virtio-rng-pci in: 9ea02e8f1306 ("virtio-rng-pci: Allow setting nvectors, so we can use MSI-X") bad9c5a5166f ("virtio-rng-pci: fix migration compat for vectors") 62bdb8871512 ("virtio-rng-pci: fix transitional migration compat for vectors") Acked-by: David Hildenbrand Signed-off-by: Reza Arbab Tested-by: Mario Casquero Message-ID: <20250115161425.246348-1-arbab@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/core/machine.c | 3 +++ hw/virtio/virtio-balloon-pci.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index c23b399496..8f396ef803 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -38,6 +38,9 @@ GlobalProperty hw_compat_9_2[] = { {"arm-cpu", "backcompat-pauth-default-use-qarma5", "true"}, + { "virtio-balloon-pci", "vectors", "0" }, + { "virtio-balloon-pci-transitional", "vectors", "0" }, + { "virtio-balloon-pci-non-transitional", "vectors", "0" }, }; const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); diff --git a/hw/virtio/virtio-balloon-pci.c b/hw/virtio/virtio-balloon-pci.c index ce2645ba71..db7e1cb475 100644 --- a/hw/virtio/virtio-balloon-pci.c +++ b/hw/virtio/virtio-balloon-pci.c @@ -35,11 +35,22 @@ struct VirtIOBalloonPCI { VirtIOBalloon vdev; }; +static const Property virtio_balloon_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), +}; + static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 2; + } + vpci_dev->class_code = PCI_CLASS_OTHERS; qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -55,6 +66,7 @@ static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; + device_class_set_props(dc, virtio_balloon_properties); } static void virtio_balloon_pci_instance_init(Object *obj) From 03248e714b0dc95f13221cfd4496cbdfa118e114 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 28 Jan 2025 19:57:04 +0100 Subject: [PATCH 1401/2892] virtio-mem-pci: Allow setting nvectors, so we can use MSI-X Let's do it similar as virtio-balloon-pci. With this change, we can use virtio-mem-pci on s390x, although plugging will still fail until properly wired up in the machine. No need to worry about transitional/non_transitional devices, because they don't exist for virtio-mem. Signed-off-by: David Hildenbrand Reviewed-by: Thomas Huth Message-ID: <20250128185705.1609038-2-david@redhat.com> Signed-off-by: Thomas Huth --- hw/core/machine.c | 1 + hw/virtio/virtio-mem-pci.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index 8f396ef803..7b74cde10a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -41,6 +41,7 @@ GlobalProperty hw_compat_9_2[] = { { "virtio-balloon-pci", "vectors", "0" }, { "virtio-balloon-pci-transitional", "vectors", "0" }, { "virtio-balloon-pci-non-transitional", "vectors", "0" }, + { "virtio-mem-pci", "vectors", "0" }, }; const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index 1b4e9a3284..6cc5f0fd3b 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -22,6 +22,10 @@ static void virtio_mem_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOMEMPCI *mem_pci = VIRTIO_MEM_PCI(vpci_dev); DeviceState *vdev = DEVICE(&mem_pci->vdev); + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 2; + } + virtio_pci_force_virtio_1(vpci_dev); qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -152,6 +156,13 @@ static void virtio_mem_pci_set_requested_size(Object *obj, Visitor *v, object_property_set(OBJECT(&pci_mem->vdev), name, v, errp); } +static const Property virtio_mem_pci_class_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), +}; + static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -164,6 +175,7 @@ static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; + device_class_set_props(dc, virtio_mem_pci_class_properties); mdc->get_addr = virtio_mem_pci_get_addr; mdc->set_addr = virtio_mem_pci_set_addr; From d77ae821e8940bdb9d97ee688aaa949f45de1758 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 28 Jan 2025 19:57:05 +0100 Subject: [PATCH 1402/2892] s390x/s390-virtio-ccw: Support plugging PCI-based virtio memory devices Let's just wire it up, unlocking virtio-mem-pci support on s390x. While at it, drop the "return;" in s390_machine_device_unplug_request(), to make it look like the other handlers. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-ID: <20250128185705.1609038-3-david@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index b069303592..d9e683c5b4 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -576,8 +576,7 @@ static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { virtio_ccw_md_pre_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { - error_setg(errp, - "PCI-attached virtio based memory devices not supported"); + virtio_md_pci_pre_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } } @@ -588,7 +587,8 @@ static void s390_machine_device_plug(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { s390_cpu_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { /* * At this point, the device is realized and set all memdevs mapped, so * qemu_maxrampagesize() will pick up the page sizes of these memdevs @@ -602,7 +602,11 @@ static void s390_machine_device_plug(HotplugHandler *hotplug_dev, " initial memory"); return; } - virtio_ccw_md_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); + } else { + virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); + } } } @@ -611,10 +615,12 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { error_setg(errp, "CPU hot unplug not supported on this machine"); - return; } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { virtio_ccw_md_unplug_request(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_unplug_request(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), + errp); } } @@ -623,7 +629,9 @@ static void s390_machine_device_unplug(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { virtio_ccw_md_unplug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); - } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_unplug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); + } } static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms, From fc9fea48be250ad3e51ddaea9508a320896c094b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 29 Jan 2025 11:48:44 +0100 Subject: [PATCH 1403/2892] tests/functional: Extend PPC 40p test with Linux boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fetch the cdrom image for the IBM 6015 PReP PowerPC machine hosted on the Juneau Linux Users Group site, boot and check Linux version. Reviewed-by: Thomas Huth Signed-off-by: Cédric Le Goater Message-ID: <20250129104844.1322100-1-clg@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_ppc_40p.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/test_ppc_40p.py index 7a74e0cca7..614972a7eb 100755 --- a/tests/functional/test_ppc_40p.py +++ b/tests/functional/test_ppc_40p.py @@ -9,6 +9,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern, skipUntrustedTest +from qemu_test import exec_command_and_wait_for_pattern class IbmPrep40pMachine(QemuSystemTest): @@ -72,5 +73,22 @@ class IbmPrep40pMachine(QemuSystemTest): self.vm.launch() wait_for_console_pattern(self, 'NetBSD/prep BOOT, Revision 1.9') + ASSET_40P_SANDALFOOT = Asset( + 'http://www.juneau-lug.org/zImage.initrd.sandalfoot', + '749ab02f576c6dc8f33b9fb022ecb44bf6a35a0472f2ea6a5e9956bc15933901') + + def test_openbios_and_linux(self): + self.set_machine('40p') + self.require_accelerator("tcg") + drive_path = self.ASSET_40P_SANDALFOOT.fetch() + self.vm.set_console() + self.vm.add_args('-cdrom', drive_path, + '-boot', 'd') + + self.vm.launch() + wait_for_console_pattern(self, 'Please press Enter to activate this console.') + exec_command_and_wait_for_pattern(self, '\012', '#') + exec_command_and_wait_for_pattern(self, 'uname -a', 'Linux ppc 2.4.18') + if __name__ == '__main__': QemuSystemTest.main() From 84dfdcbff33fff185528501be408c25c44499f32 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 17 Jan 2025 12:17:08 +0100 Subject: [PATCH 1404/2892] net: Fix announce_self b9ad513e1876 ("net: Remove receive_raw()") adds an iovec entry in qemu_deliver_packet_iov() to add the virtio-net header in the data when QEMU_NET_PACKET_FLAG_RAW is set but forgets to increase the number of iovec entries in the array, so receive_iov() will only send the first entry (the virtio-net entry, full of 0) and no data. The packet will be discarded. The only user of QEMU_NET_PACKET_FLAG_RAW is announce_self. We can see the problem with tcpdump: - QEMU parameters: .. -monitor stdio \ -netdev bridge,id=netdev0,br=virbr0 \ -device virtio-net,mac=9a:2b:2c:2d:2e:2f,netdev=netdev0 \ - HMP command: (qemu) announce_self - TCP dump: $ sudo tcpdump -nxi virbr0 without the fix: with the fix: ARP, Reverse Request who-is 9a:2b:2c:2d:2e:2f tell 9a:2b:2c:2d:2e:2f, length 46 0x0000: 0001 0800 0604 0003 9a2b 2c2d 2e2f 0000 0x0010: 0000 9a2b 2c2d 2e2f 0000 0000 0000 0000 0x0020: 0000 0000 0000 0000 0000 0000 0000 Reported-by: Xiaohui Li Bug: https://issues.redhat.com/browse/RHEL-73891 Fixes: b9ad513e1876 ("net: Remove receive_raw()") Cc: akihiko.odaki@daynix.com Signed-off-by: Laurent Vivier Reviewed-by: Akihiko Odaki Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- net/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/net.c b/net/net.c index c1bb19a523..9cded70dde 100644 --- a/net/net.c +++ b/net/net.c @@ -822,6 +822,7 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, iov_copy[0].iov_len = nc->vnet_hdr_len; memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov)); iov = iov_copy; + iovcnt++; } if (nc->info->receive_iov) { From c6a1b591a68b4d7230d6c3f56965e18080d737e5 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 17 Jan 2025 12:17:09 +0100 Subject: [PATCH 1405/2892] net/dump: Correctly compute Ethernet packet offset When a packet is sent with QEMU_NET_PACKET_FLAG_RAW by QEMU it never includes virtio-net header even if qemu_get_vnet_hdr_len() is not 0, and filter-dump is not managing this case. The only user of QEMU_NET_PACKET_FLAG_RAW is announce_self, we can show the problem using it and tcpddump: - QEMU parameters: .. -monitor stdio \ -netdev bridge,id=netdev0,br=virbr0 \ -device virtio-net,mac=9a:2b:2c:2d:2e:2f,netdev=netdev0 \ -object filter-dump,netdev=netdev0,file=log.pcap,id=pcap0 - HMP command: (qemu) announce_self - TCP dump: $ tcpdump -nxr log.pcap without the fix: 08:00:06:04:00:03 > 2e:2f:80:35:00:01, ethertype Unknown (0x9a2b), length 50: 0x0000: 2c2d 2e2f 0000 0000 9a2b 2c2d 2e2f 0000 0x0010: 0000 0000 0000 0000 0000 0000 0000 0000 0x0020: 0000 0000 with the fix: ARP, Reverse Request who-is 9a:2b:2c:2d:2e:2f tell 9a:2b:2c:2d:2e:2f, length 46 0x0000: 0001 0800 0604 0003 9a2b 2c2d 2e2f 0000 0x0010: 0000 9a2b 2c2d 2e2f 0000 0000 0000 0000 0x0020: 0000 0000 0000 0000 0000 0000 0000 Fixes: 481c52320a26 ("net: Strip virtio-net header when dumping") Cc: akihiko.odaki@daynix.com Signed-off-by: Laurent Vivier Reviewed-by: Akihiko Odaki Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- net/dump.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/dump.c b/net/dump.c index d7dd2ce461..140215aa10 100644 --- a/net/dump.c +++ b/net/dump.c @@ -155,7 +155,8 @@ static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, { NetFilterDumpState *nfds = FILTER_DUMP(nf); - dump_receive_iov(&nfds->ds, iov, iovcnt, qemu_get_vnet_hdr_len(nf->netdev)); + dump_receive_iov(&nfds->ds, iov, iovcnt, flags & QEMU_NET_PACKET_FLAG_RAW ? + 0 : qemu_get_vnet_hdr_len(nf->netdev)); return 0; } From 4ae633b012210452b68dc20238fe4edd41d2635b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 28 Jan 2025 16:28:35 +0100 Subject: [PATCH 1406/2892] tests/functional: Add a decorator for skipping long running tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some tests have a very long runtime and might run into timeout issues e.g. when QEMU has been compiled with --enable-debug. Add a decorator for marking them more easily. Rename the corresponding environment variable to be more in sync with the other QEMU_TEST_ALLOW_* switches that we already have, and add a paragraph about it in the documentation. Reviewed-by: Daniel P. Berrangé Message-ID: <20250128152839.184599-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 8 ++++++++ tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/decorators.py | 14 ++++++++++++++ tests/functional/test_aarch64_sbsaref_alpine.py | 5 ++--- tests/functional/test_aarch64_sbsaref_freebsd.py | 9 +++------ tests/functional/test_arm_quanta_gsj.py | 6 +++--- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index ae238ed3fc..ecc738922b 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -351,5 +351,13 @@ the code snippet below: Tests should not live in this state forever and should either be fixed or eventually removed. +QEMU_TEST_ALLOW_SLOW +^^^^^^^^^^^^^^^^^^^^ +Tests that have a very long runtime and might run into timeout issues +e.g. if the QEMU binary has been compiled with debugging options enabled. +To avoid these timeout issues by default and to save some precious CPU +cycles during normal testing, such tests are disabled by default unless +the QEMU_TEST_ALLOW_SLOW environment variable has been set. + .. _unittest: https://docs.python.org/3/library/unittest.html diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index da1830286d..5c972843a6 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -14,7 +14,7 @@ from .cmd import is_readable_executable_file, \ from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ - skipFlakyTest, skipUntrustedTest, skipBigDataTest, \ + skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ skipIfMissingImports from .archive import archive_extract from .uncompress import uncompress diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index 3d9c02fd59..1651eb739a 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -86,6 +86,20 @@ def skipBigDataTest(): return skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'Test requires large host storage space') +''' +Decorator to skip execution of tests which have a really long +runtime (and might e.g. time out if QEMU has been compiled with +debugging enabled) unless the $QEMU_TEST_ALLOW_SLOW +environment variable is set + +Example: + + @skipSlowTest() +''' +def skipSlowTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_SLOW'), + 'Test has a very long runtime and might time out') + ''' Decorator to skip execution of a test if the list of python imports is not available. diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py index 6dbc90f30e..ce974fd7e1 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -10,7 +10,7 @@ import os -from qemu_test import QemuSystemTest, Asset +from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware @@ -53,8 +53,7 @@ class Aarch64SbsarefAlpine(QemuSystemTest): def test_sbsaref_alpine_linux_max_pauth_impdef(self): self.boot_alpine_linux("max,pauth-impdef=on") - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), - 'Test might timeout due to PAuth emulation') + @skipSlowTest() # Test might timeout due to PAuth emulation def test_sbsaref_alpine_linux_max(self): self.boot_alpine_linux("max") diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index 77ba2ba1da..5b10bb9b64 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -10,9 +10,8 @@ import os -from qemu_test import QemuSystemTest, Asset +from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware @@ -50,13 +49,11 @@ class Aarch64SbsarefFreeBSD(QemuSystemTest): def test_sbsaref_freebsd14_max_pauth_off(self): self.boot_freebsd14("max,pauth=off") - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), - 'Test might timeout due to PAuth emulation') + @skipSlowTest() # Test might timeout due to PAuth emulation def test_sbsaref_freebsd14_max_pauth_impdef(self): self.boot_freebsd14("max,pauth-impdef=on") - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), - 'Test might timeout due to PAuth emulation') + @skipSlowTest() # Test might timeout due to PAuth emulation def test_sbsaref_freebsd14_max(self): self.boot_freebsd14("max") diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py index 7b82e2185c..da60aeb659 100755 --- a/tests/functional/test_arm_quanta_gsj.py +++ b/tests/functional/test_arm_quanta_gsj.py @@ -7,8 +7,8 @@ import os from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern -from qemu_test import interrupt_interactive_console_until_pattern -from unittest import skipUnless +from qemu_test import interrupt_interactive_console_until_pattern, skipSlowTest + class EmcraftSf2Machine(LinuxKernelTest): @@ -32,7 +32,7 @@ class EmcraftSf2Machine(LinuxKernelTest): '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb'), '3249b2da787d4b9ad4e61f315b160abfceb87b5e1895a7ce898ce7f40c8d4045') - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') + @skipSlowTest() def test_arm_quanta_gsj(self): self.set_machine('quanta-gsj') image_path = self.uncompress(self.ASSET_IMAGE, format='gz') From a2fe7bb751817f014a357e0234d2c3bfc8f85d10 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 28 Jan 2025 16:28:36 +0100 Subject: [PATCH 1407/2892] tests/functional: Add the ReplayKernelBase class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy the ReplayKernelBase class from the avocado tests. We are going to need it to convert the related replay tests in the following patches. Reviewed-by: Daniel P. Berrangé Message-ID: <20250128152839.184599-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/functional/replay_kernel.py | 84 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tests/functional/replay_kernel.py diff --git a/MAINTAINERS b/MAINTAINERS index 59c3c45f86..e880933a53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3633,6 +3633,7 @@ F: stubs/replay.c F: tests/avocado/replay_kernel.py F: tests/avocado/replay_linux.py F: tests/avocado/reverse_debugging.py +F: tests/functional/*replay*.py F: qapi/replay.json IOVA Tree diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py new file mode 100644 index 0000000000..8e8ac7d052 --- /dev/null +++ b/tests/functional/replay_kernel.py @@ -0,0 +1,84 @@ +# Record/replay test that boots a Linux kernel +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import logging +import time +import subprocess + +from qemu_test.linuxkernel import LinuxKernelTest + +class ReplayKernelBase(LinuxKernelTest): + """ + Boots a Linux kernel in record mode and checks that the console + is operational and the kernel command line is properly passed + from QEMU to the kernel. + Then replays the same scenario and verifies, that QEMU correctly + terminates. + """ + + timeout = 180 + REPLAY_KERNEL_COMMAND_LINE = 'printk.time=1 panic=-1 ' + + def run_vm(self, kernel_path, kernel_command_line, console_pattern, + record, shift, args, replay_path): + # icount requires TCG to be available + self.require_accelerator('tcg') + + logger = logging.getLogger('replay') + start_time = time.time() + vm = self.get_vm() + vm.set_console() + if record: + logger.info('recording the execution...') + mode = 'record' + else: + logger.info('replaying the execution...') + mode = 'replay' + vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % + (shift, mode, replay_path), + '-kernel', kernel_path, + '-append', kernel_command_line, + '-net', 'none', + '-no-reboot') + if args: + vm.add_args(*args) + vm.launch() + self.wait_for_console_pattern(console_pattern, vm) + if record: + vm.shutdown() + logger.info('finished the recording with log size %s bytes' + % os.path.getsize(replay_path)) + self.run_replay_dump(replay_path) + logger.info('successfully tested replay-dump.py') + else: + vm.wait() + logger.info('successfully finished the replay') + elapsed = time.time() - start_time + logger.info('elapsed time %.2f sec' % elapsed) + return elapsed + + def run_replay_dump(self, replay_path): + try: + subprocess.check_call(["./scripts/replay-dump.py", + "-f", replay_path], + stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.fail('replay-dump.py failed') + + def run_rr(self, kernel_path, kernel_command_line, console_pattern, + shift=7, args=None): + replay_path = os.path.join(self.workdir, 'replay.bin') + t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + True, shift, args, replay_path) + t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + False, shift, args, replay_path) + logger = logging.getLogger('replay') + logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) From 350a998d856d1a4274acef983c6d443b233b4f33 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 28 Jan 2025 16:28:37 +0100 Subject: [PATCH 1408/2892] tests/functional/test_mipsel_malta: Convert the mipsel replay tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the mipsel replay tests from tests/avocado/replay_kernel.py to the functional framework. Since the functional tests should be run per target, we cannot stick all replay tests in one file. Thus let's add these tests to a new, separate file there instead. Reviewed-by: Daniel P. Berrangé Message-ID: <20250128152839.184599-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/replay_kernel.py | 54 -------------------------- tests/functional/meson.build | 2 + tests/functional/test_mipsel_replay.py | 54 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 54 deletions(-) create mode 100644 tests/functional/test_mipsel_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index e22c200a36..4f50f48163 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -521,57 +521,3 @@ class ReplayKernelSlow(ReplayKernelBase): console_pattern = 'Boot successful.' self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, args=('-initrd', initrd_path)) - - def do_test_mips_malta32el_nanomips(self, kernel_path_xz): - kernel_path = self.workdir + "kernel" - with lzma.open(kernel_path_xz, 'rb') as f_in: - with open(kernel_path, 'wb') as f_out: - shutil.copyfileobj(f_in, f_out) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'mem=256m@@0x0 ' - 'console=ttyS0') - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_mips_malta32el_nanomips_4k(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page4k.xz') - kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) - - def test_mips_malta32el_nanomips_16k_up(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page16k_up.xz') - kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) - - def test_mips_malta32el_nanomips_64k_dbg(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page64k_dbg.xz') - kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 2e0802144a..9049e2f142 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -35,6 +35,7 @@ test_timeouts = { 'arm_sx1' : 360, 'intel_iommu': 300, 'mips_malta' : 120, + 'mipsel_replay' : 480, 'netdev_ethtool' : 180, 'ppc_40p' : 240, 'ppc64_hv' : 1000, @@ -162,6 +163,7 @@ tests_mips_system_thorough = [ tests_mipsel_system_thorough = [ 'mipsel_malta', + 'mipsel_replay', 'mipsel_tuxrun', ] diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/test_mipsel_replay.py new file mode 100644 index 0000000000..0a330de43f --- /dev/null +++ b/tests/functional/test_mipsel_replay.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 32-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, wait_for_console_pattern, skipSlowTest +from replay_kernel import ReplayKernelBase + + +class MipselReplay(ReplayKernelBase): + + ASSET_KERNEL_4K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page4k.xz'), + '019e034094ac6cf3aa77df5e130fb023ce4dbc804b04bfcc560c6403e1ae6bdb') + ASSET_KERNEL_16K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page16k_up.xz'), + '3a54a10b3108c16a448dca9ea3db378733a27423befc2a45a5bdf990bd85e12c') + ASSET_KERNEL_64K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page64k_dbg.xz'), + 'ce21ff4b07a981ecb8a39db2876616f5a2473eb2ab459c6f67465b9914b0c6b6') + + def do_test_replay_mips_malta32el_nanomips(self, kernel_asset): + self.set_machine('malta') + self.cpu = 'I7200' + kernel_path = self.uncompress(kernel_asset) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'mem=256m@@0x0 ' + 'console=ttyS0') + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_4k(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_4K) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_16k_up(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_16K) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_64k_dbg(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) + + +if __name__ == '__main__': + ReplayKernelBase.main() From f348229ec7e7019c93a66754e363bcb4977b9024 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 28 Jan 2025 16:28:38 +0100 Subject: [PATCH 1409/2892] tests/functional/test_mips64el_malta: Convert the mips64el replay tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the mips64el replay tests from tests/avocado/replay_kernel.py to the functional framework. Since the functional tests should be run per target, we cannot stick all replay tests in one file. Thus let's add these tests to a separate file there now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250128152839.184599-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/replay_kernel.py | 57 ---------------------- tests/functional/meson.build | 1 + tests/functional/test_mips64el_replay.py | 60 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 57 deletions(-) create mode 100755 tests/functional/test_mips64el_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 4f50f48163..a45881b9a6 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -162,33 +162,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - def test_mips64el_malta(self): - """ - This test requires the ar tool to extract "data.tar.gz" from - the Debian package. - - The kernel can be rebuilt using this Debian kernel source [1] and - following the instructions on [2]. - - [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/ - #linux-source-2.6.32_2.6.32-48 - [2] https://kernel-team.pages.debian.net/kernel-handbook/ - ch-common-tasks.html#s-common-official - - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20130217T032700Z/pool/main/l/linux-2.6/' - 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb') - deb_hash = '1aaec92083bf22fda31e0d27fa8d9a388e5fc3d5' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-5kc-malta') - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - def test_aarch64_virt(self): """ :avocado: tags=arch:aarch64 @@ -491,33 +464,3 @@ class ReplayKernelSlow(ReplayKernelBase): console_pattern = 'Boot successful.' self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, args=('-initrd', initrd_path)) - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_mips64el_malta_5KEc_cpio(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=slowness:high - :avocado: tags=cpu:5KEc - """ - kernel_url = ('https://github.com/philmd/qemu-testing-blob/' - 'raw/9ad2df38/mips/malta/mips64el/' - 'vmlinux-3.19.3.mtoman.20150408') - kernel_hash = '00d1d268fb9f7d8beda1de6bebcc46e884d71754' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - initrd_url = ('https://github.com/groeck/linux-build-test/' - 'raw/8584a59e/rootfs/' - 'mipsel64/rootfs.mipsel64r1.cpio.gz') - initrd_hash = '1dbb8a396e916847325284dbe2151167' - initrd_path_gz = self.fetch_asset(initrd_url, algorithm='md5', - asset_hash=initrd_hash) - initrd_path = self.workdir + "rootfs.cpio" - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 console=tty ' - 'rdinit=/sbin/init noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, - args=('-initrd', initrd_path)) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 9049e2f142..cb24e0bb00 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -175,6 +175,7 @@ tests_mips64el_system_thorough = [ 'mips64el_fuloong2e', 'mips64el_loongson3v', 'mips64el_malta', + 'mips64el_replay', 'mips64el_tuxrun', ] diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/test_mips64el_replay.py new file mode 100755 index 0000000000..4f63d7fb34 --- /dev/null +++ b/tests/functional/test_mips64el_replay.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import logging + +from qemu_test import Asset, exec_command_and_wait_for_pattern +from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest +from replay_kernel import ReplayKernelBase + + +class Mips64elReplay(ReplayKernelBase): + + ASSET_KERNEL_2_63_2 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20130217T032700Z/pool/main/l/linux-2.6/' + 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb'), + '35eb476f03be589824b0310358f1c447d85e645b88cbcd2ac02b97ef560f9f8d') + + def test_replay_mips64el_malta(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-5kc-malta') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + + ASSET_KERNEL_3_19_3 = Asset( + ('https://github.com/philmd/qemu-testing-blob/' + 'raw/9ad2df38/mips/malta/mips64el/' + 'vmlinux-3.19.3.mtoman.20150408'), + '8d3beb003bc66051ead98e7172139017fcf9ce2172576541c57e86418dfa5ab8') + + ASSET_CPIO_R1 = Asset( + ('https://github.com/groeck/linux-build-test/' + 'raw/8584a59e/rootfs/mipsel64/' + 'rootfs.mipsel64r1.cpio.gz'), + '75ba10cd35fb44e32948eeb26974f061b703c81c4ba2fab1ebcacf1d1bec3b61') + + @skipUntrustedTest() + def test_replay_mips64el_malta_5KEc_cpio(self): + self.set_machine('malta') + self.cpu = '5KEc' + kernel_path = self.ASSET_KERNEL_3_19_3.fetch() + initrd_path = self.uncompress(self.ASSET_CPIO_R1) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + 'rdinit=/sbin/init noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=('-initrd', initrd_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() From f194f0cab092f976c80e2ec226aa20fb9c35cb1f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 28 Jan 2025 16:28:39 +0100 Subject: [PATCH 1410/2892] tests/functional/test_mips_malta: Convert the mips big endian replay tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the mips big endian replay tests from tests/avocado/replay_kernel.py to the functional framework. Since the functional tests should be run per target, we cannot stick all replay tests in one file. Thus let's add these tests to a separate file now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250128152839.184599-6-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/replay_kernel.py | 54 --------------------------- tests/functional/meson.build | 1 + tests/functional/test_mips_replay.py | 55 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 54 deletions(-) create mode 100755 tests/functional/test_mips_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index a45881b9a6..b9b54a8793 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -144,24 +144,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - def test_mips_malta(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20130217T032700Z/pool/main/l/linux-2.6/' - 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb') - deb_hash = 'a8cfc28ad8f45f54811fc6cf74fc43ffcfe0ba04' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-4kc-malta') - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - def test_aarch64_virt(self): """ :avocado: tags=arch:aarch64 @@ -428,39 +410,3 @@ class ReplayKernelNormal(ReplayKernelBase): '/qac-best-of-multiarch/download/day02.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf') - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayKernelSlow(ReplayKernelBase): - # Override the timeout, because this kernel includes an inner - # loop which is executed with TB recompilings during replay, - # making it very slow. - timeout = 180 - - def test_mips_malta_cpio(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - :avocado: tags=slowness:high - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20160601T041800Z/pool/main/l/linux/' - 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb') - deb_hash = 'a3c84f3e88b54e06107d65a410d1d1e8e0f340f8' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-4.5.0-2-4kc-malta') - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/' - 'mips/rootfs.cpio.gz') - initrd_hash = 'bf806e17009360a866bf537f6de66590de349a99' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = self.workdir + "rootfs.cpio" - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 console=tty ' - 'rdinit=/sbin/init noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, - args=('-initrd', initrd_path)) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index cb24e0bb00..3f085bfbca 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -158,6 +158,7 @@ tests_microblazeel_system_thorough = [ tests_mips_system_thorough = [ 'mips_malta', + 'mips_replay', 'mips_tuxrun', ] diff --git a/tests/functional/test_mips_replay.py b/tests/functional/test_mips_replay.py new file mode 100755 index 0000000000..eda031ccad --- /dev/null +++ b/tests/functional/test_mips_replay.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# Replay tests for the big-endian 32-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipSlowTest, exec_command_and_wait_for_pattern +from replay_kernel import ReplayKernelBase + + +class MipsReplay(ReplayKernelBase): + + ASSET_KERNEL_2_63_2 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20130217T032700Z/pool/main/l/linux-2.6/' + 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb'), + '16ca524148afb0626f483163e5edf352bc1ab0e4fc7b9f9d473252762f2c7a43') + + def test_replay_mips_malta(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-4kc-malta') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + ASSET_KERNEL_4_5_0 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20160601T041800Z/pool/main/l/linux/' + 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb'), + '526b17d5889840888b76fc2c36a0ebde182c9b1410a3a1e68203c3b160eb2027') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/' + 'mips/rootfs.cpio.gz'), + 'dcfe3a7fe3200da3a00d176b95caaa086495eb158f2bff64afc67d7e1eb2cddc') + + @skipSlowTest() + def test_replay_mips_malta_cpio(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_4_5_0, + member='boot/vmlinux-4.5.0-2-4kc-malta') + initrd_path = self.uncompress(self.ASSET_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + 'rdinit=/sbin/init noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=('-initrd', initrd_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 6003402aa91053c6884f1cf324692dcc035df96c Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 19 Jan 2025 12:35:47 +0300 Subject: [PATCH 1411/2892] vvfat: create_long_filename: fix out-of-bounds array access create_long_filename() intentionally uses direntry_t->name[8+3] array as a larger array. This works, but makes static code analysis tools unhappy. The problem here is that a directory entry holding long file name is significantly different from regular directory entry, and the name is split into several parts within the entry, not just in regular 8+3 name field. Treat the entry as array of bytes instead. This fixes the OOB access from the compiler/tools PoV, but does not change the resulting code in any way. Keep the existing code style. Signed-off-by: Michael Tokarev --- block/vvfat.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/block/vvfat.c b/block/vvfat.c index 8ffe8b3b9b..bfbcc5562c 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -403,7 +403,6 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename) { int number_of_entries, i; glong length; - direntry_t *entry; gunichar2 *longname = g_utf8_to_utf16(filename, -1, NULL, &length, NULL); if (!longname) { @@ -414,24 +413,24 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename) number_of_entries = DIV_ROUND_UP(length * 2, 26); for(i=0;idirectory)); + direntry_t *entry=array_get_next(&(s->directory)); entry->attributes=0xf; entry->reserved[0]=0; entry->begin=0; entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); } for(i=0;i<26*number_of_entries;i++) { + unsigned char *entry=array_get(&(s->directory),s->directory.next-1-(i/26)); int offset=(i%26); if(offset<10) offset=1+offset; else if(offset<22) offset=14+offset-10; else offset=28+offset-22; - entry=array_get(&(s->directory),s->directory.next-1-(i/26)); if (i >= 2 * length + 2) { - entry->name[offset] = 0xff; + entry[offset] = 0xff; } else if (i % 2 == 0) { - entry->name[offset] = longname[i / 2] & 0xff; + entry[offset] = longname[i / 2] & 0xff; } else { - entry->name[offset] = longname[i / 2] >> 8; + entry[offset] = longname[i / 2] >> 8; } } g_free(longname); From 8b647bd352505234cab2acd2422aba183a1aa1fd Mon Sep 17 00:00:00 2001 From: Dominik 'Disconnect3d' Czarnota Date: Mon, 20 Jan 2025 23:28:58 +0100 Subject: [PATCH 1412/2892] gdbstub/user-target: fix gdbserver int format (%d -> %x) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes an incorrect format string for formatting integers provided to GDB when debugging a target run in QEMU user mode. The correct format is hexadecimal for both success and errno values, some of which can be seen here [0]. [0] https://github.com/bminor/binutils-gdb/blob/e65a355022d0dc6b5707310876a72b5693ec0aa5/gdbserver/hostio.cc#L196-L213 Signed-off-by: Dominik 'Disconnect3d' Czarnota Reviewed-by: Alex Bennée Fixes: e282010b2e1e ("gdbstub: Add support for info proc mappings") Cc: qemu-stable@nongnu.org Reviewed-by: Ilya Leoshkevich Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- gdbstub/user-target.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c index 22bf4008c0..4bfcf78aaa 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -317,9 +317,9 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx) int fd = open(filename, flags, mode); #endif if (fd < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); } else { - g_string_printf(gdbserver_state.str_buf, "F%d", fd); + g_string_printf(gdbserver_state.str_buf, "F%x", fd); } gdb_put_strbuf(); } @@ -329,7 +329,7 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx) int fd = gdb_get_cmd_param(params, 0)->val_ul; if (close(fd) == -1) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -352,7 +352,7 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx) ssize_t n = pread(fd, buf, bufsiz, offset); if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -375,7 +375,7 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) ssize_t n = readlink(filename, buf, BUFSIZ); #endif if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } From 394388dfdb413d50167b2efc47ee9f2432657482 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 27 Jan 2025 19:41:10 +0100 Subject: [PATCH 1413/2892] tests/functional/test_mips_malta: Fix comment about endianness of the test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test is for the big endian MIPS target, not for the little endian target. Signed-off-by: Thomas Huth Fixes: 79cb4a14cb6 ("tests/functional: Convert mips32eb 4Kc Malta tests") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- tests/functional/test_mips_malta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index 3b15038d89..eaf372255b 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Functional tests for the little-endian 32-bit MIPS Malta board +# Functional tests for the big-endian 32-bit MIPS Malta board # # Copyright (c) Philippe Mathieu-Daudé # From d6f7642230f15c5e470a8988b31980ff570124f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Jan 2025 17:05:10 +0100 Subject: [PATCH 1414/2892] licenses: Remove SPDX tags not being license identifier for Linaro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per [*]: "we're only interested in adopting SPDX for recording the licensing info, [not] any other SPDX metadata." Replace the 'SPDX-FileCopyrightText' and 'SPDX-FileContributor' tags added by Linaro by 'Copyright (c)' and 'Authors' words respectively. [*] https://lore.kernel.org/qemu-devel/20241007154548.1144961-4-berrange@redhat.com/ Inspired-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- accel/tcg/vcpu-state.h | 9 +++++++-- hw/misc/ivshmem-flat.c | 5 +++-- include/hw/misc/ivshmem-flat.h | 5 +++-- scripts/qom-cast-macro-clean-cocci-gen.py | 7 +++++-- target/m68k/semihosting-stub.c | 7 +++++-- target/mips/tcg/system/semihosting-stub.c | 5 +++-- tests/functional/test_aarch64_sbsaref.py | 8 +++++--- tests/functional/test_aarch64_sbsaref_alpine.py | 8 +++++--- tests/functional/test_aarch64_sbsaref_freebsd.py | 8 +++++--- tests/qtest/libqos/virtio-scmi.c | 2 +- 10 files changed, 42 insertions(+), 22 deletions(-) diff --git a/accel/tcg/vcpu-state.h b/accel/tcg/vcpu-state.h index e407d914df..2e3464b5ee 100644 --- a/accel/tcg/vcpu-state.h +++ b/accel/tcg/vcpu-state.h @@ -1,6 +1,11 @@ /* - * SPDX-FileContributor: Philippe Mathieu-Daudé - * SPDX-FileCopyrightText: 2023 Linaro Ltd. + * TaskState helpers for QEMU + * + * Copyright (c) 2023 Linaro Ltd. + * + * Authors: + * Philippe Mathieu-Daudé + * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef ACCEL_TCG_VCPU_STATE_H diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index 33fc9425d2..40309a8ff3 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -1,9 +1,10 @@ /* * Inter-VM Shared Memory Flat Device * - * SPDX-FileCopyrightText: 2023 Linaro Ltd. - * SPDX-FileContributor: Gustavo Romero * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2023 Linaro Ltd. + * Authors: + * Gustavo Romero * */ diff --git a/include/hw/misc/ivshmem-flat.h b/include/hw/misc/ivshmem-flat.h index 97ca0ddce6..0c2b015781 100644 --- a/include/hw/misc/ivshmem-flat.h +++ b/include/hw/misc/ivshmem-flat.h @@ -1,9 +1,10 @@ /* * Inter-VM Shared Memory Flat Device * - * SPDX-FileCopyrightText: 2023 Linaro Ltd. - * SPDX-FileContributor: Gustavo Romero * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2023 Linaro Ltd. + * Authors: + * Gustavo Romero * */ diff --git a/scripts/qom-cast-macro-clean-cocci-gen.py b/scripts/qom-cast-macro-clean-cocci-gen.py index 2fa8438a14..5aa51d0c18 100644 --- a/scripts/qom-cast-macro-clean-cocci-gen.py +++ b/scripts/qom-cast-macro-clean-cocci-gen.py @@ -13,8 +13,11 @@ # --in-place \ # --dir . # -# SPDX-FileContributor: Philippe Mathieu-Daudé -# SPDX-FileCopyrightText: 2023 Linaro Ltd. +# Copyright (c) 2023 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# # SPDX-License-Identifier: GPL-2.0-or-later import re diff --git a/target/m68k/semihosting-stub.c b/target/m68k/semihosting-stub.c index d6a5965e29..dbe669cc5f 100644 --- a/target/m68k/semihosting-stub.c +++ b/target/m68k/semihosting-stub.c @@ -1,8 +1,11 @@ /* * m68k/ColdFire semihosting stub * - * SPDX-FileContributor: Philippe Mathieu-Daudé - * SPDX-FileCopyrightText: 2024 Linaro Ltd. + * Copyright (c) 2024 Linaro Ltd. + * + * Authors: + * Philippe Mathieu-Daudé + * * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/target/mips/tcg/system/semihosting-stub.c b/target/mips/tcg/system/semihosting-stub.c index 7ae27d746f..bb1f7aae62 100644 --- a/target/mips/tcg/system/semihosting-stub.c +++ b/target/mips/tcg/system/semihosting-stub.c @@ -1,9 +1,10 @@ /* * MIPS semihosting stub * - * SPDX-FileContributor: Philippe Mathieu-Daudé - * SPDX-FileCopyrightText: 2024 Linaro Ltd. * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2024 Linaro Ltd. + * Authors: + * Philippe Mathieu-Daudé */ #include "qemu/osdep.h" diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index 99cfb6f29a..e6a55aecfa 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -2,9 +2,11 @@ # # Functional test that boots a kernel and checks the console # -# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé -# SPDX-FileContributor: Marcin Juszkiewicz +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz # # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py index 6dbc90f30e..9faf066d18 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -2,9 +2,11 @@ # # Functional test that boots a kernel and checks the console # -# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé -# SPDX-FileContributor: Marcin Juszkiewicz +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz # # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index 77ba2ba1da..8dcb4991c3 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -2,9 +2,11 @@ # # Functional test that boots a kernel and checks the console # -# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé -# SPDX-FileContributor: Marcin Juszkiewicz +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz # # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/tests/qtest/libqos/virtio-scmi.c b/tests/qtest/libqos/virtio-scmi.c index ce8f4d5c06..6b5bd4db42 100644 --- a/tests/qtest/libqos/virtio-scmi.c +++ b/tests/qtest/libqos/virtio-scmi.c @@ -1,7 +1,7 @@ /* * virtio-scmi nodes for testing * - * SPDX-FileCopyrightText: Linaro Ltd + * Copyright (c) Linaro Ltd. * SPDX-FileCopyrightText: Red Hat, Inc. * SPDX-License-Identifier: GPL-2.0-or-later * From 6a784f12000582b9f0f40fadc967ad474fc27c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 00:22:27 +0100 Subject: [PATCH 1415/2892] hw/i386/pc: Remove unused pc_compat_2_3 declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We removed the implementations in commit 46a2bd52571 ("hw/i386/pc: Remove deprecated pc-i440fx-2.3 machine") but forgot to remove the declarations. Do it now. Fixes: 46a2bd52571 ("hw/i386/pc: Remove deprecated pc-i440fx-2.3 machine") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- include/hw/i386/pc.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index a558705cb9..103b54301f 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -302,9 +302,6 @@ extern const size_t pc_compat_2_5_len; extern GlobalProperty pc_compat_2_4[]; extern const size_t pc_compat_2_4_len; -extern GlobalProperty pc_compat_2_3[]; -extern const size_t pc_compat_2_3_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ From 2177d0c17737bd529bc7885ce6b8616fdbbc74c3 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 30 Jan 2025 13:36:24 +0100 Subject: [PATCH 1416/2892] MAINTAINERS: Add myself as HPPA maintainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since I contribute quite some code to hppa, I'd like to step up and become the secondary maintainer for HPPA beside Richard. Additionally change status of hppa machines to maintained as I will take care of them. Signed-off-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- MAINTAINERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7be3d8f431..dbf39cfbb0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -251,6 +251,7 @@ F: target/hexagon/gen_idef_parser_funcs.py HPPA (PA-RISC) TCG CPUs M: Richard Henderson +M: Helge Deller S: Maintained F: target/hppa/ F: disas/hppa.c @@ -1188,8 +1189,8 @@ HP-PARISC Machines ------------------ HP B160L, HP C3700 M: Richard Henderson -R: Helge Deller -S: Odd Fixes +M: Helge Deller +S: Maintained F: configs/devices/hppa-softmmu/default.mak F: hw/display/artist.c F: hw/hppa/ From 3f8c3d7bf6d4d3454cd1e56b4bb253c301d72ca2 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 28 Jan 2025 15:34:30 +0100 Subject: [PATCH 1417/2892] hppa: Sync contents of hppa_hardware.h header file with SeaBIOS-hppa The hppa_hardware.h header file holds many constants for addresses and offsets which are needed while building the firmware (SeaBIOS-hppa) and while setting up the virtual machine in QEMU. This patch brings it in sync between both source code repositories. Signed-off-by: Helge Deller Acked-by: Richard Henderson --- hw/hppa/hppa_hardware.h | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index a276240967..21c777cba6 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -6,6 +6,11 @@ #define FIRMWARE_START 0xf0000000 #define FIRMWARE_END 0xf0800000 +#define FIRMWARE_HIGH 0xfffffff0 /* upper 32-bits of 64-bit firmware address */ + +#define RAM_MAP_HIGH 0x0100000000 /* memory above 3.75 GB is mapped here */ + +#define MEM_PDC_ENTRY 0x4800 /* PDC entry address */ #define DEVICE_HPA_LEN 0x00100000 @@ -18,6 +23,7 @@ #define LASI_UART_HPA 0xffd05000 #define LASI_SCSI_HPA 0xffd06000 #define LASI_LAN_HPA 0xffd07000 +#define LASI_RTC_HPA 0xffd09000 #define LASI_LPT_HPA 0xffd02000 #define LASI_AUDIO_HPA 0xffd04000 #define LASI_PS2KBD_HPA 0xffd08000 @@ -27,16 +33,23 @@ #define CPU_HPA 0xfffb0000 #define MEMORY_HPA 0xfffff000 -#define PCI_HPA DINO_HPA /* PCI bus */ #define IDE_HPA 0xf9000000 /* Boot disc controller */ +#define ASTRO_HPA 0xfed00000 +#define ELROY0_HPA 0xfed30000 +#define ELROY2_HPA 0xfed32000 +#define ELROY8_HPA 0xfed38000 +#define ELROYc_HPA 0xfed3c000 +#define ASTRO_MEMORY_HPA 0xfed10200 + +#define SCSI_HPA 0xf1040000 /* emulated SCSI, needs to be in f region */ /* offsets to DINO HPA: */ #define DINO_PCI_ADDR 0x064 #define DINO_CONFIG_DATA 0x068 #define DINO_IO_DATA 0x06c -#define PORT_PCI_CMD (PCI_HPA + DINO_PCI_ADDR) -#define PORT_PCI_DATA (PCI_HPA + DINO_CONFIG_DATA) +#define PORT_PCI_CMD hppa_port_pci_cmd +#define PORT_PCI_DATA hppa_port_pci_data #define FW_CFG_IO_BASE 0xfffa0000 @@ -46,9 +59,24 @@ #define HPPA_MAX_CPUS 16 /* max. number of SMP CPUs */ #define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */ +#define CR_PSW_DEFAULT 6 /* used by SeaBIOS & QEMU for default PSW */ #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) */ -#define RAM_MAP_HIGH 0x0100000000 /* memory above 3.75 GB is mapped here */ +#define ASTRO_BUS_MODULE 0x0a /* C3700: 0x0a, others maybe 0 ? */ + +/* ASTRO Memory and I/O regions */ +#define ASTRO_BASE_HPA 0xfffed00000 +#define ELROY0_BASE_HPA 0xfffed30000 /* ELROY0_HPA */ + +#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ + +#define LMMIO_DIRECT0_BASE 0x300 +#define LMMIO_DIRECT0_MASK 0x308 +#define LMMIO_DIRECT0_ROUTE 0x310 + +/* space register hashing */ +#define HPPA64_DIAG_SPHASH_ENABLE 0x200 /* DIAG_SPHASH_ENAB (bit 54) */ +#define HPPA64_PDC_CACHE_RET_SPID_VAL 0xfe0 /* PDC return value on 64-bit CPU */ #endif From bf60e2a72adc190d3a4d8eb28b6f9c4c17ddd649 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 28 Jan 2025 15:37:41 +0100 Subject: [PATCH 1418/2892] disas/hppa: implement mfdiag/mtdiag disassembly The various PA-RISC CPUs implement different CPU-specific diag instructions (mfdiag, mtdiag, mfcpu, mtcpu, ...) to access CPU-internal diagnose/configuration registers, e.g. for cache control, managing space register hashing, control front panel LEDs and read status of the hardware reset button. Those instructions are mostly undocumented, but are used by ODE, HP-UX and Linux. This patch adds some neccessary instructions for PCXL and PCXU CPUs. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- disas/hppa.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/disas/hppa.c b/disas/hppa.c index 49e2231ae6..2b58434966 100644 --- a/disas/hppa.c +++ b/disas/hppa.c @@ -606,7 +606,7 @@ struct pa_opcode In the args field, the following characters are unused: - ' " - / 34 6789:; ' + ' " - / 34 678 :; ' '@ C M [\] ' '` e g } ' @@ -650,6 +650,7 @@ Also these: | 6 bit field length at 19,27:31 (fixed extract/deposit) A 13 bit immediate at 18 (to support the BREAK instruction) ^ like b, but describes a control register + 9 like b, but describes a diagnose register ! sar (cr11) register D 26 bit immediate at 31 (to support the DIAG instruction) $ 9 bit immediate at 28 (to support POPBTS) @@ -1322,13 +1323,19 @@ static const struct pa_opcode pa_opcodes[] = { "fdce", 0x040012c0, 0xfc00ffdf, "cZx(b)", pa10, 0}, { "fdce", 0x040012c0, 0xfc003fdf, "cZx(s,b)", pa10, 0}, { "fice", 0x040002c0, 0xfc001fdf, "cZx(S,b)", pa10, 0}, -{ "diag", 0x14000000, 0xfc000000, "D", pa10, 0}, { "idtlbt", 0x04001800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, { "iitlbt", 0x04000800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, +/* completely undocumented, but used by ODE, HP-UX and Linux: */ +{ "mfcpu_pcxu", 0x140008a0, 0xfc9fffe0, "9,t", pa20, 0}, /* PCXU: mfdiag */ +{ "mtcpu_pcxu", 0x14001840, 0xfc00ffff, "x,9", pa20, 0}, + /* These may be specific to certain versions of the PA. Joel claimed they were 72000 (7200?) specific. However, I'm almost certain the mtcpu/mfcpu were undocumented, but available in the older 700 machines. */ +{ "mfcpu_c", 0x14000600, 0xfc00ffff, "9,x", pa10, 0}, /* PCXL: for dr0 and dr8 only */ +{ "mfcpu_t", 0x14001400, 0xfc9fffe0, "9,t", pa10, 0}, /* PCXL: all dr except dr0 and dr8 */ +{ "mtcpu_pcxl", 0x14000240, 0xfc00ffff, "x,9", pa11, 0}, /* PCXL: mtcpu for dr0 and dr8 */ { "mtcpu", 0x14001600, 0xfc00ffff, "x,^", pa10, 0}, { "mfcpu", 0x14001A00, 0xfc00ffff, "^,x", pa10, 0}, { "tocen", 0x14403600, 0xffffffff, "", pa10, 0}, @@ -1336,6 +1343,9 @@ static const struct pa_opcode pa_opcodes[] = { "shdwgr", 0x14402600, 0xffffffff, "", pa10, 0}, { "grshdw", 0x14400620, 0xffffffff, "", pa10, 0}, +/* instead of showing D only, show all other registers too */ +{ "diag", 0x14000000, 0xfc000000, "D x,9,t", pa10, 0}, + /* gfw and gfr are not in the HP PA 1.1 manual, but they are in either the Timex FPU or the Mustang ERS (not sure which) manual. */ { "gfw", 0x04001680, 0xfc00ffdf, "cZx(b)", pa11, 0}, @@ -1801,6 +1811,12 @@ fput_creg (unsigned reg, disassemble_info *info) (*info->fprintf_func) (info->stream, "%s", control_reg[reg]); } +static void +fput_dreg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "dr%d", reg); +} + /* Print constants with sign. */ static void @@ -2007,6 +2023,9 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info) case '^': fput_creg (GET_FIELD (insn, 6, 10), info); break; + case '9': + fput_dreg (GET_FIELD (insn, 6, 10), info); + break; case 't': fput_reg (GET_FIELD (insn, 27, 31), info); break; From 8f2a1c5926bf933abce54de23d5ae44371c50e23 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 28 Jan 2025 19:47:31 +0100 Subject: [PATCH 1419/2892] target/hppa: Add CPU diagnose registers Add the diagnose registers (%dr) to the CPUArchState. Those are mostly undocumented and control cache behaviour, memory behaviour, reset button management and many other related internal CPU things. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- target/hppa/cpu.h | 1 + target/hppa/machine.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index beea42d105..b858986c41 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -232,6 +232,7 @@ typedef struct CPUArchState { target_ulong cr[32]; /* control registers */ target_ulong cr_back[2]; /* back of cr17/cr18 */ target_ulong shadow[7]; /* shadow registers */ + target_ulong dr[32]; /* diagnose registers */ /* * During unwind of a memory insn, the base register of the address. diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 211bfcf640..bb47a2e689 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -198,6 +198,7 @@ static const VMStateField vmstate_env_fields[] = { VMSTATE_UINT64(iasq_b, CPUHPPAState), VMSTATE_UINT32(fr0_shadow, CPUHPPAState), + VMSTATE_UINT64_ARRAY(dr, CPUHPPAState, 32), VMSTATE_END_OF_LIST() }; @@ -208,8 +209,8 @@ static const VMStateDescription * const vmstate_env_subsections[] = { static const VMStateDescription vmstate_env = { .name = "env", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = vmstate_env_fields, .subsections = vmstate_env_subsections, }; From 009e0927f3d8cc852a6fee928dca52b5e9bd2ced Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 29 Jan 2025 04:27:01 +0100 Subject: [PATCH 1420/2892] target/hppa: Drop diag_getshadowregs_pa2 and diag_putshadowregs_pa2 diag_getshadowregs_pa2() and diag_putshadowregs_pa2() were added in commit 3bdf20819e68 based on some analysis of ODE code, but now they conflict with the generic mfdiag/mtdiag instructions. I believe the former analysis was wrong, so remove them again. Note that all diag instructions are badly documented, so most things are based on reverse engineering and thus may be wrong. Signed-off-by: Helge Deller Fixes: 3bdf20819e68 ("target/hppa: Add diag instructions to set/restore shadow registers") Reviewed-by: Richard Henderson --- target/hppa/insns.decode | 2 -- target/hppa/translate.c | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 71074a64c1..527c453443 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -646,8 +646,6 @@ xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64 diag_putshadowregs_pa1 000101 00 0000 0000 0001 1010 0100 0000 # For 64-bit PA8700 (PCX-W2) - diag_getshadowregs_pa2 000101 00 0111 1000 0001 1000 0100 0000 - diag_putshadowregs_pa2 000101 00 0111 0000 0001 1000 0100 0000 ] diag_unimp 000101 i:26 } diff --git a/target/hppa/translate.c b/target/hppa/translate.c index dc04f9f3c0..30fba5297a 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4593,21 +4593,11 @@ static bool trans_diag_getshadowregs_pa1(DisasContext *ctx, arg_empty *a) return !ctx->is_pa20 && do_getshadowregs(ctx); } -static bool trans_diag_getshadowregs_pa2(DisasContext *ctx, arg_empty *a) -{ - return ctx->is_pa20 && do_getshadowregs(ctx); -} - static bool trans_diag_putshadowregs_pa1(DisasContext *ctx, arg_empty *a) { return !ctx->is_pa20 && do_putshadowregs(ctx); } -static bool trans_diag_putshadowregs_pa2(DisasContext *ctx, arg_empty *a) -{ - return ctx->is_pa20 && do_putshadowregs(ctx); -} - static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); From f7aa7fa97ce7d66d9255d663aa9df46f3505b137 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 29 Jan 2025 04:28:57 +0100 Subject: [PATCH 1421/2892] target/hppa: Add instruction decoding for mfdiag and mtdiag Add 32- and 64-bit instruction decoding of the mfdiag and mtdiag instructions which modify the diagnose registers. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- target/hppa/insns.decode | 4 ++++ target/hppa/translate.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 527c453443..4eaac750ea 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -644,8 +644,12 @@ xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64 # For 32-bit PA-7300LC (PCX-L2) diag_getshadowregs_pa1 000101 00 0000 0000 0001 1010 0000 0000 diag_putshadowregs_pa1 000101 00 0000 0000 0001 1010 0100 0000 + diag_mfdiag 000101 dr:5 rt:5 0000 0110 0000 0000 + diag_mtdiag 000101 dr:5 r1:5 0001 0110 0000 0000 # For 64-bit PA8700 (PCX-W2) + diag_mfdiag 000101 dr:5 0 0000 0000 1000 101 rt:5 + diag_mtdiag 000101 dr:5 r1:5 0001 1000 0100 0000 ] diag_unimp 000101 i:26 } diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 30fba5297a..7b9d3deb39 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4598,6 +4598,26 @@ static bool trans_diag_putshadowregs_pa1(DisasContext *ctx, arg_empty *a) return !ctx->is_pa20 && do_putshadowregs(ctx); } +static bool trans_diag_mfdiag(DisasContext *ctx, arg_diag_mfdiag *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + nullify_over(ctx); + TCGv_i64 dest = dest_gpr(ctx, a->rt); + tcg_gen_ld_i64(dest, tcg_env, + offsetof(CPUHPPAState, dr[a->dr])); + save_gpr(ctx, a->rt, dest); + return nullify_end(ctx); +} + +static bool trans_diag_mtdiag(DisasContext *ctx, arg_diag_mtdiag *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + nullify_over(ctx); + tcg_gen_st_i64(load_gpr(ctx, a->r1), tcg_env, + offsetof(CPUHPPAState, dr[a->dr])); + return nullify_end(ctx); +} + static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); From 75f73d5af1104c25975322a41b4bc4965fdff719 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 28 Jan 2025 20:08:17 +0100 Subject: [PATCH 1422/2892] target/hppa: 64-bit CPUs start with space register hashing enabled Turn on space register hashing for 64-bit CPUs when reset. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- target/hppa/cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index b0bc9d35e4..c86f9190d2 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -28,6 +28,7 @@ #include "exec/translation-block.h" #include "fpu/softfloat.h" #include "tcg/tcg.h" +#include "hw/hppa/hppa_hardware.h" static void hppa_cpu_set_pc(CPUState *cs, vaddr value) { @@ -217,6 +218,10 @@ static void hppa_cpu_reset_hold(Object *obj, ResetType type) memset(env, 0, offsetof(CPUHPPAState, end_reset_fields)); cpu_hppa_loaded_fr0(env); + + /* 64-bit machines start with space-register hashing enabled in %dr2 */ + env->dr[2] = hppa_is_pa20(env) ? HPPA64_DIAG_SPHASH_ENABLE : 0; + cpu_hppa_put_psw(env, PSW_M); } From f141caa270af536b4d5b7c8540820f1bdd245d71 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Thu, 30 Jan 2025 15:32:53 +0300 Subject: [PATCH 1423/2892] net/slirp: libslirp 4.9.0 compatibility Update the code in net/slirp.c to be compatible with libslirp 4.9.0, which deprecated slirp_pollfds_fill() and started using slirp_os_socket type for sockets (which is a 64-bit integer on win64) for all callbacks starting with version 6 of the interface. Signed-off-by: Michael Tokarev Reviewed-by: Samuel Thibault Message-ID: <20250130123253.864681-1-mjt@tls.msk.ru> [thuth: Added some spaces to make checkpatch.pl happy] Signed-off-by: Thomas Huth --- net/slirp.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/net/slirp.c b/net/slirp.c index 49dc62f776..97d08ed1fb 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -247,7 +247,14 @@ static void net_slirp_timer_mod(void *timer, int64_t expire_timer, timer_mod(&t->timer, expire_timer); } -static void net_slirp_register_poll_fd(int fd, void *opaque) +#if !SLIRP_CHECK_VERSION(4, 9, 0) +# define slirp_os_socket int +# define slirp_pollfds_fill_socket slirp_pollfds_fill +# define register_poll_socket register_poll_fd +# define unregister_poll_socket unregister_poll_fd +#endif + +static void net_slirp_register_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 AioContext *ctxt = qemu_get_aio_context(); @@ -260,7 +267,7 @@ static void net_slirp_register_poll_fd(int fd, void *opaque) #endif } -static void net_slirp_unregister_poll_fd(int fd, void *opaque) +static void net_slirp_unregister_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 if (WSAEventSelect(fd, NULL, 0) != 0) { @@ -286,8 +293,8 @@ static const SlirpCb slirp_cb = { #endif .timer_free = net_slirp_timer_free, .timer_mod = net_slirp_timer_mod, - .register_poll_fd = net_slirp_register_poll_fd, - .unregister_poll_fd = net_slirp_unregister_poll_fd, + .register_poll_socket = net_slirp_register_poll_sock, + .unregister_poll_socket = net_slirp_unregister_poll_sock, .notify = net_slirp_notify, }; @@ -314,7 +321,7 @@ static int slirp_poll_to_gio(int events) return ret; } -static int net_slirp_add_poll(int fd, int events, void *opaque) +static int net_slirp_add_poll(slirp_os_socket fd, int events, void *opaque) { GArray *pollfds = opaque; GPollFD pfd = { @@ -363,8 +370,8 @@ static void net_slirp_poll_notify(Notifier *notifier, void *data) switch (poll->state) { case MAIN_LOOP_POLL_FILL: - slirp_pollfds_fill(s->slirp, &poll->timeout, - net_slirp_add_poll, poll->pollfds); + slirp_pollfds_fill_socket(s->slirp, &poll->timeout, + net_slirp_add_poll, poll->pollfds); break; case MAIN_LOOP_POLL_OK: case MAIN_LOOP_POLL_ERR: @@ -629,7 +636,9 @@ static int net_slirp_init(NetClientState *peer, const char *model, s = DO_UPCAST(SlirpState, nc, nc); - cfg.version = SLIRP_CHECK_VERSION(4,7,0) ? 4 : 1; + cfg.version = + SLIRP_CHECK_VERSION(4, 9, 0) ? 6 : + SLIRP_CHECK_VERSION(4, 7, 0) ? 4 : 1; cfg.restricted = restricted; cfg.in_enabled = ipv4; cfg.vnetwork = net; From 58607752d173438994d28dea7e2c2587726663e6 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Thu, 12 Dec 2024 13:41:22 +0300 Subject: [PATCH 1424/2892] parallels: fix ext_off assertion failure due to overflow This error was discovered by fuzzing qemu-img. When ph.ext_off has a sufficiently large value, the operation le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS in parallels_read_format_extension() can cause an overflow in int64_t. This overflow triggers the assert(ext_off > 0) check in block/parallels-ext.c: parallels_read_format_extension(), leading to a crash. This commit adds a check to prevent overflow when shifting ph.ext_off by BDRV_SECTOR_BITS, ensuring that the value remains within a valid range. Reported-by: Leonid Reviakin Signed-off-by: Denis Rastyogin Reviewed-by: Denis V. Lunev Message-ID: <20241212104212.513947-2-gerben@altlinux.org> Signed-off-by: Stefan Hajnoczi --- block/parallels.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/parallels.c b/block/parallels.c index 23751b28a9..d4bfc44e64 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -1298,6 +1298,10 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, error_setg(errp, "Catalog too large"); return -EFBIG; } + if (le64_to_cpu(ph.ext_off) >= (INT64_MAX >> BDRV_SECTOR_BITS)) { + error_setg(errp, "Invalid image: Too big offset"); + return -EFBIG; + } size = bat_entry_off(s->bat_size); s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs)); From 644ce5df2ea7b0fe34c56ab414182dd7ecd009dc Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 28 Jan 2025 23:36:34 +0100 Subject: [PATCH 1425/2892] target/hppa: Implement space register hashing for 64-bit HP-UX The Linux kernel turns space-register hashing off unconditionally at bootup. That code was provided by HP at the beginning of the PA-RISC Linux porting effort, and I don't know why it was decided then why Linux should not use space register hashing. 32-bit HP-UX versions seem to not use space register hashing either. But for 64-bit HP-UX versions, Sven Schnelle noticed that space register hashing needs to be enabled and is required, otherwise the HP-UX kernel will crash badly. On 64-bit CPUs space register hashing is controlled by a bit in diagnose register %dr2. Since we want to support Linux and 32- and 64-bit HP-UX, we need to fully emulate the diagnose registers and handle specifically the bit in %dr2. This patch adds the code to calculate the gva memory mask based on the space-register hashing bit in %dr2 and the PSW_W (64-bit) flag. The value is cached in the gva_offset_mask variable in CPUArchState and recalculated at every modification of the CPU PSW or %dr2. Signed-off-by: Helge Deller Suggested-by: Sven Schnelle Suggested-by: Richard Henderson Reviewed-by: Richard Henderson --- target/hppa/cpu.c | 9 +++++++-- target/hppa/cpu.h | 20 ++++++++------------ target/hppa/helper.c | 25 +++++++++++++++++++++++-- target/hppa/helper.h | 1 + target/hppa/int_helper.c | 10 ++++++---- target/hppa/mem_helper.c | 5 +++++ target/hppa/sys_helper.c | 4 ++-- target/hppa/translate.c | 12 +++++++++++- 8 files changed, 63 insertions(+), 23 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index c86f9190d2..5655677431 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -45,8 +45,9 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) { CPUHPPAState *env = cpu_env(cs); - return hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), - env->iaoq_f & -4); + return hppa_form_gva_mask(env->gva_offset_mask, + (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); } void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, @@ -91,6 +92,10 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, & (env->sr[4] == env->sr[7])) { flags |= TB_FLAG_SR_SAME; } + if ((env->psw & PSW_W) && + (env->dr[2] & HPPA64_DIAG_SPHASH_ENABLE)) { + flags |= TB_FLAG_SPHASH; + } #endif *pcsbase = cs_base; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index b858986c41..7be4a1d380 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -223,6 +223,7 @@ typedef struct CPUArchState { target_ulong psw_cb; /* in least significant bit of next nibble */ target_ulong psw_cb_msb; /* boolean */ + uint64_t gva_offset_mask; /* cached address mask based on PSW and %dr2 */ uint64_t iasq_f; uint64_t iasq_b; @@ -320,27 +321,20 @@ void hppa_translate_code(CPUState *cs, TranslationBlock *tb, #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU -static inline uint64_t gva_offset_mask(target_ulong psw) -{ - return (psw & PSW_W - ? MAKE_64BIT_MASK(0, 62) - : MAKE_64BIT_MASK(0, 32)); -} - -static inline target_ulong hppa_form_gva_psw(target_ulong psw, uint64_t spc, - target_ulong off) +static inline target_ulong hppa_form_gva_mask(uint64_t gva_offset_mask, + uint64_t spc, target_ulong off) { #ifdef CONFIG_USER_ONLY - return off & gva_offset_mask(psw); + return off & gva_offset_mask; #else - return spc | (off & gva_offset_mask(psw)); + return spc | (off & gva_offset_mask); #endif } static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, target_ulong off) { - return hppa_form_gva_psw(env->psw, spc, off); + return hppa_form_gva_mask(env->gva_offset_mask, spc, off); } hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr); @@ -354,6 +348,7 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); #define TB_FLAG_SR_SAME PSW_I #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 +#define TB_FLAG_SPHASH 0x800 #define CS_BASE_DIFFPAGE (1 << 12) #define CS_BASE_DIFFSPACE (1 << 13) @@ -362,6 +357,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, target_ulong cpu_hppa_get_psw(CPUHPPAState *env); void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); +void update_gva_offset_mask(CPUHPPAState *env); void cpu_hppa_loaded_fr0(CPUHPPAState *env); #ifdef CONFIG_USER_ONLY diff --git a/target/hppa/helper.c b/target/hppa/helper.c index d4b1a3cd5a..ac7f58f0af 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" +#include "hw/hppa/hppa_hardware.h" target_ulong cpu_hppa_get_psw(CPUHPPAState *env) { @@ -59,6 +60,22 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env) return psw; } +void update_gva_offset_mask(CPUHPPAState *env) +{ + uint64_t gom; + + if (env->psw & PSW_W) { + gom = (env->dr[2] & HPPA64_DIAG_SPHASH_ENABLE) + ? MAKE_64BIT_MASK(0, 62) & + ~((uint64_t)HPPA64_PDC_CACHE_RET_SPID_VAL << 48) + : MAKE_64BIT_MASK(0, 62); + } else { + gom = MAKE_64BIT_MASK(0, 32); + } + + env->gva_offset_mask = gom; +} + void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) { uint64_t reserved; @@ -98,6 +115,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) cb |= ((psw >> 9) & 1) << 8; cb |= ((psw >> 8) & 1) << 4; env->psw_cb = cb; + + update_gva_offset_mask(env); } void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) @@ -133,9 +152,11 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n" "IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n", env->iasq_f >> 32, w, m & env->iaoq_f, - hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f), + hppa_form_gva_mask(env->gva_offset_mask, env->iasq_f, + env->iaoq_f), env->iasq_b >> 32, w, m & env->iaoq_b, - hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b)); + hppa_form_gva_mask(env->gva_offset_mask, env->iasq_b, + env->iaoq_b)); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); diff --git a/target/hppa/helper.h b/target/hppa/helper.h index de411923d9..8369855d78 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -99,6 +99,7 @@ DEF_HELPER_FLAGS_2(ptlb_l, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tl, env, tl) DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_1(update_gva_offset_mask, TCG_CALL_NO_RWG, void, env) DEF_HELPER_1(diag_btlb, void, env) DEF_HELPER_1(diag_console_output, void, env) #endif diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 58695def82..7d48643bb6 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -94,11 +94,12 @@ void hppa_cpu_do_interrupt(CPUState *cs) HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; int i = cs->exception_index; - uint64_t old_psw; + uint64_t old_psw, old_gva_offset_mask; /* As documented in pa2.0 -- interruption handling. */ /* step 1 */ env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env); + old_gva_offset_mask = env->gva_offset_mask; /* step 2 -- Note PSW_W is masked out again for pa1.x */ cpu_hppa_put_psw(env, @@ -112,9 +113,9 @@ void hppa_cpu_do_interrupt(CPUState *cs) */ if (old_psw & PSW_C) { env->cr[CR_IIASQ] = - hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; + hppa_form_gva_mask(old_gva_offset_mask, env->iasq_f, env->iaoq_f) >> 32; env->cr_back[0] = - hppa_form_gva_psw(old_psw, env->iasq_b, env->iaoq_b) >> 32; + hppa_form_gva_mask(old_gva_offset_mask, env->iasq_b, env->iaoq_b) >> 32; } else { env->cr[CR_IIASQ] = 0; env->cr_back[0] = 0; @@ -165,7 +166,8 @@ void hppa_cpu_do_interrupt(CPUState *cs) if (old_psw & PSW_C) { int prot, t; - vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr); + vaddr = hppa_form_gva_mask(old_gva_offset_mask, + env->iasq_f, vaddr); t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX, 0, 0, &paddr, &prot); if (t >= 0) { diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index b8c3e55170..304f0b61e2 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -824,3 +824,8 @@ uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f) } return iaoq_f; } + +void HELPER(update_gva_offset_mask)(CPUHPPAState *env) +{ + update_gva_offset_mask(env); +} diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index da5b569de8..052a6a88a2 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -73,7 +73,7 @@ target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm) * machines set the Q bit from 0 to 1 without an exception, * so let this go without comment. */ - env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); + cpu_hppa_put_psw(env, (psw & ~PSW_SM) | (nsm & PSW_SM)); return psw & PSW_SM; } @@ -88,7 +88,7 @@ void HELPER(rfi)(CPUHPPAState *env) * To recreate the space identifier, remove the offset bits. * For pa1.x, the mask reduces to no change to space. */ - mask = gva_offset_mask(env->psw); + mask = env->gva_offset_mask; env->iaoq_f = env->cr[CR_IIAOQ]; env->iaoq_b = env->cr_back[1]; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 7b9d3deb39..0d0d1bc99b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -73,6 +73,7 @@ typedef struct DisasContext { /* IAOQ_Front at entry to TB. */ uint64_t iaoq_first; + uint64_t gva_offset_mask; DisasCond null_cond; TCGLabel *null_lab; @@ -1577,7 +1578,7 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, *pofs = ofs; *pgva = addr = tcg_temp_new_i64(); tcg_gen_andi_i64(addr, modify <= 0 ? ofs : base, - gva_offset_mask(ctx->tb_flags)); + ctx->gva_offset_mask); #ifndef CONFIG_USER_ONLY if (!is_phys) { tcg_gen_or_i64(addr, addr, space_select(ctx, sp, base)); @@ -4615,6 +4616,14 @@ static bool trans_diag_mtdiag(DisasContext *ctx, arg_diag_mtdiag *a) nullify_over(ctx); tcg_gen_st_i64(load_gpr(ctx, a->r1), tcg_env, offsetof(CPUHPPAState, dr[a->dr])); +#ifndef CONFIG_USER_ONLY + if (ctx->is_pa20 && (a->dr == 2)) { + /* Update gva_offset_mask from the new value of %dr2 */ + gen_helper_update_gva_offset_mask(tcg_env); + /* Exit to capture the new value for the next TB. */ + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + } +#endif return nullify_end(ctx); } @@ -4635,6 +4644,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->tb_flags = ctx->base.tb->flags; ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); ctx->psw_xb = ctx->tb_flags & (PSW_X | PSW_B); + ctx->gva_offset_mask = cpu_env(cs)->gva_offset_mask; #ifdef CONFIG_USER_ONLY ctx->privilege = PRIV_USER; From db34be329162cf6b06192703065e6c1010dbe3c5 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 30 Jan 2025 14:16:58 +0100 Subject: [PATCH 1426/2892] target/hppa: Update SeaBIOS-hppa to version 18 This is SeaBIOS for the hppa architecture v18. It allows us to emulate up to 256 GB RAM on 64-bit guests and to boot HP-UX 64-bit one step further. Fixes: - Fix PDC_CACHE/PDC_CACHE_RET_SPID return value for space register hashing on 64-bit HP-UX - Fix IRT table entries to use slot number - Increase PCI alignment for memory bars to 64k New PDC functions & general enhancements: - Allow up to 256 GB RAM on 64-bit machines Signed-off-by: Helge Deller --- pc-bios/hppa-firmware.img | Bin 676760 -> 167644 bytes pc-bios/hppa-firmware64.img | Bin 763416 -> 206104 bytes roms/seabios-hppa | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 pc-bios/hppa-firmware.img mode change 100644 => 100755 pc-bios/hppa-firmware64.img diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img old mode 100644 new mode 100755 index 6832dfc06d6b2f180e59fa9c3270d04e87fabd9c..d5f6f2fcaf5cc7e16565cf174dbaee6aa75303fc GIT binary patch delta 5458 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�EtJ>=PFrxI6Rc)~wq)RCMa8+AS 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 z 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 xW{ir zRM>5Y(pL4?>qJv0UE?r=Q}*z*8@cL7%1|aayGM65-5(~Yr%n&khFF<<;Z|{i)3>@x zy#ari+3+xIagCn@nvVavV5t|a&4UIc-U(0~CxLWITc zrBWLBymO^-DrLoN6m<$&Zhq67&t$rrT7M*Hw7G&RI|pTt96t_@IzB&%v6i+Hi+cDI zA6NM)mwyJwC!;8HEig`}NuaL<`@UFtxl#e^v`PSVcYx2HLjj!u8~i90vPbiCh&>O+ zABWr~JNTQ6GsT57nWTvus33O7ZMm@m;A4gP%H2uxl}nY`LrE=E@@vIlakeZO^t~v9 zrz`lmR9G*Ri_O2*VkWGIp^QbztXuh+3V!Ab9Ha!9_18E>GqT9ROno1I4Zho{0h*VY zHTDeo%ce`dT(Mrk=X%AqJ~ND>di;es*vM_XNQhEFM+>*zs()|2m+q8RQL!LW+7VZ8w*kBxO5BKGa=i~Q0T-L8-08ciuBjKnfc}bNTSXcSzgLtg5hWo zIuP4ikT1CqF%89f6tIOu<#WDuU5^u_^`dVTJMy*_7& z%h^AWk?)!}hXCEQA`_aB3U;rB@*pIiy!qjJiJ8?o#k>de0|0~uLa2DyKT8g&9%SC^mQNVcwxY0=)9|M_fGUQ^uDmqYKz zwW#fKZRl~NT0I!vR9O2Sbh3$32d-I#KU#gz+?HmVRwEEIm~pS~8=S zNJX-1Xgt~l*2xU&1OX3&01tK@0tEIpWbL=fAn&jTiru$;z*AiVQE+^!M-13l%u@#k zOE*NGIw)}lfA*CF`QhNR$&UmB@f3rKv8mfD)mE+S&tBh0s80uE*1cFklH>hbE`ycK zNjJ@n06=A$lJxvo(o_%r`_t+u8NFaiF510duN*2oj((hinN6 z(J0j(@zJKHQnQAJ&Cl+mXy#UP_@V;buWoVm9oq4=kK%oVMvU|LRsmD==P^Z}@DIDp zW#^PK$C|E`Um;}?r|DqZtXV^6uxm++r9%^lxD_AHy!m!L$5m8dOuj)HyQ+M+5v~6U z`;yeBcKIbp-C8Rp2D>rCjrB|A<6^6^xj2@2Q`57uMsMz_4&ggA(%m?MNyFgqmL#ypD>f#NS{UTgCRHjw9J?!}TQemb-)?UAuoAB3|jwn(gvw-3^G zFr=bj{m!~Jnh_dYjrDp|_^GKYC8)EWD_V}?XzVB%Mtw(=A&K{FaA>m@sxXeIAZ_H2 zT8E(oTfNG!v}<`E9`mXy+ba(*1a7dIo+DO@FNgGdU<3C+lt^dDovDf8>!pa?3(&D| z{O}dJn|q}sls(srP@dN5;y%)Xor}Z!xno6K2igw<%lqeYf#&?wP8bBHvP`h zJ5$bG(db-%V#~pRFhSjCr~R(i$`oSGo~L@XuXtN-xXs;m82$4ywh+dotSM(_TxMRp zaY>5vvQ4>?_KQ>PzrfWwT;S=E+x+tc1gjWdk5zQpAE>u!e>9f;q^6K@#19PKfrp*Y zydCW&Z8iNslleN-2z`L<#!(M^7FToeBKQZ+ddv8vFT>4YEi-Q)Z-eE%txQpAw+e=v z+r3nkWIFhzs3Nh`O5mS`qTzS91zW>TZZhY2rwE-IZa8<3UF>OZI``ui=xloRIri$*`s$#o`!}P#5OAX1`ZuH4D%*Rg zuIZgn%R77heF)wK}Ta&I*IYPoV zjdfh@fnRlShLYq{0+)}Kuf?3Eb@+U~o7U0aD7wrrO&f;q>KbI3a|mV`$0%i-z{8(# z!<>k`o3kMgAF0mZdEPCeQP4Ps)ccZ4sEI7FsWS0$l!h5bIpBs7BE$qX#RP0veq%%1 zS)0Vaaivb=pUdgBUgrvQ`!{x?>Otqg(k^1zTT>%5VE%1`H3(7^W{_#i3|t?}bGrsOskbNA1^o>7f|6!N)#ml*+5BW0X9nB<@un3j-Z_NUtK2^zCr+A@)a z{87nCMez`ey8dh_2q1|3cDD5xAme#_W=`fQ>M?^;L0c|^0$yAfRfMfKD*u;ZMUv9n zJHe1GN59+(?=A&(ix(pQ+_e^(Wrq)xr2JxdpYz7}U`Kxx_hAOApU(9!JDAV1yXevq z+(n0`p-h4kd^^ij`vOeVEd=;T1_mFQPbj?w*3$x;KZxDT^)KZmjd+^#&8LyHL+x=E=2&tNh$b+Ozxfm8aZ`FN z%FZ}EJ&Z!Wwh3&g{&40@rh4!9UkOed?&OQtm+i~b*JyvYNy!k^vp8Wn*W;rJ2ZEtP z&-O~1#4XM1>x#cvy?^U;!I)-Lp;4o9bB1E$rSAR!nM?|Nwioucn2Yn2Y(G6=N;19X z2uIU9px5uW0vOLSo^UVTV53z^A57BY9?KkKy{$=-pUt|To*2VTdn$aQ6wbd;8xCiF z`cezefI*6MG=#l8PgT#XN*#pbqO@q14>4x=EMVWoiF3q{}E5DrD(vmT0W zxnUobY%2ZR^UqWC?FLm=G!QD}|=-(X@L z)<@w$FdeYG4tr6-U?K@w-J_TH%P_$H1P;pV)k%Brx>41Zzmyi_kDL4#7rF7mG{*UE zx-eTzA;!%X*Q!!HFkO$rNnJdK?yCu{ryA7iC$#QaH8I+%K{*hriP5vg6^-pmam{Lv z(QCzTDf(N*r7ATlMlV%;t%~TcRjs9?cegFwJl-7XhtCI8M8SAIyQ39jC;$CC!}cl&V3`dX3K2Mb%mn@Qn%cWp(94` z6z1&UB`K=ZiBKr(xamx0 z-;do|7jA@DFX+PHD7J0+$`Mm}a}tzatcV}h&IAO3MTzC-PJ=TpGl{2lY^8oFn>*d) zekpr3mw%ew#zcO)LV={7S(n+C-eNpI6xMI^x{my?8|7{vqq?Bih)q1UoYNB4 z9|mSdcMuB@Y#7QuV*QYn$3Eg?ACZNh&D#LahR*f&6`%4l3%7d_`G}I-mqaH0hn_m( zwcL)*RJw%j!A@OrH$~RqsF_vSqC42%n0qS71|e)K#EHvx0#4bq-QAD@W{`meM(u{jfPl<5cRQ#-$L<^McLQ(j(*^a1TvLl(d$&nKSGv!netaM&cc_sxb*N_9cwm*OSJ`bQwlC z61WZ{f-hmj!$*uv3l*}ZAR8D_5APe18I57&zOe)&1k)H%O27!gGK}0eUsuA&ee)h8 zZ%$#Pu7r_Nbf|l|gb{1Yid2hs5PP2mT`AV1zowSQpp#;}INGr*($IL{MuJfVOBl70 zJFpmVYR7gXX(AiBpwpQPU?>%9)Rz8-(N`IU=PoeXWo1M(k4y;56Y-RHx7tT69;Lpo zA(_UJj~-xs*S1w}kntl6F{X$O(1ERFfC#1sNGV}}2qpt0e`lR_NhX4p>8$4zoo$v$ zXSE8Ja76F!MLaOS&UT)axPP^II3-h@$V`Nl0Dm{a4-Ww_56ex;r?{j|?7Xsh(Yq`Y zPXvIkhryT6hA1Z%1ET{T8y%f%+`E{}+k2Oj{t!$fK`8+V1QR5PKi<0;snoZ26Yf(! zbBi~exjeT@k-iDnGSco@YY2wvdS886undQ98BF6aabayZoY8YNyKpFY!{NBlCGHhC zZg^(^bm%c^GL~V{X9J6<1k0WeC{+X&g@w1A?~8N&Ewc$038t~Alz>HoWmtU6Di&9f z`@LnA)d3tPvyn4x7Rpc)%1Eg~RjKJEO?G_ChGcVFBz@4P>b=DTg$lBEi6ZZ95xQ+f z^TEgxZ2*6AEsffV^jw9Gm@y|&w_Z+1i{z3&2v#K+vU@;kk1rw4UmcYbdp`S{pxXUr zC@7h!33pDKayO#x-D5)kSQcZm6YgW2U25>+wq!yaS1(!nHic-~Lr?E7N)K)m{QlZx zR0yU=MJZua2qvSV34TAO+A)87*#y78-!ciE3PPOx(f9q05^S(tYbxh)!0m5BEbx34 zsPbgXyYHe-x3Gpi7>H9;W(9}B_rbsFSlR1Zfbx3!j!3XxEZDb&ah^K&uin8U&4^V^ z=7fkZz(qj5BIIMNIb1{IP0!Vv4y@|$n%*QI#Ca^^N-*_SQje>zuNuo#?Ywfr-uP^| zS>^XkWn)Z3V|^(mU7(I5C=aykqsID*vCKUvI_n#&12|t)25R;#$%hW)ukb=mVJ4Gr z?1dJ>x2vxGs}drt3%|;8Xr|e+Qt3DxuxYayzv2(NvpbPO{rQ%9?giCfUH9S#l`(RKl1Gq_ED5DH?5t_xgXUIlSQ47IirjVxw)=B5ZS_8^L092e@O#zyW} zyUk~Zg+JfWCLOZjm@pGTClKmW0|TkO6_M<#wFZ2iSyu`wo2fYGy1HF;kr`nH@+E#I z3Yi;d*7fUKQ3KlduUy8{*-r$WARnS)bHf$d{<$WZSmOlUZ#h!5N56)~!`XWJhHKVxO*sbl#S&Qsk=;p+u5eZAl} z?@>juT9~XPb+YnPdXVH(mD6b&Lw7P$By}bzMPjD8;${uen>Ay5(+wEgdj)S;?#jN= z{psk@{Tr_c>czc1v(??o2D2VvF9JKhrAWZasgp=iiS~QjG(XIwcw$dK$8fN36^Ik)mz`L;`$Q3^5kC8|B&Fl-|h*c{S zwy>;&%ViEBIcGwPuyf1AKAoTMo8D&;VH=!%6i_p?@1lCYHMTe|1BW_xZkbN%&_p45 zFgD7w8a0bzkbT|*cc8{|{{taV`7BgkEC^5v*baQ^2W_xAsG9mw^4LiuLk^w{(fuuE z0;bmWceS$I+7JyM=6;E$H)@G&f1MMw1(E#VPBua<*mT6zHG70!*m3v{LWCcTBWT$| zB}WcNo8Rn_p)0cN@E53Q)R7^n2Of_0W<0O@RARG{$$0mz0-H&U&W^ZRJ1gqV9&!Es z`KtfT1SGCaDBb)vfPaB@s6lJS$_Q`a|3e%b&qU1Q*gejHi!BF4tPc z@k31{7d0Oh*=SNB%BLBGiFBl1j)&3Nqdh}~rvk|dSx0;83m+&W!J~)6p&6{$pMI(W z7&hPRP`Rt`hJRNN#2~$7kIpiepNP#E=#v`{RF_L;PK%JpNwV*hlDN`)@Ff^OF9M#) zG~C%w-(q+`{Y>IBoR6zpmWlFE4TZ5CSk$yMq8M0#PYG+N53&O|2$iF<0Te5mk_v7V zqr+Ll(c`r)b6G|ym-E-sIO4_hap~=3g_V807Fk^t_$(}HO@*iZWNdmv;e|3pJzkHZ zwlLyTdmh}K$}(XlC0C`ovlc(zx+&$JPEP&eNf#d<1ut5_81f?;a{1n?y_Vwxy<{G` zFeV2QOTWs7L}U&Gc^2y-*KtrK92}GZ!QhCF>qkcI0)w{@HE$j=E6@#kGbvD4k8L)E zsp0#2*%R-oE_+}XF7FXi00}9q2wuR+9%~(p_&?Sj4NtPi+7Fgu59ZHJsI97k_%KFcx`S(>4tA@m6lLMU80oIPA1Ccc$G{Vn*<8n|fg5 z_=Si>CY_%v$2&@w&&Xv0qRkt^)+Pa`xEWsGJBFs&lwIa`*bYBcds&QS^i6o8o zi$lw(!saUGF@?R?Pl|esRp8FtnGSa+u^M)(;4v7FokJhCo`8bKW>ZVYV+UOZ2a#@1 zOT^Y=2V3HD@z}wde6iDuBUwHq=^By<2g1rMO*GIPI|5ufRg^R{+AXn1m1maD%e3ya z!-Oz@+QpS7vvgI=@F6#v8-v+oY6I}LHXsi&0#P_Af3iN;5zC(xw6E$C3rLVszpQX1&&-#ZL^jad>OmeQJnjF= zMJbQ5hM!asLz4ja$PF`TAmZV_UCx7Hk}8TIh_RW|;@hLS>9&890~ ztqB9p4xO>{h9h5Xj+0rehW-QBjI34)h669f!nWa10LTU<2?HFXDK`lw?s9}NRg+u_ z;l4(r&i-Aa_7?W%QTSD`hiU|~23W#gmS1sae2_=1e0B1Mlk0c8{gAH}FAFQ?!}@nS zY%`r;Wo{@ziUlK176MKN0!p-(RC`w=T3VQfEuE&g@9Z|bcw_=lN($Io?W7csLxK@o ztL=s@PdX~t^0lkTfUVW`1aSn*5eNJY5Qi(Ih?L)B*}MVA`Q5YrjAyQD^kz!;NI7L% zf)b9 z{JqhD-BoQ^=pSUy2Hi@hEt4yWkyFsS8iIA4&-KI1e0~iGNJ~~ zV9^~IK?uis=fbeeXsr(w{r*8|8^COh?0JlcR~BnXk3kV}4BHy%TMK^+jCy6GMvf+8 zbiVkLo}C$rSGFl$YvpqjR8d!TCSzgFis~A|$2ccKl3|4zpfePvCgDccSmesK7HkXt z{(vN)B$$eY<7-5Ie?We;lQ6(Bnj(^5Fun9N@cSZd<4T_U1P+A-yA4;ybEw~+1?nH* zP``h1*JRXb+vEKbKjI(VyJblO0E=s32wQrg%HF|1u}amHC$5P7K{g?lTP{n2z}mOR zmnGU2s%{xm{0UdnQg}Y|oNV=4T8qzRp3l67w-#J$d%?oK78*&=pOk985H8$XAyH4=)JFe#5@SGxj3tFwC{;-8yf0)h z6cSa_yl{#4w{|*jjcD&J{(NRU^O`p?b9)aKENsn*rF>44K`1Ond=(oipUX6o2mM%6 zh_v1Zq<_vq8z9crvU#zQViT4iuD&rME`@8K<%F_?!L=-5a4ier5{*E^b+oCjF{`5v zMvc~rM#N}Ta|1O`*pM|Rq1K|s(b4O(P;&z{$1E|_TC`BeqHLYB3%u<1DYhRL2})2fLOkg77g)Lo12zOI3P8XEJ@A+j_! zXOC)uDU}f6I1Xq|SOM+$%z|$2oOJi}um<<|i-~Eiv^C2EalHq@4xhgq%4h~9$1sr~ zKeH+H!#0r9+~EOLBUboJ4YlbSKM8~CrN-V7phj-=oVnF0Fgv(tr(5lZa_0`ZLmoJ( zH;$n|dmuWl!=H#iWd}Qx&76byrK1*wLOkuG`yZbPh8}m3^{$Q-V+i97%=4^Er)co0 zMK(@uv3N0UMi|@RfljznKDE4!p)4D>8-~kE8FZ(vNvOESeNJuR*t204jiL?LWA=S5 zht&`U@7Pn@(_AZgSt7YHH}34)wD=FA%0JIH`Omv~rO&$y{`-P%`U77WFL-zq$$YOC zF7XZgrKm`rsKDv^K5PYDHz3J8EcJjq)8E2b_1-B#j6gw3& z1%sX`%oX^a!wrIcFBP`Zt3Yo}-*#zO0|o+#k!s@n#O0@~B(a zm4~D50=NGaS+S*`CE@v~FWNH%^`!%ilS(>5NwoL~MbLsHfT>dIe4(3x6o+bTm!4@= z-rR785fxJ^FAde)FWBG|ewNGW`kctE*zcH|EQrn7UxHO*jxY4XG@^IP6_imiyheo; zpR0WR9r7TzkwkA~znKl*0NFFlW>BM$J#94|`jl=dWgRW5{zNu8%GDW%~(Q z^FOvIefddOnViReY&DY`1qnLP{TSI4_OKI!-VF*5Y)Ia;{66l89^F85r=ErXK#kHG z!GB{D>*`0eN;KISYrC;%=;$aMOJNlzMmaIq6ndbK3+5Y=?v&TXo5@2+liH>8r{zWU z)WojW)D{L_boglG>DUO+Lc53`muN$53^ zdSbhsnl1S8HtA()gx{=wyiLn~A8U|h!QXqB$sg~CM&6;~y(`W;9%j?WsZ;h_(!G5v z>z&!87akfS*!DQS?N_tOv&O#Pds*%EJXrmrO+Zzk=I@=BkD!z8^*1)QSF((Qi*}@q zGokW)gCSpO{jhWueDhMcv4CRmVJ`cwmYvvXek2n(LdV8;3>6-F(e*JZL(|#lj?=Rn zfznT0G2F#wgJ)mfh}>%m6p@%{$_8aGvk{)F@j|o2*4}mULMFr0%_ht!#CIMrREg*d zQcD?6yoZ*lGWu`FnbG7RIq^;sk!~%KMa)bLO_M`DF8SFXfd9{snu7?QmZj5)47=MO zWn?Qwv=?$-pGcY}P9#BT$<1X}8${K1A7$N_UYqrBeIE!&^-+0~KdSL1oN)DaYU{5N zri2!iAQYyeEVptZ5=fCBo}sZuKRzW14p~&Hw7@!&HZ}%!;34O<8DgBcq3rh8rOP+= zAQU`qFm{U>BpFPK^3!-YdLeAnOaW*YB+|nshJrcyb%ahGN1-Vx2$2f?#!qq0ilbRK z8nk;Z-grb|MfY6d9marMig`;XqASI4;`Jbs=I%iV5ayN45_ii&0Nf0}Xab*2t%c`D zM@Oq5`L-0QT-$)nOX|M)AY|qlw+|ZoxtU|$U>xIvY8~aitpj`&PQ2(&GS&9hiDCQ@ zUS%OY@zwxCR34ERI5BYGNSsp=K|f*?@CaiM3Q)0Z)k4%W{$SaCtR-?lcc+- zW=sl0+!cxwAj~n!ej93KaOnp*mfzhcj*I8O^1(&L=Gx2YzMH7-z8j#tn<{Hathv$H zzP(#^*f?cRAC||;-#!wC(?%c?q8Ws}eMGj(Vd5HGt)6CnAyayo+10k)rntm*p_F75 z=pQihGv~#ZPP!J9puvSpsL2hdwEqdL8H5d|Nn&^egpQQiGsc-13kSrrLREN~3e)+! zueW!$S1Z)UVECW>Sr(Tm$;$_AAM0_TzNMfoHPqEJpi`}P z=~PQcd7i|np@+$%m8-Uue_Gq~rys*%y>!z7#xNY=@~08^YFq86RZCqv8HmykH(_wo z!&l6yUlCUiv_BkY{iW=3E{2!!pcGxMujO9K$M~1Hd=Yp9k1Jh_4$-Z=(mzSb$GzkD z;e~8;er}b7XsaZ6T3_S3bX@Z%LnXIyS=%ZJj8Ja;!1rNij&Ti*J&}iEC3V=G{ zy6zNs07ArV#3^ zJhS$WI7)Xk#xZJa4sp97!w`5#AsR>@7a*;{T(|YirvpTGbfvhb>q2Prih5e>;dm?J zQhLg%q$H8s;Fdp5xUNI!9S#ks$QU*7BvDUZv00y%B2@;2YR}Z3N@cK?hD3H2>|j%D zi$oTU-bqM+Q78DW<`#6=`L0=Jfqf6Q!lLaE;9Y~QmNXUiS!(qIO= z+wBp9Nv~(N$9SMqlNhPlJf>d1tZN@47+Zo&yepFntbGr3nySnC8$0~~q;?m~@~(1u zb$}@FIOLujNsU%1r*+M;qP1x&oE|k9qRk44oU_v$y;=Trchpc8BlFl-8*(PCu>_u+ zuDwyM@REUW3)24uyFfK5Anq%FTF_0dS5)9 z;r@%1owfDYF#EeOBDKC=opzS~ROzV89AH2Wl#kJ2Cez`g#2+2JQOA9I$=U$fNS1cZ z<-_w1qTXRyOWlw4hFZ=0!U6L8s^bjnw0C1HYkGqD8fQhQsAf3^bhkICWoZ}{41}X@ zXNl>$L*ni9$>&Ujq0p|F8+YEFP4~WLl!g?=+ZOVz(s3?8URWm(_#nbioLU%mN?C$7;0{i;YHDHl! zGKyay-aGF19{XivBg6Ap2)`<4DF~b<5R|iHBdYSwtA&;#t)vrFExwhuigjRj61%tVG6~hRL0{*G=7^^Pqy<2xDX7N0*(umw9$~J;<4E z1dkdzKQGH?zW8i!nyAQ=Dk%DqI^PA3xr7s%`A9pYSSsTY=1T`C?ii+{!yCIe3h?i1 z)iZ=iosX^uR5k(M@|?He3PbAC*n|*$(mhu$SC+)H{$Xjg*l2XqDdgZ(Ym{UMdk6B* z|IAD7K(B^8PMBc5(LW*NBcZ{D8sGt_VX>jH`zkbZ;2QpT<|Fhbplh#_h;inX%fC9~sq_ z+!pgA6EbcB6@|mt=#0bpL*9W!@MnD!%*9|t z)nf9Ek~eCT5dz@jR|1)1i^EYsb}o8$OLZ+llz@%4N3};84R>n7__zs~%E|rR`Lx?N z48A%3M|GGFc2OVS#-=RmswluxosAfDjm9rRnKpQ^+hpHZl21=i3HMi zpX-aFkaP9k4y2=>*6Y;9YEeS7Si%r5esRqu@)hF$#cYsgr6Gmz$~M=&iE5m{>_qQI zd+J!nzhH`(80f7utyhY3wAir_8NC4P`M?yip+7`zrsj(|&P=?gLtDD_-c+1uFJ8Ib z>y-Rv%?b#5@~IX^Pb67mtgctVUaALQaxccKGqX99h={o=@Nacg7~YHGV)cV%KY@iW$`8h|G~ z#C`vJ%M=mu@QXE4_4B@cvvs_=jc%KG|BE|=qVHEU=`>pv@_ozb!dGQE&s?FopF+qm zzSU1x4CYfcePwz0HUm%PC+yjoWo6|wdq)=kkhQPI5N=K5AC(VZyqm$hyBE~;PG=FX zv8SI#hmj&aH_NGNC|IZr`Ru6qyfilPC5Xt~489~12YBooT%#>RzVyKDed*De-i_LZ zp`B}|A})@^!$@Wf-b3_MikwPV>!fq?ZCiyPYtC>-;_lZ5_kyliJ&{SiiZ!~{?W->V zky$9b)ED1aw-w@3oNb+9v9L5uaV;Dwnw?_F6g_fygHE}*Fp<$-0V@Iunq72Q*Yf=q zMmNOZFY;zMwO$e?y}j((l*+W`FARnimWhai4g6c?UZ``Q#MxnbNG64e2f zOERsXow@q_afl}6lvX!xef|)Aqx{?sWeI!8jfNJ)VH1xh`g|y%w04&YVc^x9yK(iV z8>W4%nd(g@=}mC?#-cK#v8z1 zmY=m-#!6R=GXHKu)969js6HWs5Rve|TXdNTTTy3W@>=Y25()m@m7XO0hlg$Z?b!!e z{%})*pB@4kS5<3OD>wNG#WJbn9e<>^jDsF}bA>yGe%AO3zs|bVz=gr(imf6)i&b+s zac|Iw;SV=!f)!wp5&wOUWJD|oX}0HKLI!pG`<1YK*XM_M4ZeO}MglweO>YR`K@b{7 zaq`AVI!_SbAl~Qvqi%AFS})}{#Nv|q@<;HP%KUI^!BxVMA9hRmc`jV4K2`WfFs}{0 ziLj8Li4Phmnq}y+-fWxAPgRCXFdkmjXsG6LxpVDqD!)3L&kmD;HT?ql&JjFNS0_62prQWodi>Ff^WAbJ@Kg z%HYcMje23?l_oUJyN!z7kz{}-|G3ld6cij}Ij{vyg8(M+>EXuwtM!35``DwzzJ!58 zC}G{3s@f3!ahn7pmc0B97a3wc-KvF zV_xXOsx5e4`;(E@+;DRy5cr?Wiohoj&D0N}t<5fu8JClNjgUbNAr|4{t{dK0H?W)%DxpTa*oQl;flz+-cz0Jhe z*O=;Ijaj`i01~ZC1(VmTR@qJrL^}_OiXlvI)%R9yRAh4vzE*4TK9()aWb*&lpJcGu zxsVUx8vy)i3gAzr0oz@|N(x|Q7XY53R65-#%LhZUXuq-Y1v1Y4P+UK^-p-6*8I!}t z$yiXRko3La!0lbqs$|J-Tw(=q+Qrbj#x2xy5o+swxFD7R1grd4CtEKUv}yu0M>i3Ml+fSc&oD98Pv7c|fM( zb5&R)CFxuCoguGyvZQQgj>t>3sjWGROGZ(L>*lRKSpdt{Yl=>Eor*rP~< zUaWN?Kkm8>mhhEthzzK#;U3nwd$gJCImJKIelufk>c7!ay0BrSv)1by*xDWITO0H` zESk19?h4)|c2fWFjp|p!K%c2V%T<*_rjt}r*JONVXt9p`$AdS7m-u8@`HzFU;4j?} zJ}Lh{uBYIwhW~db*m-gp5uYQ{Uhu*}?!4(}d_&?{ut0~{=nt)n?$(3yB#NVRntBg0 z^+s*i8>2^AzHK)~S++Y5-2G3NH3}GX-T#}UGVFgH8n?!i{m-DSdfHsyLD?PY!zI@@Lexm&X~)zJrkx1$*Ed@Q znBuPsbakaqo?PD?n2p^G(1W(VVspgswAvo;{on=@;>dLVY^{hg>5@^+t1}g#Dg1G2 z9ZwZI58J8Y>jYH!l{8SmDcI%p>tI82Av^9*ieD!dI^$yejN2-PatO^<#rWB(@tRWk zte%;ccplv1Q*Kkuc@V7s*xGFyi*6=l2Gmih+OjW=RMf!~ZePj|J_vId^ z+=&XlHVar9A2^M>_OQKedeT=vU_(WuwE_^Z$Je~O22V|zf#fXDJNj}3c0IfIq-TqP-Uc&;)2kW~|n6Yz}9R!*01n?5de`vh&! z;&u_E^5R&G9ow_GNAEH3S=y8TIA>i;s^|$=PBue+KMAgZJaSTOF5|X^z<@cBWPjlT z-L^WXUX9ZtB``WLPL!F)P-brDD#kONuhraPWRD4hJ{@Un$ngS4E;l)3iTpu zS*-vy7VrKuih9C7VI8`r7H(!IrH-9M9b26IKVEc`Pcnfns_>J(et5AVT>rB1#^uid z?;~R4{iNHfz=D6kt*gsV@&lbwnL)d$!i56WU62|0F^1(5U!~U0RzM<8u)Cj7>O^5m zo@K0nV+O=60ivGp*7~W|u2x*F0PCy_Cr0Wl!^qnu@3 z&a>2z^I=~d1?1d$%>o|C4xwXG-`GrN{w-C*hL)v~|qqeIn^Nh}o*zt`b<)lrl|KUr;`7e8R2KdO)ld z1DQQSV8q3=GEG-stS*t3K@WQ9!2dNSl{i{+hWp+B=yGpdmY2$w@u_4PpK=$HW&A>M zvl!twiz|EL^1iZXZBL9}qx0pQTYZWx?})tF0%sT7xu7iJ1Oy8&uQ+k@d*=6q zuzC!y11HDKz|6P}7hyKsjwy{K4=i%uCCCHZ zOBpkn0daz1xLAzv7mJrHtoTb|P!!%|$|gJBN?{FDXNgol zVeVG@KxB2hhAkhyg$Dh(#u~$uju}U!hKjEZhkGvAs;+1a@DH!) zo}_VO;=J>f8`iku1+m2oj1L(!G{_Gc6+kXjZB$X%rD|F{IDB^Do#i~o6y`THe&Del1GHeYU->=T2mU%C)|?O>;dm4&@X5R z4!8`2T^li~UHk#JuE_O@@zmOGAX?NR2Qd4%+Nh8ar{$WD#Y%E5R<2f-8njc1kF_UQ zHRp>Ksq+eIn6F%rD88VpcPHJY${omzcuLt+_z8ToiPpmU0yuC8TrAI}6~REXsGy_V z(BkyC5e*OQ*+Okm$B3Gdpjj#UxKITx@Q#jX;bHMmAGuWE2zli!ytyd{a)kJCuoz~<$>ASDt z=VnA$J1cZKf>jH>R7T-Cg=-rW-rJ3Kf}1i5*Bw^bH8?~iyaBhxkAkZ=Fnub&C2cEQ z-{2ZsOP`HRrLfQPiZOwyka{KgPhUbva;5lMYnYF3vSMGcWZ7$Rc57hv8M6B%pWVW~ z%|1&lZMMeTN}Fq4y_KdgE1q@j)@uqkx45=W*WIteeN202DXdgPkL^r-yEUVQn;NN4 z!-G%qW%_V^V+p1ouUX1}Mfn?>RA1}hu9(titFO#}{a`3#BL}s3TM*a}PiV|)#@2?da0#Y)qy*D^QRNhCaQv()ni-$a zjO-XX(lG4VF(XxLUUsbBU<98XYeBW;^Jd37Q(>B|Vhtu0{%DOgqZLi~PELs*ov}=! zt?Wm)N|k>URa>n5IqQcn>^T}m0}cMUHidb5OPN0c0>doHeuT*sd+`wvMc7*Vk$HvN zx`5C8=y?wVeYxHZMl!dUqg9(~Rp#y$d7ZS!31wX4v0C-^yZ)nZLbkd91}54CLaUxnZyk?SKcz4;4S0B0p2KDE~qnM zBHjxkLQ%LQQ2F<^D799o$=qus=gG;3)8*W|X^=MJ_c5BjRl*)KMA`2fQ>4%C!|Ckz zft?!H-EEK?yPMI`SIXAW4*LB*u1dn*MNU9rP;S(yhiHcU1Z4_x8++;w(gGl_c+ z)p%g5RKCK@M(}AE=&Vzcr{qqE!}rVP(N7rN2(kfjaVzIWInGmYKiPwJ)pd;85ZzDB z)YikJ9`0bjlJBoA)uwQ&w)>&DSlj*mu{OG8ZMlEmVrpF7u*&HIn6an+R#BYiaX&R? z1sckKYE|$a{XabsN)r!lbL~C2M0WkB-0)ecVO88uGtD#54D3vw-KT^=zYgdE8Qrj6 z`*1^>VBQXrH7PJbwa;~Zs)7dBGN>5c?-B$aX7t(9Da16wn+@Ks!MKjI*kfF>Cj9JW z70|zsb;}sjD95|3fP|OGpgrQJk6|yWwmNIquFr=^wVIp8t>}ka^bNB73idF)t91j^ zv`9_HIvBo7c^o!PJ5~HJHT59qZD-xMTeK)yaZ{m9hWC0>(AW$55FMkNb==<7r@06T z1T>2pw6!?b9bk_YRXpkxhv&(-PBUAGlo7J9EMz+2^ zrzVw`0dIkRm$S(J7qhU)wJb9HR@NkS%M!u-g!s7u6FSxbp$4Ye=3~7uuag0@GP|cPPQxt-FImkZyJl`8r_gImb+RO&exR9BDst z;%@*yWRDz0`}1HoR8kLmWx|1jxnY`HN1eQztL81E#?8%6ou`oXRD<1JI05QC>?~uR z9>l9FNKO<+gMoB_{|GTBlu#k`#tTBdK3!$;hY}{1D&G`F5RRNx1Y6N4b}0p0Oalt% zdkd*}@5k6We(MKb)x2$do zc7ut6t%9#cV?^pOlCJ+^%q-!Y5`oB~Q$0X5!f&X-Rh?)G{v7N;3jQ4QPzt{LIFh2q zaDSEGSA%<8>>ux^s73qCCLvxQ z!u$WA5Id=pN?X8}N2wj3TqeYkr16F}Xr3*GG31EAVFiW|O%nXd^&)(u8fQZ=$3f?k zQjyeXD)#f|QgK)_pN2i{{&cLrCoZ)A6%Pmun??0Nsm_gk8`S{bdrlH+ zQ8dpL`Pg$J1>MhE%u|bbOEgR!wde<>4%DLS?~$S$wRw~|odRDRAjcM`d)4}|wA0@I7&4rVB5*1G` zXV?3i^U5fasc8gyN~hVf5<|E%r9Tzn00#+d6Goies)zpX)KY^QrnrW`(}A;f^vHaw zT1Z{u`Q~?qi2l$ynySWACpqaePzZG_Rpc`jTtW7{pQ%nf|7WP2Vk_eLYyqY3s8t>J z9cmFih7y0ToSCzzvFEk3{@k=4hL}ApO;!=>AK24N@v;xRG=b9paE8nyG|p8g;KHmr zqS1X4MR&6KWuC@fDZ??Bk8#!M z1

#c-IoYVVQGq(zs4Se?Z72^bXg=uH*}Oq=zNE!)5VCubeHIhEA)5~f3zmR@kO8`ROaoxYb{hL*Uo#8o6O7&$8W~t$?1`y(469|H5^!P@ zfuG0ATbXZ+m5y~tkt*q@o}9;2N3(dp6Z6k7=~jZ+G0rdrHDYjcw6-FV_xmDS_|$@|q1W*}N& zz&4j01DG>Yc~Txp$I-+@x|+ht7tT^1tyj}oquFXayFW+W%^A#7!+E25YCLZwUybGC zPiZga&uv!sH%GRpzAYnLsHr5moCPx)S4PBvErf>-)#;3~WBA&6Tuzr20$=wB_mC4; z5*Bew2+mne;ze0a9}2JED0n+9^Z9k@h-Zgiu%vws@dH8xwxe|x`7F(UYQ!ndce+!E`DM%=JEVz zkI2gIJ7&0#2wDE%8yFjSF2qt)&#vDpeteu4_pqY80? zJ#IN)i-icxU=FiCrmN*t^$1rPS5Zn~DL>cHGycW|<>TkdR(k$hZQmtoIej8WP36qzsHL2V z4Qgt`BE|m}eFx}|xTF4-3!Tco%Y*3e6}huXHOGUO5KPgQ0Q+5b;?KLx@;=tzk5YxP zH=%b8{c`DDIYaV!7=y4bk&(Ln5l*{`*PGk&fN`a zbi?>YHM#K-SN)%mYmpY_y#7PzHIc#L5~h{uyx9$Ee!~FsEf;VTXB~R^xm?S74g!vDU-IMk+6or3NV!8_F8y80Loy)Ypoif26GyTPBf|A%zK_OCDTLy8claHVi00 zW&ev27E4=Z!}<}*8=)wSr|QWxHJvsSCtz_#L5cCB!Nj=v(KOe023Mn*6yqVGEAWWT zn*G=RIY1TDYmWajL#v&}Ifd`BE%5B|uz_;rv5Ee(9#m}l|J+CE`7GgI21tUAx&LK} zC6`c%KZYq`3-}s8qsuLxA5Sq)l=dwr*i(G}F0GC#*oS_9gcXI+ORGWI9sT}1CsL4O ze1DnDd+^EFUiEPAgCezDG`(NV?w{SS=J(I|aFuX+9k!Te;bqn> z;l9(l<#p=Oy74qMiT4HLDqM^_>vFn^WDI4fdpJB-)3_ssOK5YMxK#EaQ!Quq<*I?) z@mw{Dsb4+FUCvdHa_{A-(Y#?Ej6dOmG2@hXa6S5|Zi9HIFrlBi2xTG8orftPf*(x9 zrHEnN@WA_paq)GEc0pz-Kp9xaHzIJIWoDh4TQ^IaB9GFrsu`qJ%}lzQOXuasbOwTx zrEC?;9>`IHIg>eRI%hUV&F4gNRbTEyp4Wd`tqjalh(**;#(1`x%wEh^4|LQvl%wwD z%;l*2In$aKMyAx1no+p#I*_gIQdo2^dnQ}WWk1YTojK7Q)t|$zPzMb^O=OtHtvuYD z`w!+?rd*3z$hCF}4g*%04o@(Zp=NNq09OGY(8zJ5qGXJRa#h0{WeGfkQx#0hJhw^e zeLdYeQZa50;708v*}6lO@yKzhvR1yKj9z)93Ft%@R>S(d`=<{nQ|lOQj7_CrJCI%3 z9QJx}D~Y#SRVTKtp|yET>}pxzvhPEL?(2rv;SMThv>9z?97M->EdLBM0n7rIc<3d@ zWTBV_CQ~?=n@+)3M6qmH($Yr}KT>zhsU@C7ewI!{xfT%|?Ke>~9QPp>=# zI5W7vAim9b+vP?~vO(RY(zhABv z%V(cg^UwFaMGd@Vu|hql=&V%H%Dzf9P&rbm#wsT&)l}t7rJAdZvCkkWwU&@!-njVz z>K9T|&n-QtV$VH#PDRRNrwX~-%JV%Pn zm{SJrqtJ6^5k2dP`3G=&>1wHb{H<#8t#hWjZ!VZ>$?SBisJq{-?z)F`t-z*Kc;PTkMoG4yW~QKc_*P`T9nLBYTp3IfvPf&1iqC>KNexuw{_xA}o4q4VmLo&Oojj z%pJ*9W4ZIWypGb&bS#fI9UNO5X)#iLp5$#ErbW+0I#yy2(jSp`9?ZbE05~#sY|En^ zaUZN7jhxAONO6~ATL~>R&yx#wYzKq%oMZbrWnfc)BU{II*b<6Bqd8J_>;Pkds*cco zlzViDH_sK`*}%HE#~R%kVeR!X*wwF%kxJQ(XN1Ny^*i>Vz7G|a5s%iXNLqwOykkH2 zG9BXamOvjaGC-MAv>h=oTR@8`BglmRi2qG}RMUA!KSm@DUa$h5ptGGR+QH#vn4?$^ zg#rxuCB316ZJq_#0xda5b}j58F(u=qh)Uhy;uxuF0q%)N2M3km!gJ%QVL!gB(p!HP)V-3DNl|Zt4^FAq)=}f0GkOJDuy`{Gyy*T0qu; zWta=Ak%(T6I7;ub-Y~Q%j(3!zA$YE0In~M-##UDW~iK}mUfXVdX4tO$y}t2e^fv$D(XQ))o5wx`fW#T6b;$0c}< zIeDhxJwL~@9aL3prIxT+V0k@<0fDd8Po!W6Wq}J6o+5p{%JJ+Fw+;gUXA=5C&9kgm zec!;LZ=UeACC9TEbeL(K)7-Q-x8j^A-j(9QF_<5vsEFRhaFmyk{T0#%F{y?ixkDoj zSKy4vzf@_sz4I^&|Ayy>ocQ<%f^K|tPL30bFsTc-hXJ}&I{iCdNdMl)vomXw^1r8V zaWg&|T1)BQ;R^b9ah!4l1}R5icyc2?f{O%#4=778Mnc+fa|A!w5W!Hu4-JTlYI^$9 zcv2Sc=oQ9rrikxu;VVE0#g=G``}~CozU$B5f-v-axrmc6eRM}( zbTMGeeH7_K4qa$8__{8>`ZJJ*KOcv4^De!WzUE(+uS z(RBs_(K(7iA5aY1pQZ-VhSG3lbR-=&K5_dO7arr8X42==@nw)DQqe$$x=VY{W3=lv zlR2AdXPn5K$;55u#Z0OoezJas9|z3CnYEKK{X=GgpFI3#(F5J0#7ChGm*Uxn-+cTM z{VqNV-J4oBI6pus%uDGLc)o9Lsz$mQ&FaZ^cjuU0xn?BK3~wMlk_*0E%75Uv&h3)4 zDSR%wH`OAw+c3KvX2fZRQ-~O^SKc_DZS~5#4_siQFXOo^fj1mjfvF8H8$BX)Y1am- ze}l>G3ggkz$3`lXWQl$@R^z$ChWqhsD|h2G@VNwilMBzbdT*)(F0#=Z@myiUBY0Le zJc{QX_!a$an!$6K=ugt}iN8eBq@MZIZ&Z%7FaJEAPubdc02uv|e-HP37b{u!H;uRT zIc_+wr}Sb&xo2H>T331`BP<3)^1#2P57jGjb3ecGdcmEo{o8JDzqRA$<9$1C?7Hsi z-5q_R=gIEEuBReLJNl8{fM3!jWbJr}k4CMJF2jsC!cHQ@ z_58RG&$B1Aok|;ak`7Mcw*@EkU{32s_6`l)hxE9YY z8}7t&wGGeWxe>ob_(hpdK%S$yO zcafZt1icISZT*7@9=Om(zn?_Mqz_Daq~1@HO`NmoCJz{Ge!3GMr%XLQNFJrl-~)X? zemrWJJr1+mX?CT!BPyIq9F~3Ui=qwJ%uPcd2BW+8lo@@=?0MPjK5ceYyCbidVft#) z0m=}=VgbA*=BCLHf%kaylMb`z4agmKm|dU3Jn?CV8U77&5tbdisT9wxVk41_W_Xj0 zHoOK++F4AW<=VHf|91hS{NieS%;`4uz)z%9hE=ztOFN4K&GQV}?%9>ViOtfok9Oj75Wg<`qRz*5;Nz}k>+r#A%+<%nz(Wlc zTRAq3=K}FztN_SaK-vL4mhcqkEu(UagqO-fnPsK;7}aI6uXc;G7FlJBpv(B>zLWH4 zE0B(Up;(2}Mc%X2KbZ3<9pg3Pbca*A$cQB0o6%=RnQfd)zBf}JOs|no=+9BUSxxUU zjz?gd+D?RCPM;VbRBjU8g>$WYC-q~GalG5%W^>cVCNiFnf@df`2PWz+^tTj| z3{6ieL;B(^REDKr*e#X&R{9v%W9E1d=G;_Rm^++qc8QM_*>4>O-&*aX{(+nd)Xj+) zgX(|FIDXS%_BqWPDdu%$_NJQAb?%-tvpe04WS$7G@1k!Ui(GRK&+}{OwLr^tkKnUU z(<8?5FpqcXtI9IaD)oF~AGej)kp4i|#iSX`9a-lN>o0FvW9kIiflu2<#4xp77t+v{ z6Epaj)cKF|?8oyU5#wX~g+4rsuH*F*J-=8ET7L4p{bDuJ&g0jGUzG78wd{@kq$$aU925LpR58;BYvgdPmbYvN^}&> z?#VH`bIqt5>o_7bFr@LPmmwDax5_;{f0 z>$+j~I?Slk>`6hlnCO(QRPL5i&3mdJHdLj3*)0PGbR(tmx^lDqm_2Fk?)0tih<>>|SOCh4kXi50Jk~XewF?wmsE9syQ z;5T3Mzd~(>Od7+K5>KDT^BO+r4C%VYW}hPG4C(ZmwrSQ~0WfrRW*HyiVPB zlgG(BNe3196`psM;@OIUMdrJx(GA+B+*Xur(MV6I3->8cl)%#8?4Q3bt-0$xouJG53r}%-dan=9 ztv35`5fSfCpTh^nEYH{O?)REq9y3yFhP_0VdV22SxytH`WE=7s>#C40w#4x?)_H8uVh?p= zpj-AJ4<^IzOYv9 zMl9;YR2^1Gw(QK;foHLYBs&w?zGcuwuPj^A{S`>JuU|!v{!XOF`!sp3@=wFhK^D~~ zYE5UKPLLfW}nhWp%Bu`wO!yT zE;j^Qcfi)E>|Vqv>t2XDdYs)UUAWP1=etaODsdj#!iyvn?e1}KkH!7&W$M>FiOafE z-CgV4k+c>5?8-c3!M|{OyVjeLEO$7Y3UkpvF{1-@b?mU(dOV^|SmpsTwGTX3`_Tur zx({wZw+eyZi`N~!c=KX+s#T(lqYuQ8#Tt8Jo7X^dOqiQSX^m!v|Eq(&0?B?v&{P3R${af@_&@n!EkK}llX-3>;xDsh1|5r)1{}z1|^g&&}%I&1O|yDm<+h`;>vc!ummJ{%$BFAV9lf_4J^haEJ3N#PvKi$0 z^*QL&=iapNK`6(J_BpG2ZaTYfIbZ*X^G?{=A93F9a^C88-t2MqMV&W#o!75Bdv7?q zZaX9W&hQ=CUu8AoJ3`R2qfFQDhGAZJn7vLjnqunZXV<#;9*0Yp{RZhHX4$7+->e+p z!?oZkl|3o3gW&AP`clPw}y6`od_TM;g{Y|~a(Pw%}x({|8iX283vCmJD46*6BpQu0@l~Ic}B2M5jb0^E}&o*!8 zn74Axn|bcO4L3HL*EgBH`DS#p*|Wv$e$4DDsElk4Z!=$~xk~!!(+?4Qk3r`jK)ot^ zudD8x>g0{6x{f{i8>**Ib=^{t+bT?lqL$D9tr0vwlssk_%criWiu4jk+1#c4JVW1j^u{(KoN`<(8s ze&@?~a5UZJyxr}*)#JPwb@uf-Z(Mg?zu}DDboShGc4IW$afV;#(IEBtJoU^&OIIuE zQ>x4Q{3FB2z#a*;v!=%tc`{u1`sO=Z`X9SpaBJ($ZGGEs?704T@6PD1?%iEaP?mho zhjUweO58*#@pS6{3fsI= zHPUBS@QdyL1+@=*ZZhA}1|)8S;)iS-?f#2pd{ia-vTstJ5?u!SGU5GZBhsz4O(C9< z|C%F$Bg8f;k;A*DYw5cU3 z8NHd&^*veL9I9G6U!Xe8td7MN7BF6YVq1}H4i7|6xNCqsg`y{|58OOJniZppzQ0?E zw0l;a(s8b2;=5z`DW!ktF1umu!@7p&+v?X3-zn`sa{K75V>iqCp1twr>(BL;N1yL` zOZQv5Dk8_jCbDS#;m#DEp(pA`JXt=*K_N^^wfO#U)(Pu4ka={S92=_9u5#VpZlf}> zZzHld_JP}e)^4Sbu1FVo8|RQNF(=MP!zsKKgf#A*m}651o-1{E@wv){G>rRA6NZs3 z>pC1Rybw+4QQfIsc-a}qrJ84VKiV{}+k`b_ch+fZ1(`TvNLoWmzNZ=tW9zv}J-1f( z7WM4w-v9cpJFfoSx1YH6rGw5}+%f0NgMz8O;D32UF!?OP&=2#o(E!f~ z=HqeT`+{liCVtox^Ji&Hdy$OU;$zM(8lO}?AIAXq38u9-@^=jg=7zPpR>{54{X(o z^7jH4+UR$Ihi#bp%B3;a_meS$$2}!c{t-=!x94*!@YiB;&j!Xy{P%(L@L4GMuYqZ- z_ym7H=KSaoFujBg{hfOa^t(3xWni+$`@;Xox>uvIBLjE}bbR58%5&t!41Iq_@K#{5 z*8;&V;AP-4!FywdzCR~n@0i1`%zyuw`pjQGH3IAt-8-XqPe*{c@qJ;iGfJcFM z2>u>0*^gEKzr#Hk1ze)(8Q%b={;=dj-}&c(PieZ-VcnY%oD*}Fp?>rtNUEQ+z`7?R z^rvFZBM*_@DfDMzhQ1FY^yhH5Dq9c!xkUbyUBL4;Tn2v*Xw+)`rBvbW z4fT)oN6NdvPxdV1CFKIJ)gI_8rPI2rBjw#de^7hGo>D#=gOa3nbw*n6f{uAq-j^O`^@!Kc)seIViZmO^3R|>csI7{=ZZNOAsf#8F{wZJaH zCU7TkwcuBPseO%t5d`TwJA&_^{w~ndb$_ct;36CT&6x2d^{-W*F96>KmhqhMIceX7agy_tOAa={oM#nr&nek%W4F+<;(5e$E<@5u=MN9%5k;D3%8`u>aHp8`KZ zQ!6x1%Z(ZO9*bba3;N!Q-~*UH=1_hf3WtAAI}S|!mnHZe*1ZiE4&?W1h`M||Azl619qY@Xm9$@VtDHxu#D#n#5elBhvv`NA9McN7eSZ#Afp`k z3pTtF_d6PZeZv2C>%NCzA6^642fDN`1M{oC&mr^=1Cu>Ve`R1i>AM_4|J9iD&9G1I zU)Wm)#*ez6ZM4OU5WLwLc{EuVDVZEBWDnGG_6P!7AWJp??RM>~l&m#*4nA zAsG63+aTxz8fRjByuAcii%m*bs;{ZOy?=gd)A%DiOm8# zK>gwv<3Zo6kn-n&J3zlC_}kV!3c-ILGtN$cF7~os;obuvzBTYJ`Os~zi97McY!Bu{7o@K-D`sUMZ$j<_z~~{!JmdcBEX`L?9apueZN5H|78XK z>I#f_B8eaVE(!kwa0TScc+LI^Fx5}W&%t`RegXKdE-z( zOkLmxWhUF7yOIZZ<(j|>hYU<6WG?@xdXBvW6?hrbDqhk z`hg$wQ|>*`hk)}X|2WoHi@*|(=OVt0-W3f0kozsrp)dB2lz$0y>TmHMxeq}n`;+{6 zY4nawV6oS{%`xK|?B6k=>z{}I*XQnnE3NZ(!5AO<99?jub$%}RGG3uc?Unw^3**eY z5LoOb5AmTs9~XYCC-j-P;LpVjef}*N<2}-k{KJCbuk<;$V8lE6j9c*cfYHB}{pQ7h zsr_P~8=NsipJQwO4f()>us3P{hMn+lWG~b|(C3CX0XN$4bHJ@Od=m4mZNAxncu=2r zi~NtpjGIH?7yH{#i~YGyTmDO+--rIiUu;0UsL!n>|9=98JsJ_g7(e=aTJUe9zoVeb ze7WKC= z0jBm#|87M5^Q(C_><1nN9u$6z7kyqV_(RrNuweKHeV!}$Ud(udOL^pnrX?D)j#a41S0ByN&Q4pMHo?eoW{OV}?HS)Ob@y%+TkX zf-yeyd8S~jNA>xnV8n+}DqqI)Cd9w`Y*Og2S!a=gu|Ck}j)JkDsLvDy-@ps}AkXTL zO&BlwtWfBqcr~9wc@G7D85s7RA@T2~KgD`?1j8K?D1O-VP3(U}z)$`K@#LnziaEn% zuLasZHvP~#zYuvpg}zIqJ+S|L%oq9`P1Eyt;oVy(f4z)Hl>vV0f9aq6r-2KA#oy#( z|K-_5w1>(=`S~x>TbV$w6nW=SU#g$zFTVlw2f)&w`5(f5%M9=q$$u+m=<_nk{|4w| zpl=pB=9534wdMaR=osJe{NKX)SUK>x{%iCH>3^rl`#Z!V zQ^2DC%@5(vNMC}tq@#Vyg3*6lwt_wcEb_MOhWtqzE{44kmi6+Mx5k_isy|`a)0Ve^ zUJLw$E`Li6jF@!iOP$9+>(Y@*Z=8PWEHb-yU;buSI9 zo<;i#KrcallJ{N2D@!)~Pr%~|T;KpEeF=X7_P5Va|5Yf?t&LKk6S0k%C(>N4E=f zm*8K+{5faK{~MqK#_7KgTmgOKKhFOp=zm)Hp|1ky>x=@X{)N5@{ueOWpM|j>5uE^* z`Fg7pbh2lmZ{2|Ub^?q2ZpHX|WfEBIW9uPcKx0zN-}-#aN$YF27nHZP3i%t5Ano0H zF6Qh<|E0LVkNR!B5Odxh0=+|UN6dMv7VW7Jd>#2~K&SW<_PG`N5%^m#IR6m*ThRA4 zVCj#oX#Yn$Z20TIcWwAPF~_4JbO-e(^u09}bNoXmR3mtsGv@f?S}d0=ygBCRsW6NR z3m3(lbqhv(zjYhzp`P?b`a^rSVSLn=08{%2UjcuujsF(#tc`ydc+|%KUt&)0f{h>b zud~bl8(_QqpMbwM!M{Bp*d~8F?AdFRzr7;nthLGC{tnP>^0#9=du{T!hrw@`5B=BL z<&Of}<$opS^w{Np8+5yTjL%w|{2lOD9-I6fE?}Gd9T zRbadP+u*m$|1_{&{sgdHKIW(Q*yaB%=yv%(1GdS3d^7lM@*l_k?0anTA2)$*@*ju& zpSQ_>9P`P0Z1Nw+{{MUI@;?TCyZqk;w#&!-ao#TfJ2B_G?ec#Jx=sGh^}sgyJ29T$ zZIizf@#DK~@^`)lY?HsU74wP9h}-|p9^igpvHzVP2W|xx```I{F=x||jsI()=h^rd zK#$q@e+2AG@bAh7w#nc1B(P2Xu4iM;#vvPjHRv|^yWn52B#hT**EL|5jsKT`?ec#U z*e)Ob{|Wf3fi3x%FF!O1Y{`EJJOnKD*^T+}gEslQ zp8~xi!N2=CV3&>m74X~S?`{B|wDBWe{@{>}ANvj0?DDZ*{9uKRAM^bOT{eEK7p~dm z|2^mQw}4$X{(k_!O@84z;K>Ak;SS)T1b-pofy*}e zg(oo{Ds227V3&=*9sISxVxNV5n6Ddw$vzPu6n+x)Q5*j_^4sh4-+?EEAMA^!Xht>_LIIWdLPOoee4kW8!<-(9yb(8dC*Td3cBbc zH;DZJ8egZRJknP+aK6^po-f95mIQTGi9GDreF|iwQ2758_o`cgO9jI}NBV(9{vO0j zA0GiuMgQUdJ&$m|ECO7u=-f(i#2$)F>wYWbVZBv^c%c*am_n7u_%6bFInoOLbivU7`$vJT^57q)I>{t;dy9Hu zPjf_vJO}b4-i(57>8I$E^cpw1zpwRCg!NRU5oD{re}ep{fXgKReR{hauuJ&A7jv}t zqpC{ls|fbiSq{3@z8^y#=_60;_i6Y8y7rn9@souz=HzkTVj*BG$m-?vfor*vq2@7s?3EY#B7ffrl-~zv$*%#u&_;g`^s@k5Ao-uL^a%`o?!$T}5&&J=yAS^M zD%po6zZ>%I+VE}go5Zi%yASbTgzZDy!#=DZI!WJFea4_Ks-M-~uc5t@z?S@Ppgiht z3*X27>oq`2{u1O9mj2wgEc><8A5{Mz!+*lw4YBwAPViR(OZoc|&qpeNMc#hg*V5l# z(e}Fk0LBB2f3d&)FUB0Z`(ax1LO%aGT|A^hnKN^$oDMmX zVl=ci)qDKSq2N^`=x_6f4~kFx1}axd38=r>>gap>LwdtqgK#Q*)LPEx6 zf3Pjk(p+3`)YrH8{D%)+X!M4PTZ%(Xtw{5D+OBx(4j=N=w=|==%^^uST57bkhpre6 z-Zqc7Efj1qTH8bI0nqW;7HGcM=x^|M5Ri|054a5cHu{^5CVx{sR^veQ0R-I;3e~q> zHJaNS8*75zKqI7_ZS~gs4WIvlx4kjsX$`j2`_Te4>{6Z26KD<~7qr^w_qA~f0aX8@ z;cpDKT=leExIlt6XlV6HLR;~{gNKc_`nG@v+6$tq3~$Km@ppuR-ujS76k=RxX>azS z-A%3DP@t~S?`^0L7@*|^!r!mOC1{LzYu`NyumiVr~U9DgF4>RSl`g*xe#df8D^lR z?L~h}lONXPM%6+|DF*RCMxezok*_7_^9O~?f(9B(GKq;gBT$ddqUQ#0W2m_7km2?D zJazudMwR!nzp9eN80LxBv=mBvA)aQ0TAMtq`Id_wFEmIAhe|vbq5rn_)>d>?eX!2k ze5p+cfZ%WR*N0$gYo&SG0`K>eH8psk0WVq!v*cpxz0IDshL$UyCVz9gQB(a=yo2bO z?60-qYFnV*+ej*G^Ebm_pyz|hT0VBvXsr*hCO!3y-nKTQ+I_Ope>nhy(z^53w+1{R zFFM+2YpR2?J|0`pY!7b+&A2@ zQ8gd+Ny8NnhDvi=6UHApq3RtTUoe2|o=}UYz0J?vrc<~JVezDtdT*<@4x>7Bl^Bpn zBab_<N)xG>U!PZAN{2FvyzJ7&`C< z18wy}ZiDj$FX_diLkJCcsIRplP~T?B3HqB_F8kqxTbewVFs?m~fwmBehaRCM*aJRV zg5HaM19lT=Z6jaU)Z`^qwjFA134+ZJFAo0&3kXryG+k^WcZMDz$^3Mor8(qjZ*Fb6 z%#fvXMRYcQ;{{L1k76lTp!t2|ZZ9zKchsXw4ZcQlo+yA6NgYXZ3MKfOyqw%ZX_mk2Pzf5-=0`pGS-U3775Q4|=`da|r+t&beqMLYg#g9^^-3c~x&JcCB!pp( zaY2?uBzfckr)3@F^t$#yqtD|DT%<3M zba2p*KK8R0gPRC77&H^Y=o&F?8T1bUq!}&n)t-j-ItU5D=@{g@J=|Ahbd7A*^aO{| zDgX~u6|z)nm^B*6fT%M97d=;kfsh|l9@`c5JJVpQ$tg5cjMhM)1M@I!hizk}lsX=; zB9|(1Wo3fqV6Sc)I3$PN5%|AG33XFYJD$gHUzPm}i&`i;CQ<>yg_ zV8M@`h{qh3OG+e=ktH%x{aTtEuVQ>qMsA4D-&TL9HPDK&=|5T;@BQ`=)W<#0=JnJC z1HOy)j(~S=!;q*m5O1)h8?7x@{6Pd_UUGGwW_aPcwyVb3)j_i>Ib?eGHr5+t zBA5JEF@?6EQ5O)dBqzZhEGOk@##DwjB8Gt7HnsTL;ZE3cx%1d?w0rW@VGbdara|h# zR9uWKq2iJ_HkuIm1y;6)qZka-M)8@bz6>HBhPE-3ghMTC>>NRnzmGqV=cNRE>M5B3 zCRHC>H3n=W9Lz;;8>Zb00W^vD<5ML0BAys7z@5iasoxBmL5b{droOl3A*#5ucn%q~ zlqZQFW)~J{ZnP$kM8`ngJi`>T!$V}*3SE0! zY4&OI)>DMU0~fBqnyFg-L3n+5K@Kt7k}_&GU0HG;NkmL6R1Y1-ozdV7MOPM~vBisM zA;e?B)6}kqht*h$yw0>YSGT~CYQ<25M7}Jk5Fx<_1iXz&6jTQm)e!IpY4LCs?SaVo`C?qA(XL0IlVi$D%-lHb$$Lp|zwQC!0vBro~9+ zK=FC=MN&E&2822b3pC4fpy;7u*dclX7cpenXxrQB$gfc&J?tDMfU!+T z)HkiL^7wE~%#h;XIl+o_82AEhm)Ku2hYnORl-Hmo&9|tNfmjYprMQ0$v45pHu)}5F zLQaO=iB&anK}?|p8qHiR7JT5NB=mA~h@weNr(!4|!jhA^*0LYC9vC~;d%>0~#Rsu6 z1BKUH@GcDM8ZR|nhL*_?h^R5GBHL^jp3h?7P%gpGa3xu1Zw=IY0(I6DsmGz#-H8i< zv#tL6lfDxaR-Fw6+pVdY6cP$x)yVT3HqXztGsy+IsbD;G$8fSNBzw)wFsQO%_yzT#(($^OelKh}m=-6N*v zVzC79G`2UxY_Tz*li2O*8XAq*)L}j<6nqBkHaWoxUnoD;`$0r=MiX`{l5+9Jjff0f z#^lHQ0;E{#fKXkd@ltE(cWky3!K7WZ77fsKd`%OKdR;cs(nHyl#UxNVAnc*zCpn zKu@)jE+bYuXCaA@Kr^-!Z52LT8eeDYO+MtMo@$|(n^p@3K55;F?G|ho#S!cn0RaDt zrF2Jfbd*%W!BTDE6zv$k7HzuPdsyysRSbs_i&1dd8>Fs+UkQec*G`-{3mqG0Uox?- zIqvfX4RX>Hej{|g=*8D{{4kw1TC;kwgHU|znBlHWY~8UNq^P8=)$jN5+9d?@#Wonb z{;Kx6)3i6*+)j#ZTcM;Qx(PbsNNU~T!v=yac1s@YU12j9!kKTSj6*AA9$bM-lJTKu z4fol1)VQmP4;?&mcm)l6n^+Ke5yN6dP8(BK0zO)wV~+-1LLqfSpxKWx!k-@V;8A?r zIn-De9}`J!ICPBHW@O^cms`*T3ZJcAm}JhQSe(iDjLV9r@BoCXUs2g3D{v|HoSQsY zTcV1zeSnFdHXPXv@~$Ion9(``bwGeXp*y9Jc_t+=OA!2uPN2@Yo8BO=0b(xO*_ne265l+hI%O z*^MJ?J{$YX;4@;LG<>o|B+YuHLT+Ciraq;o6fU`Yoy;gXY%c3{hjBMb_fJQR;y zC@bL~4;h2irnM=gpw*NW;z|y(xD}%8sR=Pw>S_^3A!N0PcxsXePF*bmhQ5Y~cxsXe zPF*eH$eJSJsYxO@b+w2?Yl?`cCW+wG)gnsQ6cJBN62YmfMR?Z~5l>AL!Ktf7keRIR zk$7s72u@u?1WaQM5msul2ufWcq8?RPT?>0^LWGsNLPQDacy%posRe+3lSDA@Y7ub@AQz$s70QdJCW+wG6(TNNsPnI`g*`PP!b)8&;sQjhG4PQgiIOBL zPFW$&?}M;adayjIL?YELLZ_}45m#bDwSM2>cxsY}wNwk{xLGHOh^Hor zSj*TUf(&qljo4F@L@@8Fh8!ewSS2EnY8RnXSBRjRt*(VVH6g-ET_NJa1#$-~Yhh1K zh_F&uiqJ!GMMEywQf(q6b)^VBNv#xNOSOrR)YT%$URH~UrzVNu)YT%mqmn1d3m3}b zsYxO@b)|?k$H)a+s!fEXt`-rWfD+aT5%JU{5o=9LV2;nENh0E@Ng~#oUm+qslO~CX zrzVM5Ykq}@_?S!*5l>ALvDQe1i1;*^BqE-gBx0?}5h8d1ujo`;YLW=%UEL#P)*O~p zi+F0Xh&5(59%QSm)1I0TVWqC91vPH9f!k9PBCOOEBE;>l8Y4UzaB4z?#k)!bHF}L< z7EiT{(7dZe5btUciB!7?or*Kx%O`R5DR}rG--j^Tf+hN)DTtjI+>kJcW94XrGHqrGD-fp;nJ3_c3(jLOD3$}#( zL9CUqPlLtdOWqD#^DwcXJB355`m1tqWE2)s6+8z^jMoC*7ffDsn^@MJrL7#kK%#|U z>)3-$hWIWA>*vJFHD})GsX1j@VxH9xCq0L0Gor2q%Lu*gF6#(eu9GjHtTA4D@px5B z%cUw@A;9KW%~hOU;w-J{3LOzzySvzQ(o$Pa;@I1|_T<5ufj1xM2#D@FSoy4~U~9vJ z6K^cYxQQn{M-6>-3OBvlgS?AEo0-@sDJ&#K8?>()JVi~!1_*6z;LZn^fC~*+TOCr?$LeEEgbHoD%T4R-MHn5Fb1hCXLFNt@BEO6(#A zn{j=B7_Z{WO+wSW0jr0Vt;X)tFTZf|MdKvhIj9Vv@4Qz};*ut5o33}@7@2lv_-14^ zHmWYP1e)Qb~cT(O#yNrqCKNS-^D?jVSqusvb5W13pU{x z^B}kE8&^o$8)+z42ZFrUO75Sws-Xv5#JXa6&Ni#caPFc2sDd=^ZL{u86&BLW6K5<< zxL@5NR++0+Qgh;LjomHObtLlgjwdSvZEk9Tg7qxm@rx(WrdWCUjA0bFU2THF;uDu% z`Ln@#V(*RgfKe>@imQ*GIeFGy+}?bJE`k>O8*o{T?v)g?#*1ms3Uf{|cBc;>EiQhh zEm;2y_9lz#afnq4rhkKc@ZTaII`*tlOwOXX8OKE6B%g`faoESTJ;OrYQFF}kmTsZKm9`D50?P?t{%z59dX=7cjk5C{y5(a z7kavU(s%NR&*Zz~&hl#8tIH`}$*+y?rCG!IGWh74J^$GGo=1w5`_cmVYJn%1OaDXi z=*K1x?|65f-hs5WO13vnG#J^YWeej+CIrtucukl~udlBiR zmlXcahVyi%VH}MBTR&ER@b?dp2tK^a#@S8!vS`GM@2&6;l_T=-jvHrp7VuhpH*9vEVrN7pI|Fz_OJBcr?uMV%j zq3=vsKNjB)kYGKdMW4X;Rvi4@6=Ae=`7gq!>zDRM%rO2uYzY$@jTTqbdh;P5 zbrlH)m;!~SP^2P-U@0kBXi$+LWsL$$m9oTLy_>tmHA=Asi@j&QnE-n4|KHy)p7)tE zXU;qmtmzn6^GT9>>|j;aY?MfbAUi5V@$g?W8otVYS8{dfz(6!gTGTPPs-_hCR&|<_ z3l)xPNNWsw)OZ6f^fcgmv;|}5-}K1DngNKurK2OEqJZx!p57x$kX6>+BvV6C>koC-f_T}5E@SP`CXIxp* zW3i5QSde6kb9j1&ZL=8UI+D+_!U;BJ9W2HY#~T)=$-*8v{jc=|=a zLjpewcv#?v0gnp2Nh%>o;A;Rka@>{vG7uIacnol>!0!XxF7SzfO9IaV+#&FIz?~dV z`xbCn;HLn03;Y1!UV(1~+$ZoAfComql-{R32Si8+<^moT_*B570v{`tsF1*i0&e7Z z>J`8(0{;eZtH6%~ZWnk9;F7>M0`3s_G8Yg|K|BSxEbv)?y9GWOaIe5~0rv@92Y7(v z!!80I68Krb!va4HcvRp`Qb`;#d|*0M8N_7;!P5H5BU75|CY;AO?0LgB8cUv5+wFk& z+X?`;O?-vK8Jk-t-lf8pw~Cfy>sv*)c|Iv>fr^xizZ;6>W~cNI43#$Xx9#yz@k3@8 zlkIKI((DWjb-mz07oI53_+ZLcGd?U$hAKrfJ}T8k%;r-Ng~TH=9{}79JQkS)AfzVF z*=+gU5v+p_SWxBBDZ8c8c&WLke6R`{R68;TVQ=#d^B=)kK10}8d%7;uZ0I-1P~wRB zd@?X)x>OYB^E6c40zrf;Mq?akPI{s@0RoJB<^=FMpZN`&P0jh0cg8d5T-2?`q2}_% z^D)f0k6vkp3@ln=#?j`QB_9y7y}?ud>KxJ(X>NTrCkZBh*IUnONb3zAY2&}i-a9<@ zO~c9FIFDn~Jt+r~I)OZ(vm4hLHku^Ezl`Y-^?^=LVAIDW9yM3jy_k@&OJaHVl&9U& zeEZwW33(^b+|j5{An`^|*G^bHJaN1J0c}Kf?TUw`q^89K`SpzPk4#T68f0VUa(RSg z%fv{o8nK9VS1O8^JPZ68*x{-6-;SN04*w%q_E=lzVYkQKT8h1%P-{W1QX~auOY5cL z_)z!2{<&&I>=E1eJhqNC6KR(ZxeGlMp^U{6yV;uFI-2bdAK}jjdw@M`mMlob7 z0JjMIIl!#~p9{EM;8OvQ4e?mO9fCg;a3{xyUjbYe_&0#N1%4cGuY$YG!&`vx2?5kb zLO|fl01pZLDZs-5p9OeS;FG12K}g`afExv_1Kh&#tc!qK1%4KAyTA_vE+q+~2?&Qr zYP%QmTibRE{~zsyQ$p2i`h38izb%4))@J=Z5yXfV}|z8}{sJ zf0L+@=Z?VhP*iP!NtmdOo}NPwz;I9dAOni7;)5+P%t;?on05^x=D=tMKPQTr6cv#ZjDToJVQEfhZM~2%z>7xIy3Ol5KND*I(dYb zxmY$*4!}y{Yu@q27B;D+Gxx$s)t)JUAWj1AXts1Ut8~umnv$Et=d>6`^?dlBu)>`D z3f3E6bL8t|1lEr||18DepeOQQDcihfYPDf?CZr>a%!AK^k%WebC;Hzh>072fx)4o! zv=Ws)`ZAIf-x@MoDoPABTmCf&7TM3PoX!4)Wz#npxKzIMH zEX7URa16&Et&zrASkTmJ!Mm`9hAsFJY^8UMz@I{ghGb*dv*@&JoQIusA){ovBO9k* zT{tbBwp8@!bt7)4;cPrC%dN&_j*`LNt;?>@pfp13rbBWdKnHy^2Oq;;zx4+EGz-_8 z3(ZXQL3ju(arfoj(|=)EtPOC!5C4iEqWvTBIUJ^4dH7~XgfSnl1j?I_Kg3c0qc`Gb zF-b!H&QW+cCR-|K-)P8}g$BmpKanIOEgXyMfb1L#R#sXu4qUlu*?2qyQcyb{mqH4< z#w&TTTk%gYcqKRCPspAfe)R-=8-d-+-oaPvD&M2>!{Bz|nPzQt{)s7T z?!Al()kA=<+S5ht598saHHkVNhQV#M(AtObAI3G$WD(6jJ7o>CXTBn;<}u<#vIhKy zA=>&gMcQZt?_J=x{vO{P2SfMhAMo#B!UX<+rw{{LJ0I%VV5E1<$G3n`o{xv844EB4 zNsUsW4)y>xz{BIEn+;aFXFi_r8^fam0|O<${vc%70iTcH)5&B2zy9Ak#`5cV2pOH! zR)H(isu04h*FH%Ip`-{xItL9@z_{CKqymD>%%)==$DbxD8-i49aL~9Xpj521@Clgn z0owlrK1tfz=;u%3`b4X8IfMfity%yJwUer!!aL$}WZX|%pTebCQ=oTI);pFWsaO+c zsqDV&dt6yuhx`#QRe$#iyOwfW$m-DU|4ACv)JsifNtH9b9S}#7?L`-8p7?eb(Yh?wnPM1Ry|U->SZe*e}$2BUyb8(L$XrKaPU&{$p+=( z_M^6RRfJ7q#_R5BtLHVIl*{@Ct{va~d4yYXA?dq6BdcvNMsR$|XF2-r)pyT9^8*pH z=87R~3bcDhAFyS*N(ZD$WS9PgteXO(H-f^qwxNhH1H(lVoOkqgmRQf~d}JGfh-Qyw zh@r<6VsgC$5Ao^NA{C zeb$965p&|jCmg8I@JPQY0HYtO>_-+B9Y8BIYGsXrQWMa2)>xSfl`($Atk)Uppy5j7 z`1hlli7c6$9F}~W86~ih{`c>_7O6BrsTO!6`5AoV(&$2*u3?kc*di6`B|79eoIOXq z#tF7iO^)-MM-)o)k#xQxyAZZ>(&oK!tTIqg)mKNOcjwv4FQ+`iGT$y+j4(2HM$Brc zpweDjDnV@PQby#W_ciK%4*w=)&{QY%31)`)FX&HVPeuZrcl8bQ=U=U=zXV%`K^;jo~H7j@ThU|P0$wP zCVr#d1-XG4BE5hd?Us>KnI5-kd`+E3W`j*0?SwX5@en(&m|i))^t)HuwP5E2A5Cl= zluBB<^az__z+1-hq4?>C>i{Bls4z!+IQKp_HezB7e$$aV5%j*zeq{Vl(n4ha!Igv# zdUADKM;(AMJ#o-sF=j6r|5HR|jyp@Mp2y=xshXTnuB^d!d2%xkB5u~zAZp_#vJNe; zT{@ngd>)T>ZNQ=gbF!)CN*ZP zm%-U&R@qG7HY_;|)j1U^(oH)S;d{u|$u#u^SoZ?75WvIiFtV5}%dCFbvTPeR#NB0N#R{;vyZ_yzY&@>&jY?wUy%_W+t2NhMuZj&`*tchkS_HZO3TD9BxV=h`0m zrM>hg992Sp9*3nxIK+;`5W?Ku;2fJ+s`P>^eO3mcuOc#6(Tz zg%aaZ?Yg*3re`}BhmQqYGSH=Z zA8mXIKQzYz)6v|kxG6@)?n}*+Ss#_Wu(bC#>)3GcA%Q{8OtMNh>jUh4jQJ_}n@X-c zHsu+3f?5Sj7O4zQsh9E-VQ;k{>ZrmaR7a-lrc0~vEwkK8yEp4Y>}tqxSUV-~rQp(N zeU#CP&W#52xWFGTVLArIpP~;>DX?5ZA1p9U;RG<`Wt`{o*#;*WIUAvzx+?>Ehw)rN zE$oYO5~}Ne#VY0MQBu1pptvv5*ljBI5C$$v;}KhZv(d@{CaZX4kSvYT%abw@+_Dk6 zup|jdV-hX8T5dZcymCOwOSDexM-Gu$lBdo-*O=*Nen*=HZIJ3GJV)$0x-N* zxGX$a;9>*STR%7EkjseQD>`n2r)ufBjc%V0(X7)w15Q48aCy zhwOD}`{Odg3jSK(a z(dr<@x;9f_;sB%#cv22>Bn`0CS@>d5gxC>lad%HsE)D-D&Tv_oNXOzSMX~`-RSG>% zp`nl<0lIbHsyJkSyoCOz3un0`CXEeEch8QeSjw3f76oMGnymLAwxz+&smPdjU6`E%m6nPO z4yMnd0h>^F?}2w>SybdVYNNW6>k?9o!aUm(HeZ6_Br9c$^0cF~@IsVE>q5+d&S6|9 z0*P{i&Fkni8P+e1bS{&u@Os|$X-lMbPK_l$MO0sM1piQH6Wv9PA$LtaVn0bRg& zuCflf*|Pg&9rEfdFl*&}FIxxUso`I*;M}Rz%#o6u9D}c{3Rzalxgi#aG%j8VP|A(B zt9QofCw7^CUKLn`)Ou2Wrgr#(W_s*^CoctNGHB=SKStu$+}okg{tA3H6O!mgW>ouWWe`LS`F8h5*&!}guVm=GHDvibrCVeSAnnkrOQ z@ke$wfbINRz!H3shM2QSe^r~Vvu^}GyQ^K_xmMu87{O8)3v^Hw3INke`L0Rn>&iQYD&vcl7ooxIoo1*FzG6Sj(UPEx< zkAC{fYq-=k)8a0GXI^csrVd8@Hf^0|UO_Ef63=PQ!9p-sJFCzOa5h{5-AR(M7e1E^ zep7Vk;(2Ec`%F>BV{`QHvTHY}lL zh8bD17p)v%Gy0(eUW@w23&aRtj-8${m=8*CFEL6sQQFMtb?o(&cYQo0$* zEXeHET?3t4wP6;h=3?AX%WOD7Jc!7o)yOLElUt#2yLLU@{%1Tk?HlDSfF(l*r)RyG zhCMC2Y4p!{C^plC*YRlArQ|o%4`_xNdf_%}H=fmR1SlELLUw$Bhi1${7CnWjIt;GA z>j4iNzpU&jeMbMHDWH!UzXTZi?PX+n_C;j*Z7&>C(#V`=pGVd&{pkxPwOEk;d6OPa z7gs7Tz_cEg(7`$cH{p0FH8*7GRQ@Kf#RTU)FH1j5B{WP}3M1yUZ`dNMjOvejpQEAI z;qh$eDH?qpP7R$>zS^!VTjTT&TdE4_I{5ypP9hIVrJxCuTF`LYu+o8?RP!0O4gdR@&C!W!kC0$ zOv(+jV1KLC%BO49x4|zqW)=*T4ru#Hx#0nm`*f`~RZ~mK^-kjnL%FFM3Xj0HU@^=# zt^0Dd_R1v4ZRBQiW-b&$IaIf@FF~$$>ydHJ7s-b3cXd$uEbw9R|G|qvJsBDf)9N>& zlY6Jq#y9a@BfOfMGJPOD2bW@aLdKt5_*36E0&I&CGMKz1yq z@=ACP_0q1DIGen?c&!`0$?&O5H0KQ8Ks|>|$`Oj?M9EW6>&w*U#>J-X^=$lEIKpp$ z%5I!Owoj)uZsona&5g~sZq^%(CnDz5_(@Kv9LP4yI?wp-t!yM$ZmLty%@S@!v>mSK)E& znYUvV9`6dlNmhmhG;nSF{h!&=2iX<#RLti`kv^#1}ugtANgIigo(~v9`6Ml}n^tQ4i8j22 zC%Z1SH!+2XIrAIVGK-$I!g>I&4^bxLIc@pk*S%04uZs-G?QokSDE{}gj@*jrMFj4} zM20n1c^jC%p7>y;gG+kDJ8+>VtPOU6O9~si+N#=@QHKW)N(}H=8y==k4<0r>#OK`& zAvXT3uab>~l|E-C8KnXeg)R4l5@jZA>qQ3X$*$7ZnEcDiw1rZ!3c=c;tPR}lmQ}RV zgU!iNsC+Pkcj?;F-BLbARJ|JCNw$ARrPVN!+gDNNYCKY-GIF!cn0i;^dqx?IkPIj_@zWEC%wtEW(_W$u>TOxukm|k5;3Q5oStTYmz?x9 z(;tZaFbn#gz=6D1pb84$M?|B!HC*7Wr%JMCYKhYvr{1UrQ?BiNQX{jy3@22}%j z?Q1(l3n{$S2j~Kd3*ZYG{!QWH?!NcGp+1W5x%2(|rteodSnG;a0<(HQclwJeC*#4a z8K;bLsALuVGGuOd|F%nNBX149(H^4ke)7+v_8Lgm{#;sJgFT6|!tQImLT^}yOXFo& zslK8W>)=c%)9Q72UZR&P_#dOub$G&ry;mS=-y;ENgSSbp#?rs#v;W;*^H@4_J)Ypg z%DV#0=B{?QJyv#2zLkwL%P=H9(B0Rfyt7BlgYIU2;Y@F2KhWX#J`1{i9hwb#$~vQr zN?*&Gg5czvm_Me^{3(5BQ~J_wom#D|;k%iP=~UAA4LCQ&w~;9^hArKQ?plZ7ONjmb z)V=|a)cbNP1Ct+S2JLe0A-Zq_&YKwS?%Na3OyH$cxvTPu@F{x=%$jeRDkB``*i??pmBO90o-;u2&C()0^2e76uwFb;>)| z%kZlvqd!=SM`n}vHqz)ps0Rx*w&OcfcCusFoCrde86T7!g5c`Ae{7gV7qsISR6Xr; zX{4R)5zo=2090A)S9E3o?zSevS3u#YL+Lly-Or9W%AkBcC;ciP`mgTp+5ZA{9D(-t-%6_h z4sW><%MaX0=df6r574dd?rAwp_Z(pZu$Xo+n|*iFz9Z1j*6-;JL4fbSK&2oI>ic&q zxncQ(O5`!Lg8v{48q0AI`q$J%1I%XMZhAh5e<1IjrQxIC^WBNG|0wVeuA}*%;J3*B zXK33eK<|Qr`~*)Tn-i(!7*4;f{xjBJF&0ym_SjN+vA&-1@9mn5mn^&S3X?DOI-E?- zXHGoZOX|COc21;K$KaH(?<>0F805YAC=DNj0&QML`;Os}rnj#?M{)g@1Jv4qQ%vy) zmS8sf>bo!R{DwW1dZZJn)Y$>!-rPi6JD`vIzM)+m_&27;Rj~(pMgL;MGqmJ5jM;lS zT74Xz4y=CPaSXrSary&)!G9#gcQ>_u2FEEUo%tDbb<1M^8=v7nVzNEg-~TyGKC*2( z-Ej(Lu+!gh3jZFH{l}^GH2zo0=4YNfH|3pn`Ie2W!%9a>iFJjEd3Ssx{5v|aP3@Pz!fPox;Gp3Y6?E-cl0aU9pS>_bdV#=C z)2RS12I=eYi?z^*w~MC6QqqYVLv$PlgW4&?~5w+Yc4%5tW&!LozJaPc}~c2(HVUi z=j-hP-ofFy^BfNGahMuZAnAy3S}*KVMf9#bAxL&}h2(`C>QXsO>Jv2LP}|61o>zgy zCmQha28pjm@GH39P~m^5XmDtl%Xg_2s8tmW=8Qen)cOh;=8AH=uxPJn2&YXBp~mAO zdqSM{$y}ZnYggj5M(Fzrg6>`yV zdZVD-Lhe%_*%es31iBNWV$jRA9^zhAc_+E{UuFxK&*AYt0eP$BIM*jVyjAkGgUdf% zq(GIXmBTy{Or#_iLP5Wm6Ut_lu2 zxPNlY!Rey{?xNqf6stt5?ug)ba=1$PTV)lr zox@;AfiAL25`qc=WdYp+HVW9v;ah4!2L&x0zf~ys5y7tzuv);`0*dzDYGm|~C<%&4 zSH~O$Y4}w#oOsaMhyjGK9cJCAU)+Cr@$#z8uJIT`0#Em~i_h^wzwyhq9EfS_ZI zL^!<$31}2h*srk&T12@Ctyf{F0v*l7+wM00tN&W0j!BRh;n{V1hiJo z>9rz|wGKgxhSydLx>3NOfQsLlQLwJPA;c&fCKyFCZ3%m6~e|@Wv2LucW80Bz-2xNn>-=NsLqQo0B>=cT!fHlH_UC<(+ z4Wefo!h$aXtQGck0nV>Q9M*{dHVM5tVOQ$~xeNXk+9sh`D+@!>K&>#?Xyp8jB7jX6 zPH!p{P!e{1LeDCo;BQlzEntO!MgeOC6am*4 za=KpV*Ee!{v+%PS8f5L&+XX*Z>f#KcP~Ry8Y5_&0n^OfXW$rL!vnm5w`B=>;^+=6pTxiOLjJXk@73e3OmCf^Rw0baFU2U&w`j zQy-_pn8RdtD9MUORVFWI47MmRcDPaLom74kj`TMuY@};dAaNqDuFK@GD3wD~ghM?~ zkZMSyLL-BSPj@Sq=LH4q;Ls%b``#qC4#j$ZaRYf?g$wC>+sG7L=>L8jc|m>sKQ;9~ z=Ob(4f592Lmpqj0>Qrlx8X=U9@bh7WUn)^2DRd^_c7!*4t%hHofxHmm0}g~g=@Rq= zz(N05z8Wj?RKTt9kp*o8yc*$-J@5-|W}gChD}34*q5R>F0K&JOUZ9Rw6d-`pPWUXm zrf_-@(2K64WuSLlYv8mG=uT=2l6T|b3(4BA>DNIrft)O(sYl^#a4Me`9)+7>R~dC2 zRSr|tjBP@+@hCArRwKf=gGW;f9va4|;g={tYXFDdY!6?fPeX6N%rE0c2yl>h*{;#s z!G6be@-92V=l1x=d_qpd7x4imO}qd}6E7&TlNSWpX;cQ3?BsPscIFEje#spo+Y*I& zkL-_x$n!Y4lZ(GD6EL5C7QrQS$LBNxQSf8|9o)JfMvZg+RxMzkfSuer z^RGl>`dduNDTI<}++q}xvx~UoY(&5yH}Ci>G#(W^hk$l&J@8XlSa(%$Nmr+Ut=zim zG+2iPPZH4TpMHf5if5_W?U&Ay-o#`PbB}P-Q>K__{CpzTy9*-l3&Pn2`c)sfnYqx= z^Z$fKMA#Q*^F}Td@(_PM5zFomBKRLdT;m5H53x1^)*|d5%7iRmSbs)D2ctnQ`Myv< zQCidf8?1TMD#E>}2=9F6z)!L0zZYHqJ|L{UJOzHo74nSAU-$zVA1`81X?P4Ojfi2O zPk2!!axbbx?nRZzCjd?AgBW;xsze^2Dv`&RInWp2Jy5CbO4};6Pe48aXd76kwhCUp zfNZ581EBz2HI>{*9SJIZnwTc4K~}Ca$Sa^S73dR}h|bt3Sg9N)T$=)!10kmfB*7x2 zZUOm3pqZDU8#RKb<}f}&3onzqMD?rUWx?jvPdk`NHRr|k3E0WUZXmQ+G#D3yPNBhz zpH}?@){8h#6%mk^eP&N=Ua3e_NE`z4;-~-LkspYPHt}q3g@Xb%@~Z3VhqgtVc_J>@ zfG^|$*i(MU`wIiL{ew|pN zMgxf1ug3Xbp2flt?ro=o+CiM^Tdxh% zyEkZu)6XOLZmgwU8?-qI0i~Pt=f98?oJ=!owWTPZj<{tdz$y{|T;J;FB;VNV=H-TRlQOoBfBWoh4YMCwk z39ssb zjbKoymT}d4qe{bBe*`jtvk+H>|1pX}t5OPSVLgoZ^R%m8n@#`lDM_X)>$OQ)YeD`? z_o|^`ZZph59iPGgFumNC|3fb#^jL`SM}kzY*Jj|8G;^~y<3=mz9=VmntN*oM#Sr=e z@IkAl4^^qst#r|52=E;3+N@2}s?t{rH5-k7Ml#^k$u&U|XL4$mldW=+W zM*mJSQi5s1SA(^3Q$&YTGE(tA%~`Fbabs8!{4L`ltmyfov7&z3upLG!-b*I+!c?-{ ze8)Sp@J{bfEBy-pJHA34QKdsNzuX~7e+^uH`3=_wr+&L65V%?mTr*Xje(UW^em4L1 zThuGO>e%2d^QT<yN{iFIu{EzLfgMZJLNZ}O)&ByXuCox`|F(j;|<6jw@2 zQlJZXh=hv&PFPew`k7*q&RlJiIzsg_`jI?&HV6B$P0a4HO~6x#$BSn?o^g0Qc&^Is zQYRLpzsv~x^Aww;{BW_!`|sxZb7)_^3uOyTku%K4(8mf%KC>MCR>scRLilz7tmz>FHoSwdiSv zo+^53)l-|EX6k8{p4#=)p{GOiG+R%H>gg~&9j>Pr>FL+>)TyUAdYY@JBlOgzrz7=r zl%Be^RFW(^IDSdhQ@@^u^faQU)p}a5r*SPcNqVa4sb5b+dK%HwYCWyj)3}z(lAfx1 z>ethdo<{VvT2JfsG_Iv-lAfx1>ethdo<{VvT2JfsG_Iu(+1mJ}YxH!5p8iHpFV@qW z_4IK){j*4KcbX%&=a|=79C9h-%j@>5vm3)DQY3t>v@RSlN#R*0M|h4Y5}s>X2U)Zu zTq5sEofr8hF-P9YjGQ|FKDR#Z^1fw~)RJ#u4rW9+2RkG6l=I1dnjA>8kheQjF)#ed zLS+88oO1Gi@$Z|N3wt0fbuMq>-~>p|5*dd z9VplPAIbhN&~4FvHp=z>N3#Fy0pAa+l8f~IUF?6@fbxq_uJ`X^{}&A?ccNVH-^Kn7 zTR>Dp|G6mF`**Sb3n+wgSCu?T?|%gQcMWKN6w3AfN3j1<1IksD>-~>l|LTD9(J0sZ zAHn`d4=BI5N}j0qpUeI)9#DP>%Ju$p+5aU2%EzKy??0FQj~!5c8OrtkbJ_o81IoWy zB~Q@%&td=H98mr(lWrjC@(;{-hU4JFBnjMCCc^wbJ+it1Ij&Ba-rV8ll^-J zl#fTb-oKOmj~`H8h;qGuC;KlPP(BgmdjC%LKXE{LQI+h~`~MpIFB(u@jB>sIud)B) z)N;(rQaokD{Fa(?Q__7M{(8w;Cc9t@AfGx<7RdS;`EZtG7vK z{5I)a2oF;gk7Sen5r>U5O1|3r77XrJvk!z@eaFHleFwrPe7nOfzHQ-V-};h?zDM{C z-Y-Xfy`KLG^7rfc`N%KV^Rv7seX~3{lAJ2n)NgQo&rk`Y3VQ~&$~wYB_^3O1-}0I4 z2OO^k(SH#AdfzHGRZ8j&5tqNjV_GMzD)cLmd*^5idQ6hv;*@W)I~*VV=p)GjJ2A1! zRM$=$19*|y-sg=a{HAYt__*&U;WvEq!%aQ|)(d2Chw@bKK$wVc!l;cy7PwU}qrGAk zww(5UP-;GOM#Wga&N{X(kry}^RDV8iSt{}=?SP(_Q3fZ!)AU8TLo$ude;amS!YrKw z%{t>LbVZ%Tuli0bZuK2leA2gXF=)5A#rNdmX5bMtyMPV?_vTgSaW7+B#ip6~ekQ)> zIEly4zu!{{PH3m1gudN3)zwTa?}o zf1}hIwZ_?>C08zMElrCzMKco336%FLy{$28+ym*|9kWUJPJ92c@^NdaHO_Clx0nT9 z?0!dE(*$=<_%|kt_mpp2@Loyn@OMeU|B?!VuNI$X-QaJ0ne2!B3ZAPDA1OZIJ%V|6 zxcHc7urw@usQ5Hsm>zB@{=kwWPYWOP4e=f9U*(ecVSsqfGyu=; zQ&Y?LpzATv^)%?PEeL#qwkE-&hl`Ke1CF*}Ib1-oYL$Fpez$k<4{Ru}Bfgk7<{#yVJ??Nd zMcqA>8_Jgmc--#m0gs@|RL~CXs+HZ50^%rt6&%>hoPKZA^n<2|?6O8OqM(t$G5A~Z zxFz))Ayo<#nb$3dnXbX#623255R*K33h~Z~$A$M53pPmO@l0H>!IVo{LQDZ2 z7#?Do8bdo1tX;}7xqRvd(>0(~&q{b?PoA@OS$$HDb*MD7)u+T4JR%hUzHbRQP)|w7 zG3!t{2%6Sfee&F<(%c^M$;#=Gg4S|t&o@Gt&_#n)drXbyclGQuVDb=H^}^Q-(F z&JUv=VIr@!&ede>%q@eR9kyTH#yYLF;PqN1+8uRlx7L_l)~M2Etx?MGMET(TA^m-{ zyVtP-?Ys2yQ>`z{*Xwdj^p`553Fl|``$R=bzh$CL;I+GVh=us=?#*^EMIT#RUrwvk z+aJSk2DWdE2{@=@U)s2~*QB;jz45~e#|ouFaeY`Z#G<9y4yGJmYc#D=qpMh-1znAK z$vMgSNF3Ws95HLJ*|j#}p8`1(gA6G|pJHwkMtga7q%*PqxF>gTBi7bcgy&Op5Ht|7 zVm5q-e=v;=A1(eM{CliVzw-?V|JIime$979_=xZ7@L}Jq@FD1+4bVk@1D*7sZ~e?} zY3b|XjEC{G**hE?Uk~R#rL@W5M#sZSg&akBiPEGDeSKrbiyYhQ8?DHXs8pKd4ax>Q zc(=G$ia;J&>dyKWvOUPD`|V>Lt1KDvjnENpvKKg3+6x``L1*|r*01Ykj*--v7fWLy z|ASb+rp_EK70jG06MT?tzu-$&$gZ%Iep{)}jo+@p{rt zyuk&QLdBXHa;(l+tyo{*sC*{_@FIDfoxt_aUEWTfPR|4V9-_hRHWsGAUa| z)>>@xjhqkUD~^lt$De*^j-L?7K!{PnQ; z`>%&HqxLSx8z;jfF41ZKCTWlIbsFun{-M#{-gI)~h{36}5BEZcPDl>Yp6!A5uYul2 zblQJE_;rl=5~%>RpH12a$4LI*#ZthaeQ>lC3KmLBLHl{IugX1N1MM@@LHo?7m5&u0 zX#M-w!&yZd4-ER9PIo{5ws-~Ye@EI|!ZLURbiPNjW~`Q1zrJzsGfCRN2-?30p3rEI zGTSnZ_JeH(?Z=;!90u({>+ANvuXd0ZQ)pfv1YVWHp!pMm=8d3nlICHK6Zj_0!x*o1 zui^x)^THf2@sBZs)>nr)UeW<$7BmMvK<}S`-pd8Oj|zIv1K-Wo>HU4^%B1&ki&djH z^vyEQMbeGH)xSb!4CA_ea-+}-7PX$m`rX~$^Ke*_#)o~0!$bXrSVRBS+AYV7b)D;U zQ@ove5Of?(?kf+ErA~AEfN%ZQa~FPh*zM1?PjR@L67HVWXNwcm2|C=P-lyL4z$V{3 z{qLjvo9lO7xmpO`EaTYhzU+uKfq9mIjfn58XOv+lVb=hDq1OQ3ZCImv9=bjDEb7Jf zM|^9w_T0 zd)GyLcY3x<1-k7qt!05duEV)B3Fi@0iD6&qa1If0!lnU?A7DP309RGZb^&7q`9qP% z{0_p{L0vV?m$23A{ln(!OuTZI{jq=c7mN6|dJahr>Twv$w$HPdFvg?AV-hath``y9 zK>px$u)U&dx2j?-q&>#9Cos|Ez}hp9>mJsg2lEL*)PGM0J^^@+7_`)XMZePj*F z>q&S1orQiuw?^wmrOh78j-Y%i?0{7g{&5`PLy&jCVe1&^Ab^FqjQQ0g!1Gi~^$lge zh*Hn@CDukl)lK&~-NIH%H*BaK4UkE5iAP=c3)m~O8|_uuk#MC{5RORx#rHvn)z;ht zuv0FuKfx>S1D*!zb^;!Mw!x!u!Uj6Fvn&?RTVz+YGHDu_0GP+ zG1B{jG)Rdm!}i~?O~$wG?!*v_{a(jK)_=&~4~&B|9P$ye>87k|$COpySJ!#p#vDp0 z4?k5uqrz&EX>9QumeeGB@U&2dxDA$>SL!W$^EH~^b#L!?0NfDN>Ag$lZ=LMLNA z4Ug$vgnwn+cbskL->G;PwMI0WP=9TTI%)T;q3#d*CVb)&^az#gZ@~26l+hpB}<+H z*}jDO#-sN|ROL>LMUk)PIQX7pd30suN{q1#kBwu5e7rs*3VH7ybvet$H3qjA z!^Y?WFZsa}u;+ar{C$13LS7*c&4@`Ql^^?r?hhWQwXbuuf&Ew}_$=nnbAlhrU|$w= zCx$wwmPuE3N2%Y7cOK50sX^(R)|gx_?6@*%yk7P#+Hx^ft;U>id#{4d+FczHd_LOY zg3K|)7vO@w;dXn>G4!F3?>)*P`&Ntb~cECt))dR_Xo`+_RLsYi66ILP_Z$~|8tu^g;Y&bYryXs?f$^U0V7=V!8RpwP zy4o*U&z$!kK;HsQR#j6s3nruhamBjAM>0Wj0I$0}!|`0?92DE6Zkf43^27eSD;Sdk zzT!D{=YcSVltR-WF{H4#z1^JBiO|!#l&q5BD z)>KprA0fUe!{5_TXF9*v>zqn{OTX9oZ|@Opp-svRA!~e=0Lx*+9A>-x-m=h~X04Rd z;0w9l_87*Y!7xp$0_J^+cOjSwfK6O?4aK8**#9O-(QILy;d! zIj*g}@P#C(Ly)#uFHR5iX_2lvADf%XM)c&zzt`Fv&3KSHH0>1554MlB&Zw76jddGy zrw4OqX&8FN$s<_z;2(v2ngQIOhKv$^Tdsp#4?$nLOZ|lD4Po#pnNO7&83F}oz)$H%WzKZWjDc=RXP0)2fopC93)`_(pI@`du zSf?&B`4sqO8uY5@+W*|zppS29YJ1SRw~`kzhwmls;VTa9MAwoG$7nC*QHgz0Fv!Ko8Jq>cW%7uVMWtU3jASg!cq=RnThT zo5g$Z)Z#gcZ;yi)M=l2c0LJpVxVnh6o7rA`di6W%4Yu1HZrhK5U&uZ~?%9@V`hvjy z?GCOrWr7E(+YZoM;5XkUmK51VSVv# zN4^Dq4y?R4&$6(&xB>cx8T(NRbfpOP`J}}y#jklAimxDyV9#rZScU`&fFyyD8 z3uWg@zgp5d+prfb>+pTM_ssABvSY`%El zD$6dmd2h^LI6f@xf(iuG^!sc+W)YCHI-& zS7>WaLV#b~T^NnP}8Ue_N>o5;l$tW8wNz208o2qgzxb#^ZpmwlE1V-~_%M z_Vtc-Yh0{@*~BB-*(Bfw{*d1T*?CODKeo%Uc%1Nyz}6w(kQX=)A>SH16T1$OCrB&U zH(8+y=?|d_1lLxWz6<<~!r%L8?*vhwGMhFI+eO(%YuRS_&eN1S_=CNrz69-E+D*W{ zBT!^gnMYbc_)z~pV`Z@hzFWPeu9@YnrS^Do4`|E4J)pF|!BajK<~e$kZFpcI$cUYg z5q}^T;VHob-;W%^b6X;j*vkj`yYQ^g^Xrh0;@N@c1w8e5ey^9c7~lNgmLfoXGoHsa zIMa3^U-MZo;{6eUZ}-S?z^5NHFagik#mb^*H9Xf2{&GCacO?@2Wx#a>QtYvrRx#D{ z8}WCwo__{;;8@i84f2h6+VFJXIRm^3I%(yylj%SEppA?5?{Cw_HKGde2lV_B-2mk^3_^C_bl>|AK*=Q?kVK!^?U>Jje343@^L-C3HdHPe@~!D zmh}8B$UF4>^~kGw9{86F^!!NV{dztf`GA(sc^~Q0M}D22UxR$L zp1%|MU3z{V^7VRt8uE>L{&M8wdVVJM{cf?NM*Ge}eCZ zc`77}_poo9sJ8`vfs}7xk0e$`)Cj+UKgn_iu@&&~xV}tBLykf zaF3RCJIr-=46MujS;!sfWEi&Y&#|ZKQieZNjeX_~%5dOeyVY%WDK99)v#R08i7CUm zPU8K}tQWDDjec~0Nj=vC_Q$_L&&}K$wr_^Nv{WgB9=^aj$L#cCpD{2oy@0S-Cn`Bs zr!oio*|}JwH#Ivg3%FLLu2V+)fUh|GThpSGe}%2^TJi3{rRnwXk#dYBN!tPZMf<_9 z8!}bLeUUc*j@D_2XLy2k74~KG(+g-DcwUy|W|w6FVHU8IDy8s0%H5@o1(xl~H1AyB zG3!(%*P5^7fPV8VwH&vFlrgM@!qyxwD#{v-U`;%(mFVqHgj(66r*W5d}7j?H_x_zLblaBQuY zVEh;-{5I4@5z|t1g_uWf#9^#*Jdfvvtmm_LJo`cUsjL^WcW~U5tCt_K>`-zV+eTdS zMoq@0%~8vtmLt}W$wSur%!4o&=1_;w_~R9iD39h|w73GQcY#5Is&+9PniyHll_Ay@ZH_AE_zZwFc zcZB1CjkJ?vuikqvftgGqt*-mT)ZOOYd8Qbo$}k1K@+-te^A~ z{`^$lq+cJjgDjIsJ8#2;^+E=wftHYgmV=Z_({z4pHM5Pf@S%yzi65ON zr}3{?0GAvC+arEyr$Ro=fou-J z|JIG?4CY4Hv!}(}=z@JX7dGlGNm$c0{$v{+<~m0FRg_8XPYSqrLb=aI_9(Y>+QO*(=5kySHSS$+6m9&A6^oYxmv=Y?AW8 z-6kP-JIv4D*4kr@msFS>hOJ|a4P#Bl)lpuGcCklw@-L9>*k1xpOTf#=(2psQZw^2{ z9l+Ry9Sq+{0rm+BTQ9N4VavCT8zVVSeBBzydc4unprngARtJCM2J_GdHdJ@8ZPGyO z`?$d$dR$nC`MGZjeri*48+)BSh?9~UdvlnIb+EBFx5qvsdl2|(4&Ip|zXb1gn0ME} zhOxl+Qn0i_@>^;Y7t(@~3Q5JfEH6UJbxz3bFWD>8=C?UCy5f%sjt_R6xpfTJH~1B3 zlh2hL!~y)G!f(XAFzzWDz9aiJn(v4{4EuD4dtuY0-Slg;Ml<3I+a}S!lIp>JiE^s`IYa;$0i@)cSJ_bGLF9$sk`U3E?Nv;Cr%MN%D6)MWR6i5!zVLKgYU&zcyIV*;Iq*1xcXhyKcl^P8+to}mS`qE|KEf?_O!!r*4^3l(3g)H2ZJsLi%F6#!g8T#@z z;m4wnV4_t`wmHl`8f}htc(Ru1ZQhYRntlV)oqqM!!J7WbxdfRsP0S-rPMhebqfEh` z@Z6abF>iry`f1?Hhg^g6pc*lh?_z%%IsyHm@N?ALb~)O}zoY^FZVc4GPmlI6-&N!} z{|k`%k@}GaI?PY5Kt8~>1r8@#8jbeY2FkCod@R-+%7<;B$*25hLBw;W#5;dXJ+-yW z8Yi8^I3a(*ms>QxsM|hJ&o=5I9xskq$IB?MgJqCS=s&nc3UnswmRRR1ThRY$_|O?s znDp0powC$C!SWvavF0dNQ@;~%VSgDopU!@Oe=+BXE6zLO zKHvYnR>;j&Xk*DazfEebvL>R{s-Oerlnnmnp8SDi#FV zKMT4@;0dO({bG@t?U#rY@kS!G*e?^QV!IhW><40BXFF(fGw@@BPGGaTuy(oRIkuMU z2;qlKakj&}{cp5qpc60-xXi$-K8_Ia!f|MHsA=F>Nq5+az1T-s$~bgUm+h5GV4` z)+$--9W~xPqNjO6hK2hSfual*ZJgw{%|5p-Q}?g2&QNlbbD=1s0Cn0RJI&U2WH;l; zED6Qc*q+@Sf9{;n$H9Ku-&l}gF<`;|(^TT6C$UCk208(UfY zj(`b$i1xLzaV(=R$7mlon8w&SgZ|ue<~*QGX`PCg^U@K0^s`{R!gsXRug0nCvrXX0 zrNR74RpZY`bRBm2cZfb0NDjuWaUAqb9v|U!G*;yHtgcd530N4DCH#qd&+Qxb;gEUr*=!36wG<=p0z*oh#NGry~AwABNF>^YuzfwFLV3jyFL1WJIBwe9n8{@;6 zI}IP^}C7f6fgswta*|=at5A_MlWAxMP97Fwu zZRoO)^7EuNwjF(}{}p5>bR50UcjR=v?WtTl5i=R#+N8tK2EXPQw7-ROlI;rlc}(-a zhyPf7HDcpDh~b;%!D)NM@y+*SN>{?C`xB4N)DNHTa*x$!0dK4aUkn3ZY-_z%_n*LD z4Zp=)%2vS}(D&YWIm6PpeFXMH;Md)bIj`}GDco3$nD^oXorxFM7;}O1U_~g@Bj}BG zwDG@qeMs2+d+&2v39c%V>chZ)}O%V zw;X)19{#>2`aHKM z%l0J8$RmJHo7*pJC!Y8X=I(HgyYXH0X~R3=zy0+z)GNKeW^_Ta&n%;C*X1m$ydQDf z#@v9f2=Y%S`e`O3}EeXSrd-F9L?0ne^qFuzX ztP1S0bKOM$6ptlS7~k%`^Ee&B*r6Gs?MJY`hPl_7*ek}fGUl7c z_FF5hJ>aWy`ki4r^;rM3DuQ{8Sc@GI%F>JlIJ+P@T=!S}>p20(ElL__bsWCyo`HHd z?SG+bXfe(v@_&kbGeF;ZKF5$3uy3e$Pygp+#p6dmOICo-^!f7m8eLv|p-n?sLAeAu z&<49GowyP*gE)Omi!Gw;fZhHYZlxN5@xKk7_0xY zY&gP$G0mY*gLbkQ1N&!RfL-hVar6(r_W;|Oe8m_xfj|15!AJCMyixBOW$+hxf3Bg+ zdJaqUiJQ0{btaw@@l#k+cMF_xKPoxzo)b7m**=!F)LHu(7wKiJ+;HiKh%p+*xOVt~ z<^TRs9erjJV;wOLH+&W<`2%`Ti2G$TThKn|9N^H#vM*_`qimljOY-8r?Zih-HDZ$t z{+XB&IOmf^d=&Rc1BDr4ydCDpdbo!Q`Mo@du?I2s;HP}8#oG*i3pbd?($*8W()`w9 zyyx+;UBCt1UdR(|Z_+I6c+C5``4Kzy&r*()Z7{AgHMUT!w`dnMG6`L&0q1^_`bLcy z7uL@`#`?*4Y0}Q16W2CF4iF|@CrR8;Cr3Ne**EFRzKP4u#IO5E&w=z~Jq^#luBAO? z&=B;{=r>FnZNa_+W1Sg?_4oe<{qBI=#Qp&4;0&uyGwSTgwq6wam%%5bo7TsRf=`n8 zCoMjM2IN!tz}iRye#h|%-H-UHg06_TVn6(o3(Bx>YsQ)7gtd1;xpd{!aZ+h>!t&mN z*^*~!ku(S8*};6|wk z^4g7aNL`4_-T7u%aqz4JefBsLhd9F*Z*49g*-qaSeWUb|eikj0d;~v?7<*sM^#gsH z`h0g_9$awl;jpVdKzh?}5G#uK^JAwH=hn)55SyOUR0ey0OQ!Rj4{;ww*?#bj%yO?! zHF=}0~+Hx9IT}thZ2yN1R)N zT?d^?hdr@>J77G4abDmb{Q~&?j8WF%FGz)7z&%mKHh&84SZ}OBhfBZ<*;EdDqrW-s z8U;R!q{0k%mk=KYo(T(W4C;&C*I++D2h-179Y7q-5h3feJ1tHJ#$-guQ&@-VDeG{j z#dQNq5dktTo#`IT_WQ4@iHh&4t=~P-jC@U!)!J`EqGF$C-+6>`fF3 zypl)CA`ZbHhy^)U4!-a^T;vOH`FWlIZjWAoTb!o>jXn>zcQUSo^APJuyt&-<9(e&g z(WAj$Bi3_{NBet!1LADn9>^bt9z(m@V}Wij;xRCvVcX#jO%bP#GXhzlg?1iJU(cay zz=qvF5^}7xDdz5384n3Njs7a?N0CK5<8JDgA%z0%8S9`g#I!Ti zFSNYGPQfnQ~pV#4RcuX1DR5#*gp2cSzZubW44{lH(s}Sqc zZ64aa0cYazH`dN3;~nyh*KKc2;59ND3-r2b1!(t20u~@ z^#|fl%%}dx&vGq=t+JP}ih1wLaDdNo?pC{-gYnzEnPUBtinAl4(qxfuiu27Hj8$ML>^<;d3}zZ5Cr5Y1c*wjvGKzv~!k|CR%5p&NIC zxO*O0rXFc^!EZ;u)W^y&>qpAatcYV+R>*-n0o*;T_rM1AUR_V9WgWzd-+=lK=z;!} zZ#Lqa4XAq=>hj)zqb#G$Lf`M}dqm<4)%p2JzlmjC`1ht&-roy8X6zT@+Tb5hgn!@w z&frOuF~IQ*@U3=t5&TM&H_5&7h~qWEr;0ph)SIS^;cM5f21AgZ96$qN{sNb!*Yj`8^W6bodO>RjY{}I4Dz_G2g}u{30%y6}eNVJeiL*?< zV*~EIiCe;pA{~S)i0-FRgv%IZ1usFY0K-+gU)DX?R}-SoT_amBZF5vkP`& zBlbJxXiHs#(0_qXOAF#9A@4f-cfL<~gm%}Ewl!`0@1!1P>%tiV$j+L!>Jm$v*>!&I zJ#&=fU|ozoT3K2C3magCZ9drcZgzc1LQ+@%W%Bzr^vQ3mxDP_>=b3r@4)Jgzj>OahbeIa9#e$syoC$d&Xeid(9ZA1|XCKaz6NjX) zDEpO&cY)7Vi>1Z>!0P*bW3bmfK+k#lxS{7zw;^8G4tz$`e}0*iWknfZWS4*R*><@J z_U;qMppR(&8{pb#AGUTpKg-^Vb4ReCPFV9Ecky|pv3ua^7O z8Z21zX_w=?H~99S^5fDL<9%XWA4N&_C%EW5Bc^Q#A7hevd+Gz=Ly_guaq4?|40yfN zzf4ls{#oq7-w0V6SO7abfHs<9;(XdZw0ALLclO{+Y7fc|ENCz0JvT1fs}3KYt88^z z9E$Dv?9q%x1MgSiyw5d^_oeKD+%n>lw?mI{A-0BhD$V)u=Z-v4?#ZaOZ_Gw)JL-8h-yj*VG&IX2+kI{Sv~&0IH9=UaXRF&TO+IrLJ=9?{~-p;L;v&-DegNZK-- ze|r4GUe4EK{6qiC$@m8Wi!Y;|a2b53f3vHz|K&T+^Az!u(TIAH0reVDZ_I#t(r2`H zq_cnDaigA~d7?HQ;}Cas41}9-wH8|A@U=A>dIr{*Xk}64F350M$7elwaW3AUWP2TH z+x{$gh;|J4C?%d_Gv$6~e@%nvU)!s^KyNVctheh(>2shBy9oA0T(<$|7&ajLhuxOe z+5gh1Gs|$^fH58ebd%4pXNA2Adp3gku$}cmUtuG%J=_NoL3@)aSM%erYZI_*GU|ueK616CgHbaUd&ZVS_NBa6!vW+;IYfBTh8y@aS!v5%a0g6#+zD3BUUDA?CWV| z?aSCk^XN)n|^Go!;`<|-xmJT~$`we_&Lrwj-8>|51DTOUmcflQ9;tU}6 zs(Q73VfF$0mHUk1ZZ~oMFD94K*TFJfe?1Vy`Vz$0le!|}lsG>~OUOJGco~8_Sxt*i z6n}&>m>=kTuoS#?f-vJ=*D}N(IpLe!>D^NN!OS+?k$7*!0vhm)=sC14bWrHir$xI( zkfDs>G2|!YDP`zN6+S)4Pu|g}_Yo&w!|o06Zw)`NJ0auI_UHPdxxb9LLHG{AADB*m zEOAPGLtERnT~C<_K6I~o%-6vA066EdO=Ded1&o-}koE8hrUPEW3K$`?V%S5RLpYnG ziUY6=0}XY*W$hX(Vy`&vAnF32Nk3oH7VK#QS6Hit81zl~PUs(iVGX}E_?Ef>$IUec zdVq*^f{q|;H`;83hj59QvIydctDv)S4L8<}M9l=`r)ujSWYpI)mQpAE2K0aNdlqqR z?+|(&Z11`}#sbfBn}vfI5@V zi;Vqx@G@{Rq_cnTErgML-u#Kv0iN%wj4f9Q6W^mvZST8(?{Lm>F~%H8?5PVn^}qCv z@m|;k+Bao-*-n;u+~Yc|f08R37qoY6T!1!0=Cng;dfbP5H2mG?=R19kkOeCBa_Hpr zUqqW@E;r>B5?;q%8w_@MtI?Rt7#P^l-jce-?{da*PMH2E8xCA6$*X!rF7y^v`ahiZgVy9EXw*`dIWPol)>I|lHT<1VLsjh|9E`H$;l^T{>fD{ z8zvvqVAylN_;$Mz>yGgrL&e=48zS^i<~e7(W4sdqae;Npw3h8@Hv_KgaJGNBkYk6i z4?Y-stGEaEEPP34eHP9~0e59(M8v~T#$h{CmwAs*K36W^khDc#o&2TtsQ0bO2QW6? zyMVd9N9=FHC;yX`vKGIx)(pPE_=6{KpRTC$L{;!)@hswpa~=BMDfr!lJV~iD*{Bml zOnIy$;t#eJ&z}`m5C^KzC$Qdybv>Lws|D`B`O(Bf|%|5^*#EXrs9= z>l*Xih>zeL=N!ga`Ms@kjkEHQAy^9#1GJPl zjreNmo9&Qr{~ly3`AMVUbK}TsLRNpy`bYhp@;|BTL*C9T7w1I8IyJM*R0w$H%$#c~ zz`1?>j^5|*1RSL)XGK1}7ECnOf?Xd8J_4L2KQ|R>aMiw(e0R6Wp}pHJ;u(Oi*=*Oq z7x92~PUvI6%|Kl&AoQ@Zh<|{ec#CMW|M3RuPQZ&FI5;izuN2=g_7hb4j_rQ!{x8Fa zWawb_Ef@Oc(LZB9#2uc5PtXYZ1N}EBYxj1GI}YA>l=2F990bouOJP4`Xd9O*5+ zk8RMMl6o21Kd|v`DK7L$+(p8+ai_>lc;RX)0BIV(<*3pkT40J)W-d}dKYjr2YRqWlc~>qKzWO}D&d0x zJWFt{>7b4mbs%1VlkJ#iLRSG^3NQu(&zL_K;NbMk80JMU?$&)Mwg+o0&OrON6TWag zbgnX!3O;avW*@MR)cx1Bk!9j+9N!~eX&YqO2Y_obWq_ggalR8LfqbQayaSl49;{Xu zv5vqu`T>1F!zWMUeDNqDFLR`~wKkvJNB*F#soOY!{X#vE{g96Z?~y-gdrY7bPs5Yn1SZJbz!I#RL%!;u3S`Q#Qx>aXpm*-<}yd+sA8H=y7>ygMDga zeBv*Z-5j?VQxV2g1-~KorxDLBH->TEty|oOHw-O_pjl7wzt`rf%nIog3cbuj1Tbsm~qbx@+VM7 zUeHwhUQ;lqhj+zroba)n#`|G-|Cgp9{4OW4-w(cC-0sT&?uMY=9z3;pc>fR1s1^SS z&pW8!+1d9@I`V%f4eMZUa4njN?{MCX=cM{(wE0Y5a!zVF=8a)@CG8r>W3gV4@3>~H zycfIyS;9O0An`OFDdN3+BF_GFDldTV-!!7h4LGhMJp|AGT)GH$j;4R% z9sRT#-D>&_-o!i2SDUC2XZG-YI=|OwV^*QxBPI}M8nl?e|DfHYpA+5Ct{g`lA-!W4soCDqwcR_W? zb=vvogXhl;Qx0xy5oftK6FvdI5sT8d|4I6P`5rRo!n53ck9}a2Q6`A-_tuDe2*PbR z_sX`=PPnaTzJ|yBLs%a=o)%O1>ZAi-ZX-=PE#NWE5zG5s$v&z3an2Av+IM}mvtS2D z*f0Fm4asxFCq=xuCOi6mdB1UfJJJUIS=1d>Mfmd{_)dp6Iw;MUJBS(Co&V6$kG#>Z z4MkqWdgMP``898JLNES4kpG|ZAa8W+-;jSTf3xM^z0tJO#+rk@MG@}<9yA$ej!}>6 z5@J?fIm`Y8eqdJ;e&VM?uDKaF9t*i~TF6xHIpBNBDXfcvzwz5Ls-_cx#}S{|YJ+bF zd;@;DR`mTk_B4+b=ZQE`#Bwqom9ePgHS!tvM1@@qo+AGcK7Eetq7H#Mf;~IN;fj64 z13|PGM0@{}*ih){r@eEqW+F~gn$;4fqCk zK}U*tdKqU1UFX2$53IPC8tpj2&)>!KJ#Rpcdyo3I;oIX`)jVfA)S2~5#>K!c>6U|8 zk85{_F}@+|3H%;Hw_Luwy?Qk6Bz1fLDh}%bFPnEvC`GvM^*)CM`)KA2r()Ua*gB@ALW$ecvSs-B&30Gz$njSq-lL`1 zl%+b#R#&Pw+)#wyiohKxLA(>OuC(JaW#KPRUHi#Th!rdVxMBcrK#$Fcyt-81ihl$0^6D z*J^U)mC2kJi+N@l{Vx9PBr%_$)5>E1Cb>@TOYUo7&w38ll4(LFxO86Y|J9|2yZ{cD zqFnzCljpZ;`&E;^Qhc-btx4OAevSEx*jORg#XRP|A!7s#KT-d-M_3PdgYGpc>E{u? zwrMHxcQ}hrcsUO{d-o1E^h136eLNh`O?Y^YP0{8K))&YQL+7HNA@mr;+39rgQx2SL z(ENGSSDtnJ^ju9Rm)68AzT zNLS(x;<27Zn9HP@&@uO{5qJTO_%4-(-b0g)b@slr6LYn6@!?6wY>RNN^WV@H?o>3+ z#N*Dt^ZotE;fDXXY%$Ke6Q|z8n(U`cL?1$SbjZ)SfM>);=8a_?qm1tePlvp_jd1uY zq(b(CJ)?<6UwBu9GzxooE!b64D930i)M zPjAuzfd`|#{$F0o_HbThJNpAa+zC8-jCln5lnMGkU&fq>K@S>j-|X1%PzdLMFdsW` ztPV6jy#c>&f_(OjpyYpGneSK7-FUZ4Y-K{_yKC@nnRvI(v0+9ze%k;(akhh$zaKM$ zWw_&xazr)eSgP!xF2{Zho!Een@4*8BV?Tua1pn4B+?_de`X=eN*Iz)q;igO{+Bet5 zd<}@9^e5*>?+cqkA)Hb9g8dfyLt1fv4QU6yr9CwlI!;%r-Yw#E`+t2u+a(SyHJBI3 zH#v15L;sV@`5o3xP2Z;OZRq#9pZ~M^;d+N6#(gptD(}W)}Mv3GZAjm`bNUk_jZ64*R z!=2r|kr3eFeKtBgHSa?XXz*0PH*B2i9$?b}jt2H=z=1X_knOOKG=BwP7$RUu)Lc&( z05k8_;6A|x`nB=BaVE{UKb~g~A(u|KPPg_n|4U8|c_EcH)S)_iU%V5(v@F2`hMed5 zzW!(L6ui+UyH~%5{T1-YSBa71-jbzG{Qk=l(hy_Z#5qSfim~ua2XJioWA?9s9eTkW z!MxCXyzuo@`XUy@`V7VVVZ4W&h1em)xj|oWHy}p3hvUX?-!J7nKY1JWV}v*)ZFr9W z$4J~j21Ll?pkah#6mq;5aUQVS5c?ZiS|Nq}b<)z%GHL15sN|vSV@wI?SpYwz*jo!B zK3JdEpK0f&!p(lg{01DD%k&NL{2k-KoP-(Lz%J-nB3Kp{Wpe$kyY`M?J>yg|V4}UdFxBhL4K+ICX%; z%Sk^K@WZ=Fr*ZFE$Mxs>rDm=)IiNppZ+TbSAJcI9N|gt;k4AIf65=3GV^W2l!aZ`` zW=mZM;6ECP`=bVdZ-?9JwcqU$@TSaj5pMuL7VvrlVZ*vK7C1=${sr$Wh5uzL-yz?F z`wXOD9gZtPVkV%Z2(9dS=JL}2Cex7#!2hN=1UQ^)OShIqLtWUY8 z=u>(YXiNNxeQ(ehx|=xXfId{>CrS6j-kmxh9x!w)L%$*q2wWKFX&HZ~^Il-0x!&MC z@+acylYUsiI}^1c%q>^#)M5T0sn24@H;)^-5gcp z+r&$^Kpzr(8;j)aLOpS2D*&Dpx)|+Z%q6yYA#LZ+5&fYp(O=(-Rn(hw80K)@G3IJ# z{|k@64(MT7v|i;I{B0;N2hLUOm$n2MkBD~iRm1P`V)EP@_+k~u2KizZ>-iRKeedjl zzDmq7;#-HWvv1Em$#=Zd12AZ~cpm*rd%tWh8FGepG3{ZytlpXl}n0o9H)^AmPm$R?TOB}oxtN+0# z#@+>+tvvIa4%gK&Y0k<&kBPLdvBxv2#T`38$N5sZDY6geIUaEA*(s?_k-a_5(fcjP zJCLtvj;>mPyaoAUHQv zxYKKM20NS9=Jp)##eE#glQ)}wq{Z7I^$T8k0{28>ow44h3?khc>!m@htLguLZcR%g zVnR~R+>83CgRDu*6tr7pEJJ0 z64LHA;+;lZJ6m4o@4iXA+o)`9q-d+)=eSmfcQ ze}pzSc$UZrF-8VQ^FgA_f;_qR&+gI%0@LoH7yM(?!e4C|zYhhpdw~A+| z?=-(1_o0w|Hs~Je{f_w&&oh!bs}wpmVJ^jfEAiX+Y}=L=+KBI9tLgQx$NOPy&-$OZvqbn8 zpBbaKJ4$au=r2LQ(u8(`fDPZ|vK^!tuWJwboVMu?lArVCjaMBK@0`F~iNaqr41TDg z^dXTpz-KOS=>%?Fz^jhar>7Awh=VF_df3_1v?-^DZ9~8B!F`-j`@^_PX%p^I+L&#| zUh^Q!?-bYS4do53_wn9{i#8G0nSRH?Xh{7xTZLoj1GlJtuB(jA6Z~%YH@H>@rn> z_?~p}xK*m@xat~p;sI+vV*zXH^CZaAS=1XbZ)NUhlji?e205MbyFj%YA{NH2;rD6E zF&|y{CP>?1G|s_|u3noMUB5Q*=u+tJz$e!IMZA9y^t|cy1kS#lgM5}ui*W8n=nBL) zbQ;)3yH3C-DE8d;7x)fmsoEOv`55m+`3oOejw<3FQ+%WU=J58j>|4K~j_zPJA+J2JjqYGGcF^S%k? z-yR3q54*Oc_3t>VG1Qcem~Yq0b(1Gs8WqPh#QyTE)GbI+cPDhjv$gez6?U6&PQ#Q< ze9|Vw?{WMLW!|KoQiuy%cOO<^CN=a0$w_EnpBOLU;pSulC=02CTVd5zwHdw zbn(L`{$4-?_aJXq?!w)car{>1P)QN_Uc?FaXloJXpNg{0Nj%y zb&T=^;2RmVvkVyENE`fQ?|#(JA~mI^WhkX|!SB9(~MtGl4q-h%<~w z!^ghlI3V-2z0BU{gpM!T#czMJF27BVJ+ZGU*>(#4Sr2~HeW;tH0rN(mdVd1{upI-m z=Nb=IA(^eQRn?Ha zH@x1K+ky2ex1~bcM{21^i{tO?mI}E8ab_$V2A;icU3@Hk>Hmhm%`FwV9b6NTS6V7^ zx&WsHeP|H)kM~-Y+Fr~Sd&qCTnQ*`2wC_(VIiIn&x6<-;Zr%C+%q)RNv?sZ`-XnUA>1F1-yp=f zGWcIKy4H@=_EoS>82h~VK8SS|HsgUHzK5UobgFHQz0EwXtJpKXntlM@e~J7oqy0_hcNFIebv^JZ?pEP>AW^47-ZqEvdO`HdI`C`Dgu!#jEJ5eE9(3#yatpQ# z=rgZFuI{0ZCfeiOH*8PbIrd5>?`*;EF}=eww#Rl;+hbdVv$8TK_#1n3Ye+lVfSwGq zB*uc@-9x`Sy}v8ILiiqV_KkSgXsXW)0M@!PPlxy8NjYaceRiWgM9O`_D%2XuilM4WZ??}U1k{bMvS=+=cXBd{H}c8 z;U7*35f}K4fK%Mp^E@T)d2@N5l+0*bxiH@JRvE9?=WZdb~5qXU%7%O zF<)^$D_478b4t`hyX?nyd-DCx%53{r}y)q8?$;H}NK>lR8c(31Y zw=#0ioy5ae<#k^$7#AQ$+)(0I?^rXszx?+}MeSxXwC zeaKUBhXnc=gZ>?u&)WKc_Zaj1FIXp%`{s~$YHS#ppNNq0@vV{I;#{cMjg&+^cus@yd^=uD#IM6y6V+Yj%52=@=!_%3N9eNsEdqnLy&guE=FUr+Q&9Ygq$o*zd& z#AsK%OFXw#Yc~`S@6n!CcTX+(b$OXu&-YwsGu&dG)#M7sCt&S+4*Qd0E@^8c*U8V} zr6ATv10MZboDVYj**^U5To?SSdwtgvKcHDe)YX57p)>LL-HcZN52mxeK`;6?upbs6 z-M9|5me_kZH*p3;5^IyLk8nP64KDeU&BC)6@QufEK*`m)E zvP%MEm1^+%O}0JRH}YK|PuWFThd!<;Stf19*_SyybB4da!!?$8M_%ZsfN@Ty1po3% zdtIgrV@~<~P{1;ju%SF(BKdJJMUH)!TZFPQy%6^ z{tM4h^wnj+U-zkV6yCQ63tq!-u#5d;gD=60JPT0hai-JxcaIVBdIn`hB`TZA8ZQjrNYX4qwi!P zesg@UWEp=B``I;0^yE3*1r*Dk1D{!$sK1#pj3duNHkFCk#xP?Webe;V#wDbszFzvd zE^f!|SxWX;d`x-yt`^3;gLeVg?WRm#I&vGsoW+7p*r;~9f@L^>q%l$wUh zU8K#{$e0`Q{SVFO`FyRfeL`QiRcEhaJNRujm9j)H8%|hJwwYz%)9JRKIJTgComPH6 zE)y|J;+zTY6VBDkwq}&Vukyp}`}r;Q9M;?BYkPZvpG!iX;oWlZghQ)e!~3L#4gnhj zzDd=Bwep*u+^@IbH|(O|(f6!n_+9NXrH%F^Qa)|ny}0ixX2$%)*~xOAopem0f4ViA zb_w|B3Q?E!@xTUM!0}kOm^Bzbd1gEB0Y<$Egpt4XhVg0pcj1)%yF0bmN!!oP@85ye zh}nJdIQQ>>mua?J_3sv^e76MqO+R+z8gZWNL)6#46?(-R)vi&In$bch5PZ(EPq6<5 zT?Da=r}z!&2HeG(6QkOpMyuGjS}t&w*f)l_)Z{1PCBf^sS9lig6`qfKg@1y3g_pxO z^p)-$Lfq|vnugdd$6O=MWDJJwy$t|T8*9Tg(mSCSq`))FS2N&N7Uc;Vc6~3L8@6TF* z{Pyne*x(l$`M!{oSbrdY%I$yC=t7&{{@;#^FlTSFJ}iG9`~5kvxrR2Yt{md#p_^4_ z(*)!0xtD4oJ4L*lh&Mqj3*{Bh!O?HY`F`>}Jw|R2(iFXz`V{oS=Ly5iEx5DI@@ohE zLHeC-`&`}gNbynVWqS;KiFLln65$27f z8qgP|s_wTA;PV$NUcebXl+*F`H|Z>*-?hKJi#!!E_ZutxZqcdkoKv8KNvJnr#S5?{ zD7r`!Jdy71HfbrDe(3z5571_MM#>D1L5c@7L0W+9FU0tdfV{JW|d3iqTV40Lu>>r2AywjcS7;sCUP?v)L?bAk@l+(utaz?13eOsxK zCVLa`snB^*<&8zy57*-xHFWGXar|MY(kkF9fH`djF1U9-4Rl#YKE(M%-i0%ee&Qsz zSgWMo0sD+O?hWTA!XkK?qHS#G+@HpFBl(blS7Tm6wl^C-=aF_K)EgqcfGacVo^TYd`w6#EP zF!loko{qgOgik-;kU+T9SX&#R2l@@zL2Elu*H4%ZJ|R9nGCm!9alf^e?{eHDbk6+~ zDUXA!+)I!8vRp_}ptR zI0rDTUW3ei1M3mW@wwt(-Mgw^Wo!%;Lli!?jf5d(2xEDbJdmEz_jnk7x6KJ%6z#!J zdt`UTGj5fNzQR24mr*{ckWU2O`+DZb)JklhT+C%N)@}{`q%JBx%lf{|pRKUXG9UI> z-w5O8R`A^UDUjI>+_`)D(RK*@<*@KnoL{6*0pnL<9i`t%bA>;7CuH-nu#HrM-$Ryu zSM~}aSB6+mna{nM=MI4b5Bsp@%h6XESDx~MrJwm}%yFC}fFB%S(fg?MlUrNE^CjR< zrk@AzS2OzP{g3--dgIPMLa$N!-mMDL9sTsFekS?ggJtKLWcXQl^uC_y>TA;XrLTMK z)v&$j{ZINHV55JLidEBA9`+-_zKF~jg_rUS$p@T-<2ACJ;H=yP9HjzRgNQTW%7?j7 zQn)_J;(C?)q=N=Qh8_#trQLzMeg^KC|KF2M3)kZuy3}>gG}g6fnKK?dnz3SDVqXHD z(ev4dxNjrYx8t7YnFd{w@6xt7c91ybwP165&FPu$q(u)54uU@axaa}TTs)!sYmYC=Oq#{Hg|+5!KCz}L`fCe9 zfih>Kd=AGu2L7t3GYaiZkTxFeaXsuYn`cU4- z^3va;z)-?bScH2e(H`kS)B5hGEZA$FbA9#txH`mO?)ztdh-YuscXoE>^Iy}buBy;Fvc0;L7$SFWb=9+bFzCJ2e>t>V;Rg1(OW8(DsjS!)&zj&Ntu9<+DX_K)T@X_YvG8gI@$^Owub z8~&o?o3y{UJJ0-Oprg8A;B&{|^J>FBNqQ~T;o7WgHUHOEwQ&_-Z)!$3I4{&-_UyWQragf?$X6iY;~kXp3CN#tZX0A z5jpndA*VgPQpKY4K4c<}Wo}kDopzy1M`D_c0r6nRn$UW5aM~x*b}8ZDShGCJePWai zI7gi}#!;0yQm&Ud$TEMg^ZJH7fjw5|KVS_P`}k4!f`TLNwctIIy(4X@1&6Wkdf2_T z;63-=f#eMrXod6wy}e%Por%0z%lWu&nw1b>W#TLlzk

^^w{tj-Gh8*KGd-X=j!zK?~(Q~Z_Cm5q6j+oVNYu> z=?-i3bXi}atB{Tg_G_M+e}Zk}DCqkL==*T|nYj0GX6I0RWx+wjM0PrZ1qZZL>rbqG zq|ce%L!g)1f@;|72Vu+|;`6YV{Vceo=)IwHcR9{7oPl51am1NBV{2a)WV|Qt#n7I{ zbqTV{f=IgyeE! z4W8DNWpOp>#ZgGZ`$qW|pR!4w15%3RnVj;bJaON6j4%1U-RP^tKr>gY5Bk&B={~1z zTV?%&EGz!3g3l4%D|26$v#JT?MKY!-HV^$LIR5pB2}_!~aijMtYlYQO(dvE%`$|&_ zf9lDBk5w~lTt1#dWLa;f%?rDqojR03v!LJDdaSR=_XD#i z2>(Eym+@}U=`_4M^*q^l7urXjp?LS`-}~X+tKRSFz`IX>zl!&K{rxoF{rdYsycg;3 zxVNB3P=8;7_dWXiOL!0I@00LeslR98y-t50jQ2+UJr?_&ntC5}6YqBY{Sw}t`uhpI zr|R#!@a_?Qofwl(86P_H)R%$!RE#ymbdxl!3G4&+ zeP|m*Oux18|D3?O!sQLV-m!HMbb_vSM)nNWEK++Szxll-5AEoBV(E)$XMh3E`17MP z0Q(+kZ?0zkO_L=LXAJV#Zo{oo?x9Owlvgrg*k5kN!sXm_fJx{jvwefQBqE?6M9h$_rQ3j zAPox#(w@}o%O}jRolZe{6*rYIcf704X%a5POl4WbOQj#{<5-WzELyZ^BmL22EJj8J z6$}*%q%M@H@DKKZHpR}tdc~Fq+^c-M)*|+E57aYkjem&PJ#@LC2=rfks_cV53m$=p+92INFMqa)6anUZj~ITPciEmSB*Txi`4&K67QM-zQ#x!wTwfMc!}r2UAJa0pk;3hI%G@TrcglXegHtdu@*tq-@mr~Hm~ z+rU?2(RL5XEA{W_ZL~Z7Jm`?HU(CvYJ)U=sOaboDU&Kw&aRq-7-bY!7xuxLl*uFMg z{zBG7me0MIH72)OXd3%oNBbFN2`lLUKHINK`^c-t`t1pjyABDyBkg7FZx6!v!}@pe zwZ`wGs?5%-pPsZO+$gpvTN+{!bB(fGL<|({`!Oal{A7eD@O&`nQa_h*_-4~1mPj7d zsOa>_6~Y8Nl#}hTE|(()YaZ^U2p}#C(q@LThH>6uek*%x$F_@t-`cEUh5n>URZh3J z-EUDmuj7dDp$dPJ?2)QDWKn#j<8X`M0qr4Oo$oPl((yjd?JJoW_O}Gguk4hC?l$I! z-OI0Fet(zYdj_~GyXGML(cnKRd0Mv(#&^YY5=@%3hq2SsCNNGz{`00($Z8^g&zfhF zbqzLfC)X$9!HCy@-j&Dz=D4E*1g z=cD?oayvJGcAsIn;*irrpDLDRI?6HLj^bOLJF|hqXPAy}jOSW{b@H#yU%(!ATFFN0 zTq5M}!>mtWO}FGr-54{}jd&sIzAc=|lz6>_0WtgUezR`$eE1V7I28O|j+26SN80O~ zu&af<)ISIh=I{h>x&i!^8 z*E7p*vd^qvU>;!!l3v)h&`J-bJ0O-N;xcR>WZM+gpLqqgR@{?}vbn#={94?k|29+i z+xhRn_kP$uq{Fx8o8f!w((UjeuJzvtA8q~g0R|4S7mFulG{{Mm(<(fOC2}`y!_?c* zSL6jPT)9(@Vt@Y%&nFB#A02G)$*;w8Pv9B!rtn-k;r4#)8_Kfq9qA+t*fR=X4nB$) zRRNsirSAyO|K@-`%Bgcj(#8M(ejR@Kj(*)fKDYI2>*m}0wTJfL|3beI;}2tyz74V} z#sR!T?(3+@hMtM>BHhaP={6VITE5ZF(t&d|lnFr7;Gsq2p)x+17STg3Tdm#ca}SSXbE)Dy|?M2if_#LW_nS6msnF$(|_ATqYaz$if2M~ zJGK}7l=B9W`-h@m^ISFMa{UZ8^auLTPR`B2{%hO2S&%D~jKni1)Ej^koFNRbE#{N? zdu#hEMn3tDJWS&^rbFhwx8c_IVU{MvFR>o=Rr9{&lJExL40UJAJI_F|Ebql`+a>M8 zX53M@RPFzQgBkInN||5HrHm`^XZoA(Nl*XnNEute>I?Uw?VX5tF1MZ4?aFiOtsO=` z*>>31yfrw1dj}kQ^y4w!AqU@ckr^OEss8XTxVE=9vpmjlbzdBGrZPYK6YUmXa_JA| z-Zb_}`ljPJ0+07WFDM}%y)$8dVtbH}^}K2=Z+-6!=x{RXkhnIuOH6bJ*IhWvc4{PL zG@i42hI!Q3mChk=tMK1Dg!N(GGiKQKahFH-MqG{5s`h~n#+icu@b)$S!FHr?!G92Z zLZdD4xp~^7<7!9t!}AF|2jQ86=L2|p$JLISb`tKz<7-Ema%)GGQbHOmLAZ0gkP z5lo)TgR!;hN79HXQa=7RpwWQr4oG+x&+NU z4Ow#%#(=Wxvy#8_FweIH(e@tcAN0dmO^@Sm&~NAdELV4TC;A;4S2gnLsr{k3%nSJE zfc9Eyupa@x^!p&6=fIaS2LIN@70EwkEH~5h38AyaeKq7mth(f9>BDoOG_Ltn)h~Rm zXm=;-h=rUIb*jvRJF*6L;I5P7*yl;WH~QCht}7g&xq3>z-lu9kcI-J>_&t5O7UliE zZezr`S;&e?4s1ODXJhCkiSEm>Q!%!Hb8$t|MlbxkKdnf-d|{id8GE}uFz4v2E%JF> zVSIwvI2u}W|(yfUXg|h>^#{u`+_0Q%y;C-0y-=7|64ZOtal`=CY@uy@Ojwgvhm!&F#!F?qRw%P5mWKg z&cq7N2l%cwufF%6Cje*KId|f#_?C7NvFa2J+Jy#L7wOBDI9m3=T@I_ZGLX)58;Y)8 zH0Y{);@8vFp`<(LO7t(GCDIr8lETgYCEr9(D*fG3;*PY0K1pAVb<~vrJ^i1$@?zQ;J*dN4)VxhP2Tv)wkeq_OCPAlUmF?J04vCcYk zmLK2Ez)vpd<~zOm#79-W4Looh*beM0y}Co+6W{a$3|(@=@n{vqH^ z-N6C)a{+rU;D#J_9CG_#>u|puz53V-&iLGQ?lZ?`RZM4{fNMJRtU=U)!+3$7%GilA z|EX(z8)psJU(9FV}5T?qe+pwkW=x>Dx*;Aok%1&HC} z{U@8Rm(P9~=RV@&uE5u{Fb8z1eXn30_I%f)e|1hvK>+>xGQJXVVxfP3vp%|_(D#$^ zU%K}$Tspmw<%>ez9;oZ76-%9u=YHuv4*T$3z5MomE6!DnWBQ77&H=C|r#`-3OI>lG z!ohFg?YZFBm*U_zX7nH9|BTXgZP?Q^S!(*n)d4@ng~-l57vO9%Xh7$;4OYEIiKr-jd z>HT(tH$bi}v%ufI&_71mOexpXZb~~~>z3t|Y4bwr42y3z_%FtbeZ+hdTVaeZK7{#- z{iX6!d`|^FDy43Y>uq(FTw~0nywh1XnV$DaRtM{nI`z7q`=NGk%q-ORFH&Fk=km2w z>{rTO{aD;>wsNg=pR3)C7#gEj*A!wdLY9DCss!s&65{R;`|q8d$U44eS=J@A)39b? zmc$)6=E*82j48%GMeDYY(x*Hqu`WRSl!fMF9O?Tabiy^8asy+Zl5ZmZjuZBEjdB2J zW!%^9uNd+H_h4YZ$qksb;!dYC4gRX>#hQKP4fnYk#Bz06l&*oe9lgO*y_hjaSzB?xU1IRY4=Y!F` z^)lTdKliBcr|eyOL=*E^+SD_5Ba^6yT@LBD1b60 zyl3Ese%Cv`ZRHs}!htwE(~({ZKgg-{c?`oL@E(aJEULc2+(XZn_7@IL#j_9en^e?= zGg;csmF@aX%ZQwfWt zwY)z8^FZKm^;3NY-G3MG$i5uLK=~1L1Rp>h;zyv`jPgO24~?sRXi^2%sj(kD1i#5R zf6Y2;-MEtvO_Mw+QI(L32VAJN3NONYBKkIiIw{^Qc+Z2Kr!W5R8h`nr8Te+cYDcWR z0>r(W7ax<;4xMk^z~j_s;74~4WS$-uP7d6HG~VH(p^d!(_f{Z&#(c(Y$6Q;8XKqy|Vi6!M5BY$HH<#QC z9nPWMyZSzyMRREP;6Dd?GGY^HX2fhx#=Dd95abav>M{XuXu~9Nsc~13*;CtXDf8gD z49{ddS73eEz&d39cjtOB=q1p6FFdE=3IDS}6Yz}36Z)#w7tf)1-i7BNJQMJY#S=Fc zDZat^oc`P1LC$H)7Nk9{G2j`ZPm?bohJcEz2Y%7e^4>JzzrZQ-F9_3X=l-Gm4j$|! zzPrvVJT&H;WTEumJ~RWkF!(?T#xsh%0&xk}VxHoR=R;z5P&{GpCA2pO{wJa@fkx*b z*4NO8Ii>T1gYchur~tfMcs=Tub%S+c+(o}+eXPHsb8niebM$7y%(|EcyR~2Xb>X4o zIE%sioidq@xi?l|X*raJ_bH&6EcAIQ_Z8IK>s}=1JJg+K*Yvwujs5pxrT;tULbg%$ zbiy7EI%eI#TMp;8^ap-I;BTHT+#b3D7(})s47yxor#uAR$&iP#0gKpdFb{OO==u?AM}#R7k+@4wQacPsh4@mV}FnMK!8VL0}+lE+{>wthwxs3 zZU0OBw#NUxMxg=jccN{fgSL0W<42uIeZ&5%QAZ+T^Zrs(`|>DBv5Ec`QD1CCedD|G?zx1rh%2YlmUjYYj171?4ySRP1x|d! z`JG47KhBV`7kE1kas8hR+Pz@pQT^CmigQ7|@Llvv(3Z@nFkQAoZs^bP_IZqQsQUuz zQuQ9>z8QE)$}*5NAo~Vb#aUjDa;t)^As|H-aCLUcZYmdIt2O&&S;pjd{H4W9Nq`_c+@W80TvF z()K3pVZ?5ok^2VLHk_B1bl|{)bmkk2bfyWOI(M0H&R^_%#4YLhHU&6JM!g` zn~mQm%YKnCzIr750!GSu2EWLw#9cntQqoh+O5MKvD(uU>U|(Jd`*JVsHNiD-IChQZ zB5io`)tEH2A53F;p;g9V3)8{7VSXXwpAk(NV@$pv zE@VF(XOqhm7+xNMscf!wt_HtS0F~_-&HHB+ZUXJ##*KNzs z8;7%Bb%;FzU%|Yonv3(T*ypssR^o>}VCw9t+6ZZHEbc%2HvBWQ<14YZb*aE3dUFZn zg|FT7gk9xySZ?XB%{A^4+uSC zUS9c3+;Lwk)}I0Jg-zbwG5rfZ=@$!s+9Ztm80dO4;L}3it6JQLItKFBFtj-XPx0MJ zqn{7_pgzsB!cCH4_{dRG|h2W=F&%!6Q_go{eG%=M)|;^A9qE_jh{Cn>Or!V(#HQ;Plr!G4?!% z+?n9OJ=}>kOXO@$HvEiG$7!xtxBH;7cCr?uI;uad@0`q{b*~xf-L) zYhODDD2o|;0{VVH-ZR>>dEeEhG_+KFMEgp?2U#YL>I1qpV@j`(K$^o0Y`6p@Uh7W3&;Uei5=G z%bpr-lDW`Q(cf4L_|(76kvXH_N=BI(Qs$Rg6-=jW)X;KlNBXZur?CF8tkhCriYO;> z3sH7~s`J=m5;vRqVGGi~DV}>dYb?gSCv-9oY%pVB6KGqr0)9YjA94fd2i6$c9D?L! zprdEWKgq`h9`pxwPU3psaI1oG>%`}Wd>{Q*GEL{UT{3rtjx&${_tw_TE8x3!_7yZf ziF;zwpU%Tc91GY%nguUNmvQA;K8&U6^ZOSObFPm*u8%XHj4kqV{9p^T7o1dM%KQZ~ zmWQs)tjtP6y*bQ3_VVZ?_#)zdICcM=%Mpcrw9!|IBc$~~TE9r!3-O2VcY8CU{M4V7 z{bh82+F#O(H8XL^z5rGg`>o{&eIU?}q%OJgWY!ga>Ae>Pc zgniN;TetIE#hRcKHW1izn1=HP^*y)5JojVmiRTKmoag3iN`6E=ptU`OSLj)%Ez%Y4 zK09s5khjywOk^6`u6P%Qn`_@L6N7@#A2F9)LK@Sl_`<#rbfN z8;A>(AB+2KDoOL+w=`eQb|r&i9Q>YAZEhE@cyAbK8~IEkbE9mEU$bC;a|B?S3|~1p zN8)=PWgxFsHsf9dj2+Tt&BK0+&o@*1NyDv!2WQPf8z@g4dHz-V3GfiYJ%DqG2k%_{ zOxmr(C&D;DA0Zq6#PfYndDv8s|uYgDFI}@+cU+4hDslk`J z@OtQZ1FyutfiEco{OLF=C;o_gBc0#*4R;fS<=n5w=f0Q!yZB81tyQgmSl?^@0rc4) zdtJKi+BHC(KclUIavtpox1Fiw{ST362)O@koFAags{QT320a9pB$FqPO+T4-jo(}e z7JHa3TK1NZE{OxsxYAh+nrYc9_s;;=wC!0rF0lLc;M~A_6yzYevmEbo@7<%V8e{Av z`*2n&O+K&0`tgi`xHcXb(=9HCxNaTH{0APL|<)H5VVi zULxs$dNlWp=p!%pZM?IYYmn3(ZT{|48PRn}Q$9rM{#f2;co6$xlvPEBlJcmltBxIF z$1v*1y@-4&op;5OA5DWk*ulBOJj4lX>97eWcGUbC`ZZgcbW_+m&2_ZZa=+92Pf@Ti#dUHWREy8-u3*gW^lnAzT~lKmzOUN>~1b`@*G z`;Qh+Y@PiC&O**|ZFOH`I`>U6|FS=ygMCaro-_Cn<9S{49<>!g_xONyLFPN?%5y9J zMIH0*ZqyeNK~MVn7R<)G;$zVJ*caE`xU-phpyAuqiF+yZJj4g=hYPsR0hmsuS3yn+ zn4(;txv#nG<`~}P^!c?d!k-&j%3d+Xin!rBzD=Z05XuUC!e0oV$vmDshQHB|$6z-e z=8AQjl0w+?!Wkr_dq1D^w#Yp6+miAo=r<@Ub-?F_<;kls7ZIOV%6Cip5O&L+=BHHo zcYa4ax_YAzrS5Cg*FN9-?)O=j0f#FZX;#=P0MF-h4iw3Gg1^tcZ4!7+&dQ?uzUOJz zo>;T+K5yNgsXk&c5)k?0Gx?2xDfEXAS$qSu? zY3JPz@gY^|M=l!ax0gG31h#?7S+$DK-jT-miwQ^@!Ft92%Yu9OtXNB6;CJ=H6CvN= zXW%i-N(kj#)<&FPXcyl*+#OS&hJ7%Bl|D-ChyFx8PW3%yJNllD--cfR?ONddTV~+< zlv&#qFLCv^X!(#!X-k&$_V-1%%r8?iB=SBX^((t5+IYV=WeKGF1rPX6J3|)?N6+Z# zMbZAJe*Clbng&1X-ow|sJFC~@-nu$-f3t1l6QF|dlk&d9@YbSLc=)+d#3l2H%S6#QC3M0&q)I?Cr90p6wD(KNv86C9F<@Y}7e zSuBtCUwYhDKfB zd(V?r@okd8int{3*}8%MFMI8eZ`{3L`lVh6;v4ymzaQIVzAAdA1AAooNP~kG-zORnj=N(%z1nMmokZm1**u zX|1~-kotgQ+fU8UgwxoDD$Dz=g&#dutZjd6qZT4pVdfb+DcgD&`(Fyz;2D1DPjB>{^ND4fk3LV{2|u1;Vn?opT?jDo z+f#|^{N3zNvR-6f*tOQe?szfp!>k>7zshu?}*2JeeP?5hv`;l1RiEdUECj(er^lj zp9ego)~vk~58ICb?r*cL;Tf27x8dPXHt?YKbvS;Me+{{``S3L0j9gk?ULrn7$LN2%V zc+($Zw2^`Mnhfsy;Gt;aq>oFk!^HB0?GC+xf}y}gw9R6I1%oCDoqR!yPMi;SIG zH{VKv{PEzUJE?P7k}V&bm*V_HF>!0aTY2(wPg(=jr&qAoU}S%)3>-< zO+Htvf{C%?q|UZ2Jg=dh_a{cxEP_7TZW5agW7M!-S3C0C%uk5S2-{Oqu!jjNfGJsv z(JcQWv6Ke*U&8(+$L}TTQKoo*e$t2(zu9D&C+QC|EhPuC4Drxh7Mngr#YqEyd(sTw zxaeB?#vv9Cd<+OD&beO0{}svKRuU_90~Uc1va5ey@&8Txo+C128Q4f4ZR@bbw)ltV{eauJryV`v_ zgMGnT(@(XVc$K)sT-9akW1C2ifM*)h9|b>Lx%}RT@q84tRYH6%TCq4DJkmqDCyxv{ ze%~nV82e3a6{63uJnGJdEVM^xn*9OqR5TrY<-%KPj~Tp5^sy9=e$KaT$2C{IzMc)x zF-bq2wHLCw@N_jU?;n@Bd)n%N-s3@; zD3mFeev_Y}zpCx++!xpQUHoW9Mk#!?Xv_5U-t3e3ny@{)=BI3rx+kQ=hRZ!F*f3~A zmu*JJt?-o7iskGC%nw^TKAlL)MNne zZp1^Tq)f-LlD(uL<7Z4xD-QG2@|snEwM^EHFSyU6_-oJZLWg&+d63%gUs#YKGMy$ zZW*+TC51e4mIS)jUhel%529}mbRyYDg8V7I_F`}2KA-q}LGL{Sz1P@7q92dG&l;DC zc$oF53;bwv?BKH#addrR56xNjiN7eO&R#ijp4E>!z3AH+_F?$o9vbKx@_V_Qe^m1{ zMRbPYSYL7PY3iC~?$VkfwGRz{UWuP;?P2W^^G7*ra3a=jU%KjY$_1_!o&(xbx!}Xs ze{9dQu8ZpPSYIs%cY#%1UvFQGm~ef1<;RBzPjTqp)S8LlU*Px5^BQ6&u+D0nQ6^kx z69zPcFOCq{TDSHWS_QtrUq$agnop$#!*L5LFQ_|qgs^7jX6iupv#!Z7mgpZmVN)^0F@YrH&j`$Fb8!zKA-W z(9<>;Y1Ij^74(NLY{>1chx;3JcHx4gJ&Uy}$n@&OxRx8meKMlkt@i3Zu#LbDz`Ir? zzZ%c*d0gNu@_Y91kv*K8&v@F6{FYEuf6zcI^v-|d9n8W9^!m=banGgHiFkFwUlZl7 z+f|`?Kjc^ff1bs^a(-C77JH-MJ)fSmJOUpa8AsUq4BrRH6Ng^mS)(_dIG>63rN0+( zCkJH=*57(4!|3nCM9{5+_CWY1ATB7*!eLGgn3T56z1Oo}^PVjj*z(%<+-Z;#zm2&z z7Apm`e=XR{3ndJ*`0VV#NT3jf-Q@7E6lwQ8d?#@ zTfbKcPqnVJ?b*b-v5uQowtp+6crJJ;zIkybO!3=;$3Y_+eDC+BS7q&G9vOSX9*Z#} zzm~kMyD}>C1~xDDIfpPG{Pb?oW;3=rYwyajDYMcMcT@7++84&V-f#LZA`TbrViuqJ zW`QlpsV()TjPRyLIr;6Yf9p1dXKYgHdaZ4$LyYAB;%gv&1kPaj5hE%@zUOsY!8z0& zE=rW5s@pIY^abI6#%Ygqu+8mU<8U7UPKYlBm zHJ7|hSy15$@FrP2F;4qQ@Re(ypV^{;ms`-za-5&gXiskZ$etH{UsMmv3)YiMoZmi7 z+zJj_N+xm~!u7aqm4rLJ6EVL6!A95&WgK9O4XONF#xOtRFYGU-ZkKVm4EYcBA(n;{ zcdH*~KCFT8%PonPH63GsJ@36;Z3ZtQuZLyBG|*DAmg)Q!@DKeE{d`O_^-}9j=e}~T zFH#=$Kpy5E6Xwm17xOTtkejku?yG`s85|eLK|`B5%}tM9h8?Wkne4f&J%u|o@_2^^ z{3mJ)mTJBj?*G}$6KaZahiaSycS_sypx7c#eaI@P_c7^*VfSo(YZ&;D;CJ;q$=pAXIHj(H zR1^BpwFln%?l4yjzM&uRuVB2@oZIUuEivjr9pfa8d%NLwc1c{I&02Du)B%|8D4)dg zVoP8f36MRB@2>J;I0Y_)K3ab+^@2C{X1xad;JMN^>lC_ZeP{2r$6?3#Gx~>o(JRc> zXiwB#Z^F+$4RXik5M*Z5Y5n!GpzAKsl*RlsbqB~~lPq_^&I5ab)N^akHPTGLfA1dF zvsrT{usm^2eDi(Hs;Q@IRzrrVw4VfT97LM;)M|2$aLvNZn~=$Tpoyz^=5t+#9UP76aR@&O|FQtTE1`nc;Zye$2Kv?oy5aB{|L$_k2ka&84O$*x@MQJZA;m~i0>0N`;zO~ zCSgu{MT-(#wj9`uwx&Su9Z(f;JcqvWz9&b`5?qz#HV24b<^MC#VY_PXPJ1`cTO@n5 zsG>TY?~ZqB#dXOWy`N~l&;O8Ig!z$E9!wd4eXWT%a8}WB+3J9f)Ehd|6v&^DXAiGt z`?OCAt?1vaQ&Q?+_c8+m5lWr-WC^!Mq|-*Ha8&S^nAQ!zGE zk7X`S9sUgb^>F@tc)GjO`i%8%s|WXuyl(FO`5*4J5I*F6nsq}bn&iER-|_Hy>`mJu z=$x?KdS?veKl*Rv6E>`w3v&NH_I6npK?Bbc*9z|k%Q21#Y^Q3;d3iBv0#>k64Cfb5SRF zP5YXKBflUmVlH~2d*jTTdv9Rf$P=9!OjqCb$~ineudEaL_ti#TkwHe{UO}!Ekb@sA?$8nVLZwZ0{uS zhaoe4fITY<>)>7|&XCT74r>2=&en*rd8gbp0Qw?u6!51n0X~dae?8)iIp2nT8pK6o z9>8S>FP%qtPF#?E#J*Xl1^n z^E+eNca~%scye7-^HYASwC>^9+!rBZu?*!qNeiAvj1Q3yz>6k8XUPW~Pf8xP*RpdO z=9ti60Di8BlP+tZx7`+E9q4N<;flQfk#JRQgwGG#G3>!DrMrxJ^!Angx$V&Mi6fOg zhx<#CX4t0N4o_qm&pcVj5tBseN|Gni*P`vP$*5b|FcK$-jd&hpy+Y?B1{{K4?o+qC zwE?haV{YC;EFM{R^m1uJH`Ea&-}dLJkHJO@`=(pY*m`+iKoIZfTfL*tX0933i-ZO< zZH*DOlJ5aGa+V6Rg_mc(IsZuOxN~w(L&uJb0vB+l=)W90K8zdPbvB&!vOdJxQhLV- zbuOIsuq^5cVNbmtb&Ua?B@^$euCfJE*Hq1qF?(uUG2~T;nfsYxyEyI3RmB4gU$XFd zblfLa3z^7*dlunqHn5##(u+;EDAT}Z2nk==h3eAygd-_QyHT3Jpyy~l&Oyai>f0|sB$wj_gt3+|QlU~Ll_L(#$EInu8s*s~3e2)a(#wN*cMrW$vpt31cR$Fu>Y9e$^c^2^IH zh~uM$q<-2CTT6M*!xUSgIdNs7`?T;K$iJ08(eIjT-7>7n9iw60#e5ho@e85*;@%+i z5%fDg(9_TO&d|GLjYMqeo^i|Ufs~@y!?3T%*xHBCe=E76eUC}tjhF+lvCZc<(BC=E z4FjL7*T5tCBxfK9FXFFv82!=vCw}_GTjbn|tViXmm{2v+(HxnF^u&II?<7MU0iHd< zUhFuI8_%AitsxvgwoQ1%#|GmyK49@QH1F73^Lw4QVNXc$h;U5ApzBpNkEQ+Qcja6v z#%~t!D|tu6h~Me$^lPy+bd=1voqi9L2;Kr(QTm^7#u|5p(AIquW5T#;xD#0`J{2sI z@nkHr))LX*s0V?kdSpBSzpM!=eGb+EGh%AW`o^`x%zd6jxtH&Gwgq}%ERnzIUNVKp zZ?q#Vz%K+YX9dnd1UJWu6`AIRrN5GoX(qwnMv&*r#9* zhW*`4`?4#}6rz0@x}8%$qwszGu9UZEXNEkmigsqm2Xa?R(!FtQJg<-xg(rMF8Q+BG zbb!;5v@Ihx@m`FzI=@5PGkiT~her3u9&AFBU+&j3_vhi+cWhzV=xqdfSYB@EmbQo+G$zDi=SiiBqGBhP#!G1?LEw3xDTN9lO!= z=!$=YUS-E!bGXCM)@(+MmwTRvU)7^keFolI>T~8)yKODDKbqIkKj2iKkR9JUU-ON= zpZ!HVm)zg@67R`dXL@u|!=fDMDO1jS68kUuV&SvAgK^OxQyQUbKE3EhiXZSqogLbgJnB8-t=GDEPt6T$9nRp7gs!qAZynB>-EePM_=C+A#8GuYTpjL_IWQdYXL_x3 zZm2$pdt?q+`~5j6_sHO$0n;i?(?0F&yj6zhR6hTff8=dOS^J{TlY_w9_C+^FzD{_c z+nr{+h_&XYeS&eP744J_A}ztDlEpb_>uobqmXXFb zkOynRJqJ^ggbzg79Oi^V&G{j|JK2w=&bgm4AI5J0#x6n5b9`yet?2~L*K5N$H-LNB z@K?2{sxJ`>2jjrl2An%c9|4(GbfI3a#EZP#&~&IAd0G+cZ7A^oSsD7gQ#*B`dOF*~ z_wK4a@uN<#t~2*}ZSKgyOnc4wKYuMSBgO*iXeVAV*Jj~N#|iv~Y&Bpr=-zwkV8~v5 z#Kzy{y_VT?PfZFrd-?6T=}n+h#L@m~ogZ|nJ$NcDhLX6t+6|2&|= zIdXzpD+grtT5Qk7?|FQUN!!?47~v_?MoRo3&^}`bEovXWI?Rik-g}<3f&4Dw7kP&Zjo=)Ll&#Rq;!O9V zfcvJakBN4!=~Z72Y+kVrF(>+%ZmqoG|M+}eVgh0fHhK=Ix#rn#8UVQ<3OLO595f~3 z`#qd*+_UDqvKC`5;l9u6dDc4Le)yD^X)eTeY%cVtO=ADaR~cU$b(j&W!Z91a>mB`i zf~L}34#1$V>sTi=%40@sTRD#z=|lUs>}BZk8{m1G?I8w}Bd*il=j^MF<~4Qh^ZdSI zsVAWtwn@te(1ClHCm_2A?=3na&Ok#3mOTg? z(dftCuy};WB}Lm|+lP#<>|~VZPbD57LB9~*t--aRVrr83I_YbH8F3^I(pD*Bg!pJU z_t<+ZY`E1w!F^p*k}9n;&Be=3&)5Svdqi(VKk5Ld#0<;2^7pZeVK0q3WpkO>h6o4r zlUcx#(tA6Ve>J{!$t3iNWSP!)E*Y?6-p}WnDR>wWlTRGpAtN1VXx~j=k_Z`p%Vae- z?wJ%5ibssgNW6K5SbuouU1hSiysaW=8<-oroM?j02-gf@5@gAkKLq|l@tF*fKQBPBJJg*J>c&I`=R6|$hW#I?M<;c zp(C$(?6St7V8XMZ*nQyd-JmO~|wA-@#G<2CdwkbXluBf6%)@hB`3;HBH z640*S=#00+zC0b@z9b)EJQ{reirH?y(@I=yQxYiu>W#U0;V#OV}g7 zUI2Ueji^4?*Qq^o*q^G5Z+Ksa|I0O<<_Rd1`>SW_jiJa9tED7NHe&1O0yPNi_fl4#5qCxXV!U9v{BXa9(6qVU(0lEf%S}Qo3!1uV=&i0=#*D-A-h739|Qf!M7!1u|7 z@^%e(u`WVBubyv%kuS_EJGQ-Kd=owayPNcnzTzWoKE2#v=0Ur%H!pOVY23Nxyg)oi z8RWqkHPx5aUDMD9vDGonn`Gk|BgLPaN|zXI8fmV6QRIy(&Xq~_VF$3hwBNe33omGk zxF-H2t?8|y3bw>^#0kFvZcjsdX}S9D*ud|Q(=?qfq)*{>%2;mzJM~S@y@u8UGk6yi z%V96$S@6?roMSx#-^k|Ib>GNSj!@3n-;6$Z>h~VXHcEH<8oL7TgnBIm-zVe&o|NDF zT=VPb!yg3x=69E4Zw@we_seY0@`1VAVxN0D^9yc9;_M3g)X@I(K?ctFo9_)xJI}cW z#08ChH*{`2q4VTRJOiZq8x=x+6X<_N!P%?@1!uC>7F1@H6#N=G*C$!W3r-`B->q z_|4i(zuAtb6VFsUJ$NGB?87r3Pd}bTcn0y@gJ%fON<8cEY%K71(`Nd++3|GZnTn?e zPj7+Wg6|f5hb~{NIV%9~Ouwy>H2ouAoYR{UC3^h}zPMCZf;oq@M*KB2l}zKDA+A;b zWBjnY-<4iG!c-H8ZmcR!XohTb5BBgAYuc?&=!3HFg7bJoP|ptl&CM7yR%j5p4f;9eh$c{sgkOIVLb-2<2=b+?osVLQy{>Tfy_ z@qKy3_je+`!>7aK*Yj@=!`M)-c; z+il(b5okgDpo>F8WRAhr^zobvCOhyldS&7e!JoI*>Y7~POiO_M!dx6mvXA@asQG&TWUe(d!T&MbZ}^Rjj1ig=oHV_^9OcEALN8N^ zdpWx6X@W;r1$1=m0i6#d*r_u|V+~!o?#D%h$<|;^oLM_+K4OQ@u&vwfpLu!I47@L= ztc>r=$5oG-k2pGeSl`O2>jj4mt?!Coo%9L&&w;|ha|uK7sg#7-wWAi`T#>95=}U13 z*tcx-vq>fVuveL5wjo}hikpBraI`mmOZ+Ofw`L*kZ=;_-@yz~mzX5kBNnbh+MMrlAt*5dtEBrcB3Vcn4J9%p@okN&Wz_gUz3JUgc9<+;mny$2^-{%GwX=SZ0! z=OrQIG!vKLd1qoO;;raIr4D~g7wm^O#RSzEUAD#g*^c<%er-DkmVaG4sNe27s`Zun zgNS>m?(YI_j`Zhz@C7B#pB=dCEBH>-EciAAIu+)-yA$sZ;{99rf5>%0GnaducFNB& zA0rmv2`!3y+}w{cXjS2I|D5!bqvl9`@Lgcr^1XGZ>^qM=F$%F6MoorH<^9RX_ixOHJpabL!prWD3C7gfYq8dOSuSP8FYCu&#(XQj@7T)rB+3{v zkI_FT{DCH54r0A>HmdUa-U^4=!!tiGobpA;S@{Wkb)%X>4@&y1QV`N?YxytTYH z3}vG9@7WRGP55p>JJVsyolBV&d>nf=*6EdH>UU-7z;;WSbxLKe`VBdGdgV#v>9htc zwbpNA?r7sT^-Qo$x4yx9y7B*prP}&NebkmAY|mD1-K_r?<8Jmm=-_o|XZrP5Aqzup zzy2!Zba_(FryOrBu36V@QB85Tp2!!t9zZ&H!;Manp&i!)D2Lz3ciH%c_W=HDYlr-P@0B6l#)vi|sl#eUkzXTpA+hQF$a1m?OSA<>U zK6iHwK9j;1QyxD_KLCvP-h=5=)L4_AD*WHFc<+k|60a$bSRT~#yp%qr$My>UKOJUW zpp~#ql{QY5XWv@z3R$~^E;C|^IImd`WV%Ml)4nen`@Ijr2kfNOLs^jYWzc!ko?SQ# zs(ehz`f`^S{o6Y-2zIKg=};y}j8z*mf2(ptBWFMX)euofl7 z?1{<5-CnbZsUIC>kP4V9;L>kQ+Y ztZ7JBZEhV_^#=Ozs^;X{CblfZLBO8mRN&kvLpb(QU&r>_!O7^KtR2{U*~q)k*Zg7Fy^_|nP4tWk|1g>Bs=qcL z`inTPslVk@#qhVERBLgJQ_8jP8e{ZJ#}R#26W94u&Gn`@JHO#Q3h%fh-MlJHqZzWl zWNK*LKSTHk>Abtri^1Q)-*^_2_cHPB73?MLc?CAw$vj6uoZvj# z=9(q8x^NqVO=?`5f?@w~#8Vsr{HyU%ckt?X+luoQrn)ZNLZ3S+cY>Z^AE1vt!8z?J zb??RdI9IF*>ok5_B<)G;^T1AK(O@U@xJ<}Xg8L3ky+gze*E)scAom9aR(@le{+;n^ zBJdtlNgQH~>e;sBY27Nk7w`*D2$yXw=eZD>e^?J_n}km41Z{NR!#unn4)fO61;frH z%I4s`ej}cN5lfJ3Za(OXZQ`!JRDr#_7L_st`rs0|;|}BXeo4x+AuWuje~PK=<+Fod zMmc)DO4*2IZF>O!qE$ZMT=Egt1OI2mlbU}{{|AvZU54jX1&8!$|vqL ze?{qxHsx>0u^ph@qsH)E?$L=H!}(3xWq*WDTej~Y&pnlRrXTT!{Y>NTC0*~f-$Xqc z_N5(hE9`w37wURJyq)%{xK(znUtH(R)8k&V!@h*QvOe0+R^C`A_6h7Y`Nsydg|vAY z_AY!|OnVphgI2H(q%XVCX-hy{%%#`YTkSZL{WQL<8vBO!vm4lJlD(z%+H2R>TQohb zh%o5ie|mjAU{Ph(3oHTH<6qHbji$0;wpPk74~tID@n^rigDrk#-luFJyt57aReHW9$hTC_M_p}7 zN-^S;LQbDqi#0k?`xc%`rcE?|iT4?22&YYBfK65#q(*V23 z(Rk5M(&G@gI*;u1Z#*H?{m^GjG8f9?qriWPu@$phQ?t(kqF9sU7GhdR^ zqOXx}ok+{j`a-6ZHHP^V?%&?PIi~7xa(;YwZ0)E#$UghVo*Fd+@6W?WXk6uI1DjXX zLB2*g)a_y2tWUx5RxD^s>cI26dLC)ND^KYO=0P8wY>V^>{TKWL_pb(UkB_Q1Q;oM- zm)Ey&UAICfoXawL9#{GW%*Xy_HSSGY4l<8|KWPZy9~a46`XwLng#Tw!fM2xVL*?Dtfjgk@D)eW_ zek%I+lSSysd|)&8fFWlq+5A1o<_Yp! z$>uHc{_o4?$LzSz*>ZyNIokx!#9I8`D_k$x-+D)n(||uaJ5$RUzYdqqzqYN--+}f3oA;>lmv;4+*peokpWTGBT%x;vP1+2kC9oW5BE06o zMleHSvyg7|b2I`=>y8q@EA155-Romt5Zh_q>&0Au5L=~KbKtJQ)T(95*A;eC2+I1J z{MDgp1}&hEPJzEGZ7^{g^C9mYMEnS}i}*OQ&fr@V)1>|8ZCCJHY*JOPCp1_6tbcRW z2Al=OtyH83_|JGOH>`oUrg$yxt9WzV&G;zjx;^02U|#v9`$Wx+xH{s1bqRi_z~`WO z+lwwM_AYeX8ENbr(uM&)7N)`H)hhVI{;QFvp?T*OBR#U-hUT}98{dJi=LGhzjtc#< zE?{Lp`l-PWFoAXlmc`Sd@AVa9Uk!Jh;Os^*_TAcX{;l0w%zu18sedoNj=eYAdgMVm z`-11MFz?J4SK(~+6uFZgW51)s`Y+a)i#XTJc5y!3#I^+YJP+PnI){0ghPCJd`kVvU z%A$+KuWh%Hmrv$=d=Z4v77E4bf!0Q=!FOqV(ge$c!n8hO593`{@3m>#h`i1z=gTnYKW?d4=_ zN?ycwkyl9bMqAXc6da}fksf4yDIV#(Yq|kvDQpMC=bA;Y0l7 z57|EF6zs9fId=>Fk8Bh?Nd5cH(N7<6iQE;gyQr&f_B*m}){8qW?0|WkuU31OawPa( zd+ut@Ii^f|WYPKs%cL)`sj4y97YjOUj`hU*C$`6GcnVFCr^XdJ14it9$kb~EF6;qF zxz_E`;3tezR>?NuZ=(f@5Z?~C$*Y76aX026@Q`;tGnegB*5`Tds&xaJe={#OVP<9K z;ZQKV1?3j!O!U6vZVJW{ zfqepe4sussZRT>9N!jW0&StKhc{y`6<*J#dGS{;F%;T9G8rt64ggSV~Mu_c5U!AJ2 zpffeMT1&?meJ!hVK8t?r_)qkChXwot{uLeQ_l|ME_1RZ(N5qX-_>pzL1zyz%U$TU; zwVBfy&xG;Ys28ny?aix%Wp*fYFYU$W+hY^*LYd`&e=qxwz8q0F_&LudPwB$J{o8>9 z+@Iw(q5mbAdnH)IV(`9|O zaWd}HOxcqx_%usoyt8iro~L6I8d?sYlY2)wK8a>8Vs6dHoSq@@eTw=ZM}n?O1kSdt zAF+PanZ`PMMcLsa)7#RkhaG+^=BdET>J{@cJ8VVPzQbcRY#`bDfp&u!ga7kpF!MZd z5gWAMWj$@Z>)6lA-jm<3F|S#cR2{ohY=-gmxMOimQPLZ+@1o9j^SZMImz$0FV5jGsJ(gA4%I9e@q6UtoM(6;3P z;BG^Of7Ax{C%;ll%|ESq(67GK4biVY^aHf!TOH9AQIHwp>SkTmynwaO(jSdVC;YGF zI~M|0-ftCLbmQJ3c>W*wwRSR%^AvuLmv|T6nx)S8*mq!aS~z+)VI|Da|0jT^PQXXE zj%hfnwUh%;{C+R9;KTx)Jhf(p^I^aRpF0P94SfTMbI4)0U|a2h^Z_`N z2KscY2JY?TA8%?IUl13>wW^z^@4Xw9JN!X@vG#%1;>cH{6%Z_z+~`nd?8p*y79| z{a`PAra9IuPQNL6hW2oR-q7Fl%l|=t+oZoMUS9%u9|5e(2^;%O7zjK1rszicT3&AS zwZfl12>lh=D$G|~%3dO@v1L5_f6qbE7SDvzzFP0NtDN@$b8VM?HSNvD`ffoT+z<01 z<`(Uevsi|08~G#;&Kou~9XNO1nfNBtjWpQMFA1*rZ5QhO&}~Y42eKXUNB_L0JriR6 z@S8(AcqDDakAu%U152D!VxJ`q^ht=F!nqu8kDUry0L{}*5c|)VgRq5A-vVt-i!J8e zw2ldBPe6~+`>gt~e^=U3b?%ffNxS;kHZ<*j8L&TVz$?u(Sy znO+ve{Un%=QhsnuKA$X$xE@LtY%y?zt1w(gZ>*baX+RnYaiOe z?|%*VmUE-%OX>alrXx9x}8oqr=8uI+O z%ioeVWcpm@Ue20m{js&L?aQdXXCwVC_d^ueAK`boAN#fV4WG)}{4Sl&kVfB2SCaPb zzw3XA`WG&EQ`WtzC9qAbzNen@NVqRw?cI!ikAa*~k9Y%1fbY)5!MyFn6XK0+6dFm2 zaRMLun)+UNpO0g9JAGEr-zH+5xesFDUYKQxIT`z4c5QW3awDIJ0c$Eao;A^B>j}OL z`omcMKl0u_zRK#__kGqPz{*-l)*^r=A&`|sEn2Nn1A&mR7T`;&b$4Mzfe&n=NTsEo z1C6%o#qA0&5=A8lC~$%aD2i2UD4;+CNp_23t9wTxuYp6{yY8lqZXyH-7|i|t=JTu& zX?xDO_x^D|_nv${EAyFeV~#oIm}8FlHl|%xVnAiF_rX2fca4r%OgiBMuba2Q3Abf1 zVH0;I>Kvfbj*hjjaTV6N>ofG~sY|LtroSv%bnPLn&nypMigx)t9y*1z~=Z(i5w)_D9c%yggZq}K1<3G7N!^|<@|H%_~+WXd8=SE(F$F{4b z6LL>ABAglfwuy(AM4#v{`Xd7$G5Ent)ThPf&8-cLS6NCgJfO<}x#&Dk^X)>1$+W`@ zzGCj9dsK0pN%!P^XwMA{jB~H3eAX{*{ZHv5`H5i%zAZz5zj(t{Wyn}d{Y!W;R^Pl} ze*P`Fmxnf2zL!6O@oL<}D(c^*^XCEPpc$PDs&Wr$99Ny7)jH+7$Un8btEzSJ&|u*| z;b_663G5;CKHtr_lCiACmN^Hj(nFFxcQC%dU&Bd%FLUc#I_lzP(9Q$EIcVej>oqFl z(3`$)>}VKzJ9V<<-PgZ1<4yb?x0%yo?{RPQ3yw_7Pb_g@3=oX?&)w;In;bgcKsqz` zbYRW3(`{~0x<&udTW%yqow%nJr@Dck3;c4Na8v%el58_hIq)i+xJ>1N&S7-ri7$t< z%$&waQ*EcIIqanQN779BU~$9VSo6s_#Q#o<{P)s#`T>W1m}ar^(nEsIO)t-!}n0q zn*MLsk#n6Io4)>4gHy+`%4h0GTfJN|_Bz$ordv|LNo(Yf_PbSo)kAqWXT`WYbdT)l zDZK+f=;Uz$KMooeC@nl(D4O?5mt&`Ud$aaVNOy&uZrGQko4BNF-9v&W-IqIITSoF$ zL(-GMl}A=`Cv1gx&|jkFPFMl=zgGUOpJiFUyEL4zT7Dn#`xKdZ3-V)zza_BZsSUbzQ0#Myftc0u$Z&I<2-reoSQFW0YVx{1Gz=SX_9m>tq`TSpy?UJ!XU?iSarU`#Z~*6>HlAD7N#EhU*j<9FGO5epNPbd8`R%gb zHgXm|#9nmC4~&_l@-ox^wVUthj2m$ezCUIb zV?oM;oL@!GyS3K!V57aC3Z3pH-9zxoWX7W@53)|BJyexVzDMvsM*I^q|7A?s%mZT< z@>|Sr3F}Jx=OAzII9~AdvW`rnuiSRLVES!G3hvW>x%Rz$Yk6lgoT;}-Mhaiy1Jf0Ie3i|&_Dh|4%b5Fwvv4H*A>q`63uj9WE=R^JY&-Mj$1{(NS#0AF93>hu zSDp?Y_eozeIyQT!OQP2^Z?WIflAR#6KW(7*o{$r-%@ba&8UCKOwcBR*M8=?o3402h zcLj|8_78%CF7ZRTdef5ch;$bD8CS=#-dUjUNM=&SMV({g8!BVs`y9X#|6ucy-))Rj z4d}4N?5_=?zfMbfATaout(mv8raO&ud`JBinqzVHpS-NUW{fDnHWuq(%U%dAZhqR= z!q_nmUWwYe%B!C#U1fFe@g}L-uH&nw{21?=^qp@_GH>DxKDa zJhkph`bb~SDRm;_a+c*}wl)`lljRP2S9%AHxY}}iId34lTn~1+teKszIPYS2&s{rX zld3WU+Zn#jnO&}a{8%4wRpWY>zEEi7XJvJv?AA=rJ8Jha43vh3_AuBi7L$lKGwoe{IHn48Q5?^o!{_x1l?NoT;9L{~_E* z+Rp44J@e=oa~^!c{({Y{9k`+KJ$VNyBgN|HU1jKdJG?Z5zB|qT$3W`R+`+P{GrjOR&#Co_TdJdNZ;yf0feGr&rx}v|x>qEzFw< zH)DYE@-utl?EgJu_uWnA9v^9jDsO8>(rJZPR$uuYm1p=Z-#KS@ez(z`*^iGzwy?fW zdd|MHUKjKv&Bre1ie$Zab#!($YgoMZpzyh*WzV{?by#3e!g1MrWDh$UsASE^BJ4}S z*PI|b$lmbfx_e>8u`fI*CRf|e@E)J|dsx8UC#KDz&wat8|Jsc#8UEmhVKhFx-WsRAB+aAujXw3NB zeT&XyK3ko6=nnb{cu!Q{5$^cs;hzF6hlBeR{Ey-1j?@^lUWUI6|BrE>#+`?qd8ZFr z_lx(y{VvfM`Xn=+l<_@}pXqz1uhGv4e`4mIF^h30@tgkOM~pvv#yq6HNB9!vVGqsR zJI3r2&H2#gUDestSLX+u@vH5OZKqrHT-Fh^$Hpf3_jtjY-s6(ZFA2|yUku-NV*k?c zMZ$E^hBL!!SA?|=8UN2K!sZU(HTd1=M&ohgkWatnyOZ<4hnL;*Dz1jRj!l|(B|QvpbB0>+%N7KG8J=$iSzE|5?<%rpiO%!{ z>r?6IC!9l1Yt{MKgWc&F&@ZWkbS3j$BUP646yy$b*tgC6p=XXdEWym_mMy4yPwD<1 zd}QbX%9$aUv2xIJ3MuD5!#{Jm!TZCf{xLfI$nETVcg5BCUcj#B(=Kb|G>do5T<*jx z0@D^Qi(Zp>ZJ-#rIA8WGus_lM%)`+rFg%HzM_jr*c>eqO?7u);*5o&c)~t{HtnKXf z>txU3`!^8>y~+0t_eKZ0N>l2aDhFw=;hXyktns(xVrz~u=$4zTAKWs+iY$5RsiKJ^ zt>FJeccvNKU+_auc6Z+LU8@NDhvxkP!FoS^+}vA6T0hu#^jl%K3%=w8XP>5&?k}8I zdcfXSXmxK03V-1VzZqRh^=NgkZ|1DW9mPc{;iwL)}{%FNH{^67v&H4}Y4&t9V zXz&ga7MAYa+NbuX^v8c{-sNle)oka6oqKv>LGsP=Cyq@1!Q$~#b|n@j7oEH-un4;l z-9MUPRgRo$z3_Yma57v`eiir6ur?fd(%W+XH0wp*N_QpxUpCj_zsLGj>^+D3i>mq@44C46`qntL<&TQ3 zSMR^u+Jr7@=9KqOvevepdu2IguO2^xw^3+Q?cGyGO~QP8Pa)x-0vX>e;SCJ>=&o?) zT+ReXS#Npa!75J<@7Y`^UEvWug3na4&pFUNJ$n=DX}dDIeOsUh{-#|++7IsXEP^I2 zr`EBbUrRe3WqnZX#d}V231dy&8rHn4Petx!W(@mg>ATT1@dkX+zVxN4W6isUG{6Jb z!Ke2KE`4^RXOMeZ;_$$9^-1RVr|FYA!{@|vHbb2&x#+}%OXb|l|>?h?1^*o{Y^1$1W6iPrtgO$Qq@+ z*gJ0FZSlzg=02QPcFr`{*=NFTRhvXcnRTZ1DqHPq+j8@K(RTjrw`pUu7N_#sE>!>7 z$ZFEq{#P&9^S~?1P5+LCpV@g><~%`_iL0m{ zCB3O1b}*LT(PfQdJ`Y{2V0O7>&B3ql+S<(7*gIHpTLt^oUw70t7ZHd87xR4yJEGc9x z41G;sD-?}+`#1SE-kS8SSgm^oQPoj(@y@sXvg6QHJmmEAB?bG*_a%6&*!M90Xo22i zAE&&wto*gPKWK0VZ~E}m);^oRP}#`;RnVYCIP>;Jd#h{PwZf0^{mtvV(JwX?NDpAn z3(xoP)|S)EjW`D~SHV&WJ?aRB{rhUgO7A?SejUE1~?x+jK zB<|QnE&f!7?vPVXxby>85q)gS_fEm-)vmld;Bss%yz`LwPy7Q7dhAz1pWb|6-Yzj^oqs3s ze<6KTe53R|JOe$AjCXhkei@~6S(5LJ6|*Gc4UfcR;hA5p>BTpk~dtj`Ay4Kl&fF_ZJ)L7%M{;pxefT z7W@59r$1Db?r{wV*Ttr8;{OR&j_TNU9=kfwv9?6AFLWW{70y><^9Q$_vKqLv^Ht0? z2t4+s=}s5WoI#Oxtri^9<9BjoF)?~Q8AbnI4W%t0?;49YAizQvkr&IlCrvk0&J z;Aq&xoHoiY+(WN3@BCOi68VW$5}9M~X^DrwsM(Mb`Ejpw=U>yhq|t4OFTOagCN&a% zH@d0T5AOliz0hCt+4a2X^@-*iYCok%wj6-w2URw2cuUWcPJn%l-DQ!-Y`vd0xVEx( zA!n0a*L<5$?+5Cz&zA!2LOE2fuLM|31I#6Yw$P z_1`bw&z+}Tru^8L&AI9n8$RRQEhjW zawYG2_77%Vx_^*_ExB&?5WfARwc$E{F25qh;oki>(tOlAj5IBFTJ=wBcxCN6Q~uJ$ zyWZ)AM=g3)7wJWt|II=jvo0^r>aV)mT>FAMpE~W%UbMR{ujtn8}tE&*_@@ zdiFH7>PPzRXt|HtuD*@Gi(N+2CNx&omIcA%8}Rs7+RMeS3IC5OYbzJ)eFo7j67mQA zym8^0rL$uLW&1V5*P^|B_xMGxW~JF{hy~d6VH}$eFNS>WN?+BIg%J?WGhs*%I&)-?kJQ|(E_2}FZoo{#DT3b72iD(jelD7d`vI4-4 zm@;mP51`);e7+>Jm3dbZ`tKnBN_&l|bR~CySI~d49o}kLEw~Yb(=01;Vo@+B@*^vJ zVv$uZJ3h#nwR2~22V)j%QoxS<+zKB#6n|{)qsZ}?4p>=lmsyZlv*%3xT>t9eVMCKV ze~xkU{b9SmY@fI9z>s$K`|2sT9vbrAM{qd$s)ZkU5b9QXk7jtroNX#dyBqocbSkH;tZ{n~_jL8}Tk=bwDx*JXTTOm&{u# z&Dmw_Jw>kUTg~11Jw>%~o6rrDi9f6UCAg6Xkq6ozqAsdiMN4f>ZvVKNwy!rohHg4J z;REyL$lJ54<2HR)JX4TZkBr-6>iOcZTU5^_FTNa79WNRCFWuM6fyd!xik0`Iw3e+tQeJq(TJyC6FzmJl! zhqdqkd>B6QZ-q|~9zOBMg6mb+x|V`3!Sh^EtyNE+TZFG{q(n%)et!AJ8 zadYQzecS1+n?)n$a1W^*`bjuaXI1QevOwo552L$1BwV5Woxt9!c7V^_kx)O=CJ|>` zpzj^mn~v%`MO&;!(eucB)_6jRTC=M9;COui+cqI=9EKF$bZSVq#!_sT2CzS$q3^TZ zak53vTr+L4)?uACY1Fu5_w6xPsx4@r;E5Nal+U^>>wk0c!?(jDqx})5zVWIbxQ|d; zekBIhO!`(Ru^wGUegE)@MLRji7Bcp-+D^Z+S@gE=Zp-#q>J#YWF&tWi1Mfbxloq)3 z#_3e_uy(#9*-zJ7r3F?a-zDJU#trjp)VhqnAi18n5`)v%Z!NdmGj*Nl3_VBWrf1X( zx4P))hmbE3`hhvOYS*FmbCq*xU+`6Js%~Y?LG{VKJn=@xly=cUc{D#&|KYy3&T(Cw zpLV@s=F8{{@W$mItG_c=med7DJ)v@yNB3gva%*l7--u6;eNor6l(1xj%2Phoac-?E z;PjQFcT`$?IcsV74cLQ;GxR?3l+`HME2692yz9xGuRQjjZ2pqnRMyxlpy|=Pm4mf5 zpUn6Emme|rJ!a%pS~YbmLm!*;1)gf@ti-RBU;F9gty$Vp!kSm;9A);E{5etn?g_P} zF4fj%W1whWMu4Pp&!!9;ek$ zj4m-cZmtbu({jOx%x2DObeSM$*!s4JPn9-rYUu08clsLs_526#J<%qut{sPv4@UQq zKIB{CO|@aYeopHM$bleh%F@-i6XH$p-*RKLe~UXxJ%d$MRYqs*AH6Zk7!AJ-dRu%a z`Y@&hL?h9ky^kO^@;5@O9F1=${w+8cnnX_gF<36Xs%yzL^3SxzO-U>k2b%b;U_8rF zJK_e}Q-QAr4%HrtNf$~&y9gYoZJv-kVB9uxocQ|eGfRG!*FDHmKJ5kMb>|-99o&tA zr@nBaeBdPTh%6~z@k6!e|3vN%vMdYT!LU#?wHbHAfU@-JH zeN}v^a2wxEPn+>S!@pnnir++QWVqT1*&Oz$uS)lwF1lC@gxUVUU~k4c-Ss~i&+N{-`7RI_c8tM zcGD)dU5<<;E!RJye8`b&p*v-6dB(*?vL>guzd5 zfCSBZ5Ffagu-6*-VjebtJnlR69+Jv8YwLQa=ghBGF?X1X{mSkoEy2~oh4E;ZZ?W`8 zYr~@Y;9BJE?eXD@cL&*LM6OvG*)6UpxD3qh?&jSk|2L=pl3>03(Rq9{Z)U!|o>u*Qu?IjQ9^nM*Jl>SM5>P^4+t- zL;YKR$pQ6m)-SqEzYZk@ZF%6`kDtEe-NW8`ebJLvQKTR#$XSi+lo#Ej5;pk2 z$!h&T=GkI8h`erh5i}8ZLaj~S-g|AA#)V^V&scgvqrHe>8`E5M*7bM$!F2U z(BkYn7fPM=KE{jywBs$u_zR>vYwnezN11e|*S~>&IhA^Z8QaE3jx&d92{cJgGS0|t zEIwuC2f^ZQGe^kb-64%t#}m3q!(A)Z%NmQ0@)k0B9QS@_&Fy0BKz3ck$eL#DD{#%wl4CWSv|7(>>F=zHf%C*Z!>T!#Y&U}a_~erb*6tNoU1z89)YDE)yU|;-7GBRfZeO+kmuIm3 zxclMIo;-AS%{8-$JMwU!7X0>m11poGw0n7LM%>4=kC_W{POQnswJ-9|!L_pQB++C^ z<>KDsU!Bgt_`8BD)TY?*8u*uz*m=T}i@Ll2{?D>3=GxZYSQ@^WZ}V1?!w;q%J8JKg z&WF4!gqCr=Xu%zeD@NG^t91Q5$Q??Q#^~* zkLW9(H>bq%CY1ndIxvqaeeUH0s_ECW=(n0fnf0sEe{t=_X6Rum!AnqhwwC{J8>$nQOZa+ow32_(uza&M#0CN{}No{^{$$&-}S*(l8>?Uqfi<4r46h| zroSR`n*Pb2ZR8AnsGW5$oqMO>CbR?&ah`^ElD4nP{gZt^-IS5(uTJrO$<~YhH>T3_Rg!^|@UR zF3*C?5*rsAk9R*8PVw>VHy5z4xrR4Dft!0g&T?p3Z)`y@_b=|^E!|UAN%3hbTzsZS zUY8Z0MW(g~4i$GWtvxDwzgrmD5oh(_x2;bJZPIA)dpE8k@?M;2@5uH(!M1bTK9B6^ zW9Vec+kyPe_qW)%Z8PV65&{A97Nh3F;wQ-=TaL09TZxSz&NW3san-T0DH2Mm@U!Nm zGYtx}*Q2b*7502d=u74?nZ@uqYbp1!mNJjE6usTDoVAqo>=kTg521ne-A`EW{R|sT zUzspjsm-hOx%7mb$W2MrS}Rr` z%=_586F={z0?PJKiI_^-UE2UJCpZXZs4t!aXo!9)*mnFi}daHDMRDh zQD{+_n2~H5wbqT($<0)ZqD4{;WqsjZ^>T1Fz!;c$y1+)q2SZfMoS6yIM7--6? zM4y#BpRY9r%R2jVAFXTDW9uuN+edpBL5p^65uD7HWvr)J*6f-6qmgTpN=9^Pjl{A} zE#x8dYvY57m9|clS--41a%FtZh@)1c;v^T9?o5WeG%*6~tK;W_XH>M3{>wQBjluBj z?h)T=!*kVxV}652K4Ut4P-(MAE$e1oBB$-_hGVC)6C6G_xYi4%(wlnZ)8}~0*}y-$ z!R!^AzQvls!h-6e=GAJ0pr!aoKK41WA0I2z)~*XjH}w)wlQy^#XHrMgO=7yLx%mk1q39ET=^MY36QtP@>gb7yn+udJ=(SLS^| z;2FJLyeXLCC+3#45wOS?30WnyAMJ{NhVjqUx5D6=u*xG%6ut>7?2@u7Ox-%(-Ue;7 z_Y)CK61g|5vb6S$4WD|or`j%i!T0*8Ou-dAgHLtfXyjT?MP8-5QGWDD$&4K3iM3H% z$J_fwds_w>`c?PDovmZt23cz4r^-xhp}nG}{Ly~O=xLLhwzG8uV)QogSNhs*o^oiH zZ`u6Q`KsQba-{B(4!lI#)~>rNY^Pnnxp<9LLillwJulA>28Baum$TLw@>}+|XzPlY zJZJ079-~vIQQ$sBZDGUp&$V%VW0m+uX;hEwe?xD`?rYj!@AXDT_noWszU;U@N`Gjk zkG-!~^nRMzqvCCoKB7tgFl#|!)`F&5!OF+&wV=2Xdo2h#qq+U@=2K}u%}ww3sMf;J zO%2^NhRvz!SIut|znVzMRU$mpU*=Ty+g@7LAHHi&?juYQRCx4eQNOj7+ zJauh^Rc-m9X_nR|P5sWjZsZwjL$g#z_Gs#X)dYVf>{&tZJ=qX-(G1 zXlG4AV}#`J?D_q3_>H>st-7UM!z)yl;qz>N1$#ZgWzj1S89x7BUsF%^4T7Fu#U+h6 zn(@u@`bT*L-aW0ka@bFoED8oqzL38fyjC2TpOCDvI*~C}=lqsEWuL7RUnopz6&|_T z&)DT}5&nqCIXx_hu$~0EwH5M!7l|;M8jga z8Uw^P$;cMPpRe02KHxXQn?qTYUksd)H&_Ag?|rp9cdWT1!#R-|+;QL@A-WzmIVr2b z8!@;Y#g1aCe^Fp6c;4;p!Dn+=Wr-J?-{H=}vBHKCCny(QW)Jp5%ARb>{JveL@MG>W zcH~`FARb#cyC;SUS>qo99gccx<1%JHDLb0T>zu%G&c+Rd4=YCeOQ>6CAF(Gq$lsA% zk;OUci6JY8_;mDzio>5`!=}Jx#!6tMEC1~IRyko6`bHnKG>$5c@S2e;na3Bns$*eJ zdh`xgjoQz<&^ss=NBY&G31b!eNrnnn341Jy-Be+^2?4xko@&_e(|YQ zH2c4>zFB~-c@buW{4Iht@_hN{TPr5B5Ym9%iza5zY4Usb;=oU&YH{YYIfN>fB zFSYez@)dZI-Mm#J-AOWIVS>(llqFiS7n*5gR9naTFO9wl49wO8@4=+}1ot#M~(^fC6$RZoRw-|YXI z@BybL*t(Ydip!hI7;f^sd0274zu@!(trrfKyiH;+L2sn`R*j0b^45)Sm3bfRXDgx= zY52$EUWV&k_I%-S@T|$}Vtw>U-jVq;Ywea5(D=OY#n3^81AmoCQyH40cUl>@JC{WZ z|Et2ZZf&Q@c;qKf_YaDW@X($}0Xk4LP_?2ZXtCG*GwdE{@59PFJvahw?(rj&S%aH= zhP4mLWyZK5?eggv^hljG=j>fIZReKED?MY)y{i-(lBZZ(?`H4jbZCk4e^=uGZH)cW z>cnub{hOOSHSp?$r5&>xMK9JIM~hy$eG}N12pm3JWqGO!7Pp9I;(1$-atE77r#@`b zpWFF{(z9N0Olka2Ba5v-;^DXbox7@o*YJ+%*WeaB^&GgxRy zq4!fymU^u0b!U(1?pj`D-}#EG>Iyw67}=Cd`xKtKlXt+1gXoRCfq0fRiVXL7&RI|6 z4yT**yGLn{>At&1Ne7yYY^z9!1Wxli=iipx{;T_D9P{r;ZdaK6@J7m!vyA6atx<*U z6d$WiXpbXxVfK{icXd`UnBBRooBs5y_K%4C^&d~l; zz&cC)A~#tjUgTGEW!g{COOq5Xe32V-g>TmCce-^}LpXc4#tjNiWllf zcmJ%n{Qf<0dUE?a<7OO7oRHjZ!tfWTS;3P# z20ZJTlKOpVRj3U2I%r)_|Jna?bugK^ zYER#RCc1M+KTSO*xnOwh%zNhEi0VgwaOSCABU4QL*EQN}6VLZGG=T3-UrVs8b#Db* zu=^X9Kk~h#aBfN;EAbG(w_~E9b4AJYg1+8 z3;0cq8;^Ic+(@5M8FMM4Iyana#e6T<-B$B`lsAPM3XML; z`q5qgIbegCyR57J?p{+D>d@9v^LZ}TeFVQMI(#*}a0`9Ftd$`@WY1G`m2=={=0UjH zJGIxmPVY49B=#AD@8k%E=t7#|Cg16uLzPzX%!lUwb^MNFieL6Jhhi?xdv^C{;$iIX zwkuBMUX(RE9#TGqJFsQrJE(Gd(wya-C;88waBow(R;ynLdr|4WmvnC0x+X?{(TO!w z#;o|k^yM1=J;~97U7qp0>4q(lP`BjIL2O(5Cnr}VRRliu)*+AJasQ0ur!@w`_iq1` zx*#5qj?I~n>pVloG5Gf^8%S?x)ZF@3M<-Cmtc|qp3;}Bxt@{U179rY2?YY+L*O^Y~ zxN3*eW$Y=Zpx43MbIOe!*)r9S`#hgSuo)L&*W+R1`%i0^1LIwgH_j) z+pzDl9)0>H?2K}U2V8Q5OAn89y!GJ-1U>rwX3^GrlcUdNS{0rGbcWB7Z7tZ$qb}kl zyDfL@iQ&we+_aY}*>_|QlQ*Z#d-L2Id%LuY_EQ_DSYhQ=nRebc|17znxE`7jKVmIy zmcm=%RV?LI^4c~^8Hcs3{>`*9bjUv+#Lbz70_VNcM@`MHGW zlEYfZLw*jy=1AWclLCPzY*r;2xnbiGwEN|+zqI?^>UDzc;AqF4eb4k8Y&DyCCbDP- zI!v^7OX!5gL+Vx`8R>7a&rh&6+lWqM%gsKjYRpt8C=*CKF)qSX{jiwNahXc`zuSXoIP>0du~KGjI?e7+=*-573di3VQl>B zqHckmP57gBe%)f>LLIB^I@wJxqT$>pG?#zJtvx@DXg*5YVfwMr1Sa zIR8}{+8i0qx||g_&YhM?vem;FQO((_-+NgfjKt>&^Qd;EajKdg^Z7v9`aZy2A?*a5)G~cw-uGySiy<=g2OZLuG zC*Qw&`y{jk_R0>J)*PWn65sc!Z{p5Vn$q7E-Y~+0y(sLk&BS&Y_SdwRxi;@#3YL@Z zhuAD}@A3tI2Are0?}u&@KDmRi6UAH{Jom!;@5?sth`i8Ebsa(8qz`SbTamp}>p9e^ zlYRgCI@y~SFUpQq=k9eA*Y*h-ehLJRPE57ODa{ooEQcSJ=G5-fvS+I_lrwqa`93+l z@(SEZXTt;!7YT|w9i z+-GEGlQ3IP{`xl$X}6H-oeO^Yvd%)FuZ9|s3-p0u>*QZr6}+oA zP7A9F2aE0#kMt?AX|mId%gn`Y5v{;|HF?sUJg?CQR;ezk6X7>H;mC8FRC zpbK>_Ia#$!dN}&}`Xi6|I}_AT=mV;M-HPBf6Z88-?YeO9lQyMKzpc8cpHJw3wrhgP zc3;m}r##xm!P%j?2+!>!|ZnYX5~*`FoTf z_&;Q<>s+!xcR3l4GIS=-YS+9}@{)eao_paqwSj0B$?sE*jj=)-@BTj)4Au?Wq5C@8 z5qTE0cuRyaqfd>}7#Z04?y z^~gMM;STR-$dlWOe(Y)~`jM+u@7*Mn1g0!32$mGhakclVcYRyuwmSurvqFz4uY<=d zJ8YPA*rTS7+>NsF**zb6CP{|sPO8$C9-~|b7uB=vT+O5hKPa4J@^M$q&<5DIRoeGK zg?p=O*B8ob>ZE&DtkDzpJJrR&1)kvA_`I{tGso-8J#HnzyPEGFm7dC)V;|mKnLNAO z9`j~1<`s4IAI^D$sOqA6Mn?BR-@(o2ypwdm&MgnAT*YghO#H$5kb4Y&M7c-H8$F!) zy^isZ{V#Mp&SL|k-Qq9rzgy9!X+zpMud%%NU}#0tjMVnhbnoio!}!nL`%>u<+{5>-Dc&DiUP_wcPeaR_@Ab8m5?|az7%+>; z!~2Hm`|46zGxlaYQx#2V9_eezE@uw3!aMLtsjr3i!j)&a%50v>_dy$<{Db$t*wg_| zgj42n>~jvJ%;m+Dd*oqX3-5d=&g2_&&%J9kmI|j+D-$fzESIf*U%ofZTgSTeg>(lbg z!;6YoN4-aVZ(sfB_I(G|v?B|H`|9&M_Z?U(yGen4^<(DmJMiD;^X=h#H{ZMYj^mEd zaqsas&fc<~!TjWPd?I#4q6xLrXAA7e@oM&If`2_KRVnuC~i>RuFwS&M?Qrs zOy!{SPHnYzJSzI@`(oMe{n1<=t>B$|Rgf~^J4a+6-)YCE;3_8m^Wm|$tWPVHCx&!KU}v=w(Smpkov-!q>^W%pwS zZDX;Hptqzf<@tN*iFY@(jBao0rVT5tU{gzeXHz$A$eM6d%b591-Lzq)<>7lb-@Eya z(|K0e@7LaDa~12ZpKgjWo_H5LQk7xKgfHk@eT6&YO^)`p_!%27+yURJv6{Q^EeijC zR(@9Gr5CNS-iPA`9eFgh9XnJlM}FaJr(CybixlLJ^=??@8UIk$7Zv8(`WCX_ZqqjK zOeyW*@W+e$c9=d^da%;Ver0H$uYmno+Wg2`_+(}EsOB}7@X54jKw;0!U$V*Y$Q1a3 zGHkxsRzx||Vz?YIJh^47>8s*Nm3{PL*@iy|^KNxdDkgkEE#+}0CdMO62S-_BH$38r z@yGua-f;b#epYFPKUF`YZ#6zZABtLfXLpL$oEourQ`)4ywB?ZLOIn9g9BXmx2j5Je zOz+Llf+v1fA4Mk3GySyEefJ^t->Ud7_18n-e~30|ewTie4-MB;PZrPQy-QzX|4(*a zmQJIu5f)Wnn-P~`=t!K&L%70XIJR29uHLtjzUJU&_w&sOreF8!=gm9pzNWs4+?}_u zsyfnqo^p5CdW46)m?qw4@I({>@vsx_WqXD&=mq%N30l7L@I6CVt;) zyUzROn{m_d_r6jyZk94$z9jylPNm{o#>*E~PrYL(xJ{?%dx|HFF?KbyD`jk51O4VT zrQ7|Iv2>m#IyR1mpqa+e5sagE^Bo&Ui#3jBwQC$*dTQYz_;`}!)xK)R(QRwm+3yJMt7aVC zwziY+z`kn6(QW^2KHna`ck{iQ?>J=0aqqivmpxSKw^0^qTZ%y3lI~U)aU5WBmH|o{Af2#!J};vEEEEe8;zVPT{StHx|KnwoF$0 zGgcn~N0lM?3)UeEw*aTgI|!RxLB%cXh^BbwV?T*7`StC3gnIMcMBkMAx5mf?TdF?h zZ1*(X$rJ6HqKto)Htn0DjDM9j?VF;Ef0Z`v`QFX9p*=eOaqLcA_I!QTp%;J4__M_x z{}y&s4HaDMrl)(i^fl|P!r@f62VNTXIp+kXu|5){&7HCE^-41q(l-qsFc!!7fN{2m z5BTok1HOCsfNz@**c)9Iy(-YW3i?Cm=Ch22rL^%G?9V`B#vo+RrW`X4%CB)yVXoII zp?$HDJsOK*eGgsJv^Qa99EASj8=V6(?M!>BUD1*Ls-1lUX=h-o+*a%LqHndcDRbl2 zo_1E5^lj7MDbut!VRn1hPQTdRmA5F4dHdzAnaErEFZ>=hGO@%;Z)$Pm?sneXiG!a9 z!_SOcrTfLtud&zR=!L2`;jOM$-x3^CZ->_;SH)|BDVa;Z87vvAblykYlhmj7)yAJv zA8M{e20Vt&$a+*>E&AJ|-hpc8rNz-4g)JP2?47_lIdnJ^M|y=T%#35~Pqtbc1N*js zC*KM?uuJ;tEAv%WZ@gmT*b#UiJ$~MToygwt_BiI5P1~5eFprRa z;$t6s>4<2Gr#Nn)-pKM5(AS}ZbST*e^R4lwAK2y%Xk74ZL&u$89X!JL`po>`D9I=9 zDkFole5d15ed&GEl}`0hob*;-Zd``1xbHy5)j_mNz2-7zjzBzZo}srlPp$adda%Z4 zwdG6bI|tE)jlI`CjL9YFI~xDcADS+MKJZ_wwZ7Bvh;)jf*a0EF)m8m~WM}WT5IoUB zIEaRhJS}d{pw2%>-$AaT>$EgqhQ71Y=_`yEzSZ=VY4jDo7p6zO^6T3-g}Vj91zFo_ zRkyRw)E?6f?6J6RvieM zfBQiEVR%w}Lq2qxRpJ|_zgAu*@7;{f(t2lLOE748^4&vQ!}acE!SUC;R7hJ?$Ax`U z`?-9JrOW*w4^KvecKqQu2Pf=p?_z-@+-?DSS`&<-6DNTO)U2 zxMb9)qdlX1!G~SZ{E{?ZI4<=M@k&dYMBjQFhV38wxyfS7L}(Keyq9(2jUy>Z}jAYgEe%gZgTK?J|e#6n*$s+F1DwHoPI_9%ux7rSDyT z`LA^HtNsmqQ~nTBelLHl{$=H<`Ipo`U*$KnT4}0(nGGu~R({N1F1SuS@YCix^{4#& zD?NsOX{LPVdpPM`D&O&YJoz#DkKXfXre{&Bm3ri2eCl4}WArN{E>?a6;oz294jh#~ z4!DYsl^^pLDcpeveyKT5`>A~SJ;cZI<4)X|8U0x&n#UJ>~@R#F`bNrNl zxhcPwKUV(x9_UE?a9q`=!Q&GBF82rr`O8yB_HKW{C;qZV^-0Csil~lYX4sRx7!8{pH_F`d;w^4Ls5}cnm)!x00{H_znKavGSezBwtK#$L}TmIHh;W z^8jCdl@ERcS|tN|#SeGlm7e(IqD%Z?$M59lp5wRbGeGHupW>;{fZ2b?Uq(3a13mS3 z{Cml#{7(FUBK&3YE1dM@N_Vln1F`xmJ>><8c9B^RGxah)BpYI|MK_r zf5-3idB6HU`PKjZHNE;j<@?JC@5K*(Q~pK%@Ed%4`S&Wl=qtR)@4=1bAJ=LnwTu6q z^ztix!$tqM;1TZ+6EAvDA62+1&n0>ZUYv{isQ=6FiOC;Z9wn-L=?9K{ zN>uqKKQ8GLP5l+`_{)36ml1F3@5IYrM7;1*dii_9lfS-K{&CpNGxc}U%dhke7yULo z@TYC!T*Q}qsBf=$@n0`~NZ6!!r@oXYc&7g9&+>bm`j=6E{M;*W=x@jSNN>Z(-#~m% z{T+Wf@uE)|uHsGoFSfU@h;+g)7B6_c)5q$sc*^(2>L2qfeS_-j$yfV&H#zVc1P6aP z{ssp=`F*DT_+#-(|5y1h@`o?RA5MA3@9G6_F6G4hDz6BCtUUR{j=zC&C_moRU*$V~ zr7!E{--~}-FMm({N#6iGi+GhM{DJ4$OSsz4Y47+V{3gE>->Udt@S+~*gx{%;N9h|J zc;MRu?|s3+?@{}U9=5-~p?@#`ULW*W`|xcK|^w7=swuijQ<)u;KjR*vHHjS zd!6)-zi&|tp2A6Q>Z9_=kH4?VAJ@y@17G<_zn651Z*cteF~3~$$Lg>0NZ&{4%LLB{ zT=IJuA2%w$6W{8@3!dVm_+#+pe>CP-I{d3i=W@zdyy(#zzRK_I7yV;?^7kq7P)`RR z`NNLiDZkHl{NreEN5AV6cI2DzS9(k7FUGrvE4_n{i})UVNiY9gC%#N>tK}{tT;(_5 zZ{RLb`6cmD;tfA4ob=oA3w{}{((hGxFMqxK;N$df%d~$l|6b)&deW7VzesK`e^`Ff zJL9`G7x}%em%oVoivN@7z&cBg#Lxw%v_AV(!wskL{q0g{R7@S(}*v zyxr_WRO41J?8?+W`i6yFS+eaOUfPwt8h8AJX;yH_Q$d|=cXR)(AAKRKDtd+P9;{!s zD5y8Sbe2eVzmI91hqh}k{^x>*yspqV);l|OmW1_1?nTC-^#qRY&%#FR)9!TMynj49 z8nw<=B*-7XE|@i&ddhz3Y|2eBQ-V(3#0eTtzE_B-ywe#boQca&kjV>$La)?>e8Gxj?g{+12!y#HSCnabzP(%0th zUKMAmT0*kJ(=YM?=l3~B!+F;fgU{*eHM!?FAI!O$Wzm1y#v0`<#OdtrMY~QX-`b`; z*lF@|?_eC~q~mmM7kIiG-%oiPYtuUUPPqAT>x#DS9qWPN=Iqizt+`&L@y<-nnqhzP zpS~j+v2QXM*IIb$Wo(YISJTDWwrEKa-|R0Hn!AHe`~H!z@xCtY=ecg=yd5@|pl5f; zwWM|3bNbdEUyqIBCxex`L#VyIwy10xxw*v@SAiYjKVqM_5gWom;AfBz9EK(;KW8d} zOREaDi6+?1Dxs~9aMxfG_gRvNi$X`)0_Ln*!8@d9&nJBywk+?4Z?03F@IRq&y)SeZZFKesp#A@`3)&Z9*Q-QnINSK4Xzp(b{NB*-1KwaY z{PV^-+1r}Ax8Uq2>BCxQ9xXU0`m0@(9{&ZM!Bkq_GmC5Mc&e-;Vu(P8y2F{t+ zzZBke*tb{yUg6itMh<7~Y)y~Ilrbmzu5J2bM6TH_Pm22xP;rbzR%Q55sf*sV&0=u zT=)a)(eUrB$2kY6_6XYL?0k+qhEB_(k8Y$bbiRCQ(H?WJdA;(roqV(J{u$|i;;h@} zvHLCJ_c`?i=VJF<=Ikv@Jy_{W5&mu6Tb9qMFZ@2gHNJK3>EImT%;2BEu^YE7?|9)J z+&>>&;td?E$~exOR9|5?s-C?+_nf1JuDqj#pKG6&?|8z)ga`Q^kX>PYl@;|`d}jjF zoKxrfYSOgit+3+e94{O#7DO!?7XCUbV>)%#7J1#XAXWPy0SpP^Yj`aU{ z=)A|ATXgajDlYd8Ymwobjn-3#J(LH(9=@8r8t#l@`^v+aORwPv!?Uk!(|JdwH#{mn zvBPy=^i~tj-GpB0C+TjJZ=riawpjPt1Y=pWbe-gZ_)q;^ zc~tM*Us}P#w_~59qi(e6i=?H0m~*!#{xvJ*(EHF+@6~tg{wnuP?){NQae{Gp4sqBo zgYN5cU$+9LK8ZH`Lw}03*X}aPoWeQXQ;Me!UmtwVvyuBV>$T5~j5!s5tv6lpkdtZV zj!VbxROLbDxW61#x68mKUU=B;wd*i=F3Nq&9%~LiOZ#9WqV%J}ui~w{zQ5{f%i>(W zCq5V#&mGMm?@Y&Ww!fe3M|^U1N=B^yZ5`D{KJ5-?)yK-P;n$s0y?o2vy!o7K%116> z-@%=3-cYmpELa>24p)C|ns)KMrT5drVbkxwg==jYxEfo>I_t(c=DXB~+PdHTs`(4( zr?f-ecbRfeZjhZ6&dMUQpw&L!3cHBwZ**VGwCBF5Hs31`yzFV~_|1+OZ||FG^Su){ zm$9|IGOd&VErm-fc`(yzX}D9<12OPRMt_As_euc1%h{Gs(|4{j%SensB7 zl+%xUL(+TDZ9?6An={`{qZiD@tnNlfm|R@V4&PHvlp=oPMXVaF{_L8mE@TNN6#@k|D!};jWk~-^J;gwr!g-do>-}46pmvaZxlDyu1 zl(&~hTke;ugFk`ajyA1I=`7i1l{BqN?WCL^@qK^OsKOXez{ebzlrciR{f-+6>|*w633 zU%U_g8uwG7(R$xgZtTMjn>FYC!ihcQo%~Iv>PGlR!6e@-bW=0`%8sk%{JYEO^LoE5 z)_!d#x5Z~q2wF-{yFbvQqo3S*nD?iabN1MU?ZEr&v&Uo0n9dn^J@4?GzIdP0#`)a~ z1|Q&kw?ucEykS$PP+Uuoyf|NTC3iXMf%}=@QbuA7uFB6%!?y45U9$y`IpF5p_$&Sk z^T39@z4&==Mm8jw4}NCevVMPHnckKO*76?eMpuIB#vF4%+o`Qz#_*7wdA8&}BexoV z!ubp7zaHA*)Njmqt-AA;XjL=k-{Nm_S2lGsE^rno_J$wxi*9uJ?xyavjQ7*)E6~L{ zxyx7XX>zXs-(Ra7yY6p%qVZqwjQ{k;%`qHoTKH=G3yp0vcz&0;&m`2N6p z+0UC=*Alvu`^`a<-{89CPJ=hFX zR>`UE0UqXs_e(EfK9i9Y4GiXNF6ZOYT4&C%?w>i$Dp{~47$(iAs%UV#aGzOh-a{LY zo6oO+`Ip}L9LrofiG8hWdgfez{dQH5w(YR2KK%L)=XW#nFTTwiE+^2A_z}(1{dzz|*qKHv#_soSV1nitP6n{kI1)&^uCc>H$-`lg z;VvfZ+EbPBo(Z2@BL$PV@5w%l6PI(Ua$t%Rexu%%b;5InlM_Bl@9CZ_9hd^F{8N>@ z?`Xp*5YA5evD^glHi z39ss#G+W(wC%hII%zM;#KeJ-&P}MoS)fyGvV&yAs3@%{>F_^@S6}*wWBT~1baF=N0 z?+gs|pAV!9?-O?9M#up6pxW=(ndgKP|mNGN3M6@Q}t# zwIR5U67TZOUADLgvUWJ*Pk?t~;&PAJ@2>IPkNZ$T+S`Mhh3mz=0ylv>!B@3)zS={NRoL;k4s5qyfItEc=f#uRq6W z{~H78k}s-n%h%YzY#xyM^Nb;IdVlW zzPgIAY0p$PWrGdr~E%~_~jo0gB-Zm^n!~l?1766?2%{Rv*Bi~rR=cM zP|xu(n1tP|w8*Vn1yk@QFt%yT>s=rG7yCf5HCFgb{_I|n*>3Rte^>rkdH34#$JRGC zEmwboU$+~5j`c8fd&|h3I^+&xA^tdi{h&pVd(a--EL<<{6}So5CAbRyPSP02_&G{r zAa1_KKWup=@?D^M!_%^b7)M%*d~V!0c=b~`j{?&GR|Jq(@EA6!YmjkIL{WLC0{`@IR1t&yr5c%~rx9@7}hx2LpzY?;P3+~peI=G5~Z?Lqd3sMDOLvGcqd z=Xj^W=C@ZillP6f=$LilH=}>TV|R4x-JcItkBeAy^%JHL5{e{FKK_gq2yNwM^j&pmUI^P71$_}gg(e@WRBSFV zhszi&9F~n;Fy0rwXXs12IB!ipPmnX$yEtGRqwBlCaby0!*#3U^+iv{NY&Y(t?Rx*&cKhcWHzWN^ zwFT!Ge((4{w*5W_p2w?jwWZ4T9$fkl6))(| zlQuc}9tyxAkS1Kg z{T%+!F8br~#~Z(B;?&37u}~TBjL&#Pc*gM1H!%K}VG5psi+q7jF}NGv89!_fu+{?C zLpd9e(Ra$P?;aXt40}R#AfNbfI(cTPY}Q~-lh6GgKfW=`W71qBKlCuTah@ZMxS^iZ zzII!FJA9SGmA*HP>^wua`F8YGnJ+l-bVl-sq3v|f4rsUD(?iR6%8HNCHdQz_O&Naf zP4*V3M=SMqzd0r2SkshIoiEwA{ojEVaA4_<&`{v*7rb7$PZ569b(h0OF*-~E*KrOV z7Wbk<@9-}#p+oPup$;8b-yixpI5bcP^vq!o>D%so|NsBrhd(QBQUCAW`Q=05>bN!k zU#s!$r>vaYS+mM+)me?KppSR$bxwmdu`0in99`B@5|oWF)&*8Q|60^@WUIdgf4`&- z_LK)>Q+iNxId)Mxl1?X2&K;a$5nj~X;p_07c4zSYgJms2Pf|;AMeZPv;-)btt@>S6 zee1|lpLwTDZ^b3?rbO0HE$jAf?9D!G1-Vaba8S9-=Z0eoKB_q0zjAQ1{dwI1V~YbD zm(irIzzpmzwI_83iVrw*73Ja0N!A6Z54QYO_MWQxBMMh}I)4&_nYH9Q538Jm-6@^} zRjGkFD?KTct?v}Rg9oZo0&}W7p6t&0VfcsV4sv~iKOkJ^Ac+af!9NOG=4d@Zu(SVk z<)^ZXWb4#6XNacX@&^NJStprsc1FS?@ND=018+GN1qSYWcgO|NdgOsn_fsE5AJsfW z_77Ogt!0fR$Nyq*UGpx^&(wN{wpG_lS0{z*Rerof?BX1atDN7X{AP34a@q&0>b=|< zJ}SIEc%(jX;5cXdWtTwyNyfji$3MdO-;eqCZOiCf7`d#o9?Z1&$u1i$RoGKhmUOYwI3BP@ zZiBB>7IEbBd^_KjpR!Jrqc~H>X@}1STS?e$j*fhiL-5#2`ajuw8~7-zD}VgXOcI6w zVSuPnBY6Trf|4*GO3*+iFF>L|#v~}dH3>-|sUaEi0zoN^)^=&@YJ91s72C9Go2spB zS9kerU4Q9rU(i}hyRD_It*N?}ZM!zDc7NG6Kl1yYd(NHbnaRu~BK`mO^Z9X~%yaI! z_ndRjJ@?$#dGZYQe(xOmd1H*oL7VUz2@tdo_w;)dG*Im)8 z(x?uzKdCN4JVoN?zVga+?~H$NUq|okK)PGf&!PYEP5-fb3%&0H{S7SU50^qXh;BYY;G(#fZ0Spt_vELbp}s?Xj@psvz~hvs z_uYmTeUatm#me*fA~b%Xfo{$+(az_rI*b=rAUJ)7aMR~APWo=f7*#(zdv0aM?|e7U z&Y8Ov@r^$pn>|8f7;ycp)U7%AJ~2n-XJ8x#^smhEw*&Sqe0_TQs&RKS$p>Dv@b!d)?cW>2rzH3-REMwgP)sJsa?hI$R11^xB4bc`D_gSt0CAn~Jum&%4QKrR?O(sl@sarl#!5z=JkB`rkDub}jeFj>$Ha#-M&^&M zWh@y#$?7W}+kZ-A5C`d7GK&5@JZ;Dmc9xbX?g>vDnmwE|Vzik$Kk=E1O1tW)U7rJE zR~_|)Jz>gET(Yxln|J)w?awiHa(<^=&(O4C%>MeCHtZR4<~iedHa2cxqp^U|oUxL) zL`ZbXa>l9rjL=N~On*k`bj$z4z3+Vgo$vqQ-l>-FIpsN}=Bu@8w|dL48ELklLokUq1Q=??PIPuWMWte=s#OPSgsghHBU)~kD_P$*5a?Z=H30E{1W7ON`_o4COfU_|U zWRGMMhHHM&?4oHScq?U}BCK!r$h6^j5)!A8FF3D6l=1BsZM$UScYJn>!$l!;We;N<3Fo|A^mb^UN$lHK@^;S4@ya%~B^WP%N@>HSuhtel zCibFqPuMktnk5Ljmw`gl41+G}Vrx*}oGj?hqt|QHZqR5bV>mlhR5Tf%$+vC6{02Aq zrwfgosnmhXpz=?*w26GC{E0b|lC6h58Z9UKAM9?j6Y)FNKgIC5v0blrAKsI_KYL$x zpL8Z(5hdbBONHUFxQwO~Va`ZS!Ud3{e3Yl_f_k`yAXjTo6-#DOn_pKo8&A`W&K_08 zjM2I-x&ELlR{PYL>s8O;Y1tXs7`CO}x>VALuLK4lD(Ad$VB|ogbmBz$*))PAU{@IF zNBbNz5AlbJ?e*wn`s?ypV_JB|c!Av(3CoYO{*AXHo+`ht?^w=Q#BQ)}$!JN!`a9(s zZ6`}}<~!rb_!IVDS!p;uF0y(E>rqXwOPu8-iaY((8Bat{>2b@7qf;Uow*QmQ@T!UG zLKpFHjN|cS&`P5Z3zc1`x=-!7XHSOX`fT^9UH=ur61L=2&Z&63lonk+`>n$#*)vJA z1R=)Xi-3<{=8NzlR(xReKtlgnRN=UfcpBdwi-@U|IkcmT@(t1hIb8hhxe%c7|i$KzDIS3cMXMxkwe}zGa-MJ z?W5bxV9EM2?bG|_?_0YcVb9y$FL%GZ>mJA6U3T`NgbN_s{(YEp_h;|P?moQh)zBE` z;$k~5vHczG@AO(nJbC-a^grU${msVNQ-{vWb1k-r>P!(%px)PQj@i5!#cHKjMxaV#1 z$$e}4=2K2WeiTLpQ<+pAmqx2^T7T0DyhxA6$@nKqa9WPziTHKj;uRLHUWQ#m&Xpf~ zU$Xv3d)?SwODs+_iOQ#ehKeViX}CyR+O?=*TvWUvNFYGVHk~8^OjJHEG1Y=a?i`bNknA-kd0G`ggYXq$>NL!YHw@;EE; zDcGOdAnw>rdKgV9J{vnhgF9i;`QOa_Bw!Sm!DAqeL&be`zD$#@ ze}eL3)&owC==h}m$K!l7lLPA>+cQS%H|KuX_@{#`VN~jm-tSxmB(Fb>`Q}3XM@-xS z!=5o$!tu+gjInq$ocj2|eB|joA3JAcXE}-Drrc=n;}gt8JkE4eK9x)B06u}^R3}}c zxG6sxO8pazKTcVChKjQah@QEe9QZyP-?B^#J6a201+I;>s7?=iMqCN!e`h~W7LP8~ z#@1q-nHF|(x=@-EpB65GM)AV@?2_y~hr3_hH5y8&Up&5eX&$U4W)IIFDemhlHs8l% z38$}dd?y}{hteElhI7_5af~F~f4D{yPf^TJL$adV-C&dWzpVLUbeZz}N%bZ_CH|7r zSbsS6KP`T}ZshZo)8XfpHubmD)1T^XLZmHC>k*u!{haoT@WUumUB5W(cUt@o`<)iQ zxEa|X(OjNv{o$F_(((NpKQt>&z==`q^&hD4xgV}`cZoE|?MW7suBUTe3rnQ14U zKP4M~oqFxZvpk;9dhYSuQ*zINQ}3L5BTUyjFCRFyHsSnPg6}Q=F!U?OMWdAyNBq>U z5{_Ta_KRl=3>cxJ3a}&5K{eZ9%oGW zqvmhhYVmCr#gAP_?Drowzmqb4Jo);K^ftQ?(#8HJm%q=Q>>FJB84g~YW-Ic469+^{ z=KrL#S+AM%6Phc{Ud}+)bK)Hj$%Nlf#p3=WPh-#GQgsbN5U?H`W6fPgsHsAo)n zS1oH0`mW8n`Y>^_#sREECWOdHaGLXvgMaGdpF;}=e|%q>r2QTI3H&GN{ynFNzP(?q z*Ixa0`i#X(&AzCgctLfyV)C0 zjo-*L{y{j$Bj3A9C!NIpMi5}^T;j-@QdIXdN7tJQlm7ZPE%L?f@I{!_P=m3HPOZi> zT@Cz=7|vJ{$;!|V590JMEh8f%I|I+`U}k*T^Vw-@Gxle{z53^5(bAkePduU2`}=I!^28IT z{}~^D;)(b3iMH&S#m2*Qot{ja*N(-L6z9l3a(I6#L{~BjcxaM<*#p<<@!MM4vG<}s z>rR`FsnjC5j>&QpRiEpA`sLgDi=+evCDScACku>cQ%bX~&ani=Cr!3xBr0W+bUe3< zCwG&EWD>@8NXnixllhaCFiD=)KRHb@1*W2ZGU@M2e_MAu7H2F<=6@Ln^fk!{l0X9U zVzQf!=eK81;sq0}$Xxo{x6{%BTR~hTcPgzgv3#40B_*h3V%tROpWKQQOXd{2d^;A8 zHj!R-{gdmT$p7q8?bv%MpY%%38Bdy>X7h3^k)Afk#29lXve4w|Q}NtHdeO*-i+Y@n zP1G)VN-Te(a*|1rK9tx;C&`(L{SxVAx0)SGM4Kr6^k*)%io=F+Mm{c;wLATLGW%2B zq>|}>W!}g2K3H|Jlj%2E{SxKpyj^k%{|jXvMi+?v->ICp6Y-Sb|E=`%b4(qg@6hxU zSLTRk!t+GYl4AFa<`gm(Msvl-v@E;as91asvuBNY3Uk;sq<4|{%ACHA()-dx0vzc% z_+O$vvPQ9eDlpmkpMyQ#`I~+w`@pLQ4%_x{azw{T&p7>Z$VK0)CkR9zIX9c|y|w+{ z4++@goSE)0^~>}> zsC51MtW*>O&_OX!mS!X;kAN3cwhQ7?{VPfj3*1Qx3R44;ClPw4Qgq56s@jHOFeZd|F598I?m z@Je3Pk*?{!NPVZ{*CR93bkIl3o3CDqV%`Ty^r1W6h#tmUz^FvL@gh191(!;8PFxSN2w!9u*cjS*^)*o*$;?;2r1ES# z2a?L8cH^s<=wJ^o>M+qR(d}#O>?J!#g;+Y%YO!=|J7YhtDcxs>qr{1g4V_!1DVCUA z9_i--CH>l4GI?k%_LK?j7OkHP|7)BItIUSf=n&hF68TlMUC7Gp`R-_O@D*qOp!P6r z5=}_*>~`~pXTs;FLu#x)N6YhuXW>eZLl>=I;&P+qk>B$*EgY?%O&35$;{HM92J|>Y z{+F1}^tDR(elj}p6uyd*(jjw7fqH)QMQoH39p);&qEq8xnToCh`fkHmO0w!&Ri!F% zt%c`JM|`cebu^`4RfemOw*tOC6Z!L0UN|pY5srN=176auuS}JND<3v03zw^MRUD3% zm!^AI3M3^D)VD}SU*sYz1${0$_;f0zn~UZW;)@r&>JqFO>AD0oi39uG0npGj)j+M5 zxNNM=rYa~xH_;Dh)FIp0*0GeOeLTXXBQ(zh!lmIdv{hv|7;Xp$aOL9r%IM(Lt;O|p z-eVsbV0knHxzK|6IXqovKphB2%Ui2T6;TmghH^=~6uuQ5%!oG!dX_?p3$%Pi(;Wy0 zq@?{3X(~KX9$r=@bRSTSs5xH~>kEB#S?n!jN48{aD|3B~&B&G}9<85E2kVCCLps+z zw!Su(vrV0Gn}TC@9>wDN6?)-J>&#KKtr3WOW=MP9HGmtC)D1p^qN z8Zb@;Fp8i+jp2nVC#E;W%1dMy zt)DK^NlG%Nd(kUz5wb3tlTw--=yF7&?N~9@H+>La}xxopW{1 zjP0S(_A7;*$&VIby%AlPXgV5qY2@X3-4_|HWBp}*WWKj&eu~BR-vA_7nwCnX@rOoq z^6ls_U#FY3femBjd7=LT)a3nz@gc*{^_*yblRpvC%;{1k+MeZVO$4ZAJecxOp9RLXB^d( zXc6Xw4HJ_iDVKz|sQ8}b*dd#h1ezjxu~%T ztw+rH8fh8toRH4+=%Qes07kcneO^L#F=s>Z&+DjlrG2H%XiSX`(RMNRo7ip@x~_Go z1=WMked4umth_|$VMSU}G5o!cMmQs`zfg&WF7f%_M0xSg|Dx#>x;Gz(?~V3-D2dyetagIT%OQOt{>#VNAZb0PmC@% zwtv{Z6-~$fLZeLVIbk#%+azB9h@pf1_Ge1IT(m?Bf zTKi8Hcx^u!mD0PN$*Gh&UtK&EGJPtelyk>r$4eX%xIOGVXUs`QEcQBkKI%>%FHT#x zh8H{J*|UfvF;0|Dc@$PlX1!$3(iDpg+$v1ZYn|A24xi-2?l@!RFfRLeheyG;^gf<; za`9xu?q_T|ZVx&^nEd>JN@x1X*V$(GMlkw{u9G?aB1k9C7G2>v7(rq~AY1@1KmKyY+uv56Wf=#xCMBfOJ@o8c|xpAY8C1yw0h0O>0LUsbR#X=#%V~WR*&Aj<->>L z2N!&Ap&kqA8-#1YXT=bjUYC4dOKUIEOhH~QYL#ezY12)%?=kY&Lbj*Is0SNRYbj`6 ziWxA`zFS4;CezQH&`zeGp_@!UGqz2pACH2Q+t0|GOg}?6nSN%xoJ>DMH<|sE-nG#@ zGTFYCyx#PHc*n~$-H&Lk^vRqAh4=sT5M?1rPm z>@bZK{2kn4tSgIkAE{6q!qIdzXXaslH@W{Ae@U*NE&U=`XB|!&uEn{@f+XidSE-6{ zO1Ome3twdmka>{mk$hdkEXMy5>&NQ|J${Fsb6f1WDAPq#7|b~j()UkOS(lh9@wy|i zendxJ$JbIoK1Iz{tJQWR1d!~({mf`(VDxzy&#RN$kLRJu>5K|YpS-{AhwkPZjlQsn z<>CZ9Hqe;{e%=aa754dG^mzr_dGd7~uRABFW4lc52P|)LI=08;bf)G^pLl%XIZ?|~ z0nBH~&hKfoA`5c=jepL?^))^c4W0gMk7pF)vnG2jX0*4VO>d8WvFA;Fn7RF!+mTLK(g{GS{~7EwE^GC{ee`btTpw z_7zPPeO|)*OWME1?n9ki$=1IXzSTm_!nvvo^Z#@_&#(w%<-~n=G~JAFX21%UqPI*N za+XAK@)sArFUmzfqSN!7SOUR`&Rq2ELn1mN_hR+903-7Ptk=9@N{I~z@J(s_E0&UQ zBZ;NPqu@05I5DL{~A|Q-_U)KnP0s*BC9t;L8agfo%r*4T2iRP2ESR_9P=BjNd{#h1x`75tT9%Ixf<8(i zL@8@96dJU26o`e^kvIdt-zN#9QR#31KP7m*T9$0ELI)2G9y@q2q)Dl15MUdG&_ORW z3bPO(ey=w|7VIAdohBQByF_wA2g4%=jkHmeMAgt_@JRfL0L1ztJggV%22zTcDv$ye zemqtOC}LNLl8wEzqS6W~bZBs}KWvvXibCN92v7`v2S+plMG=IIk|Q~zs1LHZC?N0= zcnJmKIb^B;dRue|UVy+xB+vKx;4%~r_74mV^os^034ui)JV^d2IR}p&8yq}zh!qV{ zvMC3;kvd^5jdQ4HBT!C6G;|9^EUNn}^wB&lOlk@Qe(J~>JYh7TUKNZWx{tAdQSAoC z58#yuT+?t^yQt4gEmY*yp-3012PqwM+{O?`yE#%24SD+x_UR;45>=A+K}sJII0u6J zki!V^92}J#e`O#r41Kf!;vq6GRaT_2MbSeWBJG2PNtRYs$aD*70e;mlviWD&|S$v{GAp%I;oUQ10Bu>^$KmE$2-=tzN}#LaNe zsOHf%1wM+dFiPoJ5F5ZJ3vmEDs0P#x9pBeLWC+0l4Rmxcs4t3#pfDa0M}W150fuD` zz~D_YhCi^C#(2(=L6rwc(7p#Au9!@1JluvbeT2|s;D?r;WuQR=M&$;3mLc6_^k;Qw zMuR%KD`ALc-L#@I={59+P-U5!7?JtMS4mO(x_>qagwZf*+2;69@@95uf5=Y?O0s z1Wzs52=QnaLDk?94#exD6bWVk)~GMvBolNZ4v*5hk%w|GK{+;}eEC-2h}G~Is;2NW zXm-G~1?MM)kf!G_;^OX%7KAM&n8DkUiB69cK%l|FV`d!H>e!vuQo5#=7C}hBjQ((+ zP%s!GBa&`bpV5t6Erd(+_$w{a!N+Q#pONK;hzEgYcs;+PrszX5q%k66l^_v=pj4_H z$aN(Q4*+HG>aI- zc4e(~ga;iSgTTRKRF35o03+JpwH5eiA{IW8nXr00j=`o%% zGb(@PT=&eV{A{<;m+3Pz=cnlE8hb|S8?j%seda<#Z73o3FnPWK|yf?iC5bcd3pQu5OCu(Ur}Aq zpOslxBn_HZmrwhlysIlXA3XtE?2q4dcNgQB-Z>(7y?Mgg%}k0ELx2cF=Bq2(S5znB zkqG5Zk%G~|Y;knz!~;m#tKsSK6fN8`Yuv27va%a*EGx^)VtHO(%X+=sKB~CPjm8JL z$Rs@5?RJ+HGkwa;%)E;I@F#A6wI3e4&yinT2D}VG==Zzbq5<<4Q0Gc`sfV>ok(Mf;crhQSr%{4VO8v{l^Dt|xeQ^tkuLySIU`xvMC zP%!j5&yla|PXwX@;eCV$9Q>QXAod_uXL-EaL;DsnJ#|$I6@r(QRa6E;l@&%mEt+mp zURl|8YEA^^FWa`QrsnFa*RL0x>ZgO0uj{9U82vc=>f#hPrRe;iT^`lH*wH@ZiX5ao z5pU{u=baeu(2I=2>g*eD+#b=7T#uYk^dou@Bd|Q}FHZlnB#8cI$7uYon9Y@vBKf|2 z>4+@Oc^?Nt-r-Es@nZK{I3XvKX>=R!v&qN?rhYI5QAg;Bs<>=_BtLrr^z9>^ZGNVA zyGg}}e5pVBxAIfC@Jun*UKwXsR;Q_4`LT|dO>917E@9w8SF%CCxBLxjc`XPvY zC{^e0gJ2=Kay{~?6AIqGB3l1qcz}c7w~zGY`BQU{cw;|GpeDtv6$x6s_6Vxj101;K zXZujRp=Y6Za-r)Hv8}oIx)`zwTcpxA1 zKkLW)W$;JlcRG5+>P7sY^|jYCypwh4ul2FlBfP1!*9VN-^S{YY@k;r66Pk@Ffc4{i zd;Vt}`bOo?^ks|vB%g+0T|Xm#W>o&nxfyQuC#;{)k~1UgF{Zce&$zuFF!{!Qj63Y7 z^|#kcS_%7xS(rM|+;kKO)CxzjS{ubrQfCWAp% zo>?g{TX7kUNHiYeI7lQ>8spHfjOU-o_=6=KsrAgODR&(n8zqyGW<2^Sy8g(s~hb?^c zcxg;(@=f_1g@LJ@RQ3q%hxXG;1`!P_CO0q^TQ>g_z8EGuW?8D z!0qz0^0K1*9r~eue%&Hm|21XnufDpbW?R{FjF(`<+fSojHRE0kl+op*f?5FQ(|t|Z z<}Sle_}b|uXaI_luF>ls%*c-NIbZ9m{Xul61dXGt=>7+p(fViQRba@gsA%3w<(bBG z@JAG&d^l$mE=i8|*$39w@iFT`<(|g#ztJbEzoRlyxN!#XYkJeT2AA?lzRtH*VmzOm zOE178`IHne^Rd@QtU74^JXKnX&8iMmKMJP(d47d@m~FBh>kX5jiFtoDbFLZx8MpU; zjN9Ws&wuuOqJ=mH8LMGIUVi4%y7{vI)VObHjghZ?X|39?`RGQ?QS88{&&{jMn>#&{ zPrsa}gQQR?sLlj!%>B=vi1IJx{^dz0q7cP8goE0dS6R)vzvSF0vz-=c7G{^Ew@`D>HgPputD z&R=pcd46dydH#kYljNUFp1*k#`&Rps^H+zG=hsyx&%ec+JilR*`ZpX*&fgeFp8vtY z^ON&Gm)!oY z+m0mX&rHs*ei%y5|6*X0{N(m`H79R>*GpN+f#m#64TR)&8s@W@GxdtQ5&VFveq&ub%sOMidK&{YV%q2qkdYa1oqFOcU!&n$tj6-#=) zhVCOgPI+cA?wL8t`Lh(mbjeSb@%;N+Iel>}!-Y)me)%Zl*@GIopLtB+Ax{6L$XR@h z@pMT~?`M3LkKt;!Ko`TsM>KRlBl2Gx<@8^QoqqYI;3otQGki_-e=S9Dk^h>P@rze{O-~GLS;9Q>jdm=CERh{nsUY6vue(vw(GoDc-&@XT$!`FVtFmr%m z7MJgSN$Qay{8@r87C0x&^mCdS&hj#xc~amA!x@JdE@t`eBS$svK4S2Lf^TKIb430* z#{@sl`ClDne6@%1XZjd_Elc1Srym(+d{{ADEq0$9f>>uuzD|Nd2D%R)#Nex$dV$?stQn z{sKdfbwcBwoRbXav%c=9^O^2xFQ+f=7kE(M5a<6b>+k;Cu;85UzUQdmhXf7^JR;EW z9~YeUaR2Q|!ABY1qXhZ{S_1t70|J8r8w9or3<>NLX!H}k?l~su#{~`x6g}=$jNh9j zP|Cem%Dq?0y;sV;SIWIt%DXo#eEk9k1RfDMB=CelQ|>6^{a%Lm3<@rK^ydp+Dexe} zV@Cxa5qO;8eM)dCr~jCwi@f_R!Tk)6iC*^wBz>#Ekif9OLjnf{jtV@=@P3hdf4-!j zko2tr`vg`Bl=ALB#P~7M^Fbr$sL%}wl=|I2Ecl4PQHBqA1!f5pJsv0$e5*hy|3M$) z4=PC)c@K#E2c*0Qj!OQZz(WEL3hWcu%J9K_fd&`)2aiblF@YxpO1YmDJw7S?pY(D5 zCj$bv3KY4=M88iCNcxb#qXPQ{h6NrGDCK|B@DFqPG12Ffql`Z!dOQ>qT*`f@Pw+zm zj|vpI4~g7|MD9Z;IR8^#fkgrX0viPO2|Ot9kieq?MgB37`za~s*h$Gh!SL9K;D&CL z@k3dXZ*WV}iv)%Q9ujy|pwNHX$M~lM0)qlu1&SV@J}CHrz!MBVqXY`yXN2!FLjRdT z$v+}+NZ_zQ(c@v^e^~ej4Bs)PdpIQUpz!$x7kwU3h!Q#}|Fb^EKbtQwAbcl<&Jrl;kF*M%fkT2z`JWwR z{BuJ8xdz4uiUd{)+$vBBJSb4~{d_*-pBMViA7cCqBIgTI-sk<2Zulhs^T&jLRPsg7 zFL(u){70qyN3%HnQIYqkl>2B%(hmt7WcZj*pwK@S6nsGFjthR2;a83bG`Q&Vn1Q0t zW5Z1MxDqIOne*4j1ClP~J>DRAtH3^i2L&Et_!W`=718Sjk^h31^Iz}@l=5D%1kV@f z7g!`v^mw6?@yCxa{G!PFqUiCZR!)C(Sl|hPCm9Y3{oqzf9}zez`3(X^&Yx48PJUxa2>?<$GrNb-HI3 z%k^XkoLedBTo2FOVU4TVyiWAY&Ex!4zhIbtjN!a2hI2*!+>?wC%l>VS;qMoEpN3`~ z>&a-)xbhuhI6I%|W@R!y>!^nAkdky>*Lqfa8J~NCVa8U5nMRK6vt}*l^u;X4v-mmT z3*>L!vT@@QJg#55v5t2CTQ*;_x%&Fex#lDwSW#YIURhdOTUD!*2oF|mQUo_uSFAJ7 z0BX9@^2&9>NO)ziRMVAL2WzU!IfK%xsw)J;KUM&*tFF+nwyv~nqX?k%&DT|LEUl~3 zNrabHRMcNry0MC$JJoVF*VffI%Ck%1{IZSJ<=52LZKMazbUBC-+_aAM57u7K<=#+R zw{BDYrqUqO)mE>ss}ELRzYfBQzPzTiwz580vrh8C&%`2k-8z)Ki4`IG&DXK~+I8zH z>etoOREwM{JlR9}Wi{2M73HP1b@estYS(GA>-3GKo9khNx~l5U^}3QGA67*@YDzb6 zSjUYZ>6@>suU%J8b614NfA9S7e*KYKzLEOb6Wczs=`;(uq{uzJU37w;G!nz8(zXRiL6)~)Bv`T5MkSo)M? zbivvcx(c=HYN|>%)-NwyeTCm&SX9{2+0jB78)`~}l~v`n`ODGy5QJ_;EZw#1Hfh6M zbKNHX)6`0}^fWDNDO%wdRO4+snhQEZT`fD>I{6tyWD*^vZ)@(}SwDow$9F;g6)lM?FD-p+j>|srFC{Sw{%HC+uAz23wE@0?riDl+KaR@ zvzl9Ww>7m`et)Z#zrMXQ6xzEaUVahf{({z6FX(qQ+~>YpVn=4{p&hj3w1IH`tsG5-n+CX@teb9ddQPgGu8XgQi%;I0hCP zj1gpfw**z|Hr7<%&}4EI<3%y}iWuB);~2$is%b=xss-L_rPEC*iuKsAeyicsdZCkS zs%ASF{F<@~O``GAEfrN_)gZ>EMWLWdr^y5Mx0=u@F#asi6mbhiCqzit?R25E)2SStSkT_tXm?j#es@b(Q)fp(sI#j_?cUMY)w#2KS@(`c zDxh#%8xIxBTH3oh_ZEbj+FCoi(f5}%w{>*ds6s};Hq>*6RCJN9ol;8|_30`tThtw( z=p`Bh|0MxGK8yNtC8wZ(1pfhg-b*~^CvTig@3msO^ke$UqO~8d+I+CJRzN! z5Zb%jEc7c2O`v_Or8^XbZ)(}mSW}9LqPDcw%11Ax+$Gk=3gOrp>WQNF(TnmBeE9Di zf=~}h%0=r-tF5E0r>(K4rP#mkYY0UBp>Kge?7|yIk{*`@8xQceqstVEqE=oQ) zOvyDZyL#KYsBVpzsJEdmR!^tZ*woXu8xCXjw6<87s-<#}lOBXObX)6c%dPF5UD}2g zjn2kx^f=tzRlmKZv8T7Ir5={*>FR9f@sP(>9%2g`JDOTM&GIHrz8+O&w2QR2@~>Rx zU%qUGfB8ynHdSA-sj42)+MdR)9;;eULwc~NZEB^HX4uBoRYmpm-M5zqfl^K~LMx7OeYrH1_a<&Nv?Bla=+9hFf=cqaNC$({F8| zH6DfNbbEbR+tS|B)Wi1Y_1~4c<1T6{L>2Kqw4Oq0IIFy~V|&|<-Y&h6f)aJqpj}va zp{oY5yh0=Hv^I4%_hQ|b4~8XHTQ^*x6&|{$u?uTKUR%QPds>mRlY4t%dQ}fr0v%Rs zOMA$IY1_8%)xOf!(bKY{tF31*t=YQwqL`glcjxvVlBHdQbb=gOaqj8tx|JNSsk5sK zb!p#AUY1S@HuWNtyB0cCx5HIaZ)nV@ET)af{~Acyo(! zY#C+f|Lm)2UE1B+7DA=Ft!*tWsA}V_Egf7?Y(6ROq_M10s~5wgwg&O>N>$6V6;DSw zb&a~VdKny(ZI_HmT3149343hr>}k2us_TU5Asj`KJ+KC|w)b|Rmv(kEw!_>@BPP;| z>d0~$Hi+0kcjENY-B|Qf8M0wZ zQ)^?#ju!GbtoGXU{M2Q&kwcL)W8A_7*o}b?ZjBDHZLhVn4RyG+WvPYzoQ!ilo#|#| zgl|TA0Q(*Jb8}}4dvt3f767K!JZ5U|vbuYlnjmg_Z+rV*tBKTYX)a8^p%cZop(#6B zP&kHN-U2N%PHJjuHSTC^!|X{FtHv?`tG3b(3(H>&z@1IHvml8U(@wV@-Qe7;B=jtY%abZN zL$Q{xZdFMqc^UMtgva(^WFg~rHHNH(i`sjaT8%wqs`kDs^?j*TRaHY9kM=%9t81*r z=H@QOnLspH)TGT7xovBQ4QZzwYHvjEQ|Xa1c_zYn%A#h}y0ZzxasHzAX0u1K5QkmN zZm7M_Dy`X|cS(q1wJ0SZaV4uk`Fgv=qH0T!yn)Vu zuF*mY7Idct8n(YMb6-R&Y%vYd#!YEQ`wFVN6kabuC$lgDt14r^#m5bfM;^`ew2gNQnm%MUXqgb^# zN=9uX3ffUHM^c@UILXc=8F6&ci@M;3WSxTCn}wb~bkH zU)wu-JFsHJINsaSNg1>j+Su07+lN(IS4T^`wFje1t6t6OgGjxYrSXF{`rva~(IMEe zpRz0I$5M>;kQ7W;z&Q{0fSYK=>~ya898ZUlIh1$q+)100M4_UkyQ!;9Z|EaDQd}Yy zr)m+NvTAJEZVJ{^?ZfHTzG7;awYNAVl+l@oJ{Rl8cQb7``Q_G&2&i0#J?>4#v|Cua zuegcTc8I{nlNN!z7p6lIy{`Zi(ug2F({dCY6UEf7Pcdk>%X{Yt>$MGrscRDWvzzwS#6mW@lx8&?rlN!?GBAr*8ZU@p(m6pR_dw1Oz62+Psu{K{<|y zTH2SCkVy0xr-MqjXpD>u<}fOwmREw%8)1s&937w+#X^5gqQoX3cKl19;~%$1j4mX0G) zi^yrasO^f|_^E%>QGx-%@p^y;HU1Y$NA%)F1J&EN6Qb5P;%gVcI`|ghRr)BnJ@SQQ zZEJ5&Ga8EWC|uvz)d4f8nqE%Ap53ZOw6?NzIpqVcxExDwz{`rTIs&|WIo6Z4n=UJ$ z3`*A#B1X`-rgY$%gYg|rO;=j^8_KarSbkaIio)fXx>sE0U$vsh%CA9Bti*P$ysHyi zan3GSzQn?JC%rvw?cG;K7LqFpSNaPsE8O06*=iQ6x?A-3EONNK{PKcT{(>Eim#-+= z?q5zztWaZDTX$3a4lJ{9)}<6JdF%0&Mg6wkw)SQ$=jbcd?)r9Y8MH)vXVVyJYpU0$ zlJ!`E_l9&jf4NyN;)D%3x3;u|>aog12{>K?qn)MsbWb;IrfVUmr?aUZn_{fmd3{Q2 zOH!*1`!M>(N8{alG{jeMrXG|`t6p<#Xwus{k%4dg@l_{3k0FXW)7RD3MeN~7FR$Lb zzKS0vpmNz>j{I8v#pc9xdRieu&&ywEuPYr_4n3WL?@`uWSKroM-_g=T$AV%lJ6+_k z5gqj1_>vsG(5Aof&b^=R_~~yyfA59Qz53#|Z@e;&K=frFUv}sx`CnbIYgNU43xD78 zy=RC1^8Md!x@y^fJp1#<@A%lypId$1zufdr<+DXM{9(=?{vq}F`S+LH`PkFvS3mq@ z_6L6avz>QjotXB-p+`OLvbi6=$+a%j`F!5;k37Hm&9B|PW#)(e^~dASyx#TvEj@p{ z{bQZsU;f>xtImDs?;d_N?MsWkGxzduJp6~A@3&q2;F}M(edDu#xcY~`e}2P9i>epB zG5Z^jeESo3|M=OwnV)}Y&r^q63jSkh=M9&-d#~=f>xqspzWL#sk9_&ne=hyYk9Pm} z!JmD2PVKXY)?Qol_V(AF99;hGodwUV@E!3iK6dCYxh4PWh6CSu{`VPgetUSvJ8K5- z{g)qq>AnpQqj|Nga>^zOP(Jl*um-5*TsS3aGV zx25IFzIQV&|H$>btV?_!90;xW`VWqt|JXOqJ^I7r$L6nU_g-82WbNY0JsV%U=eM6; zdE~igfAw(X;Ogv|fB&r)Zfx3o;)$!@DE;_%Z#w*9^`53Tx?2l7-+AS0M<4CE_s+L( z`Tf_%`W|oUEI)5q!(GSvGCx1^WY^u_aeFUlUj4iN=g0rNuxWn&W5M>{{_@U${*UMX z`_cdXwQuh|=i9sb{`f{$?WHd+UihJQQ%Pamp2 z@OH-!`qD;rJkfPt=Rc)4edt?XICkiZb4m`SJpbRbzIx%V8JEw?-f&_5&-eUkVakW@ zTJhqOw+FtH_dxMypHDyMh3B6uzi7ud5Bp|q-1w~@J^$UcW&d!)fhC8>TJ~R3|J}|f zuYBdtziY2J|L&_dY(0%jeL##jC_pzi+qdxihPRviF}FthFG0O5L~|EvXz%#v8rfwRV_0rEcD|G?l4fq_k^XCpIm6`2L z)%lr-TlfTQd6|{K8yt8m@W&kZAn-RG_(`SCap-pr^gE}{&Oc`>@DpCQE)V*hGpy9P z^5R>Ca4yo$b=2ov)aTrTHvhT(z#ZkCi}KEMly@G=JFmi~Kd%zFga176pV#lmKLq@! z10TX0{r$FmvWkHBJ8;lvodmtGWR2kW8!PPkoL{Nbg#qtOoj(_G;1~J5GYozaeg5POQl0w*tVA;f0-) zgz_!q=MDl9lH4QsrKmicenGxc3*Yt5()p0Ta1?L9a_Ex>eezb?^m))f&!K-F^v`p& zN8U+=Uof)i7a_VR%Yj?K9saOrEASyl{xK{zM{Iw*82Vg1X6G+PwH7<{Uz`tosUsiy z=09fJD}PX_C4F9xRtff862|Aa*bntxasp2fd{gW-AMs=OILf;e-9M)LCe`mMfnh{dV&Zl<@uFbdWv(^v1 z!GX5|Kjgp%lqw0^{#YK(oxf!O_z9bT%dk?{IqY}cLF^D4KrbxUw<@*uDe+$m@gw*+__u=p21k3|0Qoms z!mJR_SL&uZ=?~E7rmc8)?+LrV+zkF(9R2qeuzuhfyL~=zRH^!>#NM!HJ<4yWwB6MN+$jyiwPA^(Gre`~zVz~cfZ5`_6LEt+w}WkfBZgPWPCW_1zu^F zcL4O^Y`eTL$`2p2+beursXOv*`aAr<`y6-}_>XLR-El&x54YO#K8*4|)@s}PV}ysG zgoKa5zMmMh$G1-$0q&^pCs5ye58D0p-hTXQ#xu73{-a9WUnlL2@$vqxN<9#k_C$L> za8RiSXWIHc=mYNX-v>eeP`ge45cK=hR(rhqRD)87n(guNP^(g(E)pE`+ouCcedeUi z|Cte`9_g|1M^L{<4%_YV$beFxdr<7vh4>+*1~LUle;LS9>d|+-8QLG=Z;y^D^;m_? z|5&9`kLTI-e>@-fNjv{>l>fzzHvYvR@L{`szIYNZ%x<>z8$^A+++h31mw`XgW83SA zKH!e={fQx^o?L3nd(sd5Zo9lE`+>jfz@h(BAzR*4kar}-*8hlCsb_|4|9Iw@QqN`! zKgNe=Ev5e9DJhTQN0j=pgb;zK-(0@jF?6 z7a~5Q)W7E2{{F9i;4caNO2onc&2}6AW=N@T1%w{?->OvV+gUdLZRq%Iv%bkf9P(c> z{tMk+>R0OfeOzA`XuluEZ<_Us|APMbA*Fs`=4<%F4+wY6Z$B7T>W7Z`;)l@ZhlgN9 z2|pYFZpLHS?}sOq`jN3O#{VC|z9&X`zu~f!`YHH+R>Atau>SiQ>hrTP+g?9M{_vRi zBQ~qQ0{*KbcKiM6s8X-LYuoSjQKjCzOX>spZyv-OVTXl(1>(@>H-4M{H$_VQ)-m7z z7W8i)7Jv8x;sZ+ke%Pk}J@`+BBp>Z}st+H>czo)Za=G_O{sP3q_>meT?_$IU@Ui{V zHG33~Egy{dtIOA~Tyyeld`>=a+y7m2hLmfry`FQ;J)&Imyf*zjAMjwr|L|Tn;AXy} zygud1d@j=eT$zw}j-$MDP~L?bWjutw7Y3DUex20ELLBmPUbW@t3@caei1ddG5J!C$ znDyhih@VuhTZ)B$BjN#k`fPn~2`g8lncu+Qi1N4D^R=rn;Mj> zd1a*i@g6$h2P5MH-pi(3dn#r8hWtIK&uvdh|I0@Fh;ls~5dFaW+>mm;X!KoycmN;M zK3RxE-i!A7#PuTT_fn>9&zC^+lD*$>y<{oZD=&$DDF2n?`25HVFV?~J$_d~n9Qd$u z{bZ(Hzn}Pk7uonQ^m{$urhnZJyvTt=-#3ri_?w`A%bu@YZ$ZDeEIa=#*!wMeedT%! z^4@aj^A_~^b&5^@YcKGb5ghMLRro}3*Ta>{b+|%slZ#p`xu&%`vz0rQv2 zrt@liv2&D@T-Xl5M>$};P{o}8qeGlDtx}cE+dap6%eQdS^^sc4hS3)ILxps>R>RU?AgfwYZgGqJsbXivVgX| z{GVsNUz8Zfs7g`pr?*JI4Gu7#AJ))yvLP(=4D4ofr0a3k&WU2s$baKL>cgjKVxBW) z%#j~lcM7>^wXkqCt~Br!_LG><&Qe4m730W{|di9 z!TptC!TJ3S?yrn6{#7NA-`n8+YL?&@i5nl~)-}419}nqL#*gc4Wy&W;j8wNO@-xOFN3$>Wz2spxgTl3FOV5bIq+c&$;>{vwpfVwiw>oq=acao9VR{;`y%a3=#fBPtR7Vk6?D`cCjYqW4C)bAUaTI@bN2sfdZgo- z0d0>n(Qjejc=D!V5AfZ7hV_^rFIJE8IQ{`Wdd{#O6XeC}u_=xoA3u}h82kN%aWPhp z1pVdXdf@ok-1k3zupSfS#p=931tR8X3bsBd*qV4gY7r%dd@H{s`UaTH9u{Gp#&Z5hgnSapN1h)(^T?!k@X431E zJzY=sP?Arn5y|oj{8|jebm76reJj%S8TGjSy;A*MWaI?&zOcn?_Ub3V3Fe4Yn~A@{ zM8@A4dM2WKfTIlzFnvpe^_#WOq{l)-mnhxjiySMK&ftd5;0Bs>4e9-CykCDu&RgD< z^MQAZ48!R_`u{?a+aOTR2i`eyI`Rz9sed!)BY%|hmUk*ee!ihO9eC>B!lLgV<-FyO zLoD}?1E(X;@SOU$A(8tBIdA!6k;u(5G^Ybk{lG19M~pY@ z;Bm9w9{1pB2^1#!zf#dJAn=I5{`VvIEbmzhoVCDN3!JsUSqq%Cz*!5t7Yj@u$|Kw{pnobe&qw|S<^ zJ}{m4ub%YxwSOHSjmYKwpl3So2R+mKwce+Bqv!cb=sd^c<$bYdI`4};(> zNs;RpI3O^5I`Rz9IQ*OCOdAw=S%Skt$~}I3NKqTS*FeuRiX2N|zrfIYmF1)#Ki(j6 z(+-I|FT*tIFQ*;oxl57b6BriQaN0t_NY8PyoYaE?M+KfhHtg#IjsHCtF@8K?a)H5Cjtq(X@CgOrM zpvN9XYmzgk2mZr3NuaVgSSF&39<=57}tfBK3?Xg!Ug2 zB6ObPq(_fQfB%s6NGiwtpB#vc9&YG?cH!r7PW<_dwM)`-MDu@p4n5$fq{p4=(@6YeE+uITO>VAw0LoA>AL9!t^# z40jtmEa?rBZt@#q3*h{S{2|6e!rvo&p#hPj81^&>->}dRG3*^yD*RErYmLk~PGxa= zx1{g##ZO<76`fA=iI#(Y?s8FHiMB(x#p%1|#HJGsh3NEN$nj<3hUe?OTBi~%rx$W4 zo#~ue;cj4QY6u%?ANYVppmN(!?4@i20 zn(MP1>6H}MVX%ebO-S5_}CpbhTtrE z)&gfOU|YaV-$ml3RXW71{HT9Xh#qfuhcO<4t^7XDGyOMYv5-mbZk11uMz~xkH>fZr zqHKyG4d19zY%V3}SRQ}}1o`@VQ@4&8UP*h}y~sWH!twV1n(eJtG5W`9Qo{4>5X0^U zlP`FH@e~yP-a^ui-4sO4n*z;QlK*)YV87X86iZ?JSv}Wvj-UFI-~k~>6+B zn}xcWyJkpw7Qu#?ISqJ}p8}V8^w?2EDa>aOxt|k>am2&r-_{V5e#X(^Wy8xpsUB0q zvv8$8sOWQ~U&jf5sBpht4dRSwA!9PBm-ItV`+t#WK9O z4rGkXo<3VWs2(5j?ebmgo8wb+_RP6{&Utgx+?KiP=BCb7^Sb8M%$qw;W%gv&WoBlo zbM~Hd!#U@lqt5L;cgwlwoU6`jJFn`z>F24e&aCRJ*;(rR(D}jhedntSZo1(13og81 z+v7gjFxE>w9R%)2@-Jx?vVbUv_y3-UA$f77FDm>)Eadi{Pu%*Yy0)8QQe9S>{hi+wX1-- zK{ctJs#M*GO;m>}Q#Yv=)v3zW%~-sJRE4@l?NGan*a+O9HmsuFR;enB{uZ!GRjs}Y z@alrA3+_=@k6fd!S=kMIquO}+y?~q4rYm{?H($E>(tfphq*_%kr`XjmV6Cbxd;l;uTzB(z`zY-r>TlG?)F;$O)j=G*=>2_~(7OD~3;oL(Im2o0 zFRN59NiIxrx&D$d3sWa1PdywUl4C}J4maYn3A>nbahvYe9UO?~|0q|UC&T+nZeA|J zWos7XF3incux4RyKH&&R&s(@`!QurAm*wUmcTFxnxaWZok&*?#b92|^Azg>OH4Dqu ztVPy>HMxs(J=qpA7UV8ii%gQeC^zSQU5norcMepFE`L$(LO(0MI5%(28tCnrd$lLs z{eN68PF}QTjc3Moim(5ld-Ke?I(PY^T+b{kk6OYr-(uxqcpbsr@wZ?NDyrLC=U%2s z(3;F6oNJi%;t~+ASyNF$ExL#jSt6QTC>BN7Ob!w}Gc3wqfcAE~Xi2Bk6;pE7`iQeG zCnnrnWs8~9(;3&z&DqIyAg{;g7JQoUX~$e4<- zC6&?bdFmxEdT{$Uy1h!bH|h2pEFjb7;FdZAH}x*v#&L6{;%wG6i*A{qb6td+dl$tn zrkkH`tB9@~ecu(N+l?s0)ktZb#MVc*JL&cbBK{=ZK1aDv(CzPt<2{h zQ;NMovER_`9TG5xn41)G&%nadeJ-IE=-ijm?Fzb;Qd$+oYUox^x1Ese?xT$Tbo(f! z-A8GkBJ?prpP<-3QtV$b;J9Bx72H3fb@|Wf_AAQx9Y|7hi0)5x`#)H-c<>5m<(W;l zENJCf06I@T-ImdspP?Hmcw>PLul$1&@Xi@-Kyz!J>42IaQh(Ly6Co#Zhu3!zopwlbbE|$Ptolt-Tsws~ojMzN zY3GA?UWz--^MWb_^Z6vpvj^Fp=~k-Cvj>SfR-NXtt|sZo_d&{Xz}czu7Nxo_l>4-s zQe79NrZ3b*OotLE`TVV^^Q_dFR_b&VI|~WZ({4zesi8kL9WXWRLg4cNSE95VkdT?` zMf!|osWVC8v}L4E-qlwZgVl3OVOnA89HhI`3iKTUwZi#SaZt`FO!el1a+xkHH4W;) zGU;inq;O83p5{-rr`x#$=L>VH_dd-#n-t7Vot*41)Eh0U zIL-5O7a1*efeVVy1A!~mOD0~MI!D(HG5}p`Qaov%Kkg&a%z4O~gDU5yrmodWxk%ro zM3xq5*UVN=rk02rrOwC&xrK^Twa9j}tkm;i)IzW?OD#yl#ykaTxGqL*p#%&NOipo0B>lhJbq7J3)L- zPU;LU{yehCrKxikK|Cotr*LbkYdI6HgV1YGw5~#$D^a5lLB>xdFP_n# zlA7ijy`9P?b9r3z=8=~0|7#f1eGT1m>9&Awmtdt202%`qpqR`w&v&@dS!GwMpQy-3 za2TI-8eO^1jUCBK#$CnwExT8o50s~Rrmfr+IJu8+8VBOuLj#1?v>=(zLF(p z!jkO-NHlvrQ=X|x?oxAh{NH_V)->tHY=^x5Pt8vjOQKUQ!DB$9X(|J-^(DM3StLhdx21j|zX^G3#s~xICM+8V8CR zfdE{=ak(eb*$5dFt<^82t1)VT5fQU-`ZBEKosf)XsWT5Bz0UBWIuM5Gn$KiSKlCIQ zrho;Tzh!t;&BHHg%IQHh{I+IX6rc*{U>Y|vA)RrHLG`87N#Xsi!mn9g*oxwkEwzR3 zoXBkTx7FhV?7{YWoUn^uUVK7>8^iZey*g8&mtH5p>RFv(2y50-(VJp#3QT5oNXUy$96q^ihSco@m;$H-c21@jMMF+;*K0Hn27zsP>H2Vblva z*aIQV#_$z`o|n*9weY*GFYZ|(K0HZ3Z(r=^n}1F}KWF_E2NFLTY5Ih`V@Zeq{DePy zi*;CY5(3ZkE4CND!08$iYj^;r!LL?tm>3ek^mH}#u2a!G9IF?gjiF~&%r_ymi`_+`8imr+szigVvCypRU#Tk(@3>AY zvdYqEoMmbJiY<-pUoMu$#lm)A_;{Z`n^qq5F%Vni;j_?ECio$#+!L0SOQqVv2=W!< zX7={#C+w&v{C@b7Cw<{vEHN+eS>{{!^|;kOK#Caj3dAH@^_=@(U>Ur_&3~x2e-(i>ro&eJIg(ubHIjuq>10lQS?ZsoCK z|2KF%!@bE-eaK9$3VaRRR&?^I|3N3$YOWV?pRT}hykp(!*wcO1Lg-$^_dmZkzW+bR z_W5QVd@pt9;kT}v1qVAO;y{B1!8&xwS7U;?BIY4-j_9W8^mBIE9cCOj+Ju(%5sFfi z#M1$OE3l)lMWwXuw@2ZAfr|m8l!&y08hGLOKw$sZ45> zs_vnRRX-$B^01;sN>I%?a5O~nmc+F+Zk>qPQ0%n79SCp3{_^6u6{@E}<=d$OQMoZc zPkmcW*cM`B2cz++q?wqo`vdiIdHtKtUjLrBX?gwV1gd&A_RSgIAu4A(X7ei`GZKVQ zy~eF`JEU=x6Qrzz3r3~-HNZyf=QG5}_m@lp5w|ySwP}nm<6%B+ax|6n)?+&}gItq` ztGK3Y(`QkI5LhLvZ*U>OBx@Q)%_&s`$d6uSLYn0I_Cs+3HH`awN8X%;tLfWFX&67h z4O+)mL0y*1_o6V0yW|k?^$qk}CeKq_J+*_=x%1HRQKYSf_ZH0HmQIvLx66J7f2HWn5E*C8p;nSHVvJkhx zb!A=~ZltT?iOWP|MQ4zFu7(D5jDyDG=y=1!X`KzzHJ=%++#crD7V#Wi=#ZdO4c9O4 zZ)f<)3B4$X=OH}w#Ln=~C(zgp!r|%Y$k5d@Ul;d9dBI~n3XiJC^1c)t0P1HavZ@If zZQ*di+>HF zdyq^-(s+%?u9vMRbfTx!t2ML9W*#7AXzg*VF0O*HSNz?2ZCK}jC^Ey)hioMfH!V%9 zRoXrQB}7-lxDZlL(42L@p8sDp65P@t(3RroQ7uET#ulTmhC<6I=R2xKYTynsz$BA^ z*4H}g4i=7c)w+ldDxax}{cc&PN9&ge1MD&j7!e@!SF6dGLtjO!c-1V4G=_Ul2n60+ zJwTFrA95rb7=V*jn}@%;hRGAcqt3s!WDmpluF=f1rjk}OIzVSp%=Op*#PXCet!x}7p>@Sf$bQ01SfSX_z!t2JOs({frqxTE3E4CN zkdWUdq(V`jJ(X;OAThwQ1_Lb|Hsqp(yr>)Fo7L@lwl4J&a*cCFh0IHdR^41wYtKgFWMjGb_t^!Im6kUP7Vd_4=%f7G|5ZCnO$0#bL z(h)3NxrmTHB$3BeP0TZH>JSSl1CXIO2&8C2_Qt_6E@il&FapPPT(7asn*pV5J`P@> zq8rWH)^mVM2@mR^I8;xpHtI4cR$QWfUi2qqjp{k>5<9r-20W7LG0@%?4ZnuY|iVQ6)6Fm+ke6j{TJ#W3I`cc;?R9mNRu zo%x{nu2tfwl`y-VK(cqgjl}r+v{20$58YdbNEONpab36qWO)KN66K_I`5S)qCgz)% z3E4aJMl%!2^O_q~J_fU9u?ngtUDQp;V0muSqtldV7|Lb_+9r7Ma)}ZEQ+Uvg32tQR zUqCUY77I%T210|0Om$epE1UTZFFI!YbTIFaUC2X2GHK7!J^I>pX^an#UF%|8Z-4Xk z^p(bhX|WN@CRF5g`Y~qMCHRVR$$U@@z?da1Ebn=}sU;Mi+Vrw~Q_d&07dNE=B$4ph zx%H=5*@A9`fLg}Oo6%caWhpRgmeNG z*#eCh?zAROGP8SK`^&>Kn=ZMe5=K`R#hrZ`C(}FbN@Xq;yN%j_=U0rtYJ$4sYN#B` z%rS9Fh~ok&CM%F~pJdR8eUBTH=-!$gQFnpG2olt8Bb7;U=*SO?yzS-PI`a*v|EwU! z(cstQny*VBRKp^uLIwTZ#$T#ZQLShH)hD_ye#h)yvM>m2w#3Walo|$w3WLphcto8y z0#n-~Y!;+ciYgAL$9dbf{xlzx|HXcH{1@xbeZVI?(bjzEpDhgvk#0LG{O1aikE>xM zmTgj{M}arls0nJ{#9K<~@Vetr@qEstw67z8^P#QMd`?Vz(6p~CycQ9DwND&WLF736 zK9-F{ja5@Q+;v=ZdTn^W)vSAoh<4wk0QBllREx`-e|eUsWIoBl8_sO1H+;ecPG%x!ki{4)6F5JXTEaV}1?(1y5VQ6J79o?4DId=e}e?51c_{Dl{(sTqn_$q*?+UfY<*t#=Nnr?H0HRSnS3m*h4H#u48oqNOf@{wc^7>YI%GSTrcw;#k&Md=Lmyda5DA$9f*`PwZ{}cxnr)uuovT z9FMh!>GrC57r$4jDGfz1wxOJ(p;TM_r^VVqqz-!bxTg17R+9DG$YI z7f`3ET8lvMW~w>X;d&gNi~Za`L@Q2K8S$uoj2E#{hif%?0s^Zl83`8|*ly#cm_rt@ zxUtR>Y%y%J<_vrRK`>n*fC{>C5EpR3Gdw8U8dpcs6s5YCsED@=<9HPC`lbt z5s6Hqb)P?wy?YF`yR{OuI&r^CbQ4|c6n-bsZ3ox*`OKPpI%b#7W63D`iTEl>8_hkx zRDc#jZKIbDW{_^m7@UslwnY7f3|tfkN8-8%$H57^-H9l9wZO80)!~;LTEAsvj$t~f zUJ7J4-K%3l*rH=*8^(7Wc4U&Z?gPQX#h&r&(K)isIDxWtEqSOPCCedS6@{8N?4w> zfhpKx6MB&@Yy^H4h(im+MyXW%&ukD^y^vG?a%sj+-rU6tz>3(-06g z`;2q1g+Xwg6coj7RGX#YJZ#bG!YF6XRtAm%W02e5q=FhmAKMO|f3SH_LU@Oe7fV>3 zz$M5;2ec?79zv7%lAyNx)MjTaU#Q@d@(h20?)rKZI)Z2IK2@ebb=UDrLTHe6ohk46 zw#o0lboFWnfiwJ$#r4}F>WolfD>w&Va%q+cda;S=v`Nqcv}(HtEj$A3lP{`kgOgE| zBPuEb{szAt&HA|#ev8tN;Uj3(hEJVrdR+(1(^h`%i)`5n?Tbia3K8Mag7a_ z!0}Lq!!M-1nK2?P8R~y3v_ze%nzpX1PV&OK{n5cc+k z_gy!?^_P0V1)Pu zK@U?x6Ug03>)W^%6z{}iwZX0z;b@2e?)KZtTQb@M%W%mfiX4~TRWy1AGJVH}dvh_| z@7QqP!>uFKr-`u;M!nO$Vdo*v(p@Ord3f2y0N3Kwwvl6qyUWRv3+|1o&Gv^11beuq z9dP$)#nj~fW-s3ucHFl5tRc;anAJa+22C0udZprz_;gX>ZUCu)2k$3^brr0ql$pQxAF;4FiM7;{*^5HX7VI2a+`Anun z#hc0FV>Z*h78-Tu;%8Pke3H*P^EpcO7-fA1vH*-ObV^}LJ_b};XE9gx877Q;RXv4^|sH)Dr0QlH6v(6ypzZYVnj0M zwdF_Qu9I<-xn<43_fX30P#KA$8kLNeLjvsYUZeiRGx&OkO?wZ*1}mA-E1quBHNH{J zM~?*wqABf<)s1u%U8GZUl!=LVCq2b&HKTKi#iD(eCSsk?r>-#Gr1^-(hLk~g7atB& z2F(m(9ia{l@y*3!1{HkK;<^-Jt?Gzo8Qco6rx4zswxdCz=HAGJq(0~25AiYHZ8 z!d%9Be;_l=`z*|v2^Yu_F|fG4->`gr-^2ReMnKLi3;L_pi8r#iN30pA0Qa!uB_n{f zvxzg_R?>j01Da)-@73Hn02(iECtCuzI=cJ{9np-pouRnuSkyF&IaMv1acWswBkRj_ zQ+sCbX}ncd3ZONwKl!{IJ|YS|hgg)cZ~Z2~ISEj$OdfzfUH;gL=4k;sC*18Iop@yz3<7x2E6lv z3H1u~`dGjc68jkF1(XuJu%g%{vUdJBL{*T2SRMjiiXqh^La2#ldH8dJJ)LAKs#!ek zzU4{tCR5!R_&z3df1F|anDta#ou%%WjS*r5Tfq`sA9GYcl2%VStP-jpOSBlQ(m3#{ ztTn1e|4d+fR4VR>yh)h8kYvLE3FEFA>tVB6dvx8eCYE0@_q+xk2xw|TE+{bQ58z0$ z0ComN>X=OhjUz&$q&KHaU4ePwh&{8bIvV=hkH%d&Jg1%?$dBYwRei(}kpdQwY1`8sbZa?eEs& zdYqXXifb?xShN8ZfeveO9Kd!jINE)rV9dvf+12>5Env#1tp`CpDn{Hlz$E;wih#L_ z@4)D&wMiw?^c1?m!aCey>J_T$t!9&1+{#rMGe>sCd^5wDkT9#;EPmY$2CSfNRpr_% zrZOOpQISbD>tpNhRs?=Vb>Xbq+YT)E$$X%g(Har?wbF>D?x<-SF|0l!AYEOriVb6q zI0FjvOCI!9Yrrv$e+^NAK%oKmMN0)gu5LmIZCx>X;xVpD^jB!@z|hnxv*eM=#A;YAG-M@S zX|-<--=p?kgiB+jFJl_d!{)O`!yb+2HHs@m1eU<63tUoDRTB~ln2}^Jn@uK!e|EKa z12TZPbQ|ivtn=p(Y3))pQoh+vOaPk)=tQrZ>Bl;JqeZE%3;%-|WEMY#A!Gka?u&xspSujZELL;+wp3S#qA?;L!BOt z#R13FO4m_X&?2x@WN|t z2}|ZQ!elB{x@vz35W$JrUs@eLc07YNm3WtsU#_^g{l;0vq~dliZyQdQ+y!WDVI>Uwxf0COpY^BvfG4 z;b|Hk3!E!abw!7&MX=QdW~5+&8SVu&to>ckscl%V+xGa zN~8s%R`qGyX8x2^I487=349ZvmWJn=@s;L*o-!?JO2zXhn3CqijdrA}sAxYg+43Q` zx8y!{CD8vF`{JSgQmrEf>MsppNyXbD6DMtcQAQIi%V~Y{s4I@J4Q_MnOv1XYQd>PE z7n%Vze>rW{aEC)hL|+W-&>eV{cordys<;=f3lYe})92$xbw+NfB}Bwftv@_5{dM%Q;Rr zWNdJ_q>X<06i!1v);<+XDf|%if!40+mV{@6PYgtPs)8gs8h+nu6PjHM#QCwt0t#Ho7h74AYLvTDXAUsjb z#rimiojzZG*zpC43@uHnr6NC;Zrk8CiC`VgLx<7;YBVcDY~MSt$WJTuP*@QDO`N@! zisUC{P&o-hm)4d9^Lgz`d&PAU&Y4(k2f=5fQS~jrd%|xTGI!15ss^IAcGwl$*N=!& z71Z&M`3e*AmV^ZiSCgksI#L+Kibu$Adefj_5y_4Bq=9LaMN}sWj9J_&dZ5n4p+h6+-HGkcb~{?V_hEpR^i{~VD5_j(1R`dYX-0dr!sUlA*xx9 z03^&`;Z;I^;&_&b;8L1nek|3zd9h*-r1!t=)8OoRF|MuVv-LSl@`p7bMMEGmUT-21 zR{|P)HL&1Ky5(-E;26)v8wjDUyJTpDSyP(C!eoRzi&(hF*H-lOyJ*_oG2OAkF*LA#(5Ks+U=qjzLA34dg#731 z#-upwY>f$y9pTTs2RwP3_25f{5M^BRSy;TbE~)Pizzk7?kh4pXxM}#s0IDf_JVXw7 zy+2125%IKP;ur-lq`>m`P{h8-s1Hfi6|6LqRdbBaQ-@P#E*UKsk}kl z>L@VGB3LJ4kJPnsEWQ#&Xa(|8ln^>q!jr|=`kCJMT>AlauG6>C_nVnxn2v%NjOrM*a=>#|hu+8Rd6YT+)`(k-*>KCPv2U(Jx;kONq zvFi@-WE;mfxG;&jrck&TA4>legESs6NUaV}25DQD zv?~EpQb;*G6CqNZ{-O|k=RMBv{0@UER;m|-ONMGFER?9O&U?yETV9LC;K5yoAU5>O!6$g zO~$|CCXB31v^s=;CV!~x9(aKy$upyF;9u~{Tg*!2AA@>Ze!lT4Oc+2LGZ==MXcnPHyk^Kuclh_jq zV`sM+J8@oRAc6U?7QXOD?3D0PjVB2~4 zJU+NYuQ#Gmxi*M6yzxNOB?xSF+8VmkP^V_h1BIA+oT?#**Jy_wC)q6U0Q-sOZmBHB z)M>BNFl%S4sS#rhBMq^Jk%l_mFesSEv=iKeBA}xbdnhDTf_4fM5`$*Fi^S<8si_ek zp0q-_?HJr_2~jN%A5qD~h^!rFN3wsw#^Ru?Ea89H+Ncu4SDGM-r43QimvfDEgWFfcAMDyB$J(yeQ54%!Wc`bP2QtjNo|3 zg`&v%a4qj+P!%Oal1QF~H;IK2s;QxBS?z#W_s5yxSf>(3*=q#E`KMV7MSwe?gxd7| zUg%B_N+#smBAxQ^1Gt|#rYL2PZl~(WTHL~YS4&b*L#U`}#m>OGXz ziiB0Kh0$qy^KNcC!WT{ejNRewv<>h+D|7hbX4KvA7+!!kU*v_p_gDiS+@59RZ6c`e zQ&92I@Ou=)1Fw2J8UGmW1f&D_@!y^h)oz)FkmXY~#@Mt~^;ohqEi(_BlwiaUrvWTT zaOxV46&UqCt$;jSBE)Y4o-=;`P3VEkH!fkQzu||FEX*OF;9VBJ7Ec@fGR@xT#ufk) z9;J6%l9q5cFxw-$%B+#h2`*RR&o_?yRx1imGS1QD>1`34WFUhtGwKF9=@}ZWgl+7^0XUyzQt!wYs~QmEG}L! zxZ^k#lSq#1CL2A$vOhhF#)_cBPBmgb_1$2aDf2#p7ks7(KF`V2CjEfblkpe13ZoH@ z$i6LMI6SrWBza#h^0me;D8>vLqIG72r65&q7Tt$h_5=`&(#G71!O&tATx4-N4mr&QJ>B!k6vU@`MhJEoY5pRri61Tk%hvRdO9b;^pFVaUg$ za+8GA1U0s)QfMf4?>F!XaB#pejTgg<$Ji9zhtg;`kfk-OERsyVxEhy8fdu?eW~dsv|>$|*FJr~jCn*u>zP`zsDXxsX70pVWGhea9%yKu_={H|VHvB9 zb>OSEIl+3s>fM6!gr>QOE-6lV4(&M)|G4P?nXsNs9FiD1vAkzT!3uVLAWg~2T|-!| zgl}9si<;LTZVmNxFa5h3UK?xxdPZ#WwKB2vgxC4Qs>JMYkMzctWlZcrgF@6)1zC7~ zAhdw(klLW}vuib#OiLw>*jgRgzX&pukNcHokf~xp%6D;d6*?0R)!tB}r$ZLia~ z{#k=)O-%g<2U^nv-~huCxOJGDKpURKp}{&*&zS)m@uC}EouN3RWQ5xeL!-}-w2dF^ zNwm7y{5WkiS|bNekoXL974vtVwhC1F0l^;-&l`&!Bm7n14m&NZuSR3~6Vk9OElS?r zs28g;2p6i_H(laa@CgGMv*C={43EtKLG3xv=R-s8OP4_cNc$lO|ec^@f z76(rAX_NLpqVzfGLTC^?RLyBV5J%7Qh5VRgUU&U#=a+J#DSr=w$Bo)kcGdYuGm*2>LCmN z?CQRw1Zuj7tX6`k6NN#XQ~@}96Z|N8LY;?7A*P<*`T-ztJK$$B>*+L zJ(4_1LR+ocmw$^7MhMqQExn3#&WueymW_C!+8+=%jzJU$apI~(d-w?KR+|vT1HbJM zDx!}u-@HerL1VCw$_r8KbjEu!;71*u<@biuP ztipGa{=@z*yT<}&5Y!U`a{#RlNZ+%B1GED&}{@*~oG_yA=` z^Q%qN!K$!3eMdcZjjXfyq4jRsUkrI)H~O7I674NpxKyDwTb;|Uj?9UCAuUcpQIVJL zhN3dx4WqB4jfgKCL2K#84Amj;N7!vh4Hj^H5;>L~8JaIluQCl%sy%F}`bp!qQmx8gTiJp1Ss zE*z|a%VKv=b|COuR8e*(n+C}Q@Hvx&a5+=I=qM`I8*sR-g3jy$ZCl$3C(>3alXtY+ zt0%0LqH_=wXFrav?Ab}Xg65Qod6hVvg}w58G)ZA*cgQd69v4^p7CHJ{F}tLGZ4pv* zJ2nRLHeng$1G!N&Hq~f*thP_Yy<@z+656mzu(={bnl-wu&X+B$K`CB(+`%|NwK7|W z|7L6jRY67r0b%jV?CG-@pAm}fRrqhb9FH3&;`y*S6u*9E2$yL1HLp#dqqwktn8}1R z;-rs7N3c~7A1erim!0tG`2pyWg_js< zH(RJsH+-L`j!&KdMPe;d!Wy_B?jB&$p*kLu472$@!IgOl#4gH#=Zct6V_s1tZh{l z4hxgoOsn^W$4Vq}o3huNP&IIQ9LC`yrv?*p7{p@zd8k`we$>z%YsFD>C1A8wgtXk| zL`Z|J$Zg(8?DCw7DDZIMK77lS*gh7|8PQ+dDqf1@k2!HYq6OqoLR~x=s}phoDMba1 zih*?^EK7|-CsxRZbnP;ay!?@*eJ_17(JCW@tAb(*EU*d{>oRpCJBw~;mGipj=rMpB z9tzVmd#D0{e1t%;bBF0%uf9$w!3<(1JZr?-Qr|Fk8pGfJzCylXK03dCk`**$*fq{7 zVIxfdXs;ko>M*bW7Q%D(dlGVFMQ^;r)*)WN zz-*>ey=CCZ)=IqIE&Y? zQTvO0&+u*`P+{8X0Je=nIes5ixWFO+v!4jdtSLRSvG*pKTSP_~l<0OA8ng1ihF?z+ zN6<>L_Q|N_%MTl4m7dhB8%{H7vYqxeY#wg74ncajaOwRER?1F`XaU8HfC<$oSVYXQ zbs=96;0Kj!+G=>VcAO1Uj%{Jg9BuW>4Aigy!#1|KGR!u5N`WhyR@L(z1F7nufA~W_ zlzP^QvZF!Ch@^^k=u^3=1~;5psrT3=1MH!p3ZjQ6tNwDh(U7q>{BVVtH#P`aWmWHr z-P5sCu^HIKyHkut@oU?U@H%uq46_EW*%O#d8O3_lQpVCum{m1D0$W^7ML%$b;{Gls z4@o?2g8jLt3~eJ6sZbg4j$kL3h2Y=JS4k7>Jq97G@Ch)1=mr+2q8r5AS8E4UY~9JC zGH8^1-e2F9UL#WE0Vk7qS2l-|{~Xo`%t2ycuVNt!6_jL<6e;2evz=)5SI#QASl9ZVb zLRpZUI}+O^9nT%&UyO(KbI5W_@i06I45qV%iHRNF&4Uo8Q*Q5{Fv=vc-8U0G-hU?! z6oiI=fOTYTLL%(XFX%)3EAlfH*{!D1|Q)(LOD~&tPR7bJv%bNAQ=-_;!fJq zmKpnHWEbh)hQ+`M4pZ{I`=nPMhv59kGzq8^!%UYV=g=#G$4Dc)8AS0^Aat>*fkA9Q zCe(t#GQFNlczXB7ILg)!3YK8(j39K?WegL2>Yj+b1_sO?FiBy$JRL#<(uE4w!xQJl z#)@{pg&R;d686ZY!qod;go7a^Y{+EZRB{SMP{ZXK$`BzgE)kfl9wdE_os?t*awwAV z9OckGuNh`m{1m>EQ0Up3zz6vRu9M%s5Vl)_YoQF}bve}!f%b%%GLkBh2cSL@t=oPU z=h7FPB|#*5mjF6+yw0j~=_Q0uMkr^103mnwc@Y)gU+%doanIRgvZP-<+5RM6fJMnE z_RJ=>7ck3J2cdKpKI}A-HG8mxxP_B+MQhO#5lKL9lVYQ>DTrtRlg&6EDf|SQLd~A1 zrxVws1{MV!__bSXdiBnP3)w!Mm8?|^u(}9?Va=+9f1!-=^I&E;7X>5C*JC4GU>+mwagPK2ykEPH)FNY+n*;R&vJy8U+$o5YZLLB|xfO-! zT9N~Ch|qFyhCvO9$D{SytKG=$=Z14L?TRE3Q6%xZwX+i|EM?&^?`^eMi*XYz$&b7- z4xJY_g6GTIqeZV)-`=YoDtkcYqh&`p&NuaPr*!tXh3#{g^tCHvQf_022M|!==NOdq znNMG-e$)mKHmY%YG@_+x6u!PxI@{+Y(>kzMiA-#~oa})0o%--6$!hmXiN?YK~?Re}n3{YSlJ5poEW8&K3*s()@BcijI8dU1h2klDc zf@MC@H)s#-?(j7P3|7EvZB`aNnC@?pPK8=#)Q-@vL670<*iXGP9@;@0a4?JV7+G?q zbv0w{H)LN(-ppTjfv4royaT*g?#;x&yGI5Edi2+l7M&Hx1K)g)j^)w%AAzf)@c@5xTGdYm>^#eZa?uq3wN-kWgmf%D+DFdepdn~)Dp39wXrgBAh&>2UhX`!Hcsu&Hvt&)97SEI@y zYC|C2R?@u;bG>t%-VvvES`laRdKLT4xAZVogI!KP^=NnSWxrFML~+<10fXF`NGL8@ zSVeJX5pHgGMk+2bQ`Z|MZw}hJ>DaCL2Fm{=f>3i5QT{rhB5kwl%Z57r*uFYr*TY#l zYIN4oq?0qXgj&QKy3a}mx9rQY>$I8P=&+){=)OLpq$vLqgsRQx5gQDBG$oibbJG$ATI7A z{53RD5Cij3Lvdg2wYSM%ScAFF3Yx_A+F3PR^~CjPVB6w)Nbd;l9O13FS4U8KmI~w& zROSP@q6vzau`mYV{vJy#^bql8&nr~oh?zBsYClYqfmvwjo-pF*sR#DX^A4!-X^EsP z`E^Q=Q==6Un4c>E{78RN{?{h~NB34Ffmbu)8_+xx&;3(C`W**ERmyfY7ZFMtR@F4m z!X5k^a}}|KZ4F@RiC%(1C`w#Nf35+oB*y@n5_g@o^f*ixmZm9}Vvzah$umBW<9H(58FKa`oW-|$^o6kDzFYB5mBY7jdhz6K8U<%r?CsqJ9 z4hMePGPe*qA<0H@2%n@Ex!~?RH%*ci4*x(K+2cL$7h3vks?es5xypBXKqz?oaa3wy zdJ8uy4+JsLGAid#PSPP1j33s}M6BZ@7}GU>%PLgG%2(DV-`=oGIIs*tP7Rf`0hJ+9 zMo|SF-A@u2%guWX70ZNjhQWw;2ikTb5oH zJ_-O#&N|zQ)%t{_hgSe%$j9J%Tdg0)6a=AZvb!4+8@`Z9-EM}rvya>FrJdVCzOQEj zZ&6rjcL*W0UFLSxgRI%FXh`A<(U+?6A_V$f7Tdf_iw@lRIFu=^BUOXc`4N-`x1*Sa z8ogT!{Ws9ph$^#%E{y~6<1!B)6?FxnWUP_yj#1R0ak_xU{jm@-Sk)4K0MDq1yfPsO zlEzNMubBacz%)84^nRj)8L}g!e!Aq2Zp!`#S0cerH&ZXWJ`GNR_rY-I&|_urZPF!6~X$-_8VPfN;r_l;!C;S4ieoE_mi$JN1` z5h(9}9jIgX!vX9Kz;5yNn|smRM( zhGd4HdQU-n9qiYS>y>k>Z~-b><=G0+)D07p?q%caXUaX0rQyn=pfdNf{}@z}J1^7pI*#bfB~kEy*E$&Vni9@U!GRhZjPAVfH5Nv3Zh=>dd{K5h*j@u9ow?}27*~VL`gl&Gn7;v}XT_pY^F7k_V zBQS;+*u3KW=c6JjlZD}e57t7=W=QjL>f&0V3}lAjIkmbFZ431bvSstZA; z@J;f*3(ikHf81s^_&l733u_zyork*w$ynt_ro}n0{gSF zaJk~jF-d1Kgmk10@a2I0Cku}fYb~xiv_CG>{jgn?znDJbdg8Vqx? zTR5EYBfI!S+?YXkMuJdhxNF7js2yx~LkH%7H=e0j*C^Iv`aDCG;rw}OV;dUlIcKWF zFN$`w@6S1rNk#MUDEe548`=fmlQyn*VwFkKgrDE!e(SM(V_1)UmXCXFcOS<#79UYF zgaYYJ46?#DV%E7i6KwczLtAS{ws<4)?aY}{US;8{S!UVn0*9ReFgXEHwRGm_8$?Ao zlQ?`WBN9|k-fB<4!Z!Q#OW4TTaoxQQQLU)%OGsk=Y<}r8lB3fytn|c!9wX(EXU^(; z+wwZU=oCbI+`qnpj1lu}I^(qAeBa?;GSf!q`p5}zgz1*Ub5Se%g3>ycISY;0JFwCx zvBK2gry|Ls2E(Lu01Y4lL$Xa}+5lL~ckzVVoXm+#(~sS<^#VRA=bI($YyNlS^KufK zM~)g)J*a?asH+A|yv0bZ$y1AGB?~&tb#J&+>`?mw&U#=n^?nI1Xzmxwy3-r(ZdGh4 zb4(&U8}jEUx83;RKpdtTP8z5!UeJ_gMQTk#(-HcxlF2U)PMc8-=IQ_`d?V)kQ5jqZn6jqR>()LzX2Hf{ z_A&djz&%9Yi0Rjkq-msg?Cnb~unKA_Qz=mtFIoGq)2*ra001Pmd@+<7Q||a^BOj?& znP%^gTLhEv4uN)`c|+@g`EAT9U&|e?+|d>oCKi}s4E;&Nb_*j{B`rqLHuj@sruMZ3lA1cL zx=e8L4QO-!KVKm_*6`bbCLdW$`tG~TtGG7nF(*3>n~FGC?~_>Ovl=NaVLH!KTy^r1 zud=nb>LZTQO}ht-#0ioJ0KLr)!+f$KF+~9(7QaCmsN6aztTj z*g4D#$i^y3@0e&UZ6xWuxxyfTFn8{l#S^731S9jSgg@XU%cCqoj_xA@-Csi;WOHzj zNZO#KFXWuvS#@W6NB+RlX4I*-Ng1j}?LC3DsZ{tPdrxIMQx=dibS!5R60_ z6oEDS?8`PO`V~tjx2#6u!=9!ZQ>+BZ#HaJ;#7;8R!zgym(x-zaMu+?YYhOY^`1cdd z0}v}a^Ol{PW@$4jcV zRd`1gTL*E*3g;uWtPkO*B>;*Q@>__LET4~F3nep_(iJ;y(8@8A2u+aAc@Ff5^Wa7$ z3gG*s8Lft1!nlz<*bJM88Oa!xa52#}Agt~!)M1q(>(tN0am27aE>v1HoEa559sR`K z9LacHMm!WI35_YUcTJr~B9PRlxi#pXw4^F+9FUNQ7uXgKX@cFRo34xPkD+cTeMXi@ zz%ZcolqpkDGp=g*>??rD6nSWv>&e39Y)+fnI%OuX-8P*v^O?D?{jo)rwoCZuSNU6L zu!~iG)3T_0AKPE*o26D4hm~E?R9F8+63Wl7_gS#aHFUM0-C`~BC5gpLs#a9EO>?4G z4ev#Mp~hq|x{vmMc>mge-hZZjXO{Pv;mP(y8V75oQtG+XLMhk2~NB&nr&d=qZpw~QccP4tf*26M`G?{ImE3^y0#kp5WhpuQP7U!Ln569IT z(UW8%QiD2S27V3mfMvcErckKv$ZEn<1vP4ASlE>ND}Lzqsr%=!z|$*|5njZ8Arj@G zLud7atUxH=-<)WL8{>TR5Wu(?OJ(t(<0edWVI6GHLkOMwy3{Kz9qW}xb$tCl(<|*_ zs%d)%>w7IoyDq;z5+HzL7d~k9p~1>H12*EQ>?hU@V)5$hOtB5$USVN#6cAUXI|6C= z1jacRb#82t2+-NyF;5dvoi&cmI~jm^hy7d?C2f!lBPwP5hF=+9bg$eM`jvcQW}AXf zX`zsBBz@2Tth$F_E-fI1K`NXKKTz3&nh?>Xwgt2NhTC~Vc%Gif_O!w6!ADfy63Ou? zfL_nKR4_!1t#Yb?%~bSwJ?GufF?+Hru2mq;dTV?=8tyQ~*Rp(32G{#W45&8M*hjd^ zi)!?QMkeyT_K5|7s`{xoQ9+q?qR*xs%78cjcn!exSkn8P8H-i*nX!R~-zt$4(1B8z zJd}u4S%eX4yJRh>yS0F;%}N@8AVI{4>;6X(rI+$jO>{LQXSG*;8YOTP#YUlvufhK_ zj7s>|7RA7fggeiINf@fYB_WP|bQV;i`MW_SZ~s*4dZfrp-ZLH3)-nRwANW61q zZ~$sru!|3EoF)h(*gA$aS{Rlv%@D@KDq)Bef@K(T+NRYp1Vz}i!-K@M`H8ci&|cgG zAJewrmuL&p36}qb)cu7ro|)94HjHIrbzo13Zs~T~pkgT%n36}wHOM);5x(>uyTV4) zexy!t+<~vhWBC(YuoM)~f9)@48|KDY352rRq^q9ayj>V{F?k^jL`8t?G)J2b=Ff~> z4Mx-{3jta@a&Ovms-@!zl0?RtbPc>SugGDnXeuo*lSn9~IZ!;h%wy#F10H&^W|!T8^wy{;o-BZ9gcPReEht+@LVRxZ#8L7s-DK|2x1}1F>RJi#cu} zVpNS8s9Ixkz6EZT3(HavSP>kC&4re8m6ADa9_tEh&u57q+c#MSaN=*I;14>W<>Qh+x19 zMd}$Tgx_DWU{l3@D914w_>z;7CJ^!LTf2&=8`9HW; zyhwPN6@yDr)#4|uZmDHRn>i>iUG~KaEQL!=2ERrJjMGUlaGai&D&mLasFq^Wd-{Sx1F^?Qkyj1wEY^4L zdjF|a?@S?p!e|pVdS;Y0HdzzRyR0K*x>Y}ny^#hI1c9Dl%XR#%C02ciA`nrt4(=B! z6?Rn6%LpG(CvRX~B?7a>V@TGUu4^in0XvF>$LVvn*YFlJdfRoUDQqV&h~S$TzShsL z<2>G_3{&X6Lv>Rp#&{W!=eQ=-zQ_IeGUf?6mGbP7v4<0wEkGVfmDAXzYRf{zdKt&$ zL1AV?Kwi!4xV-CT!D6ezv?!BsMk3=9FZ#3$LfA!dt`Eh|J?X?!tBwkQ4UH!ILHhN) zOsdGA7x#P9{a!QqJ|Dh?5YMSnCXQL)u$z3Jg%W_t_X-C|vgzV|{~^T49eZ@*^$M>o;=tiEUHgbeK>klh~ZmjdSp?XZE3ah#Os>?_g( zSyXL%Gw0zE$wk7@)f@PlrLOp_6iO`dO9+{FIFeJXV{5Dm(C9d#MG|KUlHa4`K%TsC$l~vyELC}lnLr^9NP#6wJ;8($ zQM-#~-2pfz66_9r5Wwt1GA9Znjl3Q-qeplL_?hT3B%dfNRtFEw1JZ%(I?s?vkztbf zBZ)<#4dcvOtIK<=WFX1+sMUnEV~=mrtFX+Egpua-6ak$z&J_ZVoU;H(rhG{e*u2Eh z?B?cVbT)N8T{D@^sEZvXZm_bbl~I8pTy|Qhj-lI79qRBk-1CN~?y432bL&<2ztyhvzfx&G?>d2j1S9 z;{GreywwD}*p?t+jIsPOhQ{g zWfYCbcsPMd%N48WCw5PM<0?c7?V&1%Tl2eYqgNijMkp37u%U#4;SE%)SuVC80UwfF zxV(vV;nX3MJY{EaPkcHY!AblGsAb8m_16=$wMpy1ZpUG#Ej?2fSgZz&JN$)gQ6>k7 zf*C>?dw3(pr%vCcy0~j)h7aDY-kKO@Zop5|RZ-JUrZEfu*s6`nJexsPCzxAGdvy5S zSpuNog8svdJ_i8+>wyrhpr6{ICwR*W$qo;3K3(?3b)smKh5}z9s)*Tb?aT&vz2Pr7 z3;`t|y!q<7HoI>N_sESrVuBuk&v)Udw3fv<+H8Ng-Wh{=V-nmF6`8c~d!q2l`4z&n zSR>2Ld%GanD+@IRYlH+tHF5!eg8d*4tQdZJC=cnQYz9=3*lL2 z!*8g=;T>lyX8SC{vzHWm``L>5xf3UCCdd5D#J8TUob$8iP@M?8R@1tRlQ+OD#`GB? z0{s@e-MXr!RV+Ig2@!WA#sLK<>_ajCj89lCPEwtwmAwD(w@tjd!VLOkyv0uST-Mql zc228JRKGktS9Bqn-`DRIeO-Sgf3XBj2q6n3qcVDUPA-qrh~kj zD%SMSazVWTr|fsWx!h@>!4;h)y?`R3>BqDYF_FU_j*60`ycxAsr$%ularn`askmBW z-ToDtFqek+!t@encxcG{TUad?sg`aV9S;3FAl z$#kU>`5f0!2SQ`_XuYRY79dYbh*%QjH?)(s5F7d>aw|DGVp1!3#Vj)|-Js96i9C^m z+pmuOkMS*1JB*aZ{7J};$TdoT#C8J<@#U|S5vhtM42eWZ&SqK4bi1ZLi=(6`9MQ0) zpow8wix@Ycbg()JfJTiKTeQQ}HC5q;7_HzG{DY;OPBCyXN;$P1%q6!fjb z%N`UKL|?+uw5M7Bj?I7f^`XfybQhMT`7?|0l+T@2NT1cD8I4Hpz;#>RWq;vuvI+xy zglw*HheMf*cmA-Y?5RoqYO(tW0kMROy?3hxH&SvuqMlc`Z zLMWn<#P8*D&WuLn>6+lW>_1>p$j_%)FgmnEm8-Uf!8jRF5_dEr%35%z4~URzJu{@! zSPL?;*T_gCbSyxfk7)lQ6Kz*!Cz~}(4rFifL#Grv&P_&jqnzdvRjQ<+_s&|hN1-pwt*z! zzK&nex;S>J5`MK=(m`zDlb(HqWoVh&+_YtXwU5Z;z3w=&N3+wCMr})XJ@lE#eVAtY zn5=yXk7-zCNeQb!`6uxf8w;`oZmK9Z70)CYHDp(3Jv~{y1pN~X<d1sq4YA8@RVj6hG1f^eIRyw z#z1@=4Cf(~9kYWXxwx<4jHeWaJ{KFI#3OO(Umfi-95;TFDhlO#<`ixP&UKDBs=DMZ7I6jV1qPTAH77e z!Oe2+lqVGZ!@fQ&*Kmoo+wAX_I5kJeQm+ zrHwox0dHxXT(?A;x&0w*)M8aYiL&9H)=utl#X(=dn1HBFM(#1+JuzwfV;>ChCH)^} zE4VpAq~R9sVy7b7BX~&Z(tKaLS|y_>N-1CpugAKtZhT$c@Oye~T|&Ofe@`vg#vKrBNb}w;$i30=J!wjxy-b5}Ya%J&Sa2XCS#>G$$kF-o( zdZ?V6Qiuf3|Bk_bnEt#6S!rqK#l0*YHZlw1SO-WFehI19>@m3?TSo1w4%0QEQYTpP zB?))WmriMiGcxGil6VrFeuU))${%iM6@W!V&d1{Ex?mCqS=d^|*~=;x!hvzidsd@I z;5IP8hC;u3mGg|CXZpq%#mFX*6A32MxgSM;%^|Y1zqc`I#KxKpRo&;Qr`&Nvvw=F}9{PfPizn$6_$a@YoT4-vhhk;7glm z{+LD589waKJ8*8q^+yOS`UHFaxL`Edlx3+sv&xQ zs#?1Q832Iy1q+UL7KXn|un1pnTd=u!tFntq2MK@9l2PH?1nr`*i^PB4cF+pHg>#?0 z@K;h{!llnF{1vPX7S;=F38`0WU;u-$OL`5M6(ni!<*0*0*C&1Bkj$bIVvm@$h_QbH z#Wum$mJ#V;$h?cwg{XSTx8?8;*Gh<>x2CxAS4Rz!Pr>HKrL$H*;}mv5sDQ>Y% z^vX7&+a{}sZBB?WeFFpkGe9y|=~5s`Pzi_b8!!eI#tlx(5(9vYGGXOg0-j|uGyqAd z;>lfW=?+`>X^d&~#VR?=7{@inp8-cnkUJq9^KmD1*DV)%wmMv9;UXJlA^KHyAK_R^ zZlNuD)Gf3_Ypc+M%Z?iV;v^$Zw+$pCS_6xeuDAxx(SlPL@KA%>DTYaCtO!~a9?emVX*lPa0*wOMIvc%WyR=?(i&tCQzzb{~{x z*A>iynNDH2C}t0zKIh7cFds3I_*@xnIpzKeo*QcC^K6}6XB%P3DPiPIZAuu3+TF(v z*^Y3JFWY=xnTM3Ube7jcV(p3Ik>yVuP3|^(lF|xexzOp<)0bm%RlBicPb92I`IR+S zG^gA+v}Xbt0u_^%#Hr17BN$@mwaWi)qR28108Ow6HHC3?g+0ZxQ*q- zyUy?~EB9HhYvE0Vtz_XZxsI2ELAcHIQ7#r`E(rHS7(oU+tT3TgM?Jje8Z4iR(IR^e zPjDY$2hpBhD2@WLJeRYwF8b8wPOJ-(4Z?mG(TQNK&hyPst9+zuB;v7QSrtv0bM%1{ z0#4Dvi#5?$xjdb9A-S+$0IPwxYmx_qyp5wrV?36g%q_d-jYdy`O08!|E`eyL4%M*x z!&k?qh?pgU0lzK)0GL9D@E5pyQu9S4n##aw#aZ}_{M&f9OkQ6!L=x^=m+-Gdx4s4u zRr1;JiS~@`czv0vVBf|q&$sTo5@pbI!%Wji1eBE1{uPh_qgOOT(@V*fNt$oO&K@~9 zM~6!6ZfN2()!%Awetq zGu;>OzXd^S@$h-Q#8AsYiio#bkUw!bS*G`PVzY~C-4?mKb{)oOAfJkWq-4Qef(;?dLXQ9pL%-XT8r%?H}I?Sh7-@rkvI#c!0C z3+!ZzV0a8)$!6nDqVPYXBQ^;2V4*KSqH@6=4>s0}@O#FH9Bh52@@Net9ZpCxMht?m zs1iPN0zp2!+va@A3VBi0$=TVhhC0JrOlqcf?UJ;ZfYfOm-}0(<;Z=&=;@netMqoA$ z3u0@9N$b5Q^d1Y(idL1anUJfDh*Z&A=q}REu+oAzF1Z{O2T9Z`g;Y)|;6J?4oU-_Y zlY9ajnuI#ZW(>2a(U5cebM-d4J}gL0b(QGHzDDTOkXD8$iw3p9vv&r$Nh zGbjYp^$B+7C1;X}F{-;7j+kK*g4V*v5*=*3dNJ@ zo#~i=$vQI+Hp|V<{p$&nVnN}6t7^i2VRT1$huZ3^A@=b0vsjtav_K3DkOTNxI3Izm zvKkV(Dj;|2sd+{O-St>JWeLq1v2P!b8&(17ZW5s}tSQof4~|DdHanqO9i$2|v=xD> zc;|i!&Ny=1q)No4{>lZY7aT|PRBMGTzN4|gGV0O;LQ#)ti53=)J#>!#Ah^IMlzOc# zTv$Lu{e-pJOopdvgkeBC^!xUkXghW^K3YW%vi%HhHHI+)=LT0C2x!)dK6FO0w;pYO z(47hCd!r^g-y8S;I6G{jQCj-!kNMOF+3 z^L$b}_&|Nzp4jZk1k^GaoJ|PL<>E88qa`;bT5bp_klVi57EuR7ECMNM2eND}XSS@F z--5(B^7-JDghC5iAvQw2!As;jkiU_kYdrGC?IUE%LV=+0ILGkr4Nd{HHRh~b{EN5J zupSuWW|-80+t6@{d{l(x?GWK49pSszf)wEmfn{e-!L&WV80mJVGrZi$yYVdfbRiKt z0cm7%>T##?@OR^B#X_mxaW#fgHVu8yZEj01SrSQwma|$q!>g=9Y|EwdpqJtGcob^X z_jNcPPjDEd2VY%$z*1uWOv4IV#oAwQ7nz;;D_kr#P|l>`UBPGvGy&40L}ASYHkpK$ z+97)K8ny}xOED9q70Xugd4EJSXzQ6jd1mpz+~Hc2#9`>K;2QpzQFXeV!#hBP6Rj?W z7J*Hiex5ZCI;ey_$1Vo|mvd1_oO8l1b;uyM)EaK2g>!JuV`0A5>{m&=C5X0Jebx`6oqO{Hh+XSlwEr^;*H(2yQhL2-cJ zximgyg3`xr8DNPb79J;^`j%sRm!uGW9h4Fp^f3cwT#+y_=Z9gL6;VM?gx&~p%K=9; zLtkoU5>zID0XMR8Zu&MsNm#|SGhtIC{oYAW!#`Yu8ISf~h!(Y7J;uIjTgq5IS7OKU z>^^3CFj>lCU0zJ#Os*s5h|nVTsri^^UG3!b1NE9$b0j1o>8~oX4Y#&T#6h4CA|+Rm z2u%qFgmy1o3F&drtyf3lnlZU{*KqbJf9<*=hpFi}0t-oaTLS%Dih;ElTc;O9%VZ z!RUf0oYugKDx&1nEMc8me8`>7nl+0=$3x6+kak!jawLk2MuTWh~ z$CpC+N<>|ym2guz}_BMST)H3aeyNlZj?zyh8%ZkPT< zT7H`94bKiX39i^ps;LNY!&@XN)s&|UwOQ?Pg!qYr76%YcDz%QxELD-*h)p63t@I?ddDy&-QtS(k0xMY1L^4o_v2pZ=RU0A}fx*ab~u>K}wu>P^Go%JlB29ABL%2 z_il!jlus+lOp5HYXUUzKwy0Bor4NjuTbDW9sz|%8v?JD|Y5%zJ#Ywl2iGij#ryGn5 z-*DA^v6g+TQ+<@1ZO(!>jd9qOsVO)FSXN%h7M7Wfj0?*wgITW~+1a+e7iQ&RoIA3* zRqKER;)1E*@>XW79muqQS)jU*#6KJIxh*!scsJ00cckl!*~{eDgdFGf!F&sVQ?#Ct zL611!VDX8)+M{vIh*plW@99PWW#1MJLY}tdsH$Z z`g+d_cr6Xs>LA5f3BK^ZFqC$AZB9JuaZ-Z6+X<}3x&ze^XojL~GHrISe@Gl&5-sBZ zt2C+$R1>spU+XI0ugUWfQ@Hi5fm zk6+3R)F@E75_@**-j)QE*ULIs)39z?KCD}9SS}rdmZDV{4=~DTB`Me3>QVOFCe2?F z6();)T0RC5gD!ZJBYHba+nP)HW3==Nf2rrhQXV!-5HLns9Q+hFu%o{xd>-3uceoQ_ zxQFutWHhO2L*W)E2sMPq(-R5lpL(X|EBVz2!XI1%o)e6tIYeJk!zRQb%;M^k=@sO# zIOzORu_-57h#iJahR?Tk2oJTEIOLaK)KpV(GWMjLyajh_xLxNogxkR>bInQxDIjZm zFhN5#96n8ZB3HDj_6_;1OSc$6Osjh*tzpg?g-5TVL9p5++}SvUO-cA%bi$8yCiyd` za%ms162Tn+02q-f;pqz&yWO)>+)o-P zLjE*-hJ)=EUuYuHRed2xmT8y zucmfT3+hR;4!wx|ckpEO0L+YnL~Ml1fPbjsN_uJ&^0`lGXWx6>EcxNW-Rw=^3I3Se z>>R`4&sJFUnKnvdjpgP3WiDk+XuN?F0V;I#F0ph`ix-*Qbjh}~AEIrsEe)I4xUYjF z0?X~nHumo0b?`L{cN(b?LSO-|>YN4wt<+_Y6@k2#q`YO}1GECGewVhX+AgS8;<1Z~ z9b}oXS!xjloz*C=6i*t5$j$3zc#0p3Cypy0C^}uD1zubx0CY^BdBZM49T{%hhaUx9EnAC&t zN92p8`VaAyJp>s<*8=P-6)Hd6l{Vgp3Fu8j17lt7xvw{IT8YM5|8^8UHt@`6V$?#{ zRhQkczMbc(o`8F$2WiquDs351i-b5nu0?=~Ea z1V+$3yb=~6k*j$(Et=G+(e1%sc|E@(C`c+=y0)K`-^JWfQ_M|U{=^uEB4-;bOvg7* zo83D{f!$e9+$oOp!{&gObJLOdi`(O-M(o_BGo@38TZJ@2`a6*aHgYM+#c8q7R2DQ5 z-2r#!BWFW?W%M#-hydpnIli!^V>0NM8T4dQKwT9UO~7p6D-^FeW)ScJ+te#;kj6lb zHrf-IlO~KQ;%~`}0v52rxy?MVOSiBUd{l)Ed{(#1kx-$lu(a^1J>OvVt)f2h@+I}z zzIzV@aTnrZ+g_>F%yBh@<*Om9Ue91?J{As`!sV=Yjl912btymipJ^#oEztFA(>A%J zk=g6Oi0FEdZi-17v((Zi|89Zmj?pzHLb33Rze<(^7HW5L!FrJ$m^u@LH5cKNE3#7@ zH_6kz9e9rYyF{ zm9jQ*M7g5i>%e=kon?@BdotK z;IJi|MxUg~aE~k!$jfpTh%0IJO9$6)!-ncsYo$a1!#}5Ob1720J9YUE_NiKODU(4H ze2HA}HeYpZCexrF+}3C4_i0DJu=?~-UU*<8AhG6f`O$K@0qd5!8}gSZ62&h^QxOAQ z8q$qqrO`OPNGD`M5q&DgA#GLW&~bA-OLfQ7@AJh(_>$lmzPq9xUatc_`QEw{-u1u9dlTp?tE>P2K6&z7 z5;D&L0=Y>DGZ`2JG4~1qE+G*VhZYfs5CTLQA_)#y#aUY?iq+cIu^MNsty8tmS}WC7 z>(J3!tG>Q=($)^Io&TTjKF_^L5N&_IzW=rUYyDYz^XxO9efHUBpMCb($L(swBP!eR zNhu1sfbt}H%~#vfYDRIzG%M2tl}j(RdT_zEUT~A#v1;?z0Y7(Icj;D$5BuMUi0W+X z%bJ5CTx4-ALMtSvq?BpeMzK_X_R37%40keLieL=avX)>ucW38^^S8*UU-yr$jJY9v zO7adapUt%LRS({+eV85XqPca#g@)ZOC3CV`hLcm62#$v+6!uE>FJT(04HQ1-!o7v& zhO~z)6Kuhx{mc_({d=Z%tV+sS2O}KASs+wJ9n>o8f~>0n_IG5+I|77E>zB-e@W|<_ zezVNir2iUZ=*WE*4-Vi2(vINMwqFv<)MPyMVBW;9RoSm&RBf@Stc4L1KjWGX$7k-> zDR#wXZDGWHW;=zK`e&UIbFKUblA)`$S#2EQm7C-SQD~OPE(AhZ&gTL4>G`2!Kq#hu zOuc-n%miV1Gbwch_s>;oHhLXBhas53bU}ZL#OetPhNRJ&JCQKDYHY3~8e@prL7c;e zSX~XGmpZ(axu>+kU5PkkT^NySJB9q+;Y-c#ByU-O(E_T0k+@woXJXQi)XC=tVfw`WMd5FTGE8{55a4gjs3#&tAp9>@wi4oAZEjwYV6WOj+K%nng_2V zu~6f@-bx^wQ~ALY)=QeUh4EK%U+1=6cE?Ky(ld&l|EwX%=0^}cDU{i3UB#&inr*L+ zA;l<$EXS%^Y_@+rJdf5$=OT ziD|nTiHS^pqxdsmdPZg}Bw&kaL`oe;IVoMUgXdCx!8$jsq9?akWG zjKnu;Qs@o;E}-NR9OSco);fKr5LWKSKw`cqAYfyT{e=00NeyOtQ)>l_o}70^ClcFk z4B0<43$x1~zE@$CdWp5agf|%}xxFRUZUp;Vc`k_hf5}LX->x)ay76>fK|ZxD(X4Rf zxN62s(N$po0*p|~{I0B>(nqai@dssn4Vm*;2L4M*11E>nnYcDiFUr~U=hYkSesq0l zl_AdVctsC)<)>*-#sPG4kf9fHOe=0a4vdyc(yotAPRSyr2O5`x$aGDOd@)|h@nrbG zf|jXU0MCdTO&!xuLqPf0S;2=^f$d=jVh0rFq0Dsa&F;jJQ=^h&zYsaVtO`20}{x ztI!H@-xA~y^nHRF6coaeocDyyMvF9Nrez7a{>3_19+hzdYakM&%-1k^kxhlhC0wTb z-#Fgz@2ylRy7{cz9vX+S^)zJlLKGVUFsGwVExx*Bg>^NDa6|<$?P59n=#VtRs-v#(sFhnMTnj~ z4)85V?dEwW7#B?2U(eZ65Wm6k%=j7BBBaD{7>`WR>xrq5RhpL7Ch)y(XyVd{bHu_L ziUCllQ*@;6+RNbJ`-iR~ft#s!j^7gFN(2LHBo_#Z(?e~YgpsR zTJKy-%7?Uc+I3blNq@+^8l$%`NK)qij0DGYPSk-@VTzhEMwf^mp03u&OomKrktGdv?Bf~@rldiQyN%P@s zd^8DFmliY6zg?R#x7>TY55PdTGwYtGOt^!#so61bYq|+(-`wf3?U$Ib#Wq(pDH)E_ z#HKKhF!a!U?v4L3yl8R3X{>3WkZTq)D8!)6N&V>!sxtwqFuC0L2XC8{OX@PSUEQ&D zs}JfP&p$t0FYW9=RD*M38`w}#0L^e`!&*r2YPYmJDVG;~vOp@UYX`Jzwf>G#1U2>z zx2CRvQ}>1*Zlk49=x{^@sb>+ek8IaaL}k$|I?(t+zM`EsQUj=mKIA|})Z-5Szhji| z>@2a{OVg=SBT5;B>d}Vy_K><>=IIUXCH)uGNQit0MUMAhri~TjgYm+{c(V^O!fR2< zfVKm2;?e1bPqh0&sD#LZK}&Brf-|O}4VPlrh&j9=s;VSW)(}iOAuDBP*35B;bv)`n zB|1|Zt>@P7X*T13Wz(w6zw9(U%$f=_;uRKBr`UQOAUxO(myJ;LuoFH@9-e)Ls{vU5 zb;PTHHNw_dw4I+x+;xhu#jKiDeSZI2BhkcbnOt%>F{D#I_TNQvP(mh7>EC(G&wd~Z#JZQ zvy$2jJ6yK0nM8E1914{9x5C&8{grz4oDwd?=m9~d)`&_ol(E}s&2o4$KY5NxNqyoD zeL*c#Qxa8hgeiR{)&?UT%V#p{lxBzkD^bbw*U&QQrc=Uc6*4FXIMJOY=CcsF+{I#6 z{_98sC2)<536kEPq&|X59LXYxGCDX&)tKmf*=>}bMzLt7H>|7{LNNwQWNWsDa!=cM zM{3r}>JDcd{ASt@La@8eh2Ut)=8Hp{AiX6L_`hW^^@cUC=%QqGB8%08K22!eYsK3* z1#s%X`uZT?Vtajs6N53xS0?rS$>6Ve68PNzq7blKxJqtkq(% zyrMqQuXKu-zhpPS2{|;T&Db!XAhHi_w9W4#IxLF)wcb_1O42HGFC}?9Q|5uTS&0pv z;MXXXzsrAyymqYbzl+sdfm4qX9{Y%J9c9mCoS7JIdj}`1Axv9Z&*gTuD=J@s9qZ90 zpvvIzMatD-MIoL8r3q}t zcyXOh<9GIBW$ZX~GrMK5X6x9QY8p&{KN*wt1MMrQ!mg=XaQFhz(7lr8fR8mh1WJk^kV1bw0Ko+5Q0xxzELAefhsc&wClWtO7q4gI;g~X-gyrCyraF zQ?$(;8mS%9L82Y@(0{AWyI8;r{4=@V;kq)*(OzNd0DvmIXjpG-D91GLvdpfIq-yw!8eI@E?h%Eo&v=BG1ft&U(Z<$ z<8Q+WE(>CkakKS+@hHDXw_n0f;f9JBe@&c-5!v1W9hX&wILXlwOtvi;EL|Xx5SDP(~NN?RWDRZxv$M5k2N}R zA{t{5%qxZK;S%Ym3K~lGDdDO#)Znidg3$3$?qAqAvaixxkbW5~!zpS3y4?%QA|2x8 z_BItTpPHOWNJ<@NBB&L|AJ0hmf}opz^N%SM{k?hEK<(h5hg@tyL z4WEGMPV5dFYLf;$-B>;hLyi@5lM*eDHW!^6UD*)KR3EV=phl9mgdRXMcljCH~a?q~2_1-39V2a&5T z;j$DJ^g&(-vRq@f$|Z;wYXg*qQ2YFehU5MIiZZ4wWbrDoo95 z-V`_EM+R-aO%RP$AUAil=H|{XdnWxQ+Lh}&`S-K0Y#9#n9!ssi!3Hn4gI51>c<_-i3v~e%NSU@%W~n~VeQ_cMPvo+a)DlR|mN!jxN&eU-hG!_xK_qtaqD6q4 z=68{MHHoCT`-Pow&@5S4^K{rF+izDyF203K%3$U_Lvy6TzR%U1juWzrP>UJt)R&}v|2>`4 zc>evy&O>7pzc8yk?6h#6qMBiSOL1evhO3^^zMi_nUdqt{$MtN_DOlvjB!=}pAx9$Dr=7WdAh2vMY?ZK~+P#H1$7G)nzzOeCOQsl%{!Sm zv6DdX2b)BB&KyFU6f7bjaj=-W11dajHauz$rI09LKseLF00!YToClhy&BxJ}JUl#s zX^Tr|s>ir#4x*z|v(dfU*1^IM3-+_$>6m-FUCVUJX6FV&6(3Pe@H?yDuVg1{U$um( zMK$Q2aOW%%3j<0@{k6}e0lH$}SV5pz(Bj z`L(FCWwyllIdR~!+IHFUQcYvEa=~#$8NSDT`brjva68UzaB%$%+oTwOgQv2N@E@`Z zN01^2<1Hn~61GITy({@^41*Z7kKsP>7+tn6;hPJ@Ja2sL~ryb3xX6!uKH5nnjuxU>GXeP zq?+YlW|C#2XJlOB|H9}Uh8vP`)W1As=qc|ex1!20JR|9#swzZ|s9qK{>PUpuGfMwv}7D^Pdm>utf{kp={XgAkb{>9_kX=ENwVdy)E*>GZpvXkMBlmMsh zMKxetLKfnQlC}hu)8yoVx1^(5wMuo^{M`sb495m{?dZp9MY5kbi)5K__bpmud%0R0 zgkUnSzS*;Ew%ACXt5L~D={yr&gwo$hYj%rDR-^OCHMlv8R|E$W!0CTk^v+;0pJnU| z#aTv)uOrvex2y0y!Gx-5(#_VQrXeS{GKUV;sG!MeG+< zV5u4EntK6Qr{rDY&Gev*2{aUH3Q&dyth#k_TUKNJD~oQn{MA})XNS{$h7-i?d>d0W zQ=YJYVz2gN@nP)43_^a8#Jg<8!$0{J!zu9vxdVZ@u7r+ zI;DZ4-HuOJ)I(uEynEK35?riJ?ZI*oA<10$&>DbM3O>c&Z*R~{ltMmD8G5usE-b`L zb)8R$sf)Xdh}==ANqA3&h4a|(@XoQVDVTJI60qCe$t41rkl3oi=i{fL zVMR+>C7*FL3h9??KCIo&dy?keVA*Niq8BY|VIZ%QeW7DH*^De!%8yXSG-G*x8m5xhjV=k;65EWzs^Q2~TS!=H zzRm*w{8KpJsq|k*Q7`hz;8l2Q438RP$Hj~gt=Dv@IP1<}bDtWa<~}~GxwcAVfFcgb zDfNFMl;HW_Bqiv2=EP^zWDd`uiowJA1iMO!<~;S#J`P`j>!};I08TBB!n)iD=Jua zclE3ZcvL|hfU%_}0C#u@Zsw1p#%3VY6rvD`Tf!wxk!Xhd`XQkRuPMS+$Cb{qswp#U zbmSObs|FZ9JWT6>AY9QOWgWSYQ#9Aiq_ z)99QLX`oUa(O+caZ*uZ_)lA>ntm!z6V zylugpTg6IpE7o4nuoGW66e+E7V!v&gC^u-V6&FS};#dq6+}H2e8DLqu-zILjc_(qh z@zTmc3izB2-TT9a4od@9_&AM2 zlY6QH^$a>R3zMm;nVq`xFqtEhRPkuaW%zCDyn{>qpK~;aHnEiB`0yxCb?7QR>l6=_ zLqSe1;0U}*D?$jF?m_aBST#8&u!3_0l|%Z#-{!?yf)-s_Gk*Xq4r&uI`V%_HV(HUS zEYDPH;_?`^aaJ5o?Z?j4RdN9ZpyK%jFGB@5%0fdgwq-}e+Da7ad`){G8I55XZAY$J zio$Bc$*bu{YHeIixu$~|Z3Hwg@#V(f32GLifc6 z>5_ajZ6$}eRc1;Fea6MR?M5|YPBbI9mn3q@?6*SgzcU|eRje+Js4j3i)sU5Og_#MF zqLhkgnGP<7u@)7Y3NX3{#(9AwDxOVo%t92T>Egf1HUp7wSS9d8j-mqR8%Z=0(iMN5 zeo5kqB=Q(#=r5_Yu!B4sO~bNdhz)ibn!}^)(pob>I+$&FEJ*(v-u=g|5l;NEh-Im5*yrD40G9W*7xhE_onI0(pF3hreVs}_iF*j`hTM`|e8C}}sYZdiph z+8qb0LZm=Utta51(2b%>Vt?VDYD%w}X(q43rxe5g6DJwD{^w1!cPC7+2?=xI)sWPj zas-@aj2$#z~6vUPoQK8le47I`jN*06phUrD7+VS1TTL2sc>L1#D1sSg> zcuf{OwAxOGMe_&1ET%ODHFG#2s0U_!MApnUUgqS~J)C06+(u5ZO1T$ObCzRzDzQlJ zD~gbl)r2v$8#*T$SAtGYtwyE|kLdsu`TwMXcx#8pZKPJyMMZ$v~9#iEj_ zkg~mI*#QR26S7QlQAEoFC7yYlm5SOVm!jg=a+Bc}SO2}pN>NZW#%t+#t#KBEGL$uL z?sP^~wZW)y(mPhsjIlu#ZtnFcUky7SJFcD*@aVK84ePgxlbMOra*RTu;*KH2@+gDu zNXw#mS|$4ygD9q^Iv7(QRLRjR6%ji+gz(Uh__CLxmCy=B1&hqdfR3tCZs^BGO7W6~ z)G{q`+)crxgx{bhDBVveTV-T!;=C?dh1yM325p}~$b74J5N&S5EF)-^HDTWrfjd>J zRRuj)EC@!;C9AWOHW4CppU+Z+9g5I{qKJ4))2SMk43;yaWfnXlG{$9U^$C-O)pT0U;t<*}ICXyb&^@QjCHC;5;>)pn zlpjG&Vv&TZ%JnaHO;=q`lKd@etOywET1|PTm>eg#w(yL(|J`HB>W+L}hP&)=?eNS| zAmSQTBr7?mcH)>zE!4t3OKd6SG>XNZbwnZMq9g_-W`pU*yw3D|1=ScU@Cs}AD}>le zEMu7>nbIgkbEQo07*^SfhSB(>}27WN)Ya0Gn}ov`o&|bOgAn#iBYrv>u|G> zFH~I6aFrEUTLxmEvGTCo)~EULnInM2lw4;O2fTUuyab!{*g>C zSVP9j&M_Fn15MbAVUNNA7-v!OU~F~Z(&hQP2(R{kL-<(Pw|vf$39PpHMzV31ejEoh zHT3aP5{$EQFvTo^gU2{s!3Hy^!{0Lgfx;6{Xwm-Jq@yUtK7~T@O*M~dxudUY6{FMP zv4|Wloy7Ob8nu(gD_Rqw+}0dcSmz!@GEz44(Fyrnnv{hP>9m|7;JtIImpKWl*|lDC zqXOE3!8HdprXmNAJlbA0F39K3kZ3~7GaZ79K!{w>XPUTTeqPCBBvKS56{ucLVky(> z6Jn^j{$(3&V!El_;Vvs@j}6a6MUl&pN4T#?8L(;^RA~F;l$6H&&Gw%!!*r1Z94Hmk zP_~SHBuKjTOj2p6@+^Vii)fz|C`>;Nuu9m*6loz68Zx3&hUz$D;m!ZKp;1@p5kOlZ zj2C9=k7+nZN{`J8F1FkIJ?4wDoSfEye`+Q1ui1E}m73u+Y$9@8Ly-kVJF_L{YTJsV zgk__fAv;Xo1SA&qNd}f&h!{CKVbj$WNwMMOX(^?^8Ml9&73jmDag6^Yhx7>dcW(q! z;%D;81X+u9mdhmzBE!b@waZ-W#huZsmvFAM9=zyQ$JUNCh5>*^;x1x9ic@12iTN~X0!#OWVvqhARKWzArdK;p5zQ8 zOv8phI@F6u=zuG)qs#IhV+(F-v3B;dOlua`dAuY~Vg8rpGVZmFF;!Z@VZNbKtDq~k zXl3e%D9A^Dr@KE(`4epibGZ1?xZ;qN&awNwE~t=%hSER`I5oOs*)7&N*$X-<4aQ46 zbV3(rQfRa_{4>Y^f8kT8P*oUyrw%{^8q#pY1?TyH=Wc-r+gvP4q;aA}^u!>4B3L^C zS3KAYkiA$g%|{^fsbS-T{Q3zs*3=Att5t5p67mq!Z`jx|_5PX#+Q7U%x`P{kO$MzN0X&%hcE@Tg;dU7=`kv1Q2f|75xl=3`2!m-2;| zzA6`%eq37N>jf|~aT)ZHsf#cY&Nk-Ctt<=Jp)aurVOl}Izd(OUkI zL8&$*W>_;qYC+8}NS*Qf<;g?IxINIhr4&9XgQzm&R%{z-X$iKrB2LgyE1#S~0X)lL zI7o;-a)@v{50D9W$~U>iN#d3~ieIWMMv$lS`4n( z$06Xd3*iKy1UafgvL9nhiBZfHf^3r)ty-8>b&EK>{~cJLd-oW=#`1#TtjQ*3CK^hK zY?}5m*u-*ezPiZ760}Y~#$@6u#%}5ADAJ!zcE2MQ-3H2$Hgxau_mP!IHCNK$0P$D5 z4AUA%Mb9|wiJ_O%7vd)~T>CSjF#HQuMtxj*4IBAj7U@|!9kCrYNH_My03zUE0kYS5 zL1PQyPb1-^f&{Tr*B4PR`~CY@|IEA zHBPC;-ofnyQZ6x?-0(RgIFOnG;m;z}k=h2aqbR4;;msgCe4d1k!f4%nY$*XUXjZO& zwlOE-i1`cFInu1_UF}h@`Fj1j&F*@(Fy%)C$WknvrRzV}<4g@Q%pTFiV9vK}Gt88v zv}g@KI%Uv9M*d$uWikjF<&Wq-<9K~VJB>o#;Cj9ZdA5lxKg%zao;1@`XjV~{W;pIU zY@*{($;OkK=5q=}!=229sR}ZoGP(sKU_3Arm+yP_E^lE$#s$RVz?OR*s-Kje+cv6L zRv-Q9t$-T;7PLtS+?;_0l!fwL?a4rt$i6+TOmPE$FnwhUDOzr{v*uQ^RR^tZCbtro zmO4~?zoiR$t{Iw5`(hF8KK$i;I-Nmf#BB{#tkzQQ(WXThDlCO(A0V!f9n+J1(KBU8 zDhzNH1Wyhclnnom5%D_h-~`UlcMG_HbG>t1gBE*Dv(P!2BwqGZI$gKaQCVOO=d6-F z12Rc=)47;iIGn70M=9PJRM?IHb*#5fT^rnL5nuVT! zTY+qanF!zLO$#(UDaSyf3DH<)Tk&T)MEl#t64?}3A1g!bhmBMf>6MLflju$XcI8|< zfM#qdQ`O#IK&J=g+-zL+!i1V^cjEbfwhIjBaprBCHiNVMtAKeCkIMCeHM8WXm>ssb z*ORo#^G}Cz_xBG~l4&vjFjDzV%&O(=E@%luMo}E?O5=gFySL{oY}Gi2Bf{W-Rojk` zw<7$wfQ{fFD}otB-Ta2msJM`eH;n>^djB5`4O%B{_&3HxrjXPObfwK;he6AyI_B)_M_(wTTE z1{uiZ={PZccg|`_1#TD`TVpLa%E@JvyUNgJYAHQbRW7PAxAe5vy|$rA7F2Lc@*5m+ znh2@t5is;;m=Nl^@nnH6`%NLk6Ch4&G0H9L6XmFOk?7EAE&k)I0^l@8O;Ev8kWxNd z9xd$LLoR^TaBJUPkYk{jt^CRkOwtmcVpgnuw&tPmbIV|AkK!t3+C1UgwYLW`q;e@1 z@%Kc%DT?D-3exm<>!2M<1J6*nYO29u6E#WPN!T!n=c@AZg+ZO1Hce#<8hUv}Ks>n6Igk!H#Z5cxgplTEx2*YD* zh@9>YQFBcD6JmvPW9@op`1fLmS?E7|3c?CvreHPS-+c;p^=dNhyC;+5Kw~taUuSW# z%!cT(8vE#J89qKPQGR%l?2P^Y`On|})_bGLe{B@SP=d2^RHfKtW@PM{@~y}GUs_6J z#U65)fk7ru6Gr`edq5NUnVk)kPMcmsK&jAEg~O9EYh1Dxm+WVjZ2drOE0AuuJ>&7 zZ)t!BMajDcBPTLIiJ^8ZiUo6H`YrU2j={=V$7sjuREc{nkleqbz=dSTY>A`Jgk?Hn z&rHLRO{QO~@v>Q^@uSdsTCSC;TzetB`TpgjL9^M62RV~4Y(r`5NPZM2#wbb9Amb!o z%MV*XVjxlEijvS@cx5z;dZ0CgS+QZeB8J3cTV7xlrEs?mKoi3h2>GU~=Rb-Jx7O5W zS|!JiUAZ-IktwFil138I&AgAAL|u#Qrd_Omn|3zCC~+7*>D5(mQ;3(X9WnmP?7YZ! z%ZIYD6b>WctI1a3-<(dTsbf^LXZhDyLc2XXtQ~t!(Q92hZb54i_&;DDNi%Lt7oRG9 z&t_a_r4P6bC~HwFUApv-nAu;peCC{SSgX>l5BbaW74G;-nn_;v&{$_n*XRZtCy+>W zGOm`&Qupq};p-6UiWo3S-bE~N?yocK*Z$tYa9%0;py(+O^r*8e7z74^%w0F*aZ??j zrMVjh75*P|&^~NzDAbXl#)huFWMlaI52Z`55qleZ^1W>N+#62nQ;KHNV=uN&x>qdS z?>q zhV?&9`(giIqX3RE85jQ@vX{wvp|Pr_uBNKqxSB(7>O>69k4BQ=s_I7(*r0X)Eubma*+ErXhA$gb$oom2zZH2&R=>SeHB6 zkS@q2^3XqIAM{`{2v`tgqk<%4bTofbFa`xG-3j!_dbl3-NJ6n_q87PN+b8A3ri9Rk z1&+5T`A=IzKcdI4``>l_pd&>bE0%K7j@&n1Bh8tPWb2Pb#D8b`KliIha^qVL^djdZG3@t6gTXES6ZD%mY2+MR`n0#!5?}W_q6g z6OmwPrq4t`ODB7ba!TgX>-=@&kYujc`HBA;-pYKI<;Np<1e2!7e`JTJaYPCgrTEuU z-F(W#TV!Zcp-%AzcZOrFbx?-CROLr^uFM?i7nywTbHl>(zay_jW&6M4G>;=oZL8YZkYajJ z$i8+YY5glvdU^hfr%*X|ig0MLaQ{~5$+d5$4X)dK==eiBXf6k&9D?a3yu2t|cQO@t zrZkLYnu^1J;8WTHbAoF1Jji}UC2F^%9Tw^Xo!UV};eF?udHGEKGd^R73{vLPBw?#e z?=^}jjNrgPh(Tvjr0(i|Vzyg!!3~?>Ci8>K92moPU6&2a*4R0_hWR>Ziz&j1ZVBRw z(n{p|?CxeUUrlyp1DfSb9%}uutf@%tcI=ux?!ZZih?vXyC!kTotp`ZU^$T&;jqZ^H zMO`$+e2nVRg%DTjZ=y@3L9wR<(Mql(BGZreyJCJttkz%8pOt=(N$gVtkrY00;*|^-}fAXtEwbf{%`&AJm4_zFN=8RM)e+Yh(!?pP9o9KbW1%^r%0P z@i1#msa~DdOs-RHq@)2du!87`l&XDicEd3 zbNu5D_{TH`c;NqhfVKQV0r1sPrjT!8n;x?-6g2J8MUVzw)ubWdiz$lnnI0>K)J*Z1#AJk5wZ@th8e>g^Zc(x99%}G|}W>Cn3U|~pCDg?Dr zam^%htO*lO1Y6Q(xqUDfA~c#(p$NG$4%JeS7hPa10}6)LtP(;wcD7HmO6BQnTzTMi z2^zbOTitzlqoiHzUI2Ef@oe{&kN`k4d%KAycRP9?+f9qzGpf46ooq!X6 z$iJTJDm+g;o>UlR)N@EE>en8Bs=oo7UJo&jg9vxPPI|t`@Ub2DK8QsihuVf+%gU=deuP2sH*X41( zO^tj&bYc`PBicYmEyC1LM5R~pkvZ`*^@#N@+kcRAC`w^T)$LO>$Rx?|`!#eK(Uoq* zXAT+h=PLbkHR2{wvqFNX{E#qkvzR7~=VJb!WIV!SO0&S*U40sqAln|#;hj^WaNvU# zc>~d9{sVpTGwBf?1I`1+H+efjS^jq{sLa2+FV;c;9P2x|o-$zs)hADgmc$MoO$dQ4F4&JNP0 zgfrQiX`^a)JaoFr^kyZD3fV>4ja9_<6DOM4Zf^Y!)~520#@MtP$nzI^Yq#W;>M_7v z9onS7%;#_oxI(DrU-%Y~Ej5!?n)RoI4_SdSZC9CCk`)??YVj=oOaL8dM~65nyD}Vv z_}e(S!NlKv!N5k_+ebmxnL!g;!^vklDQrfNkbFojteTThCGw4{;j9=3U}@>E*-%i& zPE#U;49c)+K!uKtAU?XyQZTg0-PSBzmiV_rE7{TIu$6qdocHf!mrYBDHkzKijGxdl z|J}W!$*yR=icXD59k#qZm}9<*&U9WBnH5kgiws5%re>LGYL%cV7^h~|`UUIkj!!{N zN)%>&upLKQkl&ZaA`@{o5zVjRYmzno3~RjqbFSrwmW_o8A-*!kChL<>J|54MGB>u; z=KzWry;p^+6k{i$y-p}->4;8AR@2e9_S(^4r2i#4k&B-p5I^{BG;tZ3%`VE>He{)h z2Zo!OtW|bG7u%^5IwUgQ0|67``Q_XgiErum-plcauR1OjwHLgMQ97B-&sptA2M~|eCmt7&|aR&7- zOGU2#%Bi^KlIE21*H=ca@b=`Du zK)k5J-#6fq8vC;6YAla#m{B4F=sFp=qK&t>X5uwHQqfmP*gJoBrJe2A_B(}3{;5rKwnJK`3AMP%g<*Hia@65+M zZpX$zAFp<;G^~Q>mm<6*rY)o*Tfj5Qyp^CDOuNUVvM`%+XWTN^#{gLTkYsmq0Hl$K z!min5VP=(yB`9O`{@@ttm|CNR^)6$g4UP?o-uz%jW@MH`CkL4oHkE+b@(M+18%n;- zkwnIs3Kb5*5mKsuIYKyXAlbh+ARihP8=!wRDr$xskZ?Q%3-52i<=r^QU{Ubi#sS2B zg>lG%E8vDc3;73Bp=RP$-zt77%v+}}gj5<7?Q1y8FwLW|$5+m{YGp+$DVUOStRyfR zGgPW!&R_1og|m;RY87Gl{20=9m{iJ;zW2bS4GJ$Xk>EpQ9um7n7Q0`i3qL~~qwRYK zbb`F28p@1_9p+H^bLj|wHsgYEXdq{a-nNTR;MkBty#Ar69G6DrxUngu=8Oe(q|B%f za>h8EVg$}V<^KOX9*faW#ah(I;-&hDi6Dxn*XZ{)7hsW5+0yU9MXZe2My!tw{ehxI}%?0^W8@)E8`t z0{DM#iIVj2ajTRKL59p#hjKnQ3>uidtWApi_+?2c+_GeDdU9lb=oTY?iiV$c!G2Z2 zUZ$%e3l$emQRIx`Q6c9$c}Td@xkXEY@ra5b6D7Qs1+DicU-L*FYZt`vQn)g)z?7I7 zCacLutV2@Gjl=~=bPT30NE=olD$Zfcl9s9jt5R%;H^f!P-?JGqw)Whkx@-U#DqP^B_E|TEb;LO=9fiUP^JNDK`9N!@i}2nZn#dP;}#Zt z_w9un4&jSqxE`?AC1y|7s4fpB&OrvU7ZfORgjz9-LM=c)|BFkESfgfEv9UhK7_X{n zSto05-VxOAAb~=bD4hoX8J^VEP%06e;(^vb3m6*ezC&viJBn#YnMs@U1Z&KS)|q}r zH>UDxWcAp{mV)caTmj+o6xSK-j%gm71k3|L> zN)#T?I(@qG!hr!+Vn1f>NaY_-U~OGZ9k8lZum!mSfc7kdpqhFz z45I7O{}5fb?ewq8_f)QG4}ZsZ{Qvvk0fvhIn;)a%#tmCGbvJgM(ARi;_oi*`XX}ly`5Wo`ck3mPK-2e>)YD6v1d)=`mU}<)wa*9 z=^J}CZ#!xF`pw%Kn`bumZ0_2)ZEbg$y{~^QUni{ZRMBfr?(g2Rb#3?7h7D>?@3wv_ z@9W>%vw1xqYc_7_I*z}`ty#xLU zeSrCx-8@4Hy4DLBE~uG5ygHQ9{hezzb}OEUp3OaK#>Sq$ehTaF-E{QY ztvy1UqOJ3a?dZacQ>3v;&itq00O?__c>q&ift#z$mw{c5v@5v-nYkIa!U$arD4}q|Rt}R=;*KFCM z*7R;tm)C6VUf)M8>gGvd|14rlFJ){NA(CqCCJmA@_Vt3d4a%OzzgoYxbL)Dc`s7~5 zU_)eWPv3F;S+mv#)4JWWg#bpW>o_HFgs~ygOH(y)1a)@x_9)3F8)rj_TB{gHgkQC8 zL#jXOTpF;>g@uZ$Hl)XoV!F3>*<^uWH*DEL?WuvFMK-)7SMR%B`DsMB&<)$xgz-iM zE^ymmQu}mNVgAVpYocx@Nuoz%UV-!zK()@ab5HR|uNz1=o>Y$6Y( zUJ%?RH20nm4yS5R3!&!in>yEaixCjNY1_vB9vkMN-_Tue3fUo5Y}#bQ5>oro8@Et~ zXin{e-WieUYx~wRiMmg6;{)!1r}u7!d=+TWCb-EU1YvCGH84H6>G_nZAx)OXC(`3c zRx00o!lv%&J)1Rk8MAHuJsaIuNEd_wuW8<=N#UD1yEgP}?w;PYwY#%leeaq%r>Tj* zbDC!#z0WK@NFVY8zSe0p`5W>Qg>2c}FPaeQ#R}FEmj2iTJ{8%uvHK*O!9=<+TCk;S zL!V3BwZ{F0o%C*j6VQs@Su^MG*Oh0K)^}ppwyk|z#N|j$L+tN{6G^2W3v}~VSV#jQ zBMoQ8;1CWQaoWAMt8=T^>DJCoHu;#KJ)%MVf~D?F;%gfs>pHjg_G^S&)@l;}&H}!1X*aICLf@~hCj|85bKS@%L*flHu zUQr{6f5rSi_jJXb9wsb}0xTcP&vgFhU&bA5N+JvRzk&0q$jq@iyNZm<%o-7i-1R5+ zrjf|{w-q!mh(x^JOeM_rt)`4Sf0)X9tmR!{c~_AzvPeJ2rW2kXiTJ;LDQv@$&r@F6 z`)b5~{LjDH&$_?*&G_%_uI{!-2>7#0%=>?awKwb-(qM1kE_Z+cr0XL&Qj= zvzeb2+m&SfPxak-hSxVYGO~=%leSwAen#r%eIhx^dBOG!`gT+%i3rLqC+OfI>M}xS?_AKG=40=$V(^8a2s)1xyOQ`~9S8Uu8X(`oU4MZw7^zf|~3}i+wrT9Z< zX0?>i3h%Hr{FEGjLg&UvOIfz6iBQ^i-)R{mBuq^fw$u>nox_;SS<_NWSG~g`6@A_7 zBQ14Bq}Y)aSy$K+Q;pZEsLsP8k;7hTskgR7N+OZ`p2C)Dkm%jh#m_lLBG>;R_e~Ob zk2yTPP4~RVAf0>O&wWdcoXYpc?YaMD-^t8bqbWXPM8(>kh!he9ADl1j3e z;M?wX!7G&J_K6|j0qjnM0D9WU6OV_0A6XYx(0zP&x`u%@H0dpV7*$FiqXwK`(MJRH z=^Ak66Cq#?uq01Y@+nCdYz;W&@(@r1E;=#<)PQprgn%``a=(3ice(~OAfsD9=c@r# z)aqwftm%!|r)xlVO9)s4EXfi|Zm4y^)_{f`A)p4dP7VPzpk==hum)J}^~ZOoYp@2q zO#}RFHDIh7@R^D=Cq?YjHQ>hCAz%%#Bu`hY*%&5S5vf?SA@!~5Z)tJKSE%~yE)4;z z-tyA_bor{@U$39fsQPhKpP5~;mg@ECsxN&o1gv^XvbbU`)msv(UrY7wTh*U)RS2m1 z)7}gLtKRZz{8Qyy_3C}5^}brwpI^~Mv_4(+-#Rx0ta?lGR7DrnTN0}8qI&nO>Oa3Y z1XTScAB2EaZ+YK3zB^sMs?Y4!kBz>j7i#EEs@JEh{@jcZu<9+z!V0Fb3#Ix_s<-4T zRQ*G%LqOHv^UV;j>Md{A@!je2t$I;^FhW!KnvFANRWOBv?^LYqi_EGNo7fSl=;nKr zeP=MWGZts+_vtKt{Uy2jeY%j}%*CbpeX5e*U||)N7e6$s1}^Gd8v{7`&RMlqMw4nP z{d`q?EHy=y?z~6i;}m6dd}MyS+5q*Xw<#WT6$3Qv8+XNJR%~3;8HrnQua>H~=KR9= z1Qqu*>1OA~Ct4;khS{a@N$C=%UlyNiWoHuenZL)U7=ZuMt;a{=Qw@j`lX+fzngMX4 zO;j}9fUJt%o-L7hg8|tjJNnspqktVF0cduU0l5G-wZ)q)Sw2ARP4O896hxAR@g;ml z|A*kyy5nu)tFNk+bAB9ODquyH%g}BB6j>QRfD9uhxeUt)9x*A*u-wuVx(o*zP!!Rh zS4lK?6dzN)$hyjS5<(iW3T}PQkK-LV!qM^l%`+qMgY^AB!rtT`u8!{m_OhBOjKP?r z<{Ilg>CyOH14K)?x5f7rFd*!m)D@p+KvqRxZ+|4dp8@Lp$gFsafLGPDCqIbC1?;E< z7?%@I7(I-xIIjETNPK^bnL^C?JLB^Ote6Hc=jr$Y1EvdjA>L}q8UUs&i!TJoYHk7; zQ5jz(V8JYatjY1kO7?0C)Hh=8B16Oq#Tp{~G`zxKqxQDD3*(2W+kd41I`;{yN7$uv zpClq;{Ogd)_(}tOVs2`RA8vqf@YGT9RhB0JfB=rLm=OS9`&N9l0UFQT8{G7-&5 zN5zk_m>hs*E8<66j1kT3_%Rj}5>2N81sb6Hn{(pbfZ11&Df6`WI*U?ESN=J^-T=*( zMR&wE7@*PW|6RPt0BiQ__^}44*8x0WQpIa2)WPtjR{Zf3h0qVoO ztK(Y?P#=o^9Pc$?q#D;cFF)P~nEm65wQHb+elVQ%S2ZqgR{R7@r@nssNAVL4P+xPW z$4@dKPbD0Cp&vgTaAW~d;QkDYQVGE`@iPrj_co4?pJhOXGOgWQ7(Z8;jw5RBZSnKc znW|#(^Jzo&kCo}A7vrC{81;NfQ~Wa)6F3fXfsj9-{=sN2G$1!}91QLI8IkxUD*RF6 ziXV#awC~6=B;mK?yDXvibs+Dd_~&c@y@!Anw#F|OyUDg(p} zj=4V}o*!9J4Dk3f31j*@N&pVKFCoz}vZ74MW+q|=lmiT$mylc<*--&dFeOoIF{1!( zcs5aIF{1(Es}ti5(0X#&%)|te<-a!$pe~-6Xn7I<&rVHDGGKoIe?el30rLTF-ZwGT zfCT_&uTM+^$p2?6!1(!z=@zpTVB{wgwgBvC2dIrD+yZa_K-WJKZUI;haQ&8qcz!{_ zK>#^R60@vr#{gV>U}CnFwg%wP&n5P;m@a^UWr;Z!(+v=`CFUBi4q)uziG7W4t_OJZ z-o!kMIT7HVixQ6Uo&<16b|P*up91*Ol0?FQQvvqtOE_M88o-U$Cgxkr=>UoM6AKJD z17OVhM5_U30(|n_#6knk0yyNj#3F#K=Cc91pGqt?;Cz5j)FiZ|MRr^Wu>JHzn*kRC zJh3dX)C$-Mu(~4mRM60Ha4G4l&?Min%Xg%)a?bfFoZ@e1e#Q z>T3Z8KAUjd{>uQl_a+>-zaC)zK;m#i{0#uB+Y-j@2krsb@m9hy_^$)(^QXj-l$QU` z2LL|tlf+S$=P`h}$0v>^Cad`c0Y6WOrx!H7Z2Vw{CQLZ}wQLK1&)|#{zsUcm!PP0a zpupg0Dt|$>!6VY~jRt#Zcz=TrwCQ`aH$8E&>VJ~9ocCx#{LP76bDmC!!4*6y{xYFD z@mXtsEx@^-O6;;^GX$KUxYU4|0PW`_J_q1d-3Vnm0l*ZxdA54kCU~_sY|F#DU%W^2Xa1PbX z{Crt15qdR~eOa_DHwFMw(tEZtE@oTI!Rd-D=T!_EM=1TnpI8nMi5^# zwqniNb&+{lMztz-cXD1f5l$$$?Xh_y**6y!h~s_lzgSrsi(VFu zD7u)6KmW)5@nnhe;BzE8`&mN9R!q#QCiZT=&-(9)S(C%>lS?NSs)+;Ebpn6!X-}hk za`XHWWYeOb^8KyG`6YCz*rWK!3KhBjLP{UCGIPGTV6hOk_>B4UOyglHha;|8urE1A zzD~0E6HTp6O7>Hdz4T&hvtdsiU*osVFhC@K;fbv?QJ>Up&j7e-cB^q~6JMu%xplUo zL?fGdbgOae0j)7#eYVwcYYp>+?X8X{*8n{EL96lP9UAlFKWJ^SqBZ7Eu5NWad0NHB zZJQ&li9&U1x^P-Nlg>W;`vvo$qvChzZ1GImUw2*Bf_9o&GKSm-R4(YSwy+`Fyf(5x z8v|*1NcYfjv4x_H5^QS!l|h9hm^Kg)4cAUX_fYwMF|*TBO#NYT4Y9SY)lWZBXq^i>$S_4Z33GBDHqP zcD3oIwnY`Hd4)v8mBAw0nhvNLQ=VC5tsPJ^Ix7}gYhTrPU2x7KYwZdR(UJL!B#4at zphj(4{``W&W3*tU+O(U1_*WOKGFs&Ov9}i-q3<2EHTSUU#o{t$`%=c1#Nu4*z9^{n zrp0-NGGTVgGmG;LkVt)S@nUh9$O@r(%U2h>c`lLqTh}cvQUN=J^ZQmUc2iquUfs1= zC`o=%qVnbEEG``xiEO%8{X6H4#bs8SM5E7UEp{U)(P+e(iw!3O>g*l8iw!3`M4UIp z7mu<$#OOc+rAOm{9ip)t@fdnF{~f7Z>c5&hk}F-_>N4>=>Eqi=4}@B;(P4T<}@q%5LX- z)AR)cDg3Vc-h#VgNRU?$Ui0jNpDBFzOJXUvF8DbmX8qM8=DCL!ya5-`{D@3^al!kR zisHO)MIz-Ta0;p^A8`x;y$ie|hN`>-AhHNxoPcCzQrg7wolDt7u1Su`BXS@=0(9DU zlT`+J`9PN+kR0ov0-&#~Nse<+A<&YhWVM5efTlc?j2V<)Y^v4f6`9FV2=V3jA4X?i zO4gueEuX=6^NOTNrUQ{S_~C`g@xI!j_TEoBOJ6HYN<6I)OBk1tbkeCv>Z0!@opdUu zbm*~3(^c#eQ@XGyX}XGsMQppSNt&+0HZITnBRMTwUCR|C*mhM*vQcF}PP&fYBuyfH zSY>;^PMS($mrC&dCn@nXvP%@&`^Dr;14NX)Hztky?GjN=`CHPs-!82IQ!|ss{dQ@4 z`rS_@r6j0$TogKDV{)zvc(@7RszphsB$x%zn9rK2#?7_Wet;a`Ga|A5_b~@#B_)Yg z{&6YWxrs^J^bdF%qEAgv+OB`V^MFoyAgOJCWWdV+y7ld(_WeLUP+mjQHvR)%CQxil zGU?(1pi4F+O*S3yqCjUXOPX*x;Ee!!VN7zVOPK|9QDM@=(*Z9V=((RJ9jEn10=3_l zbgb6P0m{2Axh&gi&jtFz3CZOS$^+WEBYB{ML|7NhP9EeS5mwiZQp675%_5WQUt!kfRMmXHy|YNGYkj` z{7eHv0zbcT(gcCCCLKCr2)}&)jpo4cjA^=zB!vQWmOPk~=M@?9ZkcO&t6RaY>9 zs6&35yuzaV*+5gHSgm;a82;McOkMXUld@Lz>e(XDR@ue4&SMiXL{^9>?p&O_#sDpcvo1aNblf2$)6Ox-&CvPx7 zNM3tTa=-u~`Hb%+Z!|zizVS!Nn+5EM0gS&qd5ZxOwNAV!d8;KG50Jejd7A-~0Iqu| zdAkAA03KPDywd=2E12w81ndyEy8IK#yR6mw2sk+TRRc8fmc5w#ngMeGo*kRK8({1Q z5(%EDNZw+5jHCIQf79l7Fu2Og?D9K>#EF zocx9XhXS1WK=L62Rszg;G5N3os{tx+Og>`3F#vv6@=*iU0>s`(K4!oMfP%-9j~lQN zpnGoe2?KgfdQ3iPKtC~G{z3Aa0At@d383@3`&JK*$otRT>->&w1jV!sN>g$hey+>hfEYuc%zpULWz*lRvRmW+~6E zWh{Hi?5yOURmQy(&+70OMHwHauD^!qW^pwBZdTHZkT6zth*jo!u6(U{%XcTe3sp|6@8Y>9($QckmHq0YMTX79pl6FB;9u-NhOHZoEJ8El4?pfdGcV=QIC<7S|T-%hfSUyHaTomgBn#WT&uH< zgE`wKlXHf0{)Nb?ze{>1cUmPqwR!(YkXAfq)_8c-w9a>u-w$4P%bnE7YNsmFT zvMT8C+W3<_}Uo&yVfVYg$9%`m7B6Mv|Fm}6Dj9rjAo}% z%i7GESG!W0j@nP7fyOEu7?aIhPY(gU$-c4+AYTwOTs2x2JjjnySP?dO(B@0_i!AM;N z&3ZoBT1K2>Yt^T=mecMcme_TLt%XdAx*5moM6|V7l=bQw3i-V_%C>hWwTkjlY@igs zzBN}`m21RNvi57uqaDTXidD4lZgrbiEyKM{tw!`ain~;IrOG+>(yZ3WD(4{*%$(CY zMFV9VcK*YyQwb{megkM`1;l@Aj&p&mtEP~3%ZMW z0VDF&fk{iUsAc>oC~J1^l5Dgin4kzKT{6-DwRZL!OL7b-G>zC2o6IXJiJA5LB{r8= zi~_jtw@Yj)uh8j0)1gbOQ@g7G>Kc|v=~py1roP1=oxemo@9`H9)%uksrKQrnsMb3k zUQ&kMV*CXX6rR6%iH<%ZE9Po&>Ru=3+0prJ(iGO8Qi08Aq)lyi`qYgrZBA1tQMY|} zo8v`7(L?vQIacHYocdDRpa!Aj%C1Q=dpt;@xHraZL?IO^!Zd~n_z3xw?Amx$J#9+g4Svi><$oV?OX$LU0vc(6CQBb zV-GW7w_(qhCjO|Wy+~UNC|T8Mc0~; z#fVNk(%Lj84R12IBn_Wxa84S&$l(3c@KpvMEsFU3H#W4jQT!yewe>4)OQ8rijVf<# zYbV{Ln5NOTquM$I)X#Neb%1~=^D8h?jvzNNrt0T+%k6CK0$MlX5Zd~f7vZ(kA zbxYk;79YCo(@Pyc6(1V-(o&Nl2C9U!C5vaai>XX@>fm-w(yRr{qP3CsO10os5&OjT z?QV{$Ih9TAZjKgJbZy-nX&*xtj#DeT)~<=PSFsFEo@T?Rm2Ywjh>A?DR#jccZfuXK zsw*huhV$EPjv7U;9MfKFT^B`Py`kOasOcx?-qt?eiWfs^yR^OD05OzXcD74_HSwZc z>HX>+%B;>zFM3xf_-}M7=bge1jmH!%Z|%D~a;ZU0;V2gy~B(}M_sCcFUs%ztl4kxu~J-c*y z$83ucIv@H;hvSr46`kCz>6l|NT7lLr>yRuLS)mnZ&bu8Z%b}U22?zV*JH+y)9bVD9 zwm;G#j_11j)n_}z@zUKD$1A8-6R6dlQ-|FZ#~b^B_H{R%)3KN-G3{_oqdQ;hNDAmF zgg>7U=~yD*m3Wto>G&kzbnTeeztpjk@39}K zqAleehg&8USMzv>DgW#ck6N7>k~$43thQv?6)YWm6#b^*Zp6fW; zVk8FaSK4um0Yw1)S9ElW8h4Ze9R6^}8jC5f*xI`(($QrxqatmnzE0@a!~~swGmLlo z9oxvUn~%rm@`3JG)1Qf5Jh5R^NqPpq0|U4_(l8nX7r&bheO>9^Xc$wJ4*4Mopn*t3 zPBSBvvGX2XHc~AaI3M8d zKQGHcerlY#Q=5^7M?h}FBNQzHZ5(ar*bxbl-8d6GH}a~qJD5d;B6Z_vki4RJd9w~r zcBGnF`~;ydUa)+IN#yC!#|eG%!sRoEhdxH=MZ-gH9>JQEyF5wPvlf8jeP3OkF_|Po zhW`qJa}HUaF=cqp&)(=|Ezg(+Dw;Mg?CU;0vOHtDP5j8hjoq7p8i3GQLK4qzU!I}O zYJ?pN6*Ym$rqc|^Wf4fNW%crmCWxSEGfgb|cI`OA5WK@>s(2^$S1zgb;-;cV2VY3y zZ$5Wmxk7iRL(eDlyax}QG(7ZdLMIj;IH_`2+S3WW^2r159$wvPg!aS^yju!@!A6`) zBWf2M=tYMo{uJnYYVL?~5y1c%i!ZKeA71rI5a`!>W0SROX?_LD`|d51P3$od;WO_| zHnGR}%c@zEZJjmQWI^X-TW9U8e4o!vHnGP9{Lh~`+1A-j5@b)lbF$mPjI~8*vewyx z>T%-u7k;X6@+h`*GsKi8yfJyS5x=IzlxHS8U5NPlyfu?e6M_N`Zgb(MCXZ#LnqQet z*`p^{TgUi*(Ezqb=Y(`4yDNW$Kbu!bcI z31QFD31LsC6SB}G-AMi-L+8G?s!p9#b!t0xs_uYzIbq;H)g8?C1o~>CdX~}F4rLL) zG!K3RBW(2Pi2nf3cMY_)$EIgOCYgLRt-q}|c#i)ISZTU$ivlr(06dd!iw;AH2EsYG zY>sB@gUSo-&Y4qe z6H>AAj>olMl1|NIj1lG4noZ3rx!B9QWmD4_7Teg*Y^r!UH*5cnO$<{F9BS_>u&LEz zuw?Zyv-%iWZT@SljWf@L=Ob`hPP0Wopke9EvF7i#NRf`6(Xi{b&Qc!~=(R*!mnghD zPl1*WwRP=_wRoll;N|CRQaP9V1Av+CBI+Oju*q2tm-@(c>JPCtPDX~MN6d#bgVDGW zH!#!1MYbUXo0%zUVJITUf51!wvu&|bzRdLSMq8Y)L!AO|@dC)fvU4^uB{f9@lsRo` zPC|B;{mZ6SZ{j7B@H{A+cek@0A{*xsKI%=I>=TIV`5vR~3DT4~RpEbZM+js~ZO~ZT zQ3;_kx{GYb1aOk^&=uQL0%(p+{l@mR0M3Bop0*tqAQrh{S;+eF7u(rRBJ0FVgd=O& zAZX57HV7o^XOnHRCm`;}4YczwiN=T^xw6u6c9k2PJ9J4^OHK*?jy|)krKta zQOb+rphhp8*wp{|)>ZX5X+bwB0otj)}+arLEE0Z3!?G?ZRE&Iy$fPhdoj)>`P zZ7)KG;Uf_Q8D5eguMFIuQCd0eS)1(7h-yIHWPip+)o3_xll>X-^jX@=_L`)T9gf4ZE*wTmkAq!~GC@xt<%7nCE-UCP`Ql%x7$me0|VIjS$FRvep|qxxd8 zzT8z}&4~iN!y+Qn)t8G*SWpxylo zW)yS4K2$Oy?dZ1lSOFXsvU}Ta4+}}N?Hm^Rp0*(5wTJC$ScnC9VvAi33&R+aXjj8R zGRx=5bhDk^;mExR+V{16q|j#hKYYlSnQJ!G-@z{UfyhWkZr-)RTlev#(OWqU0$Wx zt5v)Rmm4lF3sgKsd?CQ7_k2xs)R)pA#Jn;;&up&+k9k=r_q-|VN{>o zj-=onsdHr=DNW|Z_j6?#Dc$jz`dqb&WOsb@)7%J2!y%<;Q?6=r(j9y5HRsVfKZf(Q zqsQ_jk+g**d!8f`?vCD*hKOyn^36C^#Dl}EFSQk=e@=4A;W zlS@9$n;?Kpy6?)HD1b^itUhm&04il>PTphz)c%}tc~b>Y`#V0DH%$Qh+f%3WvISTG zCLYMM35W)WxRRG6pg%x%yF9yqK>%yM&&w6SzGD2Tyy*hiS9I;1moFe0VAnl)1p-C` zbZ?heC?EyEa6PX`fEA#_=XnkRSp;9_%@i;hAo-EJVgcCz2^aH90LFfj+gz4o|087= z%zc8eZ_L{C2`IdAG}0{jb^1go)QAD%#T79xZ2CL~O+%0&V)}gQn&?pS;pwW?v0T^m znyy+M%XLHF=?gKP3*9GeB(%&py@nL(6opzJg`TfXS53}exOy9L)(ThR+BvQcCXxKg zo_bgKHIG~CT}l~Fe1X@VK0Won$ZD*4h4m2)wf9(4@;Uz)_iF?$=+N>bkTQNL)$E?* z`J&?utdEs*^E-E9ELK6-_xWAAX8lFy!|{>XclHYK+de0K|W620$jgD4e(qz4&=PmvWk|H>a`Vg0MDz#GYS(jlRT5 zAb%h>wi{IsKJs9^4IfYc9goG7a2bc_H8+I8Q6X^Ph48p2c%WNTxC12h>{HS;fhZ zuwE!m+D;4jNr0D=n*iiwmNDRa*cj5@Eyd40c`UGipM#L<2AQ1VZw&Y^nfwaN{1;_Y47?QzY*r!nd(+dAbK23u+gsIF?kMaet9I6P&h)}gXp0G%$#Tk| zP=*)OH7E?J?^#IgPk#dOW;hC?d~N=)U!j`0v)AaKQ>bR{Y)VOw7IL#kv#~dxU0kRP z8SIfxe_f~y8C>U7zF)}wh{3a(_8xgoS^I6#SODvfawH0>rDX(&N`_L-tF=Yr7RaMF*>)s+-Q!_eb1{P&9ek0Iec!! zbJmt3JD-!Z%-%(u@n(D`o^eG}(YaWAXwdtNdnF5d-=aKb!9{)2KxYv*KQh^?q#Z3P zKxDO}ytt^S5CxLBnc5BpibVCl&g#HZj9AbRx)(x5+Wyp_9;qM#%^ppbwpl0OgSJ+IvH}aRrkxHF$hC z{C5f4M)o=j-9rnI>3E+(IrKHWUG>+YFSN2JhjKe3Z$Y4G7f5B%J@)J=({3S4IZQ^= z9sw-0<=0Gm1+bZQ+-rJ309AhQlcom+NQVH2W zaDwnTEoJaf5S}mv$x(L!huma=)xv^jOPyhq6+amF=oMRY&JzX_*~1ESFHs|QdE`zdc2&u6*WPk@oJZe z51??(QDqQy(#HsV(GlfFe}SS)^M zq2=tmHzpSSt1-^!F%!BB*+gxOj7dZnHKhsJtWS#>9?0}mk=U~_$pWY%cT9>IA%Goh zMP1BD0aTHs!!e@-u-I(}V@3<0ilpw0p(S6b$nZ`vV+Bw}?%WwOjzCNfJ;ukR5Xer_ z)~91q1#qqx+94)QqL~2Iq>nQz7%zawLE-)0m)H zm}~(QW@Ax|O@LRJ906Wo>;k;PF7Liu7NQ)^E;4OBi057L50p#?;7cr#*$myF; z#gqvkr>`H1aSQNrS}wrL=`4aKW5ndVsaniz0Ang!W_47IM?%6P(yeJ)%p8EhzeFNr z%fgrn3F!}TcSua7fI*z7z1%&f3Tv#X<&bClGsc)|$}YYxV!79caP zzW_QJIP}G!fa413rDd8!uOYdI^mN?&2vo-)_(HQ! zT!Mh7q$#%19*1hrTW`^q;@t-z6C3Z)@4$L=+8hqN_m6hy%kX#F2g(9?5|3ZCNO|xd zv3dh6G*jQv-POp2*N{wm%BZD#aD|~mUx}pJNlW@7L8}Et<2U4xLti7Pe>zXdIrOz` zX%7y(yIYSY3UZg|cAY{nv$d?@YMnQBF zn0(Hm-!F)#0^Z!}&^HU>Ie@S`9r{*5X8g`P;n277zQoI6KzD!T(6u!6aO z6{j8gBMKG(cG%(24@fS44#4{cIP}K=bLRUw0ZYa>be@*bPWm+hM%-}dhZS56XkX{h zpHy%i;DW&p{fL710=^pL(4SUt6JV9gp&wWIZUvkMu1=`Xoq&F?IrL`~+w}|{RM{Z3s5?B8PDqe3$?ZH%?y;Q6Ax#PbE)1w$uu8U!otX815N1FyFfz>;2ZBc^nVD{2LrzHtwaAph4Q}9Glw1emnv;6pcC!mp8`z@fG0{F`d2D+ z44JGlfyuAIWNs}&4Pf${j$|^BO!_Tx=>L((mO#KoDC++TH1HgaYnDU*UZBo=jhh_$ z4+;(i95~#e|0vLpZ1^v9=syW;LpBzH@=bv_0tq$_XF`Zyz{d0eD9DlMV1AQgWi?JJ zbLhXLSnU&8jfPJg`Yj1#H9k1iVbBDz8eiMxFzA9LIgDe?TT<1%!1Ydz1_iK2ov1LaP+J1$`mo&fde>hF&mI zYY4mV1BanER>K&+fY1yLa+|43SA&Nknq7g5K7rS+It+aT`9Tk+{pv9E#ar#9G=sNZ zb{P5z?8s(tB-vr;FVHwVo&KZ_!vHeiGN!xXspBvV6x0q;o*(Hj#0cWSF?8pKL4w+Y zOz(pZ!(bs}b^;E(on##8w z2Ch$32V;4_VQ>gCB5YP4hk=tC4SXPB>Uy)o!0|nQu^LYeGQLaC^u}Ku1`hCAjBjAh zUoCm6?VW!k)z&}Y1A^OX&+7gT!wF0+3I;5nv!GTpfMM;Vj%R$m>6G`m8t>8j^Zo3F zcs{M3%lM3*pYIp(`HXsA%;&S}nIpR49G@eXT!}DBpzh;Nv*t zD;Qe_b`QXuf|2EUj~?qVyok5Q(`PS4f|rmWf3cQcM)0zL#%=l{qE~=&=Ib0~_RMz} zUKP^11+c^O4g-}Fd`1IyeBEKVtYCk@15ldRVCpIufVD3(eQ~g%z+rd;X3v2t(>Jl+ z$zQE)(MS8+g3WThmB%e0*>u6O|8!Z7XtSJ9JqbU9|ZR&ZI}%pgGW3+BFJa@M_Cx6i3$I zyL5m;7e3w0Sg_N9x8HTaH(R})l)(iS>^lFcYcS-&=Np^fa^oYZ%l@t&RBI9E=2sKC z&Ox3-&T}q1WJK2rEUrWM?c^N3dSka@I?@&0g`oLki`zj8Wr1CLq1c1~wV+PhSlk}K z7{_V*%-~|0ZHn&Ve0

VzJxErImNxEEc;>LlfJq6I!3Id8dMngD@=?+IcK}k;)E89Pf3B3;%)AkRu zB0$q?#RFO3O%%w|R4l7tVcR~uSUf$1?V7iX)hd{5&s$bJL@J2~YhwRZOhXs$0*ojv ztJR8SyPzRF!i{IyEogv)4Z7T7F?2O_L6UuG#R-zW8^GO@i^b8Sp$E5cdd3asfllo* zo}2K@Ys2~c(7NGW2J-ni=w+v0hU~cr4(7di~kuiPXN{8tz9AW1(?}(`<91PqZvA` zSunJI8?q4ghnd5DttJaX%&8Yg$95JszT8lh@Dc<`Dfin4F3S9Y*~O8KM6UmP7Xu^#7NB3r$hqPmp%hpaI~h z0fvB#+~g3MwH4PR_O+`a`mZdr%iL?2`9B;J*PRJ5801|fP<23v!4ExFr|A};!xuvg z{#dJ;L;BM!JUK1I5P%?PtaRXs)G? zCL^OwyF>iMG~jb9|0oNuO9q7a(MWLH+x#OdULVWBYv@Lc*C~tPu=QDp{{)on?bpE; zSmF#@^wG|C;MPg)n?KfR685N#o6e0#XFqkyRZGlQLB!cB2=7wlB` zKm3(;!+8hvKquYXN~;k!?~GF%Wn?RCmDQ=bP?~At^PF7~OA~L&%gddr3uW7V;5Vo0 zLMcb^KBsuJN$(sx+$kPya>aA!U?-gl^!;pxn<|~kseoNhhTADysSP~2z5sLIa1Io%`!VFC6Z0~N zK%r~w-%Xn1>r|LmzO}{eE$x~hGYUN3%oN)pMBgC(_Q*F4i^n1*7 zCP>IofH4l3 zPG!@!0{C@xrVD`#fF9GGJp85EG6CYoIMrswcz~lloS71m1#t02=Xe1V0A{`96l-_W zL;&d71ca1UPi@w-QW~DLm~%e7NpL@oEL}buAqK(A+x0o~wEiwkyh?wFsMcI#!uZ72 z(&IkYSgt)zvK>8;>>9^1xlHvb|I(ErfF{)3Ko_@f%i7VfH)Mp1+qa?nEHpW5t{Ns` zU@~9wUFpyKu)}LhjIN1bqbxBGokN6+=W5I9@%-3JE*`5bTg}Gtlq+<#s{}%o)q{4_ zV=gChE&GPd&5v_&C#>ui3-Wk_s}$XqJ7o0~H2WE@GO+FLsjp^!Wr@6Zarb0C-ib&< z@9XcHMv~t|xbab(Y>|T$S3Ts)f$U|$6!*>|mt6oQOxWSdm1vO>_)L=Knl7MQgr`<> z<;$}rV(|jaRUpsNQrwq8iQPL_E$A0n2<{NUA%b1xQ1C6mSZ%mtOBi2yy-s>7(6t9_ zy316adxbW>kbyiOkY|6%^Feu*>#IhL2y;EmBu66VL9$1@DdsKGTnD@<@Zl`Y)ufU{ z%$uvZ9+hXD`o{A?c{a1)+u8D~U57xcybSXDZ*m<*Sb3S2i;se7t|tf<2c)2|%_i5A zM0W)ewHe_$D(VwLRAqA=19F#zWC0C%&-D}vqI^$?11R^f>uI9HA)A2A?On%FHRWf_ zu|QCV6X@;B-wutJfNeJkCYPm;U$x=sm~`5vGZpSey++2&U@mvQ-GDMNju@5No} zMT76f9w~Kokary4x?{X+n86~l&hQH)NQVksw@$2Klwu}<^MKL!zAGC z2VI8OF~=|e#I$tIY|Z7zlPTq&2)}5#T_t5(#;iH(@{0<>yY?sp!(K~i5DI=49ow$% zEDc6U%sNf|Y%`)XL<*Psx#F`@8Hg@ZKX>mf4V6*}!m#UtXDtmYt2T<`6V+ zEKAvH0D+%aD+SDAwC}A| zLcjyyUTT%)h;0r)N>{5aM{E@U-LtK-9I;gbWc}M(EorI%hJS8dAYd+l!Df}+Cfhs$ zhgJ4lZSw)@k60HoT2nPwL|unRTkD{G70eadSKm_m6!2=FfM)Gez^i=(KJ6p$X`e)E z);^(5mx?6w| zz#3)UBY-obmvXE!Qj3oVltYGUxn$$1`>Y(ROB*A(#LJ%?Z`C<5t)jIKs-h29B>{x2 zyvQF2U6n#MF8pxEuouxfi_S_AN&^VJl^_%c5V|WBvx!SY^j9h@t{V_KEN>VWjCbT) z^<+45YYovrdw@m|HPL|l_;#y4lIX@jvh7$n*{X8_JC}np*_Lmei8~V6R;Zb6K|Zz> zYG#}0HrqtE*_N0-wk6ERwhC)zn>QpVwlOB?ydptsAlu28t-3ht-{=oic&AlY-ukgX z7YnUA-Su%94G_gu{`x6E6jvPfZ&*1q&@V#Lmt~}u(PhkE{WIuZJojf*^|h-R%37t= zBSTrMIGXx@kfE$q;>UaTLS#lX63sh+px1xT=o3w%OjNTpL#?Q&Q?KmGP%A2$VD4O+ zA;%uXTJ?NRhFVck+u|BB)QXCYA$v{+S5(GS4p!^U8C+3?^rTH{%cmJ?MMYEE)6+7B zAVhti;y_7;ngnpATWQHqlK?U3oPDEf23Jn=xmp9Ki9Sv@_RLTVCvxh3Dnl)t$muH& zX3%F@8H?I{lR?KIO-^o>L}$=5NckwGF3T7tAzUP!`XNJk2JwoS2d`x)&!8?ECk{)F zBK=8s8YT|&=U|$!r7I^6Z(BHV2!4(ehxYz{`1Ne1JJ!RgbjPZHmF`%@U&y_@1^>3V zIlrHO6~uQ9bK9^SQTEfB!EQP?DZie%Gu?L7@cefv(V(x~xeSqO!dI_z(~T3?;UT0Z z%{^VBu^bcjy7MKZ8y)H9&%x|y{*CLr{i}1=Xg4=AsyQwqQ#woeGMy=aGb?0T3=q1{ zn`wzqW50n+odTHY@GEYY0A>mf=;N)9VuOP+36Z`&eQh`QEati$@%p^t7K@6Q4j`)7 zQN)F5RGeEmTQFD4H8-6t`puLj!+kFs-SbF+(&$$7FAzY!L3^P9QUG^ijT7B` z=#qOeLaP6!He76Y{NC4_-uGvJH;2Oo0~C$=meiN}sMme%z5`&v0DqaIgT{hnAB`=k z(e67*n{}h_cFY>cyW7>SCde@_en~BZM*Jq&C$Z#_hY8SnHWlROUp!CeTl&D zwwC;E7Yfvb;^pog0_1B23%=N{FP@_r-Mc~ZZbfo$OOg-xNTQM+6yU9-eFA6{DjVqD z53mrzgo0}n@WNfHfI)n|=I?$4Z{9#ekZ6)r?46)Pj|!j$w0X{bkR;yl)zQKmzB=l; z+T!M7aS<&x(BvnWOeWpKese!5z^i&k0HAt^=K0ZmR6=Nh>etqN3}8`=rJ2D+BYo5# z{?O$93#rc+w|V!wOeWQEIl_HiXpqC#M!Vk=Ag9z~wwc^;%+nSfP>KH9GSP>=L~#q; zAKgyWu5EAkr%d#oczC-%gHO?-kt_=c^|{1jM~f!(H-LUK<<=k9OFi6QAkku7CHj(y z<}(rE{Zrz}rAZ6E40r#V@zPbiuYJU?=DNS}5#Led{#HORD74w-{*QnToZ1b}33vZW z3YAKx-xwqXQGdk!J3v3oqu)axI%RcRe*~R$>pHs8#U}@0hD78_fsy0=HncvS%hAQ(r1Lshw;rs-!&tfYf?E#>WKDDO zu`S!Jn=KSiws+6ghPZW(dNuvM3H!84*w>dZw|&%o>m*m?{{(Q9L#EVdlSaBN*M1;c;!pCo&ePO2T7Rx&oH;Xj0vxK6E0`M zk(yV=J2|;)pp5?YZe0#z-UwvjTrdT?^_5KcyEow~U&5m6Zhf^%$f%E<2zQG&SM2~V z)pZicJT~-o>+3;S^-|rS-`v4#_FeT(G5*_dUmTi=R^7^Z>V`p&Iyld!fVW84|%)_0Q3x859g`Q-3_ z*R89mE(_qTG`Ftiy1_tq+;r=E5wkXzw-#C6ck2%bZ(T_=^`owCeLslSWq64`wyLIPq6cmhz2=ttN9!_t_x#o6s-6LPY&qP`tv|**9{1+)xGxVfeyCL* zG-2VEdN1Qo`0{wU)U7`$Wh0EorhxHdB-&gWPqiZYbSt9ATM<2>h*JK{wW!8ZAhX2o z8(7R?&}yf2G6^W6Fi zzAO$u;nrXDWpNNq^CdwYNG9ed;8#iJ6ECNiNM=5Dvt+SbzbrYjrbzTPN!T7}$OyOo zx+GH;O$;kbzV+2jx7aAR{w9L!o8^DY#~kbCijO%e)!RPis8m;d@>j>Y^=lGF`Bxut z>+gW1F;3(k)ZpjV-(?o}B5>qxw|<>b8_4*$f4cSe5Cnq}P#;wB`x4RjDaHCdK2{fB z@^|ZhWiZ!b%<@CYl5|#|a_b*q3#$GV8Tjh$c>J?3<(&NnxBefbT*|oQ;5#1&O@rL} ze*6zyi>?#tzJa6s;Hm~ydP_6g-2Le>0omqg=2#X7QF&I(Abqpy{hNr*bmR9+6y z(2I&XHL`rRgfs#;SCq>lwP`g#b#1vUQk&K_&-xqU#MUCZv*@wL@+$K81ENCqxg;X8 z53-cc6F?0?LChDx7IO4dxvXI216n8*S;0105K@1nTvo76(Q+=foGaMDzw}2)*@kki zU~xCLoR}%E0WhYHkSTU~t%RiF#+pT1d7XeXq>xpV<%#!9W7rShR#B+{J!US`CVcuh(!okT!UCXvDOM0fsn~3 z%GZ(l&J;GB#x}CO^E;xROx%wbD^?6bAG?ang`7{gTnK%i%Z0UHa=CE&PyYB_pUCAx z)`wg!Jbr`A1)JT7-`CHy!|xNdCj8zR+#bLEdj{h7q9q8wgF}Py>wGr^zwSY1{BGG9 zieFog4){$P-4VZg z_heL0{01(w;P=puUighH=#AeYjwt+gFOKGx=E_UDCGiGIVa2B?gq3e3{uNtVD?Zh7 z%WD^Fi5~)t`cl`htGFj8@gpF7$H5FV>3}4ui2{)b3d~OY6sSX>E`!ae zFG24+QD8Ymz9-U<2(yT`l3paS*AxFPHNb^ob!_541n5+XEz8#>{*&>3kQ)&azXDBl zzeMaWiC;^0)YHOG6TcBK2w<%z@mm3`>M<)4|3e^qL{Ghv_#MEC@3=>_{q4m63ZO9} z;Df{;(Q&W*A%z#%CH}A)2Ku}&Y>Swj=`wt%P~9;?{@ zbE0lkaM(7TS63zK?Z83kK0O)1+h!!{CI(;DQ`jOqO-CL(yp@E0Jo1Y~-HaBrYAph_ zxAAbo8;DV3)qy9#+iT|%^+?!iR-H(>g@1^~e+XbzYJPn8|N4*Q8dfFhy+Cf zc=iWBuY=zmU_WsZW-+~=nYCS>v#=oVJi9ZTx-=K~TAhLyLA_>o!QQZT51!3;&hCoc zW$h%MOYfge-}2Qnp;~it1838-d-cJHsxobM3~0g;A)=~Un>|RL@vVbWZT4V!mdh#U z# z{}LHb&!xMv5EtJP?#jVkhL%%Wd^Z8K2id=h?=FCS-fQ2*_YlCL<;Z||aa@w2v=a~%Ffl3;eqkAbgwZh8p>UWr2!fv6%Bw( zuJ};|vUBBlB!0928tZ3{jOR58Ys_*hw|)_iJ$=+e+nVFkNIh3kPZxq->Q*21P*Z#c z0s1kBHQH^CpU6b(Ria5P6HWFdGCm(a<#wXBhm7$#OmtqcZuccR9~m!u%`)x>bdTp= z^P2O%ncbQ<6t`!g#G}{77h>#M^8s7=n)T41uAjs|jCQu>UwCoX@_A@!6XPEV;$LU{ zBi)B#KzP>@uhZRW?Lsv1tf+WhiN&SBn~%rqB3BdFEsy;YuV)HZ-0|`>#p~k*@sL_% zYP_B$h>M2drSbX%L1A*@AYPvc#0d^E%#4eS*SQn0_RomQ3}yHIhPpgAUYG6nCT0O; z(Q|xR^gbA`t3qkcLX`^FB`J&J^*m_)+CN9KH(@2N{ah6bavZ+tSiD|<9M_FN+^Y%k zdLddRoV;xJzB3_>%WA%QZwYl-SL-KzNsQw5LItZ=l36}2Kg`jM{ zt6RMO87cEnBl-PY;*;{ux$*kn1hMc~B!3q~$sR?S|AXj8pj;UouYVz7l;zIh@%op7 zq`0hSki~b*f@`DYuf^;C?L~mD6o1Cz|C`b?%0xDy-c!uZ$CZT&n&>HE2s@%RE{~JF z6g#4`8$B+xChgzQ)KyQLJ)HQgPl3|ap7y|xTU+;cs&~8BJ&gj`BebjctPsE^JTus{ zQa~W5&U@bQtj1uz?srftc+~9Sd}jT95%X&1YM%8zYHKfe?rur#9v`)iM$f&ssl^=f z+?N0XlV_PdkD%LHe>M+eMWd%Fiag@^sZh_O1DON9f9H7vyOZlb21{{od)|VQE6yJo z>bXMjzQ2|k;JJ!8cPAsxfDN8^P#o*tq;z-R?fJ6+ZVNU}_54Ku&D9-?J?{$e1L(2E z!z&fny~#otdza@u0c~*ij^}l7clYE{vdEv0+3t^}FY#=G{P&zg^!g*7Ek6010zBJ& z^0)ucv%@FFw*d0GHnPu5j=o)JI|m{sI?Rsg5WRj+u? z2;gWj?||p5fXM*CPR}`l_pkOR0f6mg z&x-_qcLE%H*YlD9H$eRf&&vWl08>8pynVJ)oY820n(y$-N6~mYpm5G;bhj2be`Kmi<)dLOKyZAP&;UQWLwKsZg^6YTSjZ6*4;zN9685Rmki>zDk`ixb0vC+&Wi>`llSM;9No7 zSo?^%Vu%2?*`0r?7%G5mcH@YOSYH`G5m*r?Ky_Rd@c_8_lNsHw87t`MaUbVZ$Vly@ zQ+MJnRH%J)b`DdcD(Hj3Sl)r9yx;6N<8Q3s{`q|y6p_)w28n>%F#<>g+>RAM zZtIU!D34@vd+g1M6vZuh>U7Ll!J+uR9}xtel;bhU&G%Gf_{haRTp{DIx(H)O#drx} z?T)!tktKjG03BYhm>_^vJ$ge0rzCIzU`FRp_@xH`ZtQ^|LqvL831UVsypwJuLk+S9 zT4OY)Q%N_@m8EvN(oqf9aMo(+N=GT*l3&w<(Qr1-rF>Ptr7IofP8mMut*nP)i@xti zL{i`X@fmzKs_*~Ep>dlA2 zb5>MkTd5P4$D5B=N_oiSD>tfAHD;<)!IVnXm^m{(d#qCYAZ2Ep{z9erL5hD-MPp^K zP>V#g$`>n@X_YN${0Ehx5@G=;J73vB02|N2s>+T6*m&m7sZ@qkcJbXmuT%}2UHo=q zr8I1Dj(=-oWu&BG7ynjNWoH5G;_tsvsb=~q07a`SRa>?K%zvd)&GfSXs-LY?GktdP z9adDT$vwOH1^X)HR6-Mb^#$iEdkblHB%6U2D1*V0lpGFaetYUXHCy6tERwh^aD3z)pbre^d?^ zuooaDx>7AB_5(yeSg95h2LMh5Rmx(b=`jG;x0SM(XnF#`zNd1m5I6=f>G#TU0!{!d z>Q$*)`m+F)zgDJ7$XS5dD=WFI*nIdRz^loXYQ}vNApLr!nsEQh&Icc-9TL6^wXst8G#jF2gXRdOiWHoCbd+oHaII$XMgesnFRs^mNt%?#r0>ewIuT+Vsy=8inr*R`X{};?Y+(%``&eIU zB#856AA7wjUchKX8+fTIK|qR@xvYvMw&g9nM&9FmsgZYzFYm$ss7jRt%zJooRhj_m z2^dKiMwV!^mQ@8}X0){vCW<>vRh`i*Y)r2iZJog+`#f$j1Wa9aWoGRX8 zw`KJLP7A8WV^+VlQ*+^L?IYd%9qcaO?AVBl0}#prns~XYJs3u9U?Q^REr0J#RaKL) zr@ys|bKyNS&%ON!&2xe0Xr5cY(-$j;vDV;S&clqA58n~=Qt{yyi?5gZ$jmprEw{f{ z^^uWphFGF~ae@LzW0**{^o6`FEad)p`_`wH*53g6NLz@$;y;#r3$p$mLdKO~>C)G{ z-y-+1Y>UUl=4ygPU##XnKJ6@W241r0Hw3Bmn??Vt6qTG1-|8P=k;T$BUh=`HvRIP+slIJ2I*oE_Uoif#MOS7y zeuebp6Be~tYLKs+KKfOP#V{FhxABM~-eu?NSG?G~s)cS{e>gp8?po|Mz03%Kt7g=WDjDzS$csC~N- zK}H&8`{W?YlVQ=Q&FxOq0W)5y(#`mbw;dVbY0RHcDFwlTJ*&tP+_TCY_iVvl&@iN5(GPx`{jMI`G%Ok)+ z76NuG*O0{86vmJE;aWMivmHb6Uyc9L3_3j(Hp}D{%y8n_8pwGWF^(rQ1%7}m=;a>^ zTL3NI;j5CsF9`RcbSM8K?06vK`NIIZJWLO027Z-cdN%>cHgrAu2Y;!PAnro#_#Sq5 zYetx!9mRm=l5LMxtF^ljrpulJN$xxoW++9Rto7&hUNHIdV^-6jNa~ILXICi2d;U<8 zTm;d?uDCokN%fb%!YI9PeiHl3UA=Tzwp~lKq`quXP5PDG@-iZ{J)n1T%iTnhbA+I8!As{ONqu2B zK-l*{S2iW}182rm{icjEHgYM2^OFy&{hznA^ZEcBYnh|O=4}+a=TK}&8YALRQk20U zB^#~w3)d$NX1sdcAE<9}(h!E-(5rJ7k}H0p1!zg`!ni~rE~akijX=YIP|`r_fD(ZE z3)%z}4>VZNPM|oTctQJtVu6MWdMtP02mrJh5IL5+Fc~04z_YmvSw2G*Cl_-Uvh0Sk z25#t=au>2(hq4B4pmr9r%!gV8T}K$|V<>CjhW;_A5U~bspv)Jt9*439Zs_0UE@T}K zWewc8U9P*;_rHE=F4x`a{>Mt~0%p-k7PRZ#PcE2~0Qvf3T-rHu!JH(N123E17011j zC^+UO(Vuzu3mAKLLyg}@UliDxR_~-?c)y3c3)P;aLG8XOj7e3 zf1&8EY&7PURo7@qqlC)uXg!e0#*h*`#N6Q% zW>3DnSpJiC5$wEqeawYb7`-J3mQQ0gLBnMTb==!7CcwYLd^i(`p#9e&@h0k(R0;Y zGZNsX6LZyEQ+9&%iy-`~x7||=7k0LSgzL~@+VkM_NbNkN+Vg8@qSbEAO++01p0tAO zbnNIA@QvDEm@qYWa-^}7#XP~^q0MNtrj0Mr+Z+7#87tb$Gk(&HVS}t`dE@n+9_Mza zbMacI4l{P?!{%+JuIg3moTVBg)s$A7Onfl+F0rST%vjkqJAFc|u_SAPH8Ayx$6 zW}puT$xg|C*P=&O;c)8EZry^4$v=Z8At(y@_wlV;7f|K=cd>O)}B`m4Lo|~#w^Nq8buUt!=CbM&Y;Qd>i+7@T~Tel8q`-AV_;?lOd0<}+mXmdb2 zU+K$^U+WE z2!jN1R_%bshj@ADTw+hKBHJyD5SY|e(+`;Z9`ZY&nftV$-#NP=E&ot&L9Ro9X=zw}f-_@$@ek1^AV zU-RUyL;ad{nKWmt9GsJz4hfaww};BJSRzfG#_QEuwPrT-rfM(5MAi^cugzFFd{Ua# z7v0YOQrzx3WI+CYEFbnN8pTwkXC zK2j{9{|hOzDQLXD#BYh8(b&6D(r5UmPn#0gTsm!Z)gd%<#Pa>$$lUb9tz$;aPbg9* z$;AJGjLG1mg9`dz=@>AfF~V#I)tq3wtrJj0d#>JIJ7O}TNf4w%AM-5uv07#{+A}7i zXEYuWMI}g2i_Ms52@Abtm?& z)2HS-_5B_A#(MjW$w)uQX;CP1Y}BZByrJAsJLF_l~JPltCY>m1GbbC!!J@cbI?X> zDQ6p%vYC0nmU3dT(}IHlN!c7VptYQUE#>6Y{B}vVm6XkLwrwpZU`shM{I`(PC@GtZ z#MoL+z?O2NLv0~vyH;|xYb_^WOF1$BXd$Pmm7J#5assxL6T@f=Ior3Avwdqh0b9z6 zj=hDPfvw~WY%M2XOF4sD$%%1a@h+MW)LKr!mU3d%)I!eSR&oZnmJ_h0oLEk`kTax} zoFT2{1Z*iM)&ec$G`Et|+*(e+mU3b;hLp{E8`?_F(AIJSwv@9&D>*x~lCwi=IRRVB ziP>ojIXkwJvtw&H0b9z6HGT^@!&=E1)>=-$mU4D#C1=hk%L&+0PRs^d$QjW}&WP4>0=ASBi}@CEMz)eOvbCImE#<^K94VXY6Kfd7yXbA_ z)^Y;2loPfI6<7McF0JJ3(ppZymU3cMhm`2EBqgRpK+XM7*Vb|Zwv@A5D>=KhlCxWD zIRRVBiMzp&OS7EaTglnIwVZ%0<-}^Zg`7QF$=Rc|oPaIm?Ac1ro~`8U*;-COCFhJ4 z(L?<>l5$3tjhUUEonwnN;wO$j@%%|JhIO1^!;rtf-5sVz4`#6K4w%}9B~7rU=d?qF zoY>G{Q~UJ+T02=m)N6P1ov96eC+u&(&0}h;_VD=tU(Z-Mxh+ekc}5b5*)ZqY9LepW z;iiu3gSB>kTrHwQ*0@5!LLx-X^vDF+3kxl=POxjs`D|*GHzBgpI6#&JN0iue?E2jv z#RaD3ITQHan|swNA|2g-#7P-es?s4Fc>zwA?pi;r~bW z@h!5Cm+a&JkbV6Bl6|~4d+Q&w$7<*QS^9()*(XT$34h2w;eW|KVaCd7{*d1O$L#%8 z_RT89B@wE;d2-)AEI=wdti38@vWi!%UGw;0P6fjf)3c|f>R4c>XHT2z(VW$x?P0g4 z2?76H)t+@OY^lp1Kdssc^~KhDzT~wZDxM6MHU`!c6Je850dNdYdj`Rg@!h z-J>SUj1_?b@Kcgv4x1Q&hjb09WiKhmDxVYC%r@JcYsrO|tW$ICH{IbhwbhoE>ZK0_ z;&nL|h?oGDR^L&oYi0X;Z{Ff6t1c7Po!WlWmNI>6Zf2oVf5<3*O@1{_^VD`FPEhUS_OHYm=UxntI5549+FS3D%Ml z)1g-70x?2LrljV=P5`A~1YoYwf$h0ooG~$0XB88Az(ncat2-+elYu6FS>0WsB|w*s zSNBpV8ff6T1zut118vk62=lnq9jNQB1tt}98BpvS3%ENDv4;ZP)v%z0Lh0T@II>Ah zP`XP@P*bljK=6?Uy_7;jGc>)SWiiwu?><|mj)On3q7 zhcp5t;y=8|_~VPotzMW1fFkS$B^?6%b>xNTVI>`m{w4$6QGgN|LR#jOz!u7;7V=X} z*%{N7`6S4eF(nTQ8i!0X?73Kcp$%nB%h8EwEc1fwTx0VZlp(Fwpkcu|*6ds~t&Hqk zSZTrn^K-J%R6WGXNDyQ)jc18qjbE?b!=Axpm@sY1RQ2eO#{y8FFm(cnpxzPG{7~S6 zmGrpclM5{hjRER;W?{5K6X^xL z{JZ&vENQ=4aEx1n&8&{J>al!p|Ks}sk^kTKw_~(?!W?>8VB;eCggk{$-oTfAks0&N zj$LWtUF%uIyBeR`V4+=^{vS`N@87gx;S4#xC=4CMQ{{~$2=*G;mv%*I+AKonc@Fv2{K7atEj8q{i7BaRVnlA}@;r>2=0BCn?%k=UA*@SE6#q@=FnvQN) z6BY+Th@ovL#E=n-gD^b_-6vaL9fuhgQ`o1MBItm9v6;h|e890|`r=Rv1H^atX>1%k zeYf`z917{&E0)D{;ClK!q>Eg$So}O>Yi)SxV&&&yjHoQH*A|PPhkSFS`v;4a0~ZbC zhc7PXwlG zwFAKRGv6WF*e`2EspO8ihxXKpQpp{2qq1t17a+aMcK56u%#yJA$>w&g@&Y8+5EtMUM-#ejBygL+>=~87KM6hJV>O!P^-LrNbl^iTIJyrzNxj@0-_NT@@cJ2 zK!1RC;k7ve1_6xjT&o=LV*!Q@uT}o{!&t55p4vQA>&Y}!Ymei4EtT%nJf#W+A_C@w z45}?ev{Uw3#@bS>>`pxfe~Yb;)Rw`Z6w0qXmfC9FL1YWhXL{F`%QIDXLsxUH3xrPn zR)(LYaBXnU(rRauw+5rCAdlo@iYS{kTdSQTpuJL(3IT!OI^(U{N&(_CJG)mw?L5F| z7g3%oV+m3Kpgz@7R@nf1 zKdoIL8RY_u`Jr~9fINy4GjvldcQT)S6hUc6W4pmtgp-01wF|V^?ywd-`)F)LZM7EL z1LN|k-{ix+C-@QFNAL2)G2Q8&k}j?}9Y59=*B#xM`dD9jNL&vYjp(!0^`W?)SUnG# zlS1-w79pQa@^QV;ot^GkfJiSo;$$A<76|np_Hc_PVtMeXEL1O9GOnPiO505 z@NiH(Eq?S@z6_5Lz@DP_^zcZ`Ku&*d1{mEjoc>p*zu`9QxlQ3+V1zsUGYc>_D!i+N zP`~p37TyiuxnP#&xi#V4C4`)>SQ<_-p6kb=o#zVgNsy8Su=Txgei``mH!SC_1HyYT zB)5Rfm zfF=e!*ReSu1@AiG`_U``1`KKr$i=%^=jB}ndi>MT2rOT(YlBf3--btHEPk$#gU3^N zSRuQ1~Z_X{71 zvhVyuv-8S_-{b9$55oDiw!Wu*Z$HP|6OG|x@bzU6hM zA+vuSJzCE$0gaQR>Xgfl2Jqapx_+!6btLQ8I$6)kneQPN>IN`GT&(8zs}sK%aj|-( zs!sf3ZdkNgDRoik{myR5gWH9FT{L>Rvs+5>V-fEP=;YZ)q(tSw>V@+ub)2!CJ%z6C z>=Jl9o9hz6);T&tfUP9qglwS%hWprhuBk4WK&~90)>t<}0J=v^zUoE_V2_x5xNeky zU~QJUE&*(v8>rYC2DbX1CR?Z2ZJ8|QI!@Eh@%ty|&|%}<;nn6kx`UmYg>$J@T3wp2 zXDI)*PDZ!}nRtv{ZmHu)cJ6WHIIM5oc=SEz#%d8|3(B>+EcA2d9%rYKFucw?kWIh@ zc4{5J&yH`RTw$X+#|syBmG!UVMCjbFijLh&XA$Y-lFqNt3e;nMC+MzDMF7nrDuSF> z7~`&+DL}r{e)bX4odvpw-ac061l{w?wMfT4ZrDkYi+;PTYEDv7p;C_N%1-1+BPItNs>HI27c;pg)^c)xL-fqBfVhPhSZiB?N zN!-NYS`(l1>WiA6FW1-4;Bx~du_2S+B==kFm&d$T&l>?R{2R~Z1M8hEg}?qE!rlWs zs^WVCzO$RU*@Y0wO)x+L0Ro`}Lhrqk5IP75gc6VvddiZru)Aa>geD*&AVopxih?2r z6(lN6r3oU{iVBE|g&*Jh&b@cD>;L1Bt2{?x>bC5mu@oC)T=~43;2q;@CHj~hJMawJ=8oV) zSWAT;`09aUFV8^sceP=lL3B|Pj22q+=IzSAs(rjSmbMOH_OhBRsX@_mk(wg&UJ4Vit=~~oAg)h6+()C#3{?jh^~Sud z4n%kxmT%N+`8Dc|c|#o(4a@q}i@gMeSH7VRu8jIyH>yLR$G|k?)56rD)LsJeT?5o% zGCvAiU$Z*Af>FEfCGdQ{hU&rsQ>N#-XMr`fSC{%@KLhqan7RxSHke{?up9z5)}-P) zjlS3&qtLlG)#X73UEPzQ%b%&PlyVqY%VyGjrmiaQ#jGRdlDfJKvm7V5?5ut(}Te=Nsj-XnEAbn8QV1lX#<>Wlu^L!)r}ejD{AZ%cLefi6)|H~R6kEWvDO z@}s)d#_>XaK#ICe=3!~{x*xqGO@rm}^hfiBC|%d8qA~ub z>^Fq540uHhR|RinKanawHm8cVaDX^w!kBN^tBQ8$^a=Tf<9FUtMf=)-hVrTLqaUlH z1NJ~Cp5|MrBEC?Cd_bo(2GGvws^H%J#Mn4M=lZImv*g*TD>qoG=mOCyRO^JYkIJi} zt1RmUD500CJu+W9fJ^4kdiXOe9Dh>yT?L%{DJQi1{{xhF;ZIDTsG>VO#PP#?!N%zi zt?8=h2bw2fwRWB=`eWzZKz`1)J35jK|1xkdMV}Y0iWK};O2^{w!3L@rAfXBP8+1_> zsS--Z-?SH1F%ZHk2^oN1@1cr8fKEKg9Dw;-q>90SJ}xiP>A#>dPds9CS zq050CdDz~&Hj=fhhX-& zeyn|qKZgupw)*e(!~PuDHxXXynHv;3U9qpW8)KKPCa0 zK+kp9MR$KDV}X7n+AjL|GwB5IP_kVl`!O-!1G-w4U5xW%V!j0QhM(Ayk`!T7T0d%ysY#_6?lcWzfd4!@lRwo*hc zCHF2=rr-7fTeU}SKYtFKUIUutj`ioz3)q{>LaP&Yv+mf{!A30+s(=oseVkZeGhcSfAhpp zKPJ~s0$eyFPmJ_qaxD|+)Jh9J@$<^yXFa0P_ zeB{UEDm%Y!X`U$cV{&x|z%#f#=9E8^;Xv1$oF{JhGhydvoX8X3`ZI|EnEpnd_|cEa zm9sD^=l1`&lQZda;S3OcPsFC;^A37&YdSL)A^wS6FnXc8DkkHg?(>n}vf(VtK$&}m zYAWXOllNiOy6F{I269&v@Oa??$Yy4!rseg3n{ULMP%Q#G@50PvfQ2m}EJ$9s^tNyVX$iv((+8XSlIa=AP)WE)e=&Ed` z^3$Ny?*EeSg8d>JdVYPb8u&DhJihSgEKH|q?YlauAuIgUSamZc*bd55QqX58z=1#1 zkd+|&;ypuvs2HH9xYV#Qpi244%d{8JYvR#(XZz8ZKn#6P895vI+R{=J8RD4s= z*68h}jR!V2KE4I8)vtT8k-+wB9^V!mY7`k9A|C;}@t61x#2)n0h68(MMtm1wW1_vZ zw}CD9Q+#*)i`nVLUd6ca6!^RoT2m69@c8olygdSQ#a$O$^Yc!fRD3rUq*XoyYM&&P zR&stbNefQZ!-PA(&r8~g*t4%CvJX3StZ8(~USOYDC_4IL2W%#`TrU%(Eu)RkCw9~6 zfc-vL@jXSjki;ZKkYC=bPNQD)eUz1Nx4KZvXzjCmpnWP2DEZp81bKDuLJDXX-HWn* zj5CM}&v@mH;frW$){e5CHc;33mWGHa`PIQ?VOy8Q2wc z&tK0s0+giYzZi&9h);%h$_c^I=*8|hS?w`5Csdx(DgX}ulVgzpkA0H{<%CIqcTse% zm17MhZ!-Yf@*KX(C!`5q8Z@g%PB}@&k3ACV((70KhrXH9)`v@>)* zRo!8FPLd>R0pQ(fIlP5Lk>B;3btR{{1bAs?)9*Rjo1|I;cwu#p_9m$|0Ja9?@b0DZ zHQECBFfE7omME$10pM($&sr;~9RO5Vn!}q*l+=y@+SSeBT}*YScQVv`>4;QoHFkZ^ zpYa8OD89@)PfqqI2*RAW+>>43R=XgW<14S8Hh!rOuraVz;V6&8E)s8trSFS z^&v#g!h&d=XlfjCvI?R>?d1>+pP_S@*{hf13)K4RDO5VQ!0{#Wb^B8vhmJVT1E`

XkB{Qc6!QS$%!#Z~418;hTk{~s@o4rqqJCb5T8BtwoXb1*Ciolf~$O$ZC9_GYIT zfFdXj(3H=eLf%d$KfADIhEwn=D%6IuBX2v!APEHkvMqOt!GPcm0ImMlDTc^0ejlz` zL#G%D2>t+2kj*KE0fIvSwDkq2e8na_0-!NFonnO4G0#i)PIiisl4lo%ho1I<<^ZSI z1i3#!Y0WlH`9Ts*?)~STxZ&N9`{RyIu|-0rt`o+kDNeB!(3MZj18^V3U8mS4%go-F ziCwuORc(4=OqVM^$?5XG5{xbPN>>$(%atq4zv!yx&2MwH^X7NEQkee)G;_DOo<}~U z3G!1dt~JPC?2i1nBd!;buR9v~)oopyh#x}w*IX}q=_6cQ4EnhTT(5fZRb4v_{J1Y% zyOG!Ubam|~Uh*k%z2T*A;(F6d|Gn$5m%gv-J%fH~YuEb*&Flp(z6?#{ImmU=pqXFJ z^$GH_FV8M^eQMB;KjS)U(7*h|^`)1-itB=xeyQuCLI3P(*Ht4w{-Wy!^0KYWw_SIP z{IhpmKNvJ=Rb9Ur`1zw;50KYncYmZxJF9P>{_f!X?xS(AF1RqJo5^SN{4{NHtKJ zS$QP@TgQGbQ3+1zS{DEdTDioyICNqnCijI8fVE4?Xi}bT?hot2RM+?t-}&v_QOK7&gZ$fj-Bo4YsH4TdR@O=0=dQ*&?-^x3NPZPqR=ttC7RxT{ zW#0tKGL>axXSwUM?04jwn%w*d)3wXUPwnjHT}2X~^qsq_fnQzU-N(QWk8=+)@b5hD z=5=>TpLy3k-oU?Ub@Ph?5}*B^o3H#004D~46A;*m5c+abl z<7TLQ$F*k%<&d`$yWjWhlmIm{+~RqifV?+q#m}ByvV;Ly=T|+uCBR#^Y|nc3NPxO} z@3v0G>6T{Sx54G7TGf4oHBvLsp#Xc|!u!?~Xjrn-Yix&|!t= zpak#+2qkN=2X{h)HDlPUHJ-O65T~respx4ywDdA%hiAEVsP86o2VyZkaSL-vACD)vlGTDc(}I)N`H1r6`_b zO&g!Cc)mkKd20^Lz&XQn7rW70naGD`dH&^KUA6M7=AOq=hp^MpNgg5OC;~{D?GYve z*jC#k%o3o%FXp?*Z?tLzf3?xWOQN@~fd#m}i`JB@Hp%tf7~~nUfa|;Ukl&6xn7-Rw z&x7Z?otd{zQ9SRFd1*L=t60u+8aFfCx(0X2S%7|7p7m_j`*oYokDTn`*<43P$|x-Q z)l*;;GL%ALK}{ASpS8zR(3p(DH*uPW5g*^5ssuOiJSX$vRs_@@d$bOfFgm$1uw zvJ7sEY-!UcD4wiHa9G)2QWHpeMfJzm=_slUW?Lr|$m{XS`*yfL$ zrg-LH`NNoiaQs})bc3)|Bb+V?S=eT(XR=ZFKr5VFzl_V|mIjx`t32a$!aLmEp}BFA zkX!;fd7d^3xm!Wu)A40oo~HJtEK6J-`5;=UqTFGG2*M$fkX)L)>`5`Wc(lTlI9;h* zay%(b$VKa2N_&IjmRX+OMkUT=aO~|sJ8>Ds}P*PyHB- zxdZv0iJtli%wxDV{orY!*#}LfId>9tW=RyH0}+orDx4fPQ_8n} z!4AbdJ61}`LEa;3aEQ^3tu3A`>02n8={@*vv}N|hxk{ms4-}MQLJYiHsJ|~C!2H35 z`s?y7ypO$5ds{xqvL6?Uag=tuW#SAJOl2M~K2*AoEfmuXpx22)FSa@4+^ZV*@3KrhR1l6Zt;q5pw0Cl6*g|{)eD{QjLf%=w=Ink%37XBy~XQey# zG3N@;8q7vXb1OUxHz*l^@GRWOb%&J$^|=%b=23V_a$@aPo$#JOvQ?DtU(sqGWzfau zQTr%NN3UJtt$ke94!5FHC-q1ToeKV6ArH7Dr>{`^WRJouhtmodm9;R%%i>RWJOJW&+(!#axy~xFx4>s&uc?85K_L zBz25Rn<^KMH7a$|D)DlyQE9BK#4#?npiuh3R7Lr5l+>)Y?^QsZJ-jd(Q|re98}B17 zOqQmnwUi9?U>yd7l>3Ff;cB=j|F}^j?2QEoFkIc>jKU;{@#Alr^~gg=&D9kqN$Y`V zJrfI)KnhF3yyVqQ>CoT_T10@&TU->opDEOC_?DyfbXH*<-MRh58#GZWUn5yc2Jt=& z>PZF^a`eQ)>WutK73HTCO&!%Sc`o+^bF>tr5yA&9e!*=Ts4MQL=cNyHo8)ssnh$iF z(mB}MrQ36xrpS@UJkNgYHq9WAr_8}#CERW5K`zkhPw!!ZyG^~(Dqe`6iEJO4RgQze zx!i5)D4TH+wPb9w7V0C?u&r*9bt1g60S>^<)`)k68*ih4QE*MIsJN5(r4 zc-)0P_nv3y%+u++wK!1|tUR5*i$_(JGvnN*YOKt=+R40{tjv!peb&}(Qdu-cFH*fl zr-R(43M}G%*sN8-TXgC#w<&~0FY84ivIx4Uy47v6Kq9O@;O?pE7JrfMh(;&=^0IJu zc8lLwbWtz*t*oeqTij*Q1HFjR8*~AN9shKTJ1F{@z7#TvJKiGKW4AcZBHk^`qVuwd z10X->iScNQkUYsP7`D+Ge$D~q$sRz+pb0?M9sn|G(z+sei|o%i;eZ;w=N2E-29&P_ zXHgiLk`pN2|Cd|Pzx{j&43-rDK=-Aa&q#EO{Xi}-pR-l=Xe7uDGFUnWH`z5_xAco7Wn0DzCeTkFYmt`_2wB&U!-w;%SAtgac&kr?)y# zw#tfKvfX&eo~Dyu-Fn5IvZ7AgThao^$Z!j7@&cGX-z^wgX*N}@qg!+^xNtROKRd*+ zc>r5-0AMC6rMfH~w5XACA9ssZM#Z~&#a6x*Tgi$F97MwnZc!Vw_uA^CRa=IRs5E&5 zN@8>q=n0#vErUmCb9GUUMF%3|6hPD~vxWwz-69g~7$x4D3@VoM0)~ufFpEXir)Y`P z(rC9^&=Jsb?zuqA{#B53hJHTET}1o8_o4LjMSF~pcKwy2J#fhQ=qPp|mx}hlpyZ|~ zBe6(Z^I3Uu>!MAld|%NjZ=wWTBv5%1oSRX36Rny%DpcOoLW+XQ>zfzx^!Yv?BcHOX zh~GHFh&X`CTT!$e|CCfv9lKuSB-J!fCG;wCvWZmN%t@1!A}8cyeg?92nZ=S2Hoj;H zDDjz+`AU&T<|t^J14TLH!Z`^pIqW3`Lc2LJWmR*v3Rs2b+$)+XML?yqTBVt?X|0Xf zl8|l8)vBP4^qEDIP~|}tV8-k$nq+7a?M;GJ7*bCHsV?;->Ox97#mlA37e!qDA8;24 zF7n$+R4KTOg#$CB9!ozbqt0r3i$>^@KFH82@f%Ze98hV5Y??Zlc&~`l?7?dqw*fwy z^zlK)YXvVu145ZHbD+ue=KT) z!r!>)L#Q@BZMP|FyN$Q)_Mih>)a*H{h&!9#IAKwb{nXM>AulByVgQ(P`nMS-d3z%ef$8{yP@xKB=U4-BE)pYs)ZlLKy-I0tVMApkrBKA%h1BKconwlv%_d-8a+b_uItwMW3{!O6oC z8Jv`7aPn|pIYs&URmf?IcZmb&=fgV?;UxGSh>^Ytj;~^Gmv|LLzjHUr!}C`W8R3=` zl#BonrwhSKJ7wSjiP(L9jQ!zpZRrN2$xcr?HrypR&HiWv%5K3fF}*wiDf?WomAdIQ zz$IQHfG-3pzjHxOOmT_yKzZ$#sAW28su%S~b)YEVe5n8c+xoaf7L454hxds5$i#&#_+)gG9a>*^J6p5YUt#c2Lqy0b1p?LLoF5!e;VI4+4 zpM#_PIUna@D5fl%$Tv@FABF}=r1qm3S6=u4%J^iGOEBjBW0PD~0LbVCJ)aEi4vh6K zD*)iYuRElgw4j)6P$=H`B@K-Y4$~WKi<>NPF@cdV*O))Jvt&76SR(@>mv)FC7bkMz zZOadj!*AlG5yqxP#;EuwDpGeHwFTz{7{2y~Q+!P}=)enqIK>6v{^S#7$HxP}>|l(5 z>ur~$adQc4qdL7|-=7&W_yK~tD3$#NAonfc(Wo_mhTL3RGG@*J7hbEvu_uOZ7utP; zZLQNQy@7crBc^GoC~3_SYWB(;5I3IdM2rX2ah-SvkY3^)EhI5eMf;p$A?O|j>U0a~ zQy7Mk>B4p_fzq9VC}{}1Kh(vnqa9j9n)6#uLjH-^=ESv8g$WnEhBh6Yw#`@B=o3sc0u>gW~g$cj2` zJdo0Tg=RTLJp!_CFP(OZ>PAJze(YQIdOj^y$6#o*xrdyB+acLVu1>2;S{ZdZh4#uC z3N#P{Y=lLBy{*$$zzVB}eJI7EriXn1Iv9=AFYgp#MkBZMiebJL!(>G^5{1j%e32FB z@ZZsTN9A!e&WibqZWm}PA=$Z#sgWWlV3&N(c2 z7X=WKN8M6L2tS8omPY~Rvn8B5aRGGl8ez3Zkg?wgr(O4rV6?HRGmVTMfl-|lXIiWt z>$XdBroqof2-Mh`?3{>V+1rI{oRegpQcQ2-9E8gM)&r)~A?F}ULG{3O4U+1ya);!6 z+FPMtUuS<-=%Q8VkDamBpP-t~E-aX(4NR996j15wuQ1_Fi_R)2c+4X(6jYJ&Tjw`XoRPq2JmsqpO1Z!;5z;mT}OfNu2Dltf6epnwFs~OT?VI+z%w9 z*DUKG=K}IM3zv12AD-zz1yMK zlq{76;c^xhn^Lr{f_2k>icOe3Lg@k2|m0KpJ;P65$1iMLE;7M90Nln_TjXMMI&toqsNuO` zvU~0o^T1G;mqyT-MNhog+Psuu_@yH#j{2uqv}f@lt++kjwQXLiOVJ(y0>WtsxcKv8 zVFg)0YmLl`mwB6)S|*JL$8yqFL3Ibyi{+hKa{5-QQ>novA581wgo>l{-438}m15Bd+g!|XUJ^+@RxGb=V&(>hp)%TfgbpjE$fMpPZfmx~ zsZS0q{#*7P`OKZge}|I>`6(@m|E9f{T4uvPYd21mS|&3u@0%#KjAvfPsh~kto1T`* z$jZBZK!I$KWNt}EHdtn1ZUNsz-pm@PqOBZ6aefjNsG-n6A>;-ys$EQbkWRI*6=B}i$LT+wQu%tokpWzXagVaCE zt&WvFzo!=?dtb}4-w$LKat9uu-f9%zgMGk$i1|pv+}AL?zJ>A!8pK6<@3CU+W<^l6 zZNi+nO7R6)ONC9gIkPdVF5rT#jFfxSE55+PJB}9o^92~R%)^E+^pY(#!ae@ui;vhNElHL!_)d#V6Q@?N)y6t@vXtKL@@| zI>_yeknmz15wuiBNP!gm@bkqlQ}ClUPK=kOMS~S=b*r}e0)Q3n*NqT0hOa3`2rPo_ zvPK$Szi(bDuelaalnT^pPx97=r7x8}N#nFua)M5S?LQ0I<38RZ! z%ASxF`p%b&RM*NSV9=^l+~4f+P1 zX$|-W7?NaK0~RSa9$;F-$0BRA)f)iQ8a)lBNv{@H1k+Hn#DxGOXOfJJZD2%ObQcG`)2r0GQ-Y-wu`6MMbE5!UNYtk&~tx ziepF6Vpbjd(h<;=73thru`7_qPC$gtC565d03<7i$;uNzm)p@1kjTnk>y;B_WecyP zIRcsjDFc>9tsMbLhNG@_$`Q~^mRNZ0%@M$zxsob_4M#u+xn*z!v?7IE#rD782*6Q< zu*B+ZsJ?B;2%ACCv{XkxReYGi5=*a`nBoYi7N~_3a4Hv%p-zQij(`9%;*~M>G(a-q zLR58{!~8!MP0@k>eqKi@*3nDXz`7#1P8JT4yI?P{zK&ml0)nUE@Kpr;d zEOeN!O3j7Kn9O0mCY=HRFt`IZ$L$4p?nSKAl(w3?v4M_O=(BE@sk|pU8G|KMSi>fONaY^NH^SJ-cGttb-L|dwM>a{ zm^ZNKv0k)67U3AvVcsaaYnk5^`Asq}nV}P_P{SIj*I4CcHom#TjC)#ywXa^}DPy)& zGJvT}Omdiu%DB~an5VHyzFuXTtfJG+FeoCvsRvPo7!;IVUgms5rX`E4M%ua_n z4M-fgpx+CH!;I5KVf|32>kg#0A#Z{{;iy+wd8|a$b(AdNWN=@DpanLOqxr!WhnXRh zt&UD-m2_~XHq|m`Cb)!Sj*88=sY^mtG>EO29CVn2SXH-wbC9gcqC@BaHZQ`)3(;&{ z@fMX7I!rh~6Sf6fk?HTUq8x|mA&XY(MGv(ixTEmf4$~DD{Ugs|0Z7*w!K;k`cy7T% z+4UO$!~sZ+z?%n=r}IpWm>}~ln1|v&800X0Ep@HWY|}UT%x0&iJ50FSL6obl%Wz)G zU{GExqqP6-z-uQ!*-59gdzC-rs>3v&Md^Cce96Q|`9rQ!P4*oQd?6Ba4CSFFQ<(>` zZ*cGq4^i$dtw{VOxy#uo9>c{WR_D|f|C3Xj+^iW6@qks%=~W(h1+wQm#7`_@1crvh zPh~}O9Rjc06Xh8wvgoR}s9!S&F9C@1jrF1n|LsP<+N|c0Uq9{;2U&lwUjLv^{kMJUAM&k#*jqpQzC-M0{R?{i-9GjA%K8*D`;LPb zyhM41l1ZApU@ z0C+M)pu=p3SS?E|JXvvw^@dKK3vh@R4FKL;Z1DE{_010MBt^LH3`Bvqd)cQPA{#{& zwELd`$RgcTxm;(TqVn2=IYb8Oc=AEzWk@>B2It6TrH=PI#1xW^)5*A?{q(Iy_G}s1 zc86e$TEVW9@m4HtC_)Z8c+ZKbuwO44AdArT?q?igAdpfIS$iB}kOY*rxYh#;fGa|g zA|j|b(gw-HJpiN)qKlvcPxf$#Q0z!9_tYW-?e0;Q6x?!@?FHdU1XUvI3@LDkN`|XW z8srd_WeGNh4k3e*Quxb=Uf{7BLt59nzlwMoD1;88wdOWRHA6?W2Me*8dzXj;9Yse1 zlxK&!=$5m0lLsA&wNh1#%9@Hm`NI*28XP2%!C!|$Fh!ja@=?<>&TY2gcjS3ckp&IXagL!KI@o7HT6UN8(Mw7 z$rT6LnV5OnZPRRHLTASiwgL5l*N|9Q1^BEB4xHzUN<5Se>*?rEwaWWppAL7(Yj1fd zd0UV`BbB^r3P~5N+L< zzY#;=joT~tls7i#?~xjtkEZ!p3%069YviB6E=oy7e$co1Cn!RybTF@EqK8=jB{ST)KD!-vq{uJ(kq%?1h#SI+o zqsj?xOX;B+KZq)4Ldk_B0Bb)6fVD^hf#U%{(hEs&N>Sy^a(tn{DpxJDGk~z+Glgo9 zbS<+xN0qBaCbht1dabB()hT2GfTi^TK+}BH2WD)Qw_D&T$5m0bn>MIcwMWdJIb8u1 zPr#Cf>}@n7HxDxo06?E45OE~RCeMPnlEwT9KxzTNfsO#UlI2lK+qY6moO>e6y+$cp zeQNP_j7(a~DBCr)Hlb=NSL|jX!D}#kBlxJAvUb#QZj1mjhyU zz*W|RQ8u?+)O9fv_2OMp%nx)i-96Cs2ZoqS2z~4ov#=+?&%9!K*a?V9iK4dg&sX?U zH4v@6JUYsjjU!pFFbq!kv$C})Ln+z42pFy95GqrOy*I!zr7WZ@dTZ)Aw$_t>W}>xo z-$mKF#eus%Lf1>!6z1$SwpJ}o?_0M%zO8kS@oTL|tY2$95B3>?-o_BXyEF|`+(1tLLZYmf0?i<~1aJq^D+E48<}lLRcxul_m|lD?GvMtg z+jt64Y6x%=lqU=U$mfC~z+C`8ruqml3J4|OW8X&q0p>5gQNCwDKo`D^rprbzM%mKP z=u>_4Mg=q{BK1Z^eE|4$jqJ6+Fp74*y+$Oc_Zm6*3-A#@^ap)kLx9l$p2kD`KE2LV zOnUW9y?S_*4M#-csf&g%(@<-wA&ebBwjsbOh*@hM#P`5L7F;(zP&Dh zX%K)^tmkDza}(d&g#hr=^Focr$AfCD(Kx1tNlpzm?m=4O)A-G3qjBcs&#!UJ9@AQ* zaSVkCL!rR9#*UA&Rg6Pk_YpTcA&(!n)bJ5CFQABSx*~i;%?m&uMS3SmI_pZrT>*Rz z>4NUEEB6KRy4M$0f$!4&#o*{aGl z2?*G)IW?9S2%>CHAqE})I}odoierh2Ht602>b2M`qO6d=6sm-C4F~|g)N?>Gsv>e6 z(&%LT*{X;d0kJ&ijk2|XJakzBP-{Sbm$E^*4JBw}ik$X?iMBTeC}2P6_v!-_Py*m6 z0Sr*U=SXLL2I#ieJeiX}U-JyO0s3$B0SdSW;1|%qb=170wSRN05(I4HC|fM-vql@V z!@wbsHuD%j%|8LuL#pjN-*3ejX2G2NLH>Gy;P5de%|(UINF52ltz(1Ske&ks-iF02>Se z4glEaD?l%=0LaOop8&@||Gpu>SpcW~9L_EDPM04!`KuIVYe-?v8^U~pT3;K&UcRw>5QQne!i@EDLr(sDg$b$v`trIkK`{WT`wKI{aK*QvYx#?gvNeJ*vEy}Nf^cjW z)KC{Ds4IZZhA@b*f`<4C<6ReS8S8@8XI%)I1bR4Cxf%v#1AzPY9h&77ARsNuhOg#{ z*jt7Gg`jjB0;~hD#t`5&06TmIK&h@<tH0M%7ZZ|!LvaFiE8x5 zpX%3{E2@nb3FSrE0vMTdWvdk-?>7Yo(&BcU>GSBG0ul(&UB~|xW&JJ=&~{DNw@Z-6 zPYkBJj=!vhbk^?>6vkiH14!%lgsvIks`UqitMRuC7j6BqtLCD8q6Bc&qXdJ$5*=l| zi-D;{S3|E6ya=hl(9>!FD+#dAaBsn{_~_~Omxi90lRrN_AubC(Xy^%+5e&=l6$YPK z)H{1suMS~;hA?p+Ll{^|uwf;^u##ZIO7KP~CHS$gFn7JeASZvm^CSeL5@J|P2*w~J z#?MS|KcdB$_eNQNp#V>G0YYFkA%@k2bO+E?*G9+)0KXL8hs9wg#=)#A)An1G8%mYzzLPWL z1JIWk0$`aB!9e*6(2P~g-$YsOL4Z2K5a13drK_L-50M_|0)(QIp+UX^wDJmooc#F; zz=Och%DModbpXJ|eFf;pD&}9KtoJDZu470e3~dg|Bmxj1v>Q?vLxAA`hWZNNb^Uh? z*Y76)CQj%?LjXitJaP3EAX5tPB+B{_t8ZPiA%GK)rSB% z`STOtAn4yP1o#L5#>8h9+yQgFI&$)dN%RMW8E6Re1!|o$gt-aeI)MquJVbin+n;Zx z6$X@#vi?gJr*#$+&S41k{MK{)M*VQ;fDdg0ejU45lR+sh=UhSO8;u1^CP>0CMu@C%{b5Pd5aBv$8>Q@Q%^Lq8Kan@Qd z)$7%->47&}i}@0NVw>>Ks1~?Gq9n?Co>$)MEsc-|E7tR^xdcS;LHH=^g?KcrJ(LZjA?1Fh(?;CV|-z{_-y9CQ+ z0`M&s_-D%xO2@KFO+yqd$X1Zkew8A-fkFKkg8|&Q1xt@)5Qb1b%!Zhin}41*n@Y!k zwza^n)nfkMTM!QFPMq~Q^@aL`~V%lNLC2H=Pj6^7pzBtXyQd@ z>gWTK&qJk(F40oOJZzQH53jp87TG)y9nywX!6!+z-&yLdLFM|qPa?8;6p(r7QeC82 z=yLCJU6-8{0bSZ7TZSnjp$U4wcMC{*Aa#pmK@c!|0gXh0zw;f2QEbS(G!hp)L;{x< z4nx?pNRtgM768cOFnHUWtC;nc=W22)*2uOtFnHBy`+3w^Wi-1Jz^jr$nqFZG3~nboAgYd4AVtltk=(a6#6HJ7xHq^%Olw?a#XRJIPBx8; ze9e)8A{Yk&4f8-ZA(gjGm!#wF4dI!Z2#0p<&9(oy>F-W5boI+*> z(lppg&5_Xg(EEU(cWXX@$H=^Zw6-3sHI#oU^G_`QG(-K;EGW1n~A}wG=cf zvJM=k)|Ug|vnLkL8DgPfVg}^psf;#J`f1Z&^!EdnSc0_@R~~v!QdjS zU+99_FvD$O0EUGk(-x^Yfy&4{gEWi)M#;7oiRMUwZD_=H)JO0QQgF_SfxR`=RY}B> zro=`?;>1BDj)C+8hoTb3-gX`%F>tm{HdsNku?quDLos_2>ya)WZo4BAKYKPuQM|b8;?vT z(tH9ig79LbA_CdSY(d&Uz=aH!sPK2$>Y!Aut*AtO7UKw7YgCyKXcF)Pjp@SqGSzU5 zbNDCVFlZVw@GRjsssYF{tq9R(t%zEpnAgT}*3MIem=ds}-Rb}#(oG?7*CWv18+u!h z^cJvqCECDn;7u_51L+=tUvYC+eWW+uP$oka z7}@`q3^a?qn%fdpuu#Af%SYms#v<_r^pUQr0{mpbyzoFA8Y0DpzVwfXR&co!}qCt*k{{E77I%-J&+DDm!a@bkXNKC)zH z+RSOdV$S$rKZ-`huchvl7w=L4>*@Fn7}}W~U;h6M)=WF#!{R0wlOFk@O=n%aN8rC02a4Of+Kyn~+{wpvkeJAI$`7shA>- zC0&2%wmI^u5R;|X$0Fkud&5{gRBQx*U8&d!se|5&iYWm48@<@~|Jw`bzFD@` z=a)EGiuF$Xet4Lq95(ejn2kY#?-tFVpNcb)o+YpfnUzS(3AmBjiS#PD_&UQpXU7227e+u zf)VOA$GL|m{%Ky&Y&J||^fXp}m3MPDcoMI@$`1L4f$>j6=W=lmnHfK-v5}u2KML!D z#24I(A8p{*Z-^faVI|(yD1MB@LpU@x7Q!`~X9(A99;AuC%sjmSH4YZmX-%AQ%!`$ta%(;uE-h4N{51YD+JmzYOHdl3>G{dVO zagfeGSNP|8S+l1z@vVpe)mk}R^Nrd2&KE}>r)o=u>=ShYo)Cd@c=5!x1aGHLet(%TRjtwdHCgN~O=i{H(r?td3O78{hV^-<{A3 zJYw=8jZ;oL1|v;tsn!^X(xNlkY+4Irm3%^yIgaUMx9Q@WwpOj&m$aBdvep}5d=5AP z?6Mc`*68w=7;7JL+AmJE;?@e$Vjn!;LY|o~d2D=N5qUE; zHU+~CHppo|4D5wakYwN2N$g${ICuF2$+b}Qt;#H&WVIB!w`%~)prM&QpUdNXJB_7SwTTujR>N+Y*k| ziuvA9)W1Xr?=a$effsfH&zK+&?c+L_st@_%#j@M;qRKtZLs>NnoR=dlW&h`)Jr(|1 zg(p`J5x@pj{TAsafeyGU0nxGgFM+-QFaaX#62Le_K8@6q04z812-2GbGLZQd={u?c zwve=1)5~JSx!#}Us^|^^Y zJ~s-kd9v3Vcfw#PBD1#EsxwCFVV5e;I$Jkk_jjSK%$8^ejyS3{fo{l*Mj9mRE@-XQ zbzzo({Guw4DckU3(Sn|!LeLuJCL`L!0t`kJd`Z-61d5Tlhx8+X4Y*$c7FHE8xLAPY zrYho&s#OUfo~=3yX$k=tRn;3vpXNdmgkVV))r{M2MC%KfESWUeBn#*=(a=*EfTsrH zP9;=>;UvK;w!TjuzXOlu?x`1z7|;DCYAn-D$^DrxfLWZmOgEFl48Q2k1QB3M3)ydE z=VN%25>$C!*@kaLdjDc`1x0mL64uhO#mhoJsk+>T zEiz7|MNr82x50KW_?uV)`Kn=KR&5n^HU1M6O+XPgXJY&reN!wvhm6kS-Ze}sI1`a> zwupT69^z+3YvMnr&^spOsJ1A$u=Zyteh%8mst^7rkv>8bS$%+Aizcuw6j>?2UPrpd z*0W$)Nv|rRy^BTTfyP9s_A`N*2nV2=YL5xn0aQb(%(j2m+Ag}QU?wJgsG3edb?vIc z1hdP7W*rg_NwXnY^)HbOSHBDg7BXC~SfDhGD^O}Si_?xH%rTd0qLYFX#}qOEAHjnQ zv+JH+-r*dkVBAA~23rJ;0H>&@FM2of~*sT$SWSRy54l9YHZhr(!G*VUF zsEXnhbL%IXM!}Qa`9SN=GI{4l(x0jcPm76mf1n3h@EA4VU}|88D6+01(*~&(fiuYT zM(U|M!DZODtc^px8%9S&FJmm1!QQiHz+E8vl}%pFGmIwqsD#$FCp^-(iH-)DM}11I3`Z-_j8LC z6RtRj4(-ryvn3d3HF`rk6!Rwto;o~3wKro2lUgh0=I@9^FG-1l5eLb9l63N-uj}X{ zqPrRBrLa*+z0DsTt(bzAgUK;%82t-d>2FagVY8+<%1Ql2AI=pM6!WFmwPEO}kLNu) zw`Sc`TO8}0W1Vi)Z-3~PiNQwlNMxoVO(Fnmi&=-X+z|F(P4gY!l9~rQUEJl+NboFw zaWi1Ddi1k=6dIKGZXu`tR9k#dWyRFLzE-eutU~FAs(sG?tO&|r*ernA{|=uN+-fC`Zc2y{f|2-0iN3qJ|h+@y%kA6Emw(-M6s zpNHLTd>aLBjG7UW>@-f(#q&5$TYU$`+c~J-#a18g=MViSOn)r|)@v$&rr2gbw28>7 zGh#GTKa5h%{~`S=i)N#!If{O-hoS`%1p{l|4_yl?PADmQ|fh7A7Bv4MRbdG($KXtOf1{q2IMX?)c5s>Iw$|N*1 zLk$bWh^#ltMQ2rP^&?vR{fck~ueTlyv0WCcYKTUy~S8sq;wZitV`F# zaFFZ1q;x3Y*@{u)1%w}LuX~s;L$5Yo7_JxB_bJ{T;;oHs*A#TPdl$BsS;iGwlK7GT z!DEG&_}TvW^)Y%gD}0-YH=0@BOfd(yKr=i4Q_W~zE`n%Yx5D)wu^FA#8(!EkjXz(k zkBJ@EK894+$Hcx|F-?wEM2~Qv=C_3#&D)RY#m#+-6X|Z|c)N_)Pb#v<2wx%?!W7Zj zFa-NUY}gm-jV=sO%%7#9(WSl>Hn&zx>9(?9Vc}SWH{XG3Kd7`2$MBTf4jBS2{7fgh z@PCO)45Hiynmfg6>uHifvitf@|*D^qITU`_SC5b%`I4ET@g^RPTo^}QN^%3t-{$9kLFtP`dF zU!rs`(Me6FUh{n0%k-BCwn4djt@fo^Vz9`^Jf*et@}2nG*ZoK2{C?UP_qtZb#B4d_ zH@f-x+UZCy`aiR!7e*o7$BqkNCUgkBW6M~W)@4vuuN^yaDP!0weI_~KR_ZO)joNve z`(@M~Oy((ly_Zp(|N88z@$GS+{dOlsOfe>fTX%oGH~F-_eV^6=y+O|aMO+?a09eCz z1?xaD+)LIyU6kd^6@jO?MDGW(pXSZzf?=kod$H3Y-celsrbgR`UeIg_@EZB@vzj*g z`1-#Wy#u@ap>TB^O8g3M?)ZKsIA-=`wWtx-@ z1e!!=y@yIW#tJcsDATfTWlm+~SwtS4@uUF!@)YkK4Pxaz9le-JLx`)7-l`Z~FuHt0 z5j)Vui9WWvDpONO-!DOgP2brv!Az%?pr>V1e${H7-)}zrc4io!c{PHwn@{P>$BW^l z^`YCHj*DK3e8{a|1B8Z3LS;VW)-QUPqAaYZTKO?VB|*j?O8!~o`5PtA(0_ z3AWMrfRhsU1nD0jcTNHl8+IO;*N#T2q922Yg&|VKAwq4e(zRRh;Gy3SI_pQ0F*YEo z)fNssFD3Q+2V~ef)#fo{{}5zmAf@X|p^|@_euL`SDyACVLK&zi<{#Ct|4F&gLjNie z@(e!ge~_f+FY>*ds+C{igm@hkW#nu%>}x9^{kD?!F}fu3wvyx+^k?!2kl?1CWE9n2 zf%F`Zp22F^wOAmrU8wyQ(mo*HII4zyQw0o&xPo*(RvzDn-KfOldms?2*ZwguKPR3Bq9GDLb*$xztH*_ta*+7Ez>Ijb3y(telb^7{V{ zMQENJ7y0CLF^(ye_Lwd_9G+@jU>s9K?~un7UP@k&8j#FCgLP|2ZX>Os0<9srehUPr z(Q4R-yo!@N&ytab%O_#S5y)1k7J#c+(BTQ$Ljab0Y8Xy+c%u_)^#`<4);gru!bOO( zTG)RKOCi6{=&0_DdPZ?&r~gcSHaGt zDS*9m)G+xByrzG&tTaZ@WuUd9f+LX6J_42c-h0p)X zW(pEi%RcN4Qr@={0#Mid1&s`_M$FBe3uX&y5QCP&>jS`97M|Z{BUw_7^4-L$C0k0O+(e{P;~Uj7 z-X<{14-QZ*+6NM~UB&|J0{JtK-=An8Bw(h(SsLoQPC3vy~C~A+WgGgIrGk^-k>X|Z1QTTeE zI{$#*fHc)&iP2iF3s+TlERX|Ns+KS!p}@K~kiG_TW{PUD#wyD2dbqExLy-of zopCpj!Fgr`vlfmTL>`W(<0LF$3KIL1{u(k%kQ@ZwMrIq*76Kn3 z1NW4`WApSg$Y3}UN(o#=<{Hu^oav`)QH;R*owOfj{16-#iu50G1BMw%7=}0no07bX zdNej=pSCdTx9V_;Xe=G4uKdEIri z-iNb!p4SOXJ)-T$hK{Fm0ABCYv&)GG&am*8A8DPMd{`TwXEC`T?I<-ghn>2KeoTf| z8#<8e?8hjGG5Hc83`@gp1YqP1N%Ap)2grPfbVIl7XHO!&MU)6r9QRmjVtzN-#B4S6 z44N3m$DhWR$E!o2w?+|cVm26!$1>Fj2G^)20jwI0`XKevxlW7c(CB@dU!vKXe*c}k z3?$DBWUG^VXsu3bC0l(-4b5e%yr?yKI9QHB8qQW>f zlig}4zEL5DFNAaxFlvn+A(FjROf3BU2NpI)M$yv_@*F+vYTkGrjz9 zUTibQ`ahfLY1<&pLg{oSJ<|g8#CmeTpbx`uraPW zh{*}aq$Bat$>dhZV74@#r;pGytY>9+C-UO0*852=t=Z{TIg?7&(8XwK16^KipIB)cw@?>_dx=VjsGU_832fu7){_ZGMIFUydpm7TVyc% z68jMN4Vei@<6)WeGj%xAR9_cv(+mK<4 zM+xjzlqUU2Wn*`oL<^`3n|y6ep+v5 zd_=7`DW`_IsdYS%BPL-sG`Wp*6QVRJ2T>+Hiwst{CJzXpCrvRin^x5rMQDs>c9V>1 zsG%M(dODPh;y~3HsU8{OYP6VyVQ89))R#a9WFSw|41Ge+e26s$wg|ONyrgr*xf|*> zS3`@))dj8-e?*lvNGr*;IT%g+6VQGn&W}j}$ec#{#Nc{}8qn2|0aq!FP@fLwsypQR zWT;SUe52NQ#xB|TAT@M38Xuj&#xWzB{(y9cjU(6=6W_zN^PzH6;v^VIQYW(Ebg@J1q%@?b2~r3FOEWW>=dG19JFNtuy z*HZA3pymn)2(GxJqJpC0nyi9~qJW87?rWOJrlq;#ZmyY{d#Rx2)@G$;X{BXli)m%~ ze$SbCF4y|~-}`ZJ=FH5QGiT16IkP?-1-^%@Mbz6A6#?*7xJ%C=(1~)QLxdbe^hS#A zgFQ7cy~RI@XX7`}1}zOCQy@DEI3d^odZ5?AWSKfck$KH9S;k)@S;No6E#Z<5%tQ8#SG>RLKxx^$au*fq5R9@O3l{M8UZ$hd< zGTUCY6b&}qP7Map4eJLN56o#Z;UVlvP}ylq6oLYyY&1?0r2-%<2W~vd(aY6V)`a8E za*nGER=YdC@*{>_$JSB#=g2_lryC49k82$MpcsnoH)r3RfRT0D$EpWUfMk+>9$wW& zKdZ% z1J8SK71X&>ewFBQ4wI$}xooex87Q7#MkpX+VNj7_-1o7wDpGy0u7G4=e^CHa*zf>2 zKMFK~rwv>L1u*c0KMywun}w-eu~|3<|AX-%*aH=*35b~rH(6CZ=eA~gHv)yaMQI}M zQxQ!6{RTTx#pilcKSeyPe8T9G1`PL7&|)OYRY-dLA4v8oBz)L}kyP3RN;~9U#R}k9 z)n>U5p}(J@vX){_mVtFb{J{T|B*`!wp;ag3;FG@?dsRFXC`NdJc_|Q@@R#8hpvo!J z;du>iIRzHOvlR||iXw%eFtEelqQFLYU>=2^Rs`k65e-~wm@0L-A$fVrRjC$x)nimD zT_sHEirgQ=eMsoQ@o=-Mi3s9 z|H|(1kp;}D02h<~_Td?-$45}LeTG8EW4`He)fdt9?p0u>-QyFq2-Z`OGpxw&!Ef|a z&g32{=N{Lb`&@Ir$vMG%T7LA*Hw?U;Yj{!5YWFCnHLm0zxf9{u^?r;SoBpFZqY3ur}5bg_{1ddyF@Dm=dNqF}|4#4|A9k z#I~pkyGN2Uags`$gl_9(xSI7N@uenqkKWG2`&8oI4O|k(89Xj2v3tbVL522trV`is z`q8qlJnZe!&&sRCpm<<+;C;Tg-6O(@_g95CqLB;UNCPjmdX%%()8Dka-!*ckrpTP` zcU^M2|LBs_{U^=|Zek_c{hkX+?msh0h-uu}?*0qs@mAD6i|)#6+nP3DgWdgtv%Dj! zybJXzDRsZ(THa-&ytGGl_bVKeCPgmwJ*N=tGTJlsJ#J5TYR}XoV9c($cK01dacLG+ z+z!{`cDfd~%gLDWf7{(xICC~pIahE_YWLyI?e24(@q<+ST<$J%_1JxWRR~uFB_8#K z-Q6isX{%J?@fczH3|)>9Wx)}0PoFT?IWkOP!6vAx|r%*dJUqjJIya>}TCgpm{C;oj28g`sWj?!L~Pu_~u; zeV3f{{S`x6i@5lRgM6je4p49{-(Xk zdZXZxT~)#BT`F6p%s>w|RoTb|gCxjil}#K&Xs*n%S9!&W=Ac6JifhdaT+u9brN5A4 z@Jyh+N)d}s+JB_|03IrbXjEAyOQa&3JZG;GZxorKsUqWpDx0ZFKi47?49X8|u~+F2 zC4*@*bV{jAhF*5I2yU+c` z?nVckGU=KXzJG%$<%)c}+X#qM%>iuuXiW|v0zY#itr=&&*kWuRHcQ+_pifi3j>v&a z(*|pr|4F-BFE5p81ZBaGpP9)#A`L{X3Cs?;hLHn<#5nG&Bi%qOckQ>kg;j^&SgfW- zq95ZJ)n6`FyM?s??MW!~Al#eJu*z|hy&r%paF+mtc)PXa76af8+_y>&U2Y#;Rc32y zswT33hOt7stRTy&maj5hPYJu5&4sR%5kwSzSBW8`7bv=HVTvxy6(W9@yGL%dR2#K~ zaoCccDuaG>d)ffD1P~V+nAm3&u`T-o*jw(Yy4B_`4?q@NrmDf{SDggjQ}?4%9!ZKc zOcO`f1;*&pcDJBf)X2y<2X2O8^rwL4?4Oa0EI+xMT8Yk?2=?EkOGw1_-$bY7nF839 zJ%H2x5H?|r>1E0-z^5sgF_w*fmB%t0jb)=3pn*rn+uf?WGLlRp`ZXa&j!=C z8o1>qIOEdTm^ucPyNoNtDY5^%*>=GoQ`Y5}I!r<>B4LaLBow;Y-FUxW$r!A3`I96p zQ6yAVGS>1WNI3c*$=TIVNlr?g|AT~vEIIleyZO3Hg_8iLpJ2%u8zxILUssaT@=KKV ziIg1k7fMLzV-1n+QgX~U3?S^0NSl-#GdAA2og)VT*jGx9`70$kk>df(`j6!7idJ*L zB$jD*IGo-fN9OQ*qH%1S=0;B^&THCggC zDoepg?ew?U&6zHZG;BJzk!IRJQki{CHBx4%l*-8EVCa0+NTZGRxCP)1)ktFu;9~%3 zhZnlYoTa*V6v9z9)ktGiBSnP+7!oic4cpvidW)a-jHBYLA_8+zg3H8-*vWTc5f{1q5;Omf&};bcJr2czzGc= zwHt1Snm#{IOQa)=h98DS8*2D*v<-^Mr4xQnPVvUA1l%a$zx%|yvU#7_Jkgl@4L8$j zxe~qM-m-u`4o?teITObrTQ}f7RXoA% zc*MiKiK>aV#sK0QO<>6;oCCuB*mZVududDvpIw9+KR%Q-A&;@Q$Gn2Dw*oxs5mx1l zGxJCdgTevT+o7r^Cd8N!brb18ek*%60_sYM&n4r`Jo)%1&&(@IeeTg^c3>C@vZ}%| z@Q4ibh^%jjvM)q=LLQM?9%MJyRHW88u29`0M&xW{Qfme(Qd@(B5OAexzTIpG%S3+w z(4$)=D}@lK6hf=H0M7K6mNT7Mb$kMpEd$2nz<4(EY&jmuR>Hkz&b*p;ZvDc@^E4t{Z($UUdp##BqL0kj5N zTTd{=`LeygG&Vi{lebSbc2%eA%+eDnesQKyIq_)8-d|C|R=CXsGjb+KQOsLSoXK~ZidKy2d)6y% zIlDj3=$F$eRot8-b+td*Zn|M7enVYNq^Er^_XMOp&^Jb)aSQ^92<%5-=vN4k9akae z$tG#~&`ip7lNB6c+#G8+op55D1bC?pkQiE$QW#G*Av+3zr4e@1$(Ak{-{o$}7*8=q z&#~x&rc*rQBJ8tj+f5st*bn>g;YZU(h`TX+H(`qa)nOc4TySi0;yB&GZkq1I@tdm5 z%*t%DIGb5kf0i>}V~gFC!6ESKg(xHn4h1Aq)r_i{X#}WdqtKVYD`F2Gt|qG@0&k_a zvzr2FxpR^kurS001S4)g%!-gd?WTH-IYx?&el8B+i0gJ!Af~pHtj4HW5eM30w%JWV zl4i+-`rHu_a{>LgKKc+iLS@tWH;{paK~n>!-V?@;sR0{eKwmz_Zfb;%?R<+yKYvMe zSeF@Gw6|O}F~xy#q!?~I)ieKy(T+PkFrmmFh9<+3w7$l< zB%Qy|Gdmq`bLeZC*@!Q9AW(x6AbyCXz`jh#+p9~sSMg9?Pm)c}iiK4|MJa!bK20KP!4dSlYA#2=}`S?RpayW>CL$j$N;5NcNPvDs@e;1JVI( z15Vj>F9X|@zCL_#TlWHeR|cm^W23Tj?V3+n7Vfj_CTEsu3Z+SkyUgze#&X6RD^I=p= zDcRxzOEyGqr`;|ta#w&%P7RSlC_Zt@^wi;=S!r0#5}&e6QwJ8G!A_Q6eTJPTuGePn zkxWk=|73n8(*`=Ct%7ddeIJ>b9^xWkR=d#k^GwwsQ@5_&n0xE^snXO7V+%s+2s^9;~Cp3!GDG zKK|U;mBFari$kDz+pV#SVwlq};-dmA*82IYnl~a|J!2QEYT}b!(^Z72RyoLm*>)VM z=Wsu+O6K^MFuS1r?H70WNZ$HUW3Be#nXK@Wbw!UX?O+#lI_AZ%Kuz#f1h5;!2fQ1V zBLHfJyC++7!n+z9gV@;9mey6rtcFFSI`nHpz3rt(SYbPN)BZRb5M*t$4sIn)c@H2m zgbzc@&o5Ktak!(Jvi63>Aq)Lr(Z4iAr3A^0<|0zQ=S-Qv>14_|DkWIMDL+TbYtEF3 zE-81al!hMo0V%(CrX1>$@)MO(>GL*^kn&H%NE!yq>4}RiYYxDWm#Ts|GqtHWYeQP zkSfMLjvBa(zuIdbM@@K*QoR{$AH}KYi-1TqiY$;aFZ$@SeG~?|gBQHglF1KYBYz6{ zgQY)}{1AV9VF;f}(~>6GGh{~}j=9T2`^YNzKlKZHdKHw@c)mT2%Q=a1wq3TT!H}AM zl9u3oEQmh!qdk?Qzm(Cb0UV8(-81Yd9P<}qTF$el1n}#jh&g%Ro=h<_=-cOrNyf?7#^3HR;K)Bl^#+J{#mQoXv`hgCsErwKrZeqtX=u_r6+L+F2f zq(-!_f&MzwD)Qh@D5M~%6px^WLHo?(p|w{GA<}+LD)b?v#5fCcGUSb zWO`AQ zMka#m_zoT-dqo%YG9u|~!YHB_hWSsr!M|=wL9b3sBYnc7pf}S?nU0$ZWX15h*KnDs zlS`&foJk`V963}F!swA}$)^RpxDO&{7Ry|BG8I6KZC6^pl1EbF9N0AG6mN4NT6APxw{Ec*T(BEx(TF4JQ1P}-dOO%YjRwd^`CVPNE67aCAa@)=hX8!p(;vjS9Nge}*W>>|)kEP~K}${b&4@A5_sJQv78% z(DV!hKx^#IwW~waHm2TEWA8LH?E0l94}rng{k5g~@5~8saCW2*zPkIWNmP96Yn!|g@NV`pr7f{mM5Yx^C5Ujb+j1P~2GA2MqPCDzUY;PGE=djBAN zj_xP`(Qpv~Ox|Egp4Ki4u*`tW2`MOtrnnFet=*e&+oniD8)6f1P7i2hT5dF8SpJy9 zFDXNt%XiAOhLb3bJWw_fcu@14R%cP;sGByu7XTI15qspDsep2*9c0t1k#;YWlx#&|{NuI6xkaja)mo_P-9WBu0bCPrqb7d=_Fy*( zbUe~wk!bPHU{AZ;ru(P_?NN4n?|4m=%14p4tynh!@!f2?)e}NmdKvNPli#0Kb{TEAID85-Zw~k2$FtN zG=G3pMDvn3oA^*Ah(b8mqQ4MLdFjoC{)9R@R(RlV%^Ie-b$*l(l z4E)k24!ATWX;J<)q^6wz<4DP|15lz`ycmVA0vM0rH>d?>@ZmTF2;d}NVW52cm#yULu_wv-dM z08)b3}XNK?ZGW7-c6RXg1p>M!K+mo6#~;iS>)T zr1}&imHO<^5F|Y-+k=7S$k@+O=M$E{qkxhu^md0u2@NIhfN5S(RtcsPf0#*#}g&U^}0<=sES;V(IU%W zWp&sON0&L4wR8=@uJ3Rf?p<(ySzXm&+c7ptWgAU>G}<^Ju5CwaEL}>R8NR?K5?wh{ zK(yk=)1pP+NG1$3ss^K_!~2Q}WCk)5z|0Q614x?bd6lwbHBIa2=2D$)8@M_z*aSA- z#G=06+6M! zP62SZ(_-4?22Uhh1Z_n*F&uDWI8ccp77sQqe$<&T7&*HkXBVSaZ5(6psD7|b__*@u zw!|W)6ORfOf=4v4`8-Dh8&C{|OHe%W;QMk z1JEPgCh)BQPT3jE>1-xoO`G7a6HWsFCUxfk#lG5#eVt*Qbsi01T_2mMV`QHR;AGy- zvI#!}cm}|C{4sa4Yuepe4ImBa z>e2u%h_Im*4fU`IBfFxF#(4Tc4nnX! zwdAZ)6 z`FnDMi{MK2x}eexp%JCSgB3kWW%#;i1H$OA&xe0bJ76CaM}K(Vx1kanZor#t=wKQ(If09o#<8sUnl*3Intp`Nk~?&I&+zCb4`s;*kQ z8&rKaU)9>(fVo=_&EX@K$$ku< z{F!avfb62B$U_a$Z8zLbgY4r79DRc9a?0g(&2}3jWYKA()Qd>>o>A%z1U}bVLaLOR zQ1$`W^{(yCQ%XL&{e(0>DEX`mKt5xDzS}<(gCRAhI-Hm4nq}9doQ|^68IH%#v35tZWiKQJs{y2cX$XzM2o_TpFf0nZ z(_F#O&10I?%N>U?v)og`q5*4HFEtOa3Io;$uwEpb#|GN?%&=j@$8mu;IYvd6_>)nt zK9w~hj%;6#Gq$(UawV9y^5h`qd0-l*co;Jdfh=P=b;KmYhzC`e-TE}OmC*)ZNg{;! zNRX`z711iNu3OUGooliADW=#PA=GoH3=xqFBW+83E3NFw4moB+ELm_-bwJtr*-H#8#l>ygkIJ|VIIVRfjj4}=b zb_g&`A!w0?jMJENa20jX+Hq1=ffWpUs-_L6=S1lw5)LBmQ#iKK@?8TMgJZsjyFn4L6#R*JNVmD(5SpRmiEY&5=Ci{{+)bV zN=Nb&woMhb;ZAIE65DXb20t(iCv0W#1H*8_rZ5a=Y=q(YCAL9M422TIpt>q0Fbrx` ziD3{~O$x)HHsq&WD#(RyVq1zSP|dJqoBh)|sS-ZzNF%gES=3k)#N=AU;{74g(R?4Cs0s02)IFfZ*S2l->*& zs7=sfcol0&h`^%+;?+-){_KnpVS&JC@w_D)BCNHrkcpi@@#sw{F_2rVva@sOG`9wEYH$;6=+l7|XQ?zmj|sg-%>;f45+b;M09rN6e`iw+=! ztOJ@FezMecke3>(y{M>lknMwYy@-JIAw&RDyfZXpJ$0RFD75Rjkd03RzRg0XdPC$^ zE$udJdl-VHWiDbxW3MrJW3`ZcB;>3g#+mRMTL)wtP3~=2$1iOCVG5qxgg|svLU3-MIXFtSnWm`moB~2tth(XD}hQxSL zHq;iMm=SA#{>%tn6>gCi+8Xf{j9`0SlA5lURlcG9gIsOhIp^s@+3%# zw=)Ha_u#*RPXqVst$?#r_2ARc(lu6tq{wZ-?WYEx#VgRSw^FU0eV6jFPzMV) z;$!e9%txd>(JJ^;s_8ZOXWtF}%<#W8BKVr&-_b7kpK83X{uCNA2klimK3HyEpp+rh zUcvpIqxPa*ee3)}u^n4Dt1Lb!3H}Bo2z)>Rfp#P;o+(Kb^9B=)Dle{GD0YI?t1Jj~ zxmYN6fvtSgs%3Ja*iCfMGIGLE1Q4T@dD7Rb5kM_CrfPnn*u&%^+51-t#a>1Yl&?Pm zgra}Okbz->SW3Y^krlgQv@P-0=GL&Tniq;lHzcV58M7qikWVrO=V5N*U+hYonKF36AER(P)|XF>k>JB44X z{Hr55-PeJP8~N?`3hAwa)e{(gxuME`qE+E#vJ0+R(u1`^xpZ&A8zhDAOM-zl*iwkE z9noT~SiOOBA7R1Z9LKncIma=X17(ePt}qW}t?~6@aly?62ndJ;Bl1u%E5dol3iH^U zz}7I>o7a{~K(nv@1@eq`o+_~cvc@bSi>q_PQ#VHZDj82~%PTJQQv|M=D~t5Q6AMO> zex$6(211d3P~=*?g^MKl;3C(gNtA(=MZO>*QBM@vTNQ~KHfd2rFBq&jPy8ra07FKO z6h#YQ%kW4sDYIxnHxy7|f%Fu;%)$etZCe#Bg2LJ7Pb%S1qau2_Ywc7TWQtNS3~|=H z;YF$S89Xj8S1U?`3~2kkGRMM89NUkoG>$1shdSa5IG}AcDES zZ9(1;v~%)(l$JLNR6+Nkv`c+*FR9Yj+W@V-Huq9PQimv_@z1$e0yVMT2LHTcx!lpH z5n3kaz7HYe2HDUr_x;++@7pQ&1MG~guZ=X#qH;fk7O$~dHOS~!sjM|3ZNda>5V0mK z4g%ly^lgB18Hta)f75Ss z*8q>UAO4oZbJrUFT61&P5r3|&v5V)bGBycv?a0~+HbcLU4e5FP1JcB53Xud&6@YUP}?6;7x zj#zIuolI~Ud)X|@nvDM_(-g>8O29+RK@oi zl-nBWZq_}?fHp?)s9alaezy8@ou*E$*17GOELrIkgOsw)R34dys}ggQ43f$^Gf8$L zsVuA+CVo|No1lZ06{9!^N(eV5$}h_SpxKe!P^WQIHs=WlDd5U@GI&ooa_b7zVjXgy zK@B1JbnT?(0Qezy4&19-UmJZT_gUG?I=~eDqF6jeo{eNn1z#=JtDuU{{y2cwy+tw& zgYR`K*1aeLSrCVBAwV@K^Bk`g>rZiN94&_z-3Kos(-g2+ug-9!-*~xLcW`M43@t9! z1C4<7`C>hY1D=`4(NKCvk(ae3Sg`R0WJ=gmtT&Pxp9b-gy~TP22Ps66k!Wlh(&>=T znj1O8=y1=Fw@YwEO%t1&gGpz@OZ;)xWpi^?E6;oY$3>RV{RXYfLdVQXAS3L@F|(4! zb;*x3C&Ee^2P*&5zm|kDiGZJqE{Ot5wRreD|5nllOXQowEc7&ONt@8u=2lsd|ij5Vr~SlH;BqktvkaLGU;&GL06BMj_sdz2($qkMBaq^WVGWHk9Z z!C!M<$!Of&-Q3AS2ZKxG?E=6D#g}AZuBp9jp_xU=IB3=yet?hrtYo2qzfpM!UFKj_ zS3RKQFlxI9Z-j_Vw~(;ekrLWol#|8HPg#;v)3uWE#P4aAYz%!Rx#X{~WKI~bmE^IX z&g*V=Jdsz5rh$o+(V`0cSzJJW{=lDC=-t#;7DE7c4;ZJ%Q%n8Mv)6I@Af_2@+vllq z`twyO27$LS5r9?4LKyt}I6VVec}o?0wzSbLnl5k{sjEyr#C29R2xjMFF9Lt1ZsjANWWMb?W1Y{BDk`eKd)C#iTUm3y!+ zRcqu({27&jKbc=Q!}*I-S@Y4Cx5S~7WjPr>CO+$B$lsPY@F=ED)*{TQw)FS5q-j}& zm}G3}&m+f*SwIp~1|%KAvZfG{1xSvpFB)0Qna&Dk%;^d?W|NREs`H6D6>+*XcqHk-({u2KWHN%PNx)&X+`S%?X02P z6VWV@W1dFmBidlai3NqZCq7N&h%yR25&R@qw2`t>+` z8@P2WG}sipl+_s8e(T#_xms4^Fz_ypS5GP;gieKjga&R{D?)fs>C=d=fIlPdlHS<* z2y`vz5%IIpfjSM2_!+9Sh9B9Bo^MB7Knrbaif(!-DB=RiC4J()L)C~2h~}P^xjo`C zSBePP(m8^@?YXTlu)cOHVhg(Qw!W5}tSl`8=MLbfhJ5WLQtrJQv4m1il_~LXpfhEW zOqmIb(Gt;K-5&htD$1junO6C>yJz5KuZZ?!T28|Y?I~HQT0|#SCbBeqG@I~EfYOMN zo6pWpgWj<18pX4UEvVu{W&dL$oZu{Ww^I)aFANlD0SZBxPtde@*Yz!lff3})ojfvf`#Cnv%75B#Bm4lkt(HpVNj#$l` zrH0jdY((q^x`J2-ljhx6c*2azYThD=)_NR8^gi%&_X)Ym{uom~qz9XbCPo=@ODL}`AtQNVj(R?lDH?$tq{ z4HGR;fR6Z3>tO7Zzfne52Ej0bf!>o&qrBG(0Wdt)L0Cec10)R_Q$}tbni}x#)>w;? z6WuhH&U$c8%H5N4cBk^uDzU@hu(xC^(+Y7Fi^2G`MQ{o&au(KPQrwH?ip?q zpTLx=Fwq4}nM(HvC8}I7EmCRtkq^eCdpMBB14P{ptoks>N&RM(l0N-mV0)HQqR=N( z;vgc|;C+{rf&UXHmd-gPee}k_iO(DT7pL$bzCsKexTI{TaFz#Fy&a^s?}flwKBUxM z!u=H(8-7Eq<{jweyX(UGKHd&3c{~JdmIF7QT5qd8@8{KlxN{HohcZnEAKOD>0{+*P5h`xG<5kC2|TFq~wtL+|( zoy6@APyuwy*x#_uzXP4S7c^`yyq9d+0VRO1@@T!R#wm!$Yc#sBtgh1NG&0Jc*iENo zTop^NAY`n^YR?r+dbm0Ml%9^joP46yjJE|fvBwO(OG;}-3s7|!0a8LU+5!1BPqP24 z=2rmJra{|=EVi1LP~e(|c%L3tb1It%Qa=SLJu3K#m%V;1q+U;LE%j)xR-lUB;ypFa zYR0k&Ytmk5#$NOthQuVRd87fH24H?b4rcpSGoOfqn(OsC+^a}gtAkZO-9f-ZaC?*l z1pTIogLuSb&t}=u#vV_7m`w~nPVk9F+v7-&CJtb-wCC!-(qk^n>F@muK1rtEOTs{S z)ShMiVoI-IbhLMux7FOuu$}e}^pSk%){6K5E>7-eH79UYJ!KX9OO_&My0Dr~7&-UR zh=`HMbOJ4dVUlcmtLZd434bWXt%}ujhjP-GxpzGfqp?kAoaX!92XbIFoqL+IQ|8MM zD|zD%N$`kz>0MfTribykl0=#}q^3D+I&zd3_QLEKF-+pXYyy+2{8p$lDWx zx#@c@6ET$0M*H$!dCF1Qz{hHT~fv8;x)J z%OH*@ear#H$Ur0J_y<;V5CtlL4z)9f8Ud2@aIO&9_szCi&4L%U5lHyUYSt+L!Qvy@ z`&w8{+l+5>5>O#s>|snzpw@e~O^YN3ABg9#0am=o_nPp)=d1$1k!35fb{_cb9xVl8vr1+1|E%;z(oMg29O$gJaz;)`vCy=07w!x zzKa502k>L`WD>3%Wh(W7g6#dsUoJe*+R3J$kzxvDeEQR{us~xUpy}fX$i|5et7(Rj z4VCW`3CY~Q!7Mw#OxXc&aBKS^$&5>pC|C2DZ8gnB+Sw$Y)No*o@4tNcqYM3 zMA!0}4eh%f6!w`1H^-?gE@BMb76o3qQAhWPG&=vqd;fiCjiSlOOnb#@8m^4YphO}9 zzcJJz2iA?$v^klG?m&7_jB1fP4z+roLnwCzTnX8iCd~iYzU=chKnKB+ZovSeb(kd= z04!IWOg32Z9e}rC=$VFs+;;pB7Xh8=gQTQv~`te^$<9zTghc|Wm z141!ZVW}M9w4efGe?ABTz&~%R35(w10KSCHBV1en@YF>;Ok>nA7YCV%Yo>;|xb_IN z#gPo8RxK8^QVp9LeDYVzYQj}YBtgr?p>lDts^#K_ApoO+8!c`O0@yWzRIHw0HT9#u zb4`s-4uy%&`y3pQNMhcvnPk5lYfMTJYjH0EO}^sm0L9n1VgL(?ud&egwH31lD!#_8 zN9270qHW?T|b3;f(cUPK8Ezpm<@xePxb9W3|Br=M;o(6f4 zg}C$Th((Z*k;*@<%7@WSD?3`;7jW09T@QYu+EqodN`$Z&DWS=Zy=pZ%9|S#=?L%A( z!-&57E#8r|n(&QasSc^%hocg#b3N}_O>$O`5~!bdU@}qWp-QN%7tc~GstSHhLP+PcAK>eP_xzQQ62a#2$aL{A|F_8`ZNNkvR`3ea*VX)V6`M8gQx&zqJkP z0}+Un@!jCsLnMZDv+898#1Imn1(!KMYTsD;=t~uRdHo2T(m(ddsvjhojK{y(a5DjH zG1{uLokGB6aCCY$v$<90dr1U52)7q_+-;gTybH1GqOAG{-U`nR#C{Nvf?aH@{-FZ) z#s7URN`PaJBPIRfkbMPZ54Y-9X>cw>DvU#Y1C{j^3&l*2{!;*kMV_F>Pd znyDqM=))Ie1ey9%oeI7utoZQH5sMWd!bPz!RmL8W6TBnduCbWzR8AJOo}h#<98oWy z5eD#9l@5)-lg%T2D>2~ZK48eJOjAo?$gWH?y)w3q!cgoQOBg&JJ4;D`xTk@JJ(Ok& z*J6^>QjJ9&Tfg@2+?aY9*?Zt1UyEpD=a=8)?9(|L(t6C7u}7;DihsxU#Eg*Gd$cLV zzQ9FKc4F??P!fMMzB2YjuD(kXV5&y*@uT#FmBJudJ#K zM7RM;W*5jls`bS%-ZxFjY_~;>6uYaMIMyH8TgO;+X}%#9<_mpu6`9>w)`(2W#228I zm6&vepb;03O>jozf}@H=*Oie=DkDRkk>E_&OUS9-d?fM1j0NI>1~v+wP6Fo4tiNEw zoqt>y|IMn8qLsPhb>%EWAA{kV50M?C^B*uE^l=!{<-Wl2>Q91{x;9df{kve4RqtQ} zI~o(x9B3KXk9r4;^8C8X3^1kxD#NcMS@q7Wng|W1o_Y+aw8mJA9|`5q9SWC@nSjWw zFRXeb6S#TYZsW$bj zOrX-SS{>4{CtgnE8J8Z!#fVXfv;%@1?J0-I4B%;qL`kDqN74V1|~<0|6u6MWn2RBt(75)ELyVk1E7`xx#I zezX*MS~Hime$Q%JKYvI;Vy0C*2ScQ}8vt$KS^?M+z(&kx05Bl*>)|hN82xsfidK4L zW+E4hhom&|ZWCmq=4n=46)+Up2Ezc~`n41kq*f+^GoKa7G3>tc1%zcAR7*dM?)@?m z*!nv>bKqtveK0c>JKa8{4@gtsyX7<%t4rxKmjeY4tWwL8M$bSxL*LBYu9@DP4-)N^ z{{^RxnOSbExOzOobVX5VbS)J4?%%3T9gI4`g6LO5bz167ZPdxgSIa6opib}NyHBRA zZy-Gu&--nlz&d!|gxhP>>7h|49>h)2(Sc5yXVua4%#%QKiL@idX9%YPofWJqbek$v zsWfNBI4cC^D^)zxL(?+`YT{HKBsL!KSsLw(#tpHNbr2ox8l8wzq0QBa{h1igaZhzH z7D=XCg+obQA8G>njUXj->Q5zgju0tz{VGt^Q%dR_5G>M;{cgeCh^OR|fj|Py?p|OO zfwMiuyhnX=(NL6u z^*AHn$<%;^>M_wOEL4v(oe|#!u9K=q?*wTvoaRMeMQt0RHp*y;-Wc+zs;6<~*Dx47 zs6ysEEhNC1YNbMi2auhqrpsy@WgVA@&Q!jB%a@lk-14UDz@)^l_qBg9?}{XcFNQeU zTmK^7or(8i75rBNX5A26mG!jVSDYII1;anKPRe~6G3V}wD#i^&{rffILGL~WRBHc( z32+{UM_LLs0<>u6A(+`u72( zm#V<)1*$J|1*+7TKD}TzQ;Ql*{L6Y<)u2P1szDre!iqdugWF1W`zHb8Fj<4!N_6{A z1#D71Zq@Hd#ZN=H1g=PlPY3Kz(AG*tKNkrjE}A^2J9TFV-u-b&(9uXh`^c{)T0h13 zUGW+p9JhYT62ZIi)=#A*pcbt+Ti;T(7=~_7Ke3ByaqdJTUhsR12SiY|32wct#aq#` z9s0im*fCj)gGMc`1NK=xEFT{tNCiSa!+l?m4uMz?vHe6(=)l!*V3&8c^=*_}y)GVW zZcn93jWp~xqpQIue`BmyFtndN*%_%WC@u}h42XK`*~uNMdWun0{s=PI%KWAPE~jLQ^;H;FCXx^#c%g4tHhsOc-Yan!q)p zz-RD4%ck;11qPQ>XHU~4o{@{K*Kx3z_SKv+$U?rLcTVPCvGnp92kbpygl?5vHQ}hF7cOH z6OG<&;{GCVx+;GM2=z#PH>nJW?wtL_WRW}dn9Or+@U@bd}>n28Ju!%UTJ<`C`*U)9=yIav_ zP$_L>?@Pp3Rm0DV*Pjw$aPtumXxzlM@Lxlvuy~Z%jo=nuZyHSeD5H}#gZp4Rn*xMw zg2WcvfUqG~@mXsu>3=$viWR4R0BP}b>SVR^&R-6o6U&M{Ups$2D(+q7L~VZOI`%lC#d;e;HTrdt_}` z%YPl=%!S5$9L!&yJ)+WHe%gPAQQD?C{xiu>S8|YjW&_Sn14FAB{=Jno z7vB(2`rRX*U(9Vp>e9bAj9^li13rcO2x~j00si>V6>&=&@DT172TYgY{s~YqC4WGz z)XPrF(){Vf9IbKg}qNaNQ4NI!2> z?RGtSK+!*mYlB2D!_Cw72pDMUT0Ka|faJc>Ki*9VWPCO)JfRwXmE9Do#fU{m=h)Vj zu`g7{!bl)BcX3c4qat(&ZV$wE^bP-}N{kY}0PH$?)n$8A?o|=q+>Z=K#-Tj=c_2lUOwrQk zi4=)40se~1Ly!V}F;b==tXVQoBvgesZpc%gT~eMdzg)?yN@?qJUd8fR5M#b(&|kyQQ4)$k`Fjq1MJ)vOAkgWuLtjY@rd5Y|#}Ggh zaUeU`p})>Co|ziFU^Yae1JqBJLmvwx@5*Upp;VJ)sx+i}(Lj~G$DuE#z%*JdpdzP% zxRO1*E;#*Nht7LMY5^!s=_kR*>E2Hd$jKZDWP_n==>s5%zry_-KvWDNlogsb2+cOg zGb=L{FnQ_{`bnvPG%P}2E!V{Rk;K?M=c5g2m}~4=ulSpGLlYm*qXhINPxTxY4STej zu{Fe@V|ES{H$nX7tqz^`oq3g}q_IP<%S0fi_F9MDz!QrNo}MXVb3qD=?=S4R1!B`^TY2!3-!F&4q`gI0Oz(Y2t(W$X)Mkhxnb! zqxNaK+oAJ|8rO)ajemBCKLV&xsX*~3VFYVhL#|gj1ifj60>lRiNIEgwA>Jb_+!}?6 z4sk`6ubo1&ZYLe$6jO-2r?)!9AtUcr80K|~Y0x%cA=>S4z#THUmsQRF7aAnU(GPg0A0Gx+AtH@0{O7!4K znM}3XGVLLaFP@VfVn1xp4=0d?IUgCW!F_6Ur|REHjQi~1@YeFoX3 zLyyMLc(q)u>WoG^e0)}MeRL#ZYOB<=2HX9r1M>kwgEJxNpfUs62rPll5*bjLT7_b3 zMiUAswvupnze>Vg|0{tV7^+NmM~$Z)aodtU5%yx-KM+A4$oHrXh?jh zNko&(MPIuIcJ#;4`3~W)`eTaGu!+>LSIa?qD~N?Z#tLr3#b}uY05&QIun{m&8*YW$ z03b2NA0;#J9Drv)+2HC9;Q+azp3tnSIZ|$DroA=L9Hv(uzJw&W0JGg8YAG7}6#xbO z`25-@))v%0wP+ZchP2rjI#RDN1k&lz%3kcU^DV8|yLY zn)tLzy$Ly*;|k`+`NYY1IHFu{^ay`+p7S;Hygh>1V2RqONP}k*+(ZfxPZq-AO))XO7d)%rRvMKV8LOF=@o8mV+jg!yJGeQn1gX-;Xl^Jy z+OZvXBd)zk%@GYEue4OuUg->KQM)VgzKowU;Qw<3U!iJ*3hSpK) zZDcNhr&Xh5VCjYw;4YS_;!eAgMyqBl*G%|cv$%%I zK7G5!FfH%h)0d(hGxW($t0@DP)YWoTaONb1_0>wOuvrz2nK(X_dC~|RrvRA%&LyAB zhOmcuBN#sUvpNoAp^=w+hW0~s<0dkR`NsDWSDX39Vg9Xw4-+dyQ-7Xd^G*(A(x0uREGsP)3DmCPJ^Yb4Xhg zDxl5RzB+C$h|jzE(5w;xlme{|tvXEjiiaWyt1DmnitWgU)fOcD+KfJ0&sV$w64>19 zWAPO`Ip7Ja+E?s?g16>*<_5#R8`8x&Ua09SA2FaU&9D&&*np4%vzPjcy{NR0K%mc4 z2w;fw5g3UK({cWnG&CL@0F0xW18_moSIlHG`GTacm<8?d?2jn1%Ll$VT5IxJby``HGH= zoU%P1=qtL^P)w+G+E;Y-h9& zd%hx!1B!c9b15f%rN!&6uYfQii|ky1k*EdzHq)4XV?Qi}wGWX5eXeE~F)9!{jqV6W zP!4n~4DmzfVI(E?)&PGPxEH`J{5GSn@KL~F-61$#0y0u3ptAl!gc^V(xM8)hc+^D2 z*fHpEOg>`rWP_f9_Fv=h{fTP%2Shmdci*3%)o`Z|{#8|ce`kw+teh|K)oK{x8!A&FGD>IEZz|ccCj&m1#h)T5IAf^CHBJ zYv;SLCLx4Df@zrUyM)kHgWqk2Zvp&Y;hRh1%j*`po$I@dD`Uyje3!FX;%#BES=j)2 zAQ8BkNU6%4sW!T%+N@GBdG4sp#u~nKSq+UPz8X%Ix$658#71i@(M*$m?)TH#4@$o3 z(-bV}==%~(OmUo7BkNxAr7ndKt#v*#%y%?c_ti=-&Nv$Vm>+3rRPKvoDCim}2!c3) z1wzRo8KBP$)4Jf2a$Vm9;%@~od?qm@K!4R(?ZJH~fy~GK7WByhur95Mzy-X6xVfqD z>gm4CQTWZ~GHY`yWu@dH4uZ<$k%pwiJnhTp9BJJXBz5F8k|cgNw0b(RY7q{aY73kD zK23Buw#Mbv+i6GFmPBptxF1nfcX0^=rWLw_6Xvs>>)G|B3*nl6tUd#|n z*0@wHvp1Fg!2qe&#*ZZHbIkS3ID&ox|K2|`$g#J86 zqdsjaohKEH_-2^b#2l@3UOf$7FtAyT()rK8pA3IcoznSW=Il)P4|gh^kK@tbjD)}1 znbHN|S~5L1*|tjQf*_S9+oyCvIHdtMOJDW`^fOS^pkwJG)M0ie{L7vzU5re#qv0>x zRQd{~83zBF$E8cyZyA%Kl@{NM3tMX)y@kthI|vi_sqJk6cw+ z+>rdtfYK5pujyK8DURcOGX{Wok5cpy>F?-Ky4=9ISTB8*{Se@JbGQVZ+|F5OD8RM$ZpO6fuY=jt?~bUU{lWmcB{?%!D?N!m$@RXpuJj#@p|fG2PzeaW2VtTh;d%pV`t(bsf`-}I!x;2C zK;|{2IAlN=5NMf<0J;?ij<+rq0gS*KD>$WspLeRj$IsDB93afRL^LQD6@yS)xtD;DZxiSyV(i`)OE;8Bi+d0TeC+2_S>BZ{Uwc*Q#e{ z(w|s}WcTW&V-1nq0}*-eo6@n3K?C{A-zgp21AdapdB;k}!iHuxCApVoQ#nbDbW;aF z_lN3@(2c(JM9EiLgzC-Ms31DVTo2V-pr>$DbZ)2~4wcGpDD7w$sz;z6&;Cg1njHz% z!)nOoCBi%J7EGKKheO2!!b?k-Uauh#2%9F#Gdn3?3l(_988MzYIMEg={)RsOb}eqn zV38$MJOYfzXtjRm;p}L*) ze+3*Un!lF*wEI?I+5Fp$sP$A^sMrSTZU=bMpcyK5Jxu{%+Wc3jI7UKPhQP6mP;uM{ zP$f=)IrELl?Cl6{t$Ly26cIxw#lSLA&Mf1_#8APn6@dz3ivZ{85supt(Cb=bAA0fP zc73R_l=nk%yAXjXo+-&$pvHt^=H7)Yg$hq9p5!w-7y2uSIZT+T6NG9hMO~8>wkT zy|d4Y$()3=qvXUX;=Jib40K+7J*7*Jca}4PG5xA}Hp#WiB z1b<-Bo#h8%ocRp?wCI38p&x;ZdzXfrry5+myBXh!M3~9-NzI%AEI!_6Hb5`0gu+U0O_CngIqM0^)l;=3~OiqE}UdE%&WxH%5MAI8y?HJopJ{*dd* zwWGNr5A#0^RyFAIWLtEtY>SA`(E_bE(4VFqFiyM>t}mx*+~m#?uD{Nm18Cm(CtT;& zp2nXW4A=1#hWOzQ`1HeZS&F{O*=>G!|4ESSHsMAJoUXt%)?FAtez=K3H@=(3Q_LUR zaiM9Vo6#eGtj&1RM2r*lkIkM0Nz~31)a7P3NP8azkcIx^6?*d2xX?RlHc&2lD_i4yb>Zk7&5tyT0_S3PFs)?UMxXOO| zW~0hKJ;n9&)3-RQ{1c6(&5k%hvdYdNt%^^Y>ZkKDil5MRpw$qf&P|tR!>;=2lh9T_ zZRWc~etJHW>zR#jA0vR4rw1GHHVOhXg`%k&XNumQ+p8304z z1_k7%6O3#R3|GTP!DY~=0Q}^0m=y@W2=@Z7E%=GIjbhdT_?l#+_SeM8>-Rg(Ts7_iz$bqme&QrHFn?R?B^-Bf>H8!jro+8N6u5j{qOLgu@QngV-4f(m z>_Qzg{MQdTPJj1EE_Xz_??fwP2yDYGDVEhAZM~Q-P8+7qDi{{camb2 zWb}WMpkJC_&^kJg=bzcE>E-=_Z6=b6R?b48$CwU2{_j!<`i_yN9Nmx8oc&iRc*Mt< zCQ(uH{T&LhKC7u7&1b96&^UD9VL>YiP&hQArCmn{*ojGIa#9kt|eT<=O`4Bh5v3YITBz!Zm}H`g)R|@IZokp|Db6AJl-MTosAv zEOK%T9KHAO+9aH&e)?q~2|a)D|0sJ8_^gWT@%!HUBq2cHxe1_{0ttBn2@nJVgwT7D zt|*|igqBbP1PDz+6{LnH(ow1)qKIojMFlA$f+8xQV%x=zWnJA>dB107?vn?0_xJwy z`Fu_?<;;WO6b^C@cCAIc|FI($w@u&cu7?*lzPaY=LJyF-f4i$wSY zjOO!8B7gJvMY??xte0py(Scgc})-#CLdIw> zoUE+d50$Y=sK}69V5ocnCE-UpUKPdX9x_Y%xUyyckqHf!gWrozwjl#6GUOo+%s3HA zS|^vE6$d;jHQ{G`j6Z&nDZD*6&j3$JqzQowz*&hfQ4jeA_>tH#&Y$MW3N~G#m%7hP|U9uEqi`yDHR(EYK zl9c`#iY^RYLR#I&5^_75wEd(#MjGqRJO;Kra2|RJc%8J#FC^sE0&fTLuYk{!)yyzT z7$j}jZ9rMNx$)kFQRPkA-K6y(t!2Xmd1R^Z(CA@PNt?PqVH9Fkzger4a7|bWT>&o;d5IZ8)+XzAR*V1JO)x!6G+JZ={>KAq3-w>8&K)OmD;Kt>K%5 z0aJ3xLN%*6ytZ-U3}v?pqU=)>lw+wwZy^(12+C=L^5;!dxYS7|v3fyTSJZ6k*z)>w zs6V*W)qL2p1pY5G46>g>zf>T@5aWZelnHuDISyw}cEvXxM4#CMabY|U* z6Pf6Bz_Jwv=h1(|sU5_5*3@KZvMW=BGO_?t<61WaQ4N7PEVPi+4VO}a%c zVfi%>35D~C4w2A(b+v?YDUjPmOXxna{j@xHBrse{=)Sfl?E%teYiSHH(#T~#$lVzt z4XTo5R;`2qNFzK&_(dAI>~ZA&AQ7aI2ST2OG{X5drjkZpB~tua8mEISQ$=`-gn>~~ z>`M^t0lG`%LjqW_yob$k#THA{HKy{O;Gx(A^>WG%8$90mmF-};UqXFFrN+R7yTDPW zmFQ7*bJ-57A%`-`CUj?|<%FM(RFdhgbVr?5O6ab1hEKh)#_6tg1sbnrB&hu@RnITF z6qk&lak^AzEk|f?o}bWCXh+1CQ?pIW`VyuDvv|vuB-gqr`E5U&WW(1hYMOr2oG zOX*%#S03Y@i`iq#6s{#ceFYivJ1R<2YJ5%Ea*x@AuE>k4d53|giIkoiv*&iOP?EgQ zfsc~qbWdZM$pe$yOpEK`x%|K_>uQJrLQss@9n5V>1mQsy?21cJ}w6fIg zR5;>cL4z^qgH*iggZs5o+u+UaFa7`V<)_Bs!=mxHHg%WW_?#M-Yrnlvll&312$#ljH z!{XmP2it29@1ce8STD$99><1i#Vj!^@EKPOf6WwsBQ$?Y(hPscsrMB_aVCCT2*n0L ziYV^uePW8FTN7yGnR0nUCt5K@=p13W{~)HQIeDDTlFsGnp?XN{L_UrR)p;>yuJ?Gx ziG2Bn6zyJm`J%DWi|*i|ew~2-2C?}1q{!{pHfH?k)yN+T4fNZ@b4XWeOj6jhEwi{Ls}b8U_M zgUo-)<={RnQt3rE=8AhcDn=dt2`PiX;=2WV*R4o1lZ?(G?OczTIv(C?(pDg^#ka+@5WF4a zXH|)5t-|E(10G9;az{oT*2)_F0`Q!%ByUBMC_i@b$}Xg6#;BOiqAr5?7VxGSCT~@i z*J#CqQ0sQPvmE&k-k5o&fh@*j3Sziy&?(EZQC*Zd`U(VO-+AA00{;O1j_$=2;6lq4 zr(+_360`K?@0MI~>Xf}IwUjt@yc{QQm+|CE(u-ut?aaQ(IXtsM+l#20K@P$3+^{oG zo7|4UT)S~F^Q?M5*ohhz%yc4~dp7aRH#auBMUd#5n5Yb-7A0@Il$mBV4#=B0_2PxS zX+fL%R$svq%4$K&u1Ju;X|_sKyFQofnbIz`-u0ECPmI*xWN40nWblfgwen0#Y%<*| zW~p2yRNJa`T}DMWfAkzPDkeTls{CZr$tiafTU;kAdJY;ea}YwO%$N$K)jSbBCl)NG zjWNv4V;YgRqjvNh{>jeK7{To~Zb(6)75zYYu27c|Wh`_7y>fy{wcPe#RYUXTXUqGTj@vL7O0g7q?hJP^DDGl$(aYDh^OEzdXk{iuW=nwklli)m zdM{Q|J^?;R&LM@#TjV+xDr3Wwhmc|>=&{{MX`4Kh6u-eKBBd~S7%7@fYwQ71wkHoK zMa+-Txe8oKR_&@%TBnVR;gm~Mavm>UL|66FXxO;B5;;Hfdvp?J`uW-3TRI2MGGGrD z>Y1XN@-3YYqHD6O@-JhI9>kE0n+MF1$U*{=u#y4md?0Jyt;Eo(aZgJGqmqwZm*b70 zM1K{B)%h~IjUS?)8KU`jgLs!Anm>%l5Fz@dA)1c^l0U&C`WGUPi}xDjG`Y4sM{<6P z?kvxTh0FZE13wvpQzvBZr$M+eUQNFH~gu2(_QV_$P4pY$SmJ={p;Af9BxjUGUDt7qNF zE!YD^v3ju^xs`!Y=^k|)YeR|~`Lt-_Nq|=DXyZm=!zd|?OKuyl6${@c!oXEI(xkSk zNuZ6RN`14U;;UUnp+|cEZ{5iDs<_xNs&Jw|P-8Mes*1SkmXhO{PE0v)pi$}%$vjDf zs!IJ4u;d0^8d~bQrh)9zj=H5j^<)>*LDyaCbCq8ve>k{bBr=t^C8K#Yx71&Q1!vE7 zOUY#&vXk$`$g?9_Q0g?uGe05{`v#G8$>v~J0c6euOO{J@OFa=RdAv*kcM-XzcooQ+ zvZsuk|8YybL}cPP>O4}>E%maL++oNZ36eRJ=awq=$W)NG=8xr&ax2KahzJA40mVQ8 z+mmjoRhY#oy-fw%LMwPAw1P*273_Y`EtM0jAcu5mVNPO53v)tSsO}IrDGFJPPW1E3%4&-{)$IyAf}~MZx!Sd`W3+gwa@Q{CJSYnMpZqx8>BlooU-{h%>>HIG(BH zQbzU{TgtVMR@oJ9#AH1++AI&!1*WT{hO#K7Co&XGOXZ^UCvNGf^Kl zvfn)5p%BtP*Ns?{W;wScv6YdC;TiuX@P-^+sCq@yee;Morg4|th_%Te@)>of>W?5% z$@q(cm{8v!zON9!b0b#c!rqz;VnQ#Fz6HLNNIT1#PzDG?L*l+;wi~(b!HVNKSR6b` zl*2;aOVRdOjw_jmSacpICHR|b!>Am6@x*pEC^kYtv2ADw<-W8_#5Unyz;9B>6WdwD zX81W$QfQ+TT1p`i;Y5TnF;YYrf6$M5g|UKWRO-&&^;X zr%3deGgO>U;IP*SrN+bU`Ve5Qj@O%Z_PNi!<`B|9v}>up!Hn9;aH5O*8V6yUQJL@UuW)uMSjRUtNnA_BXumPq4XkODxWM7j}R z>=iT>(KHm%AeI6&tl%DrOeF9ius|Y31a<(AqBikMbp_|m-Ol#;+M+h*Oj)QNoDb>& zpcP+ko>#*szPlT-Hwi>8a8!L?a1_K>RRc?#`j1#hCw{OS@fdx7 z+i7VN(?h{Kz&VMa1O<$xf-m((qx~-GU9M7SUGH*;1QHY7hy!TDZGSg)O=1?9q!8oI z>$={(q7di05lKCj}&eS(=33$Csta_r}46NL6Z;8*zv&yx86#itbP9 z3fv)xaU(SO3%Lq$4~S(!{8}h7WGCGRaAC3&x0S$XV5CG=5tt54l?b+Q5+i()jFGsh z1oi^Eq)Ydp$Z^kWMUFd8>g&K!qsSL9tkP%t5*~CoRS$FZQ3}tlUN~%-Z#fatzlv_e zi=~Zyo%AjQ-!b-e(icQNyS>CHQ$%0qY?l;nP|Nnp{tWt$rtD<;aWZbaOlS@7bR(4e zAbYcM=b56GRwo71A7c|4-OG&_Ld2366KbAtBZhjBc?;c$VP0f<3pZjo%JAG7NLQWb zM&u&*375-=OA|3h=Di(KS%)v(2-!T6i|L%W>oEm*c@h2aS0y}7!pZ1Sm@MZeG?PCM zQ)q8lH=?UoD85llZnl)_nh0hiDDS9EBu%Ymr9>tr>U4G^0>Kh8@2_f23gaMNMfwqG zLS%#Hue#z7#vgduji@0Jd81oek&4?JzjXjl2-dj~ZG^4y`{mC;sp+yf^R1x8sSenn z-c3F?{A;h?1o8Ej9V3N$b>v+l&4{F{dc(g}_2O9OT5kAv@s>S*lBudf8F>-M4gZJ7 zOp8-)M5IR7v%a`i_!97uzgGVIO!W=#b;Az?tA84dI-JjV5P8-FhAbIXE;>75>QeOUsYA4j7qW$m>f-FnWrdM zUw9y)J$U^YlWc4jq&tr>23tWfyfP>^k=Aca^oGe{w}P6&l+ngVTWTj++D zhC)(3Q*G&Z84c=wBXZzDRU5w24KI)OPk%{vB-W?8VL!-@MA|hsj9a%UK1#1&!Y)C^ zGNK9liYBjI?}mLG$@m!{P42zT4f~7WFEIFr6+Tx#vb(s_4dcyDiqj`6xFKq$iU{^$ zgFQQx`lHf!w-0y2?h4|4Zt(62#hWB}uPk=M8hUuu^qZz`Sf;{L3#hPWi8vH(W!vB{ z8{M#~(yD4%G=QduCDAd{=kwAlYw@rY5G%D8#6eHFVREINQ*Crr-nFmbhE+t3D(wqy zl3k&E!g4pPlGL+9PIkV)wN<6eK4Bqqx%v$jBb6K((8#4^b7Kdis14AJM6@QXR87_A z)|8hGMxUU(>)j5G3L4 zw)b7%cZRpgR}^O!Pw(I@a1b^y#J=wmNtPPd0Lw}2EQw0BNO&H2nnY}+?|bHGl+>E? z3Ge}lPx8Ho?=y7qLJV4ADH5^2K3N#cMsi^Tpf-uvU*9z%QW0ERh4(6}>ic`_4OZ3n zo9aFC=K`~|t@O!A5L;Qe5m?8|kil&@nuPT$bBpWyv5b+`6b|!ke+LC{#=Gn5sNY9pq>~pIl zkR#N|LAomPTi5r7>8kN&u2WZ&&{|W|`16)ihbJHbIs7Aw8ZMHWNZTYBd{M;1<-x@9 zXAzY;13$4W#Zo@t!GOu0z=J!L-Apmfee^opSosMRC0k`hd0A>iyUktSF*WE?Fa+~m zpT3t*`b10~rop$NiuhAuIJHw<-=;L?H(Vvz^71_q$Lh>%dHM94Q+jm!p443o&8{Q*!=afTj2)PqMsr^M%=HZ;GD+S|WYf;458Ig*C_h#0 z^7KF9`q~96cn#z*1koXplIso4o}qm93gz=oEeQOd?LWA_#)^!d{+lxTzf*h* zN*c~{A2Czndl>y{#u3?hMS`Ipd&%aKHeDlxuvoB3Flexw@-U*tH(=vP@C3=6sKrs7 z@dAP>eAo5e5+yCxjm&yqH_h%Tb}lVk3+fgQ{E48ZGSsH_HiEiE?b(rlxtTg0TNJ(4 z^;JgKCSN1rAh3rHXHDbdi*=Af2uTGKMDEo^2)FRDR75=O(ip8O>8e|O#fyEE5)G-N zy4F{`&Sy-SyrCdHGo2OA^zuezP3CElrM{;`mK-Xw(s)pjiGj$(>Ygx~9QH(~O^Ftr zh!N4P^LfT!yoO%F;~)aH>^b6LPrio!@2lVj`coo-q-Xl`;c0HOM z?kM6#e~=i>&hz4`=`B^~C9FlkGlsg_bXQxdS*=}XXUJGf-Axhsofj3V6_GI{8~f$# zL_=f-69Y88I*cw(Br(|}b|A4$hSCIQXB#O-!cbr!nB|Uw*~MTkB5^J%GPkAc>_(&o zHi||3r3ly2X3;55)EeU$%V}=&xI5>JJD0>6=mh|_;aY!g>nCqyA zKJpNwR%3+1lOt!nSQBF$^~J_Yoay6SN8Oqr$hE5(3fhW#?YyZF-+fsfnb}#xee2i&aQww0dwNLzI^qP>xlX&wP9<-sjebOPp-7Pik+B~ zsh;n_WBPNd8`R;v!u8bphH9++US9mK4E~rPSViMAA$ozb+9m<2z!MYS@3W z!C5N;G=`l0XG6&`oLY?3!nusF+%$mH;wmfUQ#pf+;ZESU`oN&7bc+|{K zHGArVD7TLePT1mcs`o&RL8^&kq33foDd{=fyb>tnWmcL@n~`uq8`nM&BH@C*r1Nvs zQNj9k)O2m1NY&Hz%rh|ZDiUH@VRN2DEi2?^f%|s&|30yMfSwZhg1{7DqC|cqunbrtk?RC@0~?8CU>;_D z5ByCc3?BJXbx{R2@iMB9=1)CnKQCF>u%aeFBZ(lzqB{YpD5EQZBA}2Rqej(rlP1$b zN`BzlY`{46(#$kxkUDndd4N{<|h}iu%HFE{#BRnO;c26 z$oi`^?z1%wu4w6Vm;tp%Bii{~ZBW(rb>eZ?p5ztG=%))#3N1J}wBTe_u(Vd^$&4FZ z7&Xb*(-UQq2~)F$Jp%>e4orDL#y$&}a+ zt&Dy$TQVQnv(Xqe`94;Q-W3CLQemx025X?26{h)RYIqlZ_P*!Z@v1$#Z#QiPpC1>d zVksMTy=vP_T{{U2mhnVex!23JtEJ&A|6;Yx&lYZzaUB|VjTeK(?t&e_Bgj}F9=2{~ zx@snUjMF~*6Ra4Mqg_Weipc{doD-%eSi6pDHVJqpbL8WO(&D+d%j>F^ zlQ0yxm&5~gT)VGeo+M#4u!6*C;jS%*$K;3`UlFwCJf=1)?1ALXc)?e^#@AhZS<7kg z87U&T`buzMo&w~1)*`^>N`NGM4tz?YRHi;zB9;9L{DP(MhSio-5nj1!l&gf!%aEGO znsjb7I$y4u8B%i@vvbqHGnRSNuX?kiVW}G}Gux<3J)F%)4Nh%vgn6{z7V{mt*Sh(4 zs${P_yuiwHYH zTK_*H(wrQbw0Y8Em+c}#6{pWGhZ^CiFra4N=c@N%E8Qa$FMx_~V~bYk6HB(EQETA| z?R0+@jpM#s92-;s%%ACWt5*~>e+l9>y6#>Tyk6inlB4?YcopNy(PHqv3gT_v>*~2) z@VaZf%TlqzlZ%aDu?Opw>u$c#GYibd?-S+++h}@bCnob+Ke+kTB~1Tdrswt~jippR z{Uxe3cP1duA5rx2tf}SM#N0i=qonn}#T}n&@XnCN)F%&<^esEYe*$Fd?ao)+=}1TB zx_M~Ryjw_PpwBTwVqPcGIwa$T&0}qv*^O8~;BKQTSsXN#b$Mnwy{y;KxiTdi`w7`+L~H6>VK?uxtx4z&%Q$)$bq%0U*&Z3So?=2&<$@w5vY@u)2*>#?WI9E-P%aC z$Z~Oy9JMzj2U<68%+W8@5NqZlpFWAoUDpbs?D8lQ}G%o_3)gg zJbTZYSWfPC%#r=17#lW6^vh`q%PI`}O`DoVBMiA4SP5gziJWHQ@`{iUMl*Vw*JARl zoED;y>SGK~U(61RgnpCSG2Tow{TY#FQV2Vc9i~0+G9x9Z z6r;>23Hk+rS*b-~c34>)wH(<#sBnk~!&pUPD`tm9m600A+2InwRB`lg$OOu(t%;Lm zhK!J~p2vpdNEjR{o+?3%t7d%uli9xSh4C*mMUY-_IqC`+g2G?)y2&__}u4z6&tkbhBan z0y|fp#C;cVpMs2E$nqrad%uM7_e&UmKa}wgLK*)cl<|w9j9(04Tt6gI68BvSVf<1F znn`^C1yMg?Hfz*xSDDhtWA&OrdqXHVlzOX@;Q~WeGLWscZJ;$gHIzT zqE)_zHG|F4&wb>pAafG3eYL=E)+A;;*W3|dmQj*;a}AMtN=?u`UwVlo(v>8n^rV-v z9T95tx-nANxqwSbbxNh33yBh;m|K>d+B#?Wa6x0ta=V~hgGQvaVvFnFvz_;m7%Ep# zPr_Ia3D=jfvNL74XN2%AI=z8}G3|oWP{PXolqqN=VVUPh&y+AWQNj|`5|Crx+0F-L zL}ZMIlS_%viRvh9wsSGqQ0Jm*C@8bnWILBsKTE53znJZekygJHlRF7YV=!?;WCoi1 zCy`9k>Vwm>ov~?V0LC27cJei0hOCpX6iCLjldqe`&~W4sIGhfZAdU<{kxxD~5~K&6 zeBH#!5)>1!OiAx6*-kgOY0+Kxffz@^Lx)INI4kVodF{bJ7#^URw2qU@y%%`=Hs*oyshi$&qI0uuNS!8~HP2(L`1% zMcGcQ))lA4i<-Mw996hemBpe4N^UP~01-7nb%eV((dm;hLN+c@f?SMAI3c7u9k!@s z*eBWcb+3}Ys!HtZ)uj^dn$33PWDRlxvFtC|_GbpMWqpnKndU~w+n-_Lsa0BIeG`wQJ`(6YbKrRk5^so6FI*J;^D)47zST7_EKml{ZyTTR)9xg*Uzux0g|kXLamlYVrbY{l;v2xl#ophjNLiz9upPipy&Yo6szkPwrhE*Z=wB4FkG zU3ASZF)p??E~U?w?HiARcFHtyLW1VT-bltFg!Z!r3QPk*Lvx6h3Z2_DR*wC``af z)ySyP82Xb+1ETUJsK!H7fduh~@g)in!YNIu02!Q8b4XMH1N4-dLR?%CjM10S7;-%d zi}R)GYf`aFUom43rApY|^>OyghV3@#n(dcW-$o6=1izwd7=i_UMb#ihrp(WN-Yas4 zF7mu8VhzNpeJtxY8!vA1w5%L?s?oMLRlf6fRxXHEFTy>xX5}fWg!hihlIvSzY{G-z z%9`kfAKac*pgKzVWLcAxPXFY}VJGEU9EG7C&T);D_rCSFHTeM2 z+I7K#EW_4a0cRmN-a(m%-#1H3JLzdlpHfl9?dEJSlpyKHm0Xvpu8foO$X)7bVJ008 zw)wDOEYa`dLL#R_#de@|l|Q_1;&kapFBsiP^0xW(b7i&9;Y8NAJXRBbw|txb0*Q=J zRu_8Nd}Y8ltQOf_$Syf(V<$C`VxdL0!V=7OiZRSyX6BSDPwk}3hFRAi)>O3MMbUyp zq}~B&BRy-5(%DH@Z>DzO-#=u_SPHl9n=MA`mB%MfcL&JW+!gc4eJ;c z%4JV;V8u>6OudJUkP^?|TyL)0+Z%Y8A#&X34=ZPeNR__2E%rhgBAsH?ew*Jr`eZ#Y zmlnNmv1LhdLt zaU7#L0t;zx6}&d({l4E6o}4&)JA)V#6yFXZFKLb6_p!e6^7_Hj_h1NBTbvpfy-}kH*y>%#&68!8H zBKj`11Dfiy!gDXgz7|+PV!x}Di6ikO2`>PLVs(|zt13zO6u78tmhVMlvzmY-oV;gn z7LvZuHOEyC&69fh;S_Y9nWRg;d8;r17F8~+X0~4rJ$;iN75{2k=xyf}VU(tA7j@gP zkY-R@&n&c%iCQV4y5)hq-j~Zyep$GwZzl9(m%ecQMuI zR_FMiS4wCdQm+ZPbNs&ZRMADQG(q95g*gD1z&VMZ5SRr_m&kDfTY-&I-yAWL*!_iX z0R(a(e}0lhmPvmK>aaVXNQQ9p)IuLhIbWV}1VbN&hfO_XKHmD|2I#CsuYAd)uuyhdlKn3-c*xb zQuDkUlf0S@hIDAmcl5$*UH!f);i|_hr@OqBQ2!26(oLPO^(TUd8C0UgV4Yu>3V5QV zzpdeP`}M{ouTFy@t*aF0cV3{sd!&N;z$1P~pZOuu{wKe4M)}S}=2!DOZz%rNemZK_y>0hUQ@5eu?Kt+l7& ztdS~qkT3`sP~vG=eSWrB!bG<7I}fvv?I|xR;b~Zl#sOnwHt1QT8`KAf5X6Cg=OIDt zLM@R4nO1tP3_6WgpU*wxs1GCR#|Tq|NzcxF>!@Q*3aFhPp6M8CB4)$HIn#zu{|DwJraFLMu zMfn{b@HlstWgfpvm}B*|=sSrp-7mTh{NeTA#m`BVy8~_fe(%O`>1J-Bh8Dt&-t2eo zF~U8!&eYIO3HKXBCc>4^@riIBq#GZMB2k2Uj}h*J41x#ikSM~v#|Zbq-X!*9nc%hJ z+|ynwB&2?{qOY{#oN2{_V<bMfOS}1<9w^gYNvMo4 z+9OFAB_a&=fU%%5^=LtzC*=j0SMK*a9i`Ct82RoO5n5%t!^bs^ZiPAEY>1FQ%# zf|d^RJDJMth%CN`nT0QqQdQ82oV!FsF(aPy#!Y@F+VtL8ulIhJ-n+XBwc#c>(Z+;5 zxEztnr@EzfxkyPy!GrY1gZoL8u^63(I&2}~67UX*B8BLtdJrwf>@2pEa@ZDdxLOB; zPn_-e#R2vq;#w)cQ;m7nZA~r`THGK>`9)5$S8NE0a&=$tBVpS32lNzy zo!e)e*>ja)(27~@cQR0tyX9rhedj2)2$(OC;{>(>vb_+qnoCXO;-tk-0FMX9*;%~T zy;*9UHCD=g$o1P7jJWm{N)49fQwxngzjkLS_&8O4L=#ST^4lMmBNNN9_+{Wl4jKCG z&m;+{Eq)s~ZEVNm2ul4DDg3ayr`&IUAuf#+{vP<&vj_WF>hW!F=k6yBPsT6a$68LM zpH%6=JC^ock#vuLjQfBM+7vv_nJd^m>bHMHI`^zV2Ib`}o$J({{+0Ay20 zu6kdB{aW%5mSEXSInRd)>9bXGmN780fDDPOBQPAeH$J3cze>aW9sG~bu0GXGyOyp8 zZLMk7(r1V~CHL}p(s&|Ddd`2Je@8rp?(+(r1&n%ya$51{5bnjH(YpCTm)HP znXd+`J@U*ge*3X-8gtTe`gShOX~TO-sKsf=l`*3yjYWAM%amLO9HZKPk_TpeJD0)c z*-zdg)0{dF>bGB_BqWTx+_xXYSnkkbu4tR*x3?l;W<;m&CbjKPf-0*X{t;0oipN1Y zP~F;)i$L3|3U(-GQ)tve?beCNg~`K)$#u1bN!jOLLAfQS4gD;#&i81nF*M{v zi=xrseR45t${KG+%K7a=n3`5XaGECA2TK_3w{sLhJs4g?!PIZ7Sn7Mf%aH2_K~Yt( zB<0EJvM|g_0~(W~sv;pBs6paeyGdlHKn&5cJAtmqUHWvCsv7T(L6iM2QxgmHU`W^5 z({FP-pL6dAOfNYM!)0{LvhfmWM&JQpu0-k**aB>j2-Plo4mc3`v$kXI&7olfyIe2us_GI^uuIVV`G2X*^sE6cZO9l1f&E8?MuQ>stT z8gKMyPpq1`y;*7f;xGI*PJ~jZ`x?-}ZT%$hWLUYBtf}n2cmX^K)I*?eD~W_@arIZ? zlC{7C*wL<`-9y{ePjyfKrKWqj))x)n>KX_v`xB7}{jTVo^u%&p*X2nWx}L5bO=4FP zU_$N0mLoJSxsOzFjFE62|Ns}kqFMx z@`J$R5*bS19pEkLo~;DF2EH`i^IC!Kp8h>$R{4MIp4XVRZtR}3|FL^sJ8)C?v^e0m z+nVm_|D5Tbwkgs*rV~2qPC$rBUHx{Zhx>&YbeU>I7~B@Y4x9d_->yTd)k}-9YM~Kc zUl~sWnj&$3X}>=7c+%6U(^riaRo!RU^m3LKsq@*DYx!+;|AASrRe9e~l@F^i^#<7E zW4H%y6@!FK{{((jeu29E03}$F3?z8fG_#x`po2NA^r8O`*=QPYy3BA!yQ9ov z85#Hyh{oco>!2T1=|c9J^)HE40{xz$!0hwf82mWYT zVpoo>^Awkm{`K^Kh29RTD>_)2YqH7%vIi+ac=mEzlhbcrC*;x<4tiC|WW z*{Up-dxFHQ6i)*Rjouz($5qVAAhtivt8-rO{dco+4EO0qy;WvKmOsN)rO-uhY6O!13;$q+dB#litO@qZG6dwa#sHsfC zY3Vyj`vv$$OaIL}TKX$fczrY>QAz*ZQ0c#Cq`$H=2py5WXEu(qm8CVGkpA`bUqJeU z_lqRqbY)-Q9*JNzR0GhFKx3L;4j}m#+GwPSM z^an5aU!)&-v-FkK_;=}74wXLId@$Spq7uBMms3;Yi%QAMDW!d}y@;)p64xtATv2H1 zYg$%4EbBz3{~%M#fJ`l+DrlVg?UqrTJ%~PH6d#P&gLowq*~)Xt48mvB7~~=Y%C7vE z(TpmGZyLj!zVRai=IP2adtLoL7D9XKEpX3vfG@9$|%cawAzLc?m ze!Vis9!c@_7c!Oz-?P*)hK%L9PnU!D^7u^q8@n~HP?yiNU!%UEGU2uVD$_ou8d^ri zMW%g$D&z~y&d{=C*LgA1elNJ&I<(4-NqQSJh#|Yg2exL~qlNg}pgt0tX-_C^BAwsL zvafVe8#tAEWb(?oGi;+K+E_KLT+YzpR;GO13?y*? zx@9Jd>J0lyVesPRkiyA5w8dn2*%QzVr6QEf%tJMR1+0xTP#Bo;zb~R96Ol(Kg|#_R6^F zs`yM@d^)05#VIl!-lMmW{=my|({G2A9LOI1VcbkMl84VUq~t2^+AF1N?zI_nFL#U^ zt7NQgTjL^%$*;Ezf)VkIxwkXo#$tbN4`*!c19sLNIiUyPf`=dR8_;Vr!zvvsZR0A$ILr-oQ& zv&)*Zu>{p`o#N~VYD9W>BRv`Ut9t?6NZT_!?rxFdW#Z$3Jh-3*edv2>!Rm*ARiyRU z8h4*`)=A=z1AE8G=N#e&OWI{tWy$iQXk503@)9VA&HOqxp3Rg#G~^WXtJ>Z)Z+DHI zLE>k!eW2cS=5B7Y!!njlnWw+87zqzmj^o}P5ga7wDVorTj;VAH;WJy}8l}l0KI8+- zjTOs;VYQj!L*$kf*GidD)0;ARJycoQqd09-#jqpQ>NjXpUY3^h7A;Rhh&zdRBRVPjFMS8E?(;a zx;2#>^cngeX+{~;Z+doz8||xSvSHDyd1j>i-C$k zruyBnEY2)vWDAYev%KVJEIHDv!tcBt+mj*0_qF8(xY&Nuo0oA!V((S`%AgpT5;e?< z?H^~^d+w&2dk%;lAa?MwsZx69S3Sz>{=fZ=gF$LZcJuT`9e03Lt9}8lNalk+hA#TGD7< zVmfJz{57utFBs)4T_ANP(}0@nIPbZetj~c@P48!1wR~%O(fcWK@rJFhkEj<2iB!u7 z=<^J|@`I}}uKr&TJAs&60V0W$t!n09y#SPy2s$;4z10%|9IIb{C4zs72yQhZSj*U0 zdyB_sSHou#}ml4AXGjR(`#vE3?B(vW?6(TYJ)m>Fvr2Cqww$q*gIJCA=L1Gx7Oc|O z#ri=*i_YfbIX?Z_0R83@IaZL)WofI(;;Fm`ZSNZd!{P~lEaQ2*6g0xICgS6a$>}WV+wrqsV zoHMc=J~qi;4XRLzINY$UCsUy3%BA_0GU#TwIugT%iC^ah2T3MgWSq?DKiiHyz&*pbW~s!Z8~0lTyp zS^Rv!b_MN4EYaAVgLxkQHdHF&vmCJ`={b^;W|G7xH%X^Sy6={*6vVhL6)lI(X#D#2<&QpdRg`xYE4UE{c3B#qcc zQjo84LrFUIc)-3@bMF$B##Bc4DP+wPQ5H2;Gw@0!R?aqzEBXpaAKe}}W|Xj+8IVoX z4%MNsc*{AoLaPE<4j$Lv11Hdg@dL|fnMW*=yKp&NM(t6tF zQq&$nQW3$BrGJ6$fqoQw*QB>H$(H5h&qBYQLl=daJpI(#4xoiF&^Ry%X7aDe^!_(0 zV<qgB3yDi`0BBkc?#G_qHl#`Dz!GIZX z0W8Kt0UBLVEQYcRa+pls^9^`*Bfz70^~R*+=mT_Q%N%2dN^)b%etVvY1fI2cfa*m~ zSiU~1l&J4Ftv{<;*Cp@FddQ7TO4@m@V4I8vu(tbpY%2w}f znzqn#Cax5$3t-@xuG?+&sQOTS;BX>eUnyzhZm$EX7n$tIA=ytCdOc8mqj5ch(mBAe zrkbyG^J}=4F$RRx&t6i&{yN6u@^YVLe~fI_9R*&M$Snju1TIMg?`YjGz|TZJ`rWb~ z_5&h;j}iUDc$p6;NTex&mO#2hIuSqu56@*b|M+gC&8Bz32+6sZ)HA>-)9M<{bh8RN z&@3+V@Y+)&*Q}qMNtH#{^fe#T;9N-NLh=M8pB9p1Aoo|`Q;#I8lXX|fRp&oLNFLE7 zJz_F>3$f`C`v^H;nCy>1fA~+}pB^y=g(CLX8~IB`eVk5mH+)|Z0ZiBD-_ZGqONS!rm~!%5)3Ce4yv(L8zg+{<0Z!{4vS zy(GDlf-OsX)@yVjCG^_IE7D36oLoVQNOqpx%9BW%s5X|*)D%=vtVpii5dIC3l+k)6 zI{993gIKx2Y{rf(R3}dj&0c8f<*J=>nygj9T3{kr@Tz*4?Mzmu85pOgMD;{U9Mf%= z_K;&Zsg6Cqf~zq3Q=zAu)L<)NWSQmCO<9?oekoGD5M zo^ZTOs7Y5$>0ZKmNmtbUMl&*HxMswgFzQuQT@;)^Qeb!=rZ3*|bsnaNeA-d5g@N$E zdTim;E2vch;3kj#;$={3Vl@&Gf2gq*VHN93{uj%qCkHrDyq*0oM zTJ01I1%XZBq5}oEbV-FAz={Vdv(U)AGl(ZDxW)}T%%`WO4HD!vAZHE%u@O+;Fjfm? z`Ggh7H0Jb?;ZgOw_3Z6wdVPGW%ynWuoyloV1r~p6q$FbyIDi<`m6$Mpwkghx^Pd0pIYH z-pOQc_ZpS4UA2iz+uX2+Y5UAtu#$eS@}!*f5fHow=uJ!03kVDchDro8u!;F|liaJ7 z&Pd&~99Sw9Dmc*!gj`L zgFOc}Kx`|sXFb8=k9wqRmJAIxa;4J{s|`w|&)wOLYI}V|Nd4@yZrBqpEloPZ#U}Lp z=JKL+m$bl@(j|SaBaQk73->i0ywwfMri1U_ECha`Fb?15c1W_CkKQ(|YgC~5y}y?hCSkX+C$dI(YDZ^5=MJr3h@xhs-65FGLPx}N|3t|akubFe$vBaTo}p^=vmpK_ zB)hTqv|UEgcGEi7Yfz>#%`~dW4I4&-W^I z=%ZnFBgs|+)y9AgSHWR+*PuJwu(!NFCcL7S!53`TpAc4;j{Cj&?{xdeOdeY*0H~qh`An>e^c~7&Gg=@ldk;?IQZP;1I9OH}o z9Gv%ncOX+;Jm9!%#C`-=ZTaGUx2&z@0QSp#@sR|o0|B;keep?>lt#1(&_E&?1eh1M z_MSncrLME-XvyhBY926*EuP1|2y$b#c;HL_;^Tkhb+NgBfw8@K9$RMvd>hO67=~F# zOwVTxwd}cai!PIg3=sLJJlgmfns}3Ds;MpGuQRnS{2Utb4?|-OP%M$91P%al-(>Zc z!Xo;-6<2=iafv)h;IF`^62WcV2KlXjNaO;M+kjhHADlR6D%0emcfsOeX)4@-Igv`EU2*wR-i;{F2C2Naqoq>)*4V8@l1U5!l&iq%U$)A#Q zFK|!DlBj8augQe;FEajhlym;mu#Ft8ZR3G)A#8(aJicI&o0deRn{gk4aRb{W=^oof z)N;QtVC)BnT6_nB)}%>KiJ7b>qa|W$Xu^d3Bqt^2vkRy`r|cnZ4?${LTO%!yjT^gO zuntVYld&GFzZ)Fhz?Hl2ye4obzVFaN%UN)@^fX4xRL~6h1`9XastYe%PT{p-Q>^*{ z4b`St)yjl@6CZV*Uu?#IOa({K|DRAB$#b^Pp_I=A&T3anO32`3u^c@EAlT%LA=I-C*F%M{vK^En&P|%Lo$|l%pGr+!GJC5 zCdQIEX&VcfZA--(j3cla*l6^*DaJlH#F}oDdcB;dRZUDlRc_bho3lTTWtkpt`&jU= zeF5TCT0sksVcU~piKkOT72KL_$ADK7i1byiNzl|bU#IN)ruZ?!-BHiHVz0J+Y^rUJ ztH&AOcqZttO|`$PLXXE!qR?8tu;)wul>$sE+kTc>A5XJSI4D~HUQq$ST_sk&8FyB@a@Egsj7 ztV4Wm`x&z$BOfKG(aO$@BI}uNRN=G=rbVpK$n06wZg4Aw>0n>l?NnM9s3VbN0xf~& z9v8VJUKA6nd_e8vu~4~zqZNwikg9ewo)RpHhR&fw(C7|y4drZwxOg>&z!@mQ*`uuN zA04U&OX|0V!_(f5bxT6(7hlzGjRGurOc;!y1QXJuGl-=AlK`&9ql=lGg9Oe82@q1h zidDzR3&^VMM$p%LW%m-<!+%BHD>L-QdPTV6Bm{OMtGSl5td6MXE^0v@kDg$4 z>g}7hz{4^lg^>D1Rkin5rml-{Ctkv%p8{7xyadV^TO_5`?eU5w)a{$2_8G zfoF6(TNNtZezdB690RvTG&XR;vbI+M{4yXPW#|RoHA62zpWYR5V?U{lN!s@7O(-8+Qz?{HOnOl`|qA?79J3rg$( zc8OK|!84D4_=*om2UIUj)(gO2BvOgMIp8g3mqT95l^LjUvFg8?LUOFv@GGEw6v`{A z1g{rE8vX+)^tu^`tq<#QxMH%WE#77{z0$_jy>c^p5zUlo zV!dv6o2RAOHNL89FJPJ|9w)7S&a!sG&rU3!FJ&KrXh5l`nkHTsIfV2`n-hxre^#|! zOlWa$X(c9fXChD~q?O=F_x`h_M|NVqccur|>}{}OA&Mjqy~P#rkJhZV&GkBqkowv0 zSGCuot!u<=`{}`*Er4e9hJPV}I|0s9`TVO1Oa#WW#H>EjP%8*hE6~)eb`h(1U1Vit zh*e(zI`i+&*%DbvU=<+C!|KR;=XT%`>GaJ64gybFkoKFkbX)dFOeL!SA%8w%gKIgf z-Vi>22LDyyh(xXucpEqiH5bI4p8{7xRH*G`F-56mA~-1OtbCF(GIn;NA3MJf5iV%V zPpM@}RA?8)c3#KILb#1)r(KxG7$N;@5YYnxuJS|I9mM^AboDM3xr;y&P!-j8M}-Xc zb}W3nUQ+97*VvGDorGj+Y{Xq?(yB2+5jSZUy|_z^om+)KYoH~@E;UxjHVh@3UqV(U znVCY2snMJL+Z9@7-AYLR;v@Q0g0MINyLtlMC4%YOB@G#1#z8w4b0JQE8a!r#M11~j zMly$$lxs$8av|b>2?ApQ*62R}Qv~h@W(eOkMW0R)TLvr%>8EyTV=T34#CZB?wK$T} zPrEh&8$$XCH0h_NA^r3?C`{&|{q%FNpHLR{s~s_wep)TtAF7|&c-SRAvMc@cCh*41 z{nWm4i9m0VWP6+e-D*PmS21Fyc&JK}A42SsL`0I`0ACqNwr6!vB1t2|;NWe2gR{k+ z?6c^f3}4T;Y4xl(<4Z{WaE|0a)|%I)I-H|j{{ns^BF@q7GC*XAbJXfckN_d|3vrHi z-zKm2d1YzXZf{Se6(SGeTbUK}q^ZU4)P6;5YH-RnIjy3|Q5(X+95cnY#`=cf2W@l< zJXYQ%ch=UF{K&fAR$`0M-a&~FFO+Q0YPYa(pzjVwoA;EJk+J*D85FWsTRRJj?`zd8 z;7th4SC|ZTwe!NFi;8A=)ipoiRi_zeA5?ZWsZY#)ym@ThzQ5S))GqOM7#*;#26Bu4 z1DRz4a;z=23wkbZbMI!~XLoyvFu(2Y1KeGg2$mr=+j1V-Cppzf9RUoJNF4&~XzZSZ zKG)b|^z41laclp+={YNYJ3B%Tjilll$3Vy4?w%(RCZF980c#i#-fb{;he(eAA^nSt z-zfw*+$NX7Si5%u+dY}KM+k=+1!H)rL6Np!ttI!+c_bG;*(&{3Y!DAeCZUHBeTP(o z_nVq>gOcw+H%phS;~cu$95SP$!(d65w8_Gtp^`O2KQ3Fx4QeCpR>5TaYB^1}Zio=q z&wNxzsm)5SEZf}*X@MZ=ZES@E9aCx6@3fPLACg^CX=Ai7Vg=NnlN|K{p^~Z_Ga5uq z*PWA0^upBE_?oGWO)vBIr`Z$_qGVOH9J!J7;V4blE1HEyh)5ph&|n^?iG$pA^+ZCN zvIYChqwU5EJrhJo|J-=Hk!)a#2eMDJ0T*KTL8}WJ&EvHeH9c%l<;+7I4c`4a@G6N$ z8nv%kH=do_z*8Ka{0vpoRL|y`OnNJ2>cq513O6J@F6A=vW{h*MznGjd)vyv^|bQnBVrq0+ofzY^dySL##GHjuC7+Qg}U&;ysX>T{nxW z_@=7-E3my!R@&*P=<9hmZx`hVZQ#$~~4Mk$eJ7%#U%X-RVA)z${=26U5`O zhNoXW`<_ln{kXsA7)r50EW_iqz~2w-V^(q6n6nohD|(tl$o%|{Jj2?^RXa`_Io91~ zl>C_@f9A@c9dSSQF>pyDpA+~U_=R!mZqb76 zTv;?e?Nj_Zgt;5v-H9Hnv+hujeQJ(yBd?dXoNeEs2sKAQCmLsZZwi3>=R_}pZAn~9 zLU*8zL>?tTyZ6qM$N>WQNqd#7R+v-vp3%olD*jtX7@GO+?p9=adT=gyU1ks z4$ThE3f*d(Y&i+wHE>g+SGpTELarCy9NPZP!E=M%R+#K3LrQNx5-fexWTQwy;+t1^ zwzJzek{z108(pIVE2QcU?5;L6YsI^f3rO7-MxRx{H`w?>{M+90RGad7g0Ard)HU%KJ_aa z8B`EuF*n2Hwv>@zDI~IKiBF1U4m@BV8J2E$Cxnw-VM{HXJb8it+7I{nP6tO65CVeetOTUiq5*gfzx8~=K zkcR3ay*CDCU<)oSORE)5kX92+QP{BJURF1RA}}9hY_uo==#n(;urm??>X^STP>dRB+Bh^ zU!}~EXi+*s{OHIK7Yg!=8&bc~)Gu-^b;Iag+EoHu@F28H$H19Lv-8_-hG^aR6_LT$ z(0B_UO3D@RAo7BH(k5X>u9*Z<@N~sxO9se80&TH?4O&;B7lAS$Eqfoz;tgk?QIp+-CXvD0fiCz6 z`Q;3owG1V`Og6m}nvgQSGND7beUj4bLX*->JYU?MO{ZG$jHDDIAaU6hN!g8h#b+{H z$lrgPz8e1(x8Ngj9?5qNp6a+HLD}q{v{85wzfUHN;yEc2BA#n{mCY6Z8_1n;&r!Jq zDSoKsntz0>66k%)-@px{nbHmuGm($-vMM-#Bl`?#iXbsc((Z#Z12>ubR3s||rdT@( zg@LWZt?~(12PvZ}Is7g%07-NNA<+?^4%p;!4tjx?c-N!~Dq2n-<%A5ks>BVq%JN#K zVe_+Wl*nN^Iq)z2NsaMRI}Q~|-JCO9&H&bmS^vntoWv*rzr$Y43mQ|1I$5UX3;J;t zhphHvL~J4Jb1Ck1U&n2~{vTTXlN@D06siLYzCp;|g$;!ozydVf@;wr0gXb6AWtu&0 zu~ypcIZ8%?6OqAlnY1h{N3*B>z?XF4{|ZZ*T}+R(U4+WwFo;bLZ+}&HwuN%?&FPDC z(}fvWG6son=Bi>KgKvwH7!H35XTb@^#}$nw zfZ@DC03G2|;emJ}pXKi9Fa|N-~-#88HB?tc9y70Sa2ls^!Xd zEQ^|_NF@=PJu$9W6;5ueS4dZO1Eq@uFb`KE`zwbD6>${2`*Lctwe3enptn((>j|23eS+YxwkUNo2qY%%iWp z;FteMtI6`87&Tdbkx`T7F@#p40#9hbBIs<0wUcl*)U%6DNA9i?rBo; zBk2yZi@ov+?vh$#^JV25pgNiX$|9@QY|$;b=AV@>(#Z_x(l!)bZWvjm;ZmjO^^{cZU0rm!$)@OXjBT>h>ylm2RgG~CO(&B#+rvDR$6-s;i(P*@H7Q?EX8#mb}^^h~$WX~mt$3KiTcadYh zbhM0xsD|?bVHtdE!!Z%%dW9O#>1-_x3zAmLOf7L=+!yqsRWeIYl;G$wV?Y~_JzbbV5Vg6?U|ah455T~F!<(j43; z?FcOQ;W3gW)eo?Xi+-Q0F*aYt51H4^Fsf)SSJN{c~!3nbcV4mDNL) zIi#R_0%a~&Wws};1Gv3Hn<*1KDf3?eb_^9>cEu?d<8~DXU?|s;3;EWnM-srCU0o7a zLY4S0sidyaY%X)pbqn`Y)CefKLO_&?viBl zfRvNJ;ERfM;5C*6kk2*Eamfsym4 z`rZ*z8q;HnRLx3kbbuX%z^A?qXXB=uCxg3divw~F(P4;ZtF~* z8!T!rxnF=CF8ypWO}Bn7PLA_W`U}r@xOKu~)-c?YixSGi9h5*BJhaw&mKxlXA!hwO z+#Ly|^QIXcW&I-wG$K$4S5N}f=$lxawAke5@t{w?NlqbWV1SyTwww#jA~KL=`<4rRiV?%=1T^Pee(DEyIi+Dj{luL%^;IZG&Qq|8*92I77-~+(tuuw~ z$}okgmo?W6?axDcan9Gkob8TX48XFPxuzpox;+CJ2K5Ue0m1LHt!)b3HOyhbKfb|* zz4!LXxOw&m$YH-X;y$o#AYI$Wjgr4hd&l*%k-V06919JU@mpB89$l@t)?64_^IMQ* zjanbqE(9XwWxdr;$IK_u<4Mk5# z9^MNb&3^^!QBG9ZIE2-5t$T?VyyD~(n6;A})>b0Rls0h{0oZfp37)mJG$bJ(^0bB| zCsshSUYM?+HcA=5O3TUbu2FEk`AAM%Zc@Ya%pvs**t4QNx=DGf=_ zhuqeXnm))nTSIt7$)s9WL__#yl>;fKA+_@%@**qTsFM$At|4{vA|z{W zuaaCv%HH}S;*37^V{x-i|HjF4TkA_<2qx8xI8(y1-b6uu^Cs>!V3l`7oIxYWt7hMj z$2YTpElh|wLvzZ8(l=XhKlJZDc$gJ&E|SOC#5~vi!AfsMzPo5vwd1dhHhCuyRia)u z)FNu7GmTPD9{PHWn4ZBIirxX^B6c|RI(k9VyGzr%!=d*obez}p?lAOz8){kK7SQzW zF!X+#1gxT_ch^7Yt+7?pyPi+0nH~H8h29z`|5tiZ5GbhSH6v!}g?uZ`@YfzD{QWk& zvf|M!ulS+iS=`gm*0O!XEQU+r`M2-lUe|S)Raf);TLzeK*Hq*heYc3&w5qhjw`Xxj zy{`=EXGQ#5V59gX84drP8@wk!iuhDuCka=;J<VJrx5 znbxYFBlWUe-^_7pFWhEF8o`0sfRH}MBw%O*lIfMw@*2-ey^)pHL{G33mU@r8p^VeESgZMm~8!8 zp`^wZwc^`I8?AjSqK;{!(#?SKB$t%wZ6$nP8S0&FdBHy~kTNBsA%;Cgwx1HlwikeNUxd3yjUY#=*;eDCcE zq?my`0`ebkFCbAG@`IQ6$Vz*A11W7FZGklL<^XxpK;8y2!rP&W6~WPXRoD-o16tti zP!&+P0c`@bstkJnuZSkti5vDgYZl9|GOO93Pw-l$$AE6x>my!frR4i3nb&Q&WLX;- z6*gj3xHjsG2ZDyRYwwPDD$H_ycNVmb8$s!ZdszZ&@Ss9AzRro z4zzEa%rUPw?zDKB!dIP%vZP}F9g$YbNpKvTqfP?mI2*4jYBq)`i@=JGmbr&3B4RWP z06+8>#3w-d5AJtAYCv;?78fC}i2e|4GS3aRtlU78c`i+zTU-`+9BN1uNTO;HDeS%c zP{&C`cg`*6B&tCm&A$pHqVZjgB2t(DsMg4B0!||*K_&r|jubQ4R|TZi)gfYZ48?E~ z*0m8C&FzI_66D&*{2_M;Zjc1fO}U6h?%OH{y@zc@=O`xhj(95qIpMmxYhz7trr;*| z*C)r4`izK(r#>6P1r1@zWfqk?R{{u2?ke1h$C!XVBGCsC@o;WK11HgD@N%6*2MO$d zj0ri439};NeM~q9j_jwaQtlDBgCR_?kzqoP<+Nyy$QQrJh_{qX_OUG4q{wAH>)P0v zKqM|g0`PHD42~AZbhoZT;J%W0P%xZY*|3ob+H;8!=|upY1b@SwaE8b9SP5FhOcnjfleko0T;tCol6-L$B`%4Qbh>9=i{LRBo#)0z5?9F=*Sl9loG)&$#PN_GsNuu$ z#VOwie3Y*9>i1bhj1PZ-;KxKe_%SFQQi31DR9xdnzaLS`hrdhkIdLpluiz8>c4A*V zlNr9jjMvpF0_11!7u~vPNqMm^!Z(=ldJ}fwrUYP>cZ6?{$}_HQ>Wk|IY+*w91~Xo7 zngZ-YHC~73!vE}SCg(0>Yts_le%0oR2o?wmKY(4n=|i}^=>SPK;iS87`VkMUzUit2 zzTx}i@M3crx?F)ER$(@sB2*t&O9I#Mbi=(UfnV{A!VPC3YqPBvvNnghCoOUr9afF$ zg$$Uv`(;!Tb3Z440%WBHkPJnYd&`OSA28F{gcIw1Qz2ih88B-*Lt4YK>LZad zS9tQH@Q*e8H7k5N2Fm6q!o)fWpCQnbGDFDDmfp2c>Y{pAJsH0D{WrX(8NN$4BbuMB zJ|4GiB*S-jO?m{M8_>gdcuj`y%~5Lj4(I#cO3XHYMXS#CcD2IQd(sHr7K+&t!tfnl z8-*yMxTOv*&D$M_4BrCjgX_xh9nQTr4VeaH0>gKB?P3P95y&^*UO;5{mQ)vUCmFuO zYs>HrWGkt+1~Ytz*OuX1AdPW#WcUuRV@TZk5}+IzzQgOt@GV%c<6bLcF?@$NVEEql zku!X+%Aq@K)0;(B$#I}t)}HY04BuN$A@ti8@R)WZTmy!pRc&;y0gd6?Iul+U<+^1K zs~M|a2Bib8y#zYr>4EEps`O7}aVcY(_30H6&uVvm1veun4{NFOz5+4ZkhYNmQ0V8UHGfh{m^{4NqW*S5??n z1squqmDbpXlG;{JRbhKoUlov6yJz??+W*G^RAKwGAhpGDal^H}B_2u1QkQGHB*!G( z)>nquZC6YVYzmRXknrIQ-`i4!34eg|Chm3r`t()x85hp6R@aYv9P-A4hPkH6n@wQW zV@zPRj=X1tSAcWd~IoblWjYyhVSiq zkH{+3*BQR?>0e~{aHPPs9dl{hH5?1ouI-Ffs&2nYfN^X)8fbd~J$$c#z>kcAVffxQ zGM@QWv+!y%+$|KQEMvF}uco`J#MKW^_Qma%xMW}48$scfeQ{SLuCffP3uV}U!xdi6 ziQDd$xN`PeUHt zEipR_=$O_zCW#%1>!B=cbIV&tuth19^|7qHR`qgS^t)hn!4R7I5@l85~mT3?a8 zUQNQKNgu~uA$$8fu`D{$Z6QMN~uKiL)madM5IwSCQbn*86Uik_@ zs51s{KL_lLpKy_*v_S39T5Q(t>gd-B#7HZ&@@s|XomN1kx8EWX3*X#~wn{8AOUvTZ zzu?e1G7H&FX!|4FL(rX;2T7kE$wzd*v4$iE=iPjseD~VNLHfHqJ`4+UKIEf#+bu=ziA7-~)g%Nph31&t z3#i=5C1ZBH9q6(6oDhKzSHoM7QM4mjLx3>;~z4Fxb{3l zWjch#Ka^^=QxCw$KNQ{D4mepoMCa?z)@;2Te^1deNquN1{$3@io5s3V6|9B$w>%$z z4^~<27!2O&5dWY!LN!)C!Sv?4@%l?WT6f%<3lcweff4SXURR$-i_ZgHbcZZA?kG)H z3LYDuhnknKPVZ=fh z{(LCgT9Og{YRAXGqEg~7YJONdz5upReVQfyk^@@{Z1sHPW_xh_4NdlKzH8~)`wEq# zEsun-d)`1s|DW`EH0#zMQl}EF;!iPi+ItQ5{`3Qx{D|8kffaZ#oOT?PfYkE>?wkZ> z;z6bFxFdlHv=PPbFxU94+W}^Fq1SWGv?jy1n0ITx-mUqGbvkxyGMxF&?M9ZXa|<$* zL)_|giJX{bQdQtJEBFx?!T9>O%ZJRUGMn?3Kj|%>wk-KM=J0gq1derkyybeXnptMP zy@auEU4-SQUBRk#wwJ^Ix)z-6b@K&B7MmTx;q3(xLHSTv zyeuv=|D{U~3yqg8aOv6mYKQk9V8ySl0N>MTgP1qtzajg?Kk@Ht6927gIQ(rB;+Ygv z9)1KeSKy<H{tD!&Q-#*lAh3d|al{Z|f-OspIJZ z@o!+%?K_NqUQj&#%~(FpQ^_hZsCQ2*exwBWhCScndDV&^6%X1HA$mva`1xukBZ2ux z;}>duXUPY>p83&hzXAFC+bC7xwZ}M$Xuq6DPWr)Xzw1*(`|CXp(u&B3A&Th3FfS{> zlHp`(kW`aUQJ*66+ApizJ>9wvw7hm-RY#?Wymo&Tu*&iIyR%+9O7u0N?K)6Uec2O8 zEs+I~y0s(Fi0Fl+OuO#2OF7vTY)n9j2{E<*A|P_IFw!Gh-fKUjdEj{oe?2idcY@aQ zsMpSr0G&)`xX;%0-ZwMc>*E0Nb4Q8h$&0NlUr_VjuO@Vm7!9;D0?qT`DzDf~lEKaf zxY}hf>OS@UiV@fHEK3&^84z>5zr`T$Zxh`e*VQPUZV#ov)*Mgz+j1T15=-`Ws8G3+ zf3M+QZ6^h5jdF855mt$UUb|2s`hH$V;-}-LNC5HQxf~}evw8HIo!fCgN&tPk^9b%| zIA4N&ZdYD6LRWB?B=8EJ|KNCB!<9F}vUVXGyA;}cc&N&*Cz*{b*4y3I%^PADlql~# z&0*eytU0vg}f2rzr=E@j8<0%j|+}0k;o3W;OG;;`MT;>Bdx0rskNTB1=FXiI8W)k ziYbYEOPR+$gG36p$AlRwUjjNXH=VFpjPMxU@C{c2a zD|zJ@k?M_kbCqKD?v3*)X7656zXpKc zYWIGwawPY9A9=rYN~QlTR{an7ngH(-!5PJNxA$v>trffXE2G%4L7p$*9i}vp?t0w@ zkboag)1nCuFQwO$%`nuhZ2#?&p7J&@G}AD}48n_WV&YTu@EW@7@1h&-I)FMzc-s5A z6pY5$O?TS$+xOJ1);aGRiW(b?j$^!UQYpqYYd0+2T}|2R-tkVV_P{t0;ImwwFOdIa zq1C;WIXNLd{R{R^#*jbE*(_yu@6I88FF(5*L^G%NCHaTl4cy3*AI4*LcRt+M-Me3- z6dCHBl-<5(x+Zjdh!l!#(kEolR$IUPXAWgp-Qxtuh<*2TnOrJ~{-ukW+>I)mz#b-9 z{H=zo1UnG5rtId!C(!0eKP1R`BV#uozV$dr4_7`6F>YJ`op4?9JMt9lPO6z8-!CpS zCrmVb!cCPmBJ<&@gj5+pDK58T;PO9G5(_0d#sxloV5(tW`2~gwke6U zHceP3t*_181l~0ib$~9cS}{R=loB)d_^YA|6ZqPsHgnr1BxI{rr#-ejpP;@jsmGXOMs zC*9HYqlFhcnyVn9m5`;LVLCnmg>pbj=2Uq)t>+M=+Os=nr%&;l{V zFW**8Xi-7@;EZXM&>{u@PFdX#C!@kP=m)=A`24rzy|HcVVs z$=8O7>+(B);=26KpZE>!+3auW{O=4*oaNW~uU@5k{B-9()me4^#95~E?|Bo^m*YUe zV-shY&cEj~V6!Vq=TDqXlb`BL_($AEf%Q)O)PY?DcHXb^zcUbo((@7*sZp572e^B{ zqEZqW0ENreUKSSj+N$#>E_PtF(%wkj`4d-|-dz+)IN4rvnZqzKS%_9>8d2fsars1Jo_sRyqsrT{J!^wvGDJr}dt9WlK3A~Eu1>AF{^IxNv z`PoAMqw`zw;;7R;^BZY2)J$v&e!&?0>5CK9l`iSuz1~P{<-p?yB|eQ`_4|*XNNmGy;_1)B zdJKX#J;56d!P|oq$097Jg$Oz+du<{tN^Qg?zUHWnS`8AF*(F8PYMZFl3H?eaBk@|% zBJsuKE3Kbr&*<#%;h^wgMrUOZii-Gd!VIif%XLPM=p9U*Q5ritxd8r*!84vDuVNkX zkKH&U#;N7X<1=CvC&C8wA7ss_NY^Mdr=zy6Jj0cBIwM6Pkg&nB8ENQ=!eYUGU3bP) zx*yeN&bQMO5Glp>5<4TMjVIst%BRq&e&Jnp$t@cGfEU@k)I zf~(ypN~SP73+qhg)|3EcNLlmfNY+9EQr5h(CLkouQ_~*o!?kxj?)gH+PBYDRM;(Q@ zuek=ZpfumA`A`o+`eKi}v#@U`fQ`8C02Z6+aVyKH9e`7~pJRLE!1b4v zF0-utRdAJJ2XzPbiUZ36)-iU_E5Q0Yu-?FWLjHvE9``6pS-pleBMMlz&*L5)EnEO$ z7;Z4YxE>z&R3T*)06O0Oi9-E-%XMW$?4ZG9`;PD)lJ+0N9f_4Mhyhw{-zV}{~hLdfqAX<1 z=|)pRZE>w6&;bu*9q1*2u6Rb^hDzWSJd<$$lECYDm`NX)!|v&gaqdYw0t7k@2=>r{ zEfT<3I*^Au$dw`UnyGbWuA(m9W#w#EVLgu$A*`A#aZw$hl1T$mGkZ)EBjd#wq%YTYGHyt!`kXcK zRe6uI3zwh9uMpbhV0+)B{uDj+)=;wRB66YE;?&c*uHYMvvOM#K<+{mvtji<)aU3)1 z470M;H;mFfn_#(j-UAhd&wB+s0z}T#b1FCQyI4zg(AcoJRT8vak|ToQz=vtO?Mk@7 zAD`g`i?EKeL>G_de|?L#hg$6F?ixuO>#lH?3T01o^4rQ49awm!s+spW@UxbyeHGwx z1H>%KiAk9wv^W_(Knjp=r}I2-xmV5?^r4sz^pl;R>H<#&OM4gl-sA2;vs_tD*Vwrj z9{0p`H+h}Jcw$FcdY8TtXq|#lq+)a*jWv1gBk554NP%WJ$fLxr8=LX0#4VwB726}- z;3T0#xP211hUW(Essw(stV0lW$W0%*={9}nCc5NMGA_|iI8UP-$?t;4b)}Hyx++V+ zBI$?f;%fT|CwM~Mg?za=gsM8!ie(du&@a+ir^gtP>_S#&A>`(h;Jk=?-qicFe`B0B zjd69OX$-^J(=3PJX+cJ>Ax-~~(Rn@`T3KZDnpzuh$I^GHzgm(x#-X9!{bb?v@T0Z$A|PJC~vj|2ar7RH4X zS@v~ePhvqys$VKSp6Vpa>GDb8Uz!U1f288<;>!8Ns#oCra%0#~soqqaU_tM(ve*bv zv7e~euOt>BHguj`Wb#}kv3_}?{8U-H1rEN`%i=88e|gD9n%M_DF~FHJs&>tvGDe8G zP?y^-D?RR~kZWtF?vl@*#V@}FVu;)*jUmzjGwi9dNI#~*p((ftr7UaaO^>^_u_g|E z3ve|+u@~w%K*r%iKO@zPN-(=mkdo-b&gqDb8Pb`8i+J2k!zo4%rSGQDL$`4^{g!9J zGt0C1^zR?bv%n3*{4gF{p3R4w<=MPIUUCkQ+zjhFBI8;?4FSlU;MrJ@+l8HY9V*c~ zm{MY6xx4})AQp=Y{c!eekG3-k092c5V!L>v1daT^Nb|UhO3XEhD%AN zJx9*a%NaN2Ef87DG_5sqy(8297c4U@i=lbqdXMWnB&+N#XuVYu4!~X4cTiJS7P7}~ zBmh;)mKV~#)2+x{(4{9ARwfeL)8ks=*C$FeBZd6bL5y2NF5^8exZTltu?D)mw zS|;qKJLb{-@`eEX=}V7mx#?PnfW zrX$Zxuw2=tWr9Tvvlv$=25KvjX2J`K3A)R=da2xAkOdl#>wVBsUM_X! zP%eSqNe05wgey~C|6;wy({tcJ_^20b6!jU^Jgx*E^&>R(3G%%~p@}glY;bP-B0N4c}0oB?lqk}Lx{A}<;$@{q^A>9FMP7m6kJO{sT!Vbuio%?y&u8V~%j?<)T2_OS0O zdJMDdyM|c|Q%xtpvFafn`=B4kK6ccPx>y*0;@CcbJ5x0s z2cH%?06f6mqO%G;d5Rv(I$R7V?`|Pm_F*Mk0I1(#FF<6>KBB}5Ks8)d0T%VxM;%~e zfb}(-?Nf%$hr0uO;RaPe-fU-CYI<~Q%L?0Ix$b>ICdiwOA{BYF4v)mW;dI51>55`0 z+}ldK*zClpXB|yH{rIHi`h7i-=Y5SvSM())Kx`JV|CLzut4TE-DSyBLY(8xSTrUc% zlLSfy7*#tRQ+p*l1uw~`NmZHB%oE0V_)Mp8m45pmL^pG9E-;Qsr?w)cy(>s80s5`o z8z!lK+dJE1_boyo-Uz?|#c%?`=z+dYeBNUZQoJN)*B>5xsPJ&1M;2&3_Up`j9)u9H zk#gQ}0t5ME!W5-zsn-1)L<=(qXl9B~4M_3WZ^>}B(9^H$F!Fg@1=KcGvl>X)mqR^v zQ9lWLe;CO8T-Bsc{`R|;u%br74o{_d#>eT}7Bv!fcr(E70ZOfl)+F&x0M~FAXkC%8 zl19Rggjv=RS(K5Amo^f1gdTdNln4_N7Hx#-2m{oSN&-Z}o^XI20Ja4vwR%#xLqSKd zU5>mAaN2y29m6;-3r9zg@gwsAqTOt#%OBYVa6?l)kURC1s{Wb?rZBFgk;0>}^k^W@ zXS!Ex!CIo-D>C=_^(Bnq71y!MSDYpuvX2%Q$N?iYA527QSfcGChg-z9U-2JZ)(25? zK0EYdkDW|5zs(?rm9eCTRYz+`peUZUI5tAvD}(U#$GyzU7%Rt^9D0b|7`iUZZdOEa zrV;-k?p+?^a3=mA3^0!`S>n^bFuM}_n7^fwMkZ}XQ4mKL`Az)6G>ZlKY4T?lN527m z9V_6E6=FJ)FF|maT}2**Q3dQE#kR)_FiTJQ_XbiTtV3V&?N=h#pJ9B~(^bTdW2j!YJKi%_x&9er-D6 zF(mC+KVU4A%3hT$B_4YV_a-ptU8br`cm_^R#?qlyI`kq<$36$f(&!o|ExO=XF0l3g z@vL)Thk)%f4K{U#G>ql?W3n{P5T9P2nx`A`52k}Jakk6R*HiBRX9)OXhrk_u?Zi$? zH?e1>+l}z0n%369dH>GuwN7gq=L96T|3nHrzm#dC4ZNbj8P*N8)3C)AeulvFC)3kJ z>?EHof%}!a!@vg!Jb%=gcGkc-bnsZYe;D{&f#;X|aioE-7kGZTAIBN^et{1Oa>V`P z+D2N`5rlY-cdYWfuY`r6>c16e2xQ~^Xps#q;^X~9e1d9Y;I!>Wy3L+L!&mZjFl)s# zGqkALOGK0(zMw^|1Mib4QM*g?UZcqotByvjj+eBo<0XJ0R+&0W>v$5d^1u+QEF)IO z=?cf&39P6m+kp)P))yG$bT^`Qd?v6dvKjsAVUIacJ*)4NdX^nhL#QRep@W_`g0&1E zUrU13M%T}Lpy}uSrR%RKuN)5bd4^}Mr0j!*xBJ+Gp-|CIf%-9V22 zjANb5cZmT+Gh6p1`NwP>xREG7jK^l{`EWB^KYo!?&I0eP#7uJUU>I?(q zV~NM_4bszZam=s!9YMXEN&72E0FH*=IiA-}?G?Tfr^w)>7N~{e@zcgpHN_wcq zi~PXe)V`W*VLn; zXn?=pr5>PHGlSWQmJrz3Fy_spzJ?r4+o*YzjA1oNXSR$eJ@|Q#*GK0qO7HoX#~VQ* za<^poQjfQU@l%YqlA*un4v#k#xIQuODJRNdp`Q{6eqPQOpfovhY&9wl~k6p5$Tz(H2BYoErc1^8OBSrZ|PmSXY~E~h4_ zhBF?l5{R7a|kS1a8W8P?rOUdsksXQoGW*y*j5JVmx*AMM9t zfAnwYO=*)n9rMsuZ!SGr2ISDeL+n_;*1kB7B>V#8;+wmG ztY;&=d(uoBtgq{a*uz6*VGWF5a97PJbD5f|ABxpJY%uE; zG?@PZ^LL}cE@!DdE4R&QfPZWhOwZ6;O0wS}OV_edATPf%qcHvJGA4We-iPdOu~wih z=PQd01vf0W>vM=Nb1THoslfT;d+tej4%Ss>8|sJ6^=pt_&sEOic$R(M?2kemFyG(eySFqa`U9RCyh2=O0cfvs?`;@ zYob(Fn{etJmYv~L>T0m%?$cMIvB}ij$#Y)U=~9^5^PEk)7qr~&SSjgmN@5}I@ z9!9WJLl{wt&!+59k5tyM`cfHPwmVcZS1&B+l^tKvm%2PwG!rUMV_o!R#s!~Gam;EP zVz&u}jQLV>S4yR)oEjqmI)NN^JH@87+tbgFg3Esx3O@2tfRRQk=hyxU%MJOpcPO_^ zx^0>*jxP1v6TLLfl8^AX-PZ&~?_f=0oXW9l`sz;yp4!-Qvk#_Fd~n)Ck_M>3Lu8B( z1>`ES>icjo$yM4qlJ<3aP|Nvl>r)z<;sllH5$z5(}gfJr-C(jvW%=#ytqb;_M2 z@O%UO3Utm$0j|1)s)}=MV^WmV$d~ig^}6!}{2@6!w#zoX%m1(QT-`6bxROx6AR?Uy z$+zJ~0WUVI6(8&ir>bzItk^cV6k-=eN6X5OyTDj<#KwC-DvLfO;7TJ3e;%v3T7h;!CD#fEUUX%nDTCJGTH4EY7Dp_g~JO2lMe)dGsxq*K$!cxZRidyBK zT8n7OEd+8uogtYMzKlDq4sC{u zqcE+h#NM=|=uxKViLydftZ~RZS~D0E-|d-5Ne^)Mq_oCT+9#0fls4Ct_PLZME3p5w zv_pFyE6s0`c4)Mtn(^shaLDX%I40Kzb^|%fhhNRF0c$i#LXCbXs{1jWE^{6oNV)u@ zkfy$rBUH+!$}H9?n>Hck2%WOI(C~qzth|SmTZe||1I<$AB9&5~Xx7+I3HAdaZtwDt zioPOFDa49SoguEG*eIuTqRKiEMZ|X@Wz_wUC?{oT36(O+E4r(@~WY3|S5v+^N%1FB7N9L6$WlxzlMha9WeFo&&5-SE%jHmjJie*-12IY7_kF zi#uI&{AFJ0^o!%K72WB&>o>WdRNw(#Pu58@1N>0^og3I;msW`j%a%{wk}{ z)QZv;$={hTcbc#7Ms(^SY-ym>W8>SDwtQIja#Q~>xvep{Q}*)w`e(0I^{0%jXRK_9ozp0rAJPH#>}3y()XF^6Rp*kCE=}7TJ3hJ?hGJ_D{+WyiP{;Pqd!Mt(+{# z-ft8hB(1xgt!K2BJG_##Wp)FH0TcRVH-`b?mBPArd(Fyzn$Fm;q2fSx8z<*oe`L28 z|3aOyx*@aEQ1n_@_d%q~k#y?bbkYym&CM17)HJmJ1ux>e2ilu#rh+ja1lpe(=7e)_ zi(riK%|QD@ z8ZNlLYWP5Vys0?J>huY;2TQWcl$8YH;8+QG_>#0ejJC1*aclrDb2>}*iZj*++QS9j zPWoDds@N?P{tmQXP$Z*|BgFO)nqQC$N^zYjPS_9!$gCk`4LqeeK0mItq z=>t4ed5ePit>@*QQmb(Cx7u-YJJ62Qxks9qiYi8lY<+owBJ<0@t5V?KRDJ9BfmfMu zsYQhG)dH`IR&B??9{7tYzydvPOW-Nc3)Tu(1)UP*Eo%;-%grL?C!13h#g8m~QZDeE zBpxM+J68+bB#HZyfQU^ZvT~K5#GBxmlXw$4h*m&&`(FzjYec5tJP-$W3LKl?p8_X1 z{i$-dz$vP)h~oS4?Z6zBNQye5Q{^z)r%lEWW}j41JxZPf)vEzJB~^{#fpwi!k>NU3 zT~(G&Rj*2ZD&0FI)tJqJ<$b9dfQfvTrz)vU%M9QR>Z1F+a`%duL~^c zOC+L5qLNHENi?)8&sUYKBz%EDQkB-JYEi8-SuRFjc?S0YYcP^me>D;B#b&vNNMp$H_t3m7m%cyikxLv|t~Vt{&PlB9m*pB6 zYBEcmp5=O5Wq^1UiXb}a2eNT#zKffQp&{0+J&i>(6Dm8moRrpz5?NR}-hxi8{`5 z^)x+PRteaaEZmm)18>G`kjc^do>}&0+BLNa%qk*B!UlH&LU&1}*SD-Wm9p$+ zLVb62S0~GEC9TNgJnkYHCg-OMm|KSKB7E{q6=pzA_9kc{>V+ytyDGpE5%r=~5Fdg{ zeBeh?O#=C%9_>n`ZbhpoQHXXWQKgKV6p&gY+Li1C>hy?qr8t2BDbcPfA`3f};74mI z5y4NL9_>nX&_eT~T~8@AV!lm}cGYsaL_}(|tF{VI!J@IMHjv@pC8Aw*^1DRFXS zUbKzkq*QJ1&lP3K&wW-b)~Lu-DruimmBb{b@z2JjN?!q}`U-$&a8Ij)C;(NT0Wb_V zFix?>KCSu=fKPC;nwaxvw0&CjApo0k-!nN3j*GT0DxLwjj=O;BsgxXTU!hM$MKZ;R zm2MSnUsbHItaFucB>@VoiSU;&3zp~PKqqXn#U4Y!Tj$Q;P66C zY=Le9`=V}w)|M6C+Hx0{<@NRFqU}ef^PL|`Rs#W6ofhrlTgW0_*7@nUDP~eNe>B_h zED9C7Et4vyZ4R6{vcQW6>B*K4zTCjoVHh}I<}35}3tZs>WJ#?2V%_TVjyjt<`1CK5 znlRfeE?=@Kza3(h;&_$BUH&^fxws9ILsYEE;i@kO>5Ba90G&gLx+IFKM}mDgZjZX7 zQt_O{oy3TUYQ+aFU1ga_74_w0CUo;mPF7B|y`_NVF2UlYYezf@AL9O$0D^e|!{LI* zY@>YENM>Zt!Trz1P~YlKRIUUJCq6rAxwhvBCtib8!_K*L_1v{YcOu+F&vMGy>~nbR zGCriOnztohf1KxYPZ~Je0R9``a~~M^Vuc^%&^E7`z)i{XdKe`3e@PtaL-Mb{JjlyW zK~wTgOLvq@X4pempwZmp(J}WEZPZOm6na!_XQ~%(C&@Xd&S#?SjjF4o5~F$-?kOE- zV@dHN={{w$n6&#EYWH&`i%s3 zXGf7UVAx%hx}IyxY0PMQoJTduOB^wZ>dgQjZNK7V)4pD`{TkVnDkIf?XsZ2;t~Q`j zQrSI4qU{l?J=I9*p&oJ(P1FeuSEqd^%i29Y+HPRB|Jxf?Z*3J-U%FA8*1=4}U>%u0n_w zRw!`s0OHzcyD{Cnbaiw?g(Q-(!gPUqFzyOM!d%>^5393DPi$V_NA5Xe)5S0Be#`Kx?>s9ZoZ&^*VquEA)l&Q*Kh_#=~qoH_>zf!mcU(l%;O+O-VBCSo%{+$+l6G_RQiazp-BiSEA#{j=YJY)7h&F% zF@b%5 zBM z(bhx6sz%wcUQl~b!SxPs^cIolg^-1AdMbLVQ1(O%T^=W^G-a70+$Khc8BrKf~GhiUa&^k~yj=F9*tztPfBo=j$TN9&D_ zmBvGoF5}LU3-x@*X@n2Hj2@%t?yr>TyGob3CIy=|k&6KokU!|n*&#w2UN?Fy9pQ;b zW}Lf7t{3HQwv2NZOX5nH@oNs_m*0D@W*C|Cz2#n>0g0s;MhdVw?~QVX{y7hv0dEKu zVGPYa65ZWtvL}0L7IrTuEDRf%)76UZqnjpYNH;5bKrxuNNzM;!wW9TGRCm9oR~29M zNpE+|%E}78F4R_Q2(<|D#qw~g+R^B`$h|b&#dbIj#fvOu7*%m`Ag}@6L4#4F4OFp& z$KXb5?lt@e_r%=VYPIHi@l4AdQ;5uHzznTjtc$ZqID;i@pFz>At?)MR%V<$DF`XRV zeg)nlhqu@?7r!yQZT7t3tt+^P&o*e*NRPKM1NDlwqdS@n6P6qPH=8OcTr=hLP8RUC@k7 zH;lXl^DYIMU1MLC78%Mu5ll)qOuB?DT`J+oM$G$KJW*Iyl-sDwi|bKR83 zsQXP*mxy*2>MkKomnvxL67fqjUuuSHps7oCsLKJ?OEgHS2e}S z4#i_ZeAnT6U78d@uZ5vrE7KLDNVVLt|6)o;5b-ms4+M}qv-*(fDo6K>{g+J0GhOA8 z>5LBOyY$7Gt_n)(2)q}Y>5?yHsR{Y&ewnUhH9`}UIz7`>O{HNph?U8E(hub5KfB6w zrFeY{4jF;%x$KdeX>U{1p{UAU6Y}hBiafR8K!94B1qT8(eG3km_I4S0^nwEcSZx*@ zGVR@}77!4cmuc&zRs!SR%(V9jYV`DgF6AKVC=aLi3Bo?OjPccY3JlyqnUR9V960`8rRIU2O_Fo8IYG> z%e05)TcerwNX7JydcLPtD&FpwX-|N8_5RWWKdFtZGKAFowmz?|%*nJPX?X8>Wldz- z@;O;$O?WpNP8Rc76W)W5gPcqahRkEjOo_4Y%euyjv>2PKbMCmO3X8%eP)52U8FcRw zW7-Y-F~-*W$9{v(iZsc0aHkmi{1e)u!%BH?YK;Av45e{09w*f#gfXXjjbJXI9nvOy zbV&5s)I>*S$A`=sL`8-|p z<}#+})HyMmX?@~A{JvU@yp}776+(1L>!`k2F^}OBsd}^S``R(zYIfJ5BBf)#6ltzd zOK1J1lQndHN&TJFU#XRIwHY@!W(LKivb$M9&im2IGe}hR1parkVrEo=1lcqg+B#+y zdZ5Bcu_ocyY`O`F#AZxwLdgsF$4pQqf2m8Jp!f+c8BS7CBc*Mr9rK=3se`(-_hL+G zWcnUGl1!_zj@dkBltHXm1jIG9Vn(UDXx@%Wge*A?NRJ(rE*%zs&Yv-(8VFo=qst$N z(a##|VSN4{M98nL7#S6BBK(v$6k(7`T$+iSR#{2%&^DHRV-exCxaHtc#B0c!+`+kY z6nC(O<*qbdXS;waj@YgPV}^$@2dlJ~=-+S;B(TY{F4HiV-4a-X2UcD#CxI{VK21o;4<>pK!X8@P&4j{;t}U3iYe4Dg>v$ zy%N(C(x{!gQf-ac(@%VQ>G><1-kledRe+q4vr5?@!s#r!yl#emQ)8N%W~ek?M>Z`Z zk@(?DQ^dW{oXDZ5r1+R;B9(FdWmHU6l|)E9uG3UiX>`+7RZXXEsKivQE2F;7E0qbM z(~POHiXg1951)@IqpG3#{muX)_!W>3_PCRCX&yLGhCebxzdQo>5&<|-rk<*Ub(to+ zEO_+7*lK#xe&t!qy3&|tjOwei9m5VOUr?PNQ!$bzkuN=5S%~{Y0w3bJjyocOk$kI* z#=6QLt>yWV6BAM3U7dh?R{|^Xtj4XBz(zdVaGNE76?^q8?xX}R<9Uetm5En{cYGw6 zr9X(#zoOChG|cwO4=K6AM9aDcSFXj7L510PuuHEc$(5{1S zahGfkQe&LF3{w)>xHb&OT|Fx_gnUYNB=iMtz684Cf!=EyBmkeUJ;MFTGG)m(?dJsc zzGeMV5m#0MALD6_YaxO8czWY{N?ge_z(t1;K#!;L}8oYffcB*S+3 zt1;K%;`OQWoCphBy43dws^1vPoft}`O1Z%K9JjzM*}S)v{GG+z_x_Oe8<}3oVlMfV ziq!dxi!}Mr3W-f5AI!(=>u{@#q8Zo9KdG6p_bwAcnd)c?ZL`z84~QK(JN%a-Jfv>B+wGiP24q+&K@*C z1v=P`P@LVDK!2cv1xio5ff~M%q}#8gIj=QtfoZ>J^ju94O9p|2V!zQ$q{VC)_^_BZ zCaXSH3=HPiH?o}>?zq*$V5!fb0WyF0G1uYlxQmv1F)fwcPLT4O{QUskddY)TVmZ#H z&KdioP<3g% zw$+Zd9f=fkd*elHkdg&w5&3ER!(Z0TG;p(yw$db z1Fur7Z4K#P?W}S1{7$zjsZTcTbW7;vB9$so3P(;Pl=(&_uMT}esNIB_{pD|wB{OrV z%Z$jGja9kV$aqGHnT@3^v1&Sm&1_r=O6NjP#TzqQXiDKilQT2x3K!gWXY#6z<*rf! z?AIsFtP~;C;BWlZ%t{QTPzFaLKAOq;fV;}e;r)B{vS!A>SU%a%{}sG3;wAU_U(fWC zOx3pj(V6;=3h@0+XL@BE64P&Z<(b~<;+H)8eK)hX?DTQOLyJsoGo>7h6pgA=&1<7AqX9iW;=yo5jY2gV;`SFE&Q8Rk#M&$Gu}hy=7h6z@H;ees&c!dk+G5b8%2!}W zT2PfsFYb%WF&*jI@73KccPht`UJ7A_Gh}qE`mPC& zQK?nKhFU?tYW~fEfF2Wrt|~UjcfGzi9JEITv~Fv>T+^f;GI3WMS-D$}URKZ^sn1Rb zVuZT!Ugd5>1_2YM2rLixQ)`wQ-?7})61M``@*HHx1yAH5|R$@{s*z3TCc>7pE z`qC94CMh?kRg_h`AhnSoxvju`Z3LtdioC{Lid0+Qav`ISUER_0jTtM`PUF;Lz(agP zUgKc~E>F}mwhE^PF*X@Vm@2V;F-QsBWNTv3R|r+wG~_8{c+giRq4^~K(({78ipPlR zDE%SmxgfQ&l6E>=${pb{*iT>dOGpMDY&nn?pYANV5R0gN4q{{>?kWM*Dx=hXg z22J$iZ$n2q{}BGxn5+3aQD_j3?8W^Ef1zt44Ja$JH~+-lH!H0TPXd(RU=qIqJ9m@` znj&Hh;kUfF*l5dLe}S%l!@tC|3#=3LF@jP3Jp`kEE+~+BD@6jU@zlrFlXK$@Ui2k` z+`y-GqVK?KKx5vbk#BXB07L(+-ngEU$SJ82a}yH15H!6wCEamKl7z#Zk|q)uC*5M% zWnEGu-&Ydw>0dz5bgcgxRYo8RV* z63mi#;UQM=U!?}g$CJ|IVl!IC$?7uptW(cMITp5QV85W*JcX_5c$-dmyO0E^qb$(e zE~9$L-s(Z4DBm&%>l@6~wAx|Pf;AuM791sm0WEmD22OeiEhwkhqy=yH#Jylzu)!Xk z+(L9~PSP#-wrRoJ6F~gXkFO1G2ww~Q67()DnBGiSnG4En+$;&ug11p0x4-mnK@!2L z0{_*C-gIiW8N6Jlb_WRT^KU`6hNT5PL7atkr>}HMx&+<@rzBeB_C0CA4ttOWy7Z7{t^>=BOyC*BF z;ot>^9T^wz6~=i?Q`OG{C_k!H=PBJ(T})HmdkVyKzox2xM;KUOK@dkr-L-ieSTXP( zT)!t=m!`T0*Y7E=J35X;G*yAsI?-5D^?S(Qy?&z^fC zba@>fjrZ5(4Va+)-#eqW-LFOgRVht6+x>>P`j)h4ulIslnHJ4R z*L2$QUW>clb={(^OpD%!nfKf37Hwr(^!|ImM(P%Al}u_{{yvPqzr?g?2J) z-J)$wi{3v8;&Hzg%^0Wn+%Bj+e6A}?I>P7s*KvG}Q1SWxe+m5IFU%y87VWANWlII= zHV+EnQXWH5S)$kc20xX5Iz|9^=L-1l`29`7Kxw}P_J3iagM+KQ1s zQ_LX=2qWph56+9t;MA#pielu{pzLI)8u!7u>o+KX;wImz`K13!Ill@65x-LZhpqE~ zud4PQ_|3iV(LsSy%3h^V3I)m@Dr#8@SX!3KQjl2?*-K;;L~)>k3{m_c3Me9b$ll0M zM3jx7h^RP05o8Jf?@4lBANYTtPuh%gPEJlvl9SwwDG~SBQnn~!glf!)2J?*i-DB17 z5(eR=d`A85vFdk;4&>5X;HsRyK$dL0HwG@OTBa`T>U^o{UYibq+=o|)bG;oIJt&Pq;}~B z?pnC|wV0!GY97HB5wC$vc_gRHvADc8CwZIia!J2(TeJg&%57j4enOSrVy5Z)8?f$h zTi*d6-%5I^LVMcU_c#z6&@S0`&avd_QW!b*mgg_&I=8zFdrH2+Kv0>J= zh^P~xKcm1|q^#~l)JZ_vmGEPN*P$(8f+PR&xg)CNlYnVOh0fuZUuEfJ8a{4nxk>^t zbRL%>*{>+qbzh1-7_2xJNiQtu#J3LxHpUFN&k}gHbW-9{4Sw{R3tvRqUQ#B)! z88>k4YG$hHz%?pq;i`wj)iZEJZ6A>-pLM-P^=wv26?9f4-Th^zDlSQMYdWjqG^t;I zbVwTVM5c<9B-w{4T*MXgla_SKR8f-jqD_i2Ny&UX{Ur0Iq`ar!g~_~0*(nqWv&&^( zmV^x^;j$?$3Gb(5{w4{BOu}!dvB4;@H1m5&_}wIYA0jn$dFFl+8s|d#Ft$IGdAZY> zY&h{6mo^Ez=m;nad1O77DPNHr%V9|{Hir^AG|c>15?YxA8n?xmJ2&$UNf=@>f1{Le z71Q0xWY4bfhpJZ?nK_u|PhEJ8r|5T_GUdJolbD<*(?8qVc!{AQpXp5En&iyRKIZo( zv9rlwP>)_tW>=We>uzm(W@gb4A{$5>74+j6`neGRs3+&98( zcP6uvB*+pO!mJcZSTZNGv?R##7YU_lA;11;=qlOtw`7i{+b)xXR$h}Y4OG!Pp|-aZ z-3}xsD$ehD4~(FF%Q1tfy6FEy+Y^=G={+keYa}Z9gzU6>%;5^HM0E{_2Bi|!IkeK5 z9}jYc=&pmDM0JdQA=u54n0tU3Q94omOxH=xNmLe54`heYnN9CQx@6V{*?|8V2`#TCa)^>D zYKoYu3XwNLRb<3n8w(wAQ;hA?6IC$*k>#;fQxlb0%avk3)IL!~NYW9qkYY!W#jihx zND2D8qY~L_0nAMcBatDM64AUnN>pmqki<)XH7%o6D)Ewr(jTRtO}rj-1f=7+#M1&I z->u2*v<9Iq%$=6_o0h5uI+?gdfaLfeKwH9qKGQ&9mD2H~VDxL4I7cuR8jLx2^4G7S z$>ZZt9{tKCB1Nz10fX_{e=y1k-Ep%L2MUy|*TTubFixb|p^TEbN~9a{niV%do&Q5h z3Di@U5~bg0mS&*Z_@wF_P1M`@=L=BB`H6Cxp;-?D^gtMp9&hH$dfw6*iDhJcyV>Cg zI+1@8&0azLJ-Q@Tq=xh&{UgN_Q+z*JRMLK$S+i9DM)gUoY1)#+>>{RGX5!rirQPXg zv4 zpJu2hNlKSf)fP*Sc98LGP^`NHU)le9EL)t0287OS>Sg58W@hB6=b2sHU3Ly4uJ zN15$yGSppuAo?)^T7RTe{Vrvw@32wH8NfzAtR87?S2_AnH;)`w~BxOUaUdJOEPWH-C^Y=8KVWf zqTy(C1xYXh7+p;RO+=6%{*f^xk9gb?8GW^9U`9qyk?fdhhVU;YUEhHX(rvWb%3DW8JBbhA;5~)QXVgX*cp0QzdYKyeP^7ZtgXbc zGOoS8DCsL1*IGVG%7AJ~dL#H)na;gYCuxVKC#kPMu-37p9b!t!#IMJmq#bm@S`oq8 zQBnNzajbdXq(y=tmyN)|qC2FX%-D4{Nd~o+qVhn@*98tUE()KOhB0?!5>4N0dCfq* z6(*LqwO9lw<&mUpfLci(1*z;Xpz&d{A&qZxG+oRieR)={hP4ATwntfQdp zdqA-s)GSCH)ti!3S?Z|ObyARaZIePBeg0k&`w^f{YU^eYq>c(fP~Q$p9Tj5Q<=0Um zS@PlXj2?rWq*8#2pPjl%VmMNF4IHQQ&p}r#q1J=qYGEr#71B8PJv@jD1Io;Oz zr=Z#+9JQA5cvcc2%w9cYEd4O3jJ=4aPpTEnIe^f&RnSdh^}h8@QkK3KG{-HB%@TA? zZ-8ag`a6;r#Rb)VBWc;To_0RyR@FfQDN#YUnym$fqtry?USw&IyB8g=jhJ7O>jpi= z(TuCDA(MPvyP#SsU{XHRlq) zvaOL87^Dv9-_4+U*?y)kG#Lo_wUbqwwsE~oZZC_F_NqSR94C7?&{8?*#pOA;w}BZw zi^yt3wo+LHXPzQ>pTMYI)F~oUa>PNSYyoABL?Z52O$9oae<1Rda}P$~z@Qo#)SzD5 z`rsflIUow<=-w1{l#tPzf0~yMUY3%!l|zPeX9q9q_Ud%xcJflevwm?}0xmzjP2*u% zL7CVo_@mACA57SP3J@2wI{1?xM-UsC%h57}Tm?zKgnQz;>if@v`hfmk!mr?7W?AZy z8FsyEfWK;7Xn@X=$2fw&$+FZVD*^of`HuH+I_yx;Z**%7>vnQ)5U_iC1&?X4o(Alg29uQCe*`(B4S{Tva=@e{v%r!a z+${-jnuOhc%4m}?DkouhaHu5AFbP9V0$tPMv%$X7HOa})N4ApM zd~dL?UOd1L({kB2OKdQ@WK5848IJep-G)Y8O#^>cpJ3f2A%TDI6ie`QawfZupx0hV8y^aYP@{#}i5f`v3z zDOn2$Je=<3lF}c}nf?>qJdp1GKy*9Fc#si&keoJAr%o%| zVC0&}4@y2JSCFNmyAh|>MjbGB;1wvSi3n9FKp6fQdMql81h12V$>G>T(UG73O)5eAAZbv~9BSMfmh9h+hF;Ta5y4zOZ z%q)+9)?&+S+v>uEPn@-zx~+xtCf}qpIRxpvC7uzElyN6glWzoRW_G&U6r>KmW;6{) zYI-Nq%xpp00g^1McaX`s6vHg5TlN~>iL}EYJ(cd(7o=A$QvL9p>i-AnDP0?7)7`3q zw8J7*4HsC|JEdH+tRU6?C|&N?@j8l;EbJwRBPDAjz1o*<38_aWjI0<0>2Cgzu~BVix{}5Hj{7X>-@-}%b|>j-Ger=Orlj+zlH+w!7U_pDk*go> z6#1hmf>d~CI@gwhB-1krt4@X^ozzH%Z}m+W-|Ab^M$!!WJxA{NhMhLkYtJBqTxO+_cP$}&4 zaIr2A)oita=}NZgJ$9dklYM%Aub`xxF}*^q9qCF2*2j8UxVhnQb3<@7dZep21#Xsw zdovvF%{!~JMh{`O!tQkSiXa`ZNUww=y>cf~h24UbbRb;~2kG$#EYk3Bq~UiWB^_W1 zZ?KcD#-n;=M@IMS+l|1p;9{iq>zgG3sOo@#JzG%+>9U*Jo_5 z1HyA1aA&TiFX{44O=p`WNXslz?{K8vcOq4os*zqySJI_Ee#;_-cd5#BsnPQMU%KjC zh$7WMck6wWuDS`Y;Vr+yi$bnUV!D!^w{tB^u5CCEZSUlvOrjtqf0wT0!%^qn7O6!z zQj0s0lD`wAqNCH5%#S+Hwnz_zBRy~@Qqj?Zl$bkR6#%JAS&LL49I1dtvK3e;yaEdY z9;(2?5DHab;ffMKC!vG52W*W- zgx6>uA-h**@@0NamsOB1auxsvFN868K{F^o#xzAe69Ov#R{B8!lCO;b9Sj4K_mboOU~MH`7AP=o36G9b6FenxtohA9QOw~G~b&(w-H&7 zjnP!);C0y=P<`ff`ujx$*HCr--YWD=cI%8JnGXkLUAse6`UuHJEN99$LVw!Dj}S)G z$jN3zT23}{@I+@rRmxFVzbeP-swbDG>(jG+Kh-);`zEc@f+6P5}3WRI+Aa2`jdLf0Ovo*&9LcDCg_(rNb7JQou)_s=6Kyon372A zqoxM+KoOhjOl83(xFGFBEo2(Qg_M+O%#Sbzs z(S?wUVvzgDb_IMQW$V7+t;$M!-g&F6dX#;AO;VM71WNqF%x{lFLVjFr$W)?7B}g`0 z3J7MpSla)5hTEne-AGj>b#;1EUWnDNY^16(M$FC?2>x>wy?dp6swxk2Cz9MLk#M?r zeyX}Zx8&wHSE$b`W$a$5JsMuK;v^+bz+I49~mJa}NSWp_=LTdB`ce*ezHLwmG5oq)c4C2Jz9>O29NPx5BmdPq%uJXPk# zvOaRlCWI6iJ%EtP=^4RySpDI^Z4mU=|i826IU z4cF1`XF7UX1SrKHNvhrCb!-xT`60!g50`lgt2I^S<~^H-Duenm?nT*rHT7px35R|Y z*n(YCUyKF*1Zyu-uL3g*_a=d9K|CMfKE(De*p-di)AAFdcvsi5tTU~Ip>jCZU)I5A ze}U?GP^&-@0YZVWnJuN9n2O<*2+Rharm z0xccq8d`jfBff4zA3Uh`wIaN+lGH9l%&roJhy`MU4@O(9J2Js>6H(b-{Y1NJg=yD} znZ9Nond5jf5pJ*NMYzL42S$$Uaon&D_$$g@Y@;pjUef>`lFIuV^;Vvm@SUINm z0~VZ^%1Xc2>lzYRQU`A7XRgU6$tBdZ35akfBHSeniD7KG)_{)Aik7|Oa?5{KNmJ31 z@g1%6aF%JES#YIw!AGSRk=B{j;*R(|rgdfwlK4AYr&X>a3`IH0#VU1RL24ga#>rYj zw@5P@Tu_>k86fmUnSlikB`f$&YJ1%Tf&5fD`PB9t9pHUnI{D_kQ%l*j-hFjiDK4Cu zP!EDDqf!$zK|Ls@meK8zcyzzKGz?i6s`*cM;Pe zl6^0|Tbb1R!%`cWRC8f@Dqq74QSe%t6nR-GtzN*zN)GiPU(LZUKiwtwJ|nNI0-?F4 znjXis(;aZY9j4hNViay9S~<1|k$J)0O9Tz?+KX1SUu}fLGa~!Jq>}4<9RhAzPOfU> zR^s{P(f5))J=NrzRz}Owxwa8nvpKE%^i-`PYV$eAMJ#Iq?xQr~5!@^0JxwQcy(6v> zXTC4oZ{RjHcF2Ugcikl5o{XS5U7%yS4u99Tp@MP$(xxuZ+OGeJSgx5)9`QPg}^m(Cl)pG(@9w84H^$?#CGanvDY(}Nd_jHPn1h=w0d()iYTQZ*l)04(7e))7;^oRT)%h$kl%4^ZFRq()^FUv(aPN~2C&&IKaPcM_lxd$=*9nK z3%C14dpyl?4JFV5&oemLT33L(2HY>E5qb+ZlQMiU$D4B65zi4u@M`=&16WUTA1$EY z1GW&iKvK?=GRg7!>VjP$b{%ew=?Bza^le2CQXjTD=f@wi@!Yr(wy$38y}^wE+-}&% zj@e%RQS>@mS>^V%UyVRMP0#{aP-!WhTVIZdyL+yX=^orJTZx}m=Iy)3h4|CdVa{_P zYVQkncdGczFS61*d{lla3p$?>^Y3xo(VXW%1kh*ON4fnu5P^s=tc8dMa)&<$l9O40 zzINqBayWMPNdCn54yEb8st(8TM`c2`R z*P(V*0)IG;d>6lEhjg`dEmY?Ms>eT6%~r$KeezPGRE^};oE-+ ziF@c6o_lay+~6KUl(!zjHIo26&n?D}TkR!4Z+MH|?bhHDjyK~kn=1unt62WBOVO5r za$nFJg#lTLTP%Uxc(&p;6PR%e&jH+iTRFS2W!@+!MKZ^KjTuxgB#+%`rk(~i6jA8A zIA~WEc@H@s)&i6^nFR@cXyBE`)Mgm@4kwRJ#FwLd zrx-SGF`(SKE`h^zhIw#YYT};y4i9$|I|+#J)bF;Dp1Mj*9b7Fycu7n-O>vDSkRJ~w zu=6EmNYf>7Gi+$sF^>o*{)s9qD0qW@M0^YlN)^EsvemJJ25*K2X`5D| zceFJPdC*O%B;F`Q2zc87qXmOG`jevtev_F-B0cutE`mjm zZR5A^GfipmMB;<`R4~|)Lo3^U+Xp*m!KA|v4tJuippJl5xgBgrz=9pNU>6Knss;09 zkRV`yL_+C6xu|pWRS>Hh8Y>z)?(yZeRym>)@@e(-+6LTevB>uLHso3vS6)<;YggRk zRH@>oFV#W<$M8Ik>m-4*c+eOQS`oNl)aW?pz|}+?*F3o=BJsS5n=XNPJZo@EC4kkd ze#4!RK#HSWRLQN4uAFE}#z=K|LrG~yY;&BbHt$6%w>PdA-S3GfObI@jWEz-r#qowU z5vZ;}jKB>;iB1f(P^$zges<-TFm;E;QP30b0m7*`m!skV=l3}sB^&{--+yjkneyc@&n8|0NruecfhjMjTt4ou1BT}C zSfQCSB<_-i2oJ+6*m%G6x{8F99s{KCHlrPXn{HuvXJKFz0?%S_u!%c$ON1OR1K8m^ ztYAAE8y<(hC4{TrUVxolEGH`jHK)`&ZK;PJkoeJ3!D0OGTxLn0v+6DGB7p!fK;1;9Bk%4@(#u4Q!=Al0S=)NwHZ!0X&{0y}3aDFvKJzqG zzU^s~bj1{28yzSrN!_UI!fWZ0za4@=-^Kkekt2h}Lcn%u?E-x2Ure-jlm1}j8Y*Hj z)`UmL>#Y_8;@fGE+Dp=>F!b%4j*-2?4F%rbIYdjx7n)#ZOEVbk@aA~rbyY0{d(!ca zXTnyh)WcAzC;c}|mfm`K(dq(gZ(g+bH+W|nfM*l{<3#X6IMQUZ)cpo!fI%7Jqxc=C zbD_J;@<8&UM^iM~+#g3xvHwia;yWJTb`i%L{|;LF$LUZKGm*l_OS%{&`5ss0jib?d3&(asNC# zHYn*%K{%tt*b8m}<1mhs(r$1i9?Bfx(7GFBj1FAKUA6Qs(uwifNf=8P_0thWT9tIC zGW#1{QLtbs7>$dR04xRRc!EWI5*0!$*-~9Z?y&SkNp~7=n~v`5lc)$*vQOeJ0(E^7 zbnv=V>dHe#Fc&9i5<^P5Z^Pl(#?;A`K42i{-~$qP3=j1dd_*b;n@e4xuL)-1dRj%l zIN8zf+>ZWC6fra~$c+GcsyO<5NI~^7K~^}-A;&F1fToBGiIo7&yvKZcgzKO6fr6tcoI_&eJIy(*-2a(D*xMKaqRgCX?) zq%uT#ElAZPpYA=)WK1k~E1bViTA06uiEtR&o`vCLpr$icuY8BW$f|uWA!eKVrE&sz z2O(A80_moFezB?7OO>T57kRFv%jOqvJPM`GZ=li;VQ`pKB_^;szlYFt9II*SJc=A# zjay#OQU9E?WF~3#c)z6E6ZII!%^B*SYruSt+bd+=7mbJ@guNX+BLVEL;APxjShEX% zFl0F6E^TLfN%cDRVs;{Z=tr)ja>e50==(wtOu&}(GCjbvaZ*xjTtq*{bW~T&x`uO! zg(~PBFRA1{pzL;{sP{r55tm?f{Zd~dU#G=|xVZGUgJu=^xulvxi_2cgP=877?gl4>et9LGp+%Ge0%4Sec4;8Sn>-!l3d zF%#rppi)XiK$G&z#H{4X>lWzpG@~}-8llr+DtDPBHBrlP9VI?T%`P8s+`yB>V+>@e zroK{LzF?F}q8tjRLPG9jEokRbCtJ9fl5TedK90*!jKCkJaBV`|)eqzDQcG0o46xrx z5kgZC=O2uv*42r>D)FO2Mf-0hi1<)W7vUsG_jt>Z?9t}gv+ zni1A`-3vq}_|59Ct>#X&@!dp&B7K9$`HLB5q+TGArBCM}_{?jyJ=>O(WmCm1SSnXf zTvv%iE^l`-P=D6)FZ94?elY>plM(Y9dfvBt%b#Jk#{R75xSf7L3)P#WEqWR&nNE?b z0XG23vvQ|kk*lD*`I7v3!^cb4crsv|SZWd531ZQxfExtyxgj7Hjgm>_MwlFj8;vn? zVh^#raPw?uNuinMnuU9Vk9F>e>xRxhjIaPzuX2W}d`_&=uOoq3hg%~w8Ef6IX~ntr z;&w|Q0S|U}uA>sDj7OU51xS8vFWLQPjm7QFdfgsIOYWI zsTc8d$90v!XgnAO5d$SK5YKpAwry|!tdh<=M}wrJ&2q<4gMVP$25Z{PS8MgCxOctOUBh^e^OEcq++abFJ=@??s_iE{ZlUjSYfT#W8oW6(J# zHm_B2mOb5wfz*dqOv->cpB73)|0C(hp3|dXXbSB7gXY^wR2m48BjZCWDi2ZLCrpX1 zR5Jx~&}#R@x3K&X?gJuGt|Mku(D`$cuEFepn+u^&d|K)z8~7s(8WFp2+fgn)3p+($ zhmVbtf^tQCgFC6|+_ags)yg%$vOJyZO>9d=9L){TB^(xyQ*=J4M+-YDl7+!YPXcA} zNJIe%RK>%k=8=`rhog5}n)9UJF1o(*HAp@$i*$k4yN>q?QHvnyP~{&e=<{p@aF5Re{uvw=se61To|kbiN?;P6w{SBhfR!2f zA?^bSFl~rji~HEG{{;Atl;A*7$0^2^h>1*<-2*I@Mc%+&6FLVnZDylH z35wlcR!!qm*GsbCa)1hoDu~Mu(PH-tDrROB25eMi2_UX0dg7=DB`^UGJ#SPOq4~O{ zc~+F5SpTw$6U^RAVlE$84$Sko@e-gCqv$50{wEpq7c!d(ZO6$p@4#nxuHt@|z%fTf z*Tbbqd42$XHYAHl%8$g-F+>ky#r)F?ChKc&`=dNQ^NTF29%ht(F_oM@bAV@QB6?EJ zS3Czud+SpO=k~970%31J3WEs$iYE}}N1e%6JQe*v+$@O`zAwV}0eDy-eE&_#65K)w z+`@AhcM$pQ`O*~P`bk|Ur^_m~n|Uuzf$=@G-sp3I+;&c#CS2o)6Q-W^xtFP@2$7TrVANc3efZr3-yY zPX;hY{9GBIzm&;`opoE2z&%je*8PQ?I&cqQ97bdQMqehf6*WK10R4GdjO}6<0&bK{ zj*DHO5Ab?&yBVg zy|m`?Er2PH?qWhY(OEc7@rX*Q5Y#8tx71?-j*4+D^_T(#@>}XL3=c77EcJhxglHWJ z#!zvP@>fhr{w*S1Sn3v=~B+QKfk!Bww-P=BfoS50Tw!gCbZ<7L z2veky(6QS(^&8MlRwayWqx1BKwLj*Nt8J4vMwvGG%AZ0)4{JHL%?(UPX_&9FcT%t6 z`bfv5Du}O4yt<2QBZt?z?fZjsgArAOa0RAWQqTF6=KR}TcNgBPPYB(d`NzMkHfW?V z;Yhv!7n%CcS+A1~AyLCa-z%6cxS!26m3T9YBFayO)Xz||Jmqi+5_6B^x6L}ATs_aDiL#`sJdo7^ex3H6oSlIVVS=g0Pq}CKz2EdQs z3nJ(6`e6Y@+1=Pm|Z1B*h06XfZWRwl46Ou9?oY;K?N=P8y@m@PfOe#Dr zLJ$)g%Ko(1RvK+<`YQ1OrV@lArM)S020?JV_0~;w~++6LZ%mzxE~08Y~h*H0hPw6H6@KgPTUeu z4RlKMkm@x7Yv_X={3@WDOQ2PBzX9DydB}kE8^9%kD%}qZCeXqJ`gVPSK!yZPLV5?P zl&Mn)q_haAk@@LUA0ZG?B%q$t0eQhGqC*3&CW#B^%iyMT0i)qk0u&xKBdH-8!bw$c zikys*q&QT}B(=F7P)d?SX(UPMr0A~Qg_A%j$6bAy{F-hHsGxAN*Woi+_MyNfjdUK} zC{jCc37frzY!n_)EpRCr(h5JsF5M^DK+s$PCWwN5f49IML4W;41GWc!(B}roWlwp@ zggy4iOP@fH{q~4!zhrLq-Unn~oA^bh%sY)An6G?45*!%Zq1rDQ+ufHgNMiPO_ z{YIjiWA7{qoHrp6WMnJYD?cRAU$ZCYK`2H4vcevisPjOg^tpl&-2&#uBI08{3dof- zurzqU5GSCwNy^cH4{8VG(jDg{{>AqNM(FyI9sYeL1}4hx%=wO7Q}JS}Kub~FR)}Qk zAd_dyGB!_isBKuDkL!F)o}I(;lsebP5y^LhS?OD`fZ@HCM+AP$#w3jK;;5bbi+aA%ECcRZzoVy_}1^JEsF>w z!6}Tf5#FFAiR=Vr!l^(KlM*_BM;+(WI8Q%S=btqw*p3wWO%Sf9ZLBF=0|EjE1XP}m zxHe_vjwes$G1;j+40Cx#mXTZ6Je4=Mg=DnNwy>~gECeua|r&oMNe`xf0R&n+#@ElvlUEcdC4rV|MF0cwT^e;5vf;Rv0#|w zUvK8TylIi;o9?L`>~TD~0&s?#hul7mmE7`>%cpFd?SC|nF3P9U*yjtwxfXaIgfaY; zwSLVf#2Lf0x>t9+4``rV&Dkewumm)-s<&bDgNlaDe1#m9FR#dCif7Q{3uOU7IM zm)tFHsD8q%`&Zs98okfvEc73VQ>MEn%BwJZzXhRM zPftJ6G#3AsyS*1mTI3e^k($0_=83-E3-{y@dZC#nMAo}kd1G=x>5X!E1`HX*mxMxE zK5|vIH-@5V^`5xJ8^Z>AvPMEB26-H*gg;ZI@#k!n>W{2%4CK6(18H3f;D-C7omTG*PJ5w-Aeod{PNQ+U}m)xsn_PZ>A>d+ z+*c`sYY;`hB#nTrpDN!?kwuero*C+`EV2w8OXy`ys6K>5n`2p2icQgk>O)9J^(N{G zkxYFoF_BE{9NbLV+&?bfOVvC|GpC3R-u2iaCvp((QngkPovW~yTFFGiVd{Oljv<(; zYdDsxM!o9NOYPMc+z(yom4&D>+f8NDnfaBF=J;kAsk^ee+&p_d!10pxKmkWz;y#xG zCw}0S6s}sC0*^jt3Fyjyb90vN+DgKwu6|u(C8cAWuX?lH;LXhz?Si01_wh={p!IJu z&xrjQ_k#pj{$S#zVsA+R+2=2glO^itKCD5!`4^$OxT+FhCYS$loOG~s>Yv{97zbd^ z@!pC;Hk*ffalS%5_mvk{Mk|zNDh}Oq=F`D@1*5c2OiZs3NMJK`Y_8_&TLIr{NAld+ z1S-hsH3G?d2~a=UGHX7BKvQc9#cgz5Gb{(VC8Ct;4$`EBT<3ROw+G7l)(4hc4@{yE zxgM3n$n|I#My{7;$dbznk!wELbvp>Tla^cu)IpPzY&*~|J9|kH?!+KNXW?PX3D<26 z(yaUzsWq)6gw$FZijUNqt^y=bTRiT%4++v;APMn@P>&GYLkPvdRrsIlHWD})^98O^ zI9#I;T>d9r_a1?hQ(ppiPq8q1_fRW_UR*BMtuAn^fNS|zr^$qvtuD3aGh1B?OiK97 zW>;T*Fzcd4N~Qw~Atl2^2r0R~FP47mTzUN3dt1)CNKKb!G7&;5t*t;(M3$w=!^m>u zd#+npkXl-#!oDm8t}u<+z|CFlx_JfeNlPLxje#V_;u0-gww{pmydj}(ko4=1QjP}g zUG+UAbr=G^p=NAVq!$q-Fna zCeoX@1rQpvbTj$K;6~K!`P2|deunbElgj@tZq^i4W-tSRs}9&Rm(lR(2OW4Hw&GXlNO1 z*DA+*XOhHCi8M2OS-qCUFp76?8&|ERcz%z)W|@?Y-y$jA>=rc*%WLU)#H7mqJMJf2 zX7=dnGRMU_DlW#Bd9BejML(e8DojzfM4Rp?(Prs7!%DQ>mMD(FJnkWDU9M&QA&hhL zz-kAkDwkaKk=0Zrtw=@@5?@q(+I2(VAE9e%tLk0MNOy5DO17AM zI^jCVN%YiBu3F5u-`OklbGeDD^MXBi1QyOyu9|LhTD8YyWG>8lC&3nYy6)DY#J;Y$ z-nzh2WixF7{dJbrF}5uJcA@w=#;)q)h-N<7$5nDc+B@yzjP8srEV?sAZa5ZluV`|y z(cOlM20hDjnD4aLyvF6?Ou936w5$5bjOLw57QY`xx8O_hgup-)RF1E8X4!fw`CL zxK@DgAO*UW9g6fXo2H}+d_mOb3^(}(lqLEKB34@+v7=RilVxdvw`h&hTH@#8o};eD zopNPAx->*pusW_Jk;`_uN?F{3&l5R@$SuoU<;4KmiioAS56g(1<*MR>&hlG9z6unS zd&r}bTvb9M|8^9Ek}8x7`Mlb}RV8)qELP2{V}{aYtTw~!SFVbRbG$hR88zoU2m)=j zP(juYy?Lz-;EH(~@QkbO&F^^cUJ}430ZPWn;edIa@zlZ9D&d5#wg-uf^Idd*VAA?- zg~p`(#D`pO@mfoCnXIM8dhSn>-V!p4kwPJaU8s?UvpbhoP!8Y7eaJ&VLfLQB8@KOEl5I$cbH2w)cHH2>)>WXuM z@2Bb$99lvnOkq}L%NlxYc~`Yzo%_9IA{5qY3-!XuTJ5|K@r=biBLO~y6k^s=NY;1f zp@D^1oi21h0?*+=)e4nxgyeA8UjSy-3juM7CPRG5k=e7^)L4USDQd>=l!KoXR=Juq@Kah80KKK*tfw=xss+JUFK?EVDFonl3Rgla3V!KEz zE9Zq7ND9B>=Y$>Uvo2iQQZG-JXmXH6P?^H|@o0gLpK(Y0-!@*31BAh^C}-pMBtsw8Q<~wDQ`l!@lW$FFoEoxfzpA>-#d&SNHPmqfb(rx8 zDBU|8CI|f}-nj4G!?h$EVCY{Cr%Bh8M>?;=EhIW-$WsIO*eo5HEc9R?$R%NPOLV3k z>$=-)XBsz$NPa3?I@9-8n9g*YjC?OrZ>w-i^vfphwj{t#5oM-Ek?{P%BFUk$XfJ0y5;%7uqq?X!-%TuV0R;R03dfuOb_keF z6jA5O)zaW$fSoMhC$|Cq_Fupe7VwYT05AL(aGC*pzwm8`+NJxcEQ)m`ORY!{;Br>C(W z6g3sQN~;nz(+tu#qEzzGMAK>0G*~sH)qCZ6r;y&*$b3=E2G~-}RY&_OR-1n_HRpcT7 zzn360+n>>!NQ0{E%GtFKq+LmOR-6-gL=;}!?>Wff5Ux2qc$?A zFJS%9Oo4v^iDDHXsl{{Lp}wSA!i@a%?0CU?BdMVp3FaAai*`&QhoR-9B@-Hn8&<*L z*K$3HTx30{l5NFtZa0&yD$vg4>Lf@7eR!YA zwi_0_R8Udsz397=Z8vz<*-F+MnVx@mpUy@IFzP2vdGA`5ml@TnznF?5$U)&LNd${~c(4l+Qrkoty)Cw_e>4npJlt*9}45WgzQniQ^~tx<=Ae)_paSt7}M8cv*_n zzs911ABiCjxXj_DvWiW{y<{q;hF>uv^+k3(7|Wn8FNuFNOPVysftAwOdc=Kh<7Aq! zyG?Sc&Q{_LPpK7jCM32Rl3dA8{KX_z(!~_ab#sy{*u+H7b$tVwQuF;Z@e zG^s~SYJRCPojRdFWMnAyZ<88nQeUP_pMIb6jOK?Gb*JzfDcq^zuD-5cU0>BK(bc3?I_PNn+qGDr_LeLrr@6ChH782nwH3Fq zR&#)uKoS#irdDGC+GT(mBSO}XNVvapt$QWmTa$2af}ikg&02LO;fhHxw*!kd)QYNA zNwi^UDU(vEkj-%9Gqoy#j>QMV@634nctOpgxAg8jgZn#gAE(%Aj#I~Rp12aDmWyc=9|vpJ|QDNz3j7W7FkFE zp3&I!3@nTlJE9i>V$4nA(mP@d75megE%zmGNW6|Py#Tyn!pBKOv22g!V^}yJ<{Phw zV%O_2YLJLx*`F40P?<1M43ZI~l>qjf5~KPF;PPS?uwOV}KWU0%oeo?Y=fudSbwp4vIrlX28)h+JSGyv1r0!dm-#jCw$@R>%im!FqsFh4PXMDMMHf$b3g@Tb$20^&jN0iA;p$t(5ui zoM6oU;+89_C%kHnV|bz;8r=<_i*^UOx8&gOSNjL zn0E1=_{JWKsbhVg$0Wpii0qXrs*@CRRb$FBqonFoQ8W<$r&m;``!x~Byq==k`R#O7 z7g$m47_{m9NZV0Z>G4FmvsI%c?O{pNW?8FQsy9TX)=?awqsV;NddTt$wU=z zK;ymFHs02(#H`+G8gC!HSX;1xYN$CNp_OFPFvLLp-Rgdyfz?k*+Wm%sCOmXgTGiJK zNM|5P^-XrjX=ZtKm!^m;R?m~PyFw)28lmchNN$z1I))@=`lN+vIwwt;WT&_O)e|%H z&Oz_QwYX6BSjQ6^r6>o65&SL!<(r#i$gPjy`Sz zUmvrqTb$W)@$r&N3|kjJex2E67)m`oi7lZjiT$s^-v@l2fS0h~q7)OCt0I13KYmAR zzbcXdki1bdsJSX3t^H$xeBfVIP)khcZ8DGrQ9`3!jmoIF5S_10YMi8M^;)r7<@3e% z@l}(WPg2J^X+q5wlL-Bb7m2MY@ia;MIV+v!xZkcZAcZGekbnkx;AxURQGMj!SM=r( zTm6E8y!XOw#O6t5zKI0Z;8rqe@?{I0eS1EH5u@%73GD#j`>nj7zT#Iyy^SUAOcp-Y zZidwrR~S<000LnAfID@67&z9NMHIVDensd3!R%yFN)&KdbSJRl3ZA;S+SI21;BcEX zL5cW2=2jgk2Mfa`9>8Jvc^}&o4uH4<5_)i0m3SBsVYFcwp&|YhvCR-6$uBZSMO5&q zb_b=aNXMr-#9kcWH{eeSZ*auu@j;rGE5!|x+6SZaDjX?-CeAFL15eYko^E(EvJufj+0>EBFwh0=$A z0{D(>nf|P}rMpw=BeHCwdYneP?z~4*C1A2~W3YdCw=x761hA$63Nbo{k}T<$e1$yH z95u3m0c&^KWa?~{Qg0~DHieOG0Na7vAb~D;SSK&}b3p?3)B{Mu#K^*K6PQ^4hQNLA zD7lTXRIvdB{jxMeZ{%`Q)b2vjcYK8Vt*XIL#M#ri}k`gGgVw=V%E-|O@gTm&)fZ%by5 z&0Q}@Ly`5tgH>;d_6m;9+r8;N|y zr}YaM=X_fK68Oi`N}yH~?7d4)M}$3$L)ZREtdUY|E7}f|{+mYc(c8A*j_r(ylos%& zH__bQUqCto#{qe__t$t}Ie|}UZttJ)%)!0IF7VzL@qB{YB7s|Y{>J^tX`Mb%6k(3z zZ7M}dpS+GLQynK)PH|98m7$u-Ja4({ltrYic;SDTp>tjMo| zD8c3^E0{#SMJ0MxFp0F7#8Or;iT4t?i)%4@Uz9($d_Hc73RxqDBFaRBO!=23O2-{^ zM(G}3YqE2$NPb%i(mO;U-9&nb#I_Qke@q;YliN|EPU4w`d!s!8dw#t$nxlWT(#?Gs z#-e_f1w%iB&Zw}-3ZMCnih4@AG);}6GI6D!&sPNYOF%gI75DQ)1pItL=zMjL`k57W zTNRyIG?NAf=J&|SzIYWfGtpg!8u`OU!f7!YcU~nkg8P~odfqo6cFuVKQ zJb_RD>K9VqFc27|aUk)<*N~e_;D4|&k)E>bHry5~@ zgy|Yg+^{ID1FCYxEU|KQkmYJyPP#f+Bz6i;k8hV7x}pcGUl6$-vty_)5@(Juwp}Xe z7!oYk0mt`#b&M?o<>))gO_;45^^Gk~_i|$TMxIDeb)smEAz~p>a~8#lI)VP2694AY z`Vd+iF*CVrBCt;E347~8_Yr~>`AdSji>B|bc$9P+UAaRNKr@mOa5Q~y1@)eU$|Y5? z*z!y-0pKEQ+~QL-T`@abULKLP2wAYI{F?S z^dYII1lX~Y^grCx+vG9FapC*l1ofE6BSd+hUxD56>0glq{h-HomJAK8J#5RQeN z03He)I&t(wg|}mqPc{p(-7RN*7&mokR3SK2%VKCH11ly*)@i(*Vw!6lI^gpA{H&gwOk1fVav8eEBA&BZ z=^Lzfj-`Vf`jcYYsv1VyD1Qa_w*(k_ljCtQeE|5|k-sNIz-~o*Q+(fcmXH?kC#j5O z6|aLpSCsrThxt+p(!De6rj#Lq29rF$4}r)+wlC_N?dTqVr{49D)8nfL93KLGOkouI zBrm$JZ)WF)C$`L%enI+(PZ+OU zwp6R+9k@-f8^7JK8^523A91H)H-5FU%sdr|jCpGHlgZK$r$lmHR7xaS*eZXjljEjx z`c9v(%aVF$OAt3pu`ftu*3Z6J*l(tlnLb*ut2Y-qhLSe=Rof4r{#7fd5*1U68qb*` zkYJ~vA&?5?RD~clU*mO0p#duSmIP)KD1j?xneVp`-PrDUyV8aENIvDWFJyqvd%x3? zDk-`a+(?Kzu%XK@{UPBXVq5<@q4_18^B$Y??GB&ua;ndNDpu(W$EEVYh-9me?z znGz_6=LOt&w)?aeM$y0uT&m!;X0)%s2d@g7;JI}X{~3V(S_qI5i&D09X@k|84w$3O0VWus+!JB|4y~6iIV!35gOU*{*Ovw&h)$YkO$yZqhy&!b+>K4^2$^ z5&e??7u_ip_+Q>}r_`b$@%*-`Z~^xVnwfOm&uk#Z*#xl?b#J5F1}&t5w2&#aLu z0&Eyo6*2rO$_43p;jH2j+Bxgd(jzO_de)u#-oKD% zT8%OFeTu&yvK04Nik+nHK7AV^ztdHvVCSS@ z=(~S7c!uK!Qz?H?DJe5>uR{L!i!2|8mWf857Rx94foPX4)840CSEbjAQg#FVN!VHu zaGt>BtmLF%$fPh1SSdUc!znH+Tis>Uo^CjO#xJ|>=srs!huT;mDX35iOB`P9C6208 z7$-YrYVX2>-B+n5?Xxz@Sczs_>0#Pu?e%_LJ=4zd>LT;ca+E-3d_EsT9PeJ)?V-11 znzbH%8<8gc2s1trh#^PDmHK#5RrWjS@Da6tw9AXw1^&3UoEn z{@5QDZb$?9{CD^*Fob-k<6fh0D)1^B|6!;Am0g)SuDn7b7tmqBSb=f=VE^$UhW?Wf z7$*5mN${JbV!RPO3ppZpi>u3#orc>{N#+Q;T+%o2}sE{SMPxjJ< zkY{52I#>h0v)2G>_q4mOV=AOMYP3BCwYG~?gX8cT36T3YGS-h4;a|r6QjtJ;`BS}B z7{*qCkDXpv4UMR0wrx-Y>1>J5#;lL zFiW@L$77MqUPHVr_JlZR=(N|0hV?KTI^9t})3RWI5`8tnNNB?d!%?mgQlcZ*sE)6u z$e-Er=fi?wkUIr3_ELh|k7j!0H>r>K!NvrakVA(L$nj@gZ6^f4SKye zf0+b#INJ^nMuVNd3~&dB@c5Tt9~=i}S6ml~Md7g})WKQalo>jeo+))Sj;nvv!IgM+ z;#OcG9o!0~`yB7U9|Hf0@M6WZ8DolvvAqK?i};?_syr$r#LbOG3rG!XoMWVM`U0i# zq421>Pp+?{4y^*=w2&LB$&C~Q5x1QpZJxk%?Nd5ib?_i;VIqF{zOZ#DoGpqXY<-#d z|FU(=u=SlT?4a3lvPv$U0ZCX~gFbg{Rsa5$caAH*c|D%ZJ=r-*W z-f-FyJ_KPu-)XcM^H!8tF}-pO;f-ji$mJ_99D12(<470A4S02*_(p`I!Bd25-NDG6 zwiYa8b#H77`xBw6ZGVgdGQU^A>V*gmpu;Z#gx(8m2!*cP-8j?c@>V!g$8xAp1U3Y- zVaV3*M)XOuwJRLqf{zdwtioWCkLK-XDT36F|NKb}zhB%`K|sQiF|;@`K?9=rUww>j z45AgZLpQ!!gQ{C5w$SN|Y8<4bzKT(!ekJNMNs8!lmMd%C1yQc=O-tVNwpd5`CnUJf z(ZUWFl@`YLcy;hkzlG6Lu>(U@jTqo~)$DzRo6h<}K0fn{Nl>?9xjx8WFyF-F+GcUZ zf)q(`y(q^!RF08KmdL9X!SQWLFP>MW>{ZK4fMxZncjM~)XN`C>7TCsY1m{K585z-t zV&sPDSr8rsM=+&#M@Dw^u)Q&vU|()%1cXUJ(v*{X(Hzy7makcIli3nncsp0-Zi7M6)U+X$yIyex=Vmr zV^s#hs`7?XPm%sK-0RF6de*Sis0D<%xcF4G;Yga`Fg>a|5BHt~9>uc>$FSx0WW}p0 z3kX&BiPEs6!|mCP&^Ne~knH`8CCP^e(l-~MC<=B4#NICe@;mNVzwq0tVFO`59KNOt zKPMF5*3s)GM@mV&u`7mYHRijCRd9LoVwx7_;7DmxQXP6~6KVq6P@9kRdIif;eie=| zRd_^OJ-VA$&2Fo}txSo;U^_CMynP**p5_zSy37&~JH~YEvTP&G)n>63mC^*h5C~Mw zGQ9PI#D^1=(bekVhP5>AU5vGE*N?3HE|^-y;$W;j_7MW?y3tY90tk>}MQ&G=u^R2A z8dq7lUFl@1W#Ksa<97WG&s(@j62N$>b_92TKsVP>)$`->iKX9lkK&bt&=IojX};wl zKJ&}>U_Qq?D*Kb=I9YX!-|A(X*W8YHe?Sy|a@bfa#*x?RJ5ym_p0+}pBo%f%6T7F? z9|)out`S3z|3KnbH-URN}83S)&47F2hpQXhN-vsJnfBze5;*C&&(y-x04FbOo#}xGTdYPG<`-ug_?7Zq zeOliyuy{lS99hBF^zSAEl?_vBkum#~n{Bwtqc=SLvJG2uQ9LvGnx=D-biSCDd^KTT|=GJu4qzh`=D|6G(fgTxsbBLbDBd$bmIwqeZgGGL)YeDfyLL86UW)ka0;? zcgTo}zf#RufIfv`p&?m$^nYSy zoRBj6+B&19t|KcKAD}b1(-L^o4+v>Q zyV5MTssI`UQhI!li*~%TPXW8KH?Z_pH7-cW(vkDZVUDU<7*{|7&*GtDsad5x0dt9> zkTE~_J^6q@O$VYQuD#e!tJvOB>Mh4ReU_wErHH2cuQ@;h2(IQkxLFdQ{ngx#`OBMR`L8U%*yl+=Y3bbprz_CuCRy~Jj0Is_P zXuP!$WGz~=T1Bq4_ToN~z#=xe@xOLJ0_*S~quK@dv(9{h(AT1p$))%Jb6W2k*+d$zmwS&DVZiZD zy(mzO{IYaady$B6eGA7aO)EPkysvLf^j6$PUz>BMo2xfZF)?E9?_0(Bw#DIx&|-#T~Us$0_0SS(VWZ$6a{2Tc$%k$!2T zu~l^Hxv=Co7ylewMeK)6!@-NVJRj25ky5& zjq6z7`*tMB5yPEYC^Ai97$ zEMKbC)$yPLbtXymHj(PZ4z05Q_deRZ+B}4;BGXKM4x`WM*9D5iI`mt0R!g8Ko?W<| z5@0Z|^DXX_1hVn`i@R=19obUWVx=0NVGQs^vB&XhjUiGm12LK7XB8k4?Wr|}Otsag z6+oq-V62z>6e~Mw^Ab}TTD?}zr6gFIXPd&nN=j0fL8xx51XkmrQ>vR1CRTZ9!}hph zILa>&?|qi+c;8{LYq8et2~KyXFBG;ydz6scMifGOL!hukp;liI`TN z!$YI5yNEhjKfvZgV}#7Yc<&g?x@U`04eM6`vjry$SnKEGIg697e*H{G)r-Q(xU>Fc zJgGPq#nt*Tc$(pwNYyPQxAjn{hYHlgTvhA$;X(ZMMoNI@T<=|+?Cn_pBOds#w?+cA z?f+x%O`z<$$~)h4rIKVU4>A%MgGnSaIfL!0(&!jw$xCTSovkUSEcGzlaSLMMcfG)+T?*d&C6bO>PPDc!t;%>Da+-`?k( zTQceOR=-}W-&(D;uIij~&pl_Ky}#lA{l9PTbL_AAe3hMFODIG}Vs5=zJAjs3&+%EK z?H>-ld&k?79o+5VecP`D!hY)gwBy5w!mTgo^G~&drM~qId|sRlJo{}=Y;=Z@%^QJWA6dZJUYfNsjDvC*5Y%HU+ypIws)}i zB0duIx1$iZp(VG4WduuO1KC_XHW1eA!$aT84{^PpWkyKpq4Ks@JxPDt&T~BULVo*` zd|rd-K18;%^w5v92cEd?E!ue_8wloY@5h4t)b|H{z9NYh`F&ty`ehIMh*;_(qh2y(9 z{;oH2{Dh8YkzVNvtg0P)XON`!l5)d$YRLzhe9l?)RPW zfxmLU0+0QN_wCrruz%q)$+JM~`_O{hZ{YK7wE2A%j-H@ZAJR8#9L0Oze$1}(8o$m} zaIl?BXH6HqPd|M-d&u_fO4z*by=)-Xx4$Yr`-8%0{FwYw zd>;@a?UYmLD*V;w=fWezU*NKZ`95#?Zy5)@A2o{FRi9LO0N)DVgc04o`!zag>x_*%|=cYbdgYPO45`|!JSSAX@>b!vW^ER$XU5l=JI^gf@^Qo8!PqrT{g!C1Hd zJfENSG5F2D2xCx_E8gGSx8qvI;ENBayP7&5Q+WFa`Mh6aa1BRa#t<#)8w}j-h~Mp> zWem2}KIhlD8mGxg|7u_9mr?sKP|>)p25@HW#M14om(Djt^mRt&j%)cmVVs?-IrSvE6(W;7s5+0`o%JJHjLT;cnk9&TUL0sf)CU-%2P66+@l#tsdGedRa8IXJ$^2ySG(9-4gB9v__e z60Zx{37HOWM^Yd658fwpsV9%TQ zDqvrl`6YV#C7tX$Da7ww`{l>sEV5q^kcIErvVTv%_6t{r)?EFEp3eIcq+^uWZLc{N ztGjeS-4KtwO{5#N+BahJ_8@+^0c+k%ym@U{a7 zy4|a|)sOIbxtbTxdqdVdw(K9x`)g~S&VMVPpN-8!R+RJna_=8+-W$Amf15S$BYE>e zTyo8g;bzu*U)itb5sKUag1zHc)x3D#vDmz8*b2?tcBM5>=l^Fup9syn2K`X;zH-e! z-n>^>^R`_bn|IB6*1R+S-h1yUi+-=|muYanb`Q-v^KowV4L+*mJ@eV5^gZ9s=Q@<_ z%ztHLfzRFA`Fl2A!{+a|6S?O1d>sgK z{umI1AH8rX{OCgoHsywQUORT*RXh~rbm@h)q;%&Me3ZJqLGb;B=h*}I-g%uf-lgk5 zs*HKu`7&p`gj2H_GNtQr+u{0qvJM?%&hhrI-l98P!?nJX&yCTRU(b_$H@V%fS`zRg z213Q%q066oSyJl%3|~^F8i6H{Gk$x_2jr{5Ur~5pbBQ?r0&oR!9(!IKkf(A>iF3Hj zQ$7zh2wvv^M4>SKTpN&oo|MdsBEzxf6a0s`H#Pi z{?SFhb|m~bn73~O9@wHV7M8>P?wj#$Jn+rXjbE3jeEnWb|2{nUo!`Spb#VJCY&@UO z^Qafu_acLt+xESJ14N8>z8G?MU#CO{D!fIebOWvQE3@VQ_U+lV6S?`?qg+mJl)3XQ ze10Z0?1m2|{zjSYm+$&W)(5Q5VKt{;H|UYqoQ9{r0k&8bCSNBV08fNIdu<8n*okMw ze!Ssb-tS*OSs1>b%)0Z2PbQ@w_}k<^A*4Is!sqqB!qzi>NFN=$zQdU=k-^uclv{y`onIw*{IhN{q`;|8$>YX$G};3MovW?=Nr@OE zS^KR2`P0%ie*VKREN$a=-%mV}Jo2*-;>oozNAk#9Uiw5fVGNT;KJqdhy}7i9(;rza zXisI+YlJ(0X>Yv+_+yK|T*a(flR^#MNb-?>3f%J=_-{g00ghsn0b1BOb^ z7~9yO=*c#(*-T2?_R&&E|I(db=c5(;kGy>>O=;~udZhFu9r!2~@ZsO$dN1b`2!}hr zsRJMI19xG1?kaKMksl5RuHwMuI#3P=p2mTv>LF}6~&zOtPn2XPt zi_e&g&zOtPn2QhNmx)hlJ0ejKpM9g^^MoiqPrD+D&mUe<5FZ*4#iz9WT1N3{_?-T` zOFJGf@d;^;yq_?B$=ENj*3LeCOFv`b?z+H=^qR+{eK5dXAG%H_x#aOvhUVzb(vE$X zBgKkc?}Ea->rb_V{N0Twx_g&)-pj@!pF2^5qr5vW8T+{YaBff4#A`nH*1GC7~vO$N;y+J#ZY9<#(z|Xd3-x_(6)cp zH+xh%eNHy|Y1?b##M<%@FW;_rJ$#;~v)R^BS${TlROW}xNjBwtUcUW2Cw#$wcAgWy z;6JMnzxlyRi%pmpPTky0#>gPk1U%=Ci`y_HzcKb?%p|5?M;_6U4~{+4j%fS03)`P4 zY=11@{^;up+ph`R+tZZyCi!*j%8&ZJCbN6(yvy%(50a3)-0zide_|}({#d@vy|UvU zeNEW*d--wQYv)h+l@|+7k#B!uEZ_cEzRi`h<6POd{mQ;=SLVIV>ao5*#nsze6Uo@C z1Sej;y*19JJpIdQ!mjJR39l_YT)zE@v3&bu`8G|+j?)C+_9pmtay3_d__xMh7q9y8 z2lDMlez>q*&bJ@_^?du0H|5(8|7yPd$ScD(&A(n3e=VO~RO!oS?wx!rH~c0!ZvO4W zx-b0R|8~RC4c~R9v}31P1GoQqKbaeT^vYp$!zZwXC*I29Yrzel_$vEfylyio?YInk zAkTc_ANc$oT0Zt@b~TL5(XWl-`8Yo2j5+xv&gi6GD=~&?Jb8%EjaaCo zU&UbU5MbHy6#W++>`83g$u$})2Yd3#xp)!wqG#!*}urgoF{Je*&pTeL4EUzq;${Y*`c5$+3{n@(~j4NXL-K~kbA}r zefuW!P;c|E-+V{-`W<-dFX>9Z%$0Eb_uQ|Y-(-W3`<{w+KFmgkPfI(1ihC~d+0@RX zZ2WURFV)VUv+*WAuhY)ou>mP`&##-e-bj*Tbo%xczS;@LdCw>KnETYP@ssy_UfBGe1Q$pTX$9SQlo%7C`I^=4BC{g*WhdEjEAWUvb(v zr`@2_{+^?B-NL_RXV>Ly;9M7eS36H+AzR%Frt3?*?dmw$XgP_5BTh3_Lv9wf~fuUGH33*iH($xByi`gY_$2P1v}^1 zcrTw{*Uod;c$m)zw1d>LAZ+Q(W!iyyKl7b@o~{n&9;4p-o!`oPzw>`R@BKFSMY;Fh zX+hcl4DY?u+}Ur86} z(}aBce`)Ux>*wB^bsNol?|Y<_uqdHhosZr9A&|>Q-^k~WIPaJ&M|L8weJL$}!%zJ9 zD_`-gPy8EBT={K3@#jvypT2tRD?Z_BPCQ%n6aV-P6qbFf6Q9P3_x`A#_|{jx<%ZD{ zOV=cOuDz!8_^Y>H^^~igaTO^!0PHgptuD8%#3FMgHntyN{}wFx7}Ljh-?@7`Ab7{2 zWcyXO9Vp$t`_=xGSpKN=Q3QxB|<6U*zZcg4k zzt?=hj$~r*wrk%x&1qwHsol5l(3f2KWa-7Fm)MPW9?H+!70$9tBni|+e#IntT-XiQ zxSW$O<9=axw_h?zt_a7&MXuymIxXy8b#=1)z-4N~)uFxid$o6u0xbA-?~uob--XNX z-CNpz)$ZMXA$8ck-6j2b!c$8VdzVV@n%;5t+WU7ucgKCZb@YiR6Fpy&T+2g1J(f=!2=w~IxgJuhU^Z=$*%lCo)*saANyE7y*Lix_s=Lcd4BJH>xE~A6N^87 zRydJk{%kjnG|%tw9eT1P`Of0dg&TcW@%QmY-+dz;a6P?yQ}JSVU3UkZ%*{8K?z%3D zk>B!`tq)6wQa-Vpkvw~?`g_8aV{-JTE*A%jauFJJC=(8(MHe_+Y^8U`kzx<|p`%$l zOm?gl$99-r-a4N=SlTPKx-Dx2*Y@_`K6=R8b;szVYoNa;JI{LCZ+d639rJrN+;>0e z0TZ*kvi1Za2!eL^&Gbz1#HsAU)~5XjOckcL{v2gs@#&xerbp6%S(ydao-B5eKf>|7 zrJ{zN*j>_1)t@JyIARIri>Btv-RHRKv$H1IvmzPZsCzW3x(o|5Z#|{>NT-UIWDt_m zQdHgh%xFW4KUxew@@L~m&&j$VT7x86%5H0&6=hcMsR>Fxzt^8(g+sZq;Bse+jY^Vx zHN?{MB)Ma`=n zu@-ELU0L6+j~ELMG_o!-i6}nM3nYTF7w1fIcjQ*R=|&()THHG~l3cExcS4eM!na&` zey{G{eJWQpH=z5t{bb?u;()RqXz|hSVifzup>Q1-gmXfV>Dt35l%92+f4ec-*|Kk& z@_R{gA$wYrvf|T1?c#^I&;z+^f1!RQX?W4)e0dEQc(Jwhz$X$M>VfU^dv_lgyK?sx z8ipT`P)B*zZ(fqM!tP~5{$MFc1z3Try|jeJUUu7oJEGtHr={#iK|y(21iz6fcv(qL zs4M@olDFb_5@C+VMa6h|NivZn|D22DraVbrF|sesN|OJBa}(ABE}p65KRj|oSNoAt zkfCf8|Ibn;4|Kw1YSF(ajp2;%Jg|%Ym=8*zijVy`aox{^jc7iK;4>r0uGM{!7!pDJDj z2j!#i28n~~kHq2M`0jykXj~bLH`2gh>JIF+-yGPkJG`kVMUu)lmyQ506W&3Z2?h_I z3C7RRMb<&|xqeR?vUKTKy@jWWVj~#)iqdXf^JjgucfX=^07-Shw=w@Y-3;LChxX3z z)fpO*w}vmSj-d0QcxCQCzgO1m=Su>R`hnW>3;z9q>s$>uykB?zx7lGH=G*LaJN`B# z62ruueleW!M2$0W^?C{QFKy|60}OBHOgCol$iEKbW+(iz_w|7*)aXKA|B4OV9oMz5 z64rlb))@ti`!3|Q*uB5Xy`m>@yZ7#Rgli|suSx4Orw!`US09) z8o>h(j@@?Ph^f4K?KgDWf>D02U&hW*Hy@zv<2Un5A9y0iVD;WtdP1V-W2x?V7v$QM z2=(>h((XiY z#DB8u`5@kQ;B}?jB`3cNZi+G5Spq-BX&)&O_*hi;d!^t8h;$#eqfqjBZ_>f0Au<9V z9kEHMjquy=yQJ)3*JBh7*&mFYnIs>J7vy<#!6f-ZBF-fFxSP}6yE&Tw`j7mQJ99t$ z$2NlM4VSY|*uH1YR7-h`!~25J;$GJIm)2u@F(-b*6n$x|Cw38 z9F>2HGoxTX>pG3tr}Hxm=6=ST6X4q=%Yhs(Z)S1h@4;iOCv*}H&cE&T3IeCBm0$jdoD@ma{I3qt$&CD zzLr06IP&$pG1h-sWB$DObL*b}g1ibuAOcbR7{hI5mmwGqyn5_bsd0#>wwoC_u+2r@ z*#k_poE{B?K5%ESE$)*z7ueuBuN*Up@i20i$J&?W+Mp4ZHasp@fy=kXZoAF+M?Xww zTw!-B9Jq4SDqb~e^se^caZl!gt#9IhKY-j_QylH^$jt7egThYMFX4H&+Yj>RALVES zp>{eO0V6HPm37=@c}xp?l3oAuVmz=r>%33z^6vEq<|^U#PNArKfAsP~_g)pT^3@_v z_FUS_*W|tY_^rLXcWW>2+uF-d813aJj`s4k-pfz=R=q4`x$ZHdu_wDsj8H5wB)eWF zQu6gbaWEu-YTG5l;lbwro56Vb|Gx&~F@*lfk1;?`@d3JiG&FJ}_tboV9*ffa|M(bk zkpKJc1ez8~(vBF>ntiwDHQ8pq!L)96zQfPY567)i)4u=t;`Jyi(7)0T%{r0$DKb#@ z?+V}l%zJ&ev=Y9*+xNG<$oD@Pzi;~fX8|PkFZ}+q@p=!1?{5s>Ul#U18TN1V{m;e5 z`}f;g@%u2l=RNQB^RIl+&;Q`vzMm|F{gB~KLVG{>dH=pN?%!)M5c?PI|DpZ9dwED; z{=r9lKMCLe!5@bGaQ{E}WY`ba``DhaA7@8|UHhZ)+y3V-^GEXg7m6o~XZp6C|6ku# zJU=f8yYzGx0OR_9b64^FJahdm&L7+Mcg6ERcDJ9mZQ)y;zx|oT^FJQ?cbgv^{gK); zwq0*KMh-J zuhFmGvR*IOZeC7qS?{#!w^TR!x16uHhW=l#)9T;Sua_&0PXDI$ZnvDAIDK~i)bjqx zn-7gA(`V-wX6H{+$fTn)6G!(?9K3n*=8652#uz#l4p@!DK zzG>n+_TSX1HTvaBvwqWRw^v{5@4snn|4nE3v)XAj1~;wt%B}iM-42DZ^&ST~eUCsUYlbatoH}ycCFFox2x^WO|^QnKB(W+9yXitw>MSS>(z6*Zmm2h-_);H z2aQf!`__bO-_!0S_s%UX&!0J+Y*f3$ldAV^c4)T!r@d;eLX~ma>eT9KzuxZG z)3ti1m9U?7%Y*fFy<1LK$0x>*jE|2edcdS#?Ke_xG8p!g_06J83rB=aPQCM@OV|eJiat>*ZeRzfW4_ z3(aA>&rciaYKLZ5%I$MBaIM}?Xnwu7QLh~;=bM$JHR$nchOEnv&((FeMtilBt|D1! zx7yGf3I|m#L7mt}rJm}`q|)l9wR%-eN|x0b)5F!(dhcXoZGDPr?4(s+EBDL&l(wWi zJYvb|8;xo`rCa-hb(*rCG^%N(+^;7tvsA{t=HIKaGLe@+F(7ocdfj1=9GTe?Ox=881A&%8A?*Q z86{KwYMIXKr041vtBhwdKXdZdG3&3h^d{P3ifjqA9%m^J1+G?c?Oxmg=FBN(_@$RBAHN+)J%Y)9a znRMw8jnN?K(i2UZDB(dFBq20J1L6Z^zdM$!qgu^+dUQNl?l#)P?i@0u23Kk{I;mD- zWS~E%M)w`8WXTZ?6QrpXxm|A$($!{p4Pjt3x(Q=j-{4@ffe3ey z-)2(3Fc`?9MLCgzgv)iyLo~X)S#Au{c4v@Q`WKT6{n{X@x6cpj!@5Kz=~k=sSJEF= zX1D>(p6Rqq+&6oT0irGAcClMe2K|1AMlm|fQ=lgY)r*X7wSm&x?Q8Yb@~}Bb86Tbq z!PSGKn~icarB^F;L}bv5@@wCgtAi#_%hUBI`}H-1vzFNh^H>W zuZ?UV)eSlr?I~aICXQO4a&@(XkYmn=z5XB>_A6YyU!CkW5I64-IU%=3T{nklL$nYR zhq&9tM!i|X>NR@j6Bl$81Np9{%?=O2^#-^f`*Trd#64XyXmy)utrwiY8q(cuN%797 z>kVYQS6#m-XO?v}dw)7RzdSX)FsF-} zon0+Ansj(lZIOS3a#YS%*j)&>gt+Y3W8)>yxKrbWpuX)M7?&f8uUgsAP7#c zuA7d?bJD}2&`#MxlfvrxdUC2eSe{v)pFguyr*{w!+3tJm+_KX?Rek~Qq+IKj>6dEX z9Eq7jG+iJ8ZzlKYu42Rvm-YPex_FWXqJSCg;fw6?gBPlH4;?%zH#%%L%#E&>`|IjK zJp1~sX|szv>9(qAANS{e3#ft?PmE7Y`n{*+FnYB?zY1LHGKw6=N?xee8NXVym#oa3 zNSD`#gIZ^^z1XYk{LEsZs(mD*j3G>`tHXBn;3W1R8CJ`?9eNtr)=R2r4F2EjMK8F# zq-|Z%={45eKFJ^$;5oTCmGsvzae@n@*Oef0#d@zdL=4=8^$t!3SC9DAP<_+~nF2ttGY%v>U-9W9q}Y$;H{5e@LDhTfAZ zU*UbTMvsrzrj&*-hY_ z9KWBMom)z0&YU_mb$T|Pnw?E&PR`BTvwT+gY}j9yr;{B*#QN@U=x02;tVORyyHZ(X zo>fr5K(C&!JYZzcA_@~n%pQ$9W-w5LC*VQVt8!Tq>TbK6;tlzs>0J)jL2E~)caDP? z4DSh{jDoeUjURL6JU>LI0YHl8P0qj%8ruCRXQcm#PPWT7#DQ#^;T$tMRO<8{|E*^~_OyV$t<*OZq2wle} z=$Gs{k0`HhE~t+f);q}}mPIajY1nRa%QIjhnHe{0hICOvZjMM3iKwUO&VUeFYe)-E zX!u?7&~bP*;>((yAjoQ=X2Bx1AS9wU0PXo95icXtF=bC3QW0`^Ol6?Kz_K}SEQmSj z6!RFP6!8TqUaomDtsg@GQly~=ib8D}h#v1PPSZ}CZySi|5$umC7sjnxS;YjEFE%>> zZAM!*#o$MPPu#KEYjianhVi7F7Q;wW5{42#j6ZIy8az>$gbftzGgnK9&1D7oEmpOl zNd~!rUBKMc85dfhwr-Ytwd5o-gio!OvnbPi?b>Q$zB;$~7%D}ldBX$^>NV+Q0TPW* zlzj}(uhSh|pD;_-y$y<1hj7p&p+6W_z`*^knFPTh0;akRhC~cMrEf9mz)(ph0g}L? z;eCu`r3MP8(}ul^Qct`?U)FVYYGo>&K70H)#?c}9db0rlj`gaJ{= z=xSM`W6byS-}M-CZ>f}Lh~*U@SL4<1G0 zk1;Icj!=}Q;x#u=1X?}08p0P4c`?j!9>GkT!L%6J1Vk^RltWCDXC|KHauM=CqelUacC^3V zSREkTx#iZiFwcOdM2M-=qc4Iv(-alp#|-3+xnos5#sLhX+5vQ1lPhpt zv{8{FBxbP+5fq@HoXY;=C)5T)4~Rlct>nmJ#vi`Vd)IyIbIB*RYq_R=57&ipK z)>wtB&BoxO89t5885^6n$D99~qy6u2w2-!&>%`ciafxYK;xW7KOeI|w;F}|>G{7e*^jNxF1G%Qv z^X3`SiGzm@gZw0F!{#}0`DR3+0;1iRI7Fa;o=I_6VJV1!VNIRgBR<;-TX_-!`thWJ zI#`HkB4%+w1NYhm5Sd+;-%CI-pmk$4>@nnh=1<={wJ;AUw6Zieb&5bdLi|zlDfbo_ z{*2J!adE9BpWcB=NG?pnAnAAT_%4l$P2O5AzCTI6$j_ z1Ox5r-v*>x1BbcrK&w0p0f;lfo}~)%tlQV2XBu=h*bm#hS>)D;j4|Yx2lLS4Eo?N+ zr@HTH5kX&2R4b#_6h*IyKB8FezQOIx^-mle7koB&i){|*7TK#o3yCPrC@;*3bC0Y} zGHa$n;mV*2>NZMp$vbuljhFK_;Lxg<`wX;Px~x>Q1713}hOr7w6D-0Icl(>jsS$>K zJ?+KF3u1C>)=UYxx(=biWEm*396*JDb1Od`orb|)bEQH{QPzme%aTNc$`VH5o{aVu zj0mvLY&+bH(PD%E-Vx360~gZ}Z}%^@ak4x-J|Ab&t8`%cG5;A)qj*IYP&eqWPnj{_ zf>ij%KzG)>!^aNNyww)HMk++mUnHVPTNwhG>x+&LykSx`1cAGXWsMAm)U{x?tqbLn zdY!<{L{U&cqic#f9M^vn`foA--GeT&AON7D(*^u6fE0q?bmr7-x`2*B&7|{Zl4;`Z z6J(6)gh4aLF3e1ys<&1QEJ_wdzP4@P;4G(`L$_c#3Rqn|=H#k}5~!1?Kzq`D+J?Q4 zyc}_Z_+Vp3!8yP{vpKb)5-N&c!AL2@l>yE^t})^rI*LJ02EFxaIcfm?4H$-FD?%&W zoDG#}bO=J^lOc;Nk<^ja139n@wSJ5m6nJodWM#v$8VOvBVidHs*GXF~0x0=%i}u{0 zA#d{7gt}lU=qpRUJP%S&5rEPB#|YiKm=FaPjK7|*T=O8*xh4s5fI=h^>6TtAc>x_E zE4aM4Fu#(nEKQwWo|;)9^%tz*sN*OLWMORS)p_v31$%IZ&ji9RSY> zfs%{!c*8cUeK>$4hsG})IyQdcm}R&ur;;qyyUmNom91UYPjnEhRzbeJ30u=N!M6lA zWMIuSL!49*G}{$cjZ?-&HI%1{8YV;0=m%PaCs)d)4-6Gm5n>b&f2zSXw64)+^i~@e zwm5D08B3??3CSGB-(uAWG9aNWmbFIJFLaYR>>xqyjOCNDIu%sloIhkxX%j1{Xc9b# zrOV92G6t(;a6AMvQck&vZdLJYbf!02q?#_Vs!=uNP{gJ8MU~MD>xc($if4Fb z<#Rq703ucc!*9qnWK-3y;CChtMeWF_+n{DZV8lp*Cm<~E>uJuU+;6IRT|yq9xCooZ zg+nR6CYWs3OgC47bBi?PQIy;OFzMx5qej9kS>)G?EpgW}jeQaw8eesUUq?F9l_@E= zhvZ{~Sb9QZljc@UB7xvo`@m`xsd92Tn^Q^_C>Rkmks((y-5?inyx?&aM`u91MFv&$ zWJqV0f&APDY05eazH&6FFtxC7=DzgQ)com{xzkgpXXYNmu!QcE(Hti|1v*zADWhD3 zISx)5L~yecVjU?nDFd0M2JyHOu9oTLQ3w?n=Qx5`jA7f3jx(0D8s=zGP%exxfUv8e z%Isx4hLwdsHiFmNx8?g|`rdN*DJfGV=e21vzT9fa!Opr54+9~C81x8R zgX^*?1;;|ec0!ZH1qS9YmFKk;%fm!1C@Q$t)6F0gyAAYroD)e z$j{Y?6U5%aVAB~`;fxxdKC`qkHyfHMALeFJatEEJHj~9KH>=h?W=|9Adf$}Iftyh` zq(_cLj6_NXz)^uO2Er2*=pT!HX#B$)g8>AnVH8?$25y2 zi!s|u*`d0F^4ijg>F1oCnnh_#|(r(!cKnMwGNx2_VZ#|HT#S`e7$&_^^WLuCY z<(yK94+w{@5Lj|wA)bA!nIs;NmvWA_5|b@1A0`QUN)N}4u2z1*FD{`Gb|M??c-Vqr z#lc&FBc}MU?!nV)C_-z3loIXNU}ew;m0vVkDmO9z1OuvC3nkAjT_sYmzt*&H$pOQ} z;lrzkl?t`&w*~x(*cF#R435ljo{S2>v(YwZmdn;bXNd>U)0P{MIOLR9P<9;*p4&1d z2-&i`Uch&1(Z5AY=7LQUTs>J;XxOYZ4ki)SS$&ErhYv^Ijw6l^tzUJoq|?RwS+_*f zemGgYSDH6%b#H7Z<0IMQBQg ziNH21S{+s0$wQ>qB%}tyL^Ad~FfjvR0n5V7$z{bXA|6!`0``{b2`877utu(wZb3S2WzowSZ9{~~g8N)6r9|*$Cz|1YCr_Yd{JFOIYjNt=J>~9iL ztXqOd2975sk1#WR-z-phB^3NDxL_94!lE;E_SC7_th1xwj>i^HOhj6`-LXI;^adax z6~8H3R9>Vw{x}cQVwnT3L+9E9$@Wl80o$k)f=EGlUShRYANJOn5CIqq&RiLTu3@5@ zDxisp*kn-aiv%?sJTVCfHowJfNAyObZ|aS+W*{qDP3h`tWfe1*9_8I?P#G%8vjUDH z^6epaTYzttG?S2-qSY!AAcUA)rre>#**z6HGjR|^4+SCZTUOFuXlI8Z`9qCRCw< zHJlPkhsfT|EZ-NffLcVq3r))miLsMicDExj5WKYlLw~+$*0cZvg(2ou5*H zR%WFFb~x9dp_S9a*1TwP8M6?%IsYY7ghTW0M%;w`q`MI3rTNn*(zB;2DOfstI)}XD2gw*qS1pG_JV%kK^LoHrm3msCtqmUjX{1=t zhD~tZ)ld4H1pyi$7Y_j#%T|Oi)t|-)6lpv61F~3VC_IH0!agJ;Q{nu$6>uac`Q>-s%f< zHA1)cKzShd{2?AUrdr*G&i0c*Q<(~?V-PPSLrx^i4ajDKl1z0ywANM4ETVAJczl43 z8mMOZupu9lzRO1s9;wPZM zkV@pl`kxIHff(6B9$PPPLdc|lwZUUu~ru& z>IU3FiNi@uigWr!P7fqA>XGX;fC>>=7Q`1wZ4aFh2u&Zc6lp-pCLMB(jS6Xyd@v_a z3+PjtgP^s+@R*U1$H>y5BAYBUP^~zbh(3IEeUoZ5C!sA!iNbLR#6gXZNA1XoK|2jN zDG~Q`aLS@bBSsn_h`@ELqTJ9zckUTM;{`E|{(Y*r&IImOOk${>aIU*)0|6BTgMLwGNPJX;<{^>q?u2D^>Y(7P7(nM6VL zRxtCzoy42=chcKz%$1r`RdJA%qmb^M7+1C=YAnn!#=}r#RkfSZAzBT9gFk*On!ZD+ zphB2XqveU+tw`7!%MGb~G@?E}=P}1(9ciNF9cci-YGO;{fa;#V?Sxe2>ZL_$5=F#I ze2STN9G#qs`Swv920vZyN8I~@?pmeWbY2bi^Zpg0_ok0V`^$D~3tvOpw^ zp!G@3Fhz(;D9W6}BX*j@Ry%h6r2%0uH9Ux_(}NYMcWFL-pz^I?-darWD>HY%XjxSW zn=Qip+WR>)%1Ew-^2RXwP*=utq(c1PvuQI9$6ZlfdMJ`&_!{6+)f3Cpjp+fd8|TRF;P)?^hSxPMXiaYchX|0)N`s2NU2_$d$=aYY*Zo zpMq)TBkS8E<;jFOQ?Y#2tc|)cXINqh5fW!!&;gRU*h^F@GQ|`C+_yBpLIx@{a`})0 zOP=2FU=W!tE=eMQ-~}*?n20}`3|vmZ!&5aJjY)bC>@e(EJ-AJO!TVQ$)%}dMUd)RT z6sDt5j69xSHxdvV6`ELdSgjz6s^Aog+tMKup?uyGtp;*)Zo9v1CZ}1SI3n|negK=h z7D`x9v(Q8fqgKTPyls!Je#gTvLC(}Gq9SL+tqau$1?Kbwfe)VX65nBHiK`yQ6th; zC=VksAkHt4%L$~;p#YuQht?o>1WVd zkV+M(6QrBvj5C6$H-X=hqmdX2D*&+JHq7y9)UsSOSMr)!iLP;NB55eYH3KWs0!vSp zhxr{!{8$o_LH5)dno1B*x4AJ=R3|qbsT7JaIL`F#_4)@a;rR4qaNv>KcACq%Pc#1=5`b7CJYCOcPxd;kW6rvEBzI#1Q6Vf{&;#Dn5HD=;sJszDx zao%VWlBy)fE_T=ffcQA}&7qYa>U8xpt3DIiikBr>i{vwH zIn$}p93=I+ChAu2N~EC*_@I|DkL9EcF3*B-17V(H&FnA}%w%5^$it>ot1H;3IVE}= zZcR^zMGf3OQ3BXv=1Lt6)M5lNjYd0Xc<6biO~`YFW(=52TLfHAuNYNtdV@7EJ~6HW z5fd#VD?tDlf+~R1C6@R?uAlU=DcH^s-4$mIQT$1~Vg6=|QHl0QLEd33W|?huQJ0Dfb3?*VdRFUoS#U=p z_7Je5O>uQr@q(T2abIJ0GBLJWz|<-fN(P&a8Y-|z*n&LGot&A^?NmP9+k-gI=h|}! zjhQ~H#zk|jyv_iL@?7Qy!!oW^kWD*Y-<+9Vlo<@6B+DhxDOMT9Bdy{#qi#bHX1Z;r zl|99eDscjgz}9!8_{%rb?=S<5oEvKw|K|2d(TPrp|NsHld4Lts2ZAm^@Q zw9YI{UO#i+>A9tug}JFEP4Qht@^Gi>cVpT#3^Gj?2_mBOYqF+k+L6t-(Uy>S)Dvml z&3TiPHt-uZnp}}ZG2(-;Ucv8CXv3^39tBpl(YC-6KZP(EpB+HPICj?zsM!#ugAXQ+ z+P$;~;N)&ZWmR$tmxTTR4ucFKXT`JF{4NB{rn6M7#1di+cO@$zD22OG29#^B3-U_K zDwOEDk#)*WR@UlDDAbENC;CWJIt=@4xoW$Bp33z0E<1Xt}#$vB;%$n9x&P8 zYGlG%3$i1Q87Mz8JW{h9wsH3O&ANpdFab5OiHfx1>JFysjs${)NUQ1Po`L3yfj#83 zsYmuX8{=YHjJ*x40`x8J##j8v+Uy!*<|s1KPDh8UFdIB>9n7{4IZ;1YM|4r$?D!Fk zM_jK)=7ga#Y9L`09HU@xw>ty!7RqmZ5>TE0$t4RNh1B2g*LX+dhK#K%sU)gR~9afOd)@@p?}riY=lcx%h@ zB?gmf)HaCtAs20R=yvAT+=uyGQIcAFX`x$Hsd0D1nISop52MnY>Wq~&1{b-`c_jAb>j z^l%GSh$O6~umgag;S##zi04Q~!^A+GSrHRcF=S8Wbv(8;grB)dCs`y>7>zjB%WSjN z{G|(Xr%zBA5TjrbzTA{nWXVsWBuA%J17)dvjT=K47i=V;&P?MH@;;?F&;;MA<4o{D zGScRZ|F9r2Oo582O#*$3=qwR#xW=@NjFgtv;=Rv-e{?W_#&OUjR5m%Vjkr%1M)N$r z420J1u{TDmg30uo8s(8i3C`=A3>2WAauXs1ZJ8yDl}#CD-O^0Y-MgrzI4W}?Rj#KM z7eR-R&14V(E>S~Sn@_Be*Eu<&79<4!m>uqiY6$i=;>5vdVY%o=!%8KrG}z}O^K4=# zgzAb=;-D?L1p;Ai8D`TT(}QD~5}XvZe8y}EOk+k6KV$>@;y9NH6$O(Wo}5Bs?}CZA zK`pxM`t%7onZRE`BVeLK@1ZlaLxzz?WqSg7@oC-&wshx0I7Ku3WBcFD! z(r5z@>VOyuP|#xK8cYma_RMYY88*Rt5?(duG@%3BUj;NWx_D1f$*K2eF*vii*fDc$ zW_?3(2MAL+;>a8?pITHd7zyG`pAH#F!(zVtObsn8q`WL0LWm6GKz8d*BWyjKVd^x1Tq}CyX-s=M zt69nLqwyGS(*+eM@m$N2p?bJrJ&h>w=fYX54u=pRWx+;zj!Ds4*%5DujYhvjOKX`3 z868bQk}^7pnzpJ|;*ckTn&iboL7B!u_1TP;6|xw%4@*Rz@d*oAh~N{Q8D=aXt}*L) zF$tmmFACQYkhRfoVd*YnH3G6c&mJoC=or)C1O}E)Oek5#qpWwJ zZL%bWwIDK=M(8$)1bmekbAI898F?mjYfK3ygd%?U(0FRXWshJy*tbZm z7#kTg0wm(8Q}NnxHsuMH4Kw0Nd5-XC?fO7V(F7_g5PX6hp89E=Do!JFG_JaE#25-M zmN7-PE7W+$X-UU2F6A(NDd^_U%bz$@tZZq8^KoB8HwkSM9jQ-2SCVQ#;ix84#btg} zgZPiwx#P=oCuDJ|2#RV%Gab^17s%6&EsiJb7bcZS5oAa+VJ5}$eJfNx%CTDMQG*EqsHDxY#(HCnEY=7s z<}#o*r^Hw~O;5F=6{~~_NDGISY_6#4eZf;2Ys5chwJEWI!}QDts^26fAPQgUR9j{b zPyh$(kjm?Ga5A)>Xw$8?didaB#~mW~DM7&2)n34;YsPIHgb652_&Tx4W<#tz$NNY4 z2-!YKxt!v_nGotr)vWr%#uk0+9S&o%aa7IR4lWx?j^j{0hgTH`5BEe&i>y>R*;kW9UMWG(^K@=@9 zy((Y`MMLx$K&iFXA{Q8m6#Kngz$nkeV$RcZ$;|9rYnZ79qUjFEVWh7o7NkC}qtJjF z`x-q_+1OuO#do4Sel)sD_!(zXGdG6qBani;bT&$lY>9!|MSu=?9aF06IhNb<3P%}c z9!U#P;SxiNE<&7?vlvHKlCVWZl}@vQ9QTQt)pRTA%X%I1xe%lxZ4*( zDVyPg*s^ZxB=j$J!!o(>j~4It6Bgr0m*!4rX8+1_7w5DJgMzLCCWqsvk%2d}k=6-W zxsWJE&InVg?0cMwC&^@R6m_A%k}#z`S!=jZ@4^vA=>(Xo-acjN!^nA2M9ZXyh)}Gg z*3wkcYu7bP_A@<|6i9EdpSaan_5CPxQd`G%n|K=t56+GXXAO!)P85z`qJzOoDktGU zLUDxx&MkoefW&6pXNh_|h9(wjd_;E`Tb@Zhb`>aP={&QWXmXJwfdzh$%$C6c}*li>ojoKiDiS4Oj|PtJ<$GT9Tw zNawQj3gH7<;vU@;LghVHhUJh!I&Vc=F{waj^y;&;9T9TE%{av)7hox4FIWm1L+o^D zkpwe9Oh1Hr(rlqZmTj?gqOy?2F$$QN#8VZ*b%YnI>7` zmlNr6@;3^Zqy&%-@GU{5HCsIgZ$Gz9J&vJ1=X;t>#!-3_k|mI)t&N~ru@w23h$ig4 zr4ggOVttqbHqag%Krq9a`^YS3pdb-wlk!fr%>>zE6KFE65}>Hh$`oVFKC*5>hR7&) z42%%H(*=%H;cB>G-{*8(f{5fCdgaXmSpnLW`3s$h_ytsmk*rzH)FBD+%sS~yW5tZr z!wK5DgMn-?fbNfVADYf&3KfiZ(~a})rJVFK=@)L9!^j~%$Z|8KZRKU)VbwSUq)SK2z2vt;qE#^j%RDnp)LxYK5$+6|ts=60HL5s~nT= z!HkKrpddqw0$@);9M&bV540A*d97NLlmA#6GaK?J>Nl@@GN;GTYaWBl3ySTRg1E9v zN;R|f)-`(%p;liXxBP+^;n*Bs=I3joZUz~IxE&RYCJ7lXJ+0CUI8JzeEtC!u12B`% zRYPP<+=J>>0JCS8rfm5bb26vqX6ISlb|l58aRnKZ`VSR17%yAdC?Et)utC*wWrsA( zS9h=gGpsns>O<;Lix%9FOK7E3_Z}yemL5O5un;YUnGgp|ngWC1hDsd4xcpxCD*Dxv zd98#rg;l0lHAk6XJPt9U5mPcv?s7Dl6f{jAbQXM^I}K=+YS+K#AnW~$w?-g z^rNzvTvef6&^8UYOsCI!V@>xm?2(BEQ0g;jHHVul)QJiWTP;?0=BGN~1qj69%ppE+%MK~lXH{q62L zJBjt`H4)Yd3B6maV#R!n*2>SjQ+%S zV@yf1(zEbiswwf9xUd#W&z2YnhDsO~l&iWY+@AMIK_}Fol+L)0#3jH-mvUg?(G|?MNpg2q%AhodPC>?_*Ud#NdNwchkSw3Mz zi-&yPbvA7;t_Y*GVFIKxV-AN5R-J|jliBozyN!i&cU|sMSw|N#*)|vqSp!CL(&!F# zi5O7`r(}7Z**KeGtLjZKRs%!{7|P}+t4vYUwe%WdPoW{12p$|mT&T@5@r$0}$dRzZ zE?`AiErK-B>tH$(LKEg{c#6_0P^2ke5pH^Ak` zggv%Mp6g&TuSb}5AUIE=W>_H*PAWxVF{gOMYc^F)l%z8%BUsD}2qd^3CgxMEr5>th z(wrp&lHf4#D`rJcYmcmiiUb!3ZHxaKgXk6hlY+{vOJsQb&LO^^x%P(Jgq6`GerP*3T-ld}Iz=>3K z_+MzE;6SxTPvjf3i2!=>L2f`MfcoxY@j7O!DZ##PW?J>na(`YOr0aud?D`-YPcSIP zR{CFErP;u$B0*lU0_eVh9Lj8E&;eLd8Jb0Z$huMC=oKsOPg_)fjX=BP&OZC@5_s+@QRPT}SZHP(lDlWZsV|vzXP#i5;(L-2KQpsA5oB)#80@)b13NmJU zc_lUz6@motonsu!zylIJX#uOQI!?u4sHuz-?id^|#d7-1V;Q$h3<~f~Ph~FIVwC{- zyl|tC%n8T{%0)GP$7xsX7#$dY?~#%I%Ly@57R?HNb-8T zLyH0?h3zz3#?Bnx%KUP=GJk6B%vo076fZZGTykjo(nB+s9-0L@c!Vc4ni$s#nr0%I zE+8_EMMG3*!js{~EW?OHRZxI=E)nTJ}q_?(-APb^;b0{hoOqBA>b%X z2I=sqE_&&o`_57Ai<4dLSK*2UCrQFaHK+`^N;8bBi*aKhy{VYPY*IWs+K2I_bb6_P z$L4mPuO8ii9WAU_e92hPoH=`XrLdYCV>K&kkEuqr#)hUEsk+4L?CEm2n$6RQ!`$p8 zSCYg8k&|;U(+n7>Sa7kW%@`sjRN^}i+R6(=Qw@a>eSBWLL5!t^ zh~0e^-f1N)Di)S$Qz2O@3M0#zyHJX1l?S2|^4Axz(-VxzW2&UgdlZ4qp)%~`tQCft zQ$17jV}wwi z2rJTjR=ORs#O+~=GBFGUS&9r3rBuDNC$vgYcwZa-v=S=EeT}(Te}KIioffco^0~|N zGgAvGzF?UGINpLae|%0JkJn+%+@k^*n>KCzA|gq1`_a`-SA|*DYgMBkiPVuPGAx-C z7KBu8d=M}d#^~sap<0QZH?PI?1AI>-F zVmCJ8S9mNZJ+k=B4MeO&h+r9|sPhsUMFaf?YoyJr17*&*XlFn5Ob!A`9X+7O7l3A0 z33Fc%botb&#Z$AUPGZ!A+I79*2;Uf}22vAYLZDBa$X+SM_#mPTi=@?%5mu92m;f0J zfaY5QlHBl0J5rSj8Kp*O+2V#wGDE_dk73#TEQMgRy$nx_RFPPMT2*~j0}C1Il=^6} zvvtZ8AxFU1(!lA`uPr~EexrQ;AnPe8d_o18FJ zC>~2ITtT)9T$s@f9Pu8=E`|lkw#7S<> z2PxGi9-OxsD|5M}VR7|?%<^KrtYnVi#8uL>7(ID0G61=qq~lczVD#w%7>AY5l++C< z*H;M?U$fC8dZW0B!mUSP4;(|kr3d{q}45#lqz6bT89 zt%e0XedkGDKHonqX*mc%h5ZBe-~FX~Gj5-^HbJuJa9gLmK>qQat= zaB4FcI@z8=V8vu>d&3;ykowb*+qD-(K{JGj!dXoR9w0Qi_(~Cf`*5PSb_Se6s?Gvy zw*r6^y@3LyXPF|8q4@&bL*|22h(;n$p5f@Npi)U=VGg}yju-6mv@c*%1lWt2>m;AL zjLGI~yPA4CzCQJiPNg$C}|cJ+v_<`RhHsr+w|*fX$4CP}-do6G-1!J}5k!_Of+M zMYC#Nv{fF)Wr$e+ZvbebJ5{q6v{-SxM#SG)g$!idM0q)Yk)}Z}Q2U7SqjHDUZ*haJ zBPujuo-I%lUnvgJC^sW@&8wM(Q7}NsnOve8yk((T>(uldMVKH{fdJVN*b+_RH{~)L zY26m=SH)HqHrb0SfDC$TFzkV zj+-$t%b+SRiL$K&15gIX`6`xy+EV!3vtvGKE($5(YZxdo-WIE#D!jRXcfkzRv$MEC_mQh(xzUi=zg&!8wWtoW_4h@`QsxJ^9Jkm#hfUO2dF za&m%yEL29E;B!BP6C!*@(adyp*E{t#rMH+O=t$*e2mJ&MA9im#J3 zp8_ttBLW4Cd=3E_CA5l$$J7r6^b#9;8;zMbAq3V8Du}Dn!b}^%?Xf0?ZspT{<&?B` zp#ZVAJ>9I5UrP?TZJNb-PMcqua-gsPz|k$_9z3-aXb_iqr$U&%>^>X|BYteTw8&aKSN=p`r~e~b1o18<6?+E6PWGz(gpm++{nd`6fvE1X@JrR0@YkjzXg z98nNn06jiped;H0eZqF`7M-;PR@+NcO-5*!O^QG-P(*Vs1u(Z|;#c+x7fk?T&L5RP z4b>i8Oy*}#!Dz1r10o%@`g|c9y3g0Zilgg|bEsqXheQ`qrY3|>f)yTfnxYrhX)E*_5OJot8z9 zxuT(A(zSw;+^XC!E2&{CjK;D@CAx`xsmywVJV>93Lz-KpBB6+h&McF~Y*&=$@4!(L z@fcLXFYp^=BrG0}@nYRy^T9dwp*L=#d{lB7zKrXA1K#zFt|yqn^E082bPKGDYh75` zbh*YmjZ{Ca0#dJ(k^eS5Qm>(8w$6*uv!btu6{Slw6(cX@4^?~WR#|ls1OB3JTaHCZ zXEQH49wGpLRY(R@btfq{W!FNANGxDbMF#P$V!_mGD0qhjhfvAZ#38P;93^20$@o#j z9AiA)Sqruq8|GRLS~6MEjVmWjjtJqRcgejNVDv z_P6u_^+0vyCAgT1EWwX>9KsH;3>94iR(?m?c=fp^0j(@e&&&vB(h>&*VP%OnHCnLn zHuNk=?P4kXimapG)J8DJDpX>bn*zxBYORW7vH6`YH6`X7@!!zz=8*gznnIm90#)oN zFl3^HRqp`wnK&wXMI)PIeexon78;9 zdlT$Z@cLW&(_g{j0IYu~<)x@00QN^oHq09r-#lkQzt|1)cO*4bTnB+m0dh$HrIl=} zngj-8!P}jOhQ=9$s#U;4$j{=aK;~wFoMGwPU8FxGn5_Pjb^N<94;G)(4Lywj7>Seu zCm~{G&D*7bRptU=#G^_&7)qX;x$jzQQC`=UBS@-3eZV#iX>J9BmZizOcfBe2L<@33l$wr*1&0W6);}Ycm@mrf6*N|w=ASAglnU7*zht3o zSr!A!TArf#0t+6q(jjxcC4Rg#-NRaYPSI)n^>ztH+SVzhGkI>bs%d$Q)WJ@dG6j)I z*(!pX3) z!Z(sDg?`vwbe!eYP;`ZkC=HBsaUB@Cd97}fSuI?}@|fSz1bUv5@$_nvw9G;QvTJ$? z1k(kaD+nvNAs{kuE~rAd;=kR|B}1&PYek%Y@6-Eeqa-;%cI8x-EkiS-;okC~sDvfR zS-Fpy|K$tTLjJQkRw|lmvY?-ZU8bM%3jRh%g)+e?$YGiTwKZsL1a%pBP(jSasimn? zVD+T{G}TLV7_^711`QP>8u%#XEVjU&Y_$b1lnzBuTGS!>Zdo{5;#NH17r33pE#w)kreSn0S=C9f41o2|QFfvHGy-H0 zSPZCwd8&|E(=DqsG^xRwYV{BX*_tPZ2_PU{(g)KrD$=S@SCeWjpCCV+Ls+L|dcN#H zs7aP}^i)OUaI{*MYA(%W@Wh(ln=h!r$@ZRO?i7%)CP+q-tqmQM8&`7+u>`mI7Br_Qe6d|QTjBy`t+XX3&{B+6%LLc7Jo+j>pBSMy7o9p7*gz-W z9eO)tfzOD=-r>c)CZZ-@(eV2D2(56ug{#^i+5{{^QcPc@CdFYX_^2nLbY}|AGhLQF z>rn$loLy@;CnCAoDZ>!4uZN zt1D&N+(=3?BP;+8Rc!$_Ua3=|VuUvXo-sLJRQnZRg&CUxr>t+1Kw!P95U$CPXQ0>1 zhl{J^M@q_=6-&Dqoo~Db7ETeJoC)~3MK}6eei1XRr5(B7`uL$Om?OjS9b1fxiy2bY z#6v27$}HCb7|HUi%4N|?%WxoZvf}_FUy2NJEU9dOa#Vb+cl+>Y#dN$O8ae0hDLAy_Q&1FxYv&foBzXBSMJj6F8DW+54o3^PRI zEUO@3VX$31gXfXbHEN4qC`|=5X-*ND5+=sZi)+Hq^Bk4ucv(h=srsv{8jFy+2gt?x z8kGault@bhW)cO9DK{u~)in>kKVaKX+YmY0L*i(a%X0BH!cj}&vjF?RR-s$s4U`vG zBxr;l6F$#RnQx7+ULds5-(*}yXLm*WXG{D$I<#olR9VU`LoB>%HDhwY<4SqZhEQha z7TYA!OnOq+)Iwo)&34yZfkq3f8jLUR<~@&8fhJ@Fj08g=#)I$|U{qKdg`o`8zPYZD z%&?zOm}ZOYAUoaiMbT;nt~I)qEHwb_>U3X-^mr05Bk4$1Y%)9%A`%-(2I2yVYXV?j zw8A)bREh!xUAr#)1s<{Jsy`EI0S;t^|?!{{29ie72o`;5(j>SO-UCU}f%Bv_YY%XJIMRA)r zbOklSM6OPwj+$~ql?&Kg+b#)Z78|`Oadkm46$%Ij1fdgD#4FqCfzz0h6TAgosv_dc z#g`|hL?NShr}UO=oZNY0#I^P0oIy=aHS8Vkp>`H#J%H=+i_A#Kyfw~KTtUB9Q36Td zL%^J8JsDfY1f{QEz&KXzjRkseSr@&JhE-UHUeBdOi`?2 z8U+NZC9`TdcW^aUP0C8U)x5A&jo2?^483H;UzF=~1NxX&svBFaX{J(K#C}QV7#Xb2 zQZMDJUTE5CjW$UR88_Az7VuVCg?d*}B5__sVY=3q_$$6Z*2MtX^crumd_V=`f)Ibz zG&iDpa%vd(kGDPm~MJ%SY&$bw=OY}R8h zckKAd~UCxq9(T!SZ} zo{3L!MO#nJtZ$*fC9Skb6T;+Ymv!Ydtkp|KTX#XsK6#pZB>^2F{e-3 z%94?UU}A~hwdr-)(%8)Ss$qpYU%Cjw!kN=2!fIclK|@^b@rRGNV^?`^Hv+Us<^^X6 zM{afL?zbd71dp@>BNVFEvg_9apq7G2ae+Lf*253flz~9CU9ro!J%zSd-UK=%G6G7O%fp(La%U$+wTKG7S4)uS za{d|~8E4^X<6E-sl=$UoYnAW@+is4N&&%(O#Ge^v|LdPBzEV%Z{j zBtPKneJH2#8MiLN8c-|pW4^Uv469XP(9OAb9|+=Bgde18#l5=^R#K17)E;WU+iAsC zq=Nf%_p%tKFQ=o0KLQ&Wnot}X6P+yuQx@zA-f?Xs>jTbbe9E=+kTBpS%k*kIlw}n? ziiWZBAYmMfuqxMT0TYADlF!QKp>dOy6z*5WgYsOqSQT}Z>(w}~%at<9p1@aVZ#)Dd zm$MZ+qiL=hzv%s|3i)K`RH5fFsjvX2c(ILKjlJ5()BqXvB|xM>^d@OE`9@{ujFoW_ ztgm9!tpot&WOZ5LU38l+Y4`P_TdnC}H*tO-3m+L<@{Rh9NMS_;d5nlpGWVsXn$+0p ze*&K#$7<{d(axM*VsePDLmZ@u5pLm&&AM*>H`?9@sLt!k6a4;M9#0asiH#*}(Kfc8 zENtT#NyNf2iJxt3<2G#(gCvsR7P63pWNcvp5^k$w5|aeUFAYlQ4%1Cq%!VX%tnQ|2 z)mv1Js+QfTx9JISNDq_IOtjT>2hXUcwA)m*YF9m_YVYrN?!Di4zXx*Cv%60=@7?eI zKlj{o&OPVc^Bu!%b;N=?$l73<Jdk5Ml}x7}N?AhQ+)?rV^=8H8vyW2N((c=VX*- zV;e6f?aQt880M7+dXqwSf^AChMZ_#_YHDntW|hs&q4Ndo&%N-{vvqdtIE0ItC>9tX zA?k%TW5|j_J@W0PJuusm#gQ$Fa*3~mLKrtjsFSn^ zI70S}Vv)=VMlj0XR@GXCu*Se$Ax_H`{UR?`b(0vn zEXfs_l>CBXtD%BIceGAOFCdsXdc=F$h0|dpb9^|afc@dRf>BUJ_Mj7RU(!J(#1{s1 zg%|?y@?bASMDSv8DJ-uE_Kr8l!jtH?^c7u=r0x4(-3QTED~t(jvyAp!934Ql37j5? zngs5JKzbP9hW5rC-!K+$fg(C@QDkM*>D&l2M6XH+fI3r&mfQr62#vAj3=~~4HO(M^y+TsXut*zeL zg^*-O`?Wwxt1rqUz^aZ@E0=rZCXnX-zz%v7XyFMbA)Y!dg%giDd7F^vO^x0s3UP*; z|2EngFaDdrXSl3~9RPi;uq^6?@y zQzxcR%=Mmny;IySpkj~{Gms>YxIs3Tw-;OdM`UoElDI{!GqneoC%o$qxC`1SFN{IY za74rWv-)&!~;18$|_O@7QTnY<*hCq7gTdi@kkd( zl_>C!z@LEC}r1#o7LeCr>U>I^H zEMPbXnzzqJ5mD)c!ofq{$LfK8==aha+kl}GGD^LClu@iw90U~VW5Alzd-bryu>rZ^ zmfQN+tMGybCSza0n!Vj3Di%@e1u;K3hQt2@Ba80%nIk9%#5j3C1aa#|Kz4xwR2@JM znC(T3fQ*heRRE4UN*p4kGAyNdpwQ1us~4;PZQ%!iB+xeuGkLV4U9UN(VBYLaO@h{C3a>xFmnrewV~)0zta+0Gu`i+V+J;k? z$UBy%?|b!CG1c?pfzT9vuoZ3%dC9@^#Otphb_m}k2iGx0VVDxDhIY3M|r59 zpdxDoUw8qg8GR83lu0y!bXnqrzyRWl6~d!p^D3QQC{T)&M+G1ZytuEQ z5^N7HFSwCpt`1PA(bI#~;j93WE)WjthqS$mr}5~FE*IrBB@@XAuFj1J_Nbk;cfSyc z?<@6JvIG0x;ha}9jW;9eSS+M|I4=R^qC*u2hw2LB^;Xz!Y&qsK<&&(k;?R=(w z@6)@Udk)?xHm2(S_7h-B6oc~4e0nBwtkj|)aKxOBwOA=4e(DD16EYylw@_5ekhRUGKBDVq9$FB>{-P&;np~mQFsFxZ$oA2u}|ilcUXNgJ)x2wc5Rj1I&c%XR{uY(>iGsPLV>t=oprK0Z@}ZROuUZ zA~{m#1~a5;UW}>GBqAIL1Xe@i=urlO2!Q~b!$#$?G6AJi9y}Q=K?4o(66p9Nj9*l8 zwD9o0w|R!0D?yKO7Z_I)o_eY2sb?futUggOwhmUW#$!CnXP$1*E~!vT!MfLji{EsJ z7s7Se0UezEwTQsBA$I7C1CT3>^AijKz&H3f(WCIa$!ASQ5 zxmWHnq0$@!7l)nW#-)_fB-qFZIDcqO-*Y@k7#xtjDh@itO~$z2E-~)A$r6EKWK$y0 zP#J8U7zj*eKpBBg@#*YGNrVTqTt^d|**Q0!Zg1@E=LBL>@ML z1aaFPVlof45^Njdd?(t#LOLFYtrt$i*^t zN`2Ic;xRDpO558Z34il{58!1v@8c)wQFMB&k02^Vcz-E z#5~ekYH^sVILw#tlE;Aqi-PO7E-f}M*&=di;T-w{9hd>v!YT0}Fgt84*v7{WA$S_? zFmg*B%ZPm17eEF?iewxC-tnNaS=dVCI3Px7yg!;={6Kq9clsts7-MslsG2)toURWdYe&PthAXCj1MMD}64)lvfJ5iPhHkq9#5>VTaY z?R5x$P4(~wfj9CqFFxB$LaH$bS}Ul+Kt}TRgS|vAvxTU@SKDY$HCH#X7212t8mhqG zV@AQ$Hp_a<33TgYuQ52@8#t=Tg+I4uby7*g4T|w=rvt4#pyFMw*}_1~>zQ=%aj&aF zX0oi6K%jdghz<|sJUBRhpulG?KJA8Z0x%>9mpZAVj8AOFiZ0iqi2pz|$XGl(kj3(i zy<%d3203YFr|bPz~&N{5DXb&@75R{T@nMJR%NI6t1iU*NsP_U6#=#lsyGD0S47+ zgf4MH#fb`<>iG9#9{tRpX?o#>oiFQy1?YycPU{dsKs!o}5wq5MDH3RT6^kZ2SYc7h znYE!#2oa@4`5?woVv$B(y(J+dV#)@*`ur}7q{UZAMdag{6Uy~zogF|X;Mx8BQ&c9` z%YHo89K|2dFF8d;Z5+}|v(lYH=)Ctk2ViA_0K^5L2xwj^7KJJ&#<4f+u0=@8fnRP6 zYN<&ZpoLLNQ6o#)Ai6LI&{T<&0$>C69f+H=t%XA)CMkWMjT9slR9HComLl}84aWas6y3KEH1MbeZR)pqwv1L&OH2rUB!r0Klb=GGz^=Z z924Y*4pxA27sC$-Vb*tRB=WA+-h)2TFh#RpQmg45NB0Nd`ora&;Ib}S#5x;TDt?&@dILEC>F(;T<5~1g zk-dnKBru@;RMry>p+PcM}Yt0j6#i`Jx_>FicDSX zGo%@YDe=&ukHcEwXU&-+%8MNLCZsaC$_c4ht}+3c^x4t2{yL&gQEw)Q4zrjZr6b|+ z&IC>OqGY5x5a=oUN(TqS(fTk&3y?3FsPHb~v_j^o+TJDvo>v{Lmz*3ku>ZiMu*o#T zglG;=e8y{QGAf(vOzL;-Ug2XhBeJlu`=wXhoq zi_vEqidh6S6(9JYv1;M~wlE#xmJV_QqdjLJP8pj-*v~nkTuFw;;eiLt!#B%v@hQ?0qfuM+8PU0()6QL-k4q8A9$7F&Z|*Xlb0ET5+kiM%8woH;bis8H4H1V8REpg8wB)Bl zGF z3f-Rxbz$T03T=g*iqbGHOUL_&qKOvVVeLAhuTz+vXD>#GYT3TYn@DYU4|XOc*mkHu zas1&qlZPvA8xt&zBnMLPHfl-pai{OVe)uDact_8C@Q;HCaf|@!XpS$5ht-j-l4V0F z4~dYf>a7s01kPe<09w4-0J6mWL`p9wlbSx*(K>``zIfm*htzaHrN+Cfc%ciVLV(rk z7~{YQMC6X&k$#Q28WR!?l3)-;TS#+8wd=l%NF1M@g+SAjh*l^?;fhX^2N)i+iB9oF zBSh|y*d2Va4Q9!=fNRr5_2cf_bq#5O?1_sVJ#X=p#z25K3kCF4U0oCiC{hWC{lgFk z!{Qm;L4YT06XHf!hw&W~!x32c3imy8H}@GGQCyTK8hboO?-U^`3XC9@QEKpb5XSN_ z6vDo)r+`5sKbtp1n2U*BxbaGn&D_w^M2rYPsoNZXVZ4lmSSG9_c*%Dz3QU$s0!>!8G>2_6SguwE5^UJZya78T_T0Q#*Ei1~1#dJ6Sof z?f9dIj|o8tW@@dTQRBwnGq!r z)d+vKdb{d$vAuf{MHqknzk|uY2n-1TLb!W`8jMF0PgjE!Uub}>(|))bV?V|TzuIKH&&*T3QD=}#J8#h&MT&p(y%x9{8PJ^9f` z%gg>B)$8xEdgR}v%$Ob=vulW<0+b zzxkMLJMZUiUgrIr9?!>>`Ng;{^S9+6(~cR>g&EKP?0K(%p!U4LOY5rtd9iHW*GooJ zx;&FEf8eZSrgYZgtn&JOH&^xh*Znm)Z?(dgm8aIdJN!RfG`UJ;{i9fQ)6d`47KLj* zI-7W)))tH>pZJF`09zxQmKyE@e}6pkzpF0~ww_I-Q$?QNUz~iFXtK44RpJ&~i!jCr z7Jt`K98|frJBp>nlH{{Qi#>`};ud>UqQ$L|0k#)6KUNG7 zxJk8kt#bQE0T+goDp4LucTHw6zbD3*F`jsxW{OpPZ6^67(d2B}R*J_ln|9m%`{I&R z(I=Db$}_AE1{?K-XKu@XUxt=-wWwu0^E)wN&ofB*_gUbXe?+MpN`2Qqq7gPPeJ{Qc^Ba3ctZ-a4yp1jC{FX03l#SKTjF8{Y-!|pCzEPzs@%aN z7I2V$30Qs*JsCQ}-ZrTX+&y&ZR_Ke}5lOq5ztjxgttP;0)kpwboacQPZSeyGm2I@A6LT-~w3wi7i4-#!&#$3bXIxX>8-a6q~qS>pM zzpfUIcIj$@EiPSO30ZlDmeURl@XT%b!}LnX;+Z?<4@C0Z_5wpuji z56csq?8EA`%lMEKKtMK#YoLgOT_4sYSS6Frp8v2R*? zxy5a?QdntHs#HV^TeY=Rssd;)3CBcUDkp$G|D8 zqwoG;vdC`&<0rdMmN|?vSA2huy5dh3iLB%m+-4B;?zW=TxaU8z9i81`pLKfnnR1>H zoQX69VmEE;taIwD3FV9t?)bmGqSYS%lNZF|(cL4;%lW1mb>6>Ve|rU7Fnr9s?!E}l z9yhJ(n<2I|w?ax8@N<5g;c|J_X^pb}DOvKLbn{XB^U5m?a-J$}gj~?q@8fqQQQ0w| zObK#JDuSG^DjG|A?;i0XXw+nSH_DIuMuM}tt`@*-y&K9a!$`|ACA~S{R{4hhbzh9I z{@ph+cDOaKc|l=ea*XS%l$Y`Msm!>)_RAhRUc1w~4?PC+)2X zL({JPGUHs|Y}*eo%aL#b8&XZEDV0KR??9fKR`UgTG+(e-U?0t-W>fa}Z0dRn*}1|T zY1TU1dJIP^mRg{*!?I|`?*Kr4hsrcNPnySlJQ@i>s5;R7MSjhxMSfkuIRRe-1%n0n z8ZH>;*JQytew{Cv=GR=o0>5Tc?-lwTpe6caXWOT|mNj4Y*7R#(aXH8-Cpn@n6gbH- z%rj2rwRBB+9XLWskOghx=IdSrt)1?&T7PM-qr9V2I;#b@2=}0=jl@(&P`RH zJgcf|%ziv4cil1oykE%htir(^yKEmhjZVy1?L#UsioG8X<-nD;DN z4DXMwcDc%rRnmLce3cOANfm?9a6z%X#PvAY#nIa2SlOe_f_r0ajfp;(c2qdX-0Phvtjg@XQ*XrJC(qeaH8Y$6tsFsOytz$|K;wO< zxQ&Z~k6D!O6f3VB>WA{Zx0)SadT~T;b$j?u@m{+#N@q~h!09{1#coI6DTgHDrrAFy!P4#p=c{@Ke=djbK}J$nD#jk|RHEY8hRD$jIoK^j)_fl~(rUQSt)aHK?rwxPaOvZ!r-?{N9fg+nJYWjBp35X)9 zsC=yf`FH2fs@m}so{gve=ahfgOed=Gb}95Cp}!lGIKK^Fv)jh1@ikvP{V9KUgopk} zQ5KyO2xb!fgHUCtwe{XvmCiV#m*1Z&mtjUFzdXw=1AWI}_=zulbnlnEFL_muRCxaS zJ8F$GGz{=h24))nN{#4*UR7iMzoX7(Ab`H96Y7@$&i$$= zlX=&>eN(^v_+RnXmpDm1Q*444QFt)8zCqy7g%QXzkBf(oqPA4Z~2Cx-oTpC!j3rWSlf%3k+x;3x8-lnKT~*EAkX z1=#Ou3IGYVomDd!U9kPE3=7{e4t#mO*kG0aU$Wf2`eUKn0fboh4MY4k6$3ABTX(uk z6FMeG{d3CSs|Hg`DSt1jt_10v@}9(!G&HA%ymv@Fn_CNWUP^wpb|R;9HvGg}-dDW) zYs&a^)4gjJAq`Cw^ru!nc6Z#sfbycTF9Kz+`m=$Z3(_L2MoY`m%E?u#7)+8?pt2yI zni)&06gQaD?x`d^q!n4GVl4Spe}F4b7-QG6sN0BIH;~qYl3!6%c6YO7r@UT%oa)dl zG^ki5avR^0t zE-Np1N`BPV=_dwUtBz?u#&n)LN~9jeYnd6F#Ia`B(kaY47XY zxL5s=Aa&{;`8^Vx3&4IZaD|$%LRW*RRIm`*QqcE@_W5Q0*0qzkNjQ7A}5y^9AH^g_OzIyD=H?6`i~l5&@!ruMGYv$!0!4P}YUbW2>7`R$~83#qF-&~Xfc z-I_Nzozs+1(}_O|q^=M}Sduy*eoVR3Ag|RRR}++N+tq*?jtnfR6px~Q5ETW>t}web zysP}Is;B{CPlmG;if6;QFg&fWO~PgT0O4AO^(Q>thci|PL&XuTGQb#Z$HUki+)r&? z7}{b8dmC7~I?RA62smK}$;RJb5}52UEhZ&T;O>9pSlvS(=4 zdzBziC}@)3m;#;HC6vlcIk!wXtW%X@VWdI+ar4&q85?f8kzIRruX6T&h`Wsi3w*gDIp{wM}fYz72`>X94xtwR@u9LS!&)>v8 zmfCLJFgN3=4JQE$&erBYgscW>u_!Ligfp~Ff%?UtsMRT)Jc!y|eOT>je?}zB!YhS*c7^(vPL)fKq{E?E zPfe|xS_eo#o#+`jYF+RpmJfxid2wyou> z>nvEEI?yX_3A>klwWZ4cysCR!e!mUr>hrdByWW53DQ{EQea%;!DlyjV3Hf~jta0~> zqL38s4ZE-V>fQ?fi>kI;es^E+aVT`A{re?6{O(rG$k@%zKuT`AVr%I4fE87MCZ@&#KZ& zn_pSE2AU8V2TbZOLg&!jZ1bAm!RcBcDV>-x{DE+sHl@k{sZoELaJvYP1pxPac%GgJ z(JV(VR|WlCJcDe1&|F@$Hk`4_BF@^kyM7c?D~a~#C^&STlL8QuB{9&7It-5!Hv=2R zE+=^^g9ifEE`H9I<3&KdIN^oBNn0NT)#Fa|(y|jdX`SWu&l@(=D%v#W;FHKatTvEL zm9vDni|9Ff{Z~MrSUOrDum8FpXu3GeK>#Q&X90(XN&ydzdBp|YIOs?bL;7r|p8?^k z1mmlmrM+erTEA6B#zNZxsqHi56EnJy_GO7jqYHJY?P!VgL0aknopXwS3qb{`JZg^$ z%xZe5xb9SWyqK1zc%SJ6)#8-#F|a++$Rp4ZmA%GJuK9~8J=_R2PUP~*z+0d}M6t2~ zu%JX+r4(zAPX}T2b5sa?>+JRyG9N2ofXym~f{tzaQjK z^#|YPVe8ug3=@0Q1qBP?9@%#>k2DkM!$c}jrlK!Akv*$&{&`<@_XlS|NG1c-Jp%!E zGWZ1#aK8vf)h)-31RXw+hBR^x_J!mlnU@PEY%XYqplb`xha37J()3ZJF+a+-XL@-U zIUkM|+_Ori+3An7uk!_0epMOS+jfD_@n`{h)H2uFAd=#u)^3&w%Ii=su5?#=>)G%4 z-goZxRNr^~pmQ0}`7ysSQ$q^OdgFmvvj`IM!;GqZm>lqk>KI4^CIjhQTF?d($Q3@g zk{(~9AB?XVy%i5eZ=JkNKbX92`Zhe6zHPkNlql}M3lIA5TG*%`ENr~6S#!QsM_@Lpj zuz1(p-TJ}Y-OG35!SdZQG*nsGICT#mOx-hnpDrthhG9Wfh`)oBQ!RzmFtX)9&Z{xR>O*X{ z=Olg(K%>aSFi8P)u>L5Mx>k_vq>?0P7xYCOeL4P?tMU#!fN%_OlJ^0MiD97zMK%=J ztqbfHIuZe-12Zhah#R1nS1p(0QQPre0nNgw>ghq(ydTq+=Iv^9NcL`KLqLLB#?fdW z&aT5zmE}!ahZ|tMK{MqUSr!A#!{;3?co-(nl5s z>JWLen(4ka4$&QpUV|+I^J4)@YyxsS4NWSqNA?VyL(S4y5$d&b%G;<;U5BczvR;4J zcim!?Q<&VQK1X|gyl#*uGH%}plaALZ?;TkT=nVON1sXptO0<`ymZm*XtIJL|deo9G;>Cu!i**+NxWr)p@CO|g3U zNG$2D>q}PeMzd8QXjPq@mD3+HgwPnUbryL7=Zwi6Wc-^qF`LcdNhj)vki z>`Z{QKv2Lyd`R)T*d9IxYOhx*uMyMk_#b`zouNW_KXT15-)L;1z$Y49TVdLWj8y8^ zG=Q2ri}^Y-9|kk6X_o_rY321L6?7N7FpYY}6!uF7#~Tl-mbA1}m6WR-6`QSHNObk7 zuQJgGQqL2)Q7gOEFqb0k+@i7#@or3NILpfKT0M)qwY(ClIlC!PFJHq6_0Un((T6`L zF>vxR%_J;n{(tTtQ?={TR}u7buf%B#Cr%EO31zdn7yBcEfI?=pZ_JYaAB9^K(Wbct zKU)m$+j_GEwWhkJ(ov9sHsI*VQDW)@86}aPAGZWsL=k>X_KYHZ3*Z8yAsd?pH*>N1O{_a6vP+$#g=d!`oKf4%rrUyQ6ri2ML5!(+7S@L9 zxM&-B4|Hs4${OQZJrvQ9H>=9gdPH&F>?pVAU84(jApGW}7B^a2v2{lLNy2#&z$_T* z?}uf;ArEgt)~jCgwmBc1RhMM*3PAiN#U_} zN|6|wL91gU6lWq*cx7# z%(oF4Fx#UAQ@Q&BEhX@UxfZD!qpTU|d9Zfb3Cbwvg5|u$?UwvKDvJy)s7ETRo>C48 zHzTSkKQ8#c1Ui)BfaaXbcFZ_UO8*m`Sg z1?>zJduyQ{kK5{Ph7vypYVRmyQLL6NJX)ebWz*B-e$Ha{f^c*5liP|>5)zS_LN9q_?8h{+zm0bMBkIW+5}O%+ z)`(!U3ZwY26-O2QYIlLqkSL&=2-o%_3*2Fk&eAAjYXJ?xP*vwiiuX8@ z*yo^W-n#B|q;e6(t~=ZPpdlME(2bRLwsC(znY=7loZVMprU;o1r&pL~HPnZLOO)-S zDeh~nh~Q&}wGYsl_SYP@z=Yj&*-g&jeo~z4?l;ziyIK=!KpIJiY5NA0H$ljq_iu2k zc?Fs?Nl;LsH{f&;$PGkxzx-z96T^9t1E z8*d#d8&Lz^M(w|%?HJK4?kMI-_Xu^TO{zwV;cj!cPZ{(u3eboeAtR~MjR$W=@`)Kz z&LcFY!6&{&FGmZV3)wjte@&lVBtdl9@zEf#CMU_$PlC_x2A@6ehsPnRp7)oSUZU@P zLeqH%XzIh5Z!OgyQ8y(mGG9xvN??7aN2j4dC4(Lc1E!`|uQCmgvezucI2cnxoAIzm z!Ssj-B?mMJgd$jQdk>cahi>WwP%Tnn{Y^#a`j(FopG8ilwhylZU(1}d*KZW0$4n{2+R3vIL!fM03d z0E2v-Y`X)_P~?IUWFEpwYRxkBD`hzMF*8DA?@NGO1~`YXhy6%K16HyZd~?6$wRuN)%~w%^`OeOg1)x0 z>_L=;M*bi)@@QPN_|c3YYNm_faoc7+3D(ktT{d-p zB&nf@EV8AQl6G$q-Mk4a^|DHsZm`5UTGP*t8*@V^(dTBEmZyzV5N4><9`J zQWE-Qmeh-6AoXMekT2*CR5@L6-N^%lt(-j1)&gBqLjR~SGaU+6kNc<*~<#Or9v zOuJJ?8i0?chh&%*&r7{Ay)C6Y*Px>~o$U8dKqAD*x7}TgKbcD;f7nKe*^0H^pLA4( zYx=pUrd4?Wv%mX5t?k9Y-6x5O{ICx98UJ~~qhZW8^TTFQxVm6@|GJT~c=~NSxdNG( zui@FTPC_D7eKP5Weh#-56M#Nf4I*6F4NK6FcfTsRS8SeU9_J7rAzki_2oFFjBIuv1 zFA4x%C%q2Uo#lNWazz7|x1nYA;k-Y=;qZtQ;okcb7*LVjiI-BF(yF91nJ(QC45j04 z-=C-v_`50s@4u%R1Dcr5e!r1U>qpeRV8pv{a2i=fBkJiwhow-HEd++{>La8Oy{lAx z8`w(+ECccV74Q2ORB9mazQFgp1Tywg-S;Qdnp{yNTSbp2J#v;PdBKxg3<6I}?!FJ1 zX9p&+0|Lh`aj4b%c?NoIL!~0%>#~Q{4tOB$#xb;?@-F6*;7%+wDxT1z;++eD78-J3 zx1O=HmMm%23?PYLfVvifG!%S1-+n9Vh;St0=#yDo_l)`d zWE}-Kcaws_Hxo6R+k`Y2D7{~oM=9QK$HokXR34FdzY|2lrLlP65{dWwbbdr7-tQxo z5M1$b`m|{Jy=5!&z`sJ(ff?ct9UfKPo-pTu3Po66EYpsXC6F@RRrns3QVtsz&31Jr z%e-mrIL0-K^&Z~$rg+B(`ds@7&7yv<1DFw1)76k({-qiE{Aa4DAFHY*+-hr0Oc)29 zzE>}vG@eqG*3D$?rIf1twod|UhyJ~0irR4!VhV%FpsM_H2S7rM5y)3*M!qst@>NyK zubWgOQ^~^IQiSWh|GZFzU!@uTD#x!(6@MW(0p`J99)Jw)CpJ;cKR6AYun(?LGK(h+S<-m zA$&I3b&eYSh}*U`irC<$Wx~)GI&1diq~`sZDo)MKnZ&-P4`hW{72$a3$`F%RXqJv- zjz1};tPpiI*#Ye6cQ`*Dq>8+CZ%!qjk$&2bS@#LxkeqCJPE}L}X~KQUU(6FPSuzXZ zKD)>w<}eviOXd#pHtHZzez&@a`Y0x%2D@~+L$O0BHbceHj4pO4kNUGU>>yzOG}Tj` zP_8T*fW%GBYok8wHk266s!S{rXCk^ZaWz4gCYDK;-0^_<)?iXKVv*#eEE$~W<37qhQlAIP0grF_*%70i!KA55zDk-J4Ul%D>%8SN;I`E35<^xL9x=uy?*b0-04$A z#i@SpI30V6x3B4Af{znbe1D3nfOhq87T2_)dpBZ)hOXphF;Zr-F@4*B{-L_YqiK1?F1;N}w*#i@y!|SVE&J^*-tY zg+|^aJQ54i|E(TAbmwI;^^Bro(#}454u2WP+za8f;$_@upk7JXSK*FaX`X8*f*2T% zn@UiU7VJz6bu$pZ9B5G9683f>v{7rA!&mD$=df-jwHXLgkCm#QwA90b*4w;9HRmo- z1gR`_iqeOv_&}C6X_zf0;)+q#2Y~xLFr^<=IjSPycmm5uvn2FD?JoIgKko({QFZVO zpvHQ{86r!X-dL;&+rA0J(fm_nIKJSMeK?J98^t9R%?aRdGa2s; zkID9EUwNCyN?mD!NwNzw$<5;>QaGL=0IPkzA$dCGDl5^4HG6uS%WF75cjQ2Q*OA&1 zByTf_p@zThvPeT6ja#t3aD_VFBdQ0e`+1=5l<`<)5q=ExS*#JDY`jL)MPN0pRl{A> zDQiP;O8;h$R{NKC0viXY?`;Cw;ufB3|M6Zd(pRy?V->Z{AYUA#>kA~-xc3(qw1-!z zH~?Pt!D^`1a!NF07$5*{Fg*5*X`I!^c@8U@1{8hXZKU0|m4UyM(KLY?`_xG=6ne~^ zH>xE&?FLMQRHb3ByCMw>aL3T?+Jesw8d<*uG^GVDvPMN|=Jb5J+->0bdhM-abH~_T z%woLERdkD3k~N#xA|3%$f~D(D!(Y}UJ)T9)x=6{jIb0Fmse^E&ON&_4remQ#bJYt@ z17<1HsY&Z;nAX0G%`ZfJ04H)a5E>h#lyQoPqD}Hd#4`JRM~7ML1%Ur z8_l6s8oD!JRxwbym|3DzFum+7#@0{3<0TL@F{dnPk=Jd8c|~ZZNb=lvkdxO=XBE3V zg6lgLFQNrxmNdgGv=Uh1va?F77@c&gsK1|wBVdZ<>`}{Qx@F}Q53&i6e5dcGgJ9OG zgZNHgau7&k5CeIGh+57c1gj?p@l>P1u*MaTV3rJmS%ico2*?n4#%J_b(h3-HL^Vhl zOS%T;=N&eAZab*Oc4%piMCMnTK}2|X_S#kNIE+;79jf8K9)nOQf#coJX7DV*mzwJ-0Uc5G^W^OFFpC*r#Iw*v4y$^LT{3EO zliF(@wxh07-5hltzV)b0*Uck4%w1^CneGIWIR)HW3RiLMeQA(Agz7#Tjary>|J0bj5 z0E$p@6N(j;YkR%zcN_3kP9?x+o=(Abz6w-H1RP?@hKq>EMJ3_8fNBY34OP06zcCww z6^%uGsa~z%ji~2=z5(B;hJrJQu?WG!ZTCQ*6-u(0e6}v^rY3B_54(xg@-b6;2m4nJ zf-{7GxrLMGw(GIb69Cf_(-c&v;ryjH`-+yHfS%w9h@QJ)#gal=oQ@%>TRNnkfK~#> zQ(Xv3f@reu3`2`R<)@kaJJ-{~o)TRh0hUz*hpL+~yXIC|Sk*cvjI2P1nl;m7NwIY{ zV|G#!hXIz<1~#_pl&r?^tJxfmTdl7l@tSxsV-nc8PSH6-lQZbPrq7Jew6h;QrZKj( zb|#Q|Q76n4e0w4|Q>30?ktAlbfB=zM<}6|wZW=b`gca^H-l1vU0E5W?7?{vZY<|HR82D?qL-QML-1QYGYf1U zY#S{cD-@lC>H4onw4FF&JAXY!QyEI;&^k)$tL%yF;xPSN(9D&D>F2>cV0Zfh3PUgW z1u|tqMUW1^RU~}=TOeqoC>h~m3X?=j8%+{zom9$p_Hj?-sOiO(FKi?!HyGjq?H)KY z!lzMg9@<7N4B{rimT2r?a7wl~Q;rzq{s$z!V z>JQI}snH%u{_SigT=Ng$8O{StIAR&hr%lOyKmy{XHGu5+e|#O?-!g=g+s?a5d_MIc zIP#X;PRQvkdV~>MB!M~=3?xckru`zyot@-jMY*%*B-fI|*((_RzoyV+8p8uXHKHrC zSITAd?gNhAP0NYQRR3`+A}?Qc+QS0@ag-0U514LT1Vc1`D!PAD_aENUNY+|O}Nwr3VgaUBheAg;NXIP0PS+6BT!{SQ#C zr|gnwXEKxD&LnXoZWc+5XN~5hW-ddJ3&h0=M(o|HAF&H7mQp z4Ss{Sf<*#xw&D<045~(II-y+B`c2!@JWLPVkqgE}N!jb^-f$lYF8DdMUJju1TXWPU zLud_x*dA$34X8_3$9Z$Zxb1A?Q{5@9xIU z>}>k}%!9^ZD8jfLII&tg=zS{lu6N(3z<*WoI!LxQ$pHf=4B&D=Pw-ncNLAo&1OS^X zAZ>^b*fOZ}iA^R_1c$Wd#E&qfq@Pl*0xr$Ydgm&&CNS=(3U_nJ(*{zL+ArFmLausG z5`l9y7{3S}h|rfrg4|VHhnHgv?l4niu{3O`-gSEQh8nOlWMwRVOj&>Cm8gR((U;dC z%N|56co@A#0dv&DYOw!HXVt?jl!rhl@-QGMS->6}cf{!#aS^;7nL!+785uyO5h_-a z^kH0sPEF$Bnp-4+9;77VG8-7Z2Xj5b3cE>oWE2#nQqCh>hw;wF6{Hh(7(a4{A!h?; zj4}nDb1(%>?4qR;Ah>luW(t*gQ(#k0FXHC0@-+3?4zoP;i%srqX36pxmswd=+axn( zdA8`~*}_6tp1d`XVmF__7H18%ROd|~0{Uk=5^KO@-WuSMy9P++uK^yp!^j8pzhs;h zdE;~d&1MA9juw?*Gwbm7Fi*treQy9IZ?H#sav2R@ajr*{(pK8s8V-Su>W{4j;X)u)jbT^UM#yJfi84h*KK(iy4?nf5 z&8F+s&JDUC_Mp#XK)~q)%Syr<6Y9*l>(HKB@1k2OphKOa8T1B@;`o|F(AVH?4^dxn zRpqynvHeAV`<1;&!xgFonTubxp1^F#wY8A9(1Z4;(s===3YZgF@T46gK2=Fx^;fq-bGD$q=b2!}x zh%Q>)&nv3(nhLxzi#th#VL1@7k{uoZS`nNoRuw&dv+6OGF8N4Yf>{AgED?K~Sw%j! z-gT`sy2yB7l;N)OOT1Uj29J8^fQGVUsCq;VAqLWxA>7K_GUVY8bOo7`p(;GbeXA`) zh_bq6=%hTq$Q{RK^gpM|D*qLDea(s27W<^SfSpR{fV4rz`ymm)uPt!WtM=~|6c##V z)~_-~#FUB~tz4}@Nl8YN)(z|R_xgJ`m2bXJ2Nt#?CkR$+ekWp>7H8J40tDnZ6S(dL zpGu~IZ0_iA4SriTPB0Z~M*T6qMWy2gH*e5<8*Rs>*2c5W|2@ayh)kRSb*$I3wn!_&j ztcBSBU}j$XK}1b|Fk_;2{GQ6%^$yzn zO|hU2Xqg$$-+(VcwYv)nT85jF5j$C5XJo}IJjWS)i9yJkgMt`?ippX9nUf@obM~=g zhtINKrx%p8rM;odc>iYPk3tvE7xc;*X9<5{#&b8x!puMVYjBx55L*Z~OSEJQz!o9@ zbN)a2w|9dX;_O?K=`L7-8~=W;gdj1S*8lg5c!^IUxdydO2N4{9o^EsD`16kDu-knn z*v~s4`d~C>RzL43PYrrGypP9)=+8lW%Yri%s%%H_b;+>Xh&dIhQ{DjI;8U@K7K5m% z@iOj{D0xBNZ0e>lFzZm||6FHVF_>u?x7(Z7*JmVZ+!Seo9(mA^q(N$wi7otSEReEd zDSR6O)7$49onfMgXl~xd4$(!Dr*HQ6HcsCHCd(==S(r@wnYC2?H2*xsq zmbAZ1p-BErwABg;v~`jC!bRrT|;?)Q9t*9{rYj9GOK~i0b%8aY&lq^7*ZL`Wo|# zpVluJgFRL21!+&k=}f5PeWyc1CO@W4Om;=1r{0o!+03av`GH1H{f=$))Z4$h(LeX6 zgNO2mdhj3eUj%WWdMG5F+QYMQB-G{~^v zoCTdcFn{=uF6u(ud#*|7L14YbHdWg! zR4rg`>b?6KYauwJB5b9$VI1>?#VhOxm?ZPPoY+vtHl)SGcu>gGE5yrO<~aRvI(!@RdmHP>8$VJS#8LWXym)+K^6?PV6UBm> z#bS`_k>5QhBczNb@=%|%^b!f#^ebPc^PU#QUp&IgAn^XWpb-`RM6gusQdfgqsQQP0 z7p7$fQOOr*Jze>*>IAlY9$Zwa17a6^l(tq*+i9Z14%G?T)e71rb--ON@Fr6Se4@Zf zS}}L14R8yJ3NhaDi9d;E4l&|4AP?AH@PA5E&QCS?1lUj*9fHDWfN@{2FCzZkTT+5X z6G@u;4JI?!4c6uLf*NGWR78YeLl)_Kt038+r9>J{$!N_$SsQv3L;=IGQUmk6k7@uW zs|iOaZBq9uuzzqd`I19m_; zcf}6}p_0XGI)l`i;?y==G@7KQ3^C8Cmq8J{Z`|bM(?T|*T@fz?{%`9yowP&KcLS-s znMNhgQB!xGMIaYptQHg#Yzh=4+e+Hu7nnI5FZ1rU>f3r7=i!zoU1V5%RFYTe+CytTU?~g`RPWdbPZ8Da@0XN|bj1m{J0|ENPc|2Q&A$6dBwUi2>}C zK$LYr+F%qAbjm=~iM!X0(;Widc6`IrW3 zE*TE>F2#*^q3{za)f9?=&SIS?z`J{@>oS4{;hxWBT{>TgVA(zZxo?Sr^pY-=&!bof zT^>T0*Ga^wYSiSCU7Nu@o&g8OCB+jkV-~@^YuHa8@6!o)4v&M>rPmC3aDUG6-CF5~ zVXa2O1cJi*o4^N-uVN8F_7|OHCdmg#Z^Y+Tg0mG>1 z`V^flqw%tAEud7}1G?KIWx{aPq%lPQy3&iXgm53(8=dM6{5~)+)idxwXTOQaJ8J)e*Y$Q`Z1H6)jALJ!A+`7L+ZR) zwFO)@zDc!3e>S{XwOJS?Zc=SNT)bH|fKBgtzkInuFv2A7D;+0>ShVx+%RX!^c(?yo zP#jy#d3?h;_b|=E?F~Dd7RO|2gI1MJ`kh!$8A^$RHA#vIvrGeYo-p6cU8Iu)rp4 zLfX+znzpq?oFD8GotcIs;zq*9nree@mcvv#89D!d6PDYH@MU&t2!u^*EmJ0FnRRop zGG&X=Cq;wY)4`jwM^ux9loPiHjd9+di*umEUZFKC?78M?Q99;!|Eb<+G9W7M26vp+ z#K#?pE&>e(isY%@bXJFY60ZNd)S#{{Y{87+KjDqEJfzsdG%V_MPv4r>&>;R{c5{6khEmWd%B`dpQ`PunI=S+6!mTlAXXj<&}P-?+E zjDV?8|1rc^2$z@i`j@0PZ|`x zmjToK{6(Iq6aLxr*RkiV6Tq!NrF0c+o(*!;hFFS^1xFeMc`a4dLJh%h@~-gRDpwu;)?*SV%qyt9B-)j<;QgQr;w=OlXn0EfZYtP2|TpF`Kqz>yYJvRmV#>mzE|X-M`m%YgrLbR<01c9 zpYr-SO1?=p=OJIt}k5Nqia($jwE!%!{NY zllP-}9(nL4pXaf4pI_DCfkkj@K~)cv*bL)-26j}m@tk(5&_oqvImSGeLZAYnC7(aK z-YDKxL|dyh`Vl6SMx``D4(31CS0r)77BqKjh}!*LRJ+$)uFPw(M6@X-jRGRGgDzGe zSgu(kJL0NeJ#!TK)iXz-kDqzVDl>1n$;|QmCNoFQ$Icx2tIhmJgDYl!&m}l}V9o0W zNJ$5x7f&QGT?&zZ*Wpew>=y>%Gz<+0{}N10uEObsP>sfQ27WQFx^$H~(7o3JPik2a zVtr#P@T(PAZS4rGlIR3{r3QnoVFgfgM7rrvEvTSx+OH^n2rs z1__ZOai~kBpmL(v0aV8&;V!R`ce}yoQur>c=ttYqVJA2u`tp^b-a zv_n{;3LB{E33TxUOOQHCgl(sd40cSW^6nw;cCG(-7w>`Bzw9gEE1&ocL@5|iH!I+Q zW@#77nu+ox19}5_61j0S7Tt*6b`$G-1Z}LvBx)%kV%OI}+)J>rH=v_PV-Dqe1W2Z{ zH|n>Crw1vx*as)?h=3IJ@H%#oM245hsGR~PQBZ4=VgT{_Ii8+&sj6lwZ{_3Ulz}9b zRq3iIVTq9~ah`SYPQVJXbF4*T7d-%6+2m^_!q$wFxIa=h+**&f z=<;KS=$ZTTtpWyWw@sG+<2vPmyVIdDK~DW2w^P864$L|kx9R`5lWcUq>gw_LYoBQ@ zru@&F3RWEOH)XNYxsyuD@V0MrAm4JStQuZkW$vRZ@~E;JswuY=Z<<%T4_S^6v-SBU zyRh@!hf)#`;nN543346X_dbzJHjl@1$;VQy%~)%d2BDvkX*`Z;Fnmx&SE(7}*3V0QA4wfb)XV>3Y&*Y8uxea+kTvpx<7?sqOJI}2*wk6y94NypN1`F|m z0=+eD))ZMI-Z5TsneI3dM79*?LVJe)!$}8JZpL{?=BwA3iq)9@mSUJW_?i<jZ^R)=No8_;5-j)_img?hNw6k4G?~Hw~mVn zs}xb5|6^bg^x2{g3fe;0^-YQDWQwZEqfkvwbB~V(V+^`F7K|s&O8ypzVZ58SsUcx* z^0$ne=ha3`G7zo6(NxOwxwV=352is$;yXG1gXK6u<2IX-iTnqD;gx$ny%86Dit1PN_utu846dy;aCV7- zy|vlB59@CQ+#xYoPrV1Pd)zrT`A{)m!V@Pma0ZA19QhAryzKw0xnH@*vz^o>HBURR^Xj?Qdv+k1%JxF3Vquka1REn9Yd zPTwkUMg?mCY?5aNVNO%L{~PhI?bT(78W<6S?bRDTK2W`tiu~;paCzT{*&8xaU4?@` zAQ91xYKVG_Yntu~dv9~-Pj!~!NXowZd;0|Lg2R(K`2f^E`2bT-JP6bMXT1Wq5ex<& z`=q)Eix&-zIK&T$Pz1Zj8tJok1fmIaj!;(wO9H&5;JpwMNiH0hOOHPDN5P67t(L^fLgnF^^>{biqZrMj)%+dB z$MAQwKKk)15)rBDS==abC;0GHjld8myGz9epJSSsZYx0^>wxzM=AzkYEWP-~6Fh(}cP=4!mW%jvvk0@q_x zs4|Rq7cO#43+YCNG%bC~X zU0}<++IdM`(4p&0-;WP0vF`+iJbsNgF|eU3vXJPqdqufs%6}$1eLJ#TKs<}(UKgCH zR_nmK_QYG`@n)DCaNp5|!KZ7L2gis#z_VrgLjXMHwC$TL#DotAl6Ok|Pj^7>A$wg6 z#^oZsQw!L6jhu5nm@Uw((Kh%5Hh&E){Ud%(Io=oASdrDDv*gx% zocSc?T?tIMLbn%2_q`rmqKjAzqwIOyl5?@=$s%!;*$w#!eou)V^CWiTypKCM!@*+U zzvANOlQX9!~5o)#J zG4FY_zE92}^iVvNC+FB&oV##aB6k7T=i;uj6^w`OPJyokq2EglULqA^*k`9ya3LXjU-KEXQy~^x*z*kS}ZPx({b_pe)v_~j2lJ-GyZUA$>!hZ{W%2mq+d(? zu?>O5DX>ghC!=+G>Li5n1jarEjHNxHxkCVLQ0=MZdH^SGbPnBq-}i#3 zS@R&mvZv2KioihXl+5}^@ zQcRbITXDyUJjg)d+0GjxoJ8ph&{;Wb?%8>0_#C?V?4>M{5mG*T$qRp{7>7iQ zrFekmqvUg#6l&WGKQ)82qB}Cp6Bp=o{8kz#w(uZ8h@L%?1tTVDD#aTP5fK~# z0dt7S4q{*i|42tVEzcOF%z2to>ln#0TFgf}GS=B-$Nv%I^COoa_{PcSW}sGN_xPAC z@!Y)LsavKg)QqOCxp6c6=Sy5AW>o2UoW5{P3z5!e6sb^ZW*xrf){WfGud<2X09F$O z_{b4U|1~{6-w8!9=Ug>L1tUo(zGe#M3&K7S#48vAbFZaxIN7NjqG^d%d#rGh>A|w^ zmN6}j7sy}RF1F4bor$lgX-b|K3KsZvIW$#6iep8{LIHXC z7frX}ym5Y?qTq_x$*S{;vAoWs_W*jp3%KAI)SSg_{6WBr@85}qxZi}lSVKs)^@*S} zSujH)>^qiIb#!^1_CdF zR)fdF+dCYkk2kaUakAm?2Ir#iI;S&>MZ(vzz6Pjpo|TWVVU2!zq|RvB!zJVWb3?8{ z-$0~LUW1;14=Q0ZNng}m;=(L}%f`YhaCHg;4ZxNmv7kL+WkzaPFn^C_oL+QFx-6-mZ!bn}Z?Z4cncaV@S zTWHGcfZQ#)TL^*NEsM87I8|3}ySPD*P>;uy{~XF!hG+g4UscNW;R#+%#JYqRxw+!C zw8bwQJgAVc8gD~``ZlN|&ldCgNU8xzAMzR6}&hK>=sq zHlZqIA36^t+I+c~q`Do@{zdWzlmBuBH%{{ z^~jF^zIz%$b_71rQD^r^CW7AXX$?Kw`tDj$oJUEZ;*nSxw|g4o3^#_YhpW#A5I7_8 zcJ~b|3?OJ`zNZaJ(fHbqzh_C-#%6zVUcbx{r!c!VkCxCR44>5oDB z&sQSkqVivbNQL)ud;xzh6(V)15Ut>V0l?UT^~yVkXiI7&b%7lq$KB2 z&r_+H6r-`ur!J>BUwlZ_agYXAmx^W_QTtQaeZgB980VHq3zzV6^un)sYj1ckB#nm` zg)`x;X-M3sQ+N{FY&+{qMX(vA^KBN?da)T}(7O)cQD?G=J& zVieIZrpZI!dH`yTNxb3_r-!K!7d)pC5E@?-kSUotRj^#J;a%@`Mo3uq4M~uKiF)Pa&x^VDNY*HHXI>4&N@ov6ul_cweB{`Bnv9#hnZE z_e6*T90b1*QuIMJUND9RD==-ee*o>DL-PM&?S0@Xzp8uD-|w8wIR`i}Co{~z%%GA4 z6)RPYT%{HhGJ_53c*D(*gk*>zh9u0CC~ZhG>S&HPBsnwEhP+b6mU}(@lztxfmDdkX zxh=gH^+ajKmcGkXs=OzfqoPHNmKdzGMf<+rwg3EnKOoxn-7^X2?7i3ixAxj=t-bf! zYbUVInV0B!berHB*}AWxE{AJDVIw>WMVTDE_hwKwmL>PAm6B1EmBEe?s<_`&kwFze z@crlsYs%okcs%`nKRQG_XQ+~~KQhj`+HhTDN75tto{T5*Ui|s1J=B;N{WD*(St4=2 z;Gt*eA>by^HHmwJr@MzZcF*L6oQqItFNk!?&rnT1Ghj01Vv~A^` zCh45UR!HFG%D-b9v9AdDxr7@6wj<$8z*G^ge!sX72jPMuUi#h!?c@UIf^HRjM@JnM z0?SJ87{it$8e`bTxhfc4d1W?uMv_d?$Ut&V9@Jxmm0&&``o27IHGcbnhZ!5#_-Z z?*;W+u%lJ~@e3ZerQslhzXDXeb{d95Zw;dMT6`$6C#Ls-_Oz{C+kZ&{lc|yz}00Nx!DFh_xIBEm-=5>yo+8%LBK;)n6`l zr(xOW@g13j?R*7l!W zCVS1^Xa;F?dG91dUJL!nwPbzmQ110FzcvH012;h~eAUMl{j_Npb1k)iK#Z8218FP% zVyxsTytM@Lb}V;Fp1O#iZum^wPp;C4_zu-x;C6a}cjnGv4ZH?lhy;~geG9q}Hj)H^RNTNm zQNewvKv>6yKTYR#)3HTsE)!$}rPf0stH(m{UM%df86wLGMGCM*?{Z1erRqjGzefET zKBPQDN)o%j$f<=_o8q3*EvgC@)ei!*u>L_F+UwxXGAt0lnD_}8yH}g$K3_jxXTpM1 zuHQb04A>K)KM~DUz1n`Vu;|CMt`@u~{R_k1nX0OnUE5Y){qn1ay?+l!YSjq6{OVT@ zdv6T_@8?sL>6$k@={?~gGE%_b^@0V z-tdHJGQa;7oy}tfuJb4+>zAdsMp(bfrmhkQvD^3<-yk+b_2yRm-~V38hzC>&31Q zPAnNla*1^}$mCO4&4$8APK64Kr(T5xR44-J<&~kz9*hRhMuS*ay1ZDN_l2tJ$E#>S zYEZYN2Q9~q^E2U=OLEW>X2*tZI0D7HCsu0vaZ(r!-X9G@=T3>gPRu_u>6#miZ=W#= ztTBJX2)Ku*&>~n(x>mn0pjdMOcI66(KW^9?>kA0*idO?sgQjvWk&}Kf-Sem#5Q%{0M_NN5aVO80v|-2&+esuQodds*@diW%4hF*sV@>?32lF z4Y5<5>^MDxV5gSxGZ^CZ8aQYe_H;v>{sau6ApU}uNOI6lB=cP<>u3hx$Q?8c$-feA zm?S$se_|j5B{LFyAQ}Xp4^P4xOu`l{Vp?#05Vy<>SF{@0aX9}U@vgw{16_xh#jtlA zShsqbq4Jt>@<;f=kheqZr5&50W-@rA#b#&;XRwzqgi9HS>OvS|Xiv+k^Ge!aL~r$! ztj}$Q)p@qip5uGfQv&Q#r}s7I2jK5==?Husmr!C0Mm}h^X<9@ec_US>PYVb|Pq@)P zK%~wHh84gke znWC5qtFmwqb`Ig7aKUh6|29W?iiZ|gXbri7ThM}JwHb%$kzK%=z7tk%!()r@oLT?` zU@6sYSN?XJtW%12eEa*g+-|&6b?;(HqIUJ&WP+GNzYndxJoZSbD!;_dmVF5+Req73 zvhHlYKSD9JCya)=^a)wlq8$68<`2%`mIpQ;PqxRO8Kp$G*y`stg*^iBzK@KMouP=$ zbUX#UGJx1jTLVs0#Aa=qfY`lX4-qUQ95x6s-F?>}gf8&iJ{O^`P`?S$2J&g^$83b% z8Jy-|9d=gMVHV$U&$cT*SSwCg9N?ig@>)iA=P6JNHer z?_u7{M1$D8UCm;pi%R!i@FP$#(FA1gMEhQMc%n`3ooL@1%0(vH-==sH;fDWjn<5kK zZ#yFs?R%r$;fcmzXreI~nrIA$CK`i#X2HhBzo5$m`Z-XI3*16mhZOl+Nxt042HVQV5rLshPuokx-4p-qmOUQ;ph^y-iTEI^eQ;3l5vLa zhAj?_AVb82lowM9b`VWTL0Rii{$p2OftUSa54}Pqdq6oCLA$uiO;1H+01?{!2H{aR z&Z6Mz0f1*U;GS{$21FmasRp(<4R*MRK@tf*rh(tAA>Ienkp@hYPAK`j@BeWgTz{PI z4fxXUD!yDko2^M(HnrRgt(NP1WSF4aQ&EKNJo;uoZxr;azDJ#t5ZCXodgKQ1RRB*c z-D)9KOlcezQ`t1f!+cDAUu`C{#r@q;LXW1V4fJ$ssT|P0^VJrVe~Ea-lEK622^8=+ zY-nGHLm@SX=K)ng<>k`KdUCj&XG3uI7De+{gX}a}VYWlByH4JrZF*IM=N~%)BM}sW zG?o!Uu5EEvd9ZfDQ?M{-!PjPT4yL$jvn*R~&}jijmG@df-=6@Bw%-B}+DzSO$icM39ir$ALT zop|Q6Sd<)?ib70{U+ay`>aS%PbQ0)m{rZtP-88#8Z=Jw)Gwrrpo& zV$$IZC#;G-w~GlAd94yKd|AfiDP{+RENpPHgUz*i@^g7F_DorzH=A&fK7;hiP`VBW z=>?=$h0=A{S~h#n(W3Z|0?TF>;Z%gR6yCS@M=?{-X8p0~YQ)qOB&X|v@=@*x^OnY3 zTq0*NP0$scEtCaUGXM+vgPCs%LfNZo)=Ut3g%VfvEXzn(47eMYUpCGHoRYeME7;g{2(R-rA<#C+Tkdox}!aQ zB@dcG8=TTsje)D-xp+-Y9CAd}w8#t!^?AIXR}(I%)&(|-rxalw$jtuxfx0K1_v*^bDM|(}xI8uQ5D_jj}krR=QW@rd29=+Eo z1lJ)y0W0-}c*sX5cy$<#9Owd3?1?yjhR(*DL?Si zfOJCqc4PBOfcEy7KysXj=E2311`_4i)c%p_ao{3o%|~Xng|ba93~?Va)U*N>5ImQa zgkP(NKWO*VY@3SuYGBP9!R=aa4s%6U5&P+SdikPzArhzH=TtU6W!7I)aY#ihbL3-+ zH_&`Ba+`8#J2tBj&5vZ!e9ZD1ga{q3IV&id_jUg)VP2>?OddEQ%>a7IXyDmMb3kdC8T-V=>t6uKE7mD$nCGJl%y; zWSj!!XvlbX7{kl~o_`krk%Vg?+WsRfB(5xSR{(c%twJ^FCvey1GQ8#$=#7~^rP`3H#3=qDI2^92!<8|6PVQZ)X)k%iyY3|RYuG{8HK>H` zXlHvrwit7C?aH(DNOC`J+eu1~^e{++W>Jf@c{CGWt2~MYUM;V@GU#Df)v-<;1gF=2 zNUjW~km_FO(Lu*c~a$X-xKI%HC#y{6c)dU~}9;bg4auz_(>&)(%3 zQ%__1ogP(~S))pTF#TN30Ek^_iWE3XTf8eRO36CIRXny7^?u-ufnU%9QCqo&nz#(@ z8$pN1G0eACu(DA{O8r=yVIwQtu3y1kwe$n_N+7JzLhxflv=O!+L%1Kq%uKPd zsp{CnEvg!9f@cN`+}97+0sR*f;5LZSt$jz`A4B31GK(YmSo|T-6@mD})Mcx}QUyB^ zczZ!8_e-Q!r@sBp{43#>4j(g2zmw^qB5(I}eg}6fL#wji>E&YEW&T(}-luor-&xYv zVnQAGt(v+hZ3!b$btE0BRcZ?KBBCbcFI9Oxh~-%*UYiz+WA&S{umk6lI4&H`BKB zn>*s-0-~q*)Wv2He0Jb`Tm}1AiXy;H_W{8Xlx1@bWM{z)cSbJ*%%YY{1$< zZllcL79ZF{L=)Gga$!770do##fB@3s*%b9HbSnjfwseR&gR4TvpH)pXk+P;`-g-Mc zM^R9gl=xVcCrlyhO?EZT18?Ao7e1=0cqs&bn?c8fM%nw}gLj~i?Sy?4>Y*}i*49?9 zJtA>ci9LFQW7o(pZs76~&Af8{1l9|<1v6>%kjeNO7Z4C^l)%>BhVbJ)n>EhUz1y+u z29qVn<~FjnTn)5c0}VPE_lq&utO^(mSy4836+*-Jr7Z5{Q|%lftJPaErmtUGrn{{H z`ppZe*I!!YmbarJ1_P(b-j_z(5Oik%D4k_m=J}=JU@hQKk*}e@stf88EGMz)3e!|g zZgYuZ5@qR+X%b<@2^q{tdtlsJ;6PZ>HFMcwG%M#E&I)__iNKHT|wz@W^AxfQcUQ4#l%J; z$fxV{wF|6}UdtHY0N06 zd*)|=crF{yEGAUPTj*H|3mw}@Nrc%m5_#69KQ8a@d2G+^Dg`aPTP-X2Qs~Bme>?aR zNK?x1RpGHa(m%Y0l%(Kx5wAn>yc8b;@%z2xvv`iTzbxxr^vVwX^9gY#9|6dKZ)C9g z3=CSm-&@UqmSkuRdqj-TpABwwnR#s6f;oxzQ90T|u*rJ^0ddWCgGpb(*2zI3B(Ygh4VngoH)P9=^O1IY_yju#eo(QJuEj3x* zPH|IL!snym_>~M!GVGqiEs#&S;6k%!!h&v#?~jk0#r!MdS71r<%B)#CI(hW*MR?&j zk_$;F#28NF2IVH;vKxr#&T?nP`i*Q$8@iZkNhf7Kp2dY8+>GrUC9Iz z+Jr+ydwr8Z;z7kTQG7RE8^4U=uiaZb(pkLg-Su5u;JePbWQXkE-{Df?f;vsdrF)Ug zMA=|CjX5F^Bsftu4apIr1^l(;G^|VnLfESz{KD3P5*xN#hPk*A1=btBurRnckH#d4tB8 zR1P=6nbK*4l9`Iu~ce_Um8livoSsCr^ZJSg3!wtZaV$zqu@4@(FB2#*7yU;zmsNka+fm{!t0 zF@((=EFS_rgrFD`oK*TB_G1s>R16i`R)KltDP!hha3;fI#&ayV?9aoqC56G_#}xeJ2=`pEogEjQHxZ-|!QB`zJ#M0_n>;$D#|)2@ z=E@uHrWQM(&UB{Un=qbd7E4z30@(1!;r`ZyUib#*1Wz=HDU*|l!;8460~v17Wx`Hc zJh?)eGJ z_Zd_T*nD#f{arq=8rsnapc%KHv_d*M%0Syyr4Gl~SG%#(kbZVW0?FjMIW(3m+9)xK^g)oiaK1`pkr)wWAfei7T zhBA-=Hm^Gf&zY(hOR1^_o^2kDRNMH|ZNh9_5J!r*N*)bbKT_UlooZ2jI*M`z?VJux zn2s%j^g&SfV4yMLMD9XxBF7BoWBST`f_|}13>3n@qcZe&`Xn#R9^Tqa!zDKiZ*7eX z1A+$)1EE60fS?-&rk|yp`ak#)v&mD>tuxd7!+J6Fi*=mZUp&>ZK|2*1w(!X z8m{e|=!8CvuAaszt++ZNG{{e&wdziU8Ek3jesvnUBAonTXAjWOI0d>tIFGq;gSKD} zOE+$Ldx8gHiBKUdA?UDVW#1`ysnY)ZLXA4r0NM0nlV7E)LsLD%G18eAO*)7{*fBWO z#*uPIPYi}Q!l~YU!GKx~7Ta3P2vI!2f`7@3&=Rjn>>D9M%LoA)_CgvOBP7uKMo2?r zgyuLxa~c{WB+wWki^;ZZe|SjRa*+{2@SqVQRA__{+&4mP1)iow*hyPKvu(>7B7-1o z`@od+esz(XV1i-kaX}Tq=7r(gO!yAvlc9uJrRtGGy zGsmE$DBwvo23WF5V8&E1Yw8nM%$uBcV2*8g`@u)eW-<-%LwK{LO+y()X;qEDZeueQ zS3DI-w0d5!)qZRSP!=IAkI^0fy|B*>#Wc0xoZ2DxSTFqb(*OiK*H+Yr6{gFO#VZ7* zBVOqa9?`(zL2dnEL+~xxG2g^`Jd+6LE*bIOi1V)6`pOV6VnX$1Q#6Hh%YtKrnMB@; z-+DDVVdm|8-oal(RB>ZCizg+~>IG~vBSha2_*$X|3@*NXYsCK7tKggQXa0wWLr8d;dL`%2VXEK!-EGr0=0^kEBPz)Se(NLFO3-C$GRQJY465jomT0> zN6kWUHBU!*!AP%cw&^{1la4SX2bEA)^vWg{B3uR6Hv_57Z9L|-GVHH29xxmZvNX3M z|8no3Doep%fuk(MkWE@b`|?P;@zoIf$Xp0)wW%TE$i+j1ofzU4G1R^3aWbMj%0CF$df|Ivf%&li;UNwVWl%B_8DO_=8bVYxrU?SmOS zS}k?rh6TnOOCmMJBXn%GOsUawM2wb?SL%rI$_bFUny6ag-X|bds%DO}jN^+Zbj0F` z;t50)Ph?L@gtX6)Z=|iJd7oec**X&bt)Q6_REgpt6 zOfvqb0M`Oq6%hE!CJ0GZkBR@8i}e(aLGV!QktPo;LOU}4%*8|TN19lmjmU8y+hW1A zWw9RzryPJBoZ#r@=jrqX8#5M>y{&jB@x8G7Aq;<@JiJjQe+^~|ea2!C`~miC zW3WZ|DeYjz*`cu9@UEU;ST<+iy&|>(!S5GFXp(a)HUnsuNDH>$sLd}-*Gu)5B{BkS zcEQg#&g(rH)BDeNU{5kcsh{tJDj&ke=euC@6yBxtKi`XIA7hWSszaC!@M)Du#Dc?C z|MTnCtRa=S>^+m>8{4Ur{Nl^*sovD(#}G?L;z65w!F)rvuTX`!dJF>ySvage3;B*k z;aT2h(i_d-2c}2Qc13&eY|oCLc+}1-3)I_`c;DW*i%WdgmLD@@&*r47E$R^5P7w(< z_woW9VT2(+iw0pc_$=7w(P%Ta<)1^#=|&qWAax7V?$1q%TXfCXO!=RKwt+_naAyXd zqcmHar%H0x*&X_yTaDITytEIWpIe{t6Cc=v(9i9{up)ej?k`}roFpqd3Ex%VVf3;U zpnGA$(hi+9*3`z|3yP`jioe(LB>;$>DR2kOO*)3CI1J(GRM^zOtBFs; zl^K6Z+@DR6UB3(L8m`x2Y_l1kQ(|ai(evkP5Hm8WjspIyR!`auT7?b3z7_`offnii z8MMf7Fb~6}cLj9vXAu))|IcQz5{@Jz%=02MJdz&b?4Pg7(EXwemvv?Nsd=sqa46J_ zKUqHunhRLjr(2;8o((+yN%*#~hrLg?LZHSUS@hIv0U9j5{7(-KhAI5htCWC_a6Wyp zRVJWmD1#xM@o9L8PqhVy*ghht^f?xbu+-&DGPcI`(Nhk z^<;Ta$YY~vl*A)vn^MO`+3iZDt-2O@dBpo+xALZNokh(i)=Jq3yFdD)My~cO?!6yw z^D2#I*skyw&Opf}LDj*s5jLT)Q3Meb6x{H2Lw$(A!6)*~*iS+lzC-0^K@ys%OpAXw zfpaz9f7&V;=Tih0$=(l(V)qpeBiS-Rwh))I+umOPG8{34V(Ur6;cz(93pL^e*City zuJVPZnpkT{4MsSUJPf`Eb=wfA_X5L#aPVFjht)aO-T@|l)W{>8TX{l_1ftXJ{YR2; zl?b9dVy^rna7dbZx7;L@7qbRvKZ3Z!`$4K}GsX;7vHnqhRfk;#f4_nuK>^;&uqeGT zjHJKM)a%>44i7(?f&|47YJ&EDv<*uU1vcXV{=T_k&jsKg^`!UoH4D;J$ zrG2_$OSBtn!!fRSU5|d8A4c?^aG)7@k`D7fi{KyUX;BpkBYQX*4TuDznK{{<{PT8z z>{>A2S}sIG1hdn`Mn5D-d}t36(Vr%uDqpcv2_)J_DZJ-LS`Nb9P8Vn{V1}LvhY+;W zZ*Z6QVe}8BB{;>~nY5yZ>tMOoYtT7Ov$(|=E_L1xyg19ze}F0oB@J%_#1?s*ZGRHpsbUh|2IIaIN6Jom=GCHn zk=G`)2RjXo5dImS$~^ekZ2t)MLl%wWC=kx1c4@xs?{Jm26VEa!i+{+hmygT}jlyOR zYDK%xp*k3*!uvt&$9(4S$6R;u-cCWmaS1p*8X;!EHupb*UfXG@5Qo9=J3VKLig?~@ zv=s7>c`jP8?__r;|EbWNEc;&X`|-xSVFUlP?IGqK#-N2Za#?||y)N_&lJ`$*HT3XH zE?~ywAGb&_56g6lKHB9yzd=5FR;^WyV9o7S+H#*Sx?MyOou<0oL1N9xaLj~U0G#{}daFhGGakFh#r&l>}vW`2Kt!PT@kGJ{UqY~~lT z$uBb!`)h+)Y6c@Q!jV92=2vYKe?5lb3+Vw>9h7T0dg5}E_%L1k0N8z+3zQ5u%+aoB z3i_LYMJ+^85DyIFR*0&Bw+`y(NO4GDXWLK43wrC7$1?;(tb`3WpKBu0ouH|zgOOCU!?TMtRD_B4 zO{n80<5Vkxl9`k_R+=qEmu5@nX^0vxrf!4t_l)k12Up>0~xLXY9c_suAaOAm?$geqkNh}Y{;}6GZref|UV*3eTGzf*gC(b$I4@2hdDBh33pIlP1<&vZH z!ZM0?uOvK`T;tm@k&jlr;-S`bV$OG?8Sg>;Fd}*nT;zO(4|1=^p5pcG8G8Fi8eGBS zE6>AAJX)Su<=0elHfhB!IvgqV9x3!56pAj~gVNBJd(e~+7yG}`U>Mx6JA=1}@U`P_ z$@2t)JNaWu)PP`jJ+d|!G0(Z|z?CGK;6e=f$ObLpe4>e(x3i#_neuqS)+uJ6z`_p6 z4jRFb#pEa8G30}2r&ZHGu`S^cSN>#{8qD3QjpVs6{vO8Tp=AgqO%V<;%qJHz)6h0@ z!v1S8&uW_DAH-<3odHJA^ziroLf^$g-?cv;`@=(Md`Xg?!VR`qjfW*SEEDjQ?HSkt zB=#i}-AM0eI*n}DQj>iabGZ)9g7V8|!SmNULCt1L&Zcnw7YcUijXaXP&ECa=(W^Si zj-5X84x1hKRISoy;16R$+yo|i6SjG96AX*3fJM445eTLbsuj!*D>@An;+3JYn$Rgo z{M)&o)UR&R_t4qH5KJp>g|vcI;mBJTwf7p{;a+p=!>SuRtkxe}3?RfLme;zQtSTEy zycV@zOQl0(N+j=9S!0cZl@9#5=}T5Pq+K9y;x-#UrYH>y{bc~y85rf#NK=(f&tb6R z&%74H*${u`H5e~2J&q7Q!I&QQh`+O{U5t>s;tye9yM*cqXX_%AxE*hoA)w~2TVw0$ zG@A5&Lzt^130g6eFfeut*h50>id151@sD&Hou)1rXkCFMiCaY`p%g28k-&3oJ>WsF zJ_I~MD8p+VaATcXhU@Y)1O!4SIxi4XhM~p}P1{@3rh^~KvuWqCsSFg7Q`&e`BtEwq z!A{*=_|R-7JQopsU!bFddw2#2|UjK=N!G+vlW*4!Ti4I|M8Q z^sR%gjkZf?3#O4Ru3EDKq0`6ns((17ks7RZ0mB)jWy~CMgwiES`@TAEZ8GHFki23L z(O~%%E{>|ML5C$b0&y<(NN5<4;@1<|T<3X8mABQ~%qi{yJUYQ)-MIui8MS*(9;QmFLp(dab zVYb;%2>J%J_67k11<(T_!f-!m@Ee-NESl>Rfzd3|6mv%ddX9QfKtGWdofMLa#`J58 zBG);_bF*LK!E8lKwomY54+N^{i+kw zi0;c~X!$|$1!-c78zXPIO?3=HG*HdZYZIHwrcop7%8GGw+L*zqJDq#9F+0K^}98Hlh|zB)Se*O~Z3 zb%qebV|WgNst1Vf!FdW%2jPszVf`W(4n-XI??s=6Kd*Wqf#Eiv5c&|@b(0WX&Cnt^y6>B9>N zzGh|4U=e}W123qr(v5@B3@qNy?P55O;x0=aoJYK()@hfvZ*kljTlg=N;=jh8r>c7NpVl}5XJ{q=3mSR-qDYrydW zfr_95TerN|RCQ0A;k?37hopnj1BX09h%XGe=tT&FK_~d{>7yh+uhQZ*h?|!OL5Kvx z+u-UXA;JRxp1xMSkPGSr+TV_`e7j+DrbTM*YdgW5ZnV!E83-# z|3N8|1eaM_VX>72z9jlQvr6e8bLo?2tGZm-eYAyt!p=&?|^rXiA!-WY1=yU68QBxsi# z4i$ZQdsQE8F-o=S2E*DXdvn0yy% zv|7;A8o!KXQERuXj^LFWTZ@mPM~_~V(T-%va>AZhhcZEuq=$iiwZqJ(7N?%IbPBxc zl)`nn>LSF;1+D2k4HtZ-9Mia8#i+{$aLN^uO6yA53kwA{UJo^+9@GZHlTNy{4UiRO za#b_D&}s%B`ybtj+NoEkx~E=!a>|3#&6eW_Cs7k=X+J)4U_Ky4vt= zC|(}jZelrH`gCX^`fmMb6=^q_S%N9x*Xxvb2KmpxG?M4>qR~Na&BUgjbsNvUhbTx&f zVN|L0N-1Y=DPw+~dqjCa9fM+u|K9NOH`Le4qjYb0`7iOsx=nU$k6Qb_1|kT@g*dTL zjrv$2G89k!2jypQr|W%156lb4O4q(2xY-qs)NM29c|Y1OwAqWM z_4YdoMiW91vR4IjP7PtDav0+EV7RXrgDaLHYJX5dk0G>^i4pH%+!Bgn=K~-!*@ziJ z1w#i4M1qK7F$h8&3Rwj1v10 z+=}B#nVC@^n3B8EE;#L(?P`-Ni&kBWVZByPf6Qvvrge}L-KrXmbTUO|FLueyvB5EI zJ8Fab&#C?xyhE4_j005LZkWYHvG~VpdI5v$FVHc%@u#~FJsR^r!xhIOo!x1&)70hg zHrK1XZ;^Jth3l9y{Mv%$0yf51OaxqGQjDzQf`lUwnSvqw&lQySILmn)=McJ_IUbW- zP`r!6uNclBz>HLGPay_lf7(O^Yk-Tx0W~-je;izL*oRSCWVZZiJ7vTGPFt`*G99uc z@ZXk=EY5TNLT8Gh)`o%P1~hmEEQvHwl)wHr@K;gw`lD3Bc-h422g~NpJ#k=K|EVk} zh|@Gf{BOj^U@oNcxLOicndXOJUCo7z{5c3E9efW4L|Bc% z>=<7QDoZaQ;19whScLwVX6RyIri@~^&ZfmOih~`gz z#rtXYGSzQ)hqmjOkhm&4^DM7H*g5xX6_?MU+zoiY)2JcQ+(@8FtDC&fB7(!`N^0Q- zOwf#RZodW})8=Ql$Y?I88~Q*TeONU-oW*4cxGORwy})NNg>{5^G;_(O;ygE1j8`;> zK7rmRC<$E;c3CeBRZ4~~E3kHzZrioDRdm?Wfl9RNH4NPXeBU81%Lm3lBz#tmHbeZF zyq|3YVWuB}6WZ6m+_)Ld)RaAmhfGbV=9Lv-*g-O~nt}gpXJ_IOW1Vjepg-H@!GSoc zurO^I9S&9bKZ}qlB;ZDjJT?SzvzAs4?y`mV#rjlA;(iIvTI>}J#5Sq2O3;vfd!8}Y zW^?a#x^H-9K|M`uOxt}dwjvN+1QM|UAQ)mtmURw68VG`8BD5psR`3Rk8t4IaL!O#O zUYE4@+(!f5uWpzmYcacJ;y>0P>l^JiV?Av@x@ccPAsyxD;hk9CgqteQAI8^c)q0ho zPy-l;z6PRI8J;^NF~~L^!fSx&Uf>l4oMhU;@#Fb6hV4A{OIpdI5a!2Lk5XxsH6EK~ z?b-7AL(ys#I4A;_5T4oH$pxT0wJnSElTY*il!6l};`leTD55voF zJa$k5%GWgNm$qIDH@`u!S6}IHb(c0#AHH3Uz}+TPnLtzZb;>(+3Qv8bc~Xyh&GnDa z5{y}0DB82~PJIKZ<6iV^0Ga@fCr!i3mw66kj|Tnn#fI1;4Iz)6emO*_7aM7ykdER5 zW5IhMD|9IzX);GC17u4|aMJ9TZZsL38RuyHw$4mIi-q!a^fFM~T;?mPI z4IY-tPIWPu5eUvXP~=^_R*3To-0!#x^o(mC1DtdUv&I@k_$Bb@84@6VV!0D%{BBqo zj9mUoEc5E{{kT_--O~GmZYQz519E^GW*ag?Picv~7yoA5gE{rMGQVF0C=&w0)u01Q z`o`Y}K={2M2>oPD=-|HmWSLBgFtL(?!jc+RHZ%_sNge6{(yIA7NA)OQyB+0Sm&nD! z5~%o1V1U~(xLm$dNCo)7$6|o#9EzrG%De5@48;L#aA1){hj5!B9Y|sJ6Uhjp4rb)D z5o2=m4ZM)g8ZB!t%xVn$#V`U5sw{(jPcMV{| zJPUzbCQAa76mtbH*Tli?VV8{ZY-t$AjGz@MckNFhRw*F@ichxG&`8@&2!xUB=pMn; zBMtCK_gKikgrDe-p5FV3NlKwRRTX{xQ-d+ zTML?ED|4FhW?qRU3FeB;aY%+Gd;=)NIV+$Ca6h9TXrlwhNw(HLh~ZcC)rVSzGd@?Q z>0XQ89WV^_P&dV^qpm|)m9RbxL(S*6-7X)Xk#l1XvW5*{DZ z?ab(&ELg3Ev*h<@xe9YiwowkQ<*JrY7FW;7PQ@3gFFUFv@PG)Z#fjYU+jtYS&;{6AHq|j@9E0cCN-4 zHvo?fJ-}km)&V;YK0$CDO1-uZ*y|Gzx0B`u+xhxbjgC~M5zJNzk3HCIydf!G8WPIV*R!6czAEuI!rKD+f2Hn1oKZdYXqpv`y zF6?LbL37E*VxxnmaMe3Zqu5~>#l}W|UFE3O+{=8Kw%F*q1xPiv0G_61QgYoGAo&YG ztsTtN(JwWmUK?(+0GL0@6AZ=|VkcRP15hS&m!Z2fmR*AU#fH*^N)8z=UP38@F2vpV@v_Z&@#0PD(9(xOY zY61;F4=z~TO>@vLpfxL;=0YTWq)G+YRW7gk@Mp0koMn-Q)O~1OA;r|xaw?pP!7WAf zMA=RmzIHIi;neB!?Q%rVRZLWZdtq?#zxJe{#-UQ-=2BbzQ(Uwxp>~@1&L!uQLF`<` zNF{jaNabjym?Mo=qMY|Y3GhASH&d%*xdb_h2>IY@Kpu-Rl~2v3Vxx6xx@^20WsH|k zmLq6^2Ekk2qh^s8pzAd17rqe#=i7>5KA0|_(bi{)tGdie&wHp;hoz$!WIB}0`@7t%;uOwU!I zi3OJWU_hX=D!q0NEeZmrcXJ1#p|5PU9BXLqy1QdY*^e z^Q`8ADx|?dK{x4{3WPKRK!XY-7^&NimXrq!mv7Oz{}ver4sOcF(a(3I%q28HEv118 zc+O{=`*DLD<*imo!_<5Q92X3DhjcMa83v11!<8dI&>&dH7=vSA9Mml6j{~irt5~Xl z09vY8uAr8%Tv0?2F1e+hTDeI6=Yv!RS$bViGybGe7w{Y$IruavUi0i?VwY26H!-Vy zi|Gorb|6urf2r-V91u)C?afvs@?L!RPegnmEJD_TLZZ0Ozp-+cU;n@~aDp5(LQ@B= zUX1#iyhSsK%@Z9lhfX1)m{>Ck$Xap*1gmmjrTE$@&tVKyu41-=RLsqY$cM7~KGi%K zk=>JVT&lVcH>-*X$O$LXi{vZ!fv;pELOYuTmQY3cctsd`oK?z=%aH5~>V^{(E#u%t zs}D=}5$*|1a$n?v%44Wxyhg}`++d+!eQKp_Q+E%P*au3?YP%KCI;sn1v#UttEJ7l6 zMc&>k^0xGf7l>Ptvr_rJsC>8tWH($g5)rT?CDV*9l4;+o=5YrqEJD|kcyi!AwOP7O zB`U`Wjx|pCPDIY`l+RJ48m$;7A^S30aaS!@j&l|x`=9b~j=uv;W?WRN?g_6JvBYN- z5C!1WbetktN<(lQ&8O-*QZh<;s}?}PRAGY;o;<*CJ)p+OLooHhLnwfYs52o|>6%3R zh%5-Q;+LXG#>6ER-iJ{eg~-E}e4aYq4&UJhcP#-y-UmdXn)`u@F;+8%;t?q{L7oB$ zjuPZo0lzetgcU579~6bEAo3NzC=is9%R6|{nq@KVUZjZ-o8kMJ4@!o_<6080;=x8o z3Ru%jYdrwD9)QF~y$?b;k#?3qwSn@G)FIqn7XOnd7)l3TD3GR1QMU4zG-aw}g`F&x z?DA`u4@>E$usPD8buMN5u=vc9q77k>C#EQBSr%IP6glx?0(i0nivl6mA&XfVI6Kos zCqS8MoR6-6XHyHlAVX>vnGj(M)IB-M5S~h!07YpR(pc>xvLwg;X|#9AmKjoWkZS`H z7ZFYyysQaXotmJ?o=A;QaLRz`#Tpv2!kd)Pk5ce`1V+yZPE>Mk*$>KxJEH;kE!s#1 zGz_v9alDbl3C+E@*@&v(Z7n&LLTD_77u#{p#NuRY!RY|HP&Lboqmb)(!X77vTCS(I zIN!F|pnGwb6w1MUXcsudy(nmkv^G^SP4OYEyiewuYvV?8l9x9oQ#eY3q)#nD2Zf|h zL45#zOQ9s+qMLJpVr;W?1R4r5LydsqaG!`V#O@LayQP?5Sm+v=wON|wP-V-8p#}}A zVJbHRlv$esM@*{WH0A<3s&e?nF@Um7GS8*Q(nt2p`-eQ-ZWv2XrH}4U;AQ|oM8hrZE{xqVAhuRDw(X5_VeC?e`+h>gL90#< z@9Qh0!hJnCy05P~D(I^Pv9+Q}>?>?(Yg7QGV}tHksA#SLJvzOq87>$cNRO17R<#cA zwI<4e^hbMKE8(EWSEpEnx&#RajcXiMdp4&|sqc^!8+nan4?1xTcJjj5xmJi9rnMP2 zOxFSga^YRzm=0RTpzxLI>LSU*0iKs zwE5qfk#LZIjp6P=b#d7ax&Mt0oL#EgI$P&AdZJ}~qgTQX58s&E6P7ZYJM7Rkx9a@h zHh9NR^}#QfE#mh|_%-iXNO-F}} zkR5UFtr)Rz*$i{?!I_LL;;lmjag)P)>m0TvQi#f|PNCCiIi?pi}kq*kX7&Q{$(} zM%}rA<1p2T4)xopa~KrJqE^^N+Nhou7TVgcE(Ri?x08jXLTttBtC#X!xBGw=2sU80 z)t1yJLxv#=X7v3{rd0&2Q7!APFAr2n6=_BXv*>{VGP8nTh+*S#KCZG@nyUqsErBsm zzQk7aKvv=HLY%EnIc_qbD+)16??K>Efl6S~ta>lmybl9#Glo+>+#MW&S6Ua3{-jk~ zr^@o$W+Ap5lReLHvVxd|MfD^NW~XW5JBMQ_0d-JlzQW>eBBqw%E!##vB4ZOs!uPfd zRvbya7ujmSC@uW^G`wZ;-Uk|GhQNCQB|`eG~$ZG-AK-Ah1rpLWq-%)e==KnM-1~bUx+EK%)0^(qWK+ zye`K)n+kQawq6Ylx!6S^lT3()`7&J%o;R={xlz(gQ?m_UmVy>wdF^5rSaeg1YCWU6 zFl1}ktA>fjm>c}dVSr&DyHMjk4oeOKy^UhV8XQZQHqSsLz2gf^~MjJg`6S6IO z8jMm#3)ElVfCSYlL^WfGY5~r6upd9^NN7)nR>y9h$+Dz|b}$TE_%k4dZq@5%IKwr# zZe!DbE62ugo}!68@T6lCJ~GIJ7B``Pcg?giGpH^F9PvM;B-)k#pDs#yVQT!(ZRBB? zO^bFm**ch_aQ%1P$H{nB&orC%@q!fWaGMvphY+IgA)99hGcYlew0S0LC66QT$xb$= zK4`6paE1Q7@E`Qc0N3D68r%m{O%5A>08-?b*_!`gNy2WH{_hP8SM{lW8t>!AsRf)u ziAVu{!0ITsLWTa9aGK-X|Hm+Mq#JDse>f1W`VTQsXo{=)4=1Gt5}~SbL^jW4h+Uf7GUr^DEFnE;bUB2N-Dbbpqom*<@s06B@SR z+B#FOyBO9AL^QUl?j;}jr*VYF7A;^m3``%V*hSdFKs%XH@Tj#}f=6km6F6MTC*h1~ zIHg9gDTM8kRmaF?fLg3FoG$YTpuSl(kGdv&0$5b4(T&w7X3SUwO6e!0=_yy{Co$+E zJ8L>sC&Y8W)$vLWG^GYNgtKR)e~vIr*b(!8on>V!A&mUjCFOx{(M^}d9Pg!Ix@}{` z6fccH?v2K=DBPv!B?rv2XyQhKcoFE>Sd@jo-s@E0+D)Z0Ys9*{(n!-lu+HpKJvwPgs;D4$eR+fry#;78UE5gy< z!6_iql$t%dUZXZ^hJ$iU^y*QydT?6t=*CgCd2rfX^?bD|RIgO4mFg81yj!Do_mwVC zOV!KOY8Ozpsb3f-PH2q)O>Wi_fUloHkxP&kl2L7{ZH@eF+$s$Iv%qOaO{7;VEu4)h zq;b|5M1LLwhoP?x^b?$w;y@JSw zdq)?wD&?&tY{bHfWX1qbE08;qHNpFTHZ2Krt~5T1&8dZ;)A5 z3gU?Z(srP2eF@N%iiQJ2nQ3eieWp#dcW?>Q-Gd6C&yR%G{$EN3nXq8PT4&iGckKU5 zjX^4wdMK~YwZ~goBSJG7)!b>i<$Xm6d>VJ`Y|Ltt@*3bfz=pqyDWX)@a0$E-;$^bA z)O-q??DOR~UtTVEKm{_0jvgES8k$kBMty9`%tBV!=&z&0D9ka1uXV!93GDgCq0|m*Pr!ClK~j-UvRkkZnIFR9ker@Ffz1l{NAvnL1Dpa zv(*e}PoGWRtR*c3>z2(O&i+ezJ54-B)I*yF5s&$9_ITe z_8*|bm^zK5rnYvN;@WEeZo_m$mO84wQ4L-Ur*WefYamImv&7q~k+cKUOsxxpvQcPQ3cUaV#B^A5 zSbw3HZC%0zOz1hw3JhRA30Ip-jx04mPK`Q%@kp9dGcdYROE>|v_5P@zWwv#=FxnYz z*Yj=BJ^km=C{SlBOhDEvY$g*)h0{g#DJVP}AAF;OV0<<1FUc+J)1__ zT3T)3z(;MRx6^7ToeiKyIF2(F*+Z@Z#IPmdIuV6&X;F(6yOw`@&ritUHK<(WTBRyh zt_LZYn2;avbx6$|8ab>c4^JIdGlvU@)#BmeVYPdB z^oSZiQaqwIjx1NHV%3(L^UotE8|O+|VI!sv4IfrhhvyEf!r|4!YWr~Rh#KWvaK$5w zRcf_r!{z^nWx+ah?`f^%LQL(%M&e*cBEg}Ttl^(6o?iaJs4=4;_m<&^NiV@BU^qsD z_BhUPq3~t#&*u22IT^+?E{wvslViirgM4e%dR6YI8a+B<1-rMq%ji0%@ccw(Lb};z z6PNX>KH%z%03K>p{TT|3UM`{>@G8@KhV9(+sa`=Nw>0Gc#fD(fil92Z#KMvqOk;z~ zizz$L{lE029t|oS;>L`9d;hN%OjH$W0kse%h1^W>= zpu+X!f||Do^L{EYSYQKS*q@9f`mwBZ87pM$MLVS*x3aLAs378Hr!ijwOZll3JSrDb zSUw1wpDI-|rE{gKP`Xm8)=GHp`Tm|&B$SpZCDw)$y3Oz>$u$d z!_L99Lr%#V>R5e6E=hN}8kLpDI%nLr!)eJ72}En6wauy(3mIMq#-%M;>&x(>HAWXM zxn!W5;o@OluY0E;_&^At1WarTH$#qxYizDD`oj7MFijN#6>gJ=}eZNfGNh}g8O0-_W9g)4Astwzx7 za6Ir3V$*&_KRe4k_&VG;0S0sEsDnq5DJ-l5bgQVB%vdy48gX9O*o4X7nG37za9>HP0 z&2a*wh#>@@%=&@wJh{cut{s9{S#!;2Bvgga9DL~%uhL+J3~hNN*qZa}(B716VNST* z*KEkxry*_KB)PUtDA?-t>meCdU&YEupYFiPuLtqzxO&_C`eoXps$9)vjhe4ns8LHb zW5?CR@!ZvF^yLVH7@ly?xIIJv)$P=Ir_y6p zc&~WosG2*9{gjQPb58sapdJ8D5%*u+PyKA?$na5H@O40*NYtFKmT>n~B8FIjknT6)DygPLntZ&JHWv)8Hl>n2`rTTR&Ze;Md6 zDxzYzdj2J9;UyEVP?N97HK@^s`6ji}G;*C9yKehBTPi^@{*EUyj7eWT_w%#q`VXHRzZ^DGbRKcpe8|oVl7A)*9 z3m;OBRNjmkS1iG;30$aJNlv6O;Mt5C&x||bdGolm-)V+#29{)@T(T%p3#7>qJUDPG7~6UY9+F&i{PjlR z2FfnX{Tn+(1GB94@i_z$7|m*E5-C!D)>bddq$n-P zYVy?lDYbNJ^OV{?HF{c&pI$z#R!{GoR@wHocD3HV)2^~_8GoC~zis1fYU^#|Z&&%Z zPrqHwzJ25EYU}Nzowj8+4Tz9(bLu9H4$uUqTn{ptFHwb(1*_isH*JxN)?kgg0!0|d zfFn>&%b=m=%jAH1sSHN*t3saTssMX$Y%Itg%|?aVs>oHU@ya`!GwtBW&%~y|{bPe7ix#eRUP%QDN3EDAS zCRcl4ygX5+CSj+HyO_&mYPD>pT+NlQmc!yan}#`Ru|jQ7H6PWR2rHFpt#a&;nm9Cn zNG%-7XVg??KBE>gYe&`k(dlDq_So7nwSH{tnA$luU9D!TcO9R&Ng845-ztS|?*Gz8 zg!_t(D;xmgl0dEm_lYMlX%e!JH3>;eO@z0aesGJ}+=Rj!Z?haXedg0J|Ha;n`FL1 zEmVxSWl;!z8CR`v(du89iTAVAXgStiKev*g0V|;fob6$Qt2$Kgx>L4X+`8ZcULGwz zbPBaE+|zSA?Ecw7?)&ou)&l;AIhltw>dYlk7oqe`#MC5CO&tXIlfsi0xMtw?P5ln= z??E+mQC$jjxwkdT6i(TUw}N3~$KHvZTc;)6p4j`ZX;j`Yq|40{U23vxrc2Fr&EBf! zZ{508?c6$chnl!!9Jko-n!HO*-?ea;TDoibF131B@h-J-*T_3eek|XVt{nDR{Ry zJKd#byEeMiR@Vqze0GoasPUeO9yQrB-=h|K7JJlk&uWh<_6);uhi=Dfwplh7UxAo= zEt)2J=V(u!d1oCz|2L)78lJg$3!1oeHrr*Zer=Gld5ewg9-}(wEE)Z7aMiG|cr9>| zR=X%h4R?*>?daPl&Z)_BQ|Hvoxy5s8`P|AmwRUd(oGnko=QZdOyw=~j4?YYk+&$lw zy;UvVIu5@l8c8e!G|*LWZNGC-Rv=^c_*soYbCC79&2w%DN)^mxLThsuHA-XCV9nms zB+}N=VE1a2M(=_cg@AOt=k8(Uh4vu*2SCGZxU90AQ2CQnP8d7@R6<5OQty}Ppec-O zD75hFUuO$&y3#H57tFl=9hQN;%0*lc!Xf<_&XM9!lQ!ZIF5&f|Irt*~dZ$ zf&&=hDs1*+qhqRS>ZqDIx^z^n9L*h5qsPXNsr<32V-{ZnE!2t!F1qj~?o}*SY*nb8 zicu{cZ5n+952&GGE-CXoFu|_lI*!6zIuP2S`nhovY^A;XA`5iKZvML$@SFiVY;(P* zU3tCg5*C(|SSq{NNvhyDU+Tto-aOBO)%>vmH!$lM;X)pBDb-p9gAXo z9qg%W!N1loqa5A|`LzK?!5Xw4XLi_|b)Y&PQibsWV|Zwu+G6{*tK%V14%Rop?#;t< zB#jP-)rU8QK|A*LXeUX_8CCi3Ljj5>nn3EbtN;BV4-k^mgFXbx$|kVoA$yTCj>7(9 zhH%`;37GujJ&eNuMn~uv@lBsWuPvwYKS_dH!BXf}lG zr?)ssvhl1#|NquYC4UlI-D)v7&hZ(Im5!|^J~4Z60?zQyUP457B+3{cWNx?cbcYslm4BCl>Jp~Fu(pLW#VEF7Hv86h3rX_ z{1*oDqC zNqc5t7V|uj7ml$()_4#=L2qz|pity`xJXV6oEgn>YOT|JY?)?Y`7-zo8LpHI@bIS~ z$0vnKI2C|d048Eyo1%HDfaRWAELoymF;1-u$pu(c032Q}aHA3zDPbWe<{JfEm4qu7 zO!U=M9@h@1%CNMeXAZg555E=gQlgg2M#`aWkCnsU#zeWAET1Ze8-qo2aJ)+mf4X9( z0{86a@C)7mkT1PO-5=*DSzN2cQ8H5Ia3P5 zqVw(IHBmLft?xNGa4DTY=O@bZ>>XAW#c~+jZGgYvZuKq?ghr+1Xngvg@xh>`V(FK% zGgbU!Nu9s(VO%)AMq)i&R`9%9{xUINJ@t6`so^rDhsrzLYv9{XA6VYuyiWJCr;ZGuyr;ZVPx_~xh@E~MXf|2H z?S`?t{?y|u)Ss#j6VJNC@%<*gZBP8vi^Ol3c=kC|&QXi6HpH`TgRi67oulUspECVB z^|5ba=`dN=pPm;qL_so~PcN!0j+@bnVfuc#D~VS&gNfh55_B0{?#+49JYtF zYsTlA1|9D&w@ZKizv9k5KC0`y^LIu<2=OKm2H{uP21~qH79ayoNh@a-&LP(EvBm>0@lWlnij@vbE~I*4gr zMGyzYqxoS&J&)|G%5&`{%R?LU{akdgXn;#IPtRR0zXj>6X7yeTE>yhLbWaD#t9Yx~ zKA}EckA}lhszn2)*5%cMn)61B=Y2h5>goMM^CAQDcjKi64pm>)^|Rceh$+vg_SahO z3d>!KRBbR4x%pHNPu}HERQuyK{@6NybiF^aAu_zt zAG+HgxW^x?wX)iO-Fw}#P@i9M?jumJbK>3p_yK?Hpg(%ZA9>Ipe#jqsk3aCRKS(vk z9;)7L%P<>_m~j(^OVPnHjF<>(IgZEsKgezGXMI6ER{nk?J+t}Ve#Od#JZmrXJENh< zaM3_XbR_Kh9Toq5PQJ;n_-x}4v^mA9w>s@2kn_FykNuAaNR8n#=( zP^IlBN4%Z0$HPkR&L&pe}l7${P=;_#+os@AWz4 zRQ%ENRxVWR%lIcUR2&(c7ab^}K@grR`CU%$*8@L0R5UPe*GOnM?1n#DemC1cRAc)i z_~oIZ-Gjv>@47i%UVEH=T`!cM*>jcCTk2PIpnm&9^P}Tof2_zKMFlPK2TT2d1^$Tb zK$Sfjuf2smqV&wR%e}0>sLnV2;#to>6ZTIR`KOBglk@zE5`TQYKUV6GEGjsMWj9}x&7rV*X;c8 z-1TaI#>$1z{%Bkq83_*;*>a5xm_b8+{7vUr@1$){LtbXebs^BBmbg91HuSMg9mX(L8^s#2+a22ZIhv>3gt_!;yVe zIv$*2zQeV5+^hQV;0!;1I%o%&H@x}ykh;_~Q*6zQ@&TRSsItrSlo5XKuNcjMW#~?)(e- z%P7wymA=;V953dpG+*DT+?B4+2^RCFY<(W_jB^{pZ#=jsdG<_9F&^B-ejqXsW;dYq zCSCnOe`Ngo@Vq@kC1d!cc?8?92W)Ao@@=cLdW+DGk08D88eB24czns&($VsfWy8yd zDh6(tGd&-0eu@3vFvd9;KbYZzt_L4*_IS$qFO_apo<|!wnx|Yj;9k{}qgVMk$*FLi zoPMuHg;V9x$?d|MwDmVSgze<;PDY!SbNWYZzl<3V<~iL!D0((1^P;iZGi29~p8oK!nFHe+!J<5)XC7{A9KtMf-U z`6HYC;VsdjdN*6hOJ`ptt80Gie!q|H7W#}|@tm>8&}M(2e$VJVBXz@@2JQ4v)$2!V zSg&pm%07=?;^#-^CC+j<+3agUap zVZA~BuGW)P{Et_$+>{$1M@&C&)^%KqRCaoNn&ox-SAOGh*RClJ#Xs!Ijp~5X_u(ei zr^it$A0Kw@|4C;bw5upPJ|*{&Qq~iF>vsAP7vJUl{9=?J4HmMLefq0dFY1x?KfB2J z42P8a(e0@JGRq%v=}>l8>HTOO%k7(E+@j_BS?=&0<*m~B(JNLi^rRcFMQDY07v*Wh z&4YK4jBUpQY}a`|vF&*0$puYgi{@^`L*?9zRe6qaczOe9*8W-V7`MAH+BL1yo%ME3 zhN7p7A`>OM#^;aW&6Jsj_ybFJp9!DB%jc0YvxH|-uj<9b1w*hds^-&FcQ~F2u4hG0 zyK&pQCPL%kv7*u9k$J-VcJUSL0DIRi@F@#fg{aCYA7b>;=8P?G-4vyq!RLP&~u<{<}<+SeOkCaA+7x+VE z{@_C2t=+41{c;2AM?19kq%rnjNsynwn2=N9K7P%H3y->WzMW=~58=oLdCPp-FZ~MF z<2M`U{K^!k=el2Un);VrL-E0ufmTCRdH&~fR?h3U{sXI1GIWX3Gus}q-P9aKGu&TA zCTslD>m#Q&oZJ{0ul2|7@ki?-Bby?_oBg3J{$Ra7aBuX?x{13fl&Tk>x^DG&O>P{H zdu8{h8~OQ+^AkJWye<;>Kc(;2YgqnyrB6R+`+Ci;Uxd8M|8F#Lc=q3zr^`56mD6ur zwQ}_Smdn>nqwvPS)>vJ>O8m2RXU%!#FYCd40QrAE%*5<=(h_3g_~gjaGGo2|ZZ#`#ky)aVeu3>>(&twkK>F?W!YOHc}^~jCGHx1o9VD?X_a{A&mt3R~EjU%u;{TB0>KdIyNO!*c2$PUA| zM~ChhymNrktkV4_F5PpuZpPR`+{R?stRhre>=Nom;;@X&M9bnSVI;YS~Uc@7NR@kGVh z6_YodS$TTZsmhb9CvF_SY3%0FTSjgjzHO*#@OBbb_35vs2;jiD!p<+0zurd=IR2}2 zjrU+L`DPu%f1<=cJKvuy_0KHuPnShbEj+o%pIGdVFY(8g`lIFk$g;@r^2iYW>qkFW z;)s$4tMBXVM~dsYTq7t@AI0`gly6XsC}>-ZC~8%bsvN&bDshAwBNg8^^JUE7Z zK;z-a$Z&XgM3ukF_usktj`c0d_b}G|gMAAsoiAGTUWpf}c#`UE^wfcq2P5MTjy>d$ zzQ-Rq92t6VWbl1^CikCdI{og6L&Fab9N{2&?dvrMpRx63)U)$0%tK@;MAfs`F4_R0 z2Dg4?MiP0ix%9Z__3JE;brW81^~W()FY`wi`ewy7I<&+sxT^fUF>1pVAF=hvKeIA& zdX;~w(jUJmGIp~+dP{_AjX!Y5Zu&Kki5saNxxA?KUUl_jpUWTionVA*CRxK=21@=P zw{v>mN?!Saf8ul&VSM|_hfxmx#CRw&avE_=h8`F^8ya{b^u$2u>|kheD0F5xbb2Io z3O_P578;)jjhzgQo(c`0anmC0_x{Hervvjy+n>&nZfS4cKXX2ppuJLmu=h;G&@F?v z?jN}AiSo0{CYPUHaq5PXD<@WsSB|Y7y>aBGVe$o1??PW`uJ#W4PZ(%=81H8`vHtH4#%@J0}> z+y6xPY|&)#nR%y6PR&1AIRG^u9l3-*2#-hxQtb|JC!qb;;20301wg{ko0EyT;>WGuhUQ3V6PQWUO89VE^zo zjMJZ?e(T+Xo1a*7cJ1U{XR1%voLYBs{ltdxjbnF@){fjWTsO37fL2IVdLC&YT{w2Q z{Nq}s=aGJT=y^p?+k8icOU<^FDD9aXqL*hllzg1aIdsLnZ;lKWMTbK}VYRTP;)>?ki-+U-{3$!?xbcWy-hQDIe*9VixBnw>_*7p3w_hTJpK+as2!EbTb`V4L+*qYHbcrX~%&%mqny}JfC6Cii*&fs^rYkda4Q{TI5aN|4Ny*q=i zb=Ud~ewV)I>rk=xv+vEpOTp%9rxl79t8iokCr@GBL2)t{I3-MjN;!BzVvCu ze;^z`XC=qK316!06Qb^p?Tr8HC^$mO{^iOJA$-Lakk&?Rsc0C zUdf7(-lacNyoovb#!yP}e&#QMe^B`HX66mxA69(I@=!lG^`M*{{asDywBn6cp4xfn z7ZvYd-T;o@iwg9dSNv0oUu90||Fm%ZT}|ls6hF-Juy^Qr#b=bBzZI^(qY2F@?(DV$ zdj3K2N!EjSLO)hqr88WFnI`vl&$#@eUW8YRy#Br>e3#;ltUn6*w<+%8Ee7AO_&Jte z1O7h6r}JKCoow z`NeOL2YfqoiuZTG+0Kg{p9cQ}(XYSvDf*hygZ5?)pdu;$WA5pxD%V|=J*@6vj(RxnkA=H>Rg?k$ zYo-4j%M<@c;TxgX9F+baOT2$^k@*y=vZv&mnM1y0p2U0edCMW`m8?+mtcSisD!E0t ze1`;nx8gIbAK^-V4xHzMuh%hO1pZFpZ%kWG-x-x068?IF<>AM`TZPX|TTb5@l~8%H ze*K+MNx$MQ-p@du`Vl8zWA#wIEury)<1@_hyySO;>+g(8sK2oCdEXh8{4qH9JMx_o z^n67)EsvPv2>3S?Z)W~H`0K*uyCHBjevt2hz<(g}@|_R3YIobNv;I-fn;#PS?fuMQ z|M|-lzhXIRh&TT>;qrYB_(tLKJr4Lb;X9gH|1@|M+T3{NY3AkN4=a9-IqJ#$wCH(z z74ysBsy*uOa^|aXg65@aoF?DvK+i9W{&%=~QV;%Hig$3l#Q&S(Q_SB0|B|$$SCBq) zpkB@Yn#lh=>~4-exXPajZ(vMo^7Fr?_;&jsR0;kg(O-Ru0|3p(H}|gJDRXv1yrs(& zpJaLBsz2J<#2n|+EfVj}Dds-`-zD5<9zwa4s(xR-zX4bMzI<;3{vpw`>jLZlI{2V) z`F;jm*?*6dhkZ&_zrW|I)f1w8y&!tz`xo$+h0FIY;HtgI_bZ;aV4ld!cPij3#GZ@G zIsFh_aJ$IM_bT9<70+-w*MQ#-J?#JQgNQkR7QA0^Tn1c?6Xd%U$ajf;`A!9#-jA{W zk?&E!)x1f*KLP(Wk*6uUIVQn>Lvh@jVh3YYItz`q4O z&JSE<`C@SVBAiRV{{CdaHN~e`eh1|LMe!NtPh&o^RMPqCb>`4twp!%nyA$wg;qtu+ z_}hfb_axwITyfaNI}LtV-JNSkRlg(OcYxn7=}fR1ZwmZ< zlAc!`r}P|E@{lvfAh;S=%l8}L^hTKT`zpelgYncSN)xQuL1s|$REAR@{RcUmXL7yP6NC`@xv@X39i~jr_(bE z{vMH+?=Qd;$d{WhyZTcKo)LNZ{sNrFZ|uk9dkgSi6#Kj~&FO)i7b!m`-(7$|Cwk;N z3ve~=%Jj3I>)^_t$oCW26SF|{WUH)v=p48j56gED;CG0;eD?sZ`q^VMtfwAat;@-G z3gC~39{DZ-T>1Y;Ypfm*<-T}KqAFBHQ_%w5*cgb=kf0a4au<(J5<@0_5uG+iar@wTq$jd%`$Zr)c z`|iQ_D9+_u^fWll?>N7*pB`NK0ogYXuEvM5FCP3!(Iflb!PU4?_PK-qSCN8gaSAhQ$EkJGk;!pK|GG0{>@` zm;LMD%FoL_bYfc3zvQ2 z(0{XV*#{1;{M*H5tA{o!EU%UHS6;H5e#d6{JA(9tU%0{?zl*SZujrS3;oxdqB>TX@ zlOiwsyunpJ@+Z#DQ{W#LdD*`W{yE{YUmN@{gv-8caP^+zOI4iy%izk7$v$h(t5D-Z z*+&ho`U}|y4X)lt$UbNA`vSWaEm8iV!k2W)K4-|QagpqAhRb+C zKdVsvi|iK$|B%Sb{$KEu!exIixbjyove`;-u2An8Ut{h)1O7*<{G@4FFpePZ^C8YEVfrJ5ia{f=7kRepT=$ z$yYV!s~pK%@m?w4W&JjtUL$y$;tA%J;C+GqknA%9|ES0>E@gSxZG{>~$v#i;Pl_IT z|3}B-D!h+)MD#4;`atWwEAZRM&Yvv0X2h{51pWo#3(r{|Y66$_TeYtf{8h-af2*ES zdj3h$v-CRiClMvR?{n{!E;3&UF6*u#+4l*4my&n-v0L#58Fz(bA1L@fB|ob4ACdIP zzEH?_3zz+$;KRaY-zT`NON3;fC%DYNLbAUT{P$FP4s&|2@!*C(7X7ks6Z|hBU$8GI zd@WK%!5@Tvt`GNNtByH=faHQ zPl>$4^a^w3)57mVcykb^_fXuQE=T<~M-}*U;BH;+DswdKm0wkSn)w~ze=Gb#hB@lz z%I|=?c5#k5<%`~fyZYb2d=-i|49@8(Q|VcClajAtd9;^RG`@9upw}F$!GBKpXRk3Q z{WQL1`OjWqPI}&__%w6W&s8+O<#F;~xbkWM&kFxqGs`3XRsSjAMP*JN_Fi>b@llp1 zuJ$c__M+1BX(jLMN&2ZBu|01*Z{<-ly;aXE`F^E`+L6oe1;wcyIXzR%DLp?BzPieC z+Ml!P-<19-%gIkx(tAUv-*J4-TzP}ylPr(%Qsr9F|9)kM%6o-ZKhN?=Ph}L`*}su_ z9r#0vS1R5v{Hs?L?^WE{ne<3`g}>_3Lwr)?f0(g)7IlFCs_-|O6qkM^{58nYVdU}u z4+Qo3yyadw^!zEfYZs1FeyO}1zs&N)Usn7D#lNleyL^#;Do0l?eJqc9zMA$C@%zmg z#@;k|x$y5a@PjJwsz87Eg=>n_It=Ui4&=;%^XhGiH(Lzit=@^hoUhfW2j)OJS04c9 z^8IQZ^HT7*;@cJPQF<8DIDGY>l7CFe{{pzP!(ry|534^V@-JLw4m+=YM#(oYC%Z}e z3jcYN<@bU|DQ3Z_PF}*z&XEFSFC(d6*zvP-tpV6TJBu~|6jsy<@yk6faoG{ zm;M@-N56C9&5FZb=C~Prqv)xgWRB-ID*IQDDt=JOPcw%dZfpkU_u7vzr+tVwlHX(A zG-c)KJ=l$8f95s)me&yfSinP9n=Nl3F8(e2txJ}BP2kin*q(2=a<2rZ@s;DaH%Art zzX<#&pyb64W@YgRYJ!BszU!(M-6o-AyQ3X985gzPw zp!EEb;!{@Kqx2}h@p?b=Dsb8d!tuU#o;m4}{Wjs(ogGLIjenf{b(xd$C%vGg_#w{H0Ou5qYNyYn_lOFM( z;hARURp7E8Dg5dvb4t(Oi=ND6 z((^sVuP}$6Tj_nAlfNi>ZoNhLOEaS9)_UPDJ3Ek`cPd_|^pHPc{VyF>dX6jZ>_B=> zDSlq*QGWZy%Sw-|JB43rU`~3jC_PooalZBY!hdj;Ir4Ga0^vV&>4cu!Zc}`_=(&x? zEu60(G%G#(6nE()JuQk~P+c?aZO4YJ=jqH%ApX^*gNp+C}E1=dkeax%88s zZpBMkp7conUKBp8^n4G@41U&9>n-~I=Rmol#c|7+o;bxO}aDn7*=dhYNP?`K{GzEb#nXMg1Dj=P1I zIy;b_i17K>l^)s;#`c*Hz2+c28O5C)NYAk18J4dCe@b}CIp(D2bHe94JCL3~6JByv z>5*|rQAxSd^J697&m4O0r12Z4zoeOY75E*(=Z!K)dhS%~qVujZCq4U>{1v4qsW@Y* zKX*Q+ct+`YLUG8MqYCopg%|fRCp~|pxU&PL=Wi6h#PX!)pM)1(6FqCf!i$@kL(iH@ z#T%Gcf!7Kz>R^uatl6cwvjgd&`8(UW=%Ui|LE+&UrDs%lk+TEo`Bla1lpdNFu>Me^ z((~tvyLd^@Yl@#!dj6+y?~3SIy9C_L`!dX-XYCr157mgCwQp1WveNTz;o%16q(|n( zMd2#uIIn$t?(c*C-F)Ot^qledyQH7}>}TKQcyU?_o7nG_z^^I(Yj46ot@w2%|GEF5 zc)#!;6z`w>2gQ5&KPFzR+|T)4+gcje&GG%|+Sl+~p}em^ejft$?eEJ29;#RM=U;9Q z+V$c(=1_fCx$x!Z6~CDlt-cIQyT_ z74Y9v@}sPW?C_^z=LJ(r|KCZxf7@Vrcpv0n7ya_PL*UnhKgxEi!h4fy`tZP(Zz$Mb z2X)o7IN^Ae)lYw`SBd;f9g0^4^4@|&%&~q_y-nnQd(v`xuTuREvH!AWmPdY<{z%ff zVwySPsaEB<*!e5cb3o$t`mH>@m#ltopvTj{%Tyf?c<3t)tRHr(J}T*1KEu2e{Be{E z_aoa-PUe^gJ}L1oeavF|ez*D)O3xp&JQlF3e^bSKmF4TeKPNoF_F4WM`17hhtYJM^ z2d@68O3%xz2hXc@eTcF=PSw(W^L|&R`nyO!|89W(eSqp8OL+zJLFlRRBt2!9Sbs72 z06+5Bt0))V2)GG!z$jZ%qzfS(8Kj%CZyz(!e4de zMfS-_`F_Kthxms@|1z#W^u1xtsHEqWb4veN;V(5Q{&CT>ob60gh?-B92X@fE=TP%$ z(ev_orRN!?=Znlq&lkmR!TU?vW^5kT{7m#O zX}0q8dT$-ot2utxV%-fQAG}wE=(@WlU$0!Sdg$-Etx_)UpSHZ8cm#nso#o2T>)s>s zl`K!8)*X>@l;2?j?@@O63`ydE-PR3>{-re*({JXj`z6sMzb6AeCF%d>q?HfD&g*^? zdgf@S>pmlPSiFMu!~W}jU!~_K%%Ni4mn7at`Yoq@gX>;Y^3yC&>Hi1CF;+AO@qdwY zzWfw(oYogfeuaW7|FGl*d9r>r((l@7Kl1~Szf<%qzQ`Q*Tfar6$CVdE*S}4rXHe;n zihY)Sh536SPal(TxrdK1hn?3SQTop^M}F5QA-fP&$WKVT%O6*I zJ}&8eaY_`e|E$O_Uc?;bxc-Zx{~I&R5zqQRpRK2;5B%%U!+z;LG5y^@ALq0F#Z$~Hq31<#JFW{~ zy2za5*C{*y7IWBn!@Z*a<$X&2=Y_v?PVq*S&JQa++HdzUhdnp6i2Rbnig$^fTb+88 z6bszXyu{a+o=5T;!4&7 z{TpwQ{4U~U9GWXcTe_&mrdr9J$&62&mK)?y*0U>c($iE6Kli6 zj#x`N-IGiR{XlQ7Czee*oXW+z(%tcFYe%d#)5{OMHTf8!yE3s{0dMJT zYeNQ7X=vf#=Gp|THJ1?FvRO;SKG2J#^`v8MschHrcsA(;f*w@oQt9s6R6adb-RCjx4vLo3?H+*W1<$AIn z{&gn1y{=?eD?Y)&9i4E80-?K}*31d7ySKCRP&S_GgqDMucx%#2B-`S>ojtKkHr<+p z*Ja z!;AOCW68drY`nE6#uo6}(!JdYWV9<2?@6_ECgUBgDX%BhmCVL+oylaz>rEs(<0rf> zd$LbjJCdzOH+#8`_~v`J##%bltw-r5b~IhPeLG#R)-sZJ;rdyD?WLpXri)VAm zST72UYFn(cwIhdO>P~omDxKStOm`(wg(FB>Pag3|20Bt{&qug)Hj&J-l)LqiS<*>D zR2ZpN6c$}~#5;Rxw{Q01iA1a=dCY5!A4@hyNsZ_4ddO-awmV>6Po^toET3+Vq5i}P zY_5y7!~VJ6Oa?{Onr(@9AIBSb9IaUz#O3nCNdlHI5>Did% zvdMO2G8XSlwRf9H8RUAq^lewFJsa;y#+Bv8|jA!!M+ZO8E#s(g2@3+)KTYOFtCfi#f0I3C;f=D0lhAW~pW z>_f|oMVcOrH8$tg9$~vj+t8%jY*LWmh5bc^&}BLg-UgQ zkQ`>4xk>i5B5fUsPV$C`fGkQSN<9;SL{~i43O8wmktme1iIwx0+`hRExyn%&V6LsN zalR#yrmHR6>lsjYVLC6AQCkX?n~J9s%{F-qanc>yEh;)yDSE`0lq~XSX?SxuJuOOH=9IxCXJ&(OYZ6LsE0&->O@!K(I2`2^U`pqv5wvrDCvP;@W{Q! zjP)m3z=8)V*8d+c~N)ss9M{D%FRc8WqHpd!fD-(<(UzN)mc#iIRYoFfLfN4iYjxgf7fOdjiUn6U@skiq&GOlC7^TpjLO;%MZl){gsP z@OTJppT=Tc@uMg~a)A`yG#&KpNt8)2B5;03097CfNLERAcb-7qr+_9SiDa&Ib0(ER zyGvr^;_5+f&vEaA$*$g5Yg;>#K%IowwEw`N*t>V#|6nY#drvHKXrG6Q2rHX{%EexM^e$(`^HW>Vd~nO#ULLU5NI?`-v` zP9062Ag_+Rw_ym9k5Fx#!@@8NuEUHAP|>^6iC(xCQ`JowoBCy4RjdU)2ekoer!jP| zwP+hXwROS0*M%WusxUuhWP-NZ$^H|`=T>K0sA_b1_dRLTI?Skue1C93UYG#Rwwt9J zbd(8G`C-^j_?Y&14vhu_LhH4I0wH%~ItdzO29%1|qw=E1$!@A5iTtPR(Si`%$|+2q zJRF8e=z)0GK@0)*qfyuI5mmXPsf^rFwAPEIx;tG*A_W6;o8G3@_&kgjGq80$Lp@Pf zyp={hrhUVAxLzxh%);@*5t?CRE-#=Jvxy~tllKsv40%)2ti1#F<^YYD+=I?^JVC=@ zYBI5|UfYUXW@$8aptrjzjSj*ZhDJT){PH5HX2PbKefuWb;WZ>kLLxYhg=Tm3|rz^UCad(NiPe-b~L*1%}m}0cM zCq~K`0J$|IC-(#@Y&CZE0|w5kD^#2ElIaAgBGG8QyPXQbR4FtA3`$xs2|!nZ2_Ov- z%y2VC(HM^#OSPl1o9f=1Yax$FnT#3l%flem<5AgU&_05OE}b#=AO^T$lDSEwaz~A) zH4=8X3nwtgv%IbyY4R{4#KbA^%ss|4QyPq`HeQL`lW~78Y2>`j69VK1OthH(L5>hg zxx0tPr1m*QL!&87K&fb5{eja$Gr{Z+)9l(!n5W^1nSH@8nVXi*qg}^fWvU4D(B9LW zGNbs`w0VJ^U$>=j9o8lG$5S$;S(xiyYILNn&wo#{;G#KBa%7q#Qe z#%L4GW%({*?`TSTfabr4Ff#R8j$!uN-GfneYc`qePIb4X?VOovCV472d2)beR)Qi$ z1MkdtI=ODvK%Kkk)ydPqcbS{oEn7XBvm82nsCLUXyM98xj@D|VOEP(|?vnyjcc0gc zgBiCmzvm2A&mltWew)vY>Q8}`ton!Kx6qX}b zf?9{!2PM^9k@q*t2>WOTWr~XWgN_868+Aq2vADsEUPwWONWgKK;Xhw?B@-z$&!BWj zQ>$HZ;W2@6m|TH7li57;A&03%?S0#LHKsj@87AiX7_^&cG24iB_I9J%Vwu1q<92Nd zy-qCg7&(d*avth!K5zpc3Xj=;7UMv#3+oqoq0BN4t4tk3XJ}Rl$gorZJuRKy{h6M< z`y+cX*F2E?U@vu|U2$ZK7N`z(U?hCpkM1^|7Tdimo-syuQ+_EbcwuxdcxYz$TxKQ? z({Evhgkf#&INW|u4|-sCOYSJT>miyKJNM2NBNp5J-~*9Ed-p#Oi`CX|3Z!gF&M8#q zgu0VG$J5!Pu8!rY+H#+pxpSp}*K>rtZbv*zy;U;X9mfR3cD2m8#aidgk61~n8!L(` z!CUHsNxEI~Ls%-QG>z72Y~|rhlU6LxVDTupLp{a-!2HELyf1%Tl^4UNdRxP3oa80a zw(45%;oRq}(H~kYTEVe+mWm2~CEMdYwClh@*w{OGzmJL2&O{>Xk&~u@I|kkD@gA&0 zqUz*mdK||(MD4b1UL-0DdBzRWsKj{s#@?3wSb)eRu_#RQq#o2{tdG$W7#TKKV5EC( z7VL;4FLztEco=LMw-m!13v0R1XygiYY%b8bso=gY|9@#66uYLA}pQ=jLr@=0?@H`&b&uq`|dYui2CCMDKn-)_35g@`G)~ zJdPV=pg16=#lg21gt@oiz8*`?T`|m7ky=`sKu=HWjmGbpMMn&;Xn}#67Dfa#$|pkg zF%KA#yvMVms%o9?w8j5DEY<=t5MHZKQ|m^3X^ zB0V(Xu+nZlB;P_<>4p?7ontw%%iORNGV5a$++2_p-fFOO!}4F&j<<-LArdt*H%u}v zTSh%{OHI+lVUQXKnR`1B%1_^Buh%$A^ed?1iQ3Ht6%VUjsKL~K)NZ{%%-|NWGNqQ?Q<_^Xv`F&;Yi!|I&;LRGPpd={e%oM`-yq= zBkJll8FdA!G!-?D&Q`G%N@lAFXr2l~XRAO(pF>4J^Hdl*TgAO|st9PF3PWeB*gU6- zfaa+%bhe86IaLHSPlchgRmA615zssphR#+&)ns;w1T;^Dp>wD})tEztqxmX`7N}@N z5@x4DQ&Hh)fr>h^@$6J6Dk_}pY!y_+3p29WRAxi-R2bRWDuNn7F2pt}#23&!6^0h5 zXlrXp&Q67WHR!Dva!`jBKLnFiV9{t-{g*6_m2s zsnAqZI9i~ht&QA4VJb8g6^<6FuuZWbBW;Q*6-*0N*iNcYg`!FY)7dJhzRXq;&^#4} z&Q@UxD!-F#Yug^sJQao(s+hBlv?;1oFrBR;=zyelLPbFHRLs>aK{)71^Hc;hPsLpQ zD^vtMX`YIJ=Bb#ge}#&mP3EZxXr79>S}Ifo-C&-Ifaa-~t8;`3(|`*KRna^ZMs{|I zYZTUSjI~RG{&*jDf%us4?Zf-8))V{=%3HIBQV`oz=RK5)@Al2q z+sCx;Ag%3~{Uufj){QGFR?q6QTg%K+16DV&a z(Y1oKpquQ$Iwi6d@3J%0&76fGWLFpFbM!VKox#pMD#5)m%*J{!@3p(IsCK|?u~;vh zN+Pwz3Za;3BG?OgVwBeh5Bex5Z*TPecm^xbExqmgvEJX?(?khn&GlHk+11qxb7CJK z)xRuOp|Iy88Sir`!nPW0HO$~0cvr67MYhdd-0MXUs+SftvS~7p(~RV>VR9KP(PL%D zz2?C?N?HuFZ-cWjvmR)c18nqWlb~moBxtf}%Ei8EHuHOI-7{5#%)=32@fWLDC?jiq zDs^KvS=pm^Vk!#z7L(Q{@yf<-&oWI6@7a5t5~_pJ#%%kLLg(cLE^bWdO_UF!1lltm z?@QQ(4KP^K;I;o~)1h5%60V!deiw~hUvhVNv18-3ies2Pn*~6X3m6JZQT~H__eJ+U zuzSCHVi$I^edeVa8v_dL+ljrjgl%nzEu-Do)j)zLu**k`ZkBCr+f!+c?0;bQ-aX!4 z+6oa(p;+T5_HrGiT_<>LO-n~+J7p7=zS`2+F1#Jas!02OddF&71{ORxL7TA+0*wIm z+Eg3cfa#^gtmeK2OYpR@!Y+xUJ+g*-;eBbt7MADaZ9EqmEiXDbydYyMZp}-)@ks{` zMQD)-m7g{@w3;1>vpw2DZ200>gA8nAgWAQYHrF=?`tNE<%@=Sv=$7D2s( zT`BT0Y(1~fZWcnGQU7@Nt8E3gil(uWXYNh=chws^TY5XGl{clbW`&me2wLEV9nk$8 zckVUkl8hdJ4FFS+#p5}*Rcp-}>YW0?dhyY=KwO-&O>$`0!9&_(wDd_>?Apaz4jBV^ z?n=YJwyTIG*-6-{7~Oxs^J;S^x{zC(v2oU%JKU9V@!%=1mYREQ27P30g8s0YTGMPZ zfG4`4#-3rw+V<|=TB;ddZPU&Jdk;oxd%KU*?$g?22X@xc#+O=CL=-UIm1@N;IvR9o zwO9?`w6(VOo?N!|9xRR4w&JB({aZiY^j42IZ@bT{B`;Ikjn`6;Bu9)reOOUe*Kle` zwS7i|Ja-*$X~|+MD?dTE(T%MQ?$(5%m)WQyHp}J(UM&p@Qh2cf zYZBgT{{MTr2@2@L06Ogd7i_Qbm%huRQ?PH|+~QBayTBQr4HSeA&Dh7G*ZS?nq&>;s zD!DZOcdLs(|GTm92m7sDS+kO-OYFxFjt#gn;j0S2w+eq~#!3|xCP0U;n;rh~YbIRy zBN-ch>8L#ie|7kKL0$U!dn5jCFMRsC4N=`6gtQkr{M&&F!t?h9;b;3eTxCIcCr@}6 z{w?I^96$fR&f#ZWzA3!*t=(^%^5#hg|iDoNMFY?l0M&jj~@Q~ z5fd)_>`UfMU&z?Q*^T0*gU)k==l`ENe2K$*lzuwq3Qzw@hvS_7FC$4f!k4a@v**3R zXLtHPr2i7WH^dSCv#)c2w~Bv`^j}JFco*-#H+&C=ci;8=d&3Xf@LwtZe@y?A9Dc?B zWB8{z{F?uZ@YKd!`LX}u@7}^+*>1zX%atRm%d3C%4O%!-Y6+e1|0mVh`J688x z$%pV;yl)b|^l|?1?S^eGAfI*kwQnB&7pkm$QG-1Pf2rQ_5vs?9)0dzB(qC$}LVt5N z3%iq($2s)#fbKp&{DPyWH~__=4<}FI8}VT7@?UWDc^fEnQU}JhD*w>?aqslyi!JDC zG9a{nXZ`~%AJYCiU##r8vleWBD*r)VcxwN1$6xj%w#VJJJ&6+6UWiaw-JV2h;hik^ y&Kf}c3zM&)!-cQJHTf0Sf73HL7Ity`8^0;gT Date: Fri, 10 Jan 2025 19:01:31 +0100 Subject: [PATCH 1427/2892] hw/rx/rx-gdbsim: Remove unnecessary uses of &first_cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rx_gdbsim_init() has access to the single CPU via: RxGdbSimMachineState { RX62NState { RXCPU cpu; ... } mcu; } s; Directly use that instead of the &first_cpu global. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250110180442.82687-1-philmd@linaro.org> --- hw/rx/rx-gdbsim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 02fdbdf824..88c8f12c10 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -127,7 +127,7 @@ static void rx_gdbsim_init(MachineState *machine) * the latter half of the SDRAM space. */ kernel_offset = machine->ram_size / 2; - rx_load_image(RX_CPU(first_cpu), kernel_filename, + rx_load_image(&s->mcu.cpu, kernel_filename, SDRAM_BASE + kernel_offset, kernel_offset); if (dtb_filename) { ram_addr_t dtb_offset; @@ -153,7 +153,7 @@ static void rx_gdbsim_init(MachineState *machine) qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, rom_ptr(SDRAM_BASE + dtb_offset, dtb_size)); /* Set dtb address to R1 */ - RX_CPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset; + s->mcu.cpu.env.regs[1] = SDRAM_BASE + dtb_offset; } } } From 6286423b8bae2dafb5209a2e45d3ad30d75d146d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:05:46 +0100 Subject: [PATCH 1428/2892] hw/mips/loongson3_virt: Factor generic_cpu_reset() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main_cpu_reset() is misleadingly named "main": it resets all vCPUs, with a special case for the first vCPU. Factor generic_cpu_reset() out of main_cpu_reset(), allowing to remove one &first_cpu use. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-2-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index f3cc7a8376..47d112981a 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -399,25 +399,33 @@ static uint64_t load_kernel(CPUMIPSState *env) return kernel_entry; } -static void main_cpu_reset(void *opaque) +static void generic_cpu_reset(void *opaque) { MIPSCPU *cpu = opaque; CPUMIPSState *env = &cpu->env; cpu_reset(CPU(cpu)); - /* Loongson-3 reset stuff */ if (loaderparams.kernel_filename) { - if (cpu == MIPS_CPU(first_cpu)) { - env->active_tc.gpr[4] = loaderparams.a0; - env->active_tc.gpr[5] = loaderparams.a1; - env->active_tc.gpr[6] = loaderparams.a2; - env->active_tc.PC = loaderparams.kernel_entry; - } env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); } } +static void main_cpu_reset(void *opaque) +{ + generic_cpu_reset(opaque); + + if (loaderparams.kernel_filename) { + MIPSCPU *cpu = opaque; + CPUMIPSState *env = &cpu->env; + + env->active_tc.gpr[4] = loaderparams.a0; + env->active_tc.gpr[5] = loaderparams.a1; + env->active_tc.gpr[6] = loaderparams.a2; + env->active_tc.PC = loaderparams.kernel_entry; + } +} + static inline void loongson3_virt_devices_init(MachineState *machine, DeviceState *pic) { @@ -572,7 +580,7 @@ static void mips_loongson3_virt_init(MachineState *machine) /* Init internal devices */ cpu_mips_irq_init_cpu(cpu); cpu_mips_clock_init(cpu); - qemu_register_reset(main_cpu_reset, cpu); + qemu_register_reset(i ? generic_cpu_reset : main_cpu_reset, cpu); if (!kvm_enabled()) { hwaddr base = ((hwaddr)node << 44) + virt_memmap[VIRT_IPI].base; From 2bf8545603e3388c33745b41dc797fef7b2224aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:01:24 +0100 Subject: [PATCH 1429/2892] hw/mips/loongson3_virt: Invert vCPU creation order to remove &first_cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create vCPUs from the last one to the first one. No need to use the &first_cpu global since we already have it referenced. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250115232952.31166-3-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 47d112981a..28d4eaf1e5 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -492,9 +492,8 @@ static void mips_loongson3_virt_init(MachineState *machine) { int i; long bios_size; - MIPSCPU *cpu; + MIPSCPU *cpu = NULL; Clock *cpuclk; - CPUMIPSState *env; DeviceState *liointc; DeviceState *ipi = NULL; char *filename; @@ -569,7 +568,7 @@ static void mips_loongson3_virt_init(MachineState *machine) cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ); - for (i = 0; i < machine->smp.cpus; i++) { + for (i = machine->smp.cpus - 1; i >= 0; --i) { int node = i / LOONGSON3_CORE_PER_NODE; int core = i % LOONGSON3_CORE_PER_NODE; int ip; @@ -609,7 +608,7 @@ static void mips_loongson3_virt_init(MachineState *machine) pin, cpu->env.irq[ip + 2]); } } - env = &MIPS_CPU(first_cpu)->env; + assert(cpu); /* This variable points to the first created cpu. */ /* Allocate RAM/BIOS, 0x00000000~0x10000000 is alias of 0x80000000~0x90000000 */ memory_region_init_rom(bios, NULL, "loongson3.bios", @@ -640,7 +639,7 @@ static void mips_loongson3_virt_init(MachineState *machine) loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; - loaderparams.kernel_entry = load_kernel(env); + loaderparams.kernel_entry = load_kernel(&cpu->env); init_boot_rom(); init_boot_param(); From 87fd8a80c734781865318d7d511e735b6f49f8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:46:51 +0100 Subject: [PATCH 1430/2892] hw/mips/loongson3_virt: Have fw_conf_init() access local loaderparams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'loaderparams' is declared statically. Let fw_conf_init() access its 'cpu_freq' and 'ram_size' fields. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-4-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 28d4eaf1e5..45a524cca8 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -280,7 +280,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } -static void fw_conf_init(unsigned long ram_size) +static void fw_conf_init(void) { static const uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; FWCfgState *fw_cfg; @@ -289,9 +289,9 @@ static void fw_conf_init(unsigned long ram_size) fw_cfg = fw_cfg_init_mem_wide(cfg_addr, cfg_addr + 8, 8, 0, NULL); fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)current_machine->smp.cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)current_machine->smp.max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, loaderparams.ram_size); fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1); - fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz()); + fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, loaderparams.cpu_freq); fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup2(suspend, sizeof(suspend)), sizeof(suspend)); @@ -633,9 +633,9 @@ static void mips_loongson3_virt_init(MachineState *machine) * Please use -L to set the BIOS path and -bios to set bios name. */ + loaderparams.cpu_freq = get_cpu_freq_hz(); + loaderparams.ram_size = ram_size; if (kernel_filename) { - loaderparams.cpu_freq = get_cpu_freq_hz(); - loaderparams.ram_size = ram_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; @@ -661,7 +661,7 @@ static void mips_loongson3_virt_init(MachineState *machine) exit(1); } - fw_conf_init(ram_size); + fw_conf_init(); } loongson3_virt_devices_init(machine, liointc); From 72a4da938680edeef2be454a67c0f7db22176a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:42:10 +0100 Subject: [PATCH 1431/2892] hw/mips/loongson3_virt: Pass CPU argument to get_cpu_freq_hz() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the first vCPU as argument, allowing to remove another &first_cpu global use. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-5-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 45a524cca8..9f6fdd0f28 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -153,7 +153,7 @@ static const MemoryRegionOps loongson3_pm_ops = { #define DEF_LOONGSON3_FREQ (800 * 1000 * 1000) -static uint64_t get_cpu_freq_hz(void) +static uint64_t get_cpu_freq_hz(const MIPSCPU *cpu) { #ifdef CONFIG_KVM int ret; @@ -164,7 +164,7 @@ static uint64_t get_cpu_freq_hz(void) }; if (kvm_enabled()) { - ret = kvm_vcpu_ioctl(first_cpu, KVM_GET_ONE_REG, &freq_reg); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_ONE_REG, &freq_reg); if (ret >= 0) { return freq * 2; } @@ -633,7 +633,7 @@ static void mips_loongson3_virt_init(MachineState *machine) * Please use -L to set the BIOS path and -bios to set bios name. */ - loaderparams.cpu_freq = get_cpu_freq_hz(); + loaderparams.cpu_freq = get_cpu_freq_hz(cpu); loaderparams.ram_size = ram_size; if (kernel_filename) { loaderparams.kernel_filename = kernel_filename; From ce6768965b5713c353097d362f712e2bca1ef354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:14:07 +0100 Subject: [PATCH 1432/2892] hw/mips/loongson3_bootp: Include missing headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MemMapEntry is declared in "exec/hwaddr.h", cpu_to_le32() in "qemu/bswap.h". These headers are indirectly included via "cpu.h". Include them explicitly in order to avoid when removing "cpu.h": In file included from ../../hw/mips/loongson3_bootp.c:27: hw/mips/loongson3_bootp.h:234:14: error: unknown type name 'MemMapEntry' 234 | extern const MemMapEntry virt_memmap[]; | ^ hw/mips/loongson3_bootp.c:33:18: error: call to undeclared function 'cpu_to_le32'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 33 | c->cputype = cpu_to_le32(Loongson_3A); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-6-philmd@linaro.org> --- hw/mips/loongson3_bootp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index b97b81903b..712439c257 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -21,6 +21,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/cutils.h" +#include "qemu/bswap.h" +#include "exec/hwaddr.h" #include "cpu.h" #include "hw/boards.h" #include "hw/mips/loongson3_bootp.h" From 61f6e494e3ee060c92491d8df9315e4fdf590864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:15:30 +0100 Subject: [PATCH 1433/2892] hw/mips/loongson3: Propagate cpu_count to init_loongson_params() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate the %cpu_count from the machine file, allowing to remove the "hw/boards.h" dependency (which is machine specific) from loongson3_bootp. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-7-philmd@linaro.org> --- hw/mips/loongson3_bootp.c | 11 ++++++----- hw/mips/loongson3_bootp.h | 1 + hw/mips/loongson3_virt.c | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index 712439c257..91b58a71a6 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -24,10 +24,10 @@ #include "qemu/bswap.h" #include "exec/hwaddr.h" #include "cpu.h" -#include "hw/boards.h" #include "hw/mips/loongson3_bootp.h" -static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq) +static void init_cpu_info(void *g_cpuinfo, uint32_t cpu_count, + uint64_t cpu_freq) { struct efi_cpuinfo_loongson *c = g_cpuinfo; @@ -40,8 +40,8 @@ static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq) } c->cpu_startup_core_id = cpu_to_le16(0); - c->nr_cpus = cpu_to_le32(current_machine->smp.cpus); - c->total_node = cpu_to_le32(DIV_ROUND_UP(current_machine->smp.cpus, + c->nr_cpus = cpu_to_le32(cpu_count); + c->total_node = cpu_to_le32(DIV_ROUND_UP(cpu_count, LOONGSON3_CORE_PER_NODE)); } @@ -112,9 +112,10 @@ static void init_special_info(void *g_special) } void init_loongson_params(struct loongson_params *lp, void *p, + uint32_t cpu_count, uint64_t cpu_freq, uint64_t ram_size) { - init_cpu_info(p, cpu_freq); + init_cpu_info(p, cpu_count, cpu_freq); lp->cpu_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp); p += ROUND_UP(sizeof(struct efi_cpuinfo_loongson), 64); diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h index 9091265df7..ee6340e42c 100644 --- a/hw/mips/loongson3_bootp.h +++ b/hw/mips/loongson3_bootp.h @@ -233,6 +233,7 @@ enum { extern const MemMapEntry virt_memmap[]; void init_loongson_params(struct loongson_params *lp, void *p, + uint32_t cpu_count, uint64_t cpu_freq, uint64_t ram_size); void init_reset_system(struct efi_reset_system_t *reset); diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 9f6fdd0f28..eb2a6a248d 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -185,6 +185,7 @@ static void init_boot_param(void) init_reset_system(&(bp->reset_system)); p += ROUND_UP(sizeof(struct boot_params), 64); init_loongson_params(&(bp->efi.smbios.lp), p, + current_machine->smp.cpus, loaderparams.cpu_freq, loaderparams.ram_size); rom_add_blob_fixed("params_rom", bp, From 03ed671c82b97ab1640dd5e0c8c5adcf1c4c6ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:38:52 +0100 Subject: [PATCH 1434/2892] hw/mips/loongson3_virt: Propagate cpu_count to init_boot_param() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove one use of the 'current_machine' global. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-8-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index eb2a6a248d..5fe5bc6fc0 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -173,7 +173,7 @@ static uint64_t get_cpu_freq_hz(const MIPSCPU *cpu) return DEF_LOONGSON3_FREQ; } -static void init_boot_param(void) +static void init_boot_param(unsigned cpu_count) { static void *p; struct boot_params *bp; @@ -184,8 +184,7 @@ static void init_boot_param(void) bp->efi.smbios.vers = cpu_to_le16(1); init_reset_system(&(bp->reset_system)); p += ROUND_UP(sizeof(struct boot_params), 64); - init_loongson_params(&(bp->efi.smbios.lp), p, - current_machine->smp.cpus, + init_loongson_params(&(bp->efi.smbios.lp), p, cpu_count, loaderparams.cpu_freq, loaderparams.ram_size); rom_add_blob_fixed("params_rom", bp, @@ -643,7 +642,7 @@ static void mips_loongson3_virt_init(MachineState *machine) loaderparams.kernel_entry = load_kernel(&cpu->env); init_boot_rom(); - init_boot_param(); + init_boot_param(machine->smp.cpus); } else { filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: LOONGSON3_BIOSNAME); From 18271b5357a288c915d86f8f997085668e9d0b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:08:06 +0100 Subject: [PATCH 1435/2892] hw/mips/loongson3_bootp: Propagate processor_id to init_cpu_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-9-philmd@linaro.org> --- hw/mips/loongson3_bootp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index 91b58a71a6..1aab26df69 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -27,12 +27,12 @@ #include "hw/mips/loongson3_bootp.h" static void init_cpu_info(void *g_cpuinfo, uint32_t cpu_count, - uint64_t cpu_freq) + uint32_t processor_id, uint64_t cpu_freq) { struct efi_cpuinfo_loongson *c = g_cpuinfo; c->cputype = cpu_to_le32(Loongson_3A); - c->processor_id = cpu_to_le32(MIPS_CPU(first_cpu)->env.CP0_PRid); + c->processor_id = cpu_to_le32(processor_id); if (cpu_freq > UINT_MAX) { c->cpu_clock_freq = cpu_to_le32(UINT_MAX); } else { @@ -115,7 +115,7 @@ void init_loongson_params(struct loongson_params *lp, void *p, uint32_t cpu_count, uint64_t cpu_freq, uint64_t ram_size) { - init_cpu_info(p, cpu_count, cpu_freq); + init_cpu_info(p, MIPS_CPU(first_cpu)->env.CP0_PRid, cpu_count, cpu_freq); lp->cpu_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp); p += ROUND_UP(sizeof(struct efi_cpuinfo_loongson), 64); From 7eb372cc8bed1639e43529632a4fbb8b3f645194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:09:50 +0100 Subject: [PATCH 1436/2892] hw/mips/loongson3_virt: Propagate processor_id to init_loongson_params() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove one &first_cpu use in hw/mips/loongson3_bootp.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-10-philmd@linaro.org> --- hw/mips/loongson3_bootp.c | 5 ++--- hw/mips/loongson3_bootp.h | 2 +- hw/mips/loongson3_virt.c | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index 1aab26df69..67812666c5 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -23,7 +23,6 @@ #include "qemu/cutils.h" #include "qemu/bswap.h" #include "exec/hwaddr.h" -#include "cpu.h" #include "hw/mips/loongson3_bootp.h" static void init_cpu_info(void *g_cpuinfo, uint32_t cpu_count, @@ -112,10 +111,10 @@ static void init_special_info(void *g_special) } void init_loongson_params(struct loongson_params *lp, void *p, - uint32_t cpu_count, + uint32_t cpu_count, uint32_t processor_id, uint64_t cpu_freq, uint64_t ram_size) { - init_cpu_info(p, MIPS_CPU(first_cpu)->env.CP0_PRid, cpu_count, cpu_freq); + init_cpu_info(p, cpu_count, processor_id, cpu_freq); lp->cpu_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp); p += ROUND_UP(sizeof(struct efi_cpuinfo_loongson), 64); diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h index ee6340e42c..9dc325a855 100644 --- a/hw/mips/loongson3_bootp.h +++ b/hw/mips/loongson3_bootp.h @@ -233,7 +233,7 @@ enum { extern const MemMapEntry virt_memmap[]; void init_loongson_params(struct loongson_params *lp, void *p, - uint32_t cpu_count, + uint32_t cpu_count, uint32_t processor_id, uint64_t cpu_freq, uint64_t ram_size); void init_reset_system(struct efi_reset_system_t *reset); diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 5fe5bc6fc0..ee71fe9e9b 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -185,6 +185,7 @@ static void init_boot_param(unsigned cpu_count) init_reset_system(&(bp->reset_system)); p += ROUND_UP(sizeof(struct boot_params), 64); init_loongson_params(&(bp->efi.smbios.lp), p, cpu_count, + MIPS_CPU(first_cpu)->env.CP0_PRid, loaderparams.cpu_freq, loaderparams.ram_size); rom_add_blob_fixed("params_rom", bp, From 92c63c9afdf462e949ca8e74dba6b3abf44f993d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 21:39:54 +0100 Subject: [PATCH 1437/2892] hw/mips/loongson3_virt: Propagate %processor_id to init_boot_param() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate %processor_id from mips_loongson3_virt_init() where we have a reference to the first vCPU, so use it instead of the &first_cpu global. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-11-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index ee71fe9e9b..85cf1f707f 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -173,7 +173,7 @@ static uint64_t get_cpu_freq_hz(const MIPSCPU *cpu) return DEF_LOONGSON3_FREQ; } -static void init_boot_param(unsigned cpu_count) +static void init_boot_param(unsigned cpu_count, uint32_t processor_id) { static void *p; struct boot_params *bp; @@ -184,8 +184,7 @@ static void init_boot_param(unsigned cpu_count) bp->efi.smbios.vers = cpu_to_le16(1); init_reset_system(&(bp->reset_system)); p += ROUND_UP(sizeof(struct boot_params), 64); - init_loongson_params(&(bp->efi.smbios.lp), p, cpu_count, - MIPS_CPU(first_cpu)->env.CP0_PRid, + init_loongson_params(&(bp->efi.smbios.lp), p, cpu_count, processor_id, loaderparams.cpu_freq, loaderparams.ram_size); rom_add_blob_fixed("params_rom", bp, @@ -643,7 +642,7 @@ static void mips_loongson3_virt_init(MachineState *machine) loaderparams.kernel_entry = load_kernel(&cpu->env); init_boot_rom(); - init_boot_param(machine->smp.cpus); + init_boot_param(machine->smp.cpus, cpu->env.CP0_PRid); } else { filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: LOONGSON3_BIOSNAME); From 70bc20311f760da9bd93f20cd7bda7419778f858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Jan 2025 22:53:27 +0100 Subject: [PATCH 1438/2892] hw/mips/loongson3_bootp: Move to common_ss[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit loongson3_bootp.c doesn't contain any target-specific code and can be build generically, move it to common_ss[]. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250115232952.31166-12-philmd@linaro.org> --- hw/mips/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/mips/meson.build b/hw/mips/meson.build index fcbee53bb3..31dbd2bf4d 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -1,7 +1,8 @@ mips_ss = ss.source_set() mips_ss.add(files('bootloader.c', 'mips_int.c')) common_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c')) -mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c')) +common_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c')) +mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_virt.c')) mips_ss.add(when: 'CONFIG_MALTA', if_true: files('malta.c')) mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c')) From c17943b00fd98c64d2fa591f1da72cb9a4f61133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 16:37:02 +0100 Subject: [PATCH 1439/2892] hw/irq: Introduce qemu_init_irqs() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While qemu_init_irq() initialize a single IRQ, qemu_init_irqs() initialize an array of them. Suggested-by: Bernhard Beschow Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121155526.29982-2-philmd@linaro.org> --- hw/core/irq.c | 8 ++++++++ include/hw/irq.h | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/hw/core/irq.c b/hw/core/irq.c index 7d5b0038c1..6dd8d47bd6 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -49,6 +49,14 @@ void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, init_irq_fields(irq, handler, opaque, n); } +void qemu_init_irqs(IRQState irq[], size_t count, + qemu_irq_handler handler, void *opaque) +{ + for (size_t i = 0; i < count; i++) { + qemu_init_irq(&irq[i], handler, opaque, i); + } +} + qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, void *opaque, int n) { diff --git a/include/hw/irq.h b/include/hw/irq.h index c861c1debd..b3012237ac 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -41,6 +41,17 @@ static inline void qemu_irq_pulse(qemu_irq irq) void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, int n); +/** + * qemu_init_irqs: Initialize an array of IRQs. + * + * @irq: Array of IRQs to initialize + * @count: number of IRQs to initialize + * @handler: handler to assign to each IRQ + * @opaque: opaque data to pass to @handler + */ +void qemu_init_irqs(IRQState irq[], size_t count, + qemu_irq_handler handler, void *opaque); + /* Returns an array of N IRQs. Each IRQ is assigned the argument handler and * opaque data. */ From 29df910401a66ee65f553eecebf01993115082d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 08:31:52 +0100 Subject: [PATCH 1440/2892] hw/ipack: Clarify KConfig symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split IPACK Kconfig key as {IPACK, TPCI200, IP_OCTAL_232} - IPack is a bus - TPCI200 is a PCI device providing an IPack bus - IP-Octal232 is an IPack device plugged on an IPack bus Signed-off-by: Philippe Mathieu-Daudé Acked-by: Fabiano Rosas Message-Id: <20250121155526.29982-3-philmd@linaro.org> --- hw/char/Kconfig | 5 +++++ hw/char/meson.build | 2 +- hw/ipack/Kconfig | 4 ++++ hw/ipack/meson.build | 3 ++- tests/qtest/libqos/meson.build | 4 +++- tests/qtest/meson.build | 4 +++- 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 4b73a803bf..1dc20ee4c2 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -78,3 +78,8 @@ config GOLDFISH_TTY config SHAKTI_UART bool + +config IP_OCTAL_232 + bool + default y + depends on IPACK diff --git a/hw/char/meson.build b/hw/char/meson.build index 1750834385..ed3529cbbb 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -4,7 +4,7 @@ system_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) -system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) +system_ss.add(when: 'CONFIG_IP_OCTAL_232', if_true: files('ipoctal232.c')) system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) diff --git a/hw/ipack/Kconfig b/hw/ipack/Kconfig index f8da24a62b..28d668727c 100644 --- a/hw/ipack/Kconfig +++ b/hw/ipack/Kconfig @@ -1,4 +1,8 @@ config IPACK bool + +config TPCI200 + bool + select IPACK default y if PCI_DEVICES depends on PCI diff --git a/hw/ipack/meson.build b/hw/ipack/meson.build index 26567f1068..e480522892 100644 --- a/hw/ipack/meson.build +++ b/hw/ipack/meson.build @@ -1 +1,2 @@ -system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c', 'tpci200.c')) +system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c')) +system_ss.add(when: 'CONFIG_TPCI200', if_true: files('tpci200.c')) diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 46f130ccfd..1ddaf7b095 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -32,7 +32,6 @@ libqos_srcs = files( 'i2c-omap.c', 'igb.c', 'sdhci.c', - 'tpci200.c', 'virtio.c', 'virtio-balloon.c', 'virtio-blk.c', @@ -70,6 +69,9 @@ endif if config_all_devices.has_key('CONFIG_RISCV_IOMMU') libqos_srcs += files('riscv-iommu.c') endif +if config_all_devices.has_key('CONFIG_TPCI200') + libqos_srcs += files('tpci200.c') +endif libqos = static_library('qos', libqos_srcs + genh, build_by_default: false) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 94b28e5a53..e60e92fe9d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -286,7 +286,6 @@ qos_test_ss.add( 'e1000-test.c', 'eepro100-test.c', 'es1370-test.c', - 'ipoctal232-test.c', 'lsm303dlhc-mag-test.c', 'isl_pmbus_vr-test.c', 'max34451-test.c', @@ -317,6 +316,9 @@ qos_test_ss.add( if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') qos_test_ss.add(files('virtio-serial-test.c')) endif +if config_all_devices.has_key('CONFIG_IP_OCTAL_232') + qos_test_ss.add(files('ipoctal232-test.c')) +endif if host_os != 'windows' qos_test_ss.add(files('e1000e-test.c')) From d50280af1bc4b9a59c4a6424c6172a1397f5f6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 08:34:55 +0100 Subject: [PATCH 1441/2892] hw/ipack: Remove legacy qemu_allocate_irqs() use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to dynamically allocate IRQ when we know before hands how many we'll use. Declare the 2 of them in IPackDevice state and initialize them in the DeviceRealize handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121155526.29982-4-philmd@linaro.org> --- hw/char/ipoctal232.c | 4 ++-- hw/ipack/ipack.c | 5 +---- hw/ipack/tpci200.c | 6 +++--- include/hw/ipack/ipack.h | 7 ++----- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index d1e5f6dad2..a2879977fb 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -184,9 +184,9 @@ static void update_irq(IPOctalState *dev, unsigned block) unsigned intno = block / 2; if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) { - qemu_irq_raise(idev->irq[intno]); + qemu_irq_raise(&idev->irq[intno]); } else { - qemu_irq_lower(idev->irq[intno]); + qemu_irq_lower(&idev->irq[intno]); } } diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index ed75f79183..b6defae602 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -55,22 +55,19 @@ static void ipack_device_realize(DeviceState *dev, Error **errp) } bus->free_slot = idev->slot + 1; - idev->irq = qemu_allocate_irqs(bus->set_irq, idev, 2); + qemu_init_irqs(idev->irq, ARRAY_SIZE(idev->irq), bus->set_irq, idev); k->realize(dev, errp); } static void ipack_device_unrealize(DeviceState *dev) { - IPackDevice *idev = IPACK_DEVICE(dev); IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev); if (k->unrealize) { k->unrealize(dev); return; } - - qemu_free_irqs(idev->irq, 2); } static const Property ipack_device_props[] = { diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index 88eef4b830..470a4203ae 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -275,11 +275,11 @@ static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val, if (ip != NULL) { if (val & STATUS_INT(i, 0)) { DPRINTF("Clear IP %c INT0# status\n", 'A' + i); - qemu_irq_lower(ip->irq[0]); + qemu_irq_lower(&ip->irq[0]); } if (val & STATUS_INT(i, 1)) { DPRINTF("Clear IP %c INT1# status\n", 'A' + i); - qemu_irq_lower(ip->irq[1]); + qemu_irq_lower(&ip->irq[1]); } } @@ -344,7 +344,7 @@ static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size) bool int_set = s->status & STATUS_INT(ip_n, intno); bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno); if (int_set && !int_edge_sensitive) { - qemu_irq_lower(ip->irq[intno]); + qemu_irq_lower(&ip->irq[intno]); } } diff --git a/include/hw/ipack/ipack.h b/include/hw/ipack/ipack.h index cbcdda509d..00f397fd02 100644 --- a/include/hw/ipack/ipack.h +++ b/include/hw/ipack/ipack.h @@ -12,6 +12,7 @@ #define QEMU_IPACK_H #include "hw/qdev-core.h" +#include "hw/irq.h" #include "qom/object.h" @@ -19,10 +20,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IPackBus, IPACK_BUS) struct IPackBus { - /*< private >*/ BusState parent_obj; - /* All fields are private */ uint8_t n_slots; uint8_t free_slot; qemu_irq_handler set_irq; @@ -58,13 +57,11 @@ struct IPackDeviceClass { }; struct IPackDevice { - /*< private >*/ DeviceState parent_obj; - /*< public >*/ int32_t slot; /* IRQ objects for the IndustryPack INT0# and INT1# */ - qemu_irq *irq; + IRQState irq[2]; }; extern const VMStateDescription vmstate_ipack_device; From 6b8f40c61bbfef1abe77eeb9c716ec642927c12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 08:51:25 +0100 Subject: [PATCH 1442/2892] hw/sh4/r2d: Convert legacy qemu_allocate_irqs() to qemu_init_irqs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FPGA exposes a fixed set of IRQs. Hold them in the FPGA state and initialize them in place calling qemu_init_irqs(). Move r2d_fpga_irq enums earlier so we can use NR_IRQS within the r2d_fpga_t structure. r2d_fpga_init() returns r2d_fpga_t, and we dereference irq from it in r2d_init(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121182445.35309-1-philmd@linaro.org> --- hw/sh4/r2d.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 2fa439819e..d68c94e82e 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -63,6 +63,12 @@ #define PA_VERREG 0x32 #define PA_OUTPORT 0x36 +enum r2d_fpga_irq { + PCI_INTD, CF_IDE, CF_CD, PCI_INTC, SM501, KEY, RTC_A, RTC_T, + SDCARD, PCI_INTA, PCI_INTB, EXT, TP, + NR_IRQS +}; + typedef struct { uint16_t bcr; uint16_t irlmsk; @@ -88,15 +94,10 @@ typedef struct { /* output pin */ qemu_irq irl; + IRQState irq[NR_IRQS]; MemoryRegion iomem; } r2d_fpga_t; -enum r2d_fpga_irq { - PCI_INTD, CF_IDE, CF_CD, PCI_INTC, SM501, KEY, RTC_A, RTC_T, - SDCARD, PCI_INTA, PCI_INTB, EXT, TP, - NR_IRQS -}; - static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { [CF_IDE] = { 1, 1 << 9 }, [CF_CD] = { 2, 1 << 8 }, @@ -186,8 +187,8 @@ static const MemoryRegionOps r2d_fpga_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irl) +static r2d_fpga_t *r2d_fpga_init(MemoryRegion *sysmem, + hwaddr base, qemu_irq irl) { r2d_fpga_t *s; @@ -197,7 +198,10 @@ static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, memory_region_init_io(&s->iomem, NULL, &r2d_fpga_ops, s, "r2d-fpga", 0x40); memory_region_add_subregion(sysmem, base, &s->iomem); - return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS); + + qemu_init_irqs(s->irq, NR_IRQS, r2d_fpga_irq_set, s); + + return s; } typedef struct ResetData { @@ -239,13 +243,13 @@ static void r2d_init(MachineState *machine) ResetData *reset_info; struct SH7750State *s; MemoryRegion *sdram = g_new(MemoryRegion, 1); - qemu_irq *irq; DriveInfo *dinfo; DeviceState *dev; SysBusDevice *busdev; MemoryRegion *address_space_mem = get_system_memory(); PCIBus *pci_bus; USBBus *usb_bus; + r2d_fpga_t *fpga; cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -260,7 +264,7 @@ static void r2d_init(MachineState *machine) memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram); /* Register peripherals */ s = sh7750_init(cpu, address_space_mem); - irq = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); + fpga = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); dev = qdev_new("sh_pci"); busdev = SYS_BUS_DEVICE(dev); @@ -268,10 +272,10 @@ static void r2d_init(MachineState *machine) pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci")); sysbus_mmio_map(busdev, 0, P4ADDR(0x1e200000)); sysbus_mmio_map(busdev, 1, A7ADDR(0x1e200000)); - sysbus_connect_irq(busdev, 0, irq[PCI_INTA]); - sysbus_connect_irq(busdev, 1, irq[PCI_INTB]); - sysbus_connect_irq(busdev, 2, irq[PCI_INTC]); - sysbus_connect_irq(busdev, 3, irq[PCI_INTD]); + sysbus_connect_irq(busdev, 0, &fpga->irq[PCI_INTA]); + sysbus_connect_irq(busdev, 1, &fpga->irq[PCI_INTB]); + sysbus_connect_irq(busdev, 2, &fpga->irq[PCI_INTC]); + sysbus_connect_irq(busdev, 3, &fpga->irq[PCI_INTD]); dev = qdev_new("sysbus-sm501"); busdev = SYS_BUS_DEVICE(dev); @@ -281,13 +285,13 @@ static void r2d_init(MachineState *machine) sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0x10000000); sysbus_mmio_map(busdev, 1, 0x13e00000); - sysbus_connect_irq(busdev, 0, irq[SM501]); + sysbus_connect_irq(busdev, 0, &fpga->irq[SM501]); /* onboard CF (True IDE mode, Master only). */ dinfo = drive_get(IF_IDE, 0, 0); dev = qdev_new("mmio-ide"); busdev = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(busdev, 0, irq[CF_IDE]); + sysbus_connect_irq(busdev, 0, &fpga->irq[CF_IDE]); qdev_prop_set_uint32(dev, "shift", 1); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0x14001000); From 4ef6d66547cbd1331628530dfe03620bf41e3fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 08:07:21 +0100 Subject: [PATCH 1443/2892] hw/char/pci-multi: Convert legacy qemu_allocate_irqs to qemu_init_irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a fixed number of PCI IRQs, known beforehand. Allocate them within PCIMultiSerialState, and initialize using qemu_init_irq(), allowing to remove the legacy qemu_allocate_irqs() and qemu_free_irqs() calls. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121182828.45088-1-philmd@linaro.org> --- hw/char/serial-pci-multi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 7578e863cf..718ae25131 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -45,7 +45,7 @@ typedef struct PCIMultiSerialState { char *name[PCI_SERIAL_MAX_PORTS]; SerialState state[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS]; - qemu_irq *irqs; + IRQState irqs[PCI_SERIAL_MAX_PORTS]; uint8_t prog_if; } PCIMultiSerialState; @@ -61,7 +61,6 @@ static void multi_serial_pci_exit(PCIDevice *dev) memory_region_del_subregion(&pci->iobar, &s->io); g_free(pci->name[i]); } - qemu_free_irqs(pci->irqs, pci->ports); } static void multi_serial_irq_mux(void *opaque, int n, int level) @@ -102,7 +101,6 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); - pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, nports); for (i = 0; i < nports; i++) { s = pci->state + i; @@ -110,7 +108,7 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) multi_serial_pci_exit(dev); return; } - s->irq = pci->irqs[i]; + s->irq = &pci->irqs[i]; pci->name[i] = g_strdup_printf("uart #%zu", i + 1); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, pci->name[i], 8); @@ -183,6 +181,7 @@ static void multi_serial_init(Object *o) size_t i, nports = multi_serial_get_port_count(PCI_DEVICE_GET_CLASS(dev)); for (i = 0; i < nports; i++) { + qemu_init_irq(&pms->irqs[i], multi_serial_irq_mux, pms, i); object_initialize_child(o, "serial[*]", &pms->state[i], TYPE_SERIAL); } } From fd7d66de5486b5b9ee871f65c88299f84c64b81b Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Tue, 21 Jan 2025 10:59:34 +0000 Subject: [PATCH 1444/2892] hw/misc/i2c-echo: add tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has been useful when debugging and unsure if the guest is generating i2c traffic. Signed-off-by: Titus Rwantare Reviewed-by: Hao Wu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250121105935.3069035-1-titusr@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/i2c-echo.c | 8 ++++++++ hw/misc/trace-events | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c index 5ae3d0817e..65d10029dc 100644 --- a/hw/misc/i2c-echo.c +++ b/hw/misc/i2c-echo.c @@ -13,6 +13,7 @@ #include "qemu/main-loop.h" #include "block/aio.h" #include "hw/i2c/i2c.h" +#include "trace.h" #define TYPE_I2C_ECHO "i2c-echo" OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO) @@ -80,11 +81,13 @@ static int i2c_echo_event(I2CSlave *s, enum i2c_event event) case I2C_START_RECV: state->pos = 0; + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_START_RECV"); break; case I2C_START_SEND: state->pos = 0; + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_START_SEND"); break; case I2C_FINISH: @@ -92,12 +95,15 @@ static int i2c_echo_event(I2CSlave *s, enum i2c_event event) state->state = I2C_ECHO_STATE_START_SEND; i2c_bus_master(state->bus, state->bh); + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_FINISH"); break; case I2C_NACK: + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_NACK"); break; default: + trace_i2c_echo_event(DEVICE(s)->canonical_path, "UNHANDLED"); return -1; } @@ -112,6 +118,7 @@ static uint8_t i2c_echo_recv(I2CSlave *s) return 0xff; } + trace_i2c_echo_recv(DEVICE(s)->canonical_path, state->data[state->pos]); return state->data[state->pos++]; } @@ -119,6 +126,7 @@ static int i2c_echo_send(I2CSlave *s, uint8_t data) { I2CEchoState *state = I2C_ECHO(s); + trace_i2c_echo_send(DEVICE(s)->canonical_path, data); if (state->pos > 2) { return -1; } diff --git a/hw/misc/trace-events b/hw/misc/trace-events index cf1abe6928..b35b0e77f7 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -390,3 +390,8 @@ ivshmem_flat_read_write_mmr_invalid(uint64_t addr_offset) "No ivshmem register m ivshmem_flat_interrupt_invalid_peer(uint16_t peer_id) "Can't interrupt non-existing peer %u" ivshmem_flat_write_mmr(uint64_t addr_offset) "Write access at offset %"PRIu64 ivshmem_flat_interrupt_peer(uint16_t peer_id, uint16_t vector_id) "Interrupting peer ID %u, vector %u..." + +# i2c-echo.c +i2c_echo_event(const char *id, const char *event) "%s: %s" +i2c_echo_recv(const char *id, uint8_t data) "%s: recv 0x%02" PRIx8 +i2c_echo_send(const char *id, uint8_t data) "%s: send 0x%02" PRIx8 From a40b5f32867294b7c855d2e4b98a4c2d32b3be28 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 24 Jan 2025 13:47:13 +0100 Subject: [PATCH 1445/2892] hw/usb/hcd-ehci: Fix debug printf format string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable is uint64_t so needs %PRIu64 instead of %d. Fixes: 3ae7eb88c47 ("ehci: fix overflow in frame timer code") Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250124124713.64F8C4E6031@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ehci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 6c4c14c895..b090f25365 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2287,7 +2287,8 @@ static void ehci_work_bh(void *opaque) ehci_update_frindex(ehci, skipped_uframes); ehci->last_run_ns += UFRAME_TIMER_NS * skipped_uframes; uframes -= skipped_uframes; - DPRINTF("WARNING - EHCI skipped %d uframes\n", skipped_uframes); + DPRINTF("WARNING - EHCI skipped %"PRIu64" uframes\n", + skipped_uframes); } for (i = 0; i < uframes; i++) { From 29c7bd8eaf0384ec0752779ca71fdbb1d6f5d961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 08:23:37 +0100 Subject: [PATCH 1446/2892] hw/avr/boot: Replace load_elf_ram_sym() -> load_elf_as() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_elf_ram_sym() with load_rom=true, sym_cb=NULL is equivalent to load_elf_as(). Replace by the latter to simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250127113824.50177-2-philmd@linaro.org> --- hw/avr/boot.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/avr/boot.c b/hw/avr/boot.c index 617f3a144c..6a91dcd12d 100644 --- a/hw/avr/boot.c +++ b/hw/avr/boot.c @@ -71,11 +71,9 @@ bool avr_load_firmware(AVRCPU *cpu, MachineState *ms, return false; } - bytes_loaded = load_elf_ram_sym(filename, - NULL, NULL, NULL, - &entry, NULL, NULL, - &e_flags, 0, EM_AVR, 0, 0, - NULL, true, NULL); + bytes_loaded = load_elf_as(filename, NULL, NULL, NULL, + &entry, NULL, NULL, + &e_flags, 0, EM_AVR, 0, 0, NULL); if (bytes_loaded >= 0) { /* If ELF file is provided, determine CPU type reading ELF e_flags. */ const char *elf_cpu = avr_elf_e_flags_to_cpu_type(e_flags); From ff12b602fc9077197839f068b5c21beb82c71964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 08:28:02 +0100 Subject: [PATCH 1447/2892] hw/loader: Remove unused load_elf_ram() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last use of load_elf_ram() was removed in commit 188e255bf8e ("hw/s390x: Remove the possibility to load the s390-netboot.img binary"), remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20250127113824.50177-3-philmd@linaro.org> --- hw/core/loader.c | 16 +--------------- include/hw/loader.h | 14 +------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 4dfdb027ee..ead10fb6cb 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -425,26 +425,12 @@ ssize_t load_elf_as(const char *filename, uint64_t *highaddr, uint32_t *pflags, int big_endian, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) -{ - return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, - elf_machine, clear_lsb, data_swab, as, true); -} - -/* return < 0 if error, otherwise the number of bytes loaded in memory */ -ssize_t load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, int clear_lsb, - int data_swab, AddressSpace *as, bool load_rom) { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, pentry, lowaddr, highaddr, pflags, big_endian, elf_machine, clear_lsb, data_swab, as, - load_rom, NULL); + true, NULL); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ diff --git a/include/hw/loader.h b/include/hw/loader.h index 8985046be4..9bb34e6f06 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -155,20 +155,8 @@ ssize_t load_elf_ram_sym(const char *filename, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); -/** load_elf_ram: - * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a - * symbol callback function - */ -ssize_t load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, int clear_lsb, - int data_swab, AddressSpace *as, bool load_rom); - /** load_elf_as: - * Same as load_elf_ram(), but always loads the elf as ROM + * Same as load_elf_ram_sym(), but always loads the elf as ROM */ ssize_t load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), From 518f8fdfe265ffff6e2f2ad7a7bbb7f95b270434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:54:00 +0100 Subject: [PATCH 1448/2892] hw/loader: Clarify local variable name in load_elf_ram_sym() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_elf_ram_sym() compares target_data_order versus host data_order. Rename 'data_order' -> 'host_data_order' to ease code review. Avoid the preprocessor by directly checking HOST_BIG_ENDIAN. Reviewed-by: BALATON Zoltan Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250127113824.50177-4-philmd@linaro.org> --- hw/core/loader.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index ead10fb6cb..de6b173f4a 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -443,7 +443,8 @@ ssize_t load_elf_ram_sym(const char *filename, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { - int fd, data_order, target_data_order, must_swab; + const int host_data_order = HOST_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB; + int fd, target_data_order, must_swab; ssize_t ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -461,12 +462,7 @@ ssize_t load_elf_ram_sym(const char *filename, ret = ELF_LOAD_NOT_ELF; goto fail; } -#if HOST_BIG_ENDIAN - data_order = ELFDATA2MSB; -#else - data_order = ELFDATA2LSB; -#endif - must_swab = data_order != e_ident[EI_DATA]; + must_swab = host_data_order != e_ident[EI_DATA]; if (big_endian) { target_data_order = ELFDATA2MSB; } else { From 3cd6dbce752d0cd78ab85a7f476b7ddc6933e0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 15:03:04 +0100 Subject: [PATCH 1449/2892] hw/loader: Pass ELFDATA endian order argument to load_elf_ram_sym() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than passing a boolean 'is_big_endian' argument, directly pass the ELFDATA, which can be unspecified using the ELFDATANONE value. Update the call sites: 0 -> ELFDATA2LSB Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250127113824.50177-5-philmd@linaro.org> --- hw/core/loader.c | 17 +++++++---------- hw/riscv/boot.c | 3 ++- include/hw/loader.h | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index de6b173f4a..f1fab3e91b 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -428,7 +428,8 @@ ssize_t load_elf_as(const char *filename, { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, + pentry, lowaddr, highaddr, pflags, + big_endian ? ELFDATA2MSB : ELFDATA2LSB, elf_machine, clear_lsb, data_swab, as, true, NULL); } @@ -439,12 +440,12 @@ ssize_t load_elf_ram_sym(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int big_endian, int elf_machine, + uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { const int host_data_order = HOST_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB; - int fd, target_data_order, must_swab; + int fd, must_swab; ssize_t ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -462,18 +463,14 @@ ssize_t load_elf_ram_sym(const char *filename, ret = ELF_LOAD_NOT_ELF; goto fail; } - must_swab = host_data_order != e_ident[EI_DATA]; - if (big_endian) { - target_data_order = ELFDATA2MSB; - } else { - target_data_order = ELFDATA2LSB; - } - if (target_data_order != e_ident[EI_DATA]) { + if (elf_data_order != ELFDATANONE && elf_data_order != e_ident[EI_DATA]) { ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } + must_swab = host_data_order != e_ident[EI_DATA]; + lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, elf_note_fn, diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 90e75c69a0..c309441b7d 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -246,7 +246,8 @@ void riscv_load_kernel(MachineState *machine, */ kernel_size = load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL, &info->image_low_addr, &info->image_high_addr, - NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb); + NULL, ELFDATA2LSB, EM_RISCV, + 1, 0, NULL, true, sym_cb); if (kernel_size > 0) { info->kernel_size = kernel_size; goto out; diff --git a/include/hw/loader.h b/include/hw/loader.h index 9bb34e6f06..8202c37604 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -120,7 +120,7 @@ const char *load_elf_strerror(ssize_t error); * @lowaddr: Populated with lowest loaded address. Ignored if NULL. * @highaddr: Populated with highest loaded address. Ignored if NULL. * @pflags: Populated with ELF processor-specific flags. Ignore if NULL. - * @bigendian: Expected ELF endianness. 0 for LE otherwise BE + * @elf_data_order: Expected ELF endianness (ELFDATA2LSB or ELFDATA2MSB). * @elf_machine: Expected ELF machine type * @clear_lsb: Set to mask off LSB of addresses (Some architectures use * this for non-address data) @@ -151,7 +151,7 @@ ssize_t load_elf_ram_sym(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int big_endian, int elf_machine, + uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); From 90f5c86acbfcd1f19fd1245c96aa2c51be31361a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 15:09:52 +0100 Subject: [PATCH 1450/2892] hw/loader: Pass ELFDATA endian order argument to load_elf_as() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than passing a boolean 'is_big_endian' argument, directly pass the ELFDATA, which can be unspecified using the ELFDATANONE value. Update the call sites: 0 -> ELFDATA2LSB 1 -> ELFDATA2MSB Note, this allow removing the target_words_bigendian() call in the GENERIC_LOADER device, where we pass ELFDATANONE. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250127113824.50177-6-philmd@linaro.org> --- hw/arm/armv7m.c | 2 +- hw/arm/boot.c | 16 ++++++++-------- hw/avr/boot.c | 2 +- hw/core/generic-loader.c | 6 +----- hw/core/loader.c | 8 ++++---- include/hw/loader.h | 2 +- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index a67a890a33..98a6984611 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -608,7 +608,7 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, if (kernel_filename) { image_size = load_elf_as(kernel_filename, NULL, NULL, NULL, &entry, NULL, NULL, - NULL, 0, EM_ARM, 1, 0, as); + NULL, ELFDATA2LSB, EM_ARM, 1, 0, as); if (image_size < 0) { image_size = load_image_targphys_as(kernel_filename, mem_base, mem_size, as); diff --git a/hw/arm/boot.c b/hw/arm/boot.c index b44bea8a82..cbc24356fc 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -798,7 +798,7 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, Elf64_Ehdr h64; } elf_header; int data_swab = 0; - bool big_endian; + int elf_data_order; ssize_t ret; Error *err = NULL; @@ -814,12 +814,12 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } if (elf_is64) { - big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB; - info->endianness = big_endian ? ARM_ENDIANNESS_BE8 - : ARM_ENDIANNESS_LE; + elf_data_order = elf_header.h64.e_ident[EI_DATA]; + info->endianness = elf_data_order == ELFDATA2MSB ? ARM_ENDIANNESS_BE8 + : ARM_ENDIANNESS_LE; } else { - big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB; - if (big_endian) { + elf_data_order = elf_header.h32.e_ident[EI_DATA]; + if (elf_data_order == ELFDATA2MSB) { if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) { info->endianness = ARM_ENDIANNESS_BE8; } else { @@ -839,8 +839,8 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } ret = load_elf_as(info->kernel_filename, NULL, NULL, NULL, - pentry, lowaddr, highaddr, NULL, big_endian, elf_machine, - 1, data_swab, as); + pentry, lowaddr, highaddr, NULL, elf_data_order, + elf_machine, 1, data_swab, as); if (ret <= 0) { /* The header loaded but the image didn't */ error_report("Couldn't load elf '%s': %s", diff --git a/hw/avr/boot.c b/hw/avr/boot.c index 6a91dcd12d..e5a29c7218 100644 --- a/hw/avr/boot.c +++ b/hw/avr/boot.c @@ -73,7 +73,7 @@ bool avr_load_firmware(AVRCPU *cpu, MachineState *ms, bytes_loaded = load_elf_as(filename, NULL, NULL, NULL, &entry, NULL, NULL, - &e_flags, 0, EM_AVR, 0, 0, NULL); + &e_flags, ELFDATA2LSB, EM_AVR, 0, 0, NULL); if (bytes_loaded >= 0) { /* If ELF file is provided, determine CPU type reading ELF e_flags. */ const char *elf_cpu = avr_elf_e_flags_to_cpu_type(e_flags); diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index fb354693af..d9f5c2e832 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -31,7 +31,6 @@ */ #include "qemu/osdep.h" -#include "exec/tswap.h" #include "system/dma.h" #include "system/reset.h" #include "hw/boards.h" @@ -66,7 +65,6 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) { GenericLoaderState *s = GENERIC_LOADER(dev); hwaddr entry; - int big_endian; ssize_t size = 0; s->set_pc = false; @@ -134,14 +132,12 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) s->cpu = first_cpu; } - big_endian = target_words_bigendian(); - if (s->file) { AddressSpace *as = s->cpu ? s->cpu->as : NULL; if (!s->force_raw) { size = load_elf_as(s->file, NULL, NULL, NULL, &entry, NULL, NULL, - NULL, big_endian, 0, 0, 0, as); + NULL, ELFDATANONE, 0, 0, 0, as); if (size < 0) { size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL, diff --git a/hw/core/loader.c b/hw/core/loader.c index f1fab3e91b..cc0631e7dd 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -413,7 +413,8 @@ ssize_t load_elf(const char *filename, int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, + pentry, lowaddr, highaddr, pflags, + big_endian ? ELFDATA2MSB : ELFDATA2LSB, elf_machine, clear_lsb, data_swab, NULL); } @@ -422,14 +423,13 @@ ssize_t load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, - big_endian ? ELFDATA2MSB : ELFDATA2LSB, + pentry, lowaddr, highaddr, pflags, elf_data_order, elf_machine, clear_lsb, data_swab, as, true, NULL); } diff --git a/include/hw/loader.h b/include/hw/loader.h index 8202c37604..84737c05b8 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -162,7 +162,7 @@ ssize_t load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as); From adc1a4a26a9e060c76f213e2b5bf275519844068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 18:01:41 +0100 Subject: [PATCH 1451/2892] hw/loader: Pass ELFDATA endian order argument to load_elf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than passing a boolean 'is_big_endian' argument, directly pass the ELFDATA, which can be unspecified using the ELFDATANONE value. Update the call sites: 0 -> ELFDATA2LSB 1 -> ELFDATA2MSB TARGET_BIG_ENDIAN -> TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: BALATON Zoltan Reviewed-by: Richard Henderson Message-Id: <20250127113824.50177-7-philmd@linaro.org> --- hw/alpha/dp264.c | 4 ++-- hw/core/loader.c | 5 ++--- hw/hppa/machine.c | 4 ++-- hw/i386/multiboot.c | 4 ++-- hw/i386/x86-common.c | 4 ++-- hw/loongarch/boot.c | 2 +- hw/m68k/an5206.c | 2 +- hw/m68k/mcf5208.c | 2 +- hw/m68k/q800.c | 2 +- hw/m68k/virt.c | 2 +- hw/microblaze/boot.c | 6 ++++-- hw/mips/boston.c | 2 +- hw/mips/fuloong2e.c | 2 +- hw/mips/loongson3_virt.c | 2 +- hw/mips/malta.c | 5 +++-- hw/mips/mipssim.c | 3 ++- hw/openrisc/boot.c | 2 +- hw/pci-host/raven.c | 4 ++-- hw/ppc/e500.c | 2 +- hw/ppc/mac_newworld.c | 5 +++-- hw/ppc/mac_oldworld.c | 4 ++-- hw/ppc/pegasos2.c | 8 ++++---- hw/ppc/ppc405_boards.c | 2 +- hw/ppc/ppc440_bamboo.c | 3 ++- hw/ppc/sam460ex.c | 2 +- hw/ppc/spapr.c | 8 ++++---- hw/ppc/virtex_ml507.c | 4 ++-- hw/s390x/ipl.c | 6 +++--- hw/sparc/leon3.c | 2 +- hw/sparc/sun4m.c | 5 +++-- hw/sparc64/sun4u.c | 6 +++--- hw/tricore/triboard.c | 2 +- hw/tricore/tricore_testboard.c | 2 +- hw/xtensa/sim.c | 3 ++- hw/xtensa/xtfpga.c | 3 ++- include/hw/loader.h | 2 +- 36 files changed, 67 insertions(+), 59 deletions(-) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 52a1fa310b..570ea9edf2 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -144,7 +144,7 @@ static void clipper_init(MachineState *machine) } size = load_elf(palcode_filename, NULL, cpu_alpha_superpage_to_phys, NULL, &palcode_entry, NULL, NULL, NULL, - 0, EM_ALPHA, 0, 0); + ELFDATA2LSB, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load palcode '%s'", palcode_filename); exit(1); @@ -163,7 +163,7 @@ static void clipper_init(MachineState *machine) size = load_elf(kernel_filename, NULL, cpu_alpha_superpage_to_phys, NULL, &kernel_entry, &kernel_low, NULL, NULL, - 0, EM_ALPHA, 0, 0); + ELFDATA2LSB, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); exit(1); diff --git a/hw/core/loader.c b/hw/core/loader.c index cc0631e7dd..fd25c5e01b 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -409,12 +409,11 @@ ssize_t load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, - big_endian ? ELFDATA2MSB : ELFDATA2LSB, + pentry, lowaddr, highaddr, pflags, elf_data_order, elf_machine, clear_lsb, data_swab, NULL); } diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 0dd1908214..b6135d9526 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -440,7 +440,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, size = load_elf(firmware_filename, NULL, translate, NULL, &firmware_entry, &firmware_low, &firmware_high, NULL, - true, EM_PARISC, 0, 0); + ELFDATA2MSB, EM_PARISC, 0, 0); if (size < 0) { error_report("could not load firmware '%s'", firmware_filename); @@ -467,7 +467,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, if (kernel_filename) { size = load_elf(kernel_filename, NULL, linux_kernel_virt_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, NULL, - true, EM_PARISC, 0, 0); + ELFDATA2MSB, EM_PARISC, 0, 0); kernel_entry = linux_kernel_virt_to_phys(NULL, kernel_entry); diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 1d66ca3204..cd07a05861 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -202,8 +202,8 @@ int load_multiboot(X86MachineState *x86ms, } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, - 0, 0); + &elf_low, &elf_high, NULL, + ELFDATA2LSB, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { error_report("Error while loading elf kernel"); exit(1); diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 008496b5b8..1b0671c523 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -608,8 +608,8 @@ static bool load_elfboot(const char *kernel_filename, uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; kernel_size = load_elf(kernel_filename, read_pvh_start_addr, NULL, &elf_note_type, &elf_entry, - &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, - 0, 0); + &elf_low, &elf_high, NULL, + ELFDATA2LSB, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { error_report("Error while loading elf kernel"); diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index bd8763c61c..354cf458c8 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -243,7 +243,7 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) kernel_size = load_elf(info->kernel_filename, NULL, cpu_loongarch_virt_to_phys, NULL, &kernel_entry, &kernel_low, - &kernel_high, NULL, 0, + &kernel_high, NULL, ELFDATA2LSB, EM_LOONGARCH, 1, 0); if (kernel_size < 0) { kernel_size = load_loongarch_linux_image(info->kernel_filename, diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 7b8210475e..d97399b882 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -74,7 +74,7 @@ static void an5206_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, NULL, 1, EM_68K, 0, 0); + NULL, NULL, NULL, ELFDATA2MSB, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 409bb72574..75cc076f78 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -372,7 +372,7 @@ static void mcf5208evb_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, NULL, 1, EM_68K, 0, 0); + NULL, NULL, NULL, ELFDATA2MSB, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index ca3adb9a8a..aeed4c8ddb 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -585,7 +585,7 @@ static void q800_machine_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high, NULL, 1, + &elf_entry, NULL, &high, NULL, ELFDATA2MSB, EM_68K, 0, 0); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 87ec39eeae..d967bdd743 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -228,7 +228,7 @@ static void virt_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high, NULL, 1, + &elf_entry, NULL, &high, NULL, ELFDATA2MSB, EM_68K, 0, 0); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 3854bc2291..60b4ef0abe 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -144,13 +144,15 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, &high, NULL, - !is_little_endian, EM_MICROBLAZE, 0, 0); + is_little_endian ? ELFDATA2LSB : ELFDATA2MSB, + EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, &entry, NULL, NULL, NULL, - !is_little_endian, EM_MICROBLAZE, 0, 0); + is_little_endian ? ELFDATA2LSB : ELFDATA2MSB, + EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = (uint32_t)entry; diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 67044af962..364c328032 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -792,7 +792,7 @@ static void boston_mach_init(MachineState *machine) kernel_size = load_elf(machine->kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, &kernel_high, - NULL, 0, EM_MIPS, 1, 0); + NULL, ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size > 0) { int dt_size; diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 16b6a5129e..646044e274 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -106,7 +106,7 @@ static uint64_t load_kernel(MIPSCPU *cpu) cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, &kernel_high, NULL, - 0, EM_MIPS, 1, 0); + ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 85cf1f707f..831fddb1bd 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -358,7 +358,7 @@ static uint64_t load_kernel(CPUMIPSState *env) cpu_mips_kseg0_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, - NULL, 0, EM_MIPS, 1, 0); + NULL, ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 4e9cccaa34..8e9cea70b1 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -880,8 +880,9 @@ static uint64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, - &kernel_high, NULL, TARGET_BIG_ENDIAN, EM_MIPS, - 1, 0); + &kernel_high, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, + EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index a294779a82..c530688e76 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -73,7 +73,8 @@ static uint64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &entry, NULL, - &kernel_high, NULL, TARGET_BIG_ENDIAN, + &kernel_high, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 83c1fc6705..0f08df812d 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -32,7 +32,7 @@ hwaddr openrisc_load_kernel(ram_addr_t ram_size, if (kernel_filename && !qtest_enabled()) { kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high_addr, NULL, 1, + &elf_entry, NULL, &high_addr, NULL, ELFDATA2MSB, EM_OPENRISC, 1, 0); entry = elf_entry; if (kernel_size < 0) { diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 918a3237a9..e3d8d206b7 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -357,8 +357,8 @@ static void raven_realize(PCIDevice *d, Error **errp) if (filename) { if (s->elf_machine != EM_NONE) { bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, 1, s->elf_machine, - 0, 0); + NULL, NULL, NULL, + ELFDATA2MSB, s->elf_machine, 0, 0); } if (bios_size < 0) { bios_size = get_image_size(filename); diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 4551157c01..26933e0457 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1194,7 +1194,7 @@ void ppce500_init(MachineState *machine) payload_size = load_elf(filename, NULL, NULL, NULL, &bios_entry, &loadaddr, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (payload_size < 0) { /* * Hrm. No ELF image? Try a uImage, maybe someone is giving us an diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 6369961f78..cb3dc3ab48 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -182,7 +182,8 @@ static void ppc_core99_init(MachineState *machine) if (filename) { /* Load OpenBIOS (ELF) */ bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (bios_size <= 0) { /* or load binary ROM image */ @@ -204,7 +205,7 @@ static void ppc_core99_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 59653e174b..0dbcea035c 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -136,7 +136,7 @@ static void ppc_heathrow_init(MachineState *machine) if (filename) { /* Load OpenBIOS (ELF) */ bios_size = load_elf(filename, NULL, NULL, NULL, NULL, &bios_addr, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32 */ bios_addr = (uint32_t)bios_addr; @@ -161,7 +161,7 @@ static void ppc_heathrow_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b3c21bdc57..0364243f4f 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -160,8 +160,8 @@ static void pegasos2_init(MachineState *machine) } memory_region_init_rom(rom, NULL, "pegasos2.rom", PROM_SIZE, &error_fatal); memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); - sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, - PPC_ELF_MACHINE, 0, 0); + sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (sz <= 0) { sz = load_image_targphys(filename, pm->vof ? 0 : PROM_ADDR, PROM_SIZE); } @@ -239,8 +239,8 @@ static void pegasos2_init(MachineState *machine) if (machine->kernel_filename) { sz = load_elf(machine->kernel_filename, NULL, NULL, NULL, - &pm->kernel_entry, &pm->kernel_addr, NULL, NULL, 1, - PPC_ELF_MACHINE, 0, 0); + &pm->kernel_entry, &pm->kernel_addr, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (sz <= 0) { error_report("Could not load kernel '%s'", machine->kernel_filename); diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index e9f65fab70..969cac345a 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -232,7 +232,7 @@ static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu) kernel_size = load_elf(machine->kernel_filename, NULL, NULL, NULL, &boot_entry, &kernel_base, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) { error_report("Could not load kernel '%s' : %s", machine->kernel_filename, load_elf_strerror(kernel_size)); diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 45c5b8678d..099fda3909 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -228,7 +228,8 @@ static void bamboo_init(MachineState *machine) if (success < 0) { uint64_t elf_entry; success = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; } /* XXX try again as binary */ diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index e74642a3b7..3ecae6a950 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -479,7 +479,7 @@ static void sam460ex_init(MachineState *machine) success = load_elf(machine->kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; } /* XXX try again as binary */ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 623842f806..f3a4b4235d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3022,13 +3022,13 @@ static void spapr_machine_init(MachineState *machine) spapr->kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, spapr, - NULL, &loaded_addr, NULL, NULL, 1, - PPC_ELF_MACHINE, 0, 0); + NULL, &loaded_addr, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) { spapr->kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, spapr, - NULL, &loaded_addr, NULL, NULL, 0, - PPC_ELF_MACHINE, 0, 0); + NULL, &loaded_addr, NULL, NULL, + ELFDATA2LSB, PPC_ELF_MACHINE, 0, 0); spapr->kernel_le = spapr->kernel_size > 0; } if (spapr->kernel_size < 0) { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index ea7ab8a569..2323811927 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -242,8 +242,8 @@ static void virtex_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, &high, NULL, 1, PPC_ELF_MACHINE, - 0, 0); + &entry, NULL, &high, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; if (kernel_size < 0) { diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 4aa21c91fc..ce6f6078d7 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -162,8 +162,8 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) bios_size = load_elf(bios_filename, NULL, bios_translate_addr, &fwbase, - &ipl->bios_start_addr, NULL, NULL, NULL, 1, - EM_S390, 0, 0); + &ipl->bios_start_addr, NULL, NULL, NULL, + ELFDATA2MSB, EM_S390, 0, 0); if (bios_size > 0) { /* Adjust ELF start address to final location */ ipl->bios_start_addr += fwbase; @@ -187,7 +187,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->kernel) { kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, &pentry, NULL, - NULL, NULL, 1, EM_S390, 0, 0); + NULL, NULL, ELFDATA2MSB, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ms->ram_size); if (kernel_size < 0) { diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 84381254ad..0aeaad3bec 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -380,7 +380,7 @@ static void leon3_generic_hw_init(MachineState *machine) kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, NULL, NULL, - 1 /* big endian */, EM_SPARC, 0, 0); + ELFDATA2MSB, EM_SPARC, 0, 0); if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, NULL, &entry, NULL, NULL, NULL); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index e070360a2c..a48d3622c5 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -242,7 +242,8 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, #endif kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - NULL, NULL, NULL, NULL, 1, EM_SPARC, 0, 0); + NULL, NULL, NULL, NULL, + ELFDATA2MSB, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, RAM_size - KERNEL_LOAD_ADDR, bswap_needed, @@ -703,7 +704,7 @@ static void prom_init(hwaddr addr, const char *bios_name) if (filename) { ret = load_elf(filename, NULL, translate_prom_address, &addr, NULL, - NULL, NULL, NULL, 1, EM_SPARC, 0, 0); + NULL, NULL, NULL, ELFDATA2MSB, EM_SPARC, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 0980b44659..8ab5cf0461 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -176,8 +176,8 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, bswap_needed = 0; #endif kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry, - kernel_addr, &kernel_top, NULL, 1, EM_SPARCV9, 0, - 0); + kernel_addr, &kernel_top, NULL, + ELFDATA2MSB, EM_SPARCV9, 0, 0); if (kernel_size < 0) { *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; @@ -441,7 +441,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, NULL, translate_prom_address, &addr, - NULL, NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); + NULL, NULL, NULL, NULL, ELFDATA2MSB, EM_SPARCV9, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index 9cc8d282ff..f5baa8ccbb 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -39,7 +39,7 @@ static void tricore_load_kernel(TriCoreCPU *cpu, const char *kernel_filename) kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, - NULL, NULL, 0, + NULL, NULL, ELFDATA2LSB, EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("no kernel file '%s'", kernel_filename); diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index c29db8b451..3facfdfd61 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -42,7 +42,7 @@ static void tricore_load_kernel(CPUTriCoreState *env) kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, NULL, NULL, &entry, NULL, - NULL, NULL, 0, + NULL, NULL, ELFDATA2LSB, EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("no kernel file '%s'", diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 0a1fd90037..1cea29c66d 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -100,7 +100,8 @@ void xtensa_sim_load_kernel(XtensaCPU *cpu, MachineState *machine) if (kernel_filename) { uint64_t elf_entry; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, NULL, NULL, NULL, TARGET_BIG_ENDIAN, + &elf_entry, NULL, NULL, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, EM_XTENSA, 0, 0); if (success > 0) { diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 9efe91933f..3f3677f1c9 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -398,7 +398,8 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) uint64_t elf_entry; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, NULL, NULL, NULL, TARGET_BIG_ENDIAN, + &elf_entry, NULL, NULL, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; diff --git a/include/hw/loader.h b/include/hw/loader.h index 84737c05b8..784a93d6c1 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -174,7 +174,7 @@ ssize_t load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab); /** load_elf_hdr: From 4cadcb6b5f90bf0e9d0d23ad7552a0857dc593e7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:09 +0000 Subject: [PATCH 1452/2892] hw/sd/omap_mmc: Do a minimal conversion to QDev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do a minimal conversion of the omap_mmc device model to QDev. In this commit we do the bare minimum to produce a working device: * add the SysBusDevice parent_obj and the usual type boilerplate * omap_mmc_init() now returns a DeviceState* * reset is handled by sysbus reset, so the SoC reset function doesn't need to call omap_mmc_reset() any more * code that should obviously be in init/realize is moved there from omap_mmc_init() We leave various pieces of cleanup to later commits: * rationalizing 'struct omap_mmc_s *' to 'OMAPMMCState *' * using gpio lines rather than having omap_mmc_init() directly set s->irq, s->dma * switching away from the legacy SD API and instead having the SD card plugged into a bus Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-2-peter.maydell@linaro.org> [PMD: Do not add omap_mmc_realize()] Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 1 - hw/sd/omap_mmc.c | 77 ++++++++++++++++++++++++++++++++++--------- include/hw/arm/omap.h | 15 +++++---- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index f3a0ac40e4..ea07b9aa31 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -3716,7 +3716,6 @@ static void omap1_mpu_reset(void *opaque) omap_uart_reset(mpu->uart[0]); omap_uart_reset(mpu->uart[1]); omap_uart_reset(mpu->uart[2]); - omap_mmc_reset(mpu->mmc); omap_mpuio_reset(mpu->mpuio); omap_uwire_reset(mpu->microwire); omap_pwl_reset(mpu->pwl); diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 1d4e30e6b7..fec2cfd4d6 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -21,11 +21,15 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qapi/error.h" #include "hw/irq.h" +#include "hw/sysbus.h" #include "hw/arm/omap.h" #include "hw/sd/sdcard_legacy.h" -struct omap_mmc_s { +typedef struct omap_mmc_s { + SysBusDevice parent_obj; + qemu_irq irq; qemu_irq *dma; qemu_irq coverswitch; @@ -66,7 +70,7 @@ struct omap_mmc_s { int cdet_enable; int cdet_state; qemu_irq cdet; -}; +} OMAPMMCState; static void omap_mmc_interrupts_update(struct omap_mmc_s *s) { @@ -297,7 +301,7 @@ static void omap_mmc_pseudo_reset(struct omap_mmc_s *host) host->fifo_len = 0; } -void omap_mmc_reset(struct omap_mmc_s *host) +static void omap_mmc_reset(struct omap_mmc_s *host) { host->last_cmd = 0; memset(host->rsp, 0, sizeof(host->rsp)); @@ -328,7 +332,9 @@ void omap_mmc_reset(struct omap_mmc_s *host) * into any bus, and we must reset it manually. When omap_mmc is * QOMified this must move into the QOM reset function. */ - device_cold_reset(DEVICE(host->card)); + if (host->card) { + device_cold_reset(DEVICE(host->card)); + } } static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) @@ -583,29 +589,70 @@ static const MemoryRegionOps omap_mmc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -struct omap_mmc_s *omap_mmc_init(hwaddr base, - MemoryRegion *sysmem, - BlockBackend *blk, - qemu_irq irq, qemu_irq dma[], omap_clk clk) +DeviceState *omap_mmc_init(hwaddr base, + MemoryRegion *sysmem, + BlockBackend *blk, + qemu_irq irq, qemu_irq dma[], omap_clk clk) { - struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); + DeviceState *dev; + OMAPMMCState *s; + + dev = qdev_new(TYPE_OMAP_MMC); + s = OMAP_MMC(dev); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); s->irq = irq; s->dma = dma; s->clk = clk; - s->lines = 1; /* TODO: needs to be settable per-board */ - s->rev = 1; - memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800); - memory_region_add_subregion(sysmem, base, &s->iomem); + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(s), 0)); /* Instantiate the storage */ s->card = sd_init(blk, false); if (s->card == NULL) { exit(1); } + return dev; +} + +static void omap_mmc_reset_hold(Object *obj, ResetType type) +{ + OMAPMMCState *s = OMAP_MMC(obj); omap_mmc_reset(s); - - return s; } + +static void omap_mmc_initfn(Object *obj) +{ + OMAPMMCState *s = OMAP_MMC(obj); + + /* In theory these could be settable per-board */ + s->lines = 1; + s->rev = 1; + + memory_region_init_io(&s->iomem, obj, &omap_mmc_ops, s, "omap.mmc", 0x800); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void omap_mmc_class_init(ObjectClass *oc, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(oc); + + rc->phases.hold = omap_mmc_reset_hold; +} + +static const TypeInfo omap_mmc_info = { + .name = TYPE_OMAP_MMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OMAPMMCState), + .instance_init = omap_mmc_initfn, + .class_init = omap_mmc_class_init, +}; + +static void omap_mmc_register_types(void) +{ + type_register_static(&omap_mmc_info); +} + +type_init(omap_mmc_register_types) diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 420ed1d573..6339c5a581 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -529,12 +529,13 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, omap_clk clk); /* omap_mmc.c */ -struct omap_mmc_s; -struct omap_mmc_s *omap_mmc_init(hwaddr base, - MemoryRegion *sysmem, - BlockBackend *blk, - qemu_irq irq, qemu_irq dma[], omap_clk clk); -void omap_mmc_reset(struct omap_mmc_s *s); +#define TYPE_OMAP_MMC "omap-mmc" +OBJECT_DECLARE_SIMPLE_TYPE(omap_mmc_s, OMAP_MMC) + +DeviceState *omap_mmc_init(hwaddr base, + MemoryRegion *sysmem, + BlockBackend *blk, + qemu_irq irq, qemu_irq dma[], omap_clk clk); /* omap_i2c.c */ I2CBus *omap_i2c_bus(DeviceState *omap_i2c); @@ -601,7 +602,7 @@ struct omap_mpu_state_s { /* MPU public TIPB peripherals */ struct omap_32khz_timer_s *os_timer; - struct omap_mmc_s *mmc; + DeviceState *mmc; struct omap_mpuio_s *mpuio; From 408ccf5fd60a281b90f846d48d1cf36a14c04232 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:10 +0000 Subject: [PATCH 1453/2892] hw/sd/omap_mmc: Convert remaining 'struct omap_mmc_s' uses to OMAPMMCState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanically convert the remaining uses of 'struct omap_mmc_s' to 'OMAPMMCState'. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/omap_mmc.c | 20 ++++++++++---------- include/hw/arm/omap.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index fec2cfd4d6..0f17479ecb 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -27,7 +27,7 @@ #include "hw/arm/omap.h" #include "hw/sd/sdcard_legacy.h" -typedef struct omap_mmc_s { +typedef struct OMAPMMCState { SysBusDevice parent_obj; qemu_irq irq; @@ -72,12 +72,12 @@ typedef struct omap_mmc_s { qemu_irq cdet; } OMAPMMCState; -static void omap_mmc_interrupts_update(struct omap_mmc_s *s) +static void omap_mmc_interrupts_update(OMAPMMCState *s) { qemu_set_irq(s->irq, !!(s->status & s->mask)); } -static void omap_mmc_fifolevel_update(struct omap_mmc_s *host) +static void omap_mmc_fifolevel_update(OMAPMMCState *host) { if (!host->transfer && !host->fifo_len) { host->status &= 0xf3ff; @@ -125,7 +125,7 @@ typedef enum { SD_TYPE_ADTC = 3, /* addressed with data transfer */ } MMCCmdType; -static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, +static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, MMCCmdType type, int busy, sd_rsp_type_t resptype, int init) { @@ -234,7 +234,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, host->status |= 0x0001; } -static void omap_mmc_transfer(struct omap_mmc_s *host) +static void omap_mmc_transfer(OMAPMMCState *host) { uint8_t value; @@ -289,19 +289,19 @@ static void omap_mmc_transfer(struct omap_mmc_s *host) static void omap_mmc_update(void *opaque) { - struct omap_mmc_s *s = opaque; + OMAPMMCState *s = opaque; omap_mmc_transfer(s); omap_mmc_fifolevel_update(s); omap_mmc_interrupts_update(s); } -static void omap_mmc_pseudo_reset(struct omap_mmc_s *host) +static void omap_mmc_pseudo_reset(OMAPMMCState *host) { host->status = 0; host->fifo_len = 0; } -static void omap_mmc_reset(struct omap_mmc_s *host) +static void omap_mmc_reset(OMAPMMCState *host) { host->last_cmd = 0; memset(host->rsp, 0, sizeof(host->rsp)); @@ -340,7 +340,7 @@ static void omap_mmc_reset(struct omap_mmc_s *host) static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) { uint16_t i; - struct omap_mmc_s *s = opaque; + OMAPMMCState *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, offset); @@ -433,7 +433,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { int i; - struct omap_mmc_s *s = opaque; + OMAPMMCState *s = opaque; if (size != 2) { omap_badwidth_write16(opaque, offset, value); diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 6339c5a581..7d1a1afc4f 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -530,7 +530,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, /* omap_mmc.c */ #define TYPE_OMAP_MMC "omap-mmc" -OBJECT_DECLARE_SIMPLE_TYPE(omap_mmc_s, OMAP_MMC) +OBJECT_DECLARE_SIMPLE_TYPE(OMAPMMCState, OMAP_MMC) DeviceState *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, From 68b48857640e4ffce96bdf691de5cb68e96595bf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:11 +0000 Subject: [PATCH 1454/2892] hw/sd/omap_mmc: Convert output qemu_irqs to gpio and sysbus IRQ APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The omap_mmc device has three outbound qemu_irq lines: * one actual interrupt line * two which connect to the DMA controller and are signalled for TX and RX DMA Convert these to a sysbus IRQ and two named GPIO outputs. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/omap_mmc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 0f17479ecb..43203a76c5 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -31,7 +31,8 @@ typedef struct OMAPMMCState { SysBusDevice parent_obj; qemu_irq irq; - qemu_irq *dma; + qemu_irq dma_tx_gpio; + qemu_irq dma_rx_gpio; qemu_irq coverswitch; MemoryRegion iomem; omap_clk clk; @@ -87,22 +88,22 @@ static void omap_mmc_fifolevel_update(OMAPMMCState *host) if (host->fifo_len > host->af_level && host->ddir) { if (host->rx_dma) { host->status &= 0xfbff; - qemu_irq_raise(host->dma[1]); + qemu_irq_raise(host->dma_rx_gpio); } else host->status |= 0x0400; } else { host->status &= 0xfbff; - qemu_irq_lower(host->dma[1]); + qemu_irq_lower(host->dma_rx_gpio); } if (host->fifo_len < host->ae_level && !host->ddir) { if (host->tx_dma) { host->status &= 0xf7ff; - qemu_irq_raise(host->dma[0]); + qemu_irq_raise(host->dma_tx_gpio); } else host->status |= 0x0800; } else { - qemu_irq_lower(host->dma[0]); + qemu_irq_lower(host->dma_tx_gpio); host->status &= 0xf7ff; } } @@ -601,12 +602,13 @@ DeviceState *omap_mmc_init(hwaddr base, s = OMAP_MMC(dev); sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); - s->irq = irq; - s->dma = dma; s->clk = clk; memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(SYS_BUS_DEVICE(s), 0)); + qdev_connect_gpio_out_named(dev, "dma-tx", 0, dma[0]); + qdev_connect_gpio_out_named(dev, "dma-rx", 0, dma[1]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); /* Instantiate the storage */ s->card = sd_init(blk, false); @@ -633,6 +635,10 @@ static void omap_mmc_initfn(Object *obj) memory_region_init_io(&s->iomem, obj, &omap_mmc_ops, s, "omap.mmc", 0x800); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + qdev_init_gpio_out_named(DEVICE(obj), &s->dma_tx_gpio, "dma-tx", 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->dma_rx_gpio, "dma-rx", 1); } static void omap_mmc_class_init(ObjectClass *oc, void *data) From 0c90811231a9afc67d5d1119d3db0dcc8552f54d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:12 +0000 Subject: [PATCH 1455/2892] hw/sd/omap_mmc: Convert to SDBus API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the OMAP MMC controller to the new SDBus API: * the controller creates an SDBus bus * instead of sd_foo functions on the SDState object, call sdbus_foo functions on the SDBus * the board code creates a proper TYPE_SD_CARD object and attaches it to the controller's SDBus, instead of the controller creating a card directly via sd_init() that never gets attached to any bus * because the SD card object is on a bus, it gets reset automatically by the "traverse the qbus tree resetting things" code, and we don't need to manually reset the card from the controller reset function Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-5-peter.maydell@linaro.org> [PMD: Include "hw/sd/sd.h" instead of "hw/sd/sdcard_legacy.h", create bus in omap_mmc_initfn() instead of omap_mmc_realize()] Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 10 +++++++++- hw/sd/omap_mmc.c | 31 ++++++++++--------------------- include/hw/arm/omap.h | 1 - 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index ea07b9aa31..15ba0a0d0c 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -29,6 +29,7 @@ #include "hw/qdev-properties.h" #include "hw/arm/boot.h" #include "hw/arm/omap.h" +#include "hw/sd/sd.h" #include "system/blockdev.h" #include "system/system.h" #include "hw/arm/soc_dma.h" @@ -3981,11 +3982,18 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, warn_report("missing SecureDigital device"); } s->mmc = omap_mmc_init(0xfffb7800, system_memory, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN), &s->drq[OMAP_DMA_MMC_TX], omap_findclk(s, "mmc_ck")); + if (dinfo) { + DeviceState *card = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + qdev_realize_and_unref(card, qdev_get_child_bus(s->mmc, "sd-bus"), + &error_fatal); + } + s->mpuio = omap_mpuio_init(system_memory, 0xfffb5000, qdev_get_gpio_in(s->ih[1], OMAP_INT_KEYBOARD), qdev_get_gpio_in(s->ih[1], OMAP_INT_MPUIO), diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 43203a76c5..1bc8290f9d 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -25,18 +25,19 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/arm/omap.h" -#include "hw/sd/sdcard_legacy.h" +#include "hw/sd/sd.h" typedef struct OMAPMMCState { SysBusDevice parent_obj; + SDBus sdbus; + qemu_irq irq; qemu_irq dma_tx_gpio; qemu_irq dma_rx_gpio; qemu_irq coverswitch; MemoryRegion iomem; omap_clk clk; - SDState *card; uint16_t last_cmd; uint16_t sdio; uint16_t rsp[8]; @@ -158,7 +159,7 @@ static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, request.arg = host->arg; request.crc = 0; /* FIXME */ - rsplen = sd_do_command(host->card, &request, response); + rsplen = sdbus_do_command(&host->sdbus, &request, response); /* TODO: validate CRCs */ switch (resptype) { @@ -247,10 +248,10 @@ static void omap_mmc_transfer(OMAPMMCState *host) if (host->fifo_len > host->af_level) break; - value = sd_read_byte(host->card); + value = sdbus_read_byte(&host->sdbus); host->fifo[(host->fifo_start + host->fifo_len) & 31] = value; if (-- host->blen_counter) { - value = sd_read_byte(host->card); + value = sdbus_read_byte(&host->sdbus); host->fifo[(host->fifo_start + host->fifo_len) & 31] |= value << 8; host->blen_counter --; @@ -262,10 +263,10 @@ static void omap_mmc_transfer(OMAPMMCState *host) break; value = host->fifo[host->fifo_start] & 0xff; - sd_write_byte(host->card, value); + sdbus_write_byte(&host->sdbus, value); if (-- host->blen_counter) { value = host->fifo[host->fifo_start] >> 8; - sd_write_byte(host->card, value); + sdbus_write_byte(&host->sdbus, value); host->blen_counter --; } @@ -328,14 +329,6 @@ static void omap_mmc_reset(OMAPMMCState *host) host->clkdiv = 0; omap_mmc_pseudo_reset(host); - - /* Since we're still using the legacy SD API the card is not plugged - * into any bus, and we must reset it manually. When omap_mmc is - * QOMified this must move into the QOM reset function. - */ - if (host->card) { - device_cold_reset(DEVICE(host->card)); - } } static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) @@ -592,7 +585,6 @@ static const MemoryRegionOps omap_mmc_ops = { DeviceState *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, - BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk clk) { DeviceState *dev; @@ -610,11 +602,6 @@ DeviceState *omap_mmc_init(hwaddr base, qdev_connect_gpio_out_named(dev, "dma-rx", 0, dma[1]); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - /* Instantiate the storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } return dev; } @@ -639,6 +626,8 @@ static void omap_mmc_initfn(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); qdev_init_gpio_out_named(DEVICE(obj), &s->dma_tx_gpio, "dma-tx", 1); qdev_init_gpio_out_named(DEVICE(obj), &s->dma_rx_gpio, "dma-rx", 1); + + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(obj), "sd-bus"); } static void omap_mmc_class_init(ObjectClass *oc, void *data) diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 7d1a1afc4f..d20c55a895 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -534,7 +534,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(OMAPMMCState, OMAP_MMC) DeviceState *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, - BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk clk); /* omap_i2c.c */ From 3102d81fc707550675be7af0c49da7c4299e9af9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:13 +0000 Subject: [PATCH 1456/2892] hw/sd/omap_mmc: Use similar API for "wire up omap_clk" to other OMAP devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The approach we've settled on for handling the omap_clk wiring for OMAP devices converted to QDev is to have a function omap_foo_set_clk() whose implementation just sets the field directly in the device's state struct. (See the "TODO" comment near the top of omap.h.) Make omap_mmc do the same. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-6-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/omap_mmc.c | 9 ++++++++- include/hw/arm/omap.h | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 1bc8290f9d..c6b8cf65d7 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -583,6 +583,13 @@ static const MemoryRegionOps omap_mmc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +void omap_mmc_set_clk(DeviceState *dev, omap_clk clk) +{ + OMAPMMCState *s = OMAP_MMC(dev); + + s->clk = clk; +} + DeviceState *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, qemu_irq irq, qemu_irq dma[], omap_clk clk) @@ -594,7 +601,7 @@ DeviceState *omap_mmc_init(hwaddr base, s = OMAP_MMC(dev); sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); - s->clk = clk; + omap_mmc_set_clk(dev, clk); memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(SYS_BUS_DEVICE(s), 0)); diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index d20c55a895..7cb87ea89c 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -535,6 +535,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(OMAPMMCState, OMAP_MMC) DeviceState *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, qemu_irq irq, qemu_irq dma[], omap_clk clk); +/* TODO: clock framework (see above) */ +void omap_mmc_set_clk(DeviceState *dev, omap_clk clk); + /* omap_i2c.c */ I2CBus *omap_i2c_bus(DeviceState *omap_i2c); From 9c2fce87fc5218c0181abd9d8eb1dd7858103375 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:14 +0000 Subject: [PATCH 1457/2892] hw/arm/omap1: Inline creation of MMC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our style for other conversions of OMAP devices to qdev has been to inline the creation and wiring into omap310_mpu_init() -- see for instance the handling of omap-intc, omap-gpio and omap_i2c. Do the same for omap-mmc. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-7-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 15 +++++++++++---- hw/sd/omap_mmc.c | 22 ---------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 15ba0a0d0c..ca2eb0d157 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -3981,10 +3981,17 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, if (!dinfo && !qtest_enabled()) { warn_report("missing SecureDigital device"); } - s->mmc = omap_mmc_init(0xfffb7800, system_memory, - qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN), - &s->drq[OMAP_DMA_MMC_TX], - omap_findclk(s, "mmc_ck")); + + s->mmc = qdev_new(TYPE_OMAP_MMC); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s->mmc), &error_fatal); + omap_mmc_set_clk(s->mmc, omap_findclk(s, "mmc_ck")); + + memory_region_add_subregion(system_memory, 0xfffb7800, + sysbus_mmio_get_region(SYS_BUS_DEVICE(s->mmc), 0)); + qdev_connect_gpio_out_named(s->mmc, "dma-tx", 0, s->drq[OMAP_DMA_MMC_TX]); + qdev_connect_gpio_out_named(s->mmc, "dma-rx", 0, s->drq[OMAP_DMA_MMC_RX]); + sysbus_connect_irq(SYS_BUS_DEVICE(s->mmc), 0, + qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN)); if (dinfo) { DeviceState *card = qdev_new(TYPE_SD_CARD); diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index c6b8cf65d7..a8b541ca78 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -590,28 +590,6 @@ void omap_mmc_set_clk(DeviceState *dev, omap_clk clk) s->clk = clk; } -DeviceState *omap_mmc_init(hwaddr base, - MemoryRegion *sysmem, - qemu_irq irq, qemu_irq dma[], omap_clk clk) -{ - DeviceState *dev; - OMAPMMCState *s; - - dev = qdev_new(TYPE_OMAP_MMC); - s = OMAP_MMC(dev); - sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); - - omap_mmc_set_clk(dev, clk); - - memory_region_add_subregion(sysmem, base, - sysbus_mmio_get_region(SYS_BUS_DEVICE(s), 0)); - qdev_connect_gpio_out_named(dev, "dma-tx", 0, dma[0]); - qdev_connect_gpio_out_named(dev, "dma-rx", 0, dma[1]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - static void omap_mmc_reset_hold(Object *obj, ResetType type) { OMAPMMCState *s = OMAP_MMC(obj); From d44515d1862139d0456ed71209088fd3c0cf438e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:15 +0000 Subject: [PATCH 1458/2892] hw/sd/omap_mmc: Remove unused coverswitch qemu_irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The coverswitch qemu_irq is never connected to anything, and the only thing we do with it is set it in omap_mmc_reset(). Remove it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-8-peter.maydell@linaro.org> [PMD: Remove unused 'coverswitch' field] Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/omap_mmc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index a8b541ca78..18016a2f2e 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -35,7 +35,6 @@ typedef struct OMAPMMCState { qemu_irq irq; qemu_irq dma_tx_gpio; qemu_irq dma_rx_gpio; - qemu_irq coverswitch; MemoryRegion iomem; omap_clk clk; uint16_t last_cmd; @@ -70,7 +69,6 @@ typedef struct OMAPMMCState { int cdet_wakeup; int cdet_enable; - int cdet_state; qemu_irq cdet; } OMAPMMCState; @@ -325,7 +323,6 @@ static void omap_mmc_reset(OMAPMMCState *host) host->transfer = 0; host->cdet_wakeup = 0; host->cdet_enable = 0; - qemu_set_irq(host->coverswitch, host->cdet_state); host->clkdiv = 0; omap_mmc_pseudo_reset(host); From 1671542fcefe6e00c4e039ecf1799a164f596b61 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:16 +0000 Subject: [PATCH 1459/2892] hw/sd/omap_mmc: Untabify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a very old source file, and still has some lingering hard-coded tabs; untabify it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-9-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/omap_mmc.c | 124 +++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 18016a2f2e..bbe7b971bb 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -109,11 +109,11 @@ static void omap_mmc_fifolevel_update(OMAPMMCState *host) /* These must match the encoding of the MMC_CMD Response field */ typedef enum { - sd_nore = 0, /* no response */ - sd_r1, /* normal response command */ - sd_r2, /* CID, CSD registers */ - sd_r3, /* OCR register */ - sd_r6 = 6, /* Published RCA response */ + sd_nore = 0, /* no response */ + sd_r1, /* normal response command */ + sd_r2, /* CID, CSD registers */ + sd_r3, /* OCR register */ + sd_r6 = 6, /* Published RCA response */ sd_r1b = -1, } sd_rsp_type_t; @@ -229,7 +229,7 @@ static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, if (timeout) host->status |= 0x0080; else if (cmd == 12) - host->status |= 0x0005; /* Makes it more real */ + host->status |= 0x0005; /* Makes it more real */ else host->status |= 0x0001; } @@ -338,32 +338,32 @@ static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) } switch (offset) { - case 0x00: /* MMC_CMD */ + case 0x00: /* MMC_CMD */ return s->last_cmd; - case 0x04: /* MMC_ARGL */ + case 0x04: /* MMC_ARGL */ return s->arg & 0x0000ffff; - case 0x08: /* MMC_ARGH */ + case 0x08: /* MMC_ARGH */ return s->arg >> 16; - case 0x0c: /* MMC_CON */ + case 0x0c: /* MMC_CON */ return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) | (s->be << 10) | s->clkdiv; - case 0x10: /* MMC_STAT */ + case 0x10: /* MMC_STAT */ return s->status; - case 0x14: /* MMC_IE */ + case 0x14: /* MMC_IE */ return s->mask; - case 0x18: /* MMC_CTO */ + case 0x18: /* MMC_CTO */ return s->cto; - case 0x1c: /* MMC_DTO */ + case 0x1c: /* MMC_DTO */ return s->dto; - case 0x20: /* MMC_DATA */ + case 0x20: /* MMC_DATA */ /* TODO: support 8-bit access */ i = s->fifo[s->fifo_start]; if (s->fifo_len == 0) { @@ -378,42 +378,42 @@ static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) omap_mmc_interrupts_update(s); return i; - case 0x24: /* MMC_BLEN */ + case 0x24: /* MMC_BLEN */ return s->blen_counter; - case 0x28: /* MMC_NBLK */ + case 0x28: /* MMC_NBLK */ return s->nblk_counter; - case 0x2c: /* MMC_BUF */ + case 0x2c: /* MMC_BUF */ return (s->rx_dma << 15) | (s->af_level << 8) | (s->tx_dma << 7) | s->ae_level; - case 0x30: /* MMC_SPI */ + case 0x30: /* MMC_SPI */ return 0x0000; - case 0x34: /* MMC_SDIO */ + case 0x34: /* MMC_SDIO */ return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio; - case 0x38: /* MMC_SYST */ + case 0x38: /* MMC_SYST */ return 0x0000; - case 0x3c: /* MMC_REV */ + case 0x3c: /* MMC_REV */ return s->rev; - case 0x40: /* MMC_RSP0 */ - case 0x44: /* MMC_RSP1 */ - case 0x48: /* MMC_RSP2 */ - case 0x4c: /* MMC_RSP3 */ - case 0x50: /* MMC_RSP4 */ - case 0x54: /* MMC_RSP5 */ - case 0x58: /* MMC_RSP6 */ - case 0x5c: /* MMC_RSP7 */ + case 0x40: /* MMC_RSP0 */ + case 0x44: /* MMC_RSP1 */ + case 0x48: /* MMC_RSP2 */ + case 0x4c: /* MMC_RSP3 */ + case 0x50: /* MMC_RSP4 */ + case 0x54: /* MMC_RSP5 */ + case 0x58: /* MMC_RSP6 */ + case 0x5c: /* MMC_RSP7 */ return s->rsp[(offset - 0x40) >> 2]; /* OMAP2-specific */ - case 0x60: /* MMC_IOSR */ - case 0x64: /* MMC_SYSC */ + case 0x60: /* MMC_IOSR */ + case 0x64: /* MMC_SYSC */ return 0; - case 0x68: /* MMC_SYSS */ - return 1; /* RSTD */ + case 0x68: /* MMC_SYSS */ + return 1; /* RSTD */ } OMAP_BAD_REG(offset); @@ -432,7 +432,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset, } switch (offset) { - case 0x00: /* MMC_CMD */ + case 0x00: /* MMC_CMD */ if (!s->enable) break; @@ -447,17 +447,17 @@ static void omap_mmc_write(void *opaque, hwaddr offset, omap_mmc_update(s); break; - case 0x04: /* MMC_ARGL */ + case 0x04: /* MMC_ARGL */ s->arg &= 0xffff0000; s->arg |= 0x0000ffff & value; break; - case 0x08: /* MMC_ARGH */ + case 0x08: /* MMC_ARGH */ s->arg &= 0x0000ffff; s->arg |= value << 16; break; - case 0x0c: /* MMC_CON */ + case 0x0c: /* MMC_CON */ s->dw = (value >> 15) & 1; s->mode = (value >> 12) & 3; s->enable = (value >> 11) & 1; @@ -477,27 +477,27 @@ static void omap_mmc_write(void *opaque, hwaddr offset, omap_mmc_pseudo_reset(s); break; - case 0x10: /* MMC_STAT */ + case 0x10: /* MMC_STAT */ s->status &= ~value; omap_mmc_interrupts_update(s); break; - case 0x14: /* MMC_IE */ + case 0x14: /* MMC_IE */ s->mask = value & 0x7fff; omap_mmc_interrupts_update(s); break; - case 0x18: /* MMC_CTO */ + case 0x18: /* MMC_CTO */ s->cto = value & 0xff; if (s->cto > 0xfd && s->rev <= 1) printf("MMC: CTO of 0xff and 0xfe cannot be used!\n"); break; - case 0x1c: /* MMC_DTO */ + case 0x1c: /* MMC_DTO */ s->dto = value & 0xffff; break; - case 0x20: /* MMC_DATA */ + case 0x20: /* MMC_DATA */ /* TODO: support 8-bit access */ if (s->fifo_len == 32) break; @@ -508,18 +508,18 @@ static void omap_mmc_write(void *opaque, hwaddr offset, omap_mmc_interrupts_update(s); break; - case 0x24: /* MMC_BLEN */ + case 0x24: /* MMC_BLEN */ s->blen = (value & 0x07ff) + 1; s->blen_counter = s->blen; break; - case 0x28: /* MMC_NBLK */ + case 0x28: /* MMC_NBLK */ s->nblk = (value & 0x07ff) + 1; s->nblk_counter = s->nblk; s->blen_counter = s->blen; break; - case 0x2c: /* MMC_BUF */ + case 0x2c: /* MMC_BUF */ s->rx_dma = (value >> 15) & 1; s->af_level = (value >> 8) & 0x1f; s->tx_dma = (value >> 7) & 1; @@ -534,38 +534,38 @@ static void omap_mmc_write(void *opaque, hwaddr offset, break; /* SPI, SDIO and TEST modes unimplemented */ - case 0x30: /* MMC_SPI (OMAP1 only) */ + case 0x30: /* MMC_SPI (OMAP1 only) */ break; - case 0x34: /* MMC_SDIO */ + case 0x34: /* MMC_SDIO */ s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020); s->cdet_wakeup = (value >> 9) & 1; s->cdet_enable = (value >> 2) & 1; break; - case 0x38: /* MMC_SYST */ + case 0x38: /* MMC_SYST */ break; - case 0x3c: /* MMC_REV */ - case 0x40: /* MMC_RSP0 */ - case 0x44: /* MMC_RSP1 */ - case 0x48: /* MMC_RSP2 */ - case 0x4c: /* MMC_RSP3 */ - case 0x50: /* MMC_RSP4 */ - case 0x54: /* MMC_RSP5 */ - case 0x58: /* MMC_RSP6 */ - case 0x5c: /* MMC_RSP7 */ + case 0x3c: /* MMC_REV */ + case 0x40: /* MMC_RSP0 */ + case 0x44: /* MMC_RSP1 */ + case 0x48: /* MMC_RSP2 */ + case 0x4c: /* MMC_RSP3 */ + case 0x50: /* MMC_RSP4 */ + case 0x54: /* MMC_RSP5 */ + case 0x58: /* MMC_RSP6 */ + case 0x5c: /* MMC_RSP7 */ OMAP_RO_REG(offset); break; /* OMAP2-specific */ - case 0x60: /* MMC_IOSR */ + case 0x60: /* MMC_IOSR */ if (value & 0xf) printf("MMC: SDIO bits used!\n"); break; - case 0x64: /* MMC_SYSC */ - if (value & (1 << 2)) /* SRTS */ + case 0x64: /* MMC_SYSC */ + if (value & (1 << 2)) /* SRTS */ omap_mmc_reset(s); break; - case 0x68: /* MMC_SYSS */ + case 0x68: /* MMC_SYSS */ OMAP_RO_REG(offset); break; From 08726e8259c09592cd7415ebae55c43a12d9657d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:17 +0000 Subject: [PATCH 1460/2892] hw/sd: Remove unused 'enable' method from SDCardClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDCardClass has an 'enable' method, but nothing actually invokes it. The underlying implementation is sd_enable(), which is documented in sdcard_legacy.h as something that should not be used and was only present for the benefit of the now-removed nseries boards. Unlike all the other method pointers in SDCardClass, this one doesn't have an sdbus_foo() function wrapper in hw/sd/core.c. Remove the unused method. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-10-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 1 - include/hw/sd/sd.h | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0330d432fd..f781fd1641 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2831,7 +2831,6 @@ static void sdmmc_common_class_init(ObjectClass *klass, void *data) sc->read_byte = sd_read_byte; sc->receive_ready = sd_receive_ready; sc->data_ready = sd_data_ready; - sc->enable = sd_enable; sc->get_inserted = sd_get_inserted; sc->get_readonly = sd_get_readonly; } diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index f2458f37b3..d6bad17513 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -119,7 +119,6 @@ struct SDCardClass { void (*set_voltage)(SDState *sd, uint16_t millivolts); uint8_t (*get_dat_lines)(SDState *sd); bool (*get_cmd_line)(SDState *sd); - void (*enable)(SDState *sd, bool enable); bool (*get_inserted)(SDState *sd); bool (*get_readonly)(SDState *sd); void (*set_cid)(SDState *sd); From bda21477f523622a2c904a3fa80fd6b485b22bff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:18 +0000 Subject: [PATCH 1461/2892] hw/sd: Remove unused legacy functions, stop killing mammoths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sdcard_legacy.h header defines function prototypes for the "legacy" SD card API, which was used by non-qdevified SD controller models. We've now converted the only remaining non-qdev SD controller, so we can drop the legacy API. Entirely unused functions: sd_init(), sd_set_cb(), sd_enable() Functions which now become static inside sd.c (they are the underlying implementations of methods on SDCardClass): sd_do_command(), sd_write_byte(), sd_read_byte() Removal of sd_init() means that we can also remove the me_no_qdev_me_kill_mammoth_with_rocks flag, the codepaths that were only reachable when it was set, and the inserted_cb and readonly_cb qemu_irq lines that went with that. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-ID: <20250128104519.3981448-11-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 77 ++++------------------------------- include/hw/sd/sdcard_legacy.h | 50 ----------------------- 2 files changed, 8 insertions(+), 119 deletions(-) delete mode 100644 include/hw/sd/sdcard_legacy.h diff --git a/hw/sd/sd.c b/hw/sd/sd.c index f781fd1641..74bb7f39bb 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -39,7 +39,6 @@ #include "hw/registerfields.h" #include "system/block-backend.h" #include "hw/sd/sd.h" -#include "hw/sd/sdcard_legacy.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/bitmap.h" @@ -120,10 +119,6 @@ typedef struct SDProto { struct SDState { DeviceState parent_obj; - /* If true, created by sd_init() for a non-qdevified caller */ - /* TODO purge them with fire */ - bool me_no_qdev_me_kill_mammoth_with_rocks; - /* SD Memory Card Registers */ uint32_t ocr; uint8_t scr[8]; @@ -177,8 +172,6 @@ struct SDState { uint32_t data_offset; size_t data_size; uint8_t data[512]; - qemu_irq readonly_cb; - qemu_irq inserted_cb; QEMUTimer *ocr_power_timer; bool enable; uint8_t dat_lines; @@ -892,17 +885,10 @@ static void sd_cardchange(void *opaque, bool load, Error **errp) trace_sdcard_ejected(); } - if (sd->me_no_qdev_me_kill_mammoth_with_rocks) { - qemu_set_irq(sd->inserted_cb, inserted); - if (inserted) { - qemu_set_irq(sd->readonly_cb, readonly); - } - } else { - sdbus = SD_BUS(qdev_get_parent_bus(dev)); - sdbus_set_inserted(sdbus, inserted); - if (inserted) { - sdbus_set_readonly(sdbus, readonly); - } + sdbus = SD_BUS(qdev_get_parent_bus(dev)); + sdbus_set_inserted(sdbus, inserted); + if (inserted) { + sdbus_set_readonly(sdbus, readonly); } } @@ -1000,48 +986,6 @@ static const VMStateDescription sd_vmstate = { }, }; -/* Legacy initialization function for use by non-qdevified callers */ -SDState *sd_init(BlockBackend *blk, bool is_spi) -{ - Object *obj; - DeviceState *dev; - SDState *sd; - Error *err = NULL; - - obj = object_new(is_spi ? TYPE_SD_CARD_SPI : TYPE_SD_CARD); - dev = DEVICE(obj); - if (!qdev_prop_set_drive_err(dev, "drive", blk, &err)) { - error_reportf_err(err, "sd_init failed: "); - return NULL; - } - - /* - * Realizing the device properly would put it into the QOM - * composition tree even though it is not plugged into an - * appropriate bus. That's a no-no. Hide the device from - * QOM/qdev, and call its qdev realize callback directly. - */ - object_ref(obj); - object_unparent(obj); - sd_realize(dev, &err); - if (err) { - error_reportf_err(err, "sd_init failed: "); - return NULL; - } - - sd = SD_CARD(dev); - sd->me_no_qdev_me_kill_mammoth_with_rocks = true; - return sd; -} - -void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) -{ - sd->readonly_cb = readonly; - sd->inserted_cb = insert; - qemu_set_irq(readonly, sd->blk ? !blk_is_writable(sd->blk) : 0); - qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0); -} - static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_read_block(addr, len); @@ -2196,8 +2140,8 @@ static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) return cmd_class == 0 || cmd_class == 7; } -int sd_do_command(SDState *sd, SDRequest *req, - uint8_t *response) { +static int sd_do_command(SDState *sd, SDRequest *req, + uint8_t *response) { int last_state; sd_rsp_type_t rtype; int rsplen; @@ -2349,7 +2293,7 @@ static bool sd_generic_read_byte(SDState *sd, uint8_t *value) return false; } -void sd_write_byte(SDState *sd, uint8_t value) +static void sd_write_byte(SDState *sd, uint8_t value) { int i; @@ -2478,7 +2422,7 @@ void sd_write_byte(SDState *sd, uint8_t value) } } -uint8_t sd_read_byte(SDState *sd) +static uint8_t sd_read_byte(SDState *sd) { /* TODO: Append CRCs */ const uint8_t dummy_byte = 0x00; @@ -2561,11 +2505,6 @@ static bool sd_data_ready(SDState *sd) return sd->state == sd_sendingdata_state; } -void sd_enable(SDState *sd, bool enable) -{ - sd->enable = enable; -} - static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { diff --git a/include/hw/sd/sdcard_legacy.h b/include/hw/sd/sdcard_legacy.h deleted file mode 100644 index 0dc3889555..0000000000 --- a/include/hw/sd/sdcard_legacy.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SD Memory Card emulation (deprecated legacy API) - * - * Copyright (c) 2006 Andrzej Zaborowski - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef HW_SDCARD_LEGACY_H -#define HW_SDCARD_LEGACY_H - -#include "hw/sd/sd.h" - -/* Legacy functions to be used only by non-qdevified callers */ -SDState *sd_init(BlockBackend *blk, bool is_spi); -int sd_do_command(SDState *card, SDRequest *request, uint8_t *response); -void sd_write_byte(SDState *card, uint8_t value); -uint8_t sd_read_byte(SDState *card); -void sd_set_cb(SDState *card, qemu_irq readonly, qemu_irq insert); - -/* sd_enable should not be used -- it is only used on the nseries boards, - * where it is part of a broken implementation of the MMC card slot switch - * (there should be two card slots which are multiplexed to a single MMC - * controller, but instead we model it with one card and controller and - * disable the card when the second slot is selected, so it looks like the - * second slot is always empty). - */ -void sd_enable(SDState *card, bool enable); - -#endif /* HW_SDCARD_LEGACY_H */ From aa0cbf85f3f0b949f61e7aead9902fb6eaee29a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jan 2025 10:45:19 +0000 Subject: [PATCH 1462/2892] hw/sd: Remove unused SDState::enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that sd_enable() has been removed, SD::enable is set to true in sd_instance_init() and then never changed. So we can remove it. Note that the VMSTATE_UNUSED() size argument should be '1', not 'sizeof(bool)', as noted in the CAUTION comment in vmstate.h. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250128104519.3981448-12-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 74bb7f39bb..e541c57f8c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -173,7 +173,6 @@ struct SDState { size_t data_size; uint8_t data[512]; QEMUTimer *ocr_power_timer; - bool enable; uint8_t dat_lines; bool cmd_line; }; @@ -292,12 +291,12 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) static uint8_t sd_get_dat_lines(SDState *sd) { - return sd->enable ? sd->dat_lines : 0; + return sd->dat_lines; } static bool sd_get_cmd_line(SDState *sd) { - return sd->enable ? sd->cmd_line : false; + return sd->cmd_line; } static void sd_set_voltage(SDState *sd, uint16_t millivolts) @@ -976,7 +975,7 @@ static const VMStateDescription sd_vmstate = { VMSTATE_UINT32(data_offset, SDState), VMSTATE_UINT8_ARRAY(data, SDState, 512), VMSTATE_UNUSED_V(1, 512), - VMSTATE_BOOL(enable, SDState), + VMSTATE_UNUSED(1), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { @@ -2146,7 +2145,7 @@ static int sd_do_command(SDState *sd, SDRequest *req, sd_rsp_type_t rtype; int rsplen; - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) { + if (!sd->blk || !blk_is_inserted(sd->blk)) { return 0; } @@ -2297,8 +2296,9 @@ static void sd_write_byte(SDState *sd, uint8_t value) { int i; - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) + if (!sd->blk || !blk_is_inserted(sd->blk)) { return; + } if (sd->state != sd_receivingdata_state) { qemu_log_mask(LOG_GUEST_ERROR, @@ -2429,8 +2429,9 @@ static uint8_t sd_read_byte(SDState *sd) uint8_t ret; uint32_t io_len; - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) + if (!sd->blk || !blk_is_inserted(sd->blk)) { return dummy_byte; + } if (sd->state != sd_sendingdata_state) { qemu_log_mask(LOG_GUEST_ERROR, @@ -2664,7 +2665,6 @@ static void sd_instance_init(Object *obj) sd->proto = sc->proto; sd->last_cmd_name = "UNSET"; - sd->enable = true; sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd); } From 4e059a9d966703e27c9f15e47c9b5e94509c5538 Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Thu, 23 Jan 2025 09:53:21 +0100 Subject: [PATCH 1463/2892] chardev/char-pty: send CHR_EVENT_CLOSED on disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change makes code symmetric to the code, which handles the "connected" state, i.e. send CHR_EVENT_CLOSED when state changes from "connected" to "disconnected". This behavior is similar to char-socket, for example. Signed-off-by: Roman Penyaev Reviewed-by: "Marc-André Lureau" Reviewed-by: "Alex Bennée" Cc: qemu-devel@nongnu.org Message-ID: <20250123085327.965501-2-r.peniaev@gmail.com> --- chardev/char-pty.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chardev/char-pty.c b/chardev/char-pty.c index cbb21b76ae..6a2c1dc13a 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -181,6 +181,9 @@ static void pty_chr_state(Chardev *chr, int connected) if (!connected) { remove_fd_in_watch(chr); + if (s->connected) { + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + } s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to @@ -215,7 +218,6 @@ static void char_pty_finalize(Object *obj) pty_chr_state(chr, 0); object_unref(OBJECT(s->ioc)); pty_chr_timer_cancel(s); - qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } #if defined HAVE_PTY_H From b66ed232383162dc52d6a1c996599541860d1f1a Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Thu, 23 Jan 2025 09:53:22 +0100 Subject: [PATCH 1464/2892] chardev/char-hub: implement backend chardev aggregator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements a new chardev backend `hub` device, which aggregates input from multiple backend devices and forwards it to a single frontend device. Additionally, `hub` device takes the output from the frontend device and sends it back to all the connected backend devices. This allows for seamless interaction between different backend devices and a single frontend interface. The idea of the change is trivial: keep list of backend devices (up to 4), init them on demand and forward data buffer back and forth. The following is QEMU command line example: -chardev pty,path=/tmp/pty,id=pty0 \ -chardev vc,id=vc0 \ -chardev hub,id=hub0,chardevs.0=pty0,chardevs.1=vc0 \ -device virtconsole,chardev=hub0 \ -vnc 0.0.0.0:0 Which creates 2 backend devices: text virtual console (`vc0`) and a pseudo TTY (`pty0`) connected to the single virtio hvc console with the backend aggregator (`hub0`) help. `vc0` renders text to an image, which can be shared over the VNC protocol. `pty0` is a pseudo TTY backend which provides biderectional communication to the virtio hvc console. 'chardevs.N' list syntax is used for the sake of compatibility with the representation of JSON lists in 'key=val' pairs format of the util/keyval.c, despite the fact that modern QAPI way of parsing, namely qobject_input_visitor_new_str(), is not used. Choice of keeping QAPI list syntax may help to smoothly switch to modern parsing in the future. Signed-off-by: Roman Penyaev Reviewed-by: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Message-ID: <20250123085327.965501-3-r.peniaev@gmail.com> --- chardev/char-hub.c | 301 +++++++++++++++++++++++++++++++++++++ chardev/char.c | 23 ++- chardev/chardev-internal.h | 51 ++++++- chardev/meson.build | 1 + include/chardev/char.h | 1 + qapi/char.json | 27 ++++ 6 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 chardev/char-hub.c diff --git a/chardev/char-hub.c b/chardev/char-hub.c new file mode 100644 index 0000000000..3a4aae3289 --- /dev/null +++ b/chardev/char-hub.c @@ -0,0 +1,301 @@ +/* + * QEMU Character Hub Device + * + * Author: Roman Penyaev + * + * 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 "qapi/error.h" +#include "qemu/option.h" +#include "chardev/char.h" +#include "chardev-internal.h" + +/* + * Character hub device aggregates input from multiple backend devices + * and forwards it to a single frontend device. Additionally, hub + * device takes the output from the frontend device and sends it back + * to all the connected backend devices. + */ + +/* + * Write to all backends. Different backend devices accept data with + * various rate, so it is quite possible that one device returns less, + * then others. In this case we return minimum to the caller, + * expecting caller will repeat operation soon. When repeat happens + * send to the devices which consume data faster must be avoided + * for obvious reasons not to send data, which was already sent. + * Called with chr_write_lock held. + */ +static int hub_chr_write(Chardev *chr, const uint8_t *buf, int len) +{ + HubChardev *d = HUB_CHARDEV(chr); + int r, i, ret = len; + unsigned int written; + + /* Invalidate index on every write */ + d->be_eagain_ind = -1; + + for (i = 0; i < d->be_cnt; i++) { + if (!d->backends[i].be.chr->be_open) { + /* Skip closed backend */ + continue; + } + written = d->be_written[i] - d->be_min_written; + if (written) { + /* Written in the previous call so take into account */ + ret = MIN(written, ret); + continue; + } + r = qemu_chr_fe_write(&d->backends[i].be, buf, len); + if (r < 0) { + if (errno == EAGAIN) { + /* Set index and expect to be called soon on watch wake up */ + d->be_eagain_ind = i; + } + return r; + } + d->be_written[i] += r; + ret = MIN(r, ret); + } + d->be_min_written += ret; + + + return ret; +} + +static int hub_chr_can_read(void *opaque) +{ + HubCharBackend *backend = opaque; + CharBackend *fe = backend->hub->parent.be; + + if (fe && fe->chr_can_read) { + return fe->chr_can_read(fe->opaque); + } + + return 0; +} + +static void hub_chr_read(void *opaque, const uint8_t *buf, int size) +{ + HubCharBackend *backend = opaque; + CharBackend *fe = backend->hub->parent.be; + + if (fe && fe->chr_read) { + fe->chr_read(fe->opaque, buf, size); + } +} + +static void hub_chr_event(void *opaque, QEMUChrEvent event) +{ + HubCharBackend *backend = opaque; + HubChardev *d = backend->hub; + CharBackend *fe = d->parent.be; + + if (event == CHR_EVENT_OPENED) { + /* + * Catch up with what was already written while this backend + * was closed + */ + d->be_written[backend->be_ind] = d->be_min_written; + + if (d->be_event_opened_cnt++) { + /* Ignore subsequent open events from other backends */ + return; + } + } else if (event == CHR_EVENT_CLOSED) { + if (!d->be_event_opened_cnt) { + /* Don't go below zero. Probably assert is better */ + return; + } + if (--d->be_event_opened_cnt) { + /* Serve only the last one close event */ + return; + } + } + + if (fe && fe->chr_event) { + fe->chr_event(fe->opaque, event); + } +} + +static GSource *hub_chr_add_watch(Chardev *s, GIOCondition cond) +{ + HubChardev *d = HUB_CHARDEV(s); + Chardev *chr; + ChardevClass *cc; + + if (d->be_eagain_ind == -1) { + return NULL; + } + + assert(d->be_eagain_ind < d->be_cnt); + chr = qemu_chr_fe_get_driver(&d->backends[d->be_eagain_ind].be); + cc = CHARDEV_GET_CLASS(chr); + if (!cc->chr_add_watch) { + return NULL; + } + + return cc->chr_add_watch(chr, cond); +} + +static bool hub_chr_attach_chardev(HubChardev *d, Chardev *chr, + Error **errp) +{ + bool ret; + + if (d->be_cnt >= MAX_HUB) { + error_setg(errp, "hub: too many uses of chardevs '%s'" + " (maximum is " stringify(MAX_HUB) ")", + d->parent.label); + return false; + } + ret = qemu_chr_fe_init(&d->backends[d->be_cnt].be, chr, errp); + if (ret) { + d->backends[d->be_cnt].hub = d; + d->backends[d->be_cnt].be_ind = d->be_cnt; + d->be_cnt += 1; + } + + return ret; +} + +static void char_hub_finalize(Object *obj) +{ + HubChardev *d = HUB_CHARDEV(obj); + int i; + + for (i = 0; i < d->be_cnt; i++) { + qemu_chr_fe_deinit(&d->backends[i].be, false); + } +} + +static void hub_chr_update_read_handlers(Chardev *chr) +{ + HubChardev *d = HUB_CHARDEV(chr); + int i; + + for (i = 0; i < d->be_cnt; i++) { + qemu_chr_fe_set_handlers_full(&d->backends[i].be, + hub_chr_can_read, + hub_chr_read, + hub_chr_event, + NULL, + &d->backends[i], + chr->gcontext, true, false); + } +} + +static void qemu_chr_open_hub(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) +{ + ChardevHub *hub = backend->u.hub.data; + HubChardev *d = HUB_CHARDEV(chr); + strList *list = hub->chardevs; + + d->be_eagain_ind = -1; + + if (list == NULL) { + error_setg(errp, "hub: 'chardevs' list is not defined"); + return; + } + + while (list) { + Chardev *s; + + s = qemu_chr_find(list->value); + if (s == NULL) { + error_setg(errp, "hub: chardev can't be found by id '%s'", + list->value); + return; + } + if (CHARDEV_IS_HUB(s) || CHARDEV_IS_MUX(s)) { + error_setg(errp, "hub: multiplexers and hub devices can't be " + "stacked, check chardev '%s', chardev should not " + "be a hub device or have 'mux=on' enabled", + list->value); + return; + } + if (!hub_chr_attach_chardev(d, s, errp)) { + return; + } + list = list->next; + } + + /* Closed until an explicit event from backend */ + *be_opened = false; +} + +static void qemu_chr_parse_hub(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + ChardevHub *hub; + strList **tail; + int i; + + backend->type = CHARDEV_BACKEND_KIND_HUB; + hub = backend->u.hub.data = g_new0(ChardevHub, 1); + qemu_chr_parse_common(opts, qapi_ChardevHub_base(hub)); + + tail = &hub->chardevs; + + for (i = 0; i < MAX_HUB; i++) { + char optbuf[16]; + const char *dev; + + snprintf(optbuf, sizeof(optbuf), "chardevs.%u", i); + dev = qemu_opt_get(opts, optbuf); + if (!dev) { + break; + } + + QAPI_LIST_APPEND(tail, g_strdup(dev)); + } +} + +static void char_hub_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->parse = qemu_chr_parse_hub; + cc->open = qemu_chr_open_hub; + cc->chr_write = hub_chr_write; + cc->chr_add_watch = hub_chr_add_watch; + /* We handle events from backends only */ + cc->chr_be_event = NULL; + cc->chr_update_read_handler = hub_chr_update_read_handlers; +} + +static const TypeInfo char_hub_type_info = { + .name = TYPE_CHARDEV_HUB, + .parent = TYPE_CHARDEV, + .class_init = char_hub_class_init, + .instance_size = sizeof(HubChardev), + .instance_finalize = char_hub_finalize, +}; + +static void register_types(void) +{ + type_register_static(&char_hub_type_info); +} + +type_init(register_types); diff --git a/chardev/char.c b/chardev/char.c index 7705da5ad0..5a9e9762ad 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -943,7 +943,26 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "chardev", .type = QEMU_OPT_STRING, + }, + /* + * Multiplexer options. Follows QAPI array syntax. + * See MAX_HUB macro to obtain array capacity. + */ + { + .name = "chardevs.0", + .type = QEMU_OPT_STRING, },{ + .name = "chardevs.1", + .type = QEMU_OPT_STRING, + },{ + .name = "chardevs.2", + .type = QEMU_OPT_STRING, + },{ + .name = "chardevs.3", + .type = QEMU_OPT_STRING, + }, + + { .name = "append", .type = QEMU_OPT_BOOL, },{ @@ -1106,8 +1125,8 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, return NULL; } - if (CHARDEV_IS_MUX(chr)) { - error_setg(errp, "Mux device hotswap not supported yet"); + if (CHARDEV_IS_MUX(chr) || CHARDEV_IS_HUB(chr)) { + error_setg(errp, "For mux or hub device hotswap is not supported yet"); return NULL; } diff --git a/chardev/chardev-internal.h b/chardev/chardev-internal.h index 853807f3cb..9752dd75f7 100644 --- a/chardev/chardev-internal.h +++ b/chardev/chardev-internal.h @@ -29,13 +29,16 @@ #include "chardev/char-fe.h" #include "qom/object.h" +#define MAX_HUB 4 #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) struct MuxChardev { Chardev parent; + /* Linked frontends */ CharBackend *backends[MAX_MUX]; + /* Linked backend */ CharBackend chr; unsigned long mux_bitset; int focus; @@ -53,11 +56,57 @@ struct MuxChardev { int64_t timestamps_start; }; typedef struct MuxChardev MuxChardev; +typedef struct HubChardev HubChardev; +typedef struct HubCharBackend HubCharBackend; + +/* + * Back-pointer on a hub, actual backend and its index in + * `hub->backends` array + */ +struct HubCharBackend { + HubChardev *hub; + CharBackend be; + unsigned int be_ind; +}; + +struct HubChardev { + Chardev parent; + /* Linked backends */ + HubCharBackend backends[MAX_HUB]; + /* + * Number of backends attached to this hub. Once attached, a + * backend can't be detached, so the counter is only increasing. + * To safely remove a backend, hub has to be removed first. + */ + unsigned int be_cnt; + /* + * Number of CHR_EVEN_OPENED events from all backends. Needed to + * send CHR_EVEN_CLOSED only when counter goes to zero. + */ + unsigned int be_event_opened_cnt; + /* + * Counters of written bytes from a single frontend device + * to multiple backend devices. + */ + unsigned int be_written[MAX_HUB]; + unsigned int be_min_written; + /* + * Index of a backend device which got EAGAIN on last write, + * -1 is invalid index. + */ + int be_eagain_ind; +}; +typedef struct HubChardev HubChardev; DECLARE_INSTANCE_CHECKER(MuxChardev, MUX_CHARDEV, TYPE_CHARDEV_MUX) -#define CHARDEV_IS_MUX(chr) \ +DECLARE_INSTANCE_CHECKER(HubChardev, HUB_CHARDEV, + TYPE_CHARDEV_HUB) + +#define CHARDEV_IS_MUX(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) +#define CHARDEV_IS_HUB(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_HUB) bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b, unsigned int *tag, Error **errp); diff --git a/chardev/meson.build b/chardev/meson.build index 70070a8279..56ee39ac0b 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -3,6 +3,7 @@ chardev_ss.add(files( 'char-file.c', 'char-io.c', 'char-mux.c', + 'char-hub.c', 'char-null.c', 'char-pipe.c', 'char-ringbuf.c', diff --git a/include/chardev/char.h b/include/chardev/char.h index 01df55f9e8..429852f8d9 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -232,6 +232,7 @@ OBJECT_DECLARE_TYPE(Chardev, ChardevClass, CHARDEV) #define TYPE_CHARDEV_NULL "chardev-null" #define TYPE_CHARDEV_MUX "chardev-mux" +#define TYPE_CHARDEV_HUB "chardev-hub" #define TYPE_CHARDEV_RINGBUF "chardev-ringbuf" #define TYPE_CHARDEV_PTY "chardev-pty" #define TYPE_CHARDEV_CONSOLE "chardev-console" diff --git a/qapi/char.json b/qapi/char.json index e045354350..f02b66c06b 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -332,6 +332,19 @@ 'data': { 'chardev': 'str' }, 'base': 'ChardevCommon' } +## +# @ChardevHub: +# +# Configuration info for hub chardevs. +# +# @chardevs: List of chardev IDs, which should be added to this hub +# +# Since: 10.0 +## +{ 'struct': 'ChardevHub', + 'data': { 'chardevs': ['str'] }, + 'base': 'ChardevCommon' } + ## # @ChardevStdio: # @@ -479,6 +492,8 @@ # # @mux: (since 1.5) # +# @hub: (since 10.0) +# # @msmouse: emulated Microsoft serial mouse (since 1.5) # # @wctablet: emulated Wacom Penpartner serial tablet (since 2.9) @@ -521,6 +536,7 @@ 'pty', 'null', 'mux', + 'hub', 'msmouse', 'wctablet', { 'name': 'braille', 'if': 'CONFIG_BRLAPI' }, @@ -595,6 +611,16 @@ { 'struct': 'ChardevMuxWrapper', 'data': { 'data': 'ChardevMux' } } +## +# @ChardevHubWrapper: +# +# @data: Configuration info for hub chardevs +# +# Since: 10.0 +## +{ 'struct': 'ChardevHubWrapper', + 'data': { 'data': 'ChardevHub' } } + ## # @ChardevStdioWrapper: # @@ -703,6 +729,7 @@ 'pty': 'ChardevPtyWrapper', 'null': 'ChardevCommonWrapper', 'mux': 'ChardevMuxWrapper', + 'hub': 'ChardevHubWrapper', 'msmouse': 'ChardevCommonWrapper', 'wctablet': 'ChardevCommonWrapper', 'braille': { 'type': 'ChardevCommonWrapper', From c9c05ed525d368c0771ccf1195613bdf49d1bd21 Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Thu, 23 Jan 2025 09:53:23 +0100 Subject: [PATCH 1465/2892] tests/unit/test-char: add unit tests for hub chardev backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces a new test function `char_hub_test` to validate the functionality and constraints of the "hub" chardev backend in QEMU. The test includes multiple scenarios: 1. Invalid hub creation: - Creating a hub without defining `chardevs.N` (expects an error). - Creating a hub with an embedded multiplexer (`mux=on`) or a chardev already in use (expects errors). 2. Max backend limit: - Ensures the hub does not accept more backends than the maximum allowed, with appropriate error handling. 3. Valid hub creation and data aggregation: - Successfully creating a hub with two ring buffer backends. - Verifying data aggregation from backends to a frontend and vice versa. - Ensuring correct error handling for attempts to attach a hub multiple times or remove busy chardevs. 4. Extended EAGAIN simulation (non-Windows only): - Simulates a setup with three backends, including a pipe, to test EAGAIN handling and watcher behavior. - Verifies data flow and recovery in scenarios involving buffer overflows and drained pipes. The test also ensures correct cleanup of chardevs in all cases, covering both valid and invalid configurations. Signed-off-by: Roman Penyaev Reviewed-by: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Message-ID: <20250123085327.965501-4-r.peniaev@gmail.com> --- tests/unit/test-char.c | 398 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c index 98a60d86b1..85b350a9b7 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -359,6 +359,403 @@ static void char_mux_test(void) qmp_chardev_remove("mux-label", &error_abort); } +static void char_hub_test(void) +{ + QemuOpts *opts; + Chardev *hub, *chr1, *chr2, *base; + char *data; + FeHandler h = { 0, false, 0, false, }; + Error *error = NULL; + CharBackend chr_be; + int ret, i; + +#define RB_SIZE 128 + + /* + * Create invalid hub + * 1. Create hub without a 'chardevs.N' defined (expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "hub: 'chardevs' list is not defined"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + /* + * Create invalid hub + * 1. Create chardev with embedded mux: 'mux=on' + * 2. Create hub which refers mux + * 3. Create hub which refers chardev already attached + * to the mux (already in use, expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr0", + 1, &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + base = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(base); + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr0", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "hub: multiplexers and hub devices can't be " + "stacked, check chardev 'chr0', chardev should " + "not be a hub device or have 'mux=on' enabled"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr0-base", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "chardev 'chr0-base' is already in use"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + /* Finalize chr0 */ + qmp_chardev_remove("chr0", &error_abort); + + /* + * Create invalid hub with more than maximum allowed backends + * 1. Create more than maximum allowed 'chardevs.%d' options for + * hub (expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + for (i = 0; i < 10; i++) { + char key[32], val[32]; + + snprintf(key, sizeof(key), "chardevs.%d", i); + snprintf(val, sizeof(val), "chr%d", i); + qemu_opt_set(opts, key, val, &error); + if (error) { + char buf[64]; + + snprintf(buf, sizeof(buf), "Invalid parameter 'chardevs.%d'", i); + g_assert_cmpstr(error_get_pretty(error), ==, buf); + error_free(error); + break; + } + } + g_assert_nonnull(error); + error = NULL; + qemu_opts_del(opts); + + /* + * Create hub with 2 backend chardevs and 1 frontend and perform + * data aggregation + * 1. Create 2 ringbuf backend chardevs + * 2. Create 1 frontend + * 3. Create hub which refers 2 backend chardevs + * 4. Attach hub to a frontend + * 5. Attach hub to a frontend second time (expect error) + * 6. Perform data aggregation + * 7. Remove chr1 ("chr1 is busy", expect error) + * 8. Remove hub0 ("hub0 is busy", expect error); + * 9. Finilize frontend, hub and backend chardevs in correct order + */ + + /* Create first chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr1", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_opts_del(opts); + + /* Create second chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr2", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr2 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr2); + qemu_opts_del(opts); + + /* Create hub0 and refer 2 backend chardevs */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr1", &error_abort); + qemu_opt_set(opts, "chardevs.1", "chr2", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(hub); + qemu_opts_del(opts); + + /* Attach hub to a frontend */ + qemu_chr_fe_init(&chr_be, hub, &error_abort); + qemu_chr_fe_set_handlers(&chr_be, + fe_can_read, + fe_read, + fe_event, + NULL, + &h, + NULL, true); + + /* Fails second time */ + qemu_chr_fe_init(&chr_be, hub, &error); + g_assert_cmpstr(error_get_pretty(error), ==, "chardev 'hub0' is already in use"); + error_free(error); + error = NULL; + + /* Write to backend, chr1 */ + base = qemu_chr_find("chr1"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h.read_count, ==, 6); + g_assert_cmpstr(h.read_buf, ==, "hello"); + h.read_count = 0; + + /* Write to backend, chr2 */ + base = qemu_chr_find("chr2"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"olleh", 6); + g_assert_cmpint(h.read_count, ==, 6); + g_assert_cmpstr(h.read_buf, ==, "olleh"); + h.read_count = 0; + + /* Write to frontend, chr_be */ + ret = qemu_chr_fe_write(&chr_be, (void *)"heyhey", 6); + g_assert_cmpint(ret, ==, 6); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "heyhey"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "heyhey"); + g_free(data); + + /* Can't be removed, depends on hub0 */ + qmp_chardev_remove("chr1", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'chr1' is busy"); + error_free(error); + error = NULL; + + /* Can't be removed, depends on frontend chr_be */ + qmp_chardev_remove("hub0", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'hub0' is busy"); + error_free(error); + error = NULL; + + /* Finalize frontend */ + qemu_chr_fe_deinit(&chr_be, false); + + /* Finalize hub0 */ + qmp_chardev_remove("hub0", &error_abort); + + /* Finalize backend chardevs */ + qmp_chardev_remove("chr1", &error_abort); + qmp_chardev_remove("chr2", &error_abort); + +#ifndef _WIN32 + /* + * Create 3 backend chardevs to simulate EAGAIN and watcher. + * Mainly copied from char_pipe_test(). + * 1. Create 2 ringbuf backend chardevs + * 2. Create 1 pipe backend chardev + * 3. Create 1 frontend + * 4. Create hub which refers 2 backend chardevs + * 5. Attach hub to a frontend + * 6. Perform data aggregation and check watcher + * 7. Finilize frontend, hub and backend chardevs in correct order + */ + { + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + gchar *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL); + Chardev *chr3; + int fd, len; + char buf[128]; + + in = g_strdup_printf("%s.in", pipe); + if (mkfifo(in, 0600) < 0) { + abort(); + } + out = g_strdup_printf("%s.out", pipe); + if (mkfifo(out, 0600) < 0) { + abort(); + } + + /* Create first chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr1", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_opts_del(opts); + + /* Create second chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr2", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr2 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr2); + qemu_opts_del(opts); + + /* Create third chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr3", + 1, &error_abort); + qemu_opt_set(opts, "backend", "pipe", &error_abort); + qemu_opt_set(opts, "path", pipe, &error_abort); + chr3 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr3); + + /* Create hub0 and refer 3 backend chardevs */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr1", &error_abort); + qemu_opt_set(opts, "chardevs.1", "chr2", &error_abort); + qemu_opt_set(opts, "chardevs.2", "chr3", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(hub); + qemu_opts_del(opts); + + /* Attach hub to a frontend */ + qemu_chr_fe_init(&chr_be, hub, &error_abort); + qemu_chr_fe_set_handlers(&chr_be, + fe_can_read, + fe_read, + fe_event, + NULL, + &h, + NULL, true); + + /* Write to frontend, chr_be */ + ret = qemu_chr_fe_write(&chr_be, (void *)"thisis", 6); + g_assert_cmpint(ret, ==, 6); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "thisis"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "thisis"); + g_free(data); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 6); + buf[ret] = 0; + g_assert_cmpstr(buf, ==, "thisis"); + close(fd); + + /* Add watch. 0 indicates no watches if nothing to wait for */ + ret = qemu_chr_fe_add_watch(&chr_be, G_IO_OUT | G_IO_HUP, + NULL, NULL); + g_assert_cmpint(ret, ==, 0); + + /* + * Write to frontend, chr_be, until EAGAIN. Make sure length is + * power of two to fit nicely the whole pipe buffer. + */ + len = 0; + while ((ret = qemu_chr_fe_write(&chr_be, (void *)"thisisit", 8)) + != -1) { + len += ret; + } + g_assert_cmpint(errno, ==, EAGAIN); + + /* Further all writes should cause EAGAIN */ + ret = qemu_chr_fe_write(&chr_be, (void *)"b", 1); + g_assert_cmpint(ret, ==, -1); + g_assert_cmpint(errno, ==, EAGAIN); + + /* + * Add watch. Non 0 indicates we have a blocked chardev, which + * can wakes us up when write is possible. + */ + ret = qemu_chr_fe_add_watch(&chr_be, G_IO_OUT | G_IO_HUP, + NULL, NULL); + g_assert_cmpint(ret, !=, 0); + g_source_remove(ret); + + /* Drain pipe and ring buffers */ + fd = open(out, O_RDWR); + while ((ret = read(fd, buf, MIN(sizeof(buf), len))) != -1 && len > 0) { + len -= ret; + } + close(fd); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 128); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 128); + g_free(data); + + /* + * Now we are good to go, first repeat "lost" sequence, which + * was already consumed and drained by the ring buffers, but + * pipe have not recieved that yet. + */ + ret = qemu_chr_fe_write(&chr_be, (void *)"thisisit", 8); + g_assert_cmpint(ret, ==, 8); + + ret = qemu_chr_fe_write(&chr_be, (void *)"streamisrestored", 16); + g_assert_cmpint(ret, ==, 16); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 16); + /* Only last 16 bytes, see big comment above */ + g_assert_cmpstr(data, ==, "streamisrestored"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 16); + /* Only last 16 bytes, see big comment above */ + g_assert_cmpstr(data, ==, "streamisrestored"); + g_free(data); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 24); + buf[ret] = 0; + /* Both 8 and 16 bytes */ + g_assert_cmpstr(buf, ==, "thisisitstreamisrestored"); + close(fd); + + g_free(in); + g_free(out); + g_free(tmp_path); + g_free(pipe); + + /* Finalize frontend */ + qemu_chr_fe_deinit(&chr_be, false); + + /* Finalize hub0 */ + qmp_chardev_remove("hub0", &error_abort); + + /* Finalize backend chardevs */ + qmp_chardev_remove("chr1", &error_abort); + qmp_chardev_remove("chr2", &error_abort); + qmp_chardev_remove("chr3", &error_abort); + } +#endif +} static void websock_server_read(void *opaque, const uint8_t *buf, int size) { @@ -1507,6 +1904,7 @@ int main(int argc, char **argv) g_test_add_func("/char/invalid", char_invalid_test); g_test_add_func("/char/ringbuf", char_ringbuf_test); g_test_add_func("/char/mux", char_mux_test); + g_test_add_func("/char/hub", char_hub_test); #ifdef _WIN32 g_test_add_func("/char/console/subprocess", char_console_test_subprocess); g_test_add_func("/char/console", char_console_test); From aeb6b818c1816304cdfdf846041e2b2ba0e73aca Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Thu, 23 Jan 2025 09:53:24 +0100 Subject: [PATCH 1466/2892] qemu-options.hx: describe hub chardev and aggregation of several backends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a few lines describing `hub` aggregator configuration for aggregation of several backend devices with a single frontend device. Signed-off-by: Roman Penyaev Reviewed-by: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Message-ID: <20250123085327.965501-5-r.peniaev@gmail.com> --- qemu-options.hx | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index d19bf533d6..ec0090dfe2 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3733,7 +3733,7 @@ SRST The general form of a character device option is: ``-chardev backend,id=id[,mux=on|off][,options]`` - Backend is one of: ``null``, ``socket``, ``udp``, ``msmouse``, + Backend is one of: ``null``, ``socket``, ``udp``, ``msmouse``, ``hub``, ``vc``, ``ringbuf``, ``file``, ``pipe``, ``console``, ``serial``, ``pty``, ``stdio``, ``braille``, ``parallel``, ``spicevmc``, ``spiceport``. The specific backend will determine the @@ -3790,9 +3790,10 @@ The general form of a character device option is: the QEMU monitor, and ``-nographic`` also multiplexes the console and the monitor to stdio. - There is currently no support for multiplexing in the other - direction (where a single QEMU front end takes input and output from - multiple chardevs). + If you need to aggregate data in the opposite direction (where one + QEMU frontend interface receives input and output from multiple + backend chardev devices), please refer to the paragraph below + regarding chardev ``hub`` aggregator device configuration. Every backend supports the ``logfile`` option, which supplies the path to a file to record all data transmitted via the backend. The @@ -3892,6 +3893,46 @@ The available backends are: Forward QEMU's emulated msmouse events to the guest. ``msmouse`` does not take any options. +``-chardev hub,id=id,chardevs.0=id[,chardevs.N=id]`` + Explicitly create chardev backend hub device with the possibility + to aggregate input from multiple backend devices and forward it to + a single frontend device. Additionally, ``hub`` device takes the + output from the frontend device and sends it back to all the + connected backend devices. This allows for seamless interaction + between different backend devices and a single frontend + interface. Aggregation supported for up to 4 chardev + devices. (Since 10.0) + + For example, the following is a use case of 2 backend devices: + virtual console ``vc0`` and a pseudo TTY ``pty0`` connected to + a single virtio hvc console frontend device with a hub ``hub0`` + help. Virtual console renders text to an image, which can be + shared over the VNC protocol. In turn, pty backend provides + bidirectional communication to the virtio hvc console over the + pseudo TTY file. The example configuration can be as follows: + + :: + + -chardev pty,path=/tmp/pty,id=pty0 \ + -chardev vc,id=vc0 \ + -chardev hub,id=hub0,chardevs.0=pty0,chardevs.1=vc0 \ + -device virtconsole,chardev=hub0 \ + -vnc 0.0.0.0:0 + + Once QEMU starts VNC client and any TTY emulator can be used to + control a single hvc console: + + :: + + # Start TTY emulator + tio /tmp/pty + + # Start VNC client and switch to virtual console Ctrl-Alt-2 + vncviewer :0 + + Several frontend devices is not supported. Stacking of multiplexers + and hub devices is not supported as well. + ``-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]`` Connect to a QEMU text console. ``vc`` may optionally be given a specific size. From 453b3f3cdc6326ad75a9dedd6bb21c570e7697ef Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 20 Jan 2025 10:08:32 +0530 Subject: [PATCH 1467/2892] libqos/fw_cfg: refactor file directory iteraton to make it more reusable fw-cfg file directory iteration code can be used by other functions that may want to implement fw-cfg file operations. Refactor it into a smaller helper so that it can be reused. No functional change. Signed-off-by: Ani Sinha Reviewed-by: Fabiano Rosas Message-ID: <20250120043847.954881-2-anisinha@redhat.com> Signed-off-by: Fabiano Rosas --- tests/qtest/libqos/fw_cfg.c | 63 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index 89f053ccac..22435873d9 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -60,6 +60,37 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) qtest_writew(fw_cfg->qts, fw_cfg->base, key); } +static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename, + uint16_t *sel, uint32_t *size) +{ + g_autofree unsigned char *filesbuf = NULL; + uint32_t count; + size_t dsize; + FWCfgFile *pdir_entry; + uint32_t i; + bool found = false; + + *size = 0; + *sel = 0; + + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); + count = be32_to_cpu(count); + dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); + filesbuf = g_malloc(dsize); + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); + pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); + for (i = 0; i < count; ++i, ++pdir_entry) { + if (!strcmp(pdir_entry->name, filename)) { + *size = be32_to_cpu(pdir_entry->size); + *sel = be16_to_cpu(pdir_entry->select); + found = true; + break; + } + } + + return found; +} + /* * The caller need check the return value. When the return value is * nonzero, it means that some bytes have been transferred. @@ -73,34 +104,20 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) * populated, it has received only a starting slice of the fw_cfg file. */ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, - void *data, size_t buflen) + void *data, size_t buflen) { - uint32_t count; - uint32_t i; - unsigned char *filesbuf = NULL; - size_t dsize; - FWCfgFile *pdir_entry; size_t filesize = 0; + uint32_t len; + uint16_t sel; - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); - count = be32_to_cpu(count); - dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); - filesbuf = g_malloc(dsize); - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); - pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); - for (i = 0; i < count; ++i, ++pdir_entry) { - if (!strcmp(pdir_entry->name, filename)) { - uint32_t len = be32_to_cpu(pdir_entry->size); - uint16_t sel = be16_to_cpu(pdir_entry->select); - filesize = len; - if (len > buflen) { - len = buflen; - } - qfw_cfg_get(fw_cfg, sel, data, len); - break; + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + filesize = len; + if (len > buflen) { + len = buflen; } + qfw_cfg_get(fw_cfg, sel, data, len); } - g_free(filesbuf); + return filesize; } From 9c38fea83be147aa0b65eec881a2883089ecac40 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 20 Jan 2025 10:08:33 +0530 Subject: [PATCH 1468/2892] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files At present, the libqos/fw_cfg.c library does not support the modern DMA interface which is required to write to the fw_cfg files. It only uses the IO interface. Implement read and write methods based on DMA. This will enable developers to add tests that writes to the fw_cfg file(s). The structure of the code is taken from edk2 fw_cfg implementation. It has been tested by writing a qtest that writes to a fw_cfg file. Signed-off-by: Ani Sinha Message-ID: <20250120043847.954881-3-anisinha@redhat.com> Signed-off-by: Fabiano Rosas --- tests/qtest/libqos/fw_cfg.c | 139 ++++++++++++++++++++++++++++++++++++ tests/qtest/libqos/fw_cfg.h | 6 +- 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index 22435873d9..0ab3959171 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -14,6 +14,8 @@ #include "qemu/osdep.h" #include "fw_cfg.h" +#include "malloc-pc.h" +#include "libqos-malloc.h" #include "../libqtest.h" #include "qemu/bswap.h" #include "hw/nvram/fw_cfg.h" @@ -60,6 +62,60 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) qtest_writew(fw_cfg->qts, fw_cfg->base, key); } +static void qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, + uint32_t length, uint32_t control) +{ + FWCfgDmaAccess access; + uint32_t addr; + uint64_t guest_access_addr; + uint64_t gaddr; + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, length); + + if (control & FW_CFG_DMA_CTL_WRITE) { + qtest_bufwrite(fw_cfg->qts, gaddr, address, length); + } + access.address = cpu_to_be64(gaddr); + access.length = cpu_to_be32(length); + access.control = cpu_to_be32(control); + + /* now create a separate buffer in guest memory for 'access' */ + guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); + qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); + + /* write lower 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); + qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); + + /* write upper 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); + qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); + + g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); + + if (control & FW_CFG_DMA_CTL_READ) { + qtest_bufread(fw_cfg->qts, gaddr, address, length); + } + + guest_free(&qs->alloc, guest_access_addr); + guest_free(&qs->alloc, gaddr); +} + +static void qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); +} + +static void qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); +} + static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename, uint16_t *sel, uint32_t *size) { @@ -121,6 +177,89 @@ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, return filesize; } +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the first len bytes were read. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually read. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + g_assert(filename); + g_assert(data); + g_assert(buflen); + /* check if DMA is supported since we use DMA for read */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); + } + + return len; +} + +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the buffer has been partially written. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually written. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + g_assert(filename); + g_assert(data); + g_assert(buflen); + /* write operation is only valid if DMA is supported */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); + } + return len; +} + static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) { uint8_t *ptr = data; diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h index b0456a15df..6d6ff09725 100644 --- a/tests/qtest/libqos/fw_cfg.h +++ b/tests/qtest/libqos/fw_cfg.h @@ -14,6 +14,7 @@ #define LIBQOS_FW_CFG_H #include "../libqtest.h" +#include "libqos.h" typedef struct QFWCFG QFWCFG; @@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, void *data, size_t buflen); - +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); void mm_fw_cfg_uninit(QFWCFG *fw_cfg); QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); From 5fddf0c0459a9d637b6cfc683d938061c0c3b1a8 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 20 Jan 2025 10:08:34 +0530 Subject: [PATCH 1469/2892] tests/qtest/vmcoreinfo: add a unit test to exercize basic vmcoreinfo function A new qtest is written that exercizes the fw-cfg DMA based read and write ops to write values into vmcoreinfo fw-cfg file and read them back and verify that they are the same. Signed-off-by: Ani Sinha Message-ID: <20250120043847.954881-4-anisinha@redhat.com> Signed-off-by: Fabiano Rosas --- MAINTAINERS | 2 + tests/qtest/meson.build | 1 + tests/qtest/vmcoreinfo-test.c | 90 +++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 tests/qtest/vmcoreinfo-test.c diff --git a/MAINTAINERS b/MAINTAINERS index bf737eb6db..0cf37fce7b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3019,6 +3019,7 @@ F: include/system/device_tree.h Dump S: Supported M: Marc-André Lureau +R: Ani Sinha F: dump/ F: hw/misc/vmcoreinfo.c F: include/hw/misc/vmcoreinfo.h @@ -3029,6 +3030,7 @@ F: qapi/dump.json F: scripts/dump-guest-memory.py F: stubs/dump.c F: docs/specs/vmcoreinfo.rst +F: tests/qtest/vmcoreinfo-test.c Error reporting M: Markus Armbruster diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index e60e92fe9d..5e062c752a 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -57,6 +57,7 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \ (config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \ + (config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \ diff --git a/tests/qtest/vmcoreinfo-test.c b/tests/qtest/vmcoreinfo-test.c new file mode 100644 index 0000000000..dcf3b5ae05 --- /dev/null +++ b/tests/qtest/vmcoreinfo-test.c @@ -0,0 +1,90 @@ +/* + * qtest vmcoreinfo test case + * + * Copyright Red Hat. 2025. + * + * Authors: + * Ani Sinha + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "libqos/libqos-pc.h" +#include "libqtest.h" +#include "standard-headers/linux/qemu_fw_cfg.h" +#include "libqos/fw_cfg.h" +#include "qemu/bswap.h" +#include "hw/misc/vmcoreinfo.h" + +static void test_vmcoreinfo_write_basic(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + FWCfgVMCoreInfo info; + size_t filesize; + uint16_t guest_format; + uint16_t host_format; + uint32_t size; + uint64_t paddr; + + qs = qtest_pc_boot("-device vmcoreinfo"); + fw_cfg = pc_fw_cfg_init(qs->qts); + + memset(&info, 0 , sizeof(info)); + /* read vmcoreinfo and read back the host format */ + filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + host_format = le16_to_cpu(info.host_format); + g_assert_cmpint(host_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF); + + memset(&info, 0 , sizeof(info)); + info.guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF); + info.size = cpu_to_le32(1 * MiB); + info.paddr = cpu_to_le64(0xffffff00); + info.host_format = cpu_to_le16(host_format); + + /* write the values to the host */ + filesize = qfw_cfg_write_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + memset(&info, 0 , sizeof(info)); + + /* now read back the values we wrote and compare that they are the same */ + filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + size = le32_to_cpu(info.size); + paddr = le64_to_cpu(info.paddr); + guest_format = le16_to_cpu(info.guest_format); + + g_assert_cmpint(size, ==, 1 * MiB); + g_assert_cmpint(paddr, ==, 0xffffff00); + g_assert_cmpint(guest_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF); + + pc_fw_cfg_uninit(fw_cfg); + qtest_shutdown(qs); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + /* skip for non-x86 */ + exit(EXIT_SUCCESS); + } + + qtest_add_func("vmcoreinfo/basic-write", + test_vmcoreinfo_write_basic); + + return g_test_run(); +} From 6f02691ff47255798def37bc47021e847c8d843d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 Jan 2025 11:37:27 +0100 Subject: [PATCH 1470/2892] tests/qtest: Extract qtest_qom_has_concrete_type() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract qtest_qom_has_concrete_type() out of qtest_has_device() in order to re-use it in the following commit. Reviewed-by: Thomas Huth Reviewed-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250130103728.536-2-philmd@linaro.org> Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 89 ++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 437b24fa2e..f416cf8a59 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1011,6 +1011,56 @@ const char *qtest_get_arch(void) return end + 1; } +static bool qtest_qom_has_concrete_type(const char *parent_typename, + const char *child_typename, + QList **cached_list) +{ + QList *list = cached_list ? *cached_list : NULL; + const QListEntry *p; + QObject *qobj; + QString *qstr; + QDict *devinfo; + int idx; + + if (!list) { + QDict *resp; + QDict *args; + QTestState *qts = qtest_init("-machine none"); + + args = qdict_new(); + qdict_put_bool(args, "abstract", false); + qdict_put_str(args, "implements", parent_typename); + + resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", + args); + g_assert(qdict_haskey(resp, "return")); + list = qdict_get_qlist(resp, "return"); + qobject_ref(list); + qobject_unref(resp); + + qtest_quit(qts); + + if (cached_list) { + *cached_list = list; + } + } + + for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { + devinfo = qobject_to(QDict, qlist_entry_obj(p)); + g_assert(devinfo); + + qobj = qdict_get(devinfo, "name"); + g_assert(qobj); + qstr = qobject_to(QString, qobj); + g_assert(qstr); + if (g_str_equal(qstring_get_str(qstr), child_typename)) { + return true; + } + } + + return false; +} + bool qtest_has_accel(const char *accel_name) { if (g_str_equal(accel_name, "tcg")) { @@ -1790,45 +1840,8 @@ bool qtest_has_machine(const char *machine) bool qtest_has_device(const char *device) { static QList *list; - const QListEntry *p; - QObject *qobj; - QString *qstr; - QDict *devinfo; - int idx; - if (!list) { - QDict *resp; - QDict *args; - QTestState *qts = qtest_init("-machine none"); - - args = qdict_new(); - qdict_put_bool(args, "abstract", false); - qdict_put_str(args, "implements", "device"); - - resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", - args); - g_assert(qdict_haskey(resp, "return")); - list = qdict_get_qlist(resp, "return"); - qobject_ref(list); - qobject_unref(resp); - - qtest_quit(qts); - } - - for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { - devinfo = qobject_to(QDict, qlist_entry_obj(p)); - g_assert(devinfo); - - qobj = qdict_get(devinfo, "name"); - g_assert(qobj); - qstr = qobject_to(QString, qobj); - g_assert(qstr); - if (g_str_equal(qstring_get_str(qstr), device)) { - return true; - } - } - - return false; + return qtest_qom_has_concrete_type("device", device, &list); } /* From b5467913722a008eedeed71bccf20a38175a5818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 Jan 2025 11:37:28 +0100 Subject: [PATCH 1471/2892] tests/qtest: Make qtest_has_accel() generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit b14a0b7469f ("accel: Use QOM classes for accel types") accelerators are registered as QOM objects. Use QOM as a generic API to query for available accelerators. This is in particular useful to query hardware accelerators such HFV, Xen or WHPX which otherwise have their definitions poisoned in "exec/poison.h". Reviewed-by: Thomas Huth Reviewed-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250130103728.536-3-philmd@linaro.org> Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index f416cf8a59..fe8606ba6a 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -30,6 +30,7 @@ #include "libqtest.h" #include "libqmp.h" +#include "qemu/accel.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/sockets.h" @@ -1063,13 +1064,10 @@ static bool qtest_qom_has_concrete_type(const char *parent_typename, bool qtest_has_accel(const char *accel_name) { - if (g_str_equal(accel_name, "tcg")) { -#if defined(CONFIG_TCG) - return true; -#else - return false; -#endif - } else if (g_str_equal(accel_name, "kvm")) { + static QList *list; + g_autofree char *accel_type = NULL; + + if (g_str_equal(accel_name, "kvm")) { int i; const char *arch = qtest_get_arch(); const char *targets[] = { CONFIG_KVM_TARGETS }; @@ -1081,11 +1079,12 @@ bool qtest_has_accel(const char *accel_name) } } } - } else { - /* not implemented */ - g_assert_not_reached(); + return false; } - return false; + + accel_type = g_strconcat(accel_name, ACCEL_CLASS_SUFFIX, NULL); + + return qtest_qom_has_concrete_type("accel", accel_type, &list); } bool qtest_get_irq(QTestState *s, int num) From cd6406df15ed2216e70e34c45a4a82f42b55873f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 3 Feb 2025 13:43:46 +0100 Subject: [PATCH 1472/2892] tests/qtest/vhost-user-test: Use modern virtio for vhost-user tests All other vhost-user tests here use modern virtio, too, so let's adjust the vhost-user-net test accordingly. Signed-off-by: Thomas Huth Message-ID: <20250203124346.169607-1-thuth@redhat.com> Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/vhost-user-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 76d142a158..bd977ef28d 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -1043,7 +1043,8 @@ static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) static uint64_t vu_net_get_features(TestServer *s) { - uint64_t features = 0x1ULL << VHOST_F_LOG_ALL | + uint64_t features = 0x1ULL << VIRTIO_F_VERSION_1 | + 0x1ULL << VHOST_F_LOG_ALL | 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; if (s->queues > 1) { From ccf86c392c5b8949bafd363e44d3abb112578044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 13 Jan 2025 23:31:33 +0400 Subject: [PATCH 1473/2892] ui/dbus: on win32, allow ANONYMOUS with p2p MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GLib doesn't implement EXTERNAL on win32 at the moment, and disables ANONYMOUS by default. zbus dropped support for COOKIE_SHA1 in 5.0, making it no longer possible to connect to qemu -display dbus. Since p2p connections are gated by existing QMP (or a D-Bus connection), qemu -display dbus p2p can accept authentication with ANONYMOUS. Signed-off-by: Marc-André Lureau --- audio/dbusaudio.c | 8 +++++++- ui/dbus-console.c | 8 +++++++- ui/dbus.c | 10 ++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c index 095e739382..af77e7cc33 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -524,11 +524,17 @@ dbus_audio_register_listener(AudioState *s, ); } + GDBusConnectionFlags flags = + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER; +#ifdef WIN32 + flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif + listener_conn = g_dbus_connection_new_sync( G_IO_STREAM(socket_conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + flags, NULL, NULL, &err); if (err) { error_report("Failed to setup peer connection: %s", err->message); diff --git a/ui/dbus-console.c b/ui/dbus-console.c index 5eb1d40d16..85e215ef23 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -305,10 +305,16 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, #endif ); + GDBusConnectionFlags flags = + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER; +#ifdef WIN32 + flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif + listener_conn = g_dbus_connection_new_sync( G_IO_STREAM(socket_conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + flags, NULL, NULL, &err); if (err) { error_report("Failed to setup peer connection: %s", err->message); diff --git a/ui/dbus.c b/ui/dbus.c index 7b258c6823..2eb03aa247 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -317,11 +317,17 @@ dbus_display_add_client(int csock, Error **errp) conn = g_socket_connection_factory_create_connection(socket); dbus_display->add_client_cancellable = g_cancellable_new(); + GDBusConnectionFlags flags = + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | + G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING; + +#ifdef WIN32 + flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif g_dbus_connection_new(G_IO_STREAM(conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | - G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, + flags, NULL, dbus_display->add_client_cancellable, dbus_display_add_client_ready, From 5b4109232e5c6db61fb02aa86b30170d93234acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 14 Jan 2025 14:16:04 +0400 Subject: [PATCH 1474/2892] ui/dbus: clarify the kind of win32 handle that is shared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "-display dbus" hands over a file mapping handle to the peer process (not a file handle). Signed-off-by: Marc-André Lureau --- ui/dbus-display1.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index e70f2848b7..d702253431 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -527,14 +527,14 @@ From c0fb8e88cbbfc140800e614586b446193a3fd93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 15 Nov 2024 16:18:13 +0400 Subject: [PATCH 1475/2892] plugins: fix -Werror=maybe-uninitialized false-positive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../contrib/plugins/cache.c:638:9: error: ‘l2_cache’ may be used uninitialized [-Werror=maybe-uninitialized] 638 | append_stats_line(rep, l1_dmem_accesses, l1_dmisses, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Is a false-positive, since cores > 1, so the variable is set in the above loop. Signed-off-by: Marc-André Lureau Reviewed-by: Pierrick Bouvier --- contrib/plugins/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index 7baff86860..7cfd3df249 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -603,7 +603,7 @@ static int l2_cmp(gconstpointer a, gconstpointer b) static void log_stats(void) { int i; - Cache *icache, *dcache, *l2_cache; + Cache *icache, *dcache, *l2_cache = NULL; g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses," " dmiss rate, insn accesses," From 19c628f2f579f2702dd13192b7c2de6bc8d665ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 24 Jan 2025 17:29:24 +0400 Subject: [PATCH 1476/2892] dbus: add -audio dbus nsamples option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow to set the number of audio samples per read/write to dbus. Signed-off-by: Marc-André Lureau --- audio/dbusaudio.c | 21 ++++++++++++++++++--- qapi/audio.json | 22 +++++++++++++++++++++- ui/dbus-display1.xml | 12 ++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c index af77e7cc33..b44fdd1511 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -43,9 +43,10 @@ #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio" -#define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */ +#define DBUS_DEFAULT_AUDIO_NSAMPLES 480 typedef struct DBusAudio { + Audiodev *dev; GDBusObjectManagerServer *server; bool p2p; GDBusObjectSkeleton *audio; @@ -151,6 +152,18 @@ dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } +static guint +dbus_audio_get_nsamples(DBusAudio *da) +{ + AudiodevDBusOptions *opts = &da->dev->u.dbus; + + if (opts->has_nsamples && opts->nsamples) { + return opts->nsamples; + } else { + return DBUS_DEFAULT_AUDIO_NSAMPLES; + } +} + static int dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { @@ -160,7 +173,7 @@ dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) QemuDBusDisplay1AudioOutListener *listener = NULL; audio_pcm_init_info(&hw->info, as); - hw->samples = DBUS_AUDIO_NSAMPLES; + hw->samples = dbus_audio_get_nsamples(da); audio_rate_start(&vo->rate); g_hash_table_iter_init(&iter, da->out_listeners); @@ -274,7 +287,7 @@ dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) QemuDBusDisplay1AudioInListener *listener = NULL; audio_pcm_init_info(&hw->info, as); - hw->samples = DBUS_AUDIO_NSAMPLES; + hw->samples = dbus_audio_get_nsamples(da); audio_rate_start(&vo->rate); g_hash_table_iter_init(&iter, da->in_listeners); @@ -399,6 +412,7 @@ dbus_audio_init(Audiodev *dev, Error **errp) { DBusAudio *da = g_new0(DBusAudio, 1); + da->dev = dev; da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal, @@ -652,6 +666,7 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) "swapped-signal::handle-register-out-listener", dbus_audio_register_out_listener, s, NULL); + qemu_dbus_display1_audio_set_nsamples(da->iface, dbus_audio_get_nsamples(da)); g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio), G_DBUS_INTERFACE_SKELETON(da->iface)); diff --git a/qapi/audio.json b/qapi/audio.json index 519697c0cd..dd5a58d13e 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -65,6 +65,26 @@ '*in': 'AudiodevPerDirectionOptions', '*out': 'AudiodevPerDirectionOptions' } } +## +# @AudiodevDBusOptions: +# +# Options of the D-Bus audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @nsamples: set the number of samples per read/write calls (default to 480, +# 10ms at 48kHz). +# +# Since: 10.0 +## +{ 'struct': 'AudiodevDBusOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*nsamples': 'uint32'} } + ## # @AudiodevAlsaPerDirectionOptions: # @@ -490,7 +510,7 @@ 'if': 'CONFIG_AUDIO_ALSA' }, 'coreaudio': { 'type': 'AudiodevCoreaudioOptions', 'if': 'CONFIG_AUDIO_COREAUDIO' }, - 'dbus': { 'type': 'AudiodevGenericOptions', + 'dbus': { 'type': 'AudiodevDBusOptions', 'if': 'CONFIG_DBUS_DISPLAY' }, 'dsound': { 'type': 'AudiodevDsoundOptions', 'if': 'CONFIG_AUDIO_DSOUND' }, diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index d702253431..72deefa455 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -773,6 +773,18 @@ + + + + + hw/char/pl011/src/device.rs:382:5 | 382 | / fn set_read_trigger(&mut self) { 383 | | self.read_trigger = 1; 384 | | } | |_____^ Just use the standard set that is present in rust/Cargo.toml, with just a small adjustment to allow upper case acronyms which are used for register names. Reported-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/lib.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index e704daf6e3..3c72f1221f 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -12,16 +12,7 @@ //! See [`PL011State`](crate::device::PL011State) for the device model type and //! the [`registers`] module for register types. -#![deny( - clippy::correctness, - clippy::suspicious, - clippy::complexity, - clippy::perf, - clippy::nursery, - clippy::style -)] #![allow(clippy::upper_case_acronyms)] -#![allow(clippy::result_unit_err)] use qemu_api::c_str; From 5a3c49dedf1e9e5e6a8f3e63952e2e546b6c8433 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Feb 2025 16:09:17 +0000 Subject: [PATCH 1521/2892] target/arm: deprecate the pxa2xx CPUs and iwMMXt emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pxa2xx CPUs are now only useful with user-mode emulation, because we dropped all the machine types that used them in 9.2. (Technically you could alse use "-cpu pxa270" with a board model like versatilepb which doesn't sanity-check the CPU type, but that has never been a supported config.) To use them (or iwMMXt emulation) with QEMU user-mode you would need to explicitly select them with the -cpu option or the QEMU_CPU environment variable. A google search finds no examples of anybody doing this in the last decade; I don't believe the GCC folks are using QEMU to test their iwMMXt codegen either. In fact, GCC is in the process of dropping support for iwMMXT entirely. The iwMMXt emulation is thousands of lines of code in QEMU, and is now the only bit of Arm insn decode which doesn't use decodetree. We have no way to test or validate changes to it. This code is just dead weight that is almost certainly not being used by anybody. Mark it as deprecated. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250127112715.2936555-2-peter.maydell@linaro.org --- docs/about/deprecated.rst | 21 +++++++++++++++++++++ target/arm/cpu.c | 3 +++ target/arm/cpu.h | 1 + target/arm/tcg/cpu32.c | 36 ++++++++++++++++++++++++------------ 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 4a3c302962..29de49351d 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -204,6 +204,27 @@ is going to be so much slower it wouldn't make sense for any serious instrumentation. Due to implementation differences there will also be anomalies in things like memory instrumentation. +linux-user mode CPUs +-------------------- + +iwMMXt emulation and the ``pxa`` CPUs (since 10.0) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``, +``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``, +``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) are no +longer used in system emulation, because all the machine types which +used these CPUs were removed in the QEMU 9.2 release. These CPUs can +now only be used in linux-user mode, and to do that you would have to +explicitly select one of these CPUs with the ``-cpu`` command line +option or the ``QEMU_CPU`` environment variable. + +We don't believe that anybody is using the iwMMXt emulation, and we do +not have any tests to validate it or any real hardware or similar +known-good implementation to test against. GCC is in the process of +dropping their support for iwMMXt codegen. These CPU types are +therefore deprecated in QEMU, and will be removed in a future release. + System emulator CPUs -------------------- diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 7a83b9ee34..32dc7c1e69 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2758,6 +2758,9 @@ static void cpu_register_class_init(ObjectClass *oc, void *data) acc->info = data; cc->gdb_core_xml_file = "arm-core.xml"; + if (acc->info->deprecation_note) { + cc->deprecation_note = acc->info->deprecation_note; + } } void arm_cpu_register(const ARMCPUInfo *info) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 2213c27734..c2d2d99b46 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1118,6 +1118,7 @@ struct ArchCPU { typedef struct ARMCPUInfo { const char *name; + const char *deprecation_note; void (*initfn)(Object *obj); void (*class_init)(ObjectClass *oc, void *data); } ARMCPUInfo; diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 2ad2182525..0f1c5bc87e 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -1026,19 +1026,31 @@ static const ARMCPUInfo arm_tcg_cpus[] = { { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, { .name = "sa1110", .initfn = sa1110_initfn }, - { .name = "pxa250", .initfn = pxa250_initfn }, - { .name = "pxa255", .initfn = pxa255_initfn }, - { .name = "pxa260", .initfn = pxa260_initfn }, - { .name = "pxa261", .initfn = pxa261_initfn }, - { .name = "pxa262", .initfn = pxa262_initfn }, + { .name = "pxa250", .initfn = pxa250_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa255", .initfn = pxa255_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa260", .initfn = pxa260_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa261", .initfn = pxa261_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa262", .initfn = pxa262_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, /* "pxa270" is an alias for "pxa270-a0" */ - { .name = "pxa270", .initfn = pxa270a0_initfn }, - { .name = "pxa270-a0", .initfn = pxa270a0_initfn }, - { .name = "pxa270-a1", .initfn = pxa270a1_initfn }, - { .name = "pxa270-b0", .initfn = pxa270b0_initfn }, - { .name = "pxa270-b1", .initfn = pxa270b1_initfn }, - { .name = "pxa270-c0", .initfn = pxa270c0_initfn }, - { .name = "pxa270-c5", .initfn = pxa270c5_initfn }, + { .name = "pxa270", .initfn = pxa270a0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-a0", .initfn = pxa270a0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-a1", .initfn = pxa270a1_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-b0", .initfn = pxa270b0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-b1", .initfn = pxa270b1_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-c0", .initfn = pxa270c0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-c5", .initfn = pxa270c5_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, #ifndef TARGET_AARCH64 { .name = "max", .initfn = arm_max_initfn }, #endif From 86f847a39aef93bcfbea65a702ba76762ae54d61 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Feb 2025 16:09:18 +0000 Subject: [PATCH 1522/2892] tests/tcg/arm: Remove test-arm-iwmmxt test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test-arm-iwmmmxt test isn't testing what it thinks it's testing. If you run it with a CPU type that supports iwMMXt then it will crash immediately with a SIGILL, because (even with -marm) GCC will link it against startup code that is in Thumb mode, and no iwMMXt CPU has Thumb: 00010338 <_start>: 10338: f04f 0b00 mov.w fp, #0 1033c: f04f 0e00 mov.w lr, #0 If you run it with a CPU type which does *not* support iwMMXt, which is what 'make check-tcg' does, then QEMU will not try to handle the insns as iwMMXt. Instead the translator turns them into illegal instructions. Then in the linux-user cpu_loop() code we identify them as FPA11 instructions inside emulate_arm_fpa11(), because the FPA11 happened to use the same coprocessor number as these iwMMXt insns. So we execute a completely different set of FPA11 insns, which means we don't crash, but we will print garbage to stdout. Then the test binary always exits with a 0 return code, so 'make check-tcg' thinks the test passes. Modern gnueabihf toolchains assume in their startup code that the CPU is not so old as to not support Thumb, so there's no way to get them to generate a binary that actually does what the test wants. Since we're deprecating iwMMXt emulation anyway, it's not worth trying to salvage the test case to get it to really test the iwMMXt insns. Delete the test entirely. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250127112715.2936555-3-peter.maydell@linaro.org --- tests/tcg/arm/Makefile.target | 7 ----- tests/tcg/arm/README | 5 ---- tests/tcg/arm/test-arm-iwmmxt.S | 49 --------------------------------- 3 files changed, 61 deletions(-) delete mode 100644 tests/tcg/arm/test-arm-iwmmxt.S diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 06ddf3e04f..99a953b667 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -20,13 +20,6 @@ ARM_TESTS = hello-arm hello-arm: CFLAGS+=-marm -ffreestanding -fno-stack-protector hello-arm: LDFLAGS+=-nostdlib -# IWMXT floating point extensions -ARM_TESTS += test-arm-iwmmxt -# Clang assembler does not support IWMXT, so use the external assembler. -test-arm-iwmmxt: CFLAGS += -marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 $(CROSS_CC_HAS_FNIA) -test-arm-iwmmxt: test-arm-iwmmxt.S - $(CC) $(CFLAGS) -Wa,--noexecstack $< -o $@ $(LDFLAGS) - # Float-convert Tests ARM_TESTS += fcvt fcvt: LDFLAGS += -lm diff --git a/tests/tcg/arm/README b/tests/tcg/arm/README index e6307116e2..aceccc127f 100644 --- a/tests/tcg/arm/README +++ b/tests/tcg/arm/README @@ -4,8 +4,3 @@ hello-arm --------- A very simple inline assembly, write syscall based hello world - -test-arm-iwmmxt ---------------- - -A simple test case for older iwmmxt extended ARMs diff --git a/tests/tcg/arm/test-arm-iwmmxt.S b/tests/tcg/arm/test-arm-iwmmxt.S deleted file mode 100644 index d647f9404a..0000000000 --- a/tests/tcg/arm/test-arm-iwmmxt.S +++ /dev/null @@ -1,49 +0,0 @@ -@ Checks whether iwMMXt is functional. -.code 32 -.globl main - -main: -ldr r0, =data0 -ldr r1, =data1 -ldr r2, =data2 -#ifndef FPA -wldrd wr0, [r0, #0] -wldrd wr1, [r0, #8] -wldrd wr2, [r1, #0] -wldrd wr3, [r1, #8] -wsubb wr2, wr2, wr0 -wsubb wr3, wr3, wr1 -wldrd wr0, [r2, #0] -wldrd wr1, [r2, #8] -waddb wr0, wr0, wr2 -waddb wr1, wr1, wr3 -wstrd wr0, [r2, #0] -wstrd wr1, [r2, #8] -#else -ldfe f0, [r0, #0] -ldfe f1, [r0, #8] -ldfe f2, [r1, #0] -ldfe f3, [r1, #8] -adfdp f2, f2, f0 -adfdp f3, f3, f1 -ldfe f0, [r2, #0] -ldfe f1, [r2, #8] -adfd f0, f0, f2 -adfd f1, f1, f3 -stfe f0, [r2, #0] -stfe f1, [r2, #8] -#endif -mov r0, #1 -mov r1, r2 -mov r2, #0x11 -swi #0x900004 -mov r0, #0 -swi #0x900001 - -.data -data0: -.string "aaaabbbbccccdddd" -data1: -.string "bbbbccccddddeeee" -data2: -.string "hvLLWs\x1fsdrs9\x1fNJ-\n" From 7623676948d35cdbb31fd976f96277e3f1673e52 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Feb 2025 16:09:18 +0000 Subject: [PATCH 1523/2892] target/arm: Drop unused AArch64DecodeTable typedefs We removed the old table-based decoder in favour of decodetree, but we left a couple of typedefs that are now unused; delete them. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250128135046.4108775-1-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0b76a2cdb7..d6ac2ed418 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -75,17 +75,6 @@ static int scale_by_log2_tag_granule(DisasContext *s, int x) #include "decode-sme-fa64.c.inc" #include "decode-a64.c.inc" -/* Table based decoder typedefs - used when the relevant bits for decode - * are too awkwardly scattered across the instruction (eg SIMD). - */ -typedef void AArch64DecodeFn(DisasContext *s, uint32_t insn); - -typedef struct AArch64DecodeTable { - uint32_t pattern; - uint32_t mask; - AArch64DecodeFn *disas_fn; -} AArch64DecodeTable; - /* initialize TCG globals. */ void a64_translate_init(void) { From cbb95d496810023a485a3cfc1a096733b055ce89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Feb 2025 16:09:18 +0000 Subject: [PATCH 1524/2892] hw/arm/boot: Propagate vCPU to arm_load_dtb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In heterogeneous setup the first vCPU might not be the one expected, better pass it explicitly. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Message-id: 20250130112615.3219-2-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/boot.c | 11 ++++++----- hw/arm/virt.c | 2 +- include/hw/arm/boot.h | 4 +++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index cbc24356fc..42c18355e8 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -432,13 +432,12 @@ out: return ret; } -static void fdt_add_psci_node(void *fdt) +static void fdt_add_psci_node(void *fdt, ARMCPU *armcpu) { uint32_t cpu_suspend_fn; uint32_t cpu_off_fn; uint32_t cpu_on_fn; uint32_t migrate_fn; - ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); const char *psci_method; int64_t psci_conduit; int rc; @@ -512,7 +511,8 @@ static void fdt_add_psci_node(void *fdt) } int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit, AddressSpace *as, MachineState *ms) + hwaddr addr_limit, AddressSpace *as, MachineState *ms, + ARMCPU *cpu) { void *fdt = NULL; int size, rc, n = 0; @@ -655,7 +655,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } } - fdt_add_psci_node(fdt); + fdt_add_psci_node(fdt, cpu); if (binfo->modify_dtb) { binfo->modify_dtb(binfo, fdt); @@ -1327,7 +1327,8 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) * 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) { + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, + as, ms, cpu) < 0) { exit(1); } } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 99e0a68b6c..d23b14718a 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1746,7 +1746,7 @@ void virt_machine_done(Notifier *notifier, void *data) vms->memmap[VIRT_PLATFORM_BUS].size, vms->irqmap[VIRT_PLATFORM_BUS]); } - if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms) < 0) { + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms, cpu) < 0) { exit(1); } diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index 80c492d742..b12bf61ca8 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -160,6 +160,7 @@ AddressSpace *arm_boot_address_space(ARMCPU *cpu, * @binfo: struct describing the boot environment * @addr_limit: upper limit of the available memory area at @addr * @as: address space to load image to + * @cpu: ARM CPU object * * Load a device tree supplied by the machine or by the user with the * '-dtb' command line option, and put it at offset @addr in target @@ -176,7 +177,8 @@ AddressSpace *arm_boot_address_space(ARMCPU *cpu, * Note: Must not be called unless have_dtb(binfo) is true. */ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit, AddressSpace *as, MachineState *ms); + hwaddr addr_limit, AddressSpace *as, MachineState *ms, + ARMCPU *cpu); /* Write a secure board setup routine with a dummy handler for SMCs */ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, From eead29c0e7f1d7b320247dc89891c9137eafe85c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Feb 2025 16:09:18 +0000 Subject: [PATCH 1525/2892] hw/arm/fsl-imx6: Add local 'mpcore/gic' variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The A9MPCore forward the IRQs from its internal GIC. To make the code clearer, add the 'mpcore' and 'gic' variables. Reviewed-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Message-id: 20250130112615.3219-3-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/fsl-imx6.c | 52 +++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 88b9ccff49..dc86338b3a 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -117,6 +117,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) uint16_t i; qemu_irq irq; unsigned int smp_cpus = ms->smp.cpus; + DeviceState *mpcore = DEVICE(&s->a9mpcore); + DeviceState *gic; if (smp_cpus > FSL_IMX6_NUM_CPUS) { error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", @@ -143,21 +145,21 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) } } - object_property_set_int(OBJECT(&s->a9mpcore), "num-cpu", smp_cpus, - &error_abort); + object_property_set_int(OBJECT(mpcore), "num-cpu", smp_cpus, &error_abort); - object_property_set_int(OBJECT(&s->a9mpcore), "num-irq", + object_property_set_int(OBJECT(mpcore), "num-irq", FSL_IMX6_MAX_IRQ + GIC_INTERNAL, &error_abort); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(mpcore), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, FSL_IMX6_A9MPCORE_ADDR); + sysbus_mmio_map(SYS_BUS_DEVICE(mpcore), 0, FSL_IMX6_A9MPCORE_ADDR); + gic = mpcore; for (i = 0; i < smp_cpus; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i, + sysbus_connect_irq(SYS_BUS_DEVICE(gic), i, qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + smp_cpus, + sysbus_connect_irq(SYS_BUS_DEVICE(gic), i + smp_cpus, qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); } @@ -195,8 +197,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - serial_table[i].irq)); + qdev_get_gpio_in(gic, serial_table[i].irq)); } s->gpt.ccm = IMX_CCM(&s->ccm); @@ -207,8 +208,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX6_GPT_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_GPT_IRQ)); + qdev_get_gpio_in(gic, FSL_IMX6_GPT_IRQ)); /* Initialize all EPIT timers */ for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { @@ -228,8 +228,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - epit_table[i].irq)); + qdev_get_gpio_in(gic, epit_table[i].irq)); } /* Initialize all I2C */ @@ -249,8 +248,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - i2c_table[i].irq)); + qdev_get_gpio_in(gic, i2c_table[i].irq)); } /* Initialize all GPIOs */ @@ -307,11 +305,9 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - gpio_table[i].irq_low)); + qdev_get_gpio_in(gic, gpio_table[i].irq_low)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - gpio_table[i].irq_high)); + qdev_get_gpio_in(gic, gpio_table[i].irq_high)); } /* Initialize all SDHC */ @@ -338,8 +334,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - esdhc_table[i].irq)); + qdev_get_gpio_in(gic, esdhc_table[i].irq)); } /* USB */ @@ -360,8 +355,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, FSL_IMX6_USBOH3_USB_ADDR + i * 0x200); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_USBn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6_USBn_IRQ[i])); } /* Initialize all ECSPI */ @@ -384,8 +378,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - spi_table[i].irq)); + qdev_get_gpio_in(gic, spi_table[i].irq)); } object_property_set_uint(OBJECT(&s->eth), "phy-num", s->phy_num, @@ -396,11 +389,9 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth), 0, FSL_IMX6_ENET_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_ENET_MAC_IRQ)); + qdev_get_gpio_in(gic, FSL_IMX6_ENET_MAC_IRQ)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth), 1, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_ENET_MAC_1588_IRQ)); + qdev_get_gpio_in(gic, FSL_IMX6_ENET_MAC_1588_IRQ)); /* * SNVS @@ -427,8 +418,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX6_WDOGn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_WDOGn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6_WDOGn_IRQ[i])); } /* From 4033d1d56e3689a4733b41b3f9297900ac6e2053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Feb 2025 16:09:19 +0000 Subject: [PATCH 1526/2892] hw/arm/fsl-imx6ul: Add local 'mpcore/gic' variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The A7MPCore forward the IRQs from its internal GIC. To make the code clearer, add the 'mpcore' and 'gic' variables. Rename 'd' variable as 'cpu'. Reviewed-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Message-id: 20250130112615.3219-4-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/fsl-imx6ul.c | 64 +++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 79e4847953..34c4aa15cd 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -157,10 +157,12 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); FslIMX6ULState *s = FSL_IMX6UL(dev); + DeviceState *mpcore = DEVICE(&s->a7mpcore); int i; char name[NAME_SIZE]; - SysBusDevice *sbd; - DeviceState *d; + DeviceState *gic; + SysBusDevice *gicsbd; + DeviceState *cpu; if (ms->smp.cpus > 1) { error_setg(errp, "%s: Only a single CPU is supported (%d requested)", @@ -173,19 +175,19 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * A7MPCORE */ - object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", 1, &error_abort); - object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", + object_property_set_int(OBJECT(mpcore), "num-cpu", 1, &error_abort); + object_property_set_int(OBJECT(mpcore), "num-irq", FSL_IMX6UL_MAX_IRQ + GIC_INTERNAL, &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX6UL_A7MPCORE_ADDR); + sysbus_realize(SYS_BUS_DEVICE(mpcore), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(mpcore), 0, FSL_IMX6UL_A7MPCORE_ADDR); - sbd = SYS_BUS_DEVICE(&s->a7mpcore); - d = DEVICE(&s->cpu); - - sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(d, ARM_CPU_IRQ)); - sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(d, ARM_CPU_FIQ)); - sysbus_connect_irq(sbd, 2, qdev_get_gpio_in(d, ARM_CPU_VIRQ)); - sysbus_connect_irq(sbd, 3, qdev_get_gpio_in(d, ARM_CPU_VFIQ)); + gic = mpcore; + gicsbd = SYS_BUS_DEVICE(gic); + cpu = DEVICE(&s->cpu); + sysbus_connect_irq(gicsbd, 0, qdev_get_gpio_in(cpu, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, 1, qdev_get_gpio_in(cpu, ARM_CPU_FIQ)); + sysbus_connect_irq(gicsbd, 2, qdev_get_gpio_in(cpu, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicsbd, 3, qdev_get_gpio_in(cpu, ARM_CPU_VFIQ)); /* * A7MPCORE DAP @@ -244,8 +246,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_GPTn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_GPTn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_GPTn_IRQ[i])); } /* @@ -269,8 +270,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_EPITn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_EPITn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_EPITn_IRQ[i])); } /* @@ -307,12 +307,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_GPIOn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_GPIOn_LOW_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_GPIOn_LOW_IRQ[i])); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_GPIOn_HIGH_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_GPIOn_HIGH_IRQ[i])); } /* @@ -366,8 +364,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_SPIn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_SPIn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_SPIn_IRQ[i])); } /* @@ -392,8 +389,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX6UL_I2Cn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_I2Cn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_I2Cn_IRQ[i])); } /* @@ -430,8 +426,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_UARTn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_UARTn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_UARTn_IRQ[i])); } /* @@ -480,12 +475,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_ENETn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_ENETn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_ENETn_IRQ[i])); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_ENETn_TIMER_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_ENETn_TIMER_IRQ[i])); } /* @@ -521,8 +514,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, FSL_IMX6UL_USB02_USBn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_USBn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_USBn_IRQ[i])); } /* @@ -547,8 +539,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_USDHCn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_USDHCn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_USDHCn_IRQ[i])); } /* @@ -580,8 +571,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX6UL_WDOGn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_WDOGn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_WDOGn_IRQ[i])); } /* From 04c58c3b20030db145308be26a7df98d02847ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Feb 2025 16:09:19 +0000 Subject: [PATCH 1527/2892] hw/arm/fsl-imx7: Add local 'mpcore/gic' variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The A7MPCore forward the IRQs from its internal GIC. To make the code clearer, add the 'mpcore' and 'gic' variables. Reviewed-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Message-id: 20250130112615.3219-5-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/fsl-imx7.c | 52 +++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 004bf49937..3374018cde 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -166,7 +166,8 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); FslIMX7State *s = FSL_IMX7(dev); - Object *o; + DeviceState *mpcore = DEVICE(&s->a7mpcore); + DeviceState *gic; int i; qemu_irq irq; char name[NAME_SIZE]; @@ -182,7 +183,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) * CPUs */ for (i = 0; i < smp_cpus; i++) { - o = OBJECT(&s->cpu[i]); + Object *o = OBJECT(&s->cpu[i]); /* On uniprocessor, the CBAR is set to 0 */ if (smp_cpus > 1) { @@ -205,16 +206,15 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) /* * A7MPCORE */ - object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", smp_cpus, - &error_abort); - object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", + object_property_set_int(OBJECT(mpcore), "num-cpu", smp_cpus, &error_abort); + object_property_set_int(OBJECT(mpcore), "num-irq", FSL_IMX7_MAX_IRQ + GIC_INTERNAL, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(mpcore), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(mpcore), 0, FSL_IMX7_A7MPCORE_ADDR); - sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR); - + gic = mpcore; for (i = 0; i < smp_cpus; i++) { - SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); + SysBusDevice *sbd = SYS_BUS_DEVICE(gic); DeviceState *d = DEVICE(qemu_get_cpu(i)); irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); @@ -255,8 +255,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_GPTn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_GPTn_IRQ[i])); } /* @@ -298,12 +297,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_GPIOn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_GPIOn_LOW_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_GPIOn_LOW_IRQ[i])); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_GPIOn_HIGH_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_GPIOn_HIGH_IRQ[i])); } /* @@ -355,8 +352,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, FSL_IMX7_SPIn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_SPIn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_SPIn_IRQ[i])); } /* @@ -381,8 +377,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_I2Cn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_I2Cn_IRQ[i])); } /* @@ -416,7 +411,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]); + irq = qdev_get_gpio_in(gic, FSL_IMX7_UARTn_IRQ[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq); } @@ -454,9 +449,9 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0)); + irq = qdev_get_gpio_in(gic, FSL_IMX7_ENET_IRQ(i, 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3)); + irq = qdev_get_gpio_in(gic, FSL_IMX7_ENET_IRQ(i, 3)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq); } @@ -483,7 +478,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, FSL_IMX7_USDHCn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]); + irq = qdev_get_gpio_in(gic, FSL_IMX7_USDHCn_IRQ[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq); } @@ -522,8 +517,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_WDOGn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_WDOGn_IRQ[i])); } /* @@ -606,11 +600,11 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_MSI_IRQ); qdev_connect_gpio_out(DEVICE(&s->pcie4_msi_irq), 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ); + irq = qdev_get_gpio_in(gic, FSL_IMX7_PCI_INTA_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ); + irq = qdev_get_gpio_in(gic, FSL_IMX7_PCI_INTB_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ); + irq = qdev_get_gpio_in(gic, FSL_IMX7_PCI_INTC_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq); irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); @@ -643,7 +637,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, FSL_IMX7_USBn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]); + irq = qdev_get_gpio_in(gic, FSL_IMX7_USBn_IRQ[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq); snprintf(name, NAME_SIZE, "usbmisc%d", i); From f09613965664e268b00dfc7b4253065e6a0543a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Feb 2025 16:09:19 +0000 Subject: [PATCH 1528/2892] hw/cpu/arm: Alias 'num-cpu' property on TYPE_REALVIEW_MPCORE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to duplicate and forward the 'num-cpu' property from TYPE_ARM11MPCORE_PRIV to TYPE_REALVIEW_MPCORE, alias it with QOM object_property_add_alias(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Message-id: 20250130112615.3219-6-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/cpu/realview_mpcore.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 4268735e3a..7480b38d1a 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -14,7 +14,6 @@ #include "hw/cpu/arm11mpcore.h" #include "hw/intc/realview_gic.h" #include "hw/irq.h" -#include "hw/qdev-properties.h" #include "qom/object.h" #define TYPE_REALVIEW_MPCORE_RIRQ "realview_mpcore" @@ -68,7 +67,6 @@ static void realview_mpcore_realize(DeviceState *dev, Error **errp) int n; int i; - qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); if (!sysbus_realize(SYS_BUS_DEVICE(&s->priv), errp)) { return; } @@ -100,6 +98,7 @@ static void mpcore_rirq_init(Object *obj) int i; object_initialize_child(obj, "a11priv", &s->priv, TYPE_ARM11MPCORE_PRIV); + object_property_add_alias(obj, "num-cpu", OBJECT(&s->priv), "num-cpu"); privbusdev = SYS_BUS_DEVICE(&s->priv); sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0)); @@ -108,16 +107,11 @@ static void mpcore_rirq_init(Object *obj) } } -static const Property mpcore_rirq_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), -}; - static void mpcore_rirq_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = realview_mpcore_realize; - device_class_set_props(dc, mpcore_rirq_properties); } static const TypeInfo mpcore_rirq_info = { From 3272ddee8caf65f31c131dfca2cf1c133edd745c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Feb 2025 16:09:19 +0000 Subject: [PATCH 1529/2892] hw/cpu/arm: Declare CPU QOM types using DEFINE_TYPES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple QOM types are registered in the same file, it is simpler to use the the DEFINE_TYPES() macro. In particular because type array declared with such macro are easier to review. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Message-id: 20250130112615.3219-7-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/cpu/a15mpcore.c | 21 +++++++++------------ hw/cpu/a9mpcore.c | 21 +++++++++------------ hw/cpu/arm11mpcore.c | 21 +++++++++------------ hw/cpu/realview_mpcore.c | 21 +++++++++------------ 4 files changed, 36 insertions(+), 48 deletions(-) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 3b0897e54e..d24ab0a6ab 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -164,17 +164,14 @@ static void a15mp_priv_class_init(ObjectClass *klass, void *data) /* We currently have no saveable state */ } -static const TypeInfo a15mp_priv_info = { - .name = TYPE_A15MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A15MPPrivState), - .instance_init = a15mp_priv_initfn, - .class_init = a15mp_priv_class_init, +static const TypeInfo a15mp_types[] = { + { + .name = TYPE_A15MPCORE_PRIV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A15MPPrivState), + .instance_init = a15mp_priv_initfn, + .class_init = a15mp_priv_class_init, + }, }; -static void a15mp_register_types(void) -{ - type_register_static(&a15mp_priv_info); -} - -type_init(a15mp_register_types) +DEFINE_TYPES(a15mp_types) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 9671585b5f..25416c5032 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -177,17 +177,14 @@ static void a9mp_priv_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, a9mp_priv_properties); } -static const TypeInfo a9mp_priv_info = { - .name = TYPE_A9MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A9MPPrivState), - .instance_init = a9mp_priv_initfn, - .class_init = a9mp_priv_class_init, +static const TypeInfo a9mp_types[] = { + { + .name = TYPE_A9MPCORE_PRIV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A9MPPrivState), + .instance_init = a9mp_priv_initfn, + .class_init = a9mp_priv_class_init, + }, }; -static void a9mp_register_types(void) -{ - type_register_static(&a9mp_priv_info); -} - -type_init(a9mp_register_types) +DEFINE_TYPES(a9mp_types) diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index 94861a06d9..b56bee6d54 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -152,17 +152,14 @@ static void mpcore_priv_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, mpcore_priv_properties); } -static const TypeInfo mpcore_priv_info = { - .name = TYPE_ARM11MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARM11MPCorePriveState), - .instance_init = mpcore_priv_initfn, - .class_init = mpcore_priv_class_init, +static const TypeInfo arm11mp_types[] = { + { + .name = TYPE_ARM11MPCORE_PRIV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARM11MPCorePriveState), + .instance_init = mpcore_priv_initfn, + .class_init = mpcore_priv_class_init, + }, }; -static void arm11mpcore_register_types(void) -{ - type_register_static(&mpcore_priv_info); -} - -type_init(arm11mpcore_register_types) +DEFINE_TYPES(arm11mp_types) diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 7480b38d1a..b140888618 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -114,17 +114,14 @@ static void mpcore_rirq_class_init(ObjectClass *klass, void *data) dc->realize = realview_mpcore_realize; } -static const TypeInfo mpcore_rirq_info = { - .name = TYPE_REALVIEW_MPCORE_RIRQ, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mpcore_rirq_state), - .instance_init = mpcore_rirq_init, - .class_init = mpcore_rirq_class_init, +static const TypeInfo realview_mpcore_types[] = { + { + .name = TYPE_REALVIEW_MPCORE_RIRQ, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(mpcore_rirq_state), + .instance_init = mpcore_rirq_init, + .class_init = mpcore_rirq_class_init, + }, }; -static void realview_mpcore_register_types(void) -{ - type_register_static(&mpcore_rirq_info); -} - -type_init(realview_mpcore_register_types) +DEFINE_TYPES(realview_mpcore_types) From 76723b8ed7b8274a4e30cdf0a9f3e7c47ebe251a Mon Sep 17 00:00:00 2001 From: Andrew Yuan Date: Fri, 7 Feb 2025 16:09:20 +0000 Subject: [PATCH 1530/2892] hw/net/cadence_gem: Fix the mask/compare/disable-mask logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our current handling of the mask/compare logic in the Cadence GEM ethernet device is wrong: (1) we load the same byte twice from rx_buf when creating the compare value (2) we ignore the DISABLE_MASK flag The "Cadence IP for Gigabit Ethernet MAC Part Number: IP7014 IP Rev: R1p12 - Doc Rev: 1.3 User Guide" states that if the DISABLE_MASK bit in type2_compare_x_word_1 is set, the mask_value field in type2_compare_x_word_0 is used as an additional 2 byte Compare Value. Correct these bugs: * in the !disable_mask codepath, use lduw_le_p() so we correctly load a 16-bit value for comparison * in the disable_mask codepath, we load a full 4-byte value from rx_buf for the comparison, set the compare value to the whole of the cr0 register (i.e. the concatenation of the mask and compare fields), and set mask to 0xffffffff to force a 32-bit comparison Signed-off-by: Andrew Yuan Message-id: 20241219061658.805-1-andrew.yuan@jaguarmicro.com Suggested-by: Philippe Mathieu-Daudé [PMM: Expand commit message and comment] Reviewed-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index f744054a6d..80fbbacc1e 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -909,8 +909,8 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, /* Compare A, B, C */ for (j = 0; j < 3; j++) { - uint32_t cr0, cr1, mask, compare; - uint16_t rx_cmp; + uint32_t cr0, cr1, mask, compare, disable_mask; + uint32_t rx_cmp; int offset; int cr_idx = extract32(reg, R_SCREENING_TYPE2_REG0_COMPARE_A_SHIFT + j * 6, R_SCREENING_TYPE2_REG0_COMPARE_A_LENGTH); @@ -946,9 +946,25 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, break; } - rx_cmp = rxbuf_ptr[offset] << 8 | rxbuf_ptr[offset]; - mask = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, MASK_VALUE); - compare = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE); + disable_mask = + FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, DISABLE_MASK); + if (disable_mask) { + /* + * If disable_mask is set, mask_value is used as an + * additional 2 byte Compare Value; that is equivalent + * to using the whole cr0 register as the comparison value. + * Load 32 bits of data from rx_buf, and set mask to + * all-ones so we compare all 32 bits. + */ + rx_cmp = ldl_le_p(rxbuf_ptr + offset); + mask = 0xFFFFFFFF; + compare = cr0; + } else { + rx_cmp = lduw_le_p(rxbuf_ptr + offset); + mask = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, MASK_VALUE); + compare = + FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE); + } if ((rx_cmp & mask) == (compare & mask)) { matched = true; From 751002e74a8e3fc2dd388d57a93e11d3624af6f6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Feb 2025 16:09:20 +0000 Subject: [PATCH 1531/2892] qemu-options: Deprecate -old-param command line option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The '-old-param' command line option is specific to Arm targets; it is very briefly documented as "old param mode". What this option actually does is change the behaviour when directly booting a guest kernel, so that command line arguments are passed to the kernel using the extremely old "param_struct" ABI, rather than the newer ATAGS or even newer DTB mechanisms. This support was added back in 2007 to support an old vendor kernel on the akita/terrier board types: https://mail.gnu.org/archive/html/qemu-devel/2007-07/msg00344.html Even then, it was an out-of-date mechanism from the kernel's point of view -- the kernel has had a comment since 2001 marking it as deprecated. As of mid-2024, the kernel only retained param_struct support for the RiscPC and Footbridge platforms: https://lore.kernel.org/linux-arm-kernel/2831c5a6-cfbf-4fe0-b51c-0396e5b0aeb7@app.fastmail.com/ None of the board types QEMU supports need param_struct support; mark this option as deprecated. Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Message-id: 20250127123113.2947620-1-peter.maydell@linaro.org --- docs/about/deprecated.rst | 13 +++++++++++++ system/vl.c | 1 + 2 files changed, 14 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 29de49351d..a4c7d64223 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -68,6 +68,19 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is marked deprecated since 9.0, users have to ensure that all the topology members described with -smp are supported by the target machine. +``-old-param`` option for booting Arm kernels via param_struct (since 10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``-old-param`` command line option is specific to Arm targets: +it is used when directly booting a guest kernel to pass it the +command line and other information via the old ``param_struct`` ABI, +rather than the newer ATAGS or DTB mechanisms. This option was only +ever needed to support ancient kernels on some old board types +like the ``akita`` or ``terrier``; it has been deprecated in the +kernel since 2001. None of the board types QEMU supports need +``param_struct`` support, so this option has been deprecated and will +be removed in a future QEMU version. + User-mode emulator command line arguments ----------------------------------------- diff --git a/system/vl.c b/system/vl.c index db8e604eba..aa91047c48 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3469,6 +3469,7 @@ void qemu_init(int argc, char **argv) nb_prom_envs++; break; case QEMU_OPTION_old_param: + warn_report("-old-param is deprecated"); old_param = 1; break; case QEMU_OPTION_rtc: From 27a8d899c7a100fd5aa040a8b993bb257687c393 Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Fri, 7 Feb 2025 16:09:20 +0000 Subject: [PATCH 1532/2892] linux-user: Do not define struct sched_attr if libc headers do glibc 2.41+ has added [1] definitions for sched_setattr and sched_getattr functions and struct sched_attr. Therefore, it needs to be checked for here as well before defining sched_attr, to avoid a compilation failure. Define sched_attr conditionally only when SCHED_ATTR_SIZE_VER0 is not defined. [1] https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=21571ca0d70302909cf72707b2a7736cf12190a0;hp=298bc488fdc047da37482f4003023cb9adef78f8 Signed-off-by: Khem Raj Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2799 Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/syscall.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6ee02383da..df5ed18062 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -360,7 +360,8 @@ _syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, #define __NR_sys_sched_setaffinity __NR_sched_setaffinity _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); -/* sched_attr is not defined in glibc */ +/* sched_attr is not defined in glibc < 2.41 */ +#ifndef SCHED_ATTR_SIZE_VER0 struct sched_attr { uint32_t size; uint32_t sched_policy; @@ -373,6 +374,7 @@ struct sched_attr { uint32_t sched_util_min; uint32_t sched_util_max; }; +#endif #define __NR_sys_sched_getattr __NR_sched_getattr _syscall4(int, sys_sched_getattr, pid_t, pid, struct sched_attr *, attr, unsigned int, size, unsigned int, flags); From a802d5c456f55d94fae1833b042a4b574b53dd38 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Feb 2025 13:38:14 -0800 Subject: [PATCH 1533/2892] meson: Drop tcg as a module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit dae0ec159f9 ("accel: build tcg modular"). The attempt was only enabled for x86, only modularized a small portion of tcg, and in more than 3 years there have been no follow-ups to improve the situation. Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 11 ++++------- meson.build | 18 +----------------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index aef80de967..69f4808ac4 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -21,16 +21,13 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', 'watchpoint.c', + 'tcg-accel-ops.c', + 'tcg-accel-ops-mttcg.c', + 'tcg-accel-ops-icount.c', + 'tcg-accel-ops-rr.c', )) system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'icount-common.c', 'monitor.c', )) - -tcg_module_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( - 'tcg-accel-ops.c', - 'tcg-accel-ops-mttcg.c', - 'tcg-accel-ops-icount.c', - 'tcg-accel-ops-rr.c', -)) diff --git a/meson.build b/meson.build index 131b2225ab..e50a103f8a 100644 --- a/meson.build +++ b/meson.build @@ -322,12 +322,6 @@ if cpu in ['x86', 'x86_64'] } endif -modular_tcg = [] -# Darwin does not support references to thread-local variables in modules -if host_os != 'darwin' - modular_tcg = ['i386-softmmu', 'x86_64-softmmu'] -endif - ################## # Compiler flags # ################## @@ -3279,11 +3273,6 @@ foreach target : target_dirs if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, []) config_target += { sym: 'y' } config_all_accel += { sym: 'y' } - if target in modular_tcg - config_target += { 'CONFIG_TCG_MODULAR': 'y' } - else - config_target += { 'CONFIG_TCG_BUILTIN': 'y' } - endif target_kconfig += [ sym + '=y' ] endif endforeach @@ -3642,7 +3631,6 @@ util_ss = ss.source_set() # accel modules qtest_module_ss = ss.source_set() -tcg_module_ss = ss.source_set() modules = {} target_modules = {} @@ -3803,11 +3791,7 @@ subdir('tests/qtest/libqos') subdir('tests/qtest/fuzz') # accel modules -tcg_real_module_ss = ss.source_set() -tcg_real_module_ss.add_all(when: 'CONFIG_TCG_MODULAR', if_true: tcg_module_ss) -specific_ss.add_all(when: 'CONFIG_TCG_BUILTIN', if_true: tcg_module_ss) -target_modules += { 'accel' : { 'qtest': qtest_module_ss, - 'tcg': tcg_real_module_ss }} +target_modules += { 'accel' : { 'qtest': qtest_module_ss }} ############################################## # Internal static_libraries and dependencies # From 33614fa3105c3b348d512e0ec466c82bf322e45c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 3 Feb 2025 12:28:59 -0800 Subject: [PATCH 1534/2892] meson: Disallow 64-bit on 32-bit KVM emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require a 64-bit host binary to spawn a 64-bit guest. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- meson.build | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index e50a103f8a..1af8aeb194 100644 --- a/meson.build +++ b/meson.build @@ -277,21 +277,27 @@ else host_arch = cpu endif -if cpu in ['x86', 'x86_64'] +if cpu == 'x86' + kvm_targets = ['i386-softmmu'] +elif cpu == 'x86_64' kvm_targets = ['i386-softmmu', 'x86_64-softmmu'] elif cpu == 'aarch64' kvm_targets = ['aarch64-softmmu'] elif cpu == 's390x' kvm_targets = ['s390x-softmmu'] -elif cpu in ['ppc', 'ppc64'] +elif cpu == 'ppc' + kvm_targets = ['ppc-softmmu'] +elif cpu == 'ppc64' kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] -elif cpu in ['mips', 'mips64'] +elif cpu == 'mips' + kvm_targets = ['mips-softmmu', 'mipsel-softmmu'] +elif cpu == 'mips64' kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] -elif cpu in ['riscv32'] +elif cpu == 'riscv32' kvm_targets = ['riscv32-softmmu'] -elif cpu in ['riscv64'] +elif cpu == 'riscv64' kvm_targets = ['riscv64-softmmu'] -elif cpu in ['loongarch64'] +elif cpu == 'loongarch64' kvm_targets = ['loongarch64-softmmu'] else kvm_targets = [] From 807a85dfc8542de3cd9f2d37314006ff638d5761 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 08:16:36 -0800 Subject: [PATCH 1535/2892] meson: Disallow 64-bit on 32-bit Xen emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require a 64-bit host binary to spawn a 64-bit guest. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- meson.build | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 1af8aeb194..911955cfa8 100644 --- a/meson.build +++ b/meson.build @@ -304,9 +304,14 @@ else endif accelerator_targets = { 'CONFIG_KVM': kvm_targets } -if cpu in ['x86', 'x86_64'] +if cpu == 'x86' + xen_targets = ['i386-softmmu'] +elif cpu == 'x86_64' xen_targets = ['i386-softmmu', 'x86_64-softmmu'] -elif cpu in ['arm', 'aarch64'] +elif cpu == 'arm' + # i386 emulator provides xenpv machine type for multiple architectures + xen_targets = ['i386-softmmu'] +elif cpu == 'aarch64' # i386 emulator provides xenpv machine type for multiple architectures xen_targets = ['i386-softmmu', 'x86_64-softmmu', 'aarch64-softmmu'] else From 83ef486aeb18c9bb262ccd72c63ad21e5ac56ff3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 08:20:42 -0800 Subject: [PATCH 1536/2892] meson: Disallow 64-bit on 32-bit HVF/NVMM/WHPX emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require a 64-bit host binary to spawn a 64-bit guest. For HVF this is trivially true because macOS 11 dropped support for 32-bit applications entirely. For NVMM, NetBSD only enables nvmm on x86_64: http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/dev/nvmm/Makefile?rev=1.1.6.2;content-type=text%2Fplain For WHPX, we have already dropped support for 32-bit Windows. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- meson.build | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 911955cfa8..85317cd63f 100644 --- a/meson.build +++ b/meson.build @@ -319,13 +319,11 @@ else endif accelerator_targets += { 'CONFIG_XEN': xen_targets } -if cpu in ['aarch64'] +if cpu == 'aarch64' accelerator_targets += { 'CONFIG_HVF': ['aarch64-softmmu'] } -endif - -if cpu in ['x86', 'x86_64'] +elif cpu == 'x86_64' accelerator_targets += { 'CONFIG_HVF': ['x86_64-softmmu'], 'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'], From 35aae9d24c060f5de2cfb3511359818a41e383b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Feb 2025 18:37:22 -0800 Subject: [PATCH 1537/2892] gitlab-ci: Replace aarch64 with arm in cross-i686-tci build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configuration of 64-bit host on 32-bit guest will shortly be denied. Use a 32-bit guest instead. Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- .gitlab-ci.d/crossbuilds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 95dfc39224..7ae0f966f1 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -61,7 +61,7 @@ cross-i686-tci: variables: IMAGE: debian-i686-cross ACCEL: tcg-interpreter - EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user --disable-plugins --disable-kvm + EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,arm-softmmu,arm-linux-user,ppc-softmmu,ppc-linux-user --disable-plugins --disable-kvm # Force tests to run with reduced parallelism, to see whether this # reduces the flakiness of this CI job. The CI # environment by default shows us 8 CPUs and so we From 537600df6141640d411cda5deea742081d2f9962 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Jan 2025 09:51:03 -0800 Subject: [PATCH 1538/2892] configure: Define TARGET_LONG_BITS in configs/targets/*.mak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define TARGET_LONG_BITS in each target's configure fragment. Do this without removing the define in target/*/cpu-param.h so that errors are caught like so: In file included from .../src/include/exec/cpu-defs.h:26, from ../src/target/hppa/cpu.h:24, from ../src/linux-user/qemu.h:4, from ../src/linux-user/hppa/cpu_loop.c:21: ../src/target/hppa/cpu-param.h:11: error: "TARGET_LONG_BITS" redefined [-Werror] 11 | #define TARGET_LONG_BITS 64 | In file included from .../src/include/qemu/osdep.h:36, from ../src/linux-user/hppa/cpu_loop.c:20: ./hppa-linux-user-config-target.h:32: note: this is the location of the previous definition 32 | #define TARGET_LONG_BITS 32 | cc1: all warnings being treated as errors Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- configs/targets/aarch64-bsd-user.mak | 1 + configs/targets/aarch64-linux-user.mak | 1 + configs/targets/aarch64-softmmu.mak | 1 + configs/targets/aarch64_be-linux-user.mak | 1 + configs/targets/alpha-linux-user.mak | 1 + configs/targets/alpha-softmmu.mak | 1 + configs/targets/arm-bsd-user.mak | 1 + configs/targets/arm-linux-user.mak | 1 + configs/targets/arm-softmmu.mak | 1 + configs/targets/armeb-linux-user.mak | 1 + configs/targets/avr-softmmu.mak | 1 + configs/targets/hexagon-linux-user.mak | 1 + configs/targets/hppa-linux-user.mak | 2 ++ configs/targets/hppa-softmmu.mak | 1 + configs/targets/i386-bsd-user.mak | 1 + configs/targets/i386-linux-user.mak | 1 + configs/targets/i386-softmmu.mak | 1 + configs/targets/loongarch64-linux-user.mak | 1 + configs/targets/loongarch64-softmmu.mak | 1 + configs/targets/m68k-linux-user.mak | 1 + configs/targets/m68k-softmmu.mak | 1 + configs/targets/microblaze-linux-user.mak | 1 + configs/targets/microblaze-softmmu.mak | 3 +++ configs/targets/microblazeel-linux-user.mak | 1 + configs/targets/microblazeel-softmmu.mak | 3 +++ configs/targets/mips-linux-user.mak | 1 + configs/targets/mips-softmmu.mak | 1 + configs/targets/mips64-linux-user.mak | 1 + configs/targets/mips64-softmmu.mak | 1 + configs/targets/mips64el-linux-user.mak | 1 + configs/targets/mips64el-softmmu.mak | 1 + configs/targets/mipsel-linux-user.mak | 1 + configs/targets/mipsel-softmmu.mak | 1 + configs/targets/mipsn32-linux-user.mak | 1 + configs/targets/mipsn32el-linux-user.mak | 1 + configs/targets/or1k-linux-user.mak | 1 + configs/targets/or1k-softmmu.mak | 1 + configs/targets/ppc-linux-user.mak | 1 + configs/targets/ppc-softmmu.mak | 1 + configs/targets/ppc64-linux-user.mak | 1 + configs/targets/ppc64-softmmu.mak | 1 + configs/targets/ppc64le-linux-user.mak | 1 + configs/targets/riscv32-linux-user.mak | 1 + configs/targets/riscv32-softmmu.mak | 1 + configs/targets/riscv64-bsd-user.mak | 1 + configs/targets/riscv64-linux-user.mak | 1 + configs/targets/riscv64-softmmu.mak | 1 + configs/targets/rx-softmmu.mak | 1 + configs/targets/s390x-linux-user.mak | 1 + configs/targets/s390x-softmmu.mak | 1 + configs/targets/sh4-linux-user.mak | 1 + configs/targets/sh4-softmmu.mak | 1 + configs/targets/sh4eb-linux-user.mak | 1 + configs/targets/sh4eb-softmmu.mak | 1 + configs/targets/sparc-linux-user.mak | 1 + configs/targets/sparc-softmmu.mak | 1 + configs/targets/sparc32plus-linux-user.mak | 1 + configs/targets/sparc64-linux-user.mak | 1 + configs/targets/sparc64-softmmu.mak | 1 + configs/targets/tricore-softmmu.mak | 1 + configs/targets/x86_64-bsd-user.mak | 1 + configs/targets/x86_64-linux-user.mak | 1 + configs/targets/x86_64-softmmu.mak | 1 + configs/targets/xtensa-linux-user.mak | 1 + configs/targets/xtensa-softmmu.mak | 1 + configs/targets/xtensaeb-linux-user.mak | 1 + configs/targets/xtensaeb-softmmu.mak | 1 + 67 files changed, 72 insertions(+) diff --git a/configs/targets/aarch64-bsd-user.mak b/configs/targets/aarch64-bsd-user.mak index 8aaa5d8c80..f99c73377a 100644 --- a/configs/targets/aarch64-bsd-user.mak +++ b/configs/targets/aarch64-bsd-user.mak @@ -1,3 +1,4 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index 4c6570f56a..b779ac3b4a 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -6,3 +6,4 @@ CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y TARGET_SYSTBL_ABI=common,64,renameat,rlimit,memfd_secret TARGET_SYSTBL=syscall_64.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index 84cb32dc2f..82cb72cb83 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -5,3 +5,4 @@ TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml # needed by boot.c TARGET_NEED_FDT=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index dcef597a80..ef9be02290 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -7,3 +7,4 @@ CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y TARGET_SYSTBL_ABI=common,64,renameat,rlimit,memfd_secret TARGET_SYSTBL=syscall_64.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/alpha-linux-user.mak b/configs/targets/alpha-linux-user.mak index f7d3fb4afa..ef8e365b09 100644 --- a/configs/targets/alpha-linux-user.mak +++ b/configs/targets/alpha-linux-user.mak @@ -1,3 +1,4 @@ TARGET_ARCH=alpha TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak index 9dbe160740..89f3517aca 100644 --- a/configs/targets/alpha-softmmu.mak +++ b/configs/targets/alpha-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=alpha TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/arm-bsd-user.mak b/configs/targets/arm-bsd-user.mak index cb143e6426..472a4f9fb1 100644 --- a/configs/targets/arm-bsd-user.mak +++ b/configs/targets/arm-bsd-user.mak @@ -1,2 +1,3 @@ TARGET_ARCH=arm TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index 7f5d65794c..bf35ded7fe 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -5,3 +5,4 @@ TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index bf390b7a8d..afc64f5927 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -3,3 +3,4 @@ TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml # needed by boot.c TARGET_NEED_FDT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index 943d0d87bf..35fa4d91b3 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -6,3 +6,4 @@ TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/avr-softmmu.mak b/configs/targets/avr-softmmu.mak index e3f921c019..b6157fc465 100644 --- a/configs/targets/avr-softmmu.mak +++ b/configs/targets/avr-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=avr TARGET_XML_FILES= gdb-xml/avr-cpu.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/hexagon-linux-user.mak b/configs/targets/hexagon-linux-user.mak index b912045bd3..aec1a04d1b 100644 --- a/configs/targets/hexagon-linux-user.mak +++ b/configs/targets/hexagon-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=hexagon TARGET_XML_FILES=gdb-xml/hexagon-core.xml gdb-xml/hexagon-hvx.xml TARGET_SYSTBL=syscall.tbl TARGET_SYSTBL_ABI=common,32,hexagon,time32,stat64,rlimit,renameat +TARGET_LONG_BITS=32 diff --git a/configs/targets/hppa-linux-user.mak b/configs/targets/hppa-linux-user.mak index 8e0a80492f..59190f6335 100644 --- a/configs/targets/hppa-linux-user.mak +++ b/configs/targets/hppa-linux-user.mak @@ -3,3 +3,5 @@ TARGET_ABI32=y TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +# Compromise to ease maintenance vs system mode +TARGET_LONG_BITS=64 diff --git a/configs/targets/hppa-softmmu.mak b/configs/targets/hppa-softmmu.mak index a41662aa99..63ca74ed5e 100644 --- a/configs/targets/hppa-softmmu.mak +++ b/configs/targets/hppa-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=hppa TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/i386-bsd-user.mak b/configs/targets/i386-bsd-user.mak index 0283bb62a0..70e098da49 100644 --- a/configs/targets/i386-bsd-user.mak +++ b/configs/targets/i386-bsd-user.mak @@ -1,2 +1,3 @@ TARGET_ARCH=i386 TARGET_XML_FILES= gdb-xml/i386-32bit.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/i386-linux-user.mak b/configs/targets/i386-linux-user.mak index b72a156473..ea68a266fc 100644 --- a/configs/targets/i386-linux-user.mak +++ b/configs/targets/i386-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=i386 TARGET_SYSTBL_ABI=i386 TARGET_SYSTBL=syscall_32.tbl TARGET_XML_FILES= gdb-xml/i386-32bit.xml gdb-xml/i386-32bit-linux.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 2eb0e86250..5dd8921756 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -3,3 +3,4 @@ TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak index dfded79dfa..249a26a798 100644 --- a/configs/targets/loongarch64-linux-user.mak +++ b/configs/targets/loongarch64-linux-user.mak @@ -4,3 +4,4 @@ TARGET_BASE_ARCH=loongarch TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml TARGET_SYSTBL=syscall.tbl TARGET_SYSTBL_ABI=common,64 +TARGET_LONG_BITS=64 diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index ce19ab6a16..351341132f 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -5,3 +5,4 @@ TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml # all boards require libfdt TARGET_NEED_FDT=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/m68k-linux-user.mak b/configs/targets/m68k-linux-user.mak index 579b5d299c..2d9bae2270 100644 --- a/configs/targets/m68k-linux-user.mak +++ b/configs/targets/m68k-linux-user.mak @@ -4,3 +4,4 @@ TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/m68k-softmmu.mak b/configs/targets/m68k-softmmu.mak index bbcd0bada6..bacc52e96a 100644 --- a/configs/targets/m68k-softmmu.mak +++ b/configs/targets/m68k-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=m68k TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblaze-linux-user.mak b/configs/targets/microblaze-linux-user.mak index 0a2322c249..3772779769 100644 --- a/configs/targets/microblaze-linux-user.mak +++ b/configs/targets/microblaze-linux-user.mak @@ -4,3 +4,4 @@ TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index eea266d4f3..99a33ed44a 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -4,3 +4,6 @@ TARGET_SUPPORTS_MTTCG=y # needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml +# System mode can address up to 64 bits via lea/sea instructions. +# TODO: These bypass the mmu, so we could emulate these differently. +TARGET_LONG_BITS=64 diff --git a/configs/targets/microblazeel-linux-user.mak b/configs/targets/microblazeel-linux-user.mak index 270743156a..a51a05488d 100644 --- a/configs/targets/microblazeel-linux-user.mak +++ b/configs/targets/microblazeel-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_HAS_BFLT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index 77b968acad..52cdeae1a2 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -3,3 +3,6 @@ TARGET_SUPPORTS_MTTCG=y # needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml +# System mode can address up to 64 bits via lea/sea instructions. +# TODO: These bypass the mmu, so we could emulate these differently. +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips-linux-user.mak b/configs/targets/mips-linux-user.mak index b4569a9893..69bdc459b6 100644 --- a/configs/targets/mips-linux-user.mak +++ b/configs/targets/mips-linux-user.mak @@ -3,3 +3,4 @@ TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/mips-softmmu.mak b/configs/targets/mips-softmmu.mak index d34b4083fc..b62a088249 100644 --- a/configs/targets/mips-softmmu.mak +++ b/configs/targets/mips-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=mips TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/mips64-linux-user.mak b/configs/targets/mips64-linux-user.mak index d2ff509a11..04e82b3ab1 100644 --- a/configs/targets/mips64-linux-user.mak +++ b/configs/targets/mips64-linux-user.mak @@ -4,3 +4,4 @@ TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips64-softmmu.mak b/configs/targets/mips64-softmmu.mak index 12d9483bf0..7202655fca 100644 --- a/configs/targets/mips64-softmmu.mak +++ b/configs/targets/mips64-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips64el-linux-user.mak b/configs/targets/mips64el-linux-user.mak index f9efeec8ea..27f4169426 100644 --- a/configs/targets/mips64el-linux-user.mak +++ b/configs/targets/mips64el-linux-user.mak @@ -3,3 +3,4 @@ TARGET_ABI_MIPSN64=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak index 3864daa736..3ebeadb29e 100644 --- a/configs/targets/mips64el-softmmu.mak +++ b/configs/targets/mips64el-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips +TARGET_LONG_BITS=64 diff --git a/configs/targets/mipsel-linux-user.mak b/configs/targets/mipsel-linux-user.mak index e8d7241d31..8b7e86ab28 100644 --- a/configs/targets/mipsel-linux-user.mak +++ b/configs/targets/mipsel-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=mips TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl +TARGET_LONG_BITS=32 diff --git a/configs/targets/mipsel-softmmu.mak b/configs/targets/mipsel-softmmu.mak index 0829659fc2..620ec68178 100644 --- a/configs/targets/mipsel-softmmu.mak +++ b/configs/targets/mipsel-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=mips TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/mipsn32-linux-user.mak b/configs/targets/mipsn32-linux-user.mak index 206095da64..39ae214633 100644 --- a/configs/targets/mipsn32-linux-user.mak +++ b/configs/targets/mipsn32-linux-user.mak @@ -5,3 +5,4 @@ TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mipsn32el-linux-user.mak b/configs/targets/mipsn32el-linux-user.mak index ca2a3ed753..d9b61d6990 100644 --- a/configs/targets/mipsn32el-linux-user.mak +++ b/configs/targets/mipsn32el-linux-user.mak @@ -4,3 +4,4 @@ TARGET_ABI32=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/or1k-linux-user.mak b/configs/targets/or1k-linux-user.mak index eecb1e2241..810567a98f 100644 --- a/configs/targets/or1k-linux-user.mak +++ b/configs/targets/or1k-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=openrisc TARGET_BIG_ENDIAN=y TARGET_SYSTBL_ABI=common,32,or1k,time32,stat64,rlimit,renameat TARGET_SYSTBL=syscall.tbl +TARGET_LONG_BITS=32 diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 0341cb2a6b..adfddb1a8a 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -3,3 +3,4 @@ TARGET_SUPPORTS_MTTCG=y TARGET_BIG_ENDIAN=y # needed by boot.c and all boards TARGET_NEED_FDT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/ppc-linux-user.mak b/configs/targets/ppc-linux-user.mak index cc0439a528..970d04a5ba 100644 --- a/configs/targets/ppc-linux-user.mak +++ b/configs/targets/ppc-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common,nospu,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index 53120dab41..9bfa7df6c3 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -2,3 +2,4 @@ TARGET_ARCH=ppc TARGET_BIG_ENDIAN=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/ppc64-linux-user.mak b/configs/targets/ppc64-linux-user.mak index 4d81969f4a..461f1c67d1 100644 --- a/configs/targets/ppc64-linux-user.mak +++ b/configs/targets/ppc64-linux-user.mak @@ -5,3 +5,4 @@ TARGET_SYSTBL_ABI=common,nospu,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index 40881d9396..7cee0e97f4 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -6,3 +6,4 @@ TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml # all boards require libfdt TARGET_NEED_FDT=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/ppc64le-linux-user.mak b/configs/targets/ppc64le-linux-user.mak index 426d5a28d6..cf9d8a400d 100644 --- a/configs/targets/ppc64le-linux-user.mak +++ b/configs/targets/ppc64le-linux-user.mak @@ -4,3 +4,4 @@ TARGET_ABI_DIR=ppc TARGET_SYSTBL_ABI=common,nospu,64 TARGET_SYSTBL=syscall.tbl TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak index 0dbaf5210a..a0ef03c0c3 100644 --- a/configs/targets/riscv32-linux-user.mak +++ b/configs/targets/riscv32-linux-user.mak @@ -7,3 +7,4 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y TARGET_SYSTBL_ABI=32 TARGET_SYSTBL_ABI=common,32,riscv,memfd_secret TARGET_SYSTBL=syscall.tbl +TARGET_LONG_BITS=32 diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak index 338182d5b8..c828066ce6 100644 --- a/configs/targets/riscv32-softmmu.mak +++ b/configs/targets/riscv32-softmmu.mak @@ -4,3 +4,4 @@ TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml # needed by boot.c TARGET_NEED_FDT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/riscv64-bsd-user.mak b/configs/targets/riscv64-bsd-user.mak index 191c2c483f..c6348a7962 100644 --- a/configs/targets/riscv64-bsd-user.mak +++ b/configs/targets/riscv64-bsd-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak index 477cd4523e..aac7568305 100644 --- a/configs/targets/riscv64-linux-user.mak +++ b/configs/targets/riscv64-linux-user.mak @@ -7,3 +7,4 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y TARGET_SYSTBL_ABI=64 TARGET_SYSTBL_ABI=common,64,riscv,rlimit,memfd_secret TARGET_SYSTBL=syscall.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index 6c5de72e03..09f613d24a 100644 --- a/configs/targets/riscv64-softmmu.mak +++ b/configs/targets/riscv64-softmmu.mak @@ -5,3 +5,4 @@ TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-virtual.xml # needed by boot.c TARGET_NEED_FDT=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak index 706bbe6062..1c250a6450 100644 --- a/configs/targets/rx-softmmu.mak +++ b/configs/targets/rx-softmmu.mak @@ -2,3 +2,4 @@ TARGET_ARCH=rx TARGET_XML_FILES= gdb-xml/rx-core.xml # all boards require libfdt TARGET_NEED_FDT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/s390x-linux-user.mak b/configs/targets/s390x-linux-user.mak index 24c04c8589..68c2f28872 100644 --- a/configs/targets/s390x-linux-user.mak +++ b/configs/targets/s390x-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-virt-kvm.xml gdb-xml/s390-gs.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak index b22218aacc..5242ebe7c2 100644 --- a/configs/targets/s390x-softmmu.mak +++ b/configs/targets/s390x-softmmu.mak @@ -3,3 +3,4 @@ TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-virt-kvm.xml gdb-xml/s390-gs.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/sh4-linux-user.mak b/configs/targets/sh4-linux-user.mak index 9908887566..d58c5471b7 100644 --- a/configs/targets/sh4-linux-user.mak +++ b/configs/targets/sh4-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=sh4 TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sh4-softmmu.mak b/configs/targets/sh4-softmmu.mak index f9d62d91e4..787d349b50 100644 --- a/configs/targets/sh4-softmmu.mak +++ b/configs/targets/sh4-softmmu.mak @@ -1 +1,2 @@ TARGET_ARCH=sh4 +TARGET_LONG_BITS=32 diff --git a/configs/targets/sh4eb-linux-user.mak b/configs/targets/sh4eb-linux-user.mak index 9db6b3609c..99007f0f2d 100644 --- a/configs/targets/sh4eb-linux-user.mak +++ b/configs/targets/sh4eb-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sh4eb-softmmu.mak b/configs/targets/sh4eb-softmmu.mak index 226b1fc698..cdea2c61c5 100644 --- a/configs/targets/sh4eb-softmmu.mak +++ b/configs/targets/sh4eb-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=sh4 TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc-linux-user.mak b/configs/targets/sparc-linux-user.mak index abcfb8fc62..4ff4b7287d 100644 --- a/configs/targets/sparc-linux-user.mak +++ b/configs/targets/sparc-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index a5d9200382..78c2e25bd1 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=sparc TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc32plus-linux-user.mak b/configs/targets/sparc32plus-linux-user.mak index 6cc8fa516b..7a16934fd1 100644 --- a/configs/targets/sparc32plus-linux-user.mak +++ b/configs/targets/sparc32plus-linux-user.mak @@ -5,3 +5,4 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 52f05ec000..64ea04e3e2 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -4,3 +4,4 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index 36ca64ec41..f7bab97a00 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -2,3 +2,4 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/tricore-softmmu.mak b/configs/targets/tricore-softmmu.mak index 96b10af853..781ce49a62 100644 --- a/configs/targets/tricore-softmmu.mak +++ b/configs/targets/tricore-softmmu.mak @@ -1 +1,2 @@ TARGET_ARCH=tricore +TARGET_LONG_BITS=32 diff --git a/configs/targets/x86_64-bsd-user.mak b/configs/targets/x86_64-bsd-user.mak index 799cd4acd4..d62d656f2c 100644 --- a/configs/targets/x86_64-bsd-user.mak +++ b/configs/targets/x86_64-bsd-user.mak @@ -1,3 +1,4 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_XML_FILES= gdb-xml/i386-64bit.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/x86_64-linux-user.mak b/configs/targets/x86_64-linux-user.mak index 86042814d3..b093ab5a16 100644 --- a/configs/targets/x86_64-linux-user.mak +++ b/configs/targets/x86_64-linux-user.mak @@ -3,3 +3,4 @@ TARGET_BASE_ARCH=i386 TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall_64.tbl TARGET_XML_FILES= gdb-xml/i386-64bit.xml gdb-xml/i386-64bit-linux.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index 920e9a4200..1ceefde131 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -4,3 +4,4 @@ TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/xtensa-linux-user.mak b/configs/targets/xtensa-linux-user.mak index 420b30a68d..cbec6e368a 100644 --- a/configs/targets/xtensa-linux-user.mak +++ b/configs/targets/xtensa-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=xtensa TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensa-softmmu.mak b/configs/targets/xtensa-softmmu.mak index f075557bfa..65845df4ff 100644 --- a/configs/targets/xtensa-softmmu.mak +++ b/configs/targets/xtensa-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=xtensa TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensaeb-linux-user.mak b/configs/targets/xtensaeb-linux-user.mak index bce2d1d65d..f455b1c780 100644 --- a/configs/targets/xtensaeb-linux-user.mak +++ b/configs/targets/xtensaeb-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensaeb-softmmu.mak b/configs/targets/xtensaeb-softmmu.mak index b02e11b820..f1f789d697 100644 --- a/configs/targets/xtensaeb-softmmu.mak +++ b/configs/targets/xtensaeb-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=xtensa TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 From 175646f64178ba5bfebd6312ac2acd4e1a0e9451 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Jan 2025 09:53:57 -0800 Subject: [PATCH 1539/2892] target/*: Remove TARGET_LONG_BITS from cpu-param.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now handled by the configs/targets/*.mak fragment. Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/alpha/cpu-param.h | 2 -- target/arm/cpu-param.h | 2 -- target/avr/cpu-param.h | 1 - target/hexagon/cpu-param.h | 1 - target/hppa/cpu-param.h | 2 -- target/i386/cpu-param.h | 2 -- target/loongarch/cpu-param.h | 1 - target/m68k/cpu-param.h | 1 - target/microblaze/cpu-param.h | 2 -- target/mips/cpu-param.h | 5 ----- target/openrisc/cpu-param.h | 1 - target/ppc/cpu-param.h | 2 -- target/riscv/cpu-param.h | 2 -- target/rx/cpu-param.h | 1 - target/s390x/cpu-param.h | 1 - target/sh4/cpu-param.h | 1 - target/sparc/cpu-param.h | 2 -- target/tricore/cpu-param.h | 1 - target/xtensa/cpu-param.h | 1 - 19 files changed, 31 deletions(-) diff --git a/target/alpha/cpu-param.h b/target/alpha/cpu-param.h index c21ddf1afd..ff06e41497 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -8,8 +8,6 @@ #ifndef ALPHA_CPU_PARAM_H #define ALPHA_CPU_PARAM_H -#define TARGET_LONG_BITS 64 - /* ??? EV4 has 34 phys addr bits, EV5 has 40, EV6 has 44. */ #define TARGET_PHYS_ADDR_SPACE_BITS 44 diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index bed29613c8..896b35bd6d 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -9,11 +9,9 @@ #define ARM_CPU_PARAM_H #ifdef TARGET_AARCH64 -# define TARGET_LONG_BITS 64 # define TARGET_PHYS_ADDR_SPACE_BITS 52 # define TARGET_VIRT_ADDR_SPACE_BITS 52 #else -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 40 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 93c2f470d0..81f3f49ee1 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -21,7 +21,6 @@ #ifndef AVR_CPU_PARAM_H #define AVR_CPU_PARAM_H -#define TARGET_LONG_BITS 32 /* * TARGET_PAGE_BITS cannot be more than 8 bits because * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 71b4a9b83e..45ee7b4640 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -19,7 +19,6 @@ #define HEXAGON_CPU_PARAM_H #define TARGET_PAGE_BITS 16 /* 64K pages */ -#define TARGET_LONG_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index ef3200f0f3..7ed6b5741e 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -8,8 +8,6 @@ #ifndef HPPA_CPU_PARAM_H #define HPPA_CPU_PARAM_H -#define TARGET_LONG_BITS 64 - #if defined(CONFIG_USER_ONLY) && defined(TARGET_ABI32) # define TARGET_PHYS_ADDR_SPACE_BITS 32 # define TARGET_VIRT_ADDR_SPACE_BITS 32 diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index 8c75abe141..b0e884c5d7 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -9,7 +9,6 @@ #define I386_CPU_PARAM_H #ifdef TARGET_X86_64 -# define TARGET_LONG_BITS 64 # define TARGET_PHYS_ADDR_SPACE_BITS 52 /* * ??? This is really 48 bits, sign-extended, but the only thing @@ -18,7 +17,6 @@ */ # define TARGET_VIRT_ADDR_SPACE_BITS 47 #else -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index db5ad1c69f..52437946e5 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -8,7 +8,6 @@ #ifndef LOONGARCH_CPU_PARAM_H #define LOONGARCH_CPU_PARAM_H -#define TARGET_LONG_BITS 64 #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 diff --git a/target/m68k/cpu-param.h b/target/m68k/cpu-param.h index 5bbe623ba7..7afbf6d302 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -8,7 +8,6 @@ #ifndef M68K_CPU_PARAM_H #define M68K_CPU_PARAM_H -#define TARGET_LONG_BITS 32 /* * Coldfire Linux uses 8k pages * and m68k linux uses 4k pages diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index 00efb509e3..c866ec6c14 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -17,11 +17,9 @@ * of address space. */ #ifdef CONFIG_USER_ONLY -#define TARGET_LONG_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 #else -#define TARGET_LONG_BITS 64 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 #endif diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index f3a37e2dbe..11b3ac0ac6 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -7,11 +7,6 @@ #ifndef MIPS_CPU_PARAM_H #define MIPS_CPU_PARAM_H -#ifdef TARGET_MIPS64 -# define TARGET_LONG_BITS 64 -#else -# define TARGET_LONG_BITS 32 -#endif #ifdef TARGET_ABI_MIPSN64 #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 diff --git a/target/openrisc/cpu-param.h b/target/openrisc/cpu-param.h index 6169ed9f55..37627f2c39 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -8,7 +8,6 @@ #ifndef OPENRISC_CPU_PARAM_H #define OPENRISC_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 13 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index 9c481b9f6c..6c4525fdf3 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -9,7 +9,6 @@ #define PPC_CPU_PARAM_H #ifdef TARGET_PPC64 -# define TARGET_LONG_BITS 64 /* * Note that the official physical address space bits is 62-M where M * is implementation dependent. I've not looked up M for the set of @@ -27,7 +26,6 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 64 # endif #else -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h index 25686192c0..fba30e966a 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -9,11 +9,9 @@ #define RISCV_CPU_PARAM_H #if defined(TARGET_RISCV64) -# define TARGET_LONG_BITS 64 # define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */ # define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */ #elif defined(TARGET_RISCV32) -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */ # define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */ #endif diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h index 521d669bdf..ef1970a09e 100644 --- a/target/rx/cpu-param.h +++ b/target/rx/cpu-param.h @@ -19,7 +19,6 @@ #ifndef RX_CPU_PARAM_H #define RX_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 32 diff --git a/target/s390x/cpu-param.h b/target/s390x/cpu-param.h index a05ffcf78d..5c331ec424 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -8,7 +8,6 @@ #ifndef S390_CPU_PARAM_H #define S390_CPU_PARAM_H -#define TARGET_LONG_BITS 64 #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index a30ba992b3..2b6e11dd0a 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -8,7 +8,6 @@ #ifndef SH4_CPU_PARAM_H #define SH4_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 /* 4k */ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #ifdef CONFIG_USER_ONLY diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h index 14105dc18b..6952ee2b82 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -8,7 +8,6 @@ #define SPARC_CPU_PARAM_H #ifdef TARGET_SPARC64 -# define TARGET_LONG_BITS 64 # define TARGET_PAGE_BITS 13 /* 8k */ # define TARGET_PHYS_ADDR_SPACE_BITS 41 # ifdef TARGET_ABI32 @@ -17,7 +16,6 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 44 # endif #else -# define TARGET_LONG_BITS 32 # define TARGET_PAGE_BITS 12 /* 4k */ # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 diff --git a/target/tricore/cpu-param.h b/target/tricore/cpu-param.h index e29d551dd6..790242ef3d 100644 --- a/target/tricore/cpu-param.h +++ b/target/tricore/cpu-param.h @@ -8,7 +8,6 @@ #ifndef TRICORE_CPU_PARAM_H #define TRICORE_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 14 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 diff --git a/target/xtensa/cpu-param.h b/target/xtensa/cpu-param.h index 0000725f2f..5e4848ad05 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -8,7 +8,6 @@ #ifndef XTENSA_CPU_PARAM_H #define XTENSA_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #ifdef CONFIG_USER_ONLY From acce728cbc6c154b215dfc8d05c12d8fcc2483d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Feb 2025 15:09:23 -0800 Subject: [PATCH 1540/2892] meson: Disallow 64-bit on 32-bit emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For system mode, we can rarely support the amount of RAM that the guest requires. TCG emulation is restricted to round-robin mode, which solves many of the atomicity issues, but not those associated with virtio. In any case, round-robin does nothing to help the speed of emulation. For user mode, most emulation does not succeed at all. Most of the time we cannot even load 64-bit non-PIE binaries due to lack of a 64-bit address space. Threads are run in parallel, not round-robin, which means that atomicity is not handled. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- meson.build | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 85317cd63f..ec51827f40 100644 --- a/meson.build +++ b/meson.build @@ -3185,6 +3185,9 @@ if host_os == 'windows' endif endif +# Detect host pointer size for the target configuration loop. +host_long_bits = cc.sizeof('void *') * 8 + ######################## # Target configuration # ######################## @@ -3277,8 +3280,14 @@ foreach target : target_dirs } endif + config_target += keyval.load('configs/targets' / target + '.mak') + target_kconfig = [] foreach sym: accelerators + # Disallow 64-bit on 32-bit emulation and virtualization + if host_long_bits < config_target['TARGET_LONG_BITS'].to_int() + continue + endif if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, []) config_target += { sym: 'y' } config_all_accel += { sym: 'y' } @@ -3292,9 +3301,6 @@ foreach target : target_dirs error('No accelerator available for target @0@'.format(target)) endif - config_target += keyval.load('configs/targets' / target + '.mak') - config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } - if 'TARGET_NEED_FDT' in config_target and not fdt.found() if default_targets warning('Disabling ' + target + ' due to missing libfdt') @@ -3307,6 +3313,7 @@ foreach target : target_dirs actual_target_dirs += target # Add default keys + config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } if 'TARGET_BASE_ARCH' not in config_target config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']} endif From 6d701c9bac1d3571e9ad511e01b27df7237f0b13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Jan 2025 16:22:24 -0800 Subject: [PATCH 1541/2892] meson: Deprecate 32-bit host support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We deprecated i686 system mode support for qemu 8.0. However, to make real cleanups to TCG we need to deprecate all 32-bit hosts. Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/about/deprecated.rst | 7 +++++++ meson.build | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 4a3c302962..7c61d0ba16 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -204,6 +204,13 @@ is going to be so much slower it wouldn't make sense for any serious instrumentation. Due to implementation differences there will also be anomalies in things like memory instrumentation. +32-bit host operating systems (since 10.0) +'''''''''''''''''''''''''''''''''''''''''' + +Keeping 32-bit host support alive is a substantial burden for the +QEMU project. Thus QEMU will in future drop the support for all +32-bit host systems. + System emulator CPUs -------------------- diff --git a/meson.build b/meson.build index ec51827f40..387490d922 100644 --- a/meson.build +++ b/meson.build @@ -4841,14 +4841,12 @@ if host_arch == 'unknown' message('configure has succeeded and you can continue to build, but') message('QEMU will use a slow interpreter to emulate the target CPU.') endif -elif host_arch == 'mips' +elif host_long_bits < 64 message() warning('DEPRECATED HOST CPU') message() - message('Support for CPU host architecture ' + cpu + ' is going to be') - message('dropped as soon as the QEMU project stops supporting Debian 12') - message('("Bookworm"). Going forward, the QEMU project will not guarantee') - message('that QEMU will compile or work on this host CPU.') + message('Support for 32-bit CPU host architecture ' + cpu + ' is going') + message('to be dropped in a future QEMU release.') endif if not supported_oses.contains(host_os) From 476d6e4c9c4965734d6f47ee299ac9f84440a9b3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Feb 2025 15:38:40 +0100 Subject: [PATCH 1542/2892] rust: restrict missing_const_for_fn to qemu_api crate missing_const_for_fn is not necessarily useful or good. For example in a private API you can always add const later, and in a public API it can be unnecessarily restrictive to annotate everything with const (blocking further improvements to the API). Nevertheless, QEMU turns it on because qemu_api uses const quite aggressively and therefore it can be handy to have as much as possible annotated with const. Outside qemu_api though, not so much: devices are self contained consumers and if there is nothing that could use their functions in const contexts that were not anticipated. Since missing_const_for_fn can be a bit noisy and trigger on trivial functions that no one would ever call in const context, do not turn it on everywhere and only keep it in qemu_api as a special case. Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 1 - rust/qemu-api/src/lib.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 5b6b6ca438..5b0cb55928 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -52,7 +52,6 @@ empty_structs_with_brackets = "deny" ignored_unit_patterns = "deny" implicit_clone = "deny" macro_use_imports = "deny" -missing_const_for_fn = "deny" missing_safety_doc = "deny" multiple_crate_versions = "deny" mut_mut = "deny" diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 83c6a987c0..3cf9371cff 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #![cfg_attr(not(MESON), doc = include_str!("../README.md"))] +#![deny(clippy::missing_const_for_fn)] #[rustfmt::skip] pub mod bindings; From 0fa0b5edaaa84a967f96c62f38ddd2d5c08a975a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:30:56 +0000 Subject: [PATCH 1543/2892] tests/docker: replicate the check-rust-tools-nightly CI job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows people to run the test locally: make docker-test-rust@fedora-rust-nightly Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-2-alex.bennee@linaro.org> --- MAINTAINERS | 1 + tests/docker/Makefile.include | 3 +++ tests/docker/test-rust | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100755 tests/docker/test-rust diff --git a/MAINTAINERS b/MAINTAINERS index 0091bd1a90..aac7404473 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3386,6 +3386,7 @@ F: rust/rustfmt.toml Rust-related patches CC here L: qemu-rust@nongnu.org +F: tests/docker/test-rust F: rust/ SLIRP diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index fead7d3abe..fa1cbb6726 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -236,3 +236,6 @@ docker-image: ${DOCKER_IMAGES:%=docker-image-%} docker-clean: $(call quiet-command, $(DOCKER_SCRIPT) clean) + +# Overrides +docker-test-rust%: NETWORK=1 diff --git a/tests/docker/test-rust b/tests/docker/test-rust new file mode 100755 index 0000000000..e7e3e94a55 --- /dev/null +++ b/tests/docker/test-rust @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Run the rust code checks (a.k.a. check-rust-tools-nightly) +# +# Copyright (c) 2025 Linaro Ltd +# +# Authors: +# Alex Bennée +# +# This work is licensed under the terms of the GNU GPL, version 2 +# or (at your option) any later version. See the COPYING file in +# the top-level directory. + +. common.rc + +cd "$BUILD_DIR" + +configure_qemu --disable-user --disable-docs --enable-rust +pyvenv/bin/meson devenv -w $QEMU_SRC/rust ${CARGO-cargo} fmt --check +make clippy +make rustdoc From 059d06c8a84352a64da963fbe70b978e3f225af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:30:57 +0000 Subject: [PATCH 1544/2892] tests/qtest: don't attempt to clock_step while waiting for virtio ISR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replicates the changes from 92cb8f8bf6 (tests/qtest: remove clock_steps from virtio tests) as there are no timers in the virtio code. We still busy wait and timeout though. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-3-alex.bennee@linaro.org> --- tests/qtest/libqos/virtio-pci-modern.c | 6 ++---- tests/qtest/libqos/virtio-pci.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/qtest/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c index 18d118866f..4e67fcbd5d 100644 --- a/tests/qtest/libqos/virtio-pci-modern.c +++ b/tests/qtest/libqos/virtio-pci-modern.c @@ -173,13 +173,11 @@ static bool get_config_isr_status(QVirtioDevice *d) static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) { - QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); gint64 start_time = g_get_monotonic_time(); - do { + while (!get_config_isr_status(d)) { g_assert(g_get_monotonic_time() - start_time <= timeout_us); - qtest_clock_step(dev->pdev->bus->qts, 100); - } while (!get_config_isr_status(d)); + } } static void queue_select(QVirtioDevice *d, uint16_t index) diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c index 485b8f6b7e..002bf8b8c2 100644 --- a/tests/qtest/libqos/virtio-pci.c +++ b/tests/qtest/libqos/virtio-pci.c @@ -171,13 +171,11 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) { - QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); gint64 start_time = g_get_monotonic_time(); - do { + while (!qvirtio_pci_get_config_isr_status(d)) { g_assert(g_get_monotonic_time() - start_time <= timeout_us); - qtest_clock_step(dev->pdev->bus->qts, 100); - } while (!qvirtio_pci_get_config_isr_status(d)); + } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) From ff6c3b1a16ac2ee570447660526eea531d748396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:30:58 +0000 Subject: [PATCH 1545/2892] tests/qtest: don't step clock at start of npcm7xx periodic IRQ test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until there are timers enabled the semantics of clock_step_next() will fail. Since d524441a36 (system/qtest: properly feedback results of clock_[step|set]) we will signal a FAIL if time doesn't advance. Reviewed-by: Hao Wu Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-4-alex.bennee@linaro.org> --- tests/qtest/npcm7xx_timer-test.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/qtest/npcm7xx_timer-test.c b/tests/qtest/npcm7xx_timer-test.c index 58f58c2f71..43711049ca 100644 --- a/tests/qtest/npcm7xx_timer-test.c +++ b/tests/qtest/npcm7xx_timer-test.c @@ -465,7 +465,6 @@ static void test_periodic_interrupt(gconstpointer test_data) int i; tim_reset(td); - clock_step_next(); tim_write_ticr(td, count); tim_write_tcsr(td, CEN | IE | MODE_PERIODIC | PRESCALE(ps)); From 1e00ebfd8012061c1186fbf368b4216ac6e299c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:30:59 +0000 Subject: [PATCH 1546/2892] tests/qtest: simplify qtest_process_inbuf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't both creating a GString to temporarily hold our qtest command. Instead do a simpler g_strndup and use autofree to clean up afterwards. Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-5-alex.bennee@linaro.org> --- system/qtest.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/system/qtest.c b/system/qtest.c index e68ed0f2a8..bb1efba9fd 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -763,25 +763,21 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } } +/* + * Process as much of @inbuf as we can in newline terminated chunks. + * Remove the processed commands from @inbuf as we go. + */ static void qtest_process_inbuf(CharBackend *chr, GString *inbuf) { char *end; while ((end = strchr(inbuf->str, '\n')) != NULL) { - size_t offset; - GString *cmd; - gchar **words; + size_t len = end - inbuf->str; + g_autofree char *cmd = g_strndup(inbuf->str, len); + g_auto(GStrv) words = g_strsplit(cmd, " ", 0); - offset = end - inbuf->str; - - cmd = g_string_new_len(inbuf->str, offset); - g_string_erase(inbuf, 0, offset + 1); - - words = g_strsplit(cmd->str, " ", 0); + g_string_erase(inbuf, 0, len + 1); qtest_process_command(chr, words); - g_strfreev(words); - - g_string_free(cmd, TRUE); } } From 31ef3c333db3b1293b65da0e8fe602ad5ebcd698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:31:00 +0000 Subject: [PATCH 1547/2892] tests/qtest: rename qtest_send_prefix and roll-up into qtest_send MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qtest_send_prefix never actually sent something over the chardev, all it does is print the timestamp to the QTEST_LOG when enabled. So rename the function, make it static, remove the unused CharDev and simplify all the call sites by handling that directly with qtest_send (and qtest_log_send). Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-6-alex.bennee@linaro.org> --- hw/ppc/spapr_rtas.c | 1 - hw/riscv/riscv_hart.c | 1 - include/system/qtest.h | 1 - system/qtest.c | 26 +++----------------------- 4 files changed, 3 insertions(+), 26 deletions(-) diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index df2e837632..503d441b48 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -565,7 +565,6 @@ static bool spapr_qtest_callback(CharBackend *chr, gchar **words) g_assert(rc == 0); res = qtest_rtas_call(words[1], nargs, args, nret, ret); - qtest_send_prefix(chr); qtest_sendf(chr, "OK %"PRIu64"\n", res); return true; diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index ad67cd7645..a55d156668 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -94,7 +94,6 @@ static bool csr_qtest_callback(CharBackend *chr, gchar **words) g_assert(rc == 0); csr_call(words[1], cpu, csr, &val); - qtest_send_prefix(chr); qtest_sendf(chr, "OK 0 "TARGET_FMT_lx"\n", (target_ulong)val); return true; diff --git a/include/system/qtest.h b/include/system/qtest.h index c161d75165..6ddddc501b 100644 --- a/include/system/qtest.h +++ b/include/system/qtest.h @@ -24,7 +24,6 @@ static inline bool qtest_enabled(void) } #ifndef CONFIG_USER_ONLY -void qtest_send_prefix(CharBackend *chr); void G_GNUC_PRINTF(2, 3) qtest_sendf(CharBackend *chr, const char *fmt, ...); void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)); bool qtest_driver(void); diff --git a/system/qtest.c b/system/qtest.c index bb1efba9fd..28b6fac37c 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -265,7 +265,7 @@ static int hex2nib(char ch) } } -void qtest_send_prefix(CharBackend *chr) +static void qtest_log_timestamp(void) { if (!qtest_log_fp || !qtest_opened) { return; @@ -282,7 +282,7 @@ static void G_GNUC_PRINTF(1, 2) qtest_log_send(const char *fmt, ...) return; } - qtest_send_prefix(NULL); + qtest_log_timestamp(); va_start(ap, fmt); vfprintf(qtest_log_fp, fmt, ap); @@ -301,6 +301,7 @@ static void qtest_server_char_be_send(void *opaque, const char *str) static void qtest_send(CharBackend *chr, const char *str) { + qtest_log_timestamp(); qtest_server_send(qtest_server_send_opaque, str); } @@ -324,7 +325,6 @@ static void qtest_irq_handler(void *opaque, int n, int level) if (irq_levels[n] != level) { CharBackend *chr = &qtest->qtest_chr; irq_levels[n] = level; - qtest_send_prefix(chr); qtest_sendf(chr, "IRQ %s %d\n", level ? "raise" : "lower", n); } @@ -380,19 +380,16 @@ static void qtest_process_command(CharBackend *chr, gchar **words) is_outbound = words[0][14] == 'o'; dev = DEVICE(object_resolve_path(words[1], NULL)); if (!dev) { - qtest_send_prefix(chr); qtest_send(chr, "FAIL Unknown device\n"); return; } if (is_named && !is_outbound) { - qtest_send_prefix(chr); qtest_send(chr, "FAIL Interception of named in-GPIOs not yet supported\n"); return; } if (irq_intercept_dev) { - qtest_send_prefix(chr); if (irq_intercept_dev != dev) { qtest_send(chr, "FAIL IRQ intercept already enabled\n"); } else { @@ -419,7 +416,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } } - qtest_send_prefix(chr); if (interception_succeeded) { irq_intercept_dev = dev; qtest_send(chr, "OK\n"); @@ -438,7 +434,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) dev = DEVICE(object_resolve_path(words[1], NULL)); if (!dev) { - qtest_send_prefix(chr); qtest_send(chr, "FAIL Unknown device\n"); return; } @@ -457,7 +452,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) irq = qdev_get_gpio_in_named(dev, name, num); qemu_set_irq(irq, level); - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "outb") == 0 || strcmp(words[0], "outw") == 0 || @@ -480,7 +474,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else if (words[0][3] == 'l') { cpu_outl(addr, value); } - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "inb") == 0 || strcmp(words[0], "inw") == 0 || @@ -501,7 +494,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else if (words[0][2] == 'l') { value = cpu_inl(addr); } - qtest_send_prefix(chr); qtest_sendf(chr, "OK 0x%04x\n", value); } else if (strcmp(words[0], "writeb") == 0 || strcmp(words[0], "writew") == 0 || @@ -537,7 +529,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, &data, 8); } - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "readb") == 0 || strcmp(words[0], "readw") == 0 || @@ -571,7 +562,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) &value, 8); tswap64s(&value); } - qtest_send_prefix(chr); qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value); } else if (strcmp(words[0], "read") == 0) { g_autoptr(GString) enc = NULL; @@ -593,7 +583,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) enc = qemu_hexdump_line(NULL, data, len, 0, 0); - qtest_send_prefix(chr); qtest_sendf(chr, "OK 0x%s\n", enc->str); g_free(data); @@ -613,7 +602,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, len); b64_data = g_base64_encode(data, len); - qtest_send_prefix(chr); qtest_sendf(chr, "OK %s\n", b64_data); g_free(data); @@ -649,7 +637,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) len); g_free(data); - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "memset") == 0) { uint64_t addr, len; @@ -673,7 +660,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_free(data); } - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "b64write") == 0) { uint64_t addr, len; @@ -705,10 +691,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, len); - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "endianness") == 0) { - qtest_send_prefix(chr); if (target_words_bigendian()) { qtest_sendf(chr, "OK big\n"); } else { @@ -726,7 +710,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) QEMU_TIMER_ATTR_ALL); } new_ns = qemu_clock_advance_virtual_time(old_ns + ns); - qtest_send_prefix(chr); qtest_sendf(chr, "%s %"PRIi64"\n", new_ns > old_ns ? "OK" : "FAIL", new_ns); } else if (strcmp(words[0], "module_load") == 0) { @@ -734,7 +717,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) int rv; g_assert(words[1] && words[2]); - qtest_send_prefix(chr); rv = module_load(words[1], words[2], &local_err); if (rv > 0) { qtest_sendf(chr, "OK\n"); @@ -752,13 +734,11 @@ static void qtest_process_command(CharBackend *chr, gchar **words) ret = qemu_strtoi64(words[1], NULL, 0, &ns); g_assert(ret == 0); new_ns = qemu_clock_advance_virtual_time(ns); - qtest_send_prefix(chr); qtest_sendf(chr, "%s %"PRIi64"\n", new_ns == ns ? "OK" : "FAIL", new_ns); } else if (process_command_cb && process_command_cb(chr, words)) { /* Command got consumed by the callback handler */ } else { - qtest_send_prefix(chr); qtest_sendf(chr, "FAIL Unknown command '%s'\n", words[0]); } } From 14fa0e6b2065bf693e4bdfc3fa0a84e1b8b5ffe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:31:01 +0000 Subject: [PATCH 1548/2892] tests/qtest: tighten up the checks on clock_step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is invalid to call clock_step with an implied time to step forward as if no timers are running we won't be able to advance. Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-7-alex.bennee@linaro.org> --- system/qtest.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/system/qtest.c b/system/qtest.c index 28b6fac37c..12152efbcd 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -708,10 +708,19 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else { ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); + if (ns < 0) { + qtest_send(chr, "FAIL " + "cannot advance clock to the next deadline " + "because there is no pending deadline\n"); + return; + } } new_ns = qemu_clock_advance_virtual_time(old_ns + ns); - qtest_sendf(chr, "%s %"PRIi64"\n", - new_ns > old_ns ? "OK" : "FAIL", new_ns); + if (new_ns > old_ns) { + qtest_sendf(chr, "OK %"PRIi64"\n", new_ns); + } else { + qtest_sendf(chr, "FAIL could not advance time\n"); + } } else if (strcmp(words[0], "module_load") == 0) { Error *local_err = NULL; int rv; From a08eac97615e20facfadc0c27ef116c12b2163b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Feb 2025 15:31:02 +0000 Subject: [PATCH 1549/2892] Revert "util/timer: avoid deadlock when shutting down" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bc02be4508d8753d1f6071b77d10f4661587df6f. Now we catch attempts to clock_step to the next timer when none are enabled we can revert the previous attempt to prevent deadlock. As long as a new target time is given we will move time forward even if no timers will fire. This is desirable for tests which are checking that nothing changes when things are disabled. Previously most tests got away with it because --enable-slirp always has a timer running while the test is active. Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-8-alex.bennee@linaro.org> --- util/qemu-timer.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 0e8a453eaa..3243d2c515 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -675,17 +675,10 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) { int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); AioContext *aio_context; - int64_t deadline; - aio_context = qemu_get_aio_context(); - - deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - /* - * A deadline of < 0 indicates this timer is not enabled, so we - * won't get far trying to run it forward. - */ - while (deadline >= 0 && clock < dest) { + while (clock < dest) { + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); int64_t warp = qemu_soonest_timeout(dest - clock, deadline); qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); @@ -693,9 +686,6 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); } qemu_clock_notify(QEMU_CLOCK_VIRTUAL); From bc2a1f1a45eb9d52d0e22fea93589bab1a6e95be Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 7 Feb 2025 15:31:03 +0000 Subject: [PATCH 1550/2892] tests/qtest/migration: Add --full option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new command line option to allow selecting between running the full set of tests or a smaller set of tests. The default will be to run the small set (i.e. no comand line option provided) so we can reduce the amount of tests run by default. Only hosts which support KVM for the target architecture being tested will run the complete set of tests. Adjust the meson.build file to pass in the --full option when appropriate. (for now, set the option unconditionally until the next patch actually creates the small set) Use cases: configure --target-list=aarch64-softmmu,ppc64-softmmu,s390x-softmmu,x86_64-softmmu | before - 615s/244 tests | after - 244s/100 tests ------------------------+--------------------------+----------------------------- make check | full set for all archs | full set for the KVM arch, make check-qtest | | small set for the rest | | qemu-system-$ARCH | full set for $ARCH | small set for $ARCH, KVM or ./migration-test | | TCG automatically chosen | | qemu-system-$ARCH | N/A | full set for $ARCH, KVM or ./migration-test --full | | TCG automatically chosen | | migration-compat-x86_64 | full set for x86_64 | small set for x86_64 CI job | | ------------------------+--------------------------+----------------------------- Signed-off-by: Fabiano Rosas Reviewed-by: Peter Xu Message-Id: <20250130184012.5711-2-farosas@suse.de> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-9-alex.bennee@linaro.org> --- tests/qtest/meson.build | 11 +++++++++- tests/qtest/migration-test.c | 26 +++++++++++++++++++++++ tests/qtest/migration/compression-tests.c | 4 ++++ tests/qtest/migration/cpr-tests.c | 4 ++++ tests/qtest/migration/file-tests.c | 4 ++++ tests/qtest/migration/framework.h | 1 + tests/qtest/migration/misc-tests.c | 4 ++++ tests/qtest/migration/postcopy-tests.c | 4 ++++ tests/qtest/migration/precopy-tests.c | 4 ++++ tests/qtest/migration/tls-tests.c | 4 ++++ 10 files changed, 65 insertions(+), 1 deletion(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5e062c752a..68316dbdc1 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -405,6 +405,8 @@ foreach dir : target_dirs target_base = dir.split('-')[0] qtest_emulator = emulators['qemu-system-' + target_base] target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic + has_kvm = ('CONFIG_KVM' in config_all_accel and host_os == 'linux' + and cpu == target_base and fs.exists('/dev/kvm')) test_deps = roms qtest_env = environment() @@ -438,11 +440,18 @@ foreach dir : target_dirs test: executable(test, src, dependencies: deps) } endif + + test_args = ['--tap', '-k'] + + if test == 'migration-test' and has_kvm + test_args += ['--full'] + endif + test('qtest-@0@/@1@'.format(target_base, test), qtest_executables[test], depends: [test_deps, qtest_emulator, emulator_modules], env: qtest_env, - args: ['--tap', '-k'], + args: test_args, protocol: 'tap', timeout: slow_qtests.get(test, 60), priority: slow_qtests.get(test, 60), diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 5cad5060b3..61180fcbd5 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -14,13 +14,39 @@ #include "migration/framework.h" #include "qemu/module.h" +static void parse_args(int *argc_p, char ***argv_p, bool *full_set) +{ + int argc = *argc_p; + char **argv = *argv_p; + int i, j; + + j = 1; + for (i = 1; i < argc; i++) { + if (g_str_equal(argv[i], "--full")) { + *full_set = true; + continue; + } + argv[j++] = argv[i]; + if (i >= j) { + argv[i] = NULL; + } + } + *argc_p = j; +} + int main(int argc, char **argv) { MigrationTestEnv *env; int ret; + bool full_set = false; + + /* strip the --full option if it's present */ + parse_args(&argc, &argv, &full_set); g_test_init(&argc, &argv, NULL); env = migration_get_env(); + env->full_set = full_set; + env->full_set = true; /* temporary */ module_call_init(MODULE_INIT_QOM); migration_test_add_tls(env); diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c index d78f1f11f1..9d0a258d51 100644 --- a/tests/qtest/migration/compression-tests.c +++ b/tests/qtest/migration/compression-tests.c @@ -155,6 +155,10 @@ void migration_test_add_compression(MigrationTestEnv *env) { tmpfs = env->tmpfs; + if (!env->full_set) { + return; + } + #ifdef CONFIG_ZSTD migration_test_add("/migration/multifd/tcp/plain/zstd", test_multifd_tcp_zstd); diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 215b0df8c0..b1651fe48c 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -104,6 +104,10 @@ void migration_test_add_cpr(MigrationTestEnv *env) { tmpfs = env->tmpfs; + if (!env->full_set) { + return; + } + /* * Our CI system has problems with shared memory. * Don't run this test until we find a workaround. diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c index 84225c8c33..5b190853a5 100644 --- a/tests/qtest/migration/file-tests.c +++ b/tests/qtest/migration/file-tests.c @@ -304,6 +304,10 @@ void migration_test_add_file(MigrationTestEnv *env) { tmpfs = env->tmpfs; + if (!env->full_set) { + return; + } + migration_test_add("/migration/precopy/file", test_precopy_file); diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index cb4a984700..e4a11870f6 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -24,6 +24,7 @@ typedef struct MigrationTestEnv { bool uffd_feature_thread_id; bool has_dirty_ring; bool is_x86; + bool full_set; const char *arch; const char *qemu_src; const char *qemu_dst; diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index dda3707cf3..996256ef87 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -262,6 +262,10 @@ void migration_test_add_misc(MigrationTestEnv *env) { tmpfs = env->tmpfs; + if (!env->full_set) { + return; + } + migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c index daf7449f2c..b08cde3270 100644 --- a/tests/qtest/migration/postcopy-tests.c +++ b/tests/qtest/migration/postcopy-tests.c @@ -81,6 +81,10 @@ static void test_postcopy_preempt_recovery(void) void migration_test_add_postcopy(MigrationTestEnv *env) { + if (!env->full_set) { + return; + } + if (env->has_uffd) { migration_test_add("/migration/postcopy/plain", test_postcopy); migration_test_add("/migration/postcopy/recovery/plain", diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 436dbd98e8..c99a487bf1 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -955,6 +955,10 @@ void migration_test_add_precopy(MigrationTestEnv *env) { tmpfs = env->tmpfs; + if (!env->full_set) { + return; + } + if (env->is_x86) { migration_test_add("/migration/precopy/unix/suspend/live", test_precopy_unix_suspend_live); diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index 5704a1f992..aee56930ac 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -726,6 +726,10 @@ void migration_test_add_tls(MigrationTestEnv *env) { tmpfs = env->tmpfs; + if (!env->full_set) { + return; + } + migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); From 43ab3fb375ebb55c5b2ee7b88945f1466e7e2d5f Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 7 Feb 2025 15:31:04 +0000 Subject: [PATCH 1551/2892] tests/qtest/migration: Pick smoke tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Choose a few tests per group and move them from the full set to the smoke set. Signed-off-by: Fabiano Rosas Reviewed-by: Peter Xu Message-Id: <20250130184012.5711-3-farosas@suse.de> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-10-alex.bennee@linaro.org> --- tests/qtest/migration-test.c | 1 - tests/qtest/migration/compression-tests.c | 11 ++++++--- tests/qtest/migration/cpr-tests.c | 2 ++ tests/qtest/migration/file-tests.c | 17 +++++++++----- tests/qtest/migration/misc-tests.c | 12 +++++++--- tests/qtest/migration/postcopy-tests.c | 18 ++++++++++----- tests/qtest/migration/precopy-tests.c | 27 ++++++++++++++--------- tests/qtest/migration/tls-tests.c | 10 +++++++-- 8 files changed, 67 insertions(+), 31 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 61180fcbd5..0893687174 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -46,7 +46,6 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); env = migration_get_env(); env->full_set = full_set; - env->full_set = true; /* temporary */ module_call_init(MODULE_INIT_QOM); migration_test_add_tls(env); diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c index 9d0a258d51..8b58401b84 100644 --- a/tests/qtest/migration/compression-tests.c +++ b/tests/qtest/migration/compression-tests.c @@ -151,10 +151,18 @@ static void test_multifd_tcp_zlib(void) test_precopy_common(&args); } +static void migration_test_add_compression_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/multifd/tcp/plain/zlib", + test_multifd_tcp_zlib); +} + void migration_test_add_compression(MigrationTestEnv *env) { tmpfs = env->tmpfs; + migration_test_add_compression_smoke(env); + if (!env->full_set) { return; } @@ -183,7 +191,4 @@ void migration_test_add_compression(MigrationTestEnv *env) migration_test_add("/migration/precopy/unix/xbzrle", test_precopy_unix_xbzrle); } - - migration_test_add("/migration/multifd/tcp/plain/zlib", - test_multifd_tcp_zlib); } diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index b1651fe48c..4758841824 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -104,6 +104,8 @@ void migration_test_add_cpr(MigrationTestEnv *env) { tmpfs = env->tmpfs; + /* no tests in the smoke set for now */ + if (!env->full_set) { return; } diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c index 5b190853a5..f474b3590a 100644 --- a/tests/qtest/migration/file-tests.c +++ b/tests/qtest/migration/file-tests.c @@ -300,17 +300,25 @@ static void test_multifd_file_mapped_ram_fdset_dio(void) } #endif /* !_WIN32 */ +static void migration_test_add_file_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/precopy/file", + test_precopy_file); + + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); +} + void migration_test_add_file(MigrationTestEnv *env) { tmpfs = env->tmpfs; + migration_test_add_file_smoke(env); + if (!env->full_set) { return; } - migration_test_add("/migration/precopy/file", - test_precopy_file); - migration_test_add("/migration/precopy/file/offset", test_precopy_file_offset); #ifndef _WIN32 @@ -330,9 +338,6 @@ void migration_test_add_file(MigrationTestEnv *env) migration_test_add("/migration/multifd/file/mapped-ram/live", test_multifd_file_mapped_ram_live); - migration_test_add("/migration/multifd/file/mapped-ram/dio", - test_multifd_file_mapped_ram_dio); - #ifndef _WIN32 migration_test_add("/migration/multifd/file/mapped-ram/fdset", test_multifd_file_mapped_ram_fdset); diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index 996256ef87..d03199f3c5 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -258,18 +258,24 @@ static void test_validate_uri_channels_none_set(void) do_test_validate_uri_channel(&args); } +static void migration_test_add_misc_smoke(MigrationTestEnv *env) +{ +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif +} + void migration_test_add_misc(MigrationTestEnv *env) { tmpfs = env->tmpfs; + migration_test_add_misc_smoke(env); + if (!env->full_set) { return; } migration_test_add("/migration/bad_dest", test_baddest); -#ifndef _WIN32 - migration_test_add("/migration/analyze-script", test_analyze_script); -#endif /* * Our CI system has problems with shared memory. diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c index b08cde3270..7e84e60fea 100644 --- a/tests/qtest/migration/postcopy-tests.c +++ b/tests/qtest/migration/postcopy-tests.c @@ -79,18 +79,26 @@ static void test_postcopy_preempt_recovery(void) test_postcopy_recovery_common(&args); } -void migration_test_add_postcopy(MigrationTestEnv *env) +static void migration_test_add_postcopy_smoke(MigrationTestEnv *env) { - if (!env->full_set) { - return; - } - if (env->has_uffd) { migration_test_add("/migration/postcopy/plain", test_postcopy); migration_test_add("/migration/postcopy/recovery/plain", test_postcopy_recovery); migration_test_add("/migration/postcopy/preempt/plain", test_postcopy_preempt); + } +} + +void migration_test_add_postcopy(MigrationTestEnv *env) +{ + migration_test_add_postcopy_smoke(env); + + if (!env->full_set) { + return; + } + + if (env->has_uffd) { migration_test_add("/migration/postcopy/preempt/recovery/plain", test_postcopy_preempt_recovery); diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index c99a487bf1..b16f10e908 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -951,14 +951,8 @@ static void test_dirty_limit(void) migrate_end(from, to, true); } -void migration_test_add_precopy(MigrationTestEnv *env) +static void migration_test_add_precopy_smoke(MigrationTestEnv *env) { - tmpfs = env->tmpfs; - - if (!env->full_set) { - return; - } - if (env->is_x86) { migration_test_add("/migration/precopy/unix/suspend/live", test_precopy_unix_suspend_live); @@ -970,6 +964,21 @@ void migration_test_add_precopy(MigrationTestEnv *env) test_precopy_unix_plain); migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + migration_test_add("/migration/multifd/tcp/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/plain/cancel", + test_multifd_tcp_cancel); +} + +void migration_test_add_precopy(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_precopy_smoke(env); + + if (!env->full_set) { + return; + } migration_test_add("/migration/precopy/tcp/plain/switchover-ack", test_precopy_tcp_switchover_ack); @@ -993,16 +1002,12 @@ void migration_test_add_precopy(MigrationTestEnv *env) test_dirty_limit); } } - migration_test_add("/migration/multifd/tcp/uri/plain/none", - test_multifd_tcp_uri_none); migration_test_add("/migration/multifd/tcp/channels/plain/none", test_multifd_tcp_channels_none); migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", test_multifd_tcp_zero_page_legacy); migration_test_add("/migration/multifd/tcp/plain/zero-page/none", test_multifd_tcp_no_zero_page); - migration_test_add("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); if (g_str_equal(env->arch, "x86_64") && env->has_kvm && env->has_dirty_ring) { diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index aee56930ac..2cb4a44bcd 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -722,10 +722,18 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void) } #endif /* CONFIG_TASN1 */ +static void migration_test_add_tls_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/precopy/tcp/tls/psk/match", + test_precopy_tcp_tls_psk_match); +} + void migration_test_add_tls(MigrationTestEnv *env) { tmpfs = env->tmpfs; + migration_test_add_tls_smoke(env); + if (!env->full_set) { return; } @@ -755,8 +763,6 @@ void migration_test_add_tls(MigrationTestEnv *env) test_precopy_unix_tls_x509_override_host); #endif /* CONFIG_TASN1 */ - migration_test_add("/migration/precopy/tcp/tls/psk/match", - test_precopy_tcp_tls_psk_match); migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", test_precopy_tcp_tls_psk_mismatch); #ifdef CONFIG_TASN1 From 9853485dd420a567500019752d011cc8d38f7199 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:05 +0000 Subject: [PATCH 1552/2892] gdbstub: Allow the %d placeholder in the socket path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like for QEMU_LOG_FILENAME, replace %d with PID in the GDB socket path. This allows running multi-process applications with, e.g., export QEMU_GDB=/tmp/qemu-%d.sock. Currently this is not possible, since the first process will cause the subsequent ones to fail due to not being able to bind() the GDB socket. Reviewed-by: Richard Henderson Reviewed-by: Warner Losh Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-2-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-11-alex.bennee@linaro.org> --- gdbstub/user.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gdbstub/user.c b/gdbstub/user.c index c2bdfc3d49..fd29d595f4 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -317,9 +317,19 @@ static bool gdb_accept_socket(int gdb_fd) static int gdbserver_open_socket(const char *path) { + g_autoptr(GString) buf = g_string_new(""); struct sockaddr_un sockaddr = {}; + char *pid_placeholder; int fd, ret; + pid_placeholder = strstr(path, "%d"); + if (pid_placeholder != NULL) { + g_string_append_len(buf, path, pid_placeholder - path); + g_string_append_printf(buf, "%d", qemu_get_thread_id()); + g_string_append(buf, pid_placeholder + 2); + path = buf->str; + } + fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("create socket"); From fccb744f41c69fec6fd92225fe907c6e69de5d44 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:06 +0000 Subject: [PATCH 1553/2892] gdbstub: Try unlinking the unix socket before binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case an emulated process execve()s another emulated process, bind() will fail, because the socket already exists. So try deleting it. Use the existing unix_listen() function which does this. Link qemu-user with qemu-sockets.c and add the monitor_get_fd() stub. Note that it is not possible to handle this in do_execv(): deleting gdbserver_user_state.socket_path before safe_execve() is not correct, because the latter may fail, and afterwards we may lose control. Signed-off-by: Ilya Leoshkevich Reviewed-by: Alex Bennée Message-Id: <20250117001542.8290-3-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-12-alex.bennee@linaro.org> --- gdbstub/user.c | 29 +++-------------------------- stubs/meson.build | 2 ++ stubs/monitor-fd.c | 9 +++++++++ util/meson.build | 2 ++ 4 files changed, 16 insertions(+), 26 deletions(-) create mode 100644 stubs/monitor-fd.c diff --git a/gdbstub/user.c b/gdbstub/user.c index fd29d595f4..8225b70280 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -315,12 +315,10 @@ static bool gdb_accept_socket(int gdb_fd) return true; } -static int gdbserver_open_socket(const char *path) +static int gdbserver_open_socket(const char *path, Error **errp) { g_autoptr(GString) buf = g_string_new(""); - struct sockaddr_un sockaddr = {}; char *pid_placeholder; - int fd, ret; pid_placeholder = strstr(path, "%d"); if (pid_placeholder != NULL) { @@ -330,28 +328,7 @@ static int gdbserver_open_socket(const char *path) path = buf->str; } - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - perror("create socket"); - return -1; - } - - sockaddr.sun_family = AF_UNIX; - pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path); - ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (ret < 0) { - perror("bind socket"); - close(fd); - return -1; - } - ret = listen(fd, 1); - if (ret < 0) { - perror("listen socket"); - close(fd); - return -1; - } - - return fd; + return unix_listen(path, errp); } static bool gdb_accept_tcp(int gdb_fd) @@ -424,7 +401,7 @@ bool gdbserver_start(const char *port_or_path, Error **errp) if (port > 0) { gdb_fd = gdbserver_open_port(port, errp); } else { - gdb_fd = gdbserver_open_socket(port_or_path); + gdb_fd = gdbserver_open_socket(port_or_path, errp); } if (gdb_fd < 0) { diff --git a/stubs/meson.build b/stubs/meson.build index a8b3aeb564..b0fee37e05 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -61,6 +61,8 @@ if have_user if not have_system stub_ss.add(files('qdev.c')) endif + + stub_ss.add(files('monitor-fd.c')) endif if have_system diff --git a/stubs/monitor-fd.c b/stubs/monitor-fd.c new file mode 100644 index 0000000000..9bb6749885 --- /dev/null +++ b/stubs/monitor-fd.c @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "monitor/monitor.h" + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + abort(); +} diff --git a/util/meson.build b/util/meson.build index 5d8bef9891..780b5977a8 100644 --- a/util/meson.build +++ b/util/meson.build @@ -84,6 +84,8 @@ if have_block or have_ga util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c')) util_ss.add(files(f'coroutine-@coroutine_backend@.c')) util_ss.add(files('thread-pool.c', 'qemu-timer.c')) +endif +if have_block or have_ga or have_user util_ss.add(files('qemu-sockets.c')) endif if have_block From 08916fd4b6b308941568ecd7305455121ce7c267 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:07 +0000 Subject: [PATCH 1554/2892] user: Introduce user/signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gdbstub needs target_to_host_signal(), so move its declaration to a public header. Reviewed-by: Richard Henderson Reviewed-by: Warner Losh Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-4-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-13-alex.bennee@linaro.org> --- bsd-user/signal-common.h | 1 - bsd-user/signal.c | 1 + include/user/signal.h | 23 +++++++++++++++++++++++ linux-user/signal-common.h | 1 - linux-user/signal.c | 1 + linux-user/syscall.c | 1 + 6 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 include/user/signal.h diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 77d7c7a78b..4e634e04a3 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -42,7 +42,6 @@ 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); /* diff --git a/bsd-user/signal.c b/bsd-user/signal.c index b4e1458237..8c51f6ce65 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -24,6 +24,7 @@ #include "user/cpu_loop.h" #include "exec/page-protection.h" #include "user/page-protection.h" +#include "user/signal.h" #include "user/tswap-target.h" #include "gdbstub/user.h" #include "signal-common.h" diff --git a/include/user/signal.h b/include/user/signal.h new file mode 100644 index 0000000000..19b6b9e5dd --- /dev/null +++ b/include/user/signal.h @@ -0,0 +1,23 @@ +/* + * Signal-related declarations. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef USER_SIGNAL_H +#define USER_SIGNAL_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +/** + * target_to_host_signal: + * @sig: target signal. + * + * On success, return the host signal between 0 (inclusive) and NSIG + * (exclusive) corresponding to the target signal @sig. Return any other value + * on failure. + */ +int target_to_host_signal(int sig); + +#endif diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 8584d9ecc2..196d2406f8 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -61,7 +61,6 @@ void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info); void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo); -int target_to_host_signal(int sig); int host_to_target_signal(int sig); long do_sigreturn(CPUArchState *env); long do_rt_sigreturn(CPUArchState *env); diff --git a/linux-user/signal.c b/linux-user/signal.c index 087c4d270e..bffbef235c 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -36,6 +36,7 @@ #include "user/cpu_loop.h" #include "user/page-protection.h" #include "user/safe-syscall.h" +#include "user/signal.h" #include "tcg/tcg.h" /* target_siginfo_t must fit in gdbstub's siginfo save area. */ diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6ee02383da..90afaa4426 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -138,6 +138,7 @@ #include "user-mmap.h" #include "user/page-protection.h" #include "user/safe-syscall.h" +#include "user/signal.h" #include "qemu/guest-random.h" #include "qemu/selfmap.h" #include "user/syscall-trace.h" From 2b3ccf5f0db7c75ba990da7e6223c3f7319480c0 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:08 +0000 Subject: [PATCH 1555/2892] user: Introduce host_interrupt_signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attaching to the gdbstub of a running process requires stopping its threads. For threads that run on a CPU, cpu_exit() is enough, but the only way to grab attention of a thread that is stuck in a long-running syscall is to interrupt it with a signal. Reserve a host realtime signal for this, just like it's already done for TARGET_SIGABRT on Linux. This may reduce the number of available guest realtime signals by one, but this is acceptable, since there are quite a lot of them, and it's unlikely that there are apps that need them all. Set signal_pending for the safe_sycall machinery to prevent invoking the syscall. This is a lie, since we don't queue a guest signal, but process_pending_signals() can handle the absence of pending signals. The syscall returns with QEMU_ERESTARTSYS errno, which arranges for the automatic restart. This is important, because it helps avoiding disturbing poorly written guests. Reviewed-by: Warner Losh Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-5-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-14-alex.bennee@linaro.org> --- bsd-user/signal.c | 12 ++++++++++++ include/user/signal.h | 2 ++ linux-user/signal.c | 25 ++++++++++++++++++++----- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 8c51f6ce65..ff2ccbbf60 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -51,6 +51,8 @@ static inline int sas_ss_flags(TaskState *ts, unsigned long sp) on_sig_stack(ts, sp) ? SS_ONSTACK : 0; } +int host_interrupt_signal = SIGRTMAX; + /* * The BSD ABIs use the same signal numbers across all the CPU architectures, so * (unlike Linux) these functions are just the identity mapping. This might not @@ -491,6 +493,12 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) uintptr_t pc = 0; bool sync_sig = false; + if (host_sig == host_interrupt_signal) { + ts->signal_pending = 1; + cpu_exit(thread_cpu); + return; + } + /* * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special * handling wrt signal blocking and unwinding. @@ -854,6 +862,9 @@ void signal_init(void) for (i = 1; i <= TARGET_NSIG; i++) { host_sig = target_to_host_signal(i); + if (host_sig == host_interrupt_signal) { + continue; + } sigaction(host_sig, NULL, &oact); if (oact.sa_sigaction == (void *)SIG_IGN) { sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN; @@ -872,6 +883,7 @@ void signal_init(void) sigaction(host_sig, &act, NULL); } } + sigaction(host_interrupt_signal, &act, NULL); } static void handle_pending_signal(CPUArchState *env, int sig, diff --git a/include/user/signal.h b/include/user/signal.h index 19b6b9e5dd..7fa33b05d9 100644 --- a/include/user/signal.h +++ b/include/user/signal.h @@ -20,4 +20,6 @@ */ int target_to_host_signal(int sig); +extern int host_interrupt_signal; + #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index bffbef235c..81a98c6d02 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -517,6 +517,8 @@ static int core_dump_signal(int sig) } } +int host_interrupt_signal; + static void signal_table_init(const char *rtsig_map) { int hsig, tsig, count; @@ -580,10 +582,10 @@ static void signal_table_init(const char *rtsig_map) * Attempts for configure "missing" signals via sigaction will be * silently ignored. * - * Reserve one signal for internal usage (see below). + * Reserve two signals for internal usage (see below). */ - hsig = SIGRTMIN + 1; + hsig = SIGRTMIN + 2; for (tsig = TARGET_SIGRTMIN; hsig <= SIGRTMAX && tsig <= TARGET_NSIG; hsig++, tsig++) { @@ -604,12 +606,17 @@ static void signal_table_init(const char *rtsig_map) host_to_target_signal_table[SIGABRT] = 0; for (hsig = SIGRTMIN; hsig <= SIGRTMAX; hsig++) { if (!host_to_target_signal_table[hsig]) { - host_to_target_signal_table[hsig] = TARGET_SIGABRT; - break; + if (host_interrupt_signal) { + host_to_target_signal_table[hsig] = TARGET_SIGABRT; + break; + } else { + host_interrupt_signal = hsig; + } } } if (hsig > SIGRTMAX) { - fprintf(stderr, "No rt signals left for SIGABRT mapping\n"); + fprintf(stderr, + "No rt signals left for interrupt and SIGABRT mapping\n"); exit(EXIT_FAILURE); } @@ -689,6 +696,8 @@ void signal_init(const char *rtsig_map) } sigact_table[tsig - 1]._sa_handler = thand; } + + sigaction(host_interrupt_signal, &act, NULL); } /* Force a synchronously taken signal. The kernel force_sig() function @@ -1036,6 +1045,12 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) bool sync_sig = false; void *sigmask; + if (host_sig == host_interrupt_signal) { + ts->signal_pending = 1; + cpu_exit(thread_cpu); + return; + } + /* * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special * handling wrt signal blocking and unwinding. Non-spoofed SIGILL, From a33dcfe771c7c2460f2a6809c3b73a5d0772396f Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:09 +0000 Subject: [PATCH 1556/2892] osdep: Introduce qemu_kill_thread() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function for sending signals to individual threads. It does not make sense on Windows, so do not provide an implementation, so that if someone uses it by accident, they will get a linker error. Reviewed-by: Richard Henderson Reviewed-by: Warner Losh Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-6-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-15-alex.bennee@linaro.org> --- include/qemu/osdep.h | 9 +++++++++ util/oslib-posix.c | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 112ebdff21..4397a90680 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -631,6 +631,15 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp); int qemu_get_thread_id(void); +/** + * qemu_kill_thread: + * @tid: thread id. + * @sig: host signal. + * + * Send @sig to one of QEMU's own threads with identifier @tid. + */ +int qemu_kill_thread(int tid, int sig); + #ifndef CONFIG_IOVEC struct iovec { void *iov_base; diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 2bb34dade3..a697c602c6 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -111,6 +111,21 @@ int qemu_get_thread_id(void) #endif } +int qemu_kill_thread(int tid, int sig) +{ +#if defined(__linux__) + return syscall(__NR_tgkill, getpid(), tid, sig); +#elif defined(__FreeBSD__) + return thr_kill2(getpid(), tid, sig); +#elif defined(__NetBSD__) + return _lwp_kill(tid, sig); +#elif defined(__OpenBSD__) + return thrkill(tid, sig, NULL); +#else + return kill(tid, sig); +#endif +} + int qemu_daemon(int nochdir, int noclose) { return daemon(nochdir, noclose); From d156d5d1df12a73e06092dc5fa6595d6f16f8521 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:10 +0000 Subject: [PATCH 1557/2892] gdbstub: Allow late attachment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow debugging individual processes in multi-process applications by starting them with export QEMU_GDB=/tmp/qemu-%d.sock,suspend=n. Currently one would have to attach to every process to ensure the app makes progress. In case suspend=n is not specified, the flow remains unchanged. If it is specified, then accepting the client connection is delegated to a thread. In the future this machinery may be reused for handling reconnections and interruptions. On accepting a connection, the thread schedules gdb_handlesig() on the first CPU and wakes it up with host_interrupt_signal. Note that the result of this gdb_handlesig() invocation is handled, as opposed to many other existing call sites. These other call sites probably need to be fixed separately. Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-7-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-16-alex.bennee@linaro.org> --- bsd-user/main.c | 1 - gdbstub/user.c | 117 ++++++++++++++++++++++++++++++++++++++++------ linux-user/main.c | 1 - 3 files changed, 104 insertions(+), 15 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index b2f6a9be2f..fdb160bed0 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -629,7 +629,6 @@ int main(int argc, char **argv) if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); - gdb_handlesig(cpu, 0, NULL, NULL, 0); } cpu_loop(env); /* never exits */ diff --git a/gdbstub/user.c b/gdbstub/user.c index 8225b70280..3730f32c41 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -22,6 +22,7 @@ #include "gdbstub/user.h" #include "gdbstub/enums.h" #include "hw/core/cpu.h" +#include "user/signal.h" #include "trace.h" #include "internals.h" @@ -393,32 +394,122 @@ static int gdbserver_open_port(int port, Error **errp) return fd; } -bool gdbserver_start(const char *port_or_path, Error **errp) +static bool gdbserver_accept(int port, int gdb_fd, const char *path) { - int port = g_ascii_strtoull(port_or_path, NULL, 10); - int gdb_fd; + bool ret; + if (port > 0) { + ret = gdb_accept_tcp(gdb_fd); + } else { + ret = gdb_accept_socket(gdb_fd); + if (ret) { + gdbserver_user_state.socket_path = g_strdup(path); + } + } + + if (!ret) { + close(gdb_fd); + } + + return ret; +} + +struct { + int port; + int gdb_fd; + char *path; +} gdbserver_args; + +static void do_gdb_handlesig(CPUState *cs, run_on_cpu_data arg) +{ + int sig; + + sig = target_to_host_signal(gdb_handlesig(cs, 0, NULL, NULL, 0)); + if (sig >= 1 && sig < NSIG) { + qemu_kill_thread(gdb_get_cpu_index(cs), sig); + } +} + +static void *gdbserver_accept_thread(void *arg) +{ + if (gdbserver_accept(gdbserver_args.port, gdbserver_args.gdb_fd, + gdbserver_args.path)) { + CPUState *cs = first_cpu; + + async_safe_run_on_cpu(cs, do_gdb_handlesig, RUN_ON_CPU_NULL); + qemu_kill_thread(gdb_get_cpu_index(cs), host_interrupt_signal); + } + + g_free(gdbserver_args.path); + gdbserver_args.path = NULL; + + return NULL; +} + +#define USAGE "\nUsage: -g {port|path}[,suspend={y|n}]" + +bool gdbserver_start(const char *args, Error **errp) +{ + g_auto(GStrv) argv = g_strsplit(args, ",", 0); + const char *port_or_path = NULL; + bool suspend = true; + int gdb_fd, port; + GStrv arg; + + for (arg = argv; *arg; arg++) { + g_auto(GStrv) tokens = g_strsplit(*arg, "=", 2); + + if (g_strcmp0(tokens[0], "suspend") == 0) { + if (tokens[1] == NULL) { + error_setg(errp, + "gdbstub: missing \"suspend\" option value" USAGE); + return false; + } else if (!qapi_bool_parse(tokens[0], tokens[1], + &suspend, errp)) { + return false; + } + } else { + if (port_or_path) { + error_setg(errp, "gdbstub: unknown option \"%s\"" USAGE, *arg); + return false; + } + port_or_path = *arg; + } + } + if (!port_or_path) { + error_setg(errp, "gdbstub: port or path not specified" USAGE); + return false; + } + + port = g_ascii_strtoull(port_or_path, NULL, 10); if (port > 0) { gdb_fd = gdbserver_open_port(port, errp); } else { gdb_fd = gdbserver_open_socket(port_or_path, errp); } - if (gdb_fd < 0) { return false; } - if (port > 0 && gdb_accept_tcp(gdb_fd)) { - return true; - } else if (gdb_accept_socket(gdb_fd)) { - gdbserver_user_state.socket_path = g_strdup(port_or_path); + if (suspend) { + if (gdbserver_accept(port, gdb_fd, port_or_path)) { + gdb_handlesig(first_cpu, 0, NULL, NULL, 0); + return true; + } else { + error_setg(errp, "gdbstub: failed to accept connection"); + return false; + } + } else { + QemuThread thread; + + gdbserver_args.port = port; + gdbserver_args.gdb_fd = gdb_fd; + gdbserver_args.path = g_strdup(port_or_path); + qemu_thread_create(&thread, "gdb-accept", + &gdbserver_accept_thread, NULL, + QEMU_THREAD_DETACHED); return true; } - - /* gone wrong */ - close(gdb_fd); - error_setg(errp, "gdbstub: failed to accept connection"); - return false; } void gdbserver_fork_start(void) diff --git a/linux-user/main.c b/linux-user/main.c index 7198fa0986..5c74c52cc5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1024,7 +1024,6 @@ int main(int argc, char **argv, char **envp) if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); - gdb_handlesig(cpu, 0, NULL, NULL, 0); } #ifdef CONFIG_SEMIHOSTING From 628d64222e6bef249d23ce3147cbfb47259f2ede Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:11 +0000 Subject: [PATCH 1558/2892] docs/user: Document the %d placeholder and suspend=n QEMU_GDB features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-8-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-17-alex.bennee@linaro.org> --- docs/user/main.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index 80a77f0a0c..9a1c60448c 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -54,7 +54,7 @@ Command line options :: - qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...] + qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g endpoint] [-B offset] [-R size] program [arguments...] ``-h`` Print the help @@ -91,8 +91,18 @@ Debug options: Activate logging of the specified items (use '-d help' for a list of log items) -``-g port`` - Wait gdb connection to port +``-g endpoint`` + Wait gdb connection to a port (e.g., ``1234``) or a unix socket (e.g., + ``/tmp/qemu.sock``). + + If a unix socket path contains single ``%d`` placeholder (e.g., + ``/tmp/qemu-%d.sock``), it is replaced by the emulator PID, which is useful + when passing this option via the ``QEMU_GDB`` environment variable to a + multi-process application. + + If the endpoint address is followed by ``,suspend=n`` (e.g., + ``1234,suspend=n``), then the emulated program starts without waiting for a + connection, which can be established at any later point in time. ``-one-insn-per-tb`` Run the emulation with one guest instruction per translation block. From 24c61663dcec0e87bb4206a7623f0e222e188b47 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 7 Feb 2025 15:31:12 +0000 Subject: [PATCH 1559/2892] tests/tcg: Add late gdbstub attach test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a small test to prevent regressions. Make sure that host_interrupt_signal is not visible to the guest. Reviewed-by: Alex Bennée Signed-off-by: Ilya Leoshkevich Message-Id: <20250117001542.8290-9-iii@linux.ibm.com> Signed-off-by: Alex Bennée Message-Id: <20250207153112.3939799-18-alex.bennee@linaro.org> --- tests/guest-debug/run-test.py | 15 ++++++-- tests/tcg/multiarch/Makefile.target | 9 ++++- tests/tcg/multiarch/gdbstub/late-attach.py | 28 +++++++++++++++ tests/tcg/multiarch/late-attach.c | 41 ++++++++++++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/multiarch/gdbstub/late-attach.py create mode 100644 tests/tcg/multiarch/late-attach.c diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index 5a091db8be..75e9c92e03 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -36,6 +36,8 @@ def get_args(): parser.add_argument("--gdb-args", help="Additional gdb arguments") parser.add_argument("--output", help="A file to redirect output to") parser.add_argument("--stderr", help="A file to redirect stderr to") + parser.add_argument("--no-suspend", action="store_true", + help="Ask the binary to not wait for GDB connection") return parser.parse_args() @@ -73,10 +75,19 @@ if __name__ == '__main__': # Launch QEMU with binary if "system" in args.qemu: + if args.no_suspend: + suspend = '' + else: + suspend = ' -S' cmd = f'{args.qemu} {args.qargs} {args.binary}' \ - f' -S -gdb unix:path={socket_name},server=on' + f'{suspend} -gdb unix:path={socket_name},server=on' else: - cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}' + if args.no_suspend: + suspend = ',suspend=n' + else: + suspend = '' + cmd = f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \ + f' {args.binary}' log(output, "QEMU CMD: %s" % (cmd)) inferior = subprocess.Popen(shlex.split(cmd)) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 18d3cf4ae0..688a6be203 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -130,6 +130,13 @@ run-gdbstub-follow-fork-mode-parent: follow-fork-mode --bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \ following parents on fork) +run-gdbstub-late-attach: late-attach + $(call run-test, $@, env LATE_ATTACH_PY=1 $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" --no-suspend \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/late-attach.py, \ + attaching to a running process) + else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") @@ -139,7 +146,7 @@ EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ run-gdbstub-registers run-gdbstub-prot-none \ run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \ run-gdbstub-follow-fork-mode-parent \ - run-gdbstub-qxfer-siginfo-read + run-gdbstub-qxfer-siginfo-read run-gdbstub-late-attach # ARM Compatible Semi Hosting Tests # diff --git a/tests/tcg/multiarch/gdbstub/late-attach.py b/tests/tcg/multiarch/gdbstub/late-attach.py new file mode 100644 index 0000000000..1d40efb5ec --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/late-attach.py @@ -0,0 +1,28 @@ +"""Test attaching GDB to a running process. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + try: + phase = gdb.parse_and_eval("phase").string() + except gdb.error: + # Assume the guest did not reach main(). + phase = "start" + + if phase == "start": + gdb.execute("break sigwait") + gdb.execute("continue") + phase = gdb.parse_and_eval("phase").string() + report(phase == "sigwait", "{} == \"sigwait\"".format(phase)) + + gdb.execute("signal SIGUSR1") + + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/late-attach.c b/tests/tcg/multiarch/late-attach.c new file mode 100644 index 0000000000..20a364034b --- /dev/null +++ b/tests/tcg/multiarch/late-attach.c @@ -0,0 +1,41 @@ +/* + * Test attaching GDB to a running process. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +static const char *phase = "start"; + +int main(void) +{ + sigset_t set; + int sig; + + assert(sigfillset(&set) == 0); + assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0); + + /* Let GDB know it can send SIGUSR1. */ + phase = "sigwait"; + if (getenv("LATE_ATTACH_PY")) { + assert(sigwait(&set, &sig) == 0); + if (sig != SIGUSR1) { + fprintf(stderr, "Unexpected signal %d\n", sig); + return EXIT_FAILURE; + } + } + + /* Check that the guest does not see host_interrupt_signal. */ + assert(sigpending(&set) == 0); + for (sig = 1; sig < NSIG; sig++) { + if (sigismember(&set, sig)) { + fprintf(stderr, "Unexpected signal %d\n", sig); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} From 407bc4bf9027f7ac4333e47cd900d773b99a23e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 18 Nov 2024 16:12:34 +0100 Subject: [PATCH 1560/2892] qapi: Move include/qapi/qmp/ to include/qobject/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The general expectation is that header files should follow the same file/path naming scheme as the corresponding source file. There are various historical exceptions to this practice in QEMU, with one of the most notable being the include/qapi/qmp/ directory. Most of the headers there correspond to source files in qobject/. This patch corrects most of that inconsistency by creating include/qobject/ and moving the headers for qobject/ there. This also fixes MAINTAINERS for include/qapi/qmp/dispatch.h: scripts/get_maintainer.pl now reports "QAPI" instead of "No maintainers found". Signed-off-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Acked-by: Halil Pasic #s390x Signed-off-by: Markus Armbruster Message-ID: <20241118151235.2665921-2-armbru@redhat.com> [Rebased] --- MAINTAINERS | 5 +- audio/audio-hmp-cmds.c | 2 +- audio/audio.c | 2 +- authz/listfile.c | 4 +- backends/cryptodev-hmp-cmds.c | 2 +- block.c | 8 +- block/blkdebug.c | 6 +- block/blkio.c | 2 +- block/blklogwrites.c | 4 +- block/blkverify.c | 4 +- block/copy-before-write.c | 2 +- block/copy-on-read.c | 2 +- block/curl.c | 4 +- block/file-posix.c | 4 +- block/file-win32.c | 4 +- block/gluster.c | 2 +- block/iscsi.c | 4 +- block/monitor/block-hmp-cmds.c | 2 +- block/nbd.c | 2 +- block/nfs.c | 4 +- block/null.c | 4 +- block/nvme.c | 4 +- block/parallels.c | 2 +- block/qapi-system.c | 2 +- block/qapi.c | 10 +- block/qcow.c | 4 +- block/qcow2.c | 4 +- block/qed.c | 2 +- block/quorum.c | 6 +- block/rbd.c | 8 +- block/replication.c | 2 +- block/snapshot.c | 4 +- block/ssh.c | 4 +- block/stream.c | 2 +- block/vhdx.c | 2 +- block/vmdk.c | 2 +- block/vpc.c | 2 +- block/vvfat.c | 4 +- blockdev.c | 8 +- chardev/char-hmp-cmds.c | 2 +- docs/devel/qapi-code-gen.rst | 2 +- dump/dump-hmp-cmds.c | 2 +- hw/arm/aspeed_ast27x0.c | 2 +- hw/arm/mps2-tz.c | 2 +- hw/arm/mps2.c | 2 +- hw/arm/mps3r.c | 2 +- hw/arm/sbsa-ref.c | 2 +- hw/arm/stellaris.c | 2 +- hw/arm/vexpress.c | 2 +- hw/arm/virt.c | 2 +- hw/arm/xlnx-versal.c | 2 +- hw/block/xen-block.c | 4 +- hw/core/machine-hmp-cmds.c | 2 +- hw/core/machine-qmp-cmds.c | 2 +- hw/core/qdev-properties.c | 2 +- hw/core/qdev.c | 2 +- hw/hyperv/hv-balloon.c | 2 +- hw/i386/acpi-build.c | 2 +- hw/i386/kvm/xen_evtchn.c | 2 +- hw/i386/monitor.c | 2 +- hw/i386/pc.c | 2 +- hw/net/rocker/rocker-hmp-cmds.c | 2 +- hw/net/virtio-net.c | 2 +- hw/net/xen_nic.c | 2 +- hw/pci/pci-hmp-cmds.c | 2 +- hw/ppc/pegasos2.c | 2 +- hw/ppc/spapr_drc.c | 2 +- hw/rx/rx62n.c | 2 +- hw/s390x/s390-skeys.c | 2 +- hw/s390x/s390-stattrib.c | 2 +- hw/usb/xen-usb.c | 4 +- hw/vfio/pci.c | 2 +- hw/virtio/virtio-hmp-cmds.c | 2 +- hw/virtio/virtio-qmp.c | 4 +- hw/xen/xen-bus.c | 2 +- include/block/qdict.h | 2 +- include/{qapi/qmp => qobject}/json-parser.h | 0 include/{qapi/qmp => qobject}/json-writer.h | 0 include/{qapi/qmp => qobject}/qbool.h | 2 +- include/{qapi/qmp => qobject}/qdict.h | 2 +- include/{qapi/qmp => qobject}/qjson.h | 0 include/{qapi/qmp => qobject}/qlist.h | 2 +- include/{qapi/qmp => qobject}/qlit.h | 0 include/{qapi/qmp => qobject}/qnull.h | 2 +- include/{qapi/qmp => qobject}/qnum.h | 2 +- include/{qapi/qmp => qobject}/qobject.h | 2 +- include/{qapi/qmp => qobject}/qstring.h | 2 +- migration/dirtyrate.c | 2 +- migration/migration-hmp-cmds.c | 2 +- migration/migration.c | 2 +- migration/migration.h | 2 +- migration/options.c | 2 +- migration/vmstate.c | 2 +- monitor/hmp-cmds-target.c | 2 +- monitor/hmp-cmds.c | 2 +- monitor/hmp.c | 4 +- monitor/monitor-internal.h | 2 +- monitor/monitor.c | 2 +- monitor/qemu-config-qmp.c | 2 +- monitor/qmp.c | 6 +- net/net-hmp-cmds.c | 2 +- net/net.c | 2 +- net/slirp.c | 2 +- qapi/qapi-clone-visitor.c | 2 +- qapi/qapi-dealloc-visitor.c | 2 +- qapi/qapi-forward-visitor.c | 14 +- qapi/qmp-dispatch.c | 6 +- qapi/qmp-event.c | 6 +- qapi/qobject-input-visitor.c | 14 +- qapi/qobject-output-visitor.c | 12 +- qapi/string-input-visitor.c | 2 +- qemu-img.c | 4 +- qemu-io-cmds.c | 2 +- qemu-io.c | 4 +- qemu-nbd.c | 4 +- qga/main.c | 6 +- qobject/block-qdict.c | 8 +- qobject/json-parser-int.h | 2 +- qobject/json-parser.c | 12 +- qobject/json-writer.c | 2 +- qobject/qbool.c | 2 +- qobject/qdict.c | 10 +- qobject/qjson.c | 16 +- qobject/qlist.c | 10 +- qobject/qlit.c | 14 +- qobject/qnull.c | 2 +- qobject/qnum.c | 2 +- qobject/qobject-internal.h | 2 +- qobject/qobject.c | 12 +- qobject/qstring.c | 2 +- qom/object.c | 10 +- qom/object_interfaces.c | 6 +- qom/qom-hmp-cmds.c | 4 +- qom/qom-qmp-cmds.c | 2 +- replay/replay-debugging.c | 2 +- replay/replay-snapshot.c | 2 +- scripts/qapi/commands.py | 4 +- scripts/qapi/events.py | 2 +- scripts/qapi/introspect.py | 2 +- scsi/qemu-pr-helper.c | 2 +- stats/stats-hmp-cmds.c | 2 +- storage-daemon/qemu-storage-daemon.c | 4 +- system/device_tree.c | 2 +- system/dirtylimit.c | 2 +- system/qdev-monitor.c | 4 +- system/runstate-hmp-cmds.c | 2 +- system/vl.c | 6 +- target/arm/arm-qmp-cmds.c | 2 +- target/i386/cpu-apic.c | 2 +- target/i386/cpu-system.c | 2 +- target/i386/monitor.c | 2 +- target/loongarch/loongarch-qmp-cmds.c | 2 +- target/ppc/cpu_init.c | 2 +- target/riscv/riscv-qmp-cmds.c | 4 +- target/s390x/cpu_models_system.c | 2 +- tests/qtest/adm1266-test.c | 4 +- tests/qtest/adm1272-test.c | 4 +- tests/qtest/ahci-test.c | 2 +- tests/qtest/arm-cpu-features.c | 4 +- tests/qtest/aspeed_gpio-test.c | 2 +- tests/qtest/ast2700-gpio-test.c | 2 +- tests/qtest/boot-order-test.c | 2 +- tests/qtest/cdrom-test.c | 2 +- tests/qtest/cpu-plug-test.c | 4 +- tests/qtest/device-introspect-test.c | 6 +- tests/qtest/device-plug-test.c | 4 +- tests/qtest/drive_del-test.c | 4 +- tests/qtest/emc141x-test.c | 2 +- tests/qtest/fdc-test.c | 2 +- tests/qtest/hd-geo-test.c | 2 +- tests/qtest/ide-test.c | 2 +- tests/qtest/isl_pmbus_vr-test.c | 4 +- tests/qtest/libqmp.c | 4 +- tests/qtest/libqmp.h | 2 +- tests/qtest/libqos/generic-pcihost.c | 2 +- tests/qtest/libqos/libqos.c | 2 +- tests/qtest/libqos/pci-pc.c | 2 +- tests/qtest/libqos/qos_external.c | 8 +- tests/qtest/libqtest.c | 10 +- tests/qtest/libqtest.h | 6 +- tests/qtest/lsm303dlhc-mag-test.c | 2 +- tests/qtest/machine-none-test.c | 2 +- tests/qtest/max34451-test.c | 4 +- tests/qtest/migration-helpers.c | 530 ++++++++++++++++++++ tests/qtest/migration/file-tests.c | 2 +- tests/qtest/migration/framework.c | 4 +- tests/qtest/migration/migration-qmp.c | 6 +- tests/qtest/migration/migration-util.c | 2 +- tests/qtest/migration/misc-tests.c | 2 +- tests/qtest/migration/postcopy-tests.c | 2 +- tests/qtest/migration/precopy-tests.c | 2 +- tests/qtest/netdev-socket.c | 2 +- tests/qtest/npcm7xx_adc-test.c | 2 +- tests/qtest/npcm7xx_emc-test.c | 4 +- tests/qtest/npcm7xx_pwm-test.c | 4 +- tests/qtest/npcm7xx_watchdog_timer-test.c | 2 +- tests/qtest/numa-test.c | 4 +- tests/qtest/pvpanic-pci-test.c | 2 +- tests/qtest/pvpanic-test.c | 2 +- tests/qtest/q35-test.c | 2 +- tests/qtest/qmp-cmd-test.c | 2 +- tests/qtest/qmp-test.c | 6 +- tests/qtest/qom-test.c | 4 +- tests/qtest/qos-test.c | 2 +- tests/qtest/readconfig-test.c | 6 +- tests/qtest/tco-test.c | 2 +- tests/qtest/test-filter-mirror.c | 2 +- tests/qtest/test-filter-redirector.c | 2 +- tests/qtest/test-netfilter.c | 2 +- tests/qtest/test-x86-cpuid-compat.c | 8 +- tests/qtest/tmp105-test.c | 2 +- tests/qtest/tpm-emu.c | 4 +- tests/qtest/tpm-util.c | 2 +- tests/qtest/vhost-user-test.c | 2 +- tests/qtest/virtio-net-failover.c | 6 +- tests/qtest/virtio-net-test.c | 2 +- tests/qtest/vmgenid-test.c | 2 +- tests/qtest/wdt_ib700-test.c | 2 +- tests/unit/check-block-qdict.c | 4 +- tests/unit/check-qdict.c | 6 +- tests/unit/check-qjson.c | 12 +- tests/unit/check-qlist.c | 4 +- tests/unit/check-qlit.c | 12 +- tests/unit/check-qnull.c | 2 +- tests/unit/check-qnum.c | 2 +- tests/unit/check-qobject.c | 12 +- tests/unit/check-qom-proplist.c | 4 +- tests/unit/check-qstring.c | 2 +- tests/unit/test-block-iothread.c | 2 +- tests/unit/test-blockjob-txn.c | 2 +- tests/unit/test-blockjob.c | 2 +- tests/unit/test-char.c | 2 +- tests/unit/test-forward-visitor.c | 4 +- tests/unit/test-image-locking.c | 2 +- tests/unit/test-keyval.c | 6 +- tests/unit/test-qemu-opts.c | 4 +- tests/unit/test-qga.c | 4 +- tests/unit/test-qmp-cmds.c | 8 +- tests/unit/test-qmp-event.c | 10 +- tests/unit/test-qobject-input-visitor.c | 12 +- tests/unit/test-qobject-output-visitor.c | 12 +- tests/unit/test-replication.c | 2 +- tests/unit/test-visitor-serialization.c | 4 +- trace/trace-hmp-cmds.c | 2 +- ui/ui-hmp-cmds.c | 2 +- util/keyval.c | 6 +- util/qemu-config.c | 4 +- util/qemu-option.c | 8 +- 248 files changed, 970 insertions(+), 443 deletions(-) rename include/{qapi/qmp => qobject}/json-parser.h (100%) rename include/{qapi/qmp => qobject}/json-writer.h (100%) rename include/{qapi/qmp => qobject}/qbool.h (94%) rename include/{qapi/qmp => qobject}/qdict.h (98%) rename include/{qapi/qmp => qobject}/qjson.h (100%) rename include/{qapi/qmp => qobject}/qlist.h (98%) rename include/{qapi/qmp => qobject}/qlit.h (100%) rename include/{qapi/qmp => qobject}/qnull.h (94%) rename include/{qapi/qmp => qobject}/qnum.h (98%) rename include/{qapi/qmp => qobject}/qobject.h (98%) rename include/{qapi/qmp => qobject}/qstring.h (96%) create mode 100644 tests/qtest/migration-helpers.c diff --git a/MAINTAINERS b/MAINTAINERS index 0091bd1a90..fefabe0cee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3235,8 +3235,6 @@ S: Supported F: qapi/ X: qapi/*.json F: include/qapi/ -X: include/qapi/qmp/ -F: include/qapi/qmp/dispatch.h F: tests/qapi-schema/ F: tests/unit/test-*-visitor.c F: tests/unit/test-qapi-*.c @@ -3260,8 +3258,7 @@ QObject M: Markus Armbruster S: Supported F: qobject/ -F: include/qapi/qmp/ -X: include/qapi/qmp/dispatch.h +F: include/qobject/ F: scripts/coccinelle/qobject.cocci F: tests/unit/check-qdict.c F: tests/unit/check-qjson.c diff --git a/audio/audio-hmp-cmds.c b/audio/audio-hmp-cmds.c index c9608b715b..8774c09f18 100644 --- a/audio/audio-hmp-cmds.c +++ b/audio/audio-hmp-cmds.c @@ -27,7 +27,7 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" static QLIST_HEAD (capture_list_head, CaptureState) capture_head; diff --git a/audio/audio.c b/audio/audio.c index 87b4e9b6f2..41ee11aaad 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -32,7 +32,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-audio.h" #include "qapi/qapi-commands-audio.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" diff --git a/authz/listfile.c b/authz/listfile.c index 45a60e987d..d31d9103f7 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -28,8 +28,8 @@ #include "qemu/filemonitor.h" #include "qom/object_interfaces.h" #include "qapi/qapi-visit-authz.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qjson.h" +#include "qobject/qobject.h" #include "qapi/qobject-input-visitor.h" diff --git a/backends/cryptodev-hmp-cmds.c b/backends/cryptodev-hmp-cmds.c index 4f7220bb13..01396d227c 100644 --- a/backends/cryptodev-hmp-cmds.c +++ b/backends/cryptodev-hmp-cmds.c @@ -14,7 +14,7 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-cryptodev.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void hmp_info_cryptodev(Monitor *mon, const QDict *qdict) diff --git a/block.c b/block.c index f60606f242..b4459dee2e 100644 --- a/block.c +++ b/block.c @@ -36,10 +36,10 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnull.h" +#include "qobject/qstring.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "system/block-backend.h" diff --git a/block/blkdebug.c b/block/blkdebug.c index f500824608..1c1967f8e0 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -33,9 +33,9 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "system/qtest.h" diff --git a/block/blkio.c b/block/blkio.c index 003cb63832..5f4fce2b1b 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -16,7 +16,7 @@ #include "qemu/defer-call.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/module.h" #include "system/block-backend.h" #include "exec/memory.h" /* for ram_block_discard_disable() */ diff --git a/block/blklogwrites.c b/block/blklogwrites.c index ed38a93f21..b0f78c4bc7 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -14,8 +14,8 @@ #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ #include "block/block-io.h" #include "block/block_int.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/blkverify.c b/block/blkverify.c index 5a9bf674d9..db79a36681 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -12,8 +12,8 @@ #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ #include "block/block-io.h" #include "block/block_int.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/copy-before-write.c b/block/copy-before-write.c index c00bc2351b..fd470f5f92 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" #include "system/block-backend.h" #include "qemu/cutils.h" diff --git a/block/copy-on-read.c b/block/copy-on-read.c index c36f253d16..accf1402f0 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -25,7 +25,7 @@ #include "block/block_int.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "block/copy-on-read.h" diff --git a/block/curl.c b/block/curl.c index 0fdb6d39ac..5467678024 100644 --- a/block/curl.c +++ b/block/curl.c @@ -29,8 +29,8 @@ #include "qemu/option.h" #include "block/block-io.h" #include "block/block_int.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "crypto/secret.h" #include #include "qemu/cutils.h" diff --git a/block/file-posix.c b/block/file-posix.c index 90fa54352c..44e16dda87 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -36,8 +36,8 @@ #include "block/thread-pool.h" #include "qemu/iov.h" #include "block/raw-aio.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "scsi/pr-manager.h" #include "scsi/constants.h" diff --git a/block/file-win32.c b/block/file-win32.c index 7e1baa1ece..af9aea631c 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -33,8 +33,8 @@ #include "trace.h" #include "block/thread-pool.h" #include "qemu/iov.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include #include diff --git a/block/gluster.c b/block/gluster.c index e9c038042b..c6d25ae733 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -15,7 +15,7 @@ #include "block/block_int.h" #include "block/qdict.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/module.h" diff --git a/block/iscsi.c b/block/iscsi.c index a5f8921426..2f0f4dac09 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -44,8 +44,8 @@ #include "system/replay.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "crypto/secret.h" #include "scsi/utils.h" #include "trace.h" diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 1d312513fc..3f183b6b8e 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -41,7 +41,7 @@ #include "system/blockdev.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-block-export.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/config-file.h" diff --git a/block/nbd.c b/block/nbd.c index d464315766..887841bc81 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -36,7 +36,7 @@ #include "qemu/main-loop.h" #include "qapi/qapi-visit-sockets.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qapi/clone-visitor.h" #include "block/qdict.h" diff --git a/block/nfs.c b/block/nfs.c index 7d34b58750..0a7d38db09 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -41,8 +41,8 @@ #include "qemu/cutils.h" #include "system/replay.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include diff --git a/block/null.c b/block/null.c index 8135055834..dc0b1fdbd9 100644 --- a/block/null.c +++ b/block/null.c @@ -12,8 +12,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/module.h" #include "qemu/option.h" #include "block/block-io.h" diff --git a/block/nvme.c b/block/nvme.c index 5ba6a0c9c9..bbf7c23dcd 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -14,8 +14,8 @@ #include "qemu/osdep.h" #include #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/block/parallels.c b/block/parallels.c index d4bfc44e64..347ca127f3 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -36,7 +36,7 @@ #include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "qemu/bswap.h" diff --git a/block/qapi-system.c b/block/qapi-system.c index 3277f37fd0..54b7409b2b 100644 --- a/block/qapi-system.c +++ b/block/qapi-system.c @@ -35,7 +35,7 @@ #include "block/block_int.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "system/block-backend.h" #include "system/blockdev.h" diff --git a/block/qapi.c b/block/qapi.c index 902ecb08e0..bc04b14ad2 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -33,11 +33,11 @@ #include "qapi/qapi-commands-block-core.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/qemu-print.h" #include "system/block-backend.h" diff --git a/block/qcow.c b/block/qcow.c index 37be7e7cb4..da8ad4d243 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -34,8 +34,8 @@ #include "qemu/cutils.h" #include "qemu/memalign.h" #include -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "crypto/block.h" diff --git a/block/qcow2.c b/block/qcow2.c index d732162391..dd6bcafbd8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -32,8 +32,8 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "trace.h" #include "qemu/option_int.h" #include "qemu/cutils.h" diff --git a/block/qed.c b/block/qed.c index 8b33594546..382c9e5335 100644 --- a/block/qed.c +++ b/block/qed.c @@ -24,7 +24,7 @@ #include "trace.h" #include "qed.h" #include "system/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" diff --git a/block/quorum.c b/block/quorum.c index 46be65a95f..30747a6df9 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -23,10 +23,10 @@ #include "block/qdict.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "crypto/hash.h" #define HASH_LENGTH 32 diff --git a/block/rbd.c b/block/rbd.c index e814856609..af984fb7db 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -24,10 +24,10 @@ #include "crypto/secret.h" #include "qemu/cutils.h" #include "system/replay.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" diff --git a/block/replication.c b/block/replication.c index 2ce16f0589..adac1cf207 100644 --- a/block/replication.c +++ b/block/replication.c @@ -21,7 +21,7 @@ #include "block/block_backup.h" #include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "block/replication.h" typedef enum { diff --git a/block/snapshot.c b/block/snapshot.c index d27afe7c0e..9c44780e96 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -27,8 +27,8 @@ #include "block/block_int.h" #include "block/qdict.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/option.h" #include "system/block-backend.h" diff --git a/block/ssh.c b/block/ssh.c index b9f33ec739..70fe7cf86e 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -39,8 +39,8 @@ #include "qemu/sockets.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "trace.h" diff --git a/block/stream.c b/block/stream.c index 9a06c0decb..999d9e56d4 100644 --- a/block/stream.c +++ b/block/stream.c @@ -16,7 +16,7 @@ #include "block/block_int.h" #include "block/blockjob_int.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/ratelimit.h" #include "system/block-backend.h" #include "block/copy-on-read.h" diff --git a/block/vhdx.c b/block/vhdx.c index 42c919f51a..b2a4b813a0 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -29,7 +29,7 @@ #include "vhdx.h" #include "migration/blocker.h" #include "qemu/uuid.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" diff --git a/block/vmdk.c b/block/vmdk.c index 6ef266df87..2adec49912 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "block/block_int.h" #include "system/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/vpc.c b/block/vpc.c index 6489ee756a..9110d87746 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -34,7 +34,7 @@ #include "qemu/bswap.h" #include "qemu/uuid.h" #include "qemu/memalign.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" diff --git a/block/vvfat.c b/block/vvfat.c index bfbcc5562c..91d69b3cc8 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -34,8 +34,8 @@ #include "qemu/option.h" #include "qemu/bswap.h" #include "migration/blocker.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" diff --git a/blockdev.c b/blockdev.c index 218024497b..7acca5ac48 100644 --- a/blockdev.c +++ b/blockdev.c @@ -46,12 +46,12 @@ #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-transaction.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qapi/qobject-output-visitor.h" #include "system/system.h" #include "system/iothread.h" diff --git a/chardev/char-hmp-cmds.c b/chardev/char-hmp-cmds.c index 287c2b1bcd..8e9e1c1c02 100644 --- a/chardev/char-hmp-cmds.c +++ b/chardev/char-hmp-cmds.c @@ -19,7 +19,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/option.h" diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 3e26d2d104..259d6b10c3 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1986,7 +1986,7 @@ Example:: #ifndef EXAMPLE_QAPI_INTROSPECT_H #define EXAMPLE_QAPI_INTROSPECT_H - #include "qapi/qmp/qlit.h" + #include "qobject/qlit.h" extern const QLitObject example_qmp_schema_qlit; diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c index d9340427c3..21023db6fd 100644 --- a/dump/dump-hmp-cmds.c +++ b/dump/dump-hmp-cmds.c @@ -10,7 +10,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-dump.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) { diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 4114e15ddd..2d0c99f159 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -20,7 +20,7 @@ #include "net/net.h" #include "system/system.h" #include "hw/intc/arm_gicv3.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/log.h" static const hwaddr aspeed_soc_ast2700_memmap[] = { diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index d3a9f1b03a..13ed868b6b 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -48,7 +48,7 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/error-report.h" #include "hw/arm/boot.h" #include "hw/arm/armv7m.h" diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 56b2af40f1..3f8db0cab6 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -48,7 +48,7 @@ #include "net/net.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/qdev-clock.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qom/object.h" typedef enum MPS2FPGAType { diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index 2b104671db..1bddb5e822 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "exec/address-spaces.h" #include "cpu.h" #include "system/system.h" diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 6183111f2d..e720de3064 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -48,7 +48,7 @@ #include "hw/char/pl011.h" #include "hw/watchdog/sbsa_gwdt.h" #include "net/net.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qom/object.h" #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index c3c3fd0410..47c1cfa048 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -32,7 +32,7 @@ #include "hw/timer/stellaris-gptm.h" #include "hw/qdev-clock.h" #include "qom/object.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "ui/input.h" #define GPIO_A 0 diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 42c6703406..b886d16c02 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -42,7 +42,7 @@ #include "hw/cpu/a15mpcore.h" #include "hw/i2c/arm_sbcon_i2c.h" #include "hw/sd/sd.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qom/object.h" #include "audio/audio.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 99e0a68b6c..3448200e3b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -70,7 +70,7 @@ #include "hw/firmware/smbios.h" #include "qapi/visitor.h" #include "qapi/qapi-visit-common.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index cccdd99fcc..278545a3f7 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "net/net.h" diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 034a18b70e..6c26052561 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -16,8 +16,8 @@ #include "qapi/qapi-visit-block-core.h" #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qom/object_interfaces.h" #include "hw/block/xen_blkif.h" #include "hw/qdev-properties.h" diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 916727961c..c6325cdcaa 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -19,7 +19,7 @@ #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/string-output-visitor.h" #include "qemu/error-report.h" #include "system/numa.h" diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 4eefe6ba86..3130c5cd45 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -15,7 +15,7 @@ #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/uuid.h" diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 434a76f503..0b52aad555 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -2,7 +2,7 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qapi/qapi-types-misc.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/visitor.h" diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 82bbdcb654..2745b5e092 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-events-qdev.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qemu/option.h" diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index e21fc675a6..6f33c3e741 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -26,7 +26,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-events-machine.h" #include "qapi/qapi-types-machine.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qemu/module.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 53b7306b43..3fffa4a332 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" #include "acpi-build.h" #include "acpi-common.h" #include "qemu/bitmap.h" diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 58484f308e..9b8b092bc2 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -20,7 +20,7 @@ #include "monitor/hmp.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qom/object.h" #include "exec/target_page.h" #include "exec/address-spaces.h" diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c index 1ebd3564bf..1921e4d52e 100644 --- a/hw/i386/monitor.c +++ b/hw/i386/monitor.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" #include "hw/i386/x86.h" diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b46975c8a4..0eb52d315b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -44,7 +44,7 @@ #include "system/reset.h" #include "kvm/kvm_i386.h" #include "hw/xen/xen.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/error-report.h" #include "hw/acpi/cpu_hotplug.h" #include "acpi-build.h" diff --git a/hw/net/rocker/rocker-hmp-cmds.c b/hw/net/rocker/rocker-hmp-cmds.c index 197c6e28dc..df40991f6d 100644 --- a/hw/net/rocker/rocker-hmp-cmds.c +++ b/hw/net/rocker/rocker-hmp-cmds.c @@ -18,7 +18,7 @@ #include "monitor/monitor.h" #include "net/eth.h" #include "qapi/qapi-commands-rocker.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void hmp_rocker(Monitor *mon, const QDict *qdict) { diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 85e14b788c..d847429b1c 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -26,7 +26,7 @@ #include "qemu/option.h" #include "qemu/option_int.h" #include "qemu/config-file.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" #include "net/announce.h" diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 5410039490..c48691207d 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -24,7 +24,7 @@ #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/qemu-print.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include diff --git a/hw/pci/pci-hmp-cmds.c b/hw/pci/pci-hmp-cmds.c index fdfe44435c..a5f6483cc3 100644 --- a/hw/pci/pci-hmp-cmds.c +++ b/hw/pci/pci-hmp-cmds.c @@ -20,7 +20,7 @@ #include "monitor/monitor.h" #include "pci-internal.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qapi-commands-pci.h" #include "qemu/cutils.h" diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 0364243f4f..b057672e82 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -33,7 +33,7 @@ #include "kvm_ppc.h" #include "exec/address-spaces.h" #include "qom/qom-qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "trace.h" #include "qemu/datadir.h" #include "system/device_tree.h" diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 2435397e94..549b652c20 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qemu/cutils.h" #include "hw/ppc/spapr_drc.h" #include "qom/object.h" diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index 8c1d1023ed..e6bac4f053 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -29,7 +29,7 @@ #include "hw/sysbus.h" #include "hw/qdev-properties.h" #include "system/system.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qom/object.h" /* diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 995817f4a3..811d892122 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -16,7 +16,7 @@ #include "hw/s390x/storage-keys.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "system/memory_mapping.h" #include "exec/address-spaces.h" diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 8e07acbddc..be07c28c6e 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -18,7 +18,7 @@ #include "qemu/error-report.h" #include "exec/ram_addr.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "cpu.h" /* 512KiB cover 2GB of guest memory */ diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 13901625c0..13b065b0fa 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -30,8 +30,8 @@ #include "hw/xen/xen-legacy-backend.h" #include "monitor/qdev.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "hw/xen/interface/io/usbif.h" diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index ab17a98ee5..9a55e7b773 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -30,7 +30,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c index 477c97dea2..7d8677bcf0 100644 --- a/hw/virtio/virtio-hmp-cmds.c +++ b/hw/virtio/virtio-hmp-cmds.c @@ -9,7 +9,7 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-virtio.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" static void hmp_virtio_dump_protocols(Monitor *mon, diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index 8a32a3b105..3b6377cf0d 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -15,8 +15,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-virtio.h" #include "qapi/qapi-commands-qom.h" -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qobject.h" +#include "qobject/qjson.h" #include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_ids.h" diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index feeb612681..8260f1e1bb 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -18,7 +18,7 @@ #include "hw/xen/xen-bus-helper.h" #include "monitor/monitor.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "system/system.h" #include "net/net.h" #include "trace.h" diff --git a/include/block/qdict.h b/include/block/qdict.h index b4c28d96a9..53c4df4cb2 100644 --- a/include/block/qdict.h +++ b/include/block/qdict.h @@ -10,7 +10,7 @@ #ifndef BLOCK_QDICT_H #define BLOCK_QDICT_H -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" QObject *qdict_crumple(const QDict *src, Error **errp); void qdict_flatten(QDict *qdict); diff --git a/include/qapi/qmp/json-parser.h b/include/qobject/json-parser.h similarity index 100% rename from include/qapi/qmp/json-parser.h rename to include/qobject/json-parser.h diff --git a/include/qapi/qmp/json-writer.h b/include/qobject/json-writer.h similarity index 100% rename from include/qapi/qmp/json-writer.h rename to include/qobject/json-writer.h diff --git a/include/qapi/qmp/qbool.h b/include/qobject/qbool.h similarity index 94% rename from include/qapi/qmp/qbool.h rename to include/qobject/qbool.h index 0d09726939..b348e17867 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qobject/qbool.h @@ -14,7 +14,7 @@ #ifndef QBOOL_H #define QBOOL_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" struct QBool { struct QObjectBase_ base; diff --git a/include/qapi/qmp/qdict.h b/include/qobject/qdict.h similarity index 98% rename from include/qapi/qmp/qdict.h rename to include/qobject/qdict.h index 82e90fc072..903e6e5462 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qobject/qdict.h @@ -13,7 +13,7 @@ #ifndef QDICT_H #define QDICT_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qemu/queue.h" #define QDICT_BUCKET_MAX 512 diff --git a/include/qapi/qmp/qjson.h b/include/qobject/qjson.h similarity index 100% rename from include/qapi/qmp/qjson.h rename to include/qobject/qjson.h diff --git a/include/qapi/qmp/qlist.h b/include/qobject/qlist.h similarity index 98% rename from include/qapi/qmp/qlist.h rename to include/qobject/qlist.h index e4e985d435..0377bf824e 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qobject/qlist.h @@ -13,7 +13,7 @@ #ifndef QLIST_H #define QLIST_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qemu/queue.h" typedef struct QListEntry { diff --git a/include/qapi/qmp/qlit.h b/include/qobject/qlit.h similarity index 100% rename from include/qapi/qmp/qlit.h rename to include/qobject/qlit.h diff --git a/include/qapi/qmp/qnull.h b/include/qobject/qnull.h similarity index 94% rename from include/qapi/qmp/qnull.h rename to include/qobject/qnull.h index 7feb7c7d83..4423836a0c 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qobject/qnull.h @@ -13,7 +13,7 @@ #ifndef QNULL_H #define QNULL_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" struct QNull { struct QObjectBase_ base; diff --git a/include/qapi/qmp/qnum.h b/include/qobject/qnum.h similarity index 98% rename from include/qapi/qmp/qnum.h rename to include/qobject/qnum.h index e86788dd2e..1ce24b3668 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qobject/qnum.h @@ -15,7 +15,7 @@ #ifndef QNUM_H #define QNUM_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" typedef enum { QNUM_I64, diff --git a/include/qapi/qmp/qobject.h b/include/qobject/qobject.h similarity index 98% rename from include/qapi/qmp/qobject.h rename to include/qobject/qobject.h index 256d782688..a6244d0ce0 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qobject/qobject.h @@ -34,7 +34,7 @@ #include "qapi/qapi-builtin-types.h" -/* Not for use outside include/qapi/qmp/ */ +/* Not for use outside include/qobject/ */ struct QObjectBase_ { QType type; size_t refcnt; diff --git a/include/qapi/qmp/qstring.h b/include/qobject/qstring.h similarity index 96% rename from include/qapi/qmp/qstring.h rename to include/qobject/qstring.h index 318d815d6a..1e2abe4032 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qobject/qstring.h @@ -13,7 +13,7 @@ #ifndef QSTRING_H #define QSTRING_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" struct QString { struct QObjectBase_ base; diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index 7c955894e4..4cd14779d6 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -24,7 +24,7 @@ #include "dirtyrate.h" #include "monitor/hmp.h" #include "monitor/monitor.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "system/kvm.h" #include "system/runstate.h" #include "exec/memory.h" diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index e8527bef80..3347e34c48 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-visit-migration.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" #include "qemu/cutils.h" diff --git a/migration/migration.c b/migration/migration.c index 74c50cc72c..0aa084f380 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -45,7 +45,7 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-events-migration.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qemu/rcu.h" #include "postcopy-ram.h" #include "qemu/thread.h" diff --git a/migration/migration.h b/migration/migration.h index 4c1fafc2b5..70f45af036 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -17,7 +17,7 @@ #include "exec/cpu-common.h" #include "hw/qdev-core.h" #include "qapi/qapi-types-migration.h" -#include "qapi/qmp/json-writer.h" +#include "qobject/json-writer.h" #include "qemu/thread.h" #include "qemu/coroutine.h" #include "io/channel.h" diff --git a/migration/options.c b/migration/options.c index 1ad950e397..4db340b502 100644 --- a/migration/options.c +++ b/migration/options.c @@ -19,7 +19,7 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "system/runstate.h" #include "migration/colo.h" #include "migration/cpr.h" diff --git a/migration/vmstate.c b/migration/vmstate.c index 047a52af89..5feaa3244d 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -15,7 +15,7 @@ #include "migration/vmstate.h" #include "savevm.h" #include "qapi/error.h" -#include "qapi/qmp/json-writer.h" +#include "qobject/json-writer.h" #include "qemu-file.h" #include "qemu/bitops.h" #include "qemu/error-report.h" diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 0300faa8a2..27ffe61818 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -29,7 +29,7 @@ #include "monitor/hmp-target.h" #include "monitor/monitor-internal.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "system/hw_accel.h" /* Set the current CPU defined by the user. Callers must hold BQL. */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 0aa22e1ae2..3825ff40a9 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -25,7 +25,7 @@ #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "system/system.h" diff --git a/monitor/hmp.c b/monitor/hmp.c index db90360553..34e2b8f748 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -27,8 +27,8 @@ #include "hw/qdev-core.h" #include "monitor-internal.h" #include "monitor/hmp.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/config-file.h" #include "qemu/ctype.h" #include "qemu/cutils.h" diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 088960a503..72cab703eb 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -29,7 +29,7 @@ #include "monitor/monitor.h" #include "qapi/qapi-types-control.h" #include "qapi/qmp/dispatch.h" -#include "qapi/qmp/json-parser.h" +#include "qobject/json-parser.h" #include "qemu/readline.h" #include "system/iothread.h" diff --git a/monitor/monitor.c b/monitor/monitor.c index 9fad61f9df..c5a5d30877 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -28,7 +28,7 @@ #include "qapi/opts-visitor.h" #include "qapi/qapi-emit-events.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "system/qtest.h" diff --git a/monitor/qemu-config-qmp.c b/monitor/qemu-config-qmp.c index 24477a0e44..9a3b183602 100644 --- a/monitor/qemu-config-qmp.c +++ b/monitor/qemu-config-qmp.c @@ -2,7 +2,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/option.h" #include "qemu/config-file.h" #include "hw/boards.h" diff --git a/monitor/qmp.c b/monitor/qmp.c index 5e538f34c0..2f46cf9e49 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -28,9 +28,9 @@ #include "monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" #include "trace.h" /* diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c index 41d326bf5f..e7c55d2787 100644 --- a/net/net-hmp-cmds.c +++ b/net/net-hmp-cmds.c @@ -22,7 +22,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-commands-net.h" #include "qapi/qapi-visit-net.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/help_option.h" #include "qemu/option.h" diff --git a/net/net.c b/net/net.c index 9cded70dde..a3996d5c62 100644 --- a/net/net.c +++ b/net/net.c @@ -36,7 +36,7 @@ #include "qemu/help_option.h" #include "qapi/qapi-commands-net.h" #include "qapi/qapi-visit-net.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/sockets.h" diff --git a/net/slirp.c b/net/slirp.c index 97d08ed1fb..9657e86a84 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -43,7 +43,7 @@ #include "system/system.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "util.h" #include "migration/register.h" #include "migration/vmstate.h" diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c index bbf953698f..30997638de 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -12,7 +12,7 @@ #include "qapi/clone-visitor.h" #include "qapi/visitor-impl.h" #include "qapi/error.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" struct QapiCloneVisitor { Visitor visitor; diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index ef283f2966..57a2c904bb 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/visitor-impl.h" struct QapiDeallocVisitor diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c index e36d9bc9ba..dea540c3c9 100644 --- a/qapi/qapi-forward-visitor.c +++ b/qapi/qapi-forward-visitor.c @@ -14,14 +14,14 @@ #include "qapi/forward-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" struct ForwardFieldVisitor { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 176b549473..e24ba9f0b6 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -17,11 +17,11 @@ #include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qmp/dispatch.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qbool.h" #include "qemu/coroutine.h" #include "qemu/main-loop.h" diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c index 0fe0d0a5a6..11cb6ace99 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -14,9 +14,9 @@ #include "qemu/osdep.h" #include "qapi/qmp-event.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" static void timestamp_put(QDict *qdict) { diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index f110a804b2..93317532be 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -19,14 +19,14 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/keyval.h" diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 74770edd73..13d3ae95b7 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -17,12 +17,12 @@ #include "qapi/qobject-output-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct QStackEntry { QObject *value; diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index 3f1b9e9b41..f4eecc73d0 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -15,7 +15,7 @@ #include "qapi/string-input-visitor.h" #include "qapi/visitor-impl.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qemu/option.h" #include "qemu/cutils.h" diff --git a/qemu-img.c b/qemu-img.c index 2f2fac90e8..89c93c1eb5 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -32,8 +32,8 @@ #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qobject-output-visitor.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" #include "qemu/config-file.h" #include "qemu/option.h" diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index bf08dcb8f5..13e0330162 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu-io.h" #include "system/block-backend.h" #include "block/block.h" diff --git a/qemu-io.c b/qemu-io.c index fa04695d1d..8f2de83f3c 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -27,8 +27,8 @@ #include "qemu/readline.h" #include "qemu/log.h" #include "qemu/sockets.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" #include "qom/object_interfaces.h" #include "system/block-backend.h" #include "block/block_int.h" diff --git a/qemu-nbd.c b/qemu-nbd.c index e7a961a556..b30d3ab8de 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -37,8 +37,8 @@ #include "qemu/log.h" #include "qemu/systemd.h" #include "block/snapshot.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qom/object_interfaces.h" #include "io/channel-socket.h" #include "io/net-listener.h" diff --git a/qga/main.c b/qga/main.c index 4a695235f0..531853e13d 100644 --- a/qga/main.c +++ b/qga/main.c @@ -19,9 +19,9 @@ #include #endif #include "qemu/help-texts.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/json-parser.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" #include "guest-agent-core.h" #include "qga-qapi-init-commands.h" #include "qapi/error.h" diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 4a83bda2c3..d0e1c63cf6 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -9,10 +9,10 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/cutils.h" #include "qapi/error.h" diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h index 16a25d00bb..8c01f23627 100644 --- a/qobject/json-parser-int.h +++ b/qobject/json-parser-int.h @@ -14,7 +14,7 @@ #ifndef JSON_PARSER_INT_H #define JSON_PARSER_INT_H -#include "qapi/qmp/json-parser.h" +#include "qobject/json-parser.h" typedef enum json_token_type { JSON_ERROR = 0, /* must be zero, see json_lexer[] */ diff --git a/qobject/json-parser.c b/qobject/json-parser.c index d498db6e70..7483e582fe 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -16,12 +16,12 @@ #include "qemu/cutils.h" #include "qemu/unicode.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "json-parser-int.h" struct JSONToken { diff --git a/qobject/json-writer.c b/qobject/json-writer.c index 309a31d57a..aac2c6ab71 100644 --- a/qobject/json-writer.c +++ b/qobject/json-writer.c @@ -14,7 +14,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/json-writer.h" +#include "qobject/json-writer.h" #include "qemu/unicode.h" struct JSONWriter { diff --git a/qobject/qbool.c b/qobject/qbool.c index c7049c0c50..00d7066aae 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qbool.h" #include "qobject-internal.h" /** diff --git a/qobject/qdict.c b/qobject/qdict.c index 8faff230d3..a90ac9ae2f 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -11,11 +11,11 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qbool.h" +#include "qobject/qnull.h" +#include "qobject/qstring.h" #include "qobject-internal.h" /** diff --git a/qobject/qjson.c b/qobject/qjson.c index 167fcb429c..c858dafb5e 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -13,14 +13,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-writer.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/json-parser.h" +#include "qobject/json-writer.h" +#include "qobject/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct JSONParsingState { JSONMessageParser parser; diff --git a/qobject/qlist.c b/qobject/qlist.c index 356ad946b0..41e6876d5b 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -11,11 +11,11 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/queue.h" #include "qobject-internal.h" diff --git a/qobject/qlit.c b/qobject/qlit.c index a62865b642..a44f47eaa5 100644 --- a/qobject/qlit.c +++ b/qobject/qlit.c @@ -15,13 +15,13 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qlit.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" +#include "qobject/qnull.h" static bool qlit_equal_qdict(const QLitObject *lhs, const QDict *qdict) { diff --git a/qobject/qnull.c b/qobject/qnull.c index 445a5db7f3..0fb78cbd0a 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qobject-internal.h" QNull qnull_ = { diff --git a/qobject/qnum.c b/qobject/qnum.c index dd8ea49565..a938b64537 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" #include "qobject-internal.h" /** diff --git a/qobject/qobject-internal.h b/qobject/qobject-internal.h index b310c8e1b5..0c7679fe98 100644 --- a/qobject/qobject-internal.h +++ b/qobject/qobject-internal.h @@ -10,7 +10,7 @@ #ifndef QOBJECT_INTERNAL_H #define QOBJECT_INTERNAL_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" static inline void qobject_init(QObject *obj, QType type) { diff --git a/qobject/qobject.c b/qobject/qobject.c index d7077b8f2a..78d1e057c1 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -8,12 +8,12 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qobject-internal.h" QEMU_BUILD_BUG_MSG( diff --git a/qobject/qstring.c b/qobject/qstring.c index 794f8c9357..d316604914 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qobject-internal.h" /** diff --git a/qom/object.c b/qom/object.c index ec447f14a7..01618d06bd 100644 --- a/qom/object.c +++ b/qom/object.c @@ -23,16 +23,16 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/forward-visitor.h" #include "qapi/qapi-builtin-visit.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" #include "trace.h" /* TODO: replace QObject with a simpler visitor to avoid a dependency * of the QOM core on QObject? */ #include "qom/qom-qobject.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/error-report.h" #define MAX_INTERFACES 32 diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 1a6f29c053..f35d331331 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -3,10 +3,10 @@ #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qapi-visit-qom.h" -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qom/object_interfaces.h" diff --git a/qom/qom-hmp-cmds.c b/qom/qom-hmp-cmds.c index 6e3a2175a4..a00a564b1e 100644 --- a/qom/qom-hmp-cmds.c +++ b/qom/qom-hmp-cmds.c @@ -11,8 +11,8 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-qom.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" #include "qemu/readline.h" #include "qom/object.h" #include "qom/object_interfaces.h" diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 46e4562300..e866547618 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -20,7 +20,7 @@ #include "qapi/qapi-commands-qdev.h" #include "qapi/qapi-commands-qom.h" #include "qapi/qapi-visit-qom.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c index b672ec3e3f..1105364002 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -17,7 +17,7 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-replay.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/timer.h" #include "block/snapshot.h" #include "migration/snapshot.h" diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index 7b7b326925..3c0a89442a 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -14,7 +14,7 @@ #include "system/replay.h" #include "replay-internal.h" #include "monitor/monitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/error-report.h" #include "migration/vmstate.h" #include "migration/snapshot.h" diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 79951a841f..f0aa13b5cf 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -320,7 +320,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): #include "qemu/osdep.h" #include "qapi/compat-policy.h" #include "qapi/visitor.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/dealloc-visitor.h" #include "qapi/error.h" #include "%(visit)s.h" @@ -330,7 +330,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): if self._gen_tracing and commands != 'qapi-commands': self._genc.add(mcgen(''' -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" #include "trace/trace-%(nm)s_trace_events.h" ''', nm=c_name(commands, protect=False))) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index d1f639981a..d179b0ed69 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -194,7 +194,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): #include "%(visit)s.h" #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp-event.h" ''', events=events, visit=visit, diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index ac14b20f30..42e5185c7c 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -197,7 +197,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): # generate C name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit' self._genh.add(mcgen(''' -#include "qapi/qmp/qlit.h" +#include "qobject/qlit.h" extern const QLitObject %(c_name)s; ''', diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index c6c6347e9b..b69dd982d6 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -47,7 +47,7 @@ #include "qemu/log.h" #include "qemu/systemd.h" #include "qapi/util.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "io/channel-socket.h" #include "trace/control.h" #include "qemu-version.h" diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c index 1f91bf8bd5..b93b471b1b 100644 --- a/stats/stats-hmp-cmds.c +++ b/stats/stats-hmp-cmds.c @@ -11,7 +11,7 @@ #include "monitor/monitor.h" #include "qemu/cutils.h" #include "hw/core/cpu.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index 325966eb9e..eb72561358 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -38,8 +38,8 @@ #include "qapi/qapi-visit-block-core.h" #include "qapi/qapi-visit-block-export.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/help-texts.h" diff --git a/system/device_tree.c b/system/device_tree.c index 11f3178095..4bc2d61b93 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -28,7 +28,7 @@ #include "hw/boards.h" #include "qemu/config-file.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "monitor/hmp.h" #include diff --git a/system/dirtylimit.c b/system/dirtylimit.c index 7c071248bb..7dedef8dd4 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include "system/dirtyrate.h" #include "system/dirtylimit.h" diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 861c25c855..81fe531bd6 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -27,9 +27,9 @@ #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" #include "qapi/qmp/dispatch.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/config-file.h" #include "qemu/error-report.h" diff --git a/system/runstate-hmp-cmds.c b/system/runstate-hmp-cmds.c index 2df670f0c0..be1d676992 100644 --- a/system/runstate-hmp-cmds.c +++ b/system/runstate-hmp-cmds.c @@ -19,7 +19,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-run-state.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/accel.h" void hmp_info_status(Monitor *mon, const QDict *qdict) diff --git a/system/vl.c b/system/vl.c index db8e604eba..ff91af8eca 100644 --- a/system/vl.c +++ b/system/vl.c @@ -31,9 +31,9 @@ #include "hw/qdev-properties.h" #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" +#include "qobject/qjson.h" #include "qemu-version.h" #include "qemu/cutils.h" #include "qemu/help_option.h" diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index 33cea080d1..883c0a0e8c 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -28,7 +28,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-commands-machine-target.h" #include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qom/qom-qobject.h" static GICCapability *gic_cap_new(int version) diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c index dc844cae8b..c1708b04bb 100644 --- a/target/i386/cpu-apic.c +++ b/target/i386/cpu-apic.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" diff --git a/target/i386/cpu-system.c b/target/i386/cpu-system.c index b56a2821af..55f192e819 100644 --- a/target/i386/cpu-system.c +++ b/target/i386/cpu-system.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "qapi/error.h" #include "qapi/qapi-visit-run-state.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qom/qom-qobject.h" #include "qapi/qapi-commands-machine-target.h" diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 2d766b2637..3ea92b066e 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -27,7 +27,7 @@ #include "monitor/monitor.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 782fd511fd..3fde5a5a20 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -10,7 +10,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-machine-target.h" #include "cpu.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qom/qom-qobject.h" diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index c05c2dc42d..8e49051254 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -32,7 +32,7 @@ #include "qemu/module.h" #include "qemu/qemu-print.h" #include "qapi/error.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/visitor.h" #include "hw/qdev-properties.h" #include "hw/ppc/ppc.h" diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index e945b3eb02..d0a324364d 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -26,8 +26,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-machine-target.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" #include "qom/qom-qobject.h" diff --git a/target/s390x/cpu_models_system.c b/target/s390x/cpu_models_system.c index 6b65fa2276..4351182f72 100644 --- a/target/s390x/cpu_models_system.c +++ b/target/s390x/cpu_models_system.c @@ -18,7 +18,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qapi-commands-machine-target.h" static void list_add_feat(const char *name, void *opaque); diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c index 6c312c499f..5ae8206234 100644 --- a/tests/qtest/adm1266-test.c +++ b/tests/qtest/adm1266-test.c @@ -13,8 +13,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "adm1266-test" diff --git a/tests/qtest/adm1272-test.c b/tests/qtest/adm1272-test.c index 63f8514801..2abda8d5be 100644 --- a/tests/qtest/adm1272-test.c +++ b/tests/qtest/adm1272-test.c @@ -12,8 +12,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "adm1272-test" diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 5a1923f721..88ac6c66ce 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -30,7 +30,7 @@ #include "libqos/ahci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/host-utils.h" #include "hw/pci/pci_ids.h" diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 98d6c970ea..eb8ddebffb 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" /* * We expect the SVE max-vq to be 16. Also it must be <= 64 diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index d38f51d719..12675d4cbb 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "libqtest-single.h" #define AST2600_GPIO_BASE 0x1E780000 diff --git a/tests/qtest/ast2700-gpio-test.c b/tests/qtest/ast2700-gpio-test.c index 9275845564..eeae9bf11f 100644 --- a/tests/qtest/ast2700-gpio-test.c +++ b/tests/qtest/ast2700-gpio-test.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "libqtest-single.h" #define AST2700_GPIO_BASE 0x14C0B000 diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index 4c851c2cdb..74d6b82dd2 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqos/fw_cfg.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "standard-headers/linux/qemu_fw_cfg.h" typedef struct { diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index c86725a511..56e2d283a9 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "boot-sector.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" static char isoimage[] = "cdrom-boot-iso-XXXXXX"; diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c index 7f5dd5f85a..6633abfc10 100644 --- a/tests/qtest/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -10,8 +10,8 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" struct PlugTestData { char *machine; diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index 587da59623..f84cec51dc 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -18,9 +18,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "libqtest.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index 127a7f9efe..2707ee59f6 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -12,8 +12,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" static void wait_device_deleted_event(QTestState *qtest, const char *id) { diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 99f6fc2de1..30d9451ddd 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -13,8 +13,8 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "libqos/virtio.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" static const char *qvirtio_get_dev_type(void); diff --git a/tests/qtest/emc141x-test.c b/tests/qtest/emc141x-test.c index 8c86694091..a24103e2cd 100644 --- a/tests/qtest/emc141x-test.c +++ b/tests/qtest/emc141x-test.c @@ -10,7 +10,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/sensor/emc141x_regs.h" #define EMC1414_TEST_ID "emc1414-test" diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c index 8645b080f7..1b37a8a4d2 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -26,7 +26,7 @@ #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define DRIVE_FLOPPY_BLANK \ "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k" diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index 1c73dea8f7..41481a5e09 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "libqtest.h" #include "libqos/fw_cfg.h" #include "libqos/libqos.h" diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 90ba6b298b..ceee444a9e 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -29,7 +29,7 @@ #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/malloc-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/bswap.h" #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c index 5553ea410a..1ff840c6b7 100644 --- a/tests/qtest/isl_pmbus_vr-test.c +++ b/tests/qtest/isl_pmbus_vr-test.c @@ -21,8 +21,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "isl_pmbus_vr-test" diff --git a/tests/qtest/libqmp.c b/tests/qtest/libqmp.c index a89cab03c3..16fe546885 100644 --- a/tests/qtest/libqmp.c +++ b/tests/qtest/libqmp.c @@ -25,8 +25,8 @@ #include "qemu/cutils.h" #include "qemu/sockets.h" #include "qapi/error.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/qjson.h" +#include "qobject/json-parser.h" +#include "qobject/qjson.h" #define SOCKET_MAX_FDS 16 diff --git a/tests/qtest/libqmp.h b/tests/qtest/libqmp.h index 3445b753ff..4a931c93ab 100644 --- a/tests/qtest/libqmp.h +++ b/tests/qtest/libqmp.h @@ -18,7 +18,7 @@ #ifndef LIBQMP_H #define LIBQMP_H -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" QDict *qmp_fd_receive(int fd); #ifndef _WIN32 diff --git a/tests/qtest/libqos/generic-pcihost.c b/tests/qtest/libqos/generic-pcihost.c index 3124b0e46b..4bbeb5ff50 100644 --- a/tests/qtest/libqos/generic-pcihost.c +++ b/tests/qtest/libqos/generic-pcihost.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "generic-pcihost.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "qemu/host-utils.h" diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 28a0901a0a..9b49d0d4dd 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -2,7 +2,7 @@ #include "../libqtest.h" #include "libqos.h" #include "pci.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" /*** Test Setup & Teardown ***/ diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c index 96046287ac..147009f4f4 100644 --- a/tests/qtest/libqos/pci-pc.c +++ b/tests/qtest/libqos/pci-pc.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "qemu/module.h" diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c index c6bb8bff09..493ab747de 100644 --- a/tests/qtest/libqos/qos_external.c +++ b/tests/qtest/libqos/qos_external.c @@ -19,11 +19,11 @@ #include "qemu/osdep.h" #include #include "../libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qbool.h" +#include "qobject/qstring.h" #include "qemu/module.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "libqos-malloc.h" #include "qgraph.h" #include "qgraph_internal.h" diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index fe8606ba6a..2750067861 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -34,11 +34,11 @@ #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/sockets.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qbool.h" #define MAX_IRQ 256 diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 29f123e281..930a91dcb7 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -17,9 +17,9 @@ #ifndef LIBQTEST_H #define LIBQTEST_H -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "libqmp.h" typedef struct QTestState QTestState; diff --git a/tests/qtest/lsm303dlhc-mag-test.c b/tests/qtest/lsm303dlhc-mag-test.c index 0f64e7fc67..55ef4594f9 100644 --- a/tests/qtest/lsm303dlhc-mag-test.c +++ b/tests/qtest/lsm303dlhc-mag-test.c @@ -13,7 +13,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define LSM303DLHC_MAG_TEST_ID "lsm303dlhc_mag-test" #define LSM303DLHC_MAG_REG_CRA 0x00 diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index 159b2a705a..b6a87d27ed 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" struct arch2cpu { diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c index dbf6ddc829..5e0878c923 100644 --- a/tests/qtest/max34451-test.c +++ b/tests/qtest/max34451-test.c @@ -11,8 +11,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "max34451-test" diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c new file mode 100644 index 0000000000..b08b49bd43 --- /dev/null +++ b/tests/qtest/migration-helpers.c @@ -0,0 +1,530 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/ctype.h" +#include "qobject/qjson.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/error.h" +#include "qobject/qlist.h" +#include "qemu/cutils.h" +#include "qemu/memalign.h" + +#include "migration-helpers.h" + +/* + * Number of seconds we wait when looking for migration + * status changes, to avoid test suite hanging forever + * when things go wrong. Needs to be higher enough to + * avoid false positives on loaded hosts. + */ +#define MIGRATION_STATUS_WAIT_TIMEOUT 120 + +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("vsock:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static QDict *SocketAddress_to_qdict(SocketAddress *addr) +{ + QDict *dict = qdict_new(); + + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + qdict_put_str(dict, "type", "inet"); + qdict_put_str(dict, "host", addr->u.inet.host); + qdict_put_str(dict, "port", addr->u.inet.port); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + qdict_put_str(dict, "type", "unix"); + qdict_put_str(dict, "path", addr->u.q_unix.path); + break; + case SOCKET_ADDRESS_TYPE_FD: + qdict_put_str(dict, "type", "fd"); + qdict_put_str(dict, "str", addr->u.fd.str); + break; + case SOCKET_ADDRESS_TYPE_VSOCK: + qdict_put_str(dict, "type", "vsock"); + qdict_put_str(dict, "cid", addr->u.vsock.cid); + qdict_put_str(dict, "port", addr->u.vsock.port); + break; + default: + g_assert_not_reached(); + } + + return dict; +} + +static SocketAddressList *migrate_get_socket_address(QTestState *who) +{ + QDict *rsp; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, "socket-address"); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); + visit_free(iv); + + qobject_unref(rsp); + return addrs; +} + +static char * +migrate_get_connect_uri(QTestState *who) +{ + SocketAddressList *addrs; + char *connect_uri; + + addrs = migrate_get_socket_address(who); + connect_uri = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_uri; +} + +static QDict * +migrate_get_connect_qdict(QTestState *who) +{ + SocketAddressList *addrs; + QDict *connect_qdict; + + addrs = migrate_get_socket_address(who); + connect_qdict = SocketAddress_to_qdict(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_qdict; +} + +static void migrate_set_ports(QTestState *to, QList *channel_list) +{ + QDict *addr; + QListEntry *entry; + const char *addr_port = NULL; + + addr = migrate_get_connect_qdict(to); + + QLIST_FOREACH_ENTRY(channel_list, entry) { + QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); + QDict *addrdict = qdict_get_qdict(channel, "addr"); + + if (qdict_haskey(addrdict, "port") && + qdict_haskey(addr, "port") && + (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { + addr_port = qdict_get_str(addr, "port"); + qdict_put_str(addrdict, "port", addr_port); + } + } + + qobject_unref(addr); +} + +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque) +{ + QTestMigrationState *state = opaque; + + if (g_str_equal(name, "STOP")) { + state->stop_seen = true; + return true; + } else if (g_str_equal(name, "SUSPEND")) { + state->suspend_seen = true; + return true; + } else if (g_str_equal(name, "RESUME")) { + state->resume_seen = true; + return true; + } + + return false; +} + +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args, *err; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + qdict_put_obj(args, "channels", channels_obj); + } + + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(err, "desc")); + + qobject_unref(err); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args; + g_autofree char *connect_uri = NULL; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } else if (!channels) { + connect_uri = migrate_get_connect_uri(to); + qdict_put_str(args, "uri", connect_uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + QList *channel_list = qobject_to(QList, channels_obj); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels_obj); + } + + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate', 'arguments': %p}", args); +} + +void migrate_set_capability(QTestState *who, const char *capability, + bool value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); +} + +void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + /* This function relies on the event to work, make sure it's enabled */ + migrate_set_capability(to, "events", true); + + rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", + args); + + if (!qdict_haskey(rsp, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); + g_test_message("%s", s->str); + } + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + migration_event_wait(to, "setup"); +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); +} + +QDict *migrate_query_not_failed(QTestState *who) +{ + const char *status; + QDict *rsp = migrate_query(who); + status = qdict_get_str(rsp, "status"); + if (g_str_equal(status, "failed")) { + g_printerr("query-migrate shows failed migration: %s\n", + qdict_get_str(rsp, "error-desc")); + } + g_assert(!g_str_equal(status, "failed")); + return rsp; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +static gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + g_test_timer_start(); + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + g_test_timer_start(); + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = qtest_qmp_assert_success_ref(from, + "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} + +char *find_common_machine_version(const char *mtype, const char *var1, + const char *var2) +{ + g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); + g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); + + g_assert(type1 && type2); + + if (g_str_equal(type1, type2)) { + /* either can be used */ + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var2, type1)) { + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var1, type2)) { + return g_strdup(type2); + } + + g_test_message("No common machine version for machine type '%s' between " + "binaries %s and %s", mtype, getenv(var1), getenv(var2)); + g_assert_not_reached(); +} + +char *resolve_machine_version(const char *alias, const char *var1, + const char *var2) +{ + const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); + g_autofree char *machine_name = NULL; + + if (mname) { + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + + machine_name = g_strdup(mname); + + if (dash && dot) { + assert(qtest_has_machine(machine_name)); + return g_steal_pointer(&machine_name); + } + /* else: probably an alias, let it be resolved below */ + } else { + /* use the hardcoded alias */ + machine_name = g_strdup(alias); + } + + return find_common_machine_version(machine_name, var1, var2); +} + +typedef struct { + char *name; + void (*func)(void); +} MigrationTest; + +static void migration_test_destroy(gpointer data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_free(test->name); + g_free(test); +} + +static void migration_test_wrapper(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func(); +} + +void migration_test_add(const char *path, void (*fn)(void)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + test->func = fn; + test->name = g_strdup(path); + + qtest_add_data_func_full(path, test, migration_test_wrapper, + migration_test_destroy); +} + +#ifdef O_DIRECT +/* + * Probe for O_DIRECT support on the filesystem. Since this is used + * for tests, be conservative, if anything fails, assume it's + * unsupported. + */ +bool probe_o_direct_support(const char *tmpfs) +{ + g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); + int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; + void *buf; + ssize_t ret, len; + uint64_t offset; + + fd = open(filename, flags, 0660); + if (fd < 0) { + unlink(filename); + return false; + } + + /* + * Using 1MB alignment as conservative choice to satisfy any + * plausible architecture default page size, and/or filesystem + * alignment restrictions. + */ + len = 0x100000; + offset = 0x100000; + + buf = qemu_try_memalign(len, len); + g_assert(buf); + + ret = pwrite(fd, buf, len, offset); + unlink(filename); + g_free(buf); + + if (ret < 0) { + return false; + } + + return true; +} +#endif + +/* + * Wait for a "MIGRATION" event. This is what Libvirt uses to track + * migration status changes. + */ +void migration_event_wait(QTestState *s, const char *target) +{ + QDict *response, *data; + const char *status; + bool found; + + do { + response = qtest_qmp_eventwait_ref(s, "MIGRATION"); + data = qdict_get_qdict(response, "data"); + g_assert(data); + status = qdict_get_str(data, "status"); + found = (strcmp(status, target) == 0); + qobject_unref(response); + } while (!found); +} diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c index 84225c8c33..6400ddca51 100644 --- a/tests/qtest/migration/file-tests.c +++ b/tests/qtest/migration/file-tests.c @@ -15,7 +15,7 @@ #include "migration/framework.h" #include "migration/migration-qmp.h" #include "migration/migration-util.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" static char *tmpfs; diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index de65bfe40d..10e1d04b58 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -19,8 +19,8 @@ #include "migration/migration-util.h" #include "ppc-util.h" #include "qapi/error.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 5610f6d15d..fb59741b2c 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -17,9 +17,9 @@ #include "qapi/error.h" #include "qapi/qapi-types-migration.h" #include "qapi/qapi-visit-migration.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 0ce1413b6c..6261d80e4a 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -15,7 +15,7 @@ #include "qapi/qapi-visit-sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/cutils.h" #include "qemu/memalign.h" diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index dda3707cf3..04e5a472d5 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" #include "libqtest.h" #include "migration/framework.h" #include "migration/migration-qmp.h" diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c index daf7449f2c..59e8c124c3 100644 --- a/tests/qtest/migration/postcopy-tests.c +++ b/tests/qtest/migration/postcopy-tests.c @@ -14,7 +14,7 @@ #include "libqtest.h" #include "migration/framework.h" #include "migration/migration-util.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 436dbd98e8..7d6d4f56e2 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -19,7 +19,7 @@ #include "migration/migration-qmp.h" #include "migration/migration-util.h" #include "ppc-util.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index 317af03817..b731af0ad9 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -11,7 +11,7 @@ #include #include "../unit/socket-helpers.h" #include "libqtest.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-sockets.h" diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index e751a72e36..8bc89b8a8b 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -18,7 +18,7 @@ #include "qemu/bitops.h" #include "qemu/timer.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define REF_HZ (25000000) diff --git a/tests/qtest/npcm7xx_emc-test.c b/tests/qtest/npcm7xx_emc-test.c index 2e1a1a6d70..eeedb27ee6 100644 --- a/tests/qtest/npcm7xx_emc-test.c +++ b/tests/qtest/npcm7xx_emc-test.c @@ -16,8 +16,8 @@ #include "qemu/osdep.h" #include "libqos/libqos.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #include "qemu/iov.h" diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c index b53a43c417..052ea87662 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -17,8 +17,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" static int verbosity_level; diff --git a/tests/qtest/npcm7xx_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c index 981b853c99..521ea789f1 100644 --- a/tests/qtest/npcm7xx_watchdog_timer-test.c +++ b/tests/qtest/npcm7xx_watchdog_timer-test.c @@ -18,7 +18,7 @@ #include "qemu/timer.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define WTCR_OFFSET 0x1c #define REF_HZ (25000000) diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 6d92baee86..d657f38947 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" static char *make_cli(const GString *generic_cli, const char *test_cli) { diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index dc021c2fdf..f788a44dbe 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "hw/misc/pvpanic.h" diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index d49d2ba931..5606baf47b 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/misc/pvpanic.h" static void test_panic_nopause(void) diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index 7f58fc3746..75d4078b79 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -14,7 +14,7 @@ #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "hw/pci-host/q35.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128 diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 2c15f60958..15c88248b7 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -14,7 +14,7 @@ #include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-introspect.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c index 22957fa49c..edf0886787 100644 --- a/tests/qtest/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -14,10 +14,10 @@ #include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index d677f87c8e..1e30a5bfe8 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qemu/cutils.h" #include "libqtest.h" diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index 2f7e75a339..abfd4b9512 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -20,7 +20,7 @@ #include #include "libqtest-single.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/module.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-machine.h" diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c index 760f974e63..c6f32a4e14 100644 --- a/tests/qtest/readconfig-test.c +++ b/tests/qtest/readconfig-test.c @@ -13,10 +13,10 @@ #include "qapi/qapi-visit-machine.h" #include "qapi/qapi-visit-qom.h" #include "qapi/qapi-visit-ui.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/units.h" static QTestState *qtest_init_with_config(const char *cfgdata) diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c index 0547d41173..20ccefabcb 100644 --- a/tests/qtest/tco-test.c +++ b/tests/qtest/tco-test.c @@ -12,7 +12,7 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "hw/southbridge/ich9.h" #include "hw/acpi/ich9.h" diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c index f3865f7519..723d2c2f29 100644 --- a/tests/qtest/test-filter-mirror.c +++ b/tests/qtest/test-filter-mirror.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c index a77d5fd8ec..a996a80c1c 100644 --- a/tests/qtest/test-filter-redirector.c +++ b/tests/qtest/test-filter-redirector.c @@ -52,7 +52,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" diff --git a/tests/qtest/test-netfilter.c b/tests/qtest/test-netfilter.c index b09ef7fae9..326d4bd85f 100644 --- a/tests/qtest/test-netfilter.c +++ b/tests/qtest/test-netfilter.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" /* add a netfilter to a netdev and then remove it */ static void add_one_netfilter(void) diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index 9cbc8b7ae9..b9603d46fa 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qbool.h" #include "libqtest-single.h" static char *get_cpu0_qom_path(void) diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c index 85ad4eed85..3b114a50f5 100644 --- a/tests/qtest/tmp105-test.c +++ b/tests/qtest/tmp105-test.c @@ -12,7 +12,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/sensor/tmp105_regs.h" #define TMP105_TEST_ID "tmp105-test" diff --git a/tests/qtest/tpm-emu.c b/tests/qtest/tpm-emu.c index 2bf8ff4c86..9e4c2005d0 100644 --- a/tests/qtest/tpm-emu.c +++ b/tests/qtest/tpm-emu.c @@ -16,8 +16,8 @@ #include "backends/tpm/tpm_ioctl.h" #include "io/channel-socket.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "tpm-emu.h" void tpm_emu_test_wait_cond(TPMTestState *s) diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c index 1c0319e6e7..2cb2dd4796 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -18,7 +18,7 @@ #include "hw/acpi/tpm.h" #include "libqtest.h" #include "tpm-util.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index bd977ef28d..75cb3e44b2 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -12,7 +12,7 @@ #include "libqtest-single.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/range.h" diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index f04573f98c..5baf81c3e6 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -13,9 +13,9 @@ #include "libqos/pci-pc.h" #include "migration/migration-qmp.h" #include "migration/migration-util.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qjson.h" #include "libqos/malloc-pc.h" #include "libqos/virtio-pci.h" #include "hw/pci/pci.h" diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index 2df75c9780..60e5229a3d 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -11,7 +11,7 @@ #include "libqtest-single.h" #include "qemu/iov.h" #include "qemu/module.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/virtio/virtio-net.h" #include "libqos/qgraph.h" #include "libqos/virtio-net.h" diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c index 29fee9e7c0..e613374665 100644 --- a/tests/qtest/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -15,7 +15,7 @@ #include "boot-sector.h" #include "acpi-utils.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" #define VMGENID_GUID_OFFSET 40 /* allow space for diff --git a/tests/qtest/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c index 797288d939..1754757162 100644 --- a/tests/qtest/wdt_ib700-test.c +++ b/tests/qtest/wdt_ib700-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/timer.h" static void qmp_check_no_event(QTestState *s) diff --git a/tests/unit/check-block-qdict.c b/tests/unit/check-block-qdict.c index 751c58e737..0036d85cfa 100644 --- a/tests/unit/check-block-qdict.c +++ b/tests/unit/check-block-qdict.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" #include "qapi/error.h" static void qdict_defaults_test(void) diff --git a/tests/unit/check-qdict.c b/tests/unit/check-qdict.c index b5efa859b0..a1312be30a 100644 --- a/tests/unit/check-qdict.c +++ b/tests/unit/check-qdict.c @@ -11,9 +11,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qjson.c b/tests/unit/check-qjson.c index a89293ce51..780a3654d0 100644 --- a/tests/unit/check-qjson.c +++ b/tests/unit/check-qjson.c @@ -14,12 +14,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qjson.h" +#include "qobject/qlit.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/unicode.h" static QString *from_json_str(const char *jstr, bool single, Error **errp) diff --git a/tests/unit/check-qlist.c b/tests/unit/check-qlist.c index 3cd0ccbf19..1388aeede3 100644 --- a/tests/unit/check-qlist.c +++ b/tests/unit/check-qlist.c @@ -11,8 +11,8 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qlist.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qlit.c b/tests/unit/check-qlit.c index bd6798d912..ea7a0d9119 100644 --- a/tests/unit/check-qlit.c +++ b/tests/unit/check-qlit.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qlit.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) { { "foo", QLIT_QNUM(42) }, diff --git a/tests/unit/check-qnull.c b/tests/unit/check-qnull.c index 5ceacc65d7..724a66d0bd 100644 --- a/tests/unit/check-qnull.c +++ b/tests/unit/check-qnull.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/error.h" diff --git a/tests/unit/check-qnum.c b/tests/unit/check-qnum.c index bf7fe45bac..a40120e8d3 100644 --- a/tests/unit/check-qnum.c +++ b/tests/unit/check-qnum.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qobject.c b/tests/unit/check-qobject.c index 022b7c74fe..ccb25660f2 100644 --- a/tests/unit/check-qobject.c +++ b/tests/unit/check-qobject.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index b48e890577..13d632cfed 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -22,8 +22,8 @@ #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qdict.h" +#include "qobject/qobject.h" #include "qom/object.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/tests/unit/check-qstring.c b/tests/unit/check-qstring.c index bd861f4f8b..2e6a00570f 100644 --- a/tests/unit/check-qstring.c +++ b/tests/unit/check-qstring.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" /* * Public Interface test-cases diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 1de04a8a13..7324ea4a68 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -28,7 +28,7 @@ #include "block/blockjob_int.h" #include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/clang-tsa.h" #include "qemu/main-loop.h" #include "iothread.h" diff --git a/tests/unit/test-blockjob-txn.c b/tests/unit/test-blockjob-txn.c index c7b4e55483..118503a8e8 100644 --- a/tests/unit/test-blockjob-txn.c +++ b/tests/unit/test-blockjob-txn.c @@ -15,7 +15,7 @@ #include "qemu/main-loop.h" #include "block/blockjob_int.h" #include "system/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" typedef struct { BlockJob common; diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index 1df5739b13..abdbe4b835 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -15,7 +15,7 @@ #include "qemu/main-loop.h" #include "block/blockjob_int.h" #include "system/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "iothread.h" static const BlockJobDriver test_block_job_driver = { diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c index 85b350a9b7..60a843b79d 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -10,7 +10,7 @@ #include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qom/qom-qobject.h" #include "io/channel-socket.h" #include "qapi/qobject-input-visitor.h" diff --git a/tests/unit/test-forward-visitor.c b/tests/unit/test-forward-visitor.c index eea8ffc072..aad1c89f13 100644 --- a/tests/unit/test-forward-visitor.c +++ b/tests/unit/test-forward-visitor.c @@ -12,8 +12,8 @@ #include "qapi/forward-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" #include "test-qapi-visit.h" #include "qemu/keyval.h" diff --git a/tests/unit/test-image-locking.c b/tests/unit/test-image-locking.c index 7ccf9567f1..019195f899 100644 --- a/tests/unit/test-image-locking.c +++ b/tests/unit/test-image-locking.c @@ -28,7 +28,7 @@ #include "block/block.h" #include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/main-loop.h" static BlockBackend *open_image(const char *path, diff --git a/tests/unit/test-keyval.c b/tests/unit/test-keyval.c index 4dc52c7a1a..c6e8f4fe37 100644 --- a/tests/unit/test-keyval.c +++ b/tests/unit/test-keyval.c @@ -13,9 +13,9 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" #include "qemu/cutils.h" diff --git a/tests/unit/test-qemu-opts.c b/tests/unit/test-qemu-opts.c index 828d40e928..8d03a69f7c 100644 --- a/tests/unit/test-qemu-opts.c +++ b/tests/unit/test-qemu-opts.c @@ -12,8 +12,8 @@ #include "qemu/option.h" #include "qemu/option_int.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/config-file.h" diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c index 8cddf5dc37..541b08a5e7 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -5,8 +5,8 @@ #include #include "../qtest/libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" typedef struct { char *test_dir; diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c index 6d52b4e5d8..ad53886886 100644 --- a/tests/unit/test-qmp-cmds.c +++ b/tests/unit/test-qmp-cmds.c @@ -1,9 +1,9 @@ #include "qemu/osdep.h" #include "qapi/compat-policy.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" #include "tests/test-qapi-types.h" diff --git a/tests/unit/test-qmp-event.c b/tests/unit/test-qmp-event.c index 08e95a382b..2aac27163d 100644 --- a/tests/unit/test-qmp-event.c +++ b/tests/unit/test-qmp-event.c @@ -15,11 +15,11 @@ #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qmp-event.h" #include "test-qapi-events.h" #include "test-qapi-emit-events.h" diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c index 5479e68237..84bdcdf702 100644 --- a/tests/unit/test-qobject-input-visitor.c +++ b/tests/unit/test-qobject-input-visitor.c @@ -17,12 +17,12 @@ #include "qapi/qapi-visit-introspect.h" #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" +#include "qobject/qjson.h" #include "test-qapi-introspect.h" #include "qapi/qapi-introspect.h" diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c index 3455f3b107..407ab9ed50 100644 --- a/tests/unit/test-qobject-output-visitor.c +++ b/tests/unit/test-qobject-output-visitor.c @@ -15,12 +15,12 @@ #include "qapi/error.h" #include "qapi/qobject-output-visitor.h" #include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct TestOutputVisitorData { Visitor *ov; diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c index 2a60f78e0a..3aa98e6f56 100644 --- a/tests/unit/test-replication.c +++ b/tests/unit/test-replication.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/option.h" #include "qemu/main-loop.h" #include "block/replication.h" diff --git a/tests/unit/test-visitor-serialization.c b/tests/unit/test-visitor-serialization.c index c2056c3eaa..2d365999fc 100644 --- a/tests/unit/test-visitor-serialization.c +++ b/tests/unit/test-visitor-serialization.c @@ -16,8 +16,8 @@ #include "test-qapi-visit.h" #include "qapi/error.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/string-input-visitor.h" diff --git a/trace/trace-hmp-cmds.c b/trace/trace-hmp-cmds.c index d38dd600de..45f4335ff5 100644 --- a/trace/trace-hmp-cmds.c +++ b/trace/trace-hmp-cmds.c @@ -27,7 +27,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-trace.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "trace/control.h" #ifdef CONFIG_TRACE_SIMPLE #include "trace/simple.h" diff --git a/ui/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c index 26c8ced1f2..980a8bbc51 100644 --- a/ui/ui-hmp-cmds.c +++ b/ui/ui-hmp-cmds.c @@ -21,7 +21,7 @@ #include "monitor/monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" #include "ui/console.h" #include "ui/input.h" diff --git a/util/keyval.c b/util/keyval.c index 66a5b4740f..a70629a481 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -91,9 +91,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/keyval.h" #include "qemu/help_option.h" diff --git a/util/qemu-config.c b/util/qemu-config.c index a90c18dad2..d1fc49c507 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "block/qdict.h" /* for qdict_extract_subqdict() */ #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/config-file.h" diff --git a/util/qemu-option.c b/util/qemu-option.c index 201f7a87f3..770300dff1 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -27,10 +27,10 @@ #include "qapi/error.h" #include "qemu/error-report.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qmp/qerror.h" #include "qemu/option_int.h" #include "qemu/cutils.h" From 153b0989d8ec7e8ef660ed9a614964f3dbdf231f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 18 Nov 2024 16:12:35 +0100 Subject: [PATCH 1561/2892] qapi: Move and rename qapi/qmp/dispatch.h to qapi/qmp-registry.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The general expectation is that header files should follow the same file/path naming scheme as the corresponding source file. There are various historical exceptions to this practice in QEMU, with one of the most notable being the include/qapi/qmp/ directory. include/qapi/qmp/dispatch.h corresponds mostly to qapi/qmp-registry.c. Move and rename it to include/qapi/qmp-registry.h. Now just qerror.h is left in include/qapi/qmp/. Since it's deprecated & (slowly) getting eliminated anyway, it isn't worth moving. Signed-off-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Signed-off-by: Markus Armbruster Message-ID: <20241118151235.2665921-3-armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 2 +- include/qapi/{qmp/dispatch.h => qmp-registry.h} | 0 monitor/monitor-internal.h | 2 +- qapi/qmp-dispatch.c | 2 +- qapi/qmp-registry.c | 2 +- qga/guest-agent-core.h | 2 +- scripts/qapi/commands.py | 2 +- stubs/qmp-command-available.c | 2 +- stubs/qmp-quit.c | 2 +- system/qdev-monitor.c | 2 +- 10 files changed, 9 insertions(+), 9 deletions(-) rename include/qapi/{qmp/dispatch.h => qmp-registry.h} (100%) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 259d6b10c3..9fa94251b0 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1855,7 +1855,7 @@ Example:: #ifndef EXAMPLE_QAPI_INIT_COMMANDS_H #define EXAMPLE_QAPI_INIT_COMMANDS_H - #include "qapi/qmp/dispatch.h" + #include "qapi/qmp-registry.h" void example_qmp_init_marshal(QmpCommandList *cmds); diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp-registry.h similarity index 100% rename from include/qapi/qmp/dispatch.h rename to include/qapi/qmp-registry.h diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 72cab703eb..5676eb334e 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -28,7 +28,7 @@ #include "chardev/char-fe.h" #include "monitor/monitor.h" #include "qapi/qapi-types-control.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" #include "qobject/json-parser.h" #include "qemu/readline.h" #include "system/iothread.h" diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index e24ba9f0b6..fa95fcceac 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -16,7 +16,7 @@ #include "block/aio.h" #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" #include "qobject/qdict.h" #include "qobject/qjson.h" #include "qapi/qobject-input-visitor.h" diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 485bc5e6fc..ac989074ed 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options, diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index b4e7c52c61..a536d07d0d 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -13,7 +13,7 @@ #ifndef GUEST_AGENT_CORE_H #define GUEST_AGENT_CORE_H -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" #include "qga-qapi-types.h" #define QGA_READ_COUNT_DEFAULT 4096 diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index f0aa13b5cf..74f341b2c7 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -346,7 +346,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): def visit_begin(self, schema: QAPISchema) -> None: self._add_module('./init', ' * QAPI Commands initialization') self._genh.add(mcgen(''' -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', diff --git a/stubs/qmp-command-available.c b/stubs/qmp-command-available.c index 46540af7bf..8851faced1 100644 --- a/stubs/qmp-command-available.c +++ b/stubs/qmp-command-available.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" bool qmp_command_available(const QmpCommand *cmd, Error **errp) { diff --git a/stubs/qmp-quit.c b/stubs/qmp-quit.c index a3ff47f7bd..8fb523e905 100644 --- a/stubs/qmp-quit.c +++ b/stubs/qmp-quit.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" void qmp_quit(Error **errp) { diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 81fe531bd6..856c9e8c32 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -26,7 +26,7 @@ #include "system/runstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" #include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qobject/qstring.h" From e00e0d0bd7cf261da4eb45bd183d6ceb72c04ee2 Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Tue, 17 Dec 2024 10:15:04 +0100 Subject: [PATCH 1562/2892] qapi: fix colon in Since tag section As described in docs/devel/qapi-code-gen.rst line 998, there should be no space between "Since" and ":". Signed-off-by: Victor Toso Message-ID: <20241217091504.16416-1-victortoso@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/cxl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi/cxl.json b/qapi/cxl.json index 9f65589bce..dd947d3bbc 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -460,7 +460,7 @@ # # @unstable: For now this command is subject to change. # -# Since : 9.1 +# Since: 9.1 ## { 'command': 'cxl-add-dynamic-capacity', 'data': { 'path': 'str', @@ -539,7 +539,7 @@ # # @unstable: For now this command is subject to change. # -# Since : 9.1 +# Since: 9.1 ## { 'command': 'cxl-release-dynamic-capacity', 'data': { 'path': 'str', From 208bd4333583e6e3228e8601a4f1b52595dde291 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Thu, 16 Jan 2025 18:44:33 +0800 Subject: [PATCH 1563/2892] qapi/ui: Fix documentation of upper bound value in InputMoveEvent The upper bound of pointer position in InputMoveEvent should be 0x7fff, according to INPUT_EVENT_ABS_MAX. Signed-off-by: Zhang Boyang Message-ID: <20250116104433.12114-1-zhangboyang.id@gmail.com> Acked-by: Markus Armbruster [Phrasing tweak squashed in] Signed-off-by: Markus Armbruster --- qapi/ui.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/ui.json b/qapi/ui.json index 460a26b981..c536d4e524 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1133,7 +1133,7 @@ # @axis: Which axis is referenced by @value. # # @value: Pointer position. For absolute coordinates the valid range -# is 0 -> 0x7ffff +# is 0 to 0x7fff. # # Since: 2.0 ## From d8a22e69fec2aa495665ee95af1997651a20ca1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 5 Feb 2025 12:35:47 +0000 Subject: [PATCH 1564/2892] qapi: cope with feature names containing a '-' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we shortly expose all feature names to code, it will be valid to include a '-', which must be translated to a '_' for the enum constants. Signed-off-by: Daniel P. Berrangé Message-ID: <20250205123550.2754387-2-berrange@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/gen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 6a8abe0041..c53ca72950 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -24,6 +24,7 @@ from typing import ( ) from .common import ( + c_enum_const, c_fname, c_name, guardend, @@ -41,7 +42,7 @@ from .source import QAPISourceInfo def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: - special_features = [f"1u << QAPI_{feat.name.upper()}" + special_features = [f"1u << {c_enum_const('qapi', feat.name)}" for feat in features if feat.is_special()] return ' | '.join(special_features) or '0' From 696ae1ac91fc50f87838519a0717d74f5816fd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 5 Feb 2025 12:35:48 +0000 Subject: [PATCH 1565/2892] qapi: change 'unsigned special_features' to 'uint64_t features' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "special_features" field / parameter holds the subset of schema features that are for internal code use. Specifically 'DEPRECATED' and 'UNSTABLE'. This special casing of internal features is going to be removed, so prepare for that by renaming to 'features'. Using a fixed size type is also best practice for bit fields. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé Message-ID: <20250205123550.2754387-3-berrange@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- include/qapi/compat-policy.h | 2 +- include/qapi/qmp-registry.h | 4 ++-- include/qapi/util.h | 2 +- include/qapi/visitor-impl.h | 4 ++-- include/qapi/visitor.h | 12 ++++++------ qapi/qapi-forward-visitor.c | 8 ++++---- qapi/qapi-util.c | 6 +++--- qapi/qapi-visit-core.c | 12 ++++++------ qapi/qmp-dispatch.c | 2 +- qapi/qmp-registry.c | 4 ++-- qapi/qobject-input-visitor.c | 4 ++-- qapi/qobject-output-visitor.c | 6 +++--- scripts/qapi/types.py | 2 +- 13 files changed, 34 insertions(+), 34 deletions(-) diff --git a/include/qapi/compat-policy.h b/include/qapi/compat-policy.h index 8b7b25c0b5..ea65e10744 100644 --- a/include/qapi/compat-policy.h +++ b/include/qapi/compat-policy.h @@ -18,7 +18,7 @@ extern CompatPolicy compat_policy; -bool compat_policy_input_ok(unsigned special_features, +bool compat_policy_input_ok(uint64_t features, const CompatPolicy *policy, ErrorClass error_class, const char *kind, const char *name, diff --git a/include/qapi/qmp-registry.h b/include/qapi/qmp-registry.h index f2e956813a..e0ee1ad3ac 100644 --- a/include/qapi/qmp-registry.h +++ b/include/qapi/qmp-registry.h @@ -33,7 +33,7 @@ typedef struct QmpCommand /* Runs in coroutine context if QCO_COROUTINE is set */ QmpCommandFunc *fn; QmpCommandOptions options; - unsigned special_features; + uint64_t features; QTAILQ_ENTRY(QmpCommand) node; bool enabled; const char *disable_reason; @@ -43,7 +43,7 @@ typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options, - unsigned special_features); + uint64_t features); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name, diff --git a/include/qapi/util.h b/include/qapi/util.h index b8254247b8..29bc4eb865 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -18,7 +18,7 @@ typedef enum { typedef struct QEnumLookup { const char *const *array; - const unsigned char *const special_features; + const uint64_t *const features; const int size; } QEnumLookup; diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 2badec5ba4..7beb0dbfa5 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -115,11 +115,11 @@ struct Visitor /* Optional */ bool (*policy_reject)(Visitor *v, const char *name, - unsigned special_features, Error **errp); + uint64_t features, Error **errp); /* Optional */ bool (*policy_skip)(Visitor *v, const char *name, - unsigned special_features); + uint64_t features); /* Must be set */ VisitorType type; diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 27b85d4700..f6a9b0743f 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -463,29 +463,29 @@ bool visit_optional(Visitor *v, const char *name, bool *present); /* * Should we reject member @name due to policy? * - * @special_features is the member's special features encoded as a - * bitset of QapiSpecialFeature. + * @features is the member's special features encoded as a + * bitset of QapiFeature. * * @name must not be NULL. This function is only useful between * visit_start_struct() and visit_end_struct(), since only objects * have deprecated members. */ bool visit_policy_reject(Visitor *v, const char *name, - unsigned special_features, Error **errp); + uint64_t features, Error **errp); /* * * Should we skip member @name due to policy? * - * @special_features is the member's special features encoded as a - * bitset of QapiSpecialFeature. + * @features is the member's special features encoded as a + * bitset of QapiFeature. * * @name must not be NULL. This function is only useful between * visit_start_struct() and visit_end_struct(), since only objects * have deprecated members. */ bool visit_policy_skip(Visitor *v, const char *name, - unsigned special_features); + uint64_t features); /* * Set policy for handling deprecated management interfaces. diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c index dea540c3c9..d91d921bdb 100644 --- a/qapi/qapi-forward-visitor.c +++ b/qapi/qapi-forward-visitor.c @@ -246,7 +246,7 @@ static void forward_field_optional(Visitor *v, const char *name, bool *present) } static bool forward_field_policy_reject(Visitor *v, const char *name, - unsigned special_features, + uint64_t features, Error **errp) { ForwardFieldVisitor *ffv = to_ffv(v); @@ -254,18 +254,18 @@ static bool forward_field_policy_reject(Visitor *v, const char *name, if (!forward_field_translate_name(ffv, &name, errp)) { return true; } - return visit_policy_reject(ffv->target, name, special_features, errp); + return visit_policy_reject(ffv->target, name, features, errp); } static bool forward_field_policy_skip(Visitor *v, const char *name, - unsigned special_features) + uint64_t features) { ForwardFieldVisitor *ffv = to_ffv(v); if (!forward_field_translate_name(ffv, &name, NULL)) { return true; } - return visit_policy_skip(ffv->target, name, special_features); + return visit_policy_skip(ffv->target, name, features); } static void forward_field_complete(Visitor *v, void *opaque) diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c index 65a7d18437..3d849fe034 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -37,19 +37,19 @@ static bool compat_policy_input_ok1(const char *adjective, } } -bool compat_policy_input_ok(unsigned special_features, +bool compat_policy_input_ok(uint64_t features, const CompatPolicy *policy, ErrorClass error_class, const char *kind, const char *name, Error **errp) { - if ((special_features & 1u << QAPI_DEPRECATED) + if ((features & 1u << QAPI_DEPRECATED) && !compat_policy_input_ok1("Deprecated", policy->deprecated_input, error_class, kind, name, errp)) { return false; } - if ((special_features & (1u << QAPI_UNSTABLE)) + if ((features & (1u << QAPI_UNSTABLE)) && !compat_policy_input_ok1("Unstable", policy->unstable_input, error_class, kind, name, errp)) { diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 6c13510a2b..706c61e026 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -141,21 +141,21 @@ bool visit_optional(Visitor *v, const char *name, bool *present) } bool visit_policy_reject(Visitor *v, const char *name, - unsigned special_features, Error **errp) + uint64_t features, Error **errp) { trace_visit_policy_reject(v, name); if (v->policy_reject) { - return v->policy_reject(v, name, special_features, errp); + return v->policy_reject(v, name, features, errp); } return false; } bool visit_policy_skip(Visitor *v, const char *name, - unsigned special_features) + uint64_t features) { trace_visit_policy_skip(v, name); if (v->policy_skip) { - return v->policy_skip(v, name, special_features); + return v->policy_skip(v, name, features); } return false; } @@ -409,8 +409,8 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, return false; } - if (lookup->special_features - && !compat_policy_input_ok(lookup->special_features[value], + if (lookup->features + && !compat_policy_input_ok(lookup->features[value], &v->compat_policy, ERROR_CLASS_GENERIC_ERROR, "value", enum_str, errp)) { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index fa95fcceac..e569224eae 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -173,7 +173,7 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ "The command %s has not been found", command); goto out; } - if (!compat_policy_input_ok(cmd->special_features, &compat_policy, + if (!compat_policy_input_ok(cmd->features, &compat_policy, ERROR_CLASS_COMMAND_NOT_FOUND, "command", command, &err)) { goto out; diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index ac989074ed..e2623f2b78 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -17,7 +17,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options, - unsigned special_features) + uint64_t features) { QmpCommand *cmd = g_malloc0(sizeof(*cmd)); @@ -28,7 +28,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, cmd->fn = fn; cmd->enabled = true; cmd->options = options; - cmd->special_features = special_features; + cmd->features = features; QTAILQ_INSERT_TAIL(cmds, cmd, node); } diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 93317532be..c52d36997d 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -664,10 +664,10 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present) } static bool qobject_input_policy_reject(Visitor *v, const char *name, - unsigned special_features, + uint64_t features, Error **errp) { - return !compat_policy_input_ok(special_features, &v->compat_policy, + return !compat_policy_input_ok(features, &v->compat_policy, ERROR_CLASS_GENERIC_ERROR, "parameter", name, errp); } diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 13d3ae95b7..de5b36bda5 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -210,13 +210,13 @@ static bool qobject_output_type_null(Visitor *v, const char *name, } static bool qobject_output_policy_skip(Visitor *v, const char *name, - unsigned special_features) + uint64_t features) { CompatPolicy *pol = &v->compat_policy; - return ((special_features & 1u << QAPI_DEPRECATED) + return ((features & 1u << QAPI_DEPRECATED) && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) - || ((special_features & 1u << QAPI_UNSTABLE) + || ((features & 1u << QAPI_UNSTABLE) && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE); } diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 0dd0b00ada..7bc3f8241f 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -71,7 +71,7 @@ const QEnumLookup %(c_name)s_lookup = { if feats: ret += mcgen(''' }, - .special_features = (const unsigned char[%(max_index)s]) { + .features = (const uint64_t[%(max_index)s]) { ''', max_index=max_index) ret += feats From ba27dccc04f16e143cbf59afec5402198d69be30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 5 Feb 2025 12:35:49 +0000 Subject: [PATCH 1566/2892] qapi: rename 'special_features' to 'features' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This updates the QAPI code generation to refer to 'features' instead of 'special_features', in preparation for generalizing their exposure. Signed-off-by: Daniel P. Berrangé Message-ID: <20250205123550.2754387-4-berrange@redhat.com> Reviewed-by: Markus Armbruster [Imports tidied up with isort] Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 4 ++-- scripts/qapi/gen.py | 8 ++++---- scripts/qapi/types.py | 14 +++++--------- scripts/qapi/visit.py | 18 +++++++----------- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 74f341b2c7..9556af5416 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -25,7 +25,7 @@ from .gen import ( QAPIGenC, QAPISchemaModularCVisitor, build_params, - gen_special_features, + gen_features, ifcontext, ) from .schema import ( @@ -298,7 +298,7 @@ def gen_register_command(name: str, ''', name=name, c_name=c_name(name), opts=' | '.join(options) or 0, - feats=gen_special_features(features)) + feats=gen_features(features)) return ret diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index c53ca72950..b51f8d955e 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -41,10 +41,10 @@ from .schema import ( from .source import QAPISourceInfo -def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: - special_features = [f"1u << {c_enum_const('qapi', feat.name)}" - for feat in features if feat.is_special()] - return ' | '.join(special_features) or '0' +def gen_features(features: Sequence[QAPISchemaFeature]) -> str: + featenum = [f"1u << {c_enum_const('qapi', feat.name)}" + for feat in features if feat.is_special()] + return ' | '.join(featenum) or '0' class QAPIGen: diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 7bc3f8241f..e4a1bb9f85 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -16,11 +16,7 @@ This work is licensed under the terms of the GNU GPL, version 2. from typing import List, Optional from .common import c_enum_const, c_name, mcgen -from .gen import ( - QAPISchemaModularCVisitor, - gen_special_features, - ifcontext, -) +from .gen import QAPISchemaModularCVisitor, gen_features, ifcontext from .schema import ( QAPISchema, QAPISchemaAlternatives, @@ -61,12 +57,12 @@ const QEnumLookup %(c_name)s_lookup = { index=index, name=memb.name) ret += memb.ifcond.gen_endif() - special_features = gen_special_features(memb.features) - if special_features != '0': + features = gen_features(memb.features) + if features != '0': feats += mcgen(''' - [%(index)s] = %(special_features)s, + [%(index)s] = %(features)s, ''', - index=index, special_features=special_features) + index=index, features=features) if feats: ret += mcgen(''' diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 12f92e429f..928273b9bb 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -21,11 +21,7 @@ from .common import ( indent, mcgen, ) -from .gen import ( - QAPISchemaModularCVisitor, - gen_special_features, - ifcontext, -) +from .gen import QAPISchemaModularCVisitor, gen_features, ifcontext from .schema import ( QAPISchema, QAPISchemaAlternatives, @@ -103,15 +99,15 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', name=memb.name, has=has) indent.increase() - special_features = gen_special_features(memb.features) - if special_features != '0': + features = gen_features(memb.features) + if features != '0': ret += mcgen(''' - if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) { + if (visit_policy_reject(v, "%(name)s", %(features)s, errp)) { return false; } - if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) { + if (!visit_policy_skip(v, "%(name)s", %(features)s)) { ''', - name=memb.name, special_features=special_features) + name=memb.name, features=features) indent.increase() ret += mcgen(''' if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) { @@ -120,7 +116,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', c_type=memb.type.c_name(), name=memb.name, c_name=c_name(memb.name)) - if special_features != '0': + if features != '0': indent.decrease() ret += mcgen(''' } From 2ebb09f34ffff152dda6d2b5d9a2348f3fefc6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 5 Feb 2025 12:35:50 +0000 Subject: [PATCH 1567/2892] qapi: expose all schema features to code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces use of the constants from the QapiSpecialFeatures enum, with constants from the auto-generate QapiFeatures enum in qapi-features.h The 'deprecated' and 'unstable' features still have a little bit of special handling, being force defined to be the 1st + 2nd features in the enum, regardless of whether they're used in the schema. This retains compatibility with common code that references the features via the QapiSpecialFeatures constants. Signed-off-by: Daniel P. Berrangé Message-ID: <20250205123550.2754387-5-berrange@redhat.com> Reviewed-by: Markus Armbruster [Imports tidied up with isort] Signed-off-by: Markus Armbruster --- meson.build | 1 + scripts/qapi/commands.py | 1 + scripts/qapi/features.py | 48 ++++++++++++++++++++++++ scripts/qapi/gen.py | 6 +-- scripts/qapi/main.py | 2 + scripts/qapi/schema.py | 31 ++++++++++++++- scripts/qapi/types.py | 7 +++- scripts/qapi/visit.py | 3 +- tests/meson.build | 2 + tests/qapi-schema/features-too-many.err | 2 + tests/qapi-schema/features-too-many.json | 13 +++++++ tests/qapi-schema/features-too-many.out | 0 tests/qapi-schema/meson.build | 1 + 13 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 scripts/qapi/features.py create mode 100644 tests/qapi-schema/features-too-many.err create mode 100644 tests/qapi-schema/features-too-many.json create mode 100644 tests/qapi-schema/features-too-many.out diff --git a/meson.build b/meson.build index 131b2225ab..ee59d647e4 100644 --- a/meson.build +++ b/meson.build @@ -3444,6 +3444,7 @@ qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', meson.current_source_dir() / 'scripts/qapi/schema.py', meson.current_source_dir() / 'scripts/qapi/source.py', meson.current_source_dir() / 'scripts/qapi/types.py', + meson.current_source_dir() / 'scripts/qapi/features.py', meson.current_source_dir() / 'scripts/qapi/visit.py', meson.current_source_dir() / 'scripts/qapi-gen.py' ] diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 9556af5416..7914227382 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -355,6 +355,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); #include "qemu/osdep.h" #include "%(prefix)sqapi-commands.h" #include "%(prefix)sqapi-init-commands.h" +#include "%(prefix)sqapi-features.h" void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) { diff --git a/scripts/qapi/features.py b/scripts/qapi/features.py new file mode 100644 index 0000000000..57563207a8 --- /dev/null +++ b/scripts/qapi/features.py @@ -0,0 +1,48 @@ +""" +QAPI features generator + +Copyright 2024 Red Hat + +This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. +""" + +from typing import ValuesView + +from .common import c_enum_const, c_name +from .gen import QAPISchemaMonolithicCVisitor +from .schema import QAPISchema, QAPISchemaFeature + + +class QAPISchemaGenFeatureVisitor(QAPISchemaMonolithicCVisitor): + + def __init__(self, prefix: str): + super().__init__( + prefix, 'qapi-features', + ' * Schema-defined QAPI features', + __doc__) + + self.features: ValuesView[QAPISchemaFeature] + + def visit_begin(self, schema: QAPISchema) -> None: + self.features = schema.features() + self._genh.add("#include \"qapi/util.h\"\n\n") + + def visit_end(self) -> None: + self._genh.add("typedef enum {\n") + for f in self.features: + self._genh.add(f" {c_enum_const('qapi_feature', f.name)}") + if f.name in QAPISchemaFeature.SPECIAL_NAMES: + self._genh.add(f" = {c_enum_const('qapi', f.name)},\n") + else: + self._genh.add(",\n") + + self._genh.add("} " + c_name('QapiFeature') + ";\n") + + +def gen_features(schema: QAPISchema, + output_dir: str, + prefix: str) -> None: + vis = QAPISchemaGenFeatureVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index b51f8d955e..d3c56d45c8 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -42,9 +42,9 @@ from .source import QAPISourceInfo def gen_features(features: Sequence[QAPISchemaFeature]) -> str: - featenum = [f"1u << {c_enum_const('qapi', feat.name)}" - for feat in features if feat.is_special()] - return ' | '.join(featenum) or '0' + feats = [f"1u << {c_enum_const('qapi_feature', feat.name)}" + for feat in features] + return ' | '.join(feats) or '0' class QAPIGen: diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index 316736b6a2..324081b9fc 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -15,6 +15,7 @@ from .commands import gen_commands from .common import must_match from .error import QAPIError from .events import gen_events +from .features import gen_features from .introspect import gen_introspect from .schema import QAPISchema from .types import gen_types @@ -49,6 +50,7 @@ def generate(schema_file: str, schema = QAPISchema(schema_file) gen_types(schema, output_dir, prefix, builtins) + gen_features(schema, output_dir, prefix) gen_visit(schema, output_dir, prefix, builtins) gen_commands(schema, output_dir, prefix, gen_tracing) gen_events(schema, output_dir, prefix) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index e97c978d38..7f70969c09 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -29,6 +29,7 @@ from typing import ( List, Optional, Union, + ValuesView, cast, ) @@ -933,8 +934,11 @@ class QAPISchemaEnumMember(QAPISchemaMember): class QAPISchemaFeature(QAPISchemaMember): role = 'feature' + # Features which are standardized across all schemas + SPECIAL_NAMES = ['deprecated', 'unstable'] + def is_special(self) -> bool: - return self.name in ('deprecated', 'unstable') + return self.name in QAPISchemaFeature.SPECIAL_NAMES class QAPISchemaObjectTypeMember(QAPISchemaMember): @@ -1138,6 +1142,16 @@ class QAPISchema: self._entity_list: List[QAPISchemaEntity] = [] self._entity_dict: Dict[str, QAPISchemaDefinition] = {} self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() + # NB, values in the dict will identify the first encountered + # usage of a named feature only + self._feature_dict: Dict[str, QAPISchemaFeature] = OrderedDict() + + # All schemas get the names defined in the QapiSpecialFeature enum. + # Rely on dict iteration order matching insertion order so that + # the special names are emitted first when generating code. + for f in QAPISchemaFeature.SPECIAL_NAMES: + self._feature_dict[f] = QAPISchemaFeature(f, None) + self._schema_dir = os.path.dirname(fname) self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) self._make_module(fname) @@ -1147,6 +1161,9 @@ class QAPISchema: self._def_exprs(exprs) self.check() + def features(self) -> ValuesView[QAPISchemaFeature]: + return self._feature_dict.values() + def _def_entity(self, ent: QAPISchemaEntity) -> None: self._entity_list.append(ent) @@ -1258,6 +1275,12 @@ class QAPISchema: ) -> List[QAPISchemaFeature]: if features is None: return [] + + for f in features: + feat = QAPISchemaFeature(f['name'], info) + if feat.name not in self._feature_dict: + self._feature_dict[feat.name] = feat + return [QAPISchemaFeature(f['name'], info, QAPISchemaIfCond(f.get('if'))) for f in features] @@ -1485,6 +1508,12 @@ class QAPISchema: for doc in self.docs: doc.check() + features = list(self._feature_dict.values()) + if len(features) > 64: + raise QAPISemError( + features[64].info, + "Maximum of 64 schema features is permitted") + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_begin(self) for mod in self._module_dict.values(): diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index e4a1bb9f85..2bf7533828 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -304,11 +304,14 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): #include "qapi/dealloc-visitor.h" #include "%(types)s.h" #include "%(visit)s.h" +#include "%(prefix)sqapi-features.h" ''', - types=types, visit=visit)) + types=types, visit=visit, + prefix=self._prefix)) self._genh.preamble_add(mcgen(''' #include "qapi/qapi-builtin-types.h" -''')) +''', + prefix=self._prefix)) def visit_begin(self, schema: QAPISchema) -> None: # gen_object() is recursive, ensure it doesn't visit the empty type diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 928273b9bb..36e240967b 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -356,8 +356,9 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): #include "qemu/osdep.h" #include "qapi/error.h" #include "%(visit)s.h" +#include "%(prefix)sqapi-features.h" ''', - visit=visit)) + visit=visit, prefix=self._prefix)) self._genh.preamble_add(mcgen(''' #include "qapi/qapi-builtin-visit.h" #include "%(types)s.h" diff --git a/tests/meson.build b/tests/meson.build index f96c1be574..c59619220f 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -16,6 +16,8 @@ test_qapi_outputs = [ 'test-qapi-events-sub-sub-module.h', 'test-qapi-events.c', 'test-qapi-events.h', + 'test-qapi-features.c', + 'test-qapi-features.h', 'test-qapi-init-commands.c', 'test-qapi-init-commands.h', 'test-qapi-introspect.c', diff --git a/tests/qapi-schema/features-too-many.err b/tests/qapi-schema/features-too-many.err new file mode 100644 index 0000000000..bbbd6e5202 --- /dev/null +++ b/tests/qapi-schema/features-too-many.err @@ -0,0 +1,2 @@ +features-too-many.json: In command 'go-fish': +features-too-many.json:2: Maximum of 64 schema features is permitted diff --git a/tests/qapi-schema/features-too-many.json b/tests/qapi-schema/features-too-many.json new file mode 100644 index 0000000000..aab0a0b5f1 --- /dev/null +++ b/tests/qapi-schema/features-too-many.json @@ -0,0 +1,13 @@ +# Max 64 features, with 2 specials, so 63rd custom is invalid +{ 'command': 'go-fish', + 'features': [ + 'f00', 'f01', 'f02', 'f03', 'f04', 'f05', 'f06', 'f07', + 'f08', 'f09', 'f0a', 'f0b', 'f0c', 'f0d', 'f0e', 'f0f', + 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', + 'f18', 'f19', 'f1a', 'f1b', 'f1c', 'f1d', 'f1e', 'f1f', + 'f20', 'f21', 'f22', 'f23', 'f24', 'f25', 'f26', 'f27', + 'f28', 'f29', 'f2a', 'f2b', 'f2c', 'f2d', 'f2e', 'f2f', + 'f30', 'f31', 'f32', 'f33', 'f34', 'f35', 'f36', 'f37', + 'f38', 'f39', 'f3a', 'f3b', 'f3c', 'f3d', 'f3e' + ] +} diff --git a/tests/qapi-schema/features-too-many.out b/tests/qapi-schema/features-too-many.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 0f479d9317..9577178b6f 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -105,6 +105,7 @@ schemas = [ 'event-case.json', 'event-member-invalid-dict.json', 'event-nest-struct.json', + 'features-too-many.json', 'features-bad-type.json', 'features-deprecated-type.json', 'features-duplicate-name.json', From 0146604190e922f3ca32e580f0a46bc04f1975c5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 7 Feb 2025 15:44:08 +0100 Subject: [PATCH 1568/2892] tests/functional: Convert the aarch64 xen test to the functional framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test just needs the adaption for the asset handling, then we can move it to the functional framework. Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-ID: <20250207144409.220006-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/avocado/boot_xen.py | 95 ---------------------------- tests/functional/meson.build | 1 + tests/functional/test_aarch64_xen.py | 90 ++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 96 deletions(-) delete mode 100644 tests/avocado/boot_xen.py create mode 100755 tests/functional/test_aarch64_xen.py diff --git a/MAINTAINERS b/MAINTAINERS index fefabe0cee..ebb415bc40 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2539,7 +2539,7 @@ M: Alex Bennée S: Maintained F: hw/core/guest-loader.c F: docs/system/guest-loader.rst -F: tests/avocado/boot_xen.py +F: tests/functional/test_aarch64_xen.py Intel Hexadecimal Object File Loader M: Su Hang diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py deleted file mode 100644 index 490a127a3e..0000000000 --- a/tests/avocado/boot_xen.py +++ /dev/null @@ -1,95 +0,0 @@ -# Functional test that boots a Xen hypervisor with a domU kernel and -# checks the console output is vaguely sane . -# -# Copyright (c) 2020 Linaro -# -# Author: -# Alex Bennée -# -# SPDX-License-Identifier: GPL-2.0-or-later -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado_qemu import wait_for_console_pattern -from boot_linux_console import LinuxKernelTest - - -class BootXen(LinuxKernelTest): - """ - Boots a Xen hypervisor with a Linux DomU kernel. - - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - - timeout = 90 - XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' - - def setUp(self): - super(BootXen, self).setUp() - - # Using my own built kernel - which works - kernel_url = ('https://fileserver.linaro.org/' - 's/JSsewXGZ6mqxPr5/download?path=%2F&files=' - 'linux-5.9.9-arm64-ajb') - kernel_sha1 = '4f92bc4b9f88d5ab792fa7a43a68555d344e1b83' - self.kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_sha1) - - def launch_xen(self, xen_path): - """ - Launch Xen with a dom0 guest kernel - """ - self.log.info("launch with xen_path: %s", xen_path) - - self.vm.set_console() - - self.vm.add_args('-machine', 'virtualization=on', - '-m', '768', - '-kernel', xen_path, - '-append', self.XEN_COMMON_COMMAND_LINE, - '-device', - 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' - % (self.kernel_path)) - - self.vm.launch() - - console_pattern = 'VFS: Cannot open root device' - wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") - - def test_arm64_xen_411_and_dom0(self): - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ - xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' - 'download?path=%2F&files=' - 'xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb') - xen_sha1 = '034e634d4416adbad1212d59b62bccdcda63e62a' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.11-arm64") - - self.launch_xen(xen_path) - - def test_arm64_xen_414_and_dom0(self): - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ - xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' - 'download?path=%2F&files=' - 'xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb') - xen_sha1 = 'b9d209dd689ed2b393e625303a225badefec1160' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.14-arm64") - - self.launch_xen(xen_path) - - def test_arm64_xen_415_and_dom0(self): - xen_url = ('https://fileserver.linaro.org/' - 's/JSsewXGZ6mqxPr5/download' - '?path=%2F&files=xen-upstream-4.15-unstable.deb') - xen_sha1 = 'fc191172b85cf355abb95d275a24cc0f6d6579d8' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.15-unstable") - - self.launch_xen(xen_path) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 3f085bfbca..e595508bfa 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -77,6 +77,7 @@ tests_aarch64_system_thorough = [ 'aarch64_tcg_plugins', 'aarch64_tuxrun', 'aarch64_virt', + 'aarch64_xen', 'aarch64_xlnx_versal', 'multiprocess', ] diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/test_aarch64_xen.py new file mode 100755 index 0000000000..339904221b --- /dev/null +++ b/tests/functional/test_aarch64_xen.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Xen hypervisor with a domU kernel and +# checks the console output is vaguely sane . +# +# Copyright (c) 2020 Linaro +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import Asset, LinuxKernelTest, wait_for_console_pattern + + +class BootXen(LinuxKernelTest): + """ + Boots a Xen hypervisor with a Linux DomU kernel. + """ + + timeout = 90 + XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' + + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' + 'download?path=%2F&files=linux-5.9.9-arm64-ajb'), + '00366fa51ea957c19462d2e2aefd480bef80ce727120e714ae48e0c88f261edb') + + def launch_xen(self, xen_path): + """ + Launch Xen with a dom0 guest kernel + """ + self.set_machine('virt') + self.cpu = "cortex-a57" + self.kernel_path = self.ASSET_KERNEL.fetch() + self.log.info("launch with xen_path: %s", xen_path) + + self.vm.set_console() + + self.vm.add_args('-machine', 'virtualization=on', + '-m', '768', + '-kernel', xen_path, + '-append', self.XEN_COMMON_COMMAND_LINE, + '-device', + 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' + % (self.kernel_path)) + + self.vm.launch() + + console_pattern = 'VFS: Cannot open root device' + wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") + + ASSET_XEN_4_11 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb'), + 'b745c2631342f9fcc0147ddc364edb62c20ecfebd430e5a3546e7d7c6891c0bc') + + def test_arm64_xen_411_and_dom0(self): + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_path = self.archive_extract(self.ASSET_XEN_4_11, format='deb', + member="boot/xen-4.11-arm64") + self.launch_xen(xen_path) + + ASSET_XEN_4_14 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb'), + 'e930a3293248edabd367d5b4b3b6448b9c99c057096ea8b47228a7870661d5cb') + + def test_arm64_xen_414_and_dom0(self): + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_path = self.archive_extract(self.ASSET_XEN_4_14, format='deb', + member="boot/xen-4.14-arm64") + self.launch_xen(xen_path) + + ASSET_XEN_4_15 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-upstream-4.15-unstable.deb'), + '2a9a8af8acf0231844657cc28baab95bd918b0ee2d493ee4ee6f8846e1358bc9') + + def test_arm64_xen_415_and_dom0(self): + xen_path = self.archive_extract(self.ASSET_XEN_4_15, format='deb', + member="boot/xen-4.15-unstable") + self.launch_xen(xen_path) + + +if __name__ == '__main__': + LinuxKernelTest.main() From b93fff4ec94c13a8d5e8c55d381a189705e6e266 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 5 Feb 2025 15:11:40 +0100 Subject: [PATCH 1569/2892] tests/functional/test_aarch64_virt: Fix vulkan test without egl-headless MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vulkan test currently fails if the egl-headless device is not available. Let's add a proper check to skip the test in this case. Fixes: 3d30f882ce ("tests/functional: extend test_aarch64_virt with vulkan test") Reported-by: Cornelia Huck Acked-by: Alex Bennée Tested-by: Cornelia Huck Message-ID: <20250205141140.97437-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_virt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 07c1c13638..95f5ce8b4c 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -189,6 +189,8 @@ class Aarch64VirtMachine(QemuSystemTest): self.skipTest("No venus support for virtio-gpu") elif "egl: no drm render node available" in excp.output: self.skipTest("Can't access host DRM render node") + elif "'type' does not accept value 'egl-headless'" in excp.output: + self.skipTest("egl-headless support is not available") else: self.log.info(f"unhandled launch failure: {excp.output}") raise excp From 5f6a260fceadf6466211dbf6873abf1b08ab5272 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 30 Jan 2025 20:27:12 +0100 Subject: [PATCH 1570/2892] tests/functional: Convert the hotplug_blk avocado test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using the serial console instead of ssh for executing commands in the guest, we can convert this test to the functional framework. Reviewed-by: Daniel P. Berrangé Message-ID: <20250130192712.19542-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/avocado/hotplug_blk.py | 69 ----------------- tests/functional/meson.build | 1 + tests/functional/test_x86_64_hotplug_blk.py | 85 +++++++++++++++++++++ 4 files changed, 87 insertions(+), 69 deletions(-) delete mode 100644 tests/avocado/hotplug_blk.py create mode 100755 tests/functional/test_x86_64_hotplug_blk.py diff --git a/MAINTAINERS b/MAINTAINERS index ebb415bc40..a593241800 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2275,6 +2275,7 @@ F: hw/block/virtio-blk.c F: hw/block/dataplane/* F: include/hw/virtio/virtio-blk-common.h F: tests/qtest/virtio-blk-test.c +F: tests/functional/test_x86_64_hotplug_blk.py T: git https://github.com/stefanha/qemu.git block virtio-ccw diff --git a/tests/avocado/hotplug_blk.py b/tests/avocado/hotplug_blk.py deleted file mode 100644 index b36bca02ec..0000000000 --- a/tests/avocado/hotplug_blk.py +++ /dev/null @@ -1,69 +0,0 @@ -# Functional test that hotplugs a virtio blk disk and checks it on a Linux -# guest -# -# Copyright (c) 2021 Red Hat, Inc. -# Copyright (c) Yandex -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import time - -from avocado_qemu.linuxtest import LinuxTest - - -class HotPlug(LinuxTest): - def blockdev_add(self) -> None: - self.vm.cmd('blockdev-add', **{ - 'driver': 'null-co', - 'size': 1073741824, - 'node-name': 'disk' - }) - - def assert_vda(self) -> None: - self.ssh_command('test -e /sys/block/vda') - - def assert_no_vda(self) -> None: - with self.assertRaises(AssertionError): - self.assert_vda() - - def plug(self) -> None: - args = { - 'driver': 'virtio-blk-pci', - 'drive': 'disk', - 'id': 'virtio-disk0', - 'bus': 'pci.1', - 'addr': '1', - } - - self.assert_no_vda() - self.vm.cmd('device_add', args) - try: - self.assert_vda() - except AssertionError: - time.sleep(1) - self.assert_vda() - - def unplug(self) -> None: - self.vm.cmd('device_del', id='virtio-disk0') - - self.vm.event_wait('DEVICE_DELETED', 1.0, - match={'data': {'device': 'virtio-disk0'}}) - - self.assert_no_vda() - - def test(self) -> None: - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - """ - self.require_accelerator('kvm') - self.vm.add_args('-accel', 'kvm') - self.vm.add_args('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') - - self.launch_and_wait() - self.blockdev_add() - - self.plug() - self.unplug() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e595508bfa..70646d3457 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -285,6 +285,7 @@ tests_x86_64_system_thorough = [ 'multiprocess', 'netdev_ethtool', 'virtio_gpu', + 'x86_64_hotplug_blk', 'x86_64_hotplug_cpu', 'x86_64_kvm_xen', 'x86_64_tuxrun', diff --git a/tests/functional/test_x86_64_hotplug_blk.py b/tests/functional/test_x86_64_hotplug_blk.py new file mode 100755 index 0000000000..7ddbfefc21 --- /dev/null +++ b/tests/functional/test_x86_64_hotplug_blk.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# +# Functional test that hotplugs a virtio blk disk and checks it on a Linux +# guest +# +# Copyright (c) 2021 Red Hat, Inc. +# Copyright (c) Yandex +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern + + +class HotPlugBlk(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + def blockdev_add(self) -> None: + self.vm.cmd('blockdev-add', **{ + 'driver': 'null-co', + 'size': 1073741824, + 'node-name': 'disk' + }) + + def assert_vda(self) -> None: + exec_command_and_wait_for_pattern(self, 'while ! test -e /sys/block/vda ;' + ' do sleep 0.2 ; done', '# ') + + def assert_no_vda(self) -> None: + exec_command_and_wait_for_pattern(self, 'while test -e /sys/block/vda ;' + ' do sleep 0.2 ; done', '# ') + + def plug(self) -> None: + args = { + 'driver': 'virtio-blk-pci', + 'drive': 'disk', + 'id': 'virtio-disk0', + 'bus': 'pci.1', + 'addr': '1', + } + + self.assert_no_vda() + self.vm.cmd('device_add', args) + self.wait_for_console_pattern('virtio_blk virtio0: [vda]') + self.assert_vda() + + def unplug(self) -> None: + self.vm.cmd('device_del', id='virtio-disk0') + + self.vm.event_wait('DEVICE_DELETED', 1.0, + match={'data': {'device': 'virtio-disk0'}}) + + self.assert_no_vda() + + def test(self) -> None: + self.require_accelerator('kvm') + self.set_machine('q35') + + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') + self.vm.add_args('-m', '1G') + self.vm.add_args('-append', 'console=ttyS0 rd.rescue') + + self.launch_kernel(self.ASSET_KERNEL.fetch(), + self.ASSET_INITRD.fetch(), + wait_for='Entering emergency mode.') + self.wait_for_console_pattern('# ') + + self.blockdev_add() + + self.plug() + self.unplug() + + +if __name__ == '__main__': + LinuxKernelTest.main() From 557277710ae7f66ae572a362ac32a8b865e7104e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 3 Feb 2025 10:26:06 +0100 Subject: [PATCH 1571/2892] tests/functional: Add a ppc sam460ex test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test sequence boots from kernel a sam460ex machine with a virtio-net device to check PCI. The buildroot is built with config : BR2_powerpc=y BR2_powerpc_440fp=y and the kernel with the '44x/canyonlands' deconfig and virtio support. Signed-off-by: Cédric Le Goater Message-ID: <20250203092606.491933-1-clg@redhat.com> [thuth: sort meson.build alphabetically] Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_ppc_sam460ex.py | 38 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 tests/functional/test_ppc_sam460ex.py diff --git a/MAINTAINERS b/MAINTAINERS index a593241800..794bde23a4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1562,6 +1562,7 @@ F: pc-bios/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex F: docs/system/ppc/amigang.rst +F: tests/functional/test_ppc_sam460ex.py pegasos2 M: BALATON Zoltan diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 70646d3457..b516d21cba 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -197,6 +197,7 @@ tests_ppc_system_thorough = [ 'ppc_bamboo', 'ppc_mac', 'ppc_mpc8544ds', + 'ppc_sam460ex', 'ppc_tuxrun', 'ppc_virtex_ml507', ] diff --git a/tests/functional/test_ppc_sam460ex.py b/tests/functional/test_ppc_sam460ex.py new file mode 100644 index 0000000000..31cf9dd6de --- /dev/null +++ b/tests/functional/test_ppc_sam460ex.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a sam460ex machine with a PPC 460EX CPU +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + + +class sam460exTest(LinuxKernelTest): + + ASSET_BR2_SAM460EX_LINUX = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc_sam460ex-2023.11-8-gdcd9f0f6eb-20240105/vmlinux', + '6f46346f3e20e8b5fc050ff363f350f8b9d76a051b9e0bd7ea470cc680c14df2') + + def test_ppc_sam460ex_buildroot(self): + self.set_machine('sam460ex') + self.require_netdev('user') + + linux_path = self.ASSET_BR2_SAM460EX_LINUX.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', linux_path, + '-device', 'virtio-net-pci,netdev=net0', + '-netdev', 'user,id=net0') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('Hardware name: amcc,canyonlands 460EX') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15 obtained') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'System Halted') + +if __name__ == '__main__': + LinuxKernelTest.main() From 7976879c4b8439d78865c7ce98297f51cc90d061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Dec 2024 19:48:01 +0000 Subject: [PATCH 1572/2892] gitlab: don't fail cirrus CI jobs when credits are exhausted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the last week of the month we have often run out of credits on Cirrus CI, which causes the jobs to fail, in turn causing the overall pipeline to fail. The cirrus-run tool can now detect the "out of credits" scenario and exits with a code of '3'. We can tell gitlab to treat this exit code as special and mark the job as "warning" instead of "failed". This allows the pipeline status overall to remain green, when we have non-technical issues with Cirrus CI. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20241204194807.1472261-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/cirrus.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index a9e43e21d0..adc0007e5d 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -15,6 +15,8 @@ stage: build image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:latest needs: [] + allow_failure: + exit_codes: 3 # 20 mins larger than "timeout_in" in cirrus/build.yml # as there's often a 5-10 minute delay before Cirrus CI # actually starts the task From b56e07ac1e11a8cfdce521e7e0301f7dcb90af2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Dec 2024 19:48:02 +0000 Subject: [PATCH 1573/2892] gitlab: use new(ish) cirrus-vars command for creating config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than a giant sed command with a hardcoded list of env var name, we can now use the new(ish) cirrus-vars command that libvirt has added to the 'cirrus-run' container. Signed-off-by: Daniel P. Berrangé Message-ID: <20241204194807.1472261-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/cirrus.yml | 23 +++-------------------- .gitlab-ci.d/cirrus/build.yml | 2 +- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index adc0007e5d..16411f3d2b 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -22,27 +22,10 @@ # actually starts the task timeout: 80m script: + - set -o allexport - source .gitlab-ci.d/cirrus/$NAME.vars - - sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g" - -e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g" - -e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g" - -e "s|[@]CIRRUS_VM_INSTANCE_TYPE@|$CIRRUS_VM_INSTANCE_TYPE|g" - -e "s|[@]CIRRUS_VM_IMAGE_SELECTOR@|$CIRRUS_VM_IMAGE_SELECTOR|g" - -e "s|[@]CIRRUS_VM_IMAGE_NAME@|$CIRRUS_VM_IMAGE_NAME|g" - -e "s|[@]CIRRUS_VM_CPUS@|$CIRRUS_VM_CPUS|g" - -e "s|[@]CIRRUS_VM_RAM@|$CIRRUS_VM_RAM|g" - -e "s|[@]UPDATE_COMMAND@|$UPDATE_COMMAND|g" - -e "s|[@]INSTALL_COMMAND@|$INSTALL_COMMAND|g" - -e "s|[@]PATH@|$PATH_EXTRA${PATH_EXTRA:+:}\$PATH|g" - -e "s|[@]PKG_CONFIG_PATH@|$PKG_CONFIG_PATH|g" - -e "s|[@]PKGS@|$PKGS|g" - -e "s|[@]MAKE@|$MAKE|g" - -e "s|[@]PYTHON@|$PYTHON|g" - -e "s|[@]PIP3@|$PIP3|g" - -e "s|[@]PYPI_PKGS@|$PYPI_PKGS|g" - -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g" - -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g" - <.gitlab-ci.d/cirrus/build.yml >.gitlab-ci.d/cirrus/$NAME.yml + - set +o allexport + - cirrus-vars <.gitlab-ci.d/cirrus/build.yml >.gitlab-ci.d/cirrus/$NAME.yml - cat .gitlab-ci.d/cirrus/$NAME.yml - cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml variables: diff --git a/.gitlab-ci.d/cirrus/build.yml b/.gitlab-ci.d/cirrus/build.yml index 102cdbd8b1..41abd0b31a 100644 --- a/.gitlab-ci.d/cirrus/build.yml +++ b/.gitlab-ci.d/cirrus/build.yml @@ -8,7 +8,7 @@ env: CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@" CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@" CI_COMMIT_SHA: "@CI_COMMIT_SHA@" - PATH: "@PATH@" + PATH: "@PATH_EXTRA@:$PATH" PKG_CONFIG_PATH: "@PKG_CONFIG_PATH@" PYTHON: "@PYTHON@" MAKE: "@MAKE@" From 66a1b4991c32e370a4e0ddabf496aa1563aff286 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 11 Feb 2025 12:47:18 +0100 Subject: [PATCH 1574/2892] gitlab-ci.d/cirrus: Update the FreeBSD job to v14.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FreeBSD job started to fail since the 14-1 image disappeared from the cloud. Update the job to v14.2 to fix it. Message-ID: <20250211120817.35050-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- .gitlab-ci.d/cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 16411f3d2b..75b611418e 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -37,7 +37,7 @@ x64-freebsd-14-build: NAME: freebsd-14 CIRRUS_VM_INSTANCE_TYPE: freebsd_instance CIRRUS_VM_IMAGE_SELECTOR: image_family - CIRRUS_VM_IMAGE_NAME: freebsd-14-1 + CIRRUS_VM_IMAGE_NAME: freebsd-14-2 CIRRUS_VM_CPUS: 8 CIRRUS_VM_RAM: 8G UPDATE_COMMAND: pkg update; pkg upgrade -y From f0c29a02343d733154a64271124609cb5f117797 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 11 Feb 2025 12:58:27 +0000 Subject: [PATCH 1575/2892] target/alpha: Don't corrupt error_code with unknown softfloat flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In do_cvttq() we set env->error_code with what is supposed to be a set of FPCR exception bit values. However, if the set of float exception flags we get back from softfloat for the conversion includes a flag which is not one of the three we expect here (invalid_cvti, invalid, inexact) then we will fall through the if-ladder and set env->error_code to the unconverted softfloat exception_flag value. This will then cause us to take a spurious exception. This is harmless now, but when we add new floating point exception flags to softfloat it will cause problems. Add an else clause to the if-ladder to make it ignore any float exception flags it doesn't care about. Specifically, without this fix, 'make check-tcg' will fail for Alpha when the commit adding float_flag_input_denormal_used lands. Fixes: aa3bad5b59e7 ("target/alpha: Use float64_to_int64_modulo for CVTTQ") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- target/alpha/fpu_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c index 63d9e9ce39..f810a9b6a4 100644 --- a/target/alpha/fpu_helper.c +++ b/target/alpha/fpu_helper.c @@ -476,6 +476,8 @@ static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode) exc = FPCR_INV; } else if (exc & float_flag_inexact) { exc = FPCR_INE; + } else { + exc = 0; } } env->error_code = exc; From 6e7998ceb9008e82501c7cf069e5552c7e352c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Jan 2025 14:06:04 +0100 Subject: [PATCH 1576/2892] vfio/igd: Fix potential overflow in igd_gtt_memory_size() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The risk is mainly theoretical since the applied bit mask will keep the 'ggms' shift value below 3. Nevertheless, let's use a 64 bit integer type and resolve the coverity issue. Resolves: Coverity CID 1585908 Fixes: 1e1eac5f3dcd ("vfio/igd: canonicalize memory size calculations") Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20250107130604.669697-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 0740a5dd8c..b5303ea565 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -133,7 +133,7 @@ static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch) } else { ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK; if (ggms != 0) { - ggms = 1 << ggms; + ggms = 1ULL << ggms; } } From dee69a8ca6ebc325939dc0b6800becdb26cd9dbf Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Sat, 4 Jan 2025 23:42:16 +0800 Subject: [PATCH 1577/2892] vfio/pci: declare generic quirks in a new header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare generic vfio_generic_{window_address,window_data,mirror}_quirk in newly created pci_quirks.h so that they can be used elsewhere, like igd.c. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20250104154219.7209-2-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 55 +++------------------------------- hw/vfio/pci-quirks.h | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 hw/vfio/pci-quirks.h diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index c8e60475d5..2258fdda5f 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -25,6 +25,7 @@ #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" #include "pci.h" +#include "pci-quirks.h" #include "trace.h" /* @@ -66,40 +67,6 @@ bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev) * Device specific region quirks (mostly backdoors to PCI config space) */ -/* - * The generic window quirks operate on an address and data register, - * vfio_generic_window_address_quirk handles the address register and - * vfio_generic_window_data_quirk handles the data register. These ops - * pass reads and writes through to hardware until a value matching the - * stored address match/mask is written. When this occurs, the data - * register access emulated PCI config space for the device rather than - * passing through accesses. This enables devices where PCI config space - * is accessible behind a window register to maintain the virtualization - * provided through vfio. - */ -typedef struct VFIOConfigWindowMatch { - uint32_t match; - uint32_t mask; -} VFIOConfigWindowMatch; - -typedef struct VFIOConfigWindowQuirk { - struct VFIOPCIDevice *vdev; - - uint32_t address_val; - - uint32_t address_offset; - uint32_t data_offset; - - bool window_enabled; - uint8_t bar; - - MemoryRegion *addr_mem; - MemoryRegion *data_mem; - - uint32_t nr_matches; - VFIOConfigWindowMatch matches[]; -} VFIOConfigWindowQuirk; - static uint64_t vfio_generic_window_quirk_address_read(void *opaque, hwaddr addr, unsigned size) @@ -135,7 +102,7 @@ static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr, } } -static const MemoryRegionOps vfio_generic_window_address_quirk = { +const MemoryRegionOps vfio_generic_window_address_quirk = { .read = vfio_generic_window_quirk_address_read, .write = vfio_generic_window_quirk_address_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -178,26 +145,12 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr, addr + window->data_offset, data, size); } -static const MemoryRegionOps vfio_generic_window_data_quirk = { +const MemoryRegionOps vfio_generic_window_data_quirk = { .read = vfio_generic_window_quirk_data_read, .write = vfio_generic_window_quirk_data_write, .endianness = DEVICE_LITTLE_ENDIAN, }; -/* - * The generic mirror quirk handles devices which expose PCI config space - * through a region within a BAR. When enabled, reads and writes are - * redirected through to emulated PCI config space. XXX if PCI config space - * used memory regions, this could just be an alias. - */ -typedef struct VFIOConfigMirrorQuirk { - struct VFIOPCIDevice *vdev; - uint32_t offset; - uint8_t bar; - MemoryRegion *mem; - uint8_t data[]; -} VFIOConfigMirrorQuirk; - static uint64_t vfio_generic_quirk_mirror_read(void *opaque, hwaddr addr, unsigned size) { @@ -228,7 +181,7 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, addr, data); } -static const MemoryRegionOps vfio_generic_mirror_quirk = { +const MemoryRegionOps vfio_generic_mirror_quirk = { .read = vfio_generic_quirk_mirror_read, .write = vfio_generic_quirk_mirror_write, .endianness = DEVICE_LITTLE_ENDIAN, diff --git a/hw/vfio/pci-quirks.h b/hw/vfio/pci-quirks.h new file mode 100644 index 0000000000..c0e96a01cc --- /dev/null +++ b/hw/vfio/pci-quirks.h @@ -0,0 +1,71 @@ +/* + * vfio generic region quirks (mostly backdoors to PCI config space) + * + * Copyright Red Hat, Inc. 2012-2015 + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#ifndef HW_VFIO_VFIO_PCI_QUIRKS_H +#define HW_VFIO_VFIO_PCI_QUIRKS_H + +#include "qemu/osdep.h" +#include "exec/memop.h" + +/* + * The generic window quirks operate on an address and data register, + * vfio_generic_window_address_quirk handles the address register and + * vfio_generic_window_data_quirk handles the data register. These ops + * pass reads and writes through to hardware until a value matching the + * stored address match/mask is written. When this occurs, the data + * register access emulated PCI config space for the device rather than + * passing through accesses. This enables devices where PCI config space + * is accessible behind a window register to maintain the virtualization + * provided through vfio. + */ +typedef struct VFIOConfigWindowMatch { + uint32_t match; + uint32_t mask; +} VFIOConfigWindowMatch; + +typedef struct VFIOConfigWindowQuirk { + struct VFIOPCIDevice *vdev; + + uint32_t address_val; + + uint32_t address_offset; + uint32_t data_offset; + + bool window_enabled; + uint8_t bar; + + MemoryRegion *addr_mem; + MemoryRegion *data_mem; + + uint32_t nr_matches; + VFIOConfigWindowMatch matches[]; +} VFIOConfigWindowQuirk; + +extern const MemoryRegionOps vfio_generic_window_address_quirk; +extern const MemoryRegionOps vfio_generic_window_data_quirk; + +/* + * The generic mirror quirk handles devices which expose PCI config space + * through a region within a BAR. When enabled, reads and writes are + * redirected through to emulated PCI config space. XXX if PCI config space + * used memory regions, this could just be an alias. + */ +typedef struct VFIOConfigMirrorQuirk { + struct VFIOPCIDevice *vdev; + uint32_t offset; + uint8_t bar; + MemoryRegion *mem; + uint8_t data[]; +} VFIOConfigMirrorQuirk; + +extern const MemoryRegionOps vfio_generic_mirror_quirk; + +#endif /* HW_VFIO_VFIO_PCI_QUIRKS_H */ From f36e7ba9750cbeffdc465c2d195a232d7cb67555 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Sat, 4 Jan 2025 23:42:17 +0800 Subject: [PATCH 1578/2892] vfio/pci: introduce config_offset field in VFIOConfigMirrorQuirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device may only expose a specific portion of PCI config space through a region in a BAR, such behavior is seen in igd GGC and BDSM mirrors in BAR0. To handle these, config_offset is introduced to allow mirroring arbitrary region in PCI config space. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20250104154219.7209-3-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 2 ++ hw/vfio/pci-quirks.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 2258fdda5f..fbe43b0a79 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -162,6 +162,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, (void)vfio_region_read(&vdev->bars[mirror->bar].region, addr + mirror->offset, size); + addr += mirror->config_offset; data = vfio_pci_read_config(&vdev->pdev, addr, size); trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name, memory_region_name(mirror->mem), @@ -175,6 +176,7 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + addr += mirror->config_offset; vfio_pci_write_config(&vdev->pdev, addr, data, size); trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name, memory_region_name(mirror->mem), diff --git a/hw/vfio/pci-quirks.h b/hw/vfio/pci-quirks.h index c0e96a01cc..d1532e379b 100644 --- a/hw/vfio/pci-quirks.h +++ b/hw/vfio/pci-quirks.h @@ -60,7 +60,8 @@ extern const MemoryRegionOps vfio_generic_window_data_quirk; */ typedef struct VFIOConfigMirrorQuirk { struct VFIOPCIDevice *vdev; - uint32_t offset; + uint32_t offset; /* Offset in BAR */ + uint32_t config_offset; /* Offset in PCI config space */ uint8_t bar; MemoryRegion *mem; uint8_t data[]; From e45891e0b17884b7170c441eab3a5cdd856946f7 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Sat, 4 Jan 2025 23:42:18 +0800 Subject: [PATCH 1579/2892] vfio/igd: use VFIOConfigMirrorQuirk for mirrored registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the introduction of config_offset field, VFIOConfigMirrorQuirk can now be used for those mirrored register in igd bar0. This eliminates the need for the macro intoduced in 1a2623b5c9e7 ("vfio/igd: add macro for declaring mirrored registers"). Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/r/20250104154219.7209-4-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 125 +++++++++++++------------------------------------- 1 file changed, 31 insertions(+), 94 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index b5303ea565..b1a237edd6 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -18,6 +18,7 @@ #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" #include "pci.h" +#include "pci-quirks.h" #include "trace.h" /* @@ -422,83 +423,13 @@ static const MemoryRegionOps vfio_igd_index_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint64_t vfio_igd_pci_config_read(VFIOPCIDevice *vdev, uint64_t offset, - unsigned size) -{ - switch (size) { - case 1: - return pci_get_byte(vdev->pdev.config + offset); - case 2: - return pci_get_word(vdev->pdev.config + offset); - case 4: - return pci_get_long(vdev->pdev.config + offset); - case 8: - return pci_get_quad(vdev->pdev.config + offset); - default: - hw_error("igd: unsupported pci config read at %"PRIx64", size %u", - offset, size); - break; - } - - return 0; -} - -static void vfio_igd_pci_config_write(VFIOPCIDevice *vdev, uint64_t offset, - uint64_t data, unsigned size) -{ - switch (size) { - case 1: - pci_set_byte(vdev->pdev.config + offset, data); - break; - case 2: - pci_set_word(vdev->pdev.config + offset, data); - break; - case 4: - pci_set_long(vdev->pdev.config + offset, data); - break; - case 8: - pci_set_quad(vdev->pdev.config + offset, data); - break; - default: - hw_error("igd: unsupported pci config write at %"PRIx64", size %u", - offset, size); - break; - } -} - -#define VFIO_IGD_QUIRK_MIRROR_REG(reg, name) \ -static uint64_t vfio_igd_quirk_read_##name(void *opaque, \ - hwaddr addr, unsigned size) \ -{ \ - VFIOPCIDevice *vdev = opaque; \ - \ - return vfio_igd_pci_config_read(vdev, reg + addr, size); \ -} \ - \ -static void vfio_igd_quirk_write_##name(void *opaque, hwaddr addr, \ - uint64_t data, unsigned size) \ -{ \ - VFIOPCIDevice *vdev = opaque; \ - \ - vfio_igd_pci_config_write(vdev, reg + addr, data, size); \ -} \ - \ -static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \ - .read = vfio_igd_quirk_read_##name, \ - .write = vfio_igd_quirk_write_##name, \ - .endianness = DEVICE_LITTLE_ENDIAN, \ -}; - -VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc) -VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM, bdsm) -VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm64) - #define IGD_GGC_MMIO_OFFSET 0x108040 #define IGD_BDSM_MMIO_OFFSET 0x1080C0 void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) { - VFIOQuirk *quirk; + VFIOQuirk *ggc_quirk, *bdsm_quirk; + VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror; int gen; /* @@ -522,33 +453,39 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = vfio_quirk_alloc(2); - quirk->data = vdev; + ggc_quirk = vfio_quirk_alloc(1); + ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror)); + ggc_mirror->mem = ggc_quirk->mem; + ggc_mirror->vdev = vdev; + ggc_mirror->bar = nr; + ggc_mirror->offset = IGD_GGC_MMIO_OFFSET; + ggc_mirror->config_offset = IGD_GMCH; - memory_region_init_io(&quirk->mem[0], OBJECT(vdev), - &vfio_igd_quirk_mirror_ggc, vdev, + memory_region_init_io(ggc_mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, ggc_mirror, "vfio-igd-ggc-quirk", 2); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_GGC_MMIO_OFFSET, &quirk->mem[0], + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + ggc_mirror->offset, ggc_mirror->mem, 1); - if (gen < 11) { - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), - &vfio_igd_quirk_mirror_bdsm, vdev, - "vfio-igd-bdsm-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_BDSM_MMIO_OFFSET, - &quirk->mem[1], 1); - } else { - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), - &vfio_igd_quirk_mirror_bdsm64, vdev, - "vfio-igd-bdsm-quirk", 8); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_BDSM_MMIO_OFFSET, - &quirk->mem[1], 1); - } + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next); - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + bdsm_quirk = vfio_quirk_alloc(1); + bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror)); + bdsm_mirror->mem = bdsm_quirk->mem; + bdsm_mirror->vdev = vdev; + bdsm_mirror->bar = nr; + bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET; + bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11; + + memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, bdsm_mirror, + "vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + bdsm_mirror->offset, bdsm_mirror->mem, + 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); } void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) From 7b3d5b84cbd742356a1afc6b0fa489d0663f235d Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 16 Jan 2025 18:23:07 +0800 Subject: [PATCH 1580/2892] vfio/iommufd: Fix SIGSEV in iommufd_cdev_attach() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When iommufd_cdev_ram_block_discard_disable() fails for whatever reason, errp should be set or else SIGSEV is triggered in vfio_realize() when error_prepend() is called. By this chance, use the same error message for both legacy and iommufd backend. Fixes: 5ee3dc7af785 ("vfio/iommufd: Implement the iommufd backend") Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Link: https://lore.kernel.org/r/20250116102307.260849-1-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 3490a8f1eb..df61edffc0 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -515,8 +515,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, } else { ret = iommufd_cdev_ram_block_discard_disable(true); if (ret) { - error_setg(errp, - "Cannot set discarding of RAM broken (%d)", ret); + error_setg_errno(errp, -ret, + "Cannot set discarding of RAM broken"); goto err_discard_disable; } goto found_container; @@ -544,6 +544,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, ret = iommufd_cdev_ram_block_discard_disable(true); if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); goto err_discard_disable; } From 96b339cc4cc7e3d2da0e615e1851d3250b526ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:29 +0100 Subject: [PATCH 1581/2892] util/error: Introduce warn_report_err_once() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Depending on the configuration of the host and VM, a passthrough device may generate recurring DMA mapping errors at runtime. In such cases, reporting the issue once is sufficient. We have already the warn/error_report_once() routines taking a format and arguments. Using the same design pattern, add a new warning variant taking an 'Error *' parameter. Cc: Markus Armbruster Reviewed-by: Alex Williamson Reviewed-by: Markus Armbruster Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-2-clg@redhat.com Signed-off-by: Cédric Le Goater --- include/qapi/error.h | 12 ++++++++++++ util/error.c | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/qapi/error.h b/include/qapi/error.h index 71f8fb2c50..f5fe216262 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -466,6 +466,18 @@ void warn_reportf_err(Error *err, const char *fmt, ...) void error_reportf_err(Error *err, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +/* + * Similar to warn_report_err(), except it prints the message just once. + * Return true when it prints, false otherwise. + */ +bool warn_report_err_once_cond(bool *printed, Error *err); + +#define warn_report_err_once(err) \ + ({ \ + static bool print_once_; \ + warn_report_err_once_cond(&print_once_, err); \ + }) + /* * Just like error_setg(), except you get to specify the error class. * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is diff --git a/util/error.c b/util/error.c index e5e247209a..673011b89e 100644 --- a/util/error.c +++ b/util/error.c @@ -247,6 +247,17 @@ void warn_report_err(Error *err) error_free(err); } +bool warn_report_err_once_cond(bool *printed, Error *err) +{ + if (*printed) { + error_free(err); + return false; + } + *printed = true; + warn_report_err(err); + return true; +} + void error_reportf_err(Error *err, const char *fmt, ...) { va_list ap; From cbfbedd6173cf8ff3f374ec6b8e1a17e9fa75872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:30 +0100 Subject: [PATCH 1582/2892] vfio/pci: Replace "iommu_device" by "vIOMMU" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to be consistent with other reported errors related to vIOMMU devices. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-3-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9a55e7b773..89d900e9cf 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3116,7 +3116,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (!vbasedev->mdev && !pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { - error_prepend(errp, "Failed to set iommu_device: "); + error_prepend(errp, "Failed to set vIOMMU: "); goto out_teardown; } From cdc6f2e0c9fcef2027f4fc5f25ddd15e36283e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:31 +0100 Subject: [PATCH 1583/2892] vfio: Rephrase comment in vfio_listener_region_add() error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rephrase a bit the ending comment about how errors are handled depending on the phase in which vfio_listener_region_add() is called. Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-4-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f7499a9b74..62af1216fc 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -683,12 +683,13 @@ fail: error_reportf_err(err, "PCI p2p may not work: "); return; } - /* - * On the initfn path, store the first error in the container so we - * can gracefully fail. Runtime, there's not much we can do other - * than throw a hardware error. - */ + if (!bcontainer->initialized) { + /* + * At machine init time or when the device is attached to the + * VM, store the first error in the container so we can + * gracefully fail the device realize routine. + */ if (!bcontainer->error) { error_propagate_prepend(&bcontainer->error, err, "Region %s: ", @@ -697,6 +698,10 @@ fail: error_free(err); } } else { + /* + * At runtime, there's not much we can do other than throw a + * hardware error. + */ error_report_err(err); hw_error("vfio: DMA mapping failed, unable to continue"); } From 80936cf7f38e55f41bf5b482ee04f055c76a0df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:32 +0100 Subject: [PATCH 1584/2892] vfio: Introduce vfio_get_vfio_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper will be useful in the listener handlers to extract the VFIO device from a memory region using memory_region_owner(). At the moment, we only care for PCI passthrough devices. If the need arises, we will add more. Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-5-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/helpers.c | 10 ++++++++++ include/hw/vfio/vfio-common.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 913796f437..4b255d4f3a 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -23,6 +23,7 @@ #include #include "hw/vfio/vfio-common.h" +#include "hw/vfio/pci.h" #include "hw/hw.h" #include "trace.h" #include "qapi/error.h" @@ -728,3 +729,12 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); } + +VFIODevice *vfio_get_vfio_device(Object *obj) +{ + if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) { + return &VFIO_PCI(obj)->vbasedev; + } else { + return NULL; + } +} diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0c60be5b15..ac35136a11 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -252,6 +252,7 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp); bool vfio_attach_device(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); void vfio_detach_device(VFIODevice *vbasedev); +VFIODevice *vfio_get_vfio_device(Object *obj); int vfio_kvm_device_add_fd(int fd, Error **errp); int vfio_kvm_device_del_fd(int fd, Error **errp); From aaec00422ba6538ca3e6bfa612bbae155ec15c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:33 +0100 Subject: [PATCH 1585/2892] vfio: Improve error reporting when MMIO region mapping fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the IOMMU address space width is smaller than the physical address width, a MMIO region of a device can fail to map because the region is outside the supported IOVA ranges of the VM. In this case, PCI peer-to-peer transactions on BARs are not supported. This can occur with the 39-bit IOMMU address space width, as can be the case on some Intel consumer processors, or when using a vIOMMU device with default settings. The current error message is unclear, improve it and also change the error report to a warning because it is a non fatal condition for the VM. To prevent excessive log messages, restrict these recurring DMA mapping errors to a single warning at runtime. Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-6-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 62af1216fc..4fca4c6166 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -555,6 +555,18 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer, return true; } +static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) +{ + /* + * MMIO region mapping failures are not fatal but in this case PCI + * peer-to-peer transactions are broken. + */ + if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + error_append_hint(errp, "%s: PCI peer-to-peer transactions " + "on BARs are not supported.\n", vbasedev->name); + } +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -670,7 +682,10 @@ static void vfio_listener_region_add(MemoryListener *listener, strerror(-ret)); if (memory_region_is_ram_device(section->mr)) { /* Allow unexpected mappings not to be fatal for RAM devices */ - error_report_err(err); + VFIODevice *vbasedev = + vfio_get_vfio_device(memory_region_owner(section->mr)); + vfio_device_error_append(vbasedev, &err); + warn_report_err_once(err); return; } goto fail; From 889695f8f3c10647cc27a3788b4a3f1d0192926c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:34 +0100 Subject: [PATCH 1586/2892] vfio: Remove reports of DMA mapping errors in backends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the mapping handlers of the IOMMU backends, VFIO IOMMU Type 1 aka. legacy and IOMMUFD, return an errno and also report an error. This can lead to excessive log messages at runtime for recurring DMA mapping errors. Since these errors are already reported by the callers in the vfio_container_dma_un/map() routines, simply remove them and allow the callers to handle the reporting. The mapping handler of the IOMMUFD backend has a comment suggesting MMIO region mapping failures return EFAULT. I am not sure this is entirely true, so keep the EFAULT case until the conditions are clarified. Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-7-clg@redhat.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 3 --- hw/vfio/container.c | 2 -- 2 files changed, 5 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index 7b4fc8ec46..d57da44755 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -167,8 +167,6 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, /* TODO: Not support mapping hardware PCI BAR region for now. */ if (errno == EFAULT) { warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?"); - } else { - error_report("IOMMU_IOAS_MAP failed: %m"); } } return ret; @@ -203,7 +201,6 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, if (ret) { ret = -errno; - error_report("IOMMU_IOAS_UNMAP failed: %m"); } return ret; } diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 4ebb526808..7c57bdd27b 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -159,7 +159,6 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, unmap.size -= 1ULL << ctz64(bcontainer->pgsizes); continue; } - error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); return -errno; } @@ -204,7 +203,6 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, return 0; } - error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); return -errno; } From be7d8579eb5758c0edf81eb068017a56471a77e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 6 Feb 2025 14:14:35 +0100 Subject: [PATCH 1587/2892] vfio: Remove superfluous error report in vfio_listener_region_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For pseries machines, commit 567b5b309abe ("vfio/pci: Relax DMA map errors for MMIO regions") introduced 2 error reports to notify the user of MMIO mapping errors. Consolidate both code paths under one. Cc: Harsh Prateek Bora Cc: Shivaprasad G Bhat Reviewed-by: Harsh Prateek Bora Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250206131438.1505542-8-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 4fca4c6166..abbdc56b6d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -594,8 +594,9 @@ static void vfio_listener_region_add(MemoryListener *listener, return; } + /* PPC64/pseries machine only */ if (!vfio_container_add_section_window(bcontainer, section, &err)) { - goto fail; + goto mmio_dma_error; } memory_region_ref(section->mr); @@ -680,6 +681,7 @@ static void vfio_listener_region_add(MemoryListener *listener, "0x%"HWADDR_PRIx", %p) = %d (%s)", bcontainer, iova, int128_get64(llsize), vaddr, ret, strerror(-ret)); + mmio_dma_error: if (memory_region_is_ram_device(section->mr)) { /* Allow unexpected mappings not to be fatal for RAM devices */ VFIODevice *vbasedev = @@ -694,11 +696,6 @@ static void vfio_listener_region_add(MemoryListener *listener, return; fail: - if (memory_region_is_ram_device(section->mr)) { - error_reportf_err(err, "PCI p2p may not work: "); - return; - } - if (!bcontainer->initialized) { /* * At machine init time or when the device is attached to the @@ -806,6 +803,7 @@ static void vfio_listener_region_del(MemoryListener *listener, memory_region_unref(section->mr); + /* PPC64/pseries machine only */ vfio_container_del_section_window(bcontainer, section); } From 7ad6a567570ed8cd829c4f05a006e2ba2c86fa4a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:06 +0000 Subject: [PATCH 1588/2892] fpu: Add float_class_denormal Currently in softfloat we canonicalize input denormals and so the code that implements floating point operations does not need to care whether the input value was originally normal or denormal. However, both x86 and Arm FEAT_AFP require that an exception flag is set if: * an input is denormal * that input is not squashed to zero * that input is actually used in the calculation (e.g. we did not find the other input was a NaN) So we need to track that the input was a non-squashed denormal. To do this we add a new value to the FloatClass enum. In this commit we add the value and adjust the code everywhere that looks at FloatClass values so that the new float_class_denormal behaves identically to float_class_normal. We will add the code that does the "raise a new float exception flag if an input was an unsquashed denormal and we used it" in a subsequent commit. There should be no behavioural change in this commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 40 ++++++++++++++++++++++++--------------- fpu/softfloat.c | 32 ++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 73621f4a97..8621cb8718 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -204,7 +204,7 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status, frac_clear(p); } else { int shift = frac_normalize(p); - p->cls = float_class_normal; + p->cls = float_class_denormal; p->exp = fmt->frac_shift - fmt->exp_bias - shift + !fmt->m68k_denormal; } @@ -395,7 +395,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, static void partsN(uncanon)(FloatPartsN *p, float_status *s, const FloatFmt *fmt) { - if (likely(p->cls == float_class_normal)) { + if (likely(is_anynorm(p->cls))) { parts_uncanon_normal(p, s, fmt); } else { switch (p->cls) { @@ -435,7 +435,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, if (a->sign != b_sign) { /* Subtraction */ - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { if (parts_sub_normal(a, b)) { return a; } @@ -468,7 +468,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, } } else { /* Addition */ - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { parts_add_normal(a, b); return a; } @@ -488,12 +488,12 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, } if (b->cls == float_class_zero) { - g_assert(a->cls == float_class_normal); + g_assert(is_anynorm(a->cls)); return a; } g_assert(a->cls == float_class_zero); - g_assert(b->cls == float_class_normal); + g_assert(is_anynorm(b->cls)); return_b: b->sign = b_sign; return b; @@ -513,7 +513,7 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); bool sign = a->sign ^ b->sign; - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { FloatPartsW tmp; frac_mulw(&tmp, a, b); @@ -596,7 +596,7 @@ static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, a->sign ^= 1; } - if (unlikely(ab_mask != float_cmask_normal)) { + if (unlikely(!cmask_is_only_normals(ab_mask))) { if (unlikely(ab_mask == float_cmask_infzero)) { float_raise(float_flag_invalid | float_flag_invalid_imz, s); goto d_nan; @@ -611,7 +611,7 @@ static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, } g_assert(ab_mask & float_cmask_zero); - if (c->cls == float_class_normal) { + if (is_anynorm(c->cls)) { *a = *c; goto return_normal; } @@ -692,7 +692,7 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); bool sign = a->sign ^ b->sign; - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { a->sign = sign; a->exp -= b->exp + frac_div(a, b); return a; @@ -750,7 +750,7 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b, { int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { frac_modrem(a, b, mod_quot); return a; } @@ -800,6 +800,8 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status, if (unlikely(a->cls != float_class_normal)) { switch (a->cls) { + case float_class_denormal: + break; case float_class_snan: case float_class_qnan: parts_return_nan(a, status); @@ -1130,6 +1132,7 @@ static void partsN(round_to_int)(FloatPartsN *a, FloatRoundMode rmode, case float_class_inf: break; case float_class_normal: + case float_class_denormal: if (parts_round_to_int_normal(a, rmode, scale, fmt->frac_size)) { float_raise(float_flag_inexact, s); } @@ -1174,6 +1177,7 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode, return 0; case float_class_normal: + case float_class_denormal: /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ if (parts_round_to_int_normal(p, rmode, scale, N - 2)) { flags = float_flag_inexact; @@ -1241,6 +1245,7 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode, return 0; case float_class_normal: + case float_class_denormal: /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ if (parts_round_to_int_normal(p, rmode, scale, N - 2)) { flags = float_flag_inexact; @@ -1304,6 +1309,7 @@ static int64_t partsN(float_to_sint_modulo)(FloatPartsN *p, return 0; case float_class_normal: + case float_class_denormal: /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ if (parts_round_to_int_normal(p, rmode, 0, N - 2)) { flags = float_flag_inexact; @@ -1452,9 +1458,10 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, a_exp = a->exp; b_exp = b->exp; - if (unlikely(ab_mask != float_cmask_normal)) { + if (unlikely(!cmask_is_only_normals(ab_mask))) { switch (a->cls) { case float_class_normal: + case float_class_denormal: break; case float_class_inf: a_exp = INT16_MAX; @@ -1467,6 +1474,7 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, } switch (b->cls) { case float_class_normal: + case float_class_denormal: break; case float_class_inf: b_exp = INT16_MAX; @@ -1513,7 +1521,7 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, { int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { FloatRelation cmp; if (a->sign != b->sign) { @@ -1581,6 +1589,7 @@ static void partsN(scalbn)(FloatPartsN *a, int n, float_status *s) case float_class_inf: break; case float_class_normal: + case float_class_denormal: a->exp += MIN(MAX(n, -0x10000), 0x10000); break; default: @@ -1599,6 +1608,8 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt) if (unlikely(a->cls != float_class_normal)) { switch (a->cls) { + case float_class_denormal: + break; case float_class_snan: case float_class_qnan: parts_return_nan(a, s); @@ -1615,9 +1626,8 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt) } return; default: - break; + g_assert_not_reached(); } - g_assert_not_reached(); } if (unlikely(a->sign)) { goto d_nan; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 26f3a8dc87..03a604c38e 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -404,12 +404,16 @@ float64_gen2(float64 xa, float64 xb, float_status *s, /* * Classify a floating point number. Everything above float_class_qnan * is a NaN so cls >= float_class_qnan is any NaN. + * + * Note that we canonicalize denormals, so most code should treat + * class_normal and class_denormal identically. */ typedef enum __attribute__ ((__packed__)) { float_class_unclassified, float_class_zero, float_class_normal, + float_class_denormal, /* input was a non-squashed denormal */ float_class_inf, float_class_qnan, /* all NaNs from here */ float_class_snan, @@ -420,12 +424,14 @@ typedef enum __attribute__ ((__packed__)) { enum { float_cmask_zero = float_cmask(float_class_zero), float_cmask_normal = float_cmask(float_class_normal), + float_cmask_denormal = float_cmask(float_class_denormal), float_cmask_inf = float_cmask(float_class_inf), float_cmask_qnan = float_cmask(float_class_qnan), float_cmask_snan = float_cmask(float_class_snan), float_cmask_infzero = float_cmask_zero | float_cmask_inf, float_cmask_anynan = float_cmask_qnan | float_cmask_snan, + float_cmask_anynorm = float_cmask_normal | float_cmask_denormal, }; /* Flags for parts_minmax. */ @@ -459,6 +465,20 @@ static inline __attribute__((unused)) bool is_qnan(FloatClass c) return c == float_class_qnan; } +/* + * Return true if the float_cmask has only normals in it + * (including input denormals that were canonicalized) + */ +static inline bool cmask_is_only_normals(int cmask) +{ + return !(cmask & ~float_cmask_anynorm); +} + +static inline bool is_anynorm(FloatClass c) +{ + return float_cmask(c) & float_cmask_anynorm; +} + /* * Structure holding all of the decomposed parts of a float. * The exponent is unbiased and the fraction is normalized. @@ -1729,6 +1749,7 @@ static float64 float64r32_round_pack_canonical(FloatParts64 *p, */ switch (p->cls) { case float_class_normal: + case float_class_denormal: if (unlikely(p->exp == 0)) { /* * The result is denormal for float32, but can be represented @@ -1817,6 +1838,7 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p, switch (p->cls) { case float_class_normal: + case float_class_denormal: if (s->floatx80_rounding_precision == floatx80_precision_x) { parts_uncanon_normal(p, s, fmt); frac = p->frac_hi; @@ -2697,6 +2719,7 @@ static void parts_float_to_ahp(FloatParts64 *a, float_status *s) break; case float_class_normal: + case float_class_denormal: case float_class_zero: break; @@ -2729,7 +2752,7 @@ static void parts_float_to_float_narrow(FloatParts64 *a, FloatParts128 *b, a->sign = b->sign; a->exp = b->exp; - if (a->cls == float_class_normal) { + if (is_anynorm(a->cls)) { frac_truncjam(a, b); } else if (is_nan(a->cls)) { /* Discard the low bits of the NaN. */ @@ -3218,6 +3241,7 @@ static Int128 float128_to_int128_scalbn(float128 a, FloatRoundMode rmode, return int128_zero(); case float_class_normal: + case float_class_denormal: if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) { flags = float_flag_inexact; } @@ -3645,6 +3669,7 @@ static Int128 float128_to_uint128_scalbn(float128 a, FloatRoundMode rmode, return int128_zero(); case float_class_normal: + case float_class_denormal: if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) { flags = float_flag_inexact; if (p.cls == float_class_zero) { @@ -5231,6 +5256,8 @@ float32 float32_exp2(float32 a, float_status *status) float32_unpack_canonical(&xp, a, status); if (unlikely(xp.cls != float_class_normal)) { switch (xp.cls) { + case float_class_denormal: + break; case float_class_snan: case float_class_qnan: parts_return_nan(&xp, status); @@ -5240,9 +5267,8 @@ float32 float32_exp2(float32 a, float_status *status) case float_class_zero: return float32_one; default: - break; + g_assert_not_reached(); } - g_assert_not_reached(); } float_raise(float_flag_inexact, status); From 029a2083a293f55b7b3d56e16b1546c882d9299c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:07 +0000 Subject: [PATCH 1589/2892] fpu: Implement float_flag_input_denormal_used For the x86 and the Arm FEAT_AFP semantics, we need to be able to tell the target code that the FPU operation has used an input denormal. Implement this; when it happens we set the new float_flag_denormal_input_used. Note that we only set this when an input denormal is actually used by the operation: if the operation results in Invalid Operation or Divide By Zero or the result is a NaN because some other input was a NaN then we never needed to look at the input denormal and do not set denormal_input_used. We mostly do not need to adjust the hardfloat codepaths to deal with this flag, because almost all hardfloat operations are already gated on the input not being a denormal, and will fall back to softfloat for a denormal input. The only exception is the comparison operations, where we need to add the check for input denormals, which must now fall back to softfloat where they did not before. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 68 ++++++++++++++++++++++++++++++++++- fpu/softfloat.c | 38 +++++++++++++++++--- include/fpu/softfloat-types.h | 7 ++++ 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 8621cb8718..0122b35008 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -433,6 +433,15 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, bool b_sign = b->sign ^ subtract; int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); + /* + * For addition and subtraction, we will consume an + * input denormal unless the other input is a NaN. + */ + if ((ab_mask & (float_cmask_denormal | float_cmask_anynan)) == + float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (a->sign != b_sign) { /* Subtraction */ if (likely(cmask_is_only_normals(ab_mask))) { @@ -516,6 +525,10 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, if (likely(cmask_is_only_normals(ab_mask))) { FloatPartsW tmp; + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + frac_mulw(&tmp, a, b); frac_truncjam(a, &tmp); @@ -541,6 +554,10 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, } /* Multiply by 0 or Inf */ + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (ab_mask & float_cmask_inf) { a->cls = float_class_inf; a->sign = sign; @@ -664,6 +681,16 @@ static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, if (flags & float_muladd_negate_result) { a->sign ^= 1; } + + /* + * All result types except for "return the default NaN + * because this is an Invalid Operation" go through here; + * this matches the set of cases where we consumed a + * denormal input. + */ + if (abc_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } return a; return_sub_zero: @@ -693,6 +720,9 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, bool sign = a->sign ^ b->sign; if (likely(cmask_is_only_normals(ab_mask))) { + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } a->sign = sign; a->exp -= b->exp + frac_div(a, b); return a; @@ -713,6 +743,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, return parts_pick_nan(a, b, s); } + if ((ab_mask & float_cmask_denormal) && b->cls != float_class_zero) { + float_raise(float_flag_input_denormal_used, s); + } + a->sign = sign; /* Inf / X */ @@ -751,6 +785,9 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b, int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); if (likely(cmask_is_only_normals(ab_mask))) { + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } frac_modrem(a, b, mod_quot); return a; } @@ -771,6 +808,10 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b, return a; } + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + /* N % Inf; 0 % N */ g_assert(b->cls == float_class_inf || a->cls == float_class_zero); return a; @@ -801,6 +842,10 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status, if (unlikely(a->cls != float_class_normal)) { switch (a->cls) { case float_class_denormal: + if (!a->sign) { + /* -ve denormal will be InvalidOperation */ + float_raise(float_flag_input_denormal_used, status); + } break; case float_class_snan: case float_class_qnan: @@ -1431,6 +1476,9 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, if ((flags & (minmax_isnum | minmax_isnumber)) && !(ab_mask & float_cmask_snan) && (ab_mask & ~float_cmask_qnan)) { + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } return is_nan(a->cls) ? b : a; } @@ -1455,6 +1503,10 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, return parts_pick_nan(a, b, s); } + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + a_exp = a->exp; b_exp = b->exp; @@ -1524,6 +1576,10 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, if (likely(cmask_is_only_normals(ab_mask))) { FloatRelation cmp; + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (a->sign != b->sign) { goto a_sign; } @@ -1549,6 +1605,10 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, return float_relation_unordered; } + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (ab_mask & float_cmask_zero) { if (ab_mask == float_cmask_zero) { return float_relation_equal; @@ -1588,8 +1648,10 @@ static void partsN(scalbn)(FloatPartsN *a, int n, float_status *s) case float_class_zero: case float_class_inf: break; - case float_class_normal: case float_class_denormal: + float_raise(float_flag_input_denormal_used, s); + /* fall through */ + case float_class_normal: a->exp += MIN(MAX(n, -0x10000), 0x10000); break; default: @@ -1609,6 +1671,10 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt) if (unlikely(a->cls != float_class_normal)) { switch (a->cls) { case float_class_denormal: + if (!a->sign) { + /* -ve denormal will be InvalidOperation */ + float_raise(float_flag_input_denormal_used, s); + } break; case float_class_snan: case float_class_qnan: diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 03a604c38e..f4fed9bfda 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -2718,8 +2718,10 @@ static void parts_float_to_ahp(FloatParts64 *a, float_status *s) float16_params_ahp.frac_size + 1); break; - case float_class_normal: case float_class_denormal: + float_raise(float_flag_input_denormal_used, s); + break; + case float_class_normal: case float_class_zero: break; @@ -2733,6 +2735,9 @@ static void parts64_float_to_float(FloatParts64 *a, float_status *s) if (is_nan(a->cls)) { parts_return_nan(a, s); } + if (a->cls == float_class_denormal) { + float_raise(float_flag_input_denormal_used, s); + } } static void parts128_float_to_float(FloatParts128 *a, float_status *s) @@ -2740,6 +2745,9 @@ static void parts128_float_to_float(FloatParts128 *a, float_status *s) if (is_nan(a->cls)) { parts_return_nan(a, s); } + if (a->cls == float_class_denormal) { + float_raise(float_flag_input_denormal_used, s); + } } #define parts_float_to_float(P, S) \ @@ -2752,12 +2760,21 @@ static void parts_float_to_float_narrow(FloatParts64 *a, FloatParts128 *b, a->sign = b->sign; a->exp = b->exp; - if (is_anynorm(a->cls)) { + switch (a->cls) { + case float_class_denormal: + float_raise(float_flag_input_denormal_used, s); + /* fall through */ + case float_class_normal: frac_truncjam(a, b); - } else if (is_nan(a->cls)) { + break; + case float_class_snan: + case float_class_qnan: /* Discard the low bits of the NaN. */ a->frac = b->frac_hi; parts_return_nan(a, s); + break; + default: + break; } } @@ -2772,6 +2789,9 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b, if (is_nan(a->cls)) { parts_return_nan(a, s); } + if (a->cls == float_class_denormal) { + float_raise(float_flag_input_denormal_used, s); + } } float32 float16_to_float32(float16 a, bool ieee, float_status *s) @@ -4411,7 +4431,11 @@ float32_hs_compare(float32 xa, float32 xb, float_status *s, bool is_quiet) goto soft; } - float32_input_flush2(&ua.s, &ub.s, s); + if (unlikely(float32_is_denormal(ua.s) || float32_is_denormal(ub.s))) { + /* We may need to set the input_denormal_used flag */ + goto soft; + } + if (isgreaterequal(ua.h, ub.h)) { if (isgreater(ua.h, ub.h)) { return float_relation_greater; @@ -4461,7 +4485,11 @@ float64_hs_compare(float64 xa, float64 xb, float_status *s, bool is_quiet) goto soft; } - float64_input_flush2(&ua.s, &ub.s, s); + if (unlikely(float64_is_denormal(ua.s) || float64_is_denormal(ub.s))) { + /* We may need to set the input_denormal_used flag */ + goto soft; + } + if (isgreaterequal(ua.h, ub.h)) { if (isgreater(ua.h, ub.h)) { return float_relation_greater; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 2e43d1dd9e..bba1c397bb 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -165,6 +165,13 @@ enum { float_flag_invalid_sqrt = 0x0800, /* sqrt(-x) */ float_flag_invalid_cvti = 0x1000, /* non-nan to integer */ float_flag_invalid_snan = 0x2000, /* any operand was snan */ + /* + * An input was denormal and we used it (without flushing it to zero). + * Not set if we do not actually use the denormal input (e.g. + * because some other input was a NaN, or because the operation + * wasn't actually carried out (divide-by-zero; invalid)) + */ + float_flag_input_denormal_used = 0x4000, }; /* From 28f13bccbe6a100f53519d1f32cbb78e407e2b83 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:08 +0000 Subject: [PATCH 1590/2892] fpu: allow flushing of output denormals to be after rounding Currently we handle flushing of output denormals in uncanon_normal always before we deal with rounding. This works for architectures that detect tininess before rounding, but is usually not the right place when the architecture detects tininess after rounding. For example, for x86 the SDM states that the MXCSR FTZ control bit causes outputs to be flushed to zero "when it detects a floating-point underflow condition". This means that we mustn't flush to zero if the input is such that after rounding it is no longer tiny. At least one of our guest architectures does underflow detection after rounding but flushing of denormals before rounding (MIPS MSA); this means we need to have a config knob for this that is separate from our existing tininess_before_rounding setting. Add an ftz_detection flag. For consistency with tininess_before_rounding, we make it default to "detect ftz after rounding"; this means that we need to explicitly set the flag to "detect ftz before rounding" on every existing architecture that sets flush_to_zero, so that this commit has no behaviour change. (This means more code change here but for the long term a less confusing API.) For several architectures the current behaviour is either definitely or possibly wrong; annotate those with TODO comments. These architectures are definitely wrong (and should detect ftz after rounding): * x86 * Alpha For these architectures the spec is unclear: * MIPS (for non-MSA) * RX * SH4 PA-RISC makes ftz detection IMPDEF, but we aren't setting the "tininess before rounding" setting that we ought to. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 21 +++++++++++++++------ include/fpu/softfloat-helpers.h | 11 +++++++++++ include/fpu/softfloat-types.h | 18 ++++++++++++++++++ target/alpha/cpu.c | 7 +++++++ target/arm/cpu.c | 1 + target/hppa/fpu_helper.c | 11 +++++++++++ target/i386/tcg/fpu_helper.c | 8 ++++++++ target/mips/fpu_helper.h | 6 ++++++ target/mips/msa.c | 9 +++++++++ target/ppc/cpu_init.c | 3 +++ target/rx/cpu.c | 8 ++++++++ target/sh4/cpu.c | 8 ++++++++ target/tricore/helper.c | 1 + tests/fp/fp-bench.c | 1 + 14 files changed, 107 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 0122b35008..1d09f066c5 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -334,7 +334,8 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_lo &= ~round_mask; } frac_shr(p, frac_shift); - } else if (s->flush_to_zero) { + } else if (s->flush_to_zero && + s->ftz_detection == float_ftz_before_rounding) { flags |= float_flag_output_denormal_flushed; p->cls = float_class_zero; exp = 0; @@ -381,11 +382,19 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal; frac_shr(p, frac_shift); - if (is_tiny && (flags & float_flag_inexact)) { - flags |= float_flag_underflow; - } - if (exp == 0 && frac_eqz(p)) { - p->cls = float_class_zero; + if (is_tiny) { + if (s->flush_to_zero) { + assert(s->ftz_detection == float_ftz_after_rounding); + flags |= float_flag_output_denormal_flushed; + p->cls = float_class_zero; + exp = 0; + frac_clear(p); + } else if (flags & float_flag_inexact) { + flags |= float_flag_underflow; + } + if (exp == 0 && frac_eqz(p)) { + p->cls = float_class_zero; + } } } p->exp = exp; diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 4cb30a4822..8983c2748e 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -109,6 +109,12 @@ static inline void set_flush_inputs_to_zero(bool val, float_status *status) status->flush_inputs_to_zero = val; } +static inline void set_float_ftz_detection(FloatFTZDetection d, + float_status *status) +{ + status->ftz_detection = d; +} + static inline void set_default_nan_mode(bool val, float_status *status) { status->default_nan_mode = val; @@ -183,4 +189,9 @@ static inline bool get_default_nan_mode(const float_status *status) return status->default_nan_mode; } +static inline FloatFTZDetection get_float_ftz_detection(const float_status *status) +{ + return status->ftz_detection; +} + #endif /* SOFTFLOAT_HELPERS_H */ diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index bba1c397bb..53d5eb8521 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -304,6 +304,22 @@ typedef enum __attribute__((__packed__)) { float_infzeronan_suppress_invalid = (1 << 7), } FloatInfZeroNaNRule; +/* + * When flush_to_zero is set, should we detect denormal results to + * be flushed before or after rounding? For most architectures this + * should be set to match the tininess_before_rounding setting, + * but a few architectures, e.g. MIPS MSA, detect FTZ before + * rounding but tininess after rounding. + * + * This enum is arranged so that the default if the target doesn't + * configure it matches the default for tininess_before_rounding + * (i.e. "after rounding"). + */ +typedef enum __attribute__((__packed__)) { + float_ftz_after_rounding = 0, + float_ftz_before_rounding = 1, +} FloatFTZDetection; + /* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The @@ -321,6 +337,8 @@ typedef struct float_status { bool tininess_before_rounding; /* should denormalised results go to zero and set output_denormal_flushed? */ bool flush_to_zero; + /* do we detect and flush denormal results before or after rounding? */ + FloatFTZDetection ftz_detection; /* should denormalised inputs go to zero and set input_denormal_flushed? */ bool flush_inputs_to_zero; bool default_nan_mode; diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index e1b898e575..f5dd744987 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -202,6 +202,13 @@ static void alpha_cpu_initfn(Object *obj) set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); /* Default NaN: sign bit clear, msb frac bit set */ set_float_default_nan_pattern(0b01000000, &env->fp_status); + /* + * TODO: this is incorrect. The Alpha Architecture Handbook version 4 + * section 4.7.7.11 says that we flush to zero for underflow cases, so + * this should be float_ftz_after_rounding to match the + * tininess_after_rounding (which is specified in section 4.7.5). + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); #if defined(CONFIG_USER_ONLY) env->flags = ENV_FLAG_PS_USER | ENV_FLAG_FEN; cpu_alpha_store_fpcr(env, (uint64_t)(FPCR_INVD | FPCR_DZED | FPCR_OVFD diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 32dc7c1e69..8037744300 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -185,6 +185,7 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, static void arm_set_default_fp_behaviours(float_status *s) { set_float_detect_tininess(float_tininess_before_rounding, s); + set_float_ftz_detection(float_ftz_before_rounding, s); set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 239c027ec5..8ff4b44804 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -67,6 +67,17 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); /* Default NaN: sign bit clear, msb-1 frac bit set */ set_float_default_nan_pattern(0b00100000, &env->fp_status); + /* + * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing + * enabled by FPSR.D happens before or after rounding. We pick "before" + * for consistency with tininess detection. + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); + /* + * TODO: "PA-RISC 2.0 Architecture" chapter 10 says that we should + * detect tininess before rounding, but we don't set that here so we + * get the default tininess after rounding. + */ } void cpu_hppa_loaded_fr0(CPUHPPAState *env) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index de6d0b252e..f112c6c673 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -188,6 +188,14 @@ void cpu_init_fp_statuses(CPUX86State *env) set_float_default_nan_pattern(0b11000000, &env->fp_status); set_float_default_nan_pattern(0b11000000, &env->mmx_status); set_float_default_nan_pattern(0b11000000, &env->sse_status); + /* + * TODO: x86 does flush-to-zero detection after rounding (the SDM + * section 10.2.3.3 on the FTZ bit of MXCSR says that we flush + * when we detect underflow, which x86 does after rounding). + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); + set_float_ftz_detection(float_ftz_before_rounding, &env->mmx_status); + set_float_ftz_detection(float_ftz_before_rounding, &env->sse_status); } static inline uint8_t save_exception_flags(CPUX86State *env) diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index 6ad1e466cf..08fb409390 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -84,6 +84,12 @@ static inline void fp_reset(CPUMIPSState *env) */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->active_fpu.fp_status); + /* + * TODO: the spec does't say clearly whether FTZ happens before + * or after rounding for normal FPU operations. + */ + set_float_ftz_detection(float_ftz_before_rounding, + &env->active_fpu.fp_status); } /* MSA */ diff --git a/target/mips/msa.c b/target/mips/msa.c index fc77bfc7b9..32c6acbcc5 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -48,6 +48,15 @@ void msa_reset(CPUMIPSState *env) /* tininess detected after rounding.*/ set_float_detect_tininess(float_tininess_after_rounding, &env->active_tc.msa_fp_status); + /* + * MSACSR.FS detects tiny results to flush to zero before rounding + * (per "MIPS Architecture for Programmers Volume IV-j: The MIPS64 SIMD + * Architecture Module, Revision 1.1" section 3.5.4), even though it + * detects tininess after rounding for underflow purposes (section 3.4.2 + * table 3.3). + */ + set_float_ftz_detection(float_ftz_before_rounding, + &env->active_tc.msa_fp_status); /* * According to MIPS specifications, if one of the two operands is diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 8e49051254..062a6e85fb 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7262,6 +7262,9 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) /* tininess for underflow is detected before rounding */ set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); + /* Similarly for flush-to-zero */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); + /* * PowerPC propagation rules: * 1. A if it sNaN or qNaN diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 8c50c7a1bc..37a6fdd569 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -103,6 +103,14 @@ static void rx_cpu_reset_hold(Object *obj, ResetType type) set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); /* Default NaN value: sign bit clear, set frac msb */ set_float_default_nan_pattern(0b01000000, &env->fp_status); + /* + * TODO: "RX Family RXv1 Instruction Set Architecture" is not 100% clear + * on whether flush-to-zero should happen before or after rounding, but + * section 1.3.2 says that it happens when underflow is detected, and + * implies that underflow is detected after rounding. So this may not + * be the correct setting. + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); } static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 24a22724c6..4ac693d99b 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -130,6 +130,14 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) set_default_nan_mode(1, &env->fp_status); /* sign bit clear, set all frac bits other than msb */ set_float_default_nan_pattern(0b00111111, &env->fp_status); + /* + * TODO: "SH-4 CPU Core Architecture ADCS 7182230F" doesn't say whether + * it detects tininess before or after rounding. Section 6.4 is clear + * that flush-to-zero happens when the result underflows, though, so + * either this should be "detect ftz after rounding" or else we should + * be setting "detect tininess before rounding". + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); } static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) diff --git a/target/tricore/helper.c b/target/tricore/helper.c index e8b0ec5161..9898752eb0 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -116,6 +116,7 @@ void fpu_set_state(CPUTriCoreState *env) set_flush_inputs_to_zero(1, &env->fp_status); set_flush_to_zero(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); set_default_nan_mode(1, &env->fp_status); /* Default NaN pattern: sign bit clear, frac msb set */ set_float_default_nan_pattern(0b01000000, &env->fp_status); diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index eacb39b99c..d90f542ea2 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -496,6 +496,7 @@ static void run_bench(void) set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status); set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); set_float_default_nan_pattern(0b01000000, &soft_status); + set_float_ftz_detection(float_ftz_before_rounding, &soft_status); f = bench_funcs[operation][precision]; g_assert(f); From 7ea2b119f939ec408cc3fc28cb190343d28af427 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:09 +0000 Subject: [PATCH 1591/2892] target/arm: Define FPCR AH, FIZ, NEP bits The Armv8.7 FEAT_AFP feature defines three new control bits in the FPCR: * FPCR.AH: "alternate floating point mode"; this changes floating point behaviour in a variety of ways, including: - the sign of a default NaN is 1, not 0 - if FPCR.FZ is also 1, denormals detected after rounding with an unbounded exponent has been applied are flushed to zero - FPCR.FZ does not cause denormalized inputs to be flushed to zero - miscellaneous other corner-case behaviour changes * FPCR.FIZ: flush denormalized numbers to zero on input for most instructions * FPCR.NEP: makes scalar SIMD operations merge the result with higher vector elements in one of the source registers, instead of zeroing the higher elements of the destination This commit defines the new bits in the FPCR, and allows them to be read or written when FEAT_AFP is implemented. Actual behaviour changes will be implemented in subsequent commits. Note that these are the first FPCR bits which don't appear in the AArch32 FPSCR view of the register, and which share bit positions with FPSR bits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu-features.h | 5 +++++ target/arm/cpu.h | 3 +++ target/arm/vfp_helper.c | 11 ++++++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 30302d6c5b..7bf24c506b 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -802,6 +802,11 @@ static inline bool isar_feature_aa64_hcx(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HCX) != 0; } +static inline bool isar_feature_aa64_afp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, AFP) != 0; +} + static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, TIDCP1) != 0; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c2d2d99b46..1c91b1f50f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1714,6 +1714,9 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); */ /* FPCR bits */ +#define FPCR_FIZ (1 << 0) /* Flush Inputs to Zero (FEAT_AFP) */ +#define FPCR_AH (1 << 1) /* Alternate Handling (FEAT_AFP) */ +#define FPCR_NEP (1 << 2) /* SIMD scalar ops preserve elts (FEAT_AFP) */ #define FPCR_IOE (1 << 8) /* Invalid Operation exception trap enable */ #define FPCR_DZE (1 << 9) /* Divide by Zero exception trap enable */ #define FPCR_OFE (1 << 10) /* Overflow exception trap enable */ diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 3c8f3e6588..8c79ab4fc8 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -242,6 +242,9 @@ static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) if (!cpu_isar_feature(any_fp16, cpu)) { val &= ~FPCR_FZ16; } + if (!cpu_isar_feature(aa64_afp, cpu)) { + val &= ~(FPCR_FIZ | FPCR_AH | FPCR_NEP); + } if (!cpu_isar_feature(aa64_ebf16, cpu)) { val &= ~FPCR_EBF; @@ -271,12 +274,14 @@ static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) * We don't implement trapped exception handling, so the * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) * - * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF - * and FZ16. Len, Stride and LTPSIZE we just handled. Store those bits + * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF, FZ16, + * FIZ, AH, and NEP. + * Len, Stride and LTPSIZE we just handled. Store those bits * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI * bits. */ - val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | FPCR_EBF; + val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | + FPCR_EBF | FPCR_FIZ | FPCR_AH | FPCR_NEP; env->vfp.fpcr &= ~mask; env->vfp.fpcr |= val; } From c2e60204779616daf35ade1bc5ef31f1388e9892 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:10 +0000 Subject: [PATCH 1592/2892] target/arm: Implement FPCR.FIZ handling Part of FEAT_AFP is the new control bit FPCR.FIZ. This bit affects flushing of single and double precision denormal inputs to zero for AArch64 floating point instructions. (For half-precision, the existing FPCR.FZ16 control remains the only one.) FPCR.FIZ differs from FPCR.FZ in that if we flush an input denormal only because of FPCR.FIZ then we should *not* set the cumulative exception bit FPSR.IDC. FEAT_AFP also defines that in AArch64 the existing FPCR.FZ only applies when FPCR.AH is 0. We can implement this by setting the "flush inputs to zero" state appropriately when FPCR is written, and by not reflecting the float_flag_input_denormal status flag into FPSR reads when it is the result only of FPSR.FIZ. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/vfp_helper.c | 60 ++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 8c79ab4fc8..30c170ecee 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -61,19 +61,31 @@ static inline uint32_t vfp_exceptbits_from_host(int host_bits) static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { - uint32_t i = 0; + uint32_t a32_flags = 0, a64_flags = 0; - i |= get_float_exception_flags(&env->vfp.fp_status_a32); - i |= get_float_exception_flags(&env->vfp.fp_status_a64); - i |= get_float_exception_flags(&env->vfp.standard_fp_status); + a32_flags |= get_float_exception_flags(&env->vfp.fp_status_a32); + a32_flags |= get_float_exception_flags(&env->vfp.standard_fp_status); /* FZ16 does not generate an input denormal exception. */ - i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) & ~float_flag_input_denormal_flushed); - i |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) + a32_flags |= (get_float_exception_flags(&env->vfp.standard_fp_status_f16) & ~float_flag_input_denormal_flushed); - i |= (get_float_exception_flags(&env->vfp.standard_fp_status_f16) + + a64_flags |= get_float_exception_flags(&env->vfp.fp_status_a64); + a64_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) & ~float_flag_input_denormal_flushed); - return vfp_exceptbits_from_host(i); + /* + * Flushing an input denormal *only* because FPCR.FIZ == 1 does + * not set FPSR.IDC; if FPCR.FZ is also set then this takes + * precedence and IDC is set (see the FPUnpackBase pseudocode). + * So squash it unless (FPCR.AH == 0 && FPCR.FZ == 1). + * We only do this for the a64 flags because FIZ has no effect + * on AArch32 even if it is set. + */ + if ((env->vfp.fpcr & (FPCR_FZ | FPCR_AH)) != FPCR_FZ) { + a64_flags &= ~float_flag_input_denormal_flushed; + } + return vfp_exceptbits_from_host(a32_flags | a64_flags); } static void vfp_clear_float_status_exc_flags(CPUARMState *env) @@ -91,6 +103,17 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); } +static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) +{ + /* + * Synchronize any pending exception-flag information in the + * float_status values into env->vfp.fpsr, and then clear out + * the float_status data. + */ + env->vfp.fpsr |= vfp_get_fpsr_from_host(env); + vfp_clear_float_status_exc_flags(env); +} + static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { uint64_t changed = env->vfp.fpcr; @@ -130,9 +153,18 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a32); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a32); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a64); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a64); + /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a32); + } + if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { + /* + * A64: Flush denormalized inputs to zero if FPCR.FIZ = 1, or + * both FPCR.AH = 0 and FPCR.FZ = 1. + */ + bool fitz_enabled = (val & FPCR_FIZ) || + (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ; + set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status_a64); } if (changed & FPCR_DN) { bool dnan_enabled = val & FPCR_DN; @@ -141,6 +173,14 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); } + /* + * If any bits changed that we look at in vfp_get_fpsr_from_host(), + * we must sync the float_status flags into vfp.fpsr now (under the + * old regime) before we update vfp.fpcr. + */ + if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { + vfp_sync_and_clear_float_status_exc_flags(env); + } } #else From b3d00d0a448b6ea5750da1a163aeead0287f6c05 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:11 +0000 Subject: [PATCH 1593/2892] target/arm: Adjust FP behaviour for FPCR.AH = 1 When FPCR.AH is set, various behaviours of AArch64 floating point operations which are controlled by softfloat config settings change: * tininess and ftz detection before/after rounding * NaN propagation order * result of 0 * Inf + NaN * default NaN value When the guest changes the value of the AH bit, switch these config settings on the fp_status_a64 and fp_status_f16_a64 float_status fields. This requires us to make the arm_set_default_fp_behaviours() function global, since we now need to call it from cpu.c and vfp_helper.c; we move it to vfp_helper.c so it can be next to the new arm_set_ah_fp_behaviours(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.c | 23 ---------------- target/arm/internals.h | 4 +++ target/arm/vfp_helper.c | 58 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 8037744300..f95e6cf09e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -169,29 +169,6 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node); } -/* - * Set the float_status behaviour to match the Arm defaults: - * * tininess-before-rounding - * * 2-input NaN propagation prefers SNaN over QNaN, and then - * operand A over operand B (see FPProcessNaNs() pseudocode) - * * 3-input NaN propagation prefers SNaN over QNaN, and then - * operand C over A over B (see FPProcessNaNs3() pseudocode, - * but note that for QEMU muladd is a * b + c, whereas for - * the pseudocode function the arguments are in the order c, a, b. - * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, - * and the input NaN if it is signalling - * * Default NaN has sign bit clear, msb frac bit set - */ -static void arm_set_default_fp_behaviours(float_status *s) -{ - set_float_detect_tininess(float_tininess_before_rounding, s); - set_float_ftz_detection(float_ftz_before_rounding, s); - set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); - set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); - set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); - set_float_default_nan_pattern(0b01000000, s); -} - static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) { /* Reset a single ARMCPRegInfo register */ diff --git a/target/arm/internals.h b/target/arm/internals.h index 863a84edf8..98073acc27 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1828,4 +1828,8 @@ uint64_t gt_virt_cnt_offset(CPUARMState *env); * all EL1" scope; this covers stage 1 and stage 2. */ int alle1_tlbmask(CPUARMState *env); + +/* Set the float_status behaviour to match the Arm defaults */ +void arm_set_default_fp_behaviours(float_status *s); + #endif diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 30c170ecee..ef85c186f1 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -22,15 +22,59 @@ #include "exec/helper-proto.h" #include "internals.h" #include "cpu-features.h" +#include "fpu/softfloat.h" #ifdef CONFIG_TCG #include "qemu/log.h" -#include "fpu/softfloat.h" #endif /* VFP support. We follow the convention used for VFP instructions: Single precision routines have a "s" suffix, double precision a "d" suffix. */ +/* + * Set the float_status behaviour to match the Arm defaults: + * * tininess-before-rounding + * * 2-input NaN propagation prefers SNaN over QNaN, and then + * operand A over operand B (see FPProcessNaNs() pseudocode) + * * 3-input NaN propagation prefers SNaN over QNaN, and then + * operand C over A over B (see FPProcessNaNs3() pseudocode, + * but note that for QEMU muladd is a * b + c, whereas for + * the pseudocode function the arguments are in the order c, a, b. + * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, + * and the input NaN if it is signalling + * * Default NaN has sign bit clear, msb frac bit set + */ +void arm_set_default_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_before_rounding, s); + set_float_ftz_detection(float_ftz_before_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); + set_float_default_nan_pattern(0b01000000, s); +} + +/* + * Set the float_status behaviour to match the FEAT_AFP + * FPCR.AH=1 requirements: + * * tininess-after-rounding + * * 2-input NaN propagation prefers the first NaN + * * 3-input NaN propagation prefers a over b over c + * * 0 * Inf + NaN always returns the input NaN and doesn't + * set Invalid for a QNaN + * * default NaN has sign bit set, msb frac bit set + */ +static void arm_set_ah_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_after_rounding, s); + set_float_ftz_detection(float_ftz_after_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_abc, s); + set_float_infzeronan_rule(float_infzeronan_dnan_never | + float_infzeronan_suppress_invalid, s); + set_float_default_nan_pattern(0b11000000, s); +} + #ifdef CONFIG_TCG /* Convert host exception flags to vfp form. */ @@ -173,6 +217,18 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); } + if (changed & FPCR_AH) { + bool ah_enabled = val & FPCR_AH; + + if (ah_enabled) { + /* Change behaviours for A64 FP operations */ + arm_set_ah_fp_behaviours(&env->vfp.fp_status_a64); + arm_set_ah_fp_behaviours(&env->vfp.fp_status_f16_a64); + } else { + arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); + arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); + } + } /* * If any bits changed that we look at in vfp_get_fpsr_from_host(), * we must sync the float_status flags into vfp.fpsr now (under the From a4550b7be90ccbda06b58b2c965bc87d697ec130 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:12 +0000 Subject: [PATCH 1594/2892] target/arm: Adjust exception flag handling for AH = 1 When FPCR.AH = 1, some of the cumulative exception flags in the FPSR behave slightly differently for A64 operations: * IDC is set when a denormal input is used without flushing * IXC (Inexact) is set when an output denormal is flushed to zero Update vfp_get_fpsr_from_host() to do this. Note that because half-precision operations never set IDC, we now need to add float_flag_input_denormal_used to the set we mask out of fp_status_f16_a64. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/vfp_helper.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index ef85c186f1..62b86d56b8 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -78,7 +78,7 @@ static void arm_set_ah_fp_behaviours(float_status *s) #ifdef CONFIG_TCG /* Convert host exception flags to vfp form. */ -static inline uint32_t vfp_exceptbits_from_host(int host_bits) +static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah) { uint32_t target_bits = 0; @@ -100,6 +100,16 @@ static inline uint32_t vfp_exceptbits_from_host(int host_bits) if (host_bits & float_flag_input_denormal_flushed) { target_bits |= FPSR_IDC; } + /* + * With FPCR.AH, IDC is set when an input denormal is used, + * and flushing an output denormal to zero sets both IXC and UFC. + */ + if (ah && (host_bits & float_flag_input_denormal_used)) { + target_bits |= FPSR_IDC; + } + if (ah && (host_bits & float_flag_output_denormal_flushed)) { + target_bits |= FPSR_IXC; + } return target_bits; } @@ -117,7 +127,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a64_flags |= get_float_exception_flags(&env->vfp.fp_status_a64); a64_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) - & ~float_flag_input_denormal_flushed); + & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); /* * Flushing an input denormal *only* because FPCR.FIZ == 1 does * not set FPSR.IDC; if FPCR.FZ is also set then this takes @@ -129,7 +139,8 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) if ((env->vfp.fpcr & (FPCR_FZ | FPCR_AH)) != FPCR_FZ) { a64_flags &= ~float_flag_input_denormal_flushed; } - return vfp_exceptbits_from_host(a32_flags | a64_flags); + return vfp_exceptbits_from_host(a64_flags, env->vfp.fpcr & FPCR_AH) | + vfp_exceptbits_from_host(a32_flags, false); } static void vfp_clear_float_status_exc_flags(CPUARMState *env) From 731528d35e681eb7e70207f58b8dcce419c8d567 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:13 +0000 Subject: [PATCH 1595/2892] target/arm: Add FPCR.AH to tbflags We are going to need to generate different code in some cases when FPCR.AH is 1. For example: * Floating point neg and abs must not flip the sign bit of NaNs * some insns (FRECPE, FRECPS, FRECPX, FRSQRTE, FRSQRTS, and various BFCVT and BFM bfloat16 ops) need to use a different float_status to the usual one Encode FPCR.AH into the A64 tbflags, so we can refer to it at translate time. Because we now have a bit in FPCR that affects codegen, we can't mark the AArch64 FPCR register as being SUPPRESS_TB_END any more; writes to it will now end the TB and trigger a regeneration of hflags. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.h | 1 + target/arm/helper.c | 2 +- target/arm/tcg/hflags.c | 4 ++++ target/arm/tcg/translate-a64.c | 1 + target/arm/tcg/translate.h | 2 ++ 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1c91b1f50f..a71f848cc5 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3198,6 +3198,7 @@ FIELD(TBFLAG_A64, NV2, 34, 1) FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1) /* Set if FEAT_NV2 RAM accesses are big-endian */ FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) +FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */ /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/helper.c b/target/arm/helper.c index 40bdfc851a..7d95eae997 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4848,7 +4848,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .writefn = aa64_daif_write, .resetfn = arm_cp_reset_ignore }, { .name = "FPCR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 4, - .access = PL0_RW, .type = ARM_CP_FPU | ARM_CP_SUPPRESS_TB_END, + .access = PL0_RW, .type = ARM_CP_FPU, .readfn = aa64_fpcr_read, .writefn = aa64_fpcr_write }, { .name = "FPSR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4, diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index f03977b4b0..b3a78564ec 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -404,6 +404,10 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, DP_TBFLAG_A64(flags, TCMA, aa64_va_parameter_tcma(tcr, mmu_idx)); } + if (env->vfp.fpcr & FPCR_AH) { + DP_TBFLAG_A64(flags, AH, 1); + } + return rebuild_hflags_common(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d6ac2ed418..be3f4489e5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9655,6 +9655,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2); dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20); dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); + dc->fpcr_ah = EX_TBFLAG_A64(tb_flags, AH); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 084ee63d99..1fc4fdd779 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -155,6 +155,8 @@ typedef struct DisasContext { bool nv2_mem_e20; /* True if NV2 enabled and NV2 RAM accesses are big-endian */ bool nv2_mem_be; + /* True if FPCR.AH is 1 (alternate floating point handling) */ + bool fpcr_ah; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. From 1828053990e3666a51ec08b6e23d1d70a82a68f9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:14 +0000 Subject: [PATCH 1596/2892] target/arm: Set up float_status to use for FPCR.AH=1 behaviour When FPCR.AH is 1, the behaviour of some instructions changes: * AdvSIMD BFCVT, BFCVTN, BFCVTN2, BFMLALB, BFMLALT * SVE BFCVT, BFCVTNT, BFMLALB, BFMLALT, BFMLSLB, BFMLSLT * SME BFCVT, BFCVTN, BFMLAL, BFMLSL (these are all in SME2 which QEMU does not yet implement) * FRECPE, FRECPS, FRECPX, FRSQRTE, FRSQRTS The behaviour change is: * the instructions do not update the FPSR cumulative exception flags * trapped floating point exceptions are disabled (a no-op for QEMU, which doesn't implement FPCR.{IDE,IXE,UFE,OFE,DZE,IOE}) * rounding is always round-to-nearest-even regardless of FPCR.RMode * denormalized inputs and outputs are always flushed to zero, as if FPCR.{FZ,FIZ} is {1,1} * FPCR.FZ16 is still honoured for half-precision inputs (See the Arm ARM DDI0487L.a section A1.5.9.) We can provide all these behaviours with another pair of float_status fields which we use only for these insns, when FPCR.AH is 1. These float_status fields will always have: * flush_to_zero and flush_inputs_to_zero set for the non-F16 field * rounding mode set to round-to-nearest-even and so the only FPCR fields they need to honour are DN and FZ16. In this commit we only define the new fp_status fields and give them the required behaviour when FPSR is updated. In subsequent commits we will arrange to use this new fp_status field for the instructions that should be affected by FPCR.AH in this way. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.c | 4 ++++ target/arm/cpu.h | 15 +++++++++++++++ target/arm/internals.h | 2 ++ target/arm/tcg/translate.h | 14 ++++++++++++++ target/arm/vfp_helper.c | 13 ++++++++++++- 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f95e6cf09e..1307ee34ce 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -556,6 +556,10 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16); + arm_set_ah_fp_behaviours(&env->vfp.ah_fp_status); + set_flush_to_zero(1, &env->vfp.ah_fp_status); + set_flush_inputs_to_zero(1, &env->vfp.ah_fp_status); + arm_set_ah_fp_behaviours(&env->vfp.ah_fp_status_f16); #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a71f848cc5..51fc50980b 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -640,6 +640,13 @@ typedef struct CPUArchState { * standard_fp_status : the ARM "Standard FPSCR Value" * standard_fp_status_fp16 : used for half-precision * calculations with the ARM "Standard FPSCR Value" + * ah_fp_status: used for the A64 insns which change behaviour + * when FPCR.AH == 1 (bfloat16 conversions and multiplies, + * and the reciprocal and square root estimate/step insns) + * ah_fp_status_f16: used for the A64 insns which change behaviour + * when FPCR.AH == 1 (bfloat16 conversions and multiplies, + * and the reciprocal and square root estimate/step insns); + * for half-precision * * Half-precision operations are governed by a separate * flush-to-zero control bit in FPSCR:FZ16. We pass a separate @@ -654,6 +661,12 @@ typedef struct CPUArchState { * the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than * using a fixed value for it. * + * The ah_fp_status is needed because some insns have different + * behaviour when FPCR.AH == 1: they don't update cumulative + * exception flags, they act like FPCR.{FZ,FIZ} = {1,1} and + * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, + * which means we need an ah_fp_status_f16 as well. + * * To avoid having to transfer exception bits around, we simply * say that the FPSCR cumulative exception flags are the logical * OR of the flags in the four fp statuses. This relies on the @@ -666,6 +679,8 @@ typedef struct CPUArchState { float_status fp_status_f16_a64; float_status standard_fp_status; float_status standard_fp_status_f16; + float_status ah_fp_status; + float_status ah_fp_status_f16; uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ diff --git a/target/arm/internals.h b/target/arm/internals.h index 98073acc27..b318734145 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1831,5 +1831,7 @@ int alle1_tlbmask(CPUARMState *env); /* Set the float_status behaviour to match the Arm defaults */ void arm_set_default_fp_behaviours(float_status *s); +/* Set the float_status behaviour to match Arm FPCR.AH=1 behaviour */ +void arm_set_ah_fp_behaviours(float_status *s); #endif diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 1fc4fdd779..3be3fcbe72 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -676,6 +676,8 @@ typedef enum ARMFPStatusFlavour { FPST_A64, FPST_A32_F16, FPST_A64_F16, + FPST_AH, + FPST_AH_F16, FPST_STD, FPST_STD_F16, } ARMFPStatusFlavour; @@ -696,6 +698,12 @@ typedef enum ARMFPStatusFlavour { * for AArch32 operations controlled by the FPCR where FPCR.FZ16 is to be used * FPST_A64_F16 * for AArch64 operations controlled by the FPCR where FPCR.FZ16 is to be used + * FPST_AH: + * for AArch64 operations which change behaviour when AH=1 (specifically, + * bfloat16 conversions and multiplies, and the reciprocal and square root + * estimate/step insns) + * FPST_AH_F16: + * ditto, but for half-precision operations * FPST_STD * for A32/T32 Neon operations using the "standard FPSCR value" * FPST_STD_F16 @@ -719,6 +727,12 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) case FPST_A64_F16: offset = offsetof(CPUARMState, vfp.fp_status_f16_a64); break; + case FPST_AH: + offset = offsetof(CPUARMState, vfp.ah_fp_status); + break; + case FPST_AH_F16: + offset = offsetof(CPUARMState, vfp.ah_fp_status_f16); + break; case FPST_STD: offset = offsetof(CPUARMState, vfp.standard_fp_status); break; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 62b86d56b8..5c370e263c 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -64,7 +64,7 @@ void arm_set_default_fp_behaviours(float_status *s) * set Invalid for a QNaN * * default NaN has sign bit set, msb frac bit set */ -static void arm_set_ah_fp_behaviours(float_status *s) +void arm_set_ah_fp_behaviours(float_status *s) { set_float_detect_tininess(float_tininess_after_rounding, s); set_float_ftz_detection(float_ftz_after_rounding, s); @@ -128,6 +128,11 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a64_flags |= get_float_exception_flags(&env->vfp.fp_status_a64); a64_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); + /* + * We do not merge in flags from ah_fp_status or ah_fp_status_f16, because + * they are used for insns that must not set the cumulative exception bits. + */ + /* * Flushing an input denormal *only* because FPCR.FIZ == 1 does * not set FPSR.IDC; if FPCR.FZ is also set then this takes @@ -156,6 +161,8 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); set_float_exception_flags(0, &env->vfp.standard_fp_status); set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); + set_float_exception_flags(0, &env->vfp.ah_fp_status); + set_float_exception_flags(0, &env->vfp.ah_fp_status_f16); } static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) @@ -201,9 +208,11 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); + set_flush_to_zero(ftz_enabled, &env->vfp.ah_fp_status_f16); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.ah_fp_status_f16); } if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; @@ -227,6 +236,8 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); + set_default_nan_mode(dnan_enabled, &env->vfp.ah_fp_status); + set_default_nan_mode(dnan_enabled, &env->vfp.ah_fp_status_f16); } if (changed & FPCR_AH) { bool ah_enabled = val & FPCR_AH; From a8559e0e5306c7faa76de8e571d1edf92f436df3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:15 +0000 Subject: [PATCH 1597/2892] target/arm: Use FPST_FPCR_AH for FRECPE, FRECPS, FRECPX, FRSQRTE, FRSQRTS For the instructions FRECPE, FRECPS, FRECPX, FRSQRTE, FRSQRTS, use FPST_FPCR_AH or FPST_FPCR_AH_F16 when FPCR.AH is 1, so that they get the required behaviour changes. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 119 +++++++++++++++++++++++++-------- target/arm/tcg/translate-a64.h | 13 ++++ target/arm/tcg/translate-sve.c | 30 ++++++--- 3 files changed, 127 insertions(+), 35 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index be3f4489e5..1556980c76 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -712,10 +712,10 @@ static void gen_gvec_op3_ool(DisasContext *s, bool is_q, int rd, * an out-of-line helper. */ static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, - int rm, bool is_fp16, int data, + int rm, ARMFPStatusFlavour fpsttype, int data, gen_helper_gvec_3_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_A64_F16 : FPST_A64); + TCGv_ptr fpst = fpstatus_ptr(fpsttype); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), fpst, @@ -5025,14 +5025,16 @@ typedef struct FPScalar { void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); } FPScalar; -static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +static bool do_fp3_scalar_with_fpsttype(DisasContext *s, arg_rrr_e *a, + const FPScalar *f, + ARMFPStatusFlavour fpsttype) { switch (a->esz) { case MO_64: if (fp_access_check(s)) { TCGv_i64 t0 = read_fp_dreg(s, a->rn); TCGv_i64 t1 = read_fp_dreg(s, a->rm); - f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); + f->gen_d(t0, t0, t1, fpstatus_ptr(fpsttype)); write_fp_dreg(s, a->rd, t0); } break; @@ -5040,7 +5042,7 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) if (fp_access_check(s)) { TCGv_i32 t0 = read_fp_sreg(s, a->rn); TCGv_i32 t1 = read_fp_sreg(s, a->rm); - f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); + f->gen_s(t0, t0, t1, fpstatus_ptr(fpsttype)); write_fp_sreg(s, a->rd, t0); } break; @@ -5051,7 +5053,7 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) if (fp_access_check(s)) { TCGv_i32 t0 = read_fp_hreg(s, a->rn); TCGv_i32 t1 = read_fp_hreg(s, a->rm); - f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); + f->gen_h(t0, t0, t1, fpstatus_ptr(fpsttype)); write_fp_sreg(s, a->rd, t0); } break; @@ -5061,6 +5063,18 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) return true; } +static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +{ + return do_fp3_scalar_with_fpsttype(s, a, f, + a->esz == MO_16 ? + FPST_A64_F16 : FPST_A64); +} + +static bool do_fp3_scalar_ah(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +{ + return do_fp3_scalar_with_fpsttype(s, a, f, select_ah_fpst(s, a->esz)); +} + static const FPScalar f_scalar_fadd = { gen_helper_vfp_addh, gen_helper_vfp_adds, @@ -5214,14 +5228,14 @@ static const FPScalar f_scalar_frecps = { gen_helper_recpsf_f32, gen_helper_recpsf_f64, }; -TRANS(FRECPS_s, do_fp3_scalar, a, &f_scalar_frecps) +TRANS(FRECPS_s, do_fp3_scalar_ah, a, &f_scalar_frecps) static const FPScalar f_scalar_frsqrts = { gen_helper_rsqrtsf_f16, gen_helper_rsqrtsf_f32, gen_helper_rsqrtsf_f64, }; -TRANS(FRSQRTS_s, do_fp3_scalar, a, &f_scalar_frsqrts) +TRANS(FRSQRTS_s, do_fp3_scalar_ah, a, &f_scalar_frsqrts) static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, const FPScalar *f, bool swap) @@ -5472,8 +5486,10 @@ TRANS(CMHS_s, do_cmop_d, a, TCG_COND_GEU) TRANS(CMEQ_s, do_cmop_d, a, TCG_COND_EQ) TRANS(CMTST_s, do_cmop_d, a, TCG_COND_TSTNE) -static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, - gen_helper_gvec_3_ptr * const fns[3]) +static bool do_fp3_vector_with_fpsttype(DisasContext *s, arg_qrrr_e *a, + int data, + gen_helper_gvec_3_ptr * const fns[3], + ARMFPStatusFlavour fpsttype) { MemOp esz = a->esz; int check = fp_access_check_vector_hsd(s, a->q, esz); @@ -5482,11 +5498,26 @@ static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, return check == 0; } - gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, - esz == MO_16, data, fns[esz - 1]); + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, fpsttype, + data, fns[esz - 1]); return true; } +static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fns[3]) +{ + return do_fp3_vector_with_fpsttype(s, a, data, fns, + a->esz == MO_16 ? + FPST_A64_F16 : FPST_A64); +} + +static bool do_fp3_vector_ah(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const f[3]) +{ + return do_fp3_vector_with_fpsttype(s, a, data, f, + select_ah_fpst(s, a->esz)); +} + static gen_helper_gvec_3_ptr * const f_vector_fadd[3] = { gen_helper_gvec_fadd_h, gen_helper_gvec_fadd_s, @@ -5611,14 +5642,14 @@ static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = { gen_helper_gvec_recps_s, gen_helper_gvec_recps_d, }; -TRANS(FRECPS_v, do_fp3_vector, a, 0, f_vector_frecps) +TRANS(FRECPS_v, do_fp3_vector_ah, a, 0, f_vector_frecps) static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = { gen_helper_gvec_rsqrts_h, gen_helper_gvec_rsqrts_s, gen_helper_gvec_rsqrts_d, }; -TRANS(FRSQRTS_v, do_fp3_vector, a, 0, f_vector_frsqrts) +TRANS(FRSQRTS_v, do_fp3_vector_ah, a, 0, f_vector_frsqrts) static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = { gen_helper_gvec_faddp_h, @@ -6374,7 +6405,8 @@ static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, } gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, - esz == MO_16, a->idx, fns[esz - 1]); + esz == MO_16 ? FPST_A64_F16 : FPST_A64, + a->idx, fns[esz - 1]); return true; } @@ -8383,8 +8415,9 @@ typedef struct FPScalar1 { void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_ptr); } FPScalar1; -static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, - const FPScalar1 *f, int rmode) +static bool do_fp1_scalar_with_fpsttype(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode, + ARMFPStatusFlavour fpsttype) { TCGv_i32 tcg_rmode = NULL; TCGv_ptr fpst; @@ -8396,7 +8429,7 @@ static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, return check == 0; } - fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); + fpst = fpstatus_ptr(fpsttype); if (rmode >= 0) { tcg_rmode = gen_set_rmode(rmode, fpst); } @@ -8427,6 +8460,20 @@ static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, return true; } +static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode) +{ + return do_fp1_scalar_with_fpsttype(s, a, f, rmode, + a->esz == MO_16 ? + FPST_A64_F16 : FPST_A64); +} + +static bool do_fp1_scalar_ah(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode) +{ + return do_fp1_scalar_with_fpsttype(s, a, f, rmode, select_ah_fpst(s, a->esz)); +} + static const FPScalar1 f_scalar_fsqrt = { gen_helper_vfp_sqrth, gen_helper_vfp_sqrts, @@ -8481,21 +8528,21 @@ static const FPScalar1 f_scalar_frecpe = { gen_helper_recpe_f32, gen_helper_recpe_f64, }; -TRANS(FRECPE_s, do_fp1_scalar, a, &f_scalar_frecpe, -1) +TRANS(FRECPE_s, do_fp1_scalar_ah, a, &f_scalar_frecpe, -1) static const FPScalar1 f_scalar_frecpx = { gen_helper_frecpx_f16, gen_helper_frecpx_f32, gen_helper_frecpx_f64, }; -TRANS(FRECPX_s, do_fp1_scalar, a, &f_scalar_frecpx, -1) +TRANS(FRECPX_s, do_fp1_scalar_ah, a, &f_scalar_frecpx, -1) static const FPScalar1 f_scalar_frsqrte = { gen_helper_rsqrte_f16, gen_helper_rsqrte_f32, gen_helper_rsqrte_f64, }; -TRANS(FRSQRTE_s, do_fp1_scalar, a, &f_scalar_frsqrte, -1) +TRANS(FRSQRTE_s, do_fp1_scalar_ah, a, &f_scalar_frsqrte, -1) static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) { @@ -9350,9 +9397,10 @@ TRANS_FEAT(FRINT64Z_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint64, FPROUNDING_ZERO) TRANS_FEAT(FRINT64X_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint64, -1) -static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, - int rd, int rn, int data, - gen_helper_gvec_2_ptr * const fns[3]) +static bool do_gvec_op2_fpst_with_fpsttype(DisasContext *s, MemOp esz, + bool is_q, int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3], + ARMFPStatusFlavour fpsttype) { int check = fp_access_check_vector_hsd(s, is_q, esz); TCGv_ptr fpst; @@ -9361,7 +9409,7 @@ static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, return check == 0; } - fpst = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); + fpst = fpstatus_ptr(fpsttype); tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), fpst, is_q ? 16 : 8, vec_full_reg_size(s), @@ -9369,6 +9417,23 @@ static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, return true; } +static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, + int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3]) +{ + return do_gvec_op2_fpst_with_fpsttype(s, esz, is_q, rd, rn, data, fns, + esz == MO_16 ? FPST_A64_F16 : + FPST_A64); +} + +static bool do_gvec_op2_ah_fpst(DisasContext *s, MemOp esz, bool is_q, + int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3]) +{ + return do_gvec_op2_fpst_with_fpsttype(s, esz, is_q, rd, rn, data, + fns, select_ah_fpst(s, esz)); +} + static gen_helper_gvec_2_ptr * const f_scvtf_v[] = { gen_helper_gvec_vcvt_sh, gen_helper_gvec_vcvt_sf, @@ -9478,14 +9543,14 @@ static gen_helper_gvec_2_ptr * const f_frecpe[] = { gen_helper_gvec_frecpe_s, gen_helper_gvec_frecpe_d, }; -TRANS(FRECPE_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frecpe) +TRANS(FRECPE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frecpe) static gen_helper_gvec_2_ptr * const f_frsqrte[] = { gen_helper_gvec_frsqrte_h, gen_helper_gvec_frsqrte_s, gen_helper_gvec_frsqrte_d, }; -TRANS(FRSQRTE_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frsqrte) +TRANS(FRSQRTE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frsqrte) static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) { diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 0fcf7cb63a..7d3b59ccd9 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -185,6 +185,19 @@ static inline TCGv_ptr pred_full_reg_ptr(DisasContext *s, int regno) return ret; } +/* + * Return the ARMFPStatusFlavour to use based on element size and + * whether FPCR.AH is set. + */ +static inline ARMFPStatusFlavour select_ah_fpst(DisasContext *s, MemOp esz) +{ + if (s->fpcr_ah) { + return esz == MO_16 ? FPST_AH_F16 : FPST_AH; + } else { + return esz == MO_16 ? FPST_A64_F16 : FPST_A64; + } +} + bool disas_sve(DisasContext *, uint32_t); bool disas_sme(DisasContext *, uint32_t); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index e1788330aa..c084bb58e7 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -137,11 +137,11 @@ static bool gen_gvec_fpst_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, return true; } -static bool gen_gvec_fpst_arg_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, - arg_rr_esz *a, int data) +static bool gen_gvec_fpst_ah_arg_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, + arg_rr_esz *a, int data) { return gen_gvec_fpst_zz(s, fn, a->rd, a->rn, data, - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); + select_ah_fpst(s, a->esz)); } /* Invoke an out-of-line helper on 3 Zregs. */ @@ -194,6 +194,13 @@ static bool gen_gvec_fpst_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } +static bool gen_gvec_fpst_ah_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, + arg_rrr_esz *a, int data) +{ + return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, + select_ah_fpst(s, a->esz)); +} + /* Invoke an out-of-line helper on 4 Zregs. */ static bool gen_gvec_ool_zzzz(DisasContext *s, gen_helper_gvec_4 *fn, int rd, int rn, int rm, int ra, int data) @@ -3597,13 +3604,13 @@ static gen_helper_gvec_2_ptr * const frecpe_fns[] = { NULL, gen_helper_gvec_frecpe_h, gen_helper_gvec_frecpe_s, gen_helper_gvec_frecpe_d, }; -TRANS_FEAT(FRECPE, aa64_sve, gen_gvec_fpst_arg_zz, frecpe_fns[a->esz], a, 0) +TRANS_FEAT(FRECPE, aa64_sve, gen_gvec_fpst_ah_arg_zz, frecpe_fns[a->esz], a, 0) static gen_helper_gvec_2_ptr * const frsqrte_fns[] = { NULL, gen_helper_gvec_frsqrte_h, gen_helper_gvec_frsqrte_s, gen_helper_gvec_frsqrte_d, }; -TRANS_FEAT(FRSQRTE, aa64_sve, gen_gvec_fpst_arg_zz, frsqrte_fns[a->esz], a, 0) +TRANS_FEAT(FRSQRTE, aa64_sve, gen_gvec_fpst_ah_arg_zz, frsqrte_fns[a->esz], a, 0) /* *** SVE Floating Point Compare with Zero Group @@ -3707,11 +3714,18 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) }; \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_arg_zzz, name##_fns[a->esz], a, 0) +#define DO_FP3_AH(NAME, name) \ + static gen_helper_gvec_3_ptr * const name##_fns[4] = { \ + NULL, gen_helper_gvec_##name##_h, \ + gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ + }; \ + TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_ah_arg_zzz, name##_fns[a->esz], a, 0) + DO_FP3(FADD_zzz, fadd) DO_FP3(FSUB_zzz, fsub) DO_FP3(FMUL_zzz, fmul) -DO_FP3(FRECPS, recps) -DO_FP3(FRSQRTS, rsqrts) +DO_FP3_AH(FRECPS, recps) +DO_FP3_AH(FRSQRTS, rsqrts) #undef DO_FP3 @@ -3993,7 +4007,7 @@ static gen_helper_gvec_3_ptr * const frecpx_fns[] = { gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, }; TRANS_FEAT(FRECPX, aa64_sve, gen_gvec_fpst_arg_zpz, frecpx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + a, 0, select_ah_fpst(s, a->esz)) static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { NULL, gen_helper_sve_fsqrt_h, From b6295046e6b4bc5cb070df702ddb10e3672ac6ff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:16 +0000 Subject: [PATCH 1598/2892] target/arm: Use FPST_FPCR_AH for BFCVT* insns When FPCR.AH is 1, use FPST_FPCR_AH for: * AdvSIMD BFCVT, BFCVTN, BFCVTN2 * SVE BFCVT, BFCVTNT so that they get the required behaviour changes. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 27 +++++++++++++++++++++------ target/arm/tcg/translate-sve.c | 6 ++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1556980c76..f3028463e3 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8503,7 +8503,7 @@ TRANS(FRINTX_s, do_fp1_scalar, a, &f_scalar_frintx, -1) static const FPScalar1 f_scalar_bfcvt = { .gen_s = gen_helper_bfcvt, }; -TRANS_FEAT(BFCVT_s, aa64_bf16, do_fp1_scalar, a, &f_scalar_bfcvt, -1) +TRANS_FEAT(BFCVT_s, aa64_bf16, do_fp1_scalar_ah, a, &f_scalar_bfcvt, -1) static const FPScalar1 f_scalar_frint32 = { NULL, @@ -9279,12 +9279,27 @@ static void gen_bfcvtn_hs(TCGv_i64 d, TCGv_i64 n) tcg_gen_extu_i32_i64(d, tmp); } -static ArithOneOp * const f_vector_bfcvtn[] = { - NULL, - gen_bfcvtn_hs, - NULL, +static void gen_bfcvtn_ah_hs(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_ptr fpst = fpstatus_ptr(FPST_AH); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_bfcvt_pair(tmp, n, fpst); + tcg_gen_extu_i32_i64(d, tmp); +} + +static ArithOneOp * const f_vector_bfcvtn[2][3] = { + { + NULL, + gen_bfcvtn_hs, + NULL, + }, { + NULL, + gen_bfcvtn_ah_hs, + NULL, + } }; -TRANS_FEAT(BFCVTN_v, aa64_bf16, do_2misc_narrow_vector, a, f_vector_bfcvtn) +TRANS_FEAT(BFCVTN_v, aa64_bf16, do_2misc_narrow_vector, a, + f_vector_bfcvtn[s->fpcr_ah]) static bool trans_SHLL_v(DisasContext *s, arg_qrr_e *a) { diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index c084bb58e7..be2b5528ba 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3904,7 +3904,8 @@ TRANS_FEAT(FCVT_hs, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_hs, a, 0, FPST_A64_F16) TRANS_FEAT(BFCVT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, - gen_helper_sve_bfcvt, a, 0, FPST_A64) + gen_helper_sve_bfcvt, a, 0, + s->fpcr_ah ? FPST_AH : FPST_A64) TRANS_FEAT(FCVT_dh, aa64_sve, gen_gvec_fpst_arg_zpz, gen_helper_sve_fcvt_dh, a, 0, FPST_A64) @@ -7054,7 +7055,8 @@ TRANS_FEAT(FCVTNT_ds, aa64_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_ds, a, 0, FPST_A64) TRANS_FEAT(BFCVTNT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, - gen_helper_sve_bfcvtnt, a, 0, FPST_A64) + gen_helper_sve_bfcvtnt, a, 0, + s->fpcr_ah ? FPST_AH : FPST_A64) TRANS_FEAT(FCVTLT_hs, aa64_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_hs, a, 0, FPST_A64) From 8a5a2b943e6a96487d5cf000b39aff8a4faf0821 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:17 +0000 Subject: [PATCH 1599/2892] target/arm: Use FPST_FPCR_AH for BFMLAL*, BFMLSL* insns When FPCR.AH is 1, use FPST_FPCR_AH for: * AdvSIMD BFMLALB, BFMLALT * SVE BFMLALB, BFMLALT, BFMLSLB, BFMLSLT so that they get the required behaviour changes. We do this by making gen_gvec_op4_fpst() take an ARMFPStatusFlavour rather than a bool is_fp16; existing callsites now select FPST_FPCR_F16_A64 vs FPST_FPCR_A64 themselves rather than passing in the boolean. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 20 +++++++++++++------- target/arm/tcg/translate-sve.c | 6 ++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f3028463e3..f9b62a2c4f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -754,10 +754,11 @@ static void gen_gvec_op4_env(DisasContext *s, bool is_q, int rd, int rn, * an out-of-line helper. */ static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, - int rm, int ra, bool is_fp16, int data, + int rm, int ra, ARMFPStatusFlavour fpsttype, + int data, gen_helper_gvec_4_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_A64_F16 : FPST_A64); + TCGv_ptr fpst = fpstatus_ptr(fpsttype); tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), @@ -5826,7 +5827,8 @@ static bool trans_BFMLAL_v(DisasContext *s, arg_qrrr_e *a) } if (fp_access_check(s)) { /* Q bit selects BFMLALB vs BFMLALT. */ - gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, false, a->q, + gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, + s->fpcr_ah ? FPST_AH : FPST_A64, a->q, gen_helper_gvec_bfmlal); } return true; @@ -5859,7 +5861,8 @@ static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) } gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, - a->esz == MO_16, a->rot, fn[a->esz]); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64, + a->rot, fn[a->esz]); return true; } @@ -6439,7 +6442,8 @@ static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) } gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, - esz == MO_16, (a->idx << 1) | neg, + esz == MO_16 ? FPST_A64_F16 : FPST_A64, + (a->idx << 1) | neg, fns[esz - 1]); return true; } @@ -6574,7 +6578,8 @@ static bool trans_BFMLAL_vi(DisasContext *s, arg_qrrx_e *a) } if (fp_access_check(s)) { /* Q bit selects BFMLALB vs BFMLALT. */ - gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, 0, + gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, + s->fpcr_ah ? FPST_AH : FPST_A64, (a->idx << 1) | a->q, gen_helper_gvec_bfmlal_idx); } @@ -6603,7 +6608,8 @@ static bool trans_FCMLA_vi(DisasContext *s, arg_FCMLA_vi *a) } if (fp_access_check(s)) { gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, - a->esz == MO_16, (a->idx << 2) | a->rot, fn); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64, + (a->idx << 2) | a->rot, fn); } return true; } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index be2b5528ba..e38a49dd31 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7117,7 +7117,8 @@ TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_env_arg_zzzz, static bool do_BFMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) { return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlal, - a->rd, a->rn, a->rm, a->ra, sel, FPST_A64); + a->rd, a->rn, a->rm, a->ra, sel, + s->fpcr_ah ? FPST_AH : FPST_A64); } TRANS_FEAT(BFMLALB_zzzw, aa64_sve_bf16, do_BFMLAL_zzzw, a, false) @@ -7127,7 +7128,8 @@ static bool do_BFMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) { return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlal_idx, a->rd, a->rn, a->rm, a->ra, - (a->index << 1) | sel, FPST_A64); + (a->index << 1) | sel, + s->fpcr_ah ? FPST_AH : FPST_A64); } TRANS_FEAT(BFMLALB_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, false) From 7025fa996f57f3f767a1e7fe150df21da198118b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:18 +0000 Subject: [PATCH 1600/2892] target/arm: Add FPCR.NEP to TBFLAGS For FEAT_AFP, we want to emit different code when FPCR.NEP is set, so that instead of zeroing the high elements of a vector register when we write the output of a scalar operation to it, we instead merge in those elements from one of the source registers. Since this affects the generated code, we need to put FPCR.NEP into the TBFLAGS. FPCR.NEP is treated as 0 when in streaming SVE mode and FEAT_SME_FA64 is not implemented or not enabled; we can implement this logic in rebuild_hflags_a64(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.h | 1 + target/arm/tcg/hflags.c | 9 +++++++++ target/arm/tcg/translate-a64.c | 1 + target/arm/tcg/translate.h | 2 ++ 4 files changed, 13 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 51fc50980b..24b0a5fcb9 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3214,6 +3214,7 @@ FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1) /* Set if FEAT_NV2 RAM accesses are big-endian */ FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */ +FIELD(TBFLAG_A64, NEP, 38, 1) /* FPCR.NEP */ /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index b3a78564ec..9e6a1869f9 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -407,6 +407,15 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, if (env->vfp.fpcr & FPCR_AH) { DP_TBFLAG_A64(flags, AH, 1); } + if (env->vfp.fpcr & FPCR_NEP) { + /* + * In streaming-SVE without FA64, NEP behaves as if zero; + * compare pseudocode IsMerging() + */ + if (!(EX_TBFLAG_A64(flags, PSTATE_SM) && !sme_fa64(env, el))) { + DP_TBFLAG_A64(flags, NEP, 1); + } + } return rebuild_hflags_common(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f9b62a2c4f..d94a0022e4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9742,6 +9742,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20); dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); dc->fpcr_ah = EX_TBFLAG_A64(tb_flags, AH); + dc->fpcr_nep = EX_TBFLAG_A64(tb_flags, NEP); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 3be3fcbe72..0dff00015e 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -157,6 +157,8 @@ typedef struct DisasContext { bool nv2_mem_be; /* True if FPCR.AH is 1 (alternate floating point handling) */ bool fpcr_ah; + /* True if FPCR.NEP is 1 (FEAT_AFP scalar upper-element result handling) */ + bool fpcr_nep; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. From 78dcfe20e92a928375ff18c13b868ce2778c571c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:19 +0000 Subject: [PATCH 1601/2892] target/arm: Define and use new write_fp_*reg_merging() functions For FEAT_AFP's FPCR.NEP bit, we need to programmatically change the behaviour of the writeback of the result for most SIMD scalar operations, so that instead of zeroing the upper part of the result register it merges the upper elements from one of the input registers. Provide new functions write_fp_*reg_merging() which can be used instead of the existing write_fp_*reg() functions when we want this "merge the result with one of the input registers if FPCR.NEP is enabled" handling, and use them in do_fp3_scalar_with_fpsttype(). Note that (as documented in the description of the FPCR.NEP bit) which input register to use as the merge source varies by instruction: for these 2-input scalar operations, the comparison instructions take from Rm, not Rn. We'll extend this to also provide the merging behaviour for the remaining scalar insns in subsequent commits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 117 +++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d94a0022e4..67c85db477 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -654,6 +654,68 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) write_fp_dreg(s, reg, tmp); } +/* + * Write a double result to 128 bit vector register reg, honouring FPCR.NEP: + * - if FPCR.NEP == 0, clear the high elements of reg + * - if FPCR.NEP == 1, set the high elements of reg from mergereg + * (i.e. merge the result with those high elements) + * In either case, SVE register bits above 128 are zeroed (per R_WKYLB). + */ +static void write_fp_dreg_merging(DisasContext *s, int reg, int mergereg, + TCGv_i64 v) +{ + if (!s->fpcr_nep) { + write_fp_dreg(s, reg, v); + return; + } + + /* + * Move from mergereg to reg; this sets the high elements and + * clears the bits above 128 as a side effect. + */ + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, reg), + vec_full_reg_offset(s, mergereg), + 16, vec_full_reg_size(s)); + tcg_gen_st_i64(v, tcg_env, vec_full_reg_offset(s, reg)); +} + +/* + * Write a single-prec result, but only clear the higher elements + * of the destination register if FPCR.NEP is 0; otherwise preserve them. + */ +static void write_fp_sreg_merging(DisasContext *s, int reg, int mergereg, + TCGv_i32 v) +{ + if (!s->fpcr_nep) { + write_fp_sreg(s, reg, v); + return; + } + + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, reg), + vec_full_reg_offset(s, mergereg), + 16, vec_full_reg_size(s)); + tcg_gen_st_i32(v, tcg_env, fp_reg_offset(s, reg, MO_32)); +} + +/* + * Write a half-prec result, but only clear the higher elements + * of the destination register if FPCR.NEP is 0; otherwise preserve them. + * The caller must ensure that the top 16 bits of v are zero. + */ +static void write_fp_hreg_merging(DisasContext *s, int reg, int mergereg, + TCGv_i32 v) +{ + if (!s->fpcr_nep) { + write_fp_sreg(s, reg, v); + return; + } + + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, reg), + vec_full_reg_offset(s, mergereg), + 16, vec_full_reg_size(s)); + tcg_gen_st16_i32(v, tcg_env, fp_reg_offset(s, reg, MO_16)); +} + /* Expand a 2-operand AdvSIMD vector operation using an expander function. */ static void gen_gvec_fn2(DisasContext *s, bool is_q, int rd, int rn, GVecGen2Fn *gvec_fn, int vece) @@ -5027,7 +5089,7 @@ typedef struct FPScalar { } FPScalar; static bool do_fp3_scalar_with_fpsttype(DisasContext *s, arg_rrr_e *a, - const FPScalar *f, + const FPScalar *f, int mergereg, ARMFPStatusFlavour fpsttype) { switch (a->esz) { @@ -5036,7 +5098,7 @@ static bool do_fp3_scalar_with_fpsttype(DisasContext *s, arg_rrr_e *a, TCGv_i64 t0 = read_fp_dreg(s, a->rn); TCGv_i64 t1 = read_fp_dreg(s, a->rm); f->gen_d(t0, t0, t1, fpstatus_ptr(fpsttype)); - write_fp_dreg(s, a->rd, t0); + write_fp_dreg_merging(s, a->rd, mergereg, t0); } break; case MO_32: @@ -5044,7 +5106,7 @@ static bool do_fp3_scalar_with_fpsttype(DisasContext *s, arg_rrr_e *a, TCGv_i32 t0 = read_fp_sreg(s, a->rn); TCGv_i32 t1 = read_fp_sreg(s, a->rm); f->gen_s(t0, t0, t1, fpstatus_ptr(fpsttype)); - write_fp_sreg(s, a->rd, t0); + write_fp_sreg_merging(s, a->rd, mergereg, t0); } break; case MO_16: @@ -5055,7 +5117,7 @@ static bool do_fp3_scalar_with_fpsttype(DisasContext *s, arg_rrr_e *a, TCGv_i32 t0 = read_fp_hreg(s, a->rn); TCGv_i32 t1 = read_fp_hreg(s, a->rm); f->gen_h(t0, t0, t1, fpstatus_ptr(fpsttype)); - write_fp_sreg(s, a->rd, t0); + write_fp_hreg_merging(s, a->rd, mergereg, t0); } break; default: @@ -5064,16 +5126,19 @@ static bool do_fp3_scalar_with_fpsttype(DisasContext *s, arg_rrr_e *a, return true; } -static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f, + int mergereg) { - return do_fp3_scalar_with_fpsttype(s, a, f, + return do_fp3_scalar_with_fpsttype(s, a, f, mergereg, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } -static bool do_fp3_scalar_ah(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +static bool do_fp3_scalar_ah(DisasContext *s, arg_rrr_e *a, const FPScalar *f, + int mergereg) { - return do_fp3_scalar_with_fpsttype(s, a, f, select_ah_fpst(s, a->esz)); + return do_fp3_scalar_with_fpsttype(s, a, f, mergereg, + select_ah_fpst(s, a->esz)); } static const FPScalar f_scalar_fadd = { @@ -5081,63 +5146,63 @@ static const FPScalar f_scalar_fadd = { gen_helper_vfp_adds, gen_helper_vfp_addd, }; -TRANS(FADD_s, do_fp3_scalar, a, &f_scalar_fadd) +TRANS(FADD_s, do_fp3_scalar, a, &f_scalar_fadd, a->rn) static const FPScalar f_scalar_fsub = { gen_helper_vfp_subh, gen_helper_vfp_subs, gen_helper_vfp_subd, }; -TRANS(FSUB_s, do_fp3_scalar, a, &f_scalar_fsub) +TRANS(FSUB_s, do_fp3_scalar, a, &f_scalar_fsub, a->rn) static const FPScalar f_scalar_fdiv = { gen_helper_vfp_divh, gen_helper_vfp_divs, gen_helper_vfp_divd, }; -TRANS(FDIV_s, do_fp3_scalar, a, &f_scalar_fdiv) +TRANS(FDIV_s, do_fp3_scalar, a, &f_scalar_fdiv, a->rn) static const FPScalar f_scalar_fmul = { gen_helper_vfp_mulh, gen_helper_vfp_muls, gen_helper_vfp_muld, }; -TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul) +TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul, a->rn) static const FPScalar f_scalar_fmax = { gen_helper_vfp_maxh, gen_helper_vfp_maxs, gen_helper_vfp_maxd, }; -TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax) +TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax, a->rn) static const FPScalar f_scalar_fmin = { gen_helper_vfp_minh, gen_helper_vfp_mins, gen_helper_vfp_mind, }; -TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin) +TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin, a->rn) static const FPScalar f_scalar_fmaxnm = { gen_helper_vfp_maxnumh, gen_helper_vfp_maxnums, gen_helper_vfp_maxnumd, }; -TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm) +TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm, a->rn) static const FPScalar f_scalar_fminnm = { gen_helper_vfp_minnumh, gen_helper_vfp_minnums, gen_helper_vfp_minnumd, }; -TRANS(FMINNM_s, do_fp3_scalar, a, &f_scalar_fminnm) +TRANS(FMINNM_s, do_fp3_scalar, a, &f_scalar_fminnm, a->rn) static const FPScalar f_scalar_fmulx = { gen_helper_advsimd_mulxh, gen_helper_vfp_mulxs, gen_helper_vfp_mulxd, }; -TRANS(FMULX_s, do_fp3_scalar, a, &f_scalar_fmulx) +TRANS(FMULX_s, do_fp3_scalar, a, &f_scalar_fmulx, a->rn) static void gen_fnmul_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) { @@ -5162,42 +5227,42 @@ static const FPScalar f_scalar_fnmul = { gen_fnmul_s, gen_fnmul_d, }; -TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul) +TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul, a->rn) static const FPScalar f_scalar_fcmeq = { gen_helper_advsimd_ceq_f16, gen_helper_neon_ceq_f32, gen_helper_neon_ceq_f64, }; -TRANS(FCMEQ_s, do_fp3_scalar, a, &f_scalar_fcmeq) +TRANS(FCMEQ_s, do_fp3_scalar, a, &f_scalar_fcmeq, a->rm) static const FPScalar f_scalar_fcmge = { gen_helper_advsimd_cge_f16, gen_helper_neon_cge_f32, gen_helper_neon_cge_f64, }; -TRANS(FCMGE_s, do_fp3_scalar, a, &f_scalar_fcmge) +TRANS(FCMGE_s, do_fp3_scalar, a, &f_scalar_fcmge, a->rm) static const FPScalar f_scalar_fcmgt = { gen_helper_advsimd_cgt_f16, gen_helper_neon_cgt_f32, gen_helper_neon_cgt_f64, }; -TRANS(FCMGT_s, do_fp3_scalar, a, &f_scalar_fcmgt) +TRANS(FCMGT_s, do_fp3_scalar, a, &f_scalar_fcmgt, a->rm) static const FPScalar f_scalar_facge = { gen_helper_advsimd_acge_f16, gen_helper_neon_acge_f32, gen_helper_neon_acge_f64, }; -TRANS(FACGE_s, do_fp3_scalar, a, &f_scalar_facge) +TRANS(FACGE_s, do_fp3_scalar, a, &f_scalar_facge, a->rm) static const FPScalar f_scalar_facgt = { gen_helper_advsimd_acgt_f16, gen_helper_neon_acgt_f32, gen_helper_neon_acgt_f64, }; -TRANS(FACGT_s, do_fp3_scalar, a, &f_scalar_facgt) +TRANS(FACGT_s, do_fp3_scalar, a, &f_scalar_facgt, a->rm) static void gen_fabd_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) { @@ -5222,21 +5287,21 @@ static const FPScalar f_scalar_fabd = { gen_fabd_s, gen_fabd_d, }; -TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd) +TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd, a->rn) static const FPScalar f_scalar_frecps = { gen_helper_recpsf_f16, gen_helper_recpsf_f32, gen_helper_recpsf_f64, }; -TRANS(FRECPS_s, do_fp3_scalar_ah, a, &f_scalar_frecps) +TRANS(FRECPS_s, do_fp3_scalar_ah, a, &f_scalar_frecps, a->rn) static const FPScalar f_scalar_frsqrts = { gen_helper_rsqrtsf_f16, gen_helper_rsqrtsf_f32, gen_helper_rsqrtsf_f64, }; -TRANS(FRSQRTS_s, do_fp3_scalar_ah, a, &f_scalar_frsqrts) +TRANS(FRSQRTS_s, do_fp3_scalar_ah, a, &f_scalar_frsqrts, a->rn) static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, const FPScalar *f, bool swap) From 0378199dc2fdf90550f9a819701b0c1709b34eab Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:20 +0000 Subject: [PATCH 1602/2892] target/arm: Handle FPCR.NEP for 3-input scalar operations Handle FPCR.NEP for the 3-input scalar operations which use do_fmla_scalar_idx() and do_fmadd(), by making them call the appropriate write_fp_*reg_merging() functions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 67c85db477..3f6e31d36a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6345,7 +6345,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) gen_vfp_negd(t1, t1); } gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); - write_fp_dreg(s, a->rd, t0); + write_fp_dreg_merging(s, a->rd, a->rd, t0); } break; case MO_32: @@ -6359,7 +6359,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) gen_vfp_negs(t1, t1); } gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); - write_fp_sreg(s, a->rd, t0); + write_fp_sreg_merging(s, a->rd, a->rd, t0); } break; case MO_16: @@ -6377,7 +6377,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) } gen_helper_advsimd_muladdh(t0, t1, t2, t0, fpstatus_ptr(FPST_A64_F16)); - write_fp_sreg(s, a->rd, t0); + write_fp_hreg_merging(s, a->rd, a->rd, t0); } break; default: @@ -6856,7 +6856,7 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) } fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_muladdd(ta, tn, tm, ta, fpst); - write_fp_dreg(s, a->rd, ta); + write_fp_dreg_merging(s, a->rd, a->ra, ta); } break; @@ -6874,7 +6874,7 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) } fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_muladds(ta, tn, tm, ta, fpst); - write_fp_sreg(s, a->rd, ta); + write_fp_sreg_merging(s, a->rd, a->ra, ta); } break; @@ -6895,7 +6895,7 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) } fpst = fpstatus_ptr(FPST_A64_F16); gen_helper_advsimd_muladdh(ta, tn, tm, ta, fpst); - write_fp_sreg(s, a->rd, ta); + write_fp_hreg_merging(s, a->rd, a->ra, ta); } break; From 053b39aad9a94784d5f1926e50ec7199978525a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:21 +0000 Subject: [PATCH 1603/2892] target/arm: Handle FPCR.NEP for BFCVT scalar Currently we implement BFCVT scalar via do_fp1_scalar(). This works even though BFCVT is a narrowing operation from 32 to 16 bits, because we can use write_fp_sreg() for float16. However, FPCR.NEP support requires that we use write_fp_hreg_merging() for float16 outputs, so we can't continue to borrow the non-narrowing do_fp1_scalar() function for this. Split out trans_BFCVT_s() into its own implementation that honours FPCR.NEP. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3f6e31d36a..61ea2bb9f7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8571,10 +8571,27 @@ static const FPScalar1 f_scalar_frintx = { }; TRANS(FRINTX_s, do_fp1_scalar, a, &f_scalar_frintx, -1) -static const FPScalar1 f_scalar_bfcvt = { - .gen_s = gen_helper_bfcvt, -}; -TRANS_FEAT(BFCVT_s, aa64_bf16, do_fp1_scalar_ah, a, &f_scalar_bfcvt, -1) +static bool trans_BFCVT_s(DisasContext *s, arg_rr_e *a) +{ + ARMFPStatusFlavour fpsttype = s->fpcr_ah ? FPST_AH : FPST_A64; + TCGv_i32 t32; + int check; + + if (!dc_isar_feature(aa64_bf16, s)) { + return false; + } + + check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; + } + + t32 = read_fp_sreg(s, a->rn); + gen_helper_bfcvt(t32, t32, fpstatus_ptr(fpsttype)); + write_fp_hreg_merging(s, a->rd, a->rd, t32); + return true; +} static const FPScalar1 f_scalar_frint32 = { NULL, From 3f88032ce23db57e64efb6ce24b760b201106a87 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:22 +0000 Subject: [PATCH 1604/2892] target/arm: Handle FPCR.NEP for 1-input scalar operations Handle FPCR.NEP for the 1-input scalar operations. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 61ea2bb9f7..a5f59a94ec 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8509,17 +8509,17 @@ static bool do_fp1_scalar_with_fpsttype(DisasContext *s, arg_rr_e *a, case MO_64: t64 = read_fp_dreg(s, a->rn); f->gen_d(t64, t64, fpst); - write_fp_dreg(s, a->rd, t64); + write_fp_dreg_merging(s, a->rd, a->rd, t64); break; case MO_32: t32 = read_fp_sreg(s, a->rn); f->gen_s(t32, t32, fpst); - write_fp_sreg(s, a->rd, t32); + write_fp_sreg_merging(s, a->rd, a->rd, t32); break; case MO_16: t32 = read_fp_hreg(s, a->rn); f->gen_h(t32, t32, fpst); - write_fp_sreg(s, a->rd, t32); + write_fp_hreg_merging(s, a->rd, a->rd, t32); break; default: g_assert_not_reached(); @@ -8640,7 +8640,7 @@ static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, fpst); - write_fp_dreg(s, a->rd, tcg_rd); + write_fp_dreg_merging(s, a->rd, a->rd, tcg_rd); } return true; } @@ -8653,8 +8653,8 @@ static bool trans_FCVT_s_hs(DisasContext *s, arg_rr *a) TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); - /* write_fp_sreg is OK here because top half of result is zero */ - write_fp_sreg(s, a->rd, tmp); + /* write_fp_hreg_merging is OK here because top half of result is zero */ + write_fp_hreg_merging(s, a->rd, a->rd, tmp); } return true; } @@ -8667,7 +8667,7 @@ static bool trans_FCVT_s_sd(DisasContext *s, arg_rr *a) TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, fpst); - write_fp_sreg(s, a->rd, tcg_rd); + write_fp_sreg_merging(s, a->rd, a->rd, tcg_rd); } return true; } @@ -8681,8 +8681,8 @@ static bool trans_FCVT_s_hd(DisasContext *s, arg_rr *a) TCGv_ptr fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); - /* write_fp_sreg is OK here because top half of tcg_rd is zero */ - write_fp_sreg(s, a->rd, tcg_rd); + /* write_fp_hreg_merging is OK here because top half of tcg_rd is zero */ + write_fp_hreg_merging(s, a->rd, a->rd, tcg_rd); } return true; } @@ -8696,7 +8696,7 @@ static bool trans_FCVT_s_sh(DisasContext *s, arg_rr *a) TCGv_i32 tcg_ahp = get_ahp_flag(); gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); - write_fp_sreg(s, a->rd, tcg_rd); + write_fp_sreg_merging(s, a->rd, a->rd, tcg_rd); } return true; } @@ -8710,7 +8710,7 @@ static bool trans_FCVT_s_dh(DisasContext *s, arg_rr *a) TCGv_i32 tcg_ahp = get_ahp_flag(); gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); - write_fp_dreg(s, a->rd, tcg_rd); + write_fp_dreg_merging(s, a->rd, a->rd, tcg_rd); } return true; } @@ -8958,7 +8958,9 @@ static bool do_fcvt_f(DisasContext *s, arg_fcvt *a, do_fcvt_scalar(s, a->esz | (is_signed ? MO_SIGN : 0), a->esz, tcg_int, a->shift, a->rn, rmode); - clear_vec(s, a->rd); + if (!s->fpcr_nep) { + clear_vec(s, a->rd); + } write_vec_element(s, tcg_int, a->rd, 0, a->esz); return true; } From 13ab4eb19088f78851c69faaa8b5c6f3a3821150 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:23 +0000 Subject: [PATCH 1605/2892] target/arm: Handle FPCR.NEP in do_cvtf_scalar() Handle FPCR.NEP in the operations handled by do_cvtf_scalar(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a5f59a94ec..804b6b5b67 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8733,7 +8733,7 @@ static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, } else { gen_helper_vfp_uqtod(tcg_double, tcg_int, tcg_shift, tcg_fpstatus); } - write_fp_dreg(s, rd, tcg_double); + write_fp_dreg_merging(s, rd, rd, tcg_double); break; case MO_32: @@ -8743,7 +8743,7 @@ static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, } else { gen_helper_vfp_uqtos(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); } - write_fp_sreg(s, rd, tcg_single); + write_fp_sreg_merging(s, rd, rd, tcg_single); break; case MO_16: @@ -8753,7 +8753,7 @@ static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, } else { gen_helper_vfp_uqtoh(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); } - write_fp_sreg(s, rd, tcg_single); + write_fp_hreg_merging(s, rd, rd, tcg_single); break; default: From 64339259a9c2c5d504ea5179fc42091e3e4a7b22 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:24 +0000 Subject: [PATCH 1606/2892] target/arm: Handle FPCR.NEP for scalar FABS and FNEG Handle FPCR.NEP merging for scalar FABS and FNEG; this requires an extra parameter to do_fp1_scalar_int(), since FMOV scalar does not have the merging behaviour. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 804b6b5b67..3195cb5fef 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8426,21 +8426,30 @@ typedef struct FPScalar1Int { } FPScalar1Int; static bool do_fp1_scalar_int(DisasContext *s, arg_rr_e *a, - const FPScalar1Int *f) + const FPScalar1Int *f, + bool merging) { switch (a->esz) { case MO_64: if (fp_access_check(s)) { TCGv_i64 t = read_fp_dreg(s, a->rn); f->gen_d(t, t); - write_fp_dreg(s, a->rd, t); + if (merging) { + write_fp_dreg_merging(s, a->rd, a->rd, t); + } else { + write_fp_dreg(s, a->rd, t); + } } break; case MO_32: if (fp_access_check(s)) { TCGv_i32 t = read_fp_sreg(s, a->rn); f->gen_s(t, t); - write_fp_sreg(s, a->rd, t); + if (merging) { + write_fp_sreg_merging(s, a->rd, a->rd, t); + } else { + write_fp_sreg(s, a->rd, t); + } } break; case MO_16: @@ -8450,7 +8459,11 @@ static bool do_fp1_scalar_int(DisasContext *s, arg_rr_e *a, if (fp_access_check(s)) { TCGv_i32 t = read_fp_hreg(s, a->rn); f->gen_h(t, t); - write_fp_sreg(s, a->rd, t); + if (merging) { + write_fp_hreg_merging(s, a->rd, a->rd, t); + } else { + write_fp_sreg(s, a->rd, t); + } } break; default: @@ -8464,21 +8477,21 @@ static const FPScalar1Int f_scalar_fmov = { tcg_gen_mov_i32, tcg_gen_mov_i64, }; -TRANS(FMOV_s, do_fp1_scalar_int, a, &f_scalar_fmov) +TRANS(FMOV_s, do_fp1_scalar_int, a, &f_scalar_fmov, false) static const FPScalar1Int f_scalar_fabs = { gen_vfp_absh, gen_vfp_abss, gen_vfp_absd, }; -TRANS(FABS_s, do_fp1_scalar_int, a, &f_scalar_fabs) +TRANS(FABS_s, do_fp1_scalar_int, a, &f_scalar_fabs, true) static const FPScalar1Int f_scalar_fneg = { gen_vfp_negh, gen_vfp_negs, gen_vfp_negd, }; -TRANS(FNEG_s, do_fp1_scalar_int, a, &f_scalar_fneg) +TRANS(FNEG_s, do_fp1_scalar_int, a, &f_scalar_fneg, true) typedef struct FPScalar1 { void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_ptr); From 555639065d5b5310e8e67af383539309784d37dd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:25 +0000 Subject: [PATCH 1607/2892] target/arm: Handle FPCR.NEP for FCVTXN (scalar) Unlike the other users of do_2misc_narrow_scalar(), FCVTXN (scalar) is always double-to-single and must honour FPCR.NEP. Implement this directly in a trans function rather than using do_2misc_narrow_scalar(). We still need gen_fcvtxn_sd() and the f_scalar_fcvtxn[] array for the FCVTXN (vector) insn, so we move those down in the file to where they are used. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 43 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3195cb5fef..ac8196136c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9247,24 +9247,21 @@ static ArithOneOp * const f_scalar_uqxtn[] = { }; TRANS(UQXTN_s, do_2misc_narrow_scalar, a, f_scalar_uqxtn) -static void gen_fcvtxn_sd(TCGv_i64 d, TCGv_i64 n) +static bool trans_FCVTXN_s(DisasContext *s, arg_rr_e *a) { - /* - * 64 bit to 32 bit float conversion - * with von Neumann rounding (round to odd) - */ - TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_fcvtx_f64_to_f32(tmp, n, fpstatus_ptr(FPST_A64)); - tcg_gen_extu_i32_i64(d, tmp); + if (fp_access_check(s)) { + /* + * 64 bit to 32 bit float conversion + * with von Neumann rounding (round to odd) + */ + TCGv_i64 src = read_fp_dreg(s, a->rn); + TCGv_i32 dst = tcg_temp_new_i32(); + gen_helper_fcvtx_f64_to_f32(dst, src, fpstatus_ptr(FPST_A64)); + write_fp_sreg_merging(s, a->rd, a->rd, dst); + } + return true; } -static ArithOneOp * const f_scalar_fcvtxn[] = { - NULL, - NULL, - gen_fcvtxn_sd, -}; -TRANS(FCVTXN_s, do_2misc_narrow_scalar, a, f_scalar_fcvtxn) - #undef WRAP_ENV static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) @@ -9366,11 +9363,27 @@ static void gen_fcvtn_sd(TCGv_i64 d, TCGv_i64 n) tcg_gen_extu_i32_i64(d, tmp); } +static void gen_fcvtxn_sd(TCGv_i64 d, TCGv_i64 n) +{ + /* + * 64 bit to 32 bit float conversion + * with von Neumann rounding (round to odd) + */ + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_fcvtx_f64_to_f32(tmp, n, fpstatus_ptr(FPST_A64)); + tcg_gen_extu_i32_i64(d, tmp); +} + static ArithOneOp * const f_vector_fcvtn[] = { NULL, gen_fcvtn_hs, gen_fcvtn_sd, }; +static ArithOneOp * const f_scalar_fcvtxn[] = { + NULL, + NULL, + gen_fcvtxn_sd, +}; TRANS(FCVTN_v, do_2misc_narrow_vector, a, f_vector_fcvtn) TRANS(FCVTXN_v, do_2misc_narrow_vector, a, f_scalar_fcvtxn) From 7bcde0c6dd9e7d150fb530b85f9218270db98638 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:26 +0000 Subject: [PATCH 1608/2892] target/arm: Handle FPCR.NEP for NEP for FMUL, FMULX scalar by element do_fp3_scalar_idx() is used only for the FMUL and FMULX scalar by element instructions; these both need to merge the result with the Rn register when FPCR.NEP is set. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ac8196136c..f31acee30a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6296,7 +6296,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) read_vec_element(s, t1, a->rm, a->idx, MO_64); f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); - write_fp_dreg(s, a->rd, t0); + write_fp_dreg_merging(s, a->rd, a->rn, t0); } break; case MO_32: @@ -6306,7 +6306,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) read_vec_element_i32(s, t1, a->rm, a->idx, MO_32); f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); - write_fp_sreg(s, a->rd, t0); + write_fp_sreg_merging(s, a->rd, a->rn, t0); } break; case MO_16: @@ -6319,7 +6319,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) read_vec_element_i32(s, t1, a->rm, a->idx, MO_16); f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); - write_fp_sreg(s, a->rd, t0); + write_fp_hreg_merging(s, a->rd, a->rn, t0); } break; default: From 2a734f3fe3cf528f3cf2f69600e88797b937201a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:27 +0000 Subject: [PATCH 1609/2892] target/arm: Implement FPCR.AH semantics for scalar FMIN/FMAX When FPCR.AH == 1, floating point FMIN and FMAX have some odd special cases: * comparing two zeroes (even of different sign) or comparing a NaN with anything always returns the second argument (possibly squashed to zero) * denormal outputs are not squashed to zero regardless of FZ or FZ16 Implement these semantics in new helper functions and select them at translate time if FPCR.AH is 1 for the scalar FMAX and FMIN insns. (We will convert the other FMAX and FMIN insns in subsequent commits.) Note that FMINNM and FMAXNM are not affected. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-a64.c | 36 ++++++++++++++++++++++++++++++++++ target/arm/tcg/helper-a64.h | 7 +++++++ target/arm/tcg/translate-a64.c | 23 ++++++++++++++++++++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 05036089dd..ed5e4a4599 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -399,6 +399,42 @@ float32 HELPER(fcvtx_f64_to_f32)(float64 a, float_status *fpst) return r; } +/* + * AH=1 min/max have some odd special cases: + * comparing two zeroes (regardless of sign), (NaN, anything), + * or (anything, NaN) should return the second argument (possibly + * squashed to zero). + * Also, denormal outputs are not squashed to zero regardless of FZ or FZ16. + */ +#define AH_MINMAX_HELPER(NAME, CTYPE, FLOATTYPE, MINMAX) \ + CTYPE HELPER(NAME)(CTYPE a, CTYPE b, float_status *fpst) \ + { \ + bool save; \ + CTYPE r; \ + a = FLOATTYPE ## _squash_input_denormal(a, fpst); \ + b = FLOATTYPE ## _squash_input_denormal(b, fpst); \ + if (FLOATTYPE ## _is_zero(a) && FLOATTYPE ## _is_zero(b)) { \ + return b; \ + } \ + if (FLOATTYPE ## _is_any_nan(a) || \ + FLOATTYPE ## _is_any_nan(b)) { \ + float_raise(float_flag_invalid, fpst); \ + return b; \ + } \ + save = get_flush_to_zero(fpst); \ + set_flush_to_zero(false, fpst); \ + r = FLOATTYPE ## _ ## MINMAX(a, b, fpst); \ + set_flush_to_zero(save, fpst); \ + return r; \ + } + +AH_MINMAX_HELPER(vfp_ah_minh, dh_ctype_f16, float16, min) +AH_MINMAX_HELPER(vfp_ah_mins, float32, float32, min) +AH_MINMAX_HELPER(vfp_ah_mind, float64, float64, min) +AH_MINMAX_HELPER(vfp_ah_maxh, dh_ctype_f16, float16, max) +AH_MINMAX_HELPER(vfp_ah_maxs, float32, float32, max) +AH_MINMAX_HELPER(vfp_ah_maxd, float64, float64, max) + /* 64-bit versions of the CRC helpers. Note that although the operation * (and the prototypes of crc32c() and crc32() mean that only the bottom * 32 bits of the accumulator and result are used, we pass and return diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index bac12fbe55..ae0424f6de 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -67,6 +67,13 @@ DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, fpst) DEF_HELPER_2(advsimd_rinth_exact, f16, f16, fpst) DEF_HELPER_2(advsimd_rinth, f16, f16, fpst) +DEF_HELPER_3(vfp_ah_minh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_ah_mins, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_ah_mind, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_ah_maxh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_ah_maxs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_ah_maxd, f64, f64, f64, fpst) + DEF_HELPER_2(exception_return, void, env, i64) DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f31acee30a..89f061d20b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5141,6 +5141,15 @@ static bool do_fp3_scalar_ah(DisasContext *s, arg_rrr_e *a, const FPScalar *f, select_ah_fpst(s, a->esz)); } +/* Some insns need to call different helpers when FPCR.AH == 1 */ +static bool do_fp3_scalar_2fn(DisasContext *s, arg_rrr_e *a, + const FPScalar *fnormal, + const FPScalar *fah, + int mergereg) +{ + return do_fp3_scalar(s, a, s->fpcr_ah ? fah : fnormal, mergereg); +} + static const FPScalar f_scalar_fadd = { gen_helper_vfp_addh, gen_helper_vfp_adds, @@ -5174,14 +5183,24 @@ static const FPScalar f_scalar_fmax = { gen_helper_vfp_maxs, gen_helper_vfp_maxd, }; -TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax, a->rn) +static const FPScalar f_scalar_fmax_ah = { + gen_helper_vfp_ah_maxh, + gen_helper_vfp_ah_maxs, + gen_helper_vfp_ah_maxd, +}; +TRANS(FMAX_s, do_fp3_scalar_2fn, a, &f_scalar_fmax, &f_scalar_fmax_ah, a->rn) static const FPScalar f_scalar_fmin = { gen_helper_vfp_minh, gen_helper_vfp_mins, gen_helper_vfp_mind, }; -TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin, a->rn) +static const FPScalar f_scalar_fmin_ah = { + gen_helper_vfp_ah_minh, + gen_helper_vfp_ah_mins, + gen_helper_vfp_ah_mind, +}; +TRANS(FMIN_s, do_fp3_scalar_2fn, a, &f_scalar_fmin, &f_scalar_fmin_ah, a->rn) static const FPScalar f_scalar_fmaxnm = { gen_helper_vfp_maxnumh, From d613e44010658ced19ac9add6a9587bef1d63c67 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:28 +0000 Subject: [PATCH 1610/2892] target/arm: Implement FPCR.AH semantics for vector FMIN/FMAX Implement the FPCR.AH == 1 semantics for vector FMIN/FMAX, by creating new _ah_ versions of the gvec helpers which invoke the scalar fmin_ah and fmax_ah helpers on each element. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 14 ++++++++++++++ target/arm/tcg/translate-a64.c | 21 +++++++++++++++++++-- target/arm/tcg/vec_helper.c | 8 ++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index fea43b319c..f1b4606f76 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -972,6 +972,20 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmax_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmax_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmax_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fmin_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmin_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmin_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 89f061d20b..24695ab55b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5596,6 +5596,13 @@ static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, FPST_A64_F16 : FPST_A64); } +static bool do_fp3_vector_2fn(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fnormal[3], + gen_helper_gvec_3_ptr * const fah[3]) +{ + return do_fp3_vector(s, a, data, s->fpcr_ah ? fah : fnormal); +} + static bool do_fp3_vector_ah(DisasContext *s, arg_qrrr_e *a, int data, gen_helper_gvec_3_ptr * const f[3]) { @@ -5636,14 +5643,24 @@ static gen_helper_gvec_3_ptr * const f_vector_fmax[3] = { gen_helper_gvec_fmax_s, gen_helper_gvec_fmax_d, }; -TRANS(FMAX_v, do_fp3_vector, a, 0, f_vector_fmax) +static gen_helper_gvec_3_ptr * const f_vector_fmax_ah[3] = { + gen_helper_gvec_ah_fmax_h, + gen_helper_gvec_ah_fmax_s, + gen_helper_gvec_ah_fmax_d, +}; +TRANS(FMAX_v, do_fp3_vector_2fn, a, 0, f_vector_fmax, f_vector_fmax_ah) static gen_helper_gvec_3_ptr * const f_vector_fmin[3] = { gen_helper_gvec_fmin_h, gen_helper_gvec_fmin_s, gen_helper_gvec_fmin_d, }; -TRANS(FMIN_v, do_fp3_vector, a, 0, f_vector_fmin) +static gen_helper_gvec_3_ptr * const f_vector_fmin_ah[3] = { + gen_helper_gvec_ah_fmin_h, + gen_helper_gvec_ah_fmin_s, + gen_helper_gvec_ah_fmin_d, +}; +TRANS(FMIN_v, do_fp3_vector_2fn, a, 0, f_vector_fmin, f_vector_fmin_ah) static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[3] = { gen_helper_gvec_fmaxnum_h, diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 7330b373c3..9f77aa6b91 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1448,6 +1448,14 @@ DO_3OP(gvec_rsqrts_h, helper_rsqrtsf_f16, float16) DO_3OP(gvec_rsqrts_s, helper_rsqrtsf_f32, float32) DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) +DO_3OP(gvec_ah_fmax_h, helper_vfp_ah_maxh, float16) +DO_3OP(gvec_ah_fmax_s, helper_vfp_ah_maxs, float32) +DO_3OP(gvec_ah_fmax_d, helper_vfp_ah_maxd, float64) + +DO_3OP(gvec_ah_fmin_h, helper_vfp_ah_minh, float16) +DO_3OP(gvec_ah_fmin_s, helper_vfp_ah_mins, float32) +DO_3OP(gvec_ah_fmin_d, helper_vfp_ah_mind, float64) + #endif #undef DO_3OP From bb9330b662c62c5e84620f7028859c9b178642d6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:29 +0000 Subject: [PATCH 1611/2892] target/arm: Implement FPCR.AH semantics for FMAXV and FMINV Implement the FPCR.AH semantics for FMAXV and FMINV. These are the "recursively reduce all lanes of a vector to a scalar result" insns; we just need to use the _ah_ helper for the reduction step when FPCR.AH == 1. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 24695ab55b..4ae0426bf7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7018,27 +7018,35 @@ static TCGv_i32 do_reduction_op(DisasContext *s, int rn, MemOp esz, } static bool do_fp_reduction(DisasContext *s, arg_qrr_e *a, - NeonGenTwoSingleOpFn *fn) + NeonGenTwoSingleOpFn *fnormal, + NeonGenTwoSingleOpFn *fah) { if (fp_access_check(s)) { MemOp esz = a->esz; int elts = (a->q ? 16 : 8) >> esz; TCGv_ptr fpst = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); - TCGv_i32 res = do_reduction_op(s, a->rn, esz, 0, elts, fpst, fn); + TCGv_i32 res = do_reduction_op(s, a->rn, esz, 0, elts, fpst, + s->fpcr_ah ? fah : fnormal); write_fp_sreg(s, a->rd, res); } return true; } -TRANS_FEAT(FMAXNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_maxnumh) -TRANS_FEAT(FMINNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_minnumh) -TRANS_FEAT(FMAXV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_maxh) -TRANS_FEAT(FMINV_h, aa64_fp16, do_fp_reduction, a, gen_helper_vfp_minh) +TRANS_FEAT(FMAXNMV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_maxnumh, gen_helper_vfp_maxnumh) +TRANS_FEAT(FMINNMV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_minnumh, gen_helper_vfp_minnumh) +TRANS_FEAT(FMAXV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_maxh, gen_helper_vfp_ah_maxh) +TRANS_FEAT(FMINV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_minh, gen_helper_vfp_ah_minh) -TRANS(FMAXNMV_s, do_fp_reduction, a, gen_helper_vfp_maxnums) -TRANS(FMINNMV_s, do_fp_reduction, a, gen_helper_vfp_minnums) -TRANS(FMAXV_s, do_fp_reduction, a, gen_helper_vfp_maxs) -TRANS(FMINV_s, do_fp_reduction, a, gen_helper_vfp_mins) +TRANS(FMAXNMV_s, do_fp_reduction, a, + gen_helper_vfp_maxnums, gen_helper_vfp_maxnums) +TRANS(FMINNMV_s, do_fp_reduction, a, + gen_helper_vfp_minnums, gen_helper_vfp_minnums) +TRANS(FMAXV_s, do_fp_reduction, a, gen_helper_vfp_maxs, gen_helper_vfp_ah_maxs) +TRANS(FMINV_s, do_fp_reduction, a, gen_helper_vfp_mins, gen_helper_vfp_ah_mins) /* * Floating-point Immediate From 384433e709836209ae34bedda8a2a57992be8e18 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:30 +0000 Subject: [PATCH 1612/2892] target/arm: Implement FPCR.AH semantics for FMINP and FMAXP Implement the FPCR.AH semantics for the pairwise floating point minimum/maximum insns FMINP and FMAXP. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 14 ++++++++++++++ target/arm/tcg/translate-a64.c | 25 +++++++++++++++++++++---- target/arm/tcg/vec_helper.c | 10 ++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index f1b4606f76..8349752e99 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -986,6 +986,20 @@ DEF_HELPER_FLAGS_5(gvec_ah_fmin_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_ah_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmaxp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmaxp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmaxp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fminp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fminp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fminp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 4ae0426bf7..02701b10c6 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5765,14 +5765,24 @@ static gen_helper_gvec_3_ptr * const f_vector_fmaxp[3] = { gen_helper_gvec_fmaxp_s, gen_helper_gvec_fmaxp_d, }; -TRANS(FMAXP_v, do_fp3_vector, a, 0, f_vector_fmaxp) +static gen_helper_gvec_3_ptr * const f_vector_ah_fmaxp[3] = { + gen_helper_gvec_ah_fmaxp_h, + gen_helper_gvec_ah_fmaxp_s, + gen_helper_gvec_ah_fmaxp_d, +}; +TRANS(FMAXP_v, do_fp3_vector_2fn, a, 0, f_vector_fmaxp, f_vector_ah_fmaxp) static gen_helper_gvec_3_ptr * const f_vector_fminp[3] = { gen_helper_gvec_fminp_h, gen_helper_gvec_fminp_s, gen_helper_gvec_fminp_d, }; -TRANS(FMINP_v, do_fp3_vector, a, 0, f_vector_fminp) +static gen_helper_gvec_3_ptr * const f_vector_ah_fminp[3] = { + gen_helper_gvec_ah_fminp_h, + gen_helper_gvec_ah_fminp_s, + gen_helper_gvec_ah_fminp_d, +}; +TRANS(FMINP_v, do_fp3_vector_2fn, a, 0, f_vector_fminp, f_vector_ah_fminp) static gen_helper_gvec_3_ptr * const f_vector_fmaxnmp[3] = { gen_helper_gvec_fmaxnump_h, @@ -6764,9 +6774,16 @@ static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) return true; } +static bool do_fp3_scalar_pair_2fn(DisasContext *s, arg_rr_e *a, + const FPScalar *fnormal, + const FPScalar *fah) +{ + return do_fp3_scalar_pair(s, a, s->fpcr_ah ? fah : fnormal); +} + TRANS(FADDP_s, do_fp3_scalar_pair, a, &f_scalar_fadd) -TRANS(FMAXP_s, do_fp3_scalar_pair, a, &f_scalar_fmax) -TRANS(FMINP_s, do_fp3_scalar_pair, a, &f_scalar_fmin) +TRANS(FMAXP_s, do_fp3_scalar_pair_2fn, a, &f_scalar_fmax, &f_scalar_fmax_ah) +TRANS(FMINP_s, do_fp3_scalar_pair_2fn, a, &f_scalar_fmin, &f_scalar_fmin_ah) TRANS(FMAXNMP_s, do_fp3_scalar_pair, a, &f_scalar_fmaxnm) TRANS(FMINNMP_s, do_fp3_scalar_pair, a, &f_scalar_fminnm) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 9f77aa6b91..61e6e54570 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2444,6 +2444,16 @@ DO_3OP_PAIR(gvec_fminnump_h, float16_minnum, float16, H2) DO_3OP_PAIR(gvec_fminnump_s, float32_minnum, float32, H4) DO_3OP_PAIR(gvec_fminnump_d, float64_minnum, float64, ) +#ifdef TARGET_AARCH64 +DO_3OP_PAIR(gvec_ah_fmaxp_h, helper_vfp_ah_maxh, float16, H2) +DO_3OP_PAIR(gvec_ah_fmaxp_s, helper_vfp_ah_maxs, float32, H4) +DO_3OP_PAIR(gvec_ah_fmaxp_d, helper_vfp_ah_maxd, float64, ) + +DO_3OP_PAIR(gvec_ah_fminp_h, helper_vfp_ah_minh, float16, H2) +DO_3OP_PAIR(gvec_ah_fminp_s, helper_vfp_ah_mins, float32, H4) +DO_3OP_PAIR(gvec_ah_fminp_d, helper_vfp_ah_mind, float64, ) +#endif + #undef DO_3OP_PAIR #define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ From dac3a42f39ef1fae52b5b7b15662f3277f9ce7f2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:31 +0000 Subject: [PATCH 1613/2892] target/arm: Implement FPCR.AH semantics for SVE FMAXV and FMINV Implement the FPCR.AH semantics for the SVE FMAXV and FMINV vector-reduction-to-scalar max/min operations. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 14 +++++++++++ target/arm/tcg/sve_helper.c | 43 +++++++++++++++++++++------------- target/arm/tcg/translate-sve.c | 16 +++++++++++-- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 8349752e99..7ca95b8fa9 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1035,6 +1035,20 @@ DEF_HELPER_FLAGS_4(sve_fminv_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_4(sve_fminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fmaxv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fmaxv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fmaxv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(sve_ah_fminv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fminv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fminv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 9837c5bc7a..3631d85f23 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4190,7 +4190,7 @@ static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ uintptr_t half = n / 2; \ TYPE lo = NAME##_reduce(data, status, half); \ TYPE hi = NAME##_reduce(data + half, status, half); \ - return TYPE##_##FUNC(lo, hi, status); \ + return FUNC(lo, hi, status); \ } \ } \ uint64_t HELPER(NAME)(void *vn, void *vg, float_status *s, uint32_t desc) \ @@ -4211,26 +4211,37 @@ uint64_t HELPER(NAME)(void *vn, void *vg, float_status *s, uint32_t desc) \ return NAME##_reduce(data, s, maxsz / sizeof(TYPE)); \ } -DO_REDUCE(sve_faddv_h, float16, H1_2, add, float16_zero) -DO_REDUCE(sve_faddv_s, float32, H1_4, add, float32_zero) -DO_REDUCE(sve_faddv_d, float64, H1_8, add, float64_zero) +DO_REDUCE(sve_faddv_h, float16, H1_2, float16_add, float16_zero) +DO_REDUCE(sve_faddv_s, float32, H1_4, float32_add, float32_zero) +DO_REDUCE(sve_faddv_d, float64, H1_8, float64_add, float64_zero) /* Identity is floatN_default_nan, without the function call. */ -DO_REDUCE(sve_fminnmv_h, float16, H1_2, minnum, 0x7E00) -DO_REDUCE(sve_fminnmv_s, float32, H1_4, minnum, 0x7FC00000) -DO_REDUCE(sve_fminnmv_d, float64, H1_8, minnum, 0x7FF8000000000000ULL) +DO_REDUCE(sve_fminnmv_h, float16, H1_2, float16_minnum, 0x7E00) +DO_REDUCE(sve_fminnmv_s, float32, H1_4, float32_minnum, 0x7FC00000) +DO_REDUCE(sve_fminnmv_d, float64, H1_8, float64_minnum, 0x7FF8000000000000ULL) -DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, maxnum, 0x7E00) -DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, maxnum, 0x7FC00000) -DO_REDUCE(sve_fmaxnmv_d, float64, H1_8, maxnum, 0x7FF8000000000000ULL) +DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, float16_maxnum, 0x7E00) +DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, float32_maxnum, 0x7FC00000) +DO_REDUCE(sve_fmaxnmv_d, float64, H1_8, float64_maxnum, 0x7FF8000000000000ULL) -DO_REDUCE(sve_fminv_h, float16, H1_2, min, float16_infinity) -DO_REDUCE(sve_fminv_s, float32, H1_4, min, float32_infinity) -DO_REDUCE(sve_fminv_d, float64, H1_8, min, float64_infinity) +DO_REDUCE(sve_fminv_h, float16, H1_2, float16_min, float16_infinity) +DO_REDUCE(sve_fminv_s, float32, H1_4, float32_min, float32_infinity) +DO_REDUCE(sve_fminv_d, float64, H1_8, float64_min, float64_infinity) -DO_REDUCE(sve_fmaxv_h, float16, H1_2, max, float16_chs(float16_infinity)) -DO_REDUCE(sve_fmaxv_s, float32, H1_4, max, float32_chs(float32_infinity)) -DO_REDUCE(sve_fmaxv_d, float64, H1_8, max, float64_chs(float64_infinity)) +DO_REDUCE(sve_fmaxv_h, float16, H1_2, float16_max, float16_chs(float16_infinity)) +DO_REDUCE(sve_fmaxv_s, float32, H1_4, float32_max, float32_chs(float32_infinity)) +DO_REDUCE(sve_fmaxv_d, float64, H1_8, float64_max, float64_chs(float64_infinity)) + +DO_REDUCE(sve_ah_fminv_h, float16, H1_2, helper_vfp_ah_minh, float16_infinity) +DO_REDUCE(sve_ah_fminv_s, float32, H1_4, helper_vfp_ah_mins, float32_infinity) +DO_REDUCE(sve_ah_fminv_d, float64, H1_8, helper_vfp_ah_mind, float64_infinity) + +DO_REDUCE(sve_ah_fmaxv_h, float16, H1_2, helper_vfp_ah_maxh, + float16_chs(float16_infinity)) +DO_REDUCE(sve_ah_fmaxv_s, float32, H1_4, helper_vfp_ah_maxs, + float32_chs(float32_infinity)) +DO_REDUCE(sve_ah_fmaxv_d, float64, H1_8, helper_vfp_ah_maxd, + float64_chs(float64_infinity)) #undef DO_REDUCE diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index e38a49dd31..35f6d78a0e 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3588,11 +3588,23 @@ static bool do_reduce(DisasContext *s, arg_rpr_esz *a, }; \ TRANS_FEAT(NAME, aa64_sve, do_reduce, a, name##_fns[a->esz]) +#define DO_VPZ_AH(NAME, name) \ + static gen_helper_fp_reduce * const name##_fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + static gen_helper_fp_reduce * const name##_ah_fns[4] = { \ + NULL, gen_helper_sve_ah_##name##_h, \ + gen_helper_sve_ah_##name##_s, gen_helper_sve_ah_##name##_d, \ + }; \ + TRANS_FEAT(NAME, aa64_sve, do_reduce, a, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz]) + DO_VPZ(FADDV, faddv) DO_VPZ(FMINNMV, fminnmv) DO_VPZ(FMAXNMV, fmaxnmv) -DO_VPZ(FMINV, fminv) -DO_VPZ(FMAXV, fmaxv) +DO_VPZ_AH(FMINV, fminv) +DO_VPZ_AH(FMAXV, fmaxv) #undef DO_VPZ From bf92725bb26e6bce2b2267f384d8070595c46daa Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:32 +0000 Subject: [PATCH 1614/2892] target/arm: Implement FPCR.AH semantics for SVE FMIN/FMAX immediate Implement the FPCR.AH semantics for the SVE FMAX and FMIN operations that take an immediate as the second operand. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 14 ++++++++++++++ target/arm/tcg/sve_helper.c | 8 ++++++++ target/arm/tcg/translate-sve.c | 25 +++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 7ca95b8fa9..3c1d2624ed 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1231,6 +1231,20 @@ DEF_HELPER_FLAGS_6(sve_fmins_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmins_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmaxs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmaxs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmaxs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fmins_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmins_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmins_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) + DEF_HELPER_FLAGS_5(sve_fcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_dh, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 3631d85f23..2f6fc82ee4 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4459,6 +4459,14 @@ DO_ZPZS_FP(sve_fmins_h, float16, H1_2, float16_min) DO_ZPZS_FP(sve_fmins_s, float32, H1_4, float32_min) DO_ZPZS_FP(sve_fmins_d, float64, H1_8, float64_min) +DO_ZPZS_FP(sve_ah_fmaxs_h, float16, H1_2, helper_vfp_ah_maxh) +DO_ZPZS_FP(sve_ah_fmaxs_s, float32, H1_4, helper_vfp_ah_maxs) +DO_ZPZS_FP(sve_ah_fmaxs_d, float64, H1_8, helper_vfp_ah_maxd) + +DO_ZPZS_FP(sve_ah_fmins_h, float16, H1_2, helper_vfp_ah_minh) +DO_ZPZS_FP(sve_ah_fmins_s, float32, H1_4, helper_vfp_ah_mins) +DO_ZPZS_FP(sve_ah_fmins_d, float64, H1_8, helper_vfp_ah_mind) + /* Fully general two-operand expander, controlled by a predicate, * With the extra float_status parameter. */ diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 35f6d78a0e..187bd647c2 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3821,14 +3821,35 @@ static bool do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, TRANS_FEAT(NAME##_zpzi, aa64_sve, do_fp_imm, a, \ name##_const[a->esz][a->imm], name##_fns[a->esz]) +#define DO_FP_AH_IMM(NAME, name, const0, const1) \ + static gen_helper_sve_fp2scalar * const name##_fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d \ + }; \ + static gen_helper_sve_fp2scalar * const name##_ah_fns[4] = { \ + NULL, gen_helper_sve_ah_##name##_h, \ + gen_helper_sve_ah_##name##_s, \ + gen_helper_sve_ah_##name##_d \ + }; \ + static uint64_t const name##_const[4][2] = { \ + { -1, -1 }, \ + { float16_##const0, float16_##const1 }, \ + { float32_##const0, float32_##const1 }, \ + { float64_##const0, float64_##const1 }, \ + }; \ + TRANS_FEAT(NAME##_zpzi, aa64_sve, do_fp_imm, a, \ + name##_const[a->esz][a->imm], \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz]) + DO_FP_IMM(FADD, fadds, half, one) DO_FP_IMM(FSUB, fsubs, half, one) DO_FP_IMM(FMUL, fmuls, half, two) DO_FP_IMM(FSUBR, fsubrs, half, one) DO_FP_IMM(FMAXNM, fmaxnms, zero, one) DO_FP_IMM(FMINNM, fminnms, zero, one) -DO_FP_IMM(FMAX, fmaxs, zero, one) -DO_FP_IMM(FMIN, fmins, zero, one) +DO_FP_AH_IMM(FMAX, fmaxs, zero, one) +DO_FP_AH_IMM(FMIN, fmins, zero, one) #undef DO_FP_IMM From 4ba5383bc50d8ef18aed0d6aaa203c331dc0abf5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:33 +0000 Subject: [PATCH 1615/2892] target/arm: Implement FPCR.AH semantics for SVE FMIN/FMAX vector Implement the FPCR.AH semantics for the SVE FMAX and FMIN operations that take two vector operands. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 14 ++++++++++++++ target/arm/tcg/sve_helper.c | 8 ++++++++ target/arm/tcg/translate-sve.c | 17 +++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 3c1d2624ed..918f2e61b7 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1140,6 +1140,20 @@ DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fmax_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmax_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmax_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 2f6fc82ee4..a688b98d28 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4347,6 +4347,14 @@ DO_ZPZZ_FP(sve_fmax_h, uint16_t, H1_2, float16_max) DO_ZPZZ_FP(sve_fmax_s, uint32_t, H1_4, float32_max) DO_ZPZZ_FP(sve_fmax_d, uint64_t, H1_8, float64_max) +DO_ZPZZ_FP(sve_ah_fmin_h, uint16_t, H1_2, helper_vfp_ah_minh) +DO_ZPZZ_FP(sve_ah_fmin_s, uint32_t, H1_4, helper_vfp_ah_mins) +DO_ZPZZ_FP(sve_ah_fmin_d, uint64_t, H1_8, helper_vfp_ah_mind) + +DO_ZPZZ_FP(sve_ah_fmax_h, uint16_t, H1_2, helper_vfp_ah_maxh) +DO_ZPZZ_FP(sve_ah_fmax_s, uint32_t, H1_4, helper_vfp_ah_maxs) +DO_ZPZZ_FP(sve_ah_fmax_d, uint64_t, H1_8, helper_vfp_ah_maxd) + DO_ZPZZ_FP(sve_fminnum_h, uint16_t, H1_2, float16_minnum) DO_ZPZZ_FP(sve_fminnum_s, uint32_t, H1_4, float32_minnum) DO_ZPZZ_FP(sve_fminnum_d, uint64_t, H1_8, float64_minnum) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 187bd647c2..2813e5f487 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3759,11 +3759,24 @@ TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, }; \ TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, name##_zpzz_fns[a->esz], a) +#define DO_ZPZZ_AH_FP(NAME, FEAT, name, ah_name) \ + static gen_helper_gvec_4_ptr * const name##_zpzz_fns[4] = { \ + NULL, gen_helper_##name##_h, \ + gen_helper_##name##_s, gen_helper_##name##_d \ + }; \ + static gen_helper_gvec_4_ptr * const name##_ah_zpzz_fns[4] = { \ + NULL, gen_helper_##ah_name##_h, \ + gen_helper_##ah_name##_s, gen_helper_##ah_name##_d \ + }; \ + TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, \ + s->fpcr_ah ? name##_ah_zpzz_fns[a->esz] : \ + name##_zpzz_fns[a->esz], a) + DO_ZPZZ_FP(FADD_zpzz, aa64_sve, sve_fadd) DO_ZPZZ_FP(FSUB_zpzz, aa64_sve, sve_fsub) DO_ZPZZ_FP(FMUL_zpzz, aa64_sve, sve_fmul) -DO_ZPZZ_FP(FMIN_zpzz, aa64_sve, sve_fmin) -DO_ZPZZ_FP(FMAX_zpzz, aa64_sve, sve_fmax) +DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) +DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) DO_ZPZZ_FP(FMINNM_zpzz, aa64_sve, sve_fminnum) DO_ZPZZ_FP(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) DO_ZPZZ_FP(FABD, aa64_sve, sve_fabd) From e76df44d2dd06018016f35aff41258ce356d6e40 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:34 +0000 Subject: [PATCH 1616/2892] target/arm: Implement FPCR.AH handling of negation of NaN FPCR.AH == 1 mandates that negation of a NaN value should not flip its sign bit. This means we can no longer use gen_vfp_neg*() everywhere but must instead generate slightly more complex code when FPCR.AH is set. Make this change for the scalar FNEG and for those places in translate-a64.c which were previously directly calling gen_vfp_neg*(). This change in semantics also affects any other instruction whose pseudocode calls FPNeg(); in following commits we extend this change to the other affected instructions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 125 ++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 02701b10c6..abd0d916b1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -828,6 +828,74 @@ static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, is_q ? 16 : 8, vec_full_reg_size(s), data, fn); } +/* + * When FPCR.AH == 1, NEG and ABS do not flip the sign bit of a NaN. + * These functions implement + * d = floatN_is_any_nan(s) ? s : floatN_chs(s) + * which for float32 is + * d = (s & ~(1 << 31)) > 0x7f800000UL) ? s : (s ^ (1 << 31)) + * and similarly for the other float sizes. + */ +static void gen_vfp_ah_negh(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(), chs_s = tcg_temp_new_i32(); + + gen_vfp_negh(chs_s, s); + gen_vfp_absh(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7c00), + s, chs_s); +} + +static void gen_vfp_ah_negs(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(), chs_s = tcg_temp_new_i32(); + + gen_vfp_negs(chs_s, s); + gen_vfp_abss(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7f800000UL), + s, chs_s); +} + +static void gen_vfp_ah_negd(TCGv_i64 d, TCGv_i64 s) +{ + TCGv_i64 abs_s = tcg_temp_new_i64(), chs_s = tcg_temp_new_i64(); + + gen_vfp_negd(chs_s, s); + gen_vfp_absd(abs_s, s); + tcg_gen_movcond_i64(TCG_COND_GTU, d, + abs_s, tcg_constant_i64(0x7ff0000000000000ULL), + s, chs_s); +} + +static void gen_vfp_maybe_ah_negh(DisasContext *dc, TCGv_i32 d, TCGv_i32 s) +{ + if (dc->fpcr_ah) { + gen_vfp_ah_negh(d, s); + } else { + gen_vfp_negh(d, s); + } +} + +static void gen_vfp_maybe_ah_negs(DisasContext *dc, TCGv_i32 d, TCGv_i32 s) +{ + if (dc->fpcr_ah) { + gen_vfp_ah_negs(d, s); + } else { + gen_vfp_negs(d, s); + } +} + +static void gen_vfp_maybe_ah_negd(DisasContext *dc, TCGv_i64 d, TCGv_i64 s) +{ + if (dc->fpcr_ah) { + gen_vfp_ah_negd(d, s); + } else { + gen_vfp_negd(d, s); + } +} + /* Set ZF and NF based on a 64 bit result. This is alas fiddlier * than the 32 bit equivalent. */ @@ -5241,12 +5309,35 @@ static void gen_fnmul_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) gen_vfp_negd(d, d); } +static void gen_fnmul_ah_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_mulh(d, n, m, s); + gen_vfp_ah_negh(d, d); +} + +static void gen_fnmul_ah_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_muls(d, n, m, s); + gen_vfp_ah_negs(d, d); +} + +static void gen_fnmul_ah_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_muld(d, n, m, s); + gen_vfp_ah_negd(d, d); +} + static const FPScalar f_scalar_fnmul = { gen_fnmul_h, gen_fnmul_s, gen_fnmul_d, }; -TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul, a->rn) +static const FPScalar f_scalar_ah_fnmul = { + gen_fnmul_ah_h, + gen_fnmul_ah_s, + gen_fnmul_ah_d, +}; +TRANS(FNMUL_s, do_fp3_scalar_2fn, a, &f_scalar_fnmul, &f_scalar_ah_fnmul, a->rn) static const FPScalar f_scalar_fcmeq = { gen_helper_advsimd_ceq_f16, @@ -6388,7 +6479,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) read_vec_element(s, t2, a->rm, a->idx, MO_64); if (neg) { - gen_vfp_negd(t1, t1); + gen_vfp_maybe_ah_negd(s, t1, t1); } gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); write_fp_dreg_merging(s, a->rd, a->rd, t0); @@ -6402,7 +6493,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) read_vec_element_i32(s, t2, a->rm, a->idx, MO_32); if (neg) { - gen_vfp_negs(t1, t1); + gen_vfp_maybe_ah_negs(s, t1, t1); } gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); write_fp_sreg_merging(s, a->rd, a->rd, t0); @@ -6419,7 +6510,7 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) read_vec_element_i32(s, t2, a->rm, a->idx, MO_16); if (neg) { - gen_vfp_negh(t1, t1); + gen_vfp_maybe_ah_negh(s, t1, t1); } gen_helper_advsimd_muladdh(t0, t1, t2, t0, fpstatus_ptr(FPST_A64_F16)); @@ -6902,10 +6993,10 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) TCGv_i64 ta = read_fp_dreg(s, a->ra); if (neg_a) { - gen_vfp_negd(ta, ta); + gen_vfp_maybe_ah_negd(s, ta, ta); } if (neg_n) { - gen_vfp_negd(tn, tn); + gen_vfp_maybe_ah_negd(s, tn, tn); } fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_muladdd(ta, tn, tm, ta, fpst); @@ -6920,10 +7011,10 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) TCGv_i32 ta = read_fp_sreg(s, a->ra); if (neg_a) { - gen_vfp_negs(ta, ta); + gen_vfp_maybe_ah_negs(s, ta, ta); } if (neg_n) { - gen_vfp_negs(tn, tn); + gen_vfp_maybe_ah_negs(s, tn, tn); } fpst = fpstatus_ptr(FPST_A64); gen_helper_vfp_muladds(ta, tn, tm, ta, fpst); @@ -6941,10 +7032,10 @@ static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) TCGv_i32 ta = read_fp_hreg(s, a->ra); if (neg_a) { - gen_vfp_negh(ta, ta); + gen_vfp_maybe_ah_negh(s, ta, ta); } if (neg_n) { - gen_vfp_negh(tn, tn); + gen_vfp_maybe_ah_negh(s, tn, tn); } fpst = fpstatus_ptr(FPST_A64_F16); gen_helper_advsimd_muladdh(ta, tn, tm, ta, fpst); @@ -8533,6 +8624,13 @@ static bool do_fp1_scalar_int(DisasContext *s, arg_rr_e *a, return true; } +static bool do_fp1_scalar_int_2fn(DisasContext *s, arg_rr_e *a, + const FPScalar1Int *fnormal, + const FPScalar1Int *fah) +{ + return do_fp1_scalar_int(s, a, s->fpcr_ah ? fah : fnormal, true); +} + static const FPScalar1Int f_scalar_fmov = { tcg_gen_mov_i32, tcg_gen_mov_i32, @@ -8552,7 +8650,12 @@ static const FPScalar1Int f_scalar_fneg = { gen_vfp_negs, gen_vfp_negd, }; -TRANS(FNEG_s, do_fp1_scalar_int, a, &f_scalar_fneg, true) +static const FPScalar1Int f_scalar_ah_fneg = { + gen_vfp_ah_negh, + gen_vfp_ah_negs, + gen_vfp_ah_negd, +}; +TRANS(FNEG_s, do_fp1_scalar_int_2fn, a, &f_scalar_fneg, &f_scalar_ah_fneg) typedef struct FPScalar1 { void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_ptr); From d091ce2d3f5bad6df30c686558807bcffb0c5a96 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:35 +0000 Subject: [PATCH 1617/2892] target/arm: Implement FPCR.AH handling for scalar FABS and FABD FPCR.AH == 1 mandates that taking the absolute value of a NaN should not change its sign bit. This means we can no longer use gen_vfp_abs*() everywhere but must instead generate slightly more complex code when FPCR.AH is set. Implement these semantics for scalar FABS and FABD. This change also affects all other instructions whose psuedocode calls FPAbs(); we will extend the change to those instructions in following commits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 69 +++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index abd0d916b1..83cfcdc43b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -869,6 +869,43 @@ static void gen_vfp_ah_negd(TCGv_i64 d, TCGv_i64 s) s, chs_s); } +/* + * These functions implement + * d = floatN_is_any_nan(s) ? s : floatN_abs(s) + * which for float32 is + * d = (s & ~(1 << 31)) > 0x7f800000UL) ? s : (s & ~(1 << 31)) + * and similarly for the other float sizes. + */ +static void gen_vfp_ah_absh(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(); + + gen_vfp_absh(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7c00), + s, abs_s); +} + +static void gen_vfp_ah_abss(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(); + + gen_vfp_abss(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7f800000UL), + s, abs_s); +} + +static void gen_vfp_ah_absd(TCGv_i64 d, TCGv_i64 s) +{ + TCGv_i64 abs_s = tcg_temp_new_i64(); + + gen_vfp_absd(abs_s, s); + tcg_gen_movcond_i64(TCG_COND_GTU, d, + abs_s, tcg_constant_i64(0x7ff0000000000000ULL), + s, abs_s); +} + static void gen_vfp_maybe_ah_negh(DisasContext *dc, TCGv_i32 d, TCGv_i32 s) { if (dc->fpcr_ah) { @@ -5392,12 +5429,35 @@ static void gen_fabd_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) gen_vfp_absd(d, d); } +static void gen_fabd_ah_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subh(d, n, m, s); + gen_vfp_ah_absh(d, d); +} + +static void gen_fabd_ah_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subs(d, n, m, s); + gen_vfp_ah_abss(d, d); +} + +static void gen_fabd_ah_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_subd(d, n, m, s); + gen_vfp_ah_absd(d, d); +} + static const FPScalar f_scalar_fabd = { gen_fabd_h, gen_fabd_s, gen_fabd_d, }; -TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd, a->rn) +static const FPScalar f_scalar_ah_fabd = { + gen_fabd_ah_h, + gen_fabd_ah_s, + gen_fabd_ah_d, +}; +TRANS(FABD_s, do_fp3_scalar_2fn, a, &f_scalar_fabd, &f_scalar_ah_fabd, a->rn) static const FPScalar f_scalar_frecps = { gen_helper_recpsf_f16, @@ -8643,7 +8703,12 @@ static const FPScalar1Int f_scalar_fabs = { gen_vfp_abss, gen_vfp_absd, }; -TRANS(FABS_s, do_fp1_scalar_int, a, &f_scalar_fabs, true) +static const FPScalar1Int f_scalar_ah_fabs = { + gen_vfp_ah_absh, + gen_vfp_ah_abss, + gen_vfp_ah_absd, +}; +TRANS(FABS_s, do_fp1_scalar_int_2fn, a, &f_scalar_fabs, &f_scalar_ah_fabs) static const FPScalar1Int f_scalar_fneg = { gen_vfp_negh, From 538deec62339594ecac4434e278b43b9807085e2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:36 +0000 Subject: [PATCH 1618/2892] target/arm: Handle FPCR.AH in vector FABD Split the handling of vector FABD so that it calls a different set of helpers when FPCR.AH is 1, which implement the "no negation of the sign of a NaN" semantics. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper.h | 4 ++++ target/arm/tcg/translate-a64.c | 7 ++++++- target/arm/tcg/vec_helper.c | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 15bad0773c..43505d5fed 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -722,6 +722,10 @@ DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 83cfcdc43b..76c41b9bdb 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5888,7 +5888,12 @@ static gen_helper_gvec_3_ptr * const f_vector_fabd[3] = { gen_helper_gvec_fabd_s, gen_helper_gvec_fabd_d, }; -TRANS(FABD_v, do_fp3_vector, a, 0, f_vector_fabd) +static gen_helper_gvec_3_ptr * const f_vector_ah_fabd[3] = { + gen_helper_gvec_ah_fabd_h, + gen_helper_gvec_ah_fabd_s, + gen_helper_gvec_ah_fabd_d, +}; +TRANS(FABD_v, do_fp3_vector_2fn, a, 0, f_vector_fabd, f_vector_ah_fabd) static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = { gen_helper_gvec_recps_h, diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 61e6e54570..0b84a562c0 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1302,6 +1302,25 @@ static float64 float64_abd(float64 op1, float64 op2, float_status *stat) return float64_abs(float64_sub(op1, op2, stat)); } +/* ABD when FPCR.AH = 1: avoid flipping sign bit of a NaN result */ +static float16 float16_ah_abd(float16 op1, float16 op2, float_status *stat) +{ + float16 r = float16_sub(op1, op2, stat); + return float16_is_any_nan(r) ? r : float16_abs(r); +} + +static float32 float32_ah_abd(float32 op1, float32 op2, float_status *stat) +{ + float32 r = float32_sub(op1, op2, stat); + return float32_is_any_nan(r) ? r : float32_abs(r); +} + +static float64 float64_ah_abd(float64 op1, float64 op2, float_status *stat) +{ + float64 r = float64_sub(op1, op2, stat); + return float64_is_any_nan(r) ? r : float64_abs(r); +} + /* * Reciprocal step. These are the AArch32 version which uses a * non-fused multiply-and-subtract. @@ -1389,6 +1408,10 @@ DO_3OP(gvec_fabd_h, float16_abd, float16) DO_3OP(gvec_fabd_s, float32_abd, float32) DO_3OP(gvec_fabd_d, float64_abd, float64) +DO_3OP(gvec_ah_fabd_h, float16_ah_abd, float16) +DO_3OP(gvec_ah_fabd_s, float32_ah_abd, float32) +DO_3OP(gvec_ah_fabd_d, float64_ah_abd, float64) + DO_3OP(gvec_fceq_h, float16_ceq, float16) DO_3OP(gvec_fceq_s, float32_ceq, float32) DO_3OP(gvec_fceq_d, float64_ceq, float64) From 47d3216240f642102821467809d6719509eebc12 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:37 +0000 Subject: [PATCH 1619/2892] target/arm: Handle FPCR.AH in SVE FNEG Make SVE FNEG honour the FPCR.AH "don't negate the sign of a NaN" semantics. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 4 ++++ target/arm/tcg/sve_helper.c | 8 ++++++++ target/arm/tcg/translate-sve.c | 7 ++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 918f2e61b7..867a6d96e0 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -545,6 +545,10 @@ DEF_HELPER_FLAGS_4(sve_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_not_zpz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_not_zpz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_not_zpz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index a688b98d28..976f3be44e 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -885,6 +885,14 @@ DO_ZPZ(sve_fneg_h, uint16_t, H1_2, DO_FNEG) DO_ZPZ(sve_fneg_s, uint32_t, H1_4, DO_FNEG) DO_ZPZ_D(sve_fneg_d, uint64_t, DO_FNEG) +#define DO_AH_FNEG_H(N) (float16_is_any_nan(N) ? (N) : DO_FNEG(N)) +#define DO_AH_FNEG_S(N) (float32_is_any_nan(N) ? (N) : DO_FNEG(N)) +#define DO_AH_FNEG_D(N) (float64_is_any_nan(N) ? (N) : DO_FNEG(N)) + +DO_ZPZ(sve_ah_fneg_h, uint16_t, H1_2, DO_AH_FNEG_H) +DO_ZPZ(sve_ah_fneg_s, uint32_t, H1_4, DO_AH_FNEG_S) +DO_ZPZ_D(sve_ah_fneg_d, uint64_t, DO_AH_FNEG_D) + #define DO_NOT(N) (~N) DO_ZPZ(sve_not_zpz_b, uint8_t, H1, DO_NOT) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2813e5f487..4d5de2004f 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -789,7 +789,12 @@ static gen_helper_gvec_3 * const fneg_fns[4] = { NULL, gen_helper_sve_fneg_h, gen_helper_sve_fneg_s, gen_helper_sve_fneg_d, }; -TRANS_FEAT(FNEG, aa64_sve, gen_gvec_ool_arg_zpz, fneg_fns[a->esz], a, 0) +static gen_helper_gvec_3 * const fneg_ah_fns[4] = { + NULL, gen_helper_sve_ah_fneg_h, + gen_helper_sve_ah_fneg_s, gen_helper_sve_ah_fneg_d, +}; +TRANS_FEAT(FNEG, aa64_sve, gen_gvec_ool_arg_zpz, + s->fpcr_ah ? fneg_ah_fns[a->esz] : fneg_fns[a->esz], a, 0) static gen_helper_gvec_3 * const sxtb_fns[4] = { NULL, gen_helper_sve_sxtb_h, From 4de25ed588dd29387af78829197bc16cfa00aa2e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:38 +0000 Subject: [PATCH 1620/2892] target/arm: Handle FPCR.AH in SVE FABS Make SVE FABS honour the FPCR.AH "don't negate the sign of a NaN" semantics. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 4 ++++ target/arm/tcg/sve_helper.c | 8 ++++++++ target/arm/tcg/translate-sve.c | 7 ++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 867a6d96e0..ff12f650c8 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -541,6 +541,10 @@ DEF_HELPER_FLAGS_4(sve_fabs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fabs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fabs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fabs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fabs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fabs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 976f3be44e..5ce7d73647 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -879,6 +879,14 @@ DO_ZPZ(sve_fabs_h, uint16_t, H1_2, DO_FABS) DO_ZPZ(sve_fabs_s, uint32_t, H1_4, DO_FABS) DO_ZPZ_D(sve_fabs_d, uint64_t, DO_FABS) +#define DO_AH_FABS_H(N) (float16_is_any_nan(N) ? (N) : DO_FABS(N)) +#define DO_AH_FABS_S(N) (float32_is_any_nan(N) ? (N) : DO_FABS(N)) +#define DO_AH_FABS_D(N) (float64_is_any_nan(N) ? (N) : DO_FABS(N)) + +DO_ZPZ(sve_ah_fabs_h, uint16_t, H1_2, DO_AH_FABS_H) +DO_ZPZ(sve_ah_fabs_s, uint32_t, H1_4, DO_AH_FABS_S) +DO_ZPZ_D(sve_ah_fabs_d, uint64_t, DO_AH_FABS_D) + #define DO_FNEG(N) (N ^ ~((__typeof(N))-1 >> 1)) DO_ZPZ(sve_fneg_h, uint16_t, H1_2, DO_FNEG) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 4d5de2004f..646d0171d9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -783,7 +783,12 @@ static gen_helper_gvec_3 * const fabs_fns[4] = { NULL, gen_helper_sve_fabs_h, gen_helper_sve_fabs_s, gen_helper_sve_fabs_d, }; -TRANS_FEAT(FABS, aa64_sve, gen_gvec_ool_arg_zpz, fabs_fns[a->esz], a, 0) +static gen_helper_gvec_3 * const fabs_ah_fns[4] = { + NULL, gen_helper_sve_ah_fabs_h, + gen_helper_sve_ah_fabs_s, gen_helper_sve_ah_fabs_d, +}; +TRANS_FEAT(FABS, aa64_sve, gen_gvec_ool_arg_zpz, + s->fpcr_ah ? fabs_ah_fns[a->esz] : fabs_fns[a->esz], a, 0) static gen_helper_gvec_3 * const fneg_fns[4] = { NULL, gen_helper_sve_fneg_h, From 60dd5806608ea9be3fb9b542dadf2909bd0acb76 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:39 +0000 Subject: [PATCH 1621/2892] target/arm: Handle FPCR.AH in SVE FABD Make the SVE FABD insn honour the FPCR.AH "don't negate the sign of a NaN" semantics. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 7 +++++++ target/arm/tcg/sve_helper.c | 22 ++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index ff12f650c8..29c70f054a 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1183,6 +1183,13 @@ DEF_HELPER_FLAGS_6(sve_fabd_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fabd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fabd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fabd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_6(sve_fscalbn_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 5ce7d73647..8527a7495a 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4394,9 +4394,31 @@ static inline float64 abd_d(float64 a, float64 b, float_status *s) return float64_abs(float64_sub(a, b, s)); } +/* ABD when FPCR.AH = 1: avoid flipping sign bit of a NaN result */ +static float16 ah_abd_h(float16 op1, float16 op2, float_status *stat) +{ + float16 r = float16_sub(op1, op2, stat); + return float16_is_any_nan(r) ? r : float16_abs(r); +} + +static float32 ah_abd_s(float32 op1, float32 op2, float_status *stat) +{ + float32 r = float32_sub(op1, op2, stat); + return float32_is_any_nan(r) ? r : float32_abs(r); +} + +static float64 ah_abd_d(float64 op1, float64 op2, float_status *stat) +{ + float64 r = float64_sub(op1, op2, stat); + return float64_is_any_nan(r) ? r : float64_abs(r); +} + DO_ZPZZ_FP(sve_fabd_h, uint16_t, H1_2, abd_h) DO_ZPZZ_FP(sve_fabd_s, uint32_t, H1_4, abd_s) DO_ZPZZ_FP(sve_fabd_d, uint64_t, H1_8, abd_d) +DO_ZPZZ_FP(sve_ah_fabd_h, uint16_t, H1_2, ah_abd_h) +DO_ZPZZ_FP(sve_ah_fabd_s, uint32_t, H1_4, ah_abd_s) +DO_ZPZZ_FP(sve_ah_fabd_d, uint64_t, H1_8, ah_abd_d) static inline float64 scalbn_d(float64 a, int64_t b, float_status *s) { diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 646d0171d9..0d8bd1a49c 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3789,7 +3789,7 @@ DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) DO_ZPZZ_FP(FMINNM_zpzz, aa64_sve, sve_fminnum) DO_ZPZZ_FP(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) -DO_ZPZZ_FP(FABD, aa64_sve, sve_fabd) +DO_ZPZZ_AH_FP(FABD, aa64_sve, sve_fabd, sve_ah_fabd) DO_ZPZZ_FP(FSCALE, aa64_sve, sve_fscalbn) DO_ZPZZ_FP(FDIV, aa64_sve, sve_fdiv) DO_ZPZZ_FP(FMULX, aa64_sve, sve_fmulx) From 416650ac2ba1f30ddb41bea92fe7b8b7d1f6ec73 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:40 +0000 Subject: [PATCH 1622/2892] target/arm: Handle FPCR.AH in negation steps in SVE FCADD The negation steps in FCADD must honour FPCR.AH's "don't change the sign of a NaN" semantics. Implement this in the same way we did for the base ASIMD FCADD, by encoding FPCR.AH into the SIMD data field passed to the helper and using that to decide whether to negate the values. The construction of neg_imag and neg_real were done to make it easy to apply both in parallel with two simple logical operations. This changed with FPCR.AH, which is more complex than that. Switch to an approach that follows the pseudocode more closely, by extracting the 'rot=1' parameter from the SIMD data field and changing the sign of the appropriate input value. Note that there was a naming issue with neg_imag and neg_real. They were named backward, with neg_imag being non-zero for rot=1, and vice versa. This was combined with reversed usage within the loop, so that the negation in the end turned out correct. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/sve_helper.c | 42 ++++++++++++++++++++++++---------- target/arm/tcg/translate-sve.c | 2 +- target/arm/tcg/vec_internal.h | 17 ++++++++++++++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 8527a7495a..770945a2c6 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -5131,8 +5131,8 @@ void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; - float16 neg_imag = float16_set_sign(0, simd_data(desc)); - float16 neg_real = float16_chs(neg_imag); + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 1, 1); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5144,9 +5144,15 @@ void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, i -= 2 * sizeof(float16); e0 = *(float16 *)(vn + H1_2(i)); - e1 = *(float16 *)(vm + H1_2(j)) ^ neg_real; + e1 = *(float16 *)(vm + H1_2(j)); e2 = *(float16 *)(vn + H1_2(j)); - e3 = *(float16 *)(vm + H1_2(i)) ^ neg_imag; + e3 = *(float16 *)(vm + H1_2(i)); + + if (rot) { + e3 = float16_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float16_maybe_ah_chs(e1, fpcr_ah); + } if (likely((pg >> (i & 63)) & 1)) { *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, s); @@ -5163,8 +5169,8 @@ void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; - float32 neg_imag = float32_set_sign(0, simd_data(desc)); - float32 neg_real = float32_chs(neg_imag); + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 1, 1); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5176,9 +5182,15 @@ void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, i -= 2 * sizeof(float32); e0 = *(float32 *)(vn + H1_2(i)); - e1 = *(float32 *)(vm + H1_2(j)) ^ neg_real; + e1 = *(float32 *)(vm + H1_2(j)); e2 = *(float32 *)(vn + H1_2(j)); - e3 = *(float32 *)(vm + H1_2(i)) ^ neg_imag; + e3 = *(float32 *)(vm + H1_2(i)); + + if (rot) { + e3 = float32_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float32_maybe_ah_chs(e1, fpcr_ah); + } if (likely((pg >> (i & 63)) & 1)) { *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, s); @@ -5195,8 +5207,8 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; - float64 neg_imag = float64_set_sign(0, simd_data(desc)); - float64 neg_real = float64_chs(neg_imag); + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 1, 1); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5208,9 +5220,15 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, i -= 2 * sizeof(float64); e0 = *(float64 *)(vn + H1_2(i)); - e1 = *(float64 *)(vm + H1_2(j)) ^ neg_real; + e1 = *(float64 *)(vm + H1_2(j)); e2 = *(float64 *)(vn + H1_2(j)); - e3 = *(float64 *)(vm + H1_2(i)) ^ neg_imag; + e3 = *(float64 *)(vm + H1_2(i)); + + if (rot) { + e3 = float64_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float64_maybe_ah_chs(e1, fpcr_ah); + } if (likely((pg >> (i & 63)) & 1)) { *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, s); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 0d8bd1a49c..7816b5801a 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3916,7 +3916,7 @@ static gen_helper_gvec_4_ptr * const fcadd_fns[] = { gen_helper_sve_fcadd_s, gen_helper_sve_fcadd_d, }; TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], - a->rd, a->rn, a->rm, a->pg, a->rot, + a->rd, a->rn, a->rm, a->pg, a->rot | (s->fpcr_ah << 1), a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) #define DO_FMLA(NAME, name) \ diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 094f5c169c..826791523a 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -20,6 +20,8 @@ #ifndef TARGET_ARM_VEC_INTERNAL_H #define TARGET_ARM_VEC_INTERNAL_H +#include "fpu/softfloat.h" + /* * Note that vector data is stored in host-endian 64-bit chunks, * so addressing units smaller than that needs a host-endian fixup. @@ -265,4 +267,19 @@ float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, */ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp); +static inline float16 float16_maybe_ah_chs(float16 a, bool fpcr_ah) +{ + return fpcr_ah && float16_is_any_nan(a) ? a : float16_chs(a); +} + +static inline float32 float32_maybe_ah_chs(float32 a, bool fpcr_ah) +{ + return fpcr_ah && float32_is_any_nan(a) ? a : float32_chs(a); +} + +static inline float64 float64_maybe_ah_chs(float64 a, bool fpcr_ah) +{ + return fpcr_ah && float64_is_any_nan(a) ? a : float64_chs(a); +} + #endif /* TARGET_ARM_VEC_INTERNAL_H */ From 72203eefab04a6903328807b0e3c635210031262 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:41 +0000 Subject: [PATCH 1623/2892] target/arm: Handle FPCR.AH in negation steps in FCADD The negation steps in FCADD must honour FPCR.AH's "don't change the sign of a NaN" semantics. Implement this by encoding FPCR.AH into the SIMD data field passed to the helper and using that to decide whether to negate the values. The construction of neg_imag and neg_real were done to make it easy to apply both in parallel with two simple logical operations. This changed with FPCR.AH, which is more complex than that. Switch to an approach closer to the pseudocode, where we extract the rot parameter from the SIMD data word and negate the appropriate input value. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 10 +++++-- target/arm/tcg/vec_helper.c | 54 +++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 76c41b9bdb..e8db9f39b1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6106,8 +6106,14 @@ static gen_helper_gvec_3_ptr * const f_vector_fcadd[3] = { gen_helper_gvec_fcadds, gen_helper_gvec_fcaddd, }; -TRANS_FEAT(FCADD_90, aa64_fcma, do_fp3_vector, a, 0, f_vector_fcadd) -TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1, f_vector_fcadd) +/* + * Encode FPCR.AH into the data so the helper knows whether the + * negations it does should avoid flipping the sign bit on a NaN + */ +TRANS_FEAT(FCADD_90, aa64_fcma, do_fp3_vector, a, 0 | (s->fpcr_ah << 1), + f_vector_fcadd) +TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1 | (s->fpcr_ah << 1), + f_vector_fcadd) static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) { diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 0b84a562c0..b181b9734d 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -879,19 +879,21 @@ void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, float16 *d = vd; float16 *n = vn; float16 *m = vm; - uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = neg_real ^ 1; + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract64(desc, SIMD_DATA_SHIFT + 1, 1); uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; - for (i = 0; i < opr_sz / 2; i += 2) { float16 e0 = n[H2(i)]; - float16 e1 = m[H2(i + 1)] ^ neg_imag; + float16 e1 = m[H2(i + 1)]; float16 e2 = n[H2(i + 1)]; - float16 e3 = m[H2(i)] ^ neg_real; + float16 e3 = m[H2(i)]; + + if (rot) { + e3 = float16_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float16_maybe_ah_chs(e1, fpcr_ah); + } d[H2(i)] = float16_add(e0, e1, fpst); d[H2(i + 1)] = float16_add(e2, e3, fpst); @@ -906,19 +908,21 @@ void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, float32 *d = vd; float32 *n = vn; float32 *m = vm; - uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = neg_real ^ 1; + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract64(desc, SIMD_DATA_SHIFT + 1, 1); uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; - for (i = 0; i < opr_sz / 4; i += 2) { float32 e0 = n[H4(i)]; - float32 e1 = m[H4(i + 1)] ^ neg_imag; + float32 e1 = m[H4(i + 1)]; float32 e2 = n[H4(i + 1)]; - float32 e3 = m[H4(i)] ^ neg_real; + float32 e3 = m[H4(i)]; + + if (rot) { + e3 = float32_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float32_maybe_ah_chs(e1, fpcr_ah); + } d[H4(i)] = float32_add(e0, e1, fpst); d[H4(i + 1)] = float32_add(e2, e3, fpst); @@ -933,19 +937,21 @@ void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, float64 *d = vd; float64 *n = vn; float64 *m = vm; - uint64_t neg_real = extract64(desc, SIMD_DATA_SHIFT, 1); - uint64_t neg_imag = neg_real ^ 1; + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract64(desc, SIMD_DATA_SHIFT + 1, 1); uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 63; - neg_imag <<= 63; - for (i = 0; i < opr_sz / 8; i += 2) { float64 e0 = n[i]; - float64 e1 = m[i + 1] ^ neg_imag; + float64 e1 = m[i + 1]; float64 e2 = n[i + 1]; - float64 e3 = m[i] ^ neg_real; + float64 e3 = m[i]; + + if (rot) { + e3 = float64_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float64_maybe_ah_chs(e1, fpcr_ah); + } d[i] = float64_add(e0, e1, fpst); d[i + 1] = float64_add(e2, e3, fpst); From 28048a3d180b3893912344869dfa64c1e6a7d40f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:42 +0000 Subject: [PATCH 1624/2892] target/arm: Handle FPCR.AH in FRECPS and FRSQRTS scalar insns Handle the FPCR.AH semantics that we do not change the sign of an input NaN in the FRECPS and FRSQRTS scalar insns, by providing new helper functions that do the CHS part of the operation differently. Since the extra helper functions would be very repetitive if written out longhand, we condense them and the existing non-AH helpers into being emitted via macros. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-a64.c | 115 ++++++++++++--------------------- target/arm/tcg/helper-a64.h | 6 ++ target/arm/tcg/translate-a64.c | 25 +++++-- target/arm/tcg/vec_internal.h | 18 ++++++ 4 files changed, 83 insertions(+), 81 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index ed5e4a4599..32f0647ca4 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -38,6 +38,7 @@ #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" #endif +#include "vec_internal.h" /* C2.4.7 Multiply and divide */ /* special cases for 0 and LLONG_MIN are mandated by the standard */ @@ -208,88 +209,52 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, float_status *fpst) return -float64_lt(b, a, fpst); } -/* Reciprocal step and sqrt step. Note that unlike the A32/T32 +/* + * Reciprocal step and sqrt step. Note that unlike the A32/T32 * versions, these do a fully fused multiply-add or * multiply-add-and-halve. + * The FPCR.AH == 1 versions need to avoid flipping the sign of NaN. */ - -uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, float_status *fpst) -{ - a = float16_squash_input_denormal(a, fpst); - b = float16_squash_input_denormal(b, fpst); - - a = float16_chs(a); - if ((float16_is_infinity(a) && float16_is_zero(b)) || - (float16_is_infinity(b) && float16_is_zero(a))) { - return float16_two; +#define DO_RECPS(NAME, CTYPE, FLOATTYPE, CHSFN) \ + CTYPE HELPER(NAME)(CTYPE a, CTYPE b, float_status *fpst) \ + { \ + a = FLOATTYPE ## _squash_input_denormal(a, fpst); \ + b = FLOATTYPE ## _squash_input_denormal(b, fpst); \ + a = FLOATTYPE ## _ ## CHSFN(a); \ + if ((FLOATTYPE ## _is_infinity(a) && FLOATTYPE ## _is_zero(b)) || \ + (FLOATTYPE ## _is_infinity(b) && FLOATTYPE ## _is_zero(a))) { \ + return FLOATTYPE ## _two; \ + } \ + return FLOATTYPE ## _muladd(a, b, FLOATTYPE ## _two, 0, fpst); \ } - return float16_muladd(a, b, float16_two, 0, fpst); -} -float32 HELPER(recpsf_f32)(float32 a, float32 b, float_status *fpst) -{ - a = float32_squash_input_denormal(a, fpst); - b = float32_squash_input_denormal(b, fpst); +DO_RECPS(recpsf_f16, uint32_t, float16, chs) +DO_RECPS(recpsf_f32, float32, float32, chs) +DO_RECPS(recpsf_f64, float64, float64, chs) +DO_RECPS(recpsf_ah_f16, uint32_t, float16, ah_chs) +DO_RECPS(recpsf_ah_f32, float32, float32, ah_chs) +DO_RECPS(recpsf_ah_f64, float64, float64, ah_chs) - a = float32_chs(a); - if ((float32_is_infinity(a) && float32_is_zero(b)) || - (float32_is_infinity(b) && float32_is_zero(a))) { - return float32_two; - } - return float32_muladd(a, b, float32_two, 0, fpst); -} +#define DO_RSQRTSF(NAME, CTYPE, FLOATTYPE, CHSFN) \ + CTYPE HELPER(NAME)(CTYPE a, CTYPE b, float_status *fpst) \ + { \ + a = FLOATTYPE ## _squash_input_denormal(a, fpst); \ + b = FLOATTYPE ## _squash_input_denormal(b, fpst); \ + a = FLOATTYPE ## _ ## CHSFN(a); \ + if ((FLOATTYPE ## _is_infinity(a) && FLOATTYPE ## _is_zero(b)) || \ + (FLOATTYPE ## _is_infinity(b) && FLOATTYPE ## _is_zero(a))) { \ + return FLOATTYPE ## _one_point_five; \ + } \ + return FLOATTYPE ## _muladd_scalbn(a, b, FLOATTYPE ## _three, \ + -1, 0, fpst); \ + } \ -float64 HELPER(recpsf_f64)(float64 a, float64 b, float_status *fpst) -{ - a = float64_squash_input_denormal(a, fpst); - b = float64_squash_input_denormal(b, fpst); - - a = float64_chs(a); - if ((float64_is_infinity(a) && float64_is_zero(b)) || - (float64_is_infinity(b) && float64_is_zero(a))) { - return float64_two; - } - return float64_muladd(a, b, float64_two, 0, fpst); -} - -uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, float_status *fpst) -{ - a = float16_squash_input_denormal(a, fpst); - b = float16_squash_input_denormal(b, fpst); - - a = float16_chs(a); - if ((float16_is_infinity(a) && float16_is_zero(b)) || - (float16_is_infinity(b) && float16_is_zero(a))) { - return float16_one_point_five; - } - return float16_muladd_scalbn(a, b, float16_three, -1, 0, fpst); -} - -float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, float_status *fpst) -{ - a = float32_squash_input_denormal(a, fpst); - b = float32_squash_input_denormal(b, fpst); - - a = float32_chs(a); - if ((float32_is_infinity(a) && float32_is_zero(b)) || - (float32_is_infinity(b) && float32_is_zero(a))) { - return float32_one_point_five; - } - return float32_muladd_scalbn(a, b, float32_three, -1, 0, fpst); -} - -float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, float_status *fpst) -{ - a = float64_squash_input_denormal(a, fpst); - b = float64_squash_input_denormal(b, fpst); - - a = float64_chs(a); - if ((float64_is_infinity(a) && float64_is_zero(b)) || - (float64_is_infinity(b) && float64_is_zero(a))) { - return float64_one_point_five; - } - return float64_muladd_scalbn(a, b, float64_three, -1, 0, fpst); -} +DO_RSQRTSF(rsqrtsf_f16, uint32_t, float16, chs) +DO_RSQRTSF(rsqrtsf_f32, float32, float32, chs) +DO_RSQRTSF(rsqrtsf_f64, float64, float64, chs) +DO_RSQRTSF(rsqrtsf_ah_f16, uint32_t, float16, ah_chs) +DO_RSQRTSF(rsqrtsf_ah_f32, float32, float32, ah_chs) +DO_RSQRTSF(rsqrtsf_ah_f64, float64, float64, ah_chs) /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ uint32_t HELPER(frecpx_f16)(uint32_t a, float_status *fpst) diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index ae0424f6de..85023465b7 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -38,9 +38,15 @@ DEF_HELPER_FLAGS_3(neon_cgt_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) DEF_HELPER_FLAGS_3(recpsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) DEF_HELPER_FLAGS_3(recpsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(recpsf_ah_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(recpsf_ah_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(recpsf_ah_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_ah_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_ah_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_ah_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, fpst) DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, fpst) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index e8db9f39b1..1b6d8598b8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5239,11 +5239,12 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f, FPST_A64_F16 : FPST_A64); } -static bool do_fp3_scalar_ah(DisasContext *s, arg_rrr_e *a, const FPScalar *f, - int mergereg) +static bool do_fp3_scalar_ah_2fn(DisasContext *s, arg_rrr_e *a, + const FPScalar *fnormal, const FPScalar *fah, + int mergereg) { - return do_fp3_scalar_with_fpsttype(s, a, f, mergereg, - select_ah_fpst(s, a->esz)); + return do_fp3_scalar_with_fpsttype(s, a, s->fpcr_ah ? fah : fnormal, + mergereg, select_ah_fpst(s, a->esz)); } /* Some insns need to call different helpers when FPCR.AH == 1 */ @@ -5464,14 +5465,26 @@ static const FPScalar f_scalar_frecps = { gen_helper_recpsf_f32, gen_helper_recpsf_f64, }; -TRANS(FRECPS_s, do_fp3_scalar_ah, a, &f_scalar_frecps, a->rn) +static const FPScalar f_scalar_ah_frecps = { + gen_helper_recpsf_ah_f16, + gen_helper_recpsf_ah_f32, + gen_helper_recpsf_ah_f64, +}; +TRANS(FRECPS_s, do_fp3_scalar_ah_2fn, a, + &f_scalar_frecps, &f_scalar_ah_frecps, a->rn) static const FPScalar f_scalar_frsqrts = { gen_helper_rsqrtsf_f16, gen_helper_rsqrtsf_f32, gen_helper_rsqrtsf_f64, }; -TRANS(FRSQRTS_s, do_fp3_scalar_ah, a, &f_scalar_frsqrts, a->rn) +static const FPScalar f_scalar_ah_frsqrts = { + gen_helper_rsqrtsf_ah_f16, + gen_helper_rsqrtsf_ah_f32, + gen_helper_rsqrtsf_ah_f64, +}; +TRANS(FRSQRTS_s, do_fp3_scalar_ah_2fn, a, + &f_scalar_frsqrts, &f_scalar_ah_frsqrts, a->rn) static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, const FPScalar *f, bool swap) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 826791523a..6b93b5aeb9 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -267,6 +267,24 @@ float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, */ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp); +/* + * Negate as for FPCR.AH=1 -- do not negate NaNs. + */ +static inline float16 float16_ah_chs(float16 a) +{ + return float16_is_any_nan(a) ? a : float16_chs(a); +} + +static inline float32 float32_ah_chs(float32 a) +{ + return float32_is_any_nan(a) ? a : float32_chs(a); +} + +static inline float64 float64_ah_chs(float64 a) +{ + return float64_is_any_nan(a) ? a : float64_chs(a); +} + static inline float16 float16_maybe_ah_chs(float16 a, bool fpcr_ah) { return fpcr_ah && float16_is_any_nan(a) ? a : float16_chs(a); From fdf89638dc1ca42222685d06dc0c465e4735cf44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:43 +0000 Subject: [PATCH 1625/2892] target/arm: Handle FPCR.AH in FRECPS and FRSQRTS vector insns Handle the FPCR.AH "don't negate the sign of a NaN" semantics in the vector versions of FRECPS and FRSQRTS, by implementing new vector wrappers that call the _ah_ scalar helpers. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 14 ++++++++++++++ target/arm/tcg/translate-a64.c | 21 ++++++++++++++++----- target/arm/tcg/translate-sve.c | 7 ++++++- target/arm/tcg/vec_helper.c | 8 ++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 29c70f054a..a2e96a498d 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -980,6 +980,20 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_recps_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_recps_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_recps_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_rsqrts_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_rsqrts_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_rsqrts_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(gvec_ah_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ah_fmax_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1b6d8598b8..50c207c9a1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5767,10 +5767,11 @@ static bool do_fp3_vector_2fn(DisasContext *s, arg_qrrr_e *a, int data, return do_fp3_vector(s, a, data, s->fpcr_ah ? fah : fnormal); } -static bool do_fp3_vector_ah(DisasContext *s, arg_qrrr_e *a, int data, - gen_helper_gvec_3_ptr * const f[3]) +static bool do_fp3_vector_ah_2fn(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fnormal[3], + gen_helper_gvec_3_ptr * const fah[3]) { - return do_fp3_vector_with_fpsttype(s, a, data, f, + return do_fp3_vector_with_fpsttype(s, a, data, s->fpcr_ah ? fah : fnormal, select_ah_fpst(s, a->esz)); } @@ -5913,14 +5914,24 @@ static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = { gen_helper_gvec_recps_s, gen_helper_gvec_recps_d, }; -TRANS(FRECPS_v, do_fp3_vector_ah, a, 0, f_vector_frecps) +static gen_helper_gvec_3_ptr * const f_vector_ah_frecps[3] = { + gen_helper_gvec_ah_recps_h, + gen_helper_gvec_ah_recps_s, + gen_helper_gvec_ah_recps_d, +}; +TRANS(FRECPS_v, do_fp3_vector_ah_2fn, a, 0, f_vector_frecps, f_vector_ah_frecps) static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = { gen_helper_gvec_rsqrts_h, gen_helper_gvec_rsqrts_s, gen_helper_gvec_rsqrts_d, }; -TRANS(FRSQRTS_v, do_fp3_vector_ah, a, 0, f_vector_frsqrts) +static gen_helper_gvec_3_ptr * const f_vector_ah_frsqrts[3] = { + gen_helper_gvec_ah_rsqrts_h, + gen_helper_gvec_ah_rsqrts_s, + gen_helper_gvec_ah_rsqrts_d, +}; +TRANS(FRSQRTS_v, do_fp3_vector_ah_2fn, a, 0, f_vector_frsqrts, f_vector_ah_frsqrts) static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = { gen_helper_gvec_faddp_h, diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7816b5801a..50f16d5aff 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3741,7 +3741,12 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) NULL, gen_helper_gvec_##name##_h, \ gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ }; \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_ah_arg_zzz, name##_fns[a->esz], a, 0) + static gen_helper_gvec_3_ptr * const name##_ah_fns[4] = { \ + NULL, gen_helper_gvec_ah_##name##_h, \ + gen_helper_gvec_ah_##name##_s, gen_helper_gvec_ah_##name##_d \ + }; \ + TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_ah_arg_zzz, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz], a, 0) DO_FP3(FADD_zzz, fadd) DO_FP3(FSUB_zzz, fsub) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index b181b9734d..e4c519f9e3 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1477,6 +1477,14 @@ DO_3OP(gvec_rsqrts_h, helper_rsqrtsf_f16, float16) DO_3OP(gvec_rsqrts_s, helper_rsqrtsf_f32, float32) DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) +DO_3OP(gvec_ah_recps_h, helper_recpsf_ah_f16, float16) +DO_3OP(gvec_ah_recps_s, helper_recpsf_ah_f32, float32) +DO_3OP(gvec_ah_recps_d, helper_recpsf_ah_f64, float64) + +DO_3OP(gvec_ah_rsqrts_h, helper_rsqrtsf_ah_f16, float16) +DO_3OP(gvec_ah_rsqrts_s, helper_rsqrtsf_ah_f32, float32) +DO_3OP(gvec_ah_rsqrts_d, helper_rsqrtsf_ah_f64, float64) + DO_3OP(gvec_ah_fmax_h, helper_vfp_ah_maxh, float16) DO_3OP(gvec_ah_fmax_s, helper_vfp_ah_maxs, float32) DO_3OP(gvec_ah_fmax_d, helper_vfp_ah_maxd, float64) From b85d8684c5f6be7d52355e1c8fc7444ee46a8237 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:44 +0000 Subject: [PATCH 1626/2892] target/arm: Handle FPCR.AH in negation step in FMLS (indexed) Handle the FPCR.AH "don't negate the sign of a NaN" semantics in FMLS (indexed). We do this by creating 6 new helpers, which allow us to do the negation either by XOR (for AH=0) or by muladd flags (for AH=1). Signed-off-by: Peter Maydell [PMM: Mostly from RTH's patch; error in index order into fns[][] fixed] Reviewed-by: Richard Henderson --- target/arm/helper.h | 14 ++++++++++++++ target/arm/tcg/translate-a64.c | 17 +++++++++++------ target/arm/tcg/translate-sve.c | 31 +++++++++++++++++-------------- target/arm/tcg/vec_helper.c | 24 +++++++++++++++--------- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 43505d5fed..be47edff89 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -813,6 +813,20 @@ DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_uqadd_h, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 50c207c9a1..dc35e5d896 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6726,10 +6726,16 @@ TRANS(FMULX_vi, do_fp3_vector_idx, a, f_vector_idx_fmulx) static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) { - static gen_helper_gvec_4_ptr * const fns[3] = { - gen_helper_gvec_fmla_idx_h, - gen_helper_gvec_fmla_idx_s, - gen_helper_gvec_fmla_idx_d, + static gen_helper_gvec_4_ptr * const fns[3][3] = { + { gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, + gen_helper_gvec_fmla_idx_d }, + { gen_helper_gvec_fmls_idx_h, + gen_helper_gvec_fmls_idx_s, + gen_helper_gvec_fmls_idx_d }, + { gen_helper_gvec_ah_fmls_idx_h, + gen_helper_gvec_ah_fmls_idx_s, + gen_helper_gvec_ah_fmls_idx_d }, }; MemOp esz = a->esz; int check = fp_access_check_vector_hsd(s, a->q, esz); @@ -6740,8 +6746,7 @@ static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, esz == MO_16 ? FPST_A64_F16 : FPST_A64, - (a->idx << 1) | neg, - fns[esz - 1]); + a->idx, fns[neg ? 1 + s->fpcr_ah : 0][esz - 1]); return true; } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 50f16d5aff..e81e996c56 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3524,21 +3524,24 @@ DO_SVE2_RRXR_ROT(CDOT_zzxw_d, gen_helper_sve2_cdot_idx_d) *** SVE Floating Point Multiply-Add Indexed Group */ -static bool do_FMLA_zzxz(DisasContext *s, arg_rrxr_esz *a, bool sub) -{ - static gen_helper_gvec_4_ptr * const fns[4] = { - NULL, - gen_helper_gvec_fmla_idx_h, - gen_helper_gvec_fmla_idx_s, - gen_helper_gvec_fmla_idx_d, - }; - return gen_gvec_fpst_zzzz(s, fns[a->esz], a->rd, a->rn, a->rm, a->ra, - (a->index << 1) | sub, - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); -} +static gen_helper_gvec_4_ptr * const fmla_idx_fns[4] = { + NULL, gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, gen_helper_gvec_fmla_idx_d +}; +TRANS_FEAT(FMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, + fmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -TRANS_FEAT(FMLA_zzxz, aa64_sve, do_FMLA_zzxz, a, false) -TRANS_FEAT(FMLS_zzxz, aa64_sve, do_FMLA_zzxz, a, true) +static gen_helper_gvec_4_ptr * const fmls_idx_fns[4][2] = { + { NULL, NULL }, + { gen_helper_gvec_fmls_idx_h, gen_helper_gvec_ah_fmls_idx_h }, + { gen_helper_gvec_fmls_idx_s, gen_helper_gvec_ah_fmls_idx_s }, + { gen_helper_gvec_fmls_idx_d, gen_helper_gvec_ah_fmls_idx_d }, +}; +TRANS_FEAT(FMLS_zzxz, aa64_sve, gen_gvec_fpst_zzzz, + fmls_idx_fns[a->esz][s->fpcr_ah], + a->rd, a->rn, a->rm, a->ra, a->index, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Multiply Indexed Group diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index e4c519f9e3..ae3cb50fa2 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1680,29 +1680,35 @@ DO_FMUL_IDX(gvec_fmls_nf_idx_s, float32_sub, float32_mul, float32, H4) #undef DO_FMUL_IDX -#define DO_FMLA_IDX(NAME, TYPE, H) \ +#define DO_FMLA_IDX(NAME, TYPE, H, NEGX, NEGF) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ float_status *stat, uint32_t desc) \ { \ intptr_t i, j, oprsz = simd_oprsz(desc); \ intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ - TYPE op1_neg = extract32(desc, SIMD_DATA_SHIFT, 1); \ - intptr_t idx = desc >> (SIMD_DATA_SHIFT + 1); \ + intptr_t idx = simd_data(desc); \ TYPE *d = vd, *n = vn, *m = vm, *a = va; \ - op1_neg <<= (8 * sizeof(TYPE) - 1); \ for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ TYPE mm = m[H(i + idx)]; \ for (j = 0; j < segment; j++) { \ - d[i + j] = TYPE##_muladd(n[i + j] ^ op1_neg, \ - mm, a[i + j], 0, stat); \ + d[i + j] = TYPE##_muladd(n[i + j] ^ NEGX, mm, \ + a[i + j], NEGF, stat); \ } \ } \ clear_tail(d, oprsz, simd_maxsz(desc)); \ } -DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2) -DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4) -DO_FMLA_IDX(gvec_fmla_idx_d, float64, H8) +DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2, 0, 0) +DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4, 0, 0) +DO_FMLA_IDX(gvec_fmla_idx_d, float64, H8, 0, 0) + +DO_FMLA_IDX(gvec_fmls_idx_h, float16, H2, INT16_MIN, 0) +DO_FMLA_IDX(gvec_fmls_idx_s, float32, H4, INT32_MIN, 0) +DO_FMLA_IDX(gvec_fmls_idx_d, float64, H8, INT64_MIN, 0) + +DO_FMLA_IDX(gvec_ah_fmls_idx_h, float16, H2, 0, float_muladd_negate_product) +DO_FMLA_IDX(gvec_ah_fmls_idx_s, float32, H4, 0, float_muladd_negate_product) +DO_FMLA_IDX(gvec_ah_fmls_idx_d, float64, H8, 0, float_muladd_negate_product) #undef DO_FMLA_IDX From 1fae4f5e9f48f1620c20f33eb1c673276a301548 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:45 +0000 Subject: [PATCH 1627/2892] target/arm: Handle FPCR.AH in negation in FMLS (vector) Handle the FPCR.AH "don't negate the sign of a NaN" semantics in FMLS (vector), by implementing a new set of helpers for the AH=1 case. The float_muladd_negate_product flag produces the same result as negating either of the multiplication operands, assuming neither of the operands are NaNs. But since FEAT_AFP does not negate NaNs, this behaviour is exactly what we need. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper.h | 4 ++++ target/arm/tcg/translate-a64.c | 7 ++++++- target/arm/tcg/vec_helper.c | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index be47edff89..f0a783b708 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -782,6 +782,10 @@ DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index dc35e5d896..3ab84611a6 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5860,7 +5860,12 @@ static gen_helper_gvec_3_ptr * const f_vector_fmls[3] = { gen_helper_gvec_vfms_s, gen_helper_gvec_vfms_d, }; -TRANS(FMLS_v, do_fp3_vector, a, 0, f_vector_fmls) +static gen_helper_gvec_3_ptr * const f_vector_fmls_ah[3] = { + gen_helper_gvec_ah_vfms_h, + gen_helper_gvec_ah_vfms_s, + gen_helper_gvec_ah_vfms_d, +}; +TRANS(FMLS_v, do_fp3_vector_2fn, a, 0, f_vector_fmls, f_vector_fmls_ah) static gen_helper_gvec_3_ptr * const f_vector_fcmeq[3] = { gen_helper_gvec_fceq_h, diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index ae3cb50fa2..fc3e6587b8 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1558,6 +1558,24 @@ static float64 float64_mulsub_f(float64 dest, float64 op1, float64 op2, return float64_muladd(float64_chs(op1), op2, dest, 0, stat); } +static float16 float16_ah_mulsub_f(float16 dest, float16 op1, float16 op2, + float_status *stat) +{ + return float16_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + +static float32 float32_ah_mulsub_f(float32 dest, float32 op1, float32 op2, + float_status *stat) +{ + return float32_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + +static float64 float64_ah_mulsub_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + #define DO_MULADD(NAME, FUNC, TYPE) \ void HELPER(NAME)(void *vd, void *vn, void *vm, \ float_status *stat, uint32_t desc) \ @@ -1584,6 +1602,10 @@ DO_MULADD(gvec_vfms_h, float16_mulsub_f, float16) DO_MULADD(gvec_vfms_s, float32_mulsub_f, float32) DO_MULADD(gvec_vfms_d, float64_mulsub_f, float64) +DO_MULADD(gvec_ah_vfms_h, float16_ah_mulsub_f, float16) +DO_MULADD(gvec_ah_vfms_s, float32_ah_mulsub_f, float32) +DO_MULADD(gvec_ah_vfms_d, float64_ah_mulsub_f, float64) + /* For the indexed ops, SVE applies the index per 128-bit vector segment. * For AdvSIMD, there is of course only one such vector segment. */ From 51330e58480b05cd39a9b913be9f83a0530471cb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:46 +0000 Subject: [PATCH 1628/2892] target/arm: Handle FPCR.AH in negation step in SVE FMLS (vector) Handle the FPCR.AH "don't negate the sign of a NaN" semantics fro the SVE FMLS (vector) insns, by providing new helpers for the AH=1 case which end up passing fpcr_ah = true to the do_fmla_zpzzz_* functions that do the work. The float*_muladd functions have a flags argument that can perform optional negation of various operand. We don't use that for "normal" arm fmla, because the muladd flags are not applied when an input is a NaN. But since FEAT_AFP does not negate NaNs, this behaviour is exactly what we need. The non-AH helpers pass in a zero flags argument and control the negation via the neg1 and neg3 arguments; the AH helpers always pass in neg1 and neg3 as zero and control the negation via the flags argument. This allows us to avoid conditional branches within the inner loop. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-sve.h | 21 ++++++++ target/arm/tcg/sve_helper.c | 99 +++++++++++++++++++++++++++------- target/arm/tcg/translate-sve.c | 18 ++++--- 3 files changed, 114 insertions(+), 24 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index a2e96a498d..0b1b588783 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1475,6 +1475,27 @@ DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 770945a2c6..90d4defc0d 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4802,7 +4802,7 @@ DO_ZPZ_FP(flogb_d, float64, H1_8, do_float64_logb_as_int) static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, - uint16_t neg1, uint16_t neg3) + uint16_t neg1, uint16_t neg3, int flags) { intptr_t i = simd_oprsz(desc); uint64_t *g = vg; @@ -4817,7 +4817,7 @@ static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; e2 = *(uint16_t *)(vm + H1_2(i)); e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; - r = float16_muladd(e1, e2, e3, 0, status); + r = float16_muladd(e1, e2, e3, flags, status); *(uint16_t *)(vd + H1_2(i)) = r; } } while (i & 63); @@ -4827,30 +4827,51 @@ static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, void HELPER(sve_fmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, 0); } void HELPER(sve_fmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0, 0); } void HELPER(sve_fnmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000, 0); } void HELPER(sve_fnmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0x8000); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0x8000, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); } static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, - uint32_t neg1, uint32_t neg3) + uint32_t neg1, uint32_t neg3, int flags) { intptr_t i = simd_oprsz(desc); uint64_t *g = vg; @@ -4865,7 +4886,7 @@ static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, e1 = *(uint32_t *)(vn + H1_4(i)) ^ neg1; e2 = *(uint32_t *)(vm + H1_4(i)); e3 = *(uint32_t *)(va + H1_4(i)) ^ neg3; - r = float32_muladd(e1, e2, e3, 0, status); + r = float32_muladd(e1, e2, e3, flags, status); *(uint32_t *)(vd + H1_4(i)) = r; } } while (i & 63); @@ -4875,30 +4896,51 @@ static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, void HELPER(sve_fmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, 0); } void HELPER(sve_fmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0, 0); } void HELPER(sve_fnmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0x80000000); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0x80000000, 0); } void HELPER(sve_fnmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0x80000000); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0x80000000, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); } static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, - uint64_t neg1, uint64_t neg3) + uint64_t neg1, uint64_t neg3, int flags) { intptr_t i = simd_oprsz(desc); uint64_t *g = vg; @@ -4913,7 +4955,7 @@ static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, e1 = *(uint64_t *)(vn + i) ^ neg1; e2 = *(uint64_t *)(vm + i); e3 = *(uint64_t *)(va + i) ^ neg3; - r = float64_muladd(e1, e2, e3, 0, status); + r = float64_muladd(e1, e2, e3, flags, status); *(uint64_t *)(vd + i) = r; } } while (i & 63); @@ -4923,25 +4965,46 @@ static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, void HELPER(sve_fmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, 0); } void HELPER(sve_fmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, 0); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, 0, 0); } void HELPER(sve_fnmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, INT64_MIN); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, INT64_MIN, 0); } void HELPER(sve_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, INT64_MIN); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, INT64_MIN, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); } /* Two operand floating-point comparison controlled by a predicate. diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index e81e996c56..17016854d8 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3927,19 +3927,25 @@ TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], a->rd, a->rn, a->rm, a->pg, a->rot | (s->fpcr_ah << 1), a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -#define DO_FMLA(NAME, name) \ +#define DO_FMLA(NAME, name, ah_name) \ static gen_helper_gvec_5_ptr * const name##_fns[4] = { \ NULL, gen_helper_sve_##name##_h, \ gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ }; \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, name##_fns[a->esz], \ + static gen_helper_gvec_5_ptr * const name##_ah_fns[4] = { \ + NULL, gen_helper_sve_##ah_name##_h, \ + gen_helper_sve_##ah_name##_s, gen_helper_sve_##ah_name##_d \ + }; \ + TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz], \ a->rd, a->rn, a->rm, a->ra, a->pg, 0, \ a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -DO_FMLA(FMLA_zpzzz, fmla_zpzzz) -DO_FMLA(FMLS_zpzzz, fmls_zpzzz) -DO_FMLA(FNMLA_zpzzz, fnmla_zpzzz) -DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz) +/* We don't need an ah_fmla_zpzzz because fmla doesn't negate anything */ +DO_FMLA(FMLA_zpzzz, fmla_zpzzz, fmla_zpzzz) +DO_FMLA(FMLS_zpzzz, fmls_zpzzz, ah_fmls_zpzzz) +DO_FMLA(FNMLA_zpzzz, fnmla_zpzzz, ah_fnmla_zpzzz) +DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz, ah_fnmls_zpzzz) #undef DO_FMLA From 6dcd51ccf6815578bd34ea64a33a1eda9cc324e5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:47 +0000 Subject: [PATCH 1629/2892] target/arm: Handle FPCR.AH in SVE FTSSEL The negation step in the SVE FTSSEL insn mustn't negate a NaN when FPCR.AH is set. Pass FPCR.AH to the helper via the SIMD data field and use that to determine whether to do the negation. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/sve_helper.c | 18 +++++++++++++++--- target/arm/tcg/translate-sve.c | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 90d4defc0d..bf88bde8a3 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -2555,6 +2555,7 @@ void HELPER(sve_fexpa_d)(void *vd, void *vn, uint32_t desc) void HELPER(sve_ftssel_h)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 2; + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT, 1); uint16_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz; i += 1) { uint16_t nn = n[i]; @@ -2562,13 +2563,17 @@ void HELPER(sve_ftssel_h)(void *vd, void *vn, void *vm, uint32_t desc) if (mm & 1) { nn = float16_one; } - d[i] = nn ^ (mm & 2) << 14; + if (mm & 2) { + nn = float16_maybe_ah_chs(nn, fpcr_ah); + } + d[i] = nn; } } void HELPER(sve_ftssel_s)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 4; + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz; i += 1) { uint32_t nn = n[i]; @@ -2576,13 +2581,17 @@ void HELPER(sve_ftssel_s)(void *vd, void *vn, void *vm, uint32_t desc) if (mm & 1) { nn = float32_one; } - d[i] = nn ^ (mm & 2) << 30; + if (mm & 2) { + nn = float32_maybe_ah_chs(nn, fpcr_ah); + } + d[i] = nn; } } void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 8; + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz; i += 1) { uint64_t nn = n[i]; @@ -2590,7 +2599,10 @@ void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) if (mm & 1) { nn = float64_one; } - d[i] = nn ^ (mm & 2) << 62; + if (mm & 2) { + nn = float64_maybe_ah_chs(nn, fpcr_ah); + } + d[i] = nn; } } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 17016854d8..2dd4605bb2 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -1238,14 +1238,14 @@ static gen_helper_gvec_2 * const fexpa_fns[4] = { gen_helper_sve_fexpa_s, gen_helper_sve_fexpa_d, }; TRANS_FEAT_NONSTREAMING(FEXPA, aa64_sve, gen_gvec_ool_zz, - fexpa_fns[a->esz], a->rd, a->rn, 0) + fexpa_fns[a->esz], a->rd, a->rn, s->fpcr_ah) static gen_helper_gvec_3 * const ftssel_fns[4] = { NULL, gen_helper_sve_ftssel_h, gen_helper_sve_ftssel_s, gen_helper_sve_ftssel_d, }; TRANS_FEAT_NONSTREAMING(FTSSEL, aa64_sve, gen_gvec_ool_arg_zzz, - ftssel_fns[a->esz], a, 0) + ftssel_fns[a->esz], a, s->fpcr_ah) /* *** SVE Predicate Logical Operations Group From 07e6b8d7526380a8a30081354f12f48f39d3464e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:48 +0000 Subject: [PATCH 1630/2892] target/arm: Handle FPCR.AH in SVE FTMAD The negation step in the SVE FTMAD insn mustn't negate a NaN when FPCR.AH is set. Pass FPCR.AH to the helper via the SIMD data field, so we can select the correct behaviour. Because the operand is known to be negative, negating the operand is the same as taking the absolute value. Defer this to the muladd operation via flags, so that it happens after NaN detection, which is correct for FPCR.AH. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/sve_helper.c | 42 ++++++++++++++++++++++++++-------- target/arm/tcg/translate-sve.c | 3 ++- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index bf88bde8a3..c12b2600bd 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -5134,16 +5134,24 @@ void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, 0x3c00, 0xb800, 0x293a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float16); - intptr_t x = simd_data(desc); + intptr_t x = extract32(desc, SIMD_DATA_SHIFT, 3); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 3, 1); float16 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { float16 mm = m[i]; intptr_t xx = x; + int flags = 0; + if (float16_is_neg(mm)) { - mm = float16_abs(mm); + if (fpcr_ah) { + flags = float_muladd_negate_product; + } else { + mm = float16_abs(mm); + } xx += 8; } - d[i] = float16_muladd(n[i], mm, coeff[xx], 0, s); + d[i] = float16_muladd(n[i], mm, coeff[xx], flags, s); } } @@ -5157,16 +5165,24 @@ void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, 0x37cd37cc, 0x00000000, 0x00000000, 0x00000000, }; intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float32); - intptr_t x = simd_data(desc); + intptr_t x = extract32(desc, SIMD_DATA_SHIFT, 3); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 3, 1); float32 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { float32 mm = m[i]; intptr_t xx = x; + int flags = 0; + if (float32_is_neg(mm)) { - mm = float32_abs(mm); + if (fpcr_ah) { + flags = float_muladd_negate_product; + } else { + mm = float32_abs(mm); + } xx += 8; } - d[i] = float32_muladd(n[i], mm, coeff[xx], 0, s); + d[i] = float32_muladd(n[i], mm, coeff[xx], flags, s); } } @@ -5184,16 +5200,24 @@ void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, 0x3e21ee96d2641b13ull, 0xbda8f76380fbb401ull, }; intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float64); - intptr_t x = simd_data(desc); + intptr_t x = extract32(desc, SIMD_DATA_SHIFT, 3); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 3, 1); float64 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { float64 mm = m[i]; intptr_t xx = x; + int flags = 0; + if (float64_is_neg(mm)) { - mm = float64_abs(mm); + if (fpcr_ah) { + flags = float_muladd_negate_product; + } else { + mm = float64_abs(mm); + } xx += 8; } - d[i] = float64_muladd(n[i], mm, coeff[xx], 0, s); + d[i] = float64_muladd(n[i], mm, coeff[xx], flags, s); } } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2dd4605bb2..410087c3fb 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3685,7 +3685,8 @@ static gen_helper_gvec_3_ptr * const ftmad_fns[4] = { gen_helper_sve_ftmad_s, gen_helper_sve_ftmad_d, }; TRANS_FEAT_NONSTREAMING(FTMAD, aa64_sve, gen_gvec_fpst_zzz, - ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, + ftmad_fns[a->esz], a->rd, a->rn, a->rm, + a->imm | (s->fpcr_ah << 3), a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* From a7868aaa30e4df6e6040fd96217e1febe247714a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:39:49 +0000 Subject: [PATCH 1631/2892] target/arm: Handle FPCR.AH in vector FCMLA The negation step in FCMLA mustn't negate a NaN when FPCR.AH is set. Handle this by passing FPCR.AH to the helper via the SIMD data field, and use this to select whether to do the negation via XOR or via the muladd negate_product flag. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-26-richard.henderson@linaro.org [PMM: Expanded commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/vec_helper.c | 66 ++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3ab84611a6..ca06982d31 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6164,7 +6164,7 @@ static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64, - a->rot, fn[a->esz]); + a->rot | (s->fpcr_ah << 2), fn[a->esz]); return true; } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index fc3e6587b8..630513f00b 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -965,22 +965,26 @@ void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, void *va, uintptr_t opr_sz = simd_oprsz(desc); float16 *d = vd, *n = vn, *m = vm, *a = va; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - uint32_t neg_real = flip ^ neg_imag; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float16 negx_imag, negx_real; uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 15; + negx_imag = (negf_imag & ~fpcr_ah) << 15; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < opr_sz / 2; i += 2) { float16 e2 = n[H2(i + flip)]; - float16 e1 = m[H2(i + flip)] ^ neg_real; + float16 e1 = m[H2(i + flip)] ^ negx_real; float16 e4 = e2; - float16 e3 = m[H2(i + 1 - flip)] ^ neg_imag; + float16 e3 = m[H2(i + 1 - flip)] ^ negx_imag; - d[H2(i)] = float16_muladd(e2, e1, a[H2(i)], 0, fpst); - d[H2(i + 1)] = float16_muladd(e4, e3, a[H2(i + 1)], 0, fpst); + d[H2(i)] = float16_muladd(e2, e1, a[H2(i)], negf_real, fpst); + d[H2(i + 1)] = float16_muladd(e4, e3, a[H2(i + 1)], negf_imag, fpst); } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -1025,22 +1029,26 @@ void HELPER(gvec_fcmlas)(void *vd, void *vn, void *vm, void *va, uintptr_t opr_sz = simd_oprsz(desc); float32 *d = vd, *n = vn, *m = vm, *a = va; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - uint32_t neg_real = flip ^ neg_imag; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float32 negx_imag, negx_real; uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 31; + negx_imag = (negf_imag & ~fpcr_ah) << 31; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < opr_sz / 4; i += 2) { float32 e2 = n[H4(i + flip)]; - float32 e1 = m[H4(i + flip)] ^ neg_real; + float32 e1 = m[H4(i + flip)] ^ negx_real; float32 e4 = e2; - float32 e3 = m[H4(i + 1 - flip)] ^ neg_imag; + float32 e3 = m[H4(i + 1 - flip)] ^ negx_imag; - d[H4(i)] = float32_muladd(e2, e1, a[H4(i)], 0, fpst); - d[H4(i + 1)] = float32_muladd(e4, e3, a[H4(i + 1)], 0, fpst); + d[H4(i)] = float32_muladd(e2, e1, a[H4(i)], negf_real, fpst); + d[H4(i + 1)] = float32_muladd(e4, e3, a[H4(i + 1)], negf_imag, fpst); } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -1085,22 +1093,26 @@ void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, void *va, uintptr_t opr_sz = simd_oprsz(desc); float64 *d = vd, *n = vn, *m = vm, *a = va; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint64_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - uint64_t neg_real = flip ^ neg_imag; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float64 negx_real, negx_imag; uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 63; - neg_imag <<= 63; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (uint64_t)(negf_real & ~fpcr_ah) << 63; + negx_imag = (uint64_t)(negf_imag & ~fpcr_ah) << 63; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < opr_sz / 8; i += 2) { float64 e2 = n[i + flip]; - float64 e1 = m[i + flip] ^ neg_real; + float64 e1 = m[i + flip] ^ negx_real; float64 e4 = e2; - float64 e3 = m[i + 1 - flip] ^ neg_imag; + float64 e3 = m[i + 1 - flip] ^ negx_imag; - d[i] = float64_muladd(e2, e1, a[i], 0, fpst); - d[i + 1] = float64_muladd(e4, e3, a[i + 1], 0, fpst); + d[i] = float64_muladd(e2, e1, a[i], negf_real, fpst); + d[i + 1] = float64_muladd(e4, e3, a[i + 1], negf_imag, fpst); } clear_tail(d, opr_sz, simd_maxsz(desc)); } From 6d5ccfd44fdcf606ceadaab7c90d44ea7d5f4fe5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:39:50 +0000 Subject: [PATCH 1632/2892] target/arm: Handle FPCR.AH in FCMLA by index The negation step in FCMLA by index mustn't negate a NaN when FPCR.AH is set. Use the same approach as vector FCMLA of passing in FPCR.AH and using it to select whether to negate by XOR or by the muladd negate_product flag. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-27-richard.henderson@linaro.org [PMM: Expanded commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/vec_helper.c | 44 ++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ca06982d31..1846f81bf5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6916,7 +6916,7 @@ static bool trans_FCMLA_vi(DisasContext *s, arg_FCMLA_vi *a) if (fp_access_check(s)) { gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64, - (a->idx << 2) | a->rot, fn); + (s->fpcr_ah << 4) | (a->idx << 2) | a->rot, fn); } return true; } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 630513f00b..c2f98a5c67 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -995,29 +995,33 @@ void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, void *va, uintptr_t opr_sz = simd_oprsz(desc); float16 *d = vd, *n = vn, *m = vm, *a = va; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); - uint32_t neg_real = flip ^ neg_imag; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 4, 1); + uint32_t negf_real = flip ^ negf_imag; intptr_t elements = opr_sz / sizeof(float16); intptr_t eltspersegment = MIN(16 / sizeof(float16), elements); + float16 negx_imag, negx_real; intptr_t i, j; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 15; + negx_imag = (negf_imag & ~fpcr_ah) << 15; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < elements; i += eltspersegment) { float16 mr = m[H2(i + 2 * index + 0)]; float16 mi = m[H2(i + 2 * index + 1)]; - float16 e1 = neg_real ^ (flip ? mi : mr); - float16 e3 = neg_imag ^ (flip ? mr : mi); + float16 e1 = negx_real ^ (flip ? mi : mr); + float16 e3 = negx_imag ^ (flip ? mr : mi); for (j = i; j < i + eltspersegment; j += 2) { float16 e2 = n[H2(j + flip)]; float16 e4 = e2; - d[H2(j)] = float16_muladd(e2, e1, a[H2(j)], 0, fpst); - d[H2(j + 1)] = float16_muladd(e4, e3, a[H2(j + 1)], 0, fpst); + d[H2(j)] = float16_muladd(e2, e1, a[H2(j)], negf_real, fpst); + d[H2(j + 1)] = float16_muladd(e4, e3, a[H2(j + 1)], negf_imag, fpst); } } clear_tail(d, opr_sz, simd_maxsz(desc)); @@ -1059,29 +1063,33 @@ void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, void *va, uintptr_t opr_sz = simd_oprsz(desc); float32 *d = vd, *n = vn, *m = vm, *a = va; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); - uint32_t neg_real = flip ^ neg_imag; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 4, 1); + uint32_t negf_real = flip ^ negf_imag; intptr_t elements = opr_sz / sizeof(float32); intptr_t eltspersegment = MIN(16 / sizeof(float32), elements); + float32 negx_imag, negx_real; intptr_t i, j; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 31; + negx_imag = (negf_imag & ~fpcr_ah) << 31; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < elements; i += eltspersegment) { float32 mr = m[H4(i + 2 * index + 0)]; float32 mi = m[H4(i + 2 * index + 1)]; - float32 e1 = neg_real ^ (flip ? mi : mr); - float32 e3 = neg_imag ^ (flip ? mr : mi); + float32 e1 = negx_real ^ (flip ? mi : mr); + float32 e3 = negx_imag ^ (flip ? mr : mi); for (j = i; j < i + eltspersegment; j += 2) { float32 e2 = n[H4(j + flip)]; float32 e4 = e2; - d[H4(j)] = float32_muladd(e2, e1, a[H4(j)], 0, fpst); - d[H4(j + 1)] = float32_muladd(e4, e3, a[H4(j + 1)], 0, fpst); + d[H4(j)] = float32_muladd(e2, e1, a[H4(j)], negf_real, fpst); + d[H4(j + 1)] = float32_muladd(e4, e3, a[H4(j + 1)], negf_imag, fpst); } } clear_tail(d, opr_sz, simd_maxsz(desc)); From 0b5ca769cfae0039d6a71a4dd2d28845f96c4b81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:39:51 +0000 Subject: [PATCH 1633/2892] target/arm: Handle FPCR.AH in SVE FCMLA The negation step in SVE FCMLA mustn't negate a NaN when FPCR.AH is set. Use the same approach as we did for A64 FCMLA of passing in FPCR.AH and using it to select whether to negate by XOR or by the muladd negate_product flag. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-28-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 69 +++++++++++++++++++++------------- target/arm/tcg/translate-sve.c | 2 +- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index c12b2600bd..c206ca65ce 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -5347,13 +5347,18 @@ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); - unsigned rot = simd_data(desc); - bool flip = rot & 1; - float16 neg_imag, neg_real; + bool flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float16 negx_imag, negx_real; uint64_t *g = vg; - neg_imag = float16_set_sign(0, (rot & 2) != 0); - neg_real = float16_set_sign(0, rot == 1 || rot == 2); + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 15; + negx_imag = (negf_imag & ~fpcr_ah) << 15; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5370,18 +5375,18 @@ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, mi = *(float16 *)(vm + H1_2(j)); e2 = (flip ? ni : nr); - e1 = (flip ? mi : mr) ^ neg_real; + e1 = (flip ? mi : mr) ^ negx_real; e4 = e2; - e3 = (flip ? mr : mi) ^ neg_imag; + e3 = (flip ? mr : mi) ^ negx_imag; if (likely((pg >> (i & 63)) & 1)) { d = *(float16 *)(va + H1_2(i)); - d = float16_muladd(e2, e1, d, 0, status); + d = float16_muladd(e2, e1, d, negf_real, status); *(float16 *)(vd + H1_2(i)) = d; } if (likely((pg >> (j & 63)) & 1)) { d = *(float16 *)(va + H1_2(j)); - d = float16_muladd(e4, e3, d, 0, status); + d = float16_muladd(e4, e3, d, negf_imag, status); *(float16 *)(vd + H1_2(j)) = d; } } while (i & 63); @@ -5392,13 +5397,18 @@ void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); - unsigned rot = simd_data(desc); - bool flip = rot & 1; - float32 neg_imag, neg_real; + bool flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float32 negx_imag, negx_real; uint64_t *g = vg; - neg_imag = float32_set_sign(0, (rot & 2) != 0); - neg_real = float32_set_sign(0, rot == 1 || rot == 2); + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 31; + negx_imag = (negf_imag & ~fpcr_ah) << 31; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5415,18 +5425,18 @@ void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, mi = *(float32 *)(vm + H1_2(j)); e2 = (flip ? ni : nr); - e1 = (flip ? mi : mr) ^ neg_real; + e1 = (flip ? mi : mr) ^ negx_real; e4 = e2; - e3 = (flip ? mr : mi) ^ neg_imag; + e3 = (flip ? mr : mi) ^ negx_imag; if (likely((pg >> (i & 63)) & 1)) { d = *(float32 *)(va + H1_2(i)); - d = float32_muladd(e2, e1, d, 0, status); + d = float32_muladd(e2, e1, d, negf_real, status); *(float32 *)(vd + H1_2(i)) = d; } if (likely((pg >> (j & 63)) & 1)) { d = *(float32 *)(va + H1_2(j)); - d = float32_muladd(e4, e3, d, 0, status); + d = float32_muladd(e4, e3, d, negf_imag, status); *(float32 *)(vd + H1_2(j)) = d; } } while (i & 63); @@ -5437,13 +5447,18 @@ void HELPER(sve_fcmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); - unsigned rot = simd_data(desc); - bool flip = rot & 1; - float64 neg_imag, neg_real; + bool flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float64 negx_imag, negx_real; uint64_t *g = vg; - neg_imag = float64_set_sign(0, (rot & 2) != 0); - neg_real = float64_set_sign(0, rot == 1 || rot == 2); + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (uint64_t)(negf_real & ~fpcr_ah) << 63; + negx_imag = (uint64_t)(negf_imag & ~fpcr_ah) << 63; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5460,18 +5475,18 @@ void HELPER(sve_fcmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, mi = *(float64 *)(vm + H1_2(j)); e2 = (flip ? ni : nr); - e1 = (flip ? mi : mr) ^ neg_real; + e1 = (flip ? mi : mr) ^ negx_real; e4 = e2; - e3 = (flip ? mr : mi) ^ neg_imag; + e3 = (flip ? mr : mi) ^ negx_imag; if (likely((pg >> (i & 63)) & 1)) { d = *(float64 *)(va + H1_2(i)); - d = float64_muladd(e2, e1, d, 0, status); + d = float64_muladd(e2, e1, d, negf_real, status); *(float64 *)(vd + H1_2(i)) = d; } if (likely((pg >> (j & 63)) & 1)) { d = *(float64 *)(va + H1_2(j)); - d = float64_muladd(e4, e3, d, 0, status); + d = float64_muladd(e4, e3, d, negf_imag, status); *(float64 *)(vd + H1_2(j)) = d; } } while (i & 63); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 410087c3fb..6af94fedd0 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3955,7 +3955,7 @@ static gen_helper_gvec_5_ptr * const fcmla_fns[4] = { gen_helper_sve_fcmla_zpzzz_s, gen_helper_sve_fcmla_zpzzz_d, }; TRANS_FEAT(FCMLA_zpzzz, aa64_sve, gen_gvec_fpst_zzzzp, fcmla_fns[a->esz], - a->rd, a->rn, a->rm, a->ra, a->pg, a->rot, + a->rd, a->rn, a->rm, a->ra, a->pg, a->rot | (s->fpcr_ah << 2), a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_4_ptr * const fcmla_idx_fns[4] = { From 0fa4b7afd4f78ed6ec0dedffba7dc93c00ca3d83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:39:52 +0000 Subject: [PATCH 1634/2892] target/arm: Handle FPCR.AH in FMLSL (by element and vector) Handle FPCR.AH's requirement to not negate the sign of a NaN in FMLSL by element and vector, using the usual trick of negating by XOR when AH=0 and by muladd flags when AH=1. Since we have the CPUARMState* in the helper anyway, we can look directly at env->vfp.fpcr and don't need toa pass in the FPCR.AH value via the SIMD data word. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-31-richard.henderson@linaro.org [PMM: commit message tweaked] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 71 ++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index c2f98a5c67..5f0656f34c 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2124,27 +2124,24 @@ static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2) */ static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, - uint32_t desc, bool fz16) + uint64_t negx, int negf, uint32_t desc, bool fz16) { intptr_t i, oprsz = simd_oprsz(desc); - int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); int is_q = oprsz == 16; uint64_t n_4, m_4; - /* Pre-load all of the f16 data, avoiding overlap issues. */ - n_4 = load4_f16(vn, is_q, is_2); + /* + * Pre-load all of the f16 data, avoiding overlap issues. + * Negate all inputs for AH=0 FMLSL at once. + */ + n_4 = load4_f16(vn, is_q, is_2) ^ negx; m_4 = load4_f16(vm, is_q, is_2); - /* Negate all inputs for FMLSL at once. */ - if (is_s) { - n_4 ^= 0x8000800080008000ull; - } - for (i = 0; i < oprsz / 4; i++) { float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); float32 m_1 = float16_to_float32_by_bits(m_4 >> (i * 16), fz16); - d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], negf, fpst); } clear_tail(d, oprsz, simd_maxsz(desc)); } @@ -2152,14 +2149,28 @@ static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { - do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc, + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = is_s ? 0x8000800080008000ull : 0; + + do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, negx, 0, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { - do_fmlal(vd, vn, vm, &env->vfp.fp_status_a64, desc, + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = 0; + int negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000800080008000ull; + } + } + do_fmlal(vd, vn, vm, &env->vfp.fp_status_a64, negx, negf, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64)); } @@ -2184,29 +2195,25 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, } static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, - uint32_t desc, bool fz16) + uint64_t negx, int negf, uint32_t desc, bool fz16) { intptr_t i, oprsz = simd_oprsz(desc); - int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); int index = extract32(desc, SIMD_DATA_SHIFT + 2, 3); int is_q = oprsz == 16; uint64_t n_4; float32 m_1; - /* Pre-load all of the f16 data, avoiding overlap issues. */ - n_4 = load4_f16(vn, is_q, is_2); - - /* Negate all inputs for FMLSL at once. */ - if (is_s) { - n_4 ^= 0x8000800080008000ull; - } - + /* + * Pre-load all of the f16 data, avoiding overlap issues. + * Negate all inputs for AH=0 FMLSL at once. + */ + n_4 = load4_f16(vn, is_q, is_2) ^ negx; m_1 = float16_to_float32_by_bits(((float16 *)vm)[H2(index)], fz16); for (i = 0; i < oprsz / 4; i++) { float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); - d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], negf, fpst); } clear_tail(d, oprsz, simd_maxsz(desc)); } @@ -2214,14 +2221,28 @@ static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { - do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc, + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = is_s ? 0x8000800080008000ull : 0; + + do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, negx, 0, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, CPUARMState *env, uint32_t desc) { - do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status_a64, desc, + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = 0; + int negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000800080008000ull; + } + } + do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status_a64, negx, negf, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64)); } From f67a16e7d754cde9a55dd1e26ea4db48701b6fb9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:39:53 +0000 Subject: [PATCH 1635/2892] target/arm: Handle FPCR.AH in SVE FMLSL (indexed) Handle FPCR.AH's requirement to not negate the sign of a NaN in SVE FMLSL (indexed), using the usual trick of negating by XOR when AH=0 and by muladd flags when AH=1. Since we have the CPUARMState* in the helper anyway, we can look directly at env->vfp.fpcr and don't need toa pass in the FPCR.AH value via the SIMD data word. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-32-richard.henderson@linaro.org [PMM: commit message tweaked] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 5f0656f34c..42bb43acd7 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2250,23 +2250,32 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, CPUARMState *env, uint32_t desc) { intptr_t i, j, oprsz = simd_oprsz(desc); - uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); float_status *status = &env->vfp.fp_status_a64; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64); + int negx = 0, negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000; + } + } for (i = 0; i < oprsz; i += 16) { float16 mm_16 = *(float16 *)(vm + i + idx); float32 mm = float16_to_float32_by_bits(mm_16, fz16); for (j = 0; j < 16; j += sizeof(float32)) { - float16 nn_16 = *(float16 *)(vn + H1_2(i + j + sel)) ^ negn; + float16 nn_16 = *(float16 *)(vn + H1_2(i + j + sel)) ^ negx; float32 nn = float16_to_float32_by_bits(nn_16, fz16); float32 aa = *(float32 *)(va + H1_4(i + j)); *(float32 *)(vd + H1_4(i + j)) = - float32_muladd(nn, mm, aa, 0, status); + float32_muladd(nn, mm, aa, negf, status); } } } From a66c4585fff70ffc4a61e0f5f5528320a55cd9cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:39:54 +0000 Subject: [PATCH 1636/2892] target/arm: Handle FPCR.AH in SVE FMLSLB, FMLSLT (vectors) Handle FPCR.AH's requirement to not negate the sign of a NaN in SVE FMLSL (indexed), using the usual trick of negating by XOR when AH=0 and by muladd flags when AH=1. Since we have the CPUARMState* in the helper anyway, we can look directly at env->vfp.fpcr and don't need toa pass in the FPCR.AH value via the SIMD data word. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-33-richard.henderson@linaro.org [PMM: tweaked commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 42bb43acd7..aefcd07ef0 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2178,19 +2178,28 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, CPUARMState *env, uint32_t desc) { intptr_t i, oprsz = simd_oprsz(desc); - uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); float_status *status = &env->vfp.fp_status_a64; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64); + int negx = 0, negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000; + } + } for (i = 0; i < oprsz; i += sizeof(float32)) { - float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negn; + float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negx; float16 mm_16 = *(float16 *)(vm + H1_2(i + sel)); float32 nn = float16_to_float32_by_bits(nn_16, fz16); float32 mm = float16_to_float32_by_bits(mm_16, fz16); float32 aa = *(float32 *)(va + H1_4(i)); - *(float32 *)(vd + H1_4(i)) = float32_muladd(nn, mm, aa, 0, status); + *(float32 *)(vd + H1_4(i)) = float32_muladd(nn, mm, aa, negf, status); } } From d38a57a3f1ea66c4338a10d70c032741e8786c51 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:55 +0000 Subject: [PATCH 1637/2892] target/arm: Enable FEAT_AFP for '-cpu max' Now that we have completed the handling for FPCR.{AH,FIZ,NEP}, we can enable FEAT_AFP for '-cpu max', and document that we support it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 60176d0859..63b4cdf5fb 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -20,6 +20,7 @@ the following architecture extensions: - FEAT_AA64EL3 (Support for AArch64 at EL3) - FEAT_AdvSIMD (Advanced SIMD Extension) - FEAT_AES (AESD and AESE instructions) +- FEAT_AFP (Alternate floating-point behavior) - FEAT_Armv9_Crypto (Armv9 Cryptographic Extension) - FEAT_ASID16 (16 bit ASID) - FEAT_BBM at level 2 (Translation table break-before-make levels) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 93573ceeb1..0bc68aac17 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1218,6 +1218,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* FEAT_ETS2 */ t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ + t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* FEAT_AFP */ t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* FEAT_CMOW */ cpu->isar.id_aa64mmfr1 = t; From 0ff5c021f04d7dbaec174451e7a0dab3822b1b0d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:56 +0000 Subject: [PATCH 1638/2892] target/arm: Plumb FEAT_RPRES frecpe and frsqrte through to new helper FEAT_RPRES implements an "increased precision" variant of the single precision FRECPE and FRSQRTE instructions from an 8 bit to a 12 bit mantissa. This applies only when FPCR.AH == 1. Note that the halfprec and double versions of these insns retain the 8 bit precision regardless. In this commit we add all the plumbing to make these instructions call a new helper function when the increased-precision is in effect. In the following commit we will provide the actual change in behaviour in the helpers. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu-features.h | 5 +++++ target/arm/helper.h | 4 ++++ target/arm/tcg/translate-a64.c | 34 ++++++++++++++++++++++++++++++---- target/arm/tcg/translate-sve.c | 16 ++++++++++++++-- target/arm/tcg/vec_helper.c | 2 ++ target/arm/vfp_helper.c | 32 ++++++++++++++++++++++++++++++-- 6 files changed, 85 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 7bf24c506b..525e4cee12 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -597,6 +597,11 @@ static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); } +static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, RPRES); +} + static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ diff --git a/target/arm/helper.h b/target/arm/helper.h index f0a783b708..0907505839 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -245,9 +245,11 @@ DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, fpst) DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, fpst) DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(recpe_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, fpst) DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rsqrte_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, fpst) DEF_HELPER_FLAGS_1(recpe_u32, TCG_CALL_NO_RWG, i32, i32) DEF_HELPER_FLAGS_1(rsqrte_u32, TCG_CALL_NO_RWG, i32, i32) @@ -680,10 +682,12 @@ DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1846f81bf5..8bef391bb0 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8903,7 +8903,14 @@ static const FPScalar1 f_scalar_frecpe = { gen_helper_recpe_f32, gen_helper_recpe_f64, }; -TRANS(FRECPE_s, do_fp1_scalar_ah, a, &f_scalar_frecpe, -1) +static const FPScalar1 f_scalar_frecpe_rpres = { + gen_helper_recpe_f16, + gen_helper_recpe_rpres_f32, + gen_helper_recpe_f64, +}; +TRANS(FRECPE_s, do_fp1_scalar_ah, a, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + &f_scalar_frecpe_rpres : &f_scalar_frecpe, -1) static const FPScalar1 f_scalar_frecpx = { gen_helper_frecpx_f16, @@ -8917,7 +8924,14 @@ static const FPScalar1 f_scalar_frsqrte = { gen_helper_rsqrte_f32, gen_helper_rsqrte_f64, }; -TRANS(FRSQRTE_s, do_fp1_scalar_ah, a, &f_scalar_frsqrte, -1) +static const FPScalar1 f_scalar_frsqrte_rpres = { + gen_helper_rsqrte_f16, + gen_helper_rsqrte_rpres_f32, + gen_helper_rsqrte_f64, +}; +TRANS(FRSQRTE_s, do_fp1_scalar_ah, a, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + &f_scalar_frsqrte_rpres : &f_scalar_frsqrte, -1) static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) { @@ -9948,14 +9962,26 @@ static gen_helper_gvec_2_ptr * const f_frecpe[] = { gen_helper_gvec_frecpe_s, gen_helper_gvec_frecpe_d, }; -TRANS(FRECPE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frecpe) +static gen_helper_gvec_2_ptr * const f_frecpe_rpres[] = { + gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_rpres_s, + gen_helper_gvec_frecpe_d, +}; +TRANS(FRECPE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? f_frecpe_rpres : f_frecpe) static gen_helper_gvec_2_ptr * const f_frsqrte[] = { gen_helper_gvec_frsqrte_h, gen_helper_gvec_frsqrte_s, gen_helper_gvec_frsqrte_d, }; -TRANS(FRSQRTE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, f_frsqrte) +static gen_helper_gvec_2_ptr * const f_frsqrte_rpres[] = { + gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_rpres_s, + gen_helper_gvec_frsqrte_d, +}; +TRANS(FRSQRTE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? f_frsqrte_rpres : f_frsqrte) static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) { diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 6af94fedd0..d23be477b4 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3629,13 +3629,25 @@ static gen_helper_gvec_2_ptr * const frecpe_fns[] = { NULL, gen_helper_gvec_frecpe_h, gen_helper_gvec_frecpe_s, gen_helper_gvec_frecpe_d, }; -TRANS_FEAT(FRECPE, aa64_sve, gen_gvec_fpst_ah_arg_zz, frecpe_fns[a->esz], a, 0) +static gen_helper_gvec_2_ptr * const frecpe_rpres_fns[] = { + NULL, gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_rpres_s, gen_helper_gvec_frecpe_d, +}; +TRANS_FEAT(FRECPE, aa64_sve, gen_gvec_fpst_ah_arg_zz, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + frecpe_rpres_fns[a->esz] : frecpe_fns[a->esz], a, 0) static gen_helper_gvec_2_ptr * const frsqrte_fns[] = { NULL, gen_helper_gvec_frsqrte_h, gen_helper_gvec_frsqrte_s, gen_helper_gvec_frsqrte_d, }; -TRANS_FEAT(FRSQRTE, aa64_sve, gen_gvec_fpst_ah_arg_zz, frsqrte_fns[a->esz], a, 0) +static gen_helper_gvec_2_ptr * const frsqrte_rpres_fns[] = { + NULL, gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_rpres_s, gen_helper_gvec_frsqrte_d, +}; +TRANS_FEAT(FRSQRTE, aa64_sve, gen_gvec_fpst_ah_arg_zz, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + frsqrte_rpres_fns[a->esz] : frsqrte_fns[a->esz], a, 0) /* *** SVE Floating Point Compare with Zero Group diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index aefcd07ef0..ff3f7d8208 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1236,10 +1236,12 @@ void HELPER(NAME)(void *vd, void *vn, float_status *stat, uint32_t desc) \ DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) DO_2OP(gvec_frecpe_s, helper_recpe_f32, float32) +DO_2OP(gvec_frecpe_rpres_s, helper_recpe_rpres_f32, float32) DO_2OP(gvec_frecpe_d, helper_recpe_f64, float64) DO_2OP(gvec_frsqrte_h, helper_rsqrte_f16, float16) DO_2OP(gvec_frsqrte_s, helper_rsqrte_f32, float32) +DO_2OP(gvec_frsqrte_rpres_s, helper_rsqrte_rpres_f32, float32) DO_2OP(gvec_frsqrte_d, helper_rsqrte_f64, float64) DO_2OP(gvec_vrintx_h, float16_round_to_int, float16) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 5c370e263c..b97417e5a1 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -841,7 +841,11 @@ uint32_t HELPER(recpe_f16)(uint32_t input, float_status *fpst) return make_float16(f16_val); } -float32 HELPER(recpe_f32)(float32 input, float_status *fpst) +/* + * FEAT_RPRES means the f32 FRECPE has an "increased precision" variant + * which is used when FPCR.AH == 1. + */ +static float32 do_recpe_f32(float32 input, float_status *fpst, bool rpres) { float32 f32 = float32_squash_input_denormal(input, fpst); uint32_t f32_val = float32_val(f32); @@ -890,6 +894,16 @@ float32 HELPER(recpe_f32)(float32 input, float_status *fpst) return make_float32(f32_val); } +float32 HELPER(recpe_f32)(float32 input, float_status *fpst) +{ + return do_recpe_f32(input, fpst, false); +} + +float32 HELPER(recpe_rpres_f32)(float32 input, float_status *fpst) +{ + return do_recpe_f32(input, fpst, true); +} + float64 HELPER(recpe_f64)(float64 input, float_status *fpst) { float64 f64 = float64_squash_input_denormal(input, fpst); @@ -1035,7 +1049,11 @@ uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) return make_float16(val); } -float32 HELPER(rsqrte_f32)(float32 input, float_status *s) +/* + * FEAT_RPRES means the f32 FRSQRTE has an "increased precision" variant + * which is used when FPCR.AH == 1. + */ +static float32 do_rsqrte_f32(float32 input, float_status *s, bool rpres) { float32 f32 = float32_squash_input_denormal(input, s); uint32_t val = float32_val(f32); @@ -1080,6 +1098,16 @@ float32 HELPER(rsqrte_f32)(float32 input, float_status *s) return make_float32(val); } +float32 HELPER(rsqrte_f32)(float32 input, float_status *s) +{ + return do_rsqrte_f32(input, s, false); +} + +float32 HELPER(rsqrte_rpres_f32)(float32 input, float_status *s) +{ + return do_rsqrte_f32(input, s, true); +} + float64 HELPER(rsqrte_f64)(float64 input, float_status *s) { float64 f64 = float64_squash_input_denormal(input, s); From c1567205e000369aec6c73bfbd60219098b34d3e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:57 +0000 Subject: [PATCH 1639/2892] target/arm: Implement increased precision FRECPE Implement the increased precision variation of FRECPE. In the pseudocode this corresponds to the handling of the "increasedprecision" boolean in the FPRecipEstimate() and RecipEstimate() functions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/vfp_helper.c | 54 +++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index b97417e5a1..2df97e128f 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -733,6 +733,33 @@ static int recip_estimate(int input) return r; } +/* + * Increased precision version: + * input is a 13 bit fixed point number + * input range 2048 .. 4095 for a number from 0.5 <= x < 1.0. + * result range 4096 .. 8191 for a number from 1.0 to 2.0 + */ +static int recip_estimate_incprec(int input) +{ + int a, b, r; + assert(2048 <= input && input < 4096); + a = (input * 2) + 1; + /* + * The pseudocode expresses this as an operation on infinite + * precision reals where it calculates 2^25 / a and then looks + * at the error between that and the rounded-down-to-integer + * value to see if it should instead round up. We instead + * follow the same approach as the pseudocode for the 8-bit + * precision version, and calculate (2 * (2^25 / a)) as an + * integer so we can do the "add one and halve" to round it. + * So the 1 << 26 here is correct. + */ + b = (1 << 26) / a; + r = (b + 1) >> 1; + assert(4096 <= r && r < 8192); + return r; +} + /* * Common wrapper to call recip_estimate * @@ -742,7 +769,8 @@ static int recip_estimate(int input) * callee. */ -static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac) +static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac, + bool increasedprecision) { uint32_t scaled, estimate; uint64_t result_frac; @@ -758,12 +786,22 @@ static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac) } } - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - estimate = recip_estimate(scaled); + if (increasedprecision) { + /* scaled = UInt('1':fraction<51:41>) */ + scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); + estimate = recip_estimate_incprec(scaled); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + estimate = recip_estimate(scaled); + } result_exp = exp_off - *exp; - result_frac = deposit64(0, 44, 8, estimate); + if (increasedprecision) { + result_frac = deposit64(0, 40, 12, estimate); + } else { + result_frac = deposit64(0, 44, 8, estimate); + } if (result_exp == 0) { result_frac = deposit64(result_frac >> 1, 51, 1, 1); } else if (result_exp == -1) { @@ -832,7 +870,7 @@ uint32_t HELPER(recpe_f16)(uint32_t input, float_status *fpst) } f64_frac = call_recip_estimate(&f16_exp, 29, - ((uint64_t) f16_frac) << (52 - 10)); + ((uint64_t) f16_frac) << (52 - 10), false); /* result = sign : result_exp<4:0> : fraction<51:42> */ f16_val = deposit32(0, 15, 1, f16_sign); @@ -885,7 +923,7 @@ static float32 do_recpe_f32(float32 input, float_status *fpst, bool rpres) } f64_frac = call_recip_estimate(&f32_exp, 253, - ((uint64_t) f32_frac) << (52 - 23)); + ((uint64_t) f32_frac) << (52 - 23), rpres); /* result = sign : result_exp<7:0> : fraction<51:29> */ f32_val = deposit32(0, 31, 1, f32_sign); @@ -943,7 +981,7 @@ float64 HELPER(recpe_f64)(float64 input, float_status *fpst) return float64_set_sign(float64_zero, float64_is_neg(f64)); } - f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac); + f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac, false); /* result = sign : result_exp<10:0> : fraction<51:0>; */ f64_val = deposit64(0, 63, 1, f64_sign); From 22330d2b0f6152d87114fec916f323e6726341d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:58 +0000 Subject: [PATCH 1640/2892] target/arm: Implement increased precision FRSQRTE Implement the increased precision variation of FRSQRTE. In the pseudocode this corresponds to the handling of the "increasedprecision" boolean in the FPRSqrtEstimate() and RecipSqrtEstimate() functions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/vfp_helper.c | 77 ++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 2df97e128f..9d58fa7d6f 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -1015,8 +1015,36 @@ static int do_recip_sqrt_estimate(int a) return estimate; } +static int do_recip_sqrt_estimate_incprec(int a) +{ + /* + * The Arm ARM describes the 12-bit precision version of RecipSqrtEstimate + * in terms of an infinite-precision floating point calculation of a + * square root. We implement this using the same kind of pure integer + * algorithm as the 8-bit mantissa, to get the same bit-for-bit result. + */ + int64_t b, estimate; -static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) + assert(1024 <= a && a < 4096); + if (a < 2048) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 8192; + while (a * (b + 1) * (b + 1) < (1ULL << 39)) { + b += 1; + } + estimate = (b + 1) / 2; + + assert(4096 <= estimate && estimate < 8192); + + return estimate; +} + +static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac, + bool increasedprecision) { int estimate; uint32_t scaled; @@ -1029,17 +1057,32 @@ static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) frac = extract64(frac, 0, 51) << 1; } - if (*exp & 1) { - /* scaled = UInt('01':fraction<51:45>) */ - scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); + if (increasedprecision) { + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:42>) */ + scaled = deposit32(1 << 10, 0, 10, extract64(frac, 42, 10)); + } else { + /* scaled = UInt('1':fraction<51:41>) */ + scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); + } + estimate = do_recip_sqrt_estimate_incprec(scaled); } else { - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:45>) */ + scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + } + estimate = do_recip_sqrt_estimate(scaled); } - estimate = do_recip_sqrt_estimate(scaled); *exp = (exp_off - *exp) / 2; - return extract64(estimate, 0, 8) << 44; + if (increasedprecision) { + return extract64(estimate, 0, 12) << 40; + } else { + return extract64(estimate, 0, 8) << 44; + } } uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) @@ -1078,7 +1121,7 @@ uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) f64_frac = ((uint64_t) f16_frac) << (52 - 10); - f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac); + f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac, false); /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ val = deposit32(0, 15, 1, f16_sign); @@ -1127,12 +1170,20 @@ static float32 do_rsqrte_f32(float32 input, float_status *s, bool rpres) f64_frac = ((uint64_t) f32_frac) << 29; - f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac); + f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac, rpres); - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(15) */ + /* + * result = sign : result_exp<7:0> : estimate<7:0> : Zeros(15) + * or for increased precision + * result = sign : result_exp<7:0> : estimate<11:0> : Zeros(11) + */ val = deposit32(0, 31, 1, f32_sign); val = deposit32(val, 23, 8, f32_exp); - val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); + if (rpres) { + val = deposit32(val, 11, 12, extract64(f64_frac, 52 - 12, 12)); + } else { + val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); + } return make_float32(val); } @@ -1176,7 +1227,7 @@ float64 HELPER(rsqrte_f64)(float64 input, float_status *s) return float64_zero; } - f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac); + f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac, false); /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ val = deposit64(0, 61, 1, f64_sign); From b0bf37746bb9d66496ce8ef076c74388aa7ef899 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Feb 2025 16:39:59 +0000 Subject: [PATCH 1641/2892] target/arm: Enable FEAT_RPRES for -cpu max Now the emulation is complete, we can enable FEAT_RPRES for the 'max' CPU type. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 63b4cdf5fb..78c2fd2113 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -118,6 +118,7 @@ the following architecture extensions: - FEAT_RDM (Advanced SIMD rounding double multiply accumulate instructions) - FEAT_RME (Realm Management Extension) (NB: support status in QEMU is experimental) - FEAT_RNG (Random number generator) +- FEAT_RPRES (Increased precision of FRECPE and FRSQRTE) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) - FEAT_SEL2 (Secure EL2) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 0bc68aac17..29ab0ac79d 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1167,6 +1167,7 @@ void aarch64_max_tcg_initfn(Object *obj) cpu->isar.id_aa64isar1 = t; t = cpu->isar.id_aa64isar2; + t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* FEAT_RPRES */ t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ From b902f5c62dbf2e796e39af55ed28294ec0467a56 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:00 +0000 Subject: [PATCH 1642/2892] target/arm: Introduce CPUARMState.vfp.fp_status[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move ARMFPStatusFlavour to cpu.h with which to index this array. For now, place the array in an anonymous union with the existing structures. Adjust the order of the existing structures to match the enum. Simplify fpstatus_ptr() using the new array. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 119 +++++++++++++++++++++---------------- target/arm/tcg/translate.h | 64 +------------------- 2 files changed, 70 insertions(+), 113 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 24b0a5fcb9..bf68e135d7 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -202,6 +202,61 @@ typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; typedef struct NVICState NVICState; +/* + * Enum for indexing vfp.fp_status[]. + * + * FPST_A32: is the "normal" fp status for AArch32 insns + * FPST_A64: is the "normal" fp status for AArch64 insns + * FPST_A32_F16: used for AArch32 half-precision calculations + * FPST_A64_F16: used for AArch64 half-precision calculations + * FPST_STD: the ARM "Standard FPSCR Value" + * FPST_STD_F16: used for half-precision + * calculations with the ARM "Standard FPSCR Value" + * FPST_AH: used for the A64 insns which change behaviour + * when FPCR.AH == 1 (bfloat16 conversions and multiplies, + * and the reciprocal and square root estimate/step insns) + * FPST_AH_F16: used for the A64 insns which change behaviour + * when FPCR.AH == 1 (bfloat16 conversions and multiplies, + * and the reciprocal and square root estimate/step insns); + * for half-precision + * + * Half-precision operations are governed by a separate + * flush-to-zero control bit in FPSCR:FZ16. We pass a separate + * status structure to control this. + * + * The "Standard FPSCR", ie default-NaN, flush-to-zero, + * round-to-nearest and is used by any operations (generally + * Neon) which the architecture defines as controlled by the + * standard FPSCR value rather than the FPSCR. + * + * The "standard FPSCR but for fp16 ops" is needed because + * the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than + * using a fixed value for it. + * + * The ah_fp_status is needed because some insns have different + * behaviour when FPCR.AH == 1: they don't update cumulative + * exception flags, they act like FPCR.{FZ,FIZ} = {1,1} and + * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, + * which means we need an ah_fp_status_f16 as well. + * + * To avoid having to transfer exception bits around, we simply + * say that the FPSCR cumulative exception flags are the logical + * OR of the flags in the four fp statuses. This relies on the + * only thing which needs to read the exception flags being + * an explicit FPSCR read. + */ +typedef enum ARMFPStatusFlavour { + FPST_A32, + FPST_A64, + FPST_A32_F16, + FPST_A64_F16, + FPST_AH, + FPST_AH_F16, + FPST_STD, + FPST_STD_F16, +} ARMFPStatusFlavour; +#define FPST_COUNT 8 + typedef struct CPUArchState { /* Regs for current mode. */ uint32_t regs[16]; @@ -631,56 +686,20 @@ typedef struct CPUArchState { /* Scratch space for aa32 neon expansion. */ uint32_t scratch[8]; - /* There are a number of distinct float control structures: - * - * fp_status_a32: is the "normal" fp status for AArch32 insns - * fp_status_a64: is the "normal" fp status for AArch64 insns - * fp_status_fp16_a32: used for AArch32 half-precision calculations - * fp_status_fp16_a64: used for AArch64 half-precision calculations - * standard_fp_status : the ARM "Standard FPSCR Value" - * standard_fp_status_fp16 : used for half-precision - * calculations with the ARM "Standard FPSCR Value" - * ah_fp_status: used for the A64 insns which change behaviour - * when FPCR.AH == 1 (bfloat16 conversions and multiplies, - * and the reciprocal and square root estimate/step insns) - * ah_fp_status_f16: used for the A64 insns which change behaviour - * when FPCR.AH == 1 (bfloat16 conversions and multiplies, - * and the reciprocal and square root estimate/step insns); - * for half-precision - * - * Half-precision operations are governed by a separate - * flush-to-zero control bit in FPSCR:FZ16. We pass a separate - * status structure to control this. - * - * The "Standard FPSCR", ie default-NaN, flush-to-zero, - * round-to-nearest and is used by any operations (generally - * Neon) which the architecture defines as controlled by the - * standard FPSCR value rather than the FPSCR. - * - * The "standard FPSCR but for fp16 ops" is needed because - * the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than - * using a fixed value for it. - * - * The ah_fp_status is needed because some insns have different - * behaviour when FPCR.AH == 1: they don't update cumulative - * exception flags, they act like FPCR.{FZ,FIZ} = {1,1} and - * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, - * which means we need an ah_fp_status_f16 as well. - * - * To avoid having to transfer exception bits around, we simply - * say that the FPSCR cumulative exception flags are the logical - * OR of the flags in the four fp statuses. This relies on the - * only thing which needs to read the exception flags being - * an explicit FPSCR read. - */ - float_status fp_status_a32; - float_status fp_status_a64; - float_status fp_status_f16_a32; - float_status fp_status_f16_a64; - float_status standard_fp_status; - float_status standard_fp_status_f16; - float_status ah_fp_status; - float_status ah_fp_status_f16; + /* There are a number of distinct float control structures. */ + union { + float_status fp_status[FPST_COUNT]; + struct { + float_status fp_status_a32; + float_status fp_status_a64; + float_status fp_status_f16_a32; + float_status fp_status_f16_a64; + float_status ah_fp_status; + float_status ah_fp_status_f16; + float_status standard_fp_status; + float_status standard_fp_status_f16; + }; + }; uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 0dff00015e..f8dc2f0d4b 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -670,80 +670,18 @@ static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb) return (CPUARMTBFlags){ tb->flags, tb->cs_base }; } -/* - * Enum for argument to fpstatus_ptr(). - */ -typedef enum ARMFPStatusFlavour { - FPST_A32, - FPST_A64, - FPST_A32_F16, - FPST_A64_F16, - FPST_AH, - FPST_AH_F16, - FPST_STD, - FPST_STD_F16, -} ARMFPStatusFlavour; - /** * fpstatus_ptr: return TCGv_ptr to the specified fp_status field * * We have multiple softfloat float_status fields in the Arm CPU state struct * (see the comment in cpu.h for details). Return a TCGv_ptr which has * been set up to point to the requested field in the CPU state struct. - * The options are: - * - * FPST_A32 - * for AArch32 non-FP16 operations controlled by the FPCR - * FPST_A64 - * for AArch64 non-FP16 operations controlled by the FPCR - * FPST_A32_F16 - * for AArch32 operations controlled by the FPCR where FPCR.FZ16 is to be used - * FPST_A64_F16 - * for AArch64 operations controlled by the FPCR where FPCR.FZ16 is to be used - * FPST_AH: - * for AArch64 operations which change behaviour when AH=1 (specifically, - * bfloat16 conversions and multiplies, and the reciprocal and square root - * estimate/step insns) - * FPST_AH_F16: - * ditto, but for half-precision operations - * FPST_STD - * for A32/T32 Neon operations using the "standard FPSCR value" - * FPST_STD_F16 - * as FPST_STD, but where FPCR.FZ16 is to be used */ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) { TCGv_ptr statusptr = tcg_temp_new_ptr(); - int offset; + int offset = offsetof(CPUARMState, vfp.fp_status[flavour]); - switch (flavour) { - case FPST_A32: - offset = offsetof(CPUARMState, vfp.fp_status_a32); - break; - case FPST_A64: - offset = offsetof(CPUARMState, vfp.fp_status_a64); - break; - case FPST_A32_F16: - offset = offsetof(CPUARMState, vfp.fp_status_f16_a32); - break; - case FPST_A64_F16: - offset = offsetof(CPUARMState, vfp.fp_status_f16_a64); - break; - case FPST_AH: - offset = offsetof(CPUARMState, vfp.ah_fp_status); - break; - case FPST_AH_F16: - offset = offsetof(CPUARMState, vfp.ah_fp_status_f16); - break; - case FPST_STD: - offset = offsetof(CPUARMState, vfp.standard_fp_status); - break; - case FPST_STD_F16: - offset = offsetof(CPUARMState, vfp.standard_fp_status_f16); - break; - default: - g_assert_not_reached(); - } tcg_gen_addi_ptr(statusptr, tcg_env, offset); return statusptr; } From 7678859152df85c21380e3cf441eaab94f1a04ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:01 +0000 Subject: [PATCH 1643/2892] target/arm: Remove standard_fp_status_f16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_STD_F16]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 4 ++-- target/arm/cpu.h | 1 - target/arm/tcg/mve_helper.c | 24 ++++++++++++------------ target/arm/vfp_helper.c | 8 ++++---- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1307ee34ce..c6d91fd2c8 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -549,13 +549,13 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) set_flush_to_zero(1, &env->vfp.standard_fp_status); set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); set_default_nan_mode(1, &env->vfp.standard_fp_status); - set_default_nan_mode(1, &env->vfp.standard_fp_status_f16); + set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); - arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); arm_set_ah_fp_behaviours(&env->vfp.ah_fp_status); set_flush_to_zero(1, &env->vfp.ah_fp_status); set_flush_inputs_to_zero(1, &env->vfp.ah_fp_status); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index bf68e135d7..085d5383e0 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -697,7 +697,6 @@ typedef struct CPUArchState { float_status ah_fp_status; float_status ah_fp_status_f16; float_status standard_fp_status; - float_status standard_fp_status_f16; }; }; diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 03ebef5ef2..911a53a23a 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -2814,7 +2814,7 @@ DO_VMAXMINA(vminaw, 4, int32_t, uint32_t, DO_MIN) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ @@ -2888,7 +2888,7 @@ DO_2OP_FP_ALL(vminnma, minnuma) r[e] = 0; \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(tm & 1)) { \ /* We need the result but without updating flags */ \ @@ -2926,7 +2926,7 @@ DO_VCADD_FP(vfcadd270s, 4, float32, float32_add, float32_sub) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ @@ -2964,7 +2964,7 @@ DO_VFMA(vfmss, 4, float32, true) if ((mask & MAKE_64BIT_MASK(0, ESIZE * 2)) == 0) { \ continue; \ } \ - fpst0 = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst0 = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ fpst1 = fpst0; \ if (!(mask & 1)) { \ @@ -3049,7 +3049,7 @@ DO_VCMLA(vcmla270s, 4, float32, 3, DO_VCMLAS) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ @@ -3084,7 +3084,7 @@ DO_2OP_FP_SCALAR_ALL(vfmul_scalar, mul) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ @@ -3117,7 +3117,7 @@ DO_2OP_FP_ACC_SCALAR(vfmas_scalars, 4, float32, DO_VFMAS_SCALARS) TYPE *m = vm; \ TYPE ra = (TYPE)ra_in; \ float_status *fpst = (ESIZE == 2) ? \ - &env->vfp.standard_fp_status_f16 : \ + &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ if (mask & 1) { \ @@ -3168,7 +3168,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) if ((mask & emask) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ @@ -3202,7 +3202,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) if ((mask & emask) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ @@ -3267,7 +3267,7 @@ DO_VCMP_FP_BOTH(vfcmples, vfcmple_scalars, 4, float32, !DO_GT32) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ @@ -3301,7 +3301,7 @@ DO_VCVT_FIXED(vcvt_fu, 4, uint32_t, helper_vfp_touls_round_to_zero) float_status *fpst; \ float_status scratch_fpst; \ float_status *base_fpst = (ESIZE == 2) ? \ - &env->vfp.standard_fp_status_f16 : \ + &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ uint32_t prev_rmode = get_float_rounding_mode(base_fpst); \ set_float_rounding_mode(rmode, base_fpst); \ @@ -3427,7 +3427,7 @@ void HELPER(mve_vcvtt_hs)(CPUARMState *env, void *vd, void *vm) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ + fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ &env->vfp.standard_fp_status; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 9d58fa7d6f..61e769d430 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -122,7 +122,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) /* FZ16 does not generate an input denormal exception. */ a32_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) & ~float_flag_input_denormal_flushed); - a32_flags |= (get_float_exception_flags(&env->vfp.standard_fp_status_f16) + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16]) & ~float_flag_input_denormal_flushed); a64_flags |= get_float_exception_flags(&env->vfp.fp_status_a64); @@ -160,7 +160,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status_f16_a32); set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); set_float_exception_flags(0, &env->vfp.standard_fp_status); - set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); set_float_exception_flags(0, &env->vfp.ah_fp_status); set_float_exception_flags(0, &env->vfp.ah_fp_status_f16); } @@ -207,11 +207,11 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) bool ftz_enabled = val & FPCR_FZ16; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); - set_flush_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.ah_fp_status_f16); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.ah_fp_status_f16); } if (changed & FPCR_FZ) { From f069b26b8e6b7d03399fc4fd7540229f1cb45e27 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:02 +0000 Subject: [PATCH 1644/2892] target/arm: Remove standard_fp_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_STD]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 8 ++++---- target/arm/cpu.h | 1 - target/arm/tcg/mve_helper.c | 28 ++++++++++++++-------------- target/arm/tcg/vec_helper.c | 4 ++-- target/arm/vfp_helper.c | 4 ++-- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c6d91fd2c8..75b6b93c7c 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -546,13 +546,13 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) env->sau.ctrl = 0; } - set_flush_to_zero(1, &env->vfp.standard_fp_status); - set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); - set_default_nan_mode(1, &env->vfp.standard_fp_status); + set_flush_to_zero(1, &env->vfp.fp_status[FPST_STD]); + set_flush_inputs_to_zero(1, &env->vfp.fp_status[FPST_STD]); + set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD]); set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); - arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 085d5383e0..9f57dfc3e9 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -696,7 +696,6 @@ typedef struct CPUArchState { float_status fp_status_f16_a64; float_status ah_fp_status; float_status ah_fp_status_f16; - float_status standard_fp_status; }; }; diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 911a53a23a..3763d71e20 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -2815,7 +2815,7 @@ DO_VMAXMINA(vminaw, 4, int32_t, uint32_t, DO_MIN) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2889,7 +2889,7 @@ DO_2OP_FP_ALL(vminnma, minnuma) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(tm & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2927,7 +2927,7 @@ DO_VCADD_FP(vfcadd270s, 4, float32, float32_add, float32_sub) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2965,7 +2965,7 @@ DO_VFMA(vfmss, 4, float32, true) continue; \ } \ fpst0 = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ fpst1 = fpst0; \ if (!(mask & 1)) { \ scratch_fpst = *fpst0; \ @@ -3050,7 +3050,7 @@ DO_VCMLA(vcmla270s, 4, float32, 3, DO_VCMLAS) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3085,7 +3085,7 @@ DO_2OP_FP_SCALAR_ALL(vfmul_scalar, mul) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3118,7 +3118,7 @@ DO_2OP_FP_ACC_SCALAR(vfmas_scalars, 4, float32, DO_VFMAS_SCALARS) TYPE ra = (TYPE)ra_in; \ float_status *fpst = (ESIZE == 2) ? \ &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ if (mask & 1) { \ TYPE v = m[H##ESIZE(e)]; \ @@ -3169,7 +3169,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3203,7 +3203,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3268,7 +3268,7 @@ DO_VCMP_FP_BOTH(vfcmples, vfcmple_scalars, 4, float32, !DO_GT32) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3302,7 +3302,7 @@ DO_VCVT_FIXED(vcvt_fu, 4, uint32_t, helper_vfp_touls_round_to_zero) float_status scratch_fpst; \ float_status *base_fpst = (ESIZE == 2) ? \ &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ uint32_t prev_rmode = get_float_rounding_mode(base_fpst); \ set_float_rounding_mode(rmode, base_fpst); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ @@ -3347,7 +3347,7 @@ static void do_vcvt_sh(CPUARMState *env, void *vd, void *vm, int top) unsigned e; float_status *fpst; float_status scratch_fpst; - float_status *base_fpst = &env->vfp.standard_fp_status; + float_status *base_fpst = &env->vfp.fp_status[FPST_STD]; bool old_fz = get_flush_to_zero(base_fpst); set_flush_to_zero(false, base_fpst); for (e = 0; e < 16 / 4; e++, mask >>= 4) { @@ -3377,7 +3377,7 @@ static void do_vcvt_hs(CPUARMState *env, void *vd, void *vm, int top) unsigned e; float_status *fpst; float_status scratch_fpst; - float_status *base_fpst = &env->vfp.standard_fp_status; + float_status *base_fpst = &env->vfp.fp_status[FPST_STD]; bool old_fiz = get_flush_inputs_to_zero(base_fpst); set_flush_inputs_to_zero(false, base_fpst); for (e = 0; e < 16 / 4; e++, mask >>= 4) { @@ -3428,7 +3428,7 @@ void HELPER(mve_vcvtt_hs)(CPUARMState *env, void *vd, void *vm) continue; \ } \ fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.standard_fp_status; \ + &env->vfp.fp_status[FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index ff3f7d8208..cffd022260 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2154,7 +2154,7 @@ void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t negx = is_s ? 0x8000800080008000ull : 0; - do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, negx, 0, desc, + do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); } @@ -2235,7 +2235,7 @@ void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t negx = is_s ? 0x8000800080008000ull : 0; - do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, negx, 0, desc, + do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); } diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 61e769d430..28b9132be5 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -118,7 +118,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) uint32_t a32_flags = 0, a64_flags = 0; a32_flags |= get_float_exception_flags(&env->vfp.fp_status_a32); - a32_flags |= get_float_exception_flags(&env->vfp.standard_fp_status); + a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]); /* FZ16 does not generate an input denormal exception. */ a32_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) & ~float_flag_input_denormal_flushed); @@ -159,7 +159,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status_a64); set_float_exception_flags(0, &env->vfp.fp_status_f16_a32); set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); - set_float_exception_flags(0, &env->vfp.standard_fp_status); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); set_float_exception_flags(0, &env->vfp.ah_fp_status); set_float_exception_flags(0, &env->vfp.ah_fp_status_f16); From fc25b174d5c514d70138bf2e77b1e32649bcead2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:03 +0000 Subject: [PATCH 1645/2892] target/arm: Remove ah_fp_status_f16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_AH_F16]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 3 +-- target/arm/vfp_helper.c | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 75b6b93c7c..5a21a6c6eb 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -559,7 +559,7 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_ah_fp_behaviours(&env->vfp.ah_fp_status); set_flush_to_zero(1, &env->vfp.ah_fp_status); set_flush_inputs_to_zero(1, &env->vfp.ah_fp_status); - arm_set_ah_fp_behaviours(&env->vfp.ah_fp_status_f16); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH_F16]); #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 9f57dfc3e9..91e132024e 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -237,7 +237,7 @@ typedef struct NVICState NVICState; * behaviour when FPCR.AH == 1: they don't update cumulative * exception flags, they act like FPCR.{FZ,FIZ} = {1,1} and * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, - * which means we need an ah_fp_status_f16 as well. + * which means we need an FPST_AH_F16 as well. * * To avoid having to transfer exception bits around, we simply * say that the FPSCR cumulative exception flags are the logical @@ -695,7 +695,6 @@ typedef struct CPUArchState { float_status fp_status_f16_a32; float_status fp_status_f16_a64; float_status ah_fp_status; - float_status ah_fp_status_f16; }; }; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 28b9132be5..ec7c0d6a98 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -129,7 +129,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a64_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); /* - * We do not merge in flags from ah_fp_status or ah_fp_status_f16, because + * We do not merge in flags from ah_fp_status or FPST_AH_F16, because * they are used for insns that must not set the cumulative exception bits. */ @@ -162,7 +162,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); set_float_exception_flags(0, &env->vfp.ah_fp_status); - set_float_exception_flags(0, &env->vfp.ah_fp_status_f16); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH_F16]); } static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) @@ -208,11 +208,11 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); - set_flush_to_zero(ftz_enabled, &env->vfp.ah_fp_status_f16); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.ah_fp_status_f16); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); } if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; @@ -237,7 +237,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); set_default_nan_mode(dnan_enabled, &env->vfp.ah_fp_status); - set_default_nan_mode(dnan_enabled, &env->vfp.ah_fp_status_f16); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); } if (changed & FPCR_AH) { bool ah_enabled = val & FPCR_AH; From ee4316f65b49cfdbec64ec99288d677ad68adb00 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:04 +0000 Subject: [PATCH 1646/2892] target/arm: Remove ah_fp_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_AH]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 6 +++--- target/arm/cpu.h | 3 +-- target/arm/vfp_helper.c | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 5a21a6c6eb..6b4839b5ba 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -556,9 +556,9 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); - arm_set_ah_fp_behaviours(&env->vfp.ah_fp_status); - set_flush_to_zero(1, &env->vfp.ah_fp_status); - set_flush_inputs_to_zero(1, &env->vfp.ah_fp_status); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH]); + set_flush_to_zero(1, &env->vfp.fp_status[FPST_AH]); + set_flush_inputs_to_zero(1, &env->vfp.fp_status[FPST_AH]); arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH_F16]); #ifndef CONFIG_USER_ONLY diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 91e132024e..577c69dec1 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -233,7 +233,7 @@ typedef struct NVICState NVICState; * the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than * using a fixed value for it. * - * The ah_fp_status is needed because some insns have different + * FPST_AH is needed because some insns have different * behaviour when FPCR.AH == 1: they don't update cumulative * exception flags, they act like FPCR.{FZ,FIZ} = {1,1} and * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, @@ -694,7 +694,6 @@ typedef struct CPUArchState { float_status fp_status_a64; float_status fp_status_f16_a32; float_status fp_status_f16_a64; - float_status ah_fp_status; }; }; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index ec7c0d6a98..2bb98c4ee4 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -129,7 +129,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a64_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); /* - * We do not merge in flags from ah_fp_status or FPST_AH_F16, because + * We do not merge in flags from FPST_AH or FPST_AH_F16, because * they are used for insns that must not set the cumulative exception bits. */ @@ -161,7 +161,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); - set_float_exception_flags(0, &env->vfp.ah_fp_status); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH_F16]); } @@ -236,7 +236,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); - set_default_nan_mode(dnan_enabled, &env->vfp.ah_fp_status); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); } if (changed & FPCR_AH) { From c71296565c0d3763615885306aa6a9904ea32869 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:05 +0000 Subject: [PATCH 1647/2892] target/arm: Remove fp_status_f16_a64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_A64_F16]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 1 - target/arm/tcg/sme_helper.c | 2 +- target/arm/tcg/vec_helper.c | 9 ++++----- target/arm/vfp_helper.c | 16 ++++++++-------- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6b4839b5ba..18e7debe29 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -554,7 +554,7 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); - arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH]); set_flush_to_zero(1, &env->vfp.fp_status[FPST_AH]); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 577c69dec1..6cf97ba9c9 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -693,7 +693,6 @@ typedef struct CPUArchState { float_status fp_status_a32; float_status fp_status_a64; float_status fp_status_f16_a32; - float_status fp_status_f16_a64; }; }; diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 727c085f37..6e336e10c6 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1043,7 +1043,7 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, * produces default NaNs. We also need a second copy of fp_status with * round-to-odd -- see above. */ - fpst_f16 = env->vfp.fp_status_f16_a64; + fpst_f16 = env->vfp.fp_status[FPST_A64_F16]; fpst_std = env->vfp.fp_status_a64; set_default_nan_mode(true, &fpst_std); set_default_nan_mode(true, &fpst_f16); diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index cffd022260..48dbd8bdd2 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2173,7 +2173,7 @@ void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, } } do_fmlal(vd, vn, vm, &env->vfp.fp_status_a64, negx, negf, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64)); + get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16])); } void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, @@ -2183,7 +2183,7 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); float_status *status = &env->vfp.fp_status_a64; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64); + bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16]); int negx = 0, negf = 0; if (is_s) { @@ -2254,7 +2254,7 @@ void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, } } do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status_a64, negx, negf, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64)); + get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16])); } void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, @@ -2265,7 +2265,7 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); float_status *status = &env->vfp.fp_status_a64; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a64); + bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16]); int negx = 0, negf = 0; if (is_s) { @@ -2275,7 +2275,6 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, negx = 0x8000; } } - for (i = 0; i < oprsz; i += 16) { float16 mm_16 = *(float16 *)(vm + i + idx); float32 mm = float16_to_float32_by_bits(mm_16, fz16); diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 2bb98c4ee4..ea6018864d 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -126,7 +126,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) & ~float_flag_input_denormal_flushed); a64_flags |= get_float_exception_flags(&env->vfp.fp_status_a64); - a64_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a64) + a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16]) & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); /* * We do not merge in flags from FPST_AH or FPST_AH_F16, because @@ -158,7 +158,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) set_float_exception_flags(0, &env->vfp.fp_status_a32); set_float_exception_flags(0, &env->vfp.fp_status_a64); set_float_exception_flags(0, &env->vfp.fp_status_f16_a32); - set_float_exception_flags(0, &env->vfp.fp_status_f16_a64); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH]); @@ -201,16 +201,16 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_float_rounding_mode(i, &env->vfp.fp_status_a32); set_float_rounding_mode(i, &env->vfp.fp_status_a64); set_float_rounding_mode(i, &env->vfp.fp_status_f16_a32); - set_float_rounding_mode(i, &env->vfp.fp_status_f16_a64); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); } if (changed & FPCR_FZ16) { bool ftz_enabled = val & FPCR_FZ16; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a64); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); } @@ -235,7 +235,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a64); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); } @@ -245,10 +245,10 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) if (ah_enabled) { /* Change behaviours for A64 FP operations */ arm_set_ah_fp_behaviours(&env->vfp.fp_status_a64); - arm_set_ah_fp_behaviours(&env->vfp.fp_status_f16_a64); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); } else { arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); - arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a64); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); } } /* From 828def800e864c0ec781ea3eb5488ab186f0e24b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:06 +0000 Subject: [PATCH 1648/2892] target/arm: Remove fp_status_f16_a32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_A32_F16]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 1 - target/arm/tcg/vec_helper.c | 4 ++-- target/arm/vfp_helper.c | 14 +++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 18e7debe29..88bff67300 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -553,7 +553,7 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); - arm_set_default_fp_behaviours(&env->vfp.fp_status_f16_a32); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH]); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6cf97ba9c9..1593cc1036 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -692,7 +692,6 @@ typedef struct CPUArchState { struct { float_status fp_status_a32; float_status fp_status_a64; - float_status fp_status_f16_a32; }; }; diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 48dbd8bdd2..78f14503f4 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2155,7 +2155,7 @@ void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, uint64_t negx = is_s ? 0x8000800080008000ull : 0; do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); + get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A32_F16])); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, @@ -2236,7 +2236,7 @@ void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, uint64_t negx = is_s ? 0x8000800080008000ull : 0; do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16_a32)); + get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A32_F16])); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index ea6018864d..dae46a099a 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -120,7 +120,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a32_flags |= get_float_exception_flags(&env->vfp.fp_status_a32); a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]); /* FZ16 does not generate an input denormal exception. */ - a32_flags |= (get_float_exception_flags(&env->vfp.fp_status_f16_a32) + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A32_F16]) & ~float_flag_input_denormal_flushed); a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16]) & ~float_flag_input_denormal_flushed); @@ -157,7 +157,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) */ set_float_exception_flags(0, &env->vfp.fp_status_a32); set_float_exception_flags(0, &env->vfp.fp_status_a64); - set_float_exception_flags(0, &env->vfp.fp_status_f16_a32); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); @@ -200,16 +200,16 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } set_float_rounding_mode(i, &env->vfp.fp_status_a32); set_float_rounding_mode(i, &env->vfp.fp_status_a64); - set_float_rounding_mode(i, &env->vfp.fp_status_f16_a32); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); } if (changed & FPCR_FZ16) { bool ftz_enabled = val & FPCR_FZ16; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16_a32); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); @@ -234,7 +234,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) bool dnan_enabled = val & FPCR_DN; set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16_a32); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); @@ -496,7 +496,7 @@ void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ softfloat_to_vfp_compare(env, \ FLOATTYPE ## _compare(a, b, &env->vfp.FPST)); \ } -DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status_f16_a32) +DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status[FPST_A32_F16]) DO_VFP_cmp(s, float32, float32, fp_status_a32) DO_VFP_cmp(d, float64, float64, fp_status_a32) #undef DO_VFP_cmp From 1c349f43b18608d57a72a3d6d5e95b28a1c14470 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:07 +0000 Subject: [PATCH 1649/2892] target/arm: Remove fp_status_a64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_A64]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 1 - target/arm/tcg/sme_helper.c | 2 +- target/arm/tcg/vec_helper.c | 10 +++++----- target/arm/vfp_helper.c | 16 ++++++++-------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 88bff67300..f04f28b681 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -551,7 +551,7 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD]); set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); - arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1593cc1036..0d8b99bd8a 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -691,7 +691,6 @@ typedef struct CPUArchState { float_status fp_status[FPST_COUNT]; struct { float_status fp_status_a32; - float_status fp_status_a64; }; }; diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 6e336e10c6..dcc48e43db 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1044,7 +1044,7 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, * round-to-odd -- see above. */ fpst_f16 = env->vfp.fp_status[FPST_A64_F16]; - fpst_std = env->vfp.fp_status_a64; + fpst_std = env->vfp.fp_status[FPST_A64]; set_default_nan_mode(true, &fpst_std); set_default_nan_mode(true, &fpst_f16); fpst_odd = fpst_std; diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 78f14503f4..215affc271 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2172,7 +2172,7 @@ void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, negx = 0x8000800080008000ull; } } - do_fmlal(vd, vn, vm, &env->vfp.fp_status_a64, negx, negf, desc, + do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_A64], negx, negf, desc, get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16])); } @@ -2182,7 +2182,7 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, intptr_t i, oprsz = simd_oprsz(desc); bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - float_status *status = &env->vfp.fp_status_a64; + float_status *status = &env->vfp.fp_status[FPST_A64]; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16]); int negx = 0, negf = 0; @@ -2253,7 +2253,7 @@ void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, negx = 0x8000800080008000ull; } } - do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status_a64, negx, negf, desc, + do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_A64], negx, negf, desc, get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16])); } @@ -2264,7 +2264,7 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); - float_status *status = &env->vfp.fp_status_a64; + float_status *status = &env->vfp.fp_status[FPST_A64]; bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16]); int negx = 0, negf = 0; @@ -2951,7 +2951,7 @@ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) */ bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; - *statusp = is_a64(env) ? env->vfp.fp_status_a64 : env->vfp.fp_status_a32; + *statusp = is_a64(env) ? env->vfp.fp_status[FPST_A64] : env->vfp.fp_status_a32; set_default_nan_mode(true, statusp); if (ebf) { diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index dae46a099a..e6b4f63401 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -125,7 +125,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16]) & ~float_flag_input_denormal_flushed); - a64_flags |= get_float_exception_flags(&env->vfp.fp_status_a64); + a64_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A64]); a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16]) & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); /* @@ -156,7 +156,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) * be the architecturally up-to-date exception flag information first. */ set_float_exception_flags(0, &env->vfp.fp_status_a32); - set_float_exception_flags(0, &env->vfp.fp_status_a64); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); @@ -199,7 +199,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) break; } set_float_rounding_mode(i, &env->vfp.fp_status_a32); - set_float_rounding_mode(i, &env->vfp.fp_status_a64); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); } @@ -217,7 +217,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a32); - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a64); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]); /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a32); } @@ -228,12 +228,12 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) */ bool fitz_enabled = (val & FPCR_FIZ) || (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ; - set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status_a64); + set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_A64]); } if (changed & FPCR_DN) { bool dnan_enabled = val & FPCR_DN; set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a64); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); @@ -244,10 +244,10 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) if (ah_enabled) { /* Change behaviours for A64 FP operations */ - arm_set_ah_fp_behaviours(&env->vfp.fp_status_a64); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64]); arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); } else { - arm_set_default_fp_behaviours(&env->vfp.fp_status_a64); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); } } From 54afbf663234756ebc89a1cfee0a8d4c578b26f7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:08 +0000 Subject: [PATCH 1650/2892] target/arm: Remove fp_status_a32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with fp_status[FPST_A32]. As this was the last of the old structures, we can remove the anonymous union and struct. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-15-richard.henderson@linaro.org [PMM: tweak to account for change to is_ebf()] Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 7 +------ target/arm/tcg/vec_helper.c | 2 +- target/arm/vfp_helper.c | 18 +++++++++--------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f04f28b681..656070afb5 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -550,7 +550,7 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) set_flush_inputs_to_zero(1, &env->vfp.fp_status[FPST_STD]); set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD]); set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD_F16]); - arm_set_default_fp_behaviours(&env->vfp.fp_status_a32); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32_F16]); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 0d8b99bd8a..6f6cf5c888 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -687,12 +687,7 @@ typedef struct CPUArchState { uint32_t scratch[8]; /* There are a number of distinct float control structures. */ - union { - float_status fp_status[FPST_COUNT]; - struct { - float_status fp_status_a32; - }; - }; + float_status fp_status[FPST_COUNT]; uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 215affc271..2da44ae710 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2951,7 +2951,7 @@ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) */ bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; - *statusp = is_a64(env) ? env->vfp.fp_status[FPST_A64] : env->vfp.fp_status_a32; + *statusp = env->vfp.fp_status[is_a64(env) ? FPST_A64 : FPST_A32]; set_default_nan_mode(true, statusp); if (ebf) { diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index e6b4f63401..4d1b697a66 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -117,7 +117,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { uint32_t a32_flags = 0, a64_flags = 0; - a32_flags |= get_float_exception_flags(&env->vfp.fp_status_a32); + a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A32]); a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]); /* FZ16 does not generate an input denormal exception. */ a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A32_F16]) @@ -155,7 +155,7 @@ static void vfp_clear_float_status_exc_flags(CPUARMState *env) * values. The caller should have arranged for env->vfp.fpsr to * be the architecturally up-to-date exception flag information first. */ - set_float_exception_flags(0, &env->vfp.fp_status_a32); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]); set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); @@ -198,7 +198,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) i = float_round_to_zero; break; } - set_float_rounding_mode(i, &env->vfp.fp_status_a32); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); @@ -216,10 +216,10 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_a32); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]); /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_a32); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); } if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { /* @@ -232,7 +232,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } if (changed & FPCR_DN) { bool dnan_enabled = val & FPCR_DN; - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_a32); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); @@ -497,8 +497,8 @@ void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ FLOATTYPE ## _compare(a, b, &env->vfp.FPST)); \ } DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status[FPST_A32_F16]) -DO_VFP_cmp(s, float32, float32, fp_status_a32) -DO_VFP_cmp(d, float64, float64, fp_status_a32) +DO_VFP_cmp(s, float32, float32, fp_status[FPST_A32]) +DO_VFP_cmp(d, float64, float64, fp_status[FPST_A32]) #undef DO_VFP_cmp /* Integer to float and float to integer conversions */ @@ -1385,7 +1385,7 @@ uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) { - uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status_a32); + uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status[FPST_A32]); uint32_t result = pair; uint32_t z = (pair >> 32) == 0; From f81c46987aad633ff8c9a7d0fece6c3839ccc463 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:09 +0000 Subject: [PATCH 1651/2892] target/arm: Simplify fp_status indexing in mve_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Select on index instead of pointer. No functional change. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 40 +++++++++++++------------------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 3763d71e20..274003e2e5 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -2814,8 +2814,7 @@ DO_VMAXMINA(vminaw, 4, int32_t, uint32_t, DO_MIN) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2888,8 +2887,7 @@ DO_2OP_FP_ALL(vminnma, minnuma) r[e] = 0; \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(tm & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2926,8 +2924,7 @@ DO_VCADD_FP(vfcadd270s, 4, float32, float32_add, float32_sub) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2964,8 +2961,7 @@ DO_VFMA(vfmss, 4, float32, true) if ((mask & MAKE_64BIT_MASK(0, ESIZE * 2)) == 0) { \ continue; \ } \ - fpst0 = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst0 = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ fpst1 = fpst0; \ if (!(mask & 1)) { \ scratch_fpst = *fpst0; \ @@ -3049,8 +3045,7 @@ DO_VCMLA(vcmla270s, 4, float32, 3, DO_VCMLAS) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3084,8 +3079,7 @@ DO_2OP_FP_SCALAR_ALL(vfmul_scalar, mul) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3116,9 +3110,8 @@ DO_2OP_FP_ACC_SCALAR(vfmas_scalars, 4, float32, DO_VFMAS_SCALARS) unsigned e; \ TYPE *m = vm; \ TYPE ra = (TYPE)ra_in; \ - float_status *fpst = (ESIZE == 2) ? \ - &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + float_status *fpst = \ + &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ if (mask & 1) { \ TYPE v = m[H##ESIZE(e)]; \ @@ -3168,8 +3161,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) if ((mask & emask) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3202,8 +3194,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) if ((mask & emask) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3267,8 +3258,7 @@ DO_VCMP_FP_BOTH(vfcmples, vfcmple_scalars, 4, float32, !DO_GT32) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3300,9 +3290,8 @@ DO_VCVT_FIXED(vcvt_fu, 4, uint32_t, helper_vfp_touls_round_to_zero) unsigned e; \ float_status *fpst; \ float_status scratch_fpst; \ - float_status *base_fpst = (ESIZE == 2) ? \ - &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + float_status *base_fpst = \ + &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ uint32_t prev_rmode = get_float_rounding_mode(base_fpst); \ set_float_rounding_mode(rmode, base_fpst); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ @@ -3427,8 +3416,7 @@ void HELPER(mve_vcvtt_hs)(CPUARMState *env, void *vd, void *vm) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.fp_status[FPST_STD_F16] : \ - &env->vfp.fp_status[FPST_STD]; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ From d75f68047624e5337963690a9a96906fcca3aefe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:10 +0000 Subject: [PATCH 1652/2892] target/arm: Simplify DO_VFP_cmp in vfp_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass ARMFPStatusFlavour index instead of fp_status[FOO]. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250129013857.135256-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/vfp_helper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 4d1b697a66..5d424477a2 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -489,16 +489,16 @@ static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) void VFP_HELPER(cmp, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ { \ softfloat_to_vfp_compare(env, \ - FLOATTYPE ## _compare_quiet(a, b, &env->vfp.FPST)); \ + FLOATTYPE ## _compare_quiet(a, b, &env->vfp.fp_status[FPST])); \ } \ void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ { \ softfloat_to_vfp_compare(env, \ - FLOATTYPE ## _compare(a, b, &env->vfp.FPST)); \ + FLOATTYPE ## _compare(a, b, &env->vfp.fp_status[FPST])); \ } -DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status[FPST_A32_F16]) -DO_VFP_cmp(s, float32, float32, fp_status[FPST_A32]) -DO_VFP_cmp(d, float64, float64, fp_status[FPST_A32]) +DO_VFP_cmp(h, float16, dh_ctype_f16, FPST_A32_F16) +DO_VFP_cmp(s, float32, float32, FPST_A32) +DO_VFP_cmp(d, float64, float64, FPST_A32) #undef DO_VFP_cmp /* Integer to float and float to integer conversions */ From bbeed9f06a4e66d444bcde9ef4bdd21e866b7d77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:11 +0000 Subject: [PATCH 1653/2892] target/arm: Read fz16 from env->vfp.fpcr Read the bit from the source, rather than from the proxy via get_flush_inputs_to_zero. This makes it clear that it does not matter which of the float_status structures is used. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-34-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 2da44ae710..cc3586f44a 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2155,7 +2155,7 @@ void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, uint64_t negx = is_s ? 0x8000800080008000ull : 0; do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A32_F16])); + env->vfp.fpcr & FPCR_FZ16); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, @@ -2173,7 +2173,7 @@ void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, } } do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_A64], negx, negf, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16])); + env->vfp.fpcr & FPCR_FZ16); } void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, @@ -2183,7 +2183,7 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); float_status *status = &env->vfp.fp_status[FPST_A64]; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16]); + bool fz16 = env->vfp.fpcr & FPCR_FZ16; int negx = 0, negf = 0; if (is_s) { @@ -2236,7 +2236,7 @@ void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, uint64_t negx = is_s ? 0x8000800080008000ull : 0; do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A32_F16])); + env->vfp.fpcr & FPCR_FZ16); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, @@ -2254,7 +2254,7 @@ void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, } } do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_A64], negx, negf, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16])); + env->vfp.fpcr & FPCR_FZ16); } void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, @@ -2265,7 +2265,7 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); float_status *status = &env->vfp.fp_status[FPST_A64]; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status[FPST_A64_F16]); + bool fz16 = env->vfp.fpcr & FPCR_FZ16; int negx = 0, negf = 0; if (is_s) { From ca4c34e07d1388df8e396520b5e7d60883cd3690 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Feb 2025 16:40:12 +0000 Subject: [PATCH 1654/2892] target/arm: Sink fp_status and fpcr access into do_fmlal* Sink common code from the callers into do_fmlal and do_fmlal_idx. Reorder the arguments to minimize the re-sorting from the caller's arguments. Signed-off-by: Richard Henderson Message-id: 20250129013857.135256-35-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index cc3586f44a..986eaf8ffa 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2125,9 +2125,13 @@ static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2) * as there is not yet SVE versions that might use blocking. */ -static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, - uint64_t negx, int negf, uint32_t desc, bool fz16) +static void do_fmlal(float32 *d, void *vn, void *vm, + CPUARMState *env, uint32_t desc, + ARMFPStatusFlavour fpst_idx, + uint64_t negx, int negf) { + float_status *fpst = &env->vfp.fp_status[fpst_idx]; + bool fz16 = env->vfp.fpcr & FPCR_FZ16; intptr_t i, oprsz = simd_oprsz(desc); int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); int is_q = oprsz == 16; @@ -2154,8 +2158,7 @@ void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t negx = is_s ? 0x8000800080008000ull : 0; - do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, - env->vfp.fpcr & FPCR_FZ16); + do_fmlal(vd, vn, vm, env, desc, FPST_STD, negx, 0); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, @@ -2172,8 +2175,7 @@ void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, negx = 0x8000800080008000ull; } } - do_fmlal(vd, vn, vm, &env->vfp.fp_status[FPST_A64], negx, negf, desc, - env->vfp.fpcr & FPCR_FZ16); + do_fmlal(vd, vn, vm, env, desc, FPST_A64, negx, negf); } void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, @@ -2205,9 +2207,13 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, } } -static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, - uint64_t negx, int negf, uint32_t desc, bool fz16) +static void do_fmlal_idx(float32 *d, void *vn, void *vm, + CPUARMState *env, uint32_t desc, + ARMFPStatusFlavour fpst_idx, + uint64_t negx, int negf) { + float_status *fpst = &env->vfp.fp_status[fpst_idx]; + bool fz16 = env->vfp.fpcr & FPCR_FZ16; intptr_t i, oprsz = simd_oprsz(desc); int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); int index = extract32(desc, SIMD_DATA_SHIFT + 2, 3); @@ -2235,8 +2241,7 @@ void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t negx = is_s ? 0x8000800080008000ull : 0; - do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_STD], negx, 0, desc, - env->vfp.fpcr & FPCR_FZ16); + do_fmlal_idx(vd, vn, vm, env, desc, FPST_STD, negx, 0); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, @@ -2253,8 +2258,7 @@ void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, negx = 0x8000800080008000ull; } } - do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status[FPST_A64], negx, negf, desc, - env->vfp.fpcr & FPCR_FZ16); + do_fmlal_idx(vd, vn, vm, env, desc, FPST_A64, negx, negf); } void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, From 617017f8dce8d8f0cd4d175cbfaac0b4077b4a1e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 3 Feb 2025 16:26:06 -0600 Subject: [PATCH 1655/2892] qemu-nbd: Allow users to adjust handshake limit Although defaulting the handshake limit to 10 seconds was a nice QoI change to weed out intentionally slow clients, it can interfere with integration testing done with manual NBD_OPT commands over 'nbdsh --opt-mode'. Expose a command line option to allow the user to alter the timeout away from the default. This option is unlikely to be used in enough scenarios to warrant a short option letter. The option --handshake-limit intentionally differs from the name of the constant added in commit fb1c2aaa98 (limit instead of max_secs) and the QMP name to be added in the next commit; this is because typing a longer command-line name is undesirable and there is sufficient --help text to document the units. Signed-off-by: Eric Blake Message-ID: <20250203222722.650694-5-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- docs/tools/qemu-nbd.rst | 5 +++++ qemu-nbd.c | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 4f21b7904a..f82ea5fd77 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -156,6 +156,11 @@ driver options if :option:`--image-opts` is specified. Set the NBD volume export description, as a human-readable string. +.. option:: --handshake-limit=N + + Set the timeout for a client to successfully complete its handshake + to N seconds (default 10), or 0 for no limit. + .. option:: -L, --list Connect as a client and list all details about the exports exposed by diff --git a/qemu-nbd.c b/qemu-nbd.c index b30d3ab8de..05b61da51e 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -57,19 +57,20 @@ #define HAVE_NBD_DEVICE 0 #endif -#define SOCKET_PATH "/var/lock/qemu-nbd-%s" -#define QEMU_NBD_OPT_CACHE 256 -#define QEMU_NBD_OPT_AIO 257 -#define QEMU_NBD_OPT_DISCARD 258 -#define QEMU_NBD_OPT_DETECT_ZEROES 259 -#define QEMU_NBD_OPT_OBJECT 260 -#define QEMU_NBD_OPT_TLSCREDS 261 -#define QEMU_NBD_OPT_IMAGE_OPTS 262 -#define QEMU_NBD_OPT_FORK 263 -#define QEMU_NBD_OPT_TLSAUTHZ 264 -#define QEMU_NBD_OPT_PID_FILE 265 -#define QEMU_NBD_OPT_SELINUX_LABEL 266 -#define QEMU_NBD_OPT_TLSHOSTNAME 267 +#define SOCKET_PATH "/var/lock/qemu-nbd-%s" +#define QEMU_NBD_OPT_CACHE 256 +#define QEMU_NBD_OPT_AIO 257 +#define QEMU_NBD_OPT_DISCARD 258 +#define QEMU_NBD_OPT_DETECT_ZEROES 259 +#define QEMU_NBD_OPT_OBJECT 260 +#define QEMU_NBD_OPT_TLSCREDS 261 +#define QEMU_NBD_OPT_IMAGE_OPTS 262 +#define QEMU_NBD_OPT_FORK 263 +#define QEMU_NBD_OPT_TLSAUTHZ 264 +#define QEMU_NBD_OPT_PID_FILE 265 +#define QEMU_NBD_OPT_SELINUX_LABEL 266 +#define QEMU_NBD_OPT_TLSHOSTNAME 267 +#define QEMU_NBD_OPT_HANDSHAKE_LIMIT 268 #define MBR_SIZE 512 @@ -80,6 +81,7 @@ static int nb_fds; static QIONetListener *server; static QCryptoTLSCreds *tlscreds; static const char *tlsauthz; +static int handshake_limit = NBD_DEFAULT_HANDSHAKE_MAX_SECS; static void usage(const char *name) { @@ -101,6 +103,7 @@ static void usage(const char *name) " -v, --verbose display extra debugging information\n" " -x, --export-name=NAME expose export by name (default is empty string)\n" " -D, --description=TEXT export a human-readable description\n" +" --handshake-limit=N limit client's handshake to N seconds (default 10)\n" "\n" "Exposing part of the image:\n" " -o, --offset=OFFSET offset into the image\n" @@ -390,8 +393,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nb_fds++; nbd_update_server_watch(); - /* TODO - expose handshake timeout as command line option */ - nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + nbd_client_new(cioc, handshake_limit, tlscreds, tlsauthz, nbd_client_closed, NULL); } @@ -569,6 +571,8 @@ int main(int argc, char **argv) { "object", required_argument, NULL, QEMU_NBD_OPT_OBJECT }, { "export-name", required_argument, NULL, 'x' }, { "description", required_argument, NULL, 'D' }, + { "handshake-limit", required_argument, NULL, + QEMU_NBD_OPT_HANDSHAKE_LIMIT }, { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, { "tls-hostname", required_argument, NULL, QEMU_NBD_OPT_TLSHOSTNAME }, { "tls-authz", required_argument, NULL, QEMU_NBD_OPT_TLSAUTHZ }, @@ -815,6 +819,13 @@ int main(int argc, char **argv) case QEMU_NBD_OPT_SELINUX_LABEL: selinux_label = optarg; break; + case QEMU_NBD_OPT_HANDSHAKE_LIMIT: + if (qemu_strtoi(optarg, NULL, 0, &handshake_limit) < 0 || + handshake_limit < 0) { + error_report("Invalid handshake limit '%s'", optarg); + exit(EXIT_FAILURE); + } + break; } } From ff12e6a5ff904e68e222b0ba567938943e0f4b68 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 3 Feb 2025 16:26:07 -0600 Subject: [PATCH 1656/2892] nbd/server: Allow users to adjust handshake limit in QMP Although defaulting the handshake limit to 10 seconds was a nice QoI change to weed out intentionally slow clients, it can interfere with integration testing done with manual NBD_OPT commands over 'nbdsh --opt-mode'. Expose a QMP knob 'handshake-max-secs' to allow the user to alter the timeout away from the default. The parameter name here intentionally matches the spelling of the constant added in commit fb1c2aaa98, and not the command-line spelling added in the previous patch for qemu-nbd; that's because in QMP, longer names serve as good self-documentation, and unlike the command line, machines don't have problems generating longer spellings. Signed-off-by: Eric Blake Message-ID: <20250203222722.650694-6-eblake@redhat.com> [eblake: s/max-secs/max-seconds/ in QMP] Acked-by: Markus Armbruster Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/monitor/block-hmp-cmds.c | 4 ++-- blockdev-nbd.c | 26 ++++++++++++++++++-------- include/block/nbd.h | 6 +++--- qapi/block-export.json | 10 ++++++++++ 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index ad7dc1de45..6919a49bf5 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -402,8 +402,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS, - &local_err); + nbd_server_start(addr, NBD_DEFAULT_HANDSHAKE_MAX_SECS, NULL, NULL, + NBD_DEFAULT_MAX_CONNECTIONS, &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 9e61fbaf2b..3f6f4ef92b 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -28,6 +28,7 @@ typedef struct NBDConn { typedef struct NBDServerData { QIONetListener *listener; + uint32_t handshake_max_secs; QCryptoTLSCreds *tlscreds; char *tlsauthz; uint32_t max_connections; @@ -84,8 +85,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nbd_update_server_watch(nbd_server); qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); - /* TODO - expose handshake timeout as QMP option */ - nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + nbd_client_new(cioc, nbd_server->handshake_max_secs, nbd_server->tlscreds, nbd_server->tlsauthz, nbd_blockdev_client_closed, conn); } @@ -162,9 +162,9 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) } -void nbd_server_start(SocketAddress *addr, const char *tls_creds, - const char *tls_authz, uint32_t max_connections, - Error **errp) +void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs, + const char *tls_creds, const char *tls_authz, + uint32_t max_connections, Error **errp) { if (nbd_server) { error_setg(errp, "NBD server already running"); @@ -173,6 +173,7 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, nbd_server = g_new0(NBDServerData, 1); nbd_server->max_connections = max_connections; + nbd_server->handshake_max_secs = handshake_max_secs; nbd_server->listener = qio_net_listener_new(); qio_net_listener_set_name(nbd_server->listener, @@ -210,12 +211,17 @@ void nbd_server_start_options(NbdServerOptions *arg, Error **errp) if (!arg->has_max_connections) { arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS; } + if (!arg->has_handshake_max_seconds) { + arg->handshake_max_seconds = NBD_DEFAULT_HANDSHAKE_MAX_SECS; + } - nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, - arg->max_connections, errp); + nbd_server_start(arg->addr, arg->handshake_max_seconds, arg->tls_creds, + arg->tls_authz, arg->max_connections, errp); } void qmp_nbd_server_start(SocketAddressLegacy *addr, + bool has_handshake_max_secs, + uint32_t handshake_max_secs, const char *tls_creds, const char *tls_authz, bool has_max_connections, uint32_t max_connections, @@ -226,8 +232,12 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, if (!has_max_connections) { max_connections = NBD_DEFAULT_MAX_CONNECTIONS; } + if (!has_handshake_max_secs) { + handshake_max_secs = NBD_DEFAULT_HANDSHAKE_MAX_SECS; + } - nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); + nbd_server_start(addr_flat, handshake_max_secs, tls_creds, tls_authz, + max_connections, errp); qapi_free_SocketAddress(addr_flat); } diff --git a/include/block/nbd.h b/include/block/nbd.h index d4f8b21aec..92987c76fd 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -428,9 +428,9 @@ void nbd_client_put(NBDClient *client); void nbd_server_is_qemu_nbd(int max_connections); bool nbd_server_is_running(void); int nbd_server_max_connections(void); -void nbd_server_start(SocketAddress *addr, const char *tls_creds, - const char *tls_authz, uint32_t max_connections, - Error **errp); +void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs, + const char *tls_creds, const char *tls_authz, + uint32_t max_connections, Error **errp); void nbd_server_start_options(NbdServerOptions *arg, Error **errp); /* nbd_read diff --git a/qapi/block-export.json b/qapi/block-export.json index 117b05d13c..68dcec7edc 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -17,6 +17,10 @@ # # @addr: Address on which to listen. # +# @handshake-max-seconds: Time limit, in seconds, at which a client +# that has not completed the negotiation handshake will be +# disconnected, 0 for no limit (since 10.0; default: 10). +# # @tls-creds: ID of the TLS credentials object (since 2.6). # # @tls-authz: ID of the QAuthZ authorization object used to validate @@ -34,6 +38,7 @@ ## { 'struct': 'NbdServerOptions', 'data': { 'addr': 'SocketAddress', + '*handshake-max-seconds': 'uint32', '*tls-creds': 'str', '*tls-authz': 'str', '*max-connections': 'uint32' } } @@ -52,6 +57,10 @@ # # @addr: Address on which to listen. # +# @handshake-max-seconds: Time limit, in seconds, at which a client +# that has not completed the negotiation handshake will be +# disconnected, or 0 for no limit (since 10.0; default: 10). +# # @tls-creds: ID of the TLS credentials object (since 2.6). # # @tls-authz: ID of the QAuthZ authorization object used to validate @@ -72,6 +81,7 @@ ## { 'command': 'nbd-server-start', 'data': { 'addr': 'SocketAddressLegacy', + '*handshake-max-seconds': 'uint32', '*tls-creds': 'str', '*tls-authz': 'str', '*max-connections': 'uint32' }, From f051a9c4dc70cd1b6eafa61aec8f3b9344e02e85 Mon Sep 17 00:00:00 2001 From: William Roche Date: Wed, 22 Jan 2025 19:40:53 +0000 Subject: [PATCH 1657/2892] system/physmem: take into account fd_offset for file fallocate Punching a hole in a file with fallocate needs to take into account the fd_offset value for a correct file location. But guest_memfd internal use doesn't currently consider fd_offset. Fixes: 4b870dc4d0c0 ("hostmem-file: add offset option") Signed-off-by: William Roche Reviewed-by: Peter Xu Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/r/20250122194053.3103617-2-william.roche@oracle.com Signed-off-by: Peter Xu --- system/physmem.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 67c9db9daa..235015f3ea 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3797,18 +3797,19 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) } ret = fallocate(rb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - start, length); + start + rb->fd_offset, length); if (ret) { ret = -errno; - error_report("%s: Failed to fallocate %s:%" PRIx64 " +%zx (%d)", - __func__, rb->idstr, start, length, ret); + error_report("%s: Failed to fallocate %s:%" PRIx64 "+%" PRIx64 + " +%zx (%d)", __func__, rb->idstr, start, + rb->fd_offset, length, ret); goto err; } #else ret = -ENOSYS; error_report("%s: fallocate not available/file" - "%s:%" PRIx64 " +%zx (%d)", - __func__, rb->idstr, start, length, ret); + "%s:%" PRIx64 "+%" PRIx64 " +%zx (%d)", __func__, + rb->idstr, start, rb->fd_offset, length, ret); goto err; #endif } @@ -3855,6 +3856,7 @@ int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, int ret = -1; #ifdef CONFIG_FALLOCATE_PUNCH_HOLE + /* ignore fd_offset with guest_memfd */ ret = fallocate(rb->guest_memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, start, length); From 5dd3a714d50d2777a33b53c7cb3192fbd49f4bb7 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 8 Feb 2025 15:06:53 +0800 Subject: [PATCH 1658/2892] hw/loongarch/virt: Rename filename acpi-build with virt-acpi-build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit File acpi-build.c is relative with virt machine type, rename it with virt-acpi-build.c Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/loongarch/meson.build | 2 +- hw/loongarch/{acpi-build.c => virt-acpi-build.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename hw/loongarch/{acpi-build.c => virt-acpi-build.c} (100%) diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index 005f017e21..3f020de7dc 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -4,6 +4,6 @@ loongarch_ss.add(files( )) common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.c')) loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c')) -loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) +loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/virt-acpi-build.c similarity index 100% rename from hw/loongarch/acpi-build.c rename to hw/loongarch/virt-acpi-build.c From e733b473b875cb65d7f4544e34fc19491249ae25 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 8 Feb 2025 15:06:54 +0800 Subject: [PATCH 1659/2892] hw/loongarch/virt: Rename function prefix name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace function prefix name loongarch_xxx with virt_xxx in file virt-acpi-build.c Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/loongarch/virt-acpi-build.c | 6 +++--- hw/loongarch/virt.c | 2 +- include/hw/loongarch/virt.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index fdd62acf7e..9ca88d63ae 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -656,7 +656,7 @@ static const VMStateDescription vmstate_acpi_build = { }, }; -static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms) +static bool virt_is_acpi_enabled(LoongArchVirtMachineState *lvms) { if (lvms->acpi == ON_OFF_AUTO_OFF) { return false; @@ -664,7 +664,7 @@ static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms) return true; } -void loongarch_acpi_setup(LoongArchVirtMachineState *lvms) +void virt_acpi_setup(LoongArchVirtMachineState *lvms) { AcpiBuildTables tables; AcpiBuildState *build_state; @@ -674,7 +674,7 @@ void loongarch_acpi_setup(LoongArchVirtMachineState *lvms) return; } - if (!loongarch_is_acpi_enabled(lvms)) { + if (!virt_is_acpi_enabled(lvms)) { ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); return; } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 63fa0f4e32..82d840d93f 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -686,7 +686,7 @@ static void virt_done(Notifier *notifier, void *data) LoongArchVirtMachineState *lvms = container_of(notifier, LoongArchVirtMachineState, machine_done); virt_build_smbios(lvms); - loongarch_acpi_setup(lvms); + virt_acpi_setup(lvms); virt_fdt_setup(lvms); } diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 9ba47793ef..062f63d874 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -64,5 +64,5 @@ struct LoongArchVirtMachineState { #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) -void loongarch_acpi_setup(LoongArchVirtMachineState *lvms); +void virt_acpi_setup(LoongArchVirtMachineState *lvms); #endif From 3754f985a8816a966b72da0ec8fb9e8a3cf689b9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 8 Feb 2025 15:06:55 +0800 Subject: [PATCH 1660/2892] hw/loongarch/virt: Add separate file for fdt building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similiar with virt-acpi-build.c, file virt-fdt-build.c is added here. And move functions relative with fdt table building to the file. It is only code movement and there is no function change. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/loongarch/meson.build | 4 +- hw/loongarch/virt-fdt-build.c | 535 ++++++++++++++++++++++++++++++++++ hw/loongarch/virt.c | 524 --------------------------------- include/hw/loongarch/virt.h | 1 + 4 files changed, 539 insertions(+), 525 deletions(-) create mode 100644 hw/loongarch/virt-fdt-build.c diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index 3f020de7dc..d494d1e283 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -3,7 +3,9 @@ loongarch_ss.add(files( 'boot.c', )) common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.c')) -loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c')) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files( + 'virt-fdt-build.c', + 'virt.c')) loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c new file mode 100644 index 0000000000..dbc269afba --- /dev/null +++ b/hw/loongarch/virt-fdt-build.c @@ -0,0 +1,535 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include +#include "hw/acpi/generic_event_device.h" +#include "hw/core/sysbus-fdt.h" +#include "hw/intc/loongarch_extioi.h" +#include "hw/loader.h" +#include "hw/loongarch/virt.h" +#include "hw/pci-host/gpex.h" +#include "hw/pci-host/ls7a.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "target/loongarch/cpu.h" + +static void create_fdt(LoongArchVirtMachineState *lvms) +{ + MachineState *ms = MACHINE(lvms); + uint8_t rng_seed[32]; + + ms->fdt = create_device_tree(&lvms->fdt_size); + if (!ms->fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + /* Header */ + qemu_fdt_setprop_string(ms->fdt, "/", "compatible", + "linux,dummy-loongson3"); + qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); + qemu_fdt_add_subnode(ms->fdt, "/chosen"); + + /* Pass seed to RNG */ + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); +} + +static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) +{ + int num; + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus; + LoongArchCPU *cpu; + CPUState *cs; + char *nodename, *map_path; + + qemu_fdt_add_subnode(ms->fdt, "/cpus"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); + + /* cpu nodes */ + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + nodename = g_strdup_printf("/cpus/cpu@%d", num); + cpu = LOONGARCH_CPU(cs); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + cpu->dtb_compatible); + if (possible_cpus->cpus[num].props.has_node_id) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", + possible_cpus->cpus[num].props.node_id); + } + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + g_free(nodename); + } + + /*cpu map */ + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + nodename = g_strdup_printf("/cpus/cpu@%d", num); + if (ms->smp.threads > 1) { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d/thread%d", + num / (ms->smp.cores * ms->smp.threads), + (num / ms->smp.threads) % ms->smp.cores, + num % ms->smp.threads); + } else { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d", + num / ms->smp.cores, + num % ms->smp.cores); + } + qemu_fdt_add_path(ms->fdt, map_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename); + + g_free(map_path); + g_free(nodename); + } +} + +static void fdt_add_memory_node(MachineState *ms, + uint64_t base, uint64_t size, int node_id) +{ + char *nodename = g_strdup_printf("/memory@%" PRIx64, base); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base, + size >> 32, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + + if (ms->numa_state && ms->numa_state->num_nodes) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); + } + + g_free(nodename); +} + +static void fdt_add_memory_nodes(MachineState *ms) +{ + hwaddr base, size, ram_size, gap; + int i, nb_numa_nodes, nodes; + NodeInfo *numa_info; + + ram_size = ms->ram_size; + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + nodes = nb_numa_nodes = ms->numa_state->num_nodes; + numa_info = ms->numa_state->nodes; + if (!nodes) { + nodes = 1; + } + + for (i = 0; i < nodes; i++) { + if (nb_numa_nodes) { + size = numa_info[i].node_mem; + } else { + size = ram_size; + } + + /* + * memory for the node splited into two part + * lowram: [base, +gap) + * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) + */ + if (size >= gap) { + fdt_add_memory_node(ms, base, gap, i); + size -= gap; + base = VIRT_HIGHMEM_BASE; + gap = ram_size - VIRT_LOWMEM_SIZE; + } + + if (size) { + fdt_add_memory_node(ms, base, size, i); + base += size; + gap -= size; + } + } +} + +static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms) +{ + char *nodename; + hwaddr base = VIRT_FWCFG_BASE; + const MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "qemu,fw-cfg-mmio"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base, 2, 0x18); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + g_free(nodename); +} + +static void fdt_add_flash_node(LoongArchVirtMachineState *lvms) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + MemoryRegion *flash_mem; + + hwaddr flash0_base; + hwaddr flash0_size; + + hwaddr flash1_base; + hwaddr flash1_size; + + flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + + flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); + flash1_base = flash_mem->addr; + flash1_size = memory_region_size(flash_mem); + + nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, flash0_base, 2, flash0_size, + 2, flash1_base, 2, flash1_size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); + g_free(nodename); +} + +static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms, + uint32_t *cpuintc_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + + *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/cpuic"); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,cpu-interrupt-controller"); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + g_free(nodename); +} + +static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms, + uint32_t *cpuintc_phandle, + uint32_t *eiointc_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + hwaddr extioi_base = APIC_BASE; + hwaddr extioi_size = EXTIOI_SIZE; + + *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,ls2k2000-eiointc"); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *cpuintc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, + extioi_base, 0x0, extioi_size); + g_free(nodename); +} + +static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms, + uint32_t *eiointc_phandle, + uint32_t *pch_pic_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + hwaddr pch_pic_base = VIRT_PCH_REG_BASE; + hwaddr pch_pic_size = VIRT_PCH_REG_SIZE; + + *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,pch-pic-1.0"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, + pch_pic_base, 0, pch_pic_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *eiointc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0); + g_free(nodename); +} + +static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms, + uint32_t *eiointc_phandle, + uint32_t *pch_msi_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW; + hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE; + + *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,pch-msi-1.0"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", + 0, pch_msi_base, + 0, pch_msi_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *eiointc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec", + VIRT_PCH_PIC_IRQ_NUM); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs", + EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM); + g_free(nodename); +} + +static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, + char *nodename, + uint32_t *pch_pic_phandle) +{ + int pin, dev; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {}; + uint32_t *irq_map = full_irq_map; + const MachineState *ms = MACHINE(lvms); + + /* + * This code creates a standard swizzle of interrupts such that + * each device's first interrupt is based on it's PCI_SLOT number. + * (See pci_swizzle_map_irq_fn()) + * + * We only need one entry per interrupt in the table (not one per + * possible slot) seeing the interrupt-map-mask will allow the table + * to wrap to any number of devices. + */ + + for (dev = 0; dev < PCI_NUM_PINS; dev++) { + int devfn = dev * 0x8; + + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); + int i = 0; + + /* Fill PCI address cells */ + irq_map[i] = cpu_to_be32(devfn << 8); + i += 3; + + /* Fill PCI Interrupt cells */ + irq_map[i] = cpu_to_be32(pin + 1); + i += 1; + + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(*pch_pic_phandle); + irq_map[i++] = cpu_to_be32(irq_nr); + + if (!irq_map_stride) { + irq_map_stride = i; + } + irq_map += irq_map_stride; + } + } + + + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map, + PCI_NUM_PINS * PCI_NUM_PINS * + irq_map_stride * sizeof(uint32_t)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + +static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, + uint32_t *pch_msi_phandle) +{ + char *nodename; + hwaddr base_mmio = VIRT_PCI_MEM_BASE; + hwaddr size_mmio = VIRT_PCI_MEM_SIZE; + hwaddr base_pio = VIRT_PCI_IO_BASE; + hwaddr size_pio = VIRT_PCI_IO_SIZE; + hwaddr base_pcie = VIRT_PCI_CFG_BASE; + hwaddr size_pcie = VIRT_PCI_CFG_SIZE; + hwaddr base = base_pcie; + const MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/pcie@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "pci-host-ecam-generic"); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0, + PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1)); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base_pcie, 2, size_pcie); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio); + qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", + 0, *pch_msi_phandle, 0, 0x10000); + fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle); + g_free(nodename); +} + +static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, hwaddr base, + int irq, bool chosen) +{ + char *nodename; + hwaddr size = VIRT_UART_SIZE; + MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/serial@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); + if (chosen) { + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + } + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *pch_pic_phandle); + g_free(nodename); +} + +static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle) +{ + char *nodename; + hwaddr base = VIRT_RTC_REG_BASE; + hwaddr size = VIRT_RTC_LEN; + MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/rtc@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,ls7a-rtc"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", + VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *pch_pic_phandle); + g_free(nodename); +} + +static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms) +{ + char *name; + uint32_t ged_handle; + MachineState *ms = MACHINE(lvms); + hwaddr base = VIRT_GED_REG_ADDR; + hwaddr size = ACPI_GED_REG_COUNT; + + ged_handle = qemu_fdt_alloc_phandle(ms->fdt); + name = g_strdup_printf("/ged@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon"); + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size); + /* 8 bit registers */ + qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0); + qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1); + qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle); + ged_handle = qemu_fdt_get_phandle(ms->fdt, name); + g_free(name); + + name = g_strdup_printf("/reboot"); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot"); + qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle); + qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET); + qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE); + g_free(name); + + name = g_strdup_printf("/poweroff"); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff"); + qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle); + qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL); + qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN | + (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS)); + g_free(name); +} + +void virt_fdt_setup(LoongArchVirtMachineState *lvms) +{ + MachineState *machine = MACHINE(lvms); + uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; + int i; + + create_fdt(lvms); + fdt_add_cpu_nodes(lvms); + fdt_add_memory_nodes(machine); + fdt_add_fw_cfg_node(lvms); + fdt_add_flash_node(lvms); + + /* Add cpu interrupt-controller */ + fdt_add_cpuic_node(lvms, &cpuintc_phandle); + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); + /* Add PCH PIC node */ + fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + /* Add pcie node */ + fdt_add_pcie_node(lvms, &pch_pic_phandle, &pch_msi_phandle); + + /* + * Create uart fdt node in reverse order so that they appear + * in the finished device tree lowest address first + */ + for (i = VIRT_UART_COUNT; i-- > 0;) { + hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE; + int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE; + fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0); + } + + fdt_add_rtc_node(lvms, &pch_pic_phandle); + fdt_add_ged_reset(lvms); + platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", + VIRT_PLATFORM_BUS_BASEADDRESS, + VIRT_PLATFORM_BUS_SIZE, + VIRT_PLATFORM_BUS_IRQ); + + /* + * Since lowmem region starts from 0 and Linux kernel legacy start address + * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer + * access. FDT size limit with 1 MiB. + * Put the FDT into the memory map as a ROM image: this will ensure + * the FDT is copied again upon reset, even if addr points into RAM. + */ + qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); + rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, + &address_space_memory); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); +} diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 82d840d93f..9f3843ee34 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -33,13 +33,9 @@ #include "hw/loongarch/fw_cfg.h" #include "target/loongarch/cpu.h" #include "hw/firmware/smbios.h" -#include "hw/acpi/aml-build.h" #include "qapi/qapi-visit-common.h" #include "hw/acpi/generic_event_device.h" #include "hw/mem/nvdimm.h" -#include "system/device_tree.h" -#include -#include "hw/core/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/display/ramfb.h" #include "hw/mem/pc-dimm.h" @@ -48,7 +44,6 @@ #include "hw/block/flash.h" #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" -#include "qemu/guest-random.h" static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) { @@ -135,471 +130,6 @@ static void virt_flash_map(LoongArchVirtMachineState *lvms, virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); } -static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms, - uint32_t *cpuintc_phandle) -{ - MachineState *ms = MACHINE(lvms); - char *nodename; - - *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt); - nodename = g_strdup_printf("/cpuic"); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongson,cpu-interrupt-controller"); - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); - g_free(nodename); -} - -static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms, - uint32_t *cpuintc_phandle, - uint32_t *eiointc_phandle) -{ - MachineState *ms = MACHINE(lvms); - char *nodename; - hwaddr extioi_base = APIC_BASE; - hwaddr extioi_size = EXTIOI_SIZE; - - *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt); - nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongson,ls2k2000-eiointc"); - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); - qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", - *cpuintc_phandle); - qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, - extioi_base, 0x0, extioi_size); - g_free(nodename); -} - -static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms, - uint32_t *eiointc_phandle, - uint32_t *pch_pic_phandle) -{ - MachineState *ms = MACHINE(lvms); - char *nodename; - hwaddr pch_pic_base = VIRT_PCH_REG_BASE; - hwaddr pch_pic_size = VIRT_PCH_REG_SIZE; - - *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt); - nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongson,pch-pic-1.0"); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, - pch_pic_base, 0, pch_pic_size); - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2); - qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", - *eiointc_phandle); - qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0); - g_free(nodename); -} - -static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms, - uint32_t *eiointc_phandle, - uint32_t *pch_msi_phandle) -{ - MachineState *ms = MACHINE(lvms); - char *nodename; - hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW; - hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE; - - *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); - nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongson,pch-msi-1.0"); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", - 0, pch_msi_base, - 0, pch_msi_size); - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", - *eiointc_phandle); - qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec", - VIRT_PCH_PIC_IRQ_NUM); - qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs", - EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM); - g_free(nodename); -} - -static void fdt_add_flash_node(LoongArchVirtMachineState *lvms) -{ - MachineState *ms = MACHINE(lvms); - char *nodename; - MemoryRegion *flash_mem; - - hwaddr flash0_base; - hwaddr flash0_size; - - hwaddr flash1_base; - hwaddr flash1_size; - - flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); - flash0_base = flash_mem->addr; - flash0_size = memory_region_size(flash_mem); - - flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); - flash1_base = flash_mem->addr; - flash1_size = memory_region_size(flash_mem); - - nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, flash0_base, 2, flash0_size, - 2, flash1_base, 2, flash1_size); - qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); - g_free(nodename); -} - -static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, - uint32_t *pch_pic_phandle) -{ - char *nodename; - hwaddr base = VIRT_RTC_REG_BASE; - hwaddr size = VIRT_RTC_LEN; - MachineState *ms = MACHINE(lvms); - - nodename = g_strdup_printf("/rtc@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongson,ls7a-rtc"); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4); - qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", - *pch_pic_phandle); - g_free(nodename); -} - -static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms) -{ - char *name; - uint32_t ged_handle; - MachineState *ms = MACHINE(lvms); - hwaddr base = VIRT_GED_REG_ADDR; - hwaddr size = ACPI_GED_REG_COUNT; - - ged_handle = qemu_fdt_alloc_phandle(ms->fdt); - name = g_strdup_printf("/ged@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, name); - qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size); - /* 8 bit registers */ - qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0); - qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1); - qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle); - ged_handle = qemu_fdt_get_phandle(ms->fdt, name); - g_free(name); - - name = g_strdup_printf("/reboot"); - qemu_fdt_add_subnode(ms->fdt, name); - qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot"); - qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle); - qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET); - qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE); - g_free(name); - - name = g_strdup_printf("/poweroff"); - qemu_fdt_add_subnode(ms->fdt, name); - qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff"); - qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle); - qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL); - qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN | - (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS)); - g_free(name); -} - -static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, - uint32_t *pch_pic_phandle, hwaddr base, - int irq, bool chosen) -{ - char *nodename; - hwaddr size = VIRT_UART_SIZE; - MachineState *ms = MACHINE(lvms); - - nodename = g_strdup_printf("/serial@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a"); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); - qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); - if (chosen) { - qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); - } - qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4); - qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", - *pch_pic_phandle); - g_free(nodename); -} - -static void create_fdt(LoongArchVirtMachineState *lvms) -{ - MachineState *ms = MACHINE(lvms); - uint8_t rng_seed[32]; - - ms->fdt = create_device_tree(&lvms->fdt_size); - if (!ms->fdt) { - error_report("create_device_tree() failed"); - exit(1); - } - - /* Header */ - qemu_fdt_setprop_string(ms->fdt, "/", "compatible", - "linux,dummy-loongson3"); - qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); - qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); - qemu_fdt_add_subnode(ms->fdt, "/chosen"); - - /* Pass seed to RNG */ - qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); - qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); -} - -static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) -{ - int num; - MachineState *ms = MACHINE(lvms); - MachineClass *mc = MACHINE_GET_CLASS(ms); - const CPUArchIdList *possible_cpus; - LoongArchCPU *cpu; - CPUState *cs; - char *nodename, *map_path; - - qemu_fdt_add_subnode(ms->fdt, "/cpus"); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); - - /* cpu nodes */ - possible_cpus = mc->possible_cpu_arch_ids(ms); - for (num = 0; num < possible_cpus->len; num++) { - cs = possible_cpus->cpus[num].cpu; - if (cs == NULL) { - continue; - } - - nodename = g_strdup_printf("/cpus/cpu@%d", num); - cpu = LOONGARCH_CPU(cs); - - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - cpu->dtb_compatible); - if (possible_cpus->cpus[num].props.has_node_id) { - qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", - possible_cpus->cpus[num].props.node_id); - } - qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", - qemu_fdt_alloc_phandle(ms->fdt)); - g_free(nodename); - } - - /*cpu map */ - qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); - for (num = 0; num < possible_cpus->len; num++) { - cs = possible_cpus->cpus[num].cpu; - if (cs == NULL) { - continue; - } - - nodename = g_strdup_printf("/cpus/cpu@%d", num); - if (ms->smp.threads > 1) { - map_path = g_strdup_printf( - "/cpus/cpu-map/socket%d/core%d/thread%d", - num / (ms->smp.cores * ms->smp.threads), - (num / ms->smp.threads) % ms->smp.cores, - num % ms->smp.threads); - } else { - map_path = g_strdup_printf( - "/cpus/cpu-map/socket%d/core%d", - num / ms->smp.cores, - num % ms->smp.cores); - } - qemu_fdt_add_path(ms->fdt, map_path); - qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename); - - g_free(map_path); - g_free(nodename); - } -} - -static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms) -{ - char *nodename; - hwaddr base = VIRT_FWCFG_BASE; - const MachineState *ms = MACHINE(lvms); - - nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, - "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, base, 2, 0x18); - qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); - g_free(nodename); -} - -static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, - char *nodename, - uint32_t *pch_pic_phandle) -{ - int pin, dev; - uint32_t irq_map_stride = 0; - uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {}; - uint32_t *irq_map = full_irq_map; - const MachineState *ms = MACHINE(lvms); - - /* This code creates a standard swizzle of interrupts such that - * each device's first interrupt is based on it's PCI_SLOT number. - * (See pci_swizzle_map_irq_fn()) - * - * We only need one entry per interrupt in the table (not one per - * possible slot) seeing the interrupt-map-mask will allow the table - * to wrap to any number of devices. - */ - - for (dev = 0; dev < PCI_NUM_PINS; dev++) { - int devfn = dev * 0x8; - - for (pin = 0; pin < PCI_NUM_PINS; pin++) { - int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); - int i = 0; - - /* Fill PCI address cells */ - irq_map[i] = cpu_to_be32(devfn << 8); - i += 3; - - /* Fill PCI Interrupt cells */ - irq_map[i] = cpu_to_be32(pin + 1); - i += 1; - - /* Fill interrupt controller phandle and cells */ - irq_map[i++] = cpu_to_be32(*pch_pic_phandle); - irq_map[i++] = cpu_to_be32(irq_nr); - - if (!irq_map_stride) { - irq_map_stride = i; - } - irq_map += irq_map_stride; - } - } - - - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map, - PCI_NUM_PINS * PCI_NUM_PINS * - irq_map_stride * sizeof(uint32_t)); - qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", - 0x1800, 0, 0, 0x7); -} - -static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms, - uint32_t *pch_pic_phandle, - uint32_t *pch_msi_phandle) -{ - char *nodename; - hwaddr base_mmio = VIRT_PCI_MEM_BASE; - hwaddr size_mmio = VIRT_PCI_MEM_SIZE; - hwaddr base_pio = VIRT_PCI_IO_BASE; - hwaddr size_pio = VIRT_PCI_IO_SIZE; - hwaddr base_pcie = VIRT_PCI_CFG_BASE; - hwaddr size_pcie = VIRT_PCI_CFG_SIZE; - hwaddr base = base_pcie; - - const MachineState *ms = MACHINE(lvms); - - nodename = g_strdup_printf("/pcie@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, - "compatible", "pci-host-ecam-generic"); - qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci"); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2); - qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0); - qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0, - PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1)); - qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, base_pcie, 2, size_pcie); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", - 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET, - 2, base_pio, 2, size_pio, - 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, - 2, base_mmio, 2, size_mmio); - qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", - 0, *pch_msi_phandle, 0, 0x10000); - - fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle); - - g_free(nodename); -} - -static void fdt_add_memory_node(MachineState *ms, - uint64_t base, uint64_t size, int node_id) -{ - char *nodename = g_strdup_printf("/memory@%" PRIx64, base); - - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base, - size >> 32, size); - qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); - - if (ms->numa_state && ms->numa_state->num_nodes) { - qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); - } - - g_free(nodename); -} - -static void fdt_add_memory_nodes(MachineState *ms) -{ - hwaddr base, size, ram_size, gap; - int i, nb_numa_nodes, nodes; - NodeInfo *numa_info; - - ram_size = ms->ram_size; - base = VIRT_LOWMEM_BASE; - gap = VIRT_LOWMEM_SIZE; - nodes = nb_numa_nodes = ms->numa_state->num_nodes; - numa_info = ms->numa_state->nodes; - if (!nodes) { - nodes = 1; - } - - for (i = 0; i < nodes; i++) { - if (nb_numa_nodes) { - size = numa_info[i].node_mem; - } else { - size = ram_size; - } - - /* - * memory for the node splited into two part - * lowram: [base, +gap) - * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) - */ - if (size >= gap) { - fdt_add_memory_node(ms, base, gap, i); - size -= gap; - base = VIRT_HIGHMEM_BASE; - gap = ram_size - VIRT_LOWMEM_SIZE; - } - - if (size) { - fdt_add_memory_node(ms, base, size, i); - base += size; - gap -= size; - } - } -} - static void virt_build_smbios(LoongArchVirtMachineState *lvms) { MachineState *ms = MACHINE(lvms); @@ -627,60 +157,6 @@ static void virt_build_smbios(LoongArchVirtMachineState *lvms) } } -static void virt_fdt_setup(LoongArchVirtMachineState *lvms) -{ - MachineState *machine = MACHINE(lvms); - uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; - int i; - - create_fdt(lvms); - fdt_add_cpu_nodes(lvms); - fdt_add_memory_nodes(machine); - fdt_add_fw_cfg_node(lvms); - fdt_add_flash_node(lvms); - - /* Add cpu interrupt-controller */ - fdt_add_cpuic_node(lvms, &cpuintc_phandle); - /* Add Extend I/O Interrupt Controller node */ - fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); - /* Add PCH PIC node */ - fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); - /* Add PCH MSI node */ - fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); - /* Add pcie node */ - fdt_add_pcie_node(lvms, &pch_pic_phandle, &pch_msi_phandle); - - /* - * Create uart fdt node in reverse order so that they appear - * in the finished device tree lowest address first - */ - for (i = VIRT_UART_COUNT; i-- > 0;) { - hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE; - int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE; - fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0); - } - - fdt_add_rtc_node(lvms, &pch_pic_phandle); - fdt_add_ged_reset(lvms); - platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", - VIRT_PLATFORM_BUS_BASEADDRESS, - VIRT_PLATFORM_BUS_SIZE, - VIRT_PLATFORM_BUS_IRQ); - - /* - * Since lowmem region starts from 0 and Linux kernel legacy start address - * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer - * access. FDT size limit with 1 MiB. - * Put the FDT into the memory map as a ROM image: this will ensure - * the FDT is copied again upon reset, even if addr points into RAM. - */ - qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); - rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, - &address_space_memory); - qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, - rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); -} - static void virt_done(Notifier *notifier, void *data) { LoongArchVirtMachineState *lvms = container_of(notifier, diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 062f63d874..f01350017b 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -65,4 +65,5 @@ struct LoongArchVirtMachineState { #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) void virt_acpi_setup(LoongArchVirtMachineState *lvms); +void virt_fdt_setup(LoongArchVirtMachineState *lvms); #endif From 28bec94c7da29860c27394b1668afc85762db981 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 5 Feb 2025 11:36:12 +0800 Subject: [PATCH 1661/2892] hw/loongarch/virt: Set iocsr address space when CPU is created There is only one iocsr address space for the whole virt-machine board. When CPU is created, the one of percpu points to that of the board. Here set iocsr address space when CPU is created rather than IPI creation stage. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 9f3843ee34..db48217228 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -388,7 +388,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) cpudev = DEVICE(cpu_state); lacpu = LOONGARCH_CPU(cpu_state); env = &(lacpu->env); - env->address_space_iocsr = &lvms->as_iocsr; /* connect ipi irq to cpu irq */ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); @@ -689,6 +688,7 @@ static void virt_init(MachineState *machine) machine->possible_cpus->cpus[i].cpu = cpu; lacpu = LOONGARCH_CPU(cpu); lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; + lacpu->env.address_space_iocsr = &lvms->as_iocsr; } fw_cfg_add_memory(machine); From 2d2c37c492770ab627c0e60836198745a45c0385 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 5 Feb 2025 11:36:13 +0800 Subject: [PATCH 1662/2892] hw/loongarch/virt: Remove unused ipistate Field ipistate in LoongArch CPU object is not used any more, remove it here. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt.c | 5 ----- target/loongarch/cpu.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index db48217228..da98b21c58 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -324,8 +324,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) DeviceState *pch_pic, *pch_msi, *cpudev; DeviceState *ipi, *extioi; SysBusDevice *d; - LoongArchCPU *lacpu; - CPULoongArchState *env; CPUState *cpu_state; int cpu, pin, i, start, num; @@ -386,12 +384,9 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) for (cpu = 0; cpu < ms->smp.cpus; cpu++) { cpu_state = qemu_get_cpu(cpu); cpudev = DEVICE(cpu_state); - lacpu = LOONGARCH_CPU(cpu_state); - env = &(lacpu->env); /* connect ipi irq to cpu irq */ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); - env->ipistate = ipi; } /* Create EXTIOI device */ diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 8eee49a984..f2a23b7a43 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -385,8 +385,6 @@ typedef struct CPUArchState { bool load_elf; uint64_t elf_address; uint32_t mp_state; - /* Store ipistate to access from this struct */ - DeviceState *ipistate; struct loongarch_boot_info *boot_info; #endif From 456739ce4347c21b6fa2ec1b6585bc4a6504446f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 5 Feb 2025 11:36:14 +0800 Subject: [PATCH 1663/2892] hw/loongarch/virt: CPU irq line connection improvement Interrupt controller extioi and ipi connect to CPU with irq line method. With command -smp x, -device la464-loongarch-cpu, smp.cpus is not accurate for all possible CPU objects, possible_cpu_arch_ids() is used. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt.c | 60 ++++++++++++++++++++++--------------- include/hw/loongarch/virt.h | 2 ++ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index da98b21c58..f2aa0a9782 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -318,14 +318,43 @@ static void virt_devices_init(DeviceState *pch_pic, lvms->platform_bus_dev = create_platform_bus(pch_pic); } +static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) +{ + int num, pin; + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus; + CPUState *cs; + + /* cpu nodes */ + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + /* connect ipi irq to cpu irq */ + qdev_connect_gpio_out(lvms->ipi, num, + qdev_get_gpio_in(DEVICE(cs), IRQ_IPI)); + + /* + * connect ext irq to the cpu irq + * cpu_pin[9:2] <= intc_pin[7:0] + */ + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_connect_gpio_out(lvms->extioi, (num * LS3A_INTC_IP + pin), + qdev_get_gpio_in(DEVICE(cs), pin + 2)); + } + } +} + static void virt_irq_init(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lvms); - DeviceState *pch_pic, *pch_msi, *cpudev; + DeviceState *pch_pic, *pch_msi; DeviceState *ipi, *extioi; SysBusDevice *d; - CPUState *cpu_state; - int cpu, pin, i, start, num; + int i, start, num; /* * Extended IRQ model. @@ -373,6 +402,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Create IPI device */ ipi = qdev_new(TYPE_LOONGARCH_IPI); + lvms->ipi = ipi; sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); /* IPI iocsr memory region */ @@ -381,16 +411,9 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); - for (cpu = 0; cpu < ms->smp.cpus; cpu++) { - cpu_state = qemu_get_cpu(cpu); - cpudev = DEVICE(cpu_state); - - /* connect ipi irq to cpu irq */ - qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); - } - /* Create EXTIOI device */ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + lvms->extioi = extioi; if (virt_is_veiointc_enabled(lvms)) { qdev_prop_set_bit(extioi, "has-virtualization-extension", true); } @@ -402,18 +425,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); } - /* - * connect ext irq to the cpu irq - * cpu_pin[9:2] <= intc_pin[7:0] - */ - for (cpu = 0; cpu < ms->smp.cpus; cpu++) { - cpudev = DEVICE(qemu_get_cpu(cpu)); - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_connect_gpio_out(extioi, (cpu * 8 + pin), - qdev_get_gpio_in(cpudev, pin + 2)); - } - } - + virt_cpu_irq_init(lvms); pch_pic = qdev_new(TYPE_LOONGARCH_PIC); num = VIRT_PCH_PIC_IRQ_NUM; qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index f01350017b..661efae61d 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -60,6 +60,8 @@ struct LoongArchVirtMachineState { MemoryRegion iocsr_mem; AddressSpace as_iocsr; struct loongarch_boot_info bootinfo; + DeviceState *ipi; + DeviceState *extioi; }; #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") From 9976be3911a2d0503f026ae37c17077273bf30ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 6 Dec 2024 11:45:24 +0000 Subject: [PATCH 1664/2892] scripts: improve error from qemu-trace-stap on missing 'stap' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the 'stap' binary is missing in $PATH, a huge trace is thrown $ qemu-trace-stap list /usr/bin/qemu-system-x86_64 Traceback (most recent call last): File "/usr/bin/qemu-trace-stap", line 169, in main() File "/usr/bin/qemu-trace-stap", line 165, in main args.func(args) File "/usr/bin/qemu-trace-stap", line 83, in cmd_run subprocess.call(stapargs) File "/usr/lib64/python3.12/subprocess.py", line 389, in call with Popen(*popenargs, **kwargs) as p: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.12/subprocess.py", line 1026, in {}init{} self._execute_child(args, executable, preexec_fn, close_fds, File "/usr/lib64/python3.12/subprocess.py", line 1955, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename) FileNotFoundError: [Errno 2] No such file or directory: 'stap' With this change the user now gets $ qemu-trace-stap list /usr/bin/qemu-system-x86_64 Unable to find 'stap' in $PATH Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-id: 20241206114524.1666664-1-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/qemu-trace-stap | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/qemu-trace-stap b/scripts/qemu-trace-stap index eb6e951ff2..e983460ee7 100755 --- a/scripts/qemu-trace-stap +++ b/scripts/qemu-trace-stap @@ -56,6 +56,7 @@ def tapset_dir(binary): def cmd_run(args): + stap = which("stap") prefix = probe_prefix(args.binary) tapsets = tapset_dir(args.binary) @@ -76,7 +77,7 @@ def cmd_run(args): # We request an 8MB buffer, since the stap default 1MB buffer # can be easily overflowed by frequently firing QEMU traces - stapargs = ["stap", "-s", "8", "-I", tapsets ] + stapargs = [stap, "-s", "8", "-I", tapsets ] if args.pid is not None: stapargs.extend(["-x", args.pid]) stapargs.extend(["-e", script]) @@ -84,6 +85,7 @@ def cmd_run(args): def cmd_list(args): + stap = which("stap") tapsets = tapset_dir(args.binary) if args.verbose: @@ -96,7 +98,7 @@ def cmd_list(args): if verbose: print("Listing probes with name '%s'" % script) - proc = subprocess.Popen(["stap", "-I", tapsets, "-l", script], + proc = subprocess.Popen([stap, "-I", tapsets, "-l", script], stdout=subprocess.PIPE, universal_newlines=True) out, err = proc.communicate() From 52012209e1802e67aa186459e3e965f669e553df Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:42 +0100 Subject: [PATCH 1665/2892] physmem: factor out memory_region_is_ram_device() check in memory_access_is_direct() As documented in commit 4a2e242bbb306 ("memory: Don't use memcpy for ram_device regions"), we disallow direct access to RAM DEVICE regions. Let's make this clearer to prepare for further changes. Note that romd regions will never be RAM DEVICE at the same time. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-2-david@redhat.com Signed-off-by: Peter Xu --- include/exec/memory.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 9f73b59867..5cd7574c60 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2997,12 +2997,19 @@ bool prepare_mmio_access(MemoryRegion *mr); static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { + /* + * RAM DEVICE regions can be accessed directly using memcpy, but it might + * be MMIO and access using mempy can be wrong (e.g., using instructions not + * intended for MMIO access). So we treat this as IO. + */ + if (memory_region_is_ram_device(mr)) { + return false; + } if (is_write) { return memory_region_is_ram(mr) && !mr->readonly && - !mr->rom_device && !memory_region_is_ram_device(mr); + !mr->rom_device; } else { - return (memory_region_is_ram(mr) && !memory_region_is_ram_device(mr)) || - memory_region_is_romd(mr); + return memory_region_is_ram(mr) || memory_region_is_romd(mr); } } From e76d7b6b8cd564d4d5ea6e7c7daea541e100caa4 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:43 +0100 Subject: [PATCH 1666/2892] physmem: factor out RAM/ROMD check in memory_access_is_direct() Let's factor more of the generic "is this directly accessible" check, independent of the "write" condition out. Note that the "!mr->rom_device" check in the write case essentially disallows the memory_region_is_romd() condition again. Further note that RAM DEVICE regions are also RAM regions, so we can check for RAM+ROMD first. This is a preparation for further changes. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-3-david@redhat.com Signed-off-by: Peter Xu --- include/exec/memory.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 5cd7574c60..cb35c38402 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2997,6 +2997,10 @@ bool prepare_mmio_access(MemoryRegion *mr); static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { + /* ROM DEVICE regions only allow direct access if in ROMD mode. */ + if (!memory_region_is_ram(mr) && !memory_region_is_romd(mr)) { + return false; + } /* * RAM DEVICE regions can be accessed directly using memcpy, but it might * be MMIO and access using mempy can be wrong (e.g., using instructions not @@ -3006,11 +3010,9 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) return false; } if (is_write) { - return memory_region_is_ram(mr) && !mr->readonly && - !mr->rom_device; - } else { - return memory_region_is_ram(mr) || memory_region_is_romd(mr); + return !mr->readonly && !mr->rom_device; } + return true; } /** From 7fd970a7d35af543992bf85e77b75de6b8125eb1 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:44 +0100 Subject: [PATCH 1667/2892] physmem: factor out direct access check into memory_region_supports_direct_access() Let's factor the complete "directly accessible" check independent of the "write" condition out so we can reuse it next. We can now split up the checks RAM and ROMD check, so we really only check for RAM DEVICE in case of RAM -- ROM DEVICE is neither RAM not RAM DEVICE. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-4-david@redhat.com Signed-off-by: Peter Xu --- include/exec/memory.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index cb35c38402..4e2cf95ab6 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2995,10 +2995,13 @@ MemTxResult address_space_write_cached_slow(MemoryRegionCache *cache, int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr); bool prepare_mmio_access(MemoryRegion *mr); -static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +static inline bool memory_region_supports_direct_access(MemoryRegion *mr) { /* ROM DEVICE regions only allow direct access if in ROMD mode. */ - if (!memory_region_is_ram(mr) && !memory_region_is_romd(mr)) { + if (memory_region_is_romd(mr)) { + return true; + } + if (!memory_region_is_ram(mr)) { return false; } /* @@ -3006,7 +3009,12 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) * be MMIO and access using mempy can be wrong (e.g., using instructions not * intended for MMIO access). So we treat this as IO. */ - if (memory_region_is_ram_device(mr)) { + return !memory_region_is_ram_device(mr); +} + +static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +{ + if (!memory_region_supports_direct_access(mr)) { return false; } if (is_write) { From d4337aa8e222802d342b9f58440ca8e005b8bf91 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:45 +0100 Subject: [PATCH 1668/2892] physmem: disallow direct access to RAM DEVICE in address_space_write_rom() As documented in commit 4a2e242bbb306 ("memory: Don't use memcpy for ram_device regions"), we disallow direct access to RAM DEVICE regions. This change implies that address_space_write_rom() and cpu_memory_rw_debug() won't be able to write to RAM DEVICE regions. It will also affect cpu_flush_icache_range(), but it's only used by hw/core/loader.c after writing to ROM, so it is expected to not apply here with RAM DEVICE. This fixes direct access to these regions where we don't want direct access. We'll extend cpu_memory_rw_debug() next to also be able to write to these (and IO) regions. This is a preparation for further changes. Cc: Alex Williamson Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-5-david@redhat.com Signed-off-by: Peter Xu --- system/physmem.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 235015f3ea..cff15ca1df 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3137,8 +3137,7 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, l = len; mr = address_space_translate(as, addr, &addr1, &l, true, attrs); - if (!(memory_region_is_ram(mr) || - memory_region_is_romd(mr))) { + if (!memory_region_supports_direct_access(mr)) { l = memory_access_size(mr, l, addr1); } else { /* ROM/RAM case */ From d732b5a4ac3e8222e9527654f067bb766fdaecb6 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:46 +0100 Subject: [PATCH 1669/2892] memory: pass MemTxAttrs to memory_access_is_direct() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to pass another flag that will be stored in MemTxAttrs. So pass MemTxAttrs directly. Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-6-david@redhat.com [peterx: Fix MacOS builds] Signed-off-by: Peter Xu --- hw/core/loader.c | 2 +- hw/display/apple-gfx.m | 3 ++- hw/remote/vfio-user-obj.c | 2 +- include/exec/memory.h | 5 +++-- system/memory_ldst.c.inc | 18 +++++++++--------- system/physmem.c | 12 ++++++------ 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index fd25c5e01b..332b879a0b 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -144,7 +144,7 @@ ssize_t load_image_mr(const char *filename, MemoryRegion *mr) { ssize_t size; - if (!memory_access_is_direct(mr, false)) { + if (!memory_access_is_direct(mr, false, MEMTXATTRS_UNSPECIFIED)) { /* Can only load an image into RAM or ROM */ return -1; } diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index aa1455b629..1554f3b801 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -137,7 +137,8 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, MEMTXATTRS_UNSPECIFIED); if (!ram_region || ram_region_length < length || - !memory_access_is_direct(ram_region, !read_only)) { + !memory_access_is_direct(ram_region, !read_only, + MEMTXATTRS_UNSPECIFIED)) { return NULL; } diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 9e5ff6d87a..6e51a92856 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -358,7 +358,7 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, int access_size; uint64_t val; - if (memory_access_is_direct(mr, is_write)) { + if (memory_access_is_direct(mr, is_write, MEMTXATTRS_UNSPECIFIED)) { /** * Some devices expose a PCI expansion ROM, which could be buffer * based as compared to other regions which are primarily based on diff --git a/include/exec/memory.h b/include/exec/memory.h index 4e2cf95ab6..b18ecf933e 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3012,7 +3012,8 @@ static inline bool memory_region_supports_direct_access(MemoryRegion *mr) return !memory_region_is_ram_device(mr); } -static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write, + MemTxAttrs attrs) { if (!memory_region_supports_direct_access(mr)) { return false; @@ -3053,7 +3054,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, fv = address_space_to_flatview(as); l = len; mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); - if (len == l && memory_access_is_direct(mr, false)) { + if (len == l && memory_access_is_direct(mr, false, attrs)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); memcpy(buf, ptr, len); } else { diff --git a/system/memory_ldst.c.inc b/system/memory_ldst.c.inc index 0e6f3940a9..7f32d3d9ff 100644 --- a/system/memory_ldst.c.inc +++ b/system/memory_ldst.c.inc @@ -34,7 +34,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 4 || !memory_access_is_direct(mr, false)) { + if (l < 4 || !memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -103,7 +103,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 8 || !memory_access_is_direct(mr, false)) { + if (l < 8 || !memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -170,7 +170,7 @@ uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (!memory_access_is_direct(mr, false)) { + if (!memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -207,7 +207,7 @@ static inline uint16_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 2 || !memory_access_is_direct(mr, false)) { + if (l < 2 || !memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -277,7 +277,7 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 4 || !memory_access_is_direct(mr, true)) { + if (l < 4 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_32, attrs); @@ -314,7 +314,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 4 || !memory_access_is_direct(mr, true)) { + if (l < 4 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_32 | devend_memop(endian), attrs); @@ -377,7 +377,7 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (!memory_access_is_direct(mr, true)) { + if (!memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_8, attrs); } else { @@ -410,7 +410,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 2 || !memory_access_is_direct(mr, true)) { + if (l < 2 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_16 | devend_memop(endian), attrs); @@ -474,7 +474,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 8 || !memory_access_is_direct(mr, true)) { + if (l < 8 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_64 | devend_memop(endian), attrs); diff --git a/system/physmem.c b/system/physmem.c index cff15ca1df..8745c10c9d 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -573,7 +573,7 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, is_write, true, &as, attrs); mr = section.mr; - if (xen_enabled() && memory_access_is_direct(mr, is_write)) { + if (xen_enabled() && memory_access_is_direct(mr, is_write, attrs)) { hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } @@ -2869,7 +2869,7 @@ static MemTxResult flatview_write_continue_step(MemTxAttrs attrs, return MEMTX_ACCESS_ERROR; } - if (!memory_access_is_direct(mr, true)) { + if (!memory_access_is_direct(mr, true, attrs)) { uint64_t val; MemTxResult result; bool release_lock = prepare_mmio_access(mr); @@ -2965,7 +2965,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, return MEMTX_ACCESS_ERROR; } - if (!memory_access_is_direct(mr, false)) { + if (!memory_access_is_direct(mr, false, attrs)) { /* I/O case */ uint64_t val; MemTxResult result; @@ -3274,7 +3274,7 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, while (len > 0) { l = len; mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); - if (!memory_access_is_direct(mr, is_write)) { + if (!memory_access_is_direct(mr, is_write, attrs)) { l = memory_access_size(mr, l, addr); if (!memory_region_access_valid(mr, xlat, l, is_write, attrs)) { return false; @@ -3354,7 +3354,7 @@ void *address_space_map(AddressSpace *as, fv = address_space_to_flatview(as); mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); - if (!memory_access_is_direct(mr, is_write)) { + if (!memory_access_is_direct(mr, is_write, attrs)) { size_t used = qatomic_read(&as->bounce_buffer_size); for (;;) { hwaddr alloc = MIN(as->max_bounce_buffer_size - used, l); @@ -3487,7 +3487,7 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, mr = cache->mrs.mr; memory_region_ref(mr); - if (memory_access_is_direct(mr, is_write)) { + if (memory_access_is_direct(mr, is_write, MEMTXATTRS_UNSPECIFIED)) { /* We don't care about the memory attributes here as we're only * doing this if we found actual RAM, which behaves the same * regardless of attributes; so UNSPECIFIED is fine. From 425ce9b37b98799b46cd0bed0df3dc3af25ba57a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:47 +0100 Subject: [PATCH 1670/2892] hmp: use cpu_get_phys_page_debug() in hmp_gva2gpa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need the MemTxAttrs, so let's simply use the simpler function variant. Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-7-david@redhat.com Signed-off-by: Peter Xu --- monitor/hmp-cmds-target.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 27ffe61818..239c2a61a4 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -301,7 +301,6 @@ void hmp_gpa2hva(Monitor *mon, const QDict *qdict) void hmp_gva2gpa(Monitor *mon, const QDict *qdict) { target_ulong addr = qdict_get_int(qdict, "addr"); - MemTxAttrs attrs; CPUState *cs = mon_get_cpu(mon); hwaddr gpa; @@ -310,7 +309,7 @@ void hmp_gva2gpa(Monitor *mon, const QDict *qdict) return; } - gpa = cpu_get_phys_page_attrs_debug(cs, addr & TARGET_PAGE_MASK, &attrs); + gpa = cpu_get_phys_page_debug(cs, addr & TARGET_PAGE_MASK); if (gpa == -1) { monitor_printf(mon, "Unmapped\n"); } else { From 1cceedd7726556052d3d3bcf08a07b7762f8aa7c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 Feb 2025 09:46:48 +0100 Subject: [PATCH 1671/2892] physmem: teach cpu_memory_rw_debug() to write to more memory regions Right now, we only allow for writing to memory regions that allow direct access using memcpy etc; all other writes are simply ignored. This implies that debugging guests will not work as expected when writing to MMIO device regions. Let's extend cpu_memory_rw_debug() to write to more memory regions, including MMIO device regions. Reshuffle the condition in memory_access_is_direct() to make it easier to read and add a comment. While this change implies that debug access can now also write to MMIO devices, we now are also permit ELF image loads and similar users of cpu_memory_rw_debug() to write to MMIO devices; currently we ignore these writes. Peter assumes [1] that there's probably a class of guest images, which will start writing junk (likely zeroes) into device model registers; we previously would silently ignore any such bogus ELF sections. Likely these images are of questionable correctness and this can be ignored. If ever a problem, we could make these cases use address_space_write_rom() instead, which is left unchanged for now. This patch is based on previous work by Stefan Zabka. [1] https://lore.kernel.org/all/CAFEAcA_2CEJKFyjvbwmpt=on=GgMVamQ5hiiVt+zUr6AY3X=Xg@mail.gmail.com/ Resolves: https://gitlab.com/qemu-project/qemu/-/issues/213 Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Link: https://lore.kernel.org/r/20250210084648.33798-8-david@redhat.com Signed-off-by: Peter Xu --- hw/core/cpu-system.c | 13 +++++++++---- include/exec/memattrs.h | 5 ++++- include/exec/memory.h | 3 ++- system/physmem.c | 9 ++------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 6aae28a349..6e307c8959 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -51,13 +51,18 @@ hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs) { CPUClass *cc = CPU_GET_CLASS(cpu); + hwaddr paddr; if (cc->sysemu_ops->get_phys_page_attrs_debug) { - return cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, attrs); + paddr = cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, attrs); + } else { + /* Fallback for CPUs which don't implement the _attrs_ hook */ + *attrs = MEMTXATTRS_UNSPECIFIED; + paddr = cc->sysemu_ops->get_phys_page_debug(cpu, addr); } - /* Fallback for CPUs which don't implement the _attrs_ hook */ - *attrs = MEMTXATTRS_UNSPECIFIED; - return cc->sysemu_ops->get_phys_page_debug(cpu, addr); + /* Indicate that this is a debug access. */ + attrs->debug = 1; + return paddr; } hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 060b7e7131..8db1d30464 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -44,6 +44,8 @@ typedef struct MemTxAttrs { * (see MEMTX_ACCESS_ERROR). */ unsigned int memory:1; + /* Debug access that can even write to ROM. */ + unsigned int debug:1; /* Requester ID (for MSI for example) */ unsigned int requester_id:16; @@ -56,7 +58,8 @@ typedef struct MemTxAttrs { * Bus masters which don't specify any attributes will get this * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can * distinguish "all attributes deliberately clear" from - * "didn't specify" if necessary. + * "didn't specify" if necessary. "debug" can be set alongside + * "unspecified". */ bool unspecified; diff --git a/include/exec/memory.h b/include/exec/memory.h index b18ecf933e..78c4e0aec8 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3018,7 +3018,8 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write, if (!memory_region_supports_direct_access(mr)) { return false; } - if (is_write) { + /* Debug access can write to ROM. */ + if (is_write && !attrs.debug) { return !mr->readonly && !mr->rom_device; } return true; diff --git a/system/physmem.c b/system/physmem.c index 8745c10c9d..d3efdf13d3 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3680,13 +3680,8 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, if (l > len) l = len; phys_addr += (addr & ~TARGET_PAGE_MASK); - if (is_write) { - res = address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr, - attrs, buf, l); - } else { - res = address_space_read(cpu->cpu_ases[asidx].as, phys_addr, - attrs, buf, l); - } + res = address_space_rw(cpu->cpu_ases[asidx].as, phys_addr, attrs, buf, + l, is_write); if (res != MEMTX_OK) { return -1; } From c1cda1c5f8faf18994dacb8c733ad22e22c2318f Mon Sep 17 00:00:00 2001 From: William Roche Date: Tue, 11 Feb 2025 21:27:05 +0000 Subject: [PATCH 1672/2892] system/physmem: handle hugetlb correctly in qemu_ram_remap() The list of hwpoison pages used to remap the memory on reset is based on the backend real page size. To correctly handle hugetlb, we must mmap(MAP_FIXED) a complete hugetlb page; hugetlb pages cannot be partially mapped. Signed-off-by: William Roche Co-developed-by: David Hildenbrand Acked-by: David Hildenbrand Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250211212707.302391-2-william.roche@oracle.com Signed-off-by: Peter Xu --- accel/kvm/kvm-all.c | 2 +- include/exec/cpu-common.h | 2 +- system/physmem.c | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index c65b790433..f89568bfa3 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1288,7 +1288,7 @@ static void kvm_unpoison_all(void *param) QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) { QLIST_REMOVE(page, list); - qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE); + qemu_ram_remap(page->ram_addr); g_free(page); } } diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index b1d76d6985..3771b2130c 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -67,7 +67,7 @@ typedef uintptr_t ram_addr_t; /* memory API */ -void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); +void qemu_ram_remap(ram_addr_t addr); /* This should not be used by devices. */ ram_addr_t qemu_ram_addr_from_host(void *ptr); ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr); diff --git a/system/physmem.c b/system/physmem.c index d3efdf13d3..af1175a57c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2275,17 +2275,35 @@ void qemu_ram_free(RAMBlock *block) } #ifndef _WIN32 -void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) +/* + * qemu_ram_remap - remap a single RAM page + * + * @addr: address in ram_addr_t address space. + * + * This function will try remapping a single page of guest RAM identified by + * @addr, essentially discarding memory to recover from previously poisoned + * memory (MCE). The page size depends on the RAMBlock (i.e., hugetlb). @addr + * does not have to point at the start of the page. + * + * This function is only to be used during system resets; it will kill the + * VM if remapping failed. + */ +void qemu_ram_remap(ram_addr_t addr) { RAMBlock *block; - ram_addr_t offset; + uint64_t offset; int flags; void *area, *vaddr; int prot; + size_t page_size; RAMBLOCK_FOREACH(block) { offset = addr - block->offset; if (offset < block->max_length) { + /* Respect the pagesize of our RAMBlock */ + page_size = qemu_ram_pagesize(block); + offset = QEMU_ALIGN_DOWN(offset, page_size); + vaddr = ramblock_ptr(block, offset); if (block->flags & RAM_PREALLOC) { ; @@ -2299,21 +2317,23 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) prot = PROT_READ; prot |= block->flags & RAM_READONLY ? 0 : PROT_WRITE; if (block->fd >= 0) { - area = mmap(vaddr, length, prot, flags, block->fd, + area = mmap(vaddr, page_size, prot, flags, block->fd, offset + block->fd_offset); } else { flags |= MAP_ANONYMOUS; - area = mmap(vaddr, length, prot, flags, -1, 0); + area = mmap(vaddr, page_size, prot, flags, -1, 0); } if (area != vaddr) { - error_report("Could not remap addr: " - RAM_ADDR_FMT "@" RAM_ADDR_FMT "", - length, addr); + error_report("Could not remap RAM %s:%" PRIx64 "+%" PRIx64 + " +%zx", block->idstr, offset, + block->fd_offset, page_size); exit(1); } - memory_try_enable_merging(vaddr, length); - qemu_ram_setup_dump(vaddr, length); + memory_try_enable_merging(vaddr, page_size); + qemu_ram_setup_dump(vaddr, page_size); } + + break; } } } From 30943e496f2b0a49357581af480bdcd74fb338f5 Mon Sep 17 00:00:00 2001 From: William Roche Date: Tue, 11 Feb 2025 21:27:06 +0000 Subject: [PATCH 1673/2892] system/physmem: poisoned memory discard on reboot Repair poisoned memory location(s), calling ram_block_discard_range(): punching a hole in the backend file when necessary and regenerating a usable memory. If the kernel doesn't support the madvise calls used by this function and we are dealing with anonymous memory, fall back to remapping the location(s). Signed-off-by: William Roche Acked-by: David Hildenbrand Link: https://lore.kernel.org/r/20250211212707.302391-3-william.roche@oracle.com Signed-off-by: Peter Xu --- system/physmem.c | 57 ++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index af1175a57c..67bdf631e6 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2275,6 +2275,23 @@ void qemu_ram_free(RAMBlock *block) } #ifndef _WIN32 +/* Simply remap the given VM memory location from start to start+length */ +static int qemu_ram_remap_mmap(RAMBlock *block, uint64_t start, size_t length) +{ + int flags, prot; + void *area; + void *host_startaddr = block->host + start; + + assert(block->fd < 0); + flags = MAP_FIXED | MAP_ANONYMOUS; + flags |= block->flags & RAM_SHARED ? MAP_SHARED : MAP_PRIVATE; + flags |= block->flags & RAM_NORESERVE ? MAP_NORESERVE : 0; + prot = PROT_READ; + prot |= block->flags & RAM_READONLY ? 0 : PROT_WRITE; + area = mmap(host_startaddr, length, prot, flags, -1, 0); + return area != host_startaddr ? -errno : 0; +} + /* * qemu_ram_remap - remap a single RAM page * @@ -2292,9 +2309,7 @@ void qemu_ram_remap(ram_addr_t addr) { RAMBlock *block; uint64_t offset; - int flags; - void *area, *vaddr; - int prot; + void *vaddr; size_t page_size; RAMBLOCK_FOREACH(block) { @@ -2310,24 +2325,24 @@ void qemu_ram_remap(ram_addr_t addr) } else if (xen_enabled()) { abort(); } else { - flags = MAP_FIXED; - flags |= block->flags & RAM_SHARED ? - MAP_SHARED : MAP_PRIVATE; - flags |= block->flags & RAM_NORESERVE ? MAP_NORESERVE : 0; - prot = PROT_READ; - prot |= block->flags & RAM_READONLY ? 0 : PROT_WRITE; - if (block->fd >= 0) { - area = mmap(vaddr, page_size, prot, flags, block->fd, - offset + block->fd_offset); - } else { - flags |= MAP_ANONYMOUS; - area = mmap(vaddr, page_size, prot, flags, -1, 0); - } - if (area != vaddr) { - error_report("Could not remap RAM %s:%" PRIx64 "+%" PRIx64 - " +%zx", block->idstr, offset, - block->fd_offset, page_size); - exit(1); + if (ram_block_discard_range(block, offset, page_size) != 0) { + /* + * Fall back to using mmap() only for anonymous mapping, + * as if a backing file is associated we may not be able + * to recover the memory in all cases. + * So don't take the risk of using only mmap and fail now. + */ + if (block->fd >= 0) { + error_report("Could not remap RAM %s:%" PRIx64 "+%" + PRIx64 " +%zx", block->idstr, offset, + block->fd_offset, page_size); + exit(1); + } + if (qemu_ram_remap_mmap(block, offset, page_size) != 0) { + error_report("Could not remap RAM %s:%" PRIx64 " +%zx", + block->idstr, offset, page_size); + exit(1); + } } memory_try_enable_merging(vaddr, page_size); qemu_ram_setup_dump(vaddr, page_size); From e76fadf93e4d64492206b34fd3d434b515450d2c Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Wed, 12 Feb 2025 17:39:17 +0300 Subject: [PATCH 1674/2892] os: add an ability to lock memory on_fault This will be used in the following commits to make it possible to only lock memory on fault instead of right away. Signed-off-by: Daniil Tatianin Reviewed-by: Vladimir Sementsov-Ogievskiy Link: https://lore.kernel.org/r/20250212143920.1269754-2-d-tatianin@yandex-team.ru [peterx: fail os_mlock(on_fault=1) when not supported] [peterx: use G_GNUC_UNUSED instead of "(void)on_fault", per Dan] Signed-off-by: Peter Xu --- include/system/os-posix.h | 2 +- include/system/os-win32.h | 2 +- meson.build | 6 ++++++ migration/postcopy-ram.c | 2 +- os-posix.c | 15 +++++++++++++-- system/vl.c | 2 +- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/system/os-posix.h b/include/system/os-posix.h index b881ac6c6f..ce5b3bccf8 100644 --- a/include/system/os-posix.h +++ b/include/system/os-posix.h @@ -53,7 +53,7 @@ bool os_set_runas(const char *user_id); void os_set_chroot(const char *path); void os_setup_limits(void); void os_setup_post(void); -int os_mlock(void); +int os_mlock(bool on_fault); /** * qemu_alloc_stack: diff --git a/include/system/os-win32.h b/include/system/os-win32.h index b82a5d3ad9..bc623061d8 100644 --- a/include/system/os-win32.h +++ b/include/system/os-win32.h @@ -123,7 +123,7 @@ static inline bool is_daemonized(void) return false; } -static inline int os_mlock(void) +static inline int os_mlock(bool on_fault G_GNUC_UNUSED) { return -ENOSYS; } diff --git a/meson.build b/meson.build index 18cf9e2913..59953cbe6b 100644 --- a/meson.build +++ b/meson.build @@ -2885,6 +2885,12 @@ config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' return mlockall(MCL_FUTURE); }''')) +config_host_data.set('HAVE_MLOCK_ONFAULT', cc.links(gnu_source_prefix + ''' + #include + int main(void) { + return mlockall(MCL_FUTURE | MCL_ONFAULT); + }''')) + have_l2tpv3 = false if get_option('l2tpv3').allowed() and have_system have_l2tpv3 = cc.has_type('struct mmsghdr', diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 6a6da6ba7f..fc4d8a10df 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -652,7 +652,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) } if (enable_mlock) { - if (os_mlock() < 0) { + if (os_mlock(false) < 0) { error_report("mlock: %s", strerror(errno)); /* * It doesn't feel right to fail at this point, we have a valid diff --git a/os-posix.c b/os-posix.c index 9cce55ff2f..52925c23d3 100644 --- a/os-posix.c +++ b/os-posix.c @@ -327,18 +327,29 @@ void os_set_line_buffering(void) setvbuf(stdout, NULL, _IOLBF, 0); } -int os_mlock(void) +int os_mlock(bool on_fault) { #ifdef HAVE_MLOCKALL int ret = 0; + int flags = MCL_CURRENT | MCL_FUTURE; - ret = mlockall(MCL_CURRENT | MCL_FUTURE); + if (on_fault) { +#ifdef HAVE_MLOCK_ONFAULT + flags |= MCL_ONFAULT; +#else + error_report("mlockall: on_fault not supported"); + return -EINVAL; +#endif + } + + ret = mlockall(flags); if (ret < 0) { error_report("mlockall: %s", strerror(errno)); } return ret; #else + (void)on_fault; return -ENOSYS; #endif } diff --git a/system/vl.c b/system/vl.c index 9c6942c6cf..e94fc7ea35 100644 --- a/system/vl.c +++ b/system/vl.c @@ -797,7 +797,7 @@ static QemuOptsList qemu_run_with_opts = { static void realtime_init(void) { if (enable_mlock) { - if (os_mlock() < 0) { + if (os_mlock(false) < 0) { error_report("locking memory failed"); exit(1); } From cb74f2b8a65cde2eadbcb5574327ac3f49983d8a Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Wed, 12 Feb 2025 17:39:18 +0300 Subject: [PATCH 1675/2892] system/vl: extract overcommit option parsing into a helper This will be extended in the future commits, let's move it out of line right away so that it's easier to read. Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Xu Signed-off-by: Daniil Tatianin Link: https://lore.kernel.org/r/20250212143920.1269754-3-d-tatianin@yandex-team.ru Signed-off-by: Peter Xu --- system/vl.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/system/vl.c b/system/vl.c index e94fc7ea35..72a40985f5 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1875,6 +1875,19 @@ static void object_option_parse(const char *str) visit_free(v); } +static void overcommit_parse(const char *str) +{ + QemuOpts *opts; + + opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), + str, false); + if (!opts) { + exit(1); + } + enable_mlock = qemu_opt_get_bool(opts, "mem-lock", enable_mlock); + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); +} + /* * Very early object creation, before the sandbox options have been activated. */ @@ -3575,13 +3588,7 @@ void qemu_init(int argc, char **argv) object_option_parse(optarg); break; case QEMU_OPTION_overcommit: - opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), - optarg, false); - if (!opts) { - exit(1); - } - enable_mlock = qemu_opt_get_bool(opts, "mem-lock", enable_mlock); - enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); + overcommit_parse(optarg); break; case QEMU_OPTION_compat: { From cd2e472e54a49c13b0a728cdda7c10c50421e23d Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Wed, 12 Feb 2025 17:39:19 +0300 Subject: [PATCH 1676/2892] system: introduce a new MlockState enum Replace the boolean value enable_mlock with an enum and add a helper to decide whether we should be calling os_mlock. This is a stepping stone towards introducing a new mlock mode, which will be the third possible state of this enum. Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Xu Signed-off-by: Daniil Tatianin Link: https://lore.kernel.org/r/20250212143920.1269754-4-d-tatianin@yandex-team.ru Signed-off-by: Peter Xu --- hw/virtio/virtio-mem.c | 2 +- include/system/system.h | 10 +++++++++- migration/postcopy-ram.c | 2 +- system/globals.c | 7 ++++++- system/vl.c | 9 +++++++-- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index b1a003736b..7b140add76 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -991,7 +991,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) return; } - if (enable_mlock) { + if (should_mlock(mlock_state)) { error_setg(errp, "Incompatible with mlock"); return; } diff --git a/include/system/system.h b/include/system/system.h index 0cbb43ec30..dc7628357a 100644 --- a/include/system/system.h +++ b/include/system/system.h @@ -44,10 +44,18 @@ extern int display_opengl; extern const char *keyboard_layout; extern int old_param; extern uint8_t *boot_splash_filedata; -extern bool enable_mlock; extern bool enable_cpu_pm; extern QEMUClockType rtc_clock; +typedef enum { + MLOCK_OFF = 0, + MLOCK_ON, +} MlockState; + +bool should_mlock(MlockState); + +extern MlockState mlock_state; + #define MAX_OPTION_ROMS 16 typedef struct QEMUOptionRom { const char *name; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index fc4d8a10df..04068ee039 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -651,7 +651,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) mis->have_fault_thread = false; } - if (enable_mlock) { + if (should_mlock(mlock_state)) { if (os_mlock(false) < 0) { error_report("mlock: %s", strerror(errno)); /* diff --git a/system/globals.c b/system/globals.c index 4867c93ca6..adeff38348 100644 --- a/system/globals.c +++ b/system/globals.c @@ -31,10 +31,15 @@ #include "system/cpus.h" #include "system/system.h" +bool should_mlock(MlockState state) +{ + return state == MLOCK_ON; +} + enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; int display_opengl; const char* keyboard_layout; -bool enable_mlock; +MlockState mlock_state; bool enable_cpu_pm; int autostart = 1; int vga_interface_type = VGA_NONE; diff --git a/system/vl.c b/system/vl.c index 72a40985f5..2895824c1a 100644 --- a/system/vl.c +++ b/system/vl.c @@ -796,7 +796,7 @@ static QemuOptsList qemu_run_with_opts = { static void realtime_init(void) { - if (enable_mlock) { + if (should_mlock(mlock_state)) { if (os_mlock(false) < 0) { error_report("locking memory failed"); exit(1); @@ -1878,13 +1878,18 @@ static void object_option_parse(const char *str) static void overcommit_parse(const char *str) { QemuOpts *opts; + bool enable_mlock; opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), str, false); if (!opts) { exit(1); } - enable_mlock = qemu_opt_get_bool(opts, "mem-lock", enable_mlock); + + enable_mlock = qemu_opt_get_bool(opts, "mem-lock", + should_mlock(mlock_state)); + mlock_state = enable_mlock ? MLOCK_ON : MLOCK_OFF; + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); } From 13057e064a3edae7abf9ca2c207cdf48b82c5aad Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Wed, 12 Feb 2025 17:39:20 +0300 Subject: [PATCH 1677/2892] overcommit: introduce mem-lock=on-fault Locking the memory without MCL_ONFAULT instantly prefaults any mmaped anonymous memory with a write-fault, which introduces a lot of extra overhead in terms of memory usage when all you want to do is to prevent kcompactd from migrating and compacting QEMU pages. Add an option to only lock pages lazily as they're faulted by the process by using MCL_ONFAULT if asked. Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Xu Signed-off-by: Daniil Tatianin Link: https://lore.kernel.org/r/20250212143920.1269754-5-d-tatianin@yandex-team.ru Signed-off-by: Peter Xu --- include/system/system.h | 2 ++ migration/postcopy-ram.c | 2 +- qemu-options.hx | 14 +++++++++----- system/globals.c | 7 ++++++- system/vl.c | 34 +++++++++++++++++++++++++++------- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/include/system/system.h b/include/system/system.h index dc7628357a..a7effe7dfd 100644 --- a/include/system/system.h +++ b/include/system/system.h @@ -50,9 +50,11 @@ extern QEMUClockType rtc_clock; typedef enum { MLOCK_OFF = 0, MLOCK_ON, + MLOCK_ON_FAULT, } MlockState; bool should_mlock(MlockState); +bool is_mlock_on_fault(MlockState); extern MlockState mlock_state; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 04068ee039..5d3edfcfec 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -652,7 +652,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) } if (should_mlock(mlock_state)) { - if (os_mlock(false) < 0) { + if (os_mlock(is_mlock_on_fault(mlock_state)) < 0) { error_report("mlock: %s", strerror(errno)); /* * It doesn't feel right to fail at this point, we have a valid diff --git a/qemu-options.hx b/qemu-options.hx index 1b26ad53bd..61270e3206 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4632,21 +4632,25 @@ SRST ERST DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, - "-overcommit [mem-lock=on|off][cpu-pm=on|off]\n" + "-overcommit [mem-lock=on|off|on-fault][cpu-pm=on|off]\n" " run qemu with overcommit hints\n" - " mem-lock=on|off controls memory lock support (default: off)\n" + " mem-lock=on|off|on-fault controls memory lock support (default: off)\n" " cpu-pm=on|off controls cpu power management (default: off)\n", QEMU_ARCH_ALL) SRST -``-overcommit mem-lock=on|off`` +``-overcommit mem-lock=on|off|on-fault`` \ ``-overcommit cpu-pm=on|off`` Run qemu with hints about host resource overcommit. The default is to assume that host overcommits all resources. Locking qemu and guest memory can be enabled via ``mem-lock=on`` - (disabled by default). This works when host memory is not - overcommitted and reduces the worst-case latency for guest. + or ``mem-lock=on-fault`` (disabled by default). This works when + host memory is not overcommitted and reduces the worst-case latency for + guest. The on-fault option is better for reducing the memory footprint + since it makes allocations lazy, but the pages still get locked in place + once faulted by the guest or QEMU. Note that the two options are mutually + exclusive. Guest ability to manage power state of host cpus (increasing latency for other processes on the same host cpu, but decreasing latency for diff --git a/system/globals.c b/system/globals.c index adeff38348..316623bd20 100644 --- a/system/globals.c +++ b/system/globals.c @@ -33,7 +33,12 @@ bool should_mlock(MlockState state) { - return state == MLOCK_ON; + return state == MLOCK_ON || state == MLOCK_ON_FAULT; +} + +bool is_mlock_on_fault(MlockState state) +{ + return state == MLOCK_ON_FAULT; } enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; diff --git a/system/vl.c b/system/vl.c index 2895824c1a..3c0fa2ff64 100644 --- a/system/vl.c +++ b/system/vl.c @@ -351,7 +351,7 @@ static QemuOptsList qemu_overcommit_opts = { .desc = { { .name = "mem-lock", - .type = QEMU_OPT_BOOL, + .type = QEMU_OPT_STRING, }, { .name = "cpu-pm", @@ -797,7 +797,7 @@ static QemuOptsList qemu_run_with_opts = { static void realtime_init(void) { if (should_mlock(mlock_state)) { - if (os_mlock(false) < 0) { + if (os_mlock(is_mlock_on_fault(mlock_state)) < 0) { error_report("locking memory failed"); exit(1); } @@ -1878,7 +1878,7 @@ static void object_option_parse(const char *str) static void overcommit_parse(const char *str) { QemuOpts *opts; - bool enable_mlock; + const char *mem_lock_opt; opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), str, false); @@ -1886,11 +1886,31 @@ static void overcommit_parse(const char *str) exit(1); } - enable_mlock = qemu_opt_get_bool(opts, "mem-lock", - should_mlock(mlock_state)); - mlock_state = enable_mlock ? MLOCK_ON : MLOCK_OFF; - enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); + + mem_lock_opt = qemu_opt_get(opts, "mem-lock"); + if (!mem_lock_opt) { + return; + } + + if (strcmp(mem_lock_opt, "on") == 0) { + mlock_state = MLOCK_ON; + return; + } + + if (strcmp(mem_lock_opt, "off") == 0) { + mlock_state = MLOCK_OFF; + return; + } + + if (strcmp(mem_lock_opt, "on-fault") == 0) { + mlock_state = MLOCK_ON_FAULT; + return; + } + + error_report("parameter 'mem-lock' expects one of " + "'on', 'off', 'on-fault'"); + exit(1); } /* From df45e26a81022f4f8f976b603cb0466b1cd64baf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 12:12:43 +0100 Subject: [PATCH 1678/2892] rust: docs: document naming convention As agreed in the "vtables and procedural macros" thread on the mailing list. Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 390aae4386..8cccca7a73 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -194,6 +194,50 @@ module status interface either. Also, ``unsafe`` interfaces may be replaced by safe interfaces later. +Naming convention +''''''''''''''''' + +C function names usually are prefixed according to the data type that they +apply to, for example ``timer_mod`` or ``sysbus_connect_irq``. Furthermore, +both function and structs sometimes have a ``qemu_`` or ``QEMU`` prefix. +Generally speaking, these are all removed in the corresponding Rust functions: +``QEMUTimer`` becomes ``timer::Timer``, ``timer_mod`` becomes ``Timer::modify``, +``sysbus_connect_irq`` becomes ``SysBusDeviceMethods::connect_irq``. + +Sometimes however a name appears multiple times in the QOM class hierarchy, +and the only difference is in the prefix. An example is ``qdev_realize`` and +``sysbus_realize``. In such cases, whenever a name is not unique in +the hierarchy, always add the prefix to the classes that are lower in +the hierarchy; for the top class, decide on a case by case basis. + +For example: + +========================== ========================================= +``device_cold_reset()`` ``DeviceMethods::cold_reset()`` +``pci_device_reset()`` ``PciDeviceMethods::pci_device_reset()`` +``pci_bridge_reset()`` ``PciBridgeMethods::pci_bridge_reset()`` +========================== ========================================= + +Here, the name is not exactly the same, but nevertheless ``PciDeviceMethods`` +adds the prefix to avoid confusion, because the functionality of +``device_cold_reset()`` and ``pci_device_reset()`` is subtly different. + +In this case, however, no prefix is needed: + +========================== ========================================= +``device_realize()`` ``DeviceMethods::realize()`` +``sysbus_realize()`` ``SysbusDeviceMethods::sysbus_realize()`` +``pci_realize()`` ``PciDeviceMethods::pci_realize()`` +========================== ========================================= + +Here, the lower classes do not add any functionality, and mostly +provide extra compile-time checking; the basic *realize* functionality +is the same for all devices. Therefore, ``DeviceMethods`` does not +add the prefix. + +Whenever a name is unique in the hierarchy, instead, you should +always remove the class name prefix. + Common pitfalls ''''''''''''''' From 0fcccf3ff04a54d597bffcb7a42668c52a7dcec0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 12:00:01 +0100 Subject: [PATCH 1679/2892] rust: qom: add reference counting functionality Add a smart pointer that allows to add and remove references from QOM objects. It's important to note that while all QOM objects have a reference count, in practice not all of them have their lifetime guarded by it. Embedded objects, specifically, are confined to the lifetime of the owner. When writing Rust bindings this is important, because embedded objects are *never* used through the "Owned<>" smart pointer that is introduced here. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 166 +++++++++++++++++++++++++++++++++-- rust/qemu-api/src/vmstate.rs | 6 +- rust/qemu-api/tests/tests.rs | 13 ++- 3 files changed, 178 insertions(+), 7 deletions(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index f50ee371aa..404446d57f 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -56,6 +56,7 @@ use std::{ ffi::CStr, fmt, + mem::ManuallyDrop, ops::{Deref, DerefMut}, os::raw::c_void, ptr::NonNull, @@ -63,7 +64,13 @@ use std::{ pub use bindings::{Object, ObjectClass}; -use crate::bindings::{self, object_dynamic_cast, object_get_class, object_get_typename, TypeInfo}; +use crate::{ + bindings::{ + self, object_dynamic_cast, object_get_class, object_get_typename, object_ref, object_unref, + TypeInfo, + }, + cell::bql_locked, +}; /// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct /// or indirect parent of `Self`). @@ -280,10 +287,10 @@ where /// /// # Safety /// - /// This method is unsafe because it overrides const-ness of `&self`. - /// Bindings to C APIs will use it a lot, but otherwise it should not - /// be necessary. - unsafe fn as_mut_ptr(&self) -> *mut U + /// This method is safe because only the actual dereference of the pointer + /// has to be unsafe. Bindings to C APIs will use it a lot, but care has + /// to be taken because it overrides the const-ness of `&self`. + fn as_mut_ptr(&self) -> *mut U where Self::Target: IsA, { @@ -610,6 +617,148 @@ unsafe impl ObjectType for Object { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; } +/// A reference-counted pointer to a QOM object. +/// +/// `Owned` wraps `T` with automatic reference counting. It increases the +/// reference count when created via [`Owned::from`] or cloned, and decreases +/// it when dropped. This ensures that the reference count remains elevated +/// as long as any `Owned` references to it exist. +/// +/// `Owned` can be used for two reasons: +/// * because the lifetime of the QOM object is unknown and someone else could +/// take a reference (similar to `Arc`, for example): in this case, the +/// object can escape and outlive the Rust struct that contains the `Owned` +/// field; +/// +/// * to ensure that the object stays alive until after `Drop::drop` is called +/// on the Rust struct: in this case, the object will always die together with +/// the Rust struct that contains the `Owned` field. +/// +/// Child properties are an example of the second case: in C, an object that +/// is created with `object_initialize_child` will die *before* +/// `instance_finalize` is called, whereas Rust expects the struct to have valid +/// contents when `Drop::drop` is called. Therefore Rust structs that have +/// child properties need to keep a reference to the child object. Right now +/// this can be done with `Owned`; in the future one might have a separate +/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like +/// `Owned`, but does not allow cloning. +/// +/// Note that dropping an `Owned` requires the big QEMU lock to be taken. +#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Owned(NonNull); + +// The following rationale for safety is taken from Linux's kernel::sync::Arc. + +// SAFETY: It is safe to send `Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has an `Owned` may ultimately access `T` using a +// mutable reference when the reference count reaches zero and `T` is dropped. +unsafe impl Send for Owned {} + +// SAFETY: It is safe to send `&Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has a `&Owned` may clone it and get an `Owned` on that +// thread, so the thread may ultimately access `T` using a mutable reference +// when the reference count reaches zero and `T` is dropped. +unsafe impl Sync for Owned {} + +impl Owned { + /// Convert a raw C pointer into an owned reference to the QOM + /// object it points to. The object's reference count will be + /// decreased when the `Owned` is dropped. + /// + /// # Panics + /// + /// Panics if `ptr` is NULL. + /// + /// # Safety + /// + /// The caller must indeed own a reference to the QOM object. + /// The object must not be embedded in another unless the outer + /// object is guaranteed to have a longer lifetime. + /// + /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed + /// back to `from_raw()` (assuming the original `Owned` was valid!), + /// since the owned reference remains there between the calls to + /// `into_raw()` and `from_raw()`. + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the pointer passed to from_raw + // remains const + Owned(NonNull::new(ptr as *mut T).unwrap()) + } + + /// Obtain a raw C pointer from a reference. `src` is consumed + /// and the reference is leaked. + #[allow(clippy::missing_const_for_fn)] + pub fn into_raw(src: Owned) -> *mut T { + let src = ManuallyDrop::new(src); + src.0.as_ptr() + } + + /// Increase the reference count of a QOM object and return + /// a new owned reference to it. + /// + /// # Safety + /// + /// The object must not be embedded in another, unless the outer + /// object is guaranteed to have a longer lifetime. + pub unsafe fn from(obj: &T) -> Self { + unsafe { + object_ref(obj.as_object_mut_ptr().cast::()); + + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the reference passed to from_raw + // remains shared + Owned(NonNull::new_unchecked(obj.as_mut_ptr())) + } + } +} + +impl Clone for Owned { + fn clone(&self) -> Self { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { Owned::from(self.deref()) } + } +} + +impl Deref for Owned { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + // With that guarantee, reference counting ensures that + // the object remains alive. + unsafe { &*self.0.as_ptr() } + } +} +impl ObjectDeref for Owned {} + +impl Drop for Owned { + fn drop(&mut self) { + assert!(bql_locked()); + // SAFETY: creation method is unsafe, and whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { + object_unref(self.as_object_mut_ptr().cast::()); + } + } +} + +impl> fmt::Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().debug_fmt(f) + } +} + /// Trait for methods exposed by the Object class. The methods can be /// called on all objects that have the trait `IsA`. /// @@ -641,6 +790,13 @@ where klass } + + /// Convenience function for implementing the Debug trait + fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple(&self.typename()) + .field(&(self as *const Self)) + .finish() + } } impl ObjectMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 6ac432cf52..11d21b8791 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -29,6 +29,8 @@ use core::{marker::PhantomData, mem, ptr::NonNull}; pub use crate::bindings::{VMStateDescription, VMStateField}; use crate::{ bindings::{self, VMStateFlags}, + prelude::*, + qom::Owned, zeroable::Zeroable, }; @@ -191,7 +193,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, /// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) /// * a raw pointer to any of the above -/// * a `NonNull` pointer or a `Box` for any of the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of +/// the above /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented @@ -398,6 +401,7 @@ impl_vmstate_pointer!(NonNull where T: VMState); // Unlike C pointers, Box is always non-null therefore there is no need // to specify VMS_ALLOC. impl_vmstate_pointer!(Box where T: VMState); +impl_vmstate_pointer!(Owned where T: VMState + ObjectType); // Arrays using the underlying type's VMState plus // VMS_ARRAY/VMS_ARRAY_OF_POINTER diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 5c3e75ed3d..5f6096a572 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -15,7 +15,7 @@ use qemu_api::{ declare_properties, define_property, prelude::*, qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, - qom::{ClassInitImpl, ObjectImpl, ParentField}, + qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -138,6 +138,17 @@ fn test_object_new() { } } +#[test] +#[allow(clippy::redundant_clone)] +/// Create, clone and then drop an instance. +fn test_clone() { + init_qom(); + let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; + let p = unsafe { Owned::from_raw(p) }; + assert_eq!(p.clone().typename(), "dummy"); + drop(p); +} + #[test] /// Try invoking a method on an object. fn test_typename() { From ec3eba98967014f942bafb4307303d853d96e7e7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Oct 2024 14:27:36 +0100 Subject: [PATCH 1680/2892] rust: qom: add object creation functionality The basic object lifecycle test can now be implemented using safe code! Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 23 ++++++++++++--------- rust/qemu-api/src/prelude.rs | 1 + rust/qemu-api/src/qom.rs | 23 +++++++++++++++++++-- rust/qemu-api/tests/tests.rs | 35 ++++++++++++-------------------- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 8050ede9c8..f5db114b0c 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -10,11 +10,11 @@ use std::{ use qemu_api::{ bindings::{ - error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_new, - qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, - qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, - sysbus_realize_and_unref, CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, - QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, + error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_prop_set_chr, + qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, + qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, sysbus_realize, + CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, QEMUChrEvent, + CHR_IOCTL_SERIAL_SET_BREAK, }, c_str, impl_vmstate_forward, irq::InterruptSource, @@ -705,15 +705,18 @@ pub unsafe extern "C" fn pl011_create( irq: qemu_irq, chr: *mut Chardev, ) -> *mut DeviceState { + let pl011 = PL011State::new(); unsafe { - let dev: *mut DeviceState = qdev_new(PL011State::TYPE_NAME.as_ptr()); - let sysbus: *mut SysBusDevice = dev.cast::(); - + let dev = pl011.as_mut_ptr::(); qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); - sysbus_realize_and_unref(sysbus, addr_of_mut!(error_fatal)); + + let sysbus = pl011.as_mut_ptr::(); + sysbus_realize(sysbus, addr_of_mut!(error_fatal)); sysbus_mmio_map(sysbus, 0, addr); sysbus_connect_irq(sysbus, 0, irq); - dev + + // return the pointer, which is kept alive by the QOM tree; drop owned ref + pl011.as_mut_ptr() } } diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 2dc86e19b2..3df6a5c21e 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -12,6 +12,7 @@ pub use crate::qom::Object; pub use crate::qom::ObjectCast; pub use crate::qom::ObjectCastMut; pub use crate::qom::ObjectDeref; +pub use crate::qom::ObjectClassMethods; pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 404446d57f..3e63cb30ca 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -66,8 +66,8 @@ pub use bindings::{Object, ObjectClass}; use crate::{ bindings::{ - self, object_dynamic_cast, object_get_class, object_get_typename, object_ref, object_unref, - TypeInfo, + self, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, + object_unref, TypeInfo, }, cell::bql_locked, }; @@ -759,6 +759,24 @@ impl> fmt::Debug for Owned { } } +/// Trait for class methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectClassMethods: IsA { + /// Return a new reference counted instance of this class + fn new() -> Owned { + assert!(bql_locked()); + // SAFETY: the object created by object_new is allocated on + // the heap and has a reference count of 1 + unsafe { + let obj = &*object_new(Self::TYPE_NAME.as_ptr()); + Owned::from_raw(obj.unsafe_cast::()) + } + } +} + /// Trait for methods exposed by the Object class. The methods can be /// called on all objects that have the trait `IsA`. /// @@ -799,4 +817,5 @@ where } } +impl ObjectClassMethods for T where T: IsA {} impl ObjectMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 5f6096a572..10748fba19 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -3,8 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - ffi::CStr, - os::raw::c_void, + ffi::{c_void, CStr}, ptr::{addr_of, addr_of_mut}, }; @@ -15,7 +14,7 @@ use qemu_api::{ declare_properties, define_property, prelude::*, qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, - qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, + qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -132,10 +131,8 @@ fn init_qom() { /// Create and immediately drop an instance. fn test_object_new() { init_qom(); - unsafe { - object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); - object_unref(object_new(DummyChildState::TYPE_NAME.as_ptr()).cast()); - } + drop(DummyState::new()); + drop(DummyChildState::new()); } #[test] @@ -143,8 +140,7 @@ fn test_object_new() { /// Create, clone and then drop an instance. fn test_clone() { init_qom(); - let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; - let p = unsafe { Owned::from_raw(p) }; + let p = DummyState::new(); assert_eq!(p.clone().typename(), "dummy"); drop(p); } @@ -153,12 +149,8 @@ fn test_clone() { /// Try invoking a method on an object. fn test_typename() { init_qom(); - let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; - let p_ref: &DummyState = unsafe { &*p }; - assert_eq!(p_ref.typename(), "dummy"); - unsafe { - object_unref(p_ref.as_object_mut_ptr().cast::()); - } + let p = DummyState::new(); + assert_eq!(p.typename(), "dummy"); } // a note on all "cast" tests: usually, especially for downcasts the desired @@ -173,24 +165,23 @@ fn test_typename() { /// Test casts on shared references. fn test_cast() { init_qom(); - let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; + let p = DummyState::new(); + let p_ptr: *mut DummyState = p.as_mut_ptr(); + let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; - let p_ref: &DummyState = unsafe { &*p }; let obj_ref: &Object = p_ref.upcast(); - assert_eq!(addr_of!(*obj_ref), p.cast()); + assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); assert!(sbd_ref.is_none()); let dev_ref: Option<&DeviceState> = obj_ref.downcast(); - assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast()); + assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); // SAFETY: the cast is wrong, but the value is only used for comparison unsafe { let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); - assert_eq!(addr_of!(*sbd_ref), p.cast()); - - object_unref(p_ref.as_object_mut_ptr().cast::()); + assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); } } From 66bcc554d27f693f89bf04df24d474463a90a894 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 6 Dec 2024 17:08:49 +0100 Subject: [PATCH 1681/2892] rust: callbacks: allow passing optional callbacks as () In some cases, callbacks are optional. Using "Some(function)" and "None" does not work well, because when someone writes "None" the compiler does not know what to use for "F" in "Option". Therefore, adopt () to mean a "null" callback. It is possible to enforce that a callback is valid by adding a "let _: () = F::ASSERT_IS_SOME" before the invocation of F::call. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/callbacks.rs | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs index 314f9dce96..9642a16eb8 100644 --- a/rust/qemu-api/src/callbacks.rs +++ b/rust/qemu-api/src/callbacks.rs @@ -79,6 +79,31 @@ use std::{mem, ptr::NonNull}; /// call_it(&move |_| String::from(x), "hello workd"); /// ``` /// +/// `()` can be used to indicate "no function": +/// +/// ``` +/// # use qemu_api::callbacks::FnCall; +/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { +/// if F::IS_SOME { +/// Some(F::call((s,))) +/// } else { +/// None +/// } +/// } +/// +/// assert!(optional(&(), "hello world").is_none()); +/// ``` +/// +/// Invoking `F::call` will then be a run-time error. +/// +/// ```should_panic +/// # use qemu_api::callbacks::FnCall; +/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// # F::call((s,)) +/// # } +/// let s: String = call_it(&(), "hello world"); // panics +/// ``` +/// /// # Safety /// /// Because `Self` is a zero-sized type, all instances of the type are @@ -93,10 +118,70 @@ pub unsafe trait FnCall: 'static + Sync + Sized { /// Rust 1.79.0+. const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::() == 0) }; + /// Referring to this constant asserts that the `Self` type is an actual + /// function type, which can be used to catch incorrect use of `()` + /// at compile time. + /// + /// # Examples + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// let _: () = F::ASSERT_IS_SOME; + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + /// + /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in + /// Rust 1.79.0 or newer. + const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) }; + + /// `true` if `Self` is an actual function type and not `()`. + /// + /// # Examples + /// + /// You can use `IS_SOME` to catch this at compile time: + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// const { assert!(F::IS_SOME) } + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + const IS_SOME: bool; + + /// `false` if `Self` is an actual function type, `true` if it is `()`. + fn is_none() -> bool { + !Self::IS_SOME + } + + /// `true` if `Self` is an actual function type, `false` if it is `()`. + fn is_some() -> bool { + Self::IS_SOME + } + /// Call the function with the arguments in args. fn call(a: Args) -> R; } +/// `()` acts as a "null" callback. Using `()` and `function` is nicer +/// than `None` and `Some(function)`, because the compiler is unable to +/// infer the type of just `None`. Therefore, the trait itself acts as the +/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. +unsafe impl FnCall for () { + const IS_SOME: bool = false; + + /// Call the function with the arguments in args. + fn call(_a: Args) -> R { + panic!("callback not specified") + } +} + macro_rules! impl_call { ($($args:ident,)* ) => ( // SAFETY: because each function is treated as a separate type, @@ -106,6 +191,8 @@ macro_rules! impl_call { where F: 'static + Sync + Sized + Fn($($args, )*) -> R, { + const IS_SOME: bool = true; + #[inline(always)] fn call(a: ($($args,)*)) -> R { let _: () = Self::ASSERT_ZERO_SIZED; @@ -141,4 +228,14 @@ mod tests { fn test_call() { assert_eq!(do_test_call(&str::to_owned), "hello world") } + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { + assert!(F::is_some()); + } + + #[test] + fn test_is_some() { + do_test_is_some(&str::to_owned); + } } From 201ef001dd40fdb11c83f3e47604219c374590ec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 11:21:26 +0100 Subject: [PATCH 1682/2892] rust: qdev: add clock creation Add a Rust version of qdev_init_clock_in, which can be used in instance_init. There are a couple differences with the C version: - in Rust the object keeps its own reference to the clock (in addition to the one embedded in the NamedClockList), and the reference is dropped automatically by instance_finalize(); this is encoded in the signature of DeviceClassMethods::init_clock_in, which makes the lifetime of the clock independent of that of the object it holds. This goes unnoticed in the C version and is due to the existence of aliases. - also, anything that happens during instance_init uses the pinned_init framework to operate on a partially initialized object, and is done through class methods (i.e. through DeviceClassMethods rather than DeviceMethods) because the device does not exist yet. Therefore, Rust code *must* create clocks from instance_init, which is stricter than C. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 43 +++++-------- rust/qemu-api/src/prelude.rs | 2 + rust/qemu-api/src/qdev.rs | 107 ++++++++++++++++++++++++++++++- rust/qemu-api/src/vmstate.rs | 4 +- 4 files changed, 125 insertions(+), 31 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index f5db114b0c..37936a328b 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -10,17 +10,16 @@ use std::{ use qemu_api::{ bindings::{ - error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_prop_set_chr, - qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, - qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, sysbus_realize, - CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, QEMUChrEvent, - CHR_IOCTL_SERIAL_SET_BREAK, + error_fatal, hwaddr, memory_region_init_io, qdev_prop_set_chr, qemu_chr_fe_accept_input, + qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq, + sysbus_connect_irq, sysbus_mmio_map, sysbus_realize, CharBackend, Chardev, MemoryRegion, + QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, }, c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, - qdev::{DeviceImpl, DeviceState, Property}, - qom::{ClassInitImpl, ObjectImpl, ParentField}, + qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property}, + qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, sysbus::{SysBusDevice, SysBusDeviceClass}, vmstate::VMStateDescription, }; @@ -131,7 +130,7 @@ pub struct PL011State { #[doc(alias = "irq")] pub interrupts: [InterruptSource; IRQMASK.len()], #[doc(alias = "clk")] - pub clock: NonNull, + pub clock: Owned, #[doc(alias = "migrate_clk")] pub migrate_clock: bool, } @@ -485,8 +484,6 @@ impl PL011State { /// location/instance. All its fields are expected to hold unitialized /// values with the sole exception of `parent_obj`. unsafe fn init(&mut self) { - const CLK_NAME: &CStr = c_str!("clk"); - // SAFETY: // // self and self.iomem are guaranteed to be valid at this point since callers @@ -506,22 +503,16 @@ impl PL011State { // SAFETY: // - // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, - // we can overwrite the undefined value without side effects. This is - // safe since all PL011State instances are created by QOM code which - // calls this function to initialize the fields; therefore no code is - // able to access an invalid self.clock value. - unsafe { - let dev: &mut DeviceState = self.upcast_mut(); - self.clock = NonNull::new(qdev_init_clock_in( - dev, - CLK_NAME.as_ptr(), - None, /* pl011_clock_update */ - addr_of_mut!(*self).cast::(), - ClockEvent::ClockUpdate.0, - )) - .unwrap(); - } + // self.clock is not initialized at this point; but since `Owned<_>` is + // not Drop, we can overwrite the undefined value without side effects; + // it's not sound but, because for all PL011State instances are created + // by QOM code which calls this function to initialize the fields, at + // leastno code is able to access an invalid self.clock value. + self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate); + } + + const fn clock_update(&self, _event: ClockEvent) { + /* pl011_trace_baudrate_change(s); */ } fn post_init(&self) { diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 3df6a5c21e..87e3ce90f2 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,6 +7,8 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; +pub use crate::qdev::DeviceMethods; + pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index f4c75c752f..176c69a560 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -4,14 +4,20 @@ //! Bindings to create devices and access device functionality from Rust. -use std::{ffi::CStr, ptr::NonNull}; +use std::{ + ffi::{CStr, CString}, + os::raw::c_void, + ptr::NonNull, +}; -pub use bindings::{DeviceClass, DeviceState, Property}; +pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property}; use crate::{ bindings::{self, Error}, + callbacks::FnCall, + cell::bql_locked, prelude::*, - qom::{ClassInitImpl, ObjectClass}, + qom::{ClassInitImpl, ObjectClass, Owned}, vmstate::VMStateDescription, }; @@ -143,3 +149,98 @@ unsafe impl ObjectType for DeviceState { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } qom_isa!(DeviceState: Object); + +/// Trait for methods exposed by the [`DeviceState`] class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA`. +pub trait DeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Add an input clock named `name`. Invoke the callback with + /// `self` as the first parameter for the events that are requested. + /// + /// The resulting clock is added as a child of `self`, but it also + /// stays alive until after `Drop::drop` is called because C code + /// keeps an extra reference to it until `device_finalize()` calls + /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in + /// which Rust code has a reference to a child object) it would be + /// possible for this function to return a `&Clock` too. + #[inline] + fn init_clock_in FnCall<(&'a Self::Target, ClockEvent)>>( + &self, + name: &str, + _cb: &F, + events: ClockEvent, + ) -> Owned { + fn do_init_clock_in( + dev: *mut DeviceState, + name: &str, + cb: Option, + events: ClockEvent, + ) -> Owned { + assert!(bql_locked()); + + // SAFETY: the clock is heap allocated, but qdev_init_clock_in() + // does not gift the reference to its caller; so use Owned::from to + // add one. The callback is disabled automatically when the clock + // is unparented, which happens before the device is finalized. + unsafe { + let cstr = CString::new(name).unwrap(); + let clk = bindings::qdev_init_clock_in( + dev, + cstr.as_ptr(), + cb, + dev.cast::(), + events.0, + ); + + Owned::from(&*clk) + } + } + + let cb: Option = if F::is_some() { + unsafe extern "C" fn rust_clock_cb FnCall<(&'a T, ClockEvent)>>( + opaque: *mut c_void, + event: ClockEvent, + ) { + // SAFETY: the opaque is "this", which is indeed a pointer to T + F::call((unsafe { &*(opaque.cast::()) }, event)) + } + Some(rust_clock_cb::) + } else { + None + }; + + do_init_clock_in(self.as_mut_ptr(), name, cb, events) + } + + /// Add an output clock named `name`. + /// + /// The resulting clock is added as a child of `self`, but it also + /// stays alive until after `Drop::drop` is called because C code + /// keeps an extra reference to it until `device_finalize()` calls + /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in + /// which Rust code has a reference to a child object) it would be + /// possible for this function to return a `&Clock` too. + #[inline] + fn init_clock_out(&self, name: &str) -> Owned { + unsafe { + let cstr = CString::new(name).unwrap(); + let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr()); + + Owned::from(&*clk) + } + } +} + +impl DeviceMethods for R where R::Target: IsA {} + +unsafe impl ObjectType for Clock { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; +} +qom_isa!(Clock: Object); diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 11d21b8791..164effc655 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -470,11 +470,11 @@ macro_rules! vmstate_clock { $crate::assert_field_type!( $struct_name, $field_name, - core::ptr::NonNull<$crate::bindings::Clock> + $crate::qom::Owned<$crate::bindings::Clock> ); $crate::offset_of!($struct_name, $field_name) }, - size: ::core::mem::size_of::<*const $crate::bindings::Clock>(), + size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, ..$crate::zeroable::Zeroable::ZERO From 688c67415858684a2feef4477e6bc8159ac090bd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 11:23:14 +0100 Subject: [PATCH 1683/2892] rust: qom: allow initializing interface vtables Unlike regular classes, interface vtables can only be obtained via object_class_dynamic_cast. Provide a wrapper that allows accessing the vtable and pass it to a ClassInitImpl implementation, for example ClassInitImpl. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/prelude.rs | 1 + rust/qemu-api/src/qom.rs | 45 ++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 87e3ce90f2..254edb476d 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -9,6 +9,7 @@ pub use crate::cell::BqlRefCell; pub use crate::qdev::DeviceMethods; +pub use crate::qom::InterfaceType; pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 3e63cb30ca..3d5ab2d901 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -66,8 +66,8 @@ pub use bindings::{Object, ObjectClass}; use crate::{ bindings::{ - self, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, - object_unref, TypeInfo, + self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, + object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, cell::bql_locked, }; @@ -263,6 +263,47 @@ pub unsafe trait ObjectType: Sized { } } +/// Trait exposed by all structs corresponding to QOM interfaces. +/// Unlike `ObjectType`, it is implemented on the class type (which provides +/// the vtable for the interfaces). +/// +/// # Safety +/// +/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; +/// right now, interfaces can only be declared in C. +pub unsafe trait InterfaceType: Sized { + /// The name of the type, which can be passed to + /// `object_class_dynamic_cast()` to obtain the pointer to the vtable + /// for this interface. + const TYPE_NAME: &'static CStr; + + /// Initialize the vtable for the interface; the generic argument `T` is the + /// type being initialized, while the generic argument `U` is the type that + /// lists the interface in its `TypeInfo`. + /// + /// # Panics + /// + /// Panic if the incoming argument if `T` does not implement the interface. + fn interface_init< + T: ObjectType + ClassInitImpl + ClassInitImpl, + U: ObjectType, + >( + klass: &mut U::Class, + ) { + unsafe { + // SAFETY: upcasting to ObjectClass is always valid, and the + // return type is either NULL or the argument itself + let result: *mut Self = object_class_dynamic_cast( + (klass as *mut U::Class).cast(), + Self::TYPE_NAME.as_ptr(), + ) + .cast(); + + >::class_init(result.as_mut().unwrap()) + } + } +} + /// This trait provides safe casting operations for QOM objects to raw pointers, /// to be used for example for FFI. The trait can be applied to any kind of /// reference or smart pointers, and enforces correctness through the [`IsA`] From 68da5402df003a855c581563acc6f5f8c5d563f0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Jan 2025 12:01:18 +0100 Subject: [PATCH 1684/2892] rust: qdev: make ObjectImpl a supertrait of DeviceImpl In practice it has to be implemented always in order to access an implementation of ClassInitImpl. Make the relationship explicit in the code. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 176c69a560..34d24da4b6 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -17,12 +17,12 @@ use crate::{ callbacks::FnCall, cell::bql_locked, prelude::*, - qom::{ClassInitImpl, ObjectClass, Owned}, + qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, }; /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl { +pub trait DeviceImpl: ObjectImpl { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). From 5472a38cb9e10bda897fc29d4841c00476f22585 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 11:26:48 +0100 Subject: [PATCH 1685/2892] rust: qdev: switch from legacy reset to Resettable Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- meson.build | 1 + rust/hw/char/pl011/src/device.rs | 10 ++- rust/qemu-api/src/qdev.rs | 111 ++++++++++++++++++++++++------- rust/qemu-api/tests/tests.rs | 5 +- 4 files changed, 99 insertions(+), 28 deletions(-) diff --git a/meson.build b/meson.build index 18cf9e2913..16c76c493f 100644 --- a/meson.build +++ b/meson.build @@ -4073,6 +4073,7 @@ if have_rust 'MigrationPriority', 'QEMUChrEvent', 'QEMUClockType', + 'ResetType', 'device_endian', 'module_init_type', ] diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 37936a328b..1d0390b4fb 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -18,7 +18,7 @@ use qemu_api::{ c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, - qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property}, + qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, sysbus::{SysBusDevice, SysBusDeviceClass}, vmstate::VMStateDescription, @@ -171,7 +171,10 @@ impl DeviceImpl for PL011State { Some(&device_class::VMSTATE_PL011) } const REALIZE: Option = Some(Self::realize); - const RESET: Option = Some(Self::reset); +} + +impl ResettablePhasesImpl for PL011State { + const HOLD: Option = Some(Self::reset_hold); } impl PL011Registers { @@ -622,7 +625,7 @@ impl PL011State { } } - pub fn reset(&self) { + pub fn reset_hold(&self, _type: ResetType) { self.regs.borrow_mut().reset(); } @@ -737,3 +740,4 @@ impl ObjectImpl for PL011Luminary { } impl DeviceImpl for PL011Luminary {} +impl ResettablePhasesImpl for PL011Luminary {} diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 34d24da4b6..64ba3d9098 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,10 +10,10 @@ use std::{ ptr::NonNull, }; -pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property}; +pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType}; use crate::{ - bindings::{self, Error}, + bindings::{self, Error, ResettableClass}, callbacks::FnCall, cell::bql_locked, prelude::*, @@ -21,8 +21,70 @@ use crate::{ vmstate::VMStateDescription, }; +/// Trait providing the contents of the `ResettablePhases` struct, +/// which is part of the QOM `Resettable` interface. +pub trait ResettablePhasesImpl { + /// If not None, this is called when the object enters reset. It + /// can reset local state of the object, but it must not do anything that + /// has a side-effect on other objects, such as raising or lowering an + /// [`InterruptSource`](crate::irq::InterruptSource), or reading or + /// writing guest memory. It takes the reset's type as argument. + const ENTER: Option = None; + + /// If not None, this is called when the object for entry into reset, once + /// every object in the system which is being reset has had its + /// `ResettablePhasesImpl::ENTER` method called. At this point devices + /// can do actions that affect other objects. + /// + /// If in doubt, implement this method. + const HOLD: Option = None; + + /// If not None, this phase is called when the object leaves the reset + /// state. Actions affecting other objects are permitted. + const EXIT: Option = None; +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_enter_fn( + obj: *mut Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::ENTER.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_hold_fn( + obj: *mut Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::HOLD.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_exit_fn( + obj: *mut Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); +} + /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: ObjectImpl { +pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). @@ -31,13 +93,6 @@ pub trait DeviceImpl: ObjectImpl { /// with the function pointed to by `REALIZE`. const REALIZE: Option = None; - /// If not `None`, the parent class's `reset` method is overridden - /// with the function pointed to by `RESET`. - /// - /// Rust does not yet support the three-phase reset protocol; this is - /// usually okay for leaf classes. - const RESET: Option = None; - /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants /// is unstable until Rust 1.83.0. @@ -65,29 +120,36 @@ unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp T::REALIZE.unwrap()(unsafe { state.as_ref() }); } -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { - let mut state = NonNull::new(dev).unwrap().cast::(); - T::RESET.unwrap()(unsafe { state.as_mut() }); +unsafe impl InterfaceType for ResettableClass { + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; +} + +impl ClassInitImpl for T +where + T: ResettablePhasesImpl, +{ + fn class_init(rc: &mut ResettableClass) { + if ::ENTER.is_some() { + rc.phases.enter = Some(rust_resettable_enter_fn::); + } + if ::HOLD.is_some() { + rc.phases.hold = Some(rust_resettable_hold_fn::); + } + if ::EXIT.is_some() { + rc.phases.exit = Some(rust_resettable_exit_fn::); + } + } } impl ClassInitImpl for T where - T: ClassInitImpl + DeviceImpl, + T: ClassInitImpl + ClassInitImpl + DeviceImpl, { fn class_init(dc: &mut DeviceClass) { if ::REALIZE.is_some() { dc.realize = Some(rust_realize_fn::); } - if ::RESET.is_some() { - unsafe { - bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); - } - } if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; } @@ -98,6 +160,7 @@ where } } + ResettableClass::interface_init::(dc); >::class_init(&mut dc.parent_class); } } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 10748fba19..92dbfb8a0c 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -13,7 +13,7 @@ use qemu_api::{ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, + qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, @@ -61,6 +61,8 @@ impl ObjectImpl for DummyState { const ABSTRACT: bool = false; } +impl ResettablePhasesImpl for DummyState {} + impl DeviceImpl for DummyState { fn properties() -> &'static [Property] { &DUMMY_PROPERTIES @@ -101,6 +103,7 @@ impl ObjectImpl for DummyChildState { const ABSTRACT: bool = false; } +impl ResettablePhasesImpl for DummyChildState {} impl DeviceImpl for DummyChildState {} impl ClassInitImpl for DummyChildState { From d449d29a99dc132d4a49351e3501b6bff7500784 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Dec 2024 17:09:35 +0100 Subject: [PATCH 1686/2892] rust: bindings: add Send and Sync markers for types that have bindings This is needed for the MemoryRegionOps to be declared as static; Rust requires static elements to be Sync. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 46 +++++++++++++++++++++++++++++++++++ rust/qemu-api/src/irq.rs | 3 +++ 2 files changed, 49 insertions(+) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 8a9b821bb9..b71220113e 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -21,9 +21,55 @@ include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); +// SAFETY: these are implemented in C; the bindings need to assert that the +// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. +unsafe impl Send for BusState {} +unsafe impl Sync for BusState {} + +unsafe impl Send for CharBackend {} +unsafe impl Sync for CharBackend {} + +unsafe impl Send for Chardev {} +unsafe impl Sync for Chardev {} + +unsafe impl Send for Clock {} +unsafe impl Sync for Clock {} + +unsafe impl Send for DeviceState {} +unsafe impl Sync for DeviceState {} + +unsafe impl Send for MemoryRegion {} +unsafe impl Sync for MemoryRegion {} + +unsafe impl Send for ObjectClass {} +unsafe impl Sync for ObjectClass {} + +unsafe impl Send for Object {} +unsafe impl Sync for Object {} + +unsafe impl Send for SysBusDevice {} +unsafe impl Sync for SysBusDevice {} + +// SAFETY: this is a pure data struct +unsafe impl Send for CoalescedMemoryRange {} +unsafe impl Sync for CoalescedMemoryRange {} + +// SAFETY: these are constants and vtables; the Send and Sync requirements +// are deferred to the unsafe callbacks that they contain +unsafe impl Send for MemoryRegionOps {} +unsafe impl Sync for MemoryRegionOps {} + unsafe impl Send for Property {} unsafe impl Sync for Property {} + +unsafe impl Send for TypeInfo {} unsafe impl Sync for TypeInfo {} + +unsafe impl Send for VMStateDescription {} unsafe impl Sync for VMStateDescription {} + +unsafe impl Send for VMStateField {} unsafe impl Sync for VMStateField {} + +unsafe impl Send for VMStateInfo {} unsafe impl Sync for VMStateInfo {} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 378e520295..638545c3a6 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -43,6 +43,9 @@ where _marker: PhantomData, } +// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken +unsafe impl Sync for InterruptSource where c_int: From {} + impl InterruptSource { /// Send a low (`false`) value to the interrupt sink. pub fn lower(&self) { From 590faa03ee64b4221d1be39949190e82e361efb7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Jan 2025 18:14:25 +0100 Subject: [PATCH 1687/2892] rust: bindings for MemoryRegionOps Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + rust/hw/char/pl011/src/device.rs | 51 +++---- rust/hw/char/pl011/src/lib.rs | 1 - rust/hw/char/pl011/src/memory_ops.rs | 34 ----- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/memory.rs | 191 +++++++++++++++++++++++++++ rust/qemu-api/src/sysbus.rs | 7 +- rust/qemu-api/src/zeroable.rs | 1 + 9 files changed, 227 insertions(+), 61 deletions(-) delete mode 100644 rust/hw/char/pl011/src/memory_ops.rs create mode 100644 rust/qemu-api/src/memory.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 8cccca7a73..a5399db50b 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -180,6 +180,7 @@ module status ``cell`` stable ``c_str`` complete ``irq`` complete +``memory`` stable ``module`` complete ``offset_of`` stable ``qdev`` stable diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 1d0390b4fb..5e4e75133c 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -10,13 +10,14 @@ use std::{ use qemu_api::{ bindings::{ - error_fatal, hwaddr, memory_region_init_io, qdev_prop_set_chr, qemu_chr_fe_accept_input, - qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq, - sysbus_connect_irq, sysbus_mmio_map, sysbus_realize, CharBackend, Chardev, MemoryRegion, - QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, + error_fatal, qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, + qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, + sysbus_mmio_map, sysbus_realize, CharBackend, Chardev, QEMUChrEvent, + CHR_IOCTL_SERIAL_SET_BREAK, }, c_str, impl_vmstate_forward, irq::InterruptSource, + memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, @@ -26,7 +27,6 @@ use qemu_api::{ use crate::{ device_class, - memory_ops::PL011_OPS, registers::{self, Interrupt}, RegisterOffset, }; @@ -487,20 +487,24 @@ impl PL011State { /// location/instance. All its fields are expected to hold unitialized /// values with the sole exception of `parent_obj`. unsafe fn init(&mut self) { + static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() + .read(&PL011State::read) + .write(&PL011State::write) + .native_endian() + .impl_sizes(4, 4) + .build(); + // SAFETY: // // self and self.iomem are guaranteed to be valid at this point since callers // must make sure the `self` reference is valid. - unsafe { - memory_region_init_io( - addr_of_mut!(self.iomem), - addr_of_mut!(*self).cast::(), - &PL011_OPS, - addr_of_mut!(*self).cast::(), - Self::TYPE_NAME.as_ptr(), - 0x1000, - ); - } + MemoryRegion::init_io( + unsafe { &mut *addr_of_mut!(self.iomem) }, + addr_of_mut!(*self), + &PL011_OPS, + "pl011", + 0x1000, + ); self.regs = Default::default(); @@ -525,7 +529,7 @@ impl PL011State { } } - pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { + pub fn read(&self, offset: hwaddr, _size: u32) -> u64 { match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { let device_id = self.get_class().device_id; @@ -540,7 +544,7 @@ impl PL011State { if update_irq { self.update(); unsafe { - qemu_chr_fe_accept_input(&mut self.char_backend); + qemu_chr_fe_accept_input(addr_of!(self.char_backend) as *mut _); } } result.into() @@ -548,7 +552,7 @@ impl PL011State { } } - pub fn write(&mut self, offset: hwaddr, value: u64) { + pub fn write(&self, offset: hwaddr, value: u64, _size: u32) { let mut update_irq = false; if let Ok(field) = RegisterOffset::try_from(offset) { // qemu_chr_fe_write_all() calls into the can_receive @@ -561,14 +565,15 @@ impl PL011State { // XXX this blocks entire thread. Rewrite to use // qemu_chr_fe_write and background I/O callbacks unsafe { - qemu_chr_fe_write_all(&mut self.char_backend, &ch, 1); + qemu_chr_fe_write_all(addr_of!(self.char_backend) as *mut _, &ch, 1); } } - update_irq = self - .regs - .borrow_mut() - .write(field, value as u32, &mut self.char_backend); + update_irq = self.regs.borrow_mut().write( + field, + value as u32, + addr_of!(self.char_backend) as *mut _, + ); } else { eprintln!("write bad offset {offset} value {value}"); } diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 3c72f1221f..1bf46c65af 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -18,7 +18,6 @@ use qemu_api::c_str; mod device; mod device_class; -mod memory_ops; pub use device::pl011_create; diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs deleted file mode 100644 index 432d326389..0000000000 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use core::ptr::NonNull; -use std::os::raw::{c_uint, c_void}; - -use qemu_api::{bindings::*, zeroable::Zeroable}; - -use crate::device::PL011State; - -pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { - read: Some(pl011_read), - write: Some(pl011_write), - read_with_attrs: None, - write_with_attrs: None, - endianness: device_endian::DEVICE_NATIVE_ENDIAN, - valid: Zeroable::ZERO, - impl_: MemoryRegionOps__bindgen_ty_2 { - min_access_size: 4, - max_access_size: 4, - ..Zeroable::ZERO - }, -}; - -unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { - let mut state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_mut() }.read(addr, size) -} - -unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { - let mut state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_mut() }.write(addr, data); -} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 60944a657d..80eafc7f6b 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -22,6 +22,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/c_str.rs', 'src/irq.rs', + 'src/memory.rs', 'src/module.rs', 'src/offset_of.rs', 'src/prelude.rs', diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 3cf9371cff..e4316b21cf 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -19,6 +19,7 @@ pub mod c_str; pub mod callbacks; pub mod cell; pub mod irq; +pub mod memory; pub mod module; pub mod offset_of; pub mod qdev; diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs new file mode 100644 index 0000000000..963d689c27 --- /dev/null +++ b/rust/qemu-api/src/memory.rs @@ -0,0 +1,191 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for `MemoryRegion` and `MemoryRegionOps` + +use std::{ + ffi::{CStr, CString}, + marker::{PhantomData, PhantomPinned}, + os::raw::{c_uint, c_void}, + ptr::addr_of, +}; + +pub use bindings::hwaddr; + +use crate::{ + bindings::{self, device_endian, memory_region_init_io}, + callbacks::FnCall, + prelude::*, + zeroable::Zeroable, +}; + +pub struct MemoryRegionOps( + bindings::MemoryRegionOps, + // Note: quite often you'll see PhantomData mentioned when discussing + // covariance and contravariance; you don't need any of those to understand + // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* + // holds callbacks that take an argument of type &T, except the type is erased + // before the callback is stored in the bindings::MemoryRegionOps field. + // The argument of PhantomData is a function pointer in order to represent + // that relationship; while that will also provide desirable and safe variance + // for T, variance is not the point but just a consequence. + PhantomData, +); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for MemoryRegionOps {} + +#[derive(Clone)] +pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); + +unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( + opaque: *mut c_void, + addr: hwaddr, + size: c_uint, +) -> u64 { + F::call((unsafe { &*(opaque.cast::()) }, addr, size)) +} + +unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( + opaque: *mut c_void, + addr: hwaddr, + data: u64, + size: c_uint, +) { + F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) +} + +impl MemoryRegionOpsBuilder { + #[must_use] + pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { + self.0.read = Some(memory_region_ops_read_cb::); + self + } + + #[must_use] + pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { + self.0.write = Some(memory_region_ops_write_cb::); + self + } + + #[must_use] + pub const fn big_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; + self + } + + #[must_use] + pub const fn little_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; + self + } + + #[must_use] + pub const fn native_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; + self + } + + #[must_use] + pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { + self.0.valid.min_access_size = min; + self.0.valid.max_access_size = max; + self + } + + #[must_use] + pub const fn valid_unaligned(mut self) -> Self { + self.0.valid.unaligned = true; + self + } + + #[must_use] + pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { + self.0.impl_.min_access_size = min; + self.0.impl_.max_access_size = max; + self + } + + #[must_use] + pub const fn impl_unaligned(mut self) -> Self { + self.0.impl_.unaligned = true; + self + } + + #[must_use] + pub const fn build(self) -> MemoryRegionOps { + MemoryRegionOps::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::MemoryRegionOps::ZERO, PhantomData) + } +} + +impl Default for MemoryRegionOpsBuilder { + fn default() -> Self { + Self::new() + } +} + +/// A safe wrapper around [`bindings::MemoryRegion`]. Compared to the +/// underlying C struct it is marked as pinned because the QOM tree +/// contains a pointer to it. +pub struct MemoryRegion { + inner: bindings::MemoryRegion, + _pin: PhantomPinned, +} + +impl MemoryRegion { + // inline to ensure that it is not included in tests, which only + // link to hwcore and qom. FIXME: inlining is actually the opposite + // of what we want, since this is the type-erased version of the + // init_io function below. Look into splitting the qemu_api crate. + #[inline(always)] + unsafe fn do_init_io( + slot: *mut bindings::MemoryRegion, + owner: *mut Object, + ops: &'static bindings::MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + let cstr = CString::new(name).unwrap(); + memory_region_init_io( + slot, + owner.cast::(), + ops, + owner.cast::(), + cstr.as_ptr(), + size, + ); + } + } + + pub fn init_io>( + &mut self, + owner: *mut T, + ops: &'static MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + Self::do_init_io(&mut self.inner, owner.cast::(), &ops.0, name, size); + } + } + + pub(crate) const fn as_mut_ptr(&self) -> *mut bindings::MemoryRegion { + addr_of!(self.inner) as *mut _ + } +} + +unsafe impl ObjectType for MemoryRegion { + type Class = bindings::MemoryRegionClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; +} +qom_isa!(MemoryRegion: Object); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index e6762b5c14..c27dbf79e4 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -2,7 +2,7 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, ptr::addr_of}; +use std::ffi::CStr; pub use bindings::{SysBusDevice, SysBusDeviceClass}; @@ -10,6 +10,7 @@ use crate::{ bindings, cell::bql_locked, irq::InterruptSource, + memory::MemoryRegion, prelude::*, qdev::{DeviceClass, DeviceState}, qom::ClassInitImpl, @@ -42,10 +43,10 @@ where /// important, since whoever creates the sysbus device will refer to the /// region with a number that corresponds to the order of calls to /// `init_mmio`. - fn init_mmio(&self, iomem: &bindings::MemoryRegion) { + fn init_mmio(&self, iomem: &MemoryRegion) { assert!(bql_locked()); unsafe { - bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _); + bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr()); } } diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 7b04947cb6..75742b50d4 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -100,3 +100,4 @@ impl_zeroable!(crate::bindings::VMStateField); impl_zeroable!(crate::bindings::VMStateDescription); impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1); impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2); +impl_zeroable!(crate::bindings::MemoryRegionOps); From 61faf6ac7b25b9a743817f0c5fc935a6cdfa7dfb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Feb 2025 11:04:07 +0100 Subject: [PATCH 1688/2892] rust: irq: define ObjectType for IRQState This is a small preparation in order to use an Owned for the argument to sysbus_connect_irq. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/irq.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 638545c3a6..835b027d5e 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -5,11 +5,12 @@ //! Bindings for interrupt sources use core::ptr; -use std::{marker::PhantomData, os::raw::c_int}; +use std::{ffi::CStr, marker::PhantomData, os::raw::c_int}; use crate::{ - bindings::{qemu_set_irq, IRQState}, + bindings::{self, qemu_set_irq}, prelude::*, + qom::ObjectClass, }; /// Interrupt sources are used by devices to pass changes to a value (typically @@ -21,7 +22,8 @@ use crate::{ /// method sends a `true` value to the sink. If the guest has to see a /// different polarity, that change is performed by the board between the /// device and the interrupt controller. -/// +pub type IRQState = bindings::IRQState; + /// Interrupts are implemented as a pointer to the interrupt "sink", which has /// type [`IRQState`]. A device exposes its source as a QOM link property using /// a function such as [`SysBusDeviceMethods::init_irq`], and @@ -91,3 +93,10 @@ impl Default for InterruptSource { } } } + +unsafe impl ObjectType for IRQState { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; +} +qom_isa!(IRQState: Object); From a22bd55ffd889f3027c3158d0014c76f204c69dd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Feb 2025 11:04:07 +0100 Subject: [PATCH 1689/2892] rust: chardev, qdev: add bindings to qdev_prop_set_chr Because the argument to the function is an Owned, this also adds an ObjectType implementation to Chardev. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 3 ++- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/chardev.rs | 19 +++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/qdev.rs | 9 +++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 rust/qemu-api/src/chardev.rs diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 5e4e75133c..4e95907371 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -12,9 +12,10 @@ use qemu_api::{ bindings::{ error_fatal, qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, - sysbus_mmio_map, sysbus_realize, CharBackend, Chardev, QEMUChrEvent, + sysbus_mmio_map, sysbus_realize, CharBackend, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, }, + chardev::Chardev, c_str, impl_vmstate_forward, irq::InterruptSource, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 80eafc7f6b..45e30324b2 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -20,6 +20,7 @@ _qemu_api_rs = static_library( 'src/bitops.rs', 'src/callbacks.rs', 'src/cell.rs', + 'src/chardev.rs', 'src/c_str.rs', 'src/irq.rs', 'src/memory.rs', diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs new file mode 100644 index 0000000000..74cfb634e5 --- /dev/null +++ b/rust/qemu-api/src/chardev.rs @@ -0,0 +1,19 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for character devices + +use std::ffi::CStr; + +use crate::{bindings, prelude::*}; + +pub type Chardev = bindings::Chardev; +pub type ChardevClass = bindings::ChardevClass; + +unsafe impl ObjectType for Chardev { + type Class = ChardevClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) }; +} +qom_isa!(Chardev: Object); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index e4316b21cf..2a338a888a 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -18,6 +18,7 @@ pub mod bitops; pub mod c_str; pub mod callbacks; pub mod cell; +pub mod chardev; pub mod irq; pub mod memory; pub mod module; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 64ba3d9098..73343e10b9 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -16,6 +16,7 @@ use crate::{ bindings::{self, Error, ResettableClass}, callbacks::FnCall, cell::bql_locked, + chardev::Chardev, prelude::*, qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, @@ -297,6 +298,14 @@ where Owned::from(&*clk) } } + + fn prop_set_chr(&self, propname: &str, chr: &Owned) { + assert!(bql_locked()); + let c_propname = CString::new(propname).unwrap(); + unsafe { + bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr()); + } + } } impl DeviceMethods for R where R::Target: IsA {} From 7630ca2a701a0f79728996e660cda06518c97b9b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 10 Feb 2025 16:11:58 +0100 Subject: [PATCH 1690/2892] rust: pl011: convert pl011_create to safe Rust Not a major change but, as a small but significant step in creating qdev bindings, show how pl011_create can be written without "unsafe" calls (apart from converting pointers to references). This also provides a starting point for creating Error** bindings. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 37 ++++++++++++++++---------------- rust/qemu-api/src/sysbus.rs | 34 ++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 4e95907371..fe73771021 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -10,14 +10,12 @@ use std::{ use qemu_api::{ bindings::{ - error_fatal, qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, - qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, - sysbus_mmio_map, sysbus_realize, CharBackend, QEMUChrEvent, - CHR_IOCTL_SERIAL_SET_BREAK, + qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, + qemu_chr_fe_write_all, CharBackend, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, }, chardev::Chardev, - c_str, impl_vmstate_forward, - irq::InterruptSource, + impl_vmstate_forward, + irq::{IRQState, InterruptSource}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, @@ -698,26 +696,27 @@ pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { /// # Safety /// -/// We expect the FFI user of this function to pass a valid pointer for `chr`. +/// We expect the FFI user of this function to pass a valid pointer for `chr` +/// and `irq`. #[no_mangle] pub unsafe extern "C" fn pl011_create( addr: u64, - irq: qemu_irq, + irq: *mut IRQState, chr: *mut Chardev, ) -> *mut DeviceState { - let pl011 = PL011State::new(); - unsafe { - let dev = pl011.as_mut_ptr::(); - qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); + // SAFETY: The callers promise that they have owned references. + // They do not gift them to pl011_create, so use `Owned::from`. + let irq = unsafe { Owned::::from(&*irq) }; + let chr = unsafe { Owned::::from(&*chr) }; - let sysbus = pl011.as_mut_ptr::(); - sysbus_realize(sysbus, addr_of_mut!(error_fatal)); - sysbus_mmio_map(sysbus, 0, addr); - sysbus_connect_irq(sysbus, 0, irq); + let dev = PL011State::new(); + dev.prop_set_chr("chardev", &chr); + dev.sysbus_realize(); + dev.mmio_map(0, addr); + dev.connect_irq(0, &irq); - // return the pointer, which is kept alive by the QOM tree; drop owned ref - pl011.as_mut_ptr() - } + // The pointer is kept alive by the QOM tree; drop the owned ref + dev.as_mut_ptr() } #[repr(C)] diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index c27dbf79e4..1f66a5f1e0 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -2,18 +2,18 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later -use std::ffi::CStr; +use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ bindings, cell::bql_locked, - irq::InterruptSource, + irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, qdev::{DeviceClass, DeviceState}, - qom::ClassInitImpl, + qom::{ClassInitImpl, Owned}, }; unsafe impl ObjectType for SysBusDevice { @@ -60,6 +60,34 @@ where bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr()); } } + + // TODO: do we want a type like GuestAddress here? + fn mmio_map(&self, id: u32, addr: u64) { + assert!(bql_locked()); + let id: i32 = id.try_into().unwrap(); + unsafe { + bindings::sysbus_mmio_map(self.as_mut_ptr(), id, addr); + } + } + + // Owned<> is used here because sysbus_connect_irq (via + // object_property_set_link) adds a reference to the IRQState, + // which can prolong its life + fn connect_irq(&self, id: u32, irq: &Owned) { + assert!(bql_locked()); + let id: i32 = id.try_into().unwrap(); + unsafe { + bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr()); + } + } + + fn sysbus_realize(&self) { + // TODO: return an Error + assert!(bql_locked()); + unsafe { + bindings::sysbus_realize(self.as_mut_ptr(), addr_of_mut!(bindings::error_fatal)); + } + } } impl SysBusDeviceMethods for R where R::Target: IsA {} From f32352ff9ea1ce3bdf432e29a587ccf84b1ec57a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:42 +0800 Subject: [PATCH 1691/2892] i386/fw_cfg: move hpet_cfg definition to hpet.c HPET device needs to access and update hpet_cfg variable, but now it is defined in hw/i386/fw_cfg.c and Rust code can't access it. Move hpet_cfg definition to hpet.c (and rename it to hpet_fw_cfg). This allows Rust HPET device implements its own global hpet_fw_cfg variable, and will further reduce the use of unsafe C code access and calls in the Rust HPET implementation. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/fw_cfg.c | 6 ++++-- hw/timer/hpet.c | 16 +++++++++------- include/hw/timer/hpet.h | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 91bf1df0f2..d08aefa029 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -26,7 +26,9 @@ #include CONFIG_DEVICES #include "target/i386/cpu.h" -struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; +#if !defined(CONFIG_HPET) && !defined(CONFIG_X_HPET_RUST) +struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; +#endif const char *fw_cfg_arch_key_name(uint16_t key) { @@ -149,7 +151,7 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, #endif fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_fw_cfg, sizeof(hpet_fw_cfg)); /* allocate memory for the NUMA channel: one (64bit) word for the number * of nodes, one word for each VCPU->node and one word for each node to * hold the amount of memory. diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 1c8c6c69ef..dcff18a987 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -40,6 +40,8 @@ #include "qom/object.h" #include "trace.h" +struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; + #define HPET_MSI_SUPPORT 0 OBJECT_DECLARE_SIMPLE_TYPE(HPETState, HPET) @@ -278,7 +280,7 @@ static int hpet_post_load(void *opaque, int version_id) /* Push number of timers into capability returned via HPET_ID */ s->capability &= ~HPET_ID_NUM_TIM_MASK; s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ s->flags &= ~(1 << HPET_MSI_SUPPORT); @@ -665,8 +667,8 @@ static void hpet_reset(DeviceState *d) s->hpet_counter = 0ULL; s->hpet_offset = 0ULL; s->config = 0ULL; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; + hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + hpet_fw_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; /* to document that the RTC lowers its output on reset as well */ s->rtc_irq_level = 0; @@ -708,17 +710,17 @@ static void hpet_realize(DeviceState *dev, Error **errp) if (!s->intcap) { warn_report("Hpet's intcap not initialized"); } - if (hpet_cfg.count == UINT8_MAX) { + if (hpet_fw_cfg.count == UINT8_MAX) { /* first instance */ - hpet_cfg.count = 0; + hpet_fw_cfg.count = 0; } - if (hpet_cfg.count == 8) { + if (hpet_fw_cfg.count == 8) { error_setg(errp, "Only 8 instances of HPET is allowed"); return; } - s->hpet_id = hpet_cfg.count++; + s->hpet_id = hpet_fw_cfg.count++; for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) { sysbus_init_irq(sbd, &s->irqs[i]); diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h index 71e8c62453..c2656f7f0b 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -73,7 +73,7 @@ struct hpet_fw_config struct hpet_fw_entry hpet[8]; } QEMU_PACKED; -extern struct hpet_fw_config hpet_cfg; +extern struct hpet_fw_config hpet_fw_cfg; #define TYPE_HPET "hpet" From 7f2d4181a3efc3c1fd9de4bdca81317a1116239a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:43 +0800 Subject: [PATCH 1692/2892] rust/qdev: add the macro to define bit property HPET device (Rust device) needs to define the bit type property. Add a variant of define_property macro to define bit type property. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 73343e10b9..c44a22876b 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -168,6 +168,18 @@ where #[macro_export] macro_rules! define_property { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: $crate::offset_of!($state, $field) as isize, + bitnr: $bitnr, + set_default: true, + defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, + ..$crate::zeroable::Zeroable::ZERO + } + }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { $crate::bindings::Property { // use associated function syntax for type checking From e6f1195f55427bf246bb85b1bcbbfd8fbdc51889 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:44 +0800 Subject: [PATCH 1693/2892] rust/irq: Add a helper to convert [InterruptSource] to pointer This is useful when taking an InterruptSource slice and passing it to C function. Suggested-by: Paolo Bonzini Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/irq.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 835b027d5e..672eec1430 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -83,6 +83,12 @@ where pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState { self.cell.as_ptr() } + + #[allow(dead_code)] + pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut IRQState { + assert!(!slice.is_empty()); + slice[0].as_ptr() + } } impl Default for InterruptSource { From 9a96d410073df04808c6757fd4aab6cb8684b301 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:45 +0800 Subject: [PATCH 1694/2892] rust: add bindings for gpio_{in|out} initialization Wrap qdev_init_gpio_{in|out} as methods in DeviceMethods. And for qdev_init_gpio_in, based on FnCall, it can support idiomatic Rust callback without the need for C style wrapper. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/irq.rs | 1 - rust/qemu-api/src/qdev.rs | 47 +++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 672eec1430..d1c9dc96ef 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -84,7 +84,6 @@ where self.cell.as_ptr() } - #[allow(dead_code)] pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut IRQState { assert!(!slice.is_empty()); slice[0].as_ptr() diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index c44a22876b..3a7aa4def6 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -6,17 +6,18 @@ use std::{ ffi::{CStr, CString}, - os::raw::c_void, + os::raw::{c_int, c_void}, ptr::NonNull, }; pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType}; use crate::{ - bindings::{self, Error, ResettableClass}, + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass}, callbacks::FnCall, cell::bql_locked, chardev::Chardev, + irq::InterruptSource, prelude::*, qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, @@ -28,8 +29,8 @@ pub trait ResettablePhasesImpl { /// If not None, this is called when the object enters reset. It /// can reset local state of the object, but it must not do anything that /// has a side-effect on other objects, such as raising or lowering an - /// [`InterruptSource`](crate::irq::InterruptSource), or reading or - /// writing guest memory. It takes the reset's type as argument. + /// [`InterruptSource`], or reading or writing guest memory. It takes the + /// reset's type as argument. const ENTER: Option = None; /// If not None, this is called when the object for entry into reset, once @@ -318,6 +319,44 @@ where bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr()); } } + + fn init_gpio_in FnCall<(&'a Self::Target, u32, u32)>>( + &self, + num_lines: u32, + _cb: F, + ) { + let _: () = F::ASSERT_IS_SOME; + + unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( + opaque: *mut c_void, + line: c_int, + level: c_int, + ) { + // SAFETY: the opaque was passed as a reference to `T` + F::call((unsafe { &*(opaque.cast::()) }, line as u32, level as u32)) + } + + let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = + rust_irq_handler::; + + unsafe { + qdev_init_gpio_in( + self.as_mut_ptr::(), + Some(gpio_in_cb), + num_lines as c_int, + ); + } + } + + fn init_gpio_out(&self, pins: &[InterruptSource]) { + unsafe { + qdev_init_gpio_out( + self.as_mut_ptr::(), + InterruptSource::slice_as_ptr(pins), + pins.len() as c_int, + ); + } + } } impl DeviceMethods for R where R::Target: IsA {} From d015d4cbb4d16cf8adc8c10cbd2d0a45f014dad1 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Sat, 25 Jan 2025 20:51:32 +0800 Subject: [PATCH 1695/2892] rust: add bindings for memattrs The MemTxAttrs structure contains bitfield members, and bindgen is unable to generate an equivalent macro definition for MEMTXATTRS_UNSPECIFIED. Therefore, manually define a global constant variable MEMTXATTRS_UNSPECIFIED to support calls from Rust code. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250125125137.1223277-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/memory.rs | 16 ++++++++++++++-- rust/qemu-api/src/zeroable.rs | 1 + rust/wrapper.h | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index 963d689c27..682951ab44 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -2,7 +2,7 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later -//! Bindings for `MemoryRegion` and `MemoryRegionOps` +//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` use std::{ ffi::{CStr, CString}, @@ -11,7 +11,7 @@ use std::{ ptr::addr_of, }; -pub use bindings::hwaddr; +pub use bindings::{hwaddr, MemTxAttrs}; use crate::{ bindings::{self, device_endian, memory_region_init_io}, @@ -189,3 +189,15 @@ unsafe impl ObjectType for MemoryRegion { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; } qom_isa!(MemoryRegion: Object); + +/// A special `MemTxAttrs` constant, used to indicate that no memory +/// attributes are specified. +/// +/// Bus masters which don't specify any attributes will get this, +/// which has all attribute bits clear except the topmost one +/// (so that we can distinguish "all attributes deliberately clear" +/// from "didn't specify" if necessary). +pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { + unspecified: true, + ..Zeroable::ZERO +}; diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 75742b50d4..9f009606b1 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -101,3 +101,4 @@ impl_zeroable!(crate::bindings::VMStateDescription); impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1); impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2); impl_zeroable!(crate::bindings::MemoryRegionOps); +impl_zeroable!(crate::bindings::MemTxAttrs); diff --git a/rust/wrapper.h b/rust/wrapper.h index a9bc67af0d..54839ce0f5 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -62,3 +62,4 @@ typedef enum memory_order { #include "qapi/error.h" #include "migration/vmstate.h" #include "chardev/char-serial.h" +#include "exec/memattrs.h" From eadb83f9a3722045e291b6a73994004007dc4657 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:47 +0800 Subject: [PATCH 1696/2892] rust: add bindings for timer Add timer bindings to help handle idiomatic Rust callbacks. Additionally, wrap QEMUClockType in ClockType binding to avoid unsafe calls in device code. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + meson.build | 7 +++ rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/timer.rs | 98 ++++++++++++++++++++++++++++++++++++++ rust/wrapper.h | 1 + 6 files changed, 109 insertions(+) create mode 100644 rust/qemu-api/src/timer.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index a5399db50b..90958e5a30 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -186,6 +186,7 @@ module status ``qdev`` stable ``qom`` stable ``sysbus`` stable +``timer`` stable ``vmstate`` proof of concept ``zeroable`` stable ================ ====================== diff --git a/meson.build b/meson.build index 16c76c493f..8ed10b6624 100644 --- a/meson.build +++ b/meson.build @@ -4087,6 +4087,13 @@ if have_rust foreach enum : c_bitfields bindgen_args += ['--bitfield-enum', enum] endforeach + c_nocopy = [ + 'QEMUTimer', + ] + # Used to customize Drop trait + foreach struct : c_nocopy + bindgen_args += ['--no-copy', struct] + endforeach # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 45e30324b2..2e9c1078b9 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -30,6 +30,7 @@ _qemu_api_rs = static_library( 'src/qdev.rs', 'src/qom.rs', 'src/sysbus.rs', + 'src/timer.rs', 'src/vmstate.rs', 'src/zeroable.rs', ], diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 2a338a888a..ed1a8f9a2b 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -26,6 +26,7 @@ pub mod offset_of; pub mod qdev; pub mod qom; pub mod sysbus; +pub mod timer; pub mod vmstate; pub mod zeroable; diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs new file mode 100644 index 0000000000..a593538917 --- /dev/null +++ b/rust/qemu-api/src/timer.rs @@ -0,0 +1,98 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::os::raw::{c_int, c_void}; + +use crate::{ + bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType}, + callbacks::FnCall, +}; + +pub type Timer = bindings::QEMUTimer; +pub type TimerListGroup = bindings::QEMUTimerListGroup; + +impl Timer { + pub const MS: u32 = bindings::SCALE_MS; + pub const US: u32 = bindings::SCALE_US; + pub const NS: u32 = bindings::SCALE_NS; + + pub fn new() -> Self { + Default::default() + } + + const fn as_mut_ptr(&self) -> *mut Self { + self as *const Timer as *mut _ + } + + pub fn init_full<'timer, 'opaque: 'timer, T, F>( + &'timer mut self, + timer_list_group: Option<&TimerListGroup>, + clk_type: ClockType, + scale: u32, + attributes: u32, + _cb: F, + opaque: &'opaque T, + ) where + F: for<'a> FnCall<(&'a T,)>, + { + let _: () = F::ASSERT_IS_SOME; + + /// timer expiration callback + unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( + opaque: *mut c_void, + ) { + // SAFETY: the opaque was passed as a reference to `T`. + F::call((unsafe { &*(opaque.cast::()) },)) + } + + let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; + + // SAFETY: the opaque outlives the timer + unsafe { + timer_init_full( + self, + if let Some(g) = timer_list_group { + g as *const TimerListGroup as *mut _ + } else { + ::core::ptr::null_mut() + }, + clk_type.id, + scale as c_int, + attributes as c_int, + Some(timer_cb), + (opaque as *const T).cast::() as *mut c_void, + ) + } + } + + pub fn modify(&self, expire_time: u64) { + unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } + } + + pub fn delete(&self) { + unsafe { timer_del(self.as_mut_ptr()) } + } +} + +impl Drop for Timer { + fn drop(&mut self) { + self.delete() + } +} + +pub struct ClockType { + id: QEMUClockType, +} + +impl ClockType { + pub fn get_ns(&self) -> u64 { + // SAFETY: cannot be created outside this module, therefore id + // is valid + (unsafe { qemu_clock_get_ns(self.id) }) as u64 + } +} + +pub const CLOCK_VIRTUAL: ClockType = ClockType { + id: QEMUClockType::QEMU_CLOCK_VIRTUAL, +}; diff --git a/rust/wrapper.h b/rust/wrapper.h index 54839ce0f5..a35bfbd176 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -63,3 +63,4 @@ typedef enum memory_order { #include "migration/vmstate.h" #include "chardev/char-serial.h" #include "exec/memattrs.h" +#include "qemu/timer.h" From 0534248a6b515cb4dea29a6fd6c256dc77f2a953 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:48 +0800 Subject: [PATCH 1697/2892] rust/timer/hpet: define hpet_fw_cfg Define HPETFwEntry structure with the same memory layout as hpet_fw_entry in C. Further, define the global hpet_cfg variable in Rust which is the same as the C version. This hpet_cfg variable in Rust will replace the C version one and allows both Rust code and C code to access it. The Rust version of hpet_cfg is self-contained, avoiding unsafe access to C code. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 8 ++++ rust/Cargo.toml | 1 + rust/hw/meson.build | 1 + rust/hw/timer/hpet/Cargo.toml | 18 ++++++++ rust/hw/timer/hpet/meson.build | 18 ++++++++ rust/hw/timer/hpet/src/fw_cfg.rs | 71 ++++++++++++++++++++++++++++++++ rust/hw/timer/hpet/src/lib.rs | 10 +++++ rust/hw/timer/meson.build | 1 + rust/qemu-api/src/zeroable.rs | 6 ++- 9 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 rust/hw/timer/hpet/Cargo.toml create mode 100644 rust/hw/timer/hpet/meson.build create mode 100644 rust/hw/timer/hpet/src/fw_cfg.rs create mode 100644 rust/hw/timer/hpet/src/lib.rs create mode 100644 rust/hw/timer/meson.build diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c0c6069247..79e142723b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -37,6 +37,14 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "hpet" +version = "0.1.0" +dependencies = [ + "qemu_api", + "qemu_api_macros", +] + [[package]] name = "itertools" version = "0.11.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 5b0cb55928..5041d6291f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -4,6 +4,7 @@ members = [ "qemu-api-macros", "qemu-api", "hw/char/pl011", + "hw/timer/hpet", ] [workspace.lints.rust] diff --git a/rust/hw/meson.build b/rust/hw/meson.build index 860196645e..9749d4adfc 100644 --- a/rust/hw/meson.build +++ b/rust/hw/meson.build @@ -1 +1,2 @@ subdir('char') +subdir('timer') diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml new file mode 100644 index 0000000000..147f216e72 --- /dev/null +++ b/rust/hw/timer/hpet/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "hpet" +version = "0.1.0" +edition = "2021" +authors = ["Zhao Liu "] +license = "GPL-2.0-or-later" +description = "IA-PC High Precision Event Timer emulation in Rust" +rust-version = "1.63.0" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +qemu_api = { path = "../../../qemu-api" } +qemu_api_macros = { path = "../../../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build new file mode 100644 index 0000000000..c2d7c0532c --- /dev/null +++ b/rust/hw/timer/hpet/meson.build @@ -0,0 +1,18 @@ +_libhpet_rs = static_library( + 'hpet', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [ + qemu_api, + qemu_api_macros, + ], +) + +rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( + link_whole: [_libhpet_rs], + # Putting proc macro crates in `dependencies` is necessary for Meson to find + # them when compiling the root per-target static rust lib. + dependencies: [qemu_api_macros], + variables: {'crate': 'hpet'}, +)]) diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs new file mode 100644 index 0000000000..849e277d48 --- /dev/null +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -0,0 +1,71 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +#![allow(dead_code)] + +use std::ptr::addr_of_mut; + +use qemu_api::{cell::bql_locked, impl_zeroable, zeroable::Zeroable}; + +/// Each `HPETState` represents a Event Timer Block. The v1 spec supports +/// up to 8 blocks. QEMU only uses 1 block (in PC machine). +const HPET_MAX_NUM_EVENT_TIMER_BLOCK: usize = 8; + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct HPETFwEntry { + pub event_timer_block_id: u32, + pub address: u64, + pub min_tick: u16, + pub page_prot: u8, +} +impl_zeroable!(HPETFwEntry); + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct HPETFwConfig { + pub count: u8, + pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK], +} +impl_zeroable!(HPETFwConfig); + +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { + count: u8::MAX, + ..Zeroable::ZERO +}; + +impl HPETFwConfig { + pub(crate) fn assign_hpet_id() -> usize { + assert!(bql_locked()); + // SAFETY: all accesses go through these methods, which guarantee + // that the accesses are protected by the BQL. + let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; + + if fw_cfg.count == u8::MAX { + // first instance + fw_cfg.count = 0; + } + + if fw_cfg.count == 8 { + // TODO: Add error binding: error_setg() + panic!("Only 8 instances of HPET is allowed"); + } + + let id: usize = fw_cfg.count.into(); + fw_cfg.count += 1; + id + } + + pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) { + assert!(bql_locked()); + // SAFETY: all accesses go through these methods, which guarantee + // that the accesses are protected by the BQL. + let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; + + fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id; + fw_cfg.hpet[hpet_id].address = address; + } +} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs new file mode 100644 index 0000000000..44e9f3fb8a --- /dev/null +++ b/rust/hw/timer/hpet/src/lib.rs @@ -0,0 +1,10 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +//! # HPET QEMU Device Model +//! +//! This library implements a device model for the IA-PC HPET (High +//! Precision Event Timers) device in QEMU. + +pub mod fw_cfg; diff --git a/rust/hw/timer/meson.build b/rust/hw/timer/meson.build new file mode 100644 index 0000000000..22a84f1553 --- /dev/null +++ b/rust/hw/timer/meson.build @@ -0,0 +1 @@ +subdir('hpet') diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 9f009606b1..cd424e6ea0 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -56,6 +56,7 @@ pub unsafe trait Zeroable: Default { /// ## Differences with `core::mem::zeroed` /// /// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't +#[macro_export] macro_rules! const_zero { // This macro to produce a type-generic zero constant is taken from the // const_zero crate (v0.1.1): @@ -77,10 +78,11 @@ macro_rules! const_zero { } /// A wrapper to implement the `Zeroable` trait through the `const_zero` macro. +#[macro_export] macro_rules! impl_zeroable { ($type:ty) => { - unsafe impl Zeroable for $type { - const ZERO: Self = unsafe { const_zero!($type) }; + unsafe impl $crate::zeroable::Zeroable for $type { + const ZERO: Self = unsafe { $crate::const_zero!($type) }; } }; } From 269a8f155c7265488945e60ef0cae77556017ddd Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:49 +0800 Subject: [PATCH 1698/2892] rust/timer/hpet: add basic HPET timer and HPETState Add the HPETTimer and HPETState (HPET timer block), along with their basic methods and register definitions. This is in preparation for supporting the QAPI interfaces. Note, wrap all items in HPETState that may be changed in the callback called by C code into the BqlCell/BqlRefCell. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-9-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 629 +++++++++++++++++++++++++++++++++ rust/hw/timer/hpet/src/lib.rs | 1 + rust/wrapper.h | 1 + 3 files changed, 631 insertions(+) create mode 100644 rust/hw/timer/hpet/src/hpet.rs diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs new file mode 100644 index 0000000000..795610f8e8 --- /dev/null +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -0,0 +1,629 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +#![allow(dead_code)] + +use std::ptr::{addr_of_mut, null_mut, NonNull}; + +use qemu_api::{ + bindings::{address_space_memory, address_space_stl_le}, + cell::{BqlCell, BqlRefCell}, + irq::InterruptSource, + memory::{MemoryRegion, MEMTXATTRS_UNSPECIFIED}, + prelude::*, + qom::ParentField, + sysbus::SysBusDevice, + timer::{Timer, CLOCK_VIRTUAL}, +}; + +/// Register space for each timer block (`HPET_BASE` is defined in hpet.h). +const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes + +/// Minimum recommended hardware implementation. +const HPET_MIN_TIMERS: usize = 3; +/// Maximum timers in each timer block. +const HPET_MAX_TIMERS: usize = 32; + +/// Flags that HPETState.flags supports. +const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0; + +const HPET_NUM_IRQ_ROUTES: usize = 32; +const HPET_LEGACY_PIT_INT: u32 = 0; // HPET_LEGACY_RTC_INT isn't defined here. +const RTC_ISA_IRQ: usize = 8; + +const HPET_CLK_PERIOD: u64 = 10; // 10 ns +const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns + +/// General Capabilities and ID Register +const HPET_CAP_REG: u64 = 0x000; +/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec). +const HPET_CAP_REV_ID_VALUE: u64 = 0x1; +const HPET_CAP_REV_ID_SHIFT: usize = 0; +/// Number of Timers (bits 8:12) +const HPET_CAP_NUM_TIM_SHIFT: usize = 8; +/// Counter Size (bit 13) +const HPET_CAP_COUNT_SIZE_CAP_SHIFT: usize = 13; +/// Legacy Replacement Route Capable (bit 15) +const HPET_CAP_LEG_RT_CAP_SHIFT: usize = 15; +/// Vendor ID (bits 16:31) +const HPET_CAP_VENDER_ID_VALUE: u64 = 0x8086; +const HPET_CAP_VENDER_ID_SHIFT: usize = 16; +/// Main Counter Tick Period (bits 32:63) +const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32; + +/// General Configuration Register +const HPET_CFG_REG: u64 = 0x010; +/// Overall Enable (bit 0) +const HPET_CFG_ENABLE_SHIFT: usize = 0; +/// Legacy Replacement Route (bit 1) +const HPET_CFG_LEG_RT_SHIFT: usize = 1; +/// Other bits are reserved. +const HPET_CFG_WRITE_MASK: u64 = 0x003; + +/// General Interrupt Status Register +const HPET_INT_STATUS_REG: u64 = 0x020; + +/// Main Counter Value Register +const HPET_COUNTER_REG: u64 = 0x0f0; + +/// Timer N Configuration and Capability Register (masked by 0x18) +const HPET_TN_CFG_REG: u64 = 0x000; +/// bit 0, 7, and bits 16:31 are reserved. +/// bit 4, 5, 15, and bits 32:64 are read-only. +const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e; +/// Timer N Interrupt Type (bit 1) +const HPET_TN_CFG_INT_TYPE_SHIFT: usize = 1; +/// Timer N Interrupt Enable (bit 2) +const HPET_TN_CFG_INT_ENABLE_SHIFT: usize = 2; +/// Timer N Type (Periodic enabled or not, bit 3) +const HPET_TN_CFG_PERIODIC_SHIFT: usize = 3; +/// Timer N Periodic Interrupt Capable (support Periodic or not, bit 4) +const HPET_TN_CFG_PERIODIC_CAP_SHIFT: usize = 4; +/// Timer N Size (timer size is 64-bits or 32 bits, bit 5) +const HPET_TN_CFG_SIZE_CAP_SHIFT: usize = 5; +/// Timer N Value Set (bit 6) +const HPET_TN_CFG_SETVAL_SHIFT: usize = 6; +/// Timer N 32-bit Mode (bit 8) +const HPET_TN_CFG_32BIT_SHIFT: usize = 8; +/// Timer N Interrupt Rout (bits 9:13) +const HPET_TN_CFG_INT_ROUTE_MASK: u64 = 0x3e00; +const HPET_TN_CFG_INT_ROUTE_SHIFT: usize = 9; +/// Timer N FSB Interrupt Enable (bit 14) +const HPET_TN_CFG_FSB_ENABLE_SHIFT: usize = 14; +/// Timer N FSB Interrupt Delivery (bit 15) +const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; +/// Timer N Interrupt Routing Capability (bits 32:63) +const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; + +/// Timer N Comparator Value Register (masked by 0x18) +const HPET_TN_CMP_REG: u64 = 0x008; + +/// Timer N FSB Interrupt Route Register (masked by 0x18) +const HPET_TN_FSB_ROUTE_REG: u64 = 0x010; + +const fn hpet_next_wrap(cur_tick: u64) -> u64 { + (cur_tick | 0xffffffff) + 1 +} + +const fn hpet_time_after(a: u64, b: u64) -> bool { + ((b - a) as i64) < 0 +} + +const fn ticks_to_ns(value: u64) -> u64 { + value * HPET_CLK_PERIOD +} + +const fn ns_to_ticks(value: u64) -> u64 { + value / HPET_CLK_PERIOD +} + +// Avoid touching the bits that cannot be written. +const fn hpet_fixup_reg(new: u64, old: u64, mask: u64) -> u64 { + (new & mask) | (old & !mask) +} + +const fn activating_bit(old: u64, new: u64, shift: usize) -> bool { + let mask: u64 = 1 << shift; + (old & mask == 0) && (new & mask != 0) +} + +const fn deactivating_bit(old: u64, new: u64, shift: usize) -> bool { + let mask: u64 = 1 << shift; + (old & mask != 0) && (new & mask == 0) +} + +fn timer_handler(timer_cell: &BqlRefCell) { + timer_cell.borrow_mut().callback() +} + +/// HPET Timer Abstraction +#[repr(C)] +#[derive(Debug, Default)] +#[cfg_attr(has_offset_of, derive(qemu_api_macros::offsets))] +pub struct HPETTimer { + /// timer N index within the timer block (`HPETState`) + #[doc(alias = "tn")] + index: usize, + qemu_timer: Option>, + /// timer block abstraction containing this timer + state: Option>, + + // Memory-mapped, software visible timer registers + /// Timer N Configuration and Capability Register + config: u64, + /// Timer N Comparator Value Register + cmp: u64, + /// Timer N FSB Interrupt Route Register + fsb: u64, + + // Hidden register state + /// comparator (extended to counter width) + cmp64: u64, + /// Last value written to comparator + period: u64, + /// timer pop will indicate wrap for one-shot 32-bit + /// mode. Next pop will be actual timer expiration. + wrap_flag: u8, + /// last value armed, to avoid timer storms + last: u64, +} + +impl HPETTimer { + fn init(&mut self, index: usize, state_ptr: *mut HPETState) -> &mut Self { + *self = HPETTimer::default(); + self.index = index; + self.state = NonNull::new(state_ptr); + self + } + + fn init_timer_with_state(&mut self) { + self.qemu_timer = Some(Box::new({ + let mut t = Timer::new(); + t.init_full( + None, + CLOCK_VIRTUAL, + Timer::NS, + 0, + timer_handler, + &self.get_state().timers[self.index], + ); + t + })); + } + + fn get_state(&self) -> &HPETState { + // SAFETY: + // the pointer is convertible to a reference + unsafe { self.state.unwrap().as_ref() } + } + + fn is_int_active(&self) -> bool { + self.get_state().is_timer_int_active(self.index) + } + + const fn is_fsb_route_enabled(&self) -> bool { + self.config & (1 << HPET_TN_CFG_FSB_ENABLE_SHIFT) != 0 + } + + const fn is_periodic(&self) -> bool { + self.config & (1 << HPET_TN_CFG_PERIODIC_SHIFT) != 0 + } + + const fn is_int_enabled(&self) -> bool { + self.config & (1 << HPET_TN_CFG_INT_ENABLE_SHIFT) != 0 + } + + const fn is_32bit_mod(&self) -> bool { + self.config & (1 << HPET_TN_CFG_32BIT_SHIFT) != 0 + } + + const fn is_valset_enabled(&self) -> bool { + self.config & (1 << HPET_TN_CFG_SETVAL_SHIFT) != 0 + } + + fn clear_valset(&mut self) { + self.config &= !(1 << HPET_TN_CFG_SETVAL_SHIFT); + } + + /// True if timer interrupt is level triggered; otherwise, edge triggered. + const fn is_int_level_triggered(&self) -> bool { + self.config & (1 << HPET_TN_CFG_INT_TYPE_SHIFT) != 0 + } + + /// calculate next value of the general counter that matches the + /// target (either entirely, or the low 32-bit only depending on + /// the timer mode). + fn calculate_cmp64(&self, cur_tick: u64, target: u64) -> u64 { + if self.is_32bit_mod() { + let mut result: u64 = cur_tick.deposit(0, 32, target); + if result < cur_tick { + result += 0x100000000; + } + result + } else { + target + } + } + + const fn get_individual_route(&self) -> usize { + ((self.config & HPET_TN_CFG_INT_ROUTE_MASK) >> HPET_TN_CFG_INT_ROUTE_SHIFT) as usize + } + + fn get_int_route(&self) -> usize { + if self.index <= 1 && self.get_state().is_legacy_mode() { + // If LegacyReplacement Route bit is set, HPET specification requires + // timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, + // timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. + // + // If the LegacyReplacement Route bit is set, the individual routing + // bits for timers 0 and 1 (APIC or FSB) will have no impact. + // + // FIXME: Consider I/O APIC case. + if self.index == 0 { + 0 + } else { + RTC_ISA_IRQ + } + } else { + // (If the LegacyReplacement Route bit is set) Timer 2-n will be + // routed as per the routing in the timer n config registers. + // ... + // If the LegacyReplacement Route bit is not set, the individual + // routing bits for each of the timers are used. + self.get_individual_route() + } + } + + fn set_irq(&mut self, set: bool) { + let route = self.get_int_route(); + + if set && self.is_int_enabled() && self.get_state().is_hpet_enabled() { + if self.is_fsb_route_enabled() { + // SAFETY: + // the parameters are valid. + unsafe { + address_space_stl_le( + addr_of_mut!(address_space_memory), + self.fsb >> 32, // Timer N FSB int addr + self.fsb as u32, // Timer N FSB int value, truncate! + MEMTXATTRS_UNSPECIFIED, + null_mut(), + ); + } + } else if self.is_int_level_triggered() { + self.get_state().irqs[route].raise(); + } else { + self.get_state().irqs[route].pulse(); + } + } else if !self.is_fsb_route_enabled() { + self.get_state().irqs[route].lower(); + } + } + + fn update_irq(&mut self, set: bool) { + // If Timer N Interrupt Enable bit is 0, "the timer will + // still operate and generate appropriate status bits, but + // will not cause an interrupt" + self.get_state() + .update_int_status(self.index as u32, set && self.is_int_level_triggered()); + self.set_irq(set); + } + + fn arm_timer(&mut self, tick: u64) { + let mut ns = self.get_state().get_ns(tick); + + // Clamp period to reasonable min value (1 us) + if self.is_periodic() && ns - self.last < 1000 { + ns = self.last + 1000; + } + + self.last = ns; + self.qemu_timer.as_ref().unwrap().modify(self.last); + } + + fn set_timer(&mut self) { + let cur_tick: u64 = self.get_state().get_ticks(); + + self.wrap_flag = 0; + self.cmp64 = self.calculate_cmp64(cur_tick, self.cmp); + if self.is_32bit_mod() { + // HPET spec says in one-shot 32-bit mode, generate an interrupt when + // counter wraps in addition to an interrupt with comparator match. + if !self.is_periodic() && self.cmp64 > hpet_next_wrap(cur_tick) { + self.wrap_flag = 1; + self.arm_timer(hpet_next_wrap(cur_tick)); + return; + } + } + self.arm_timer(self.cmp64); + } + + fn del_timer(&mut self) { + // Just remove the timer from the timer_list without destroying + // this timer instance. + self.qemu_timer.as_ref().unwrap().delete(); + + if self.is_int_active() { + // For level-triggered interrupt, this leaves interrupt status + // register set but lowers irq. + self.update_irq(true); + } + } + + /// Configuration and Capability Register + fn set_tn_cfg_reg(&mut self, shift: u32, len: u32, val: u64) { + // TODO: Add trace point - trace_hpet_ram_write_tn_cfg(addr & 4) + let old_val: u64 = self.config; + let mut new_val: u64 = old_val.deposit(shift, len, val); + new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + + // Switch level-type interrupt to edge-type. + if deactivating_bit(old_val, new_val, HPET_TN_CFG_INT_TYPE_SHIFT) { + // Do this before changing timer.config; otherwise, if + // HPET_TN_FSB is set, update_irq will not lower the qemu_irq. + self.update_irq(false); + } + + self.config = new_val; + + if activating_bit(old_val, new_val, HPET_TN_CFG_INT_ENABLE_SHIFT) && self.is_int_active() { + self.update_irq(true); + } + + if self.is_32bit_mod() { + self.cmp = u64::from(self.cmp as u32); // truncate! + self.period = u64::from(self.period as u32); // truncate! + } + + if self.get_state().is_hpet_enabled() { + self.set_timer(); + } + } + + /// Comparator Value Register + fn set_tn_cmp_reg(&mut self, shift: u32, len: u32, val: u64) { + let mut length = len; + let mut value = val; + + // TODO: Add trace point - trace_hpet_ram_write_tn_cmp(addr & 4) + if self.is_32bit_mod() { + // High 32-bits are zero, leave them untouched. + if shift != 0 { + // TODO: Add trace point - trace_hpet_ram_write_invalid_tn_cmp() + return; + } + length = 64; + value = u64::from(value as u32); // truncate! + } + + if !self.is_periodic() || self.is_valset_enabled() { + self.cmp = self.cmp.deposit(shift, length, value); + } + + if self.is_periodic() { + self.period = self.period.deposit(shift, length, value); + } + + self.clear_valset(); + if self.get_state().is_hpet_enabled() { + self.set_timer(); + } + } + + /// FSB Interrupt Route Register + fn set_tn_fsb_route_reg(&mut self, shift: u32, len: u32, val: u64) { + self.fsb = self.fsb.deposit(shift, len, val); + } + + fn reset(&mut self) { + self.del_timer(); + self.cmp = u64::MAX; // Comparator Match Registers reset to all 1's. + self.config = (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << HPET_TN_CFG_SIZE_CAP_SHIFT); + if self.get_state().has_msi_flag() { + self.config |= 1 << HPET_TN_CFG_FSB_CAP_SHIFT; + } + // advertise availability of ioapic int + self.config |= + (u64::from(self.get_state().int_route_cap)) << HPET_TN_CFG_INT_ROUTE_CAP_SHIFT; + self.period = 0; + self.wrap_flag = 0; + } + + /// timer expiration callback + fn callback(&mut self) { + let period: u64 = self.period; + let cur_tick: u64 = self.get_state().get_ticks(); + + if self.is_periodic() && period != 0 { + while hpet_time_after(cur_tick, self.cmp64) { + self.cmp64 += period; + } + if self.is_32bit_mod() { + self.cmp = u64::from(self.cmp64 as u32); // truncate! + } else { + self.cmp = self.cmp64; + } + self.arm_timer(self.cmp64); + } else if self.wrap_flag != 0 { + self.wrap_flag = 0; + self.arm_timer(self.cmp64); + } + self.update_irq(true); + } +} + +/// HPET Event Timer Block Abstraction +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +pub struct HPETState { + parent_obj: ParentField, + iomem: MemoryRegion, + + // HPET block Registers: Memory-mapped, software visible registers + /// General Capabilities and ID Register + capability: BqlCell, + /// General Configuration Register + config: BqlCell, + /// General Interrupt Status Register + #[doc(alias = "isr")] + int_status: BqlCell, + /// Main Counter Value Register + #[doc(alias = "hpet_counter")] + counter: BqlCell, + + // Internal state + /// Capabilities that QEMU HPET supports. + /// bit 0: MSI (or FSB) support. + flags: u32, + + /// Offset of main counter relative to qemu clock. + hpet_offset: BqlCell, + hpet_offset_saved: bool, + + irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES], + rtc_irq_level: BqlCell, + pit_enabled: InterruptSource, + + /// Interrupt Routing Capability. + /// This field indicates to which interrupts in the I/O (x) APIC + /// the timers' interrupt can be routed, and is encoded in the + /// bits 32:64 of timer N's config register: + #[doc(alias = "intcap")] + int_route_cap: u32, + + /// HPET timer array managed by this timer block. + #[doc(alias = "timer")] + timers: [BqlRefCell; HPET_MAX_TIMERS], + num_timers: BqlCell, + + /// Instance id (HPET timer block ID). + hpet_id: BqlCell, +} + +impl HPETState { + const fn has_msi_flag(&self) -> bool { + self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0 + } + + fn is_legacy_mode(&self) -> bool { + self.config.get() & (1 << HPET_CFG_LEG_RT_SHIFT) != 0 + } + + fn is_hpet_enabled(&self) -> bool { + self.config.get() & (1 << HPET_CFG_ENABLE_SHIFT) != 0 + } + + fn is_timer_int_active(&self, index: usize) -> bool { + self.int_status.get() & (1 << index) != 0 + } + + fn get_ticks(&self) -> u64 { + ns_to_ticks(CLOCK_VIRTUAL.get_ns() + self.hpet_offset.get()) + } + + fn get_ns(&self, tick: u64) -> u64 { + ticks_to_ns(tick) - self.hpet_offset.get() + } + + fn handle_legacy_irq(&self, irq: u32, level: u32) { + if irq == HPET_LEGACY_PIT_INT { + if !self.is_legacy_mode() { + self.irqs[0].set(level != 0); + } + } else { + self.rtc_irq_level.set(level); + if !self.is_legacy_mode() { + self.irqs[RTC_ISA_IRQ].set(level != 0); + } + } + } + + fn init_timer(&self) { + let raw_ptr: *mut HPETState = self as *const HPETState as *mut HPETState; + + for (index, timer) in self.timers.iter().enumerate() { + timer + .borrow_mut() + .init(index, raw_ptr) + .init_timer_with_state(); + } + } + + fn update_int_status(&self, index: u32, level: bool) { + self.int_status + .set(self.int_status.get().deposit(index, 1, u64::from(level))); + } + + /// General Configuration Register + fn set_cfg_reg(&self, shift: u32, len: u32, val: u64) { + let old_val = self.config.get(); + let mut new_val = old_val.deposit(shift, len, val); + + new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + self.config.set(new_val); + + if activating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { + // Enable main counter and interrupt generation. + self.hpet_offset + .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); + + for timer in self.timers.iter().take(self.num_timers.get()) { + let mut t = timer.borrow_mut(); + + if t.is_int_enabled() && t.is_int_active() { + t.update_irq(true); + } + t.set_timer(); + } + } else if deactivating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { + // Halt main counter and disable interrupt generation. + self.counter.set(self.get_ticks()); + + for timer in self.timers.iter().take(self.num_timers.get()) { + timer.borrow_mut().del_timer(); + } + } + + // i8254 and RTC output pins are disabled when HPET is in legacy mode + if activating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { + self.pit_enabled.set(false); + self.irqs[0].lower(); + self.irqs[RTC_ISA_IRQ].lower(); + } else if deactivating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { + // TODO: Add irq binding: qemu_irq_lower(s->irqs[0]) + self.irqs[0].lower(); + self.pit_enabled.set(true); + self.irqs[RTC_ISA_IRQ].set(self.rtc_irq_level.get() != 0); + } + } + + /// General Interrupt Status Register: Read/Write Clear + fn set_int_status_reg(&self, shift: u32, _len: u32, val: u64) { + let new_val = val << shift; + let cleared = new_val & self.int_status.get(); + + for (index, timer) in self.timers.iter().take(self.num_timers.get()).enumerate() { + if cleared & (1 << index) != 0 { + timer.borrow_mut().update_irq(false); + } + } + } + + /// Main Counter Value Register + fn set_counter_reg(&self, shift: u32, len: u32, val: u64) { + if self.is_hpet_enabled() { + // TODO: Add trace point - + // trace_hpet_ram_write_counter_write_while_enabled() + // + // HPET spec says that writes to this register should only be + // done while the counter is halted. So this is an undefined + // behavior. There's no need to forbid it, but when HPET is + // enabled, the changed counter value will not affect the + // tick count (i.e., the previously calculated offset will + // not be changed as well). + } + self.counter + .set(self.counter.get().deposit(shift, len, val)); + } +} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index 44e9f3fb8a..d6ac0b2521 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -8,3 +8,4 @@ //! Precision Event Timers) device in QEMU. pub mod fw_cfg; +pub mod hpet; diff --git a/rust/wrapper.h b/rust/wrapper.h index a35bfbd176..d927ad6799 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -64,3 +64,4 @@ typedef enum memory_order { #include "chardev/char-serial.h" #include "exec/memattrs.h" #include "qemu/timer.h" +#include "exec/address-spaces.h" From 6e90a8f8136f65273fe7320904e06b27a8a40eda Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:50 +0800 Subject: [PATCH 1699/2892] rust/timer/hpet: add qom and qdev APIs support Implement QOM & QAPI support for HPET device. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-10-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/fw_cfg.rs | 2 - rust/hw/timer/hpet/src/hpet.rs | 278 ++++++++++++++++++++++++++++++- rust/hw/timer/hpet/src/lib.rs | 4 + 3 files changed, 273 insertions(+), 11 deletions(-) diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index 849e277d48..bef03727ea 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -2,8 +2,6 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -#![allow(dead_code)] - use std::ptr::addr_of_mut; use qemu_api::{cell::bql_locked, impl_zeroable, zeroable::Zeroable}; diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 795610f8e8..75ff5b3e8d 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -2,21 +2,33 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -#![allow(dead_code)] - -use std::ptr::{addr_of_mut, null_mut, NonNull}; +use std::{ + ffi::CStr, + ptr::{addr_of_mut, null_mut, NonNull}, + slice::from_ref, +}; use qemu_api::{ - bindings::{address_space_memory, address_space_stl_le}, + bindings::{ + address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, + qdev_prop_uint32, qdev_prop_uint8, + }, + c_str, cell::{BqlCell, BqlRefCell}, irq::InterruptSource, - memory::{MemoryRegion, MEMTXATTRS_UNSPECIFIED}, + memory::{ + hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, + }, prelude::*, - qom::ParentField, + qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, + qom::{ObjectImpl, ObjectType, ParentField}, + qom_isa, sysbus::SysBusDevice, timer::{Timer, CLOCK_VIRTUAL}, }; +use crate::fw_cfg::HPETFwConfig; + /// Register space for each timer block (`HPET_BASE` is defined in hpet.h). const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes @@ -139,8 +151,7 @@ fn timer_handler(timer_cell: &BqlRefCell) { /// HPET Timer Abstraction #[repr(C)] -#[derive(Debug, Default)] -#[cfg_attr(has_offset_of, derive(qemu_api_macros::offsets))] +#[derive(Debug, Default, qemu_api_macros::offsets)] pub struct HPETTimer { /// timer N index within the timer block (`HPETState`) #[doc(alias = "tn")] @@ -451,11 +462,41 @@ impl HPETTimer { } self.update_irq(true); } + + const fn read(&self, addr: hwaddr, _size: u32) -> u64 { + let shift: u64 = (addr & 4) * 8; + + match addr & !4 { + HPET_TN_CFG_REG => self.config >> shift, // including interrupt capabilities + HPET_TN_CMP_REG => self.cmp >> shift, // comparator register + HPET_TN_FSB_ROUTE_REG => self.fsb >> shift, + _ => { + // TODO: Add trace point - trace_hpet_ram_read_invalid() + // Reserved. + 0 + } + } + } + + fn write(&mut self, addr: hwaddr, value: u64, size: u32) { + let shift = ((addr & 4) * 8) as u32; + let len = std::cmp::min(size * 8, 64 - shift); + + match addr & !4 { + HPET_TN_CFG_REG => self.set_tn_cfg_reg(shift, len, value), + HPET_TN_CMP_REG => self.set_tn_cmp_reg(shift, len, value), + HPET_TN_FSB_ROUTE_REG => self.set_tn_fsb_route_reg(shift, len, value), + _ => { + // TODO: Add trace point - trace_hpet_ram_write_invalid() + // Reserved. + } + } + } } /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qemu_api_macros::offsets)] +#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)] pub struct HPETState { parent_obj: ParentField, iomem: MemoryRegion, @@ -626,4 +667,223 @@ impl HPETState { self.counter .set(self.counter.get().deposit(shift, len, val)); } + + unsafe fn init(&mut self) { + static HPET_RAM_OPS: MemoryRegionOps = + MemoryRegionOpsBuilder::::new() + .read(&HPETState::read) + .write(&HPETState::write) + .native_endian() + .valid_sizes(4, 8) + .impl_sizes(4, 8) + .build(); + + // SAFETY: + // self and self.iomem are guaranteed to be valid at this point since callers + // must make sure the `self` reference is valid. + MemoryRegion::init_io( + unsafe { &mut *addr_of_mut!(self.iomem) }, + addr_of_mut!(*self), + &HPET_RAM_OPS, + "hpet", + HPET_REG_SPACE_LEN, + ); + } + + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.irqs.iter() { + self.init_irq(irq); + } + } + + fn realize(&self) { + if self.int_route_cap == 0 { + // TODO: Add error binding: warn_report() + println!("Hpet's hpet-intcap property not initialized"); + } + + self.hpet_id.set(HPETFwConfig::assign_hpet_id()); + + if self.num_timers.get() < HPET_MIN_TIMERS { + self.num_timers.set(HPET_MIN_TIMERS); + } else if self.num_timers.get() > HPET_MAX_TIMERS { + self.num_timers.set(HPET_MAX_TIMERS); + } + + self.init_timer(); + // 64-bit General Capabilities and ID Register; LegacyReplacementRoute. + self.capability.set( + HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT | + 1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT | + 1 << HPET_CAP_LEG_RT_CAP_SHIFT | + HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT | + ((self.num_timers.get() - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer + (HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns + ); + + self.init_gpio_in(2, HPETState::handle_legacy_irq); + self.init_gpio_out(from_ref(&self.pit_enabled)); + } + + fn reset_hold(&self, _type: ResetType) { + let sbd = self.upcast::(); + + for timer in self.timers.iter().take(self.num_timers.get()) { + timer.borrow_mut().reset(); + } + + self.counter.set(0); + self.config.set(0); + self.pit_enabled.set(true); + self.hpet_offset.set(0); + + HPETFwConfig::update_hpet_cfg( + self.hpet_id.get(), + self.capability.get() as u32, + sbd.mmio[0].addr, + ); + + // to document that the RTC lowers its output on reset as well + self.rtc_irq_level.set(0); + } + + fn timer_and_addr(&self, addr: hwaddr) -> Option<(&BqlRefCell, hwaddr)> { + let timer_id: usize = ((addr - 0x100) / 0x20) as usize; + + // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) + if timer_id > self.num_timers.get() { + // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) + None + } else { + // Keep the complete address so that HPETTimer's read and write could + // detect the invalid access. + Some((&self.timers[timer_id], addr & 0x1F)) + } + } + + fn read(&self, addr: hwaddr, size: u32) -> u64 { + let shift: u64 = (addr & 4) * 8; + + // address range of all TN regs + // TODO: Add trace point - trace_hpet_ram_read(addr) + if (0x100..=0x3ff).contains(&addr) { + match self.timer_and_addr(addr) { + None => 0, // Reserved, + Some((timer, tn_addr)) => timer.borrow_mut().read(tn_addr, size), + } + } else { + match addr & !4 { + HPET_CAP_REG => self.capability.get() >> shift, /* including HPET_PERIOD 0x004 */ + // (CNT_CLK_PERIOD field) + HPET_CFG_REG => self.config.get() >> shift, + HPET_COUNTER_REG => { + let cur_tick: u64 = if self.is_hpet_enabled() { + self.get_ticks() + } else { + self.counter.get() + }; + + // TODO: Add trace point - trace_hpet_ram_read_reading_counter(addr & 4, + // cur_tick) + cur_tick >> shift + } + HPET_INT_STATUS_REG => self.int_status.get() >> shift, + _ => { + // TODO: Add trace point- trace_hpet_ram_read_invalid() + // Reserved. + 0 + } + } + } + } + + fn write(&self, addr: hwaddr, value: u64, size: u32) { + let shift = ((addr & 4) * 8) as u32; + let len = std::cmp::min(size * 8, 64 - shift); + + // TODO: Add trace point - trace_hpet_ram_write(addr, value) + if (0x100..=0x3ff).contains(&addr) { + match self.timer_and_addr(addr) { + None => (), // Reserved. + Some((timer, tn_addr)) => timer.borrow_mut().write(tn_addr, value, size), + } + } else { + match addr & !0x4 { + HPET_CAP_REG => {} // General Capabilities and ID Register: Read Only + HPET_CFG_REG => self.set_cfg_reg(shift, len, value), + HPET_INT_STATUS_REG => self.set_int_status_reg(shift, len, value), + HPET_COUNTER_REG => self.set_counter_reg(shift, len, value), + _ => { + // TODO: Add trace point - trace_hpet_ram_write_invalid() + // Reserved. + } + } + } + } +} + +qom_isa!(HPETState: SysBusDevice, DeviceState, Object); + +unsafe impl ObjectType for HPETState { + // No need for HPETClass. Just like OBJECT_DECLARE_SIMPLE_TYPE in C. + type Class = ::Class; + const TYPE_NAME: &'static CStr = crate::TYPE_HPET; +} + +impl ObjectImpl for HPETState { + type ParentType = SysBusDevice; + + const INSTANCE_INIT: Option = Some(Self::init); + const INSTANCE_POST_INIT: Option = Some(Self::post_init); +} + +// TODO: Make these properties user-configurable! +qemu_api::declare_properties! { + HPET_PROPERTIES, + qemu_api::define_property!( + c_str!("timers"), + HPETState, + num_timers, + unsafe { &qdev_prop_uint8 }, + u8, + default = HPET_MIN_TIMERS + ), + qemu_api::define_property!( + c_str!("msi"), + HPETState, + flags, + unsafe { &qdev_prop_bit }, + u32, + bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, + default = false, + ), + qemu_api::define_property!( + c_str!("hpet-intcap"), + HPETState, + int_route_cap, + unsafe { &qdev_prop_uint32 }, + u32, + default = 0 + ), + qemu_api::define_property!( + c_str!("hpet-offset-saved"), + HPETState, + hpet_offset_saved, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} + +impl DeviceImpl for HPETState { + fn properties() -> &'static [Property] { + &HPET_PROPERTIES + } + + const REALIZE: Option = Some(Self::realize); +} + +impl ResettablePhasesImpl for HPETState { + const HOLD: Option = Some(Self::reset_hold); } diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index d6ac0b2521..5e7c961c28 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -7,5 +7,9 @@ //! This library implements a device model for the IA-PC HPET (High //! Precision Event Timers) device in QEMU. +use qemu_api::c_str; + pub mod fw_cfg; pub mod hpet; + +pub const TYPE_HPET: &::std::ffi::CStr = c_str!("hpet"); From d128c341a744ba3e92fa67d9f1b02dd9a7bd68b9 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 10 Feb 2025 11:00:51 +0800 Subject: [PATCH 1700/2892] i386: enable rust hpet for pc when rust is enabled Add HPET configuration in PC's Kconfig options, and select HPET device (Rust version) if Rust is supported. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250210030051.2562726-11-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- configs/devices/i386-softmmu/default.mak | 1 + hw/i386/pc.c | 2 +- hw/timer/Kconfig | 2 +- rust/hw/Kconfig | 1 + rust/hw/timer/Kconfig | 2 ++ tests/qtest/meson.build | 3 ++- 6 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 rust/hw/timer/Kconfig diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index 4faf2f0315..9ef343cace 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -6,6 +6,7 @@ #CONFIG_APPLESMC=n #CONFIG_FDC=n #CONFIG_HPET=n +#CONFIG_X_HPET_RUST=n #CONFIG_HYPERV=n #CONFIG_ISA_DEBUG=n #CONFIG_ISA_IPMI_BT=n diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 0eb52d315b..22641e6ddc 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1701,7 +1701,7 @@ static void pc_machine_initfn(Object *obj) pcms->sata_enabled = true; pcms->i8042_enabled = true; pcms->max_fw_size = 8 * MiB; -#ifdef CONFIG_HPET +#if defined(CONFIG_HPET) || defined(CONFIG_X_HPET_RUST) pcms->hpet_enabled = true; #endif pcms->fd_bootchk = true; diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index c96fd5d97a..9ac0084534 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -11,7 +11,7 @@ config A9_GTIMER config HPET bool - default y if PC + default y if PC && !HAVE_RUST config I8254 bool diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig index 4d934f30af..36f92ec028 100644 --- a/rust/hw/Kconfig +++ b/rust/hw/Kconfig @@ -1,2 +1,3 @@ # devices Kconfig source char/Kconfig +source timer/Kconfig diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig new file mode 100644 index 0000000000..afd9803350 --- /dev/null +++ b/rust/hw/timer/Kconfig @@ -0,0 +1,2 @@ +config X_HPET_RUST + bool diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 68316dbdc1..8a6243382a 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -103,7 +103,8 @@ qtests_i386 = \ config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \ slirp.found() ? ['virtio-net-failover'] : []) + \ (unpack_edk2_blobs and \ - config_all_devices.has_key('CONFIG_HPET') and \ + (config_all_devices.has_key('CONFIG_HPET') or \ + config_all_devices.has_key('CONFIG_X_HPET_RUST')) and \ config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \ qtests_pci + \ qtests_cxl + \ From ebacd14a6f97b6235e078d9a9ac8a342a3be7c96 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 30 Jan 2025 11:11:18 +0100 Subject: [PATCH 1701/2892] rust: qemu_api: add a documentation header for all modules Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/assertions.rs | 4 ++++ rust/qemu-api/src/bindings.rs | 2 ++ rust/qemu-api/src/c_str.rs | 8 ++++++++ rust/qemu-api/src/offset_of.rs | 7 +++++++ rust/qemu-api/src/prelude.rs | 2 ++ rust/qemu-api/src/sysbus.rs | 2 ++ rust/qemu-api/src/zeroable.rs | 2 ++ 7 files changed, 27 insertions(+) diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index 6e42046980..fa1a18de6f 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -2,9 +2,13 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later +#![doc(hidden)] //! This module provides macros to check the equality of types and //! the type of `struct` fields. This can be useful to ensure that //! types match the expectations of C code. +//! +//! Documentation is hidden because it only exposes macros, which +//! are exported directly from `qemu_api`. // Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 // (stackoverflow answers are released under MIT license). diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index b71220113e..d2868639ff 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -15,6 +15,8 @@ clippy::missing_safety_doc )] +//! `bindgen`-generated declarations. + #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/qemu-api/src/c_str.rs b/rust/qemu-api/src/c_str.rs index 4cd96da0b4..3fa61b59c7 100644 --- a/rust/qemu-api/src/c_str.rs +++ b/rust/qemu-api/src/c_str.rs @@ -2,6 +2,14 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later +#![doc(hidden)] +//! This module provides a macro to define a constant of type +//! [`CStr`](std::ffi::CStr), for compatibility with versions of +//! Rust that lack `c""` literals. +//! +//! Documentation is hidden because it only exposes macros, which +//! are exported directly from `qemu_api`. + #[macro_export] /// Given a string constant _without_ embedded or trailing NULs, return /// a `CStr`. diff --git a/rust/qemu-api/src/offset_of.rs b/rust/qemu-api/src/offset_of.rs index 075e98f986..373229bbde 100644 --- a/rust/qemu-api/src/offset_of.rs +++ b/rust/qemu-api/src/offset_of.rs @@ -1,5 +1,12 @@ // SPDX-License-Identifier: MIT +#![doc(hidden)] +//! This module provides macros that emulate the functionality of +//! `core::mem::offset_of` on older versions of Rust. +//! +//! Documentation is hidden because it only exposes macros, which +//! are exported directly from `qemu_api`. + /// This macro provides the same functionality as `core::mem::offset_of`, /// except that only one level of field access is supported. The declaration /// of the struct must be wrapped with `with_offsets! { }`. diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 254edb476d..fbf0ee23e0 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -2,6 +2,8 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later +//! Commonly used traits and types for QEMU. + pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 1f66a5f1e0..fa36e12178 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -2,6 +2,8 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later +//! Bindings to access `sysbus` functionality from Rust. + use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index cd424e6ea0..a2356cb2f2 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +//! Defines a trait for structs that can be safely initialized with zero bytes. + /// Encapsulates the requirement that /// `MaybeUninit::::zeroed().assume_init()` does not cause undefined /// behavior. This trait in principle could be implemented as just: From ee7d3aec54a32ce53c9b5ca86c75c945a877db19 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 30 Jan 2025 10:55:16 +0100 Subject: [PATCH 1702/2892] rust: vmstate: remove redundant link targets Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 164effc655..c6dfb60935 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -191,10 +191,9 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * scalar types (integer and `bool`) /// * the C struct `QEMUTimer` /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, -/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) +/// [`BqlCell`], [`BqlRefCell`] /// * a raw pointer to any of the above -/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of -/// the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented From 16534af51bc0e9c3db94097ab37ebd3ed50e1c0f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Feb 2025 13:55:53 +0100 Subject: [PATCH 1703/2892] rust: fix doctests Doctests were not being run by CI, and have broken. Fix them. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 6 ++++++ rust/qemu-api/src/vmstate.rs | 2 +- rust/qemu-api/src/zeroable.rs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 4265a57783..00f4bfcd9f 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -131,6 +131,12 @@ build-system-fedora-rust-nightly: CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints TARGETS: aarch64-softmmu MAKE_CHECK_ARGS: check-build + after_script: + - source scripts/ci/gitlab-ci-section + - section_start test "Running Rust doctests" + - cd build + - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api + allow_failure: true check-system-fedora: diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index c6dfb60935..24a4dc81e7 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -294,7 +294,7 @@ impl VMStateField { /// # Examples /// /// ``` -/// # use qemu_api::vmstate::impl_vmstate_forward; +/// # use qemu_api::impl_vmstate_forward; /// pub struct Fifo([u8; 16]); /// impl_vmstate_forward!(Fifo); /// ``` diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index a2356cb2f2..47b6977828 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -7,7 +7,7 @@ /// behavior. This trait in principle could be implemented as just: /// /// ``` -/// pub unsafe trait Zeroable { +/// pub unsafe trait Zeroable: Default { /// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; /// } /// ``` From 4dafba778aa3e5f5fd3b2c6333afd7650dcf54e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Tue, 31 Dec 2024 12:59:50 +0100 Subject: [PATCH 1704/2892] ui/sdl2: reenable the SDL2 Windows keyboard hook procedure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows only: The libSDL2 Windows message loop needs the libSDL2 Windows low level keyboard hook procedure to grab the left and right Windows keys correctly. Reenable the SDL2 Windows keyboard hook procedure. Since SDL2 2.30.4 the SDL2 keyboard hook procedure also filters out the special left Control key event for every Alt Gr key event on keyboards with an international layout. This means the QEMU low level keyboard hook procedure is no longer needed. Remove the QEMU Windows keyboard hook procedure. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2139 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2323 Signed-off-by: Volker Rümelin Link: https://lore.kernel.org/r/20241231115950.6732-1-vr_qemu@t-online.de Signed-off-by: Paolo Bonzini --- ui/meson.build | 4 ---- ui/sdl2.c | 26 -------------------------- 2 files changed, 30 deletions(-) diff --git a/ui/meson.build b/ui/meson.build index 28c7381dd1..35fb04cadf 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -120,10 +120,6 @@ if gtk.found() endif if sdl.found() - if host_os == 'windows' - system_ss.add(files('win32-kbd-hook.c')) - endif - sdl_ss = ss.source_set() sdl_ss.add(sdl, sdl_image, pixman, glib, files( 'sdl2-2d.c', diff --git a/ui/sdl2.c b/ui/sdl2.c index 445eb1dd9f..cda4293a53 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -32,7 +32,6 @@ #include "system/runstate.h" #include "system/runstate-action.h" #include "system/system.h" -#include "ui/win32-kbd-hook.h" #include "qemu/log.h" #include "qemu-main.h" @@ -263,7 +262,6 @@ static void sdl_grab_start(struct sdl2_console *scon) } SDL_SetWindowGrab(scon->real_window, SDL_TRUE); gui_grab = 1; - win32_kbd_set_grab(true); sdl_update_caption(scon); } @@ -271,7 +269,6 @@ static void sdl_grab_end(struct sdl2_console *scon) { SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; - win32_kbd_set_grab(false); sdl_show_cursor(scon); sdl_update_caption(scon); } @@ -372,19 +369,6 @@ static int get_mod_state(void) } } -static void *sdl2_win32_get_hwnd(struct sdl2_console *scon) -{ -#ifdef CONFIG_WIN32 - SDL_SysWMinfo info; - - SDL_VERSION(&info.version); - if (SDL_GetWindowWMInfo(scon->real_window, &info)) { - return info.info.win.window; - } -#endif - return NULL; -} - static void handle_keydown(SDL_Event *ev) { int win; @@ -609,10 +593,6 @@ static void handle_windowevent(SDL_Event *ev) sdl2_redraw(scon); break; case SDL_WINDOWEVENT_FOCUS_GAINED: - win32_kbd_set_grab(gui_grab); - if (qemu_console_is_graphic(scon->dcl.con)) { - win32_kbd_set_window(sdl2_win32_get_hwnd(scon)); - } /* fall through */ case SDL_WINDOWEVENT_ENTER: if (!gui_grab && (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { @@ -628,9 +608,6 @@ static void handle_windowevent(SDL_Event *ev) scon->ignore_hotkeys = get_mod_state(); break; case SDL_WINDOWEVENT_FOCUS_LOST: - if (qemu_console_is_graphic(scon->dcl.con)) { - win32_kbd_set_window(NULL); - } if (gui_grab && !gui_fullscreen) { sdl_grab_end(scon); } @@ -870,10 +847,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* only available since SDL 2.0.8 */ SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); #endif -#ifndef CONFIG_WIN32 - /* QEMU uses its own low level keyboard hook procedure on Windows */ SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); -#endif #ifdef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); #endif From 9038ac0c5c4ce8f6cf49e1146a79b633dc534b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 Jan 2025 11:57:43 +0100 Subject: [PATCH 1705/2892] overall: Remove unnecessary g_strdup_printf() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace g_strdup_printf("%s", value) -> g_strdup(value) to avoid unnecessary string formatting. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- crypto/hash-afalg.c | 2 +- hw/ppc/spapr_caps.c | 2 +- plugins/loader.c | 2 +- target/i386/cpu.c | 2 +- trace/simple.c | 2 +- ui/console.c | 4 +--- ui/gtk.c | 3 +-- util/module.c | 2 +- 8 files changed, 8 insertions(+), 11 deletions(-) diff --git a/crypto/hash-afalg.c b/crypto/hash-afalg.c index 8c0ce5b520..bd3fe3b427 100644 --- a/crypto/hash-afalg.c +++ b/crypto/hash-afalg.c @@ -59,7 +59,7 @@ qcrypto_afalg_hash_format_name(QCryptoHashAlgo alg, if (is_hmac) { name = g_strdup_printf("hmac(%s)", alg_name); } else { - name = g_strdup_printf("%s", alg_name); + name = g_strdup(alg_name); } return name; diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 7edd138360..904bff87ce 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -1034,7 +1034,7 @@ void spapr_caps_add_properties(SpaprMachineClass *smc) for (i = 0; i < ARRAY_SIZE(capability_table); i++) { SpaprCapabilityInfo *cap = &capability_table[i]; g_autofree char *name = g_strdup_printf("cap-%s", cap->name); - g_autofree char *desc = g_strdup_printf("%s", cap->description); + g_autofree char *desc = g_strdup(cap->description); object_class_property_add(klass, name, cap->type, cap->get, cap->set, diff --git a/plugins/loader.c b/plugins/loader.c index ebc01da9c6..99686b5466 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -128,7 +128,7 @@ static int plugin_add(void *opaque, const char *name, const char *value, /* Will treat arg="argname" as "argname=on" */ fullarg = g_strdup_printf("%s=%s", value, "on"); } else { - fullarg = g_strdup_printf("%s", value); + fullarg = g_strdup(value); } warn_report("using 'arg=%s' is deprecated", value); error_printf("Please use '%s' directly\n", fullarg); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b5dd60d281..72ab147e85 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6166,7 +6166,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("%s [%s]", model_id, cc->model->note); } if (!desc) { - desc = g_strdup_printf("%s", model_id); + desc = g_strdup(model_id); } if (cc->model && cc->model->cpudef->deprecation_note) { diff --git a/trace/simple.c b/trace/simple.c index 18af590cf7..c0aba00cb7 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -366,7 +366,7 @@ void st_set_trace_file(const char *file) /* Type cast needed for Windows where getpid() returns an int. */ trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE "-" FMT_pid, (pid_t)getpid()); } else { - trace_file_name = g_strdup_printf("%s", file); + trace_file_name = g_strdup(file); } st_set_trace_file_enabled(saved_enable); diff --git a/ui/console.c b/ui/console.c index 914ed2cc76..6456e8dd90 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1386,9 +1386,7 @@ char *qemu_console_get_label(QemuConsole *con) object_get_typename(c->device), c->head); } else { - return g_strdup_printf("%s", dev->id ? - dev->id : - object_get_typename(c->device)); + return g_strdup(dev->id ? : object_get_typename(c->device)); } } return g_strdup("VGA"); diff --git a/ui/gtk.c b/ui/gtk.c index c023743148..59bda83da6 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1944,8 +1944,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, vcd->console = vc; snprintf(buffer, sizeof(buffer), "vc%d", idx); - vc->label = g_strdup_printf("%s", vc->vte.chr->label - ? vc->vte.chr->label : buffer); + vc->label = g_strdup(vc->vte.chr->label ? : buffer); group = gd_vc_menu_init(s, vc, idx, group, view_menu); vc->vte.terminal = vte_terminal_new(); diff --git a/util/module.c b/util/module.c index 3eb0f06df1..1aa2079d01 100644 --- a/util/module.c +++ b/util/module.c @@ -234,7 +234,7 @@ int module_load(const char *prefix, const char *name, Error **errp) search_dir = getenv("QEMU_MODULE_DIR"); if (search_dir != NULL) { - dirs[n_dirs++] = g_strdup_printf("%s", search_dir); + dirs[n_dirs++] = g_strdup(search_dir); } dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); From c996dacfa1fd090910f8614c06df2a350b15211a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 19:24:24 +0100 Subject: [PATCH 1706/2892] qemu/timer: Clarify timer_new*() must be freed with timer_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was not mention QEMUTimer created with timer_new*() must be released with timer_free() instead of g_free(), because then active timers are removed from the active list. Update the documentation mentioning timer_free(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- include/qemu/timer.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index cc167bd825..abd2204f3b 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -507,6 +507,8 @@ static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, * with an AioContext---each of them runs its timer callbacks in its own * AioContext thread. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the timer */ static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, @@ -530,6 +532,8 @@ static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, * and associate it with the default timer list for the clock type @type. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the timer */ static inline QEMUTimer *timer_new(QEMUClockType type, int scale, @@ -548,6 +552,8 @@ static inline QEMUTimer *timer_new(QEMUClockType type, int scale, * associated with the clock. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the newly created timer */ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, @@ -566,6 +572,8 @@ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, * associated with the clock. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the newly created timer */ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, @@ -584,6 +592,8 @@ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, * associated with the clock. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the newly created timer */ static inline QEMUTimer *timer_new_ms(QEMUClockType type, QEMUTimerCB *cb, From 0f35d854d25f70b6cf37b3c122e11b83e4832516 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 7 Feb 2025 15:28:23 +0000 Subject: [PATCH 1707/2892] target/riscv: Fix minor whitespace issue in riscv_cpu_properties The mvendorid/mimpid/marchid properties have the wrong amount of whitespace ahead of them. Signed-off-by: Rob Bradford Reviewed-by: Daniel Henrique Barboza Signed-off-by: Michael Tokarev --- target/riscv/cpu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3d4bd157d2..cca24b9f1f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2844,9 +2844,9 @@ static const Property riscv_cpu_properties[] = { {.name = "cbop_blocksize", .info = &prop_cbop_blksize}, {.name = "cboz_blocksize", .info = &prop_cboz_blksize}, - {.name = "mvendorid", .info = &prop_mvendorid}, - {.name = "mimpid", .info = &prop_mimpid}, - {.name = "marchid", .info = &prop_marchid}, + {.name = "mvendorid", .info = &prop_mvendorid}, + {.name = "mimpid", .info = &prop_mimpid}, + {.name = "marchid", .info = &prop_marchid}, #ifndef CONFIG_USER_ONLY DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC), From b79b05d1a06a013447ea93b81c07612766b735f2 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 13 Feb 2025 17:53:20 -0600 Subject: [PATCH 1708/2892] make-release: don't rely on $CWD when excluding subproject directories The current logic scans qemu.git/subprojects/ from *.wrap files to determine whether or not to include the associated directories in the release tarballs. However, the script assumes that it is being run from the top-level of the source directory, which may not always be the case. In particular, when generating releases via, e.g.: make qemu-9.2.1.tar.xz the $CWD will either be an arbitrary external build directory, or qemu.git/build, and the exclusions will not be processed as expected. Fix this by using the $src parameter passed to the script as the root directory for the various subproject/ paths referenced by this logic. Also, the error case at the beginning of the subproject_dir() will not result in the error message being printed, and will instead produce an error message about "error" not being a valid command. Fix this by using basic shell commands. Fixes: be27b5149c86 ("make-release: only leave tarball of wrap-file subprojects") Cc: Paolo Bonzini Cc: Michael Tokarev Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth Signed-off-by: Michael Tokarev --- scripts/make-release | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/make-release b/scripts/make-release index 2885e87210..1b89b3423a 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -11,8 +11,9 @@ # See the COPYING file in the top-level directory. function subproject_dir() { - if test ! -f "subprojects/$1.wrap"; then - error "scripts/archive-source.sh should only process wrap subprojects" + if test ! -f "$src/subprojects/$1.wrap"; then + echo "scripts/archive-source.sh should only process wrap subprojects" + exit 1 fi # Print the directory key of the wrap file, defaulting to the @@ -26,7 +27,7 @@ function subproject_dir() { -e 's///p' \ -e 'q' \ -e '}' \ - "subprojects/$1.wrap") + "$src/subprojects/$1.wrap") echo "${dir:-$1}" } @@ -76,7 +77,7 @@ popd exclude=(--exclude=.git) # include the tarballs in subprojects/packagecache but not their expansion for sp in $SUBPROJECTS; do - if grep -xqF "[wrap-file]" subprojects/$sp.wrap; then + if grep -xqF "[wrap-file]" $src/subprojects/$sp.wrap; then exclude+=(--exclude=subprojects/"$(subproject_dir $sp)") fi done From ab1cb3683bd0462695a75aa9a8c1d07731caf304 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 5 Feb 2025 13:13:53 -0300 Subject: [PATCH 1709/2892] crypto: Allow gracefully ending the TLS session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU's TLS session code provides no way to call gnutls_bye() to terminate a TLS session. Callers of qcrypto_tls_session_read() can choose to ignore a GNUTLS_E_PREMATURE_TERMINATION error by setting the gracefulTermination argument. The QIOChannelTLS ignores the premature termination error whenever shutdown() has already been issued. This was found to be not enough for the migration code because shutdown() might not have been issued before the connection is terminated. Add support for calling gnutls_bye() in the tlssession layer so users of QIOChannelTLS can clearly identify the end of a TLS session. Reviewed-by: Daniel P. Berrangé Acked-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- crypto/tlssession.c | 41 +++++++++++++++++++++++++++++++++++++ include/crypto/tlssession.h | 22 ++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 77286e23f4..d769d7a304 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -585,6 +585,40 @@ qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) } } +int +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) +{ + int ret; + + if (!session->handshakeComplete) { + return 0; + } + + ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR); + + if (!ret) { + return QCRYPTO_TLS_BYE_COMPLETE; + } + + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { + int direction = gnutls_record_get_direction(session->handle); + return direction ? QCRYPTO_TLS_BYE_SENDING : QCRYPTO_TLS_BYE_RECVING; + } + + if (session->rerr || session->werr) { + error_setg(errp, "TLS termination failed: %s: %s", gnutls_strerror(ret), + error_get_pretty(session->rerr ? + session->rerr : session->werr)); + } else { + error_setg(errp, "TLS termination failed: %s", gnutls_strerror(ret)); + } + + error_free(session->rerr); + error_free(session->werr); + session->rerr = session->werr = NULL; + + return -1; +} int qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, @@ -699,6 +733,13 @@ qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) } +int +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) +{ + return QCRYPTO_TLS_BYE_COMPLETE; +} + + int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, Error **errp) diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index f694a5c3c5..c0f64ce989 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -323,6 +323,28 @@ typedef enum { QCryptoTLSSessionHandshakeStatus qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); +typedef enum { + QCRYPTO_TLS_BYE_COMPLETE, + QCRYPTO_TLS_BYE_SENDING, + QCRYPTO_TLS_BYE_RECVING, +} QCryptoTLSSessionByeStatus; + +/** + * qcrypto_tls_session_bye: + * @session: the TLS session object + * @errp: pointer to a NULL-initialized error object + * + * Start, or continue, a TLS termination sequence. If the underlying + * data channel is non-blocking, then this method may return control + * before the termination is complete. The return value will indicate + * whether the termination has completed, or is waiting to send or + * receive data. In the latter cases, the caller should setup an event + * loop watch and call this method again once the underlying data + * channel is ready to read or write again. + */ +int +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp); + /** * qcrypto_tls_session_get_key_size: * @sess: the TLS session object From 30ee88622edfa962154222b4a674361488ed823b Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 5 Feb 2025 13:15:00 -0300 Subject: [PATCH 1710/2892] io: tls: Add qio_channel_tls_bye MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a task dispatcher for gnutls_bye similar to the qio_channel_tls_handshake_task(). The gnutls_bye() call might be interrupted and so it needs to be rescheduled. The migration code will make use of this to help the migration destination identify a premature EOF. Once the session termination is in place, any EOF that happens before the source issued gnutls_bye() will be considered an error. Reviewed-by: Daniel P. Berrangé Acked-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- include/io/channel-tls.h | 12 ++++++ io/channel-tls.c | 84 ++++++++++++++++++++++++++++++++++++++++ io/trace-events | 5 +++ 3 files changed, 101 insertions(+) diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h index 26c67f17e2..7e9023570d 100644 --- a/include/io/channel-tls.h +++ b/include/io/channel-tls.h @@ -49,8 +49,20 @@ struct QIOChannelTLS { QCryptoTLSSession *session; QIOChannelShutdown shutdown; guint hs_ioc_tag; + guint bye_ioc_tag; }; +/** + * qio_channel_tls_bye: + * @ioc: the TLS channel object + * @errp: pointer to a NULL-initialized error object + * + * Perform the TLS session termination. This method will return + * immediately and the termination will continue in the background, + * provided the main loop is running. + */ +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp); + /** * qio_channel_tls_new_server: * @master: the underlying channel object diff --git a/io/channel-tls.c b/io/channel-tls.c index aab630e5ae..517ce190a4 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -247,6 +247,85 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, qio_channel_tls_handshake_task(ioc, task, context); } +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, + gpointer user_data); + +static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task, + GMainContext *context) +{ + GIOCondition condition; + QIOChannelTLSData *data; + int status; + Error *err = NULL; + + status = qcrypto_tls_session_bye(ioc->session, &err); + + if (status < 0) { + trace_qio_channel_tls_bye_fail(ioc); + qio_task_set_error(task, err); + qio_task_complete(task); + return; + } + + if (status == QCRYPTO_TLS_BYE_COMPLETE) { + qio_task_complete(task); + return; + } + + data = g_new0(typeof(*data), 1); + data->task = task; + data->context = context; + + if (context) { + g_main_context_ref(context); + } + + if (status == QCRYPTO_TLS_BYE_SENDING) { + condition = G_IO_OUT; + } else { + condition = G_IO_IN; + } + + trace_qio_channel_tls_bye_pending(ioc, status); + ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition, + qio_channel_tls_bye_io, + data, NULL, context); +} + + +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, + gpointer user_data) +{ + QIOChannelTLSData *data = user_data; + QIOTask *task = data->task; + GMainContext *context = data->context; + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task)); + + tioc->bye_ioc_tag = 0; + g_free(data); + qio_channel_tls_bye_task(tioc, task, context); + + if (context) { + g_main_context_unref(context); + } + + return FALSE; +} + +static void propagate_error(QIOTask *task, gpointer opaque) +{ + qio_task_propagate_error(task, opaque); +} + +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp) +{ + QIOTask *task; + + task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL); + + trace_qio_channel_tls_bye_start(ioc); + qio_channel_tls_bye_task(ioc, task, NULL); +} static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) { @@ -379,6 +458,11 @@ static int qio_channel_tls_close(QIOChannel *ioc, g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); } + if (tioc->bye_ioc_tag) { + trace_qio_channel_tls_bye_cancel(ioc); + g_clear_handle_id(&tioc->bye_ioc_tag, g_source_remove); + } + return qio_channel_close(tioc->master, errp); } diff --git a/io/trace-events b/io/trace-events index d4c0f84a9a..dc3a63ba1f 100644 --- a/io/trace-events +++ b/io/trace-events @@ -44,6 +44,11 @@ qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" qio_channel_tls_handshake_cancel(void *ioc) "TLS handshake cancel ioc=%p" +qio_channel_tls_bye_start(void *ioc) "TLS termination start ioc=%p" +qio_channel_tls_bye_pending(void *ioc, int status) "TLS termination pending ioc=%p status=%d" +qio_channel_tls_bye_fail(void *ioc) "TLS termination fail ioc=%p" +qio_channel_tls_bye_complete(void *ioc) "TLS termination complete ioc=%p" +qio_channel_tls_bye_cancel(void *ioc) "TLS termination cancel ioc=%p" qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p" From 0b8a70d70f65fbcf3ad62c975a64a356779095a9 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 6 Feb 2025 15:34:08 -0300 Subject: [PATCH 1711/2892] crypto: Remove qcrypto_tls_session_get_handshake_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct way of calling qcrypto_tls_session_handshake() requires calling qcrypto_tls_session_get_handshake_status() right after it so there's no reason to have a separate method. Refactor qcrypto_tls_session_handshake() to inform the status in its own return value and alter the callers accordingly. No functional change. Suggested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Acked-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- crypto/tlssession.c | 57 ++++++++++------------------- include/crypto/tlssession.h | 32 ++++------------ io/channel-tls.c | 7 ++-- tests/unit/test-crypto-tlssession.c | 12 ++---- 4 files changed, 36 insertions(+), 72 deletions(-) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index d769d7a304..6d8f8df623 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -546,45 +546,35 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, Error **errp) { int ret = gnutls_handshake(session->handle); - if (ret == 0) { + if (!ret) { session->handshakeComplete = true; - } else { - if (ret == GNUTLS_E_INTERRUPTED || - ret == GNUTLS_E_AGAIN) { - ret = 1; - } else { - if (session->rerr || session->werr) { - error_setg(errp, "TLS handshake failed: %s: %s", - gnutls_strerror(ret), - error_get_pretty(session->rerr ? - session->rerr : session->werr)); - } else { - error_setg(errp, "TLS handshake failed: %s", - gnutls_strerror(ret)); - } - ret = -1; - } + return QCRYPTO_TLS_HANDSHAKE_COMPLETE; } + + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { + int direction = gnutls_record_get_direction(session->handle); + return direction ? QCRYPTO_TLS_HANDSHAKE_SENDING : + QCRYPTO_TLS_HANDSHAKE_RECVING; + } + + if (session->rerr || session->werr) { + error_setg(errp, "TLS handshake failed: %s: %s", + gnutls_strerror(ret), + error_get_pretty(session->rerr ? + session->rerr : session->werr)); + } else { + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); + } + error_free(session->rerr); error_free(session->werr); session->rerr = session->werr = NULL; - return ret; + return -1; } -QCryptoTLSSessionHandshakeStatus -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) -{ - if (session->handshakeComplete) { - return QCRYPTO_TLS_HANDSHAKE_COMPLETE; - } else if (gnutls_record_get_direction(session->handle) == 0) { - return QCRYPTO_TLS_HANDSHAKE_RECVING; - } else { - return QCRYPTO_TLS_HANDSHAKE_SENDING; - } -} - int qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) { @@ -726,13 +716,6 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *sess, } -QCryptoTLSSessionHandshakeStatus -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) -{ - return QCRYPTO_TLS_HANDSHAKE_COMPLETE; -} - - int qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) { diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index c0f64ce989..d77ae0d423 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -75,12 +75,14 @@ * GINT_TO_POINTER(fd)); * * while (1) { - * if (qcrypto_tls_session_handshake(sess, errp) < 0) { + * int ret = qcrypto_tls_session_handshake(sess, errp); + * + * if (ret < 0) { * qcrypto_tls_session_free(sess); * return -1; * } * - * switch(qcrypto_tls_session_get_handshake_status(sess)) { + * switch(ret) { * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { * qcrypto_tls_session_free(sess); @@ -170,7 +172,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) * * Validate the peer's credentials after a successful * TLS handshake. It is an error to call this before - * qcrypto_tls_session_get_handshake_status() returns + * qcrypto_tls_session_handshake() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * * Returns 0 if the credentials validated, -1 on error @@ -226,7 +228,7 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, * registered with qcrypto_tls_session_set_callbacks() * * It is an error to call this before - * qcrypto_tls_session_get_handshake_status() returns + * qcrypto_tls_session_handshake() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * * Returns: the number of bytes sent, @@ -256,7 +258,7 @@ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, * opposed to an error. * * It is an error to call this before - * qcrypto_tls_session_get_handshake_status() returns + * qcrypto_tls_session_handshake() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * * Returns: the number of bytes received, @@ -289,8 +291,7 @@ size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); * the underlying data channel is non-blocking, then * this method may return control before the handshake * is complete. On non-blocking channels the - * qcrypto_tls_session_get_handshake_status() method - * should be used to determine whether the handshake + * return value determines whether the handshake * has completed, or is waiting to send or receive * data. In the latter cases, the caller should setup * an event loop watch and call this method again @@ -306,23 +307,6 @@ typedef enum { QCRYPTO_TLS_HANDSHAKE_RECVING, } QCryptoTLSSessionHandshakeStatus; -/** - * qcrypto_tls_session_get_handshake_status: - * @sess: the TLS session object - * - * Check the status of the TLS handshake. This - * is used with non-blocking data channels to - * determine whether the handshake is waiting - * to send or receive further data to/from the - * remote peer. - * - * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE - * it is permitted to send/receive payload data on - * the channel - */ -QCryptoTLSSessionHandshakeStatus -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); - typedef enum { QCRYPTO_TLS_BYE_COMPLETE, QCRYPTO_TLS_BYE_SENDING, diff --git a/io/channel-tls.c b/io/channel-tls.c index 517ce190a4..ecde6b57bf 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -162,16 +162,17 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, GMainContext *context) { Error *err = NULL; - QCryptoTLSSessionHandshakeStatus status; + int status; - if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { + status = qcrypto_tls_session_handshake(ioc->session, &err); + + if (status < 0) { trace_qio_channel_tls_handshake_fail(ioc); qio_task_set_error(task, err); qio_task_complete(task); return; } - status = qcrypto_tls_session_get_handshake_status(ioc->session); if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { trace_qio_channel_tls_handshake_complete(ioc); if (qcrypto_tls_session_check_credentials(ioc->session, diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index 3395f73560..554054e934 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -158,8 +158,7 @@ static void test_crypto_tls_session_psk(void) rv = qcrypto_tls_session_handshake(serverSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { serverShake = true; } } @@ -167,8 +166,7 @@ static void test_crypto_tls_session_psk(void) rv = qcrypto_tls_session_handshake(clientSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } @@ -352,8 +350,7 @@ static void test_crypto_tls_session_x509(const void *opaque) rv = qcrypto_tls_session_handshake(serverSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { serverShake = true; } } @@ -361,8 +358,7 @@ static void test_crypto_tls_session_x509(const void *opaque) rv = qcrypto_tls_session_handshake(clientSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } From a25b013019672ab456ef8b51912eadcdda418b73 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 7 Feb 2025 10:46:17 -0300 Subject: [PATCH 1712/2892] io: Add flags argument to qio_channel_readv_full_all_eof MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to pass flags into qio_channel_tls_readv() but qio_channel_readv_full_all_eof() doesn't take a flags argument. No functional change. Reviewed-by: Daniel P. Berrangé Acked-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- hw/remote/mpqemu-link.c | 2 +- include/io/channel.h | 2 ++ io/channel.c | 9 ++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c index e25f97680d..49885a1db6 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -110,7 +110,7 @@ static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds, bql_unlock(); } - ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, errp); + ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, 0, errp); if (drop_bql && !iothread && !qemu_in_coroutine()) { bql_lock(); diff --git a/include/io/channel.h b/include/io/channel.h index bdf0bca92a..58940eead5 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -885,6 +885,7 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, * @niov: the length of the @iov array * @fds: an array of file handles to read * @nfds: number of file handles in @fds + * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) * @errp: pointer to a NULL-initialized error object * * @@ -903,6 +904,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, const struct iovec *iov, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp); /** diff --git a/io/channel.c b/io/channel.c index e3f17c24a0..ebd9322765 100644 --- a/io/channel.c +++ b/io/channel.c @@ -115,7 +115,8 @@ int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, size_t niov, Error **errp) { - return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp); + return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, 0, + errp); } int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, @@ -130,6 +131,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, const struct iovec *iov, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { int ret = -1; @@ -155,7 +157,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, while ((nlocal_iov > 0) || local_fds) { ssize_t len; len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds, - local_nfds, 0, errp); + local_nfds, flags, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, G_IO_IN); @@ -222,7 +224,8 @@ int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, int **fds, size_t *nfds, Error **errp) { - int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp); + int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, 0, + errp); if (ret == 0) { error_setg(errp, "Unexpected end-of-file before all data were read"); From 322d873b634dc515220f154e29626a33f528bbfb Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 7 Feb 2025 10:48:21 -0300 Subject: [PATCH 1713/2892] io: Add a read flag for relaxed EOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a read flag that can inform a channel that it's ok to receive an EOF at any moment. Channels that have some form of strict EOF tracking, such as TLS session termination, may choose to ignore EOF errors with the use of this flag. This is being added for compatibility with older migration streams that do not include a TLS termination step. Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- include/io/channel.h | 1 + io/channel-tls.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/io/channel.h b/include/io/channel.h index 58940eead5..62b657109c 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -35,6 +35,7 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, #define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1 #define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 +#define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2 typedef enum QIOChannelFeature QIOChannelFeature; diff --git a/io/channel-tls.c b/io/channel-tls.c index ecde6b57bf..caf8301a9e 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -359,6 +359,7 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, tioc->session, iov[i].iov_base, iov[i].iov_len, + flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF || qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, errp); if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { From 48796f6b44df1dd0f78d18757889d5ac478c33e4 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 5 Feb 2025 13:17:22 -0300 Subject: [PATCH 1714/2892] migration/multifd: Terminate the TLS connection The multifd recv side has been getting a TLS error of GNUTLS_E_PREMATURE_TERMINATION at the end of migration when the send side closes the sockets without ending the TLS session. This has been masked by the code not checking the migration error after loadvm. Start ending the TLS session at multifd_send_shutdown() so the recv side always sees a clean termination (EOF) and we can start to differentiate that from an actual premature termination that might possibly happen in the middle of the migration. There's nothing to be done if a previous migration error has already broken the connection, so add a comment explaining it and ignore any errors coming from gnutls_bye(). This doesn't break compat with older recv-side QEMUs because EOF has always caused the recv thread to exit cleanly. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/multifd.c | 38 +++++++++++++++++++++++++++++++++++++- migration/tls.c | 5 +++++ migration/tls.h | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index ab73d6d984..0296758c08 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -490,6 +490,36 @@ void multifd_send_shutdown(void) return; } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + /* thread_created implies the TLS handshake has succeeded */ + if (p->tls_thread_created && p->thread_created) { + Error *local_err = NULL; + /* + * The destination expects the TLS session to always be + * properly terminated. This helps to detect a premature + * termination in the middle of the stream. Note that + * older QEMUs always break the connection on the source + * and the destination always sees + * GNUTLS_E_PREMATURE_TERMINATION. + */ + migration_tls_channel_end(p->c, &local_err); + + /* + * The above can return an error in case the migration has + * already failed. If the migration succeeded, errors are + * not expected but there's no need to kill the source. + */ + if (local_err && !migration_has_failed(migrate_get_current())) { + warn_report( + "multifd_send_%d: Failed to terminate TLS connection: %s", + p->id, error_get_pretty(local_err)); + break; + } + } + } + multifd_send_terminate_threads(); for (i = 0; i < migrate_multifd_channels(); i++) { @@ -1141,7 +1171,13 @@ static void *multifd_recv_thread(void *opaque) ret = qio_channel_read_all_eof(p->c, (void *)p->packet, p->packet_len, &local_err); - if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */ + if (!ret) { + /* EOF */ + assert(!local_err); + break; + } + + if (ret == -1) { break; } diff --git a/migration/tls.c b/migration/tls.c index fa03d9136c..5cbf952383 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -156,6 +156,11 @@ void migration_tls_channel_connect(MigrationState *s, NULL); } +void migration_tls_channel_end(QIOChannel *ioc, Error **errp) +{ + qio_channel_tls_bye(QIO_CHANNEL_TLS(ioc), errp); +} + bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) { if (!migrate_tls()) { diff --git a/migration/tls.h b/migration/tls.h index 5797d153cb..58b25e1228 100644 --- a/migration/tls.h +++ b/migration/tls.h @@ -36,7 +36,7 @@ void migration_tls_channel_connect(MigrationState *s, QIOChannel *ioc, const char *hostname, Error **errp); - +void migration_tls_channel_end(QIOChannel *ioc, Error **errp); /* Whether the QIO channel requires further TLS handshake? */ bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); From 9b3b192f65b1cf635719a2981dd2d4b70892d2ec Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 7 Feb 2025 10:50:49 -0300 Subject: [PATCH 1715/2892] migration/multifd: Add a compat property for TLS termination We're currently changing the way the source multifd migration handles the shutdown of the multifd channels when TLS is in use to perform a clean termination by calling gnutls_bye(). Older src QEMUs will always close the channel without terminating the TLS session. New dst QEMUs treat an unclean termination as an error. Add multifd_clean_tls_termination (default true) that can be switched on the destination whenever a src QEMU <= 9.2 is in use. (Note that the compat property is only strictly necessary for src QEMUs older than 9.1. Due to synchronization coincidences, src QEMUs 9.1 and 9.2 can put the destination in a condition where it doesn't see the unclean termination. Still, make the property more inclusive to facilitate potential backports.) Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- hw/core/machine.c | 1 + migration/migration.h | 33 +++++++++++++++++++++++++++++++++ migration/multifd.c | 14 ++++++++++++-- migration/multifd.h | 2 ++ migration/options.c | 2 ++ 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 254cc20c4c..02cff735b3 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -42,6 +42,7 @@ GlobalProperty hw_compat_9_2[] = { { "virtio-balloon-pci-transitional", "vectors", "0" }, { "virtio-balloon-pci-non-transitional", "vectors", "0" }, { "virtio-mem-pci", "vectors", "0" }, + { "migration", "multifd-clean-tls-termination", "false" }, }; const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); diff --git a/migration/migration.h b/migration/migration.h index eaebcc2042..eb84f75b4a 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -443,6 +443,39 @@ struct MigrationState { * Default value is false. (since 8.1) */ bool multifd_flush_after_each_section; + + /* + * This variable only makes sense when set on the machine that is + * the destination of a multifd migration with TLS enabled. It + * affects the behavior of the last send->recv iteration with + * regards to termination of the TLS session. + * + * When set: + * + * - the destination QEMU instance can expect to never get a + * GNUTLS_E_PREMATURE_TERMINATION error. Manifested as the error + * message: "The TLS connection was non-properly terminated". + * + * When clear: + * + * - the destination QEMU instance can expect to see a + * GNUTLS_E_PREMATURE_TERMINATION error in any multifd channel + * whenever the last recv() call of that channel happens after + * the source QEMU instance has already issued shutdown() on the + * channel. + * + * Commit 637280aeb2 (since 9.1) introduced a side effect that + * causes the destination instance to not be affected by the + * premature termination, while commit 1d457daf86 (since 10.0) + * causes the premature termination condition to be once again + * reachable. + * + * NOTE: Regardless of the state of this option, a premature + * termination of the TLS connection might happen due to error at + * any moment prior to the last send->recv iteration. + */ + bool multifd_clean_tls_termination; + /* * This decides the size of guest memory chunk that will be used * to track dirty bitmap clearing. The size of memory chunk will diff --git a/migration/multifd.c b/migration/multifd.c index 0296758c08..554035e095 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1151,6 +1151,7 @@ void multifd_recv_sync_main(void) static void *multifd_recv_thread(void *opaque) { + MigrationState *s = migrate_get_current(); MultiFDRecvParams *p = opaque; Error *local_err = NULL; bool use_packets = multifd_use_packets(); @@ -1159,18 +1160,27 @@ static void *multifd_recv_thread(void *opaque) trace_multifd_recv_thread_start(p->id); rcu_register_thread(); + if (!s->multifd_clean_tls_termination) { + p->read_flags = QIO_CHANNEL_READ_FLAG_RELAXED_EOF; + } + while (true) { uint32_t flags = 0; bool has_data = false; p->normal_num = 0; if (use_packets) { + struct iovec iov = { + .iov_base = (void *)p->packet, + .iov_len = p->packet_len + }; + if (multifd_recv_should_exit()) { break; } - ret = qio_channel_read_all_eof(p->c, (void *)p->packet, - p->packet_len, &local_err); + ret = qio_channel_readv_full_all_eof(p->c, &iov, 1, NULL, NULL, + p->read_flags, &local_err); if (!ret) { /* EOF */ assert(!local_err); diff --git a/migration/multifd.h b/migration/multifd.h index bd785b9873..cf408ff721 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -244,6 +244,8 @@ typedef struct { uint32_t zero_num; /* used for de-compression methods */ void *compress_data; + /* Flags for the QIOChannel */ + int read_flags; } MultiFDRecvParams; typedef struct { diff --git a/migration/options.c b/migration/options.c index 4db340b502..bb259d192a 100644 --- a/migration/options.c +++ b/migration/options.c @@ -99,6 +99,8 @@ const Property migration_properties[] = { clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT), DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState, preempt_pre_7_2, false), + DEFINE_PROP_BOOL("multifd-clean-tls-termination", MigrationState, + multifd_clean_tls_termination, true), /* Migration parameters */ DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, From e0ad300fe1a0e0b12b90994bab6e4df77dd1ee8a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 5 Feb 2025 13:23:55 -0300 Subject: [PATCH 1716/2892] migration: Check migration error after loadvm We're currently only checking the QEMUFile error after qemu_loadvm_state(). This was causing a TLS termination error from multifd recv threads to be ignored. Start checking the migration error as well to avoid missing further errors. Regarding compatibility concerning the TLS termination error that was being ignored, for QEMUs <= 9.2 - if the old QEMU is being used as migration source - the recently added migration property multifd-tls-clean-termination needs to be set to OFF in the *destination* machine. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/savevm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/migration/savevm.c b/migration/savevm.c index bc375db282..4046faf009 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2940,7 +2940,11 @@ int qemu_loadvm_state(QEMUFile *f) /* When reaching here, it must be precopy */ if (ret == 0) { - ret = qemu_file_get_error(f); + if (migrate_has_error(migrate_get_current())) { + ret = -EINVAL; + } else { + ret = qemu_file_get_error(f); + } } /* From a47f0cfba8d33f7001bcc4616f96e42dbd553135 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:19 -0300 Subject: [PATCH 1717/2892] migration: Set migration error outside of migrate_cancel There's no point passing the error into migration cancel only for it to call migrate_set_error(). Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-2-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/migration.c | 7 ++----- migration/migration.h | 2 +- migration/ram.c | 4 +++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 396928513a..7728f52aef 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -342,11 +342,8 @@ void migration_bh_schedule(QEMUBHFunc *cb, void *opaque) qemu_bh_schedule(bh); } -void migration_cancel(const Error *error) +void migration_cancel() { - if (error) { - migrate_set_error(current_migration, error); - } if (migrate_dirty_limit()) { qmp_cancel_vcpu_dirty_limit(false, -1, NULL); } @@ -365,7 +362,7 @@ void migration_shutdown(void) * Cancel the current migration - that will (eventually) * stop the migration using this structure */ - migration_cancel(NULL); + migration_cancel(); object_unref(OBJECT(current_migration)); /* diff --git a/migration/migration.h b/migration/migration.h index eb84f75b4a..f083f4f87e 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -563,7 +563,7 @@ void migration_make_urgent_request(void); void migration_consume_urgent_request(void); bool migration_rate_limit(void); void migration_bh_schedule(QEMUBHFunc *cb, void *opaque); -void migration_cancel(const Error *error); +void migration_cancel(void); void migration_populate_vfio_info(MigrationInfo *info); void migration_reset_vfio_bytes_transferred(void); diff --git a/migration/ram.c b/migration/ram.c index 6f460fd22d..589b6505eb 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4465,8 +4465,10 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, * Abort and indicate a proper reason. */ error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr); - migration_cancel(err); + migrate_set_error(migrate_get_current(), err); error_free(err); + + migration_cancel(); } switch (ps) { From 8444d0938112b7da8d88cc6a7481b9eb33654997 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:20 -0300 Subject: [PATCH 1718/2892] migration: Unify migration_cancel and migrate_fd_cancel There's no need for two separate functions and this _fd_ is a historic artifact that makes little sense nowadays. Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-3-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/migration.c | 18 +++++++----------- migration/trace-events | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 7728f52aef..e37842fdd2 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -106,7 +106,6 @@ static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); static bool migration_switchover_start(MigrationState *s, Error **errp); -static void migrate_fd_cancel(MigrationState *s); static bool close_return_path_on_source(MigrationState *s); static void migration_completion_end(MigrationState *s); static void migrate_hup_delete(MigrationState *s); @@ -342,14 +341,6 @@ void migration_bh_schedule(QEMUBHFunc *cb, void *opaque) qemu_bh_schedule(bh); } -void migration_cancel() -{ - if (migrate_dirty_limit()) { - qmp_cancel_vcpu_dirty_limit(false, -1, NULL); - } - migrate_fd_cancel(current_migration); -} - void migration_shutdown(void) { /* @@ -1555,12 +1546,17 @@ static void migrate_fd_error(MigrationState *s, const Error *error) migrate_set_error(s, error); } -static void migrate_fd_cancel(MigrationState *s) +void migration_cancel(void) { + MigrationState *s = migrate_get_current(); int old_state ; bool setup = (s->state == MIGRATION_STATUS_SETUP); - trace_migrate_fd_cancel(); + trace_migration_cancel(); + + if (migrate_dirty_limit()) { + qmp_cancel_vcpu_dirty_limit(false, -1, NULL); + } WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { if (s->rp_state.from_dst_file) { diff --git a/migration/trace-events b/migration/trace-events index 12b262f8ee..d22600abe6 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -156,7 +156,7 @@ multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostnam migrate_set_state(const char *new_state) "new state %s" migrate_fd_cleanup(void) "" migrate_error(const char *error_desc) "error=%s" -migrate_fd_cancel(void) "" +migration_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" migrate_pending_estimate(uint64_t size, uint64_t pre, uint64_t post) "estimate pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" From 4bbadfc55e6ec608df75911b4360e6e995daa28c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:21 -0300 Subject: [PATCH 1719/2892] migration: Change migrate_fd_ to migration_ Remove all instances of _fd_ from the migration generic code. These functions have grown over time and the _fd_ part is now just confusing. migration_fd_error() -> migration_error() makes it a little vague. Since it's only used for migration_connect() failures, change it to migration_connect_set_error(). Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-4-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/channel.c | 4 ++-- migration/migration.c | 30 +++++++++++++++--------------- migration/migration.h | 2 +- migration/multifd.c | 2 +- migration/rdma.c | 2 +- migration/trace-events | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/migration/channel.c b/migration/channel.c index f9de064f3b..24a91ef911 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -74,7 +74,7 @@ void migration_channel_connect(MigrationState *s, if (!error) { /* tls_channel_connect will call back to this * function after the TLS handshake, - * so we mustn't call migrate_fd_connect until then + * so we mustn't call migration_connect until then */ return; @@ -89,7 +89,7 @@ void migration_channel_connect(MigrationState *s, qemu_mutex_unlock(&s->qemu_file_lock); } } - migrate_fd_connect(s, error); + migration_connect(s, error); error_free(error); } diff --git a/migration/migration.c b/migration/migration.c index e37842fdd2..c39cedef3b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1423,12 +1423,12 @@ static void migration_cleanup_json_writer(MigrationState *s) g_clear_pointer(&s->vmdesc, json_writer_free); } -static void migrate_fd_cleanup(MigrationState *s) +static void migration_cleanup(MigrationState *s) { MigrationEventType type; QEMUFile *tmp = NULL; - trace_migrate_fd_cleanup(); + trace_migration_cleanup(); migration_cleanup_json_writer(s); @@ -1485,9 +1485,9 @@ static void migrate_fd_cleanup(MigrationState *s) yank_unregister_instance(MIGRATION_YANK_INSTANCE); } -static void migrate_fd_cleanup_bh(void *opaque) +static void migration_cleanup_bh(void *opaque) { - migrate_fd_cleanup(opaque); + migration_cleanup(opaque); } void migrate_set_error(MigrationState *s, const Error *error) @@ -1517,7 +1517,7 @@ static void migrate_error_free(MigrationState *s) } } -static void migrate_fd_error(MigrationState *s, const Error *error) +static void migration_connect_set_error(MigrationState *s, const Error *error) { MigrationStatus current = s->state; MigrationStatus next; @@ -2198,7 +2198,7 @@ void qmp_migrate(const char *uri, bool has_channels, out: if (local_err) { - migrate_fd_error(s, local_err); + migration_connect_set_error(s, local_err); error_propagate(errp, local_err); } } @@ -2243,7 +2243,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, if (!resume_requested) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); } - migrate_fd_error(s, local_err); + migration_connect_set_error(s, local_err); error_propagate(errp, local_err); return; } @@ -3427,7 +3427,7 @@ static void migration_iteration_finish(MigrationState *s) break; } - migration_bh_schedule(migrate_fd_cleanup_bh, s); + migration_bh_schedule(migration_cleanup_bh, s); bql_unlock(); } @@ -3455,7 +3455,7 @@ static void bg_migration_iteration_finish(MigrationState *s) break; } - migration_bh_schedule(migrate_fd_cleanup_bh, s); + migration_bh_schedule(migration_cleanup_bh, s); bql_unlock(); } @@ -3837,7 +3837,7 @@ fail_setup: return NULL; } -void migrate_fd_connect(MigrationState *s, Error *error_in) +void migration_connect(MigrationState *s, Error *error_in) { Error *local_err = NULL; uint64_t rate_limit; @@ -3847,24 +3847,24 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) /* * If there's a previous error, free it and prepare for another one. * Meanwhile if migration completes successfully, there won't have an error - * dumped when calling migrate_fd_cleanup(). + * dumped when calling migration_cleanup(). */ migrate_error_free(s); s->expected_downtime = migrate_downtime_limit(); if (error_in) { - migrate_fd_error(s, error_in); + migration_connect_set_error(s, error_in); if (resume) { /* * Don't do cleanup for resume if channel is invalid, but only dump * the error. We wait for another channel connect from the user. * The error_report still gives HMP user a hint on what failed. - * It's normally done in migrate_fd_cleanup(), but call it here + * It's normally done in migration_cleanup(), but call it here * explicitly. */ error_report_err(error_copy(s->error)); } else { - migrate_fd_cleanup(s); + migration_cleanup(s); } return; } @@ -3944,7 +3944,7 @@ fail: migrate_set_error(s, local_err); migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); error_report_err(local_err); - migrate_fd_cleanup(s); + migration_cleanup(s); } static void migration_class_init(ObjectClass *klass, void *data) diff --git a/migration/migration.h b/migration/migration.h index f083f4f87e..4639e2a7e4 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -517,7 +517,7 @@ bool migration_has_all_channels(void); void migrate_set_error(MigrationState *s, const Error *error); bool migrate_has_error(MigrationState *s); -void migrate_fd_connect(MigrationState *s, Error *error_in); +void migration_connect(MigrationState *s, Error *error_in); int migration_call_notifiers(MigrationState *s, MigrationEventType type, Error **errp); diff --git a/migration/multifd.c b/migration/multifd.c index 554035e095..215ad0414a 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -444,7 +444,7 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp) * channels have no I/O handler callback registered when reaching * here, because migration thread will wait for all multifd channel * establishments to complete during setup. Since - * migrate_fd_cleanup() will be scheduled in main thread too, all + * migration_cleanup() will be scheduled in main thread too, all * previous callbacks should guarantee to be completed when * reaching here. See multifd_send_state.channels_created and its * usage. In the future, we could replace this with an assert diff --git a/migration/rdma.c b/migration/rdma.c index 855753c671..76fb034923 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -4174,7 +4174,7 @@ void rdma_start_outgoing_migration(void *opaque, s->to_dst_file = rdma_new_output(rdma); s->rdma_migration = true; - migrate_fd_connect(s, NULL); + migration_connect(s, NULL); return; return_path_err: qemu_rdma_cleanup(rdma); diff --git a/migration/trace-events b/migration/trace-events index d22600abe6..58c0f07f5b 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -154,7 +154,7 @@ multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostnam # migration.c migrate_set_state(const char *new_state) "new state %s" -migrate_fd_cleanup(void) "" +migration_cleanup(void) "" migrate_error(const char *error_desc) "error=%s" migration_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" From 2b667a8c0f7ad423c9141b3a487898c50a6ff5e0 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:22 -0300 Subject: [PATCH 1720/2892] migration: Fix hang after error in destination setup phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the destination side fails at migration_ioc_process_incoming() before starting the coroutine, it will report the error but QEMU will not exit. Set the migration state to FAILED and exit the process if exit-on-error allows. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2633 Reported-by: Daniel P. Berrangé Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-5-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/channel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/migration/channel.c b/migration/channel.c index 24a91ef911..a547b1fbfe 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -33,6 +33,7 @@ void migration_channel_process_incoming(QIOChannel *ioc) { MigrationState *s = migrate_get_current(); + MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; trace_migration_set_incoming_channel( @@ -47,6 +48,10 @@ void migration_channel_process_incoming(QIOChannel *ioc) if (local_err) { error_report_err(local_err); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + if (mis->exit_on_error) { + exit(EXIT_FAILURE); + } } } From 646119088f8a1d9925239e70b0a7b426bfb6e58a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:23 -0300 Subject: [PATCH 1721/2892] migration: Reject qmp_migrate_cancel after postcopy After postcopy has started, it's not possible to recover the source machine in case a migration error occurs because the destination has already been changing the state of the machine. For that same reason, it doesn't make sense to try to cancel the migration after postcopy has started. Reject the cancel command during postcopy. Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-6-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/migration.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index c39cedef3b..48c9ad3c96 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2251,7 +2251,18 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, void qmp_migrate_cancel(Error **errp) { - migration_cancel(NULL); + /* + * After postcopy migration has started, the source machine is not + * recoverable in case of a migration error. This also means the + * cancel command cannot be used as cancel should allow the + * machine to continue operation. + */ + if (migration_in_postcopy()) { + error_setg(errp, "Postcopy migration in progress, cannot cancel."); + return; + } + + migration_cancel(); } void qmp_migrate_continue(MigrationStatus state, Error **errp) From 4a228bcc994ea8ab05d4927e23e7916f32cc1168 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:24 -0300 Subject: [PATCH 1722/2892] migration: Don't set FAILED state when cancelling The expected outcome from qmp_migrate_cancel() is that the source migration goes to the terminal state MIGRATION_STATUS_CANCELLED. Anything different from this is a bug when cancelling. Make sure there is never a state transition from an unspecified state into FAILED. Code that sets FAILED, should always either make sure that the old state is not CANCELLING or specify the old state. Note that the destination is allowed to go into FAILED, so there's no issue there. (I don't think this is relevant as a backport because cancelling does work, it just doesn't show the right state at the end) Fixes: 3dde8fdbad ("migration: Merge precopy/postcopy on switchover start") Fixes: d0edb8a173 ("migration: Create the postcopy preempt channel asynchronously") Fixes: 8518278a6a ("migration: implementation of background snapshot thread") Fixes: bf78a046b9 ("migration: refactor migrate_fd_connect failures") Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-7-farosas@suse.de> Signed-off-by: Fabiano Rosas --- migration/migration.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 48c9ad3c96..c597aa707e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2648,7 +2648,10 @@ static int postcopy_start(MigrationState *ms, Error **errp) if (migrate_postcopy_preempt()) { migration_wait_main_channel(ms); if (postcopy_preempt_establish_channel(ms)) { - migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED); + if (ms->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&ms->state, ms->state, + MIGRATION_STATUS_FAILED); + } error_setg(errp, "%s: Failed to establish preempt channel", __func__); return -1; @@ -2986,7 +2989,9 @@ fail: error_free(local_err); } - migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + if (s->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + } } /** @@ -3009,7 +3014,7 @@ static void bg_migration_completion(MigrationState *s) qemu_put_buffer(s->to_dst_file, s->bioc->data, s->bioc->usage); qemu_fflush(s->to_dst_file); } else if (s->state == MIGRATION_STATUS_CANCELLING) { - goto fail; + return; } if (qemu_file_get_error(s->to_dst_file)) { @@ -3953,7 +3958,9 @@ void migration_connect(MigrationState *s, Error *error_in) fail: migrate_set_error(s, local_err); - migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + if (s->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + } error_report_err(local_err); migration_cleanup(s); } From aabb2a5b5d54acde0992d933b647a306e59362b4 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:25 -0300 Subject: [PATCH 1723/2892] tests/qtest/migration: Introduce migration_test_add_suffix Introduce a new migration_test_add_suffix to allow programmatic creation of tests based on a suffix. Pass the test name into the test so it can know which variant to run. Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-8-farosas@suse.de> Signed-off-by: Fabiano Rosas --- tests/qtest/migration/migration-util.c | 24 ++++++++++++++++++++++++ tests/qtest/migration/migration-util.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 6261d80e4a..642cf50c8d 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -236,6 +236,7 @@ char *resolve_machine_version(const char *alias, const char *var1, typedef struct { char *name; void (*func)(void); + void (*func_full)(void *); } MigrationTest; static void migration_test_destroy(gpointer data) @@ -265,6 +266,29 @@ void migration_test_add(const char *path, void (*fn)(void)) migration_test_destroy); } +static void migration_test_wrapper_full(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func_full(test->name); +} + +void migration_test_add_suffix(const char *path, const char *suffix, + void (*fn)(void *)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + g_assert(g_str_has_suffix(path, "/")); + g_assert(!g_str_has_prefix(suffix, "/")); + + test->func_full = fn; + test->name = g_strconcat(path, suffix, NULL); + + qtest_add_data_func_full(test->name, test, migration_test_wrapper_full, + migration_test_destroy); +} + #ifdef O_DIRECT /* * Probe for O_DIRECT support on the filesystem. Since this is used diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h index f5f2e4650e..44815e9c42 100644 --- a/tests/qtest/migration/migration-util.h +++ b/tests/qtest/migration/migration-util.h @@ -51,6 +51,8 @@ static inline bool probe_o_direct_support(const char *tmpfs) bool ufd_version_check(bool *uffd_feature_thread_id); bool kvm_dirty_ring_supported(void); void migration_test_add(const char *path, void (*fn)(void)); +void migration_test_add_suffix(const char *path, const char *suffix, + void (*fn)(void *)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); From 538e03d28001a325a93154df2313d215721b2241 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:26 -0300 Subject: [PATCH 1724/2892] tests/qtest/migration: Add a cancel test The qmp_migrate_cancel() command is poorly tested and code inspection reveals that there might be concurrency issues with its usage. Add a test that runs a migration and calls qmp_migrate_cancel() at specific moments. In order to make the test more deterministic, instead of calling qmp_migrate_cancel() at random moments during migration, do it after the migration status change events are seen. The expected result is that qmp_migrate_cancel() on the source ends migration on the source with the "cancelled" state and ends migration on the destination with the "failed" state. The only exception is that a failed migration should continue in the failed state. Cancelling is not allowed during postcopy (no test is added for this because it's a trivial check in the code). Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-9-farosas@suse.de> Signed-off-by: Fabiano Rosas --- tests/qtest/migration/precopy-tests.c | 176 ++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 162fa69531..ba273d10b9 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -20,6 +20,7 @@ #include "migration/migration-util.h" #include "ppc-util.h" #include "qobject/qlist.h" +#include "qapi-types-migration.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" @@ -536,6 +537,161 @@ static void test_multifd_tcp_cancel(void) migrate_end(from, to2, true); } +static void test_cancel_src_after_failed(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + /* + * No migrate_incoming_qmp() at the start to force source into + * failed state during migrate_qmp(). + */ + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + + /* cancelling will not move the migration out of 'failed' */ + + wait_for_migration_status(from, "failed", + (const char * []) { "completed", NULL }); + + /* + * Not waiting for the destination because it never started + * migration. + */ +} + +static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* To move to cancelled/cancelling */ + migrate_cancel(from); + migration_event_wait(from, phase); + + /* The migrate_cancel under test */ + migrate_cancel(from); + + wait_for_migration_status(from, "cancelled", + (const char * []) { "completed", NULL }); + + wait_for_migration_status(to, "failed", + (const char * []) { "completed", NULL }); +} + +static void test_cancel_src_after_complete(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + + /* + * qmp_migrate_cancel() exits early if migration is not running + * anymore, the status will not change to cancelled. + */ + wait_for_migration_complete(from); + wait_for_migration_complete(to); +} + +static void test_cancel_src_after_none(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + /* + * Test that cancelling without a migration happening does not + * affect subsequent migrations + */ + migrate_cancel(to); + + wait_for_serial("src_serial"); + migrate_cancel(from); + + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + + wait_for_migration_complete(from); + wait_for_migration_complete(to); +} + +static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_set_capability(from, "pause-before-switchover", true); + migrate_set_capability(to, "pause-before-switchover", true); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + migration_event_wait(from, "cancelling"); + + wait_for_migration_status(from, "cancelled", + (const char * []) { "completed", NULL }); + + wait_for_migration_status(to, "failed", + (const char * []) { "completed", NULL }); +} + +static void test_cancel_src_after_status(void *opaque) +{ + const char *test_path = opaque; + g_autofree char *phase = g_path_get_basename(test_path); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .hide_stderr = true, + }; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + if (g_str_equal(phase, "cancelling") || + g_str_equal(phase, "cancelled")) { + test_cancel_src_after_cancelled(from, to, uri, phase); + + } else if (g_str_equal(phase, "completed")) { + test_cancel_src_after_complete(from, to, uri, phase); + + } else if (g_str_equal(phase, "failed")) { + test_cancel_src_after_failed(from, to, uri, phase); + + } else if (g_str_equal(phase, "none")) { + test_cancel_src_after_none(from, to, uri, phase); + + } else { + /* any state that comes before pre-switchover */ + test_cancel_src_pre_switchover(from, to, uri, phase); + } + + migrate_end(from, to, false); +} + static void calc_dirty_rate(QTestState *who, uint64_t calc_time) { qtest_qmp_assert_success(who, @@ -1018,4 +1174,24 @@ void migration_test_add_precopy(MigrationTestEnv *env) test_vcpu_dirty_limit); } } + + /* ensure new status don't go unnoticed */ + assert(MIGRATION_STATUS__MAX == 15); + + for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) { + switch (i) { + case MIGRATION_STATUS_DEVICE: /* happens too fast */ + case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */ + case MIGRATION_STATUS_COLO: /* no support in tests */ + case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */ + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + continue; + default: + migration_test_add_suffix("/migration/cancel/src/after/", + MigrationStatus_str(i), + test_cancel_src_after_status); + } + } } From 24f4c80cfc31ab4ed4cb6553c9289e3cf8ca63ac Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 14:59:27 -0300 Subject: [PATCH 1725/2892] migration: Update migrate_cancel documentation Update the migrate_cancel command documentation with a few words about postcopy and the expected state of the machine after migration. Acked-by: Markus Armbruster Reviewed-by: Peter Xu Message-ID: <20250213175927.19642-10-farosas@suse.de> Signed-off-by: Fabiano Rosas --- qapi/migration.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qapi/migration.json b/qapi/migration.json index 43babd1df4..8b9c53595c 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1524,7 +1524,9 @@ ## # @migrate_cancel: # -# Cancel the current executing migration process. +# Cancel the currently executing migration process. Allows a new +# migration to be started right after. When postcopy-ram is in use, +# cancelling is not allowed after the postcopy phase has started. # # .. note:: This command succeeds even if there is no migration # process running. From b451705e3b90e55c6070338fa97aaae274721a5c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 5 Feb 2025 12:54:01 -0800 Subject: [PATCH 1726/2892] migration: use parameters.mode in cpr_state_save qmp_migrate guarantees that cpr_channel is not null for MIG_MODE_CPR_TRANSFER when cpr_state_save is called: qmp_migrate() if (s->parameters.mode == MIG_MODE_CPR_TRANSFER && !cpr_channel) { return; } cpr_state_save(cpr_channel) but cpr_state_save checks for mode differently before using channel, and Coverity cannot infer that they are equivalent in outgoing QEMU, and warns that channel may be NULL: cpr_state_save(channel) MigMode mode = migrate_mode(); if (mode == MIG_MODE_CPR_TRANSFER) { f = cpr_transfer_output(channel, errp); To make Coverity happy, assert that channel != NULL in cpr_state_save. Resolves: Coverity CID 1590980 Reported-by: Peter Maydell Signed-off-by: Steve Sistare Message-ID: <1738788841-211843-1-git-send-email-steven.sistare@oracle.com> [assert instead of using parameters.mode in cpr_state_save] Signed-off-by: Fabiano Rosas --- migration/cpr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/cpr.c b/migration/cpr.c index 584b0b98f7..180faab247 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -137,6 +137,7 @@ int cpr_state_save(MigrationChannel *channel, Error **errp) trace_cpr_state_save(MigMode_str(mode)); if (mode == MIG_MODE_CPR_TRANSFER) { + g_assert(channel); f = cpr_transfer_output(channel, errp); } else { return 0; From 32a1bb21c6f4d569427099e4e495f1d07d017fdb Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Fri, 14 Feb 2025 18:55:23 +0800 Subject: [PATCH 1727/2892] guestperf: Support deferred migration for multifd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way to enable multifd migration has been changed by commit, 82137e6c8c (migration: enforce multifd and postcopy preempt to be set before incoming), and guestperf has not made the necessary changes. If multifd migration had been enabled in the previous manner, the following error would have occurred: Multifd must be set before incoming starts Supporting deferred migration will fix it. Signed-off-by: Hyman Huang Reviewed-by: Daniel P. Berrangé Message-ID: <8874e170f890ce0bc6f25cb0d9b9ae307ce2e070.1739530098.git.yong.huang@smartx.com> Signed-off-by: Fabiano Rosas --- tests/migration-stress/guestperf/engine.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/migration-stress/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py index 608d7270f6..4b15322e8d 100644 --- a/tests/migration-stress/guestperf/engine.py +++ b/tests/migration-stress/guestperf/engine.py @@ -106,7 +106,8 @@ class Engine(object): info.get("dirty-limit-ring-full-time", 0), ) - def _migrate(self, hardware, scenario, src, dst, connect_uri): + def _migrate(self, hardware, scenario, src, + dst, connect_uri, defer_migrate): src_qemu_time = [] src_vcpu_time = [] src_pid = src.get_pid() @@ -220,6 +221,8 @@ class Engine(object): resp = src.cmd("migrate-set-parameters", vcpu_dirty_limit=scenario._vcpu_dirty_limit) + if defer_migrate: + resp = dst.cmd("migrate-incoming", uri=connect_uri) resp = src.cmd("migrate", uri=connect_uri) post_copy = False @@ -373,11 +376,14 @@ class Engine(object): def _get_src_args(self, hardware): return self._get_common_args(hardware) - def _get_dst_args(self, hardware, uri): + def _get_dst_args(self, hardware, uri, defer_migrate): tunnelled = False if self._dst_host != "localhost": tunnelled = True argv = self._get_common_args(hardware, tunnelled) + + if defer_migrate: + return argv + ["-incoming", "defer"] return argv + ["-incoming", uri] @staticmethod @@ -424,6 +430,7 @@ class Engine(object): def run(self, hardware, scenario, result_dir=os.getcwd()): abs_result_dir = os.path.join(result_dir, scenario._name) + defer_migrate = False if self._transport == "tcp": uri = "tcp:%s:9000" % self._dst_host @@ -439,6 +446,9 @@ class Engine(object): except: pass + if scenario._multifd: + defer_migrate = True + if self._dst_host != "localhost": dstmonaddr = ("localhost", 9001) else: @@ -452,7 +462,7 @@ class Engine(object): monitor_address=srcmonaddr) dst = QEMUMachine(self._binary, - args=self._get_dst_args(hardware, uri), + args=self._get_dst_args(hardware, uri, defer_migrate), wrapper=self._get_dst_wrapper(hardware), name="qemu-dst-%d" % os.getpid(), monitor_address=dstmonaddr) @@ -461,7 +471,8 @@ class Engine(object): src.launch() dst.launch() - ret = self._migrate(hardware, scenario, src, dst, uri) + ret = self._migrate(hardware, scenario, src, + dst, uri, defer_migrate) progress_history = ret[0] qemu_timings = ret[1] vcpu_timings = ret[2] From 42f5975cd84eed96661468ae0b895eed0e16074b Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Fri, 14 Feb 2025 18:55:24 +0800 Subject: [PATCH 1728/2892] guestperf: Nitpick the inconsistent parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hyman Huang Reviewed-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Message-ID: Signed-off-by: Fabiano Rosas --- tests/migration-stress/guestperf/comparison.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/migration-stress/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py index 42cc0372d1..40e9d2eb1d 100644 --- a/tests/migration-stress/guestperf/comparison.py +++ b/tests/migration-stress/guestperf/comparison.py @@ -127,7 +127,7 @@ COMPARISONS = [ # varying numbers of channels Comparison("compr-multifd", scenarios = [ Scenario("compr-multifd-channels-4", - multifd=True, multifd_channels=2), + multifd=True, multifd_channels=4), Scenario("compr-multifd-channels-8", multifd=True, multifd_channels=8), Scenario("compr-multifd-channels-32", From 45f34156e4d9c3f4215402b34d7da32f00073066 Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Fri, 14 Feb 2025 18:55:25 +0800 Subject: [PATCH 1729/2892] guestperf: Introduce multifd compression option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guestperf tool does not cover the multifd compression option currently, it is worth supporting so that developers can analysis the migration performance with different compression algorithms. Multifd support 4 compression algorithms currently: zlib, zstd, qpl, uadk To request that multifd with the specified compression algorithm such as zlib: $ ./tests/migration-stress/guestperf.py \ --multifd --multifd-channels 4 --multifd-compression zlib \ --output output.json To run the entire standardized set of multifd compression comparisons, with unix migration: $ ./tests/migration-stress/guestperf-batch.py \ --dst-host localhost --transport unix \ --filter compr-multifd-compression* --output outputdir Signed-off-by: Hyman Huang Reviewed-by: Daniel P. Berrangé Message-ID: Signed-off-by: Fabiano Rosas --- tests/migration-stress/guestperf/comparison.py | 13 +++++++++++++ tests/migration-stress/guestperf/engine.py | 14 ++++++++++++++ tests/migration-stress/guestperf/scenario.py | 7 +++++-- tests/migration-stress/guestperf/shell.py | 3 +++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/migration-stress/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py index 40e9d2eb1d..dee3ac25e4 100644 --- a/tests/migration-stress/guestperf/comparison.py +++ b/tests/migration-stress/guestperf/comparison.py @@ -158,4 +158,17 @@ COMPARISONS = [ Scenario("compr-dirty-limit-50MB", dirty_limit=True, vcpu_dirty_limit=50), ]), + + # Looking at effect of multifd with + # different compression algorithms + Comparison("compr-multifd-compression", scenarios = [ + Scenario("compr-multifd-compression-zlib", + multifd=True, multifd_channels=2, multifd_compression="zlib"), + Scenario("compr-multifd-compression-zstd", + multifd=True, multifd_channels=2, multifd_compression="zstd"), + Scenario("compr-multifd-compression-qpl", + multifd=True, multifd_channels=2, multifd_compression="qpl"), + Scenario("compr-multifd-compression-uadk", + multifd=True, multifd_channels=2, multifd_compression="uadk"), + ]), ] diff --git a/tests/migration-stress/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py index 4b15322e8d..e11f6a8496 100644 --- a/tests/migration-stress/guestperf/engine.py +++ b/tests/migration-stress/guestperf/engine.py @@ -31,6 +31,8 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'python')) from qemu.machine import QEMUMachine +# multifd supported compression algorithms +MULTIFD_CMP_ALGS = ("zlib", "zstd", "qpl", "uadk") class Engine(object): @@ -191,6 +193,12 @@ class Engine(object): scenario._compression_xbzrle_cache)) if scenario._multifd: + if (scenario._multifd_compression and + (scenario._multifd_compression not in MULTIFD_CMP_ALGS)): + raise Exception("unsupported multifd compression " + "algorithm: %s" % + scenario._multifd_compression) + resp = src.cmd("migrate-set-capabilities", capabilities = [ { "capability": "multifd", @@ -206,6 +214,12 @@ class Engine(object): resp = dst.cmd("migrate-set-parameters", multifd_channels=scenario._multifd_channels) + if scenario._multifd_compression: + resp = src.cmd("migrate-set-parameters", + multifd_compression=scenario._multifd_compression) + resp = dst.cmd("migrate-set-parameters", + multifd_compression=scenario._multifd_compression) + if scenario._dirty_limit: if not hardware._dirty_ring_size: raise Exception("dirty ring size must be configured when " diff --git a/tests/migration-stress/guestperf/scenario.py b/tests/migration-stress/guestperf/scenario.py index 154c4f5d5f..4be7fafebf 100644 --- a/tests/migration-stress/guestperf/scenario.py +++ b/tests/migration-stress/guestperf/scenario.py @@ -30,7 +30,7 @@ class Scenario(object): auto_converge=False, auto_converge_step=10, compression_mt=False, compression_mt_threads=1, compression_xbzrle=False, compression_xbzrle_cache=10, - multifd=False, multifd_channels=2, + multifd=False, multifd_channels=2, multifd_compression="", dirty_limit=False, x_vcpu_dirty_limit_period=500, vcpu_dirty_limit=1): @@ -61,6 +61,7 @@ class Scenario(object): self._multifd = multifd self._multifd_channels = multifd_channels + self._multifd_compression = multifd_compression self._dirty_limit = dirty_limit self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period @@ -85,6 +86,7 @@ class Scenario(object): "compression_xbzrle_cache": self._compression_xbzrle_cache, "multifd": self._multifd, "multifd_channels": self._multifd_channels, + "multifd_compression": self._multifd_compression, "dirty_limit": self._dirty_limit, "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, "vcpu_dirty_limit": self._vcpu_dirty_limit, @@ -109,4 +111,5 @@ class Scenario(object): data["compression_xbzrle"], data["compression_xbzrle_cache"], data["multifd"], - data["multifd_channels"]) + data["multifd_channels"], + data["multifd_compression"]) diff --git a/tests/migration-stress/guestperf/shell.py b/tests/migration-stress/guestperf/shell.py index 046afeb84e..63bbe3226c 100644 --- a/tests/migration-stress/guestperf/shell.py +++ b/tests/migration-stress/guestperf/shell.py @@ -131,6 +131,8 @@ class Shell(BaseShell): action="store_true") parser.add_argument("--multifd-channels", dest="multifd_channels", default=2, type=int) + parser.add_argument("--multifd-compression", dest="multifd_compression", + default="") parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, action="store_true") @@ -167,6 +169,7 @@ class Shell(BaseShell): multifd=args.multifd, multifd_channels=args.multifd_channels, + multifd_compression=args.multifd_compression, dirty_limit=args.dirty_limit, x_vcpu_dirty_limit_period=\ From 5984870e02aa6cf471bc9225ae91640b544b31c8 Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Fri, 14 Feb 2025 18:55:26 +0800 Subject: [PATCH 1730/2892] guestperf: Add test result data into report The migration result data is not included in the guestperf report information; include the result as a report entry so the developer can check whether the migration was successful after running guestperf. Signed-off-by: Hyman Huang Message-ID: <6303400c2983ffe5647f07caa6406f00ceae4581.1739530098.git.yong.huang@smartx.com> Signed-off-by: Fabiano Rosas --- tests/migration-stress/guestperf/engine.py | 10 ++++++++-- tests/migration-stress/guestperf/report.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/migration-stress/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py index e11f6a8496..d8462db765 100644 --- a/tests/migration-stress/guestperf/engine.py +++ b/tests/migration-stress/guestperf/engine.py @@ -24,7 +24,7 @@ import sys import time from guestperf.progress import Progress, ProgressStats -from guestperf.report import Report +from guestperf.report import Report, ReportResult from guestperf.timings import TimingRecord, Timings sys.path.append(os.path.join(os.path.dirname(__file__), @@ -276,7 +276,11 @@ class Engine(object): src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) sleep_secs -= 1 - return [progress_history, src_qemu_time, src_vcpu_time] + result = ReportResult() + if progress._status == "completed" and not paused: + result = ReportResult(True) + + return [progress_history, src_qemu_time, src_vcpu_time, result] if self._verbose and (loop % 20) == 0: print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( @@ -490,6 +494,7 @@ class Engine(object): progress_history = ret[0] qemu_timings = ret[1] vcpu_timings = ret[2] + result = ret[3] if uri[0:5] == "unix:" and os.path.exists(uri[5:]): os.remove(uri[5:]) @@ -509,6 +514,7 @@ class Engine(object): Timings(self._get_timings(src) + self._get_timings(dst)), Timings(qemu_timings), Timings(vcpu_timings), + result, self._binary, self._dst_host, self._kernel, self._initrd, self._transport, self._sleep) except Exception as e: diff --git a/tests/migration-stress/guestperf/report.py b/tests/migration-stress/guestperf/report.py index 1efd40c868..e135e01be6 100644 --- a/tests/migration-stress/guestperf/report.py +++ b/tests/migration-stress/guestperf/report.py @@ -24,6 +24,22 @@ from guestperf.scenario import Scenario from guestperf.progress import Progress from guestperf.timings import Timings +class ReportResult(object): + + def __init__(self, success=False): + self._success = success + + def serialize(self): + return { + "success": self._success, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["success"]) + + class Report(object): def __init__(self, @@ -33,6 +49,7 @@ class Report(object): guest_timings, qemu_timings, vcpu_timings, + result, binary, dst_host, kernel, @@ -46,6 +63,7 @@ class Report(object): self._guest_timings = guest_timings self._qemu_timings = qemu_timings self._vcpu_timings = vcpu_timings + self._result = result self._binary = binary self._dst_host = dst_host self._kernel = kernel @@ -61,6 +79,7 @@ class Report(object): "guest_timings": self._guest_timings.serialize(), "qemu_timings": self._qemu_timings.serialize(), "vcpu_timings": self._vcpu_timings.serialize(), + "result": self._result.serialize(), "binary": self._binary, "dst_host": self._dst_host, "kernel": self._kernel, @@ -78,6 +97,7 @@ class Report(object): Timings.deserialize(data["guest_timings"]), Timings.deserialize(data["qemu_timings"]), Timings.deserialize(data["vcpu_timings"]), + ReportResult.deserialize(data["result"]), data["binary"], data["dst_host"], data["kernel"], From 1e0d4eb4ee7c909323bffc39bc348eb3174b426b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 12 Apr 2024 00:33:30 -0700 Subject: [PATCH 1731/2892] backends/tpm: Use qemu_hexdump_line() to avoid sprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Using qemu_hexdump_line() both fixes the deprecation warning and simplifies the code base. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Berger [rth: Keep the linebreaks every 16 bytes] Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240412073346.458116-12-richard.henderson@linaro.org> [PMD: Rebased] --- backends/tpm/tpm_util.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index 3294625106..0a428eaf75 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "tpm_int.h" @@ -336,8 +337,8 @@ void tpm_sized_buffer_reset(TPMSizedBuffer *tsb) void tpm_util_show_buffer(const unsigned char *buffer, size_t buffer_size, const char *string) { - size_t len, i; - char *line_buffer, *p; + g_autoptr(GString) str = NULL; + size_t len, i, l; if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER_CONTENT)) { return; @@ -345,19 +346,14 @@ void tpm_util_show_buffer(const unsigned char *buffer, len = MIN(tpm_cmd_get_size(buffer), buffer_size); trace_tpm_util_show_buffer_header(string, len); - /* - * allocate enough room for 3 chars per buffer entry plus a - * newline after every 16 chars and a final null terminator. - */ - line_buffer = g_malloc(len * 3 + (len / 16) + 1); - - for (i = 0, p = line_buffer; i < len; i++) { - if (i && !(i % 16)) { - p += sprintf(p, "\n"); + for (i = 0; i < len; i += l) { + if (str) { + g_string_append_c(str, '\n'); } - p += sprintf(p, "%.2X ", buffer[i]); + l = MIN(len, 16); + str = qemu_hexdump_line(str, buffer, l, 1, 0); } - trace_tpm_util_show_buffer_content(line_buffer); - g_free(line_buffer); + g_string_ascii_up(str); + trace_tpm_util_show_buffer_content(str->str); } From 7f2626dc24198700683d264c265a2d337fff980b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Nov 2024 15:24:50 +0000 Subject: [PATCH 1732/2892] hw/arm/xlnx-zynqmp: Use &error_abort for programming errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a property value is static (not provided by QMP or CLI), error shouldn't happen, otherwise it is a programming error. Therefore simplify and use &error_abort as this can't fail. Reported-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20241108154317.12129-11-philmd@linaro.org> --- hw/arm/xlnx-zynqmp.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index bd5b0dd5e7..d6022ff2d3 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -689,16 +689,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) * - SDIO Specification Version 3.0 * - eMMC Specification Version 4.51 */ - if (!object_property_set_uint(sdhci, "sd-spec-version", 3, errp)) { - return; - } - if (!object_property_set_uint(sdhci, "capareg", SDHCI_CAPABILITIES, - errp)) { - return; - } - if (!object_property_set_uint(sdhci, "uhs", UHS_I, errp)) { - return; - } + object_property_set_uint(sdhci, "sd-spec-version", 3, &error_abort); + object_property_set_uint(sdhci, "capareg", SDHCI_CAPABILITIES, + &error_abort); + object_property_set_uint(sdhci, "uhs", UHS_I, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(sdhci), errp)) { return; } @@ -763,14 +757,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) xlnx_zynqmp_create_unimp_mmio(s); for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { - if (!object_property_set_uint(OBJECT(&s->gdma[i]), "bus-width", 128, - errp)) { - return; - } - if (!object_property_set_link(OBJECT(&s->gdma[i]), "dma", - OBJECT(system_memory), errp)) { - return; - } + object_property_set_uint(OBJECT(&s->gdma[i]), "bus-width", 128, + &error_abort); + object_property_set_link(OBJECT(&s->gdma[i]), "dma", + OBJECT(system_memory), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gdma[i]), errp)) { return; } @@ -811,10 +801,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) 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)) { - return; - } + object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma", + OBJECT(&s->qspi_dma), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) { return; } @@ -833,10 +821,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { - if (!object_property_set_link(OBJECT(&s->usb[i].sysbus_xhci), "dma", - OBJECT(system_memory), errp)) { - return; - } + object_property_set_link(OBJECT(&s->usb[i].sysbus_xhci), "dma", + OBJECT(system_memory), &error_abort); qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "intrs", 4); qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); From 1e71a9b1147145c9904b2ce5350c5591d5badb23 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Mon, 9 Dec 2024 21:36:28 +0100 Subject: [PATCH 1733/2892] hw/intc/apic: Fixes magic number use, removes outdated comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes replaces the use of an explicit literal constant for the APIC base address mask with the existing symbolic constant intended for this purpose. Additionally, we remove the comment about not being able to re-enable the APIC after disabling it. This is no longer the case after the APIC implementation's state machine was modified in 9.0. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241209203629.74436-11-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/apic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index d1d343d421..d18c1dbf2c 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -350,9 +350,8 @@ static int apic_set_base(APICCommonState *s, uint64_t val) return -1; } - s->apicbase = (val & 0xfffff000) | + s->apicbase = (val & MSR_IA32_APICBASE_BASE) | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); - /* if disabled, cannot be enabled again */ if (!(val & MSR_IA32_APICBASE_ENABLE)) { s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; cpu_clear_apic_feature(&s->cpu->env); From 0d2d00e57a55d3d8205923e60c2553d83d288ebb Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 10 Jan 2025 22:51:11 +0800 Subject: [PATCH 1734/2892] hw/core/machine: Reject thread level cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, neither i386 nor ARM have real hardware support for per- thread cache, and there is no clear demand for this specific cache topology. Additionally, since ARM even can't support this special cache topology in device tree, it is unnecessary to support it at this moment, even though per-thread cache might have potential scheduling benefits for VMs without CPU affinity. Therefore, disable thread-level cache topology in the general machine part. At present, i386 has not enabled SMP cache, so disabling the thread parameter does not pose compatibility issues. In the future, if there is a clear demand for this feature, the correct approach would be to add a new control field in MachineClass.smp_props and enable it only for the machines that require it. Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250110145115.1574345-2-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-smp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index b954eb8490..4e020c358b 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -321,6 +321,13 @@ bool machine_parse_smp_cache(MachineState *ms, return false; } + if (props->topology == CPU_TOPOLOGY_LEVEL_THREAD) { + error_setg(errp, + "%s level cache not supported by this machine", + CpuTopologyLevel_str(props->topology)); + return false; + } + if (!machine_check_topo_support(ms, props->topology, errp)) { return false; } From d4194e19cc202774251ae03a1658077fd954797f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 18:24:39 +0100 Subject: [PATCH 1735/2892] hw/sysbus: Use sizeof(BusState) in main_system_bus_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using the obscure system_bus_info.instance_size, directly use sizeof(BusState). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Message-Id: <20250125181343.59151-2-philmd@linaro.org> --- hw/core/sysbus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 9355849ff0..f713bbfe04 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -323,8 +323,8 @@ static void main_system_bus_create(void) * assign main_system_bus before qbus_init() * in order to make "if (bus != sysbus_get_default())" work */ - main_system_bus = g_malloc0(system_bus_info.instance_size); - qbus_init(main_system_bus, system_bus_info.instance_size, + main_system_bus = g_new0(BusState, 1); + qbus_init(main_system_bus, sizeof(BusState), TYPE_SYSTEM_BUS, NULL, "main-system-bus"); OBJECT(main_system_bus)->free = g_free; } From 45683d1e7c622b1e9c0a41054847f519cd2aaa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 18:24:31 +0100 Subject: [PATCH 1736/2892] hw/sysbus: Declare QOM types using DEFINE_TYPES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple QOM types are registered in the same file, it is simpler to use the the DEFINE_TYPES() macro. In particular because type array declared with such macro are easier to review. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Reviewed-by: Bernhard Beschow Message-Id: <20250125181343.59151-3-philmd@linaro.org> --- hw/core/sysbus.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index f713bbfe04..075c7dfd69 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu/module.h" #include "hw/sysbus.h" #include "monitor/monitor.h" #include "exec/address-spaces.h" @@ -80,13 +79,6 @@ static void system_bus_class_init(ObjectClass *klass, void *data) k->get_fw_dev_path = sysbus_get_fw_dev_path; } -static const TypeInfo system_bus_info = { - .name = TYPE_SYSTEM_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(BusState), - .class_init = system_bus_class_init, -}; - /* Check whether an IRQ source exists */ bool sysbus_has_irq(SysBusDevice *dev, int n) { @@ -306,15 +298,6 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data) k->user_creatable = false; } -static const TypeInfo sysbus_device_type_info = { - .name = TYPE_SYS_BUS_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SysBusDevice), - .abstract = true, - .class_size = sizeof(SysBusDeviceClass), - .class_init = sysbus_device_class_init, -}; - static BusState *main_system_bus; static void main_system_bus_create(void) @@ -337,10 +320,21 @@ BusState *sysbus_get_default(void) return main_system_bus; } -static void sysbus_register_types(void) -{ - type_register_static(&system_bus_info); - type_register_static(&sysbus_device_type_info); -} +static const TypeInfo sysbus_types[] = { + { + .name = TYPE_SYSTEM_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(BusState), + .class_init = system_bus_class_init, + }, + { + .name = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SysBusDevice), + .abstract = true, + .class_size = sizeof(SysBusDeviceClass), + .class_init = sysbus_device_class_init, + }, +}; -type_init(sysbus_register_types) +DEFINE_TYPES(sysbus_types) From 47dfd350fbf80bdfc7dcc102974fad328bf3e993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 14:36:42 +0100 Subject: [PATCH 1737/2892] hw/sysbus: Introduce TYPE_DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some TYPE_SYS_BUS_DEVICEs can be optionally dynamically plugged on the TYPE_PLATFORM_BUS_DEVICE. Rather than sometimes noting that with comment around the 'user_creatable = true' line in each DeviceRealize handler, introduce an abstract TYPE_DYNAMIC_SYS_BUS_DEVICE class. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Message-Id: <20250125181343.59151-4-philmd@linaro.org> --- hw/core/sysbus.c | 14 ++++++++++++++ include/hw/sysbus.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 075c7dfd69..98819d5dc6 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -320,6 +320,14 @@ BusState *sysbus_get_default(void) return main_system_bus; } +static void dynamic_sysbus_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + + k->user_creatable = true; + k->hotpluggable = false; +} + static const TypeInfo sysbus_types[] = { { .name = TYPE_SYSTEM_BUS, @@ -335,6 +343,12 @@ static const TypeInfo sysbus_types[] = { .class_size = sizeof(SysBusDeviceClass), .class_init = sysbus_device_class_init, }, + { + .name = TYPE_DYNAMIC_SYS_BUS_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = dynamic_sysbus_device_class_init, + .abstract = true, + } }; DEFINE_TYPES(sysbus_types) diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index c9b1e0e90e..81bbda10d3 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -19,6 +19,8 @@ DECLARE_INSTANCE_CHECKER(BusState, SYSTEM_BUS, OBJECT_DECLARE_TYPE(SysBusDevice, SysBusDeviceClass, SYS_BUS_DEVICE) +#define TYPE_DYNAMIC_SYS_BUS_DEVICE "dynamic-sysbus-device" + /** * SysBusDeviceClass: * From 341df541dce236ab8f68ac9a3a0d63897767215b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 13:48:29 +0100 Subject: [PATCH 1738/2892] hw/vfio: Have VFIO_PLATFORM devices inherit from DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not explain why VFIO_PLATFORM devices are user_creatable, have them inherit TYPE_DYNAMIC_SYS_BUS_DEVICE, to make explicit that they can optionally be plugged on TYPE_PLATFORM_BUS_DEVICE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Message-Id: <20250125181343.59151-5-philmd@linaro.org> --- hw/vfio/amd-xgbe.c | 2 -- hw/vfio/calxeda-xgmac.c | 2 -- hw/vfio/platform.c | 4 +--- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index 96bd608b8d..aaa96903db 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -41,8 +41,6 @@ static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) &vcxc->parent_realize); dc->desc = "VFIO AMD XGBE"; dc->vmsd = &vfio_platform_amd_xgbe_vmstate; - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_amd_xgbe_dev_info = { diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index 87c382e736..b016d42b49 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -41,8 +41,6 @@ static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) &vcxc->parent_realize); dc->desc = "VFIO Calxeda XGMAC"; dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_calxeda_xgmac_dev_info = { diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 1070a2113a..f491f4dc95 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -672,13 +672,11 @@ static void vfio_platform_class_init(ObjectClass *klass, void *data) dc->desc = "VFIO-based platform device assignment"; sbc->connect_irq_notifier = vfio_start_irqfd_injection; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_platform_dev_info = { .name = TYPE_VFIO_PLATFORM, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(VFIOPlatformDevice), .instance_init = vfio_platform_instance_init, .class_init = vfio_platform_class_init, From 8abda739f35373ba0525be4bf6df4f69e31241b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 18:36:43 +0100 Subject: [PATCH 1739/2892] hw/display: Have RAMFB device inherit from DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the RAM FB device can be optionally plugged on the TYPE_PLATFORM_BUS_DEVICE, have it inherit TYPE_DYNAMIC_SYS_BUS_DEVICE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Message-Id: <20250125181343.59151-6-philmd@linaro.org> --- hw/display/ramfb-standalone.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 6c35028965..1be106b57f 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -72,13 +72,12 @@ static void ramfb_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &ramfb_dev_vmstate; dc->realize = ramfb_realizefn; dc->desc = "ram framebuffer standalone device"; - dc->user_creatable = true; device_class_set_props(dc, ramfb_properties); } static const TypeInfo ramfb_info = { .name = TYPE_RAMFB_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(RAMFBStandaloneState), .class_init = ramfb_class_initfn, }; From 1e2f32bf71cd93788aa71859d0145d9a56667310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 18:36:50 +0100 Subject: [PATCH 1740/2892] hw/i386: Have X86_IOMMU devices inherit from DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not explain why _X86_IOMMU devices are user_creatable, have them inherit TYPE_DYNAMIC_SYS_BUS_DEVICE, to explicit they can optionally be plugged on TYPE_PLATFORM_BUS_DEVICE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Message-Id: <20250125181343.59151-7-philmd@linaro.org> --- hw/i386/amd_iommu.c | 2 -- hw/i386/intel_iommu.c | 2 -- hw/i386/x86-iommu.c | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 6b13ce894b..e8e084c7cf 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1687,8 +1687,6 @@ static void amdvi_sysbus_class_init(ObjectClass *klass, void *data) dc->hotpluggable = false; dc_class->realize = amdvi_sysbus_realize; dc_class->int_remap = amdvi_int_remap; - /* Supported by the pc-q35-* machine types */ - dc->user_creatable = true; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "AMD IOMMU (AMD-Vi) DMA Remapping device"; device_class_set_props(dc, amdvi_properties); diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index f366c223d0..7fde0603bf 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4871,8 +4871,6 @@ static void vtd_class_init(ObjectClass *klass, void *data) dc->hotpluggable = false; x86_class->realize = vtd_realize; x86_class->int_remap = vtd_int_remap; - /* Supported by the pc-q35-* machine types */ - dc->user_creatable = true; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "Intel IOMMU (VT-d) DMA Remapping device"; } diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index fed34b2fcf..5cdd165af0 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -146,7 +146,7 @@ bool x86_iommu_ir_supported(X86IOMMUState *s) static const TypeInfo x86_iommu_info = { .name = TYPE_X86_IOMMU_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(X86IOMMUState), .class_init = x86_iommu_class_init, .class_size = sizeof(X86IOMMUClass), From 4b2e34d9ef2d66811e7bc36a637a0701efc05d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 18:36:57 +0100 Subject: [PATCH 1741/2892] hw/net: Have eTSEC device inherit from DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the network eTSEC device can be optionally plugged on the TYPE_PLATFORM_BUS_DEVICE, have it inherit TYPE_DYNAMIC_SYS_BUS_DEVICE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Tested-by: Bernhard Beschow Acked-by: Bernhard Beschow Message-Id: <20250125181343.59151-8-philmd@linaro.org> --- hw/net/fsl_etsec/etsec.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 781b900395..3ce4fa2662 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -425,14 +425,12 @@ static void etsec_class_init(ObjectClass *klass, void *data) dc->realize = etsec_realize; device_class_set_legacy_reset(dc, etsec_reset); device_class_set_props(dc, etsec_properties); - /* Supported by ppce500 machine */ - dc->user_creatable = true; } static const TypeInfo etsec_types[] = { { .name = TYPE_ETSEC_COMMON, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(eTSEC), .class_init = etsec_class_init, .instance_init = etsec_instance_init, From c10f4c744a7cac8be38158b0793ccf8d754cecd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 14:43:24 +0100 Subject: [PATCH 1742/2892] hw/tpm: Have TPM TIS sysbus device inherit from DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the TPM TIS sysbus device can be optionally plugged on the TYPE_PLATFORM_BUS_DEVICE, have it inherit TYPE_DYNAMIC_SYS_BUS_DEVICE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alexander Graf Reviewed-by: Clément Mathieu--Drif Reviewed-by: Stefan Berger Message-Id: <20250125181343.59151-9-philmd@linaro.org> --- hw/tpm/tpm_tis_sysbus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index ee0bfe9538..4f187690a2 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -133,7 +133,6 @@ static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_tpm_tis_sysbus; tc->model = TPM_MODEL_TPM_TIS; dc->realize = tpm_tis_sysbus_realizefn; - dc->user_creatable = true; device_class_set_legacy_reset(dc, tpm_tis_sysbus_reset); tc->request_completed = tpm_tis_sysbus_request_completed; tc->get_version = tpm_tis_sysbus_get_tpm_version; @@ -142,7 +141,7 @@ static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) static const TypeInfo tpm_tis_sysbus_info = { .name = TYPE_TPM_TIS_SYSBUS, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(TPMStateSysBus), .instance_init = tpm_tis_sysbus_initfn, .class_init = tpm_tis_sysbus_class_init, From 83f0f363e4a24b40142079ab1b328ed653f7d14b Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jan 2025 10:41:29 +0100 Subject: [PATCH 1743/2892] hw/xen: Prefer QOM cast for XenLegacyDevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the code less sensitive regarding changes in the class hierarchy which will be performed in the next patch. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250127094129.15941-1-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/xen-usb.c | 6 +++--- hw/xen/xen-legacy-backend.c | 2 +- hw/xen/xen_pvdev.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 13b065b0fa..fa46a7da01 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -755,10 +755,10 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, qdict = qdict_new(); qdict_put_str(qdict, "driver", "usb-host"); - tmp = g_strdup_printf("%s.0", usbif->xendev.qdev.id); + tmp = g_strdup_printf("%s.0", DEVICE(&usbif->xendev)->id); qdict_put_str(qdict, "bus", tmp); g_free(tmp); - tmp = g_strdup_printf("%s-%u", usbif->xendev.qdev.id, port); + tmp = g_strdup_printf("%s-%u", DEVICE(&usbif->xendev)->id, port); qdict_put_str(qdict, "id", tmp); g_free(tmp); qdict_put_int(qdict, "port", port); @@ -1022,7 +1022,7 @@ static void usbback_alloc(struct XenLegacyDevice *xendev) usbif = container_of(xendev, struct usbback_info, xendev); usb_bus_new(&usbif->bus, sizeof(usbif->bus), &xen_usb_bus_ops, - DEVICE(&xendev->qdev)); + DEVICE(xendev)); for (i = 0; i < USBBACK_MAXPORTS; i++) { p = &(usbif->ports[i].port); usb_register_port(&usbif->bus, p, usbif, i, &xen_usb_port_ops, diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 118c571b3a..ca2fe0e6b3 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -163,7 +163,7 @@ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, /* init new xendev */ xendev = g_malloc0(ops->size); - object_initialize(&xendev->qdev, ops->size, TYPE_XENBACKEND); + object_initialize(xendev, ops->size, TYPE_XENBACKEND); OBJECT(xendev)->free = g_free; qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev), &error_fatal); diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index c9143ba259..fe95b62d13 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -273,7 +273,7 @@ void xen_pv_del_xendev(struct XenLegacyDevice *xendev) QTAILQ_REMOVE(&xendevs, xendev, next); - qdev_unplug(&xendev->qdev, NULL); + qdev_unplug(DEVICE(xendev), NULL); } void xen_pv_insert_xendev(struct XenLegacyDevice *xendev) From 250e797ceadad60ee7ebfdb92c76ba2057687597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Jan 2025 14:52:12 +0100 Subject: [PATCH 1744/2892] hw/xen: Have legacy Xen backend inherit from DYNAMIC_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the legacy Xen backend devices can optionally be plugged on the TYPE_PLATFORM_BUS_DEVICE, have it inherit TYPE_DYNAMIC_SYS_BUS_DEVICE. Remove the implicit TYPE_XENSYSDEV instance_size. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alexander Graf Tested-by: Bernhard Beschow Reviewed-by: Bernhard Beschow Message-Id: <20250125181343.59151-10-philmd@linaro.org> --- hw/xen/xen-legacy-backend.c | 7 ++----- include/hw/xen/xen_pvdev.h | 5 +++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index ca2fe0e6b3..bf58db0ca6 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -640,16 +640,14 @@ static void xendev_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_MISC, dc->categories); - /* xen-backend devices can be plugged/unplugged dynamically */ - dc->user_creatable = true; dc->bus_type = TYPE_XENSYSBUS; } static const TypeInfo xendev_type_info = { .name = TYPE_XENBACKEND, - .parent = TYPE_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .class_init = xendev_class_init, - .instance_size = sizeof(struct XenLegacyDevice), + .instance_size = sizeof(XenLegacyDevice), }; static void xen_sysbus_class_init(ObjectClass *klass, void *data) @@ -672,7 +670,6 @@ static const TypeInfo xensysbus_info = { static const TypeInfo xensysdev_info = { .name = TYPE_XENSYSDEV, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), }; static void xenbe_register_types(void) diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index 0c98444047..629bec90d0 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -1,7 +1,7 @@ #ifndef QEMU_HW_XEN_PVDEV_H #define QEMU_HW_XEN_PVDEV_H -#include "hw/qdev-core.h" +#include "hw/sysbus.h" #include "hw/xen/xen_backend_ops.h" /* ------------------------------------------------------------- */ @@ -32,7 +32,8 @@ struct XenDevOps { }; struct XenLegacyDevice { - DeviceState qdev; + SysBusDevice parent_obj; + const char *type; int dom; int dev; From e3660f60dca85de599c913fda1d156f20495b45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Nov 2024 15:16:54 +0100 Subject: [PATCH 1745/2892] hw/boards: Convert no_sdcard flag to OnOffAuto tri-state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MachineClass::no_sdcard is initialized as false by default. To catch all uses, convert it to a tri-state, having the current default (false) becoming AUTO. No logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250204200934.65279-2-philmd@linaro.org> --- hw/arm/xilinx_zynq.c | 2 +- hw/core/null-machine.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- include/hw/boards.h | 2 +- system/vl.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 8477b82874..12418094f9 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -463,7 +463,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; mc->init = zynq_init; mc->max_cpus = ZYNQ_MAX_CPUS; - mc->no_sdcard = 1; + mc->no_sdcard = ON_OFF_AUTO_ON; mc->ignore_memory_transaction_failures = true; mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "zynq.ext_ram"; diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index f586a4bef5..b93056c0f7 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -53,7 +53,7 @@ static void machine_none_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->no_sdcard = 1; + mc->no_sdcard = ON_OFF_AUTO_ON; } DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index d9e683c5b4..5f78c8d20f 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -817,7 +817,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_cdrom = 1; mc->no_floppy = 1; mc->no_parallel = 1; - mc->no_sdcard = 1; + mc->no_sdcard = ON_OFF_AUTO_ON; mc->max_cpus = S390_MAX_CPUS; mc->has_hotpluggable_cpus = true; mc->smp_props.books_supported = true; diff --git a/include/hw/boards.h b/include/hw/boards.h index e1f41b2a53..d61b0a4778 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -283,9 +283,9 @@ struct MachineClass { no_parallel:1, no_floppy:1, no_cdrom:1, - no_sdcard:1, pci_allow_0_address:1, legacy_fw_cfg_order:1; + OnOffAuto no_sdcard; bool is_default; const char *default_machine_opts; const char *default_boot_order; diff --git a/system/vl.c b/system/vl.c index 9c6942c6cf..04ce290997 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1346,7 +1346,7 @@ static void qemu_disable_default_devices(void) if (!has_defaults || machine_class->no_cdrom) { default_cdrom = 0; } - if (!has_defaults || machine_class->no_sdcard) { + if (!has_defaults || machine_class->no_sdcard == ON_OFF_AUTO_ON) { default_sdcard = 0; } if (!has_defaults) { From 8a2f1f921cc84cae3aa54c29e24e8c1defc9ef34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Nov 2024 15:26:15 +0100 Subject: [PATCH 1746/2892] hw/boards: Explicit no_sdcard=false as ON_OFF_AUTO_OFF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update MachineClass::no_sdcard default implicit AUTO initialization to explicit OFF. This flag is consumed in system/vl.c::qemu_disable_default_devices(). Use this place to assert we don't have anymore AUTO state. In hw/ppc/e500.c we add the ppce500_machine_class_init() method to initialize once all the inherited classes. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250204200934.65279-3-philmd@linaro.org> --- hw/alpha/dp264.c | 1 + hw/arm/aspeed.c | 21 +++++++++++++++++++++ hw/arm/b-l475e-iot01a.c | 1 + hw/arm/bananapi_m2u.c | 1 + hw/arm/collie.c | 1 + hw/arm/cubieboard.c | 1 + hw/arm/digic_boards.c | 1 + hw/arm/exynos4_boards.c | 2 ++ hw/arm/fby35.c | 1 + hw/arm/highbank.c | 2 ++ hw/arm/imx25_pdk.c | 1 + hw/arm/integratorcp.c | 1 + hw/arm/kzm.c | 1 + hw/arm/mcimx6ul-evk.c | 1 + hw/arm/mcimx7d-sabre.c | 1 + hw/arm/microbit.c | 1 + hw/arm/mps2-tz.c | 4 ++++ hw/arm/mps2.c | 4 ++++ hw/arm/mps3r.c | 1 + hw/arm/msf2-som.c | 1 + hw/arm/musca.c | 2 ++ hw/arm/musicpal.c | 1 + hw/arm/netduino2.c | 1 + hw/arm/netduinoplus2.c | 1 + hw/arm/npcm7xx_boards.c | 5 +++++ hw/arm/olimex-stm32-h405.c | 1 + hw/arm/omap_sx1.c | 2 ++ hw/arm/orangepi.c | 1 + hw/arm/raspi.c | 5 +++++ hw/arm/raspi4b.c | 1 + hw/arm/realview.c | 4 ++++ hw/arm/sabrelite.c | 1 + hw/arm/sbsa-ref.c | 1 + hw/arm/stellaris.c | 2 ++ hw/arm/stm32vldiscovery.c | 1 + hw/arm/versatilepb.c | 2 ++ hw/arm/vexpress.c | 2 ++ hw/arm/virt.c | 1 + hw/arm/xen-pvh.c | 1 + hw/arm/xlnx-versal-virt.c | 1 + hw/arm/xlnx-zcu102.c | 1 + hw/avr/arduino.c | 1 + hw/hppa/machine.c | 2 ++ hw/i386/pc.c | 1 + hw/i386/x86.c | 1 + hw/i386/xen/xen-pvh.c | 1 + hw/loongarch/virt.c | 1 + hw/m68k/an5206.c | 1 + hw/m68k/mcf5208.c | 1 + hw/m68k/next-cube.c | 1 + hw/m68k/q800.c | 1 + hw/m68k/virt.c | 1 + hw/microblaze/petalogix_ml605_mmu.c | 1 + hw/microblaze/petalogix_s3adsp1800_mmu.c | 1 + hw/microblaze/xlnx-zynqmp-pmu.c | 1 + hw/mips/boston.c | 1 + hw/mips/fuloong2e.c | 1 + hw/mips/jazz.c | 2 ++ hw/mips/loongson3_virt.c | 1 + hw/mips/malta.c | 1 + hw/mips/mipssim.c | 1 + hw/openrisc/openrisc_sim.c | 1 + hw/openrisc/virt.c | 1 + hw/ppc/amigaone.c | 1 + hw/ppc/e500plat.c | 1 + hw/ppc/mac_newworld.c | 1 + hw/ppc/mac_oldworld.c | 1 + hw/ppc/mpc8544ds.c | 1 + hw/ppc/pegasos2.c | 1 + hw/ppc/pnv.c | 1 + hw/ppc/ppc405_boards.c | 1 + hw/ppc/ppc440_bamboo.c | 1 + hw/ppc/prep.c | 1 + hw/ppc/sam460ex.c | 1 + hw/ppc/spapr.c | 1 + hw/ppc/virtex_ml507.c | 1 + hw/remote/machine.c | 1 + hw/riscv/microchip_pfsoc.c | 1 + hw/riscv/opentitan.c | 1 + hw/riscv/shakti_c.c | 1 + hw/riscv/sifive_e.c | 1 + hw/riscv/sifive_u.c | 1 + hw/riscv/spike.c | 1 + hw/riscv/virt.c | 1 + hw/rx/rx-gdbsim.c | 1 + hw/sh4/r2d.c | 1 + hw/sparc/leon3.c | 1 + hw/sparc/sun4m.c | 1 + hw/sparc64/niagara.c | 1 + hw/sparc64/sun4u.c | 2 ++ hw/tricore/triboard.c | 1 + hw/tricore/tricore_testboard.c | 1 + hw/xen/xen-pvh-common.c | 1 + hw/xenpv/xen_machine_pv.c | 1 + hw/xtensa/sim.c | 1 + hw/xtensa/virt.c | 1 + hw/xtensa/xtfpga.c | 8 ++++++++ system/vl.c | 1 + 98 files changed, 152 insertions(+) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 570ea9edf2..b11e527be9 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -213,6 +213,7 @@ static void clipper_machine_init(MachineClass *mc) mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67"); mc->default_ram_id = "ram"; mc->default_nic = "e1000"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index d9418e2b9f..9d9c55adcd 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1253,6 +1253,7 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = palmetto_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1269,6 +1270,7 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 1; amc->i2c_init = quanta_q71l_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 128 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1287,6 +1289,7 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1305,6 +1308,7 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1321,6 +1325,7 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1338,6 +1343,7 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = yosemitev2_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1354,6 +1360,7 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = romulus_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1371,6 +1378,7 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = tiogapass_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1387,6 +1395,7 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = sonorapass_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1403,6 +1412,7 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = witherspoon_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1423,6 +1433,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) ASPEED_MAC3_ON; amc->sdhci_wp_inverted = true; amc->i2c_init = ast2600_evb_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1441,6 +1452,7 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = g220a_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1024 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1458,6 +1470,7 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = fp5280g2_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1476,6 +1489,7 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = rainier_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1498,6 +1512,7 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fuji_bmc_i2c_init; amc->uart_default = ASPEED_DEV_UART1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1518,6 +1533,7 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = BLETCHLEY_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1559,6 +1575,7 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fby35_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; /* FIXME: Replace this macro with something more general */ mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); @@ -1641,6 +1658,7 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, amc->spi_model = "w25q256"; amc->num_cs = 2; amc->macs_mask = 0; + mc->no_sdcard = ON_OFF_AUTO_OFF; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1669,6 +1687,7 @@ static void aspeed_machine_ast2700_evb_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1689,6 +1708,7 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_bmc_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1708,6 +1728,7 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_firework_i2c_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index c9a5209216..d43c84435b 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -120,6 +120,7 @@ static void bl475e_machine_init(ObjectClass *oc, void *data) mc->desc = "B-L475E-IOT01A Discovery Kit (Cortex-M4)"; mc->init = bl475e_init; mc->valid_cpu_types = machine_valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 0a4b6f29b1..3da6ec4a03 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -141,6 +141,7 @@ static void bpim2u_machine_init(MachineClass *mc) mc->valid_cpu_types = valid_cpu_types; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "bpim2u.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("bpim2u", bpim2u_machine_init) diff --git a/hw/arm/collie.c b/hw/arm/collie.c index eaa5c52d45..80bf12246a 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -79,6 +79,7 @@ static void collie_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("sa1110"); mc->default_ram_size = RAM_SIZE; mc->default_ram_id = "strongarm.sdram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo collie_machine_typeinfo = { diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index b976727eef..11d896f832 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -122,6 +122,7 @@ static void cubieboard_machine_init(MachineClass *mc) mc->units_per_default_bus = 1; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "cubieboard.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("cubieboard", cubieboard_machine_init) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 2492fafeb8..a6ccf7ef9b 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -143,6 +143,7 @@ static void canon_a1100_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; mc->default_ram_size = 64 * MiB; mc->default_ram_id = "ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init) diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 2410e2a28e..63e86e2c60 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -165,6 +165,7 @@ static void nuri_class_init(ObjectClass *oc, void *data) mc->min_cpus = EXYNOS4210_NCPUS; mc->default_cpus = EXYNOS4210_NCPUS; mc->ignore_memory_transaction_failures = true; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo nuri_type = { @@ -184,6 +185,7 @@ static void smdkc210_class_init(ObjectClass *oc, void *data) mc->min_cpus = EXYNOS4210_NCPUS; mc->default_cpus = EXYNOS4210_NCPUS; mc->ignore_memory_transaction_failures = true; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo smdkc210_type = { diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index 83d08e578b..9b448bf764 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -170,6 +170,7 @@ static void fby35_class_init(ObjectClass *oc, void *data) mc->init = fby35_init; mc->no_floppy = 1; mc->no_cdrom = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->min_cpus = mc->max_cpus = mc->default_cpus = 3; object_class_property_add_bool(oc, "execute-in-place", diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 495704d972..97477571e6 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -357,6 +357,7 @@ static void highbank_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo highbank_type = { @@ -381,6 +382,7 @@ static void midway_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo midway_type = { diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index c9c2e5dd3b..8f89e03332 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -147,6 +147,7 @@ static void imx25_pdk_machine_init(MachineClass *mc) mc->init = imx25_pdk_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "imx25.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 4e1b8627d3..905a7c2aec 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -688,6 +688,7 @@ static void integratorcp_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "integrator.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 08d2b3025c..45b3b08eb8 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -137,6 +137,7 @@ static void kzm_machine_init(MachineClass *mc) mc->init = kzm_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "kzm.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("kzm", kzm_machine_init) diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 690cb64ef3..49520b47f1 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -74,5 +74,6 @@ static void mcimx6ul_evk_machine_init(MachineClass *mc) mc->init = mcimx6ul_evk_init; mc->max_cpus = FSL_IMX6UL_NUM_CPUS; mc->default_ram_id = "mcimx6ul-evk.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("mcimx6ul-evk", mcimx6ul_evk_machine_init) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index b3e8e50779..da32fdd29d 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -74,5 +74,6 @@ static void mcimx7d_sabre_machine_init(MachineClass *mc) mc->init = mcimx7d_sabre_init; mc->max_cpus = FSL_IMX7_NUM_CPUS; mc->default_ram_id = "mcimx7d-sabre.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init) diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 3f56fb45ce..9d32ae5bd5 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -67,6 +67,7 @@ static void microbit_machine_class_init(ObjectClass *oc, void *data) mc->desc = "BBC micro:bit (Cortex-M0)"; mc->init = microbit_init; mc->max_cpus = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo microbit_info = { diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 13ed868b6b..1062612036 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1320,6 +1320,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41045050; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1354,6 +1355,7 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN521; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41045210; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1388,6 +1390,7 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN524; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41045240; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1427,6 +1430,7 @@ static void mps3tz_an547_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN547; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m55"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41055470; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = 25 * 1000 * 1000; /* 25MHz */ diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 3f8db0cab6..2bce37644e 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -487,6 +487,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41043850; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -506,6 +507,7 @@ static void mps2_an386_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN386; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41043860; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -525,6 +527,7 @@ static void mps2_an500_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN500; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m7"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41045000; mmc->psram_base = 0x60000000; mmc->ethernet_base = 0xa0000000; @@ -544,6 +547,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->scc_id = 0x41045110; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index 1bddb5e822..ae70ebe860 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -618,6 +618,7 @@ static void mps3r_an536_class_init(ObjectClass *oc, void *data) mc->max_cpus = 2; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-r52"); mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->raminfo = an536_raminfo; mps3r_set_default_ram_info(mmc); } diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 9b20f1e2c9..349a96a0b5 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -106,6 +106,7 @@ static void emcraft_sf2_machine_init(MachineClass *mc) mc->desc = "SmartFusion2 SOM kit from Emcraft (M2S010)"; mc->init = emcraft_sf2_s2s010_init; mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("emcraft-sf2", emcraft_sf2_machine_init) diff --git a/hw/arm/musca.c b/hw/arm/musca.c index e9c092abc3..89451b9684 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -615,6 +615,7 @@ static void musca_a_class_init(ObjectClass *oc, void *data) MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); mc->desc = "ARM Musca-A board (dual Cortex-M33)"; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->type = MUSCA_A; mmc->init_svtor = 0x10200000; mmc->sram_addr_width = 15; @@ -629,6 +630,7 @@ static void musca_b1_class_init(ObjectClass *oc, void *data) MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); mc->desc = "ARM Musca-B1 board (dual Cortex-M33)"; + mc->no_sdcard = ON_OFF_AUTO_OFF; mmc->type = MUSCA_B1; /* * This matches the DAPlink firmware which boots from QSPI. There diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 48a32c2407..d5ebfabe3f 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1342,6 +1342,7 @@ static void musicpal_machine_init(MachineClass *mc) mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_size = MP_RAM_DEFAULT_SIZE; mc->default_ram_id = "musicpal.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index df793c77fe..9ca0ee6a34 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -63,6 +63,7 @@ static void netduino2_machine_init(MachineClass *mc) mc->init = netduino2_init; mc->valid_cpu_types = valid_cpu_types; mc->ignore_memory_transaction_failures = true; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("netduino2", netduino2_machine_init) diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index 81b6334cf7..abe6060765 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -63,6 +63,7 @@ static void netduinoplus2_machine_init(MachineClass *mc) mc->desc = "Netduino Plus 2 Machine (Cortex-M4)"; mc->init = netduinoplus2_init; mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("netduinoplus2", netduinoplus2_machine_init) diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 7727e0dc4b..38b8f02364 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -481,6 +481,7 @@ static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Nuvoton NPCM750 Evaluation Board (Cortex-A9)"; mc->init = npcm750_evb_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; }; @@ -493,6 +494,7 @@ static void gsj_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Quanta GSJ (Cortex-A9)"; mc->init = quanta_gsj_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 512 * MiB; }; @@ -505,6 +507,7 @@ static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Quanta GBS (Cortex-A9)"; mc->init = quanta_gbs_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; } @@ -517,6 +520,7 @@ static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Kudo BMC (Cortex-A9)"; mc->init = kudo_bmc_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; }; @@ -529,6 +533,7 @@ static void mori_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Mori BMC (Cortex-A9)"; mc->init = mori_bmc_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_size = 1 * GiB; } diff --git a/hw/arm/olimex-stm32-h405.c b/hw/arm/olimex-stm32-h405.c index 1f15620f9f..01ae12fa4a 100644 --- a/hw/arm/olimex-stm32-h405.c +++ b/hw/arm/olimex-stm32-h405.c @@ -66,6 +66,7 @@ static void olimex_stm32_h405_machine_init(MachineClass *mc) mc->desc = "Olimex STM32-H405 (Cortex-M4)"; mc->init = olimex_stm32_h405_init; mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 623ebd6639..8170669db1 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -216,6 +216,7 @@ static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo sx1_machine_v2_type = { @@ -234,6 +235,7 @@ static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo sx1_machine_v1_type = { diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 77e328191d..76ab214853 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -121,6 +121,7 @@ static void orangepi_machine_init(MachineClass *mc) mc->valid_cpu_types = valid_cpu_types; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "orangepi.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("orangepi-pc", orangepi_machine_init) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index a7a662f40d..176c324cf8 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -342,6 +342,7 @@ static void raspi0_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->no_sdcard = ON_OFF_AUTO_OFF; rmc->board_rev = 0x920092; /* Revision 1.2 */ raspi_machine_class_init(mc, rmc->board_rev); }; @@ -351,6 +352,7 @@ static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->no_sdcard = ON_OFF_AUTO_OFF; rmc->board_rev = 0x900021; /* Revision 1.1 */ raspi_machine_class_init(mc, rmc->board_rev); }; @@ -360,6 +362,7 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->no_sdcard = ON_OFF_AUTO_OFF; rmc->board_rev = 0xa21041; raspi_machine_class_init(mc, rmc->board_rev); }; @@ -370,6 +373,7 @@ static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->no_sdcard = ON_OFF_AUTO_OFF; rmc->board_rev = 0x9020e0; /* Revision 1.0 */ raspi_machine_class_init(mc, rmc->board_rev); }; @@ -379,6 +383,7 @@ static void raspi3b_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->no_sdcard = ON_OFF_AUTO_OFF; rmc->board_rev = 0xa02082; raspi_machine_class_init(mc, rmc->board_rev); }; diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 1264e0d6ee..37eef37888 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -118,6 +118,7 @@ static void raspi4b_machine_class_init(ObjectClass *oc, void *data) rmc->board_rev = 0xb03115; /* Revision 1.5, 2 Gb RAM */ #endif raspi_machine_class_common_init(mc, rmc->board_rev); + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->init = raspi4b_machine_init; } diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 9900a98f3b..4bc8f3956f 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -415,6 +415,7 @@ static void realview_eb_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } @@ -435,6 +436,7 @@ static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm11mpcore"); + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } @@ -453,6 +455,7 @@ static void realview_pb_a8_class_init(ObjectClass *oc, void *data) mc->init = realview_pb_a8_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a8"); + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } @@ -472,6 +475,7 @@ static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 1eb47042ec..8d57653ab3 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -110,6 +110,7 @@ static void sabrelite_machine_init(MachineClass *mc) mc->max_cpus = FSL_IMX6_NUM_CPUS; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "sabrelite.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("sabrelite", sabrelite_machine_init) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index e720de3064..41dba85f44 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -900,6 +900,7 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) mc->minimum_page_bits = 12; mc->block_default_type = IF_IDE; mc->no_cdrom = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_nic = "e1000e"; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "sbsa-ref.ram"; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 47c1cfa048..ef3fd508ba 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1421,6 +1421,7 @@ static void lm3s811evb_class_init(ObjectClass *oc, void *data) mc->init = lm3s811evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo lm3s811evb_type = { @@ -1441,6 +1442,7 @@ static void lm3s6965evb_class_init(ObjectClass *oc, void *data) mc->init = lm3s6965evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo lm3s6965evb_type = { diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index e6c1f5b8d7..01b4afcb1a 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -66,6 +66,7 @@ static void stm32vldiscovery_machine_init(MachineClass *mc) mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; mc->init = stm32vldiscovery_init; mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index bc4522989e..def3da4a34 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -419,6 +419,7 @@ static void versatilepb_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "versatile.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } @@ -439,6 +440,7 @@ static void versatileab_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "versatile.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index b886d16c02..06ec78e6eb 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -803,6 +803,7 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Versatile Express for Cortex-A9"; mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; vmc->daughterboard = &a9_daughterboard; } @@ -818,6 +819,7 @@ static void vexpress_a15_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Versatile Express for Cortex-A15"; mc->valid_cpu_types = valid_cpu_types; + mc->no_sdcard = ON_OFF_AUTO_OFF; vmc->daughterboard = &a15_daughterboard; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4a5a9666e9..e2ac6ce464 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3125,6 +3125,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) #endif mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->pci_allow_0_address = true; /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */ mc->minimum_page_bits = 12; diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c index d1509bd235..e49ab0e7f5 100644 --- a/hw/arm/xen-pvh.c +++ b/hw/arm/xen-pvh.c @@ -75,6 +75,7 @@ static void xen_arm_machine_class_init(ObjectClass *oc, void *data) * mc->max_cpus, QEMU will bail out with an error message. */ mc->max_cpus = GUEST_MAX_VCPUS; + mc->no_sdcard = ON_OFF_AUTO_OFF; /* Xen/ARM does not use buffered IOREQs. */ xpc->handle_bufioreq = HVM_IOREQSRV_BUFIOREQ_OFF; diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 1401d37959..8a1cdb037c 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -818,6 +818,7 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->no_cdrom = true; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_id = "ddr"; object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model, versal_set_ospi_model); diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 70fb444bbd..311d8f1cad 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -280,6 +280,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS; mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; mc->default_ram_id = "ddr-ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); object_class_property_add_bool(oc, "secure", zcu102_get_secure, diff --git a/hw/avr/arduino.c b/hw/avr/arduino.c index 48ef478346..1801074a42 100644 --- a/hw/avr/arduino.c +++ b/hw/avr/arduino.c @@ -67,6 +67,7 @@ static void arduino_machine_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index c5f247633e..363ca89ca1 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -704,6 +704,7 @@ static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; mc->default_nic = "tulip"; + mc->no_sdcard = ON_OFF_AUTO_OFF; nc->nmi_monitor_handler = hppa_nmi; } @@ -740,6 +741,7 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; mc->default_nic = "tulip"; + mc->no_sdcard = ON_OFF_AUTO_OFF; nc->nmi_monitor_handler = hppa_nmi; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 22641e6ddc..9d8b7389e4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1799,6 +1799,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; mc->smp_props.modules_supported = true; mc->default_ram_id = "pc.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 69bfc00b9a..e3b92fcb74 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -382,6 +382,7 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->kvm_type = x86_kvm_type; + mc->no_sdcard = ON_OFF_AUTO_OFF; x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c index 33c1027976..33e5882c2d 100644 --- a/hw/i386/xen/xen-pvh.c +++ b/hw/i386/xen/xen-pvh.c @@ -82,6 +82,7 @@ static void xen_pvh_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xen PVH x86 machine"; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; + mc->no_sdcard = ON_OFF_AUTO_OFF; /* mc->max_cpus holds the MAX value allowed in the -smp cmd-line opts. */ mc->max_cpus = HVM_MAX_VCPUS; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index f2aa0a9782..7732547a6f 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -954,6 +954,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; mc->no_cdrom = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index d97399b882..286c3bac2a 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -99,6 +99,7 @@ static void an5206_machine_init(MachineClass *mc) mc->init = an5206_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5206"); mc->default_ram_id = "an5206.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("an5206", an5206_machine_init) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 75cc076f78..a0c90d111e 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -398,6 +398,7 @@ static void mcf5208evb_machine_init(MachineClass *mc) mc->is_default = true; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); mc->default_ram_id = "mcf5208.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 0570e4a76f..06a4d825e7 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -1359,6 +1359,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); mc->no_cdrom = true; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo next_typeinfo = { diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index aeed4c8ddb..21fa56e7a9 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -743,6 +743,7 @@ static void q800_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->block_default_type = IF_SCSI; mc->default_ram_id = "m68k_mac.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); compat_props_add(mc->compat_props, hw_compat_q800, hw_compat_q800_len); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index d967bdd743..a1bd9c432d 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -318,6 +318,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->no_floppy = 1; mc->no_parallel = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_ram_id = "m68k_virt.ram"; } diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 8b44be75a2..8d0e6c948f 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -220,6 +220,7 @@ static void petalogix_ml605_machine_init(MachineClass *mc) mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; #endif mc->init = petalogix_ml605_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init) diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 2c0d8c34cd..5b06e7d82c 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -142,6 +142,7 @@ static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, void *data) mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; mc->is_default = true; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo petalogix_s3adsp1800_machine_types[] = { diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index bdbf7328bf..14386785f8 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -188,6 +188,7 @@ static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; #endif mc->init = xlnx_zynqmp_pmu_init; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 364c328032..bd9059a207 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -842,6 +842,7 @@ static void boston_mach_class_init(MachineClass *mc) mc->default_ram_id = "boston.ddr"; mc->max_cpus = 16; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("boston", boston_mach_class_init) diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 646044e274..83e95c3b20 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -335,6 +335,7 @@ static void mips_fuloong2e_machine_init(MachineClass *mc) mc->default_ram_size = 256 * MiB; mc->default_ram_id = "fuloong2e.ram"; mc->minimum_page_bits = 14; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index c89610639a..326f60c448 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -424,6 +424,7 @@ static void mips_magnum_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); mc->default_ram_id = "mips_jazz.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo mips_magnum_type = { @@ -441,6 +442,7 @@ static void mips_pica61_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); mc->default_ram_id = "mips_jazz.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo mips_pica61_type = { diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 831fddb1bd..f44932b331 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -679,6 +679,7 @@ static void loongson3v_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1600 * MiB; mc->minimum_page_bits = 14; mc->default_nic = "virtio-net-pci"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo loongson3_machine_types[] = { diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 8e9cea70b1..13811f89f9 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -1306,6 +1306,7 @@ static void mips_malta_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_malta.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; compat_props_add(mc->compat_props, malta_compat, malta_compat_len); } diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index c530688e76..f0d06ab549 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -247,6 +247,7 @@ static void mips_mipssim_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_mipssim.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index e0da4067ba..b9d71ea0c3 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -368,6 +368,7 @@ static void openrisc_sim_machine_init(ObjectClass *oc, void *data) mc->max_cpus = OR1KSIM_CPUS_MAX; mc->is_default = true; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo or1ksim_machine_typeinfo = { diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 7b60bf8509..d250e2ab21 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -554,6 +554,7 @@ static void openrisc_virt_machine_init(ObjectClass *oc, void *data) mc->max_cpus = VIRT_CPUS_MAX; mc->is_default = false; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo or1ksim_machine_typeinfo = { diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index b02792221c..39449e3632 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -173,6 +173,7 @@ static void amigaone_machine_init(MachineClass *mc) mc->default_display = "std"; mc->default_ram_id = "ram"; mc->default_ram_size = 512 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("amigaone", amigaone_machine_init) diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 70a8033373..0b91d422aa 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -100,6 +100,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; mc->default_nic = "virtio-net-pci"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index cb3dc3ab48..da53eb11ed 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -580,6 +580,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_display = "std"; mc->default_nic = "sungem"; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->kvm_type = core99_kvm_type; #ifdef TARGET_PPC64 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("970fx_v3.1"); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 0dbcea035c..b23bfff696 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -427,6 +427,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->default_nic = "ne2k_pci"; mc->ignore_boot_device_suffixes = true; mc->default_ram_id = "ppc_heathrow.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; fwc->get_dev_path = heathrow_fw_dev_path; } diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index d74af766ee..0fa3a1b776 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -62,6 +62,7 @@ static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; mc->default_nic = "virtio-net-pci"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } #define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b057672e82..03ddbb96b6 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -604,6 +604,7 @@ static void pegasos2_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); mc->default_ram_id = "pegasos2.ram"; mc->default_ram_size = 512 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); vhc->cpu_in_nested = pegasos2_cpu_in_nested; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 11fd477b71..70203449fd 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2878,6 +2878,7 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) /* Pnv provides a AHCI device for storage */ mc->block_default_type = IF_IDE; mc->no_parallel = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_boot_order = NULL; /* * RAM defaults to less than 2048 for 32-bit hosts, and large diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 969cac345a..c602d60817 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -351,6 +351,7 @@ static void ppc405_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 128 * MiB; mc->default_ram_id = "ppc405.ram"; mc->deprecation_reason = "machine is old and unmaintained"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo ppc405_machine_type = { diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 099fda3909..5b2d52032f 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -268,6 +268,7 @@ static void bamboo_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); mc->default_ram_id = "ppc4xx.sdram"; mc->default_nic = "e1000"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 3e68d8e6e2..998e8ecd42 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -428,6 +428,7 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); mc->default_display = "std"; mc->default_nic = "pcnet"; + mc->no_sdcard = ON_OFF_AUTO_OFF; machine_add_audiodev_property(mc); } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 3ecae6a950..a1b1fc8724 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -524,6 +524,7 @@ static void sam460ex_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc4xx.sdram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f3a4b4235d..fa030d5276 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4594,6 +4594,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = SPAPR_IRQ_NR_IPIS; mc->no_parallel = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->default_boot_order = ""; mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc_spapr.ram"; diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 2323811927..23d115d997 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -288,6 +288,7 @@ static void virtex_machine_init(MachineClass *mc) mc->init = virtex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440-xilinx"); mc->default_ram_id = "ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("virtex-ml507", virtex_machine_init) diff --git a/hw/remote/machine.c b/hw/remote/machine.c index fdc6c441bb..1f7b0b96dd 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -128,6 +128,7 @@ static void remote_machine_class_init(ObjectClass *oc, void *data) mc->init = remote_machine_init; mc->desc = "Experimental remote machine"; + mc->no_sdcard = ON_OFF_AUTO_OFF; hc->unplug = remote_machine_dev_unplug_cb; diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index ec7e2e4226..2417342a71 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -650,6 +650,7 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) mc->min_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + 1; mc->default_cpus = mc->min_cpus; mc->default_ram_id = "microchip.icicle.kit.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; /* * Map 513 MiB high memory, the minimum required high memory size, because diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index b9e56235d8..3b26e1f53b 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -121,6 +121,7 @@ static void opentitan_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static void lowrisc_ibex_soc_init(Object *obj) diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index e2242b97d0..2f59c86bf0 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -84,6 +84,7 @@ static void shakti_c_machine_class_init(ObjectClass *klass, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_SHAKTI_C; mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "riscv.shakti.c.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo shakti_c_machine_type_info = { diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 73d3b74281..56a2ca7cab 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -153,6 +153,7 @@ static void sifive_e_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SIFIVE_E_CPU; mc->default_ram_id = "riscv.sifive.e.ram"; mc->default_ram_size = sifive_e_memmap[SIFIVE_E_DEV_DTIM].size; + mc->no_sdcard = ON_OFF_AUTO_OFF; object_class_property_add_bool(oc, "revb", sifive_e_machine_get_revb, sifive_e_machine_set_revb); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 9a20bcbf7f..fd57d02dca 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -724,6 +724,7 @@ static void sifive_u_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SIFIVE_U_CPU; mc->default_cpus = mc->min_cpus; mc->default_ram_id = "riscv.sifive.u.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; object_class_property_add_bool(oc, "start-in-flash", sifive_u_machine_get_start_in_flash, diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 74a20016f1..a0fa727f6b 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -358,6 +358,7 @@ static void spike_machine_class_init(ObjectClass *oc, void *data) /* platform instead of architectural choice */ mc->cpu_cluster_has_numa_boundary = true; mc->default_ram_id = "riscv.spike.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; object_class_property_add_str(oc, "signature", NULL, spike_set_signature); object_class_property_set_description(oc, "signature", "File to write ACT test signature"); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 241389d72f..d9fc7cdf9a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1918,6 +1918,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_BASE; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; mc->pci_allow_0_address = true; mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 88c8f12c10..9c3ae60bf8 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -166,6 +166,7 @@ static void rx_gdbsim_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RX62N_CPU; mc->default_ram_size = 16 * MiB; mc->default_ram_id = "ext-sdram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static void rx62n7_class_init(ObjectClass *oc, void *data) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index d68c94e82e..6ef552ae53 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -383,6 +383,7 @@ static void r2d_machine_init(MachineClass *mc) mc->block_default_type = IF_IDE; mc->default_cpu_type = TYPE_SH7751R_CPU; mc->default_nic = "rtl8139"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("r2d", r2d_machine_init) diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 0aeaad3bec..e99d6d71e8 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -440,6 +440,7 @@ static void leon3_generic_machine_init(MachineClass *mc) mc->default_cpu_type = SPARC_CPU_TYPE_NAME("LEON3"); mc->default_ram_id = "leon3.ram"; mc->max_cpus = MAX_CPUS; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index a48d3622c5..19a2a9f2ff 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -1113,6 +1113,7 @@ static void sun4m_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_display = "tcx"; mc->default_ram_id = "sun4m.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static void ss5_class_init(ObjectClass *oc, void *data) diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 805ba6b1e3..409c67b1b4 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -167,6 +167,7 @@ static void niagara_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Sun-UltraSparc-T1"); mc->default_ram_id = "sun4v-partition.ram"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo niagara_type = { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 8ab5cf0461..eaf3d42bd0 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -809,6 +809,7 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->default_display = "std"; mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); + mc->no_sdcard = ON_OFF_AUTO_OFF; fwc->get_dev_path = sun4u_fw_dev_path; compat_props_add(mc->compat_props, hw_compat_sparc64, hw_compat_sparc64_len); } @@ -836,6 +837,7 @@ static void sun4v_class_init(ObjectClass *oc, void *data) mc->default_display = "std"; mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo sun4v_type = { diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index f5baa8ccbb..3cd93daf6a 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -73,6 +73,7 @@ static void triboard_machine_tc277d_class_init(ObjectClass *oc, mc->init = triboard_machine_init; mc->desc = "Infineon AURIX TriBoard TC277 (D-Step)"; mc->max_cpus = 1; + mc->no_sdcard = ON_OFF_AUTO_OFF; amc->soc_name = "tc277d-soc"; }; diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 3facfdfd61..2971805170 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -111,6 +111,7 @@ static void ttb_machine_init(MachineClass *mc) mc->desc = "a minimal TriCore board"; mc->init = tricoreboard_init; mc->default_cpu_type = TRICORE_CPU_TYPE_NAME("tc1796"); + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("tricore_testboard", ttb_machine_init) diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index 9c21fa858d..ed42e4b624 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -381,6 +381,7 @@ static void xen_pvh_class_init(ObjectClass *oc, void *data) mc->default_machine_opts = "accel=xen"; /* Set to zero to make sure that the real ram size is passed. */ mc->default_ram_size = 0; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xen_pvh_info = { diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 99c02492ef..a05713e5da 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -67,6 +67,7 @@ static void xenpv_machine_init(MachineClass *mc) mc->init = xen_init_pv; mc->max_cpus = 1; mc->default_machine_opts = "accel=xen"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("xenpv", xenpv_machine_init) diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 1cea29c66d..6c86d4939f 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -125,6 +125,7 @@ static void xtensa_sim_machine_init(MachineClass *mc) mc->max_cpus = 4; mc->no_serial = 1; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("sim", xtensa_sim_machine_init) diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index b08404fc17..ffa6f21063 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -122,6 +122,7 @@ static void xtensa_virt_machine_init(MachineClass *mc) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_nic = "virtio-net-pci"; + mc->no_sdcard = ON_OFF_AUTO_OFF; } DEFINE_MACHINE("virt", xtensa_virt_machine_init) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 3f3677f1c9..2b5f20acef 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -594,6 +594,7 @@ static void xtfpga_lx60_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 64 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_lx60_type = { @@ -611,6 +612,7 @@ static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 64 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_lx60_nommu_type = { @@ -628,6 +630,7 @@ static void xtfpga_lx200_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 96 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_lx200_type = { @@ -645,6 +648,7 @@ static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 96 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_lx200_nommu_type = { @@ -662,6 +666,7 @@ static void xtfpga_ml605_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 512 * MiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_ml605_type = { @@ -679,6 +684,7 @@ static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 256 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_ml605_nommu_type = { @@ -696,6 +702,7 @@ static void xtfpga_kc705_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 1 * GiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_kc705_type = { @@ -713,6 +720,7 @@ static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 256 * MiB; + mc->no_sdcard = ON_OFF_AUTO_OFF; } static const TypeInfo xtfpga_kc705_nommu_type = { diff --git a/system/vl.c b/system/vl.c index 04ce290997..3904d0d158 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1346,6 +1346,7 @@ static void qemu_disable_default_devices(void) if (!has_defaults || machine_class->no_cdrom) { default_cdrom = 0; } + assert(machine_class->no_sdcard != ON_OFF_AUTO_AUTO); if (!has_defaults || machine_class->no_sdcard == ON_OFF_AUTO_ON) { default_sdcard = 0; } From cdc8d7cadaac33ca103791a6ebb535a3ad9fa05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Nov 2024 18:26:48 +0100 Subject: [PATCH 1747/2892] hw/boards: Rename no_sdcard -> auto_create_sdcard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Invert the 'no_sdcard' logic, renaming it as the more explicit "auto_create_sdcard". Machines are supposed to create a SD Card drive when this flag is set. In many cases it doesn't make much sense (as boards don't expose SD Card host controller), but this is patch only aims to expose that nonsense; so no logical change intended (mechanical patch using gsed). Most of the changes are: - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; Except in . hw/core/null-machine.c . hw/arm/xilinx_zynq.c . hw/s390x/s390-virtio-ccw.c where the disabled option is manually removed (since default): - mc->no_sdcard = ON_OFF_AUTO_ON; + mc->auto_create_sdcard = false; - mc->auto_create_sdcard = false; and in system/vl.c we change the 'default_sdcard' type to boolean. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250204200934.65279-4-philmd@linaro.org> --- hw/alpha/dp264.c | 2 +- hw/arm/aspeed.c | 42 ++++++++++++------------ hw/arm/b-l475e-iot01a.c | 2 +- hw/arm/bananapi_m2u.c | 2 +- hw/arm/collie.c | 2 +- hw/arm/cubieboard.c | 2 +- hw/arm/digic_boards.c | 2 +- hw/arm/exynos4_boards.c | 4 +-- hw/arm/fby35.c | 2 +- hw/arm/highbank.c | 4 +-- hw/arm/imx25_pdk.c | 2 +- hw/arm/integratorcp.c | 2 +- hw/arm/kzm.c | 2 +- hw/arm/mcimx6ul-evk.c | 2 +- hw/arm/mcimx7d-sabre.c | 2 +- hw/arm/microbit.c | 2 +- hw/arm/mps2-tz.c | 8 ++--- hw/arm/mps2.c | 8 ++--- hw/arm/mps3r.c | 2 +- hw/arm/msf2-som.c | 2 +- hw/arm/musca.c | 4 +-- hw/arm/musicpal.c | 2 +- hw/arm/netduino2.c | 2 +- hw/arm/netduinoplus2.c | 2 +- hw/arm/npcm7xx_boards.c | 10 +++--- hw/arm/olimex-stm32-h405.c | 2 +- hw/arm/omap_sx1.c | 4 +-- hw/arm/orangepi.c | 2 +- hw/arm/raspi.c | 10 +++--- hw/arm/raspi4b.c | 2 +- hw/arm/realview.c | 8 ++--- hw/arm/sabrelite.c | 2 +- hw/arm/sbsa-ref.c | 2 +- hw/arm/stellaris.c | 4 +-- hw/arm/stm32vldiscovery.c | 2 +- hw/arm/versatilepb.c | 4 +-- hw/arm/vexpress.c | 4 +-- hw/arm/virt.c | 2 +- hw/arm/xen-pvh.c | 2 +- hw/arm/xilinx_zynq.c | 1 - hw/arm/xlnx-versal-virt.c | 2 +- hw/arm/xlnx-zcu102.c | 2 +- hw/avr/arduino.c | 2 +- hw/core/null-machine.c | 1 - hw/hppa/machine.c | 4 +-- hw/i386/pc.c | 2 +- hw/i386/x86.c | 2 +- hw/i386/xen/xen-pvh.c | 2 +- hw/loongarch/virt.c | 2 +- hw/m68k/an5206.c | 2 +- hw/m68k/mcf5208.c | 2 +- hw/m68k/next-cube.c | 2 +- hw/m68k/q800.c | 2 +- hw/m68k/virt.c | 2 +- hw/microblaze/petalogix_ml605_mmu.c | 2 +- hw/microblaze/petalogix_s3adsp1800_mmu.c | 2 +- hw/microblaze/xlnx-zynqmp-pmu.c | 2 +- hw/mips/boston.c | 2 +- hw/mips/fuloong2e.c | 2 +- hw/mips/jazz.c | 4 +-- hw/mips/loongson3_virt.c | 2 +- hw/mips/malta.c | 2 +- hw/mips/mipssim.c | 2 +- hw/openrisc/openrisc_sim.c | 2 +- hw/openrisc/virt.c | 2 +- hw/ppc/amigaone.c | 2 +- hw/ppc/e500plat.c | 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/mpc8544ds.c | 2 +- hw/ppc/pegasos2.c | 2 +- hw/ppc/pnv.c | 2 +- hw/ppc/ppc405_boards.c | 2 +- hw/ppc/ppc440_bamboo.c | 2 +- hw/ppc/prep.c | 2 +- hw/ppc/sam460ex.c | 2 +- hw/ppc/spapr.c | 2 +- hw/ppc/virtex_ml507.c | 2 +- hw/remote/machine.c | 2 +- hw/riscv/microchip_pfsoc.c | 2 +- hw/riscv/opentitan.c | 2 +- hw/riscv/shakti_c.c | 2 +- hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_u.c | 2 +- hw/riscv/spike.c | 2 +- hw/riscv/virt.c | 2 +- hw/rx/rx-gdbsim.c | 2 +- hw/s390x/s390-virtio-ccw.c | 1 - hw/sh4/r2d.c | 2 +- hw/sparc/leon3.c | 2 +- hw/sparc/sun4m.c | 2 +- hw/sparc64/niagara.c | 2 +- hw/sparc64/sun4u.c | 4 +-- hw/tricore/triboard.c | 2 +- hw/tricore/tricore_testboard.c | 2 +- hw/xen/xen-pvh-common.c | 2 +- hw/xenpv/xen_machine_pv.c | 2 +- hw/xtensa/sim.c | 2 +- hw/xtensa/virt.c | 2 +- hw/xtensa/xtfpga.c | 16 ++++----- include/hw/boards.h | 2 +- system/vl.c | 9 +++-- 102 files changed, 156 insertions(+), 160 deletions(-) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index b11e527be9..14b942fd5a 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -213,7 +213,7 @@ static void clipper_machine_init(MachineClass *mc) mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67"); mc->default_ram_id = "ram"; mc->default_nic = "e1000"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 9d9c55adcd..f3ba90896c 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1253,7 +1253,7 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = palmetto_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1270,7 +1270,7 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 1; amc->i2c_init = quanta_q71l_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 128 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1289,7 +1289,7 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 256 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1308,7 +1308,7 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1325,7 +1325,7 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1343,7 +1343,7 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = yosemitev2_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1360,7 +1360,7 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = romulus_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1378,7 +1378,7 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = tiogapass_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1395,7 +1395,7 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = sonorapass_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1412,7 +1412,7 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = witherspoon_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1433,7 +1433,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) ASPEED_MAC3_ON; amc->sdhci_wp_inverted = true; amc->i2c_init = ast2600_evb_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1452,7 +1452,7 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = g220a_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1024 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1470,7 +1470,7 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = fp5280g2_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1489,7 +1489,7 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = rainier_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); aspeed_machine_ast2600_class_emmc_init(oc); @@ -1512,7 +1512,7 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fuji_bmc_i2c_init; amc->uart_default = ASPEED_DEV_UART1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1533,7 +1533,7 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = BLETCHLEY_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1575,7 +1575,7 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fby35_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; /* FIXME: Replace this macro with something more general */ mc->default_ram_size = FUJI_BMC_RAM_SIZE; aspeed_machine_class_init_cpus_defaults(mc); @@ -1658,7 +1658,7 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, amc->spi_model = "w25q256"; amc->num_cs = 2; amc->macs_mask = 0; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1687,7 +1687,7 @@ static void aspeed_machine_ast2700_evb_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } @@ -1708,7 +1708,7 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_bmc_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; @@ -1728,7 +1728,7 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_firework_i2c_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); }; diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index d43c84435b..f05ee0fee0 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -120,7 +120,7 @@ static void bl475e_machine_init(ObjectClass *oc, void *data) mc->desc = "B-L475E-IOT01A Discovery Kit (Cortex-M4)"; mc->init = bl475e_init; mc->valid_cpu_types = machine_valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 3da6ec4a03..4d84d10d24 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -141,7 +141,7 @@ static void bpim2u_machine_init(MachineClass *mc) mc->valid_cpu_types = valid_cpu_types; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "bpim2u.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("bpim2u", bpim2u_machine_init) diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 80bf12246a..864c66193b 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -79,7 +79,7 @@ static void collie_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("sa1110"); mc->default_ram_size = RAM_SIZE; mc->default_ram_id = "strongarm.sdram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo collie_machine_typeinfo = { diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 11d896f832..d665d4edd9 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -122,7 +122,7 @@ static void cubieboard_machine_init(MachineClass *mc) mc->units_per_default_bus = 1; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "cubieboard.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("cubieboard", cubieboard_machine_init) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index a6ccf7ef9b..f334c1fb02 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -143,7 +143,7 @@ static void canon_a1100_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; mc->default_ram_size = 64 * MiB; mc->default_ram_id = "ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init) diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 63e86e2c60..43dc89d902 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -165,7 +165,7 @@ static void nuri_class_init(ObjectClass *oc, void *data) mc->min_cpus = EXYNOS4210_NCPUS; mc->default_cpus = EXYNOS4210_NCPUS; mc->ignore_memory_transaction_failures = true; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo nuri_type = { @@ -185,7 +185,7 @@ static void smdkc210_class_init(ObjectClass *oc, void *data) mc->min_cpus = EXYNOS4210_NCPUS; mc->default_cpus = EXYNOS4210_NCPUS; mc->ignore_memory_transaction_failures = true; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo smdkc210_type = { diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index 9b448bf764..6d3663f14a 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -170,7 +170,7 @@ static void fby35_class_init(ObjectClass *oc, void *data) mc->init = fby35_init; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->min_cpus = mc->max_cpus = mc->default_cpus = 3; object_class_property_add_bool(oc, "execute-in-place", diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 97477571e6..975fd7a094 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -357,7 +357,7 @@ static void highbank_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo highbank_type = { @@ -382,7 +382,7 @@ static void midway_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo midway_type = { diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 8f89e03332..e95ea5e4e1 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -147,7 +147,7 @@ static void imx25_pdk_machine_init(MachineClass *mc) mc->init = imx25_pdk_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "imx25.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 905a7c2aec..8aa2e6e98e 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -688,7 +688,7 @@ static void integratorcp_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "integrator.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 45b3b08eb8..736eabab66 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -137,7 +137,7 @@ static void kzm_machine_init(MachineClass *mc) mc->init = kzm_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "kzm.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("kzm", kzm_machine_init) diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 49520b47f1..86982cb077 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -74,6 +74,6 @@ static void mcimx6ul_evk_machine_init(MachineClass *mc) mc->init = mcimx6ul_evk_init; mc->max_cpus = FSL_IMX6UL_NUM_CPUS; mc->default_ram_id = "mcimx6ul-evk.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("mcimx6ul-evk", mcimx6ul_evk_machine_init) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index da32fdd29d..3311961011 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -74,6 +74,6 @@ static void mcimx7d_sabre_machine_init(MachineClass *mc) mc->init = mcimx7d_sabre_init; mc->max_cpus = FSL_IMX7_NUM_CPUS; mc->default_ram_id = "mcimx7d-sabre.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init) diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 9d32ae5bd5..fb09950832 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -67,7 +67,7 @@ static void microbit_machine_class_init(ObjectClass *oc, void *data) mc->desc = "BBC micro:bit (Cortex-M0)"; mc->init = microbit_init; mc->max_cpus = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo microbit_info = { diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 1062612036..91b8ae6d38 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1320,7 +1320,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41045050; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1355,7 +1355,7 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN521; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41045210; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1390,7 +1390,7 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN524; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41045240; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1430,7 +1430,7 @@ static void mps3tz_an547_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN547; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m55"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41055470; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = 25 * 1000 * 1000; /* 25MHz */ diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 2bce37644e..40eb5d1618 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -487,7 +487,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41043850; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -507,7 +507,7 @@ static void mps2_an386_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN386; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41043860; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -527,7 +527,7 @@ static void mps2_an500_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN500; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m7"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41045000; mmc->psram_base = 0x60000000; mmc->ethernet_base = 0xa0000000; @@ -547,7 +547,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->scc_id = 0x41045110; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index ae70ebe860..f26d1cfb2c 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -618,7 +618,7 @@ static void mps3r_an536_class_init(ObjectClass *oc, void *data) mc->max_cpus = 2; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-r52"); mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->raminfo = an536_raminfo; mps3r_set_default_ram_info(mmc); } diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 349a96a0b5..6d3f0a89e0 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -106,7 +106,7 @@ static void emcraft_sf2_machine_init(MachineClass *mc) mc->desc = "SmartFusion2 SOM kit from Emcraft (M2S010)"; mc->init = emcraft_sf2_s2s010_init; mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("emcraft-sf2", emcraft_sf2_machine_init) diff --git a/hw/arm/musca.c b/hw/arm/musca.c index 89451b9684..6f19b7d58a 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -615,7 +615,7 @@ static void musca_a_class_init(ObjectClass *oc, void *data) MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); mc->desc = "ARM Musca-A board (dual Cortex-M33)"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->type = MUSCA_A; mmc->init_svtor = 0x10200000; mmc->sram_addr_width = 15; @@ -630,7 +630,7 @@ static void musca_b1_class_init(ObjectClass *oc, void *data) MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); mc->desc = "ARM Musca-B1 board (dual Cortex-M33)"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mmc->type = MUSCA_B1; /* * This matches the DAPlink firmware which boots from QSPI. There diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index d5ebfabe3f..fd2975753e 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1342,7 +1342,7 @@ static void musicpal_machine_init(MachineClass *mc) mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_size = MP_RAM_DEFAULT_SIZE; mc->default_ram_id = "musicpal.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index 9ca0ee6a34..fca32d4592 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -63,7 +63,7 @@ static void netduino2_machine_init(MachineClass *mc) mc->init = netduino2_init; mc->valid_cpu_types = valid_cpu_types; mc->ignore_memory_transaction_failures = true; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("netduino2", netduino2_machine_init) diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index abe6060765..e1a59fb9e2 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -63,7 +63,7 @@ static void netduinoplus2_machine_init(MachineClass *mc) mc->desc = "Netduino Plus 2 Machine (Cortex-M4)"; mc->init = netduinoplus2_init; mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("netduinoplus2", netduinoplus2_machine_init) diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 38b8f02364..eb28b97ad8 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -481,7 +481,7 @@ static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Nuvoton NPCM750 Evaluation Board (Cortex-A9)"; mc->init = npcm750_evb_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; }; @@ -494,7 +494,7 @@ static void gsj_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Quanta GSJ (Cortex-A9)"; mc->init = quanta_gsj_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; }; @@ -507,7 +507,7 @@ static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Quanta GBS (Cortex-A9)"; mc->init = quanta_gbs_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; } @@ -520,7 +520,7 @@ static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Kudo BMC (Cortex-A9)"; mc->init = kudo_bmc_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; }; @@ -533,7 +533,7 @@ static void mori_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Mori BMC (Cortex-A9)"; mc->init = mori_bmc_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; } diff --git a/hw/arm/olimex-stm32-h405.c b/hw/arm/olimex-stm32-h405.c index 01ae12fa4a..23f686de87 100644 --- a/hw/arm/olimex-stm32-h405.c +++ b/hw/arm/olimex-stm32-h405.c @@ -66,7 +66,7 @@ static void olimex_stm32_h405_machine_init(MachineClass *mc) mc->desc = "Olimex STM32-H405 (Cortex-M4)"; mc->init = olimex_stm32_h405_init; mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 8170669db1..c6b0bed079 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -216,7 +216,7 @@ static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo sx1_machine_v2_type = { @@ -235,7 +235,7 @@ static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo sx1_machine_v1_type = { diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 76ab214853..634af9b0a1 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -121,7 +121,7 @@ static void orangepi_machine_init(MachineClass *mc) mc->valid_cpu_types = valid_cpu_types; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "orangepi.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("orangepi-pc", orangepi_machine_init) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 176c324cf8..dce35ca11a 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -342,7 +342,7 @@ static void raspi0_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; rmc->board_rev = 0x920092; /* Revision 1.2 */ raspi_machine_class_init(mc, rmc->board_rev); }; @@ -352,7 +352,7 @@ static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; rmc->board_rev = 0x900021; /* Revision 1.1 */ raspi_machine_class_init(mc, rmc->board_rev); }; @@ -362,7 +362,7 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; rmc->board_rev = 0xa21041; raspi_machine_class_init(mc, rmc->board_rev); }; @@ -373,7 +373,7 @@ static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; rmc->board_rev = 0x9020e0; /* Revision 1.0 */ raspi_machine_class_init(mc, rmc->board_rev); }; @@ -383,7 +383,7 @@ static void raspi3b_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; rmc->board_rev = 0xa02082; raspi_machine_class_init(mc, rmc->board_rev); }; diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 37eef37888..f6de103a3e 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -118,7 +118,7 @@ static void raspi4b_machine_class_init(ObjectClass *oc, void *data) rmc->board_rev = 0xb03115; /* Revision 1.5, 2 Gb RAM */ #endif raspi_machine_class_common_init(mc, rmc->board_rev); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->init = raspi4b_machine_init; } diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 4bc8f3956f..436eef816e 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -415,7 +415,7 @@ static void realview_eb_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } @@ -436,7 +436,7 @@ static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm11mpcore"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } @@ -455,7 +455,7 @@ static void realview_pb_a8_class_init(ObjectClass *oc, void *data) mc->init = realview_pb_a8_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a8"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } @@ -475,7 +475,7 @@ static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 8d57653ab3..df60d47c6f 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -110,7 +110,7 @@ static void sabrelite_machine_init(MachineClass *mc) mc->max_cpus = FSL_IMX6_NUM_CPUS; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "sabrelite.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("sabrelite", sabrelite_machine_init) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 41dba85f44..02c72a62a3 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -900,7 +900,7 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) mc->minimum_page_bits = 12; mc->block_default_type = IF_IDE; mc->no_cdrom = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_nic = "e1000e"; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "sbsa-ref.ram"; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index ef3fd508ba..25283fd623 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1421,7 +1421,7 @@ static void lm3s811evb_class_init(ObjectClass *oc, void *data) mc->init = lm3s811evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo lm3s811evb_type = { @@ -1442,7 +1442,7 @@ static void lm3s6965evb_class_init(ObjectClass *oc, void *data) mc->init = lm3s6965evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo lm3s6965evb_type = { diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index 01b4afcb1a..a71da292b8 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -66,7 +66,7 @@ static void stm32vldiscovery_machine_init(MachineClass *mc) mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; mc->init = stm32vldiscovery_init; mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index def3da4a34..941616cd25 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -419,7 +419,7 @@ static void versatilepb_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "versatile.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } @@ -440,7 +440,7 @@ static void versatileab_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "versatile.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 06ec78e6eb..48e18a49d5 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -803,7 +803,7 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Versatile Express for Cortex-A9"; mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; vmc->daughterboard = &a9_daughterboard; } @@ -819,7 +819,7 @@ static void vexpress_a15_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Versatile Express for Cortex-A15"; mc->valid_cpu_types = valid_cpu_types; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; vmc->daughterboard = &a15_daughterboard; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e2ac6ce464..9aea06b707 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3125,7 +3125,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) #endif mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->pci_allow_0_address = true; /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */ mc->minimum_page_bits = 12; diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c index e49ab0e7f5..ce4cc4fce9 100644 --- a/hw/arm/xen-pvh.c +++ b/hw/arm/xen-pvh.c @@ -75,7 +75,7 @@ static void xen_arm_machine_class_init(ObjectClass *oc, void *data) * mc->max_cpus, QEMU will bail out with an error message. */ mc->max_cpus = GUEST_MAX_VCPUS; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; /* Xen/ARM does not use buffered IOREQs. */ xpc->handle_bufioreq = HVM_IOREQSRV_BUFIOREQ_OFF; diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 12418094f9..3c6a4604cc 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -463,7 +463,6 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; mc->init = zynq_init; mc->max_cpus = ZYNQ_MAX_CPUS; - mc->no_sdcard = ON_OFF_AUTO_ON; mc->ignore_memory_transaction_failures = true; mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "zynq.ext_ram"; diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 8a1cdb037c..0c6f0359e3 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -818,7 +818,7 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->no_cdrom = true; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_id = "ddr"; object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model, versal_set_ospi_model); diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 311d8f1cad..4fdb153e4d 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -280,7 +280,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS; mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; mc->default_ram_id = "ddr-ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); object_class_property_add_bool(oc, "secure", zcu102_get_secure, diff --git a/hw/avr/arduino.c b/hw/avr/arduino.c index 1801074a42..5f30a7d585 100644 --- a/hw/avr/arduino.c +++ b/hw/avr/arduino.c @@ -67,7 +67,7 @@ static void arduino_machine_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index b93056c0f7..7f1fb562be 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -53,7 +53,6 @@ static void machine_none_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->no_sdcard = ON_OFF_AUTO_ON; } DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 363ca89ca1..b4b238ed2c 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -704,7 +704,7 @@ static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; mc->default_nic = "tulip"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; nc->nmi_monitor_handler = hppa_nmi; } @@ -741,7 +741,7 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; mc->default_nic = "tulip"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; nc->nmi_monitor_handler = hppa_nmi; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 9d8b7389e4..a5324a1246 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1799,7 +1799,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; mc->smp_props.modules_supported = true; mc->default_ram_id = "pc.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", diff --git a/hw/i386/x86.c b/hw/i386/x86.c index e3b92fcb74..790b1863f5 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -382,7 +382,7 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->kvm_type = x86_kvm_type; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c index 33e5882c2d..6f5b6a2b8f 100644 --- a/hw/i386/xen/xen-pvh.c +++ b/hw/i386/xen/xen-pvh.c @@ -82,7 +82,7 @@ static void xen_pvh_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xen PVH x86 machine"; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; /* mc->max_cpus holds the MAX value allowed in the -smp cmd-line opts. */ mc->max_cpus = HVM_MAX_VCPUS; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 7732547a6f..a7f1c8acee 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -954,7 +954,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; mc->no_cdrom = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 286c3bac2a..1947870512 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -99,7 +99,7 @@ static void an5206_machine_init(MachineClass *mc) mc->init = an5206_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5206"); mc->default_ram_id = "an5206.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("an5206", an5206_machine_init) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index a0c90d111e..9a8c551224 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -398,7 +398,7 @@ static void mcf5208evb_machine_init(MachineClass *mc) mc->is_default = true; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); mc->default_ram_id = "mcf5208.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 06a4d825e7..ee06a5e2e0 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -1359,7 +1359,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); mc->no_cdrom = true; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo next_typeinfo = { diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 21fa56e7a9..f3cb8541b3 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -743,7 +743,7 @@ static void q800_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->block_default_type = IF_SCSI; mc->default_ram_id = "m68k_mac.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); compat_props_add(mc->compat_props, hw_compat_q800, hw_compat_q800_len); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index a1bd9c432d..69e8f53482 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -318,7 +318,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->no_floppy = 1; mc->no_parallel = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_ram_id = "m68k_virt.ram"; } diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 8d0e6c948f..1e3d55dd3b 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -220,7 +220,7 @@ static void petalogix_ml605_machine_init(MachineClass *mc) mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; #endif mc->init = petalogix_ml605_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init) diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 5b06e7d82c..95b1b6483f 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -142,7 +142,7 @@ static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, void *data) mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; mc->is_default = true; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo petalogix_s3adsp1800_machine_types[] = { diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 14386785f8..b40d82b396 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -188,7 +188,7 @@ static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; #endif mc->init = xlnx_zynqmp_pmu_init; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index bd9059a207..66cdad639e 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -842,7 +842,7 @@ static void boston_mach_class_init(MachineClass *mc) mc->default_ram_id = "boston.ddr"; mc->max_cpus = 16; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("boston", boston_mach_class_init) diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 83e95c3b20..e10b5a2774 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -335,7 +335,7 @@ static void mips_fuloong2e_machine_init(MachineClass *mc) mc->default_ram_size = 256 * MiB; mc->default_ram_id = "fuloong2e.ram"; mc->minimum_page_bits = 14; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 326f60c448..ad4561a51e 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -424,7 +424,7 @@ static void mips_magnum_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); mc->default_ram_id = "mips_jazz.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo mips_magnum_type = { @@ -442,7 +442,7 @@ static void mips_pica61_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); mc->default_ram_id = "mips_jazz.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo mips_pica61_type = { diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index f44932b331..46c2e1e9de 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -679,7 +679,7 @@ static void loongson3v_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1600 * MiB; mc->minimum_page_bits = 14; mc->default_nic = "virtio-net-pci"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo loongson3_machine_types[] = { diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 13811f89f9..31ff279b4c 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -1306,7 +1306,7 @@ static void mips_malta_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_malta.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; compat_props_add(mc->compat_props, malta_compat, malta_compat_len); } diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index f0d06ab549..ff2b905070 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -247,7 +247,7 @@ static void mips_mipssim_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_mipssim.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index b9d71ea0c3..5e4686ba7a 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -368,7 +368,7 @@ static void openrisc_sim_machine_init(ObjectClass *oc, void *data) mc->max_cpus = OR1KSIM_CPUS_MAX; mc->is_default = true; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo or1ksim_machine_typeinfo = { diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index d250e2ab21..2764e398a9 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -554,7 +554,7 @@ static void openrisc_virt_machine_init(ObjectClass *oc, void *data) mc->max_cpus = VIRT_CPUS_MAX; mc->is_default = false; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo or1ksim_machine_typeinfo = { diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 39449e3632..296c30da92 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -173,7 +173,7 @@ static void amigaone_machine_init(MachineClass *mc) mc->default_display = "std"; mc->default_ram_id = "ram"; mc->default_ram_size = 512 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("amigaone", amigaone_machine_init) diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 0b91d422aa..afad4802ca 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -100,7 +100,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; mc->default_nic = "virtio-net-pci"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index da53eb11ed..869f3f7104 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -580,7 +580,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_display = "std"; mc->default_nic = "sungem"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->kvm_type = core99_kvm_type; #ifdef TARGET_PPC64 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("970fx_v3.1"); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index b23bfff696..08e30a4a4e 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -427,7 +427,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->default_nic = "ne2k_pci"; mc->ignore_boot_device_suffixes = true; mc->default_ram_id = "ppc_heathrow.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; fwc->get_dev_path = heathrow_fw_dev_path; } diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 0fa3a1b776..38bdf45316 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -62,7 +62,7 @@ static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; mc->default_nic = "virtio-net-pci"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } #define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 03ddbb96b6..479dcfe809 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -604,7 +604,7 @@ static void pegasos2_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); mc->default_ram_id = "pegasos2.ram"; mc->default_ram_size = 512 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); vhc->cpu_in_nested = pegasos2_cpu_in_nested; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 70203449fd..15fbbf6c15 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2878,7 +2878,7 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) /* Pnv provides a AHCI device for storage */ mc->block_default_type = IF_IDE; mc->no_parallel = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_boot_order = NULL; /* * RAM defaults to less than 2048 for 32-bit hosts, and large diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index c602d60817..8946b5173c 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -351,7 +351,7 @@ static void ppc405_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 128 * MiB; mc->default_ram_id = "ppc405.ram"; mc->deprecation_reason = "machine is old and unmaintained"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo ppc405_machine_type = { diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 5b2d52032f..081a993ef0 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -268,7 +268,7 @@ static void bamboo_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); mc->default_ram_id = "ppc4xx.sdram"; mc->default_nic = "e1000"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 998e8ecd42..85bfc2fd4f 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -428,7 +428,7 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); mc->default_display = "std"; mc->default_nic = "pcnet"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index a1b1fc8724..d9c871ef20 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -524,7 +524,7 @@ static void sam460ex_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc4xx.sdram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index fa030d5276..42b07fadd9 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4594,7 +4594,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = SPAPR_IRQ_NR_IPIS; mc->no_parallel = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->default_boot_order = ""; mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc_spapr.ram"; diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 23d115d997..22184fb698 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -288,7 +288,7 @@ static void virtex_machine_init(MachineClass *mc) mc->init = virtex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440-xilinx"); mc->default_ram_id = "ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("virtex-ml507", virtex_machine_init) diff --git a/hw/remote/machine.c b/hw/remote/machine.c index 1f7b0b96dd..fa4a1bb815 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -128,7 +128,7 @@ static void remote_machine_class_init(ObjectClass *oc, void *data) mc->init = remote_machine_init; mc->desc = "Experimental remote machine"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; hc->unplug = remote_machine_dev_unplug_cb; diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 2417342a71..9c846f9b5b 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -650,7 +650,7 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) mc->min_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + 1; mc->default_cpus = mc->min_cpus; mc->default_ram_id = "microchip.icicle.kit.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; /* * Map 513 MiB high memory, the minimum required high memory size, because diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 3b26e1f53b..d78a96c535 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -121,7 +121,7 @@ static void opentitan_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static void lowrisc_ibex_soc_init(Object *obj) diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 2f59c86bf0..efe814b586 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -84,7 +84,7 @@ static void shakti_c_machine_class_init(ObjectClass *klass, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_SHAKTI_C; mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "riscv.shakti.c.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo shakti_c_machine_type_info = { diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 56a2ca7cab..164eb3ab83 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -153,7 +153,7 @@ static void sifive_e_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SIFIVE_E_CPU; mc->default_ram_id = "riscv.sifive.e.ram"; mc->default_ram_size = sifive_e_memmap[SIFIVE_E_DEV_DTIM].size; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; object_class_property_add_bool(oc, "revb", sifive_e_machine_get_revb, sifive_e_machine_set_revb); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index fd57d02dca..679f2024bc 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -724,7 +724,7 @@ static void sifive_u_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SIFIVE_U_CPU; mc->default_cpus = mc->min_cpus; mc->default_ram_id = "riscv.sifive.u.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; object_class_property_add_bool(oc, "start-in-flash", sifive_u_machine_get_start_in_flash, diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index a0fa727f6b..1ea35937e1 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -358,7 +358,7 @@ static void spike_machine_class_init(ObjectClass *oc, void *data) /* platform instead of architectural choice */ mc->cpu_cluster_has_numa_boundary = true; mc->default_ram_id = "riscv.spike.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; object_class_property_add_str(oc, "signature", NULL, spike_set_signature); object_class_property_set_description(oc, "signature", "File to write ACT test signature"); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index d9fc7cdf9a..2aa420f6e5 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1918,7 +1918,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_BASE; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; mc->pci_allow_0_address = true; mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 9c3ae60bf8..de3b708bc5 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -166,7 +166,7 @@ static void rx_gdbsim_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RX62N_CPU; mc->default_ram_size = 16 * MiB; mc->default_ram_id = "ext-sdram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static void rx62n7_class_init(ObjectClass *oc, void *data) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 5f78c8d20f..51ae0c133d 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -817,7 +817,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_cdrom = 1; mc->no_floppy = 1; mc->no_parallel = 1; - mc->no_sdcard = ON_OFF_AUTO_ON; mc->max_cpus = S390_MAX_CPUS; mc->has_hotpluggable_cpus = true; mc->smp_props.books_supported = true; diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 6ef552ae53..e34deb33dc 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -383,7 +383,7 @@ static void r2d_machine_init(MachineClass *mc) mc->block_default_type = IF_IDE; mc->default_cpu_type = TYPE_SH7751R_CPU; mc->default_nic = "rtl8139"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("r2d", r2d_machine_init) diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index e99d6d71e8..3bd2449575 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -440,7 +440,7 @@ static void leon3_generic_machine_init(MachineClass *mc) mc->default_cpu_type = SPARC_CPU_TYPE_NAME("LEON3"); mc->default_ram_id = "leon3.ram"; mc->max_cpus = MAX_CPUS; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 19a2a9f2ff..d555548a1c 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -1113,7 +1113,7 @@ static void sun4m_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_display = "tcx"; mc->default_ram_id = "sun4m.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static void ss5_class_init(ObjectClass *oc, void *data) diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 409c67b1b4..37004b99c4 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -167,7 +167,7 @@ static void niagara_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Sun-UltraSparc-T1"); mc->default_ram_id = "sun4v-partition.ram"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo niagara_type = { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index eaf3d42bd0..6e9a3c5a2e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -809,7 +809,7 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->default_display = "std"; mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; fwc->get_dev_path = sun4u_fw_dev_path; compat_props_add(mc->compat_props, hw_compat_sparc64, hw_compat_sparc64_len); } @@ -837,7 +837,7 @@ static void sun4v_class_init(ObjectClass *oc, void *data) mc->default_display = "std"; mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo sun4v_type = { diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index 3cd93daf6a..d4550507a9 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -73,7 +73,7 @@ static void triboard_machine_tc277d_class_init(ObjectClass *oc, mc->init = triboard_machine_init; mc->desc = "Infineon AURIX TriBoard TC277 (D-Step)"; mc->max_cpus = 1; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; amc->soc_name = "tc277d-soc"; }; diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 2971805170..9299cd5394 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -111,7 +111,7 @@ static void ttb_machine_init(MachineClass *mc) mc->desc = "a minimal TriCore board"; mc->init = tricoreboard_init; mc->default_cpu_type = TRICORE_CPU_TYPE_NAME("tc1796"); - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("tricore_testboard", ttb_machine_init) diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index ed42e4b624..9df50cd538 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -381,7 +381,7 @@ static void xen_pvh_class_init(ObjectClass *oc, void *data) mc->default_machine_opts = "accel=xen"; /* Set to zero to make sure that the real ram size is passed. */ mc->default_ram_size = 0; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xen_pvh_info = { diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index a05713e5da..abdc5bc9a3 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -67,7 +67,7 @@ static void xenpv_machine_init(MachineClass *mc) mc->init = xen_init_pv; mc->max_cpus = 1; mc->default_machine_opts = "accel=xen"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("xenpv", xenpv_machine_init) diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 6c86d4939f..989cfd4918 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -125,7 +125,7 @@ static void xtensa_sim_machine_init(MachineClass *mc) mc->max_cpus = 4; mc->no_serial = 1; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("sim", xtensa_sim_machine_init) diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index ffa6f21063..0a78ab3a6f 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -122,7 +122,7 @@ static void xtensa_virt_machine_init(MachineClass *mc) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_nic = "virtio-net-pci"; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("virt", xtensa_virt_machine_init) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 2b5f20acef..e00ae9d2e2 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -594,7 +594,7 @@ static void xtfpga_lx60_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 64 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx60_type = { @@ -612,7 +612,7 @@ static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 64 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx60_nommu_type = { @@ -630,7 +630,7 @@ static void xtfpga_lx200_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 96 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx200_type = { @@ -648,7 +648,7 @@ static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 96 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx200_nommu_type = { @@ -666,7 +666,7 @@ static void xtfpga_ml605_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 512 * MiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_ml605_type = { @@ -684,7 +684,7 @@ static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 256 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_ml605_nommu_type = { @@ -702,7 +702,7 @@ static void xtfpga_kc705_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 1 * GiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_kc705_type = { @@ -720,7 +720,7 @@ static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 256 * MiB; - mc->no_sdcard = ON_OFF_AUTO_OFF; + mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_kc705_nommu_type = { diff --git a/include/hw/boards.h b/include/hw/boards.h index d61b0a4778..9360d1ce39 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -285,7 +285,7 @@ struct MachineClass { no_cdrom:1, pci_allow_0_address:1, legacy_fw_cfg_order:1; - OnOffAuto no_sdcard; + bool auto_create_sdcard; bool is_default; const char *default_machine_opts; const char *default_boot_order; diff --git a/system/vl.c b/system/vl.c index 3904d0d158..f9a0526d5f 100644 --- a/system/vl.c +++ b/system/vl.c @@ -194,7 +194,7 @@ static int default_parallel = 1; static int default_monitor = 1; static int default_floppy = 1; static int default_cdrom = 1; -static int default_sdcard = 1; +static bool auto_create_sdcard = true; static int default_vga = 1; static int default_net = 1; @@ -718,7 +718,7 @@ static void configure_blockdev(BlockdevOptionsQueue *bdo_queue, default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2, CDROM_OPTS); default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); - default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); + default_drive(auto_create_sdcard, snapshot, IF_SD, 0, SD_OPTS); } @@ -1346,9 +1346,8 @@ static void qemu_disable_default_devices(void) if (!has_defaults || machine_class->no_cdrom) { default_cdrom = 0; } - assert(machine_class->no_sdcard != ON_OFF_AUTO_AUTO); - if (!has_defaults || machine_class->no_sdcard == ON_OFF_AUTO_ON) { - default_sdcard = 0; + if (!has_defaults || !machine_class->auto_create_sdcard) { + auto_create_sdcard = false; } if (!has_defaults) { default_audio = 0; From 5824fad4e92e3d10de1ce86d900dcde8f8dfaf76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Feb 2025 13:17:43 +0100 Subject: [PATCH 1748/2892] hw/boards: Do not create unusable default if=sd drives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of machines create an if=sd drive by default even though they lack an SD bus, and therefore cannot use the drive. This drive is created when the machine sets flag @auto_create_sdcard. See for example running HMP "info block" on the HPPA C3700 machine: $ qemu-system-hppa -M C3700 -monitor stdio -S (qemu) info block floppy0: [not inserted] Removable device: not locked, tray closed sd0: [not inserted] Removable device: not locked, tray closed $ qemu-system-hppa -M C3700 -sd /bin/sh qemu-system-hppa: -sd /bin/sh: machine type does not support if=sd,bus=0,unit=0 Delete that from machines that lack an SD bus. Note, only the ARM and RISCV targets use such feature: $ git grep -wl IF_SD hw | cut -d/ -f-2 | sort -u hw/arm hw/riscv $ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250204200934.65279-5-philmd@linaro.org> --- hw/alpha/dp264.c | 1 - hw/avr/arduino.c | 1 - hw/hppa/machine.c | 2 - hw/i386/pc.c | 1 - hw/i386/x86.c | 1 - hw/i386/xen/xen-pvh.c | 1 - hw/loongarch/virt.c | 1 - hw/m68k/an5206.c | 1 - hw/m68k/mcf5208.c | 1 - hw/m68k/next-cube.c | 1 - hw/m68k/q800.c | 1 - hw/m68k/virt.c | 1 - hw/microblaze/petalogix_ml605_mmu.c | 1 - hw/microblaze/petalogix_s3adsp1800_mmu.c | 1 - hw/microblaze/xlnx-zynqmp-pmu.c | 1 - hw/mips/boston.c | 1 - hw/mips/fuloong2e.c | 1 - hw/mips/jazz.c | 2 - hw/mips/loongson3_virt.c | 1 - hw/mips/malta.c | 1 - hw/mips/mipssim.c | 1 - hw/openrisc/openrisc_sim.c | 1 - hw/openrisc/virt.c | 1 - hw/ppc/amigaone.c | 1 - hw/ppc/e500plat.c | 1 - hw/ppc/mac_newworld.c | 1 - hw/ppc/mac_oldworld.c | 1 - hw/ppc/mpc8544ds.c | 1 - hw/ppc/pegasos2.c | 1 - hw/ppc/pnv.c | 1 - hw/ppc/ppc405_boards.c | 1 - hw/ppc/ppc440_bamboo.c | 1 - hw/ppc/prep.c | 1 - hw/ppc/sam460ex.c | 1 - hw/ppc/spapr.c | 1 - hw/ppc/virtex_ml507.c | 1 - hw/remote/machine.c | 1 - hw/rx/rx-gdbsim.c | 1 - hw/sh4/r2d.c | 1 - hw/sparc/leon3.c | 1 - hw/sparc/sun4m.c | 1 - hw/sparc64/niagara.c | 1 - hw/sparc64/sun4u.c | 2 - hw/tricore/triboard.c | 1 - hw/tricore/tricore_testboard.c | 1 - hw/xen/xen-pvh-common.c | 1 - hw/xenpv/xen_machine_pv.c | 1 - hw/xtensa/sim.c | 1 - hw/xtensa/virt.c | 1 - hw/xtensa/xtfpga.c | 8 ---- tests/qemu-iotests/172.out | 60 ------------------------ 51 files changed, 120 deletions(-) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 14b942fd5a..570ea9edf2 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -213,7 +213,6 @@ static void clipper_machine_init(MachineClass *mc) mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67"); mc->default_ram_id = "ram"; mc->default_nic = "e1000"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/hw/avr/arduino.c b/hw/avr/arduino.c index 5f30a7d585..48ef478346 100644 --- a/hw/avr/arduino.c +++ b/hw/avr/arduino.c @@ -67,7 +67,6 @@ static void arduino_machine_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->auto_create_sdcard = true; } static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index b4b238ed2c..c5f247633e 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -704,7 +704,6 @@ static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; mc->default_nic = "tulip"; - mc->auto_create_sdcard = true; nc->nmi_monitor_handler = hppa_nmi; } @@ -741,7 +740,6 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; mc->default_nic = "tulip"; - mc->auto_create_sdcard = true; nc->nmi_monitor_handler = hppa_nmi; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a5324a1246..22641e6ddc 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1799,7 +1799,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; mc->smp_props.modules_supported = true; mc->default_ram_id = "pc.ram"; - mc->auto_create_sdcard = true; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 790b1863f5..69bfc00b9a 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -382,7 +382,6 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->kvm_type = x86_kvm_type; - mc->auto_create_sdcard = true; x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c index 6f5b6a2b8f..33c1027976 100644 --- a/hw/i386/xen/xen-pvh.c +++ b/hw/i386/xen/xen-pvh.c @@ -82,7 +82,6 @@ static void xen_pvh_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xen PVH x86 machine"; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; - mc->auto_create_sdcard = true; /* mc->max_cpus holds the MAX value allowed in the -smp cmd-line opts. */ mc->max_cpus = HVM_MAX_VCPUS; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a7f1c8acee..f2aa0a9782 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -954,7 +954,6 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; mc->no_cdrom = 1; - mc->auto_create_sdcard = true; mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 1947870512..d97399b882 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -99,7 +99,6 @@ static void an5206_machine_init(MachineClass *mc) mc->init = an5206_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5206"); mc->default_ram_id = "an5206.ram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("an5206", an5206_machine_init) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 9a8c551224..75cc076f78 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -398,7 +398,6 @@ static void mcf5208evb_machine_init(MachineClass *mc) mc->is_default = true; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); mc->default_ram_id = "mcf5208.ram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index ee06a5e2e0..0570e4a76f 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -1359,7 +1359,6 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); mc->no_cdrom = true; - mc->auto_create_sdcard = true; } static const TypeInfo next_typeinfo = { diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index f3cb8541b3..aeed4c8ddb 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -743,7 +743,6 @@ static void q800_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->block_default_type = IF_SCSI; mc->default_ram_id = "m68k_mac.ram"; - mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); compat_props_add(mc->compat_props, hw_compat_q800, hw_compat_q800_len); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 69e8f53482..d967bdd743 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -318,7 +318,6 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->no_floppy = 1; mc->no_parallel = 1; - mc->auto_create_sdcard = true; mc->default_ram_id = "m68k_virt.ram"; } diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 1e3d55dd3b..8b44be75a2 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -220,7 +220,6 @@ static void petalogix_ml605_machine_init(MachineClass *mc) mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; #endif mc->init = petalogix_ml605_init; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init) diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 95b1b6483f..2c0d8c34cd 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -142,7 +142,6 @@ static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, void *data) mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; mc->is_default = true; - mc->auto_create_sdcard = true; } static const TypeInfo petalogix_s3adsp1800_machine_types[] = { diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index b40d82b396..bdbf7328bf 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -188,7 +188,6 @@ static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; #endif mc->init = xlnx_zynqmp_pmu_init; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 66cdad639e..364c328032 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -842,7 +842,6 @@ static void boston_mach_class_init(MachineClass *mc) mc->default_ram_id = "boston.ddr"; mc->max_cpus = 16; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); - mc->auto_create_sdcard = true; } DEFINE_MACHINE("boston", boston_mach_class_init) diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index e10b5a2774..646044e274 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -335,7 +335,6 @@ static void mips_fuloong2e_machine_init(MachineClass *mc) mc->default_ram_size = 256 * MiB; mc->default_ram_id = "fuloong2e.ram"; mc->minimum_page_bits = 14; - mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index ad4561a51e..c89610639a 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -424,7 +424,6 @@ static void mips_magnum_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); mc->default_ram_id = "mips_jazz.ram"; - mc->auto_create_sdcard = true; } static const TypeInfo mips_magnum_type = { @@ -442,7 +441,6 @@ static void mips_pica61_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); mc->default_ram_id = "mips_jazz.ram"; - mc->auto_create_sdcard = true; } static const TypeInfo mips_pica61_type = { diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 46c2e1e9de..831fddb1bd 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -679,7 +679,6 @@ static void loongson3v_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1600 * MiB; mc->minimum_page_bits = 14; mc->default_nic = "virtio-net-pci"; - mc->auto_create_sdcard = true; } static const TypeInfo loongson3_machine_types[] = { diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 31ff279b4c..8e9cea70b1 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -1306,7 +1306,6 @@ static void mips_malta_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_malta.ram"; - mc->auto_create_sdcard = true; compat_props_add(mc->compat_props, malta_compat, malta_compat_len); } diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index ff2b905070..c530688e76 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -247,7 +247,6 @@ static void mips_mipssim_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_mipssim.ram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 5e4686ba7a..e0da4067ba 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -368,7 +368,6 @@ static void openrisc_sim_machine_init(ObjectClass *oc, void *data) mc->max_cpus = OR1KSIM_CPUS_MAX; mc->is_default = true; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); - mc->auto_create_sdcard = true; } static const TypeInfo or1ksim_machine_typeinfo = { diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 2764e398a9..7b60bf8509 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -554,7 +554,6 @@ static void openrisc_virt_machine_init(ObjectClass *oc, void *data) mc->max_cpus = VIRT_CPUS_MAX; mc->is_default = false; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); - mc->auto_create_sdcard = true; } static const TypeInfo or1ksim_machine_typeinfo = { diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 296c30da92..b02792221c 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -173,7 +173,6 @@ static void amigaone_machine_init(MachineClass *mc) mc->default_display = "std"; mc->default_ram_id = "ram"; mc->default_ram_size = 512 * MiB; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("amigaone", amigaone_machine_init) diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index afad4802ca..70a8033373 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -100,7 +100,6 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; mc->default_nic = "virtio-net-pci"; - mc->auto_create_sdcard = true; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 869f3f7104..cb3dc3ab48 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -580,7 +580,6 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->default_display = "std"; mc->default_nic = "sungem"; - mc->auto_create_sdcard = true; mc->kvm_type = core99_kvm_type; #ifdef TARGET_PPC64 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("970fx_v3.1"); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 08e30a4a4e..0dbcea035c 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -427,7 +427,6 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->default_nic = "ne2k_pci"; mc->ignore_boot_device_suffixes = true; mc->default_ram_id = "ppc_heathrow.ram"; - mc->auto_create_sdcard = true; fwc->get_dev_path = heathrow_fw_dev_path; } diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 38bdf45316..d74af766ee 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -62,7 +62,6 @@ static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; mc->default_nic = "virtio-net-pci"; - mc->auto_create_sdcard = true; } #define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 479dcfe809..b057672e82 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -604,7 +604,6 @@ static void pegasos2_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); mc->default_ram_id = "pegasos2.ram"; mc->default_ram_size = 512 * MiB; - mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); vhc->cpu_in_nested = pegasos2_cpu_in_nested; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 15fbbf6c15..11fd477b71 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2878,7 +2878,6 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) /* Pnv provides a AHCI device for storage */ mc->block_default_type = IF_IDE; mc->no_parallel = 1; - mc->auto_create_sdcard = true; mc->default_boot_order = NULL; /* * RAM defaults to less than 2048 for 32-bit hosts, and large diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 8946b5173c..969cac345a 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -351,7 +351,6 @@ static void ppc405_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 128 * MiB; mc->default_ram_id = "ppc405.ram"; mc->deprecation_reason = "machine is old and unmaintained"; - mc->auto_create_sdcard = true; } static const TypeInfo ppc405_machine_type = { diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 081a993ef0..099fda3909 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -268,7 +268,6 @@ static void bamboo_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); mc->default_ram_id = "ppc4xx.sdram"; mc->default_nic = "e1000"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 85bfc2fd4f..3e68d8e6e2 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -428,7 +428,6 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); mc->default_display = "std"; mc->default_nic = "pcnet"; - mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index d9c871ef20..3ecae6a950 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -524,7 +524,6 @@ static void sam460ex_machine_init(MachineClass *mc) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc4xx.sdram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 42b07fadd9..f3a4b4235d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4594,7 +4594,6 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = SPAPR_IRQ_NR_IPIS; mc->no_parallel = 1; - mc->auto_create_sdcard = true; mc->default_boot_order = ""; mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc_spapr.ram"; diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 22184fb698..2323811927 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -288,7 +288,6 @@ static void virtex_machine_init(MachineClass *mc) mc->init = virtex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440-xilinx"); mc->default_ram_id = "ram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("virtex-ml507", virtex_machine_init) diff --git a/hw/remote/machine.c b/hw/remote/machine.c index fa4a1bb815..fdc6c441bb 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -128,7 +128,6 @@ static void remote_machine_class_init(ObjectClass *oc, void *data) mc->init = remote_machine_init; mc->desc = "Experimental remote machine"; - mc->auto_create_sdcard = true; hc->unplug = remote_machine_dev_unplug_cb; diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index de3b708bc5..88c8f12c10 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -166,7 +166,6 @@ static void rx_gdbsim_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RX62N_CPU; mc->default_ram_size = 16 * MiB; mc->default_ram_id = "ext-sdram"; - mc->auto_create_sdcard = true; } static void rx62n7_class_init(ObjectClass *oc, void *data) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index e34deb33dc..d68c94e82e 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -383,7 +383,6 @@ static void r2d_machine_init(MachineClass *mc) mc->block_default_type = IF_IDE; mc->default_cpu_type = TYPE_SH7751R_CPU; mc->default_nic = "rtl8139"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("r2d", r2d_machine_init) diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 3bd2449575..0aeaad3bec 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -440,7 +440,6 @@ static void leon3_generic_machine_init(MachineClass *mc) mc->default_cpu_type = SPARC_CPU_TYPE_NAME("LEON3"); mc->default_ram_id = "leon3.ram"; mc->max_cpus = MAX_CPUS; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index d555548a1c..a48d3622c5 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -1113,7 +1113,6 @@ static void sun4m_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_display = "tcx"; mc->default_ram_id = "sun4m.ram"; - mc->auto_create_sdcard = true; } static void ss5_class_init(ObjectClass *oc, void *data) diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 37004b99c4..805ba6b1e3 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -167,7 +167,6 @@ static void niagara_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Sun-UltraSparc-T1"); mc->default_ram_id = "sun4v-partition.ram"; - mc->auto_create_sdcard = true; } static const TypeInfo niagara_type = { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 6e9a3c5a2e..8ab5cf0461 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -809,7 +809,6 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->default_display = "std"; mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); - mc->auto_create_sdcard = true; fwc->get_dev_path = sun4u_fw_dev_path; compat_props_add(mc->compat_props, hw_compat_sparc64, hw_compat_sparc64_len); } @@ -837,7 +836,6 @@ static void sun4v_class_init(ObjectClass *oc, void *data) mc->default_display = "std"; mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); - mc->auto_create_sdcard = true; } static const TypeInfo sun4v_type = { diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index d4550507a9..f5baa8ccbb 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -73,7 +73,6 @@ static void triboard_machine_tc277d_class_init(ObjectClass *oc, mc->init = triboard_machine_init; mc->desc = "Infineon AURIX TriBoard TC277 (D-Step)"; mc->max_cpus = 1; - mc->auto_create_sdcard = true; amc->soc_name = "tc277d-soc"; }; diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 9299cd5394..3facfdfd61 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -111,7 +111,6 @@ static void ttb_machine_init(MachineClass *mc) mc->desc = "a minimal TriCore board"; mc->init = tricoreboard_init; mc->default_cpu_type = TRICORE_CPU_TYPE_NAME("tc1796"); - mc->auto_create_sdcard = true; } DEFINE_MACHINE("tricore_testboard", ttb_machine_init) diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index 9df50cd538..9c21fa858d 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -381,7 +381,6 @@ static void xen_pvh_class_init(ObjectClass *oc, void *data) mc->default_machine_opts = "accel=xen"; /* Set to zero to make sure that the real ram size is passed. */ mc->default_ram_size = 0; - mc->auto_create_sdcard = true; } static const TypeInfo xen_pvh_info = { diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index abdc5bc9a3..99c02492ef 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -67,7 +67,6 @@ static void xenpv_machine_init(MachineClass *mc) mc->init = xen_init_pv; mc->max_cpus = 1; mc->default_machine_opts = "accel=xen"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("xenpv", xenpv_machine_init) diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 989cfd4918..1cea29c66d 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -125,7 +125,6 @@ static void xtensa_sim_machine_init(MachineClass *mc) mc->max_cpus = 4; mc->no_serial = 1; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("sim", xtensa_sim_machine_init) diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index 0a78ab3a6f..b08404fc17 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -122,7 +122,6 @@ static void xtensa_virt_machine_init(MachineClass *mc) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_nic = "virtio-net-pci"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("virt", xtensa_virt_machine_init) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index e00ae9d2e2..3f3677f1c9 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -594,7 +594,6 @@ static void xtfpga_lx60_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 64 * MiB; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx60_type = { @@ -612,7 +611,6 @@ static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 64 * MiB; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx60_nommu_type = { @@ -630,7 +628,6 @@ static void xtfpga_lx200_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 96 * MiB; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx200_type = { @@ -648,7 +645,6 @@ static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 96 * MiB; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_lx200_nommu_type = { @@ -666,7 +662,6 @@ static void xtfpga_ml605_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 512 * MiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_ml605_type = { @@ -684,7 +679,6 @@ static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 256 * MiB; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_ml605_nommu_type = { @@ -702,7 +696,6 @@ static void xtfpga_kc705_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; mc->default_ram_size = 1 * GiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_kc705_type = { @@ -720,7 +713,6 @@ static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; mc->default_ram_size = 256 * MiB; - mc->auto_create_sdcard = true; } static const TypeInfo xtfpga_kc705_nommu_type = { diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out index 07eebf3583..146fc72388 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -68,9 +68,6 @@ floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -125,9 +122,6 @@ ide1-cd0: [not inserted] floppy0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -183,9 +177,6 @@ floppy1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -265,9 +256,6 @@ floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -322,9 +310,6 @@ ide1-cd0: [not inserted] floppy0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -380,9 +365,6 @@ floppy1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -422,9 +404,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -461,9 +440,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -519,9 +495,6 @@ none1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -586,9 +559,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -644,9 +614,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -702,9 +669,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -760,9 +724,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -827,9 +788,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -885,9 +843,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -930,9 +885,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1106,9 +1058,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1145,9 +1094,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1187,9 +1133,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1226,9 +1169,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit From f208970a06e2af65c351dd6e12df4ca4f382acdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Feb 2025 16:59:18 +0100 Subject: [PATCH 1749/2892] hw/arm: Remove all invalid uses of auto_create_sdcard=true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MachineClass::auto_create_sdcard is only useful to automatically create a SD card, attach a IF_SD block drive to it and plug the card onto a SD bus. None of the ARM machines modified by this commit try to use the IF_SD interface. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250204200934.65279-6-philmd@linaro.org> --- hw/arm/aspeed.c | 1 - hw/arm/b-l475e-iot01a.c | 1 - hw/arm/collie.c | 1 - hw/arm/digic_boards.c | 1 - hw/arm/highbank.c | 2 -- hw/arm/kzm.c | 1 - hw/arm/microbit.c | 1 - hw/arm/mps2-tz.c | 4 ---- hw/arm/mps2.c | 4 ---- hw/arm/mps3r.c | 1 - hw/arm/msf2-som.c | 1 - hw/arm/musca.c | 2 -- hw/arm/musicpal.c | 1 - hw/arm/netduino2.c | 1 - hw/arm/netduinoplus2.c | 1 - hw/arm/olimex-stm32-h405.c | 1 - hw/arm/sbsa-ref.c | 1 - hw/arm/stellaris.c | 1 - hw/arm/stm32vldiscovery.c | 1 - hw/arm/virt.c | 1 - hw/arm/xen-pvh.c | 1 - 21 files changed, 29 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index f3ba90896c..98bf071139 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1658,7 +1658,6 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, amc->spi_model = "w25q256"; amc->num_cs = 2; amc->macs_mask = 0; - mc->auto_create_sdcard = true; aspeed_machine_class_init_cpus_defaults(mc); } diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index f05ee0fee0..c9a5209216 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -120,7 +120,6 @@ static void bl475e_machine_init(ObjectClass *oc, void *data) mc->desc = "B-L475E-IOT01A Discovery Kit (Cortex-M4)"; mc->init = bl475e_init; mc->valid_cpu_types = machine_valid_cpu_types; - mc->auto_create_sdcard = true; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 864c66193b..eaa5c52d45 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -79,7 +79,6 @@ static void collie_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("sa1110"); mc->default_ram_size = RAM_SIZE; mc->default_ram_id = "strongarm.sdram"; - mc->auto_create_sdcard = true; } static const TypeInfo collie_machine_typeinfo = { diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index f334c1fb02..2492fafeb8 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -143,7 +143,6 @@ static void canon_a1100_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; mc->default_ram_size = 64 * MiB; mc->default_ram_id = "ram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 975fd7a094..495704d972 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -357,7 +357,6 @@ static void highbank_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; - mc->auto_create_sdcard = true; } static const TypeInfo highbank_type = { @@ -382,7 +381,6 @@ static void midway_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; - mc->auto_create_sdcard = true; } static const TypeInfo midway_type = { diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 736eabab66..08d2b3025c 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -137,7 +137,6 @@ static void kzm_machine_init(MachineClass *mc) mc->init = kzm_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "kzm.ram"; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("kzm", kzm_machine_init) diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index fb09950832..3f56fb45ce 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -67,7 +67,6 @@ static void microbit_machine_class_init(ObjectClass *oc, void *data) mc->desc = "BBC micro:bit (Cortex-M0)"; mc->init = microbit_init; mc->max_cpus = 1; - mc->auto_create_sdcard = true; } static const TypeInfo microbit_info = { diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 91b8ae6d38..13ed868b6b 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1320,7 +1320,6 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41045050; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1355,7 +1354,6 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN521; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41045210; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1390,7 +1388,6 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN524; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41045240; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1430,7 +1427,6 @@ static void mps3tz_an547_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN547; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m55"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41055470; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = 25 * 1000 * 1000; /* 25MHz */ diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 40eb5d1618..3f8db0cab6 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -487,7 +487,6 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41043850; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -507,7 +506,6 @@ static void mps2_an386_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN386; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41043860; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -527,7 +525,6 @@ static void mps2_an500_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN500; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m7"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41045000; mmc->psram_base = 0x60000000; mmc->ethernet_base = 0xa0000000; @@ -547,7 +544,6 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->scc_id = 0x41045110; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index f26d1cfb2c..1bddb5e822 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -618,7 +618,6 @@ static void mps3r_an536_class_init(ObjectClass *oc, void *data) mc->max_cpus = 2; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-r52"); mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; mmc->raminfo = an536_raminfo; mps3r_set_default_ram_info(mmc); } diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 6d3f0a89e0..9b20f1e2c9 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -106,7 +106,6 @@ static void emcraft_sf2_machine_init(MachineClass *mc) mc->desc = "SmartFusion2 SOM kit from Emcraft (M2S010)"; mc->init = emcraft_sf2_s2s010_init; mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("emcraft-sf2", emcraft_sf2_machine_init) diff --git a/hw/arm/musca.c b/hw/arm/musca.c index 6f19b7d58a..e9c092abc3 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -615,7 +615,6 @@ static void musca_a_class_init(ObjectClass *oc, void *data) MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); mc->desc = "ARM Musca-A board (dual Cortex-M33)"; - mc->auto_create_sdcard = true; mmc->type = MUSCA_A; mmc->init_svtor = 0x10200000; mmc->sram_addr_width = 15; @@ -630,7 +629,6 @@ static void musca_b1_class_init(ObjectClass *oc, void *data) MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); mc->desc = "ARM Musca-B1 board (dual Cortex-M33)"; - mc->auto_create_sdcard = true; mmc->type = MUSCA_B1; /* * This matches the DAPlink firmware which boots from QSPI. There diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index fd2975753e..48a32c2407 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1342,7 +1342,6 @@ static void musicpal_machine_init(MachineClass *mc) mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_size = MP_RAM_DEFAULT_SIZE; mc->default_ram_id = "musicpal.ram"; - mc->auto_create_sdcard = true; machine_add_audiodev_property(mc); } diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index fca32d4592..df793c77fe 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -63,7 +63,6 @@ static void netduino2_machine_init(MachineClass *mc) mc->init = netduino2_init; mc->valid_cpu_types = valid_cpu_types; mc->ignore_memory_transaction_failures = true; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("netduino2", netduino2_machine_init) diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index e1a59fb9e2..81b6334cf7 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -63,7 +63,6 @@ static void netduinoplus2_machine_init(MachineClass *mc) mc->desc = "Netduino Plus 2 Machine (Cortex-M4)"; mc->init = netduinoplus2_init; mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("netduinoplus2", netduinoplus2_machine_init) diff --git a/hw/arm/olimex-stm32-h405.c b/hw/arm/olimex-stm32-h405.c index 23f686de87..1f15620f9f 100644 --- a/hw/arm/olimex-stm32-h405.c +++ b/hw/arm/olimex-stm32-h405.c @@ -66,7 +66,6 @@ static void olimex_stm32_h405_machine_init(MachineClass *mc) mc->desc = "Olimex STM32-H405 (Cortex-M4)"; mc->init = olimex_stm32_h405_init; mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 02c72a62a3..e720de3064 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -900,7 +900,6 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) mc->minimum_page_bits = 12; mc->block_default_type = IF_IDE; mc->no_cdrom = 1; - mc->auto_create_sdcard = true; mc->default_nic = "e1000e"; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "sbsa-ref.ram"; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 25283fd623..3361111360 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1421,7 +1421,6 @@ static void lm3s811evb_class_init(ObjectClass *oc, void *data) mc->init = lm3s811evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mc->auto_create_sdcard = true; } static const TypeInfo lm3s811evb_type = { diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index a71da292b8..e6c1f5b8d7 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -66,7 +66,6 @@ static void stm32vldiscovery_machine_init(MachineClass *mc) mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; mc->init = stm32vldiscovery_init; mc->valid_cpu_types = valid_cpu_types; - mc->auto_create_sdcard = true; } DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9aea06b707..4a5a9666e9 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3125,7 +3125,6 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) #endif mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; - mc->auto_create_sdcard = true; mc->pci_allow_0_address = true; /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */ mc->minimum_page_bits = 12; diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c index ce4cc4fce9..d1509bd235 100644 --- a/hw/arm/xen-pvh.c +++ b/hw/arm/xen-pvh.c @@ -75,7 +75,6 @@ static void xen_arm_machine_class_init(ObjectClass *oc, void *data) * mc->max_cpus, QEMU will bail out with an error message. */ mc->max_cpus = GUEST_MAX_VCPUS; - mc->auto_create_sdcard = true; /* Xen/ARM does not use buffered IOREQs. */ xpc->handle_bufioreq = HVM_IOREQSRV_BUFIOREQ_OFF; From b5d5edc1d9564b5156230860e494e1279099587d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Feb 2025 16:47:50 +0100 Subject: [PATCH 1750/2892] hw/riscv: Remove all invalid uses of auto_create_sdcard=true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MachineClass::auto_create_sdcard is only useful to automatically create a SD card, attach a IF_SD block drive to it and plug the card onto a SD bus. None of the RISCV machines modified by this commit try to use the IF_SD interface. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250204200934.65279-7-philmd@linaro.org> --- hw/riscv/opentitan.c | 1 - hw/riscv/shakti_c.c | 1 - hw/riscv/sifive_e.c | 1 - hw/riscv/spike.c | 1 - hw/riscv/virt.c | 1 - 5 files changed, 5 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index d78a96c535..b9e56235d8 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -121,7 +121,6 @@ static void opentitan_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; - mc->auto_create_sdcard = true; } static void lowrisc_ibex_soc_init(Object *obj) diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index efe814b586..e2242b97d0 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -84,7 +84,6 @@ static void shakti_c_machine_class_init(ObjectClass *klass, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_SHAKTI_C; mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "riscv.shakti.c.ram"; - mc->auto_create_sdcard = true; } static const TypeInfo shakti_c_machine_type_info = { diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 164eb3ab83..73d3b74281 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -153,7 +153,6 @@ static void sifive_e_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SIFIVE_E_CPU; mc->default_ram_id = "riscv.sifive.e.ram"; mc->default_ram_size = sifive_e_memmap[SIFIVE_E_DEV_DTIM].size; - mc->auto_create_sdcard = true; object_class_property_add_bool(oc, "revb", sifive_e_machine_get_revb, sifive_e_machine_set_revb); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 1ea35937e1..74a20016f1 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -358,7 +358,6 @@ static void spike_machine_class_init(ObjectClass *oc, void *data) /* platform instead of architectural choice */ mc->cpu_cluster_has_numa_boundary = true; mc->default_ram_id = "riscv.spike.ram"; - mc->auto_create_sdcard = true; object_class_property_add_str(oc, "signature", NULL, spike_set_signature); object_class_property_set_description(oc, "signature", "File to write ACT test signature"); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2aa420f6e5..241389d72f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1918,7 +1918,6 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TYPE_RISCV_CPU_BASE; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; - mc->auto_create_sdcard = true; mc->pci_allow_0_address = true; mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; From 2be22bc72d18d92ca9e0c16e56bf309839360f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Feb 2025 14:42:01 +0100 Subject: [PATCH 1751/2892] hw/boards: Ensure machine setting auto_create_sdcard expose a SD Bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the auto_create_sdcard feature without SD Bus is irrelevant. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250204200934.65279-8-philmd@linaro.org> --- system/vl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/system/vl.c b/system/vl.c index f9a0526d5f..2a570ed9ff 100644 --- a/system/vl.c +++ b/system/vl.c @@ -53,6 +53,7 @@ #include "hw/usb.h" #include "hw/isa/isa.h" #include "hw/scsi/scsi.h" +#include "hw/sd/sd.h" #include "hw/display/vga.h" #include "hw/firmware/smbios.h" #include "hw/acpi/acpi.h" @@ -2661,12 +2662,27 @@ static void qemu_init_displays(void) static void qemu_init_board(void) { + MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); + /* process plugin before CPUs are created, but once -smp has been parsed */ qemu_plugin_load_list(&plugin_list, &error_fatal); /* From here on we enter MACHINE_PHASE_INITIALIZED. */ machine_run_board_init(current_machine, mem_path, &error_fatal); + if (machine_class->auto_create_sdcard) { + bool ambigous; + + /* Ensure there is a SD bus available to create SD card on */ + Object *obj = object_resolve_path_type("", TYPE_SD_BUS, &ambigous); + if (!obj && !ambigous) { + fprintf(stderr, "Can not create sd-card on '%s' machine" + " because it lacks a sd-bus\n", + machine_class->name); + abort(); + } + } + drive_check_orphaned(); realtime_init(); From bb741c4f405cf5ade6d2cf3cbbf776a6f26016dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Feb 2025 16:24:25 +0100 Subject: [PATCH 1752/2892] hw/riscv/opentitan: Include missing 'exec/address-spaces.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opentitan_machine_init() calls get_system_memory(), which is declared in "exec/address-spaces.h". Include it in order to avoid when refactoring unrelated headers: hw/riscv/opentitan.c:83:29: error: call to undeclared function 'get_system_memory' 83 | MemoryRegion *sys_mem = get_system_memory(); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20250206181827.41557-4-philmd@linaro.org> --- hw/riscv/opentitan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index b9e56235d8..98a67fe52a 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -28,6 +28,7 @@ #include "hw/riscv/boot.h" #include "qemu/units.h" #include "system/system.h" +#include "exec/address-spaces.h" /* * This version of the OpenTitan machine currently supports From 937df81af6757638a7f1908747560dd342947213 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Feb 2025 15:11:57 +0000 Subject: [PATCH 1753/2892] hw/net/smc91c111: Ignore attempt to pop from empty RX fifo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMC91C111 includes an MMU Command register which permits the guest to remove entries from the RX FIFO. The datasheet does not specify what happens if the guest tries to do this when the FIFO is already empty; there are no status registers containing error bits which might be applicable. Currently we don't guard at all against pop of an empty RX FIFO, with the result that we allow the guest to drive the rx_fifo_len index to negative values, which will cause smc91c111_receive() to write to the rx_fifo[] array out of bounds when we receive the next packet. Instead ignore attempts to pop an empty RX FIFO. Cc: qemu-stable@nongnu.org Fixes: 80337b66a8e7 ("NIC emulation for qemu arm-softmmu") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2780 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250207151157.3151776-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/smc91c111.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index b18d5c23c3..0e13dfa18b 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -182,6 +182,15 @@ static void smc91c111_pop_rx_fifo(smc91c111_state *s) { int i; + if (s->rx_fifo_len == 0) { + /* + * The datasheet doesn't document what the behaviour is if the + * guest tries to pop an empty RX FIFO, and there's no obvious + * error status register to report it. Just ignore the attempt. + */ + return; + } + s->rx_fifo_len--; if (s->rx_fifo_len) { for (i = 0; i < s->rx_fifo_len; i++) From a029fe842f39af07ff5a203a34ebd5243df0e396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Sep 2024 22:16:12 +0200 Subject: [PATCH 1754/2892] tests/functional: Explicit endianness of microblaze assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The archive used in test_microblaze_s3adsp1800.py (testing a big-endian target) contains a big-endian kernel. Rename using the _BE suffix. Similarly, the archive in test_microblazeel_s3adsp1800 (testing a little-endian target) contains a little-endian kernel. Rename using _LE suffix. These changes will help when adding cross-endian kernel tests. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-Id: <20250206131052.30207-13-philmd@linaro.org> --- tests/functional/test_microblaze_s3adsp1800.py | 6 +++--- tests/functional/test_microblazeel_s3adsp1800.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index 2c4464bd05..fac364b1ea 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -15,14 +15,14 @@ class MicroblazeMachine(QemuSystemTest): timeout = 90 - ASSET_IMAGE = Asset( + ASSET_IMAGE_BE = Asset( ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' 'day17.tar.xz'), '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') - def test_microblaze_s3adsp1800(self): + def test_microblaze_s3adsp1800_be(self): self.set_machine('petalogix-s3adsp1800') - self.archive_extract(self.ASSET_IMAGE) + self.archive_extract(self.ASSET_IMAGE_BE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day17', 'ballerina.bin')) diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index c382afe6bf..5d353dba5d 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -17,14 +17,14 @@ class MicroblazeelMachine(QemuSystemTest): timeout = 90 - ASSET_IMAGE = Asset( + ASSET_IMAGE_LE = Asset( ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') - def test_microblazeel_s3adsp1800(self): + def test_microblazeel_s3adsp1800_le(self): self.require_netdev('user') self.set_machine('petalogix-s3adsp1800') - self.archive_extract(self.ASSET_IMAGE) + self.archive_extract(self.ASSET_IMAGE_LE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) tftproot = self.scratch_file('day13') From fe52b090c03bfb93a76883b8234fd42e975cb930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Feb 2025 11:47:13 +0100 Subject: [PATCH 1755/2892] tests/functional: Allow microblaze tests to take a machine name argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make microblaze tests a bit more generic. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250206131052.30207-14-philmd@linaro.org> --- tests/functional/test_microblaze_s3adsp1800.py | 7 +++++-- tests/functional/test_microblazeel_s3adsp1800.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index fac364b1ea..c4226f49cf 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -20,8 +20,8 @@ class MicroblazeMachine(QemuSystemTest): 'day17.tar.xz'), '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') - def test_microblaze_s3adsp1800_be(self): - self.set_machine('petalogix-s3adsp1800') + def do_ballerina_be_test(self, machine): + self.set_machine(machine) self.archive_extract(self.ASSET_IMAGE_BE) self.vm.set_console() self.vm.add_args('-kernel', @@ -34,5 +34,8 @@ class MicroblazeMachine(QemuSystemTest): # message, that's why we don't test for a later string here. This # needs some investigation by a microblaze wizard one day... + def test_microblaze_s3adsp1800_legacy_be(self): + self.do_ballerina_be_test('petalogix-s3adsp1800') + if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index 5d353dba5d..715ef3f79a 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -21,9 +21,9 @@ class MicroblazeelMachine(QemuSystemTest): ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') - def test_microblazeel_s3adsp1800_le(self): + def do_xmaton_le_test(self, machine): self.require_netdev('user') - self.set_machine('petalogix-s3adsp1800') + self.set_machine(machine) self.archive_extract(self.ASSET_IMAGE_LE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) @@ -38,5 +38,8 @@ class MicroblazeelMachine(QemuSystemTest): 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', '821cd3cab8efd16ad6ee5acc3642a8ea') + def test_microblaze_s3adsp1800_legacy_le(self): + self.do_xmaton_le_test('petalogix-s3adsp1800') + if __name__ == '__main__': QemuSystemTest.main() From 94dbecb994a38cafa057e5bfa515cef6faadcea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Feb 2025 13:58:38 +0100 Subject: [PATCH 1756/2892] tests/functional: Remove sleep() kludges from microblaze tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f0ec14c78c4 ("tests/avocado: Fix console data loss") fixed QEMUMachine's problem with console, we don't need to use the sleep() kludges. Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250206131052.30207-15-philmd@linaro.org> --- tests/functional/test_microblazeel_s3adsp1800.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index 715ef3f79a..60aab4a45e 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -7,8 +7,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import time -from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern @@ -31,9 +30,8 @@ class MicroblazeelMachine(QemuSystemTest): self.vm.add_args('-nic', f'user,tftp={tftproot}') self.vm.launch() wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) + wait_for_console_pattern(self, 'buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') exec_command_and_wait_for_pattern(self, 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', '821cd3cab8efd16ad6ee5acc3642a8ea') From d31f1185fb029b44c439a6961a6cb087df6567d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Feb 2025 09:49:30 +0100 Subject: [PATCH 1757/2892] hw: Declare various const data as 'const' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210133134.90879-7-philmd@linaro.org> --- hw/isa/vt82c686.c | 2 +- hw/rtc/m48t59-isa.c | 2 +- hw/rtc/m48t59.c | 2 +- hw/sensor/tmp421.c | 2 +- hw/usb/hcd-ehci-pci.c | 2 +- hw/usb/hcd-uhci.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 6f44b381a5..43bd67eeef 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -224,7 +224,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - ViaPMInitInfo *info = data; + const ViaPMInitInfo *info = data; k->realize = via_pm_realize; k->config_write = pm_write_config; diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index 38bc8dcf10..9c3855a3ef 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -129,7 +129,7 @@ static void m48txx_isa_class_init(ObjectClass *klass, void *data) static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) { M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); - M48txxInfo *info = data; + const M48txxInfo *info = data; u->info = *info; } diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index c9bd6f878f..3fb2f27d9d 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -639,7 +639,7 @@ static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) { M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); - M48txxInfo *info = data; + const M48txxInfo *info = data; u->info = *info; } diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index 82e604279c..007f7cd018 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -68,7 +68,7 @@ struct TMP421State { struct TMP421Class { I2CSlaveClass parent_class; - DeviceInfo *dev; + const DeviceInfo *dev; }; #define TYPE_TMP421 "tmp421-generic" diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index d410c38a8a..e00316721a 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -182,7 +182,7 @@ static void ehci_data_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - EHCIPCIInfo *i = data; + const EHCIPCIInfo *i = data; k->vendor_id = i->vendor_id; k->device_id = i->device_id; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 8528d493d6..0561a6d801 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1289,7 +1289,7 @@ void uhci_data_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); UHCIPCIDeviceClass *u = UHCI_CLASS(klass); - UHCIInfo *info = data; + const UHCIInfo *info = data; k->realize = info->realize ? info->realize : usb_uhci_common_realize; k->exit = info->unplug ? usb_uhci_exit : NULL; From 788369f477a3c89023f5ab19590baee4239623bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Feb 2025 09:49:38 +0100 Subject: [PATCH 1758/2892] hw: Make class data 'const' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the %data argument is not modified, we can declare it const. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210133134.90879-8-philmd@linaro.org> --- hw/sd/sdhci-internal.h | 2 +- hw/sd/sdhci.c | 2 +- hw/sensor/emc141x.c | 2 +- hw/sensor/isl_pmbus_vr.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index 5f3765f12d..9f768c418e 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -322,6 +322,6 @@ void sdhci_initfn(SDHCIState *s); void sdhci_uninitfn(SDHCIState *s); void sdhci_common_realize(SDHCIState *s, Error **errp); void sdhci_common_unrealize(SDHCIState *s); -void sdhci_common_class_init(ObjectClass *klass, void *data); +void sdhci_common_class_init(ObjectClass *klass, const void *data); #endif diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 99dd4a4e95..1f45a77566 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1542,7 +1542,7 @@ const VMStateDescription sdhci_vmstate = { }, }; -void sdhci_common_class_init(ObjectClass *klass, void *data) +void sdhci_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/emc141x.c b/hw/sensor/emc141x.c index aeccd2a3c9..33c1bd330f 100644 --- a/hw/sensor/emc141x.c +++ b/hw/sensor/emc141x.c @@ -265,7 +265,7 @@ static void emc141x_initfn(Object *obj) emc141x_set_temperature, NULL, NULL); } -static void emc141x_class_init(ObjectClass *klass, void *data) +static void emc141x_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index 304a66ea8b..c60282cfe7 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -233,7 +233,7 @@ static void raa228000_init(Object *obj) isl_pmbus_vr_add_props(obj, flags, 1); } -static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data, +static void isl_pmbus_vr_class_init(ObjectClass *klass, const void *data, uint8_t pages) { PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); From 4ec96630f93ec2a1fd8bf9c9150cdae330531de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 10:56:26 +0100 Subject: [PATCH 1759/2892] hw/qdev-properties-system: Introduce EndianMode QAPI enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the EndianMode type and the DEFINE_PROP_ENDIAN() macros. Endianness can be BIG, LITTLE or unspecified (default). Reviewed-by: Thomas Huth Acked-by: Markus Armbruster Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250213122217.62654-2-philmd@linaro.org> --- hw/core/qdev-properties-system.c | 11 +++++++++++ include/hw/qdev-properties-system.h | 7 +++++++ qapi/common.json | 14 ++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a96675beb0..89f954f569 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1283,3 +1283,14 @@ const PropertyInfo qdev_prop_iothread_vq_mapping_list = { .set = set_iothread_vq_mapping_list, .release = release_iothread_vq_mapping_list, }; + +/* --- Endian modes */ + +const PropertyInfo qdev_prop_endian_mode = { + .name = "EndianMode", + .description = "Endian mode, big/little/unspecified", + .enum_table = &EndianMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index 7ec37f6316..ead4dfc2f0 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -30,6 +30,7 @@ extern const PropertyInfo qdev_prop_pcie_link_speed; extern const PropertyInfo qdev_prop_pcie_link_width; extern const PropertyInfo qdev_prop_cpus390entitlement; extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; +extern const PropertyInfo qdev_prop_endian_mode; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) @@ -97,4 +98,10 @@ extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; DEFINE_PROP(_name, _state, _field, qdev_prop_iothread_vq_mapping_list, \ IOThreadVirtQueueMappingList *) +#define DEFINE_PROP_ENDIAN(_name, _state, _field, _default) \ + DEFINE_PROP_UNSIGNED(_name, _state, _field, _default, \ + qdev_prop_endian_mode, EndianMode) +#define DEFINE_PROP_ENDIAN_NODEFAULT(_name, _state, _field) \ + DEFINE_PROP_ENDIAN(_name, _state, _field, ENDIAN_MODE_UNSPECIFIED) + #endif diff --git a/qapi/common.json b/qapi/common.json index 6ffc7a3789..0e3a0bbbfb 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -212,3 +212,17 @@ ## { 'struct': 'HumanReadableText', 'data': { 'human-readable-text': 'str' } } + +## +# @EndianMode: +# +# @unspecified: Endianness not specified +# +# @little: Little endianness +# +# @big: Big endianness +# +# Since: 10.0 +## +{ 'enum': 'EndianMode', + 'data': [ 'unspecified', 'little', 'big' ] } From 2cdf693b197db6c6c27ff2bf02fce1c0bb384786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Sep 2024 23:15:04 +0200 Subject: [PATCH 1760/2892] hw/intc/xilinx_intc: Make device endianness configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the DEVICE_NATIVE_ENDIAN MemoryRegionOps by a pair of DEVICE_LITTLE_ENDIAN / DEVICE_BIG_ENDIAN. Add the "endianness" property to select the device endianness. This property is unspecified by default, and machines need to set it explicitly. Set the proper endianness for each machine using the device. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250213122217.62654-3-philmd@linaro.org> --- hw/intc/xilinx_intc.c | 59 ++++++++++++++++++------ hw/microblaze/petalogix_ml605_mmu.c | 3 ++ hw/microblaze/petalogix_s3adsp1800_mmu.c | 3 ++ hw/ppc/virtex_ml507.c | 1 + hw/riscv/microblaze-v-generic.c | 1 + 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index 6930f83907..ab1c4a3222 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -3,6 +3,9 @@ * * Copyright (c) 2009 Edgar E. Iglesias. * + * https://docs.amd.com/v/u/en-US/xps_intc + * DS572: LogiCORE IP XPS Interrupt Controller (v2.01a) + * * 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 @@ -23,10 +26,12 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "qemu/module.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "qom/object.h" #define D(x) @@ -49,6 +54,7 @@ struct XpsIntc { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; qemu_irq parent_irq; @@ -140,18 +146,28 @@ static void pic_write(void *opaque, hwaddr addr, update_irq(p); } -static const MemoryRegionOps pic_ops = { - .read = pic_read, - .write = pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, +static const MemoryRegionOps pic_ops[2] = { + [0 ... 1] = { + .read = pic_read, + .write = pic_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + /* + * All XPS INTC registers are accessed through the PLB interface. + * The base address for these registers is provided by the + * configuration parameter, C_BASEADDR. Each register is 32 bits + * although some bits may be unused and is accessed on a 4-byte + * boundary offset from the base address. + */ + .min_access_size = 4, + .max_access_size = 4, + }, }, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static void irq_handler(void *opaque, int irq, int level) @@ -174,13 +190,27 @@ static void xilinx_intc_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); - - memory_region_init_io(&p->mmio, obj, &pic_ops, p, "xlnx.xps-intc", - R_MAX * 4); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); } +static void xilinx_intc_realize(DeviceState *dev, Error **errp) +{ + XpsIntc *p = XILINX_INTC(dev); + + if (p->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_INTC " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + + memory_region_init_io(&p->mmio, OBJECT(dev), + &pic_ops[p->model_endianness == ENDIAN_MODE_BIG], + p, "xlnx.xps-intc", + R_MAX * 4); +} + static const Property xilinx_intc_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XpsIntc, model_endianness), DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0), }; @@ -188,6 +218,7 @@ static void xilinx_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = xilinx_intc_realize; device_class_set_props(dc, xilinx_intc_properties); } diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 8b44be75a2..a876aeb0bb 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -80,6 +80,8 @@ petalogix_ml605_init(MachineState *machine) MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; + EndianMode endianness = TARGET_BIG_ENDIAN ? ENDIAN_MODE_BIG + : ENDIAN_MODE_LITTLE; /* init CPUs */ cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); @@ -111,6 +113,7 @@ petalogix_ml605_init(MachineState *machine) dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint32(dev, "kind-of-intr", 1 << TIMER_IRQ); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 2c0d8c34cd..15cabe1177 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -71,6 +71,8 @@ petalogix_s3adsp1800_init(MachineState *machine) MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; MemoryRegion *sysmem = get_system_memory(); + EndianMode endianness = TARGET_BIG_ENDIAN ? ENDIAN_MODE_BIG + : ENDIAN_MODE_LITTLE; cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); object_property_set_str(OBJECT(cpu), "version", "7.10.d", &error_abort); @@ -95,6 +97,7 @@ petalogix_s3adsp1800_init(MachineState *machine) 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint32(dev, "kind-of-intr", 1 << ETHLITE_IRQ | 1 << UARTLITE_IRQ); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 2323811927..df8f964482 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -217,6 +217,7 @@ static void virtex_init(MachineState *machine) cpu_irq = qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT); dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_BIG); qdev_prop_set_uint32(dev, "kind-of-intr", 0); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c index 26788a1824..ebdd461ae9 100644 --- a/hw/riscv/microblaze-v-generic.c +++ b/hw/riscv/microblaze-v-generic.c @@ -79,6 +79,7 @@ static void mb_v_generic_init(MachineState *machine) memory_region_add_subregion(sysmem, ddr_base, phys_ram); dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "kind-of-intr", 1 << UARTLITE_IRQ); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); From 644276db5d707eba7dd89cc8550b3639dbd29f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Sep 2024 23:15:58 +0200 Subject: [PATCH 1761/2892] hw/net/xilinx_ethlite: Make device endianness configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the DEVICE_NATIVE_ENDIAN MemoryRegionOps by a pair of DEVICE_LITTLE_ENDIAN / DEVICE_BIG_ENDIAN. Add the "endianness" property to select the device endianness. This property is unspecified by default, and machines need to set it explicitly. Set the proper endianness for each machine using the device. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250213122217.62654-4-philmd@linaro.org> --- hw/microblaze/petalogix_s3adsp1800_mmu.c | 1 + hw/net/xilinx_ethlite.c | 29 +++++++++++++++++++----- hw/riscv/microblaze-v-generic.c | 1 + 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 15cabe1177..d419dc49a2 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -123,6 +123,7 @@ petalogix_s3adsp1800_init(MachineState *machine) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); dev = qdev_new("xlnx.xps-ethernetlite"); + qdev_prop_set_enum(dev, "endianness", endianness); qemu_configure_nic_device(dev, true, NULL); qdev_prop_set_uint32(dev, "tx-ping-pong", 0); qdev_prop_set_uint32(dev, "rx-ping-pong", 0); diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 14bf2b2e17..15d9b95aa8 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -34,6 +34,7 @@ #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/misc/unimp.h" #include "net/net.h" #include "trace.h" @@ -85,6 +86,7 @@ struct XlnxXpsEthLite { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion container; qemu_irq irq; NICState *nic; @@ -183,10 +185,10 @@ static void port_tx_write(void *opaque, hwaddr addr, uint64_t value, } } -static const MemoryRegionOps eth_porttx_ops = { +static const MemoryRegionOps eth_porttx_ops[2] = { + [0 ... 1] = { .read = port_tx_read, .write = port_tx_write, - .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 4, .max_access_size = 4, @@ -195,6 +197,9 @@ static const MemoryRegionOps eth_porttx_ops = { .min_access_size = 4, .max_access_size = 4, }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static uint64_t port_rx_read(void *opaque, hwaddr addr, unsigned int size) @@ -232,10 +237,10 @@ static void port_rx_write(void *opaque, hwaddr addr, uint64_t value, } } -static const MemoryRegionOps eth_portrx_ops = { +static const MemoryRegionOps eth_portrx_ops[2] = { + [0 ... 1] = { .read = port_rx_read, .write = port_rx_write, - .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 4, .max_access_size = 4, @@ -244,6 +249,9 @@ static const MemoryRegionOps eth_portrx_ops = { .min_access_size = 4, .max_access_size = 4, }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static bool eth_can_rx(NetClientState *nc) @@ -300,6 +308,14 @@ static NetClientInfo net_xilinx_ethlite_info = { static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { XlnxXpsEthLite *s = XILINX_ETHLITE(dev); + unsigned ops_index; + + if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_ETHLITE " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + ops_index = s->model_endianness == ENDIAN_MODE_BIG ? 1 : 0; memory_region_init(&s->container, OBJECT(dev), "xlnx.xps-ethernetlite", 0x2000); @@ -328,7 +344,7 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) BUFSZ_MAX, &error_abort); memory_region_add_subregion(&s->container, 0x0800 * i, &s->port[i].txbuf); memory_region_init_io(&s->port[i].txio, OBJECT(dev), - ð_porttx_ops, s, + ð_porttx_ops[ops_index], s, i ? "ethlite.tx[1]io" : "ethlite.tx[0]io", 4 * TX_MAX); memory_region_add_subregion(&s->container, i ? A_TX_BASE1 : A_TX_BASE0, @@ -340,7 +356,7 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->container, 0x1000 + 0x0800 * i, &s->port[i].rxbuf); memory_region_init_io(&s->port[i].rxio, OBJECT(dev), - ð_portrx_ops, s, + ð_portrx_ops[ops_index], s, i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", 4 * RX_MAX); memory_region_add_subregion(&s->container, i ? A_RX_BASE1 : A_RX_BASE0, @@ -363,6 +379,7 @@ static void xilinx_ethlite_init(Object *obj) } static const Property xilinx_ethlite_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XlnxXpsEthLite, model_endianness), DEFINE_PROP_UINT32("tx-ping-pong", XlnxXpsEthLite, c_tx_pingpong, 1), DEFINE_PROP_UINT32("rx-ping-pong", XlnxXpsEthLite, c_rx_pingpong, 1), DEFINE_NIC_PROPERTIES(XlnxXpsEthLite, conf), diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c index ebdd461ae9..a21fdfbe6d 100644 --- a/hw/riscv/microblaze-v-generic.c +++ b/hw/riscv/microblaze-v-generic.c @@ -120,6 +120,7 @@ static void mb_v_generic_init(MachineState *machine) /* Emaclite */ dev = qdev_new("xlnx.xps-ethernetlite"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qemu_configure_nic_device(dev, true, NULL); qdev_prop_set_uint32(dev, "tx-ping-pong", 0); qdev_prop_set_uint32(dev, "rx-ping-pong", 0); From df1f35ab67e50f572934b7ea705764b77cf6d525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Sep 2024 23:16:21 +0200 Subject: [PATCH 1762/2892] hw/timer/xilinx_timer: Make device endianness configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the DEVICE_NATIVE_ENDIAN MemoryRegionOps by a pair of DEVICE_LITTLE_ENDIAN / DEVICE_BIG_ENDIAN. Add the "endianness" property to select the device endianness. This property is unspecified by default, and machines need to set it explicitly. Set the proper endianness for each machine using the device. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250213122217.62654-5-philmd@linaro.org> --- hw/microblaze/petalogix_ml605_mmu.c | 1 + hw/microblaze/petalogix_s3adsp1800_mmu.c | 1 + hw/ppc/virtex_ml507.c | 1 + hw/riscv/microblaze-v-generic.c | 2 ++ hw/timer/xilinx_timer.c | 43 +++++++++++++++++------- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index a876aeb0bb..984287fdc5 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -129,6 +129,7 @@ petalogix_ml605_init(MachineState *machine) /* 2 timers at irq 2 @ 100 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 100 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index d419dc49a2..caaea222a8 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -116,6 +116,7 @@ petalogix_s3adsp1800_init(MachineState *machine) /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index df8f964482..a01354d991 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -231,6 +231,7 @@ static void virtex_init(MachineState *machine) /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_BIG); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c index a21fdfbe6d..3c79f5733b 100644 --- a/hw/riscv/microblaze-v-generic.c +++ b/hw/riscv/microblaze-v-generic.c @@ -104,6 +104,7 @@ static void mb_v_generic_init(MachineState *machine) /* 2 timers at irq 0 @ 100 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 100000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -112,6 +113,7 @@ static void mb_v_generic_init(MachineState *machine) /* 2 timers at irq 3 @ 100 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 100000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 6595cf5f51..4620528f98 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -3,6 +3,9 @@ * * Copyright (c) 2009 Edgar E. Iglesias. * + * DS573: https://docs.amd.com/v/u/en-US/xps_timer + * LogiCORE IP XPS Timer/Counter (v1.02a) + * * 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 @@ -23,10 +26,12 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" @@ -69,6 +74,7 @@ struct XpsTimerState { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; qemu_irq irq; uint8_t one_timer_only; @@ -189,18 +195,21 @@ timer_write(void *opaque, hwaddr addr, timer_update_irq(t); } -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, +static const MemoryRegionOps timer_ops[2] = { + [0 ... 1] = { + .read = timer_read, + .write = timer_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, }, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static void timer_hit(void *opaque) @@ -220,6 +229,12 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp) XpsTimerState *t = XILINX_TIMER(dev); unsigned int i; + if (t->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_TIMER " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + /* Init all the ptimers. */ t->timers = g_malloc0(sizeof t->timers[0] * num_timers(t)); for (i = 0; i < num_timers(t); i++) { @@ -233,8 +248,9 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp) ptimer_transaction_commit(xt->ptimer); } - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, "xlnx.xps-timer", - R_MAX * 4 * num_timers(t)); + memory_region_init_io(&t->mmio, OBJECT(t), + &timer_ops[t->model_endianness == ENDIAN_MODE_BIG], + t, "xlnx.xps-timer", R_MAX * 4 * num_timers(t)); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &t->mmio); } @@ -247,6 +263,7 @@ static void xilinx_timer_init(Object *obj) } static const Property xilinx_timer_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XpsTimerState, model_endianness), DEFINE_PROP_UINT32("clock-frequency", XpsTimerState, freq_hz, 62 * 1000000), DEFINE_PROP_UINT8("one-timer-only", XpsTimerState, one_timer_only, 0), }; From 8a8c92c8afbb8a153968a72dd4ce504884a3209d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 23:24:04 +0000 Subject: [PATCH 1763/2892] hw/char/xilinx_uartlite: Make device endianness configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the DEVICE_NATIVE_ENDIAN MemoryRegionOps by a pair of DEVICE_LITTLE_ENDIAN / DEVICE_BIG_ENDIAN. Add the "endianness" property to select the device endianness. This property is unspecified by default, and machines need to set it explicitly. Set the proper endianness for each machine using the device. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250213122217.62654-6-philmd@linaro.org> --- hw/char/xilinx_uartlite.c | 34 ++++++++++++++++-------- hw/microblaze/petalogix_s3adsp1800_mmu.c | 1 + hw/riscv/microblaze-v-generic.c | 1 + 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 56955e0d74..4037c937ee 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qapi/error.h" #include "hw/char/xilinx_uartlite.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -57,6 +58,7 @@ struct XilinxUARTLite { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; CharBackend chr; qemu_irq irq; @@ -166,17 +168,21 @@ uart_write(void *opaque, hwaddr addr, uart_update_irq(s); } -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } +static const MemoryRegionOps uart_ops[2] = { + [0 ... 1] = { + .read = uart_read, + .write = uart_write, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static const Property xilinx_uartlite_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XilinxUARTLite, model_endianness), DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr), }; @@ -214,6 +220,15 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) { XilinxUARTLite *s = XILINX_UARTLITE(dev); + if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_UARTLITE " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + + memory_region_init_io(&s->mmio, OBJECT(dev), + &uart_ops[s->model_endianness == ENDIAN_MODE_BIG], + s, "xlnx.xps-uartlite", R_MAX * 4); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, NULL, s, NULL, true); } @@ -223,9 +238,6 @@ static void xilinx_uartlite_init(Object *obj) XilinxUARTLite *s = XILINX_UARTLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, &uart_ops, s, - "xlnx.xps-uartlite", R_MAX * 4); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index caaea222a8..bdba2006b7 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -109,6 +109,7 @@ petalogix_s3adsp1800_init(MachineState *machine) } dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_chr(dev, "chardev", serial_hd(0)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, UARTLITE_BASEADDR); diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c index 3c79f5733b..d8e67906d2 100644 --- a/hw/riscv/microblaze-v-generic.c +++ b/hw/riscv/microblaze-v-generic.c @@ -92,6 +92,7 @@ static void mb_v_generic_init(MachineState *machine) /* Uartlite */ dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_chr(dev, "chardev", serial_hd(0)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, UARTLITE_BASEADDR); From e87c93df1134516166ff3d8f9a56e168ff7e1c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Nov 2024 23:24:27 +0000 Subject: [PATCH 1764/2892] hw/ssi/xilinx_spi: Make device endianness configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the DEVICE_NATIVE_ENDIAN MemoryRegionOps by a pair of DEVICE_LITTLE_ENDIAN / DEVICE_BIG_ENDIAN. Add the "endianness" property to select the device endianness. This property is unspecified by default, and machines need to set it explicitly. Set the proper endianness on the single machine using the device. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250213122217.62654-7-philmd@linaro.org> --- hw/microblaze/petalogix_ml605_mmu.c | 1 + hw/ssi/xilinx_spi.c | 32 +++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 984287fdc5..21ad215e44 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -177,6 +177,7 @@ petalogix_ml605_init(MachineState *machine) SSIBus *spi; dev = qdev_new("xlnx.xps-spi"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index fd1ff12eb1..be5baa6b35 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -25,6 +25,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" @@ -32,6 +33,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/ssi/ssi.h" #include "qom/object.h" @@ -83,6 +85,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XilinxSPI, XILINX_SPI) struct XilinxSPI { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; qemu_irq irq; @@ -313,14 +316,17 @@ done: xlx_spi_update_irq(s); } -static const MemoryRegionOps spi_ops = { - .read = spi_read, - .write = spi_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } +static const MemoryRegionOps spi_ops[2] = { + [0 ... 1] = { + .read = spi_read, + .write = spi_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static void xilinx_spi_realize(DeviceState *dev, Error **errp) @@ -329,6 +335,12 @@ static void xilinx_spi_realize(DeviceState *dev, Error **errp) XilinxSPI *s = XILINX_SPI(dev); int i; + if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_SPI " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + DB_PRINT("\n"); s->spi = ssi_create_bus(dev, "spi"); @@ -339,7 +351,8 @@ static void xilinx_spi_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->cs_lines[i]); } - memory_region_init_io(&s->mmio, OBJECT(s), &spi_ops, s, + memory_region_init_io(&s->mmio, OBJECT(s), + &spi_ops[s->model_endianness == ENDIAN_MODE_BIG], s, "xilinx-spi", R_MAX * 4); sysbus_init_mmio(sbd, &s->mmio); @@ -362,6 +375,7 @@ static const VMStateDescription vmstate_xilinx_spi = { }; static const Property xilinx_spi_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XilinxSPI, model_endianness), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1), }; From ba26f1477735a5ad7dd40a3227ac2a54cf82014d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 09:15:48 +0100 Subject: [PATCH 1765/2892] hw/arm: Mark Allwinner Technology devices as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These devices are only used by the ARM targets, which are only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250212113938.38692-2-philmd@linaro.org> --- hw/arm/allwinner-a10.c | 2 +- hw/arm/allwinner-h3.c | 8 ++++---- hw/arm/allwinner-r40.c | 2 +- hw/i2c/allwinner-i2c.c | 2 +- hw/intc/allwinner-a10-pic.c | 2 +- hw/misc/allwinner-a10-ccm.c | 2 +- hw/misc/allwinner-a10-dramc.c | 2 +- hw/misc/allwinner-cpucfg.c | 2 +- hw/misc/allwinner-h3-ccu.c | 2 +- hw/misc/allwinner-h3-dramc.c | 6 +++--- hw/misc/allwinner-h3-sysctrl.c | 2 +- hw/misc/allwinner-r40-ccu.c | 2 +- hw/misc/allwinner-r40-dramc.c | 10 +++++----- hw/misc/allwinner-sid.c | 2 +- hw/misc/allwinner-sramc.c | 2 +- hw/net/allwinner-sun8i-emac.c | 2 +- hw/net/allwinner_emac.c | 2 +- hw/rtc/allwinner-rtc.c | 2 +- hw/sd/allwinner-sdhost.c | 2 +- hw/ssi/allwinner-a10-spi.c | 2 +- hw/timer/allwinner-a10-pit.c | 2 +- hw/watchdog/allwinner-wdt.c | 2 +- 22 files changed, 31 insertions(+), 31 deletions(-) diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index a829913f1b..f1b399759a 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -158,7 +158,7 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) /* FIXME use a qdev chardev prop instead of serial_hd() */ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, qdev_get_gpio_in(dev, 1), - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); for (size_t i = 0; i < AW_A10_NUM_USB; i++) { g_autofree char *bus = g_strdup_printf("usb-bus.%zu", i); diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 2efced3f66..1b1afa4fb6 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -408,19 +408,19 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART0], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); /* UART1 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART1], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART1), - 115200, serial_hd(1), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(1), DEVICE_LITTLE_ENDIAN); /* UART2 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART2], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART2), - 115200, serial_hd(2), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(2), DEVICE_LITTLE_ENDIAN); /* UART3 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART3], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART3), - 115200, serial_hd(3), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(3), DEVICE_LITTLE_ENDIAN); /* DRAMC */ sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 47b3180f0e..cef6e4d18c 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -492,7 +492,7 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) serial_mm_init(get_system_memory(), addr, 2, qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]), - 115200, serial_hd(i), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN); } /* I2C */ diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c index 16f1d6d40e..66d6431c50 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -407,7 +407,7 @@ static const MemoryRegionOps allwinner_i2c_ops = { .write = allwinner_i2c_write, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static const VMStateDescription allwinner_i2c_vmstate = { diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index c0f30092cd..93a604f7a0 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -135,7 +135,7 @@ static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, static const MemoryRegionOps aw_a10_pic_ops = { .read = aw_a10_pic_read, .write = aw_a10_pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static const VMStateDescription vmstate_aw_a10_pic = { diff --git a/hw/misc/allwinner-a10-ccm.c b/hw/misc/allwinner-a10-ccm.c index 575b018952..6ca1daaff8 100644 --- a/hw/misc/allwinner-a10-ccm.c +++ b/hw/misc/allwinner-a10-ccm.c @@ -147,7 +147,7 @@ static void allwinner_a10_ccm_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_a10_ccm_ops = { .read = allwinner_a10_ccm_read, .write = allwinner_a10_ccm_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c index a7c58fa6d0..badc4c56eb 100644 --- a/hw/misc/allwinner-a10-dramc.c +++ b/hw/misc/allwinner-a10-dramc.c @@ -114,7 +114,7 @@ static void allwinner_a10_dramc_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_a10_dramc_ops = { .read = allwinner_a10_dramc_read, .write = allwinner_a10_dramc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-cpucfg.c b/hw/misc/allwinner-cpucfg.c index 022f63ddf3..a4f7a01141 100644 --- a/hw/misc/allwinner-cpucfg.c +++ b/hw/misc/allwinner-cpucfg.c @@ -217,7 +217,7 @@ static void allwinner_cpucfg_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_cpucfg_ops = { .read = allwinner_cpucfg_read, .write = allwinner_cpucfg_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-h3-ccu.c b/hw/misc/allwinner-h3-ccu.c index 92e579a991..e765f4c54b 100644 --- a/hw/misc/allwinner-h3-ccu.c +++ b/hw/misc/allwinner-h3-ccu.c @@ -155,7 +155,7 @@ static void allwinner_h3_ccu_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_h3_ccu_ops = { .read = allwinner_h3_ccu_read, .write = allwinner_h3_ccu_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index 13bba26d0e..c4f3eb9274 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -219,7 +219,7 @@ static void allwinner_h3_dramphy_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_h3_dramcom_ops = { .read = allwinner_h3_dramcom_read, .write = allwinner_h3_dramcom_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -230,7 +230,7 @@ static const MemoryRegionOps allwinner_h3_dramcom_ops = { static const MemoryRegionOps allwinner_h3_dramctl_ops = { .read = allwinner_h3_dramctl_read, .write = allwinner_h3_dramctl_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -241,7 +241,7 @@ static const MemoryRegionOps allwinner_h3_dramctl_ops = { static const MemoryRegionOps allwinner_h3_dramphy_ops = { .read = allwinner_h3_dramphy_read, .write = allwinner_h3_dramphy_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c index 40059e8cb0..32a0ceb01a 100644 --- a/hw/misc/allwinner-h3-sysctrl.c +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -78,7 +78,7 @@ static void allwinner_h3_sysctrl_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_h3_sysctrl_ops = { .read = allwinner_h3_sysctrl_read, .write = allwinner_h3_sysctrl_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c index 005a15b2da..8f37a9213c 100644 --- a/hw/misc/allwinner-r40-ccu.c +++ b/hw/misc/allwinner-r40-ccu.c @@ -129,7 +129,7 @@ static void allwinner_r40_ccu_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_ccu_ops = { .read = allwinner_r40_ccu_read, .write = allwinner_r40_ccu_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c index 97c3664e3a..96e1848c21 100644 --- a/hw/misc/allwinner-r40-dramc.c +++ b/hw/misc/allwinner-r40-dramc.c @@ -297,7 +297,7 @@ static void allwinner_r40_dramphy_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_dramcom_ops = { .read = allwinner_r40_dramcom_read, .write = allwinner_r40_dramcom_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -308,7 +308,7 @@ static const MemoryRegionOps allwinner_r40_dramcom_ops = { static const MemoryRegionOps allwinner_r40_dramctl_ops = { .read = allwinner_r40_dramctl_read, .write = allwinner_r40_dramctl_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -319,7 +319,7 @@ static const MemoryRegionOps allwinner_r40_dramctl_ops = { static const MemoryRegionOps allwinner_r40_dramphy_ops = { .read = allwinner_r40_dramphy_read, .write = allwinner_r40_dramphy_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -358,7 +358,7 @@ static void allwinner_r40_detect_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_detect_ops = { .read = allwinner_r40_detect_read, .write = allwinner_r40_detect_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -393,7 +393,7 @@ static uint64_t allwinner_r40_dualrank_detect_read(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_dualrank_detect_ops = { .read = allwinner_r40_dualrank_detect_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c index 042b747f30..2bb81f9c54 100644 --- a/hw/misc/allwinner-sid.c +++ b/hw/misc/allwinner-sid.c @@ -99,7 +99,7 @@ static void allwinner_sid_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_sid_ops = { .read = allwinner_sid_read, .write = allwinner_sid_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c index a20b0b4c5c..51df5e45aa 100644 --- a/hw/misc/allwinner-sramc.c +++ b/hw/misc/allwinner-sramc.c @@ -104,7 +104,7 @@ static void allwinner_sramc_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_sramc_ops = { .read = allwinner_sramc_read, .write = allwinner_sramc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index ff44554e95..5adb41dc46 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -784,7 +784,7 @@ static void allwinner_sun8i_emac_set_link(NetClientState *nc) static const MemoryRegionOps allwinner_sun8i_emac_mem_ops = { .read = allwinner_sun8i_emac_read, .write = allwinner_sun8i_emac_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 3eb9e09dc5..47f1e7f086 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -421,7 +421,7 @@ static void aw_emac_set_link(NetClientState *nc) static const MemoryRegionOps aw_emac_mem_ops = { .read = aw_emac_read, .write = aw_emac_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index a19e4310bb..fd8355a867 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -259,7 +259,7 @@ static void allwinner_rtc_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_rtc_ops = { .read = allwinner_rtc_read, .write = allwinner_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index ee5c5c78a8..03980d2716 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -761,7 +761,7 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_sdhost_ops = { .read = allwinner_sdhost_read, .write = allwinner_sdhost_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, diff --git a/hw/ssi/allwinner-a10-spi.c b/hw/ssi/allwinner-a10-spi.c index 3eb50b44ac..d2f6bb9cdc 100644 --- a/hw/ssi/allwinner-a10-spi.c +++ b/hw/ssi/allwinner-a10-spi.c @@ -502,7 +502,7 @@ static const MemoryRegionOps allwinner_a10_spi_ops = { .write = allwinner_a10_spi_write, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static const VMStateDescription allwinner_a10_spi_vmstate = { diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index ddaf2128c2..da3d7173ef 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -185,7 +185,7 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, static const MemoryRegionOps a10_pit_ops = { .read = a10_pit_read, .write = a10_pit_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static const Property a10_pit_properties[] = { diff --git a/hw/watchdog/allwinner-wdt.c b/hw/watchdog/allwinner-wdt.c index 1bfec41ff8..78f4f9d6f6 100644 --- a/hw/watchdog/allwinner-wdt.c +++ b/hw/watchdog/allwinner-wdt.c @@ -275,7 +275,7 @@ static void allwinner_wdt_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_wdt_ops = { .read = allwinner_wdt_read, .write = allwinner_wdt_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, From 7830a2ea8053e4dbe2466dba6a13204873a0ef87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 09:38:17 +0100 Subject: [PATCH 1766/2892] hw/mips: Mark Boston machine devices as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Boston machine is only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250212113938.38692-3-philmd@linaro.org> --- hw/mips/boston.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 364c328032..4690b254dd 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -220,7 +220,7 @@ static void boston_lcd_write(void *opaque, hwaddr addr, static const MemoryRegionOps boston_lcd_ops = { .read = boston_lcd_read, .write = boston_lcd_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint64_t boston_platreg_read(void *opaque, hwaddr addr, @@ -299,7 +299,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, static const MemoryRegionOps boston_platreg_ops = { .read = boston_platreg_read, .write = boston_platreg_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void mips_boston_instance_init(Object *obj) @@ -758,7 +758,7 @@ static void boston_mach_init(MachineState *machine) s->uart = serial_mm_init(sys_mem, boston_memmap[BOSTON_UART].base, 2, get_cps_irq(&s->cps, 3), 10000000, - serial_hd(0), DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_LITTLE_ENDIAN); lcd = g_new(MemoryRegion, 1); memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); From 8970e2ea01f91c3162c36c731ea9720cb0df9bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 09:38:26 +0100 Subject: [PATCH 1767/2892] hw/mips: Mark Loonson3 Virt machine devices as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Loonson3 Virt machine is only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250212113938.38692-4-philmd@linaro.org> --- hw/mips/loongson3_virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 831fddb1bd..db1cc51314 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -144,7 +144,7 @@ static void loongson3_pm_write(void *opaque, hwaddr addr, static const MemoryRegionOps loongson3_pm_ops = { .read = loongson3_pm_read, .write = loongson3_pm_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 1 @@ -560,7 +560,7 @@ static void mips_loongson3_virt_init(MachineState *machine) serial_mm_init(address_space_mem, virt_memmap[VIRT_UART].base, 0, qdev_get_gpio_in(liointc, UART_IRQ), 115200, serial_hd(0), - DEVICE_NATIVE_ENDIAN); + DEVICE_LITTLE_ENDIAN); sysbus_create_simple("goldfish_rtc", virt_memmap[VIRT_RTC].base, qdev_get_gpio_in(liointc, RTC_IRQ)); From 62fb8ec35b7d7de7bfd4bd008026d49a5f52f946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 09:21:41 +0100 Subject: [PATCH 1768/2892] hw/pci-host: Mark versatile regions as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device is only used by the ARM targets, which are only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250212113938.38692-5-philmd@linaro.org> --- hw/pci-host/versatile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index c3fbf4cbf9..33a8ceb3b5 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -246,7 +246,7 @@ static uint64_t pci_vpb_reg_read(void *opaque, hwaddr addr, static const MemoryRegionOps pci_vpb_reg_ops = { .read = pci_vpb_reg_read, .write = pci_vpb_reg_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -312,7 +312,7 @@ static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, static const MemoryRegionOps pci_vpb_config_ops = { .read = pci_vpb_config_read, .write = pci_vpb_config_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static int pci_vpb_map_irq(PCIDevice *d, int irq_num) From 5bf24ec9c4d4771a9469cadd19cf534e9a32a9db Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 14 Feb 2025 18:16:50 -0800 Subject: [PATCH 1769/2892] hw/rx: Allow execution without either bios or kernel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users can use -device loader to get an ELF file loaded to memory, so we don't need to require one of these options. Signed-off-by: Keith Packard Reviewed-by: Richard Henderson Message-ID: <20250215021654.1786679-2-keithp@keithp.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/rx/rx-gdbsim.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 88c8f12c10..4afd77efd5 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -110,9 +110,6 @@ static void rx_gdbsim_init(MachineState *machine) if (!kernel_filename) { if (machine->firmware) { rom_add_file_fixed(machine->firmware, RX62N_CFLASH_BASE, 0); - } else if (!qtest_enabled()) { - error_report("No bios or kernel specified"); - exit(1); } } From f8af22afec5092ce641fb5e5305f2bb9b232f206 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Tue, 7 Jan 2025 16:26:42 +0900 Subject: [PATCH 1770/2892] hw/ufs: Fix legacy single doorbell support bit QEMU UFS has supported both legacy single doorbell and MCQ, but the LSDBS value was incorrectly set. This change corrects the LSDBS value to 0. Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 428fe927ad..1ccd6f88b6 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1635,7 +1635,7 @@ static void ufs_init_hc(UfsHc *u) cap = FIELD_DP32(cap, CAP, OODDS, 0); cap = FIELD_DP32(cap, CAP, UICDMETMS, 0); cap = FIELD_DP32(cap, CAP, CS, 0); - cap = FIELD_DP32(cap, CAP, LSDBS, 1); + cap = FIELD_DP32(cap, CAP, LSDBS, 0); cap = FIELD_DP32(cap, CAP, MCQS, u->params.mcq); u->reg.cap = cap; From e041d3d2165994311a6ee4bee6d1c7864ff81916 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Wed, 12 Feb 2025 14:04:19 +0900 Subject: [PATCH 1771/2892] tests/qtest/ufs-test: Cleanup unused code Removed dead code related to the unimplemented task management request. Acked-by: Fabiano Rosas Signed-off-by: Jeuk Kim --- tests/qtest/ufs-test.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 1f860b41c0..ce8b398c6b 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -8,7 +8,6 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "qemu/units.h" #include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" @@ -35,7 +34,6 @@ struct QUfs { QPCIBar bar; uint64_t utrlba; - uint64_t utmrlba; uint64_t cmd_desc_addr; uint64_t data_buffer_addr; @@ -257,7 +255,7 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) { uint64_t end_time; - uint32_t nutrs, nutmrs; + uint32_t nutrs; uint32_t hcs, is, ucmdarg2, cap; uint32_t hce = 0, ie = 0; UtpTransferReqDesc utrd; @@ -305,7 +303,6 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) hcs = ufs_rreg(ufs, A_HCS); g_assert_true(FIELD_EX32(hcs, HCS, DP)); g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY)); - g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY)); g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); /* Enable all interrupt functions */ @@ -326,20 +323,15 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) /* Enable transfer request and task management request */ cap = ufs_rreg(ufs, A_CAP); nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; - nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1; ufs->cmd_desc_addr = guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); ufs->data_buffer_addr = guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); - ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc)); ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); - ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff); - ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32); ufs_wreg(ufs, A_UTRLRSR, 1); - ufs_wreg(ufs, A_UTMRLRSR, 1); /* Send nop out to test transfer request */ ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu); @@ -370,7 +362,6 @@ static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) { if (ufs->enabled) { guest_free(alloc, ufs->utrlba); - guest_free(alloc, ufs->utmrlba); guest_free(alloc, ufs->cmd_desc_addr); guest_free(alloc, ufs->data_buffer_addr); } From 5cb3566a5860f35a8871277748616b9ab11f5cd2 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Wed, 12 Feb 2025 14:04:20 +0900 Subject: [PATCH 1772/2892] tests/qtest/ufs-test: Prepare for MCQ test In legacy doorbell mode, the command descriptor slot matched the UTRD slot. To maintain consistency in MCQ mode, command descriptor slot allocation and deallocation now use a bitmap-based approach. Acked-by: Fabiano Rosas Signed-off-by: Jeuk Kim --- tests/qtest/ufs-test.c | 570 +++++++++++++++++++++-------------------- 1 file changed, 299 insertions(+), 271 deletions(-) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index ce8b398c6b..f5b311554b 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -13,6 +13,7 @@ #include "libqos/pci.h" #include "scsi/constants.h" #include "block/ufs.h" +#include "qemu/bitmap.h" /* Test images sizes in Bytes */ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) @@ -25,6 +26,8 @@ #define UTP_COMMAND_DESCRIPTOR_SIZE 4096 #define UTP_RESPONSE_UPIU_OFFSET 1024 #define UTP_PRDT_UPIU_OFFSET 2048 +#define UTRD_TEST_SLOT 0 +#define UFS_MAX_CMD_DESC 32 typedef struct QUfs QUfs; @@ -34,6 +37,7 @@ struct QUfs { QPCIBar bar; uint64_t utrlba; + DECLARE_BITMAP(cmd_desc_bitmap, UFS_MAX_CMD_DESC); uint64_t cmd_desc_addr; uint64_t data_buffer_addr; @@ -50,6 +54,24 @@ static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) qpci_io_writel(&ufs->dev, ufs->bar, offset, value); } +static int alloc_cmd_desc_slot(QUfs *ufs) +{ + int slot = find_first_zero_bit(ufs->cmd_desc_bitmap, UFS_MAX_CMD_DESC); + if (slot == UFS_MAX_CMD_DESC) { + g_assert_not_reached(); + } + set_bit(slot, ufs->cmd_desc_bitmap); + return slot; +} + +static void release_cmd_desc_slot(QUfs *ufs, int slot) +{ + if (!test_bit(slot, ufs->cmd_desc_bitmap)) { + g_assert_not_reached(); + } + clear_bit(slot, ufs->cmd_desc_bitmap); +} + static void ufs_wait_for_irq(QUfs *ufs) { uint64_t end_time; @@ -62,14 +84,11 @@ static void ufs_wait_for_irq(QUfs *ufs) } while (is == 0 && g_get_monotonic_time() < end_time); } -static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, - uint8_t slot, +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t command_desc_base_addr, uint32_t data_direction, uint16_t prd_table_length) { UtpTransferReqDesc req = { 0 }; - uint64_t command_desc_base_addr = - cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; req.header.dword_0 = cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD); @@ -86,24 +105,19 @@ static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, return req; } -static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, - UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes +ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) { - /* Build up utp transfer request descriptor */ - UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, - UFS_UTP_NO_DATA_TRANSFER, 0); - uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); - uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; - uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; - qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); - - /* Build up request upiu */ - UtpUpiuReq req_upiu = { 0 }; - req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; - req_upiu.header.task_tag = slot; - qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, - sizeof(req_upiu)); + UtpTransferReqDesc utrd_result; + /* + * Currently, the transfer request is sent synchronously, so UTRD_TEST_SLOT + * is fixed to 0. If asynchronous testing is added in the future, this value + * should be adjusted dynamically. + */ + uint64_t utrd_addr = + ufs->utrlba + UTRD_TEST_SLOT * sizeof(UtpTransferReqDesc); + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); /* Ring Doorbell */ ufs_wreg(ufs, A_UTRLDBR, 1); @@ -111,29 +125,53 @@ static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); - qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); - qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); + qtest_memread(ufs->dev.bus->qts, utrd_addr, &utrd_result, + sizeof(utrd_result)); + + return le32_to_cpu(utrd_result.header.dword_2) & 0xf; } -static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, - uint8_t query_opcode, uint8_t idn, uint8_t index, - uint8_t selector, uint32_t attr_value, - UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out) { - /* Build up utp transfer request descriptor */ - UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, - UFS_UTP_NO_DATA_TRANSFER, 0); - uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; + uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; + + /* Build up request upiu */ + UtpUpiuReq req_upiu = { 0 }; + req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; + req_upiu.header.task_tag = cmd_desc_slot; + qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, + sizeof(req_upiu)); + + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd); + + qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; +} + +static enum UtpOcsCodes ufs_send_query(QUfs *ufs, uint8_t query_function, + uint8_t query_opcode, uint8_t idn, + uint8_t index, uint8_t selector, + uint32_t attr_value, UtpUpiuRsp *rsp_out) +{ + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); + uint64_t req_upiu_addr = + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; - qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); /* Build up request upiu */ UtpUpiuReq req_upiu = { 0 }; req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ; req_upiu.header.query_func = query_function; - req_upiu.header.task_tag = slot; + req_upiu.header.task_tag = cmd_desc_slot; /* * QEMU UFS does not currently support Write descriptor, * so the value of data_segment_length is always 0. @@ -148,22 +186,23 @@ static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, sizeof(req_upiu)); - /* Ring Doorbell */ - ufs_wreg(ufs, A_UTRLDBR, 1); - ufs_wait_for_irq(ufs); - g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); - ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd); - qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } -static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, - const uint8_t *cdb, const uint8_t *data_in, - size_t data_in_len, uint8_t *data_out, - size_t data_out_len, - UtpTransferReqDesc *utrd_out, - UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes +ufs_send_scsi_command(QUfs *ufs, uint8_t lun, const uint8_t *cdb, + const uint8_t *data_in, size_t data_in_len, + uint8_t *data_out, size_t data_out_len, + UtpUpiuRsp *rsp_out) { /* Build up PRDT */ @@ -173,8 +212,9 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, uint8_t flags; uint16_t prd_table_length, i; uint32_t data_direction, data_len; + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET; g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); @@ -216,36 +256,33 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries, prd_table_length * sizeof(UfshcdSgEntry)); - /* Build up utp transfer request descriptor */ - UtpTransferReqDesc utrd = ufs_build_req_utrd( - ufs->cmd_desc_addr, slot, data_direction, prd_table_length); - uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; - qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); /* Build up request upiu */ UtpUpiuReq req_upiu = { 0 }; req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND; req_upiu.header.flags = flags; req_upiu.header.lun = lun; - req_upiu.header.task_tag = slot; + req_upiu.header.task_tag = cmd_desc_slot; req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len); memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE); qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, sizeof(req_upiu)); - /* Ring Doorbell */ - ufs_wreg(ufs, A_UTRLDBR, 1); - ufs_wait_for_irq(ufs); - g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); - ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, data_direction, prd_table_length); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, lun, &utrd); - qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); if (data_out_len) { qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out, data_out_len); } + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } /** @@ -258,7 +295,7 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) uint32_t nutrs; uint32_t hcs, is, ucmdarg2, cap; uint32_t hce = 0, ie = 0; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); @@ -320,11 +357,11 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) ufs_wreg(ufs, A_IE, ie); ufs_wreg(ufs, A_UTRIACR, 0); - /* Enable transfer request and task management request */ + /* Enable transfer request */ cap = ufs_rreg(ufs, A_CAP); nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; ufs->cmd_desc_addr = - guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); + guest_alloc(alloc, UFS_MAX_CMD_DESC * UTP_COMMAND_DESCRIPTOR_SIZE); ufs->data_buffer_addr = guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); @@ -334,23 +371,27 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) ufs_wreg(ufs, A_UTRLRSR, 1); /* Send nop out to test transfer request */ - ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_nop_out(ufs, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); /* Set fDeviceInit flag via query request */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); /* Wait for device to reset */ end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; do { qtest_clock_step(ufs->dev.bus->qts, 100); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, - &rsp_upiu); + ocs = + ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_COMMAND_RESULT_SUCCESS); } while (be32_to_cpu(rsp_upiu.qr.value) != 0 && g_get_monotonic_time() < end_time); g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0); @@ -424,15 +465,15 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { REQUEST_SENSE, }; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Check REPORT_LUNS */ - ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf), - &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, 0, report_luns_cdb, NULL, 0, buf, + sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); /* LUN LIST LENGTH should be 8, in big endian */ g_assert_cmpuint(buf[3], ==, 8); @@ -440,15 +481,15 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpuint(buf[9], ==, 0); /* Clear Unit Attention */ - ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf, - sizeof(buf), &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, 0, request_sense_cdb, NULL, 0, buf, + sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); /* Check TEST_UNIT_READY */ - ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, - &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); ufs_exit(ufs, alloc); @@ -490,22 +531,22 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; uint32_t block_size; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; const int test_lun = 1; ufs_init(ufs, alloc); /* Clear Unit Attention */ - ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0, - read_buf, sizeof(read_buf), &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); /* Read capacity */ - ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0, - read_buf, sizeof(read_buf), &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, read_capacity_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, UFS_COMMAND_RESULT_SUCCESS); block_size = ldl_be_p(&read_buf[8]); @@ -513,16 +554,16 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) /* Write data */ memset(write_buf, 0xab, block_size); - ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size, - NULL, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, block_size, + NULL, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, UFS_COMMAND_RESULT_SUCCESS); /* Read data and verify */ - ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf, - block_size, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, read_cdb, NULL, 0, read_buf, + block_size, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); @@ -535,76 +576,74 @@ static void ufstest_query_flag_request(void *obj, void *data, { QUfs *ufs = obj; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Read read-only flag */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_FLAG); g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_FLAG_IDN_FDEVICEINIT); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); /* Flag Set, Clear, Toggle Test with fDeviceLifeSpanModeEn */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); /* Read Write-only Flag (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_READABLE); /* Write Read-Only Flag (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, UFS_QUERY_FLAG_IDN_BUSY_RTC, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_BUSY_RTC, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE); @@ -616,130 +655,122 @@ static void ufstest_query_attr_request(void *obj, void *data, { QUfs *ufs = obj; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Read Readable Attributes*/ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_ATTR); g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_ATTR_IDN_BOOT_LU_EN); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); /* Write Writable Attributes & Read Again */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07)); /* Write Invalid Value (Intended Error) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_VALUE); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); /* Read Write-Only Attribute (Intended Error) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_READABLE); /* Write Read-Only Attribute (Intended Error) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); /* Reset Written Attributes */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); @@ -751,17 +782,17 @@ static void ufstest_query_desc_request(void *obj, void *data, { QUfs *ufs = obj; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Write Descriptor is not supported yet */ /* Read Device Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_DEVICE, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_DEVICE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_DESC); g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_DESC_IDN_DEVICE); @@ -771,126 +802,123 @@ static void ufstest_query_desc_request(void *obj, void *data, /* Read Configuration Descriptor is not supported yet*/ /* Read Unit Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 0, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 0); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 1, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 1, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 1); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, - UFS_UPIU_RPMB_WLUN, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = + ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, + UFS_UPIU_RPMB_WLUN, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(RpmbUnitDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); g_assert_cmpuint(rsp_upiu.qr.data[2], ==, UFS_UPIU_RPMB_WLUN); /* Read Interconnect Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, - UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(InterconnectDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_INTERCONNECT); /* Read String Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x12); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 1, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 1, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x22); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 4, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 4, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x0a); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); /* Read Geometry Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_GEOMETRY, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_GEOMETRY, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(GeometryDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_GEOMETRY); /* Read Power Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_POWER, 0, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_POWER, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(PowerParametersDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_POWER); /* Read Health Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_HEALTH, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_HEALTH, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(DeviceHealthDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_HEALTH); /* Invalid Index (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 4, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 4, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_INDEX); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 5, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 5, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_INDEX); /* Invalid Selector (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_DEVICE, - 0, 1, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_DEVICE, 0, 1, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_SELECTOR); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 0, 1, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 0, 1, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_SELECTOR); From a54596a96006096798b172a368ae952a231f9f72 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Wed, 12 Feb 2025 14:04:21 +0900 Subject: [PATCH 1773/2892] tests/qtest/ufs-test: Add test code for MCQ functionality This patch tests whether MCQ initialization and basic read-write operations work correctly when the MCQ parameter of hw/ufs is enabled. Acked-by: Fabiano Rosas Signed-off-by: Jeuk Kim --- tests/qtest/ufs-test.c | 171 ++++++++++++++++++++++++++++++++++------- 1 file changed, 142 insertions(+), 29 deletions(-) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index f5b311554b..d5076bdeb5 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -15,6 +15,7 @@ #include "block/ufs.h" #include "qemu/bitmap.h" +#define DWORD_BYTE 4 /* Test images sizes in Bytes */ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) /* Timeout for various operations, in seconds. */ @@ -28,6 +29,10 @@ #define UTP_PRDT_UPIU_OFFSET 2048 #define UTRD_TEST_SLOT 0 #define UFS_MAX_CMD_DESC 32 +/* Constants for MCQ */ +#define TEST_QID 0 +#define QUEUE_SIZE 32 +#define UFS_MCQ_MAX_QNUM 32 typedef struct QUfs QUfs; @@ -36,12 +41,22 @@ struct QUfs { QPCIDevice dev; QPCIBar bar; - uint64_t utrlba; DECLARE_BITMAP(cmd_desc_bitmap, UFS_MAX_CMD_DESC); uint64_t cmd_desc_addr; uint64_t data_buffer_addr; bool enabled; + bool support_mcq; + + /* for legacy doorbell mode */ + uint64_t utrlba; + + /* for mcq mode */ + uint32_t maxq; + uint64_t sqlba[UFS_MCQ_MAX_QNUM]; + uint64_t cqlba[UFS_MCQ_MAX_QNUM]; + uint64_t sqdao[UFS_MCQ_MAX_QNUM]; + uint64_t cqdao[UFS_MCQ_MAX_QNUM]; }; static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) @@ -106,31 +121,67 @@ static UtpTransferReqDesc ufs_build_req_utrd(uint64_t command_desc_base_addr, } static enum UtpOcsCodes -ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun, - const UtpTransferReqDesc *utrd) +__ufs_send_transfer_request_doorbell(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) { - UtpTransferReqDesc utrd_result; - /* - * Currently, the transfer request is sent synchronously, so UTRD_TEST_SLOT - * is fixed to 0. If asynchronous testing is added in the future, this value - * should be adjusted dynamically. - */ uint64_t utrd_addr = ufs->utrlba + UTRD_TEST_SLOT * sizeof(UtpTransferReqDesc); + UtpTransferReqDesc utrd_result; + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); - /* Ring Doorbell */ + /* Ring the doorbell */ ufs_wreg(ufs, A_UTRLDBR, 1); ufs_wait_for_irq(ufs); g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + /* Handle completed command */ qtest_memread(ufs->dev.bus->qts, utrd_addr, &utrd_result, sizeof(utrd_result)); - return le32_to_cpu(utrd_result.header.dword_2) & 0xf; } +static enum UtpOcsCodes +__ufs_send_transfer_request_mcq(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + uint32_t sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + uint64_t utrd_addr = ufs->sqlba[TEST_QID] + sqtp; + uint32_t cqhp; + uint64_t cqentry_addr; + UfsCqEntry cqentry; + + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); + + /* Insert a new entry into the submission queue */ + sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + sqtp = (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry)); + ufs_wreg(ufs, ufs->sqdao[TEST_QID] + 0x4, sqtp); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, CQES)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, CQES, 1)); + + /* Handle the completed command from the completion queue */ + cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]); + cqentry_addr = ufs->cqlba[TEST_QID] + cqhp; + qtest_memread(ufs->dev.bus->qts, cqentry_addr, &cqentry, sizeof(cqentry)); + ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp); + + return cqentry.status; +} + +static enum UtpOcsCodes +ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + if (ufs->support_mcq) { + return __ufs_send_transfer_request_mcq(ufs, lun, utrd); + } + + return __ufs_send_transfer_request_doorbell(ufs, lun, utrd); +} + static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out) { int cmd_desc_slot = alloc_cmd_desc_slot(ufs); @@ -342,6 +393,10 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY)); g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); + /* Check MCQ support */ + cap = ufs_rreg(ufs, A_CAP); + ufs->support_mcq = FIELD_EX32(cap, CAP, MCQS); + /* Enable all interrupt functions */ ie = FIELD_DP32(ie, IE, UTRCE, 1); ie = FIELD_DP32(ie, IE, UEE, 1); @@ -354,21 +409,66 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) ie = FIELD_DP32(ie, IE, HCFEE, 1); ie = FIELD_DP32(ie, IE, SBFEE, 1); ie = FIELD_DP32(ie, IE, CEFEE, 1); + if (ufs->support_mcq) { + ie = FIELD_DP32(ie, IE, CQEE, 1); + } ufs_wreg(ufs, A_IE, ie); ufs_wreg(ufs, A_UTRIACR, 0); /* Enable transfer request */ - cap = ufs_rreg(ufs, A_CAP); - nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; ufs->cmd_desc_addr = guest_alloc(alloc, UFS_MAX_CMD_DESC * UTP_COMMAND_DESCRIPTOR_SIZE); ufs->data_buffer_addr = guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); - ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); - ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); - ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); - ufs_wreg(ufs, A_UTRLRSR, 1); + if (ufs->support_mcq) { + uint32_t mcqcap, qid, qcfgptr, mcq_reg_offset; + uint32_t cqattr = 0, sqattr = 0; + + mcqcap = ufs_rreg(ufs, A_MCQCAP); + qcfgptr = FIELD_EX32(mcqcap, MCQCAP, QCFGPTR); + ufs->maxq = FIELD_EX32(mcqcap, MCQCAP, MAXQ) + 1; + for (qid = 0; qid < ufs->maxq; ++qid) { + ufs->sqlba[qid] = + guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc)); + ufs->cqlba[qid] = + guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc)); + mcq_reg_offset = qcfgptr * 0x200 + qid * 0x40; + + ufs_wreg(ufs, mcq_reg_offset + A_SQLBA, + ufs->sqlba[qid] & 0xffffffff); + ufs_wreg(ufs, mcq_reg_offset + A_SQUBA, ufs->sqlba[qid] >> 32); + ufs_wreg(ufs, mcq_reg_offset + A_CQLBA, + ufs->cqlba[qid] & 0xffffffff); + ufs_wreg(ufs, mcq_reg_offset + A_CQUBA, ufs->cqlba[qid] >> 32); + + /* Enable Completion Queue */ + cqattr = FIELD_DP32(cqattr, CQATTR, CQEN, 1); + cqattr = FIELD_DP32(cqattr, CQATTR, SIZE, + QUEUE_SIZE * sizeof(UtpTransferReqDesc) / + DWORD_BYTE); + ufs_wreg(ufs, mcq_reg_offset + A_CQATTR, cqattr); + + /* Enable Submission Queue */ + sqattr = FIELD_DP32(sqattr, SQATTR, SQEN, 1); + sqattr = FIELD_DP32(sqattr, SQATTR, SIZE, + QUEUE_SIZE * sizeof(UtpTransferReqDesc) / + DWORD_BYTE); + sqattr = FIELD_DP32(sqattr, SQATTR, CQID, qid); + ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, sqattr); + + /* Cache head & tail pointer */ + ufs->sqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_SQDAO); + ufs->cqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_CQDAO); + } + } else { + nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; + ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); + + ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); + ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); + ufs_wreg(ufs, A_UTRLRSR, 1); + } /* Send nop out to test transfer request */ ocs = ufs_send_nop_out(ufs, &rsp_upiu); @@ -402,7 +502,15 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) { if (ufs->enabled) { - guest_free(alloc, ufs->utrlba); + if (ufs->support_mcq) { + for (uint32_t qid = 0; qid < ufs->maxq; ++qid) { + guest_free(alloc, ufs->sqlba[qid]); + guest_free(alloc, ufs->cqlba[qid]); + } + } else { + guest_free(alloc, ufs->utrlba); + } + guest_free(alloc, ufs->cmd_desc_addr); guest_free(alloc, ufs->data_buffer_addr); } @@ -966,12 +1074,16 @@ static void ufs_register_nodes(void) QOSGraphEdgeOptions edge_opts = { .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on", .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0", - .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8" + .extra_device_opts = "addr=04.0,id=ufs0" }; - QOSGraphTestOptions io_test_opts = { - .before = ufs_blk_test_setup, - }; + QOSGraphTestOptions io_test_opts = { .before = ufs_blk_test_setup, + .edge.extra_device_opts = + "mcq=false,nutrs=32,nutmrs=8" }; + + QOSGraphTestOptions mcq_test_opts = { .before = ufs_blk_test_setup, + .edge.extra_device_opts = + "mcq=true,mcq-maxq=1" }; add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); @@ -991,13 +1103,14 @@ static void ufs_register_nodes(void) return; } qos_add_test("init", "ufs", ufstest_init, NULL); - qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts); - qos_add_test("flag read-write", "ufs", - ufstest_query_flag_request, &io_test_opts); - qos_add_test("attr read-write", "ufs", - ufstest_query_attr_request, &io_test_opts); - qos_add_test("desc read-write", "ufs", - ufstest_query_desc_request, &io_test_opts); + qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts); + qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts); + qos_add_test("query-flag", "ufs", ufstest_query_flag_request, + &io_test_opts); + qos_add_test("query-attribute", "ufs", ufstest_query_attr_request, + &io_test_opts); + qos_add_test("query-desciptor", "ufs", ufstest_query_desc_request, + &io_test_opts); } libqos_init(ufs_register_nodes); From 3a3b282879e83efdee1cb752e75351725e07e90a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 15 Feb 2025 11:45:21 -0800 Subject: [PATCH 1774/2892] tcg: Remove last traces of TCG_TARGET_NEED_POOL_LABELS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These should have been removed with the rest. There are a couple of hosts which can emit guest_base into the constant pool: aarch64, mips64, ppc64, riscv64. Fixes: a417ef835058 ("tcg: Remove TCG_TARGET_NEED_LDST_LABELS and TCG_TARGET_NEED_POOL_LABELS") Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- tcg/tcg.c | 4 ---- tcg/tci/tcg-target.h | 1 - 2 files changed, 5 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 43b6712286..53de13df71 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1598,21 +1598,17 @@ void tcg_prologue_init(void) tcg_qemu_tb_exec = (tcg_prologue_fn *)tcg_splitwx_to_rx(s->code_ptr); #endif -#ifdef TCG_TARGET_NEED_POOL_LABELS s->pool_labels = NULL; -#endif qemu_thread_jit_write(); /* Generate the prologue. */ tcg_target_qemu_prologue(s); -#ifdef TCG_TARGET_NEED_POOL_LABELS /* Allow the prologue to put e.g. guest_base into a pool entry. */ { int result = tcg_out_pool_finalize(s); tcg_debug_assert(result == 0); } -#endif prologue_size = tcg_current_code_size(s); perf_report_prologue(s->code_gen_ptr, prologue_size); diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index a9ca493d20..bd03aa1bc4 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -72,6 +72,5 @@ typedef enum { } TCGReg; #define HAVE_TCG_QEMU_TB_EXEC -#define TCG_TARGET_NEED_POOL_LABELS #endif /* TCG_TARGET_H */ From f441b4d19b289f55a378b8d033994f45a333b581 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Feb 2025 18:03:51 -0800 Subject: [PATCH 1775/2892] tcg: Remove TCG_OVERSIZED_GUEST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now prohibited in configuration. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 7 ------- accel/tcg/tcg-all.c | 9 ++++----- docs/devel/multi-thread-tcg.rst | 1 - include/qemu/atomic.h | 18 +++-------------- include/tcg/oversized-guest.h | 23 ---------------------- target/arm/ptw.c | 34 --------------------------------- target/riscv/cpu_helper.c | 13 +------------ 7 files changed, 8 insertions(+), 97 deletions(-) delete mode 100644 include/tcg/oversized-guest.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b4ccf0cdcb..17e2251695 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -47,7 +47,6 @@ #include "qemu/plugin-memory.h" #endif #include "tcg/tcg-ldst.h" -#include "tcg/oversized-guest.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ @@ -118,12 +117,8 @@ static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry, return qatomic_read(ptr); #else const uint64_t *ptr = &entry->addr_idx[access_type]; -# if TCG_OVERSIZED_GUEST - return *ptr; -# else /* ofs might correspond to .addr_write, so use qatomic_read */ return qatomic_read(ptr); -# endif #endif } @@ -908,8 +903,6 @@ static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, uint32_t *ptr_write = (uint32_t *)&tlb_entry->addr_write; ptr_write += HOST_BIG_ENDIAN; qatomic_set(ptr_write, *ptr_write | TLB_NOTDIRTY); -#elif TCG_OVERSIZED_GUEST - tlb_entry->addr_write |= TLB_NOTDIRTY; #else qatomic_set(&tlb_entry->addr_write, tlb_entry->addr_write | TLB_NOTDIRTY); diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 95adaacee8..c1a30b0121 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -28,7 +28,6 @@ #include "exec/replay-core.h" #include "system/cpu-timers.h" #include "tcg/startup.h" -#include "tcg/oversized-guest.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/accel.h" @@ -41,6 +40,8 @@ #include "hw/boards.h" #endif #include "internal-common.h" +#include "cpu-param.h" + struct TCGState { AccelState parent_obj; @@ -72,7 +73,7 @@ DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, static bool default_mttcg_enabled(void) { - if (icount_enabled() || TCG_OVERSIZED_GUEST) { + if (icount_enabled()) { return false; } #ifdef TARGET_SUPPORTS_MTTCG @@ -145,9 +146,7 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp) TCGState *s = TCG_STATE(obj); if (strcmp(value, "multi") == 0) { - if (TCG_OVERSIZED_GUEST) { - error_setg(errp, "No MTTCG when guest word size > hosts"); - } else if (icount_enabled()) { + if (icount_enabled()) { error_setg(errp, "No MTTCG when icount is enabled"); } else { #ifndef TARGET_SUPPORTS_MTTCG diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index 7fd0a07633..b0f473961d 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -37,7 +37,6 @@ if: * forced by --accel tcg,thread=single * enabling --icount mode -* 64 bit guests on 32 bit hosts (TCG_OVERSIZED_GUEST) In the general case of running translated code there should be no inter-vCPU dependencies and all vCPUs should be able to run at full diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 7a3f2e6576..f80cba24cf 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -56,25 +56,13 @@ */ #define signal_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) -/* Sanity check that the size of an atomic operation isn't "overly large". +/* + * Sanity check that the size of an atomic operation isn't "overly large". * Despite the fact that e.g. i686 has 64-bit atomic operations, we do not * want to use them because we ought not need them, and this lets us do a * bit of sanity checking that other 32-bit hosts might build. - * - * That said, we have a problem on 64-bit ILP32 hosts in that in order to - * sync with TCG_OVERSIZED_GUEST, this must match TCG_TARGET_REG_BITS. - * We'd prefer not want to pull in everything else TCG related, so handle - * those few cases by hand. - * - * Note that x32 is fully detected with __x86_64__ + _ILP32, and that for - * Sparc we always force the use of sparcv9 in configure. MIPS n32 (ILP32) & - * n64 (LP64) ABIs are both detected using __mips64. */ -#if defined(__x86_64__) || defined(__sparc__) || defined(__mips64) -# define ATOMIC_REG_SIZE 8 -#else -# define ATOMIC_REG_SIZE sizeof(void *) -#endif +#define ATOMIC_REG_SIZE sizeof(void *) /* Weak atomic operations prevent the compiler moving other * loads/stores past the atomic operation load/store. However there is diff --git a/include/tcg/oversized-guest.h b/include/tcg/oversized-guest.h deleted file mode 100644 index 641b9749ff..0000000000 --- a/include/tcg/oversized-guest.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Define TCG_OVERSIZED_GUEST - * Copyright (c) 2008 Fabrice Bellard - */ - -#ifndef EXEC_TCG_OVERSIZED_GUEST_H -#define EXEC_TCG_OVERSIZED_GUEST_H - -#include "tcg-target-reg-bits.h" -#include "cpu-param.h" - -/* - * Oversized TCG guests make things like MTTCG hard - * as we can't use atomics for cputlb updates. - */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -#define TCG_OVERSIZED_GUEST 1 -#else -#define TCG_OVERSIZED_GUEST 0 -#endif - -#endif diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 64bb6878a4..4330900348 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -16,9 +16,6 @@ #include "internals.h" #include "cpu-features.h" #include "idau.h" -#ifdef CONFIG_TCG -# include "tcg/oversized-guest.h" -#endif typedef struct S1Translate { /* @@ -840,7 +837,6 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, ptw->out_rw = true; } -#ifdef CONFIG_ATOMIC64 if (ptw->out_be) { old_val = cpu_to_be64(old_val); new_val = cpu_to_be64(new_val); @@ -852,36 +848,6 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, cur_val = qatomic_cmpxchg__nocheck((uint64_t *)host, old_val, new_val); cur_val = le64_to_cpu(cur_val); } -#else - /* - * We can't support the full 64-bit atomic cmpxchg on the host. - * Because this is only used for FEAT_HAFDBS, which is only for AA64, - * we know that TCG_OVERSIZED_GUEST is set, which means that we are - * running in round-robin mode and could only race with dma i/o. - */ -#if !TCG_OVERSIZED_GUEST -# error "Unexpected configuration" -#endif - bool locked = bql_locked(); - if (!locked) { - bql_lock(); - } - if (ptw->out_be) { - cur_val = ldq_be_p(host); - if (cur_val == old_val) { - stq_be_p(host, new_val); - } - } else { - cur_val = ldq_le_p(host); - if (cur_val == old_val) { - stq_le_p(host, new_val); - } - } - if (!locked) { - bql_unlock(); - } -#endif - return cur_val; #else /* AArch32 does not have FEAT_HADFS; non-TCG guests only use debug-mode. */ diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index e1dfc4ecbf..8ff6d900f2 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -32,7 +32,6 @@ #include "system/cpu-timers.h" #include "cpu_bits.h" #include "debug.h" -#include "tcg/oversized-guest.h" #include "pmp.h" int riscv_env_mmu_index(CPURISCVState *env, bool ifetch) @@ -1167,9 +1166,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, hwaddr pte_addr; int i; -#if !TCG_OVERSIZED_GUEST -restart: -#endif + restart: for (i = 0; i < levels; i++, ptshift -= ptidxbits) { target_ulong idx; if (i == 0) { @@ -1388,13 +1385,6 @@ restart: false, MEMTXATTRS_UNSPECIFIED); if (memory_region_is_ram(mr)) { target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); -#if TCG_OVERSIZED_GUEST - /* - * MTTCG is not enabled on oversized TCG guests so - * page table updates do not need to be atomic - */ - *pte_pa = pte = updated_pte; -#else target_ulong old_pte; if (riscv_cpu_sxl(env) == MXL_RV32) { old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, pte, updated_pte); @@ -1405,7 +1395,6 @@ restart: goto restart; } pte = updated_pte; -#endif } else { /* * Misconfigured PTE in ROM (AD bits are not preset) or From a0ecb8e49418ab0bb01d47493fafe6a0a357e952 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 11:06:26 -0800 Subject: [PATCH 1776/2892] tcg: Drop support for two address registers in gen_ldst 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/tcg-op-ldst.c | 21 +++------------------ tcg/tcg.c | 4 +--- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 77271e0193..7ba9a3ef7e 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -91,25 +91,10 @@ static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) static void gen_ldst(TCGOpcode opc, TCGType type, TCGTemp *vl, TCGTemp *vh, TCGTemp *addr, MemOpIdx oi) { - if (TCG_TARGET_REG_BITS == 64 || tcg_ctx->addr_type == TCG_TYPE_I32) { - if (vh) { - tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), - temp_arg(addr), oi); - } else { - tcg_gen_op3(opc, type, temp_arg(vl), temp_arg(addr), oi); - } + if (vh) { + tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), temp_arg(addr), oi); } else { - /* See TCGV_LOW/HIGH. */ - TCGTemp *al = addr + HOST_BIG_ENDIAN; - TCGTemp *ah = addr + !HOST_BIG_ENDIAN; - - if (vh) { - tcg_gen_op5(opc, type, temp_arg(vl), temp_arg(vh), - temp_arg(al), temp_arg(ah), oi); - } else { - tcg_gen_op4(opc, type, temp_arg(vl), - temp_arg(al), temp_arg(ah), oi); - } + tcg_gen_op3(opc, type, temp_arg(vl), temp_arg(addr), oi); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 53de13df71..14c2d38160 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1690,9 +1690,7 @@ void tcg_func_start(TCGContext *s) s->emit_before_op = NULL; QSIMPLEQ_INIT(&s->labels); - tcg_debug_assert(s->addr_type == TCG_TYPE_I32 || - s->addr_type == TCG_TYPE_I64); - + tcg_debug_assert(s->addr_type <= TCG_TYPE_REG); tcg_debug_assert(s->insn_start_words > 0); } From 50b7a197e1d1782f9366d5e43d1f94700f6236c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 13:46:09 -0800 Subject: [PATCH 1777/2892] tcg: Merge INDEX_op_qemu_*_{a32,a64}_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 64-on-32 is now unsupported, guest addresses always fit in one host register. Drop the replication of opcodes. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 28 ++------ tcg/aarch64/tcg-target.c.inc | 36 ++++------ tcg/arm/tcg-target.c.inc | 40 +++-------- tcg/i386/tcg-target.c.inc | 69 ++++-------------- tcg/loongarch64/tcg-target.c.inc | 36 ++++------ tcg/mips/tcg-target.c.inc | 51 +++---------- tcg/optimize.c | 21 ++---- tcg/ppc/tcg-target.c.inc | 68 ++++-------------- tcg/riscv/tcg-target.c.inc | 24 +++---- tcg/s390x/tcg-target.c.inc | 36 ++++------ tcg/sparc64/tcg-target.c.inc | 24 +++---- tcg/tcg-op-ldst.c | 82 +++++---------------- tcg/tcg.c | 42 ++++------- tcg/tci.c | 119 ++++++------------------------- tcg/tci/tcg-target.c.inc | 60 ++++------------ 15 files changed, 177 insertions(+), 559 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 9383e295f4..5bf78b0764 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -188,36 +188,22 @@ DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT) -/* Replicate ld/st ops for 32 and 64-bit guest addresses. */ -DEF(qemu_ld_a32_i32, 1, 1, 1, +DEF(qemu_ld_i32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_a32_i32, 0, 1 + 1, 1, +DEF(qemu_st_i32, 0, 1 + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_ld_a32_i64, DATA64_ARGS, 1, 1, +DEF(qemu_ld_i64, DATA64_ARGS, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_a32_i64, 0, DATA64_ARGS + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) - -DEF(qemu_ld_a64_i32, 1, DATA64_ARGS, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_a64_i32, 0, 1 + DATA64_ARGS, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_ld_a64_i64, DATA64_ARGS, DATA64_ARGS, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_a64_i64, 0, DATA64_ARGS + DATA64_ARGS, 1, +DEF(qemu_st_i64, 0, DATA64_ARGS + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Only used by i386 to cope with stupid register constraints. */ -DEF(qemu_st8_a32_i32, 0, 1 + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st8_a64_i32, 0, 1 + DATA64_ARGS, 1, +DEF(qemu_st8_i32, 0, 1 + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Only for 64-bit hosts at the moment. */ -DEF(qemu_ld_a32_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_ld_a64_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_a32_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_a64_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) /* Host vector support. */ diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 66eb4b73b5..45dc2c649b 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2398,24 +2398,18 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3506, CSEL, ext, a0, REG0(3), REG0(4), args[5]); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, a0, a1, a2, ext); break; - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: tcg_out_qemu_st(s, REG0(0), a1, a2, ext); break; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], true); break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: tcg_out_qemu_ldst_i128(s, REG0(0), REG0(1), a2, args[3], false); break; @@ -3084,21 +3078,15 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rZ, rZ); - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: return C_O2_I1(r, r, r); - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: return C_O0_I2(rZ, r); - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: return C_O0_I3(rZ, rZ, r); case INDEX_op_deposit_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 12dad7307f..05bb367a39 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2071,37 +2071,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, ARITH_MOV, args[0], 0, 0); break; - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a64_i32: - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], - args[3], TCG_TYPE_I32); - break; - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, args[0], args[1], args[2], -1, args[3], TCG_TYPE_I64); break; - case INDEX_op_qemu_ld_a64_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], - args[4], TCG_TYPE_I64); - break; - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a64_i32: - tcg_out_qemu_st(s, args[0], -1, args[1], args[2], - args[3], TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: tcg_out_qemu_st(s, args[0], args[1], args[2], -1, args[3], TCG_TYPE_I64); break; - case INDEX_op_qemu_st_a64_i64: - tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], - args[4], TCG_TYPE_I64); - break; case INDEX_op_bswap16_i32: tcg_out_bswap16(s, COND_AL, args[0], args[1], args[2]); @@ -2243,22 +2227,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, rI, rI); - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: return C_O1_I1(r, q); - case INDEX_op_qemu_ld_a64_i32: - return C_O1_I2(r, q, q); - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: return C_O2_I1(e, p, q); - case INDEX_op_qemu_ld_a64_i64: - return C_O2_I2(e, p, q, q); - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: return C_O0_I2(q, q); - case INDEX_op_qemu_st_a64_i32: - return C_O0_I3(q, q, q); - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: return C_O0_I3(Q, p, q); - case INDEX_op_qemu_st_a64_i64: - return C_O0_I4(Q, p, q, q); case INDEX_op_st_vec: return C_O0_I2(w, r); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 2cac151331..ca6e8abc57 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2879,62 +2879,33 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); break; - case INDEX_op_qemu_ld_a64_i32: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_ld(s, a0, -1, a1, a2, args[3], TCG_TYPE_I32); - break; - } - /* fall through */ - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); } else { tcg_out_qemu_ld(s, a0, a1, a2, -1, args[3], TCG_TYPE_I64); } break; - case INDEX_op_qemu_ld_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_ld(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); - } - break; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_qemu_ld(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); break; - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st8_a64_i32: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_st(s, a0, -1, a1, a2, args[3], TCG_TYPE_I32); - break; - } - /* fall through */ - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st8_a32_i32: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st8_i32: tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); } else { tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I64); } break; - case INDEX_op_qemu_st_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); - } - break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); break; @@ -3824,36 +3795,24 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_clz_i64: return have_lzcnt ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: return C_O1_I1(r, L); - case INDEX_op_qemu_ld_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O1_I2(r, L, L); - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: return C_O0_I2(L, L); - case INDEX_op_qemu_st_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I3(L, L, L); - case INDEX_op_qemu_st8_a32_i32: + case INDEX_op_qemu_st8_i32: return C_O0_I2(s, L); - case INDEX_op_qemu_st8_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(s, L) : C_O0_I3(s, L, L); - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O2_I1(r, r, L); - case INDEX_op_qemu_ld_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O2_I2(r, r, L, L); - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I3(L, L, L); - case INDEX_op_qemu_st_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I4(L, L, L, L); - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); return C_O2_I1(r, r, L); - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); return C_O0_I3(L, L, L); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index cebe8dd354..4f32bf3e97 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1675,28 +1675,22 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_ST_D, a0, a1, a2); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, true); break; - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i64: tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, false); break; @@ -2233,18 +2227,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st32_i64: case INDEX_op_st_i32: case INDEX_op_st_i64: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: return C_O0_I2(rZ, r); - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: return C_N2_I1(r, r, r); - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: return C_O0_I3(r, r, r); case INDEX_op_brcond_i32: @@ -2290,10 +2280,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld_i32: case INDEX_op_ld_i64: - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); case INDEX_op_andc_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 99f6ef6c76..b1d512ca2a 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2095,53 +2095,27 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); break; - case INDEX_op_qemu_ld_a64_i32: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_ld(s, a0, 0, a1, a2, args[3], TCG_TYPE_I32); - break; - } - /* fall through */ - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); } else { tcg_out_qemu_ld(s, a0, a1, a2, 0, args[3], TCG_TYPE_I64); } break; - case INDEX_op_qemu_ld_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_ld(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); - } - break; - case INDEX_op_qemu_st_a64_i32: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_st(s, a0, 0, a1, a2, args[3], TCG_TYPE_I32); - break; - } - /* fall through */ - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); } else { tcg_out_qemu_st(s, a0, a1, a2, 0, args[3], TCG_TYPE_I64); } break; - case INDEX_op_qemu_st_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); - } - break; case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], @@ -2301,23 +2275,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_brcond2_i32: return C_O0_I4(rZ, rZ, rZ, rZ); - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O1_I2(r, r, r); - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: return C_O0_I2(rZ, r); - case INDEX_op_qemu_st_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) : C_O0_I3(rZ, r, r); - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_ld_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I2(r, r, r, r); - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) : C_O0_I3(rZ, rZ, r); - case INDEX_op_qemu_st_a64_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) - : C_O0_I4(rZ, rZ, r, r)); default: return C_NotImplemented; diff --git a/tcg/optimize.c b/tcg/optimize.c index bca11cc427..f922f86a1d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -3011,29 +3011,22 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(orc): done = fold_orc(&ctx, op); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_i32: done = fold_qemu_ld_1reg(&ctx, op); break; - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { done = fold_qemu_ld_1reg(&ctx, op); break; } QEMU_FALLTHROUGH; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: done = fold_qemu_ld_2reg(&ctx, op); break; - case INDEX_op_qemu_st8_a32_i32: - case INDEX_op_qemu_st8_a64_i32: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st8_i32: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st_i128: done = fold_qemu_st(&ctx, op); break; CASE_OP_32_64(rem): diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 6e711cd53f..801cb6f3cb 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3308,17 +3308,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, MODUD | TAB(args[0], args[1], args[2])); break; - case INDEX_op_qemu_ld_a64_i32: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], - args[3], TCG_TYPE_I32); - break; - } - /* fall through */ - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I64); @@ -3327,32 +3320,15 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3], TCG_TYPE_I64); } break; - case INDEX_op_qemu_ld_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, args[0], -1, args[1], -1, - args[2], TCG_TYPE_I64); - } else { - tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], - args[4], TCG_TYPE_I64); - } - break; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); break; - case INDEX_op_qemu_st_a64_i32: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_st(s, args[0], -1, args[1], args[2], - args[3], TCG_TYPE_I32); - break; - } - /* fall through */ - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I64); @@ -3361,17 +3337,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3], TCG_TYPE_I64); } break; - case INDEX_op_qemu_st_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, args[0], -1, args[1], -1, - args[2], TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], - args[4], TCG_TYPE_I64); - } - break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; @@ -4306,29 +4272,19 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i32: return C_O2_I4(r, r, rI, rZM, r, r); - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O1_I2(r, r, r); - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_ld_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I2(r, r, r, r); - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: return C_O0_I2(r, r); - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i64: return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); - case INDEX_op_qemu_st_a32_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); - case INDEX_op_qemu_st_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I4(r, r, r, r); - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: return C_N1O1_I1(o, m, r); - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: return C_O0_I3(o, m, r); case INDEX_op_add_vec: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 61dc310c1a..55a3398712 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2309,20 +2309,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3], const_args[3], args[4], const_args[4]); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i64: tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; @@ -2761,15 +2757,11 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i64: return C_O2_I4(r, r, rZ, rZ, rM, rM); - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: return C_O0_I2(rZ, r); case INDEX_op_st_vec: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index dc7722dc31..6786e7b316 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2455,28 +2455,22 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[2], const_args[2], args[3], const_args[3], args[4]); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I64); break; - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, args[0], args[1], args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i64: tcg_out_qemu_st(s, args[0], args[1], args[2], TCG_TYPE_I64); break; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; @@ -3366,21 +3360,15 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctpop_i64: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st_i32: return C_O0_I2(r, r); - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_ld_i128: return C_O2_I1(o, m, r); - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_st_i128: return C_O0_I3(o, m, r); case INDEX_op_deposit_i32: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 733cb51651..ea0a3b8692 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1426,20 +1426,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i32: tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i64: tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; @@ -1570,10 +1566,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extract_i64: case INDEX_op_sextract_i64: - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: @@ -1583,10 +1577,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st_i64: return C_O0_I2(rZ, r); case INDEX_op_add_i32: diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 7ba9a3ef7e..73838e2701 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -217,7 +217,6 @@ static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, MemOp orig_memop; MemOpIdx orig_oi, oi; TCGv_i64 copy_addr; - TCGOpcode opc; tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); orig_memop = memop = tcg_canonicalize_memop(memop, 0, 0); @@ -233,12 +232,8 @@ static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, } copy_addr = plugin_maybe_preserve_addr(addr); - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_ld_a32_i32; - } else { - opc = INDEX_op_qemu_ld_a64_i32; - } - gen_ldst(opc, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr, oi); + gen_ldst(INDEX_op_qemu_ld_i32, TCG_TYPE_I32, + tcgv_i32_temp(val), NULL, addr, oi); plugin_gen_mem_callbacks_i32(val, copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); @@ -295,17 +290,9 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, } if (TCG_TARGET_HAS_qemu_st8_i32 && (memop & MO_SIZE) == MO_8) { - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st8_a32_i32; - } else { - opc = INDEX_op_qemu_st8_a64_i32; - } + opc = INDEX_op_qemu_st8_i32; } else { - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st_a32_i32; - } else { - opc = INDEX_op_qemu_st_a64_i32; - } + opc = INDEX_op_qemu_st_i32; } gen_ldst(opc, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr, oi); plugin_gen_mem_callbacks_i32(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); @@ -329,7 +316,6 @@ static void tcg_gen_qemu_ld_i64_int(TCGv_i64 val, TCGTemp *addr, MemOp orig_memop; MemOpIdx orig_oi, oi; TCGv_i64 copy_addr; - TCGOpcode opc; if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_ld_i32_int(TCGV_LOW(val), addr, idx, memop); @@ -355,12 +341,7 @@ static void tcg_gen_qemu_ld_i64_int(TCGv_i64 val, TCGTemp *addr, } copy_addr = plugin_maybe_preserve_addr(addr); - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_ld_a32_i64; - } else { - opc = INDEX_op_qemu_ld_a64_i64; - } - gen_ldst_i64(opc, val, addr, oi); + gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, oi); plugin_gen_mem_callbacks_i64(val, copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); @@ -397,7 +378,6 @@ static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, { TCGv_i64 swap = NULL; MemOpIdx orig_oi, oi; - TCGOpcode opc; if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_st_i32_int(TCGV_LOW(val), addr, idx, memop); @@ -428,12 +408,7 @@ static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, oi = make_memop_idx(memop, idx); } - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st_a32_i64; - } else { - opc = INDEX_op_qemu_st_a64_i64; - } - gen_ldst_i64(opc, val, addr, oi); + gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, oi); plugin_gen_mem_callbacks_i64(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { @@ -545,7 +520,6 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, { MemOpIdx orig_oi; TCGv_i64 ext_addr = NULL; - TCGOpcode opc; check_max_alignment(memop_alignment_bits(memop)); tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); @@ -573,12 +547,7 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, hi = TCGV128_HIGH(val); } - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_ld_a32_i128; - } else { - opc = INDEX_op_qemu_ld_a64_i128; - } - gen_ldst(opc, TCG_TYPE_I128, tcgv_i64_temp(lo), + gen_ldst(INDEX_op_qemu_ld_i128, TCG_TYPE_I128, tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); if (need_bswap) { @@ -594,12 +563,6 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, canonicalize_memop_i128_as_i64(mop, memop); need_bswap = (mop[0] ^ memop) & MO_BSWAP; - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_ld_a32_i64; - } else { - opc = INDEX_op_qemu_ld_a64_i64; - } - /* * Since there are no global TCGv_i128, there is no visible state * changed if the second load faults. Load directly into the two @@ -613,7 +576,8 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, y = TCGV128_LOW(val); } - gen_ldst_i64(opc, x, addr, make_memop_idx(mop[0], idx)); + gen_ldst_i64(INDEX_op_qemu_ld_i64, x, addr, + make_memop_idx(mop[0], idx)); if (need_bswap) { tcg_gen_bswap64_i64(x, x); @@ -629,7 +593,8 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, addr_p8 = tcgv_i64_temp(t); } - gen_ldst_i64(opc, y, addr_p8, make_memop_idx(mop[1], idx)); + gen_ldst_i64(INDEX_op_qemu_ld_i64, y, addr_p8, + make_memop_idx(mop[1], idx)); tcg_temp_free_internal(addr_p8); if (need_bswap) { @@ -663,7 +628,6 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, { MemOpIdx orig_oi; TCGv_i64 ext_addr = NULL; - TCGOpcode opc; check_max_alignment(memop_alignment_bits(memop)); tcg_gen_req_mo(TCG_MO_ST_LD | TCG_MO_ST_ST); @@ -694,13 +658,8 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, hi = TCGV128_HIGH(val); } - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st_a32_i128; - } else { - opc = INDEX_op_qemu_st_a64_i128; - } - gen_ldst(opc, TCG_TYPE_I128, tcgv_i64_temp(lo), - tcgv_i64_temp(hi), addr, oi); + gen_ldst(INDEX_op_qemu_st_i128, TCG_TYPE_I128, + tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_temp_free_i64(lo); @@ -713,12 +672,6 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, canonicalize_memop_i128_as_i64(mop, memop); - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st_a32_i64; - } else { - opc = INDEX_op_qemu_st_a64_i64; - } - if ((memop & MO_BSWAP) == MO_LE) { x = TCGV128_LOW(val); y = TCGV128_HIGH(val); @@ -733,7 +686,8 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, x = b; } - gen_ldst_i64(opc, x, addr, make_memop_idx(mop[0], idx)); + gen_ldst_i64(INDEX_op_qemu_st_i64, x, addr, + make_memop_idx(mop[0], idx)); if (tcg_ctx->addr_type == TCG_TYPE_I32) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -747,10 +701,12 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, if (b) { tcg_gen_bswap64_i64(b, y); - gen_ldst_i64(opc, b, addr_p8, make_memop_idx(mop[1], idx)); + gen_ldst_i64(INDEX_op_qemu_st_i64, b, addr_p8, + make_memop_idx(mop[1], idx)); tcg_temp_free_i64(b); } else { - gen_ldst_i64(opc, y, addr_p8, make_memop_idx(mop[1], idx)); + gen_ldst_i64(INDEX_op_qemu_st_i64, y, addr_p8, + make_memop_idx(mop[1], idx)); } tcg_temp_free_internal(addr_p8); } else { diff --git a/tcg/tcg.c b/tcg/tcg.c index 14c2d38160..fef93b25ff 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2147,24 +2147,17 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_exit_tb: case INDEX_op_goto_tb: case INDEX_op_goto_ptr: - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_st_i64: return true; - case INDEX_op_qemu_st8_a32_i32: - case INDEX_op_qemu_st8_a64_i32: + case INDEX_op_qemu_st8_i32: return TCG_TARGET_HAS_qemu_st8_i32; - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_st_i128: return TCG_TARGET_HAS_qemu_ldst_i128; case INDEX_op_mov_i32: @@ -2862,20 +2855,13 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } i = 1; break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st8_a32_i32: - case INDEX_op_qemu_st8_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: - case INDEX_op_qemu_ld_a32_i128: - case INDEX_op_qemu_ld_a64_i128: - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st8_i32: + case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_st_i128: { const char *s_al, *s_op, *s_at; MemOpIdx oi = op->args[k++]; diff --git a/tcg/tci.c b/tcg/tci.c index 8c1c53424d..d223258efe 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -154,16 +154,6 @@ static void tci_args_rrrbb(uint32_t insn, TCGReg *r0, TCGReg *r1, *i4 = extract32(insn, 26, 6); } -static void tci_args_rrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, - TCGReg *r2, TCGReg *r3, TCGReg *r4) -{ - *r0 = extract32(insn, 8, 4); - *r1 = extract32(insn, 12, 4); - *r2 = extract32(insn, 16, 4); - *r3 = extract32(insn, 20, 4); - *r4 = extract32(insn, 24, 4); -} - static void tci_args_rrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGReg *r3) { @@ -912,43 +902,21 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tb_ptr = ptr; break; - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: tci_args_rrm(insn, &r0, &r1, &oi); - taddr = (uint32_t)regs[r1]; - goto do_ld_i32; - case INDEX_op_qemu_ld_a64_i32: - if (TCG_TARGET_REG_BITS == 64) { - tci_args_rrm(insn, &r0, &r1, &oi); - taddr = regs[r1]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - taddr = tci_uint64(regs[r2], regs[r1]); - oi = regs[r3]; - } - do_ld_i32: + taddr = regs[r1]; regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr); break; - case INDEX_op_qemu_ld_a32_i64: - if (TCG_TARGET_REG_BITS == 64) { - tci_args_rrm(insn, &r0, &r1, &oi); - taddr = (uint32_t)regs[r1]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - taddr = (uint32_t)regs[r2]; - oi = regs[r3]; - } - goto do_ld_i64; - case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; } else { - tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); - taddr = tci_uint64(regs[r3], regs[r2]); - oi = regs[r4]; + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + taddr = regs[r2]; + oi = regs[r3]; } - do_ld_i64: tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr); if (TCG_TARGET_REG_BITS == 32) { tci_write_reg64(regs, r1, r0, tmp64); @@ -957,47 +925,23 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, } break; - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: tci_args_rrm(insn, &r0, &r1, &oi); - taddr = (uint32_t)regs[r1]; - goto do_st_i32; - case INDEX_op_qemu_st_a64_i32: - if (TCG_TARGET_REG_BITS == 64) { - tci_args_rrm(insn, &r0, &r1, &oi); - taddr = regs[r1]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - taddr = tci_uint64(regs[r2], regs[r1]); - oi = regs[r3]; - } - do_st_i32: + taddr = regs[r1]; tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr); break; - case INDEX_op_qemu_st_a32_i64: - if (TCG_TARGET_REG_BITS == 64) { - tci_args_rrm(insn, &r0, &r1, &oi); - tmp64 = regs[r0]; - taddr = (uint32_t)regs[r1]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - tmp64 = tci_uint64(regs[r1], regs[r0]); - taddr = (uint32_t)regs[r2]; - oi = regs[r3]; - } - goto do_st_i64; - case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { tci_args_rrm(insn, &r0, &r1, &oi); tmp64 = regs[r0]; taddr = regs[r1]; } else { - tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); tmp64 = tci_uint64(regs[r1], regs[r0]); - taddr = tci_uint64(regs[r3], regs[r2]); - oi = regs[r4]; + taddr = regs[r2]; + oi = regs[r3]; } - do_st_i64: tci_qemu_st(env, taddr, tmp64, oi, tb_ptr); break; @@ -1269,42 +1213,21 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) str_r(r3), str_r(r4), str_r(r5)); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_st_a32_i32: - len = 1 + 1; - goto do_qemu_ldst; - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_st_a64_i32: - len = 1 + DIV_ROUND_UP(64, TCG_TARGET_REG_BITS); - goto do_qemu_ldst; - case INDEX_op_qemu_ld_a64_i64: - case INDEX_op_qemu_st_a64_i64: - len = 2 * DIV_ROUND_UP(64, TCG_TARGET_REG_BITS); - goto do_qemu_ldst; - do_qemu_ldst: - switch (len) { - case 2: - tci_args_rrm(insn, &r0, &r1, &oi); - info->fprintf_func(info->stream, "%-12s %s, %s, %x", - op_name, str_r(r0), str_r(r1), oi); - break; - case 3: + case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_st_i64: + if (TCG_TARGET_REG_BITS == 32) { tci_args_rrrr(insn, &r0, &r1, &r2, &r3); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2), str_r(r3)); break; - case 4: - tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s", - op_name, str_r(r0), str_r(r1), - str_r(r2), str_r(r3), str_r(r4)); - break; - default: - g_assert_not_reached(); } + /* fall through */ + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_st_i32: + tci_args_rrm(insn, &r0, &r1, &oi); + info->fprintf_func(info->stream, "%-12s %s, %s, %x", + op_name, str_r(r0), str_r(r1), oi); break; case 0: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d6c77325a3..36e018dd19 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -169,22 +169,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, r, r); - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_a64_i32: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O1_I2(r, r, r); - case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_i64: return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_ld_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I2(r, r, r, r); - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_i32: return C_O0_I2(r, r); - case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_i64: return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); - case INDEX_op_qemu_st_a32_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); - case INDEX_op_qemu_st_a64_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I4(r, r, r, r); default: return C_NotImplemented; @@ -422,20 +414,6 @@ static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0, tcg_out32(s, insn); } -static void tcg_out_op_rrrrr(TCGContext *s, TCGOpcode op, TCGReg r0, - TCGReg r1, TCGReg r2, TCGReg r3, TCGReg r4) -{ - tcg_insn_unit insn = 0; - - insn = deposit32(insn, 0, 8, op); - insn = deposit32(insn, 8, 4, r0); - insn = deposit32(insn, 12, 4, r1); - insn = deposit32(insn, 16, 4, r2); - insn = deposit32(insn, 20, 4, r3); - insn = deposit32(insn, 24, 4, r4); - tcg_out32(s, insn); -} - static void tcg_out_op_rrrr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3) { @@ -833,29 +811,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], args[3]); break; - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_st_a32_i32: - tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_st_a32_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); - } else { + case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_st_i64: + if (TCG_TARGET_REG_BITS == 32) { tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); + break; } - break; - case INDEX_op_qemu_ld_a64_i64: - case INDEX_op_qemu_st_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); + /* fall through */ + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_st_i32: + if (TCG_TARGET_REG_BITS == 64 && s->addr_type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_TMP, args[1]); + tcg_out_op_rrm(s, opc, args[0], TCG_REG_TMP, args[2]); } else { - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[4]); - tcg_out_op_rrrrr(s, opc, args[0], args[1], - args[2], args[3], TCG_REG_TMP); + tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); } break; From d9a8889f6d10f586e28f4bfb8611cf0bc36fcaa9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 17:20:55 -0800 Subject: [PATCH 1778/2892] tcg/arm: Drop addrhi from prepare_host_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest address will now always be TCG_TYPE_I32. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 73 +++++++++++++--------------------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 05bb367a39..93a3ccaf66 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -676,14 +676,8 @@ static void tcg_out_ldrd_r(TCGContext *s, ARMCond cond, TCGReg rt, tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0); } -static void __attribute__((unused)) -tcg_out_ldrd_rwb(TCGContext *s, ARMCond cond, TCGReg rt, TCGReg rn, TCGReg rm) -{ - tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 1); -} - -static void __attribute__((unused)) -tcg_out_strd_8(TCGContext *s, ARMCond cond, TCGReg rt, TCGReg rn, int imm8) +static void tcg_out_strd_8(TCGContext *s, ARMCond cond, TCGReg rt, + TCGReg rn, int imm8) { tcg_out_memop_8(s, cond, INSN_STRD_IMM, rt, rn, imm8, 1, 0); } @@ -1455,8 +1449,7 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) #define MIN_TLB_MASK_TABLE_OFS -256 static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, bool is_ld) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); @@ -1465,14 +1458,14 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, if (tcg_use_softmmu) { *h = (HostAddress){ .cond = COND_AL, - .base = addrlo, + .base = addr, .index = TCG_REG_R1, .index_scratch = true, }; } else { *h = (HostAddress){ .cond = COND_AL, - .base = addrlo, + .base = addr, .index = guest_base ? TCG_REG_GUEST_BASE : -1, .index_scratch = false, }; @@ -1492,8 +1485,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {r0,r1}. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); @@ -1501,30 +1493,20 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, 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, + tcg_out_dat_reg(s, COND_AL, ARITH_AND, TCG_REG_R0, TCG_REG_R0, addr, SHIFT_IMM_LSR(s->page_bits - CPU_TLB_ENTRY_BITS)); /* * Add the tlb_table pointer, creating the CPUTLBEntry address in R1. - * Load the tlb comparator into R2/R3 and the fast path addend into R1. + * Load the tlb comparator into R2 and the fast path addend into R1. */ QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); if (cmp_off == 0) { - if (s->addr_type == TCG_TYPE_I32) { - tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, - TCG_REG_R1, TCG_REG_R0); - } else { - tcg_out_ldrd_rwb(s, COND_AL, TCG_REG_R2, - TCG_REG_R1, TCG_REG_R0); - } + tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); } else { tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R1, TCG_REG_R1, TCG_REG_R0, 0); - if (s->addr_type == TCG_TYPE_I32) { - tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); - } else { - tcg_out_ldrd_8(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); - } + tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); } /* Load the tlb addend. */ @@ -1543,11 +1525,11 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * This leaves the least significant alignment bits unchanged, and of * course must be zero. */ - t_addr = addrlo; + t_addr = addr; 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); + addr, s_mask - a_mask); } if (use_armv7_instructions && s->page_bits <= 16) { tcg_out_movi32(s, COND_AL, TCG_REG_TMP, ~(s->page_mask | a_mask)); @@ -1558,7 +1540,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } else { 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_imm(s, COND_AL, ARITH_TST, 0, addr, a_mask); } tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, t_addr, SHIFT_IMM_LSR(s->page_bits)); @@ -1566,21 +1548,16 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, 0, TCG_REG_R2, TCG_REG_TMP, SHIFT_IMM_LSL(s->page_bits)); } - - if (s->addr_type != TCG_TYPE_I32) { - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, TCG_REG_R3, addrhi, 0); - } } else if (a_mask) { ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* We are expecting alignment to max out at 7 */ tcg_debug_assert(a_mask <= 0xff); /* tst addr, #mask */ - tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addr, a_mask); } return ldst; @@ -1678,14 +1655,13 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, } static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + ldst = prepare_host_addr(s, &h, addr, oi, true); if (ldst) { ldst->type = data_type; ldst->datalo_reg = datalo; @@ -1764,14 +1740,13 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, } static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); if (ldst) { ldst->type = data_type; ldst->datalo_reg = datalo; @@ -2072,19 +2047,17 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); + tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2], -1, - args[3], TCG_TYPE_I64); + tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); + tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args[0], args[1], args[2], -1, - args[3], TCG_TYPE_I64); + tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; case INDEX_op_bswap16_i32: From dc8e2f8f7840835e51f23b891b75a79101efc91c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 17:36:35 -0800 Subject: [PATCH 1779/2892] tcg/i386: Drop addrhi from prepare_host_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest address will now always fit in one register. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 56 ++++++++++++++------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index ca6e8abc57..b33fe7fe23 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2169,8 +2169,7 @@ static inline int setup_guest_base_seg(void) * is required and fill in @h with the host address for the fast path. */ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, bool is_ld) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); @@ -2184,7 +2183,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } else { *h = x86_guest_base; } - h->base = addrlo; + h->base = addr; h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, s_bits == MO_128); a_mask = (1 << h->aa.align) - 1; @@ -2202,8 +2201,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; if (TCG_TARGET_REG_BITS == 64) { ttype = s->addr_type; @@ -2217,7 +2215,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } } - tcg_out_mov(s, tlbtype, TCG_REG_L0, addrlo); + tcg_out_mov(s, tlbtype, TCG_REG_L0, addr); tcg_out_shifti(s, SHIFT_SHR + tlbrexw, TCG_REG_L0, s->page_bits - CPU_TLB_ENTRY_BITS); @@ -2233,10 +2231,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * check that we don't cross pages for the complete access. */ if (a_mask >= s_mask) { - tcg_out_mov(s, ttype, TCG_REG_L1, addrlo); + tcg_out_mov(s, ttype, TCG_REG_L1, addr); } else { tcg_out_modrm_offset(s, OPC_LEA + trexw, TCG_REG_L1, - addrlo, s_mask - a_mask); + addr, s_mask - a_mask); } tlb_mask = s->page_mask | a_mask; tgen_arithi(s, ARITH_AND + trexw, TCG_REG_L1, tlb_mask, 0); @@ -2250,17 +2248,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->label_ptr[0] = s->code_ptr; s->code_ptr += 4; - if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I64) { - /* cmp 4(TCG_REG_L0), addrhi */ - tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, - TCG_REG_L0, cmp_ofs + 4); - - /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); - ldst->label_ptr[1] = s->code_ptr; - s->code_ptr += 4; - } - /* TLB Hit. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_L0, TCG_REG_L0, offsetof(CPUTLBEntry, addend)); @@ -2270,11 +2257,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* jne slow_path */ - jcc = tcg_out_cmp(s, TCG_COND_TSTNE, addrlo, a_mask, true, false); + jcc = tcg_out_cmp(s, TCG_COND_TSTNE, addr, a_mask, true, false); tcg_out_opc(s, OPC_JCC_long + jcc, 0, 0, 0); ldst->label_ptr[0] = s->code_ptr; s->code_ptr += 4; @@ -2446,13 +2432,12 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + ldst = prepare_host_addr(s, &h, addr, oi, true); tcg_out_qemu_ld_direct(s, datalo, datahi, h, data_type, get_memop(oi)); if (ldst) { @@ -2574,13 +2559,12 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); tcg_out_qemu_st_direct(s, datalo, datahi, h, get_memop(oi)); if (ldst) { @@ -2880,34 +2864,34 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I32); + tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I32); break; case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); + tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I64); } else { - tcg_out_qemu_ld(s, a0, a1, a2, -1, args[3], TCG_TYPE_I64); + tcg_out_qemu_ld(s, a0, a1, a2, args[3], TCG_TYPE_I64); } break; case INDEX_op_qemu_ld_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_out_qemu_ld(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); + tcg_out_qemu_ld(s, a0, a1, a2, args[3], TCG_TYPE_I128); break; case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st8_i32: - tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I32); + tcg_out_qemu_st(s, a0, -1, a1, a2, TCG_TYPE_I32); break; case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); + tcg_out_qemu_st(s, a0, -1, a1, a2, TCG_TYPE_I64); } else { - tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I64); + tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I64); } break; case INDEX_op_qemu_st_i128: tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); + tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I128); break; OP_32_64(mulu2): From 0d000618d92344e0575018dcaa8ebc01e2d589b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 17:43:23 -0800 Subject: [PATCH 1780/2892] tcg/mips: Drop addrhi from prepare_host_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest address will now always fit in one register. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target.c.inc | 62 ++++++++++++++------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index b1d512ca2a..153ce1f3c3 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1217,8 +1217,7 @@ bool tcg_target_has_memory_bswap(MemOp memop) * is required and fill in @h with the host address for the fast path. */ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, bool is_ld) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; @@ -1245,8 +1244,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_AREG0, mask_off); @@ -1254,11 +1252,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the TLB index from the address into TMP3. */ if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP3, addrlo, + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP3, addr, s->page_bits - CPU_TLB_ENTRY_BITS); } else { - tcg_out_dsrl(s, TCG_TMP3, addrlo, - s->page_bits - CPU_TLB_ENTRY_BITS); + tcg_out_dsrl(s, TCG_TMP3, addr, s->page_bits - CPU_TLB_ENTRY_BITS); } tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); @@ -1288,48 +1285,35 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_opc_imm(s, (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32 ? OPC_ADDIU : OPC_DADDIU), - TCG_TMP2, addrlo, s_mask - a_mask); + TCG_TMP2, addr, s_mask - a_mask); tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP2); } else { - tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrlo); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addr); } /* Zero extend a 32-bit guest address for a 64-bit host. */ if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_TMP2, addrlo); - addrlo = TCG_TMP2; + tcg_out_ext32u(s, TCG_TMP2, addr); + addr = TCG_TMP2; } ldst->label_ptr[0] = s->code_ptr; tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); - /* Load and test the high half tlb comparator. */ - if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { - /* delay slot */ - 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_TMP3, TCG_TMP3, add_off); - - ldst->label_ptr[1] = s->code_ptr; - tcg_out_opc_br(s, OPC_BNE, addrhi, TCG_TMP0); - } - /* delay slot */ base = TCG_TMP3; - tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP3, addrlo); + tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP3, addr); } else { if (a_mask && (use_mips32r6_instructions || a_bits != s_bits)) { ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* 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); + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, addr, a_mask); ldst->label_ptr[0] = s->code_ptr; if (use_mips32r6_instructions) { @@ -1340,7 +1324,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } } - base = addrlo; + base = addr; if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { tcg_out_ext32u(s, TCG_REG_A0, base); base = TCG_REG_A0; @@ -1460,14 +1444,13 @@ static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + ldst = prepare_host_addr(s, &h, addr, oi, true); if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, data_type); @@ -1547,14 +1530,13 @@ static void tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { tcg_out_qemu_st_direct(s, datalo, datahi, h.base, opc); @@ -2096,24 +2078,24 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I32); + tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I32); break; case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); + tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I64); } else { - tcg_out_qemu_ld(s, a0, a1, a2, 0, args[3], TCG_TYPE_I64); + tcg_out_qemu_ld(s, a0, a1, a2, args[3], TCG_TYPE_I64); } break; case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I32); + tcg_out_qemu_st(s, a0, 0, a1, a2, TCG_TYPE_I32); break; case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); + tcg_out_qemu_st(s, a0, 0, a1, a2, TCG_TYPE_I64); } else { - tcg_out_qemu_st(s, a0, a1, a2, 0, args[3], TCG_TYPE_I64); + tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I64); } break; From 7a967f34660c06f8d304fecd118f046fe21cb261 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 17:49:19 -0800 Subject: [PATCH 1781/2892] tcg/ppc: Drop addrhi from prepare_host_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest address will now always fit in one register. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target.c.inc | 75 ++++++++++++---------------------------- 1 file changed, 23 insertions(+), 52 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 801cb6f3cb..74b93f4b57 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2438,8 +2438,7 @@ bool tcg_target_has_memory_bswap(MemOp memop) * is required and fill in @h with the host address for the fast path. */ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, bool is_ld) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; @@ -2474,8 +2473,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, mask_off); @@ -2483,10 +2481,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the page index, shifted into place for tlb index. */ if (TCG_TARGET_REG_BITS == 32) { - tcg_out_shri32(s, TCG_REG_R0, addrlo, + tcg_out_shri32(s, TCG_REG_R0, addr, s->page_bits - CPU_TLB_ENTRY_BITS); } else { - tcg_out_shri64(s, TCG_REG_R0, addrlo, + tcg_out_shri64(s, TCG_REG_R0, addr, s->page_bits - CPU_TLB_ENTRY_BITS); } tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); @@ -2534,10 +2532,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, if (a_bits < s_bits) { a_bits = s_bits; } - tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, + tcg_out_rlw(s, RLWINM, TCG_REG_R0, addr, 0, (32 - a_bits) & 31, 31 - s->page_bits); } else { - TCGReg t = addrlo; + TCGReg t = addr; /* * If the access is unaligned, we need to make sure we fail if we @@ -2566,30 +2564,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } } - if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { - /* Low part comparison into cr7. */ - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, - 0, 7, TCG_TYPE_I32); - - /* Load the high part TLB comparator into TMP2. */ - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, TCG_REG_TMP1, - cmp_off + 4 * !HOST_BIG_ENDIAN); - - /* Load addend, deferred for this case. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, - offsetof(CPUTLBEntry, addend)); - - /* High part comparison into cr6. */ - tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_TMP2, - 0, 6, TCG_TYPE_I32); - - /* Combine comparisons into cr0. */ - tcg_out32(s, CRAND | BT(0, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); - } else { - /* Full comparison into cr0. */ - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, - 0, 0, addr_type); - } + /* Full comparison into cr0. */ + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, 0, 0, addr_type); /* Load a pointer into the current opcode w/conditional branch-link. */ ldst->label_ptr[0] = s->code_ptr; @@ -2601,12 +2577,11 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->addrlo_reg = addr; /* 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, (1 << a_bits) - 1)); + tcg_out32(s, ANDI | SAI(addr, TCG_REG_R0, (1 << a_bits) - 1)); ldst->label_ptr[0] = s->code_ptr; tcg_out32(s, BC | BI(0, CR_EQ) | BO_COND_FALSE | LK); @@ -2617,24 +2592,23 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { /* Zero-extend the guest address for use in the host address. */ - tcg_out_ext32u(s, TCG_REG_TMP2, addrlo); + tcg_out_ext32u(s, TCG_REG_TMP2, addr); h->index = TCG_REG_TMP2; } else { - h->index = addrlo; + h->index = addr; } return ldst; } static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + ldst = prepare_host_addr(s, &h, addr, oi, true); if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { if (opc & MO_BSWAP) { @@ -2678,14 +2652,13 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, } static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { if (opc & MO_BSWAP) { @@ -2729,7 +2702,7 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, uint32_t insn; TCGReg index; - ldst = prepare_host_addr(s, &h, addr_reg, -1, oi, is_ld); + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); /* Compose the final address, as LQ/STQ have no indexing. */ index = h.index; @@ -3309,14 +3282,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); + tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; case INDEX_op_qemu_ld_i64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, args[0], -1, args[1], -1, - args[2], TCG_TYPE_I64); + tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I64); } else { - tcg_out_qemu_ld(s, args[0], args[1], args[2], -1, + tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); } break; @@ -3326,14 +3298,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); + tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, args[0], -1, args[1], -1, - args[2], TCG_TYPE_I64); + tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I64); } else { - tcg_out_qemu_st(s, args[0], args[1], args[2], -1, + tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); } break; From 0cd38379a8f47f3bbfb0b0c8419a7de28f8c9b8c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 18:08:19 -0800 Subject: [PATCH 1782/2892] tcg: Replace addr{lo,hi}_reg with addr_reg in TCGLabelQemuLdst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is now always only one guest address register. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 4 ++-- tcg/arm/tcg-target.c.inc | 4 ++-- tcg/i386/tcg-target.c.inc | 4 ++-- tcg/loongarch64/tcg-target.c.inc | 4 ++-- tcg/mips/tcg-target.c.inc | 4 ++-- tcg/ppc/tcg-target.c.inc | 4 ++-- tcg/riscv/tcg-target.c.inc | 4 ++-- tcg/s390x/tcg-target.c.inc | 4 ++-- tcg/sparc64/tcg-target.c.inc | 4 ++-- tcg/tcg.c | 18 +++++++++--------- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 45dc2c649b..6f383c1592 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1775,7 +1775,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; mask_type = (s->page_bits + s->tlb_dyn_max_bits > 32 ? TCG_TYPE_I64 : TCG_TYPE_I32); @@ -1837,7 +1837,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; /* tst addr, #mask */ tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, a_mask); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 93a3ccaf66..83f6eb6099 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1485,7 +1485,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {r0,r1}. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); @@ -1552,7 +1552,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* We are expecting alignment to max out at 7 */ tcg_debug_assert(a_mask <= 0xff); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index b33fe7fe23..cfea4c496d 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2201,7 +2201,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; if (TCG_TARGET_REG_BITS == 64) { ttype = s->addr_type; @@ -2257,7 +2257,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* jne slow_path */ jcc = tcg_out_cmp(s, TCG_COND_TSTNE, addr, a_mask, true, false); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 4f32bf3e97..dd67e8f6bc 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1010,7 +1010,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); @@ -1055,7 +1055,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; /* * Without micro-architecture details, we don't know which of diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 153ce1f3c3..d744b853cd 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1244,7 +1244,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_AREG0, mask_off); @@ -1309,7 +1309,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* We are expecting a_bits to max out at 7, much lower than ANDI. */ tcg_debug_assert(a_bits < 16); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 74b93f4b57..2d16807ec7 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2473,7 +2473,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, mask_off); @@ -2577,7 +2577,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr; + ldst->addr_reg = addr; /* We are expecting a_bits to max out at 7, much lower than ANDI. */ tcg_debug_assert(a_bits < 16); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 55a3398712..689fbea0df 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1727,7 +1727,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; init_setting_vtype(s); @@ -1790,7 +1790,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; init_setting_vtype(s); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 6786e7b316..b2e1cd60ff 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1920,7 +1920,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; tcg_out_sh64(s, RSY_SRLG, TCG_TMP0, addr_reg, TCG_REG_NONE, s->page_bits - CPU_TLB_ENTRY_BITS); @@ -1974,7 +1974,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; tcg_debug_assert(a_mask <= 0xffff); tcg_out_insn(s, RI, TMLL, addr_reg, a_mask); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index ea0a3b8692..527af5665d 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1127,7 +1127,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; ldst->label_ptr[0] = s->code_ptr; /* bne,pn %[xi]cc, label0 */ @@ -1147,7 +1147,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; ldst->label_ptr[0] = s->code_ptr; /* bne,pn %icc, label0 */ diff --git a/tcg/tcg.c b/tcg/tcg.c index fef93b25ff..55cb9b3ac7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -100,8 +100,7 @@ struct TCGLabelQemuLdst { bool is_ld; /* qemu_ld: true, qemu_st: false */ MemOpIdx oi; TCGType type; /* result type of a load */ - TCGReg addrlo_reg; /* reg index for low word of guest virtual addr */ - TCGReg addrhi_reg; /* reg index for high word of guest virtual addr */ + TCGReg addr_reg; /* reg index for guest virtual addr */ TCGReg datalo_reg; /* reg index for low word to be loaded or stored */ TCGReg datahi_reg; /* reg index for high word to be loaded or stored */ const tcg_insn_unit *raddr; /* addr of the next IR of qemu_ld/st IR */ @@ -6061,7 +6060,7 @@ static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, */ tcg_out_helper_add_mov(mov, loc + HOST_BIG_ENDIAN, TCG_TYPE_I32, TCG_TYPE_I32, - ldst->addrlo_reg, -1); + ldst->addr_reg, -1); tcg_out_helper_load_slots(s, 1, mov, parm); tcg_out_helper_load_imm(s, loc[!HOST_BIG_ENDIAN].arg_slot, @@ -6069,7 +6068,7 @@ static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, next_arg += 2; } else { nmov = tcg_out_helper_add_mov(mov, loc, TCG_TYPE_I64, s->addr_type, - ldst->addrlo_reg, ldst->addrhi_reg); + ldst->addr_reg, -1); tcg_out_helper_load_slots(s, nmov, mov, parm); next_arg += nmov; } @@ -6226,21 +6225,22 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, /* Handle addr argument. */ loc = &info->in[next_arg]; - if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + tcg_debug_assert(s->addr_type <= TCG_TYPE_REG); + if (TCG_TARGET_REG_BITS == 32) { /* - * 32-bit host with 32-bit guest: zero-extend the guest address + * 32-bit host (and thus 32-bit guest): zero-extend the guest address * to 64-bits for the helper by storing the low part. Later, * after we have processed the register inputs, we will load a * zero for the high part. */ tcg_out_helper_add_mov(mov, loc + HOST_BIG_ENDIAN, TCG_TYPE_I32, TCG_TYPE_I32, - ldst->addrlo_reg, -1); + ldst->addr_reg, -1); next_arg += 2; nmov += 1; } else { n = tcg_out_helper_add_mov(mov, loc, TCG_TYPE_I64, s->addr_type, - ldst->addrlo_reg, ldst->addrhi_reg); + ldst->addr_reg, -1); next_arg += n; nmov += n; } @@ -6288,7 +6288,7 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, g_assert_not_reached(); } - if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + if (TCG_TARGET_REG_BITS == 32) { /* Zero extend the address by loading a zero for the high part. */ loc = &info->in[1 + !HOST_BIG_ENDIAN]; tcg_out_helper_load_imm(s, loc->arg_slot, TCG_TYPE_I32, 0, parm); From 09ac62682b8d2a8bac36d068f63a31331cc6259a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 18:49:40 -0800 Subject: [PATCH 1783/2892] plugins: Fix qemu_plugin_read_memory_vaddr parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The declaration uses uint64_t for addr. Fixes: 595cd9ce2ec ("plugins: add plugin API to read guest memory") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- plugins/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/api.c b/plugins/api.c index 4110cfaa23..cf8cdf076a 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -561,7 +561,7 @@ GArray *qemu_plugin_get_registers(void) return create_register_handles(regs); } -bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len) +bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len) { g_assert(current_cpu); From 252394c95baddf3d61f95cdd0c4697298de425d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 18:51:30 -0800 Subject: [PATCH 1784/2892] accel/tcg: Fix tlb_set_page_with_attrs, tlb_set_page The declarations use vaddr for size. Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 17e2251695..75d075d044 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1193,7 +1193,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, hwaddr paddr, MemTxAttrs attrs, int prot, - int mmu_idx, uint64_t size) + int mmu_idx, vaddr size) { CPUTLBEntryFull full = { .phys_addr = paddr, @@ -1208,7 +1208,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, void tlb_set_page(CPUState *cpu, vaddr addr, hwaddr paddr, int prot, - int mmu_idx, uint64_t size) + int mmu_idx, vaddr size) { tlb_set_page_with_attrs(cpu, addr, paddr, MEMTXATTRS_UNSPECIFIED, prot, mmu_idx, size); From a0ea8654e525a2bd5c4c197948d3ba179ded90c7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 17 Feb 2025 08:21:36 -0800 Subject: [PATCH 1785/2892] target/loongarch: Use VADDR_PRIx for logging pc_next DisasContextBase.pc_next has type vaddr; use the correct log format. Fixes: 85c19af63e7 ("include/exec: Use vaddr in DisasContextBase for virtual addresses") Signed-off-by: Richard Henderson --- target/loongarch/tcg/insn_trans/trans_atomic.c.inc | 2 +- target/loongarch/tcg/translate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc index 974bc2a70f..3d70d75941 100644 --- a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc @@ -56,7 +56,7 @@ static bool gen_am(DisasContext *ctx, arg_rrr *a, if (a->rd != 0 && (a->rj == a->rd || a->rk == a->rd)) { qemu_log_mask(LOG_GUEST_ERROR, "Warning: source register overlaps destination register" - "in atomic insn at pc=0x" TARGET_FMT_lx "\n", + "in atomic insn at pc=0x%" VADDR_PRIx "\n", ctx->base.pc_next - 4); return false; } diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index 3480f54c71..e59e4ed25b 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -289,7 +289,7 @@ static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) if (!decode(ctx, ctx->opcode)) { qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " - TARGET_FMT_lx ": 0x%x\n", + "0x%" VADDR_PRIx ": 0x%x\n", ctx->base.pc_next, ctx->opcode); generate_exception(ctx, EXCCODE_INE); } From a630055df39e1960275d0e273af036f794b15662 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Feb 2025 08:26:40 -0800 Subject: [PATCH 1786/2892] target/mips: Use VADDR_PRIx for logging pc_next DisasContextBase.pc_next has type vaddr; use the correct log format. Fixes: 85c19af63e7 ("include/exec: Use vaddr in DisasContextBase for virtual addresses") Signed-off-by: Richard Henderson --- target/mips/tcg/octeon_translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index e25c4cbaa0..d9eb43716e 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -18,8 +18,8 @@ static bool trans_BBIT(DisasContext *ctx, arg_BBIT *a) TCGv p; if (ctx->hflags & MIPS_HFLAG_BMASK) { - LOG_DISAS("Branch in delay / forbidden slot at PC 0x" - TARGET_FMT_lx "\n", ctx->base.pc_next); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x%" VADDR_PRIx "\n", + ctx->base.pc_next); generate_exception_end(ctx, EXCP_RI); return true; } From a70af12addd9060fdf8f3dbd42b42e3072c3914f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 18:56:07 -0800 Subject: [PATCH 1787/2892] include/exec: Change vaddr to uintptr_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we no longer support 64-bit guests on 32-bit hosts, we can use a 32-bit type on a 32-bit host. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/vaddr.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/exec/vaddr.h b/include/exec/vaddr.h index b9844afc77..28bec632fb 100644 --- a/include/exec/vaddr.h +++ b/include/exec/vaddr.h @@ -6,13 +6,15 @@ /** * vaddr: * Type wide enough to contain any #target_ulong virtual address. + * We do not support 64-bit guest on 32-host and detect at configure time. + * Therefore, a host pointer width will always fit a guest pointer. */ -typedef uint64_t vaddr; -#define VADDR_PRId PRId64 -#define VADDR_PRIu PRIu64 -#define VADDR_PRIo PRIo64 -#define VADDR_PRIx PRIx64 -#define VADDR_PRIX PRIX64 -#define VADDR_MAX UINT64_MAX +typedef uintptr_t vaddr; +#define VADDR_PRId PRIdPTR +#define VADDR_PRIu PRIuPTR +#define VADDR_PRIo PRIoPTR +#define VADDR_PRIx PRIxPTR +#define VADDR_PRIX PRIXPTR +#define VADDR_MAX UINTPTR_MAX #endif From bf455ec50b6fea15b4d2493059365bf94c706273 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 4 Feb 2025 19:34:51 -0800 Subject: [PATCH 1788/2892] include/exec: Use uintptr_t in CPUTLBEntry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we no longer support 64-bit guests on 32-bit hosts, we can use a 32-bit type on a 32-bit host. This shrinks the size of the structure to 16 bytes on a 32-bit host. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 21 ++++----------------- include/exec/tlb-common.h | 10 +++++----- tcg/arm/tcg-target.c.inc | 1 - tcg/mips/tcg-target.c.inc | 12 +++++------- tcg/ppc/tcg-target.c.inc | 21 +++++---------------- 5 files changed, 19 insertions(+), 46 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 75d075d044..ad158050a1 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -104,22 +104,15 @@ static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry, { /* Do not rearrange the CPUTLBEntry structure members. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) != - MMU_DATA_LOAD * sizeof(uint64_t)); + MMU_DATA_LOAD * sizeof(uintptr_t)); QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) != - MMU_DATA_STORE * sizeof(uint64_t)); + MMU_DATA_STORE * sizeof(uintptr_t)); QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) != - MMU_INST_FETCH * sizeof(uint64_t)); + MMU_INST_FETCH * sizeof(uintptr_t)); -#if TARGET_LONG_BITS == 32 - /* Use qatomic_read, in case of addr_write; only care about low bits. */ - const uint32_t *ptr = (uint32_t *)&entry->addr_idx[access_type]; - ptr += HOST_BIG_ENDIAN; - return qatomic_read(ptr); -#else - const uint64_t *ptr = &entry->addr_idx[access_type]; + const uintptr_t *ptr = &entry->addr_idx[access_type]; /* ofs might correspond to .addr_write, so use qatomic_read */ return qatomic_read(ptr); -#endif } static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry) @@ -899,14 +892,8 @@ static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, addr &= TARGET_PAGE_MASK; addr += tlb_entry->addend; if ((addr - start) < length) { -#if TARGET_LONG_BITS == 32 - uint32_t *ptr_write = (uint32_t *)&tlb_entry->addr_write; - ptr_write += HOST_BIG_ENDIAN; - qatomic_set(ptr_write, *ptr_write | TLB_NOTDIRTY); -#else qatomic_set(&tlb_entry->addr_write, tlb_entry->addr_write | TLB_NOTDIRTY); -#endif } } } diff --git a/include/exec/tlb-common.h b/include/exec/tlb-common.h index dc5a5faa0b..03b5a8ffc7 100644 --- a/include/exec/tlb-common.h +++ b/include/exec/tlb-common.h @@ -19,14 +19,14 @@ #ifndef EXEC_TLB_COMMON_H #define EXEC_TLB_COMMON_H 1 -#define CPU_TLB_ENTRY_BITS 5 +#define CPU_TLB_ENTRY_BITS (HOST_LONG_BITS == 32 ? 4 : 5) /* Minimalized TLB entry for use by TCG fast path. */ typedef union CPUTLBEntry { struct { - uint64_t addr_read; - uint64_t addr_write; - uint64_t addr_code; + uintptr_t addr_read; + uintptr_t addr_write; + uintptr_t addr_code; /* * Addend to virtual address to get host address. IO accesses * use the corresponding iotlb value. @@ -37,7 +37,7 @@ typedef union CPUTLBEntry { * Padding to get a power of two size, as well as index * access to addr_{read,write,code}. */ - uint64_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uint64_t)]; + uintptr_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uintptr_t)]; } CPUTLBEntry; QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 83f6eb6099..cec3d761d4 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1500,7 +1500,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * Add the tlb_table pointer, creating the CPUTLBEntry address in R1. * Load the tlb comparator into R2 and the fast path addend into R1. */ - QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); if (cmp_off == 0) { tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); } else { diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index d744b853cd..14b3cb1eba 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1262,18 +1262,16 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Add the tlb_table pointer, creating the CPUTLBEntry address. */ tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); - if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { - /* Load the (low half) tlb comparator. */ + /* Load the tlb comparator. */ + if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, cmp_off + HOST_BIG_ENDIAN * 4); } else { - tcg_out_ld(s, TCG_TYPE_I64, TCG_TMP0, TCG_TMP3, cmp_off); + tcg_out_ld(s, TCG_TYPE_REG, TCG_TMP0, TCG_TMP3, cmp_off); } - if (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32) { - /* Load the tlb addend for the fast path. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP3, TCG_TMP3, add_off); - } + /* Load the tlb addend for the fast path. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP3, TCG_TMP3, add_off); /* * Mask the page bits, keeping the alignment bits to compare against. diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 2d16807ec7..822925a19b 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2490,27 +2490,16 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); /* - * Load the (low part) TLB comparator into TMP2. + * Load the TLB comparator into TMP2. * For 64-bit host, always load the entire 64-bit slot for simplicity. * We will ignore the high bits with tcg_out_cmp(..., addr_type). */ - if (TCG_TARGET_REG_BITS == 64) { - if (cmp_off == 0) { - tcg_out32(s, LDUX | TAB(TCG_REG_TMP2, - TCG_REG_TMP1, TCG_REG_TMP2)); - } else { - tcg_out32(s, ADD | TAB(TCG_REG_TMP1, - TCG_REG_TMP1, TCG_REG_TMP2)); - tcg_out_ld(s, TCG_TYPE_I64, TCG_REG_TMP2, - TCG_REG_TMP1, cmp_off); - } - } else if (cmp_off == 0 && !HOST_BIG_ENDIAN) { - tcg_out32(s, LWZUX | TAB(TCG_REG_TMP2, - TCG_REG_TMP1, TCG_REG_TMP2)); + if (cmp_off == 0) { + tcg_out32(s, (TCG_TARGET_REG_BITS == 64 ? LDUX : LWZUX) + | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); } else { tcg_out32(s, ADD | TAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP2)); - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, TCG_REG_TMP1, - cmp_off + 4 * HOST_BIG_ENDIAN); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); } /* From 6b8abd244b9355bc840bc14182aae9043f86f2f6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 16:01:38 -0800 Subject: [PATCH 1789/2892] tcg: Introduce the 'z' constraint for a hardware zero register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For loongarch, mips, riscv and sparc, a zero register is available all the time. For aarch64, register index 31 depends on context: sometimes it is the stack pointer, and sometimes it is the zero register. Introduce a new general-purpose constraint which maps 0 to TCG_REG_ZERO, if defined. This differs from existing constant constraints in that const_arg[*] is recorded as false, indicating that the value is in a register. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 +++- include/tcg/tcg.h | 3 ++- tcg/aarch64/tcg-target.h | 2 ++ tcg/loongarch64/tcg-target.h | 2 ++ tcg/mips/tcg-target.h | 2 ++ tcg/riscv/tcg-target.h | 2 ++ tcg/sparc64/tcg-target.h | 3 ++- tcg/tcg.c | 29 ++++++++++++++++++++++------- 8 files changed, 37 insertions(+), 10 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 6608a29376..688984fd39 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -927,7 +927,9 @@ operation uses a constant input constraint which does not allow all constants, it must also accept registers in order to have a fallback. The constraint '``i``' is defined generically to accept any constant. The constraint '``r``' is not defined generically, but is consistently -used by each backend to indicate all registers. +used by each backend to indicate all registers. If ``TCG_REG_ZERO`` +is defined by the backend, the constraint '``z``' is defined generically +to map constant 0 to the hardware zero register. The movi_i32 and movi_i64 operations must accept any constants. diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 1d1d668f52..84d99508b6 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -713,7 +713,8 @@ void tb_target_set_jmp_target(const TranslationBlock *, int, void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); -#define TCG_CT_CONST 1 /* any constant of register size */ +#define TCG_CT_CONST 1 /* any constant of register size */ +#define TCG_CT_REG_ZERO 2 /* zero, in TCG_REG_ZERO */ typedef struct TCGArgConstraint { unsigned ct : 16; diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 0dd6e1f069..3f3df5176d 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -45,6 +45,8 @@ typedef enum { TCG_AREG0 = TCG_REG_X19, } TCGReg; +#define TCG_REG_ZERO TCG_REG_XZR + #define TCG_TARGET_NB_REGS 64 #endif /* AARCH64_TCG_TARGET_H */ diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 8533284631..6a206fb97e 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -85,4 +85,6 @@ typedef enum { TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; +#define TCG_REG_ZERO TCG_REG_ZERO + #endif /* LOONGARCH_TCG_TARGET_H */ diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 3090acc4f5..bd4ca5f852 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -70,4 +70,6 @@ typedef enum { TCG_AREG0 = TCG_REG_S8, } TCGReg; +#define TCG_REG_ZERO TCG_REG_ZERO + #endif diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index db5f3d8b72..6dc77d944b 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -57,4 +57,6 @@ typedef enum { TCG_REG_TMP2 = TCG_REG_T4, } TCGReg; +#define TCG_REG_ZERO TCG_REG_ZERO + #endif diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index f7d75d5806..1b9adccd85 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -64,6 +64,7 @@ typedef enum { TCG_REG_I7, } TCGReg; -#define TCG_AREG0 TCG_REG_I0 +#define TCG_AREG0 TCG_REG_I0 +#define TCG_REG_ZERO TCG_REG_G0 #endif diff --git a/tcg/tcg.c b/tcg/tcg.c index 55cb9b3ac7..e8950df2ad 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3223,6 +3223,11 @@ static void process_constraint_sets(void) case 'i': args_ct[i].ct |= TCG_CT_CONST; break; +#ifdef TCG_REG_ZERO + case 'z': + args_ct[i].ct |= TCG_CT_REG_ZERO; + break; +#endif /* Include all of the target-specific constraints. */ @@ -5074,13 +5079,23 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) arg_ct = &args_ct[i]; ts = arg_temp(arg); - if (ts->val_type == TEMP_VAL_CONST - && tcg_target_const_match(ts->val, arg_ct->ct, ts->type, - op_cond, TCGOP_VECE(op))) { - /* constant is OK for instruction */ - const_args[i] = 1; - new_args[i] = ts->val; - continue; + if (ts->val_type == TEMP_VAL_CONST) { +#ifdef TCG_REG_ZERO + if (ts->val == 0 && (arg_ct->ct & TCG_CT_REG_ZERO)) { + /* Hardware zero register: indicate register via non-const. */ + const_args[i] = 0; + new_args[i] = TCG_REG_ZERO; + continue; + } +#endif + + if (tcg_target_const_match(ts->val, arg_ct->ct, ts->type, + op_cond, TCGOP_VECE(op))) { + /* constant is OK for instruction */ + const_args[i] = 1; + new_args[i] = ts->val; + continue; + } } reg = ts->reg; From 3d5939e57f959a30eaf13a7897e56a2388121cc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 16:15:31 -0800 Subject: [PATCH 1790/2892] tcg/aarch64: Use 'z' constraint Note that 'Z' is still used for addsub2. Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-con-set.h | 12 ++++----- tcg/aarch64/tcg-target.c.inc | 46 ++++++++++++++------------------ 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index 44fcc1206e..1281e5efc0 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -11,27 +11,27 @@ */ C_O0_I1(r) C_O0_I2(r, rC) -C_O0_I2(rZ, r) +C_O0_I2(rz, r) C_O0_I2(w, r) -C_O0_I3(rZ, rZ, r) +C_O0_I3(rz, rz, r) C_O1_I1(r, r) C_O1_I1(w, r) C_O1_I1(w, w) C_O1_I1(w, wr) -C_O1_I2(r, 0, rZ) +C_O1_I2(r, 0, rz) C_O1_I2(r, r, r) C_O1_I2(r, r, rA) C_O1_I2(r, r, rAL) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rL) -C_O1_I2(r, rZ, rZ) +C_O1_I2(r, rz, rz) C_O1_I2(w, 0, w) C_O1_I2(w, w, w) C_O1_I2(w, w, wN) C_O1_I2(w, w, wO) C_O1_I2(w, w, wZ) C_O1_I3(w, w, w, w) -C_O1_I4(r, r, rC, rZ, rZ) +C_O1_I4(r, r, rC, rz, rz) C_O2_I1(r, r, r) -C_O2_I4(r, r, rZ, rZ, rA, rMZ) +C_O2_I4(r, r, rz, rz, rA, rMZ) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 6f383c1592..4645242d85 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2125,10 +2125,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a2 = args[2]; int c2 = const_args[2]; - /* Some operands are defined with "rZ" constraint, a register or - the zero register. These need not actually test args[I] == 0. */ -#define REG0(I) (const_args[I] ? TCG_REG_XZR : (TCGReg)args[I]) - switch (opc) { case INDEX_op_goto_ptr: tcg_out_insn(s, 3207, BR, a0); @@ -2171,18 +2167,18 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_st8_i32: case INDEX_op_st8_i64: - tcg_out_ldst(s, I3312_STRB, REG0(0), a1, a2, 0); + tcg_out_ldst(s, I3312_STRB, a0, a1, a2, 0); break; case INDEX_op_st16_i32: case INDEX_op_st16_i64: - tcg_out_ldst(s, I3312_STRH, REG0(0), a1, a2, 1); + tcg_out_ldst(s, I3312_STRH, a0, a1, a2, 1); break; case INDEX_op_st_i32: case INDEX_op_st32_i64: - tcg_out_ldst(s, I3312_STRW, REG0(0), a1, a2, 2); + tcg_out_ldst(s, I3312_STRW, a0, a1, a2, 2); break; case INDEX_op_st_i64: - tcg_out_ldst(s, I3312_STRX, REG0(0), a1, a2, 3); + tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; case INDEX_op_add_i32: @@ -2395,7 +2391,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, /* FALLTHRU */ case INDEX_op_movcond_i64: tcg_out_cmp(s, ext, args[5], a1, a2, c2); - tcg_out_insn(s, 3506, CSEL, ext, a0, REG0(3), REG0(4), args[5]); + tcg_out_insn(s, 3506, CSEL, ext, a0, args[3], args[4], args[5]); break; case INDEX_op_qemu_ld_i32: @@ -2404,13 +2400,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, break; case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, REG0(0), a1, a2, ext); + tcg_out_qemu_st(s, a0, a1, a2, ext); break; case INDEX_op_qemu_ld_i128: tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], true); break; case INDEX_op_qemu_st_i128: - tcg_out_qemu_ldst_i128(s, REG0(0), REG0(1), a2, args[3], false); + tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; case INDEX_op_bswap64_i64: @@ -2439,7 +2435,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_deposit_i64: case INDEX_op_deposit_i32: - tcg_out_dep(s, ext, a0, REG0(2), args[3], args[4]); + tcg_out_dep(s, ext, a0, a2, args[3], args[4]); break; case INDEX_op_extract_i64: @@ -2459,25 +2455,25 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_extract2_i64: case INDEX_op_extract2_i32: - tcg_out_extr(s, ext, a0, REG0(2), REG0(1), args[3]); + tcg_out_extr(s, ext, a0, a2, a1, args[3]); break; case INDEX_op_add2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), + tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, a2, args[3], (int32_t)args[4], args[5], const_args[4], const_args[5], false); break; case INDEX_op_add2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], + tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, a2, args[3], args[4], args[5], const_args[4], const_args[5], false); break; case INDEX_op_sub2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), + tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, a2, args[3], (int32_t)args[4], args[5], const_args[4], const_args[5], true); break; case INDEX_op_sub2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], + tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, a2, args[3], args[4], args[5], const_args[4], const_args[5], true); break; @@ -2513,8 +2509,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, default: g_assert_not_reached(); } - -#undef REG0 } static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, @@ -3010,7 +3004,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_add_i32: case INDEX_op_add_i64: @@ -3076,7 +3070,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rC, rZ, rZ); + return C_O1_I4(r, r, rC, rz, rz); case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: @@ -3085,23 +3079,23 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O2_I1(r, r, r); case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_qemu_st_i128: - return C_O0_I3(rZ, rZ, r); + return C_O0_I3(rz, rz, r); case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rZ); + return C_O1_I2(r, 0, rz); case INDEX_op_extract2_i32: case INDEX_op_extract2_i64: - return C_O1_I2(r, rZ, rZ); + return C_O1_I2(r, rz, rz); case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rZ, rZ, rA, rMZ); + return C_O2_I4(r, r, rz, rz, rA, rMZ); case INDEX_op_add_vec: case INDEX_op_sub_vec: From 305370e78d31f742d0b5db4809cf00ef3eeea2a2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 17:01:29 -0800 Subject: [PATCH 1791/2892] tcg/loongarch64: Use 'z' constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace target-specific 'Z' with generic 'z'. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target-con-set.h | 15 ++++++------- tcg/loongarch64/tcg-target-con-str.h | 1 - tcg/loongarch64/tcg-target.c.inc | 32 ++++++++++++---------------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index cae6c2aad6..8afaee9476 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -15,8 +15,8 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rZ) +C_O0_I2(rz, r) +C_O0_I2(rz, rz) C_O0_I2(w, r) C_O0_I3(r, r, r) C_O1_I1(r, r) @@ -28,14 +28,13 @@ C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) C_O1_I2(r, r, rU) C_O1_I2(r, r, rW) -C_O1_I2(r, r, rZ) -C_O1_I2(r, 0, rZ) -C_O1_I2(r, rZ, ri) -C_O1_I2(r, rZ, rJ) -C_O1_I2(r, rZ, rZ) +C_O1_I2(r, 0, rz) +C_O1_I2(r, rz, ri) +C_O1_I2(r, rz, rJ) +C_O1_I2(r, rz, rz) C_O1_I2(w, w, w) C_O1_I2(w, w, wM) C_O1_I2(w, w, wA) C_O1_I3(w, w, w, w) -C_O1_I4(r, rZ, rJ, rZ, rZ) +C_O1_I4(r, rz, rJ, rz, rz) C_N2_I1(r, r, r) diff --git a/tcg/loongarch64/tcg-target-con-str.h b/tcg/loongarch64/tcg-target-con-str.h index 2ba9c135ac..99759120b4 100644 --- a/tcg/loongarch64/tcg-target-con-str.h +++ b/tcg/loongarch64/tcg-target-con-str.h @@ -23,7 +23,6 @@ REGS('w', ALL_VECTOR_REGS) CONST('I', TCG_CT_CONST_S12) CONST('J', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U12) -CONST('Z', TCG_CT_CONST_ZERO) CONST('C', TCG_CT_CONST_C12) CONST('W', TCG_CT_CONST_WSZ) CONST('M', TCG_CT_CONST_VCMP) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index dd67e8f6bc..cbd7642b58 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -173,14 +173,13 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define TCG_GUEST_BASE_REG TCG_REG_S1 -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_S12 0x200 -#define TCG_CT_CONST_S32 0x400 -#define TCG_CT_CONST_U12 0x800 -#define TCG_CT_CONST_C12 0x1000 -#define TCG_CT_CONST_WSZ 0x2000 -#define TCG_CT_CONST_VCMP 0x4000 -#define TCG_CT_CONST_VADD 0x8000 +#define TCG_CT_CONST_S12 0x100 +#define TCG_CT_CONST_S32 0x200 +#define TCG_CT_CONST_U12 0x400 +#define TCG_CT_CONST_C12 0x800 +#define TCG_CT_CONST_WSZ 0x1000 +#define TCG_CT_CONST_VCMP 0x2000 +#define TCG_CT_CONST_VADD 0x4000 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -197,9 +196,6 @@ static bool tcg_target_const_match(int64_t val, int ct, if (ct & TCG_CT_CONST) { return true; } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return true; - } if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { return true; } @@ -2229,7 +2225,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_qemu_ld_i128: return C_N2_I1(r, r, r); @@ -2239,7 +2235,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(rZ, rZ); + return C_O0_I2(rz, rz); case INDEX_op_ext8s_i32: case INDEX_op_ext8s_i64: @@ -2332,14 +2328,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: /* Must deposit into the same register as input */ - return C_O1_I2(r, 0, rZ); + return C_O1_I2(r, 0, rz); case INDEX_op_sub_i32: case INDEX_op_setcond_i32: - return C_O1_I2(r, rZ, ri); + return C_O1_I2(r, rz, ri); case INDEX_op_sub_i64: case INDEX_op_setcond_i64: - return C_O1_I2(r, rZ, rJ); + return C_O1_I2(r, rz, rJ); case INDEX_op_mul_i32: case INDEX_op_mul_i64: @@ -2355,11 +2351,11 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: - return C_O1_I2(r, rZ, rZ); + return C_O1_I2(r, rz, rz); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return C_O1_I4(r, rZ, rJ, rZ, rZ); + return C_O1_I4(r, rz, rJ, rz, rz); case INDEX_op_ld_vec: case INDEX_op_dupm_vec: From 065c8f64161b4b1334e4d244dfb4e9d66551e4c2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 17:21:48 -0800 Subject: [PATCH 1792/2892] tcg/mips: Use 'z' constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace target-specific 'Z' with generic 'z'. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target-con-set.h | 26 ++++++++++----------- tcg/mips/tcg-target-con-str.h | 1 - tcg/mips/tcg-target.c.inc | 44 ++++++++++++++--------------------- 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 864034f468..06ab04cc4d 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -10,24 +10,24 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rZ) -C_O0_I3(rZ, r, r) -C_O0_I3(rZ, rZ, r) -C_O0_I4(rZ, rZ, rZ, rZ) -C_O0_I4(rZ, rZ, r, r) +C_O0_I2(rz, r) +C_O0_I2(rz, rz) +C_O0_I3(rz, r, r) +C_O0_I3(rz, rz, r) +C_O0_I4(rz, rz, rz, rz) +C_O0_I4(rz, rz, r, r) C_O1_I1(r, r) -C_O1_I2(r, 0, rZ) +C_O1_I2(r, 0, rz) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) C_O1_I2(r, r, rJ) -C_O1_I2(r, r, rWZ) -C_O1_I2(r, rZ, rN) -C_O1_I2(r, rZ, rZ) -C_O1_I4(r, rZ, rZ, rZ, 0) -C_O1_I4(r, rZ, rZ, rZ, rZ) +C_O1_I2(r, r, rzW) +C_O1_I2(r, rz, rN) +C_O1_I2(r, rz, rz) +C_O1_I4(r, rz, rz, rz, 0) +C_O1_I4(r, rz, rz, rz, rz) C_O2_I1(r, r, r) C_O2_I2(r, r, r, r) -C_O2_I4(r, r, rZ, rZ, rN, rN) +C_O2_I4(r, r, rz, rz, rN, rN) diff --git a/tcg/mips/tcg-target-con-str.h b/tcg/mips/tcg-target-con-str.h index 413c280a7a..dfe2b156df 100644 --- a/tcg/mips/tcg-target-con-str.h +++ b/tcg/mips/tcg-target-con-str.h @@ -19,4 +19,3 @@ CONST('J', TCG_CT_CONST_S16) CONST('K', TCG_CT_CONST_P2M1) CONST('N', TCG_CT_CONST_N16) CONST('W', TCG_CT_CONST_WSZ) -CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 14b3cb1eba..f8c105ba37 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -184,12 +184,11 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, g_assert_not_reached(); } -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_U16 0x200 /* Unsigned 16-bit: 0 - 0xffff. */ -#define TCG_CT_CONST_S16 0x400 /* Signed 16-bit: -32768 - 32767 */ -#define TCG_CT_CONST_P2M1 0x800 /* Power of 2 minus 1. */ -#define TCG_CT_CONST_N16 0x1000 /* "Negatable" 16-bit: -32767 - 32767 */ -#define TCG_CT_CONST_WSZ 0x2000 /* word size */ +#define TCG_CT_CONST_U16 0x100 /* Unsigned 16-bit: 0 - 0xffff. */ +#define TCG_CT_CONST_S16 0x200 /* Signed 16-bit: -32768 - 32767 */ +#define TCG_CT_CONST_P2M1 0x400 /* Power of 2 minus 1. */ +#define TCG_CT_CONST_N16 0x800 /* "Negatable" 16-bit: -32767 - 32767 */ +#define TCG_CT_CONST_WSZ 0x1000 /* word size */ #define ALL_GENERAL_REGS 0xffffffffu @@ -204,8 +203,6 @@ static bool tcg_target_const_match(int64_t val, int ct, { if (ct & TCG_CT_CONST) { return 1; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { return 1; } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { @@ -1666,11 +1663,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a0, a1, a2; int c2; - /* - * Note that many operands use the constraint set "rZ". - * We make use of the fact that 0 is the ZERO register, - * and hence such cases need not check for const_args. - */ a0 = args[0]; a1 = args[1]; a2 = args[2]; @@ -2181,14 +2173,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_add_i32: case INDEX_op_add_i64: return C_O1_I2(r, r, rJ); case INDEX_op_sub_i32: case INDEX_op_sub_i64: - return C_O1_I2(r, rZ, rN); + return C_O1_I2(r, rz, rN); case INDEX_op_mul_i32: case INDEX_op_mulsh_i32: case INDEX_op_muluh_i32: @@ -2207,7 +2199,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_remu_i64: case INDEX_op_nor_i64: case INDEX_op_setcond_i64: - return C_O1_I2(r, rZ, rZ); + return C_O1_I2(r, rz, rz); case INDEX_op_muls2_i32: case INDEX_op_mulu2_i32: case INDEX_op_muls2_i64: @@ -2234,35 +2226,35 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O1_I2(r, r, ri); case INDEX_op_clz_i32: case INDEX_op_clz_i64: - return C_O1_I2(r, r, rWZ); + return C_O1_I2(r, r, rzW); case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rZ); + return C_O1_I2(r, 0, rz); case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(rZ, rZ); + return C_O0_I2(rz, rz); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return (use_mips32r6_instructions - ? C_O1_I4(r, rZ, rZ, rZ, rZ) - : C_O1_I4(r, rZ, rZ, rZ, 0)); + ? C_O1_I4(r, rz, rz, rz, rz) + : C_O1_I4(r, rz, rz, rz, 0)); case INDEX_op_add2_i32: case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rZ, rZ, rN, rN); + return C_O2_I4(r, r, rz, rz, rN, rN); case INDEX_op_setcond2_i32: - return C_O1_I4(r, rZ, rZ, rZ, rZ); + return C_O1_I4(r, rz, rz, rz, rz); case INDEX_op_brcond2_i32: - return C_O0_I4(rZ, rZ, rZ, rZ); + return C_O0_I4(rz, rz, rz, rz); case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_st_i32: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_qemu_ld_i64: return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); case INDEX_op_qemu_st_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) : C_O0_I3(rZ, rZ, r); + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rz, r) : C_O0_I3(rz, rz, r); default: return C_NotImplemented; From f466db1e27131e58d1cfac0d7ce2eb5b28ed22a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 17:26:28 -0800 Subject: [PATCH 1793/2892] tcg/riscv: Use 'z' constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace target-specific 'Z' with generic 'z'. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target-con-set.h | 10 +++++----- tcg/riscv/tcg-target-con-str.h | 1 - tcg/riscv/tcg-target.c.inc | 28 ++++++++++++---------------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index 3c4ef44eb0..e92e815491 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -10,17 +10,17 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rZ) +C_O0_I2(rz, r) +C_O0_I2(rz, rz) C_O1_I1(r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) -C_O1_I2(r, rZ, rN) -C_O1_I2(r, rZ, rZ) +C_O1_I2(r, rz, rN) +C_O1_I2(r, rz, rz) C_N1_I2(r, r, rM) C_O1_I4(r, r, rI, rM, rM) -C_O2_I4(r, r, rZ, rZ, rM, rM) +C_O2_I4(r, r, rz, rz, rM, rM) C_O0_I2(v, r) C_O1_I1(v, r) C_O1_I1(v, v) diff --git a/tcg/riscv/tcg-target-con-str.h b/tcg/riscv/tcg-target-con-str.h index 089efe96ca..2f9700638c 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -21,4 +21,3 @@ CONST('K', TCG_CT_CONST_S5) CONST('L', TCG_CT_CONST_CMP_VI) CONST('N', TCG_CT_CONST_N12) CONST('M', TCG_CT_CONST_M12) -CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 689fbea0df..f7e1ca5a56 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -112,13 +112,12 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) return TCG_REG_A0 + slot; } -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_S12 0x200 -#define TCG_CT_CONST_N12 0x400 -#define TCG_CT_CONST_M12 0x800 -#define TCG_CT_CONST_J12 0x1000 -#define TCG_CT_CONST_S5 0x2000 -#define TCG_CT_CONST_CMP_VI 0x4000 +#define TCG_CT_CONST_S12 0x100 +#define TCG_CT_CONST_N12 0x200 +#define TCG_CT_CONST_M12 0x400 +#define TCG_CT_CONST_J12 0x800 +#define TCG_CT_CONST_S5 0x1000 +#define TCG_CT_CONST_CMP_VI 0x2000 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -391,9 +390,6 @@ static bool tcg_target_const_match(int64_t val, int ct, if (ct & TCG_CT_CONST) { return 1; } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } if (type >= TCG_TYPE_V64) { /* Val is replicated by VECE; extract the highest element. */ val >>= (-8 << vece) & 63; @@ -2681,7 +2677,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_add_i32: case INDEX_op_and_i32: @@ -2707,7 +2703,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_sub_i64: - return C_O1_I2(r, rZ, rN); + return C_O1_I2(r, rz, rN); case INDEX_op_mul_i32: case INDEX_op_mulsh_i32: @@ -2723,7 +2719,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: - return C_O1_I2(r, rZ, rZ); + return C_O1_I2(r, rz, rz); case INDEX_op_shl_i32: case INDEX_op_shr_i32: @@ -2745,7 +2741,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(rZ, rZ); + return C_O0_I2(rz, rz); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: @@ -2755,14 +2751,14 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add2_i64: case INDEX_op_sub2_i32: case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rZ, rZ, rM, rM); + return C_O2_I4(r, r, rz, rz, rM, rM); case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_st_vec: return C_O0_I2(v, r); From 1bbcae5adaad2d8f026194002f54913be5ee0933 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 18:11:20 -0800 Subject: [PATCH 1794/2892] tcg/sparc64: Use 'z' constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace target-specific 'Z' with generic 'z'. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target-con-set.h | 12 ++++++------ tcg/sparc64/tcg-target-con-str.h | 1 - tcg/sparc64/tcg-target.c.inc | 17 +++++++---------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 434bf25072..61f9fa3d9f 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -10,11 +10,11 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rJ) +C_O0_I2(rz, r) +C_O0_I2(rz, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) -C_O1_I2(r, rZ, rJ) -C_O1_I4(r, rZ, rJ, rI, 0) -C_O2_I2(r, r, rZ, rJ) -C_O2_I4(r, r, rZ, rZ, rJ, rJ) +C_O1_I2(r, rz, rJ) +C_O1_I4(r, rz, rJ, rI, 0) +C_O2_I2(r, r, rz, rJ) +C_O2_I4(r, r, rz, rz, rJ, rJ) diff --git a/tcg/sparc64/tcg-target-con-str.h b/tcg/sparc64/tcg-target-con-str.h index 0577ec4942..2f033b3ac2 100644 --- a/tcg/sparc64/tcg-target-con-str.h +++ b/tcg/sparc64/tcg-target-con-str.h @@ -16,4 +16,3 @@ REGS('r', ALL_GENERAL_REGS) */ CONST('I', TCG_CT_CONST_S11) CONST('J', TCG_CT_CONST_S13) -CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 527af5665d..7c722f59a8 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -76,7 +76,6 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #define TCG_CT_CONST_S11 0x100 #define TCG_CT_CONST_S13 0x200 -#define TCG_CT_CONST_ZERO 0x400 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) @@ -340,9 +339,7 @@ static bool tcg_target_const_match(int64_t val, int ct, val = (int32_t)val; } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) { + if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) { return 1; } else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13)) { return 1; @@ -1579,7 +1576,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: - return C_O0_I2(rZ, r); + return C_O0_I2(rz, r); case INDEX_op_add_i32: case INDEX_op_add_i64: @@ -1611,22 +1608,22 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: case INDEX_op_negsetcond_i32: case INDEX_op_negsetcond_i64: - return C_O1_I2(r, rZ, rJ); + return C_O1_I2(r, rz, rJ); case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(rZ, rJ); + return C_O0_I2(rz, rJ); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return C_O1_I4(r, rZ, rJ, rI, 0); + return C_O1_I4(r, rz, rJ, rI, 0); case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rZ, rZ, rJ, rJ); + return C_O2_I4(r, r, rz, rz, rJ, rJ); case INDEX_op_mulu2_i32: case INDEX_op_muls2_i32: - return C_O2_I2(r, r, rZ, rJ); + return C_O2_I2(r, r, rz, rJ); case INDEX_op_muluh_i64: return C_O1_I2(r, r, r); From 4b7b20a3b72c5000ea71bef505c16e6e628268b6 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Thu, 13 Feb 2025 11:35:58 -0300 Subject: [PATCH 1795/2892] elfload: Fix alignment when unmapping excess reservation When complying with the alignment requested in the ELF and unmapping the excess reservation, having align_end not aligned to the guest page causes the unmap to be rejected by the alignment check at target_munmap and later brk adjustments hit an EEXIST. Fix by aligning the start of region to be unmapped. Fixes: c81d1fafa6 ("linux-user: Honor elf alignment when placing images") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1913 Signed-off-by: Fabiano Rosas [rth: Align load_end as well.] Signed-off-by: Richard Henderson Message-ID: <20250213143558.10504-1-farosas@suse.de> --- linux-user/elfload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a2c152e5ad..8799e4ea27 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3351,8 +3351,8 @@ static void load_elf_image(const char *image_name, const ImageSource *src, if (align_size != reserve_size) { abi_ulong align_addr = ROUND_UP(load_addr, align); - abi_ulong align_end = align_addr + reserve_size; - abi_ulong load_end = load_addr + align_size; + abi_ulong align_end = TARGET_PAGE_ALIGN(align_addr + reserve_size); + abi_ulong load_end = TARGET_PAGE_ALIGN(load_addr + align_size); if (align_addr != load_addr) { target_munmap(load_addr, align_addr - load_addr); From 513823e7521a09ed7ad1e32e6454bac3b2cbf52d Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Mon, 10 Feb 2025 13:59:34 +0100 Subject: [PATCH 1796/2892] linux-user: Move TARGET_SA_RESTORER out of generic/signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SA_RESTORER and the associated sa_restorer field of struct sigaction are an obsolete feature, not expected to be used by future architectures. They are also absent on RISC-V, LoongArch, Hexagon and OpenRISC, but defined due to their use of generic/signal.h. This leads to corrupted data and out-of-bounds accesses. Move the definition of TARGET_SA_RESTORER out of generic/signal.h into the target_signal.h files that need it. Note that m68k has the sa_restorer field, but does not use it and does not define SA_RESTORER. Reported-by: Thomas Weißschuh Signed-off-by: Andreas Schwab Reviewed-by: Thomas Weißschuh Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: --- linux-user/aarch64/target_signal.h | 2 ++ linux-user/arm/target_signal.h | 2 ++ linux-user/generic/signal.h | 1 - linux-user/i386/target_signal.h | 2 ++ linux-user/m68k/target_signal.h | 1 + linux-user/microblaze/target_signal.h | 2 ++ linux-user/ppc/target_signal.h | 2 ++ linux-user/s390x/target_signal.h | 2 ++ linux-user/sh4/target_signal.h | 2 ++ linux-user/x86_64/target_signal.h | 2 ++ linux-user/xtensa/target_signal.h | 2 ++ 11 files changed, 19 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index 40e399d990..6f66a50bfd 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */ #define TARGET_SEGV_MTESERR 9 /* Synchronous ARM MTE exception */ diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index 0e6351d9f7..ff1810b1fe 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/generic/signal.h b/linux-user/generic/signal.h index 6fd05b77bb..b34740258e 100644 --- a/linux-user/generic/signal.h +++ b/linux-user/generic/signal.h @@ -15,7 +15,6 @@ #define TARGET_SA_RESTART 0x10000000 #define TARGET_SA_NODEFER 0x40000000 #define TARGET_SA_RESETHAND 0x80000000 -#define TARGET_SA_RESTORER 0x04000000 #define TARGET_SIGHUP 1 #define TARGET_SIGINT 2 diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index 9315cba241..eee792ef63 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index 6e0f4b74e3..b05b9303b0 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -3,6 +3,7 @@ #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SA_RESTORER 1 #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 7dc5c45f00..ffe4442213 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index 5be24e152b..53fae473f3 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #if !defined(TARGET_PPC64) #define TARGET_ARCH_HAS_SETUP_FRAME #endif diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index 41e0e34a55..738e0673f4 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index eee6a1a7cd..0bde417fd1 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index 9d9717406f..0af100c661 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + /* For x86_64, use of SA_RESTORER is mandatory. */ #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index e4b1bea5cb..8a198bf8ac 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 #endif From 807c3ebd1e3fc2a1be6cdfc702ccea3fa0d2d9b2 Mon Sep 17 00:00:00 2001 From: Mikael Szreder Date: Wed, 5 Feb 2025 10:03:32 +0100 Subject: [PATCH 1797/2892] target/sparc: Fix register selection for all F*TOx and FxTO* instructions A bug was introduced in commit 0bba7572d40d which causes the fdtox and fqtox instructions to incorrectly select the destination registers. More information and a test program can be found in issue #2802. Cc: qemu-stable@nongnu.org Fixes: 0bba7572d40d ("target/sparc: Perform DFPREG/QFPREG in decodetree") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2802 Signed-off-by: Mikael Szreder Acked-by: Artyom Tarasenko [rth: Squash patches together, since the second fixes a typo in the first.] Signed-off-by: Richard Henderson Message-ID: <20250205090333.19626-3-git@miszr.win> --- target/sparc/insns.decode | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 989c20b44a..cfcdf6690e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -321,12 +321,12 @@ FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @q_d_d FNHADDs 10 ..... 110100 ..... 0 0111 0001 ..... @r_r_r FNHADDd 10 ..... 110100 ..... 0 0111 0010 ..... @d_d_d FNsMULd 10 ..... 110100 ..... 0 0111 1001 ..... @d_r_r -FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 -FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_d2 -FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_q2 -FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 -FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_r2 -FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_r2 +FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @d_r2 +FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @d_d2 +FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @d_q2 +FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_d2 +FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_d2 +FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_d2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_d2 FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_q2 From 7a74e468089a58756b438d31a2a9a97f183780d7 Mon Sep 17 00:00:00 2001 From: Mikael Szreder Date: Fri, 14 Feb 2025 08:03:43 +0100 Subject: [PATCH 1798/2892] target/sparc: Fix gdbstub incorrectly handling registers f32-f62 The gdbstub implementation for the Sparc architecture would incorrectly calculate the the floating point register offset. This resulted in, for example, registers f32 and f34 to point to the same value. The issue was caused by the confusion between even register numbers and even register indexes. For example, the register index of f32 is 64 and f34 is 65. Cc: qemu-stable@nongnu.org Fixes: 30038fd81808 ("target-sparc: Change fpr representation to doubles.") Signed-off-by: Mikael Szreder Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250214070343.11501-1-git@miszr.win> --- target/sparc/gdbstub.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index ec0036e9ef..134617fb23 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -79,8 +79,13 @@ int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) } } if (n < 80) { - /* f32-f62 (double width, even numbers only) */ - return gdb_get_reg64(mem_buf, env->fpr[(n - 32) / 2].ll); + /* f32-f62 (16 double width registers, even register numbers only) + * n == 64: f32 : env->fpr[16] + * n == 65: f34 : env->fpr[17] + * etc... + * n == 79: f62 : env->fpr[31] + */ + return gdb_get_reg64(mem_buf, env->fpr[(n - 64) + 16].ll); } switch (n) { case 80: @@ -173,8 +178,13 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } return 4; } else if (n < 80) { - /* f32-f62 (double width, even numbers only) */ - env->fpr[(n - 32) / 2].ll = tmp; + /* f32-f62 (16 double width registers, even register numbers only) + * n == 64: f32 : env->fpr[16] + * n == 65: f34 : env->fpr[17] + * etc... + * n == 79: f62 : env->fpr[31] + */ + env->fpr[(n - 64) + 16].ll = tmp; } else { switch (n) { case 80: From 172e7644f336dd82cb8d56cfb964477731f34f43 Mon Sep 17 00:00:00 2001 From: Artyom Tarasenko Date: Sun, 9 Feb 2025 22:12:48 +0100 Subject: [PATCH 1799/2892] target/sparc: fake UltraSPARC T1 PCR and PIC registers Fake access to PCR Performance Control Register and PIC Performance Instrumentation Counter. Ignore writes in privileged mode, and return 0 on reads. This allows booting Tribblix, MilaX and v9os under Niagara target. Signed-off-by: Artyom Tarasenko Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250209211248.50383-1-atar4qemu@gmail.com> --- target/sparc/insns.decode | 7 ++++++- target/sparc/translate.c | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index cfcdf6690e..9e39d23273 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -96,7 +96,10 @@ CALL 01 i:s30 RDTICK 10 rd:5 101000 00100 0 0000000000000 RDPC 10 rd:5 101000 00101 0 0000000000000 RDFPRS 10 rd:5 101000 00110 0 0000000000000 - RDASR17 10 rd:5 101000 10001 0 0000000000000 + { + RDASR17 10 rd:5 101000 10001 0 0000000000000 + RDPIC 10 rd:5 101000 10001 0 0000000000000 + } RDGSR 10 rd:5 101000 10011 0 0000000000000 RDSOFTINT 10 rd:5 101000 10110 0 0000000000000 RDTICK_CMPR 10 rd:5 101000 10111 0 0000000000000 @@ -114,6 +117,8 @@ CALL 01 i:s30 WRCCR 10 00010 110000 ..... . ............. @n_r_ri WRASI 10 00011 110000 ..... . ............. @n_r_ri WRFPRS 10 00110 110000 ..... . ............. @n_r_ri + WRPCR 10 10000 110000 01000 0 0000000000000 + WRPIC 10 10001 110000 01000 0 0000000000000 { WRGSR 10 10011 110000 ..... . ............. @n_r_ri WRPOWERDOWN 10 10011 110000 ..... . ............. @n_r_ri diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7e5c7351cb..bfe63649db 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2882,6 +2882,14 @@ static TCGv do_rd_leon3_config(DisasContext *dc, TCGv dst) TRANS(RDASR17, ASR17, do_rd_special, true, a->rd, do_rd_leon3_config) +static TCGv do_rdpic(DisasContext *dc, TCGv dst) +{ + return tcg_constant_tl(0); +} + +TRANS(RDPIC, HYPV, do_rd_special, supervisor(dc), a->rd, do_rdpic) + + static TCGv do_rdccr(DisasContext *dc, TCGv dst) { gen_helper_rdccr(dst, tcg_env); @@ -3315,6 +3323,17 @@ static void do_wrfprs(DisasContext *dc, TCGv src) TRANS(WRFPRS, 64, do_wr_special, a, true, do_wrfprs) +static bool do_priv_nop(DisasContext *dc, bool priv) +{ + if (!priv) { + return raise_priv(dc); + } + return advance_pc(dc); +} + +TRANS(WRPCR, HYPV, do_priv_nop, supervisor(dc)) +TRANS(WRPIC, HYPV, do_priv_nop, supervisor(dc)) + static void do_wrgsr(DisasContext *dc, TCGv src) { gen_trap_ifnofpu(dc); From 175aa36668d6e91157d5e5b092b441f96f46b05e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 10:08:20 -0800 Subject: [PATCH 1800/2892] tcg/i386: Use tcg_{high,unsigned}_cond in tcg_out_brcond2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate code repetition by using the appropriate helpers. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 65 +++++---------------------------------- 1 file changed, 8 insertions(+), 57 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index cfea4c496d..33d303a123 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1658,6 +1658,7 @@ static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3], label_this, small); break; + case TCG_COND_NE: case TCG_COND_TSTNE: tcg_out_brcond(s, 0, cond, args[0], args[2], const_args[2], @@ -1665,64 +1666,14 @@ static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3], label_this, small); break; - case TCG_COND_LT: - tcg_out_brcond(s, 0, TCG_COND_LT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LE: - tcg_out_brcond(s, 0, TCG_COND_LT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GT: - tcg_out_brcond(s, 0, TCG_COND_GT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GE: - tcg_out_brcond(s, 0, TCG_COND_GT, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LTU: - tcg_out_brcond(s, 0, TCG_COND_LTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LEU: - tcg_out_brcond(s, 0, TCG_COND_LTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GTU: - tcg_out_brcond(s, 0, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GEU: - tcg_out_brcond(s, 0, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); - tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); - break; + default: - g_assert_not_reached(); + tcg_out_brcond(s, 0, tcg_high_cond(cond), args[1], + args[3], const_args[3], label_this, small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond(s, 0, tcg_unsigned_cond(cond), args[0], + args[2], const_args[2], label_this, small); + break; } tcg_out_label(s, label_next); } From e726f65867087d86436de05e9f372a86ec1381a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 15:01:43 -0800 Subject: [PATCH 1801/2892] tcg: Remove TCG_TARGET_HAS_{br,set}cond2 from riscv and loongarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These defines never should have been added as they were never used. Only 32-bit hosts may have these opcodes and they have them unconditionally. Fixes: 6cb14e4de29 ("tcg/loongarch64: Add the tcg-target.h file") Fixes: fb1f70f3685 ("tcg/riscv: Add the tcg-target.h file") Acked-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/riscv/tcg-target-has.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index ac88522eef..188b00799f 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -37,8 +37,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_brcond2 0 -#define TCG_TARGET_HAS_setcond2 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index f35f9b31f5..98081084f2 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -37,8 +37,6 @@ #define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_brcond2 1 -#define TCG_TARGET_HAS_setcond2 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_negsetcond_i64 1 From b819fd6994243aee6f9613edbbacedce4f511c32 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:22:56 +0000 Subject: [PATCH 1802/2892] target/arm: Report correct syndrome for UNDEFINED CNTPS_*_EL1 from EL2 and NS EL1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The access pseudocode for the CNTPS_TVAL_EL1, CNTPS_CTL_EL1 and CNTPS_CVAL_EL1 secure timer registers says that they are UNDEFINED from EL2 or NS EL1. We incorrectly return CP_ACCESS_TRAP from the access function in these cases, which means that we report the wrong syndrome value to the target EL. Use CP_ACCESS_TRAP_UNCATEGORIZED, which reports the correct syndrome value for an UNDEFINED instruction. Cc: qemu-stable@nongnu.org Fixes: b4d3978c2fd ("target-arm: Add the AArch64 view of the Secure physical timer") Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-2-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 7d95eae997..b7d6afe0a1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2385,7 +2385,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, switch (arm_current_el(env)) { case 1: if (!arm_is_secure(env)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_UNCATEGORIZED; } if (!(env->cp15.scr_el3 & SCR_ST)) { return CP_ACCESS_TRAP_EL3; @@ -2393,7 +2393,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, return CP_ACCESS_OK; case 0: case 2: - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_UNCATEGORIZED; case 3: return CP_ACCESS_OK; default: From 1960d9701ef7ed8d24e98def767bbf05d63e6992 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:22:57 +0000 Subject: [PATCH 1803/2892] target/arm: Report correct syndrome for UNDEFINED AT ops with wrong NSE, NS R_NYXTL says that these AT insns should be UNDEFINED if they would operate on an EL lower than EL3 and SCR_EL3.{NSE,NS} is set to the Reserved {1, 0}. We were incorrectly reporting them with the wrong syndrome; use CP_ACCESS_TRAP_UNCATEGORIZED so they are reported as UNDEFINED. Cc: qemu-stable@nongnu.org Fixes: 1acd00ef1410 ("target/arm/helper: Check SCR_EL3.{NSE, NS} encoding for AT instructions") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-3-peter.maydell@linaro.org --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index b7d6afe0a1..9ed1a67b76 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3601,7 +3601,7 @@ static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, * scr_write() ensures that the NSE bit is not set otherwise. */ if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_UNCATEGORIZED; } return CP_ACCESS_OK; } From ccda792945d650bce4609c8dbce8814a220df1bb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:22:58 +0000 Subject: [PATCH 1804/2892] target/arm: Report correct syndrome for UNDEFINED S1E2 AT ops at EL3 The pseudocode for AT S1E2R and AT S1E2W says that they should be UNDEFINED if executed at EL3 when EL2 is not enabled. We were incorrectly using CP_ACCESS_TRAP and reporting the wrong exception syndrome as a result. Use CP_ACCESS_TRAP_UNCATEGORIZED. Cc: qemu-stable@nongnu.org Fixes: 2a47df953202e1 ("target-arm: Wire up AArch64 EL2 and EL3 address translation ops") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-4-peter.maydell@linaro.org --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 9ed1a67b76..f4af2d2de5 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3611,7 +3611,7 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, { if (arm_current_el(env) == 3 && !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_UNCATEGORIZED; } return at_e012_access(env, ri, isread); } From 707d478ed8f2da6f2327e5af780890c1fd9c371a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:22:59 +0000 Subject: [PATCH 1805/2892] target/arm: Report correct syndrome for UNDEFINED LOR sysregs when NS=0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pseudocode for the accessors for the LOR sysregs says they are UNDEFINED if SCR_EL3.NS is 0. We were reporting the wrong syndrome value here; use CP_ACCESS_TRAP_UNCATEGORIZED. Cc: qemu-stable@nongnu.org Fixes: 2d7137c10faf ("target/arm: Implement the ARMv8.1-LOR extension") Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-5-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 f4af2d2de5..4a0db087dd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6750,8 +6750,8 @@ static CPAccessResult access_lor_other(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_is_secure_below_el3(env)) { - /* Access denied in secure mode. */ - return CP_ACCESS_TRAP; + /* UNDEF if SCR_EL3.NS == 0 */ + return CP_ACCESS_TRAP_UNCATEGORIZED; } return access_lor_ns(env, ri, isread); } From 4cf4948651615181c5bc3d0e4a9f5c46be576bb2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:00 +0000 Subject: [PATCH 1806/2892] target/arm: Make CP_ACCESS_TRAPs to AArch32 EL3 be Monitor traps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In system register access pseudocode the common pattern for AArch32 registers with access traps to EL3 is: at EL1 and EL2: if HaveEL(EL3) && !ELUsingAArch32(EL3) && (SCR_EL3.TERR == 1) then AArch64.AArch32SystemAccessTrap(EL3, 0x03); elsif HaveEL(EL3) && ELUsingAArch32(EL3) && (SCR.TERR == 1) then AArch32.TakeMonitorTrapException(); at EL3: if (PSTATE.M != M32_Monitor) && (SCR.TERR == 1) then AArch32.TakeMonitorTrapException(); (taking as an example the ERRIDR access pseudocode). This implements the behaviour of (in this case) SCR.TERR that "Accesses to the specified registers from modes other than Monitor mode generate a Monitor Trap exception" and of SCR_EL3.TERR that "Accesses of the specified Error Record registers at EL2 and EL1 are trapped to EL3, unless the instruction generates a higher priority exception". In QEMU we don't implement this pattern correctly in two ways: * in access_check_cp_reg() we turn the CP_ACCESS_TRAP_EL3 into an UNDEF, not a trap to Monitor mode * in the access functions, we check trap bits like SCR.TERR only when arm_current_el(env) < 3 -- this is correct for AArch64 EL3, but misses the "trap non-Monitor-mode execution at EL3 into Monitor mode" case for AArch32 EL3 In this commit we fix the first of these two issues, by making access_check_cp_reg() handle CP_ACCESS_TRAP_EL3 as a Monitor trap. This is a kind of exception that we haven't yet implemented(!), so we need a new EXCP_MON_TRAP for it. This diverges from the pseudocode approach, where every access check function explicitly checks for "if EL3 is AArch32" and takes a monitor trap; if we wanted to be closer to the pseudocode we could add a new CP_ACCESS_TRAP_MONITOR and make all the accessfns use it when appropriate. But because there are no non-standard cases in the pseudocode (i.e. where either it raises a Monitor trap that doesn't correspond to an AArch64 SystemAccessTrap or where it raises a SystemAccessTrap that doesn't correspond to a Monitor trap), handling this all in one place seems less likely to result in future bugs where we forgot again about this special case when writing an accessor. (The cc of stable here is because "hw/intc/arm_gicv3_cpuif: Don't downgrade monitor traps for AArch32 EL3" which is also cc:stable will implicitly use the new EXCP_MON_TRAP code path.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-6-peter.maydell@linaro.org --- target/arm/cpu.h | 1 + target/arm/helper.c | 11 +++++++++++ target/arm/tcg/op_helper.c | 13 ++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6f6cf5c888..83ceaa58c2 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -62,6 +62,7 @@ #define EXCP_NMI 26 #define EXCP_VINMI 27 #define EXCP_VFNMI 28 +#define EXCP_MON_TRAP 29 /* AArch32 trap to Monitor mode */ /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 diff --git a/target/arm/helper.c b/target/arm/helper.c index 4a0db087dd..2bf39a2051 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9684,6 +9684,7 @@ void arm_log_exception(CPUState *cs) [EXCP_NMI] = "NMI", [EXCP_VINMI] = "Virtual IRQ NMI", [EXCP_VFNMI] = "Virtual FIQ NMI", + [EXCP_MON_TRAP] = "Monitor Trap", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { @@ -10250,6 +10251,16 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) mask = CPSR_A | CPSR_I | CPSR_F; offset = 0; break; + case EXCP_MON_TRAP: + new_mode = ARM_CPU_MODE_MON; + addr = 0x04; + mask = CPSR_A | CPSR_I | CPSR_F; + if (env->thumb) { + offset = 2; + } else { + offset = 4; + } + break; default: cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); return; /* Never happens. Keep compiler happy. */ diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 1161d301b7..1ba727e8e9 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -758,6 +758,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, key); CPAccessResult res = CP_ACCESS_OK; int target_el; + uint32_t excp; assert(ri != NULL); @@ -851,8 +852,18 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, } fail: + excp = EXCP_UDEF; switch (res & ~CP_ACCESS_EL_MASK) { case CP_ACCESS_TRAP: + /* + * If EL3 is AArch32 then there's no syndrome register; the cases + * where we would raise a SystemAccessTrap to AArch64 EL3 all become + * raising a Monitor trap exception. (Because there's no visible + * syndrome it doesn't matter what we pass to raise_exception().) + */ + if ((res & CP_ACCESS_EL_MASK) == 3 && !arm_el_is_aa64(env, 3)) { + excp = EXCP_MON_TRAP; + } break; case CP_ACCESS_TRAP_UNCATEGORIZED: /* Only CP_ACCESS_TRAP traps are direct to a specified EL */ @@ -888,7 +899,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, g_assert_not_reached(); } - raise_exception(env, EXCP_UDEF, syndrome, target_el); + raise_exception(env, excp, syndrome, target_el); } const void *HELPER(lookup_cp_reg)(CPUARMState *env, uint32_t key) From d04c6c3c000ab3e588a2b91641310aeea89408f7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:01 +0000 Subject: [PATCH 1807/2892] hw/intc/arm_gicv3_cpuif: Don't downgrade monitor traps for AArch32 EL3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the gicv3_{irq,fiq,irqfiq}_access() functions, there is a check which downgrades a CP_ACCESS_TRAP_EL3 to CP_ACCESS_TRAP if EL3 is not AArch64. This has been there since the GIC was first implemented, but it isn't right: if we are trapping because of SCR.IRQ or SCR.FIQ then we definitely want to be going to EL3 (doing AArch32.TakeMonitorTrapException() in pseudocode terms). We might want to not take a trap at all, but we don't ever want to go to the default target EL, because that would mean, for instance, taking a trap to Hyp mode if the trapped access was made from Hyp mode. (This might have been an attempt to work around our failure to properly implement Monitor Traps.) Remove the bogus check. Cc: qemu-stable@nongnu.org Fixes: 359fbe65e01e ("hw/intc/arm_gicv3: Implement GICv3 CPU interface registers") Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-7-peter.maydell@linaro.org --- hw/intc/arm_gicv3_cpuif.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 9cad8313a3..8a715b3510 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -2300,9 +2300,6 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, } } - if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { - r = CP_ACCESS_TRAP; - } return r; } @@ -2365,9 +2362,6 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env, } } - if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { - r = CP_ACCESS_TRAP; - } return r; } @@ -2404,9 +2398,6 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, } } - if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { - r = CP_ACCESS_TRAP; - } return r; } From 4d436fb05c2a1fff7befc815ebcbb04a14977448 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:02 +0000 Subject: [PATCH 1808/2892] target/arm: Honour SDCR.TDCC and SCR.TERR in AArch32 EL3 non-Monitor modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are not many traps in AArch32 which should trap to Monitor mode, but these trap bits should trap not just lower ELs to Monitor mode but also the non-Monitor modes running at EL3 (i.e. Secure System, Secure Undef, etc). We get this wrong because the relevant access functions implement the AArch64-style logic of if (el < 3 && trap_bit_set) { return CP_ACCESS_TRAP_EL3; } which won't trap the non-Monitor modes at EL3. Correct this error by using arm_is_el3_or_mon() instead, which returns true when the CPU is at AArch64 EL3 or AArch32 Monitor mode. (Since the new callsites are compiled also for the linux-user mode, we need to provide a dummy implementation for CONFIG_USER_ONLY.) This affects only: * trapping of ERRIDR via SCR.TERR * trapping of the debug channel registers via SDCR.TDCC * trapping of GICv3 registers via SCR.IRQ and SCR.FIQ (which we already used arm_is_el3_or_mon() for) This patch changes the handling of SCR.TERR and SDCR.TDCC. This patch only changes guest-visible behaviour for "-cpu max" on the qemu-system-arm binary, because SCR.TERR and SDCR.TDCC (and indeed the entire SDCR register) only arrived in Armv8, and the only guest CPU we support which has any v8 features and also starts in AArch32 EL3 is the 32-bit 'max'. Other uses of CP_ACCESS_TRAP_EL3 don't need changing: * uses in code paths that can't happen when EL3 is AArch32: access_trap_aa32s_el1, cpacr_access, cptr_access, nsacr_access * uses which are in accessfns for AArch64-only registers: gt_stimer_access, gt_cntpoff_access, access_hxen, access_tpidr2, access_smpri, access_smprimap, access_lor_ns, access_pauth, access_mte, access_tfsr_el2, access_scxtnum, access_fgt * trap bits which exist only in the AArch64 version of the trap register, not the AArch32 one: access_tpm, pmreg_access, access_dbgvcr32, access_tdra, access_tda, access_tdosa (TPM, TDA and TDOSA exist only in MDCR_EL3, not in SDCR, and we enforce this in sdcr_write()) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-8-peter.maydell@linaro.org --- target/arm/cpu.h | 5 +++++ target/arm/debug_helper.c | 3 ++- target/arm/helper.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 83ceaa58c2..215845c7e2 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2596,6 +2596,11 @@ static inline bool arm_is_secure_below_el3(CPUARMState *env) return false; } +static inline bool arm_is_el3_or_mon(CPUARMState *env) +{ + return false; +} + static inline ARMSecuritySpace arm_security_space(CPUARMState *env) { return ARMSS_NonSecure; diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 2212ef4a3b..c3c1eb5f62 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -880,7 +880,8 @@ static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, if (el < 2 && (mdcr_el2_tda || mdcr_el2_tdcc)) { return CP_ACCESS_TRAP_EL2; } - if (el < 3 && ((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) { + if (!arm_is_el3_or_mon(env) && + ((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) { return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_OK; diff --git a/target/arm/helper.c b/target/arm/helper.c index 2bf39a2051..535870f69a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6103,7 +6103,7 @@ static CPAccessResult access_terr(CPUARMState *env, const ARMCPRegInfo *ri, if (el < 2 && (arm_hcr_el2_eff(env) & HCR_TERR)) { return CP_ACCESS_TRAP_EL2; } - if (el < 3 && (env->cp15.scr_el3 & SCR_TERR)) { + if (!arm_is_el3_or_mon(env) && (env->cp15.scr_el3 & SCR_TERR)) { return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_OK; From ff8b906a00494601687763446e470ff9b3580be7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:03 +0000 Subject: [PATCH 1809/2892] hw/intc/arm_gicv3_cpuif(): Remove redundant tests of is_a64() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the gicv3_{irq,fiq,irqfiq}_access() functions, in the arm_current_el(env) == 3 case we do the following test: if (!is_a64(env) && !arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } In this check, the "!is_a64(env)" is redundant, because if we are at EL3 and in AArch64 then arm_is_el3_or_mon() will return true and we will skip the if() body anyway. Remove the unnecessary tests. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-9-peter.maydell@linaro.org --- hw/intc/arm_gicv3_cpuif.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 8a715b3510..7f1d071c19 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -2291,7 +2291,7 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, r = CP_ACCESS_TRAP_EL3; break; case 3: - if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + if (!arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } break; @@ -2353,7 +2353,7 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env, r = CP_ACCESS_TRAP_EL3; break; case 3: - if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + if (!arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } break; @@ -2389,7 +2389,7 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, r = CP_ACCESS_TRAP_EL3; break; case 3: - if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + if (!arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } break; From 273d0e84ccd1f0a94f893d2f1ab750f812dfa219 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:04 +0000 Subject: [PATCH 1810/2892] target/arm: Support CP_ACCESS_TRAP_EL1 as a CPAccessResult In the CPAccessResult enum, the CP_ACCESS_TRAP* values indicate the equivalent of the pseudocode AArch64.SystemAccessTrap(..., 0x18), causing a trap to a specified exception level with a syndrome value giving information about the failing instructions. In the pseudocode, such traps are always taken to a specified target EL. We support that for target EL of 2 or 3 via CP_ACCESS_TRAP_EL2 and CP_ACCESS_TRAP_EL3, but the only way to take the access trap to EL1 currently is to use CP_ACCESS_TRAP, which takes the trap to the "usual target EL" (EL1 if in EL0, otherwise to the current EL). Add CP_ACCESS_TRAP_EL1 so that access functions can follow the pseudocode more closely. (Note that for the common case in the pseudocode of "trap to EL2 if HCR_EL2.TGE is set, otherwise trap to EL1", we handle this in raise_exception(), so access functions don't need to special case it and can use CP_ACCESS_TRAP_EL1.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-10-peter.maydell@linaro.org --- target/arm/cpregs.h | 1 + target/arm/tcg/op_helper.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 1759d9defb..fbf5798069 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -331,6 +331,7 @@ typedef enum CPAccessResult { * 0xc or 0x18). */ CP_ACCESS_TRAP = (1 << 2), + CP_ACCESS_TRAP_EL1 = CP_ACCESS_TRAP | 1, CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP | 2, CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP | 3, diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 1ba727e8e9..c427118655 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -781,7 +781,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, * the other trap takes priority. So we take the "check HSTR_EL2" path * for all of those cases.) */ - if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) == 0) && + if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) < 2) && arm_current_el(env) == 0) { goto fail; } @@ -887,6 +887,9 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, case 0: target_el = exception_target_el(env); break; + case 1: + assert(arm_current_el(env) < 2); + break; case 2: assert(arm_current_el(env) != 3); assert(arm_is_el2_enabled(env)); @@ -895,7 +898,6 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, assert(arm_feature(env, ARM_FEATURE_EL3)); break; default: - /* No "direct" traps to EL1 */ g_assert_not_reached(); } From 2d60f1acdb950e85335b018bcaf4ba0f042a350c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:05 +0000 Subject: [PATCH 1811/2892] target/arm: Use CP_ACCESS_TRAP_EL1 for traps that are always to EL1 We currently use CP_ACCESS_TRAP in a number of access functions where we know we're currently at EL0; in this case the "usual target EL" is EL1, so CP_ACCESS_TRAP and CP_ACCESS_TRAP_EL1 behave the same. Use CP_ACCESS_TRAP_EL1 to more closely match the pseudocode for this sort of check. Note that in the case of the access functions foc cacheop to PoC or PoU, the code was correct but the comment was wrong: SCTLR_EL1.UCI traps for DC CVAC, DC CIVAC, DC CVAP, DC CVADP, DC CVAU and IC IVAU should be system access traps, not UNDEFs. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-11-peter.maydell@linaro.org --- target/arm/debug_helper.c | 2 +- target/arm/helper.c | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index c3c1eb5f62..36bffde74e 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -875,7 +875,7 @@ static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, (env->cp15.mdcr_el3 & MDCR_TDCC); if (el < 1 && mdscr_el1_tdcc) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (el < 2 && (mdcr_el2_tda || mdcr_el2_tdcc)) { return CP_ACCESS_TRAP_EL2; diff --git a/target/arm/helper.c b/target/arm/helper.c index 535870f69a..aacb53d31a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -881,7 +881,7 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (el < 2 && (mdcr_el2 & MDCR_TPM)) { return CP_ACCESS_TRAP_EL2; @@ -2159,7 +2159,7 @@ static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 0 && (env->teecr & 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } return teecr_access(env, ri, isread); } @@ -2239,7 +2239,7 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, cntkctl = env->cp15.c14_cntkctl; } if (!extract32(cntkctl, 0, 2)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } break; case 1: @@ -2278,7 +2278,7 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, /* CNT[PV]CT: not visible from PL0 if EL0[PV]CTEN is zero */ if (!extract32(env->cp15.c14_cntkctl, timeridx, 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } /* fall through */ case 1: @@ -2319,7 +2319,7 @@ static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx, * EL0 if EL0[PV]TEN is zero. */ if (!extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } /* fall through */ @@ -4499,7 +4499,7 @@ static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 0 && !(arm_sctlr(env, 0) & SCTLR_UMA)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } return CP_ACCESS_OK; } @@ -4589,9 +4589,9 @@ static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env, /* Cache invalidate/clean to Point of Coherency or Persistence... */ switch (arm_current_el(env)) { case 0: - /* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */ + /* ... EL0 must trap to EL1 unless SCTLR_EL1.UCI is set. */ if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } /* fall through */ case 1: @@ -4609,9 +4609,9 @@ static CPAccessResult do_cacheop_pou_access(CPUARMState *env, uint64_t hcrflags) /* Cache invalidate/clean to Point of Unification... */ switch (arm_current_el(env)) { case 0: - /* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */ + /* ... EL0 must trap to EL1 unless SCTLR_EL1.UCI is set. */ if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } /* fall through */ case 1: @@ -4651,7 +4651,7 @@ static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, } } else { if (!(env->cp15.sctlr_el[1] & SCTLR_DZE)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (hcr & HCR_TDZ) { return CP_ACCESS_TRAP_EL2; @@ -6073,7 +6073,7 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, } } else { if (!(env->cp15.sctlr_el[1] & SCTLR_UCT)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (hcr & HCR_TID2) { return CP_ACCESS_TRAP_EL2; @@ -6372,7 +6372,7 @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, if (el == 0) { uint64_t sctlr = arm_sctlr(env, el); if (!(sctlr & SCTLR_EnTP2)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } } /* TODO: FEAT_FGT */ @@ -7172,7 +7172,7 @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri, if (hcr & HCR_TGE) { return CP_ACCESS_TRAP_EL2; } - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } } else if (el < 2 && (env->cp15.sctlr_el[2] & SCTLR_TSCXT)) { return CP_ACCESS_TRAP_EL2; @@ -7292,7 +7292,7 @@ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, if (el == 0) { uint64_t sctlr = arm_sctlr(env, el); if (!(sctlr & SCTLR_EnRCTX)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } } else if (el == 1) { uint64_t hcr = arm_hcr_el2_eff(env); From f706b67da61aecc54bbdad16bea3fc69e9fd844b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:06 +0000 Subject: [PATCH 1812/2892] target/arm: Use TRAP_UNCATEGORIZED for XScale CPAR traps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On XScale CPUs, there is no EL2 or AArch64, so no syndrome register. These traps are just UNDEFs in the traditional AArch32 sense, so CP_ACCESS_TRAP_UNCATEGORIZED is more accurate than CP_ACCESS_TRAP. This has no visible behavioural change, because the guest doesn't have a way to see the syndrome value we generate. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-12-peter.maydell@linaro.org --- target/arm/tcg/op_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index c427118655..c69d2ac643 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -764,7 +764,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { - res = CP_ACCESS_TRAP; + res = CP_ACCESS_TRAP_UNCATEGORIZED; goto fail; } From fc0ea471ec26cdc5639809c4ea4b70a80567f432 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:07 +0000 Subject: [PATCH 1813/2892] target/arm: Remove CP_ACCESS_TRAP handling There are no longer any uses of CP_ACCESS_TRAP in access functions, because we have converted them all to use either CP_ACCESS_TRAP_EL1 or CP_ACCESS_TRAP_UNCATEGORIZED, as appropriate. Remove the handling of bare CP_ACCESS_TRAP from the access_check_cp_reg() helper, so that it now asserts if an access function returns a value requesting a trap without a target EL. Rename CP_ACCESS_TRAP to CP_ACCESS_TRAP_BIT, to make it clearer that this is an internal-only definition, not something that it makes sense to return from an access function. This should help to avoid future bugs where we return the wrong syndrome value by mistake. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-13-peter.maydell@linaro.org --- target/arm/cpregs.h | 11 ++++++----- target/arm/tcg/op_helper.c | 13 ++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index fbf5798069..fb3b84baa1 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -328,12 +328,13 @@ typedef enum CPAccessResult { * Access fails due to a configurable trap or enable which would * result in a categorized exception syndrome giving information about * the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6, - * 0xc or 0x18). + * 0xc or 0x18). These traps are always to a specified target EL, + * never to the usual target EL. */ - CP_ACCESS_TRAP = (1 << 2), - CP_ACCESS_TRAP_EL1 = CP_ACCESS_TRAP | 1, - CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP | 2, - CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP | 3, + CP_ACCESS_TRAP_BIT = (1 << 2), + CP_ACCESS_TRAP_EL1 = CP_ACCESS_TRAP_BIT | 1, + CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP_BIT | 2, + CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP_BIT | 3, /* * Access fails and results in an exception syndrome 0x0 ("uncategorized"). diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index c69d2ac643..fcee11e29a 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -853,21 +853,24 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, fail: excp = EXCP_UDEF; - switch (res & ~CP_ACCESS_EL_MASK) { - case CP_ACCESS_TRAP: + switch (res) { + /* CP_ACCESS_TRAP* traps are always direct to a specified EL */ + case CP_ACCESS_TRAP_EL3: /* * If EL3 is AArch32 then there's no syndrome register; the cases * where we would raise a SystemAccessTrap to AArch64 EL3 all become * raising a Monitor trap exception. (Because there's no visible * syndrome it doesn't matter what we pass to raise_exception().) */ - if ((res & CP_ACCESS_EL_MASK) == 3 && !arm_el_is_aa64(env, 3)) { + if (!arm_el_is_aa64(env, 3)) { excp = EXCP_MON_TRAP; } break; + case CP_ACCESS_TRAP_EL2: + case CP_ACCESS_TRAP_EL1: + break; case CP_ACCESS_TRAP_UNCATEGORIZED: - /* Only CP_ACCESS_TRAP traps are direct to a specified EL */ - assert((res & CP_ACCESS_EL_MASK) == 0); + /* CP_ACCESS_TRAP_UNCATEGORIZED is never direct to a specified EL */ if (cpu_isar_feature(aa64_ids, cpu) && isread && arm_cpreg_in_idspace(ri)) { /* From 86d44c215e91da43555a2cfd58b7c6b725f036fb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:08 +0000 Subject: [PATCH 1814/2892] target/arm: Rename CP_ACCESS_TRAP_UNCATEGORIZED to CP_ACCESS_UNDEFINED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CP_ACCESS_TRAP_UNCATEGORIZED is technically an accurate description of what this return value from a cpreg accessfn does, but it's liable to confusion because it doesn't match how the Arm ARM pseudocode indicates this case. What it does is an EXCP_UDEF with a zero ("uncategorized") syndrome value, which is what an UNDEFINED instruction does. The pseudocode uses "UNDEFINED" to show this; rename our constant to CP_ACCESS_UNDEFINED to make the parallel clearer. Commit created with sed -i -e 's/CP_ACCESS_TRAP_UNCATEGORIZED/CP_ACCESS_UNDEFINED/' $(git grep -l CP_ACCESS_TRAP_UNCATEGORIZED) plus manual editing of the comment. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-14-peter.maydell@linaro.org --- target/arm/cpregs.h | 5 +++-- target/arm/helper.c | 30 +++++++++++++++--------------- target/arm/tcg/op_helper.c | 6 +++--- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index fb3b84baa1..52377c6eb5 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -337,13 +337,14 @@ typedef enum CPAccessResult { CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP_BIT | 3, /* - * Access fails and results in an exception syndrome 0x0 ("uncategorized"). + * Access fails with UNDEFINED, i.e. an exception syndrome 0x0 + * ("uncategorized"), which is what an undefined insn produces. * Note that this is not a catch-all case -- the set of cases which may * result in this failure is specifically defined by the architecture. * This trap is always to the usual target EL, never directly to a * specified target EL. */ - CP_ACCESS_TRAP_UNCATEGORIZED = (2 << 2), + CP_ACCESS_UNDEFINED = (2 << 2), } CPAccessResult; /* Indexes into fgt_read[] */ diff --git a/target/arm/helper.c b/target/arm/helper.c index aacb53d31a..71dead7241 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -285,7 +285,7 @@ static CPAccessResult access_el3_aa32ns(CPUARMState *env, { if (!is_a64(env) && arm_current_el(env) == 3 && arm_is_secure_below_el3(env)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; } @@ -310,7 +310,7 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_TRAP_EL3; } /* This will be EL1 NS and EL2 NS, which just UNDEF */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } /* @@ -2246,7 +2246,7 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, if (!isread && ri->state == ARM_CP_STATE_AA32 && arm_is_secure_below_el3(env)) { /* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } break; case 2: @@ -2255,7 +2255,7 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, } if (!isread && el < arm_highest_el(env)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; @@ -2385,7 +2385,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, switch (arm_current_el(env)) { case 1: if (!arm_is_secure(env)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } if (!(env->cp15.scr_el3 & SCR_ST)) { return CP_ACCESS_TRAP_EL3; @@ -2393,7 +2393,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, return CP_ACCESS_OK; case 0: case 2: - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; case 3: return CP_ACCESS_OK; default: @@ -3304,7 +3304,7 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, } return CP_ACCESS_TRAP_EL3; } - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } } return CP_ACCESS_OK; @@ -3601,7 +3601,7 @@ static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, * scr_write() ensures that the NSE bit is not set otherwise. */ if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; } @@ -3611,7 +3611,7 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, { if (arm_current_el(env) == 3 && !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return at_e012_access(env, ri, isread); } @@ -4684,7 +4684,7 @@ static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, * Access to SP_EL0 is undefined if it's being used as * the stack pointer. */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; } @@ -5674,7 +5674,7 @@ static CPAccessResult sel2_access(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_current_el(env) == 3 || arm_is_secure_below_el3(env)) { return CP_ACCESS_OK; } - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } static const ARMCPRegInfo el2_sec_cp_reginfo[] = { @@ -5710,7 +5710,7 @@ static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, if (isread) { return CP_ACCESS_OK; } - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } static const ARMCPRegInfo el3_cp_reginfo[] = { @@ -5798,7 +5798,7 @@ static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; } @@ -5896,7 +5896,7 @@ static CPAccessResult el2_e2h_e12_access(CPUARMState *env, } /* FOO_EL12 aliases only exist when E2H is 1; otherwise they UNDEF */ if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } if (ri->orig_accessfn) { return ri->orig_accessfn(env, ri->opaque, isread); @@ -6751,7 +6751,7 @@ static CPAccessResult access_lor_other(CPUARMState *env, { if (arm_is_secure_below_el3(env)) { /* UNDEF if SCR_EL3.NS == 0 */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return access_lor_ns(env, ri, isread); } diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index fcee11e29a..2230351a8f 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -764,7 +764,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { - res = CP_ACCESS_TRAP_UNCATEGORIZED; + res = CP_ACCESS_UNDEFINED; goto fail; } @@ -869,8 +869,8 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, case CP_ACCESS_TRAP_EL2: case CP_ACCESS_TRAP_EL1: break; - case CP_ACCESS_TRAP_UNCATEGORIZED: - /* CP_ACCESS_TRAP_UNCATEGORIZED is never direct to a specified EL */ + case CP_ACCESS_UNDEFINED: + /* CP_ACCESS_UNDEFINED is never direct to a specified EL */ if (cpu_isar_feature(aa64_ids, cpu) && isread && arm_cpreg_in_idspace(ri)) { /* From 2b95a2d01b04afadf510a49ac14b38a59be8c5f5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 30 Jan 2025 18:23:09 +0000 Subject: [PATCH 1815/2892] target/arm: Correct errors in WFI/WFE trapping The code for WFI/WFE trapping has several errors: * it wasn't using arm_sctlr(), so it would look at SCTLR_EL1 even if the CPU was in the EL2&0 translation regime * it was raising UNDEF, not Monitor Trap, for traps to AArch32 EL3 because of SCR.{TWE,TWI} * it was not honouring SCR.{TWE,TWI} when running in AArch32 at EL3 not in Monitor mode * it checked SCR.{TWE,TWI} even on v7 CPUs which don't have those bits Fix these bugs. Cc: qemu-stable@nongnu.org Fixes: b1eced713d99 ("target-arm: Add WFx instruction trap support") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250130182309.717346-15-peter.maydell@linaro.org --- target/arm/tcg/op_helper.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 2230351a8f..02c375d196 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -313,15 +313,19 @@ void HELPER(check_bxj_trap)(CPUARMState *env, uint32_t rm) } #ifndef CONFIG_USER_ONLY -/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. +/* + * Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. * The function returns the target EL (1-3) if the instruction is to be trapped; * otherwise it returns 0 indicating it is not trapped. + * For a trap, *excp is updated with the EXCP_* trap type to use. */ -static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) +static inline int check_wfx_trap(CPUARMState *env, bool is_wfe, uint32_t *excp) { int cur_el = arm_current_el(env); uint64_t mask; + *excp = EXCP_UDEF; + if (arm_feature(env, ARM_FEATURE_M)) { /* M profile cores can never trap WFI/WFE. */ return 0; @@ -331,18 +335,9 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) * WFx instructions being trapped to EL1. These trap bits don't exist in v7. */ if (cur_el < 1 && arm_feature(env, ARM_FEATURE_V8)) { - int target_el; - mask = is_wfe ? SCTLR_nTWE : SCTLR_nTWI; - if (arm_is_secure_below_el3(env) && !arm_el_is_aa64(env, 3)) { - /* Secure EL0 and Secure PL1 is at EL3 */ - target_el = 3; - } else { - target_el = 1; - } - - if (!(env->cp15.sctlr_el[target_el] & mask)) { - return target_el; + if (!(arm_sctlr(env, cur_el) & mask)) { + return exception_target_el(env); } } @@ -358,9 +353,12 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) } /* We are not trapping to EL1 or EL2; trap to EL3 if SCR_EL3 requires it */ - if (cur_el < 3) { + if (arm_feature(env, ARM_FEATURE_V8) && !arm_is_el3_or_mon(env)) { mask = (is_wfe) ? SCR_TWE : SCR_TWI; if (env->cp15.scr_el3 & mask) { + if (!arm_el_is_aa64(env, 3)) { + *excp = EXCP_MON_TRAP; + } return 3; } } @@ -383,7 +381,8 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) return; #else CPUState *cs = env_cpu(env); - int target_el = check_wfx_trap(env, false); + uint32_t excp; + int target_el = check_wfx_trap(env, false, &excp); if (cpu_has_work(cs)) { /* Don't bother to go into our "low power state" if @@ -399,7 +398,7 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) env->regs[15] -= insn_len; } - raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2), + raise_exception(env, excp, syn_wfx(1, 0xe, 0, insn_len == 2), target_el); } @@ -424,7 +423,8 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) #else ARMCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - int target_el = check_wfx_trap(env, false); + uint32_t excp; + int target_el = check_wfx_trap(env, false, &excp); /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ uint64_t cntval = gt_get_countervalue(env); uint64_t offset = gt_virt_cnt_offset(env); @@ -441,8 +441,7 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) if (target_el) { env->pc -= 4; - raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, false), - target_el); + raise_exception(env, excp, syn_wfx(1, 0xe, 0, false), target_el); } if (uadd64_overflow(timeout, offset, &nexttick)) { From 4ac4d6e77613ccbb9aa55675429dc0b9ae1f8ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:26 +0100 Subject: [PATCH 1816/2892] hw/arm/exynos4210: Replace magic 32 by proper 'GIC_INTERNAL' definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 32 IRQ lines skipped are the GIC internal ones. Use the GIC_INTERNAL definition for clarity. No logical change. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-2-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/exynos4210.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index dd0edc81d5..b6537a2d64 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -394,7 +394,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s) } if (irq_id) { qdev_connect_gpio_out(splitter, splitin, - qdev_get_gpio_in(extgicdev, irq_id - 32)); + qdev_get_gpio_in(extgicdev, + irq_id - GIC_INTERNAL)); } } for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { @@ -421,7 +422,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s) s->irq_table[n] = qdev_get_gpio_in(splitter, 0); qdev_connect_gpio_out(splitter, 0, qdev_get_gpio_in(intcdev, n)); qdev_connect_gpio_out(splitter, 1, - qdev_get_gpio_in(extgicdev, irq_id - 32)); + qdev_get_gpio_in(extgicdev, + irq_id - GIC_INTERNAL)); } else { s->irq_table[n] = qdev_get_gpio_in(intcdev, n); } From 284e354566c31687cce260401549a616cf513c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:27 +0100 Subject: [PATCH 1817/2892] hw/arm/exynos4210: Specify explicitly the GIC has 64 external IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When not specified, Cortex-A9MP configures its GIC with 64 external IRQs (see commit a32134aad89 "arm:make the number of GIC interrupts configurable"). Add the GIC_EXT_IRQS definition (with a comment) to make that explicit. Except explicitly setting a property value to its same implicit value, there is no logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-3-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/exynos4210.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index b6537a2d64..b452470598 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -103,6 +103,8 @@ #define EXYNOS4210_PL330_BASE1_ADDR 0x12690000 #define EXYNOS4210_PL330_BASE2_ADDR 0x12850000 +#define GIC_EXT_IRQS 64 /* FIXME: verify for this SoC */ + enum ExtGicId { EXT_GIC_ID_MDMA_LCD0 = 66, EXT_GIC_ID_PDMA0, @@ -588,6 +590,8 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) /* Private memory region and Internal GIC */ qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-cpu", EXYNOS4210_NCPUS); + qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-irq", + GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(&s->a9mpcore); sysbus_realize(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); From 6621cf52509b796f3c32a18b74248cf404dbe56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:28 +0100 Subject: [PATCH 1818/2892] hw/arm/realview: Specify explicitly the GIC has 64 external IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When not specified, Cortex-A9MP configures its GIC with 64 external IRQs (see commit a32134aad89 "arm:make the number of GIC interrupts configurable"). Add the GIC_EXT_IRQS definition (with a comment) to make that explicit. Except explicitly setting a property value to its same implicit value, there is no logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-4-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/realview.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 436eef816e..008eeaf049 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -35,6 +35,8 @@ #define SMP_BOOT_ADDR 0xe0000000 #define SMP_BOOTREG_ADDR 0x10000030 +#define GIC_EXT_IRQS 64 /* Realview PBX-A9 development board */ + /* Board init. */ static struct arm_boot_info realview_binfo = { @@ -185,7 +187,12 @@ static void realview_init(MachineState *machine, sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); if (is_mpcore) { - dev = qdev_new(is_pb ? TYPE_A9MPCORE_PRIV : "realview_mpcore"); + if (is_pb) { + dev = qdev_new(TYPE_A9MPCORE_PRIV); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); + } else { + dev = qdev_new("realview_mpcore"); + } qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); @@ -201,7 +208,7 @@ static void realview_init(MachineState *machine, /* For now just create the nIRQ GIC, and ignore the others. */ dev = sysbus_create_simple(TYPE_REALVIEW_GIC, gic_addr, cpu_irq[0]); } - for (n = 0; n < 64; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } From 2d269a8bbb7fc7148f429b13720c7b5d07bba3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:29 +0100 Subject: [PATCH 1819/2892] hw/arm/xilinx_zynq: Replace IRQ_OFFSET -> GIC_INTERNAL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have a definition to distinct GIC internal IRQs versus external ones, use it. No logical changes. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-5-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 3c6a4604cc..22c0bb1726 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -54,8 +54,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) #define FLASH_SIZE (64 * 1024 * 1024) #define FLASH_SECTOR_SIZE (128 * 1024) -#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ - #define MPCORE_PERIPHBASE 0xF8F00000 #define ZYNQ_BOARD_MIDR 0x413FC090 @@ -281,12 +279,12 @@ static void zynq_init(MachineState *machine) pic[n] = qdev_get_gpio_in(dev, n); } - n = zynq_init_spi_flashes(0xE0006000, pic[58 - IRQ_OFFSET], false, 0); - n = zynq_init_spi_flashes(0xE0007000, pic[81 - IRQ_OFFSET], false, n); - n = zynq_init_spi_flashes(0xE000D000, pic[51 - IRQ_OFFSET], true, n); + n = zynq_init_spi_flashes(0xE0006000, pic[58 - GIC_INTERNAL], false, 0); + n = zynq_init_spi_flashes(0xE0007000, pic[81 - GIC_INTERNAL], false, n); + n = zynq_init_spi_flashes(0xE000D000, pic[51 - GIC_INTERNAL], true, n); - sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]); - sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]); + sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - GIC_INTERNAL]); + sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - GIC_INTERNAL]); dev = qdev_new(TYPE_CADENCE_UART); busdev = SYS_BUS_DEVICE(dev); @@ -295,7 +293,7 @@ static void zynq_init(MachineState *machine) qdev_get_clock_out(slcr, "uart0_ref_clk")); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0xE0000000); - sysbus_connect_irq(busdev, 0, pic[59 - IRQ_OFFSET]); + sysbus_connect_irq(busdev, 0, pic[59 - GIC_INTERNAL]); dev = qdev_new(TYPE_CADENCE_UART); busdev = SYS_BUS_DEVICE(dev); qdev_prop_set_chr(dev, "chardev", serial_hd(1)); @@ -303,15 +301,15 @@ static void zynq_init(MachineState *machine) qdev_get_clock_out(slcr, "uart1_ref_clk")); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0xE0001000); - sysbus_connect_irq(busdev, 0, pic[82 - IRQ_OFFSET]); + sysbus_connect_irq(busdev, 0, pic[82 - GIC_INTERNAL]); sysbus_create_varargs("cadence_ttc", 0xF8001000, - pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); + pic[42-GIC_INTERNAL], pic[43-GIC_INTERNAL], pic[44-GIC_INTERNAL], NULL); sysbus_create_varargs("cadence_ttc", 0xF8002000, - pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL); + pic[69-GIC_INTERNAL], pic[70-GIC_INTERNAL], pic[71-GIC_INTERNAL], NULL); - gem_init(0xE000B000, pic[54 - IRQ_OFFSET]); - gem_init(0xE000C000, pic[77 - IRQ_OFFSET]); + gem_init(0xE000B000, pic[54 - GIC_INTERNAL]); + gem_init(0xE000C000, pic[77 - GIC_INTERNAL]); for (n = 0; n < 2; n++) { int hci_irq = n ? 79 : 56; @@ -330,7 +328,7 @@ static void zynq_init(MachineState *machine) qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - GIC_INTERNAL]); di = drive_get(IF_SD, 0, n); blk = di ? blk_by_legacy_dinfo(di) : NULL; @@ -343,7 +341,7 @@ static void zynq_init(MachineState *machine) dev = qdev_new(TYPE_ZYNQ_XADC); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-GIC_INTERNAL]); dev = qdev_new("pl330"); object_property_set_link(OBJECT(dev), "memory", @@ -363,15 +361,15 @@ static void zynq_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0xF8003000); - sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */ + sysbus_connect_irq(busdev, 0, pic[45-GIC_INTERNAL]); /* abort irq line */ for (n = 0; n < ARRAY_SIZE(dma_irqs); ++n) { /* event irqs */ - sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); + sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - GIC_INTERNAL]); } dev = qdev_new("xlnx.ps7-dev-cfg"); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]); + sysbus_connect_irq(busdev, 0, pic[40 - GIC_INTERNAL]); sysbus_mmio_map(busdev, 0, 0xF8007000); /* From 92fea7f2e7818d3019b5c29eb8379049a3b1f0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:30 +0100 Subject: [PATCH 1820/2892] hw/arm/xilinx_zynq: Specify explicitly the GIC has 64 external IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looking at the Zynq 7000 SoC Technical Reference Manual (UG585 v1.14) on Appendix A: Register Details, the mpcore Interrupt Controller Type Register (ICDICTR) has the IT_Lines_Number field read-only with value 0x2, described as: IT_Lines_Number b00010 = the distributor provides 96 interrupts, 64 external interrupt lines. Add a GIC_EXT_IRQS definition (with a comment) to make the number of GIC external IRQs explicit. Except explicitly setting a property value to its same implicit value, there is no logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-6-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 22c0bb1726..b8916665ed 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -57,6 +57,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) #define MPCORE_PERIPHBASE 0xF8F00000 #define ZYNQ_BOARD_MIDR 0x413FC090 +#define GIC_EXT_IRQS 64 /* Zynq 7000 SoC */ + static const int dma_irqs[8] = { 46, 47, 48, 49, 72, 73, 74, 75 }; @@ -205,7 +207,7 @@ static void zynq_init(MachineState *machine) MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); DeviceState *dev, *slcr; SysBusDevice *busdev; - qemu_irq pic[64]; + qemu_irq pic[GIC_EXT_IRQS]; int n; unsigned int smp_cpus = machine->smp.cpus; @@ -261,6 +263,7 @@ static void zynq_init(MachineState *machine) dev = qdev_new(TYPE_A9MPCORE_PRIV); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); @@ -275,7 +278,7 @@ static void zynq_init(MachineState *machine) qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); } - for (n = 0; n < 64; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } @@ -458,7 +461,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data) }; MachineClass *mc = MACHINE_CLASS(oc); ObjectProperty *prop; - mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; + mc->desc = "Xilinx Zynq 7000 Platform Baseboard for Cortex-A9"; mc->init = zynq_init; mc->max_cpus = ZYNQ_MAX_CPUS; mc->ignore_memory_transaction_failures = true; From e2e5266c4555ed24a4727c9ce6e34eb5213a9ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:31 +0100 Subject: [PATCH 1821/2892] hw/arm/vexpress: Specify explicitly the GIC has 64 external IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When not specified, Cortex-A9MP configures its GIC with 64 external IRQs, (see commit a32134aad89 "arm:make the number of GIC interrupts configurable"), and Cortex-15MP to 128 (see commit 528622421eb "hw/cpu/a15mpcore: Correct default value for num-irq"). The Versatile Express board however expects a fixed set of 64 interrupts (see the fixed IRQ length when this board was added in commit 2055283bcc8 ("hw/vexpress: Add model of ARM Versatile Express board"). Add the GIC_EXT_IRQS definition (with a comment) to make that explicit. Except explicitly setting a property value to its same implicit value, there is no logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-7-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/vexpress.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 48e18a49d5..76c6107766 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -51,6 +51,8 @@ #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) #define VEXPRESS_FLASH_SECT_SIZE (256 * 1024) +#define GIC_EXT_IRQS 64 /* Versatile Express A9 development board */ + /* Number of virtio transports to create (0..8; limited by * number of available IRQ lines). */ @@ -241,6 +243,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type, */ dev = qdev_new(privdev); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, periphbase); @@ -251,7 +254,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type, * external interrupts starting from 32 (because there * are internal interrupts 0..31). */ - for (n = 0; n < 64; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } @@ -543,7 +546,7 @@ static void vexpress_common_init(MachineState *machine) VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine); VEDBoardInfo *daughterboard = vmc->daughterboard; DeviceState *dev, *sysctl, *pl041; - qemu_irq pic[64]; + qemu_irq pic[GIC_EXT_IRQS]; uint32_t sys_id; DriveInfo *dinfo; PFlashCFI01 *pflash0; From 2bf8bdcbb4510f206b3d0203c9bb8fb433387a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:32 +0100 Subject: [PATCH 1822/2892] hw/arm/highbank: Specify explicitly the GIC has 128 external IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When not specified, Cortex-A9MP configures its GIC with 64 external IRQs, (see commit a32134aad89 "arm:make the number of GIC interrupts configurable"), and Cortex-15MP to 128 (see commit 528622421eb "hw/cpu/a15mpcore: Correct default value for num-irq"). The Caldexa Highbank board however expects a fixed set of 128 interrupts (see the fixed IRQ length when this board was added in commit 2488514cef2 ("arm: SoC model for Calxeda Highbank"). Add the GIC_EXT_IRQS definition (with a comment) to make that explicit. Except explicitly setting a property value to its same implicit value, there is no logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-8-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/highbank.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 495704d972..0f3c207d54 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -45,7 +45,7 @@ #define MVBAR_ADDR 0x200 #define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t)) -#define NIRQ_GIC 160 +#define GIC_EXT_IRQS 128 /* EnergyCore ECX-1000 & ECX-2000 */ /* Board init. */ @@ -180,7 +180,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) { DeviceState *dev = NULL; SysBusDevice *busdev; - qemu_irq pic[128]; + qemu_irq pic[GIC_EXT_IRQS]; int n; unsigned int smp_cpus = machine->smp.cpus; qemu_irq cpu_irq[4]; @@ -260,7 +260,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) break; } qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); - qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); @@ -271,7 +271,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]); } - for (n = 0; n < 128; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } From 262f4ab3d5e3fcb6e85e40d9e29d9914c17972ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 16:43:33 +0100 Subject: [PATCH 1823/2892] hw/cpu/arm_mpcore: Remove default values for GIC external IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implicit default values are often hard to figure out, better be explicit. Now that all boards explicitly set the number of GIC external IRQs, remove the default values (displaying an error message if it is out of range). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250212154333.28644-9-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/cpu/a15mpcore.c | 18 ++++++++++++------ hw/cpu/a9mpcore.c | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index d24ab0a6ab..676f65a0af 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -58,6 +58,11 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) bool has_el2 = false; Object *cpuobj; + if (s->num_irq < 32 || s->num_irq > 256) { + error_setg(errp, "Property 'num-irq' must be between 32 and 256"); + return; + } + gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); @@ -146,13 +151,14 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) static const Property a15mp_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1), - /* The Cortex-A15MP may have anything from 0 to 224 external interrupt - * IRQ lines (with another 32 internal). We default to 128+32, which - * is the number provided by the Cortex-A15MP test chip in the - * Versatile Express A15 development board. - * Other boards may differ and should set this property appropriately. + /* + * The Cortex-A15MP may have anything from 0 to 224 external interrupt + * lines, plus always 32 internal IRQs. This property sets the total + * of internal + external, so the valid range is from 32 to 256. + * The board model must set this to whatever the configuration + * used for the CPU on that board or SoC is. */ - DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 160), + DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 0), }; static void a15mp_priv_class_init(ObjectClass *klass, void *data) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 25416c5032..1b9f2bef93 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -56,6 +56,11 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) CPUState *cpu0; Object *cpuobj; + if (s->num_irq < 32 || s->num_irq > 256) { + error_setg(errp, "Property 'num-irq' must be between 32 and 256"); + return; + } + cpu0 = qemu_get_cpu(0); cpuobj = OBJECT(cpu0); if (strcmp(object_get_typename(cpuobj), ARM_CPU_TYPE_NAME("cortex-a9"))) { @@ -160,13 +165,14 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) static const Property a9mp_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1), - /* The Cortex-A9MP may have anything from 0 to 224 external interrupt - * IRQ lines (with another 32 internal). We default to 64+32, which - * is the number provided by the Cortex-A9MP test chip in the - * Realview PBX-A9 and Versatile Express A9 development boards. - * Other boards may differ and should set this property appropriately. + /* + * The Cortex-A9MP may have anything from 0 to 224 external interrupt + * lines, plus always 32 internal IRQs. This property sets the total + * of internal + external, so the valid range is from 32 to 256. + * The board model must set this to whatever the configuration + * used for the CPU on that board or SoC is. */ - DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96), + DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 0), }; static void a9mp_priv_class_init(ObjectClass *klass, void *data) From 464ce71a963b3dfc290cd59c3d1bfedf11c004df Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 9 Feb 2025 11:36:04 +0100 Subject: [PATCH 1824/2892] Kconfig: Extract CONFIG_USB_CHIPIDEA from CONFIG_IMX TYPE_CHIPIDEA models an IP block which is also used in TYPE_ZYNQ_MACHINE which itself is not an IMX device. CONFIG_ZYNQ selects CONFIG_USB_EHCI_SYSBUS while TYPE_CHIPIDEA is a separate compilation unit, so only works by accident if CONFIG_IMX is given. Fix that by extracting CONFIG_USB_CHIPIDEA from CONFIG_IMX. cc: qemu-stable@nongnu.org Fixes: 616ec12d0fcc "hw/arm/xilinx_zynq: Fix USB port instantiation" Signed-off-by: Bernhard Beschow Message-id: 20250209103604.29545-1-shentey@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 6 +++++- hw/usb/Kconfig | 4 ++++ hw/usb/meson.build | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 256013ca80..7eab3914d4 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -303,7 +303,7 @@ config ZYNQ select PL330 select SDHCI select SSI_M25P80 - select USB_EHCI_SYSBUS + select USB_CHIPIDEA select XILINX # UART select XILINX_AXI select XILINX_SPI @@ -489,6 +489,7 @@ config FSL_IMX25 select IMX select IMX_FEC select IMX_I2C + select USB_CHIPIDEA select WDT_IMX2 select SDHCI @@ -516,6 +517,7 @@ config FSL_IMX6 select PL310 # cache controller select PCI_EXPRESS_DESIGNWARE select SDHCI + select USB_CHIPIDEA select OR_IRQ config ASPEED_SOC @@ -576,6 +578,7 @@ config FSL_IMX7 select SDHCI select OR_IRQ select UNIMP + select USB_CHIPIDEA config ARM_SMMUV3 bool @@ -591,6 +594,7 @@ config FSL_IMX6UL select IMX_I2C select WDT_IMX2 select SDHCI + select USB_CHIPIDEA select UNIMP config MICROBIT diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 5fbecd2f43..69c663be52 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -143,3 +143,7 @@ config USB_DWC3 config XLNX_USB_SUBSYS bool select USB_DWC3 + +config USB_CHIPIDEA + bool + select USB_EHCI_SYSBUS diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 1b4d1507e4..17360a5b5a 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -25,8 +25,8 @@ system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c' system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c')) system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c')) system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) +system_ss.add(when: 'CONFIG_USB_CHIPIDEA', if_true: files('chipidea.c')) -system_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) From b2ba5ff272e0738c6b82197fe61f73344e5edcfb Mon Sep 17 00:00:00 2001 From: Stephen Longfield Date: Wed, 19 Feb 2025 16:55:34 +0000 Subject: [PATCH 1825/2892] target/arm: Use uint32_t in t32_expandimm_imm() In t32_expandimm_imm(), we take an 8 bit value XY and construct a 32-bit value which might be of the form XY, 00XY00XY, XY00XY00, or XYXYXYXY. We do this with multiplications, and we use an 'int' type. For the cases where we're setting the high byte of the 32-bit value to XY, this means that we do an integer multiplication that might overflow, and rely on the -fwrapv semantics to keep this from being undefined behaviour. It's clearer to use an unsigned type here, because we're really doing operations on the value considered as a set of bits. The result is the same. The return value from the function remains 'int', because this is a decodetree !function function, and follows the API for those functions. Signed-off-by: Stephen Longfield Signed-off-by: Roque Arcudia Hernandez Message-id: 20250219165534.3387376-1-slongfield@google.com [PMM: Rewrote the commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 68ac393415..d8225b77c8 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -3510,7 +3510,7 @@ static int t32_expandimm_rot(DisasContext *s, int x) /* Return the unrotated immediate from T32ExpandImm. */ static int t32_expandimm_imm(DisasContext *s, int x) { - int imm = extract32(x, 0, 8); + uint32_t imm = extract32(x, 0, 8); switch (extract32(x, 8, 4)) { case 0: /* XY */ From 70ce076fa6dff60585c229a4b641b13e64bf03cf Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:52 -0800 Subject: [PATCH 1826/2892] roms: Update vbootrom to 1287b6e This newer vbootrom supports NPCM8xx. Similar to the NPCM7XX one it supports loading the UBoot from the SPI device and not more. We updated the npcm7xx bootrom to be compiled from this version. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-2-wuhaotsh@google.com Signed-off-by: Peter Maydell --- pc-bios/npcm7xx_bootrom.bin | Bin 768 -> 768 bytes roms/vbootrom | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/npcm7xx_bootrom.bin b/pc-bios/npcm7xx_bootrom.bin index 38f89d1b97b0c2e133af2a9fbed0521be132065b..903f126636f9ef5d1100c056656ccfb2b32e5e10 100644 GIT binary patch delta 90 zcmZo*Yhc^(l+nU*!D9x6DNkDr=09a-2ztoGz`#|*F#jn7L;r()|Np;c0m>C1$z?$0 Ywog`Ma%Vh0Ig_b-VgU<}A_D>d06Rh+WdHyG delta 69 zcmZo*Yhc^(lu^NO!D9x2$xoRb7CdZGnE#ZCA@Cs+0|QqL!~CZV4E+!GPG)41X52Pe SmdTy*+~icIZXQJj1ONb5*AzJb diff --git a/roms/vbootrom b/roms/vbootrom index 0c37a43527..1287b6e42e 160000 --- a/roms/vbootrom +++ b/roms/vbootrom @@ -1 +1 @@ -Subproject commit 0c37a43527f0ee2b9584e7fb2fdc805e902635ac +Subproject commit 1287b6e42e839ba2ab0f06268c5b53ae60df3537 From 269b7effd906d6b22071971b7f5b4cb344403b86 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:53 -0800 Subject: [PATCH 1827/2892] pc-bios: Add NPCM8XX vBootrom The bootrom is a minimal bootrom used to load an NPCM8XX image. The source code is located in the same repo as the NPCM7XX one: github.com/google/vbootrom/tree/master/npcm8xx. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-3-wuhaotsh@google.com Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + pc-bios/README | 8 ++++---- pc-bios/meson.build | 1 + pc-bios/npcm8xx_bootrom.bin | Bin 0 -> 608 bytes roms/Makefile | 6 ++++++ 5 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 pc-bios/npcm8xx_bootrom.bin diff --git a/MAINTAINERS b/MAINTAINERS index 3848d37a38..e145017d53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -878,6 +878,7 @@ F: include/hw/*/npcm* F: tests/qtest/npcm* F: tests/qtest/adm1266-test.c F: pc-bios/npcm7xx_bootrom.bin +F: pc-bios/npcm8xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst F: tests/functional/test_arm_quanta_gsj.py diff --git a/pc-bios/README b/pc-bios/README index 7ffb2f43a4..700dcaab52 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -70,10 +70,10 @@ source code also contains code reused from other projects described here: https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md. -- npcm7xx_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for Nuvoton - NPCM7xx BMC devices. It currently implements the bare minimum to load, parse, - initialize and run boot images stored in SPI flash, but may grow more - features over time as needed. The source code is available at: +- npcm{7xx,8xx}_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for + Nuvoton NPCM7xx/8xx BMC devices. It currently implements the bare minimum to + load, parse, initialize and run boot images stored in SPI flash, but may grow + more features over time as needed. The source code is available at: https://github.com/google/vbootrom - hppa-firmware.img (32-bit) and hppa-firmware64.img (64-bit) are firmware diff --git a/pc-bios/meson.build b/pc-bios/meson.build index b68b29cc7d..51e95cc903 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -80,6 +80,7 @@ blobs = [ 'opensbi-riscv32-generic-fw_dynamic.bin', 'opensbi-riscv64-generic-fw_dynamic.bin', 'npcm7xx_bootrom.bin', + 'npcm8xx_bootrom.bin', 'vof.bin', 'vof-nvram.bin', ] diff --git a/pc-bios/npcm8xx_bootrom.bin b/pc-bios/npcm8xx_bootrom.bin new file mode 100644 index 0000000000000000000000000000000000000000..6370d6475635c4d445d2b927311edcd591949c82 GIT binary patch literal 608 zcmdUrKTE?<6vfX=0{*3B5ET?nwWA^;qEk()n=Xb9-4dxoSBrz#p|QJQL~zokn{Eyc z?PBXUkU+aB?k?IbNQftG5ej|*FC2c{bKkr7zLy3jhNxj`gc_y5h&V=Ru)PgZC)Y`f zTqA9Am28qLHlr*^&hT#;re-)dpxT0U42|O+cWOcx=B;{6xXH04vx?cjm z+%U{oFx!aPpV3>ZKz0i$XA-yq{f}x4;|pbw;l#@9zGd|z-rs*H@V-o%PEV)D-)8n2%DyH5@w_^Y8 LH5R3RMV#gjxYTW} literal 0 HcmV?d00001 diff --git a/roms/Makefile b/roms/Makefile index 31e4b97c98..beff58d9d5 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -34,6 +34,7 @@ find-cross-gcc = $(firstword $(wildcard $(patsubst %ld,%gcc,$(call find-cross-ld # finally strip off path + toolname so we get the prefix find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1)))) +aarch64_cross_prefix := $(call find-cross-prefix,aarch64) arm_cross_prefix := $(call find-cross-prefix,arm) powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64) powerpc_cross_prefix := $(call find-cross-prefix,powerpc) @@ -66,6 +67,7 @@ default help: @echo " u-boot.e500 -- update u-boot.e500" @echo " u-boot.sam460 -- update u-boot.sam460" @echo " npcm7xx_bootrom -- update vbootrom for npcm7xx" + @echo " npcm8xx_bootrom -- update vbootrom for npcm8xx" @echo " efi -- update UEFI (edk2) platform firmware" @echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine" @echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine" @@ -194,6 +196,10 @@ npcm7xx_bootrom: $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix) cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin +npcm8xx_bootrom: + $(MAKE) -C vbootrom CROSS_COMPILE=$(aarch64_cross_prefix) + cp vbootrom/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin + hppa-firmware: $(MAKE) -C seabios-hppa parisc cp seabios-hppa/out/hppa-firmware.img ../pc-bios/ From e9be8467b42f7e2af70694422f4b4d8afe82bf4e Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:54 -0800 Subject: [PATCH 1828/2892] hw/ssi: Make flash size a property in NPCM7XX FIU This allows different FIUs to have different flash sizes, useful in NPCM8XX which has multiple different sized FIU modules. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Reviewed-by: Philippe Mathieu-Daude Message-id: 20250219184609.1839281-4-wuhaotsh@google.com [PMM: flash_size must be a uint64_t to build on 32-bit hosts] Signed-off-by: Peter Maydell --- hw/arm/npcm7xx.c | 6 ++++++ hw/ssi/npcm7xx_fiu.c | 16 ++++++++++++++-- include/hw/ssi/npcm7xx_fiu.h | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 386b2c35e9..2d6e08b72b 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -292,17 +292,21 @@ static const struct { hwaddr regs_addr; int cs_count; const hwaddr *flash_addr; + size_t flash_size; } npcm7xx_fiu[] = { { .name = "fiu0", .regs_addr = 0xfb000000, .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr), .flash_addr = npcm7xx_fiu0_flash_addr, + .flash_size = 128 * MiB, + }, { .name = "fiu3", .regs_addr = 0xc0000000, .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr), .flash_addr = npcm7xx_fiu3_flash_addr, + .flash_size = 128 * MiB, }, }; @@ -735,6 +739,8 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(sbd), "cs-count", npcm7xx_fiu[i].cs_count, &error_abort); + object_property_set_int(OBJECT(sbd), "flash-size", + npcm7xx_fiu[i].flash_size, &error_abort); sysbus_realize(sbd, &error_abort); sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr); diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 21fc489038..8df4bec3f1 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -29,7 +29,7 @@ #include "trace.h" /* Up to 128 MiB of flash may be accessed directly as memory. */ -#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB) +#define NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE (128 * MiB) /* Each module has 4 KiB of register space. Only a fraction of it is used. */ #define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB) @@ -507,6 +507,17 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp) return; } + if (s->flash_size == 0) { + error_setg(errp, "%s: flash size must be set", dev->canonical_path); + return; + } + + if (s->flash_size > NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE) { + error_setg(errp, "%s: flash size should not exceed 128 MiB", + dev->canonical_path); + return; + } + s->spi = ssi_create_bus(dev, "spi"); s->cs_lines = g_new0(qemu_irq, s->cs_count); qdev_init_gpio_out_named(DEVICE(s), s->cs_lines, "cs", s->cs_count); @@ -525,7 +536,7 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp) flash->fiu = s; memory_region_init_io(&flash->direct_access, OBJECT(s), &npcm7xx_fiu_flash_ops, &s->flash[i], "flash", - NPCM7XX_FIU_FLASH_WINDOW_SIZE); + s->flash_size); sysbus_init_mmio(sbd, &flash->direct_access); } } @@ -543,6 +554,7 @@ static const VMStateDescription vmstate_npcm7xx_fiu = { static const Property npcm7xx_fiu_properties[] = { DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0), + DEFINE_PROP_SIZE("flash-size", NPCM7xxFIUState, flash_size, 0), }; static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/ssi/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h index a3a1704289..7ebd422ca6 100644 --- a/include/hw/ssi/npcm7xx_fiu.h +++ b/include/hw/ssi/npcm7xx_fiu.h @@ -60,6 +60,7 @@ struct NPCM7xxFIUState { int32_t cs_count; int32_t active_cs; qemu_irq *cs_lines; + uint64_t flash_size; NPCM7xxFIUFlash *flash; SSIBus *spi; From 506af2330cd0ef684a48aad12640a7bea8e95247 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:55 -0800 Subject: [PATCH 1829/2892] hw/misc: Rename npcm7xx_gcr to npcm_gcr NPCM7XX and NPCM8XX have a different set of GCRs and the GCR module needs to fit both. This commit changes the name of the GCR module. Future commits will add the support for NPCM8XX GCRs. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-5-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/meson.build | 2 +- hw/misc/{npcm7xx_gcr.c => npcm_gcr.c} | 2 +- include/hw/arm/npcm7xx.h | 2 +- include/hw/misc/{npcm7xx_gcr.h => npcm_gcr.h} | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename hw/misc/{npcm7xx_gcr.c => npcm_gcr.c} (99%) rename include/hw/misc/{npcm7xx_gcr.h => npcm_gcr.h} (96%) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 55f493521b..554eb8df5b 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -70,7 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( )) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm7xx_clk.c', - 'npcm7xx_gcr.c', + 'npcm_gcr.c', 'npcm7xx_mft.c', 'npcm7xx_pwm.c', 'npcm7xx_rng.c', diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm_gcr.c similarity index 99% rename from hw/misc/npcm7xx_gcr.c rename to hw/misc/npcm_gcr.c index 07464a4dc9..826fd41123 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" -#include "hw/misc/npcm7xx_gcr.h" +#include "hw/misc/npcm_gcr.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qapi/error.h" diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 4e0d210188..510170471e 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -24,7 +24,7 @@ #include "hw/i2c/npcm7xx_smbus.h" #include "hw/mem/npcm7xx_mc.h" #include "hw/misc/npcm7xx_clk.h" -#include "hw/misc/npcm7xx_gcr.h" +#include "hw/misc/npcm_gcr.h" #include "hw/misc/npcm7xx_mft.h" #include "hw/misc/npcm7xx_pwm.h" #include "hw/misc/npcm7xx_rng.h" diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm_gcr.h similarity index 96% rename from include/hw/misc/npcm7xx_gcr.h rename to include/hw/misc/npcm_gcr.h index c0bbdda77e..9b4998950c 100644 --- a/include/hw/misc/npcm7xx_gcr.h +++ b/include/hw/misc/npcm_gcr.h @@ -13,8 +13,8 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ -#ifndef NPCM7XX_GCR_H -#define NPCM7XX_GCR_H +#ifndef NPCM_GCR_H +#define NPCM_GCR_H #include "exec/memory.h" #include "hw/sysbus.h" @@ -70,4 +70,4 @@ struct NPCM7xxGCRState { #define TYPE_NPCM7XX_GCR "npcm7xx-gcr" OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxGCRState, NPCM7XX_GCR) -#endif /* NPCM7XX_GCR_H */ +#endif /* NPCM_GCR_H */ From c99064e63748f993457f1fe658d05c662c0134f1 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:56 -0800 Subject: [PATCH 1830/2892] hw/misc: Move NPCM7XX GCR to NPCM GCR A lot of NPCM7XX and NPCM8XX GCR modules share the same code, this commit moves the NPCM7XX GCR to NPCM GCR for these properties. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-6-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_gcr.c | 92 +++++++++++++++++++++----------------- hw/misc/trace-events | 6 +-- include/hw/arm/npcm7xx.h | 2 +- include/hw/misc/npcm_gcr.h | 7 +-- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index 826fd41123..0959f2e5c4 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -84,10 +84,10 @@ static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = { [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, }; -static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size) +static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxGCRState *s = opaque; + NPCMGCRState *s = opaque; if (reg >= NPCM7XX_GCR_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, @@ -96,19 +96,19 @@ static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size) return 0; } - trace_npcm7xx_gcr_read(offset, s->regs[reg]); + trace_npcm_gcr_read(offset, s->regs[reg]); return s->regs[reg]; } -static void npcm7xx_gcr_write(void *opaque, hwaddr offset, +static void npcm_gcr_write(void *opaque, hwaddr offset, uint64_t v, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxGCRState *s = opaque; + NPCMGCRState *s = opaque; uint32_t value = v; - trace_npcm7xx_gcr_write(offset, value); + trace_npcm_gcr_write(offset, value); if (reg >= NPCM7XX_GCR_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, @@ -142,9 +142,9 @@ static void npcm7xx_gcr_write(void *opaque, hwaddr offset, s->regs[reg] = value; } -static const struct MemoryRegionOps npcm7xx_gcr_ops = { - .read = npcm7xx_gcr_read, - .write = npcm7xx_gcr_write, +static const struct MemoryRegionOps npcm_gcr_ops = { + .read = npcm_gcr_read, + .write = npcm_gcr_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, @@ -155,7 +155,7 @@ static const struct MemoryRegionOps npcm7xx_gcr_ops = { static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) { - NPCM7xxGCRState *s = NPCM7XX_GCR(obj); + NPCMGCRState *s = NPCM_GCR(obj); QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); @@ -165,10 +165,10 @@ static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; } -static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) +static void npcm_gcr_realize(DeviceState *dev, Error **errp) { ERRP_GUARD(); - NPCM7xxGCRState *s = NPCM7XX_GCR(dev); + NPCMGCRState *s = NPCM_GCR(dev); uint64_t dram_size; Object *obj; @@ -210,55 +210,65 @@ static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; } -static void npcm7xx_gcr_init(Object *obj) +static void npcm_gcr_init(Object *obj) { - NPCM7xxGCRState *s = NPCM7XX_GCR(obj); + NPCMGCRState *s = NPCM_GCR(obj); - memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s, - TYPE_NPCM7XX_GCR, 4 * KiB); + memory_region_init_io(&s->iomem, obj, &npcm_gcr_ops, s, + TYPE_NPCM_GCR, 4 * KiB); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static const VMStateDescription vmstate_npcm7xx_gcr = { - .name = "npcm7xx-gcr", - .version_id = 0, - .minimum_version_id = 0, +static const VMStateDescription vmstate_npcm_gcr = { + .name = "npcm-gcr", + .version_id = 1, + .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS), + VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM7XX_GCR_NR_REGS), VMSTATE_END_OF_LIST(), }, }; -static const Property npcm7xx_gcr_properties[] = { - DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0), - DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0), +static const Property npcm_gcr_properties[] = { + DEFINE_PROP_UINT32("disabled-modules", NPCMGCRState, reset_mdlr, 0), + DEFINE_PROP_UINT32("power-on-straps", NPCMGCRState, reset_pwron, 0), }; -static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) +static void npcm_gcr_class_init(ObjectClass *klass, void *data) { - ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS); + dc->realize = npcm_gcr_realize; + dc->vmsd = &vmstate_npcm_gcr; + device_class_set_props(dc, npcm_gcr_properties); +} + +static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END != NPCM7XX_GCR_NR_REGS); dc->desc = "NPCM7xx System Global Control Registers"; - dc->realize = npcm7xx_gcr_realize; - dc->vmsd = &vmstate_npcm7xx_gcr; rc->phases.enter = npcm7xx_gcr_enter_reset; - device_class_set_props(dc, npcm7xx_gcr_properties); } -static const TypeInfo npcm7xx_gcr_info = { - .name = TYPE_NPCM7XX_GCR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(NPCM7xxGCRState), - .instance_init = npcm7xx_gcr_init, - .class_init = npcm7xx_gcr_class_init, +static const TypeInfo npcm_gcr_info[] = { + { + .name = TYPE_NPCM_GCR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMGCRState), + .instance_init = npcm_gcr_init, + .class_init = npcm_gcr_class_init, + .abstract = true, + }, + { + .name = TYPE_NPCM7XX_GCR, + .parent = TYPE_NPCM_GCR, + .class_init = npcm7xx_gcr_class_init, + }, }; - -static void npcm7xx_gcr_register_type(void) -{ - type_register_static(&npcm7xx_gcr_info); -} -type_init(npcm7xx_gcr_register_type); +DEFINE_TYPES(npcm_gcr_info) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b35b0e77f7..0f7204a237 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -134,9 +134,9 @@ mos6522_read(uint64_t addr, const char *name, unsigned val) "reg=0x%"PRIx64 " [% npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -# npcm7xx_gcr.c -npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +# npcm_gcr.c +npcm_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +npcm_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 # npcm7xx_mft.c npcm7xx_mft_read(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 510170471e..2e708471ec 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -89,7 +89,7 @@ struct NPCM7xxState { MemoryRegion ram3; MemoryRegion *dram; - NPCM7xxGCRState gcr; + NPCMGCRState gcr; NPCM7xxCLKState clk; NPCM7xxTimerCtrlState tim[3]; NPCM7xxADCState adc; diff --git a/include/hw/misc/npcm_gcr.h b/include/hw/misc/npcm_gcr.h index 9b4998950c..6d3d00d260 100644 --- a/include/hw/misc/npcm_gcr.h +++ b/include/hw/misc/npcm_gcr.h @@ -55,7 +55,7 @@ */ #define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t)) -struct NPCM7xxGCRState { +typedef struct NPCMGCRState { SysBusDevice parent; MemoryRegion iomem; @@ -65,9 +65,10 @@ struct NPCM7xxGCRState { uint32_t reset_pwron; uint32_t reset_mdlr; uint32_t reset_intcr3; -}; +} NPCMGCRState; +#define TYPE_NPCM_GCR "npcm-gcr" #define TYPE_NPCM7XX_GCR "npcm7xx-gcr" -OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxGCRState, NPCM7XX_GCR) +OBJECT_DECLARE_SIMPLE_TYPE(NPCMGCRState, NPCM_GCR) #endif /* NPCM_GCR_H */ From 8ca2021b9d3aa3e9ef276bdbf04f89677341955c Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:57 -0800 Subject: [PATCH 1831/2892] hw/misc: Add nr_regs and cold_reset_values to NPCM GCR These 2 values are different between NPCM7XX and NPCM8XX GCRs. So we add them to the class and assign different values to them. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-7-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_gcr.c | 27 ++++++++++++++++----------- include/hw/misc/npcm_gcr.h | 13 +++++++++++-- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index 0959f2e5c4..d89e8c2c3b 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -66,10 +66,9 @@ enum NPCM7xxGCRRegisters { NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t), NPCM7XX_GCR_USB1PHYCTL, NPCM7XX_GCR_USB2PHYCTL, - NPCM7XX_GCR_REGS_END, }; -static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = { +static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_GCR_NR_REGS] = { [NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */ [NPCM7XX_GCR_MISCPE] = 0x0000ffff, [NPCM7XX_GCR_SPSWC] = 0x00000003, @@ -88,8 +87,9 @@ static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); NPCMGCRState *s = opaque; + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); - if (reg >= NPCM7XX_GCR_NR_REGS) { + if (reg >= c->nr_regs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04" HWADDR_PRIx " out of range\n", __func__, offset); @@ -106,11 +106,12 @@ static void npcm_gcr_write(void *opaque, hwaddr offset, { uint32_t reg = offset / sizeof(uint32_t); NPCMGCRState *s = opaque; + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); uint32_t value = v; - trace_npcm_gcr_write(offset, value); + trace_npcm_gcr_write(offset, v); - if (reg >= NPCM7XX_GCR_NR_REGS) { + if (reg >= c->nr_regs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04" HWADDR_PRIx " out of range\n", __func__, offset); @@ -156,10 +157,12 @@ static const struct MemoryRegionOps npcm_gcr_ops = { static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) { NPCMGCRState *s = NPCM_GCR(obj); + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj); - QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - - memcpy(s->regs, cold_reset_values, sizeof(s->regs)); + g_assert(sizeof(s->regs) >= sizeof(c->cold_reset_values)); + g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t)); + memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t)); + /* These 3 registers are at the same location in both 7xx and 8xx. */ s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; @@ -224,7 +227,7 @@ static const VMStateDescription vmstate_npcm_gcr = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM7XX_GCR_NR_REGS), + VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM_GCR_MAX_NR_REGS), VMSTATE_END_OF_LIST(), }, }; @@ -238,7 +241,6 @@ static void npcm_gcr_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS); dc->realize = npcm_gcr_realize; dc->vmsd = &vmstate_npcm_gcr; @@ -247,13 +249,15 @@ static void npcm_gcr_class_init(ObjectClass *klass, void *data) static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) { + NPCMGCRClass *c = NPCM_GCR_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); - QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END != NPCM7XX_GCR_NR_REGS); dc->desc = "NPCM7xx System Global Control Registers"; rc->phases.enter = npcm7xx_gcr_enter_reset; + c->nr_regs = NPCM7XX_GCR_NR_REGS; + c->cold_reset_values = npcm7xx_cold_reset_values; } static const TypeInfo npcm_gcr_info[] = { @@ -262,6 +266,7 @@ static const TypeInfo npcm_gcr_info[] = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(NPCMGCRState), .instance_init = npcm_gcr_init, + .class_size = sizeof(NPCMGCRClass), .class_init = npcm_gcr_class_init, .abstract = true, }, diff --git a/include/hw/misc/npcm_gcr.h b/include/hw/misc/npcm_gcr.h index 6d3d00d260..9af24e5cdc 100644 --- a/include/hw/misc/npcm_gcr.h +++ b/include/hw/misc/npcm_gcr.h @@ -18,6 +18,7 @@ #include "exec/memory.h" #include "hw/sysbus.h" +#include "qom/object.h" /* * NPCM7XX PWRON STRAP bit fields @@ -53,6 +54,7 @@ * Number of registers in our device state structure. Don't change this without * incrementing the version_id in the vmstate. */ +#define NPCM_GCR_MAX_NR_REGS NPCM7XX_GCR_NR_REGS #define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t)) typedef struct NPCMGCRState { @@ -60,15 +62,22 @@ typedef struct NPCMGCRState { MemoryRegion iomem; - uint32_t regs[NPCM7XX_GCR_NR_REGS]; + uint32_t regs[NPCM_GCR_MAX_NR_REGS]; uint32_t reset_pwron; uint32_t reset_mdlr; uint32_t reset_intcr3; } NPCMGCRState; +typedef struct NPCMGCRClass { + SysBusDeviceClass parent; + + size_t nr_regs; + const uint32_t *cold_reset_values; +} NPCMGCRClass; + #define TYPE_NPCM_GCR "npcm-gcr" #define TYPE_NPCM7XX_GCR "npcm7xx-gcr" -OBJECT_DECLARE_SIMPLE_TYPE(NPCMGCRState, NPCM_GCR) +OBJECT_DECLARE_TYPE(NPCMGCRState, NPCMGCRClass, NPCM_GCR) #endif /* NPCM_GCR_H */ From d9ffb75f2a85a2271dc928849f02f7bada4d1507 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:58 -0800 Subject: [PATCH 1832/2892] hw/misc: Add support for NPCM8XX GCR Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-8-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_gcr.c | 133 ++++++++++++++++++++++++++++++++++++- include/hw/misc/npcm_gcr.h | 6 +- 2 files changed, 134 insertions(+), 5 deletions(-) diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index d89e8c2c3b..ac22fb08cb 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -1,5 +1,5 @@ /* - * Nuvoton NPCM7xx System Global Control Registers. + * Nuvoton NPCM7xx/8xx System Global Control Registers. * * Copyright 2020 Google LLC * @@ -83,6 +83,118 @@ static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_GCR_NR_REGS] = { [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, }; +enum NPCM8xxGCRRegisters { + NPCM8XX_GCR_PDID, + NPCM8XX_GCR_PWRON, + NPCM8XX_GCR_MISCPE = 0x014 / sizeof(uint32_t), + NPCM8XX_GCR_FLOCKR2 = 0x020 / sizeof(uint32_t), + NPCM8XX_GCR_FLOCKR3, + NPCM8XX_GCR_A35_MODE = 0x034 / sizeof(uint32_t), + NPCM8XX_GCR_SPSWC, + NPCM8XX_GCR_INTCR, + NPCM8XX_GCR_INTSR, + NPCM8XX_GCR_HIFCR = 0x050 / sizeof(uint32_t), + NPCM8XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t), + NPCM8XX_GCR_SRCNT = 0x068 / sizeof(uint32_t), + NPCM8XX_GCR_RESSR, + NPCM8XX_GCR_RLOCKR1, + NPCM8XX_GCR_FLOCKR1, + NPCM8XX_GCR_DSCNT, + NPCM8XX_GCR_MDLR, + NPCM8XX_GCR_SCRPAD_C = 0x080 / sizeof(uint32_t), + NPCM8XX_GCR_SCRPAD_B, + NPCM8XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t), + NPCM8XX_GCR_INTCR3, + NPCM8XX_GCR_PCIRCTL = 0x0a0 / sizeof(uint32_t), + NPCM8XX_GCR_VSINTR, + NPCM8XX_GCR_SD2SUR1 = 0x0b4 / sizeof(uint32_t), + NPCM8XX_GCR_SD2SUR2, + NPCM8XX_GCR_INTCR4 = 0x0c0 / sizeof(uint32_t), + NPCM8XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t), + NPCM8XX_GCR_CP2BST, + NPCM8XX_GCR_B2CPNT, + NPCM8XX_GCR_CPPCTL, + NPCM8XX_GCR_I2CSEGSEL = 0x0e0 / sizeof(uint32_t), + NPCM8XX_GCR_I2CSEGCTL, + NPCM8XX_GCR_VSRCR, + NPCM8XX_GCR_MLOCKR, + NPCM8XX_GCR_SCRPAD = 0x13c / sizeof(uint32_t), + NPCM8XX_GCR_USB1PHYCTL, + NPCM8XX_GCR_USB2PHYCTL, + NPCM8XX_GCR_USB3PHYCTL, + NPCM8XX_GCR_MFSEL1 = 0x260 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL2, + NPCM8XX_GCR_MFSEL3, + NPCM8XX_GCR_MFSEL4, + NPCM8XX_GCR_MFSEL5, + NPCM8XX_GCR_MFSEL6, + NPCM8XX_GCR_MFSEL7, + NPCM8XX_GCR_MFSEL_LK1 = 0x280 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL_LK2, + NPCM8XX_GCR_MFSEL_LK3, + NPCM8XX_GCR_MFSEL_LK4, + NPCM8XX_GCR_MFSEL_LK5, + NPCM8XX_GCR_MFSEL_LK6, + NPCM8XX_GCR_MFSEL_LK7, + NPCM8XX_GCR_MFSEL_SET1 = 0x2a0 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL_SET2, + NPCM8XX_GCR_MFSEL_SET3, + NPCM8XX_GCR_MFSEL_SET4, + NPCM8XX_GCR_MFSEL_SET5, + NPCM8XX_GCR_MFSEL_SET6, + NPCM8XX_GCR_MFSEL_SET7, + NPCM8XX_GCR_MFSEL_CLR1 = 0x2c0 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL_CLR2, + NPCM8XX_GCR_MFSEL_CLR3, + NPCM8XX_GCR_MFSEL_CLR4, + NPCM8XX_GCR_MFSEL_CLR5, + NPCM8XX_GCR_MFSEL_CLR6, + NPCM8XX_GCR_MFSEL_CLR7, + NPCM8XX_GCR_WD0RCRLK = 0x400 / sizeof(uint32_t), + NPCM8XX_GCR_WD1RCRLK, + NPCM8XX_GCR_WD2RCRLK, + NPCM8XX_GCR_SWRSTC1LK, + NPCM8XX_GCR_SWRSTC2LK, + NPCM8XX_GCR_SWRSTC3LK, + NPCM8XX_GCR_TIPRSTCLK, + NPCM8XX_GCR_CORSTCLK, + NPCM8XX_GCR_WD0RCRBLK, + NPCM8XX_GCR_WD1RCRBLK, + NPCM8XX_GCR_WD2RCRBLK, + NPCM8XX_GCR_SWRSTC1BLK, + NPCM8XX_GCR_SWRSTC2BLK, + NPCM8XX_GCR_SWRSTC3BLK, + NPCM8XX_GCR_TIPRSTCBLK, + NPCM8XX_GCR_CORSTCBLK, + /* 64 scratch pad registers start here. 0xe00 ~ 0xefc */ + NPCM8XX_GCR_SCRPAD_00 = 0xe00 / sizeof(uint32_t), + /* 32 semaphore registers start here. 0xf00 ~ 0xf7c */ + NPCM8XX_GCR_GP_SEMFR_00 = 0xf00 / sizeof(uint32_t), + NPCM8XX_GCR_GP_SEMFR_31 = 0xf7c / sizeof(uint32_t), +}; + +static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_GCR_NR_REGS] = { + [NPCM8XX_GCR_PDID] = 0x04a35850, /* Arbel A1 */ + [NPCM8XX_GCR_MISCPE] = 0x0000ffff, + [NPCM8XX_GCR_A35_MODE] = 0xfff4ff30, + [NPCM8XX_GCR_SPSWC] = 0x00000003, + [NPCM8XX_GCR_INTCR] = 0x0010035e, + [NPCM8XX_GCR_HIFCR] = 0x0000004e, + [NPCM8XX_GCR_SD2SUR1] = 0xfdc80000, + [NPCM8XX_GCR_SD2SUR2] = 0x5200b130, + [NPCM8XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ + [NPCM8XX_GCR_RESSR] = 0x80000000, + [NPCM8XX_GCR_DAVCLVLR] = 0x5a00f3cf, + [NPCM8XX_GCR_INTCR3] = 0x5e001002, + [NPCM8XX_GCR_VSRCR] = 0x00004800, + [NPCM8XX_GCR_SCRPAD] = 0x00000008, + [NPCM8XX_GCR_USB1PHYCTL] = 0x034730e4, + [NPCM8XX_GCR_USB2PHYCTL] = 0x034730e4, + [NPCM8XX_GCR_USB3PHYCTL] = 0x034730e4, + /* All 32 semaphores should be initialized to 1. */ + [NPCM8XX_GCR_GP_SEMFR_00...NPCM8XX_GCR_GP_SEMFR_31] = 0x00000001, +}; + static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); @@ -224,8 +336,8 @@ static void npcm_gcr_init(Object *obj) static const VMStateDescription vmstate_npcm_gcr = { .name = "npcm-gcr", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM_GCR_MAX_NR_REGS), VMSTATE_END_OF_LIST(), @@ -260,6 +372,16 @@ static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) c->cold_reset_values = npcm7xx_cold_reset_values; } +static void npcm8xx_gcr_class_init(ObjectClass *klass, void *data) +{ + NPCMGCRClass *c = NPCM_GCR_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM8xx System Global Control Registers"; + c->nr_regs = NPCM8XX_GCR_NR_REGS; + c->cold_reset_values = npcm8xx_cold_reset_values; +} + static const TypeInfo npcm_gcr_info[] = { { .name = TYPE_NPCM_GCR, @@ -275,5 +397,10 @@ static const TypeInfo npcm_gcr_info[] = { .parent = TYPE_NPCM_GCR, .class_init = npcm7xx_gcr_class_init, }, + { + .name = TYPE_NPCM8XX_GCR, + .parent = TYPE_NPCM_GCR, + .class_init = npcm8xx_gcr_class_init, + }, }; DEFINE_TYPES(npcm_gcr_info) diff --git a/include/hw/misc/npcm_gcr.h b/include/hw/misc/npcm_gcr.h index 9af24e5cdc..9ac76ca9ab 100644 --- a/include/hw/misc/npcm_gcr.h +++ b/include/hw/misc/npcm_gcr.h @@ -1,5 +1,5 @@ /* - * Nuvoton NPCM7xx System Global Control Registers. + * Nuvoton NPCM7xx/8xx System Global Control Registers. * * Copyright 2020 Google LLC * @@ -54,8 +54,9 @@ * Number of registers in our device state structure. Don't change this without * incrementing the version_id in the vmstate. */ -#define NPCM_GCR_MAX_NR_REGS NPCM7XX_GCR_NR_REGS +#define NPCM_GCR_MAX_NR_REGS NPCM8XX_GCR_NR_REGS #define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t)) +#define NPCM8XX_GCR_NR_REGS (0xf80 / sizeof(uint32_t)) typedef struct NPCMGCRState { SysBusDevice parent; @@ -78,6 +79,7 @@ typedef struct NPCMGCRClass { #define TYPE_NPCM_GCR "npcm-gcr" #define TYPE_NPCM7XX_GCR "npcm7xx-gcr" +#define TYPE_NPCM8XX_GCR "npcm8xx-gcr" OBJECT_DECLARE_TYPE(NPCMGCRState, NPCMGCRClass, NPCM_GCR) #endif /* NPCM_GCR_H */ From 0ad46bbb56585fc3900f803747c485529869ca22 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:45:59 -0800 Subject: [PATCH 1833/2892] hw/misc: Store DRAM size in NPCM8XX GCR Module NPCM8XX boot block stores the DRAM size in SCRPAD_B register in GCR module. Since we don't simulate a detailed memory controller, we need to store this information directly similar to the NPCM7XX's INCTR3 register. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-9-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_gcr.c | 24 ++++++++++++++++++++++++ include/hw/misc/npcm_gcr.h | 1 + 2 files changed, 25 insertions(+) diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index ac22fb08cb..9e4a6aee61 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -280,6 +280,19 @@ static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; } +static void npcm8xx_gcr_enter_reset(Object *obj, ResetType type) +{ + NPCMGCRState *s = NPCM_GCR(obj); + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj); + + memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t)); + /* These 3 registers are at the same location in both 7xx and 8xx. */ + s->regs[NPCM8XX_GCR_PWRON] = s->reset_pwron; + s->regs[NPCM8XX_GCR_MDLR] = s->reset_mdlr; + s->regs[NPCM8XX_GCR_INTCR3] = s->reset_intcr3; + s->regs[NPCM8XX_GCR_SCRPAD_B] = s->reset_scrpad_b; +} + static void npcm_gcr_realize(DeviceState *dev, Error **errp) { ERRP_GUARD(); @@ -323,6 +336,14 @@ static void npcm_gcr_realize(DeviceState *dev, Error **errp) * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244 */ s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; + + /* + * The boot block starting from 0.0.6 for NPCM8xx SoCs stores the DRAM size + * in the SCRPAD2 registers. We need to set this field correctly since + * the initialization is skipped as we mentioned above. + * https://github.com/Nuvoton-Israel/u-boot/blob/npcm8mnx-v2019.01_tmp/board/nuvoton/arbel/arbel.c#L737 + */ + s->reset_scrpad_b = dram_size; } static void npcm_gcr_init(Object *obj) @@ -370,16 +391,19 @@ static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) c->nr_regs = NPCM7XX_GCR_NR_REGS; c->cold_reset_values = npcm7xx_cold_reset_values; + rc->phases.enter = npcm7xx_gcr_enter_reset; } static void npcm8xx_gcr_class_init(ObjectClass *klass, void *data) { NPCMGCRClass *c = NPCM_GCR_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->desc = "NPCM8xx System Global Control Registers"; c->nr_regs = NPCM8XX_GCR_NR_REGS; c->cold_reset_values = npcm8xx_cold_reset_values; + rc->phases.enter = npcm8xx_gcr_enter_reset; } static const TypeInfo npcm_gcr_info[] = { diff --git a/include/hw/misc/npcm_gcr.h b/include/hw/misc/npcm_gcr.h index 9ac76ca9ab..d81bb9afb2 100644 --- a/include/hw/misc/npcm_gcr.h +++ b/include/hw/misc/npcm_gcr.h @@ -68,6 +68,7 @@ typedef struct NPCMGCRState { uint32_t reset_pwron; uint32_t reset_mdlr; uint32_t reset_intcr3; + uint32_t reset_scrpad_b; } NPCMGCRState; typedef struct NPCMGCRClass { From ca2fd966ea90b2ca02a4eff1afc2b89e963680a1 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:00 -0800 Subject: [PATCH 1834/2892] hw/misc: Support 8-bytes memop in NPCM GCR module The NPCM8xx GCR device can be accessed with 64-bit memory operations. This patch supports that. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Reviewed-by: Philippe Mathieu-Daude Message-id: 20250219184609.1839281-10-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_gcr.c | 92 ++++++++++++++++++++++++++++++++++---------- hw/misc/trace-events | 4 +- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index 9e4a6aee61..ec16ea620e 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -200,6 +200,7 @@ static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) uint32_t reg = offset / sizeof(uint32_t); NPCMGCRState *s = opaque; NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); + uint64_t value; if (reg >= c->nr_regs) { qemu_log_mask(LOG_GUEST_ERROR, @@ -208,9 +209,21 @@ static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) return 0; } - trace_npcm_gcr_read(offset, s->regs[reg]); + switch (size) { + case 4: + value = s->regs[reg]; + break; - return s->regs[reg]; + case 8: + value = deposit64(s->regs[reg], 32, 32, s->regs[reg + 1]); + break; + + default: + g_assert_not_reached(); + } + + trace_npcm_gcr_read(offset, value); + return value; } static void npcm_gcr_write(void *opaque, hwaddr offset, @@ -230,29 +243,65 @@ static void npcm_gcr_write(void *opaque, hwaddr offset, return; } - switch (reg) { - case NPCM7XX_GCR_PDID: - case NPCM7XX_GCR_PWRON: - case NPCM7XX_GCR_INTSR: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", - __func__, offset); - return; + switch (size) { + case 4: + switch (reg) { + case NPCM7XX_GCR_PDID: + case NPCM7XX_GCR_PWRON: + case NPCM7XX_GCR_INTSR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", + __func__, offset); + return; - case NPCM7XX_GCR_RESSR: - case NPCM7XX_GCR_CP2BST: - /* Write 1 to clear */ - value = s->regs[reg] & ~value; + case NPCM7XX_GCR_RESSR: + case NPCM7XX_GCR_CP2BST: + /* Write 1 to clear */ + value = s->regs[reg] & ~value; + break; + + case NPCM7XX_GCR_RLOCKR1: + case NPCM7XX_GCR_MDLR: + /* Write 1 to set */ + value |= s->regs[reg]; + break; + }; + s->regs[reg] = value; break; - case NPCM7XX_GCR_RLOCKR1: - case NPCM7XX_GCR_MDLR: - /* Write 1 to set */ - value |= s->regs[reg]; + case 8: + s->regs[reg] = value; + s->regs[reg + 1] = extract64(v, 32, 32); break; - }; - s->regs[reg] = value; + default: + g_assert_not_reached(); + } +} + +static bool npcm_gcr_check_mem_op(void *opaque, hwaddr offset, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(opaque); + + if (offset >= c->nr_regs * sizeof(uint32_t)) { + return false; + } + + switch (size) { + case 4: + return true; + case 8: + if (offset >= NPCM8XX_GCR_SCRPAD_00 * sizeof(uint32_t) && + offset < (NPCM8XX_GCR_NR_REGS - 1) * sizeof(uint32_t)) { + return true; + } else { + return false; + } + default: + return false; + } } static const struct MemoryRegionOps npcm_gcr_ops = { @@ -261,7 +310,8 @@ static const struct MemoryRegionOps npcm_gcr_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, - .max_access_size = 4, + .max_access_size = 8, + .accepts = npcm_gcr_check_mem_op, .unaligned = false, }, }; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 0f7204a237..f25dbd6030 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -135,8 +135,8 @@ npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " valu npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 # npcm_gcr.c -npcm_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -npcm_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +npcm_gcr_read(uint64_t offset, uint64_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx64 +npcm_gcr_write(uint64_t offset, uint64_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx64 # npcm7xx_mft.c npcm7xx_mft_read(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 From c8283b0f4a7e9397da141d70e73e79341c5df2d7 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:01 -0800 Subject: [PATCH 1835/2892] hw/misc: Rename npcm7xx_clk to npcm_clk NPCM7XX and NPCM8XX have a different set of CLK registers. This commit changes the name of the clk files to be used by both NPCM7XX and NPCM8XX CLK modules. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-11-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/meson.build | 2 +- hw/misc/{npcm7xx_clk.c => npcm_clk.c} | 2 +- include/hw/arm/npcm7xx.h | 2 +- include/hw/misc/{npcm7xx_clk.h => npcm_clk.h} | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename hw/misc/{npcm7xx_clk.c => npcm_clk.c} (99%) rename include/hw/misc/{npcm7xx_clk.h => npcm_clk.h} (98%) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 554eb8df5b..edd36a334d 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -69,7 +69,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_rngc.c', )) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( - 'npcm7xx_clk.c', + 'npcm_clk.c', 'npcm_gcr.c', 'npcm7xx_mft.c', 'npcm7xx_pwm.c', diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm_clk.c similarity index 99% rename from hw/misc/npcm7xx_clk.c rename to hw/misc/npcm_clk.c index 46f907b61c..2bcb731099 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm_clk.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" -#include "hw/misc/npcm7xx_clk.h" +#include "hw/misc/npcm_clk.h" #include "hw/timer/npcm7xx_timer.h" #include "hw/qdev-clock.h" #include "migration/vmstate.h" diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 2e708471ec..e80fd91f20 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -23,7 +23,7 @@ #include "hw/gpio/npcm7xx_gpio.h" #include "hw/i2c/npcm7xx_smbus.h" #include "hw/mem/npcm7xx_mc.h" -#include "hw/misc/npcm7xx_clk.h" +#include "hw/misc/npcm_clk.h" #include "hw/misc/npcm_gcr.h" #include "hw/misc/npcm7xx_mft.h" #include "hw/misc/npcm7xx_pwm.h" diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm_clk.h similarity index 98% rename from include/hw/misc/npcm7xx_clk.h rename to include/hw/misc/npcm_clk.h index 5ed4a4672b..0aef81e10c 100644 --- a/include/hw/misc/npcm7xx_clk.h +++ b/include/hw/misc/npcm_clk.h @@ -13,8 +13,8 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ -#ifndef NPCM7XX_CLK_H -#define NPCM7XX_CLK_H +#ifndef NPCM_CLK_H +#define NPCM_CLK_H #include "exec/memory.h" #include "hw/clock.h" @@ -177,4 +177,4 @@ struct NPCM7xxCLKState { #define TYPE_NPCM7XX_CLK "npcm7xx-clk" OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxCLKState, NPCM7XX_CLK) -#endif /* NPCM7XX_CLK_H */ +#endif /* NPCM_CLK_H */ From ca6d6a94f450f5fba92626704a5758cf4bb4a210 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:02 -0800 Subject: [PATCH 1836/2892] hw/misc: Move NPCM7XX CLK to NPCM CLK A lot of NPCM7XX and NPCM8XX CLK modules share the same code, this commit moves the NPCM7XX CLK to NPCM CLK for these properties. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-12-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_clk.c | 106 +++++++++++++++++++++---------------- hw/misc/trace-events | 6 +-- include/hw/arm/npcm7xx.h | 2 +- include/hw/misc/npcm_clk.h | 24 +++++---- 4 files changed, 77 insertions(+), 61 deletions(-) diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index 2bcb731099..0ecf0df3bb 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -198,7 +198,7 @@ static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg) } } -static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk) +static void npcm7xx_clk_update_all_plls(NPCMCLKState *clk) { int i; @@ -207,7 +207,7 @@ static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk) } } -static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk) +static void npcm7xx_clk_update_all_sels(NPCMCLKState *clk) { int i; @@ -216,7 +216,7 @@ static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk) } } -static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk) +static void npcm7xx_clk_update_all_dividers(NPCMCLKState *clk) { int i; @@ -225,7 +225,7 @@ static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk) } } -static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState *clk) +static void npcm7xx_clk_update_all_clocks(NPCMCLKState *clk) { clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ); npcm7xx_clk_update_all_plls(clk); @@ -635,7 +635,7 @@ static void npcm7xx_clk_divider_init(Object *obj) } static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll, - NPCM7xxCLKState *clk, const PLLInitInfo *init_info) + NPCMCLKState *clk, const PLLInitInfo *init_info) { pll->name = init_info->name; pll->clk = clk; @@ -647,7 +647,7 @@ static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll, } static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel, - NPCM7xxCLKState *clk, const SELInitInfo *init_info) + NPCMCLKState *clk, const SELInitInfo *init_info) { int input_size = init_info->input_size; @@ -664,7 +664,7 @@ static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel, } static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div, - NPCM7xxCLKState *clk, const DividerInitInfo *init_info) + NPCMCLKState *clk, const DividerInitInfo *init_info) { div->name = init_info->name; div->clk = clk; @@ -683,7 +683,7 @@ static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div, } } -static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type, +static Clock *npcm7xx_get_clock(NPCMCLKState *clk, ClockSrcType type, int index) { switch (type) { @@ -700,7 +700,7 @@ static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type, } } -static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk) +static void npcm7xx_connect_clocks(NPCMCLKState *clk) { int i, j; Clock *src; @@ -724,10 +724,10 @@ static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk) } } -static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size) +static uint64_t npcm_clk_read(void *opaque, hwaddr offset, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxCLKState *s = opaque; + NPCMCLKState *s = opaque; int64_t now_ns; uint32_t value = 0; @@ -766,19 +766,19 @@ static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size) break; }; - trace_npcm7xx_clk_read(offset, value); + trace_npcm_clk_read(offset, value); return value; } -static void npcm7xx_clk_write(void *opaque, hwaddr offset, +static void npcm_clk_write(void *opaque, hwaddr offset, uint64_t v, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxCLKState *s = opaque; + NPCMCLKState *s = opaque; uint32_t value = v; - trace_npcm7xx_clk_write(offset, value); + trace_npcm_clk_write(offset, value); if (reg >= NPCM7XX_CLK_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, @@ -842,7 +842,7 @@ static void npcm7xx_clk_write(void *opaque, hwaddr offset, static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n, int level) { - NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque); + NPCMCLKState *clk = NPCM_CLK(opaque); uint32_t rcr; g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS); @@ -856,9 +856,9 @@ static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n, } } -static const struct MemoryRegionOps npcm7xx_clk_ops = { - .read = npcm7xx_clk_read, - .write = npcm7xx_clk_write, +static const struct MemoryRegionOps npcm_clk_ops = { + .read = npcm_clk_read, + .write = npcm_clk_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, @@ -867,9 +867,9 @@ static const struct MemoryRegionOps npcm7xx_clk_ops = { }, }; -static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) +static void npcm_clk_enter_reset(Object *obj, ResetType type) { - NPCM7xxCLKState *s = NPCM7XX_CLK(obj); + NPCMCLKState *s = NPCM_CLK(obj); QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); @@ -882,7 +882,7 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) */ } -static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) +static void npcm7xx_clk_init_clock_hierarchy(NPCMCLKState *s) { int i; @@ -918,19 +918,19 @@ static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ); } -static void npcm7xx_clk_init(Object *obj) +static void npcm_clk_init(Object *obj) { - NPCM7xxCLKState *s = NPCM7XX_CLK(obj); + NPCMCLKState *s = NPCM_CLK(obj); - memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s, - TYPE_NPCM7XX_CLK, 4 * KiB); + memory_region_init_io(&s->iomem, obj, &npcm_clk_ops, s, + TYPE_NPCM_CLK, 4 * KiB); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static int npcm7xx_clk_post_load(void *opaque, int version_id) +static int npcm_clk_post_load(void *opaque, int version_id) { if (version_id >= 1) { - NPCM7xxCLKState *clk = opaque; + NPCMCLKState *clk = opaque; npcm7xx_clk_update_all_clocks(clk); } @@ -938,10 +938,10 @@ static int npcm7xx_clk_post_load(void *opaque, int version_id) return 0; } -static void npcm7xx_clk_realize(DeviceState *dev, Error **errp) +static void npcm_clk_realize(DeviceState *dev, Error **errp) { int i; - NPCM7xxCLKState *s = NPCM7XX_CLK(dev); + NPCMCLKState *s = NPCM_CLK(dev); qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset, NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS); @@ -996,15 +996,15 @@ static const VMStateDescription vmstate_npcm7xx_clk_divider = { }, }; -static const VMStateDescription vmstate_npcm7xx_clk = { - .name = "npcm7xx-clk", - .version_id = 1, - .minimum_version_id = 1, - .post_load = npcm7xx_clk_post_load, +static const VMStateDescription vmstate_npcm_clk = { + .name = "npcm-clk", + .version_id = 2, + .minimum_version_id = 2, + .post_load = npcm_clk_post_load, .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS), - VMSTATE_INT64(ref_ns, NPCM7xxCLKState), - VMSTATE_CLOCK(clkref, NPCM7xxCLKState), + VMSTATE_UINT32_ARRAY(regs, NPCMCLKState, NPCM_CLK_MAX_NR_REGS), + VMSTATE_INT64(ref_ns, NPCMCLKState), + VMSTATE_CLOCK(clkref, NPCMCLKState), VMSTATE_END_OF_LIST(), }, }; @@ -1033,17 +1033,23 @@ static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_npcm7xx_clk_divider; } -static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) +static void npcm_clk_class_init(ObjectClass *klass, void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS); + dc->vmsd = &vmstate_npcm_clk; + dc->realize = npcm_clk_realize; + rc->phases.enter = npcm_clk_enter_reset; +} +static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM_CLK_MAX_NR_REGS); + QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END != NPCM7XX_CLK_NR_REGS); dc->desc = "NPCM7xx Clock Control Registers"; - dc->vmsd = &vmstate_npcm7xx_clk; - dc->realize = npcm7xx_clk_realize; - rc->phases.enter = npcm7xx_clk_enter_reset; } static const TypeInfo npcm7xx_clk_pll_info = { @@ -1070,11 +1076,18 @@ static const TypeInfo npcm7xx_clk_divider_info = { .class_init = npcm7xx_clk_divider_class_init, }; +static const TypeInfo npcm_clk_info = { + .name = TYPE_NPCM_CLK, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMCLKState), + .instance_init = npcm_clk_init, + .class_init = npcm_clk_class_init, + .abstract = true, +}; + static const TypeInfo npcm7xx_clk_info = { .name = TYPE_NPCM7XX_CLK, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(NPCM7xxCLKState), - .instance_init = npcm7xx_clk_init, + .parent = TYPE_NPCM_CLK, .class_init = npcm7xx_clk_class_init, }; @@ -1083,6 +1096,7 @@ static void npcm7xx_clk_register_type(void) type_register_static(&npcm7xx_clk_pll_info); type_register_static(&npcm7xx_clk_sel_info); type_register_static(&npcm7xx_clk_divider_info); + type_register_static(&npcm_clk_info); type_register_static(&npcm7xx_clk_info); } type_init(npcm7xx_clk_register_type); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index f25dbd6030..4383808d7a 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -130,9 +130,9 @@ mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, const char *name, uint64_t val) "reg=0x%"PRIx64 " [%s] val=0x%"PRIx64 mos6522_read(uint64_t addr, const char *name, unsigned val) "reg=0x%"PRIx64 " [%s] val=0x%x" -# npcm7xx_clk.c -npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +# npcm_clk.c +npcm_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +npcm_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 # npcm_gcr.c npcm_gcr_read(uint64_t offset, uint64_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx64 diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index e80fd91f20..56536565b7 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -90,7 +90,7 @@ struct NPCM7xxState { MemoryRegion *dram; NPCMGCRState gcr; - NPCM7xxCLKState clk; + NPCMCLKState clk; NPCM7xxTimerCtrlState tim[3]; NPCM7xxADCState adc; NPCM7xxPWMState pwm[NPCM7XX_NR_PWM_MODULES]; diff --git a/include/hw/misc/npcm_clk.h b/include/hw/misc/npcm_clk.h index 0aef81e10c..db03b46a52 100644 --- a/include/hw/misc/npcm_clk.h +++ b/include/hw/misc/npcm_clk.h @@ -20,11 +20,12 @@ #include "hw/clock.h" #include "hw/sysbus.h" -/* - * Number of registers in our device state structure. Don't change this without - * incrementing the version_id in the vmstate. - */ #define NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t)) +/* + * Number of maximum registers in NPCM device state structure. Don't change + * this without incrementing the version_id in the vmstate. + */ +#define NPCM_CLK_MAX_NR_REGS NPCM7XX_CLK_NR_REGS #define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in" @@ -80,7 +81,7 @@ typedef enum NPCM7xxClockDivider { NPCM7XX_CLOCK_NR_DIVIDERS, } NPCM7xxClockConverter; -typedef struct NPCM7xxCLKState NPCM7xxCLKState; +typedef struct NPCMCLKState NPCMCLKState; /** * struct NPCM7xxClockPLLState - A PLL module in CLK module. @@ -94,7 +95,7 @@ typedef struct NPCM7xxClockPLLState { DeviceState parent; const char *name; - NPCM7xxCLKState *clk; + NPCMCLKState *clk; Clock *clock_in; Clock *clock_out; @@ -115,7 +116,7 @@ typedef struct NPCM7xxClockSELState { DeviceState parent; const char *name; - NPCM7xxCLKState *clk; + NPCMCLKState *clk; uint8_t input_size; Clock *clock_in[NPCM7XX_CLK_SEL_MAX_INPUT]; Clock *clock_out; @@ -140,7 +141,7 @@ typedef struct NPCM7xxClockDividerState { DeviceState parent; const char *name; - NPCM7xxCLKState *clk; + NPCMCLKState *clk; Clock *clock_in; Clock *clock_out; @@ -155,7 +156,7 @@ typedef struct NPCM7xxClockDividerState { }; } NPCM7xxClockDividerState; -struct NPCM7xxCLKState { +struct NPCMCLKState { SysBusDevice parent; MemoryRegion iomem; @@ -165,7 +166,7 @@ struct NPCM7xxCLKState { NPCM7xxClockSELState sels[NPCM7XX_CLOCK_NR_SELS]; NPCM7xxClockDividerState dividers[NPCM7XX_CLOCK_NR_DIVIDERS]; - uint32_t regs[NPCM7XX_CLK_NR_REGS]; + uint32_t regs[NPCM_CLK_MAX_NR_REGS]; /* Time reference for SECCNT and CNTR25M, initialized by power on reset */ int64_t ref_ns; @@ -174,7 +175,8 @@ struct NPCM7xxCLKState { Clock *clkref; }; +#define TYPE_NPCM_CLK "npcm-clk" +OBJECT_DECLARE_SIMPLE_TYPE(NPCMCLKState, NPCM_CLK) #define TYPE_NPCM7XX_CLK "npcm7xx-clk" -OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxCLKState, NPCM7XX_CLK) #endif /* NPCM_CLK_H */ From cf76c4e174e128397763c4ec98b0aa3345bab3be Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:03 -0800 Subject: [PATCH 1837/2892] hw/misc: Add nr_regs and cold_reset_values to NPCM CLK These 2 values are different between NPCM7XX and NPCM8XX CLKs. So we add them to the class and assign different values to them. Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-13-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_clk.c | 18 ++++++++++++------ hw/misc/npcm_gcr.c | 2 ++ include/hw/misc/npcm_clk.h | 9 ++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index 0ecf0df3bb..78144b14e3 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -81,7 +81,7 @@ enum NPCM7xxCLKRegisters { * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on * core domain reset, but this reset type is not yet supported by QEMU. */ -static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = { +static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_CLK_NR_REGS] = { [NPCM7XX_CLK_CLKEN1] = 0xffffffff, [NPCM7XX_CLK_CLKSEL] = 0x004aaaaa, [NPCM7XX_CLK_CLKDIV1] = 0x5413f855, @@ -728,10 +728,11 @@ static uint64_t npcm_clk_read(void *opaque, hwaddr offset, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); NPCMCLKState *s = opaque; + NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); int64_t now_ns; uint32_t value = 0; - if (reg >= NPCM7XX_CLK_NR_REGS) { + if (reg >= c->nr_regs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04" HWADDR_PRIx " out of range\n", __func__, offset); @@ -776,11 +777,12 @@ static void npcm_clk_write(void *opaque, hwaddr offset, { uint32_t reg = offset / sizeof(uint32_t); NPCMCLKState *s = opaque; + NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); uint32_t value = v; trace_npcm_clk_write(offset, value); - if (reg >= NPCM7XX_CLK_NR_REGS) { + if (reg >= c->nr_regs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04" HWADDR_PRIx " out of range\n", __func__, offset); @@ -870,10 +872,10 @@ static const struct MemoryRegionOps npcm_clk_ops = { static void npcm_clk_enter_reset(Object *obj, ResetType type) { NPCMCLKState *s = NPCM_CLK(obj); + NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); - QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - - memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); + g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t)); + memcpy(s->regs, c->cold_reset_values, sizeof(s->regs)); s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); npcm7xx_clk_update_all_clocks(s); /* @@ -1045,11 +1047,14 @@ static void npcm_clk_class_init(ObjectClass *klass, void *data) static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) { + NPCMCLKClass *c = NPCM_CLK_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM_CLK_MAX_NR_REGS); QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END != NPCM7XX_CLK_NR_REGS); dc->desc = "NPCM7xx Clock Control Registers"; + c->nr_regs = NPCM7XX_CLK_NR_REGS; + c->cold_reset_values = npcm7xx_cold_reset_values; } static const TypeInfo npcm7xx_clk_pll_info = { @@ -1081,6 +1086,7 @@ static const TypeInfo npcm_clk_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(NPCMCLKState), .instance_init = npcm_clk_init, + .class_size = sizeof(NPCMCLKClass), .class_init = npcm_clk_class_init, .abstract = true, }; diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index ec16ea620e..4e8ce2cb89 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -215,6 +215,7 @@ static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) break; case 8: + g_assert(!(reg & 1)); value = deposit64(s->regs[reg], 32, 32, s->regs[reg + 1]); break; @@ -270,6 +271,7 @@ static void npcm_gcr_write(void *opaque, hwaddr offset, break; case 8: + g_assert(!(reg & 1)); s->regs[reg] = value; s->regs[reg + 1] = extract64(v, 32, 32); break; diff --git a/include/hw/misc/npcm_clk.h b/include/hw/misc/npcm_clk.h index db03b46a52..f47614ac8d 100644 --- a/include/hw/misc/npcm_clk.h +++ b/include/hw/misc/npcm_clk.h @@ -175,8 +175,15 @@ struct NPCMCLKState { Clock *clkref; }; +typedef struct NPCMCLKClass { + SysBusDeviceClass parent; + + size_t nr_regs; + const uint32_t *cold_reset_values; +} NPCMCLKClass; + #define TYPE_NPCM_CLK "npcm-clk" -OBJECT_DECLARE_SIMPLE_TYPE(NPCMCLKState, NPCM_CLK) +OBJECT_DECLARE_TYPE(NPCMCLKState, NPCMCLKClass, NPCM_CLK) #define TYPE_NPCM7XX_CLK "npcm7xx-clk" #endif /* NPCM_CLK_H */ From 4e67d50deaf3132f392266e7251cf7ce17be8fa4 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:04 -0800 Subject: [PATCH 1838/2892] hw/misc: Support NPCM8XX CLK Module Registers NPCM8XX adds a few new registers and have a different set of reset values to the CLK modules. This patch supports them. This patch doesn't support the new clock values generated by these registers. Currently no modules use these new clock values so they are not necessary at this point. Implementation of these clocks might be required when implementing these modules. Reviewed-by: Titus Rwantare Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-14-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/misc/npcm_clk.c | 117 +++++++++++++++++++++++++++++++++++-- include/hw/misc/npcm_clk.h | 10 +++- 2 files changed, 119 insertions(+), 8 deletions(-) diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index 78144b14e3..d1f29759d5 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -1,5 +1,5 @@ /* - * Nuvoton NPCM7xx Clock Control Registers. + * Nuvoton NPCM7xx/8xx Clock Control Registers. * * Copyright 2020 Google LLC * @@ -72,7 +72,57 @@ enum NPCM7xxCLKRegisters { NPCM7XX_CLK_AHBCKFI, NPCM7XX_CLK_SECCNT, NPCM7XX_CLK_CNTR25M, - NPCM7XX_CLK_REGS_END, +}; + +enum NPCM8xxCLKRegisters { + NPCM8XX_CLK_CLKEN1, + NPCM8XX_CLK_CLKSEL, + NPCM8XX_CLK_CLKDIV1, + NPCM8XX_CLK_PLLCON0, + NPCM8XX_CLK_PLLCON1, + NPCM8XX_CLK_SWRSTR, + NPCM8XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t), + NPCM8XX_CLK_IPSRST2, + NPCM8XX_CLK_CLKEN2, + NPCM8XX_CLK_CLKDIV2, + NPCM8XX_CLK_CLKEN3, + NPCM8XX_CLK_IPSRST3, + NPCM8XX_CLK_WD0RCR, + NPCM8XX_CLK_WD1RCR, + NPCM8XX_CLK_WD2RCR, + NPCM8XX_CLK_SWRSTC1, + NPCM8XX_CLK_SWRSTC2, + NPCM8XX_CLK_SWRSTC3, + NPCM8XX_CLK_TIPRSTC, + NPCM8XX_CLK_PLLCON2, + NPCM8XX_CLK_CLKDIV3, + NPCM8XX_CLK_CORSTC, + NPCM8XX_CLK_PLLCONG, + NPCM8XX_CLK_AHBCKFI, + NPCM8XX_CLK_SECCNT, + NPCM8XX_CLK_CNTR25M, + /* Registers unique to NPCM8XX SoC */ + NPCM8XX_CLK_CLKEN4, + NPCM8XX_CLK_IPSRST4, + NPCM8XX_CLK_BUSTO, + NPCM8XX_CLK_CLKDIV4, + NPCM8XX_CLK_WD0RCRB, + NPCM8XX_CLK_WD1RCRB, + NPCM8XX_CLK_WD2RCRB, + NPCM8XX_CLK_SWRSTC1B, + NPCM8XX_CLK_SWRSTC2B, + NPCM8XX_CLK_SWRSTC3B, + NPCM8XX_CLK_TIPRSTCB, + NPCM8XX_CLK_CORSTCB, + NPCM8XX_CLK_IPSRSTDIS1, + NPCM8XX_CLK_IPSRSTDIS2, + NPCM8XX_CLK_IPSRSTDIS3, + NPCM8XX_CLK_IPSRSTDIS4, + NPCM8XX_CLK_CLKENDIS1, + NPCM8XX_CLK_CLKENDIS2, + NPCM8XX_CLK_CLKENDIS3, + NPCM8XX_CLK_CLKENDIS4, + NPCM8XX_CLK_THRTL_CNT, }; /* @@ -103,6 +153,46 @@ static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_CLK_NR_REGS] = { [NPCM7XX_CLK_AHBCKFI] = 0x000000c8, }; +/* + * These reset values were taken from version 0.92 of the NPCM8xx data sheet. + */ +static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_CLK_NR_REGS] = { + [NPCM8XX_CLK_CLKEN1] = 0xffffffff, + [NPCM8XX_CLK_CLKSEL] = 0x154aaaaa, + [NPCM8XX_CLK_CLKDIV1] = 0x5413f855, + [NPCM8XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI, + [NPCM8XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI, + [NPCM8XX_CLK_IPSRST1] = 0x00001000, + [NPCM8XX_CLK_IPSRST2] = 0x80000000, + [NPCM8XX_CLK_CLKEN2] = 0xffffffff, + [NPCM8XX_CLK_CLKDIV2] = 0xaa4f8f9f, + [NPCM8XX_CLK_CLKEN3] = 0xffffffff, + [NPCM8XX_CLK_IPSRST3] = 0x03000000, + [NPCM8XX_CLK_WD0RCR] = 0xffffffff, + [NPCM8XX_CLK_WD1RCR] = 0xffffffff, + [NPCM8XX_CLK_WD2RCR] = 0xffffffff, + [NPCM8XX_CLK_SWRSTC1] = 0x00000003, + [NPCM8XX_CLK_SWRSTC2] = 0x00000001, + [NPCM8XX_CLK_SWRSTC3] = 0x00000001, + [NPCM8XX_CLK_TIPRSTC] = 0x00000001, + [NPCM8XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI, + [NPCM8XX_CLK_CLKDIV3] = 0x00009100, + [NPCM8XX_CLK_CORSTC] = 0x04000003, + [NPCM8XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI, + [NPCM8XX_CLK_AHBCKFI] = 0x000000c8, + [NPCM8XX_CLK_CLKEN4] = 0xffffffff, + [NPCM8XX_CLK_CLKDIV4] = 0x70009000, + [NPCM8XX_CLK_IPSRST4] = 0x02000000, + [NPCM8XX_CLK_WD0RCRB] = 0xfffffe71, + [NPCM8XX_CLK_WD1RCRB] = 0xfffffe71, + [NPCM8XX_CLK_WD2RCRB] = 0xfffffe71, + [NPCM8XX_CLK_SWRSTC1B] = 0xfffffe71, + [NPCM8XX_CLK_SWRSTC2B] = 0xfffffe71, + [NPCM8XX_CLK_SWRSTC3B] = 0xfffffe71, + [NPCM8XX_CLK_TIPRSTCB] = 0xfffffe71, + [NPCM8XX_CLK_CORSTCB] = 0xfffffe71, +}; + /* The number of watchdogs that can trigger a reset. */ #define NPCM7XX_NR_WATCHDOGS (3) @@ -1000,8 +1090,8 @@ static const VMStateDescription vmstate_npcm7xx_clk_divider = { static const VMStateDescription vmstate_npcm_clk = { .name = "npcm-clk", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .post_load = npcm_clk_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, NPCMCLKState, NPCM_CLK_MAX_NR_REGS), @@ -1050,13 +1140,21 @@ static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) NPCMCLKClass *c = NPCM_CLK_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM_CLK_MAX_NR_REGS); - QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END != NPCM7XX_CLK_NR_REGS); dc->desc = "NPCM7xx Clock Control Registers"; c->nr_regs = NPCM7XX_CLK_NR_REGS; c->cold_reset_values = npcm7xx_cold_reset_values; } +static void npcm8xx_clk_class_init(ObjectClass *klass, void *data) +{ + NPCMCLKClass *c = NPCM_CLK_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM8xx Clock Control Registers"; + c->nr_regs = NPCM8XX_CLK_NR_REGS; + c->cold_reset_values = npcm8xx_cold_reset_values; +} + static const TypeInfo npcm7xx_clk_pll_info = { .name = TYPE_NPCM7XX_CLOCK_PLL, .parent = TYPE_DEVICE, @@ -1097,6 +1195,12 @@ static const TypeInfo npcm7xx_clk_info = { .class_init = npcm7xx_clk_class_init, }; +static const TypeInfo npcm8xx_clk_info = { + .name = TYPE_NPCM8XX_CLK, + .parent = TYPE_NPCM_CLK, + .class_init = npcm8xx_clk_class_init, +}; + static void npcm7xx_clk_register_type(void) { type_register_static(&npcm7xx_clk_pll_info); @@ -1104,5 +1208,6 @@ static void npcm7xx_clk_register_type(void) type_register_static(&npcm7xx_clk_divider_info); type_register_static(&npcm_clk_info); type_register_static(&npcm7xx_clk_info); + type_register_static(&npcm8xx_clk_info); } type_init(npcm7xx_clk_register_type); diff --git a/include/hw/misc/npcm_clk.h b/include/hw/misc/npcm_clk.h index f47614ac8d..8fa1e14bdd 100644 --- a/include/hw/misc/npcm_clk.h +++ b/include/hw/misc/npcm_clk.h @@ -1,5 +1,5 @@ /* - * Nuvoton NPCM7xx Clock Control Registers. + * Nuvoton NPCM7xx/8xx Clock Control Registers. * * Copyright 2020 Google LLC * @@ -21,11 +21,12 @@ #include "hw/sysbus.h" #define NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t)) +#define NPCM8XX_CLK_NR_REGS (0xc4 / sizeof(uint32_t)) /* * Number of maximum registers in NPCM device state structure. Don't change * this without incrementing the version_id in the vmstate. */ -#define NPCM_CLK_MAX_NR_REGS NPCM7XX_CLK_NR_REGS +#define NPCM_CLK_MAX_NR_REGS NPCM8XX_CLK_NR_REGS #define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in" @@ -162,6 +163,10 @@ struct NPCMCLKState { MemoryRegion iomem; /* Clock converters */ + /* + * TODO: Implement unique clock converters for NPCM8xx. + * NPCM8xx adds a few more clock outputs. + */ NPCM7xxClockPLLState plls[NPCM7XX_CLOCK_NR_PLLS]; NPCM7xxClockSELState sels[NPCM7XX_CLOCK_NR_SELS]; NPCM7xxClockDividerState dividers[NPCM7XX_CLOCK_NR_DIVIDERS]; @@ -185,5 +190,6 @@ typedef struct NPCMCLKClass { #define TYPE_NPCM_CLK "npcm-clk" OBJECT_DECLARE_TYPE(NPCMCLKState, NPCMCLKClass, NPCM_CLK) #define TYPE_NPCM7XX_CLK "npcm7xx-clk" +#define TYPE_NPCM8XX_CLK "npcm8xx-clk" #endif /* NPCM_CLK_H */ From 3d107d36f99c6eec8c8b3cb6bc387b0fcc69d7d9 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:05 -0800 Subject: [PATCH 1839/2892] hw/net: Add NPCM8XX PCS Module The PCS exists in NPCM8XX's GMAC1 and is used to control the SGMII PHY. This implementation contains all the default registers and the soft reset feature that are required to load the Linux kernel driver. Further features have not been implemented yet. Signed-off-by: Hao Wu Reviewed-by: Peter Maydell Message-id: 20250219184609.1839281-15-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/net/meson.build | 1 + hw/net/npcm_pcs.c | 410 ++++++++++++++++++++++++++++++++++++++ hw/net/trace-events | 4 +- include/hw/net/npcm_pcs.h | 42 ++++ 4 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 hw/net/npcm_pcs.c create mode 100644 include/hw/net/npcm_pcs.h diff --git a/hw/net/meson.build b/hw/net/meson.build index 3bb5d749a8..e6759e26ca 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -40,6 +40,7 @@ system_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c')) system_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c')) system_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c', 'npcm_gmac.c')) +system_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm_pcs.c')) system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_llan.c')) diff --git a/hw/net/npcm_pcs.c b/hw/net/npcm_pcs.c new file mode 100644 index 0000000000..ce5034e234 --- /dev/null +++ b/hw/net/npcm_pcs.c @@ -0,0 +1,410 @@ +/* + * Nuvoton NPCM8xx PCS Module + * + * Copyright 2022 Google LLC + * + * 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. + */ + +/* + * Disclaimer: + * Currently we only implemented the default values of the registers and + * the soft reset feature. These are required to boot up the GMAC module + * in Linux kernel for NPCM845 boards. Other functionalities are not modeled. + */ + +#include "qemu/osdep.h" + +#include "exec/hwaddr.h" +#include "hw/registerfields.h" +#include "hw/net/npcm_pcs.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/units.h" +#include "trace.h" + +#define NPCM_PCS_IND_AC_BA 0x1fe +#define NPCM_PCS_IND_SR_CTL 0x1e00 +#define NPCM_PCS_IND_SR_MII 0x1f00 +#define NPCM_PCS_IND_SR_TIM 0x1f07 +#define NPCM_PCS_IND_VR_MII 0x1f80 + +REG16(NPCM_PCS_SR_CTL_ID1, 0x08) +REG16(NPCM_PCS_SR_CTL_ID2, 0x0a) +REG16(NPCM_PCS_SR_CTL_STS, 0x10) + +REG16(NPCM_PCS_SR_MII_CTRL, 0x00) +REG16(NPCM_PCS_SR_MII_STS, 0x02) +REG16(NPCM_PCS_SR_MII_DEV_ID1, 0x04) +REG16(NPCM_PCS_SR_MII_DEV_ID2, 0x06) +REG16(NPCM_PCS_SR_MII_AN_ADV, 0x08) +REG16(NPCM_PCS_SR_MII_LP_BABL, 0x0a) +REG16(NPCM_PCS_SR_MII_AN_EXPN, 0x0c) +REG16(NPCM_PCS_SR_MII_EXT_STS, 0x1e) + +REG16(NPCM_PCS_SR_TIM_SYNC_ABL, 0x10) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x12) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0x14) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x16) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0x18) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x1a) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0x1c) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x1e) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0x20) + +REG16(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x000) +REG16(NPCM_PCS_VR_MII_AN_CTRL, 0x002) +REG16(NPCM_PCS_VR_MII_AN_INTR_STS, 0x004) +REG16(NPCM_PCS_VR_MII_TC, 0x006) +REG16(NPCM_PCS_VR_MII_DBG_CTRL, 0x00a) +REG16(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x00c) +REG16(NPCM_PCS_VR_MII_EEE_TXTIMER, 0x010) +REG16(NPCM_PCS_VR_MII_EEE_RXTIMER, 0x012) +REG16(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0x014) +REG16(NPCM_PCS_VR_MII_EEE_MCTRL1, 0x016) +REG16(NPCM_PCS_VR_MII_DIG_STS, 0x020) +REG16(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0x022) +REG16(NPCM_PCS_VR_MII_MISC_STS, 0x030) +REG16(NPCM_PCS_VR_MII_RX_LSTS, 0x040) +REG16(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x070) +REG16(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x074) +REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x07a) +REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0x07c) +REG16(NPCM_PCS_VR_MII_MP_TX_STS, 0x090) +REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0b0) +REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x0b2) +REG16(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x0ba) +REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0f0) +REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0f2) +REG16(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x110) +REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0x126) +REG16(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x130) +REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0x132) +REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0x134) +REG16(NPCM_PCS_VR_MII_DIG_CTRL2, 0x1c2) +REG16(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0x1c4) + +/* Register Fields */ +#define NPCM_PCS_SR_MII_CTRL_RST BIT(15) + +static const uint16_t npcm_pcs_sr_ctl_cold_reset_values[NPCM_PCS_NR_SR_CTLS] = { + [R_NPCM_PCS_SR_CTL_ID1] = 0x699e, + [R_NPCM_PCS_SR_CTL_STS] = 0x8000, +}; + +static const uint16_t npcm_pcs_sr_mii_cold_reset_values[NPCM_PCS_NR_SR_MIIS] = { + [R_NPCM_PCS_SR_MII_CTRL] = 0x1140, + [R_NPCM_PCS_SR_MII_STS] = 0x0109, + [R_NPCM_PCS_SR_MII_DEV_ID1] = 0x699e, + [R_NPCM_PCS_SR_MII_DEV_ID2] = 0xced0, + [R_NPCM_PCS_SR_MII_AN_ADV] = 0x0020, + [R_NPCM_PCS_SR_MII_EXT_STS] = 0xc000, +}; + +static const uint16_t npcm_pcs_sr_tim_cold_reset_values[NPCM_PCS_NR_SR_TIMS] = { + [R_NPCM_PCS_SR_TIM_SYNC_ABL] = 0x0003, + [R_NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR] = 0x0038, + [R_NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR] = 0x0038, + [R_NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR] = 0x0058, + [R_NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR] = 0x0048, +}; + +static const uint16_t npcm_pcs_vr_mii_cold_reset_values[NPCM_PCS_NR_VR_MIIS] = { + [R_NPCM_PCS_VR_MII_MMD_DIG_CTRL1] = 0x2400, + [R_NPCM_PCS_VR_MII_AN_INTR_STS] = 0x000a, + [R_NPCM_PCS_VR_MII_EEE_MCTRL0] = 0x899c, + [R_NPCM_PCS_VR_MII_DIG_STS] = 0x0010, + [R_NPCM_PCS_VR_MII_MP_TX_BSTCTRL0] = 0x000a, + [R_NPCM_PCS_VR_MII_MP_TX_LVLCTRL0] = 0x007f, + [R_NPCM_PCS_VR_MII_MP_TX_GENCTRL0] = 0x0001, + [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL0] = 0x0100, + [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL1] = 0x1100, + [R_NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0] = 0x000e, + [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL0] = 0x0100, + [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL1] = 0x0032, + [R_NPCM_PCS_VR_MII_MP_MPLL_STS] = 0x0001, + [R_NPCM_PCS_VR_MII_MP_LVL_CTRL] = 0x0019, +}; + +static void npcm_pcs_soft_reset(NPCMPCSState *s) +{ + memcpy(s->sr_ctl, npcm_pcs_sr_ctl_cold_reset_values, + NPCM_PCS_NR_SR_CTLS * sizeof(uint16_t)); + memcpy(s->sr_mii, npcm_pcs_sr_mii_cold_reset_values, + NPCM_PCS_NR_SR_MIIS * sizeof(uint16_t)); + memcpy(s->sr_tim, npcm_pcs_sr_tim_cold_reset_values, + NPCM_PCS_NR_SR_TIMS * sizeof(uint16_t)); + memcpy(s->vr_mii, npcm_pcs_vr_mii_cold_reset_values, + NPCM_PCS_NR_VR_MIIS * sizeof(uint16_t)); +} + +static uint16_t npcm_pcs_read_sr_ctl(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_CTLS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_CTL read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->sr_ctl[regno]; +} + +static uint16_t npcm_pcs_read_sr_mii(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_MII read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->sr_mii[regno]; +} + +static uint16_t npcm_pcs_read_sr_tim(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_TIMS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_TIM read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->sr_tim[regno]; +} + +static uint16_t npcm_pcs_read_vr_mii(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_VR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VR_MII read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->vr_mii[regno]; +} + +static void npcm_pcs_write_sr_ctl(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_CTLS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_CTL write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->sr_ctl[regno] = v; +} + +static void npcm_pcs_write_sr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_MII write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->sr_mii[regno] = v; + + if ((offset == A_NPCM_PCS_SR_MII_CTRL) && (v & NPCM_PCS_SR_MII_CTRL_RST)) { + /* Trigger a soft reset */ + npcm_pcs_soft_reset(s); + } +} + +static void npcm_pcs_write_sr_tim(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_TIMS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_TIM write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->sr_tim[regno] = v; +} + +static void npcm_pcs_write_vr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_VR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VR_MII write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->vr_mii[regno] = v; +} + +static uint64_t npcm_pcs_read(void *opaque, hwaddr offset, unsigned size) +{ + NPCMPCSState *s = opaque; + uint16_t v = 0; + + if (offset == NPCM_PCS_IND_AC_BA) { + v = s->indirect_access_base; + } else { + switch (s->indirect_access_base) { + case NPCM_PCS_IND_SR_CTL: + v = npcm_pcs_read_sr_ctl(s, offset); + break; + + case NPCM_PCS_IND_SR_MII: + v = npcm_pcs_read_sr_mii(s, offset); + break; + + case NPCM_PCS_IND_SR_TIM: + v = npcm_pcs_read_sr_tim(s, offset); + break; + + case NPCM_PCS_IND_VR_MII: + v = npcm_pcs_read_vr_mii(s, offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read with invalid indirect address base: 0x%" + PRIx16 "\n", DEVICE(s)->canonical_path, + s->indirect_access_base); + } + } + + trace_npcm_pcs_reg_read(DEVICE(s)->canonical_path, s->indirect_access_base, + offset, v); + return v; +} + +static void npcm_pcs_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + NPCMPCSState *s = opaque; + + trace_npcm_pcs_reg_write(DEVICE(s)->canonical_path, s->indirect_access_base, + offset, v); + if (offset == NPCM_PCS_IND_AC_BA) { + s->indirect_access_base = v; + } else { + switch (s->indirect_access_base) { + case NPCM_PCS_IND_SR_CTL: + npcm_pcs_write_sr_ctl(s, offset, v); + break; + + case NPCM_PCS_IND_SR_MII: + npcm_pcs_write_sr_mii(s, offset, v); + break; + + case NPCM_PCS_IND_SR_TIM: + npcm_pcs_write_sr_tim(s, offset, v); + break; + + case NPCM_PCS_IND_VR_MII: + npcm_pcs_write_vr_mii(s, offset, v); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write with invalid indirect address base: 0x%02" + PRIx16 "\n", DEVICE(s)->canonical_path, + s->indirect_access_base); + } + } +} + +static void npcm_pcs_enter_reset(Object *obj, ResetType type) +{ + NPCMPCSState *s = NPCM_PCS(obj); + + npcm_pcs_soft_reset(s); +} + +static const struct MemoryRegionOps npcm_pcs_ops = { + .read = npcm_pcs_read, + .write = npcm_pcs_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 2, + .max_access_size = 2, + .unaligned = false, + }, +}; + +static void npcm_pcs_realize(DeviceState *dev, Error **errp) +{ + NPCMPCSState *pcs = NPCM_PCS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&pcs->iomem, OBJECT(pcs), &npcm_pcs_ops, pcs, + TYPE_NPCM_PCS, 8 * KiB); + sysbus_init_mmio(sbd, &pcs->iomem); +} + +static const VMStateDescription vmstate_npcm_pcs = { + .name = TYPE_NPCM_PCS, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT16(indirect_access_base, NPCMPCSState), + VMSTATE_UINT16_ARRAY(sr_ctl, NPCMPCSState, NPCM_PCS_NR_SR_CTLS), + VMSTATE_UINT16_ARRAY(sr_mii, NPCMPCSState, NPCM_PCS_NR_SR_MIIS), + VMSTATE_UINT16_ARRAY(sr_tim, NPCMPCSState, NPCM_PCS_NR_SR_TIMS), + VMSTATE_UINT16_ARRAY(vr_mii, NPCMPCSState, NPCM_PCS_NR_VR_MIIS), + VMSTATE_END_OF_LIST(), + }, +}; + +static void npcm_pcs_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "NPCM PCS Controller"; + dc->realize = npcm_pcs_realize; + dc->vmsd = &vmstate_npcm_pcs; + rc->phases.enter = npcm_pcs_enter_reset; +} + +static const TypeInfo npcm_pcs_types[] = { + { + .name = TYPE_NPCM_PCS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMPCSState), + .class_init = npcm_pcs_class_init, + }, +}; +DEFINE_TYPES(npcm_pcs_types) diff --git a/hw/net/trace-events b/hw/net/trace-events index c35bfb2eb8..72b69c4a8b 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -483,8 +483,8 @@ npcm_gmac_packet_tx_desc_data(const char* name, uint32_t tdes0, uint32_t tdes1) npcm_gmac_tx_desc_owner(const char* name, uint32_t desc_addr) "%s: TX Descriptor @0x%04" PRIX32 " is owned by software" # npcm_pcs.c -npcm_pcs_reg_read(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 -npcm_pcs_reg_write(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 +npcm_pcs_reg_read(const char *name, uint16_t indirect_access_base, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 +npcm_pcs_reg_write(const char *name, uint16_t indirect_access_base, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 # dp8398x.c dp8393x_raise_irq(int isr) "raise irq, isr is 0x%04x" diff --git a/include/hw/net/npcm_pcs.h b/include/hw/net/npcm_pcs.h new file mode 100644 index 0000000000..d5c481ad70 --- /dev/null +++ b/include/hw/net/npcm_pcs.h @@ -0,0 +1,42 @@ +/* + * Nuvoton NPCM8xx PCS Module + * + * Copyright 2022 Google LLC + * + * 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. + */ + +#ifndef NPCM_PCS_H +#define NPCM_PCS_H + +#include "hw/sysbus.h" + +#define NPCM_PCS_NR_SR_CTLS (0x12 / sizeof(uint16_t)) +#define NPCM_PCS_NR_SR_MIIS (0x20 / sizeof(uint16_t)) +#define NPCM_PCS_NR_SR_TIMS (0x22 / sizeof(uint16_t)) +#define NPCM_PCS_NR_VR_MIIS (0x1c6 / sizeof(uint16_t)) + +struct NPCMPCSState { + SysBusDevice parent; + + MemoryRegion iomem; + + uint16_t indirect_access_base; + uint16_t sr_ctl[NPCM_PCS_NR_SR_CTLS]; + uint16_t sr_mii[NPCM_PCS_NR_SR_MIIS]; + uint16_t sr_tim[NPCM_PCS_NR_SR_TIMS]; + uint16_t vr_mii[NPCM_PCS_NR_VR_MIIS]; +}; + +#define TYPE_NPCM_PCS "npcm-pcs" +OBJECT_DECLARE_SIMPLE_TYPE(NPCMPCSState, NPCM_PCS) + +#endif /* NPCM_PCS_H */ From ae0c4d1a12900fdb1d853fe0505e4ba96d4bffef Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:06 -0800 Subject: [PATCH 1840/2892] hw/arm: Add NPCM8XX SoC Signed-off-by: Hao Wu Reviewed-by: Peter Maydell Message-id: 20250219184609.1839281-16-wuhaotsh@google.com Signed-off-by: Peter Maydell --- configs/devices/aarch64-softmmu/default.mak | 1 + hw/arm/Kconfig | 13 + hw/arm/meson.build | 1 + hw/arm/npcm8xx.c | 805 ++++++++++++++++++++ include/hw/arm/npcm8xx.h | 106 +++ 5 files changed, 926 insertions(+) create mode 100644 hw/arm/npcm8xx.c create mode 100644 include/hw/arm/npcm8xx.h diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak index f82a04c27d..93f4022ad6 100644 --- a/configs/devices/aarch64-softmmu/default.mak +++ b/configs/devices/aarch64-softmmu/default.mak @@ -8,3 +8,4 @@ include ../arm-softmmu/default.mak # CONFIG_XLNX_ZYNQMP_ARM=n # CONFIG_XLNX_VERSAL=n # CONFIG_SBSA_REF=n +# CONFIG_NPCM8XX=n diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7eab3914d4..504841ccab 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -481,6 +481,19 @@ config NPCM7XX select PCA954X select USB_OHCI_SYSBUS +config NPCM8XX + bool + default y + depends on TCG && AARCH64 + select ARM_GIC + select SMBUS + select PL310 # cache controller + select NPCM7XX + select SERIAL + select SSI + select UNIMP + + config FSL_IMX25 bool default y diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 490234b3b8..d7813c089c 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -12,6 +12,7 @@ arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c')) arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) arm_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) +arm_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c')) arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c')) arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c new file mode 100644 index 0000000000..f182accc47 --- /dev/null +++ b/hw/arm/npcm8xx.c @@ -0,0 +1,805 @@ +/* + * Nuvoton NPCM8xx SoC family. + * + * Copyright 2022 Google LLC + * + * 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. + */ + +#include "qemu/osdep.h" + +#include "hw/boards.h" +#include "hw/arm/boot.h" +#include "hw/arm/bsa.h" +#include "hw/arm/npcm8xx.h" +#include "hw/char/serial-mm.h" +#include "hw/intc/arm_gic.h" +#include "hw/loader.h" +#include "hw/misc/unimp.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/units.h" +#include "system/system.h" + +/* + * This covers the whole MMIO space. We'll use this to catch any MMIO accesses + * that aren't handled by a device. + */ +#define NPCM8XX_MMIO_BA 0x80000000 +#define NPCM8XX_MMIO_SZ 0x7ffd0000 + +/* OTP fuse array */ +#define NPCM8XX_OTP_BA 0xf0189000 + +/* GIC Distributor */ +#define NPCM8XX_GICD_BA 0xdfff9000 +#define NPCM8XX_GICC_BA 0xdfffa000 + +/* Core system modules. */ +#define NPCM8XX_CPUP_BA 0xf03fe000 +#define NPCM8XX_GCR_BA 0xf0800000 +#define NPCM8XX_CLK_BA 0xf0801000 +#define NPCM8XX_MC_BA 0xf0824000 +#define NPCM8XX_RNG_BA 0xf000b000 + +/* ADC Module */ +#define NPCM8XX_ADC_BA 0xf000c000 + +/* Internal AHB SRAM */ +#define NPCM8XX_RAM3_BA 0xc0008000 +#define NPCM8XX_RAM3_SZ (4 * KiB) + +/* Memory blocks at the end of the address space */ +#define NPCM8XX_RAM2_BA 0xfffb0000 +#define NPCM8XX_RAM2_SZ (256 * KiB) +#define NPCM8XX_ROM_BA 0xffff0100 +#define NPCM8XX_ROM_SZ (64 * KiB) + +/* SDHCI Modules */ +#define NPCM8XX_MMC_BA 0xf0842000 + +/* Run PLL1 at 1600 MHz */ +#define NPCM8XX_PLLCON1_FIXUP_VAL 0x00402101 +/* Run the CPU from PLL1 and UART from PLL2 */ +#define NPCM8XX_CLKSEL_FIXUP_VAL 0x004aaba9 + +/* Clock configuration values to be fixed up when bypassing bootloader */ + +/* + * Interrupt lines going into the GIC. This does not include internal Cortex-A35 + * interrupts. + */ +enum NPCM8xxInterrupt { + NPCM8XX_ADC_IRQ = 0, + NPCM8XX_PECI_IRQ = 6, + NPCM8XX_KCS_HIB_IRQ = 9, + NPCM8XX_MMC_IRQ = 26, + NPCM8XX_TIMER0_IRQ = 32, /* Timer Module 0 */ + NPCM8XX_TIMER1_IRQ, + NPCM8XX_TIMER2_IRQ, + NPCM8XX_TIMER3_IRQ, + NPCM8XX_TIMER4_IRQ, + NPCM8XX_TIMER5_IRQ, /* Timer Module 1 */ + NPCM8XX_TIMER6_IRQ, + NPCM8XX_TIMER7_IRQ, + NPCM8XX_TIMER8_IRQ, + NPCM8XX_TIMER9_IRQ, + NPCM8XX_TIMER10_IRQ, /* Timer Module 2 */ + NPCM8XX_TIMER11_IRQ, + NPCM8XX_TIMER12_IRQ, + NPCM8XX_TIMER13_IRQ, + NPCM8XX_TIMER14_IRQ, + NPCM8XX_WDG0_IRQ = 47, /* Timer Module 0 Watchdog */ + NPCM8XX_WDG1_IRQ, /* Timer Module 1 Watchdog */ + NPCM8XX_WDG2_IRQ, /* Timer Module 2 Watchdog */ + NPCM8XX_EHCI1_IRQ = 61, + NPCM8XX_OHCI1_IRQ, + NPCM8XX_EHCI2_IRQ, + NPCM8XX_OHCI2_IRQ, + NPCM8XX_PWM0_IRQ = 93, /* PWM module 0 */ + NPCM8XX_PWM1_IRQ, /* PWM module 1 */ + NPCM8XX_MFT0_IRQ = 96, /* MFT module 0 */ + NPCM8XX_MFT1_IRQ, /* MFT module 1 */ + NPCM8XX_MFT2_IRQ, /* MFT module 2 */ + NPCM8XX_MFT3_IRQ, /* MFT module 3 */ + NPCM8XX_MFT4_IRQ, /* MFT module 4 */ + NPCM8XX_MFT5_IRQ, /* MFT module 5 */ + NPCM8XX_MFT6_IRQ, /* MFT module 6 */ + NPCM8XX_MFT7_IRQ, /* MFT module 7 */ + NPCM8XX_PCI_MBOX1_IRQ = 105, + NPCM8XX_PCI_MBOX2_IRQ, + NPCM8XX_GPIO0_IRQ = 116, + NPCM8XX_GPIO1_IRQ, + NPCM8XX_GPIO2_IRQ, + NPCM8XX_GPIO3_IRQ, + NPCM8XX_GPIO4_IRQ, + NPCM8XX_GPIO5_IRQ, + NPCM8XX_GPIO6_IRQ, + NPCM8XX_GPIO7_IRQ, + NPCM8XX_SMBUS0_IRQ = 128, + NPCM8XX_SMBUS1_IRQ, + NPCM8XX_SMBUS2_IRQ, + NPCM8XX_SMBUS3_IRQ, + NPCM8XX_SMBUS4_IRQ, + NPCM8XX_SMBUS5_IRQ, + NPCM8XX_SMBUS6_IRQ, + NPCM8XX_SMBUS7_IRQ, + NPCM8XX_SMBUS8_IRQ, + NPCM8XX_SMBUS9_IRQ, + NPCM8XX_SMBUS10_IRQ, + NPCM8XX_SMBUS11_IRQ, + NPCM8XX_SMBUS12_IRQ, + NPCM8XX_SMBUS13_IRQ, + NPCM8XX_SMBUS14_IRQ, + NPCM8XX_SMBUS15_IRQ, + NPCM8XX_SMBUS16_IRQ, + NPCM8XX_SMBUS17_IRQ, + NPCM8XX_SMBUS18_IRQ, + NPCM8XX_SMBUS19_IRQ, + NPCM8XX_SMBUS20_IRQ, + NPCM8XX_SMBUS21_IRQ, + NPCM8XX_SMBUS22_IRQ, + NPCM8XX_SMBUS23_IRQ, + NPCM8XX_SMBUS24_IRQ, + NPCM8XX_SMBUS25_IRQ, + NPCM8XX_SMBUS26_IRQ, + NPCM8XX_UART0_IRQ = 192, + NPCM8XX_UART1_IRQ, + NPCM8XX_UART2_IRQ, + NPCM8XX_UART3_IRQ, + NPCM8XX_UART4_IRQ, + NPCM8XX_UART5_IRQ, + NPCM8XX_UART6_IRQ, +}; + +/* Total number of GIC interrupts, including internal Cortex-A35 interrupts. */ +#define NPCM8XX_NUM_IRQ (288) +#define NPCM8XX_PPI_BASE(cpu) \ + ((NPCM8XX_NUM_IRQ - GIC_INTERNAL) + (cpu) * GIC_INTERNAL) + +/* Register base address for each Timer Module */ +static const hwaddr npcm8xx_tim_addr[] = { + 0xf0008000, + 0xf0009000, + 0xf000a000, +}; + +/* Register base address for each 16550 UART */ +static const hwaddr npcm8xx_uart_addr[] = { + 0xf0000000, + 0xf0001000, + 0xf0002000, + 0xf0003000, + 0xf0004000, + 0xf0005000, + 0xf0006000, +}; + +/* Direct memory-mapped access to SPI0 CS0-1. */ +static const hwaddr npcm8xx_fiu0_flash_addr[] = { + 0x80000000, /* CS0 */ + 0x88000000, /* CS1 */ +}; + +/* Direct memory-mapped access to SPI1 CS0-3. */ +static const hwaddr npcm8xx_fiu1_flash_addr[] = { + 0x90000000, /* CS0 */ + 0x91000000, /* CS1 */ + 0x92000000, /* CS2 */ + 0x93000000, /* CS3 */ +}; + +/* Direct memory-mapped access to SPI3 CS0-3. */ +static const hwaddr npcm8xx_fiu3_flash_addr[] = { + 0xa0000000, /* CS0 */ + 0xa8000000, /* CS1 */ + 0xb0000000, /* CS2 */ + 0xb8000000, /* CS3 */ +}; + +/* Register base address for each PWM Module */ +static const hwaddr npcm8xx_pwm_addr[] = { + 0xf0103000, + 0xf0104000, + 0xf0105000, +}; + +/* Register base address for each MFT Module */ +static const hwaddr npcm8xx_mft_addr[] = { + 0xf0180000, + 0xf0181000, + 0xf0182000, + 0xf0183000, + 0xf0184000, + 0xf0185000, + 0xf0186000, + 0xf0187000, +}; + +/* Direct memory-mapped access to each SMBus Module. */ +static const hwaddr npcm8xx_smbus_addr[] = { + 0xf0080000, + 0xf0081000, + 0xf0082000, + 0xf0083000, + 0xf0084000, + 0xf0085000, + 0xf0086000, + 0xf0087000, + 0xf0088000, + 0xf0089000, + 0xf008a000, + 0xf008b000, + 0xf008c000, + 0xf008d000, + 0xf008e000, + 0xf008f000, + 0xfff00000, + 0xfff01000, + 0xfff02000, + 0xfff03000, + 0xfff04000, + 0xfff05000, + 0xfff06000, + 0xfff07000, + 0xfff08000, + 0xfff09000, + 0xfff0a000, +}; + +/* Register base address for each USB host EHCI registers */ +static const hwaddr npcm8xx_ehci_addr[] = { + 0xf0828100, + 0xf082a100, +}; + +/* Register base address for each USB host OHCI registers */ +static const hwaddr npcm8xx_ohci_addr[] = { + 0xf0829000, + 0xf082b000, +}; + +static const struct { + hwaddr regs_addr; + uint32_t reset_pu; + uint32_t reset_pd; + uint32_t reset_osrc; + uint32_t reset_odsc; +} npcm8xx_gpio[] = { + { + .regs_addr = 0xf0010000, + .reset_pu = 0x00000300, + .reset_pd = 0x000f0000, + }, { + .regs_addr = 0xf0011000, + .reset_pu = 0xe0fefe01, + .reset_pd = 0x07000000, + }, { + .regs_addr = 0xf0012000, + .reset_pu = 0xc00fffff, + .reset_pd = 0x3ff00000, + }, { + .regs_addr = 0xf0013000, + .reset_pd = 0x00003000, + }, { + .regs_addr = 0xf0014000, + .reset_pu = 0xffff0000, + }, { + .regs_addr = 0xf0015000, + .reset_pu = 0xff8387fe, + .reset_pd = 0x007c0001, + .reset_osrc = 0x08000000, + }, { + .regs_addr = 0xf0016000, + .reset_pu = 0x00000801, + .reset_pd = 0x00000302, + }, { + .regs_addr = 0xf0017000, + .reset_pu = 0x000002ff, + .reset_pd = 0x00000c00, + }, +}; + +static const struct { + const char *name; + hwaddr regs_addr; + int cs_count; + const hwaddr *flash_addr; + size_t flash_size; +} npcm8xx_fiu[] = { + { + .name = "fiu0", + .regs_addr = 0xfb000000, + .cs_count = ARRAY_SIZE(npcm8xx_fiu0_flash_addr), + .flash_addr = npcm8xx_fiu0_flash_addr, + .flash_size = 128 * MiB, + }, + { + .name = "fiu1", + .regs_addr = 0xfb002000, + .cs_count = ARRAY_SIZE(npcm8xx_fiu1_flash_addr), + .flash_addr = npcm8xx_fiu1_flash_addr, + .flash_size = 16 * MiB, + }, { + .name = "fiu3", + .regs_addr = 0xc0000000, + .cs_count = ARRAY_SIZE(npcm8xx_fiu3_flash_addr), + .flash_addr = npcm8xx_fiu3_flash_addr, + .flash_size = 128 * MiB, + }, +}; + +static struct arm_boot_info npcm8xx_binfo = { + .loader_start = NPCM8XX_LOADER_START, + .smp_loader_start = NPCM8XX_SMP_LOADER_START, + .smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR, + .gic_cpu_if_addr = NPCM8XX_GICC_BA, + .secure_boot = false, + .board_id = -1, + .board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR, +}; + +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc) +{ + npcm8xx_binfo.ram_size = machine->ram_size; + + arm_load_kernel(&soc->cpu[0], machine, &npcm8xx_binfo); +} + +static void npcm8xx_init_fuses(NPCM8xxState *s) +{ + NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s); + uint32_t value; + + /* + * The initial mask of disabled modules indicates the chip derivative (e.g. + * NPCM750 or NPCM730). + */ + value = cpu_to_le32(nc->disabled_modules); + npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE, + sizeof(value)); +} + +static void npcm8xx_write_adc_calibration(NPCM8xxState *s) +{ + /* Both ADC and the fuse array must have realized. */ + QEMU_BUILD_BUG_ON(sizeof(s->adc.calibration_r_values) != 4); + npcm7xx_otp_array_write(&s->fuse_array, s->adc.calibration_r_values, + NPCM7XX_FUSE_ADC_CALIB, sizeof(s->adc.calibration_r_values)); +} + +static qemu_irq npcm8xx_irq(NPCM8xxState *s, int n) +{ + return qdev_get_gpio_in(DEVICE(&s->gic), n); +} + +static void npcm8xx_init(Object *obj) +{ + NPCM8xxState *s = NPCM8XX(obj); + int i; + + object_initialize_child(obj, "cpu-cluster", &s->cpu_cluster, + TYPE_CPU_CLUSTER); + for (i = 0; i < NPCM8XX_MAX_NUM_CPUS; i++) { + object_initialize_child(OBJECT(&s->cpu_cluster), "cpu[*]", &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a35")); + } + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC); + object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM8XX_GCR); + object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr), + "power-on-straps"); + object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM8XX_CLK); + object_initialize_child(obj, "otp", &s->fuse_array, + TYPE_NPCM7XX_FUSE_ARRAY); + object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC); + object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG); + object_initialize_child(obj, "adc", &s->adc, TYPE_NPCM7XX_ADC); + + for (i = 0; i < ARRAY_SIZE(s->tim); i++) { + object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER); + } + + for (i = 0; i < ARRAY_SIZE(s->gpio); i++) { + object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_NPCM7XX_GPIO); + } + + + for (i = 0; i < ARRAY_SIZE(s->smbus); i++) { + object_initialize_child(obj, "smbus[*]", &s->smbus[i], + TYPE_NPCM7XX_SMBUS); + DEVICE(&s->smbus[i])->id = g_strdup_printf("smbus[%d]", i); + } + + for (i = 0; i < ARRAY_SIZE(s->ehci); i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], TYPE_NPCM7XX_EHCI); + } + for (i = 0; i < ARRAY_SIZE(s->ohci); i++) { + object_initialize_child(obj, "ohci[*]", &s->ohci[i], TYPE_SYSBUS_OHCI); + } + + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu)); + for (i = 0; i < ARRAY_SIZE(s->fiu); i++) { + object_initialize_child(obj, npcm8xx_fiu[i].name, &s->fiu[i], + TYPE_NPCM7XX_FIU); + } + + for (i = 0; i < ARRAY_SIZE(s->pwm); i++) { + object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM); + } + + for (i = 0; i < ARRAY_SIZE(s->mft); i++) { + object_initialize_child(obj, "mft[*]", &s->mft[i], TYPE_NPCM7XX_MFT); + } + + object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI); +} + +static void npcm8xx_realize(DeviceState *dev, Error **errp) +{ + NPCM8xxState *s = NPCM8XX(dev); + NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s); + int i; + + if (memory_region_size(s->dram) > NPCM8XX_DRAM_SZ) { + error_setg(errp, "%s: NPCM8xx cannot address more than %" PRIu64 + " MiB of DRAM", __func__, NPCM8XX_DRAM_SZ / MiB); + return; + } + + /* CPUs */ + for (i = 0; i < nc->num_cpus; i++) { + object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity", + arm_build_mp_affinity(i, NPCM8XX_MAX_NUM_CPUS), + &error_abort); + object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true, + &error_abort); + object_property_set_int(OBJECT(&s->cpu[i]), "core-count", + nc->num_cpus, &error_abort); + + /* Disable security extensions. */ + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", false, + &error_abort); + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* ARM GIC for Cortex A35. Can only fail if we pass bad parameters here. */ + object_property_set_uint(OBJECT(&s->gic), "num-cpu", nc->num_cpus, errp); + object_property_set_uint(OBJECT(&s->gic), "num-irq", NPCM8XX_NUM_IRQ, errp); + object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp); + object_property_set_bool(OBJECT(&s->gic), "has-security-extensions", true, + errp); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) { + return; + } + for (i = 0; i < nc->num_cpus; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 2, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 3, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VFIQ)); + + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_PHYS, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL1_IRQ)); + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_VIRT, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_VIRT_IRQ)); + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_HYP, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL2_IRQ)); + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_SEC, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_S_EL1_IRQ)); + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, NPCM8XX_GICD_BA); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, NPCM8XX_GICC_BA); + + /* CPU cluster */ + qdev_prop_set_uint32(DEVICE(&s->cpu_cluster), "cluster-id", 0); + qdev_realize(DEVICE(&s->cpu_cluster), NULL, &error_fatal); + + /* System Global Control Registers (GCR). Can fail due to user input. */ + object_property_set_int(OBJECT(&s->gcr), "disabled-modules", + nc->disabled_modules, &error_abort); + object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM8XX_GCR_BA); + + /* Clock Control Registers (CLK). Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM8XX_CLK_BA); + + /* OTP fuse strap array. Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM8XX_OTP_BA); + npcm8xx_init_fuses(s); + + /* Fake Memory Controller (MC). Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM8XX_MC_BA); + + /* ADC Modules. Cannot fail. */ + qdev_connect_clock_in(DEVICE(&s->adc), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "adc-clock")); + sysbus_realize(SYS_BUS_DEVICE(&s->adc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, NPCM8XX_ADC_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + npcm8xx_irq(s, NPCM8XX_ADC_IRQ)); + npcm8xx_write_adc_calibration(s); + + /* Timer Modules (TIM). Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_tim_addr) != ARRAY_SIZE(s->tim)); + for (i = 0; i < ARRAY_SIZE(s->tim); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]); + int first_irq; + int j; + + /* Connect the timer clock. */ + qdev_connect_clock_in(DEVICE(&s->tim[i]), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "timer-clock")); + + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_tim_addr[i]); + + first_irq = NPCM8XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL; + for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) { + qemu_irq irq = npcm8xx_irq(s, first_irq + j); + sysbus_connect_irq(sbd, j, irq); + } + + /* IRQ for watchdogs */ + sysbus_connect_irq(sbd, NPCM7XX_TIMERS_PER_CTRL, + npcm8xx_irq(s, NPCM8XX_WDG0_IRQ + i)); + /* GPIO that connects clk module with watchdog */ + qdev_connect_gpio_out_named(DEVICE(&s->tim[i]), + NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 0, + qdev_get_gpio_in_named(DEVICE(&s->clk), + NPCM7XX_WATCHDOG_RESET_GPIO_IN, i)); + } + + /* UART0..6 (16550 compatible) */ + for (i = 0; i < ARRAY_SIZE(npcm8xx_uart_addr); i++) { + serial_mm_init(get_system_memory(), npcm8xx_uart_addr[i], 2, + npcm8xx_irq(s, NPCM8XX_UART0_IRQ + i), 115200, + serial_hd(i), DEVICE_LITTLE_ENDIAN); + } + + /* Random Number Generator. Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->rng), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rng), 0, NPCM8XX_RNG_BA); + + /* GPIO modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_gpio) != ARRAY_SIZE(s->gpio)); + for (i = 0; i < ARRAY_SIZE(s->gpio); i++) { + Object *obj = OBJECT(&s->gpio[i]); + + object_property_set_uint(obj, "reset-pullup", + npcm8xx_gpio[i].reset_pu, &error_abort); + object_property_set_uint(obj, "reset-pulldown", + npcm8xx_gpio[i].reset_pd, &error_abort); + object_property_set_uint(obj, "reset-osrc", + npcm8xx_gpio[i].reset_osrc, &error_abort); + object_property_set_uint(obj, "reset-odsc", + npcm8xx_gpio[i].reset_odsc, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_gpio[i].regs_addr); + sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0, + npcm8xx_irq(s, NPCM8XX_GPIO0_IRQ + i)); + } + + /* SMBus modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_smbus_addr) != ARRAY_SIZE(s->smbus)); + for (i = 0; i < ARRAY_SIZE(s->smbus); i++) { + Object *obj = OBJECT(&s->smbus[i]); + + sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_smbus_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0, + npcm8xx_irq(s, NPCM8XX_SMBUS0_IRQ + i)); + } + + /* USB Host */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->ohci) != ARRAY_SIZE(s->ehci)); + for (i = 0; i < ARRAY_SIZE(s->ehci); i++) { + object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", true, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, npcm8xx_ehci_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + npcm8xx_irq(s, NPCM8XX_EHCI1_IRQ + 2 * i)); + } + for (i = 0; i < ARRAY_SIZE(s->ohci); i++) { + object_property_set_str(OBJECT(&s->ohci[i]), "masterbus", "usb-bus.0", + &error_abort); + object_property_set_uint(OBJECT(&s->ohci[i]), "num-ports", 1, + &error_abort); + object_property_set_uint(OBJECT(&s->ohci[i]), "firstport", i, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci[i]), 0, npcm8xx_ohci_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci[i]), 0, + npcm8xx_irq(s, NPCM8XX_OHCI1_IRQ + 2 * i)); + } + + /* PWM Modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_pwm_addr) != ARRAY_SIZE(s->pwm)); + for (i = 0; i < ARRAY_SIZE(s->pwm); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->pwm[i]); + + qdev_connect_clock_in(DEVICE(&s->pwm[i]), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "apb3-clock")); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_pwm_addr[i]); + sysbus_connect_irq(sbd, i, npcm8xx_irq(s, NPCM8XX_PWM0_IRQ + i)); + } + + /* MFT Modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_mft_addr) != ARRAY_SIZE(s->mft)); + for (i = 0; i < ARRAY_SIZE(s->mft); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->mft[i]); + + qdev_connect_clock_in(DEVICE(&s->mft[i]), "clock-in", + qdev_get_clock_out(DEVICE(&s->clk), + "apb4-clock")); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_mft_addr[i]); + sysbus_connect_irq(sbd, 0, npcm8xx_irq(s, NPCM8XX_MFT0_IRQ + i)); + } + + /* + * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects + * specified, but this is a programming error. + */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu)); + for (i = 0; i < ARRAY_SIZE(s->fiu); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]); + int j; + + object_property_set_int(OBJECT(sbd), "cs-count", + npcm8xx_fiu[i].cs_count, &error_abort); + object_property_set_int(OBJECT(sbd), "flash-size", + npcm8xx_fiu[i].flash_size, &error_abort); + sysbus_realize(sbd, &error_abort); + + sysbus_mmio_map(sbd, 0, npcm8xx_fiu[i].regs_addr); + for (j = 0; j < npcm8xx_fiu[i].cs_count; j++) { + sysbus_mmio_map(sbd, j + 1, npcm8xx_fiu[i].flash_addr[j]); + } + } + + /* RAM2 (SRAM) */ + memory_region_init_ram(&s->sram, OBJECT(dev), "ram2", + NPCM8XX_RAM2_SZ, &error_abort); + memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM2_BA, &s->sram); + + /* RAM3 (SRAM) */ + memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3", + NPCM8XX_RAM3_SZ, &error_abort); + memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM3_BA, &s->ram3); + + /* Internal ROM */ + memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM8XX_ROM_SZ, + &error_abort); + memory_region_add_subregion(get_system_memory(), NPCM8XX_ROM_BA, &s->irom); + + /* SDHCI */ + sysbus_realize(SYS_BUS_DEVICE(&s->mmc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc), 0, NPCM8XX_MMC_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc), 0, + npcm8xx_irq(s, NPCM8XX_MMC_IRQ)); + + + create_unimplemented_device("npcm8xx.shm", 0xc0001000, 4 * KiB); + create_unimplemented_device("npcm8xx.gicextra", 0xdfffa000, 24 * KiB); + create_unimplemented_device("npcm8xx.vdmx", 0xe0800000, 4 * KiB); + create_unimplemented_device("npcm8xx.pcierc", 0xe1000000, 64 * KiB); + create_unimplemented_device("npcm8xx.rootc", 0xe8000000, 128 * MiB); + create_unimplemented_device("npcm8xx.kcs", 0xf0007000, 4 * KiB); + create_unimplemented_device("npcm8xx.gfxi", 0xf000e000, 4 * KiB); + create_unimplemented_device("npcm8xx.fsw", 0xf000f000, 4 * KiB); + create_unimplemented_device("npcm8xx.bt", 0xf0030000, 4 * KiB); + create_unimplemented_device("npcm8xx.espi", 0xf009f000, 4 * KiB); + create_unimplemented_device("npcm8xx.peci", 0xf0100000, 4 * KiB); + create_unimplemented_device("npcm8xx.siox[1]", 0xf0101000, 4 * KiB); + create_unimplemented_device("npcm8xx.siox[2]", 0xf0102000, 4 * KiB); + create_unimplemented_device("npcm8xx.tmps", 0xf0188000, 4 * KiB); + create_unimplemented_device("npcm8xx.pspi", 0xf0201000, 4 * KiB); + create_unimplemented_device("npcm8xx.viru1", 0xf0204000, 4 * KiB); + create_unimplemented_device("npcm8xx.viru2", 0xf0205000, 4 * KiB); + create_unimplemented_device("npcm8xx.jtm1", 0xf0208000, 4 * KiB); + create_unimplemented_device("npcm8xx.jtm2", 0xf0209000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm0", 0xf0210000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm1", 0xf0211000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm2", 0xf0212000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm3", 0xf0213000, 4 * KiB); + create_unimplemented_device("npcm8xx.ahbpci", 0xf0400000, 1 * MiB); + create_unimplemented_device("npcm8xx.dap", 0xf0500000, 960 * KiB); + create_unimplemented_device("npcm8xx.mcphy", 0xf05f0000, 64 * KiB); + create_unimplemented_device("npcm8xx.pcs", 0xf0780000, 256 * KiB); + create_unimplemented_device("npcm8xx.tsgen", 0xf07fc000, 8 * KiB); + create_unimplemented_device("npcm8xx.gmac1", 0xf0802000, 8 * KiB); + create_unimplemented_device("npcm8xx.gmac2", 0xf0804000, 8 * KiB); + create_unimplemented_device("npcm8xx.gmac3", 0xf0806000, 8 * KiB); + create_unimplemented_device("npcm8xx.gmac4", 0xf0808000, 8 * KiB); + create_unimplemented_device("npcm8xx.copctl", 0xf080c000, 4 * KiB); + create_unimplemented_device("npcm8xx.tipctl", 0xf080d000, 4 * KiB); + create_unimplemented_device("npcm8xx.rst", 0xf080e000, 4 * KiB); + create_unimplemented_device("npcm8xx.vcd", 0xf0810000, 64 * KiB); + create_unimplemented_device("npcm8xx.ece", 0xf0820000, 8 * KiB); + create_unimplemented_device("npcm8xx.vdma", 0xf0822000, 8 * KiB); + create_unimplemented_device("npcm8xx.usbd[0]", 0xf0830000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[1]", 0xf0831000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[2]", 0xf0832000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[3]", 0xf0833000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[4]", 0xf0834000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[5]", 0xf0835000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[6]", 0xf0836000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[7]", 0xf0837000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[8]", 0xf0838000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[9]", 0xf0839000, 4 * KiB); + create_unimplemented_device("npcm8xx.pci_mbox1", 0xf0848000, 64 * KiB); + create_unimplemented_device("npcm8xx.gdma0", 0xf0850000, 4 * KiB); + create_unimplemented_device("npcm8xx.gdma1", 0xf0851000, 4 * KiB); + create_unimplemented_device("npcm8xx.gdma2", 0xf0852000, 4 * KiB); + create_unimplemented_device("npcm8xx.aes", 0xf0858000, 4 * KiB); + create_unimplemented_device("npcm8xx.des", 0xf0859000, 4 * KiB); + create_unimplemented_device("npcm8xx.sha", 0xf085a000, 4 * KiB); + create_unimplemented_device("npcm8xx.pci_mbox2", 0xf0868000, 64 * KiB); + create_unimplemented_device("npcm8xx.i3c0", 0xfff10000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c1", 0xfff11000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c2", 0xfff12000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c3", 0xfff13000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c4", 0xfff14000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c5", 0xfff15000, 4 * KiB); + create_unimplemented_device("npcm8xx.spixcs0", 0xf8000000, 16 * MiB); + create_unimplemented_device("npcm8xx.spixcs1", 0xf9000000, 16 * MiB); + create_unimplemented_device("npcm8xx.spix", 0xfb001000, 4 * KiB); + create_unimplemented_device("npcm8xx.vect", 0xffff0000, 256); +} + +static const Property npcm8xx_properties[] = { + DEFINE_PROP_LINK("dram-mr", NPCM8xxState, dram, TYPE_MEMORY_REGION, + MemoryRegion *), +}; + +static void npcm8xx_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + NPCM8xxClass *nc = NPCM8XX_CLASS(oc); + + dc->realize = npcm8xx_realize; + dc->user_creatable = false; + nc->disabled_modules = 0x00000000; + nc->num_cpus = NPCM8XX_MAX_NUM_CPUS; + device_class_set_props(dc, npcm8xx_properties); +} + +static const TypeInfo npcm8xx_soc_types[] = { + { + .name = TYPE_NPCM8XX, + .parent = TYPE_DEVICE, + .instance_size = sizeof(NPCM8xxState), + .instance_init = npcm8xx_init, + .class_size = sizeof(NPCM8xxClass), + .class_init = npcm8xx_class_init, + }, +}; + +DEFINE_TYPES(npcm8xx_soc_types); diff --git a/include/hw/arm/npcm8xx.h b/include/hw/arm/npcm8xx.h new file mode 100644 index 0000000000..a32fbb035b --- /dev/null +++ b/include/hw/arm/npcm8xx.h @@ -0,0 +1,106 @@ +/* + * Nuvoton NPCM8xx SoC family. + * + * Copyright 2022 Google LLC + * + * 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. + */ +#ifndef NPCM8XX_H +#define NPCM8XX_H + +#include "hw/adc/npcm7xx_adc.h" +#include "hw/core/split-irq.h" +#include "hw/cpu/cluster.h" +#include "hw/gpio/npcm7xx_gpio.h" +#include "hw/i2c/npcm7xx_smbus.h" +#include "hw/intc/arm_gic_common.h" +#include "hw/mem/npcm7xx_mc.h" +#include "hw/misc/npcm_clk.h" +#include "hw/misc/npcm_gcr.h" +#include "hw/misc/npcm7xx_mft.h" +#include "hw/misc/npcm7xx_pwm.h" +#include "hw/misc/npcm7xx_rng.h" +#include "hw/net/npcm7xx_emc.h" +#include "hw/nvram/npcm7xx_otp.h" +#include "hw/sd/npcm7xx_sdhci.h" +#include "hw/timer/npcm7xx_timer.h" +#include "hw/ssi/npcm7xx_fiu.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/usb/hcd-ohci.h" +#include "target/arm/cpu.h" + +#define NPCM8XX_MAX_NUM_CPUS (4) + +/* The first half of the address space is reserved for DDR4 DRAM. */ +#define NPCM8XX_DRAM_BA (0x00000000) +#define NPCM8XX_DRAM_SZ (2 * GiB) + +/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */ +#define NPCM8XX_LOADER_START (0x00000000) /* Start of SDRAM */ +#define NPCM8XX_SMP_LOADER_START (0xffff0000) /* Boot ROM */ +#define NPCM8XX_SMP_BOOTREG_ADDR (0xf080013c) /* GCR.SCRPAD */ +#define NPCM8XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */ + +#define NPCM8XX_NR_PWM_MODULES 3 + +struct NPCM8xxState { + DeviceState parent_obj; + + ARMCPU cpu[NPCM8XX_MAX_NUM_CPUS]; + CPUClusterState cpu_cluster; + GICState gic; + + MemoryRegion sram; + MemoryRegion irom; + MemoryRegion ram3; + MemoryRegion *dram; + + NPCMGCRState gcr; + NPCMCLKState clk; + NPCM7xxTimerCtrlState tim[3]; + NPCM7xxADCState adc; + NPCM7xxPWMState pwm[NPCM8XX_NR_PWM_MODULES]; + NPCM7xxMFTState mft[8]; + NPCM7xxOTPState fuse_array; + NPCM7xxMCState mc; + NPCM7xxRNGState rng; + NPCM7xxGPIOState gpio[8]; + NPCM7xxSMBusState smbus[27]; + EHCISysBusState ehci[2]; + OHCISysBusState ohci[2]; + NPCM7xxFIUState fiu[3]; + NPCM7xxSDHCIState mmc; +}; + +struct NPCM8xxClass { + DeviceClass parent_class; + + /* Bitmask of modules that are permanently disabled on this chip. */ + uint32_t disabled_modules; + /* Number of CPU cores enabled in this SoC class. */ + uint32_t num_cpus; +}; + +#define TYPE_NPCM8XX "npcm8xx" +OBJECT_DECLARE_TYPE(NPCM8xxState, NPCM8xxClass, NPCM8XX) + +/** + * npcm8xx_load_kernel - Loads memory with everything needed to boot + * @machine - The machine containing the SoC to be booted. + * @soc - The SoC containing the CPU to be booted. + * + * This will set up the ARM boot info structure for the specific NPCM8xx + * derivative and call arm_load_kernel() to set up loading of the kernel, etc. + * into memory, if requested by the user. + */ +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc); + +#endif /* NPCM8XX_H */ From 7e70eb3cad7c835f2ba447306927721101c0788f Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:07 -0800 Subject: [PATCH 1841/2892] hw/arm: Add NPCM845 Evaluation board Reviewed-by: Peter Maydell Signed-off-by: Hao Wu Message-id: 20250219184609.1839281-17-wuhaotsh@google.com Signed-off-by: Peter Maydell --- hw/arm/meson.build | 2 +- hw/arm/npcm8xx_boards.c | 253 +++++++++++++++++++++++++++++++++++++++ include/hw/arm/npcm8xx.h | 21 ++++ 3 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 hw/arm/npcm8xx_boards.c diff --git a/hw/arm/meson.build b/hw/arm/meson.build index d7813c089c..465c757f97 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -12,7 +12,7 @@ arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c')) arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) arm_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) -arm_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c')) +arm_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c')) arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c')) arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c new file mode 100644 index 0000000000..19610483f9 --- /dev/null +++ b/hw/arm/npcm8xx_boards.c @@ -0,0 +1,253 @@ +/* + * Machine definitions for boards featuring an NPCM8xx SoC. + * + * Copyright 2021 Google LLC + * + * 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. + */ + +#include "qemu/osdep.h" + +#include "chardev/char.h" +#include "hw/arm/npcm8xx.h" +#include "hw/core/cpu.h" +#include "hw/loader.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/datadir.h" +#include "qemu/units.h" + +#define NPCM845_EVB_POWER_ON_STRAPS 0x000017ff + +static const char npcm8xx_default_bootrom[] = "npcm8xx_bootrom.bin"; + +static void npcm8xx_load_bootrom(MachineState *machine, NPCM8xxState *soc) +{ + const char *bios_name = machine->firmware ?: npcm8xx_default_bootrom; + g_autofree char *filename = NULL; + int ret; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_report("Could not find ROM image '%s'", bios_name); + if (!machine->kernel_filename) { + /* We can't boot without a bootrom or a kernel image. */ + exit(1); + } + return; + } + ret = load_image_mr(filename, machine->ram); + if (ret < 0) { + error_report("Failed to load ROM image '%s'", filename); + exit(1); + } +} + +static void npcm8xx_connect_flash(NPCM7xxFIUState *fiu, int cs_no, + const char *flash_type, DriveInfo *dinfo) +{ + DeviceState *flash; + qemu_irq flash_cs; + + flash = qdev_new(flash_type); + if (dinfo) { + qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo)); + } + qdev_realize_and_unref(flash, BUS(fiu->spi), &error_fatal); + + flash_cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(fiu), "cs", cs_no, flash_cs); +} + +static void npcm8xx_connect_dram(NPCM8xxState *soc, MemoryRegion *dram) +{ + memory_region_add_subregion(get_system_memory(), NPCM8XX_DRAM_BA, dram); + + object_property_set_link(OBJECT(soc), "dram-mr", OBJECT(dram), + &error_abort); +} + +static NPCM8xxState *npcm8xx_create_soc(MachineState *machine, + uint32_t hw_straps) +{ + NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_GET_CLASS(machine); + Object *obj; + + obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc", + &error_abort, NULL); + object_property_set_uint(obj, "power-on-straps", hw_straps, &error_abort); + + return NPCM8XX(obj); +} + +static I2CBus *npcm8xx_i2c_get_bus(NPCM8xxState *soc, uint32_t num) +{ + g_assert(num < ARRAY_SIZE(soc->smbus)); + return I2C_BUS(qdev_get_child_bus(DEVICE(&soc->smbus[num]), "i2c-bus")); +} + +static void npcm8xx_init_pwm_splitter(NPCM8xxMachine *machine, + NPCM8xxState *soc, const int *fan_counts) +{ + SplitIRQ *splitters = machine->fan_splitter; + + /* + * PWM 0~3 belong to module 0 output 0~3. + * PWM 4~7 belong to module 1 output 0~3. + */ + for (int i = 0; i < NPCM8XX_NR_PWM_MODULES; ++i) { + for (int j = 0; j < NPCM7XX_PWM_PER_MODULE; ++j) { + int splitter_no = i * NPCM7XX_PWM_PER_MODULE + j; + DeviceState *splitter; + + if (fan_counts[splitter_no] < 1) { + continue; + } + object_initialize_child(OBJECT(machine), "fan-splitter[*]", + &splitters[splitter_no], TYPE_SPLIT_IRQ); + splitter = DEVICE(&splitters[splitter_no]); + qdev_prop_set_uint16(splitter, "num-lines", + fan_counts[splitter_no]); + qdev_realize(splitter, NULL, &error_abort); + qdev_connect_gpio_out_named(DEVICE(&soc->pwm[i]), "duty-gpio-out", + j, qdev_get_gpio_in(splitter, 0)); + } + } +} + +static void npcm8xx_connect_pwm_fan(NPCM8xxState *soc, SplitIRQ *splitter, + int fan_no, int output_no) +{ + DeviceState *fan; + int fan_input; + qemu_irq fan_duty_gpio; + + g_assert(fan_no >= 0 && fan_no <= NPCM7XX_MFT_MAX_FAN_INPUT); + /* + * Fan 0~1 belong to module 0 input 0~1. + * Fan 2~3 belong to module 1 input 0~1. + * ... + * Fan 14~15 belong to module 7 input 0~1. + * Fan 16~17 belong to module 0 input 2~3. + * Fan 18~19 belong to module 1 input 2~3. + */ + if (fan_no < 16) { + fan = DEVICE(&soc->mft[fan_no / 2]); + fan_input = fan_no % 2; + } else { + fan = DEVICE(&soc->mft[(fan_no - 16) / 2]); + fan_input = fan_no % 2 + 2; + } + + /* Connect the Fan to PWM module */ + fan_duty_gpio = qdev_get_gpio_in_named(fan, "duty", fan_input); + qdev_connect_gpio_out(DEVICE(splitter), output_no, fan_duty_gpio); +} + +static void npcm845_evb_i2c_init(NPCM8xxState *soc) +{ + /* tmp100 temperature sensor on SVB, tmp105 is compatible */ + i2c_slave_create_simple(npcm8xx_i2c_get_bus(soc, 6), "tmp105", 0x48); +} + +static void npcm845_evb_fan_init(NPCM8xxMachine *machine, NPCM8xxState *soc) +{ + SplitIRQ *splitter = machine->fan_splitter; + static const int fan_counts[] = {2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0}; + + npcm8xx_init_pwm_splitter(machine, soc, fan_counts); + npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x00, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x01, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x02, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x03, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x04, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x05, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x06, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x07, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x08, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x09, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0a, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0b, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0c, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0d, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0e, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0f, 1); +} + +static void npcm845_evb_init(MachineState *machine) +{ + NPCM8xxState *soc; + + soc = npcm8xx_create_soc(machine, NPCM845_EVB_POWER_ON_STRAPS); + npcm8xx_connect_dram(soc, machine->ram); + qdev_realize(DEVICE(soc), NULL, &error_fatal); + + npcm8xx_load_bootrom(machine, soc); + npcm8xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); + npcm845_evb_i2c_init(soc); + npcm845_evb_fan_init(NPCM8XX_MACHINE(machine), soc); + npcm8xx_load_kernel(machine, soc); +} + +static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type) +{ + NPCM8xxClass *sc = NPCM8XX_CLASS(object_class_by_name(type)); + MachineClass *mc = MACHINE_CLASS(nmc); + + nmc->soc_type = type; + mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus; +} + +static void npcm8xx_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL + }; + + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_parallel = 1; + mc->default_ram_id = "ram"; + mc->valid_cpu_types = valid_cpu_types; +} + +static void npcm845_evb_machine_class_init(ObjectClass *oc, void *data) +{ + NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + npcm8xx_set_soc_type(nmc, TYPE_NPCM8XX); + + mc->desc = "Nuvoton NPCM845 Evaluation Board (Cortex-A35)"; + mc->init = npcm845_evb_init; + mc->default_ram_size = 1 * GiB; +}; + +static const TypeInfo npcm8xx_machine_types[] = { + { + .name = TYPE_NPCM8XX_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(NPCM8xxMachine), + .class_size = sizeof(NPCM8xxMachineClass), + .class_init = npcm8xx_machine_class_init, + .abstract = true, + }, { + .name = MACHINE_TYPE_NAME("npcm845-evb"), + .parent = TYPE_NPCM8XX_MACHINE, + .class_init = npcm845_evb_machine_class_init, + }, +}; + +DEFINE_TYPES(npcm8xx_machine_types) diff --git a/include/hw/arm/npcm8xx.h b/include/hw/arm/npcm8xx.h index a32fbb035b..9812e6fa7e 100644 --- a/include/hw/arm/npcm8xx.h +++ b/include/hw/arm/npcm8xx.h @@ -51,6 +51,27 @@ #define NPCM8XX_NR_PWM_MODULES 3 +struct NPCM8xxMachine { + MachineState parent_obj; + + /* + * PWM fan splitter. each splitter connects to one PWM output and + * multiple MFT inputs. + */ + SplitIRQ fan_splitter[NPCM8XX_NR_PWM_MODULES * + NPCM7XX_PWM_PER_MODULE]; +}; + + +struct NPCM8xxMachineClass { + MachineClass parent_class; + + const char *soc_type; +}; + +#define TYPE_NPCM8XX_MACHINE MACHINE_TYPE_NAME("npcm8xx") +OBJECT_DECLARE_TYPE(NPCM8xxMachine, NPCM8xxMachineClass, NPCM8XX_MACHINE) + struct NPCM8xxState { DeviceState parent_obj; From 1c3169179b8242866316108386800379c4e22974 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 19 Feb 2025 10:46:08 -0800 Subject: [PATCH 1842/2892] docs/system/arm: Add Description for NPCM8XX SoC NPCM8XX SoC is the successor of the NPCM7XX. It features quad-core Cortex-A35 (Armv8, 64-bit) CPUs and some additional peripherals. This document describes the NPCM8XX SoC and an evaluation board (NPCM 845 EVB). Signed-off-by: Hao Wu Reviewed-by: Peter Maydell Message-id: 20250219184609.1839281-18-wuhaotsh@google.com Signed-off-by: Peter Maydell --- docs/system/arm/nuvoton.rst | 27 ++++++++++++++++++++------- hw/arm/npcm8xx_boards.c | 1 + 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index 05059378e5..e4827fb43a 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -1,12 +1,13 @@ -Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``) -===================================================================================================== +Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``, ``npcm845-evb``) +====================================================================================================================== -The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are +The `Nuvoton iBMC`_ chips are a family of Arm-based SoCs that are designed to be used as Baseboard Management Controllers (BMCs) in various -servers. They all feature one or two ARM Cortex-A9 CPU cores, as well as an -assortment of peripherals targeted for either Enterprise or Data Center / -Hyperscale applications. The former is a superset of the latter, so NPCM750 has -all the peripherals of NPCM730 and more. +servers. Currently there are two families: NPCM7XX series and +NPCM8XX series. NPCM7XX series feature one or two Arm Cortex-A9 CPU cores, +while NPCM8XX feature 4 Arm Cortex-A35 CPU cores. Both series contain a +different assortment of peripherals targeted for either Enterprise or Data +Center / Hyperscale applications. .. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/ @@ -27,6 +28,11 @@ There are also two more SoCs, NPCM710 and NPCM705, which are single-core variants of NPCM750 and NPCM730, respectively. These are currently not supported by QEMU. +The NPCM8xx SoC is the successor of the NPCM7xx SoC. It has 4 Cortex-A35 cores. +The following machines are based on this chip : + +- ``npcm845-evb`` Nuvoton NPCM845 Evaluation board + Supported devices ----------------- @@ -62,6 +68,8 @@ Missing devices * System Wake-up Control (SWC) * Shared memory (SHM) * eSPI slave interface + * Block-transfer interface (8XX only) + * Virtual UART (8XX only) * Ethernet controller (GMAC) * USB device (USBD) @@ -76,6 +84,11 @@ Missing devices * Video capture * Encoding compression engine * Security features + * I3C buses (8XX only) + * Temperature sensor interface (8XX only) + * Virtual UART (8XX only) + * Flash monitor (8XX only) + * JTAG master (8XX only) Boot options ------------ diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c index 19610483f9..3fb8478e72 100644 --- a/hw/arm/npcm8xx_boards.c +++ b/hw/arm/npcm8xx_boards.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "chardev/char.h" +#include "hw/boards.h" #include "hw/arm/npcm8xx.h" #include "hw/core/cpu.h" #include "hw/loader.h" From 6f9a1a01433738811c9795dcf29f299c60835558 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 Jan 2025 07:46:44 +0100 Subject: [PATCH 1843/2892] docs/about: Change notes on x86 machine type deprecation into a general one We now have a general note about versioned machine types getting deprecated and removed at the beginning of the deprecated.rst file, so we should also have a general note about this in removed-features.rst (which will also apply to versioned non-x86 machine types) instead of listing individual old machine types in the document. Signed-off-by: Thomas Huth Message-Id: <20250116064644.65670-1-thuth@redhat.com> Reviewed-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/about/deprecated.rst | 7 ------- docs/about/removed-features.rst | 11 +++++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 4a3c302962..7b42d6eecc 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -236,13 +236,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' - -These old machine types are quite neglected nowadays and thus might have -various pitfalls with regards to live migration. Use a newer machine type -instead. - PPC 405 ``ref405ep`` machine (since 9.1) '''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index c6616ce05e..156c0c253c 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -972,6 +972,11 @@ from Linux in 2021, and is not supported anymore by QEMU either. System emulator machines ------------------------ +Note: Versioned machine types that have been introduced in a QEMU version +that has initially been released more than 6 years before are considered +obsolete and will be removed without further notice in this document. +Please use newer machine types instead. + ``s390-virtio`` (removed in 2.6) '''''''''''''''''''''''''''''''' @@ -1006,12 +1011,6 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-i440fx-2.3`` (removed in 4.0 up to 9.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -These machine types were very old and likely could not be used for live -migration from old QEMU versions anymore. Use a newer machine type instead. - Raspberry Pi ``raspi2`` and ``raspi3`` machines (removed in 6.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' From bc82af6b0dcb0933e72640851fdd2594f822b23e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 16:30:43 +0900 Subject: [PATCH 1844/2892] hw/net: Fix NULL dereference with software RSS When an eBPF program cannot be attached, virtio_net_load_ebpf() returns false, and virtio_net_device_realize() enters the code path to handle errors because of this, but it causes NULL dereference because no error is generated. Change virtio_net_load_ebpf() to return false only when a fatal error occurred. Fixes: b5900dff14e5 ("hw/net: report errors from failing to use eBPF RSS FDs") Signed-off-by: Akihiko Odaki Message-Id: <20250116-software-v1-1-9e5161b534d8@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 85e14b788c..d64941bf8e 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1352,18 +1352,25 @@ exit: static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) { - bool ret = false; - - if (virtio_net_attach_ebpf_to_backend(n->nic, -1)) { - trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds); - if (n->ebpf_rss_fds) { - ret = virtio_net_load_ebpf_fds(n, errp); - } else { - ret = ebpf_rss_load(&n->ebpf_rss, errp); - } + if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) { + return true; } - return ret; + trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds); + + /* + * If user explicitly gave QEMU RSS FDs to use, then + * failing to use them must be considered a fatal + * error. If no RSS FDs were provided, QEMU is trying + * eBPF on a "best effort" basis only, so report a + * warning and allow fallback to software RSS. + */ + if (n->ebpf_rss_fds) { + return virtio_net_load_ebpf_fds(n, errp); + } + + ebpf_rss_load(&n->ebpf_rss, &error_warn); + return true; } static void virtio_net_unload_ebpf(VirtIONet *n) @@ -3913,23 +3920,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) net_rx_pkt_init(&n->rx_pkt); if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { - Error *err = NULL; - if (!virtio_net_load_ebpf(n, &err)) { - /* - * If user explicitly gave QEMU RSS FDs to use, then - * failing to use them must be considered a fatal - * error. If no RSS FDs were provided, QEMU is trying - * eBPF on a "best effort" basis only, so report a - * warning and allow fallback to software RSS. - */ - if (n->ebpf_rss_fds) { - error_propagate(errp, err); - } else { - warn_report("unable to load eBPF RSS: %s", - error_get_pretty(err)); - error_free(err); - } - } + virtio_net_load_ebpf(n, errp); } } From 5731b005246297ab5b91975f52fcd525e08507cd Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:52 +0900 Subject: [PATCH 1845/2892] hw/ppc/spapr_pci: Do not create DT for disabled PCI device Disabled means it is a disabled SR-IOV VF and hidden from the guest. Do not create DT when starting the system and also keep the disabled PCI device not linked to DRC, which generates DT in case of hotplug. Signed-off-by: Akihiko Odaki Reviewed-by: Shivaprasad G Bhat Tested-by: Shivaprasad G Bhat Message-Id: <20250116-reuse-v20-1-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ppc/spapr_pci.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 904227d9aa..b94e4ba131 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1283,8 +1283,7 @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev, PciWalkFdt *p = opaque; int err; - if (p->err) { - /* Something's already broken, don't keep going */ + if (p->err || !pdev->enabled) { return; } @@ -1572,6 +1571,14 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, SpaprDrc *drc = drc_from_dev(phb, pdev); uint32_t slotnr = PCI_SLOT(pdev->devfn); + /* + * If DR or the PCI device is disabled we don't need to do anything + * in the case of hotplug or coldplug callbacks. + */ + if (!pdev->enabled) { + return; + } + g_assert(drc); if (IS_PCI_BRIDGE(plugged_dev)) { @@ -1647,6 +1654,11 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, SpaprDrc *drc = drc_from_dev(phb, pdev); g_assert(drc); + + if (!drc->dev) { + return; + } + g_assert(drc->dev == plugged_dev); if (!spapr_drc_unplug_requested(drc)) { From 3bc31441ba31c94f12b7b96b1960abb4c1f21ee8 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:53 +0900 Subject: [PATCH 1846/2892] hw/ppc/spapr_pci: Do not reject VFs created after a PF A PF may automatically create VFs and the PF may be function 0. Signed-off-by: Akihiko Odaki Reviewed-by: Shivaprasad G Bhat Tested-by: Shivaprasad G Bhat Message-Id: <20250116-reuse-v20-2-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ppc/spapr_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index b94e4ba131..e0a9d50edc 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1549,7 +1549,9 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, * hotplug, we do not allow functions to be hotplugged to a * slot that already has function 0 present */ - if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && + if (plugged_dev->hotplugged && + !pci_is_vf(pdev) && + bus->devices[PCI_DEVFN(slotnr, 0)] && PCI_FUNC(pdev->devfn) != 0) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " additional functions can no longer be exposed to guest.", From b77a2778d9bbefc6119331ce3083d85d69d7a0c1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:54 +0900 Subject: [PATCH 1847/2892] s390x/pci: Avoid creating zpci for VFs VFs are automatically created by PF, and creating zpci for them will result in unexpected usage of fids. Currently QEMU does not support multifunction for s390x so we don't need zpci for VFs anyway. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-3-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/s390x/s390-pci-bus.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index eead269cc2..8c5eb69f7d 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1080,6 +1080,16 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, pbdev = s390_pci_find_dev_by_target(s, dev->id); if (!pbdev) { + /* + * VFs are automatically created by PF, and creating zpci for them + * will result in unexpected usage of fids. Currently QEMU does not + * support multifunction for s390x so we don't need zpci for VFs + * anyway. + */ + if (pci_is_vf(pdev)) { + return; + } + pbdev = s390_pci_device_new(s, dev->id, errp); if (!pbdev) { return; @@ -1167,7 +1177,10 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, int32_t devfn; pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev)); - g_assert(pbdev); + if (!pbdev) { + g_assert(pci_is_vf(pci_dev)); + return; + } s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, pbdev->fh, pbdev->fid); @@ -1206,7 +1219,11 @@ static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev, * we've checked the PCI device already (to prevent endless recursion). */ pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev)); - g_assert(pbdev); + if (!pbdev) { + g_assert(pci_is_vf(PCI_DEVICE(dev))); + return; + } + pbdev->pci_unplug_request_processed = true; qdev_unplug(DEVICE(pbdev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { From d966250e93c1194c0a4c775e08a8e23f2ef66cf8 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:55 +0900 Subject: [PATCH 1848/2892] s390x/pci: Allow plugging SR-IOV devices The guest cannot use VFs due to the lack of multifunction support but can use PFs. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-4-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/s390x/s390-pci-bus.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 8c5eb69f7d..c396d55c72 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -974,7 +974,14 @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pdev = PCI_DEVICE(dev); - if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + /* + * Multifunction is not supported due to the lack of CLP. However, + * do not check for multifunction capability for SR-IOV devices because + * SR-IOV devices automatically add the multifunction capability whether + * the user intends to use the functions other than the PF. + */ + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION && + !pdev->exp.sriov_cap) { error_setg(errp, "multifunction not supported in s390"); return; } From 2c968f465392316d09e44f4a9f7e378999e5c011 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:56 +0900 Subject: [PATCH 1849/2892] s390x/pci: Check for multifunction after device realization The SR-IOV PFs set the multifunction bit during device realization so check them after that. There is no functional change because we explicitly ignore the multifunction bit for SR-IOV devices. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-5-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/s390x/s390-pci-bus.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index c396d55c72..913d72cc74 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -971,21 +971,7 @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, "this device"); } - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pdev = PCI_DEVICE(dev); - - /* - * Multifunction is not supported due to the lack of CLP. However, - * do not check for multifunction capability for SR-IOV devices because - * SR-IOV devices automatically add the multifunction capability whether - * the user intends to use the functions other than the PF. - */ - if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION && - !pdev->exp.sriov_cap) { - error_setg(errp, "multifunction not supported in s390"); - return; - } - } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); if (!s390_pci_alloc_idx(s, pbdev)) { @@ -1076,6 +1062,18 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { pdev = PCI_DEVICE(dev); + /* + * Multifunction is not supported due to the lack of CLP. However, + * do not check for multifunction capability for SR-IOV devices because + * SR-IOV devices automatically add the multifunction capability whether + * the user intends to use the functions other than the PF. + */ + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION && + !pdev->exp.sriov_cap) { + error_setg(errp, "multifunction not supported in s390"); + return; + } + if (!dev->id) { /* In the case the PCI device does not define an id */ /* we generate one based on the PCI address */ From e8b827ce7c4bf9a334c46282779f234d1a011cd1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:57 +0900 Subject: [PATCH 1850/2892] pcie_sriov: Do not manually unrealize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A device gets automatically unrealized when being unparented. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-6-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- hw/pci/pcie_sriov.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index e9b23221d7..499becd527 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -204,11 +204,7 @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - Error *err = NULL; PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - if (!object_property_set_bool(OBJECT(vf), "realized", false, &err)) { - error_reportf_err(err, "Failed to unplug: "); - } object_unparent(OBJECT(vf)); object_unref(OBJECT(vf)); } From 3391d68e906114c364c173c7f3f7389d47d15a11 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:58 +0900 Subject: [PATCH 1851/2892] pcie_sriov: Ensure VF addr does not overflow pci_new() aborts when creating a VF with addr >= PCI_DEVFN_MAX. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-7-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/pcie_sriov.txt | 8 +++++--- hw/net/igb.c | 10 +++++++--- hw/nvme/ctrl.c | 22 ++++++++++++++-------- hw/pci/pcie_sriov.c | 14 ++++++++++++-- include/hw/pci/pcie_sriov.h | 5 +++-- 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt index a47aad0bfa..ab2142807f 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -52,9 +52,11 @@ setting up a BAR for a VF. ... /* Add and initialize the SR/IOV capability */ - pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", - vf_devid, initial_vfs, total_vfs, - fun_offset, stride); + if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", + vf_devid, initial_vfs, total_vfs, + fun_offset, stride, errp)) { + return; + } /* Set up individual VF BARs (parameters as for normal BARs) */ pcie_sriov_pf_init_vf_bar( ... ) diff --git a/hw/net/igb.c b/hw/net/igb.c index 4d93ce629f..c965fc2fb6 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -446,9 +446,13 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_ari_init(pci_dev, 0x150); - pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF, - IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, - IGB_VF_OFFSET, IGB_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF, + IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, + IGB_MAX_VF_FUNCTIONS, IGB_VF_OFFSET, IGB_VF_STRIDE, + errp)) { + igb_cleanup_msix(s); + return; + } pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 68903d1d70..8175751518 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8481,7 +8481,8 @@ out: return pow2ceil(bar_size); } -static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) +static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset, + Error **errp) { uint16_t vf_dev_id = n->params.use_intel_id ? PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME; @@ -8490,12 +8491,16 @@ static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) le16_to_cpu(cap->vifrsm), NULL, NULL); - pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, - n->params.sriov_max_vfs, n->params.sriov_max_vfs, - NVME_VF_OFFSET, NVME_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE, errp)) { + return false; + } pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); + + return true; } static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) @@ -8620,6 +8625,11 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) return false; } + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs && + !nvme_init_sriov(n, pci_dev, 0x120, errp)) { + return false; + } + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); pcie_cap_deverr_init(pci_dev); @@ -8649,10 +8659,6 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_pmr(n, pci_dev); } - if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { - nvme_init_sriov(n, pci_dev, 0x120); - } - return true; } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 499becd527..91c64c988e 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -24,14 +24,22 @@ static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, uint16_t vf_num); static void unregister_vfs(PCIDevice *dev); -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride) + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) { + int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (total_vfs && + (uint32_t)devfn + (uint32_t)(total_vfs - 1) * vf_stride >= PCI_DEVFN_MAX) { + error_setg(errp, "VF addr overflows"); + return false; + } + pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; @@ -69,6 +77,8 @@ void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); qdev_prop_set_bit(&dev->qdev, "multifunction", true); + + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 450cbef6c2..aa704e8f9d 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -27,10 +27,11 @@ typedef struct PCIESriovVF { uint16_t vf_number; /* Logical VF number of this function */ } PCIESriovVF; -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride); + uint16_t vf_offset, uint16_t vf_stride, + Error **errp); void pcie_sriov_pf_exit(PCIDevice *dev); /* Set up a VF bar in the SR/IOV bar area */ From cab1398a60eb0cb2d2d1998c9b46aaa5e0bf3ee8 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:00:59 +0900 Subject: [PATCH 1852/2892] pcie_sriov: Reuse SR-IOV VF device instances Disable SR-IOV VF devices by reusing code to power down PCI devices instead of removing them when the guest requests to disable VFs. This allows to realize devices and report VF realization errors at PF realization time. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-8-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 14 +++++- hw/pci/pcie_sriov.c | 94 +++++++++++++++---------------------- include/hw/pci/pcie_sriov.h | 1 - 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2afa423925..3e29b30d55 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2963,7 +2963,17 @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) void pci_set_power(PCIDevice *d, bool state) { - pci_set_enabled(d, state); + /* + * Don't change the enabled state of VFs when powering on/off the device. + * + * When powering on, VFs must not be enabled immediately but they must + * wait until the guest configures SR-IOV. + * When powering off, their corresponding PFs will be reset and disable + * VFs. + */ + if (!pci_is_vf(d)) { + pci_set_enabled(d, state); + } } void pci_set_enabled(PCIDevice *d, bool state) @@ -2977,7 +2987,7 @@ void pci_set_enabled(PCIDevice *d, bool state) memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_MASTER) && d->enabled); - if (!d->enabled) { + if (qdev_is_realized(&d->qdev)) { pci_device_reset(d); } } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 91c64c988e..f1993bc553 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -20,9 +20,16 @@ #include "qapi/error.h" #include "trace.h" -static PCIDevice *register_vf(PCIDevice *pf, int devfn, - const char *name, uint16_t vf_num); -static void unregister_vfs(PCIDevice *dev); +static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) +{ + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + object_unparent(OBJECT(vf)); + object_unref(OBJECT(vf)); + } + g_free(dev->exp.sriov_pf.vf); + dev->exp.sriov_pf.vf = NULL; +} bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, @@ -30,6 +37,7 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint16_t vf_offset, uint16_t vf_stride, Error **errp) { + BusState *bus = qdev_get_parent_bus(&dev->qdev); int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -44,7 +52,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; dev->exp.sriov_pf.num_vfs = 0; - dev->exp.sriov_pf.vfname = g_strdup(vfname); dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -78,14 +85,34 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); + dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); + + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = pci_new(devfn, vfname); + vf->exp.sriov_vf.pf = dev; + vf->exp.sriov_vf.vf_number = i; + + if (!qdev_realize(&vf->qdev, bus, errp)) { + unparent_vfs(dev, i); + return false; + } + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vf->config, 0xffff); + pci_config_set_device_id(vf->config, 0xffff); + + dev->exp.sriov_pf.vf[i] = vf; + devfn += vf_stride; + } + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) { - unregister_vfs(dev); - g_free((char *)dev->exp.sriov_pf.vfname); - dev->exp.sriov_pf.vfname = NULL; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -151,38 +178,11 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, } } -static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, - uint16_t vf_num) -{ - PCIDevice *dev = pci_new(devfn, name); - dev->exp.sriov_vf.pf = pf; - dev->exp.sriov_vf.vf_number = vf_num; - PCIBus *bus = pci_get_bus(pf); - Error *local_err = NULL; - - qdev_realize(&dev->qdev, &bus->qbus, &local_err); - if (local_err) { - error_report_err(local_err); - return NULL; - } - - /* set vid/did according to sr/iov spec - they are not used */ - pci_config_set_vendor_id(dev->config, 0xffff); - pci_config_set_device_id(dev->config, 0xffff); - - return dev; -} - static void register_vfs(PCIDevice *dev) { uint16_t num_vfs; uint16_t i; uint16_t sriov_cap = dev->exp.sriov_cap; - uint16_t vf_offset = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET); - uint16_t vf_stride = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - int32_t devfn = dev->devfn + vf_offset; assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); @@ -190,18 +190,10 @@ static void register_vfs(PCIDevice *dev) return; } - dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn, - dev->exp.sriov_pf.vfname, i); - if (!dev->exp.sriov_pf.vf[i]) { - num_vfs = i; - break; - } - devfn += vf_stride; + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } dev->exp.sriov_pf.num_vfs = num_vfs; } @@ -214,12 +206,8 @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - object_unparent(OBJECT(vf)); - object_unref(OBJECT(vf)); + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } - g_free(dev->exp.sriov_pf.vf); - dev->exp.sriov_pf.vf = NULL; dev->exp.sriov_pf.num_vfs = 0; } @@ -241,14 +229,10 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, PCI_FUNC(dev->devfn), off, val, len); if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (dev->exp.sriov_pf.num_vfs) { - if (!(val & PCI_SRIOV_CTRL_VFE)) { - unregister_vfs(dev); - } + if (val & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); } else { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); - } + unregister_vfs(dev); } } } diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index aa704e8f9d..70649236c1 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -18,7 +18,6 @@ typedef struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ - const char *vfname; /* Reference to the device type used for the VFs */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; From b85901e728d048c931d110bfd5ff8f119cc97df7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:01:00 +0900 Subject: [PATCH 1853/2892] pcie_sriov: Release VFs failed to realize Release VFs failed to realize just as we do in unregister_vfs(). Fixes: 7c0fa8dff811 ("pcie: Add support for Single Root I/O Virtualization (SR/IOV)") Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-9-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index f1993bc553..db087bb933 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -93,6 +93,8 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, vf->exp.sriov_vf.vf_number = i; if (!qdev_realize(&vf->qdev, bus, errp)) { + object_unparent(OBJECT(vf)); + object_unref(vf); unparent_vfs(dev, i); return false; } From 5e7dd17e43486f41d4f88dbefd7219f0524f424c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:01:01 +0900 Subject: [PATCH 1854/2892] pcie_sriov: Remove num_vfs from PCIESriovPF num_vfs is not migrated so use PCI_SRIOV_CTRL_VFE and PCI_SRIOV_NUM_VF instead. Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-10-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 38 ++++++++++++++++++++++++++----------- hw/pci/trace-events | 2 +- include/hw/pci/pcie_sriov.h | 1 - 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index db087bb933..69609c112e 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -51,7 +51,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; - dev->exp.sriov_pf.num_vfs = 0; dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -188,29 +187,28 @@ static void register_vfs(PCIDevice *dev) assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); - if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { - return; - } trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } - dev->exp.sriov_pf.num_vfs = num_vfs; + + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0); } static void unregister_vfs(PCIDevice *dev) { - uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; uint16_t i; trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } - dev->exp.sriov_pf.num_vfs = 0; + + pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); } void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, @@ -236,6 +234,17 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } else { unregister_vfs(dev); } + } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { + uint8_t *cfg = dev->config + sriov_cap; + uint8_t *wmask = dev->wmask + sriov_cap; + uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); + uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; + + if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { + wmask_val |= PCI_SRIOV_CTRL_VFE; + } + + pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); } } @@ -252,6 +261,8 @@ void pcie_sriov_pf_reset(PCIDevice *dev) unregister_vfs(dev); pci_set_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF, 0); + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_CTRL, + PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI); /* * Default is to use 4K pages, software can modify it @@ -298,7 +309,7 @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) { assert(!pci_is_vf(dev)); - if (n < dev->exp.sriov_pf.num_vfs) { + if (n < pcie_sriov_num_vfs(dev)) { return dev->exp.sriov_pf.vf[n]; } return NULL; @@ -306,5 +317,10 @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) uint16_t pcie_sriov_num_vfs(PCIDevice *dev) { - return dev->exp.sriov_pf.num_vfs; + uint16_t sriov_cap = dev->exp.sriov_cap; + uint8_t *cfg = dev->config + sriov_cap; + + return sriov_cap && + (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ? + pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0; } diff --git a/hw/pci/trace-events b/hw/pci/trace-events index 19643aa8c6..e98f575a9d 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -14,7 +14,7 @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask # hw/pci/pcie_sriov.c sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs" -sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs" +sriov_unregister_vfs(const char *name, int slot, int function) "%s %02x:%x: Unregistering vf devs" sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d" # pcie.c diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 70649236c1..5148c5b77d 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -16,7 +16,6 @@ #include "hw/pci/pci.h" typedef struct PCIESriovPF { - uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; From f9efcd47110de43dd841ada5bd1a40ec169eabca Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 16 Jan 2025 18:01:02 +0900 Subject: [PATCH 1855/2892] pcie_sriov: Register VFs after migration pcie_sriov doesn't have code to restore its state after migration, but igb, which uses pcie_sriov, naively claimed its migration capability. Add code to register VFs after migration and fix igb migration. Fixes: 3a977deebe6b ("Intrdocue igb device emulation") Signed-off-by: Akihiko Odaki Message-Id: <20250116-reuse-v20-11-7cb370606368@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 7 +++++++ hw/pci/pcie_sriov.c | 7 +++++++ include/hw/pci/pcie_sriov.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 3e29b30d55..69a1b8c298 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -803,10 +803,17 @@ static bool migrate_is_not_pcie(void *opaque, int version_id) return !pci_is_express((PCIDevice *)opaque); } +static int pci_post_load(void *opaque, int version_id) +{ + pcie_sriov_pf_post_load(opaque); + return 0; +} + const VMStateDescription vmstate_pci_device = { .name = "PCIDevice", .version_id = 2, .minimum_version_id = 1, + .post_load = pci_post_load, .fields = (const VMStateField[]) { VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice, diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 69609c112e..1eb4358256 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -248,6 +248,13 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } } +void pcie_sriov_pf_post_load(PCIDevice *dev) +{ + if (dev->exp.sriov_cap) { + register_vfs(dev); + } +} + /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 5148c5b77d..c5d2d318d3 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -57,6 +57,8 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize); void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, uint32_t val, int len); +void pcie_sriov_pf_post_load(PCIDevice *dev); + /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev); From 9e837c961a883392f8c4707a8d3d2e6c6aa793b6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 18 Jan 2025 03:22:40 +1000 Subject: [PATCH 1856/2892] qtest/libqos/pci: Do not write to PBA memory The PCI Local Bus Specification says the result of writes to MSI-X PBA memory is undefined. QEMU implements them as no-ops, so remove the pointless write from qpci_msix_pending(). Signed-off-by: Nicholas Piggin Message-Id: <20250117172244.406206-2-npiggin@gmail.com> Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/libqos/pci.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c index b23d72346b..a59197b992 100644 --- a/tests/qtest/libqos/pci.c +++ b/tests/qtest/libqos/pci.c @@ -328,8 +328,6 @@ bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) g_assert(dev->msix_enabled); pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); - qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, - pba_entry & ~(1 << bit_n)); return (pba_entry & (1 << bit_n)) != 0; } From 44ed44aefec571041fe3b3a8b6849613a74b520a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 18 Jan 2025 03:28:40 +1000 Subject: [PATCH 1857/2892] hw/pci/msix: Warn on PBA writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of the MSI-X PBA pending bits, the PCI Local Bus Specification says: Software should never write, and should only read Pending Bits. If software writes to Pending Bits, the result is undefined. Log a GUEST_ERROR message if the PBA is written to by software. Cc: Marcel Apfelbaum Cc: Dmitry Fleytman Cc: Sriram Yagnaraman Signed-off-by: Nicholas Piggin Message-Id: <20250117172842.406338-2-npiggin@gmail.com> Reviewed-by: Phil Dennis-Jordan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/msix.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 57ec7084a4..66f27b9d71 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -15,6 +15,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/pci/pci.h" @@ -260,6 +261,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, static void msix_pba_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { + PCIDevice *dev = opaque; + + qemu_log_mask(LOG_GUEST_ERROR, + "PCI [%s:%02x:%02x.%x] attempt to write to MSI-X " + "PBA at 0x%" FMT_PCIBUS ", ignoring.\n", + pci_root_bus_path(dev), pci_dev_bus_num(dev), + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + addr); } static const MemoryRegionOps msix_pba_mmio_ops = { From f6fc01c7866639649b9af58ad50a7367b2d18eae Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 18 Jan 2025 03:28:41 +1000 Subject: [PATCH 1858/2892] hw/pci: Assert a bar is not registered multiple times Nothing should be doing this, but it doesn't get caught by pci_register_bar(). Add an assertion to prevent misuse. Signed-off-by: Nicholas Piggin Message-Id: <20250117172842.406338-3-npiggin@gmail.com> Reviewed-by: Phil Dennis-Jordan Signed-off-by: Nicholas Piggin Reviewed-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 69a1b8c298..1d42847ef0 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1398,6 +1398,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2); r = &pci_dev->io_regions[region_num]; + assert(!r->size); r->addr = PCI_BAR_UNMAPPED; r->size = size; r->type = type; From de538288e4dac21332cc94ba9727ed8ec8fe5ea1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 17 Jan 2025 20:21:06 +0100 Subject: [PATCH 1859/2892] hw/i386/pc: Fix crash that occurs when introspecting TYPE_PC_MACHINE machines QEMU currently crashes when you try to inspect the machines based on TYPE_PC_MACHINE for their properties: $ echo '{ "execute": "qmp_capabilities" } { "execute": "qom-list-properties","arguments": { "typename": "pc-q35-10.0-machine"}}' \ | ./qemu-system-x86_64 -M pc -qmp stdio {"QMP": {"version": {"qemu": {"micro": 50, "minor": 2, "major": 9}, "package": "v9.2.0-1070-g87e115c122-dirty"}, "capabilities": ["oob"]}} {"return": {}} Segmentation fault (core dumped) This happens because TYPE_PC_MACHINE machines add a machine_init- done_notifier in their instance_init function - but instance_init of machines are not only called for machines that are realized, but also for machines that are introspected, so in this case the listener is added for a q35 machine that is never realized. But since there is already a running pc machine, the listener function is triggered immediately, causing a crash since it was not for the right machine it was meant for. Such listener functions must never be installed from an instance_init function. Let's do it from pc_basic_device_init() instead - this function is called from the MachineClass->init() function instead, i.e. guaranteed to be only called once in the lifetime of a QEMU process. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2779 Signed-off-by: Thomas Huth Message-Id: <20250117192106.471029-1-thuth@redhat.com> Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b46975c8a4..85b8a76455 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1241,6 +1241,9 @@ void pc_basic_device_init(struct PCMachineState *pcms, /* Super I/O */ pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled, pcms->vmport != ON_OFF_AUTO_ON, &error_fatal); + + pcms->machine_done.notify = pc_machine_done; + qemu_add_machine_init_done_notifier(&pcms->machine_done); } void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) @@ -1714,9 +1717,6 @@ static void pc_machine_initfn(Object *obj) if (pcmc->pci_enabled) { cxl_machine_init(obj, &pcms->cxl_devices_state); } - - pcms->machine_done.notify = pc_machine_done; - qemu_add_machine_init_done_notifier(&pcms->machine_done); } static void pc_machine_reset(MachineState *machine, ResetType type) From 38ef383073b8ee59d598643160f206a19a46237f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 23 Jan 2025 21:47:08 +0100 Subject: [PATCH 1860/2892] hw/i386/microvm: Fix crash that occurs when introspecting the microvm machine QEMU currently crashes when you try to inspect the properties of the microvm machine: $ echo '{ "execute": "qmp_capabilities" } { "execute": "qom-list-properties","arguments": { "typename": "microvm-machine"}}' | \ ./qemu-system-x86_64 -qmp stdio {"QMP": {"version": {"qemu": {"micro": 50, "minor": 2, "major": 9}, "package": "v9.2.0-1072-g60af367187-dirty"}, "capabilities": ["oob"]}} {"return": {}} qemu-system-x86_64: ../qemu/hw/i386/acpi-microvm.c:250: void acpi_setup_microvm(MicrovmMachineState *): Assertion `x86ms->fw_cfg' failed. Aborted (core dumped) This happens because the microvm machine adds a machine_done (and a powerdown_req) notifier in their instance_init function - however, the instance_init of machines are not only called for machines that are realized, but also for machines that are introspected, so in this case the listener is added for a microvm machine that is never realized. And since there is already a running machine, the listener function is triggered immediately, causing a crash since it was not for the right machine it was meant for. Such listener functions must never be installed from an instance_init function. Let's do it from microvm_machine_state_init() instead - this function is the MachineClass->init() function instead, i.e. guaranteed to be only called once in the lifetime of a QEMU process. Since the microvm_machine_done() and microvm_powerdown_req() were defined quite late in the microvm.c file, we have to move them now also earlier, so that we can get their function pointers from microvm_machine_state_init() without having to introduce a separate prototype for those functions earlier. Reviewed-by: Sergio Lopez Signed-off-by: Thomas Huth Message-Id: <20250123204708.1560305-1-thuth@redhat.com> Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/microvm.c | 66 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index a8d354aabe..d0a236c74f 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -451,11 +451,44 @@ static HotplugHandler *microvm_get_hotplug_handler(MachineState *machine, return NULL; } +static void microvm_machine_done(Notifier *notifier, void *data) +{ + MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, + machine_done); + X86MachineState *x86ms = X86_MACHINE(mms); + + acpi_setup_microvm(mms); + dt_setup_microvm(mms); + fw_cfg_add_e820(x86ms->fw_cfg); +} + +static void microvm_powerdown_req(Notifier *notifier, void *data) +{ + MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, + powerdown_req); + X86MachineState *x86ms = X86_MACHINE(mms); + + if (x86ms->acpi_dev) { + Object *obj = OBJECT(x86ms->acpi_dev); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); + adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev), + ACPI_POWER_DOWN_STATUS); + } +} + static void microvm_machine_state_init(MachineState *machine) { MicrovmMachineState *mms = MICROVM_MACHINE(machine); X86MachineState *x86ms = X86_MACHINE(machine); + /* State */ + mms->kernel_cmdline_fixed = false; + + mms->machine_done.notify = microvm_machine_done; + qemu_add_machine_init_done_notifier(&mms->machine_done); + mms->powerdown_req.notify = microvm_powerdown_req; + qemu_register_powerdown_notifier(&mms->powerdown_req); + microvm_memory_init(mms); x86_cpus_init(x86ms, CPU_VERSION_LATEST); @@ -581,31 +614,6 @@ static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value, mms->auto_kernel_cmdline = value; } -static void microvm_machine_done(Notifier *notifier, void *data) -{ - MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, - machine_done); - X86MachineState *x86ms = X86_MACHINE(mms); - - acpi_setup_microvm(mms); - dt_setup_microvm(mms); - fw_cfg_add_e820(x86ms->fw_cfg); -} - -static void microvm_powerdown_req(Notifier *notifier, void *data) -{ - MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, - powerdown_req); - X86MachineState *x86ms = X86_MACHINE(mms); - - if (x86ms->acpi_dev) { - Object *obj = OBJECT(x86ms->acpi_dev); - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev), - ACPI_POWER_DOWN_STATUS); - } -} - static void microvm_machine_initfn(Object *obj) { MicrovmMachineState *mms = MICROVM_MACHINE(obj); @@ -617,14 +625,6 @@ static void microvm_machine_initfn(Object *obj) mms->isa_serial = true; mms->option_roms = true; mms->auto_kernel_cmdline = true; - - /* State */ - mms->kernel_cmdline_fixed = false; - - mms->machine_done.notify = microvm_machine_done; - qemu_add_machine_init_done_notifier(&mms->machine_done); - mms->powerdown_req.notify = microvm_powerdown_req; - qemu_register_powerdown_notifier(&mms->powerdown_req); } GlobalProperty microvm_properties[] = { From 14998e50c3638b07946ebe5972a327cc26e9f03c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 3 Feb 2025 13:43:46 +0100 Subject: [PATCH 1861/2892] tests/qtest/vhost-user-test: Use modern virtio for vhost-user tests All other vhost-user tests here use modern virtio, too, so let's adjust the vhost-user-net test accordingly. Signed-off-by: Thomas Huth Message-Id: <20250203124346.169607-1-thuth@redhat.com> Reviewed-by: Fabiano Rosas Reviewed-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/vhost-user-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 76d142a158..bd977ef28d 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -1043,7 +1043,8 @@ static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) static uint64_t vu_net_get_features(TestServer *s) { - uint64_t features = 0x1ULL << VHOST_F_LOG_ALL | + uint64_t features = 0x1ULL << VIRTIO_F_VERSION_1 | + 0x1ULL << VHOST_F_LOG_ALL | 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; if (s->queues > 1) { From 8f90a54cfafe8c93a71930a96a63ccbd074f4142 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Mon, 3 Feb 2025 16:19:04 +0000 Subject: [PATCH 1862/2892] hw/cxl: Introduce CXL_T3_MSIX_VECTOR enumeration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the `CXL_T3_MSIX_VECTOR` enumeration to specify MSIX vector assignments specific to the Type 3 (T3) CXL device. The primary goal of this change is to encapsulate the MSIX vector uses that are unique to the T3 device within an enumeration, improving code readability and maintenance by avoiding magic numbers. This organizational change allows for more explicit references to each vector’s role, thereby reducing the potential for misconfiguration. It also modified `mailbox_reg_init_common` to accept the `msi_n` parameter, reflecting the new MSIX vector setup. This pertains to the T3 device privately; other endpoints should refrain from using it, despite its public accessibility to all of them. Signed-off-by: Li Zhijian Signed-off-by: Jonathan Cameron Message-Id: <20250203161908.145406-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 12 +++++------- hw/cxl/switch-mailbox-cci.c | 4 +++- hw/mem/cxl_type3.c | 20 ++++++++++++++------ include/hw/cxl/cxl_device.h | 4 ++-- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index 035d034f6d..52ad1e4c3f 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -352,10 +352,8 @@ static void device_reg_init_common(CXLDeviceState *cxl_dstate) } } -static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) +static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate, int msi_n) { - const uint8_t msi_n = 9; - /* 2048 payload size */ ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT); @@ -382,7 +380,7 @@ static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) cxl_dstate->memdev_status = memdev_status_reg; } -void cxl_device_register_init_t3(CXLType3Dev *ct3d) +void cxl_device_register_init_t3(CXLType3Dev *ct3d, int msi_n) { CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; uint64_t *cap_h = cxl_dstate->caps_reg_state64; @@ -398,7 +396,7 @@ void cxl_device_register_init_t3(CXLType3Dev *ct3d) device_reg_init_common(cxl_dstate); cxl_device_cap_init(cxl_dstate, MAILBOX, 2, CXL_DEV_MAILBOX_VERSION); - mailbox_reg_init_common(cxl_dstate); + mailbox_reg_init_common(cxl_dstate, msi_n); cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, CXL_MEM_DEV_STATUS_VERSION); @@ -408,7 +406,7 @@ void cxl_device_register_init_t3(CXLType3Dev *ct3d) CXL_MAILBOX_MAX_PAYLOAD_SIZE); } -void cxl_device_register_init_swcci(CSWMBCCIDev *sw) +void cxl_device_register_init_swcci(CSWMBCCIDev *sw, int msi_n) { CXLDeviceState *cxl_dstate = &sw->cxl_dstate; uint64_t *cap_h = cxl_dstate->caps_reg_state64; @@ -423,7 +421,7 @@ void cxl_device_register_init_swcci(CSWMBCCIDev *sw) device_reg_init_common(cxl_dstate); cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1); - mailbox_reg_init_common(cxl_dstate); + mailbox_reg_init_common(cxl_dstate, msi_n); cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1); memdev_reg_init_common(cxl_dstate); diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c index 65cdac6cc1..833b824619 100644 --- a/hw/cxl/switch-mailbox-cci.c +++ b/hw/cxl/switch-mailbox-cci.c @@ -17,10 +17,12 @@ #include "hw/qdev-properties.h" #include "hw/cxl/cxl.h" +#define CXL_SWCCI_MSIX_MBOX 3 + static void cswmbcci_reset(DeviceState *dev) { CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev); - cxl_device_register_init_swcci(cswmb); + cxl_device_register_init_swcci(cswmb, CXL_SWCCI_MSIX_MBOX); } static void cswbcci_realize(PCIDevice *pci_dev, Error **errp) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 0ae1704a34..ebc0ec536e 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -30,6 +30,14 @@ #include "hw/cxl/cxl.h" #include "hw/pci/msix.h" +/* type3 device private */ +enum CXL_T3_MSIX_VECTOR { + CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS = 0, + CXL_T3_MSIX_EVENT_START = 2, + CXL_T3_MSIX_MBOX = CXL_T3_MSIX_EVENT_START + CXL_EVENT_TYPE_MAX, + CXL_T3_MSIX_VECTOR_NR +}; + #define DWORD_BYTE 4 #define CXL_CAPACITY_MULTIPLIER (256 * MiB) @@ -843,7 +851,6 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) ComponentRegisters *regs = &cxl_cstate->crb; MemoryRegion *mr = ®s->component_registers; uint8_t *pci_conf = pci_dev->config; - unsigned short msix_num = 10; int i, rc; uint16_t count; @@ -884,16 +891,17 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) &ct3d->cxl_dstate.device_registers); /* MSI(-X) Initialization */ - rc = msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL); + rc = msix_init_exclusive_bar(pci_dev, CXL_T3_MSIX_VECTOR_NR, 4, NULL); if (rc) { goto err_address_space_free; } - for (i = 0; i < msix_num; i++) { + for (i = 0; i < CXL_T3_MSIX_VECTOR_NR; i++) { msix_vector_use(pci_dev, i); } /* DOE Initialization */ - pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 0); + pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, + CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS); cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table; cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table; @@ -908,7 +916,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) if (rc) { goto err_release_cdat; } - cxl_event_init(&ct3d->cxl_dstate, 2); + cxl_event_init(&ct3d->cxl_dstate, CXL_T3_MSIX_EVENT_START); /* Set default value for patrol scrub attributes */ ct3d->patrol_scrub_attrs.scrub_cycle_cap = @@ -1202,7 +1210,7 @@ static void ct3d_reset(DeviceState *dev) pcie_cap_fill_link_ep_usp(PCI_DEVICE(dev), ct3d->width, ct3d->speed); cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE); - cxl_device_register_init_t3(ct3d); + cxl_device_register_init_t3(ct3d, CXL_T3_MSIX_MBOX); /* * Bring up an endpoint to target with MCTP over VDM. diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 561b375dc8..3a0ee7e8e7 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -264,8 +264,8 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev, typedef struct CXLType3Dev CXLType3Dev; typedef struct CSWMBCCIDev CSWMBCCIDev; /* Set up default values for the register block */ -void cxl_device_register_init_t3(CXLType3Dev *ct3d); -void cxl_device_register_init_swcci(CSWMBCCIDev *sw); +void cxl_device_register_init_t3(CXLType3Dev *ct3d, int msi_n); +void cxl_device_register_init_swcci(CSWMBCCIDev *sw, int msi_n); /* * CXL r3.1 Section 8.2.8.1: CXL Device Capabilities Array Register From 0401c4328f4d18be540fd432c2bbacce4531d14f Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Mon, 3 Feb 2025 16:19:05 +0000 Subject: [PATCH 1863/2892] hw/mem/cxl_type3: Add paired msix_uninit_exclusive_bar() call msix_uninit_exclusive_bar() should be paired with msix_init_exclusive_bar() Ensure proper resource cleanup by adding the missing `msix_uninit_exclusive_bar()` call for the Type3 CXL device. Signed-off-by: Li Zhijian Signed-off-by: Jonathan Cameron Message-Id: <20250203161908.145406-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index ebc0ec536e..4775aab0d6 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -944,6 +944,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) err_release_cdat: cxl_doe_cdat_release(cxl_cstate); err_free_special_ops: + msix_uninit_exclusive_bar(pci_dev); g_free(regs->special_ops); err_address_space_free: if (ct3d->dc.host_dc) { @@ -967,6 +968,7 @@ static void ct3_exit(PCIDevice *pci_dev) pcie_aer_exit(pci_dev); cxl_doe_cdat_release(cxl_cstate); + msix_uninit_exclusive_bar(pci_dev); g_free(regs->special_ops); if (ct3d->dc.host_dc) { cxl_destroy_dc_regions(ct3d); From 06953e7e5ea5e8fa0b7b704bdb66ab1e62f1f378 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Mon, 3 Feb 2025 16:19:06 +0000 Subject: [PATCH 1864/2892] hw/mem/cxl_type3: Fix special_ops memory leak on msix_init_exclusive_bar() failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address a memory leak issue by ensuring `regs->special_ops` is freed when `msix_init_exclusive_bar()` encounters an error during CXL Type3 device initialization. Additionally, this patch renames err_address_space_free to err_msix_uninit for better clarity and logical flow Signed-off-by: Li Zhijian Signed-off-by: Jonathan Cameron Message-Id: <20250203161908.145406-4-Jonathan.Cameron@huawei.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 4775aab0d6..ff6861889b 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -893,7 +893,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) /* MSI(-X) Initialization */ rc = msix_init_exclusive_bar(pci_dev, CXL_T3_MSIX_VECTOR_NR, 4, NULL); if (rc) { - goto err_address_space_free; + goto err_free_special_ops; } for (i = 0; i < CXL_T3_MSIX_VECTOR_NR; i++) { msix_vector_use(pci_dev, i); @@ -907,7 +907,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table; cxl_cstate->cdat.private = ct3d; if (!cxl_doe_cdat_init(cxl_cstate, errp)) { - goto err_free_special_ops; + goto err_msix_uninit; } pcie_cap_deverr_init(pci_dev); @@ -943,10 +943,10 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) err_release_cdat: cxl_doe_cdat_release(cxl_cstate); -err_free_special_ops: +err_msix_uninit: msix_uninit_exclusive_bar(pci_dev); +err_free_special_ops: g_free(regs->special_ops); -err_address_space_free: if (ct3d->dc.host_dc) { cxl_destroy_dc_regions(ct3d); address_space_destroy(&ct3d->dc.host_dc_as); From d3c92cf6dcab028d05f306d4d50511aa805d2385 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Mon, 3 Feb 2025 16:19:07 +0000 Subject: [PATCH 1865/2892] hw/mem/cxl_type3: Ensure errp is set on realization failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simply pass the errp to its callee which will set errp if needed, to enhance error reporting for CXL Type 3 device initialization by setting the errp when realization functions fail. Previously, failing to set `errp` could result in errors being overlooked, causing the system to mistakenly treat failure scenarios as successful and potentially leading to redundant cleanup operations in ct3_exit(). Signed-off-by: Li Zhijian Signed-off-by: Jonathan Cameron Message-Id: <20250203161908.145406-5-Jonathan.Cameron@huawei.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index ff6861889b..d8b45f9bd1 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -891,7 +891,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) &ct3d->cxl_dstate.device_registers); /* MSI(-X) Initialization */ - rc = msix_init_exclusive_bar(pci_dev, CXL_T3_MSIX_VECTOR_NR, 4, NULL); + rc = msix_init_exclusive_bar(pci_dev, CXL_T3_MSIX_VECTOR_NR, 4, errp); if (rc) { goto err_free_special_ops; } @@ -912,7 +912,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) pcie_cap_deverr_init(pci_dev); /* Leave a bit of room for expansion */ - rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, NULL); + rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, errp); if (rc) { goto err_release_cdat; } From 9ac2c42f43a536f53b3d4cad8a601ccb8640cbd8 Mon Sep 17 00:00:00 2001 From: Yao Xingtao Date: Mon, 3 Feb 2025 16:19:08 +0000 Subject: [PATCH 1866/2892] mem/cxl_type3: support 3, 6, 12 and 16 interleave ways Since the kernel does not check the interleave capability, a 3-way, 6-way, 12-way or 16-way region can be create normally. Applications can access the memory of 16-way region normally because qemu can convert hpa to dpa correctly for the power of 2 interleave ways, after kernel implementing the check, this kind of region will not be created any more. For non power of 2 interleave ways, applications could not access the memory normally and may occur some unexpected behaviors, such as segmentation fault. So implements this feature is needed. Link: https://lore.kernel.org/linux-cxl/3e84b919-7631-d1db-3e1d-33000f3f3868@fujitsu.com/ Signed-off-by: Yao Xingtao Signed-off-by: Jonathan Cameron Message-Id: <20250203161908.145406-6-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-component-utils.c | 9 +++++++-- hw/mem/cxl_type3.c | 15 +++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index cd116c0401..473895948b 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -243,8 +243,13 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk, ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, POISON_ON_ERR_CAP, 0); - ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 0); - ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 0); + if (type == CXL2_TYPE3_DEVICE) { + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 1); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 1); + } else { + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 0); + } ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, UIO, 0); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, UIO_DECODER_COUNT, 0); diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index d8b45f9bd1..6fffa21ead 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1100,10 +1100,17 @@ static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa) continue; } - *dpa = dpa_base + - ((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | - ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) - >> iw)); + if (iw < 8) { + *dpa = dpa_base + + ((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | + ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) + >> iw)); + } else { + *dpa = dpa_base + + ((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | + ((((MAKE_64BIT_MASK(ig + iw, 64 - ig - iw) & hpa_offset) + >> (ig + iw)) / 3) << (ig + 8))); + } return true; } From 1456e90653c46aceb3dd83a7b9889a32aad7700d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 4 Feb 2025 09:42:02 +0000 Subject: [PATCH 1867/2892] hw/virtio: reset virtio balloon stats on machine reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a machine is first booted, all virtio balloon stats are initialized to their default value -1 (18446744073709551615 when represented as unsigned). They remain that way while the firmware is loading, and early phase of guest OS boot, until the virtio-balloon driver is activated. Thereafter the reported stats reflect the guest OS activity. When a machine reset is performed, however, the virtio-balloon stats are left unchanged by QEMU, despite the guest OS no longer updating them, nor indeed even still existing. IOW, the mgmt app keeps getting stale stats until the guest OS starts once more and loads the virtio-balloon driver (if ever). At that point the app will see a discontinuity in the reported values as they sudden jump from the stale value to the new value. This jump is indigituishable from a valid data update. While there is an "last-updated" field to report on the freshness of the stats, that does not unambiguously tell the mgmt app whether the stats are still conceptually relevant to the current running workload. It is more conceptually useful to reset the stats to their default values on machine reset, given that the previous guest workload the stats reflect no longer exists. The mgmt app can now clearly identify that there are is no stats information available from the current executing workload. The 'last-updated' time is also reset back to 0. IOW, on every machine reset, the virtio stats are in the same clean state they were when the macine first powered on. A functional test is added to validate this behaviour with a real world guest OS. Signed-off-by: Daniel P. Berrangé Message-Id: <20250204094202.2183262-1-berrange@redhat.com> Acked-by: David Hildenbrand Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + hw/virtio/virtio-balloon.c | 30 ++++- include/hw/virtio/virtio-balloon.h | 4 + tests/functional/meson.build | 2 + tests/functional/test_virtio_balloon.py | 161 ++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 1 deletion(-) create mode 100755 tests/functional/test_virtio_balloon.py diff --git a/MAINTAINERS b/MAINTAINERS index a928ce3e41..013a57d5bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2246,6 +2246,7 @@ F: include/hw/virtio/virtio-balloon.h F: system/balloon.c F: include/system/balloon.h F: tests/qtest/virtio-balloon-test.c +F: tests/functional/test_virtio_balloon.py virtio-9p M: Greg Kurz diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index ad05768ded..2eb5a14fa2 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -31,7 +31,7 @@ #include "trace.h" #include "qemu/error-report.h" #include "migration/misc.h" - +#include "system/reset.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" @@ -910,6 +910,8 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) } reset_stats(s); + s->stats_last_update = 0; + qemu_register_resettable(OBJECT(dev)); } static void virtio_balloon_device_unrealize(DeviceState *dev) @@ -917,6 +919,7 @@ static void virtio_balloon_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBalloon *s = VIRTIO_BALLOON(dev); + qemu_unregister_resettable(OBJECT(dev)); if (s->free_page_bh) { qemu_bh_delete(s->free_page_bh); object_unref(OBJECT(s->iothread)); @@ -987,6 +990,27 @@ static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) } } +static ResettableState *virtio_balloon_get_reset_state(Object *obj) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(obj); + return &s->reset_state; +} + +static void virtio_balloon_reset_enter(Object *obj, ResetType type) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(obj); + + /* + * When waking up from standby/suspend-to-ram, do not reset stats. + */ + if (type == RESET_TYPE_WAKEUP) { + return; + } + + reset_stats(s); + s->stats_last_update = 0; +} + static void virtio_balloon_instance_init(Object *obj) { VirtIOBalloon *s = VIRTIO_BALLOON(obj); @@ -1038,6 +1062,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_balloon_properties); dc->vmsd = &vmstate_virtio_balloon; @@ -1050,6 +1075,9 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) vdc->get_features = virtio_balloon_get_features; vdc->set_status = virtio_balloon_set_status; vdc->vmsd = &vmstate_virtio_balloon_device; + + rc->get_state = virtio_balloon_get_reset_state; + rc->phases.enter = virtio_balloon_reset_enter; } static const TypeInfo virtio_balloon_info = { diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index b12c18a43b..0456c211c6 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -16,6 +16,7 @@ #define QEMU_VIRTIO_BALLOON_H #include "standard-headers/linux/virtio_balloon.h" +#include "hw/resettable.h" #include "hw/virtio/virtio.h" #include "system/iothread.h" #include "qom/object.h" @@ -71,6 +72,9 @@ struct VirtIOBalloon { bool qemu_4_0_config_size; uint32_t poison_val; + + /* State of the resettable container */ + ResettableState reset_state; }; #endif diff --git a/tests/functional/meson.build b/tests/functional/meson.build index cf80924ddc..2d399cc464 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -44,6 +44,7 @@ test_timeouts = { 'riscv64_tuxrun' : 120, 's390x_ccw_virtio' : 420, 'sh4_tuxrun' : 240, + 'virtio_balloon': 120, } tests_generic_system = [ @@ -242,6 +243,7 @@ tests_x86_64_system_thorough = [ 'linux_initrd', 'multiprocess', 'netdev_ethtool', + 'virtio_balloon', 'virtio_gpu', 'x86_64_hotplug_cpu', 'x86_64_tuxrun', diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/test_virtio_balloon.py new file mode 100755 index 0000000000..67b48e1b4e --- /dev/null +++ b/tests/functional/test_virtio_balloon.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# +# virtio-balloon tests +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import time + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +UNSET_STATS_VALUE = 18446744073709551615 + + +class VirtioBalloonx86(QemuSystemTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' + 'rd.rescue') + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern( + self, + success_message, + failure_message="Kernel panic - not syncing", + vm=vm, + ) + + def mount_root(self): + self.wait_for_console_pattern('Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon", + prompt) + + def assert_initial_stats(self): + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + assert when == 0 + stats = ret.get('stats') + for name, val in stats.items(): + assert val == UNSET_STATS_VALUE + + def assert_running_stats(self, then): + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + now = time.time() + + assert when > then and when < now + stats = ret.get('stats') + # Stat we expect this particular Kernel to have set + expectData = [ + "stat-available-memory", + "stat-disk-caches", + "stat-free-memory", + "stat-htlb-pgalloc", + "stat-htlb-pgfail", + "stat-major-faults", + "stat-minor-faults", + "stat-swap-in", + "stat-swap-out", + "stat-total-memory", + ] + for name, val in stats.items(): + if name in expectData: + assert val != UNSET_STATS_VALUE + else: + assert val == UNSET_STATS_VALUE + + def test_virtio_balloon_stats(self): + self.set_machine('q35') + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + diskimage_path = self.ASSET_DISKIMAGE.fetch() + + self.vm.set_console() + self.vm.add_args("-S") + self.vm.add_args("-cpu", "max") + self.vm.add_args("-m", "2G") + # Slow down BIOS phase with boot menu, so that after a system + # reset, we can reliably catch the clean stats again in BIOS + # phase before the guest OS launches + self.vm.add_args("-boot", "menu=on") + self.vm.add_args("-machine", "q35,accel=kvm:tcg") + self.vm.add_args("-device", "virtio-balloon,id=balloon") + self.vm.add_args('-drive', + f'file={diskimage_path},if=none,id=drv0,snapshot=on') + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1') + + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + self.DEFAULT_KERNEL_PARAMS + ) + self.vm.launch() + + # Poll stats at 100ms + self.vm.qmp('qom-set', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats-polling-interval', + 'value': 100 }) + + # We've not run any guest code yet, neither BIOS or guest, + # so stats should be all default values + self.assert_initial_stats() + + self.vm.qmp('cont') + + then = time.time() + self.mount_root() + self.assert_running_stats(then) + + # Race window between these two commands, where we + # rely on '-boot menu=on' to (hopefully) ensure we're + # still executing the BIOS when QEMU processes the + # 'stop', and thus have not loaded the virtio-balloon + # driver in the guest + self.vm.qmp('system_reset') + self.vm.qmp('stop') + + # If the above assumption held, we're in BIOS now and + # stats should be all back at their default values + self.assert_initial_stats() + self.vm.qmp('cont') + + then = time.time() + self.mount_root() + self.assert_running_stats(then) + + +if __name__ == '__main__': + QemuSystemTest.main() From 63dc0b8647391b372f3bb38ff1066f6b4a5e6ea1 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 7 Feb 2025 10:23:53 +0530 Subject: [PATCH 1868/2892] amd_iommu: Use correct DTE field for interrupt passthrough Interrupt passthrough is determine by the bits 191,190,187-184. These bits are part of the 3rd quad word (i.e. index 2) in DTE. Hence replace dte[3] by dte[2]. Fixes: b44159fe0 ("x86_iommu/amd: Add interrupt remap support when VAPIC is not enabled") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250207045354.27329-2-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 6b13ce894b..98f1209a38 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1309,15 +1309,15 @@ static int amdvi_int_remap_msi(AMDVIState *iommu, ret = -AMDVI_IR_ERR; break; case AMDVI_IOAPIC_INT_TYPE_NMI: - pass = dte[3] & AMDVI_DEV_NMI_PASS_MASK; + pass = dte[2] & AMDVI_DEV_NMI_PASS_MASK; trace_amdvi_ir_delivery_mode("nmi"); break; case AMDVI_IOAPIC_INT_TYPE_INIT: - pass = dte[3] & AMDVI_DEV_INT_PASS_MASK; + pass = dte[2] & AMDVI_DEV_INT_PASS_MASK; trace_amdvi_ir_delivery_mode("init"); break; case AMDVI_IOAPIC_INT_TYPE_EINT: - pass = dte[3] & AMDVI_DEV_EINT_PASS_MASK; + pass = dte[2] & AMDVI_DEV_EINT_PASS_MASK; trace_amdvi_ir_delivery_mode("eint"); break; default: From 3684717b7407cc395dc9bf522e193dbc85293dee Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 7 Feb 2025 10:23:54 +0530 Subject: [PATCH 1869/2892] amd_iommu: Use correct bitmask to set capability BAR AMD IOMMU provides the base address of control registers through IVRS table and PCI capability. Since this base address is of 64 bit, use 32 bits mask (instead of 16 bits) to set BAR low and high. Fixes: d29a09ca68 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250207045354.27329-3-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 4 ++-- hw/i386/amd_iommu.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 98f1209a38..044fe43256 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1593,9 +1593,9 @@ static void amdvi_pci_realize(PCIDevice *pdev, Error **errp) /* reset AMDVI specific capabilities, all r/o */ pci_set_long(pdev->config + s->capab_offset, AMDVI_CAPAB_FEATURES); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_LOW, - AMDVI_BASE_ADDR & ~(0xffff0000)); + AMDVI_BASE_ADDR & MAKE_64BIT_MASK(14, 18)); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_HIGH, - (AMDVI_BASE_ADDR & ~(0xffff)) >> 16); + AMDVI_BASE_ADDR >> 32); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_RANGE, 0xff000000); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, 0); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index e0dac4d9a9..28125130c6 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -187,7 +187,7 @@ AMDVI_CAPAB_FLAG_HTTUNNEL | AMDVI_CAPAB_EFR_SUP) /* AMDVI default address */ -#define AMDVI_BASE_ADDR 0xfed80000 +#define AMDVI_BASE_ADDR 0xfed80000ULL /* page management constants */ #define AMDVI_PAGE_SHIFT 12 From 92cf61e70838c20adc82daa3170fdbb9d174b508 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Mon, 17 Feb 2025 09:49:32 -0500 Subject: [PATCH 1870/2892] vhost-iova-tree: Implement an IOVA-only tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates and supports an IOVA-only tree to support a SVQ IOVA->HVA and GPA->IOVA tree for host-only and guest-backed memory, respectively, in the next patch. The IOVA allocator still allocates an IOVA range but now adds this range to the IOVA-only tree as well as to the full IOVA->HVA tree. In the next patch, the full IOVA->HVA tree will be split into a partial SVQ IOVA->HVA tree and a GPA->IOVA tree. The motivation behind having an IOVA-only tree was to have a single tree that would keep track of all allocated IOVA ranges between the partial SVQ IOVA->HVA and GPA->IOVA trees. Signed-off-by: Jonah Palmer Acked-by: Eugenio Pérez Tested-by: Lei Yang Message-Id: <20250217144936.3589907-2-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-iova-tree.c | 26 ++++++++++++++++++++------ hw/virtio/vhost-iova-tree.h | 3 ++- hw/virtio/vhost-vdpa.c | 29 +++++++++++++++++++++-------- net/vhost-vdpa.c | 10 ++++++++-- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c index 3d03395a77..216885aa3c 100644 --- a/hw/virtio/vhost-iova-tree.c +++ b/hw/virtio/vhost-iova-tree.c @@ -28,6 +28,9 @@ struct VhostIOVATree { /* IOVA address to qemu memory maps. */ IOVATree *iova_taddr_map; + + /* Allocated IOVA addresses */ + IOVATree *iova_map; }; /** @@ -44,6 +47,7 @@ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) tree->iova_last = iova_last; tree->iova_taddr_map = iova_tree_new(); + tree->iova_map = iova_tree_new(); return tree; } @@ -53,6 +57,7 @@ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) void vhost_iova_tree_delete(VhostIOVATree *iova_tree) { iova_tree_destroy(iova_tree->iova_taddr_map); + iova_tree_destroy(iova_tree->iova_map); g_free(iova_tree); } @@ -75,6 +80,7 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, * * @tree: The iova tree * @map: The iova map + * @taddr: The translated address (HVA) * * Returns: * - IOVA_OK if the map fits in the container @@ -83,19 +89,26 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, * * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK. */ -int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map) +int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr) { + int ret; + /* Some vhost devices do not like addr 0. Skip first page */ hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size(); - if (map->translated_addr + map->size < map->translated_addr || - map->perm == IOMMU_NONE) { + if (taddr + map->size < taddr || map->perm == IOMMU_NONE) { return IOVA_ERR_INVALID; } - /* Allocate a node in IOVA address */ - return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first, - tree->iova_last); + /* Allocate a node in the IOVA-only tree */ + ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last); + if (unlikely(ret != IOVA_OK)) { + return ret; + } + + /* Insert a node in the IOVA->HVA tree */ + map->translated_addr = taddr; + return iova_tree_insert(tree->iova_taddr_map, map); } /** @@ -107,4 +120,5 @@ int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map) void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map) { iova_tree_remove(iova_tree->iova_taddr_map, map); + iova_tree_remove(iova_tree->iova_map, map); } diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h index 4adfd79ff0..525ce72b1d 100644 --- a/hw/virtio/vhost-iova-tree.h +++ b/hw/virtio/vhost-iova-tree.h @@ -21,7 +21,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete); const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree, const DMAMap *map); -int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map); +int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map, + hwaddr taddr); void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map); #endif diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 3cdaa12ed5..703dcfc929 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -360,14 +360,20 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); if (s->shadow_data) { int r; + hwaddr hw_vaddr = (hwaddr)(uintptr_t)vaddr; - mem_region.translated_addr = (hwaddr)(uintptr_t)vaddr, mem_region.size = int128_get64(llsize) - 1, mem_region.perm = IOMMU_ACCESS_FLAG(true, section->readonly), - r = vhost_iova_tree_map_alloc(s->iova_tree, &mem_region); + r = vhost_iova_tree_map_alloc(s->iova_tree, &mem_region, hw_vaddr); if (unlikely(r != IOVA_OK)) { error_report("Can't allocate a mapping (%d)", r); + + if (mem_region.translated_addr == hw_vaddr) { + error_report("Insertion to IOVA->HVA tree failed"); + /* Remove the mapping from the IOVA-only tree */ + goto fail_map; + } goto fail; } @@ -1142,16 +1148,23 @@ static void vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, * * @v: Vhost-vdpa device * @needle: The area to search iova + * @taddr: The translated address (HVA) * @errorp: Error pointer */ static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle, - Error **errp) + hwaddr taddr, Error **errp) { int r; - r = vhost_iova_tree_map_alloc(v->shared->iova_tree, needle); + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, needle, taddr); if (unlikely(r != IOVA_OK)) { error_setg(errp, "Cannot allocate iova (%d)", r); + + if (needle->translated_addr == taddr) { + error_append_hint(errp, "Insertion to IOVA->HVA tree failed"); + /* Remove the mapping from the IOVA-only tree */ + vhost_iova_tree_remove(v->shared->iova_tree, *needle); + } return false; } @@ -1192,11 +1205,11 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, vhost_svq_get_vring_addr(svq, &svq_addr); driver_region = (DMAMap) { - .translated_addr = svq_addr.desc_user_addr, .size = driver_size - 1, .perm = IOMMU_RO, }; - ok = vhost_vdpa_svq_map_ring(v, &driver_region, errp); + ok = vhost_vdpa_svq_map_ring(v, &driver_region, svq_addr.desc_user_addr, + errp); if (unlikely(!ok)) { error_prepend(errp, "Cannot create vq driver region: "); return false; @@ -1206,11 +1219,11 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, addr->avail_user_addr = driver_region.iova + avail_offset; device_region = (DMAMap) { - .translated_addr = svq_addr.used_user_addr, .size = device_size - 1, .perm = IOMMU_RW, }; - ok = vhost_vdpa_svq_map_ring(v, &device_region, errp); + ok = vhost_vdpa_svq_map_ring(v, &device_region, svq_addr.used_user_addr, + errp); if (unlikely(!ok)) { error_prepend(errp, "Cannot create vq device region: "); vhost_vdpa_svq_unmap_ring(v, driver_region.translated_addr); diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 231b45246c..5a3a57203d 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -510,14 +510,20 @@ static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size, bool write) { DMAMap map = {}; + hwaddr taddr = (hwaddr)(uintptr_t)buf; int r; - map.translated_addr = (hwaddr)(uintptr_t)buf; map.size = size - 1; map.perm = write ? IOMMU_RW : IOMMU_RO, - r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &map); + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &map, taddr); if (unlikely(r != IOVA_OK)) { error_report("Cannot map injected element"); + + if (map.translated_addr == taddr) { + error_report("Insertion to IOVA->HVA tree failed"); + /* Remove the mapping from the IOVA-only tree */ + goto dma_map_err; + } return r; } From 05063f55841babae7216d36105440ed8ba632938 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Mon, 17 Feb 2025 09:49:33 -0500 Subject: [PATCH 1871/2892] vhost-iova-tree, svq: Implement GPA->IOVA & partial IOVA->HVA trees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates and supports a GPA->IOVA tree and a partial IOVA->HVA tree by splitting up guest-backed memory maps and host-only memory maps from the full IOVA->HVA tree. That is, any guest-backed memory maps are now stored in the GPA->IOVA tree and host-only memory maps stay in the IOVA->HVA tree. Also propagates the GPAs (in_addr/out_addr) of a VirtQueueElement to vhost_svq_translate_addr() to translate GPAs to IOVAs via the GPA->IOVA tree (when descriptors are backed by guest memory). For descriptors backed by host-only memory, the existing partial SVQ IOVA->HVA tree is used. GPAs are unique in the guest's address space, ensuring unambiguous IOVA translations. This avoids the issue where different GPAs map to the same HVA, causing the original HVA->IOVA translation to potentially return an IOVA associated with the wrong intended GPA. Signed-off-by: Jonah Palmer Acked-by: Eugenio Pérez Message-Id: <20250217144936.3589907-3-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-iova-tree.c | 67 ++++++++++++++++++++++++++++++ hw/virtio/vhost-iova-tree.h | 5 +++ hw/virtio/vhost-shadow-virtqueue.c | 55 ++++++++++++++++-------- hw/virtio/vhost-shadow-virtqueue.h | 5 ++- hw/virtio/vhost-vdpa.c | 19 ++++----- include/qemu/iova-tree.h | 22 ++++++++++ net/vhost-vdpa.c | 2 +- util/iova-tree.c | 46 ++++++++++++++++++++ 8 files changed, 190 insertions(+), 31 deletions(-) diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c index 216885aa3c..9d2d6a7af2 100644 --- a/hw/virtio/vhost-iova-tree.c +++ b/hw/virtio/vhost-iova-tree.c @@ -31,6 +31,9 @@ struct VhostIOVATree { /* Allocated IOVA addresses */ IOVATree *iova_map; + + /* GPA->IOVA address memory maps */ + IOVATree *gpa_iova_map; }; /** @@ -48,6 +51,7 @@ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) tree->iova_taddr_map = iova_tree_new(); tree->iova_map = iova_tree_new(); + tree->gpa_iova_map = gpa_tree_new(); return tree; } @@ -58,6 +62,7 @@ void vhost_iova_tree_delete(VhostIOVATree *iova_tree) { iova_tree_destroy(iova_tree->iova_taddr_map); iova_tree_destroy(iova_tree->iova_map); + iova_tree_destroy(iova_tree->gpa_iova_map); g_free(iova_tree); } @@ -122,3 +127,65 @@ void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map) iova_tree_remove(iova_tree->iova_taddr_map, map); iova_tree_remove(iova_tree->iova_map, map); } + +/** + * Find the IOVA address stored from a guest memory address (GPA) + * + * @tree: The VhostIOVATree + * @map: The map with the guest memory address + * + * Returns the stored GPA->IOVA mapping, or NULL if not found. + */ +const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *tree, + const DMAMap *map) +{ + return iova_tree_find_iova(tree->gpa_iova_map, map); +} + +/** + * Allocate a new IOVA range and add the mapping to the GPA->IOVA tree + * + * @tree: The VhostIOVATree + * @map: The IOVA mapping + * @taddr: The translated address (GPA) + * + * Returns: + * - IOVA_OK if the map fits both containers + * - IOVA_ERR_INVALID if the map does not make sense (like size overflow) + * - IOVA_ERR_NOMEM if the IOVA-only tree cannot allocate more space + * + * It returns an assigned IOVA in map->iova if the return value is IOVA_OK. + */ +int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *tree, DMAMap *map, hwaddr taddr) +{ + int ret; + + /* Some vhost devices don't like addr 0. Skip first page */ + hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size(); + + if (taddr + map->size < taddr || map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* Allocate a node in the IOVA-only tree */ + ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last); + if (unlikely(ret != IOVA_OK)) { + return ret; + } + + /* Insert a node in the GPA->IOVA tree */ + map->translated_addr = taddr; + return gpa_tree_insert(tree->gpa_iova_map, map); +} + +/** + * Remove existing mappings from the IOVA-only and GPA->IOVA trees + * + * @tree: The VhostIOVATree + * @map: The map to remove + */ +void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map) +{ + iova_tree_remove(iova_tree->gpa_iova_map, map); + iova_tree_remove(iova_tree->iova_map, map); +} diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h index 525ce72b1d..0c4ba5abd5 100644 --- a/hw/virtio/vhost-iova-tree.h +++ b/hw/virtio/vhost-iova-tree.h @@ -24,5 +24,10 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree, int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map, hwaddr taddr); void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map); +const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *iova_tree, + const DMAMap *map); +int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *iova_tree, DMAMap *map, + hwaddr taddr); +void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map); #endif diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index 37aca8b431..30ba565f03 100644 --- a/hw/virtio/vhost-shadow-virtqueue.c +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -78,24 +78,39 @@ uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq) * @vaddr: Translated IOVA addresses * @iovec: Source qemu's VA addresses * @num: Length of iovec and minimum length of vaddr + * @gpas: Descriptors' GPAs, if backed by guest memory */ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, hwaddr *addrs, const struct iovec *iovec, - size_t num) + size_t num, const hwaddr *gpas) { if (num == 0) { return true; } for (size_t i = 0; i < num; ++i) { - DMAMap needle = { - .translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base, - .size = iovec[i].iov_len, - }; Int128 needle_last, map_last; size_t off; + const DMAMap *map; + DMAMap needle; + + /* Check if the descriptor is backed by guest memory */ + if (gpas) { + /* Search the GPA->IOVA tree */ + needle = (DMAMap) { + .translated_addr = gpas[i], + .size = iovec[i].iov_len, + }; + map = vhost_iova_tree_find_gpa(svq->iova_tree, &needle); + } else { + /* Search the IOVA->HVA tree */ + needle = (DMAMap) { + .translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base, + .size = iovec[i].iov_len, + }; + map = vhost_iova_tree_find_iova(svq->iova_tree, &needle); + } - const DMAMap *map = vhost_iova_tree_find_iova(svq->iova_tree, &needle); /* * Map cannot be NULL since iova map contains all guest space and * qemu already has a physical address mapped @@ -130,6 +145,7 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, * @sg: Cache for hwaddr * @iovec: The iovec from the guest * @num: iovec length + * @addr: Descriptors' GPAs, if backed by guest memory * @more_descs: True if more descriptors come in the chain * @write: True if they are writeable descriptors * @@ -137,7 +153,8 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, */ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, const struct iovec *iovec, size_t num, - bool more_descs, bool write) + const hwaddr *addr, bool more_descs, + bool write) { uint16_t i = svq->free_head, last = svq->free_head; unsigned n; @@ -149,7 +166,7 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, return true; } - ok = vhost_svq_translate_addr(svq, sg, iovec, num); + ok = vhost_svq_translate_addr(svq, sg, iovec, num, addr); if (unlikely(!ok)) { return false; } @@ -174,8 +191,9 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, const struct iovec *out_sg, size_t out_num, + const hwaddr *out_addr, const struct iovec *in_sg, size_t in_num, - unsigned *head) + const hwaddr *in_addr, unsigned *head) { unsigned avail_idx; vring_avail_t *avail = svq->vring.avail; @@ -191,13 +209,14 @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, return false; } - ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, in_num > 0, - false); + ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, out_addr, + in_num > 0, false); if (unlikely(!ok)) { return false; } - ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, false, true); + ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, in_addr, false, + true); if (unlikely(!ok)) { return false; } @@ -247,8 +266,9 @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq) * Return -EINVAL if element is invalid, -ENOSPC if dev queue is full */ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, - size_t out_num, const struct iovec *in_sg, size_t in_num, - VirtQueueElement *elem) + size_t out_num, const hwaddr *out_addr, + const struct iovec *in_sg, size_t in_num, + const hwaddr *in_addr, VirtQueueElement *elem) { unsigned qemu_head; unsigned ndescs = in_num + out_num; @@ -258,7 +278,8 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, return -ENOSPC; } - ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num, &qemu_head); + ok = vhost_svq_add_split(svq, out_sg, out_num, out_addr, in_sg, in_num, + in_addr, &qemu_head); if (unlikely(!ok)) { return -EINVAL; } @@ -274,8 +295,8 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, static int vhost_svq_add_element(VhostShadowVirtqueue *svq, VirtQueueElement *elem) { - return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->in_sg, - elem->in_num, elem); + return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->out_addr, + elem->in_sg, elem->in_num, elem->in_addr, elem); } /** diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h index 19c842a15b..9c273739d6 100644 --- a/hw/virtio/vhost-shadow-virtqueue.h +++ b/hw/virtio/vhost-shadow-virtqueue.h @@ -118,8 +118,9 @@ uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq); void vhost_svq_push_elem(VhostShadowVirtqueue *svq, const VirtQueueElement *elem, uint32_t len); int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, - size_t out_num, const struct iovec *in_sg, size_t in_num, - VirtQueueElement *elem); + size_t out_num, const hwaddr *out_addr, + const struct iovec *in_sg, size_t in_num, + const hwaddr *in_addr, VirtQueueElement *elem); size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num); void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd); diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 703dcfc929..7efbde3d4c 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -360,17 +360,17 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); if (s->shadow_data) { int r; - hwaddr hw_vaddr = (hwaddr)(uintptr_t)vaddr; + hwaddr gpa = section->offset_within_address_space; mem_region.size = int128_get64(llsize) - 1, mem_region.perm = IOMMU_ACCESS_FLAG(true, section->readonly), - r = vhost_iova_tree_map_alloc(s->iova_tree, &mem_region, hw_vaddr); + r = vhost_iova_tree_map_alloc_gpa(s->iova_tree, &mem_region, gpa); if (unlikely(r != IOVA_OK)) { error_report("Can't allocate a mapping (%d)", r); - if (mem_region.translated_addr == hw_vaddr) { - error_report("Insertion to IOVA->HVA tree failed"); + if (mem_region.translated_addr == gpa) { + error_report("Insertion to GPA->IOVA tree failed"); /* Remove the mapping from the IOVA-only tree */ goto fail_map; } @@ -392,7 +392,7 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, fail_map: if (s->shadow_data) { - vhost_iova_tree_remove(s->iova_tree, mem_region); + vhost_iova_tree_remove_gpa(s->iova_tree, mem_region); } fail: @@ -446,21 +446,18 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, if (s->shadow_data) { const DMAMap *result; - const void *vaddr = memory_region_get_ram_ptr(section->mr) + - section->offset_within_region + - (iova - section->offset_within_address_space); DMAMap mem_region = { - .translated_addr = (hwaddr)(uintptr_t)vaddr, + .translated_addr = section->offset_within_address_space, .size = int128_get64(llsize) - 1, }; - result = vhost_iova_tree_find_iova(s->iova_tree, &mem_region); + result = vhost_iova_tree_find_gpa(s->iova_tree, &mem_region); if (!result) { /* The memory listener map wasn't mapped */ return; } iova = result->iova; - vhost_iova_tree_remove(s->iova_tree, *result); + vhost_iova_tree_remove_gpa(s->iova_tree, *result); } vhost_vdpa_iotlb_batch_begin_once(s); /* diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index 44a45931d5..16d354a814 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -40,6 +40,28 @@ typedef struct DMAMap { } QEMU_PACKED DMAMap; typedef gboolean (*iova_tree_iterator)(DMAMap *map); +/** + * gpa_tree_new: + * + * Create a new GPA->IOVA tree. + * + * Returns: the tree point on success, or NULL otherwise. + */ +IOVATree *gpa_tree_new(void); + +/** + * gpa_tree_insert: + * + * @tree: The GPA->IOVA tree we're inserting the mapping to + * @map: The GPA->IOVA mapping to insert + * + * Inserts a GPA range to the GPA->IOVA tree. If there are overlapped + * ranges, IOVA_ERR_OVERLAP will be returned. + * + * Return: 0 if successful, < 0 otherwise. + */ +int gpa_tree_insert(IOVATree *tree, const DMAMap *map); + /** * iova_tree_new: * diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 5a3a57203d..bd01866878 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -649,7 +649,7 @@ static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0); int r; - r = vhost_svq_add(svq, out_sg, out_num, in_sg, in_num, NULL); + r = vhost_svq_add(svq, out_sg, out_num, NULL, in_sg, in_num, NULL, NULL); if (unlikely(r != 0)) { if (unlikely(r == -ENOSPC)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n", diff --git a/util/iova-tree.c b/util/iova-tree.c index 06295e2755..5b0c95ff15 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -257,3 +257,49 @@ void iova_tree_destroy(IOVATree *tree) g_tree_destroy(tree->tree); g_free(tree); } + +static int gpa_tree_compare(gconstpointer a, gconstpointer b, gpointer data) +{ + const DMAMap *m1 = a, *m2 = b; + + if (m1->translated_addr > m2->translated_addr + m2->size) { + return 1; + } + + if (m1->translated_addr + m1->size < m2->translated_addr) { + return -1; + } + + /* Overlapped */ + return 0; +} + +IOVATree *gpa_tree_new(void) +{ + IOVATree *gpa_tree = g_new0(IOVATree, 1); + + gpa_tree->tree = g_tree_new_full(gpa_tree_compare, NULL, g_free, NULL); + + return gpa_tree; +} + +int gpa_tree_insert(IOVATree *tree, const DMAMap *map) +{ + DMAMap *new; + + if (map->translated_addr + map->size < map->translated_addr || + map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* We don't allow inserting ranges that overlap with existing ones */ + if (iova_tree_find(tree, map)) { + return IOVA_ERR_OVERLAP; + } + + new = g_new0(DMAMap, 1); + memcpy(new, map, sizeof(*new)); + iova_tree_insert_internal(tree->tree, new); + + return IOVA_OK; +} From 332859dd597b78f7d1ebfaefd6195e3a2b3e5906 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Mon, 17 Feb 2025 09:49:34 -0500 Subject: [PATCH 1872/2892] vhost-iova-tree: Update documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor update to some of the documentation / comments in hw/virtio/vhost-iova-tree.c. Signed-off-by: Jonah Palmer Reviewed-by: Eugenio Pérez Tested-by: Lei Yang Message-Id: <20250217144936.3589907-4-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-iova-tree.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c index 9d2d6a7af2..fa4147b773 100644 --- a/hw/virtio/vhost-iova-tree.c +++ b/hw/virtio/vhost-iova-tree.c @@ -37,9 +37,9 @@ struct VhostIOVATree { }; /** - * Create a new IOVA tree + * Create a new VhostIOVATree * - * Returns the new IOVA tree + * Returns the new VhostIOVATree. */ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) { @@ -56,7 +56,7 @@ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) } /** - * Delete an iova tree + * Delete a VhostIOVATree */ void vhost_iova_tree_delete(VhostIOVATree *iova_tree) { @@ -69,10 +69,10 @@ void vhost_iova_tree_delete(VhostIOVATree *iova_tree) /** * Find the IOVA address stored from a memory address * - * @tree: The iova tree + * @tree: The VhostIOVATree * @map: The map with the memory address * - * Return the stored mapping, or NULL if not found. + * Returns the stored IOVA->HVA mapping, or NULL if not found. */ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, const DMAMap *map) @@ -81,10 +81,10 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, } /** - * Allocate a new mapping + * Allocate a new IOVA range and add the mapping to the IOVA->HVA tree * - * @tree: The iova tree - * @map: The iova map + * @tree: The VhostIOVATree + * @map: The IOVA mapping * @taddr: The translated address (HVA) * * Returns: @@ -92,7 +92,7 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, * - IOVA_ERR_INVALID if the map does not make sense (like size overflow) * - IOVA_ERR_NOMEM if tree cannot allocate more space. * - * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK. + * It returns an assigned IOVA in map->iova if the return value is IOVA_OK. */ int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr) { @@ -117,9 +117,9 @@ int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr) } /** - * Remove existing mappings from iova tree + * Remove existing mappings from the IOVA-only and IOVA->HVA trees * - * @iova_tree: The vhost iova tree + * @iova_tree: The VhostIOVATree * @map: The map to remove */ void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map) From 83cb18ac4500f3a14067b19408705068647cb0c5 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 11 Feb 2025 14:55:23 +0100 Subject: [PATCH 1873/2892] cryptodev/vhost: allocate CryptoDevBackendVhost using g_mem0() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function `vhost_dev_init()` expects the `struct vhost_dev` (passed as a parameter) to be fully initialized. This is important because some parts of the code check whether `vhost_dev->config_ops` is NULL to determine if it has been set (e.g. later via `vhost_dev_set_config_notifier`). To ensure this initialization, it’s better to allocate the entire `CryptoDevBackendVhost` structure (which includes `vhost_dev`) using `g_mem0()`, following the same approach used for other vhost devices, such as in `vhost_net_init()`. Fixes: 042cea274c ("cryptodev: add vhost-user as a new cryptodev backend") Cc: qemu-stable@nongnu.org Reported-by: myluo24@m.fudan.edu.cn Signed-off-by: Stefano Garzarella Message-Id: <20250211135523.101203-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- backends/cryptodev-vhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index 8718c97326..943680a23a 100644 --- a/backends/cryptodev-vhost.c +++ b/backends/cryptodev-vhost.c @@ -53,7 +53,7 @@ cryptodev_vhost_init( CryptoDevBackendVhost *crypto; Error *local_err = NULL; - crypto = g_new(CryptoDevBackendVhost, 1); + crypto = g_new0(CryptoDevBackendVhost, 1); crypto->dev.max_queues = 1; crypto->dev.nvqs = 1; crypto->dev.vqs = crypto->vqs; From 590de17b829d2ccd8067f19c71a53e31bec7bf00 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 11 Feb 2025 15:42:57 +0100 Subject: [PATCH 1874/2892] MAINTAINERS: add more files to `vhost` While sending a patch for backends/cryptodev-vhost.c I noticed that Michael wasn`t in CC so I took a look at the files listed under `vhost` and tried to fix it increasing the coverage by adding new files. Signed-off-by: Stefano Garzarella Message-Id: <20250211144259.117910-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 013a57d5bf..2d07d72933 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2212,12 +2212,16 @@ M: Michael S. Tsirkin R: Stefano Garzarella S: Supported F: hw/*/*vhost* -F: docs/interop/vhost-user.json -F: docs/interop/vhost-user.rst +F: docs/interop/vhost-user* +F: docs/system/devices/vhost-user* F: contrib/vhost-user-*/ -F: backends/vhost-user.c +F: backends/*vhost* F: include/system/vhost-user-backend.h +F: include/hw/virtio/vhost* +F: include/*/vhost* F: subprojects/libvhost-user/ +F: block/export/vhost-user* +F: util/vhost-user-server.c vhost-shadow-virtqueue R: Eugenio Pérez From 50e9754149066dc91f58405d3378b589098cb408 Mon Sep 17 00:00:00 2001 From: Konstantin Shkolnyy Date: Wed, 12 Feb 2025 10:49:23 -0600 Subject: [PATCH 1875/2892] vdpa: Fix endian bugs in shadow virtqueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VDPA didn't work on a big-endian machine due to missing/incorrect CPU<->LE data format conversions. Signed-off-by: Konstantin Shkolnyy Message-Id: <20250212164923.1971538-1-kshk@linux.ibm.com> Fixes: 10857ec0ad ("vhost: Add VhostShadowVirtqueue") Acked-by: Eugenio Pérez Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-shadow-virtqueue.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index 30ba565f03..2481d49345 100644 --- a/hw/virtio/vhost-shadow-virtqueue.c +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -182,10 +182,10 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, descs[i].len = cpu_to_le32(iovec[n].iov_len); last = i; - i = cpu_to_le16(svq->desc_next[i]); + i = svq->desc_next[i]; } - svq->free_head = le16_to_cpu(svq->desc_next[last]); + svq->free_head = svq->desc_next[last]; return true; } @@ -247,10 +247,12 @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq) smp_mb(); if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { - uint16_t avail_event = *(uint16_t *)(&svq->vring.used->ring[svq->vring.num]); + uint16_t avail_event = le16_to_cpu( + *(uint16_t *)(&svq->vring.used->ring[svq->vring.num])); needs_kick = vring_need_event(avail_event, svq->shadow_avail_idx, svq->shadow_avail_idx - 1); } else { - needs_kick = !(svq->vring.used->flags & VRING_USED_F_NO_NOTIFY); + needs_kick = + !(svq->vring.used->flags & cpu_to_le16(VRING_USED_F_NO_NOTIFY)); } if (!needs_kick) { @@ -386,7 +388,7 @@ static bool vhost_svq_more_used(VhostShadowVirtqueue *svq) return true; } - svq->shadow_used_idx = cpu_to_le16(*(volatile uint16_t *)used_idx); + svq->shadow_used_idx = le16_to_cpu(*(volatile uint16_t *)used_idx); return svq->last_used_idx != svq->shadow_used_idx; } @@ -404,7 +406,7 @@ static bool vhost_svq_enable_notification(VhostShadowVirtqueue *svq) { if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { uint16_t *used_event = (uint16_t *)&svq->vring.avail->ring[svq->vring.num]; - *used_event = svq->shadow_used_idx; + *used_event = cpu_to_le16(svq->shadow_used_idx); } else { svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); } @@ -429,7 +431,7 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq, uint16_t num, uint16_t i) { for (uint16_t j = 0; j < (num - 1); ++j) { - i = le16_to_cpu(svq->desc_next[i]); + i = svq->desc_next[i]; } return i; @@ -704,7 +706,7 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, svq->desc_state = g_new0(SVQDescState, svq->vring.num); svq->desc_next = g_new0(uint16_t, svq->vring.num); for (unsigned i = 0; i < svq->vring.num - 1; i++) { - svq->desc_next[i] = cpu_to_le16(i + 1); + svq->desc_next[i] = i + 1; } } From 131fe64e63c88ec52c45a5946a478c0edeb31b78 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 13 Feb 2025 11:45:41 +0000 Subject: [PATCH 1876/2892] hw/virtio/virtio-nsm: Respond with correct length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we return a response packet from NSM, we need to indicate its length according to the content of the response. Prior to this patch, we returned the length of the source buffer, which may confuse guest code that relies on the response size. Fix it by returning the response payload size instead. Fixes: bb154e3e0cc715 ("device/virtio-nsm: Support for Nitro Secure Module device") Reported-by: Vikrant Garg Signed-off-by: Alexander Graf Message-Id: <20250213114541.67515-1-graf@amazon.com> Reviewed-by: Dorjoy Chowdhury Fixes: bb154e3e0cc715 ("device/virtio-nsm: Support for Nitro Secure Module device")
Reported-by: Vikrant Garg Signed-off-by: Alexander Graf Reviewed-by: Philippe Mathieu-Daudé Tested-by: Vikrant Garg Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-nsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c index 098e1aeac6..b22aa74e34 100644 --- a/hw/virtio/virtio-nsm.c +++ b/hw/virtio/virtio-nsm.c @@ -1596,7 +1596,7 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) g_free(req.iov_base); g_free(res.iov_base); virtqueue_push(vq, out_elem, 0); - virtqueue_push(vq, in_elem, in_elem->in_sg->iov_len); + virtqueue_push(vq, in_elem, sz); virtio_notify(vdev, vq); return; From 02fd9f8aeeb184276b283ae2f404bc3acf1e7b7a Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 17 Feb 2025 10:25:50 +0100 Subject: [PATCH 1877/2892] net: vhost-user: add QAPI events to report connection state The netdev reports NETDEV_VHOST_USER_CONNECTED event when the chardev is connected, and NETDEV_VHOST_USER_DISCONNECTED when it is disconnected. The NETDEV_VHOST_USER_CONNECTED event includes the chardev id. This allows a system manager like libvirt to detect when the server fails. For instance with passt: { 'execute': 'qmp_capabilities' } { "return": { } } [killing passt here] { "timestamp": { "seconds": 1739538634, "microseconds": 920450 }, "event": "NETDEV_VHOST_USER_DISCONNECTED", "data": { "netdev-id": "netdev0" } } [automatic reconnection with reconnect-ms] { "timestamp": { "seconds": 1739538638, "microseconds": 354181 }, "event": "NETDEV_VHOST_USER_CONNECTED", "data": { "netdev-id": "netdev0", "chardev-id": "chr0" } } Tested-by: Stefano Brivio Signed-off-by: Laurent Vivier Message-Id: <20250217092550.1172055-1-lvivier@redhat.com> Acked-by: Markus Armbruster Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-user.c | 3 +++ qapi/net.json | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/net/vhost-user.c b/net/vhost-user.c index 12555518e8..0b235e50c6 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -16,6 +16,7 @@ #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qapi-commands-net.h" +#include "qapi/qapi-events-net.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -271,6 +272,7 @@ static void chr_closed_bh(void *opaque) if (err) { error_report_err(err); } + qapi_event_send_netdev_vhost_user_disconnected(name); } static void net_vhost_user_event(void *opaque, QEMUChrEvent event) @@ -300,6 +302,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) net_vhost_user_watch, s); qmp_set_link(name, true, &err); s->started = true; + qapi_event_send_netdev_vhost_user_connected(name, chr->label); break; case CHR_EVENT_CLOSED: /* a close event may happen during a read/write, but vhost diff --git a/qapi/net.json b/qapi/net.json index 2739a2f423..310cc4fd19 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -1031,3 +1031,43 @@ ## { 'event': 'NETDEV_STREAM_DISCONNECTED', 'data': { 'netdev-id': 'str' } } + +## +# @NETDEV_VHOST_USER_CONNECTED: +# +# Emitted when the vhost-user chardev is connected +# +# @netdev-id: QEMU netdev id that is connected +# +# @chardev-id: The character device id used by the QEMU netdev +# +# Since: 10.0 +# +# .. qmp-example:: +# +# <- { "timestamp": {"seconds": 1739538638, "microseconds": 354181 }, +# "event": "NETDEV_VHOST_USER_CONNECTED", +# "data": { "netdev-id": "netdev0", "chardev-id": "chr0" } } +# +## +{ 'event': 'NETDEV_VHOST_USER_CONNECTED', + 'data': { 'netdev-id': 'str', 'chardev-id': 'str' } } + +## +# @NETDEV_VHOST_USER_DISCONNECTED: +# +# Emitted when the vhost-user chardev is disconnected +# +# @netdev-id: QEMU netdev id that is disconnected +# +# Since: 10.0 +# +# .. qmp-example:: +# +# <- { "timestamp": { "seconds": 1739538634, "microseconds": 920450 }, +# "event": "NETDEV_VHOST_USER_DISCONNECTED", +# "data": { "netdev-id": "netdev0" } } +# +## +{ 'event': 'NETDEV_VHOST_USER_DISCONNECTED', + 'data': { 'netdev-id': 'str' } } From e87b6efb11be9f5ff213461f5ecdbae47d9402b9 Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Mon, 17 Feb 2025 14:12:55 +0100 Subject: [PATCH 1878/2892] vhost-user-snd: correct the calculation of config_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use virtio_get_config_size() rather than sizeof(struct virtio_snd_config) for the config_size in the vhost-user-snd frontend. The frontend shall rely on device features for the size of the device configuration space. The presence of `controls` in the config space depends on VIRTIO_SND_F_CTLS according to the specification (v1.3): ` 5.14.4 Device Configuration Layout ... controls (driver-read-only) indicates a total number of all available control elements if VIRTIO_SND_F_CTLS has been negotiated. ` This fixes an issue introduced by commit ab0c7fb2 ("linux-headers: update to current kvm/next") in which the optional field `controls` is added to the virtio_snd_config structure. This breaks vhost-user-device backends that do not implement the `controls` field. Fixes: ab0c7fb22b ("linux-headers: update to current kvm/next") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2805 Reviewed-by: Philippe Mathieu-Daudé Suggested-by: Stefano Garzarella Signed-off-by: Matias Ezequiel Vara Larsen Message-Id: <20250217131255.829892-1-mvaralar@redhat.com> Cc: qemu-stable@nongnu.org Reviewed-by: Stefano Garzarella Reviewed-by: Dorinda Bassey Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user-snd.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-user-snd.c b/hw/virtio/vhost-user-snd.c index 8610370af8..b414c75c06 100644 --- a/hw/virtio/vhost-user-snd.c +++ b/hw/virtio/vhost-user-snd.c @@ -16,6 +16,18 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_snd.h" +static const VirtIOFeature feature_sizes[] = { + {.flags = 1ULL << VIRTIO_SND_F_CTLS, + .end = endof(struct virtio_snd_config, controls)}, + {} +}; + +static const VirtIOConfigSizeParams cfg_size_params = { + .min_size = endof(struct virtio_snd_config, chmaps), + .max_size = sizeof(struct virtio_snd_config), + .feature_sizes = feature_sizes +}; + static const VMStateDescription vu_snd_vmstate = { .name = "vhost-user-snd", .unmigratable = 1, @@ -23,16 +35,20 @@ static const VMStateDescription vu_snd_vmstate = { static const Property vsnd_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_BIT64("controls", VHostUserBase, + parent_obj.host_features, VIRTIO_SND_F_CTLS, false), }; static void vu_snd_base_realize(DeviceState *dev, Error **errp) { VHostUserBase *vub = VHOST_USER_BASE(dev); VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev); + VirtIODevice *vdev = &vub->parent_obj; vub->virtio_id = VIRTIO_ID_SOUND; vub->num_vqs = 4; - vub->config_size = sizeof(struct virtio_snd_config); + vub->config_size = virtio_get_config_size(&cfg_size_params, + vdev->host_features); vub->vq_size = 64; vubs->parent_realize(dev, errp); From d261b84d354a41a38336af813f92f636d3fb3f78 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 18 Feb 2025 19:25:31 +0100 Subject: [PATCH 1879/2892] hw/virtio/virtio-iommu: Migrate to 3-phase reset Currently the iommu may be reset before the devices it protects. For example this happens with virtio-net. Let's use 3-phase reset mechanism and reset the IOMMU on exit phase after all DMA capable devices have been reset during the 'enter' or 'hold' phase. Signed-off-by: Eric Auger Acked-by: Michael S. Tsirkin Reviewed-by: Zhenzhong Duan Acked-by: Jason Wang Message-Id: <20250218182737.76722-2-eric.auger@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/trace-events | 2 +- hw/virtio/virtio-iommu.c | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 04e36ae047..76f0d458b2 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -108,7 +108,7 @@ virtio_pci_notify_write(uint64_t addr, uint64_t val, unsigned int size) "0x%" PR virtio_pci_notify_write_pio(uint64_t addr, uint64_t val, unsigned int size) "0x%" PRIx64" = 0x%" PRIx64 " (%d)" # hw/virtio/virtio-iommu.c -virtio_iommu_device_reset(void) "reset!" +virtio_iommu_device_reset_exit(void) "reset!" virtio_iommu_system_reset(void) "system reset!" virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64 virtio_iommu_device_status(uint8_t status) "driver status = %d" diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index f41104a952..b6e7e01ef7 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1504,11 +1504,11 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) virtio_cleanup(vdev); } -static void virtio_iommu_device_reset(VirtIODevice *vdev) +static void virtio_iommu_device_reset_exit(Object *obj, ResetType type) { - VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); + VirtIOIOMMU *s = VIRTIO_IOMMU(obj); - trace_virtio_iommu_device_reset(); + trace_virtio_iommu_device_reset_exit(); if (s->domains) { g_tree_destroy(s->domains); @@ -1668,6 +1668,7 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_iommu_properties); dc->vmsd = &vmstate_virtio_iommu; @@ -1675,7 +1676,12 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_iommu_device_realize; vdc->unrealize = virtio_iommu_device_unrealize; - vdc->reset = virtio_iommu_device_reset; + + /* + * Use 'exit' reset phase to make sure all DMA requests + * have been quiesced during 'enter' or 'hold' phase + */ + rc->phases.exit = virtio_iommu_device_reset_exit; vdc->get_config = virtio_iommu_get_config; vdc->set_config = virtio_iommu_set_config; vdc->get_features = virtio_iommu_get_features; From 2aaf48bcf27d8b3da5b30af6c1ced464d3df30f7 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 18 Feb 2025 19:25:32 +0100 Subject: [PATCH 1880/2892] hw/i386/intel-iommu: Migrate to 3-phase reset Currently the IOMMU may be reset before the devices it protects. For example this happens with virtio devices but also with VFIO devices. In this latter case this produces spurious translation faults on host. Let's use 3-phase reset mechanism and reset the IOMMU on exit phase after all DMA capable devices have been reset on 'enter' or 'hold' phase. Signed-off-by: Eric Auger Acked-by: Michael S. Tsirkin Acked-by: Jason Wang Zhenzhong Duan Message-Id: <20250218182737.76722-3-eric.auger@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 12 +++++++++--- hw/i386/trace-events | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index f366c223d0..a5cf2d0e81 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4697,10 +4697,11 @@ static void vtd_init(IntelIOMMUState *s) /* Should not reset address_spaces when reset because devices will still use * the address space they got at first (won't ask the bus again). */ -static void vtd_reset(DeviceState *dev) +static void vtd_reset_exit(Object *obj, ResetType type) { - IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); + IntelIOMMUState *s = INTEL_IOMMU_DEVICE(obj); + trace_vtd_reset_exit(); vtd_init(s); vtd_address_space_refresh_all(s); } @@ -4864,8 +4865,13 @@ static void vtd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - device_class_set_legacy_reset(dc, vtd_reset); + /* + * Use 'exit' reset phase to make sure all DMA requests + * have been quiesced during 'enter' or 'hold' phase + */ + rc->phases.exit = vtd_reset_exit; dc->vmsd = &vtd_vmstate; device_class_set_props(dc, vtd_properties); dc->hotpluggable = false; diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 53c02d7ac8..ac9e1a10aa 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -68,6 +68,7 @@ vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16 vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)" vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)" +vtd_reset_exit(void) "" # amd_iommu.c amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32 From e39e3f8b8dea856f141e9945167d2b18021ef445 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 18 Feb 2025 19:25:33 +0100 Subject: [PATCH 1881/2892] hw/arm/smmuv3: Move reset to exit phase Currently the iommu may be reset before the devices it protects. For example this happens with virtio-scsi-pci. when system_reset is issued from qmp monitor: spurious "virtio: zero sized buffers are not allowed" warnings can be observed. This happens because outstanding DMA requests are still happening while the SMMU gets reset. This can also happen with VFIO devices. In that case spurious DMA translation faults can be observed on host. Make sure the SMMU is reset in the 'exit' phase after all DMA capable devices have been reset during the 'enter' or 'hold' phase. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Message-Id: <20250218182737.76722-4-eric.auger@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/smmu-common.c | 9 +++++++-- hw/arm/smmuv3.c | 14 ++++++++++---- hw/arm/trace-events | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index dd74c2e558..8c1b407b82 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -924,7 +924,12 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) } } -static void smmu_base_reset_hold(Object *obj, ResetType type) +/* + * Make sure the IOMMU is reset in 'exit' phase after + * all outstanding DMA requests have been quiesced during + * the 'enter' or 'hold' reset phases + */ +static void smmu_base_reset_exit(Object *obj, ResetType type) { SMMUState *s = ARM_SMMU(obj); @@ -949,7 +954,7 @@ static void smmu_base_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, smmu_dev_properties); device_class_set_parent_realize(dc, smmu_base_realize, &sbc->parent_realize); - rc->phases.hold = smmu_base_reset_hold; + rc->phases.exit = smmu_base_reset_exit; } static const TypeInfo smmu_base_info = { diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index c0cf5df0f6..b49a59b64c 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1870,13 +1870,19 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) } } -static void smmu_reset_hold(Object *obj, ResetType type) +/* + * Make sure the IOMMU is reset in 'exit' phase after + * all outstanding DMA requests have been quiesced during + * the 'enter' or 'hold' reset phases + */ +static void smmu_reset_exit(Object *obj, ResetType type) { SMMUv3State *s = ARM_SMMUV3(obj); SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); - if (c->parent_phases.hold) { - c->parent_phases.hold(obj, type); + trace_smmu_reset_exit(); + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); } smmuv3_init_regs(s); @@ -1999,7 +2005,7 @@ static void smmuv3_class_init(ObjectClass *klass, void *data) SMMUv3Class *c = ARM_SMMUV3_CLASS(klass); dc->vmsd = &vmstate_smmuv3; - resettable_class_set_parent_phases(rc, NULL, smmu_reset_hold, NULL, + resettable_class_set_parent_phases(rc, NULL, NULL, smmu_reset_exit, &c->parent_phases); device_class_set_parent_realize(dc, smmu_realize, &c->parent_realize); diff --git a/hw/arm/trace-events b/hw/arm/trace-events index c64ad344bd..7790db780e 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -56,6 +56,7 @@ smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d" +smmu_reset_exit(void) "" # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" From d410e709526d1cd4aa9085c6e254a622594a02a5 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 18 Feb 2025 19:25:34 +0100 Subject: [PATCH 1882/2892] hw/vfio/common: Add a trace point in vfio_reset_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To ease the debug of reset sequence, let's add a trace point in vfio_reset_handler() Signed-off-by: Eric Auger Reviewed-by: Cédric Le Goater Acked-by: Michael S. Tsirkin Reviewed-by: Zhenzhong Duan Message-Id: <20250218182737.76722-5-eric.auger@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/vfio/common.c | 1 + hw/vfio/trace-events | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f7499a9b74..173fb3a997 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1386,6 +1386,7 @@ void vfio_reset_handler(void *opaque) { VFIODevice *vbasedev; + trace_vfio_reset_handler(); QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { if (vbasedev->dev->realized) { vbasedev->ops->vfio_compute_needs_reset(vbasedev); diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index cab1cf1de0..c5385e1a4f 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -120,6 +120,7 @@ vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype vfio_legacy_dma_unmap_overflow_workaround(void) "" vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 +vfio_reset_handler(void) "" # platform.c vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" From dd6d545e8f2d9a0e8a8c287ec16469f03ef5c198 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 18 Feb 2025 19:25:35 +0100 Subject: [PATCH 1883/2892] docs/devel/reset: Document reset expectations for DMA and IOMMU To avoid any translation faults, the IOMMUs are expected to be reset after the devices they protect. Document that we expect DMA requests to be stopped during the 'enter' or 'hold' phase while IOMMUs should be reset during the 'exit' phase. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Message-Id: <20250218182737.76722-6-eric.auger@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/devel/reset.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index adefd59ef9..0b8b2fa5f4 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -143,6 +143,11 @@ The *exit* phase is executed only when the last reset operation ends. Therefore the object does not need to care how many of reset controllers it has and how many of them have started a reset. +DMA capable devices are expected to cancel all outstanding DMA operations +during either 'enter' or 'hold' phases. IOMMUs are expected to reset during +the 'exit' phase and this sequencing makes sure no outstanding DMA request +will fault. + Handling reset in a resettable object ------------------------------------- From 4098c6b7b62c6a11a15c74fa275e754af8970317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 13:36:59 +0100 Subject: [PATCH 1884/2892] tests/functional: Have microblaze tests inherit common parent class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the MicroblazeMachine class being common to both MicroblazeBigEndianMachine and MicroblazeLittleEndianMachine classes. Move the xmaton and ballerina tests to the parent class. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20250212123659.52764-11-philmd@linaro.org> [thuth: Add missing ASSET statements to the leaf classes] Signed-off-by: Thomas Huth --- .../functional/test_microblaze_s3adsp1800.py | 27 ++++++++++++++++ .../test_microblazeel_s3adsp1800.py | 31 +++---------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index c4226f49cf..c93fa14232 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -7,6 +7,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +from qemu_test import exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern @@ -20,6 +21,10 @@ class MicroblazeMachine(QemuSystemTest): 'day17.tar.xz'), '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + ASSET_IMAGE_LE = Asset( + ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), + 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') + def do_ballerina_be_test(self, machine): self.set_machine(machine) self.archive_extract(self.ASSET_IMAGE_BE) @@ -34,8 +39,30 @@ class MicroblazeMachine(QemuSystemTest): # message, that's why we don't test for a later string here. This # needs some investigation by a microblaze wizard one day... + def do_xmaton_le_test(self, machine): + self.require_netdev('user') + self.set_machine(machine) + self.archive_extract(self.ASSET_IMAGE_LE) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) + tftproot = self.scratch_file('day13') + self.vm.add_args('-nic', f'user,tftp={tftproot}') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') + wait_for_console_pattern(self, 'buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', + '821cd3cab8efd16ad6ee5acc3642a8ea') + + +class MicroblazeBigEndianMachine(MicroblazeMachine): + + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + def test_microblaze_s3adsp1800_legacy_be(self): self.do_ballerina_be_test('petalogix-s3adsp1800') + if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index 60aab4a45e..ab59941d57 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -7,37 +7,16 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test import exec_command_and_wait_for_pattern -from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern +from test_microblaze_s3adsp1800 import MicroblazeMachine -class MicroblazeelMachine(QemuSystemTest): +class MicroblazeLittleEndianMachine(MicroblazeMachine): - timeout = 90 - - ASSET_IMAGE_LE = Asset( - ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), - 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') - - def do_xmaton_le_test(self, machine): - self.require_netdev('user') - self.set_machine(machine) - self.archive_extract(self.ASSET_IMAGE_LE) - self.vm.set_console() - self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) - tftproot = self.scratch_file('day13') - self.vm.add_args('-nic', f'user,tftp={tftproot}') - self.vm.launch() - wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') - wait_for_console_pattern(self, 'buildroot login:') - exec_command_and_wait_for_pattern(self, 'root', '#') - exec_command_and_wait_for_pattern(self, - 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', - '821cd3cab8efd16ad6ee5acc3642a8ea') + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE def test_microblaze_s3adsp1800_legacy_le(self): self.do_xmaton_le_test('petalogix-s3adsp1800') + if __name__ == '__main__': - QemuSystemTest.main() + MicroblazeMachine.main() From 51beb350bbc489b705714033ebb2b4dfa86064a1 Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Wed, 19 Feb 2025 10:06:03 +0100 Subject: [PATCH 1885/2892] qapi/char.json: minor doc rewording for `hub` device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refine documentation for the hub device, specify the maximum. Signed-off-by: Roman Penyaev Cc: Marc-André Lureau Cc: Markus Armbruster Cc: qemu-devel@nongnu.org Message-ID: <20250219090607.559887-1-r.peniaev@gmail.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/char.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/char.json b/qapi/char.json index f02b66c06b..dde2f9538f 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -337,7 +337,7 @@ # # Configuration info for hub chardevs. # -# @chardevs: List of chardev IDs, which should be added to this hub +# @chardevs: IDs to be added to this hub (maximum 4 devices). # # Since: 10.0 ## From ab544de12787035edb7ad4994a80f9cd6a6b55d7 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 20 Feb 2025 13:38:31 -0800 Subject: [PATCH 1886/2892] hw/arm/smmuv3: Fill u.f_cd_fetch.addr for SMMU_EVT_F_CD_FETCH When we fill in the SMMUEventInfo for SMMU_EVT_F_CD_FETCH we write the address into the f_ste_fetch member of the union, but then when we come to read it back in smmuv3_record_event() we will (correctly) be using the f_cd_fetch member. This is more like a cosmetics fix since the f_cd_fetch and f_ste_fetch are basically the same field since they are in the exact same union with exact same type, but it's conceptually wrong. Use the correct union member. Signed-off-by: Nicolin Chen Message-id: 20250220213832.80289-1-nicolinc@nvidia.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b49a59b64c..b40acbe024 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -377,7 +377,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg, qemu_log_mask(LOG_GUEST_ERROR, "Cannot fetch pte at address=0x%"PRIx64"\n", addr); event->type = SMMU_EVT_F_CD_FETCH; - event->u.f_ste_fetch.addr = addr; + event->u.f_cd_fetch.addr = addr; return -EINVAL; } for (i = 0; i < ARRAY_SIZE(buf->word); i++) { From f10104aeae3a17f181d5bb37b7fd7dad7fe86cba Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Fri, 21 Feb 2025 06:54:19 -0800 Subject: [PATCH 1887/2892] hw/arm/virt: Support larger highmem MMIO regions The MMIO region size required to support virtualized environments with large PCI BAR regions can exceed the hardcoded limit configured in QEMU. For example, a VM with multiple NVIDIA Grace-Hopper GPUs passed through requires more MMIO memory than the amount provided by VIRT_HIGH_PCIE_MMIO (currently 512GB). Instead of updating VIRT_HIGH_PCIE_MMIO, introduce a new parameter, highmem-mmio-size, that specifies the MMIO size required to support the VM configuration. Example usage with 1TB MMIO region size: -machine virt,gic-version=3,highmem-mmio-size=1T Signed-off-by: Matthew R. Ochs Reviewed-by: Gavin Shan Reviewed-by: Shameer Kolothum Reviewed-by: Eric Auger Reviewed-by: Nicolin Chen Message-id: 20250221145419.1281890-1-mochs@nvidia.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/virt.rst | 4 ++++ hw/arm/virt.c | 52 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 0c9c2ce035..adf446c0a2 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -144,6 +144,10 @@ highmem-mmio Set ``on``/``off`` to enable/disable the high memory region for PCI MMIO. The default is ``on``. +highmem-mmio-size + Set the high memory region size for PCI MMIO. Must be a power of 2 and + greater than or equal to the default size (512G). + gic-version Specify the version of the Generic Interrupt Controller (GIC) to provide. Valid values are: diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4a5a9666e9..ee69081ef4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -53,6 +53,7 @@ #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/pci-host/gpex.h" @@ -192,6 +193,10 @@ static const MemMapEntry base_memmap[] = { [VIRT_MEM] = { GiB, LEGACY_RAMLIMIT_BYTES }, }; +/* Update the docs for highmem-mmio-size when changing this default */ +#define DEFAULT_HIGH_PCIE_MMIO_SIZE_GB 512 +#define DEFAULT_HIGH_PCIE_MMIO_SIZE (DEFAULT_HIGH_PCIE_MMIO_SIZE_GB * GiB) + /* * Highmem IO Regions: This memory map is floating, located after the RAM. * Each MemMapEntry base (GPA) will be dynamically computed, depending on the @@ -207,13 +212,16 @@ static const MemMapEntry base_memmap[] = { * PA space for one specific region is always reserved, even if the region * has been disabled or doesn't fit into the PA space. However, the PA space * for the region won't be reserved in these circumstances with compact layout. + * + * Note that the highmem-mmio-size property will update the high PCIE MMIO size + * field in this array. */ static MemMapEntry extended_memmap[] = { /* Additional 64 MB redist region (can contain up to 512 redistributors) */ [VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB }, [VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB }, /* Second PCIe window */ - [VIRT_HIGH_PCIE_MMIO] = { 0x0, 512 * GiB }, + [VIRT_HIGH_PCIE_MMIO] = { 0x0, DEFAULT_HIGH_PCIE_MMIO_SIZE }, }; static const int a15irqmap[] = { @@ -2550,6 +2558,40 @@ static void virt_set_highmem_mmio(Object *obj, bool value, Error **errp) vms->highmem_mmio = value; } +static void virt_get_highmem_mmio_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t size = extended_memmap[VIRT_HIGH_PCIE_MMIO].size; + + visit_type_size(v, name, &size, errp); +} + +static void virt_set_highmem_mmio_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t size; + + if (!visit_type_size(v, name, &size, errp)) { + return; + } + + if (!is_power_of_2(size)) { + error_setg(errp, "highmem-mmio-size is not a power of 2"); + return; + } + + if (size < DEFAULT_HIGH_PCIE_MMIO_SIZE) { + char *sz = size_to_str(DEFAULT_HIGH_PCIE_MMIO_SIZE); + error_setg(errp, "highmem-mmio-size cannot be set to a lower value " + "than the default (%s)", sz); + g_free(sz); + return; + } + + extended_memmap[VIRT_HIGH_PCIE_MMIO].size = size; +} static bool virt_get_its(Object *obj, Error **errp) { @@ -3207,6 +3249,14 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set on/off to enable/disable high " "memory region for PCI MMIO"); + object_class_property_add(oc, "highmem-mmio-size", "size", + virt_get_highmem_mmio_size, + virt_set_highmem_mmio_size, + NULL, NULL); + object_class_property_set_description(oc, "highmem-mmio-size", + "Set the high memory region size " + "for PCI MMIO"); + object_class_property_add_str(oc, "gic-version", virt_get_gic_version, virt_set_gic_version); object_class_property_set_description(oc, "gic-version", From cacf4cb2516aa4de94aa80fecb08be4dafa5ed44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 6 Feb 2025 15:12:09 +0000 Subject: [PATCH 1888/2892] monitor/hmp-cmds.c: Clean up hmp_dumpdtb printf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In hmp_dumpdtb(), we print a message when the command succeeds. This message is missing the trailing \n, so the HMP command prompt is printed immediately after it. We also weren't capitalizing 'DTB', or quoting the filename in the message. Fix these nits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250206151214.2947842-2-peter.maydell@linaro.org --- monitor/hmp-cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 3825ff40a9..7ded3378cf 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -431,6 +431,6 @@ void hmp_dumpdtb(Monitor *mon, const QDict *qdict) return; } - monitor_printf(mon, "dtb dumped to %s", filename); + monitor_printf(mon, "DTB dumped to '%s'\n", filename); } #endif From 3c25f487bc0672bf13473f4a7235c3ef592c954c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 6 Feb 2025 15:12:10 +0000 Subject: [PATCH 1889/2892] hw/openrisc: Support monitor dumpdtb command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The openrisc machines don't set MachineState::fdt to point to their DTB blob. This means that although the command line '-machine dumpdtb=file.dtb' option works, the equivalent QMP and HMP monitor commands do not, but instead produce the error "This machine doesn't have a FDT". Set MachineState::fdt in openrisc_load_fdt(), when we write it to guest memory. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250206151214.2947842-3-peter.maydell@linaro.org --- hw/openrisc/boot.c | 7 +++++-- hw/openrisc/openrisc_sim.c | 2 +- hw/openrisc/virt.c | 2 +- include/hw/openrisc/boot.h | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 0f08df812d..72e2756af0 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -90,8 +90,8 @@ hwaddr openrisc_load_initrd(void *fdt, const char *filename, return start + size; } -uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, - uint64_t mem_size) +uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, + hwaddr load_start, uint64_t mem_size) { uint32_t fdt_addr; int ret; @@ -111,6 +111,9 @@ uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, /* copy in the device tree */ qemu_fdt_dumpdtb(fdt, fdtsize); + /* Save FDT for dumpdtb monitor command */ + ms->fdt = fdt; + rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index e0da4067ba..d9e0744922 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -354,7 +354,7 @@ static void openrisc_sim_init(MachineState *machine) machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(machine, state->fdt, load_addr, machine->ram_size); } } diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 7b60bf8509..9afe407b00 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -540,7 +540,7 @@ static void openrisc_virt_init(MachineState *machine) machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(machine, state->fdt, load_addr, machine->ram_size); } } diff --git a/include/hw/openrisc/boot.h b/include/hw/openrisc/boot.h index 25a313d63a..9b4d88072c 100644 --- a/include/hw/openrisc/boot.h +++ b/include/hw/openrisc/boot.h @@ -20,6 +20,7 @@ #define OPENRISC_BOOT_H #include "exec/cpu-defs.h" +#include "hw/boards.h" hwaddr openrisc_load_kernel(ram_addr_t ram_size, const char *kernel_filename, @@ -28,7 +29,7 @@ hwaddr openrisc_load_kernel(ram_addr_t ram_size, hwaddr openrisc_load_initrd(void *fdt, const char *filename, hwaddr load_start, uint64_t mem_size); -uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, +uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, hwaddr load_start, uint64_t mem_size); #endif /* OPENRISC_BOOT_H */ From dfd0de718662a58ef2f2ef051939ed4b1a4d5ea7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 6 Feb 2025 15:12:11 +0000 Subject: [PATCH 1890/2892] hw/mips/boston: Check for error return from boston_fdt_filter() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function boston_fdt_filter() can return NULL on errors (in which case it will print an error message). When we call this from the non-FIT-image codepath, we aren't checking the return value, so we will plough on with a NULL pointer, and segfault in fdt_totalsize(). Check for errors here. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250206151214.2947842-4-peter.maydell@linaro.org --- hw/mips/boston.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 4690b254dd..de6ce1f163 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -810,6 +810,10 @@ static void boston_mach_init(MachineState *machine) dtb_load_data = boston_fdt_filter(s, dtb_file_data, NULL, &dtb_vaddr); + if (!dtb_load_data) { + /* boston_fdt_filter() already printed the error for us */ + exit(1); + } /* Calculate real fdt size after filter */ dt_size = fdt_totalsize(dtb_load_data); From db0dd33559ee97a1fe84a1272258646279aca2e2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 6 Feb 2025 15:12:12 +0000 Subject: [PATCH 1891/2892] hw/mips/boston: Support dumpdtb monitor commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The boston machine doesn't set MachineState::fdt to the DTB blob that it has loaded or created, which means that the QMP/HMP dumpdtb monitor commands don't work. Setting MachineState::fdt is easy in the non-FIT codepath: we can simply do so immediately before loading the DTB into guest memory. The FIT codepath is a bit more awkward as currently the FIT loader throws away the memory that the FDT was in after it loads it into guest memory. So we add a void *pfdt argument to load_fit() for it to store the FDT pointer into. There is some readjustment required of the pointer handling in loader-fit.c, so that it applies 'const' only where it should (e.g. the data pointer we get back from fdt_getprop() is const, because it's into the middle of the input FDT data, but the pointer that fit_load_image_alloc() should not be const, because it's freshly allocated memory that the caller can change if it likes). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250206151214.2947842-5-peter.maydell@linaro.org --- hw/core/loader-fit.c | 38 +++++++++++++++++++++----------------- hw/mips/boston.c | 11 +++++++---- include/hw/loader-fit.h | 21 ++++++++++++++++++--- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 9bdd4fa17c..6eb66406b0 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -32,8 +32,8 @@ #define FIT_LOADER_MAX_PATH (128) -static const void *fit_load_image_alloc(const void *itb, const char *name, - int *poff, size_t *psz, Error **errp) +static void *fit_load_image_alloc(const void *itb, const char *name, + int *poff, size_t *psz, Error **errp) { const void *data; const char *comp; @@ -80,11 +80,11 @@ static const void *fit_load_image_alloc(const void *itb, const char *name, return NULL; } - data = g_realloc(uncomp_data, uncomp_len); + uncomp_data = g_realloc(uncomp_data, uncomp_len); if (psz) { *psz = uncomp_len; } - return data; + return uncomp_data; } error_setg(errp, "unknown compression '%s'", comp); @@ -177,13 +177,12 @@ out: static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, int cfg, void *opaque, const void *match_data, - hwaddr kernel_end, Error **errp) + hwaddr kernel_end, void **pfdt, Error **errp) { ERRP_GUARD(); Error *err = NULL; const char *name; - const void *data; - const void *load_data; + void *data; hwaddr load_addr; int img_off; size_t sz; @@ -194,7 +193,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, return 0; } - load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); + data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); if (!data) { error_prepend(errp, "unable to load FDT image from FIT: "); return -EINVAL; @@ -211,19 +210,23 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, } if (ldr->fdt_filter) { - load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + void *filtered_data; + + filtered_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + if (filtered_data != data) { + g_free(data); + data = filtered_data; + } } load_addr = ldr->addr_to_phys(opaque, load_addr); - sz = fdt_totalsize(load_data); - rom_add_blob_fixed(name, load_data, sz, load_addr); + sz = fdt_totalsize(data); + rom_add_blob_fixed(name, data, sz, load_addr); - ret = 0; + *pfdt = data; + return 0; out: g_free((void *) data); - if (data != load_data) { - g_free((void *) load_data); - } return ret; } @@ -259,7 +262,8 @@ out: return ret; } -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) +int load_fit(const struct fit_loader *ldr, const char *filename, + void **pfdt, void *opaque) { Error *err = NULL; const struct fit_loader_match *match; @@ -323,7 +327,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) goto out; } - ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, + ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, pfdt, &err); if (ret) { error_report_err(err); diff --git a/hw/mips/boston.c b/hw/mips/boston.c index de6ce1f163..79410dabe7 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -358,8 +358,8 @@ static void gen_firmware(void *p, hwaddr kernel_entry, hwaddr fdt_addr) kernel_entry); } -static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, - const void *match_data, hwaddr *load_addr) +static void *boston_fdt_filter(void *opaque, const void *fdt_orig, + const void *match_data, hwaddr *load_addr) { BostonState *s = BOSTON(opaque); MachineState *machine = s->mach; @@ -797,7 +797,7 @@ static void boston_mach_init(MachineState *machine) if (kernel_size > 0) { int dt_size; g_autofree const void *dtb_file_data = NULL; - g_autofree const void *dtb_load_data = NULL; + void *dtb_load_data = NULL; hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); @@ -815,6 +815,8 @@ static void boston_mach_init(MachineState *machine) exit(1); } + machine->fdt = dtb_load_data; + /* Calculate real fdt size after filter */ dt_size = fdt_totalsize(dtb_load_data); rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); @@ -822,7 +824,8 @@ static void boston_mach_init(MachineState *machine) rom_ptr(dtb_paddr, dt_size)); } else { /* Try to load file as FIT */ - fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); + fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, + &machine->fdt, s); if (fit_err) { error_report("unable to load kernel image"); exit(1); diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h index 0832e379dc..9a43490ed6 100644 --- a/include/hw/loader-fit.h +++ b/include/hw/loader-fit.h @@ -30,12 +30,27 @@ struct fit_loader_match { struct fit_loader { const struct fit_loader_match *matches; hwaddr (*addr_to_phys)(void *opaque, uint64_t addr); - const void *(*fdt_filter)(void *opaque, const void *fdt, - const void *match_data, hwaddr *load_addr); + void *(*fdt_filter)(void *opaque, const void *fdt, + const void *match_data, hwaddr *load_addr); const void *(*kernel_filter)(void *opaque, const void *kernel, hwaddr *load_addr, hwaddr *entry_addr); }; -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); +/** + * load_fit: load a FIT format image + * @ldr: structure defining board specific properties and hooks + * @filename: image to load + * @pfdt: pointer to update with address of FDT blob + * @opaque: opaque value passed back to the hook functions in @ldr + * Returns: 0 on success, or a negative errno on failure + * + * @pfdt is used to tell the caller about the FDT blob. On return, it + * has been set to point to the FDT blob, and it is now the caller's + * responsibility to free that memory with g_free(). Usually the caller + * will want to pass in &machine->fdt here, to record the FDT blob for + * the dumpdtb option and QMP/HMP commands. + */ +int load_fit(const struct fit_loader *ldr, const char *filename, void **pfdt, + void *opaque); #endif /* HW_LOADER_FIT_H */ From 8fd2518ef2f8d34dc9ee53d6915a2a610eb1a659 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 6 Feb 2025 15:12:13 +0000 Subject: [PATCH 1892/2892] hw: Centralize handling of -machine dumpdtb option Currently we handle the 'dumpdtb' machine sub-option ad-hoc in every board model that has an FDT. It's up to the board code to make sure it calls qemu_fdt_dumpdtb() in the right place. This means we're inconsistent and often just ignore the user's command line argument: * if the board doesn't have an FDT at all * if the board supports FDT, but there happens not to be one present (usually because of a missing -fdt option) This isn't very helpful because it gives the user no clue why their option was ignored. However, in order to support the QMP/HMP dumpdtb commands we require now that every FDT machine stores a pointer to the FDT in MachineState::fdt. This means we can handle -machine dumpdtb centrally by calling the qmp_dumpdtb() function, unifying its handling with the QMP/HMP commands. All the board code calls to qemu_fdt_dumpdtb() can then be removed. For this commit we retain the existing behaviour that if there is no FDT we silently ignore the -machine dumpdtb option. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/arm/boot.c | 2 -- hw/core/machine.c | 25 +++++++++++++++++++++++++ hw/loongarch/virt-fdt-build.c | 1 - hw/mips/boston.c | 1 - hw/openrisc/boot.c | 1 - hw/ppc/e500.c | 1 - hw/ppc/pegasos2.c | 1 - hw/ppc/pnv.c | 1 - hw/ppc/spapr.c | 1 - hw/riscv/boot.c | 2 -- include/system/device_tree.h | 2 -- system/device_tree.c | 15 --------------- 12 files changed, 25 insertions(+), 28 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 42c18355e8..e296b62fa1 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -661,8 +661,6 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, binfo->modify_dtb(binfo, fdt); } - qemu_fdt_dumpdtb(fdt, size); - /* Put the DTB into the memory map as a ROM image: this will ensure * the DTB is copied again upon reset, even if addr points into RAM. */ diff --git a/hw/core/machine.c b/hw/core/machine.c index 02cff735b3..61c22f723a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-commands-machine.h" #include "qemu/madvise.h" #include "qom/object_interfaces.h" #include "system/cpus.h" @@ -1696,6 +1697,24 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify) notifier_remove(notify); } +static void handle_machine_dumpdtb(MachineState *ms) +{ + if (!ms->dumpdtb) { + return; + } + if (!ms->fdt) { + /* Silently ignore dumpdtb option if there is nothing to dump */ + return; + } +#ifdef CONFIG_FDT + qmp_dumpdtb(ms->dumpdtb, &error_fatal); + exit(0); +#else + error_report("This machine doesn't have an FDT"); + exit(1); +#endif +} + void qdev_machine_creation_done(void) { cpu_synchronize_all_post_init(); @@ -1712,6 +1731,12 @@ void qdev_machine_creation_done(void) phase_advance(PHASE_MACHINE_READY); qdev_assert_realized_properly(); + /* + * If the user used -machine dumpdtb=file.dtb to request that we + * dump the DTB to a file, do it now, and exit. + */ + handle_machine_dumpdtb(current_machine); + /* TODO: once all bus devices are qdevified, this should be done * when bus is created by qdev.c */ /* diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c index dbc269afba..728ce46699 100644 --- a/hw/loongarch/virt-fdt-build.c +++ b/hw/loongarch/virt-fdt-build.c @@ -527,7 +527,6 @@ void virt_fdt_setup(LoongArchVirtMachineState *lvms) * Put the FDT into the memory map as a ROM image: this will ensure * the FDT is copied again upon reset, even if addr points into RAM. */ - qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 79410dabe7..149a263bd5 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -395,7 +395,6 @@ static void *boston_fdt_filter(void *opaque, const void *fdt_orig, 1, ram_high_sz); fdt = g_realloc(fdt, fdt_totalsize(fdt)); - qemu_fdt_dumpdtb(fdt, fdt_sz); s->fdt_base = *load_addr; diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 72e2756af0..0a5881be31 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -109,7 +109,6 @@ uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, /* Should only fail if we've built a corrupted tree */ g_assert(ret == 0); /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); /* Save FDT for dumpdtb monitor command */ ms->fdt = fdt; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 26933e0457..fe8b9f7962 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -658,7 +658,6 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, done: if (!dry_run) { - qemu_fdt_dumpdtb(fdt, fdt_size); cpu_physical_memory_write(addr, fdt, fdt_size); /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b057672e82..246d6d633b 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -417,7 +417,6 @@ static void pegasos2_machine_reset(MachineState *machine, ResetType type) d[1] = cpu_to_be64(pm->kernel_size - (pm->kernel_entry - pm->kernel_addr)); qemu_fdt_setprop(fdt, "/chosen", "qemu,boot-kernel", d, sizeof(d)); - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); g_free(pm->fdt_blob); pm->fdt_blob = fdt; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 11fd477b71..87607508c7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -744,7 +744,6 @@ static void pnv_reset(MachineState *machine, ResetType type) _FDT((fdt_pack(fdt))); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); /* Update machine->fdt with latest fdt */ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f3a4b4235d..c15340a58d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1760,7 +1760,6 @@ static void spapr_machine_reset(MachineState *machine, ResetType type) 0, fdt_addr, 0); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); g_free(spapr->fdt_blob); spapr->fdt_size = fdt_totalsize(fdt); diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index c309441b7d..765b9e2b1a 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -374,8 +374,6 @@ void riscv_load_fdt(hwaddr fdt_addr, void *fdt) uint32_t fdtsize = fdt_totalsize(fdt); /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); - rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, diff --git a/include/system/device_tree.h b/include/system/device_tree.h index eb601522f8..49d8482ed4 100644 --- a/include/system/device_tree.h +++ b/include/system/device_tree.h @@ -133,8 +133,6 @@ int qemu_fdt_add_path(void *fdt, const char *path); sizeof(qdt_tmp)); \ } while (0) -void qemu_fdt_dumpdtb(void *fdt, int size); - /** * qemu_fdt_setprop_sized_cells_from_array: * @fdt: device tree blob diff --git a/system/device_tree.c b/system/device_tree.c index 4bc2d61b93..d605ed2a21 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -594,21 +594,6 @@ int qemu_fdt_add_path(void *fdt, const char *path) return retval; } -void qemu_fdt_dumpdtb(void *fdt, int size) -{ - const char *dumpdtb = current_machine->dumpdtb; - - if (dumpdtb) { - /* Dump the dtb to a file and quit */ - if (g_file_set_contents(dumpdtb, fdt, size, NULL)) { - info_report("dtb dumped to %s. Exiting.", dumpdtb); - exit(0); - } - error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb); - exit(1); - } -} - int qemu_fdt_setprop_sized_cells_from_array(void *fdt, const char *node_path, const char *property, From b61b9d891305abf8fe37f07280ca5a99a10da6cf Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Wed, 12 Feb 2025 09:56:19 +0800 Subject: [PATCH 1893/2892] target/loongarch: fix vcpu reset command word issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the KVM_REG_LOONGARCH_VCPU_RESET command word is sent to the kernel through the kvm_set_one_reg interface, the parameter source needs to be a legal address, otherwise the kernel will return an error and the command word will fail to be sent. Signed-off-by: Xianglai Li Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Bibo Mao --- target/loongarch/kvm/kvm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index a3f55155b0..27df02fa3a 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -581,9 +581,16 @@ static int kvm_loongarch_get_lbt(CPUState *cs) void kvm_arch_reset_vcpu(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); + int ret = 0; + uint64_t unused = 0; env->mp_state = KVM_MP_STATE_RUNNABLE; - kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0); + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, &unused); + if (ret) { + error_report("Failed to set KVM_REG_LOONGARCH_VCPU_RESET: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } } static int kvm_loongarch_get_mpstate(CPUState *cs) From 7bd4eaa847fcdbc4505d9ab95dafa21791d8302a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 18 Feb 2025 11:20:27 +0800 Subject: [PATCH 1894/2892] target/loongarch/gdbstub: Fix gdbstub incorrectly handling some registers Write operation with R32 (orig_a0) and R34 (CSR_BADV) is discarded on gdbstub implementation for LoongArch system. And return value should be register size rather than 0, since it is used to calculate offset of next register such as R33 (PC) in function handle_write_all_regs(). Cc: qemu-stable@nongnu.org Fixes: ca61e75071c6 ("target/loongarch: Add gdb support.") Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/gdbstub.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index dafa4feb75..471eda28c7 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -63,23 +63,24 @@ int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { CPULoongArchState *env = cpu_env(cs); target_ulong tmp; - int read_length; int length = 0; + if (n < 0 || n > 34) { + return 0; + } + if (is_la64(env)) { tmp = ldq_le_p(mem_buf); - read_length = 8; + length = 8; } else { tmp = ldl_le_p(mem_buf); - read_length = 4; + length = 4; } if (0 <= n && n < 32) { env->gpr[n] = tmp; - length = read_length; } else if (n == 33) { set_pc(env, tmp); - length = read_length; } return length; } From 0262c8075e9dc62a53a4bd15ea8d92a4c9adf018 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 19 Feb 2025 15:07:00 +0800 Subject: [PATCH 1895/2892] target/loongarch: Correct maximum physical address in KVM mode On 3A5000 system, the physical address space width for host is 48, however 47 bit for KVM VM. For KVM VM, size of physical address space is the same with that of virtual user space address. Here modify physical address space width with 47 bit in KVM mode. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index e91f4a5239..f203fcc7d7 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -406,7 +406,7 @@ static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; - uint32_t data = 0; + uint32_t data = 0, field; int i; for (i = 0; i < 21; i++) { @@ -419,7 +419,13 @@ static void loongarch_la464_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG1, ARCH, 2); data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); - data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f); + if (kvm_enabled()) { + /* GPA address width of VM is 47, field value is 47 - 1 */ + field = 0x2e; + } else { + field = 0x2f; /* 48 bit - 1 */ + } + data = FIELD_DP32(data, CPUCFG1, PALEN, field); data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); data = FIELD_DP32(data, CPUCFG1, UAL, 1); data = FIELD_DP32(data, CPUCFG1, RI, 1); From 3406b001e6e5992a8cc9b2442216de312b111c07 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 14 Feb 2025 16:30:50 +0800 Subject: [PATCH 1896/2892] target/loongarch: Add post init function for kvm mode Some features such as LBT and PMU are implemented in kvm mode, With paravirt features in future, post init function is added for kvm mode, so that property for these features will be created in kvm post init function. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.c | 2 +- target/loongarch/cpu.h | 8 ++++++++ target/loongarch/kvm/kvm.c | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index f203fcc7d7..012fcfbfda 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -765,7 +765,7 @@ void loongarch_cpu_post_init(Object *obj) loongarch_set_pmu); object_property_set_description(obj, "pmu", "Set off to performance monitor unit."); - + kvm_loongarch_cpu_post_init(cpu); } else { cpu->lbt = ON_OFF_AUTO_OFF; cpu->pmu = ON_OFF_AUTO_OFF; diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index f2a23b7a43..74dffcb552 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -491,4 +491,12 @@ static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, void loongarch_cpu_post_init(Object *obj); +#ifdef CONFIG_KVM +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); +#else +static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) +{ +} +#endif + #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 27df02fa3a..2d2fb1e261 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1016,6 +1016,10 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) +{ +} + int kvm_arch_destroy_vcpu(CPUState *cs) { return 0; From 780a65bd955dee4e58b2cefb3312e85731eace6e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 14 Feb 2025 17:10:48 +0800 Subject: [PATCH 1897/2892] target/loongarch: Move kvm specified vCPU property to kvm directory LBT and PMU feature is supported only in kvm mode, move property about these two features to function kvm_loongarch_cpu_post_init(). Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.c | 40 ++------------------------------------ target/loongarch/kvm/kvm.c | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 012fcfbfda..3788f895c1 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -718,34 +718,12 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp) cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); } -static bool loongarch_get_lbt(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lbt(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; -} - -static bool loongarch_get_pmu(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_pmu(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; -} - void loongarch_cpu_post_init(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); + cpu->lbt = ON_OFF_AUTO_OFF; + cpu->pmu = ON_OFF_AUTO_OFF; cpu->lsx = ON_OFF_AUTO_AUTO; cpu->lasx = ON_OFF_AUTO_AUTO; object_property_add_bool(obj, "lsx", loongarch_get_lsx, @@ -754,21 +732,7 @@ void loongarch_cpu_post_init(Object *obj) loongarch_set_lasx); /* lbt is enabled only in kvm mode, not supported in tcg mode */ if (kvm_enabled()) { - cpu->lbt = ON_OFF_AUTO_AUTO; - object_property_add_bool(obj, "lbt", loongarch_get_lbt, - loongarch_set_lbt); - object_property_set_description(obj, "lbt", - "Set off to disable Binary Tranlation."); - - cpu->pmu = ON_OFF_AUTO_AUTO; - object_property_add_bool(obj, "pmu", loongarch_get_pmu, - loongarch_set_pmu); - object_property_set_description(obj, "pmu", - "Set off to performance monitor unit."); kvm_loongarch_cpu_post_init(cpu); - } else { - cpu->lbt = ON_OFF_AUTO_OFF; - cpu->pmu = ON_OFF_AUTO_OFF; } } diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 2d2fb1e261..b02824356a 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1016,8 +1016,43 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } +static bool loongarch_get_lbt(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lbt(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +static bool loongarch_get_pmu(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_pmu(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) { + cpu->lbt = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "lbt", loongarch_get_lbt, + loongarch_set_lbt); + object_property_set_description(OBJECT(cpu), "lbt", + "Set off to disable Binary Tranlation."); + + cpu->pmu = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "pmu", loongarch_get_pmu, + loongarch_set_pmu); + object_property_set_description(OBJECT(cpu), "pmu", + "Set off to disable performance monitor unit."); } int kvm_arch_destroy_vcpu(CPUState *cs) From 5b0502c56412c115f9ff37fad56ff53674676a22 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 24 Feb 2025 10:30:47 +0800 Subject: [PATCH 1898/2892] target/loongarch: Add vCPU property for paravirt ipi feature Property kvm-pv-ipi is added to paravirt ipi feature, it is specially for kvm mode. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 18 ++++++++++++++++++ target/loongarch/loongarch-qmp-cmds.c | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 74dffcb552..447192bfe0 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -406,6 +406,7 @@ struct ArchCPU { OnOffAuto pmu; OnOffAuto lsx; OnOffAuto lasx; + OnOffAuto kvm_pv_ipi; /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index b02824356a..83a6887fe8 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1040,6 +1040,18 @@ static void loongarch_set_pmu(Object *obj, bool value, Error **errp) cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } +static bool kvm_pv_ipi_get(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->kvm_pv_ipi != ON_OFF_AUTO_OFF; +} + +static void kvm_pv_ipi_set(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->kvm_pv_ipi = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) { cpu->lbt = ON_OFF_AUTO_AUTO; @@ -1053,6 +1065,12 @@ void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) loongarch_set_pmu); object_property_set_description(OBJECT(cpu), "pmu", "Set off to disable performance monitor unit."); + + cpu->kvm_pv_ipi = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "kvm-pv-ipi", kvm_pv_ipi_get, + kvm_pv_ipi_set); + object_property_set_description(OBJECT(cpu), "kvm-pv-ipi", + "Set off to disable KVM paravirt IPI."); } int kvm_arch_destroy_vcpu(CPUState *cs) diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 3fde5a5a20..4f94a39833 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -40,7 +40,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) } static const char *cpu_model_advertised_features[] = { - "lsx", "lasx", "lbt", "pmu", NULL + "lsx", "lasx", "lbt", "pmu", "kvm-pv-ipi", NULL }; CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, From 620d9bd0022e011147c1133ef542c45a0da962e4 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 17 Feb 2025 09:52:40 +0800 Subject: [PATCH 1899/2892] target/loongarch: Add paravirt ipi feature detection Paravirt ipi feature is OnOffAuto type, feature detection is added to check whether it is supported by KVM host. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.h | 2 ++ target/loongarch/kvm/kvm.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 447192bfe0..3a8e45d9d5 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -287,6 +287,7 @@ enum loongarch_features { LOONGARCH_FEATURE_LASX, LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ LOONGARCH_FEATURE_PMU, + LOONGARCH_FEATURE_PV_IPI, }; typedef struct LoongArchBT { @@ -310,6 +311,7 @@ typedef struct CPUArchState { lbt_t lbt; uint32_t cpucfg[21]; + uint32_t pv_features; /* LoongArch CSRs */ uint64_t CSR_CRMD; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 83a6887fe8..3117441f53 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include #include - +#include "asm-loongarch/kvm_para.h" #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/error-report.h" @@ -882,6 +882,12 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); return (ret == 0); + case LOONGARCH_FEATURE_PV_IPI: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PV_IPI; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + default: return false; } @@ -980,6 +986,29 @@ static int kvm_cpu_check_pmu(CPUState *cs, Error **errp) return 0; } +static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = cpu_env(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PV_IPI); + if (cpu->kvm_pv_ipi == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'pv_ipi' feature not supported by KVM host"); + return -ENOTSUP; + } + } else if (cpu->kvm_pv_ipi != ON_OFF_AUTO_AUTO) { + kvm_supported = false; + } + + if (kvm_supported) { + env->pv_features |= BIT(KVM_FEATURE_IPI); + } + + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { uint64_t val; @@ -1013,6 +1042,11 @@ int kvm_arch_init_vcpu(CPUState *cs) error_report_err(local_err); } + ret = kvm_cpu_check_pv_features(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + } + return ret; } From 2698cc7c99b50cf4bb127c56e5c90f7f3cba6f0d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 17 Feb 2025 09:37:11 +0800 Subject: [PATCH 1900/2892] target/loongarch: Enable paravirt ipi feature The similiar with cpucfg register, paravirt ipi feature is set in function kvm_arch_put_registers(). Instead the paravirt feature can be enabled only once, it cannot be changed dynamically. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/kvm/kvm.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 3117441f53..ed0706e8e0 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -83,6 +83,33 @@ static int kvm_set_stealtime(CPUState *cs) return 0; } +static int kvm_set_pv_features(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + int err; + uint64_t val; + struct kvm_device_attr attr = { + .group = KVM_LOONGARCH_VCPU_CPUCFG, + .attr = CPUCFG_KVM_FEATURE, + .addr = (uint64_t)&val, + }; + + err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); + if (err) { + return 0; + } + + val = env->pv_features; + err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr); + if (err) { + error_report("Fail to set pv feature "TARGET_FMT_lx " with error %s", + val, strerror(errno)); + return err; + } + + return 0; +} + static int kvm_loongarch_get_regs_core(CPUState *cs) { int ret = 0; @@ -738,6 +765,7 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) { int ret; + static int once; ret = kvm_loongarch_put_regs_core(cs); if (ret) { @@ -764,6 +792,14 @@ int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) return ret; } + if (!once) { + ret = kvm_set_pv_features(cs); + if (ret) { + return ret; + } + once = 1; + } + if (level >= KVM_PUT_FULL_STATE) { /* * only KVM_PUT_FULL_STATE is required, kvm kernel will clear From 610babce1ed83a0fd3af14f5195114d6c5338610 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sat, 15 Feb 2025 17:01:21 +0800 Subject: [PATCH 1901/2892] target/loongarch: Add vCPU property for kvm steal time feature Property kvm-steal-time is added for kvm steal time feature, it is specially for kvm mode. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 18 ++++++++++++++++++ target/loongarch/loongarch-qmp-cmds.c | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 3a8e45d9d5..3e8a91748d 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -409,6 +409,7 @@ struct ArchCPU { OnOffAuto lsx; OnOffAuto lasx; OnOffAuto kvm_pv_ipi; + OnOffAuto kvm_steal_time; /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index ed0706e8e0..def7451224 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1122,6 +1122,18 @@ static void kvm_pv_ipi_set(Object *obj, bool value, Error **errp) cpu->kvm_pv_ipi = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } +static bool kvm_steal_time_get(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->kvm_steal_time != ON_OFF_AUTO_OFF; +} + +static void kvm_steal_time_set(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->kvm_steal_time = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) { cpu->lbt = ON_OFF_AUTO_AUTO; @@ -1141,6 +1153,12 @@ void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) kvm_pv_ipi_set); object_property_set_description(OBJECT(cpu), "kvm-pv-ipi", "Set off to disable KVM paravirt IPI."); + + cpu->kvm_steal_time = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "kvm-steal-time", kvm_steal_time_get, + kvm_steal_time_set); + object_property_set_description(OBJECT(cpu), "kvm-steal-time", + "Set off to disable KVM steal time."); } int kvm_arch_destroy_vcpu(CPUState *cs) diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 4f94a39833..6f732d80f3 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -40,7 +40,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) } static const char *cpu_model_advertised_features[] = { - "lsx", "lasx", "lbt", "pmu", "kvm-pv-ipi", NULL + "lsx", "lasx", "lbt", "pmu", "kvm-pv-ipi", "kvm-steal-time", NULL }; CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, From 954cc5c311cd4459ce16a3302ff8611d98473d7d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 17 Feb 2025 09:55:32 +0800 Subject: [PATCH 1902/2892] target/loongarch: Add kvm steal time feature detection Paravirt steal time feature is OnOffAuto type, feature detection is added to check whether it is supported on KVM host. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 3e8a91748d..83183a33ab 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -288,6 +288,7 @@ enum loongarch_features { LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ LOONGARCH_FEATURE_PMU, LOONGARCH_FEATURE_PV_IPI, + LOONGARCH_FEATURE_STEALTIME, }; typedef struct LoongArchBT { diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index def7451224..59a5f84161 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -924,6 +924,12 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); return (ret == 0); + case LOONGARCH_FEATURE_STEALTIME: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PV_STEALTIME; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + default: return false; } @@ -1042,6 +1048,20 @@ static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) env->pv_features |= BIT(KVM_FEATURE_IPI); } + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_STEALTIME); + if (cpu->kvm_steal_time == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'kvm stealtime' feature not supported by KVM host"); + return -ENOTSUP; + } + } else if (cpu->kvm_steal_time != ON_OFF_AUTO_AUTO) { + kvm_supported = false; + } + + if (kvm_supported) { + env->pv_features |= BIT(KVM_FEATURE_STEAL_TIME); + } + return 0; } From db369c11c90b35f3a6ab59ad78564aea5b30c3da Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 17 Feb 2025 09:56:13 +0800 Subject: [PATCH 1903/2892] target/loongarch: Enable virtual extioi feature Feature virtual extioi is loongArch virt machine property rather than vCPU property in qemu side. However it is vCPU property in KVM kernel side, here add loongArch virt machine property checking and enable virt extioi feature when vCPU is created. Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt.c | 8 -------- include/hw/loongarch/virt.h | 9 +++++++++ target/loongarch/kvm/kvm.c | 10 ++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index f2aa0a9782..59533b058b 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -45,14 +45,6 @@ #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" -static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) -{ - if (lvms->veiointc == ON_OFF_AUTO_OFF) { - return false; - } - return true; -} - static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 661efae61d..2e7cdfaef0 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -68,4 +68,13 @@ struct LoongArchVirtMachineState { OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) void virt_acpi_setup(LoongArchVirtMachineState *lvms); void virt_fdt_setup(LoongArchVirtMachineState *lvms); + +static inline bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->veiointc == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + #endif diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 59a5f84161..28735c80be 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -21,6 +21,7 @@ #include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/irq.h" +#include "hw/loongarch/virt.h" #include "qemu/log.h" #include "hw/loader.h" #include "system/runstate.h" @@ -1030,6 +1031,7 @@ static int kvm_cpu_check_pmu(CPUState *cs, Error **errp) static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) { + MachineState *ms = MACHINE(qdev_get_machine()); LoongArchCPU *cpu = LOONGARCH_CPU(cs); CPULoongArchState *env = cpu_env(cs); bool kvm_supported; @@ -1062,6 +1064,14 @@ static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) env->pv_features |= BIT(KVM_FEATURE_STEAL_TIME); } + if (object_dynamic_cast(OBJECT(ms), TYPE_LOONGARCH_VIRT_MACHINE)) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + + if (virt_is_veiointc_enabled(lvms)) { + env->pv_features |= BIT(KVM_FEATURE_VIRT_EXTIOI); + } + } + return 0; } From d8b913f7c75cd38ad07276033faa51fee68514b4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 23 Jan 2025 21:49:56 +0100 Subject: [PATCH 1904/2892] tests/qtest/qom-test: Test retrieval of machine class properties There were recently some crashes that occurred when trying to retrieve the properties of machines. Let's add a test to avoid regression here. Message-ID: <20250123204956.1561463-1-thuth@redhat.com> Reviewed-by: Fabiano Rosas Signed-off-by: Thomas Huth --- tests/qtest/qom-test.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 1e30a5bfe8..27d70bc11c 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -88,6 +88,17 @@ static void test_machine(gconstpointer data) qts = qtest_initf("-machine %s", machine); + if (g_test_slow()) { + /* Make sure we can get the machine class properties: */ + g_autofree char *qom_machine = g_strdup_printf("%s-machine", machine); + + response = qtest_qmp(qts, "{ 'execute': 'qom-list-properties'," + " 'arguments': { 'typename': %s } }", + qom_machine); + g_assert(response); + qobject_unref(response); + } + test_properties(qts, "/machine", true); response = qtest_qmp(qts, "{ 'execute': 'quit' }"); From 36324c6774d2da16edc97cd3c9b30aa34d3f7a83 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 7 Feb 2025 14:53:43 +0900 Subject: [PATCH 1905/2892] qom: Use command line syntax for default values in help object_property_help() uses the conventional command line syntax instead of the JSON syntax. In particular, - Key-value pairs are written in the command line syntax. - bool description passed to the function says on/off instead of true/false. However, there is one exception: default values are formatted into JSON. While the command line and JSON syntaxes are consistent in many cases, there are two types where they disagree: string: The command line syntax omits quotes while JSON requires them. bool: JSON only accepts true/false for bool but the command line syntax accepts on/off too, and on/off are also more popular than true/false. For example, the docs directory has 2045 "on" occurances while it has only 194 "true" occurances. on/off are also accepted by OnOffAuto so users do not have to remember the type is bool or OnOffAuto to use the values. Omit quotes for strings and use on/off for bools when formatting default values for better consistency. Signed-off-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250207-bool-v1-1-5749d5d6df24@daynix.com Signed-off-by: Paolo Bonzini --- qom/object_interfaces.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f35d331331..1ffea1a728 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -4,9 +4,11 @@ #include "qapi/error.h" #include "qapi/qapi-visit-qom.h" #include "qobject/qobject.h" +#include "qobject/qbool.h" #include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qom/object_interfaces.h" @@ -177,9 +179,25 @@ char *object_property_help(const char *name, const char *type, g_string_append(str, description); } if (defval) { - g_autofree char *def_json = g_string_free(qobject_to_json(defval), - false); - g_string_append_printf(str, " (default: %s)", def_json); + g_autofree char *def_json = NULL; + const char *def; + + switch (qobject_type(defval)) { + case QTYPE_QSTRING: + def = qstring_get_str(qobject_to(QString, defval)); + break; + + case QTYPE_QBOOL: + def = qbool_get_bool(qobject_to(QBool, defval)) ? "on" : "off"; + break; + + default: + def_json = g_string_free(qobject_to_json(defval), false); + def = def_json; + break; + } + + g_string_append_printf(str, " (default: %s)", def); } return g_string_free(str, false); From 1433e38cc840957bafe6bc17a241c57cf93c90cd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Dec 2024 21:25:39 +0100 Subject: [PATCH 1906/2892] hpet: do not overwrite properties on post_load Migration relies on having the same device configuration on the source and destination. Therefore, there is no need to modify flags, timer capabilities and the fw_cfg HPET block id on migration; it was set to exactly the same values by realize. Reviewed-by: Zhao Liu (hpet_post_load only) Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index dcff18a987..ccb97b6806 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -77,6 +77,7 @@ struct HPETState { uint8_t rtc_irq_level; qemu_irq pit_enabled; uint8_t num_timers; + uint8_t num_timers_save; uint32_t intcap; HPETTimer timer[HPET_MAX_TIMERS]; @@ -237,15 +238,12 @@ static int hpet_pre_save(void *opaque) s->hpet_counter = hpet_get_ticks(s); } - return 0; -} - -static int hpet_pre_load(void *opaque) -{ - HPETState *s = opaque; - - /* version 1 only supports 3, later versions will load the actual value */ - s->num_timers = HPET_MIN_TIMERS; + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + s->num_timers_save = s->num_timers; return 0; } @@ -253,12 +251,7 @@ static bool hpet_validate_num_timers(void *opaque, int version_id) { HPETState *s = opaque; - if (s->num_timers < HPET_MIN_TIMERS) { - return false; - } else if (s->num_timers > HPET_MAX_TIMERS) { - return false; - } - return true; + return s->num_timers == s->num_timers_save; } static int hpet_post_load(void *opaque, int version_id) @@ -277,16 +270,6 @@ static int hpet_post_load(void *opaque, int version_id) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } - /* Push number of timers into capability returned via HPET_ID */ - s->capability &= ~HPET_ID_NUM_TIM_MASK; - s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - - /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ - s->flags &= ~(1 << HPET_MSI_SUPPORT); - if (s->timer[0].config & HPET_TN_FSB_CAP) { - s->flags |= 1 << HPET_MSI_SUPPORT; - } return 0; } @@ -347,14 +330,13 @@ static const VMStateDescription vmstate_hpet = { .version_id = 2, .minimum_version_id = 1, .pre_save = hpet_pre_save, - .pre_load = hpet_pre_load, .post_load = hpet_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64(config, HPETState), VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), - VMSTATE_UINT8_V(num_timers, HPETState, 2), - VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers), + VMSTATE_UINT8_V(num_timers_save, HPETState, 2), + VMSTATE_VALIDATE("num_timers must match", hpet_validate_num_timers), VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() From 9ee488670066b5fdeebf0015e9b4a40bdc3eccbf Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 17 Feb 2025 23:44:16 +0800 Subject: [PATCH 1907/2892] i386: Fix the missing Rust HPET configuration option The configuration option of Rust HPET is missing, so that PC machine can't boot with "hpet=on" when QEMU Rust support is enabled. Add the Rust HPET configuration option. Fixes: d128c341a744 ("i386: enable rust hpet for pc when rust is enabled") Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250217154416.3144571-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig index afd9803350..42e421317a 100644 --- a/rust/hw/timer/Kconfig +++ b/rust/hw/timer/Kconfig @@ -1,2 +1,3 @@ config X_HPET_RUST bool + default y if PC && HAVE_RUST From 4cfe9edb1b1961af9cda74351f73b0abb3159b67 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 16 Dec 2024 09:42:35 +0100 Subject: [PATCH 1908/2892] rust: subprojects: add libc crate This allows access to errno values. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 7 ++++ rust/qemu-api/Cargo.toml | 1 + scripts/archive-source.sh | 2 +- scripts/make-release | 2 +- subprojects/.gitignore | 1 + subprojects/libc-0.2-rs.wrap | 7 ++++ .../packagefiles/libc-0.2-rs/meson.build | 37 +++++++++++++++++++ 7 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 subprojects/libc-0.2-rs.wrap create mode 100644 subprojects/packagefiles/libc-0.2-rs/meson.build diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 79e142723b..2ebf0a11ea 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -54,6 +54,12 @@ dependencies = [ "either", ] +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + [[package]] name = "pl011" version = "0.1.0" @@ -100,6 +106,7 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "libc", "qemu_api_macros", "version_check", ] diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index a51dd14285..57747bc934 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -16,6 +16,7 @@ rust-version = "1.63.0" [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } +libc = "0.2.162" [build-dependencies] version_check = "~0.9" diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 30677c3ec9..e461c1531e 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -28,7 +28,7 @@ sub_file="${sub_tdir}/submodule.tar" # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" sub_deinit="" diff --git a/scripts/make-release b/scripts/make-release index 1b89b3423a..8c3594a1a4 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -41,7 +41,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 50f173f90d..d12d34618c 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -11,6 +11,7 @@ /bilge-impl-0.2.0 /either-1.12.0 /itertools-0.11.0 +/libc-0.2.162 /proc-macro-error-1.0.4 /proc-macro-error-attr-1.0.4 /proc-macro2-1.0.84 diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap new file mode 100644 index 0000000000..bbe08f8788 --- /dev/null +++ b/subprojects/libc-0.2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = libc-0.2.162 +source_url = https://crates.io/api/v1/crates/libc/0.2.162/download +source_filename = libc-0.2.162.tar.gz +source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 +#method = cargo +patch_directory = libc-0.2-rs diff --git a/subprojects/packagefiles/libc-0.2-rs/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson.build new file mode 100644 index 0000000000..ac4f80dba9 --- /dev/null +++ b/subprojects/packagefiles/libc-0.2-rs/meson.build @@ -0,0 +1,37 @@ +project('libc-0.2-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.162', + license: 'MIT OR Apache-2.0', + default_options: []) + +_libc_rs = static_library( + 'libc', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2015', 'build.rust_std=2015'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'freebsd11', + '--cfg', 'libc_priv_mod_use', + '--cfg', 'libc_union', + '--cfg', 'libc_const_size_of', + '--cfg', 'libc_align', + '--cfg', 'libc_int128', + '--cfg', 'libc_core_cvoid', + '--cfg', 'libc_packedN', + '--cfg', 'libc_cfg_target_vendor', + '--cfg', 'libc_non_exhaustive', + '--cfg', 'libc_long_array', + '--cfg', 'libc_ptr_addr_of', + '--cfg', 'libc_underscore_const_names', + '--cfg', 'libc_const_extern_fn', + ], + dependencies: [], +) + +libc_dep = declare_dependency( + link_with: _libc_rs, +) + +meson.override_dependency('libc-0.2-rs', libc_dep) From 23a4b3ebc74bbc6b6f44edf2255864042f122b82 Mon Sep 17 00:00:00 2001 From: Stephen Bates Date: Fri, 29 Nov 2024 09:27:39 -0700 Subject: [PATCH 1909/2892] hw/nvme: Add OCP SMART / Health Information Extended Log Page The Open Compute Project [1] includes a Datacenter NVMe SSD Specification [2]. The most recent version of this specification (as of November 2024) is 2.6.1. This specification layers on top of the NVM Express specifications [3] to provide additional functionality. A key part of of this is the 512 Byte OCP SMART / Health Information Extended log page that is defined in Section 4.8.6 of the specification. We add a controller argument (ocp) that toggles on/off the SMART log extended structure. To accommodate different vendor specific specifications like OCP, we add a multiplexing function (nvme_vendor_specific_log) which will route to the different log functions based on arguments and log ids. We only return the OCP extended SMART log when the command is 0xC0 and ocp has been turned on in the nvme argumants. Though we add the whole nvme SMART log extended structure, we only populate the physical_media_units_{read,written}, log_page_version and log_page_uuid. This patch is based on work done by Joel but has been modified enough that he requested a co-developed-by tag rather than a signed-off-by. [1]: https://www.opencompute.org/ [2]: https://www.opencompute.org/documents/datacenter-nvme-ssd-specification-v2-6-1-pdf [3]: https://nvmexpress.org/specifications/ Signed-off-by: Stephen Bates Co-developed-by: Joel Granados Reviewed-by: Klaus Jensen Signed-off-by: Klaus Jensen --- docs/system/devices/nvme.rst | 7 +++++ hw/nvme/ctrl.c | 59 ++++++++++++++++++++++++++++++++++++ hw/nvme/nvme.h | 1 + include/block/nvme.h | 41 +++++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/docs/system/devices/nvme.rst b/docs/system/devices/nvme.rst index d2b1ca9645..6509b35fcb 100644 --- a/docs/system/devices/nvme.rst +++ b/docs/system/devices/nvme.rst @@ -53,6 +53,13 @@ parameters. Vendor ID. Set this to ``on`` to revert to the unallocated Intel ID previously used. +``ocp`` (default: ``off``) + The Open Compute Project defines the Datacenter NVMe SSD Specification that + sits on top of NVMe. It describes additional commands and NVMe behaviors + specific for the Datacenter. When this option is ``on`` OCP features such as + the SMART / Health information extended log become available in the + controller. We emulate version 5 of this log page. + Additional Namespaces --------------------- diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 8175751518..11687e597a 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4917,6 +4917,45 @@ static void nvme_set_blk_stats(NvmeNamespace *ns, struct nvme_stats *stats) stats->write_commands += s->nr_ops[BLOCK_ACCT_WRITE]; } +static uint16_t nvme_ocp_extended_smart_info(NvmeCtrl *n, uint8_t rae, + uint32_t buf_len, uint64_t off, + NvmeRequest *req) +{ + NvmeNamespace *ns = NULL; + NvmeSmartLogExtended smart_l = { 0 }; + struct nvme_stats stats = { 0 }; + uint32_t trans_len; + + if (off >= sizeof(smart_l)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + /* accumulate all stats from all namespaces */ + for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + if (ns) { + nvme_set_blk_stats(ns, &stats); + } + } + + smart_l.physical_media_units_written[0] = cpu_to_le64(stats.units_written); + smart_l.physical_media_units_read[0] = cpu_to_le64(stats.units_read); + smart_l.log_page_version = 0x0005; + + static const uint8_t guid[16] = { + 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4, + 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF + }; + memcpy(smart_l.log_page_guid, guid, sizeof(smart_l.log_page_guid)); + + if (!rae) { + nvme_clear_events(n, NVME_AER_TYPE_SMART); + } + + trans_len = MIN(sizeof(smart_l) - off, buf_len); + return nvme_c2h(n, (uint8_t *) &smart_l + off, trans_len, req); +} + static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, uint64_t off, NvmeRequest *req) { @@ -5146,6 +5185,23 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, return nvme_c2h(n, ((uint8_t *)&log) + off, trans_len, req); } +static uint16_t nvme_vendor_specific_log(NvmeCtrl *n, uint8_t rae, + uint32_t buf_len, uint64_t off, + NvmeRequest *req, uint8_t lid) +{ + switch (lid) { + case NVME_OCP_EXTENDED_SMART_INFO: + if (n->params.ocp) { + return nvme_ocp_extended_smart_info(n, rae, buf_len, off, req); + } + break; + /* add a case for each additional vendor specific log id */ + } + + trace_pci_nvme_err_invalid_log_page(nvme_cid(req), lid); + return NVME_INVALID_FIELD | NVME_DNR; +} + static size_t sizeof_fdp_conf_descr(size_t nruh, size_t vss) { size_t entry_siz = sizeof(NvmeFdpDescrHdr) + nruh * sizeof(NvmeRuhDescr) @@ -5396,6 +5452,8 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) return nvme_smart_info(n, rae, len, off, req); case NVME_LOG_FW_SLOT_INFO: return nvme_fw_log_info(n, len, off, req); + case NVME_LOG_VENDOR_START...NVME_LOG_VENDOR_END: + return nvme_vendor_specific_log(n, rae, len, off, req, lid); case NVME_LOG_CHANGED_NSLIST: return nvme_changed_nslist(n, rae, len, off, req); case NVME_LOG_CMD_EFFECTS: @@ -8971,6 +9029,7 @@ static const Property nvme_props[] = { DEFINE_PROP_BOOL("atomic.dn", NvmeCtrl, params.atomic_dn, 0), DEFINE_PROP_UINT16("atomic.awun", NvmeCtrl, params.atomic_awun, 0), DEFINE_PROP_UINT16("atomic.awupf", NvmeCtrl, params.atomic_awupf, 0), + DEFINE_PROP_BOOL("ocp", NvmeCtrl, params.ocp, false), }; static void nvme_get_smart_warning(Object *obj, Visitor *v, const char *name, diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 7242206910..e307e733e4 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -545,6 +545,7 @@ typedef struct NvmeParams { uint32_t sriov_max_vq_per_vf; uint32_t sriov_max_vi_per_vf; bool msix_exclusive_bar; + bool ocp; struct { bool mem; diff --git a/include/block/nvme.h b/include/block/nvme.h index f4d108841b..975d321c5c 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1015,6 +1015,40 @@ typedef struct QEMU_PACKED NvmeSmartLog { uint8_t reserved2[320]; } NvmeSmartLog; +typedef struct QEMU_PACKED NvmeSmartLogExtended { + uint64_t physical_media_units_written[2]; + uint64_t physical_media_units_read[2]; + uint64_t bad_user_blocks; + uint64_t bad_system_nand_blocks; + uint64_t xor_recovery_count; + uint64_t uncorrectable_read_error_count; + uint64_t soft_ecc_error_count; + uint64_t end2end_correction_counts; + uint8_t system_data_percent_used; + uint8_t refresh_counts[7]; + uint64_t user_data_erase_counts; + uint16_t thermal_throttling_stat_and_count; + uint16_t dssd_spec_version[3]; + uint64_t pcie_correctable_error_count; + uint32_t incomplete_shutdowns; + uint32_t rsvd116; + uint8_t percent_free_blocks; + uint8_t rsvd121[7]; + uint16_t capacity_health; + uint8_t nvme_errata_ver; + uint8_t rsvd131[5]; + uint64_t unaligned_io; + uint64_t security_ver_num; + uint64_t total_nuse; + uint64_t plp_start_count[2]; + uint64_t endurance_estimate[2]; + uint64_t pcie_retraining_count; + uint64_t power_state_change_count; + uint8_t rsvd208[286]; + uint16_t log_page_version; + uint64_t log_page_guid[2]; +} NvmeSmartLogExtended; + #define NVME_SMART_WARN_MAX 6 enum NvmeSmartWarn { NVME_SMART_SPARE = 1 << 0, @@ -1052,6 +1086,12 @@ enum NvmeLogIdentifier { NVME_LOG_FDP_RUH_USAGE = 0x21, NVME_LOG_FDP_STATS = 0x22, NVME_LOG_FDP_EVENTS = 0x23, + NVME_LOG_VENDOR_START = 0xc0, + NVME_LOG_VENDOR_END = 0xff, +}; + +enum NvmeOcpLogIdentifier { + NVME_OCP_EXTENDED_SMART_INFO = 0xc0, }; typedef struct QEMU_PACKED NvmePSD { @@ -1899,6 +1939,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512); + QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLogExtended) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeEffectsLog) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrlZoned) != 4096); From cd59f50ab017183805a0dd82f5e85159ecc355ce Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:02 +0100 Subject: [PATCH 1910/2892] hw/nvme: always initialize a subsystem If no nvme-subsys is explicitly configured, instantiate one. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 36 +++++++++++++++------------- hw/nvme/ns.c | 64 +++++++++++++++++--------------------------------- 2 files changed, 42 insertions(+), 58 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 11687e597a..5a7ccbcc1b 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8823,15 +8823,13 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->psd[0].enlat = cpu_to_le32(0x10); id->psd[0].exlat = cpu_to_le32(0x4); - if (n->subsys) { - id->cmic |= NVME_CMIC_MULTI_CTRL; - ctratt |= NVME_CTRATT_ENDGRPS; + id->cmic |= NVME_CMIC_MULTI_CTRL; + ctratt |= NVME_CTRATT_ENDGRPS; - id->endgidmax = cpu_to_le16(0x1); + id->endgidmax = cpu_to_le16(0x1); - if (n->subsys->endgrp.fdp.enabled) { - ctratt |= NVME_CTRATT_FDPS; - } + if (n->subsys->endgrp.fdp.enabled) { + ctratt |= NVME_CTRATT_FDPS; } id->ctratt = cpu_to_le32(ctratt); @@ -8860,7 +8858,15 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp) int cntlid; if (!n->subsys) { - return 0; + DeviceState *dev = qdev_new(TYPE_NVME_SUBSYS); + + qdev_prop_set_string(dev, "nqn", n->params.serial); + + if (!qdev_realize(dev, NULL, errp)) { + return -1; + } + + n->subsys = NVME_SUBSYS(dev); } cntlid = nvme_subsys_register_ctrl(n, errp); @@ -8950,17 +8956,15 @@ static void nvme_exit(PCIDevice *pci_dev) nvme_ctrl_reset(n, NVME_RESET_FUNCTION); - if (n->subsys) { - for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - ns = nvme_ns(n, i); - if (ns) { - ns->attached--; - } + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + if (ns) { + ns->attached--; } - - nvme_subsys_unregister_ctrl(n->subsys, n); } + nvme_subsys_unregister_ctrl(n->subsys, n); + g_free(n->cq); g_free(n->sq); g_free(n->aer_reqs); diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 410df29591..94cabc6a5b 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -727,25 +727,14 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) uint32_t nsid = ns->params.nsid; int i; - if (!n->subsys) { - /* If no subsys, the ns cannot be attached to more than one ctrl. */ - ns->params.shared = false; - if (ns->params.detached) { - error_setg(errp, "detached requires that the nvme device is " - "linked to an nvme-subsys device"); - return; - } - } else { - /* - * If this namespace belongs to a subsystem (through a link on the - * controller device), reparent the device. - */ - if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) { - return; - } - ns->subsys = subsys; - ns->endgrp = &subsys->endgrp; + assert(subsys); + + /* reparent to subsystem bus */ + if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) { + return; } + ns->subsys = subsys; + ns->endgrp = &subsys->endgrp; if (nvme_ns_setup(ns, errp)) { return; @@ -753,7 +742,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) if (!nsid) { for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - if (nvme_ns(n, i) || nvme_subsys_ns(subsys, i)) { + if (nvme_subsys_ns(subsys, i)) { continue; } @@ -765,38 +754,29 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) error_setg(errp, "no free namespace id"); return; } - } else { - if (nvme_ns(n, nsid) || nvme_subsys_ns(subsys, nsid)) { - error_setg(errp, "namespace id '%d' already allocated", nsid); - return; - } + } else if (nvme_subsys_ns(subsys, nsid)) { + error_setg(errp, "namespace id '%d' already allocated", nsid); + return; } - if (subsys) { - subsys->namespaces[nsid] = ns; + subsys->namespaces[nsid] = ns; - ns->id_ns.endgid = cpu_to_le16(0x1); - ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); + ns->id_ns.endgid = cpu_to_le16(0x1); + ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); - if (ns->params.detached) { - return; - } + if (ns->params.detached) { + return; + } - if (ns->params.shared) { - for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { - NvmeCtrl *ctrl = subsys->ctrls[i]; + if (ns->params.shared) { + for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { + NvmeCtrl *ctrl = subsys->ctrls[i]; - if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { - nvme_attach_ns(ctrl, ns); - } + if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { + nvme_attach_ns(ctrl, ns); } - - return; } - } - - nvme_attach_ns(n, ns); } static const Property nvme_ns_props[] = { From e7047adf1ebf4b5ad63e42c799d8334dcd3d139d Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:03 +0100 Subject: [PATCH 1911/2892] hw/nvme: make oacs dynamic Virtualization Management needs sriov-related parameters. Only report support for the command when that conditions are true. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 25 ++++++++++++++++++------- hw/nvme/nvme.h | 4 ++++ include/block/nvme.h | 3 ++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 5a7ccbcc1b..4ee8588ca9 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -266,7 +266,7 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { [NVME_FDP_EVENTS] = NVME_FEAT_CAP_CHANGE | NVME_FEAT_CAP_NS, }; -static const uint32_t nvme_cse_acs[256] = { +static const uint32_t nvme_cse_acs_default[256] = { [NVME_ADM_CMD_DELETE_SQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_CREATE_SQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_LOG_PAGE] = NVME_CMD_EFF_CSUPP, @@ -278,7 +278,6 @@ static const uint32_t nvme_cse_acs[256] = { [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, - [NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP, @@ -5174,7 +5173,7 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, } } - memcpy(log.acs, nvme_cse_acs, sizeof(nvme_cse_acs)); + memcpy(log.acs, n->cse.acs, sizeof(log.acs)); if (src_iocs) { memcpy(log.iocs, src_iocs, sizeof(log.iocs)); @@ -7300,7 +7299,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode, nvme_adm_opc_str(req->cmd.opcode)); - if (!(nvme_cse_acs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + if (!(n->cse.acs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { trace_pci_nvme_err_invalid_admin_opc(req->cmd.opcode); return NVME_INVALID_OPCODE | NVME_DNR; } @@ -8740,6 +8739,9 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) uint64_t cap = ldq_le_p(&n->bar.cap); NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); uint32_t ctratt; + uint16_t oacs; + + memcpy(n->cse.acs, nvme_cse_acs_default, sizeof(n->cse.acs)); id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); @@ -8770,9 +8772,18 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); - id->oacs = - cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT | NVME_OACS_DBBUF | - NVME_OACS_DIRECTIVES); + + oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DBBUF | + NVME_OACS_DIRECTIVES; + + if (n->params.sriov_max_vfs) { + oacs |= NVME_OACS_VMS; + + n->cse.acs[NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP; + } + + id->oacs = cpu_to_le16(oacs); + id->cntrltype = 0x1; /* diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index e307e733e4..b86cad388f 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -584,6 +584,10 @@ typedef struct NvmeCtrl { uint64_t dbbuf_eis; bool dbbuf_enabled; + struct { + uint32_t acs[256]; + } cse; + struct { MemoryRegion mem; uint8_t *buf; diff --git a/include/block/nvme.h b/include/block/nvme.h index 975d321c5c..80fbcb420d 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1232,8 +1232,9 @@ enum NvmeIdCtrlOacs { NVME_OACS_SECURITY = 1 << 0, NVME_OACS_FORMAT = 1 << 1, NVME_OACS_FW = 1 << 2, - NVME_OACS_NS_MGMT = 1 << 3, + NVME_OACS_NMS = 1 << 3, NVME_OACS_DIRECTIVES = 1 << 5, + NVME_OACS_VMS = 1 << 7, NVME_OACS_DBBUF = 1 << 8, }; From 9cf6ec06592dea1973e66cd5cedf96fc59639047 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:04 +0100 Subject: [PATCH 1912/2892] hw/nvme: add knob for doorbell buffer config support Add a 'dbcs' knob to allow Doorbell Buffer Config command to be disabled. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 11 ++++++++--- hw/nvme/nvme.h | 1 + include/block/nvme.h | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 4ee8588ca9..1ad76da943 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -278,7 +278,6 @@ static const uint32_t nvme_cse_acs_default[256] = { [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, - [NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_DIRECTIVE_SEND] = NVME_CMD_EFF_CSUPP, @@ -8773,8 +8772,13 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); - oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DBBUF | - NVME_OACS_DIRECTIVES; + oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES; + + if (n->params.dbcs) { + oacs |= NVME_OACS_DBCS; + + n->cse.acs[NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP; + } if (n->params.sriov_max_vfs) { oacs |= NVME_OACS_VMS; @@ -9024,6 +9028,7 @@ static const Property nvme_props[] = { DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false), DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false), DEFINE_PROP_BOOL("ioeventfd", NvmeCtrl, params.ioeventfd, false), + DEFINE_PROP_BOOL("dbcs", NvmeCtrl, params.dbcs, true), DEFINE_PROP_UINT8("zoned.zasl", NvmeCtrl, params.zasl, 0), DEFINE_PROP_BOOL("zoned.auto_transition", NvmeCtrl, params.auto_transition_zones, true), diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index b86cad388f..b8d063a027 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -539,6 +539,7 @@ typedef struct NvmeParams { bool auto_transition_zones; bool legacy_cmb; bool ioeventfd; + bool dbcs; uint16_t sriov_max_vfs; uint16_t sriov_vq_flexible; uint16_t sriov_vi_flexible; diff --git a/include/block/nvme.h b/include/block/nvme.h index 80fbcb420d..63eb74460e 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1235,7 +1235,7 @@ enum NvmeIdCtrlOacs { NVME_OACS_NMS = 1 << 3, NVME_OACS_DIRECTIVES = 1 << 5, NVME_OACS_VMS = 1 << 7, - NVME_OACS_DBBUF = 1 << 8, + NVME_OACS_DBCS = 1 << 8, }; enum NvmeIdCtrlOncs { From b202fb549dc487c5611564e5d03286748586aa34 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:05 +0100 Subject: [PATCH 1913/2892] nvme: fix iocs status code values The status codes related to I/O Command Sets are in the wrong group. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 4 ++-- include/block/nvme.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 1ad76da943..2b73f60160 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -5681,7 +5681,7 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active) return nvme_c2h(n, (uint8_t *)&ns->id_ns, sizeof(NvmeIdNs), req); } - return NVME_INVALID_CMD_SET | NVME_DNR; + return NVME_INVALID_IOCS | NVME_DNR; } static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req, @@ -6647,7 +6647,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) case NVME_COMMAND_SET_PROFILE: if (dw11 & 0x1ff) { trace_pci_nvme_err_invalid_iocsci(dw11 & 0x1ff); - return NVME_CMD_SET_CMB_REJECTED | NVME_DNR; + return NVME_IOCS_COMBINATION_REJECTED | NVME_DNR; } break; case NVME_FDP_MODE: diff --git a/include/block/nvme.h b/include/block/nvme.h index 63eb74460e..aecfc9ce66 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -906,8 +906,6 @@ enum NvmeStatusCodes { NVME_SGL_DESCR_TYPE_INVALID = 0x0011, NVME_INVALID_USE_OF_CMB = 0x0012, NVME_INVALID_PRP_OFFSET = 0x0013, - NVME_CMD_SET_CMB_REJECTED = 0x002b, - NVME_INVALID_CMD_SET = 0x002c, NVME_FDP_DISABLED = 0x0029, NVME_INVALID_PHID_LIST = 0x002a, NVME_LBA_RANGE = 0x0080, @@ -940,6 +938,8 @@ enum NvmeStatusCodes { NVME_INVALID_SEC_CTRL_STATE = 0x0120, NVME_INVALID_NUM_RESOURCES = 0x0121, NVME_INVALID_RESOURCE_ID = 0x0122, + NVME_IOCS_COMBINATION_REJECTED = 0x012b, + NVME_INVALID_IOCS = 0x012c, NVME_CONFLICTING_ATTRS = 0x0180, NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, From d96a32de3fd0880a9340590fd279288e42e983c1 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:06 +0100 Subject: [PATCH 1914/2892] hw/nvme: be compliant wrt. dsm processing limits The specification states that, > The controller shall set all three processing limit fields (i.e., the > DMRL, DMRSL and DMSL fields) to non-zero values or shall clear all > three processing limit fields to 0h. So, set the DMRL and DMSL fields in addition to DMRSL. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 24 +++++++++++++++--------- include/block/nvme.h | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 2b73f60160..86e1c48fab 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -5639,7 +5639,9 @@ static uint16_t nvme_identify_ctrl_csi(NvmeCtrl *n, NvmeRequest *req) switch (c->csi) { case NVME_CSI_NVM: id_nvm->vsl = n->params.vsl; + id_nvm->dmrl = NVME_ID_CTRL_NVM_DMRL_MAX; id_nvm->dmrsl = cpu_to_le32(n->dmrsl); + id_nvm->dmsl = NVME_ID_CTRL_NVM_DMRL_MAX * n->dmrsl; break; case NVME_CSI_ZONED: @@ -6696,18 +6698,23 @@ static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req) return NVME_NO_COMPLETE; } -static void nvme_update_dmrsl(NvmeCtrl *n) +static void nvme_update_dsm_limits(NvmeCtrl *n, NvmeNamespace *ns) { - int nsid; + if (ns) { + n->dmrsl = + MIN_NON_ZERO(n->dmrsl, BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); - for (nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { - NvmeNamespace *ns = nvme_ns(n, nsid); + return; + } + + for (uint32_t nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { + ns = nvme_ns(n, nsid); if (!ns) { continue; } - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); + n->dmrsl = + MIN_NON_ZERO(n->dmrsl, BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); } } @@ -6795,7 +6802,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) ctrl->namespaces[nsid] = NULL; ns->attached--; - nvme_update_dmrsl(ctrl); + nvme_update_dsm_limits(ctrl, NULL); break; @@ -8902,8 +8909,7 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns) n->namespaces[nsid] = ns; ns->attached++; - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); + nvme_update_dsm_limits(n, ns); } static void nvme_realize(PCIDevice *pci_dev, Error **errp) diff --git a/include/block/nvme.h b/include/block/nvme.h index aecfc9ce66..763b2b2f0e 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1207,6 +1207,8 @@ typedef struct NvmeIdCtrlZoned { uint8_t rsvd1[4095]; } NvmeIdCtrlZoned; +#define NVME_ID_CTRL_NVM_DMRL_MAX 255 + typedef struct NvmeIdCtrlNvm { uint8_t vsl; uint8_t wzsl; From 8a420dd109b9e4e2244cfa32bc92829093268b3e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 14 Nov 2024 09:05:38 +0100 Subject: [PATCH 1915/2892] rust: add module to convert between success/-errno and io::Result It is a common convention in QEMU to return a positive value in case of success, and a negated errno value in case of error. Unfortunately, using errno portably in Rust is a bit complicated; on Unix the errno values are supported natively by io::Error, but on Windows they are not; so, use the libc crate. This is a set of utility functions that are used by both chardev and block layer bindings. Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + rust/qemu-api/meson.build | 4 + rust/qemu-api/src/assertions.rs | 28 +++ rust/qemu-api/src/errno.rs | 345 ++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/prelude.rs | 2 + 6 files changed, 381 insertions(+) create mode 100644 rust/qemu-api/src/errno.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 90958e5a30..c75dccdbb7 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -179,6 +179,7 @@ module status ``callbacks`` complete ``cell`` stable ``c_str`` complete +``errno`` complete ``irq`` complete ``memory`` stable ``module`` complete diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2e9c1078b9..bcf1cf780f 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,6 +2,8 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() +libc_dep = dependency('libc-0.2-rs') + # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] @@ -22,6 +24,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/chardev.rs', 'src/c_str.rs', + 'src/errno.rs', 'src/irq.rs', 'src/memory.rs', 'src/module.rs', @@ -39,6 +42,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, + dependencies: libc_dep, ) rust.test('rust-qemu-api-tests', _qemu_api_rs, diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index fa1a18de6f..104dec3977 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -92,3 +92,31 @@ macro_rules! assert_field_type { }; }; } + +/// Assert that an expression matches a pattern. This can also be +/// useful to compare enums that do not implement `Eq`. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_match; +/// // JoinHandle does not implement `Eq`, therefore the result +/// // does not either. +/// let result: Result, u32> = Err(42); +/// assert_match!(result, Err(42)); +/// ``` +#[macro_export] +macro_rules! assert_match { + ($a:expr, $b:pat) => { + assert!( + match $a { + $b => true, + _ => false, + }, + "{} = {:?} does not match {}", + stringify!($a), + $a, + stringify!($b) + ); + }; +} diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs new file mode 100644 index 0000000000..18d101448b --- /dev/null +++ b/rust/qemu-api/src/errno.rs @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Utility functions to convert `errno` to and from +//! [`io::Error`]/[`io::Result`] +//! +//! QEMU C functions often have a "positive success/negative `errno`" calling +//! convention. This module provides functions to portably convert an integer +//! into an [`io::Result`] and back. + +use std::{convert::TryFrom, io, io::ErrorKind}; + +/// An `errno` value that can be converted into an [`io::Error`] +pub struct Errno(pub u16); + +// On Unix, from_raw_os_error takes an errno value and OS errors +// are printed using strerror. On Windows however it takes a +// GetLastError() value; therefore we need to convert errno values +// into io::Error by hand. This is the same mapping that the +// standard library uses to retrieve the kind of OS errors +// (`std::sys::pal::unix::decode_error_kind`). +impl From for ErrorKind { + fn from(value: Errno) -> ErrorKind { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => Other, + } + } +} + +// This is used on Windows for all io::Errors, but also on Unix if the +// io::Error does not have a raw OS error. This is the reversed +// mapping of the above; EIO is returned for unknown ErrorKinds. +impl From for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + TimedOut => libc::ETIMEDOUT, + _ => libc::EIO, + }; + Errno(errno as u16) + } +} + +impl From for io::Error { + #[cfg(unix)] + fn from(value: Errno) -> io::Error { + let Errno(errno) = value; + io::Error::from_raw_os_error(errno.into()) + } + + #[cfg(windows)] + fn from(value: Errno) -> io::Error { + let error_kind: ErrorKind = value.into(); + error_kind.into() + } +} + +impl From for Errno { + fn from(value: io::Error) -> Errno { + if cfg!(unix) { + if let Some(errno) = value.raw_os_error() { + return Errno(u16::try_from(errno).unwrap()); + } + } + value.kind().into() + } +} + +/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] +/// for the "right" set of types. +mod traits { + use super::Errno; + + /// A signed type that can be converted into an + /// [`io::Result`](std::io::Result) + pub trait GetErrno { + /// Unsigned variant of `Self`, used as the type for the `Ok` case. + type Out; + + /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative + fn into_errno_result(self) -> Result; + } + + /// A type that can be taken out of an [`io::Result`](std::io::Result) and + /// converted into "positive success/negative `errno`" convention. + pub trait MergeErrno { + /// Signed variant of `Self`, used as the return type of + /// [`into_neg_errno`](super::into_neg_errno). + type Out: From + std::ops::Neg; + + /// Return `self`, asserting that it is in range + fn map_ok(self) -> Self::Out; + } + + macro_rules! get_errno { + ($t:ty, $out:ty) => { + impl GetErrno for $t { + type Out = $out; + fn into_errno_result(self) -> Result { + match self { + 0.. => Ok(self as $out), + -65535..=-1 => Err(Errno(-self as u16)), + _ => panic!("{self} is not a negative errno"), + } + } + } + }; + } + + get_errno!(i32, u32); + get_errno!(i64, u64); + get_errno!(isize, usize); + + macro_rules! merge_errno { + ($t:ty, $out:ty) => { + impl MergeErrno for $t { + type Out = $out; + fn map_ok(self) -> Self::Out { + self.try_into().unwrap() + } + } + }; + } + + merge_errno!(u8, i32); + merge_errno!(u16, i32); + merge_errno!(u32, i32); + merge_errno!(u64, i64); + + impl MergeErrno for () { + type Out = i32; + fn map_ok(self) -> i32 { + 0 + } + } +} + +use traits::{GetErrno, MergeErrno}; + +/// Convert an integer value into a [`io::Result`]. +/// +/// Positive values are turned into an `Ok` result; negative values +/// are interpreted as negated `errno` and turned into an `Err`. +/// +/// ``` +/// # use qemu_api::errno::into_io_result; +/// # use std::io::ErrorKind; +/// let ok = into_io_result(1i32).unwrap(); +/// assert_eq!(ok, 1u32); +/// +/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM +/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); +/// ``` +/// +/// # Panics +/// +/// Since the result is an unsigned integer, negative values must +/// be close to 0; values that are too far away are considered +/// likely overflows and will panic: +/// +/// ```should_panic +/// # use qemu_api::errno::into_io_result; +/// # #[allow(dead_code)] +/// let err = into_io_result(-0x1234_5678i32); // panic +/// ``` +pub fn into_io_result(value: T) -> io::Result { + value.into_errno_result().map_err(Into::into) +} + +/// Convert a [`Result`] into an integer value, using negative `errno` +/// values to report errors. +/// +/// ``` +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io::{self, ErrorKind}; +/// let ok: io::Result<()> = Ok(()); +/// assert_eq!(into_neg_errno(ok), 0); +/// +/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); +/// assert_eq!(into_neg_errno(err), -22); // -EINVAL +/// ``` +/// +/// Since this module also provides the ability to convert [`io::Error`] +/// to an `errno` value, [`io::Result`] is the most commonly used type +/// for the argument of this function: +/// +/// # Panics +/// +/// Since the result is a signed integer, integer `Ok` values must remain +/// positive: +/// +/// ```should_panic +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io; +/// let err: io::Result = Ok(0x8899_AABB); +/// into_neg_errno(err) // panic +/// # ; +/// ``` +pub fn into_neg_errno>(value: Result) -> T::Out { + match value { + Ok(x) => x.map_ok(), + Err(err) => -T::Out::from(err.into().0), + } +} + +#[cfg(test)] +mod tests { + use std::io::ErrorKind; + + use super::*; + use crate::assert_match; + + #[test] + pub fn test_from_u8() { + let ok: io::Result<_> = Ok(42u8); + assert_eq!(into_neg_errno(ok), 42); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_from_u16() { + let ok: io::Result<_> = Ok(1234u16); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i32() { + assert_match!(into_io_result(1234i32), Ok(1234)); + + let err = into_io_result(-1i32).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(1)); + assert_match!(err.kind(), ErrorKind::PermissionDenied); + } + + #[test] + pub fn test_from_u32() { + let ok: io::Result<_> = Ok(1234u32); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i64() { + assert_match!(into_io_result(1234i64), Ok(1234)); + + let err = into_io_result(-22i64).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(22)); + assert_match!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + pub fn test_from_u64() { + let ok: io::Result<_> = Ok(1234u64); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); + assert_eq!(into_neg_errno(err), -22); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); + assert_eq!(into_neg_errno(os_err), -6); + } + } + + #[test] + pub fn test_isize() { + assert_match!(into_io_result(1234isize), Ok(1234)); + + let err = into_io_result(-4isize).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(4)); + assert_match!(err.kind(), ErrorKind::Interrupted); + } + + #[test] + pub fn test_from_unit() { + let ok: io::Result<_> = Ok(()); + assert_eq!(into_neg_errno(ok), 0); + + let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); + assert_eq!(into_neg_errno(err), -12); + + if cfg!(unix) { + let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); + assert_eq!(into_neg_errno(os_err), -2); + } + } +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index ed1a8f9a2b..05f38b51d3 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -19,6 +19,7 @@ pub mod c_str; pub mod callbacks; pub mod cell; pub mod chardev; +pub mod errno; pub mod irq; pub mod memory; pub mod module; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index fbf0ee23e0..634acf37a8 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -9,6 +9,8 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; +pub use crate::errno; + pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; From 4cb7040d851cdd7b2622f83fd7d95a922225386b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:10:33 +0100 Subject: [PATCH 1916/2892] rust: tests: do not import bindings::* Similar to the devices, spell the exact set of C functions that are called directly. Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 92dbfb8a0c..03569e4a44 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -8,13 +8,14 @@ use std::{ }; use qemu_api::{ - bindings::*, + bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool}, c_str, cell::{self, BqlCell}, declare_properties, define_property, prelude::*, qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, ParentField}, + sysbus::SysBusDevice, vmstate::VMStateDescription, zeroable::Zeroable, }; From c48700e86d91004424e3a6496f194decb036dccb Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Feb 2025 16:08:35 +0800 Subject: [PATCH 1917/2892] rust: prefer importing std::ptr over core::ptr The std::ptr is same as core::ptr, but std has already been used in many cases and there's no need to choose non-std library. So, use std::ptr directly to make the used ptr library as consistent as possible. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250218080835.3341082-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/char/pl011/src/device_class.rs | 6 ++++-- rust/qemu-api/src/irq.rs | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index fe73771021..59a689fdcd 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,10 +2,10 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, os::raw::{c_int, c_void}, + ptr::{addr_of, addr_of_mut, NonNull}, }; use qemu_api::{ diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index dbef93f6cb..0b2076ddaa 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -2,8 +2,10 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::NonNull; -use std::os::raw::{c_int, c_void}; +use std::{ + os::raw::{c_int, c_void}, + ptr::NonNull, +}; use qemu_api::{ bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index d1c9dc96ef..34c19263c2 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -4,8 +4,7 @@ //! Bindings for interrupt sources -use core::ptr; -use std::{ffi::CStr, marker::PhantomData, os::raw::c_int}; +use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr}; use crate::{ bindings::{self, qemu_set_irq}, From 7a2e40866cf45a016858c73b9a5699b72be8ce38 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 17:18:00 +0100 Subject: [PATCH 1918/2892] docs: rust: fix typos Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index c75dccdbb7..d68701c9c8 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -294,7 +294,7 @@ to a Rust mutable reference, and use a shared reference instead. Rust code will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which enforce that locking rules for the "Big QEMU Lock" are respected. These cell types are also known to the ``vmstate`` crate, which is able to "look inside" -them when building an in-memory representation of a ``struct``s layout. +them when building an in-memory representation of a ``struct``'s layout. Note that the same is not true of a ``RefCell`` or ``Mutex``. In the future, similar cell types might also be provided for ``AioContext``-based @@ -350,7 +350,7 @@ Writing procedural macros ''''''''''''''''''''''''' By conventions, procedural macros are split in two functions, one -returning ``Result` with the body of +returning ``Result`` with the body of the procedural macro, and the second returning ``proc_macro::TokenStream`` which is the actual procedural macro. The former's name is the same as the latter with the ``_or_error`` suffix. The code for the latter is more From 29b9a66f9186f028ec46b58c9914d2da68c25c2c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 13:25:05 +0100 Subject: [PATCH 1919/2892] docs: rust: update description of crates Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index d68701c9c8..5d8aa3a45b 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -139,16 +139,22 @@ anymore. Writing Rust code in QEMU ------------------------- -Right now QEMU includes three crates: +QEMU includes four crates: * ``qemu_api`` for bindings to C code and useful functionality * ``qemu_api_macros`` defines several procedural macros that are useful when writing C code -* ``pl011`` (under ``rust/hw/char/pl011``) is the sample device that is being - used to further develop ``qemu_api`` and ``qemu_api_macros``. It is a functional - replacement for the ``hw/char/pl011.c`` file. +* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``) + are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are + used to further develop them. These two crates are functional\ [#issues]_ replacements + for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files. + +.. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` + as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of + commit f32352ff9e. Both are lacking tracing functionality; ``hpet`` + is also lacking support for migration. This section explains how to work with them. From 5384d92e22577306408cefff887bc5a4b154f470 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 17 Feb 2025 11:48:49 +0100 Subject: [PATCH 1920/2892] stub: Remove monitor-fd.c Both monitor-fd.c and monitor-internal.c contain a stub for monitor_get_fd(), which causes a duplicate symbol linker error when linking rust-qemu-api-integration. Use monitor-internal.c instead of monitor-fd.c and remove the latter. Reported-by: Zhao Liu Suggested-by: Zhao Liu Fixes: fccb744f41c6 ("gdbstub: Try unlinking the unix socket before binding") Signed-off-by: Ilya Leoshkevich Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250217104900.230122-1-iii@linux.ibm.com Signed-off-by: Paolo Bonzini --- stubs/meson.build | 2 +- stubs/monitor-fd.c | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 stubs/monitor-fd.c diff --git a/stubs/meson.build b/stubs/meson.build index b0fee37e05..63392f5e78 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -62,7 +62,7 @@ if have_user stub_ss.add(files('qdev.c')) endif - stub_ss.add(files('monitor-fd.c')) + stub_ss.add(files('monitor-internal.c')) endif if have_system diff --git a/stubs/monitor-fd.c b/stubs/monitor-fd.c deleted file mode 100644 index 9bb6749885..0000000000 --- a/stubs/monitor-fd.c +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "qemu/osdep.h" -#include "monitor/monitor.h" - -int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) -{ - abort(); -} From 6debfb2cb1795427d2dc6a741c7430a233c76695 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Feb 2025 13:08:12 +0100 Subject: [PATCH 1921/2892] physmem: replace assertion with error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible to start QEMU with a confidential-guest-support object even in TCG mode. While there is already a check in qemu_machine_creation_done: if (machine->cgs && !machine->cgs->ready) { error_setg(errp, "accelerator does not support confidential guest %s", object_get_typename(OBJECT(machine->cgs))); exit(1); } the creation of RAMBlocks happens earlier, in qemu_init_board(), if the command line does not override the default memory backend with -M memdev. Then the RAMBlock will try to use guest_memfd (because machine_require_guest_memfd correctly returns true; at least correctly according to the current implementation) and trigger the assertion failure for kvm_enabled(). This happend with a command line as simple as the following: qemu-system-x86_64 -m 512 -nographic -object sev-snp-guest,reduced-phys-bits=48,id=sev0 \ -M q35,kernel-irqchip=split,confidential-guest-support=sev0 qemu-system-x86_64: ../system/physmem.c:1871: ram_block_add: Assertion `kvm_enabled()' failed. Cc: Xiaoyao Li Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Reviewed-by: David Hildenbrand Reviewed-by: Pankaj Gupta Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250217120812.396522-1-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- system/physmem.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index 67bdf631e6..eff8b55c2d 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1882,7 +1882,11 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) if (new_block->flags & RAM_GUEST_MEMFD) { int ret; - assert(kvm_enabled()); + if (!kvm_enabled()) { + error_setg(errp, "cannot set up private guest memory for %s: KVM required", + object_get_typename(OBJECT(current_machine->cgs))); + goto out_free; + } assert(new_block->guest_memfd < 0); ret = ram_block_discard_require(true); From ae3a420fea8bfc545a4ca4b899d2fe6a3031aefa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 19 Feb 2025 11:18:28 +0100 Subject: [PATCH 1922/2892] pvg: do not enable it on cross-architecture targets PVG is not cross-architecture; the PVG guest drivers with x86-64 macOS do not give useful results with the aarch64 macOS host PVG framework, and vice versa. To express this repurpose CONFIG_MAC_PVG, making it true only if the target has the same architecture as the host. Furthermore, remove apple-gfx.m unless one of the devices is actually present. Signed-off-by: Paolo Bonzini --- Kconfig.host | 3 +++ hw/display/Kconfig | 4 ---- hw/display/meson.build | 9 +++------ meson.build | 6 ++++++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Kconfig.host b/Kconfig.host index 842cbe0d6c..933425c74b 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -61,3 +61,6 @@ config HV_BALLOON_POSSIBLE config HAVE_RUST bool + +config MAC_PVG + bool diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 2b53dfd7d2..1e95ab28ef 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -141,10 +141,6 @@ config XLNX_DISPLAYPORT config DM163 bool -config MAC_PVG - bool - default y - config MAC_PVG_MMIO bool depends on MAC_PVG && AARCH64 diff --git a/hw/display/meson.build b/hw/display/meson.build index 94f4f05d36..b9bdf21910 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -61,12 +61,9 @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) -if host_os == 'darwin' - system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) - system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal]) - if cpu == 'aarch64' - system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal]) - endif +if pvg.found() + system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx.m', 'apple-gfx-pci.m'), pvg, metal]) + system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m'), pvg, metal]) endif if config_all_devices.has_key('CONFIG_VIRTIO_GPU') diff --git a/meson.build b/meson.build index 0ee79c664d..ad2c6b6193 100644 --- a/meson.build +++ b/meson.build @@ -3367,6 +3367,12 @@ foreach target : target_dirs target_kconfig += 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y' target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN'] + # PVG is not cross-architecture. Use accelerator_targets as a proxy to + # figure out which target can support PVG on this host + if pvg.found() and target in accelerator_targets.get('CONFIG_HVF', []) + target_kconfig += 'CONFIG_MAC_PVG=y' + endif + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( From d50ea7f0e6fd2b0631abb61d213a396e3df32d7e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Feb 2025 14:20:27 +0100 Subject: [PATCH 1923/2892] pvg: add option to configure it out ... and also to require it (--enable-pvg). While at it, unify the dependency() call for pvg and metal, which simplifies the logic a bit. Note that all other Apple frameworks are either required or always-present, therefore do not add them to the summary in the same way as PVG. Signed-off-by: Paolo Bonzini --- hw/display/meson.build | 6 ++---- meson.build | 8 +++++--- meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/hw/display/meson.build b/hw/display/meson.build index b9bdf21910..90e6c041bd 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -61,10 +61,8 @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) -if pvg.found() - system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx.m', 'apple-gfx-pci.m'), pvg, metal]) - system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m'), pvg, metal]) -endif +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_PCI'], if_true: [files('apple-gfx.m', 'apple-gfx-pci.m')]) +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_MMIO'], if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m')]) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() diff --git a/meson.build b/meson.build index ad2c6b6193..0a2c61d2bf 100644 --- a/meson.build +++ b/meson.build @@ -821,7 +821,6 @@ version_res = [] coref = [] iokit = [] pvg = not_found -metal = [] emulator_link_args = [] midl = not_found widl = not_found @@ -843,8 +842,8 @@ elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' - pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics') - metal = dependency('appleframeworks', modules: 'Metal') + pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'], + required: get_option('pvg')) elif host_os == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), @@ -4846,6 +4845,9 @@ summary_info += {'libdw': libdw} if host_os == 'freebsd' summary_info += {'libinotify-kqueue': inotify} endif +if host_os == 'darwin' + summary_info += {'ParavirtualizedGraphics support': pvg} +endif summary(summary_info, bool_yn: true, section: 'Dependencies') if host_arch == 'unknown' diff --git a/meson_options.txt b/meson_options.txt index 5eeaf3eee5..59d973bca0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -198,6 +198,8 @@ option('lzfse', type : 'feature', value : 'auto', description: 'lzfse support for DMG images') option('lzo', type : 'feature', value : 'auto', description: 'lzo compression support') +option('pvg', type: 'feature', value: 'auto', + description: 'macOS paravirtualized graphics support') option('rbd', type : 'feature', value : 'auto', description: 'Ceph block device driver') option('opengl', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index a8066aab03..3e8e00852b 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -168,6 +168,7 @@ meson_options_help() { printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' printf "%s\n" ' png PNG support with libpng' + printf "%s\n" ' pvg macOS paravirtualized graphics support' printf "%s\n" ' qatzip QATzip compression support' printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' @@ -436,6 +437,8 @@ _meson_option_parse() { --enable-png) printf "%s" -Dpng=enabled ;; --disable-png) printf "%s" -Dpng=disabled ;; --prefix=*) quote_sh "-Dprefix=$2" ;; + --enable-pvg) printf "%s" -Dpvg=enabled ;; + --disable-pvg) printf "%s" -Dpvg=disabled ;; --enable-qatzip) printf "%s" -Dqatzip=enabled ;; --disable-qatzip) printf "%s" -Dqatzip=disabled ;; --enable-qcow1) printf "%s" -Dqcow1=enabled ;; From 2540917285872ab08f3ce66990983edd19ef4ec0 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 21 Feb 2025 00:36:09 -0800 Subject: [PATCH 1924/2892] target/i386/hvf: fix a typo in a type name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prefix x68 is wrong. Change it to x86. Signed-off-by: Wei Liu Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/1740126987-8483-2-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 2 +- target/i386/hvf/x86.c | 4 ++-- target/i386/hvf/x86.h | 8 ++++---- target/i386/hvf/x86_descr.c | 8 ++++---- target/i386/hvf/x86_descr.h | 6 +++--- target/i386/hvf/x86_task.c | 22 +++++++++++----------- target/i386/hvf/x86_task.h | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index ca08f0753f..353549fa77 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -674,7 +674,7 @@ int hvf_vcpu_exec(CPUState *cpu) } case EXIT_REASON_TASK_SWITCH: { uint64_t vinfo = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO); - x68_segment_selector sel = {.sel = exit_qual & 0xffff}; + x86_segment_selector sel = {.sel = exit_qual & 0xffff}; vmx_handle_task_switch(cpu, sel, (exit_qual >> 30) & 0x3, vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo & VMCS_INTR_T_MASK); diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index 80e36136d0..a0ede13886 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -48,7 +48,7 @@ bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel) + x86_segment_selector sel) { target_ulong base; uint32_t limit; @@ -78,7 +78,7 @@ bool x86_read_segment_descriptor(CPUState *cpu, bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel) + x86_segment_selector sel) { target_ulong base; uint32_t limit; diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 3570f29aa9..063cd0b83e 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -183,7 +183,7 @@ static inline uint32_t x86_call_gate_offset(x86_call_gate *gate) #define GDT_SEL 0 #define LDT_SEL 1 -typedef struct x68_segment_selector { +typedef struct x86_segment_selector { union { uint16_t sel; struct { @@ -192,7 +192,7 @@ typedef struct x68_segment_selector { uint16_t index:13; }; }; -} __attribute__ ((__packed__)) x68_segment_selector; +} __attribute__ ((__packed__)) x86_segment_selector; /* useful register access macros */ #define x86_reg(cpu, reg) ((x86_register *) &cpu->regs[reg]) @@ -250,10 +250,10 @@ typedef struct x68_segment_selector { /* deal with GDT/LDT descriptors in memory */ bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_segment_selector sel); bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_segment_selector sel); bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc, int gate); diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c index f33836d6cb..7b599c9037 100644 --- a/target/i386/hvf/x86_descr.c +++ b/target/i386/hvf/x86_descr.c @@ -60,14 +60,14 @@ uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg) return rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); } -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) { - x68_segment_selector sel; + x86_segment_selector sel; sel.sel = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector); return sel; } -void vmx_write_segment_selector(CPUState *cpu, x68_segment_selector selector, X86Seg seg) +void vmx_write_segment_selector(CPUState *cpu, x86_segment_selector selector, X86Seg seg) { wvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector, selector.sel); } @@ -90,7 +90,7 @@ void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Se wvmcs(cpu->accel->fd, sf->ar_bytes, desc->ar); } -void x86_segment_descriptor_to_vmx(CPUState *cpu, x68_segment_selector selector, +void x86_segment_descriptor_to_vmx(CPUState *cpu, x86_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc) { diff --git a/target/i386/hvf/x86_descr.h b/target/i386/hvf/x86_descr.h index 9f06014b56..ce5de98349 100644 --- a/target/i386/hvf/x86_descr.h +++ b/target/i386/hvf/x86_descr.h @@ -34,10 +34,10 @@ void vmx_read_segment_descriptor(CPUState *cpu, void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, enum X86Seg seg); -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, enum X86Seg seg); void vmx_write_segment_selector(CPUState *cpu, - x68_segment_selector selector, + x86_segment_selector selector, enum X86Seg seg); uint64_t vmx_read_segment_base(CPUState *cpu, enum X86Seg seg); @@ -45,7 +45,7 @@ void vmx_write_segment_base(CPUState *cpu, enum X86Seg seg, uint64_t base); void x86_segment_descriptor_to_vmx(CPUState *cpu, - x68_segment_selector selector, + x86_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc); diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index bcd844cff6..287fe11cf7 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -76,16 +76,16 @@ static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) RSI(env) = tss->esi; RDI(env) = tss->edi; - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ldt}}, R_LDTR); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->es}}, R_ES); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->cs}}, R_CS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ss}}, R_SS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ds}}, R_DS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->fs}}, R_FS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->gs}}, R_GS); } -static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, +static int task_switch_32(CPUState *cpu, x86_segment_selector tss_sel, x86_segment_selector old_tss_sel, uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) { struct x86_tss_segment32 tss_seg; @@ -108,7 +108,7 @@ static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segme return 0; } -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) { uint64_t rip = rreg(cpu->accel->fd, HV_X86_RIP); if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && @@ -122,7 +122,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea load_regs(cpu); struct x86_segment_descriptor curr_tss_desc, next_tss_desc; - x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); + x86_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); uint32_t desc_limit; struct x86_call_gate task_gate_desc; @@ -140,7 +140,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea x86_read_call_gate(cpu, &task_gate_desc, gate); dpl = task_gate_desc.dpl; - x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); + x86_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); if (tss_sel.rpl > dpl || cs.rpl > dpl) ;//DPRINTF("emulate_gp"); } diff --git a/target/i386/hvf/x86_task.h b/target/i386/hvf/x86_task.h index 4eaa61a7de..b9afac6a47 100644 --- a/target/i386/hvf/x86_task.h +++ b/target/i386/hvf/x86_task.h @@ -15,6 +15,6 @@ #ifndef HVF_X86_TASK_H #define HVF_X86_TASK_H -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type); #endif From bc4fa8c3c9b5e2ad945617b667362b71b13495ad Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 21 Feb 2025 00:36:10 -0800 Subject: [PATCH 1925/2892] target/i386/hvf: fix the declaration of hvf_handle_io There is a conflicting declaration for hvf_handle_io in x86_emu.c. The type of the first argument is wrong. There has never been a problem because the first argument is not used in hvf_handle_io. That being said, the code shouldn't contain such an error. Use the proper declaration from hvf-i386.h. Take the chance to change the first argument's type to be CPUState. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1740126987-8483-3-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf-i386.h | 2 +- target/i386/hvf/hvf.c | 6 +++--- target/i386/hvf/x86_emu.c | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index e99c02cd4b..046b681d13 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -18,7 +18,7 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); -void hvf_handle_io(CPUArchState *, uint16_t, void *, int, int, int); +void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 353549fa77..1ecb6993ba 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -103,7 +103,7 @@ static void update_apic_tpr(CPUState *cpu) #define VECTORING_INFO_VECTOR_MASK 0xff -void hvf_handle_io(CPUArchState *env, uint16_t port, void *buffer, +void hvf_handle_io(CPUState *env, uint16_t port, void *buffer, int direction, int size, int count) { int i; @@ -536,7 +536,7 @@ int hvf_vcpu_exec(CPUState *cpu) if (!string && in) { uint64_t val = 0; load_regs(cpu); - hvf_handle_io(env, port, &val, 0, size, 1); + hvf_handle_io(env_cpu(env), port, &val, 0, size, 1); if (size == 1) { AL(env) = val; } else if (size == 2) { @@ -551,7 +551,7 @@ int hvf_vcpu_exec(CPUState *cpu) break; } else if (!string && !in) { RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX); - hvf_handle_io(env, port, &RAX(env), 1, size, 1); + hvf_handle_io(env_cpu(env), port, &RAX(env), 1, size, 1); macvm_set_rip(cpu, rip + ins_len); break; } diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 69c61c9c07..2c7da10c1d 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -44,9 +44,7 @@ #include "x86_flags.h" #include "vmcs.h" #include "vmx.h" - -void hvf_handle_io(CPUState *cs, uint16_t port, void *data, - int direction, int size, uint32_t count); +#include "hvf-i386.h" #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ { \ From d54d3346b86d7c08b7fb2dac2d9a889854c7d3ba Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 21 Feb 2025 00:36:11 -0800 Subject: [PATCH 1926/2892] target/i386/hvf: use x86_segment in x86_decode.c Make the code to rely on the segment definition for checking cs.db. This allows removing HVF specific VMX related definition from the decoder. Introduce a function for retrieving the CS descriptor. No functional change intended. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1740126987-8483-4-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_decode.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index a4a28f113f..d6d5894e54 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -1893,6 +1893,16 @@ static void decode_prefix(CPUX86State *env, struct x86_decode *decode) } } +static struct x86_segment_descriptor get_cs_descriptor(CPUState *s) +{ + struct vmx_segment vmx_cs; + x86_segment_descriptor cs; + vmx_read_segment_descriptor(s, &vmx_cs, R_CS); + vmx_segment_to_x86_descriptor(s, &vmx_cs, &cs); + + return cs; +} + void set_addressing_size(CPUX86State *env, struct x86_decode *decode) { decode->addressing_size = -1; @@ -1904,10 +1914,9 @@ void set_addressing_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); + x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); /* check db */ - if ((cs.ar >> 14) & 1) { + if (cs.db) { if (decode->addr_size_override) { decode->addressing_size = 2; } else { @@ -1941,10 +1950,9 @@ void set_operand_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); + x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); /* check db */ - if ((cs.ar >> 14) & 1) { + if (cs.db) { if (decode->op_size_override) { decode->operand_size = 2; } else{ From dbccd48df0954069c12e13883ee8b2929a57ac6a Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 21 Feb 2025 00:36:14 -0800 Subject: [PATCH 1927/2892] target/i386/hvf: move and rename {load, store}_regs They contain HVF specific code. Move them to a better location and add "hvf_" prefix. Fix up all the call sites. No functional change. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1740126987-8483-7-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 71 +++++++++++++++++++++++++++++++------- target/i386/hvf/x86_emu.c | 46 ------------------------ target/i386/hvf/x86_emu.h | 3 -- target/i386/hvf/x86_task.c | 4 +-- target/i386/hvf/x86hvf.h | 3 ++ 5 files changed, 64 insertions(+), 63 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 1ecb6993ba..3ab3c0043d 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -61,6 +61,7 @@ #include "vmx.h" #include "x86.h" #include "x86_descr.h" +#include "x86_flags.h" #include "x86_mmu.h" #include "x86_decode.h" #include "x86_emu.h" @@ -434,6 +435,52 @@ static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } +void hvf_load_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); + RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); + RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); + RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); + RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); + RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); + RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); + RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); + for (i = 8; i < 16; i++) { + RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); + } + + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); + rflags_to_lflags(env); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); +} + +void hvf_store_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); + wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); + wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); + wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); + wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); + wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); + wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); + wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); + for (i = 8; i < 16; i++) { + wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); + } + + lflags_to_rflags(env); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + macvm_set_rip(cs, env->eip); +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -517,10 +564,10 @@ int hvf_vcpu_exec(CPUState *cpu) if (ept_emulation_fault(slot, gpa, exit_qual)) { struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } break; @@ -535,7 +582,7 @@ int hvf_vcpu_exec(CPUState *cpu) if (!string && in) { uint64_t val = 0; - load_regs(cpu); + hvf_load_regs(cpu); hvf_handle_io(env_cpu(env), port, &val, 0, size, 1); if (size == 1) { AL(env) = val; @@ -547,7 +594,7 @@ int hvf_vcpu_exec(CPUState *cpu) RAX(env) = (uint64_t)val; } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } else if (!string && !in) { RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX); @@ -557,11 +604,11 @@ int hvf_vcpu_exec(CPUState *cpu) } struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); assert(ins_len == decode.len); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } @@ -614,21 +661,21 @@ int hvf_vcpu_exec(CPUState *cpu) case EXIT_REASON_RDMSR: case EXIT_REASON_WRMSR: { - load_regs(cpu); + hvf_load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { simulate_rdmsr(env); } else { simulate_wrmsr(env); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_CR_ACCESS: { int cr; int reg; - load_regs(cpu); + hvf_load_regs(cpu); cr = exit_qual & 15; reg = (exit_qual >> 8) & 15; @@ -656,16 +703,16 @@ int hvf_vcpu_exec(CPUState *cpu) abort(); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_APIC_ACCESS: { /* TODO */ struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_TPR: { diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 2c7da10c1d..8b0d54d69a 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -1452,52 +1452,6 @@ static void init_cmd_handler(void) } } -void load_regs(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - int i = 0; - RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); - RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); - RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); - RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); - RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); - RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); - RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); - RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); - for (i = 8; i < 16; i++) { - RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); - } - - env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); - rflags_to_lflags(env); - env->eip = rreg(cs->accel->fd, HV_X86_RIP); -} - -void store_regs(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - int i = 0; - wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); - wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); - wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); - wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); - wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); - wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); - wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); - wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); - for (i = 8; i < 16; i++) { - wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); - } - - lflags_to_rflags(env); - wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); - macvm_set_rip(cs, env->eip); -} - bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { /*if (hvf_vcpu_id(cs)) diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 8bd97608c4..cd953849c9 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -26,9 +26,6 @@ void init_emu(void); bool exec_instruction(CPUX86State *env, struct x86_decode *ins); -void load_regs(CPUState *cpu); -void store_regs(CPUState *cpu); - void simulate_rdmsr(CPUX86State *env); void simulate_wrmsr(CPUX86State *env); diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 287fe11cf7..161217991f 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -119,7 +119,7 @@ void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int rea return; } - load_regs(cpu); + hvf_load_regs(cpu); struct x86_segment_descriptor curr_tss_desc, next_tss_desc; x86_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); @@ -178,7 +178,7 @@ void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int rea x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); - store_regs(cpu); + hvf_store_regs(cpu); hv_vcpu_invalidate_tlb(cpu->accel->fd); } diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index 423a89b6ad..8c46ce8ad0 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -31,4 +31,7 @@ void hvf_get_xsave(CPUState *cs); void hvf_get_msrs(CPUState *cs); void vmx_clear_int_window_exiting(CPUState *cs); void vmx_update_tpr(CPUState *cs); + +void hvf_load_regs(CPUState *cpu); +void hvf_store_regs(CPUState *cpu); #endif From 99e5aaf9afeed3e0437f6dbc7672e3028d2b2f4b Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 21 Feb 2025 00:36:19 -0800 Subject: [PATCH 1928/2892] target/i386/hvf: move and rename simulate_{rdmsr, wrmsr} This requires making raise_exception non-static. That function needs to be renamed to avoid clashing with a function in TCG. Mostly code movement. No functional change. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1740126987-8483-12-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf-i386.h | 2 + target/i386/hvf/hvf.c | 216 +++++++++++++++++++++++++++++++++++- target/i386/hvf/x86_emu.c | 219 +------------------------------------ target/i386/hvf/x86_emu.h | 4 +- 4 files changed, 220 insertions(+), 221 deletions(-) diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index 046b681d13..044ad236ae 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -19,6 +19,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int); +void hvf_simulate_rdmsr(CPUX86State *env); +void hvf_simulate_wrmsr(CPUX86State *env); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 3ab3c0043d..9ba0e04ac7 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -481,6 +481,218 @@ void hvf_store_regs(CPUState *cs) macvm_set_rip(cs, env->eip); } +void hvf_simulate_rdmsr(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t msr = ECX(env); + uint64_t val = 0; + + switch (msr) { + case MSR_IA32_TSC: + val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); + break; + case MSR_IA32_APICBASE: + val = cpu_get_apic_base(cpu->apic_state); + break; + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_read(index, &val); + if (ret < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_IA32_UCODE_REV: + val = cpu->ucode_rev; + break; + case MSR_EFER: + val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); + break; + case MSR_FSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); + break; + case MSR_GSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); + break; + case MSR_KERNELGSBASE: + val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_IA32_MISC_ENABLE: + val = env->msr_ia32_misc_enable; + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; + break; + case MSR_MTRRfix64K_00000: + val = env->mtrr_fixed[0]; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; + break; + case MSR_MTRRdefType: + val = env->mtrr_deftype; + break; + case MSR_CORE_THREAD_COUNT: + val = cpu_x86_get_msr_core_thread_count(cpu); + break; + default: + /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ + val = 0; + break; + } + + RAX(env) = (uint32_t)val; + RDX(env) = (uint32_t)(val >> 32); +} + +void hvf_simulate_wrmsr(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t msr = ECX(env); + uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); + + switch (msr) { + case MSR_IA32_TSC: + break; + case MSR_IA32_APICBASE: { + int r; + + r = cpu_set_apic_base(cpu->apic_state, data); + if (r < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_write(index, data); + if (ret < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_FSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); + break; + case MSR_GSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); + break; + case MSR_KERNELGSBASE: + wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_EFER: + /*printf("new efer %llx\n", EFER(cs));*/ + wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); + if (data & MSR_EFER_NXE) { + hv_vcpu_invalidate_tlb(cs->accel->fd); + } + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; + break; + case MSR_MTRRfix64K_00000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; + break; + case MSR_MTRRdefType: + env->mtrr_deftype = data; + break; + default: + break; + } + + /* Related to support known hypervisor interface */ + /* if (g_hypervisor_iface) + g_hypervisor_iface->wrmsr_handler(cs, msr, data); + + printf("write msr %llx\n", RCX(cs));*/ +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -663,9 +875,9 @@ int hvf_vcpu_exec(CPUState *cpu) { hvf_load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - simulate_rdmsr(env); + hvf_simulate_rdmsr(env); } else { - simulate_wrmsr(env); + hvf_simulate_wrmsr(env); } env->eip += ins_len; hvf_store_regs(cpu); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 8b0d54d69a..df81594115 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -661,8 +661,7 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void raise_exception(CPUX86State *env, int exception_index, - int error_code) +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code) { env->exception_nr = exception_index; env->error_code = error_code; @@ -670,227 +669,15 @@ static void raise_exception(CPUX86State *env, int exception_index, env->exception_injected = 1; } -void simulate_rdmsr(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - uint32_t msr = ECX(env); - uint64_t val = 0; - - switch (msr) { - case MSR_IA32_TSC: - val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); - break; - case MSR_IA32_APICBASE: - val = cpu_get_apic_base(cpu->apic_state); - break; - case MSR_APIC_START ... MSR_APIC_END: { - int ret; - int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - - ret = apic_msr_read(index, &val); - if (ret < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_IA32_UCODE_REV: - val = cpu->ucode_rev; - break; - case MSR_EFER: - val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); - break; - case MSR_FSBASE: - val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); - break; - case MSR_GSBASE: - val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); - break; - case MSR_KERNELGSBASE: - val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_IA32_MISC_ENABLE: - val = env->msr_ia32_misc_enable; - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; - break; - case MSR_MTRRfix64K_00000: - val = env->mtrr_fixed[0]; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; - break; - case MSR_MTRRdefType: - val = env->mtrr_deftype; - break; - case MSR_CORE_THREAD_COUNT: - val = cpu_x86_get_msr_core_thread_count(cpu); - break; - default: - /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ - val = 0; - break; - } - - RAX(env) = (uint32_t)val; - RDX(env) = (uint32_t)(val >> 32); -} - static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_rdmsr(env); + hvf_simulate_rdmsr(env); env->eip += decode->len; } -void simulate_wrmsr(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - uint32_t msr = ECX(env); - uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); - - switch (msr) { - case MSR_IA32_TSC: - break; - case MSR_IA32_APICBASE: { - int r; - - r = cpu_set_apic_base(cpu->apic_state, data); - if (r < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_APIC_START ... MSR_APIC_END: { - int ret; - int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - - ret = apic_msr_write(index, data); - if (ret < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_FSBASE: - wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); - break; - case MSR_GSBASE: - wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); - break; - case MSR_KERNELGSBASE: - wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_EFER: - /*printf("new efer %llx\n", EFER(cs));*/ - wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); - if (data & MSR_EFER_NXE) { - hv_vcpu_invalidate_tlb(cs->accel->fd); - } - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; - break; - case MSR_MTRRfix64K_00000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; - break; - case MSR_MTRRdefType: - env->mtrr_deftype = data; - break; - default: - break; - } - - /* Related to support known hypervisor interface */ - /* if (g_hypervisor_iface) - g_hypervisor_iface->wrmsr_handler(cs, msr, data); - - printf("write msr %llx\n", RCX(cs));*/ -} - static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_wrmsr(env); + hvf_simulate_wrmsr(env); env->eip += decode->len; } diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index cd953849c9..bc0fc72c76 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -25,9 +25,7 @@ void init_emu(void); bool exec_instruction(CPUX86State *env, struct x86_decode *ins); - -void simulate_rdmsr(CPUX86State *env); -void simulate_wrmsr(CPUX86State *env); +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code); target_ulong read_reg(CPUX86State *env, int reg, int size); void write_reg(CPUX86State *env, int reg, target_ulong val, int size); From 646140dfebd42a9a9df8f15a22027b7efcb072cf Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 21 Feb 2025 00:36:23 -0800 Subject: [PATCH 1929/2892] target/i386/hvf: drop some dead code Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1740126987-8483-16-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_emu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index df81594115..ebba80a36b 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -1241,10 +1241,6 @@ static void init_cmd_handler(void) bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { - /*if (hvf_vcpu_id(cs)) - printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cs), env->eip, - decode_cmd_to_string(ins->cmd));*/ - if (!_cmd_handler[ins->cmd].handler) { printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip, ins->cmd, ins->opcode[0], From ac5699c5da51fa9d39bc964e81645953796f7ad1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Feb 2025 12:18:10 +0100 Subject: [PATCH 1930/2892] rust: add IsA bounds to QOM implementation traits Check that the right bounds are provided to the qom_isa! macro whenever the class is defined to implement a certain class. This removes the need to add IsA<> bounds together with the *Impl trait bounds. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 2 +- rust/qemu-api/src/qom.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 3a7aa4def6..c4dd26b582 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -86,7 +86,7 @@ unsafe extern "C" fn rust_resettable_exit_fn( } /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { +pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 3d5ab2d901..10ce359bec 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -37,6 +37,8 @@ //! * a trait for virtual method implementations, for example `DeviceImpl`. //! Child classes implement this trait to provide their own behavior for //! virtual methods. The trait's methods take `&self` to access instance data. +//! The traits have the appropriate specialization of `IsA<>` as a supertrait, +//! for example `IsA` for `DeviceImpl`. //! //! * an implementation of [`ClassInitImpl`], for example //! `ClassInitImpl`. This fills the vtable in the class struct; @@ -497,7 +499,7 @@ impl ObjectDeref for &mut T {} impl ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + ClassInitImpl { +pub trait ObjectImpl: ObjectType + ClassInitImpl + IsA { /// The parent of the type. This should match the first field of the /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. type ParentType: ObjectType; From 3212da0033530ae896d31d90d5e81a772fc37088 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Feb 2025 12:23:59 +0100 Subject: [PATCH 1931/2892] rust: add SysBusDeviceImpl The only function, right now, is to ensure that anything with a SysBusDeviceClass class is a SysBusDevice. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 5 ++++- rust/hw/timer/hpet/src/hpet.rs | 4 +++- rust/qemu-api/src/sysbus.rs | 8 +++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 59a689fdcd..bea9723aed 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -20,7 +20,7 @@ use qemu_api::{ prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, - sysbus::{SysBusDevice, SysBusDeviceClass}, + sysbus::{SysBusDevice, SysBusDeviceClass, SysBusDeviceImpl}, vmstate::VMStateDescription, }; @@ -176,6 +176,8 @@ impl ResettablePhasesImpl for PL011State { const HOLD: Option = Some(Self::reset_hold); } +impl SysBusDeviceImpl for PL011State {} + impl PL011Registers { pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { use RegisterOffset::*; @@ -746,3 +748,4 @@ impl ObjectImpl for PL011Luminary { impl DeviceImpl for PL011Luminary {} impl ResettablePhasesImpl for PL011Luminary {} +impl SysBusDeviceImpl for PL011Luminary {} diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 75ff5b3e8d..b4ffccf815 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -23,7 +23,7 @@ use qemu_api::{ qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, ObjectType, ParentField}, qom_isa, - sysbus::SysBusDevice, + sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL}, }; @@ -887,3 +887,5 @@ impl DeviceImpl for HPETState { impl ResettablePhasesImpl for HPETState { const HOLD: Option = Some(Self::reset_hold); } + +impl SysBusDeviceImpl for HPETState {} diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index fa36e12178..fee2e3d478 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -14,7 +14,7 @@ use crate::{ irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, - qdev::{DeviceClass, DeviceState}, + qdev::{DeviceClass, DeviceImpl, DeviceState}, qom::{ClassInitImpl, Owned}, }; @@ -25,10 +25,12 @@ unsafe impl ObjectType for SysBusDevice { } qom_isa!(SysBusDevice: DeviceState, Object); -// TODO: add SysBusDeviceImpl +// TODO: add virtual methods +pub trait SysBusDeviceImpl: DeviceImpl + IsA {} + impl ClassInitImpl for T where - T: ClassInitImpl, + T: SysBusDeviceImpl + ClassInitImpl, { fn class_init(sdc: &mut SysBusDeviceClass) { >::class_init(&mut sdc.parent_class); From 4551f342fed66af7f5e2b099fa06f4007db356e6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Feb 2025 11:49:32 +0100 Subject: [PATCH 1932/2892] rust: qom: add ObjectImpl::CLASS_INIT As shown in the PL011 device, the orphan rules required a manual implementation of ClassInitImpl for anything not in the qemu_api crate; this gets in the way of moving system emulation-specific code (including DeviceClass, which as a blanket ClassInitImpl implementation) into its own crate. Make ClassInitImpl optional, at the cost of having to specify the CLASS_INIT member by hand in every implementation of ObjectImpl. The next commits will get rid of it, replacing all the "impl ClassInitImpl for T" blocks with a generic class_init method on Class. Right now the definition is always the same, but do not provide a default as that will not be true once ClassInitImpl goes away. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 3 +++ rust/hw/timer/hpet/src/hpet.rs | 3 ++- rust/qemu-api/src/qom.rs | 14 +++++++++++--- rust/qemu-api/tests/tests.rs | 3 +++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bea9723aed..ead361b3f5 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -160,6 +160,7 @@ impl ObjectImpl for PL011State { const INSTANCE_INIT: Option = Some(Self::init); const INSTANCE_POST_INIT: Option = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = >::class_init; } impl DeviceImpl for PL011State { @@ -744,6 +745,8 @@ unsafe impl ObjectType for PL011Luminary { impl ObjectImpl for PL011Luminary { type ParentType = PL011State; + + const CLASS_INIT: fn(&mut Self::Class) = >::class_init; } impl DeviceImpl for PL011Luminary {} diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index b4ffccf815..e01b4b6706 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -21,7 +21,7 @@ use qemu_api::{ }, prelude::*, qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, ObjectType, ParentField}, + qom::{ClassInitImpl, ObjectImpl, ObjectType, ParentField}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL}, @@ -836,6 +836,7 @@ impl ObjectImpl for HPETState { const INSTANCE_INIT: Option = Some(Self::init); const INSTANCE_POST_INIT: Option = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = >::class_init; } // TODO: Make these properties user-configurable! diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 10ce359bec..d821ac25ac 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -180,7 +180,7 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); } -unsafe extern "C" fn rust_class_init>( +unsafe extern "C" fn rust_class_init( klass: *mut ObjectClass, _data: *mut c_void, ) { @@ -190,7 +190,7 @@ unsafe extern "C" fn rust_class_init>( // SAFETY: klass is a T::Class, since rust_class_init // is called from QOM core as the class_init function // for class T - T::class_init(unsafe { klass.as_mut() }) + ::CLASS_INIT(unsafe { klass.as_mut() }) } unsafe extern "C" fn drop_object(obj: *mut Object) { @@ -499,7 +499,7 @@ impl ObjectDeref for &mut T {} impl ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + ClassInitImpl + IsA { +pub trait ObjectImpl: ObjectType + IsA { /// The parent of the type. This should match the first field of the /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. type ParentType: ObjectType; @@ -552,6 +552,14 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl + IsA { // methods on ObjectClass const UNPARENT: Option = None; + + /// Store into the argument the virtual method implementations + /// for `Self`. On entry, the virtual method pointers are set to + /// the default values coming from the parent classes; the function + /// can change them to override virtual methods of a parent class. + /// + /// Usually defined as `::class_init`. + const CLASS_INIT: fn(&mut Self::Class); } /// Internal trait used to automatically fill in a class struct. diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 03569e4a44..9546e9d796 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -60,6 +60,7 @@ unsafe impl ObjectType for DummyState { impl ObjectImpl for DummyState { type ParentType = DeviceState; const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyClass) = >::class_init; } impl ResettablePhasesImpl for DummyState {} @@ -102,6 +103,8 @@ unsafe impl ObjectType for DummyChildState { impl ObjectImpl for DummyChildState { type ParentType = DummyState; const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyChildClass) = + >::class_init; } impl ResettablePhasesImpl for DummyChildState {} From 567c0c41a6700be72eb9e040ba0b8d7bf0cc5919 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 12:36:42 +0100 Subject: [PATCH 1933/2892] rust: pl011, qemu_api tests: do not use ClassInitImpl Outside the qemu_api crate, orphan rules make the usage of ClassInitImpl unwieldy. Now that it is optional, do not use it. For PL011Class, this makes it easier to provide a PL011Impl trait similar to the ones in the qemu_api crate. The device id consts are moved there. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 38 ++++++++++++++++---------------- rust/qemu-api/tests/tests.rs | 33 ++++++++++----------------- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index ead361b3f5..094049cb9e 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -50,11 +50,6 @@ impl std::ops::Index for DeviceId { } } -impl DeviceId { - const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); - const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); -} - // FIFOs use 32-bit indices instead of usize, for compatibility with // the migration stream produced by the C version of this device. #[repr(transparent)] @@ -143,16 +138,24 @@ pub struct PL011Class { device_id: DeviceId, } +trait PL011Impl: SysBusDeviceImpl + IsA { + const DEVICE_ID: DeviceId; +} + +impl PL011Class { + fn class_init(&mut self) { + self.device_id = T::DEVICE_ID; + >::class_init(&mut self.parent_class); + } +} + unsafe impl ObjectType for PL011State { type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } -impl ClassInitImpl for PL011State { - fn class_init(klass: &mut PL011Class) { - klass.device_id = DeviceId::ARM; - >::class_init(&mut klass.parent_class); - } +impl PL011Impl for PL011State { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); } impl ObjectImpl for PL011State { @@ -160,7 +163,7 @@ impl ObjectImpl for PL011State { const INSTANCE_INIT: Option = Some(Self::init); const INSTANCE_POST_INIT: Option = Some(Self::post_init); - const CLASS_INIT: fn(&mut Self::Class) = >::class_init; + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; } impl DeviceImpl for PL011State { @@ -729,13 +732,6 @@ pub struct PL011Luminary { parent_obj: ParentField, } -impl ClassInitImpl for PL011Luminary { - fn class_init(klass: &mut PL011Class) { - klass.device_id = DeviceId::LUMINARY; - >::class_init(&mut klass.parent_class); - } -} - qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); unsafe impl ObjectType for PL011Luminary { @@ -746,7 +742,11 @@ unsafe impl ObjectType for PL011Luminary { impl ObjectImpl for PL011Luminary { type ParentType = PL011State; - const CLASS_INIT: fn(&mut Self::Class) = >::class_init; + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; +} + +impl PL011Impl for PL011Luminary { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); } impl DeviceImpl for PL011Luminary {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 9546e9d796..93c5637bbc 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -13,7 +13,7 @@ use qemu_api::{ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, + qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, ParentField}, sysbus::SysBusDevice, vmstate::VMStateDescription, @@ -41,6 +41,12 @@ pub struct DummyClass { parent_class: ::Class, } +impl DummyClass { + pub fn class_init(self: &mut DummyClass) { + >::class_init(&mut self.parent_class); + } +} + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -60,7 +66,7 @@ unsafe impl ObjectType for DummyState { impl ObjectImpl for DummyState { type ParentType = DeviceState; const ABSTRACT: bool = false; - const CLASS_INIT: fn(&mut DummyClass) = >::class_init; + const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::; } impl ResettablePhasesImpl for DummyState {} @@ -74,14 +80,6 @@ impl DeviceImpl for DummyState { } } -// `impl ClassInitImpl for T` doesn't work since it violates -// orphan rule. -impl ClassInitImpl for DummyState { - fn class_init(klass: &mut DummyClass) { - >::class_init(&mut klass.parent_class); - } -} - #[derive(qemu_api_macros::offsets)] #[repr(C)] #[derive(qemu_api_macros::Object)] @@ -103,22 +101,15 @@ unsafe impl ObjectType for DummyChildState { impl ObjectImpl for DummyChildState { type ParentType = DummyState; const ABSTRACT: bool = false; - const CLASS_INIT: fn(&mut DummyChildClass) = - >::class_init; + const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::; } impl ResettablePhasesImpl for DummyChildState {} impl DeviceImpl for DummyChildState {} -impl ClassInitImpl for DummyChildState { - fn class_init(klass: &mut DummyClass) { - >::class_init(&mut klass.parent_class); - } -} - -impl ClassInitImpl for DummyChildState { - fn class_init(klass: &mut DummyChildClass) { - >::class_init(&mut klass.parent_class); +impl DummyChildClass { + pub fn class_init(self: &mut DummyChildClass) { + self.parent_class.class_init::(); } } From d556226d6965738e06a1d75faaf271b769bb5880 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 12:37:43 +0100 Subject: [PATCH 1934/2892] rust: qom: get rid of ClassInitImpl Complete the conversion from the ClassInitImpl trait to class_init() methods. This will provide more freedom to split the qemu_api crate in separate parts. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 +- rust/hw/timer/hpet/src/hpet.rs | 4 +- rust/qemu-api/src/qdev.rs | 38 ++++--- rust/qemu-api/src/qom.rs | 164 +++++++++++++------------------ rust/qemu-api/src/sysbus.rs | 15 ++- rust/qemu-api/tests/tests.rs | 4 +- 6 files changed, 101 insertions(+), 130 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 094049cb9e..d0857b470c 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -19,8 +19,8 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, - sysbus::{SysBusDevice, SysBusDeviceClass, SysBusDeviceImpl}, + qom::{ObjectImpl, Owned, ParentField}, + sysbus::{SysBusDevice, SysBusDeviceImpl}, vmstate::VMStateDescription, }; @@ -145,7 +145,7 @@ trait PL011Impl: SysBusDeviceImpl + IsA { impl PL011Class { fn class_init(&mut self) { self.device_id = T::DEVICE_ID; - >::class_init(&mut self.parent_class); + self.parent_class.class_init::(); } } diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index e01b4b6706..be27eb0eff 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -21,7 +21,7 @@ use qemu_api::{ }, prelude::*, qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, ObjectType, ParentField}, + qom::{ObjectImpl, ObjectType, ParentField}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL}, @@ -836,7 +836,7 @@ impl ObjectImpl for HPETState { const INSTANCE_INIT: Option = Some(Self::init); const INSTANCE_POST_INIT: Option = Some(Self::post_init); - const CLASS_INIT: fn(&mut Self::Class) = >::class_init; + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; } // TODO: Make these properties user-configurable! diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index c4dd26b582..c136457090 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -19,7 +19,7 @@ use crate::{ chardev::Chardev, irq::InterruptSource, prelude::*, - qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, + qom::{ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, }; @@ -113,7 +113,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { /// # Safety /// /// This function is only called through the QOM machinery and -/// used by the `ClassInitImpl` trait. +/// used by `DeviceClass::class_init`. /// We expect the FFI user of this function to pass a valid pointer that /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. @@ -127,43 +127,41 @@ unsafe impl InterfaceType for ResettableClass { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; } -impl ClassInitImpl for T -where - T: ResettablePhasesImpl, -{ - fn class_init(rc: &mut ResettableClass) { +impl ResettableClass { + /// Fill in the virtual methods of `ResettableClass` based on the + /// definitions in the `ResettablePhasesImpl` trait. + pub fn class_init(&mut self) { if ::ENTER.is_some() { - rc.phases.enter = Some(rust_resettable_enter_fn::); + self.phases.enter = Some(rust_resettable_enter_fn::); } if ::HOLD.is_some() { - rc.phases.hold = Some(rust_resettable_hold_fn::); + self.phases.hold = Some(rust_resettable_hold_fn::); } if ::EXIT.is_some() { - rc.phases.exit = Some(rust_resettable_exit_fn::); + self.phases.exit = Some(rust_resettable_exit_fn::); } } } -impl ClassInitImpl for T -where - T: ClassInitImpl + ClassInitImpl + DeviceImpl, -{ - fn class_init(dc: &mut DeviceClass) { +impl DeviceClass { + /// Fill in the virtual methods of `DeviceClass` based on the definitions in + /// the `DeviceImpl` trait. + pub fn class_init(&mut self) { if ::REALIZE.is_some() { - dc.realize = Some(rust_realize_fn::); + self.realize = Some(rust_realize_fn::); } if let Some(vmsd) = ::vmsd() { - dc.vmsd = vmsd; + self.vmsd = vmsd; } let prop = ::properties(); if !prop.is_empty() { unsafe { - bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); } } - ResettableClass::interface_init::(dc); - >::class_init(&mut dc.parent_class); + ResettableClass::cast::(self).class_init::(); + self.parent_class.class_init::(); } } diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index d821ac25ac..5488643a2f 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -40,11 +40,6 @@ //! The traits have the appropriate specialization of `IsA<>` as a supertrait, //! for example `IsA` for `DeviceImpl`. //! -//! * an implementation of [`ClassInitImpl`], for example -//! `ClassInitImpl`. This fills the vtable in the class struct; -//! the source for this is the `*Impl` trait; the associated consts and -//! functions if needed are wrapped to map C types into Rust types. -//! //! * a trait for instance methods, for example `DeviceMethods`. This trait is //! automatically implemented for any reference or smart pointer to a device //! instance. It calls into the vtable provides access across all subclasses @@ -54,6 +49,48 @@ //! This provides access to class-wide functionality that doesn't depend on //! instance data. Like instance methods, these are automatically inherited by //! child classes. +//! +//! # Class structures +//! +//! Each QOM class that has virtual methods describes them in a +//! _class struct_. Class structs include a parent field corresponding +//! to the vtable of the parent class, all the way up to [`ObjectClass`]. +//! +//! As mentioned above, virtual methods are defined via traits such as +//! `DeviceImpl`. Class structs do not define any trait but, conventionally, +//! all of them have a `class_init` method to initialize the virtual methods +//! based on the trait and then call the same method on the superclass. +//! +//! ```ignore +//! impl YourSubclassClass +//! { +//! pub fn class_init(&mut self) { +//! ... +//! klass.parent_class::class_init(); +//! } +//! } +//! ``` +//! +//! If a class implements a QOM interface. In that case, the function must +//! contain, for each interface, an extra forwarding call as follows: +//! +//! ```ignore +//! ResettableClass::cast::(self).class_init::(); +//! ``` +//! +//! These `class_init` functions are methods on the class rather than a trait, +//! because the bound on `T` (`DeviceImpl` in this case), will change for every +//! class struct. The functions are pointed to by the +//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default +//! implementation, in most cases it will be enough to write it as follows: +//! +//! ```ignore +//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; +//! ``` +//! +//! This design incurs a small amount of code duplication but, by not using +//! traits, it allows the flexibility of implementing bindings in any crate, +//! without incurring into violations of orphan rules for traits. use std::{ ffi::CStr, @@ -279,19 +316,25 @@ pub unsafe trait InterfaceType: Sized { /// for this interface. const TYPE_NAME: &'static CStr; - /// Initialize the vtable for the interface; the generic argument `T` is the - /// type being initialized, while the generic argument `U` is the type that + /// Return the vtable for the interface; `U` is the type that /// lists the interface in its `TypeInfo`. /// + /// # Examples + /// + /// This function is usually called by a `class_init` method in `U::Class`. + /// For example, `DeviceClass::class_init` initializes its `Resettable` + /// interface as follows: + /// + /// ```ignore + /// ResettableClass::cast::(self).class_init::(); + /// ``` + /// + /// where `T` is the concrete subclass that is being initialized. + /// /// # Panics /// /// Panic if the incoming argument if `T` does not implement the interface. - fn interface_init< - T: ObjectType + ClassInitImpl + ClassInitImpl, - U: ObjectType, - >( - klass: &mut U::Class, - ) { + fn cast(klass: &mut U::Class) -> &mut Self { unsafe { // SAFETY: upcasting to ObjectClass is always valid, and the // return type is either NULL or the argument itself @@ -300,8 +343,7 @@ pub unsafe trait InterfaceType: Sized { Self::TYPE_NAME.as_ptr(), ) .cast(); - - >::class_init(result.as_mut().unwrap()) + result.as_mut().unwrap() } } } @@ -558,87 +600,20 @@ pub trait ObjectImpl: ObjectType + IsA { /// the default values coming from the parent classes; the function /// can change them to override virtual methods of a parent class. /// - /// Usually defined as `::class_init`. - const CLASS_INIT: fn(&mut Self::Class); -} - -/// Internal trait used to automatically fill in a class struct. -/// -/// Each QOM class that has virtual methods describes them in a -/// _class struct_. Class structs include a parent field corresponding -/// to the vtable of the parent class, all the way up to [`ObjectClass`]. -/// Each QOM type has one such class struct; this trait takes care of -/// initializing the `T` part of the class struct, for the type that -/// implements the trait. -/// -/// Each struct will implement this trait with `T` equal to each -/// superclass. For example, a device should implement at least -/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and -/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are made -/// in one of two ways. -/// -/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` -/// crate itself. The Rust implementation of methods will come from a -/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl), -/// and `ClassInitImpl` is provided by blanket implementations that -/// operate on all implementors of the `*Impl`* trait. For example: -/// -/// ```ignore -/// impl ClassInitImpl for T -/// where -/// T: ClassInitImpl + DeviceImpl, -/// ``` -/// -/// The bound on `ClassInitImpl` is needed so that, -/// after initializing the `DeviceClass` part of the class struct, -/// the parent [`ObjectClass`] is initialized as well. -/// -/// The other case is when manual implementation of the trait is needed. -/// This covers the following cases: -/// -/// * if a class implements a QOM interface, the Rust code _has_ to define its -/// own class struct `FooClass` and implement `ClassInitImpl`. -/// `ClassInitImpl`'s `class_init` method will then forward to -/// multiple other `class_init`s, for the interfaces as well as the -/// superclass. (Note that there is no Rust example yet for using interfaces). -/// -/// * for classes implemented outside the ``qemu-api`` crate, it's not possible -/// to add blanket implementations like the above one, due to orphan rules. In -/// that case, the easiest solution is to implement -/// `ClassInitImpl` for each subclass and not have a -/// `YourSuperclassImpl` trait at all. -/// -/// ```ignore -/// impl ClassInitImpl for YourSubclass { -/// fn class_init(klass: &mut YourSuperclass) { -/// klass.some_method = Some(Self::some_method); -/// >::class_init(&mut klass.parent_class); -/// } -/// } -/// ``` -/// -/// While this method incurs a small amount of code duplication, -/// it is generally limited to the recursive call on the last line. -/// This is because classes defined in Rust do not need the same -/// glue code that is needed when the classes are defined in C code. -/// You may consider using a macro if you have many subclasses. -pub trait ClassInitImpl { - /// Initialize `klass` to point to the virtual method implementations - /// for `Self`. On entry, the virtual method pointers are set to - /// the default values coming from the parent classes; the function - /// can change them to override virtual methods of a parent class. + /// Usually defined simply as `Self::Class::class_init::`; + /// however a default implementation cannot be included here, because the + /// bounds that the `Self::Class::class_init` method places on `Self` are + /// not known in advance. /// - /// The virtual method implementations usually come from another - /// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl) - /// when `T` is [`DeviceClass`](crate::qdev::DeviceClass). + /// # Safety /// - /// On entry, `klass`'s parent class is initialized, while the other fields + /// While `klass`'s parent class is initialized on entry, the other fields /// are all zero; it is therefore assumed that all fields in `T` can be /// zeroed, otherwise it would not be possible to provide the class as a /// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable) /// to T; this is more easily done once Zeroable does not require a manual /// implementation (Rust 1.75.0). - fn class_init(klass: &mut T); + const CLASS_INIT: fn(&mut Self::Class); } /// # Safety @@ -651,13 +626,12 @@ unsafe extern "C" fn rust_unparent_fn(dev: *mut Object) { T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } -impl ClassInitImpl for T -where - T: ObjectImpl, -{ - fn class_init(oc: &mut ObjectClass) { +impl ObjectClass { + /// Fill in the virtual methods of `ObjectClass` based on the definitions in + /// the `ObjectImpl` trait. + pub fn class_init(&mut self) { if ::UNPARENT.is_some() { - oc.unparent = Some(rust_unparent_fn::); + self.unparent = Some(rust_unparent_fn::); } } } diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index fee2e3d478..04821a2b9b 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -14,8 +14,8 @@ use crate::{ irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState}, - qom::{ClassInitImpl, Owned}, + qdev::{DeviceImpl, DeviceState}, + qom::Owned, }; unsafe impl ObjectType for SysBusDevice { @@ -28,12 +28,11 @@ qom_isa!(SysBusDevice: DeviceState, Object); // TODO: add virtual methods pub trait SysBusDeviceImpl: DeviceImpl + IsA {} -impl ClassInitImpl for T -where - T: SysBusDeviceImpl + ClassInitImpl, -{ - fn class_init(sdc: &mut SysBusDeviceClass) { - >::class_init(&mut sdc.parent_class); +impl SysBusDeviceClass { + /// Fill in the virtual methods of `SysBusDeviceClass` based on the + /// definitions in the `SysBusDeviceImpl` trait. + pub fn class_init(self: &mut SysBusDeviceClass) { + self.parent_class.class_init::(); } } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 93c5637bbc..e3985782a3 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -14,7 +14,7 @@ use qemu_api::{ declare_properties, define_property, prelude::*, qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, ParentField}, + qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, vmstate::VMStateDescription, zeroable::Zeroable, @@ -43,7 +43,7 @@ pub struct DummyClass { impl DummyClass { pub fn class_init(self: &mut DummyClass) { - >::class_init(&mut self.parent_class); + self.parent_class.class_init::(); } } From 2152b4bfcd91d7d8b99cd502ed049b0ab8e38649 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 10 Jan 2025 22:51:12 +0800 Subject: [PATCH 1935/2892] i386/cpu: Support module level cache topology Allow cache to be defined at the module level. This increases flexibility for x86 users to customize their cache topology. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20250110145115.1574345-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 72ab147e85..8799e22ed4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -247,6 +247,9 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, case CPU_TOPOLOGY_LEVEL_CORE: num_ids = 1 << apicid_core_offset(topo_info); break; + case CPU_TOPOLOGY_LEVEL_MODULE: + num_ids = 1 << apicid_module_offset(topo_info); + break; case CPU_TOPOLOGY_LEVEL_DIE: num_ids = 1 << apicid_die_offset(topo_info); break; @@ -255,7 +258,7 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, break; default: /* - * Currently there is no use case for THREAD and MODULE, so use + * Currently there is no use case for THREAD, so use * assert directly to facilitate debugging. */ g_assert_not_reached(); From 5ca9282d25157004601c520ed59dcb380177f728 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 10 Jan 2025 22:51:13 +0800 Subject: [PATCH 1936/2892] i386/cpu: Update cache topology with machine's configuration User will configure smp cache topology via -machine smp-cache. For this case, update the x86 CPUs' cache topology with user's configuration in MachineState. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20250110145115.1574345-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8799e22ed4..005ca4235d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7906,6 +7906,64 @@ static void x86_cpu_hyperv_realize(X86CPU *cpu) cpu->hyperv_limits[2] = 0; } +#ifndef CONFIG_USER_ONLY +static bool x86_cpu_update_smp_cache_topo(MachineState *ms, X86CPU *cpu, + Error **errp) +{ + CPUX86State *env = &cpu->env; + CpuTopologyLevel level; + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1d_cache->share_level = level; + env->cache_info_amd.l1d_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_cpuid4.l1d_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_amd.l1d_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1i_cache->share_level = level; + env->cache_info_amd.l1i_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_cpuid4.l1i_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_amd.l1i_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l2_cache->share_level = level; + env->cache_info_amd.l2_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_cpuid4.l2_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_amd.l2_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l3_cache->share_level = level; + env->cache_info_amd.l3_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_cpuid4.l3_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_amd.l3_cache->share_level); + } + + if (!machine_check_smp_cache(ms, errp)) { + return false; + } + return true; +} +#endif + static void x86_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -8145,6 +8203,15 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY MachineState *ms = MACHINE(qdev_get_machine()); + + /* + * TODO: Add a SMPCompatProps.has_caches flag to avoid useless updates + * if user didn't set smp_cache. + */ + if (!x86_cpu_update_smp_cache_topo(ms, cpu, errp)) { + return; + } + qemu_register_reset(x86_cpu_machine_reset_cb, cpu); if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || ms->smp.cpus > 1) { From 90df2cac3700188acadd12948fdad8e9b1459646 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 10 Jan 2025 22:51:14 +0800 Subject: [PATCH 1937/2892] i386/pc: Support cache topology in -machine for PC machine Allow user to configure l1d, l1i, l2 and l3 cache topologies for PC machine. Additionally, add the document of "-machine smp-cache" in qemu-options.hx. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20250110145115.1574345-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 4 ++++ qemu-options.hx | 30 +++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f199a8c7ad..63a96cd23f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1798,6 +1798,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->nvdimm_supported = true; mc->smp_props.dies_supported = true; mc->smp_props.modules_supported = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1D] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1I] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L2] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L3] = true; mc->default_ram_id = "pc.ram"; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; diff --git a/qemu-options.hx b/qemu-options.hx index 61270e3206..dc694a99a3 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -42,7 +42,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" #endif " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n" - " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n", + " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n" + " smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel\n", QEMU_ARCH_ALL) SRST ``-machine [type=]name[,prop=value[,...]]`` @@ -172,6 +173,33 @@ SRST :: -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512 + + ``smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel`` + Define cache properties for SMP system. + + ``cache=cachename`` specifies the cache that the properties will be + applied on. This field is the combination of cache level and cache + type. It supports ``l1d`` (L1 data cache), ``l1i`` (L1 instruction + cache), ``l2`` (L2 unified cache) and ``l3`` (L3 unified cache). + + ``topology=topologylevel`` sets the cache topology level. It accepts + CPU topology levels including ``core``, ``module``, ``cluster``, ``die``, + ``socket``, ``book``, ``drawer`` and a special value ``default``. If + ``default`` is set, then the cache topology will follow the architecture's + default cache topology model. If another topology level is set, the cache + will be shared at corresponding CPU topology level. For example, + ``topology=core`` makes the cache shared by all threads within a core. + The omitting cache will default to using the ``default`` level. + + The default cache topology model for an i386 PC machine is as follows: + ``l1d``, ``l1i``, and ``l2`` caches are per ``core``, while the ``l3`` + cache is per ``die``. + + Example: + + :: + + -machine smp-cache.0.cache=l1d,smp-cache.0.topology=core,smp-cache.1.cache=l1i,smp-cache.1.topology=core ERST DEF("M", HAS_ARG, QEMU_OPTION_M, From 47fc56f36d333263a5865caad306336e3e61e348 Mon Sep 17 00:00:00 2001 From: Alireza Sanaee Date: Fri, 10 Jan 2025 22:51:15 +0800 Subject: [PATCH 1938/2892] i386/cpu: add has_caches flag to check smp_cache configuration Add has_caches flag to SMPCompatProps, which helps in avoiding extra checks for every single layer of caches in x86 (and ARM in future). Signed-off-by: Alireza Sanaee Signed-off-by: Zhao Liu Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20250110145115.1574345-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/core/machine-smp.c | 2 ++ include/hw/boards.h | 3 +++ target/i386/cpu.c | 11 +++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 4e020c358b..0be0ac044c 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -332,6 +332,8 @@ bool machine_parse_smp_cache(MachineState *ms, return false; } } + + mc->smp_props.has_caches = true; return true; } diff --git a/include/hw/boards.h b/include/hw/boards.h index 9360d1ce39..f22b2e7fc7 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -156,6 +156,8 @@ typedef struct { * @modules_supported - whether modules are supported by the machine * @cache_supported - whether cache (l1d, l1i, l2 and l3) configuration are * supported by the machine + * @has_caches - whether cache properties are explicitly specified in the + * user provided smp-cache configuration */ typedef struct { bool prefer_sockets; @@ -166,6 +168,7 @@ typedef struct { bool drawers_supported; bool modules_supported; bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX]; + bool has_caches; } SMPCompatProps; /** diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 005ca4235d..2f9c604552 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8203,13 +8203,12 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); - /* - * TODO: Add a SMPCompatProps.has_caches flag to avoid useless updates - * if user didn't set smp_cache. - */ - if (!x86_cpu_update_smp_cache_topo(ms, cpu, errp)) { - return; + if (mc->smp_props.has_caches) { + if (!x86_cpu_update_smp_cache_topo(ms, cpu, errp)) { + return; + } } qemu_register_reset(x86_cpu_machine_reset_cb, cpu); From 4044f46978ca6c55e5fcdda84310d7435c7c26ac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 17:44:20 +0100 Subject: [PATCH 1939/2892] target/riscv: remove unused macro DEFINE_CPU Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index cca24b9f1f..0dcde546e4 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3051,15 +3051,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) } #endif -#define DEFINE_CPU(type_name, misa_mxl_max, initfn) \ - { \ - .name = (type_name), \ - .parent = TYPE_RISCV_CPU, \ - .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ - } - #define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max, initfn) \ { \ .name = (type_name), \ From aeb7969cba971472aba7a3bf1e0df1bcc1b6f44c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 14:54:50 +0100 Subject: [PATCH 1940/2892] target/riscv: move 128-bit check to TCG realize Besides removing non-declarative code in instance_init, this also fixes an issue with query-cpu-model-expansion. Just invoking it for the x-rv128 CPU model causes QEMU to exit immediately. With this patch it is possible to do {'execute': 'query-cpu-model-expansion', 'arguments':{'type': 'full', 'model': {'name': 'x-rv128'}}} Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 7 ------- target/riscv/tcg/tcg-cpu.c | 9 +++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0dcde546e4..d7ecf729d0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -700,13 +700,6 @@ static void rv128_base_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); CPURISCVState *env = &cpu->env; - if (qemu_tcg_mttcg_enabled()) { - /* Missing 128-bit aligned atomics */ - error_report("128-bit RISC-V currently does not work with Multi " - "Threaded TCG. Please use: -accel tcg,thread=single"); - exit(EXIT_FAILURE); - } - cpu->cfg.mmu = true; cpu->cfg.pmp = true; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 0a137281de..d7e694fdb3 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1014,6 +1014,7 @@ static bool riscv_cpu_is_generic(Object *cpu_obj) static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) { RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); if (!riscv_cpu_tcg_compatible(cpu)) { g_autofree char *name = riscv_cpu_get_name(cpu); @@ -1022,6 +1023,14 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) return false; } + if (mcc->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { + /* Missing 128-bit aligned atomics */ + error_setg(errp, + "128-bit RISC-V currently does not work with Multi " + "Threaded TCG. Please use: -accel tcg,thread=single"); + return false; + } + #ifndef CONFIG_USER_ONLY CPURISCVState *env = &cpu->env; From bb09b7bfd37024381970744c71646e0239428897 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 6 Feb 2025 15:12:14 +0000 Subject: [PATCH 1941/2892] hw/core/machine.c: Make -machine dumpdtb=file.dtb with no DTB an error Currently if the user requests via -machine dumpdtb=file.dtb that we dump the DTB, but the machine doesn't have a DTB, we silently ignore the option. This is confusing to users, and is a legacy of the old board-specific implementation of the option, where if the execution codepath didn't go via a call to qemu_fdt_dumpdtb() we would never handle the option. Now we handle the option in one place in machine.c, we can provide the user with a useful message if they asked us to dump a DTB when none exists. qmp_dumpdtb() already produces this error; remove the logic in handle_machine_dumpdtb() that was there specifically to avoid hitting it. While we're here, beef up the error message a bit with a hint, and make it consistent about "an FDT" rather than "a FDT". (In the qmp_dumpdtb() case this needs an ERRP_GUARD to make error_append_hint() work when the caller passes error_fatal.) Note that the three places where we might report "doesn't have an FDT" are hit in different situations: (1) in handle_machine_dumpdtb(), if CONFIG_FDT is not set: this is because the QEMU binary was built without libfdt at all. The build system will not let you build with a machine type that needs an FDT but no libfdt, so here we know both that the machine doesn't use FDT and that QEMU doesn't have the support: (2) in the device_tree-stub.c qmp_dumpdtb(): this is used when we had libfdt at build time but the target architecture didn't enable any machines which did "select DEVICE_TREE", so here we know that the machine doesn't use FDT. (3) in qmp_dumpdtb(), if current_machine->fdt is NULL all we know is that this machine never set it. That might be because it doesn't use FDT, or it might be because the user didn't pass an FDT on the command line and the machine doesn't autogenerate an FDT. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2733 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250206151214.2947842-7-peter.maydell@linaro.org --- hw/core/machine.c | 6 ++---- system/device_tree-stub.c | 5 ++++- system/device_tree.c | 7 ++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 61c22f723a..b68b8b94a3 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1702,15 +1702,13 @@ static void handle_machine_dumpdtb(MachineState *ms) if (!ms->dumpdtb) { return; } - if (!ms->fdt) { - /* Silently ignore dumpdtb option if there is nothing to dump */ - return; - } #ifdef CONFIG_FDT qmp_dumpdtb(ms->dumpdtb, &error_fatal); exit(0); #else error_report("This machine doesn't have an FDT"); + error_printf("(this machine type definitely doesn't use FDT, and " + "this QEMU doesn't have FDT support compiled in)\n"); exit(1); #endif } diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c index bddda6fa37..428330b0fe 100644 --- a/system/device_tree-stub.c +++ b/system/device_tree-stub.c @@ -5,6 +5,9 @@ #ifdef CONFIG_FDT void qmp_dumpdtb(const char *filename, Error **errp) { - error_setg(errp, "This machine doesn't have a FDT"); + ERRP_GUARD(); + + error_setg(errp, "This machine doesn't have an FDT"); + error_append_hint(errp, "(this machine type definitely doesn't use FDT)\n"); } #endif diff --git a/system/device_tree.c b/system/device_tree.c index d605ed2a21..aa3fe9516f 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -635,11 +635,16 @@ out: void qmp_dumpdtb(const char *filename, Error **errp) { + ERRP_GUARD(); + g_autoptr(GError) err = NULL; uint32_t size; if (!current_machine->fdt) { - error_setg(errp, "This machine doesn't have a FDT"); + error_setg(errp, "This machine doesn't have an FDT"); + error_append_hint(errp, + "(Perhaps it doesn't support FDT at all, or perhaps " + "you need to provide an FDT with the -fdt option?)\n"); return; } From 9b6e986e280f43e3df3baf7aae2069d599b5056c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:13 +0000 Subject: [PATCH 1942/2892] fpu: Make targets specify floatx80 default Inf at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we hardcode at compile time whether the floatx80 default Infinity value has the explicit integer bit set or not (x86 sets it; m68k does not). To be able to compile softfloat once for all targets we'd like to move this setting to runtime. Define a new FloatX80Behaviour enum which is a set of flags that define the target's floatx80 handling. Initially we define just one flag, for whether the default Infinity has the Integer bit set or not, but we will expand this in future commits to cover the other floatx80 target specifics that we currently make compile-time settings. Define a new function floatx80_default_inf() which returns the appropriate default Infinity value of the given sign, and use it in the code that was previously directly using the compile-time constant floatx80_infinity_{low,high} values when packing an infinity into a floatx80. Since floatx80 is highly unlikely to be supported in any new architecture, and the existing code is generally written as "default to like x87, with an ifdef for m68k", we make the default value for the floatx80 behaviour flags be "what x87 does". This means we only need to change the m68k target to specify the behaviour flags. (Other users of floatx80 are the Arm NWFPE emulation, which is obsolete and probably not actually doing the right thing anyway, and the PPC xsrqpxp insn. Making the default be "like x87" avoids our needing to review and test for behaviour changes there.) We will clean up the remaining uses of the floatx80_infinity global constant in subsequent commits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-2-peter.maydell@linaro.org Message-id: 20250217125055.160887-2-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 10 ++++++++++ fpu/softfloat.c | 7 +++---- include/fpu/softfloat-helpers.h | 12 ++++++++++++ include/fpu/softfloat-types.h | 13 +++++++++++++ include/fpu/softfloat.h | 1 + target/m68k/cpu.c | 6 ++++++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index cbbbab52ba..73789e97d7 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -227,6 +227,16 @@ floatx80 floatx80_default_nan(float_status *status) | The pattern for a default generated extended double-precision inf. *----------------------------------------------------------------------------*/ +floatx80 floatx80_default_inf(bool zSign, float_status *status) +{ + /* + * Whether the Integer bit is set in the default Infinity is + * target dependent. + */ + bool z = status->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero; + return packFloatx80(zSign, 0x7fff, z ? 0 : (1ULL << 63)); +} + #define floatx80_infinity_high 0x7FFF #if defined(TARGET_M68K) #define floatx80_infinity_low UINT64_C(0x0000000000000000) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index f4fed9bfda..b12ad2b42a 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1860,7 +1860,8 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p, case float_class_inf: /* x86 and m68k differ in the setting of the integer bit. */ - frac = floatx80_infinity_low; + frac = s->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero ? + 0 : (1ULL << 63); exp = fmt->exp_max; break; @@ -5144,9 +5145,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign, ) { return packFloatx80( zSign, 0x7FFE, ~ roundMask ); } - return packFloatx80(zSign, - floatx80_infinity_high, - floatx80_infinity_low); + return floatx80_default_inf(zSign, status); } if ( zExp <= 0 ) { isTiny = status->tininess_before_rounding diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 8983c2748e..90862f5cd2 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -75,6 +75,12 @@ static inline void set_floatx80_rounding_precision(FloatX80RoundPrec val, status->floatx80_rounding_precision = val; } +static inline void set_floatx80_behaviour(FloatX80Behaviour b, + float_status *status) +{ + status->floatx80_behaviour = b; +} + static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, float_status *status) { @@ -151,6 +157,12 @@ get_floatx80_rounding_precision(const float_status *status) return status->floatx80_rounding_precision; } +static inline FloatX80Behaviour +get_floatx80_behaviour(const float_status *status) +{ + return status->floatx80_behaviour; +} + static inline Float2NaNPropRule get_float_2nan_prop_rule(const float_status *status) { diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 53d5eb8521..dd22ecdbe6 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -320,6 +320,18 @@ typedef enum __attribute__((__packed__)) { float_ftz_before_rounding = 1, } FloatFTZDetection; +/* + * floatx80 is primarily used by x86 and m68k, and there are + * differences in the handling, largely related to the explicit + * Integer bit which floatx80 has and the other float formats do not. + * These flag values allow specification of the target's requirements + * and can be ORed together to set floatx80_behaviour. + */ +typedef enum __attribute__((__packed__)) { + /* In the default Infinity value, is the Integer bit 0 ? */ + floatx80_default_inf_int_bit_is_zero = 1, +} FloatX80Behaviour; + /* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The @@ -331,6 +343,7 @@ typedef struct float_status { uint16_t float_exception_flags; FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; + FloatX80Behaviour floatx80_behaviour; Float2NaNPropRule float_2nan_prop_rule; Float3NaNPropRule float_3nan_prop_rule; FloatInfZeroNaNRule float_infzeronan_rule; diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 09a40b4310..afae390602 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -961,6 +961,7 @@ float128 floatx80_to_float128(floatx80, float_status *status); | The pattern for an extended double-precision inf. *----------------------------------------------------------------------------*/ extern const floatx80 floatx80_infinity; +floatx80 floatx80_default_inf(bool zSign, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision operations. diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 41dfdf5804..df66e8ba22 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -107,6 +107,12 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); /* Default NaN: sign bit clear, all frac bits set */ set_float_default_nan_pattern(0b01111111, &env->fp_status); + /* + * m68k-specific floatx80 behaviour: + * * default Infinity values have a zero Integer bit + */ + set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero, + &env->fp_status); nan = floatx80_default_nan(&env->fp_status); for (i = 0; i < 8; i++) { From e456d4465b630ab2eed08611c6193f0a880e0ea3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:14 +0000 Subject: [PATCH 1943/2892] target/m68k: Avoid using floatx80_infinity global const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The global const floatx80_infinity is (unlike all the other float*_infinity values) target-specific, because whether the explicit Integer bit is set or not varies between m68k and i386. We want to be able to compile softfloat once for multiple targets, so we can't continue to use a single global whose value needs to be different between targets. Replace the direct uses of floatx80_infinity in target/m68k with calls to the new floatx80_default_inf() function. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-3-peter.maydell@linaro.org Message-id: 20250217125055.160887-3-peter.maydell@linaro.org --- target/m68k/softfloat.c | 47 ++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index 02dcc03d15..d1f150e641 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -142,8 +142,7 @@ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaN(a, b, status); } - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } if (aExp == 0) { if (aSig == 0) { @@ -245,7 +244,7 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) float_raise(float_flag_invalid, status); return floatx80_default_nan(status); } - return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -255,8 +254,7 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) if (aSign && aExp >= one_exp) { if (aExp == one_exp && aSig == one_sig) { float_raise(float_flag_divbyzero, status); - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -442,8 +440,7 @@ floatx80 floatx80_logn(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } @@ -452,8 +449,7 @@ floatx80 floatx80_logn(floatx80 a, float_status *status) if (aExp == 0) { if (aSig == 0) { /* zero */ float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } if ((aSig & one_sig) == 0) { /* denormal */ normalizeFloatx80Subnormal(aSig, &aExp, &aSig); @@ -610,15 +606,13 @@ floatx80 floatx80_log10(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } if (aExp == 0 && aSig == 0) { float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } if (aSign) { @@ -668,16 +662,14 @@ floatx80 floatx80_log2(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } if (aExp == 0) { if (aSig == 0) { float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } normalizeFloatx80Subnormal(aSig, &aExp, &aSig); } @@ -740,8 +732,7 @@ floatx80 floatx80_etox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -924,8 +915,7 @@ floatx80 floatx80_twotox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -1075,8 +1065,7 @@ floatx80 floatx80_tentox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -2260,8 +2249,7 @@ floatx80 floatx80_atanh(floatx80 a, float_status *status) if (compact >= 0x3FFF8000) { /* |X| >= 1 */ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ float_raise(float_flag_divbyzero, status); - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } else { /* |X| > 1 */ float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -2320,8 +2308,7 @@ floatx80 floatx80_etoxm1(floatx80 a, float_status *status) if (aSign) { return packFloatx80(aSign, one_exp, one_sig); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -2687,8 +2674,7 @@ floatx80 floatx80_sinh(floatx80 a, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a, status); } - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } if (aExp == 0 && aSig == 0) { @@ -2774,8 +2760,7 @@ floatx80 floatx80_cosh(floatx80 a, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a, status); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { From 165ce008d734bc0024dabdbfd1c41738bc5b834f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:15 +0000 Subject: [PATCH 1944/2892] target/i386: Avoid using floatx80_infinity global const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The global const floatx80_infinity is (unlike all the other float*_infinity values) target-specific, because whether the explicit Integer bit is set or not varies between m68k and i386. We want to be able to compile softfloat once for multiple targets, so we can't continue to use a single global whose value needs to be different between targets. Replace the direct uses of floatx80_infinity in target/i386 with calls to the new floatx80_default_inf() function. Note that because we can ask the function for either a negative or positive infinity, we don't need to change the sign of a positive infinity via floatx80_chs() for the negative-Inf case. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-4-peter.maydell@linaro.org Message-id: 20250217125055.160887-4-peter.maydell@linaro.org --- target/i386/tcg/fpu_helper.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index f112c6c673..741af09f90 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -1832,7 +1832,7 @@ void helper_fxtract(CPUX86State *env) } else if (floatx80_is_infinity(ST0)) { fpush(env); ST0 = ST1; - ST1 = floatx80_infinity; + ST1 = floatx80_default_inf(0, &env->fp_status); } else { int expdif; @@ -2358,9 +2358,8 @@ void helper_fscale(CPUX86State *env) float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else { - ST0 = (floatx80_is_neg(ST0) ? - floatx80_chs(floatx80_infinity) : - floatx80_infinity); + ST0 = floatx80_default_inf(floatx80_is_neg(ST0), + &env->fp_status); } } } else { From 9ea6d1f141426a7da91f1c7ba3d693472f0550a4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:16 +0000 Subject: [PATCH 1945/2892] fpu: Pass float_status to floatx80_is_infinity() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike the other float formats, whether a floatx80 value is considered to be an Infinity is target-dependent. (On x86 if the explicit integer bit is clear this is a "pseudo-infinity" and not a valid infinity; m68k does not care about the value of the integer bit.) Currently we select this target-specific logic at compile time with an ifdef. We're going to want to do this at runtime, so change the floatx80_is_infinity() function to take a float_status. This commit doesn't change any logic; we'll do that in the next commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-5-peter.maydell@linaro.org --- include/fpu/softfloat.h | 2 +- target/i386/tcg/fpu_helper.c | 20 +++++++++++--------- target/m68k/fpu_helper.c | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index afae390602..3c83d703ba 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -996,7 +996,7 @@ static inline floatx80 floatx80_chs(floatx80 a) return a; } -static inline bool floatx80_is_infinity(floatx80 a) +static inline bool floatx80_is_infinity(floatx80 a, float_status *status) { #if defined(TARGET_M68K) return (a.high & 0x7fff) == floatx80_infinity.high && !(a.low << 1); diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 741af09f90..3b79bc049d 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -1393,7 +1393,8 @@ void helper_fpatan(CPUX86State *env) /* Pass this NaN through. */ } else if (floatx80_is_zero(ST1) && !arg0_sign) { /* Pass this zero through. */ - } else if (((floatx80_is_infinity(ST0) && !floatx80_is_infinity(ST1)) || + } else if (((floatx80_is_infinity(ST0, &env->fp_status) && + !floatx80_is_infinity(ST1, &env->fp_status)) || arg0_exp - arg1_exp >= 80) && !arg0_sign) { /* @@ -1442,8 +1443,8 @@ void helper_fpatan(CPUX86State *env) rexp = pi_exp; rsig0 = pi_sig_high; rsig1 = pi_sig_low; - } else if (floatx80_is_infinity(ST1)) { - if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST1, &env->fp_status)) { + if (floatx80_is_infinity(ST0, &env->fp_status)) { if (arg0_sign) { rexp = pi_34_exp; rsig0 = pi_34_sig_high; @@ -1462,7 +1463,8 @@ void helper_fpatan(CPUX86State *env) rexp = pi_2_exp; rsig0 = pi_2_sig_high; rsig1 = pi_2_sig_low; - } else if (floatx80_is_infinity(ST0) || arg0_exp - arg1_exp >= 80) { + } else if (floatx80_is_infinity(ST0, &env->fp_status) || + arg0_exp - arg1_exp >= 80) { /* ST0 is negative. */ rexp = pi_exp; rsig0 = pi_sig_high; @@ -1829,7 +1831,7 @@ void helper_fxtract(CPUX86State *env) } fpush(env); ST0 = ST1; - } else if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST0, &env->fp_status)) { fpush(env); ST0 = ST1; ST1 = floatx80_default_inf(0, &env->fp_status); @@ -2173,7 +2175,7 @@ void helper_fyl2x(CPUX86State *env) } else if (arg0_sign && !floatx80_is_zero(ST0)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_infinity(ST1)) { + } else if (floatx80_is_infinity(ST1, &env->fp_status)) { FloatRelation cmp = floatx80_compare(ST0, floatx80_one, &env->fp_status); switch (cmp) { @@ -2188,7 +2190,7 @@ void helper_fyl2x(CPUX86State *env) ST1 = floatx80_default_nan(&env->fp_status); break; } - } else if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST0, &env->fp_status)) { if (floatx80_is_zero(ST1)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); @@ -2341,11 +2343,11 @@ void helper_fscale(CPUX86State *env) float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_silence_nan(ST0, &env->fp_status); } - } else if (floatx80_is_infinity(ST1) && + } else if (floatx80_is_infinity(ST1, &env->fp_status) && !floatx80_invalid_encoding(ST0) && !floatx80_is_any_nan(ST0)) { if (floatx80_is_neg(ST1)) { - if (floatx80_is_infinity(ST0)) { + if (floatx80_is_infinity(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else { diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 339b73ad7d..eb1cb8c687 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -455,7 +455,7 @@ void HELPER(ftst)(CPUM68KState *env, FPReg *val) if (floatx80_is_any_nan(val->d)) { cc |= FPSR_CC_A; - } else if (floatx80_is_infinity(val->d)) { + } else if (floatx80_is_infinity(val->d, &env->fp_status)) { cc |= FPSR_CC_I; } else if (floatx80_is_zero(val->d)) { cc |= FPSR_CC_Z; From 44eb32a9835fe2feb19503e93476eee602daee0b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:17 +0000 Subject: [PATCH 1946/2892] fpu: Make targets specify whether floatx80 Inf can have Int bit clear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Intel terminology, a floatx80 Infinity with the explicit integer bit clear is a "pseudo-infinity"; for x86 these are not valid infinity values. m68k is looser and does not care whether the Integer bit is set or clear in an infinity. Move this setting to runtime rather than using an ifdef in floatx80_is_infinity(). Since this was the last use of the floatx80_infinity global constant, we remove it and its definition here. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-6-peter.maydell@linaro.org Message-id: 20250217125055.160887-5-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 10 ---------- include/fpu/softfloat-types.h | 5 +++++ include/fpu/softfloat.h | 18 +++++++++++------- target/m68k/cpu.c | 4 +++- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 73789e97d7..8327f72786 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -237,16 +237,6 @@ floatx80 floatx80_default_inf(bool zSign, float_status *status) return packFloatx80(zSign, 0x7fff, z ? 0 : (1ULL << 63)); } -#define floatx80_infinity_high 0x7FFF -#if defined(TARGET_M68K) -#define floatx80_infinity_low UINT64_C(0x0000000000000000) -#else -#define floatx80_infinity_low UINT64_C(0x8000000000000000) -#endif - -const floatx80 floatx80_infinity - = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low); - /*---------------------------------------------------------------------------- | Returns 1 if the half-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index dd22ecdbe6..e1732beba4 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -330,6 +330,11 @@ typedef enum __attribute__((__packed__)) { typedef enum __attribute__((__packed__)) { /* In the default Infinity value, is the Integer bit 0 ? */ floatx80_default_inf_int_bit_is_zero = 1, + /* + * Are Pseudo-infinities (Inf with the Integer bit zero) valid? + * If so, floatx80_is_infinity() will return true for them. + */ + floatx80_pseudo_inf_valid = 2, } FloatX80Behaviour; /* diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 3c83d703ba..07259c5930 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -960,7 +960,6 @@ float128 floatx80_to_float128(floatx80, float_status *status); /*---------------------------------------------------------------------------- | The pattern for an extended double-precision inf. *----------------------------------------------------------------------------*/ -extern const floatx80 floatx80_infinity; floatx80 floatx80_default_inf(bool zSign, float_status *status); /*---------------------------------------------------------------------------- @@ -998,12 +997,17 @@ static inline floatx80 floatx80_chs(floatx80 a) static inline bool floatx80_is_infinity(floatx80 a, float_status *status) { -#if defined(TARGET_M68K) - return (a.high & 0x7fff) == floatx80_infinity.high && !(a.low << 1); -#else - return (a.high & 0x7fff) == floatx80_infinity.high && - a.low == floatx80_infinity.low; -#endif + /* + * It's target-specific whether the Integer bit is permitted + * to be 0 in a valid Infinity value. (x86 says no, m68k says yes). + */ + bool intbit = a.low >> 63; + + if (!intbit && + !(status->floatx80_behaviour & floatx80_pseudo_inf_valid)) { + return false; + } + return (a.high & 0x7fff) == 0x7fff && !(a.low << 1); } static inline bool floatx80_is_neg(floatx80 a) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index df66e8ba22..56b23de21f 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -110,8 +110,10 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) /* * m68k-specific floatx80 behaviour: * * default Infinity values have a zero Integer bit + * * input Infinities may have the Integer bit either 0 or 1 */ - set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero, + set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero | + floatx80_pseudo_inf_valid, &env->fp_status); nan = floatx80_default_nan(&env->fp_status); From 765fe845ccb953b77b1b7e0557b13a7b760067b0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:18 +0000 Subject: [PATCH 1947/2892] fpu: Pass float_status to floatx80_invalid_encoding() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The definition of which floatx80 encodings are invalid is target-specific. Currently we handle this with an ifdef, but we would like to defer this decision to runtime. In preparation, pass a float_status argument to floatx80_invalid_encoding(). We will change the implementation from ifdef to looking at the status argument in the following commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-7-peter.maydell@linaro.org --- fpu/softfloat.c | 2 +- include/fpu/softfloat.h | 2 +- target/i386/tcg/fpu_helper.c | 24 +++++++++++++----------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b12ad2b42a..2a20ae871e 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1810,7 +1810,7 @@ static bool floatx80_unpack_canonical(FloatParts128 *p, floatx80 f, g_assert_not_reached(); } - if (unlikely(floatx80_invalid_encoding(f))) { + if (unlikely(floatx80_invalid_encoding(f, s))) { float_raise(float_flag_invalid, s); return false; } diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 07259c5930..1c8f3cbb78 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -1081,7 +1081,7 @@ static inline bool floatx80_unordered_quiet(floatx80 a, floatx80 b, | pseudo-denormals, which must still be correctly handled as inputs even | if they are never generated as outputs. *----------------------------------------------------------------------------*/ -static inline bool floatx80_invalid_encoding(floatx80 a) +static inline bool floatx80_invalid_encoding(floatx80 a, float_status *s) { #if defined(TARGET_M68K) /*------------------------------------------------------------------------- diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 3b79bc049d..4858ae9a5f 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -1141,7 +1141,7 @@ void helper_f2xm1(CPUX86State *env) int32_t exp = extractFloatx80Exp(ST0); bool sign = extractFloatx80Sign(ST0); - if (floatx80_invalid_encoding(ST0)) { + if (floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -1383,8 +1383,8 @@ void helper_fpatan(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -1819,7 +1819,7 @@ void helper_fxtract(CPUX86State *env) &env->fp_status); fpush(env); ST0 = temp.d; - } else if (floatx80_invalid_encoding(ST0)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); fpush(env); @@ -1870,7 +1870,8 @@ static void helper_fprem_common(CPUX86State *env, bool mod) env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || exp0 == 0x7fff || exp1 == 0x7fff || - floatx80_invalid_encoding(ST0) || floatx80_invalid_encoding(ST1)) { + floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); } else { if (exp0 == 0) { @@ -2066,8 +2067,8 @@ void helper_fyl2xp1(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -2164,8 +2165,8 @@ void helper_fyl2x(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -2331,7 +2332,8 @@ void helper_frndint(CPUX86State *env) void helper_fscale(CPUX86State *env) { uint8_t old_flags = save_exception_flags(env); - if (floatx80_invalid_encoding(ST1) || floatx80_invalid_encoding(ST0)) { + if (floatx80_invalid_encoding(ST1, &env->fp_status) || + floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST1)) { @@ -2344,7 +2346,7 @@ void helper_fscale(CPUX86State *env) ST0 = floatx80_silence_nan(ST0, &env->fp_status); } } else if (floatx80_is_infinity(ST1, &env->fp_status) && - !floatx80_invalid_encoding(ST0) && + !floatx80_invalid_encoding(ST0, &env->fp_status) && !floatx80_is_any_nan(ST0)) { if (floatx80_is_neg(ST1)) { if (floatx80_is_infinity(ST0, &env->fp_status)) { From a261d3e331ca06f4d92e689f2bee40d0a0cdee08 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:19 +0000 Subject: [PATCH 1948/2892] fpu: Make floatx80 invalid encoding settable at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because floatx80 has an explicit integer bit, this permits some odd encodings where the integer bit is not set correctly for the floating point value type. In In Intel terminology the categories are: exp == 0, int = 0, mantissa == 0 : zeroes exp == 0, int = 0, mantissa != 0 : denormals exp == 0, int = 1 : pseudo-denormals 0 < exp < 0x7fff, int = 0 : unnormals 0 < exp < 0x7fff, int = 1 : normals exp == 0x7fff, int = 0, mantissa == 0 : pseudo-infinities exp == 0x7fff, int = 1, mantissa == 0 : infinities exp == 0x7fff, int = 0, mantissa != 0 : pseudo-NaNs exp == 0x7fff, int = 1, mantissa == 0 : NaNs The usual IEEE cases of zero, denormal, normal, inf and NaN are always valid. x87 permits as input also pseudo-denormals. m68k permits all those and also pseudo-infinities, pseudo-NaNs and unnormals. Currently we have an ifdef in floatx80_invalid_encoding() to select the x86 vs m68k behaviour. Add new floatx80_behaviour flags to select whether pseudo-NaN and unnormal are valid, and use these (plus the existing pseudo_inf_valid flag) to decide whether these encodings are invalid at runtime. We leave pseudo-denormals as always-valid, since both x86 and m68k accept them. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-8-peter.maydell@linaro.org Message-id: 20250217125055.160887-6-peter.maydell@linaro.org --- include/fpu/softfloat-types.h | 14 ++++++++ include/fpu/softfloat.h | 68 ++++++++++++++++++----------------- target/m68k/cpu.c | 28 ++++++++++++++- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index e1732beba4..b1941384ae 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -333,8 +333,22 @@ typedef enum __attribute__((__packed__)) { /* * Are Pseudo-infinities (Inf with the Integer bit zero) valid? * If so, floatx80_is_infinity() will return true for them. + * If not, floatx80_invalid_encoding will return false for them, + * and using them as inputs to a float op will raise Invalid. */ floatx80_pseudo_inf_valid = 2, + /* + * Are Pseudo-NaNs (NaNs where the Integer bit is zero) valid? + * If not, floatx80_invalid_encoding() will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_pseudo_nan_valid = 4, + /* + * Are Unnormals (0 < exp < 0x7fff, Integer bit zero) valid? + * If not, floatx80_invalid_encoding() will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_unnormal_valid = 8, } FloatX80Behaviour; /* diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 1c8f3cbb78..c18ab2cb60 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -1073,41 +1073,45 @@ static inline bool floatx80_unordered_quiet(floatx80 a, floatx80 b, /*---------------------------------------------------------------------------- | Return whether the given value is an invalid floatx80 encoding. -| Invalid floatx80 encodings arise when the integer bit is not set, but -| the exponent is not zero. The only times the integer bit is permitted to -| be zero is in subnormal numbers and the value zero. -| This includes what the Intel software developer's manual calls pseudo-NaNs, -| pseudo-infinities and un-normal numbers. It does not include -| pseudo-denormals, which must still be correctly handled as inputs even -| if they are never generated as outputs. +| Invalid floatx80 encodings may arise when the integer bit is not set +| correctly; this is target-specific. In Intel terminology the +| categories are: +| exp == 0, int = 0, mantissa == 0 : zeroes +| exp == 0, int = 0, mantissa != 0 : denormals +| exp == 0, int = 1 : pseudo-denormals +| 0 < exp < 0x7fff, int = 0 : unnormals +| 0 < exp < 0x7fff, int = 1 : normals +| exp == 0x7fff, int = 0, mantissa == 0 : pseudo-infinities +| exp == 0x7fff, int = 1, mantissa == 0 : infinities +| exp == 0x7fff, int = 0, mantissa != 0 : pseudo-NaNs +| exp == 0x7fff, int = 1, mantissa == 0 : NaNs +| +| The usual IEEE cases of zero, denormal, normal, inf and NaN are always valid. +| x87 permits as input also pseudo-denormals. +| m68k permits all those and also pseudo-infinities, pseudo-NaNs and unnormals. +| +| Since we don't have a target that handles floatx80 but prohibits +| pseudo-denormals in input, we don't currently have a floatx80_behaviour +| flag for that case, but instead always accept it. Conveniently this +| means that all cases with either exponent 0 or the integer bit set are +| valid for all targets. *----------------------------------------------------------------------------*/ static inline bool floatx80_invalid_encoding(floatx80 a, float_status *s) { -#if defined(TARGET_M68K) - /*------------------------------------------------------------------------- - | With m68k, the explicit integer bit can be zero in the case of: - | - zeros (exp == 0, mantissa == 0) - | - denormalized numbers (exp == 0, mantissa != 0) - | - unnormalized numbers (exp != 0, exp < 0x7FFF) - | - infinities (exp == 0x7FFF, mantissa == 0) - | - not-a-numbers (exp == 0x7FFF, mantissa != 0) - | - | For infinities and NaNs, the explicit integer bit can be either one or - | zero. - | - | The IEEE 754 standard does not define a zero integer bit. Such a number - | is an unnormalized number. Hardware does not directly support - | denormalized and unnormalized numbers, but implicitly supports them by - | trapping them as unimplemented data types, allowing efficient conversion - | in software. - | - | See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", - | "1.6 FLOATING-POINT DATA TYPES" - *------------------------------------------------------------------------*/ - return false; -#else - return (a.low & (1ULL << 63)) == 0 && (a.high & 0x7FFF) != 0; -#endif + if ((a.low >> 63) || (a.high & 0x7fff) == 0) { + /* Anything with the Integer bit set or the exponent 0 is valid */ + return false; + } + + if ((a.high & 0x7fff) == 0x7fff) { + if (a.low) { + return !(s->floatx80_behaviour & floatx80_pseudo_nan_valid); + } else { + return !(s->floatx80_behaviour & floatx80_pseudo_inf_valid); + } + } else { + return !(s->floatx80_behaviour & floatx80_unnormal_valid); + } } #define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 56b23de21f..505fa97a53 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -111,9 +111,35 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) * m68k-specific floatx80 behaviour: * * default Infinity values have a zero Integer bit * * input Infinities may have the Integer bit either 0 or 1 + * * pseudo-denormals supported for input and output + * * don't raise Invalid for pseudo-NaN/pseudo-Inf/Unnormal + * + * With m68k, the explicit integer bit can be zero in the case of: + * - zeros (exp == 0, mantissa == 0) + * - denormalized numbers (exp == 0, mantissa != 0) + * - unnormalized numbers (exp != 0, exp < 0x7FFF) + * - infinities (exp == 0x7FFF, mantissa == 0) + * - not-a-numbers (exp == 0x7FFF, mantissa != 0) + * + * For infinities and NaNs, the explicit integer bit can be either one or + * zero. + * + * The IEEE 754 standard does not define a zero integer bit. Such a number + * is an unnormalized number. Hardware does not directly support + * denormalized and unnormalized numbers, but implicitly supports them by + * trapping them as unimplemented data types, allowing efficient conversion + * in software. + * + * See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", + * "1.6 FLOATING-POINT DATA TYPES" + * + * Note though that QEMU's fp emulation does directly handle both + * denormal and unnormal values, and does not trap to guest software. */ set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero | - floatx80_pseudo_inf_valid, + floatx80_pseudo_inf_valid | + floatx80_pseudo_nan_valid | + floatx80_unnormal_valid, &env->fp_status); nan = floatx80_default_nan(&env->fp_status); From 1e75d8247ff27a307781230898fbcb8fbb9a8298 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:20 +0000 Subject: [PATCH 1949/2892] fpu: Move m68k_denormal fmt flag into floatx80_behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we compile-time set an 'm68k_denormal' flag in the FloatFmt for floatx80 for m68k. This controls our handling of what the Intel documentation calls a "pseudo-denormal": a value where the exponent field is zero and the explicit integer bit is set. For x86, the x87 FPU is supposed to accept a pseudo-denormal as input, but never generate one on output. For m68k, these values are permitted on input and may be produced on output. Replace the flag in the FloatFmt with a flag indicating whether the float format has an explicit bit (which will be true for floatx80 for all targets, and false for every other float type). Then we can gate the handling of these pseudo-denormals on the setting of a floatx80_behaviour flag. As far as I can see from the code we don't actually handle the x86-mandated "accept on input but don't generate" behaviour, because the handling in partsN(canonicalize) looked at fmt->m68k_denormal. So I have added TODO comments to that effect. This commit doesn't change any behaviour for any target. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250224111524.1101196-9-peter.maydell@linaro.org Message-id: 20250217125055.160887-7-peter.maydell@linaro.org --- fpu/softfloat-parts.c.inc | 27 ++++++++++++++++++++++++--- fpu/softfloat.c | 9 ++++----- include/fpu/softfloat-types.h | 19 +++++++++++++++++++ target/m68k/cpu.c | 3 ++- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 1d09f066c5..171bfd06e3 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -195,6 +195,25 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, static void partsN(canonicalize)(FloatPartsN *p, float_status *status, const FloatFmt *fmt) { + /* + * It's target-dependent how to handle the case of exponent 0 + * and Integer bit set. Intel calls these "pseudodenormals", + * and treats them as if the integer bit was 0, and never + * produces them on output. This is the default behaviour for QEMU. + * For m68k, the integer bit is considered validly part of the + * input value when the exponent is 0, and may be 0 or 1, + * giving extra range. They may also be generated as outputs. + * (The m68k manual actually calls these values part of the + * normalized number range, not the denormalized number range, + * but that distinction is not important for us, because + * m68k doesn't care about the input_denormal_used status flag.) + * floatx80_pseudo_denormal_valid selects the m68k behaviour, + * which changes both how we canonicalize such a value and + * how we uncanonicalize results. + */ + bool has_pseudo_denormals = fmt->has_explicit_bit && + (status->floatx80_behaviour & floatx80_pseudo_denormal_valid); + if (unlikely(p->exp == 0)) { if (likely(frac_eqz(p))) { p->cls = float_class_zero; @@ -206,7 +225,7 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status, int shift = frac_normalize(p); p->cls = float_class_denormal; p->exp = fmt->frac_shift - fmt->exp_bias - - shift + !fmt->m68k_denormal; + - shift + !has_pseudo_denormals; } } else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) { p->cls = float_class_normal; @@ -342,13 +361,15 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, frac_clear(p); } else { bool is_tiny = s->tininess_before_rounding || exp < 0; + bool has_pseudo_denormals = fmt->has_explicit_bit && + (s->floatx80_behaviour & floatx80_pseudo_denormal_valid); if (!is_tiny) { FloatPartsN discard; is_tiny = !frac_addi(&discard, p, inc); } - frac_shrjam(p, !fmt->m68k_denormal - exp); + frac_shrjam(p, !has_pseudo_denormals - exp); if (p->frac_lo & round_mask) { /* Need to recompute round-to-even/round-to-odd. */ @@ -379,7 +400,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_lo &= ~round_mask; } - exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal; + exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !has_pseudo_denormals; frac_shr(p, frac_shift); if (is_tiny) { diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 2a20ae871e..b299cfaf86 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -537,7 +537,8 @@ typedef struct { * round_mask: bits below lsb which must be rounded * The following optional modifiers are available: * arm_althp: handle ARM Alternative Half Precision - * m68k_denormal: explicit integer bit for extended precision may be 1 + * has_explicit_bit: has an explicit integer bit; this affects whether + * the float_status floatx80_behaviour handling applies */ typedef struct { int exp_size; @@ -547,7 +548,7 @@ typedef struct { int frac_size; int frac_shift; bool arm_althp; - bool m68k_denormal; + bool has_explicit_bit; uint64_t round_mask; } FloatFmt; @@ -600,9 +601,7 @@ static const FloatFmt floatx80_params[3] = { [floatx80_precision_d] = { FLOATX80_PARAMS(52) }, [floatx80_precision_x] = { FLOATX80_PARAMS(64), -#ifdef TARGET_M68K - .m68k_denormal = true, -#endif + .has_explicit_bit = true, }, }; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index b1941384ae..1af2a0cb14 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -349,6 +349,25 @@ typedef enum __attribute__((__packed__)) { * and using them as inputs to a float op will raise Invalid. */ floatx80_unnormal_valid = 8, + + /* + * If the exponent is 0 and the Integer bit is set, Intel call + * this a "pseudo-denormal"; x86 supports that only on input + * (treating them as denormals by ignoring the Integer bit). + * For m68k, the integer bit is considered validly part of the + * input value when the exponent is 0, and may be 0 or 1, + * giving extra range. They may also be generated as outputs. + * (The m68k manual actually calls these values part of the + * normalized number range, not the denormalized number range.) + * + * By default you get the Intel behaviour where the Integer + * bit is ignored; if this is set then the Integer bit value + * is honoured, m68k-style. + * + * Either way, floatx80_invalid_encoding() will always accept + * pseudo-denormals. + */ + floatx80_pseudo_denormal_valid = 16, } FloatX80Behaviour; /* diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 505fa97a53..2617d8f6ed 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -139,7 +139,8 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero | floatx80_pseudo_inf_valid | floatx80_pseudo_nan_valid | - floatx80_unnormal_valid, + floatx80_unnormal_valid | + floatx80_pseudo_denormal_valid, &env->fp_status); nan = floatx80_default_nan(&env->fp_status); From 2e01cfea0735889a1e0481fc783d621779439572 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:21 +0000 Subject: [PATCH 1950/2892] fpu: Always decide no_signaling_nans() at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we have a compile-time shortcut where we return false from no_signaling_nans() on everything except Xtensa, because we know that's the only target that might ever set status->no_signaling_nans. Remove the ifdef, so we always look at the status flag; this has no behavioural change, but will be necessary if we want to build softfloat once for all targets. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250224111524.1101196-10-peter.maydell@linaro.org Message-id: 20250217125055.160887-8-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 8327f72786..a2c6afad5d 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -85,11 +85,7 @@ this code that are retained. */ static inline bool no_signaling_nans(float_status *status) { -#if defined(TARGET_XTENSA) return status->no_signaling_nans; -#else - return false; -#endif } /* Define how the architecture discriminates signaling NaNs. From 3abed4d0eace62910e90c206cb9d5741c6095b12 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:22 +0000 Subject: [PATCH 1951/2892] fpu: Always decide snan_bit_is_one() at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we have a compile-time shortcut where we return a hardcode value from snan_bit_is_one() on everything except MIPS, because we know that's the only target that needs to change status->no_signaling_nans at runtime. Remove the ifdef, so we always look at the status flag. This means we must update the two targets (HPPA and SH4) that were previously hardcoded to return true so that they set the status flag correctly. This has no behavioural change, but will be necessary if we want to build softfloat once for all targets. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250224111524.1101196-11-peter.maydell@linaro.org Message-id: 20250217125055.160887-9-peter.maydell@linaro.org --- fpu/softfloat-specialize.c.inc | 7 ------- target/hppa/fpu_helper.c | 1 + target/sh4/cpu.c | 1 + 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index a2c6afad5d..ba4fa08b7b 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -93,17 +93,10 @@ static inline bool no_signaling_nans(float_status *status) * In IEEE 754-1985 this was implementation defined, but in IEEE 754-2008 * the msb must be zero. MIPS is (so far) unique in supporting both the * 2008 revision and backward compatibility with their original choice. - * Thus for MIPS we must make the choice at runtime. */ static inline bool snan_bit_is_one(float_status *status) { -#if defined(TARGET_MIPS) return status->snan_bit_is_one; -#elif defined(TARGET_HPPA) || defined(TARGET_SH4) - return 1; -#else - return 0; -#endif } /*---------------------------------------------------------------------------- diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 8ff4b44804..a62d9d3083 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -67,6 +67,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); /* Default NaN: sign bit clear, msb-1 frac bit set */ set_float_default_nan_pattern(0b00100000, &env->fp_status); + set_snan_bit_is_one(true, &env->fp_status); /* * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing * enabled by FPSR.D happens before or after rounding. We pick "before" diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4ac693d99b..ccfe222bdf 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -128,6 +128,7 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); + set_snan_bit_is_one(true, &env->fp_status); /* sign bit clear, set all frac bits other than msb */ set_float_default_nan_pattern(0b00111111, &env->fp_status); /* From c5d4173fcf04f6de9b9bb0959d1fdfc08254381a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:23 +0000 Subject: [PATCH 1952/2892] fpu: Don't compile-time disable hardfloat for PPC targets We happen to know that for the PPC target the FP status flags (and in particular float_flag_inexact) will always be cleared before a floating point operation, and so can_use_fpu() will always return false. So we speed things up a little by forcing QEMU_NO_HARDFLOAT to true on that target. We would like to build softfloat once for all targets; that means removing target-specific ifdefs. Remove the check for TARGET_PPC; this won't change behaviour because can_use_fpu() will see that float_flag_inexact is clear and take the softfloat path anyway. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250224111524.1101196-12-peter.maydell@linaro.org Message-id: 20250217125055.160887-10-peter.maydell@linaro.org --- fpu/softfloat.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b299cfaf86..b38eea8d87 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -220,11 +220,9 @@ GEN_INPUT_FLUSH3(float64_input_flush3, float64) * the use of hardfloat, since hardfloat relies on the inexact flag being * already set. */ -#if defined(TARGET_PPC) || defined(__FAST_MATH__) # if defined(__FAST_MATH__) # warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \ IEEE implementation -# endif # define QEMU_NO_HARDFLOAT 1 # define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN #else From 5d3462b4cde8bedb33362dab0a3ae94d403899b0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 24 Feb 2025 11:15:24 +0000 Subject: [PATCH 1953/2892] fpu: Build only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have removed all the target-specifics from the softfloat code, we can switch to building it once for the whole system rather than once per target. Signed-off-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250224111524.1101196-13-peter.maydell@linaro.org Message-id: 20250217125055.160887-11-peter.maydell@linaro.org --- fpu/meson.build | 2 +- fpu/softfloat.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/fpu/meson.build b/fpu/meson.build index 1a9992ded5..646c76f0c6 100644 --- a/fpu/meson.build +++ b/fpu/meson.build @@ -1 +1 @@ -specific_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c')) +common_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c')) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b38eea8d87..34c962d6bd 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -79,9 +79,6 @@ this code that are retained. * version 2 or later. See the COPYING file in the top-level directory. */ -/* softfloat (and in particular the code in softfloat-specialize.h) is - * target-dependent and needs the TARGET_* macros. - */ #include "qemu/osdep.h" #include #include "qemu/bitops.h" From 1deb15c88ab3f1b0788b9e41b08217036eca3c91 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Feb 2025 19:09:53 +0000 Subject: [PATCH 1954/2892] target/arm: Move TCG-only VFP code into tcg/ subdir Most of the target/arm/vfp_helper.c file is purely TCG helper code, guarded by #ifdef CONFIG_TCG. Move this into a new file in target/arm/tcg/. This leaves only the code relating to getting and setting the FPCR/FPSR/FPSCR in the original file. (Some of this also is TCG-only, but that needs more careful disentangling.) Having two vfp_helper.c files might seem a bit confusing, but once we've finished moving all the helper code out of the old file we are going to rename it to vfp_fpscr.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250221190957.811948-2-peter.maydell@linaro.org --- target/arm/tcg/meson.build | 1 + target/arm/tcg/vfp_helper.c | 1130 +++++++++++++++++++++++++++++++++++ target/arm/vfp_helper.c | 1109 ---------------------------------- 3 files changed, 1131 insertions(+), 1109 deletions(-) create mode 100644 target/arm/tcg/vfp_helper.c diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 1f9077c372..dd12ccedb1 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -41,6 +41,7 @@ arm_ss.add(files( 'vec_helper.c', 'tlb-insns.c', 'arith_helper.c', + 'vfp_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c new file mode 100644 index 0000000000..aa580ff64c --- /dev/null +++ b/target/arm/tcg/vfp_helper.c @@ -0,0 +1,1130 @@ +/* + * ARM VFP floating-point operations + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "internals.h" +#include "cpu-features.h" +#include "fpu/softfloat.h" +#include "qemu/log.h" + +/* + * VFP support. We follow the convention used for VFP instructions: + * Single precision routines have a "s" suffix, double precision a + * "d" suffix. + */ + +#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) + +#define VFP_BINOP(name) \ +dh_ctype_f16 VFP_HELPER(name, h)(dh_ctype_f16 a, dh_ctype_f16 b, float_status *fpst) \ +{ \ + return float16_ ## name(a, b, fpst); \ +} \ +float32 VFP_HELPER(name, s)(float32 a, float32 b, float_status *fpst) \ +{ \ + return float32_ ## name(a, b, fpst); \ +} \ +float64 VFP_HELPER(name, d)(float64 a, float64 b, float_status *fpst) \ +{ \ + return float64_ ## name(a, b, fpst); \ +} +VFP_BINOP(add) +VFP_BINOP(sub) +VFP_BINOP(mul) +VFP_BINOP(div) +VFP_BINOP(min) +VFP_BINOP(max) +VFP_BINOP(minnum) +VFP_BINOP(maxnum) +#undef VFP_BINOP + +dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, float_status *fpst) +{ + return float16_sqrt(a, fpst); +} + +float32 VFP_HELPER(sqrt, s)(float32 a, float_status *fpst) +{ + return float32_sqrt(a, fpst); +} + +float64 VFP_HELPER(sqrt, d)(float64 a, float_status *fpst) +{ + return float64_sqrt(a, fpst); +} + +static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) +{ + uint32_t flags; + switch (cmp) { + case float_relation_equal: + flags = 0x6; + break; + case float_relation_less: + flags = 0x8; + break; + case float_relation_greater: + flags = 0x2; + break; + case float_relation_unordered: + flags = 0x3; + break; + default: + g_assert_not_reached(); + } + env->vfp.fpsr = deposit64(env->vfp.fpsr, 28, 4, flags); /* NZCV */ +} + +/* XXX: check quiet/signaling case */ +#define DO_VFP_cmp(P, FLOATTYPE, ARGTYPE, FPST) \ +void VFP_HELPER(cmp, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ +{ \ + softfloat_to_vfp_compare(env, \ + FLOATTYPE ## _compare_quiet(a, b, &env->vfp.fp_status[FPST])); \ +} \ +void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ +{ \ + softfloat_to_vfp_compare(env, \ + FLOATTYPE ## _compare(a, b, &env->vfp.fp_status[FPST])); \ +} +DO_VFP_cmp(h, float16, dh_ctype_f16, FPST_A32_F16) +DO_VFP_cmp(s, float32, float32, FPST_A32) +DO_VFP_cmp(d, float64, float64, FPST_A32) +#undef DO_VFP_cmp + +/* Integer to float and float to integer conversions */ + +#define CONV_ITOF(name, ftype, fsz, sign) \ +ftype HELPER(name)(uint32_t x, float_status *fpst) \ +{ \ + return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ +} + +#define CONV_FTOI(name, ftype, fsz, sign, round) \ +sign##int32_t HELPER(name)(ftype x, float_status *fpst) \ +{ \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ +} + +#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ + CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ + CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ + CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) + +FLOAT_CONVS(si, h, uint32_t, 16, ) +FLOAT_CONVS(si, s, float32, 32, ) +FLOAT_CONVS(si, d, float64, 64, ) +FLOAT_CONVS(ui, h, uint32_t, 16, u) +FLOAT_CONVS(ui, s, float32, 32, u) +FLOAT_CONVS(ui, d, float64, 64, u) + +#undef CONV_ITOF +#undef CONV_FTOI +#undef FLOAT_CONVS + +/* floating point conversion */ +float64 VFP_HELPER(fcvtd, s)(float32 x, float_status *status) +{ + return float32_to_float64(x, status); +} + +float32 VFP_HELPER(fcvts, d)(float64 x, float_status *status) +{ + return float64_to_float32(x, status); +} + +uint32_t HELPER(bfcvt)(float32 x, float_status *status) +{ + return float32_to_bfloat16(x, status); +} + +uint32_t HELPER(bfcvt_pair)(uint64_t pair, float_status *status) +{ + bfloat16 lo = float32_to_bfloat16(extract64(pair, 0, 32), status); + bfloat16 hi = float32_to_bfloat16(extract64(pair, 32, 32), status); + return deposit32(lo, 16, 16, hi); +} + +/* + * VFP3 fixed point conversion. The AArch32 versions of fix-to-float + * must always round-to-nearest; the AArch64 ones honour the FPSCR + * rounding mode. (For AArch32 Neon the standard-FPSCR is set to + * round-to-nearest so either helper will work.) AArch32 float-to-fix + * must round-to-zero. + */ +#define VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ +ftype HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ + float_status *fpst) \ +{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpst); } + +#define VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ + ftype HELPER(vfp_##name##to##p##_round_to_nearest)(uint##isz##_t x, \ + uint32_t shift, \ + float_status *fpst) \ + { \ + ftype ret; \ + FloatRoundMode oldmode = fpst->float_rounding_mode; \ + fpst->float_rounding_mode = float_round_nearest_even; \ + ret = itype##_to_##float##fsz##_scalbn(x, -shift, fpst); \ + fpst->float_rounding_mode = oldmode; \ + return ret; \ + } + +#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, ROUND, suff) \ +uint##isz##_t HELPER(vfp_to##name##p##suff)(ftype x, uint32_t shift, \ + float_status *fpst) \ +{ \ + if (unlikely(float##fsz##_is_any_nan(x))) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ +} + +#define VFP_CONV_FIX(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ + float_round_to_zero, _round_to_zero) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ + get_float_rounding_mode(fpst), ) + +#define VFP_CONV_FIX_A64(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ + get_float_rounding_mode(fpst), ) + +VFP_CONV_FIX(sh, d, 64, float64, 64, int16) +VFP_CONV_FIX(sl, d, 64, float64, 64, int32) +VFP_CONV_FIX_A64(sq, d, 64, float64, 64, int64) +VFP_CONV_FIX(uh, d, 64, float64, 64, uint16) +VFP_CONV_FIX(ul, d, 64, float64, 64, uint32) +VFP_CONV_FIX_A64(uq, d, 64, float64, 64, uint64) +VFP_CONV_FIX(sh, s, 32, float32, 32, int16) +VFP_CONV_FIX(sl, s, 32, float32, 32, int32) +VFP_CONV_FIX_A64(sq, s, 32, float32, 64, int64) +VFP_CONV_FIX(uh, s, 32, float32, 32, uint16) +VFP_CONV_FIX(ul, s, 32, float32, 32, uint32) +VFP_CONV_FIX_A64(uq, s, 32, float32, 64, uint64) +VFP_CONV_FIX(sh, h, 16, dh_ctype_f16, 32, int16) +VFP_CONV_FIX(sl, h, 16, dh_ctype_f16, 32, int32) +VFP_CONV_FIX_A64(sq, h, 16, dh_ctype_f16, 64, int64) +VFP_CONV_FIX(uh, h, 16, dh_ctype_f16, 32, uint16) +VFP_CONV_FIX(ul, h, 16, dh_ctype_f16, 32, uint32) +VFP_CONV_FIX_A64(uq, h, 16, dh_ctype_f16, 64, uint64) +VFP_CONV_FLOAT_FIX_ROUND(sq, d, 64, float64, 64, int64, + float_round_to_zero, _round_to_zero) +VFP_CONV_FLOAT_FIX_ROUND(uq, d, 64, float64, 64, uint64, + float_round_to_zero, _round_to_zero) + +#undef VFP_CONV_FIX +#undef VFP_CONV_FIX_FLOAT +#undef VFP_CONV_FLOAT_FIX_ROUND +#undef VFP_CONV_FIX_A64 + +/* Set the current fp rounding mode and return the old one. + * The argument is a softfloat float_round_ value. + */ +uint32_t HELPER(set_rmode)(uint32_t rmode, float_status *fp_status) +{ + uint32_t prev_rmode = get_float_rounding_mode(fp_status); + set_float_rounding_mode(rmode, fp_status); + + return prev_rmode; +} + +/* Half precision conversions. */ +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + bool save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float32 r = float16_to_float32(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + bool save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float32_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; +} + +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + bool save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float64 r = float16_to_float64(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + bool save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float64_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; +} + +/* NEON helpers. */ + +/* Constants 256 and 512 are used in some helpers; we avoid relying on + * int->float conversions at run-time. */ +#define float64_256 make_float64(0x4070000000000000LL) +#define float64_512 make_float64(0x4080000000000000LL) +#define float16_maxnorm make_float16(0x7bff) +#define float32_maxnorm make_float32(0x7f7fffff) +#define float64_maxnorm make_float64(0x7fefffffffffffffLL) + +/* Reciprocal functions + * + * The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate + */ + +/* See RecipEstimate() + * + * input is a 9 bit fixed point number + * input range 256 .. 511 for a number from 0.5 <= x < 1.0. + * result range 256 .. 511 for a number from 1.0 to 511/256. + */ + +static int recip_estimate(int input) +{ + int a, b, r; + assert(256 <= input && input < 512); + a = (input * 2) + 1; + b = (1 << 19) / a; + r = (b + 1) >> 1; + assert(256 <= r && r < 512); + return r; +} + +/* + * Increased precision version: + * input is a 13 bit fixed point number + * input range 2048 .. 4095 for a number from 0.5 <= x < 1.0. + * result range 4096 .. 8191 for a number from 1.0 to 2.0 + */ +static int recip_estimate_incprec(int input) +{ + int a, b, r; + assert(2048 <= input && input < 4096); + a = (input * 2) + 1; + /* + * The pseudocode expresses this as an operation on infinite + * precision reals where it calculates 2^25 / a and then looks + * at the error between that and the rounded-down-to-integer + * value to see if it should instead round up. We instead + * follow the same approach as the pseudocode for the 8-bit + * precision version, and calculate (2 * (2^25 / a)) as an + * integer so we can do the "add one and halve" to round it. + * So the 1 << 26 here is correct. + */ + b = (1 << 26) / a; + r = (b + 1) >> 1; + assert(4096 <= r && r < 8192); + return r; +} + +/* + * Common wrapper to call recip_estimate + * + * The parameters are exponent and 64 bit fraction (without implicit + * bit) where the binary point is nominally at bit 52. Returns a + * float64 which can then be rounded to the appropriate size by the + * callee. + */ + +static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac, + bool increasedprecision) +{ + uint32_t scaled, estimate; + uint64_t result_frac; + int result_exp; + + /* Handle sub-normals */ + if (*exp == 0) { + if (extract64(frac, 51, 1) == 0) { + *exp = -1; + frac <<= 2; + } else { + frac <<= 1; + } + } + + if (increasedprecision) { + /* scaled = UInt('1':fraction<51:41>) */ + scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); + estimate = recip_estimate_incprec(scaled); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + estimate = recip_estimate(scaled); + } + + result_exp = exp_off - *exp; + if (increasedprecision) { + result_frac = deposit64(0, 40, 12, estimate); + } else { + result_frac = deposit64(0, 44, 8, estimate); + } + if (result_exp == 0) { + result_frac = deposit64(result_frac >> 1, 51, 1, 1); + } else if (result_exp == -1) { + result_frac = deposit64(result_frac >> 2, 50, 2, 1); + result_exp = 0; + } + + *exp = result_exp; + + return result_frac; +} + +static bool round_to_inf(float_status *fpst, bool sign_bit) +{ + switch (fpst->float_rounding_mode) { + case float_round_nearest_even: /* Round to Nearest */ + return true; + case float_round_up: /* Round to +Inf */ + return !sign_bit; + case float_round_down: /* Round to -Inf */ + return sign_bit; + case float_round_to_zero: /* Round to Zero */ + return false; + default: + g_assert_not_reached(); + } +} + +uint32_t HELPER(recpe_f16)(uint32_t input, float_status *fpst) +{ + float16 f16 = float16_squash_input_denormal(input, fpst); + uint32_t f16_val = float16_val(f16); + uint32_t f16_sign = float16_is_neg(f16); + int f16_exp = extract32(f16_val, 10, 5); + uint32_t f16_frac = extract32(f16_val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float16_silence_nan(f16, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float16_default_nan(fpst); + } + return nan; + } else if (float16_is_infinity(f16)) { + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, fpst); + return float16_set_sign(float16_infinity, float16_is_neg(f16)); + } else if (float16_abs(f16) < (1 << 8)) { + /* Abs(value) < 2.0^-16 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f16_sign)) { + return float16_set_sign(float16_infinity, f16_sign); + } else { + return float16_set_sign(float16_maxnorm, f16_sign); + } + } else if (f16_exp >= 29 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } + + f64_frac = call_recip_estimate(&f16_exp, 29, + ((uint64_t) f16_frac) << (52 - 10), false); + + /* result = sign : result_exp<4:0> : fraction<51:42> */ + f16_val = deposit32(0, 15, 1, f16_sign); + f16_val = deposit32(f16_val, 10, 5, f16_exp); + f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); + return make_float16(f16_val); +} + +/* + * FEAT_RPRES means the f32 FRECPE has an "increased precision" variant + * which is used when FPCR.AH == 1. + */ +static float32 do_recpe_f32(float32 input, float_status *fpst, bool rpres) +{ + float32 f32 = float32_squash_input_denormal(input, fpst); + uint32_t f32_val = float32_val(f32); + bool f32_sign = float32_is_neg(f32); + int f32_exp = extract32(f32_val, 23, 8); + uint32_t f32_frac = extract32(f32_val, 0, 23); + uint64_t f64_frac; + + if (float32_is_any_nan(f32)) { + float32 nan = f32; + if (float32_is_signaling_nan(f32, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float32_silence_nan(f32, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float32_default_nan(fpst); + } + return nan; + } else if (float32_is_infinity(f32)) { + return float32_set_sign(float32_zero, float32_is_neg(f32)); + } else if (float32_is_zero(f32)) { + float_raise(float_flag_divbyzero, fpst); + return float32_set_sign(float32_infinity, float32_is_neg(f32)); + } else if (float32_abs(f32) < (1ULL << 21)) { + /* Abs(value) < 2.0^-128 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f32_sign)) { + return float32_set_sign(float32_infinity, f32_sign); + } else { + return float32_set_sign(float32_maxnorm, f32_sign); + } + } else if (f32_exp >= 253 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float32_set_sign(float32_zero, float32_is_neg(f32)); + } + + f64_frac = call_recip_estimate(&f32_exp, 253, + ((uint64_t) f32_frac) << (52 - 23), rpres); + + /* result = sign : result_exp<7:0> : fraction<51:29> */ + f32_val = deposit32(0, 31, 1, f32_sign); + f32_val = deposit32(f32_val, 23, 8, f32_exp); + f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); + return make_float32(f32_val); +} + +float32 HELPER(recpe_f32)(float32 input, float_status *fpst) +{ + return do_recpe_f32(input, fpst, false); +} + +float32 HELPER(recpe_rpres_f32)(float32 input, float_status *fpst) +{ + return do_recpe_f32(input, fpst, true); +} + +float64 HELPER(recpe_f64)(float64 input, float_status *fpst) +{ + float64 f64 = float64_squash_input_denormal(input, fpst); + uint64_t f64_val = float64_val(f64); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(f64_val, 52, 11); + uint64_t f64_frac = extract64(f64_val, 0, 52); + + /* Deal with any special cases */ + if (float64_is_any_nan(f64)) { + float64 nan = f64; + if (float64_is_signaling_nan(f64, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float64_silence_nan(f64, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float64_default_nan(fpst); + } + return nan; + } else if (float64_is_infinity(f64)) { + return float64_set_sign(float64_zero, float64_is_neg(f64)); + } else if (float64_is_zero(f64)) { + float_raise(float_flag_divbyzero, fpst); + return float64_set_sign(float64_infinity, float64_is_neg(f64)); + } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { + /* Abs(value) < 2.0^-1024 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f64_sign)) { + return float64_set_sign(float64_infinity, f64_sign); + } else { + return float64_set_sign(float64_maxnorm, f64_sign); + } + } else if (f64_exp >= 2045 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float64_set_sign(float64_zero, float64_is_neg(f64)); + } + + f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac, false); + + /* result = sign : result_exp<10:0> : fraction<51:0>; */ + f64_val = deposit64(0, 63, 1, f64_sign); + f64_val = deposit64(f64_val, 52, 11, f64_exp); + f64_val = deposit64(f64_val, 0, 52, f64_frac); + return make_float64(f64_val); +} + +/* The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM. + */ + +static int do_recip_sqrt_estimate(int a) +{ + int b, estimate; + + assert(128 <= a && a < 512); + if (a < 256) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 512; + while (a * (b + 1) * (b + 1) < (1 << 28)) { + b += 1; + } + estimate = (b + 1) / 2; + assert(256 <= estimate && estimate < 512); + + return estimate; +} + +static int do_recip_sqrt_estimate_incprec(int a) +{ + /* + * The Arm ARM describes the 12-bit precision version of RecipSqrtEstimate + * in terms of an infinite-precision floating point calculation of a + * square root. We implement this using the same kind of pure integer + * algorithm as the 8-bit mantissa, to get the same bit-for-bit result. + */ + int64_t b, estimate; + + assert(1024 <= a && a < 4096); + if (a < 2048) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 8192; + while (a * (b + 1) * (b + 1) < (1ULL << 39)) { + b += 1; + } + estimate = (b + 1) / 2; + + assert(4096 <= estimate && estimate < 8192); + + return estimate; +} + +static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac, + bool increasedprecision) +{ + int estimate; + uint32_t scaled; + + if (*exp == 0) { + while (extract64(frac, 51, 1) == 0) { + frac = frac << 1; + *exp -= 1; + } + frac = extract64(frac, 0, 51) << 1; + } + + if (increasedprecision) { + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:42>) */ + scaled = deposit32(1 << 10, 0, 10, extract64(frac, 42, 10)); + } else { + /* scaled = UInt('1':fraction<51:41>) */ + scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); + } + estimate = do_recip_sqrt_estimate_incprec(scaled); + } else { + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:45>) */ + scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + } + estimate = do_recip_sqrt_estimate(scaled); + } + + *exp = (exp_off - *exp) / 2; + if (increasedprecision) { + return extract64(estimate, 0, 12) << 40; + } else { + return extract64(estimate, 0, 8) << 44; + } +} + +uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) +{ + float16 f16 = float16_squash_input_denormal(input, s); + uint16_t val = float16_val(f16); + bool f16_sign = float16_is_neg(f16); + int f16_exp = extract32(val, 10, 5); + uint16_t f16_frac = extract32(val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, s)) { + float_raise(float_flag_invalid, s); + if (!s->default_nan_mode) { + nan = float16_silence_nan(f16, s); + } + } + if (s->default_nan_mode) { + nan = float16_default_nan(s); + } + return nan; + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, s); + return float16_set_sign(float16_infinity, f16_sign); + } else if (f16_sign) { + float_raise(float_flag_invalid, s); + return float16_default_nan(s); + } else if (float16_is_infinity(f16)) { + return float16_zero; + } + + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f16_frac) << (52 - 10); + + f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac, false); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ + val = deposit32(0, 15, 1, f16_sign); + val = deposit32(val, 10, 5, f16_exp); + val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float16(val); +} + +/* + * FEAT_RPRES means the f32 FRSQRTE has an "increased precision" variant + * which is used when FPCR.AH == 1. + */ +static float32 do_rsqrte_f32(float32 input, float_status *s, bool rpres) +{ + float32 f32 = float32_squash_input_denormal(input, s); + uint32_t val = float32_val(f32); + uint32_t f32_sign = float32_is_neg(f32); + int f32_exp = extract32(val, 23, 8); + uint32_t f32_frac = extract32(val, 0, 23); + uint64_t f64_frac; + + if (float32_is_any_nan(f32)) { + float32 nan = f32; + if (float32_is_signaling_nan(f32, s)) { + float_raise(float_flag_invalid, s); + if (!s->default_nan_mode) { + nan = float32_silence_nan(f32, s); + } + } + if (s->default_nan_mode) { + nan = float32_default_nan(s); + } + return nan; + } else if (float32_is_zero(f32)) { + float_raise(float_flag_divbyzero, s); + return float32_set_sign(float32_infinity, float32_is_neg(f32)); + } else if (float32_is_neg(f32)) { + float_raise(float_flag_invalid, s); + return float32_default_nan(s); + } else if (float32_is_infinity(f32)) { + return float32_zero; + } + + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f32_frac) << 29; + + f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac, rpres); + + /* + * result = sign : result_exp<7:0> : estimate<7:0> : Zeros(15) + * or for increased precision + * result = sign : result_exp<7:0> : estimate<11:0> : Zeros(11) + */ + val = deposit32(0, 31, 1, f32_sign); + val = deposit32(val, 23, 8, f32_exp); + if (rpres) { + val = deposit32(val, 11, 12, extract64(f64_frac, 52 - 12, 12)); + } else { + val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); + } + return make_float32(val); +} + +float32 HELPER(rsqrte_f32)(float32 input, float_status *s) +{ + return do_rsqrte_f32(input, s, false); +} + +float32 HELPER(rsqrte_rpres_f32)(float32 input, float_status *s) +{ + return do_rsqrte_f32(input, s, true); +} + +float64 HELPER(rsqrte_f64)(float64 input, float_status *s) +{ + float64 f64 = float64_squash_input_denormal(input, s); + uint64_t val = float64_val(f64); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(val, 52, 11); + uint64_t f64_frac = extract64(val, 0, 52); + + if (float64_is_any_nan(f64)) { + float64 nan = f64; + if (float64_is_signaling_nan(f64, s)) { + float_raise(float_flag_invalid, s); + if (!s->default_nan_mode) { + nan = float64_silence_nan(f64, s); + } + } + if (s->default_nan_mode) { + nan = float64_default_nan(s); + } + return nan; + } else if (float64_is_zero(f64)) { + float_raise(float_flag_divbyzero, s); + return float64_set_sign(float64_infinity, float64_is_neg(f64)); + } else if (float64_is_neg(f64)) { + float_raise(float_flag_invalid, s); + return float64_default_nan(s); + } else if (float64_is_infinity(f64)) { + return float64_zero; + } + + f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac, false); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ + val = deposit64(0, 61, 1, f64_sign); + val = deposit64(val, 52, 11, f64_exp); + val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float64(val); +} + +uint32_t HELPER(recpe_u32)(uint32_t a) +{ + int input, estimate; + + if ((a & 0x80000000) == 0) { + return 0xffffffff; + } + + input = extract32(a, 23, 9); + estimate = recip_estimate(input); + + return deposit32(0, (32 - 9), 9, estimate); +} + +uint32_t HELPER(rsqrte_u32)(uint32_t a) +{ + int estimate; + + if ((a & 0xc0000000) == 0) { + return 0xffffffff; + } + + estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); + + return deposit32(0, 23, 9, estimate); +} + +/* VFPv4 fused multiply-accumulate */ +dh_ctype_f16 VFP_HELPER(muladd, h)(dh_ctype_f16 a, dh_ctype_f16 b, + dh_ctype_f16 c, float_status *fpst) +{ + return float16_muladd(a, b, c, 0, fpst); +} + +float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, + float_status *fpst) +{ + return float32_muladd(a, b, c, 0, fpst); +} + +float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, + float_status *fpst) +{ + return float64_muladd(a, b, c, 0, fpst); +} + +/* ARMv8 round to integral */ +dh_ctype_f16 HELPER(rinth_exact)(dh_ctype_f16 x, float_status *fp_status) +{ + return float16_round_to_int(x, fp_status); +} + +float32 HELPER(rints_exact)(float32 x, float_status *fp_status) +{ + return float32_round_to_int(x, fp_status); +} + +float64 HELPER(rintd_exact)(float64 x, float_status *fp_status) +{ + return float64_round_to_int(x, fp_status); +} + +dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 x, float_status *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float16 ret; + + ret = float16_round_to_int(x, fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +float32 HELPER(rints)(float32 x, float_status *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float32 ret; + + ret = float32_round_to_int(x, fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +float64 HELPER(rintd)(float64 x, float_status *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float64 ret; + + ret = float64_round_to_int(x, fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +/* Convert ARM rounding mode to softfloat */ +const FloatRoundMode arm_rmode_to_sf_map[] = { + [FPROUNDING_TIEEVEN] = float_round_nearest_even, + [FPROUNDING_POSINF] = float_round_up, + [FPROUNDING_NEGINF] = float_round_down, + [FPROUNDING_ZERO] = float_round_to_zero, + [FPROUNDING_TIEAWAY] = float_round_ties_away, + [FPROUNDING_ODD] = float_round_to_odd, +}; + +/* + * Implement float64 to int32_t conversion without saturation; + * the result is supplied modulo 2^32. + */ +uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) +{ + uint32_t frac, e_old, e_new; + bool inexact; + + e_old = get_float_exception_flags(status); + set_float_exception_flags(0, status); + frac = float64_to_int32_modulo(value, float_round_to_zero, status); + e_new = get_float_exception_flags(status); + set_float_exception_flags(e_old | e_new, status); + + /* Normal inexact, denormal with flush-to-zero, or overflow or NaN */ + inexact = e_new & (float_flag_inexact | + float_flag_input_denormal_flushed | + float_flag_invalid); + + /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ + inexact |= value == float64_chs(float64_zero); + + /* Pack the result and the env->ZF representation of Z together. */ + return deposit64(frac, 32, 32, inexact); +} + +uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) +{ + uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status[FPST_A32]); + uint32_t result = pair; + uint32_t z = (pair >> 32) == 0; + + /* Store Z, clear NCV, in FPSCR.NZCV. */ + env->vfp.fpsr = (env->vfp.fpsr & ~FPSR_NZCV_MASK) | (z * FPSR_Z); + + return result; +} + +/* Round a float32 to an integer that fits in int32_t or int64_t. */ +static float32 frint_s(float32 f, float_status *fpst, int intsize) +{ + int old_flags = get_float_exception_flags(fpst); + uint32_t exp = extract32(f, 23, 8); + + if (unlikely(exp == 0xff)) { + /* NaN or Inf. */ + goto overflow; + } + + /* Round and re-extract the exponent. */ + f = float32_round_to_int(f, fpst); + exp = extract32(f, 23, 8); + + /* Validate the range of the result. */ + if (exp < 126 + intsize) { + /* abs(F) <= INT{N}_MAX */ + return f; + } + if (exp == 126 + intsize) { + uint32_t sign = extract32(f, 31, 1); + uint32_t frac = extract32(f, 0, 23); + if (sign && frac == 0) { + /* F == INT{N}_MIN */ + return f; + } + } + + overflow: + /* + * Raise Invalid and return INT{N}_MIN as a float. Revert any + * inexact exception float32_round_to_int may have raised. + */ + set_float_exception_flags(old_flags | float_flag_invalid, fpst); + return (0x100u + 126u + intsize) << 23; +} + +float32 HELPER(frint32_s)(float32 f, float_status *fpst) +{ + return frint_s(f, fpst, 32); +} + +float32 HELPER(frint64_s)(float32 f, float_status *fpst) +{ + return frint_s(f, fpst, 64); +} + +/* Round a float64 to an integer that fits in int32_t or int64_t. */ +static float64 frint_d(float64 f, float_status *fpst, int intsize) +{ + int old_flags = get_float_exception_flags(fpst); + uint32_t exp = extract64(f, 52, 11); + + if (unlikely(exp == 0x7ff)) { + /* NaN or Inf. */ + goto overflow; + } + + /* Round and re-extract the exponent. */ + f = float64_round_to_int(f, fpst); + exp = extract64(f, 52, 11); + + /* Validate the range of the result. */ + if (exp < 1022 + intsize) { + /* abs(F) <= INT{N}_MAX */ + return f; + } + if (exp == 1022 + intsize) { + uint64_t sign = extract64(f, 63, 1); + uint64_t frac = extract64(f, 0, 52); + if (sign && frac == 0) { + /* F == INT{N}_MIN */ + return f; + } + } + + overflow: + /* + * Raise Invalid and return INT{N}_MIN as a float. Revert any + * inexact exception float64_round_to_int may have raised. + */ + set_float_exception_flags(old_flags | float_flag_invalid, fpst); + return (uint64_t)(0x800 + 1022 + intsize) << 52; +} + +float64 HELPER(frint32_d)(float64 f, float_status *fpst) +{ + return frint_d(f, fpst, 32); +} + +float64 HELPER(frint64_d)(float64 f, float_status *fpst) +{ + return frint_d(f, fpst, 64); +} + +void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) +{ + uint32_t syndrome; + + switch (reg) { + case ARM_VFP_MVFR0: + case ARM_VFP_MVFR1: + case ARM_VFP_MVFR2: + if (!(arm_hcr_el2_eff(env) & HCR_TID3)) { + return; + } + break; + case ARM_VFP_FPSID: + if (!(arm_hcr_el2_eff(env) & HCR_TID0)) { + return; + } + break; + default: + g_assert_not_reached(); + } + + syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) + | ARM_EL_IL + | (1 << 24) | (0xe << 20) | (7 << 14) + | (reg << 10) | (rt << 5) | 1); + + raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); +} diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 5d424477a2..0e849d8d4d 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -23,13 +23,6 @@ #include "internals.h" #include "cpu-features.h" #include "fpu/softfloat.h" -#ifdef CONFIG_TCG -#include "qemu/log.h" -#endif - -/* VFP support. We follow the convention used for VFP instructions: - Single precision routines have a "s" suffix, double precision a - "d" suffix. */ /* * Set the float_status behaviour to match the Arm defaults: @@ -419,1105 +412,3 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val) { HELPER(vfp_set_fpscr)(env, val); } - -#ifdef CONFIG_TCG - -#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) - -#define VFP_BINOP(name) \ -dh_ctype_f16 VFP_HELPER(name, h)(dh_ctype_f16 a, dh_ctype_f16 b, float_status *fpst) \ -{ \ - return float16_ ## name(a, b, fpst); \ -} \ -float32 VFP_HELPER(name, s)(float32 a, float32 b, float_status *fpst) \ -{ \ - return float32_ ## name(a, b, fpst); \ -} \ -float64 VFP_HELPER(name, d)(float64 a, float64 b, float_status *fpst) \ -{ \ - return float64_ ## name(a, b, fpst); \ -} -VFP_BINOP(add) -VFP_BINOP(sub) -VFP_BINOP(mul) -VFP_BINOP(div) -VFP_BINOP(min) -VFP_BINOP(max) -VFP_BINOP(minnum) -VFP_BINOP(maxnum) -#undef VFP_BINOP - -dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, float_status *fpst) -{ - return float16_sqrt(a, fpst); -} - -float32 VFP_HELPER(sqrt, s)(float32 a, float_status *fpst) -{ - return float32_sqrt(a, fpst); -} - -float64 VFP_HELPER(sqrt, d)(float64 a, float_status *fpst) -{ - return float64_sqrt(a, fpst); -} - -static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) -{ - uint32_t flags; - switch (cmp) { - case float_relation_equal: - flags = 0x6; - break; - case float_relation_less: - flags = 0x8; - break; - case float_relation_greater: - flags = 0x2; - break; - case float_relation_unordered: - flags = 0x3; - break; - default: - g_assert_not_reached(); - } - env->vfp.fpsr = deposit64(env->vfp.fpsr, 28, 4, flags); /* NZCV */ -} - -/* XXX: check quiet/signaling case */ -#define DO_VFP_cmp(P, FLOATTYPE, ARGTYPE, FPST) \ -void VFP_HELPER(cmp, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ -{ \ - softfloat_to_vfp_compare(env, \ - FLOATTYPE ## _compare_quiet(a, b, &env->vfp.fp_status[FPST])); \ -} \ -void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ -{ \ - softfloat_to_vfp_compare(env, \ - FLOATTYPE ## _compare(a, b, &env->vfp.fp_status[FPST])); \ -} -DO_VFP_cmp(h, float16, dh_ctype_f16, FPST_A32_F16) -DO_VFP_cmp(s, float32, float32, FPST_A32) -DO_VFP_cmp(d, float64, float64, FPST_A32) -#undef DO_VFP_cmp - -/* Integer to float and float to integer conversions */ - -#define CONV_ITOF(name, ftype, fsz, sign) \ -ftype HELPER(name)(uint32_t x, float_status *fpst) \ -{ \ - return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ -} - -#define CONV_FTOI(name, ftype, fsz, sign, round) \ -sign##int32_t HELPER(name)(ftype x, float_status *fpst) \ -{ \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##sign##int32##round(x, fpst); \ -} - -#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ - CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ - CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ - CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) - -FLOAT_CONVS(si, h, uint32_t, 16, ) -FLOAT_CONVS(si, s, float32, 32, ) -FLOAT_CONVS(si, d, float64, 64, ) -FLOAT_CONVS(ui, h, uint32_t, 16, u) -FLOAT_CONVS(ui, s, float32, 32, u) -FLOAT_CONVS(ui, d, float64, 64, u) - -#undef CONV_ITOF -#undef CONV_FTOI -#undef FLOAT_CONVS - -/* floating point conversion */ -float64 VFP_HELPER(fcvtd, s)(float32 x, float_status *status) -{ - return float32_to_float64(x, status); -} - -float32 VFP_HELPER(fcvts, d)(float64 x, float_status *status) -{ - return float64_to_float32(x, status); -} - -uint32_t HELPER(bfcvt)(float32 x, float_status *status) -{ - return float32_to_bfloat16(x, status); -} - -uint32_t HELPER(bfcvt_pair)(uint64_t pair, float_status *status) -{ - bfloat16 lo = float32_to_bfloat16(extract64(pair, 0, 32), status); - bfloat16 hi = float32_to_bfloat16(extract64(pair, 32, 32), status); - return deposit32(lo, 16, 16, hi); -} - -/* - * VFP3 fixed point conversion. The AArch32 versions of fix-to-float - * must always round-to-nearest; the AArch64 ones honour the FPSCR - * rounding mode. (For AArch32 Neon the standard-FPSCR is set to - * round-to-nearest so either helper will work.) AArch32 float-to-fix - * must round-to-zero. - */ -#define VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ -ftype HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ - float_status *fpst) \ -{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpst); } - -#define VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ - ftype HELPER(vfp_##name##to##p##_round_to_nearest)(uint##isz##_t x, \ - uint32_t shift, \ - float_status *fpst) \ - { \ - ftype ret; \ - FloatRoundMode oldmode = fpst->float_rounding_mode; \ - fpst->float_rounding_mode = float_round_nearest_even; \ - ret = itype##_to_##float##fsz##_scalbn(x, -shift, fpst); \ - fpst->float_rounding_mode = oldmode; \ - return ret; \ - } - -#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, ROUND, suff) \ -uint##isz##_t HELPER(vfp_to##name##p##suff)(ftype x, uint32_t shift, \ - float_status *fpst) \ -{ \ - if (unlikely(float##fsz##_is_any_nan(x))) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ -} - -#define VFP_CONV_FIX(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ - float_round_to_zero, _round_to_zero) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ - get_float_rounding_mode(fpst), ) - -#define VFP_CONV_FIX_A64(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ - get_float_rounding_mode(fpst), ) - -VFP_CONV_FIX(sh, d, 64, float64, 64, int16) -VFP_CONV_FIX(sl, d, 64, float64, 64, int32) -VFP_CONV_FIX_A64(sq, d, 64, float64, 64, int64) -VFP_CONV_FIX(uh, d, 64, float64, 64, uint16) -VFP_CONV_FIX(ul, d, 64, float64, 64, uint32) -VFP_CONV_FIX_A64(uq, d, 64, float64, 64, uint64) -VFP_CONV_FIX(sh, s, 32, float32, 32, int16) -VFP_CONV_FIX(sl, s, 32, float32, 32, int32) -VFP_CONV_FIX_A64(sq, s, 32, float32, 64, int64) -VFP_CONV_FIX(uh, s, 32, float32, 32, uint16) -VFP_CONV_FIX(ul, s, 32, float32, 32, uint32) -VFP_CONV_FIX_A64(uq, s, 32, float32, 64, uint64) -VFP_CONV_FIX(sh, h, 16, dh_ctype_f16, 32, int16) -VFP_CONV_FIX(sl, h, 16, dh_ctype_f16, 32, int32) -VFP_CONV_FIX_A64(sq, h, 16, dh_ctype_f16, 64, int64) -VFP_CONV_FIX(uh, h, 16, dh_ctype_f16, 32, uint16) -VFP_CONV_FIX(ul, h, 16, dh_ctype_f16, 32, uint32) -VFP_CONV_FIX_A64(uq, h, 16, dh_ctype_f16, 64, uint64) -VFP_CONV_FLOAT_FIX_ROUND(sq, d, 64, float64, 64, int64, - float_round_to_zero, _round_to_zero) -VFP_CONV_FLOAT_FIX_ROUND(uq, d, 64, float64, 64, uint64, - float_round_to_zero, _round_to_zero) - -#undef VFP_CONV_FIX -#undef VFP_CONV_FIX_FLOAT -#undef VFP_CONV_FLOAT_FIX_ROUND -#undef VFP_CONV_FIX_A64 - -/* Set the current fp rounding mode and return the old one. - * The argument is a softfloat float_round_ value. - */ -uint32_t HELPER(set_rmode)(uint32_t rmode, float_status *fp_status) -{ - uint32_t prev_rmode = get_float_rounding_mode(fp_status); - set_float_rounding_mode(rmode, fp_status); - - return prev_rmode; -} - -/* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, float_status *fpst, - uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing input denormals. - */ - bool save = get_flush_inputs_to_zero(fpst); - set_flush_inputs_to_zero(false, fpst); - float32 r = float16_to_float32(a, !ahp_mode, fpst); - set_flush_inputs_to_zero(save, fpst); - return r; -} - -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, float_status *fpst, - uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing output denormals. - */ - bool save = get_flush_to_zero(fpst); - set_flush_to_zero(false, fpst); - float16 r = float32_to_float16(a, !ahp_mode, fpst); - set_flush_to_zero(save, fpst); - return r; -} - -float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, float_status *fpst, - uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing input denormals. - */ - bool save = get_flush_inputs_to_zero(fpst); - set_flush_inputs_to_zero(false, fpst); - float64 r = float16_to_float64(a, !ahp_mode, fpst); - set_flush_inputs_to_zero(save, fpst); - return r; -} - -uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, float_status *fpst, - uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing output denormals. - */ - bool save = get_flush_to_zero(fpst); - set_flush_to_zero(false, fpst); - float16 r = float64_to_float16(a, !ahp_mode, fpst); - set_flush_to_zero(save, fpst); - return r; -} - -/* NEON helpers. */ - -/* Constants 256 and 512 are used in some helpers; we avoid relying on - * int->float conversions at run-time. */ -#define float64_256 make_float64(0x4070000000000000LL) -#define float64_512 make_float64(0x4080000000000000LL) -#define float16_maxnorm make_float16(0x7bff) -#define float32_maxnorm make_float32(0x7f7fffff) -#define float64_maxnorm make_float64(0x7fefffffffffffffLL) - -/* Reciprocal functions - * - * The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate - */ - -/* See RecipEstimate() - * - * input is a 9 bit fixed point number - * input range 256 .. 511 for a number from 0.5 <= x < 1.0. - * result range 256 .. 511 for a number from 1.0 to 511/256. - */ - -static int recip_estimate(int input) -{ - int a, b, r; - assert(256 <= input && input < 512); - a = (input * 2) + 1; - b = (1 << 19) / a; - r = (b + 1) >> 1; - assert(256 <= r && r < 512); - return r; -} - -/* - * Increased precision version: - * input is a 13 bit fixed point number - * input range 2048 .. 4095 for a number from 0.5 <= x < 1.0. - * result range 4096 .. 8191 for a number from 1.0 to 2.0 - */ -static int recip_estimate_incprec(int input) -{ - int a, b, r; - assert(2048 <= input && input < 4096); - a = (input * 2) + 1; - /* - * The pseudocode expresses this as an operation on infinite - * precision reals where it calculates 2^25 / a and then looks - * at the error between that and the rounded-down-to-integer - * value to see if it should instead round up. We instead - * follow the same approach as the pseudocode for the 8-bit - * precision version, and calculate (2 * (2^25 / a)) as an - * integer so we can do the "add one and halve" to round it. - * So the 1 << 26 here is correct. - */ - b = (1 << 26) / a; - r = (b + 1) >> 1; - assert(4096 <= r && r < 8192); - return r; -} - -/* - * Common wrapper to call recip_estimate - * - * The parameters are exponent and 64 bit fraction (without implicit - * bit) where the binary point is nominally at bit 52. Returns a - * float64 which can then be rounded to the appropriate size by the - * callee. - */ - -static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac, - bool increasedprecision) -{ - uint32_t scaled, estimate; - uint64_t result_frac; - int result_exp; - - /* Handle sub-normals */ - if (*exp == 0) { - if (extract64(frac, 51, 1) == 0) { - *exp = -1; - frac <<= 2; - } else { - frac <<= 1; - } - } - - if (increasedprecision) { - /* scaled = UInt('1':fraction<51:41>) */ - scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); - estimate = recip_estimate_incprec(scaled); - } else { - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - estimate = recip_estimate(scaled); - } - - result_exp = exp_off - *exp; - if (increasedprecision) { - result_frac = deposit64(0, 40, 12, estimate); - } else { - result_frac = deposit64(0, 44, 8, estimate); - } - if (result_exp == 0) { - result_frac = deposit64(result_frac >> 1, 51, 1, 1); - } else if (result_exp == -1) { - result_frac = deposit64(result_frac >> 2, 50, 2, 1); - result_exp = 0; - } - - *exp = result_exp; - - return result_frac; -} - -static bool round_to_inf(float_status *fpst, bool sign_bit) -{ - switch (fpst->float_rounding_mode) { - case float_round_nearest_even: /* Round to Nearest */ - return true; - case float_round_up: /* Round to +Inf */ - return !sign_bit; - case float_round_down: /* Round to -Inf */ - return sign_bit; - case float_round_to_zero: /* Round to Zero */ - return false; - default: - g_assert_not_reached(); - } -} - -uint32_t HELPER(recpe_f16)(uint32_t input, float_status *fpst) -{ - float16 f16 = float16_squash_input_denormal(input, fpst); - uint32_t f16_val = float16_val(f16); - uint32_t f16_sign = float16_is_neg(f16); - int f16_exp = extract32(f16_val, 10, 5); - uint32_t f16_frac = extract32(f16_val, 0, 10); - uint64_t f64_frac; - - if (float16_is_any_nan(f16)) { - float16 nan = f16; - if (float16_is_signaling_nan(f16, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float16_silence_nan(f16, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float16_default_nan(fpst); - } - return nan; - } else if (float16_is_infinity(f16)) { - return float16_set_sign(float16_zero, float16_is_neg(f16)); - } else if (float16_is_zero(f16)) { - float_raise(float_flag_divbyzero, fpst); - return float16_set_sign(float16_infinity, float16_is_neg(f16)); - } else if (float16_abs(f16) < (1 << 8)) { - /* Abs(value) < 2.0^-16 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f16_sign)) { - return float16_set_sign(float16_infinity, f16_sign); - } else { - return float16_set_sign(float16_maxnorm, f16_sign); - } - } else if (f16_exp >= 29 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float16_set_sign(float16_zero, float16_is_neg(f16)); - } - - f64_frac = call_recip_estimate(&f16_exp, 29, - ((uint64_t) f16_frac) << (52 - 10), false); - - /* result = sign : result_exp<4:0> : fraction<51:42> */ - f16_val = deposit32(0, 15, 1, f16_sign); - f16_val = deposit32(f16_val, 10, 5, f16_exp); - f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); - return make_float16(f16_val); -} - -/* - * FEAT_RPRES means the f32 FRECPE has an "increased precision" variant - * which is used when FPCR.AH == 1. - */ -static float32 do_recpe_f32(float32 input, float_status *fpst, bool rpres) -{ - float32 f32 = float32_squash_input_denormal(input, fpst); - uint32_t f32_val = float32_val(f32); - bool f32_sign = float32_is_neg(f32); - int f32_exp = extract32(f32_val, 23, 8); - uint32_t f32_frac = extract32(f32_val, 0, 23); - uint64_t f64_frac; - - if (float32_is_any_nan(f32)) { - float32 nan = f32; - if (float32_is_signaling_nan(f32, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float32_silence_nan(f32, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float32_default_nan(fpst); - } - return nan; - } else if (float32_is_infinity(f32)) { - return float32_set_sign(float32_zero, float32_is_neg(f32)); - } else if (float32_is_zero(f32)) { - float_raise(float_flag_divbyzero, fpst); - return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if (float32_abs(f32) < (1ULL << 21)) { - /* Abs(value) < 2.0^-128 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f32_sign)) { - return float32_set_sign(float32_infinity, f32_sign); - } else { - return float32_set_sign(float32_maxnorm, f32_sign); - } - } else if (f32_exp >= 253 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float32_set_sign(float32_zero, float32_is_neg(f32)); - } - - f64_frac = call_recip_estimate(&f32_exp, 253, - ((uint64_t) f32_frac) << (52 - 23), rpres); - - /* result = sign : result_exp<7:0> : fraction<51:29> */ - f32_val = deposit32(0, 31, 1, f32_sign); - f32_val = deposit32(f32_val, 23, 8, f32_exp); - f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); - return make_float32(f32_val); -} - -float32 HELPER(recpe_f32)(float32 input, float_status *fpst) -{ - return do_recpe_f32(input, fpst, false); -} - -float32 HELPER(recpe_rpres_f32)(float32 input, float_status *fpst) -{ - return do_recpe_f32(input, fpst, true); -} - -float64 HELPER(recpe_f64)(float64 input, float_status *fpst) -{ - float64 f64 = float64_squash_input_denormal(input, fpst); - uint64_t f64_val = float64_val(f64); - bool f64_sign = float64_is_neg(f64); - int f64_exp = extract64(f64_val, 52, 11); - uint64_t f64_frac = extract64(f64_val, 0, 52); - - /* Deal with any special cases */ - if (float64_is_any_nan(f64)) { - float64 nan = f64; - if (float64_is_signaling_nan(f64, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float64_silence_nan(f64, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float64_default_nan(fpst); - } - return nan; - } else if (float64_is_infinity(f64)) { - return float64_set_sign(float64_zero, float64_is_neg(f64)); - } else if (float64_is_zero(f64)) { - float_raise(float_flag_divbyzero, fpst); - return float64_set_sign(float64_infinity, float64_is_neg(f64)); - } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { - /* Abs(value) < 2.0^-1024 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f64_sign)) { - return float64_set_sign(float64_infinity, f64_sign); - } else { - return float64_set_sign(float64_maxnorm, f64_sign); - } - } else if (f64_exp >= 2045 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float64_set_sign(float64_zero, float64_is_neg(f64)); - } - - f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac, false); - - /* result = sign : result_exp<10:0> : fraction<51:0>; */ - f64_val = deposit64(0, 63, 1, f64_sign); - f64_val = deposit64(f64_val, 52, 11, f64_exp); - f64_val = deposit64(f64_val, 0, 52, f64_frac); - return make_float64(f64_val); -} - -/* The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM. - */ - -static int do_recip_sqrt_estimate(int a) -{ - int b, estimate; - - assert(128 <= a && a < 512); - if (a < 256) { - a = a * 2 + 1; - } else { - a = (a >> 1) << 1; - a = (a + 1) * 2; - } - b = 512; - while (a * (b + 1) * (b + 1) < (1 << 28)) { - b += 1; - } - estimate = (b + 1) / 2; - assert(256 <= estimate && estimate < 512); - - return estimate; -} - -static int do_recip_sqrt_estimate_incprec(int a) -{ - /* - * The Arm ARM describes the 12-bit precision version of RecipSqrtEstimate - * in terms of an infinite-precision floating point calculation of a - * square root. We implement this using the same kind of pure integer - * algorithm as the 8-bit mantissa, to get the same bit-for-bit result. - */ - int64_t b, estimate; - - assert(1024 <= a && a < 4096); - if (a < 2048) { - a = a * 2 + 1; - } else { - a = (a >> 1) << 1; - a = (a + 1) * 2; - } - b = 8192; - while (a * (b + 1) * (b + 1) < (1ULL << 39)) { - b += 1; - } - estimate = (b + 1) / 2; - - assert(4096 <= estimate && estimate < 8192); - - return estimate; -} - -static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac, - bool increasedprecision) -{ - int estimate; - uint32_t scaled; - - if (*exp == 0) { - while (extract64(frac, 51, 1) == 0) { - frac = frac << 1; - *exp -= 1; - } - frac = extract64(frac, 0, 51) << 1; - } - - if (increasedprecision) { - if (*exp & 1) { - /* scaled = UInt('01':fraction<51:42>) */ - scaled = deposit32(1 << 10, 0, 10, extract64(frac, 42, 10)); - } else { - /* scaled = UInt('1':fraction<51:41>) */ - scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); - } - estimate = do_recip_sqrt_estimate_incprec(scaled); - } else { - if (*exp & 1) { - /* scaled = UInt('01':fraction<51:45>) */ - scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); - } else { - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - } - estimate = do_recip_sqrt_estimate(scaled); - } - - *exp = (exp_off - *exp) / 2; - if (increasedprecision) { - return extract64(estimate, 0, 12) << 40; - } else { - return extract64(estimate, 0, 8) << 44; - } -} - -uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) -{ - float16 f16 = float16_squash_input_denormal(input, s); - uint16_t val = float16_val(f16); - bool f16_sign = float16_is_neg(f16); - int f16_exp = extract32(val, 10, 5); - uint16_t f16_frac = extract32(val, 0, 10); - uint64_t f64_frac; - - if (float16_is_any_nan(f16)) { - float16 nan = f16; - if (float16_is_signaling_nan(f16, s)) { - float_raise(float_flag_invalid, s); - if (!s->default_nan_mode) { - nan = float16_silence_nan(f16, s); - } - } - if (s->default_nan_mode) { - nan = float16_default_nan(s); - } - return nan; - } else if (float16_is_zero(f16)) { - float_raise(float_flag_divbyzero, s); - return float16_set_sign(float16_infinity, f16_sign); - } else if (f16_sign) { - float_raise(float_flag_invalid, s); - return float16_default_nan(s); - } else if (float16_is_infinity(f16)) { - return float16_zero; - } - - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - f64_frac = ((uint64_t) f16_frac) << (52 - 10); - - f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac, false); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ - val = deposit32(0, 15, 1, f16_sign); - val = deposit32(val, 10, 5, f16_exp); - val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float16(val); -} - -/* - * FEAT_RPRES means the f32 FRSQRTE has an "increased precision" variant - * which is used when FPCR.AH == 1. - */ -static float32 do_rsqrte_f32(float32 input, float_status *s, bool rpres) -{ - float32 f32 = float32_squash_input_denormal(input, s); - uint32_t val = float32_val(f32); - uint32_t f32_sign = float32_is_neg(f32); - int f32_exp = extract32(val, 23, 8); - uint32_t f32_frac = extract32(val, 0, 23); - uint64_t f64_frac; - - if (float32_is_any_nan(f32)) { - float32 nan = f32; - if (float32_is_signaling_nan(f32, s)) { - float_raise(float_flag_invalid, s); - if (!s->default_nan_mode) { - nan = float32_silence_nan(f32, s); - } - } - if (s->default_nan_mode) { - nan = float32_default_nan(s); - } - return nan; - } else if (float32_is_zero(f32)) { - float_raise(float_flag_divbyzero, s); - return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if (float32_is_neg(f32)) { - float_raise(float_flag_invalid, s); - return float32_default_nan(s); - } else if (float32_is_infinity(f32)) { - return float32_zero; - } - - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - f64_frac = ((uint64_t) f32_frac) << 29; - - f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac, rpres); - - /* - * result = sign : result_exp<7:0> : estimate<7:0> : Zeros(15) - * or for increased precision - * result = sign : result_exp<7:0> : estimate<11:0> : Zeros(11) - */ - val = deposit32(0, 31, 1, f32_sign); - val = deposit32(val, 23, 8, f32_exp); - if (rpres) { - val = deposit32(val, 11, 12, extract64(f64_frac, 52 - 12, 12)); - } else { - val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); - } - return make_float32(val); -} - -float32 HELPER(rsqrte_f32)(float32 input, float_status *s) -{ - return do_rsqrte_f32(input, s, false); -} - -float32 HELPER(rsqrte_rpres_f32)(float32 input, float_status *s) -{ - return do_rsqrte_f32(input, s, true); -} - -float64 HELPER(rsqrte_f64)(float64 input, float_status *s) -{ - float64 f64 = float64_squash_input_denormal(input, s); - uint64_t val = float64_val(f64); - bool f64_sign = float64_is_neg(f64); - int f64_exp = extract64(val, 52, 11); - uint64_t f64_frac = extract64(val, 0, 52); - - if (float64_is_any_nan(f64)) { - float64 nan = f64; - if (float64_is_signaling_nan(f64, s)) { - float_raise(float_flag_invalid, s); - if (!s->default_nan_mode) { - nan = float64_silence_nan(f64, s); - } - } - if (s->default_nan_mode) { - nan = float64_default_nan(s); - } - return nan; - } else if (float64_is_zero(f64)) { - float_raise(float_flag_divbyzero, s); - return float64_set_sign(float64_infinity, float64_is_neg(f64)); - } else if (float64_is_neg(f64)) { - float_raise(float_flag_invalid, s); - return float64_default_nan(s); - } else if (float64_is_infinity(f64)) { - return float64_zero; - } - - f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac, false); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ - val = deposit64(0, 61, 1, f64_sign); - val = deposit64(val, 52, 11, f64_exp); - val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float64(val); -} - -uint32_t HELPER(recpe_u32)(uint32_t a) -{ - int input, estimate; - - if ((a & 0x80000000) == 0) { - return 0xffffffff; - } - - input = extract32(a, 23, 9); - estimate = recip_estimate(input); - - return deposit32(0, (32 - 9), 9, estimate); -} - -uint32_t HELPER(rsqrte_u32)(uint32_t a) -{ - int estimate; - - if ((a & 0xc0000000) == 0) { - return 0xffffffff; - } - - estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); - - return deposit32(0, 23, 9, estimate); -} - -/* VFPv4 fused multiply-accumulate */ -dh_ctype_f16 VFP_HELPER(muladd, h)(dh_ctype_f16 a, dh_ctype_f16 b, - dh_ctype_f16 c, float_status *fpst) -{ - return float16_muladd(a, b, c, 0, fpst); -} - -float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, - float_status *fpst) -{ - return float32_muladd(a, b, c, 0, fpst); -} - -float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, - float_status *fpst) -{ - return float64_muladd(a, b, c, 0, fpst); -} - -/* ARMv8 round to integral */ -dh_ctype_f16 HELPER(rinth_exact)(dh_ctype_f16 x, float_status *fp_status) -{ - return float16_round_to_int(x, fp_status); -} - -float32 HELPER(rints_exact)(float32 x, float_status *fp_status) -{ - return float32_round_to_int(x, fp_status); -} - -float64 HELPER(rintd_exact)(float64 x, float_status *fp_status) -{ - return float64_round_to_int(x, fp_status); -} - -dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 x, float_status *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float16 ret; - - ret = float16_round_to_int(x, fp_status); - - /* Suppress any inexact exceptions the conversion produced */ - if (!(old_flags & float_flag_inexact)) { - new_flags = get_float_exception_flags(fp_status); - set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); - } - - return ret; -} - -float32 HELPER(rints)(float32 x, float_status *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float32 ret; - - ret = float32_round_to_int(x, fp_status); - - /* Suppress any inexact exceptions the conversion produced */ - if (!(old_flags & float_flag_inexact)) { - new_flags = get_float_exception_flags(fp_status); - set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); - } - - return ret; -} - -float64 HELPER(rintd)(float64 x, float_status *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float64 ret; - - ret = float64_round_to_int(x, fp_status); - - /* Suppress any inexact exceptions the conversion produced */ - if (!(old_flags & float_flag_inexact)) { - new_flags = get_float_exception_flags(fp_status); - set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); - } - - return ret; -} - -/* Convert ARM rounding mode to softfloat */ -const FloatRoundMode arm_rmode_to_sf_map[] = { - [FPROUNDING_TIEEVEN] = float_round_nearest_even, - [FPROUNDING_POSINF] = float_round_up, - [FPROUNDING_NEGINF] = float_round_down, - [FPROUNDING_ZERO] = float_round_to_zero, - [FPROUNDING_TIEAWAY] = float_round_ties_away, - [FPROUNDING_ODD] = float_round_to_odd, -}; - -/* - * Implement float64 to int32_t conversion without saturation; - * the result is supplied modulo 2^32. - */ -uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) -{ - uint32_t frac, e_old, e_new; - bool inexact; - - e_old = get_float_exception_flags(status); - set_float_exception_flags(0, status); - frac = float64_to_int32_modulo(value, float_round_to_zero, status); - e_new = get_float_exception_flags(status); - set_float_exception_flags(e_old | e_new, status); - - /* Normal inexact, denormal with flush-to-zero, or overflow or NaN */ - inexact = e_new & (float_flag_inexact | - float_flag_input_denormal_flushed | - float_flag_invalid); - - /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ - inexact |= value == float64_chs(float64_zero); - - /* Pack the result and the env->ZF representation of Z together. */ - return deposit64(frac, 32, 32, inexact); -} - -uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) -{ - uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status[FPST_A32]); - uint32_t result = pair; - uint32_t z = (pair >> 32) == 0; - - /* Store Z, clear NCV, in FPSCR.NZCV. */ - env->vfp.fpsr = (env->vfp.fpsr & ~FPSR_NZCV_MASK) | (z * FPSR_Z); - - return result; -} - -/* Round a float32 to an integer that fits in int32_t or int64_t. */ -static float32 frint_s(float32 f, float_status *fpst, int intsize) -{ - int old_flags = get_float_exception_flags(fpst); - uint32_t exp = extract32(f, 23, 8); - - if (unlikely(exp == 0xff)) { - /* NaN or Inf. */ - goto overflow; - } - - /* Round and re-extract the exponent. */ - f = float32_round_to_int(f, fpst); - exp = extract32(f, 23, 8); - - /* Validate the range of the result. */ - if (exp < 126 + intsize) { - /* abs(F) <= INT{N}_MAX */ - return f; - } - if (exp == 126 + intsize) { - uint32_t sign = extract32(f, 31, 1); - uint32_t frac = extract32(f, 0, 23); - if (sign && frac == 0) { - /* F == INT{N}_MIN */ - return f; - } - } - - overflow: - /* - * Raise Invalid and return INT{N}_MIN as a float. Revert any - * inexact exception float32_round_to_int may have raised. - */ - set_float_exception_flags(old_flags | float_flag_invalid, fpst); - return (0x100u + 126u + intsize) << 23; -} - -float32 HELPER(frint32_s)(float32 f, float_status *fpst) -{ - return frint_s(f, fpst, 32); -} - -float32 HELPER(frint64_s)(float32 f, float_status *fpst) -{ - return frint_s(f, fpst, 64); -} - -/* Round a float64 to an integer that fits in int32_t or int64_t. */ -static float64 frint_d(float64 f, float_status *fpst, int intsize) -{ - int old_flags = get_float_exception_flags(fpst); - uint32_t exp = extract64(f, 52, 11); - - if (unlikely(exp == 0x7ff)) { - /* NaN or Inf. */ - goto overflow; - } - - /* Round and re-extract the exponent. */ - f = float64_round_to_int(f, fpst); - exp = extract64(f, 52, 11); - - /* Validate the range of the result. */ - if (exp < 1022 + intsize) { - /* abs(F) <= INT{N}_MAX */ - return f; - } - if (exp == 1022 + intsize) { - uint64_t sign = extract64(f, 63, 1); - uint64_t frac = extract64(f, 0, 52); - if (sign && frac == 0) { - /* F == INT{N}_MIN */ - return f; - } - } - - overflow: - /* - * Raise Invalid and return INT{N}_MIN as a float. Revert any - * inexact exception float64_round_to_int may have raised. - */ - set_float_exception_flags(old_flags | float_flag_invalid, fpst); - return (uint64_t)(0x800 + 1022 + intsize) << 52; -} - -float64 HELPER(frint32_d)(float64 f, float_status *fpst) -{ - return frint_d(f, fpst, 32); -} - -float64 HELPER(frint64_d)(float64 f, float_status *fpst) -{ - return frint_d(f, fpst, 64); -} - -void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) -{ - uint32_t syndrome; - - switch (reg) { - case ARM_VFP_MVFR0: - case ARM_VFP_MVFR1: - case ARM_VFP_MVFR2: - if (!(arm_hcr_el2_eff(env) & HCR_TID3)) { - return; - } - break; - case ARM_VFP_FPSID: - if (!(arm_hcr_el2_eff(env) & HCR_TID0)) { - return; - } - break; - default: - g_assert_not_reached(); - } - - syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) - | ARM_EL_IL - | (1 << 24) | (0xe << 20) | (7 << 14) - | (reg << 10) | (rt << 5) | 1); - - raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); -} - -#endif From e34cfba5e8d7bd631398a09d658dee40b1aef085 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Feb 2025 19:09:54 +0000 Subject: [PATCH 1955/2892] target/arm: Move FPSCR get/set helpers to tcg/vfp_helper.c Currently the helper_vfp_get_fpscr() and helper_vfp_set_fpscr() functions do the actual work of updating the FPSCR, and we have wrappers vfp_get_fpscr() and vfp_set_fpscr() which we use for calls from other QEMU C code. Flip these around so that it is vfp_get_fpscr() and vfp_set_fpscr() which do the actual work, and helper_vfp_get_fpscr() and helper_vfp_set_fpscr() which are the wrappers; this allows us to move them to tcg/vfp_helper.c. Since this is the last HELPER() we had in arm/vfp_helper.c, we can drop the include of helper-proto.h. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250221190957.811948-3-peter.maydell@linaro.org --- target/arm/tcg/vfp_helper.c | 10 ++++++++++ target/arm/vfp_helper.c | 15 ++------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c index aa580ff64c..cd6e0d0eda 100644 --- a/target/arm/tcg/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -1128,3 +1128,13 @@ void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); } + +uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +{ + return vfp_get_fpscr(env); +} + +void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +{ + vfp_set_fpscr(env, val); +} diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 0e849d8d4d..0919acb7b8 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/helper-proto.h" #include "internals.h" #include "cpu-features.h" #include "fpu/softfloat.h" @@ -298,17 +297,12 @@ uint32_t vfp_get_fpsr(CPUARMState *env) return fpsr; } -uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +uint32_t vfp_get_fpscr(CPUARMState *env) { return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) | (vfp_get_fpsr(env) & FPSCR_FPSR_MASK); } -uint32_t vfp_get_fpscr(CPUARMState *env) -{ - return HELPER(vfp_get_fpscr)(env); -} - void vfp_set_fpsr(CPUARMState *env, uint32_t val) { ARMCPU *cpu = env_archcpu(env); @@ -402,13 +396,8 @@ void vfp_set_fpcr(CPUARMState *env, uint32_t val) vfp_set_fpcr_masked(env, val, MAKE_64BIT_MASK(0, 32)); } -void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +void vfp_set_fpscr(CPUARMState *env, uint32_t val) { vfp_set_fpcr_masked(env, val, FPSCR_FPCR_MASK); vfp_set_fpsr(env, val & FPSCR_FPSR_MASK); } - -void vfp_set_fpscr(CPUARMState *env, uint32_t val) -{ - HELPER(vfp_set_fpscr)(env, val); -} From b9d3dc45532e696f5ee566edd227a4f46bad0f35 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Feb 2025 19:09:55 +0000 Subject: [PATCH 1956/2892] target/arm: Move softfloat specific FPCR/FPSR handling to tcg/ The softfloat (i.e. TCG) specific handling for the FPCR and FPSR is abstracted behind five functions: arm_set_default_fp_behaviours arm_set_ah_fp_behaviours vfp_get_fpsr_from_host vfp_clear_float_status_exc_flags vfp_set_fpsr_to_host Currently we rely on the first two calling softfloat functions that work even in a KVM-only compile because they're defined as inline in the softfloat header file, and we provide stub versions of the last three in arm/vfp_helper.c if CONFIG_TCG isn't defined. Move the softfloat-specific versions of these functions to tcg/vfp_helper.c, and provide the non-TCG stub versions in tcg-stubs.c. This lets us drop the softfloat header include and the last set of CONFIG_TCG ifdefs from arm/vfp_helper.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250221190957.811948-4-peter.maydell@linaro.org --- target/arm/internals.h | 9 ++ target/arm/tcg-stubs.c | 22 ++++ target/arm/tcg/vfp_helper.c | 228 +++++++++++++++++++++++++++++++++ target/arm/vfp_helper.c | 248 ------------------------------------ 4 files changed, 259 insertions(+), 248 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index b318734145..a6ff228f9f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1833,5 +1833,14 @@ int alle1_tlbmask(CPUARMState *env); void arm_set_default_fp_behaviours(float_status *s); /* Set the float_status behaviour to match Arm FPCR.AH=1 behaviour */ void arm_set_ah_fp_behaviours(float_status *s); +/* Read the float_status info and return the appropriate FPSR value */ +uint32_t vfp_get_fpsr_from_host(CPUARMState *env); +/* Clear the exception status flags from all float_status fields */ +void vfp_clear_float_status_exc_flags(CPUARMState *env); +/* + * Update float_status fields to handle the bits of the FPCR + * specified by mask changing to the values in val. + */ +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask); #endif diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index f3f45d54f2..93a15cad61 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -30,3 +30,25 @@ void assert_hflags_rebuild_correctly(CPUARMState *env) void define_tlb_insn_regs(ARMCPU *cpu) { } + +/* With KVM, we never use float_status, so these can be no-ops */ +void arm_set_default_fp_behaviours(float_status *s) +{ +} + +void arm_set_ah_fp_behaviours(float_status *s) +{ +} + +uint32_t vfp_get_fpsr_from_host(CPUARMState *env) +{ + return 0; +} + +void vfp_clear_float_status_exc_flags(CPUARMState *env) +{ +} + +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) +{ +} diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c index cd6e0d0eda..b32e2f4e27 100644 --- a/target/arm/tcg/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -25,6 +25,234 @@ #include "fpu/softfloat.h" #include "qemu/log.h" +/* + * Set the float_status behaviour to match the Arm defaults: + * * tininess-before-rounding + * * 2-input NaN propagation prefers SNaN over QNaN, and then + * operand A over operand B (see FPProcessNaNs() pseudocode) + * * 3-input NaN propagation prefers SNaN over QNaN, and then + * operand C over A over B (see FPProcessNaNs3() pseudocode, + * but note that for QEMU muladd is a * b + c, whereas for + * the pseudocode function the arguments are in the order c, a, b. + * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, + * and the input NaN if it is signalling + * * Default NaN has sign bit clear, msb frac bit set + */ +void arm_set_default_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_before_rounding, s); + set_float_ftz_detection(float_ftz_before_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); + set_float_default_nan_pattern(0b01000000, s); +} + +/* + * Set the float_status behaviour to match the FEAT_AFP + * FPCR.AH=1 requirements: + * * tininess-after-rounding + * * 2-input NaN propagation prefers the first NaN + * * 3-input NaN propagation prefers a over b over c + * * 0 * Inf + NaN always returns the input NaN and doesn't + * set Invalid for a QNaN + * * default NaN has sign bit set, msb frac bit set + */ +void arm_set_ah_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_after_rounding, s); + set_float_ftz_detection(float_ftz_after_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_abc, s); + set_float_infzeronan_rule(float_infzeronan_dnan_never | + float_infzeronan_suppress_invalid, s); + set_float_default_nan_pattern(0b11000000, s); +} + +/* Convert host exception flags to vfp form. */ +static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah) +{ + uint32_t target_bits = 0; + + if (host_bits & float_flag_invalid) { + target_bits |= FPSR_IOC; + } + if (host_bits & float_flag_divbyzero) { + target_bits |= FPSR_DZC; + } + if (host_bits & float_flag_overflow) { + target_bits |= FPSR_OFC; + } + if (host_bits & (float_flag_underflow | float_flag_output_denormal_flushed)) { + target_bits |= FPSR_UFC; + } + if (host_bits & float_flag_inexact) { + target_bits |= FPSR_IXC; + } + if (host_bits & float_flag_input_denormal_flushed) { + target_bits |= FPSR_IDC; + } + /* + * With FPCR.AH, IDC is set when an input denormal is used, + * and flushing an output denormal to zero sets both IXC and UFC. + */ + if (ah && (host_bits & float_flag_input_denormal_used)) { + target_bits |= FPSR_IDC; + } + if (ah && (host_bits & float_flag_output_denormal_flushed)) { + target_bits |= FPSR_IXC; + } + return target_bits; +} + +uint32_t vfp_get_fpsr_from_host(CPUARMState *env) +{ + uint32_t a32_flags = 0, a64_flags = 0; + + a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A32]); + a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]); + /* FZ16 does not generate an input denormal exception. */ + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A32_F16]) + & ~float_flag_input_denormal_flushed); + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16]) + & ~float_flag_input_denormal_flushed); + + a64_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A64]); + a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16]) + & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); + /* + * We do not merge in flags from FPST_AH or FPST_AH_F16, because + * they are used for insns that must not set the cumulative exception bits. + */ + + /* + * Flushing an input denormal *only* because FPCR.FIZ == 1 does + * not set FPSR.IDC; if FPCR.FZ is also set then this takes + * precedence and IDC is set (see the FPUnpackBase pseudocode). + * So squash it unless (FPCR.AH == 0 && FPCR.FZ == 1). + * We only do this for the a64 flags because FIZ has no effect + * on AArch32 even if it is set. + */ + if ((env->vfp.fpcr & (FPCR_FZ | FPCR_AH)) != FPCR_FZ) { + a64_flags &= ~float_flag_input_denormal_flushed; + } + return vfp_exceptbits_from_host(a64_flags, env->vfp.fpcr & FPCR_AH) | + vfp_exceptbits_from_host(a32_flags, false); +} + +void vfp_clear_float_status_exc_flags(CPUARMState *env) +{ + /* + * Clear out all the exception-flag information in the float_status + * values. The caller should have arranged for env->vfp.fpsr to + * be the architecturally up-to-date exception flag information first. + */ + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH_F16]); +} + +static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) +{ + /* + * Synchronize any pending exception-flag information in the + * float_status values into env->vfp.fpsr, and then clear out + * the float_status data. + */ + env->vfp.fpsr |= vfp_get_fpsr_from_host(env); + vfp_clear_float_status_exc_flags(env); +} + +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) +{ + uint64_t changed = env->vfp.fpcr; + + changed ^= val; + changed &= mask; + if (changed & (3 << 22)) { + int i = (val >> 22) & 3; + switch (i) { + case FPROUNDING_TIEEVEN: + i = float_round_nearest_even; + break; + case FPROUNDING_POSINF: + i = float_round_up; + break; + case FPROUNDING_NEGINF: + i = float_round_down; + break; + case FPROUNDING_ZERO: + i = float_round_to_zero; + break; + } + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); + } + if (changed & FPCR_FZ16) { + bool ftz_enabled = val & FPCR_FZ16; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); + } + if (changed & FPCR_FZ) { + bool ftz_enabled = val & FPCR_FZ; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]); + /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); + } + if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { + /* + * A64: Flush denormalized inputs to zero if FPCR.FIZ = 1, or + * both FPCR.AH = 0 and FPCR.FZ = 1. + */ + bool fitz_enabled = (val & FPCR_FIZ) || + (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ; + set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_A64]); + } + if (changed & FPCR_DN) { + bool dnan_enabled = val & FPCR_DN; + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); + } + if (changed & FPCR_AH) { + bool ah_enabled = val & FPCR_AH; + + if (ah_enabled) { + /* Change behaviours for A64 FP operations */ + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64]); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + } else { + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + } + } + /* + * If any bits changed that we look at in vfp_get_fpsr_from_host(), + * we must sync the float_status flags into vfp.fpsr now (under the + * old regime) before we update vfp.fpcr. + */ + if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { + vfp_sync_and_clear_float_status_exc_flags(env); + } +} + /* * VFP support. We follow the convention used for VFP instructions: * Single precision routines have a "s" suffix, double precision a diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 0919acb7b8..cc0f055ef0 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -21,254 +21,6 @@ #include "cpu.h" #include "internals.h" #include "cpu-features.h" -#include "fpu/softfloat.h" - -/* - * Set the float_status behaviour to match the Arm defaults: - * * tininess-before-rounding - * * 2-input NaN propagation prefers SNaN over QNaN, and then - * operand A over operand B (see FPProcessNaNs() pseudocode) - * * 3-input NaN propagation prefers SNaN over QNaN, and then - * operand C over A over B (see FPProcessNaNs3() pseudocode, - * but note that for QEMU muladd is a * b + c, whereas for - * the pseudocode function the arguments are in the order c, a, b. - * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, - * and the input NaN if it is signalling - * * Default NaN has sign bit clear, msb frac bit set - */ -void arm_set_default_fp_behaviours(float_status *s) -{ - set_float_detect_tininess(float_tininess_before_rounding, s); - set_float_ftz_detection(float_ftz_before_rounding, s); - set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); - set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); - set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); - set_float_default_nan_pattern(0b01000000, s); -} - -/* - * Set the float_status behaviour to match the FEAT_AFP - * FPCR.AH=1 requirements: - * * tininess-after-rounding - * * 2-input NaN propagation prefers the first NaN - * * 3-input NaN propagation prefers a over b over c - * * 0 * Inf + NaN always returns the input NaN and doesn't - * set Invalid for a QNaN - * * default NaN has sign bit set, msb frac bit set - */ -void arm_set_ah_fp_behaviours(float_status *s) -{ - set_float_detect_tininess(float_tininess_after_rounding, s); - set_float_ftz_detection(float_ftz_after_rounding, s); - set_float_2nan_prop_rule(float_2nan_prop_ab, s); - set_float_3nan_prop_rule(float_3nan_prop_abc, s); - set_float_infzeronan_rule(float_infzeronan_dnan_never | - float_infzeronan_suppress_invalid, s); - set_float_default_nan_pattern(0b11000000, s); -} - -#ifdef CONFIG_TCG - -/* Convert host exception flags to vfp form. */ -static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah) -{ - uint32_t target_bits = 0; - - if (host_bits & float_flag_invalid) { - target_bits |= FPSR_IOC; - } - if (host_bits & float_flag_divbyzero) { - target_bits |= FPSR_DZC; - } - if (host_bits & float_flag_overflow) { - target_bits |= FPSR_OFC; - } - if (host_bits & (float_flag_underflow | float_flag_output_denormal_flushed)) { - target_bits |= FPSR_UFC; - } - if (host_bits & float_flag_inexact) { - target_bits |= FPSR_IXC; - } - if (host_bits & float_flag_input_denormal_flushed) { - target_bits |= FPSR_IDC; - } - /* - * With FPCR.AH, IDC is set when an input denormal is used, - * and flushing an output denormal to zero sets both IXC and UFC. - */ - if (ah && (host_bits & float_flag_input_denormal_used)) { - target_bits |= FPSR_IDC; - } - if (ah && (host_bits & float_flag_output_denormal_flushed)) { - target_bits |= FPSR_IXC; - } - return target_bits; -} - -static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) -{ - uint32_t a32_flags = 0, a64_flags = 0; - - a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A32]); - a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]); - /* FZ16 does not generate an input denormal exception. */ - a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A32_F16]) - & ~float_flag_input_denormal_flushed); - a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16]) - & ~float_flag_input_denormal_flushed); - - a64_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A64]); - a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16]) - & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); - /* - * We do not merge in flags from FPST_AH or FPST_AH_F16, because - * they are used for insns that must not set the cumulative exception bits. - */ - - /* - * Flushing an input denormal *only* because FPCR.FIZ == 1 does - * not set FPSR.IDC; if FPCR.FZ is also set then this takes - * precedence and IDC is set (see the FPUnpackBase pseudocode). - * So squash it unless (FPCR.AH == 0 && FPCR.FZ == 1). - * We only do this for the a64 flags because FIZ has no effect - * on AArch32 even if it is set. - */ - if ((env->vfp.fpcr & (FPCR_FZ | FPCR_AH)) != FPCR_FZ) { - a64_flags &= ~float_flag_input_denormal_flushed; - } - return vfp_exceptbits_from_host(a64_flags, env->vfp.fpcr & FPCR_AH) | - vfp_exceptbits_from_host(a32_flags, false); -} - -static void vfp_clear_float_status_exc_flags(CPUARMState *env) -{ - /* - * Clear out all the exception-flag information in the float_status - * values. The caller should have arranged for env->vfp.fpsr to - * be the architecturally up-to-date exception flag information first. - */ - set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH]); - set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH_F16]); -} - -static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) -{ - /* - * Synchronize any pending exception-flag information in the - * float_status values into env->vfp.fpsr, and then clear out - * the float_status data. - */ - env->vfp.fpsr |= vfp_get_fpsr_from_host(env); - vfp_clear_float_status_exc_flags(env); -} - -static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) -{ - uint64_t changed = env->vfp.fpcr; - - changed ^= val; - changed &= mask; - if (changed & (3 << 22)) { - int i = (val >> 22) & 3; - switch (i) { - case FPROUNDING_TIEEVEN: - i = float_round_nearest_even; - break; - case FPROUNDING_POSINF: - i = float_round_up; - break; - case FPROUNDING_NEGINF: - i = float_round_down; - break; - case FPROUNDING_ZERO: - i = float_round_to_zero; - break; - } - set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32]); - set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]); - set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); - set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); - } - if (changed & FPCR_FZ16) { - bool ftz_enabled = val & FPCR_FZ16; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); - } - if (changed & FPCR_FZ) { - bool ftz_enabled = val & FPCR_FZ; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]); - /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); - } - if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { - /* - * A64: Flush denormalized inputs to zero if FPCR.FIZ = 1, or - * both FPCR.AH = 0 and FPCR.FZ = 1. - */ - bool fitz_enabled = (val & FPCR_FIZ) || - (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ; - set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_A64]); - } - if (changed & FPCR_DN) { - bool dnan_enabled = val & FPCR_DN; - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32]); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64]); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); - } - if (changed & FPCR_AH) { - bool ah_enabled = val & FPCR_AH; - - if (ah_enabled) { - /* Change behaviours for A64 FP operations */ - arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64]); - arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); - } else { - arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); - arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); - } - } - /* - * If any bits changed that we look at in vfp_get_fpsr_from_host(), - * we must sync the float_status flags into vfp.fpsr now (under the - * old regime) before we update vfp.fpcr. - */ - if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { - vfp_sync_and_clear_float_status_exc_flags(env); - } -} - -#else - -static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) -{ - return 0; -} - -static void vfp_clear_float_status_exc_flags(CPUARMState *env) -{ -} - -static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) -{ -} - -#endif uint32_t vfp_get_fpcr(CPUARMState *env) { From cb8bb8472ef83d8c8c6beac37d6db47ab3b68e18 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Feb 2025 19:09:56 +0000 Subject: [PATCH 1957/2892] target/arm: Rename vfp_helper.c to vfp_fpscr.c The vfp_helper.c in the target/arm directory now only has code for handling FPSCR/FPCR/FPSR in it, and no helper functions. Rename it to vfp_fpscr.c; this helps keep it distinct from tcg/vfp_helper.c. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250221190957.811948-5-peter.maydell@linaro.org --- target/arm/meson.build | 2 +- target/arm/{vfp_helper.c => vfp_fpscr.c} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename target/arm/{vfp_helper.c => vfp_fpscr.c} (98%) diff --git a/target/arm/meson.build b/target/arm/meson.build index 2e10464dbb..3065081d24 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -4,7 +4,7 @@ arm_ss.add(files( 'debug_helper.c', 'gdbstub.c', 'helper.c', - 'vfp_helper.c', + 'vfp_fpscr.c', )) arm_ss.add(zlib) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_fpscr.c similarity index 98% rename from target/arm/vfp_helper.c rename to target/arm/vfp_fpscr.c index cc0f055ef0..92ea60ebbf 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_fpscr.c @@ -1,5 +1,5 @@ /* - * ARM VFP floating-point operations + * ARM VFP floating-point: handling of FPSCR/FPCR/FPSR * * Copyright (c) 2003 Fabrice Bellard * From fd207677a83087454b8afef31651985a1df0d2dd Mon Sep 17 00:00:00 2001 From: Joelle van Dyne Date: Mon, 24 Feb 2025 08:57:34 -0800 Subject: [PATCH 1958/2892] target/arm/hvf: Disable SME feature macOS 15.2's Hypervisor.framework exposes SME feature on M4 Macs. However, QEMU's hvf accelerator code does not properly support it yet, causing QEMU to fail to start when hvf accelerator is used on these systems, with the error message: qemu-aarch64-softmmu: cannot disable sme4224 All SME vector lengths are disabled. With SME enabled, at least one vector length must be enabled. Ideally we would have SME support on these hosts; however, until that point, we must suppress the SME feature in the ID registers, so that users can at least run non-SME guests. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2665 Signed-off-by: Joelle van Dyne Message-id: 20250224165735.36792-1-j@getutm.app Reviewed-by: Peter Maydell [PMM: expanded commit message, comment] Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 0afd96018e..872a25be86 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -899,6 +899,18 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar.id_aa64mmfr0); + /* + * Disable SME, which is not properly handled by QEMU hvf yet. + * To allow this through we would need to: + * - make sure that the SME state is correctly handled in the + * get_registers/put_registers functions + * - get the SME-specific CPU properties to work with accelerators + * other than TCG + * - fix any assumptions we made that SME implies SVE (since + * on the M4 there is SME but not SVE) + */ + host_isar.id_aa64pfr1 &= ~R_ID_AA64PFR1_SME_MASK; + ahcf->isar = host_isar; /* From 12c365315ab25d364cff24dfeea8d7ff1e752b9f Mon Sep 17 00:00:00 2001 From: Joelle van Dyne Date: Mon, 24 Feb 2025 10:41:23 -0800 Subject: [PATCH 1959/2892] target/arm/hvf: sign extend the data for a load operation when SSE=1 In the syndrome value for a data abort, bit 21 is SSE, which is set to indicate that the abort was on a sign-extending load. When we handle the data abort from the guest via address_space_read(), we forgot to handle this and so would return the wrong value if the guest did a sign-extending load to an MMIO region. Add the sign-extension of the returned data. Cc: qemu-stable@nongnu.org Signed-off-by: Joelle van Dyne Message-id: 20250224184123.50780-1-j@getutm.app [PMM: Drop an unnecessary check on 'len'; expand commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 872a25be86..2439af63a0 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1983,6 +1983,7 @@ int hvf_vcpu_exec(CPUState *cpu) bool isv = syndrome & ARM_EL_ISV; bool iswrite = (syndrome >> 6) & 1; bool s1ptw = (syndrome >> 7) & 1; + bool sse = (syndrome >> 21) & 1; uint32_t sas = (syndrome >> 22) & 3; uint32_t len = 1 << sas; uint32_t srt = (syndrome >> 16) & 0x1f; @@ -2010,6 +2011,9 @@ int hvf_vcpu_exec(CPUState *cpu) address_space_read(&address_space_memory, hvf_exit->exception.physical_address, MEMTXATTRS_UNSPECIFIED, &val, len); + if (sse) { + val = sextract64(val, 0, len * 8); + } hvf_set_reg(cpu, srt, val); } From 2cac20cbf7b01e9a4e404db2ff9bee09ee26f315 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Feb 2025 12:50:53 -0800 Subject: [PATCH 1960/2892] hw/misc/npcm_clk: fix buffer-overflow Regression introduced by cf76c4 (hw/misc: Add nr_regs and cold_reset_values to NPCM CLK) cold_reset_values has a different size, depending on device used (NPCM7xx vs NPCM8xx). However, s->regs has a fixed size, which matches NPCM8xx. Thus, when initializing a NPCM7xx, we go past cold_reset_values ending. Report by asan: ==2066==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55d68a3e97f0 at pc 0x7fcaf2b2d14b bp 0x7ffff0cc3890 sp 0x7ffff0cc3040 READ of size 196 at 0x55d68a3e97f0 thread T0 #0 0x7fcaf2b2d14a in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 #1 0x55d688447e0d in memcpy /usr/include/x86_64-linux-gnu/bits/string_fortified.h:29 #2 0x55d688447e0d in npcm_clk_enter_reset ../hw/misc/npcm_clk.c:968 #3 0x55d6899b7213 in resettable_phase_enter ../hw/core/resettable.c:136 #4 0x55d6899a1ef7 in bus_reset_child_foreach ../hw/core/bus.c:97 #5 0x55d6899b717d in resettable_child_foreach ../hw/core/resettable.c:92 #6 0x55d6899b717d in resettable_phase_enter ../hw/core/resettable.c:129 #7 0x55d6899b4ead in resettable_container_child_foreach ../hw/core/resetcontainer.c:54 #8 0x55d6899b717d in resettable_child_foreach ../hw/core/resettable.c:92 #9 0x55d6899b717d in resettable_phase_enter ../hw/core/resettable.c:129 #10 0x55d6899b7bfa in resettable_assert_reset ../hw/core/resettable.c:55 #11 0x55d6899b8666 in resettable_reset ../hw/core/resettable.c:45 #12 0x55d688d15cd2 in qemu_system_reset ../system/runstate.c:527 #13 0x55d687fc5edd in qdev_machine_creation_done ../hw/core/machine.c:1738 #14 0x55d688d209bd in qemu_machine_creation_done ../system/vl.c:2779 #15 0x55d688d209bd in qmp_x_exit_preconfig ../system/vl.c:2807 #16 0x55d688d281fb in qemu_init ../system/vl.c:3838 #17 0x55d687ceab12 in main ../system/main.c:68 #18 0x7fcaef006249 (/lib/x86_64-linux-gnu/libc.so.6+0x27249) #19 0x7fcaef006304 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27304) #20 0x55d687cf0010 in _start (/home/runner/work/qemu-ci/qemu-ci/build/qemu-system-arm+0x371c010) 0x55d68a3e97f0 is located 0 bytes to the right of global variable 'npcm7xx_cold_reset_values' defined in '../hw/misc/npcm_clk.c:134:23' (0x55d68a3e9780) of size 112 Impacted tests: Summary of Failures: check: 2/747 qemu:qtest+qtest-aarch64 / qtest-aarch64/qom-test ERROR 9.28s killed by signal 6 SIGABRT 4/747 qemu:qtest+qtest-arm / qtest-arm/qom-test ERROR 7.82s killed by signal 6 SIGABRT 32/747 qemu:qtest+qtest-aarch64 / qtest-aarch64/device-introspect-test ERROR 10.91s killed by signal 6 SIGABRT 35/747 qemu:qtest+qtest-arm / qtest-arm/device-introspect-test ERROR 11.33s killed by signal 6 SIGABRT 114/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_pwm-test ERROR 0.98s killed by signal 6 SIGABRT 115/747 qemu:qtest+qtest-aarch64 / qtest-aarch64/test-hmp ERROR 2.95s killed by signal 6 SIGABRT 117/747 qemu:qtest+qtest-arm / qtest-arm/test-hmp ERROR 2.54s killed by signal 6 SIGABRT 151/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_watchdog_timer-test ERROR 0.96s killed by signal 6 SIGABRT 247/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_adc-test ERROR 0.96s killed by signal 6 SIGABRT 248/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_gpio-test ERROR 1.05s killed by signal 6 SIGABRT 249/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_rng-test ERROR 0.97s killed by signal 6 SIGABRT 250/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_sdhci-test ERROR 0.97s killed by signal 6 SIGABRT 251/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_smbus-test ERROR 0.89s killed by signal 6 SIGABRT 252/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_timer-test ERROR 1.09s killed by signal 6 SIGABRT 253/747 qemu:qtest+qtest-arm / qtest-arm/npcm_gmac-test ERROR 1.12s killed by signal 6 SIGABRT 255/747 qemu:qtest+qtest-arm / qtest-arm/npcm7xx_emc-test ERROR 1.05s killed by signal 6 SIGABRT check-functional: 22/203 qemu:func-thorough+func-arm-thorough+thorough / func-arm-arm_quanta_gsj ERROR 0.79s exit status 1 38/203 qemu:func-quick+func-aarch64 / func-aarch64-migration ERROR 1.97s exit status 1 45/203 qemu:func-quick+func-arm / func-arm-migration ERROR 1.90s exit status 1 Fixes: cf76c4e174e1 ("hw/misc: Add nr_regs and cold_reset_values to NPCM CLK") Signed-off-by: Pierrick Bouvier Reviewed-by: Hao Wu Signed-off-by: Peter Maydell --- hw/misc/npcm_clk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index d1f29759d5..0e85974cf9 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -964,8 +964,9 @@ static void npcm_clk_enter_reset(Object *obj, ResetType type) NPCMCLKState *s = NPCM_CLK(obj); NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); - g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t)); - memcpy(s->regs, c->cold_reset_values, sizeof(s->regs)); + size_t sizeof_regs = c->nr_regs * sizeof(uint32_t); + g_assert(sizeof(s->regs) >= sizeof_regs); + memcpy(s->regs, c->cold_reset_values, sizeof_regs); s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); npcm7xx_clk_update_all_clocks(s); /* From b513766ee968dbfca31034b185f0a0fcf99f4269 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:51 +0100 Subject: [PATCH 1961/2892] hw/usb/hcd-dwc3: Align global registers size with Linux While at it add missing GUSB2RHBCTL register as found in i.MX 8M Plus reference manual. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-2-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/usb/hcd-dwc3.c | 5 +++++ include/hw/usb/hcd-dwc3.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index 9ce9ba0b04..0bceee2712 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -343,6 +343,8 @@ REG32(GFLADJ, 0x530) FIELD(GFLADJ, GFLADJ_REFCLK_FLADJ, 8, 14) FIELD(GFLADJ, GFLADJ_30MHZ_SDBND_SEL, 7, 1) FIELD(GFLADJ, GFLADJ_30MHZ, 0, 6) +REG32(GUSB2RHBCTL, 0x540) + FIELD(GUSB2RHBCTL, OVRD_L1TIMEOUT, 0, 4) #define DWC3_GLOBAL_OFFSET 0xC100 static void reset_csr(USBDWC3 * s) @@ -560,6 +562,9 @@ static const RegisterAccessInfo usb_dwc3_regs_info[] = { .rsvd = 0x40, .ro = 0x400040, .unimp = 0xffffffff, + },{ .name = "GUSB2RHBCTL", .addr = A_GUSB2RHBCTL, + .rsvd = 0xfffffff0, + .unimp = 0xffffffff, } }; diff --git a/include/hw/usb/hcd-dwc3.h b/include/hw/usb/hcd-dwc3.h index f752a27e94..dbdf12b21d 100644 --- a/include/hw/usb/hcd-dwc3.h +++ b/include/hw/usb/hcd-dwc3.h @@ -35,7 +35,7 @@ #define USB_DWC3(obj) \ OBJECT_CHECK(USBDWC3, (obj), TYPE_USB_DWC3) -#define USB_DWC3_R_MAX ((0x530 / 4) + 1) +#define USB_DWC3_R_MAX (0x600 / 4) #define DWC3_SIZE 0x10000 typedef struct USBDWC3 { From faa2150a527b1919646316dba268b71ced8762a6 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:52 +0100 Subject: [PATCH 1962/2892] hw/pci-host/designware: Prevent device attachment on internal PCIe root bus On the real device, the PCIe root bus is only connected to a PCIe bridge and does not allow for direct attachment of devices. Doing so in QEMU results in no PCI devices being detected by Linux. Instead, PCI devices should plug into the secondary PCIe bus spawned by the internal PCIe bridge. Unfortunately, QEMU defaults to plugging devices into the PCIe root bus. To work around this, every PCI device created on the command line needs an extra `bus=dw-pcie` option which is error prone. Fix that by marking the PCIe root bus as full which makes QEMU decend into the child PCIe bus. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-3-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/pci-host/designware.c | 18 +++++++++++++++++- include/hw/pci-host/designware.h | 7 +++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 3e8c36e6a7..c07740bfaa 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -55,6 +55,17 @@ #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C +static void designware_pcie_root_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + /* + * Designware has only a single root complex. Enforce the limit on the + * parent bus + */ + k->max_dev = 1; +} + static DesignwarePCIEHost * designware_pcie_root_to_host(DesignwarePCIERoot *root) { @@ -699,7 +710,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) &s->pci.memory, &s->pci.io, 0, 4, - TYPE_PCIE_BUS); + TYPE_DESIGNWARE_PCIE_ROOT_BUS); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; memory_region_init(&s->pci.address_space_root, @@ -754,6 +765,11 @@ static void designware_pcie_host_init(Object *obj) static const TypeInfo designware_pcie_types[] = { { + .name = TYPE_DESIGNWARE_PCIE_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(DesignwarePCIERootBus), + .class_init = designware_pcie_root_bus_class_init, + }, { .name = TYPE_DESIGNWARE_PCIE_HOST, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(DesignwarePCIEHost), diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h index bf8b278978..a35a3bd06c 100644 --- a/include/hw/pci-host/designware.h +++ b/include/hw/pci-host/designware.h @@ -25,12 +25,19 @@ #include "hw/pci/pci_bridge.h" #include "qom/object.h" +#define TYPE_DESIGNWARE_PCIE_ROOT_BUS "designware-pcie-root-BUS" +OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIERootBus, DESIGNWARE_PCIE_ROOT_BUS) + #define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host" OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIEHost, DESIGNWARE_PCIE_HOST) #define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root" OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIERoot, DESIGNWARE_PCIE_ROOT) +struct DesignwarePCIERootBus { + PCIBus parent; +}; + typedef struct DesignwarePCIEViewport { DesignwarePCIERoot *root; From 0f520f0a9d9516fb3563a9b69c820ac73d2017aa Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:53 +0100 Subject: [PATCH 1963/2892] hw/gpio/pca955*: Move Kconfig switches next to implementations The move of the Kconfig bits to hw/gpio is fixing a bug in 6328d8ffa6cb9d ("misc/pca955*: Move models under hw/gpio"), which moved the code but forgot to move the Kconfig sections. Fixes: 6328d8ffa6cb9d "misc/pca955*: Move models under hw/gpio" Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-4-shentey@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/gpio/Kconfig | 8 ++++++++ hw/misc/Kconfig | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index c423e10f59..a209294c20 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -16,6 +16,14 @@ config SIFIVE_GPIO config STM32L4X5_GPIO bool +config PCA9552 + bool + depends on I2C + +config PCA9554 + bool + depends on I2C + config PCF8574 bool depends on I2C diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 8f9ce2f68c..4271e2f4ac 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -30,14 +30,6 @@ config EDU default y if TEST_DEVICES depends on PCI && MSI_NONBROKEN -config PCA9552 - bool - depends on I2C - -config PCA9554 - bool - depends on I2C - config I2C_ECHO bool default y if TEST_DEVICES From a4eefc69b23713c4e5981d9d91a6e15dfd4496fe Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:54 +0100 Subject: [PATCH 1964/2892] hw/arm: Add i.MX 8M Plus EVK board As a first step, implement the bare minimum: CPUs, RAM, interrupt controller, serial. All other devices of the A53 memory map are represented as TYPE_UNIMPLEMENTED_DEVICE, i.e. the whole memory map is provided. This allows for running Linux without it crashing due to invalid memory accesses. Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-5-shentey@gmail.com Reviewed-by: Peter Maydell [PMM: drop 'static const' from serial_table[] definition to avoid compile failure on GCC 7.5] Signed-off-by: Peter Maydell --- MAINTAINERS | 9 + docs/system/arm/imx8mp-evk.rst | 54 +++++ docs/system/target-arm.rst | 1 + hw/arm/Kconfig | 12 ++ hw/arm/fsl-imx8mp.c | 367 +++++++++++++++++++++++++++++++++ hw/arm/imx8mp-evk.c | 55 +++++ hw/arm/meson.build | 2 + include/hw/arm/fsl-imx8mp.h | 189 +++++++++++++++++ 8 files changed, 689 insertions(+) create mode 100644 docs/system/arm/imx8mp-evk.rst create mode 100644 hw/arm/fsl-imx8mp.c create mode 100644 hw/arm/imx8mp-evk.c create mode 100644 include/hw/arm/fsl-imx8mp.h diff --git a/MAINTAINERS b/MAINTAINERS index 1911949526..374fe98724 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -820,6 +820,15 @@ F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h F: docs/system/arm/mcimx7d-sabre.rst +MCIMX8MP-EVK / i.MX8MP +M: Bernhard Beschow +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/imx8mp-evk.c +F: hw/arm/fsl-imx8mp.c +F: include/hw/arm/fsl-imx8mp.h +F: docs/system/arm/imx8mp-evk.rst + MPS2 / MPS3 M: Peter Maydell L: qemu-arm@nongnu.org diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst new file mode 100644 index 0000000000..b23fdcc743 --- /dev/null +++ b/docs/system/arm/imx8mp-evk.rst @@ -0,0 +1,54 @@ +NXP i.MX 8M Plus Evaluation Kit (``imx8mp-evk``) +================================================ + +The ``imx8mp-evk`` machine models the i.MX 8M Plus Evaluation Kit, based on an +i.MX 8M Plus SoC. + +Supported devices +----------------- + +The ``imx8mp-evk`` machine implements the following devices: + + * Up to 4 Cortex-A53 cores + * Generic Interrupt Controller (GICv3) + * 4 UARTs + +Boot options +------------ + +The ``imx8mp-evk`` machine can start a Linux kernel directly using the standard +``-kernel`` functionality. + +Direct Linux Kernel Boot +'''''''''''''''''''''''' + +Probably the easiest way to get started with a whole Linux system on the machine +is to generate an image with Buildroot. Version 2024.11.1 is tested at the time +of writing and involves two steps. First run the following commands in the +toplevel directory of the Buildroot source tree: + +.. code-block:: bash + + $ echo "BR2_TARGET_ROOTFS_CPIO=y" >> configs/freescale_imx8mpevk_defconfig + $ make freescale_imx8mpevk_defconfig + $ make + +Once finished successfully there is an ``output/image`` subfolder. Navigate into +it and patch the device tree with the following commands which will remove the +``cpu-idle-states`` properties from CPU nodes: + +.. code-block:: bash + + $ dtc imx8mp-evk.dtb | sed '/cpu-idle-states/d' > imx8mp-evk-patched.dts + $ dtc imx8mp-evk-patched.dts -o imx8mp-evk-patched.dtb + +Now that everything is prepared the machine can be started as follows: + +.. code-block:: bash + + $ qemu-system-aarch64 -M imx8mp-evk -smp 4 -m 3G \ + -display none -serial null -serial stdio \ + -kernel Image \ + -dtb imx8mp-evk-patched.dtb \ + -initrd rootfs.cpio \ + -append "root=/dev/ram" diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 9aaa9c414c..a43ec8f10e 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -95,6 +95,7 @@ Board-specific documentation arm/imx25-pdk arm/mcimx6ul-evk arm/mcimx7d-sabre + arm/imx8mp-evk arm/orangepi arm/raspi arm/collie diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 504841ccab..0a7de40861 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -593,6 +593,18 @@ config FSL_IMX7 select UNIMP select USB_CHIPIDEA +config FSL_IMX8MP + bool + select ARM_GIC + select IMX + select UNIMP + +config FSL_IMX8MP_EVK + bool + default y + depends on TCG && AARCH64 + select FSL_IMX8MP + config ARM_SMMUV3 bool diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c new file mode 100644 index 0000000000..084b1d3bb1 --- /dev/null +++ b/hw/arm/fsl-imx8mp.c @@ -0,0 +1,367 @@ +/* + * i.MX 8M Plus SoC Implementation + * + * Based on hw/arm/fsl-imx6.c + * + * Copyright (c) 2024, Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/fsl-imx8mp.h" +#include "hw/intc/arm_gicv3.h" +#include "hw/misc/unimp.h" +#include "hw/boards.h" +#include "system/system.h" +#include "target/arm/cpu-qom.h" +#include "qapi/error.h" +#include "qobject/qlist.h" + +static const struct { + hwaddr addr; + size_t size; + const char *name; +} fsl_imx8mp_memmap[] = { + [FSL_IMX8MP_RAM] = { FSL_IMX8MP_RAM_START, FSL_IMX8MP_RAM_SIZE_MAX, "ram" }, + [FSL_IMX8MP_DDR_PHY_BROADCAST] = { 0x3dc00000, 4 * MiB, "ddr_phy_broadcast" }, + [FSL_IMX8MP_DDR_PERF_MON] = { 0x3d800000, 4 * MiB, "ddr_perf_mon" }, + [FSL_IMX8MP_DDR_CTL] = { 0x3d400000, 4 * MiB, "ddr_ctl" }, + [FSL_IMX8MP_DDR_BLK_CTRL] = { 0x3d000000, 1 * MiB, "ddr_blk_ctrl" }, + [FSL_IMX8MP_DDR_PHY] = { 0x3c000000, 16 * MiB, "ddr_phy" }, + [FSL_IMX8MP_AUDIO_DSP] = { 0x3b000000, 16 * MiB, "audio_dsp" }, + [FSL_IMX8MP_GIC_DIST] = { 0x38800000, 512 * KiB, "gic_dist" }, + [FSL_IMX8MP_GIC_REDIST] = { 0x38880000, 512 * KiB, "gic_redist" }, + [FSL_IMX8MP_NPU] = { 0x38500000, 2 * MiB, "npu" }, + [FSL_IMX8MP_VPU] = { 0x38340000, 2 * MiB, "vpu" }, + [FSL_IMX8MP_VPU_BLK_CTRL] = { 0x38330000, 2 * MiB, "vpu_blk_ctrl" }, + [FSL_IMX8MP_VPU_VC8000E_ENCODER] = { 0x38320000, 2 * MiB, "vpu_vc8000e_encoder" }, + [FSL_IMX8MP_VPU_G2_DECODER] = { 0x38310000, 2 * MiB, "vpu_g2_decoder" }, + [FSL_IMX8MP_VPU_G1_DECODER] = { 0x38300000, 2 * MiB, "vpu_g1_decoder" }, + [FSL_IMX8MP_USB2] = { 0x38200000, 1 * MiB, "usb2" }, + [FSL_IMX8MP_USB1] = { 0x38100000, 1 * MiB, "usb1" }, + [FSL_IMX8MP_GPU2D] = { 0x38008000, 32 * KiB, "gpu2d" }, + [FSL_IMX8MP_GPU3D] = { 0x38000000, 32 * KiB, "gpu3d" }, + [FSL_IMX8MP_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" }, + [FSL_IMX8MP_PCIE1] = { 0x33800000, 4 * MiB, "pcie1" }, + [FSL_IMX8MP_QSPI1_TX_BUFFER] = { 0x33008000, 32 * KiB, "qspi1_tx_buffer" }, + [FSL_IMX8MP_APBH_DMA] = { 0x33000000, 32 * KiB, "apbh_dma" }, + + /* AIPS-5 Begin */ + [FSL_IMX8MP_MU_3_B] = { 0x30e90000, 64 * KiB, "mu_3_b" }, + [FSL_IMX8MP_MU_3_A] = { 0x30e80000, 64 * KiB, "mu_3_a" }, + [FSL_IMX8MP_MU_2_B] = { 0x30e70000, 64 * KiB, "mu_2_b" }, + [FSL_IMX8MP_MU_2_A] = { 0x30e60000, 64 * KiB, "mu_2_a" }, + [FSL_IMX8MP_EDMA_CHANNELS] = { 0x30e40000, 128 * KiB, "edma_channels" }, + [FSL_IMX8MP_EDMA_MANAGEMENT_PAGE] = { 0x30e30000, 64 * KiB, "edma_management_page" }, + [FSL_IMX8MP_AUDIO_BLK_CTRL] = { 0x30e20000, 64 * KiB, "audio_blk_ctrl" }, + [FSL_IMX8MP_SDMA2] = { 0x30e10000, 64 * KiB, "sdma2" }, + [FSL_IMX8MP_SDMA3] = { 0x30e00000, 64 * KiB, "sdma3" }, + [FSL_IMX8MP_AIPS5_CONFIGURATION] = { 0x30df0000, 64 * KiB, "aips5_configuration" }, + [FSL_IMX8MP_SPBA2] = { 0x30cf0000, 64 * KiB, "spba2" }, + [FSL_IMX8MP_AUDIO_XCVR_RX] = { 0x30cc0000, 64 * KiB, "audio_xcvr_rx" }, + [FSL_IMX8MP_HDMI_TX_AUDLNK_MSTR] = { 0x30cb0000, 64 * KiB, "hdmi_tx_audlnk_mstr" }, + [FSL_IMX8MP_PDM] = { 0x30ca0000, 64 * KiB, "pdm" }, + [FSL_IMX8MP_ASRC] = { 0x30c90000, 64 * KiB, "asrc" }, + [FSL_IMX8MP_SAI7] = { 0x30c80000, 64 * KiB, "sai7" }, + [FSL_IMX8MP_SAI6] = { 0x30c60000, 64 * KiB, "sai6" }, + [FSL_IMX8MP_SAI5] = { 0x30c50000, 64 * KiB, "sai5" }, + [FSL_IMX8MP_SAI3] = { 0x30c30000, 64 * KiB, "sai3" }, + [FSL_IMX8MP_SAI2] = { 0x30c20000, 64 * KiB, "sai2" }, + [FSL_IMX8MP_SAI1] = { 0x30c10000, 64 * KiB, "sai1" }, + /* AIPS-5 End */ + + /* AIPS-4 Begin */ + [FSL_IMX8MP_HDMI_TX] = { 0x32fc0000, 128 * KiB, "hdmi_tx" }, + [FSL_IMX8MP_TZASC] = { 0x32f80000, 64 * KiB, "tzasc" }, + [FSL_IMX8MP_HSIO_BLK_CTL] = { 0x32f10000, 64 * KiB, "hsio_blk_ctl" }, + [FSL_IMX8MP_PCIE_PHY1] = { 0x32f00000, 64 * KiB, "pcie_phy1" }, + [FSL_IMX8MP_MEDIA_BLK_CTL] = { 0x32ec0000, 64 * KiB, "media_blk_ctl" }, + [FSL_IMX8MP_LCDIF2] = { 0x32e90000, 64 * KiB, "lcdif2" }, + [FSL_IMX8MP_LCDIF1] = { 0x32e80000, 64 * KiB, "lcdif1" }, + [FSL_IMX8MP_MIPI_DSI1] = { 0x32e60000, 64 * KiB, "mipi_dsi1" }, + [FSL_IMX8MP_MIPI_CSI2] = { 0x32e50000, 64 * KiB, "mipi_csi2" }, + [FSL_IMX8MP_MIPI_CSI1] = { 0x32e40000, 64 * KiB, "mipi_csi1" }, + [FSL_IMX8MP_IPS_DEWARP] = { 0x32e30000, 64 * KiB, "ips_dewarp" }, + [FSL_IMX8MP_ISP2] = { 0x32e20000, 64 * KiB, "isp2" }, + [FSL_IMX8MP_ISP1] = { 0x32e10000, 64 * KiB, "isp1" }, + [FSL_IMX8MP_ISI] = { 0x32e00000, 64 * KiB, "isi" }, + [FSL_IMX8MP_AIPS4_CONFIGURATION] = { 0x32df0000, 64 * KiB, "aips4_configuration" }, + /* AIPS-4 End */ + + [FSL_IMX8MP_INTERCONNECT] = { 0x32700000, 1 * MiB, "interconnect" }, + + /* AIPS-3 Begin */ + [FSL_IMX8MP_ENET2_TSN] = { 0x30bf0000, 64 * KiB, "enet2_tsn" }, + [FSL_IMX8MP_ENET1] = { 0x30be0000, 64 * KiB, "enet1" }, + [FSL_IMX8MP_SDMA1] = { 0x30bd0000, 64 * KiB, "sdma1" }, + [FSL_IMX8MP_QSPI] = { 0x30bb0000, 64 * KiB, "qspi" }, + [FSL_IMX8MP_USDHC3] = { 0x30b60000, 64 * KiB, "usdhc3" }, + [FSL_IMX8MP_USDHC2] = { 0x30b50000, 64 * KiB, "usdhc2" }, + [FSL_IMX8MP_USDHC1] = { 0x30b40000, 64 * KiB, "usdhc1" }, + [FSL_IMX8MP_I2C6] = { 0x30ae0000, 64 * KiB, "i2c6" }, + [FSL_IMX8MP_I2C5] = { 0x30ad0000, 64 * KiB, "i2c5" }, + [FSL_IMX8MP_SEMAPHORE_HS] = { 0x30ac0000, 64 * KiB, "semaphore_hs" }, + [FSL_IMX8MP_MU_1_B] = { 0x30ab0000, 64 * KiB, "mu_1_b" }, + [FSL_IMX8MP_MU_1_A] = { 0x30aa0000, 64 * KiB, "mu_1_a" }, + [FSL_IMX8MP_AUD_IRQ_STEER] = { 0x30a80000, 64 * KiB, "aud_irq_steer" }, + [FSL_IMX8MP_UART4] = { 0x30a60000, 64 * KiB, "uart4" }, + [FSL_IMX8MP_I2C4] = { 0x30a50000, 64 * KiB, "i2c4" }, + [FSL_IMX8MP_I2C3] = { 0x30a40000, 64 * KiB, "i2c3" }, + [FSL_IMX8MP_I2C2] = { 0x30a30000, 64 * KiB, "i2c2" }, + [FSL_IMX8MP_I2C1] = { 0x30a20000, 64 * KiB, "i2c1" }, + [FSL_IMX8MP_AIPS3_CONFIGURATION] = { 0x309f0000, 64 * KiB, "aips3_configuration" }, + [FSL_IMX8MP_CAAM] = { 0x30900000, 256 * KiB, "caam" }, + [FSL_IMX8MP_SPBA1] = { 0x308f0000, 64 * KiB, "spba1" }, + [FSL_IMX8MP_FLEXCAN2] = { 0x308d0000, 64 * KiB, "flexcan2" }, + [FSL_IMX8MP_FLEXCAN1] = { 0x308c0000, 64 * KiB, "flexcan1" }, + [FSL_IMX8MP_UART2] = { 0x30890000, 64 * KiB, "uart2" }, + [FSL_IMX8MP_UART3] = { 0x30880000, 64 * KiB, "uart3" }, + [FSL_IMX8MP_UART1] = { 0x30860000, 64 * KiB, "uart1" }, + [FSL_IMX8MP_ECSPI3] = { 0x30840000, 64 * KiB, "ecspi3" }, + [FSL_IMX8MP_ECSPI2] = { 0x30830000, 64 * KiB, "ecspi2" }, + [FSL_IMX8MP_ECSPI1] = { 0x30820000, 64 * KiB, "ecspi1" }, + /* AIPS-3 End */ + + /* AIPS-2 Begin */ + [FSL_IMX8MP_QOSC] = { 0x307f0000, 64 * KiB, "qosc" }, + [FSL_IMX8MP_PERFMON2] = { 0x307d0000, 64 * KiB, "perfmon2" }, + [FSL_IMX8MP_PERFMON1] = { 0x307c0000, 64 * KiB, "perfmon1" }, + [FSL_IMX8MP_GPT4] = { 0x30700000, 64 * KiB, "gpt4" }, + [FSL_IMX8MP_GPT5] = { 0x306f0000, 64 * KiB, "gpt5" }, + [FSL_IMX8MP_GPT6] = { 0x306e0000, 64 * KiB, "gpt6" }, + [FSL_IMX8MP_SYSCNT_CTRL] = { 0x306c0000, 64 * KiB, "syscnt_ctrl" }, + [FSL_IMX8MP_SYSCNT_CMP] = { 0x306b0000, 64 * KiB, "syscnt_cmp" }, + [FSL_IMX8MP_SYSCNT_RD] = { 0x306a0000, 64 * KiB, "syscnt_rd" }, + [FSL_IMX8MP_PWM4] = { 0x30690000, 64 * KiB, "pwm4" }, + [FSL_IMX8MP_PWM3] = { 0x30680000, 64 * KiB, "pwm3" }, + [FSL_IMX8MP_PWM2] = { 0x30670000, 64 * KiB, "pwm2" }, + [FSL_IMX8MP_PWM1] = { 0x30660000, 64 * KiB, "pwm1" }, + [FSL_IMX8MP_AIPS2_CONFIGURATION] = { 0x305f0000, 64 * KiB, "aips2_configuration" }, + /* AIPS-2 End */ + + /* AIPS-1 Begin */ + [FSL_IMX8MP_CSU] = { 0x303e0000, 64 * KiB, "csu" }, + [FSL_IMX8MP_RDC] = { 0x303d0000, 64 * KiB, "rdc" }, + [FSL_IMX8MP_SEMAPHORE2] = { 0x303c0000, 64 * KiB, "semaphore2" }, + [FSL_IMX8MP_SEMAPHORE1] = { 0x303b0000, 64 * KiB, "semaphore1" }, + [FSL_IMX8MP_GPC] = { 0x303a0000, 64 * KiB, "gpc" }, + [FSL_IMX8MP_SRC] = { 0x30390000, 64 * KiB, "src" }, + [FSL_IMX8MP_CCM] = { 0x30380000, 64 * KiB, "ccm" }, + [FSL_IMX8MP_SNVS_HP] = { 0x30370000, 64 * KiB, "snvs_hp" }, + [FSL_IMX8MP_ANA_PLL] = { 0x30360000, 64 * KiB, "ana_pll" }, + [FSL_IMX8MP_OCOTP_CTRL] = { 0x30350000, 64 * KiB, "ocotp_ctrl" }, + [FSL_IMX8MP_IOMUXC_GPR] = { 0x30340000, 64 * KiB, "iomuxc_gpr" }, + [FSL_IMX8MP_IOMUXC] = { 0x30330000, 64 * KiB, "iomuxc" }, + [FSL_IMX8MP_GPT3] = { 0x302f0000, 64 * KiB, "gpt3" }, + [FSL_IMX8MP_GPT2] = { 0x302e0000, 64 * KiB, "gpt2" }, + [FSL_IMX8MP_GPT1] = { 0x302d0000, 64 * KiB, "gpt1" }, + [FSL_IMX8MP_WDOG3] = { 0x302a0000, 64 * KiB, "wdog3" }, + [FSL_IMX8MP_WDOG2] = { 0x30290000, 64 * KiB, "wdog2" }, + [FSL_IMX8MP_WDOG1] = { 0x30280000, 64 * KiB, "wdog1" }, + [FSL_IMX8MP_ANA_OSC] = { 0x30270000, 64 * KiB, "ana_osc" }, + [FSL_IMX8MP_ANA_TSENSOR] = { 0x30260000, 64 * KiB, "ana_tsensor" }, + [FSL_IMX8MP_GPIO5] = { 0x30240000, 64 * KiB, "gpio5" }, + [FSL_IMX8MP_GPIO4] = { 0x30230000, 64 * KiB, "gpio4" }, + [FSL_IMX8MP_GPIO3] = { 0x30220000, 64 * KiB, "gpio3" }, + [FSL_IMX8MP_GPIO2] = { 0x30210000, 64 * KiB, "gpio2" }, + [FSL_IMX8MP_GPIO1] = { 0x30200000, 64 * KiB, "gpio1" }, + [FSL_IMX8MP_AIPS1_CONFIGURATION] = { 0x301f0000, 64 * KiB, "aips1_configuration" }, + /* AIPS-1 End */ + + [FSL_IMX8MP_A53_DAP] = { 0x28000000, 16 * MiB, "a53_dap" }, + [FSL_IMX8MP_PCIE1_MEM] = { 0x18000000, 128 * MiB, "pcie1_mem" }, + [FSL_IMX8MP_QSPI_MEM] = { 0x08000000, 256 * MiB, "qspi_mem" }, + [FSL_IMX8MP_OCRAM] = { 0x00900000, 576 * KiB, "ocram" }, + [FSL_IMX8MP_TCM_DTCM] = { 0x00800000, 128 * KiB, "tcm_dtcm" }, + [FSL_IMX8MP_TCM_ITCM] = { 0x007e0000, 128 * KiB, "tcm_itcm" }, + [FSL_IMX8MP_OCRAM_S] = { 0x00180000, 36 * KiB, "ocram_s" }, + [FSL_IMX8MP_CAAM_MEM] = { 0x00100000, 32 * KiB, "caam_mem" }, + [FSL_IMX8MP_BOOT_ROM_PROTECTED] = { 0x0003f000, 4 * KiB, "boot_rom_protected" }, + [FSL_IMX8MP_BOOT_ROM] = { 0x00000000, 252 * KiB, "boot_rom" }, +}; + +static void fsl_imx8mp_init(Object *obj) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mpState *s = FSL_IMX8MP(obj); + int i; + + for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX8MP_NUM_CPUS); i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a53")); + } + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GICV3); + + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i + 1); + object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); + } +} + +static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mpState *s = FSL_IMX8MP(dev); + DeviceState *gicdev = DEVICE(&s->gic); + int i; + + if (ms->smp.cpus > FSL_IMX8MP_NUM_CPUS) { + error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX8MP, FSL_IMX8MP_NUM_CPUS, ms->smp.cpus); + return; + } + + /* CPUs */ + for (i = 0; i < ms->smp.cpus; i++) { + /* On uniprocessor, the CBAR is set to 0 */ + if (ms->smp.cpus > 1) { + object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + fsl_imx8mp_memmap[FSL_IMX8MP_GIC_DIST].addr, + &error_abort); + } + + /* + * CNTFID0 base frequency in Hz of system counter + */ + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (i) { + /* + * Secondary CPUs start in powered-down state (and can be + * powered up via the SRC system reset controller) + */ + object_property_set_bool(OBJECT(&s->cpu[i]), "start-powered-off", + true, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + { + SysBusDevice *gicsbd = SYS_BUS_DEVICE(&s->gic); + QList *redist_region_count; + + qdev_prop_set_uint32(gicdev, "num-cpu", ms->smp.cpus); + qdev_prop_set_uint32(gicdev, "num-irq", + FSL_IMX8MP_NUM_IRQS + GIC_INTERNAL); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, ms->smp.cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + object_property_set_link(OBJECT(&s->gic), "sysmem", + OBJECT(get_system_memory()), &error_fatal); + if (!sysbus_realize(gicsbd, errp)) { + return; + } + sysbus_mmio_map(gicsbd, 0, fsl_imx8mp_memmap[FSL_IMX8MP_GIC_DIST].addr); + sysbus_mmio_map(gicsbd, 1, fsl_imx8mp_memmap[FSL_IMX8MP_GIC_REDIST].addr); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < ms->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(&s->cpu[i]); + int intidbase = FSL_IMX8MP_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + static const int timer_irqs[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gicdev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpudev, j, irq); + } + + irq = qdev_get_gpio_in(gicdev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + + irq = qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, irq); + + sysbus_connect_irq(gicsbd, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, i + ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } + } + + /* UARTs */ + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX8MP_NUM_UARTS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_UART1].addr, FSL_IMX8MP_UART1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART2].addr, FSL_IMX8MP_UART2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART3].addr, FSL_IMX8MP_UART3_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART4].addr, FSL_IMX8MP_UART4_IRQ }, + }; + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gicdev, serial_table[i].irq)); + } + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { + switch (i) { + case FSL_IMX8MP_GIC_DIST: + case FSL_IMX8MP_GIC_REDIST: + case FSL_IMX8MP_RAM: + case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: + /* device implemented and treated above */ + break; + + default: + create_unimplemented_device(fsl_imx8mp_memmap[i].name, + fsl_imx8mp_memmap[i].addr, + fsl_imx8mp_memmap[i].size); + break; + } + } +} + +static void fsl_imx8mp_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx8mp_realize; + + dc->desc = "i.MX 8M Plus SoC"; +} + +static const TypeInfo fsl_imx8mp_types[] = { + { + .name = TYPE_FSL_IMX8MP, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslImx8mpState), + .instance_init = fsl_imx8mp_init, + .class_init = fsl_imx8mp_class_init, + }, +}; + +DEFINE_TYPES(fsl_imx8mp_types) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c new file mode 100644 index 0000000000..2756d4c21c --- /dev/null +++ b/hw/arm/imx8mp-evk.c @@ -0,0 +1,55 @@ +/* + * NXP i.MX 8M Plus Evaluation Kit System Emulation + * + * Copyright (c) 2024, Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/arm/fsl-imx8mp.h" +#include "hw/boards.h" +#include "system/qtest.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + +static void imx8mp_evk_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + FslImx8mpState *s; + + if (machine->ram_size > FSL_IMX8MP_RAM_SIZE_MAX) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")", + machine->ram_size, FSL_IMX8MP_RAM_SIZE_MAX); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX8MP_RAM_START, + .board_id = -1, + .ram_size = machine->ram_size, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, + }; + + s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); + qdev_realize(DEVICE(s), NULL, &error_fatal); + + memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, + machine->ram); + + if (!qtest_enabled()) { + arm_load_kernel(&s->cpu[0], machine, &boot_info); + } +} + +static void imx8mp_evk_machine_init(MachineClass *mc) +{ + mc->desc = "NXP i.MX 8M Plus EVK Board"; + mc->init = imx8mp_evk_init; + mc->max_cpus = FSL_IMX8MP_NUM_CPUS; + mc->default_ram_id = "imx8mp-evk.ram"; +} +DEFINE_MACHINE("imx8mp-evk", imx8mp_evk_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 465c757f97..ac473ce7cd 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -54,6 +54,8 @@ arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) arm_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) arm_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) +arm_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) +arm_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h new file mode 100644 index 0000000000..57e23d1b69 --- /dev/null +++ b/include/hw/arm/fsl-imx8mp.h @@ -0,0 +1,189 @@ +/* + * i.MX 8M Plus SoC Definitions + * + * Copyright (c) 2024, Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FSL_IMX8MP_H +#define FSL_IMX8MP_H + +#include "cpu.h" +#include "hw/char/imx_serial.h" +#include "hw/intc/arm_gicv3_common.h" +#include "qom/object.h" +#include "qemu/units.h" + +#define TYPE_FSL_IMX8MP "fsl-imx8mp" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) + +#define FSL_IMX8MP_RAM_START 0x40000000 +#define FSL_IMX8MP_RAM_SIZE_MAX (8 * GiB) + +enum FslImx8mpConfiguration { + FSL_IMX8MP_NUM_CPUS = 4, + FSL_IMX8MP_NUM_IRQS = 160, + FSL_IMX8MP_NUM_UARTS = 4, +}; + +struct FslImx8mpState { + DeviceState parent_obj; + + ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; + GICv3State gic; + IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; +}; + +enum FslImx8mpMemoryRegions { + FSL_IMX8MP_A53_DAP, + FSL_IMX8MP_AIPS1_CONFIGURATION, + FSL_IMX8MP_AIPS2_CONFIGURATION, + FSL_IMX8MP_AIPS3_CONFIGURATION, + FSL_IMX8MP_AIPS4_CONFIGURATION, + FSL_IMX8MP_AIPS5_CONFIGURATION, + FSL_IMX8MP_ANA_OSC, + FSL_IMX8MP_ANA_PLL, + FSL_IMX8MP_ANA_TSENSOR, + FSL_IMX8MP_APBH_DMA, + FSL_IMX8MP_ASRC, + FSL_IMX8MP_AUDIO_BLK_CTRL, + FSL_IMX8MP_AUDIO_DSP, + FSL_IMX8MP_AUDIO_XCVR_RX, + FSL_IMX8MP_AUD_IRQ_STEER, + FSL_IMX8MP_BOOT_ROM, + FSL_IMX8MP_BOOT_ROM_PROTECTED, + FSL_IMX8MP_CAAM, + FSL_IMX8MP_CAAM_MEM, + FSL_IMX8MP_CCM, + FSL_IMX8MP_CSU, + FSL_IMX8MP_DDR_BLK_CTRL, + FSL_IMX8MP_DDR_CTL, + FSL_IMX8MP_DDR_PERF_MON, + FSL_IMX8MP_DDR_PHY, + FSL_IMX8MP_DDR_PHY_BROADCAST, + FSL_IMX8MP_ECSPI1, + FSL_IMX8MP_ECSPI2, + FSL_IMX8MP_ECSPI3, + FSL_IMX8MP_EDMA_CHANNELS, + FSL_IMX8MP_EDMA_MANAGEMENT_PAGE, + FSL_IMX8MP_ENET1, + FSL_IMX8MP_ENET2_TSN, + FSL_IMX8MP_FLEXCAN1, + FSL_IMX8MP_FLEXCAN2, + FSL_IMX8MP_GIC_DIST, + FSL_IMX8MP_GIC_REDIST, + FSL_IMX8MP_GPC, + FSL_IMX8MP_GPIO1, + FSL_IMX8MP_GPIO2, + FSL_IMX8MP_GPIO3, + FSL_IMX8MP_GPIO4, + FSL_IMX8MP_GPIO5, + FSL_IMX8MP_GPT1, + FSL_IMX8MP_GPT2, + FSL_IMX8MP_GPT3, + FSL_IMX8MP_GPT4, + FSL_IMX8MP_GPT5, + FSL_IMX8MP_GPT6, + FSL_IMX8MP_GPU2D, + FSL_IMX8MP_GPU3D, + FSL_IMX8MP_HDMI_TX, + FSL_IMX8MP_HDMI_TX_AUDLNK_MSTR, + FSL_IMX8MP_HSIO_BLK_CTL, + FSL_IMX8MP_I2C1, + FSL_IMX8MP_I2C2, + FSL_IMX8MP_I2C3, + FSL_IMX8MP_I2C4, + FSL_IMX8MP_I2C5, + FSL_IMX8MP_I2C6, + FSL_IMX8MP_INTERCONNECT, + FSL_IMX8MP_IOMUXC, + FSL_IMX8MP_IOMUXC_GPR, + FSL_IMX8MP_IPS_DEWARP, + FSL_IMX8MP_ISI, + FSL_IMX8MP_ISP1, + FSL_IMX8MP_ISP2, + FSL_IMX8MP_LCDIF1, + FSL_IMX8MP_LCDIF2, + FSL_IMX8MP_MEDIA_BLK_CTL, + FSL_IMX8MP_MIPI_CSI1, + FSL_IMX8MP_MIPI_CSI2, + FSL_IMX8MP_MIPI_DSI1, + FSL_IMX8MP_MU_1_A, + FSL_IMX8MP_MU_1_B, + FSL_IMX8MP_MU_2_A, + FSL_IMX8MP_MU_2_B, + FSL_IMX8MP_MU_3_A, + FSL_IMX8MP_MU_3_B, + FSL_IMX8MP_NPU, + FSL_IMX8MP_OCOTP_CTRL, + FSL_IMX8MP_OCRAM, + FSL_IMX8MP_OCRAM_S, + FSL_IMX8MP_PCIE1, + FSL_IMX8MP_PCIE1_MEM, + FSL_IMX8MP_PCIE_PHY1, + FSL_IMX8MP_PDM, + FSL_IMX8MP_PERFMON1, + FSL_IMX8MP_PERFMON2, + FSL_IMX8MP_PWM1, + FSL_IMX8MP_PWM2, + FSL_IMX8MP_PWM3, + FSL_IMX8MP_PWM4, + FSL_IMX8MP_QOSC, + FSL_IMX8MP_QSPI, + FSL_IMX8MP_QSPI1_RX_BUFFER, + FSL_IMX8MP_QSPI1_TX_BUFFER, + FSL_IMX8MP_QSPI_MEM, + FSL_IMX8MP_RAM, + FSL_IMX8MP_RDC, + FSL_IMX8MP_SAI1, + FSL_IMX8MP_SAI2, + FSL_IMX8MP_SAI3, + FSL_IMX8MP_SAI5, + FSL_IMX8MP_SAI6, + FSL_IMX8MP_SAI7, + FSL_IMX8MP_SDMA1, + FSL_IMX8MP_SDMA2, + FSL_IMX8MP_SDMA3, + FSL_IMX8MP_SEMAPHORE1, + FSL_IMX8MP_SEMAPHORE2, + FSL_IMX8MP_SEMAPHORE_HS, + FSL_IMX8MP_SNVS_HP, + FSL_IMX8MP_SPBA1, + FSL_IMX8MP_SPBA2, + FSL_IMX8MP_SRC, + FSL_IMX8MP_SYSCNT_CMP, + FSL_IMX8MP_SYSCNT_CTRL, + FSL_IMX8MP_SYSCNT_RD, + FSL_IMX8MP_TCM_DTCM, + FSL_IMX8MP_TCM_ITCM, + FSL_IMX8MP_TZASC, + FSL_IMX8MP_UART1, + FSL_IMX8MP_UART2, + FSL_IMX8MP_UART3, + FSL_IMX8MP_UART4, + FSL_IMX8MP_USB1, + FSL_IMX8MP_USB2, + FSL_IMX8MP_USDHC1, + FSL_IMX8MP_USDHC2, + FSL_IMX8MP_USDHC3, + FSL_IMX8MP_VPU, + FSL_IMX8MP_VPU_BLK_CTRL, + FSL_IMX8MP_VPU_G1_DECODER, + FSL_IMX8MP_VPU_G2_DECODER, + FSL_IMX8MP_VPU_VC8000E_ENCODER, + FSL_IMX8MP_WDOG1, + FSL_IMX8MP_WDOG2, + FSL_IMX8MP_WDOG3, +}; + +enum FslImx8mpIrqs { + FSL_IMX8MP_UART1_IRQ = 26, + FSL_IMX8MP_UART2_IRQ = 27, + FSL_IMX8MP_UART3_IRQ = 28, + FSL_IMX8MP_UART4_IRQ = 29, + FSL_IMX8MP_UART5_IRQ = 30, + FSL_IMX8MP_UART6_IRQ = 16, +}; + +#endif /* FSL_IMX8MP_H */ From 86c2dff9552ad5a9b2febf329a2dbd2620bc2145 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:55 +0100 Subject: [PATCH 1965/2892] hw/arm/fsl-imx8mp: Implement clock tree Fixes quite a few stack traces during the Linux boot process. Also provides the clocks for devices added later, e.g. enet1. Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-6-shentey@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 2 + docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 2 + hw/arm/fsl-imx8mp.c | 20 ++++ hw/misc/Kconfig | 6 ++ hw/misc/imx8mp_analog.c | 160 +++++++++++++++++++++++++++++ hw/misc/imx8mp_ccm.c | 175 ++++++++++++++++++++++++++++++++ hw/misc/meson.build | 2 + include/hw/arm/fsl-imx8mp.h | 4 + include/hw/misc/imx8mp_analog.h | 81 +++++++++++++++ include/hw/misc/imx8mp_ccm.h | 30 ++++++ 11 files changed, 483 insertions(+) create mode 100644 hw/misc/imx8mp_analog.c create mode 100644 hw/misc/imx8mp_ccm.c create mode 100644 include/hw/misc/imx8mp_analog.h create mode 100644 include/hw/misc/imx8mp_ccm.h diff --git a/MAINTAINERS b/MAINTAINERS index 374fe98724..8ea7fb4c7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -826,7 +826,9 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/imx8mp-evk.c F: hw/arm/fsl-imx8mp.c +F: hw/misc/imx8mp_*.c F: include/hw/arm/fsl-imx8mp.h +F: include/hw/misc/imx8mp_*.h F: docs/system/arm/imx8mp-evk.rst MPS2 / MPS3 diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index b23fdcc743..f0df346113 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -12,6 +12,7 @@ The ``imx8mp-evk`` machine implements the following devices: * Up to 4 Cortex-A53 cores * Generic Interrupt Controller (GICv3) * 4 UARTs + * Clock Tree Boot options ------------ diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 0a7de40861..f77c451ba3 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -596,6 +596,8 @@ config FSL_IMX7 config FSL_IMX8MP bool select ARM_GIC + select FSL_IMX8MP_ANALOG + select FSL_IMX8MP_CCM select IMX select UNIMP diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 084b1d3bb1..bc15b25ca1 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -197,6 +197,10 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GICV3); + object_initialize_child(obj, "ccm", &s->ccm, TYPE_IMX8MP_CCM); + + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -304,6 +308,20 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) } } + /* CCM */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_CCM].addr); + + /* Analog */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->analog), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_ANA_PLL].addr); + /* UARTs */ for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { struct { @@ -329,6 +347,8 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { switch (i) { + case FSL_IMX8MP_ANA_PLL: + case FSL_IMX8MP_CCM: case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_RAM: diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 4271e2f4ac..82bd68b4bb 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -78,6 +78,12 @@ config IMX select SSI select USB_EHCI_SYSBUS +config FSL_IMX8MP_ANALOG + bool + +config FSL_IMX8MP_CCM + bool + config STM32_RCC bool diff --git a/hw/misc/imx8mp_analog.c b/hw/misc/imx8mp_analog.c new file mode 100644 index 0000000000..f7e7c83cc4 --- /dev/null +++ b/hw/misc/imx8mp_analog.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX 8M Plus ANALOG IP block emulation code + * + * Based on hw/misc/imx7_ccm.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx8mp_analog.h" +#include "migration/vmstate.h" + +#define ANALOG_PLL_LOCK BIT(31) + +static void imx8mp_analog_reset(DeviceState *dev) +{ + IMX8MPAnalogState *s = IMX8MP_ANALOG(dev); + + memset(s->analog, 0, sizeof(s->analog)); + + s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL1_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL1_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL2_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL2_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_VIDEO_PLL1_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_VIDEO_PLL1_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_DRAM_PLL_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_DRAM_PLL_FDIV_CTL0] = 0x0012c032; + s->analog[ANALOG_DRAM_PLL_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_DRAM_PLL_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_DRAM_PLL_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_GPU_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_GPU_PLL_FDIV_CTL0] = 0x000c8031; + s->analog[ANALOG_GPU_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_GPU_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_VPU_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_VPU_PLL_FDIV_CTL0] = 0x0012c032; + s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810; + s->analog[ANALOG_SYS_PLL1_FDIV_CTL0] = 0x00190032; + s->analog[ANALOG_SYS_PLL1_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL1_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL2_GEN_CTRL] = 0x0aaaa810; + s->analog[ANALOG_SYS_PLL2_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_SYS_PLL2_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL2_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL3_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_SYS_PLL3_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_SYS_PLL3_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL3_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_OSC_MISC_CFG] = 0x00000000; + s->analog[ANALOG_ANAMIX_PLL_MNIT_CTL] = 0x00000000; + s->analog[ANALOG_DIGPROG] = 0x00824010; + + /* all PLLs need to be locked */ + s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_DRAM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_GPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_VPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_ARM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL3_GEN_CTRL] |= ANALOG_PLL_LOCK; +} + +static uint64_t imx8mp_analog_read(void *opaque, hwaddr offset, unsigned size) +{ + IMX8MPAnalogState *s = opaque; + + return s->analog[offset >> 2]; +} + +static void imx8mp_analog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMX8MPAnalogState *s = opaque; + + if (offset >> 2 == ANALOG_DIGPROG) { + qemu_log_mask(LOG_GUEST_ERROR, + "Guest write to read-only ANALOG_DIGPROG register\n"); + } else { + s->analog[offset >> 2] = value; + } +} + +static const struct MemoryRegionOps imx8mp_analog_ops = { + .read = imx8mp_analog_read, + .write = imx8mp_analog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx8mp_analog_init(Object *obj) +{ + IMX8MPAnalogState *s = IMX8MP_ANALOG(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + + memory_region_init(&s->mmio.container, obj, TYPE_IMX8MP_ANALOG, 0x10000); + + memory_region_init_io(&s->mmio.analog, obj, &imx8mp_analog_ops, s, + TYPE_IMX8MP_ANALOG, sizeof(s->analog)); + memory_region_add_subregion(&s->mmio.container, 0, &s->mmio.analog); + + sysbus_init_mmio(sd, &s->mmio.container); +} + +static const VMStateDescription imx8mp_analog_vmstate = { + .name = TYPE_IMX8MP_ANALOG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(analog, IMX8MPAnalogState, ANALOG_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx8mp_analog_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, imx8mp_analog_reset); + dc->vmsd = &imx8mp_analog_vmstate; + dc->desc = "i.MX 8M Plus Analog Module"; +} + +static const TypeInfo imx8mp_analog_types[] = { + { + .name = TYPE_IMX8MP_ANALOG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX8MPAnalogState), + .instance_init = imx8mp_analog_init, + .class_init = imx8mp_analog_class_init, + } +}; + +DEFINE_TYPES(imx8mp_analog_types); diff --git a/hw/misc/imx8mp_ccm.c b/hw/misc/imx8mp_ccm.c new file mode 100644 index 0000000000..1a1c932427 --- /dev/null +++ b/hw/misc/imx8mp_ccm.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX 8M Plus CCM IP block emulation code + * + * Based on hw/misc/imx7_ccm.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx8mp_ccm.h" +#include "migration/vmstate.h" + +#include "trace.h" + +#define CKIH_FREQ 16000000 /* 16MHz crystal input */ + +static void imx8mp_ccm_reset(DeviceState *dev) +{ + IMX8MPCCMState *s = IMX8MP_CCM(dev); + + memset(s->ccm, 0, sizeof(s->ccm)); +} + +#define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t)) +#define CCM_BITOP(offset) ((offset) & (hwaddr)0xF) + +enum { + CCM_BITOP_NONE = 0x00, + CCM_BITOP_SET = 0x04, + CCM_BITOP_CLR = 0x08, + CCM_BITOP_TOG = 0x0C, +}; + +static uint64_t imx8mp_set_clr_tog_read(void *opaque, hwaddr offset, + unsigned size) +{ + const uint32_t *mmio = opaque; + + return mmio[CCM_INDEX(offset)]; +} + +static void imx8mp_set_clr_tog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + const uint8_t bitop = CCM_BITOP(offset); + const uint32_t index = CCM_INDEX(offset); + uint32_t *mmio = opaque; + + switch (bitop) { + case CCM_BITOP_NONE: + mmio[index] = value; + break; + case CCM_BITOP_SET: + mmio[index] |= value; + break; + case CCM_BITOP_CLR: + mmio[index] &= ~value; + break; + case CCM_BITOP_TOG: + mmio[index] ^= value; + break; + }; +} + +static const struct MemoryRegionOps imx8mp_set_clr_tog_ops = { + .read = imx8mp_set_clr_tog_read, + .write = imx8mp_set_clr_tog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx8mp_ccm_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX8MPCCMState *s = IMX8MP_CCM(obj); + + memory_region_init_io(&s->iomem, + obj, + &imx8mp_set_clr_tog_ops, + s->ccm, + TYPE_IMX8MP_CCM ".ccm", + sizeof(s->ccm)); + + sysbus_init_mmio(sd, &s->iomem); +} + +static const VMStateDescription imx8mp_ccm_vmstate = { + .name = TYPE_IMX8MP_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX8MPCCMState, CCM_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + /* + * This function is "consumed" by GPT emulation code. Some clocks + * have fixed frequencies and we can provide requested frequency + * easily. However for CCM provided clocks (like IPG) each GPT + * timer can have its own clock root. + * This means we need additional information when calling this + * function to know the requester's identity. + */ + uint32_t freq = 0; + + switch (clock) { + case CLK_NONE: + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + case CLK_HIGH: + freq = CKIH_FREQ; + break; + case CLK_IPG: + case CLK_IPG_HIGH: + /* + * For now we don't have a way to figure out the device this + * function is called for. Until then the IPG derived clocks + * are left unimplemented. + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n", + TYPE_IMX8MP_CCM, __func__, clock); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX8MP_CCM, __func__, clock); + break; + } + + trace_ccm_clock_freq(clock, freq); + + return freq; +} + +static void imx8mp_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + device_class_set_legacy_reset(dc, imx8mp_ccm_reset); + dc->vmsd = &imx8mp_ccm_vmstate; + dc->desc = "i.MX 8M Plus Clock Control Module"; + + ccm->get_clock_frequency = imx8mp_ccm_get_clock_frequency; +} + +static const TypeInfo imx8mp_ccm_types[] = { + { + .name = TYPE_IMX8MP_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX8MPCCMState), + .instance_init = imx8mp_ccm_init, + .class_init = imx8mp_ccm_class_init, + }, +}; + +DEFINE_TYPES(imx8mp_ccm_types); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index edd36a334d..0b5187a2f7 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -55,6 +55,8 @@ system_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) system_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) +system_ss.add(when: 'CONFIG_FSL_IMX8MP_ANALOG', if_true: files('imx8mp_analog.c')) +system_ss.add(when: 'CONFIG_FSL_IMX8MP_CCM', if_true: files('imx8mp_ccm.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx25_ccm.c', 'imx31_ccm.c', diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 57e23d1b69..ce5188e7f2 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -12,6 +12,8 @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx8mp_analog.h" +#include "hw/misc/imx8mp_ccm.h" #include "qom/object.h" #include "qemu/units.h" @@ -32,6 +34,8 @@ struct FslImx8mpState { ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; GICv3State gic; + IMX8MPCCMState ccm; + IMX8MPAnalogState analog; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; }; diff --git a/include/hw/misc/imx8mp_analog.h b/include/hw/misc/imx8mp_analog.h new file mode 100644 index 0000000000..955f03215a --- /dev/null +++ b/include/hw/misc/imx8mp_analog.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX8MP ANALOG IP block emulation code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMX8MP_ANALOG_H +#define IMX8MP_ANALOG_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +enum IMX8MPAnalogRegisters { + ANALOG_AUDIO_PLL1_GEN_CTRL = 0x000 / 4, + ANALOG_AUDIO_PLL1_FDIV_CTL0 = 0x004 / 4, + ANALOG_AUDIO_PLL1_FDIV_CTL1 = 0x008 / 4, + ANALOG_AUDIO_PLL1_SSCG_CTRL = 0x00c / 4, + ANALOG_AUDIO_PLL1_MNIT_CTRL = 0x010 / 4, + ANALOG_AUDIO_PLL2_GEN_CTRL = 0x014 / 4, + ANALOG_AUDIO_PLL2_FDIV_CTL0 = 0x018 / 4, + ANALOG_AUDIO_PLL2_FDIV_CTL1 = 0x01c / 4, + ANALOG_AUDIO_PLL2_SSCG_CTRL = 0x020 / 4, + ANALOG_AUDIO_PLL2_MNIT_CTRL = 0x024 / 4, + ANALOG_VIDEO_PLL1_GEN_CTRL = 0x028 / 4, + ANALOG_VIDEO_PLL1_FDIV_CTL0 = 0x02c / 4, + ANALOG_VIDEO_PLL1_FDIV_CTL1 = 0x030 / 4, + ANALOG_VIDEO_PLL1_SSCG_CTRL = 0x034 / 4, + ANALOG_VIDEO_PLL1_MNIT_CTRL = 0x038 / 4, + ANALOG_DRAM_PLL_GEN_CTRL = 0x050 / 4, + ANALOG_DRAM_PLL_FDIV_CTL0 = 0x054 / 4, + ANALOG_DRAM_PLL_FDIV_CTL1 = 0x058 / 4, + ANALOG_DRAM_PLL_SSCG_CTRL = 0x05c / 4, + ANALOG_DRAM_PLL_MNIT_CTRL = 0x060 / 4, + ANALOG_GPU_PLL_GEN_CTRL = 0x064 / 4, + ANALOG_GPU_PLL_FDIV_CTL0 = 0x068 / 4, + ANALOG_GPU_PLL_LOCKD_CTRL = 0x06c / 4, + ANALOG_GPU_PLL_MNIT_CTRL = 0x070 / 4, + ANALOG_VPU_PLL_GEN_CTRL = 0x074 / 4, + ANALOG_VPU_PLL_FDIV_CTL0 = 0x078 / 4, + ANALOG_VPU_PLL_LOCKD_CTRL = 0x07c / 4, + ANALOG_VPU_PLL_MNIT_CTRL = 0x080 / 4, + ANALOG_ARM_PLL_GEN_CTRL = 0x084 / 4, + ANALOG_ARM_PLL_FDIV_CTL0 = 0x088 / 4, + ANALOG_ARM_PLL_LOCKD_CTRL = 0x08c / 4, + ANALOG_ARM_PLL_MNIT_CTRL = 0x090 / 4, + ANALOG_SYS_PLL1_GEN_CTRL = 0x094 / 4, + ANALOG_SYS_PLL1_FDIV_CTL0 = 0x098 / 4, + ANALOG_SYS_PLL1_LOCKD_CTRL = 0x09c / 4, + ANALOG_SYS_PLL1_MNIT_CTRL = 0x100 / 4, + ANALOG_SYS_PLL2_GEN_CTRL = 0x104 / 4, + ANALOG_SYS_PLL2_FDIV_CTL0 = 0x108 / 4, + ANALOG_SYS_PLL2_LOCKD_CTRL = 0x10c / 4, + ANALOG_SYS_PLL2_MNIT_CTRL = 0x110 / 4, + ANALOG_SYS_PLL3_GEN_CTRL = 0x114 / 4, + ANALOG_SYS_PLL3_FDIV_CTL0 = 0x118 / 4, + ANALOG_SYS_PLL3_LOCKD_CTRL = 0x11c / 4, + ANALOG_SYS_PLL3_MNIT_CTRL = 0x120 / 4, + ANALOG_OSC_MISC_CFG = 0x124 / 4, + ANALOG_ANAMIX_PLL_MNIT_CTL = 0x128 / 4, + + ANALOG_DIGPROG = 0x800 / 4, + ANALOG_MAX, +}; + +#define TYPE_IMX8MP_ANALOG "imx8mp.analog" +OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPAnalogState, IMX8MP_ANALOG) + +struct IMX8MPAnalogState { + SysBusDevice parent_obj; + + struct { + MemoryRegion container; + MemoryRegion analog; + } mmio; + + uint32_t analog[ANALOG_MAX]; +}; + +#endif /* IMX8MP_ANALOG_H */ diff --git a/include/hw/misc/imx8mp_ccm.h b/include/hw/misc/imx8mp_ccm.h new file mode 100644 index 0000000000..685c8582ff --- /dev/null +++ b/include/hw/misc/imx8mp_ccm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX 8M Plus CCM IP block emulation code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMX8MP_CCM_H +#define IMX8MP_CCM_H + +#include "hw/misc/imx_ccm.h" +#include "qom/object.h" + +enum IMX8MPCCMRegisters { + CCM_MAX = 0xc6fc / sizeof(uint32_t) + 1, +}; + +#define TYPE_IMX8MP_CCM "imx8mp.ccm" +OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPCCMState, IMX8MP_CCM) + +struct IMX8MPCCMState { + IMXCCMState parent_obj; + + MemoryRegion iomem; + + uint32_t ccm[CCM_MAX]; +}; + +#endif /* IMX8MP_CCM_H */ From 487967bed65083db33561edc1255ced422bfbff5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:56 +0100 Subject: [PATCH 1966/2892] hw/arm/fsl-imx8mp: Add SNVS SNVS contains an RTC which allows Linux to deal correctly with time. This is particularly useful when handling persistent storage which will be done in the next patch. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-7-shentey@gmail.com Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/fsl-imx8mp.c | 10 ++++++++++ include/hw/arm/fsl-imx8mp.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index f0df346113..22541c5442 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -12,6 +12,7 @@ The ``imx8mp-evk`` machine implements the following devices: * Up to 4 Cortex-A53 cores * Generic Interrupt Controller (GICv3) * 4 UARTs + * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree Boot options diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index bc15b25ca1..18c9c54ddc 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -201,6 +201,8 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -344,6 +346,13 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, serial_table[i].irq)); } + /* SNVS */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_SNVS_HP].addr); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { switch (i) { @@ -352,6 +361,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_RAM: + case FSL_IMX8MP_SNVS_HP: case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: /* device implemented and treated above */ break; diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index ce5188e7f2..26e24e99a1 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -12,6 +12,7 @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" #include "qom/object.h" @@ -36,6 +37,7 @@ struct FslImx8mpState { GICv3State gic; IMX8MPCCMState ccm; IMX8MPAnalogState analog; + IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; }; From a81193c3e9a8220862120d8d4114191f3899f4b3 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:57 +0100 Subject: [PATCH 1967/2892] hw/arm/fsl-imx8mp: Add USDHC storage controllers The USDHC emulation allows for running real-world images such as those generated by Buildroot. Convert the board documentation accordingly instead of running a Linux kernel with ephemeral storage. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-8-shentey@gmail.com [PMM: drop 'static const' from usdhc_table[] for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 18 ++++++++++++------ hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mp.c | 28 ++++++++++++++++++++++++++++ hw/arm/imx8mp-evk.c | 18 ++++++++++++++++++ include/hw/arm/fsl-imx8mp.h | 7 +++++++ 5 files changed, 66 insertions(+), 6 deletions(-) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 22541c5442..879c822356 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -12,6 +12,7 @@ The ``imx8mp-evk`` machine implements the following devices: * Up to 4 Cortex-A53 cores * Generic Interrupt Controller (GICv3) * 4 UARTs + * 3 USDHC Storage Controllers * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree @@ -26,18 +27,23 @@ Direct Linux Kernel Boot Probably the easiest way to get started with a whole Linux system on the machine is to generate an image with Buildroot. Version 2024.11.1 is tested at the time -of writing and involves two steps. First run the following commands in the +of writing and involves three steps. First run the following commands in the toplevel directory of the Buildroot source tree: .. code-block:: bash - $ echo "BR2_TARGET_ROOTFS_CPIO=y" >> configs/freescale_imx8mpevk_defconfig $ make freescale_imx8mpevk_defconfig $ make Once finished successfully there is an ``output/image`` subfolder. Navigate into -it and patch the device tree with the following commands which will remove the -``cpu-idle-states`` properties from CPU nodes: +it and resize the SD card image to a power of two: + +.. code-block:: bash + + $ qemu-img resize sdcard.img 256M + +Finally, the device tree needs to be patched with the following commands which +will remove the ``cpu-idle-states`` properties from CPU nodes: .. code-block:: bash @@ -52,5 +58,5 @@ Now that everything is prepared the machine can be started as follows: -display none -serial null -serial stdio \ -kernel Image \ -dtb imx8mp-evk-patched.dtb \ - -initrd rootfs.cpio \ - -append "root=/dev/ram" + -append "root=/dev/mmcblk2p2" \ + -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index f77c451ba3..d2dda3213d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -599,6 +599,7 @@ config FSL_IMX8MP select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select SDHCI select UNIMP config FSL_IMX8MP_EVK diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 18c9c54ddc..da9eaeb6ff 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -207,6 +207,11 @@ static void fsl_imx8mp_init(Object *obj) g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); + object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); + } } static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) @@ -346,6 +351,28 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, serial_table[i].irq)); } + /* USDHCs */ + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } usdhc_table[FSL_IMX8MP_NUM_USDHCS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC1].addr, FSL_IMX8MP_USDHC1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC2].addr, FSL_IMX8MP_USDHC2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC3].addr, FSL_IMX8MP_USDHC3_IRQ }, + }; + + object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", + SDHCI_VENDOR_IMX, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, usdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); + } + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -363,6 +390,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_RAM: case FSL_IMX8MP_SNVS_HP: case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: + case FSL_IMX8MP_USDHC1 ... FSL_IMX8MP_USDHC3: /* device implemented and treated above */ break; diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index 2756d4c21c..27d9e9e8ee 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -11,6 +11,7 @@ #include "hw/arm/boot.h" #include "hw/arm/fsl-imx8mp.h" #include "hw/boards.h" +#include "hw/qdev-properties.h" #include "system/qtest.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -40,6 +41,23 @@ static void imx8mp_evk_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, machine->ram); + for (int i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di = drive_get(IF_SD, i, 0); + + if (!di) { + continue; + } + + blk = blk_by_legacy_dinfo(di); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); + carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); + } + if (!qtest_enabled()) { arm_load_kernel(&s->cpu[0], machine, &boot_info); } diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 26e24e99a1..349d55ca88 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -15,6 +15,7 @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/sd/sdhci.h" #include "qom/object.h" #include "qemu/units.h" @@ -28,6 +29,7 @@ enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_CPUS = 4, FSL_IMX8MP_NUM_IRQS = 160, FSL_IMX8MP_NUM_UARTS = 4, + FSL_IMX8MP_NUM_USDHCS = 3, }; struct FslImx8mpState { @@ -39,6 +41,7 @@ struct FslImx8mpState { IMX8MPAnalogState analog; IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; + SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; }; enum FslImx8mpMemoryRegions { @@ -184,6 +187,10 @@ enum FslImx8mpMemoryRegions { }; enum FslImx8mpIrqs { + FSL_IMX8MP_USDHC1_IRQ = 22, + FSL_IMX8MP_USDHC2_IRQ = 23, + FSL_IMX8MP_USDHC3_IRQ = 24, + FSL_IMX8MP_UART1_IRQ = 26, FSL_IMX8MP_UART2_IRQ = 27, FSL_IMX8MP_UART3_IRQ = 28, From fd1deb5301f89eb86c0eecadb670beb98aa74ac5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:58 +0100 Subject: [PATCH 1968/2892] hw/arm/fsl-imx8mp: Add PCIe support Linux checks for the PLLs in the PHY to be locked, so implement a model emulating that. Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-9-shentey@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 2 + docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 3 + hw/arm/fsl-imx8mp.c | 30 +++++++++ hw/pci-host/Kconfig | 3 + hw/pci-host/fsl_imx8m_phy.c | 98 +++++++++++++++++++++++++++++ hw/pci-host/meson.build | 1 + include/hw/arm/fsl-imx8mp.h | 10 +++ include/hw/pci-host/fsl_imx8m_phy.h | 28 +++++++++ 9 files changed, 176 insertions(+) create mode 100644 hw/pci-host/fsl_imx8m_phy.c create mode 100644 include/hw/pci-host/fsl_imx8m_phy.h diff --git a/MAINTAINERS b/MAINTAINERS index 8ea7fb4c7a..2e7fc6fa91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -827,8 +827,10 @@ S: Maintained F: hw/arm/imx8mp-evk.c F: hw/arm/fsl-imx8mp.c F: hw/misc/imx8mp_*.c +F: hw/pci-host/fsl_imx8m_phy.c F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h +F: include/hw/pci-host/fsl_imx8m_phy.h F: docs/system/arm/imx8mp-evk.rst MPS2 / MPS3 diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 879c822356..18a8fdd278 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -13,6 +13,7 @@ The ``imx8mp-evk`` machine implements the following devices: * Generic Interrupt Controller (GICv3) * 4 UARTs * 3 USDHC Storage Controllers + * 1 Designware PCI Express Controller * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index d2dda3213d..be5a2c02b7 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -595,10 +595,13 @@ config FSL_IMX7 config FSL_IMX8MP bool + imply PCI_DEVICES select ARM_GIC select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select PCI_EXPRESS_DESIGNWARE + select PCI_EXPRESS_FSL_IMX8M_PHY select SDHCI select UNIMP diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index da9eaeb6ff..1ee681ac1d 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -212,6 +212,10 @@ static void fsl_imx8mp_init(Object *obj) g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie_phy", &s->pcie_phy, + TYPE_FSL_IMX8M_PCIE_PHY); } static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) @@ -380,6 +384,30 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, fsl_imx8mp_memmap[FSL_IMX8MP_SNVS_HP].addr); + /* PCIe */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_PCIE1].addr); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTA_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTB_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTD_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_MSI_IRQ)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_PCIE_PHY1].addr); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { switch (i) { @@ -387,6 +415,8 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_CCM: case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: + case FSL_IMX8MP_PCIE1: + case FSL_IMX8MP_PCIE_PHY1: case FSL_IMX8MP_RAM: case FSL_IMX8MP_SNVS_HP: case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index c91880b237..35c0415242 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -99,6 +99,9 @@ config ASTRO bool select PCI +config PCI_EXPRESS_FSL_IMX8M_PHY + bool + config GT64120 bool select PCI diff --git a/hw/pci-host/fsl_imx8m_phy.c b/hw/pci-host/fsl_imx8m_phy.c new file mode 100644 index 0000000000..aa304b102b --- /dev/null +++ b/hw/pci-host/fsl_imx8m_phy.c @@ -0,0 +1,98 @@ +/* + * i.MX8 PCIe PHY emulation + * + * Copyright (c) 2025 Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/pci-host/fsl_imx8m_phy.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" + +#define CMN_REG075 0x1d4 +#define ANA_PLL_LOCK_DONE BIT(1) +#define ANA_PLL_AFC_DONE BIT(0) + +static uint64_t fsl_imx8m_pcie_phy_read(void *opaque, hwaddr offset, + unsigned size) +{ + FslImx8mPciePhyState *s = opaque; + + if (offset == CMN_REG075) { + return s->data[offset] | ANA_PLL_LOCK_DONE | ANA_PLL_AFC_DONE; + } + + return s->data[offset]; +} + +static void fsl_imx8m_pcie_phy_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + FslImx8mPciePhyState *s = opaque; + + s->data[offset] = value; +} + +static const MemoryRegionOps fsl_imx8m_pcie_phy_ops = { + .read = fsl_imx8m_pcie_phy_read, + .write = fsl_imx8m_pcie_phy_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void fsl_imx8m_pcie_phy_realize(DeviceState *dev, Error **errp) +{ + FslImx8mPciePhyState *s = FSL_IMX8M_PCIE_PHY(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &fsl_imx8m_pcie_phy_ops, s, + TYPE_FSL_IMX8M_PCIE_PHY, ARRAY_SIZE(s->data)); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void fsl_imx8m_pcie_phy_reset_hold(Object *obj, ResetType type) +{ + FslImx8mPciePhyState *s = FSL_IMX8M_PCIE_PHY(obj); + + memset(s->data, 0, sizeof(s->data)); +} + +static const VMStateDescription fsl_imx8m_pcie_phy_vmstate = { + .name = "fsl-imx8m-pcie-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(data, FslImx8mPciePhyState, + FSL_IMX8M_PCIE_PHY_DATA_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void fsl_imx8m_pcie_phy_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = fsl_imx8m_pcie_phy_realize; + dc->vmsd = &fsl_imx8m_pcie_phy_vmstate; + rc->phases.hold = fsl_imx8m_pcie_phy_reset_hold; +} + +static const TypeInfo fsl_imx8m_pcie_phy_types[] = { + { + .name = TYPE_FSL_IMX8M_PCIE_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FslImx8mPciePhyState), + .class_init = fsl_imx8m_pcie_phy_class_init, + } +}; + +DEFINE_TYPES(fsl_imx8m_pcie_phy_types) diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 3001e93a43..937a0f72ac 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -28,6 +28,7 @@ pci_ss.add(when: 'CONFIG_ARTICIA', if_true: files('articia.c')) pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) # ARM devices +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_FSL_IMX8M_PHY', if_true: files('fsl_imx8m_phy.c')) pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) # HPPA devices diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 349d55ca88..4c70c887a8 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -15,6 +15,8 @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/pci-host/designware.h" +#include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "qom/object.h" #include "qemu/units.h" @@ -42,6 +44,8 @@ struct FslImx8mpState { IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; + DesignwarePCIEHost pcie; + FslImx8mPciePhyState pcie_phy; }; enum FslImx8mpMemoryRegions { @@ -197,6 +201,12 @@ enum FslImx8mpIrqs { FSL_IMX8MP_UART4_IRQ = 29, FSL_IMX8MP_UART5_IRQ = 30, FSL_IMX8MP_UART6_IRQ = 16, + + FSL_IMX8MP_PCI_INTA_IRQ = 126, + FSL_IMX8MP_PCI_INTB_IRQ = 125, + FSL_IMX8MP_PCI_INTC_IRQ = 124, + FSL_IMX8MP_PCI_INTD_IRQ = 123, + FSL_IMX8MP_PCI_MSI_IRQ = 140, }; #endif /* FSL_IMX8MP_H */ diff --git a/include/hw/pci-host/fsl_imx8m_phy.h b/include/hw/pci-host/fsl_imx8m_phy.h new file mode 100644 index 0000000000..4f4875b37d --- /dev/null +++ b/include/hw/pci-host/fsl_imx8m_phy.h @@ -0,0 +1,28 @@ +/* + * i.MX8 PCIe PHY emulation + * + * Copyright (c) 2025 Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PCIHOST_FSLIMX8MPCIEPHY_H +#define HW_PCIHOST_FSLIMX8MPCIEPHY_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "exec/memory.h" + +#define TYPE_FSL_IMX8M_PCIE_PHY "fsl-imx8m-pcie-phy" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mPciePhyState, FSL_IMX8M_PCIE_PHY) + +#define FSL_IMX8M_PCIE_PHY_DATA_SIZE 0x800 + +struct FslImx8mPciePhyState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint8_t data[FSL_IMX8M_PCIE_PHY_DATA_SIZE]; +}; + +#endif From a17c1d932ec6ae1a3364eaf34c0660f01f806267 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:46:59 +0100 Subject: [PATCH 1969/2892] hw/arm/fsl-imx8mp: Add GPIO controllers Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-10-shentey@gmail.com [PMM: drop static const from gpio_table for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/fsl-imx8mp.c | 55 ++++++++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mp.h | 14 +++++++++ 3 files changed, 70 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 18a8fdd278..37d3630d09 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -14,6 +14,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 4 UARTs * 3 USDHC Storage Controllers * 1 Designware PCI Express Controller + * 5 GPIO Controllers * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 1ee681ac1d..541e4ab5b6 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -208,6 +208,11 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i + 1); + object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); + } + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); @@ -355,6 +360,55 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, serial_table[i].irq)); } + /* GPIOs */ + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { + struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX8MP_NUM_GPIOS] = { + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO1].addr, + FSL_IMX8MP_GPIO1_LOW_IRQ, + FSL_IMX8MP_GPIO1_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO2].addr, + FSL_IMX8MP_GPIO2_LOW_IRQ, + FSL_IMX8MP_GPIO2_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO3].addr, + FSL_IMX8MP_GPIO3_LOW_IRQ, + FSL_IMX8MP_GPIO3_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO4].addr, + FSL_IMX8MP_GPIO4_LOW_IRQ, + FSL_IMX8MP_GPIO4_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO5].addr, + FSL_IMX8MP_GPIO5_LOW_IRQ, + FSL_IMX8MP_GPIO5_HIGH_IRQ + }, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), "has-edge-sel", true, + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), "has-upper-pin-irq", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_high)); + } + /* USDHCs */ for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { struct { @@ -415,6 +469,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_CCM: case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: + case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: case FSL_IMX8MP_PCIE1: case FSL_IMX8MP_PCIE_PHY1: case FSL_IMX8MP_RAM: diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 4c70c887a8..18ea52d083 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -11,6 +11,7 @@ #include "cpu.h" #include "hw/char/imx_serial.h" +#include "hw/gpio/imx_gpio.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" @@ -29,6 +30,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_CPUS = 4, + FSL_IMX8MP_NUM_GPIOS = 5, FSL_IMX8MP_NUM_IRQS = 160, FSL_IMX8MP_NUM_UARTS = 4, FSL_IMX8MP_NUM_USDHCS = 3, @@ -39,6 +41,7 @@ struct FslImx8mpState { ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; GICv3State gic; + IMXGPIOState gpio[FSL_IMX8MP_NUM_GPIOS]; IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; @@ -202,6 +205,17 @@ enum FslImx8mpIrqs { FSL_IMX8MP_UART5_IRQ = 30, FSL_IMX8MP_UART6_IRQ = 16, + FSL_IMX8MP_GPIO1_LOW_IRQ = 64, + FSL_IMX8MP_GPIO1_HIGH_IRQ = 65, + FSL_IMX8MP_GPIO2_LOW_IRQ = 66, + FSL_IMX8MP_GPIO2_HIGH_IRQ = 67, + FSL_IMX8MP_GPIO3_LOW_IRQ = 68, + FSL_IMX8MP_GPIO3_HIGH_IRQ = 69, + FSL_IMX8MP_GPIO4_LOW_IRQ = 70, + FSL_IMX8MP_GPIO4_HIGH_IRQ = 71, + FSL_IMX8MP_GPIO5_LOW_IRQ = 72, + FSL_IMX8MP_GPIO5_HIGH_IRQ = 73, + FSL_IMX8MP_PCI_INTA_IRQ = 126, FSL_IMX8MP_PCI_INTB_IRQ = 125, FSL_IMX8MP_PCI_INTC_IRQ = 124, From 764f18afb2b749a9dcfd37bac5709e7a7bcd2589 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:00 +0100 Subject: [PATCH 1970/2892] hw/arm/fsl-imx8mp: Add I2C controllers Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-11-shentey@gmail.com [PMM: drop static const from i2c_table for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 2 ++ hw/arm/fsl-imx8mp.c | 29 +++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mp.h | 11 +++++++++++ 4 files changed, 43 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 37d3630d09..ef0d997250 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -15,6 +15,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 3 USDHC Storage Controllers * 1 Designware PCI Express Controller * 5 GPIO Controllers + * 6 I2C Controllers * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index be5a2c02b7..28ae409c85 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -595,11 +595,13 @@ config FSL_IMX7 config FSL_IMX8MP bool + imply I2C_DEVICES imply PCI_DEVICES select ARM_GIC select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select IMX_I2C select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY select SDHCI diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 541e4ab5b6..750dbf9eab 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -208,6 +208,11 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { + g_autofree char *name = g_strdup_printf("i2c%d", i + 1); + object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); + } + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { g_autofree char *name = g_strdup_printf("gpio%d", i + 1); object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); @@ -360,6 +365,29 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, serial_table[i].irq)); } + /* I2Cs */ + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX8MP_NUM_I2CS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C1].addr, FSL_IMX8MP_I2C1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C2].addr, FSL_IMX8MP_I2C2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C3].addr, FSL_IMX8MP_I2C3_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C4].addr, FSL_IMX8MP_I2C4_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C5].addr, FSL_IMX8MP_I2C5_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C6].addr, FSL_IMX8MP_I2C6_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(gicdev, i2c_table[i].irq)); + } + /* GPIOs */ for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { struct { @@ -470,6 +498,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: + case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: case FSL_IMX8MP_PCIE1: case FSL_IMX8MP_PCIE_PHY1: case FSL_IMX8MP_RAM: diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 18ea52d083..2590056627 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -12,6 +12,7 @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/gpio/imx_gpio.h" +#include "hw/i2c/imx_i2c.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" @@ -31,6 +32,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_CPUS = 4, FSL_IMX8MP_NUM_GPIOS = 5, + FSL_IMX8MP_NUM_I2CS = 6, FSL_IMX8MP_NUM_IRQS = 160, FSL_IMX8MP_NUM_UARTS = 4, FSL_IMX8MP_NUM_USDHCS = 3, @@ -45,6 +47,7 @@ struct FslImx8mpState { IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; + IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; DesignwarePCIEHost pcie; @@ -205,6 +208,11 @@ enum FslImx8mpIrqs { FSL_IMX8MP_UART5_IRQ = 30, FSL_IMX8MP_UART6_IRQ = 16, + FSL_IMX8MP_I2C1_IRQ = 35, + FSL_IMX8MP_I2C2_IRQ = 36, + FSL_IMX8MP_I2C3_IRQ = 37, + FSL_IMX8MP_I2C4_IRQ = 38, + FSL_IMX8MP_GPIO1_LOW_IRQ = 64, FSL_IMX8MP_GPIO1_HIGH_IRQ = 65, FSL_IMX8MP_GPIO2_LOW_IRQ = 66, @@ -216,6 +224,9 @@ enum FslImx8mpIrqs { FSL_IMX8MP_GPIO5_LOW_IRQ = 72, FSL_IMX8MP_GPIO5_HIGH_IRQ = 73, + FSL_IMX8MP_I2C5_IRQ = 76, + FSL_IMX8MP_I2C6_IRQ = 77, + FSL_IMX8MP_PCI_INTA_IRQ = 126, FSL_IMX8MP_PCI_INTB_IRQ = 125, FSL_IMX8MP_PCI_INTC_IRQ = 124, From 06908a84f036d7cefb834f8d67cf8b80a1791838 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:01 +0100 Subject: [PATCH 1971/2892] hw/arm/fsl-imx8mp: Add SPI controllers Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-12-shentey@gmail.com [PMM: drop static const from spi_table for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/fsl-imx8mp.c | 26 ++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mp.h | 8 ++++++++ 3 files changed, 35 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index ef0d997250..66e5865107 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -16,6 +16,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 1 Designware PCI Express Controller * 5 GPIO Controllers * 6 I2C Controllers + * 3 SPI Controllers * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 750dbf9eab..63f07eca8a 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -223,6 +223,11 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { + g_autofree char *name = g_strdup_printf("spi%d", i + 1); + object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); + } + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -459,6 +464,26 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); } + /* ECSPIs */ + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX8MP_NUM_ECSPIS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI1].addr, FSL_IMX8MP_ECSPI1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI2].addr, FSL_IMX8MP_ECSPI2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI3].addr, FSL_IMX8MP_ECSPI3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(gicdev, spi_table[i].irq)); + } + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -498,6 +523,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: + case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3: case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: case FSL_IMX8MP_PCIE1: case FSL_IMX8MP_PCIE_PHY1: diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 2590056627..296a87eb50 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -20,6 +20,7 @@ #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" #include "qom/object.h" #include "qemu/units.h" @@ -31,6 +32,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_CPUS = 4, + FSL_IMX8MP_NUM_ECSPIS = 3, FSL_IMX8MP_NUM_GPIOS = 5, FSL_IMX8MP_NUM_I2CS = 6, FSL_IMX8MP_NUM_IRQS = 160, @@ -47,6 +49,7 @@ struct FslImx8mpState { IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; + IMXSPIState spi[FSL_IMX8MP_NUM_ECSPIS]; IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; @@ -208,6 +211,11 @@ enum FslImx8mpIrqs { FSL_IMX8MP_UART5_IRQ = 30, FSL_IMX8MP_UART6_IRQ = 16, + FSL_IMX8MP_ECSPI1_IRQ = 31, + FSL_IMX8MP_ECSPI2_IRQ = 32, + FSL_IMX8MP_ECSPI3_IRQ = 33, + FSL_IMX8MP_ECSPI4_IRQ = 34, + FSL_IMX8MP_I2C1_IRQ = 35, FSL_IMX8MP_I2C2_IRQ = 36, FSL_IMX8MP_I2C3_IRQ = 37, From 1ac21eb8fbb0297716a6c525e91196a247302b2b Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:02 +0100 Subject: [PATCH 1972/2892] hw/arm/fsl-imx8mp: Add watchdog support Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-13-shentey@gmail.com [PMM: drop static const from wdog_table for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mp.c | 28 ++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mp.h | 7 +++++++ 4 files changed, 37 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 66e5865107..904de9aa7d 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -17,6 +17,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 5 GPIO Controllers * 6 I2C Controllers * 3 SPI Controllers + * 3 Watchdogs * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 28ae409c85..98ac93a23f 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -606,6 +606,7 @@ config FSL_IMX8MP select PCI_EXPRESS_FSL_IMX8M_PHY select SDHCI select UNIMP + select WDT_IMX2 config FSL_IMX8MP_EVK bool diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 63f07eca8a..762f2a52d8 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -228,6 +228,11 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); } + for (i = 0; i < FSL_IMX8MP_NUM_WDTS; i++) { + g_autofree char *name = g_strdup_printf("wdt%d", i); + object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); + } + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -491,6 +496,28 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, fsl_imx8mp_memmap[FSL_IMX8MP_SNVS_HP].addr); + /* Watchdogs */ + for (i = 0; i < FSL_IMX8MP_NUM_WDTS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } wdog_table[FSL_IMX8MP_NUM_WDTS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG1].addr, FSL_IMX8MP_WDOG1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG2].addr, FSL_IMX8MP_WDOG2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG3].addr, FSL_IMX8MP_WDOG3_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), "pretimeout-support", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, wdog_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, + qdev_get_gpio_in(gicdev, wdog_table[i].irq)); + } + /* PCIe */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { return; @@ -531,6 +558,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_SNVS_HP: case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: case FSL_IMX8MP_USDHC1 ... FSL_IMX8MP_USDHC3: + case FSL_IMX8MP_WDOG1 ... FSL_IMX8MP_WDOG3: /* device implemented and treated above */ break; diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 296a87eb50..dfbdc6ac7f 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -21,6 +21,7 @@ #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" +#include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -38,6 +39,7 @@ enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_IRQS = 160, FSL_IMX8MP_NUM_UARTS = 4, FSL_IMX8MP_NUM_USDHCS = 3, + FSL_IMX8MP_NUM_WDTS = 3, }; struct FslImx8mpState { @@ -53,6 +55,7 @@ struct FslImx8mpState { IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX8MP_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; }; @@ -235,6 +238,10 @@ enum FslImx8mpIrqs { FSL_IMX8MP_I2C5_IRQ = 76, FSL_IMX8MP_I2C6_IRQ = 77, + FSL_IMX8MP_WDOG1_IRQ = 78, + FSL_IMX8MP_WDOG2_IRQ = 79, + FSL_IMX8MP_WDOG3_IRQ = 10, + FSL_IMX8MP_PCI_INTA_IRQ = 126, FSL_IMX8MP_PCI_INTB_IRQ = 125, FSL_IMX8MP_PCI_INTC_IRQ = 124, From f8b26121762c17af9869b0ec7ccbda6df4ea37f8 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:03 +0100 Subject: [PATCH 1973/2892] hw/arm/fsl-imx8mp: Implement general purpose timers Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-14-shentey@gmail.com [PMM: drop static const from gpt_attrs for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mp.c | 53 ++++++++++++++++++++++++++++++++++ hw/timer/imx_gpt.c | 25 ++++++++++++++++ include/hw/arm/fsl-imx8mp.h | 11 +++++++ include/hw/timer/imx_gpt.h | 1 + 6 files changed, 92 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 904de9aa7d..4b195c917f 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -18,6 +18,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 6 I2C Controllers * 3 SPI Controllers * 3 Watchdogs + * 6 General Purpose Timers * Secure Non-Volatile Storage (SNVS) including an RTC * Clock Tree diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 98ac93a23f..4e83895b91 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -602,6 +602,7 @@ config FSL_IMX8MP select FSL_IMX8MP_CCM select IMX select IMX_I2C + select OR_IRQ select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY select SDHCI diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 762f2a52d8..185c32ee58 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -208,6 +208,13 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MP_NUM_GPTS; i++) { + g_autofree char *name = g_strdup_printf("gpt%d", i + 1); + object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX8MP_GPT); + } + object_initialize_child(obj, "gpt5-gpt6-irq", &s->gpt5_gpt6_irq, + TYPE_OR_IRQ); + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { g_autofree char *name = g_strdup_printf("i2c%d", i + 1); object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); @@ -375,6 +382,52 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, serial_table[i].irq)); } + /* GPTs */ + object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2, + &error_abort); + if (!qdev_realize(DEVICE(&s->gpt5_gpt6_irq), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->gpt5_gpt6_irq), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_GPT5_GPT6_IRQ)); + + for (i = 0; i < FSL_IMX8MP_NUM_GPTS; i++) { + hwaddr gpt_addrs[FSL_IMX8MP_NUM_GPTS] = { + fsl_imx8mp_memmap[FSL_IMX8MP_GPT1].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT2].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT3].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT4].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT5].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT6].addr, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_addrs[i]); + + if (i < FSL_IMX8MP_NUM_GPTS - 2) { + static const unsigned int gpt_irqs[FSL_IMX8MP_NUM_GPTS - 2] = { + FSL_IMX8MP_GPT1_IRQ, + FSL_IMX8MP_GPT2_IRQ, + FSL_IMX8MP_GPT3_IRQ, + FSL_IMX8MP_GPT4_IRQ, + }; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(gicdev, gpt_irqs[i])); + } else { + int irq = i - FSL_IMX8MP_NUM_GPTS + 2; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gpt5_gpt6_irq), irq)); + } + } + /* I2Cs */ for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { struct { diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 11eca9fa4d..200a89225b 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -126,6 +126,17 @@ static const IMXClk imx7_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx8mp_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz */ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH, /* 101 ipg_clk_16M */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + /* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_set_freq(IMXGPTState *s) { @@ -552,6 +563,13 @@ static void imx7_gpt_init(Object *obj) s->clocks = imx7_gpt_clocks; } +static void imx8mp_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx8mp_gpt_clocks; +} + static const TypeInfo imx25_gpt_info = { .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, @@ -584,6 +602,12 @@ static const TypeInfo imx7_gpt_info = { .instance_init = imx7_gpt_init, }; +static const TypeInfo imx8mp_gpt_info = { + .name = TYPE_IMX8MP_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx8mp_gpt_init, +}; + static void imx_gpt_register_types(void) { type_register_static(&imx25_gpt_info); @@ -591,6 +615,7 @@ static void imx_gpt_register_types(void) type_register_static(&imx6_gpt_info); type_register_static(&imx6ul_gpt_info); type_register_static(&imx7_gpt_info); + type_register_static(&imx8mp_gpt_info); } type_init(imx_gpt_register_types) diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index dfbdc6ac7f..975887751b 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -17,10 +17,12 @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/or-irq.h" #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" +#include "hw/timer/imx_gpt.h" #include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -35,6 +37,7 @@ enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_CPUS = 4, FSL_IMX8MP_NUM_ECSPIS = 3, FSL_IMX8MP_NUM_GPIOS = 5, + FSL_IMX8MP_NUM_GPTS = 6, FSL_IMX8MP_NUM_I2CS = 6, FSL_IMX8MP_NUM_IRQS = 160, FSL_IMX8MP_NUM_UARTS = 4, @@ -47,6 +50,7 @@ struct FslImx8mpState { ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; GICv3State gic; + IMXGPTState gpt[FSL_IMX8MP_NUM_GPTS]; IMXGPIOState gpio[FSL_IMX8MP_NUM_GPIOS]; IMX8MPCCMState ccm; IMX8MPAnalogState analog; @@ -58,6 +62,7 @@ struct FslImx8mpState { IMX2WdtState wdt[FSL_IMX8MP_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; + OrIRQState gpt5_gpt6_irq; }; enum FslImx8mpMemoryRegions { @@ -224,6 +229,12 @@ enum FslImx8mpIrqs { FSL_IMX8MP_I2C3_IRQ = 37, FSL_IMX8MP_I2C4_IRQ = 38, + FSL_IMX8MP_GPT1_IRQ = 55, + FSL_IMX8MP_GPT2_IRQ = 54, + FSL_IMX8MP_GPT3_IRQ = 53, + FSL_IMX8MP_GPT4_IRQ = 52, + FSL_IMX8MP_GPT5_GPT6_IRQ = 51, + FSL_IMX8MP_GPIO1_LOW_IRQ = 64, FSL_IMX8MP_GPIO1_HIGH_IRQ = 65, FSL_IMX8MP_GPIO2_LOW_IRQ = 66, diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index 5a1230da35..5488f7e4df 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -80,6 +80,7 @@ #define TYPE_IMX6_GPT "imx6.gpt" #define TYPE_IMX6UL_GPT "imx6ul.gpt" #define TYPE_IMX7_GPT "imx7.gpt" +#define TYPE_IMX8MP_GPT "imx8mp.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT From 0c105b261551a9b9fed086a7b9ecd0b6d6063bc4 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:04 +0100 Subject: [PATCH 1974/2892] hw/arm/fsl-imx8mp: Add Ethernet controller The i.MX 8M Plus SoC actually has two ethernet controllers, the usual ENET one and a Designware one. There is no device model for the latter, so only add the ENET one. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-15-shentey@gmail.com Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mp.c | 24 ++++++++++++++++++++++++ hw/arm/imx8mp-evk.c | 1 + include/hw/arm/fsl-imx8mp.h | 8 ++++++++ 5 files changed, 35 insertions(+) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 4b195c917f..917c1d5176 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -14,6 +14,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 4 UARTs * 3 USDHC Storage Controllers * 1 Designware PCI Express Controller + * 1 Ethernet Controller * 5 GPIO Controllers * 6 I2C Controllers * 3 SPI Controllers diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 4e83895b91..4d642db970 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -601,6 +601,7 @@ config FSL_IMX8MP select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select IMX_FEC select IMX_I2C select OR_IRQ select PCI_EXPRESS_DESIGNWARE diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 185c32ee58..2dd3c97a02 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -240,6 +240,8 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); } + object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET); + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -542,6 +544,21 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, spi_table[i].irq)); } + /* ENET1 */ + object_property_set_uint(OBJECT(&s->enet), "phy-num", s->phy_num, + &error_abort); + object_property_set_uint(OBJECT(&s->enet), "tx-ring-num", 3, &error_abort); + qemu_configure_nic_device(DEVICE(&s->enet), true, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->enet), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->enet), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_ENET1].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_ENET1_MAC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 1, + qdev_get_gpio_in(gicdev, FSL_IMX6_ENET1_MAC_1588_IRQ)); + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -604,6 +621,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3: + case FSL_IMX8MP_ENET1: case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: case FSL_IMX8MP_PCIE1: case FSL_IMX8MP_PCIE_PHY1: @@ -624,10 +642,16 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) } } +static const Property fsl_imx8mp_properties[] = { + DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mpState, phy_num, 0), + DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mpState, phy_connected, true), +}; + static void fsl_imx8mp_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, fsl_imx8mp_properties); dc->realize = fsl_imx8mp_realize; dc->desc = "i.MX 8M Plus SoC"; diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index 27d9e9e8ee..e1a7892fd7 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -36,6 +36,7 @@ static void imx8mp_evk_init(MachineState *machine) s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); + object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); qdev_realize(DEVICE(s), NULL, &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 975887751b..e292c31a3d 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -17,6 +17,7 @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/net/imx_fec.h" #include "hw/or-irq.h" #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" @@ -58,11 +59,15 @@ struct FslImx8mpState { IMXSPIState spi[FSL_IMX8MP_NUM_ECSPIS]; IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; + IMXFECState enet; SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX8MP_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; + + uint32_t phy_num; + bool phy_connected; }; enum FslImx8mpMemoryRegions { @@ -253,6 +258,9 @@ enum FslImx8mpIrqs { FSL_IMX8MP_WDOG2_IRQ = 79, FSL_IMX8MP_WDOG3_IRQ = 10, + FSL_IMX8MP_ENET1_MAC_IRQ = 118, + FSL_IMX6_ENET1_MAC_1588_IRQ = 121, + FSL_IMX8MP_PCI_INTA_IRQ = 126, FSL_IMX8MP_PCI_INTB_IRQ = 125, FSL_IMX8MP_PCI_INTC_IRQ = 124, From 4226c39fea1490060163339ae45500bda1b1be05 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:05 +0100 Subject: [PATCH 1975/2892] hw/arm/fsl-imx8mp: Add USB support Split the USB MMIO regions to better keep track of the implemented vs. unimplemented regions. Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-16-shentey@gmail.com [PMM: drop "static const" from usb_table for GCC 7.5] Signed-off-by: Peter Maydell --- docs/system/arm/imx8mp-evk.rst | 1 + hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mp.c | 37 ++++++++++++++++++++++++++++++++-- include/hw/arm/fsl-imx8mp.h | 12 +++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 917c1d5176..00527b0cbe 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -15,6 +15,7 @@ The ``imx8mp-evk`` machine implements the following devices: * 3 USDHC Storage Controllers * 1 Designware PCI Express Controller * 1 Ethernet Controller + * 2 Designware USB 3 Controllers * 5 GPIO Controllers * 6 I2C Controllers * 3 SPI Controllers diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 4d642db970..faa00d1db3 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -608,6 +608,7 @@ config FSL_IMX8MP select PCI_EXPRESS_FSL_IMX8M_PHY select SDHCI select UNIMP + select USB_DWC3 select WDT_IMX2 config FSL_IMX8MP_EVK diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 2dd3c97a02..0880f0c724 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -40,8 +40,14 @@ static const struct { [FSL_IMX8MP_VPU_VC8000E_ENCODER] = { 0x38320000, 2 * MiB, "vpu_vc8000e_encoder" }, [FSL_IMX8MP_VPU_G2_DECODER] = { 0x38310000, 2 * MiB, "vpu_g2_decoder" }, [FSL_IMX8MP_VPU_G1_DECODER] = { 0x38300000, 2 * MiB, "vpu_g1_decoder" }, - [FSL_IMX8MP_USB2] = { 0x38200000, 1 * MiB, "usb2" }, - [FSL_IMX8MP_USB1] = { 0x38100000, 1 * MiB, "usb1" }, + [FSL_IMX8MP_USB2_GLUE] = { 0x382f0000, 0x100, "usb2_glue" }, + [FSL_IMX8MP_USB2_OTG] = { 0x3820cc00, 0x100, "usb2_otg" }, + [FSL_IMX8MP_USB2_DEV] = { 0x3820c700, 0x500, "usb2_dev" }, + [FSL_IMX8MP_USB2] = { 0x38200000, 0xc700, "usb2" }, + [FSL_IMX8MP_USB1_GLUE] = { 0x381f0000, 0x100, "usb1_glue" }, + [FSL_IMX8MP_USB1_OTG] = { 0x3810cc00, 0x100, "usb1_otg" }, + [FSL_IMX8MP_USB1_DEV] = { 0x3810c700, 0x500, "usb1_dev" }, + [FSL_IMX8MP_USB1] = { 0x38100000, 0xc700, "usb1" }, [FSL_IMX8MP_GPU2D] = { 0x38008000, 32 * KiB, "gpu2d" }, [FSL_IMX8MP_GPU3D] = { 0x38000000, 32 * KiB, "gpu3d" }, [FSL_IMX8MP_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" }, @@ -230,6 +236,11 @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX8MP_NUM_USBS; i++) { + g_autofree char *name = g_strdup_printf("usb%d", i); + object_initialize_child(obj, name, &s->usb[i], TYPE_USB_DWC3); + } + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { g_autofree char *name = g_strdup_printf("spi%d", i + 1); object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); @@ -524,6 +535,27 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); } + /* USBs */ + for (i = 0; i < FSL_IMX8MP_NUM_USBS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX8MP_NUM_USBS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_USB1].addr, FSL_IMX8MP_USB1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USB2].addr, FSL_IMX8MP_USB2_IRQ }, + }; + + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p2", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p3", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0, + qdev_get_gpio_in(gicdev, usb_table[i].irq)); + } + /* ECSPIs */ for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { struct { @@ -628,6 +660,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_RAM: case FSL_IMX8MP_SNVS_HP: case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: + case FSL_IMX8MP_USB1 ... FSL_IMX8MP_USB2: case FSL_IMX8MP_USDHC1 ... FSL_IMX8MP_USDHC3: case FSL_IMX8MP_WDOG1 ... FSL_IMX8MP_WDOG3: /* device implemented and treated above */ diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index e292c31a3d..5247e972b8 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -24,6 +24,7 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/timer/imx_gpt.h" +#include "hw/usb/hcd-dwc3.h" #include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -42,6 +43,7 @@ enum FslImx8mpConfiguration { FSL_IMX8MP_NUM_I2CS = 6, FSL_IMX8MP_NUM_IRQS = 160, FSL_IMX8MP_NUM_UARTS = 4, + FSL_IMX8MP_NUM_USBS = 2, FSL_IMX8MP_NUM_USDHCS = 3, FSL_IMX8MP_NUM_WDTS = 3, }; @@ -62,6 +64,7 @@ struct FslImx8mpState { IMXFECState enet; SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX8MP_NUM_WDTS]; + USBDWC3 usb[FSL_IMX8MP_NUM_USBS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; @@ -199,6 +202,12 @@ enum FslImx8mpMemoryRegions { FSL_IMX8MP_UART4, FSL_IMX8MP_USB1, FSL_IMX8MP_USB2, + FSL_IMX8MP_USB1_DEV, + FSL_IMX8MP_USB2_DEV, + FSL_IMX8MP_USB1_OTG, + FSL_IMX8MP_USB2_OTG, + FSL_IMX8MP_USB1_GLUE, + FSL_IMX8MP_USB2_GLUE, FSL_IMX8MP_USDHC1, FSL_IMX8MP_USDHC2, FSL_IMX8MP_USDHC3, @@ -234,6 +243,9 @@ enum FslImx8mpIrqs { FSL_IMX8MP_I2C3_IRQ = 37, FSL_IMX8MP_I2C4_IRQ = 38, + FSL_IMX8MP_USB1_IRQ = 40, + FSL_IMX8MP_USB2_IRQ = 41, + FSL_IMX8MP_GPT1_IRQ = 55, FSL_IMX8MP_GPT2_IRQ = 54, FSL_IMX8MP_GPT3_IRQ = 53, From 1aaf3478684ff1cd02d1b36c32a00bfac9a5dbd5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:07 +0100 Subject: [PATCH 1976/2892] hw/arm/fsl-imx8mp: Add on-chip RAM Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-id: 20250223114708.1780-18-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/arm/fsl-imx8mp.c | 11 +++++++++++ include/hw/arm/fsl-imx8mp.h | 1 + 2 files changed, 12 insertions(+) diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 0880f0c724..1ea98e1463 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -644,6 +644,16 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, fsl_imx8mp_memmap[FSL_IMX8MP_PCIE_PHY1].addr); + /* On-Chip RAM */ + if (!memory_region_init_ram(&s->ocram, NULL, "imx8mp.ocram", + fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].size, + errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), + fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].addr, + &s->ocram); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { switch (i) { @@ -655,6 +665,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3: case FSL_IMX8MP_ENET1: case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: + case FSL_IMX8MP_OCRAM: case FSL_IMX8MP_PCIE1: case FSL_IMX8MP_PCIE_PHY1: case FSL_IMX8MP_RAM: diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 5247e972b8..bc97fc416e 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -68,6 +68,7 @@ struct FslImx8mpState { DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; + MemoryRegion ocram; uint32_t phy_num; bool phy_connected; From 006453f4003aa376086aa1faa13f96e624fdc940 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:31 +0100 Subject: [PATCH 1977/2892] tests/functional: Provide a proper name for the VMs in the replay tests With a proper name the log files get a more meaningful name. Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-2-thuth@redhat.com> --- tests/functional/replay_kernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py index 8e8ac7d052..80795eb052 100644 --- a/tests/functional/replay_kernel.py +++ b/tests/functional/replay_kernel.py @@ -34,7 +34,7 @@ class ReplayKernelBase(LinuxKernelTest): logger = logging.getLogger('replay') start_time = time.time() - vm = self.get_vm() + vm = self.get_vm(name='recording' if record else 'replay') vm.set_console() if record: logger.info('recording the execution...') From 6e52e84df990755b5e5cb6e9804c6c4e567adb52 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:32 +0100 Subject: [PATCH 1978/2892] tests/functional: Convert the xtensa replay test to the functional framework Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-3-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 11 ---------- tests/functional/meson.build | 1 + tests/functional/test_xtensa_replay.py | 28 ++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100755 tests/functional/test_xtensa_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index b9b54a8793..54b8417d3d 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -399,14 +399,3 @@ class ReplayKernelNormal(ReplayKernelBase): file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'zImage.elf') - def test_xtensa_lx60(self): - """ - :avocado: tags=arch:xtensa - :avocado: tags=machine:lx60 - :avocado: tags=cpu:dc233c - """ - tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day02.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 111d8bab26..4d3d62f3fe 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -296,6 +296,7 @@ tests_x86_64_system_thorough = [ tests_xtensa_system_thorough = [ 'xtensa_lx60', + 'xtensa_replay', ] precache_all = [] diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/test_xtensa_replay.py new file mode 100755 index 0000000000..eb00a3b004 --- /dev/null +++ b/tests/functional/test_xtensa_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an xtensa lx650 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class XTensaReplay(ReplayKernelBase): + + ASSET_DAY02 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day02.tar.xz', + '68ff07f9b3fd3df36d015eb46299ba44748e94bfbb2d5295fddc1a8d4a9fd324') + + def test_replay(self): + self.set_machine('lx60') + self.cpu = 'dc233c' + kernel_path = self.archive_extract(self.ASSET_DAY02, + member='day02/santas-sleigh-ride.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() From a14dfd93b55cb9545a510ac0e008479ada25ef94 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:33 +0100 Subject: [PATCH 1979/2892] tests/functional: Convert the sparc replay avocado test While we're at it, change the machine from SS-20 to SS-10 to increase the test coverage a little bit (SS-20 is already tested in the test_sparc_sun4m.py file). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-4-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 12 ------------ tests/functional/meson.build | 1 + tests/functional/test_sparc_replay.py | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100755 tests/functional/test_sparc_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 54b8417d3d..412bf9e06e 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -387,15 +387,3 @@ class ReplayKernelNormal(ReplayKernelBase): file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'invaders.elf', args=('-M', 'graphics=off')) - - def test_sparc_ss20(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:SS-20 - """ - tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day11.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'zImage.elf') - diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 4d3d62f3fe..46a97999ae 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -258,6 +258,7 @@ tests_sparc_system_quick = [ ] tests_sparc_system_thorough = [ + 'sparc_replay', 'sparc_sun4m', ] diff --git a/tests/functional/test_sparc_replay.py b/tests/functional/test_sparc_replay.py new file mode 100755 index 0000000000..865d6486f9 --- /dev/null +++ b/tests/functional/test_sparc_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on a sparc sun4m machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class SparcReplay(ReplayKernelBase): + + ASSET_DAY11 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day11.tar.xz', + 'c776533ba756bf4dd3f1fc4c024fb50ef0d853e05c5f5ddf0900a32d1eaa49e0') + + def test_replay(self): + self.set_machine('SS-10') + kernel_path = self.archive_extract(self.ASSET_DAY11, + member="day11/zImage.elf") + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() From 9917e06cd24dc260467aeccc1d1b9b6c15a0a0d8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:34 +0100 Subject: [PATCH 1980/2892] tests/functional: Convert the 32-bit ppc replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-5-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 24 -------------------- tests/functional/meson.build | 1 + tests/functional/test_ppc_replay.py | 34 +++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 24 deletions(-) create mode 100755 tests/functional/test_ppc_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 412bf9e06e..89ba6bb3e8 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -363,27 +363,3 @@ class ReplayKernelNormal(ReplayKernelBase): '/qac-best-of-multiarch/download/day20.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'vmlinux') - - def test_ppc_g3beige(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:g3beige - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_ppc_mac99(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mac99 - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 46a97999ae..63465139a0 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -198,6 +198,7 @@ tests_ppc_system_thorough = [ 'ppc_bamboo', 'ppc_mac', 'ppc_mpc8544ds', + 'ppc_replay', 'ppc_sam460ex', 'ppc_tuxrun', 'ppc_virtex_ml507', diff --git a/tests/functional/test_ppc_replay.py b/tests/functional/test_ppc_replay.py new file mode 100755 index 0000000000..8382070abd --- /dev/null +++ b/tests/functional/test_ppc_replay.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Replay tests for ppc machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class PpcReplay(ReplayKernelBase): + + ASSET_DAY15 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day15.tar.xz', + '03e0757c131d2959decf293a3572d3b96c5a53587165bf05ce41b2818a2bccd5') + + def do_day15_test(self): + self.require_accelerator("tcg") + kernel_path = self.archive_extract(self.ASSET_DAY15, + member='day15/invaders.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-M', 'graphics=off')) + + def test_g3beige(self): + self.set_machine('g3beige') + self.do_day15_test() + + def test_mac99(self): + self.set_machine('mac99') + self.do_day15_test() + + +if __name__ == '__main__': + ReplayKernelBase.main() From 52ec5f51996b5669fadaabb336dddfec77561941 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:35 +0100 Subject: [PATCH 1981/2892] tests/functional: Convert the or1k replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-6-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 11 ----------- tests/functional/meson.build | 1 + tests/functional/test_or1k_replay.py | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 11 deletions(-) create mode 100755 tests/functional/test_or1k_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 89ba6bb3e8..ef72b1622e 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -352,14 +352,3 @@ class ReplayKernelNormal(ReplayKernelBase): '/qac-best-of-multiarch/download/day19.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'uImage') - - def test_or1k_sim(self): - """ - :avocado: tags=arch:or1k - :avocado: tags=machine:or1k-sim - """ - tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day20.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'vmlinux') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 63465139a0..2062489230 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -183,6 +183,7 @@ tests_mips64el_system_thorough = [ ] tests_or1k_system_thorough = [ + 'or1k_replay', 'or1k_sim', ] diff --git a/tests/functional/test_or1k_replay.py b/tests/functional/test_or1k_replay.py new file mode 100755 index 0000000000..2b60a9372c --- /dev/null +++ b/tests/functional/test_or1k_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an OpenRISC-1000 SIM machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Or1kReplay(ReplayKernelBase): + + ASSET_DAY20 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day20.tar.xz', + 'ff9d7dd7c6bdba325bd85ee85c02db61ff653e129558aeffe6aff55bffb6763a') + + def test_sim(self): + self.set_machine('or1k-sim') + kernel_path = self.archive_extract(self.ASSET_DAY20, + member='day20/vmlinux') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() From 221620b79e8f380a3a3d65bddde067ad284d08aa Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:36 +0100 Subject: [PATCH 1982/2892] tests/functional: Convert the ppc64 replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-7-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 45 ------------------------ tests/functional/meson.build | 2 ++ tests/functional/test_ppc64_replay.py | 49 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 45 deletions(-) create mode 100755 tests/functional/test_ppc64_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index ef72b1622e..397f750046 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -242,39 +242,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, args=('-nodefaults', )) - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_ppc64_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ - 'console=tty0 console=hvc0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - def test_m68k_q800(self): """ :avocado: tags=arch:m68k @@ -340,15 +307,3 @@ class ReplayKernelNormal(ReplayKernelBase): '/qac-best-of-multiarch/download/day17.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'ballerina.bin') - - def test_ppc64_e500(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e5500 - """ - tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day19.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'uImage') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 2062489230..b68b4da6a3 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -41,6 +41,7 @@ test_timeouts = { 'ppc64_hv' : 1000, 'ppc64_powernv' : 480, 'ppc64_pseries' : 480, + 'ppc64_replay' : 210, 'ppc64_tuxrun' : 420, 'ppc64_mac99' : 120, 'riscv64_tuxrun' : 120, @@ -214,6 +215,7 @@ tests_ppc64_system_thorough = [ 'ppc64_hv', 'ppc64_powernv', 'ppc64_pseries', + 'ppc64_replay', 'ppc64_tuxrun', 'ppc64_mac99', ] diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/test_ppc64_replay.py new file mode 100755 index 0000000000..48ce1b7f1e --- /dev/null +++ b/tests/functional/test_ppc64_replay.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on ppc64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Ppc64Replay(ReplayKernelBase): + + ASSET_DAY19 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day19.tar.xz'), + '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + + def test_ppc64_e500(self): + self.set_machine('ppce500') + self.cpu = 'e5500' + kernel_path = self.archive_extract(self.ASSET_DAY19, + member='day19/uImage') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + + def test_ppc64_pseries(self): + self.set_machine('pseries') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def test_ppc64_powernv(self): + self.set_machine('powernv') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ + 'console=tty0 console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 6674fa9c345d95f0d15b64d3d553334e773255d7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:37 +0100 Subject: [PATCH 1983/2892] tests/functional: Convert the microblaze replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-8-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 11 --------- tests/functional/meson.build | 1 + tests/functional/test_microblaze_replay.py | 28 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100755 tests/functional/test_microblaze_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 397f750046..b2097afc30 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -296,14 +296,3 @@ class ReplayKernelNormal(ReplayKernelBase): '/qac-best-of-multiarch/download/day07.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'sanity-clause.elf') - - def test_microblaze_s3adsp1800(self): - """ - :avocado: tags=arch:microblaze - :avocado: tags=machine:petalogix-s3adsp1800 - """ - tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day17.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'ballerina.bin') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b68b4da6a3..58f12f54d1 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -152,6 +152,7 @@ tests_m68k_system_thorough = [ ] tests_microblaze_system_thorough = [ + 'microblaze_replay', 'microblaze_s3adsp1800' ] diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/test_microblaze_replay.py new file mode 100755 index 0000000000..7484c4186f --- /dev/null +++ b/tests/functional/test_microblaze_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an microblaze machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class MicroblazeReplay(ReplayKernelBase): + + ASSET_DAY17 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day17.tar.xz'), + '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + + def test_microblaze_s3adsp1800(self): + self.set_machine('petalogix-s3adsp1800') + kernel_path = self.archive_extract(self.ASSET_DAY17, + member='day17/ballerina.bin') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() From ec971d8554f620fda648d681467b1e10c086a8aa Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:38 +0100 Subject: [PATCH 1984/2892] tests/functional: Convert the m68k replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-9-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 29 ------------------- tests/functional/meson.build | 1 + tests/functional/test_m68k_replay.py | 42 ++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 29 deletions(-) create mode 100755 tests/functional/test_m68k_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index b2097afc30..4bd48878b7 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -242,24 +242,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, args=('-nodefaults', )) - def test_m68k_q800(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:q800 - """ - deb_url = ('https://snapshot.debian.org/archive/debian-ports' - '/20191021T083923Z/pool-m68k/main' - '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') - deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-5.3.0-1-m68k') - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 vga=off') - console_pattern = 'No filesystem could mount root' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - def do_test_advcal_2018(self, file_path, kernel_name, args=None): archive.extract(file_path, self.workdir) @@ -285,14 +267,3 @@ class ReplayKernelNormal(ReplayKernelBase): dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' self.do_test_advcal_2018(file_path, 'winter.zImage', args=('-dtb', dtb_path)) - - def test_m68k_mcf5208evb(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:mcf5208evb - """ - tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day07.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'sanity-clause.elf') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 58f12f54d1..995cea4f30 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -147,6 +147,7 @@ tests_loongarch64_system_thorough = [ tests_m68k_system_thorough = [ 'm68k_mcf5208evb', 'm68k_nextcube', + 'm68k_replay', 'm68k_q800', 'm68k_tuxrun', ] diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/test_m68k_replay.py new file mode 100755 index 0000000000..18c1db539c --- /dev/null +++ b/tests/functional/test_m68k_replay.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an m68k machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class M68kReplay(ReplayKernelBase): + + ASSET_Q800 = Asset( + ('https://snapshot.debian.org/' + 'archive/debian-ports/20191021T083923Z/pool-m68k/main/l/linux/' + 'kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb'), + '949e50d74d4b9bc15d26c06d402717b7a4c0e32ff8100014f5930d8024de7b73') + + def test_q800(self): + self.set_machine('q800') + kernel_path = self.archive_extract(self.ASSET_Q800, + member='boot/vmlinux-5.3.0-1-m68k') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + console_pattern = 'No filesystem could mount root' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + ASSET_MCF5208 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', + '753c2f3837126b7c6ba92d0b1e0b156e8a2c5131d2d576bb0b9a763fae73c08a') + + def test_mcf5208evb(self): + self.set_machine('mcf5208evb') + kernel_path = self.archive_extract(self.ASSET_MCF5208, + member='day07/sanity-clause.elf') + self.run_rr(kernel_path, self.KERNEL_COMMON_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() From 8a145225c296006efc28c4c4cf90b9e0b334ec87 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:39 +0100 Subject: [PATCH 1985/2892] tests/functional: Convert the arm replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-10-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 74 ----------------------------- tests/functional/meson.build | 1 + tests/functional/test_arm_replay.py | 69 +++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 74 deletions(-) create mode 100755 tests/functional/test_arm_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 4bd48878b7..02bd868a42 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -162,54 +162,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern) - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) - - def test_arm_cubieboard_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, - args=('-dtb', dtb_path, - '-initrd', initrd_path, - '-no-reboot')) - def test_s390x_s390_ccw_virtio(self): """ :avocado: tags=arch:s390x @@ -241,29 +193,3 @@ class ReplayKernelNormal(ReplayKernelBase): console_pattern = 'Kernel command line: %s' % kernel_command_line self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, args=('-nodefaults', )) - - def do_test_advcal_2018(self, file_path, kernel_name, args=None): - archive.extract(file_path, self.workdir) - - for entry in os.scandir(self.workdir): - if entry.name.startswith('day') and entry.is_dir(): - kernel_path = os.path.join(entry.path, kernel_name) - break - - kernel_command_line = '' - console_pattern = 'QEMU advent calendar' - self.run_rr(kernel_path, kernel_command_line, console_pattern, - args=args) - - def test_arm_vexpressa9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:vexpress-a9 - """ - tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day16.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' - self.do_test_advcal_2018(file_path, 'winter.zImage', - args=('-dtb', dtb_path)) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 995cea4f30..a46c4e8946 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -113,6 +113,7 @@ tests_arm_system_thorough = [ 'arm_orangepi', 'arm_quanta_gsj', 'arm_raspi2', + 'arm_replay', 'arm_smdkc210', 'arm_sx1', 'arm_vexpress', diff --git a/tests/functional/test_arm_replay.py b/tests/functional/test_arm_replay.py new file mode 100755 index 0000000000..e002e6a264 --- /dev/null +++ b/tests/functional/test_arm_replay.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on arm machines and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class ArmReplay(ReplayKernelBase): + + ASSET_VIRT = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_VIRT.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) + + ASSET_CUBIE_KERNEL = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf_' + '_6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_CUBIE_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_cubieboard(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb') + initrd_path = self.uncompress(self.ASSET_CUBIE_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, + args=('-dtb', dtb_path, + '-initrd', initrd_path, + '-no-reboot')) + + ASSET_DAY16 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day16.tar.xz', + '63311adb2d4c4e7a73214a86d29988add87266a909719c56acfadd026b4110a7') + + def test_vexpressa9(self): + self.set_machine('vexpress-a9') + self.archive_extract(self.ASSET_DAY16) + kernel_path = self.scratch_file('day16', 'winter.zImage') + dtb_path = self.scratch_file('day16', 'vexpress-v2p-ca9.dtb') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-dtb', dtb_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 7472862965f77d10b44eba0d9efa9cfb47c94651 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:40 +0100 Subject: [PATCH 1986/2892] tests/functional: Convert the alpha replay avocado tests Put the tests into a separate file now (since in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-11-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 17 ---------------- tests/functional/meson.build | 1 + tests/functional/test_alpha_replay.py | 29 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 17 deletions(-) create mode 100755 tests/functional/test_alpha_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 02bd868a42..4347d20274 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -176,20 +176,3 @@ class ReplayKernelNormal(ReplayKernelBase): kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' console_pattern = 'Kernel command line: %s' % kernel_command_line self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) - - def test_alpha_clipper(self): - """ - :avocado: tags=arch:alpha - :avocado: tags=machine:clipper - """ - kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/' - 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz') - kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - uncompressed_kernel = archive.uncompress(kernel_path, self.workdir) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, - args=('-nodefaults', )) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index a46c4e8946..e8b934d7f5 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -90,6 +90,7 @@ tests_alpha_system_quick = [ tests_alpha_system_thorough = [ 'alpha_clipper', + 'alpha_replay', ] tests_arm_system_quick = [ diff --git a/tests/functional/test_alpha_replay.py b/tests/functional/test_alpha_replay.py new file mode 100755 index 0000000000..24a17ef590 --- /dev/null +++ b/tests/functional/test_alpha_replay.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an Alpha machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class AlphaReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/dists/lenny/main/installer-alpha/' + '20090123lenny10/images/cdrom/vmlinuz'), + '34f53da3fa32212e4f00b03cb944b2ad81c06bc8faaf9b7193b2e544ceeca576') + + def test_clipper(self): + self.set_machine('clipper') + kernel_path = self.uncompress(self.ASSET_KERNEL, format='gz') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9, + args=('-nodefaults', )) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 504248520316ac72f11855e14766e6658ec486d4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:41 +0100 Subject: [PATCH 1987/2892] tests/functional: Convert the s390x replay avocado tests Put the tests into a separate file now (in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-12-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 15 -------------- tests/functional/meson.build | 1 + tests/functional/test_s390x_replay.py | 28 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 15 deletions(-) create mode 100755 tests/functional/test_s390x_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 4347d20274..4954360461 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -161,18 +161,3 @@ class ReplayKernelNormal(ReplayKernelBase): console_pattern = 'VFS: Cannot open root device' self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_s390x_s390_ccw_virtio(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/s390x/os/images' - '/kernel.img') - kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e8b934d7f5..acab536428 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -248,6 +248,7 @@ tests_rx_system_thorough = [ tests_s390x_system_thorough = [ 's390x_ccw_virtio', + 's390x_replay', 's390x_topology', 's390x_tuxrun', ] diff --git a/tests/functional/test_s390x_replay.py b/tests/functional/test_s390x_replay.py new file mode 100755 index 0000000000..33b5843ada --- /dev/null +++ b/tests/functional/test_s390x_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an s390x machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class S390xReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/s390x/os/images/kernel.img'), + 'dace03b8ae0c9f670ebb9b8d6ce5eb24b62987f346de8f1300a439bb00bb99e7') + + def test_s390_ccw_virtio(self): + self.set_machine('s390-ccw-virtio') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 4d75a3743a04a4a3db7f339eee159068fa8757b6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:42 +0100 Subject: [PATCH 1988/2892] tests/functional: Convert the aarch64 replay avocado tests Put the tests into a separate file now (in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth Message-ID: <20250218152744.228335-13-thuth@redhat.com> --- tests/avocado/replay_kernel.py | 18 --------------- tests/functional/meson.build | 1 + tests/functional/test_aarch64_replay.py | 30 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 18 deletions(-) create mode 100755 tests/functional/test_aarch64_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 4954360461..dffced62aa 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -143,21 +143,3 @@ class ReplayKernelNormal(ReplayKernelBase): console_pattern = 'VFS: Cannot open root device' self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index acab536428..e8e5a7757c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -71,6 +71,7 @@ tests_aarch64_system_thorough = [ 'aarch64_aspeed', 'aarch64_raspi3', 'aarch64_raspi4', + 'aarch64_replay', 'aarch64_rme_virt', 'aarch64_rme_sbsaref', 'aarch64_sbsaref', diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/test_aarch64_replay.py new file mode 100755 index 0000000000..04cde433bc --- /dev/null +++ b/tests/functional/test_aarch64_replay.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an aarch64 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Aarch64Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + + def test_aarch64_virt(self): + self.set_machine('virt') + self.cpu = 'cortex-a53' + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 0f31f0f53c45e248b1d361699210019ec867c22b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Feb 2025 16:27:43 +0100 Subject: [PATCH 1989/2892] tests/functional: Convert the x86_64 replay avocado tests Put the tests into a separate file now (in the functional framework, each file is run with one specific qemu-system-* binary). Signed-off-by: Thomas Huth --- tests/avocado/replay_kernel.py | 35 -------------------------- tests/functional/meson.build | 2 ++ tests/functional/test_x86_64_replay.py | 35 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 35 deletions(-) create mode 100755 tests/functional/test_x86_64_replay.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index dffced62aa..3551532372 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -108,38 +108,3 @@ class ReplayKernelNormal(ReplayKernelBase): console_pattern = 'VFS: Cannot open root device' self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - # See https://gitlab.com/qemu-project/qemu/-/issues/2094 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'pc machine is unstable with replay') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - :avocado: tags=flaky - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_x86_64_q35(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e8e5a7757c..8ae70568a2 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -49,6 +49,7 @@ test_timeouts = { 'sh4_tuxrun' : 240, 'virtio_balloon': 120, 'x86_64_kvm_xen' : 180, + 'x86_64_replay' : 480, } tests_generic_system = [ @@ -302,6 +303,7 @@ tests_x86_64_system_thorough = [ 'x86_64_hotplug_blk', 'x86_64_hotplug_cpu', 'x86_64_kvm_xen', + 'x86_64_replay', 'x86_64_tuxrun', ] diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/test_x86_64_replay.py new file mode 100755 index 0000000000..180f23a60c --- /dev/null +++ b/tests/functional/test_x86_64_replay.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on x86_64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipFlakyTest +from replay_kernel import ReplayKernelBase + + +class X86Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/29/Everything/x86_64/os/images/pxeboot/vmlinuz'), + '8f237d84712b1b411baf3af2aeaaee10b9aae8e345ec265b87ab3a39639eb143') + + def do_test_x86(self, machine): + self.set_machine(machine) + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2094') + def test_pc(self): + self.do_test_x86('pc') + + def test_q35(self): + self.do_test_x86('q35') + + +if __name__ == '__main__': + ReplayKernelBase.main() From fe95724da2180681b7d78e9c2b9fd8438023f9ce Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Feb 2025 14:06:40 +0000 Subject: [PATCH 1990/2892] tests/functional: Bump some arm test timeouts On my local machine, for a debug build, sbsaref_alpine takes nearly 900s: $ (cd build/x86 && ./pyvenv/bin/meson test --setup thorough --suite func-thorough func-aarch64-aarch64_sbsaref_alpine ) 1/1 qemu:func-thorough+func-aarch64-thorough+thorough / func-aarch64-aarch64_sbsaref_alpine OK 896.90s arm_aspeed_rainier can also run close to its current timeout: 6/44 qemu:func-thorough+func-arm-thorough+thorough / func-arm-arm_aspeed_rainier OK 215.75s and arm_aspeed_ast2500 and arm_aspeed_ast2600 can go over: 13/44 qemu:func-thorough+func-arm-thorough+thorough / func-arm-arm_aspeed_ast2600 OK 792.94s 27/44 qemu:func-thorough+func-arm-thorough+thorough / func-arm-arm_aspeed_ast2500 TIMEOUT 480.01s The sx1 test fails not on the overall meson timeout but on the 60 second timeout in some of the subtests. Bump all these timeouts up a bit. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-ID: <20250221140640.786341-1-peter.maydell@linaro.org> Signed-off-by: Thomas Huth --- tests/functional/meson.build | 8 ++++---- tests/functional/test_arm_sx1.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8ae70568a2..3fd2652c07 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -15,16 +15,16 @@ test_timeouts = { 'aarch64_raspi4' : 480, 'aarch64_rme_virt' : 1200, 'aarch64_rme_sbsaref' : 1200, - 'aarch64_sbsaref_alpine' : 720, + 'aarch64_sbsaref_alpine' : 1200, 'aarch64_sbsaref_freebsd' : 720, 'aarch64_tuxrun' : 240, 'aarch64_virt' : 720, 'acpi_bits' : 420, 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, - 'arm_aspeed_ast2500' : 480, - 'arm_aspeed_ast2600' : 720, - 'arm_aspeed_rainier' : 240, + 'arm_aspeed_ast2500' : 720, + 'arm_aspeed_ast2600' : 1200, + 'arm_aspeed_rainier' : 480, 'arm_bpim2u' : 500, 'arm_collie' : 180, 'arm_cubieboard' : 360, diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py index b85bfaa178..4dd1e1859f 100755 --- a/tests/functional/test_arm_sx1.py +++ b/tests/functional/test_arm_sx1.py @@ -44,7 +44,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-no-reboot') self.launch_kernel(zimage_path, initrd=initrd_path) - self.vm.wait(timeout=60) + self.vm.wait(timeout=120) def test_arm_sx1_sd(self): self.set_machine('sx1') @@ -55,7 +55,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}') self.launch_kernel(zimage_path) - self.vm.wait(timeout=60) + self.vm.wait(timeout=120) def test_arm_sx1_flash(self): self.set_machine('sx1') @@ -66,7 +66,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}') self.launch_kernel(zimage_path) - self.vm.wait(timeout=60) + self.vm.wait(timeout=120) if __name__ == '__main__': LinuxKernelTest.main() From d5d028eee38d4107821c0d2cfdb0dd04b9ba5ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 25 Feb 2025 11:05:25 +0000 Subject: [PATCH 1991/2892] gitlab: use --refetch in check-patch/check-dco jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When gitlab initializes the repo checkout for a CI job, it will have done a shallow clone with only partial history. Periodically the objects that are omitted cause trouble with the check-patch/check-dco jobs. This is exhibited as reporting strange errors being unable to fetch certain objects that are known to exist. Passing the --refetch flag to 'git fetch' causes it to not assume the local checkout has all common objects and thus re-fetch everything that is needed. This appears to solve the check-patch/check-dco job failures. Signed-off-by: Daniel P. Berrangé Acked-by: Michael S. Tsirkin Message-ID: <20250225110525.2209854-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/check-dco.py | 2 +- .gitlab-ci.d/check-patch.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/check-dco.py b/.gitlab-ci.d/check-dco.py index 70dec7d6ee..2fd56683dc 100755 --- a/.gitlab-ci.d/check-dco.py +++ b/.gitlab-ci.d/check-dco.py @@ -21,7 +21,7 @@ repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame) print(f"adding upstream git repo @ {repourl}") subprocess.check_call(["git", "remote", "add", "check-dco", repourl]) -subprocess.check_call(["git", "fetch", "check-dco", "master"]) +subprocess.check_call(["git", "fetch", "--refetch", "check-dco", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-dco/master", "HEAD"], diff --git a/.gitlab-ci.d/check-patch.py b/.gitlab-ci.d/check-patch.py index 68c549a146..be13e6f77d 100755 --- a/.gitlab-ci.d/check-patch.py +++ b/.gitlab-ci.d/check-patch.py @@ -24,7 +24,7 @@ print(f"adding upstream git repo @ {repourl}") # base for the user's branch. We thus need to figure out a common # ancestor between the user's branch and current git master. subprocess.check_call(["git", "remote", "add", "check-patch", repourl]) -subprocess.check_call(["git", "fetch", "check-patch", "master"]) +subprocess.check_call(["git", "fetch", "--refetch", "check-patch", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-patch/master", "HEAD"], From 72cdd672e18c486db7c54a7b33c8b4fe7a0026e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Feb 2025 07:50:13 +0100 Subject: [PATCH 1992/2892] tests/functional: Replace the ppc64 e500 advent calendar test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the advent calendar test with a buildroot image built with qemu_ppc64_e5500_defconfig. Unlike the advent calendar image, this newer buildroot image supports networking, too. Thus boot a ppce500 machine from kernel and disk, test network and poweroff. Add '-no-shutdown' to the command line to avoid exiting from QEMU as it seems to bother the functional framework. Signed-off-by: Cédric Le Goater Message-ID: <20250226065013.196052-1-clg@redhat.com> Reviewed-by: Thomas Huth [thuth: Add some wording about network support to the commit message] Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_e500.py | 33 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py index b92fe0b0e7..9ce7ae6c47 100755 --- a/tests/functional/test_ppc64_e500.py +++ b/tests/functional/test_ppc64_e500.py @@ -5,20 +5,39 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern class E500Test(LinuxKernelTest): - ASSET_DAY19 = Asset( - 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day19.tar.xz', - '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + ASSET_BR2_E5500_UIMAGE = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/uImage', + '2478187c455d6cca3984e9dfde9c635d824ea16236b85fd6b4809f744706deda') - def test_ppc64_e500(self): + ASSET_BR2_E5500_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/rootfs.ext2', + '9035ef97237c84c7522baaff17d25cdfca4bb7a053d5e296e902919473423d76') + + def test_ppc64_e500_buildroot(self): self.set_machine('ppce500') self.cpu = 'e5500' - self.archive_extract(self.ASSET_DAY19) - self.launch_kernel(self.scratch_file('day19', 'uImage'), - wait_for='QEMU advent calendar') + + uimage_path = self.ASSET_BR2_E5500_UIMAGE.fetch() + rootfs_path = self.ASSET_BR2_E5500_ROOTFS.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', uimage_path, + '-append', 'root=/dev/vda', + '-drive', f'file={rootfs_path},if=virtio,format=raw', + '-snapshot', '-no-shutdown') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'Power down') if __name__ == '__main__': LinuxKernelTest.main() From 5d20aa540b6991c0dbeef933d2055e5372f52e0e Mon Sep 17 00:00:00 2001 From: EwanHai Date: Mon, 13 Jan 2025 02:44:10 -0500 Subject: [PATCH 1993/2892] target/i386: Add support for Zhaoxin CPU vendor identification Zhaoxin currently uses two vendors: "Shanghai" and "Centaurhauls". It is important to note that the latter now belongs to Zhaoxin. Therefore, this patch replaces CPUID_VENDOR_VIA with CPUID_VENDOR_ZHAOXIN1. The previous CPUID_VENDOR_VIA macro was only defined but never used in QEMU, making this change straightforward. Additionally, the IS_ZHAOXIN_CPU macro has been added to simplify the checks for Zhaoxin CPUs. Signed-off-by: EwanHai Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250113074413.297793-2-ewanhai-oc@zhaoxin.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c67b42d34f..4279cf5cde 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1122,7 +1122,16 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ #define CPUID_VENDOR_AMD "AuthenticAMD" -#define CPUID_VENDOR_VIA "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN1_1 0x746E6543 /* "Cent" */ +#define CPUID_VENDOR_ZHAOXIN1_2 0x48727561 /* "aurH" */ +#define CPUID_VENDOR_ZHAOXIN1_3 0x736C7561 /* "auls" */ + +#define CPUID_VENDOR_ZHAOXIN2_1 0x68532020 /* " Sh" */ +#define CPUID_VENDOR_ZHAOXIN2_2 0x68676E61 /* "angh" */ +#define CPUID_VENDOR_ZHAOXIN2_3 0x20206961 /* "ai " */ + +#define CPUID_VENDOR_ZHAOXIN1 "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN2 " Shanghai " #define CPUID_VENDOR_HYGON "HygonGenuine" @@ -1132,6 +1141,15 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \ (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \ (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3) +#define IS_ZHAOXIN1_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN1_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN1_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN1_3) +#define IS_ZHAOXIN2_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN2_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN2_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN2_3) +#define IS_ZHAOXIN_CPU(env) (IS_ZHAOXIN1_CPU(env) || IS_ZHAOXIN2_CPU(env)) #define CPUID_MWAIT_IBE (1U << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */ From c0799e8b003713e07b546faba600363eccd179ee Mon Sep 17 00:00:00 2001 From: EwanHai Date: Mon, 13 Jan 2025 02:44:11 -0500 Subject: [PATCH 1994/2892] target/i386: Add CPUID leaf 0xC000_0001 EDX definitions Add new CPUID feature flags for various Zhaoxin PadLock extensions. These definitions will be used for Zhaoxin CPU models. Signed-off-by: EwanHai Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250113074413.297793-3-ewanhai-oc@zhaoxin.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 4279cf5cde..10ce019e3f 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1110,6 +1110,27 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* CPUID[0x80000007].EDX flags: */ #define CPUID_APM_INVTSC (1U << 8) +/* "rng" RNG present (xstore) */ +#define CPUID_C000_0001_EDX_XSTORE (1U << 2) +/* "rng_en" RNG enabled */ +#define CPUID_C000_0001_EDX_XSTORE_EN (1U << 3) +/* "ace" on-CPU crypto (xcrypt) */ +#define CPUID_C000_0001_EDX_XCRYPT (1U << 6) +/* "ace_en" on-CPU crypto enabled */ +#define CPUID_C000_0001_EDX_XCRYPT_EN (1U << 7) +/* Advanced Cryptography Engine v2 */ +#define CPUID_C000_0001_EDX_ACE2 (1U << 8) +/* ACE v2 enabled */ +#define CPUID_C000_0001_EDX_ACE2_EN (1U << 9) +/* PadLock Hash Engine */ +#define CPUID_C000_0001_EDX_PHE (1U << 10) +/* PHE enabled */ +#define CPUID_C000_0001_EDX_PHE_EN (1U << 11) +/* PadLock Montgomery Multiplier */ +#define CPUID_C000_0001_EDX_PMM (1U << 12) +/* PMM enabled */ +#define CPUID_C000_0001_EDX_PMM_EN (1U << 13) + #define CPUID_VENDOR_SZ 12 #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ From ff04bc1ac478656e5d6a255bf4069edb3f55bc58 Mon Sep 17 00:00:00 2001 From: EwanHai Date: Mon, 13 Jan 2025 02:44:12 -0500 Subject: [PATCH 1995/2892] target/i386: Introduce Zhaoxin Yongfeng CPU model Introduce support for the Zhaoxin Yongfeng CPU model. The Zhaoxin Yongfeng CPU is Zhaoxin's latest server CPU. This new cpu model ensure that QEMU can correctly emulate the Zhaoxin Yongfeng CPU, providing accurate functionality and performance characteristics. Signed-off-by: EwanHai Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250113074413.297793-4-ewanhai-oc@zhaoxin.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2f9c604552..bd40714613 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5498,6 +5498,130 @@ static const X86CPUDefinition builtin_x86_defs[] = { .model_id = "AMD EPYC-Genoa Processor", .cache_info = &epyc_genoa_cache_info, }, + { + .name = "YongFeng", + .level = 0x1F, + .vendor = CPUID_VENDOR_ZHAOXIN1, + .family = 7, + .model = 11, + .stepping = 3, + /* missing: CPUID_HT, CPUID_TM, CPUID_PBE */ + .features[FEAT_1_EDX] = + CPUID_SS | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_ACPI | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | + CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | + CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | + CPUID_PSE | CPUID_DE | CPUID_VME | CPUID_FP87, + /* + * missing: CPUID_EXT_OSXSAVE, CPUID_EXT_XTPR, CPUID_EXT_TM2, + * CPUID_EXT_EST, CPUID_EXT_SMX, CPUID_EXT_VMX + */ + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_TSC_DEADLINE_TIMER | + CPUID_EXT_POPCNT | CPUID_EXT_MOVBE | CPUID_EXT_X2APIC | + CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | CPUID_EXT_PCID | + CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | + CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_BMI2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_FSGSBASE, + /* missing: CPUID_7_0_ECX_OSPKE */ + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_UMIP, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, + .features[FEAT_8000_0007_EDX] = CPUID_APM_INVTSC, + /* + * TODO: When the Linux kernel introduces other existing definitions + * for this leaf, remember to update the definitions here. + */ + .features[FEAT_C000_0001_EDX] = + CPUID_C000_0001_EDX_PMM_EN | CPUID_C000_0001_EDX_PMM | + CPUID_C000_0001_EDX_PHE_EN | CPUID_C000_0001_EDX_PHE | + CPUID_C000_0001_EDX_ACE2 | + CPUID_C000_0001_EDX_XCRYPT_EN | CPUID_C000_0001_EDX_XCRYPT | + CPUID_C000_0001_EDX_XSTORE_EN | CPUID_C000_0001_EDX_XSTORE, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | + MSR_ARCH_CAP_MDS_NO | MSR_ARCH_CAP_PSCHANGE_MC_NO | + MSR_ARCH_CAP_SSB_NO, + .features[FEAT_VMX_PROCBASED_CTLS] = + VMX_CPU_BASED_VIRTUAL_INTR_PENDING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_INVLPG_EXITING | + VMX_CPU_BASED_MWAIT_EXITING | VMX_CPU_BASED_RDPMC_EXITING | + VMX_CPU_BASED_RDTSC_EXITING | VMX_CPU_BASED_CR3_LOAD_EXITING | + VMX_CPU_BASED_CR3_STORE_EXITING | VMX_CPU_BASED_CR8_LOAD_EXITING | + VMX_CPU_BASED_CR8_STORE_EXITING | VMX_CPU_BASED_TPR_SHADOW | + VMX_CPU_BASED_VIRTUAL_NMI_PENDING | VMX_CPU_BASED_MOV_DR_EXITING | + VMX_CPU_BASED_UNCOND_IO_EXITING | VMX_CPU_BASED_USE_IO_BITMAPS | + VMX_CPU_BASED_MONITOR_TRAP_FLAG | VMX_CPU_BASED_USE_MSR_BITMAPS | + VMX_CPU_BASED_MONITOR_EXITING | VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + /* + * missing: VMX_SECONDARY_EXEC_PAUSE_LOOP_EXITING, + * VMX_SECONDARY_EXEC_TSC_SCALING + */ + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + VMX_SECONDARY_EXEC_ENABLE_EPT | VMX_SECONDARY_EXEC_DESC | + VMX_SECONDARY_EXEC_RDTSCP | VMX_SECONDARY_EXEC_ENABLE_VPID | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + VMX_SECONDARY_EXEC_WBINVD_EXITING | + VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | + VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | + VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | + VMX_SECONDARY_EXEC_RDRAND_EXITING | + VMX_SECONDARY_EXEC_ENABLE_INVPCID | + VMX_SECONDARY_EXEC_ENABLE_VMFUNC | + VMX_SECONDARY_EXEC_SHADOW_VMCS | + VMX_SECONDARY_EXEC_ENABLE_PML, + .features[FEAT_VMX_PINBASED_CTLS] = + VMX_PIN_BASED_EXT_INTR_MASK | VMX_PIN_BASED_NMI_EXITING | + VMX_PIN_BASED_VIRTUAL_NMIS | VMX_PIN_BASED_VMX_PREEMPTION_TIMER | + VMX_PIN_BASED_POSTED_INTR, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | VMX_VM_EXIT_HOST_ADDR_SPACE_SIZE | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + /* missing: VMX_VM_ENTRY_SMM, VMX_VM_ENTRY_DEACT_DUAL_MONITOR */ + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | VMX_VM_ENTRY_LOAD_IA32_EFER, + /* + * missing: MSR_VMX_MISC_ACTIVITY_SHUTDOWN, + * MSR_VMX_MISC_ACTIVITY_WAIT_SIPI + */ + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_VMWRITE_VMEXIT, + /* missing: MSR_VMX_EPT_UC */ + .features[FEAT_VMX_EPT_VPID_CAPS] = + MSR_VMX_EPT_EXECONLY | MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | MSR_VMX_EPT_INVVPID | + MSR_VMX_EPT_INVVPID_ALL_CONTEXT | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Zhaoxin YongFeng Processor", + }, }; /* From a4e749780bd20593c0c386612a51bf4d64a80132 Mon Sep 17 00:00:00 2001 From: EwanHai Date: Mon, 13 Jan 2025 02:44:13 -0500 Subject: [PATCH 1996/2892] target/i386: Mask CMPLegacy bit in CPUID[0x80000001].ECX for Zhaoxin CPUs Zhaoxin CPUs (including vendors "Shanghai" and "Centaurhauls") handle the CMPLegacy bit similarly to Intel CPUs. Therefore, this commit masks the CMPLegacy bit in CPUID[0x80000001].ECX for Zhaoxin CPUs, just as it is done for Intel CPUs. AMD uses the CMPLegacy bit (CPUID[0x80000001].ECX.bit1) along with other CPUID information to enumerate platform topology (e.g., the number of logical processors per package). However, for Intel and other CPUs that follow Intel's behavior, CPUID[0x80000001].ECX.bit1 is reserved. - Impact on Intel and similar CPUs: This change has no effect on Intel and similar CPUs, as the goal is to accurately emulate CPU CPUID information. - Impact on Linux Guests running on Intel (and similar) vCPUs: During boot, Linux checks if the CPU supports Hyper-Threading. For the Linux kernel before v6.9, if it detects X86_FEATURE_CMP_LEGACY, it assumes Hyper-Threading is not supported. For Intel and similar vCPUs, if the CMPLegacy bit is not masked in CPUID[0x80000001].ECX, Linux will incorrectly assume that Hyper-Threading is not supported, even if the vCPU does support it. Signed-off-by: EwanHai Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250113074413.297793-5-ewanhai-oc@zhaoxin.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index bd40714613..0cd9b70938 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7804,9 +7804,10 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) /* * The Linux kernel checks for the CMPLegacy bit and * discards multiple thread information if it is set. - * So don't set it here for Intel to make Linux guests happy. + * So don't set it here for Intel (and other processors + * following Intel's behavior) to make Linux guests happy. */ - if (!IS_INTEL_CPU(env)) { + if (!IS_INTEL_CPU(env) && !IS_ZHAOXIN_CPU(env)) { env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; } } From 6a2c7fc29ab6e8fdce4829d3229db534abf923bb Mon Sep 17 00:00:00 2001 From: John Snow Date: Sun, 23 Feb 2025 22:37:32 -0500 Subject: [PATCH 1997/2892] qapi: update pylintrc config If you've got a newer pylint, it'll whine about positional arguments separately from the regular ones. Update the configuration to ignore both categories of warning. Signed-off-by: John Snow Message-ID: <20250224033741.222749-2-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index c028a1f9f5..d24eece741 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -17,6 +17,7 @@ disable=consider-using-f-string, too-many-arguments, too-many-branches, too-many-instance-attributes, + too-many-positional-arguments, too-many-statements, useless-option-value, From ad1e6843632555c771dda6a9425930fa25b71fb3 Mon Sep 17 00:00:00 2001 From: Konstantin Kostiuk Date: Mon, 16 Dec 2024 17:45:52 +0200 Subject: [PATCH 1998/2892] qga: Add log to guest-fsfreeze-thaw command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Daniel P. Berrangé Message-ID: <20241216154552.213961-2-kkostiuk@redhat.com> Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 2 ++ qga/commands-win32.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 6e3c15f539..12bc086d79 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -805,8 +805,10 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) int ret; ret = qmp_guest_fsfreeze_do_thaw(errp); + if (ret >= 0) { ga_unset_frozen(ga_state); + slog("guest-fsthaw called"); execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); } else { ret = 0; diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 99c026c0a0..749fdf8895 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1273,6 +1273,9 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) qga_vss_fsfreeze(&i, false, NULL, errp); ga_unset_frozen(ga_state); + + slog("guest-fsthaw called"); + return i; } From 5b567c21c6d517beeb1087399f733662d7e8ff62 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Tue, 7 Jan 2025 15:52:06 +0100 Subject: [PATCH 1999/2892] qga: Invert logic on return value in main() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current logic on return value ('ret' variable) in main() is error prone. The variable is initialized to EXIT_SUCCESS and then set to EXIT_FAILURE on error paths. This makes it very easy to forget to set the variable to indicate error when adding new error path, as is demonstrated by handling of initialize_agent() failure. It's simply lacking setting of the variable. There's just one case where success should be indicated: when dumping the config ('-D' cmd line argument). To resolve this, initialize the variable to failure value and set it explicitly to success value in that one specific case. Signed-off-by: Michal Privoznik Reviewed-by: Ján Tomko Reviewed-by: Konstantin Kostiuk Message-ID: <8a28265f50177a8dc4c10fcf4146e85a7fd748ee.1736261360.git.mprivozn@redhat.com> Signed-off-by: Konstantin Kostiuk --- qga/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qga/main.c b/qga/main.c index 531853e13d..eccfa33871 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1579,7 +1579,7 @@ static void stop_agent(GAState *s, bool requested) int main(int argc, char **argv) { - int ret = EXIT_SUCCESS; + int ret = EXIT_FAILURE; GAState *s; GAConfig *config = g_new0(GAConfig, 1); int socket_activation; @@ -1607,7 +1607,6 @@ int main(int argc, char **argv) socket_activation = check_socket_activation(); if (socket_activation > 1) { g_critical("qemu-ga only supports listening on one socket"); - ret = EXIT_FAILURE; goto end; } if (socket_activation) { @@ -1631,7 +1630,6 @@ int main(int argc, char **argv) if (!config->method) { g_critical("unsupported listen fd type"); - ret = EXIT_FAILURE; goto end; } } else if (config->channel_path == NULL) { @@ -1643,13 +1641,13 @@ int main(int argc, char **argv) config->channel_path = g_strdup(QGA_SERIAL_PATH_DEFAULT); } else { g_critical("must specify a path for this channel"); - ret = EXIT_FAILURE; goto end; } } if (config->dumpconf) { config_dump(config); + ret = EXIT_SUCCESS; goto end; } @@ -1664,6 +1662,7 @@ int main(int argc, char **argv) SERVICE_TABLE_ENTRY service_table[] = { { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; StartServiceCtrlDispatcher(service_table); + ret = EXIT_SUCCESS; } else { ret = run_agent(s); } From c6f5dd7ac8ef62dcdec4cdeda1467c658161afff Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Tue, 7 Jan 2025 15:52:07 +0100 Subject: [PATCH 2000/2892] qga: Don't daemonize before channel is initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the agent is set to daemonize but for whatever reason fails to init the channel, the error message is lost. Worse, the agent daemonizes needlessly and returns success. For instance: # qemu-ga -m virtio-serial \ -p /dev/nonexistent_device \ -f /run/qemu-ga.pid \ -t /run \ -d # echo $? 0 This makes it needlessly hard for init scripts to detect a failure in qemu-ga startup. Though, they shouldn't pass '-d' in the first place. Let's open the channel first and only after that become a daemon. Related bug: https://bugs.gentoo.org/810628 Signed-off-by: Michal Privoznik Reviewed-by: Ján Tomko Reviewed-by: Konstantin Kostiuk Message-ID: <7a42b0cbda5c7e01cf76bc1b29a1210cd018fa78.1736261360.git.mprivozn@redhat.com> Signed-off-by: Konstantin Kostiuk --- qga/main.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/qga/main.c b/qga/main.c index eccfa33871..72c39b042f 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1430,7 +1430,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) if (config->daemonize) { /* delay opening/locking of pidfile till filesystems are unfrozen */ s->deferred_options.pid_filepath = config->pid_filepath; - become_daemon(NULL); } if (config->log_filepath) { /* delay opening the log file till filesystems are unfrozen */ @@ -1438,9 +1437,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) } ga_disable_logging(s); } else { - if (config->daemonize) { - become_daemon(config->pid_filepath); - } if (config->log_filepath) { FILE *log_file = ga_open_logfile(config->log_filepath); if (!log_file) { @@ -1487,6 +1483,20 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) ga_apply_command_filters(s); + if (!channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + g_critical("failed to initialize guest agent channel"); + return NULL; + } + + if (config->daemonize) { + if (ga_is_frozen(s)) { + become_daemon(NULL); + } else { + become_daemon(config->pid_filepath); + } + } + ga_state = s; return s; } @@ -1513,8 +1523,9 @@ static void cleanup_agent(GAState *s) static int run_agent_once(GAState *s) { - if (!channel_init(s, s->config->method, s->config->channel_path, - s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + if (!s->channel && + channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { g_critical("failed to initialize guest agent channel"); return EXIT_FAILURE; } @@ -1523,6 +1534,7 @@ static int run_agent_once(GAState *s) if (s->channel) { ga_channel_free(s->channel); + s->channel = NULL; } return EXIT_SUCCESS; From 6ccca4b6bb9f994cc04e71004e1767a3476d2b23 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:07 +0100 Subject: [PATCH 2001/2892] hw/nvme: rework csi handling The controller incorrectly allows a zoned namespace to be attached even if CS.CSS is configured to only support the NVM command set for I/O queues. Rework handling of namespace command sets in general by attaching supported namespaces when the controller is started instead of, like now, statically when realized. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 213 ++++++++++++++++++++++++------------------- hw/nvme/ns.c | 14 --- hw/nvme/nvme.h | 5 +- include/block/nvme.h | 10 +- 4 files changed, 131 insertions(+), 111 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 86e1c48fab..21496c6b6b 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -277,15 +277,14 @@ static const uint32_t nvme_cse_acs_default[256] = { [NVME_ADM_CMD_SET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, - [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, + [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC | + NVME_CMD_EFF_CCC, [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_DIRECTIVE_SEND] = NVME_CMD_EFF_CSUPP, }; -static const uint32_t nvme_cse_iocs_none[256]; - -static const uint32_t nvme_cse_iocs_nvm[256] = { +static const uint32_t nvme_cse_iocs_nvm_default[256] = { [NVME_CMD_FLUSH] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE_ZEROES] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -298,7 +297,7 @@ static const uint32_t nvme_cse_iocs_nvm[256] = { [NVME_CMD_IO_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, }; -static const uint32_t nvme_cse_iocs_zoned[256] = { +static const uint32_t nvme_cse_iocs_zoned_default[256] = { [NVME_CMD_FLUSH] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE_ZEROES] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -307,6 +306,9 @@ static const uint32_t nvme_cse_iocs_zoned[256] = { [NVME_CMD_VERIFY] = NVME_CMD_EFF_CSUPP, [NVME_CMD_COPY] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_COMPARE] = NVME_CMD_EFF_CSUPP, + [NVME_CMD_IO_MGMT_RECV] = NVME_CMD_EFF_CSUPP, + [NVME_CMD_IO_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, + [NVME_CMD_ZONE_APPEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_RECV] = NVME_CMD_EFF_CSUPP, @@ -4603,6 +4605,61 @@ static uint16_t nvme_io_mgmt_send(NvmeCtrl *n, NvmeRequest *req) }; } +static uint16_t __nvme_io_cmd_nvm(NvmeCtrl *n, NvmeRequest *req) +{ + switch (req->cmd.opcode) { + case NVME_CMD_WRITE: + return nvme_write(n, req); + case NVME_CMD_READ: + return nvme_read(n, req); + case NVME_CMD_COMPARE: + return nvme_compare(n, req); + case NVME_CMD_WRITE_ZEROES: + return nvme_write_zeroes(n, req); + case NVME_CMD_DSM: + return nvme_dsm(n, req); + case NVME_CMD_VERIFY: + return nvme_verify(n, req); + case NVME_CMD_COPY: + return nvme_copy(n, req); + case NVME_CMD_IO_MGMT_RECV: + return nvme_io_mgmt_recv(n, req); + case NVME_CMD_IO_MGMT_SEND: + return nvme_io_mgmt_send(n, req); + } + + g_assert_not_reached(); +} + +static uint16_t nvme_io_cmd_nvm(NvmeCtrl *n, NvmeRequest *req) +{ + if (!(n->cse.iocs.nvm[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + trace_pci_nvme_err_invalid_opc(req->cmd.opcode); + return NVME_INVALID_OPCODE | NVME_DNR; + } + + return __nvme_io_cmd_nvm(n, req); +} + +static uint16_t nvme_io_cmd_zoned(NvmeCtrl *n, NvmeRequest *req) +{ + if (!(n->cse.iocs.zoned[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + trace_pci_nvme_err_invalid_opc(req->cmd.opcode); + return NVME_INVALID_OPCODE | NVME_DNR; + } + + switch (req->cmd.opcode) { + case NVME_CMD_ZONE_APPEND: + return nvme_zone_append(n, req); + case NVME_CMD_ZONE_MGMT_SEND: + return nvme_zone_mgmt_send(n, req); + case NVME_CMD_ZONE_MGMT_RECV: + return nvme_zone_mgmt_recv(n, req); + } + + return __nvme_io_cmd_nvm(n, req); +} + static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns; @@ -4644,11 +4701,6 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_FIELD | NVME_DNR; } - if (!(ns->iocs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { - trace_pci_nvme_err_invalid_opc(req->cmd.opcode); - return NVME_INVALID_OPCODE | NVME_DNR; - } - if (ns->status) { return ns->status; } @@ -4659,36 +4711,14 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) req->ns = ns; - switch (req->cmd.opcode) { - case NVME_CMD_WRITE_ZEROES: - return nvme_write_zeroes(n, req); - case NVME_CMD_ZONE_APPEND: - return nvme_zone_append(n, req); - case NVME_CMD_WRITE: - return nvme_write(n, req); - case NVME_CMD_READ: - return nvme_read(n, req); - case NVME_CMD_COMPARE: - return nvme_compare(n, req); - case NVME_CMD_DSM: - return nvme_dsm(n, req); - case NVME_CMD_VERIFY: - return nvme_verify(n, req); - case NVME_CMD_COPY: - return nvme_copy(n, req); - case NVME_CMD_ZONE_MGMT_SEND: - return nvme_zone_mgmt_send(n, req); - case NVME_CMD_ZONE_MGMT_RECV: - return nvme_zone_mgmt_recv(n, req); - case NVME_CMD_IO_MGMT_RECV: - return nvme_io_mgmt_recv(n, req); - case NVME_CMD_IO_MGMT_SEND: - return nvme_io_mgmt_send(n, req); - default: - g_assert_not_reached(); + switch (ns->csi) { + case NVME_CSI_NVM: + return nvme_io_cmd_nvm(n, req); + case NVME_CSI_ZONED: + return nvme_io_cmd_zoned(n, req); } - return NVME_INVALID_OPCODE | NVME_DNR; + g_assert_not_reached(); } static void nvme_cq_notifier(EventNotifier *e) @@ -5147,7 +5177,7 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, uint64_t off, NvmeRequest *req) { NvmeEffectsLog log = {}; - const uint32_t *src_iocs = NULL; + const uint32_t *iocs = NULL; uint32_t trans_len; if (off >= sizeof(log)) { @@ -5157,25 +5187,26 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, switch (NVME_CC_CSS(ldl_le_p(&n->bar.cc))) { case NVME_CC_CSS_NVM: - src_iocs = nvme_cse_iocs_nvm; - /* fall through */ - case NVME_CC_CSS_ADMIN_ONLY: + iocs = n->cse.iocs.nvm; break; - case NVME_CC_CSS_CSI: + + case NVME_CC_CSS_ALL: switch (csi) { case NVME_CSI_NVM: - src_iocs = nvme_cse_iocs_nvm; + iocs = n->cse.iocs.nvm; break; case NVME_CSI_ZONED: - src_iocs = nvme_cse_iocs_zoned; + iocs = n->cse.iocs.zoned; break; } + + break; } memcpy(log.acs, n->cse.acs, sizeof(log.acs)); - if (src_iocs) { - memcpy(log.iocs, src_iocs, sizeof(log.iocs)); + if (iocs) { + memcpy(log.iocs, iocs, sizeof(log.iocs)); } trans_len = MIN(sizeof(log) - off, buf_len); @@ -6718,25 +6749,29 @@ static void nvme_update_dsm_limits(NvmeCtrl *n, NvmeNamespace *ns) } } -static void nvme_select_iocs_ns(NvmeCtrl *n, NvmeNamespace *ns) +static bool nvme_csi_supported(NvmeCtrl *n, uint8_t csi) { - uint32_t cc = ldl_le_p(&n->bar.cc); + uint32_t cc; - ns->iocs = nvme_cse_iocs_none; - switch (ns->csi) { + switch (csi) { case NVME_CSI_NVM: - if (NVME_CC_CSS(cc) != NVME_CC_CSS_ADMIN_ONLY) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; + return true; + case NVME_CSI_ZONED: - if (NVME_CC_CSS(cc) == NVME_CC_CSS_CSI) { - ns->iocs = nvme_cse_iocs_zoned; - } else if (NVME_CC_CSS(cc) == NVME_CC_CSS_NVM) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; + cc = ldl_le_p(&n->bar.cc); + + return NVME_CC_CSS(cc) == NVME_CC_CSS_ALL; } + + g_assert_not_reached(); +} + +static void nvme_detach_ns(NvmeCtrl *n, NvmeNamespace *ns) +{ + assert(ns->attached > 0); + + n->namespaces[ns->params.nsid] = NULL; + ns->attached--; } static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) @@ -6781,7 +6816,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) switch (sel) { case NVME_NS_ATTACHMENT_ATTACH: - if (nvme_ns(ctrl, nsid)) { + if (nvme_ns(n, nsid)) { return NVME_NS_ALREADY_ATTACHED | NVME_DNR; } @@ -6789,19 +6824,17 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_NS_PRIVATE | NVME_DNR; } + if (!nvme_csi_supported(n, ns->csi)) { + return NVME_IOCS_NOT_SUPPORTED | NVME_DNR; + } + nvme_attach_ns(ctrl, ns); - nvme_select_iocs_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, ns); break; case NVME_NS_ATTACHMENT_DETACH: - if (!nvme_ns(ctrl, nsid)) { - return NVME_NS_NOT_ATTACHED | NVME_DNR; - } - - ctrl->namespaces[nsid] = NULL; - ns->attached--; - + nvme_detach_ns(ctrl, ns); nvme_update_dsm_limits(ctrl, NULL); break; @@ -7652,21 +7685,6 @@ static void nvme_ctrl_shutdown(NvmeCtrl *n) } } -static void nvme_select_iocs(NvmeCtrl *n) -{ - NvmeNamespace *ns; - int i; - - for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - ns = nvme_ns(n, i); - if (!ns) { - continue; - } - - nvme_select_iocs_ns(n, ns); - } -} - static int nvme_start_ctrl(NvmeCtrl *n) { uint64_t cap = ldq_le_p(&n->bar.cap); @@ -7733,7 +7751,18 @@ static int nvme_start_ctrl(NvmeCtrl *n) nvme_set_timestamp(n, 0ULL); - nvme_select_iocs(n); + /* verify that the command sets of attached namespaces are supported */ + for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns = nvme_subsys_ns(n->subsys, i); + + if (ns && nvme_csi_supported(n, ns->csi) && !ns->params.detached) { + if (!ns->attached || ns->params.shared) { + nvme_attach_ns(n, ns); + } + } + } + + nvme_update_dsm_limits(n, NULL); return 0; } @@ -8748,6 +8777,9 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) uint16_t oacs; memcpy(n->cse.acs, nvme_cse_acs_default, sizeof(n->cse.acs)); + memcpy(n->cse.iocs.nvm, nvme_cse_iocs_nvm_default, sizeof(n->cse.iocs.nvm)); + memcpy(n->cse.iocs.zoned, nvme_cse_iocs_zoned_default, + sizeof(n->cse.iocs.zoned)); id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); @@ -8859,9 +8891,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) NVME_CAP_SET_MQES(cap, n->params.mqes); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NVM); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_CSI_SUPP); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_ADMIN_ONLY); + NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NCSS); + NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_IOCSS); NVME_CAP_SET_MPSMAX(cap, 4); NVME_CAP_SET_CMBS(cap, n->params.cmb_size_mb ? 1 : 0); NVME_CAP_SET_PMRS(cap, n->pmr.dev ? 1 : 0); @@ -8908,8 +8939,6 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns) n->namespaces[nsid] = ns; ns->attached++; - - nvme_update_dsm_limits(n, ns); } static void nvme_realize(PCIDevice *pci_dev, Error **errp) @@ -8965,7 +8994,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } - nvme_attach_ns(n, ns); + n->subsys->namespaces[ns->params.nsid] = ns; } } diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 94cabc6a5b..98c1e75a5d 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -763,20 +763,6 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) ns->id_ns.endgid = cpu_to_le16(0x1); ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); - - if (ns->params.detached) { - return; - } - - if (ns->params.shared) { - for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { - NvmeCtrl *ctrl = subsys->ctrls[i]; - - if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { - nvme_attach_ns(ctrl, ns); - } - } - } } static const Property nvme_ns_props[] = { diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index b8d063a027..6f782ba188 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -237,7 +237,6 @@ typedef struct NvmeNamespace { NvmeLBAF lbaf; unsigned int nlbaf; size_t lbasz; - const uint32_t *iocs; uint8_t csi; uint16_t status; int attached; @@ -587,6 +586,10 @@ typedef struct NvmeCtrl { struct { uint32_t acs[256]; + struct { + uint32_t nvm[256]; + uint32_t zoned[256]; + } iocs; } cse; struct { diff --git a/include/block/nvme.h b/include/block/nvme.h index 763b2b2f0e..366739f79e 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -142,9 +142,9 @@ enum NvmeCapMask { ((cap) |= (uint64_t)((val) & CAP_CMBS_MASK) << CAP_CMBS_SHIFT) enum NvmeCapCss { - NVME_CAP_CSS_NVM = 1 << 0, - NVME_CAP_CSS_CSI_SUPP = 1 << 6, - NVME_CAP_CSS_ADMIN_ONLY = 1 << 7, + NVME_CAP_CSS_NCSS = 1 << 0, + NVME_CAP_CSS_IOCSS = 1 << 6, + NVME_CAP_CSS_NOIOCSS = 1 << 7, }; enum NvmeCcShift { @@ -177,7 +177,7 @@ enum NvmeCcMask { enum NvmeCcCss { NVME_CC_CSS_NVM = 0x0, - NVME_CC_CSS_CSI = 0x6, + NVME_CC_CSS_ALL = 0x6, NVME_CC_CSS_ADMIN_ONLY = 0x7, }; @@ -938,6 +938,8 @@ enum NvmeStatusCodes { NVME_INVALID_SEC_CTRL_STATE = 0x0120, NVME_INVALID_NUM_RESOURCES = 0x0121, NVME_INVALID_RESOURCE_ID = 0x0122, + NVME_IOCS_NOT_SUPPORTED = 0x0129, + NVME_IOCS_NOT_ENABLED = 0x012a, NVME_IOCS_COMBINATION_REJECTED = 0x012b, NVME_INVALID_IOCS = 0x012c, NVME_CONFLICTING_ATTRS = 0x0180, From 304babd9401d8ce8fe139a70fe464332eef2cee0 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:08 +0100 Subject: [PATCH 2002/2892] hw/nvme: only set command abort requested when cancelled due to Abort The Command Abort Requested status code should only be set if the command was explicitly cancelled due to an Abort command. Or, in the case the cancel was due to Submission Queue deletion, set the status code to Command Aborted due to SQ Deletion. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 21496c6b6b..07cd632985 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1783,10 +1783,6 @@ static void nvme_aio_err(NvmeRequest *req, int ret) break; } - if (ret == -ECANCELED) { - status = NVME_CMD_ABORT_REQ; - } - trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); error_setg_errno(&local_err, -ret, "aio failed"); @@ -4827,6 +4823,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req) while (!QTAILQ_EMPTY(&sq->out_req_list)) { r = QTAILQ_FIRST(&sq->out_req_list); assert(r->aiocb); + r->status = NVME_CMD_ABORT_SQ_DEL; blk_aio_cancel(r->aiocb); } @@ -6137,6 +6134,7 @@ static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *req) QTAILQ_FOREACH_SAFE(r, &sq->out_req_list, entry, next) { if (r->cqe.cid == cid) { if (r->aiocb) { + r->status = NVME_CMD_ABORT_REQ; blk_aio_cancel_async(r->aiocb); } break; From 6fc39228ffe9d54388f6d1080b502634df13bb72 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:09 +0100 Subject: [PATCH 2003/2892] hw/nvme: set error status code explicitly for misc commands The nvme_aio_err() does not handle Verify, Compare, Copy and other misc commands and defaults to setting the error status code to Internal Device Error. For some of these commands, we know better, so set it explicitly. For the commands using the nvme_misc_cb() callback (Copy, Flush, ...), if no status code has explicitly been set by the lower handlers, default to Internal Device Error as previously. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 28 ++++++++++++++++++++++------ include/block/nvme.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 07cd632985..b7222fd9ac 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1771,7 +1771,6 @@ static void nvme_aio_err(NvmeRequest *req, int ret) case NVME_CMD_READ: status = NVME_UNRECOVERED_READ; break; - case NVME_CMD_FLUSH: case NVME_CMD_WRITE: case NVME_CMD_WRITE_ZEROES: case NVME_CMD_ZONE_APPEND: @@ -2157,11 +2156,16 @@ static inline bool nvme_is_write(NvmeRequest *req) static void nvme_misc_cb(void *opaque, int ret) { NvmeRequest *req = opaque; + uint16_t cid = nvme_cid(req); - trace_pci_nvme_misc_cb(nvme_cid(req)); + trace_pci_nvme_misc_cb(cid); if (ret) { - nvme_aio_err(req, ret); + if (!req->status) { + req->status = NVME_INTERNAL_DEV_ERROR; + } + + trace_pci_nvme_err_aio(cid, strerror(-ret), req->status); } nvme_enqueue_req_completion(nvme_cq(req), req); @@ -2264,7 +2268,10 @@ static void nvme_verify_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2363,7 +2370,10 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2445,7 +2455,10 @@ static void nvme_compare_data_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2924,6 +2937,7 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + req->status = NVME_WRITE_FAULT; goto out; } else if (iocb->ret < 0) { goto out; @@ -2988,6 +3002,7 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + req->status = NVME_UNRECOVERED_READ; goto out; } else if (iocb->ret < 0) { goto out; @@ -3510,6 +3525,7 @@ static void nvme_flush_ns_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + iocb->req->status = NVME_WRITE_FAULT; goto out; } else if (iocb->ret < 0) { goto out; diff --git a/include/block/nvme.h b/include/block/nvme.h index 366739f79e..358e516e38 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -906,6 +906,7 @@ enum NvmeStatusCodes { NVME_SGL_DESCR_TYPE_INVALID = 0x0011, NVME_INVALID_USE_OF_CMB = 0x0012, NVME_INVALID_PRP_OFFSET = 0x0013, + NVME_COMMAND_INTERRUPTED = 0x0021, NVME_FDP_DISABLED = 0x0029, NVME_INVALID_PHID_LIST = 0x002a, NVME_LBA_RANGE = 0x0080, From cad58ada8f104bf342097a7a683ef594ac949c8d Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 16 Dec 2024 13:53:10 +0100 Subject: [PATCH 2004/2892] hw/nvme: remove nvme_aio_err() nvme_rw_complete_cb() is the only remaining user of nvme_aio_err(), so open code the status code setting instead. Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 60 +++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index b7222fd9ac..e62c6a3588 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1762,42 +1762,6 @@ static uint16_t nvme_check_dulbe(NvmeNamespace *ns, uint64_t slba, return NVME_SUCCESS; } -static void nvme_aio_err(NvmeRequest *req, int ret) -{ - uint16_t status = NVME_SUCCESS; - Error *local_err = NULL; - - switch (req->cmd.opcode) { - case NVME_CMD_READ: - status = NVME_UNRECOVERED_READ; - break; - case NVME_CMD_WRITE: - case NVME_CMD_WRITE_ZEROES: - case NVME_CMD_ZONE_APPEND: - case NVME_CMD_COPY: - status = NVME_WRITE_FAULT; - break; - default: - status = NVME_INTERNAL_DEV_ERROR; - break; - } - - trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); - - error_setg_errno(&local_err, -ret, "aio failed"); - error_report_err(local_err); - - /* - * Set the command status code to the first encountered error but allow a - * subsequent Internal Device Error to trump it. - */ - if (req->status && status != NVME_INTERNAL_DEV_ERROR) { - return; - } - - req->status = status; -} - static inline uint32_t nvme_zone_idx(NvmeNamespace *ns, uint64_t slba) { return ns->zone_size_log2 > 0 ? slba >> ns->zone_size_log2 : @@ -2182,8 +2146,30 @@ void nvme_rw_complete_cb(void *opaque, int ret) trace_pci_nvme_rw_complete_cb(nvme_cid(req), blk_name(blk)); if (ret) { + Error *err = NULL; + block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + + switch (req->cmd.opcode) { + case NVME_CMD_READ: + req->status = NVME_UNRECOVERED_READ; + break; + + case NVME_CMD_WRITE: + case NVME_CMD_WRITE_ZEROES: + case NVME_CMD_ZONE_APPEND: + req->status = NVME_WRITE_FAULT; + break; + + default: + req->status = NVME_INTERNAL_DEV_ERROR; + break; + } + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + + error_setg_errno(&err, -ret, "aio failed"); + error_report_err(err); } else { block_acct_done(stats, acct); } From fa4d79c64dae03ffa269e42e21822453856618b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 7 Oct 2024 15:39:51 +0100 Subject: [PATCH 2005/2892] scripts: mandate that new files have SPDX-License-Identifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Going forward we want all newly created source files to have an SPDX-License-Identifier tag present. Initially mandate this for C, Python, Perl, Shell source files, as well as JSON (QAPI) and Makefiles, while encouraging users to consider it for other file types. Reviewed-by: Brian Cain Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 06d07e6c22..01f25aa88d 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1378,6 +1378,8 @@ sub process { my $in_imported_file = 0; my $in_no_imported_file = 0; my $non_utf8_charset = 0; + my $expect_spdx = 0; + my $expect_spdx_file; our @report = (); our $cnt_lines = 0; @@ -1615,6 +1617,34 @@ sub process { WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } +# All new files should have a SPDX-License-Identifier tag + if ($line =~ /^new file mode\s*\d+\s*$/) { + if ($expect_spdx) { + if ($expect_spdx_file =~ + /\.(c|h|py|pl|sh|json|inc|Makefile)$/) { + # source code files MUST have SPDX license declared + ERROR("New file '$expect_spdx_file' requires " . + "'SPDX-License-Identifer'"); + } else { + # Other files MAY have SPDX license if appropriate + WARNING("Does new file '$expect_spdx_file' need " . + "'SPDX-License-Identifer'?"); + } + } + $expect_spdx = 1; + $expect_spdx_file = undef; + } elsif ($expect_spdx) { + $expect_spdx_file = $realfile unless + defined $expect_spdx_file; + + # SPDX tags may occurr in comments which were + # stripped from '$line', so use '$rawline' + if ($rawline =~ /SPDX-License-Identifier/) { + $expect_spdx = 0; + $expect_spdx_file = undef; + } + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . From 2b96c1a4931e3b4e0294761c16759bcbb2652df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 7 Oct 2024 15:40:28 +0100 Subject: [PATCH 2006/2892] scripts: validate SPDX license choices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We expect all new code to be contributed with the "GPL-2.0-or-later" license tag. Divergence is permitted if the new file is derived from pre-existing code under a different license, whether from elsewhere in QEMU codebase, or outside. Issue a warning if the declared license is not "GPL-2.0-or-later", and an error if the license is not one of the handful of the expected licenses to prevent unintended proliferation. The warning asks users to explain their unusual choice of license in the commit message. Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 69 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 01f25aa88d..8995d2c391 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1353,6 +1353,70 @@ sub checkfilename { } } +sub checkspdx { + my ($file, $expr) = @_; + + # Imported Linux headers probably have SPDX tags, but if they + # don't we're not requiring contributors to fix this, as these + # files are not expected to be modified locally in QEMU. + # Also don't accidentally detect own checking code. + if ($file =~ m,include/standard-headers, || + $file =~ m,linux-headers, || + $file =~ m,checkpatch.pl,) { + return; + } + + my $origexpr = $expr; + + # Flatten sub-expressions + $expr =~ s/\(|\)/ /g; + $expr =~ s/OR|AND/ /g; + + # Merge WITH exceptions to the license + $expr =~ s/\s+WITH\s+/-WITH-/g; + + # Cull more leading/trailing whitespace + $expr =~ s/^\s*//g; + $expr =~ s/\s*$//g; + + my @bits = split / +/, $expr; + + my $prefer = "GPL-2.0-or-later"; + my @valid = qw( + GPL-2.0-only + LGPL-2.1-only + LGPL-2.1-or-later + BSD-2-Clause + BSD-3-Clause + MIT + ); + + my $nonpreferred = 0; + my @unknown = (); + foreach my $bit (@bits) { + if ($bit eq $prefer) { + next; + } + if (grep /^$bit$/, @valid) { + $nonpreferred = 1; + } else { + push @unknown, $bit; + } + } + if (@unknown) { + ERROR("Saw unacceptable licenses '" . join(',', @unknown) . + "', valid choices for QEMU are:\n" . join("\n", $prefer, @valid)); + } + + if ($nonpreferred) { + WARN("Saw acceptable license '$origexpr' but note '$prefer' is " . + "preferred for new files unless the code is derived from a " . + "source file with an existing declared license that must be " . + "retained. Please explain the license choice in the commit " . + "message."); + } +} + sub process { my $filename = shift; @@ -1645,6 +1709,11 @@ sub process { } } +# Check SPDX-License-Identifier references a permitted license + if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { + &checkspdx($realfile, $1); + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . From 6b7521818b26134726b3494cd06f04e30659ce2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 7 Oct 2024 16:21:54 +0100 Subject: [PATCH 2007/2892] scripts: forbid use of arbitrary SPDX tags besides license identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While SPDX-License-Identifier is a well known SPDX tag, there are a great many more besides that[1]. These are mostly focused on making machine readable metadata available to the 'reuse' tool and similar. They cover concepts like author names, copyright owners, and much more. It is even possible to define source file line groups and apply different SPDX tags to regions of code within a file. At this time we're only interested in adopting SPDX for recording the file global licensing info, so detect & reject any other SPDX metadata. If we want to explicitly collect extra data in SPDX format, we can evaluate each data item on its merits when someone wants to propose it at a later date. [1] https://spdx.github.io/spdx-spec/v2.2.2/file-tags/ https://spdx.github.io/spdx-spec/v2.2.2/file-information/ Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8995d2c391..83b59fb443 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1714,6 +1714,18 @@ sub process { &checkspdx($realfile, $1); } + if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) { + my $tag = $1; + my @permitted = qw( + SPDX-License-Identifier + ); + + unless (grep { /^$tag$/ } @permitted) { + ERROR("Tag $tag not permitted in QEMU code, valid " . + "choices are: " . join(", ", @permitted)); + } + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . From 661c2e1ab29cd9c4d268ae3f44712e8d421c0e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 3 Mar 2025 18:25:08 +0100 Subject: [PATCH 2008/2892] scripts/checkpatch: Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running checkpatch.pl on a commit adding a file without SPDX tag we get: Undefined subroutine &main::WARNING called at ./scripts/checkpatch.pl line 1694. The WARNING level is reported by the WARN() method. Fix the typo. Fixes: fa4d79c64da ("scripts: mandate that new files have SPDX-License-Identifier") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-ID: <20250303172508.93234-1-philmd@linaro.org> Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 83b59fb443..6ae9d7febe 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1691,8 +1691,8 @@ sub process { "'SPDX-License-Identifer'"); } else { # Other files MAY have SPDX license if appropriate - WARNING("Does new file '$expect_spdx_file' need " . - "'SPDX-License-Identifer'?"); + WARN("Does new file '$expect_spdx_file' need " . + "'SPDX-License-Identifer'?"); } } $expect_spdx = 1; From bf031e257dea3d4d41f96d9eef77f447f56860e4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 15:48:43 -0300 Subject: [PATCH 2009/2892] target/riscv/csr.c: fix deadcode in rmw_xireg() Coverity found a DEADCODE issue in rmw_xireg() claiming that we can't reach 'RISCV_EXCP_VIRT_INSTRUCTION_FAULT' at the 'done' label: done: if (ret) { return (env->virt_enabled && virt) ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; This happens because the 'virt' flag, which is only used by 'done', is set to 'false' and it will always remain 'false' in any condition where we'll jump to 'done': switch (csrno) { (...) case CSR_VSIREG: isel = env->vsiselect; virt = true; break; default: goto done; }; 'virt = true' will never reach 'done' because we have a if/else-if/else block right before the label that will always return: if (xiselect_aia_range(isel)) { return ... } else if (...) { return ... } else { return RISCV_EXCP_ILLEGAL_INST; } All this means that we can preserve the current logic by reducing the 'done' label to: done: if (ret) { return RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; The flag 'virt' is now unused. Remove it. Fix the 'goto done' identation while we're at it. Resolves: Coverity CID 1590359 Fixes: dc0280723d ("target/riscv: Decouple AIA processing from xiselect and xireg") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250121184847.2109128-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index afb7544f07..ab209d0cda 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2658,7 +2658,6 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - bool virt = false; int ret = -EINVAL; target_ulong isel; @@ -2680,10 +2679,9 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, break; case CSR_VSIREG: isel = env->vsiselect; - virt = true; break; default: - goto done; + goto done; }; /* @@ -2705,8 +2703,7 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, done: if (ret) { - return (env->virt_enabled && virt) ? - RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + return RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; } From e07a89143bb2735858a1eefeb9bcd9eb637e8e8f Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 15:48:44 -0300 Subject: [PATCH 2010/2892] target/riscv/csr.c: fix 'ret' deadcode in rmw_xireg() Coverity found a second DEADCODE issue in rmw_xireg() claiming that we can't reach 'RISCV_EXCP_NONE' at the 'done' label: > 2706 done: > 2707 if (ret) { > 2708 return (env->virt_enabled && virt) ? > 2709 RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; > 2710 } >>>> CID 1590356: Control flow issues (DEADCODE) >>>> Execution cannot reach this statement: "return RISCV_EXCP_NONE;". > 2711 return RISCV_EXCP_NONE; Our label is now reduced after fixing another deadcode in the previous patch but the problem reported here still remains: done: if (ret) { return RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; This happens because 'ret' changes only once at the start of the function: ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); if (ret != RISCV_EXCP_NONE) { return ret; } So it's a guarantee that ret will be RISCV_EXCP_NONE (-1) if we ever reach the label, i.e. "if (ret)" will always be true, and the label can be even further reduced to: done: return RISCV_EXCP_ILLEGAL_INST; To make a better use of the label, remove the 'else' from the xiselect_aia_range() chain and let it fall-through to the 'done' label since they are now both returning RISCV_EXCP_ILLEGAL_INST. Resolves: Coverity CID 1590356 Fixes: dc0280723d ("target/riscv: Decouple AIA processing from xiselect and xireg") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250121184847.2109128-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index ab209d0cda..0e83c3b045 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2697,15 +2697,10 @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, } else if (riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind) { return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); - } else { - return RISCV_EXCP_ILLEGAL_INST; } done: - if (ret) { - return RISCV_EXCP_ILLEGAL_INST; - } - return RISCV_EXCP_NONE; + return RISCV_EXCP_ILLEGAL_INST; } static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, From c1edd5f756788082e70dc23a9d630bbbe8449f2a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 15:48:45 -0300 Subject: [PATCH 2011/2892] target/riscv/csr.c: fix deadcode in rmw_xiregi() Coverity found a DEADCODE issue in rmw_xiregi() claiming that we can't reach 'RISCV_EXCP_VIRT_INSTRUCTION_FAULT' at the 'done' label: > 2652 done: >>>> CID 1590357: Control flow issues (DEADCODE) >>>> Execution cannot reach the expression "RISCV_EXCP_VIRT_INSTRUCTION_FAULT" inside this statement: "return (env->virt_enabled &...". > 2653 return (env->virt_enabled && virt) ? > 2654 RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; This happens because 'virt' is being set to 'false' and it will remain as 'false' in any code path where 'done' will be called. The label can be safely reduced to: done: return RISCV_EXCP_ILLEGAL_INST; And that will leave us with the following usage of a 'goto' skipping a single 'return' to do another single 'return': } else { goto done; } return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); done: return RISCV_EXCP_ILLEGAL_INST; Which we will eliminate it and just do 'return RISCV_EXCP_ILLEGAL_INST' instead. Resolves: Coverity CID 1590357 Fixes: 5e33a20827 ("target/riscv: Support generic CSR indirect access") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250121184847.2109128-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 0e83c3b045..75f21ccabb 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2621,7 +2621,6 @@ static int rmw_xireg_csrind(CPURISCVState *env, int csrno, static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - bool virt = false; int ret = -EINVAL; target_ulong isel; @@ -2642,16 +2641,11 @@ static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val, } else if (CSR_VSIREG <= csrno && csrno <= CSR_VSIREG6 && csrno != CSR_VSIREG4 - 1) { isel = env->vsiselect; - virt = true; } else { - goto done; + return RISCV_EXCP_ILLEGAL_INST; } return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); - -done: - return (env->virt_enabled && virt) ? - RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } static RISCVException rmw_xireg(CPURISCVState *env, int csrno, From 485eb79989c6f9f00103ef2e62360ad9cf6a9b23 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 15:48:46 -0300 Subject: [PATCH 2012/2892] target/riscv/csr.c: fix deadcode in aia_smode32() Coverity reported a DEADCODE ticket in this function, as follows: >>>> CID 1590358: Control flow issues (DEADCODE) >>>> Execution cannot reach this statement: "return ret;". > 380 return ret; > 381 } The cause is that the 'if (ret != RISCV_EXCP_NONE)' conditional is duplicated: ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); if (ret != RISCV_EXCP_NONE) { return ret; } if (ret != RISCV_EXCP_NONE) { return ret; } Remove the duplication to fix the deadcode. Resolves: Coverity CID 1590358 Fixes: dbcb6e1ccf ("target/riscv: Enable S*stateen bits for AIA") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250121184847.2109128-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 75f21ccabb..dc0a88a0f0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -376,10 +376,6 @@ static RISCVException aia_smode32(CPURISCVState *env, int csrno) return ret; } - if (ret != RISCV_EXCP_NONE) { - return ret; - } - return smode32(env, csrno); } From b55538ea22c6474e62a311f5993f0f84caeb4131 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 15:48:47 -0300 Subject: [PATCH 2013/2892] target/riscv/cpu_helper.c: fix bad_shift in riscv_cpu_interrupt() Coverity reported a BAD_SHIFT issue in the following code: > 2097 >>>> CID 1590355: Integer handling issues (BAD_SHIFT) >>>> In expression "hdeleg >> cause", right shifting by more than 63 bits has undefined behavior. The shift amount, "cause", is at least 64. > 2098 vsmode_exc = env->virt_enabled && (((hdeleg >> cause) & 1) || vs_injected); > 2099 /* It is not clear to me how the tool guarantees that '"cause" is at least 64', but indeed there's no guarantees that it would be < 64 in the 'async = true' code path. A simple fix to avoid a potential UB is to add a 'cause < 64' guard like 'mode' is already doing right before 'vsmode_exc'. Resolves: Coverity CID 1590355 Fixes: 967760f62c ("target/riscv: Implement Ssdbltrp exception handling") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250121184847.2109128-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8ff6d900f2..1de8e0e494 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -2084,7 +2084,9 @@ void riscv_cpu_do_interrupt(CPUState *cs) mode = env->priv <= PRV_S && cause < 64 && (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M; - vsmode_exc = env->virt_enabled && (((hdeleg >> cause) & 1) || vs_injected); + vsmode_exc = env->virt_enabled && cause < 64 && + (((hdeleg >> cause) & 1) || vs_injected); + /* * Check double trap condition only if already in S-mode and targeting * S-mode From ffd455963f230c7dc04965609d6675da687a5a78 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 24 Jan 2025 18:14:47 +0800 Subject: [PATCH 2014/2892] target/riscv: rvv: Fix unexpected behavior of vector reduction instructions when vl is 0 According to the Vector Reduction Operations section in the RISC-V "V" Vector Extension spec, "If vl=0, no operation is performed and the destination register is not updated." The vd should be updated when vl is larger than 0. Fixes: fe5c9ab1fc ("target/riscv: vector single-width integer reduction instructions") Fixes: f714361ed7 ("target/riscv: rvv-1.0: implement vstart CSR") Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20250124101452.2519171-1-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 5386e3b97c..7773df6a7c 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -4659,7 +4659,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ } \ s1 = OP(s1, (TD)s2); \ } \ - *((TD *)vd + HD(0)) = s1; \ + if (vl > 0) { \ + *((TD *)vd + HD(0)) = s1; \ + } \ env->vstart = 0; \ /* set tail elements to 1s */ \ vext_set_elems_1s(vd, vta, esz, vlenb); \ @@ -4745,7 +4747,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ } \ s1 = OP(s1, (TD)s2, &env->fp_status); \ } \ - *((TD *)vd + HD(0)) = s1; \ + if (vl > 0) { \ + *((TD *)vd + HD(0)) = s1; \ + } \ env->vstart = 0; \ /* set tail elements to 1s */ \ vext_set_elems_1s(vd, vta, esz, vlenb); \ From bf3adf93f16730ca5aaa6c26cf969e64eeff6e7b Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 24 Jan 2025 17:05:38 +0800 Subject: [PATCH 2015/2892] target/riscv: rvv: Fix incorrect vlen comparison in prop_vlen_set In prop_vlen_set function, there is an incorrect comparison between vlen(bit) and vlenb(byte). This will cause unexpected error when user applies the `vlen=1024` cpu option with a vendor predefined cpu type that the default vlen is 1024(vlenb=128). Fixes: 4f6d036ccc ("target/riscv/cpu.c: remove cpu->cfg.vlen") Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20250124090539.2506448-1-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d7ecf729d0..99436f1750 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2027,6 +2027,7 @@ static void prop_vlen_set(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t cpu_vlen = cpu->cfg.vlenb << 3; uint16_t value; if (!visit_type_uint16(v, name, &value, errp)) { @@ -2038,10 +2039,10 @@ static void prop_vlen_set(Object *obj, Visitor *v, const char *name, return; } - if (value != cpu->cfg.vlenb && riscv_cpu_is_vendor(obj)) { + if (value != cpu_vlen && riscv_cpu_is_vendor(obj)) { cpu_set_prop_err(cpu, name, errp); error_append_hint(errp, "Current '%s' val: %u\n", - name, cpu->cfg.vlenb << 3); + name, cpu_vlen); return; } From 3fba76e61caa46329afc399b3ecaaba70c8b0a4e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 14:06:25 -0300 Subject: [PATCH 2016/2892] target/riscv/debug.c: use wp size = 4 for 32-bit CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mcontrol select bit (19) is always zero, meaning our triggers will always match virtual addresses. In this condition, if the user does not specify a size for the trigger, the access size defaults to XLEN. At this moment we're using def_size = 8 regardless of CPU XLEN. Use def_size = 4 in case we're running 32 bits. Fixes: 95799e36c1 ("target/riscv: Add initial support for the Sdtrig extension") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250121170626.1992570-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/debug.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/debug.c b/target/riscv/debug.c index f6241a80be..9db4048523 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -478,7 +478,7 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) bool enabled = type2_breakpoint_enabled(ctrl); CPUState *cs = env_cpu(env); int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; - uint32_t size; + uint32_t size, def_size; if (!enabled) { return; @@ -501,7 +501,9 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) cpu_watchpoint_insert(cs, addr, size, flags, &env->cpu_watchpoint[index]); } else { - cpu_watchpoint_insert(cs, addr, 8, flags, + def_size = riscv_cpu_mxl(env) == MXL_RV64 ? 8 : 4; + + cpu_watchpoint_insert(cs, addr, def_size, flags, &env->cpu_watchpoint[index]); } } From c86edc547692d812d1dcc04220c38310be2c00c3 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 21 Jan 2025 14:06:26 -0300 Subject: [PATCH 2017/2892] target/riscv: throw debug exception before page fault In the RISC-V privileged ISA section 3.1.15 table 15, it is determined that a debug exception that is triggered from a load/store has a higher priority than a possible fault that this access might trigger. This is not the case ATM as shown in [1]. Adding a breakpoint in an address that deliberately will fault is causing a load page fault instead of a debug exception. The reason is that we're throwing in the page fault as soon as the fault occurs (end of riscv_cpu_tlb_fill(), raise_mmu_exception()), not allowing the installed watchpoints to trigger. Call cpu_check_watchpoint() in the page fault path to search and execute any watchpoints that might exist for the address, never returning back to the fault path. If no watchpoints are found cpu_check_watchpoint() will return and we'll fall-through the regular path to raise_mmu_exception(). [1] https://gitlab.com/qemu-project/qemu/-/issues/2627 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2627 Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Message-ID: <20250121170626.1992570-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 1de8e0e494..29dc721c5d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -27,6 +27,7 @@ #include "exec/page-protection.h" #include "instmap.h" #include "tcg/tcg-op.h" +#include "hw/core/tcg-cpu-ops.h" #include "trace.h" #include "semihosting/common-semi.h" #include "system/cpu-timers.h" @@ -1697,6 +1698,23 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { + int wp_access = 0; + + if (access_type == MMU_DATA_LOAD) { + wp_access |= BP_MEM_READ; + } else if (access_type == MMU_DATA_STORE) { + wp_access |= BP_MEM_WRITE; + } + + /* + * If a watchpoint isn't found for 'addr' this will + * be a no-op and we'll resume the mmu_exception path. + * Otherwise we'll throw a debug exception and execution + * will continue elsewhere. + */ + cpu_check_watchpoint(cs, address, size, MEMTXATTRS_UNSPECIFIED, + wp_access, retaddr); + raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error, two_stage_lookup, two_stage_indirect_error); From a680d9531e9b3726dddee94e5f49900c4b756ea6 Mon Sep 17 00:00:00 2001 From: Huang Borong Date: Wed, 15 Jan 2025 11:51:05 +0800 Subject: [PATCH 2018/2892] hw/intc/riscv_aplic: Remove redundant "hart_idx" masking Remove the redundant masking of "hart_idx", as the same operation is performed later during address calculation. This change impacts the "hart_idx" value in the final qemu_log_mask() call. The original "hart_idx" parameter should be used for logging to ensure accuracy, rather than the masked value. Signed-off-by: Huang Borong Reviewed-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Message-ID: <20250115035105.19600-1-huangborong@bosc.ac.cn> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 4866649115..0974c6a5db 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -421,7 +421,6 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, 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; From 38d0ce28dfc7a73c7527442bb3b7029b402cb056 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 15 Jan 2025 15:43:11 -0300 Subject: [PATCH 2019/2892] target/riscv: add ssu64xl ssu64xl is defined in RVA22 as: "sstatus.UXL must be capable of holding the value 2 (i.e., UXLEN=64 must be supported)." This is always true in TCG and it's mandatory for RVA23, so claim support for it. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115184316.2344583-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 390 -> 398 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 99436f1750..4f5e13a759 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -213,6 +213,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(ssu64xl, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm), ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 695022d56c4ac16607d4c622955ad339fbbfe997..b14ec15e553200760a63aad65586913d31ea2edc 100644 GIT binary patch delta 48 zcmZo;?qlW(@^B96V`N}pOqj@Jz^cQ@$e^;(o|BQSxYW#~B4@H2qXkC_P&yBY0s9LH Ai~s-t delta 41 ucmeBUZe!*O@^B7mV`N}poG_8gfK`Q&kwIpoJtyPj07f&87@&9>5CZ_SKnGv| From e037673764b2b9db25f6e5deec8e11e7bf54c4b1 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 15 Jan 2025 15:43:12 -0300 Subject: [PATCH 2020/2892] target/riscv: use RVB in RVA22U64 From the time we added RVA22U64 until now the spec didn't declare 'RVB' as a dependency, using zba/zbb/zbs instead. Since then the RVA22 spec [1] added the following in the 'RVA22U64 Mandatory Extensions' section: "B Bit-manipulation instructions Note: The B extension comprises the Zba, Zbb, and Zbs extensions. At the time of RVA22U64's ratification, the B extension had not yet been defined, and so RVA22U64 explicitly mandated Zba, Zbb, and Zbs instead. Mandating B is equivalent." It is also equivalent to QEMU (see riscv_cpu_validate_b() in target/riscv/tcg/tcg-cpu.c). Finally, RVA23U64 [2] directly mentions RVB as a mandatory extension, not citing zba/zbb/zbs. To make it clear that RVA23U64 will extend RVA22U64 (i.e. RVA22 is a parent of RVA23), use RVB in RVA22U64 as well. (bios-tables-test change: RVB added to riscv,isa) [1] https://github.com/riscv/riscv-profiles/blob/main/src/profiles.adoc#61-rva22u64-profile [2] https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc#rva23u64-profile Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115184316.2344583-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- tests/data/acpi/riscv64/virt/RHCT | Bin 398 -> 400 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4f5e13a759..578bc95652 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2345,7 +2345,7 @@ static const PropertyInfo prop_marchid = { static RISCVCPUProfile RVA22U64 = { .parent = NULL, .name = "rva22u64", - .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVU, + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVB | RVU, .priv_spec = RISCV_PROFILE_ATTR_UNUSED, .satp_mode = RISCV_PROFILE_ATTR_UNUSED, .ext_offsets = { diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index b14ec15e553200760a63aad65586913d31ea2edc..13c8025b868051485be5ba62974a22971a07bc6a 100644 GIT binary patch delta 53 zcmeBUp1{l%JqB1j+%-qDZl;ot1UQ&#clNpsc(tv6T GfEWN^whTc4 delta 52 zcmbQh+{ern6Im@tvcKtP9)kwJyAsLaeHGdD3UC3&N_6yxMHMkS6EpprZw F1^_xF3qt?^ From 1813fc68c40448982f5700ab7a3e95b1aa6660ac Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 15 Jan 2025 15:43:13 -0300 Subject: [PATCH 2021/2892] target/riscv: add profile u_parent and s_parent The current 'parent' mechanic for profiles allows for one profile to be a child of a previous/older profile, enabling all its extensions (and the parent profile itself) and sparing us from tediously listing all extensions for every profile. This works fine for u-mode profiles. For s-mode profiles this is not enough: a s-mode profile extends not only his equivalent u-mode profile but also the previous s-mode profile. This means, for example, that RVA23S64 extends both RVA23U64 and RVA22S64. To fit this usage, rename the existing 'parent' to 'u_parent' and add a new 's_parent' attribute for profiles. Handle both like we were doing with the previous 'parent' attribute, i.e. if set, enable it. This change does nothing for the existing profiles but will make RVA23S64 simpler. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115184316.2344583-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++++-- target/riscv/cpu.h | 3 ++- target/riscv/tcg/tcg-cpu.c | 35 ++++++++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 578bc95652..7b708bd010 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2343,7 +2343,8 @@ static const PropertyInfo prop_marchid = { * doesn't need to be manually enabled by the profile. */ static RISCVCPUProfile RVA22U64 = { - .parent = NULL, + .u_parent = NULL, + .s_parent = NULL, .name = "rva22u64", .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVB | RVU, .priv_spec = RISCV_PROFILE_ATTR_UNUSED, @@ -2375,7 +2376,8 @@ static RISCVCPUProfile RVA22U64 = { * The remaining features/extensions comes from RVA22U64. */ static RISCVCPUProfile RVA22S64 = { - .parent = &RVA22U64, + .u_parent = &RVA22U64, + .s_parent = NULL, .name = "rva22s64", .misa_ext = RVS, .priv_spec = PRIV_VERSION_1_12_0, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 97713681cb..986131a191 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -81,7 +81,8 @@ const char *riscv_get_misa_ext_description(uint32_t bit); #define CPU_CFG_OFFSET(_prop) offsetof(struct RISCVCPUConfig, _prop) typedef struct riscv_cpu_profile { - struct riscv_cpu_profile *parent; + struct riscv_cpu_profile *u_parent; + struct riscv_cpu_profile *s_parent; const char *name; uint32_t misa_ext; bool enabled; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index d7e694fdb3..2b21942ef2 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -713,13 +713,29 @@ static bool riscv_cpu_validate_profile_satp(RISCVCPU *cpu, } #endif +static void riscv_cpu_check_parent_profile(RISCVCPU *cpu, + RISCVCPUProfile *profile, + RISCVCPUProfile *parent) +{ + const char *parent_name; + bool parent_enabled; + + if (!profile->enabled || !parent) { + return; + } + + parent_name = parent->name; + parent_enabled = object_property_get_bool(OBJECT(cpu), parent_name, NULL); + profile->enabled = parent_enabled; +} + static void riscv_cpu_validate_profile(RISCVCPU *cpu, RISCVCPUProfile *profile) { CPURISCVState *env = &cpu->env; const char *warn_msg = "Profile %s mandates disabled extension %s"; bool send_warn = profile->user_set && profile->enabled; - bool parent_enabled, profile_impl = true; + bool profile_impl = true; int i; #ifndef CONFIG_USER_ONLY @@ -773,12 +789,8 @@ static void riscv_cpu_validate_profile(RISCVCPU *cpu, profile->enabled = profile_impl; - if (profile->parent != NULL) { - parent_enabled = object_property_get_bool(OBJECT(cpu), - profile->parent->name, - NULL); - profile->enabled = profile->enabled && parent_enabled; - } + riscv_cpu_check_parent_profile(cpu, profile, profile->u_parent); + riscv_cpu_check_parent_profile(cpu, profile, profile->s_parent); } static void riscv_cpu_validate_profiles(RISCVCPU *cpu) @@ -1190,8 +1202,13 @@ static void cpu_set_profile(Object *obj, Visitor *v, const char *name, profile->user_set = true; profile->enabled = value; - if (profile->parent != NULL) { - object_property_set_bool(obj, profile->parent->name, + if (profile->u_parent != NULL) { + object_property_set_bool(obj, profile->u_parent->name, + profile->enabled, NULL); + } + + if (profile->s_parent != NULL) { + object_property_set_bool(obj, profile->s_parent->name, profile->enabled, NULL); } From 08dfc194546632ee0ca0c00030b76a89b6efd298 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 15 Jan 2025 15:43:14 -0300 Subject: [PATCH 2022/2892] target/riscv: change priv_ver check in validate_profile() The S profiles do a priv_ver check during validation to see if the running priv_ver is compatible with it. This check is done by comparing if the running priv_ver is equal to the priv_ver the profile specifies. There is an universe where we added RVA23S64 support based on both RVA23U64 and RVA22S64 and this error is being thrown: qemu-system-riscv64: warning: Profile rva22s64 requires priv spec v1.12.0, but priv ver v1.13.0 was set We're enabling RVA22S64 (priv_ver 1.12) as a dependency of RVA23S64 (priv_ver 1.13) and complaining to users about what we did ourselves. There's no drawback in allowing a profile to run in an env that has a priv_ver newer than it's required by it. So, like Hiro Nakamura saves the future by changing the past, change the priv_ver check now to allow profiles to run in a newer priv_ver. This universe will have one less warning to deal with. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115184316.2344583-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 2b21942ef2..cb9b504012 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -746,7 +746,7 @@ static void riscv_cpu_validate_profile(RISCVCPU *cpu, #endif if (profile->priv_spec != RISCV_PROFILE_ATTR_UNUSED && - profile->priv_spec != env->priv_ver) { + profile->priv_spec > env->priv_ver) { profile_impl = false; if (send_warn) { From a876221bd301d090768bb85ab0bd7e24ec21348c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 15 Jan 2025 15:43:15 -0300 Subject: [PATCH 2023/2892] target/riscv: add RVA23U64 profile Add RVA23U64 as described in [1]. Add it as a child of RVA22U64 since all RVA22U64 mandatory extensions are also present in RVA23U64. What's left then is to list the mandatory extensions that are RVA23 only. A new "rva23u64" CPU is also added. [1] https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115184316.2344583-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index d56b067bf2..53ead481a9 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -40,6 +40,7 @@ #define TYPE_RISCV_CPU_RV64E RISCV_CPU_TYPE_NAME("rv64e") #define TYPE_RISCV_CPU_RVA22U64 RISCV_CPU_TYPE_NAME("rva22u64") #define TYPE_RISCV_CPU_RVA22S64 RISCV_CPU_TYPE_NAME("rva22s64") +#define TYPE_RISCV_CPU_RVA23U64 RISCV_CPU_TYPE_NAME("rva23u64") #define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") #define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7b708bd010..511ed1df0e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2391,9 +2391,34 @@ static RISCVCPUProfile RVA22S64 = { } }; +/* + * All mandatory extensions from RVA22U64 are present + * in RVA23U64 so set RVA22 as a parent. We need to + * declare just the newly added mandatory extensions. + */ +static RISCVCPUProfile RVA23U64 = { + .u_parent = &RVA22U64, + .s_parent = NULL, + .name = "rva23u64", + .misa_ext = RVV, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .satp_mode = RISCV_PROFILE_ATTR_UNUSED, + .ext_offsets = { + CPU_CFG_OFFSET(ext_zvfhmin), CPU_CFG_OFFSET(ext_zvbb), + CPU_CFG_OFFSET(ext_zvkt), CPU_CFG_OFFSET(ext_zihintntl), + CPU_CFG_OFFSET(ext_zicond), CPU_CFG_OFFSET(ext_zimop), + CPU_CFG_OFFSET(ext_zcmop), CPU_CFG_OFFSET(ext_zcb), + CPU_CFG_OFFSET(ext_zfa), CPU_CFG_OFFSET(ext_zawrs), + CPU_CFG_OFFSET(ext_supm), + + RISCV_PROFILE_EXT_LIST_END + } +}; + RISCVCPUProfile *riscv_profiles[] = { &RVA22U64, &RVA22S64, + &RVA23U64, NULL, }; @@ -2880,6 +2905,13 @@ static void rva22s64_profile_cpu_init(Object *obj) RVA22S64.enabled = true; } + +static void rva23u64_profile_cpu_init(Object *obj) +{ + rv64i_bare_cpu_init(obj); + + RVA23U64.enabled = true; +} #endif static const gchar *riscv_gdb_arch_name(CPUState *cs) @@ -3150,6 +3182,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64E, MXL_RV64, rv64e_bare_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, MXL_RV64, rva22u64_profile_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, MXL_RV64, rva22s64_profile_cpu_init), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23U64, MXL_RV64, rva23u64_profile_cpu_init), #endif /* TARGET_RISCV64 */ }; From c91f74b91df0ff4aaf4905df328480b7c1e57c20 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 15 Jan 2025 15:43:16 -0300 Subject: [PATCH 2024/2892] target/riscv: add RVA23S64 profile Add RVA23S64 as described in [1]. This profile inherits all mandatory extensions of RVA23U64 and RVA22S64, making it a child of both profiles. A new "rva23s64" profile CPU is also added. This is the generated riscv,isa for it (taken via -M dumpdtb): rv64imafdcbvh_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ ziccrse_zicond_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zimop_ zmmul_za64rs_zaamo_zalrsc_zawrs_zfa_zfhmin_zca_zcb_zcd_zcmop_zba_zbb_zbs_ zkt_zvbb_zve32f_zve32x_zve64f_zve64d_zve64x_zvfhmin_zvkb_zvkt_shcounterenw_ sha_shgatpa_shtvala_shvsatpa_shvstvala_shvstvecd_smnpm_smstateen_ssccptr_ sscofpmf_sscounterenw_ssnpm_ssstateen_sstc_sstvala_sstvecd_ssu64xl_ supm_svade_svinval_svnapot_svpbmt [1] https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115184316.2344583-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 53ead481a9..4cfdb74891 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -41,6 +41,7 @@ #define TYPE_RISCV_CPU_RVA22U64 RISCV_CPU_TYPE_NAME("rva22u64") #define TYPE_RISCV_CPU_RVA22S64 RISCV_CPU_TYPE_NAME("rva22s64") #define TYPE_RISCV_CPU_RVA23U64 RISCV_CPU_TYPE_NAME("rva23u64") +#define TYPE_RISCV_CPU_RVA23S64 RISCV_CPU_TYPE_NAME("rva23s64") #define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") #define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 511ed1df0e..3624ffb6d9 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2415,10 +2415,41 @@ static RISCVCPUProfile RVA23U64 = { } }; +/* + * As with RVA23U64, RVA23S64 also defines 'named features'. + * + * Cache related features that we consider enabled since we don't + * implement cache: Ssccptr + * + * Other named features that we already implement: Sstvecd, Sstvala, + * Sscounterenw, Ssu64xl + * + * The remaining features/extensions comes from RVA23S64. + */ +static RISCVCPUProfile RVA23S64 = { + .u_parent = &RVA23U64, + .s_parent = &RVA22S64, + .name = "rva23s64", + .misa_ext = RVS, + .priv_spec = PRIV_VERSION_1_13_0, + .satp_mode = VM_1_10_SV39, + .ext_offsets = { + /* New in RVA23S64 */ + CPU_CFG_OFFSET(ext_svnapot), CPU_CFG_OFFSET(ext_sstc), + CPU_CFG_OFFSET(ext_sscofpmf), CPU_CFG_OFFSET(ext_ssnpm), + + /* Named features: Sha */ + CPU_CFG_OFFSET(ext_sha), + + RISCV_PROFILE_EXT_LIST_END + } +}; + RISCVCPUProfile *riscv_profiles[] = { &RVA22U64, &RVA22S64, &RVA23U64, + &RVA23S64, NULL, }; @@ -2912,6 +2943,13 @@ static void rva23u64_profile_cpu_init(Object *obj) RVA23U64.enabled = true; } + +static void rva23s64_profile_cpu_init(Object *obj) +{ + rv64i_bare_cpu_init(obj); + + RVA23S64.enabled = true; +} #endif static const gchar *riscv_gdb_arch_name(CPUState *cs) @@ -3183,6 +3221,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, MXL_RV64, rva22u64_profile_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, MXL_RV64, rva22s64_profile_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23U64, MXL_RV64, rva23u64_profile_cpu_init), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23S64, MXL_RV64, rva23s64_profile_cpu_init), #endif /* TARGET_RISCV64 */ }; From 9d6c2c1f10c7cd156d90daff505b2e2cf72fec89 Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Wed, 15 Jan 2025 22:17:29 +0800 Subject: [PATCH 2025/2892] hw/riscv/riscv-iommu: Remove redundant struct members Initially, the IOMMU would create a thread, but this thread was removed in the merged version. The struct members for thread control should have been removed as well, but they were not removed in commit 0c54acb8243 ("hw/riscv: add RISC-V IOMMU base emulation"). Signed-off-by: Jason Chien Reviewed-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115141730.30858-1-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index 9424989df4..fa8a50fa24 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -58,11 +58,6 @@ struct RISCVIOMMUState { /* interrupt notifier */ void (*notify)(RISCVIOMMUState *iommu, unsigned vector); - /* IOMMU State Machine */ - QemuThread core_proc; /* Background processing thread */ - QemuCond core_cond; /* Background processing wake up signal */ - unsigned core_exec; /* Processing thread execution actions */ - /* IOMMU target address space */ AddressSpace *target_as; MemoryRegion *target_mr; From a975e733a02ba5f1e1133c97cfe1545b08fe1fbc Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Wed, 15 Jan 2025 22:17:30 +0800 Subject: [PATCH 2026/2892] hw/riscv/riscv-iommu-bits: Remove duplicate definitions The header contains duplicate macro definitions. This commit eliminates the duplicate part. Signed-off-by: Jason Chien Reviewed-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250115141730.30858-2-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-bits.h | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index 485f36b9c9..de599b80d6 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -50,8 +50,14 @@ struct riscv_iommu_pq_record { #define RISCV_IOMMU_PREQ_HDR_PRIV BIT_ULL(33) #define RISCV_IOMMU_PREQ_HDR_EXEC BIT_ULL(34) #define RISCV_IOMMU_PREQ_HDR_DID GENMASK_ULL(63, 40) + /* Payload fields */ +#define RISCV_IOMMU_PREQ_PAYLOAD_R BIT_ULL(0) +#define RISCV_IOMMU_PREQ_PAYLOAD_W BIT_ULL(1) +#define RISCV_IOMMU_PREQ_PAYLOAD_L BIT_ULL(2) #define RISCV_IOMMU_PREQ_PAYLOAD_M GENMASK_ULL(2, 0) +#define RISCV_IOMMU_PREQ_PRG_INDEX GENMASK_ULL(11, 3) +#define RISCV_IOMMU_PREQ_UADDR GENMASK_ULL(63, 12) /* Common field positions */ #define RISCV_IOMMU_PPN_FIELD GENMASK_ULL(53, 10) @@ -382,22 +388,6 @@ enum riscv_iommu_fq_ttypes { RISCV_IOMMU_FW_TTYPE_PCIE_MSG_REQ = 9, }; -/* Header fields */ -#define RISCV_IOMMU_PREQ_HDR_PID GENMASK_ULL(31, 12) -#define RISCV_IOMMU_PREQ_HDR_PV BIT_ULL(32) -#define RISCV_IOMMU_PREQ_HDR_PRIV BIT_ULL(33) -#define RISCV_IOMMU_PREQ_HDR_EXEC BIT_ULL(34) -#define RISCV_IOMMU_PREQ_HDR_DID GENMASK_ULL(63, 40) - -/* Payload fields */ -#define RISCV_IOMMU_PREQ_PAYLOAD_R BIT_ULL(0) -#define RISCV_IOMMU_PREQ_PAYLOAD_W BIT_ULL(1) -#define RISCV_IOMMU_PREQ_PAYLOAD_L BIT_ULL(2) -#define RISCV_IOMMU_PREQ_PAYLOAD_M GENMASK_ULL(2, 0) -#define RISCV_IOMMU_PREQ_PRG_INDEX GENMASK_ULL(11, 3) -#define RISCV_IOMMU_PREQ_UADDR GENMASK_ULL(63, 12) - - /* * struct riscv_iommu_msi_pte - MSI Page Table Entry */ From 3521f9cadc29c7d68b73b325ddb46a7acebf6212 Mon Sep 17 00:00:00 2001 From: Rodrigo Dias Correa Date: Tue, 14 Jan 2025 22:21:50 +0100 Subject: [PATCH 2027/2892] goldfish_rtc: Fix tick_offset migration Instead of migrating the raw tick_offset, goldfish_rtc migrates a recalculated value based on QEMU_CLOCK_VIRTUAL. As QEMU_CLOCK_VIRTUAL stands still across a save-and-restore cycle, the guest RTC becomes out of sync with the host RTC when the VM is restored. As described in the bug description, it looks like this calculation was copied from pl031 RTC, which had its tick_offset migration fixed by Commit 032cfe6a79c8 ("pl031: Correctly migrate state when using -rtc clock=host"). Migrate the tick_offset directly, adding it as a version-dependent field to VMState. Keep the old behavior when migrating from previous versions. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2033 Signed-off-by: Rodrigo Dias Correa Reviewed-by: Alistair Francis Message-ID: <20250114212150.228241-1-r@drigo.nl> Signed-off-by: Alistair Francis --- hw/rtc/goldfish_rtc.c | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index fa1d9051f4..0f1b53e0e4 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -178,38 +178,21 @@ static void goldfish_rtc_write(void *opaque, hwaddr offset, trace_goldfish_rtc_write(offset, value); } -static int goldfish_rtc_pre_save(void *opaque) -{ - uint64_t delta; - GoldfishRTCState *s = opaque; - - /* - * We want to migrate this offset, which sounds straightforward. - * Unfortunately, we cannot directly pass tick_offset because - * rtc_clock on destination Host might not be same source Host. - * - * To tackle, this we pass tick_offset relative to vm_clock from - * source Host and make it relative to rtc_clock at destination Host. - */ - delta = qemu_clock_get_ns(rtc_clock) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tick_offset_vmstate = s->tick_offset + delta; - - return 0; -} - static int goldfish_rtc_post_load(void *opaque, int version_id) { - uint64_t delta; GoldfishRTCState *s = opaque; - /* - * We extract tick_offset from tick_offset_vmstate by doing - * reverse math compared to pre_save() function. - */ - delta = qemu_clock_get_ns(rtc_clock) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tick_offset = s->tick_offset_vmstate - delta; + if (version_id < 3) { + /* + * Previous versions didn't migrate tick_offset directly. Instead, they + * migrated tick_offset_vmstate, which is a recalculation based on + * QEMU_CLOCK_VIRTUAL. We use tick_offset_vmstate when migrating from + * older versions. + */ + uint64_t delta = qemu_clock_get_ns(rtc_clock) - + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->tick_offset = s->tick_offset_vmstate - delta; + } goldfish_rtc_set_alarm(s); @@ -239,8 +222,7 @@ static const MemoryRegionOps goldfish_rtc_ops[2] = { static const VMStateDescription goldfish_rtc_vmstate = { .name = TYPE_GOLDFISH_RTC, - .version_id = 2, - .pre_save = goldfish_rtc_pre_save, + .version_id = 3, .post_load = goldfish_rtc_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64(tick_offset_vmstate, GoldfishRTCState), @@ -249,6 +231,7 @@ static const VMStateDescription goldfish_rtc_vmstate = { VMSTATE_UINT32(irq_pending, GoldfishRTCState), VMSTATE_UINT32(irq_enabled, GoldfishRTCState), VMSTATE_UINT32(time_high, GoldfishRTCState), + VMSTATE_UINT64_V(tick_offset, GoldfishRTCState, 3), VMSTATE_END_OF_LIST() } }; From f73456781bf10a96849e2929488634f61eec8c5c Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Thu, 16 Jan 2025 17:10:07 +0100 Subject: [PATCH 2028/2892] hw/riscv/virt: Add serial alias in DTB Add an "aliases" node with a "serial0" entry for the single UART in the riscv virt machine. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2774 Signed-off-by: Vasilis Liaskovitis Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250116161007.39710-1-vliaskovitis@suse.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 241389d72f..dae46f4733 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -971,6 +971,7 @@ static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, } qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); } static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, @@ -1180,6 +1181,8 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + qemu_fdt_add_subnode(ms->fdt, "/aliases"); + create_fdt_flash(s, memmap); create_fdt_fw_cfg(s, memmap); create_fdt_pmu(s); From 4a16a1a7addaafbe65b5a7626cbe880fd9aa0c94 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 28 Jan 2025 16:05:46 +1000 Subject: [PATCH 2029/2892] MAINTAINERS: Remove Bin Meng from RISC-V maintainers Bin Meng has been a long time contributor and maintainer for QEMU RISC-V and has been very beneficial to the RISC-V ecosystem. Unfortunately his email has started to bounce so this patch is removing them from MAINTAINERS. If in the future Bin Meng wants to return we will happily re-add them. Note that I'm not removing Bin Meng as a "SD (Secure Card)" maintainer. Signed-off-by: Alistair Francis Acked-by: Daniel Henrique Barboza Message-ID: <20250128060546.1374394-1-alistair.francis@wdc.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2e7fc6fa91..433cf66e8f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -319,7 +319,6 @@ F: tests/functional/test_ppc_74xx.py RISC-V TCG CPUs M: Palmer Dabbelt M: Alistair Francis -M: Bin Meng R: Weiwei Li R: Daniel Henrique Barboza R: Liu Zhiwei @@ -1618,7 +1617,6 @@ F: include/hw/riscv/opentitan.h F: include/hw/*/ibex_*.h Microchip PolarFire SoC Icicle Kit -M: Bin Meng L: qemu-riscv@nongnu.org S: Supported F: docs/system/riscv/microchip-icicle-kit.rst @@ -1645,7 +1643,6 @@ F: include/hw/char/shakti_uart.h SiFive Machines M: Alistair Francis -M: Bin Meng M: Palmer Dabbelt L: qemu-riscv@nongnu.org S: Supported @@ -3747,7 +3744,7 @@ S: Orphan F: hw/i386/amd_iommu.? OpenSBI Firmware -M: Bin Meng +L: qemu-riscv@nongnu.org S: Supported F: pc-bios/opensbi-* F: .gitlab-ci.d/opensbi.yml From b638f679fede4835474a16cd423cb7e77ff6e7cc Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 5 Feb 2025 11:18:45 +0000 Subject: [PATCH 2030/2892] target/riscv: Remove obsolete sfence.vm instruction Signed-off-by: Rajnesh Kanwal Reviewed-by: Alistair Francis Reviewed-by: Jason Chien Message-ID: <20250205-b4-ctr_upstream_v6-v6-1-439d8e06c8ef@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 1 - target/riscv/insn_trans/trans_privileged.c.inc | 5 ----- 2 files changed, 6 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 942c434c6e..a98dab0205 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -119,7 +119,6 @@ sret 0001000 00010 00000 000 00000 1110011 mret 0011000 00010 00000 000 00000 1110011 wfi 0001000 00101 00000 000 00000 1110011 sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma -sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm # *** NMI *** mnret 0111000 00010 00000 000 00000 1110011 diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 73f940d406..27d5558d63 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -147,8 +147,3 @@ static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) #endif return false; } - -static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a) -{ - return false; -} From 3f833f8920d815caa6cd0215a5707a03426ba574 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 5 Feb 2025 11:18:46 +0000 Subject: [PATCH 2031/2892] target/riscv: Add Control Transfer Records CSR definitions. The Control Transfer Records (CTR) extension provides a method to record a limited branch history in register-accessible internal chip storage. This extension is similar to Arch LBR in x86 and BRBE in ARM. The Extension has been stable and the latest release can be found here https://github.com/riscv/riscv-control-transfer-records/releases/tag/v1.0_rc5 Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Message-ID: <20250205-b4-ctr_upstream_v6-v6-2-439d8e06c8ef@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 145 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index f97c48a394..70ef443c99 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -247,6 +247,17 @@ #define CSR_SIEH 0x114 #define CSR_SIPH 0x154 +/* Machine-Level Control transfer records CSRs */ +#define CSR_MCTRCTL 0x34e + +/* Supervisor-Level Control transfer records CSRs */ +#define CSR_SCTRCTL 0x14e +#define CSR_SCTRSTATUS 0x14f +#define CSR_SCTRDEPTH 0x15f + +/* VS-Level Control transfer records CSRs */ +#define CSR_VSCTRCTL 0x24e + /* Hpervisor CSRs */ #define CSR_HSTATUS 0x600 #define CSR_HEDELEG 0x602 @@ -344,6 +355,7 @@ #define SMSTATEEN0_CS (1ULL << 0) #define SMSTATEEN0_FCSR (1ULL << 1) #define SMSTATEEN0_JVT (1ULL << 2) +#define SMSTATEEN0_CTR (1ULL << 54) #define SMSTATEEN0_P1P13 (1ULL << 56) #define SMSTATEEN0_HSCONTXT (1ULL << 57) #define SMSTATEEN0_IMSIC (1ULL << 58) @@ -825,6 +837,139 @@ typedef enum RISCVException { #define HENVCFGH_PBMTE MENVCFGH_PBMTE #define HENVCFGH_STCE MENVCFGH_STCE +/* Offsets for every pair of control bits per each priv level */ +#define XS_OFFSET 0ULL +#define U_OFFSET 2ULL +#define S_OFFSET 5ULL +#define M_OFFSET 8ULL + +#define PM_XS_BITS (EXT_STATUS_MASK << XS_OFFSET) +#define U_PM_ENABLE (PM_ENABLE << U_OFFSET) +#define U_PM_CURRENT (PM_CURRENT << U_OFFSET) +#define U_PM_INSN (PM_INSN << U_OFFSET) +#define S_PM_ENABLE (PM_ENABLE << S_OFFSET) +#define S_PM_CURRENT (PM_CURRENT << S_OFFSET) +#define S_PM_INSN (PM_INSN << S_OFFSET) +#define M_PM_ENABLE (PM_ENABLE << M_OFFSET) +#define M_PM_CURRENT (PM_CURRENT << M_OFFSET) +#define M_PM_INSN (PM_INSN << M_OFFSET) + +/* mmte CSR bits */ +#define MMTE_PM_XS_BITS PM_XS_BITS +#define MMTE_U_PM_ENABLE U_PM_ENABLE +#define MMTE_U_PM_CURRENT U_PM_CURRENT +#define MMTE_U_PM_INSN U_PM_INSN +#define MMTE_S_PM_ENABLE S_PM_ENABLE +#define MMTE_S_PM_CURRENT S_PM_CURRENT +#define MMTE_S_PM_INSN S_PM_INSN +#define MMTE_M_PM_ENABLE M_PM_ENABLE +#define MMTE_M_PM_CURRENT M_PM_CURRENT +#define MMTE_M_PM_INSN M_PM_INSN +#define MMTE_MASK (MMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | MMTE_U_PM_INSN | \ + MMTE_S_PM_ENABLE | MMTE_S_PM_CURRENT | MMTE_S_PM_INSN | \ + MMTE_M_PM_ENABLE | MMTE_M_PM_CURRENT | MMTE_M_PM_INSN | \ + MMTE_PM_XS_BITS) + +/* (v)smte CSR bits */ +#define SMTE_PM_XS_BITS PM_XS_BITS +#define SMTE_U_PM_ENABLE U_PM_ENABLE +#define SMTE_U_PM_CURRENT U_PM_CURRENT +#define SMTE_U_PM_INSN U_PM_INSN +#define SMTE_S_PM_ENABLE S_PM_ENABLE +#define SMTE_S_PM_CURRENT S_PM_CURRENT +#define SMTE_S_PM_INSN S_PM_INSN +#define SMTE_MASK (SMTE_U_PM_ENABLE | SMTE_U_PM_CURRENT | SMTE_U_PM_INSN | \ + SMTE_S_PM_ENABLE | SMTE_S_PM_CURRENT | SMTE_S_PM_INSN | \ + SMTE_PM_XS_BITS) + +/* umte CSR bits */ +#define UMTE_U_PM_ENABLE U_PM_ENABLE +#define UMTE_U_PM_CURRENT U_PM_CURRENT +#define UMTE_U_PM_INSN U_PM_INSN +#define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN) + +/* CTR control register commom fields */ +#define XCTRCTL_U BIT_ULL(0) +#define XCTRCTL_S BIT_ULL(1) +#define XCTRCTL_RASEMU BIT_ULL(7) +#define XCTRCTL_STE BIT_ULL(8) +#define XCTRCTL_BPFRZ BIT_ULL(11) +#define XCTRCTL_LCOFIFRZ BIT_ULL(12) +#define XCTRCTL_EXCINH BIT_ULL(33) +#define XCTRCTL_INTRINH BIT_ULL(34) +#define XCTRCTL_TRETINH BIT_ULL(35) +#define XCTRCTL_NTBREN BIT_ULL(36) +#define XCTRCTL_TKBRINH BIT_ULL(37) +#define XCTRCTL_INDCALLINH BIT_ULL(40) +#define XCTRCTL_DIRCALLINH BIT_ULL(41) +#define XCTRCTL_INDJMPINH BIT_ULL(42) +#define XCTRCTL_DIRJMPINH BIT_ULL(43) +#define XCTRCTL_CORSWAPINH BIT_ULL(44) +#define XCTRCTL_RETINH BIT_ULL(45) +#define XCTRCTL_INDLJMPINH BIT_ULL(46) +#define XCTRCTL_DIRLJMPINH BIT_ULL(47) + +#define XCTRCTL_MASK (XCTRCTL_U | XCTRCTL_S | XCTRCTL_RASEMU | \ + XCTRCTL_STE | XCTRCTL_BPFRZ | XCTRCTL_LCOFIFRZ | \ + XCTRCTL_EXCINH | XCTRCTL_INTRINH | XCTRCTL_TRETINH | \ + XCTRCTL_NTBREN | XCTRCTL_TKBRINH | XCTRCTL_INDCALLINH | \ + XCTRCTL_DIRCALLINH | XCTRCTL_INDJMPINH | \ + XCTRCTL_DIRJMPINH | XCTRCTL_CORSWAPINH | \ + XCTRCTL_RETINH | XCTRCTL_INDLJMPINH | XCTRCTL_DIRLJMPINH) + +#define XCTRCTL_INH_START 32U + +/* CTR mctrctl bits */ +#define MCTRCTL_M BIT_ULL(2) +#define MCTRCTL_MTE BIT_ULL(9) + +#define MCTRCTL_MASK (XCTRCTL_MASK | MCTRCTL_M | MCTRCTL_MTE) +#define SCTRCTL_MASK XCTRCTL_MASK +#define VSCTRCTL_MASK XCTRCTL_MASK + +/* sctrstatus CSR bits. */ +#define SCTRSTATUS_WRPTR_MASK 0xFF +#define SCTRSTATUS_FROZEN BIT(31) +#define SCTRSTATUS_MASK (SCTRSTATUS_WRPTR_MASK | SCTRSTATUS_FROZEN) + +/* sctrdepth CSR bits. */ +#define SCTRDEPTH_MASK 0x7 +#define SCTRDEPTH_MIN 0U /* 16 Entries. */ +#define SCTRDEPTH_MAX 4U /* 256 Entries. */ + +#define CTR_ENTRIES_FIRST 0x200 +#define CTR_ENTRIES_LAST 0x2ff + +#define CTRSOURCE_VALID BIT(0) +#define CTRTARGET_MISP BIT(0) + +#define CTRDATA_TYPE_MASK 0xF +#define CTRDATA_CCV BIT(15) +#define CTRDATA_CCM_MASK 0xFFF0000 +#define CTRDATA_CCE_MASK 0xF0000000 + +#define CTRDATA_MASK (CTRDATA_TYPE_MASK | CTRDATA_CCV | \ + CTRDATA_CCM_MASK | CTRDATA_CCE_MASK) + +typedef enum CTRType { + CTRDATA_TYPE_NONE = 0, + CTRDATA_TYPE_EXCEPTION = 1, + CTRDATA_TYPE_INTERRUPT = 2, + CTRDATA_TYPE_EXCEP_INT_RET = 3, + CTRDATA_TYPE_NONTAKEN_BRANCH = 4, + CTRDATA_TYPE_TAKEN_BRANCH = 5, + CTRDATA_TYPE_RESERVED_0 = 6, + CTRDATA_TYPE_RESERVED_1 = 7, + CTRDATA_TYPE_INDIRECT_CALL = 8, + CTRDATA_TYPE_DIRECT_CALL = 9, + CTRDATA_TYPE_INDIRECT_JUMP = 10, + CTRDATA_TYPE_DIRECT_JUMP = 11, + CTRDATA_TYPE_CO_ROUTINE_SWAP = 12, + CTRDATA_TYPE_RETURN = 13, + CTRDATA_TYPE_OTHER_INDIRECT_JUMP = 14, + CTRDATA_TYPE_OTHER_DIRECT_JUMP = 15, +} CTRType; + /* MISELECT, SISELECT, and VSISELECT bits (AIA) */ #define ISELECT_IPRIO0 0x30 #define ISELECT_IPRIO15 0x3f From c48bd18eaeb676a7236030eb9b7984b9244d7750 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 5 Feb 2025 11:18:47 +0000 Subject: [PATCH 2032/2892] target/riscv: Add support for Control Transfer Records extension CSRs. This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and sctrdepth CSRs handling. Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Message-ID: <20250205-b4-ctr_upstream_v6-v6-3-439d8e06c8ef@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 5 ++ target/riscv/cpu_cfg.h | 2 + target/riscv/csr.c | 144 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 986131a191..ae355248f3 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -313,6 +313,11 @@ struct CPUArchState { target_ulong mcause; target_ulong mtval; /* since: priv-1.10.0 */ + uint64_t mctrctl; + uint32_t sctrdepth; + uint32_t sctrstatus; + uint64_t vsctrctl; + /* Machine and Supervisor interrupt priorities */ uint8_t miprio[64]; uint8_t siprio[64]; diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index b410b1e603..3f3c1118c0 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -133,6 +133,8 @@ struct RISCVCPUConfig { bool ext_zvfhmin; bool ext_smaia; bool ext_ssaia; + bool ext_smctr; + bool ext_ssctr; bool ext_sscofpmf; bool ext_smepmp; bool ext_smrnmi; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index dc0a88a0f0..ab295d2ef3 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -635,6 +635,48 @@ static RISCVException hgatp(CPURISCVState *env, int csrno) return hmode(env, csrno); } +/* + * M-mode: + * Without ext_smctr raise illegal inst excep. + * Otherwise everything is accessible to m-mode. + * + * S-mode: + * Without ext_ssctr or mstateen.ctr raise illegal inst excep. + * Otherwise everything other than mctrctl is accessible. + * + * VS-mode: + * Without ext_ssctr or mstateen.ctr raise illegal inst excep. + * Without hstateen.ctr raise virtual illegal inst excep. + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range. + * Always raise illegal instruction exception for sctrdepth. + */ +static RISCVException ctr_mmode(CPURISCVState *env, int csrno) +{ + /* Check if smctr-ext is present */ + if (riscv_cpu_cfg(env)->ext_smctr) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} + +static RISCVException ctr_smode(CPURISCVState *env, int csrno) +{ + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); + + if (!cfg->ext_smctr && !cfg->ext_ssctr) { + return RISCV_EXCP_ILLEGAL_INST; + } + + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR); + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH && + env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + + return ret; +} + static RISCVException aia_hmode(CPURISCVState *env, int csrno) { int ret; @@ -3216,6 +3258,10 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC); } + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_mstateen(env, csrno, wr_mask, new_val); } @@ -3255,6 +3301,10 @@ static RISCVException write_mstateen0h(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_P1P13; } + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_mstateenh(env, csrno, wr_mask, new_val); } @@ -3309,6 +3359,10 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC); } + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_hstateen(env, csrno, wr_mask, new_val); } @@ -3348,6 +3402,10 @@ static RISCVException write_hstateen0h(CPURISCVState *env, int csrno, { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_hstateenh(env, csrno, wr_mask, new_val); } @@ -4068,6 +4126,86 @@ static RISCVException write_satp(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t mask = wr_mask & SCTRDEPTH_MASK; + + if (ret_val) { + *ret_val = env->sctrdepth; + } + + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask); + + /* Correct depth. */ + if (mask) { + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK); + + if (depth > SCTRDEPTH_MAX) { + depth = SCTRDEPTH_MAX; + env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth); + } + + /* Update sctrstatus.WRPTR with a legal value */ + depth = 16 << depth; + env->sctrstatus = + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1)); + } + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint32_t mask = wr_mask & SCTRSTATUS_MASK; + + if (ret_val) { + *ret_val = env->sctrstatus; + } + + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask); + + /* Update sctrstatus.WRPTR with a legal value */ + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1)); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t csr_mask, mask = wr_mask; + uint64_t *ctl_ptr = &env->mctrctl; + + if (csrno == CSR_MCTRCTL) { + csr_mask = MCTRCTL_MASK; + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) { + csr_mask = SCTRCTL_MASK; + } else { + /* + * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true + * or csrno == CSR_VSCTRCTL. + */ + csr_mask = VSCTRCTL_MASK; + ctl_ptr = &env->vsctrctl; + } + + mask &= csr_mask; + + if (ret_val) { + *ret_val = *ctl_ptr & csr_mask; + } + + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask); + + return RISCV_EXCP_NONE; +} + static RISCVException read_vstopi(CPURISCVState *env, int csrno, target_ulong *val) { @@ -5821,6 +5959,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_TINFO] = { "tinfo", debug, read_tinfo, write_ignore }, [CSR_MCONTEXT] = { "mcontext", debug, read_mcontext, write_mcontext }, + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl }, + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl }, + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl }, + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth }, + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus }, + /* Performance Counters */ [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter }, [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter }, From 4ff7a27adce4c880d2137788da0fc57d75ee80be Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 5 Feb 2025 11:18:48 +0000 Subject: [PATCH 2033/2892] target/riscv: Add support to record CTR entries. This commit adds logic to records CTR entries of different types and adds required hooks in TCG and interrupt/Exception logic to record events. This commit also adds support to invoke freeze CTR logic for breakpoint exceptions and counter overflow interrupts. Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Message-ID: <20250205-b4-ctr_upstream_v6-v6-4-439d8e06c8ef@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 7 + target/riscv/cpu_helper.c | 259 ++++++++++++++++++ target/riscv/helper.h | 1 + .../riscv/insn_trans/trans_privileged.c.inc | 2 + target/riscv/insn_trans/trans_rvi.c.inc | 75 +++++ target/riscv/insn_trans/trans_rvzce.c.inc | 21 ++ target/riscv/op_helper.c | 19 ++ target/riscv/translate.c | 46 ++++ 8 files changed, 430 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ae355248f3..9e92144b61 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -318,6 +318,10 @@ struct CPUArchState { uint32_t sctrstatus; uint64_t vsctrctl; + uint64_t ctr_src[16 << SCTRDEPTH_MAX]; + uint64_t ctr_dst[16 << SCTRDEPTH_MAX]; + uint64_t ctr_data[16 << SCTRDEPTH_MAX]; + /* Machine and Supervisor interrupt priorities */ uint8_t miprio[64]; uint8_t siprio[64]; @@ -613,6 +617,9 @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, + enum CTRType type, target_ulong prev_priv, bool prev_virt); + void riscv_translate_init(void); void riscv_translate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 29dc721c5d..7dbdb34b17 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -875,6 +875,247 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, } } +static void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, + bool virt) +{ + uint64_t ctl = virt ? env->vsctrctl : env->mctrctl; + + assert((freeze_mask & (~(XCTRCTL_BPFRZ | XCTRCTL_LCOFIFRZ))) == 0); + + if (ctl & freeze_mask) { + env->sctrstatus |= SCTRSTATUS_FROZEN; + } +} + +static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt) +{ + switch (priv) { + case PRV_M: + return MCTRCTL_M; + case PRV_S: + if (virt) { + return XCTRCTL_S; + } + return XCTRCTL_S; + case PRV_U: + if (virt) { + return XCTRCTL_U; + } + return XCTRCTL_U; + } + + g_assert_not_reached(); +} + +static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv, + bool virt) +{ + switch (priv) { + case PRV_M: + return env->mctrctl; + case PRV_S: + case PRV_U: + if (virt) { + return env->vsctrctl; + } + return env->mctrctl; + } + + g_assert_not_reached(); +} + +/* + * This function assumes that src privilege and target privilege are not same + * and src privilege is less than target privilege. This includes the virtual + * state as well. + */ +static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv, + bool src_virt) +{ + target_long tgt_prv = env->priv; + bool res = true; + + /* + * VS and U mode are same in terms of xTE bits required to record an + * external trap. See 6.1.2. External Traps, table 8 External Trap Enable + * Requirements. This changes VS to U to simplify the logic a bit. + */ + if (src_virt && src_prv == PRV_S) { + src_prv = PRV_U; + } else if (env->virt_enabled && tgt_prv == PRV_S) { + tgt_prv = PRV_U; + } + + /* VU mode is an outlier here. */ + if (src_virt && src_prv == PRV_U) { + res &= !!(env->vsctrctl & XCTRCTL_STE); + } + + switch (src_prv) { + case PRV_U: + if (tgt_prv == PRV_U) { + break; + } + res &= !!(env->mctrctl & XCTRCTL_STE); + /* fall-through */ + case PRV_S: + if (tgt_prv == PRV_S) { + break; + } + res &= !!(env->mctrctl & MCTRCTL_MTE); + /* fall-through */ + case PRV_M: + break; + } + + return res; +} + +/* + * Special cases for traps and trap returns: + * + * 1- Traps, and trap returns, between enabled modes are recorded as normal. + * 2- Traps from an inhibited mode to an enabled mode, and trap returns from an + * enabled mode back to an inhibited mode, are partially recorded. In such + * cases, the PC from the inhibited mode (source PC for traps, and target PC + * for trap returns) is 0. + * + * 3- Trap returns from an inhibited mode to an enabled mode are not recorded. + * Traps from an enabled mode to an inhibited mode, known as external traps, + * receive special handling. + * By default external traps are not recorded, but a handshake mechanism exists + * to allow partial recording. Software running in the target mode of the trap + * can opt-in to allowing CTR to record traps into that mode even when the mode + * is inhibited. The MTE, STE, and VSTE bits allow M-mode, S-mode, and VS-mode, + * respectively, to opt-in. When an External Trap occurs, and xTE=1, such that + * x is the target privilege mode of the trap, will CTR record the trap. In such + * cases, the target PC is 0. + */ +/* + * CTR arrays are implemented as circular buffers and new entry is stored at + * sctrstatus.WRPTR, but they are presented to software as moving circular + * buffers. Which means, software get's the illusion that whenever a new entry + * is added the whole buffer is moved by one place and the new entry is added at + * the start keeping new entry at idx 0 and older ones follow. + * + * Depth = 16. + * + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * WRPTR W + * entry 7 6 5 4 3 2 1 0 F E D C B A 9 8 + * + * When a new entry is added: + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * WRPTR W + * entry 8 7 6 5 4 3 2 1 0 F E D C B A 9 + * + * entry here denotes the logical entry number that software can access + * using ctrsource, ctrtarget and ctrdata registers. So xiselect 0x200 + * will return entry 0 i-e buffer[8] and 0x201 will return entry 1 i-e + * buffer[7]. Here is how we convert entry to buffer idx. + * + * entry = isel - CTR_ENTRIES_FIRST; + * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1); + */ +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, + enum CTRType type, target_ulong src_priv, bool src_virt) +{ + bool tgt_virt = env->virt_enabled; + uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt); + uint64_t tgt_mask = riscv_ctr_priv_to_mask(env->priv, tgt_virt); + uint64_t src_ctrl = riscv_ctr_get_control(env, src_priv, src_virt); + uint64_t tgt_ctrl = riscv_ctr_get_control(env, env->priv, tgt_virt); + uint64_t depth, head; + bool ext_trap = false; + + /* + * Return immediately if both target and src recording is disabled or if + * CTR is in frozen state. + */ + if ((!(src_ctrl & src_mask) && !(tgt_ctrl & tgt_mask)) || + env->sctrstatus & SCTRSTATUS_FROZEN) { + return; + } + + /* + * With RAS Emul enabled, only allow Indirect, direct calls, Function + * returns and Co-routine swap types. + */ + if (tgt_ctrl & XCTRCTL_RASEMU && + type != CTRDATA_TYPE_INDIRECT_CALL && + type != CTRDATA_TYPE_DIRECT_CALL && + type != CTRDATA_TYPE_RETURN && + type != CTRDATA_TYPE_CO_ROUTINE_SWAP) { + return; + } + + if (type == CTRDATA_TYPE_EXCEPTION || type == CTRDATA_TYPE_INTERRUPT) { + /* Case 2 for traps. */ + if (!(src_ctrl & src_mask)) { + src = 0; + } else if (!(tgt_ctrl & tgt_mask)) { + /* Check if target priv-mode has allowed external trap recording. */ + if (!riscv_ctr_check_xte(env, src_priv, src_virt)) { + return; + } + + ext_trap = true; + dst = 0; + } + } else if (type == CTRDATA_TYPE_EXCEP_INT_RET) { + /* + * Case 3 for trap returns. Trap returns from inhibited mode are not + * recorded. + */ + if (!(src_ctrl & src_mask)) { + return; + } + + /* Case 2 for trap returns. */ + if (!(tgt_ctrl & tgt_mask)) { + dst = 0; + } + } + + /* Ignore filters in case of RASEMU mode or External trap. */ + if (!(tgt_ctrl & XCTRCTL_RASEMU) && !ext_trap) { + /* + * Check if the specific type is inhibited. Not taken branch filter is + * an enable bit and needs to be checked separatly. + */ + bool check = tgt_ctrl & BIT_ULL(type + XCTRCTL_INH_START); + if ((type == CTRDATA_TYPE_NONTAKEN_BRANCH && !check) || + (type != CTRDATA_TYPE_NONTAKEN_BRANCH && check)) { + return; + } + } + + head = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + + depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + if (tgt_ctrl & XCTRCTL_RASEMU && type == CTRDATA_TYPE_RETURN) { + head = (head - 1) & (depth - 1); + + env->ctr_src[head] &= ~CTRSOURCE_VALID; + env->sctrstatus = + set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head); + return; + } + + /* In case of Co-routine SWAP we overwrite latest entry. */ + if (tgt_ctrl & XCTRCTL_RASEMU && type == CTRDATA_TYPE_CO_ROUTINE_SWAP) { + head = (head - 1) & (depth - 1); + } + + env->ctr_src[head] = src | CTRSOURCE_VALID; + env->ctr_dst[head] = dst & ~CTRTARGET_MISP; + env->ctr_data[head] = set_field(0, CTRDATA_TYPE_MASK, type); + + head = (head + 1) & (depth - 1); + + env->sctrstatus = set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head); +} + void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) { g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); @@ -1993,10 +2234,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) !(env->mip & (1ULL << cause)); bool smode_double_trap = false; uint64_t hdeleg = async ? env->hideleg : env->hedeleg; + const bool prev_virt = env->virt_enabled; + const target_ulong prev_priv = env->priv; target_ulong tval = 0; target_ulong tinst = 0; target_ulong htval = 0; target_ulong mtval2 = 0; + target_ulong src; int sxlen = 0; int mxlen = 16 << riscv_cpu_mxl(env); bool nnmi_excep = false; @@ -2182,6 +2426,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->pc = (env->stvec >> 2 << 2) + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S, virt); + + src = env->sepc; } else { /* * If the hart encounters an exception while executing in M-mode @@ -2266,6 +2512,19 @@ void riscv_cpu_do_interrupt(CPUState *cs) ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); } riscv_cpu_set_mode(env, PRV_M, virt); + src = env->mepc; + } + + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) { + if (async && cause == IRQ_PMU_OVF) { + riscv_ctr_freeze(env, XCTRCTL_LCOFIFRZ, virt); + } else if (!async && cause == RISCV_EXCP_BREAKPOINT) { + riscv_ctr_freeze(env, XCTRCTL_BPFRZ, virt); + } + + riscv_ctr_add_entry(env, src, env->pc, + async ? CTRDATA_TYPE_INTERRUPT : CTRDATA_TYPE_EXCEPTION, + prev_priv, prev_virt); } /* diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 16ea240d26..163121ade5 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -136,6 +136,7 @@ DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wrs_nto, void, env) DEF_HELPER_1(tlb_flush, void, env) DEF_HELPER_1(tlb_flush_all, void, env) +DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl) /* Native Debug */ DEF_HELPER_1(itrigger_match, void, env) #endif diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 27d5558d63..ca52405d7d 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -86,6 +86,7 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) if (has_ext(ctx, RVS)) { decode_save_opc(ctx, 0); translator_io_start(&ctx->base); + gen_update_pc(ctx, 0); gen_helper_sret(cpu_pc, tcg_env); exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; @@ -103,6 +104,7 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) #ifndef CONFIG_USER_ONLY decode_save_opc(ctx, 0); translator_io_start(&ctx->base); + gen_update_pc(ctx, 0); gen_helper_mret(cpu_pc, tcg_env); exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 96c218a9d7..b55f56a5eb 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -93,6 +93,51 @@ static bool trans_jal(DisasContext *ctx, arg_jal *a) return true; } +#ifndef CONFIG_USER_ONLY +/* + * Indirect calls + * - jalr x1, rs where rs != x5; + * - jalr x5, rs where rs != x1; + * - c.jalr rs1 where rs1 != x5; + * + * Indirect jumps + * - jalr x0, rs where rs != x1 and rs != x5; + * - c.jr rs1 where rs1 != x1 and rs1 != x5. + * + * Returns + * - jalr rd, rs where (rs == x1 or rs == x5) and rd != x1 and rd != x5; + * - c.jr rs1 where rs1 == x1 or rs1 == x5. + * + * Co-routine swap + * - jalr x1, x5; + * - jalr x5, x1; + * - c.jalr x5. + * + * Other indirect jumps + * - jalr rd, rs where rs != x1, rs != x5, rd != x0, rd != x1 and rd != x5. + */ +static void gen_ctr_jalr(DisasContext *ctx, arg_jalr *a, TCGv dest) +{ + TCGv src = tcg_temp_new(); + TCGv type; + + if ((a->rd == 1 && a->rs1 != 5) || (a->rd == 5 && a->rs1 != 1)) { + type = tcg_constant_tl(CTRDATA_TYPE_INDIRECT_CALL); + } else if (a->rd == 0 && a->rs1 != 1 && a->rs1 != 5) { + type = tcg_constant_tl(CTRDATA_TYPE_INDIRECT_JUMP); + } else if ((a->rs1 == 1 || a->rs1 == 5) && (a->rd != 1 && a->rd != 5)) { + type = tcg_constant_tl(CTRDATA_TYPE_RETURN); + } else if ((a->rs1 == 1 && a->rd == 5) || (a->rs1 == 5 && a->rd == 1)) { + type = tcg_constant_tl(CTRDATA_TYPE_CO_ROUTINE_SWAP); + } else { + type = tcg_constant_tl(CTRDATA_TYPE_OTHER_INDIRECT_JUMP); + } + + gen_pc_plus_diff(src, ctx, 0); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); +} +#endif + static bool trans_jalr(DisasContext *ctx, arg_jalr *a) { TCGLabel *misaligned = NULL; @@ -117,6 +162,12 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); gen_set_gpr(ctx, a->rd, succ_pc); +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + gen_ctr_jalr(ctx, a, target_pc); + } +#endif + tcg_gen_mov_tl(cpu_pc, target_pc); if (ctx->fcfi_enabled) { /* @@ -231,6 +282,19 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) } else { tcg_gen_brcond_tl(cond, src1, src2, l); } + +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_NONTAKEN_BRANCH); + TCGv dest = tcg_temp_new(); + TCGv src = tcg_temp_new(); + + gen_pc_plus_diff(src, ctx, 0); + gen_pc_plus_diff(dest, ctx, ctx->cur_insn_len); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); + } +#endif + gen_goto_tb(ctx, 1, ctx->cur_insn_len); ctx->pc_save = orig_pc_save; @@ -243,6 +307,17 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) gen_pc_plus_diff(target_pc, ctx, a->imm); gen_exception_inst_addr_mis(ctx, target_pc); } else { +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_TAKEN_BRANCH); + TCGv dest = tcg_temp_new(); + TCGv src = tcg_temp_new(); + + gen_pc_plus_diff(src, ctx, 0); + gen_pc_plus_diff(dest, ctx, a->imm); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); + } +#endif gen_goto_tb(ctx, 0, a->imm); } ctx->pc_save = -1; diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index cd234ad960..c77c2b927b 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -203,6 +203,14 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) if (ret) { TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN); +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_RETURN); + TCGv src = tcg_temp_new(); + gen_pc_plus_diff(src, ctx, 0); + gen_helper_ctr_add_entry(tcg_env, src, ret_addr, type); + } +#endif tcg_gen_mov_tl(cpu_pc, ret_addr); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -309,6 +317,19 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) gen_set_gpr(ctx, xRA, succ_pc); } +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + if (a->index >= 32) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_CALL); + gen_helper_ctr_add_entry(tcg_env, cpu_pc, addr, type); + } else { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_JUMP); + gen_helper_ctr_add_entry(tcg_env, cpu_pc, addr, type); + } + } +#endif + + tcg_gen_mov_tl(cpu_pc, addr); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index ce1256f439..5a99c47b12 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -270,6 +270,8 @@ target_ulong helper_sret(CPURISCVState *env) { uint64_t mstatus; target_ulong prev_priv, prev_virt = env->virt_enabled; + const target_ulong src_priv = env->priv; + const bool src_virt = env->virt_enabled; if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); @@ -339,6 +341,11 @@ target_ulong helper_sret(CPURISCVState *env) } env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0); + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) { + riscv_ctr_add_entry(env, env->pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET, + src_priv, src_virt); + } + return retpc; } @@ -416,6 +423,11 @@ target_ulong helper_mret(CPURISCVState *env) } env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0); + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) { + riscv_ctr_add_entry(env, env->pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET, + PRV_M, false); + } + return retpc; } @@ -466,6 +478,13 @@ target_ulong helper_mnret(CPURISCVState *env) return retpc; } +void helper_ctr_add_entry(CPURISCVState *env, target_ulong src, + target_ulong dest, target_ulong type) +{ + riscv_ctr_add_entry(env, src, dest, (enum CTRType)type, + env->priv, env->virt_enabled); +} + void helper_wfi(CPURISCVState *env) { CPUState *cs = env_cpu(env); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 698b74f7a8..eaa5d86eae 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -561,6 +561,46 @@ static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) } } +#ifndef CONFIG_USER_ONLY +/* + * Direct calls + * - jal x1; + * - jal x5; + * - c.jal. + * - cm.jalt. + * + * Direct jumps + * - jal x0; + * - c.j; + * - cm.jt. + * + * Other direct jumps + * - jal rd where rd != x1 and rd != x5 and rd != x0; + */ +static void gen_ctr_jal(DisasContext *ctx, int rd, target_ulong imm) +{ + TCGv dest = tcg_temp_new(); + TCGv src = tcg_temp_new(); + TCGv type; + + /* + * If rd is x1 or x5 link registers, treat this as direct call otherwise + * its a direct jump. + */ + if (rd == 1 || rd == 5) { + type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_CALL); + } else if (rd == 0) { + type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_JUMP); + } else { + type = tcg_constant_tl(CTRDATA_TYPE_OTHER_DIRECT_JUMP); + } + + gen_pc_plus_diff(dest, ctx, imm); + gen_pc_plus_diff(src, ctx, 0); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); +} +#endif + static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { TCGv succ_pc = dest_gpr(ctx, rd); @@ -575,6 +615,12 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) } } +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + gen_ctr_jal(ctx, rd, imm); + } +#endif + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); gen_set_gpr(ctx, rd, succ_pc); From 9e69e760fdc34a9d13a8c434d6a9fada835a05ad Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 5 Feb 2025 11:18:49 +0000 Subject: [PATCH 2034/2892] target/riscv: Add CTR sctrclr instruction. CTR extension adds a new instruction sctrclr to quickly clear the recorded entries buffer. Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Message-ID: <20250205-b4-ctr_upstream_v6-v6-5-439d8e06c8ef@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 7 +++++ target/riscv/helper.h | 1 + target/riscv/insn32.decode | 1 + .../riscv/insn_trans/trans_privileged.c.inc | 11 +++++++ target/riscv/op_helper.c | 29 +++++++++++++++++++ 6 files changed, 50 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 9e92144b61..616c3bdc1c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -619,6 +619,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, enum CTRType type, target_ulong prev_priv, bool prev_virt); +void riscv_ctr_clear(CPURISCVState *env); void riscv_translate_init(void); void riscv_translate_code(CPUState *cs, TranslationBlock *tb, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 7dbdb34b17..356e84b9a2 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -887,6 +887,13 @@ static void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, } } +void riscv_ctr_clear(CPURISCVState *env) +{ + memset(env->ctr_src, 0x0, sizeof(env->ctr_src)); + memset(env->ctr_dst, 0x0, sizeof(env->ctr_dst)); + memset(env->ctr_data, 0x0, sizeof(env->ctr_data)); +} + static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt) { switch (priv) { diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 163121ade5..85d73e492d 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -132,6 +132,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl) DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) DEF_HELPER_1(mnret, tl, env) +DEF_HELPER_1(ctr_clear, void, env) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wrs_nto, void, env) DEF_HELPER_1(tlb_flush, void, env) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index a98dab0205..6d1a13c826 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -114,6 +114,7 @@ # *** Privileged Instructions *** ecall 000000000000 00000 000 00000 1110011 ebreak 000000000001 00000 000 00000 1110011 +sctrclr 000100000100 00000 000 00000 1110011 uret 0000000 00010 00000 000 00000 1110011 sret 0001000 00010 00000 000 00000 1110011 mret 0011000 00010 00000 000 00000 1110011 diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index ca52405d7d..8a62b4cfcd 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -75,6 +75,17 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) return true; } +static bool trans_sctrclr(DisasContext *ctx, arg_sctrclr *a) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + gen_helper_ctr_clear(tcg_env); + return true; + } +#endif + return false; +} + static bool trans_uret(DisasContext *ctx, arg_uret *a) { return false; diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 5a99c47b12..f156bfab12 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -485,6 +485,35 @@ void helper_ctr_add_entry(CPURISCVState *env, target_ulong src, env->priv, env->virt_enabled); } +void helper_ctr_clear(CPURISCVState *env) +{ + /* + * It's safe to call smstateen_acc_ok() for umode access regardless of the + * state of bit 54 (CTR bit in case of m/hstateen) of sstateen. If the bit + * is zero, smstateen_acc_ok() will return the correct exception code and + * if it's one, smstateen_acc_ok() will return RISCV_EXCP_NONE. In that + * scenario the U-mode check below will handle that case. + */ + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR); + if (ret != RISCV_EXCP_NONE) { + riscv_raise_exception(env, ret, GETPC()); + } + + if (env->priv == PRV_U) { + /* + * One corner case is when sctrclr is executed from VU-mode and + * mstateen.CTR = 0, in which case we are supposed to raise + * RISCV_EXCP_ILLEGAL_INST. This case is already handled in + * smstateen_acc_ok(). + */ + uint32_t excep = env->virt_enabled ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : + RISCV_EXCP_ILLEGAL_INST; + riscv_raise_exception(env, excep, GETPC()); + } + + riscv_ctr_clear(env); +} + void helper_wfi(CPURISCVState *env) { CPUState *cs = env_cpu(env); From bda6522e3f9002040fc223c12457b849328a1d39 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 5 Feb 2025 11:18:50 +0000 Subject: [PATCH 2035/2892] target/riscv: machine: Add Control Transfer Record state description Add a subsection to machine.c to migrate CTR CSR state Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Message-ID: <20250205-b4-ctr_upstream_v6-v6-6-439d8e06c8ef@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/machine.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/target/riscv/machine.c b/target/riscv/machine.c index d8445244ab..889e2b6570 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -300,6 +300,30 @@ static const VMStateDescription vmstate_envcfg = { } }; +static bool ctr_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr; +} + +static const VMStateDescription vmstate_ctr = { + .name = "cpu/ctr", + .version_id = 1, + .minimum_version_id = 1, + .needed = ctr_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(env.mctrctl, RISCVCPU), + VMSTATE_UINT32(env.sctrdepth, RISCVCPU), + VMSTATE_UINT32(env.sctrstatus, RISCVCPU), + VMSTATE_UINT64(env.vsctrctl, RISCVCPU), + VMSTATE_UINT64_ARRAY(env.ctr_src, RISCVCPU, 16 << SCTRDEPTH_MAX), + VMSTATE_UINT64_ARRAY(env.ctr_dst, RISCVCPU, 16 << SCTRDEPTH_MAX), + VMSTATE_UINT64_ARRAY(env.ctr_data, RISCVCPU, 16 << SCTRDEPTH_MAX), + VMSTATE_END_OF_LIST() + } +}; + static bool pmu_needed(void *opaque) { RISCVCPU *cpu = opaque; @@ -450,6 +474,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_jvt, &vmstate_elp, &vmstate_ssp, + &vmstate_ctr, NULL } }; From 50df464f8e9ddcc5242c058728e12c299c59db02 Mon Sep 17 00:00:00 2001 From: julia Date: Mon, 3 Feb 2025 17:18:52 +1100 Subject: [PATCH 2036/2892] target/riscv: log guest errors when reserved bits are set in PTEs For instance, QEMUs newer than b6ecc63c569bb88c0fcadf79fb92bf4b88aefea8 would silently treat this akin to an unmapped page (as required by the RISC-V spec, admittedly). However, not all hardware platforms do (e.g. CVA6) which leads to an apparent QEMU bug. Instead, log a guest error so that in future, incorrectly set up page tables can be debugged without bisecting QEMU. Signed-off-by: julia Reviewed-by: Daniel Henrique Barboza Message-ID: <20250203061852.2931556-1-midnight@trainwit.ch> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 356e84b9a2..3f5fd861a8 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1472,14 +1472,27 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, ppn = pte >> PTE_PPN_SHIFT; } else { if (pte & PTE_RESERVED) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits set in PTE: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } if (!pbmte && (pte & PTE_PBMT)) { + /* Reserved without Svpbmt. */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: PBMT bits set in PTE, " + "and Svpbmt extension is disabled: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } if (!riscv_cpu_cfg(env)->ext_svnapot && (pte & PTE_N)) { + /* Reserved without Svnapot extension */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: N bit set in PTE, " + "and Svnapot extension is disabled: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } @@ -1490,14 +1503,19 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, /* Invalid PTE */ return TRANSLATE_FAIL; } + if (pte & (PTE_R | PTE_W | PTE_X)) { goto leaf; } - /* Inner PTE, continue walking */ if (pte & (PTE_D | PTE_A | PTE_U | PTE_ATTR)) { + /* D, A, and U bits are reserved in non-leaf/inner PTEs */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: D, A, or U bits set in non-leaf PTE: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } + /* Inner PTE, continue walking */ base = ppn << PGSHIFT; } @@ -1507,10 +1525,17 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, leaf: if (ppn & ((1ULL << ptshift) - 1)) { /* Misaligned PPN */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: PPN bits in PTE is misaligned: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } if (!pbmte && (pte & PTE_PBMT)) { /* Reserved without Svpbmt. */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: PBMT bits set in PTE, " + "and Svpbmt extension is disabled: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } From d30db4df5187e2b91e589ffceace59a0ae2bc30e Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Thu, 6 Feb 2025 15:34:09 +0000 Subject: [PATCH 2037/2892] disas/riscv: Fix minor whitespace issues Some extra spaces made into into the RISC-V opcode data table. Signed-off-by: Rob Bradford Reviewed-by: Alistair Francis Message-ID: <20250206153410.236636-2-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 4075ed6bfe..305dd40ac4 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1662,7 +1662,7 @@ const rv_opcode_data rvi_opcode_data[] = { { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, + { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "aes64im", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, @@ -2214,11 +2214,11 @@ const rv_opcode_data rvi_opcode_data[] = { { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "c.mop.1", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.3", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.5", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.7", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.9", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.1", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.3", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.5", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.7", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.9", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, { "c.mop.11", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, { "c.mop.13", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, { "c.mop.15", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, From 81819038d7d01c6c8c12005b5904356efc09a909 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Thu, 6 Feb 2025 15:34:10 +0000 Subject: [PATCH 2038/2892] disas/riscv: Add missing Sdtrig CSRs This reflects the latest frozen version of the RISC-V Debug specification (1.0.0-rc4) which includes the Sdtrig extension. Signed-off-by: Rob Bradford Acked-by: Alistair Francis Message-ID: <20250206153410.236636-3-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index 305dd40ac4..85cd2a9c2a 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2438,9 +2438,11 @@ static const char *csr_name(int csrno) case 0x07a1: return "tdata1"; case 0x07a2: return "tdata2"; case 0x07a3: return "tdata3"; + case 0x07a4: return "tinfo"; case 0x07b0: return "dcsr"; case 0x07b1: return "dpc"; - case 0x07b2: return "dscratch"; + case 0x07b2: return "dscratch0"; + case 0x07b3: return "dscratch1"; case 0x0b00: return "mcycle"; case 0x0b01: return "mtime"; case 0x0b02: return "minstret"; From 59eaf1570456b701fe6dfa4a8f747e65633c385f Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Thu, 6 Feb 2025 01:58:46 -0800 Subject: [PATCH 2039/2892] target/riscv: Fix the hpmevent mask As per the latest privilege specification v1.13[1], the sscofpmf only reserves first 8 bits of hpmeventX. Update the corresponding masks accordingly. [1]https://github.com/riscv/riscv-isa-manual/issues/1578 Reviewed-by: Daniel Henrique Barboza Signed-off-by: Atish Patra Acked-by: Alistair Francis Message-ID: <20250206-pmu_minor_fixes-v2-1-1bb0f4aeb8b4@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 70ef443c99..a30317c617 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -1078,9 +1078,8 @@ typedef enum CTRType { MHPMEVENTH_BIT_VSINH | \ MHPMEVENTH_BIT_VUINH) -#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) -#define MHPMEVENT_IDX_MASK 0xFFFFF -#define MHPMEVENT_SSCOF_RESVD 16 +#define MHPMEVENT_SSCOF_MASK MAKE_64BIT_MASK(63, 56) +#define MHPMEVENT_IDX_MASK (~MHPMEVENT_SSCOF_MASK) /* RISC-V-specific interrupt pending bits. */ #define CPU_INTERRUPT_RNMI CPU_INTERRUPT_TGT_EXT_0 From abe9b81ee41b607eab1928f337837a19acae3208 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Thu, 6 Feb 2025 01:58:47 -0800 Subject: [PATCH 2040/2892] target/riscv: Mask out upper sscofpmf bits during validation As per the ISA definition, the upper 8 bits in hpmevent are defined by Sscofpmf for privilege mode filtering and overflow bits while the lower 56 bits are desginated for platform specific hpmevent values. For the reset case, mhpmevent value should have zero in lower 56 bits. Software may set the OF bit to indicate disable interrupt. Ensure that correct value is checked after masking while clearing the event encodings. Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Atish Patra Message-ID: <20250206-pmu_minor_fixes-v2-2-1bb0f4aeb8b4@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index cf713663ee..0408f96e6a 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -390,7 +390,7 @@ int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, * Expected mhpmevent value is zero for reset case. Remove the current * mapping. */ - if (!value) { + if (!(value & MHPMEVENT_IDX_MASK)) { g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, pmu_remove_event_map, GUINT_TO_POINTER(ctr_idx)); From cb0c4760263d418ea47afa9e6d88944e96e128f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Thu, 13 Feb 2025 15:56:31 +0100 Subject: [PATCH 2041/2892] target/riscv: remove warnings about Smdbltrp/Smrnmi being disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As raised by Richard Henderson, these warnings are displayed in user only as well. Since they aren't really useful for the end-user, remove them and add a "TODO" note in the leading comments. Signed-off-by: Clément Léger Reviewed-by: Daniel Henrique Barboza Message-ID: <20250213145640.117275-1-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index cb9b504012..53c9998553 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1458,22 +1458,20 @@ static void riscv_init_max_cpu_extensions(Object *obj) } /* - * ext_smrnmi requires OpenSBI changes that our current + * TODO: ext_smrnmi requires OpenSBI changes that our current * image does not have. Disable it for now. */ if (cpu->cfg.ext_smrnmi) { isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smrnmi), false); - qemu_log("Smrnmi is disabled in the 'max' type CPU\n"); } /* - * ext_smdbltrp requires the firmware to clear MSTATUS.MDT on startup to - * avoid generating a double trap. OpenSBI does not currently support it, + * TODO: ext_smdbltrp requires the firmware to clear MSTATUS.MDT on startup + * to avoid generating a double trap. OpenSBI does not currently support it, * disable it for now. */ if (cpu->cfg.ext_smdbltrp) { isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smdbltrp), false); - qemu_log("Smdbltrp is disabled in the 'max' type CPU\n"); } } From 8b65852196650417532ff924c8a2cb0117e2be19 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Wed, 12 Feb 2025 10:18:49 +0000 Subject: [PATCH 2042/2892] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs. CTR entries are accessed using ctrsource, ctrtarget and ctrdata registers using smcsrind/sscsrind extension. This commits extends the csrind extension to support CTR registers. ctrsource is accessible through xireg CSR, ctrtarget is accessible through xireg1 and ctrdata is accessible through xireg2 CSR. CTR supports maximum depth of 256 entries which are accessed using xiselect range 0x200 to 0x2ff. This commits also adds properties to enable CTR extension. CTR can be enabled using smctr=true and ssctr=true now. Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Message-ID: <20250212-b4-ctr_upstream_v6-v7-1-4e8159ea33bf@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 26 ++++++- target/riscv/csr.c | 150 ++++++++++++++++++++++++++++++++++++- target/riscv/tcg/tcg-cpu.c | 11 +++ 3 files changed, 185 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3624ffb6d9..a4ee381a07 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -216,6 +216,8 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(ssu64xl, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm), ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), + ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr), + ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr), ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu), ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot), @@ -1592,6 +1594,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false), MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false), MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false), + MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false), + MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false), MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), MULTI_EXT_CFG_BOOL("zicfilp", ext_zicfilp, false), MULTI_EXT_CFG_BOOL("zicfiss", ext_zicfiss, false), @@ -2856,6 +2860,26 @@ static RISCVCPUImpliedExtsRule SSPM_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule SMCTR_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_smctr), + .implied_misa_exts = RVS, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_sscsrind), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule SSCTR_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_ssctr), + .implied_misa_exts = RVS, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_sscsrind), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, &RVM_IMPLIED, &RVV_IMPLIED, NULL @@ -2874,7 +2898,7 @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { &ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED, &ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED, &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, &SSCFG_IMPLIED, - &SUPM_IMPLIED, &SSPM_IMPLIED, + &SUPM_IMPLIED, &SSPM_IMPLIED, &SMCTR_IMPLIED, &SSCTR_IMPLIED, NULL }; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index ab295d2ef3..0ebcca4597 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2427,6 +2427,13 @@ static bool xiselect_cd_range(target_ulong isel) return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST); } +static bool xiselect_ctr_range(int csrno, target_ulong isel) +{ + /* MIREG-MIREG6 for the range 0x200-0x2ff are not used by CTR. */ + return CTR_ENTRIES_FIRST <= isel && isel <= CTR_ENTRIES_LAST && + csrno < CSR_MIREG; +} + static int rmw_iprio(target_ulong xlen, target_ulong iselect, uint8_t *iprio, target_ulong *val, target_ulong new_val, @@ -2472,6 +2479,124 @@ static int rmw_iprio(target_ulong xlen, return 0; } +static int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + /* + * CTR arrays are treated as circular buffers and TOS always points to next + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry + * 0 is always the latest one, traversal is a bit different here. See the + * below example. + * + * Depth = 16. + * + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * TOS H + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7 + */ + const uint64_t entry = isel - CTR_ENTRIES_FIRST; + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint64_t idx; + + /* Entry greater than depth-1 is read-only zero */ + if (entry >= depth) { + if (val) { + *val = 0; + } + return 0; + } + + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + idx = (idx - entry - 1) & (depth - 1); + + if (val) { + *val = env->ctr_src[idx]; + } + + env->ctr_src[idx] = (env->ctr_src[idx] & ~wr_mask) | (new_val & wr_mask); + + return 0; +} + +static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + /* + * CTR arrays are treated as circular buffers and TOS always points to next + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry + * 0 is always the latest one, traversal is a bit different here. See the + * below example. + * + * Depth = 16. + * + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * head H + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7 + */ + const uint64_t entry = isel - CTR_ENTRIES_FIRST; + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint64_t idx; + + /* Entry greater than depth-1 is read-only zero */ + if (entry >= depth) { + if (val) { + *val = 0; + } + return 0; + } + + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + idx = (idx - entry - 1) & (depth - 1); + + if (val) { + *val = env->ctr_dst[idx]; + } + + env->ctr_dst[idx] = (env->ctr_dst[idx] & ~wr_mask) | (new_val & wr_mask); + + return 0; +} + +static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + /* + * CTR arrays are treated as circular buffers and TOS always points to next + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry + * 0 is always the latest one, traversal is a bit different here. See the + * below example. + * + * Depth = 16. + * + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * head H + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7 + */ + const uint64_t entry = isel - CTR_ENTRIES_FIRST; + const uint64_t mask = wr_mask & CTRDATA_MASK; + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint64_t idx; + + /* Entry greater than depth-1 is read-only zero */ + if (entry >= depth) { + if (val) { + *val = 0; + } + return 0; + } + + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + idx = (idx - entry - 1) & (depth - 1); + + if (val) { + *val = env->ctr_data[idx]; + } + + env->ctr_data[idx] = (env->ctr_data[idx] & ~mask) | (new_val & mask); + + return 0; +} + static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, target_ulong isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) @@ -2624,6 +2749,27 @@ done: return ret; } +static int rmw_xireg_ctr(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + if (!riscv_cpu_cfg(env)->ext_smctr && !riscv_cpu_cfg(env)->ext_ssctr) { + return -EINVAL; + } + + if (csrno == CSR_SIREG || csrno == CSR_VSIREG) { + return rmw_ctrsource(env, isel, val, new_val, wr_mask); + } else if (csrno == CSR_SIREG2 || csrno == CSR_VSIREG2) { + return rmw_ctrtarget(env, isel, val, new_val, wr_mask); + } else if (csrno == CSR_SIREG3 || csrno == CSR_VSIREG3) { + return rmw_ctrdata(env, isel, val, new_val, wr_mask); + } else if (val) { + *val = 0; + } + + return 0; +} + /* * rmw_xireg_csrind: Perform indirect access to xireg and xireg2-xireg6 * @@ -2635,11 +2781,13 @@ static int rmw_xireg_csrind(CPURISCVState *env, int csrno, target_ulong isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - int ret = -EINVAL; bool virt = csrno == CSR_VSIREG ? true : false; + int ret = -EINVAL; if (xiselect_cd_range(isel)) { ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask); + } else if (xiselect_ctr_range(csrno, isel)) { + ret = rmw_xireg_ctr(env, csrno, isel, val, new_val, wr_mask); } else { /* * As per the specification, access to unimplented region is undefined diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 53c9998553..929ed5fd2c 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -681,6 +681,17 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if ((cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr) && + (!riscv_has_ext(env, RVS) || !cpu->cfg.ext_sscsrind)) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_smctr)) || + cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ssctr))) { + error_setg(errp, "Smctr and Ssctr require S-mode and Sscsrind"); + return; + } + cpu->cfg.ext_smctr = false; + cpu->cfg.ext_ssctr = false; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. From 2c1b42144018ec5ab097e003b974e3a094d73b2f Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 10 Feb 2025 15:37:13 +0000 Subject: [PATCH 2043/2892] target/riscv: Respect mseccfg.RLB bit for TOR mode PMP entry When running in TOR mode (Top of Range) the next PMP entry controls whether the entry is locked. However simply checking if the PMP_LOCK bit is set is not sufficient with the Smepmp extension which now provides a bit (mseccfg.RLB (Rule Lock Bypass)) to disregard the lock bits. In order to respect this bit use the convenience pmp_is_locked() function rather than directly checking PMP_LOCK since this function checks mseccfg.RLB. Signed-off-by: Rob Bradford Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20250210153713.343626-1-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index a185c246d6..85ab270dad 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -524,7 +524,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); - if (pmp_cfg & PMP_LOCK && is_next_cfg_tor) { + if (pmp_is_locked(env, addr_index + 1) && is_next_cfg_tor) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - pmpcfg + 1 locked\n"); return; From 421ee1ec6f0de0b0fd96b262bda18b97e54263b4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 21 Feb 2025 12:37:56 -0300 Subject: [PATCH 2044/2892] linux-headers: Update to Linux v6.14-rc3 Update headers to retrieve the latest KVM caps for RISC-V. Signed-off-by: Daniel Henrique Barboza Message-ID: <20250221153758.652078-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- include/standard-headers/linux/ethtool.h | 4 + include/standard-headers/linux/fuse.h | 76 ++++++++++++++++++- .../linux/input-event-codes.h | 1 + include/standard-headers/linux/pci_regs.h | 16 ++-- include/standard-headers/linux/virtio_pci.h | 14 ++++ linux-headers/asm-arm64/kvm.h | 3 - linux-headers/asm-loongarch/kvm_para.h | 1 + linux-headers/asm-riscv/kvm.h | 7 +- linux-headers/asm-x86/kvm.h | 1 + linux-headers/linux/iommufd.h | 35 ++++++--- linux-headers/linux/kvm.h | 8 +- linux-headers/linux/stddef.h | 13 +++- linux-headers/linux/vduse.h | 2 +- 13 files changed, 146 insertions(+), 35 deletions(-) diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 67c47912e5..e83382531c 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -681,6 +681,8 @@ enum ethtool_link_ext_substate_module { * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics * @ETH_SS_STATS_RMON: names of RMON statistics + * @ETH_SS_STATS_PHY: names of PHY(dev) statistics + * @ETH_SS_TS_FLAGS: hardware timestamping flags * * @ETH_SS_COUNT: number of defined string sets */ @@ -706,6 +708,8 @@ enum ethtool_stringset { ETH_SS_STATS_ETH_MAC, ETH_SS_STATS_ETH_CTRL, ETH_SS_STATS_RMON, + ETH_SS_STATS_PHY, + ETH_SS_TS_FLAGS, /* add new constants above here */ ETH_SS_COUNT diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h index 889e12ad15..d303effb2a 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -220,6 +220,15 @@ * * 7.41 * - add FUSE_ALLOW_IDMAP + * 7.42 + * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data + * structures: + * - struct fuse_uring_ent_in_out + * - struct fuse_uring_req_header + * - struct fuse_uring_cmd_req + * - FUSE_URING_IN_OUT_HEADER_SZ + * - FUSE_URING_OP_IN_OUT_SZ + * - enum fuse_uring_cmd */ #ifndef _LINUX_FUSE_H @@ -251,7 +260,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 41 +#define FUSE_KERNEL_MINOR_VERSION 42 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -421,6 +430,7 @@ struct fuse_file_lock { * FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit * of the request ID indicates resend requests * FUSE_ALLOW_IDMAP: allow creation of idmapped mounts + * FUSE_OVER_IO_URING: Indicate that client supports io-uring */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -467,6 +477,7 @@ struct fuse_file_lock { /* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ #define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP #define FUSE_ALLOW_IDMAP (1ULL << 40) +#define FUSE_OVER_IO_URING (1ULL << 41) /** * CUSE INIT request/reply flags @@ -1202,4 +1213,67 @@ struct fuse_supp_groups { uint32_t groups[]; }; +/** + * Size of the ring buffer header + */ +#define FUSE_URING_IN_OUT_HEADER_SZ 128 +#define FUSE_URING_OP_IN_OUT_SZ 128 + +/* Used as part of the fuse_uring_req_header */ +struct fuse_uring_ent_in_out { + uint64_t flags; + + /* + * commit ID to be used in a reply to a ring request (see also + * struct fuse_uring_cmd_req) + */ + uint64_t commit_id; + + /* size of user payload buffer */ + uint32_t payload_sz; + uint32_t padding; + + uint64_t reserved; +}; + +/** + * Header for all fuse-io-uring requests + */ +struct fuse_uring_req_header { + /* struct fuse_in_header / struct fuse_out_header */ + char in_out[FUSE_URING_IN_OUT_HEADER_SZ]; + + /* per op code header */ + char op_in[FUSE_URING_OP_IN_OUT_SZ]; + + struct fuse_uring_ent_in_out ring_ent_in_out; +}; + +/** + * sqe commands to the kernel + */ +enum fuse_uring_cmd { + FUSE_IO_URING_CMD_INVALID = 0, + + /* register the request buffer and fetch a fuse request */ + FUSE_IO_URING_CMD_REGISTER = 1, + + /* commit fuse request result and fetch next request */ + FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2, +}; + +/** + * In the 80B command area of the SQE. + */ +struct fuse_uring_cmd_req { + uint64_t flags; + + /* entry identifier for commits */ + uint64_t commit_id; + + /* queue the command is for (queue index) */ + uint16_t qid; + uint8_t padding[6]; +}; + #endif /* _LINUX_FUSE_H */ diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index 50b2b7497e..09ba0ad878 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -519,6 +519,7 @@ #define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */ #define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */ #define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */ +#define KEY_LINK_PHONE 0x1bf /* AL Phone Syncing */ #define KEY_DEL_EOL 0x1c0 #define KEY_DEL_EOS 0x1c1 diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 1601c7ed5f..3445c4970e 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -533,7 +533,7 @@ #define PCI_EXP_DEVSTA_TRPND 0x0020 /* Transactions Pending */ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V1 12 /* v1 endpoints without link end here */ #define PCI_EXP_LNKCAP 0x0c /* Link Capabilities */ -#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Max Link Speed (prior to PCIe r3.0: Supported Link Speeds) */ #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ @@ -665,6 +665,7 @@ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCAP2_EE_PREFIX 0x00200000 /* End-End TLP Prefix */ +#define PCI_EXP_DEVCAP2_EE_PREFIX_MAX 0x00c00000 /* Max End-End TLP Prefixes */ #define PCI_EXP_DEVCTL2 0x28 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ #define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */ @@ -789,10 +790,11 @@ /* Same bits as above */ #define PCI_ERR_CAP 0x18 /* Advanced Error Capabilities & Ctrl*/ #define PCI_ERR_CAP_FEP(x) ((x) & 0x1f) /* First Error Pointer */ -#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ -#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ -#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ -#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_CAP_PREFIX_LOG_PRESENT 0x00000800 /* TLP Prefix Log Present */ #define PCI_ERR_HEADER_LOG 0x1c /* Header Log Register (16 bytes) */ #define PCI_ERR_ROOT_COMMAND 0x2c /* Root Error Command */ #define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Err Reporting Enable */ @@ -808,6 +810,7 @@ #define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ #define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */ #define PCI_ERR_ROOT_ERR_SRC 0x34 /* Error Source Identification */ +#define PCI_ERR_PREFIX_LOG 0x38 /* TLP Prefix LOG Register (up to 16 bytes) */ /* Virtual Channel */ #define PCI_VC_PORT_CAP1 0x04 @@ -1001,9 +1004,6 @@ #define PCI_ACS_CTRL 0x06 /* ACS Control Register */ #define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ -#define PCI_VSEC_HDR 4 /* extended cap - vendor-specific */ -#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */ - /* SATA capability */ #define PCI_SATA_REGS 4 /* SATA REGs specifier */ #define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */ diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h index b177ed8972..91fec6f502 100644 --- a/include/standard-headers/linux/virtio_pci.h +++ b/include/standard-headers/linux/virtio_pci.h @@ -116,6 +116,8 @@ #define VIRTIO_PCI_CAP_PCI_CFG 5 /* Additional shared memory capability */ #define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 +/* PCI vendor data configuration */ +#define VIRTIO_PCI_CAP_VENDOR_CFG 9 /* This is the PCI capability header: */ struct virtio_pci_cap { @@ -130,6 +132,18 @@ struct virtio_pci_cap { uint32_t length; /* Length of the structure, in bytes. */ }; +/* This is the PCI vendor data capability header: */ +struct virtio_pci_vndr_data { + uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + uint8_t cap_next; /* Generic PCI field: next ptr. */ + uint8_t cap_len; /* Generic PCI field: capability length */ + uint8_t cfg_type; /* Identifies the structure. */ + uint16_t vendor_id; /* Identifies the vendor-specific format. */ + /* For Vendor Definition */ + /* Pads structure to a multiple of 4 bytes */ + /* Reads must not have side effects */ +}; + struct virtio_pci_cap64 { struct virtio_pci_cap cap; uint32_t offset_hi; /* Most sig 32 bits of offset */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index dccd5d965f..ec1e82bdc8 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -43,9 +43,6 @@ #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 #define KVM_DIRTY_LOG_PAGE_OFFSET 64 -#define KVM_REG_SIZE(id) \ - (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) - struct kvm_regs { struct user_pt_regs regs; /* sp = sp_el0 */ diff --git a/linux-headers/asm-loongarch/kvm_para.h b/linux-headers/asm-loongarch/kvm_para.h index 4ba4ad8db1..fd7f40713d 100644 --- a/linux-headers/asm-loongarch/kvm_para.h +++ b/linux-headers/asm-loongarch/kvm_para.h @@ -17,5 +17,6 @@ #define KVM_FEATURE_STEAL_TIME 2 /* BIT 24 - 31 are features configurable by user space vmm */ #define KVM_FEATURE_VIRT_EXTIOI 24 +#define KVM_FEATURE_USER_HCALL 25 #endif /* _ASM_KVM_PARA_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index 3482c9a73d..f06bc5efcd 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -179,6 +179,9 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_SSNPM, KVM_RISCV_ISA_EXT_SVADE, KVM_RISCV_ISA_EXT_SVADU, + KVM_RISCV_ISA_EXT_SVVPTC, + KVM_RISCV_ISA_EXT_ZABHA, + KVM_RISCV_ISA_EXT_ZICCRSE, KVM_RISCV_ISA_EXT_MAX, }; @@ -198,6 +201,7 @@ enum KVM_RISCV_SBI_EXT_ID { KVM_RISCV_SBI_EXT_VENDOR, KVM_RISCV_SBI_EXT_DBCN, KVM_RISCV_SBI_EXT_STA, + KVM_RISCV_SBI_EXT_SUSP, KVM_RISCV_SBI_EXT_MAX, }; @@ -211,9 +215,6 @@ struct kvm_riscv_sbi_sta { #define KVM_RISCV_TIMER_STATE_OFF 0 #define KVM_RISCV_TIMER_STATE_ON 1 -#define KVM_REG_SIZE(id) \ - (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) - /* If you need to interpret the index values, here is the key: */ #define KVM_REG_RISCV_TYPE_MASK 0x00000000FF000000 #define KVM_REG_RISCV_TYPE_SHIFT 24 diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 96589490c4..86f2c34e7a 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -923,5 +923,6 @@ struct kvm_hyperv_eventfd { #define KVM_X86_SEV_VM 2 #define KVM_X86_SEV_ES_VM 3 #define KVM_X86_SNP_VM 4 +#define KVM_X86_TDX_VM 5 #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h index 37aae16502..ccbdca5e11 100644 --- a/linux-headers/linux/iommufd.h +++ b/linux-headers/linux/iommufd.h @@ -297,7 +297,7 @@ struct iommu_ioas_unmap { * ioctl(IOMMU_OPTION_HUGE_PAGES) * @IOMMU_OPTION_RLIMIT_MODE: * Change how RLIMIT_MEMLOCK accounting works. The caller must have privilege - * to invoke this. Value 0 (default) is user based accouting, 1 uses process + * to invoke this. Value 0 (default) is user based accounting, 1 uses process * based accounting. Global option, object_id must be 0 * @IOMMU_OPTION_HUGE_PAGES: * Value 1 (default) allows contiguous pages to be combined when generating @@ -390,7 +390,7 @@ struct iommu_vfio_ioas { * @IOMMU_HWPT_ALLOC_PASID: Requests a domain that can be used with PASID. The * domain can be attached to any PASID on the device. * Any domain attached to the non-PASID part of the - * device must also be flaged, otherwise attaching a + * device must also be flagged, otherwise attaching a * PASID will blocked. * If IOMMU does not support PASID it will return * error (-EOPNOTSUPP). @@ -558,16 +558,25 @@ struct iommu_hw_info_vtd { * For the details of @idr, @iidr and @aidr, please refer to the chapters * from 6.3.1 to 6.3.6 in the SMMUv3 Spec. * - * User space should read the underlying ARM SMMUv3 hardware information for - * the list of supported features. + * This reports the raw HW capability, and not all bits are meaningful to be + * read by userspace. Only the following fields should be used: * - * Note that these values reflect the raw HW capability, without any insight if - * any required kernel driver support is present. Bits may be set indicating the - * HW has functionality that is lacking kernel software support, such as BTM. If - * a VMM is using this information to construct emulated copies of these - * registers it should only forward bits that it knows it can support. + * idr[0]: ST_LEVEL, TERM_MODEL, STALL_MODEL, TTENDIAN , CD2L, ASID16, TTF + * idr[1]: SIDSIZE, SSIDSIZE + * idr[3]: BBML, RIL + * idr[5]: VAX, GRAN64K, GRAN16K, GRAN4K * - * In future, presence of required kernel support will be indicated in flags. + * - S1P should be assumed to be true if a NESTED HWPT can be created + * - VFIO/iommufd only support platforms with COHACC, it should be assumed to be + * true. + * - ATS is a per-device property. If the VMM describes any devices as ATS + * capable in ACPI/DT it should set the corresponding idr. + * + * This list may expand in future (eg E0PD, AIE, PBHA, D128, DS etc). It is + * important that VMMs do not read bits outside the list to allow for + * compatibility with future kernels. Several features in the SMMUv3 + * architecture are not currently supported by the kernel for nesting: HTTU, + * BTM, MPAM and others. */ struct iommu_hw_info_arm_smmuv3 { __u32 flags; @@ -766,7 +775,7 @@ struct iommu_hwpt_vtd_s1_invalidate { }; /** - * struct iommu_viommu_arm_smmuv3_invalidate - ARM SMMUv3 cahce invalidation + * struct iommu_viommu_arm_smmuv3_invalidate - ARM SMMUv3 cache invalidation * (IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3) * @cmd: 128-bit cache invalidation command that runs in SMMU CMDQ. * Must be little-endian. @@ -859,6 +868,7 @@ enum iommu_hwpt_pgfault_perm { * @pasid: Process Address Space ID * @grpid: Page Request Group Index * @perm: Combination of enum iommu_hwpt_pgfault_perm + * @__reserved: Must be 0. * @addr: Fault address * @length: a hint of how much data the requestor is expecting to fetch. For * example, if the PRI initiator knows it is going to do a 10MB @@ -874,7 +884,8 @@ struct iommu_hwpt_pgfault { __u32 pasid; __u32 grpid; __u32 perm; - __u64 addr; + __u32 __reserved; + __aligned_u64 addr; __u32 length; __u32 cookie; }; diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 3bcd4eabe3..27181b3dd8 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -609,10 +609,6 @@ struct kvm_ioeventfd { #define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3) -#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \ - KVM_X86_DISABLE_EXITS_HLT | \ - KVM_X86_DISABLE_EXITS_PAUSE | \ - KVM_X86_DISABLE_EXITS_CSTATE) /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { @@ -1062,6 +1058,10 @@ struct kvm_dirty_tlb { #define KVM_REG_SIZE_SHIFT 52 #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL + +#define KVM_REG_SIZE(id) \ + (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + #define KVM_REG_SIZE_U8 0x0000000000000000ULL #define KVM_REG_SIZE_U16 0x0010000000000000ULL #define KVM_REG_SIZE_U32 0x0020000000000000ULL diff --git a/linux-headers/linux/stddef.h b/linux-headers/linux/stddef.h index 96aa341942..e1416f7937 100644 --- a/linux-headers/linux/stddef.h +++ b/linux-headers/linux/stddef.h @@ -8,6 +8,13 @@ #define __always_inline __inline__ #endif +/* Not all C++ standards support type declarations inside an anonymous union */ +#ifndef __cplusplus +#define __struct_group_tag(TAG) TAG +#else +#define __struct_group_tag(TAG) +#endif + /** * __struct_group() - Create a mirrored named and anonyomous struct * @@ -20,13 +27,13 @@ * and size: one anonymous and one named. The former's members can be used * normally without sub-struct naming, and the latter can be used to * reason about the start, end, and size of the group of struct members. - * The named struct can also be explicitly tagged for layer reuse, as well - * as both having struct attributes appended. + * The named struct can also be explicitly tagged for layer reuse (C only), + * as well as both having struct attributes appended. */ #define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ union { \ struct { MEMBERS } ATTRS; \ - struct TAG { MEMBERS } ATTRS NAME; \ + struct __struct_group_tag(TAG) { MEMBERS } ATTRS NAME; \ } ATTRS #ifdef __cplusplus diff --git a/linux-headers/linux/vduse.h b/linux-headers/linux/vduse.h index 6d2ca064b5..f46269af34 100644 --- a/linux-headers/linux/vduse.h +++ b/linux-headers/linux/vduse.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ #ifndef _VDUSE_H_ #define _VDUSE_H_ From 93e59c8b7619c9ff0f5dc51633a95e3f77a85056 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 21 Feb 2025 12:37:57 -0300 Subject: [PATCH 2045/2892] target/riscv/cpu.c: create flag for ziccrse At this moment ziccrse is a TCG always enabled named feature for priv_ver > 1.11 that has no exclusive flag. In the next patch we'll make the KVM driver turn ziccrse off if the extension isn't available in the host, and we'll need an ext_ziccrse flag in the CPU state for that. Create an exclusive flag for it like we do with other named features. As with any named features we already have, it won't be exposed to users. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Message-ID: <20250221153758.652078-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 ++- target/riscv/cpu_cfg.h | 3 +++ target/riscv/tcg/tcg-cpu.c | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a4ee381a07..47424fd5e2 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -105,7 +105,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(ziccamoa, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(ziccif, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, has_priv_1_11), - ISA_EXT_DATA_ENTRY(ziccrse, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(ziccrse, PRIV_VERSION_1_11_0, ext_ziccrse), ISA_EXT_DATA_ENTRY(zicfilp, PRIV_VERSION_1_12_0, ext_zicfilp), ISA_EXT_DATA_ENTRY(zicfiss, PRIV_VERSION_1_13_0, ext_zicfiss), ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), @@ -1742,6 +1742,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), MULTI_EXT_CFG_BOOL("sha", ext_sha, true), + MULTI_EXT_CFG_BOOL("ziccrse", ext_ziccrse, true), { }, }; diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 3f3c1118c0..8a843482cc 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -166,6 +166,9 @@ struct RISCVCPUConfig { bool has_priv_1_12; bool has_priv_1_11; + /* Always enabled for TCG if has_priv_1_11 */ + bool ext_ziccrse; + /* Vendor-specific custom extensions */ bool ext_xtheadba; bool ext_xtheadbb; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 929ed5fd2c..f1d971eec1 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -360,6 +360,8 @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.ext_sha = riscv_has_ext(&cpu->env, RVH) && cpu->cfg.ext_ssstateen; + + cpu->cfg.ext_ziccrse = cpu->cfg.has_priv_1_11; } static void riscv_cpu_validate_g(RISCVCPU *cpu) From eaa910b14773403963316cfe3617eecc9e02356a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 21 Feb 2025 12:37:58 -0300 Subject: [PATCH 2046/2892] target/riscv/kvm: add extensions after 6.14-rc3 update Expose ziccrse, zabha and svvptc. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Message-ID: <20250221153758.652078-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 23ce779359..471fd554b3 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -274,6 +274,7 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), + KVM_EXT_CFG("ziccrse", ext_ziccrse, KVM_RISCV_ISA_EXT_ZICCRSE), KVM_EXT_CFG("zicntr", ext_zicntr, KVM_RISCV_ISA_EXT_ZICNTR), KVM_EXT_CFG("zicond", ext_zicond, KVM_RISCV_ISA_EXT_ZICOND), KVM_EXT_CFG("zicsr", ext_zicsr, KVM_RISCV_ISA_EXT_ZICSR), @@ -283,6 +284,7 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zihpm", ext_zihpm, KVM_RISCV_ISA_EXT_ZIHPM), KVM_EXT_CFG("zimop", ext_zimop, KVM_RISCV_ISA_EXT_ZIMOP), KVM_EXT_CFG("zcmop", ext_zcmop, KVM_RISCV_ISA_EXT_ZCMOP), + KVM_EXT_CFG("zabha", ext_zabha, KVM_RISCV_ISA_EXT_ZABHA), KVM_EXT_CFG("zacas", ext_zacas, KVM_RISCV_ISA_EXT_ZACAS), KVM_EXT_CFG("zawrs", ext_zawrs, KVM_RISCV_ISA_EXT_ZAWRS), KVM_EXT_CFG("zfa", ext_zfa, KVM_RISCV_ISA_EXT_ZFA), @@ -325,6 +327,7 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL), KVM_EXT_CFG("svnapot", ext_svnapot, KVM_RISCV_ISA_EXT_SVNAPOT), KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT), + KVM_EXT_CFG("svvptc", ext_svvptc, KVM_RISCV_ISA_EXT_SVVPTC), }; static void *kvmconfig_get_cfg_addr(RISCVCPU *cpu, KVMCPUConfig *kvmcfg) From 5bdbbacb8c3ee1705838da9b1cddfe065e53821c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 24 Feb 2025 16:08:16 -0300 Subject: [PATCH 2047/2892] hw/riscv/riscv-iommu.h: add missing headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header is incomplete, i.e. it is using definitions that are being supplied by the .c files that are including it. Adding this header into a fresh .c file will result in errors: /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:30:17: error: field ‘parent_obj’ has incomplete type 30 | DeviceState parent_obj; | ^~~~~~~~~~ /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:50:5: error: unknown type name ‘dma_addr_t’; did you mean ‘in_addr_t’? 50 | dma_addr_t cq_addr; /* Command queue base physical address */ | ^~~~~~~~~~ | in_addr_t (...) /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:62:5: error: unknown type name ‘QemuThread’; did you mean ‘GThread’? 62 | QemuThread core_proc; /* Background processing thread */ | ^~~~~~~~~~ | GThread /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:63:5: error: unknown type name ‘QemuCond’ 63 | QemuCond core_cond; /* Background processing wake up signal */ | ^~~~~~~~ /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:71:18: error: field ‘trap_as’ has incomplete type 71 | AddressSpace trap_as; | ^~~~~~~ /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:72:18: error: field ‘trap_mr’ has incomplete type 72 | MemoryRegion trap_mr; | ^~~~~~~ /home/danielhb/work/qemu/hw/riscv/riscv-iommu.h:80:18: error: field ‘regs_mr’ has incomplete type 80 | MemoryRegion regs_mr; | ^~~~~~~ Fix it by adding the missing headers for these definitions. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250224190826.1858473-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index fa8a50fa24..d2608d2f9b 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -20,6 +20,8 @@ #define HW_RISCV_IOMMU_STATE_H #include "qom/object.h" +#include "hw/qdev-properties.h" +#include "system/dma.h" #include "hw/riscv/iommu.h" #include "hw/riscv/riscv-iommu-bits.h" From 045b19afc9889aa001bab3ae2cb1ecc47b8de790 Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:17 -0300 Subject: [PATCH 2048/2892] hw/riscv/riscv-iommu-bits.h: HPM bits Add the relevant HPM (High Performance Monitor) bits that we'll be using in the next patches. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-bits.h | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index de599b80d6..b7cb1bc736 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -88,6 +88,7 @@ struct riscv_iommu_pq_record { #define RISCV_IOMMU_CAP_ATS BIT_ULL(25) #define RISCV_IOMMU_CAP_T2GPA BIT_ULL(26) #define RISCV_IOMMU_CAP_IGS GENMASK_ULL(29, 28) +#define RISCV_IOMMU_CAP_HPM BIT_ULL(30) #define RISCV_IOMMU_CAP_DBG BIT_ULL(31) #define RISCV_IOMMU_CAP_PAS GENMASK_ULL(37, 32) #define RISCV_IOMMU_CAP_PD8 BIT_ULL(38) @@ -197,6 +198,52 @@ enum { RISCV_IOMMU_INTR_COUNT }; +#define RISCV_IOMMU_IOCOUNT_NUM 31 + +/* 5.19 Performance monitoring counter overflow status (32bits) */ +#define RISCV_IOMMU_REG_IOCOUNTOVF 0x0058 +#define RISCV_IOMMU_IOCOUNTOVF_CY BIT(0) + +/* 5.20 Performance monitoring counter inhibits (32bits) */ +#define RISCV_IOMMU_REG_IOCOUNTINH 0x005C +#define RISCV_IOMMU_IOCOUNTINH_CY BIT(0) + +/* 5.21 Performance monitoring cycles counter (64bits) */ +#define RISCV_IOMMU_REG_IOHPMCYCLES 0x0060 +#define RISCV_IOMMU_IOHPMCYCLES_COUNTER GENMASK_ULL(62, 0) +#define RISCV_IOMMU_IOHPMCYCLES_OVF BIT_ULL(63) + +/* 5.22 Performance monitoring event counters (31 * 64bits) */ +#define RISCV_IOMMU_REG_IOHPMCTR_BASE 0x0068 +#define RISCV_IOMMU_REG_IOHPMCTR(_n) \ + (RISCV_IOMMU_REG_IOHPMCTR_BASE + (_n * 0x8)) + +/* 5.23 Performance monitoring event selectors (31 * 64bits) */ +#define RISCV_IOMMU_REG_IOHPMEVT_BASE 0x0160 +#define RISCV_IOMMU_REG_IOHPMEVT(_n) \ + (RISCV_IOMMU_REG_IOHPMEVT_BASE + (_n * 0x8)) +#define RISCV_IOMMU_IOHPMEVT_EVENT_ID GENMASK_ULL(14, 0) +#define RISCV_IOMMU_IOHPMEVT_DMASK BIT_ULL(15) +#define RISCV_IOMMU_IOHPMEVT_PID_PSCID GENMASK_ULL(35, 16) +#define RISCV_IOMMU_IOHPMEVT_DID_GSCID GENMASK_ULL(59, 36) +#define RISCV_IOMMU_IOHPMEVT_PV_PSCV BIT_ULL(60) +#define RISCV_IOMMU_IOHPMEVT_DV_GSCV BIT_ULL(61) +#define RISCV_IOMMU_IOHPMEVT_IDT BIT_ULL(62) +#define RISCV_IOMMU_IOHPMEVT_OF BIT_ULL(63) + +enum RISCV_IOMMU_HPMEVENT_id { + RISCV_IOMMU_HPMEVENT_INVALID = 0, + RISCV_IOMMU_HPMEVENT_URQ = 1, + RISCV_IOMMU_HPMEVENT_TRQ = 2, + RISCV_IOMMU_HPMEVENT_ATS_RQ = 3, + RISCV_IOMMU_HPMEVENT_TLB_MISS = 4, + RISCV_IOMMU_HPMEVENT_DD_WALK = 5, + RISCV_IOMMU_HPMEVENT_PD_WALK = 6, + RISCV_IOMMU_HPMEVENT_S_VS_WALKS = 7, + RISCV_IOMMU_HPMEVENT_G_WALKS = 8, + RISCV_IOMMU_HPMEVENT_MAX = 9 +}; + /* 5.24 Translation request IOVA (64bits) */ #define RISCV_IOMMU_REG_TR_REQ_IOVA 0x0258 From 4faea7e084dc02c6491b55e594ba5d39a75ff38f Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:18 -0300 Subject: [PATCH 2049/2892] hw/riscv/riscv-iommu: add riscv-iommu-hpm file The HPM (Hardware Performance Monitor) support consists of almost 7 hundred lines that would be put on top of the base riscv-iommu emulation. To avoid clogging riscv-iommu.c, add a separated riscv-iommu-hpm file that will contain HPM specific code. We'll start by adding riscv_iommu_hpmcycle_read(), a helper that will be called during the riscv_iommu_mmio_read() callback. This change will have no effect on the existing emulation since we're not declaring HPM feature support. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/meson.build | 3 ++- hw/riscv/riscv-iommu-hpm.c | 54 ++++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu-hpm.h | 27 +++++++++++++++++++ hw/riscv/riscv-iommu.c | 24 ++++++++++++++++- hw/riscv/riscv-iommu.h | 4 +++ 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 hw/riscv/riscv-iommu-hpm.c create mode 100644 hw/riscv/riscv-iommu-hpm.h diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 3c7e083aca..c22f3a7216 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -10,7 +10,8 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) -riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c')) +riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files( + 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c')) riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c new file mode 100644 index 0000000000..5833ab8956 --- /dev/null +++ b/hw/riscv/riscv-iommu-hpm.c @@ -0,0 +1,54 @@ +/* + * RISC-V IOMMU - Hardware Performance Monitor (HPM) helpers + * + * Copyright (C) 2022-2023 Rivos Inc. + * + * 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 that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "cpu_bits.h" +#include "riscv-iommu-hpm.h" +#include "riscv-iommu.h" +#include "riscv-iommu-bits.h" +#include "trace.h" + +/* For now we assume IOMMU HPM frequency to be 1GHz so 1-cycle is of 1-ns. */ +static inline uint64_t get_cycles(void) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s) +{ + const uint64_t cycle = riscv_iommu_reg_get64( + s, RISCV_IOMMU_REG_IOHPMCYCLES); + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + const uint64_t ctr_prev = s->hpmcycle_prev; + const uint64_t ctr_val = s->hpmcycle_val; + + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { + /* + * Counter should not increment if inhibit bit is set. We can't really + * stop the QEMU_CLOCK_VIRTUAL, so we just return the last updated + * counter value to indicate that counter was not incremented. + */ + return (ctr_val & RISCV_IOMMU_IOHPMCYCLES_COUNTER) | + (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); + } + + return (ctr_val + get_cycles() - ctr_prev) | + (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h new file mode 100644 index 0000000000..231c110ff2 --- /dev/null +++ b/hw/riscv/riscv-iommu-hpm.h @@ -0,0 +1,27 @@ +/* + * RISC-V IOMMU - Hardware Performance Monitor (HPM) helpers + * + * Copyright (C) 2022-2023 Rivos Inc. + * + * 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 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 . + */ + +#ifndef HW_RISCV_IOMMU_HPM_H +#define HW_RISCV_IOMMU_HPM_H + +#include "qom/object.h" +#include "hw/riscv/riscv-iommu.h" + +uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s); + +#endif diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index e7568ca227..0fbd50bb52 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -29,6 +29,7 @@ #include "cpu_bits.h" #include "riscv-iommu.h" #include "riscv-iommu-bits.h" +#include "riscv-iommu-hpm.h" #include "trace.h" #define LIMIT_CACHE_CTX (1U << 7) @@ -2153,7 +2154,28 @@ static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr, return MEMTX_ACCESS_ERROR; } - ptr = &s->regs_rw[addr]; + /* Compute cycle register value. */ + if ((addr & ~7) == RISCV_IOMMU_REG_IOHPMCYCLES) { + val = riscv_iommu_hpmcycle_read(s); + ptr = (uint8_t *)&val + (addr & 7); + } else if ((addr & ~3) == RISCV_IOMMU_REG_IOCOUNTOVF) { + /* + * Software can read RISCV_IOMMU_REG_IOCOUNTOVF before timer + * callback completes. In which case CY_OF bit in + * RISCV_IOMMU_IOHPMCYCLES_OVF would be 0. Here we take the + * CY_OF bit state from RISCV_IOMMU_REG_IOHPMCYCLES register as + * it's not dependent over the timer callback and is computed + * from cycle overflow. + */ + val = ldq_le_p(&s->regs_rw[addr]); + val |= (riscv_iommu_hpmcycle_read(s) & RISCV_IOMMU_IOHPMCYCLES_OVF) + ? RISCV_IOMMU_IOCOUNTOVF_CY + : 0; + ptr = (uint8_t *)&val + (addr & 3); + } else { + ptr = &s->regs_rw[addr]; + } + val = ldn_le_p(ptr, size); *data = val; diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index d2608d2f9b..59db3fd02a 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -81,6 +81,10 @@ struct RISCVIOMMUState { QLIST_ENTRY(RISCVIOMMUState) iommus; QLIST_HEAD(, RISCVIOMMUSpace) spaces; + + /* HPM cycle counter */ + uint64_t hpmcycle_val; /* Current value of cycle register */ + uint64_t hpmcycle_prev; /* Saved value of QEMU_CLOCK_VIRTUAL clock */ }; void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, From 11ecf24c7eda83bb92e24a81425ac6d33a63378e Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:19 -0300 Subject: [PATCH 2050/2892] hw/riscv/riscv-iommu: add riscv_iommu_hpm_incr_ctr() This function will increment a specific counter, generating an interrupt when an overflow occurs. Some extra changes in riscv-iommu.c were required to add this new helper in riscv-iommu-hpm.c: - RISCVIOMMUContext was moved to riscv-iommu.h, making it visible in riscv-iommu-hpm.c; - riscv_iommu_notify() is now public. No behavior change is made since HPM support is not being advertised yet. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-hpm.c | 114 +++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu-hpm.h | 2 + hw/riscv/riscv-iommu.c | 43 +++++++++----- hw/riscv/riscv-iommu.h | 18 ++++++ 4 files changed, 162 insertions(+), 15 deletions(-) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index 5833ab8956..8eca5ee17e 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -52,3 +52,117 @@ uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s) return (ctr_val + get_cycles() - ctr_prev) | (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); } + +static void hpm_incr_ctr(RISCVIOMMUState *s, uint32_t ctr_idx) +{ + const uint32_t off = ctr_idx << 3; + uint64_t cntr_val; + + cntr_val = ldq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]); + stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1); + + /* Handle the overflow scenario. */ + if (cntr_val == UINT64_MAX) { + /* + * Generate interrupt only if OF bit is clear. +1 to offset the cycle + * register OF bit. + */ + const uint32_t ovf = + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, + BIT(ctr_idx + 1), 0); + if (!get_field(ovf, BIT(ctr_idx + 1))) { + riscv_iommu_reg_mod64(s, + RISCV_IOMMU_REG_IOHPMEVT_BASE + off, + RISCV_IOMMU_IOHPMEVT_OF, + 0); + riscv_iommu_notify(s, RISCV_IOMMU_INTR_PM); + } + } +} + +void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + unsigned event_id) +{ + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + uint32_t did_gscid; + uint32_t pid_pscid; + uint32_t ctr_idx; + gpointer value; + uint32_t ctrs; + uint64_t evt; + + if (!(s->cap & RISCV_IOMMU_CAP_HPM)) { + return; + } + + value = g_hash_table_lookup(s->hpm_event_ctr_map, + GUINT_TO_POINTER(event_id)); + if (value == NULL) { + return; + } + + for (ctrs = GPOINTER_TO_UINT(value); ctrs != 0; ctrs &= ctrs - 1) { + ctr_idx = ctz32(ctrs); + if (get_field(inhibit, BIT(ctr_idx + 1))) { + continue; + } + + evt = riscv_iommu_reg_get64(s, + RISCV_IOMMU_REG_IOHPMEVT_BASE + (ctr_idx << 3)); + + /* + * It's quite possible that event ID has been changed in counter + * but hashtable hasn't been updated yet. We don't want to increment + * counter for the old event ID. + */ + if (event_id != get_field(evt, RISCV_IOMMU_IOHPMEVT_EVENT_ID)) { + continue; + } + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_IDT)) { + did_gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); + pid_pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); + } else { + did_gscid = ctx->devid; + pid_pscid = ctx->process_id; + } + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_PV_PSCV)) { + /* + * If the transaction does not have a valid process_id, counter + * increments if device_id matches DID_GSCID. If the transaction + * has a valid process_id, counter increments if device_id + * matches DID_GSCID and process_id matches PID_PSCID. See + * IOMMU Specification, Chapter 5.23. Performance-monitoring + * event selector. + */ + if (ctx->process_id && + get_field(evt, RISCV_IOMMU_IOHPMEVT_PID_PSCID) != pid_pscid) { + continue; + } + } + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_DV_GSCV)) { + uint32_t mask = ~0; + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_DMASK)) { + /* + * 1001 1011 mask = GSCID + * 0000 0111 mask = mask ^ (mask + 1) + * 1111 1000 mask = ~mask; + */ + mask = get_field(evt, RISCV_IOMMU_IOHPMEVT_DID_GSCID); + mask = mask ^ (mask + 1); + mask = ~mask; + } + + if ((get_field(evt, RISCV_IOMMU_IOHPMEVT_DID_GSCID) & mask) != + (did_gscid & mask)) { + continue; + } + } + + hpm_incr_ctr(s, ctr_idx); + } +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h index 231c110ff2..411d869dce 100644 --- a/hw/riscv/riscv-iommu-hpm.h +++ b/hw/riscv/riscv-iommu-hpm.h @@ -23,5 +23,7 @@ #include "hw/riscv/riscv-iommu.h" uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s); +void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + unsigned event_id); #endif diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 0fbd50bb52..0b15acf4e6 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -39,7 +39,6 @@ #define PPN_PHYS(ppn) ((ppn) << TARGET_PAGE_BITS) #define PPN_DOWN(phy) ((phy) >> TARGET_PAGE_BITS) -typedef struct RISCVIOMMUContext RISCVIOMMUContext; typedef struct RISCVIOMMUEntry RISCVIOMMUEntry; /* Device assigned I/O address space */ @@ -52,19 +51,6 @@ struct RISCVIOMMUSpace { QLIST_ENTRY(RISCVIOMMUSpace) list; }; -/* Device translation context state. */ -struct RISCVIOMMUContext { - uint64_t devid:24; /* Requester Id, AKA device_id */ - uint64_t process_id:20; /* Process ID. PASID for PCIe */ - uint64_t tc; /* Translation Control */ - uint64_t ta; /* Translation Attributes */ - uint64_t satp; /* S-Stage address translation and protection */ - uint64_t gatp; /* G-Stage address translation and protection */ - uint64_t msi_addr_mask; /* MSI filtering - address mask */ - uint64_t msi_addr_pattern; /* MSI filtering - address pattern */ - uint64_t msiptp; /* MSI redirection page table pointer */ -}; - typedef enum RISCVIOMMUTransTag { RISCV_IOMMU_TRANS_TAG_BY, /* Bypass */ RISCV_IOMMU_TRANS_TAG_SS, /* Single Stage */ @@ -101,7 +87,7 @@ static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) } } -static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) +void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) { uint32_t ipsr, icvec, vector; @@ -423,6 +409,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } } + + if (pass == S_STAGE) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_S_VS_WALKS); + } else { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_G_WALKS); + } + /* Read page table entry */ if (sc[pass].ptesize == 4) { uint32_t pte32 = 0; @@ -941,6 +934,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) /* Device directory tree walk */ for (; depth-- > 0; ) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_DD_WALK); /* * Select device id index bits based on device directory tree level * and device context format. @@ -968,6 +962,8 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN)); } + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_DD_WALK); + /* index into device context entry page */ addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK; @@ -1033,6 +1029,8 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) } for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); + /* * Select process id index bits based on process directory tree * level. See IOMMU Specification, 2.2. Process-Directory-Table. @@ -1050,6 +1048,8 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN)); } + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); + /* Leaf entry in PDT */ addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK; if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2, @@ -1419,6 +1419,8 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, GHashTable *iot_cache; int fault; + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_URQ); + iot_cache = g_hash_table_ref(s->iot_cache); /* * TC[32] is reserved for custom extensions, used here to temporarily @@ -1429,6 +1431,7 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, /* Check for ATS request. */ if (iotlb->perm == IOMMU_NONE) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_ATS_RQ); /* Check if ATS is disabled. */ if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) { enable_pri = false; @@ -1447,6 +1450,8 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, goto done; } + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_TLB_MISS); + /* Translate using device directory / page table information. */ fault = riscv_iommu_spa_fetch(s, ctx, iotlb); @@ -2375,6 +2380,10 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s, "riscv-iommu-trap", ~0ULL); address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as"); + + if (s->cap & RISCV_IOMMU_CAP_HPM) { + s->hpm_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + } } static void riscv_iommu_unrealize(DeviceState *dev) @@ -2383,6 +2392,10 @@ static void riscv_iommu_unrealize(DeviceState *dev) g_hash_table_unref(s->iot_cache); g_hash_table_unref(s->ctx_cache); + + if (s->cap & RISCV_IOMMU_CAP_HPM) { + g_hash_table_unref(s->hpm_event_ctr_map); + } } void riscv_iommu_reset(RISCVIOMMUState *s) diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index 59db3fd02a..4384f39515 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -85,12 +85,30 @@ struct RISCVIOMMUState { /* HPM cycle counter */ uint64_t hpmcycle_val; /* Current value of cycle register */ uint64_t hpmcycle_prev; /* Saved value of QEMU_CLOCK_VIRTUAL clock */ + + /* HPM event counters */ + GHashTable *hpm_event_ctr_map; /* Mapping of events to counters */ }; void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, Error **errp); void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); void riscv_iommu_reset(RISCVIOMMUState *s); +void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type); + +typedef struct RISCVIOMMUContext RISCVIOMMUContext; +/* Device translation context state. */ +struct RISCVIOMMUContext { + uint64_t devid:24; /* Requester Id, AKA device_id */ + uint64_t process_id:20; /* Process ID. PASID for PCIe */ + uint64_t tc; /* Translation Control */ + uint64_t ta; /* Translation Attributes */ + uint64_t satp; /* S-Stage address translation and protection */ + uint64_t gatp; /* G-Stage address translation and protection */ + uint64_t msi_addr_mask; /* MSI filtering - address mask */ + uint64_t msi_addr_pattern; /* MSI filtering - address pattern */ + uint64_t msiptp; /* MSI redirection page table pointer */ +}; /* private helpers */ From ffb37df056490b34cecc7958bf5f83fe5497b2d4 Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:20 -0300 Subject: [PATCH 2051/2892] hw/riscv/riscv-iommu: instantiate hpm_timer The next HPM related changes requires the HPM overflow timer to be initialized by the riscv-iommu base emulation. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-hpm.c | 36 ++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu-hpm.h | 1 + hw/riscv/riscv-iommu.c | 3 +++ hw/riscv/riscv-iommu.h | 2 ++ 4 files changed, 42 insertions(+) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index 8eca5ee17e..325088333e 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -166,3 +166,39 @@ void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, hpm_incr_ctr(s, ctr_idx); } } + +/* Timer callback for cycle counter overflow. */ +void riscv_iommu_hpm_timer_cb(void *priv) +{ + RISCVIOMMUState *s = priv; + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + uint32_t ovf; + + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { + return; + } + + if (s->irq_overflow_left > 0) { + uint64_t irq_trigger_at = + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->irq_overflow_left; + timer_mod_anticipate_ns(s->hpm_timer, irq_trigger_at); + s->irq_overflow_left = 0; + return; + } + + ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + if (!get_field(ovf, RISCV_IOMMU_IOCOUNTOVF_CY)) { + /* + * We don't need to set hpmcycle_val to zero and update hpmcycle_prev to + * current clock value. The way we calculate iohpmcycs will overflow + * and return the correct value. This avoids the need to synchronize + * timer callback and write callback. + */ + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, + RISCV_IOMMU_IOCOUNTOVF_CY, 0); + riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_IOHPMCYCLES, + RISCV_IOMMU_IOHPMCYCLES_OVF, 0); + riscv_iommu_notify(s, RISCV_IOMMU_INTR_PM); + } +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h index 411d869dce..cd896d3b7c 100644 --- a/hw/riscv/riscv-iommu-hpm.h +++ b/hw/riscv/riscv-iommu-hpm.h @@ -25,5 +25,6 @@ uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s); void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, unsigned event_id); +void riscv_iommu_hpm_timer_cb(void *priv); #endif diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 0b15acf4e6..f26aa15f55 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2382,6 +2382,8 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as"); if (s->cap & RISCV_IOMMU_CAP_HPM) { + s->hpm_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, riscv_iommu_hpm_timer_cb, s); s->hpm_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); } } @@ -2395,6 +2397,7 @@ static void riscv_iommu_unrealize(DeviceState *dev) if (s->cap & RISCV_IOMMU_CAP_HPM) { g_hash_table_unref(s->hpm_event_ctr_map); + timer_free(s->hpm_timer); } } diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index 4384f39515..2fef6eed27 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -83,8 +83,10 @@ struct RISCVIOMMUState { QLIST_HEAD(, RISCVIOMMUSpace) spaces; /* HPM cycle counter */ + QEMUTimer *hpm_timer; uint64_t hpmcycle_val; /* Current value of cycle register */ uint64_t hpmcycle_prev; /* Saved value of QEMU_CLOCK_VIRTUAL clock */ + uint64_t irq_overflow_left; /* Value beyond INT64_MAX after overflow */ /* HPM event counters */ GHashTable *hpm_event_ctr_map; /* Mapping of events to counters */ From 2cf2a6c027ba1a47be04c53d7cd8f6269007a0b1 Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:21 -0300 Subject: [PATCH 2052/2892] hw/riscv/riscv-iommu: add IOCOUNTINH mmio writes RISCV_IOMMU_REG_IOCOUNTINH is done by riscv_iommu_process_iocntinh_cy(), which is called during riscv_iommu_mmio_write() callback via a new riscv_iommu_pricess_hpm_writes() helper. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-hpm.c | 60 ++++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu-hpm.h | 1 + hw/riscv/riscv-iommu.c | 38 ++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index 325088333e..70814b942d 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -202,3 +202,63 @@ void riscv_iommu_hpm_timer_cb(void *priv) riscv_iommu_notify(s, RISCV_IOMMU_INTR_PM); } } + +static void hpm_setup_timer(RISCVIOMMUState *s, uint64_t value) +{ + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + uint64_t overflow_at, overflow_ns; + + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { + return; + } + + /* + * We are using INT64_MAX here instead to UINT64_MAX because cycle counter + * has 63-bit precision and INT64_MAX is the maximum it can store. + */ + if (value) { + overflow_ns = INT64_MAX - value + 1; + } else { + overflow_ns = INT64_MAX; + } + + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; + + if (overflow_at > INT64_MAX) { + s->irq_overflow_left = overflow_at - INT64_MAX; + overflow_at = INT64_MAX; + } + + timer_mod_anticipate_ns(s->hpm_timer, overflow_at); +} + +/* Updates the internal cycle counter state when iocntinh:CY is changed. */ +void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh) +{ + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + + /* We only need to process CY bit toggle. */ + if (!(inhibit ^ prev_cy_inh)) { + return; + } + + if (!(inhibit & RISCV_IOMMU_IOCOUNTINH_CY)) { + /* + * Cycle counter is enabled. Just start the timer again and update + * the clock snapshot value to point to the current time to make + * sure iohpmcycles read is correct. + */ + s->hpmcycle_prev = get_cycles(); + hpm_setup_timer(s, s->hpmcycle_val); + } else { + /* + * Cycle counter is disabled. Stop the timer and update the cycle + * counter to record the current value which is last programmed + * value + the cycles passed so far. + */ + s->hpmcycle_val = s->hpmcycle_val + (get_cycles() - s->hpmcycle_prev); + timer_del(s->hpm_timer); + } +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h index cd896d3b7c..ee888650fb 100644 --- a/hw/riscv/riscv-iommu-hpm.h +++ b/hw/riscv/riscv-iommu-hpm.h @@ -26,5 +26,6 @@ uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s); void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, unsigned event_id); void riscv_iommu_hpm_timer_cb(void *priv); +void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh); #endif diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index f26aa15f55..a4580dca0b 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2024,6 +2024,27 @@ static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data) riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr); } +static void riscv_iommu_process_hpm_writes(RISCVIOMMUState *s, + uint32_t regb, + bool prev_cy_inh) +{ + switch (regb) { + case RISCV_IOMMU_REG_IOCOUNTINH: + riscv_iommu_process_iocntinh_cy(s, prev_cy_inh); + break; + + case RISCV_IOMMU_REG_IOHPMCYCLES: + case RISCV_IOMMU_REG_IOHPMCYCLES + 4: + /* not yet implemented */ + break; + + case RISCV_IOMMU_REG_IOHPMEVT_BASE ... + RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4: + /* not yet implemented */ + break; + } +} + /* * Write the resulting value of 'data' for the reg specified * by 'reg_addr', after considering read-only/read-write/write-clear @@ -2051,6 +2072,7 @@ static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, uint32_t regb = addr & ~3; uint32_t busy = 0; uint64_t val = 0; + bool cy_inh = false; if ((addr & (size - 1)) != 0) { /* Unsupported MMIO alignment or access size */ @@ -2118,6 +2140,16 @@ static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY; break; + case RISCV_IOMMU_REG_IOCOUNTINH: + if (addr != RISCV_IOMMU_REG_IOCOUNTINH) { + break; + } + /* Store previous value of CY bit. */ + cy_inh = !!(riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTINH) & + RISCV_IOMMU_IOCOUNTINH_CY); + break; + + default: break; } @@ -2136,6 +2168,12 @@ static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, stl_le_p(&s->regs_rw[regb], rw | busy); } + /* Process HPM writes and update any internal state if needed. */ + if (regb >= RISCV_IOMMU_REG_IOCOUNTOVF && + regb <= (RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4)) { + riscv_iommu_process_hpm_writes(s, regb, cy_inh); + } + if (process_fn) { process_fn(s); } From 91dd0bd0216f7a70e5e30cfc24eeea455b4f6993 Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:22 -0300 Subject: [PATCH 2053/2892] hw/riscv/riscv-iommu: add IOHPMCYCLES mmio write RISCV_IOMMU_REG_IOHPMCYCLES writes are done by riscv_iommu_process_hpmcycle_write(), called by the mmio write callback via riscv_iommu_process_hpm_writes(). Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250224190826.1858473-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-hpm.c | 19 +++++++++++++++++++ hw/riscv/riscv-iommu-hpm.h | 1 + hw/riscv/riscv-iommu.c | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index 70814b942d..1cea6b1df1 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -262,3 +262,22 @@ void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh) timer_del(s->hpm_timer); } } + +void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s) +{ + const uint64_t val = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_IOHPMCYCLES); + const uint32_t ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + + /* + * Clear OF bit in IOCNTOVF if it's being cleared in IOHPMCYCLES register. + */ + if (get_field(ovf, RISCV_IOMMU_IOCOUNTOVF_CY) && + !get_field(val, RISCV_IOMMU_IOHPMCYCLES_OVF)) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, 0, + RISCV_IOMMU_IOCOUNTOVF_CY); + } + + s->hpmcycle_val = val & ~RISCV_IOMMU_IOHPMCYCLES_OVF; + s->hpmcycle_prev = get_cycles(); + hpm_setup_timer(s, s->hpmcycle_val); +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h index ee888650fb..0cd550975d 100644 --- a/hw/riscv/riscv-iommu-hpm.h +++ b/hw/riscv/riscv-iommu-hpm.h @@ -27,5 +27,6 @@ void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, unsigned event_id); void riscv_iommu_hpm_timer_cb(void *priv); void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh); +void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s); #endif diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index a4580dca0b..821ecba3a4 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2035,7 +2035,7 @@ static void riscv_iommu_process_hpm_writes(RISCVIOMMUState *s, case RISCV_IOMMU_REG_IOHPMCYCLES: case RISCV_IOMMU_REG_IOHPMCYCLES + 4: - /* not yet implemented */ + riscv_iommu_process_hpmcycle_write(s); break; case RISCV_IOMMU_REG_IOHPMEVT_BASE ... From 4faa3e6f906c832f4c5382fbd618e368525ad2dc Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:23 -0300 Subject: [PATCH 2054/2892] hw/riscv/riscv-iommu: add hpm events mmio write To support hpm events mmio writes, done via riscv_iommu_process_hpmevt_write(), we're also adding the 'hpm-counters' IOMMU property that are used to determine the amount of counters available in the IOMMU. Note that everything we did so far didn't change any IOMMU behavior because we're still not advertising HPM capability to software. This will be done in the next patch. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-hpm.c | 88 ++++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu-hpm.h | 1 + hw/riscv/riscv-iommu.c | 4 +- hw/riscv/riscv-iommu.h | 1 + 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index 1cea6b1df1..5518c287a5 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -281,3 +281,91 @@ void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s) s->hpmcycle_prev = get_cycles(); hpm_setup_timer(s, s->hpmcycle_val); } + +static inline bool check_valid_event_id(unsigned event_id) +{ + return event_id > RISCV_IOMMU_HPMEVENT_INVALID && + event_id < RISCV_IOMMU_HPMEVENT_MAX; +} + +static gboolean hpm_event_equal(gpointer key, gpointer value, gpointer udata) +{ + uint32_t *pair = udata; + + if (GPOINTER_TO_UINT(value) & (1 << pair[0])) { + pair[1] = GPOINTER_TO_UINT(key); + return true; + } + + return false; +} + +/* Caller must check ctr_idx against hpm_ctrs to see if its supported or not. */ +static void update_event_map(RISCVIOMMUState *s, uint64_t value, + uint32_t ctr_idx) +{ + unsigned event_id = get_field(value, RISCV_IOMMU_IOHPMEVT_EVENT_ID); + uint32_t pair[2] = { ctr_idx, RISCV_IOMMU_HPMEVENT_INVALID }; + uint32_t new_value = 1 << ctr_idx; + gpointer data; + + /* + * If EventID field is RISCV_IOMMU_HPMEVENT_INVALID + * remove the current mapping. + */ + if (event_id == RISCV_IOMMU_HPMEVENT_INVALID) { + data = g_hash_table_find(s->hpm_event_ctr_map, hpm_event_equal, pair); + + new_value = GPOINTER_TO_UINT(data) & ~(new_value); + if (new_value != 0) { + g_hash_table_replace(s->hpm_event_ctr_map, + GUINT_TO_POINTER(pair[1]), + GUINT_TO_POINTER(new_value)); + } else { + g_hash_table_remove(s->hpm_event_ctr_map, + GUINT_TO_POINTER(pair[1])); + } + + return; + } + + /* Update the counter mask if the event is already enabled. */ + if (g_hash_table_lookup_extended(s->hpm_event_ctr_map, + GUINT_TO_POINTER(event_id), + NULL, + &data)) { + new_value |= GPOINTER_TO_UINT(data); + } + + g_hash_table_insert(s->hpm_event_ctr_map, + GUINT_TO_POINTER(event_id), + GUINT_TO_POINTER(new_value)); +} + +void riscv_iommu_process_hpmevt_write(RISCVIOMMUState *s, uint32_t evt_reg) +{ + const uint32_t ctr_idx = (evt_reg - RISCV_IOMMU_REG_IOHPMEVT_BASE) >> 3; + const uint32_t ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + uint64_t val = riscv_iommu_reg_get64(s, evt_reg); + + if (ctr_idx >= s->hpm_cntrs) { + return; + } + + /* Clear OF bit in IOCNTOVF if it's being cleared in IOHPMEVT register. */ + if (get_field(ovf, BIT(ctr_idx + 1)) && + !get_field(val, RISCV_IOMMU_IOHPMEVT_OF)) { + /* +1 to offset CYCLE register OF bit. */ + riscv_iommu_reg_mod32( + s, RISCV_IOMMU_REG_IOCOUNTOVF, 0, BIT(ctr_idx + 1)); + } + + if (!check_valid_event_id(get_field(val, RISCV_IOMMU_IOHPMEVT_EVENT_ID))) { + /* Reset EventID (WARL) field to invalid. */ + val = set_field(val, RISCV_IOMMU_IOHPMEVT_EVENT_ID, + RISCV_IOMMU_HPMEVENT_INVALID); + riscv_iommu_reg_set64(s, evt_reg, val); + } + + update_event_map(s, val, ctr_idx); +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h index 0cd550975d..5fc4ef2e8b 100644 --- a/hw/riscv/riscv-iommu-hpm.h +++ b/hw/riscv/riscv-iommu-hpm.h @@ -28,5 +28,6 @@ void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, void riscv_iommu_hpm_timer_cb(void *priv); void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh); void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s); +void riscv_iommu_process_hpmevt_write(RISCVIOMMUState *s, uint32_t evt_reg); #endif diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 821ecba3a4..cdbb848181 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2040,7 +2040,7 @@ static void riscv_iommu_process_hpm_writes(RISCVIOMMUState *s, case RISCV_IOMMU_REG_IOHPMEVT_BASE ... RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4: - /* not yet implemented */ + riscv_iommu_process_hpmevt_write(s, regb & ~7); break; } } @@ -2487,6 +2487,8 @@ static const Property riscv_iommu_properties[] = { DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE), DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_UINT8("hpm-counters", RISCVIOMMUState, hpm_cntrs, + RISCV_IOMMU_IOCOUNT_NUM), }; static void riscv_iommu_class_init(ObjectClass *klass, void* data) diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index 2fef6eed27..a31aa62144 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -90,6 +90,7 @@ struct RISCVIOMMUState { /* HPM event counters */ GHashTable *hpm_event_ctr_map; /* Mapping of events to counters */ + uint8_t hpm_cntrs; }; void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, From 3b1d07b6279defa40bf9903f420d8ddcf839794d Mon Sep 17 00:00:00 2001 From: Tomasz Jeznach Date: Mon, 24 Feb 2025 16:08:24 -0300 Subject: [PATCH 2055/2892] hw/riscv/riscv-iommu.c: add RISCV_IOMMU_CAP_HPM cap Now that we have every piece in place we can advertise CAP_HTM to software, allowing any HPM aware driver to make use of the counters. HPM is enabled/disabled via the 'hpm-counters' attribute. Default value is 31, max value is also 31. Setting it to zero will disable HPM support. Signed-off-by: Tomasz Jeznach Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-10-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index cdbb848181..d46beb2d64 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2357,6 +2357,15 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; } + if (s->hpm_cntrs > 0) { + /* Clip number of HPM counters to maximum supported (31). */ + if (s->hpm_cntrs > RISCV_IOMMU_IOCOUNT_NUM) { + s->hpm_cntrs = RISCV_IOMMU_IOCOUNT_NUM; + } + /* Enable hardware performance monitor interface */ + s->cap |= RISCV_IOMMU_CAP_HPM; + } + /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */ s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ? RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE); @@ -2404,6 +2413,18 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); } + /* If HPM registers are enabled. */ + if (s->cap & RISCV_IOMMU_CAP_HPM) { + /* +1 for cycle counter bit. */ + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_IOCOUNTINH], + ~((2 << s->hpm_cntrs) - 1)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_IOHPMCYCLES], 0); + memset(&s->regs_ro[RISCV_IOMMU_REG_IOHPMCTR_BASE], + 0x00, s->hpm_cntrs * 8); + memset(&s->regs_ro[RISCV_IOMMU_REG_IOHPMEVT_BASE], + 0x00, s->hpm_cntrs * 8); + } + /* Memory region for downstream access, if specified. */ if (s->target_mr) { s->target_as = g_new0(AddressSpace, 1); From 66975d9cd73642ad4bb488c2e53d5b3c857e9ce8 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 24 Feb 2025 16:08:25 -0300 Subject: [PATCH 2056/2892] hw/riscv: add IOMMU HPM trace events Add a handful of trace events to allow for an easier time debugging the HPM feature. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250224190826.1858473-11-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-hpm.c | 10 ++++++++++ hw/riscv/trace-events | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index 5518c287a5..c5034bff79 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -39,6 +39,8 @@ uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s) const uint64_t ctr_prev = s->hpmcycle_prev; const uint64_t ctr_val = s->hpmcycle_val; + trace_riscv_iommu_hpm_read(cycle, inhibit, ctr_prev, ctr_val); + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { /* * Counter should not increment if inhibit bit is set. We can't really @@ -61,6 +63,8 @@ static void hpm_incr_ctr(RISCVIOMMUState *s, uint32_t ctr_idx) cntr_val = ldq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]); stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1); + trace_riscv_iommu_hpm_incr_ctr(cntr_val); + /* Handle the overflow scenario. */ if (cntr_val == UINT64_MAX) { /* @@ -244,6 +248,8 @@ void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh) return; } + trace_riscv_iommu_hpm_iocntinh_cy(prev_cy_inh); + if (!(inhibit & RISCV_IOMMU_IOCOUNTINH_CY)) { /* * Cycle counter is enabled. Just start the timer again and update @@ -268,6 +274,8 @@ void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s) const uint64_t val = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_IOHPMCYCLES); const uint32_t ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + trace_riscv_iommu_hpm_cycle_write(ovf, val); + /* * Clear OF bit in IOCNTOVF if it's being cleared in IOHPMCYCLES register. */ @@ -352,6 +360,8 @@ void riscv_iommu_process_hpmevt_write(RISCVIOMMUState *s, uint32_t evt_reg) return; } + trace_riscv_iommu_hpm_evt_write(ctr_idx, ovf, val); + /* Clear OF bit in IOCNTOVF if it's being cleared in IOHPMEVT register. */ if (get_field(ovf, BIT(ctr_idx + 1)) && !get_field(val, RISCV_IOMMU_IOHPMEVT_OF)) { diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events index 7bcbb03d08..b50b14a654 100644 --- a/hw/riscv/trace-events +++ b/hw/riscv/trace-events @@ -19,3 +19,8 @@ riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u" riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u" riscv_iommu_sys_reset_hold(int reset_type) "reset type %d" riscv_iommu_pci_reset_hold(int reset_type) "reset type %d" +riscv_iommu_hpm_read(uint64_t cycle, uint32_t inhibit, uint64_t ctr_prev, uint64_t ctr_val) "cycle 0x%"PRIx64" inhibit 0x%x ctr_prev 0x%"PRIx64" ctr_val 0x%"PRIx64 +riscv_iommu_hpm_incr_ctr(uint64_t cntr_val) "cntr_val 0x%"PRIx64 +riscv_iommu_hpm_iocntinh_cy(bool prev_cy_inh) "prev_cy_inh %d" +riscv_iommu_hpm_cycle_write(uint32_t ovf, uint64_t val) "ovf 0x%x val 0x%"PRIx64 +riscv_iommu_hpm_evt_write(uint32_t ctr_idx, uint32_t ovf, uint64_t val) "ctr_idx 0x%x ovf 0x%x val 0x%"PRIx64 From beeb56a43e8b4568c4513371349487f349df8864 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 24 Feb 2025 16:08:26 -0300 Subject: [PATCH 2057/2892] docs/specs/riscv-iommu.rst: add HPM support info Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250224190826.1858473-12-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- docs/specs/riscv-iommu.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst index b1538c9ead..000c7e1f57 100644 --- a/docs/specs/riscv-iommu.rst +++ b/docs/specs/riscv-iommu.rst @@ -82,6 +82,8 @@ Several options are available to control the capabilities of the device, namely: - "off" (Out-of-reset translation mode: 'on' for DMA disabled, 'off' for 'BARE' (passthrough)) - "s-stage": enable s-stage support - "g-stage": enable g-stage support +- "hpm-counters": number of hardware performance counters available. Maximum value is 31. + Default value is 31. Use 0 (zero) to disable HPM support riscv-iommu-sys device ---------------------- From 1c17df6fc4bbddf55c8a64a3db7fb1115ecd30f5 Mon Sep 17 00:00:00 2001 From: Quan Zhou Date: Mon, 24 Feb 2025 12:39:39 +0800 Subject: [PATCH 2058/2892] target/riscv/kvm: Add some exts support When the Sscofpmf/Svade/Svadu/Smnpm/Ssnpm exts is available expose it to the guest so that guest can use it. Signed-off-by: Quan Zhou Reviewed-by: Daniel Henrique Barboza Message-ID: <303616ccad2b5309768157b50d93b3e89fecc9cb.1740371468.git.zhouquan@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 471fd554b3..e436083dbb 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -321,9 +321,14 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zvksed", ext_zvksed, KVM_RISCV_ISA_EXT_ZVKSED), KVM_EXT_CFG("zvksh", ext_zvksh, KVM_RISCV_ISA_EXT_ZVKSH), KVM_EXT_CFG("zvkt", ext_zvkt, KVM_RISCV_ISA_EXT_ZVKT), + KVM_EXT_CFG("smnpm", ext_smnpm, KVM_RISCV_ISA_EXT_SMNPM), KVM_EXT_CFG("smstateen", ext_smstateen, KVM_RISCV_ISA_EXT_SMSTATEEN), KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), + KVM_EXT_CFG("sscofpmf", ext_sscofpmf, KVM_RISCV_ISA_EXT_SSCOFPMF), + KVM_EXT_CFG("ssnpm", ext_ssnpm, KVM_RISCV_ISA_EXT_SSNPM), KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC), + KVM_EXT_CFG("svade", ext_svade, KVM_RISCV_ISA_EXT_SVADE), + KVM_EXT_CFG("svadu", ext_svadu, KVM_RISCV_ISA_EXT_SVADU), KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL), KVM_EXT_CFG("svnapot", ext_svnapot, KVM_RISCV_ISA_EXT_SVNAPOT), KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT), From c869f4129c8e35f3c234d757c0227c53134aca16 Mon Sep 17 00:00:00 2001 From: Andrea Bolognani Date: Mon, 27 Jan 2025 19:29:22 +0100 Subject: [PATCH 2059/2892] binfmt: Shuffle things around This should make no difference from the functional point of view and it's just preparation for upcoming changes. Signed-off-by: Andrea Bolognani Reviewed-by: Alistair Francis Reviewed-by: Laurent Vivier Message-ID: <20250127182924.103510-2-abologna@redhat.com> Signed-off-by: Alistair Francis --- scripts/qemu-binfmt-conf.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 6ef9f118d9..426f075e31 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -318,20 +318,23 @@ qemu_set_binfmts() { mask=$(eval echo \$${cpu}_mask) family=$(eval echo \$${cpu}_family) + target="$cpu" + if [ "$cpu" = "i486" ] ; then + target="i386" + fi + + qemu="$QEMU_PATH/qemu-$target$QEMU_SUFFIX" + if [ "$magic" = "" ] || [ "$mask" = "" ] || [ "$family" = "" ] ; then echo "INTERNAL ERROR: unknown cpu $cpu" 1>&2 continue fi - qemu="$QEMU_PATH/qemu-$cpu" - if [ "$cpu" = "i486" ] ; then - qemu="$QEMU_PATH/qemu-i386" + if [ "$host_family" = "$family" ] ; then + continue fi - qemu="$qemu$QEMU_SUFFIX" - if [ "$host_family" != "$family" ] ; then - $BINFMT_SET - fi + $BINFMT_SET done } From 2770a46b20b7ccd23019a7b6b6631de933b53c8e Mon Sep 17 00:00:00 2001 From: Andrea Bolognani Date: Mon, 27 Jan 2025 19:29:23 +0100 Subject: [PATCH 2060/2892] binfmt: Normalize host CPU architecture Right now information regarding the family each CPU type belongs to is recorded in two places: the large data table at the top of the script, and the qemu_host_family() function. We can make things better by mapping host CPU architecture to QEMU target in the few cases where the two don't already match and then using the data table to look up the family, same as we're already doing for the guest CPU architecture. Being able to reason in terms of QEMU target regardless of whether we're looking at the host or guest CPU architecture will come in handy to implement upcoming changes. A couple of entries are dropped in the process: BePC and Power Macintosh. I'm quite certain neither of those have ever been reported as CPU architectures by Linux. I believe many more of the entries that are carried forward could be dropped as well, but I don't have the same level of confidence there so I decided to play it safe just in case. Signed-off-by: Andrea Bolognani Reviewed-by: Alistair Francis Reviewed-by: Laurent Vivier Message-ID: <20250127182924.103510-3-abologna@redhat.com> Signed-off-by: Alistair Francis --- scripts/qemu-binfmt-conf.sh | 44 +++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 426f075e31..8d9136a29f 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -144,35 +144,35 @@ loongarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x loongarch64_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' loongarch64_family=loongarch -qemu_get_family() { - cpu=${HOST_ARCH:-$(uname -m)} +# Converts the name of a host CPU architecture to the corresponding QEMU +# target. +# +# FIXME: This can probably be simplified a lot by dropping most entries. +# Remember that the script is only used on Linux, so we only need to +# handle the strings Linux uses to report the host CPU architecture. +qemu_normalize() { + cpu="$1" case "$cpu" in - amd64|i386|i486|i586|i686|i86pc|BePC|x86_64) + i[3-6]86) echo "i386" ;; - mips*) - echo "mips" + amd64) + echo "x86_64" ;; - "Power Macintosh"|ppc64|powerpc|ppc) + powerpc) echo "ppc" ;; - ppc64el|ppc64le) - echo "ppcle" + ppc64el) + echo "ppc64le" ;; - arm|armel|armhf|arm64|armv[4-9]*l|aarch64) + armel|armhf|armv[4-9]*l) echo "arm" ;; - armeb|armv[4-9]*b|aarch64_be) + armv[4-9]*b) echo "armeb" ;; - sparc*) - echo "sparc" - ;; - riscv*) - echo "riscv" - ;; - loongarch*) - echo "loongarch" + arm64) + echo "aarch64" ;; *) echo "$cpu" @@ -309,7 +309,13 @@ EOF qemu_set_binfmts() { # probe cpu type - host_family=$(qemu_get_family) + host_cpu=$(qemu_normalize ${HOST_ARCH:-$(uname -m)}) + host_family=$(eval echo \$${host_cpu}_family) + + if [ "$host_family" = "" ] ; then + echo "INTERNAL ERROR: unknown host cpu $host_cpu" 1>&2 + exit 1 + fi # register the interpreter for each cpu except for the native one From 1887cf2368189087fdf977fb8d09b5ad47cc7aea Mon Sep 17 00:00:00 2001 From: Andrea Bolognani Date: Mon, 27 Jan 2025 19:29:24 +0100 Subject: [PATCH 2061/2892] binfmt: Add --ignore-family option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, the script has worked under the assumption that a host CPU can run binaries targeting any CPU in the same family. That's a fair enough assumption when it comes to running i386 binaries on x86_64, but it doesn't quite apply in the general case. For example, while riscv64 CPUs could theoretically run riscv32 applications natively, in practice there exist few (if any?) CPUs that implement the necessary silicon; moreover, even if you had one such CPU, your host OS would most likely not have enabled the necessary kernel bits. This new option gives distro packagers the ability to opt out of the assumption, likely on a per-architecture basis, and make things work out of the box for a larger fraction of their user base. As an interesting side effect, this makes it possible to enable execution of 64-bit binaries on 32-bit CPUs of the same family, which is a perfectly valid use case that apparently hadn't been considered until now. Link: https://src.fedoraproject.org/rpms/qemu/pull-request/72 Thanks: David Abdurachmanov Thanks: Daniel P. Berrangé Signed-off-by: Andrea Bolognani Reviewed-by: Alistair Francis Reviewed-by: Laurent Vivier Message-ID: <20250127182924.103510-4-abologna@redhat.com> Signed-off-by: Alistair Francis --- scripts/qemu-binfmt-conf.sh | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 8d9136a29f..5fd462b1d1 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -205,6 +205,9 @@ Usage: qemu-binfmt-conf.sh [--qemu-path PATH][--debian][--systemd CPU] --persistent: if yes, the interpreter is loaded when binfmt is configured and remains in memory. All future uses are cloned from the open file. + --ignore-family: if yes, it is assumed that the host CPU (e.g. riscv64) + can't natively run programs targeting a CPU that is + part of the same family (e.g. riscv32). --preserve-argv0 preserve argv[0] To import templates with update-binfmts, use : @@ -337,7 +340,12 @@ qemu_set_binfmts() { fi if [ "$host_family" = "$family" ] ; then - continue + # When --ignore-family is used, we have to generate rules even + # for targets that are in the same family as the host CPU. The + # only exception is of course when the CPU types exactly match + if [ "$target" = "$host_cpu" ] || [ "$IGNORE_FAMILY" = "no" ] ; then + continue + fi fi $BINFMT_SET @@ -355,10 +363,11 @@ CREDENTIAL=no PERSISTENT=no PRESERVE_ARG0=no QEMU_SUFFIX="" +IGNORE_FAMILY=no _longopts="debian,systemd:,qemu-path:,qemu-suffix:,exportdir:,help,credential:,\ -persistent:,preserve-argv0:" -options=$(getopt -o ds:Q:S:e:hc:p:g:F: -l ${_longopts} -- "$@") +persistent:,preserve-argv0:,ignore-family:" +options=$(getopt -o ds:Q:S:e:hc:p:g:F:i: -l ${_longopts} -- "$@") eval set -- "$options" while true ; do @@ -418,6 +427,10 @@ while true ; do shift PRESERVE_ARG0="$1" ;; + -i|--ignore-family) + shift + IGNORE_FAMILY="$1" + ;; *) break ;; From afd4f4aa7f605caf8efa2f3c590e1f2572f1ea50 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 24 Feb 2025 10:57:18 +0800 Subject: [PATCH 2062/2892] hw/intc/imsic: refine the IMSIC realize When the IMSIC is emulated in the kernel, the GPIO output lines to CPUs and aia_ireg_rmw_fn setting can be remove. In this case the IMSIC trigger CPU interrupts by KVM APIs, and the RMW of IREG is handled in kernel. This patch also move the code that claim the CPU interrupts to the beginning of IMSIC realization. This can avoid the unnecessary resource allocation before checking failed. Signed-off-by: Yong-Xuan Wang Reviewed-by: Daniel Henrique Barboza Message-ID: <20250224025722.3999-2-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_imsic.c | 47 ++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index dc8162c0a7..241b12fef0 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -349,7 +349,19 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) CPUState *cpu = cpu_by_arch_id(imsic->hartid); CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; + /* Claim the CPU interrupt to be triggered by this IMSIC */ + if (riscv_cpu_claim_interrupts(rcpu, + (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { + error_setg(errp, "%s already claimed", + (imsic->mmode) ? "MEIP" : "SEIP"); + return; + } + if (!kvm_irqchip_in_kernel()) { + /* Create output IRQ lines */ + imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages); + qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages); + imsic->num_eistate = imsic->num_pages * imsic->num_irqs; imsic->eidelivery = g_new0(uint32_t, imsic->num_pages); imsic->eithreshold = g_new0(uint32_t, imsic->num_pages); @@ -361,18 +373,6 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) IMSIC_MMIO_SIZE(imsic->num_pages)); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &imsic->mmio); - /* Claim the CPU interrupt to be triggered by this IMSIC */ - if (riscv_cpu_claim_interrupts(rcpu, - (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { - error_setg(errp, "%s already claimed", - (imsic->mmode) ? "MEIP" : "SEIP"); - return; - } - - /* Create output IRQ lines */ - imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages); - qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages); - /* Force select AIA feature and setup CSR read-modify-write callback */ if (env) { if (!imsic->mmode) { @@ -381,8 +381,11 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) } else { rcpu->cfg.ext_smaia = true; } - riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, - riscv_imsic_rmw, imsic); + + if (!kvm_irqchip_in_kernel()) { + riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, + riscv_imsic_rmw, imsic); + } } msi_nonbroken = true; @@ -464,15 +467,17 @@ DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - for (i = 0; i < num_pages; i++) { - if (!i) { - qdev_connect_gpio_out_named(dev, NULL, i, - qdev_get_gpio_in(DEVICE(cpu), + if (!kvm_irqchip_in_kernel()) { + for (i = 0; i < num_pages; i++) { + if (!i) { + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), (mmode) ? IRQ_M_EXT : IRQ_S_EXT)); - } else { - qdev_connect_gpio_out_named(dev, NULL, i, - qdev_get_gpio_in(DEVICE(cpu), + } else { + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), IRQ_LOCAL_MAX + i - 1)); + } } } From 489840a012866440b7fe33e421b77cd234f83583 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 24 Feb 2025 10:57:19 +0800 Subject: [PATCH 2063/2892] hw/intc/aplic: refine the APLIC realize When the APLIC is emulated in the kernel, the GPIO output lines to CPUs can be remove. In this case the APLIC trigger CPU interrupts by KVM APIs. This patch also move the code that claim the CPU interrupts to the beginning of APLIC realization. This can avoid the unnecessary resource allocation before checking failed. Signed-off-by: Yong-Xuan Wang Reviewed-by: Daniel Henrique Barboza Message-ID: <20250224025722.3999-3-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 49 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 0974c6a5db..e5714267c0 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -893,6 +893,26 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) RISCVAPLICState *aplic = RISCV_APLIC(dev); if (riscv_use_emulated_aplic(aplic->msimode)) { + /* Create output IRQ lines for non-MSI mode */ + if (!aplic->msimode) { + /* Claim the CPU interrupt to be triggered by this APLIC */ + for (i = 0; i < aplic->num_harts; i++) { + RISCVCPU *cpu; + + cpu = RISCV_CPU(cpu_by_arch_id(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); + } + } + + aplic->external_irqs = g_malloc(sizeof(qemu_irq) * + aplic->num_harts); + qdev_init_gpio_out(dev, aplic->external_irqs, aplic->num_harts); + } + aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); aplic->state = g_new0(uint32_t, aplic->num_irqs); @@ -927,23 +947,6 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) } } - /* 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(cpu_by_arch_id(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; } @@ -1067,15 +1070,15 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, if (riscv_use_emulated_aplic(msimode)) { sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - } - if (!msimode) { - for (i = 0; i < num_harts; i++) { - CPUState *cpu = cpu_by_arch_id(hartid_base + i); + if (!msimode) { + for (i = 0; i < num_harts; i++) { + CPUState *cpu = cpu_by_arch_id(hartid_base + i); - qdev_connect_gpio_out_named(dev, NULL, i, - qdev_get_gpio_in(DEVICE(cpu), + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), (mmode) ? IRQ_M_EXT : IRQ_S_EXT)); + } } } From f0fe655502a4dde6b24383be73dbd81e774bf657 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 24 Feb 2025 10:57:20 +0800 Subject: [PATCH 2064/2892] hw/intc/aplic: refine kvm_msicfgaddr Let kvm_msicfgaddr use the same format with mmsicfgaddr and smsicfgaddr. Signed-off-by: Yong-Xuan Wang Reviewed-by: Daniel Henrique Barboza Message-ID: <20250224025722.3999-4-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index e5714267c0..5964cde7e0 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -181,8 +181,10 @@ void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr) { #ifdef CONFIG_KVM if (riscv_use_emulated_aplic(aplic->msimode)) { + addr >>= APLIC_xMSICFGADDR_PPN_SHIFT; aplic->kvm_msicfgaddr = extract64(addr, 0, 32); - aplic->kvm_msicfgaddrH = extract64(addr, 32, 32); + aplic->kvm_msicfgaddrH = extract64(addr, 32, 32) & + APLIC_xMSICFGADDRH_VALID_MASK; } #endif } @@ -403,12 +405,17 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, } } - if (aplic->mmode) { - msicfgaddr = aplic_m->mmsicfgaddr; - msicfgaddrH = aplic_m->mmsicfgaddrH; + if (aplic->kvm_splitmode) { + msicfgaddr = aplic->kvm_msicfgaddr; + msicfgaddrH = ((uint64_t)aplic->kvm_msicfgaddrH << 32); } else { - msicfgaddr = aplic_m->smsicfgaddr; - msicfgaddrH = aplic_m->smsicfgaddrH; + 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) & @@ -431,11 +438,6 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, addr |= (uint64_t)(guest_idx & APLIC_xMSICFGADDR_PPN_HART(lhxs)); addr <<= APLIC_xMSICFGADDR_PPN_SHIFT; - if (aplic->kvm_splitmode) { - addr |= aplic->kvm_msicfgaddr; - addr |= ((uint64_t)aplic->kvm_msicfgaddrH << 32); - } - address_space_stl_le(&address_space_memory, addr, eiid, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { From 1a65210876e79b4cbd40844f715eb24fb9abff14 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 24 Feb 2025 09:31:18 -0300 Subject: [PATCH 2065/2892] target/riscv/cpu: remove unneeded !kvm_enabled() check Remove the !kvm_enabled() check in kvm_riscv_reset_vcpu() since the function is already being gated by kvm_enabled() in riscv_cpu_reset_hold(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250224123120.1644186-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index e436083dbb..e110e0b909 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1611,9 +1611,6 @@ void kvm_riscv_reset_vcpu(RISCVCPU *cpu) CPURISCVState *env = &cpu->env; int i; - if (!kvm_enabled()) { - return; - } for (i = 0; i < 32; i++) { env->gpr[i] = 0; } From a1e61fc44b1a5fdad08206cbd7f015d1cc146713 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 24 Feb 2025 09:31:19 -0300 Subject: [PATCH 2066/2892] target/riscv/kvm: add kvm_riscv_reset_regs_csr() We're setting reset vals for KVM csrs during kvm_riscv_reset_vcpu(), but in no particular order and missing some of them (like env->mstatus). Create a helper to do that, unclogging reset_vcpu(), and initialize env->mstatus as well. Keep the regs in the same order they appear in struct kvm_riscv_csr from the KVM UAPI, similar to what kvm_riscv_(get|put)_regs_csr are doing. This will make a bit easier to add new KVM CSRs and to verify which values we're writing back to KVM during vcpu reset. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250224123120.1644186-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index e110e0b909..ba54eaa0b4 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -613,6 +613,19 @@ static int kvm_riscv_put_regs_core(CPUState *cs) return ret; } +static void kvm_riscv_reset_regs_csr(CPURISCVState *env) +{ + env->mstatus = 0; + env->mie = 0; + env->stvec = 0; + env->sscratch = 0; + env->sepc = 0; + env->scause = 0; + env->stval = 0; + env->mip = 0; + env->satp = 0; +} + static int kvm_riscv_get_regs_csr(CPUState *cs) { CPURISCVState *env = &RISCV_CPU(cs)->env; @@ -1617,14 +1630,8 @@ void kvm_riscv_reset_vcpu(RISCVCPU *cpu) env->pc = cpu->env.kernel_addr; env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */ env->gpr[11] = cpu->env.fdt_addr; /* a1 */ - env->satp = 0; - env->mie = 0; - env->stvec = 0; - env->sscratch = 0; - env->sepc = 0; - env->scause = 0; - env->stval = 0; - env->mip = 0; + + kvm_riscv_reset_regs_csr(env); } void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level) From 4db19d5b21e058e6eb3474b6be470d1184afaa9e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 24 Feb 2025 09:31:20 -0300 Subject: [PATCH 2067/2892] target/riscv/kvm: add missing KVM CSRs We're missing scounteren and senvcfg CSRs, both already present in the KVM UAPI. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20250224123120.1644186-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index ba54eaa0b4..7f3b59cb72 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -624,6 +624,8 @@ static void kvm_riscv_reset_regs_csr(CPURISCVState *env) env->stval = 0; env->mip = 0; env->satp = 0; + env->scounteren = 0; + env->senvcfg = 0; } static int kvm_riscv_get_regs_csr(CPUState *cs) @@ -639,6 +641,8 @@ static int kvm_riscv_get_regs_csr(CPUState *cs) KVM_RISCV_GET_CSR(cs, env, stval, env->stval); KVM_RISCV_GET_CSR(cs, env, sip, env->mip); KVM_RISCV_GET_CSR(cs, env, satp, env->satp); + KVM_RISCV_GET_CSR(cs, env, scounteren, env->scounteren); + KVM_RISCV_GET_CSR(cs, env, senvcfg, env->senvcfg); return 0; } @@ -656,6 +660,8 @@ static int kvm_riscv_put_regs_csr(CPUState *cs) KVM_RISCV_SET_CSR(cs, env, stval, env->stval); KVM_RISCV_SET_CSR(cs, env, sip, env->mip); KVM_RISCV_SET_CSR(cs, env, satp, env->satp); + KVM_RISCV_SET_CSR(cs, env, scounteren, env->scounteren); + KVM_RISCV_SET_CSR(cs, env, senvcfg, env->senvcfg); return 0; } From b61a4eb3f32ce74c5ffe001806f9e786788a546f Mon Sep 17 00:00:00 2001 From: John Snow Date: Sun, 23 Feb 2025 22:37:35 -0500 Subject: [PATCH 2068/2892] docs/qapidoc: support header-less freeform sections The code as written crashes when a free-form documentation block doesn't start with a heading or subheading, for example: | ## | # Just text, no heading. | ## The code will attempt to use the `node` variable uninitialized. To fix, create a generic block to insert the doc text into. (This patch also removes a lingering pylint warning in the QAPIDoc implementation that prevents getting a clean baseline to use for forthcoming additions.) Fixes: 43e0d14ee09a (docs/sphinx: fix extra stuff in TOC after freeform QMP sections) Signed-off-by: John Snow Message-ID: <20250224033741.222749-5-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Test updated to cover this] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 2 ++ tests/qapi-schema/doc-good.json | 4 ++++ tests/qapi-schema/doc-good.out | 3 +++ 3 files changed, 9 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 5f96b46270..5a4d7388b2 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -421,6 +421,8 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): node = self._start_new_heading(heading, len(leader)) if text == '': return + else: + node = nodes.container() self._parse_text_into_node(text, node) self._cur_doc = None diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index f64bf38d85..0a4f139f83 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -11,6 +11,10 @@ # = Section ## +## +# Just text, no heading. +## + ## # == Subsection # diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index ec277be91e..0a9da3efde 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -56,6 +56,9 @@ event EVT_BOXED Object doc freeform body= = Section +doc freeform + body= +Just text, no heading. doc freeform body= == Subsection From 5e4c466e6ad7d79ef5492b13deb110bb5e90d7c0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Sun, 23 Feb 2025 22:37:37 -0500 Subject: [PATCH 2069/2892] docs/qapidoc: remove example section support Since commit 3c5f6114 (qapi: remove "Example" doc section), Example sections no longer exist, so this support in qapidoc is now dead code. Signed-off-by: John Snow Message-ID: <20250224033741.222749-7-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 5a4d7388b2..61997fd21a 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -254,10 +254,6 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): section += dlnode return [section] - def _nodes_for_example(self, exampletext): - """Return list of doctree nodes for a code example snippet""" - return [nodes.literal_block(exampletext, exampletext)] - def _nodes_for_sections(self, doc): """Return list of doctree nodes for additional sections""" nodelist = [] @@ -275,10 +271,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): continue snode = self._make_section(section.tag) - if section.tag.startswith('Example'): - snode += self._nodes_for_example(dedent(section.text)) - else: - self._parse_text_into_node(dedent(section.text), snode) + self._parse_text_into_node(dedent(section.text), snode) nodelist.append(snode) return nodelist From dde279925c97b614e45351400bfcf9efaf732f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 24 Feb 2025 18:20:30 +0000 Subject: [PATCH 2070/2892] qapi: pluggable backend code generators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'qapi.backend.QAPIBackend' class defines an API contract for code generators. The current generator is put into a new class 'qapi.backend.QAPICBackend' and made to be the default impl. A custom generator can be requested using the '-k' arg which takes a fully qualified python class name qapi-gen.py -B the.python.module.QAPIMyBackend This allows out of tree code to use the QAPI generator infrastructure to create new language bindings for QAPI schemas. This has the caveat that the QAPI generator APIs are not guaranteed stable, so consumers of this feature may have to update their code to be compatible with future QEMU releases. Signed-off-by: Daniel P. Berrangé Message-ID: <20250224182030.2089959-1-berrange@redhat.com> Reviewed-by: Markus Armbruster [Error checking and messages tweaked] Signed-off-by: Markus Armbruster --- scripts/qapi/backend.py | 63 ++++++++++++++++++++++++++++++++ scripts/qapi/main.py | 80 +++++++++++++++++++++++------------------ 2 files changed, 108 insertions(+), 35 deletions(-) create mode 100644 scripts/qapi/backend.py diff --git a/scripts/qapi/backend.py b/scripts/qapi/backend.py new file mode 100644 index 0000000000..14e60aa67a --- /dev/null +++ b/scripts/qapi/backend.py @@ -0,0 +1,63 @@ +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +from abc import ABC, abstractmethod + +from .commands import gen_commands +from .events import gen_events +from .features import gen_features +from .introspect import gen_introspect +from .schema import QAPISchema +from .types import gen_types +from .visit import gen_visit + + +class QAPIBackend(ABC): + + @abstractmethod + def generate(self, + schema: QAPISchema, + output_dir: str, + prefix: str, + unmask: bool, + builtins: bool, + gen_tracing: bool) -> None: + """ + Generate code for the given schema into the target directory. + + :param schema: The primary QAPI schema object. + :param output_dir: The output directory to store generated code. + :param prefix: Optional C-code prefix for symbol names. + :param unmask: Expose non-ABI names through introspection? + :param builtins: Generate code for built-in types? + + :raise QAPIError: On failures. + """ + + +class QAPICBackend(QAPIBackend): + + def generate(self, + schema: QAPISchema, + output_dir: str, + prefix: str, + unmask: bool, + builtins: bool, + gen_tracing: bool) -> None: + """ + Generate C code for the given schema into the target directory. + + :param schema_file: The primary QAPI schema file. + :param output_dir: The output directory to store generated code. + :param prefix: Optional C-code prefix for symbol names. + :param unmask: Expose non-ABI names through introspection? + :param builtins: Generate code for built-in types? + + :raise QAPIError: On failures. + """ + gen_types(schema, output_dir, prefix, builtins) + gen_features(schema, output_dir, prefix) + gen_visit(schema, output_dir, prefix, builtins) + gen_commands(schema, output_dir, prefix, gen_tracing) + gen_events(schema, output_dir, prefix) + gen_introspect(schema, output_dir, prefix, unmask) diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index 324081b9fc..5b4679abcf 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -8,18 +8,14 @@ This is the main entry point for generating C code from the QAPI schema. """ import argparse +from importlib import import_module import sys from typing import Optional -from .commands import gen_commands +from .backend import QAPIBackend, QAPICBackend from .common import must_match from .error import QAPIError -from .events import gen_events -from .features import gen_features -from .introspect import gen_introspect from .schema import QAPISchema -from .types import gen_types -from .visit import gen_visit def invalid_prefix_char(prefix: str) -> Optional[str]: @@ -29,32 +25,42 @@ def invalid_prefix_char(prefix: str) -> Optional[str]: return None -def generate(schema_file: str, - output_dir: str, - prefix: str, - unmask: bool = False, - builtins: bool = False, - gen_tracing: bool = False) -> None: - """ - Generate C code for the given schema into the target directory. +def create_backend(path: str) -> QAPIBackend: + if path is None: + return QAPICBackend() - :param schema_file: The primary QAPI schema file. - :param output_dir: The output directory to store generated code. - :param prefix: Optional C-code prefix for symbol names. - :param unmask: Expose non-ABI names through introspection? - :param builtins: Generate code for built-in types? + module_path, dot, class_name = path.rpartition('.') + if not dot: + print("argument of -B must be of the form MODULE.CLASS", + file=sys.stderr) + sys.exit(1) - :raise QAPIError: On failures. - """ - assert invalid_prefix_char(prefix) is None + try: + mod = import_module(module_path) + except Exception as ex: + print(f"unable to import '{module_path}': {ex}", file=sys.stderr) + sys.exit(1) - schema = QAPISchema(schema_file) - gen_types(schema, output_dir, prefix, builtins) - gen_features(schema, output_dir, prefix) - gen_visit(schema, output_dir, prefix, builtins) - gen_commands(schema, output_dir, prefix, gen_tracing) - gen_events(schema, output_dir, prefix) - gen_introspect(schema, output_dir, prefix, unmask) + try: + klass = getattr(mod, class_name) + except AttributeError: + print(f"module '{module_path}' has no class '{class_name}'", + file=sys.stderr) + sys.exit(1) + + try: + backend = klass() + except Exception as ex: + print(f"backend '{path}' cannot be instantiated: {ex}", + file=sys.stderr) + sys.exit(1) + + if not isinstance(backend, QAPIBackend): + print(f"backend '{path}' must be an instance of QAPIBackend", + file=sys.stderr) + sys.exit(1) + + return backend def main() -> int: @@ -77,6 +83,8 @@ def main() -> int: parser.add_argument('-u', '--unmask-non-abi-names', action='store_true', dest='unmask', help="expose non-ABI names in introspection") + parser.add_argument('-B', '--backend', default=None, + help="Python module name for code generator") # Option --suppress-tracing exists so we can avoid solving build system # problems. TODO Drop it when we no longer need it. @@ -93,12 +101,14 @@ def main() -> int: return 1 try: - generate(args.schema, - output_dir=args.output_dir, - prefix=args.prefix, - unmask=args.unmask, - builtins=args.builtins, - gen_tracing=not args.suppress_tracing) + schema = QAPISchema(args.schema) + backend = create_backend(args.backend) + backend.generate(schema, + output_dir=args.output_dir, + prefix=args.prefix, + unmask=args.unmask, + builtins=args.builtins, + gen_tracing=not args.suppress_tracing) except QAPIError as err: print(err, file=sys.stderr) return 1 From 8d127aa866a42b36ec6391b025999059dd47cbfd Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:05 +0100 Subject: [PATCH 2071/2892] Add support for etc/hardware-info fw_cfg file edk2 looks for the etc/hardware-info fw_cfg file to discover hardware which can not easily be found in other ways. Entries consist of a header with hardware type and entry size (HARDWARE_INFO_HEADER), followed by the actual hardware description (which is type specific). The file can have multiple entries. This patch adds the infrastructure to add entries to the file and an entry struct for simple devices (HARDWARE_INFO_SIMPLE_DEVICE) which have an mmio address only. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-2-kraxel@redhat.com> --- hw/uefi/hardware-info.c | 31 +++++++++++++++++++++++++++++ hw/uefi/meson.build | 1 + include/hw/uefi/hardware-info.h | 35 +++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 hw/uefi/hardware-info.c create mode 100644 hw/uefi/meson.build create mode 100644 include/hw/uefi/hardware-info.h diff --git a/hw/uefi/hardware-info.c b/hw/uefi/hardware-info.c new file mode 100644 index 0000000000..930502a4df --- /dev/null +++ b/hw/uefi/hardware-info.c @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * pass hardware information to uefi + * + * see OvmfPkg/Library/HardwareInfoLib/ in edk2 + */ + +#include "qemu/osdep.h" + +#include "hw/nvram/fw_cfg.h" +#include "hw/uefi/hardware-info.h" + +static void *blob; +static uint64_t blobsize; + +void hardware_info_register(HARDWARE_INFO_TYPE type, void *info, uint64_t infosize) +{ + HARDWARE_INFO_HEADER hdr = { + .type.value = cpu_to_le64(type), + .size = cpu_to_le64(infosize), + }; + + blob = g_realloc(blob, blobsize + sizeof(hdr) + infosize); + memcpy(blob + blobsize, &hdr, sizeof(hdr)); + blobsize += sizeof(hdr); + memcpy(blob + blobsize, info, infosize); + blobsize += infosize; + + fw_cfg_modify_file(fw_cfg_find(), "etc/hardware-info", blob, blobsize); +} diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build new file mode 100644 index 0000000000..a8b1689412 --- /dev/null +++ b/hw/uefi/meson.build @@ -0,0 +1 @@ +system_ss.add(files('hardware-info.c')) diff --git a/include/hw/uefi/hardware-info.h b/include/hw/uefi/hardware-info.h new file mode 100644 index 0000000000..94c38cff20 --- /dev/null +++ b/include/hw/uefi/hardware-info.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * pass hardware information to uefi + * + * see OvmfPkg/Library/HardwareInfoLib/ in edk2 + */ +#ifndef QEMU_UEFI_HARDWARE_INFO_H +#define QEMU_UEFI_HARDWARE_INFO_H + +/* data structures */ + +typedef enum { + HardwareInfoTypeUndefined = 0, + HardwareInfoTypeHostBridge = 1, + HardwareInfoQemuUefiVars = 2, +} HARDWARE_INFO_TYPE; + +typedef struct { + union { + uint64_t uint64; + HARDWARE_INFO_TYPE value; + } type; + uint64_t size; +} HARDWARE_INFO_HEADER; + +typedef struct { + uint64_t mmio_address; +} HARDWARE_INFO_SIMPLE_DEVICE; + +/* qemu functions */ + +void hardware_info_register(HARDWARE_INFO_TYPE type, void *info, uint64_t size); + +#endif /* QEMU_UEFI_HARDWARE_INFO_H */ From 995496fd216a6bf6f709c52992c0c423565a63b7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:06 +0100 Subject: [PATCH 2072/2892] hw/uefi: add include/hw/uefi/var-service-api.h This file defines the register interface of the uefi-vars device. It's only a handful of registers: magic value, command and status registers, location and size of the communication buffer. Reviewed-by: Laszlo Ersek Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-3-kraxel@redhat.com> --- include/hw/uefi/var-service-api.h | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 include/hw/uefi/var-service-api.h diff --git a/include/hw/uefi/var-service-api.h b/include/hw/uefi/var-service-api.h new file mode 100644 index 0000000000..0d71638f3e --- /dev/null +++ b/include/hw/uefi/var-service-api.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi-vars device - API of the virtual device for guest/host communication. + */ +#ifndef QEMU_UEFI_VAR_SERVICE_API_H +#define QEMU_UEFI_VAR_SERVICE_API_H + +/* qom: device names */ +#define TYPE_UEFI_VARS_X64 "uefi-vars-x64" +#define TYPE_UEFI_VARS_SYSBUS "uefi-vars-sysbus" + +/* sysbus: fdt node path */ +#define UEFI_VARS_FDT_NODE "qemu-uefi-vars" +#define UEFI_VARS_FDT_COMPAT "qemu,uefi-vars" + +/* registers */ +#define UEFI_VARS_REG_MAGIC 0x00 /* 16 bit */ +#define UEFI_VARS_REG_CMD_STS 0x02 /* 16 bit */ +#define UEFI_VARS_REG_BUFFER_SIZE 0x04 /* 32 bit */ +#define UEFI_VARS_REG_DMA_BUFFER_ADDR_LO 0x08 /* 32 bit */ +#define UEFI_VARS_REG_DMA_BUFFER_ADDR_HI 0x0c /* 32 bit */ +#define UEFI_VARS_REG_PIO_BUFFER_TRANSFER 0x10 /* 8-64 bit */ +#define UEFI_VARS_REG_PIO_BUFFER_CRC32C 0x18 /* 32 bit (read-only) */ +#define UEFI_VARS_REG_FLAGS 0x1c /* 32 bit */ +#define UEFI_VARS_REGS_SIZE 0x20 + +/* flags register */ +#define UEFI_VARS_FLAG_USE_PIO (1 << 0) + +/* magic value */ +#define UEFI_VARS_MAGIC_VALUE 0xef1 + +/* command values */ +#define UEFI_VARS_CMD_RESET 0x01 +#define UEFI_VARS_CMD_DMA_MM 0x02 +#define UEFI_VARS_CMD_PIO_MM 0x03 +#define UEFI_VARS_CMD_PIO_ZERO_OFFSET 0x04 + +/* status values */ +#define UEFI_VARS_STS_SUCCESS 0x00 +#define UEFI_VARS_STS_BUSY 0x01 +#define UEFI_VARS_STS_ERR_UNKNOWN 0x10 +#define UEFI_VARS_STS_ERR_NOT_SUPPORTED 0x11 +#define UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE 0x12 + + +#endif /* QEMU_UEFI_VAR_SERVICE_API_H */ From 8614eb902b1a63e837e5a8fd871b8c64e700d46f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:07 +0100 Subject: [PATCH 2073/2892] hw/uefi: add include/hw/uefi/var-service-edk2.h A bunch of #defines and structs copied over from edk2, mostly needed to decode and encode the messages in the communication buffer. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-4-kraxel@redhat.com> --- include/hw/uefi/var-service-edk2.h | 227 +++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 include/hw/uefi/var-service-edk2.h diff --git a/include/hw/uefi/var-service-edk2.h b/include/hw/uefi/var-service-edk2.h new file mode 100644 index 0000000000..c743a8df94 --- /dev/null +++ b/include/hw/uefi/var-service-edk2.h @@ -0,0 +1,227 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi-vars device - structs and defines from edk2 + * + * Note: The edk2 UINTN type has been mapped to uint64_t, + * so the structs are compatible with 64bit edk2 builds. + */ +#ifndef QEMU_UEFI_VAR_SERVICE_EDK2_H +#define QEMU_UEFI_VAR_SERVICE_EDK2_H + +#include "qemu/uuid.h" + +#define MAX_BIT 0x8000000000000000ULL +#define ENCODE_ERROR(StatusCode) (MAX_BIT | (StatusCode)) +#define EFI_SUCCESS 0 +#define EFI_INVALID_PARAMETER ENCODE_ERROR(2) +#define EFI_UNSUPPORTED ENCODE_ERROR(3) +#define EFI_BAD_BUFFER_SIZE ENCODE_ERROR(4) +#define EFI_BUFFER_TOO_SMALL ENCODE_ERROR(5) +#define EFI_WRITE_PROTECTED ENCODE_ERROR(8) +#define EFI_OUT_OF_RESOURCES ENCODE_ERROR(9) +#define EFI_NOT_FOUND ENCODE_ERROR(14) +#define EFI_ACCESS_DENIED ENCODE_ERROR(15) +#define EFI_ALREADY_STARTED ENCODE_ERROR(20) +#define EFI_SECURITY_VIOLATION ENCODE_ERROR(26) + +#define EFI_VARIABLE_NON_VOLATILE 0x01 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x02 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x04 +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x08 +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x10 /* deprecated */ +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x20 +#define EFI_VARIABLE_APPEND_WRITE 0x40 + +/* SecureBootEnable */ +#define SECURE_BOOT_ENABLE 1 +#define SECURE_BOOT_DISABLE 0 + +/* SecureBoot */ +#define SECURE_BOOT_MODE_ENABLE 1 +#define SECURE_BOOT_MODE_DISABLE 0 + +/* CustomMode */ +#define CUSTOM_SECURE_BOOT_MODE 1 +#define STANDARD_SECURE_BOOT_MODE 0 + +/* SetupMode */ +#define SETUP_MODE 1 +#define USER_MODE 0 + +typedef uint64_t efi_status; +typedef struct mm_header mm_header; + +/* EFI_MM_COMMUNICATE_HEADER */ +struct mm_header { + QemuUUID guid; + uint64_t length; +}; + +/* --- EfiSmmVariableProtocol ---------------------------------------- */ + +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE 1 +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME 2 +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE 3 +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO 4 +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT 5 +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE 6 +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE 8 +#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE 11 + +typedef struct mm_variable mm_variable; +typedef struct mm_variable_access mm_variable_access; +typedef struct mm_next_variable mm_next_variable; +typedef struct mm_next_variable mm_lock_variable; +typedef struct mm_variable_info mm_variable_info; +typedef struct mm_get_payload_size mm_get_payload_size; + +/* SMM_VARIABLE_COMMUNICATE_HEADER */ +struct mm_variable { + uint64_t function; + uint64_t status; +}; + +/* SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE */ +struct QEMU_PACKED mm_variable_access { + QemuUUID guid; + uint64_t data_size; + uint64_t name_size; + uint32_t attributes; + /* Name */ + /* Data */ +}; + +/* SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME */ +struct mm_next_variable { + QemuUUID guid; + uint64_t name_size; + /* Name */ +}; + +/* SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO */ +struct QEMU_PACKED mm_variable_info { + uint64_t max_storage_size; + uint64_t free_storage_size; + uint64_t max_variable_size; + uint32_t attributes; +}; + +/* SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE */ +struct mm_get_payload_size { + uint64_t payload_size; +}; + +/* --- VarCheckPolicyLibMmiHandler ----------------------------------- */ + +#define VAR_CHECK_POLICY_COMMAND_DISABLE 0x01 +#define VAR_CHECK_POLICY_COMMAND_IS_ENABLED 0x02 +#define VAR_CHECK_POLICY_COMMAND_REGISTER 0x03 +#define VAR_CHECK_POLICY_COMMAND_DUMP 0x04 +#define VAR_CHECK_POLICY_COMMAND_LOCK 0x05 + +typedef struct mm_check_policy mm_check_policy; +typedef struct mm_check_policy_is_enabled mm_check_policy_is_enabled; +typedef struct mm_check_policy_dump_params mm_check_policy_dump_params; + +/* VAR_CHECK_POLICY_COMM_HEADER */ +struct QEMU_PACKED mm_check_policy { + uint32_t signature; + uint32_t revision; + uint32_t command; + uint64_t result; +}; + +/* VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS */ +struct QEMU_PACKED mm_check_policy_is_enabled { + uint8_t state; +}; + +/* VAR_CHECK_POLICY_COMM_DUMP_PARAMS */ +struct QEMU_PACKED mm_check_policy_dump_params { + uint32_t page_requested; + uint32_t total_size; + uint32_t page_size; + uint8_t has_more; +}; + +/* --- Edk2VariablePolicyProtocol ------------------------------------ */ + +#define VARIABLE_POLICY_ENTRY_REVISION 0x00010000 + +#define VARIABLE_POLICY_TYPE_NO_LOCK 0 +#define VARIABLE_POLICY_TYPE_LOCK_NOW 1 +#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2 +#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3 + +typedef struct variable_policy_entry variable_policy_entry; +typedef struct variable_lock_on_var_state variable_lock_on_var_state; + +/* VARIABLE_POLICY_ENTRY */ +struct variable_policy_entry { + uint32_t version; + uint16_t size; + uint16_t offset_to_name; + QemuUUID namespace; + uint32_t min_size; + uint32_t max_size; + uint32_t attributes_must_have; + uint32_t attributes_cant_have; + uint8_t lock_policy_type; + uint8_t padding[3]; + /* LockPolicy */ + /* Name */ +}; + +/* VARIABLE_LOCK_ON_VAR_STATE_POLICY */ +struct variable_lock_on_var_state { + QemuUUID namespace; + uint8_t value; + uint8_t padding; + /* Name */ +}; + +/* --- variable authentication --------------------------------------- */ + +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +typedef struct efi_time efi_time; +typedef struct efi_siglist efi_siglist; +typedef struct variable_auth_2 variable_auth_2; + +/* EFI_TIME */ +struct efi_time { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t pad1; + uint32_t nanosecond; + int16_t timezone; + uint8_t daylight; + uint8_t pad2; +}; + +/* EFI_SIGNATURE_LIST */ +struct efi_siglist { + QemuUUID guid_type; + uint32_t siglist_size; + uint32_t header_size; + uint32_t sig_size; +}; + +/* EFI_VARIABLE_AUTHENTICATION_2 */ +struct variable_auth_2 { + struct efi_time timestamp; + + /* WIN_CERTIFICATE_UEFI_GUID */ + uint32_t hdr_length; + uint16_t hdr_revision; + uint16_t hdr_cert_type; + QemuUUID guid_cert_type; + uint8_t cert_data[]; +}; + +#endif /* QEMU_UEFI_VAR_SERVICE_EDK2_H */ From 8b7ed5845cec8d4308707e53469d328b6ad0d9ed Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:08 +0100 Subject: [PATCH 2074/2892] hw/uefi: add include/hw/uefi/var-service.h Add state structs and function declarations for the uefi-vars device. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-5-kraxel@redhat.com> --- include/hw/uefi/var-service.h | 191 ++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 include/hw/uefi/var-service.h diff --git a/include/hw/uefi/var-service.h b/include/hw/uefi/var-service.h new file mode 100644 index 0000000000..f7ceac4ce2 --- /dev/null +++ b/include/hw/uefi/var-service.h @@ -0,0 +1,191 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi-vars device - state struct and function prototypes + */ +#ifndef QEMU_UEFI_VAR_SERVICE_H +#define QEMU_UEFI_VAR_SERVICE_H + +#include "qemu/uuid.h" +#include "qemu/queue.h" + +#include "hw/uefi/var-service-edk2.h" + +#define MAX_BUFFER_SIZE (64 * 1024) + +typedef struct uefi_variable uefi_variable; +typedef struct uefi_var_policy uefi_var_policy; +typedef struct uefi_vars_state uefi_vars_state; + +typedef struct uefi_vars_cert uefi_vars_cert; +typedef struct uefi_vars_hash uefi_vars_hash; +typedef struct uefi_vars_siglist uefi_vars_siglist; + +struct uefi_variable { + QemuUUID guid; + uint16_t *name; + uint32_t name_size; + uint32_t attributes; + void *data; + uint32_t data_size; + efi_time time; + void *digest; + uint32_t digest_size; + QTAILQ_ENTRY(uefi_variable) next; +}; + +struct uefi_var_policy { + variable_policy_entry *entry; + uint32_t entry_size; + uint16_t *name; + uint32_t name_size; + + /* number of hashmarks (wildcard character) in name */ + uint32_t hashmarks; + + QTAILQ_ENTRY(uefi_var_policy) next; +}; + +struct uefi_vars_state { + MemoryRegion mr; + uint16_t sts; + uint32_t buf_size; + uint32_t buf_addr_lo; + uint32_t buf_addr_hi; + uint8_t *buffer; + QTAILQ_HEAD(, uefi_variable) variables; + QTAILQ_HEAD(, uefi_var_policy) var_policies; + + /* pio transfer buffer */ + uint32_t pio_xfer_offset; + uint8_t *pio_xfer_buffer; + + /* boot phases */ + bool end_of_dxe; + bool ready_to_boot; + bool exit_boot_service; + bool policy_locked; + + /* storage accounting */ + uint64_t max_storage; + uint64_t used_storage; + + /* config options */ + char *jsonfile; + int jsonfd; + bool force_secure_boot; + bool disable_custom_mode; + bool use_pio; +}; + +struct uefi_vars_cert { + QTAILQ_ENTRY(uefi_vars_cert) next; + QemuUUID owner; + uint64_t size; + uint8_t data[]; +}; + +struct uefi_vars_hash { + QTAILQ_ENTRY(uefi_vars_hash) next; + QemuUUID owner; + uint8_t data[]; +}; + +struct uefi_vars_siglist { + QTAILQ_HEAD(, uefi_vars_cert) x509; + QTAILQ_HEAD(, uefi_vars_hash) sha256; +}; + +/* vars-service-guid.c */ +extern const QemuUUID EfiGlobalVariable; +extern const QemuUUID EfiImageSecurityDatabase; +extern const QemuUUID EfiCustomModeEnable; +extern const QemuUUID EfiSecureBootEnableDisable; + +extern const QemuUUID EfiCertSha256Guid; +extern const QemuUUID EfiCertSha384Guid; +extern const QemuUUID EfiCertSha512Guid; +extern const QemuUUID EfiCertRsa2048Guid; +extern const QemuUUID EfiCertX509Guid; +extern const QemuUUID EfiCertTypePkcs7Guid; + +extern const QemuUUID EfiSmmVariableProtocolGuid; +extern const QemuUUID VarCheckPolicyLibMmiHandlerGuid; + +extern const QemuUUID EfiEndOfDxeEventGroupGuid; +extern const QemuUUID EfiEventReadyToBootGuid; +extern const QemuUUID EfiEventExitBootServicesGuid; + +/* vars-service-utils.c */ +gboolean uefi_str_is_valid(const uint16_t *str, size_t len, + gboolean must_be_null_terminated); +size_t uefi_strlen(const uint16_t *str, size_t len); +gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen, + gboolean wildcards_in_a); +gboolean uefi_str_equal(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen); +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size); +int uefi_time_compare(efi_time *a, efi_time *b); +void uefi_trace_variable(const char *action, QemuUUID guid, + const uint16_t *name, uint64_t name_size); +void uefi_trace_status(const char *action, efi_status status); + +/* vars-service-core.c */ +extern const VMStateDescription vmstate_uefi_vars; +void uefi_vars_init(Object *obj, uefi_vars_state *uv); +void uefi_vars_realize(uefi_vars_state *uv, Error **errp); +void uefi_vars_hard_reset(uefi_vars_state *uv); + +/* vars-service-json.c */ +void uefi_vars_json_init(uefi_vars_state *uv, Error **errp); +void uefi_vars_json_save(uefi_vars_state *uv); +void uefi_vars_json_load(uefi_vars_state *uv, Error **errp); + +/* vars-service-vars.c */ +extern const VMStateDescription vmstate_uefi_variable; +uefi_variable *uefi_vars_find_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, + uint64_t name_size); +void uefi_vars_set_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size, + uint32_t attributes, + void *data, uint64_t data_size); +void uefi_vars_clear_volatile(uefi_vars_state *uv); +void uefi_vars_clear_all(uefi_vars_state *uv); +void uefi_vars_update_storage(uefi_vars_state *uv); +uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv); + +/* vars-service-auth.c */ +bool uefi_vars_is_sb_pk(uefi_variable *var); +bool uefi_vars_is_sb_any(uefi_variable *var); +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, + mm_variable_access *va, void *data); +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var); +void uefi_vars_auth_init(uefi_vars_state *uv); + +/* vars-service-pkcs7.c */ +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, + void **digest, uint32_t *digest_size, + mm_variable_access *va, void *data); + +/* vars-service-siglist.c */ +void uefi_vars_siglist_init(uefi_vars_siglist *siglist); +void uefi_vars_siglist_free(uefi_vars_siglist *siglist); +void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, + void *data, uint64_t size); +uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist); +void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, + void *data, uint64_t size); + +/* vars-service-policy.c */ +extern const VMStateDescription vmstate_uefi_var_policy; +efi_status uefi_vars_policy_check(uefi_vars_state *uv, + uefi_variable *var, + gboolean is_newvar); +void uefi_vars_policies_clear(uefi_vars_state *uv); +uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, + variable_policy_entry *pe); +uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv); + +#endif /* QEMU_UEFI_VAR_SERVICE_H */ From 231b6c9ee8d4cf4ebaae4e4da31ff73c2ec8e6e9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:09 +0100 Subject: [PATCH 2075/2892] hw/uefi: add var-service-guid.c Add variables for a bunch of UEFI GUIDs we will need. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-6-kraxel@redhat.com> --- hw/uefi/var-service-guid.c | 99 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 hw/uefi/var-service-guid.c diff --git a/hw/uefi/var-service-guid.c b/hw/uefi/var-service-guid.c new file mode 100644 index 0000000000..eba3655c8d --- /dev/null +++ b/hw/uefi/var-service-guid.c @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - GUIDs + */ + +#include "qemu/osdep.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +/* variable namespaces */ + +const QemuUUID EfiGlobalVariable = { + .data = UUID_LE(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, + 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c) +}; + +const QemuUUID EfiImageSecurityDatabase = { + .data = UUID_LE(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, + 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) +}; + +const QemuUUID EfiCustomModeEnable = { + .data = UUID_LE(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, + 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f) +}; + +const QemuUUID EfiSecureBootEnableDisable = { + .data = UUID_LE(0xf0a30bc7, 0xaf08, 0x4556, 0x99, 0xc4, + 0x0, 0x10, 0x9, 0xc9, 0x3a, 0x44) +}; + +/* signatures */ + +const QemuUUID EfiCertSha256Guid = { + .data = UUID_LE(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, + 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) +}; + +const QemuUUID EfiCertSha384Guid = { + .data = UUID_LE(0xff3e5307, 0x9fd0, 0x48c9, 0x85, 0xf1, + 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1) +}; + +const QemuUUID EfiCertSha512Guid = { + .data = UUID_LE(0x93e0fae, 0xa6c4, 0x4f50, 0x9f, 0x1b, + 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a) +}; + +const QemuUUID EfiCertRsa2048Guid = { + .data = UUID_LE(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, + 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6) +}; + +const QemuUUID EfiCertX509Guid = { + .data = UUID_LE(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, + 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) +}; + +const QemuUUID EfiCertTypePkcs7Guid = { + .data = UUID_LE(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, + 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7) +}; + +/* + * mm_header.guid values that the guest DXE/BDS phases use for + * sending requests to management mode + */ + +const QemuUUID EfiSmmVariableProtocolGuid = { + .data = UUID_LE(0xed32d533, 0x99e6, 0x4209, 0x9c, 0xc0, + 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7) +}; + +const QemuUUID VarCheckPolicyLibMmiHandlerGuid = { + .data = UUID_LE(0xda1b0d11, 0xd1a7, 0x46c4, 0x9d, 0xc9, + 0xf3, 0x71, 0x48, 0x75, 0xc6, 0xeb) +}; + +/* + * mm_header.guid values that the guest DXE/BDS phases use for + * reporting event groups being signaled to management mode + */ + +const QemuUUID EfiEndOfDxeEventGroupGuid = { + .data = UUID_LE(0x02ce967a, 0xdd7e, 0x4FFc, 0x9e, 0xe7, + 0x81, 0x0c, 0xF0, 0x47, 0x08, 0x80) +}; + +const QemuUUID EfiEventReadyToBootGuid = { + .data = UUID_LE(0x7ce88Fb3, 0x4bd7, 0x4679, 0x87, 0xa8, + 0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b) +}; + +const QemuUUID EfiEventExitBootServicesGuid = { + .data = UUID_LE(0x27abF055, 0xb1b8, 0x4c26, 0x80, 0x48, + 0x74, 0x8F, 0x37, 0xba, 0xa2, 0xdF) +}; From 1ebc319c8ca7ad8af350026662ae18fdcb8b0dac Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:10 +0100 Subject: [PATCH 2076/2892] hw/uefi: add var-service-utils.c Add utility functions. Helpers for UEFI (ucs2) string handling. Helpers for readable trace messages. Compare UEFI time stamps. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-7-kraxel@redhat.com> --- hw/uefi/var-service-utils.c | 241 ++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 hw/uefi/var-service-utils.c diff --git a/hw/uefi/var-service-utils.c b/hw/uefi/var-service-utils.c new file mode 100644 index 0000000000..c9ef46570f --- /dev/null +++ b/hw/uefi/var-service-utils.c @@ -0,0 +1,241 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - helper functions for ucs2 strings and tracing + */ +#include "qemu/osdep.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +#include "trace/trace-hw_uefi.h" + +/* ------------------------------------------------------------------ */ + +/* + * string helper functions. + * + * Most of the time uefi ucs2 strings are NULL-terminated, except + * sometimes when they are not (for example in variable policies). + */ + +gboolean uefi_str_is_valid(const uint16_t *str, size_t len, + gboolean must_be_null_terminated) +{ + size_t pos = 0; + + for (;;) { + if (pos == len) { + if (must_be_null_terminated) { + return false; + } else { + return true; + } + } + switch (str[pos]) { + case 0: + /* end of string */ + return true; + case 0xd800 ... 0xdfff: + /* reject surrogates */ + return false; + default: + /* char is good, check next */ + break; + } + pos++; + } +} + +size_t uefi_strlen(const uint16_t *str, size_t len) +{ + size_t pos = 0; + + for (;;) { + if (pos == len) { + return pos; + } + if (str[pos] == 0) { + return pos; + } + pos++; + } +} + +gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen, + gboolean wildcards_in_a) +{ + size_t pos = 0; + + alen = alen / 2; + blen = blen / 2; + for (;;) { + if (pos == alen && pos == blen) { + return true; + } + if (pos == alen && b[pos] == 0) { + return true; + } + if (pos == blen && a[pos] == 0) { + return true; + } + if (pos == alen || pos == blen) { + return false; + } + if (a[pos] == 0 && b[pos] == 0) { + return true; + } + + if (wildcards_in_a && a[pos] == '#') { + if (!isxdigit(b[pos])) { + return false; + } + } else { + if (a[pos] != b[pos]) { + return false; + } + } + pos++; + } +} + +gboolean uefi_str_equal(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen) +{ + return uefi_str_equal_ex(a, alen, b, blen, false); +} + +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size) +{ + char *str = g_malloc0(ucs2_size / 2 + 1); + int i; + + for (i = 0; i * 2 < ucs2_size; i++) { + if (ucs2[i] == 0) { + break; + } + if (ucs2[i] < 128) { + str[i] = ucs2[i]; + } else { + str[i] = '?'; + } + } + str[i] = 0; + return str; +} + +/* ------------------------------------------------------------------ */ +/* time helper functions */ + +int uefi_time_compare(efi_time *a, efi_time *b) +{ + if (a->year < b->year) { + return -1; + } + if (a->year > b->year) { + return 1; + } + + if (a->month < b->month) { + return -1; + } + if (a->month > b->month) { + return 1; + } + + if (a->day < b->day) { + return -1; + } + if (a->day > b->day) { + return 1; + } + + if (a->hour < b->hour) { + return -1; + } + if (a->hour > b->hour) { + return 1; + } + + if (a->minute < b->minute) { + return -1; + } + if (a->minute > b->minute) { + return 1; + } + + if (a->second < b->second) { + return -1; + } + if (a->second > b->second) { + return 1; + } + + if (a->nanosecond < b->nanosecond) { + return -1; + } + if (a->nanosecond > b->nanosecond) { + return 1; + } + + return 0; +} + +/* ------------------------------------------------------------------ */ +/* tracing helper functions */ + +void uefi_trace_variable(const char *action, QemuUUID guid, + const uint16_t *name, uint64_t name_size) +{ + QemuUUID be = qemu_uuid_bswap(guid); + char *str_uuid = qemu_uuid_unparse_strdup(&be); + char *str_name = uefi_ucs2_to_ascii(name, name_size); + + trace_uefi_variable(action, str_name, name_size, str_uuid); + + g_free(str_name); + g_free(str_uuid); +} + +void uefi_trace_status(const char *action, efi_status status) +{ + switch (status) { + case EFI_SUCCESS: + trace_uefi_status(action, "success"); + break; + case EFI_INVALID_PARAMETER: + trace_uefi_status(action, "invalid parameter"); + break; + case EFI_UNSUPPORTED: + trace_uefi_status(action, "unsupported"); + break; + case EFI_BAD_BUFFER_SIZE: + trace_uefi_status(action, "bad buffer size"); + break; + case EFI_BUFFER_TOO_SMALL: + trace_uefi_status(action, "buffer too small"); + break; + case EFI_WRITE_PROTECTED: + trace_uefi_status(action, "write protected"); + break; + case EFI_OUT_OF_RESOURCES: + trace_uefi_status(action, "out of resources"); + break; + case EFI_NOT_FOUND: + trace_uefi_status(action, "not found"); + break; + case EFI_ACCESS_DENIED: + trace_uefi_status(action, "access denied"); + break; + case EFI_ALREADY_STARTED: + trace_uefi_status(action, "already started"); + break; + case EFI_SECURITY_VIOLATION: + trace_uefi_status(action, "security violation"); + break; + default: + trace_uefi_status(action, "unknown error"); + break; + } +} From db1ecfb473ac58f2bd065ca6f2a50c6294ff9169 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:11 +0100 Subject: [PATCH 2077/2892] hw/uefi: add var-service-vars.c This is the uefi variable service (EfiSmmVariableProtocol), providing functions for listing, reading and updating variables. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-8-kraxel@redhat.com> --- hw/uefi/var-service-vars.c | 725 +++++++++++++++++++++++++++++++++++++ 1 file changed, 725 insertions(+) create mode 100644 hw/uefi/var-service-vars.c diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c new file mode 100644 index 0000000000..7f98d77a38 --- /dev/null +++ b/hw/uefi/var-service-vars.c @@ -0,0 +1,725 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - EfiSmmVariableProtocol implementation + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" +#include "migration/vmstate.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" +#include "hw/uefi/var-service-edk2.h" + +#include "trace/trace-hw_uefi.h" + +#define EFI_VARIABLE_ATTRIBUTE_SUPPORTED \ + (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS | \ + EFI_VARIABLE_HARDWARE_ERROR_RECORD | \ + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_APPEND_WRITE) + + +const VMStateDescription vmstate_uefi_time = { + .name = "uefi-time", + .fields = (VMStateField[]) { + VMSTATE_UINT16(year, efi_time), + VMSTATE_UINT8(month, efi_time), + VMSTATE_UINT8(day, efi_time), + VMSTATE_UINT8(hour, efi_time), + VMSTATE_UINT8(minute, efi_time), + VMSTATE_UINT8(second, efi_time), + VMSTATE_UINT32(nanosecond, efi_time), + VMSTATE_END_OF_LIST() + }, +}; + +const VMStateDescription vmstate_uefi_variable = { + .name = "uefi-variable", + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), + VMSTATE_UINT32(name_size, uefi_variable), + VMSTATE_UINT32(data_size, uefi_variable), + VMSTATE_UINT32(attributes, uefi_variable), + VMSTATE_VBUFFER_ALLOC_UINT32(name, uefi_variable, 0, NULL, name_size), + VMSTATE_VBUFFER_ALLOC_UINT32(data, uefi_variable, 0, NULL, data_size), + VMSTATE_STRUCT(time, uefi_variable, 0, vmstate_uefi_time, efi_time), + VMSTATE_END_OF_LIST() + }, +}; + +uefi_variable *uefi_vars_find_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size) +{ + uefi_variable *var; + + QTAILQ_FOREACH(var, &uv->variables, next) { + if (!uefi_str_equal(var->name, var->name_size, + name, name_size)) { + continue; + } + if (!qemu_uuid_is_equal(&var->guid, &guid)) { + continue; + } + if (!var->data_size) { + /* in process of being created/updated */ + continue; + } + return var; + } + return NULL; +} + +static uefi_variable *add_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size, + uint32_t attributes) +{ + uefi_variable *var; + + var = g_new0(uefi_variable, 1); + var->guid = guid; + var->name = g_malloc(name_size); + memcpy(var->name, name, name_size); + var->name_size = name_size; + var->attributes = attributes; + + var->attributes &= ~EFI_VARIABLE_APPEND_WRITE; + + QTAILQ_INSERT_TAIL(&uv->variables, var, next); + return var; +} + +static void del_variable(uefi_vars_state *uv, uefi_variable *var) +{ + if (!var) { + return; + } + + QTAILQ_REMOVE(&uv->variables, var, next); + g_free(var->data); + g_free(var->name); + g_free(var->digest); + g_free(var); +} + +static size_t variable_size(uefi_variable *var) +{ + size_t size; + + size = sizeof(*var); + size += var->name_size; + size += var->data_size; + size += var->digest_size; + return size; +} + +void uefi_vars_set_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size, + uint32_t attributes, + void *data, uint64_t data_size) +{ + uefi_variable *old_var, *new_var; + + uefi_trace_variable(__func__, guid, name, name_size); + + old_var = uefi_vars_find_variable(uv, guid, name, name_size); + if (old_var) { + uv->used_storage -= variable_size(old_var); + del_variable(uv, old_var); + } + + new_var = add_variable(uv, guid, name, name_size, attributes); + new_var->data = g_malloc(data_size); + new_var->data_size = data_size; + memcpy(new_var->data, data, data_size); + uv->used_storage += variable_size(new_var); +} + +void uefi_vars_clear_volatile(uefi_vars_state *uv) +{ + uefi_variable *var, *n; + + QTAILQ_FOREACH_SAFE(var, &uv->variables, next, n) { + if (var->attributes & EFI_VARIABLE_NON_VOLATILE) { + continue; + } + uv->used_storage -= variable_size(var); + del_variable(uv, var); + } +} + +void uefi_vars_clear_all(uefi_vars_state *uv) +{ + uefi_variable *var, *n; + + QTAILQ_FOREACH_SAFE(var, &uv->variables, next, n) { + del_variable(uv, var); + } + uv->used_storage = 0; +} + +void uefi_vars_update_storage(uefi_vars_state *uv) +{ + uefi_variable *var; + + uv->used_storage = 0; + QTAILQ_FOREACH(var, &uv->variables, next) { + uv->used_storage += variable_size(var); + } +} + +static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) +{ + if (!uv->exit_boot_service) { + if (!(var->attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)) { + return false; + } + } else { + if (!(var->attributes & EFI_VARIABLE_RUNTIME_ACCESS)) { + return false; + } + } + return true; +} + +static efi_status check_update(uefi_vars_state *uv, uefi_variable *old_var, + uefi_variable *new_var) +{ + efi_status status; + + if (old_var) { + if (!check_access(uv, old_var)) { + return EFI_ACCESS_DENIED; + } + } + + if (new_var) { + if (new_var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED) { + return EFI_UNSUPPORTED; + } + if (!check_access(uv, new_var)) { + return EFI_ACCESS_DENIED; + } + } + + if (old_var && new_var) { + if (old_var->attributes != new_var->attributes) { + return EFI_INVALID_PARAMETER; + } + } + + if (new_var) { + /* create + update */ + status = uefi_vars_policy_check(uv, new_var, old_var == NULL); + } else { + /* delete */ + g_assert(old_var); + status = uefi_vars_policy_check(uv, old_var, false); + } + if (status != EFI_SUCCESS) { + return status; + } + + status = uefi_vars_check_secure_boot(uv, new_var ?: old_var); + if (status != EFI_SUCCESS) { + return status; + } + + return EFI_SUCCESS; +} + +static void append_write(uefi_variable *old_var, + uefi_variable *new_var) +{ + uefi_vars_siglist siglist; + uint64_t size; + void *data; + + uefi_vars_siglist_init(&siglist); + uefi_vars_siglist_parse(&siglist, old_var->data, old_var->data_size); + uefi_vars_siglist_parse(&siglist, new_var->data, new_var->data_size); + + size = uefi_vars_siglist_blob_size(&siglist); + data = g_malloc(size); + uefi_vars_siglist_blob_generate(&siglist, data, size); + + g_free(new_var->data); + new_var->data = data; + new_var->data_size = size; + + uefi_vars_siglist_free(&siglist); +} + +static size_t uefi_vars_mm_error(mm_header *mhdr, mm_variable *mvar, + uint64_t status) +{ + mvar->status = status; + return sizeof(*mvar); +} + +static size_t uefi_vars_mm_get_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_variable_access *va = func; + uint16_t *name; + void *data; + uefi_variable *var; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*va); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (va->name_size > uv->max_storage || + va->data_size > uv->max_storage) { + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); + } + + name = func + sizeof(*va); + if (uadd64_overflow(length, va->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (!uefi_str_is_valid(name, va->name_size, true)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + + uefi_trace_variable(__func__, va->guid, name, va->name_size); + + var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); + } + + /* check permissions etc. */ + if (!check_access(uv, var)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_ACCESS_DENIED); + } + + data = func + sizeof(*va) + va->name_size; + if (uadd64_overflow(length, va->data_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + va->attributes = var->attributes; + if (va->data_size < var->data_size) { + va->data_size = var->data_size; + length -= va->data_size; + mvar->status = EFI_BUFFER_TOO_SMALL; + } else { + va->data_size = var->data_size; + memcpy(data, var->data, var->data_size); + mvar->status = EFI_SUCCESS; + } + return length; +} + +static size_t +uefi_vars_mm_get_next_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_next_variable *nv = func; + uefi_variable *var; + uint16_t *name; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*nv); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (nv->name_size > uv->max_storage) { + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); + } + + name = func + sizeof(*nv); + if (uadd64_overflow(length, nv->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (!uefi_str_is_valid(name, nv->name_size, true)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + + if (uefi_strlen(name, nv->name_size) == 0) { + /* empty string -> first */ + var = QTAILQ_FIRST(&uv->variables); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); + } + } else { + var = uefi_vars_find_variable(uv, nv->guid, name, nv->name_size); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + do { + var = QTAILQ_NEXT(var, next); + } while (var && !check_access(uv, var)); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); + } + } + + length = sizeof(*mvar) + sizeof(*nv) + var->name_size; + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + nv->guid = var->guid; + nv->name_size = var->name_size; + memcpy(name, var->name, var->name_size); + mvar->status = EFI_SUCCESS; + return length; +} + +static bool uefi_vars_mm_digest_compare(uefi_variable *old_var, + uefi_variable *new_var) +{ + if (!old_var->digest || + !new_var->digest || + !old_var->digest_size || + !new_var->digest_size) { + /* should not happen */ + trace_uefi_vars_security_violation("inconsistent authvar digest state"); + return false; + } + if (old_var->digest_size != new_var->digest_size) { + trace_uefi_vars_security_violation("authvar digest size mismatch"); + return false; + } + if (memcmp(old_var->digest, new_var->digest, + old_var->digest_size) != 0) { + trace_uefi_vars_security_violation("authvar digest data mismatch"); + return false; + } + return true; +} + +static size_t uefi_vars_mm_set_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_variable_access *va = func; + uint32_t attributes = 0; + uint16_t *name; + void *data; + uefi_variable *old_var, *new_var; + uint64_t length; + size_t new_storage; + efi_status status; + + length = sizeof(*mvar) + sizeof(*va); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (va->name_size > uv->max_storage || + va->data_size > uv->max_storage) { + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); + } + + name = func + sizeof(*va); + if (uadd64_overflow(length, va->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + data = func + sizeof(*va) + va->name_size; + if (uadd64_overflow(length, va->data_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + g_assert(va->name_size < G_MAXUINT32); + g_assert(va->data_size < G_MAXUINT32); + + if (!uefi_str_is_valid(name, va->name_size, true)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + + uefi_trace_variable(__func__, va->guid, name, va->name_size); + + old_var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); + if (va->data_size) { + new_var = add_variable(uv, va->guid, name, va->name_size, + va->attributes); + if (va->attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { + /* not implemented (deprecated in uefi spec) */ + warn_report("%s: AUTHENTICATED_WRITE_ACCESS", __func__); + mvar->status = EFI_UNSUPPORTED; + goto rollback; + } else if (va->attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + status = uefi_vars_check_auth_2(uv, new_var, va, data); + if (status != EFI_SUCCESS) { + mvar->status = status; + goto rollback; + } + if (old_var && new_var) { + if (uefi_time_compare(&old_var->time, &new_var->time) > 0) { + trace_uefi_vars_security_violation("time check failed"); + mvar->status = EFI_SECURITY_VIOLATION; + goto rollback; + } + if (old_var->digest_size || new_var->digest_size) { + if (!uefi_vars_mm_digest_compare(old_var, new_var)) { + mvar->status = EFI_SECURITY_VIOLATION; + goto rollback; + } + } + } + } else { + new_var->data = g_malloc(va->data_size); + memcpy(new_var->data, data, va->data_size); + new_var->data_size = va->data_size; + } + if (!new_var->data) { + /* we land here when deleting authenticated variables */ + del_variable(uv, new_var); + new_var = NULL; + } + } else { + new_var = NULL; + } + + if (!old_var && !new_var) { + /* delete non-existing variable -> nothing to do */ + mvar->status = EFI_SUCCESS; + return sizeof(*mvar); + } + + /* check permissions etc. */ + status = check_update(uv, old_var, new_var); + if (status != EFI_SUCCESS) { + mvar->status = status; + goto rollback; + } + + if (va->attributes & EFI_VARIABLE_APPEND_WRITE && old_var && new_var) { + /* merge signature databases */ + if (!uefi_vars_is_sb_any(new_var)) { + mvar->status = EFI_UNSUPPORTED; + goto rollback; + } + append_write(old_var, new_var); + } + + /* check storage space */ + new_storage = uv->used_storage; + if (old_var) { + new_storage -= variable_size(old_var); + } + if (new_var) { + new_storage += variable_size(new_var); + } + if (new_storage > uv->max_storage) { + mvar->status = EFI_OUT_OF_RESOURCES; + goto rollback; + } + + attributes = new_var + ? new_var->attributes + : old_var->attributes; + + /* all good, commit */ + del_variable(uv, old_var); + uv->used_storage = new_storage; + + if (attributes & EFI_VARIABLE_NON_VOLATILE) { + uefi_vars_json_save(uv); + } + + if (new_var && uefi_vars_is_sb_pk(new_var)) { + uefi_vars_auth_init(uv); + } + + mvar->status = EFI_SUCCESS; + return sizeof(*mvar); + +rollback: + del_variable(uv, new_var); + return sizeof(*mvar); +} + +static size_t uefi_vars_mm_variable_info(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_variable_info *vi = func; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*vi); + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + vi->max_storage_size = uv->max_storage; + vi->free_storage_size = uv->max_storage - uv->used_storage; + vi->max_variable_size = uv->max_storage >> 2; + vi->attributes = 0; + + mvar->status = EFI_SUCCESS; + return length; +} + +static size_t +uefi_vars_mm_get_payload_size(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_get_payload_size *ps = func; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*ps); + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + ps->payload_size = uv->buf_size; + mvar->status = EFI_SUCCESS; + return length; +} + +static size_t +uefi_vars_mm_lock_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_lock_variable *lv = func; + variable_policy_entry *pe; + uint16_t *name, *dest; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*lv); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + name = func + sizeof(*lv); + if (uadd64_overflow(length, lv->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + uefi_trace_variable(__func__, lv->guid, name, lv->name_size); + + pe = g_malloc0(sizeof(*pe) + lv->name_size); + pe->version = VARIABLE_POLICY_ENTRY_REVISION; + pe->size = sizeof(*pe) + lv->name_size; + pe->offset_to_name = sizeof(*pe); + pe->namespace = lv->guid; + pe->min_size = 0; + pe->max_size = UINT32_MAX; + pe->attributes_must_have = 0; + pe->attributes_cant_have = 0; + pe->lock_policy_type = VARIABLE_POLICY_TYPE_LOCK_NOW; + + dest = (void *)pe + pe->offset_to_name; + memcpy(dest, name, lv->name_size); + + uefi_vars_add_policy(uv, pe); + g_free(pe); + + mvar->status = EFI_SUCCESS; + return length; +} + +uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv) +{ + static const char *fnames[] = { + "zero", + "get-variable", + "get-next-variable-name", + "set-variable", + "query-variable-info", + "ready-to-boot", + "exit-boot-service", + "get-statistics", + "lock-variable", + "var-check-prop-set", + "var-check-prop-get", + "get-payload-size", + "init-runtime-cache-contect", + "sync-runtime-cache", + "get-runtime-cache-info", + }; + const char *fname; + uint64_t length; + + mm_header *mhdr = (mm_header *) uv->buffer; + mm_variable *mvar = (mm_variable *) (uv->buffer + sizeof(*mhdr)); + void *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mvar)); + + if (mhdr->length < sizeof(*mvar)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + fname = mvar->function < ARRAY_SIZE(fnames) + ? fnames[mvar->function] + : "unknown"; + trace_uefi_vars_proto_cmd(fname); + + switch (mvar->function) { + case SMM_VARIABLE_FUNCTION_GET_VARIABLE: + length = uefi_vars_mm_get_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME: + length = uefi_vars_mm_get_next_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_SET_VARIABLE: + length = uefi_vars_mm_set_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO: + length = uefi_vars_mm_variable_info(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE: + length = uefi_vars_mm_lock_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE: + length = uefi_vars_mm_get_payload_size(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: + trace_uefi_event("ready-to-boot"); + uv->ready_to_boot = true; + length = 0; + break; + + case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE: + trace_uefi_event("exit-boot-service"); + uv->exit_boot_service = true; + length = 0; + break; + + default: + length = uefi_vars_mm_error(mhdr, mvar, EFI_UNSUPPORTED); + break; + } + + if (mhdr->length < length) { + mvar->status = EFI_BUFFER_TOO_SMALL; + } + + uefi_trace_status(__func__, mvar->status); + return UEFI_VARS_STS_SUCCESS; +} From f1488fac0584cc095865e4d4d987f01f4e97fbe5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:12 +0100 Subject: [PATCH 2078/2892] hw/uefi: add var-service-auth.c This implements authenticated variable handling (see AuthVariableLib in edk2). The by far most common use case for auth variables is secure boot. The secure boot certificate databases ('PK', 'KEK', 'db' and 'dbx') are authenticated variables, with update rules being specified in the UEFI specification. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-9-kraxel@redhat.com> --- hw/uefi/var-service-auth.c | 361 +++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 hw/uefi/var-service-auth.c diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c new file mode 100644 index 0000000000..fba5a0956a --- /dev/null +++ b/hw/uefi/var-service-auth.c @@ -0,0 +1,361 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - AuthVariableLib + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +static const uint16_t name_pk[] = u"PK"; +static const uint16_t name_kek[] = u"KEK"; +static const uint16_t name_db[] = u"db"; +static const uint16_t name_dbx[] = u"dbx"; +static const uint16_t name_setup_mode[] = u"SetupMode"; +static const uint16_t name_sigs_support[] = u"SignatureSupport"; +static const uint16_t name_sb[] = u"SecureBoot"; +static const uint16_t name_sb_enable[] = u"SecureBootEnable"; +static const uint16_t name_custom_mode[] = u"CustomMode"; +static const uint16_t name_vk[] = u"VendorKeys"; +static const uint16_t name_vk_nv[] = u"VendorKeysNv"; + +static const uint32_t sigdb_attrs = + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + +static void set_secure_boot(uefi_vars_state *uv, uint8_t sb) +{ + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_sb, sizeof(name_sb), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &sb, sizeof(sb)); +} + +static void set_secure_boot_enable(uefi_vars_state *uv, uint8_t sbe) +{ + uefi_vars_set_variable(uv, EfiSecureBootEnableDisable, + name_sb_enable, sizeof(name_sb_enable), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + &sbe, sizeof(sbe)); +} + +static void set_setup_mode(uefi_vars_state *uv, uint8_t sm) +{ + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_setup_mode, sizeof(name_setup_mode), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &sm, sizeof(sm)); +} + +static void set_custom_mode(uefi_vars_state *uv, uint8_t cm) +{ + uefi_vars_set_variable(uv, EfiCustomModeEnable, + name_custom_mode, sizeof(name_custom_mode), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + &cm, sizeof(cm)); +} + +static void set_signature_support(uefi_vars_state *uv) +{ + QemuUUID sigs_support[5]; + + sigs_support[0] = EfiCertSha256Guid; + sigs_support[1] = EfiCertSha384Guid; + sigs_support[2] = EfiCertSha512Guid; + sigs_support[3] = EfiCertRsa2048Guid; + sigs_support[4] = EfiCertX509Guid; + + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_sigs_support, sizeof(name_sigs_support), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sigs_support, sizeof(sigs_support)); +} + +static bool setup_mode_is_active(uefi_vars_state *uv) +{ + uefi_variable *var; + uint8_t *value; + + var = uefi_vars_find_variable(uv, EfiGlobalVariable, + name_setup_mode, sizeof(name_setup_mode)); + if (var) { + value = var->data; + if (value[0] == SETUP_MODE) { + return true; + } + } + return false; +} + +static bool custom_mode_is_active(uefi_vars_state *uv) +{ + uefi_variable *var; + uint8_t *value; + + var = uefi_vars_find_variable(uv, EfiCustomModeEnable, + name_custom_mode, sizeof(name_custom_mode)); + if (var) { + value = var->data; + if (value[0] == CUSTOM_SECURE_BOOT_MODE) { + return true; + } + } + return false; +} + +bool uefi_vars_is_sb_pk(uefi_variable *var) +{ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, name_pk, sizeof(name_pk))) { + return true; + } + return false; +} + +static bool uefi_vars_is_sb_kek(uefi_variable *var) +{ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, name_kek, sizeof(name_kek))) { + return true; + } + return false; +} + +static bool uefi_vars_is_sb_db(uefi_variable *var) +{ + if (!qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase)) { + return false; + } + if (uefi_str_equal(var->name, var->name_size, name_db, sizeof(name_db))) { + return true; + } + if (uefi_str_equal(var->name, var->name_size, name_dbx, sizeof(name_dbx))) { + return true; + } + return false; +} + +bool uefi_vars_is_sb_any(uefi_variable *var) +{ + if (uefi_vars_is_sb_pk(var) || + uefi_vars_is_sb_kek(var) || + uefi_vars_is_sb_db(var)) { + return true; + } + return false; +} + +static uefi_variable *uefi_vars_find_siglist(uefi_vars_state *uv, + uefi_variable *var) +{ + if (uefi_vars_is_sb_pk(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + } + if (uefi_vars_is_sb_kek(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + } + if (uefi_vars_is_sb_db(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_kek, sizeof(name_kek)); + } + + return NULL; +} + +static efi_status uefi_vars_check_auth_2_sb(uefi_vars_state *uv, + uefi_variable *var, + mm_variable_access *va, + void *data, + uint64_t data_offset) +{ + variable_auth_2 *auth = data; + uefi_variable *siglist; + + if (custom_mode_is_active(uv)) { + /* no authentication in custom mode */ + return EFI_SUCCESS; + } + + if (setup_mode_is_active(uv) && !uefi_vars_is_sb_pk(var)) { + /* no authentication in setup mode (except PK) */ + return EFI_SUCCESS; + } + + if (auth->hdr_length == 24) { + /* no signature (auth->cert_data is empty) */ + return EFI_SECURITY_VIOLATION; + } + + siglist = uefi_vars_find_siglist(uv, var); + if (!siglist && setup_mode_is_active(uv) && uefi_vars_is_sb_pk(var)) { + /* check PK is self-signed */ + uefi_variable tmp = { + .guid = EfiGlobalVariable, + .name = (uint16_t *)name_pk, + .name_size = sizeof(name_pk), + .attributes = sigdb_attrs, + .data = data + data_offset, + .data_size = va->data_size - data_offset, + }; + return uefi_vars_check_pkcs7_2(&tmp, NULL, NULL, va, data); + } + + return uefi_vars_check_pkcs7_2(siglist, NULL, NULL, va, data); +} + +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, + mm_variable_access *va, void *data) +{ + variable_auth_2 *auth = data; + uint64_t data_offset; + efi_status status; + + if (va->data_size < sizeof(*auth)) { + return EFI_SECURITY_VIOLATION; + } + if (uadd64_overflow(sizeof(efi_time), auth->hdr_length, &data_offset)) { + return EFI_SECURITY_VIOLATION; + } + if (va->data_size < data_offset) { + return EFI_SECURITY_VIOLATION; + } + + if (auth->hdr_revision != 0x0200 || + auth->hdr_cert_type != WIN_CERT_TYPE_EFI_GUID || + !qemu_uuid_is_equal(&auth->guid_cert_type, &EfiCertTypePkcs7Guid)) { + return EFI_UNSUPPORTED; + } + + if (uefi_vars_is_sb_any(var)) { + /* secure boot variables */ + status = uefi_vars_check_auth_2_sb(uv, var, va, data, data_offset); + if (status != EFI_SUCCESS) { + return status; + } + } else { + /* other authenticated variables */ + status = uefi_vars_check_pkcs7_2(NULL, + &var->digest, &var->digest_size, + va, data); + if (status != EFI_SUCCESS) { + return status; + } + } + + /* checks passed, set variable data */ + var->time = auth->timestamp; + if (va->data_size - data_offset > 0) { + var->data = g_malloc(va->data_size - data_offset); + memcpy(var->data, data + data_offset, va->data_size - data_offset); + var->data_size = va->data_size - data_offset; + } + + return EFI_SUCCESS; +} + +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var) +{ + uint8_t *value = var->data; + + if (uefi_vars_is_sb_any(var)) { + if (var->attributes != sigdb_attrs) { + return EFI_INVALID_PARAMETER; + } + } + + /* reject SecureBootEnable updates if force_secure_boot is set */ + if (qemu_uuid_is_equal(&var->guid, &EfiSecureBootEnableDisable) && + uefi_str_equal(var->name, var->name_size, + name_sb_enable, sizeof(name_sb_enable)) && + uv->force_secure_boot && + value[0] != SECURE_BOOT_ENABLE) { + return EFI_WRITE_PROTECTED; + } + + /* reject CustomMode updates if disable_custom_mode is set */ + if (qemu_uuid_is_equal(&var->guid, &EfiCustomModeEnable) && + uefi_str_equal(var->name, var->name_size, + name_custom_mode, sizeof(name_custom_mode)) && + uv->disable_custom_mode) { + return EFI_WRITE_PROTECTED; + } + + return EFI_SUCCESS; +} + +/* AuthVariableLibInitialize */ +void uefi_vars_auth_init(uefi_vars_state *uv) +{ + uefi_variable *pk_var, *sbe_var; + uint8_t platform_mode, sb, sbe, vk; + + /* SetupMode */ + pk_var = uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + if (!pk_var) { + platform_mode = SETUP_MODE; + } else { + platform_mode = USER_MODE; + } + set_setup_mode(uv, platform_mode); + + /* SignatureSupport */ + set_signature_support(uv); + + /* SecureBootEnable */ + sbe = SECURE_BOOT_DISABLE; + sbe_var = uefi_vars_find_variable(uv, EfiSecureBootEnableDisable, + name_sb_enable, sizeof(name_sb_enable)); + if (sbe_var) { + if (platform_mode == USER_MODE) { + sbe = ((uint8_t *)sbe_var->data)[0]; + } + } else if (platform_mode == USER_MODE) { + sbe = SECURE_BOOT_ENABLE; + set_secure_boot_enable(uv, sbe); + } + + if (uv->force_secure_boot && sbe != SECURE_BOOT_ENABLE) { + sbe = SECURE_BOOT_ENABLE; + set_secure_boot_enable(uv, sbe); + } + + /* SecureBoot */ + if ((sbe == SECURE_BOOT_ENABLE) && (platform_mode == USER_MODE)) { + sb = SECURE_BOOT_MODE_ENABLE; + } else { + sb = SECURE_BOOT_MODE_DISABLE; + } + set_secure_boot(uv, sb); + + /* CustomMode */ + set_custom_mode(uv, STANDARD_SECURE_BOOT_MODE); + + vk = 0; + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_vk_nv, sizeof(name_vk_nv), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + &vk, sizeof(vk)); + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_vk, sizeof(name_vk), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &vk, sizeof(vk)); + + /* flush to disk */ + uefi_vars_json_save(uv); +} From 034cb968ca8fc562ea11de516828228eeb146944 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:13 +0100 Subject: [PATCH 2079/2892] hw/uefi: add var-service-policy.c Implement variable policies (Edk2VariablePolicyProtocol). This EFI protocol allows to define restrictions for variables. It also allows to lock down variables (disallow write access). Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-10-kraxel@redhat.com> --- hw/uefi/var-service-policy.c | 370 +++++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 hw/uefi/var-service-policy.c diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c new file mode 100644 index 0000000000..3b1155fe4e --- /dev/null +++ b/hw/uefi/var-service-policy.c @@ -0,0 +1,370 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - VarCheckPolicyLibMmiHandler implementation + * + * variable policy specs: + * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md + */ +#include "qemu/osdep.h" +#include "system/dma.h" +#include "migration/vmstate.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" +#include "hw/uefi/var-service-edk2.h" + +#include "trace/trace-hw_uefi.h" + +static void calc_policy(uefi_var_policy *pol); + +static int uefi_var_policy_post_load(void *opaque, int version_id) +{ + uefi_var_policy *pol = opaque; + + calc_policy(pol); + return 0; +} + +const VMStateDescription vmstate_uefi_var_policy = { + .name = "uefi-var-policy", + .post_load = uefi_var_policy_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(entry_size, uefi_var_policy), + VMSTATE_VBUFFER_ALLOC_UINT32(entry, uefi_var_policy, + 0, NULL, entry_size), + VMSTATE_END_OF_LIST() + }, +}; + +static void print_policy_entry(variable_policy_entry *pe) +{ + uint16_t *name = (void *)pe + pe->offset_to_name; + + fprintf(stderr, "%s:\n", __func__); + + fprintf(stderr, " name ´"); + while (*name) { + fprintf(stderr, "%c", *name); + name++; + } + fprintf(stderr, "', version=%d.%d, size=%d\n", + pe->version >> 16, pe->version & 0xffff, pe->size); + + if (pe->min_size) { + fprintf(stderr, " size min=%d\n", pe->min_size); + } + if (pe->max_size != UINT32_MAX) { + fprintf(stderr, " size max=%u\n", pe->max_size); + } + if (pe->attributes_must_have) { + fprintf(stderr, " attr must=0x%x\n", pe->attributes_must_have); + } + if (pe->attributes_cant_have) { + fprintf(stderr, " attr cant=0x%x\n", pe->attributes_cant_have); + } + if (pe->lock_policy_type) { + fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); + } +} + +static gboolean wildcard_str_equal(uefi_var_policy *pol, + uefi_variable *var) +{ + return uefi_str_equal_ex(pol->name, pol->name_size, + var->name, var->name_size, + true); +} + +static uefi_var_policy *find_policy(uefi_vars_state *uv, QemuUUID guid, + uint16_t *name, uint64_t name_size) +{ + uefi_var_policy *pol; + + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + if (!qemu_uuid_is_equal(&pol->entry->namespace, &guid)) { + continue; + } + if (!uefi_str_equal(pol->name, pol->name_size, + name, name_size)) { + continue; + } + return pol; + } + return NULL; +} + +static uefi_var_policy *wildcard_find_policy(uefi_vars_state *uv, + uefi_variable *var) +{ + uefi_var_policy *pol; + + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + if (!qemu_uuid_is_equal(&pol->entry->namespace, &var->guid)) { + continue; + } + if (!wildcard_str_equal(pol, var)) { + continue; + } + return pol; + } + return NULL; +} + +static void calc_policy(uefi_var_policy *pol) +{ + variable_policy_entry *pe = pol->entry; + unsigned int i; + + pol->name = (void *)pol->entry + pe->offset_to_name; + pol->name_size = pe->size - pe->offset_to_name; + + for (i = 0; i < pol->name_size / 2; i++) { + if (pol->name[i] == '#') { + pol->hashmarks++; + } + } +} + +uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, + variable_policy_entry *pe) +{ + uefi_var_policy *pol, *p; + + pol = g_new0(uefi_var_policy, 1); + pol->entry = g_malloc(pe->size); + memcpy(pol->entry, pe, pe->size); + pol->entry_size = pe->size; + + calc_policy(pol); + + /* keep list sorted by priority, add to tail of priority group */ + QTAILQ_FOREACH(p, &uv->var_policies, next) { + if ((p->hashmarks > pol->hashmarks) || + (!p->name_size && pol->name_size)) { + QTAILQ_INSERT_BEFORE(p, pol, next); + return pol; + } + } + + QTAILQ_INSERT_TAIL(&uv->var_policies, pol, next); + return pol; +} + +efi_status uefi_vars_policy_check(uefi_vars_state *uv, + uefi_variable *var, + gboolean is_newvar) +{ + uefi_var_policy *pol; + variable_policy_entry *pe; + variable_lock_on_var_state *lvarstate; + uint16_t *lvarname; + size_t lvarnamesize; + uefi_variable *lvar; + + if (!uv->end_of_dxe) { + return EFI_SUCCESS; + } + + pol = wildcard_find_policy(uv, var); + if (!pol) { + return EFI_SUCCESS; + } + pe = pol->entry; + + uefi_trace_variable(__func__, var->guid, var->name, var->name_size); + print_policy_entry(pe); + + if ((var->attributes & pe->attributes_must_have) != pe->attributes_must_have) { + trace_uefi_vars_policy_deny("must-have-attr"); + return EFI_INVALID_PARAMETER; + } + if ((var->attributes & pe->attributes_cant_have) != 0) { + trace_uefi_vars_policy_deny("cant-have-attr"); + return EFI_INVALID_PARAMETER; + } + + if (var->data_size < pe->min_size) { + trace_uefi_vars_policy_deny("min-size"); + return EFI_INVALID_PARAMETER; + } + if (var->data_size > pe->max_size) { + trace_uefi_vars_policy_deny("max-size"); + return EFI_INVALID_PARAMETER; + } + + switch (pe->lock_policy_type) { + case VARIABLE_POLICY_TYPE_NO_LOCK: + break; + + case VARIABLE_POLICY_TYPE_LOCK_NOW: + trace_uefi_vars_policy_deny("lock-now"); + return EFI_WRITE_PROTECTED; + + case VARIABLE_POLICY_TYPE_LOCK_ON_CREATE: + if (!is_newvar) { + trace_uefi_vars_policy_deny("lock-on-create"); + return EFI_WRITE_PROTECTED; + } + break; + + case VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE: + lvarstate = (void *)pol->entry + sizeof(*pe); + lvarname = (void *)pol->entry + sizeof(*pe) + sizeof(*lvarstate); + lvarnamesize = pe->offset_to_name - sizeof(*pe) - sizeof(*lvarstate); + + uefi_trace_variable(__func__, lvarstate->namespace, + lvarname, lvarnamesize); + lvar = uefi_vars_find_variable(uv, lvarstate->namespace, + lvarname, lvarnamesize); + if (lvar && lvar->data_size == 1) { + uint8_t *value = lvar->data; + if (lvarstate->value == *value) { + return EFI_WRITE_PROTECTED; + } + } + break; + } + + return EFI_SUCCESS; +} + +void uefi_vars_policies_clear(uefi_vars_state *uv) +{ + uefi_var_policy *pol; + + while (!QTAILQ_EMPTY(&uv->var_policies)) { + pol = QTAILQ_FIRST(&uv->var_policies); + QTAILQ_REMOVE(&uv->var_policies, pol, next); + g_free(pol->entry); + g_free(pol); + } +} + +static size_t uefi_vars_mm_policy_error(mm_header *mhdr, + mm_check_policy *mchk, + uint64_t status) +{ + mchk->result = status; + return sizeof(*mchk); +} + +static uint32_t uefi_vars_mm_check_policy_is_enabled(uefi_vars_state *uv, + mm_header *mhdr, + mm_check_policy *mchk, + void *func) +{ + mm_check_policy_is_enabled *mpar = func; + size_t length; + + length = sizeof(*mchk) + sizeof(*mpar); + if (mhdr->length < length) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + mpar->state = TRUE; + mchk->result = EFI_SUCCESS; + return sizeof(*mchk); +} + +static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv, + mm_header *mhdr, + mm_check_policy *mchk, + void *func) +{ + variable_policy_entry *pe = func; + uefi_var_policy *pol; + uint64_t length; + + if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + if (pe->size < sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + if (pe->offset_to_name < sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + if (pe->lock_policy_type == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE && + pe->offset_to_name < sizeof(*pe) + sizeof(variable_lock_on_var_state)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + /* check space for minimum string length */ + if (pe->size < (size_t)pe->offset_to_name) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + if (!uefi_str_is_valid((void *)pe + pe->offset_to_name, + pe->size - pe->offset_to_name, + false)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_INVALID_PARAMETER); + } + + pol = find_policy(uv, pe->namespace, + (void *)pe + pe->offset_to_name, + pe->size - pe->offset_to_name); + if (pol) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_ALREADY_STARTED); + } + + uefi_vars_add_policy(uv, pe); + + mchk->result = EFI_SUCCESS; + return sizeof(*mchk); +} + +uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv) +{ + static const char *fnames[] = { + "zero", + "disable", + "is-enabled", + "register", + "dump", + "lock", + }; + const char *fname; + mm_header *mhdr = (mm_header *) uv->buffer; + mm_check_policy *mchk = (mm_check_policy *) (uv->buffer + sizeof(*mhdr)); + void *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mchk)); + + if (mhdr->length < sizeof(*mchk)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + fname = mchk->command < ARRAY_SIZE(fnames) + ? fnames[mchk->command] + : "unknown"; + trace_uefi_vars_policy_cmd(fname); + + switch (mchk->command) { + case VAR_CHECK_POLICY_COMMAND_DISABLE: + mchk->result = EFI_UNSUPPORTED; + break; + case VAR_CHECK_POLICY_COMMAND_IS_ENABLED: + uefi_vars_mm_check_policy_is_enabled(uv, mhdr, mchk, func); + break; + case VAR_CHECK_POLICY_COMMAND_REGISTER: + if (uv->policy_locked) { + mchk->result = EFI_WRITE_PROTECTED; + } else { + uefi_vars_mm_check_policy_register(uv, mhdr, mchk, func); + } + break; + case VAR_CHECK_POLICY_COMMAND_LOCK: + uv->policy_locked = true; + mchk->result = EFI_SUCCESS; + break; + default: + mchk->result = EFI_UNSUPPORTED; + break; + } + + uefi_trace_status(__func__, mchk->result); + return UEFI_VARS_STS_SUCCESS; +} From 90ca4e03c27dc8ac821a2e1686e705ae9a93d301 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:14 +0100 Subject: [PATCH 2080/2892] hw/uefi: add var-service-core.c This is the core code for guest <-> host communication. This accepts request messages from the guest, dispatches them to the service called, and sends back the response message. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-11-kraxel@redhat.com> --- hw/uefi/var-service-core.c | 321 +++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 hw/uefi/var-service-core.c diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c new file mode 100644 index 0000000000..8ed8378ab9 --- /dev/null +++ b/hw/uefi/var-service-core.c @@ -0,0 +1,321 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device + */ +#include "qemu/osdep.h" +#include "qemu/crc32c.h" +#include "system/dma.h" +#include "migration/vmstate.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" +#include "hw/uefi/var-service-edk2.h" + +#include "trace/trace-hw_uefi.h" + +static int uefi_vars_pre_load(void *opaque) +{ + uefi_vars_state *uv = opaque; + + uefi_vars_clear_all(uv); + uefi_vars_policies_clear(uv); + g_free(uv->buffer); + return 0; +} + +static int uefi_vars_post_load(void *opaque, int version_id) +{ + uefi_vars_state *uv = opaque; + + uefi_vars_update_storage(uv); + uv->buffer = g_malloc(uv->buf_size); + return 0; +} + +const VMStateDescription vmstate_uefi_vars = { + .name = "uefi-vars", + .pre_load = uefi_vars_pre_load, + .post_load = uefi_vars_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(sts, uefi_vars_state), + VMSTATE_UINT32(buf_size, uefi_vars_state), + VMSTATE_UINT32(buf_addr_lo, uefi_vars_state), + VMSTATE_UINT32(buf_addr_hi, uefi_vars_state), + VMSTATE_UINT32(pio_xfer_offset, uefi_vars_state), + VMSTATE_VBUFFER_ALLOC_UINT32(pio_xfer_buffer, uefi_vars_state, + 0, NULL, buf_size), + VMSTATE_BOOL(end_of_dxe, uefi_vars_state), + VMSTATE_BOOL(ready_to_boot, uefi_vars_state), + VMSTATE_BOOL(exit_boot_service, uefi_vars_state), + VMSTATE_BOOL(policy_locked, uefi_vars_state), + VMSTATE_UINT64(used_storage, uefi_vars_state), + VMSTATE_QTAILQ_V(variables, uefi_vars_state, 0, + vmstate_uefi_variable, uefi_variable, next), + VMSTATE_QTAILQ_V(var_policies, uefi_vars_state, 0, + vmstate_uefi_var_policy, uefi_var_policy, next), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t uefi_vars_cmd_mm(uefi_vars_state *uv, bool dma_mode) +{ + hwaddr dma; + mm_header *mhdr; + uint64_t size; + uint32_t retval; + + dma = uv->buf_addr_lo | ((hwaddr)uv->buf_addr_hi << 32); + mhdr = (mm_header *) uv->buffer; + + if (!uv->buffer || uv->buf_size < sizeof(*mhdr)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + /* read header */ + if (dma_mode) { + dma_memory_read(&address_space_memory, dma, + uv->buffer, sizeof(*mhdr), + MEMTXATTRS_UNSPECIFIED); + } else { + memcpy(uv->buffer, uv->pio_xfer_buffer, sizeof(*mhdr)); + } + + if (uadd64_overflow(sizeof(*mhdr), mhdr->length, &size)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + if (uv->buf_size < size) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + /* read buffer (excl header) */ + if (dma_mode) { + dma_memory_read(&address_space_memory, dma + sizeof(*mhdr), + uv->buffer + sizeof(*mhdr), mhdr->length, + MEMTXATTRS_UNSPECIFIED); + } else { + memcpy(uv->buffer + sizeof(*mhdr), + uv->pio_xfer_buffer + sizeof(*mhdr), + mhdr->length); + } + memset(uv->buffer + size, 0, uv->buf_size - size); + + /* dispatch */ + if (qemu_uuid_is_equal(&mhdr->guid, &EfiSmmVariableProtocolGuid)) { + retval = uefi_vars_mm_vars_proto(uv); + + } else if (qemu_uuid_is_equal(&mhdr->guid, &VarCheckPolicyLibMmiHandlerGuid)) { + retval = uefi_vars_mm_check_policy_proto(uv); + + } else if (qemu_uuid_is_equal(&mhdr->guid, &EfiEndOfDxeEventGroupGuid)) { + trace_uefi_event("end-of-dxe"); + uv->end_of_dxe = true; + retval = UEFI_VARS_STS_SUCCESS; + + } else if (qemu_uuid_is_equal(&mhdr->guid, &EfiEventReadyToBootGuid)) { + trace_uefi_event("ready-to-boot"); + uv->ready_to_boot = true; + retval = UEFI_VARS_STS_SUCCESS; + + } else if (qemu_uuid_is_equal(&mhdr->guid, &EfiEventExitBootServicesGuid)) { + trace_uefi_event("exit-boot-service"); + uv->exit_boot_service = true; + retval = UEFI_VARS_STS_SUCCESS; + + } else { + retval = UEFI_VARS_STS_ERR_NOT_SUPPORTED; + } + + /* write buffer */ + if (dma_mode) { + dma_memory_write(&address_space_memory, dma, + uv->buffer, sizeof(*mhdr) + mhdr->length, + MEMTXATTRS_UNSPECIFIED); + } else { + memcpy(uv->pio_xfer_buffer + sizeof(*mhdr), + uv->buffer + sizeof(*mhdr), + sizeof(*mhdr) + mhdr->length); + } + + return retval; +} + +static void uefi_vars_soft_reset(uefi_vars_state *uv) +{ + g_free(uv->buffer); + uv->buffer = NULL; + uv->buf_size = 0; + uv->buf_addr_lo = 0; + uv->buf_addr_hi = 0; +} + +void uefi_vars_hard_reset(uefi_vars_state *uv) +{ + trace_uefi_hard_reset(); + uefi_vars_soft_reset(uv); + + uv->end_of_dxe = false; + uv->ready_to_boot = false; + uv->exit_boot_service = false; + uv->policy_locked = false; + + uefi_vars_clear_volatile(uv); + uefi_vars_policies_clear(uv); + uefi_vars_auth_init(uv); +} + +static uint32_t uefi_vars_cmd(uefi_vars_state *uv, uint32_t cmd) +{ + switch (cmd) { + case UEFI_VARS_CMD_RESET: + uefi_vars_soft_reset(uv); + return UEFI_VARS_STS_SUCCESS; + case UEFI_VARS_CMD_DMA_MM: + return uefi_vars_cmd_mm(uv, true); + case UEFI_VARS_CMD_PIO_MM: + return uefi_vars_cmd_mm(uv, false); + case UEFI_VARS_CMD_PIO_ZERO_OFFSET: + uv->pio_xfer_offset = 0; + return UEFI_VARS_STS_SUCCESS; + default: + return UEFI_VARS_STS_ERR_NOT_SUPPORTED; + } +} + +static uint64_t uefi_vars_read(void *opaque, hwaddr addr, unsigned size) +{ + uefi_vars_state *uv = opaque; + uint64_t retval = -1; + void *xfer_ptr; + + trace_uefi_reg_read(addr, size); + + switch (addr) { + case UEFI_VARS_REG_MAGIC: + retval = UEFI_VARS_MAGIC_VALUE; + break; + case UEFI_VARS_REG_CMD_STS: + retval = uv->sts; + break; + case UEFI_VARS_REG_BUFFER_SIZE: + retval = uv->buf_size; + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_LO: + retval = uv->buf_addr_lo; + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_HI: + retval = uv->buf_addr_hi; + break; + case UEFI_VARS_REG_PIO_BUFFER_TRANSFER: + if (uv->pio_xfer_offset + size > uv->buf_size) { + retval = 0; + break; + } + xfer_ptr = uv->pio_xfer_buffer + uv->pio_xfer_offset; + switch (size) { + case 1: + retval = *(uint8_t *)xfer_ptr; + break; + case 2: + retval = *(uint16_t *)xfer_ptr; + break; + case 4: + retval = *(uint32_t *)xfer_ptr; + break; + case 8: + retval = *(uint64_t *)xfer_ptr; + break; + } + uv->pio_xfer_offset += size; + break; + case UEFI_VARS_REG_PIO_BUFFER_CRC32C: + retval = crc32c(0xffffffff, uv->pio_xfer_buffer, uv->pio_xfer_offset); + break; + case UEFI_VARS_REG_FLAGS: + retval = 0; + if (uv->use_pio) { + retval |= UEFI_VARS_FLAG_USE_PIO; + } + } + return retval; +} + +static void uefi_vars_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + uefi_vars_state *uv = opaque; + void *xfer_ptr; + + trace_uefi_reg_write(addr, val, size); + + switch (addr) { + case UEFI_VARS_REG_CMD_STS: + uv->sts = uefi_vars_cmd(uv, val); + break; + case UEFI_VARS_REG_BUFFER_SIZE: + if (val > MAX_BUFFER_SIZE) { + val = MAX_BUFFER_SIZE; + } + uv->buf_size = val; + g_free(uv->buffer); + g_free(uv->pio_xfer_buffer); + uv->buffer = g_malloc(uv->buf_size); + uv->pio_xfer_buffer = g_malloc(uv->buf_size); + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_LO: + uv->buf_addr_lo = val; + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_HI: + uv->buf_addr_hi = val; + break; + case UEFI_VARS_REG_PIO_BUFFER_TRANSFER: + if (uv->pio_xfer_offset + size > uv->buf_size) { + break; + } + xfer_ptr = uv->pio_xfer_buffer + uv->pio_xfer_offset; + switch (size) { + case 1: + *(uint8_t *)xfer_ptr = val; + break; + case 2: + *(uint16_t *)xfer_ptr = val; + break; + case 4: + *(uint32_t *)xfer_ptr = val; + break; + case 8: + *(uint64_t *)xfer_ptr = val; + break; + } + uv->pio_xfer_offset += size; + break; + case UEFI_VARS_REG_PIO_BUFFER_CRC32C: + case UEFI_VARS_REG_FLAGS: + default: + break; + } +} + +static const MemoryRegionOps uefi_vars_ops = { + .read = uefi_vars_read, + .write = uefi_vars_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + }, +}; + +void uefi_vars_init(Object *obj, uefi_vars_state *uv) +{ + QTAILQ_INIT(&uv->variables); + QTAILQ_INIT(&uv->var_policies); + uv->jsonfd = -1; + memory_region_init_io(&uv->mr, obj, &uefi_vars_ops, uv, + "uefi-vars", UEFI_VARS_REGS_SIZE); +} + +void uefi_vars_realize(uefi_vars_state *uv, Error **errp) +{ + uefi_vars_json_init(uv, errp); + uefi_vars_json_load(uv, errp); +} From 3e33af2cb306311d6fa4372c6d27489c165c1bd4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:15 +0100 Subject: [PATCH 2081/2892] hw/uefi: add var-service-pkcs7.c This implements pkcs7 signature verification using gnutls. Needed to check authenticated variable updates. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-12-kraxel@redhat.com> --- hw/uefi/var-service-pkcs7.c | 436 ++++++++++++++++++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 hw/uefi/var-service-pkcs7.c diff --git a/hw/uefi/var-service-pkcs7.c b/hw/uefi/var-service-pkcs7.c new file mode 100644 index 0000000000..32accf4e44 --- /dev/null +++ b/hw/uefi/var-service-pkcs7.c @@ -0,0 +1,436 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - pkcs7 verification + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include +#include +#include + +#include "hw/uefi/var-service.h" + +#define AUTHVAR_DIGEST_ALGO GNUTLS_DIG_SHA256 +#define AUTHVAR_DIGEST_SIZE 32 + +/* + * Replicate the signed data for signature verification. + */ +static gnutls_datum_t *build_signed_data(mm_variable_access *va, void *data) +{ + variable_auth_2 *auth = data; + uint64_t data_offset = sizeof(efi_time) + auth->hdr_length; + uint16_t *name = (void *)va + sizeof(mm_variable_access); + gnutls_datum_t *sdata; + uint64_t pos = 0; + + sdata = g_new(gnutls_datum_t, 1); + sdata->size = (va->name_size - 2 + + sizeof(QemuUUID) + + sizeof(va->attributes) + + sizeof(auth->timestamp) + + va->data_size - data_offset); + sdata->data = g_malloc(sdata->size); + + /* Variable Name (without terminating \0) */ + memcpy(sdata->data + pos, name, va->name_size - 2); + pos += va->name_size - 2; + + /* Variable Namespace Guid */ + memcpy(sdata->data + pos, &va->guid, sizeof(va->guid)); + pos += sizeof(va->guid); + + /* Attributes */ + memcpy(sdata->data + pos, &va->attributes, sizeof(va->attributes)); + pos += sizeof(va->attributes); + + /* TimeStamp */ + memcpy(sdata->data + pos, &auth->timestamp, sizeof(auth->timestamp)); + pos += sizeof(auth->timestamp); + + /* Variable Content */ + memcpy(sdata->data + pos, data + data_offset, va->data_size - data_offset); + pos += va->data_size - data_offset; + + assert(pos == sdata->size); + return sdata; +} + +/* + * See WrapPkcs7Data() in edk2. + * + * UEFI spec allows pkcs7 signatures being used without the envelope which + * identifies them as pkcs7 signatures. openssl and gnutls will not parse them + * without the envelope though. So add it if needed. + */ +static void wrap_pkcs7(gnutls_datum_t *pkcs7) +{ + static uint8_t signed_data_oid[9] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 + }; + gnutls_datum_t wrap; + + if (pkcs7->data[4] == 0x06 && + pkcs7->data[5] == 0x09 && + memcmp(pkcs7->data + 6, signed_data_oid, sizeof(signed_data_oid)) == 0 && + pkcs7->data[15] == 0x0a && + pkcs7->data[16] == 0x82) { + return; + } + + wrap.size = pkcs7->size + 19; + wrap.data = g_malloc(wrap.size); + + wrap.data[0] = 0x30; + wrap.data[1] = 0x82; + wrap.data[2] = (wrap.size - 4) >> 8; + wrap.data[3] = (wrap.size - 4) & 0xff; + wrap.data[4] = 0x06; + wrap.data[5] = 0x09; + memcpy(wrap.data + 6, signed_data_oid, sizeof(signed_data_oid)); + + wrap.data[15] = 0xa0; + wrap.data[16] = 0x82; + wrap.data[17] = pkcs7->size >> 8; + wrap.data[18] = pkcs7->size & 0xff; + memcpy(wrap.data + 19, pkcs7->data, pkcs7->size); + + g_free(pkcs7->data); + *pkcs7 = wrap; +} + +static gnutls_datum_t *build_pkcs7(void *data) +{ + variable_auth_2 *auth = data; + gnutls_datum_t *pkcs7; + + pkcs7 = g_new(gnutls_datum_t, 1); + pkcs7->size = auth->hdr_length - 24; + pkcs7->data = g_malloc(pkcs7->size); + memcpy(pkcs7->data, data + 16 + 24, pkcs7->size); + + wrap_pkcs7(pkcs7); + + return pkcs7; +} + +/* + * Read UEFI signature database, store x509 all certificates found in + * gnutls_x509_trust_list_t. + */ +static gnutls_x509_trust_list_t build_trust_list_sb(uefi_variable *var) +{ + gnutls_x509_trust_list_t tlist; + gnutls_datum_t cert_data; + gnutls_x509_crt_t cert; + uefi_vars_siglist siglist; + uefi_vars_cert *c; + int rc; + + rc = gnutls_x509_trust_list_init(&tlist, 0); + if (rc < 0) { + warn_report("gnutls_x509_trust_list_init error: %s", + gnutls_strerror(rc)); + return NULL; + } + + uefi_vars_siglist_init(&siglist); + uefi_vars_siglist_parse(&siglist, var->data, var->data_size); + + QTAILQ_FOREACH(c, &siglist.x509, next) { + cert_data.size = c->size; + cert_data.data = c->data; + + rc = gnutls_x509_crt_init(&cert); + if (rc < 0) { + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); + break; + } + rc = gnutls_x509_crt_import(cert, &cert_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(cert); + break; + } + rc = gnutls_x509_trust_list_add_cas(tlist, &cert, 1, 0); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(cert); + break; + } + } + + uefi_vars_siglist_free(&siglist); + + return tlist; +} + +static int build_digest_authvar(gnutls_x509_crt_t signer, + gnutls_x509_crt_t root, + uint8_t *hash_digest) +{ + char *cn; + size_t cn_size = 0; + uint8_t fp[AUTHVAR_DIGEST_SIZE]; + size_t fp_size = sizeof(fp); + gnutls_hash_hd_t hash; + int rc; + + /* get signer CN */ + rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME, + 0, 0, NULL, &cn_size); + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) { + warn_report("gnutls_x509_crt_get_dn_by_oid error #1: %s", + gnutls_strerror(rc)); + return rc; + } + + cn = g_malloc(cn_size); + rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME, + 0, 0, cn, &cn_size); + if (rc < 0) { + warn_report("gnutls_x509_crt_get_dn_by_oid error #2: %s", + gnutls_strerror(rc)); + goto err; + } + + /* get root certificate fingerprint */ + rc = gnutls_x509_crt_get_fingerprint(root, AUTHVAR_DIGEST_ALGO, + fp, &fp_size); + if (rc < 0) { + warn_report("gnutls_x509_crt_get_fingerprint error: %s", + gnutls_strerror(rc)); + goto err; + } + + /* digest both items */ + rc = gnutls_hash_init(&hash, AUTHVAR_DIGEST_ALGO); + if (rc < 0) { + warn_report("gnutls_hash_init error: %s", + gnutls_strerror(rc)); + goto err; + } + rc = gnutls_hash(hash, cn, cn_size); + if (rc < 0) { + warn_report("gnutls_hash error: %s", + gnutls_strerror(rc)); + goto err; + } + rc = gnutls_hash(hash, fp, fp_size); + if (rc < 0) { + warn_report("gnutls_hash error: %s", + gnutls_strerror(rc)); + goto err; + } + gnutls_hash_deinit(hash, hash_digest); + + return 0; + +err: + g_free(cn); + return rc; +} + +/* + * uefi spec 2.9, section 8.2.2 + * + * For EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS variables which are + * NOT secure boot variables we should track the root certificate of the trust + * chain, and the subject CN of the signer certificate. + * + * So we'll go store a digest of these two items so we can verify this. Also + * create a gnutls_x509_trust_list_t with the root certificate, so + * gnutls_pkcs7_verify() will pass (assuming the signature is otherwise + * correct). + */ +static gnutls_x509_trust_list_t build_trust_list_authvar(gnutls_pkcs7_t pkcs7, + uint8_t *hash_digest) +{ + gnutls_datum_t signer_data = { 0 }; + gnutls_datum_t root_data = { 0 }; + gnutls_x509_crt_t signer = NULL; + gnutls_x509_crt_t root = NULL; + gnutls_x509_trust_list_t tlist = NULL; + int n, rc; + + n = gnutls_pkcs7_get_crt_count(pkcs7); + + /* first is signer certificate */ + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, 0, &signer_data); + if (rc < 0) { + warn_report("gnutls_pkcs7_get_crt_raw2(0) error: %s", + gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_init(&signer); + if (rc < 0) { + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_import(signer, &signer_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(signer); + goto done; + } + + /* last is root-of-trust certificate (can be identical to signer) */ + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, n - 1, &root_data); + if (rc < 0) { + warn_report("gnutls_pkcs7_get_crt_raw2(%d) error: %s", + n - 1, gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_init(&root); + if (rc < 0) { + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_import(root, &root_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + goto done; + } + + /* calc digest for signer CN + root cert */ + rc = build_digest_authvar(signer, root, hash_digest); + if (rc < 0) { + goto done; + } + + /* add root to trust list */ + rc = gnutls_x509_trust_list_init(&tlist, 0); + if (rc < 0) { + warn_report("gnutls_x509_trust_list_init error: %s", + gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_trust_list_add_cas(tlist, &root, 1, 0); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_trust_list_deinit(tlist, 1); + tlist = NULL; + goto done; + } else { + /* ownership passed to tlist */ + root = NULL; + } + +done: + if (signer_data.data) { + gnutls_free(signer_data.data); + } + if (root_data.data) { + gnutls_free(root_data.data); + } + if (signer) { + gnutls_x509_crt_deinit(signer); + } + if (root) { + gnutls_x509_crt_deinit(root); + } + return tlist; +} + +static void free_datum(gnutls_datum_t *ptr) +{ + if (!ptr) { + return; + } + g_free(ptr->data); + g_free(ptr); +} + +static void gnutls_log_stderr(int level, const char *msg) +{ + if (strncmp(msg, "ASSERT:", 7) == 0) { + return; + } + fprintf(stderr, " %d: %s", level, msg); +} + +/* + * pkcs7 signature verification (EFI_VARIABLE_AUTHENTICATION_2). + */ +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, + void **digest, uint32_t *digest_size, + mm_variable_access *va, void *data) +{ + gnutls_x509_trust_list_t tlist = NULL; + gnutls_datum_t *signed_data = NULL; + gnutls_datum_t *pkcs7_data = NULL; + gnutls_pkcs7_t pkcs7 = NULL; + efi_status status = EFI_SECURITY_VIOLATION; + int rc; + + if (0) { + /* gnutls debug logging */ + static bool first = true; + + if (first) { + first = false; + gnutls_global_set_log_function(gnutls_log_stderr); + gnutls_global_set_log_level(99); + } + } + + signed_data = build_signed_data(va, data); + pkcs7_data = build_pkcs7(data); + + rc = gnutls_pkcs7_init(&pkcs7); + if (rc < 0) { + warn_report("gnutls_pkcs7_init error: %s", gnutls_strerror(rc)); + goto out; + } + + rc = gnutls_pkcs7_import(pkcs7, pkcs7_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_pkcs7_import error: %s", gnutls_strerror(rc)); + goto out; + } + + if (siglist) { + /* secure boot variables */ + tlist = build_trust_list_sb(siglist); + } else if (digest && digest_size) { + /* other authenticated variables */ + *digest_size = AUTHVAR_DIGEST_SIZE; + *digest = g_malloc(*digest_size); + tlist = build_trust_list_authvar(pkcs7, *digest); + } else { + /* should not happen */ + goto out; + } + + rc = gnutls_pkcs7_verify(pkcs7, tlist, + NULL, 0, + 0, signed_data, + GNUTLS_VERIFY_DISABLE_TIME_CHECKS | + GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS); + if (rc < 0) { + warn_report("gnutls_pkcs7_verify error: %s", gnutls_strerror(rc)); + goto out; + } + + /* check passed */ + status = EFI_SUCCESS; + +out: + free_datum(signed_data); + free_datum(pkcs7_data); + if (tlist) { + gnutls_x509_trust_list_deinit(tlist, 1); + } + if (pkcs7) { + gnutls_pkcs7_deinit(pkcs7); + } + return status; +} From 4ec89b00d5bd4184455cf41af859ec08ed87d8e5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:16 +0100 Subject: [PATCH 2082/2892] hw/uefi: add var-service-pkcs7-stub.c pkcs7 stub which is used in case gnutls is not available. It throws EFI_WRITE_PROTECTED errors unconditionally, so all authenticated variables are readonly for the guest. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-13-kraxel@redhat.com> --- hw/uefi/var-service-pkcs7-stub.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 hw/uefi/var-service-pkcs7-stub.c diff --git a/hw/uefi/var-service-pkcs7-stub.c b/hw/uefi/var-service-pkcs7-stub.c new file mode 100644 index 0000000000..118cba446d --- /dev/null +++ b/hw/uefi/var-service-pkcs7-stub.c @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - pkcs7 stubs + */ +#include "qemu/osdep.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, + void **digest, uint32_t *digest_size, + mm_variable_access *va, void *data) +{ + return EFI_WRITE_PROTECTED; +} From f903e88306adfcce3b80a512a222c8551718d719 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:17 +0100 Subject: [PATCH 2083/2892] hw/uefi: add var-service-siglist.c Functions to serialize and de-serialize EFI signature databases. This is needed to merge signature databases (happens in practice when appending dbx updates) and also to extract the certificates for pkcs7 signature verification. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-14-kraxel@redhat.com> --- hw/uefi/var-service-siglist.c | 212 ++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 hw/uefi/var-service-siglist.c diff --git a/hw/uefi/var-service-siglist.c b/hw/uefi/var-service-siglist.c new file mode 100644 index 0000000000..8948f1b784 --- /dev/null +++ b/hw/uefi/var-service-siglist.c @@ -0,0 +1,212 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - parse and generate efi signature databases + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +/* + * Add x509 certificate to list (with duplicate check). + */ +static void uefi_vars_siglist_add_x509(uefi_vars_siglist *siglist, + QemuUUID *owner, + void *data, uint64_t size) +{ + uefi_vars_cert *c; + + QTAILQ_FOREACH(c, &siglist->x509, next) { + if (c->size != size) { + continue; + } + if (memcmp(c->data, data, size) != 0) { + continue; + } + return; + } + + c = g_malloc(sizeof(*c) + size); + c->owner = *owner; + c->size = size; + memcpy(c->data, data, size); + QTAILQ_INSERT_TAIL(&siglist->x509, c, next); +} + +/* + * Add sha256 hash to list (with duplicate check). + */ +static void uefi_vars_siglist_add_sha256(uefi_vars_siglist *siglist, + QemuUUID *owner, + void *data) +{ + uefi_vars_hash *h; + + QTAILQ_FOREACH(h, &siglist->sha256, next) { + if (memcmp(h->data, data, 32) != 0) { + continue; + } + return; + } + + h = g_malloc(sizeof(*h) + 32); + h->owner = *owner; + memcpy(h->data, data, 32); + QTAILQ_INSERT_TAIL(&siglist->sha256, h, next); +} + +void uefi_vars_siglist_init(uefi_vars_siglist *siglist) +{ + memset(siglist, 0, sizeof(*siglist)); + QTAILQ_INIT(&siglist->x509); + QTAILQ_INIT(&siglist->sha256); +} + +void uefi_vars_siglist_free(uefi_vars_siglist *siglist) +{ + uefi_vars_cert *c, *cs; + uefi_vars_hash *h, *hs; + + QTAILQ_FOREACH_SAFE(c, &siglist->x509, next, cs) { + QTAILQ_REMOVE(&siglist->x509, c, next); + g_free(c); + } + QTAILQ_FOREACH_SAFE(h, &siglist->sha256, next, hs) { + QTAILQ_REMOVE(&siglist->sha256, h, next); + g_free(h); + } +} + +/* + * Parse UEFI signature list. + */ +void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, + void *data, uint64_t size) +{ + efi_siglist *efilist; + uint64_t start; + + while (size) { + if (size < sizeof(*efilist)) { + break; + } + efilist = data; + if (size < efilist->siglist_size) { + break; + } + + if (uadd64_overflow(sizeof(*efilist), efilist->header_size, &start)) { + break; + } + if (efilist->sig_size <= sizeof(QemuUUID)) { + break; + } + + if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertX509Guid)) { + if (start + efilist->sig_size != efilist->siglist_size) { + break; + } + uefi_vars_siglist_add_x509(siglist, + (QemuUUID *)(data + start), + data + start + sizeof(QemuUUID), + efilist->sig_size - sizeof(QemuUUID)); + + } else if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertSha256Guid)) { + if (efilist->sig_size != sizeof(QemuUUID) + 32) { + break; + } + if (start + efilist->sig_size > efilist->siglist_size) { + break; + } + while (start <= efilist->siglist_size - efilist->sig_size) { + uefi_vars_siglist_add_sha256(siglist, + (QemuUUID *)(data + start), + data + start + sizeof(QemuUUID)); + start += efilist->sig_size; + } + + } else { + QemuUUID be = qemu_uuid_bswap(efilist->guid_type); + char *str_uuid = qemu_uuid_unparse_strdup(&be); + warn_report("%s: unknown type (%s)", __func__, str_uuid); + g_free(str_uuid); + } + + data += efilist->siglist_size; + size -= efilist->siglist_size; + } +} + +uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist) +{ + uefi_vars_cert *c; + uefi_vars_hash *h; + uint64_t size = 0; + + QTAILQ_FOREACH(c, &siglist->x509, next) { + size += sizeof(efi_siglist) + sizeof(QemuUUID) + c->size; + } + + if (!QTAILQ_EMPTY(&siglist->sha256)) { + size += sizeof(efi_siglist); + QTAILQ_FOREACH(h, &siglist->sha256, next) { + size += sizeof(QemuUUID) + 32; + } + } + + return size; +} + +/* + * Generate UEFI signature list. + */ +void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, + void *data, uint64_t size) +{ + uefi_vars_cert *c; + uefi_vars_hash *h; + efi_siglist *efilist; + uint64_t pos = 0, start; + uint32_t i; + + QTAILQ_FOREACH(c, &siglist->x509, next) { + efilist = data + pos; + efilist->guid_type = EfiCertX509Guid; + efilist->sig_size = sizeof(QemuUUID) + c->size; + efilist->header_size = 0; + + start = pos + sizeof(efi_siglist); + memcpy(data + start, + &c->owner, sizeof(QemuUUID)); + memcpy(data + start + sizeof(QemuUUID), + c->data, c->size); + + efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size; + pos += efilist->siglist_size; + } + + if (!QTAILQ_EMPTY(&siglist->sha256)) { + efilist = data + pos; + efilist->guid_type = EfiCertSha256Guid; + efilist->sig_size = sizeof(QemuUUID) + 32; + efilist->header_size = 0; + + i = 0; + start = pos + sizeof(efi_siglist); + QTAILQ_FOREACH(h, &siglist->sha256, next) { + memcpy(data + start + efilist->sig_size * i, + &h->owner, sizeof(QemuUUID)); + memcpy(data + start + efilist->sig_size * i + sizeof(QemuUUID), + h->data, 32); + i++; + } + + efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size * i; + pos += efilist->siglist_size; + } + + assert(pos == size); +} From 12058948abdf7eed8364aee79add66b40002fd5b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:18 +0100 Subject: [PATCH 2084/2892] hw/uefi: add var-service-json.c + qapi for NV vars. Define qapi schema for the uefi variable store state. Use it and the generated visitor helper functions to store persistent (EFI_VARIABLE_NON_VOLATILE) variables in JSON format on disk. Acked-by: Markus Armbruster Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-15-kraxel@redhat.com> [ incremental fix squashed in ] Message-ID: --- hw/uefi/var-service-json.c | 243 +++++++++++++++++++++++++++++++++++++ qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + qapi/uefi.json | 64 ++++++++++ 4 files changed, 309 insertions(+) create mode 100644 hw/uefi/var-service-json.c create mode 100644 qapi/uefi.json diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c new file mode 100644 index 0000000000..761082c11f --- /dev/null +++ b/hw/uefi/var-service-json.c @@ -0,0 +1,243 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - serialize non-volatile varstore from/to json, + * using qapi + * + * tools which can read/write these json files: + * - https://gitlab.com/kraxel/virt-firmware + * - https://github.com/awslabs/python-uefivars + */ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +#include "qobject/qobject.h" +#include "qobject/qjson.h" + +#include "qapi/dealloc-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/qapi-types-uefi.h" +#include "qapi/qapi-visit-uefi.h" + +static char *generate_hexstr(void *data, size_t len) +{ + static const char hex[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + }; + uint8_t *src = data; + char *dest; + size_t i; + + dest = g_malloc(len * 2 + 1); + for (i = 0; i < len * 2;) { + dest[i++] = hex[*src >> 4]; + dest[i++] = hex[*src & 15]; + src++; + } + dest[i++] = 0; + + return dest; +} + +static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv) +{ + UefiVarStore *vs; + UefiVariableList **tail; + UefiVariable *v; + QemuUUID be; + uefi_variable *var; + + vs = g_new0(UefiVarStore, 1); + vs->version = 2; + tail = &vs->variables; + + QTAILQ_FOREACH(var, &uv->variables, next) { + if (!(var->attributes & EFI_VARIABLE_NON_VOLATILE)) { + continue; + } + + v = g_new0(UefiVariable, 1); + be = qemu_uuid_bswap(var->guid); + v->guid = qemu_uuid_unparse_strdup(&be); + v->name = uefi_ucs2_to_ascii(var->name, var->name_size); + v->attr = var->attributes; + + v->data = generate_hexstr(var->data, var->data_size); + + if (var->attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + v->time = generate_hexstr(&var->time, sizeof(var->time)); + if (var->digest && var->digest_size) { + v->digest = generate_hexstr(var->digest, var->digest_size); + } + } + + QAPI_LIST_APPEND(tail, v); + } + return vs; +} + +static unsigned parse_hexchar(char c) +{ + switch (c) { + case '0' ... '9': return c - '0'; + case 'a' ... 'f': return c - 'a' + 0xa; + case 'A' ... 'F': return c - 'A' + 0xA; + default: return 0; + } +} + +static void parse_hexstr(void *dest, char *src, int len) +{ + uint8_t *data = dest; + size_t i; + + for (i = 0; i < len; i += 2) { + *(data++) = + parse_hexchar(src[i]) << 4 | + parse_hexchar(src[i + 1]); + } +} + +static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs) +{ + UefiVariableList *item; + UefiVariable *v; + QemuUUID be; + uefi_variable *var; + uint8_t *data; + size_t i, len; + + for (item = vs->variables; item != NULL; item = item->next) { + v = item->value; + + var = g_new0(uefi_variable, 1); + var->attributes = v->attr; + qemu_uuid_parse(v->guid, &be); + var->guid = qemu_uuid_bswap(be); + + len = strlen(v->name); + var->name_size = len * 2 + 2; + var->name = g_malloc(var->name_size); + for (i = 0; i <= len; i++) { + var->name[i] = v->name[i]; + } + + len = strlen(v->data); + var->data_size = len / 2; + var->data = data = g_malloc(var->data_size); + parse_hexstr(var->data, v->data, len); + + if (v->time && strlen(v->time) == 32) { + parse_hexstr(&var->time, v->time, 32); + } + + if (v->digest) { + len = strlen(v->digest); + var->digest_size = len / 2; + var->digest = g_malloc(var->digest_size); + parse_hexstr(var->digest, v->digest, len); + } + + QTAILQ_INSERT_TAIL(&uv->variables, var, next); + } +} + +static GString *uefi_vars_to_json(uefi_vars_state *uv) +{ + UefiVarStore *vs = uefi_vars_to_qapi(uv); + QObject *qobj = NULL; + Visitor *v; + GString *gstr; + + v = qobject_output_visitor_new(&qobj); + if (visit_type_UefiVarStore(v, NULL, &vs, NULL)) { + visit_complete(v, &qobj); + } + visit_free(v); + qapi_free_UefiVarStore(vs); + + gstr = qobject_to_json_pretty(qobj, true); + qobject_unref(qobj); + + return gstr; +} + +void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) +{ + if (uv->jsonfile) { + uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp); + } +} + +void uefi_vars_json_save(uefi_vars_state *uv) +{ + GString *gstr; + int rc; + + if (uv->jsonfd == -1) { + return; + } + + gstr = uefi_vars_to_json(uv); + + lseek(uv->jsonfd, 0, SEEK_SET); + rc = ftruncate(uv->jsonfd, 0); + if (rc != 0) { + warn_report("%s: ftruncate error", __func__); + } + rc = write(uv->jsonfd, gstr->str, gstr->len); + if (rc != gstr->len) { + warn_report("%s: write error", __func__); + } + fsync(uv->jsonfd); + + g_string_free(gstr, true); +} + +void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) +{ + UefiVarStore *vs; + QObject *qobj; + Visitor *v; + char *str; + size_t len; + int rc; + + if (uv->jsonfd == -1) { + return; + } + + len = lseek(uv->jsonfd, 0, SEEK_END); + if (len == 0) { + return; + } + + str = g_malloc(len + 1); + lseek(uv->jsonfd, 0, SEEK_SET); + rc = read(uv->jsonfd, str, len); + if (rc != len) { + warn_report("%s: read error", __func__); + } + str[len] = 0; + + qobj = qobject_from_json(str, errp); + v = qobject_input_visitor_new(qobj); + visit_type_UefiVarStore(v, NULL, &vs, errp); + visit_free(v); + + if (!(*errp)) { + uefi_vars_from_qapi(uv, vs); + uefi_vars_update_storage(uv); + } + + qapi_free_UefiVarStore(vs); + qobject_unref(qobj); + g_free(str); +} diff --git a/qapi/meson.build b/qapi/meson.build index e7bc54e5d0..eadde4db30 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -65,6 +65,7 @@ if have_system 'pci', 'rocker', 'tpm', + 'uefi', ] endif if have_system or have_tools diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index b1581988e4..2877aff73d 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -81,3 +81,4 @@ { 'include': 'vfio.json' } { 'include': 'cryptodev.json' } { 'include': 'cxl.json' } +{ 'include': 'uefi.json' } diff --git a/qapi/uefi.json b/qapi/uefi.json new file mode 100644 index 0000000000..bdfcabe1df --- /dev/null +++ b/qapi/uefi.json @@ -0,0 +1,64 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# + +## +# = UEFI Variable Store +# +# The qemu efi variable store implementation (hw/uefi/) uses this to +# store non-volatile variables in json format on disk. +# +# This is an existing format already supported by (at least) two other +# projects, specifically https://gitlab.com/kraxel/virt-firmware and +# https://github.com/awslabs/python-uefivars. +## + +## +# @UefiVariable: +# +# UEFI Variable. Check the UEFI specifification for more detailed +# information on the fields. +# +# @guid: variable namespace GUID +# +# @name: variable name, in UTF-8 encoding. +# +# @attr: variable attributes. +# +# @data: variable value, encoded as hex string. +# +# @time: variable modification time. EFI_TIME struct, encoded as hex +# string. Used only for authenticated variables, where the +# EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute bit +# is set. +# +# @digest: variable certificate digest. Used to verify the signature +# of updates for authenticated variables. UEFI has two kinds of +# authenticated variables. The secure boot variables ('PK', +# 'KEK', 'db' and 'dbx') have hard coded signature checking rules. +# For other authenticated variables the firmware stores a digest +# of the signing certificate at variable creation time, and any +# updates must be signed with the same certificate. +# +# Since: 10.0 +## +{ 'struct' : 'UefiVariable', + 'data' : { 'guid' : 'str', + 'name' : 'str', + 'attr' : 'int', + 'data' : 'str', + '*time' : 'str', + '*digest' : 'str'}} + +## +# @UefiVarStore: +# +# @version: currently always 2 +# +# @variables: list of UEFI variables +# +# Since: 10.0 +## +{ 'struct' : 'UefiVarStore', + 'data' : { 'version' : 'int', + 'variables' : [ 'UefiVariable' ] }} From 9282bed590ab54a24659fdcff668d46d7129d946 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:19 +0100 Subject: [PATCH 2085/2892] hw/uefi: add trace-events Add trace events for debugging and trouble shooting. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-16-kraxel@redhat.com> --- hw/uefi/trace-events | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 hw/uefi/trace-events diff --git a/hw/uefi/trace-events b/hw/uefi/trace-events new file mode 100644 index 0000000000..3694712a94 --- /dev/null +++ b/hw/uefi/trace-events @@ -0,0 +1,17 @@ +# device +uefi_reg_read(uint64_t addr, unsigned size) "addr 0x%" PRIx64 ", size %u" +uefi_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr 0x%" PRIx64 ", val 0x%" PRIx64 ", size %d" +uefi_hard_reset(void) "" + +# generic uefi +uefi_variable(const char *context, const char *name, uint64_t size, const char *uuid) "context %s, name %s, size %" PRIu64 ", uuid %s" +uefi_status(const char *context, const char *name) "context %s, status %s" +uefi_event(const char *name) "event %s" + +# variable protocol +uefi_vars_proto_cmd(const char *cmd) "cmd %s" +uefi_vars_security_violation(const char *reason) "reason %s" + +# variable policy protocol +uefi_vars_policy_cmd(const char *cmd) "cmd %s" +uefi_vars_policy_deny(const char *reason) "reason %s" From e8371973d7e651f9d7b82100cb9745fd2795d722 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:20 +0100 Subject: [PATCH 2086/2892] hw/uefi: add UEFI_VARS to Kconfig Add UEFI_VARS config option, enable by default for x86_64 and aarch64. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-17-kraxel@redhat.com> --- hw/Kconfig | 1 + hw/uefi/Kconfig | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 hw/uefi/Kconfig diff --git a/hw/Kconfig b/hw/Kconfig index 1b4e9bb07f..c4dfe2e7af 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -37,6 +37,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source uefi/Kconfig source ufs/Kconfig source usb/Kconfig source virtio/Kconfig diff --git a/hw/uefi/Kconfig b/hw/uefi/Kconfig new file mode 100644 index 0000000000..ca6c2bc46a --- /dev/null +++ b/hw/uefi/Kconfig @@ -0,0 +1,3 @@ +config UEFI_VARS + bool + default y if X86_64 || AARCH64 From 736ca80cdd18c837dcfca016a6df746a70d33bb2 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:21 +0100 Subject: [PATCH 2087/2892] hw/uefi: add to meson Wire up uefi-vars in the build system. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-18-kraxel@redhat.com> --- hw/meson.build | 1 + hw/uefi/meson.build | 19 +++++++++++++++++++ meson.build | 1 + 3 files changed, 21 insertions(+) diff --git a/hw/meson.build b/hw/meson.build index b827c82c5d..138f5d59e1 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -35,6 +35,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('uefi') subdir('ufs') subdir('usb') subdir('vfio') diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build index a8b1689412..e63708aa16 100644 --- a/hw/uefi/meson.build +++ b/hw/uefi/meson.build @@ -1 +1,20 @@ system_ss.add(files('hardware-info.c')) + +uefi_vars_ss = ss.source_set() +if (config_all_devices.has_key('CONFIG_UEFI_VARS')) + uefi_vars_ss.add(files('var-service-core.c', + 'var-service-json.c', + 'var-service-vars.c', + 'var-service-auth.c', + 'var-service-guid.c', + 'var-service-utils.c', + 'var-service-policy.c')) + uefi_vars_ss.add(when: gnutls, + if_true: files('var-service-pkcs7.c'), + if_false: files('var-service-pkcs7-stub.c')) + uefi_vars_ss.add(files('var-service-siglist.c')) +endif + +modules += { 'hw-uefi' : { + 'vars' : uefi_vars_ss, +}} diff --git a/meson.build b/meson.build index 0a2c61d2bf..1c1982dac3 100644 --- a/meson.build +++ b/meson.build @@ -3601,6 +3601,7 @@ if have_system 'hw/ssi', 'hw/timer', 'hw/tpm', + 'hw/uefi', 'hw/ufs', 'hw/usb', 'hw/vfio', From 5bb89df2e37382d6d278d52cfb22ae61f60daf70 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:22 +0100 Subject: [PATCH 2088/2892] hw/uefi: add uefi-vars-sysbus device This adds sysbus bindings for the variable service. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-19-kraxel@redhat.com> --- hw/uefi/meson.build | 3 +- hw/uefi/var-service-sysbus.c | 91 ++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 hw/uefi/var-service-sysbus.c diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build index e63708aa16..91eb95f89e 100644 --- a/hw/uefi/meson.build +++ b/hw/uefi/meson.build @@ -8,7 +8,8 @@ if (config_all_devices.has_key('CONFIG_UEFI_VARS')) 'var-service-auth.c', 'var-service-guid.c', 'var-service-utils.c', - 'var-service-policy.c')) + 'var-service-policy.c', + 'var-service-sysbus.c')) uefi_vars_ss.add(when: gnutls, if_true: files('var-service-pkcs7.c'), if_false: files('var-service-pkcs7-stub.c')) diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c new file mode 100644 index 0000000000..60072c8815 --- /dev/null +++ b/hw/uefi/var-service-sysbus.c @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - sysbus variant. + */ +#include "qemu/osdep.h" +#include "migration/vmstate.h" + +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" + +OBJECT_DECLARE_SIMPLE_TYPE(uefi_vars_sysbus_state, UEFI_VARS_SYSBUS) + +struct uefi_vars_sysbus_state { + SysBusDevice parent_obj; + struct uefi_vars_state state; +}; + +static const VMStateDescription vmstate_uefi_vars_sysbus = { + .name = TYPE_UEFI_VARS_SYSBUS, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(state, uefi_vars_sysbus_state, 0, + vmstate_uefi_vars, uefi_vars_state), + VMSTATE_END_OF_LIST() + } +}; + +static const Property uefi_vars_sysbus_properties[] = { + DEFINE_PROP_SIZE("size", uefi_vars_sysbus_state, state.max_storage, + 256 * 1024), + DEFINE_PROP_STRING("jsonfile", uefi_vars_sysbus_state, state.jsonfile), + DEFINE_PROP_BOOL("force-secure-boot", uefi_vars_sysbus_state, + state.force_secure_boot, false), + DEFINE_PROP_BOOL("disable-custom-mode", uefi_vars_sysbus_state, + state.disable_custom_mode, false), + DEFINE_PROP_BOOL("use-pio", uefi_vars_sysbus_state, + state.use_pio, false), +}; + +static void uefi_vars_sysbus_init(Object *obj) +{ + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(obj); + + uefi_vars_init(obj, &uv->state); +} + +static void uefi_vars_sysbus_reset(DeviceState *dev) +{ + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(dev); + + uefi_vars_hard_reset(&uv->state); +} + +static void uefi_vars_sysbus_realize(DeviceState *dev, Error **errp) +{ + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(dev); + SysBusDevice *sysbus = SYS_BUS_DEVICE(dev); + + sysbus_init_mmio(sysbus, &uv->state.mr); + uefi_vars_realize(&uv->state, errp); +} + +static void uefi_vars_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = uefi_vars_sysbus_realize; + dc->vmsd = &vmstate_uefi_vars_sysbus; + device_class_set_legacy_reset(dc, uefi_vars_sysbus_reset); + device_class_set_props(dc, uefi_vars_sysbus_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo uefi_vars_sysbus_info = { + .name = TYPE_UEFI_VARS_SYSBUS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(uefi_vars_sysbus_state), + .instance_init = uefi_vars_sysbus_init, + .class_init = uefi_vars_sysbus_class_init, +}; +module_obj(TYPE_UEFI_VARS_SYSBUS); + +static void uefi_vars_sysbus_register_types(void) +{ + type_register_static(&uefi_vars_sysbus_info); +} + +type_init(uefi_vars_sysbus_register_types) From 03223b665c675f6f7d05555d9c6a69678c9ab875 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:23 +0100 Subject: [PATCH 2089/2892] hw/uefi-vars-sysbus: qemu platform bus support Add and register function to create an device tree entry when the device is added to the qemu platform bus. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-20-kraxel@redhat.com> --- hw/core/sysbus-fdt.c | 24 ++++++++++++++++++++++++ hw/uefi/var-service-sysbus.c | 1 + 2 files changed, 25 insertions(+) diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index 774c0aed41..e85066b905 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -36,6 +36,7 @@ #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" /* @@ -471,6 +472,28 @@ static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque) } #endif +static int add_uefi_vars_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusFDTData *data = opaque; + PlatformBusDevice *pbus = data->pbus; + const char *parent_node = data->pbus_node_name; + void *fdt = data->fdt; + uint64_t mmio_base; + char *nodename; + + mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, + UEFI_VARS_FDT_NODE, mmio_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, + "compatible", UEFI_VARS_FDT_COMPAT); + qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + 1, mmio_base, + 1, UEFI_VARS_REGS_SIZE); + g_free(nodename); + return 0; +} + static int no_fdt_node(SysBusDevice *sbdev, void *opaque) { return 0; @@ -495,6 +518,7 @@ static const BindingEntry bindings[] = { TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), + TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), TYPE_BINDING("", NULL), /* last element */ }; diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c index 60072c8815..28572981c2 100644 --- a/hw/uefi/var-service-sysbus.c +++ b/hw/uefi/var-service-sysbus.c @@ -69,6 +69,7 @@ static void uefi_vars_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = uefi_vars_sysbus_realize; dc->vmsd = &vmstate_uefi_vars_sysbus; + dc->user_creatable = true; device_class_set_legacy_reset(dc, uefi_vars_sysbus_reset); device_class_set_props(dc, uefi_vars_sysbus_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); From 69392de9138fe6d49f96dfae8adfb9cd64e0578e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:24 +0100 Subject: [PATCH 2090/2892] hw/uefi-vars-sysbus: add x64 variant The x86 variant of the device is mapped on the fixed address 0xfef10000 and uses etc/hardware-info instead of FDT to pass the mapping location to the edk2 firmware. The latter allows to move the device to a different location should that turn out to be necessary in the future. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-21-kraxel@redhat.com> --- hw/uefi/var-service-sysbus.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c index 28572981c2..97da8672ee 100644 --- a/hw/uefi/var-service-sysbus.c +++ b/hw/uefi/var-service-sysbus.c @@ -9,6 +9,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" +#include "hw/uefi/hardware-info.h" #include "hw/uefi/var-service.h" #include "hw/uefi/var-service-api.h" @@ -75,6 +76,7 @@ static void uefi_vars_sysbus_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); } +/* generic: hardware discovery via FDT */ static const TypeInfo uefi_vars_sysbus_info = { .name = TYPE_UEFI_VARS_SYSBUS, .parent = TYPE_SYS_BUS_DEVICE, @@ -84,9 +86,39 @@ static const TypeInfo uefi_vars_sysbus_info = { }; module_obj(TYPE_UEFI_VARS_SYSBUS); +static void uefi_vars_x64_realize(DeviceState *dev, Error **errp) +{ + HARDWARE_INFO_SIMPLE_DEVICE hwinfo = { + .mmio_address = cpu_to_le64(0xfef10000), + }; + SysBusDevice *sysbus = SYS_BUS_DEVICE(dev); + + uefi_vars_sysbus_realize(dev, errp); + + hardware_info_register(HardwareInfoQemuUefiVars, + &hwinfo, sizeof(hwinfo)); + sysbus_mmio_map(sysbus, 0, hwinfo.mmio_address); +} + +static void uefi_vars_x64_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = uefi_vars_x64_realize; +} + +/* x64: hardware discovery via etc/hardware-info fw_cfg */ +static const TypeInfo uefi_vars_x64_info = { + .name = TYPE_UEFI_VARS_X64, + .parent = TYPE_UEFI_VARS_SYSBUS, + .class_init = uefi_vars_x64_class_init, +}; +module_obj(TYPE_UEFI_VARS_X64); + static void uefi_vars_sysbus_register_types(void) { type_register_static(&uefi_vars_sysbus_info); + type_register_static(&uefi_vars_x64_info); } type_init(uefi_vars_sysbus_register_types) From 22ebb90e62e97a431ec50edf7ddb19412a934555 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:25 +0100 Subject: [PATCH 2091/2892] hw/uefi-vars-sysbus: allow for arm virt Allow the device being added to aarch64 virt VMs. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-22-kraxel@redhat.com> --- hw/arm/virt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ee69081ef4..904c698b14 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -82,6 +82,7 @@ #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" #include "hw/acpi/generic_event_device.h" +#include "hw/uefi/var-service-api.h" #include "hw/virtio/virtio-md-pci.h" #include "hw/virtio/virtio-iommu.h" #include "hw/char/pl011.h" @@ -3162,6 +3163,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif From 918b8a12247090a03ac8db61ac19a97170a553df Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:26 +0100 Subject: [PATCH 2092/2892] hw/uefi-vars-sysbus: allow for pc and q35 Allow the device being added to x86_64 pc and q35 VMs. Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-23-kraxel@redhat.com> --- hw/i386/pc_piix.c | 2 ++ hw/i386/pc_q35.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 04d2957adc..6c91e2d292 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -65,6 +65,7 @@ #include "system/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" +#include "hw/uefi/var-service-api.h" #include "hw/i386/acpi-build.h" #include "target/i386/cpu.h" @@ -468,6 +469,7 @@ static void pc_i440fx_machine_options(MachineClass *m) m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_UEFI_VARS_X64); object_class_property_add_enum(oc, "x-south-bridge", "PCSouthBridgeOption", &PCSouthBridgeOption_lookup, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 77536dd697..fd96d0345c 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -58,6 +58,7 @@ #include "system/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" +#include "hw/uefi/var-service-api.h" #include "hw/i386/acpi-build.h" #include "target/i386/cpu.h" @@ -355,6 +356,7 @@ static void pc_q35_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_UEFI_VARS_X64); compat_props_add(m->compat_props, pc_q35_compat_defaults, pc_q35_compat_defaults_len); } From 06fa8ec6f656af28624ab2da20541ce5d6e8c18e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:27 +0100 Subject: [PATCH 2093/2892] hw/uefi: add MAINTAINERS entry Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-24-kraxel@redhat.com> --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2e7fc6fa91..27cdfbebdd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2820,6 +2820,12 @@ F: hw/misc/ivshmem-flat.c F: include/hw/misc/ivshmem-flat.h F: docs/system/devices/ivshmem-flat.rst +UEFI variable service +M: Gerd Hoffmann +S: Maintained +F: hw/uefi/ +F: include/hw/uefi/ + Subsystems ---------- Overall Audio backends From 2bc10b15deb4b29391628e10b18701bfbcf4be17 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 25 Feb 2025 17:30:28 +0100 Subject: [PATCH 2094/2892] docs: add uefi variable service documentation Signed-off-by: Gerd Hoffmann Message-ID: <20250225163031.1409078-25-kraxel@redhat.com> --- docs/devel/index-internals.rst | 1 + docs/devel/uefi-vars.rst | 68 ++++++++++++++++++++++++++++++++++ hw/uefi/LIMITATIONS.md | 7 ++++ 3 files changed, 76 insertions(+) create mode 100644 docs/devel/uefi-vars.rst create mode 100644 hw/uefi/LIMITATIONS.md diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index bca597c658..7a0678cbdd 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -20,6 +20,7 @@ Details about QEMU's various subsystems including how to add features to them. s390-cpu-topology s390-dasd-ipl tracing + uefi-vars vfio-iommufd writing-monitor-commands virtio-backends diff --git a/docs/devel/uefi-vars.rst b/docs/devel/uefi-vars.rst new file mode 100644 index 0000000000..0151a26a0a --- /dev/null +++ b/docs/devel/uefi-vars.rst @@ -0,0 +1,68 @@ +============== +UEFI variables +============== + +Guest UEFI variable management +============================== + +The traditional approach for UEFI Variable storage in qemu guests is +to work as close as possible to physical hardware. That means +providing pflash as storage and leaving the management of variables +and flash to the guest. + +Secure boot support comes with the requirement that the UEFI variable +storage must be protected against direct access by the OS. All update +requests must pass the sanity checks. (Parts of) the firmware must +run with a higher privilege level than the OS so this can be enforced +by the firmware. On x86 this has been implemented using System +Management Mode (SMM) in qemu and kvm, which again is the same +approach taken by physical hardware. Only privileged code running in +SMM mode is allowed to access flash storage. + +Communication with the firmware code running in SMM mode works by +serializing the requests to a shared buffer, then trapping into SMM +mode via SMI. The SMM code processes the request, stores the reply in +the same buffer and returns. + +Host UEFI variable service +========================== + +Instead of running the privileged code inside the guest we can run it +on the host. The serialization protocol can be reused. The +communication with the host uses a virtual device, which essentially +configures the shared buffer location and size, and traps to the host +to process the requests. + +The ``uefi-vars`` device implements the UEFI virtual device. It comes +in ``uefi-vars-x86`` and ``uefi-vars-sysbus`` flavours. The device +reimplements the handlers needed, specifically +``EfiSmmVariableProtocol`` and ``VarCheckPolicyLibMmiHandler``. It +also consumes events (``EfiEndOfDxeEventGroup``, +``EfiEventReadyToBoot`` and ``EfiEventExitBootServices``). + +The advantage of the approach is that we do not need a special +privilege level for the firmware to protect itself, i.e. it does not +depend on SMM emulation on x64, which allows the removal of a bunch of +complex code for SMM emulation from the linux kernel +(CONFIG_KVM_SMM=n). It also allows support for secure boot on arm +without implementing secure world (el3) emulation in kvm. + +Of course there are also downsides. The added device increases the +attack surface of the host, and we are adding some code duplication +because we have to reimplement some edk2 functionality in qemu. + +usage on x86_64 +--------------- + +.. code:: + + qemu-system-x86_64 \ + -device uefi-vars-x86,jsonfile=/path/to/vars.json + +usage on aarch64 +---------------- + +.. code:: + + qemu-system-aarch64 -M virt \ + -device uefi-vars-sysbus,jsonfile=/path/to/vars.json diff --git a/hw/uefi/LIMITATIONS.md b/hw/uefi/LIMITATIONS.md new file mode 100644 index 0000000000..29308bd587 --- /dev/null +++ b/hw/uefi/LIMITATIONS.md @@ -0,0 +1,7 @@ +known issues and limitations +---------------------------- + +* works only on little endian hosts + - accessing structs in guest ram is done without endian conversion. +* works only for 64-bit guests + - UINTN is mapped to uint64_t, for 32-bit guests that would be uint32_t From 996fa948f9cc87b97375e78077c7e36df100a6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 27 Dec 2024 21:12:07 +0100 Subject: [PATCH 2095/2892] hw/intc: Remove TCG dependency on ARM_GICV3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TYPE_ARM_GICV3 model doesn't have any particular dependency on TCG, remove it. Rename the Kconfig selector ARM_GICV3_TCG -> ARM_GICV3. Fixes: a8a5546798c ("hw/intc/arm_gicv3: Introduce CONFIG_ARM_GIC_TCG Kconfig selector") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Phil Dennis-Jordan Tested-by: Phil Dennis-Jordan Message-Id: <20241227202435.48055-2-philmd@linaro.org> --- hw/intc/Kconfig | 6 +++--- hw/intc/meson.build | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index dd405bdb5d..7547528f2c 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -23,13 +23,13 @@ config APIC config ARM_GIC bool - select ARM_GICV3_TCG if TCG + select ARM_GICV3 if TCG select ARM_GIC_KVM if KVM select MSI_NONBROKEN -config ARM_GICV3_TCG +config ARM_GICV3 bool - depends on ARM_GIC && TCG + depends on ARM_GIC config ARM_GIC_KVM bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 510fdfb688..602da304b0 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -6,7 +6,7 @@ system_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( 'arm_gicv3_common.c', 'arm_gicv3_its_common.c', )) -system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( +system_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files( 'arm_gicv3.c', 'arm_gicv3_dist.c', 'arm_gicv3_its.c', @@ -39,7 +39,7 @@ endif specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) -specific_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files('arm_gicv3_cpuif.c')) +specific_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) From a89607c4d0e421fa600b84e446a50f0f5f078941 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:40:28 +0000 Subject: [PATCH 2096/2892] hw/misc/pvpanic: Add MMIO interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition to the ISA and PCI variants of pvpanic, let's add an MMIO platform device that we can use in embedded arm environments. Signed-off-by: Alexander Graf Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-8-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/Kconfig | 4 +++ hw/misc/meson.build | 1 + hw/misc/pvpanic-mmio.c | 60 +++++++++++++++++++++++++++++++++++++++ include/hw/misc/pvpanic.h | 1 + 4 files changed, 66 insertions(+) create mode 100644 hw/misc/pvpanic-mmio.c diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 82bd68b4bb..ec0fa5aa9f 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -148,6 +148,10 @@ config PVPANIC_ISA depends on ISA_BUS select PVPANIC_COMMON +config PVPANIC_MMIO + bool + select PVPANIC_COMMON + config AUX bool select I2C diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 0b5187a2f7..6d47de482c 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -126,6 +126,7 @@ system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) system_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) system_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) +system_ss.add(when: 'CONFIG_PVPANIC_MMIO', if_true: files('pvpanic-mmio.c')) system_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_hace.c', diff --git a/hw/misc/pvpanic-mmio.c b/hw/misc/pvpanic-mmio.c new file mode 100644 index 0000000000..70097cecc7 --- /dev/null +++ b/hw/misc/pvpanic-mmio.c @@ -0,0 +1,60 @@ +/* + * QEMU simulated pvpanic device (MMIO frontend) + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "hw/qdev-properties.h" +#include "hw/misc/pvpanic.h" +#include "hw/sysbus.h" +#include "standard-headers/misc/pvpanic.h" + +OBJECT_DECLARE_SIMPLE_TYPE(PVPanicMMIOState, PVPANIC_MMIO_DEVICE) + +#define PVPANIC_MMIO_SIZE 0x2 + +struct PVPanicMMIOState { + SysBusDevice parent_obj; + + PVPanicState pvpanic; +}; + +static void pvpanic_mmio_initfn(Object *obj) +{ + PVPanicMMIOState *s = PVPANIC_MMIO_DEVICE(obj); + + pvpanic_setup_io(&s->pvpanic, DEVICE(s), PVPANIC_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pvpanic.mr); +} + +static const Property pvpanic_mmio_properties[] = { + DEFINE_PROP_UINT8("events", PVPanicMMIOState, pvpanic.events, + PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), +}; + +static void pvpanic_mmio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, pvpanic_mmio_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo pvpanic_mmio_info = { + .name = TYPE_PVPANIC_MMIO_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PVPanicMMIOState), + .instance_init = pvpanic_mmio_initfn, + .class_init = pvpanic_mmio_class_init, +}; + +static void pvpanic_register_types(void) +{ + type_register_static(&pvpanic_mmio_info); +} + +type_init(pvpanic_register_types) diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index 9a71a5ad0d..049a94c112 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -26,6 +26,7 @@ #define TYPE_PVPANIC_ISA_DEVICE "pvpanic" #define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci" +#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio" #define PVPANIC_IOPORT_PROP "ioport" From 11fa056e792a99c83de34af8d4266fef90e498cb Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:56:23 +0000 Subject: [PATCH 2097/2892] hw: Add vmapple subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will introduce a number of devices that are specific to the vmapple target machine. To keep them all tidily together, let's put them into a single target directory. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-7-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 7 +++++++ hw/Kconfig | 1 + hw/meson.build | 1 + hw/vmapple/Kconfig | 1 + hw/vmapple/meson.build | 1 + hw/vmapple/trace-events | 2 ++ hw/vmapple/trace.h | 2 ++ meson.build | 1 + 8 files changed, 16 insertions(+) create mode 100644 hw/vmapple/Kconfig create mode 100644 hw/vmapple/meson.build create mode 100644 hw/vmapple/trace-events create mode 100644 hw/vmapple/trace.h diff --git a/MAINTAINERS b/MAINTAINERS index 2e7fc6fa91..0e16036091 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2820,6 +2820,13 @@ F: hw/misc/ivshmem-flat.c F: include/hw/misc/ivshmem-flat.h F: docs/system/devices/ivshmem-flat.rst +VMapple +M: Alexander Graf +M: Phil Dennis-Jordan +S: Maintained +F: hw/vmapple/* +F: include/hw/vmapple/* + Subsystems ---------- Overall Audio backends diff --git a/hw/Kconfig b/hw/Kconfig index 1b4e9bb07f..2871784cfd 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -41,6 +41,7 @@ source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig +source vmapple/Kconfig source xen/Kconfig source watchdog/Kconfig diff --git a/hw/meson.build b/hw/meson.build index b827c82c5d..9c4f6d0d63 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -39,6 +39,7 @@ subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') +subdir('vmapple') subdir('watchdog') subdir('xen') subdir('xenpv') diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig new file mode 100644 index 0000000000..315c06b689 --- /dev/null +++ b/hw/vmapple/Kconfig @@ -0,0 +1 @@ +# SPDX-License-Identifier: GPL-2.0-or-later diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build new file mode 100644 index 0000000000..315c06b689 --- /dev/null +++ b/hw/vmapple/meson.build @@ -0,0 +1 @@ +# SPDX-License-Identifier: GPL-2.0-or-later diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events new file mode 100644 index 0000000000..2112579412 --- /dev/null +++ b/hw/vmapple/trace-events @@ -0,0 +1,2 @@ +# See docs/devel/tracing.rst for syntax documentation. +# SPDX-License-Identifier: GPL-2.0-or-later diff --git a/hw/vmapple/trace.h b/hw/vmapple/trace.h new file mode 100644 index 0000000000..d099d5ecd9 --- /dev/null +++ b/hw/vmapple/trace.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-hw_vmapple.h" diff --git a/meson.build b/meson.build index 0a2c61d2bf..d1b807aa53 100644 --- a/meson.build +++ b/meson.build @@ -3605,6 +3605,7 @@ if have_system 'hw/usb', 'hw/vfio', 'hw/virtio', + 'hw/vmapple', 'hw/watchdog', 'hw/xen', 'hw/gpio', From c960b389554bd04e645e321e1cee1d3b4590cc83 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:56:25 +0000 Subject: [PATCH 2098/2892] hw/vmapple/aes: Introduce aes engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VMApple contains an "aes" engine device that it uses to encrypt and decrypt its nvram. It has trivial hard coded keys it uses for that purpose. Add device emulation for this device model. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-10-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/vmapple/Kconfig | 3 + hw/vmapple/aes.c | 581 +++++++++++++++++++++++++++++++++++ hw/vmapple/meson.build | 2 + hw/vmapple/trace-events | 14 + include/hw/vmapple/vmapple.h | 17 + include/qemu/cutils.h | 15 + util/hexdump.c | 18 ++ 7 files changed, 650 insertions(+) create mode 100644 hw/vmapple/aes.c create mode 100644 include/hw/vmapple/vmapple.h diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig index 315c06b689..b1944d7312 100644 --- a/hw/vmapple/Kconfig +++ b/hw/vmapple/Kconfig @@ -1 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-or-later + +config VMAPPLE_AES + bool diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c new file mode 100644 index 0000000000..3a7641ab4b --- /dev/null +++ b/hw/vmapple/aes.c @@ -0,0 +1,581 @@ +/* + * QEMU Apple AES device emulation + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "crypto/hash.h" +#include "crypto/aes.h" +#include "crypto/cipher.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/vmapple/vmapple.h" +#include "migration/vmstate.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "system/dma.h" + +OBJECT_DECLARE_SIMPLE_TYPE(AESState, APPLE_AES) + +#define MAX_FIFO_SIZE 9 + +#define CMD_KEY 0x1 +#define CMD_KEY_CONTEXT_SHIFT 27 +#define CMD_KEY_CONTEXT_MASK (0x1 << CMD_KEY_CONTEXT_SHIFT) +#define CMD_KEY_SELECT_MAX_IDX 0x7 +#define CMD_KEY_SELECT_SHIFT 24 +#define CMD_KEY_SELECT_MASK (CMD_KEY_SELECT_MAX_IDX << CMD_KEY_SELECT_SHIFT) +#define CMD_KEY_KEY_LEN_NUM 4u +#define CMD_KEY_KEY_LEN_SHIFT 22 +#define CMD_KEY_KEY_LEN_MASK ((CMD_KEY_KEY_LEN_NUM - 1u) << CMD_KEY_KEY_LEN_SHIFT) +#define CMD_KEY_ENCRYPT_SHIFT 20 +#define CMD_KEY_ENCRYPT_MASK (0x1 << CMD_KEY_ENCRYPT_SHIFT) +#define CMD_KEY_BLOCK_MODE_SHIFT 16 +#define CMD_KEY_BLOCK_MODE_MASK (0x3 << CMD_KEY_BLOCK_MODE_SHIFT) +#define CMD_IV 0x2 +#define CMD_IV_CONTEXT_SHIFT 26 +#define CMD_IV_CONTEXT_MASK (0x3 << CMD_KEY_CONTEXT_SHIFT) +#define CMD_DSB 0x3 +#define CMD_SKG 0x4 +#define CMD_DATA 0x5 +#define CMD_DATA_KEY_CTX_SHIFT 27 +#define CMD_DATA_KEY_CTX_MASK (0x1 << CMD_DATA_KEY_CTX_SHIFT) +#define CMD_DATA_IV_CTX_SHIFT 25 +#define CMD_DATA_IV_CTX_MASK (0x3 << CMD_DATA_IV_CTX_SHIFT) +#define CMD_DATA_LEN_MASK 0xffffff +#define CMD_STORE_IV 0x6 +#define CMD_STORE_IV_ADDR_MASK 0xffffff +#define CMD_WRITE_REG 0x7 +#define CMD_FLAG 0x8 +#define CMD_FLAG_STOP_MASK BIT(26) +#define CMD_FLAG_RAISE_IRQ_MASK BIT(27) +#define CMD_FLAG_INFO_MASK 0xff +#define CMD_MAX 0x10 + +#define CMD_SHIFT 28 + +#define REG_STATUS 0xc +#define REG_STATUS_DMA_READ_RUNNING BIT(0) +#define REG_STATUS_DMA_READ_PENDING BIT(1) +#define REG_STATUS_DMA_WRITE_RUNNING BIT(2) +#define REG_STATUS_DMA_WRITE_PENDING BIT(3) +#define REG_STATUS_BUSY BIT(4) +#define REG_STATUS_EXECUTING BIT(5) +#define REG_STATUS_READY BIT(6) +#define REG_STATUS_TEXT_DPA_SEEDED BIT(7) +#define REG_STATUS_UNWRAP_DPA_SEEDED BIT(8) + +#define REG_IRQ_STATUS 0x18 +#define REG_IRQ_STATUS_INVALID_CMD BIT(2) +#define REG_IRQ_STATUS_FLAG BIT(5) +#define REG_IRQ_ENABLE 0x1c +#define REG_WATERMARK 0x20 +#define REG_Q_STATUS 0x24 +#define REG_FLAG_INFO 0x30 +#define REG_FIFO 0x200 + +static const uint32_t key_lens[CMD_KEY_KEY_LEN_NUM] = { + [0] = 16, + [1] = 24, + [2] = 32, + [3] = 64, +}; + +typedef struct Key { + uint32_t key_len; + uint8_t key[32]; +} Key; + +typedef struct IV { + uint32_t iv[4]; +} IV; + +static Key builtin_keys[CMD_KEY_SELECT_MAX_IDX + 1] = { + [1] = { + .key_len = 32, + .key = { 0x1 }, + }, + [2] = { + .key_len = 32, + .key = { 0x2 }, + }, + [3] = { + .key_len = 32, + .key = { 0x3 }, + } +}; + +struct AESState { + SysBusDevice parent_obj; + + qemu_irq irq; + MemoryRegion iomem1; + MemoryRegion iomem2; + AddressSpace *as; + + uint32_t status; + uint32_t q_status; + uint32_t irq_status; + uint32_t irq_enable; + uint32_t watermark; + uint32_t flag_info; + uint32_t fifo[MAX_FIFO_SIZE]; + uint32_t fifo_idx; + Key key[2]; + IV iv[4]; + bool is_encrypt; + QCryptoCipherMode block_mode; +}; + +static void aes_update_irq(AESState *s) +{ + qemu_set_irq(s->irq, !!(s->irq_status & s->irq_enable)); +} + +static uint64_t aes1_read(void *opaque, hwaddr offset, unsigned size) +{ + AESState *s = opaque; + uint64_t res = 0; + + switch (offset) { + case REG_STATUS: + res = s->status; + break; + case REG_IRQ_STATUS: + res = s->irq_status; + break; + case REG_IRQ_ENABLE: + res = s->irq_enable; + break; + case REG_WATERMARK: + res = s->watermark; + break; + case REG_Q_STATUS: + res = s->q_status; + break; + case REG_FLAG_INFO: + res = s->flag_info; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: Unknown AES MMIO offset %" PRIx64 "\n", + __func__, offset); + break; + } + + trace_aes_read(offset, res); + + return res; +} + +static void fifo_append(AESState *s, uint64_t val) +{ + if (s->fifo_idx == MAX_FIFO_SIZE) { + /* Exceeded the FIFO. Bail out */ + return; + } + + s->fifo[s->fifo_idx++] = val; +} + +static bool has_payload(AESState *s, uint32_t elems) +{ + return s->fifo_idx >= elems + 1; +} + +static bool cmd_key(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t key_select = (cmd & CMD_KEY_SELECT_MASK) >> CMD_KEY_SELECT_SHIFT; + uint32_t ctxt = (cmd & CMD_KEY_CONTEXT_MASK) >> CMD_KEY_CONTEXT_SHIFT; + uint32_t key_len; + + switch ((cmd & CMD_KEY_BLOCK_MODE_MASK) >> CMD_KEY_BLOCK_MODE_SHIFT) { + case 0: + s->block_mode = QCRYPTO_CIPHER_MODE_ECB; + break; + case 1: + s->block_mode = QCRYPTO_CIPHER_MODE_CBC; + break; + default: + return false; + } + + s->is_encrypt = cmd & CMD_KEY_ENCRYPT_MASK; + key_len = key_lens[(cmd & CMD_KEY_KEY_LEN_MASK) >> CMD_KEY_KEY_LEN_SHIFT]; + + if (key_select) { + trace_aes_cmd_key_select_builtin(ctxt, key_select, + s->is_encrypt ? "en" : "de", + QCryptoCipherMode_str(s->block_mode)); + s->key[ctxt] = builtin_keys[key_select]; + } else { + trace_aes_cmd_key_select_new(ctxt, key_len, + s->is_encrypt ? "en" : "de", + QCryptoCipherMode_str(s->block_mode)); + if (key_len > sizeof(s->key[ctxt].key)) { + return false; + } + if (!has_payload(s, key_len / sizeof(uint32_t))) { + /* wait for payload */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); + return false; + } + memcpy(&s->key[ctxt].key, &s->fifo[1], key_len); + s->key[ctxt].key_len = key_len; + } + + return true; +} + +static bool cmd_iv(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT; + + if (!has_payload(s, 4)) { + /* wait for payload */ + return false; + } + memcpy(&s->iv[ctxt].iv, &s->fifo[1], sizeof(s->iv[ctxt].iv)); + trace_aes_cmd_iv(ctxt, s->fifo[1], s->fifo[2], s->fifo[3], s->fifo[4]); + + return true; +} + +static void dump_data(const char *desc, const void *p, size_t len) +{ + static const size_t MAX_LEN = 0x1000; + char hex[MAX_LEN * 2 + 1] = ""; + + if (len > MAX_LEN) { + return; + } + + qemu_hexdump_to_buffer(hex, sizeof(hex), p, len); + trace_aes_dump_data(desc, hex); +} + +static bool cmd_data(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t ctxt_iv = 0; + uint32_t ctxt_key = (cmd & CMD_DATA_KEY_CTX_MASK) >> CMD_DATA_KEY_CTX_SHIFT; + uint32_t len = cmd & CMD_DATA_LEN_MASK; + uint64_t src_addr = s->fifo[2]; + uint64_t dst_addr = s->fifo[3]; + QCryptoCipherAlgo alg; + g_autoptr(QCryptoCipher) cipher = NULL; + g_autoptr(GByteArray) src = NULL; + g_autoptr(GByteArray) dst = NULL; + MemTxResult r; + + src_addr |= ((uint64_t)s->fifo[1] << 16) & 0xffff00000000ULL; + dst_addr |= ((uint64_t)s->fifo[1] << 32) & 0xffff00000000ULL; + + trace_aes_cmd_data(ctxt_key, ctxt_iv, src_addr, dst_addr, len); + + if (!has_payload(s, 3)) { + /* wait for payload */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); + return false; + } + + if (ctxt_key >= ARRAY_SIZE(s->key) || + ctxt_iv >= ARRAY_SIZE(s->iv)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key or iv\n", __func__); + return false; + } + + src = g_byte_array_sized_new(len); + g_byte_array_set_size(src, len); + dst = g_byte_array_sized_new(len); + g_byte_array_set_size(dst, len); + + r = dma_memory_read(s->as, src_addr, src->data, len, MEMTXATTRS_UNSPECIFIED); + if (r != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA read of %"PRIu32" bytes " + "from 0x%"PRIx64" failed. (r=%d)\n", + __func__, len, src_addr, r); + return false; + } + + dump_data("cmd_data(): src_data=", src->data, len); + + switch (s->key[ctxt_key].key_len) { + case 128 / 8: + alg = QCRYPTO_CIPHER_ALGO_AES_128; + break; + case 192 / 8: + alg = QCRYPTO_CIPHER_ALGO_AES_192; + break; + case 256 / 8: + alg = QCRYPTO_CIPHER_ALGO_AES_256; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key length\n", __func__); + return false; + } + cipher = qcrypto_cipher_new(alg, s->block_mode, + s->key[ctxt_key].key, + s->key[ctxt_key].key_len, NULL); + if (!cipher) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to create cipher object\n", + __func__); + return false; + } + if (s->block_mode != QCRYPTO_CIPHER_MODE_ECB) { + if (qcrypto_cipher_setiv(cipher, (void *)s->iv[ctxt_iv].iv, + sizeof(s->iv[ctxt_iv].iv), NULL) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to set IV\n", __func__); + return false; + } + } + if (s->is_encrypt) { + if (qcrypto_cipher_encrypt(cipher, src->data, dst->data, len, NULL) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Encryption failed\n", __func__); + return false; + } + } else { + if (qcrypto_cipher_decrypt(cipher, src->data, dst->data, len, NULL) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Decryption failed\n", __func__); + return false; + } + } + + dump_data("cmd_data(): dst_data=", dst->data, len); + r = dma_memory_write(s->as, dst_addr, dst->data, len, MEMTXATTRS_UNSPECIFIED); + if (r != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA write of %"PRIu32" bytes " + "to 0x%"PRIx64" failed. (r=%d)\n", + __func__, len, src_addr, r); + return false; + } + + return true; +} + +static bool cmd_store_iv(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT; + uint64_t addr = s->fifo[1]; + MemTxResult dma_result; + + if (!has_payload(s, 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); + return false; + } + + if (ctxt >= ARRAY_SIZE(s->iv)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid context. ctxt = %u, allowed: 0..%zu\n", + __func__, ctxt, ARRAY_SIZE(s->iv) - 1); + return false; + } + + addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL; + dma_result = dma_memory_write(&address_space_memory, addr, + &s->iv[ctxt].iv, sizeof(s->iv[ctxt].iv), + MEMTXATTRS_UNSPECIFIED); + + trace_aes_cmd_store_iv(ctxt, addr, s->iv[ctxt].iv[0], s->iv[ctxt].iv[1], + s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]); + + return dma_result == MEMTX_OK; +} + +static bool cmd_flag(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t raise_irq = cmd & CMD_FLAG_RAISE_IRQ_MASK; + + /* We always process data when it's coming in, so fire an IRQ immediately */ + if (raise_irq) { + s->irq_status |= REG_IRQ_STATUS_FLAG; + } + + s->flag_info = cmd & CMD_FLAG_INFO_MASK; + + trace_aes_cmd_flag(!!raise_irq, s->flag_info); + + return true; +} + +static void fifo_process(AESState *s) +{ + uint32_t cmd = s->fifo[0] >> CMD_SHIFT; + bool success = false; + + if (!s->fifo_idx) { + return; + } + + switch (cmd) { + case CMD_KEY: + success = cmd_key(s); + break; + case CMD_IV: + success = cmd_iv(s); + break; + case CMD_DATA: + success = cmd_data(s); + break; + case CMD_STORE_IV: + success = cmd_store_iv(s); + break; + case CMD_FLAG: + success = cmd_flag(s); + break; + default: + s->irq_status |= REG_IRQ_STATUS_INVALID_CMD; + break; + } + + if (success) { + s->fifo_idx = 0; + } + + trace_aes_fifo_process(cmd, success); +} + +static void aes1_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + AESState *s = opaque; + + trace_aes_write(offset, val); + + switch (offset) { + case REG_IRQ_STATUS: + s->irq_status &= ~val; + break; + case REG_IRQ_ENABLE: + s->irq_enable = val; + break; + case REG_FIFO: + fifo_append(s, val); + fifo_process(s); + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: Unknown AES MMIO offset %"PRIx64", data %"PRIx64"\n", + __func__, offset, val); + return; + } + + aes_update_irq(s); +} + +static const MemoryRegionOps aes1_ops = { + .read = aes1_read, + .write = aes1_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t aes2_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t res = 0; + + switch (offset) { + case 0: + res = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: Unknown AES MMIO 2 offset %"PRIx64"\n", + __func__, offset); + break; + } + + trace_aes_2_read(offset, res); + + return res; +} + +static void aes2_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + trace_aes_2_write(offset, val); + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, + "%s: Unknown AES MMIO 2 offset %"PRIx64", data %"PRIx64"\n", + __func__, offset, val); + return; + } +} + +static const MemoryRegionOps aes2_ops = { + .read = aes2_read, + .write = aes2_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void aes_reset(Object *obj, ResetType type) +{ + AESState *s = APPLE_AES(obj); + + s->status = 0x3f80; + s->q_status = 2; + s->irq_status = 0; + s->irq_enable = 0; + s->watermark = 0; +} + +static void aes_init(Object *obj) +{ + AESState *s = APPLE_AES(obj); + + memory_region_init_io(&s->iomem1, obj, &aes1_ops, s, TYPE_APPLE_AES, 0x4000); + memory_region_init_io(&s->iomem2, obj, &aes2_ops, s, TYPE_APPLE_AES, 0x4000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem1); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem2); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); + s->as = &address_space_memory; +} + +static void aes_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = aes_reset; +} + +static const TypeInfo aes_info = { + .name = TYPE_APPLE_AES, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AESState), + .class_init = aes_class_init, + .instance_init = aes_init, +}; + +static void aes_register_types(void) +{ + type_register_static(&aes_info); +} + +type_init(aes_register_types) diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build index 315c06b689..a701d06a39 100644 --- a/hw/vmapple/meson.build +++ b/hw/vmapple/meson.build @@ -1 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-or-later + +system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events index 2112579412..188547a6ad 100644 --- a/hw/vmapple/trace-events +++ b/hw/vmapple/trace-events @@ -1,2 +1,16 @@ # See docs/devel/tracing.rst for syntax documentation. # SPDX-License-Identifier: GPL-2.0-or-later + +# aes.c +aes_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +aes_cmd_key_select_builtin(uint32_t ctx, uint32_t key_id, const char *direction, const char *cipher) "[%d] Selecting builtin key %d to %scrypt with %s" +aes_cmd_key_select_new(uint32_t ctx, uint32_t key_len, const char *direction, const char *cipher) "[%d] Selecting new key size=%d to %scrypt with %s" +aes_cmd_iv(uint32_t ctx, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] 0x%08x 0x%08x 0x%08x 0x%08x" +aes_cmd_data(uint32_t key, uint32_t iv, uint64_t src, uint64_t dst, uint32_t len) "[key=%d iv=%d] src=0x%"PRIx64" dst=0x%"PRIx64" len=0x%x" +aes_cmd_store_iv(uint32_t ctx, uint64_t addr, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] addr=0x%"PRIx64"x -> 0x%08x 0x%08x 0x%08x 0x%08x" +aes_cmd_flag(uint32_t raise, uint32_t flag_info) "raise=%d flag_info=0x%x" +aes_fifo_process(uint32_t cmd, bool success) "cmd=%d success=%d" +aes_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +aes_dump_data(const char *desc, const char *hex) "%s%s" diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h new file mode 100644 index 0000000000..6762b6c869 --- /dev/null +++ b/include/hw/vmapple/vmapple.h @@ -0,0 +1,17 @@ +/* + * Devices specific to the VMApple machine type + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VMAPPLE_VMAPPLE_H +#define HW_VMAPPLE_VMAPPLE_H + +#define TYPE_APPLE_AES "apple-aes" + +#endif /* HW_VMAPPLE_VMAPPLE_H */ diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 34a9b9b220..36c68ce86c 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -302,4 +302,19 @@ GString *qemu_hexdump_line(GString *str, const void *buf, size_t len, void qemu_hexdump(FILE *fp, const char *prefix, const void *bufptr, size_t size); +/** + * qemu_hexdump_to_buffer: + * @buffer: output string buffer + * @buffer_size: amount of available space in buffer. Must be at least + * data_size*2+1. + * @data: input bytes + * @data_size: number of bytes in data + * + * Converts the @data_size bytes in @data into hex digit pairs, writing them to + * @buffer. Finally, a nul terminating character is written; @buffer therefore + * needs space for (data_size*2+1) chars. + */ +void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size, + const uint8_t *restrict data, size_t data_size); + #endif diff --git a/util/hexdump.c b/util/hexdump.c index ae0d4992dc..f29ffceb74 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/host-utils.h" static inline char hexdump_nibble(unsigned x) { @@ -97,3 +98,20 @@ void qemu_hexdump(FILE *fp, const char *prefix, } } + +void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size, + const uint8_t *restrict data, size_t data_size) +{ + size_t i; + uint64_t required_buffer_size; + bool overflow = umul64_overflow(data_size, 2, &required_buffer_size); + overflow |= uadd64_overflow(required_buffer_size, 1, &required_buffer_size); + assert(!overflow && buffer_size >= required_buffer_size); + + for (i = 0; i < data_size; i++) { + uint8_t val = data[i]; + *(buffer++) = hexdump_nibble(val >> 4); + *(buffer++) = hexdump_nibble(val & 0xf); + } + *buffer = '\0'; +} From 0179bb3c48cfb915da64305b3cfbc110766d4078 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:56:26 +0000 Subject: [PATCH 2099/2892] hw/vmapple/bdif: Introduce vmapple backdoor interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VMApple machine exposes AUX and ROOT block devices (as well as USB OTG emulation) via virtio-pci as well as a special, simple backdoor platform device. This patch implements this backdoor platform device to the best of my understanding. I left out any USB OTG parts; they're only needed for guest recovery and I don't understand the protocol yet. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-11-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/vmapple/Kconfig | 3 + hw/vmapple/bdif.c | 274 +++++++++++++++++++++++++++++++++++ hw/vmapple/meson.build | 1 + hw/vmapple/trace-events | 5 + include/hw/vmapple/vmapple.h | 2 + 5 files changed, 285 insertions(+) create mode 100644 hw/vmapple/bdif.c diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig index b1944d7312..ff5f97c292 100644 --- a/hw/vmapple/Kconfig +++ b/hw/vmapple/Kconfig @@ -2,3 +2,6 @@ config VMAPPLE_AES bool + +config VMAPPLE_BDIF + bool diff --git a/hw/vmapple/bdif.c b/hw/vmapple/bdif.c new file mode 100644 index 0000000000..5827dd2aab --- /dev/null +++ b/hw/vmapple/bdif.c @@ -0,0 +1,274 @@ +/* + * VMApple Backdoor Interface + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "trace.h" +#include "hw/vmapple/vmapple.h" +#include "hw/sysbus.h" +#include "hw/block/block.h" +#include "qapi/error.h" +#include "system/block-backend.h" +#include "system/dma.h" + +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) + +struct VMAppleBdifState { + SysBusDevice parent_obj; + + BlockBackend *aux; + BlockBackend *root; + MemoryRegion mmio; +}; + +#define VMAPPLE_BDIF_SIZE 0x00200000 + +#define REG_DEVID_MASK 0xffff0000 +#define DEVID_ROOT 0x00000000 +#define DEVID_AUX 0x00010000 +#define DEVID_USB 0x00100000 + +#define REG_STATUS 0x0 +#define REG_STATUS_ACTIVE BIT(0) +#define REG_CFG 0x4 +#define REG_CFG_ACTIVE BIT(1) +#define REG_UNK1 0x8 +#define REG_BUSY 0x10 +#define REG_BUSY_READY BIT(0) +#define REG_UNK2 0x400 +#define REG_CMD 0x408 +#define REG_NEXT_DEVICE 0x420 +#define REG_UNK3 0x434 + +typedef struct VblkSector { + uint32_t pad; + uint32_t pad2; + uint32_t sector; + uint32_t pad3; +} VblkSector; + +typedef struct VblkReqCmd { + uint64_t addr; + uint32_t len; + uint32_t flags; +} VblkReqCmd; + +typedef struct VblkReq { + VblkReqCmd sector; + VblkReqCmd data; + VblkReqCmd retval; +} VblkReq; + +#define VBLK_DATA_FLAGS_READ 0x00030001 +#define VBLK_DATA_FLAGS_WRITE 0x00010001 + +#define VBLK_RET_SUCCESS 0 +#define VBLK_RET_FAILED 1 + +static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t ret = -1; + uint64_t devid = offset & REG_DEVID_MASK; + + switch (offset & ~REG_DEVID_MASK) { + case REG_STATUS: + ret = REG_STATUS_ACTIVE; + break; + case REG_CFG: + ret = REG_CFG_ACTIVE; + break; + case REG_UNK1: + ret = 0x420; + break; + case REG_BUSY: + ret = REG_BUSY_READY; + break; + case REG_UNK2: + ret = 0x1; + break; + case REG_UNK3: + ret = 0x0; + break; + case REG_NEXT_DEVICE: + switch (devid) { + case DEVID_ROOT: + ret = 0x8000000; + break; + case DEVID_AUX: + ret = 0x10000; + break; + } + break; + } + + trace_bdif_read(offset, size, ret); + return ret; +} + +static void le2cpu_sector(VblkSector *sector) +{ + sector->sector = le32_to_cpu(sector->sector); +} + +static void le2cpu_reqcmd(VblkReqCmd *cmd) +{ + cmd->addr = le64_to_cpu(cmd->addr); + cmd->len = le32_to_cpu(cmd->len); + cmd->flags = le32_to_cpu(cmd->flags); +} + +static void le2cpu_req(VblkReq *req) +{ + le2cpu_reqcmd(&req->sector); + le2cpu_reqcmd(&req->data); + le2cpu_reqcmd(&req->retval); +} + +static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t gp_addr, + uint64_t static_off) +{ + VblkReq req; + VblkSector sector; + uint64_t off = 0; + g_autofree char *buf = NULL; + uint8_t ret = VBLK_RET_FAILED; + int r; + MemTxResult dma_result; + + dma_result = dma_memory_read(&address_space_memory, gp_addr, + &req, sizeof(req), MEMTXATTRS_UNSPECIFIED); + if (dma_result != MEMTX_OK) { + goto out; + } + + le2cpu_req(&req); + + if (req.sector.len != sizeof(sector)) { + goto out; + } + + /* Read the vblk command */ + dma_result = dma_memory_read(&address_space_memory, req.sector.addr, + §or, sizeof(sector), + MEMTXATTRS_UNSPECIFIED); + if (dma_result != MEMTX_OK) { + goto out; + } + le2cpu_sector(§or); + + off = sector.sector * 512ULL + static_off; + + /* Sanity check that we're not allocating bogus sizes */ + if (req.data.len > 128 * MiB) { + goto out; + } + + buf = g_malloc0(req.data.len); + switch (req.data.flags) { + case VBLK_DATA_FLAGS_READ: + r = blk_pread(blk, off, req.data.len, buf, 0); + trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root", + req.data.addr, off, req.data.len, r); + if (r < 0) { + goto out; + } + dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf, + req.data.len, MEMTXATTRS_UNSPECIFIED); + if (dma_result == MEMTX_OK) { + ret = VBLK_RET_SUCCESS; + } + break; + case VBLK_DATA_FLAGS_WRITE: + /* Not needed, iBoot only reads */ + break; + default: + break; + } + +out: + dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1, + MEMTXATTRS_UNSPECIFIED); +} + +static void bdif_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + VMAppleBdifState *s = opaque; + uint64_t devid = (offset & REG_DEVID_MASK); + + trace_bdif_write(offset, size, value); + + switch (offset & ~REG_DEVID_MASK) { + case REG_CMD: + switch (devid) { + case DEVID_ROOT: + vblk_cmd(devid, s->root, value, 0x0); + break; + case DEVID_AUX: + vblk_cmd(devid, s->aux, value, 0x0); + break; + } + break; + } +} + +static const MemoryRegionOps bdif_ops = { + .read = bdif_read, + .write = bdif_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static void bdif_init(Object *obj) +{ + VMAppleBdifState *s = VMAPPLE_BDIF(obj); + + memory_region_init_io(&s->mmio, obj, &bdif_ops, obj, + "VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const Property bdif_properties[] = { + DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux), + DEFINE_PROP_DRIVE("root", VMAppleBdifState, root), +}; + +static void bdif_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "VMApple Backdoor Interface"; + device_class_set_props(dc, bdif_properties); +} + +static const TypeInfo bdif_info = { + .name = TYPE_VMAPPLE_BDIF, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VMAppleBdifState), + .instance_init = bdif_init, + .class_init = bdif_class_init, +}; + +static void bdif_register_types(void) +{ + type_register_static(&bdif_info); +} + +type_init(bdif_register_types) diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build index a701d06a39..e2aca6b7c2 100644 --- a/hw/vmapple/meson.build +++ b/hw/vmapple/meson.build @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-or-later system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) +system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events index 188547a6ad..93380ede14 100644 --- a/hw/vmapple/trace-events +++ b/hw/vmapple/trace-events @@ -14,3 +14,8 @@ aes_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 aes_dump_data(const char *desc, const char *hex) "%s%s" + +# bdif.c +bdif_read(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 +bdif_write(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 +bdif_vblk_read(const char *dev, uint64_t addr, uint64_t offset, uint32_t len, int r) "dev=%s addr=0x%"PRIx64" off=0x%"PRIx64" size=0x%x r=%d" diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h index 6762b6c869..9090e9c5ac 100644 --- a/include/hw/vmapple/vmapple.h +++ b/include/hw/vmapple/vmapple.h @@ -14,4 +14,6 @@ #define TYPE_APPLE_AES "apple-aes" +#define TYPE_VMAPPLE_BDIF "vmapple-bdif" + #endif /* HW_VMAPPLE_VMAPPLE_H */ From 33b54462067331c75fe180f809d50efe9659895a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:57:32 +0000 Subject: [PATCH 2100/2892] hw/vmapple/cfg: Introduce vmapple cfg region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of device tree or other more standardized means, VMApple passes platform configuration to the first stage boot loader in a binary encoded format that resides at a dedicated RAM region in physical address space. This patch models this configuration space as a qdev device which we can then map at the fixed location in the address space. That way, we can influence and annotate all configuration fields easily. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-12-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/vmapple/Kconfig | 3 + hw/vmapple/cfg.c | 195 +++++++++++++++++++++++++++++++++++ hw/vmapple/meson.build | 1 + include/hw/vmapple/vmapple.h | 2 + 4 files changed, 201 insertions(+) create mode 100644 hw/vmapple/cfg.c diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig index ff5f97c292..f5898661a9 100644 --- a/hw/vmapple/Kconfig +++ b/hw/vmapple/Kconfig @@ -5,3 +5,6 @@ config VMAPPLE_AES config VMAPPLE_BDIF bool + +config VMAPPLE_CFG + bool diff --git a/hw/vmapple/cfg.c b/hw/vmapple/cfg.c new file mode 100644 index 0000000000..63414d801f --- /dev/null +++ b/hw/vmapple/cfg.c @@ -0,0 +1,195 @@ +/* + * VMApple Configuration Region + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/vmapple/vmapple.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "net/net.h" + +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleCfgState, VMAPPLE_CFG) + +#define VMAPPLE_CFG_SIZE 0x00010000 + +typedef struct VMAppleCfg { + uint32_t version; /* 0x000 */ + uint32_t nr_cpus; /* 0x004 */ + uint32_t unk1; /* 0x008 */ + uint32_t unk2; /* 0x00c */ + uint32_t unk3; /* 0x010 */ + uint32_t unk4; /* 0x014 */ + uint64_t ecid; /* 0x018 */ + uint64_t ram_size; /* 0x020 */ + uint32_t run_installer1; /* 0x028 */ + uint32_t unk5; /* 0x02c */ + uint32_t unk6; /* 0x030 */ + uint32_t run_installer2; /* 0x034 */ + uint32_t rnd; /* 0x038 */ + uint32_t unk7; /* 0x03c */ + MACAddr mac_en0; /* 0x040 */ + uint8_t pad1[2]; + MACAddr mac_en1; /* 0x048 */ + uint8_t pad2[2]; + MACAddr mac_wifi0; /* 0x050 */ + uint8_t pad3[2]; + MACAddr mac_bt0; /* 0x058 */ + uint8_t pad4[2]; + uint8_t reserved[0xa0]; /* 0x060 */ + uint32_t cpu_ids[0x80]; /* 0x100 */ + uint8_t scratch[0x200]; /* 0x180 */ + char serial[32]; /* 0x380 */ + char unk8[32]; /* 0x3a0 */ + char model[32]; /* 0x3c0 */ + uint8_t unk9[32]; /* 0x3e0 */ + uint32_t unk10; /* 0x400 */ + char soc_name[32]; /* 0x404 */ +} VMAppleCfg; + +struct VMAppleCfgState { + SysBusDevice parent_obj; + VMAppleCfg cfg; + + MemoryRegion mem; + char *serial; + char *model; + char *soc_name; +}; + +static void vmapple_cfg_reset(Object *obj, ResetType type) +{ + VMAppleCfgState *s = VMAPPLE_CFG(obj); + VMAppleCfg *cfg; + + cfg = memory_region_get_ram_ptr(&s->mem); + memset(cfg, 0, VMAPPLE_CFG_SIZE); + *cfg = s->cfg; +} + +static bool set_fixlen_property_or_error(char *restrict dst, + const char *restrict src, + size_t dst_size, Error **errp, + const char *property_name) +{ + ERRP_GUARD(); + size_t len; + + len = g_strlcpy(dst, src, dst_size); + if (len < dst_size) { /* len does not count nul terminator */ + return true; + } + + error_setg(errp, "Provided value too long for property '%s'", property_name); + error_append_hint(errp, "length (%zu) exceeds maximum of %zu\n", + len, dst_size - 1); + return false; +} + +#define set_fixlen_property_or_return(dst_array, src, errp, property_name) \ + do { \ + if (!set_fixlen_property_or_error((dst_array), (src), \ + ARRAY_SIZE(dst_array), \ + (errp), (property_name))) { \ + return; \ + } \ + } while (0) + +static void vmapple_cfg_realize(DeviceState *dev, Error **errp) +{ + VMAppleCfgState *s = VMAPPLE_CFG(dev); + uint32_t i; + + if (!s->serial) { + s->serial = g_strdup("1234"); + } + if (!s->model) { + s->model = g_strdup("VM0001"); + } + if (!s->soc_name) { + s->soc_name = g_strdup("Apple M1 (Virtual)"); + } + + set_fixlen_property_or_return(s->cfg.serial, s->serial, errp, "serial"); + set_fixlen_property_or_return(s->cfg.model, s->model, errp, "model"); + set_fixlen_property_or_return(s->cfg.soc_name, s->soc_name, errp, "soc_name"); + set_fixlen_property_or_return(s->cfg.unk8, "D/A", errp, "unk8"); + s->cfg.version = 2; + s->cfg.unk1 = 1; + s->cfg.unk2 = 1; + s->cfg.unk3 = 0x20; + s->cfg.unk4 = 0; + s->cfg.unk5 = 1; + s->cfg.unk6 = 1; + s->cfg.unk7 = 0; + s->cfg.unk10 = 1; + + if (s->cfg.nr_cpus > ARRAY_SIZE(s->cfg.cpu_ids)) { + error_setg(errp, + "Failed to create %u CPUs, vmapple machine supports %zu max", + s->cfg.nr_cpus, ARRAY_SIZE(s->cfg.cpu_ids)); + return; + } + for (i = 0; i < s->cfg.nr_cpus; i++) { + s->cfg.cpu_ids[i] = i; + } +} + +static void vmapple_cfg_init(Object *obj) +{ + VMAppleCfgState *s = VMAPPLE_CFG(obj); + + memory_region_init_ram(&s->mem, obj, "VMApple Config", VMAPPLE_CFG_SIZE, + &error_fatal); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mem); +} + +static const Property vmapple_cfg_properties[] = { + DEFINE_PROP_UINT32("nr-cpus", VMAppleCfgState, cfg.nr_cpus, 1), + DEFINE_PROP_UINT64("ecid", VMAppleCfgState, cfg.ecid, 0), + DEFINE_PROP_UINT64("ram-size", VMAppleCfgState, cfg.ram_size, 0), + DEFINE_PROP_UINT32("run_installer1", VMAppleCfgState, cfg.run_installer1, 0), + DEFINE_PROP_UINT32("run_installer2", VMAppleCfgState, cfg.run_installer2, 0), + DEFINE_PROP_UINT32("rnd", VMAppleCfgState, cfg.rnd, 0), + DEFINE_PROP_MACADDR("mac-en0", VMAppleCfgState, cfg.mac_en0), + DEFINE_PROP_MACADDR("mac-en1", VMAppleCfgState, cfg.mac_en1), + DEFINE_PROP_MACADDR("mac-wifi0", VMAppleCfgState, cfg.mac_wifi0), + DEFINE_PROP_MACADDR("mac-bt0", VMAppleCfgState, cfg.mac_bt0), + DEFINE_PROP_STRING("serial", VMAppleCfgState, serial), + DEFINE_PROP_STRING("model", VMAppleCfgState, model), + DEFINE_PROP_STRING("soc_name", VMAppleCfgState, soc_name), +}; + +static void vmapple_cfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = vmapple_cfg_realize; + dc->desc = "VMApple Configuration Region"; + device_class_set_props(dc, vmapple_cfg_properties); + rc->phases.hold = vmapple_cfg_reset; +} + +static const TypeInfo vmapple_cfg_info = { + .name = TYPE_VMAPPLE_CFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VMAppleCfgState), + .instance_init = vmapple_cfg_init, + .class_init = vmapple_cfg_class_init, +}; + +static void vmapple_cfg_register_types(void) +{ + type_register_static(&vmapple_cfg_info); +} + +type_init(vmapple_cfg_register_types) diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build index e2aca6b7c2..9e881c7b55 100644 --- a/hw/vmapple/meson.build +++ b/hw/vmapple/meson.build @@ -2,3 +2,4 @@ system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) +system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h index 9090e9c5ac..3bba59f5ec 100644 --- a/include/hw/vmapple/vmapple.h +++ b/include/hw/vmapple/vmapple.h @@ -16,4 +16,6 @@ #define TYPE_VMAPPLE_BDIF "vmapple-bdif" +#define TYPE_VMAPPLE_CFG "vmapple-cfg" + #endif /* HW_VMAPPLE_VMAPPLE_H */ From ee241d79bbf45fe7354dde51b0ca2574824205d4 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:56:22 +0000 Subject: [PATCH 2101/2892] hw/vmapple/virtio-blk: Add support for apple virtio-blk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apple has its own virtio-blk PCI device ID where it deviates from the official virtio-pci spec slightly: It puts a new "apple type" field at a static offset in config space and introduces a new barrier command. This patch first creates a mechanism for virtio-blk downstream classes to handle unknown commands. It then creates such a downstream class and a new vmapple-virtio-blk-pci class which support the additional apple type config identifier as well as the barrier command. The 'aux' or 'root' device type are selected using the 'variant' property. Signed-off-by: Alexander Graf Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Message-ID: <20241223221645.29911-13-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/block/virtio-blk.c | 17 ++- hw/core/qdev-properties-system.c | 9 ++ hw/vmapple/Kconfig | 3 + hw/vmapple/meson.build | 1 + hw/vmapple/virtio-blk.c | 204 ++++++++++++++++++++++++++++ include/hw/pci/pci_ids.h | 1 + include/hw/qdev-properties-system.h | 6 + include/hw/virtio/virtio-blk.h | 11 +- include/hw/vmapple/vmapple.h | 2 + qapi/virtio.json | 14 ++ 10 files changed, 264 insertions(+), 4 deletions(-) create mode 100644 hw/vmapple/virtio-blk.c diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index a1829e3abd..5135b4d8f1 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -50,7 +50,7 @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, req->mr_next = NULL; } -static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) +void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) { VirtIOBlock *s = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); @@ -961,8 +961,18 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) break; } default: - virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - g_free(req); + { + /* + * Give subclasses a chance to handle unknown requests. This way the + * class lookup is not in the hot path. + */ + VirtIOBlkClass *vbk = VIRTIO_BLK_GET_CLASS(s); + if (!vbk->handle_unknown_request || + !vbk->handle_unknown_request(req, mrb, type)) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); + } + } } return 0; } @@ -2029,6 +2039,7 @@ static const TypeInfo virtio_blk_info = { .instance_size = sizeof(VirtIOBlock), .instance_init = virtio_blk_instance_init, .class_init = virtio_blk_class_init, + .class_size = sizeof(VirtIOBlkClass), }; static void virtio_register_types(void) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 89f954f569..a91551a5ee 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1294,3 +1294,12 @@ const PropertyInfo qdev_prop_endian_mode = { .set = qdev_propinfo_set_enum, .set_default_value = qdev_propinfo_set_default_value_enum, }; + +const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = { + .name = "VMAppleVirtioBlkVariant", + .description = "unspecified/root/aux", + .enum_table = &VMAppleVirtioBlkVariant_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig index f5898661a9..5586fd460b 100644 --- a/hw/vmapple/Kconfig +++ b/hw/vmapple/Kconfig @@ -8,3 +8,6 @@ config VMAPPLE_BDIF config VMAPPLE_CFG bool + +config VMAPPLE_VIRTIO_BLK + bool diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build index 9e881c7b55..3553ec6151 100644 --- a/hw/vmapple/meson.build +++ b/hw/vmapple/meson.build @@ -3,3 +3,4 @@ system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) +system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c')) diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c new file mode 100644 index 0000000000..aa3f18c47d --- /dev/null +++ b/hw/vmapple/virtio-blk.c @@ -0,0 +1,204 @@ +/* + * VMApple specific VirtIO Block implementation + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * VMApple uses almost standard VirtIO Block, but with a few key differences: + * + * - Different PCI device/vendor ID + * - An additional "type" identifier to differentiate AUX and Root volumes + * - An additional BARRIER command + */ + +#include "qemu/osdep.h" +#include "hw/vmapple/vmapple.h" +#include "hw/virtio/virtio-blk.h" +#include "hw/virtio/virtio-pci.h" +#include "qemu/bswap.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" + +#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk" +OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK) + +typedef struct VMAppleVirtIOBlkClass { + VirtIOBlkClass parent; + + void (*get_config)(VirtIODevice *vdev, uint8_t *config); +} VMAppleVirtIOBlkClass; + +typedef struct VMAppleVirtIOBlk { + VirtIOBlock parent_obj; + + uint32_t apple_type; +} VMAppleVirtIOBlk; + +/* + * vmapple-virtio-blk-pci: This extends VirtioPCIProxy. + */ +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI) + +#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000 + +static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req, + MultiReqBuffer *mrb, + uint32_t type) +{ + switch (type) { + case VIRTIO_BLK_T_APPLE_BARRIER: + qemu_log_mask(LOG_UNIMP, "%s: Barrier requests are currently no-ops\n", + __func__); + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + g_free(req); + return true; + default: + return false; + } +} + +/* + * VMApple virtio-blk uses the same config format as normal virtio, with one + * exception: It adds an "apple type" specififer at the same location that + * the spec reserves for max_secure_erase_sectors. Let's hook into the + * get_config code path here, run it as usual and then patch in the apple type. + */ +static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VMAppleVirtIOBlk *dev = VMAPPLE_VIRTIO_BLK(vdev); + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_GET_CLASS(dev); + struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config; + + vvbk->get_config(vdev, config); + + g_assert(dev->parent_obj.config_size >= endof(struct virtio_blk_config, zoned)); + + /* Apple abuses the field for max_secure_erase_sectors as type id */ + stl_he_p(&blkcfg->max_secure_erase_sectors, dev->apple_type); +} + +static void vmapple_virtio_blk_class_init(ObjectClass *klass, void *data) +{ + VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_CLASS(klass); + + vbk->handle_unknown_request = vmapple_virtio_blk_handle_unknown_request; + vvbk->get_config = vdc->get_config; + vdc->get_config = vmapple_virtio_blk_get_config; +} + +static const TypeInfo vmapple_virtio_blk_info = { + .name = TYPE_VMAPPLE_VIRTIO_BLK, + .parent = TYPE_VIRTIO_BLK, + .instance_size = sizeof(VMAppleVirtIOBlk), + .class_size = sizeof(VMAppleVirtIOBlkClass), + .class_init = vmapple_virtio_blk_class_init, +}; + +/* PCI Devices */ + +struct VMAppleVirtIOBlkPCI { + VirtIOPCIProxy parent_obj; + + VMAppleVirtIOBlk vdev; + VMAppleVirtioBlkVariant variant; +}; + +static const Property vmapple_virtio_blk_pci_properties[] = { + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT("variant", VMAppleVirtIOBlkPCI, variant, + VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED), +}; + +static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + ERRP_GUARD(); + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf; + + if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) { + error_setg(errp, "vmapple virtio block device variant unspecified"); + error_append_hint(errp, + "Variant property must be set to 'aux' or 'root'.\n" + "Use a regular virtio-blk-pci device instead when " + "neither is applicaple.\n"); + return; + } + + if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) { + conf->num_queues = virtio_pci_optimal_num_queues(0); + } + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = conf->num_queues + 1; + } + + /* + * We don't support zones, but we need the additional config space size. + * Let's just expose the feature so the rest of the virtio-blk logic + * allocates enough space for us. The guest will ignore zones anyway. + */ + virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED); + /* Propagate the apple type down to the virtio-blk device */ + dev->vdev.apple_type = dev->variant; + /* and spawn the virtio-blk device */ + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); + + /* + * The virtio-pci machinery adjusts its vendor/device ID based on whether + * we support modern or legacy virtio. Let's patch it back to the Apple + * identifiers here. + */ + pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE); + pci_config_set_device_id(vpci_dev->pci_dev.config, + PCI_DEVICE_ID_APPLE_VIRTIO_BLK); +} + +static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + device_class_set_props(dc, vmapple_virtio_blk_pci_properties); + k->realize = vmapple_virtio_blk_pci_realize; + pcidev_k->vendor_id = PCI_VENDOR_ID_APPLE; + pcidev_k->device_id = PCI_DEVICE_ID_APPLE_VIRTIO_BLK; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vmapple_virtio_blk_pci_instance_init(Object *obj) +{ + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VMAPPLE_VIRTIO_BLK); +} + +static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = { + .generic_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI, + .instance_size = sizeof(VMAppleVirtIOBlkPCI), + .instance_init = vmapple_virtio_blk_pci_instance_init, + .class_init = vmapple_virtio_blk_pci_class_init, +}; + +static void vmapple_virtio_blk_register_types(void) +{ + type_register_static(&vmapple_virtio_blk_info); + virtio_pci_types_register(&vmapple_virtio_blk_pci_info); +} + +type_init(vmapple_virtio_blk_register_types) diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index f1a53fea8d..33e2898be9 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -191,6 +191,7 @@ #define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 #define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 +#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00 #define PCI_VENDOR_ID_SUN 0x108e #define PCI_DEVICE_ID_SUN_EBUS 0x1000 diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index ead4dfc2f0..b921392c52 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -31,6 +31,7 @@ extern const PropertyInfo qdev_prop_pcie_link_width; extern const PropertyInfo qdev_prop_cpus390entitlement; extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; extern const PropertyInfo qdev_prop_endian_mode; +extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) @@ -104,4 +105,9 @@ extern const PropertyInfo qdev_prop_endian_mode; #define DEFINE_PROP_ENDIAN_NODEFAULT(_name, _state, _field) \ DEFINE_PROP_ENDIAN(_name, _state, _field, ENDIAN_MODE_UNSPECIFIED) +#define DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT(_name, _state, _fld, _default) \ + DEFINE_PROP_UNSIGNED(_name, _state, _fld, _default, \ + qdev_prop_vmapple_virtio_blk_variant, \ + VMAppleVirtioBlkVariant) + #endif diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index 8a16218c40..3d8dee7ec1 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -24,7 +24,7 @@ #include "qapi/qapi-types-virtio.h" #define TYPE_VIRTIO_BLK "virtio-blk-device" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK) +OBJECT_DECLARE_TYPE(VirtIOBlock, VirtIOBlkClass, VIRTIO_BLK) /* This is the last element of the write scatter-gather list */ struct virtio_blk_inhdr @@ -100,6 +100,15 @@ typedef struct MultiReqBuffer { bool is_write; } MultiReqBuffer; +typedef struct VirtIOBlkClass { + /*< private >*/ + VirtioDeviceClass parent; + /*< public >*/ + bool (*handle_unknown_request)(VirtIOBlockReq *req, MultiReqBuffer *mrb, + uint32_t type); +} VirtIOBlkClass; + void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq); +void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status); #endif diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h index 3bba59f5ec..9c1ad1bd8c 100644 --- a/include/hw/vmapple/vmapple.h +++ b/include/hw/vmapple/vmapple.h @@ -18,4 +18,6 @@ #define TYPE_VMAPPLE_CFG "vmapple-cfg" +#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci" + #endif /* HW_VMAPPLE_VMAPPLE_H */ diff --git a/qapi/virtio.json b/qapi/virtio.json index 2529c2d8b2..d351d2166e 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -992,3 +992,17 @@ ## { 'enum': 'GranuleMode', 'data': [ '4k', '8k', '16k', '64k', 'host' ] } + +## +# @VMAppleVirtioBlkVariant: +# +# @unspecified: The default, not a valid setting. +# +# @root: Block device holding the root volume +# +# @aux: Block device holding auxiliary data required for boot +# +# Since: 9.2 +## +{ 'enum': 'VMAppleVirtioBlkVariant', + 'data': [ 'unspecified', 'root', 'aux' ] } From 9422a5acf2125e9a67a8a14538306f123bcc6098 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Sun, 8 Dec 2024 20:16:44 +0100 Subject: [PATCH 2102/2892] hw/usb/hcd-xhci-pci: Adds property for disabling mapping in IRQ mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change addresses an edge case that trips up macOS guest drivers for PCI based XHCI controllers. The guest driver would attempt to schedule events to XHCI event rings 1 and 2 even when using PCI pin-based interrupts. Interrupts would therefore be dropped, and events only handled on timeout. So, in addition to disabling interrupter mapping if numintrs is 1, a callback is added to xhci to check whether interrupter mapping should be enabled. The PCI XHCI device type now provides an implementation of this callback if the new "conditional-intr-mapping" property is enabled. (default: disabled) When enabled, interrupter mapping is only enabled when MSI-X or MSI is active. This means that when using pin-based interrupts, events are only submitted to interrupter 0 regardless of selected target. This allows the macOS guest drivers to work with the device in those configurations. Signed-off-by: Phil Dennis-Jordan Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2705 Message-ID: <20241227121336.25838-6-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci-pci.c | 24 ++++++++++++++++++++++++ hw/usb/hcd-xhci-pci.h | 1 + hw/usb/hcd-xhci.c | 3 ++- hw/usb/hcd-xhci.h | 5 +++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index 49642aab58..d908eb787d 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -82,6 +82,21 @@ static bool xhci_pci_intr_raise(XHCIState *xhci, int n, bool level) return false; } +static bool xhci_pci_intr_mapping_conditional(XHCIState *xhci) +{ + XHCIPciState *s = container_of(xhci, XHCIPciState, xhci); + PCIDevice *pci_dev = PCI_DEVICE(s); + + /* + * Implementation of the "conditional-intr-mapping" property, which only + * enables interrupter mapping if MSI or MSI-X is available and active. + * Forces all events onto interrupter/event ring 0 in pin-based IRQ mode. + * Provides compatibility with macOS guests on machine types where MSI(-X) + * is not available. + */ + return msix_enabled(pci_dev) || msi_enabled(pci_dev); +} + static void xhci_pci_reset(DeviceState *dev) { XHCIPciState *s = XHCI_PCI(dev); @@ -119,6 +134,9 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp) object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL); s->xhci.intr_update = xhci_pci_intr_update; s->xhci.intr_raise = xhci_pci_intr_raise; + if (s->conditional_intr_mapping) { + s->xhci.intr_mapping_supported = xhci_pci_intr_mapping_conditional; + } if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) { return; } @@ -201,6 +219,8 @@ static void xhci_instance_init(Object *obj) static const Property xhci_pci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), + DEFINE_PROP_BOOL("conditional-intr-mapping", XHCIPciState, + conditional_intr_mapping, false), }; static void xhci_class_init(ObjectClass *klass, void *data) @@ -215,6 +235,10 @@ static void xhci_class_init(ObjectClass *klass, void *data) k->exit = usb_xhci_pci_exit; k->class_id = PCI_CLASS_SERIAL_USB; device_class_set_props(dc, xhci_pci_properties); + object_class_property_set_description(klass, "conditional-intr-mapping", + "When true, disables interrupter mapping for pin-based IRQ mode. " + "Intended to be used with guest drivers with questionable behaviour, " + "such as macOS's."); } static const TypeInfo xhci_pci_info = { diff --git a/hw/usb/hcd-xhci-pci.h b/hw/usb/hcd-xhci-pci.h index 08f70ce97c..5b61ae8455 100644 --- a/hw/usb/hcd-xhci-pci.h +++ b/hw/usb/hcd-xhci-pci.h @@ -40,6 +40,7 @@ typedef struct XHCIPciState { XHCIState xhci; OnOffAuto msi; OnOffAuto msix; + bool conditional_intr_mapping; } XHCIPciState; #endif diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 00d5bc3779..64c3a23b9b 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -644,7 +644,8 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) dma_addr_t erdp; unsigned int dp_idx; - if (xhci->numintrs == 1) { + if (xhci->numintrs == 1 || + (xhci->intr_mapping_supported && !xhci->intr_mapping_supported(xhci))) { v = 0; } diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index 9609b83514..9c3974f148 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -193,6 +193,11 @@ typedef struct XHCIState { uint32_t max_pstreams_mask; void (*intr_update)(XHCIState *s, int n, bool enable); bool (*intr_raise)(XHCIState *s, int n, bool level); + /* + * Callback for special-casing interrupter mapping support. NULL for most + * implementations, for defaulting to enabled mapping unless numintrs == 1. + */ + bool (*intr_mapping_supported)(XHCIState *s); DeviceState *hostOpaque; /* Operational Registers */ From 59f4d65584bd3372070e2484876436c8d02505e4 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Jun 2023 22:57:34 +0000 Subject: [PATCH 2103/2892] hw/vmapple/vmapple: Add vmapple machine type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apple defines a new "vmapple" machine type as part of its proprietary macOS Virtualization.Framework vmm. This machine type is similar to the virt one, but with subtle differences in base devices, a few special vmapple device additions and a vastly different boot chain. This patch reimplements this machine type in QEMU. To use it, you have to have a readily installed version of macOS for VMApple, run on macOS with -accel hvf, pass the Virtualization.Framework boot rom (AVPBooter) in via -bios, pass the aux and root volume as pflash and pass aux and root volume as virtio drives. In addition, you also need to find the machine UUID and pass that as -M vmapple,uuid= parameter: $ qemu-system-aarch64 -accel hvf -M vmapple,uuid=0x1234 -m 4G \ -bios /System/Library/Frameworks/Virtualization.framework/Versions/A/Resources/AVPBooter.vmapple2.bin -drive file=aux,if=pflash,format=raw \ -drive file=root,if=pflash,format=raw \ -drive file=aux,if=none,id=aux,format=raw \ -device vmapple-virtio-blk-pci,variant=aux,drive=aux \ -drive file=root,if=none,id=root,format=raw \ -device vmapple-virtio-blk-pci,variant=root,drive=root With all these in place, you should be able to see macOS booting successfully. Known issues: - Currently only macOS 12 guests are supported. The boot process for 13+ will need further investigation and adjustment. Signed-off-by: Alexander Graf Co-authored-by: Phil Dennis-Jordan Signed-off-by: Phil Dennis-Jordan Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Message-ID: <20241223221645.29911-15-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + contrib/vmapple/uuid.sh | 12 + docs/system/arm/vmapple.rst | 65 ++++ docs/system/target-arm.rst | 1 + hw/vmapple/Kconfig | 21 ++ hw/vmapple/meson.build | 1 + hw/vmapple/vmapple.c | 618 ++++++++++++++++++++++++++++++++++++ 7 files changed, 719 insertions(+) create mode 100755 contrib/vmapple/uuid.sh create mode 100644 docs/system/arm/vmapple.rst create mode 100644 hw/vmapple/vmapple.c diff --git a/MAINTAINERS b/MAINTAINERS index 0e16036091..d1e69539de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2826,6 +2826,7 @@ M: Phil Dennis-Jordan S: Maintained F: hw/vmapple/* F: include/hw/vmapple/* +F: docs/system/arm/vmapple.rst Subsystems ---------- diff --git a/contrib/vmapple/uuid.sh b/contrib/vmapple/uuid.sh new file mode 100755 index 0000000000..f5637221d2 --- /dev/null +++ b/contrib/vmapple/uuid.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Used for converting a guest provisioned using Virtualization.framework +# for use with the QEMU 'vmapple' aarch64 machine type. +# +# Extracts the Machine UUID from Virtualization.framework VM JSON file. +# (as produced by 'macosvm', passed as command line argument) +# +# SPDX-License-Identifier: GPL-2.0-or-later + +plutil -extract machineId raw "$1" | base64 -d | plutil -extract ECID raw - + diff --git a/docs/system/arm/vmapple.rst b/docs/system/arm/vmapple.rst new file mode 100644 index 0000000000..35c329ea5a --- /dev/null +++ b/docs/system/arm/vmapple.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +VMApple machine emulation +======================================================================================== + +VMApple is the device model that the macOS built-in hypervisor called "Virtualization.framework" +exposes to Apple Silicon macOS guests. The "vmapple" machine model in QEMU implements the same +device model, but does not use any code from Virtualization.Framework. + +Prerequisites +------------- + +To run the vmapple machine model, you need to + + * Run on Apple Silicon + * Run on macOS 12.0 or above + * Have an already installed copy of a Virtualization.Framework macOS 12 virtual + machine. Note that newer versions than 12.x are currently NOT supported on + the guest side. I will assume that you installed it using the + `macosvm `__ CLI. + +First, we need to extract the UUID from the virtual machine that you installed. You can do this +by running the shell script in contrib/vmapple/uuid.sh on the macosvm.json file. + +.. code-block:: bash + :caption: uuid.sh script to extract the UUID from a macosvm.json file + + $ contrib/vmapple/uuid.sh "path/to/macosvm.json" + +Now we also need to trim the aux partition. It contains metadata that we can just discard: + +.. code-block:: bash + :caption: Command to trim the aux file + + $ dd if="aux.img" of="aux.img.trimmed" bs=$(( 0x4000 )) skip=1 + +How to run +---------- + +Then, we can launch QEMU with the Virtualization.Framework pre-boot environment and the readily +installed target disk images. I recommend to port forward the VM's ssh and vnc ports to the host +to get better interactive access into the target system: + +.. code-block:: bash + :caption: Example execution command line + + $ UUID="$(contrib/vmapple/uuid.sh 'macosvm.json')" + $ AVPBOOTER="/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin" + $ AUX="aux.img.trimmed" + $ DISK="disk.img" + $ qemu-system-aarch64 \ + -serial mon:stdio \ + -m 4G \ + -accel hvf \ + -M vmapple,uuid="$UUID" \ + -bios "$AVPBOOTER" \ + -drive file="$AUX",if=pflash,format=raw \ + -drive file="$DISK",if=pflash,format=raw \ + -drive file="$AUX",if=none,id=aux,format=raw \ + -drive file="$DISK",if=none,id=root,format=raw \ + -device vmapple-virtio-blk-pci,variant=aux,drive=aux \ + -device vmapple-virtio-blk-pci,variant=root,drive=root \ + -netdev user,id=net0,ipv6=off,hostfwd=tcp::2222-:22,hostfwd=tcp::5901-:5900 \ + -device virtio-net-pci,netdev=net0 + diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index a43ec8f10e..b96a05a920 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -103,6 +103,7 @@ Board-specific documentation arm/stellaris arm/stm32 arm/virt + arm/vmapple arm/xenpvh arm/xlnx-versal-virt arm/xlnx-zynq diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig index 5586fd460b..2382b29767 100644 --- a/hw/vmapple/Kconfig +++ b/hw/vmapple/Kconfig @@ -11,3 +11,24 @@ config VMAPPLE_CFG config VMAPPLE_VIRTIO_BLK bool + +config VMAPPLE + bool + depends on ARM + depends on HVF + default y if ARM + imply PCI_DEVICES + select ARM_GICV3 + select PLATFORM_BUS + select PCI_EXPRESS + select PCI_EXPRESS_GENERIC_BRIDGE + select PL011 # UART + select PL031 # RTC + select PL061 # GPIO + select GPIO_PWR + select PVPANIC_MMIO + select VMAPPLE_AES + select VMAPPLE_BDIF + select VMAPPLE_CFG + select MAC_PVG_MMIO + select VMAPPLE_VIRTIO_BLK diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build index 3553ec6151..23bc4c999e 100644 --- a/hw/vmapple/meson.build +++ b/hw/vmapple/meson.build @@ -4,3 +4,4 @@ system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c')) +specific_ss.add(when: 'CONFIG_VMAPPLE', if_true: files('vmapple.c')) diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c new file mode 100644 index 0000000000..fa117bf151 --- /dev/null +++ b/hw/vmapple/vmapple.c @@ -0,0 +1,618 @@ +/* + * VMApple machine emulation + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * VMApple is the device model that the macOS built-in hypervisor called + * "Virtualization.framework" exposes to Apple Silicon macOS guests. The + * machine model in this file implements the same device model in QEMU, but + * does not use any code from Virtualization.Framework. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/datadir.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/help-texts.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/units.h" +#include "monitor/qdev.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/usb.h" +#include "hw/arm/boot.h" +#include "hw/arm/primecell.h" +#include "hw/char/pl011.h" +#include "hw/intc/arm_gic.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/pvpanic.h" +#include "hw/pci-host/gpex.h" +#include "hw/usb/hcd-xhci-pci.h" +#include "hw/virtio/virtio-pci.h" +#include "hw/vmapple/vmapple.h" +#include "net/net.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qapi/qapi-visit-common.h" +#include "qobject/qlist.h" +#include "standard-headers/linux/input.h" +#include "system/hvf.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" + +struct VMAppleMachineState { + MachineState parent; + + Notifier machine_done; + struct arm_boot_info bootinfo; + const MemMapEntry *memmap; + const int *irqmap; + DeviceState *gic; + DeviceState *cfg; + DeviceState *pvpanic; + Notifier powerdown_notifier; + PCIBus *bus; + MemoryRegion fw_mr; + MemoryRegion ecam_alias; + uint64_t uuid; +}; + +#define TYPE_VMAPPLE_MACHINE MACHINE_TYPE_NAME("vmapple") +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleMachineState, VMAPPLE_MACHINE) + +/* Number of external interrupt lines to configure the GIC with */ +#define NUM_IRQS 256 + +enum { + VMAPPLE_FIRMWARE, + VMAPPLE_CONFIG, + VMAPPLE_MEM, + VMAPPLE_GIC_DIST, + VMAPPLE_GIC_REDIST, + VMAPPLE_UART, + VMAPPLE_RTC, + VMAPPLE_PCIE, + VMAPPLE_PCIE_MMIO, + VMAPPLE_PCIE_ECAM, + VMAPPLE_GPIO, + VMAPPLE_PVPANIC, + VMAPPLE_APV_GFX, + VMAPPLE_APV_IOSFC, + VMAPPLE_AES_1, + VMAPPLE_AES_2, + VMAPPLE_BDOOR, + VMAPPLE_MEMMAP_LAST, +}; + +static const MemMapEntry memmap[] = { + [VMAPPLE_FIRMWARE] = { 0x00100000, 0x00100000 }, + [VMAPPLE_CONFIG] = { 0x00400000, 0x00010000 }, + + [VMAPPLE_GIC_DIST] = { 0x10000000, 0x00010000 }, + [VMAPPLE_GIC_REDIST] = { 0x10010000, 0x00400000 }, + + [VMAPPLE_UART] = { 0x20010000, 0x00010000 }, + [VMAPPLE_RTC] = { 0x20050000, 0x00001000 }, + [VMAPPLE_GPIO] = { 0x20060000, 0x00001000 }, + [VMAPPLE_PVPANIC] = { 0x20070000, 0x00000002 }, + [VMAPPLE_BDOOR] = { 0x30000000, 0x00200000 }, + [VMAPPLE_APV_GFX] = { 0x30200000, 0x00010000 }, + [VMAPPLE_APV_IOSFC] = { 0x30210000, 0x00010000 }, + [VMAPPLE_AES_1] = { 0x30220000, 0x00004000 }, + [VMAPPLE_AES_2] = { 0x30230000, 0x00004000 }, + [VMAPPLE_PCIE_ECAM] = { 0x40000000, 0x10000000 }, + [VMAPPLE_PCIE_MMIO] = { 0x50000000, 0x1fff0000 }, + + /* Actual RAM size depends on configuration */ + [VMAPPLE_MEM] = { 0x70000000ULL, GiB}, +}; + +static const int irqmap[] = { + [VMAPPLE_UART] = 1, + [VMAPPLE_RTC] = 2, + [VMAPPLE_GPIO] = 0x5, + [VMAPPLE_APV_IOSFC] = 0x10, + [VMAPPLE_APV_GFX] = 0x11, + [VMAPPLE_AES_1] = 0x12, + [VMAPPLE_PCIE] = 0x20, +}; + +#define GPEX_NUM_IRQS 16 + +static void create_bdif(VMAppleMachineState *vms, MemoryRegion *mem) +{ + DeviceState *bdif; + SysBusDevice *bdif_sb; + DriveInfo *di_aux = drive_get(IF_PFLASH, 0, 0); + DriveInfo *di_root = drive_get(IF_PFLASH, 0, 1); + + if (!di_aux) { + error_report("No AUX device. Please specify one as pflash drive."); + exit(1); + } + + if (!di_root) { + /* Fall back to the first IF_VIRTIO device as root device */ + di_root = drive_get(IF_VIRTIO, 0, 0); + } + + if (!di_root) { + error_report("No root device. Please specify one as virtio drive."); + exit(1); + } + + /* PV backdoor device */ + bdif = qdev_new(TYPE_VMAPPLE_BDIF); + bdif_sb = SYS_BUS_DEVICE(bdif); + sysbus_mmio_map(bdif_sb, 0, vms->memmap[VMAPPLE_BDOOR].base); + + qdev_prop_set_drive(DEVICE(bdif), "aux", blk_by_legacy_dinfo(di_aux)); + qdev_prop_set_drive(DEVICE(bdif), "root", blk_by_legacy_dinfo(di_root)); + + sysbus_realize_and_unref(bdif_sb, &error_fatal); +} + +static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem) +{ + SysBusDevice *pvpanic; + + vms->pvpanic = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); + pvpanic = SYS_BUS_DEVICE(vms->pvpanic); + sysbus_mmio_map(pvpanic, 0, vms->memmap[VMAPPLE_PVPANIC].base); + + sysbus_realize_and_unref(pvpanic, &error_fatal); +} + +static bool create_cfg(VMAppleMachineState *vms, MemoryRegion *mem, + Error **errp) +{ + ERRP_GUARD(); + SysBusDevice *cfg; + MachineState *machine = MACHINE(vms); + uint32_t rnd = 1; + + vms->cfg = qdev_new(TYPE_VMAPPLE_CFG); + cfg = SYS_BUS_DEVICE(vms->cfg); + sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_CONFIG].base); + + qemu_guest_getrandom_nofail(&rnd, sizeof(rnd)); + + qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus); + qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid); + qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size); + qdev_prop_set_uint32(vms->cfg, "rnd", rnd); + + if (!sysbus_realize_and_unref(cfg, errp)) { + error_prepend(errp, "Error creating vmapple cfg device: "); + return false; + } + + return true; +} + +static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem) +{ + int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX]; + int irq_iosfc = vms->irqmap[VMAPPLE_APV_IOSFC]; + SysBusDevice *gfx; + + gfx = SYS_BUS_DEVICE(qdev_new("apple-gfx-mmio")); + sysbus_mmio_map(gfx, 0, vms->memmap[VMAPPLE_APV_GFX].base); + sysbus_mmio_map(gfx, 1, vms->memmap[VMAPPLE_APV_IOSFC].base); + sysbus_connect_irq(gfx, 0, qdev_get_gpio_in(vms->gic, irq_gfx)); + sysbus_connect_irq(gfx, 1, qdev_get_gpio_in(vms->gic, irq_iosfc)); + sysbus_realize_and_unref(gfx, &error_fatal); +} + +static void create_aes(VMAppleMachineState *vms, MemoryRegion *mem) +{ + int irq = vms->irqmap[VMAPPLE_AES_1]; + SysBusDevice *aes; + + aes = SYS_BUS_DEVICE(qdev_new(TYPE_APPLE_AES)); + sysbus_mmio_map(aes, 0, vms->memmap[VMAPPLE_AES_1].base); + sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base); + sysbus_connect_irq(aes, 0, qdev_get_gpio_in(vms->gic, irq)); + sysbus_realize_and_unref(aes, &error_fatal); +} + +static int arm_gic_ppi_index(int cpu_nr, int ppi_index) +{ + return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index; +} + +static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem) +{ + MachineState *ms = MACHINE(vms); + /* We create a standalone GIC */ + SysBusDevice *gicbusdev; + QList *redist_region_count; + int i; + unsigned int smp_cpus = ms->smp.cpus; + + vms->gic = qdev_new(gicv3_class_name()); + qdev_prop_set_uint32(vms->gic, "revision", 3); + qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); + /* + * Note that the num-irq property counts both internal and external + * interrupts; there are always 32 of the former (mandated by GIC spec). + */ + qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32); + + uint32_t redist0_capacity = + vms->memmap[VMAPPLE_GIC_REDIST].size / GICV3_REDIST_SIZE; + uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, redist0_count); + qdev_prop_set_array(vms->gic, "redist-region-count", redist_region_count); + + gicbusdev = SYS_BUS_DEVICE(vms->gic); + sysbus_realize_and_unref(gicbusdev, &error_fatal); + sysbus_mmio_map(gicbusdev, 0, vms->memmap[VMAPPLE_GIC_DIST].base); + sysbus_mmio_map(gicbusdev, 1, vms->memmap[VMAPPLE_GIC_REDIST].base); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < smp_cpus; i++) { + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); + + /* Map the virt timer to PPI 27 */ + qdev_connect_gpio_out(cpudev, GTIMER_VIRT, + qdev_get_gpio_in(vms->gic, + arm_gic_ppi_index(i, 27))); + + /* Map the GIC IRQ and FIQ lines to CPU */ + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } +} + +static void create_uart(const VMAppleMachineState *vms, int uart, + MemoryRegion *mem, Chardev *chr) +{ + hwaddr base = vms->memmap[uart].base; + int irq = vms->irqmap[uart]; + DeviceState *dev = qdev_new(TYPE_PL011); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + + qdev_prop_set_chr(dev, "chardev", chr); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(mem, base, + sysbus_mmio_get_region(s, 0)); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); +} + +static void create_rtc(const VMAppleMachineState *vms) +{ + hwaddr base = vms->memmap[VMAPPLE_RTC].base; + int irq = vms->irqmap[VMAPPLE_RTC]; + + sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq)); +} + +static DeviceState *gpio_key_dev; +static void vmapple_powerdown_req(Notifier *n, void *opaque) +{ + /* use gpio Pin 3 for power button event */ + qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); +} + +static void create_gpio_devices(const VMAppleMachineState *vms, int gpio, + MemoryRegion *mem) +{ + DeviceState *pl061_dev; + hwaddr base = vms->memmap[gpio].base; + int irq = vms->irqmap[gpio]; + SysBusDevice *s; + + pl061_dev = qdev_new("pl061"); + /* Pull lines down to 0 if not driven by the PL061 */ + qdev_prop_set_uint32(pl061_dev, "pullups", 0); + qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff); + s = SYS_BUS_DEVICE(pl061_dev); + sysbus_realize_and_unref(s, &error_fatal); + memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); + gpio_key_dev = sysbus_create_simple("gpio-key", -1, + qdev_get_gpio_in(pl061_dev, 3)); +} + +static void vmapple_firmware_init(VMAppleMachineState *vms, + MemoryRegion *sysmem) +{ + hwaddr size = vms->memmap[VMAPPLE_FIRMWARE].size; + hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base; + const char *bios_name; + int image_size; + char *fname; + + bios_name = MACHINE(vms)->firmware; + if (!bios_name) { + error_report("No firmware specified"); + exit(1); + } + + fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fname) { + error_report("Could not find ROM image '%s'", bios_name); + exit(1); + } + + memory_region_init_ram(&vms->fw_mr, NULL, "firmware", size, &error_fatal); + image_size = load_image_mr(fname, &vms->fw_mr); + + g_free(fname); + if (image_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + + memory_region_add_subregion(get_system_memory(), base, &vms->fw_mr); +} + +static void create_pcie(VMAppleMachineState *vms) +{ + hwaddr base_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].base; + hwaddr size_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].size; + hwaddr base_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].base; + hwaddr size_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].size; + int irq = vms->irqmap[VMAPPLE_PCIE]; + MemoryRegion *mmio_alias; + MemoryRegion *mmio_reg; + MemoryRegion *ecam_reg; + DeviceState *dev; + int i; + PCIHostState *pci; + DeviceState *usb_controller; + USBBus *usb_bus; + + dev = qdev_new(TYPE_GPEX_HOST); + qdev_prop_set_uint32(dev, "num-irqs", GPEX_NUM_IRQS); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + /* Map only the first size_ecam bytes of ECAM space */ + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_init_alias(&vms->ecam_alias, OBJECT(dev), "pcie-ecam", + ecam_reg, 0, size_ecam); + memory_region_add_subregion(get_system_memory(), base_ecam, + &vms->ecam_alias); + + /* + * Map the MMIO window from [0x50000000-0x7fff0000] in PCI space into + * system address space at [0x50000000-0x7fff0000]. + */ + mmio_alias = g_new0(MemoryRegion, 1); + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio", + mmio_reg, base_mmio, size_mmio); + memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(vms->gic, irq + i)); + gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); + } + + pci = PCI_HOST_BRIDGE(dev); + vms->bus = pci->bus; + g_assert(vms->bus); + + while ((dev = qemu_create_nic_device("virtio-net-pci", true, NULL))) { + qdev_realize_and_unref(dev, BUS(vms->bus), &error_fatal); + } + + if (defaults_enabled()) { + usb_controller = qdev_new(TYPE_QEMU_XHCI); + qdev_realize_and_unref(usb_controller, BUS(pci->bus), &error_fatal); + + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_fatal)); + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-tablet"); + } +} + +static void vmapple_reset(void *opaque) +{ + VMAppleMachineState *vms = opaque; + hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base; + + cpu_set_pc(first_cpu, base); +} + +static void mach_vmapple_init(MachineState *machine) +{ + VMAppleMachineState *vms = VMAPPLE_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *possible_cpus; + MemoryRegion *sysmem = get_system_memory(); + int n; + unsigned int smp_cpus = machine->smp.cpus; + unsigned int max_cpus = machine->smp.max_cpus; + + vms->memmap = memmap; + machine->usb = true; + + possible_cpus = mc->possible_cpu_arch_ids(machine); + assert(possible_cpus->len == max_cpus); + for (n = 0; n < possible_cpus->len; n++) { + Object *cpu; + CPUState *cs; + + if (n >= smp_cpus) { + break; + } + + cpu = object_new(possible_cpus->cpus[n].type); + object_property_set_int(cpu, "mp-affinity", + possible_cpus->cpus[n].arch_id, &error_fatal); + + cs = CPU(cpu); + cs->cpu_index = n; + + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpu), + &error_fatal); + + if (object_property_find(cpu, "has_el3")) { + object_property_set_bool(cpu, "has_el3", false, &error_fatal); + } + if (object_property_find(cpu, "has_el2")) { + object_property_set_bool(cpu, "has_el2", false, &error_fatal); + } + object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC, + &error_fatal); + + /* Secondary CPUs start in PSCI powered-down state */ + if (n > 0) { + object_property_set_bool(cpu, "start-powered-off", true, + &error_fatal); + } + + object_property_set_link(cpu, "memory", OBJECT(sysmem), &error_abort); + qdev_realize(DEVICE(cpu), NULL, &error_fatal); + object_unref(cpu); + } + + memory_region_add_subregion(sysmem, vms->memmap[VMAPPLE_MEM].base, + machine->ram); + + create_gic(vms, sysmem); + create_bdif(vms, sysmem); + create_pvpanic(vms, sysmem); + create_aes(vms, sysmem); + create_gfx(vms, sysmem); + create_uart(vms, VMAPPLE_UART, sysmem, serial_hd(0)); + create_rtc(vms); + create_pcie(vms); + + create_gpio_devices(vms, VMAPPLE_GPIO, sysmem); + + vmapple_firmware_init(vms, sysmem); + create_cfg(vms, sysmem, &error_fatal); + + /* connect powerdown request */ + vms->powerdown_notifier.notify = vmapple_powerdown_req; + qemu_register_powerdown_notifier(&vms->powerdown_notifier); + + vms->bootinfo.ram_size = machine->ram_size; + vms->bootinfo.board_id = -1; + vms->bootinfo.loader_start = vms->memmap[VMAPPLE_MEM].base; + vms->bootinfo.skip_dtb_autoload = true; + vms->bootinfo.firmware_loaded = true; + arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo); + + qemu_register_reset(vmapple_reset, vms); +} + +static CpuInstanceProperties +vmapple_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + + assert(cpu_index < possible_cpus->len); + return possible_cpus->cpus[cpu_index].props; +} + + +static int64_t vmapple_get_default_cpu_node_id(const MachineState *ms, int idx) +{ + return idx % ms->numa_state->num_nodes; +} + +static const CPUArchIdList *vmapple_possible_cpu_arch_ids(MachineState *ms) +{ + int n; + unsigned int max_cpus = ms->smp.max_cpus; + + if (ms->possible_cpus) { + assert(ms->possible_cpus->len == max_cpus); + return ms->possible_cpus; + } + + ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + + sizeof(CPUArchId) * max_cpus); + ms->possible_cpus->len = max_cpus; + for (n = 0; n < ms->possible_cpus->len; n++) { + ms->possible_cpus->cpus[n].type = ms->cpu_type; + ms->possible_cpus->cpus[n].arch_id = + arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS); + ms->possible_cpus->cpus[n].props.has_thread_id = true; + ms->possible_cpus->cpus[n].props.thread_id = n; + } + return ms->possible_cpus; +} + +static GlobalProperty vmapple_compat_defaults[] = { + { TYPE_VIRTIO_PCI, "disable-legacy", "on" }, + /* + * macOS XHCI driver attempts to schedule events onto even rings 1 & 2 + * even when (as here) there is no MSI(-X) support. Disabling interrupter + * mapping in the XHCI controller works around the problem. + */ + { TYPE_XHCI_PCI, "conditional-intr-mapping", "on" }, +}; + +static void vmapple_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = mach_vmapple_init; + mc->max_cpus = 32; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->pci_allow_0_address = true; + mc->minimum_page_bits = 12; + mc->possible_cpu_arch_ids = vmapple_possible_cpu_arch_ids; + mc->cpu_index_to_instance_props = vmapple_cpu_index_to_props; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("host"); + mc->get_default_cpu_node_id = vmapple_get_default_cpu_node_id; + mc->default_ram_id = "mach-vmapple.ram"; + mc->desc = "Apple aarch64 Virtual Machine"; + + compat_props_add(mc->compat_props, vmapple_compat_defaults, + G_N_ELEMENTS(vmapple_compat_defaults)); +} + +static void vmapple_instance_init(Object *obj) +{ + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); + + vms->irqmap = irqmap; + + object_property_add_uint64_ptr(obj, "uuid", &vms->uuid, + OBJ_PROP_FLAG_READWRITE); + object_property_set_description(obj, "uuid", "Machine UUID (SDOM)"); +} + +static const TypeInfo vmapple_machine_info = { + .name = TYPE_VMAPPLE_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(VMAppleMachineState), + .class_init = vmapple_machine_class_init, + .instance_init = vmapple_instance_init, +}; + +static void machvmapple_machine_init(void) +{ + type_register_static(&vmapple_machine_info); +} +type_init(machvmapple_machine_init); + From 00ad70b7fb527a80b31c8b6b1a0e7a66b0d08498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 17:28:39 +0100 Subject: [PATCH 2104/2892] hw/ppc/spapr: Restrict part of PAGE_INIT hypercall to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the tb_flush() call to TCG. Assert we are using KVM or TCG. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-Id: <20250127102620.39159-3-philmd@linaro.org> --- hw/ppc/spapr_hcall.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f8ab767063..f987ff323f 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -299,8 +299,10 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) { if (kvm_enabled()) { kvmppc_icbi_range(cpu, pdst, len); - } else { + } else if (tcg_enabled()) { tb_flush(CPU(cpu)); + } else { + g_assert_not_reached(); } } From 611f3bdb20f7828b0813aa90d47d1275ef18329b Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 14 Feb 2025 14:16:32 +1000 Subject: [PATCH 2105/2892] hw/acpi/ghes: Make ghes_record_cper_errors() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit acpi_ghes_memory_errors() is the only caller, no need to expose the function. Besides, the last 'return' in this function isn't necessary and remove it. No functional changes intended. Signed-off-by: Gavin Shan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250214041635.608012-2-gshan@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/acpi/ghes.c | 6 ++---- include/hw/acpi/ghes.h | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index b709c177cd..b85bb48195 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -390,8 +390,8 @@ static void get_hw_error_offsets(uint64_t ghes_addr, *read_ack_register_addr = ghes_addr + sizeof(uint64_t); } -void ghes_record_cper_errors(const void *cper, size_t len, - uint16_t source_id, Error **errp) +static void ghes_record_cper_errors(const void *cper, size_t len, + uint16_t source_id, Error **errp) { uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register; AcpiGedState *acpi_ged_state; @@ -440,8 +440,6 @@ void ghes_record_cper_errors(const void *cper, size_t len, /* Write the generic error data entry into guest memory */ cpu_physical_memory_write(cper_addr, cper, len); - - return; } int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h index 39619a2457..578a582203 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -75,8 +75,6 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors, void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr); -void ghes_record_cper_errors(const void *cper, size_t len, - uint16_t source_id, Error **errp); /** * acpi_ghes_present: Report whether ACPI GHES table is present From 8c4648f5a20d88ca16ad6e12134c6c9bf01fac9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 13:03:18 +0100 Subject: [PATCH 2106/2892] hw/arm: Do not expose the virt machine on Xen-only binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the Virt machine is useless under Xen, do not even try to build it there. A Xen-only binary now only offers the XenPVH machine: $ qemu-system-aarch64 -M help Supported machines are: none empty machine xenpvh Xen PVH ARM machine Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20250218162618.46167-3-philmd@linaro.org> --- hw/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index faa00d1db3..15200a2d7e 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -2,6 +2,7 @@ config ARM_VIRT bool default y depends on ARM + depends on TCG || KVM || HVF imply PCI_DEVICES imply TEST_DEVICES imply VFIO_AMD_XGBE From 1ff51df2ef9cce9927ee54321886b435f1a293e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 13:03:13 +0100 Subject: [PATCH 2107/2892] hw/xen: Link XenPVH with GPEX PCIe bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XenPVH requires the PCIe/GPEX device. Add it to Kconfig to avoid when configuring using --without-default-devices: /usr/bin/ld: libqemu-aarch64-softmmu.a.p/hw_xen_xen-pvh-common.c.o: in function `xenpvh_gpex_init': hw/xen/xen-pvh-common.c:174: undefined reference to `gpex_set_irq_num' /usr/bin/ld: libqemu-aarch64-softmmu.a.p/hw_xen_xen-hvm-common.c.o: in function `pci_dev_bus_num': include/hw/pci/pci.h:337: undefined reference to `pci_bus_num' /usr/bin/ld: include/hw/pci/pci.h:337: undefined reference to `pci_bus_num' /usr/bin/ld: include/hw/pci/pci.h:337: undefined reference to `pci_bus_num' /usr/bin/ld: include/hw/pci/pci.h:337: undefined reference to `pci_bus_num' /usr/bin/ld: include/hw/pci/pci.h:337: undefined reference to `pci_bus_num' /usr/bin/ld: libqemu-aarch64-softmmu.a.p/hw_xen_xen-hvm-common.c.o: in function `cpu_ioreq_config': hw/xen/xen-hvm-common.c:412: undefined reference to `pci_host_config_read_common' /usr/bin/ld: hw/xen/xen-hvm-common.c:428: undefined reference to `pci_host_config_read_common' /usr/bin/ld: hw/xen/xen-hvm-common.c:438: undefined reference to `pci_host_config_write_common' Fixes: f22e598a72c ("hw/xen: pvh-common: Add support for creating PCIe/GPEX") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20250218162618.46167-2-philmd@linaro.org> --- accel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/Kconfig b/accel/Kconfig index 794e0d18d2..4263cab722 100644 --- a/accel/Kconfig +++ b/accel/Kconfig @@ -16,4 +16,5 @@ config KVM config XEN bool select FSDEV_9P if VIRTFS + select PCI_EXPRESS_GENERIC_BRIDGE select XEN_BUS From 4702dcd4ee2dbf2c383e53e6fcbc5075a85f0613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 13:45:11 +0100 Subject: [PATCH 2108/2892] hw/xen/xen-pvh: Reduce included headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have "hw/xen/xen-pvh-common.h" include the bare minimal set of headers. Adapt sources to avoid errors when refactoring unrelated headers such: hw/i386/xen/xen-pvh.c: In function ‘xen_pvh_machine_class_init’: hw/i386/xen/xen-pvh.c:84:28: error: ‘TARGET_DEFAULT_CPU_TYPE’ undeclared (first use in this function) 84 | mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; | ^~~~~~~~~~~~~~~~~~~~~~~ hw/xen/xen-pvh-common.c: In function ‘xen_pvh_init’: hw/xen/xen-pvh-common.c:217:43: error: ‘MiB’ undeclared (first use in this function) 217 | if (s->cfg.pci_ecam.size != 256 * MiB) { | ^~~ hw/xen/xen-hvm-common.c:18:6: error: no previous prototype for ‘xen_mr_is_memory’ [-Werror=missing-prototypes] 18 | bool xen_mr_is_memory(MemoryRegion *mr) | ^~~~~~~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20250218162618.46167-5-philmd@linaro.org> --- hw/i386/xen/xen-pvh.c | 1 + hw/xen/xen-pvh-common.c | 5 ++--- include/hw/xen/xen-pvh-common.h | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c index 33c1027976..f6356f2a7e 100644 --- a/hw/i386/xen/xen-pvh.c +++ b/hw/i386/xen/xen-pvh.c @@ -14,6 +14,7 @@ #include "hw/xen/arch_hvm.h" #include #include "hw/xen/xen-pvh-common.h" +#include "target/i386/cpu.h" #define TYPE_XEN_PVH_X86 MACHINE_TYPE_NAME("xenpvh") OBJECT_DECLARE_SIMPLE_TYPE(XenPVHx86State, XEN_PVH_X86) diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index 9c21fa858d..d675f7a8ae 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -8,14 +8,13 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "qapi/error.h" +#include "qemu/units.h" #include "qapi/visitor.h" #include "hw/boards.h" #include "hw/irq.h" -#include "hw/sysbus.h" -#include "system/system.h" #include "system/tpm.h" #include "system/tpm_backend.h" +#include "system/runstate.h" #include "hw/xen/xen-pvh-common.h" #include "trace.h" diff --git a/include/hw/xen/xen-pvh-common.h b/include/hw/xen/xen-pvh-common.h index 5cdd23c2f4..17c5a58a5a 100644 --- a/include/hw/xen/xen-pvh-common.h +++ b/include/hw/xen/xen-pvh-common.h @@ -9,11 +9,11 @@ #ifndef XEN_PVH_COMMON_H__ #define XEN_PVH_COMMON_H__ -#include -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/xen/xen-hvm-common.h" +#include "exec/memory.h" +#include "qom/object.h" +#include "hw/boards.h" #include "hw/pci-host/gpex.h" +#include "hw/xen/xen-hvm-common.h" #define TYPE_XEN_PVH_MACHINE MACHINE_TYPE_NAME("xen-pvh-base") OBJECT_DECLARE_TYPE(XenPVHMachineState, XenPVHMachineClass, From 65132d39ac31b492ec60047b0c82c1a9001fb967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 13:44:57 +0100 Subject: [PATCH 2109/2892] hw/xen/xen-hvm: Reduce included headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have "hw/xen/xen-hvm-common.h" include the bare minimal set of headers. Adapt sources to avoid errors when refactoring unrelated headers such: include/hw/xen/xen-hvm-common.h:71:5: error: unknown type name ‘xenevtchn_handle’ 71 | xenevtchn_handle *xce_handle; | ^~~~~~~~~~~~~~~~ hw/xen/xen-hvm-common.c: In function ‘cpu_get_ioreq’: hw/xen/xen-hvm-common.c:227:13: error: implicit declaration of function ‘hw_error’ 227 | hw_error("Fatal error while trying to get io event!\n"); | ^~~~~~~~ | herror hw/xen/xen-hvm-common.c: In function ‘handle_ioreq’: hw/xen/xen-hvm-common.c:446:34: error: ‘target_ulong’ undeclared (first use in this function) 446 | (req->size < sizeof (target_ulong))) { | ^~~~~~~~~~~~ hw/i386/xen/xen-hvm.c: In function ‘xen_add_to_physmap’: hw/i386/xen/xen-hvm.c:298:22: error: implicit declaration of function ‘xen_replace_cache_entry’ 298 | uint8_t *p = xen_replace_cache_entry(phys_offset, start_addr, size); | ^~~~~~~~~~~~~~~~~~~~~~~ hw/i386/xen/xen-hvm.c:314:9: error: implicit declaration of function 'error_report' is invalid in C99 314 | error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx ^~~~~~~~~~~~ hw/i386/xen/xen-hvm.c: In function ‘xen_log_global_start’: hw/i386/xen/xen-hvm.c:465:9: error: implicit declaration of function ‘xen_enabled’ 465 | if (xen_enabled()) { | ^~~~~~~~~~~ hw/i386/xen/xen-hvm.c: In function ‘regs_to_cpu’: hw/i386/xen/xen-hvm.c:487:5: error: unknown type name ‘X86CPU’ 487 | X86CPU *cpu; | ^~~~~~ hw/i386/xen/xen-hvm.c:492:15: error: ‘R_EAX’ undeclared (first use in this function) 492 | env->regs[R_EAX] = req->data; | ^~~~~ | REG_RAX Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anthony PERARD Message-Id: <20250218162618.46167-6-philmd@linaro.org> --- hw/arm/xen-stubs.c | 5 ++--- hw/i386/xen/xen-hvm.c | 6 ++++++ hw/xen/xen-hvm-common.c | 7 +++++++ include/hw/xen/xen-hvm-common.h | 14 +++----------- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/hw/arm/xen-stubs.c b/hw/arm/xen-stubs.c index 34beb8b08c..5551584dc2 100644 --- a/hw/arm/xen-stubs.c +++ b/hw/arm/xen-stubs.c @@ -5,10 +5,9 @@ */ #include "qemu/osdep.h" -#include "qemu/error-report.h" #include "qapi/qapi-commands-migration.h" -#include "hw/boards.h" -#include "system/system.h" +#include "system/xen.h" +#include "hw/hw.h" #include "hw/xen/xen-hvm-common.h" #include "hw/xen/arch_hvm.h" diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index d3df488c48..d4516acec6 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -10,10 +10,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" #include "trace.h" +#include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/irq.h" #include "hw/i386/apic-msidef.h" @@ -24,6 +26,10 @@ #include "hw/xen/arch_hvm.h" #include #include "exec/target_page.h" +#include "target/i386/cpu.h" +#include "system/runstate.h" +#include "system/xen-mapcache.h" +#include "system/xen.h" static MemoryRegion ram_640k, ram_lo, ram_hi; static MemoryRegion *framebuffer; diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index 7ffbbfea23..9a677e8ed7 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -1,14 +1,21 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/error-report.h" #include "qapi/error.h" +#include "exec/target_long.h" #include "exec/target_page.h" #include "trace.h" +#include "hw/hw.h" #include "hw/pci/pci_host.h" #include "hw/xen/xen-hvm-common.h" #include "hw/xen/xen-bus.h" #include "hw/boards.h" #include "hw/xen/arch_hvm.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" +#include "system/xen-mapcache.h" MemoryRegion xen_memory, xen_grants; diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h index c1ea2c0d78..19df5600a3 100644 --- a/include/hw/xen/xen-hvm-common.h +++ b/include/hw/xen/xen-hvm-common.h @@ -1,18 +1,10 @@ #ifndef HW_XEN_HVM_COMMON_H #define HW_XEN_HVM_COMMON_H -#include "qemu/units.h" - -#include "cpu.h" -#include "hw/pci/pci.h" -#include "hw/hw.h" +#include "qemu/queue.h" +#include "exec/hwaddr.h" #include "hw/xen/xen_native.h" -#include "hw/xen/xen-legacy-backend.h" -#include "system/runstate.h" -#include "system/system.h" -#include "system/xen.h" -#include "system/xen-mapcache.h" -#include "qemu/error-report.h" +#include "hw/xen/xen_backend_ops.h" #include extern MemoryRegion xen_memory; From cc2b1c5b0764a2f96106ed75c5bd141950148b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 13:45:19 +0100 Subject: [PATCH 2110/2892] hw/xen/xen-bus: Reduce included headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have "hw/xen/xen-bus" include the bare minimal set of headers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anthony PERARD Message-Id: <20250218162618.46167-7-philmd@linaro.org> --- include/hw/xen/xen-bus.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 2adb2af839..bdbf1ed6fd 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -8,9 +8,10 @@ #ifndef HW_XEN_BUS_H #define HW_XEN_BUS_H +#include "hw/qdev-core.h" #include "hw/xen/xen_backend_ops.h" -#include "hw/sysbus.h" #include "qemu/notify.h" +#include "qemu/queue.h" #include "qom/object.h" typedef struct XenEventChannel XenEventChannel; From d1bb9921bbbc65ecbb7c6193834e154af0cd0a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 16:57:06 +0100 Subject: [PATCH 2111/2892] hw/xen/xen-legacy-backend: Remove unused 'net/net.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anthony PERARD Message-Id: <20250218162618.46167-8-philmd@linaro.org> --- include/hw/xen/xen-legacy-backend.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index e198b120c5..2d0cbfecad 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -3,7 +3,6 @@ #include "hw/xen/xen_backend_ops.h" #include "hw/xen/xen_pvdev.h" -#include "net/net.h" #include "qom/object.h" #define TYPE_XENSYSDEV "xen-sysdev" From 92988c45017ca9385f408eb109184d68d4fef8e7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 18 Feb 2025 16:54:07 +0100 Subject: [PATCH 2112/2892] hw/net/fsl_etsec: Set eTSEC device description and category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add description and set category for eTSEC device so it shows up better in -device help. Signed-off-by: BALATON Zoltan Reviewed-by: Bernhard Beschow Message-ID: <20250218155407.838774E600E@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/fsl_etsec/etsec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 3ce4fa2662..adde644892 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -423,8 +423,10 @@ static void etsec_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = etsec_realize; + dc->desc = "Freescale Enhanced Three-Speed Ethernet Controller"; device_class_set_legacy_reset(dc, etsec_reset); device_class_set_props(dc, etsec_properties); + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } static const TypeInfo etsec_types[] = { From abf2b6a028670bd2890bb3aee7e103fe53e4b0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 22 May 2023 11:05:49 +0200 Subject: [PATCH 2113/2892] hw/char/pl011: Warn when using disabled receiver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We shouldn't receive characters when the full UART or its receiver is disabled. However we don't want to break the possibly incomplete "my first bare metal assembly program"s, so we choose to simply display a warning when this occurs. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-Id: <20250220092903.3726-2-philmd@linaro.org> --- hw/char/pl011.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 06ce851044..12a2d4bc7b 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -85,6 +85,7 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) #define CR_OUT1 (1 << 12) #define CR_RTS (1 << 11) #define CR_DTR (1 << 10) +#define CR_RXE (1 << 9) #define CR_TXE (1 << 8) #define CR_LBE (1 << 7) #define CR_UARTEN (1 << 0) @@ -487,6 +488,14 @@ static int pl011_can_receive(void *opaque) PL011State *s = (PL011State *)opaque; int r; + 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"); + } r = s->read_count < pl011_get_fifo_depth(s); trace_pl011_can_receive(s->lcr, s->read_count, r); return r; From f33af61dbab3b6fe0923bd829461584eaa41039e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 15:35:53 +0100 Subject: [PATCH 2114/2892] hw/char/pl011: Simplify a bit pl011_can_receive() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce 'fifo_depth' and 'fifo_available' local variables to better express the 'r' variable use. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-3-philmd@linaro.org> --- hw/char/pl011.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 12a2d4bc7b..5bb83c5421 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -486,7 +486,9 @@ static void pl011_write(void *opaque, hwaddr offset, static int pl011_can_receive(void *opaque) { PL011State *s = (PL011State *)opaque; - int r; + unsigned fifo_depth = pl011_get_fifo_depth(s); + unsigned fifo_available = fifo_depth - s->read_count; + int r = fifo_available ? 1 : 0; if (!(s->cr & CR_UARTEN)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -496,7 +498,6 @@ static int pl011_can_receive(void *opaque) qemu_log_mask(LOG_GUEST_ERROR, "PL011 receiving data on disabled RX UART\n"); } - r = s->read_count < pl011_get_fifo_depth(s); trace_pl011_can_receive(s->lcr, s->read_count, r); return r; } From 2c459f734ddda44f92231098e8cc9e3f36a8593c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 15:37:08 +0100 Subject: [PATCH 2115/2892] hw/char/pl011: Improve RX flow tracing events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Log FIFO use (availability and depth). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Luc Michel Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-4-philmd@linaro.org> --- hw/char/pl011.c | 10 ++++++---- hw/char/trace-events | 7 ++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 5bb83c5421..f7485e7c54 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -185,7 +185,7 @@ static void pl011_fifo_rx_put(void *opaque, uint32_t value) s->read_fifo[slot] = value; s->read_count++; s->flags &= ~PL011_FLAG_RXFE; - trace_pl011_fifo_rx_put(value, s->read_count); + trace_pl011_fifo_rx_put(value, s->read_count, pipe_depth); if (s->read_count == pipe_depth) { trace_pl011_fifo_rx_full(); s->flags |= PL011_FLAG_RXFF; @@ -248,12 +248,13 @@ static void pl011_write_txdata(PL011State *s, uint8_t data) static uint32_t pl011_read_rxdata(PL011State *s) { uint32_t c; + unsigned fifo_depth = pl011_get_fifo_depth(s); s->flags &= ~PL011_FLAG_RXFF; c = s->read_fifo[s->read_pos]; if (s->read_count > 0) { s->read_count--; - s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1); + s->read_pos = (s->read_pos + 1) & (fifo_depth - 1); } if (s->read_count == 0) { s->flags |= PL011_FLAG_RXFE; @@ -261,7 +262,7 @@ static uint32_t pl011_read_rxdata(PL011State *s) if (s->read_count == s->read_trigger - 1) { s->int_level &= ~INT_RX; } - trace_pl011_read_fifo(s->read_count); + trace_pl011_read_fifo(s->read_count, fifo_depth); s->rsr = c >> 8; pl011_update(s); qemu_chr_fe_accept_input(&s->chr); @@ -498,12 +499,13 @@ static int pl011_can_receive(void *opaque) qemu_log_mask(LOG_GUEST_ERROR, "PL011 receiving data on disabled RX UART\n"); } - trace_pl011_can_receive(s->lcr, s->read_count, r); + trace_pl011_can_receive(s->lcr, s->read_count, fifo_depth, fifo_available); return r; } static void pl011_receive(void *opaque, const uint8_t *buf, int size) { + trace_pl011_receive(size); /* * In loopback mode, the RX input signal is internally disconnected * from the entire receiving logics; thus, all inputs are ignored, diff --git a/hw/char/trace-events b/hw/char/trace-events index b2e3d25ae3..05a33036c1 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -60,12 +60,13 @@ imx_serial_put_data(const char *chrname, uint32_t value) "%s: 0x%" PRIx32 # pl011.c pl011_irq_state(int level) "irq state %d" pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" -pl011_read_fifo(int read_count) "FIFO read, read_count now %d" +pl011_read_fifo(unsigned rx_fifo_used, size_t rx_fifo_depth) "RX FIFO read, used %u/%zu" pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" -pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d returning %d" -pl011_fifo_rx_put(uint32_t c, int read_count) "new char 0x%02x read_count now %d" +pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars" +pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used" pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set" pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")" +pl011_receive(int size) "recv %d chars" # cmsdk-apb-uart.c cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" From 3e0f118f8259dd5bcdf9caf3762f92718b97f47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 15:37:50 +0100 Subject: [PATCH 2116/2892] hw/char/pl011: Really use RX FIFO depth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we model a 16-elements RX FIFO since the PL011 model was introduced in commit cdbdb648b7c ("ARM Versatile Platform Baseboard emulation"), we only read 1 char at a time! Have the IOCanReadHandler handler return how many elements are available, and use that in the IOReadHandler handler. Example of FIFO better used by enabling the pl011 tracing events and running the tests/functional/test_aarch64_virt.py tests: pl011_can_receive LCR 0x70, RX FIFO used 0/16, can_receive 16 chars pl011_receive recv 5 chars pl011_fifo_rx_put RX FIFO push char [0x72] 1/16 depth used pl011_irq_state irq state 1 pl011_fifo_rx_put RX FIFO push char [0x6f] 2/16 depth used pl011_fifo_rx_put RX FIFO push char [0x6f] 3/16 depth used pl011_fifo_rx_put RX FIFO push char [0x74] 4/16 depth used pl011_fifo_rx_put RX FIFO push char [0x0d] 5/16 depth used pl011_can_receive LCR 0x70, RX FIFO used 5/16, can_receive 11 chars pl011_can_receive LCR 0x70, RX FIFO used 5/16, can_receive 11 chars pl011_write addr 0x038 value 0x00000050 reg IMSC pl011_irq_state irq state 1 pl011_can_receive LCR 0x70, RX FIFO used 5/16, can_receive 11 chars pl011_read addr 0x03c value 0x00000030 reg RIS pl011_write addr 0x044 value 0x00000000 reg ICR pl011_irq_state irq state 1 pl011_read addr 0x018 value 0x00000080 reg FR pl011_read_fifo RX FIFO read, used 4/16 pl011_irq_state irq state 1 pl011_read addr 0x000 value 0x00000072 reg DR pl011_can_receive LCR 0x70, RX FIFO used 4/16, can_receive 12 chars pl011_read addr 0x018 value 0x00000080 reg FR pl011_read_fifo RX FIFO read, used 3/16 pl011_irq_state irq state 1 pl011_read addr 0x000 value 0x0000006f reg DR pl011_can_receive LCR 0x70, RX FIFO used 3/16, can_receive 13 chars pl011_read addr 0x018 value 0x00000080 reg FR pl011_read_fifo RX FIFO read, used 2/16 pl011_irq_state irq state 1 pl011_read addr 0x000 value 0x0000006f reg DR pl011_can_receive LCR 0x70, RX FIFO used 2/16, can_receive 14 chars pl011_read addr 0x018 value 0x00000080 reg FR pl011_read_fifo RX FIFO read, used 1/16 pl011_irq_state irq state 1 pl011_read addr 0x000 value 0x00000074 reg DR pl011_can_receive LCR 0x70, RX FIFO used 1/16, can_receive 15 chars pl011_read addr 0x018 value 0x00000080 reg FR pl011_read_fifo RX FIFO read, used 0/16 pl011_irq_state irq state 0 pl011_read addr 0x000 value 0x0000000d reg DR pl011_can_receive LCR 0x70, RX FIFO used 0/16, can_receive 16 chars pl011_read addr 0x018 value 0x00000090 reg FR pl011_read addr 0x03c value 0x00000020 reg RIS pl011_write addr 0x038 value 0x00000050 reg IMSC pl011_irq_state irq state 0 pl011_can_receive LCR 0x70, RX FIFO used 0/16, can_receive 16 chars pl011_can_receive LCR 0x70, RX FIFO used 0/16, can_receive 16 chars pl011_read addr 0x018 value 0x00000090 reg FR pl011_write addr 0x000 value 0x00000072 reg DR Inspired-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-5-philmd@linaro.org> --- hw/char/pl011.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index f7485e7c54..23a9db8c57 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -489,7 +489,6 @@ static int pl011_can_receive(void *opaque) PL011State *s = (PL011State *)opaque; unsigned fifo_depth = pl011_get_fifo_depth(s); unsigned fifo_available = fifo_depth - s->read_count; - int r = fifo_available ? 1 : 0; if (!(s->cr & CR_UARTEN)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -500,7 +499,8 @@ static int pl011_can_receive(void *opaque) "PL011 receiving data on disabled RX UART\n"); } trace_pl011_can_receive(s->lcr, s->read_count, fifo_depth, fifo_available); - return r; + + return fifo_available; } static void pl011_receive(void *opaque, const uint8_t *buf, int size) @@ -515,7 +515,9 @@ static void pl011_receive(void *opaque, const uint8_t *buf, int size) return; } - pl011_fifo_rx_put(opaque, *buf); + for (int i = 0; i < size; i++) { + pl011_fifo_rx_put(opaque, buf[i]); + } } static void pl011_event(void *opaque, QEMUChrEvent event) From 2e6b2e08756e618e5d4316ff277e78213942a2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 16:23:13 +0100 Subject: [PATCH 2117/2892] hw/char/bcm2835_aux: Really use RX FIFO depth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we model a 8-elements RX FIFO since the BCM2835 AUX model was introduced in commit 97398d900ca ("bcm2835_aux: add emulation of BCM2835 AUX block") we only read 1 char at a time! Have the IOCanReadHandler handler return how many elements are available, and use that in the IOReadHandler handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-6-philmd@linaro.org> --- hw/char/bcm2835_aux.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 73ad593406..c6e7eccf7d 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -221,7 +221,7 @@ static int bcm2835_aux_can_receive(void *opaque) { BCM2835AuxState *s = opaque; - return s->read_count < BCM2835_AUX_RX_FIFO_LEN; + return BCM2835_AUX_RX_FIFO_LEN - s->read_count; } static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) @@ -243,7 +243,9 @@ static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size) { - bcm2835_aux_put_fifo(opaque, *buf); + for (int i = 0; i < size; i++) { + bcm2835_aux_put_fifo(opaque, buf[i]); + } } static const MemoryRegionOps bcm2835_aux_ops = { From 91f8c04dd25f2eacbb9fd57db95b09313cdfd085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 16:23:30 +0100 Subject: [PATCH 2118/2892] hw/char/imx_serial: Really use RX FIFO depth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we model a 32-elements RX FIFO since the IMX serial model was introduced in commit 988f2442971 ("hw/char/imx_serial: Implement receive FIFO and ageing timer") we only read 1 char at a time! Have the IOCanReadHandler handler return how many elements are available, and use that in the IOReadHandler handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Reviewed-by: Richard Henderson Tested-by: Bernhard Beschow Message-Id: <20250220092903.3726-7-philmd@linaro.org> --- hw/char/imx_serial.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 38b4865157..6f14f8403a 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -386,7 +386,8 @@ static void imx_serial_write(void *opaque, hwaddr offset, static int imx_can_receive(void *opaque) { IMXSerialState *s = (IMXSerialState *)opaque; - return s->ucr2 & UCR2_RXEN && fifo32_num_used(&s->rx_fifo) < FIFO_SIZE; + + return s->ucr2 & UCR2_RXEN ? fifo32_num_free(&s->rx_fifo) : 0; } static void imx_put_data(void *opaque, uint32_t value) @@ -417,7 +418,10 @@ static void imx_receive(void *opaque, const uint8_t *buf, int size) IMXSerialState *s = (IMXSerialState *)opaque; s->usr2 |= USR2_WAKE; - imx_put_data(opaque, *buf); + + for (int i = 0; i < size; i++) { + imx_put_data(opaque, buf[i]); + } } static void imx_event(void *opaque, QEMUChrEvent event) From 3d978e7b9b25913620d57eb73256f395ab1c18a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 16:23:44 +0100 Subject: [PATCH 2119/2892] hw/char/mcf_uart: Use FIFO_DEPTH definition instead of magic values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defines FIFO_DEPTH and use it, fixing coding style. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-8-philmd@linaro.org> --- hw/char/mcf_uart.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 980a12fcb7..95f269ee9b 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -17,6 +17,8 @@ #include "chardev/char-fe.h" #include "qom/object.h" +#define FIFO_DEPTH 4 + struct mcf_uart_state { SysBusDevice parent_obj; @@ -27,7 +29,7 @@ struct mcf_uart_state { uint8_t imr; uint8_t bg1; uint8_t bg2; - uint8_t fifo[4]; + uint8_t fifo[FIFO_DEPTH]; uint8_t tb; int current_mr; int fifo_len; @@ -247,14 +249,16 @@ static void mcf_uart_reset(DeviceState *dev) static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) { /* Break events overwrite the last byte if the fifo is full. */ - if (s->fifo_len == 4) + if (s->fifo_len == FIFO_DEPTH) { s->fifo_len--; + } s->fifo[s->fifo_len] = data; s->fifo_len++; s->sr |= MCF_UART_RxRDY; - if (s->fifo_len == 4) + if (s->fifo_len == FIFO_DEPTH) { s->sr |= MCF_UART_FFULL; + } mcf_uart_update(s); } From 3ca8af5445b493ae3bc1520c71de3fd7e4555af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 16:25:17 +0100 Subject: [PATCH 2120/2892] hw/char/mcf_uart: Really use RX FIFO depth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we model a 4-elements RX FIFO since the MCF UART model was introduced in commit 20dcee94833 ("MCF5208 emulation"), we only read 1 char at a time! Have the IOCanReadHandler handler return how many elements are available, and use that in the IOReadHandler handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Tested-by: Thomas Huth Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-9-philmd@linaro.org> --- hw/char/mcf_uart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 95f269ee9b..529c26be93 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -281,14 +281,16 @@ static int mcf_uart_can_receive(void *opaque) { mcf_uart_state *s = (mcf_uart_state *)opaque; - return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; + return s->rx_enabled ? FIFO_DEPTH - s->fifo_len : 0; } static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) { mcf_uart_state *s = (mcf_uart_state *)opaque; - mcf_uart_push_byte(s, buf[0]); + for (int i = 0; i < size; i++) { + mcf_uart_push_byte(s, buf[i]); + } } static const MemoryRegionOps mcf_uart_ops = { From 543671d9907f7604b83fa9e76cdb04fa9fab9cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Feb 2025 16:44:42 +0100 Subject: [PATCH 2121/2892] hw/char/sh_serial: Return correct number of empty RX FIFO elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the IOCanReadHandler sh_serial_can_receive(), if the Serial Control Register 'Receive Enable' bit is set (bit 4), then we return a size of (1 << 4) which happens to be equal to 16, so effectively SH_RX_FIFO_LENGTH. The IOReadHandler, sh_serial_receive1() takes care to receive multiple chars, but if the FIFO is partly filled, we only process the number of free slots in the FIFO, discarding the other chars! Fix by returning how many elements the FIFO can queue in the IOCanReadHandler, so we don't have to process more than that in the IOReadHandler, thus not discarding anything. Remove the now unnecessary check on 's->rx_cnt < SH_RX_FIFO_LENGTH' in IOReadHandler, reducing the block indentation. Fixes: 63242a007a1 ("SH4: Serial controller improvement") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Reviewed-by: Richard Henderson Message-Id: <20250220092903.3726-10-philmd@linaro.org> --- hw/char/sh_serial.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 247aeb071a..41c8175a63 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -320,7 +320,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, static int sh_serial_can_receive(SHSerialState *s) { - return s->scr & (1 << 4); + return s->scr & (1 << 4) ? SH_RX_FIFO_LENGTH - s->rx_head : 0; } static void sh_serial_receive_break(SHSerialState *s) @@ -353,22 +353,20 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) if (s->feat & SH_SERIAL_FEAT_SCIF) { int i; for (i = 0; i < size; i++) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = buf[i]; - if (s->rx_head == SH_RX_FIFO_LENGTH) { - s->rx_head = 0; - } - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - timer_del(&s->fifo_timeout_timer); - qemu_set_irq(s->rxi, 1); - } - } else { - timer_mod(&s->fifo_timeout_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); + s->rx_fifo[s->rx_head++] = buf[i]; + if (s->rx_head == SH_RX_FIFO_LENGTH) { + s->rx_head = 0; + } + s->rx_cnt++; + if (s->rx_cnt >= s->rtrg) { + s->flags |= SH_SERIAL_FLAG_RDF; + if (s->scr & (1 << 6) && s->rxi) { + timer_del(&s->fifo_timeout_timer); + qemu_set_irq(s->rxi, 1); } + } else { + timer_mod(&s->fifo_timeout_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); } } } else { From 7fc96bc4fca0cd8f1733235e987fe8ccf4517203 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 3 Mar 2025 12:31:20 +1000 Subject: [PATCH 2122/2892] hw/char/sifive_uart: Free fifo on unrealize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We previously allocate the fifo on reset and never free it, which means we are leaking memory. Instead let's allocate on realize and free on unrealize. Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Tested-by: Clément Chigot Message-ID: <20250303023120.157221-1-alistair.francis@wdc.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sifive_uart.c | 44 +++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 4bc5767284..b45e6c098c 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -251,6 +251,23 @@ static int sifive_uart_be_change(void *opaque) return 0; } +static void sifive_uart_reset_enter(Object *obj, ResetType type) +{ + SiFiveUARTState *s = SIFIVE_UART(obj); + + s->txfifo = 0; + s->ie = 0; + s->ip = 0; + s->txctrl = 0; + s->rxctrl = 0; + s->div = 0; + + s->rx_fifo_len = 0; + + memset(s->rx_fifo, 0, SIFIVE_UART_RX_FIFO_SIZE); + fifo8_reset(&s->tx_fifo); +} + static const Property sifive_uart_properties[] = { DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr), }; @@ -270,30 +287,24 @@ static void sifive_uart_realize(DeviceState *dev, Error **errp) { SiFiveUARTState *s = SIFIVE_UART(dev); + fifo8_create(&s->tx_fifo, SIFIVE_UART_TX_FIFO_SIZE); + s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, fifo_trigger_update, s); - qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, - sifive_uart_event, sifive_uart_be_change, s, - NULL, true); + if (qemu_chr_fe_backend_connected(&s->chr)) { + qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, + sifive_uart_event, sifive_uart_be_change, s, + NULL, true); + } } -static void sifive_uart_reset_enter(Object *obj, ResetType type) +static void sifive_uart_unrealize(DeviceState *dev) { - SiFiveUARTState *s = SIFIVE_UART(obj); + SiFiveUARTState *s = SIFIVE_UART(dev); - s->txfifo = 0; - s->ie = 0; - s->ip = 0; - s->txctrl = 0; - s->rxctrl = 0; - s->div = 0; - - s->rx_fifo_len = 0; - - memset(s->rx_fifo, 0, SIFIVE_UART_RX_FIFO_SIZE); - fifo8_create(&s->tx_fifo, SIFIVE_UART_TX_FIFO_SIZE); + fifo8_destroy(&s->tx_fifo); } static void sifive_uart_reset_hold(Object *obj, ResetType type) @@ -329,6 +340,7 @@ static void sifive_uart_class_init(ObjectClass *oc, void *data) ResettableClass *rc = RESETTABLE_CLASS(oc); dc->realize = sifive_uart_realize; + dc->unrealize = sifive_uart_unrealize; dc->vmsd = &vmstate_sifive_uart; rc->phases.enter = sifive_uart_reset_enter; rc->phases.hold = sifive_uart_reset_hold; From d0f439ddd37e1354daca3234976295189985cb04 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Fri, 28 Feb 2025 21:57:08 +0200 Subject: [PATCH 2123/2892] iotest: Unbreak 302 with python 3.13 This test depends on TarFile.addfile() to add tar member header without writing the member data, which we write ourself using qemu-nbd. Python 3.13 changed the function in a backward incompatible way[1] to require a file object for tarinfo with non-zero size, breaking the test: -[{"name": "vm.ovf", "offset": 512, "size": 6}, {"name": "disk", "offset": 1536, "size": 393216}] +Traceback (most recent call last): + File "/home/stefanha/qemu/tests/qemu-iotests/302", line 118, in + tar.addfile(disk) + ~~~~~~~~~~~^^^^^^ + File "/usr/lib64/python3.13/tarfile.py", line 2262, in addfile + raise ValueError("fileobj not provided for non zero-size regular file") +ValueError: fileobj not provided for non zero-size regular file The new behavior makes sense for most users, but breaks our unusual usage. Fix the test to add the member header directly using public but undocumented attributes. This is more fragile but the test works again. This also fixes a bug in the previous code - when calling addfile() without a fileobject, tar.offset points to the start of the member data instead of the end. [1] https://github.com/python/cpython/pull/117988 Signed-off-by: Nir Soffer Message-ID: <20250228195708.48035-1-nirsof@gmail.com> Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Signed-off-by: Eric Blake --- tests/qemu-iotests/302 | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/302 b/tests/qemu-iotests/302 index a6d79e727b..e980ec513f 100755 --- a/tests/qemu-iotests/302 +++ b/tests/qemu-iotests/302 @@ -115,13 +115,22 @@ with tarfile.open(tar_file, "w") as tar: disk = tarfile.TarInfo("disk") disk.size = actual_size - tar.addfile(disk) - # 6. Shrink the tar to the actual size, aligned to 512 bytes. + # Since python 3.13 we cannot use addfile() to create the member header. + # Add the tarinfo directly using public but undocumented attributes. - tar_size = offset + (disk.size + 511) & ~511 - tar.fileobj.seek(tar_size) - tar.fileobj.truncate(tar_size) + buf = disk.tobuf(tar.format, tar.encoding, tar.errors) + tar.fileobj.write(buf) + tar.members.append(disk) + + # Update the offset and position to the location of the next member. + + tar.offset = offset + (disk.size + 511) & ~511 + tar.fileobj.seek(tar.offset) + + # 6. Shrink the tar to the actual size. + + tar.fileobj.truncate(tar.offset) with tarfile.open(tar_file) as tar: members = [{"name": m.name, "size": m.size, "offset": m.offset_data} From e2668ba1ed44ad56f2f1653ff5f53b277d534fac Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 25 Feb 2025 08:06:50 +0100 Subject: [PATCH 2124/2892] iotests: Stop NBD server in test 162 before starting the next one Test 162 recently started failing for me for no obvious reasons (I did not spot any suspicious commits in this area), but looking in the 162.out.bad log file, there was a suspicious message at the end: qemu-nbd: Cannot lock pid file: Resource temporarily unavailable And indeed, the test starts the NBD server two times, without stopping the first server before running the second one, so the second one can indeed fail to lock the PID file. Thus let's make sure to stop the first server before the test continues with the second one. With this change, the test works fine for me again. Signed-off-by: Thomas Huth Message-ID: <20250225070650.387638-1-thuth@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- tests/qemu-iotests/162 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index 94dae60d30..956c2c5f33 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -65,6 +65,7 @@ done $QEMU_IMG info "json:{'driver': 'nbd', 'host': 'localhost', 'port': $port}" \ | grep '^image' | sed -e "s/$port/PORT/" +_stop_nbd_server # This is a test for NBD's bdrv_refresh_filename() implementation: It expects # either host or path to be set, but it must not assume that they are set to From 57f3962bf17c088c3567d216e3eaa1b3131be5a4 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 19 Feb 2025 22:19:14 +0300 Subject: [PATCH 2125/2892] qapi: merge common parts of NbdServerOptions and nbd-server-start data Instead of comment "Keep this type consistent with the nbd-server-start arguments", we can simply merge these things. Note that each field of new base already has "since" tag, equal in both original copies. So "since" information is saved. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-ID: <20250219191914.440451-1-vsementsov@yandex-team.ru> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- blockdev-nbd.c | 4 +- qapi/block-export.json | 94 +++++++++++++++++++----------------------- 2 files changed, 44 insertions(+), 54 deletions(-) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 3f6f4ef92b..1e3e634b87 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -219,12 +219,12 @@ void nbd_server_start_options(NbdServerOptions *arg, Error **errp) arg->tls_authz, arg->max_connections, errp); } -void qmp_nbd_server_start(SocketAddressLegacy *addr, - bool has_handshake_max_secs, +void qmp_nbd_server_start(bool has_handshake_max_secs, uint32_t handshake_max_secs, const char *tls_creds, const char *tls_authz, bool has_max_connections, uint32_t max_connections, + SocketAddressLegacy *addr, Error **errp) { SocketAddress *addr_flat = socket_address_flatten(addr); diff --git a/qapi/block-export.json b/qapi/block-export.json index 68dcec7edc..c783e01a53 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -9,53 +9,7 @@ { 'include': 'block-core.json' } ## -# @NbdServerOptions: -# -# Keep this type consistent with the nbd-server-start arguments. The -# only intended difference is using SocketAddress instead of -# SocketAddressLegacy. -# -# @addr: Address on which to listen. -# -# @handshake-max-seconds: Time limit, in seconds, at which a client -# that has not completed the negotiation handshake will be -# disconnected, 0 for no limit (since 10.0; default: 10). -# -# @tls-creds: ID of the TLS credentials object (since 2.6). -# -# @tls-authz: ID of the QAuthZ authorization object used to validate -# the client's x509 distinguished name. This object is is only -# resolved at time of use, so can be deleted and recreated on the -# fly while the NBD server is active. If missing, it will default -# to denying access (since 4.0). -# -# @max-connections: The maximum number of connections to allow at the -# same time, 0 for unlimited. Setting this to 1 also stops the -# server from advertising multiple client support (since 5.2; -# default: 100) -# -# Since: 4.2 -## -{ 'struct': 'NbdServerOptions', - 'data': { 'addr': 'SocketAddress', - '*handshake-max-seconds': 'uint32', - '*tls-creds': 'str', - '*tls-authz': 'str', - '*max-connections': 'uint32' } } - -## -# @nbd-server-start: -# -# Start an NBD server listening on the given host and port. Block -# devices can then be exported using @nbd-server-add. The NBD server -# will present them as named exports; for example, another QEMU -# instance could refer to them as "nbd:HOST:PORT:exportname=NAME". -# -# Keep this type consistent with the NbdServerOptions type. The only -# intended difference is using SocketAddressLegacy instead of -# SocketAddress. -# -# @addr: Address on which to listen. +# @NbdServerOptionsBase: # # @handshake-max-seconds: Time limit, in seconds, at which a client # that has not completed the negotiation handshake will be @@ -73,6 +27,46 @@ # same time, 0 for unlimited. Setting this to 1 also stops the # server from advertising multiple client support (since 5.2; # default: 100). +## +{ 'struct': 'NbdServerOptionsBase', + 'data': { '*handshake-max-seconds': 'uint32', + '*tls-creds': 'str', + '*tls-authz': 'str', + '*max-connections': 'uint32' } } + +## +# @NbdServerOptions: +# +# Keep this type consistent with the NbdServerOptionsLegacy type. The +# only intended difference is using SocketAddress instead of +# SocketAddressLegacy. +# +# @addr: Address on which to listen (since 4.2). +## +{ 'struct': 'NbdServerOptions', + 'base': 'NbdServerOptionsBase', + 'data': { 'addr': 'SocketAddress' } } + +## +# @NbdServerOptionsLegacy: +# +# Keep this type consistent with the NbdServerOptions type. The only +# intended difference is using SocketAddressLegacy instead of +# SocketAddress. +# +# @addr: Address on which to listen (since 1.3). +## +{ 'struct': 'NbdServerOptionsLegacy', + 'base': 'NbdServerOptionsBase', + 'data': { 'addr': 'SocketAddressLegacy' } } + +## +# @nbd-server-start: +# +# Start an NBD server listening on the given host and port. Block +# devices can then be exported using @nbd-server-add. The NBD server +# will present them as named exports; for example, another QEMU +# instance could refer to them as "nbd:HOST:PORT:exportname=NAME". # # Errors: # - if the server is already running @@ -80,11 +74,7 @@ # Since: 1.3 ## { 'command': 'nbd-server-start', - 'data': { 'addr': 'SocketAddressLegacy', - '*handshake-max-seconds': 'uint32', - '*tls-creds': 'str', - '*tls-authz': 'str', - '*max-connections': 'uint32' }, + 'data': 'NbdServerOptionsLegacy', 'allow-preconfig': true } ## From 70f98ae150ab05e4807625878d271049af23716b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 22 Feb 2025 13:28:50 +0100 Subject: [PATCH 2126/2892] hw/misc/macio: Improve trace logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add macio_gpio_read trace event and use that in macio_gpio_read() instead of macio_gpio_write. Also change log message to match macio_timer_{read,write}. Signed-off-by: BALATON Zoltan Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250222122850.9D8B84E603D@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/macio/gpio.c | 2 +- hw/misc/macio/trace-events | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 7cad62819a..4364afc84a 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -135,7 +135,7 @@ static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size) } } - trace_macio_gpio_write(addr, val); + trace_macio_gpio_read(addr, val); return val; } diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index ad4b9d1c08..055a407aeb 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -18,7 +18,8 @@ macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx6 macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d" macio_gpio_irq_assert(int gpio) "asserting GPIO %d" macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" -macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 +macio_gpio_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64 +macio_gpio_read(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64 # pmu.c pmu_adb_poll(int olen) "ADB autopoll, olen=%d" From d9bf3cec6752663946c886e28d9ef1fc9586edb3 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 24 Feb 2025 15:10:26 +0100 Subject: [PATCH 2127/2892] hw/misc/macio/gpio: Add constants for register bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add named constants for register bit values that should make it easier to understand what these mean. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Message-ID: <20250224141026.3B36C4E6010@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/macio/gpio.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 4364afc84a..e87bfca1f5 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -34,6 +34,11 @@ #include "qemu/module.h" #include "trace.h" +enum MacioGPIORegisterBits { + OUT_DATA = 1, + IN_DATA = 2, + OUT_ENABLE = 4, +}; void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) { @@ -41,14 +46,14 @@ void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) trace_macio_set_gpio(gpio, state); - if (s->gpio_regs[gpio] & 4) { + if (s->gpio_regs[gpio] & OUT_ENABLE) { qemu_log_mask(LOG_GUEST_ERROR, "GPIO: Setting GPIO %d while it's an output\n", gpio); } - new_reg = s->gpio_regs[gpio] & ~2; + new_reg = s->gpio_regs[gpio] & ~IN_DATA; if (state) { - new_reg |= 2; + new_reg |= IN_DATA; } if (new_reg == s->gpio_regs[gpio]) { @@ -107,12 +112,12 @@ static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value, addr -= 8; if (addr < 36) { - value &= ~2; + value &= ~IN_DATA; - if (value & 4) { - ibit = (value & 1) << 1; + if (value & OUT_ENABLE) { + ibit = (value & OUT_DATA) << 1; } else { - ibit = s->gpio_regs[addr] & 2; + ibit = s->gpio_regs[addr] & IN_DATA; } s->gpio_regs[addr] = value | ibit; From 07b12aae50e4dc02ebce932dafd7dcae3335a915 Mon Sep 17 00:00:00 2001 From: Keoseong Park Date: Tue, 25 Feb 2025 15:41:46 +0900 Subject: [PATCH 2128/2892] hw/ufs: Add temperature event notification support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces temperature event notification support to the UFS emulation. It enables the emulated UFS device to generate temperature-related events, including high and low temperature notifications, in compliance with the UFS specification. With this feature, UFS drivers can now handle temperature exception events during testing and development within the emulated environment. This enhances validation and debugging capabilities for thermal event handling in UFS implementations. Signed-off-by: Keoseong Park Reviewed-by: Jeuk Kim Message-ID: <20250225064146epcms2p50889cb0066e2d4734f2386de325bcdf6@epcms2p5> Signed-off-by: Philippe Mathieu-Daudé --- hw/ufs/ufs.c | 78 ++++++++++++++++++++++++++++++++++++++++++++- hw/ufs/ufs.h | 2 ++ include/block/ufs.h | 13 +++++++- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 1ccd6f88b6..857de6e9c2 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -34,6 +34,11 @@ #define UFS_MAX_NUTMRS 8 #define UFS_MCQ_QCFGPTR 2 +/* Each value represents the temperature in celsius as (value - 80) */ +#define UFS_TEMPERATURE 120 +#define UFS_TOO_HIGH_TEMP_BOUNDARY 160 +#define UFS_TOO_LOW_TEMP_BOUNDARY 60 + static void ufs_exec_req(UfsRequest *req); static void ufs_clear_req(UfsRequest *req); @@ -838,6 +843,42 @@ static const MemoryRegionOps ufs_mmio_ops = { }, }; +static void ufs_update_ee_status(UfsHc *u) +{ + uint16_t ee_status = be16_to_cpu(u->attributes.exception_event_status); + uint8_t high_temp_thresh = u->attributes.device_too_high_temp_boundary; + uint8_t low_temp_thresh = u->attributes.device_too_low_temp_boundary; + + if (u->temperature >= high_temp_thresh) { + ee_status |= MASK_EE_TOO_HIGH_TEMP; + } else { + ee_status &= ~MASK_EE_TOO_HIGH_TEMP; + } + + if (u->temperature <= low_temp_thresh) { + ee_status |= MASK_EE_TOO_LOW_TEMP; + } else { + ee_status &= ~MASK_EE_TOO_LOW_TEMP; + } + + u->attributes.exception_event_status = cpu_to_be16(ee_status); +} + +static bool ufs_check_exception_event_alert(UfsHc *u, uint8_t trans_type) +{ + uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control); + uint16_t ee_status; + + if (trans_type != UFS_UPIU_TRANSACTION_RESPONSE) { + return false; + } + + ufs_update_ee_status(u); + + ee_status = be16_to_cpu(u->attributes.exception_event_status); + + return ee_control & ee_status; +} void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, uint8_t response, uint8_t scsi_status, @@ -848,6 +889,8 @@ void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, req->rsp_upiu.header.flags = flags; req->rsp_upiu.header.response = response; req->rsp_upiu.header.scsi_status = scsi_status; + req->rsp_upiu.header.device_inf = + ufs_check_exception_event_alert(req->hc, trans_type); req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length); } @@ -1042,6 +1085,25 @@ static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op) return UFS_QUERY_RESULT_SUCCESS; } +static inline uint8_t ufs_read_device_temp(UfsHc *u) +{ + uint8_t feat_sup = u->device_desc.ufs_features_support; + bool high_temp_sup, low_temp_sup, high_temp_en, low_temp_en; + uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control); + + high_temp_sup = feat_sup & UFS_DEV_HIGH_TEMP_NOTIF; + low_temp_sup = feat_sup & UFS_DEV_LOW_TEMP_NOTIF; + high_temp_en = ee_control & MASK_EE_TOO_HIGH_TEMP; + low_temp_en = ee_control & MASK_EE_TOO_LOW_TEMP; + + if ((high_temp_sup && high_temp_en) || + (low_temp_sup && low_temp_en)) { + return u->temperature; + } + + return 0; +} + static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) { switch (idn) { @@ -1072,6 +1134,7 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) case UFS_QUERY_ATTR_IDN_EE_CONTROL: return be16_to_cpu(u->attributes.exception_event_control); case UFS_QUERY_ATTR_IDN_EE_STATUS: + ufs_update_ee_status(u); return be16_to_cpu(u->attributes.exception_event_status); case UFS_QUERY_ATTR_IDN_SECONDS_PASSED: return be32_to_cpu(u->attributes.seconds_passed); @@ -1086,7 +1149,8 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) case UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME: return u->attributes.ref_clk_gating_wait_time; case UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP: - return u->attributes.device_case_rough_temperaure; + u->attributes.device_case_rough_temperature = ufs_read_device_temp(u); + return u->attributes.device_case_rough_temperature; case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND: return u->attributes.device_too_high_temp_boundary; case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND: @@ -1677,8 +1741,12 @@ static void ufs_init_hc(UfsHc *u) u->device_desc.ud_0_base_offset = 0x16; u->device_desc.ud_config_p_length = 0x1A; u->device_desc.device_rtt_cap = 0x02; + u->device_desc.ufs_features_support = UFS_DEV_HIGH_TEMP_NOTIF | + UFS_DEV_LOW_TEMP_NOTIF; u->device_desc.queue_depth = u->params.nutrs; u->device_desc.product_revision_level = 0x04; + u->device_desc.extended_ufs_features_support = + cpu_to_be32(UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF); memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor)); u->geometry_desc.length = sizeof(GeometryDescriptor); @@ -1702,9 +1770,17 @@ static void ufs_init_hc(UfsHc *u) /* configure descriptor is not supported */ u->attributes.config_descr_lock = 0x01; u->attributes.max_num_of_rtt = 0x02; + u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY; + u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY; memset(&u->flags, 0, sizeof(u->flags)); u->flags.permanently_disable_fw_update = 1; + + /* + * The temperature value is fixed to UFS_TEMPERATURE and does not change + * dynamically + */ + u->temperature = UFS_TEMPERATURE; } static void ufs_realize(PCIDevice *pci_dev, Error **errp) diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index 4bcc41f53a..3799d97f30 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -146,6 +146,8 @@ typedef struct UfsHc { /* MCQ properties */ UfsSq *sq[UFS_MAX_MCQ_QNUM]; UfsCq *cq[UFS_MAX_MCQ_QNUM]; + + uint8_t temperature; } UfsHc; static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid) diff --git a/include/block/ufs.h b/include/block/ufs.h index 57f5ea3500..a3ee62b027 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -461,7 +461,7 @@ typedef struct Attributes { uint8_t psa_state; uint32_t psa_data_size; uint8_t ref_clk_gating_wait_time; - uint8_t device_case_rough_temperaure; + uint8_t device_case_rough_temperature; uint8_t device_too_high_temp_boundary; uint8_t device_too_low_temp_boundary; uint8_t throttling_status; @@ -1073,6 +1073,11 @@ enum health_desc_param { UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4, }; +enum { + UFS_DEV_HIGH_TEMP_NOTIF = BIT(4), + UFS_DEV_LOW_TEMP_NOTIF = BIT(5), +}; + /* WriteBooster buffer mode */ enum { UFS_WB_BUF_MODE_LU_DEDICATED = 0x0, @@ -1091,6 +1096,12 @@ enum ufs_lu_wp_type { UFS_LU_PERM_WP = 0x02, }; +/* Exception event mask values */ +enum { + MASK_EE_TOO_HIGH_TEMP = BIT(3), + MASK_EE_TOO_LOW_TEMP = BIT(4), +}; + /* UTP QUERY Transaction Specific Fields OpCode */ enum query_opcode { UFS_UPIU_QUERY_OPCODE_NOP = 0x0, From 5f6cb3ca97c7ff4999855f561cc7b64e148cf497 Mon Sep 17 00:00:00 2001 From: Keoseong Park Date: Tue, 25 Feb 2025 15:42:43 +0900 Subject: [PATCH 2129/2892] tests/qtest/ufs-test: Add test code for the temperature feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds tests to verify the correctness of query attribute results related to the temperature feature. It ensures that querying temperature attributes returns expected values. Signed-off-by: Keoseong Park Acked-by: Fabiano Rosas Reviewed-by: Jeuk Kim Message-ID: <20250225064243epcms2p8b7b59e7bf381bd68d30a6f59b40dea9f@epcms2p8> Signed-off-by: Philippe Mathieu-Daudé --- tests/qtest/ufs-test.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index d5076bdeb5..4867ccf08a 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -784,6 +784,30 @@ static void ufstest_query_attr_request(void *obj, void *data, g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(160)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(60)); + /* Write Writable Attributes & Read Again */ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, From 08c99626cb66dda00a66d4396f7e0ccf6dec75fd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Feb 2025 17:01:13 +0000 Subject: [PATCH 2130/2892] hw/arm/omap1: Convert raw printfs to qemu_log_mask() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit omap1.c is very old code, and it contains numerous calls direct to printf() for various error and information cases. In this commit, convert the printf() calls that are for either guest error or unimplemented functionality to qemu_log_mask() calls. This leaves the printf() calls that are informative or which are ifdeffed-out debug statements untouched. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250227170117.1726895-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index ca2eb0d157..3c0ce5e097 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -2559,8 +2559,9 @@ static void omap_rtc_interrupts_update(struct omap_rtc_s *s) static void omap_rtc_alarm_update(struct omap_rtc_s *s) { s->alarm_ti = mktimegm(&s->alarm_tm); - if (s->alarm_ti == -1) - printf("%s: conversion failed\n", __func__); + if (s->alarm_ti == -1) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: conversion failed\n", __func__); + } } static uint64_t omap_rtc_read(void *opaque, hwaddr addr, unsigned size) @@ -3024,8 +3025,9 @@ static void omap_mcbsp_source_tick(void *opaque) if (!s->rx_rate) return; - if (s->rx_req) - printf("%s: Rx FIFO overrun\n", __func__); + if (s->rx_req) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO overrun\n", __func__); + } s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7]; @@ -3070,8 +3072,9 @@ static void omap_mcbsp_sink_tick(void *opaque) if (!s->tx_rate) return; - if (s->tx_req) - printf("%s: Tx FIFO underrun\n", __func__); + if (s->tx_req) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO underrun\n", __func__); + } s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7]; @@ -3173,7 +3176,7 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, /* Fall through. */ case 0x02: /* DRR1 */ if (s->rx_req < 2) { - printf("%s: Rx FIFO underrun\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO underrun\n", __func__); omap_mcbsp_rx_done(s); } else { s->tx_req -= 2; @@ -3278,8 +3281,9 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, } if (s->tx_req < 2) omap_mcbsp_tx_done(s); - } else - printf("%s: Tx FIFO overrun\n", __func__); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO overrun\n", __func__); + } return; case 0x08: /* SPCR2 */ @@ -3293,8 +3297,11 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, case 0x0a: /* SPCR1 */ s->spcr[0] &= 0x0006; s->spcr[0] |= 0xf8f9 & value; - if (value & (1 << 15)) /* DLB */ - printf("%s: Digital Loopback mode enable attempt\n", __func__); + if (value & (1 << 15)) { /* DLB */ + qemu_log_mask(LOG_UNIMP, + "%s: Digital Loopback mode enable attempt\n", + __func__); + } if (~value & 1) { /* RRST */ s->spcr[0] &= ~6; s->rx_req = 0; @@ -3325,13 +3332,19 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, return; case 0x18: /* MCR2 */ s->mcr[1] = value & 0x03e3; - if (value & 3) /* XMCM */ - printf("%s: Tx channel selection mode enable attempt\n", __func__); + if (value & 3) { /* XMCM */ + qemu_log_mask(LOG_UNIMP, + "%s: Tx channel selection mode enable attempt\n", + __func__); + } return; case 0x1a: /* MCR1 */ s->mcr[0] = value & 0x03e1; - if (value & 1) /* RMCM */ - printf("%s: Rx channel selection mode enable attempt\n", __func__); + if (value & 1) { /* RMCM */ + qemu_log_mask(LOG_UNIMP, + "%s: Rx channel selection mode enable attempt\n", + __func__); + } return; case 0x1c: /* RCERA */ s->rcer[0] = value & 0xffff; @@ -3412,8 +3425,9 @@ static void omap_mcbsp_writew(void *opaque, hwaddr addr, } if (s->tx_req < 4) omap_mcbsp_tx_done(s); - } else - printf("%s: Tx FIFO overrun\n", __func__); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO overrun\n", __func__); + } return; } From 4af3c6eca90f9e3809e9c8405727c6343c0b3819 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Feb 2025 17:01:14 +0000 Subject: [PATCH 2131/2892] hw/arm/omap1: Drop ALMDEBUG ifdeffed out code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In omap1.c, there are some debug printfs in the omap_rtc_write() function that are guardad by ifdef ALMDEBUG. ALMDEBUG is never set, so this is all dead code. It's not worth the effort of converting all of these to tracepoints; a modern tracepoint approach would probably have a single tracepoint covering all the register writes anyway. Just delete the printf()s. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250227170117.1726895-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 51 -------------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 3c0ce5e097..8f5bb81c96 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -2660,25 +2660,16 @@ static void omap_rtc_write(void *opaque, hwaddr addr, switch (offset) { case 0x00: /* SECONDS_REG */ -#ifdef ALMDEBUG - printf("RTC SEC_REG <-- %02x\n", value); -#endif s->ti -= s->current_tm.tm_sec; s->ti += from_bcd(value); return; case 0x04: /* MINUTES_REG */ -#ifdef ALMDEBUG - printf("RTC MIN_REG <-- %02x\n", value); -#endif s->ti -= s->current_tm.tm_min * 60; s->ti += from_bcd(value) * 60; return; case 0x08: /* HOURS_REG */ -#ifdef ALMDEBUG - printf("RTC HRS_REG <-- %02x\n", value); -#endif s->ti -= s->current_tm.tm_hour * 3600; if (s->pm_am) { s->ti += (from_bcd(value & 0x3f) & 12) * 3600; @@ -2688,17 +2679,11 @@ static void omap_rtc_write(void *opaque, hwaddr addr, return; case 0x0c: /* DAYS_REG */ -#ifdef ALMDEBUG - printf("RTC DAY_REG <-- %02x\n", value); -#endif s->ti -= s->current_tm.tm_mday * 86400; s->ti += from_bcd(value) * 86400; return; case 0x10: /* MONTHS_REG */ -#ifdef ALMDEBUG - printf("RTC MTH_REG <-- %02x\n", value); -#endif memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); new_tm.tm_mon = from_bcd(value); ti[0] = mktimegm(&s->current_tm); @@ -2715,9 +2700,6 @@ static void omap_rtc_write(void *opaque, hwaddr addr, return; case 0x14: /* YEARS_REG */ -#ifdef ALMDEBUG - printf("RTC YRS_REG <-- %02x\n", value); -#endif memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100); ti[0] = mktimegm(&s->current_tm); @@ -2737,25 +2719,16 @@ static void omap_rtc_write(void *opaque, hwaddr addr, return; /* Ignored */ case 0x20: /* ALARM_SECONDS_REG */ -#ifdef ALMDEBUG - printf("ALM SEC_REG <-- %02x\n", value); -#endif s->alarm_tm.tm_sec = from_bcd(value); omap_rtc_alarm_update(s); return; case 0x24: /* ALARM_MINUTES_REG */ -#ifdef ALMDEBUG - printf("ALM MIN_REG <-- %02x\n", value); -#endif s->alarm_tm.tm_min = from_bcd(value); omap_rtc_alarm_update(s); return; case 0x28: /* ALARM_HOURS_REG */ -#ifdef ALMDEBUG - printf("ALM HRS_REG <-- %02x\n", value); -#endif if (s->pm_am) s->alarm_tm.tm_hour = ((from_bcd(value & 0x3f)) % 12) + @@ -2766,33 +2739,21 @@ static void omap_rtc_write(void *opaque, hwaddr addr, return; case 0x2c: /* ALARM_DAYS_REG */ -#ifdef ALMDEBUG - printf("ALM DAY_REG <-- %02x\n", value); -#endif s->alarm_tm.tm_mday = from_bcd(value); omap_rtc_alarm_update(s); return; case 0x30: /* ALARM_MONTHS_REG */ -#ifdef ALMDEBUG - printf("ALM MON_REG <-- %02x\n", value); -#endif s->alarm_tm.tm_mon = from_bcd(value); omap_rtc_alarm_update(s); return; case 0x34: /* ALARM_YEARS_REG */ -#ifdef ALMDEBUG - printf("ALM YRS_REG <-- %02x\n", value); -#endif s->alarm_tm.tm_year = from_bcd(value); omap_rtc_alarm_update(s); return; case 0x40: /* RTC_CTRL_REG */ -#ifdef ALMDEBUG - printf("RTC CONTROL <-- %02x\n", value); -#endif s->pm_am = (value >> 3) & 1; s->auto_comp = (value >> 2) & 1; s->round = (value >> 1) & 1; @@ -2802,32 +2763,20 @@ static void omap_rtc_write(void *opaque, hwaddr addr, return; case 0x44: /* RTC_STATUS_REG */ -#ifdef ALMDEBUG - printf("RTC STATUSL <-- %02x\n", value); -#endif s->status &= ~((value & 0xc0) ^ 0x80); omap_rtc_interrupts_update(s); return; case 0x48: /* RTC_INTERRUPTS_REG */ -#ifdef ALMDEBUG - printf("RTC INTRS <-- %02x\n", value); -#endif s->interrupts = value; return; case 0x4c: /* RTC_COMP_LSB_REG */ -#ifdef ALMDEBUG - printf("RTC COMPLSB <-- %02x\n", value); -#endif s->comp_reg &= 0xff00; s->comp_reg |= 0x00ff & value; return; case 0x50: /* RTC_COMP_MSB_REG */ -#ifdef ALMDEBUG - printf("RTC COMPMSB <-- %02x\n", value); -#endif s->comp_reg &= 0x00ff; s->comp_reg |= 0xff00 & (value << 8); return; From 92bf1c72e0f539172bd054f494c659fc51d055e0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Feb 2025 17:01:15 +0000 Subject: [PATCH 2132/2892] hw/arm/omap1: Convert information printfs to tracepoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The omap1 code uses raw printf() statements to print information about some events; convert these to tracepoints. In particular, this will stop the functional test for the sx1 from printing the not-very-helpful note "omap_clkm_write: clocking scheme set to synchronous scalable" to the test's default.log. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250227170117.1726895-4-peter.maydell@linaro.org> [PMD: Include component name (pwl/pwt/lpg) in trace events] Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 26 ++++++++++++++------------ hw/arm/trace-events | 7 +++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 8f5bb81c96..3ee10b4777 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -42,6 +42,7 @@ #include "qemu/cutils.h" #include "qemu/bcd.h" #include "target/arm/cpu-qom.h" +#include "trace.h" static inline void omap_log_badwidth(const char *funcname, hwaddr addr, int sz) { @@ -1731,7 +1732,7 @@ static void omap_clkm_write(void *opaque, hwaddr addr, case 0x18: /* ARM_SYSST */ if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) { s->clkm.clocking_scheme = (value >> 11) & 7; - printf("%s: clocking scheme set to %s\n", __func__, + trace_omap1_pwl_clocking_scheme( clkschemename[s->clkm.clocking_scheme]); } s->clkm.cold_start &= value & 0x3f; @@ -2335,7 +2336,7 @@ static void omap_pwl_update(struct omap_pwl_s *s) if (output != s->output) { s->output = output; - printf("%s: Backlight now at %i/256\n", __func__, output); + trace_omap1_pwl_backlight(output); } } @@ -2470,8 +2471,8 @@ static void omap_pwt_write(void *opaque, hwaddr addr, break; case 0x04: /* VRC */ if ((value ^ s->vrc) & 1) { - if (value & 1) - printf("%s: %iHz buzz on\n", __func__, (int) + if (value & 1) { + trace_omap1_pwt_buzz( /* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */ ((omap_clk_getrate(s->clk) >> 3) / /* Pre-multiplexer divider */ @@ -2487,8 +2488,9 @@ static void omap_pwt_write(void *opaque, hwaddr addr, /* 80/127 divider */ ((value & (1 << 5)) ? 80 : 127) / (107 * 55 * 63 * 127))); - else - printf("%s: silence!\n", __func__); + } else { + trace_omap1_pwt_silence(); + } } s->vrc = value & 0x7f; break; @@ -3494,7 +3496,7 @@ static void omap_lpg_tick(void *opaque) timer_mod(s->tm, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->on); s->cycle = !s->cycle; - printf("%s: LED is %s\n", __func__, s->cycle ? "on" : "off"); + trace_omap1_lpg_led(s->cycle ? "on" : "off"); } static void omap_lpg_update(struct omap_lpg_s *s) @@ -3514,11 +3516,11 @@ static void omap_lpg_update(struct omap_lpg_s *s) } timer_del(s->tm); - if (on == period && s->on < s->period) - printf("%s: LED is on\n", __func__); - else if (on == 0 && s->on) - printf("%s: LED is off\n", __func__); - else if (on && (on != s->on || period != s->period)) { + if (on == period && s->on < s->period) { + trace_omap1_lpg_led("on"); + } else if (on == 0 && s->on) { + trace_omap1_lpg_led("off"); + } else if (on && (on != s->on || period != s->period)) { s->cycle = 0; s->on = on; s->period = period; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 7790db780e..f49cae1656 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.rst for syntax documentation. +# omap1.c +omap1_pwl_clocking_scheme(const char *scheme) "omap1 CLKM: clocking scheme set to %s" +omap1_pwl_backlight(int output) "omap1 PWL: backlight now at %d/256" +omap1_pwt_buzz(int freq) "omap1 PWT: %dHz buzz on" +omap1_pwt_silence(void) "omap1 PWT: buzzer silenced" +omap1_lpg_led(const char *onoff) "omap1 LPG: LED is %s" + # virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." From a2e46dbe0a1e65ad7140120e3805cef9c99f5259 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Feb 2025 17:01:16 +0000 Subject: [PATCH 2133/2892] hw/arm/omap_sx1: Remove ifdeffed out debug printf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove an ifdeffed out debug printf from the static_write() function in omap_sx1.c. In theory we could turn this into a tracepoint, but for code this old it doesn't seem worthwhile. We can add tracepoints if and when we have a reason to debug something. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-ID: <20250227170117.1726895-5-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/omap_sx1.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index c6b0bed079..24b4043183 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -76,10 +76,6 @@ static uint64_t static_read(void *opaque, hwaddr offset, static void static_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { -#ifdef SPY - printf("%s: value %" PRIx64 " %u bytes written at 0x%x\n", - __func__, value, size, (int)offset); -#endif } static const MemoryRegionOps static_ops = { From 5ae3ca2d170ea022e8b1bf63598569aad511368f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Feb 2025 17:01:17 +0000 Subject: [PATCH 2134/2892] hw/arm/versatilepb: Convert printfs to LOG_GUEST_ERROR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert some printf() calls for attempts to access nonexistent registers into LOG_GUEST_ERROR logging. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250227170117.1726895-6-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/versatilepb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 941616cd25..35766445fa 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -27,6 +27,7 @@ #include "qom/object.h" #include "audio/audio.h" #include "target/arm/cpu-qom.h" +#include "qemu/log.h" #define VERSATILE_FLASH_ADDR 0x34000000 #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) @@ -110,7 +111,8 @@ static uint64_t vpb_sic_read(void *opaque, hwaddr offset, case 8: /* PICENABLE */ return s->pic_enable; default: - printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "vpb_sic_read: Bad register offset 0x%x\n", (int)offset); return 0; } } @@ -144,7 +146,8 @@ static void vpb_sic_write(void *opaque, hwaddr offset, vpb_sic_update_pic(s); break; default: - printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "vpb_sic_write: Bad register offset 0x%x\n", (int)offset); return; } vpb_sic_update(s); From f94a158c708666d97fe68bf39c8f8a32c0a5176d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 1 Mar 2025 15:35:33 +0100 Subject: [PATCH 2135/2892] hw/nvram/eeprom_at24c: Use OBJECT_DECLARE_SIMPLE_TYPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to open code it so use the simple object type declaration. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <08d9900af04789ede485942c8072eaa58bf52f80.1740839457.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/nvram/eeprom_at24c.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index a40cc5dd15..2ae03935d4 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -30,9 +30,7 @@ ## __VA_ARGS__) #define TYPE_AT24C_EE "at24c-eeprom" -typedef struct EEPROMState EEPROMState; -DECLARE_INSTANCE_CHECKER(EEPROMState, AT24C_EE, - TYPE_AT24C_EE) +OBJECT_DECLARE_SIMPLE_TYPE(EEPROMState, AT24C_EE) struct EEPROMState { I2CSlave parent_obj; From bf042a6a2ab9aceae951f6afc897986f7387a7e5 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 1 Mar 2025 15:35:34 +0100 Subject: [PATCH 2136/2892] hw/nvram/eeprom_at24c: Remove ERR macro that calls fprintf to stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the realize method error_setg can be used like other places there already do. The other usage can be replaced with error_report which is the preferred way instead of directly printing to stderr. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <637b92984795a385b648a84208f093947cc261e4.1740839457.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/nvram/eeprom_at24c.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 2ae03935d4..9f606842eb 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/module.h" #include "hw/i2c/i2c.h" #include "hw/nvram/eeprom_at24c.h" @@ -26,9 +27,6 @@ #define DPRINTK(FMT, ...) do {} while (0) #endif -#define ERR(FMT, ...) fprintf(stderr, TYPE_AT24C_EE " : " FMT, \ - ## __VA_ARGS__) - #define TYPE_AT24C_EE "at24c-eeprom" OBJECT_DECLARE_SIMPLE_TYPE(EEPROMState, AT24C_EE) @@ -75,8 +73,7 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) if (ee->blk && ee->changed) { int ret = blk_pwrite(ee->blk, 0, ee->rsize, ee->mem, 0); if (ret < 0) { - ERR(TYPE_AT24C_EE - " : failed to write backing file\n"); + error_report("%s: failed to write backing file", __func__); } DPRINTK("Wrote to backing file\n"); } @@ -203,8 +200,9 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) int ret = blk_pread(ee->blk, 0, ee->rsize, ee->mem, 0); if (ret < 0) { - ERR(TYPE_AT24C_EE - " : Failed initial sync with backing file\n"); + error_setg(errp, "%s: Failed initial sync with backing file", + TYPE_AT24C_EE); + return; } DPRINTK("Reset read backing file\n"); } From 902cc3c59eb64bfff95a32de11151209779d118e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 1 Mar 2025 15:35:35 +0100 Subject: [PATCH 2137/2892] hw/nvram/eeprom_at24c: Remove memset after g_malloc0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling memset to zero memory is not needed after g_malloc0 which already clears memory. These used to be in separate functions but after some patches the memset ended up after g_malloc0 and thus can be dropped. Fixes: 4f2c6448c3 (hw/nvram/eeprom_at24c: Make reset behavior more like hardware) Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: Signed-off-by: Philippe Mathieu-Daudé --- hw/nvram/eeprom_at24c.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 9f606842eb..78c81bea77 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -190,7 +190,6 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) } ee->mem = g_malloc0(ee->rsize); - memset(ee->mem, 0, ee->rsize); if (ee->init_rom) { memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize)); From 15571873d76b8fdd6cafb1d268415a1327ae3a6f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 1 Mar 2025 15:35:36 +0100 Subject: [PATCH 2138/2892] hw/nvram/eeprom_at24c: Reorganise init to avoid overwriting values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The init_rom[] can write values to the beginning of the memory but these are overwritten by values from a backing file that covers the whole memory. init_rom[] is used only if there's no backing file (provides default content) but should not overwrite backing file content (especially leaving the file unchanged and only change it in memory). Do the init_rom[] handling only if it would not be overwritten. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: Signed-off-by: Philippe Mathieu-Daudé --- hw/nvram/eeprom_at24c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 78c81bea77..ff7a21eee7 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -191,10 +191,6 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) ee->mem = g_malloc0(ee->rsize); - if (ee->init_rom) { - memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize)); - } - if (ee->blk) { int ret = blk_pread(ee->blk, 0, ee->rsize, ee->mem, 0); @@ -204,6 +200,8 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) return; } DPRINTK("Reset read backing file\n"); + } else if (ee->init_rom) { + memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize)); } /* From 9d71149a64f0ab051575a3f534e80918a3ca8610 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 20 Jan 2025 10:49:19 +0800 Subject: [PATCH 2139/2892] hw/intc/loongarch_ipi: Add basic hotplug framework LoongArch ipi can send interrupt to multiple CPUs, interrupt routing to CPU comes from destination physical cpu id. Here hotplug interface is added for IPI object, so that parent irq line can be connected, and routing table can be added for new created cpu. Here only basic hotplug framework is added, it is stub function. Signed-off-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 5376f1e084..90bbb7ac6e 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/boards.h" #include "qapi/error.h" #include "hw/intc/loongarch_ipi.h" @@ -76,9 +77,34 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) } } +static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } +} + +static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } +} + static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -86,6 +112,8 @@ static void loongarch_ipi_class_init(ObjectClass *klass, void *data) &lic->parent_realize); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; + hc->plug = loongarch_ipi_cpu_plug; + hc->unplug = loongarch_ipi_cpu_unplug; } static const TypeInfo loongarch_ipi_types[] = { @@ -95,6 +123,10 @@ static const TypeInfo loongarch_ipi_types[] = { .instance_size = sizeof(LoongarchIPIState), .class_size = sizeof(LoongarchIPIClass), .class_init = loongarch_ipi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, } }; From 54492213e6a2be203afff03098a188a0a2413fc9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 20 Jan 2025 12:02:30 +0800 Subject: [PATCH 2140/2892] hw/intc/loongarch_ipi: Implment cpu hotplug interface Add logic cpu allocation and cpu mapping with cpu hotplug interface. When cpu is added, connect ipi gpio irq to CPU IRQ_IPI irq pin. Signed-off-by: Bibo Mao --- hw/intc/loongarch_ipi.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 90bbb7ac6e..b10641dd03 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -49,6 +49,22 @@ static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics, return MEMTX_ERROR; } +static IPICore *loongarch_ipi_get_cpu(LoongsonIPICommonState *lics, + DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < lics->num_cpu; i++) { + if (lics->cpu[i].arch_id == arch_id) { + return &lics->cpu[i]; + } + } + + return NULL; +} + static void loongarch_ipi_realize(DeviceState *dev, Error **errp) { LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev); @@ -80,25 +96,48 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); Object *obj = OBJECT(dev); + IPICore *core; + int index; if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { warn_report("LoongArch extioi: Invalid %s device type", object_get_typename(obj)); return; } + + core = loongarch_ipi_get_cpu(lics, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - lics->cpu; + + /* connect ipi irq to cpu irq */ + qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI)); } static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); Object *obj = OBJECT(dev); + IPICore *core; if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { warn_report("LoongArch extioi: Invalid %s device type", object_get_typename(obj)); return; } + + core = loongarch_ipi_get_cpu(lics, dev); + if (!core) { + return; + } + + core->cpu = NULL; } static void loongarch_ipi_class_init(ObjectClass *klass, void *data) From 50ebc3fc47f7e4133e0d5e2674a1db17ddd5e815 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 10 Feb 2025 10:19:49 +0800 Subject: [PATCH 2141/2892] hw/intc/loongarch_ipi: Notify ipi object when cpu is plugged Use hotplug_handler_plug() to nofity ipi object when cold-plug cpu is created, so that ipi can set and configure irq routing to new cpu. Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 59533b058b..0629347da1 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -317,6 +317,7 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus; CPUState *cs; + Error *err = NULL; /* cpu nodes */ possible_cpus = mc->possible_cpu_arch_ids(ms); @@ -326,9 +327,7 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) continue; } - /* connect ipi irq to cpu irq */ - qdev_connect_gpio_out(lvms->ipi, num, - qdev_get_gpio_in(DEVICE(cs), IRQ_IPI)); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), &err); /* * connect ext irq to the cpu irq From 8b4b668f6a3661885fcabcedcf812930d5577f7e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 10 Jan 2025 16:29:47 +0800 Subject: [PATCH 2142/2892] hw/intc/loongarch_extioi: Move gpio irq initial to common code When cpu is added, it will connect gpio irq line to cpu irq. And cpu hot-add is put in common code, move gpio irq initial part into common code. Signed-off-by: Bibo Mao --- hw/intc/loongarch_extioi.c | 8 +------- hw/intc/loongarch_extioi_common.c | 6 +++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index f3055ec4d2..a51a215e6e 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -343,7 +343,7 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; - int i, pin; + int i; lec->parent_realize(dev, &local_err); if (local_err) { @@ -368,12 +368,6 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) } else { s->status |= BIT(EXTIOI_ENABLE); } - - for (i = 0; i < s->num_cpu; i++) { - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); - } - } } static void loongarch_extioi_unrealize(DeviceState *dev) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index fd56253d10..e3a38b318a 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -16,7 +16,7 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) MachineState *machine = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *id_list; - int i; + int i, pin; assert(mc->possible_cpu_arch_ids); id_list = mc->possible_cpu_arch_ids(machine); @@ -30,6 +30,10 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { s->cpu[i].arch_id = id_list->cpus[i].arch_id; s->cpu[i].cpu = CPU(id_list->cpus[i].cpu); + + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); + } } } From e45c96b7d62513327324e801c325b5b6530f8e4a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 10 Jan 2025 15:35:24 +0800 Subject: [PATCH 2143/2892] hw/intc/loongarch_extioi: Add basic hotplug framework LoongArch extioi interrupt controller routes peripheral interrupt to multiple CPUs, physical cpu id is used in interrupt routing table. Here hotplug interface is added for extioi object, so that parent irq line can be connected, and routing table can be added for new created cpu. Here only basic hotplug framework is added, it is stub function. Signed-off-by: Bibo Mao --- hw/intc/loongarch_extioi_common.c | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index e3a38b318a..19e19a9f73 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -4,11 +4,37 @@ * Copyright (C) 2024 Loongson Technology Corporation Limited */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" #include "hw/qdev-properties.h" #include "hw/intc/loongarch_extioi_common.h" #include "migration/vmstate.h" +#include "target/loongarch/cpu.h" + +static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } +} + +static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } +} static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) { @@ -107,11 +133,14 @@ static void loongarch_extioi_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); device_class_set_parent_realize(dc, loongarch_extioi_common_realize, &lecc->parent_realize); device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_loongarch_extioi; + hc->plug = loongarch_extioi_cpu_plug; + hc->unplug = loongarch_extioi_cpu_unplug; } static const TypeInfo loongarch_extioi_common_types[] = { @@ -121,6 +150,10 @@ static const TypeInfo loongarch_extioi_common_types[] = { .instance_size = sizeof(LoongArchExtIOICommonState), .class_size = sizeof(LoongArchExtIOICommonClass), .class_init = loongarch_extioi_common_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, .abstract = true, } }; From 8e63a7a7c2227ed014d55717c3a4de90bff426a8 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 10 Jan 2025 16:38:42 +0800 Subject: [PATCH 2144/2892] hw/intc/loongarch_extioi: Implment cpu hotplug interface When cpu is added, connect extioi gpio irq to CPU irq pin. Signed-off-by: Bibo Mao --- hw/intc/loongarch_extioi_common.c | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 19e19a9f73..ff3974f2a1 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -12,28 +12,73 @@ #include "migration/vmstate.h" #include "target/loongarch/cpu.h" +static ExtIOICore *loongarch_extioi_get_cpu(LoongArchExtIOICommonState *s, + DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].arch_id == arch_id) { + return &s->cpu[i]; + } + } + + return NULL; +} + static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); Object *obj = OBJECT(dev); + ExtIOICore *core; + int pin, index; if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { warn_report("LoongArch extioi: Invalid %s device type", object_get_typename(obj)); return; } + + core = loongarch_extioi_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - s->cpu; + + /* + * connect extioi irq to the cpu irq + * cpu_pin[LS3A_INTC_IP + 2 : 2] <= intc_pin[LS3A_INTC_IP : 0] + */ + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_connect_gpio_out(DEVICE(s), index * LS3A_INTC_IP + pin, + qdev_get_gpio_in(dev, pin + 2)); + } } static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); Object *obj = OBJECT(dev); + ExtIOICore *core; if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { warn_report("LoongArch extioi: Invalid %s device type", object_get_typename(obj)); return; } + + core = loongarch_extioi_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = NULL; } static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) From 087a23a87c57725f8653aea9be70a2d55cf0309e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 10 Feb 2025 10:45:13 +0800 Subject: [PATCH 2145/2892] hw/intc/loongarch_extioi: Use cpu plug notification Use hotplug_handler_plug() to nofity extioi object when cold-plug cpu is created, so that extioi can set and configure irq routing to new cpu. Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 0629347da1..907f965c54 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -312,7 +312,7 @@ static void virt_devices_init(DeviceState *pch_pic, static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) { - int num, pin; + int num; MachineState *ms = MACHINE(lvms); MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus; @@ -328,15 +328,7 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) } hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), &err); - - /* - * connect ext irq to the cpu irq - * cpu_pin[9:2] <= intc_pin[7:0] - */ - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_connect_gpio_out(lvms->extioi, (num * LS3A_INTC_IP + pin), - qdev_get_gpio_in(DEVICE(cs), pin + 2)); - } + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), &err); } } From 8ccf28c2f6cf54c82485de099b4566b260893445 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 26 Nov 2024 16:41:42 +0800 Subject: [PATCH 2146/2892] hw/loongarch/virt: Add CPU topology support Add topological relationships for Loongarch VCPU and initialize topology member variables. On LoongArch system there is socket/core/thread topo information, physical CPU id is calculated from CPU topo, every topo sub-field is aligned by power of 2. So it is different from logical cpu index. Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 59 ++++++++++++++++++++++++++++++++++++------ target/loongarch/cpu.h | 6 +++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 907f965c54..2bbbbbfbcf 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -774,6 +774,48 @@ static void virt_initfn(Object *obj) virt_flash_create(lvms); } +static void virt_get_topo_from_index(MachineState *ms, + LoongArchCPUTopo *topo, int index) +{ + topo->socket_id = index / (ms->smp.cores * ms->smp.threads); + topo->core_id = index / ms->smp.threads % ms->smp.cores; + topo->thread_id = index % ms->smp.threads; +} + +static unsigned int topo_align_up(unsigned int count) +{ + g_assert(count >= 1); + count -= 1; + return BIT(count ? 32 - clz32(count) : 0); +} + +/* + * LoongArch Reference Manual Vol1, Chapter 7.4.12 CPU Identity + * For CPU architecture, bit0 .. bit8 is valid for CPU id, max cpuid is 512 + * However for IPI/Eiointc interrupt controller, max supported cpu id for + * irq routingis 256 + * + * Here max cpu id is 256 for virt machine + */ +static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) +{ + int arch_id, threads, cores, sockets; + + threads = topo_align_up(ms->smp.threads); + cores = topo_align_up(ms->smp.cores); + sockets = topo_align_up(ms->smp.sockets); + if ((threads * cores * sockets) > 256) { + error_report("Exceeding max cpuid 256 with sockets[%d] cores[%d]" + " threads[%d]", ms->smp.sockets, ms->smp.cores, + ms->smp.threads); + exit(1); + } + + arch_id = topo->thread_id + topo->core_id * threads; + arch_id += topo->socket_id * threads * cores; + return arch_id; +} + static bool memhp_type_supported(DeviceState *dev) { /* we only support pc dimm now */ @@ -873,8 +915,9 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) { - int n; + int n, arch_id; unsigned int max_cpus = ms->smp.max_cpus; + LoongArchCPUTopo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -885,17 +928,17 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { + virt_get_topo_from_index(ms, &topo, n); + arch_id = virt_get_arch_id_from_topo(ms, &topo); ms->possible_cpus->cpus[n].type = ms->cpu_type; - ms->possible_cpus->cpus[n].arch_id = n; - + ms->possible_cpus->cpus[n].arch_id = arch_id; + ms->possible_cpus->cpus[n].vcpus_count = 1; ms->possible_cpus->cpus[n].props.has_socket_id = true; - ms->possible_cpus->cpus[n].props.socket_id = - n / (ms->smp.cores * ms->smp.threads); + ms->possible_cpus->cpus[n].props.socket_id = topo.socket_id; ms->possible_cpus->cpus[n].props.has_core_id = true; - ms->possible_cpus->cpus[n].props.core_id = - n / ms->smp.threads % ms->smp.cores; + ms->possible_cpus->cpus[n].props.core_id = topo.core_id; ms->possible_cpus->cpus[n].props.has_thread_id = true; - ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads; + ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id; } return ms->possible_cpus; } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 83183a33ab..9dc71fa7f1 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -393,6 +393,12 @@ typedef struct CPUArchState { #endif } CPULoongArchState; +typedef struct LoongArchCPUTopo { + int32_t socket_id; /* socket-id of this VCPU */ + int32_t core_id; /* core-id of this VCPU */ + int32_t thread_id; /* thread-id of this VCPU */ +} LoongArchCPUTopo; + /** * LoongArchCPU: * @env: #CPULoongArchState From d32fde20bd334a9fa4efab95583bc596d01a39ea Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 5 Mar 2025 09:27:59 +0800 Subject: [PATCH 2147/2892] hw/loongarch/virt: Add topo properties on CPU object Add some properties such as socket_id, core_id, thread_id and node_id on LoongArch CPU object. Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- target/loongarch/cpu.c | 9 +++++++++ target/loongarch/cpu.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 3788f895c1..df76ab66d5 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -14,6 +14,7 @@ #include "system/tcg.h" #include "system/kvm.h" #include "kvm/kvm_loongarch.h" +#include "hw/qdev-properties.h" #include "exec/exec-all.h" #include "exec/translation-block.h" #include "cpu.h" @@ -879,6 +880,13 @@ static int64_t loongarch_cpu_get_arch_id(CPUState *cs) } #endif +static const Property loongarch_cpu_properties[] = { + DEFINE_PROP_INT32("socket-id", LoongArchCPU, socket_id, 0), + DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, 0), + DEFINE_PROP_INT32("thread-id", LoongArchCPU, thread_id, 0), + DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID), +}; + static void loongarch_cpu_class_init(ObjectClass *c, void *data) { LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); @@ -886,6 +894,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) DeviceClass *dc = DEVICE_CLASS(c); ResettableClass *rc = RESETTABLE_CLASS(c); + device_class_set_props(dc, loongarch_cpu_properties); device_class_set_parent_realize(dc, loongarch_cpu_realizefn, &lacc->parent_realize); resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9dc71fa7f1..677100bd42 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -417,6 +417,10 @@ struct ArchCPU { OnOffAuto lasx; OnOffAuto kvm_pv_ipi; OnOffAuto kvm_steal_time; + int32_t socket_id; /* socket-id of this CPU */ + int32_t core_id; /* core-id of this CPU */ + int32_t thread_id; /* thread-id of this CPU */ + int32_t node_id; /* NUMA node of this CPU */ /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; From 7bf633c53f66ea9b5666ed246de4122152910f54 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 10 Feb 2025 14:56:01 +0800 Subject: [PATCH 2148/2892] hw/loongarch/virt: Add basic cpu plug interface framework Add basic cpu hotplug interface framework, cpu hotplug interface is stub function and only framework is added here. Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 29 +++++++++++++++++++++++++++++ target/loongarch/cpu.c | 13 +++++++++++++ target/loongarch/cpu.h | 1 + 3 files changed, 43 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 2bbbbbfbcf..2b4b60f718 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -816,6 +816,26 @@ static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) return arch_id; } +static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ +} + +static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ +} + +static void virt_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ +} + +static void virt_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ +} + static bool memhp_type_supported(DeviceState *dev) { /* we only support pc dimm now */ @@ -834,6 +854,8 @@ static void virt_device_pre_plug(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_pre_plug(hotplug_dev, dev, errp); } } @@ -852,6 +874,8 @@ static void virt_device_unplug_request(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug_request(hotplug_dev, dev, errp); } } @@ -870,6 +894,8 @@ static void virt_device_unplug(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug(hotplug_dev, dev, errp); } } @@ -897,6 +923,8 @@ static void virt_device_plug_cb(HotplugHandler *hotplug_dev, } } else if (memhp_type_supported(dev)) { virt_mem_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_plug(hotplug_dev, dev, errp); } } @@ -906,6 +934,7 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || + object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || memhp_type_supported(dev)) { return HOTPLUG_HANDLER(machine); diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index df76ab66d5..8b99b8def4 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -647,6 +647,17 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) lacc->parent_realize(dev, errp); } +static void loongarch_cpu_unrealizefn(DeviceState *dev) +{ + LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); + +#ifndef CONFIG_USER_ONLY + cpu_remove_sync(CPU(dev)); +#endif + + lacc->parent_unrealize(dev); +} + static bool loongarch_get_lsx(Object *obj, Error **errp) { return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; @@ -897,6 +908,8 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) device_class_set_props(dc, loongarch_cpu_properties); device_class_set_parent_realize(dc, loongarch_cpu_realizefn, &lacc->parent_realize); + device_class_set_parent_unrealize(dc, loongarch_cpu_unrealizefn, + &lacc->parent_unrealize); resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, &lacc->parent_phases); diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 677100bd42..eae874c67b 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -439,6 +439,7 @@ struct LoongArchCPUClass { CPUClass parent_class; DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; ResettablePhases parent_phases; }; From 2cd6857f6f5b92ed6c0fa3404efaa843aaa6be0c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 27 Nov 2024 09:58:17 +0800 Subject: [PATCH 2149/2892] hw/loongarch/virt: Implement cpu unplug interface Implement cpu unplug interfaces including virt_cpu_unplug_request() and virt_cpu_unplug(). Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 2b4b60f718..297f71dc00 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -816,6 +816,19 @@ static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) return arch_id; } +/* Find cpu slot in machine->possible_cpus by arch_id */ +static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].arch_id == arch_id) { + return &ms->possible_cpus->cpus[n]; + } + } + + return NULL; +} + static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -824,11 +837,56 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + Error *err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + + if (cs->cpu_index == 0) { + error_setg(&err, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", + cs->cpu_index, cpu->socket_id, + cpu->core_id, cpu->thread_id); + error_propagate(errp, err); + return; + } + + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); + if (err) { + error_propagate(errp, err); + } } static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + CPUArchId *cpu_slot; + Error *err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + + /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Notify acpi ged CPU removed */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = NULL; + return; } static void virt_cpu_plug(HotplugHandler *hotplug_dev, From ab9935d2991e620482b10a261e264f0e6e16ed61 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 10 Feb 2025 15:21:39 +0800 Subject: [PATCH 2150/2892] hw/loongarch/virt: Implement cpu plug interface Implement cpu plug interface, and cold-plug cpu uses plug interface when cpu object is created. Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 88 ++++++++++++++++++++++++++++++++++++------ target/loongarch/cpu.c | 1 + 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 297f71dc00..7788efbe44 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -647,15 +647,13 @@ static void fw_cfg_add_memory(MachineState *ms) static void virt_init(MachineState *machine) { - LoongArchCPU *lacpu; const char *cpu_model = machine->cpu_type; MemoryRegion *address_space_mem = get_system_memory(); LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); int i; hwaddr base, size, ram_size = machine->ram_size; - const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); - CPUState *cpu; + Object *cpuobj; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -671,14 +669,15 @@ static void virt_init(MachineState *machine) memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem); /* Init CPUs */ - possible_cpus = mc->possible_cpu_arch_ids(machine); - for (i = 0; i < possible_cpus->len; i++) { - cpu = cpu_create(machine->cpu_type); - cpu->cpu_index = i; - machine->possible_cpus->cpus[i].cpu = cpu; - lacpu = LOONGARCH_CPU(cpu); - lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; - lacpu->env.address_space_iocsr = &lvms->as_iocsr; + mc->possible_cpu_arch_ids(machine); + for (i = 0; i < machine->smp.cpus; i++) { + cpuobj = object_new(machine->cpu_type); + if (cpuobj == NULL) { + error_report("Fail to create object with type %s ", + machine->cpu_type); + exit(EXIT_FAILURE); + } + qdev_realize_and_unref(DEVICE(cpuobj), NULL, &error_fatal); } fw_cfg_add_memory(machine); @@ -829,9 +828,52 @@ static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id) return NULL; } +/* Find cpu slot for cold-plut CPU object where cpu is NULL */ +static CPUArchId *virt_find_empty_cpu_slot(MachineState *ms) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].cpu == NULL) { + return &ms->possible_cpus->cpus[n]; + } + } + + return NULL; +} + static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + MachineState *ms = MACHINE(OBJECT(hotplug_dev)); + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + CPUArchId *cpu_slot; + Error *err = NULL; + LoongArchCPUTopo topo; + + if (lvms->acpi_ged) { + error_setg(&err, "CPU hotplug not supported"); + goto out; + } else { + /* For cold-add cpu, find empty cpu slot */ + cpu_slot = virt_find_empty_cpu_slot(ms); + topo.socket_id = cpu_slot->props.socket_id; + topo.core_id = cpu_slot->props.core_id; + topo.thread_id = cpu_slot->props.thread_id; + object_property_set_int(OBJECT(dev), "socket-id", topo.socket_id, NULL); + object_property_set_int(OBJECT(dev), "core-id", topo.core_id, NULL); + object_property_set_int(OBJECT(dev), "thread-id", topo.thread_id, NULL); + } + + cpu->env.address_space_iocsr = &lvms->as_iocsr; + cpu->phy_id = cpu_slot->arch_id; + cs->cpu_index = cpu_slot - ms->possible_cpus->cpus; + numa_cpu_pre_plug(cpu_slot, dev, &err); +out: + if (err) { + error_propagate(errp, err); + } } static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, @@ -892,6 +934,30 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + CPUArchId *cpu_slot; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + Error *err = NULL; + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = CPU(dev); + if (lvms->ipi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + if (lvms->extioi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + return; } static bool memhp_type_supported(DeviceState *dev) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 8b99b8def4..b2961d8605 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -932,6 +932,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) #ifdef CONFIG_TCG cc->tcg_ops = &loongarch_tcg_ops; #endif + dc->user_creatable = true; } static const gchar *loongarch32_gdb_arch_name(CPUState *cs) From 25cdac981fd012d9b96d8db9a809841c7e521154 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 10 Feb 2025 16:47:00 +0800 Subject: [PATCH 2151/2892] hw/loongarch/virt: Update the ACPI table for hotplug cpu On LoongArch virt machine, ACPI GED hardware is used for CPU hotplug handler, here CPU hotplug support feature is added based on GED handler, also CPU scan and reject method is added about CPU device in DSDT table. Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- hw/loongarch/Kconfig | 1 + hw/loongarch/virt-acpi-build.c | 35 +++++++++++++++++++++++++++++++--- hw/loongarch/virt.c | 10 ++++++++++ include/hw/loongarch/virt.h | 1 + 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index fe1c6feac1..bb2838b7b5 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -17,6 +17,7 @@ config LOONGARCH_VIRT select LOONGARCH_EXTIOI select LS7A_RTC select SMBIOS + select ACPI_CPU_HOTPLUG select ACPI_PCI select ACPI_HW_REDUCED select FW_CFG_DMA diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index 9ca88d63ae..fced6c445a 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -47,6 +47,22 @@ #define ACPI_BUILD_DPRINTF(fmt, ...) #endif +static void virt_madt_cpu_entry(int uid, + const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) +{ + uint32_t flags, apic_id = apic_ids->cpus[uid].arch_id; + + flags = apic_ids->cpus[uid].cpu || force_enabled ? 1 /* Enabled */ : 0; + + /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ + build_append_int_noprefix(entry, 0, 1); /* Type */ + build_append_int_noprefix(entry, 8, 1); /* Length */ + build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ + build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ + build_append_int_noprefix(entry, flags, 4); /* Flags */ +} + /* build FADT */ static void init_common_fadt_data(AcpiFadtData *data) { @@ -112,7 +128,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, MachineState *ms = MACHINE(lvms); MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); - int i, arch_id; + int i, arch_id, flags; AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, .oem_table_id = lvms->oem_table_id }; @@ -125,13 +141,13 @@ build_madt(GArray *table_data, BIOSLinker *linker, for (i = 0; i < arch_ids->len; i++) { /* Processor Core Interrupt Controller Structure */ arch_id = arch_ids->cpus[i].arch_id; - + flags = arch_ids->cpus[i].cpu ? 1 : 0; build_append_int_noprefix(table_data, 17, 1); /* Type */ build_append_int_noprefix(table_data, 15, 1); /* Length */ build_append_int_noprefix(table_data, 1, 1); /* Version */ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ - build_append_int_noprefix(table_data, 1, 4); /* Flags */ + build_append_int_noprefix(table_data, flags, 4); /* Flags */ } /* Extend I/O Interrupt Controller Structure */ @@ -338,6 +354,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) { uint32_t event; LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + CPUHotplugFeatures opts; build_ged_aml(dsdt, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(lvms->acpi_ged), @@ -350,6 +367,18 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) AML_SYSTEM_MEMORY, VIRT_GED_MEM_ADDR); } + + if (event & ACPI_GED_CPU_HOTPLUG_EVT) { + opts.acpi_1_compatible = false; + opts.has_legacy_cphp = false; + opts.fw_unplugs_cpu = false; + opts.smi_path = NULL; + + build_cpus_aml(dsdt, machine, opts, virt_madt_cpu_entry, + VIRT_GED_CPUHP_ADDR, "\\_SB", + AML_GED_EVT_CPU_SCAN_METHOD, AML_SYSTEM_MEMORY); + } + acpi_dsdt_add_power_button(dsdt); } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 7788efbe44..8aba145566 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -187,11 +187,17 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, { DeviceState *dev; MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(lvms); uint32_t event = ACPI_GED_PWR_DOWN_EVT; if (ms->ram_slots) { event |= ACPI_GED_MEM_HOTPLUG_EVT; } + + if (mc->has_hotpluggable_cpus) { + event |= ACPI_GED_CPU_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -203,6 +209,10 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, /* ged regs used for reset and power down */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + if (mc->has_hotpluggable_cpus) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, VIRT_GED_CPUHP_ADDR); + } + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); return dev; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 2e7cdfaef0..2b7d19953f 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -30,6 +30,7 @@ #define VIRT_GED_EVT_ADDR 0x100e0000 #define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) #define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) +#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT) #define COMMAND_LINE_SIZE 512 From a97cceb1d7ed05070dc52e7850112f906d7e3512 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 10 Feb 2025 17:03:29 +0800 Subject: [PATCH 2152/2892] hw/loongarch/virt: Enable cpu hotplug feature on virt machine On virt machine, enable CPU hotplug feature has_hotpluggable_cpus. For hot-added CPUs, there is socket-id/core-id/thread-id property set, arch_id can be caculated from these properties. So that cpu slot can be searched from its arch_id. Co-developed-by: Xianglai Li Signed-off-by: Bibo Mao --- hw/loongarch/virt.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 8aba145566..a5840ff968 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -861,10 +861,42 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, CPUArchId *cpu_slot; Error *err = NULL; LoongArchCPUTopo topo; + int arch_id; if (lvms->acpi_ged) { - error_setg(&err, "CPU hotplug not supported"); - goto out; + if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { + error_setg(&err, + "Invalid thread-id %u specified, must be in range 1:%u", + cpu->thread_id, ms->smp.threads - 1); + goto out; + } + + if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { + error_setg(&err, + "Invalid core-id %u specified, must be in range 1:%u", + cpu->core_id, ms->smp.cores - 1); + goto out; + } + + if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { + error_setg(&err, + "Invalid socket-id %u specified, must be in range 1:%u", + cpu->socket_id, ms->smp.sockets - 1); + goto out; + } + + topo.socket_id = cpu->socket_id; + topo.core_id = cpu->core_id; + topo.thread_id = cpu->thread_id; + arch_id = virt_get_arch_id_from_topo(ms, &topo); + cpu_slot = virt_find_cpu_slot(ms, arch_id); + if (CPU(cpu_slot->cpu)) { + error_setg(&err, + "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", + cs->cpu_index, cpu->socket_id, cpu->core_id, + cpu->thread_id, cpu_slot->arch_id); + goto out; + } } else { /* For cold-add cpu, find empty cpu slot */ cpu_slot = virt_find_empty_cpu_slot(ms); @@ -967,6 +999,13 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, } } + if (lvms->acpi_ged) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); + if (err) { + error_propagate(errp, err); + } + } + return; } @@ -1149,6 +1188,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->numa_mem_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; + mc->has_hotpluggable_cpus = true; mc->get_hotplug_handler = virt_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; hc->plug = virt_device_plug_cb; From 0a629c827300d514cc1f61806414d214fcf75051 Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Mon, 3 Mar 2025 14:31:33 +0800 Subject: [PATCH 2153/2892] target/loongarch: Adjust the cpu reset action to a proper position The commit 5a99a10da6cf ("target/loongarch: fix vcpu reset command word issue") fixes the error in the cpu reset ioctl command word delivery process, so that the command word can be delivered correctly, and adds the judgment and processing of the error return value, which exposes another problem that under loongarch, the cpu reset action is earlier than the creation of vcpu. An error occurs when the cpu reset command is sent. Now adjust the order of cpu reset and vcpu create actions to fix this problem Signed-off-by: Xianglai Li Acked-by: Igor Mammedov Signed-off-by: Bibo Mao --- target/loongarch/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index b2961d8605..ac514a15fb 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -641,8 +641,8 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) loongarch_cpu_register_gdb_regs_for_features(cs); - cpu_reset(cs); qemu_init_vcpu(cs); + cpu_reset(cs); lacc->parent_realize(dev, errp); } From e4d6c94e674a3162857dbd78c27b1d7a1b2c250f Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Wed, 26 Feb 2025 08:59:07 +0100 Subject: [PATCH 2154/2892] ui/console-vc: introduce parsing of the 'ESC ( ' sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change introduces parsing of the 'ESC ( ' sequence, which is supposed to change character set [1]. In the QEMU case, the introduced parsing logic does not actually change the character set, but simply parses the sequence and does not let output of a tool to be corrupted with leftovers: `top` sends 'ESC ( B', so if character sequence is not parsed correctly, chracter 'B' appears in the output: Btop - 11:08:42 up 5 min, 1 user, load average: 0BB Tasks:B 158 Btotal,B 1 Brunning,B 157 Bsleeping,B 0 BsBB %Cpu(s):B 0.0 Bus,B 0.0 Bsy,B 0.0 Bni,B 99.8 Bid,B 0.2 BB MiB Mem :B 7955.6 Btotal,B 7778.6 Bfree,B 79.6 BB MiB Swap:B 0.0 Btotal,B 0.0 Bfree,B 0.0 BB PID USER PR NI VIRT RES SHR S B B 735 root 20 0 9328 3540 3152 R B B 1 root 20 0 20084 10904 8404 S B B 2 root 20 0 0 0 0 S B [1] https://vt100.net/docs/vt100-ug/chapter3.html#SCS Signed-off-by: Roman Penyaev Cc: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Reviewed-by: Marc-André Lureau Message-ID: <20250226075913.353676-2-r.peniaev@gmail.com> --- ui/console-vc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui/console-vc.c b/ui/console-vc.c index fe20579832..90ff0ffda8 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -42,6 +42,8 @@ enum TTYState { TTY_STATE_NORM, TTY_STATE_ESC, TTY_STATE_CSI, + TTY_STATE_G0, + TTY_STATE_G1, }; typedef struct QemuTextConsole { @@ -694,6 +696,10 @@ static void vc_putchar(VCChardev *vc, int ch) vc->esc_params[i] = 0; vc->nb_esc_params = 0; vc->state = TTY_STATE_CSI; + } else if (ch == '(') { + vc->state = TTY_STATE_G0; + } else if (ch == ')') { + vc->state = TTY_STATE_G1; } else { vc->state = TTY_STATE_NORM; } @@ -844,6 +850,16 @@ static void vc_putchar(VCChardev *vc, int ch) } break; } + break; + case TTY_STATE_G0: /* set character sets */ + case TTY_STATE_G1: /* set character sets */ + switch (ch) { + case 'B': + /* Latin-1 map */ + break; + } + vc->state = TTY_STATE_NORM; + break; } } From 0a9f48e9ead2b067e8d7058e7bc7a1d68721882d Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Wed, 26 Feb 2025 08:59:08 +0100 Subject: [PATCH 2155/2892] ui/console-vc: report to the application instead of screen rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Terminal Device Status Report (DSR) [1] should be sent to an application, not rendered to the screen. This patch fixes rendering of terminal report, which appear only on the graphical screen of the terminal (console "vc") and can be reproduced by the following command: echo -en '\e[6n'; IFS='[;' read -sdR _ row col; echo $row:$col Command requests cursor position and waits for terminal response, but instead, the response is rendered to the graphical screen and never sent to an application. Why bother? Busybox shell (ash) in Alpine distribution requests cursor position on each shell prompt (once is pressed), which makes a prompt on a graphical screen corrupted with repeating Cursor Position Report (CPR) [2]: [root@alpine ~]# \033[57;1R] Which is very annoying and incorrect. [1] https://vt100.net/docs/vt100-ug/chapter3.html#DSR [2] https://vt100.net/docs/vt100-ug/chapter3.html#CPR Signed-off-by: Roman Penyaev Cc: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Reviewed-by: Marc-André Lureau Message-ID: <20250226075913.353676-3-r.peniaev@gmail.com> --- ui/console-vc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ui/console-vc.c b/ui/console-vc.c index 90ff0ffda8..d512f57e10 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -617,10 +617,9 @@ static void vc_put_one(VCChardev *vc, int ch) static void vc_respond_str(VCChardev *vc, const char *buf) { - while (*buf) { - vc_put_one(vc, *buf); - buf++; - } + QemuTextConsole *s = vc->console; + + qemu_chr_be_write(s->chr, (const uint8_t *)buf, strlen(buf)); } /* set cursor, checking bounds */ From 40339871da115b68e01f1da9ce2f8175e8f65d3c Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Wed, 26 Feb 2025 08:59:09 +0100 Subject: [PATCH 2156/2892] ui/console-vc: report cursor position in the screen not in the scroll buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The format of the CSI cursor position report is `ESC[row;columnR`, where `row` is a row of a cursor in the screen, not in the scrollback buffer. What's the difference? Let's say the terminal screen has 24 lines, no matter how long the scrollback buffer may be, the last line is the 24th. For example the following command can be executed in xterm on the last screen line: $ echo -en '\e[6n'; IFS='[;' read -sdR _ row col; echo $row:$col 24:1 It shows the cursor position on the current screen and not relative to the backscroll buffer. Before this change the row number was always increasing for the QEMU VC and represents the cursor position relative to the backscroll buffer. Signed-off-by: Roman Penyaev Cc: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Reviewed-by: Marc-André Lureau Message-ID: <20250226075913.353676-4-r.peniaev@gmail.com> --- ui/console-vc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/console-vc.c b/ui/console-vc.c index d512f57e10..87f57f1c52 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -827,8 +827,7 @@ static void vc_putchar(VCChardev *vc, int ch) case 6: /* report cursor position */ response = g_strdup_printf("\033[%d;%dR", - (s->y_base + s->y) % s->total_height + 1, - s->x + 1); + s->y + 1, s->x + 1); vc_respond_str(vc, response); break; } From 1a0fd7838a9dddf91241bc9faa471dc9dec04329 Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Wed, 26 Feb 2025 08:59:10 +0100 Subject: [PATCH 2157/2892] ui/console-vc: add support for cursor DECSC and DECRC commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are aliases for save and restore cursor commands: * save cursor `ESC 7` (DEC Save Cursor [1], older VT100) `ESC [ s` (CSI Save Cursor, standard ANSI) * load cursor `ESC 8` (DEC Restore Cursor [2], older VT100) `ESC [ u` (CSI Restore Cursor, standard ANSI) This change introduces older DEC sequencies for compatibility with some scripts (for example [3]) and tools. This change also adds saving and restoring of character attributes, which is according to the VT spec [1][2] [1] https://vt100.net/docs/vt510-rm/DECSC.html [2] https://vt100.net/docs/vt510-rm/DECRC.html [3] https://wiki.archlinux.org/title/Working_with_the_serial_console#Resizing_a_terminal Signed-off-by: Roman Penyaev Cc: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Reviewed-by: Marc-André Lureau Message-ID: <20250226075913.353676-5-r.peniaev@gmail.com> --- ui/console-vc.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/ui/console-vc.c b/ui/console-vc.c index 87f57f1c52..522adc2806 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -90,6 +90,7 @@ struct VCChardev { int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; TextAttributes t_attrib; /* currently active text attributes */ + TextAttributes t_attrib_saved; int x_saved, y_saved; }; typedef struct VCChardev VCChardev; @@ -644,6 +645,31 @@ static void vc_set_cursor(VCChardev *vc, int x, int y) s->y = y; } +/** + * vc_save_cursor() - saves cursor position and character attributes. + */ +static void vc_save_cursor(VCChardev *vc) +{ + QemuTextConsole *s = vc->console; + + vc->x_saved = s->x; + vc->y_saved = s->y; + vc->t_attrib_saved = vc->t_attrib; +} + +/** + * vc_restore_cursor() - restores cursor position and character + * attributes from saved state. + */ +static void vc_restore_cursor(VCChardev *vc) +{ + QemuTextConsole *s = vc->console; + + s->x = vc->x_saved; + s->y = vc->y_saved; + vc->t_attrib = vc->t_attrib_saved; +} + static void vc_putchar(VCChardev *vc, int ch) { QemuTextConsole *s = vc->console; @@ -699,6 +725,12 @@ static void vc_putchar(VCChardev *vc, int ch) vc->state = TTY_STATE_G0; } else if (ch == ')') { vc->state = TTY_STATE_G1; + } else if (ch == '7') { + vc_save_cursor(vc); + vc->state = TTY_STATE_NORM; + } else if (ch == '8') { + vc_restore_cursor(vc); + vc->state = TTY_STATE_NORM; } else { vc->state = TTY_STATE_NORM; } @@ -833,14 +865,10 @@ static void vc_putchar(VCChardev *vc, int ch) } break; case 's': - /* save cursor position */ - vc->x_saved = s->x; - vc->y_saved = s->y; + vc_save_cursor(vc); break; case 'u': - /* restore cursor position */ - s->x = vc->x_saved; - s->y = vc->y_saved; + vc_restore_cursor(vc); break; default: trace_console_putchar_unhandled(ch); From a97ef3624437c5a5fbc8bd45e2a206d10ca840be Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Wed, 26 Feb 2025 08:59:11 +0100 Subject: [PATCH 2158/2892] ui/console-vc: implement DCH (delete) and ICH (insert) commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements DCH (delete character) and ICH (insert character) commands. DCH - Delete Character: "As characters are deleted, the remaining characters between the cursor and right margin move to the left. Character attributes move with the characters. The terminal adds blank spaces with no visual character attributes at the right margin. DCH has no effect outside the scrolling margins" [1]. ICH - Insert Character: "The ICH sequence inserts Pn blank characters with the normal character attribute. The cursor remains at the beginning of the blank characters. Text between the cursor and right margin moves to the right. Characters scrolled past the right margin are lost. ICH has no effect outside the scrolling margins" [2]. Without these commands console is barely usable. [1] https://vt100.net/docs/vt510-rm/DCH.html [1] https://vt100.net/docs/vt510-rm/ICH.html Signed-off-by: Roman Penyaev Cc: "Marc-André Lureau" Cc: qemu-devel@nongnu.org Reviewed-by: Marc-André Lureau Message-ID: <20250226075913.353676-6-r.peniaev@gmail.com> --- ui/console-vc.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/ui/console-vc.c b/ui/console-vc.c index 522adc2806..df1341513d 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -645,6 +645,88 @@ static void vc_set_cursor(VCChardev *vc, int x, int y) s->y = y; } +/** + * vc_csi_P() - (DCH) deletes one or more characters from the cursor + * position to the right. As characters are deleted, the remaining + * characters between the cursor and right margin move to the + * left. Character attributes move with the characters. + */ +static void vc_csi_P(struct VCChardev *vc, unsigned int nr) +{ + QemuTextConsole *s = vc->console; + TextCell *c1, *c2; + unsigned int x1, x2, y; + unsigned int end, len; + + if (!nr) { + nr = 1; + } + if (nr > s->width - s->x) { + nr = s->width - s->x; + if (!nr) { + return; + } + } + + x1 = s->x; + x2 = s->x + nr; + len = s->width - x2; + if (len) { + y = (s->y_base + s->y) % s->total_height; + c1 = &s->cells[y * s->width + x1]; + c2 = &s->cells[y * s->width + x2]; + memmove(c1, c2, len * sizeof(*c1)); + for (end = x1 + len; x1 < end; x1++) { + vc_update_xy(vc, x1, s->y); + } + } + /* Clear the rest */ + for (; x1 < s->width; x1++) { + vc_clear_xy(vc, x1, s->y); + } +} + +/** + * vc_csi_at() - (ICH) inserts `nr` blank characters with the default + * character attribute. The cursor remains at the beginning of the + * blank characters. Text between the cursor and right margin moves to + * the right. Characters scrolled past the right margin are lost. + */ +static void vc_csi_at(struct VCChardev *vc, unsigned int nr) +{ + QemuTextConsole *s = vc->console; + TextCell *c1, *c2; + unsigned int x1, x2, y; + unsigned int end, len; + + if (!nr) { + nr = 1; + } + if (nr > s->width - s->x) { + nr = s->width - s->x; + if (!nr) { + return; + } + } + + x1 = s->x + nr; + x2 = s->x; + len = s->width - x1; + if (len) { + y = (s->y_base + s->y) % s->total_height; + c1 = &s->cells[y * s->width + x1]; + c2 = &s->cells[y * s->width + x2]; + memmove(c1, c2, len * sizeof(*c1)); + for (end = x1 + len; x1 < end; x1++) { + vc_update_xy(vc, x1, s->y); + } + } + /* Insert blanks */ + for (x1 = s->x; x1 < s->x + nr; x1++) { + vc_clear_xy(vc, x1, s->y); + } +} + /** * vc_save_cursor() - saves cursor position and character attributes. */ @@ -847,6 +929,9 @@ static void vc_putchar(VCChardev *vc, int ch) break; } break; + case 'P': + vc_csi_P(vc, vc->esc_params[0]); + break; case 'm': vc_handle_escape(vc); break; @@ -870,6 +955,9 @@ static void vc_putchar(VCChardev *vc, int ch) case 'u': vc_restore_cursor(vc); break; + case '@': + vc_csi_at(vc, vc->esc_params[0]); + break; default: trace_console_putchar_unhandled(ch); break; From 46f83c898a6658921fed57f98af6d505ab78a6e4 Mon Sep 17 00:00:00 2001 From: Haoqian He Date: Tue, 25 Feb 2025 18:45:26 +0800 Subject: [PATCH 2159/2892] chardev: use remoteAddr if the chardev is client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the chardev is client, the socket file path in localAddr may be NULL. This is because the socket path comes from getsockname(), according to man page, getsockname() returns the current address bound by the socket sockfd. If the chardev is client, it's socket is unbound sockfd. Therefore, when computing the client chardev socket file path, using remoteAddr is more appropriate. Signed-off-by: Haoqian He Acked-by: Marc-André Lureau Message-ID: <20250225104526.2924175-1-haoqian.he@smartx.com> --- chardev/char-socket.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 91496ceda9..2f842f9f88 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -571,9 +571,13 @@ static char *qemu_chr_compute_filename(SocketChardev *s) switch (ss->ss_family) { case AF_UNIX: - return g_strdup_printf("unix:%s%s", - ((struct sockaddr_un *)(ss))->sun_path, - s->is_listen ? ",server=on" : ""); + if (s->is_listen) { + return g_strdup_printf("unix:%s,server=on", + ((struct sockaddr_un *)(ss))->sun_path); + } else { + return g_strdup_printf("unix:%s", + ((struct sockaddr_un *)(ps))->sun_path); + } case AF_INET6: left = "["; right = "]"; From 3e1683485656c095860a8dfbe39ab2d0664b84d9 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 27 Feb 2025 16:06:15 -0600 Subject: [PATCH 2160/2892] nbd: Defer trace init until after daemonization At least the simple trace backend works by spawning a helper thread, and setting up an atexit() handler that coordinates completion with the helper thread. But since atexit registrations survive fork() but helper threads do not, this means that qemu-nbd configured to use the simple trace will deadlock waiting for a thread that no longer exists when it has daemonized. Better is to follow the example of vl.c: don't call any setup functions that might spawn helper threads until we are in the final process that will be doing the work worth tracing. Tested by configuring with --enable-trace-backends=simple, then running qemu-nbd --fork --trace=nbd_\*,file=qemu-nbd.trace -f raw -r README.rst followed by `nbdinfo nbd://localhost`, and observing that the trace file is now created without hanging. Reported-by: Thomas Huth Signed-off-by: Eric Blake Message-ID: <20250227220625.870246-2-eblake@redhat.com> Reviewed-by: Thomas Huth --- qemu-nbd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/qemu-nbd.c b/qemu-nbd.c index 05b61da51e..ed5895861b 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -852,10 +852,6 @@ int main(int argc, char **argv) export_name = ""; } - if (!trace_init_backends()) { - exit(1); - } - trace_init_file(); qemu_set_log(LOG_TRACE, &error_fatal); socket_activation = check_socket_activation(); @@ -1045,6 +1041,18 @@ int main(int argc, char **argv) #endif /* WIN32 */ } + /* + * trace_init must be done after daemonization. Why? Because at + * least the simple backend spins up a helper thread as well as an + * atexit() handler that waits on that thread, but the helper + * thread won't survive a fork, leading to deadlock in the child + * if we initialized pre-fork. + */ + if (!trace_init_backends()) { + exit(1); + } + trace_init_file(); + if (opts.device != NULL && sockpath == NULL) { sockpath = g_malloc(128); snprintf(sockpath, 128, SOCKET_PATH, basename(opts.device)); From 2ad638a3d160923ef3dbf87c73944e6e44bdc724 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Tue, 4 Mar 2025 11:39:10 +0300 Subject: [PATCH 2161/2892] block/qed: fix use-after-free by nullifying timer pointer after free This error was discovered by fuzzing qemu-img. In the QED block driver, the need_check_timer timer is freed in bdrv_qed_detach_aio_context, but the pointer to the timer is not set to NULL. This can lead to a use-after-free scenario in bdrv_qed_drain_begin(). The need_check_timer pointer is set to NULL after freeing the timer. Which helps catch this condition when checking in bdrv_qed_drain_begin(). Closes: https://gitlab.com/qemu-project/qemu/-/issues/2852 Signed-off-by: Denis Rastyogin Message-ID: <20250304083927.37681-1-gerben@altlinux.org> Signed-off-by: Stefan Hajnoczi --- block/qed.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/qed.c b/block/qed.c index 382c9e5335..ac24449ffb 100644 --- a/block/qed.c +++ b/block/qed.c @@ -353,6 +353,7 @@ static void bdrv_qed_detach_aio_context(BlockDriverState *bs) qed_cancel_need_check_timer(s); timer_free(s->need_check_timer); + s->need_check_timer = NULL; } static void bdrv_qed_attach_aio_context(BlockDriverState *bs, From 4526687bf12624d957088cd40ee02540a5404546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Feb 2025 18:34:55 +0100 Subject: [PATCH 2162/2892] vfio: Add property documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigate the git history to uncover when and why the VFIO properties were introduced and update the models. This is mostly targeting vfio-pci device, since vfio-platform, vfio-ap and vfio-ccw devices are simpler. Sort the properties based on the QEMU version in which they were introduced. Cc: Tony Krowiak Cc: Eric Farman Cc: Eric Auger Reviewed-by: Kirti Wankhede Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman # vfio-ccw Reviewed-by: Alex Williamson Reviewed-by: Eric Auger Link: https://lore.kernel.org/qemu-devel/20250217173455.449983-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 9 ++++ hw/vfio/ccw.c | 15 ++++++ hw/vfio/pci.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ hw/vfio/platform.c | 24 +++++++++ 4 files changed, 173 insertions(+) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 30b08ad375..c7ab4ff57a 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -257,6 +257,15 @@ static void vfio_ap_class_init(ObjectClass *klass, void *data) dc->hotpluggable = true; device_class_set_legacy_reset(dc, vfio_ap_reset); dc->bus_type = TYPE_AP_BUS; + + object_class_property_set_description(klass, /* 3.1 */ + "sysfsdev", + "Host sysfs path of assigned device"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif } static const TypeInfo vfio_ap_info = { diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 67bc137f9b..ea766ae26c 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -717,6 +717,21 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data) cdc->handle_halt = vfio_ccw_handle_halt; cdc->handle_clear = vfio_ccw_handle_clear; cdc->handle_store = vfio_ccw_handle_store; + + object_class_property_set_description(klass, /* 2.10 */ + "sysfsdev", + "Host sysfs path of assigned device"); + object_class_property_set_description(klass, /* 3.0 */ + "force-orb-pfch", + "Force unlimited prefetch"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif + object_class_property_set_description(klass, /* 9.2 */ + "loadparm", + "Define which devices that can be used for booting"); } static const TypeInfo vfio_ccw_info = { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 89d900e9cf..4f92b50b13 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3433,6 +3433,122 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) pdc->exit = vfio_exitfn; pdc->config_read = vfio_pci_read_config; pdc->config_write = vfio_pci_write_config; + + object_class_property_set_description(klass, /* 1.3 */ + "host", + "Host PCI address [domain:] of assigned device"); + object_class_property_set_description(klass, /* 1.3 */ + "x-intx-mmap-timeout-ms", + "When EOI is not provided by KVM/QEMU, wait time " + "(milliseconds) to re-enable device direct access " + "after INTx (DEBUG)"); + object_class_property_set_description(klass, /* 1.5 */ + "x-vga", + "Expose VGA address spaces for device"); + object_class_property_set_description(klass, /* 2.3 */ + "x-req", + "Disable device request notification support (DEBUG)"); + object_class_property_set_description(klass, /* 2.4 and 2.5 */ + "x-no-mmap", + "Disable MMAP for device. Allows to trace MMIO " + "accesses (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-no-kvm-intx", + "Disable direct VFIO->KVM INTx injection. Allows to " + "trace INTx interrupts (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-no-kvm-msi", + "Disable direct VFIO->KVM MSI injection. Allows to " + "trace MSI interrupts (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-no-kvm-msix", + "Disable direct VFIO->KVM MSIx injection. Allows to " + "trace MSIx interrupts (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-vendor-id", + "Override PCI Vendor ID with provided value (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-device-id", + "Override PCI device ID with provided value (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-sub-vendor-id", + "Override PCI Subsystem Vendor ID with provided value " + "(DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-sub-device-id", + "Override PCI Subsystem Device ID with provided value " + "(DEBUG)"); + object_class_property_set_description(klass, /* 2.6 */ + "sysfsdev", + "Host sysfs path of assigned device"); + object_class_property_set_description(klass, /* 2.7 */ + "x-igd-opregion", + "Expose host IGD OpRegion to guest"); + object_class_property_set_description(klass, /* 2.7 (See c4c45e943e51) */ + "x-igd-gms", + "Override IGD data stolen memory size (32MiB units)"); + object_class_property_set_description(klass, /* 2.11 */ + "x-nv-gpudirect-clique", + "Add NVIDIA GPUDirect capability indicating P2P DMA " + "clique for device [0-15]"); + object_class_property_set_description(klass, /* 2.12 */ + "x-no-geforce-quirks", + "Disable GeForce quirks (for NVIDIA Quadro/GRID/Tesla). " + "Improves performance"); + object_class_property_set_description(klass, /* 2.12 */ + "display", + "Enable display support for device, ex. vGPU"); + object_class_property_set_description(klass, /* 2.12 */ + "x-msix-relocation", + "Specify MSI-X MMIO relocation to the end of specified " + "existing BAR or new BAR to avoid virtualization overhead " + "due to adjacent device registers"); + object_class_property_set_description(klass, /* 3.0 */ + "x-no-kvm-ioeventfd", + "Disable registration of ioeventfds with KVM (DEBUG)"); + object_class_property_set_description(klass, /* 3.0 */ + "x-no-vfio-ioeventfd", + "Disable linking of KVM ioeventfds to VFIO ioeventfds " + "(DEBUG)"); + object_class_property_set_description(klass, /* 3.1 */ + "x-balloon-allowed", + "Override allowing ballooning with device (DEBUG, DANGER)"); + object_class_property_set_description(klass, /* 3.2 */ + "xres", + "Set X display resolution the vGPU should use"); + object_class_property_set_description(klass, /* 3.2 */ + "yres", + "Set Y display resolution the vGPU should use"); + object_class_property_set_description(klass, /* 5.2 */ + "x-pre-copy-dirty-page-tracking", + "Disable dirty pages tracking during iterative phase " + "(DEBUG)"); + object_class_property_set_description(klass, /* 5.2, 8.0 non-experimetal */ + "enable-migration", + "Enale device migration. Also requires a host VFIO PCI " + "variant or mdev driver with migration support enabled"); + object_class_property_set_description(klass, /* 8.1 */ + "vf-token", + "Specify UUID VF token. Required for VF when PF is owned " + "by another VFIO driver"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif + object_class_property_set_description(klass, /* 9.1 */ + "x-device-dirty-page-tracking", + "Disable device dirty page tracking and use " + "container-based dirty page tracking (DEBUG)"); + object_class_property_set_description(klass, /* 9.1 */ + "migration-events", + "Emit VFIO migration QAPI event when a VFIO device " + "changes its migration state. For management applications"); + object_class_property_set_description(klass, /* 9.1 */ + "skip-vsc-check", + "Skip config space check for Vendor Specific Capability. " + "Setting to false will enforce strict checking of VSC content " + "(DEBUG)"); } static const TypeInfo vfio_pci_dev_info = { @@ -3461,6 +3577,15 @@ static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, vfio_pci_dev_nohotplug_properties); dc->hotpluggable = false; + + object_class_property_set_description(klass, /* 3.1 */ + "ramfb", + "Enable ramfb to provide pre-boot graphics for devices " + "enabling display option"); + object_class_property_set_description(klass, /* 8.2 */ + "x-ramfb-migrate", + "Override default migration support for ramfb support " + "(DEBUG)"); } static const TypeInfo vfio_pci_nohotplug_dev_info = { diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index f491f4dc95..d9faaa7395 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -672,6 +672,30 @@ static void vfio_platform_class_init(ObjectClass *klass, void *data) dc->desc = "VFIO-based platform device assignment"; sbc->connect_irq_notifier = vfio_start_irqfd_injection; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + object_class_property_set_description(klass, /* 2.4 */ + "host", + "Host device name of assigned device"); + object_class_property_set_description(klass, /* 2.4 and 2.5 */ + "x-no-mmap", + "Disable MMAP for device. Allows to trace MMIO " + "accesses (DEBUG)"); + object_class_property_set_description(klass, /* 2.4 */ + "mmap-timeout-ms", + "When EOI is not provided by KVM/QEMU, wait time " + "(milliseconds) to re-enable device direct access " + "after level interrupt (DEBUG)"); + object_class_property_set_description(klass, /* 2.4 */ + "x-irqfd", + "Allow disabling irqfd support (DEBUG)"); + object_class_property_set_description(klass, /* 2.6 */ + "sysfsdev", + "Host sysfs path of assigned device"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif } static const TypeInfo vfio_platform_dev_info = { From 3f8f6ef701ac6b3691b6003025005a970b2075de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 14 Feb 2025 17:19:36 +0100 Subject: [PATCH 2163/2892] vfio/ccw: Replace warn_once_pfch() with warn_report_once() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the common helper warn_report_once() instead of implementing its own. Cc: Eric Farman Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20250214161936.1720039-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/ccw.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index ea766ae26c..e5e0d9e3e7 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -51,17 +51,8 @@ struct VFIOCCWDevice { EventNotifier crw_notifier; EventNotifier req_notifier; bool force_orb_pfch; - bool warned_orb_pfch; }; -static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch, - const char *msg) -{ - warn_report_once_cond(&vcdev->warned_orb_pfch, - "vfio-ccw (devno %x.%x.%04x): %s", - sch->cssid, sch->ssid, sch->devno, msg); -} - static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) { vdev->needs_reset = false; @@ -83,7 +74,8 @@ static IOInstEnding vfio_ccw_handle_request(SubchDev *sch) if (!(sch->orb.ctrl0 & ORB_CTRL0_MASK_PFCH) && vcdev->force_orb_pfch) { sch->orb.ctrl0 |= ORB_CTRL0_MASK_PFCH; - warn_once_pfch(vcdev, sch, "PFCH flag forced"); + warn_report_once("vfio-ccw (devno %x.%x.%04x): PFCH flag forced", + sch->cssid, sch->ssid, sch->devno); } QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB)); From 9461afd2008b0820fc45a6a7bc675df1b6791e4f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 25 Feb 2025 14:52:25 -0700 Subject: [PATCH 2164/2892] hw/pci: Basic support for PCI power management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The memory and IO BARs for devices are only accessible in the D0 power state. In other power states the PCI spec defines that the device responds to TLPs and messages with an Unsupported Request response. To approximate this behavior, consider the BARs as unmapped when the device is not in the D0 power state. This makes the BARs inaccessible and has the additional bonus for vfio-pci that we don't attempt to DMA map BARs for devices in a non-D0 power state. To support this, an interface is added for devices to register the PM capability, which allows central tracking to enforce valid transitions and unmap BARs in non-D0 states. NB. We currently have device models (eepro100 and pcie_pci_bridge) that register a PM capability but do not set wmask to enable writes to the power state field. In order to maintain migration compatibility, this new helper does not manage the wmask to enable guest writes to initiate a power state change. The contents and write access of the PM capability are still managed by the caller. Cc: Michael S. Tsirkin Cc: Marcel Apfelbaum Signed-off-by: Alex Williamson Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/qemu-devel/20250225215237.3314011-2-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- hw/pci/pci.c | 93 ++++++++++++++++++++++++++++++++++++- hw/pci/trace-events | 2 + include/hw/pci/pci.h | 3 ++ include/hw/pci/pci_device.h | 3 ++ 4 files changed, 99 insertions(+), 2 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1d42847ef0..2ef7f61749 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -435,6 +435,84 @@ static void pci_msi_trigger(PCIDevice *dev, MSIMessage msg) attrs, NULL); } +/* + * Register and track a PM capability. If wmask is also enabled for the power + * state field of the pmcsr register, guest writes may change the device PM + * state. BAR access is only enabled while the device is in the D0 state. + * Return the capability offset or negative error code. + */ +int pci_pm_init(PCIDevice *d, uint8_t offset, Error **errp) +{ + int cap = pci_add_capability(d, PCI_CAP_ID_PM, offset, PCI_PM_SIZEOF, errp); + + if (cap < 0) { + return cap; + } + + d->pm_cap = cap; + d->cap_present |= QEMU_PCI_CAP_PM; + + return cap; +} + +static uint8_t pci_pm_state(PCIDevice *d) +{ + uint16_t pmcsr; + + if (!(d->cap_present & QEMU_PCI_CAP_PM)) { + return 0; + } + + pmcsr = pci_get_word(d->config + d->pm_cap + PCI_PM_CTRL); + + return pmcsr & PCI_PM_CTRL_STATE_MASK; +} + +/* + * Update the PM capability state based on the new value stored in config + * space respective to the old, pre-write state provided. If the new value + * is rejected (unsupported or invalid transition) restore the old value. + * Return the resulting PM state. + */ +static uint8_t pci_pm_update(PCIDevice *d, uint32_t addr, int l, uint8_t old) +{ + uint16_t pmc; + uint8_t new; + + if (!(d->cap_present & QEMU_PCI_CAP_PM) || + !range_covers_byte(addr, l, d->pm_cap + PCI_PM_CTRL)) { + return old; + } + + new = pci_pm_state(d); + if (new == old) { + return old; + } + + pmc = pci_get_word(d->config + d->pm_cap + PCI_PM_PMC); + + /* + * Transitions to D1 & D2 are only allowed if supported. Devices may + * only transition to higher D-states or to D0. + */ + if ((!(pmc & PCI_PM_CAP_D1) && new == 1) || + (!(pmc & PCI_PM_CAP_D2) && new == 2) || + (old && new && new < old)) { + pci_word_test_and_clear_mask(d->config + d->pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + pci_word_test_and_set_mask(d->config + d->pm_cap + PCI_PM_CTRL, + old); + trace_pci_pm_bad_transition(d->name, pci_dev_bus_num(d), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), + old, new); + return old; + } + + trace_pci_pm_transition(d->name, pci_dev_bus_num(d), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), old, new); + return new; +} + static void pci_reset_regions(PCIDevice *dev) { int r; @@ -474,6 +552,11 @@ static void pci_do_device_reset(PCIDevice *dev) pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) | pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; + /* Default PM state is D0 */ + if (dev->cap_present & QEMU_PCI_CAP_PM) { + pci_word_test_and_clear_mask(dev->config + dev->pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + } pci_reset_regions(dev); pci_update_mappings(dev); @@ -1606,7 +1689,7 @@ static void pci_update_mappings(PCIDevice *d) continue; new_addr = pci_bar_address(d, i, r->type, r->size); - if (!d->enabled) { + if (!d->enabled || pci_pm_state(d)) { new_addr = PCI_BAR_UNMAPPED; } @@ -1672,6 +1755,7 @@ uint32_t pci_default_read_config(PCIDevice *d, void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int l) { + uint8_t new_pm_state, old_pm_state = pci_pm_state(d); int i, was_irq_disabled = pci_irq_disabled(d); uint32_t val = val_in; @@ -1684,11 +1768,16 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ } + + new_pm_state = pci_pm_update(d, addr, l, old_pm_state); + if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || - range_covers_byte(addr, l, PCI_COMMAND)) + range_covers_byte(addr, l, PCI_COMMAND) || + !!new_pm_state != !!old_pm_state) { pci_update_mappings(d); + } if (ranges_overlap(addr, l, PCI_COMMAND, 2)) { pci_update_irq_disabled(d, was_irq_disabled); diff --git a/hw/pci/trace-events b/hw/pci/trace-events index e98f575a9d..6a9968962f 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -1,6 +1,8 @@ # See docs/devel/tracing.rst for syntax documentation. # pci.c +pci_pm_bad_transition(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, uint8_t old, uint8_t new) "%s %02x:%02x.%x REJECTED PM transition D%d->D%d" +pci_pm_transition(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, uint8_t old, uint8_t new) "%s %02x:%02x.%x PM transition D%d->D%d" pci_update_mappings_del(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "%s %02x:%02x.%x %d,0x%"PRIx64"+0x%"PRIx64 pci_update_mappings_add(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "%s %02x:%02x.%x %d,0x%"PRIx64"+0x%"PRIx64 pci_route_irq(int dev_irq, const char *dev_path, int parent_irq, const char *parent_path) "IRQ %d @%s -> IRQ %d @%s" diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 4002bbeebd..c220cc8449 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -216,6 +216,8 @@ enum { QEMU_PCIE_ARI_NEXTFN_1 = (1 << QEMU_PCIE_ARI_NEXTFN_1_BITNR), #define QEMU_PCIE_EXT_TAG_BITNR 13 QEMU_PCIE_EXT_TAG = (1 << QEMU_PCIE_EXT_TAG_BITNR), +#define QEMU_PCI_CAP_PM_BITNR 14 + QEMU_PCI_CAP_PM = (1 << QEMU_PCI_CAP_PM_BITNR), }; typedef struct PCIINTxRoute { @@ -676,5 +678,6 @@ static inline void pci_irq_deassert(PCIDevice *pci_dev) MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); void pci_set_enabled(PCIDevice *pci_dev, bool state); void pci_set_power(PCIDevice *pci_dev, bool state); +int pci_pm_init(PCIDevice *pci_dev, uint8_t offset, Error **errp); #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index add208edfa..345b12eaac 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -105,6 +105,9 @@ struct PCIDevice { /* Capability bits */ uint32_t cap_present; + /* Offset of PM capability in config space */ + uint8_t pm_cap; + /* Offset of MSI-X capability in config space */ uint8_t msix_cap; From 0681ec253141d838210b3c5e6bc0d2d71f2e111e Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 25 Feb 2025 14:52:26 -0700 Subject: [PATCH 2165/2892] pci: Use PCI PM capability initializer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch callers directly initializing the PCI PM capability with pci_add_capability() to use pci_pm_init(). Cc: Dmitry Fleytman Cc: Akihiko Odaki Cc: Jason Wang Cc: Stefan Weil Cc: Sriram Yagnaraman Cc: Keith Busch Cc: Klaus Jensen Cc: Jesper Devantier Cc: Michael S. Tsirkin Cc: Marcel Apfelbaum Cc: Cédric Le Goater Signed-off-by: Alex Williamson Reviewed-by: Eric Auger Reviewed-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/qemu-devel/20250225215237.3314011-3-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- hw/net/e1000e.c | 3 +-- hw/net/eepro100.c | 4 +--- hw/net/igb.c | 3 +-- hw/nvme/ctrl.c | 3 +-- hw/pci-bridge/pcie_pci_bridge.c | 2 +- hw/vfio/pci.c | 7 ++++++- hw/virtio/virtio-pci.c | 3 +-- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index f637853073..b72cbab7e8 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -372,8 +372,7 @@ static int e1000e_add_pm_capability(PCIDevice *pdev, uint8_t offset, uint16_t pmc) { Error *local_err = NULL; - int ret = pci_add_capability(pdev, PCI_CAP_ID_PM, offset, - PCI_PM_SIZEOF, &local_err); + int ret = pci_pm_init(pdev, offset, &local_err); if (local_err) { error_report_err(local_err); diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 6d853229ae..29a39865a6 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -551,9 +551,7 @@ static void e100_pci_reset(EEPRO100State *s, Error **errp) if (info->power_management) { /* Power Management Capabilities */ int cfg_offset = 0xdc; - int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, - cfg_offset, PCI_PM_SIZEOF, - errp); + int r = pci_pm_init(&s->dev, cfg_offset, errp); if (r < 0) { return; } diff --git a/hw/net/igb.c b/hw/net/igb.c index c965fc2fb6..e318df40e0 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -356,8 +356,7 @@ static int igb_add_pm_capability(PCIDevice *pdev, uint8_t offset, uint16_t pmc) { Error *local_err = NULL; - int ret = pci_add_capability(pdev, PCI_CAP_ID_PM, offset, - PCI_PM_SIZEOF, &local_err); + int ret = pci_pm_init(pdev, offset, &local_err); if (local_err) { error_report_err(local_err); diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e62c6a3588..518d02dc66 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8600,8 +8600,7 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) Error *err = NULL; int ret; - ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, offset, - PCI_PM_SIZEOF, &err); + ret = pci_pm_init(pci_dev, offset, &err); if (err) { error_report_err(err); return ret; diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index fd4514a595..9fa656b43b 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -52,7 +52,7 @@ static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp) goto cap_error; } - pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF, errp); + pos = pci_pm_init(d, 0, errp); if (pos < 0) { goto pm_error; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4f92b50b13..d33b795af0 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2216,7 +2216,12 @@ static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) case PCI_CAP_ID_PM: vfio_check_pm_reset(vdev, pos); vdev->pm_cap = pos; - ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; + ret = pci_pm_init(pdev, pos, errp) >= 0; + /* + * PCI-core config space emulation needs write access to the power + * state enabled for tracking BAR mapping relative to PM state. + */ + pci_set_word(pdev->wmask + pos + PCI_PM_CTRL, PCI_PM_CTRL_STATE_MASK); break; case PCI_CAP_ID_AF: vfio_check_af_flr(vdev, pos); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index c773a9130c..afe8b5551c 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2204,8 +2204,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); - pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, - PCI_PM_SIZEOF, errp); + pos = pci_pm_init(pci_dev, 0, errp); if (pos < 0) { return; } From 05c6a8eff6298675080aa2692ee05a310b3483b4 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 25 Feb 2025 14:52:27 -0700 Subject: [PATCH 2166/2892] vfio/pci: Delete local pm_cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now redundant to PCIDevice.pm_cap. Cc: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Signed-off-by: Alex Williamson Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/qemu-devel/20250225215237.3314011-4-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 9 ++++----- hw/vfio/pci.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d33b795af0..a8db19d8d2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2215,7 +2215,6 @@ static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) break; case PCI_CAP_ID_PM: vfio_check_pm_reset(vdev, pos); - vdev->pm_cap = pos; ret = pci_pm_init(pdev, pos, errp) >= 0; /* * PCI-core config space emulation needs write access to the power @@ -2412,17 +2411,17 @@ void vfio_pci_pre_reset(VFIOPCIDevice *vdev) vfio_disable_interrupts(vdev); /* Make sure the device is in D0 */ - if (vdev->pm_cap) { + if (pdev->pm_cap) { uint16_t pmcsr; uint8_t state; - pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); + pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); + vfio_pci_write_config(pdev, pdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); /* vfio handles the necessary delay here */ - pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); + pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { error_report("vfio: Unable to power on device, stuck in D%d", diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 43c166680a..d638c781f6 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -160,7 +160,6 @@ struct VFIOPCIDevice { int32_t bootindex; uint32_t igd_gms; OffAutoPCIBAR msix_relo; - uint8_t pm_cap; uint8_t nv_gpudirect_clique; bool pci_aer; bool req_enabled; From 8b8d08cf293b930d0f55b2d5385d8dd27e0c6b41 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 25 Feb 2025 14:52:28 -0700 Subject: [PATCH 2167/2892] pcie, virtio: Remove redundant pm_cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pm_cap on the PCIExpressDevice object can be distilled down to the new instance on the PCIDevice object. Cc: Michael S. Tsirkin Cc: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Signed-off-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250225215237.3314011-5-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- hw/pci-bridge/pcie_pci_bridge.c | 1 - hw/virtio/virtio-pci.c | 8 +++----- include/hw/pci/pcie.h | 2 -- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 9fa656b43b..2429503cfb 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -56,7 +56,6 @@ static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp) if (pos < 0) { goto pm_error; } - d->exp.pm_cap = pos; pci_set_word(d->config + pos + PCI_PM_PMC, 0x3); pcie_cap_arifwd_init(d); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index afe8b5551c..3ca3f849d3 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2209,8 +2209,6 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) return; } - pci_dev->exp.pm_cap = pos; - /* * Indicates that this function complies with revision 1.2 of the * PCI Power Management Interface Specification. @@ -2309,11 +2307,11 @@ static bool virtio_pci_no_soft_reset(PCIDevice *dev) { uint16_t pmcsr; - if (!pci_is_express(dev) || !dev->exp.pm_cap) { + if (!pci_is_express(dev) || !(dev->cap_present & QEMU_PCI_CAP_PM)) { return false; } - pmcsr = pci_get_word(dev->config + dev->exp.pm_cap + PCI_PM_CTRL); + pmcsr = pci_get_word(dev->config + dev->pm_cap + PCI_PM_CTRL); /* * When No_Soft_Reset bit is set and the device @@ -2342,7 +2340,7 @@ static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) { pci_word_test_and_clear_mask( - dev->config + dev->exp.pm_cap + PCI_PM_CTRL, + dev->config + dev->pm_cap + PCI_PM_CTRL, PCI_PM_CTRL_STATE_MASK); } } diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index b8d59732bc..70a5de09de 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -58,8 +58,6 @@ typedef enum { struct PCIExpressDevice { /* Offset of express capability in config space */ uint8_t exp_cap; - /* Offset of Power Management capability in config space */ - uint8_t pm_cap; /* SLOT */ bool hpev_notified; /* Logical AND of conditions for hot plug event. From 518a69a598916749338de3852d41d961d4503115 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 25 Feb 2025 14:52:29 -0700 Subject: [PATCH 2168/2892] hw/vfio/pci: Re-order pre-reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want the device in the D0 power state going into reset, but the config write can enable the BARs in the address space, which are then removed from the address space once we clear the memory enable bit in the command register. Re-order to clear the command bit first, so the power state change doesn't enable the BARs. Cc: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Signed-off-by: Alex Williamson Reviewed-by: Michael S. Tsirkin Link: https://lore.kernel.org/qemu-devel/20250225215237.3314011-6-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a8db19d8d2..c1cee280ae 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2410,6 +2410,15 @@ void vfio_pci_pre_reset(VFIOPCIDevice *vdev) vfio_disable_interrupts(vdev); + /* + * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. + * Also put INTx Disable in known state. + */ + cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + /* Make sure the device is in D0 */ if (pdev->pm_cap) { uint16_t pmcsr; @@ -2429,15 +2438,6 @@ void vfio_pci_pre_reset(VFIOPCIDevice *vdev) } } } - - /* - * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. - * Also put INTx Disable in known state. - */ - cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); - cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); } void vfio_pci_post_reset(VFIOPCIDevice *vdev) From 515d80d66527b105493fad2df313693a72bf7560 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 28 Feb 2025 00:27:41 +0800 Subject: [PATCH 2169/2892] MAINTAINERS: Add myself as vfio-igd maintainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As suggested by Cédric, I'm glad to be a maintainer of vfio-igd. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250227162741.9860-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 692628cd78..1a920e7dc4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2186,10 +2186,17 @@ M: Cédric Le Goater S: Supported F: hw/vfio/* F: include/hw/vfio/ -F: docs/igd-assign.txt F: docs/devel/migration/vfio.rst F: qapi/vfio.json +vfio-igd +M: Alex Williamson +M: Cédric Le Goater +M: Tomita Moeko +S: Supported +F: hw/vfio/igd.c +F: docs/igd-assign.txt + vfio-ccw M: Eric Farman M: Matthew Rosato From 8d8a30d1ac7cc9b35833106d749e1b3e8675bc53 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 5 Mar 2025 13:42:25 +0100 Subject: [PATCH 2170/2892] vfio-platform: Deprecate all forms of vfio-platform devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As an outcome of KVM forum 2024 "vfio-platform: live and let die?" talk, let's deprecate vfio-platform devices. Signed-off-by: Eric Auger Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250305124225.952791-1-eric.auger@redhat.com [ clg: Fixed spelling in vfio-amd-xgbe section ] Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 25 +++++++++++++++++++++++++ hw/vfio/amd-xgbe.c | 2 ++ hw/vfio/calxeda-xgmac.c | 2 ++ hw/vfio/platform.c | 1 + 4 files changed, 30 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index abadf8de27..589951b136 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -434,6 +434,31 @@ Stream ``reconnect`` (since 9.2) The ``reconnect`` option only allows specifiying second granularity timeouts, which is not enough for all types of use cases, use ``reconnect-ms`` instead. +VFIO device options +''''''''''''''''''' + +``-device vfio-calxeda-xgmac`` (since 10.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank +10Gb XGMAC Ethernet controller device ("calxeda,hb-xgmac" compatibility +string) to a guest. Calxeda HW has been ewasted now and there is no point +keeping that device. + +``-device vfio-amd-xgbe`` (since 10.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller +to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" +is not supported anymore and there is no point keeping that device. + +``-device vfio-platform`` (since 10.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The vfio-platform device allows to assign a host platform device +to a guest in a generic manner. Integrating a new device into +the vfio-platform infrastructure requires some adaptation at +both kernel and qemu level. No such attempt has been done for years +and the conclusion is that vfio-platform has not got any traction. +PCIe passthrough shall be the mainline solution. + CPU device properties ''''''''''''''''''''' diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index aaa96903db..5927503b5c 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -15,12 +15,14 @@ #include "hw/vfio/vfio-amd-xgbe.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/error-report.h" static void amd_xgbe_realize(DeviceState *dev, Error **errp) { VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev); + warn_report("-device vfio-amd-xgbe is deprecated"); vdev->compat = g_strdup("amd,xgbe-seattle-v1a"); vdev->num_compat = 1; diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index b016d42b49..a5ef262def 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -15,12 +15,14 @@ #include "hw/vfio/vfio-calxeda-xgmac.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/error-report.h" static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) { VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev); + warn_report("-device vfio-calxeda-xgmac is deprecated"); vdev->compat = g_strdup("calxeda,hb-xgmac"); vdev->num_compat = 1; diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index d9faaa7395..67bc57409c 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -575,6 +575,7 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) VFIODevice *vbasedev = &vdev->vbasedev; int i; + warn_report("-device vfio-platform is deprecated"); qemu_mutex_init(&vdev->intp_mutex); trace_vfio_platform_realize(vbasedev->sysfsdev ? From d3237d0d85e9fba79b37c776b0ddda611c338f7d Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:28 +0100 Subject: [PATCH 2171/2892] migration: Clarify that {load, save}_cleanup handlers can run without setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's possible for {load,save}_cleanup SaveVMHandlers to get called without the corresponding {load,save}_setup handler being called first. One such example is if {load,save}_setup handler of a proceeding device returns error. In this case the migration core cleanup code will call all corresponding cleanup handlers, even for these devices which haven't had its setup handler called. Since this behavior can generate some surprises let's clearly document it in these SaveVMHandlers description. Reviewed-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/991636623fb780350f493b5f045cb17e13ce4c0f.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/register.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/migration/register.h b/include/migration/register.h index f60e797894..0b02927383 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -69,7 +69,9 @@ typedef struct SaveVMHandlers { /** * @save_cleanup * - * Uninitializes the data structures on the source + * Uninitializes the data structures on the source. + * Note that this handler can be called even if save_setup + * wasn't called earlier. * * @opaque: data pointer passed to register_savevm_live() */ @@ -244,6 +246,8 @@ typedef struct SaveVMHandlers { * @load_cleanup * * Uninitializes the data structures on the destination. + * Note that this handler can be called even if load_setup + * wasn't called earlier. * * @opaque: data pointer passed to register_savevm_live() * From 03c6468a136b3d79e7c6bb269ac7924fa8fef634 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:29 +0100 Subject: [PATCH 2172/2892] thread-pool: Remove thread_pool_submit() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function name conflicts with one used by a future generic thread pool function and it was only used by one test anyway. Update the trace event name in thread_pool_submit_aio() accordingly. Acked-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/6830f07777f939edaf0a2d301c39adcaaf3817f0.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/block/thread-pool.h | 3 +-- tests/unit/test-thread-pool.c | 6 +++--- util/thread-pool.c | 7 +------ util/trace-events | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h index 948ff5f30c..4f66940261 100644 --- a/include/block/thread-pool.h +++ b/include/block/thread-pool.h @@ -30,13 +30,12 @@ ThreadPool *thread_pool_new(struct AioContext *ctx); void thread_pool_free(ThreadPool *pool); /* - * thread_pool_submit* API: submit I/O requests in the thread's + * thread_pool_submit_{aio,co} API: submit I/O requests in the thread's * current AioContext. */ BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, BlockCompletionFunc *cb, void *opaque); int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); -void thread_pool_submit(ThreadPoolFunc *func, void *arg); void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); diff --git a/tests/unit/test-thread-pool.c b/tests/unit/test-thread-pool.c index 1483e53473..33407b595d 100644 --- a/tests/unit/test-thread-pool.c +++ b/tests/unit/test-thread-pool.c @@ -43,10 +43,10 @@ static void done_cb(void *opaque, int ret) active--; } -static void test_submit(void) +static void test_submit_no_complete(void) { WorkerTestData data = { .n = 0 }; - thread_pool_submit(worker_cb, &data); + thread_pool_submit_aio(worker_cb, &data, NULL, NULL); while (data.n == 0) { aio_poll(ctx, true); } @@ -236,7 +236,7 @@ int main(int argc, char **argv) ctx = qemu_get_current_aio_context(); g_test_init(&argc, &argv, NULL); - g_test_add_func("/thread-pool/submit", test_submit); + g_test_add_func("/thread-pool/submit-no-complete", test_submit_no_complete); g_test_add_func("/thread-pool/submit-aio", test_submit_aio); g_test_add_func("/thread-pool/submit-co", test_submit_co); g_test_add_func("/thread-pool/submit-many", test_submit_many); diff --git a/util/thread-pool.c b/util/thread-pool.c index 27eb777e85..2f751d55b3 100644 --- a/util/thread-pool.c +++ b/util/thread-pool.c @@ -256,7 +256,7 @@ BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, QLIST_INSERT_HEAD(&pool->head, req, all); - trace_thread_pool_submit(pool, req, arg); + trace_thread_pool_submit_aio(pool, req, arg); qemu_mutex_lock(&pool->lock); if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { @@ -290,11 +290,6 @@ int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) return tpc.ret; } -void thread_pool_submit(ThreadPoolFunc *func, void *arg) -{ - thread_pool_submit_aio(func, arg, NULL, NULL); -} - void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) { qemu_mutex_lock(&pool->lock); diff --git a/util/trace-events b/util/trace-events index 49a4962e18..5be12d7fab 100644 --- a/util/trace-events +++ b/util/trace-events @@ -14,7 +14,7 @@ aio_co_schedule_bh_cb(void *ctx, void *co) "ctx %p co %p" reentrant_aio(void *ctx, const char *name) "ctx %p name %s" # thread-pool.c -thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" +thread_pool_submit_aio(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" thread_pool_cancel(void *req, void *opaque) "req %p opaque %p" From dc67daeed579ea52f045f78b88d9e5e712038ccf Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:30 +0100 Subject: [PATCH 2173/2892] thread-pool: Rename AIO pool functions to *_aio() and data types to *Aio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These names conflict with ones used by future generic thread pool equivalents. Generic names should belong to the generic pool type, not specific (AIO) type. Acked-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/70f9e0fb4b01042258a1a57996c64d19779dc7f0.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/block/aio.h | 8 ++--- include/block/thread-pool.h | 8 ++--- util/async.c | 6 ++-- util/thread-pool.c | 58 ++++++++++++++++++------------------- util/trace-events | 4 +-- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/include/block/aio.h b/include/block/aio.h index 43883a8a33..b2ab3514de 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -54,7 +54,7 @@ typedef void QEMUBHFunc(void *opaque); typedef bool AioPollFn(void *opaque); typedef void IOHandler(void *opaque); -struct ThreadPool; +struct ThreadPoolAio; struct LinuxAioState; typedef struct LuringState LuringState; @@ -207,7 +207,7 @@ struct AioContext { /* Thread pool for performing work and receiving completion callbacks. * Has its own locking. */ - struct ThreadPool *thread_pool; + struct ThreadPoolAio *thread_pool; #ifdef CONFIG_LINUX_AIO struct LinuxAioState *linux_aio; @@ -500,8 +500,8 @@ void aio_set_event_notifier_poll(AioContext *ctx, */ GSource *aio_get_g_source(AioContext *ctx); -/* Return the ThreadPool bound to this AioContext */ -struct ThreadPool *aio_get_thread_pool(AioContext *ctx); +/* Return the ThreadPoolAio bound to this AioContext */ +struct ThreadPoolAio *aio_get_thread_pool(AioContext *ctx); /* Setup the LinuxAioState bound to this AioContext */ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h index 4f66940261..6f27eb085b 100644 --- a/include/block/thread-pool.h +++ b/include/block/thread-pool.h @@ -24,10 +24,10 @@ typedef int ThreadPoolFunc(void *opaque); -typedef struct ThreadPool ThreadPool; +typedef struct ThreadPoolAio ThreadPoolAio; -ThreadPool *thread_pool_new(struct AioContext *ctx); -void thread_pool_free(ThreadPool *pool); +ThreadPoolAio *thread_pool_new_aio(struct AioContext *ctx); +void thread_pool_free_aio(ThreadPoolAio *pool); /* * thread_pool_submit_{aio,co} API: submit I/O requests in the thread's @@ -36,7 +36,7 @@ void thread_pool_free(ThreadPool *pool); BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, BlockCompletionFunc *cb, void *opaque); int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); +void thread_pool_update_params(ThreadPoolAio *pool, struct AioContext *ctx); -void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); #endif diff --git a/util/async.c b/util/async.c index 0fe2943609..47e3d35a26 100644 --- a/util/async.c +++ b/util/async.c @@ -369,7 +369,7 @@ aio_ctx_finalize(GSource *source) QEMUBH *bh; unsigned flags; - thread_pool_free(ctx->thread_pool); + thread_pool_free_aio(ctx->thread_pool); #ifdef CONFIG_LINUX_AIO if (ctx->linux_aio) { @@ -435,10 +435,10 @@ GSource *aio_get_g_source(AioContext *ctx) return &ctx->source; } -ThreadPool *aio_get_thread_pool(AioContext *ctx) +ThreadPoolAio *aio_get_thread_pool(AioContext *ctx) { if (!ctx->thread_pool) { - ctx->thread_pool = thread_pool_new(ctx); + ctx->thread_pool = thread_pool_new_aio(ctx); } return ctx->thread_pool; } diff --git a/util/thread-pool.c b/util/thread-pool.c index 2f751d55b3..908194dc07 100644 --- a/util/thread-pool.c +++ b/util/thread-pool.c @@ -23,9 +23,9 @@ #include "block/thread-pool.h" #include "qemu/main-loop.h" -static void do_spawn_thread(ThreadPool *pool); +static void do_spawn_thread(ThreadPoolAio *pool); -typedef struct ThreadPoolElement ThreadPoolElement; +typedef struct ThreadPoolElementAio ThreadPoolElementAio; enum ThreadState { THREAD_QUEUED, @@ -33,9 +33,9 @@ enum ThreadState { THREAD_DONE, }; -struct ThreadPoolElement { +struct ThreadPoolElementAio { BlockAIOCB common; - ThreadPool *pool; + ThreadPoolAio *pool; ThreadPoolFunc *func; void *arg; @@ -47,13 +47,13 @@ struct ThreadPoolElement { int ret; /* Access to this list is protected by lock. */ - QTAILQ_ENTRY(ThreadPoolElement) reqs; + QTAILQ_ENTRY(ThreadPoolElementAio) reqs; /* This list is only written by the thread pool's mother thread. */ - QLIST_ENTRY(ThreadPoolElement) all; + QLIST_ENTRY(ThreadPoolElementAio) all; }; -struct ThreadPool { +struct ThreadPoolAio { AioContext *ctx; QEMUBH *completion_bh; QemuMutex lock; @@ -62,10 +62,10 @@ struct ThreadPool { QEMUBH *new_thread_bh; /* The following variables are only accessed from one AioContext. */ - QLIST_HEAD(, ThreadPoolElement) head; + QLIST_HEAD(, ThreadPoolElementAio) head; /* The following variables are protected by lock. */ - QTAILQ_HEAD(, ThreadPoolElement) request_list; + QTAILQ_HEAD(, ThreadPoolElementAio) request_list; int cur_threads; int idle_threads; int new_threads; /* backlog of threads we need to create */ @@ -76,14 +76,14 @@ struct ThreadPool { static void *worker_thread(void *opaque) { - ThreadPool *pool = opaque; + ThreadPoolAio *pool = opaque; qemu_mutex_lock(&pool->lock); pool->pending_threads--; do_spawn_thread(pool); while (pool->cur_threads <= pool->max_threads) { - ThreadPoolElement *req; + ThreadPoolElementAio *req; int ret; if (QTAILQ_EMPTY(&pool->request_list)) { @@ -131,7 +131,7 @@ static void *worker_thread(void *opaque) return NULL; } -static void do_spawn_thread(ThreadPool *pool) +static void do_spawn_thread(ThreadPoolAio *pool) { QemuThread t; @@ -148,14 +148,14 @@ static void do_spawn_thread(ThreadPool *pool) static void spawn_thread_bh_fn(void *opaque) { - ThreadPool *pool = opaque; + ThreadPoolAio *pool = opaque; qemu_mutex_lock(&pool->lock); do_spawn_thread(pool); qemu_mutex_unlock(&pool->lock); } -static void spawn_thread(ThreadPool *pool) +static void spawn_thread(ThreadPoolAio *pool) { pool->cur_threads++; pool->new_threads++; @@ -173,8 +173,8 @@ static void spawn_thread(ThreadPool *pool) static void thread_pool_completion_bh(void *opaque) { - ThreadPool *pool = opaque; - ThreadPoolElement *elem, *next; + ThreadPoolAio *pool = opaque; + ThreadPoolElementAio *elem, *next; defer_call_begin(); /* cb() may use defer_call() to coalesce work */ @@ -184,8 +184,8 @@ restart: continue; } - trace_thread_pool_complete(pool, elem, elem->common.opaque, - elem->ret); + trace_thread_pool_complete_aio(pool, elem, elem->common.opaque, + elem->ret); QLIST_REMOVE(elem, all); if (elem->common.cb) { @@ -217,10 +217,10 @@ restart: static void thread_pool_cancel(BlockAIOCB *acb) { - ThreadPoolElement *elem = (ThreadPoolElement *)acb; - ThreadPool *pool = elem->pool; + ThreadPoolElementAio *elem = (ThreadPoolElementAio *)acb; + ThreadPoolAio *pool = elem->pool; - trace_thread_pool_cancel(elem, elem->common.opaque); + trace_thread_pool_cancel_aio(elem, elem->common.opaque); QEMU_LOCK_GUARD(&pool->lock); if (elem->state == THREAD_QUEUED) { @@ -234,16 +234,16 @@ static void thread_pool_cancel(BlockAIOCB *acb) } static const AIOCBInfo thread_pool_aiocb_info = { - .aiocb_size = sizeof(ThreadPoolElement), + .aiocb_size = sizeof(ThreadPoolElementAio), .cancel_async = thread_pool_cancel, }; BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, BlockCompletionFunc *cb, void *opaque) { - ThreadPoolElement *req; + ThreadPoolElementAio *req; AioContext *ctx = qemu_get_current_aio_context(); - ThreadPool *pool = aio_get_thread_pool(ctx); + ThreadPoolAio *pool = aio_get_thread_pool(ctx); /* Assert that the thread submitting work is the same running the pool */ assert(pool->ctx == qemu_get_current_aio_context()); @@ -290,7 +290,7 @@ int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) return tpc.ret; } -void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) +void thread_pool_update_params(ThreadPoolAio *pool, AioContext *ctx) { qemu_mutex_lock(&pool->lock); @@ -317,7 +317,7 @@ void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) qemu_mutex_unlock(&pool->lock); } -static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) +static void thread_pool_init_one(ThreadPoolAio *pool, AioContext *ctx) { if (!ctx) { ctx = qemu_get_aio_context(); @@ -337,14 +337,14 @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) thread_pool_update_params(pool, ctx); } -ThreadPool *thread_pool_new(AioContext *ctx) +ThreadPoolAio *thread_pool_new_aio(AioContext *ctx) { - ThreadPool *pool = g_new(ThreadPool, 1); + ThreadPoolAio *pool = g_new(ThreadPoolAio, 1); thread_pool_init_one(pool, ctx); return pool; } -void thread_pool_free(ThreadPool *pool) +void thread_pool_free_aio(ThreadPoolAio *pool) { if (!pool) { return; diff --git a/util/trace-events b/util/trace-events index 5be12d7fab..bd8f25fb59 100644 --- a/util/trace-events +++ b/util/trace-events @@ -15,8 +15,8 @@ reentrant_aio(void *ctx, const char *name) "ctx %p name %s" # thread-pool.c thread_pool_submit_aio(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" -thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" -thread_pool_cancel(void *req, void *opaque) "req %p opaque %p" +thread_pool_complete_aio(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" +thread_pool_cancel_aio(void *req, void *opaque) "req %p opaque %p" # buffer.c buffer_resize(const char *buf, size_t olen, size_t len) "%s: old %zd, new %zd" From b5aa74968b27f37523c180e9c42ca007dbc7758f Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:31 +0100 Subject: [PATCH 2174/2892] thread-pool: Implement generic (non-AIO) pool support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration code wants to manage device data sending threads in one place. QEMU has an existing thread pool implementation, however it is limited to queuing AIO operations only and essentially has a 1:1 mapping between the current AioContext and the AIO ThreadPool in use. Implement generic (non-AIO) ThreadPool by essentially wrapping Glib's GThreadPool. This brings a few new operations on a pool: * thread_pool_wait() operation waits until all the submitted work requests have finished. * thread_pool_set_max_threads() explicitly sets the maximum thread count in the pool. * thread_pool_adjust_max_threads_to_work() adjusts the maximum thread count in the pool to equal the number of still waiting in queue or unfinished work. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/b1efaebdbea7cb7068b8fb74148777012383e12b.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/block/thread-pool.h | 51 ++++++++++++++++ util/thread-pool.c | 119 ++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h index 6f27eb085b..dd48cf07e8 100644 --- a/include/block/thread-pool.h +++ b/include/block/thread-pool.h @@ -38,5 +38,56 @@ BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); void thread_pool_update_params(ThreadPoolAio *pool, struct AioContext *ctx); +/* ------------------------------------------- */ +/* Generic thread pool types and methods below */ +typedef struct ThreadPool ThreadPool; + +/* Create a new thread pool. Never returns NULL. */ +ThreadPool *thread_pool_new(void); + +/* + * Free the thread pool. + * Waits for all the previously submitted work to complete before performing + * the actual freeing operation. + */ +void thread_pool_free(ThreadPool *pool); + +/* + * Submit a new work (task) for the pool. + * + * @opaque_destroy is an optional GDestroyNotify for the @opaque argument + * to the work function at @func. + */ +void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy); + +/* + * Submit a new work (task) for the pool, making sure it starts getting + * processed immediately, launching a new thread for it if necessary. + * + * @opaque_destroy is an optional GDestroyNotify for the @opaque argument + * to the work function at @func. + */ +void thread_pool_submit_immediate(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy); + +/* + * Wait for all previously submitted work to complete before returning. + * + * Can be used as a barrier between two sets of tasks executed on a thread + * pool without destroying it or in a performance sensitive path where the + * caller just wants to wait for all tasks to complete while deferring the + * pool free operation for later, less performance sensitive time. + */ +void thread_pool_wait(ThreadPool *pool); + +/* Set the maximum number of threads in the pool. */ +bool thread_pool_set_max_threads(ThreadPool *pool, int max_threads); + +/* + * Adjust the maximum number of threads in the pool to give each task its + * own thread (exactly one thread per task). + */ +bool thread_pool_adjust_max_threads_to_work(ThreadPool *pool); #endif diff --git a/util/thread-pool.c b/util/thread-pool.c index 908194dc07..d2ead6b728 100644 --- a/util/thread-pool.c +++ b/util/thread-pool.c @@ -374,3 +374,122 @@ void thread_pool_free_aio(ThreadPoolAio *pool) qemu_mutex_destroy(&pool->lock); g_free(pool); } + +struct ThreadPool { + GThreadPool *t; + size_t cur_work; + QemuMutex cur_work_lock; + QemuCond all_finished_cond; +}; + +typedef struct { + ThreadPoolFunc *func; + void *opaque; + GDestroyNotify opaque_destroy; +} ThreadPoolElement; + +static void thread_pool_func(gpointer data, gpointer user_data) +{ + ThreadPool *pool = user_data; + g_autofree ThreadPoolElement *el = data; + + el->func(el->opaque); + + if (el->opaque_destroy) { + el->opaque_destroy(el->opaque); + } + + QEMU_LOCK_GUARD(&pool->cur_work_lock); + + assert(pool->cur_work > 0); + pool->cur_work--; + + if (pool->cur_work == 0) { + qemu_cond_signal(&pool->all_finished_cond); + } +} + +ThreadPool *thread_pool_new(void) +{ + ThreadPool *pool = g_new(ThreadPool, 1); + + pool->cur_work = 0; + qemu_mutex_init(&pool->cur_work_lock); + qemu_cond_init(&pool->all_finished_cond); + + pool->t = g_thread_pool_new(thread_pool_func, pool, 0, TRUE, NULL); + /* + * g_thread_pool_new() can only return errors if initial thread(s) + * creation fails but we ask for 0 initial threads above. + */ + assert(pool->t); + + return pool; +} + +void thread_pool_free(ThreadPool *pool) +{ + /* + * With _wait = TRUE this effectively waits for all + * previously submitted work to complete first. + */ + g_thread_pool_free(pool->t, FALSE, TRUE); + + qemu_cond_destroy(&pool->all_finished_cond); + qemu_mutex_destroy(&pool->cur_work_lock); + + g_free(pool); +} + +void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy) +{ + ThreadPoolElement *el = g_new(ThreadPoolElement, 1); + + el->func = func; + el->opaque = opaque; + el->opaque_destroy = opaque_destroy; + + WITH_QEMU_LOCK_GUARD(&pool->cur_work_lock) { + pool->cur_work++; + } + + /* + * Ignore the return value since this function can only return errors + * if creation of an additional thread fails but even in this case the + * provided work is still getting queued (just for the existing threads). + */ + g_thread_pool_push(pool->t, el, NULL); +} + +void thread_pool_submit_immediate(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy) +{ + thread_pool_submit(pool, func, opaque, opaque_destroy); + thread_pool_adjust_max_threads_to_work(pool); +} + +void thread_pool_wait(ThreadPool *pool) +{ + QEMU_LOCK_GUARD(&pool->cur_work_lock); + + while (pool->cur_work > 0) { + qemu_cond_wait(&pool->all_finished_cond, + &pool->cur_work_lock); + } +} + +bool thread_pool_set_max_threads(ThreadPool *pool, + int max_threads) +{ + assert(max_threads > 0); + + return g_thread_pool_set_max_threads(pool->t, max_threads, NULL); +} + +bool thread_pool_adjust_max_threads_to_work(ThreadPool *pool) +{ + QEMU_LOCK_GUARD(&pool->cur_work_lock); + + return thread_pool_set_max_threads(pool, pool->cur_work); +} From 4e55cb3cdeb099cb65f75f5d3b061e3e1319cf3b Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:32 +0100 Subject: [PATCH 2175/2892] migration: Add MIG_CMD_SWITCHOVER_START and its load handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This QEMU_VM_COMMAND sub-command and its switchover_start SaveVMHandler is used to mark the switchover point in main migration stream. It can be used to inform the destination that all pre-switchover main migration stream data has been sent/received so it can start to process post-switchover data that it might have received via other migration channels like the multifd ones. Add also the relevant MigrationState bit stream compatibility property and its hw_compat entry. Reviewed-by: Fabiano Rosas Reviewed-by: Zhang Chen # for the COLO part Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/311be6da85fc7e49a7598684d80aa631778dcbce.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/core/machine.c | 1 + include/migration/client-options.h | 4 +++ include/migration/register.h | 12 +++++++++ migration/colo.c | 3 +++ migration/migration-hmp-cmds.c | 2 ++ migration/migration.c | 2 ++ migration/migration.h | 2 ++ migration/options.c | 9 +++++++ migration/savevm.c | 39 ++++++++++++++++++++++++++++++ migration/savevm.h | 1 + migration/trace-events | 1 + scripts/analyze-migration.py | 11 +++++++++ 12 files changed, 87 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index b68b8b94a3..d1ddc3a3db 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -44,6 +44,7 @@ GlobalProperty hw_compat_9_2[] = { { "virtio-balloon-pci-non-transitional", "vectors", "0" }, { "virtio-mem-pci", "vectors", "0" }, { "migration", "multifd-clean-tls-termination", "false" }, + { "migration", "send-switchover-start", "off"}, }; const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); diff --git a/include/migration/client-options.h b/include/migration/client-options.h index 59f4b55cf4..289c9d7762 100644 --- a/include/migration/client-options.h +++ b/include/migration/client-options.h @@ -10,6 +10,10 @@ #ifndef QEMU_MIGRATION_CLIENT_OPTIONS_H #define QEMU_MIGRATION_CLIENT_OPTIONS_H + +/* properties */ +bool migrate_send_switchover_start(void); + /* capabilities */ bool migrate_background_snapshot(void); diff --git a/include/migration/register.h b/include/migration/register.h index 0b02927383..ff0faf5f68 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -279,6 +279,18 @@ typedef struct SaveVMHandlers { * otherwise */ bool (*switchover_ack_needed)(void *opaque); + + /** + * @switchover_start + * + * Notifies that the switchover has started. Called only on + * the destination. + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ + int (*switchover_start)(void *opaque); } SaveVMHandlers; /** diff --git a/migration/colo.c b/migration/colo.c index 9a8e5fbe9b..c976b3ff34 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -452,6 +452,9 @@ static int colo_do_checkpoint_transaction(MigrationState *s, bql_unlock(); goto out; } + + qemu_savevm_maybe_send_switchover_start(s->to_dst_file); + /* Note: device state is saved into buffer */ ret = qemu_save_device_state(fb); diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 3347e34c48..49c26daed3 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -46,6 +46,8 @@ static void migration_global_dump(Monitor *mon) ms->send_configuration ? "on" : "off"); monitor_printf(mon, "send-section-footer: %s\n", ms->send_section_footer ? "on" : "off"); + monitor_printf(mon, "send-switchover-start: %s\n", + ms->send_switchover_start ? "on" : "off"); monitor_printf(mon, "clear-bitmap-shift: %u\n", ms->clear_bitmap_shift); } diff --git a/migration/migration.c b/migration/migration.c index c597aa707e..9e9db26667 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2891,6 +2891,8 @@ static bool migration_switchover_start(MigrationState *s, Error **errp) precopy_notify_complete(); + qemu_savevm_maybe_send_switchover_start(s->to_dst_file); + return true; } diff --git a/migration/migration.h b/migration/migration.h index 4639e2a7e4..7b4278e2a3 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -400,6 +400,8 @@ struct MigrationState { bool send_configuration; /* Whether we send section footer during migration */ bool send_section_footer; + /* Whether we send switchover start notification during migration */ + bool send_switchover_start; /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; diff --git a/migration/options.c b/migration/options.c index bb259d192a..b0ac2ea408 100644 --- a/migration/options.c +++ b/migration/options.c @@ -93,6 +93,8 @@ const Property migration_properties[] = { send_configuration, true), DEFINE_PROP_BOOL("send-section-footer", MigrationState, send_section_footer, true), + DEFINE_PROP_BOOL("send-switchover-start", MigrationState, + send_switchover_start, true), DEFINE_PROP_BOOL("multifd-flush-after-each-section", MigrationState, multifd_flush_after_each_section, false), DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState, @@ -209,6 +211,13 @@ bool migrate_auto_converge(void) return s->capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE]; } +bool migrate_send_switchover_start(void) +{ + MigrationState *s = migrate_get_current(); + + return s->send_switchover_start; +} + bool migrate_background_snapshot(void) { MigrationState *s = migrate_get_current(); diff --git a/migration/savevm.c b/migration/savevm.c index 4046faf009..faebf47ef5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -90,6 +90,7 @@ enum qemu_vm_cmd { MIG_CMD_ENABLE_COLO, /* Enable COLO */ MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ + MIG_CMD_SWITCHOVER_START, /* Switchover start notification */ MIG_CMD_MAX }; @@ -109,6 +110,7 @@ static struct mig_cmd_args { [MIG_CMD_POSTCOPY_RESUME] = { .len = 0, .name = "POSTCOPY_RESUME" }, [MIG_CMD_PACKAGED] = { .len = 4, .name = "PACKAGED" }, [MIG_CMD_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, + [MIG_CMD_SWITCHOVER_START] = { .len = 0, .name = "SWITCHOVER_START" }, [MIG_CMD_MAX] = { .len = -1, .name = "MAX" }, }; @@ -1201,6 +1203,19 @@ void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name) qemu_savevm_command_send(f, MIG_CMD_RECV_BITMAP, len + 1, (uint8_t *)buf); } +static void qemu_savevm_send_switchover_start(QEMUFile *f) +{ + trace_savevm_send_switchover_start(); + qemu_savevm_command_send(f, MIG_CMD_SWITCHOVER_START, 0, NULL); +} + +void qemu_savevm_maybe_send_switchover_start(QEMUFile *f) +{ + if (migrate_send_switchover_start()) { + qemu_savevm_send_switchover_start(f); + } +} + bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; @@ -1687,6 +1702,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) ret = qemu_file_get_error(f); if (ret == 0) { + qemu_savevm_maybe_send_switchover_start(f); qemu_savevm_state_complete_precopy(f, false); ret = qemu_file_get_error(f); } @@ -2383,6 +2399,26 @@ static int loadvm_process_enable_colo(MigrationIncomingState *mis) return ret; } +static int loadvm_postcopy_handle_switchover_start(void) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + int ret; + + if (!se->ops || !se->ops->switchover_start) { + continue; + } + + ret = se->ops->switchover_start(se->opaque); + if (ret < 0) { + return ret; + } + } + + return 0; +} + /* * Process an incoming 'QEMU_VM_COMMAND' * 0 just a normal return @@ -2481,6 +2517,9 @@ static int loadvm_process_command(QEMUFile *f) case MIG_CMD_ENABLE_COLO: return loadvm_process_enable_colo(mis); + + case MIG_CMD_SWITCHOVER_START: + return loadvm_postcopy_handle_switchover_start(); } return 0; diff --git a/migration/savevm.h b/migration/savevm.h index 7957460062..58f871a7ed 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -53,6 +53,7 @@ void qemu_savevm_send_postcopy_listen(QEMUFile *f); void qemu_savevm_send_postcopy_run(QEMUFile *f); void qemu_savevm_send_postcopy_resume(QEMUFile *f); void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name); +void qemu_savevm_maybe_send_switchover_start(QEMUFile *f); void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, uint16_t len, diff --git a/migration/trace-events b/migration/trace-events index 58c0f07f5b..c506e11a2e 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -39,6 +39,7 @@ savevm_send_postcopy_run(void) "" savevm_send_postcopy_resume(void) "" savevm_send_colo_enable(void) "" savevm_send_recv_bitmap(char *name) "%s" +savevm_send_switchover_start(void) "" savevm_state_setup(void) "" savevm_state_resume_prepare(void) "" savevm_state_header(void) "" diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 8e1fbf4c9d..67631ac43e 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -620,7 +620,9 @@ class MigrationDump(object): QEMU_VM_SUBSECTION = 0x05 QEMU_VM_VMDESCRIPTION = 0x06 QEMU_VM_CONFIGURATION = 0x07 + QEMU_VM_COMMAND = 0x08 QEMU_VM_SECTION_FOOTER= 0x7e + QEMU_MIG_CMD_SWITCHOVER_START = 0x0b def __init__(self, filename): self.section_classes = { @@ -685,6 +687,15 @@ class MigrationDump(object): elif section_type == self.QEMU_VM_SECTION_PART or section_type == self.QEMU_VM_SECTION_END: section_id = file.read32() self.sections[section_id].read() + elif section_type == self.QEMU_VM_COMMAND: + command_type = file.read16() + command_data_len = file.read16() + if command_type != self.QEMU_MIG_CMD_SWITCHOVER_START: + raise Exception("Unknown QEMU_VM_COMMAND: %x" % + (command_type)) + if command_data_len != 0: + raise Exception("Invalid SWITCHOVER_START length: %x" % + (command_data_len)) elif section_type == self.QEMU_VM_SECTION_FOOTER: read_section_id = file.read32() if read_section_id != section_id: From a30363db082a6947794be6e085ad9437798ec211 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:33 +0100 Subject: [PATCH 2176/2892] migration: Add qemu_loadvm_load_state_buffer() and its handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_loadvm_load_state_buffer() and its load_state_buffer SaveVMHandler allow providing device state buffer to explicitly specified device via its idstr and instance id. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/71ca753286b87831ced4afd422e2e2bed071af25.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/register.h | 15 +++++++++++++++ migration/savevm.c | 23 +++++++++++++++++++++++ migration/savevm.h | 3 +++ 3 files changed, 41 insertions(+) diff --git a/include/migration/register.h b/include/migration/register.h index ff0faf5f68..58891aa54b 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -229,6 +229,21 @@ typedef struct SaveVMHandlers { */ int (*load_state)(QEMUFile *f, void *opaque, int version_id); + /** + * @load_state_buffer (invoked outside the BQL) + * + * Load device state buffer provided to qemu_loadvm_load_state_buffer(). + * + * @opaque: data pointer passed to register_savevm_live() + * @buf: the data buffer to load + * @len: the data length in buffer + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for errors. + */ + bool (*load_state_buffer)(void *opaque, char *buf, size_t len, + Error **errp); + /** * @load_setup * diff --git a/migration/savevm.c b/migration/savevm.c index faebf47ef5..7c1aa8ad7b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -3060,6 +3060,29 @@ int qemu_loadvm_approve_switchover(void) return migrate_send_rp_switchover_ack(mis); } +bool qemu_loadvm_load_state_buffer(const char *idstr, uint32_t instance_id, + char *buf, size_t len, Error **errp) +{ + SaveStateEntry *se; + + se = find_se(idstr, instance_id); + if (!se) { + error_setg(errp, + "Unknown idstr %s or instance id %u for load state buffer", + idstr, instance_id); + return false; + } + + if (!se->ops || !se->ops->load_state_buffer) { + error_setg(errp, + "idstr %s / instance %u has no load state buffer operation", + idstr, instance_id); + return false; + } + + return se->ops->load_state_buffer(se->opaque, buf, len, errp); +} + bool save_snapshot(const char *name, bool overwrite, const char *vmstate, bool has_devices, strList *devices, Error **errp) { diff --git a/migration/savevm.h b/migration/savevm.h index 58f871a7ed..cb58434a94 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -71,4 +71,7 @@ int qemu_loadvm_approve_switchover(void); int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool in_postcopy); +bool qemu_loadvm_load_state_buffer(const char *idstr, uint32_t instance_id, + char *buf, size_t len, Error **errp); + #endif From 6a76eb4872f632974307bf12cb7f2416a77ad4a8 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 5 Mar 2025 17:49:10 +0100 Subject: [PATCH 2177/2892] migration: Always take BQL for migration_incoming_state_destroy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers to migration_incoming_state_destroy() other than postcopy_ram_listen_thread() do this call with BQL held. Since migration_incoming_state_destroy() ultimately calls "load_cleanup" SaveVMHandlers and it will soon call BQL-sensitive code it makes sense to always call that function under BQL rather than to have it deal with both cases (with BQL and without BQL). Add the necessary bql_lock() and bql_unlock() to postcopy_ram_listen_thread(). qemu_loadvm_state_main() in postcopy_ram_listen_thread() could call "load_state" SaveVMHandlers that are expecting BQL to be held. In principle, the only devices that should be arriving on migration channel serviced by postcopy_ram_listen_thread() are those that are postcopiable and whose load handlers are safe to be called without BQL being held. But nothing currently prevents the source from sending data for "unsafe" devices which would cause trouble there. Add a TODO comment there so it's clear that it would be good to improve handling of such (erroneous) case in the future. Acked-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/21bb5ca337b1d5a802e697f553f37faf296b5ff4.1741193259.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/migration.c | 13 +++++++++++++ migration/savevm.c | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index 9e9db26667..0bf70ea971 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -402,10 +402,23 @@ void migration_incoming_state_destroy(void) struct MigrationIncomingState *mis = migration_incoming_get_current(); multifd_recv_cleanup(); + /* * RAM state cleanup needs to happen after multifd cleanup, because * multifd threads can use some of its states (receivedmap). + * The VFIO load_cleanup() implementation is BQL-sensitive. It requires + * BQL must NOT be taken when recycling load threads, so that it won't + * block the load threads from making progress on address space + * modification operations. + * + * To make it work, we could try to not take BQL for all load_cleanup(), + * or conditionally unlock BQL only if bql_locked() in VFIO. + * + * Since most existing call sites take BQL for load_cleanup(), make + * it simple by taking BQL always as the rule, so that VFIO can unlock + * BQL and retake unconditionally. */ + assert(bql_locked()); qemu_loadvm_state_cleanup(); if (mis->to_src_file) { diff --git a/migration/savevm.c b/migration/savevm.c index 7c1aa8ad7b..3e86b572cf 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1986,6 +1986,8 @@ static void *postcopy_ram_listen_thread(void *opaque) * in qemu_file, and thus we must be blocking now. */ qemu_file_set_blocking(f, true); + + /* TODO: sanity check that only postcopiable data will be loaded here */ load_res = qemu_loadvm_state_main(f, mis); /* @@ -2046,7 +2048,9 @@ static void *postcopy_ram_listen_thread(void *opaque) * (If something broke then qemu will have to exit anyway since it's * got a bad migration state). */ + bql_lock(); migration_incoming_state_destroy(); + bql_unlock(); rcu_unregister_thread(); mis->have_listen_thread = false; From 18eb55546a54e443d94a4c49286348176ad4b00a Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:35 +0100 Subject: [PATCH 2178/2892] error: define g_autoptr() cleanup function for the Error type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatic memory management helps avoid memory safety issues. Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/a5843c5fa64d7e5239a4316092ec0ef0d10c2320.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/qapi/error.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/qapi/error.h b/include/qapi/error.h index f5fe216262..41e3816380 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -437,6 +437,8 @@ Error *error_copy(const Error *err); */ void error_free(Error *err); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free) + /* * Convenience function to assert that *@errp is set, then silently free it. */ From b1937fd1eb8360d0dc0abb0a8da221d8edce3733 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:36 +0100 Subject: [PATCH 2179/2892] migration: Add thread pool of optional load threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some drivers might want to make use of auxiliary helper threads during VM state loading, for example to make sure that their blocking (sync) I/O operations don't block the rest of the migration process. Add a migration core managed thread pool to facilitate this use case. The migration core will wait for these threads to finish before (re)starting the VM at destination. Reviewed-by: Fabiano Rosas Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/b09fd70369b6159c75847e69f235cb908b02570c.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/misc.h | 3 ++ include/qemu/typedefs.h | 2 + migration/migration.c | 2 +- migration/migration.h | 5 +++ migration/savevm.c | 95 +++++++++++++++++++++++++++++++++++++++- migration/savevm.h | 2 +- 6 files changed, 105 insertions(+), 4 deletions(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index c660be8095..4c171f4e89 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -45,9 +45,12 @@ bool migrate_ram_is_ignored(RAMBlock *block); /* migration/block.c */ AnnounceParameters *migrate_announce_params(void); + /* migration/savevm.c */ void dump_vmstate_json_to_file(FILE *out_fp); +void qemu_loadvm_start_load_thread(MigrationLoadThread function, + void *opaque); /* migration/migration.c */ void migration_object_init(void); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 3d84efcac4..fd23ff7771 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -131,5 +131,7 @@ typedef struct IRQState *qemu_irq; * Function types */ typedef void (*qemu_irq_handler)(void *opaque, int n, int level); +typedef bool (*MigrationLoadThread)(void *opaque, bool *should_quit, + Error **errp); #endif /* QEMU_TYPEDEFS_H */ diff --git a/migration/migration.c b/migration/migration.c index 0bf70ea971..1833cfe358 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -419,7 +419,7 @@ void migration_incoming_state_destroy(void) * BQL and retake unconditionally. */ assert(bql_locked()); - qemu_loadvm_state_cleanup(); + qemu_loadvm_state_cleanup(mis); if (mis->to_src_file) { /* Tell source that we are done */ diff --git a/migration/migration.h b/migration/migration.h index 7b4278e2a3..d53f7cad84 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -43,6 +43,7 @@ #define MIGRATION_THREAD_DST_PREEMPT "mig/dst/preempt" struct PostcopyBlocktimeContext; +typedef struct ThreadPool ThreadPool; #define MIGRATION_RESUME_ACK_VALUE (1) @@ -187,6 +188,10 @@ struct MigrationIncomingState { Coroutine *colo_incoming_co; QemuSemaphore colo_incoming_sem; + /* Optional load threads pool and its thread exit request flag */ + ThreadPool *load_threads; + bool load_threads_abort; + /* * PostcopyBlocktimeContext to keep information for postcopy * live migration, to calculate vCPU block time diff --git a/migration/savevm.c b/migration/savevm.c index 3e86b572cf..1abc365570 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -54,6 +54,7 @@ #include "qemu/job.h" #include "qemu/main-loop.h" #include "block/snapshot.h" +#include "block/thread-pool.h" #include "qemu/cutils.h" #include "io/channel-buffer.h" #include "io/channel-file.h" @@ -131,6 +132,35 @@ static struct mig_cmd_args { * generic extendable format with an exception for two old entities. */ +/***********************************************************/ +/* Optional load threads pool support */ + +static void qemu_loadvm_thread_pool_create(MigrationIncomingState *mis) +{ + assert(!mis->load_threads); + mis->load_threads = thread_pool_new(); + mis->load_threads_abort = false; +} + +static void qemu_loadvm_thread_pool_destroy(MigrationIncomingState *mis) +{ + qatomic_set(&mis->load_threads_abort, true); + + bql_unlock(); /* Load threads might be waiting for BQL */ + g_clear_pointer(&mis->load_threads, thread_pool_free); + bql_lock(); +} + +static bool qemu_loadvm_thread_pool_wait(MigrationState *s, + MigrationIncomingState *mis) +{ + bql_unlock(); /* Let load threads do work requiring BQL */ + thread_pool_wait(mis->load_threads); + bql_lock(); + + return !migrate_has_error(s); +} + /***********************************************************/ /* savevm/loadvm support */ @@ -2783,16 +2813,68 @@ static int qemu_loadvm_state_setup(QEMUFile *f, Error **errp) return 0; } -void qemu_loadvm_state_cleanup(void) +struct LoadThreadData { + MigrationLoadThread function; + void *opaque; +}; + +static int qemu_loadvm_load_thread(void *thread_opaque) +{ + struct LoadThreadData *data = thread_opaque; + MigrationIncomingState *mis = migration_incoming_get_current(); + g_autoptr(Error) local_err = NULL; + + if (!data->function(data->opaque, &mis->load_threads_abort, &local_err)) { + MigrationState *s = migrate_get_current(); + + /* + * Can't set load_threads_abort here since processing of main migration + * channel data could still be happening, resulting in launching of new + * load threads. + */ + + assert(local_err); + + /* + * In case of multiple load threads failing which thread error + * return we end setting is purely arbitrary. + */ + migrate_set_error(s, local_err); + } + + return 0; +} + +void qemu_loadvm_start_load_thread(MigrationLoadThread function, + void *opaque) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + struct LoadThreadData *data; + + /* We only set it from this thread so it's okay to read it directly */ + assert(!mis->load_threads_abort); + + data = g_new(struct LoadThreadData, 1); + data->function = function; + data->opaque = opaque; + + thread_pool_submit_immediate(mis->load_threads, qemu_loadvm_load_thread, + data, g_free); +} + +void qemu_loadvm_state_cleanup(MigrationIncomingState *mis) { SaveStateEntry *se; trace_loadvm_state_cleanup(); + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->ops && se->ops->load_cleanup) { se->ops->load_cleanup(se->opaque); } } + + qemu_loadvm_thread_pool_destroy(mis); } /* Return true if we should continue the migration, or false. */ @@ -2943,6 +3025,7 @@ out: int qemu_loadvm_state(QEMUFile *f) { + MigrationState *s = migrate_get_current(); MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; int ret; @@ -2952,6 +3035,8 @@ int qemu_loadvm_state(QEMUFile *f) return -EINVAL; } + qemu_loadvm_thread_pool_create(mis); + ret = qemu_loadvm_state_header(f); if (ret) { return ret; @@ -2983,12 +3068,18 @@ int qemu_loadvm_state(QEMUFile *f) /* When reaching here, it must be precopy */ if (ret == 0) { - if (migrate_has_error(migrate_get_current())) { + if (migrate_has_error(migrate_get_current()) || + !qemu_loadvm_thread_pool_wait(s, mis)) { ret = -EINVAL; } else { ret = qemu_file_get_error(f); } } + /* + * Set this flag unconditionally so we'll catch further attempts to + * start additional threads via an appropriate assert() + */ + qatomic_set(&mis->load_threads_abort, true); /* * Try to read in the VMDESC section as well, so that dumping tools that diff --git a/migration/savevm.h b/migration/savevm.h index cb58434a94..138c39a7f9 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -64,7 +64,7 @@ void qemu_savevm_live_state(QEMUFile *f); int qemu_save_device_state(QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); -void qemu_loadvm_state_cleanup(void); +void qemu_loadvm_state_cleanup(MigrationIncomingState *mis); int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); int qemu_load_device_state(QEMUFile *f); int qemu_loadvm_approve_switchover(void); From 8050c435b70b6805f05441a8a7cc6a3d70c3ee71 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:37 +0100 Subject: [PATCH 2180/2892] migration/multifd: Split packet into header and RAM data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read packet header first so in the future we will be able to differentiate between a RAM multifd packet and a device state multifd packet. Since these two are of different size we can't read the packet body until we know which packet type it is. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/832ad055fe447561ac1ad565d61658660cb3f63f.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/multifd.c | 55 ++++++++++++++++++++++++++++++++++++--------- migration/multifd.h | 5 +++++ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 215ad0414a..3b47e63c2c 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -209,10 +209,10 @@ void multifd_send_fill_packet(MultiFDSendParams *p) memset(packet, 0, p->packet_len); - packet->magic = cpu_to_be32(MULTIFD_MAGIC); - packet->version = cpu_to_be32(MULTIFD_VERSION); + packet->hdr.magic = cpu_to_be32(MULTIFD_MAGIC); + packet->hdr.version = cpu_to_be32(MULTIFD_VERSION); - packet->flags = cpu_to_be32(p->flags); + packet->hdr.flags = cpu_to_be32(p->flags); packet->next_packet_size = cpu_to_be32(p->next_packet_size); packet_num = qatomic_fetch_inc(&multifd_send_state->packet_num); @@ -228,12 +228,12 @@ void multifd_send_fill_packet(MultiFDSendParams *p) p->flags, p->next_packet_size); } -static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +static int multifd_recv_unfill_packet_header(MultiFDRecvParams *p, + const MultiFDPacketHdr_t *hdr, + Error **errp) { - const MultiFDPacket_t *packet = p->packet; - uint32_t magic = be32_to_cpu(packet->magic); - uint32_t version = be32_to_cpu(packet->version); - int ret = 0; + uint32_t magic = be32_to_cpu(hdr->magic); + uint32_t version = be32_to_cpu(hdr->version); if (magic != MULTIFD_MAGIC) { error_setg(errp, "multifd: received packet magic %x, expected %x", @@ -247,7 +247,16 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) return -1; } - p->flags = be32_to_cpu(packet->flags); + p->flags = be32_to_cpu(hdr->flags); + + return 0; +} + +static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + const MultiFDPacket_t *packet = p->packet; + int ret = 0; + p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); p->packets_recved++; @@ -1165,14 +1174,18 @@ static void *multifd_recv_thread(void *opaque) } while (true) { + MultiFDPacketHdr_t hdr; uint32_t flags = 0; bool has_data = false; + uint8_t *pkt_buf; + size_t pkt_len; + p->normal_num = 0; if (use_packets) { struct iovec iov = { - .iov_base = (void *)p->packet, - .iov_len = p->packet_len + .iov_base = (void *)&hdr, + .iov_len = sizeof(hdr) }; if (multifd_recv_should_exit()) { @@ -1191,6 +1204,26 @@ static void *multifd_recv_thread(void *opaque) break; } + ret = multifd_recv_unfill_packet_header(p, &hdr, &local_err); + if (ret) { + break; + } + + pkt_buf = (uint8_t *)p->packet + sizeof(hdr); + pkt_len = p->packet_len - sizeof(hdr); + + ret = qio_channel_read_all_eof(p->c, (char *)pkt_buf, pkt_len, + &local_err); + if (!ret) { + /* EOF */ + error_setg(&local_err, "multifd: unexpected EOF after packet header"); + break; + } + + if (ret == -1) { + break; + } + qemu_mutex_lock(&p->mutex); ret = multifd_recv_unfill_packet(p, &local_err); if (ret) { diff --git a/migration/multifd.h b/migration/multifd.h index cf408ff721..f7156f66c0 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -69,6 +69,11 @@ typedef struct { uint32_t magic; uint32_t version; uint32_t flags; +} __attribute__((packed)) MultiFDPacketHdr_t; + +typedef struct { + MultiFDPacketHdr_t hdr; + /* maximum number of allocated pages */ uint32_t pages_alloc; /* non zero pages */ From f588f3c46ae278661cdad5f1198a455e3ec9f910 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:38 +0100 Subject: [PATCH 2181/2892] migration/multifd: Device state transfer support - receive side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a basic support for receiving device state via multifd channels - channels that are shared with RAM transfers. Depending whether MULTIFD_FLAG_DEVICE_STATE flag is present or not in the packet header either device state (MultiFDPacketDeviceState_t) or RAM data (existing MultiFDPacket_t) is read. The received device state data is provided to qemu_loadvm_load_state_buffer() function for processing in the device's load_state_buffer handler. Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/9b86f806c134e7815ecce0eee84f0e0e34aa0146.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/multifd.c | 101 +++++++++++++++++++++++++++++++++++++++----- migration/multifd.h | 19 ++++++++- 2 files changed, 108 insertions(+), 12 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 3b47e63c2c..01f427d8ed 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -21,6 +21,7 @@ #include "file.h" #include "migration.h" #include "migration-stats.h" +#include "savevm.h" #include "socket.h" #include "tls.h" #include "qemu-file.h" @@ -252,14 +253,24 @@ static int multifd_recv_unfill_packet_header(MultiFDRecvParams *p, return 0; } -static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +static int multifd_recv_unfill_packet_device_state(MultiFDRecvParams *p, + Error **errp) +{ + MultiFDPacketDeviceState_t *packet = p->packet_dev_state; + + packet->instance_id = be32_to_cpu(packet->instance_id); + p->next_packet_size = be32_to_cpu(packet->next_packet_size); + + return 0; +} + +static int multifd_recv_unfill_packet_ram(MultiFDRecvParams *p, Error **errp) { const MultiFDPacket_t *packet = p->packet; int ret = 0; p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); - p->packets_recved++; /* Always unfill, old QEMUs (<9.0) send data along with SYNC */ ret = multifd_ram_unfill_packet(p, errp); @@ -270,6 +281,17 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) return ret; } +static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + p->packets_recved++; + + if (p->flags & MULTIFD_FLAG_DEVICE_STATE) { + return multifd_recv_unfill_packet_device_state(p, errp); + } + + return multifd_recv_unfill_packet_ram(p, errp); +} + static bool multifd_send_should_exit(void) { return qatomic_read(&multifd_send_state->exiting); @@ -1057,6 +1079,7 @@ static void multifd_recv_cleanup_channel(MultiFDRecvParams *p) p->packet_len = 0; g_free(p->packet); p->packet = NULL; + g_clear_pointer(&p->packet_dev_state, g_free); g_free(p->normal); p->normal = NULL; g_free(p->zero); @@ -1158,6 +1181,34 @@ void multifd_recv_sync_main(void) trace_multifd_recv_sync_main(multifd_recv_state->packet_num); } +static int multifd_device_state_recv(MultiFDRecvParams *p, Error **errp) +{ + g_autofree char *dev_state_buf = NULL; + int ret; + + dev_state_buf = g_malloc(p->next_packet_size); + + ret = qio_channel_read_all(p->c, dev_state_buf, p->next_packet_size, errp); + if (ret != 0) { + return ret; + } + + if (p->packet_dev_state->idstr[sizeof(p->packet_dev_state->idstr) - 1] + != 0) { + error_setg(errp, "unterminated multifd device state idstr"); + return -1; + } + + if (!qemu_loadvm_load_state_buffer(p->packet_dev_state->idstr, + p->packet_dev_state->instance_id, + dev_state_buf, p->next_packet_size, + errp)) { + ret = -1; + } + + return ret; +} + static void *multifd_recv_thread(void *opaque) { MigrationState *s = migrate_get_current(); @@ -1176,6 +1227,7 @@ static void *multifd_recv_thread(void *opaque) while (true) { MultiFDPacketHdr_t hdr; uint32_t flags = 0; + bool is_device_state = false; bool has_data = false; uint8_t *pkt_buf; size_t pkt_len; @@ -1209,8 +1261,14 @@ static void *multifd_recv_thread(void *opaque) break; } - pkt_buf = (uint8_t *)p->packet + sizeof(hdr); - pkt_len = p->packet_len - sizeof(hdr); + is_device_state = p->flags & MULTIFD_FLAG_DEVICE_STATE; + if (is_device_state) { + pkt_buf = (uint8_t *)p->packet_dev_state + sizeof(hdr); + pkt_len = sizeof(*p->packet_dev_state) - sizeof(hdr); + } else { + pkt_buf = (uint8_t *)p->packet + sizeof(hdr); + pkt_len = p->packet_len - sizeof(hdr); + } ret = qio_channel_read_all_eof(p->c, (char *)pkt_buf, pkt_len, &local_err); @@ -1235,12 +1293,17 @@ static void *multifd_recv_thread(void *opaque) /* recv methods don't know how to handle the SYNC flag */ p->flags &= ~MULTIFD_FLAG_SYNC; - /* - * Even if it's a SYNC packet, this needs to be set - * because older QEMUs (<9.0) still send data along with - * the SYNC packet. - */ - has_data = p->normal_num || p->zero_num; + if (is_device_state) { + has_data = p->next_packet_size > 0; + } else { + /* + * Even if it's a SYNC packet, this needs to be set + * because older QEMUs (<9.0) still send data along with + * the SYNC packet. + */ + has_data = p->normal_num || p->zero_num; + } + qemu_mutex_unlock(&p->mutex); } else { /* @@ -1269,14 +1332,29 @@ static void *multifd_recv_thread(void *opaque) } if (has_data) { - ret = multifd_recv_state->ops->recv(p, &local_err); + if (is_device_state) { + assert(use_packets); + ret = multifd_device_state_recv(p, &local_err); + } else { + ret = multifd_recv_state->ops->recv(p, &local_err); + } if (ret != 0) { break; } + } else if (is_device_state) { + error_setg(&local_err, + "multifd: received empty device state packet"); + break; } if (use_packets) { if (flags & MULTIFD_FLAG_SYNC) { + if (is_device_state) { + error_setg(&local_err, + "multifd: received SYNC device state packet"); + break; + } + qemu_sem_post(&multifd_recv_state->sem_sync); qemu_sem_wait(&p->sem_sync); } @@ -1345,6 +1423,7 @@ int multifd_recv_setup(Error **errp) p->packet_len = sizeof(MultiFDPacket_t) + sizeof(uint64_t) * page_count; p->packet = g_malloc0(p->packet_len); + p->packet_dev_state = g_malloc0(sizeof(*p->packet_dev_state)); } p->name = g_strdup_printf(MIGRATION_THREAD_DST_MULTIFD, i); p->normal = g_new0(ram_addr_t, page_count); diff --git a/migration/multifd.h b/migration/multifd.h index f7156f66c0..d682c5a9b7 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -62,6 +62,12 @@ MultiFDRecvData *multifd_get_recv_data(void); #define MULTIFD_FLAG_UADK (8 << 1) #define MULTIFD_FLAG_QATZIP (16 << 1) +/* + * If set it means that this packet contains device state + * (MultiFDPacketDeviceState_t), not RAM data (MultiFDPacket_t). + */ +#define MULTIFD_FLAG_DEVICE_STATE (32 << 1) + /* This value needs to be a multiple of qemu_target_page_size() */ #define MULTIFD_PACKET_SIZE (512 * 1024) @@ -94,6 +100,16 @@ typedef struct { uint64_t offset[]; } __attribute__((packed)) MultiFDPacket_t; +typedef struct { + MultiFDPacketHdr_t hdr; + + char idstr[256]; + uint32_t instance_id; + + /* size of the next packet that contains the actual data */ + uint32_t next_packet_size; +} __attribute__((packed)) MultiFDPacketDeviceState_t; + typedef struct { /* number of used pages */ uint32_t num; @@ -227,8 +243,9 @@ typedef struct { /* thread local variables. No locking required */ - /* pointer to the packet */ + /* pointers to the possible packet types */ MultiFDPacket_t *packet; + MultiFDPacketDeviceState_t *packet_dev_state; /* size of the next packet that contains pages */ uint32_t next_packet_size; /* packets received through this channel */ From d19cc4dca0b21af95fee36a2ddad34eb4bd6b67f Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:39 +0100 Subject: [PATCH 2182/2892] migration/multifd: Make multifd_send() thread safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit multifd_send() function is currently not thread safe, make it thread safe by holding a lock during its execution. This way it will be possible to safely call it concurrently from multiple threads. Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/dd0f3bcc02ca96a7d523ca58ea69e495a33b453b.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/multifd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/migration/multifd.c b/migration/multifd.c index 01f427d8ed..add6f86175 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -50,6 +50,10 @@ typedef struct { struct { MultiFDSendParams *params; + + /* multifd_send() body is not thread safe, needs serialization */ + QemuMutex multifd_send_mutex; + /* * Global number of generated multifd packets. * @@ -339,6 +343,8 @@ bool multifd_send(MultiFDSendData **send_data) return false; } + QEMU_LOCK_GUARD(&multifd_send_state->multifd_send_mutex); + /* We wait here, until at least one channel is ready */ qemu_sem_wait(&multifd_send_state->channels_ready); @@ -507,6 +513,7 @@ static void multifd_send_cleanup_state(void) socket_cleanup_outgoing_migration(); qemu_sem_destroy(&multifd_send_state->channels_created); qemu_sem_destroy(&multifd_send_state->channels_ready); + qemu_mutex_destroy(&multifd_send_state->multifd_send_mutex); g_free(multifd_send_state->params); multifd_send_state->params = NULL; g_free(multifd_send_state); @@ -887,6 +894,7 @@ bool multifd_send_setup(void) thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); + qemu_mutex_init(&multifd_send_state->multifd_send_mutex); qemu_sem_init(&multifd_send_state->channels_created, 0); qemu_sem_init(&multifd_send_state->channels_ready, 0); qatomic_set(&multifd_send_state->exiting, 0); From 7ecfab1ddd3e6678c6a0b12d348d82cfaaa406ff Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:40 +0100 Subject: [PATCH 2183/2892] migration/multifd: Add an explicit MultiFDSendData destructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way if there are fields there that needs explicit disposal (like, for example, some attached buffers) they will be handled appropriately. Add a related assert to multifd_set_payload_type() in order to make sure that this function is only used to fill a previously empty MultiFDSendData with some payload, not the other way around. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/6755205f2b95abbed251f87061feee1c0e410836.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/multifd-nocomp.c | 3 +-- migration/multifd.c | 31 ++++++++++++++++++++++++++++--- migration/multifd.h | 5 +++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index 1325dba97c..e46e79d8b2 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -42,8 +42,7 @@ void multifd_ram_save_setup(void) void multifd_ram_save_cleanup(void) { - g_free(multifd_ram_send); - multifd_ram_send = NULL; + g_clear_pointer(&multifd_ram_send, multifd_send_data_free); } static void multifd_set_file_bitmap(MultiFDSendParams *p) diff --git a/migration/multifd.c b/migration/multifd.c index add6f86175..c8508cadab 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -123,6 +123,32 @@ MultiFDSendData *multifd_send_data_alloc(void) return g_malloc0(size_minus_payload + max_payload_size); } +void multifd_send_data_clear(MultiFDSendData *data) +{ + if (multifd_payload_empty(data)) { + return; + } + + switch (data->type) { + default: + /* Nothing to do */ + break; + } + + data->type = MULTIFD_PAYLOAD_NONE; +} + +void multifd_send_data_free(MultiFDSendData *data) +{ + if (!data) { + return; + } + + multifd_send_data_clear(data); + + g_free(data); +} + static bool multifd_use_packets(void) { return !migrate_mapped_ram(); @@ -496,8 +522,7 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp) qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; - g_free(p->data); - p->data = NULL; + g_clear_pointer(&p->data, multifd_send_data_free); p->packet_len = 0; g_free(p->packet); p->packet = NULL; @@ -695,7 +720,7 @@ static void *multifd_send_thread(void *opaque) (uint64_t)p->next_packet_size + p->packet_len); p->next_packet_size = 0; - multifd_set_payload_type(p->data, MULTIFD_PAYLOAD_NONE); + multifd_send_data_clear(p->data); /* * Making sure p->data is published before saying "we're diff --git a/migration/multifd.h b/migration/multifd.h index d682c5a9b7..8d639eec69 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -149,6 +149,9 @@ static inline bool multifd_payload_empty(MultiFDSendData *data) static inline void multifd_set_payload_type(MultiFDSendData *data, MultiFDPayloadType type) { + assert(multifd_payload_empty(data)); + assert(type != MULTIFD_PAYLOAD_NONE); + data->type = type; } @@ -365,6 +368,8 @@ static inline void multifd_send_prepare_header(MultiFDSendParams *p) void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); bool multifd_send(MultiFDSendData **send_data); MultiFDSendData *multifd_send_data_alloc(void); +void multifd_send_data_clear(MultiFDSendData *data); +void multifd_send_data_free(MultiFDSendData *data); static inline uint32_t multifd_ram_page_size(void) { From 0525b91a0b993f95d29b2ea84155e7e4366c120e Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:41 +0100 Subject: [PATCH 2184/2892] migration/multifd: Device state transfer support - send side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new function multifd_queue_device_state() is provided for device to queue its state for transmission via a multifd channel. Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/ebd55768d3e5fecb5eb3f197bad9c0c07e5bc084.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/misc.h | 4 ++ migration/meson.build | 1 + migration/multifd-device-state.c | 118 +++++++++++++++++++++++++++++++ migration/multifd-nocomp.c | 14 +++- migration/multifd.c | 42 +++++++++-- migration/multifd.h | 34 ++++++--- 6 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 migration/multifd-device-state.c diff --git a/include/migration/misc.h b/include/migration/misc.h index 4c171f4e89..bd3b725fa0 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -118,4 +118,8 @@ bool migrate_is_uri(const char *uri); bool migrate_uri_parse(const char *uri, MigrationChannel **channel, Error **errp); +/* migration/multifd-device-state.c */ +bool multifd_queue_device_state(char *idstr, uint32_t instance_id, + char *data, size_t len); + #endif diff --git a/migration/meson.build b/migration/meson.build index d3bfe84d62..9aa48b290e 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -25,6 +25,7 @@ system_ss.add(files( 'migration-hmp-cmds.c', 'migration.c', 'multifd.c', + 'multifd-device-state.c', 'multifd-nocomp.c', 'multifd-zlib.c', 'multifd-zero-page.c', diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c new file mode 100644 index 0000000000..e383e75b1a --- /dev/null +++ b/migration/multifd-device-state.c @@ -0,0 +1,118 @@ +/* + * Multifd device state migration + * + * Copyright (C) 2024,2025 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/lockable.h" +#include "migration/misc.h" +#include "multifd.h" + +static struct { + QemuMutex queue_job_mutex; + + MultiFDSendData *send_data; +} *multifd_send_device_state; + +size_t multifd_device_state_payload_size(void) +{ + return sizeof(MultiFDDeviceState_t); +} + +void multifd_device_state_send_setup(void) +{ + assert(!multifd_send_device_state); + multifd_send_device_state = g_malloc(sizeof(*multifd_send_device_state)); + + qemu_mutex_init(&multifd_send_device_state->queue_job_mutex); + + multifd_send_device_state->send_data = multifd_send_data_alloc(); +} + +void multifd_device_state_send_cleanup(void) +{ + g_clear_pointer(&multifd_send_device_state->send_data, + multifd_send_data_free); + + qemu_mutex_destroy(&multifd_send_device_state->queue_job_mutex); + + g_clear_pointer(&multifd_send_device_state, g_free); +} + +void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state) +{ + g_clear_pointer(&device_state->idstr, g_free); + g_clear_pointer(&device_state->buf, g_free); +} + +static void multifd_device_state_fill_packet(MultiFDSendParams *p) +{ + MultiFDDeviceState_t *device_state = &p->data->u.device_state; + MultiFDPacketDeviceState_t *packet = p->packet_device_state; + + packet->hdr.flags = cpu_to_be32(p->flags); + strncpy(packet->idstr, device_state->idstr, sizeof(packet->idstr) - 1); + packet->idstr[sizeof(packet->idstr) - 1] = 0; + packet->instance_id = cpu_to_be32(device_state->instance_id); + packet->next_packet_size = cpu_to_be32(p->next_packet_size); +} + +static void multifd_prepare_header_device_state(MultiFDSendParams *p) +{ + p->iov[0].iov_len = sizeof(*p->packet_device_state); + p->iov[0].iov_base = p->packet_device_state; + p->iovs_num++; +} + +void multifd_device_state_send_prepare(MultiFDSendParams *p) +{ + MultiFDDeviceState_t *device_state = &p->data->u.device_state; + + assert(multifd_payload_device_state(p->data)); + + multifd_prepare_header_device_state(p); + + assert(!(p->flags & MULTIFD_FLAG_SYNC)); + + p->next_packet_size = device_state->buf_len; + if (p->next_packet_size > 0) { + p->iov[p->iovs_num].iov_base = device_state->buf; + p->iov[p->iovs_num].iov_len = p->next_packet_size; + p->iovs_num++; + } + + p->flags |= MULTIFD_FLAG_NOCOMP | MULTIFD_FLAG_DEVICE_STATE; + + multifd_device_state_fill_packet(p); +} + +bool multifd_queue_device_state(char *idstr, uint32_t instance_id, + char *data, size_t len) +{ + /* Device state submissions can come from multiple threads */ + QEMU_LOCK_GUARD(&multifd_send_device_state->queue_job_mutex); + MultiFDDeviceState_t *device_state; + + assert(multifd_payload_empty(multifd_send_device_state->send_data)); + + multifd_set_payload_type(multifd_send_device_state->send_data, + MULTIFD_PAYLOAD_DEVICE_STATE); + device_state = &multifd_send_device_state->send_data->u.device_state; + device_state->idstr = g_strdup(idstr); + device_state->instance_id = instance_id; + device_state->buf = g_memdup2(data, len); + device_state->buf_len = len; + + if (!multifd_send(&multifd_send_device_state->send_data)) { + multifd_send_data_clear(multifd_send_device_state->send_data); + return false; + } + + return true; +} diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index e46e79d8b2..c008046523 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -14,6 +14,7 @@ #include "exec/ramblock.h" #include "exec/target_page.h" #include "file.h" +#include "migration-stats.h" #include "multifd.h" #include "options.h" #include "qapi/error.h" @@ -85,6 +86,13 @@ static void multifd_nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) return; } +static void multifd_ram_prepare_header(MultiFDSendParams *p) +{ + p->iov[0].iov_len = p->packet_len; + p->iov[0].iov_base = p->packet; + p->iovs_num++; +} + static void multifd_send_prepare_iovs(MultiFDSendParams *p) { MultiFDPages_t *pages = &p->data->u.ram; @@ -118,7 +126,7 @@ static int multifd_nocomp_send_prepare(MultiFDSendParams *p, Error **errp) * Only !zerocopy needs the header in IOV; zerocopy will * send it separately. */ - multifd_send_prepare_header(p); + multifd_ram_prepare_header(p); } multifd_send_prepare_iovs(p); @@ -133,6 +141,8 @@ static int multifd_nocomp_send_prepare(MultiFDSendParams *p, Error **errp) if (ret != 0) { return -1; } + + stat64_add(&mig_stats.multifd_bytes, p->packet_len); } return 0; @@ -431,7 +441,7 @@ int multifd_ram_flush_and_sync(QEMUFile *f) bool multifd_send_prepare_common(MultiFDSendParams *p) { MultiFDPages_t *pages = &p->data->u.ram; - multifd_send_prepare_header(p); + multifd_ram_prepare_header(p); multifd_send_zero_page_detect(p); if (!pages->normal_num) { diff --git a/migration/multifd.c b/migration/multifd.c index c8508cadab..3625c9a37c 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/iov.h" #include "qemu/rcu.h" #include "exec/target_page.h" #include "system/system.h" @@ -19,6 +20,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "file.h" +#include "migration/misc.h" #include "migration.h" #include "migration-stats.h" #include "savevm.h" @@ -111,7 +113,9 @@ MultiFDSendData *multifd_send_data_alloc(void) * added to the union in the future are larger than * (MultiFDPages_t + flex array). */ - max_payload_size = MAX(multifd_ram_payload_size(), sizeof(MultiFDPayload)); + max_payload_size = MAX(multifd_ram_payload_size(), + multifd_device_state_payload_size()); + max_payload_size = MAX(max_payload_size, sizeof(MultiFDPayload)); /* * Account for any holes the compiler might insert. We can't pack @@ -130,6 +134,9 @@ void multifd_send_data_clear(MultiFDSendData *data) } switch (data->type) { + case MULTIFD_PAYLOAD_DEVICE_STATE: + multifd_send_data_clear_device_state(&data->u.device_state); + break; default: /* Nothing to do */ break; @@ -232,6 +239,7 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) return msg.id; } +/* Fills a RAM multifd packet */ void multifd_send_fill_packet(MultiFDSendParams *p) { MultiFDPacket_t *packet = p->packet; @@ -524,6 +532,7 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp) p->name = NULL; g_clear_pointer(&p->data, multifd_send_data_free); p->packet_len = 0; + g_clear_pointer(&p->packet_device_state, g_free); g_free(p->packet); p->packet = NULL; multifd_send_state->ops->send_cleanup(p, errp); @@ -536,6 +545,7 @@ static void multifd_send_cleanup_state(void) { file_cleanup_outgoing_migration(); socket_cleanup_outgoing_migration(); + multifd_device_state_send_cleanup(); qemu_sem_destroy(&multifd_send_state->channels_created); qemu_sem_destroy(&multifd_send_state->channels_ready); qemu_mutex_destroy(&multifd_send_state->multifd_send_mutex); @@ -694,16 +704,32 @@ static void *multifd_send_thread(void *opaque) * qatomic_store_release() in multifd_send(). */ if (qatomic_load_acquire(&p->pending_job)) { + bool is_device_state = multifd_payload_device_state(p->data); + size_t total_size; + p->flags = 0; p->iovs_num = 0; assert(!multifd_payload_empty(p->data)); - ret = multifd_send_state->ops->send_prepare(p, &local_err); - if (ret != 0) { - break; + if (is_device_state) { + multifd_device_state_send_prepare(p); + } else { + ret = multifd_send_state->ops->send_prepare(p, &local_err); + if (ret != 0) { + break; + } } + /* + * The packet header in the zerocopy RAM case is accounted for + * in multifd_nocomp_send_prepare() - where it is actually + * being sent. + */ + total_size = iov_size(p->iov, p->iovs_num); + if (migrate_mapped_ram()) { + assert(!is_device_state); + ret = file_write_ramblock_iov(p->c, p->iov, p->iovs_num, &p->data->u.ram, &local_err); } else { @@ -716,8 +742,7 @@ static void *multifd_send_thread(void *opaque) break; } - stat64_add(&mig_stats.multifd_bytes, - (uint64_t)p->next_packet_size + p->packet_len); + stat64_add(&mig_stats.multifd_bytes, total_size); p->next_packet_size = 0; multifd_send_data_clear(p->data); @@ -938,6 +963,9 @@ bool multifd_send_setup(void) p->packet_len = sizeof(MultiFDPacket_t) + sizeof(uint64_t) * page_count; p->packet = g_malloc0(p->packet_len); + p->packet_device_state = g_malloc0(sizeof(*p->packet_device_state)); + p->packet_device_state->hdr.magic = cpu_to_be32(MULTIFD_MAGIC); + p->packet_device_state->hdr.version = cpu_to_be32(MULTIFD_VERSION); } p->name = g_strdup_printf(MIGRATION_THREAD_SRC_MULTIFD, i); p->write_flags = 0; @@ -973,6 +1001,8 @@ bool multifd_send_setup(void) assert(p->iov); } + multifd_device_state_send_setup(); + return true; err: diff --git a/migration/multifd.h b/migration/multifd.h index 8d639eec69..aa679d8bbe 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -127,13 +127,22 @@ struct MultiFDRecvData { off_t file_offset; }; +typedef struct { + char *idstr; + uint32_t instance_id; + char *buf; + size_t buf_len; +} MultiFDDeviceState_t; + typedef enum { MULTIFD_PAYLOAD_NONE, MULTIFD_PAYLOAD_RAM, + MULTIFD_PAYLOAD_DEVICE_STATE, } MultiFDPayloadType; typedef union MultiFDPayload { MultiFDPages_t ram; + MultiFDDeviceState_t device_state; } MultiFDPayload; struct MultiFDSendData { @@ -146,6 +155,11 @@ static inline bool multifd_payload_empty(MultiFDSendData *data) return data->type == MULTIFD_PAYLOAD_NONE; } +static inline bool multifd_payload_device_state(MultiFDSendData *data) +{ + return data->type == MULTIFD_PAYLOAD_DEVICE_STATE; +} + static inline void multifd_set_payload_type(MultiFDSendData *data, MultiFDPayloadType type) { @@ -198,8 +212,9 @@ typedef struct { /* thread local variables. No locking required */ - /* pointer to the packet */ + /* pointers to the possible packet types */ MultiFDPacket_t *packet; + MultiFDPacketDeviceState_t *packet_device_state; /* size of the next packet that contains pages */ uint32_t next_packet_size; /* packets sent through this channel */ @@ -358,13 +373,6 @@ bool multifd_send_prepare_common(MultiFDSendParams *p); void multifd_send_zero_page_detect(MultiFDSendParams *p); void multifd_recv_zero_page_process(MultiFDRecvParams *p); -static inline void multifd_send_prepare_header(MultiFDSendParams *p) -{ - p->iov[0].iov_len = p->packet_len; - p->iov[0].iov_base = p->packet; - p->iovs_num++; -} - void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); bool multifd_send(MultiFDSendData **send_data); MultiFDSendData *multifd_send_data_alloc(void); @@ -389,4 +397,14 @@ bool multifd_ram_sync_per_section(void); size_t multifd_ram_payload_size(void); void multifd_ram_fill_packet(MultiFDSendParams *p); int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); + +size_t multifd_device_state_payload_size(void); + +void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state); + +void multifd_device_state_send_setup(void); +void multifd_device_state_send_cleanup(void); + +void multifd_device_state_send_prepare(MultiFDSendParams *p); + #endif From 99fab22350e4eed1d8a238ed97c77b1a5ee77dd4 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 4 Mar 2025 23:03:42 +0100 Subject: [PATCH 2185/2892] migration/multifd: Make MultiFDSendData a struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The newly introduced device state buffer can be used for either storing VFIO's read() raw data, but already also possible to store generic device states. After noticing that device states may not easily provide a max buffer size (also the fact that RAM MultiFDPages_t after all also want to have flexibility on managing offset[] array), it may not be a good idea to stick with union on MultiFDSendData.. as it won't play well with such flexibility. Switch MultiFDSendData to a struct. It won't consume a lot more space in reality, after all the real buffers were already dynamically allocated, so it's so far only about the two structs (pages, device_state) that will be duplicated, but they're small. With this, we can remove the pretty hard to understand alloc size logic. Because now we can allocate offset[] together with the SendData, and properly free it when the SendData is freed. [MSS: Make sure to clear possible device state payload before freeing MultiFDSendData, remove placeholders for other patches not included] Signed-off-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/7b02baba8e6ddb23ef7c349d312b9b631db09d7e.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/multifd-device-state.c | 5 ----- migration/multifd-nocomp.c | 13 ++++++------- migration/multifd.c | 25 +++++++------------------ migration/multifd.h | 15 +++++++++------ 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c index e383e75b1a..64d8ca1801 100644 --- a/migration/multifd-device-state.c +++ b/migration/multifd-device-state.c @@ -20,11 +20,6 @@ static struct { MultiFDSendData *send_data; } *multifd_send_device_state; -size_t multifd_device_state_payload_size(void) -{ - return sizeof(MultiFDDeviceState_t); -} - void multifd_device_state_send_setup(void) { assert(!multifd_send_device_state); diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index c008046523..ffe75256c9 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -25,15 +25,14 @@ static MultiFDSendData *multifd_ram_send; -size_t multifd_ram_payload_size(void) +void multifd_ram_payload_alloc(MultiFDPages_t *pages) { - uint32_t n = multifd_ram_page_count(); + pages->offset = g_new0(ram_addr_t, multifd_ram_page_count()); +} - /* - * We keep an array of page offsets at the end of MultiFDPages_t, - * add space for it in the allocation. - */ - return sizeof(MultiFDPages_t) + n * sizeof(ram_addr_t); +void multifd_ram_payload_free(MultiFDPages_t *pages) +{ + g_clear_pointer(&pages->offset, g_free); } void multifd_ram_save_setup(void) diff --git a/migration/multifd.c b/migration/multifd.c index 3625c9a37c..dfb5189f0e 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -105,26 +105,12 @@ struct { MultiFDSendData *multifd_send_data_alloc(void) { - size_t max_payload_size, size_minus_payload; + MultiFDSendData *new = g_new0(MultiFDSendData, 1); - /* - * MultiFDPages_t has a flexible array at the end, account for it - * when allocating MultiFDSendData. Use max() in case other types - * added to the union in the future are larger than - * (MultiFDPages_t + flex array). - */ - max_payload_size = MAX(multifd_ram_payload_size(), - multifd_device_state_payload_size()); - max_payload_size = MAX(max_payload_size, sizeof(MultiFDPayload)); + multifd_ram_payload_alloc(&new->u.ram); + /* Device state allocates its payload on-demand */ - /* - * Account for any holes the compiler might insert. We can't pack - * the structure because that misaligns the members and triggers - * Waddress-of-packed-member. - */ - size_minus_payload = sizeof(MultiFDSendData) - sizeof(MultiFDPayload); - - return g_malloc0(size_minus_payload + max_payload_size); + return new; } void multifd_send_data_clear(MultiFDSendData *data) @@ -151,8 +137,11 @@ void multifd_send_data_free(MultiFDSendData *data) return; } + /* This also free's device state payload */ multifd_send_data_clear(data); + multifd_ram_payload_free(&data->u.ram); + g_free(data); } diff --git a/migration/multifd.h b/migration/multifd.h index aa679d8bbe..2d337e7b3b 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -115,9 +115,13 @@ typedef struct { uint32_t num; /* number of normal pages */ uint32_t normal_num; + /* + * Pointer to the ramblock. NOTE: it's caller's responsibility to make + * sure the pointer is always valid! + */ RAMBlock *block; - /* offset of each page */ - ram_addr_t offset[]; + /* offset array of each page, managed by multifd */ + ram_addr_t *offset; } MultiFDPages_t; struct MultiFDRecvData { @@ -140,7 +144,7 @@ typedef enum { MULTIFD_PAYLOAD_DEVICE_STATE, } MultiFDPayloadType; -typedef union MultiFDPayload { +typedef struct MultiFDPayload { MultiFDPages_t ram; MultiFDDeviceState_t device_state; } MultiFDPayload; @@ -394,12 +398,11 @@ void multifd_ram_save_cleanup(void); int multifd_ram_flush_and_sync(QEMUFile *f); bool multifd_ram_sync_per_round(void); bool multifd_ram_sync_per_section(void); -size_t multifd_ram_payload_size(void); +void multifd_ram_payload_alloc(MultiFDPages_t *pages); +void multifd_ram_payload_free(MultiFDPages_t *pages); void multifd_ram_fill_packet(MultiFDSendParams *p); int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); -size_t multifd_device_state_payload_size(void); - void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state); void multifd_device_state_send_setup(void); From a1131aa94256d1007b4267ff9e79c3b4ada6e2b9 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:43 +0100 Subject: [PATCH 2186/2892] migration/multifd: Add multifd_device_state_supported() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since device state transfer via multifd channels requires multifd channels with packets and is currently not compatible with multifd compression add an appropriate query function so device can learn whether it can actually make use of it. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/1ff0d98b85f470e5a33687406e877583b8fab74e.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/misc.h | 1 + migration/multifd-device-state.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/migration/misc.h b/include/migration/misc.h index bd3b725fa0..273ebfca62 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -121,5 +121,6 @@ bool migrate_uri_parse(const char *uri, MigrationChannel **channel, /* migration/multifd-device-state.c */ bool multifd_queue_device_state(char *idstr, uint32_t instance_id, char *data, size_t len); +bool multifd_device_state_supported(void); #endif diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c index 64d8ca1801..3097ffa310 100644 --- a/migration/multifd-device-state.c +++ b/migration/multifd-device-state.c @@ -13,6 +13,7 @@ #include "qemu/lockable.h" #include "migration/misc.h" #include "multifd.h" +#include "options.h" static struct { QemuMutex queue_job_mutex; @@ -111,3 +112,9 @@ bool multifd_queue_device_state(char *idstr, uint32_t instance_id, return true; } + +bool multifd_device_state_supported(void) +{ + return migrate_multifd() && !migrate_mapped_ram() && + migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE; +} From 8305921a91a940023fe971c74eb1e06f2725ebab Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:44 +0100 Subject: [PATCH 2187/2892] migration: Add save_live_complete_precopy_thread handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This SaveVMHandler helps device provide its own asynchronous transmission of the remaining data at the end of a precopy phase via multifd channels, in parallel with the transfer done by save_live_complete_precopy handlers. These threads are launched only when multifd device state transfer is supported. Management of these threads in done in the multifd migration code, wrapping them in the generic thread pool. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Peter Xu Link: https://lore.kernel.org/qemu-devel/eac74a4ca7edd8968bbf72aa07b9041c76364a16.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/misc.h | 17 ++++++ include/migration/register.h | 19 +++++++ include/qemu/typedefs.h | 3 ++ migration/multifd-device-state.c | 92 ++++++++++++++++++++++++++++++++ migration/savevm.c | 40 +++++++++++++- 5 files changed, 170 insertions(+), 1 deletion(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index 273ebfca62..8fd36eba1d 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -119,8 +119,25 @@ bool migrate_uri_parse(const char *uri, MigrationChannel **channel, Error **errp); /* migration/multifd-device-state.c */ +typedef struct SaveLiveCompletePrecopyThreadData { + SaveLiveCompletePrecopyThreadHandler hdlr; + char *idstr; + uint32_t instance_id; + void *handler_opaque; +} SaveLiveCompletePrecopyThreadData; + bool multifd_queue_device_state(char *idstr, uint32_t instance_id, char *data, size_t len); bool multifd_device_state_supported(void); +void +multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr, + char *idstr, uint32_t instance_id, + void *opaque); + +bool multifd_device_state_save_thread_should_exit(void); + +void multifd_abort_device_state_save_threads(void); +bool multifd_join_device_state_save_threads(void); + #endif diff --git a/include/migration/register.h b/include/migration/register.h index 58891aa54b..c041ce32f2 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -105,6 +105,25 @@ typedef struct SaveVMHandlers { */ int (*save_live_complete_precopy)(QEMUFile *f, void *opaque); + /** + * @save_live_complete_precopy_thread (invoked in a separate thread) + * + * Called at the end of a precopy phase from a separate worker thread + * in configurations where multifd device state transfer is supported + * in order to perform asynchronous transmission of the remaining data in + * parallel with @save_live_complete_precopy handlers. + * When postcopy is enabled, devices that support postcopy will skip this + * step. + * + * @d: a #SaveLiveCompletePrecopyThreadData containing parameters that the + * handler may need, including this device section idstr and instance_id, + * and opaque data pointer passed to register_savevm_live(). + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for errors. + */ + SaveLiveCompletePrecopyThreadHandler save_live_complete_precopy_thread; + /* This runs both outside and inside the BQL. */ /** diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index fd23ff7771..42ed4e6be1 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -108,6 +108,7 @@ typedef struct QString QString; typedef struct RAMBlock RAMBlock; typedef struct Range Range; typedef struct ReservedRegion ReservedRegion; +typedef struct SaveLiveCompletePrecopyThreadData SaveLiveCompletePrecopyThreadData; typedef struct SHPCDevice SHPCDevice; typedef struct SSIBus SSIBus; typedef struct TCGCPUOps TCGCPUOps; @@ -133,5 +134,7 @@ typedef struct IRQState *qemu_irq; typedef void (*qemu_irq_handler)(void *opaque, int n, int level); typedef bool (*MigrationLoadThread)(void *opaque, bool *should_quit, Error **errp); +typedef bool (*SaveLiveCompletePrecopyThreadHandler)(SaveLiveCompletePrecopyThreadData *d, + Error **errp); #endif /* QEMU_TYPEDEFS_H */ diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c index 3097ffa310..94222d0eb0 100644 --- a/migration/multifd-device-state.c +++ b/migration/multifd-device-state.c @@ -10,7 +10,10 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/lockable.h" +#include "block/thread-pool.h" +#include "migration.h" #include "migration/misc.h" #include "multifd.h" #include "options.h" @@ -19,6 +22,9 @@ static struct { QemuMutex queue_job_mutex; MultiFDSendData *send_data; + + ThreadPool *threads; + bool threads_abort; } *multifd_send_device_state; void multifd_device_state_send_setup(void) @@ -29,10 +35,14 @@ void multifd_device_state_send_setup(void) qemu_mutex_init(&multifd_send_device_state->queue_job_mutex); multifd_send_device_state->send_data = multifd_send_data_alloc(); + + multifd_send_device_state->threads = thread_pool_new(); + multifd_send_device_state->threads_abort = false; } void multifd_device_state_send_cleanup(void) { + g_clear_pointer(&multifd_send_device_state->threads, thread_pool_free); g_clear_pointer(&multifd_send_device_state->send_data, multifd_send_data_free); @@ -118,3 +128,85 @@ bool multifd_device_state_supported(void) return migrate_multifd() && !migrate_mapped_ram() && migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE; } + +static void multifd_device_state_save_thread_data_free(void *opaque) +{ + SaveLiveCompletePrecopyThreadData *data = opaque; + + g_clear_pointer(&data->idstr, g_free); + g_free(data); +} + +static int multifd_device_state_save_thread(void *opaque) +{ + SaveLiveCompletePrecopyThreadData *data = opaque; + g_autoptr(Error) local_err = NULL; + + if (!data->hdlr(data, &local_err)) { + MigrationState *s = migrate_get_current(); + + /* + * Can't call abort_device_state_save_threads() here since new + * save threads could still be in process of being launched + * (if, for example, the very first save thread launched exited + * with an error very quickly). + */ + + assert(local_err); + + /* + * In case of multiple save threads failing which thread error + * return we end setting is purely arbitrary. + */ + migrate_set_error(s, local_err); + } + + return 0; +} + +bool multifd_device_state_save_thread_should_exit(void) +{ + return qatomic_read(&multifd_send_device_state->threads_abort); +} + +void +multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr, + char *idstr, uint32_t instance_id, + void *opaque) +{ + SaveLiveCompletePrecopyThreadData *data; + + assert(multifd_device_state_supported()); + assert(multifd_send_device_state); + + assert(!qatomic_read(&multifd_send_device_state->threads_abort)); + + data = g_new(SaveLiveCompletePrecopyThreadData, 1); + data->hdlr = hdlr; + data->idstr = g_strdup(idstr); + data->instance_id = instance_id; + data->handler_opaque = opaque; + + thread_pool_submit_immediate(multifd_send_device_state->threads, + multifd_device_state_save_thread, + data, + multifd_device_state_save_thread_data_free); +} + +void multifd_abort_device_state_save_threads(void) +{ + assert(multifd_device_state_supported()); + + qatomic_set(&multifd_send_device_state->threads_abort, true); +} + +bool multifd_join_device_state_save_threads(void) +{ + MigrationState *s = migrate_get_current(); + + assert(multifd_device_state_supported()); + + thread_pool_wait(multifd_send_device_state->threads); + + return !migrate_has_error(s); +} diff --git a/migration/savevm.c b/migration/savevm.c index 1abc365570..5c4fdfd95e 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -37,6 +37,7 @@ #include "migration/register.h" #include "migration/global_state.h" #include "migration/channel-block.h" +#include "multifd.h" #include "ram.h" #include "qemu-file.h" #include "savevm.h" @@ -1527,6 +1528,24 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) int64_t start_ts_each, end_ts_each; SaveStateEntry *se; int ret; + bool multifd_device_state = multifd_device_state_supported(); + + if (multifd_device_state) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + SaveLiveCompletePrecopyThreadHandler hdlr; + + if (!se->ops || (in_postcopy && se->ops->has_postcopy && + se->ops->has_postcopy(se->opaque)) || + !se->ops->save_live_complete_precopy_thread) { + continue; + } + + hdlr = se->ops->save_live_complete_precopy_thread; + multifd_spawn_device_state_save_thread(hdlr, + se->idstr, se->instance_id, + se->opaque); + } + } QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || @@ -1552,16 +1571,35 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) save_section_footer(f, se); if (ret < 0) { qemu_file_set_error(f, ret); - return -1; + goto ret_fail_abort_threads; } end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id, end_ts_each - start_ts_each); } + if (multifd_device_state) { + if (migrate_has_error(migrate_get_current())) { + multifd_abort_device_state_save_threads(); + } + + if (!multifd_join_device_state_save_threads()) { + qemu_file_set_error(f, -EINVAL); + return -1; + } + } + trace_vmstate_downtime_checkpoint("src-iterable-saved"); return 0; + +ret_fail_abort_threads: + if (multifd_device_state) { + multifd_abort_device_state_save_threads(); + multifd_join_device_state_save_threads(); + } + + return -1; } int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, From 5963c219a0e60d3f20c09ba2d34671d5e9623e70 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:45 +0100 Subject: [PATCH 2188/2892] vfio/migration: Add load_device_config_state_start trace event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And rename existing load_device_config_state trace event to load_device_config_state_end for consistency since it is triggered at the end of loading of the VFIO device config state. This way both the start and end points of particular device config loading operation (a long, BQL-serialized operation) are known. Reviewed-by: Cédric Le Goater Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/1b6c5a2097e64c272eb7e53f9e4cca4b79581b38.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 4 +++- hw/vfio/trace-events | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index adfa752db5..03890eaa48 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -285,6 +285,8 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque) VFIODevice *vbasedev = opaque; uint64_t data; + trace_vfio_load_device_config_state_start(vbasedev->name); + if (vbasedev->ops && vbasedev->ops->vfio_load_config) { int ret; @@ -303,7 +305,7 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque) return -EINVAL; } - trace_vfio_load_device_config_state(vbasedev->name); + trace_vfio_load_device_config_state_end(vbasedev->name); return qemu_file_get_error(f); } diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index c5385e1a4f..a02c668f28 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -150,7 +150,8 @@ vfio_display_edid_write_error(void) "" # migration.c vfio_load_cleanup(const char *name) " (%s)" -vfio_load_device_config_state(const char *name) " (%s)" +vfio_load_device_config_state_start(const char *name) " (%s)" +vfio_load_device_config_state_end(const char *name) " (%s)" vfio_load_state(const char *name, uint64_t data) " (%s) data 0x%"PRIx64 vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size %"PRIu64" ret %d" vfio_migration_realize(const char *name) " (%s)" From bd846c5d583a63eaaf836403515c4bf3748f3bb3 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:46 +0100 Subject: [PATCH 2189/2892] vfio/migration: Convert bytes_transferred counter to atomic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So it can be safety accessed from multiple threads. This variable type needs to be changed to unsigned long since 32-bit host platforms lack the necessary addition atomics on 64-bit variables. Using 32-bit counters on 32-bit host platforms should not be a problem in practice since they can't realistically address more memory anyway. Reviewed-by: Cédric Le Goater Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/dc391771d2d9ad0f311994f0cb9e666da564aeaf.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 03890eaa48..5532787be6 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -55,7 +55,7 @@ */ #define VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE (1 * MiB) -static int64_t bytes_transferred; +static unsigned long bytes_transferred; static const char *mig_state_to_str(enum vfio_device_mig_state state) { @@ -391,7 +391,7 @@ static ssize_t vfio_save_block(QEMUFile *f, VFIOMigration *migration) qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE); qemu_put_be64(f, data_size); qemu_put_buffer(f, migration->data_buffer, data_size); - bytes_transferred += data_size; + qatomic_add(&bytes_transferred, data_size); trace_vfio_save_block(migration->vbasedev->name, data_size); @@ -1013,12 +1013,12 @@ static int vfio_block_migration(VFIODevice *vbasedev, Error *err, Error **errp) int64_t vfio_mig_bytes_transferred(void) { - return bytes_transferred; + return MIN(qatomic_read(&bytes_transferred), INT64_MAX); } void vfio_reset_bytes_transferred(void) { - bytes_transferred = 0; + qatomic_set(&bytes_transferred, 0); } /* From 47c7133629cc35b417b8f32512a4715ee53bfae3 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:47 +0100 Subject: [PATCH 2190/2892] vfio/migration: Add vfio_add_bytes_transferred() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way bytes_transferred can also be incremented in other translation units than migration.c. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/d1fbc27ac2417b49892f354ba20f6c6b3f7209f8.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 7 ++++++- include/hw/vfio/vfio-common.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 5532787be6..51c056e152 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -391,7 +391,7 @@ static ssize_t vfio_save_block(QEMUFile *f, VFIOMigration *migration) qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE); qemu_put_be64(f, data_size); qemu_put_buffer(f, migration->data_buffer, data_size); - qatomic_add(&bytes_transferred, data_size); + vfio_mig_add_bytes_transferred(data_size); trace_vfio_save_block(migration->vbasedev->name, data_size); @@ -1021,6 +1021,11 @@ void vfio_reset_bytes_transferred(void) qatomic_set(&bytes_transferred, 0); } +void vfio_mig_add_bytes_transferred(unsigned long val) +{ + qatomic_add(&bytes_transferred, val); +} + /* * Return true when either migration initialized or blocker registered. * Currently only return false when adding blocker fails which will diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index ac35136a11..5c84ebb002 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -274,6 +274,7 @@ void vfio_unblock_multiple_devices_migration(void); bool vfio_viommu_preset(VFIODevice *vbasedev); int64_t vfio_mig_bytes_transferred(void); void vfio_reset_bytes_transferred(void); +void vfio_mig_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); From eb6608619a16ab949f3cf1138638d17afa45fe4d Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:48 +0100 Subject: [PATCH 2191/2892] vfio/migration: Move migration channel flags to vfio-common.h header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way they can also be referenced in other translation units than migration.c. Reviewed-by: Cédric Le Goater Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/26a940f6b22c1b685818251b7a3ddbbca601b1d6.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 17 ----------------- include/hw/vfio/vfio-common.h | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 51c056e152..a9b0970604 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -31,23 +31,6 @@ #include "trace.h" #include "hw/hw.h" -/* - * Flags to be used as unique delimiters for VFIO devices in the migration - * stream. These flags are composed as: - * 0xffffffff => MSB 32-bit all 1s - * 0xef10 => Magic ID, represents emulated (virtual) function IO - * 0x0000 => 16-bits reserved for flags - * - * The beginning of state information is marked by _DEV_CONFIG_STATE, - * _DEV_SETUP_STATE, or _DEV_DATA_STATE, respectively. The end of a - * certain state information is marked by _END_OF_STATE. - */ -#define VFIO_MIG_FLAG_END_OF_STATE (0xffffffffef100001ULL) -#define VFIO_MIG_FLAG_DEV_CONFIG_STATE (0xffffffffef100002ULL) -#define VFIO_MIG_FLAG_DEV_SETUP_STATE (0xffffffffef100003ULL) -#define VFIO_MIG_FLAG_DEV_DATA_STATE (0xffffffffef100004ULL) -#define VFIO_MIG_FLAG_DEV_INIT_DATA_SENT (0xffffffffef100005ULL) - /* * This is an arbitrary size based on migration of mlx5 devices, where typically * total device migration size is on the order of 100s of MB. Testing with diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 5c84ebb002..bf5d520871 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -36,6 +36,23 @@ #define VFIO_MSG_PREFIX "vfio %s: " +/* + * Flags to be used as unique delimiters for VFIO devices in the migration + * stream. These flags are composed as: + * 0xffffffff => MSB 32-bit all 1s + * 0xef10 => Magic ID, represents emulated (virtual) function IO + * 0x0000 => 16-bits reserved for flags + * + * The beginning of state information is marked by _DEV_CONFIG_STATE, + * _DEV_SETUP_STATE, or _DEV_DATA_STATE, respectively. The end of a + * certain state information is marked by _END_OF_STATE. + */ +#define VFIO_MIG_FLAG_END_OF_STATE (0xffffffffef100001ULL) +#define VFIO_MIG_FLAG_DEV_CONFIG_STATE (0xffffffffef100002ULL) +#define VFIO_MIG_FLAG_DEV_SETUP_STATE (0xffffffffef100003ULL) +#define VFIO_MIG_FLAG_DEV_DATA_STATE (0xffffffffef100004ULL) +#define VFIO_MIG_FLAG_DEV_INIT_DATA_SENT (0xffffffffef100005ULL) + enum { VFIO_DEVICE_TYPE_PCI = 0, VFIO_DEVICE_TYPE_PLATFORM = 1, From 961165122bc8a4cf58eaaab75ff09ba8fc950a88 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:49 +0100 Subject: [PATCH 2192/2892] vfio/migration: Multifd device state transfer support - basic types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add basic types and flags used by VFIO multifd device state transfer support. Since we'll be introducing a lot of multifd transfer specific code, add a new file migration-multifd.c to home it, wired into main VFIO migration code (migration.c) via migration-multifd.h header file. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/4eedd529e6617f80f3d6a66d7268a0db2bc173fa.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/meson.build | 1 + hw/vfio/migration-multifd.c | 33 +++++++++++++++++++++++++++++++++ hw/vfio/migration-multifd.h | 17 +++++++++++++++++ hw/vfio/migration.c | 1 + 4 files changed, 52 insertions(+) create mode 100644 hw/vfio/migration-multifd.c create mode 100644 hw/vfio/migration-multifd.h diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index bba776f75c..260d65febd 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -5,6 +5,7 @@ vfio_ss.add(files( 'container-base.c', 'container.c', 'migration.c', + 'migration-multifd.c', 'cpr.c', )) vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c new file mode 100644 index 0000000000..fa594b33fd --- /dev/null +++ b/hw/vfio/migration-multifd.c @@ -0,0 +1,33 @@ +/* + * Multifd VFIO migration + * + * Copyright (C) 2024,2025 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/vfio/vfio-common.h" +#include "migration/misc.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" +#include "migration/qemu-file.h" +#include "migration-multifd.h" +#include "trace.h" + +#define VFIO_DEVICE_STATE_CONFIG_STATE (1) + +#define VFIO_DEVICE_STATE_PACKET_VER_CURRENT (0) + +typedef struct VFIODeviceStatePacket { + uint32_t version; + uint32_t idx; + uint32_t flags; + uint8_t data[0]; +} QEMU_PACKED VFIODeviceStatePacket; diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h new file mode 100644 index 0000000000..5b221c6e16 --- /dev/null +++ b/hw/vfio/migration-multifd.h @@ -0,0 +1,17 @@ +/* + * Multifd VFIO migration + * + * Copyright (C) 2024,2025 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_MIGRATION_MULTIFD_H +#define HW_VFIO_MIGRATION_MULTIFD_H + +#include "hw/vfio/vfio-common.h" + +#endif diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index a9b0970604..dc1fe4e717 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -23,6 +23,7 @@ #include "migration/qemu-file.h" #include "migration/register.h" #include "migration/blocker.h" +#include "migration-multifd.h" #include "qapi/error.h" #include "qapi/qapi-events-vfio.h" #include "exec/ramlist.h" From 2efa35d34edfeca0d151de8283e52006e2c6cbd4 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:50 +0100 Subject: [PATCH 2193/2892] vfio/migration: Multifd device state transfer - add support checking function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add vfio_multifd_transfer_supported() function that tells whether the multifd device state transfer is supported. Reviewed-by: Cédric Le Goater Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/8ce50256f341b3d47342bb217cb5fbb2deb14639.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration-multifd.c | 6 ++++++ hw/vfio/migration-multifd.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index fa594b33fd..79fae0b629 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -31,3 +31,9 @@ typedef struct VFIODeviceStatePacket { uint32_t flags; uint8_t data[0]; } QEMU_PACKED VFIODeviceStatePacket; + +bool vfio_multifd_transfer_supported(void) +{ + return multifd_device_state_supported() && + migrate_send_switchover_start(); +} diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index 5b221c6e16..1b60d5f67a 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -14,4 +14,6 @@ #include "hw/vfio/vfio-common.h" +bool vfio_multifd_transfer_supported(void); + #endif From ff2fd1f7e23f528f03f41b5475afb173718fea07 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:51 +0100 Subject: [PATCH 2194/2892] vfio/migration: Multifd setup/cleanup functions and associated VFIOMultifd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add multifd setup/cleanup functions and an associated VFIOMultifd data structure that will contain most of the receive-side data together with its init/cleanup methods. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/c0520523053b1087787152ddf2163257d3030be0.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration-multifd.c | 44 +++++++++++++++++++++++++++++++++++ hw/vfio/migration-multifd.h | 4 ++++ include/hw/vfio/vfio-common.h | 3 +++ 3 files changed, 51 insertions(+) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 79fae0b629..091dc43210 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -32,8 +32,52 @@ typedef struct VFIODeviceStatePacket { uint8_t data[0]; } QEMU_PACKED VFIODeviceStatePacket; +typedef struct VFIOMultifd { +} VFIOMultifd; + +static VFIOMultifd *vfio_multifd_new(void) +{ + VFIOMultifd *multifd = g_new(VFIOMultifd, 1); + + return multifd; +} + +static void vfio_multifd_free(VFIOMultifd *multifd) +{ + g_free(multifd); +} + +void vfio_multifd_cleanup(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + g_clear_pointer(&migration->multifd, vfio_multifd_free); +} + bool vfio_multifd_transfer_supported(void) { return multifd_device_state_supported() && migrate_send_switchover_start(); } + +bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev) +{ + return false; +} + +bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + /* Nothing further to check or do */ + return true; + } + + if (alloc_multifd) { + assert(!migration->multifd); + migration->multifd = vfio_multifd_new(); + } + + return true; +} diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index 1b60d5f67a..2a7a76164f 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -14,6 +14,10 @@ #include "hw/vfio/vfio-common.h" +bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp); +void vfio_multifd_cleanup(VFIODevice *vbasedev); + bool vfio_multifd_transfer_supported(void); +bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev); #endif diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index bf5d520871..4038239069 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -78,6 +78,8 @@ typedef struct VFIORegion { uint8_t nr; /* cache the region number for debug */ } VFIORegion; +typedef struct VFIOMultifd VFIOMultifd; + typedef struct VFIOMigration { struct VFIODevice *vbasedev; VMChangeStateEntry *vm_state; @@ -89,6 +91,7 @@ typedef struct VFIOMigration { uint64_t mig_flags; uint64_t precopy_init_size; uint64_t precopy_dirty_size; + VFIOMultifd *multifd; bool initial_data_sent; bool event_save_iterate_started; From 6bcffb1cad5b2b45152c0faa1133c96d3f129914 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:52 +0100 Subject: [PATCH 2195/2892] vfio/migration: Setup and cleanup multifd transfer in these general methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire VFIO multifd transfer specific setup and cleanup functions into general VFIO load/save setup and cleanup methods. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/b1f864a65fafd4fdab1f89230df52e46ae41f2ac.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index dc1fe4e717..3c8286ae62 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -453,6 +453,10 @@ static int vfio_save_setup(QEMUFile *f, void *opaque, Error **errp) uint64_t stop_copy_size = VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE; int ret; + if (!vfio_multifd_setup(vbasedev, false, errp)) { + return -EINVAL; + } + qemu_put_be64(f, VFIO_MIG_FLAG_DEV_SETUP_STATE); vfio_query_stop_copy_size(vbasedev, &stop_copy_size); @@ -509,6 +513,9 @@ static void vfio_save_cleanup(void *opaque) Error *local_err = NULL; int ret; + /* Currently a NOP, done for symmetry with load_cleanup() */ + vfio_multifd_cleanup(vbasedev); + /* * Changing device state from STOP_COPY to STOP can take time. Do it here, * after migration has completed, so it won't increase downtime. @@ -674,15 +681,28 @@ static void vfio_save_state(QEMUFile *f, void *opaque) static int vfio_load_setup(QEMUFile *f, void *opaque, Error **errp) { VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + int ret; - return vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, - vbasedev->migration->device_state, errp); + if (!vfio_multifd_setup(vbasedev, true, errp)) { + return -EINVAL; + } + + ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, + migration->device_state, errp); + if (ret) { + return ret; + } + + return 0; } static int vfio_load_cleanup(void *opaque) { VFIODevice *vbasedev = opaque; + vfio_multifd_cleanup(vbasedev); + vfio_migration_cleanup(vbasedev); trace_vfio_load_cleanup(vbasedev->name); From 3228d311ab1882f75b04d080d33a71fc7a0bcac5 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:53 +0100 Subject: [PATCH 2196/2892] vfio/migration: Multifd device state transfer support - received buffers queuing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multifd received data needs to be reassembled since device state packets sent via different multifd channels can arrive out-of-order. Therefore, each VFIO device state packet carries a header indicating its position in the stream. The raw device state data is saved into a VFIOStateBuffer for later in-order loading into the device. The last such VFIO device state packet should have VFIO_DEVICE_STATE_CONFIG_STATE flag set and carry the device config state. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/e3bff515a8d61c582b94b409eb12a45b1a143a69.1741124640.git.maciej.szmigiero@oracle.com [ clg: - Reordered savevm_vfio_handlers - Added load_state_buffer documentation ] Signed-off-by: Cédric Le Goater --- docs/devel/migration/vfio.rst | 7 ++ hw/vfio/migration-multifd.c | 163 ++++++++++++++++++++++++++++++++++ hw/vfio/migration-multifd.h | 3 + hw/vfio/migration.c | 4 + hw/vfio/trace-events | 1 + 5 files changed, 178 insertions(+) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index c49482eab6..8b1f28890a 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -76,6 +76,10 @@ VFIO implements the device hooks for the iterative approach as follows: * A ``load_state`` function that loads the config section and the data sections that are generated by the save functions above. +* A ``load_state_buffer`` function that loads the device state and the device + config that arrived via multifd channels. + It's used only in the multifd mode. + * ``cleanup`` functions for both save and load that perform any migration related cleanup. @@ -194,6 +198,9 @@ Live migration resume path (RESTORE_VM, _ACTIVE, _STOP) | For each device, .load_state() is called for that device section data + transmitted via the main migration channel. + For data transmitted via multifd channels .load_state_buffer() is called + instead. (RESTORE_VM, _ACTIVE, _RESUMING) | At the end, .load_cleanup() is called for each device and vCPUs are started diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 091dc43210..79df11b7ba 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -32,18 +32,181 @@ typedef struct VFIODeviceStatePacket { uint8_t data[0]; } QEMU_PACKED VFIODeviceStatePacket; +/* type safety */ +typedef struct VFIOStateBuffers { + GArray *array; +} VFIOStateBuffers; + +typedef struct VFIOStateBuffer { + bool is_present; + char *data; + size_t len; +} VFIOStateBuffer; + typedef struct VFIOMultifd { + VFIOStateBuffers load_bufs; + QemuCond load_bufs_buffer_ready_cond; + QemuMutex load_bufs_mutex; /* Lock order: this lock -> BQL */ + uint32_t load_buf_idx; + uint32_t load_buf_idx_last; } VFIOMultifd; +static void vfio_state_buffer_clear(gpointer data) +{ + VFIOStateBuffer *lb = data; + + if (!lb->is_present) { + return; + } + + g_clear_pointer(&lb->data, g_free); + lb->is_present = false; +} + +static void vfio_state_buffers_init(VFIOStateBuffers *bufs) +{ + bufs->array = g_array_new(FALSE, TRUE, sizeof(VFIOStateBuffer)); + g_array_set_clear_func(bufs->array, vfio_state_buffer_clear); +} + +static void vfio_state_buffers_destroy(VFIOStateBuffers *bufs) +{ + g_clear_pointer(&bufs->array, g_array_unref); +} + +static void vfio_state_buffers_assert_init(VFIOStateBuffers *bufs) +{ + assert(bufs->array); +} + +static unsigned int vfio_state_buffers_size_get(VFIOStateBuffers *bufs) +{ + return bufs->array->len; +} + +static void vfio_state_buffers_size_set(VFIOStateBuffers *bufs, + unsigned int size) +{ + g_array_set_size(bufs->array, size); +} + +static VFIOStateBuffer *vfio_state_buffers_at(VFIOStateBuffers *bufs, + unsigned int idx) +{ + return &g_array_index(bufs->array, VFIOStateBuffer, idx); +} + +/* called with load_bufs_mutex locked */ +static bool vfio_load_state_buffer_insert(VFIODevice *vbasedev, + VFIODeviceStatePacket *packet, + size_t packet_total_size, + Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + VFIOStateBuffer *lb; + + vfio_state_buffers_assert_init(&multifd->load_bufs); + if (packet->idx >= vfio_state_buffers_size_get(&multifd->load_bufs)) { + vfio_state_buffers_size_set(&multifd->load_bufs, packet->idx + 1); + } + + lb = vfio_state_buffers_at(&multifd->load_bufs, packet->idx); + if (lb->is_present) { + error_setg(errp, "%s: state buffer %" PRIu32 " already filled", + vbasedev->name, packet->idx); + return false; + } + + assert(packet->idx >= multifd->load_buf_idx); + + lb->data = g_memdup2(&packet->data, packet_total_size - sizeof(*packet)); + lb->len = packet_total_size - sizeof(*packet); + lb->is_present = true; + + return true; +} + +bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, + Error **errp) +{ + VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + VFIODeviceStatePacket *packet = (VFIODeviceStatePacket *)data; + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + error_setg(errp, + "%s: got device state packet but not doing multifd transfer", + vbasedev->name); + return false; + } + + assert(multifd); + + if (data_size < sizeof(*packet)) { + error_setg(errp, "%s: packet too short at %zu (min is %zu)", + vbasedev->name, data_size, sizeof(*packet)); + return false; + } + + if (packet->version != VFIO_DEVICE_STATE_PACKET_VER_CURRENT) { + error_setg(errp, "%s: packet has unknown version %" PRIu32, + vbasedev->name, packet->version); + return false; + } + + if (packet->idx == UINT32_MAX) { + error_setg(errp, "%s: packet index is invalid", vbasedev->name); + return false; + } + + trace_vfio_load_state_device_buffer_incoming(vbasedev->name, packet->idx); + + /* + * Holding BQL here would violate the lock order and can cause + * a deadlock once we attempt to lock load_bufs_mutex below. + */ + assert(!bql_locked()); + + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + /* config state packet should be the last one in the stream */ + if (packet->flags & VFIO_DEVICE_STATE_CONFIG_STATE) { + multifd->load_buf_idx_last = packet->idx; + } + + if (!vfio_load_state_buffer_insert(vbasedev, packet, data_size, + errp)) { + return false; + } + + qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond); + } + + return true; +} + static VFIOMultifd *vfio_multifd_new(void) { VFIOMultifd *multifd = g_new(VFIOMultifd, 1); + vfio_state_buffers_init(&multifd->load_bufs); + + qemu_mutex_init(&multifd->load_bufs_mutex); + + multifd->load_buf_idx = 0; + multifd->load_buf_idx_last = UINT32_MAX; + qemu_cond_init(&multifd->load_bufs_buffer_ready_cond); + return multifd; } static void vfio_multifd_free(VFIOMultifd *multifd) { + vfio_state_buffers_destroy(&multifd->load_bufs); + qemu_cond_destroy(&multifd->load_bufs_buffer_ready_cond); + qemu_mutex_destroy(&multifd->load_bufs_mutex); + g_free(multifd); } diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index 2a7a76164f..8c6320fcb4 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -20,4 +20,7 @@ void vfio_multifd_cleanup(VFIODevice *vbasedev); bool vfio_multifd_transfer_supported(void); bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev); +bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, + Error **errp); + #endif diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 3c8286ae62..2cdb92356e 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -802,6 +802,10 @@ static const SaveVMHandlers savevm_vfio_handlers = { .load_cleanup = vfio_load_cleanup, .load_state = vfio_load_state, .switchover_ack_needed = vfio_switchover_ack_needed, + /* + * Multifd support + */ + .load_state_buffer = vfio_multifd_load_state_buffer, }; /* ---------------------------------------------------------------------- */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index a02c668f28..404ea079b2 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -154,6 +154,7 @@ vfio_load_device_config_state_start(const char *name) " (%s)" vfio_load_device_config_state_end(const char *name) " (%s)" vfio_load_state(const char *name, uint64_t data) " (%s) data 0x%"PRIx64 vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size %"PRIu64" ret %d" +vfio_load_state_device_buffer_incoming(const char *name, uint32_t idx) " (%s) idx %"PRIu32 vfio_migration_realize(const char *name) " (%s)" vfio_migration_set_device_state(const char *name, const char *state) " (%s) state %s" vfio_migration_set_state(const char *name, const char *new_state, const char *recover_state) " (%s) new state %s, recover state %s" From c59748c1ff924963a67af9efd7e1a1ee6f82d6d6 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:54 +0100 Subject: [PATCH 2197/2892] vfio/migration: Multifd device state transfer support - load thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a thread which loads the VFIO device state buffers that were received via multifd. Each VFIO device that has multifd device state transfer enabled has one such thread, which is created using migration core API qemu_loadvm_start_load_thread(). Since it's important to finish loading device state transferred via the main migration channel (via save_live_iterate SaveVMHandler) before starting loading the data asynchronously transferred via multifd the thread doing the actual loading of the multifd transferred data is only started from switchover_start SaveVMHandler. switchover_start handler is called when MIG_CMD_SWITCHOVER_START sub-command of QEMU_VM_COMMAND is received via the main migration channel. This sub-command is only sent after all save_live_iterate data have already been posted so it is safe to commence loading of the multifd-transferred device state upon receiving it - loading of save_live_iterate data happens synchronously in the main migration thread (much like the processing of MIG_CMD_SWITCHOVER_START) so by the time MIG_CMD_SWITCHOVER_START is processed all the proceeding data must have already been loaded. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/9abe612d775aaf42e31646796acd2363c723a57a.1741124640.git.maciej.szmigiero@oracle.com [ clg: - Reordered savevm_vfio_handlers - Added switchover_start documentation ] Signed-off-by: Cédric Le Goater --- docs/devel/migration/vfio.rst | 4 + hw/vfio/migration-multifd.c | 226 ++++++++++++++++++++++++++++++++++ hw/vfio/migration-multifd.h | 2 + hw/vfio/migration.c | 12 ++ hw/vfio/trace-events | 7 ++ 5 files changed, 251 insertions(+) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index 8b1f28890a..d6cf60890c 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -67,6 +67,10 @@ VFIO implements the device hooks for the iterative approach as follows: * A ``switchover_ack_needed`` function that checks if the VFIO device uses "switchover-ack" migration capability when this capability is enabled. +* A ``switchover_start`` function that in the multifd mode starts a thread that + reassembles the multifd received data and loads it in-order into the device. + In the non-multifd mode this function is a NOP. + * A ``save_state`` function to save the device config space if it is present. * A ``save_live_complete_precopy`` function that sets the VFIO device in diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 79df11b7ba..2eef27604e 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -44,8 +44,12 @@ typedef struct VFIOStateBuffer { } VFIOStateBuffer; typedef struct VFIOMultifd { + bool load_bufs_thread_running; + bool load_bufs_thread_want_exit; + VFIOStateBuffers load_bufs; QemuCond load_bufs_buffer_ready_cond; + QemuCond load_bufs_thread_finished_cond; QemuMutex load_bufs_mutex; /* Lock order: this lock -> BQL */ uint32_t load_buf_idx; uint32_t load_buf_idx_last; @@ -186,6 +190,178 @@ bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, return true; } +static bool vfio_load_bufs_thread_load_config(VFIODevice *vbasedev, + Error **errp) +{ + error_setg(errp, "not yet there"); + return false; +} + +static VFIOStateBuffer *vfio_load_state_buffer_get(VFIOMultifd *multifd) +{ + VFIOStateBuffer *lb; + unsigned int bufs_len; + + bufs_len = vfio_state_buffers_size_get(&multifd->load_bufs); + if (multifd->load_buf_idx >= bufs_len) { + assert(multifd->load_buf_idx == bufs_len); + return NULL; + } + + lb = vfio_state_buffers_at(&multifd->load_bufs, + multifd->load_buf_idx); + if (!lb->is_present) { + return NULL; + } + + return lb; +} + +static bool vfio_load_state_buffer_write(VFIODevice *vbasedev, + VFIOStateBuffer *lb, + Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + g_autofree char *buf = NULL; + char *buf_cur; + size_t buf_len; + + if (!lb->len) { + return true; + } + + trace_vfio_load_state_device_buffer_load_start(vbasedev->name, + multifd->load_buf_idx); + + /* lb might become re-allocated when we drop the lock */ + buf = g_steal_pointer(&lb->data); + buf_cur = buf; + buf_len = lb->len; + while (buf_len > 0) { + ssize_t wr_ret; + int errno_save; + + /* + * Loading data to the device takes a while, + * drop the lock during this process. + */ + qemu_mutex_unlock(&multifd->load_bufs_mutex); + wr_ret = write(migration->data_fd, buf_cur, buf_len); + errno_save = errno; + qemu_mutex_lock(&multifd->load_bufs_mutex); + + if (wr_ret < 0) { + error_setg(errp, + "%s: writing state buffer %" PRIu32 " failed: %d", + vbasedev->name, multifd->load_buf_idx, errno_save); + return false; + } + + assert(wr_ret <= buf_len); + buf_len -= wr_ret; + buf_cur += wr_ret; + } + + trace_vfio_load_state_device_buffer_load_end(vbasedev->name, + multifd->load_buf_idx); + + return true; +} + +static bool vfio_load_bufs_thread_want_exit(VFIOMultifd *multifd, + bool *should_quit) +{ + return multifd->load_bufs_thread_want_exit || qatomic_read(should_quit); +} + +/* + * This thread is spawned by vfio_multifd_switchover_start() which gets + * called upon encountering the switchover point marker in main migration + * stream. + * + * It exits after either: + * * completing loading the remaining device state and device config, OR: + * * encountering some error while doing the above, OR: + * * being forcefully aborted by the migration core by it setting should_quit + * or by vfio_load_cleanup_load_bufs_thread() setting + * multifd->load_bufs_thread_want_exit. + */ +static bool vfio_load_bufs_thread(void *opaque, bool *should_quit, Error **errp) +{ + VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + bool ret = false; + + trace_vfio_load_bufs_thread_start(vbasedev->name); + + assert(multifd); + QEMU_LOCK_GUARD(&multifd->load_bufs_mutex); + + assert(multifd->load_bufs_thread_running); + + while (true) { + VFIOStateBuffer *lb; + + /* + * Always check cancellation first after the buffer_ready wait below in + * case that cond was signalled by vfio_load_cleanup_load_bufs_thread(). + */ + if (vfio_load_bufs_thread_want_exit(multifd, should_quit)) { + error_setg(errp, "operation cancelled"); + goto thread_exit; + } + + assert(multifd->load_buf_idx <= multifd->load_buf_idx_last); + + lb = vfio_load_state_buffer_get(multifd); + if (!lb) { + trace_vfio_load_state_device_buffer_starved(vbasedev->name, + multifd->load_buf_idx); + qemu_cond_wait(&multifd->load_bufs_buffer_ready_cond, + &multifd->load_bufs_mutex); + continue; + } + + if (multifd->load_buf_idx == multifd->load_buf_idx_last) { + break; + } + + if (multifd->load_buf_idx == 0) { + trace_vfio_load_state_device_buffer_start(vbasedev->name); + } + + if (!vfio_load_state_buffer_write(vbasedev, lb, errp)) { + goto thread_exit; + } + + if (multifd->load_buf_idx == multifd->load_buf_idx_last - 1) { + trace_vfio_load_state_device_buffer_end(vbasedev->name); + } + + multifd->load_buf_idx++; + } + + if (!vfio_load_bufs_thread_load_config(vbasedev, errp)) { + goto thread_exit; + } + + ret = true; + +thread_exit: + /* + * Notify possibly waiting vfio_load_cleanup_load_bufs_thread() that + * this thread is exiting. + */ + multifd->load_bufs_thread_running = false; + qemu_cond_signal(&multifd->load_bufs_thread_finished_cond); + + trace_vfio_load_bufs_thread_end(vbasedev->name); + + return ret; +} + static VFIOMultifd *vfio_multifd_new(void) { VFIOMultifd *multifd = g_new(VFIOMultifd, 1); @@ -198,11 +374,41 @@ static VFIOMultifd *vfio_multifd_new(void) multifd->load_buf_idx_last = UINT32_MAX; qemu_cond_init(&multifd->load_bufs_buffer_ready_cond); + multifd->load_bufs_thread_running = false; + multifd->load_bufs_thread_want_exit = false; + qemu_cond_init(&multifd->load_bufs_thread_finished_cond); + return multifd; } +/* + * Terminates vfio_load_bufs_thread by setting + * multifd->load_bufs_thread_want_exit and signalling all the conditions + * the thread could be blocked on. + * + * Waits for the thread to signal that it had finished. + */ +static void vfio_load_cleanup_load_bufs_thread(VFIOMultifd *multifd) +{ + /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */ + bql_unlock(); + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + while (multifd->load_bufs_thread_running) { + multifd->load_bufs_thread_want_exit = true; + + qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond); + qemu_cond_wait(&multifd->load_bufs_thread_finished_cond, + &multifd->load_bufs_mutex); + } + } + bql_lock(); +} + static void vfio_multifd_free(VFIOMultifd *multifd) { + vfio_load_cleanup_load_bufs_thread(multifd); + + qemu_cond_destroy(&multifd->load_bufs_thread_finished_cond); vfio_state_buffers_destroy(&multifd->load_bufs); qemu_cond_destroy(&multifd->load_bufs_buffer_ready_cond); qemu_mutex_destroy(&multifd->load_bufs_mutex); @@ -244,3 +450,23 @@ bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp) return true; } + +int vfio_multifd_switchover_start(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + + assert(multifd); + + /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */ + bql_unlock(); + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + assert(!multifd->load_bufs_thread_running); + multifd->load_bufs_thread_running = true; + } + bql_lock(); + + qemu_loadvm_start_load_thread(vfio_load_bufs_thread, vbasedev); + + return 0; +} diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index 8c6320fcb4..f0d28fcef2 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -23,4 +23,6 @@ bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev); bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, Error **errp); +int vfio_multifd_switchover_start(VFIODevice *vbasedev); + #endif diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 2cdb92356e..815ad8fc93 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -788,6 +788,17 @@ static bool vfio_switchover_ack_needed(void *opaque) return vfio_precopy_supported(vbasedev); } +static int vfio_switchover_start(void *opaque) +{ + VFIODevice *vbasedev = opaque; + + if (vfio_multifd_transfer_enabled(vbasedev)) { + return vfio_multifd_switchover_start(vbasedev); + } + + return 0; +} + static const SaveVMHandlers savevm_vfio_handlers = { .save_prepare = vfio_save_prepare, .save_setup = vfio_save_setup, @@ -806,6 +817,7 @@ static const SaveVMHandlers savevm_vfio_handlers = { * Multifd support */ .load_state_buffer = vfio_multifd_load_state_buffer, + .switchover_start = vfio_switchover_start, }; /* ---------------------------------------------------------------------- */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 404ea079b2..d6b7e34faa 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -149,12 +149,19 @@ vfio_display_edid_update(uint32_t prefx, uint32_t prefy) "%ux%u" vfio_display_edid_write_error(void) "" # migration.c +vfio_load_bufs_thread_start(const char *name) " (%s)" +vfio_load_bufs_thread_end(const char *name) " (%s)" vfio_load_cleanup(const char *name) " (%s)" vfio_load_device_config_state_start(const char *name) " (%s)" vfio_load_device_config_state_end(const char *name) " (%s)" vfio_load_state(const char *name, uint64_t data) " (%s) data 0x%"PRIx64 vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size %"PRIu64" ret %d" vfio_load_state_device_buffer_incoming(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_start(const char *name) " (%s)" +vfio_load_state_device_buffer_starved(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_load_start(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_load_end(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_end(const char *name) " (%s)" vfio_migration_realize(const char *name) " (%s)" vfio_migration_set_device_state(const char *name, const char *state) " (%s) state %s" vfio_migration_set_state(const char *name, const char *new_state, const char *recover_state) " (%s) new state %s, recover state %s" From fda70ed83d54abad6bf2d437a7c05204b0fad228 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:55 +0100 Subject: [PATCH 2198/2892] migration/qemu-file: Define g_autoptr() cleanup function for QEMUFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatic memory management helps avoid memory safety issues. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/2fd01d773a783d572dcf538a064a98cc09e75c12.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- migration/qemu-file.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 3e47a20621..f5b9f430e0 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -33,6 +33,8 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc); QEMUFile *qemu_file_new_output(QIOChannel *ioc); int qemu_fclose(QEMUFile *f); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QEMUFile, qemu_fclose) + /* * qemu_file_transferred: * From b659c07c534490369ca0954f0116b05c4a063065 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:56 +0100 Subject: [PATCH 2199/2892] vfio/migration: Multifd device state transfer support - config loading support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Load device config received via multifd using the existing machinery behind vfio_load_device_config_state(). Also, make sure to process the relevant main migration channel flags. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/5dbd3f3703ec1097da2cf82a7262233452146fee.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration-multifd.c | 49 +++++++++++++++++++++++++++++++++-- hw/vfio/migration.c | 9 ++++++- include/hw/vfio/vfio-common.h | 2 ++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 2eef27604e..1d81233c75 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -17,6 +17,7 @@ #include "qemu/lockable.h" #include "qemu/main-loop.h" #include "qemu/thread.h" +#include "io/channel-buffer.h" #include "migration/qemu-file.h" #include "migration-multifd.h" #include "trace.h" @@ -193,8 +194,52 @@ bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, static bool vfio_load_bufs_thread_load_config(VFIODevice *vbasedev, Error **errp) { - error_setg(errp, "not yet there"); - return false; + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + VFIOStateBuffer *lb; + g_autoptr(QIOChannelBuffer) bioc = NULL; + g_autoptr(QEMUFile) f_out = NULL, f_in = NULL; + uint64_t mig_header; + int ret; + + assert(multifd->load_buf_idx == multifd->load_buf_idx_last); + lb = vfio_state_buffers_at(&multifd->load_bufs, multifd->load_buf_idx); + assert(lb->is_present); + + bioc = qio_channel_buffer_new(lb->len); + qio_channel_set_name(QIO_CHANNEL(bioc), "vfio-device-config-load"); + + f_out = qemu_file_new_output(QIO_CHANNEL(bioc)); + qemu_put_buffer(f_out, (uint8_t *)lb->data, lb->len); + + ret = qemu_fflush(f_out); + if (ret) { + error_setg(errp, "%s: load config state flush failed: %d", + vbasedev->name, ret); + return false; + } + + qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); + f_in = qemu_file_new_input(QIO_CHANNEL(bioc)); + + mig_header = qemu_get_be64(f_in); + if (mig_header != VFIO_MIG_FLAG_DEV_CONFIG_STATE) { + error_setg(errp, "%s: expected FLAG_DEV_CONFIG_STATE but got %" PRIx64, + vbasedev->name, mig_header); + return false; + } + + bql_lock(); + ret = vfio_load_device_config_state(f_in, vbasedev); + bql_unlock(); + + if (ret < 0) { + error_setg(errp, "%s: vfio_load_device_config_state() failed: %d", + vbasedev->name, ret); + return false; + } + + return true; } static VFIOStateBuffer *vfio_load_state_buffer_get(VFIOMultifd *multifd) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 815ad8fc93..2ca3fa08d4 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -264,7 +264,7 @@ static int vfio_save_device_config_state(QEMUFile *f, void *opaque, return ret; } -static int vfio_load_device_config_state(QEMUFile *f, void *opaque) +int vfio_load_device_config_state(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; uint64_t data; @@ -723,6 +723,13 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id) switch (data) { case VFIO_MIG_FLAG_DEV_CONFIG_STATE: { + if (vfio_multifd_transfer_enabled(vbasedev)) { + error_report("%s: got DEV_CONFIG_STATE in main migration " + "channel but doing multifd transfer", + vbasedev->name); + return -EINVAL; + } + return vfio_load_device_config_state(f, opaque); } case VFIO_MIG_FLAG_DEV_SETUP_STATE: diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 4038239069..9d72ac1eae 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -298,6 +298,8 @@ void vfio_mig_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); +int vfio_load_device_config_state(QEMUFile *f, void *opaque); + #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info); From 6d644baef20303fa4b2b342f556e26c2262b439f Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:57 +0100 Subject: [PATCH 2200/2892] vfio/migration: Multifd device state transfer support - send side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the multifd device state transfer via additional per-device thread inside save_live_complete_precopy_thread handler. Switch between doing the data transfer in the new handler and doing it in the old save_state handler depending if VFIO multifd transfer is enabled or not. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/4d727e2e0435e0022d50004e474077632830e08d.1741124640.git.maciej.szmigiero@oracle.com [ clg: - Reordered savevm_vfio_handlers - Updated save_live_complete_precopy* documentation ] Signed-off-by: Cédric Le Goater --- docs/devel/migration/vfio.rst | 19 ++++- hw/vfio/migration-multifd.c | 142 ++++++++++++++++++++++++++++++++++ hw/vfio/migration-multifd.h | 6 ++ hw/vfio/migration.c | 22 ++++-- hw/vfio/trace-events | 2 + include/hw/vfio/vfio-common.h | 6 ++ 6 files changed, 189 insertions(+), 8 deletions(-) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index d6cf60890c..a803a09bc1 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -71,11 +71,23 @@ VFIO implements the device hooks for the iterative approach as follows: reassembles the multifd received data and loads it in-order into the device. In the non-multifd mode this function is a NOP. -* A ``save_state`` function to save the device config space if it is present. +* A ``save_state`` function to save the device config space if it is present + in the non-multifd mode. + In the multifd mode it just emits either a dummy EOS marker. * A ``save_live_complete_precopy`` function that sets the VFIO device in _STOP_COPY state and iteratively copies the data for the VFIO device until the vendor driver indicates that no data remains. + In the multifd mode it just emits a dummy EOS marker. + +* A ``save_live_complete_precopy_thread`` function that in the multifd mode + provides thread handler performing multifd device state transfer. + It sets the VFIO device to _STOP_COPY state, iteratively reads the data + from the VFIO device and queues it for multifd transmission until the vendor + driver indicates that no data remains. + After that, it saves the device config space and queues it for multifd + transfer too. + In the non-multifd mode this thread is a NOP. * A ``load_state`` function that loads the config section and the data sections that are generated by the save functions above. @@ -184,8 +196,11 @@ Live migration save path Then the VFIO device is put in _STOP_COPY state (FINISH_MIGRATE, _ACTIVE, _STOP_COPY) .save_live_complete_precopy() is called for each active device - For the VFIO device, iterate in .save_live_complete_precopy() until + For the VFIO device: in the non-multifd mode iterate in + .save_live_complete_precopy() until pending data is 0 + In the multifd mode this iteration is done in + .save_live_complete_precopy_thread() instead. | (POSTMIGRATE, _COMPLETED, _STOP_COPY) Migraton thread schedules cleanup bottom half and exits diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 1d81233c75..bfb9a72fa4 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -496,6 +496,148 @@ bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp) return true; } +void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f) +{ + assert(vfio_multifd_transfer_enabled(vbasedev)); + + /* + * Emit dummy NOP data on the main migration channel since the actual + * device state transfer is done via multifd channels. + */ + qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); +} + +static bool +vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev, + char *idstr, + uint32_t instance_id, + uint32_t idx, + Error **errp) +{ + g_autoptr(QIOChannelBuffer) bioc = NULL; + g_autoptr(QEMUFile) f = NULL; + int ret; + g_autofree VFIODeviceStatePacket *packet = NULL; + size_t packet_len; + + bioc = qio_channel_buffer_new(0); + qio_channel_set_name(QIO_CHANNEL(bioc), "vfio-device-config-save"); + + f = qemu_file_new_output(QIO_CHANNEL(bioc)); + + if (vfio_save_device_config_state(f, vbasedev, errp)) { + return false; + } + + ret = qemu_fflush(f); + if (ret) { + error_setg(errp, "%s: save config state flush failed: %d", + vbasedev->name, ret); + return false; + } + + packet_len = sizeof(*packet) + bioc->usage; + packet = g_malloc0(packet_len); + packet->version = VFIO_DEVICE_STATE_PACKET_VER_CURRENT; + packet->idx = idx; + packet->flags = VFIO_DEVICE_STATE_CONFIG_STATE; + memcpy(&packet->data, bioc->data, bioc->usage); + + if (!multifd_queue_device_state(idstr, instance_id, + (char *)packet, packet_len)) { + error_setg(errp, "%s: multifd config data queuing failed", + vbasedev->name); + return false; + } + + vfio_mig_add_bytes_transferred(packet_len); + + return true; +} + +/* + * This thread is spawned by the migration core directly via + * .save_live_complete_precopy_thread SaveVMHandler. + * + * It exits after either: + * * completing saving the remaining device state and device config, OR: + * * encountering some error while doing the above, OR: + * * being forcefully aborted by the migration core by + * multifd_device_state_save_thread_should_exit() returning true. + */ +bool +vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, + Error **errp) +{ + VFIODevice *vbasedev = d->handler_opaque; + VFIOMigration *migration = vbasedev->migration; + bool ret = false; + g_autofree VFIODeviceStatePacket *packet = NULL; + uint32_t idx; + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + /* Nothing to do, vfio_save_complete_precopy() does the transfer. */ + return true; + } + + trace_vfio_save_complete_precopy_thread_start(vbasedev->name, + d->idstr, d->instance_id); + + /* We reach here with device state STOP or STOP_COPY only */ + if (vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY, + VFIO_DEVICE_STATE_STOP, errp)) { + goto thread_exit; + } + + packet = g_malloc0(sizeof(*packet) + migration->data_buffer_size); + packet->version = VFIO_DEVICE_STATE_PACKET_VER_CURRENT; + + for (idx = 0; ; idx++) { + ssize_t data_size; + size_t packet_size; + + if (multifd_device_state_save_thread_should_exit()) { + error_setg(errp, "operation cancelled"); + goto thread_exit; + } + + data_size = read(migration->data_fd, &packet->data, + migration->data_buffer_size); + if (data_size < 0) { + error_setg(errp, "%s: reading state buffer %" PRIu32 " failed: %d", + vbasedev->name, idx, errno); + goto thread_exit; + } else if (data_size == 0) { + break; + } + + packet->idx = idx; + packet_size = sizeof(*packet) + data_size; + + if (!multifd_queue_device_state(d->idstr, d->instance_id, + (char *)packet, packet_size)) { + error_setg(errp, "%s: multifd data queuing failed", vbasedev->name); + goto thread_exit; + } + + vfio_mig_add_bytes_transferred(packet_size); + } + + if (!vfio_save_complete_precopy_thread_config_state(vbasedev, + d->idstr, + d->instance_id, + idx, errp)) { + goto thread_exit; + } + + ret = true; + +thread_exit: + trace_vfio_save_complete_precopy_thread_end(vbasedev->name, ret); + + return ret; +} + int vfio_multifd_switchover_start(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index f0d28fcef2..a664051eb8 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -23,6 +23,12 @@ bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev); bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, Error **errp); +void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f); + +bool +vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, + Error **errp); + int vfio_multifd_switchover_start(VFIODevice *vbasedev); #endif diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 2ca3fa08d4..416643ddd6 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -120,10 +120,10 @@ static void vfio_migration_set_device_state(VFIODevice *vbasedev, vfio_migration_send_event(vbasedev); } -static int vfio_migration_set_state(VFIODevice *vbasedev, - enum vfio_device_mig_state new_state, - enum vfio_device_mig_state recover_state, - Error **errp) +int vfio_migration_set_state(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state, + enum vfio_device_mig_state recover_state, + Error **errp) { VFIOMigration *migration = vbasedev->migration; uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + @@ -238,8 +238,7 @@ static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev, return ret; } -static int vfio_save_device_config_state(QEMUFile *f, void *opaque, - Error **errp) +int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp) { VFIODevice *vbasedev = opaque; int ret; @@ -638,6 +637,11 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) int ret; Error *local_err = NULL; + if (vfio_multifd_transfer_enabled(vbasedev)) { + vfio_multifd_emit_dummy_eos(vbasedev, f); + return 0; + } + trace_vfio_save_complete_precopy_start(vbasedev->name); /* We reach here with device state STOP or STOP_COPY only */ @@ -669,6 +673,11 @@ static void vfio_save_state(QEMUFile *f, void *opaque) Error *local_err = NULL; int ret; + if (vfio_multifd_transfer_enabled(vbasedev)) { + vfio_multifd_emit_dummy_eos(vbasedev, f); + return; + } + ret = vfio_save_device_config_state(f, opaque, &local_err); if (ret) { error_prepend(&local_err, @@ -825,6 +834,7 @@ static const SaveVMHandlers savevm_vfio_handlers = { */ .load_state_buffer = vfio_multifd_load_state_buffer, .switchover_start = vfio_switchover_start, + .save_live_complete_precopy_thread = vfio_multifd_save_complete_precopy_thread, }; /* ---------------------------------------------------------------------- */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index d6b7e34faa..9347e3a5f6 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -171,6 +171,8 @@ vfio_save_block_precopy_empty_hit(const char *name) " (%s)" vfio_save_cleanup(const char *name) " (%s)" vfio_save_complete_precopy(const char *name, int ret) " (%s) ret %d" vfio_save_complete_precopy_start(const char *name) " (%s)" +vfio_save_complete_precopy_thread_start(const char *name, const char *idstr, uint32_t instance_id) " (%s) idstr %s instance %"PRIu32 +vfio_save_complete_precopy_thread_end(const char *name, int ret) " (%s) ret %d" vfio_save_device_config_state(const char *name) " (%s)" vfio_save_iterate(const char *name, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy initial size %"PRIu64" precopy dirty size %"PRIu64 vfio_save_iterate_start(const char *name) " (%s)" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 9d72ac1eae..961931d9f4 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -298,6 +298,7 @@ void vfio_mig_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); +int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp); int vfio_load_device_config_state(QEMUFile *f, void *opaque); #ifdef CONFIG_LINUX @@ -314,6 +315,11 @@ struct vfio_info_cap_header * vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); struct vfio_info_cap_header * vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); + +int vfio_migration_set_state(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state, + enum vfio_device_mig_state recover_state, + Error **errp); #endif bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); From 623af41dd331d1a57a41bc3374e3d134adb33f4c Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:58 +0100 Subject: [PATCH 2201/2892] vfio/migration: Add x-migration-multifd-transfer VFIO property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This property allows configuring whether to transfer the particular device state via multifd channels when live migrating that device. It defaults to AUTO, which means that VFIO device state transfer via multifd channels is attempted in configurations that otherwise support it. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/d6dbb326e3d53c7104d62c96c9e3dd64e1c7b940.1741124640.git.maciej.szmigiero@oracle.com [ clg: Added documentation ] Signed-off-by: Cédric Le Goater --- docs/devel/migration/vfio.rst | 15 +++++++++++++++ hw/vfio/migration-multifd.c | 18 +++++++++++++++++- hw/vfio/pci.c | 7 +++++++ include/hw/vfio/vfio-common.h | 2 ++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index a803a09bc1..673e354754 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -232,3 +232,18 @@ Postcopy ======== Postcopy migration is currently not supported for VFIO devices. + +Multifd +======= + +Starting from QEMU version 10.0 there's a possibility to transfer VFIO device +_STOP_COPY state via multifd channels. This helps reduce downtime - especially +with multiple VFIO devices or with devices having a large migration state. +As an additional benefit, setting the VFIO device to _STOP_COPY state and +saving its config space is also parallelized (run in a separate thread) in +such migration mode. + +The multifd VFIO device state transfer is controlled by +"x-migration-multifd-transfer" VFIO device property. This property defaults to +AUTO, which means that VFIO device state transfer via multifd channels is +attempted in configurations that otherwise support it. diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index bfb9a72fa4..aacddc503b 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -476,18 +476,34 @@ bool vfio_multifd_transfer_supported(void) bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev) { - return false; + VFIOMigration *migration = vbasedev->migration; + + return migration->multifd_transfer; } bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp) { VFIOMigration *migration = vbasedev->migration; + if (vbasedev->migration_multifd_transfer == ON_OFF_AUTO_AUTO) { + migration->multifd_transfer = vfio_multifd_transfer_supported(); + } else { + migration->multifd_transfer = + vbasedev->migration_multifd_transfer == ON_OFF_AUTO_ON; + } + if (!vfio_multifd_transfer_enabled(vbasedev)) { /* Nothing further to check or do */ return true; } + if (!vfio_multifd_transfer_supported()) { + error_setg(errp, + "%s: Multifd device transfer requested but unsupported in the current config", + vbasedev->name); + return false; + } + if (alloc_multifd) { assert(!migration->multifd); migration->multifd = vfio_multifd_new(); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c1cee280ae..1bbf15cea3 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3381,6 +3381,9 @@ static const Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, vbasedev.enable_migration, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("x-migration-multifd-transfer", VFIOPCIDevice, + vbasedev.migration_multifd_transfer, + ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("migration-events", VFIOPCIDevice, vbasedev.migration_events, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), @@ -3553,6 +3556,10 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) "Skip config space check for Vendor Specific Capability. " "Setting to false will enforce strict checking of VSC content " "(DEBUG)"); + object_class_property_set_description(klass, /* 10.0 */ + "x-migration-multifd-transfer", + "Transfer this device state via " + "multifd channels when live migrating it"); } static const TypeInfo vfio_pci_dev_info = { diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 961931d9f4..04b123a6c9 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -91,6 +91,7 @@ typedef struct VFIOMigration { uint64_t mig_flags; uint64_t precopy_init_size; uint64_t precopy_dirty_size; + bool multifd_transfer; VFIOMultifd *multifd; bool initial_data_sent; @@ -153,6 +154,7 @@ typedef struct VFIODevice { bool no_mmap; bool ram_block_discard_allowed; OnOffAuto enable_migration; + OnOffAuto migration_multifd_transfer; bool migration_events; VFIODeviceOps *ops; unsigned int num_irqs; From 4c765ceaace4e7828e2790d8f4829f69989888de Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:03:59 +0100 Subject: [PATCH 2202/2892] vfio/migration: Make x-migration-multifd-transfer VFIO property mutable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEFINE_PROP_ON_OFF_AUTO() property isn't runtime-mutable so using it would mean that the source VM would need to decide upfront at startup time whether it wants to do a multifd device state transfer at some point. Source VM can run for a long time before being migrated so it is desirable to have a fallback mechanism to the old way of transferring VFIO device state if it turns to be necessary. This brings this property to the same mutability level as ordinary migration parameters, which too can be adjusted at the run time. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/f2f2d66bda477da3e6cb8c0311006cff36e8651d.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration-multifd.c | 4 ++++ hw/vfio/pci.c | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index aacddc503b..233724710b 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -485,6 +485,10 @@ bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp) { VFIOMigration *migration = vbasedev->migration; + /* + * Make a copy of this setting at the start in case it is changed + * mid-migration. + */ if (vbasedev->migration_multifd_transfer == ON_OFF_AUTO_AUTO) { migration->multifd_transfer = vfio_multifd_transfer_supported(); } else { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1bbf15cea3..fdbc15885d 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3357,6 +3357,8 @@ static void vfio_instance_init(Object *obj) pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; } +static PropertyInfo vfio_pci_migration_multifd_transfer_prop; + static const Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), DEFINE_PROP_UUID_NODEFAULT("vf-token", VFIOPCIDevice, vf_token), @@ -3381,9 +3383,10 @@ static const Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, vbasedev.enable_migration, ON_OFF_AUTO_AUTO), - DEFINE_PROP_ON_OFF_AUTO("x-migration-multifd-transfer", VFIOPCIDevice, - vbasedev.migration_multifd_transfer, - ON_OFF_AUTO_AUTO), + DEFINE_PROP("x-migration-multifd-transfer", VFIOPCIDevice, + vbasedev.migration_multifd_transfer, + vfio_pci_migration_multifd_transfer_prop, OnOffAuto, + .set_default = true, .defval.i = ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("migration-events", VFIOPCIDevice, vbasedev.migration_events, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), @@ -3608,6 +3611,17 @@ static const TypeInfo vfio_pci_nohotplug_dev_info = { static void register_vfio_pci_dev_type(void) { + /* + * Ordinary ON_OFF_AUTO property isn't runtime-mutable, but source VM can + * run for a long time before being migrated so it is desirable to have a + * fallback mechanism to the old way of transferring VFIO device state if + * it turns to be necessary. + * The following makes this type of property have the same mutability level + * as ordinary migration parameters. + */ + vfio_pci_migration_multifd_transfer_prop = qdev_prop_on_off_auto; + vfio_pci_migration_multifd_transfer_prop.realized_set_allowed = true; + type_register_static(&vfio_pci_dev_info); type_register_static(&vfio_pci_nohotplug_dev_info); } From 59a67e70950bcc2002d3a8d22a17743e0f70da96 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 4 Mar 2025 23:04:00 +0100 Subject: [PATCH 2203/2892] hw/core/machine: Add compat for x-migration-multifd-transfer VFIO property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a hw_compat entry for recently added x-migration-multifd-transfer VFIO property. Reviewed-by: Cédric Le Goater Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/92c354f0457c152d1f267cc258c6967fff551cb1.1741124640.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/core/machine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index d1ddc3a3db..f52a4f2273 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -45,6 +45,7 @@ GlobalProperty hw_compat_9_2[] = { { "virtio-mem-pci", "vectors", "0" }, { "migration", "multifd-clean-tls-termination", "false" }, { "migration", "send-switchover-start", "off"}, + { "vfio-pci", "x-migration-multifd-transfer", "off" }, }; const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); From 6c5a1467f8d0a9e840c8aa193bc110cc76ee80e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 28 Feb 2025 10:27:32 +0000 Subject: [PATCH 2204/2892] tests/functional: remove unused 'bin_prefix' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was copied over from avocado but has not been used in the new functional tests. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20250228102738.3064045-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 869f3949fe..9d5611c4d7 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -192,7 +192,7 @@ class QemuBaseTest(unittest.TestCase): return False return True - def setUp(self, bin_prefix): + def setUp(self): self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') self.arch = self.qemu_bin.split('-')[-1] self.socketdir = None @@ -254,7 +254,7 @@ class QemuBaseTest(unittest.TestCase): class QemuUserTest(QemuBaseTest): def setUp(self): - super().setUp('qemu-') + super().setUp() self._ldpath = [] def add_ldpath(self, ldpath): @@ -277,7 +277,7 @@ class QemuSystemTest(QemuBaseTest): def setUp(self): self._vms = {} - super().setUp('qemu-system-') + super().setUp() console_log = logging.getLogger('console') console_log.setLevel(logging.DEBUG) From 8188356a260ca0201c42d128d8fa86f40160b513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 28 Feb 2025 10:27:33 +0000 Subject: [PATCH 2205/2892] tests/functional: set 'qemu_bin' as an object level field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'qemu_bin' field is currently set on the class, despite being accessed as if it were an object instance field with 'self.qemu_bin'. This is no obvious need to have it as a class field, so move it into the object instance. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20250228102738.3064045-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 2 +- tests/functional/qemu_test/testcase.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index ecc738922b..bcb5509512 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -173,7 +173,7 @@ QEMU binary selection ^^^^^^^^^^^^^^^^^^^^^ The QEMU binary used for the ``self.vm`` QEMUMachine instance will -primarily depend on the value of the ``qemu_bin`` class attribute. +primarily depend on the value of the ``qemu_bin`` instance attribute. If it is not explicitly set by the test code, its default value will be the result the QEMU_TEST_QEMU_BINARY environment variable. diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 9d5611c4d7..058bf270ec 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -33,7 +33,6 @@ from .uncompress import uncompress class QemuBaseTest(unittest.TestCase): - qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') arch = None workdir = None @@ -193,6 +192,7 @@ class QemuBaseTest(unittest.TestCase): return True def setUp(self): + self.qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') self.arch = self.qemu_bin.split('-')[-1] self.socketdir = None From 188f71929520940048ec5ba85ea30588e0566e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 28 Feb 2025 10:27:35 +0000 Subject: [PATCH 2206/2892] tests/functional: reduce tuxrun maxmem to work on 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit maxmem=4G is too large to address on 32-bit hosts, so reduce it to 2G since the tuxrun tests don't actually need such an elevated memory limit. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-ID: <20250228102738.3064045-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_tuxrun.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/test_ppc64_tuxrun.py index 05c6162b5e..e8f79c676e 100755 --- a/tests/functional/test_ppc64_tuxrun.py +++ b/tests/functional/test_ppc64_tuxrun.py @@ -64,7 +64,7 @@ class TuxRunPPC64Test(TuxRunBaselineTest): ',"index":1,"id":"pci.1"}') self.vm.add_args('-device', '{"driver":"spapr-vscsi","id":"scsi1"' ',"reg":12288}') - self.vm.add_args('-m', '2G,slots=32,maxmem=4G', + self.vm.add_args('-m', '1G,slots=32,maxmem=2G', '-object', 'memory-backend-ram,id=ram1,size=1G', '-device', 'pc-dimm,id=dimm1,memdev=ram1') From 87c8b4fc3c1c89ec52540bfb74f9b0518f247323 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:07:55 +0100 Subject: [PATCH 2207/2892] docs/about/build-platforms: Correct minimum supported Python version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: ca056f4499c2 (Python: Drop support for Python 3.7) Signed-off-by: Markus Armbruster Message-ID: <20250227080757.3978333-2-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- docs/about/build-platforms.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 482b09819c..1552b1a704 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -101,7 +101,7 @@ Python runtime option of the ``configure`` script to point QEMU to a supported version of the Python runtime. - As of QEMU |version|, the minimum supported version of Python is 3.7. + As of QEMU |version|, the minimum supported version of Python is 3.8. Python build dependencies Some of QEMU's build dependencies are written in Python. Usually these From 5fbc8126acaf07a0294f8f94f4c244c3c5b62d5d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:07:56 +0100 Subject: [PATCH 2208/2892] qapi: Eliminate OrderedDict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use OrderedDict to ensure dictionary order is insertion order. Plain dict does that since Python 3.6, but it wasn't guaranteed until 3.7. Since we have 3.7 now, replace OrderedDict by dict. Signed-off-by: Markus Armbruster Message-ID: <20250227080757.3978333-3-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- scripts/qapi/parser.py | 5 ++--- scripts/qapi/schema.py | 11 +++++------ tests/qapi-schema/test-qapi.py | 11 +---------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index adc85b5b39..64f0bb824a 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -14,7 +14,6 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from collections import OrderedDict import os import re from typing import ( @@ -154,7 +153,7 @@ class QAPISchemaParser: "value of 'include' must be a string") incl_fname = os.path.join(os.path.dirname(self._fname), include) - self._add_expr(OrderedDict({'include': incl_fname}), info) + self._add_expr({'include': incl_fname}, info) exprs_include = self._include(include, info, incl_fname, self._included) if exprs_include: @@ -355,7 +354,7 @@ class QAPISchemaParser: raise QAPIParseError(self, "stray '%s'" % match.group(0)) def get_members(self) -> Dict[str, object]: - expr: Dict[str, object] = OrderedDict() + expr: Dict[str, object] = {} if self.tok == '}': self.accept() return expr diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 7f70969c09..cbe3b5aa91 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -19,7 +19,6 @@ from __future__ import annotations from abc import ABC, abstractmethod -from collections import OrderedDict import os import re from typing import ( @@ -557,7 +556,7 @@ class QAPISchemaObjectType(QAPISchemaType): super().check(schema) assert self._checked and not self._check_complete - seen = OrderedDict() + seen = {} if self._base_name: self.base = schema.resolve_type(self._base_name, self.info, "'base'") @@ -1141,10 +1140,10 @@ class QAPISchema: self.docs = parser.docs self._entity_list: List[QAPISchemaEntity] = [] self._entity_dict: Dict[str, QAPISchemaDefinition] = {} - self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() + self._module_dict: Dict[str, QAPISchemaModule] = {} # NB, values in the dict will identify the first encountered # usage of a named feature only - self._feature_dict: Dict[str, QAPISchemaFeature] = OrderedDict() + self._feature_dict: Dict[str, QAPISchemaFeature] = {} # All schemas get the names defined in the QapiSpecialFeature enum. # Rely on dict iteration order matching insertion order so that @@ -1454,7 +1453,7 @@ class QAPISchema: ifcond = QAPISchemaIfCond(expr.get('if')) info = expr.info features = self._make_features(expr.get('features'), info) - if isinstance(data, OrderedDict): + if isinstance(data, dict): data = self._make_implicit_object_type( name, info, ifcond, 'arg', self._make_members(data, info)) @@ -1473,7 +1472,7 @@ class QAPISchema: ifcond = QAPISchemaIfCond(expr.get('if')) info = expr.info features = self._make_features(expr.get('features'), info) - if isinstance(data, OrderedDict): + if isinstance(data, dict): data = self._make_implicit_object_type( name, info, ifcond, 'arg', self._make_members(data, info)) diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 7e3f9f4aa1..8fe951c880 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -96,17 +96,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): @staticmethod def _print_if(ifcond, indent=4): - # TODO Drop this hack after replacing OrderedDict by plain - # dict (requires Python 3.7) - def _massage(subcond): - if isinstance(subcond, str): - return subcond - if isinstance(subcond, list): - return [_massage(val) for val in subcond] - return {key: _massage(val) for key, val in subcond.items()} - if ifcond.is_present(): - print('%sif %s' % (' ' * indent, _massage(ifcond.ifcond))) + print('%sif %s' % (' ' * indent, ifcond.ifcond)) @classmethod def _print_features(cls, features, indent=4): From e6985cc4407ddb14a27a282efc0b4c8d34534317 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:07:57 +0100 Subject: [PATCH 2209/2892] qapi/introspect: Use @dataclass to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A TODO comment in class Annotated reminds us to simplify it once we can use @dataclass, new in Python 3.7. We have that now, so do it. There's a similar comment in scripts/qapi/source.py, but I can't figure out how to use @dataclass there. Left for another day. Signed-off-by: Markus Armbruster Message-ID: <20250227080757.3978333-4-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- scripts/qapi/introspect.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 42e5185c7c..89ee5d5f17 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -11,6 +11,7 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ +from dataclasses import dataclass from typing import ( Any, Dict, @@ -79,19 +80,16 @@ SchemaInfoCommand = Dict[str, object] _ValueT = TypeVar('_ValueT', bound=_Value) +@dataclass class Annotated(Generic[_ValueT]): """ Annotated generally contains a SchemaInfo-like type (as a dict), But it also used to wrap comments/ifconds around scalar leaf values, for the benefit of features and enums. """ - # TODO: Remove after Python 3.7 adds @dataclass: - # pylint: disable=too-few-public-methods - def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond, - comment: Optional[str] = None): - self.value = value - self.comment: Optional[str] = comment - self.ifcond = ifcond + value: _ValueT + ifcond: QAPISchemaIfCond + comment: Optional[str] = None def _tree_to_qlit(obj: JSONValue, From 744bce1bf7fecf8b8a8de484328515800f9c9639 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:55:56 +0100 Subject: [PATCH 2210/2892] qdev: Delete unused qdev_prop_enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Message-ID: <20250227085601.4140852-2-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- hw/core/qdev-properties.c | 7 ------- include/hw/qdev-properties.h | 1 - 2 files changed, 8 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 0b52aad555..2540bd8880 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -122,13 +122,6 @@ void qdev_propinfo_set_default_value_enum(ObjectProperty *op, qapi_enum_lookup(prop->info->enum_table, prop->defval.i)); } -const PropertyInfo qdev_prop_enum = { - .name = "enum", - .get = qdev_propinfo_get_enum, - .set = qdev_propinfo_set_enum, - .set_default_value = qdev_propinfo_set_default_value_enum, -}; - /* Bit */ static uint32_t qdev_get_prop_mask(const Property *prop) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index bf27375a3c..ae6ec2b990 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -49,7 +49,6 @@ struct PropertyInfo { extern const PropertyInfo qdev_prop_bit; extern const PropertyInfo qdev_prop_bit64; extern const PropertyInfo qdev_prop_bool; -extern const PropertyInfo qdev_prop_enum; extern const PropertyInfo qdev_prop_uint8; extern const PropertyInfo qdev_prop_uint16; extern const PropertyInfo qdev_prop_uint32; From e09daf1dff8f5117b3e482f1907e762470b5675c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:55:57 +0100 Subject: [PATCH 2211/2892] qdev: Change qdev_prop_pci_devfn member @name from "int32" to "str" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Properties using qdev_prop_pci_devfn initially accepted a string of the form "DEV.FN" or "DEV" where DEV and FN are in hexadecimal. Member @name was "pci-devfn" initially. Commit b403298adb5 (qdev: make the non-legacy pci address property accept an integer) changed them to additionally accept integers: bits 3..7 are DEV, and bits 0..2 are FN. This is inaccessible externally in device_add so far. The commit also changed @name to "int32", and set member @legacy-name to "pci-devfn". Together, this kept QMP command device-list-properties unaffected: it used @name only when @legacy_name was null. Commit 07d09c58dbb (qmp: Print descriptions of object properties) quietly dumbed that down to use @name always, and the next commit 18b91a3e082q (qdev: Drop legacy_name from qdev properties) dropped member @legacy_name. This changed the value of @type reported by QMP command device-list-properties from "pci-devfn" to "int32". But "int32" is misleading: device_add actually wants QAPI type "str". So change @name to that. Signed-off-by: Markus Armbruster Message-ID: <20250227085601.4140852-3-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- hw/core/qdev-properties-system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a91551a5ee..f2b6136d0a 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -882,7 +882,7 @@ static int print_pci_devfn(Object *obj, const Property *prop, char *dest, } const PropertyInfo qdev_prop_pci_devfn = { - .name = "int32", + .name = "str", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, .get = qdev_propinfo_get_int32, From c98dac169e645e7efe0b912a9acb3c5ec88b3bdf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:55:58 +0100 Subject: [PATCH 2212/2892] qdev: Rename PropertyInfo member @name to @type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PropertyInfo member @name becomes ObjectProperty member @type, while Property member @name becomes ObjectProperty member @name. Rename the former. Signed-off-by: Markus Armbruster Message-ID: <20250227085601.4140852-4-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé [One missed instance of @type fixed] --- backends/tpm/tpm_util.c | 2 +- hw/block/xen-block.c | 2 +- hw/core/qdev-properties-system.c | 52 ++++++++++++++++---------------- hw/core/qdev-properties.c | 36 +++++++++++----------- hw/display/apple-gfx.m | 2 +- hw/misc/xlnx-versal-trng.c | 2 +- hw/nvme/nguid.c | 2 +- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-efuse.c | 2 +- hw/pci/pci.c | 2 +- hw/s390x/ccw-device.c | 2 +- hw/s390x/css.c | 4 +-- hw/s390x/s390-pci-bus.c | 2 +- hw/vfio/pci-quirks.c | 2 +- include/hw/qdev-properties.h | 2 +- target/riscv/cpu.c | 28 ++++++++--------- target/sparc/cpu.c | 2 +- 17 files changed, 73 insertions(+), 73 deletions(-) diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index 0a428eaf75..f07a2656ce 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -76,7 +76,7 @@ static void release_tpm(Object *obj, const char *name, void *opaque) } const PropertyInfo qdev_prop_tpm = { - .name = "str", + .type = "str", .description = "ID of a tpm to use as a backend", .get = get_tpm, .set = set_tpm, diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 6c26052561..7c9d1b658c 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -661,7 +661,7 @@ invalid: * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html */ static const PropertyInfo xen_block_prop_vdev = { - .name = "str", + .type = "str", .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*", .get = xen_block_get_vdev, .set = xen_block_set_vdev, diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index f2b6136d0a..56fe5e25db 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -235,7 +235,7 @@ static void release_drive(Object *obj, const char *name, void *opaque) } const PropertyInfo qdev_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as a backend", .realized_set_allowed = true, .get = get_drive, @@ -244,7 +244,7 @@ const PropertyInfo qdev_prop_drive = { }; const PropertyInfo qdev_prop_drive_iothread = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as a backend", .realized_set_allowed = true, .get = get_drive, @@ -312,7 +312,7 @@ static void release_chr(Object *obj, const char *name, void *opaque) } const PropertyInfo qdev_prop_chr = { - .name = "str", + .type = "str", .description = "ID of a chardev to use as a backend", .get = get_chr, .set = set_chr, @@ -386,7 +386,7 @@ inval: } const PropertyInfo qdev_prop_macaddr = { - .name = "str", + .type = "str", .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, .set = set_mac, @@ -474,7 +474,7 @@ out: } const PropertyInfo qdev_prop_netdev = { - .name = "str", + .type = "str", .description = "ID of a netdev to use as a backend", .get = get_netdev, .set = set_netdev, @@ -512,7 +512,7 @@ static void set_audiodev(Object *obj, Visitor *v, const char* name, } const PropertyInfo qdev_prop_audiodev = { - .name = "str", + .type = "str", .description = "ID of an audiodev to use as a backend", /* release done on shutdown */ .get = get_audiodev, @@ -602,7 +602,7 @@ static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); const PropertyInfo qdev_prop_losttickpolicy = { - .name = "LostTickPolicy", + .type = "LostTickPolicy", .enum_table = &LostTickPolicy_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_losttickpolicy, @@ -628,7 +628,7 @@ static void set_blocksize(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_blocksize = { - .name = "size", + .type = "size", .description = "A power of two between " MIN_BLOCK_SIZE_STR " and " MAX_BLOCK_SIZE_STR, .get = qdev_propinfo_get_size32, @@ -641,7 +641,7 @@ const PropertyInfo qdev_prop_blocksize = { QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); const PropertyInfo qdev_prop_blockdev_on_error = { - .name = "BlockdevOnError", + .type = "BlockdevOnError", .description = "Error handling policy, " "report/ignore/enospc/stop/auto", .enum_table = &BlockdevOnError_lookup, @@ -655,7 +655,7 @@ const PropertyInfo qdev_prop_blockdev_on_error = { QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); const PropertyInfo qdev_prop_bios_chs_trans = { - .name = "BiosAtaTranslation", + .type = "BiosAtaTranslation", .description = "Logical CHS translation algorithm, " "auto/none/lba/large/rechs", .enum_table = &BiosAtaTranslation_lookup, @@ -667,7 +667,7 @@ const PropertyInfo qdev_prop_bios_chs_trans = { /* --- FDC default drive types */ const PropertyInfo qdev_prop_fdc_drive_type = { - .name = "FdcDriveType", + .type = "FdcDriveType", .description = "FDC drive type, " "144/288/120/none/auto", .enum_table = &FloppyDriveType_lookup, @@ -679,7 +679,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = { /* --- MultiFDCompression --- */ const PropertyInfo qdev_prop_multifd_compression = { - .name = "MultiFDCompression", + .type = "MultiFDCompression", .description = "multifd_compression values, " "none/zlib/zstd/qpl/uadk/qatzip", .enum_table = &MultiFDCompression_lookup, @@ -693,7 +693,7 @@ const PropertyInfo qdev_prop_multifd_compression = { QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); const PropertyInfo qdev_prop_mig_mode = { - .name = "MigMode", + .type = "MigMode", .description = "mig_mode values, " "normal,cpr-reboot", .enum_table = &MigMode_lookup, @@ -707,7 +707,7 @@ const PropertyInfo qdev_prop_mig_mode = { QEMU_BUILD_BUG_ON(sizeof(GranuleMode) != sizeof(int)); const PropertyInfo qdev_prop_granule_mode = { - .name = "GranuleMode", + .type = "GranuleMode", .description = "granule_mode values, " "4k, 8k, 16k, 64k, host", .enum_table = &GranuleMode_lookup, @@ -717,7 +717,7 @@ const PropertyInfo qdev_prop_granule_mode = { }; const PropertyInfo qdev_prop_zero_page_detection = { - .name = "ZeroPageDetection", + .type = "ZeroPageDetection", .description = "zero_page_detection values, " "none,legacy,multifd", .enum_table = &ZeroPageDetection_lookup, @@ -801,7 +801,7 @@ out: } const PropertyInfo qdev_prop_reserved_region = { - .name = "reserved_region", + .type = "reserved_region", .description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0", .get = get_reserved_region, .set = set_reserved_region, @@ -882,7 +882,7 @@ static int print_pci_devfn(Object *obj, const Property *prop, char *dest, } const PropertyInfo qdev_prop_pci_devfn = { - .name = "str", + .type = "str", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, .get = qdev_propinfo_get_int32, @@ -988,7 +988,7 @@ inval: } const PropertyInfo qdev_prop_pci_host_devaddr = { - .name = "str", + .type = "str", .description = "Address (bus/device/function) of " "the host device, example: 04:10.0", .get = get_pci_host_devaddr, @@ -998,7 +998,7 @@ const PropertyInfo qdev_prop_pci_host_devaddr = { /* --- OffAutoPCIBAR off/auto/bar0/bar1/bar2/bar3/bar4/bar5 --- */ const PropertyInfo qdev_prop_off_auto_pcibar = { - .name = "OffAutoPCIBAR", + .type = "OffAutoPCIBAR", .description = "off/auto/bar0/bar1/bar2/bar3/bar4/bar5", .enum_table = &OffAutoPCIBAR_lookup, .get = qdev_propinfo_get_enum, @@ -1080,7 +1080,7 @@ static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_pcie_link_speed = { - .name = "PCIELinkSpeed", + .type = "PCIELinkSpeed", .description = "2_5/5/8/16/32/64", .enum_table = &PCIELinkSpeed_lookup, .get = get_prop_pcielinkspeed, @@ -1168,7 +1168,7 @@ static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_pcie_link_width = { - .name = "PCIELinkWidth", + .type = "PCIELinkWidth", .description = "1/2/4/8/12/16/32", .enum_table = &PCIELinkWidth_lookup, .get = get_prop_pcielinkwidth, @@ -1218,7 +1218,7 @@ static void set_default_uuid_auto(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_uuid = { - .name = "str", + .type = "str", .description = "UUID (aka GUID) or \"" UUID_VALUE_AUTO "\" for random value (default)", .get = get_uuid, @@ -1231,7 +1231,7 @@ const PropertyInfo qdev_prop_uuid = { QEMU_BUILD_BUG_ON(sizeof(S390CpuEntitlement) != sizeof(int)); const PropertyInfo qdev_prop_cpus390entitlement = { - .name = "S390CpuEntitlement", + .type = "S390CpuEntitlement", .description = "low/medium (default)/high", .enum_table = &S390CpuEntitlement_lookup, .get = qdev_propinfo_get_enum, @@ -1276,7 +1276,7 @@ static void release_iothread_vq_mapping_list(Object *obj, } const PropertyInfo qdev_prop_iothread_vq_mapping_list = { - .name = "IOThreadVirtQueueMappingList", + .type = "IOThreadVirtQueueMappingList", .description = "IOThread virtqueue mapping list [{\"iothread\":\"\", " "\"vqs\":[1,2,3,...]},...]", .get = get_iothread_vq_mapping_list, @@ -1287,7 +1287,7 @@ const PropertyInfo qdev_prop_iothread_vq_mapping_list = { /* --- Endian modes */ const PropertyInfo qdev_prop_endian_mode = { - .name = "EndianMode", + .type = "EndianMode", .description = "Endian mode, big/little/unspecified", .enum_table = &EndianMode_lookup, .get = qdev_propinfo_get_enum, @@ -1296,7 +1296,7 @@ const PropertyInfo qdev_prop_endian_mode = { }; const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = { - .name = "VMAppleVirtioBlkVariant", + .type = "VMAppleVirtioBlkVariant", .description = "unspecified/root/aux", .enum_table = &VMAppleVirtioBlkVariant_lookup, .get = qdev_propinfo_get_enum, diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 2540bd8880..5a801057db 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -169,7 +169,7 @@ static void set_default_value_bool(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_bit = { - .name = "bool", + .type = "bool", .description = "on/off", .get = prop_get_bit, .set = prop_set_bit, @@ -218,7 +218,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_bit64 = { - .name = "bool", + .type = "bool", .description = "on/off", .get = prop_get_bit64, .set = prop_set_bit64, @@ -246,7 +246,7 @@ static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_bool = { - .name = "bool", + .type = "bool", .get = get_bool, .set = set_bool, .set_default_value = set_default_value_bool, @@ -285,7 +285,7 @@ void qdev_propinfo_set_default_value_uint(ObjectProperty *op, } const PropertyInfo qdev_prop_uint8 = { - .name = "uint8", + .type = "uint8", .get = get_uint8, .set = set_uint8, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -312,7 +312,7 @@ static void set_uint16(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_uint16 = { - .name = "uint16", + .type = "uint16", .get = get_uint16, .set = set_uint16, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -357,14 +357,14 @@ static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_uint32 = { - .name = "uint32", + .type = "uint32", .get = get_uint32, .set = set_uint32, .set_default_value = qdev_propinfo_set_default_value_uint, }; const PropertyInfo qdev_prop_int32 = { - .name = "int32", + .type = "int32", .get = qdev_propinfo_get_int32, .set = set_int32, .set_default_value = qdev_propinfo_set_default_value_int, @@ -409,14 +409,14 @@ static void set_int64(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_uint64 = { - .name = "uint64", + .type = "uint64", .get = get_uint64, .set = set_uint64, .set_default_value = qdev_propinfo_set_default_value_uint, }; const PropertyInfo qdev_prop_int64 = { - .name = "int64", + .type = "int64", .get = get_int64, .set = set_int64, .set_default_value = qdev_propinfo_set_default_value_int, @@ -436,7 +436,7 @@ static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_uint64_checkmask = { - .name = "uint64", + .type = "uint64", .get = get_uint64, .set = set_uint64_checkmask, }; @@ -478,7 +478,7 @@ static void set_string(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_string = { - .name = "str", + .type = "str", .release = release_string, .get = get_string, .set = set_string, @@ -487,7 +487,7 @@ const PropertyInfo qdev_prop_string = { /* --- on/off/auto --- */ const PropertyInfo qdev_prop_on_off_auto = { - .name = "OnOffAuto", + .type = "OnOffAuto", .description = "on/off/auto", .enum_table = &OnOffAuto_lookup, .get = qdev_propinfo_get_enum, @@ -530,7 +530,7 @@ static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_size32 = { - .name = "size", + .type = "size", .get = qdev_propinfo_get_size32, .set = set_size32, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -733,7 +733,7 @@ static void default_prop_array(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_array = { - .name = "list", + .type = "list", .get = get_prop_array, .set = set_prop_array, .release = release_prop_array, @@ -937,7 +937,7 @@ static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_size = { - .name = "size", + .type = "size", .get = get_size, .set = set_size, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -955,7 +955,7 @@ static ObjectProperty *create_link_property(ObjectClass *oc, const char *name, } const PropertyInfo qdev_prop_link = { - .name = "link", + .type = "link", .create = create_link_property, }; @@ -966,7 +966,7 @@ void qdev_property_add_static(DeviceState *dev, const Property *prop) assert(!prop->info->create); - op = object_property_add(obj, prop->name, prop->info->name, + op = object_property_add(obj, prop->name, prop->info->type, field_prop_getter(prop->info), field_prop_setter(prop->info), prop->info->release, @@ -993,7 +993,7 @@ static void qdev_class_add_property(DeviceClass *klass, const char *name, op = prop->info->create(oc, name, prop); } else { op = object_class_property_add(oc, - name, prop->info->name, + name, prop->info->type, field_prop_getter(prop->info), field_prop_setter(prop->info), prop->info->release, diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 1554f3b801..c4323574e1 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -871,7 +871,7 @@ separator_error: } const PropertyInfo qdev_prop_apple_gfx_display_mode = { - .name = "display_mode", + .type = "display_mode", .description = "Display mode in pixels and Hertz, as x@ " "Example: 3840x2160@60", diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index dbd9b58a4e..9a44a90d1e 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -652,7 +652,7 @@ static void trng_prop_fault_event_set(Object *obj, Visitor *v, } static const PropertyInfo trng_prop_fault_events = { - .name = "uint32:bits", + .type = "uint32:bits", .description = "Set to trigger TRNG fault events", .set = trng_prop_fault_event_set, .realized_set_allowed = true, diff --git a/hw/nvme/nguid.c b/hw/nvme/nguid.c index be63cb75e1..4cd6fad6ac 100644 --- a/hw/nvme/nguid.c +++ b/hw/nvme/nguid.c @@ -179,7 +179,7 @@ static void set_nguid(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_nguid = { - .name = "str", + .type = "str", .description = "NGUID or \"" NGUID_VALUE_AUTO "\" for random value", .get = get_nguid, diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 0e8552ce65..14cc9073c7 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -502,7 +502,7 @@ static void bbram_prop_release_drive(Object *obj, const char *name, } static const PropertyInfo bbram_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as BBRAM backend", .realized_set_allowed = true, .get = bbram_prop_get_drive, diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index e2e8311a48..29e7dd539e 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -257,7 +257,7 @@ static void efuse_prop_release_drive(Object *obj, const char *name, } static const PropertyInfo efuse_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as eFUSE backend", .realized_set_allowed = true, .get = efuse_prop_get_drive, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1d42847ef0..e3c2866830 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -77,7 +77,7 @@ static void prop_pci_busnr_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pci_busnr = { - .name = "busnr", + .type = "busnr", .get = prop_pci_busnr_get, }; diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 494faebb5a..5aa6ff8eac 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -74,7 +74,7 @@ static void ccw_device_set_loadparm(Object *obj, Visitor *v, } const PropertyInfo ccw_loadparm = { - .name = "ccw_loadparm", + .type = "ccw_loadparm", .description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass" " to the guest loader/kernel", .get = ccw_device_get_loadparm, diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 4e27b2961b..738800c98d 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -2523,7 +2523,7 @@ out: } const PropertyInfo css_devid_propinfo = { - .name = "str", + .type = "str", .description = "Identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", .get = get_css_devid, @@ -2531,7 +2531,7 @@ const PropertyInfo css_devid_propinfo = { }; const PropertyInfo css_devid_ro_propinfo = { - .name = "str", + .type = "str", .description = "Read-only identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", .get = get_css_devid, diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 913d72cc74..7f340965c0 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1495,7 +1495,7 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, } static const PropertyInfo s390_pci_fid_propinfo = { - .name = "zpci_fid", + .type = "zpci_fid", .get = s390_pci_get_fid, .set = s390_pci_set_fid, }; diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index fbe43b0a79..ba97d59791 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1480,7 +1480,7 @@ static void set_nv_gpudirect_clique_id(Object *obj, Visitor *v, } const PropertyInfo qdev_prop_nv_gpudirect_clique = { - .name = "uint4", + .type = "uint4", .description = "NVIDIA GPUDirect Clique ID (0 - 15)", .get = get_nv_gpudirect_clique_id, .set = set_nv_gpudirect_clique_id, diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index ae6ec2b990..15fcec5260 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -30,7 +30,7 @@ struct Property { }; struct PropertyInfo { - const char *name; + const char *type; const char *description; const QEnumLookup *enum_table; bool realized_set_allowed; /* allow setting property on realized device */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 47424fd5e2..1ac34e398d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1816,7 +1816,7 @@ static void prop_pmu_num_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pmu_num = { - .name = "pmu-num", + .type = "pmu-num", .get = prop_pmu_num_get, .set = prop_pmu_num_set, }; @@ -1857,7 +1857,7 @@ static void prop_pmu_mask_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pmu_mask = { - .name = "pmu-mask", + .type = "pmu-mask", .get = prop_pmu_mask_get, .set = prop_pmu_mask_set, }; @@ -1888,7 +1888,7 @@ static void prop_mmu_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_mmu = { - .name = "mmu", + .type = "mmu", .get = prop_mmu_get, .set = prop_mmu_set, }; @@ -1919,7 +1919,7 @@ static void prop_pmp_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pmp = { - .name = "pmp", + .type = "pmp", .get = prop_pmp_get, .set = prop_pmp_set, }; @@ -1993,7 +1993,7 @@ static void prop_priv_spec_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_priv_spec = { - .name = "priv_spec", + .type = "priv_spec", .get = prop_priv_spec_get, .set = prop_priv_spec_set, }; @@ -2024,7 +2024,7 @@ static void prop_vext_spec_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_vext_spec = { - .name = "vext_spec", + .type = "vext_spec", .get = prop_vext_spec_get, .set = prop_vext_spec_set, }; @@ -2065,7 +2065,7 @@ static void prop_vlen_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_vlen = { - .name = "vlen", + .type = "vlen", .get = prop_vlen_get, .set = prop_vlen_set, }; @@ -2105,7 +2105,7 @@ static void prop_elen_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_elen = { - .name = "elen", + .type = "elen", .get = prop_elen_get, .set = prop_elen_set, }; @@ -2140,7 +2140,7 @@ static void prop_cbom_blksize_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_cbom_blksize = { - .name = "cbom_blocksize", + .type = "cbom_blocksize", .get = prop_cbom_blksize_get, .set = prop_cbom_blksize_set, }; @@ -2175,7 +2175,7 @@ static void prop_cbop_blksize_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_cbop_blksize = { - .name = "cbop_blocksize", + .type = "cbop_blocksize", .get = prop_cbop_blksize_get, .set = prop_cbop_blksize_set, }; @@ -2210,7 +2210,7 @@ static void prop_cboz_blksize_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_cboz_blksize = { - .name = "cboz_blocksize", + .type = "cboz_blocksize", .get = prop_cboz_blksize_get, .set = prop_cboz_blksize_set, }; @@ -2245,7 +2245,7 @@ static void prop_mvendorid_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_mvendorid = { - .name = "mvendorid", + .type = "mvendorid", .get = prop_mvendorid_get, .set = prop_mvendorid_set, }; @@ -2280,7 +2280,7 @@ static void prop_mimpid_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_mimpid = { - .name = "mimpid", + .type = "mimpid", .get = prop_mimpid_get, .set = prop_mimpid_set, }; @@ -2336,7 +2336,7 @@ static void prop_marchid_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_marchid = { - .name = "marchid", + .type = "marchid", .get = prop_marchid_get, .set = prop_marchid_set, }; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index fbd38ec334..c8ea35be76 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -938,7 +938,7 @@ static void sparc_set_nwindows(Object *obj, Visitor *v, const char *name, } static const PropertyInfo qdev_prop_nwindows = { - .name = "int", + .type = "int", .get = sparc_get_nwindows, .set = sparc_set_nwindows, }; From ff30d3b1ac7777dd6048fb7932fedce51f3ea89d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:55:59 +0100 Subject: [PATCH 2213/2892] qdev: Change values of PropertyInfo member @type to be QAPI types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PropertyInfo member @type is externally visible via QMP device-list-properties and qom-list-properies. Its meaning is not documented at its definition. It gets passed as @type argument to object_property_add() and object_class_property_add(). This argument's documentation isn't of much help, either: * @type: the type name of the property. This namespace is pretty loosely * defined. Sub namespaces are constructed by using a prefix and then * to angle brackets. For instance, the type 'virtio-net-pci' in the * 'link' namespace would be 'link'. The two QMP commands document it as # @type: the type of the property. This will typically come in one of # four forms: # # 1) A primitive type such as 'u8', 'u16', 'bool', 'str', or # 'double'. These types are mapped to the appropriate JSON # type. # # 2) A child type in the form 'child' where subtype is a # qdev device type name. Child properties create the # composition tree. # # 3) A link type in the form 'link' where subtype is a # qdev device type name. Link properties form the device model # graph. "Typically come in one of four forms" followed by three items inspires the level of trust that is appropriate here. Clean up a bunch of funnies: * qdev_prop_fdc_drive_type.type is "FdcDriveType". Its .enum_table refers to QAPI type "FloppyDriveType". So use that. * qdev_prop_reserved_region is "reserved_region". Its only user is an array property called "reserved-regions". Its .set() visits str. So change @type to "str". * trng_prop_fault_event_set.type is "uint32:bits". Its .set() visits uint32, so change @type to "uint32". If we believe mentioning it's actually bits is useful, the proper place would be .description. * ccw_loadparm.type is "ccw_loadparm". It's users are properties called "loadparm". Its .set() visits str. So change @type to "str". * qdev_prop_nv_gpudirect_clique.type is "uint4". Its set() visits uint8, so change @type to "uint8". If we believe mentioning the range is useful, the proper place would be .description. * s390_pci_fid_propinfo.type is "zpci_fid". Its .set() visits uint32. So change type to that, and move the "zpci_fid" to .description. This is admittedly a lousy description, but it's still an improvement; for instance, output of -device zpci,help changes from fid= to fid= - zpci_fid * Similarly for a raft of PropertyInfo in target/riscv/cpu.c. Signed-off-by: Markus Armbruster Message-ID: <20250227085601.4140852-5-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé [Commit message typo fixed] --- hw/core/qdev-properties-system.c | 4 +-- hw/misc/xlnx-versal-trng.c | 2 +- hw/s390x/ccw-device.c | 2 +- hw/s390x/s390-pci-bus.c | 3 ++- hw/vfio/pci-quirks.c | 2 +- target/riscv/cpu.c | 44 ++++++++++++++++++++++---------- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 56fe5e25db..0ac1485d54 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -667,7 +667,7 @@ const PropertyInfo qdev_prop_bios_chs_trans = { /* --- FDC default drive types */ const PropertyInfo qdev_prop_fdc_drive_type = { - .type = "FdcDriveType", + .type = "FloppyDriveType", .description = "FDC drive type, " "144/288/120/none/auto", .enum_table = &FloppyDriveType_lookup, @@ -801,7 +801,7 @@ out: } const PropertyInfo qdev_prop_reserved_region = { - .type = "reserved_region", + .type = "str", .description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0", .get = get_reserved_region, .set = set_reserved_region, diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 9a44a90d1e..ba93f93cab 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -652,7 +652,7 @@ static void trng_prop_fault_event_set(Object *obj, Visitor *v, } static const PropertyInfo trng_prop_fault_events = { - .type = "uint32:bits", + .type = "uint32", .description = "Set to trigger TRNG fault events", .set = trng_prop_fault_event_set, .realized_set_allowed = true, diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 5aa6ff8eac..1d4b8ea35c 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -74,7 +74,7 @@ static void ccw_device_set_loadparm(Object *obj, Visitor *v, } const PropertyInfo ccw_loadparm = { - .type = "ccw_loadparm", + .type = "str", .description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass" " to the guest loader/kernel", .get = ccw_device_get_loadparm, diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 7f340965c0..04cdd4a11b 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1495,7 +1495,8 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, } static const PropertyInfo s390_pci_fid_propinfo = { - .type = "zpci_fid", + .type = "uint32", + .description = "zpci_fid", .get = s390_pci_get_fid, .set = s390_pci_set_fid, }; diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index ba97d59791..c53591fe2b 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1480,7 +1480,7 @@ static void set_nv_gpudirect_clique_id(Object *obj, Visitor *v, } const PropertyInfo qdev_prop_nv_gpudirect_clique = { - .type = "uint4", + .type = "uint8", .description = "NVIDIA GPUDirect Clique ID (0 - 15)", .get = get_nv_gpudirect_clique_id, .set = set_nv_gpudirect_clique_id, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1ac34e398d..045c9c78ee 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1816,7 +1816,8 @@ static void prop_pmu_num_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pmu_num = { - .type = "pmu-num", + .type = "int8", + .description = "pmu-num", .get = prop_pmu_num_get, .set = prop_pmu_num_set, }; @@ -1857,7 +1858,8 @@ static void prop_pmu_mask_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pmu_mask = { - .type = "pmu-mask", + .type = "int8", + .description = "pmu-mask", .get = prop_pmu_mask_get, .set = prop_pmu_mask_set, }; @@ -1888,7 +1890,8 @@ static void prop_mmu_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_mmu = { - .type = "mmu", + .type = "bool", + .description = "mmu", .get = prop_mmu_get, .set = prop_mmu_set, }; @@ -1919,7 +1922,8 @@ static void prop_pmp_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_pmp = { - .type = "pmp", + .type = "bool", + .description = "pmp", .get = prop_pmp_get, .set = prop_pmp_set, }; @@ -1993,7 +1997,9 @@ static void prop_priv_spec_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_priv_spec = { - .type = "priv_spec", + .type = "str", + .description = "priv_spec", + /* FIXME enum? */ .get = prop_priv_spec_get, .set = prop_priv_spec_set, }; @@ -2024,7 +2030,9 @@ static void prop_vext_spec_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_vext_spec = { - .type = "vext_spec", + .type = "str", + .description = "vext_spec", + /* FIXME enum? */ .get = prop_vext_spec_get, .set = prop_vext_spec_set, }; @@ -2065,7 +2073,8 @@ static void prop_vlen_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_vlen = { - .type = "vlen", + .type = "uint16", + .description = "vlen", .get = prop_vlen_get, .set = prop_vlen_set, }; @@ -2105,7 +2114,8 @@ static void prop_elen_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_elen = { - .type = "elen", + .type = "uint16", + .description = "elen", .get = prop_elen_get, .set = prop_elen_set, }; @@ -2140,7 +2150,8 @@ static void prop_cbom_blksize_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_cbom_blksize = { - .type = "cbom_blocksize", + .type = "uint16", + .description = "cbom_blocksize", .get = prop_cbom_blksize_get, .set = prop_cbom_blksize_set, }; @@ -2175,7 +2186,8 @@ static void prop_cbop_blksize_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_cbop_blksize = { - .type = "cbop_blocksize", + .type = "uint16", + .description = "cbop_blocksize", .get = prop_cbop_blksize_get, .set = prop_cbop_blksize_set, }; @@ -2210,7 +2222,8 @@ static void prop_cboz_blksize_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_cboz_blksize = { - .type = "cboz_blocksize", + .type = "uint16", + .description = "cboz_blocksize", .get = prop_cboz_blksize_get, .set = prop_cboz_blksize_set, }; @@ -2245,7 +2258,8 @@ static void prop_mvendorid_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_mvendorid = { - .type = "mvendorid", + .type = "uint32", + .description = "mvendorid", .get = prop_mvendorid_get, .set = prop_mvendorid_set, }; @@ -2280,7 +2294,8 @@ static void prop_mimpid_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_mimpid = { - .type = "mimpid", + .type = "uint64", + .description = "mimpid", .get = prop_mimpid_get, .set = prop_mimpid_set, }; @@ -2336,7 +2351,8 @@ static void prop_marchid_get(Object *obj, Visitor *v, const char *name, } static const PropertyInfo prop_marchid = { - .type = "marchid", + .type = "uint64", + .description = "marchid", .get = prop_marchid_get, .set = prop_marchid_set, }; From 0b9d12b03cc178c479f94413d580e8b7b37f44f9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:56:00 +0100 Subject: [PATCH 2214/2892] qdev: Improve PropertyInfo member @description for enum properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistently use format "DESCRIPTION (VALUE/VALUE...)". Signed-off-by: Markus Armbruster Message-ID: <20250227085601.4140852-6-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- hw/core/qdev-properties-system.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 0ac1485d54..6d7dcf368d 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -603,6 +603,7 @@ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); const PropertyInfo qdev_prop_losttickpolicy = { .type = "LostTickPolicy", + .description = "Policy for handling lost ticks (discard/delay/slew)", .enum_table = &LostTickPolicy_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_losttickpolicy, @@ -642,8 +643,7 @@ QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); const PropertyInfo qdev_prop_blockdev_on_error = { .type = "BlockdevOnError", - .description = "Error handling policy, " - "report/ignore/enospc/stop/auto", + .description = "Error handling policy (report/ignore/enospc/stop/auto)", .enum_table = &BlockdevOnError_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -656,8 +656,8 @@ QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); const PropertyInfo qdev_prop_bios_chs_trans = { .type = "BiosAtaTranslation", - .description = "Logical CHS translation algorithm, " - "auto/none/lba/large/rechs", + .description = "Logical CHS translation algorithm " + " (auto/none/lba/large/rechs)", .enum_table = &BiosAtaTranslation_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -668,8 +668,7 @@ const PropertyInfo qdev_prop_bios_chs_trans = { const PropertyInfo qdev_prop_fdc_drive_type = { .type = "FloppyDriveType", - .description = "FDC drive type, " - "144/288/120/none/auto", + .description = "Floppy drive type (144/288/120/none/auto)", .enum_table = &FloppyDriveType_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -680,8 +679,8 @@ const PropertyInfo qdev_prop_fdc_drive_type = { const PropertyInfo qdev_prop_multifd_compression = { .type = "MultiFDCompression", - .description = "multifd_compression values, " - "none/zlib/zstd/qpl/uadk/qatzip", + .description = "multifd_compression values" + " (none/zlib/zstd/qpl/uadk/qatzip)", .enum_table = &MultiFDCompression_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -694,8 +693,7 @@ QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); const PropertyInfo qdev_prop_mig_mode = { .type = "MigMode", - .description = "mig_mode values, " - "normal,cpr-reboot", + .description = "Migration mode (normal/cpr-reboot)", .enum_table = &MigMode_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -708,8 +706,7 @@ QEMU_BUILD_BUG_ON(sizeof(GranuleMode) != sizeof(int)); const PropertyInfo qdev_prop_granule_mode = { .type = "GranuleMode", - .description = "granule_mode values, " - "4k, 8k, 16k, 64k, host", + .description = "Granule page size (4k/8k/16k/64k/host)", .enum_table = &GranuleMode_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -718,8 +715,7 @@ const PropertyInfo qdev_prop_granule_mode = { const PropertyInfo qdev_prop_zero_page_detection = { .type = "ZeroPageDetection", - .description = "zero_page_detection values, " - "none,legacy,multifd", + .description = "Zero page detection (none/legacy/multifd)", .enum_table = &ZeroPageDetection_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -1232,7 +1228,7 @@ QEMU_BUILD_BUG_ON(sizeof(S390CpuEntitlement) != sizeof(int)); const PropertyInfo qdev_prop_cpus390entitlement = { .type = "S390CpuEntitlement", - .description = "low/medium (default)/high", + .description = "auto/low/medium/high (default medium)", .enum_table = &S390CpuEntitlement_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, From 45e5b49360224019eddd307e34ea5625e4e730bf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 27 Feb 2025 09:56:01 +0100 Subject: [PATCH 2215/2892] qdev: Improve a few more PropertyInfo @description members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Message-ID: <20250227085601.4140852-7-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- hw/block/xen-block.c | 2 +- hw/core/qdev-properties-system.c | 2 +- hw/core/qdev-properties.c | 1 + hw/s390x/ccw-device.c | 4 ++-- target/sparc/cpu.c | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 7c9d1b658c..2098286b5f 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -662,7 +662,7 @@ invalid: */ static const PropertyInfo xen_block_prop_vdev = { .type = "str", - .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*", + .description = "Virtual Disk specifier (d*p*/xvd*/hd*/sd*)", .get = xen_block_get_vdev, .set = xen_block_set_vdev, }; diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 6d7dcf368d..a7dde73c29 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -985,7 +985,7 @@ inval: const PropertyInfo qdev_prop_pci_host_devaddr = { .type = "str", - .description = "Address (bus/device/function) of " + .description = "Address (bus:device.function) of " "the host device, example: 04:10.0", .get = get_pci_host_devaddr, .set = set_pci_host_devaddr, diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 5a801057db..c04df3b337 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -247,6 +247,7 @@ static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, const PropertyInfo qdev_prop_bool = { .type = "bool", + .description = "on/off", .get = get_bool, .set = set_bool, .set_default_value = set_default_value_bool, diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 1d4b8ea35c..1ea9934f6c 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -75,8 +75,8 @@ static void ccw_device_set_loadparm(Object *obj, Visitor *v, const PropertyInfo ccw_loadparm = { .type = "str", - .description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass" - " to the guest loader/kernel", + .description = "Up to 8 chars in set of [A-Za-z0-9. ] to select" + " a guest kernel", .get = ccw_device_get_loadparm, .set = ccw_device_set_loadparm, }; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index c8ea35be76..f0613f8a8e 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -939,6 +939,7 @@ static void sparc_set_nwindows(Object *obj, Visitor *v, const char *name, static const PropertyInfo qdev_prop_nwindows = { .type = "int", + .description = "Number of register windows", .get = sparc_get_nwindows, .set = sparc_set_nwindows, }; From 71ba2613ad470b6397868ae7333cd255e467be68 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 28 Feb 2025 14:43:35 +0100 Subject: [PATCH 2216/2892] docs/devel/qapi-code-gen: Discourage use of 'prefix' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QAPI's 'prefix' feature can make the connection between enumeration type and its constants less than obvious. It's best used with restraint. Commit 7bbadc60b5..64f5e9db77 eliminated most uses. Discourage new ones. Signed-off-by: Markus Armbruster Message-ID: <20250228134335.132278-1-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- docs/devel/qapi-code-gen.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 9fa94251b0..f9cfe8721f 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -229,7 +229,8 @@ These are of the form PREFIX_NAME, where PREFIX is derived from the enumeration type's name, and NAME from the value's name. For the example above, the generator maps 'MyEnum' to MY_ENUM and 'value1' to VALUE1, resulting in the enumeration constant MY_ENUM_VALUE1. The -optional 'prefix' member overrides PREFIX. +optional 'prefix' member overrides PREFIX. This is rarely necessary, +and should be used with restraint. The generated C enumeration constants have values 0, 1, ..., N-1 (in QAPI schema order), where N is the number of values. There is an From 41494da7df8d28eb75eac4799f4b49061fbbf64c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Feb 2025 13:34:30 +0100 Subject: [PATCH 2217/2892] chardev: express dependency on io/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chardev is using qio functions, so express that in the Meson internal dependency. (I found this when adding character devices bindings for Rust; they initially needed the io dependency added by hand). Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8b9fda4d95..67ec2b7831 100644 --- a/meson.build +++ b/meson.build @@ -4015,7 +4015,7 @@ libchardev = static_library('chardev', chardev_ss.sources() + genh, build_by_default: false) chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false), - dependencies: chardev_ss.dependencies()) + dependencies: [chardev_ss.dependencies(), io]) hwcore_ss = hwcore_ss.apply({}) libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh, From cff666a3aee566889fcc1ab7167ca0a727af7167 Mon Sep 17 00:00:00 2001 From: Nabih Estefan Date: Thu, 27 Feb 2025 18:04:54 +0000 Subject: [PATCH 2218/2892] scripts: dump stdin on meson-buildoptions error Dump sys.stdin when it errors on meson-buildoptions.py, letting us debug the build errors instead of just saying "Couldn't parse" Signed-off-by: Nabih Estefan Signed-off-by: Patrick Venture Link: https://lore.kernel.org/r/20250227180454.2006757-1-venture@google.com Signed-off-by: Paolo Bonzini --- scripts/meson-buildoptions.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py index 4814a8ff61..a3e22471b2 100644 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -241,8 +241,14 @@ def print_parse(options): print(" esac") print("}") - -options = load_options(json.load(sys.stdin)) +json_data = sys.stdin.read() +try: + options = load_options(json.loads(json_data)) +except: + print("Failure in scripts/meson-buildoptions.py parsing stdin as json", + file=sys.stderr) + print(json_data, file=sys.stderr) + sys.exit(1) print("# This file is generated by meson-buildoptions.py, do not edit!") print_help(options) print_parse(options) From 0b9d05e3c98fe168f3502ccc422b9171467314fa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 10:18:34 +0100 Subject: [PATCH 2219/2892] rust: cell: add wrapper for FFI types Inspired by the same-named type in Linux. This type provides the compiler with a correct view of what goes on with FFI types. In addition, it separates the glue code from the bindgen-generated code, allowing traits such as Send, Sync or Zeroable to be specified independently for C and Rust structs. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 34 +++++-- rust/qemu-api/src/cell.rs | 204 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 223 insertions(+), 15 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 5d8aa3a45b..784c3e40bd 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -296,15 +296,33 @@ of ``&mut self``; access to internal fields must use *interior mutability* to go from a shared reference to a ``&mut``. Whenever C code provides you with an opaque ``void *``, avoid converting it -to a Rust mutable reference, and use a shared reference instead. Rust code -will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which -enforce that locking rules for the "Big QEMU Lock" are respected. These cell -types are also known to the ``vmstate`` crate, which is able to "look inside" -them when building an in-memory representation of a ``struct``'s layout. -Note that the same is not true of a ``RefCell`` or ``Mutex``. +to a Rust mutable reference, and use a shared reference instead. The +``qemu_api::cell`` module provides wrappers that can be used to tell the +Rust compiler about interior mutability, and optionally to enforce locking +rules for the "Big QEMU Lock". In the future, similar cell types might +also be provided for ``AioContext``-based locking as well. -In the future, similar cell types might also be provided for ``AioContext``-based -locking as well. +In particular, device code will usually rely on the ``BqlRefCell`` and +``BqlCell`` type to ensure that data is accessed correctly under the +"Big QEMU Lock". These cell types are also known to the ``vmstate`` +crate, which is able to "look inside" them when building an in-memory +representation of a ``struct``'s layout. Note that the same is not true +of a ``RefCell`` or ``Mutex``. + +Bindings code instead will usually use the ``Opaque`` type, which hides +the contents of the underlying struct and can be easily converted to +a raw pointer, for use in calls to C functions. It can be used for +example as follows:: + + #[repr(transparent)] + #[derive(Debug)] + pub struct Object(Opaque); + +The bindings will then manually check for the big QEMU lock with +assertions, which allows the wrapper to be declared thread-safe:: + + unsafe impl Send for Object {} + unsafe impl Sync for Object {} Writing bindings to C code '''''''''''''''''''''''''' diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index eae4e2ce78..2889abb868 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -27,7 +27,7 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//! BQL-protected mutable containers. +//! QEMU-specific mutable containers //! //! Rust memory safety is based on this rule: Given an object `T`, it is only //! possible to have one of the following: @@ -43,8 +43,10 @@ //! usually have their pointer shared with the "outside world very early in //! their lifetime", for example when they create their //! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual -//! parts of a device must be made mutable in a controlled manner through the -//! use of cell types. +//! parts of a device must be made mutable in a controlled manner; this module +//! provides the tools to do so. +//! +//! ## Cell types //! //! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. //! While they are essentially the same single-threaded primitives that are @@ -71,7 +73,7 @@ //! QEMU device implementations is usually incorrect and can lead to //! thread-safety issues. //! -//! ## `BqlCell` +//! ### `BqlCell` //! //! [`BqlCell`] implements interior mutability by moving values in and out of //! the cell. That is, an `&mut T` to the inner value can never be obtained as @@ -91,7 +93,7 @@ //! - [`set`](BqlCell::set): this method replaces the interior value, //! dropping the replaced value. //! -//! ## `BqlRefCell` +//! ### `BqlRefCell` //! //! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a //! process whereby one can claim temporary, exclusive, mutable access to the @@ -111,13 +113,82 @@ //! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), //! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The //! thread will panic if these rules are violated or if the BQL is not held. +//! +//! ## Opaque wrappers +//! +//! The cell types from the previous section are useful at the boundaries +//! of code that requires interior mutability. When writing glue code that +//! interacts directly with C structs, however, it is useful to operate +//! at a lower level. +//! +//! C functions often violate Rust's fundamental assumptions about memory +//! safety by modifying memory even if it is shared. Furthermore, C structs +//! often start their life uninitialized and may be populated lazily. +//! +//! For this reason, this module provides the [`Opaque`] type to opt out +//! of Rust's usual guarantees about the wrapped type. Access to the wrapped +//! value is always through raw pointers, obtained via methods like +//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These +//! pointers can then be passed to C functions or dereferenced; both actions +//! require `unsafe` blocks, making it clear where safety guarantees must be +//! manually verified. For example +//! +//! ```ignore +//! unsafe { +//! let state = Opaque::::uninit(); +//! qemu_struct_init(state.as_mut_ptr()); +//! } +//! ``` +//! +//! [`Opaque`] will usually be wrapped one level further, so that +//! bridge methods can be added to the wrapper: +//! +//! ```ignore +//! pub struct MyStruct(Opaque); +//! +//! impl MyStruct { +//! fn new() -> Pin> { +//! let result = Box::pin(unsafe { Opaque::uninit() }); +//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; +//! result +//! } +//! } +//! ``` +//! +//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides +//! several advantages: +//! +//! * The choice of traits to be implemented is not limited by the +//! bindgen-generated code. For example, [`Drop`] can be added without +//! disabling [`Copy`] on the underlying bindgen type +//! +//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper +//! type rather than being automatically derived from the C struct's layout +//! +//! * Methods can be implemented in a separate crate from the bindgen-generated +//! bindings +//! +//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) +//! implementations can be customized to be more readable than the raw C +//! struct representation +//! +//! The [`Opaque`] type does not include BQL validation; it is possible to +//! assert in the code that the right lock is taken, to use it together +//! with a custom lock guard type, or to let C code take the lock, as +//! appropriate. It is also possible to use it with non-thread-safe +//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] +//! it is neither `Sync` nor `Send`. +//! +//! While [`Opaque`] is necessary for C interop, it should be used sparingly +//! and only at FFI boundaries. For QEMU-specific types that need interior +//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. use std::{ cell::{Cell, UnsafeCell}, cmp::Ordering, fmt, - marker::PhantomData, - mem, + marker::{PhantomData, PhantomPinned}, + mem::{self, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -840,3 +911,122 @@ impl fmt::Display for BqlRefMut<'_, T> { (**self).fmt(f) } } + +/// Stores an opaque value that is shared with C code. +/// +/// Often, C structs can changed when calling a C function even if they are +/// behind a shared Rust reference, or they can be initialized lazily and have +/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's +/// strict aliasing rules, which normally prevent mutation through shared +/// references. +/// +/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not +/// assume the usual constraints that Rust structs require, and allows using +/// shared references on the Rust side. +/// +/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout +/// of `T`. +#[repr(transparent)] +pub struct Opaque { + value: UnsafeCell>, + // PhantomPinned also allows multiple references to the `Opaque`, i.e. + // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; + // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. + _pin: PhantomPinned, +} + +impl Opaque { + /// Creates a new shared reference from a C pointer + /// + /// # Safety + /// + /// The pointer must be valid, though it need not point to a valid value. + pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { + let ptr = NonNull::new(ptr).unwrap().cast::(); + // SAFETY: Self is a transparent wrapper over T + unsafe { ptr.as_ref() } + } + + /// Creates a new opaque object with uninitialized contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be initialized and pinned before + /// calling them. + #[allow(clippy::missing_const_for_fn)] + pub unsafe fn uninit() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::uninit()), + _pin: PhantomPinned, + } + } + + /// Creates a new opaque object with zeroed contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned (and possibly initialized) + /// before calling them. + #[allow(clippy::missing_const_for_fn)] + pub unsafe fn zeroed() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::zeroed()), + _pin: PhantomPinned, + } + } + + /// Returns a raw mutable pointer to the opaque data. + pub const fn as_mut_ptr(&self) -> *mut T { + UnsafeCell::get(&self.value).cast() + } + + /// Returns a raw pointer to the opaque data. + pub const fn as_ptr(&self) -> *const T { + self.as_mut_ptr() as *const _ + } + + /// Returns a raw pointer to the opaque data that can be passed to a + /// C function as `void *`. + pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { + UnsafeCell::get(&self.value).cast() + } + + /// Converts a raw pointer to the wrapped type. + pub const fn raw_get(slot: *mut Self) -> *mut T { + // Compare with Linux's raw_get method, which goes through an UnsafeCell + // because it takes a *const Self instead. + slot.cast() + } +} + +impl fmt::Debug for Opaque { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut name: String = "Opaque<".to_string(); + name += std::any::type_name::(); + name += ">"; + f.debug_tuple(&name).field(&self.as_ptr()).finish() + } +} + +impl Opaque { + /// Creates a new opaque object with default contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned before calling them. + pub unsafe fn new() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::new(T::default())), + _pin: PhantomPinned, + } + } +} From f07a5674cf97b8473e5d06d7b1df9b51e97d553f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 11:13:53 +0100 Subject: [PATCH 2220/2892] rust: qemu_api_macros: add Wrapper derive macro Add a derive macro that makes it easy to peel off all the layers of specialness (UnsafeCell, MaybeUninit, etc.) and just get a pointer to the wrapped type; and likewise add them back starting from a *mut. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 8 +-- rust/qemu-api-macros/src/lib.rs | 90 ++++++++++++++++++++++++++++++++- rust/qemu-api/meson.build | 7 +-- rust/qemu-api/src/cell.rs | 45 +++++++++++++++++ 4 files changed, 141 insertions(+), 9 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 784c3e40bd..88bdec1eb2 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -315,11 +315,13 @@ a raw pointer, for use in calls to C functions. It can be used for example as follows:: #[repr(transparent)] - #[derive(Debug)] + #[derive(Debug, qemu_api_macros::Wrapper)] pub struct Object(Opaque); -The bindings will then manually check for the big QEMU lock with -assertions, which allows the wrapper to be declared thread-safe:: +where the special ``derive`` macro provides useful methods such as +``from_raw``, ``as_ptr`, ``as_mut_ptr`` and ``raw_get``. The bindings will +then manually check for the big QEMU lock with assertions, which allows +the wrapper to be declared thread-safe:: unsafe impl Send for Object {} unsafe impl Sync for Object {} diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 7ec218202f..eda0d46d12 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -6,7 +6,7 @@ use proc_macro::TokenStream; use quote::quote; use syn::{ parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, - DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility, + DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Type, Variant, Visibility, }; mod utils; @@ -33,6 +33,35 @@ fn get_fields<'a>( } } +fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> { + if let Data::Struct(s) = &input.data { + let unnamed = match &s.fields { + Fields::Unnamed(FieldsUnnamed { + unnamed: ref fields, + .. + }) => fields, + _ => { + return Err(MacroError::Message( + format!("Tuple struct required for {}", msg), + s.fields.span(), + )) + } + }; + if unnamed.len() != 1 { + return Err(MacroError::Message( + format!("A single field is required for {}", msg), + s.fields.span(), + )); + } + Ok(&unnamed[0]) + } else { + Err(MacroError::Message( + format!("Struct required for {}", msg), + input.ident.span(), + )) + } +} + fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { let expected = parse_quote! { #[repr(C)] }; @@ -46,6 +75,19 @@ fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { } } +fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { + let expected = parse_quote! { #[repr(transparent)] }; + + if input.attrs.iter().any(|attr| attr == &expected) { + Ok(()) + } else { + Err(MacroError::Message( + format!("#[repr(transparent)] required for {}", msg), + input.ident.span(), + )) + } +} + fn derive_object_or_error(input: DeriveInput) -> Result { is_c_repr(&input, "#[derive(Object)]")?; @@ -72,6 +114,52 @@ pub fn derive_object(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } +fn derive_opaque_or_error(input: DeriveInput) -> Result { + is_transparent_repr(&input, "#[derive(Wrapper)]")?; + + let name = &input.ident; + let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?; + let typ = &field.ty; + + // TODO: how to add "::qemu_api"? For now, this is only used in the + // qemu_api crate so it's not a problem. + Ok(quote! { + unsafe impl crate::cell::Wrapper for #name { + type Wrapped = <#typ as crate::cell::Wrapper>::Wrapped; + } + impl #name { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { + let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); + unsafe { ptr.as_ref() } + } + + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + self.0.as_mut_ptr() + } + + pub const fn as_ptr(&self) -> *const ::Wrapped { + self.0.as_ptr() + } + + pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void { + self.0.as_void_ptr() + } + + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { + slot.cast() + } + } + }) +} + +#[proc_macro_derive(Wrapper)] +pub fn derive_opaque(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} + #[rustfmt::skip::macros(quote)] fn derive_offsets_or_error(input: DeriveInput) -> Result { is_c_repr(&input, "#[derive(offsets)]")?; diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index bcf1cf780f..6e52c4bad7 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -42,16 +42,13 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: libc_dep, + dependencies: [libc_dep, qemu_api_macros], ) rust.test('rust-qemu-api-tests', _qemu_api_rs, suite: ['unit', 'rust']) -qemu_api = declare_dependency( - link_with: _qemu_api_rs, - dependencies: qemu_api_macros, -) +qemu_api = declare_dependency(link_with: _qemu_api_rs) # Rust executables do not support objects, so add an intermediate step. rust_qemu_api_objs = static_library( diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 2889abb868..448638e896 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -1030,3 +1030,48 @@ impl Opaque { } } } + +/// Annotates [`Self`] as a transparent wrapper for another type. +/// +/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. +/// +/// # Examples +/// +/// ``` +/// # use std::mem::ManuallyDrop; +/// # use qemu_api::cell::Wrapper; +/// #[repr(transparent)] +/// pub struct Example { +/// inner: ManuallyDrop, +/// } +/// +/// unsafe impl Wrapper for Example { +/// type Wrapped = String; +/// } +/// ``` +/// +/// # Safety +/// +/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, +/// whether directly or indirectly. +/// +/// # Methods +/// +/// By convention, types that implement Wrapper also implement the following +/// methods: +/// +/// ```ignore +/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; +/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; +/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; +/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; +/// ``` +/// +/// They are not defined here to allow them to be `const`. +pub unsafe trait Wrapper { + type Wrapped; +} + +unsafe impl Wrapper for Opaque { + type Wrapped = T; +} From d7f5ae8b30cc9652a4ddcfeb52076f5aef6d78b6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Feb 2025 10:28:56 +0100 Subject: [PATCH 2221/2892] rust: vmstate: add std::pin::Pin as transparent wrapper Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 24a4dc81e7..1e7ba531e2 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -330,6 +330,7 @@ macro_rules! impl_vmstate_transparent { impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); +impl_vmstate_transparent!(std::pin::Pin where T: VMState); impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); From e8dc87fef2677dc286b3fe72e04d1b763cf98fef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Mar 2025 16:27:08 +0100 Subject: [PATCH 2222/2892] rust: hpet: embed Timer without the Option and Box indirection This simplifies things for migration, since Option> does not implement VMState. This also shows a soundness issue because Timer::new() will leave a NULL timer list pointer, which can then be dereferenced by Timer::modify(). It will be fixed shortly. Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 59 ++++++++++++++++------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index be27eb0eff..02c81ae048 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -151,14 +151,14 @@ fn timer_handler(timer_cell: &BqlRefCell) { /// HPET Timer Abstraction #[repr(C)] -#[derive(Debug, Default, qemu_api_macros::offsets)] +#[derive(Debug, qemu_api_macros::offsets)] pub struct HPETTimer { /// timer N index within the timer block (`HPETState`) #[doc(alias = "tn")] index: usize, - qemu_timer: Option>, + qemu_timer: Timer, /// timer block abstraction containing this timer - state: Option>, + state: NonNull, // Memory-mapped, software visible timer registers /// Timer N Configuration and Capability Register @@ -181,32 +181,34 @@ pub struct HPETTimer { } impl HPETTimer { - fn init(&mut self, index: usize, state_ptr: *mut HPETState) -> &mut Self { - *self = HPETTimer::default(); - self.index = index; - self.state = NonNull::new(state_ptr); - self - } + fn init(&mut self, index: usize, state: &HPETState) { + *self = HPETTimer { + index, + qemu_timer: Timer::new(), + state: NonNull::new(state as *const _ as *mut _).unwrap(), + config: 0, + cmp: 0, + fsb: 0, + cmp64: 0, + period: 0, + wrap_flag: 0, + last: 0, + }; - fn init_timer_with_state(&mut self) { - self.qemu_timer = Some(Box::new({ - let mut t = Timer::new(); - t.init_full( - None, - CLOCK_VIRTUAL, - Timer::NS, - 0, - timer_handler, - &self.get_state().timers[self.index], - ); - t - })); + self.qemu_timer.init_full( + None, + CLOCK_VIRTUAL, + Timer::NS, + 0, + timer_handler, + &state.timers[self.index], + ) } fn get_state(&self) -> &HPETState { // SAFETY: // the pointer is convertible to a reference - unsafe { self.state.unwrap().as_ref() } + unsafe { self.state.as_ref() } } fn is_int_active(&self) -> bool { @@ -330,7 +332,7 @@ impl HPETTimer { } self.last = ns; - self.qemu_timer.as_ref().unwrap().modify(self.last); + self.qemu_timer.modify(self.last); } fn set_timer(&mut self) { @@ -353,7 +355,7 @@ impl HPETTimer { fn del_timer(&mut self) { // Just remove the timer from the timer_list without destroying // this timer instance. - self.qemu_timer.as_ref().unwrap().delete(); + self.qemu_timer.delete(); if self.is_int_active() { // For level-triggered interrupt, this leaves interrupt status @@ -581,13 +583,8 @@ impl HPETState { } fn init_timer(&self) { - let raw_ptr: *mut HPETState = self as *const HPETState as *mut HPETState; - for (index, timer) in self.timers.iter().enumerate() { - timer - .borrow_mut() - .init(index, raw_ptr) - .init_timer_with_state(); + timer.borrow_mut().init(index, self); } } From a32b239699377f09bba08b2e8ae0d167c1488b1f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:06:13 +0100 Subject: [PATCH 2223/2892] rust: timer: wrap QEMUTimer with Opaque<> and express pinning requirements Timers must be pinned in memory, because modify() stores a pointer to them in the TimerList. To express this requirement, change init_full() to take a pinned reference. Because the only way to obtain a Timer is through Timer::new(), which is unsafe, modify() can assume that the timer it got was later initialized; and because the initialization takes a Pin<&mut Timer> modify() can assume that the timer is pinned. In the future the pinning requirement will be expressed through the pin_init crate instead. Note that Timer is a bit different from other users of Opaque, in that it is created in Rust code rather than C code. This is why it has to use the unsafe constructors provided by Opaque; and in fact Timer::new() is also unsafe, because it leaves it to the caller to invoke init_full() before modify(). Without a call to init_full(), modify() will cause a NULL pointer dereference. An alternative could be to combine new() + init_full() by returning a pinned box; however, using a reference makes it easier to express the requirement that the opaque outlives the timer. Signed-off-by: Paolo Bonzini --- meson.build | 7 ----- rust/hw/timer/hpet/src/hpet.rs | 10 ++++++-- rust/qemu-api/src/timer.rs | 47 ++++++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/meson.build b/meson.build index 67ec2b7831..6da4eb317c 100644 --- a/meson.build +++ b/meson.build @@ -4100,13 +4100,6 @@ if have_rust foreach enum : c_bitfields bindgen_args += ['--bitfield-enum', enum] endforeach - c_nocopy = [ - 'QEMUTimer', - ] - # Used to customize Drop trait - foreach struct : c_nocopy - bindgen_args += ['--no-copy', struct] - endforeach # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 02c81ae048..3d3d6ef8ee 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -4,6 +4,7 @@ use std::{ ffi::CStr, + pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, slice::from_ref, }; @@ -184,7 +185,9 @@ impl HPETTimer { fn init(&mut self, index: usize, state: &HPETState) { *self = HPETTimer { index, - qemu_timer: Timer::new(), + // SAFETY: the HPETTimer will only be used after the timer + // is initialized below. + qemu_timer: unsafe { Timer::new() }, state: NonNull::new(state as *const _ as *mut _).unwrap(), config: 0, cmp: 0, @@ -195,7 +198,10 @@ impl HPETTimer { last: 0, }; - self.qemu_timer.init_full( + // SAFETY: HPETTimer is only used as part of HPETState, which is + // always pinned. + let qemu_timer = unsafe { Pin::new_unchecked(&mut self.qemu_timer) }; + qemu_timer.init_full( None, CLOCK_VIRTUAL, Timer::NS, diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index a593538917..f0b04ef95d 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -2,31 +2,51 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -use std::os::raw::{c_int, c_void}; +use std::{ + os::raw::{c_int, c_void}, + pin::Pin, +}; use crate::{ bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType}, callbacks::FnCall, + cell::Opaque, }; -pub type Timer = bindings::QEMUTimer; -pub type TimerListGroup = bindings::QEMUTimerListGroup; +/// A safe wrapper around [`bindings::QEMUTimer`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Timer(Opaque); + +unsafe impl Send for Timer {} +unsafe impl Sync for Timer {} + +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct TimerListGroup(Opaque); + +unsafe impl Send for TimerListGroup {} +unsafe impl Sync for TimerListGroup {} impl Timer { pub const MS: u32 = bindings::SCALE_MS; pub const US: u32 = bindings::SCALE_US; pub const NS: u32 = bindings::SCALE_NS; - pub fn new() -> Self { - Default::default() - } - - const fn as_mut_ptr(&self) -> *mut Self { - self as *const Timer as *mut _ + /// Create a `Timer` struct without initializing it. + /// + /// # Safety + /// + /// The timer must be initialized before it is armed with + /// [`modify`](Self::modify). + pub unsafe fn new() -> Self { + // SAFETY: requirements relayed to callers of Timer::new + Self(unsafe { Opaque::zeroed() }) } + /// Create a new timer with the given attributes. pub fn init_full<'timer, 'opaque: 'timer, T, F>( - &'timer mut self, + self: Pin<&'timer mut Self>, timer_list_group: Option<&TimerListGroup>, clk_type: ClockType, scale: u32, @@ -51,7 +71,7 @@ impl Timer { // SAFETY: the opaque outlives the timer unsafe { timer_init_full( - self, + self.as_mut_ptr(), if let Some(g) = timer_list_group { g as *const TimerListGroup as *mut _ } else { @@ -67,14 +87,19 @@ impl Timer { } pub fn modify(&self, expire_time: u64) { + // SAFETY: the only way to obtain a Timer safely is via methods that + // take a Pin<&mut Self>, therefore the timer is pinned unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } } pub fn delete(&self) { + // SAFETY: the only way to obtain a Timer safely is via methods that + // take a Pin<&mut Self>, therefore the timer is pinned unsafe { timer_del(self.as_mut_ptr()) } } } +// FIXME: use something like PinnedDrop from the pinned_init crate impl Drop for Timer { fn drop(&mut self) { self.delete() From 9c9a6a889cb3589779b019a343892aa0e9bdb254 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:05:59 +0100 Subject: [PATCH 2224/2892] rust: irq: wrap IRQState with Opaque<> Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/irq.rs | 15 ++++++++++----- rust/qemu-api/src/sysbus.rs | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 34c19263c2..1222d4fde3 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -8,10 +8,16 @@ use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr}; use crate::{ bindings::{self, qemu_set_irq}, + cell::Opaque, prelude::*, qom::ObjectClass, }; +/// An opaque wrapper around [`bindings::IRQState`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct IRQState(Opaque); + /// Interrupt sources are used by devices to pass changes to a value (typically /// a boolean). The interrupt sink is usually an interrupt controller or /// GPIO controller. @@ -21,8 +27,7 @@ use crate::{ /// method sends a `true` value to the sink. If the guest has to see a /// different polarity, that change is performed by the board between the /// device and the interrupt controller. -pub type IRQState = bindings::IRQState; - +/// /// Interrupts are implemented as a pointer to the interrupt "sink", which has /// type [`IRQState`]. A device exposes its source as a QOM link property using /// a function such as [`SysBusDeviceMethods::init_irq`], and @@ -40,7 +45,7 @@ pub struct InterruptSource where c_int: From, { - cell: BqlCell<*mut IRQState>, + cell: BqlCell<*mut bindings::IRQState>, _marker: PhantomData, } @@ -79,11 +84,11 @@ where } } - pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState { + pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState { self.cell.as_ptr() } - pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut IRQState { + pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { assert!(!slice.is_empty()); slice[0].as_ptr() } diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 04821a2b9b..48803a655f 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -79,6 +79,7 @@ where fn connect_irq(&self, id: u32, irq: &Owned) { assert!(bql_locked()); let id: i32 = id.try_into().unwrap(); + let irq: &IRQState = irq; unsafe { bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr()); } From 7fb4a99df17c8ae5f5e00d643042b9d95477a426 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:09:53 +0100 Subject: [PATCH 2225/2892] rust: qom: wrap Object with Opaque<> Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 3 --- rust/qemu-api/src/memory.rs | 2 +- rust/qemu-api/src/qdev.rs | 6 +++--- rust/qemu-api/src/qom.rs | 35 ++++++++++++++++++++++------------- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index d2868639ff..be6dd68c09 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -46,9 +46,6 @@ unsafe impl Sync for MemoryRegion {} unsafe impl Send for ObjectClass {} unsafe impl Sync for ObjectClass {} -unsafe impl Send for Object {} -unsafe impl Sync for Object {} - unsafe impl Send for SysBusDevice {} unsafe impl Sync for SysBusDevice {} diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index 682951ab44..713c494ca2 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -157,7 +157,7 @@ impl MemoryRegion { let cstr = CString::new(name).unwrap(); memory_region_init_io( slot, - owner.cast::(), + owner.cast::(), ops, owner.cast::(), cstr.as_ptr(), diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index c136457090..1a4d1f3876 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -52,7 +52,7 @@ pub trait ResettablePhasesImpl { /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_resettable_enter_fn( - obj: *mut Object, + obj: *mut bindings::Object, typ: ResetType, ) { let state = NonNull::new(obj).unwrap().cast::(); @@ -65,7 +65,7 @@ unsafe extern "C" fn rust_resettable_enter_fn( /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_resettable_hold_fn( - obj: *mut Object, + obj: *mut bindings::Object, typ: ResetType, ) { let state = NonNull::new(obj).unwrap().cast::(); @@ -78,7 +78,7 @@ unsafe extern "C" fn rust_resettable_hold_fn( /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_resettable_exit_fn( - obj: *mut Object, + obj: *mut bindings::Object, typ: ResetType, ) { let state = NonNull::new(obj).unwrap().cast::(); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 5488643a2f..2defbd2351 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -101,16 +101,24 @@ use std::{ ptr::NonNull, }; -pub use bindings::{Object, ObjectClass}; +pub use bindings::ObjectClass; use crate::{ bindings::{ self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, - cell::bql_locked, + cell::{bql_locked, Opaque}, }; +/// A safe wrapper around [`bindings::Object`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Object(Opaque); + +unsafe impl Send for Object {} +unsafe impl Sync for Object {} + /// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct /// or indirect parent of `Self`). /// @@ -199,7 +207,7 @@ impl fmt::Display for ParentField { } } -unsafe extern "C" fn rust_instance_init(obj: *mut Object) { +unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { let mut state = NonNull::new(obj).unwrap().cast::(); // SAFETY: obj is an instance of T, since rust_instance_init // is called from QOM core as the instance_init function @@ -209,7 +217,7 @@ unsafe extern "C" fn rust_instance_init(obj: *mut Object) { } } -unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { +unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { let state = NonNull::new(obj).unwrap().cast::(); // SAFETY: obj is an instance of T, since rust_instance_post_init // is called from QOM core as the instance_post_init function @@ -230,7 +238,7 @@ unsafe extern "C" fn rust_class_init( ::CLASS_INIT(unsafe { klass.as_mut() }) } -unsafe extern "C" fn drop_object(obj: *mut Object) { +unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { // SAFETY: obj is an instance of T, since drop_object is called // from the QOM core function object_deinit() as the instance_finalize // function for class T. Note that while object_deinit() will drop the @@ -280,14 +288,14 @@ pub unsafe trait ObjectType: Sized { /// Return the receiver as an Object. This is always safe, even /// if this type represents an interface. fn as_object(&self) -> &Object { - unsafe { &*self.as_object_ptr() } + unsafe { &*self.as_ptr().cast() } } /// Return the receiver as a const raw pointer to Object. /// This is preferrable to `as_object_mut_ptr()` if a C /// function only needs a `const Object *`. - fn as_object_ptr(&self) -> *const Object { - self.as_ptr().cast() + fn as_object_ptr(&self) -> *const bindings::Object { + self.as_object().as_ptr() } /// Return the receiver as a mutable raw pointer to Object. @@ -297,8 +305,8 @@ pub unsafe trait ObjectType: Sized { /// This cast is always safe, but because the result is mutable /// and the incoming reference is not, this should only be used /// for calls to C functions, and only if needed. - unsafe fn as_object_mut_ptr(&self) -> *mut Object { - self.as_object_ptr() as *mut _ + unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object().as_mut_ptr() } } @@ -621,7 +629,7 @@ pub trait ObjectImpl: ObjectType + IsA { /// We expect the FFI user of this function to pass a valid pointer that /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_unparent_fn(dev: *mut Object) { +unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { let state = NonNull::new(dev).unwrap().cast::(); T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } @@ -796,8 +804,9 @@ pub trait ObjectClassMethods: IsA { // SAFETY: the object created by object_new is allocated on // the heap and has a reference count of 1 unsafe { - let obj = &*object_new(Self::TYPE_NAME.as_ptr()); - Owned::from_raw(obj.unsafe_cast::()) + let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); + let obj = Object::from_raw(raw_obj).unsafe_cast::(); + Owned::from_raw(obj) } } } From fc22d650d54363b8f2bad56aea1dde773f600067 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 11:45:25 +0100 Subject: [PATCH 2226/2892] rust: qdev: wrap Clock and DeviceState with Opaque<> Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 6 ---- rust/qemu-api/src/qdev.rs | 68 ++++++++++++++++++++++++----------- rust/qemu-api/src/vmstate.rs | 2 +- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index be6dd68c09..6e70a75a0e 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -34,12 +34,6 @@ unsafe impl Sync for CharBackend {} unsafe impl Send for Chardev {} unsafe impl Sync for Chardev {} -unsafe impl Send for Clock {} -unsafe impl Sync for Clock {} - -unsafe impl Send for DeviceState {} -unsafe impl Sync for DeviceState {} - unsafe impl Send for MemoryRegion {} unsafe impl Sync for MemoryRegion {} diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 1a4d1f3876..1c4a67b572 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,12 +10,12 @@ use std::{ ptr::NonNull, }; -pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType}; +pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass}, callbacks::FnCall, - cell::bql_locked, + cell::{bql_locked, Opaque}, chardev::Chardev, irq::InterruptSource, prelude::*, @@ -23,6 +23,22 @@ use crate::{ vmstate::VMStateDescription, }; +/// A safe wrapper around [`bindings::Clock`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Clock(Opaque); + +unsafe impl Send for Clock {} +unsafe impl Sync for Clock {} + +/// A safe wrapper around [`bindings::DeviceState`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct DeviceState(Opaque); + +unsafe impl Send for DeviceState {} +unsafe impl Sync for DeviceState {} + /// Trait providing the contents of the `ResettablePhases` struct, /// which is part of the QOM `Resettable` interface. pub trait ResettablePhasesImpl { @@ -117,7 +133,10 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { /// We expect the FFI user of this function to pass a valid pointer that /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp: *mut *mut Error) { +unsafe extern "C" fn rust_realize_fn( + dev: *mut bindings::DeviceState, + _errp: *mut *mut Error, +) { let state = NonNull::new(dev).unwrap().cast::(); T::REALIZE.unwrap()(unsafe { state.as_ref() }); } @@ -251,7 +270,7 @@ where events: ClockEvent, ) -> Owned { fn do_init_clock_in( - dev: *mut DeviceState, + dev: &DeviceState, name: &str, cb: Option, events: ClockEvent, @@ -265,14 +284,15 @@ where unsafe { let cstr = CString::new(name).unwrap(); let clk = bindings::qdev_init_clock_in( - dev, + dev.as_mut_ptr(), cstr.as_ptr(), cb, - dev.cast::(), + dev.as_void_ptr(), events.0, ); - Owned::from(&*clk) + let clk: &Clock = Clock::from_raw(clk); + Owned::from(clk) } } @@ -289,7 +309,7 @@ where None }; - do_init_clock_in(self.as_mut_ptr(), name, cb, events) + do_init_clock_in(self.upcast(), name, cb, events) } /// Add an output clock named `name`. @@ -304,9 +324,10 @@ where fn init_clock_out(&self, name: &str) -> Owned { unsafe { let cstr = CString::new(name).unwrap(); - let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr()); + let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr()); - Owned::from(&*clk) + let clk: &Clock = Clock::from_raw(clk); + Owned::from(clk) } } @@ -314,7 +335,11 @@ where assert!(bql_locked()); let c_propname = CString::new(propname).unwrap(); unsafe { - bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr()); + bindings::qdev_prop_set_chr( + self.upcast().as_mut_ptr(), + c_propname.as_ptr(), + chr.as_mut_ptr(), + ); } } @@ -323,8 +348,17 @@ where num_lines: u32, _cb: F, ) { - let _: () = F::ASSERT_IS_SOME; + fn do_init_gpio_in( + dev: &DeviceState, + num_lines: u32, + gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int), + ) { + unsafe { + qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int); + } + } + let _: () = F::ASSERT_IS_SOME; unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( opaque: *mut c_void, line: c_int, @@ -337,19 +371,13 @@ where let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = rust_irq_handler::; - unsafe { - qdev_init_gpio_in( - self.as_mut_ptr::(), - Some(gpio_in_cb), - num_lines as c_int, - ); - } + do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb); } fn init_gpio_out(&self, pins: &[InterruptSource]) { unsafe { qdev_init_gpio_out( - self.as_mut_ptr::(), + self.upcast().as_mut_ptr(), InterruptSource::slice_as_ptr(pins), pins.len() as c_int, ); diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 1e7ba531e2..f0510ae769 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -470,7 +470,7 @@ macro_rules! vmstate_clock { $crate::assert_field_type!( $struct_name, $field_name, - $crate::qom::Owned<$crate::bindings::Clock> + $crate::qom::Owned<$crate::qdev::Clock> ); $crate::offset_of!($struct_name, $field_name) }, From 09fda8f5dc925ba059aca539163d16796af6a299 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Feb 2025 10:06:20 +0100 Subject: [PATCH 2227/2892] rust: hpet: do not access fields of SysBusDevice Fields of SysBusDevice must only be accessed with the BQL taken. Add a wrapper that verifies that. Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 4 +--- rust/qemu-api/src/sysbus.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 3d3d6ef8ee..d989360ede 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -730,8 +730,6 @@ impl HPETState { } fn reset_hold(&self, _type: ResetType) { - let sbd = self.upcast::(); - for timer in self.timers.iter().take(self.num_timers.get()) { timer.borrow_mut().reset(); } @@ -744,7 +742,7 @@ impl HPETState { HPETFwConfig::update_hpet_cfg( self.hpet_id.get(), self.capability.get() as u32, - sbd.mmio[0].addr, + self.mmio_addr(0).unwrap(), ); // to document that the RTC lowers its output on reset as well diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 48803a655f..0790576d44 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -64,6 +64,18 @@ where } } + // TODO: do we want a type like GuestAddress here? + fn mmio_addr(&self, id: u32) -> Option { + assert!(bql_locked()); + let sbd = self.upcast(); + let id: usize = id.try_into().unwrap(); + if sbd.mmio[id].memory.is_null() { + None + } else { + Some(sbd.mmio[id].addr) + } + } + // TODO: do we want a type like GuestAddress here? fn mmio_map(&self, id: u32, addr: u64) { assert!(bql_locked()); From f4751c7a42b194eb4166c7f3f294bf89c3e23cd9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 11:45:36 +0100 Subject: [PATCH 2228/2892] rust: sysbus: wrap SysBusDevice with Opaque<> Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 3 --- rust/qemu-api/src/sysbus.rs | 29 +++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 6e70a75a0e..b791ca6d87 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -40,9 +40,6 @@ unsafe impl Sync for MemoryRegion {} unsafe impl Send for ObjectClass {} unsafe impl Sync for ObjectClass {} -unsafe impl Send for SysBusDevice {} -unsafe impl Sync for SysBusDevice {} - // SAFETY: this is a pure data struct unsafe impl Send for CoalescedMemoryRange {} unsafe impl Sync for CoalescedMemoryRange {} diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 0790576d44..e92502a8fe 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -6,11 +6,11 @@ use std::{ffi::CStr, ptr::addr_of_mut}; -pub use bindings::{SysBusDevice, SysBusDeviceClass}; +pub use bindings::SysBusDeviceClass; use crate::{ bindings, - cell::bql_locked, + cell::{bql_locked, Opaque}, irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, @@ -18,6 +18,14 @@ use crate::{ qom::Owned, }; +/// A safe wrapper around [`bindings::SysBusDevice`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct SysBusDevice(Opaque); + +unsafe impl Send for SysBusDevice {} +unsafe impl Sync for SysBusDevice {} + unsafe impl ObjectType for SysBusDevice { type Class = SysBusDeviceClass; const TYPE_NAME: &'static CStr = @@ -49,7 +57,7 @@ where fn init_mmio(&self, iomem: &MemoryRegion) { assert!(bql_locked()); unsafe { - bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr()); + bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); } } @@ -60,14 +68,16 @@ where fn init_irq(&self, irq: &InterruptSource) { assert!(bql_locked()); unsafe { - bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr()); + bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); } } // TODO: do we want a type like GuestAddress here? fn mmio_addr(&self, id: u32) -> Option { assert!(bql_locked()); - let sbd = self.upcast(); + // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and + // the SysBusDevice must be initialized to get an IsA. + let sbd = unsafe { *self.upcast().as_ptr() }; let id: usize = id.try_into().unwrap(); if sbd.mmio[id].memory.is_null() { None @@ -81,7 +91,7 @@ where assert!(bql_locked()); let id: i32 = id.try_into().unwrap(); unsafe { - bindings::sysbus_mmio_map(self.as_mut_ptr(), id, addr); + bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); } } @@ -93,7 +103,7 @@ where let id: i32 = id.try_into().unwrap(); let irq: &IRQState = irq; unsafe { - bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr()); + bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); } } @@ -101,7 +111,10 @@ where // TODO: return an Error assert!(bql_locked()); unsafe { - bindings::sysbus_realize(self.as_mut_ptr(), addr_of_mut!(bindings::error_fatal)); + bindings::sysbus_realize( + self.upcast().as_mut_ptr(), + addr_of_mut!(bindings::error_fatal), + ); } } } From af0868cba33aaf327a49d642b6b0ad3ae3f01240 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:34:47 +0100 Subject: [PATCH 2229/2892] rust: memory: wrap MemoryRegion with Opaque<> Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 3 --- rust/qemu-api/src/memory.rs | 35 +++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index b791ca6d87..26cc8de0cf 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -34,9 +34,6 @@ unsafe impl Sync for CharBackend {} unsafe impl Send for Chardev {} unsafe impl Sync for Chardev {} -unsafe impl Send for MemoryRegion {} -unsafe impl Sync for MemoryRegion {} - unsafe impl Send for ObjectClass {} unsafe impl Sync for ObjectClass {} diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index 713c494ca2..eff9f09fd7 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -6,9 +6,8 @@ use std::{ ffi::{CStr, CString}, - marker::{PhantomData, PhantomPinned}, + marker::PhantomData, os::raw::{c_uint, c_void}, - ptr::addr_of, }; pub use bindings::{hwaddr, MemTxAttrs}; @@ -16,6 +15,7 @@ pub use bindings::{hwaddr, MemTxAttrs}; use crate::{ bindings::{self, device_endian, memory_region_init_io}, callbacks::FnCall, + cell::Opaque, prelude::*, zeroable::Zeroable, }; @@ -132,13 +132,13 @@ impl Default for MemoryRegionOpsBuilder { } } -/// A safe wrapper around [`bindings::MemoryRegion`]. Compared to the -/// underlying C struct it is marked as pinned because the QOM tree -/// contains a pointer to it. -pub struct MemoryRegion { - inner: bindings::MemoryRegion, - _pin: PhantomPinned, -} +/// A safe wrapper around [`bindings::MemoryRegion`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct MemoryRegion(Opaque); + +unsafe impl Send for MemoryRegion {} +unsafe impl Sync for MemoryRegion {} impl MemoryRegion { // inline to ensure that it is not included in tests, which only @@ -174,13 +174,20 @@ impl MemoryRegion { size: u64, ) { unsafe { - Self::do_init_io(&mut self.inner, owner.cast::(), &ops.0, name, size); + Self::do_init_io( + // self.0.as_mut_ptr() needed because Rust tries to call + // ObjectDeref::as_mut_ptr() on "&mut Self", instead of coercing + // to "&Self" and then calling MemoryRegion::as_mut_ptr(). + // Revisit if/when ObjectCastMut is not needed anymore; it is + // only used in a couple places for initialization. + self.0.as_mut_ptr(), + owner.cast::(), + &ops.0, + name, + size, + ); } } - - pub(crate) const fn as_mut_ptr(&self) -> *mut bindings::MemoryRegion { - addr_of!(self.inner) as *mut _ - } } unsafe impl ObjectType for MemoryRegion { From 48627510a7fed7a045358743e6b869a98931f85e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:34:47 +0100 Subject: [PATCH 2230/2892] rust: chardev: wrap Chardev with Opaque<> Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 3 --- rust/qemu-api/src/chardev.rs | 8 ++++++-- rust/qemu-api/src/qdev.rs | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 26cc8de0cf..c3f36108bd 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -31,9 +31,6 @@ unsafe impl Sync for BusState {} unsafe impl Send for CharBackend {} unsafe impl Sync for CharBackend {} -unsafe impl Send for Chardev {} -unsafe impl Sync for Chardev {} - unsafe impl Send for ObjectClass {} unsafe impl Sync for ObjectClass {} diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 74cfb634e5..a35b9217e9 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -6,9 +6,13 @@ use std::ffi::CStr; -use crate::{bindings, prelude::*}; +use crate::{bindings, cell::Opaque, prelude::*}; + +/// A safe wrapper around [`bindings::Chardev`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct Chardev(Opaque); -pub type Chardev = bindings::Chardev; pub type ChardevClass = bindings::ChardevClass; unsafe impl ObjectType for Chardev { diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 1c4a67b572..18b4a9ba68 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -334,6 +334,7 @@ where fn prop_set_chr(&self, propname: &str, chr: &Owned) { assert!(bql_locked()); let c_propname = CString::new(propname).unwrap(); + let chr: &Chardev = chr; unsafe { bindings::qdev_prop_set_chr( self.upcast().as_mut_ptr(), From 2ad011d466697d69f7f9aa84662a6553049f6556 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:36:11 +0100 Subject: [PATCH 2231/2892] rust: bindings: remove more unnecessary Send/Sync impls Send and Sync are now implemented on the opaque wrappers. Remove them from the bindings module, unless the structs are pure data containers and/or have no C functions defined on them. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index c3f36108bd..3c1d297581 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -25,15 +25,11 @@ include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); // SAFETY: these are implemented in C; the bindings need to assert that the // BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. -unsafe impl Send for BusState {} -unsafe impl Sync for BusState {} - +// When bindings for character devices are introduced, this can be +// moved to the Opaque<> wrapper in src/chardev.rs. unsafe impl Send for CharBackend {} unsafe impl Sync for CharBackend {} -unsafe impl Send for ObjectClass {} -unsafe impl Sync for ObjectClass {} - // SAFETY: this is a pure data struct unsafe impl Send for CoalescedMemoryRange {} unsafe impl Sync for CoalescedMemoryRange {} From 2d0050cbe27fed5233561451e6de64af5ecb6571 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:41:14 +0100 Subject: [PATCH 2232/2892] rust: chardev: provide basic bindings to character devices Most of the character device API is pretty simple, with "0 or -errno" or "number of bytes or -errno" as the convention for return codes. Add safe wrappers for the API to the CharBackend bindgen-generated struct. The API is not complete, but it covers the parts that are used by the PL011 device, plus qemu_chr_fe_write which is needed to implement the standard library Write trait. Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 17 ++- rust/qemu-api/src/chardev.rs | 242 +++++++++++++++++++++++++++++++++- rust/qemu-api/src/zeroable.rs | 1 + 3 files changed, 255 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 6e52c4bad7..a3f226ccc2 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -54,7 +54,19 @@ qemu_api = declare_dependency(link_with: _qemu_api_rs) rust_qemu_api_objs = static_library( 'rust_qemu_api_objs', objects: [libqom.extract_all_objects(recursive: false), - libhwcore.extract_all_objects(recursive: false)]) + libhwcore.extract_all_objects(recursive: false), + libchardev.extract_all_objects(recursive: false), + libcrypto.extract_all_objects(recursive: false), + libauthz.extract_all_objects(recursive: false), + libio.extract_all_objects(recursive: false)]) +rust_qemu_api_deps = declare_dependency( + dependencies: [ + qom_ss.dependencies(), + chardev_ss.dependencies(), + crypto_ss.dependencies(), + authz_ss.dependencies(), + io_ss.dependencies()], + link_whole: [rust_qemu_api_objs, libqemuutil]) test('rust-qemu-api-integration', executable( @@ -63,8 +75,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [qemu_api, qemu_api_macros], - link_whole: [rust_qemu_api_objs, libqemuutil]), + dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index a35b9217e9..11e6c45afa 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -3,10 +3,28 @@ // SPDX-License-Identifier: GPL-2.0-or-later //! Bindings for character devices +//! +//! Character devices in QEMU can run under the big QEMU lock or in a separate +//! `GMainContext`. Here we only support the former, because the bindings +//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are +//! called. -use std::ffi::CStr; +use std::{ + ffi::CStr, + fmt::{self, Debug}, + io::{self, ErrorKind, Write}, + marker::PhantomPinned, + os::raw::{c_int, c_void}, + ptr::addr_of_mut, + slice, +}; -use crate::{bindings, cell::Opaque, prelude::*}; +use crate::{ + bindings, + callbacks::FnCall, + cell::{BqlRefMut, Opaque}, + prelude::*, +}; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] @@ -14,6 +32,226 @@ use crate::{bindings, cell::Opaque, prelude::*}; pub struct Chardev(Opaque); pub type ChardevClass = bindings::ChardevClass; +pub type Event = bindings::QEMUChrEvent; + +/// A safe wrapper around [`bindings::CharBackend`], denoting the character +/// back-end that is used for example by a device. Compared to the +/// underlying C struct it adds BQL protection, and is marked as pinned +/// because the QOM object ([`bindings::Chardev`]) contains a pointer to +/// the `CharBackend`. +pub struct CharBackend { + inner: BqlRefCell, + _pin: PhantomPinned, +} + +impl Write for BqlRefMut<'_, bindings::CharBackend> { + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write(&mut self, buf: &[u8]) -> io::Result { + let chr: &mut bindings::CharBackend = self; + + let len = buf.len().try_into().unwrap(); + let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) }; + errno::into_io_result(r).map(|cnt| cnt as usize) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let chr: &mut bindings::CharBackend = self; + + let len = buf.len().try_into().unwrap(); + let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) }; + errno::into_io_result(r).and_then(|cnt| { + if cnt as usize == buf.len() { + Ok(()) + } else { + Err(ErrorKind::WriteZero.into()) + } + }) + } +} + +impl Debug for CharBackend { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: accessed just to print the values + let chr = self.inner.as_ptr(); + Debug::fmt(unsafe { &*chr }, f) + } +} + +// FIXME: use something like PinnedDrop from the pinned_init crate +impl Drop for CharBackend { + fn drop(&mut self) { + self.disable_handlers(); + } +} + +impl CharBackend { + /// Enable the front-end's character device handlers, if there is an + /// associated `Chardev`. + pub fn enable_handlers< + 'chardev, + 'owner: 'chardev, + T, + CanReceiveFn: for<'a> FnCall<(&'a T,), u32>, + ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>, + EventFn: for<'a> FnCall<(&'a T, Event)>, + >( + // When "self" is dropped, the handlers are automatically disabled. + // However, this is not necessarily true if the owner is dropped. + // So require the owner to outlive the character device. + &'chardev self, + owner: &'owner T, + _can_receive: CanReceiveFn, + _receive: ReceiveFn, + _event: EventFn, + ) { + unsafe extern "C" fn rust_can_receive_cb FnCall<(&'a T,), u32>>( + opaque: *mut c_void, + ) -> c_int { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + let r = F::call((owner,)); + r.try_into().unwrap() + } + + unsafe extern "C" fn rust_receive_cb FnCall<(&'a T, &'b [u8])>>( + opaque: *mut c_void, + buf: *const u8, + size: c_int, + ) { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) }; + F::call((owner, buf)) + } + + unsafe extern "C" fn rust_event_cb FnCall<(&'a T, Event)>>( + opaque: *mut c_void, + event: Event, + ) { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + F::call((owner, event)) + } + + let _: () = CanReceiveFn::ASSERT_IS_SOME; + let receive_cb: Option = + if ReceiveFn::is_some() { + Some(rust_receive_cb::) + } else { + None + }; + let event_cb: Option = if EventFn::is_some() { + Some(rust_event_cb::) + } else { + None + }; + + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { + bindings::qemu_chr_fe_set_handlers( + addr_of_mut!(*chr), + Some(rust_can_receive_cb::), + receive_cb, + event_cb, + None, + (owner as *const T as *mut T).cast::(), + core::ptr::null_mut(), + true, + ); + } + } + + /// Disable the front-end's character device handlers. + pub fn disable_handlers(&self) { + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { + bindings::qemu_chr_fe_set_handlers( + addr_of_mut!(*chr), + None, + None, + None, + None, + core::ptr::null_mut(), + core::ptr::null_mut(), + true, + ); + } + } + + /// Notify that the frontend is ready to receive data. + pub fn accept_input(&self) { + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) } + } + + /// Temporarily borrow the character device, allowing it to be used + /// as an implementor of `Write`. Note that it is not valid to drop + /// the big QEMU lock while the character device is borrowed, as + /// that might cause C code to write to the character device. + pub fn borrow_mut(&self) -> impl Write + '_ { + self.inner.borrow_mut() + } + + /// Send a continuous stream of zero bits on the line if `enabled` is + /// true, or a short stream if `enabled` is false. + pub fn send_break(&self, long: bool) -> io::Result<()> { + let mut chr = self.inner.borrow_mut(); + let mut duration: c_int = long.into(); + // SAFETY: the borrow promises that the BQL is taken + let r = unsafe { + bindings::qemu_chr_fe_ioctl( + addr_of_mut!(*chr), + bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32, + addr_of_mut!(duration).cast::(), + ) + }; + + errno::into_io_result(r).map(|_| ()) + } + + /// Write data to a character backend from the front end. This function + /// will send data from the front end to the back end. Unlike + /// `write`, this function will block if the back end cannot + /// consume all of the data attempted to be written. + /// + /// Returns the number of bytes consumed (0 if no associated Chardev) or an + /// error. + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = buf.len().try_into().unwrap(); + // SAFETY: qemu_chr_fe_write is thread-safe + let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) }; + errno::into_io_result(r).map(|cnt| cnt as usize) + } + + /// Write data to a character backend from the front end. This function + /// will send data from the front end to the back end. Unlike + /// `write`, this function will block if the back end cannot + /// consume all of the data attempted to be written. + /// + /// Returns the number of bytes consumed (0 if no associated Chardev) or an + /// error. + pub fn write_all(&self, buf: &[u8]) -> io::Result<()> { + let len = buf.len().try_into().unwrap(); + // SAFETY: qemu_chr_fe_write_all is thread-safe + let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) }; + errno::into_io_result(r).and_then(|cnt| { + if cnt as usize == buf.len() { + Ok(()) + } else { + Err(ErrorKind::WriteZero.into()) + } + }) + } +} unsafe impl ObjectType for Chardev { type Class = ChardevClass; diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 47b6977828..a3415a2ebc 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -106,3 +106,4 @@ impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1); impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2); impl_zeroable!(crate::bindings::MemoryRegionOps); impl_zeroable!(crate::bindings::MemTxAttrs); +impl_zeroable!(crate::bindings::CharBackend); From 959fd759a2a55d90bf18f5b275cf6c7b11b27a79 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Feb 2025 14:36:44 +0100 Subject: [PATCH 2233/2892] rust: pl011: move register definitions out of lib.rs Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 7 +- rust/hw/char/pl011/src/lib.rs | 509 +--------------------------- rust/hw/char/pl011/src/registers.rs | 506 +++++++++++++++++++++++++++ 3 files changed, 512 insertions(+), 510 deletions(-) create mode 100644 rust/hw/char/pl011/src/registers.rs diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index d0857b470c..01540654cc 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -26,10 +26,13 @@ use qemu_api::{ use crate::{ device_class, - registers::{self, Interrupt}, - RegisterOffset, + registers::{self, Interrupt, RegisterOffset}, }; +// TODO: You must disable the UART before any of the control registers are +// reprogrammed. When the UART is disabled in the middle of transmission or +// reception, it completes the current character before stopping + /// Integer Baud Rate Divider, `UARTIBRD` const IBRD_MASK: u32 = 0xffff; diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 1bf46c65af..45c13ba899 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -18,516 +18,9 @@ use qemu_api::c_str; mod device; mod device_class; +mod registers; pub use device::pl011_create; pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011"); pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); - -/// Offset of each register from the base memory address of the device. -/// -/// # Source -/// ARM DDI 0183G, Table 3-1 p.3-3 -#[doc(alias = "offset")] -#[allow(non_camel_case_types)] -#[repr(u64)] -#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] -enum RegisterOffset { - /// Data Register - /// - /// A write to this register initiates the actual data transmission - #[doc(alias = "UARTDR")] - DR = 0x000, - /// Receive Status Register or Error Clear Register - #[doc(alias = "UARTRSR")] - #[doc(alias = "UARTECR")] - RSR = 0x004, - /// Flag Register - /// - /// A read of this register shows if transmission is complete - #[doc(alias = "UARTFR")] - FR = 0x018, - /// Fractional Baud Rate Register - /// - /// responsible for baud rate speed - #[doc(alias = "UARTFBRD")] - FBRD = 0x028, - /// `IrDA` Low-Power Counter Register - #[doc(alias = "UARTILPR")] - ILPR = 0x020, - /// Integer Baud Rate Register - /// - /// Responsible for baud rate speed - #[doc(alias = "UARTIBRD")] - IBRD = 0x024, - /// line control register (data frame format) - #[doc(alias = "UARTLCR_H")] - LCR_H = 0x02C, - /// Toggle UART, transmission or reception - #[doc(alias = "UARTCR")] - CR = 0x030, - /// Interrupt FIFO Level Select Register - #[doc(alias = "UARTIFLS")] - FLS = 0x034, - /// Interrupt Mask Set/Clear Register - #[doc(alias = "UARTIMSC")] - IMSC = 0x038, - /// Raw Interrupt Status Register - #[doc(alias = "UARTRIS")] - RIS = 0x03C, - /// Masked Interrupt Status Register - #[doc(alias = "UARTMIS")] - MIS = 0x040, - /// Interrupt Clear Register - #[doc(alias = "UARTICR")] - ICR = 0x044, - /// DMA control Register - #[doc(alias = "UARTDMACR")] - DMACR = 0x048, - ///// Reserved, offsets `0x04C` to `0x07C`. - //Reserved = 0x04C, -} - -mod registers { - //! Device registers exposed as typed structs which are backed by arbitrary - //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. - use bilge::prelude::*; - use qemu_api::impl_vmstate_bitsized; - - /// Receive Status Register / Data Register common error bits - /// - /// The `UARTRSR` register is updated only when a read occurs - /// from the `UARTDR` register with the same status information - /// that can also be obtained by reading the `UARTDR` register - #[bitsize(8)] - #[derive(Clone, Copy, Default, DebugBits, FromBits)] - pub struct Errors { - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, - _reserved_unpredictable: u4, - } - - // TODO: FIFO Mode has different semantics - /// Data Register, `UARTDR` - /// - /// The `UARTDR` register is the data register. - /// - /// For words to be transmitted: - /// - /// - if the FIFOs are enabled, data written to this location is pushed onto - /// the transmit - /// FIFO - /// - if the FIFOs are not enabled, data is stored in the transmitter - /// holding register (the - /// bottom word of the transmit FIFO). - /// - /// The write operation initiates transmission from the UART. The data is - /// prefixed with a start bit, appended with the appropriate parity bit - /// (if parity is enabled), and a stop bit. The resultant word is then - /// transmitted. - /// - /// For received words: - /// - /// - if the FIFOs are enabled, the data byte and the 4-bit status (break, - /// frame, parity, - /// and overrun) is pushed onto the 12-bit wide receive FIFO - /// - if the FIFOs are not enabled, the data byte and status are stored in - /// the receiving - /// holding register (the bottom word of the receive FIFO). - /// - /// The received data byte is read by performing reads from the `UARTDR` - /// register along with the corresponding status information. The status - /// information can also be read by a read of the `UARTRSR/UARTECR` - /// register. - /// - /// # Note - /// - /// You must disable the UART before any of the control registers are - /// reprogrammed. When the UART is disabled in the middle of - /// transmission or reception, it completes the current character before - /// stopping. - /// - /// # Source - /// ARM DDI 0183G 3.3.1 Data Register, UARTDR - #[bitsize(32)] - #[derive(Clone, Copy, Default, DebugBits, FromBits)] - #[doc(alias = "UARTDR")] - pub struct Data { - pub data: u8, - pub errors: Errors, - _reserved: u16, - } - impl_vmstate_bitsized!(Data); - - impl Data { - // bilge is not very const-friendly, unfortunately - pub const BREAK: Self = Self { value: 1 << 10 }; - } - - // TODO: FIFO Mode has different semantics - /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` - /// - /// The UARTRSR/UARTECR register is the receive status register/error clear - /// register. Receive status can also be read from the `UARTRSR` - /// register. If the status is read from this register, then the status - /// information for break, framing and parity corresponds to the - /// data character read from the [Data register](Data), `UARTDR` prior to - /// reading the UARTRSR register. The status information for overrun is - /// set immediately when an overrun condition occurs. - /// - /// - /// # Note - /// The received data character must be read first from the [Data - /// Register](Data), `UARTDR` before reading the error status associated - /// with that data character from the `UARTRSR` register. This read - /// sequence cannot be reversed, because the `UARTRSR` register is - /// updated only when a read occurs from the `UARTDR` register. However, - /// the status information can also be obtained by reading the `UARTDR` - /// register - /// - /// # Source - /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, - /// UARTRSR/UARTECR - #[bitsize(32)] - #[derive(Clone, Copy, DebugBits, FromBits)] - pub struct ReceiveStatusErrorClear { - pub errors: Errors, - _reserved_unpredictable: u24, - } - impl_vmstate_bitsized!(ReceiveStatusErrorClear); - - impl ReceiveStatusErrorClear { - pub fn set_from_data(&mut self, data: Data) { - self.set_errors(data.errors()); - } - - pub fn reset(&mut self) { - // All the bits are cleared to 0 on reset. - *self = Self::default(); - } - } - - impl Default for ReceiveStatusErrorClear { - fn default() -> Self { - 0.into() - } - } - - #[bitsize(32)] - #[derive(Clone, Copy, DebugBits, FromBits)] - /// Flag Register, `UARTFR` - #[doc(alias = "UARTFR")] - pub struct Flags { - /// CTS Clear to send. This bit is the complement of the UART clear to - /// send, `nUARTCTS`, modem status input. That is, the bit is 1 - /// when `nUARTCTS` is LOW. - pub clear_to_send: bool, - /// DSR Data set ready. This bit is the complement of the UART data set - /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when - /// `nUARTDSR` is LOW. - pub data_set_ready: bool, - /// DCD Data carrier detect. This bit is the complement of the UART data - /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is - /// 1 when `nUARTDCD` is LOW. - pub data_carrier_detect: bool, - /// BUSY UART busy. If this bit is set to 1, the UART is busy - /// transmitting data. This bit remains set until the complete - /// byte, including all the stop bits, has been sent from the - /// shift register. This bit is set as soon as the transmit FIFO - /// becomes non-empty, regardless of whether the UART is enabled - /// or not. - pub busy: bool, - /// RXFE Receive FIFO empty. The meaning of this bit depends on the - /// state of the FEN bit in the UARTLCR_H register. If the FIFO - /// is disabled, this bit is set when the receive holding - /// register is empty. If the FIFO is enabled, the RXFE bit is - /// set when the receive FIFO is empty. - pub receive_fifo_empty: bool, - /// TXFF Transmit FIFO full. The meaning of this bit depends on the - /// state of the FEN bit in the UARTLCR_H register. If the FIFO - /// is disabled, this bit is set when the transmit holding - /// register is full. If the FIFO is enabled, the TXFF bit is - /// set when the transmit FIFO is full. - pub transmit_fifo_full: bool, - /// RXFF Receive FIFO full. The meaning of this bit depends on the state - /// of the FEN bit in the UARTLCR_H register. If the FIFO is - /// disabled, this bit is set when the receive holding register - /// is full. If the FIFO is enabled, the RXFF bit is set when - /// the receive FIFO is full. - pub receive_fifo_full: bool, - /// Transmit FIFO empty. The meaning of this bit depends on the state of - /// the FEN bit in the [Line Control register](LineControl), - /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the - /// transmit holding register is empty. If the FIFO is enabled, - /// the TXFE bit is set when the transmit FIFO is empty. This - /// bit does not indicate if there is data in the transmit shift - /// register. - pub transmit_fifo_empty: bool, - /// `RI`, is `true` when `nUARTRI` is `LOW`. - pub ring_indicator: bool, - _reserved_zero_no_modify: u23, - } - impl_vmstate_bitsized!(Flags); - - impl Flags { - pub fn reset(&mut self) { - *self = Self::default(); - } - } - - impl Default for Flags { - fn default() -> Self { - let mut ret: Self = 0.into(); - // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - ret.set_receive_fifo_empty(true); - ret.set_transmit_fifo_empty(true); - ret - } - } - - #[bitsize(32)] - #[derive(Clone, Copy, DebugBits, FromBits)] - /// Line Control Register, `UARTLCR_H` - #[doc(alias = "UARTLCR_H")] - pub struct LineControl { - /// BRK Send break. - /// - /// If this bit is set to `1`, a low-level is continually output on the - /// `UARTTXD` output, after completing transmission of the - /// current character. For the proper execution of the break command, - /// the software must set this bit for at least two complete - /// frames. For normal use, this bit must be cleared to `0`. - pub send_break: bool, - /// 1 PEN Parity enable: - /// - /// - 0 = parity is disabled and no parity bit added to the data frame - /// - 1 = parity checking and generation is enabled. - /// - /// See Table 3-11 on page 3-14 for the parity truth table. - pub parity_enabled: bool, - /// EPS Even parity select. Controls the type of parity the UART uses - /// during transmission and reception: - /// - 0 = odd parity. The UART generates or checks for an odd number of - /// 1s in the data and parity bits. - /// - 1 = even parity. The UART generates or checks for an even number - /// of 1s in the data and parity bits. - /// This bit has no effect when the `PEN` bit disables parity checking - /// and generation. See Table 3-11 on page 3-14 for the parity - /// truth table. - pub parity: Parity, - /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits - /// are transmitted at the end of the frame. The receive - /// logic does not check for two stop bits being received. - pub two_stops_bits: bool, - /// FEN Enable FIFOs: - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers 1 = transmit and receive FIFO - /// buffers are enabled (FIFO mode). - pub fifos_enabled: Mode, - /// WLEN Word length. These bits indicate the number of data bits - /// transmitted or received in a frame as follows: b11 = 8 bits - /// b10 = 7 bits - /// b01 = 6 bits - /// b00 = 5 bits. - pub word_length: WordLength, - /// 7 SPS Stick parity select. - /// 0 = stick parity is disabled - /// 1 = either: - /// • if the EPS bit is 0 then the parity bit is transmitted and checked - /// as a 1 • if the EPS bit is 1 then the parity bit is - /// transmitted and checked as a 0. This bit has no effect when - /// the PEN bit disables parity checking and generation. See Table 3-11 - /// on page 3-14 for the parity truth table. - pub sticky_parity: bool, - /// 31:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u24, - } - impl_vmstate_bitsized!(LineControl); - - impl LineControl { - pub fn reset(&mut self) { - // All the bits are cleared to 0 when reset. - *self = 0.into(); - } - } - - impl Default for LineControl { - fn default() -> Self { - 0.into() - } - } - - #[bitsize(1)] - #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] - /// `EPS` "Even parity select", field of [Line Control - /// register](LineControl). - pub enum Parity { - /// - 0 = odd parity. The UART generates or checks for an odd number of - /// 1s in the data and parity bits. - Odd = 0, - /// - 1 = even parity. The UART generates or checks for an even number - /// of 1s in the data and parity bits. - Even = 1, - } - - #[bitsize(1)] - #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] - /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control - /// register](LineControl). - pub enum Mode { - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers - Character = 0, - /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). - FIFO = 1, - } - - #[bitsize(2)] - #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] - /// `WLEN` Word length, field of [Line Control register](LineControl). - /// - /// These bits indicate the number of data bits transmitted or received in a - /// frame as follows: - pub enum WordLength { - /// b11 = 8 bits - _8Bits = 0b11, - /// b10 = 7 bits - _7Bits = 0b10, - /// b01 = 6 bits - _6Bits = 0b01, - /// b00 = 5 bits. - _5Bits = 0b00, - } - - /// Control Register, `UARTCR` - /// - /// The `UARTCR` register is the control register. All the bits are cleared - /// to `0` on reset except for bits `9` and `8` that are set to `1`. - /// - /// # Source - /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 - #[bitsize(32)] - #[doc(alias = "UARTCR")] - #[derive(Clone, Copy, DebugBits, FromBits)] - pub struct Control { - /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled - /// in the middle of transmission or reception, it completes the current - /// character before stopping. 1 = the UART is enabled. Data - /// transmission and reception occurs for either UART signals or SIR - /// signals depending on the setting of the SIREN bit. - pub enable_uart: bool, - /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT` - /// remains LOW (no light pulse generated), and signal transitions on - /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is - /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, - /// in the marking state. Signal transitions on UARTRXD or modem status - /// inputs have no effect. This bit has no effect if the UARTEN bit - /// disables the UART. - pub enable_sir: bool, - /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding - /// mode. If this bit is cleared to 0, low-level bits are transmitted as - /// an active high pulse with a width of 3/ 16th of the bit period. If - /// this bit is set to 1, low-level bits are transmitted with a pulse - /// width that is 3 times the period of the IrLPBaud16 input signal, - /// regardless of the selected bit rate. Setting this bit uses less - /// power, but might reduce transmission distances. - pub sir_lowpower_irda_mode: u1, - /// Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u4, - /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is - /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR - /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed - /// through to the SIRIN path. The SIRTEST bit in the test register must - /// be set to 1 to override the normal half-duplex SIR operation. This - /// must be the requirement for accessing the test registers during - /// normal operation, and SIRTEST must be cleared to 0 when loopback - /// testing is finished. This feature reduces the amount of external - /// coupling required during system test. If this bit is set to 1, and - /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the - /// UARTRXD path. In either SIR mode or UART mode, when this bit is set, - /// the modem outputs are also fed through to the modem inputs. This bit - /// is cleared to 0 on reset, to disable loopback. - pub enable_loopback: bool, - /// `TXE` Transmit enable. If this bit is set to 1, the transmit section - /// of the UART is enabled. Data transmission occurs for either UART - /// signals, or SIR signals depending on the setting of the SIREN bit. - /// When the UART is disabled in the middle of transmission, it - /// completes the current character before stopping. - pub enable_transmit: bool, - /// `RXE` Receive enable. If this bit is set to 1, the receive section - /// of the UART is enabled. Data reception occurs for either UART - /// signals or SIR signals depending on the setting of the SIREN bit. - /// When the UART is disabled in the middle of reception, it completes - /// the current character before stopping. - pub enable_receive: bool, - /// `DTR` Data transmit ready. This bit is the complement of the UART - /// data transmit ready, `nUARTDTR`, modem status output. That is, when - /// the bit is programmed to a 1 then `nUARTDTR` is LOW. - pub data_transmit_ready: bool, - /// `RTS` Request to send. This bit is the complement of the UART - /// request to send, `nUARTRTS`, modem status output. That is, when the - /// bit is programmed to a 1 then `nUARTRTS` is LOW. - pub request_to_send: bool, - /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`) - /// modem status output. That is, when the bit is programmed to a 1 the - /// output is 0. For DTE this can be used as Data Carrier Detect (DCD). - pub out_1: bool, - /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`) - /// modem status output. That is, when the bit is programmed to a 1, the - /// output is 0. For DTE this can be used as Ring Indicator (RI). - pub out_2: bool, - /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1, - /// RTS hardware flow control is enabled. Data is only requested when - /// there is space in the receive FIFO for it to be received. - pub rts_hardware_flow_control_enable: bool, - /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1, - /// CTS hardware flow control is enabled. Data is only transmitted when - /// the `nUARTCTS` signal is asserted. - pub cts_hardware_flow_control_enable: bool, - /// 31:16 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify2: u16, - } - impl_vmstate_bitsized!(Control); - - impl Control { - pub fn reset(&mut self) { - *self = 0.into(); - self.set_enable_receive(true); - self.set_enable_transmit(true); - } - } - - impl Default for Control { - fn default() -> Self { - let mut ret: Self = 0.into(); - ret.reset(); - ret - } - } - - /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC - pub struct Interrupt(pub u32); - - impl Interrupt { - pub const OE: Self = Self(1 << 10); - pub const BE: Self = Self(1 << 9); - pub const PE: Self = Self(1 << 8); - pub const FE: Self = Self(1 << 7); - pub const RT: Self = Self(1 << 6); - pub const TX: Self = Self(1 << 5); - pub const RX: Self = Self(1 << 4); - pub const DSR: Self = Self(1 << 3); - pub const DCD: Self = Self(1 << 2); - pub const CTS: Self = Self(1 << 1); - pub const RI: Self = Self(1 << 0); - - pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); - pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0); - } -} - -// TODO: You must disable the UART before any of the control registers are -// reprogrammed. When the UART is disabled in the middle of transmission or -// reception, it completes the current character before stopping diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs new file mode 100644 index 0000000000..cd92fa2c30 --- /dev/null +++ b/rust/hw/char/pl011/src/registers.rs @@ -0,0 +1,506 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Device registers exposed as typed structs which are backed by arbitrary +//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. + +use bilge::prelude::*; +use qemu_api::impl_vmstate_bitsized; + +/// Offset of each register from the base memory address of the device. +/// +/// # Source +/// ARM DDI 0183G, Table 3-1 p.3-3 +#[doc(alias = "offset")] +#[allow(non_camel_case_types)] +#[repr(u64)] +#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] +pub enum RegisterOffset { + /// Data Register + /// + /// A write to this register initiates the actual data transmission + #[doc(alias = "UARTDR")] + DR = 0x000, + /// Receive Status Register or Error Clear Register + #[doc(alias = "UARTRSR")] + #[doc(alias = "UARTECR")] + RSR = 0x004, + /// Flag Register + /// + /// A read of this register shows if transmission is complete + #[doc(alias = "UARTFR")] + FR = 0x018, + /// Fractional Baud Rate Register + /// + /// responsible for baud rate speed + #[doc(alias = "UARTFBRD")] + FBRD = 0x028, + /// `IrDA` Low-Power Counter Register + #[doc(alias = "UARTILPR")] + ILPR = 0x020, + /// Integer Baud Rate Register + /// + /// Responsible for baud rate speed + #[doc(alias = "UARTIBRD")] + IBRD = 0x024, + /// line control register (data frame format) + #[doc(alias = "UARTLCR_H")] + LCR_H = 0x02C, + /// Toggle UART, transmission or reception + #[doc(alias = "UARTCR")] + CR = 0x030, + /// Interrupt FIFO Level Select Register + #[doc(alias = "UARTIFLS")] + FLS = 0x034, + /// Interrupt Mask Set/Clear Register + #[doc(alias = "UARTIMSC")] + IMSC = 0x038, + /// Raw Interrupt Status Register + #[doc(alias = "UARTRIS")] + RIS = 0x03C, + /// Masked Interrupt Status Register + #[doc(alias = "UARTMIS")] + MIS = 0x040, + /// Interrupt Clear Register + #[doc(alias = "UARTICR")] + ICR = 0x044, + /// DMA control Register + #[doc(alias = "UARTDMACR")] + DMACR = 0x048, + ///// Reserved, offsets `0x04C` to `0x07C`. + //Reserved = 0x04C, +} + +/// Receive Status Register / Data Register common error bits +/// +/// The `UARTRSR` register is updated only when a read occurs +/// from the `UARTDR` register with the same status information +/// that can also be obtained by reading the `UARTDR` register +#[bitsize(8)] +#[derive(Clone, Copy, Default, DebugBits, FromBits)] +pub struct Errors { + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + _reserved_unpredictable: u4, +} + +// TODO: FIFO Mode has different semantics +/// Data Register, `UARTDR` +/// +/// The `UARTDR` register is the data register. +/// +/// For words to be transmitted: +/// +/// - if the FIFOs are enabled, data written to this location is pushed onto the +/// transmit +/// FIFO +/// - if the FIFOs are not enabled, data is stored in the transmitter holding +/// register (the +/// bottom word of the transmit FIFO). +/// +/// The write operation initiates transmission from the UART. The data is +/// prefixed with a start bit, appended with the appropriate parity bit +/// (if parity is enabled), and a stop bit. The resultant word is then +/// transmitted. +/// +/// For received words: +/// +/// - if the FIFOs are enabled, the data byte and the 4-bit status (break, +/// frame, parity, +/// and overrun) is pushed onto the 12-bit wide receive FIFO +/// - if the FIFOs are not enabled, the data byte and status are stored in the +/// receiving +/// holding register (the bottom word of the receive FIFO). +/// +/// The received data byte is read by performing reads from the `UARTDR` +/// register along with the corresponding status information. The status +/// information can also be read by a read of the `UARTRSR/UARTECR` +/// register. +/// +/// # Note +/// +/// You must disable the UART before any of the control registers are +/// reprogrammed. When the UART is disabled in the middle of +/// transmission or reception, it completes the current character before +/// stopping. +/// +/// # Source +/// ARM DDI 0183G 3.3.1 Data Register, UARTDR +#[bitsize(32)] +#[derive(Clone, Copy, Default, DebugBits, FromBits)] +#[doc(alias = "UARTDR")] +pub struct Data { + pub data: u8, + pub errors: Errors, + _reserved: u16, +} +impl_vmstate_bitsized!(Data); + +impl Data { + // bilge is not very const-friendly, unfortunately + pub const BREAK: Self = Self { value: 1 << 10 }; +} + +// TODO: FIFO Mode has different semantics +/// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` +/// +/// The UARTRSR/UARTECR register is the receive status register/error clear +/// register. Receive status can also be read from the `UARTRSR` +/// register. If the status is read from this register, then the status +/// information for break, framing and parity corresponds to the +/// data character read from the [Data register](Data), `UARTDR` prior to +/// reading the UARTRSR register. The status information for overrun is +/// set immediately when an overrun condition occurs. +/// +/// +/// # Note +/// The received data character must be read first from the [Data +/// Register](Data), `UARTDR` before reading the error status associated +/// with that data character from the `UARTRSR` register. This read +/// sequence cannot be reversed, because the `UARTRSR` register is +/// updated only when a read occurs from the `UARTDR` register. However, +/// the status information can also be obtained by reading the `UARTDR` +/// register +/// +/// # Source +/// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, +/// UARTRSR/UARTECR +#[bitsize(32)] +#[derive(Clone, Copy, DebugBits, FromBits)] +pub struct ReceiveStatusErrorClear { + pub errors: Errors, + _reserved_unpredictable: u24, +} +impl_vmstate_bitsized!(ReceiveStatusErrorClear); + +impl ReceiveStatusErrorClear { + pub fn set_from_data(&mut self, data: Data) { + self.set_errors(data.errors()); + } + + pub fn reset(&mut self) { + // All the bits are cleared to 0 on reset. + *self = Self::default(); + } +} + +impl Default for ReceiveStatusErrorClear { + fn default() -> Self { + 0.into() + } +} + +#[bitsize(32)] +#[derive(Clone, Copy, DebugBits, FromBits)] +/// Flag Register, `UARTFR` +#[doc(alias = "UARTFR")] +pub struct Flags { + /// CTS Clear to send. This bit is the complement of the UART clear to + /// send, `nUARTCTS`, modem status input. That is, the bit is 1 + /// when `nUARTCTS` is LOW. + pub clear_to_send: bool, + /// DSR Data set ready. This bit is the complement of the UART data set + /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when + /// `nUARTDSR` is LOW. + pub data_set_ready: bool, + /// DCD Data carrier detect. This bit is the complement of the UART data + /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is + /// 1 when `nUARTDCD` is LOW. + pub data_carrier_detect: bool, + /// BUSY UART busy. If this bit is set to 1, the UART is busy + /// transmitting data. This bit remains set until the complete + /// byte, including all the stop bits, has been sent from the + /// shift register. This bit is set as soon as the transmit FIFO + /// becomes non-empty, regardless of whether the UART is enabled + /// or not. + pub busy: bool, + /// RXFE Receive FIFO empty. The meaning of this bit depends on the + /// state of the FEN bit in the UARTLCR_H register. If the FIFO + /// is disabled, this bit is set when the receive holding + /// register is empty. If the FIFO is enabled, the RXFE bit is + /// set when the receive FIFO is empty. + pub receive_fifo_empty: bool, + /// TXFF Transmit FIFO full. The meaning of this bit depends on the + /// state of the FEN bit in the UARTLCR_H register. If the FIFO + /// is disabled, this bit is set when the transmit holding + /// register is full. If the FIFO is enabled, the TXFF bit is + /// set when the transmit FIFO is full. + pub transmit_fifo_full: bool, + /// RXFF Receive FIFO full. The meaning of this bit depends on the state + /// of the FEN bit in the UARTLCR_H register. If the FIFO is + /// disabled, this bit is set when the receive holding register + /// is full. If the FIFO is enabled, the RXFF bit is set when + /// the receive FIFO is full. + pub receive_fifo_full: bool, + /// Transmit FIFO empty. The meaning of this bit depends on the state of + /// the FEN bit in the [Line Control register](LineControl), + /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the + /// transmit holding register is empty. If the FIFO is enabled, + /// the TXFE bit is set when the transmit FIFO is empty. This + /// bit does not indicate if there is data in the transmit shift + /// register. + pub transmit_fifo_empty: bool, + /// `RI`, is `true` when `nUARTRI` is `LOW`. + pub ring_indicator: bool, + _reserved_zero_no_modify: u23, +} +impl_vmstate_bitsized!(Flags); + +impl Flags { + pub fn reset(&mut self) { + *self = Self::default(); + } +} + +impl Default for Flags { + fn default() -> Self { + let mut ret: Self = 0.into(); + // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 + ret.set_receive_fifo_empty(true); + ret.set_transmit_fifo_empty(true); + ret + } +} + +#[bitsize(32)] +#[derive(Clone, Copy, DebugBits, FromBits)] +/// Line Control Register, `UARTLCR_H` +#[doc(alias = "UARTLCR_H")] +pub struct LineControl { + /// BRK Send break. + /// + /// If this bit is set to `1`, a low-level is continually output on the + /// `UARTTXD` output, after completing transmission of the + /// current character. For the proper execution of the break command, + /// the software must set this bit for at least two complete + /// frames. For normal use, this bit must be cleared to `0`. + pub send_break: bool, + /// 1 PEN Parity enable: + /// + /// - 0 = parity is disabled and no parity bit added to the data frame + /// - 1 = parity checking and generation is enabled. + /// + /// See Table 3-11 on page 3-14 for the parity truth table. + pub parity_enabled: bool, + /// EPS Even parity select. Controls the type of parity the UART uses + /// during transmission and reception: + /// - 0 = odd parity. The UART generates or checks for an odd number of 1s + /// in the data and parity bits. + /// - 1 = even parity. The UART generates or checks for an even number of 1s + /// in the data and parity bits. + /// This bit has no effect when the `PEN` bit disables parity checking + /// and generation. See Table 3-11 on page 3-14 for the parity + /// truth table. + pub parity: Parity, + /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits + /// are transmitted at the end of the frame. The receive + /// logic does not check for two stop bits being received. + pub two_stops_bits: bool, + /// FEN Enable FIFOs: + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers 1 = transmit and receive FIFO + /// buffers are enabled (FIFO mode). + pub fifos_enabled: Mode, + /// WLEN Word length. These bits indicate the number of data bits + /// transmitted or received in a frame as follows: b11 = 8 bits + /// b10 = 7 bits + /// b01 = 6 bits + /// b00 = 5 bits. + pub word_length: WordLength, + /// 7 SPS Stick parity select. + /// 0 = stick parity is disabled + /// 1 = either: + /// • if the EPS bit is 0 then the parity bit is transmitted and checked + /// as a 1 • if the EPS bit is 1 then the parity bit is + /// transmitted and checked as a 0. This bit has no effect when + /// the PEN bit disables parity checking and generation. See Table 3-11 + /// on page 3-14 for the parity truth table. + pub sticky_parity: bool, + /// 31:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u24, +} +impl_vmstate_bitsized!(LineControl); + +impl LineControl { + pub fn reset(&mut self) { + // All the bits are cleared to 0 when reset. + *self = 0.into(); + } +} + +impl Default for LineControl { + fn default() -> Self { + 0.into() + } +} + +#[bitsize(1)] +#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +/// `EPS` "Even parity select", field of [Line Control +/// register](LineControl). +pub enum Parity { + /// - 0 = odd parity. The UART generates or checks for an odd number of 1s + /// in the data and parity bits. + Odd = 0, + /// - 1 = even parity. The UART generates or checks for an even number of 1s + /// in the data and parity bits. + Even = 1, +} + +#[bitsize(1)] +#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +/// `FEN` "Enable FIFOs" or Device mode, field of [Line Control +/// register](LineControl). +pub enum Mode { + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers + Character = 0, + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FIFO = 1, +} + +#[bitsize(2)] +#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +/// `WLEN` Word length, field of [Line Control register](LineControl). +/// +/// These bits indicate the number of data bits transmitted or received in a +/// frame as follows: +pub enum WordLength { + /// b11 = 8 bits + _8Bits = 0b11, + /// b10 = 7 bits + _7Bits = 0b10, + /// b01 = 6 bits + _6Bits = 0b01, + /// b00 = 5 bits. + _5Bits = 0b00, +} + +/// Control Register, `UARTCR` +/// +/// The `UARTCR` register is the control register. All the bits are cleared +/// to `0` on reset except for bits `9` and `8` that are set to `1`. +/// +/// # Source +/// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 +#[bitsize(32)] +#[doc(alias = "UARTCR")] +#[derive(Clone, Copy, DebugBits, FromBits)] +pub struct Control { + /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled + /// in the middle of transmission or reception, it completes the current + /// character before stopping. 1 = the UART is enabled. Data + /// transmission and reception occurs for either UART signals or SIR + /// signals depending on the setting of the SIREN bit. + pub enable_uart: bool, + /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT` + /// remains LOW (no light pulse generated), and signal transitions on + /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is + /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, + /// in the marking state. Signal transitions on UARTRXD or modem status + /// inputs have no effect. This bit has no effect if the UARTEN bit + /// disables the UART. + pub enable_sir: bool, + /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding + /// mode. If this bit is cleared to 0, low-level bits are transmitted as + /// an active high pulse with a width of 3/ 16th of the bit period. If + /// this bit is set to 1, low-level bits are transmitted with a pulse + /// width that is 3 times the period of the IrLPBaud16 input signal, + /// regardless of the selected bit rate. Setting this bit uses less + /// power, but might reduce transmission distances. + pub sir_lowpower_irda_mode: u1, + /// Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u4, + /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is + /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR + /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed + /// through to the SIRIN path. The SIRTEST bit in the test register must + /// be set to 1 to override the normal half-duplex SIR operation. This + /// must be the requirement for accessing the test registers during + /// normal operation, and SIRTEST must be cleared to 0 when loopback + /// testing is finished. This feature reduces the amount of external + /// coupling required during system test. If this bit is set to 1, and + /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the + /// UARTRXD path. In either SIR mode or UART mode, when this bit is set, + /// the modem outputs are also fed through to the modem inputs. This bit + /// is cleared to 0 on reset, to disable loopback. + pub enable_loopback: bool, + /// `TXE` Transmit enable. If this bit is set to 1, the transmit section + /// of the UART is enabled. Data transmission occurs for either UART + /// signals, or SIR signals depending on the setting of the SIREN bit. + /// When the UART is disabled in the middle of transmission, it + /// completes the current character before stopping. + pub enable_transmit: bool, + /// `RXE` Receive enable. If this bit is set to 1, the receive section + /// of the UART is enabled. Data reception occurs for either UART + /// signals or SIR signals depending on the setting of the SIREN bit. + /// When the UART is disabled in the middle of reception, it completes + /// the current character before stopping. + pub enable_receive: bool, + /// `DTR` Data transmit ready. This bit is the complement of the UART + /// data transmit ready, `nUARTDTR`, modem status output. That is, when + /// the bit is programmed to a 1 then `nUARTDTR` is LOW. + pub data_transmit_ready: bool, + /// `RTS` Request to send. This bit is the complement of the UART + /// request to send, `nUARTRTS`, modem status output. That is, when the + /// bit is programmed to a 1 then `nUARTRTS` is LOW. + pub request_to_send: bool, + /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`) + /// modem status output. That is, when the bit is programmed to a 1 the + /// output is 0. For DTE this can be used as Data Carrier Detect (DCD). + pub out_1: bool, + /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`) + /// modem status output. That is, when the bit is programmed to a 1, the + /// output is 0. For DTE this can be used as Ring Indicator (RI). + pub out_2: bool, + /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1, + /// RTS hardware flow control is enabled. Data is only requested when + /// there is space in the receive FIFO for it to be received. + pub rts_hardware_flow_control_enable: bool, + /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1, + /// CTS hardware flow control is enabled. Data is only transmitted when + /// the `nUARTCTS` signal is asserted. + pub cts_hardware_flow_control_enable: bool, + /// 31:16 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify2: u16, +} +impl_vmstate_bitsized!(Control); + +impl Control { + pub fn reset(&mut self) { + *self = 0.into(); + self.set_enable_receive(true); + self.set_enable_transmit(true); + } +} + +impl Default for Control { + fn default() -> Self { + let mut ret: Self = 0.into(); + ret.reset(); + ret + } +} + +/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC +pub struct Interrupt(pub u32); + +impl Interrupt { + pub const OE: Self = Self(1 << 10); + pub const BE: Self = Self(1 << 9); + pub const PE: Self = Self(1 << 8); + pub const FE: Self = Self(1 << 7); + pub const RT: Self = Self(1 << 6); + pub const TX: Self = Self(1 << 5); + pub const RX: Self = Self(1 << 4); + pub const DSR: Self = Self(1 << 3); + pub const DCD: Self = Self(1 << 2); + pub const CTS: Self = Self(1 << 1); + pub const RI: Self = Self(1 << 0); + + pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); + pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0); +} From 87f5c138363da28449835055299abbae57f39a19 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Feb 2025 13:31:38 +0100 Subject: [PATCH 2234/2892] rust: pl011: clean up visibilities of callbacks Do not make callbacks unnecessarily "pub", they are only used through function pointers. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 01540654cc..4cdbbf4b73 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -537,7 +537,7 @@ impl PL011State { } } - pub fn read(&self, offset: hwaddr, _size: u32) -> u64 { + fn read(&self, offset: hwaddr, _size: u32) -> u64 { match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { let device_id = self.get_class().device_id; @@ -560,7 +560,7 @@ impl PL011State { } } - pub fn write(&self, offset: hwaddr, value: u64, _size: u32) { + fn write(&self, offset: hwaddr, value: u64, _size: u32) { let mut update_irq = false; if let Ok(field) = RegisterOffset::try_from(offset) { // qemu_chr_fe_write_all() calls into the can_receive @@ -621,7 +621,7 @@ impl PL011State { } } - pub fn realize(&self) { + fn realize(&self) { // SAFETY: self.char_backend has the correct size and alignment for a // CharBackend object, and its callbacks are of the correct types. unsafe { @@ -638,11 +638,11 @@ impl PL011State { } } - pub fn reset_hold(&self, _type: ResetType) { + fn reset_hold(&self, _type: ResetType) { self.regs.borrow_mut().reset(); } - pub fn update(&self) { + fn update(&self) { let regs = self.regs.borrow(); let flags = regs.int_level & regs.int_enabled; for (irq, i) in self.interrupts.iter().zip(IRQMASK) { From 9b642097d6b793c161c3d1c540dd19a66e02100f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Feb 2025 13:33:29 +0100 Subject: [PATCH 2235/2892] rust: pl011: switch to safe chardev operation Switch bindings::CharBackend with chardev::CharBackend. This removes occurrences of "unsafe" due to FFI and switches the wrappers for receive, can_receive and event callbacks to the common ones implemented by chardev::CharBackend. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 119 +++++++------------------------ 1 file changed, 25 insertions(+), 94 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 4cdbbf4b73..4e282bc9e9 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,18 +2,10 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ - ffi::CStr, - os::raw::{c_int, c_void}, - ptr::{addr_of, addr_of_mut, NonNull}, -}; +use std::{ffi::CStr, ptr::addr_of_mut}; use qemu_api::{ - bindings::{ - qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, - qemu_chr_fe_write_all, CharBackend, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, - }, - chardev::Chardev, + chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, irq::{IRQState, InterruptSource}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, @@ -235,7 +227,7 @@ impl PL011Registers { &mut self, offset: RegisterOffset, value: u32, - char_backend: *mut CharBackend, + char_backend: &CharBackend, ) -> bool { // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; @@ -269,17 +261,9 @@ impl PL011Registers { self.reset_tx_fifo(); } let update = (self.line_control.send_break() != new_val.send_break()) && { - let mut break_enable: c_int = new_val.send_break().into(); - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - unsafe { - qemu_chr_fe_ioctl( - char_backend, - CHR_IOCTL_SERIAL_SET_BREAK as i32, - addr_of_mut!(break_enable).cast::(), - ); - } - self.loopback_break(break_enable > 0) + let break_enable = new_val.send_break(); + let _ = char_backend.send_break(break_enable); + self.loopback_break(break_enable) }; self.line_control = new_val; self.set_read_trigger(); @@ -551,9 +535,7 @@ impl PL011State { let (update_irq, result) = self.regs.borrow_mut().read(field); if update_irq { self.update(); - unsafe { - qemu_chr_fe_accept_input(addr_of!(self.char_backend) as *mut _); - } + self.char_backend.accept_input(); } result.into() } @@ -567,21 +549,16 @@ impl PL011State { // callback, so handle writes before entering PL011Registers. if field == RegisterOffset::DR { // ??? Check if transmitter is enabled. - let ch: u8 = value as u8; - // SAFETY: char_backend is a valid CharBackend instance after it's been - // initialized in realize(). + let ch: [u8; 1] = [value as u8]; // XXX this blocks entire thread. Rewrite to use // qemu_chr_fe_write and background I/O callbacks - unsafe { - qemu_chr_fe_write_all(addr_of!(self.char_backend) as *mut _, &ch, 1); - } + let _ = self.char_backend.write_all(&ch); } - update_irq = self.regs.borrow_mut().write( - field, - value as u32, - addr_of!(self.char_backend) as *mut _, - ); + update_irq = self + .regs + .borrow_mut() + .write(field, value as u32, &self.char_backend); } else { eprintln!("write bad offset {offset} value {value}"); } @@ -590,15 +567,18 @@ impl PL011State { } } - pub fn can_receive(&self) -> bool { - // trace_pl011_can_receive(s->lcr, s->read_count, r); + fn can_receive(&self) -> u32 { let regs = self.regs.borrow(); - regs.read_count < regs.fifo_depth() + // trace_pl011_can_receive(s->lcr, s->read_count, r); + u32::from(regs.read_count < regs.fifo_depth()) } - pub fn receive(&self, ch: u32) { + fn receive(&self, buf: &[u8]) { + if buf.is_empty() { + return; + } let mut regs = self.regs.borrow_mut(); - let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch); + let update_irq = !regs.loopback_enabled() && regs.put_fifo(buf[0].into()); // Release the BqlRefCell before calling self.update() drop(regs); @@ -607,10 +587,10 @@ impl PL011State { } } - pub fn event(&self, event: QEMUChrEvent) { + fn event(&self, event: Event) { let mut update_irq = false; let mut regs = self.regs.borrow_mut(); - if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() { + if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() { update_irq = regs.put_fifo(registers::Data::BREAK.into()); } // Release the BqlRefCell before calling self.update() @@ -622,20 +602,8 @@ impl PL011State { } fn realize(&self) { - // SAFETY: self.char_backend has the correct size and alignment for a - // CharBackend object, and its callbacks are of the correct types. - unsafe { - qemu_chr_fe_set_handlers( - addr_of!(self.char_backend) as *mut CharBackend, - Some(pl011_can_receive), - Some(pl011_receive), - Some(pl011_event), - None, - addr_of!(*self).cast::() as *mut c_void, - core::ptr::null_mut(), - true, - ); - } + self.char_backend + .enable_handlers(self, Self::can_receive, Self::receive, Self::event); } fn reset_hold(&self, _type: ResetType) { @@ -666,43 +634,6 @@ const IRQMASK: [u32; 6] = [ Interrupt::E.0, ]; -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { - let state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_ref().can_receive().into() } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -/// -/// The buffer and size arguments must also be valid. -pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { - let state = NonNull::new(opaque).unwrap().cast::(); - unsafe { - if size > 0 { - debug_assert!(!buf.is_null()); - state.as_ref().receive(u32::from(buf.read_volatile())); - } - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { - let state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_ref().event(event) } -} - /// # Safety /// /// We expect the FFI user of this function to pass a valid pointer for `chr` From aa50bc4fb9d4fc1dc027c4d70babe0acb6c09971 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Dec 2024 12:31:46 +0100 Subject: [PATCH 2236/2892] rust: pl011: pass around registers::Data The values stored in the Fifo are instances of the bitfield-struct registers::Data. Convert as soon as possible the value written into DR, and always refer to the bitfield struct; it's generally cleaner other than PL011State::receive having to do a double conversion u8=>u32=>registers::Data. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 4e282bc9e9..af93ae8beb 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -234,7 +234,7 @@ impl PL011Registers { match offset { DR => { // interrupts always checked - let _ = self.loopback_tx(value); + let _ = self.loopback_tx(value.into()); self.int_level |= Interrupt::TX.0; return true; } @@ -301,7 +301,7 @@ impl PL011Registers { #[inline] #[must_use] - fn loopback_tx(&mut self, value: u32) -> bool { + fn loopback_tx(&mut self, value: registers::Data) -> bool { // Caveat: // // In real hardware, TX loopback happens at the serial-bit level @@ -370,7 +370,7 @@ impl PL011Registers { } fn loopback_break(&mut self, enable: bool) -> bool { - enable && self.loopback_tx(registers::Data::BREAK.into()) + enable && self.loopback_tx(registers::Data::BREAK) } fn set_read_trigger(&mut self) { @@ -429,11 +429,11 @@ impl PL011Registers { } #[must_use] - pub fn put_fifo(&mut self, value: u32) -> bool { + pub fn put_fifo(&mut self, value: registers::Data) -> bool { let depth = self.fifo_depth(); assert!(depth > 0); let slot = (self.read_pos + self.read_count) & (depth - 1); - self.read_fifo[slot] = registers::Data::from(value); + self.read_fifo[slot] = value; self.read_count += 1; self.flags.set_receive_fifo_empty(false); if self.read_count == depth { @@ -578,7 +578,8 @@ impl PL011State { return; } let mut regs = self.regs.borrow_mut(); - let update_irq = !regs.loopback_enabled() && regs.put_fifo(buf[0].into()); + let c: u32 = buf[0].into(); + let update_irq = !regs.loopback_enabled() && regs.put_fifo(c.into()); // Release the BqlRefCell before calling self.update() drop(regs); @@ -591,7 +592,7 @@ impl PL011State { let mut update_irq = false; let mut regs = self.regs.borrow_mut(); if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() { - update_irq = regs.put_fifo(registers::Data::BREAK.into()); + update_irq = regs.put_fifo(registers::Data::BREAK); } // Release the BqlRefCell before calling self.update() drop(regs); From 519088b7cf6dbdef08d8753b57aa29162b83d1a1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Feb 2025 18:19:35 +0100 Subject: [PATCH 2237/2892] rust: hpet: decode HPET registers into enums Generalize timer_and_addr() to decode all registers into a single enum HPETRegister, and use the TryInto derive to separate valid and invalid values. The main advantage lies in checking that all registers are enumerated in the "match" statements. Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 2 + rust/hw/char/pl011/src/lib.rs | 2 - rust/hw/timer/hpet/src/hpet.rs | 222 +++++++++++++++++---------------- 3 files changed, 119 insertions(+), 107 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 5041d6291f..ab1185a814 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -37,6 +37,8 @@ result_unit_err = "allow" should_implement_trait = "deny" # can be for a reason, e.g. in callbacks unused_self = "allow" +# common in device crates +upper_case_acronyms = "allow" # default-allow lints as_ptr_cast_mut = "deny" diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 45c13ba899..dbae76991c 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -12,8 +12,6 @@ //! See [`PL011State`](crate::device::PL011State) for the device model type and //! the [`registers`] module for register types. -#![allow(clippy::upper_case_acronyms)] - use qemu_api::c_str; mod device; diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index d989360ede..20e0afdfca 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -48,8 +48,6 @@ const RTC_ISA_IRQ: usize = 8; const HPET_CLK_PERIOD: u64 = 10; // 10 ns const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns -/// General Capabilities and ID Register -const HPET_CAP_REG: u64 = 0x000; /// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec). const HPET_CAP_REV_ID_VALUE: u64 = 0x1; const HPET_CAP_REV_ID_SHIFT: usize = 0; @@ -65,8 +63,6 @@ const HPET_CAP_VENDER_ID_SHIFT: usize = 16; /// Main Counter Tick Period (bits 32:63) const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32; -/// General Configuration Register -const HPET_CFG_REG: u64 = 0x010; /// Overall Enable (bit 0) const HPET_CFG_ENABLE_SHIFT: usize = 0; /// Legacy Replacement Route (bit 1) @@ -74,14 +70,6 @@ const HPET_CFG_LEG_RT_SHIFT: usize = 1; /// Other bits are reserved. const HPET_CFG_WRITE_MASK: u64 = 0x003; -/// General Interrupt Status Register -const HPET_INT_STATUS_REG: u64 = 0x020; - -/// Main Counter Value Register -const HPET_COUNTER_REG: u64 = 0x0f0; - -/// Timer N Configuration and Capability Register (masked by 0x18) -const HPET_TN_CFG_REG: u64 = 0x000; /// bit 0, 7, and bits 16:31 are reserved. /// bit 4, 5, 15, and bits 32:64 are read-only. const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e; @@ -109,11 +97,51 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; /// Timer N Interrupt Routing Capability (bits 32:63) const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; -/// Timer N Comparator Value Register (masked by 0x18) -const HPET_TN_CMP_REG: u64 = 0x008; +#[derive(qemu_api_macros::TryInto)] +#[repr(u64)] +#[allow(non_camel_case_types)] +/// Timer registers, masked by 0x18 +enum TimerRegister { + /// Timer N Configuration and Capability Register + CFG = 0, + /// Timer N Comparator Value Register + CMP = 8, + /// Timer N FSB Interrupt Route Register + ROUTE = 16, +} -/// Timer N FSB Interrupt Route Register (masked by 0x18) -const HPET_TN_FSB_ROUTE_REG: u64 = 0x010; +#[derive(qemu_api_macros::TryInto)] +#[repr(u64)] +#[allow(non_camel_case_types)] +/// Global registers +enum GlobalRegister { + /// General Capabilities and ID Register + CAP = 0, + /// General Configuration Register + CFG = 0x10, + /// General Interrupt Status Register + INT_STATUS = 0x20, + /// Main Counter Value Register + COUNTER = 0xF0, +} + +enum HPETRegister<'a> { + /// Global register in the range from `0` to `0xff` + Global(GlobalRegister), + + /// Register in the timer block `0x100`...`0x3ff` + Timer(&'a BqlRefCell, TimerRegister), + + /// Invalid address + #[allow(dead_code)] + Unknown(hwaddr), +} + +struct HPETAddrDecode<'a> { + shift: u32, + len: u32, + reg: HPETRegister<'a>, +} const fn hpet_next_wrap(cur_tick: u64) -> u64 { (cur_tick | 0xffffffff) + 1 @@ -471,33 +499,21 @@ impl HPETTimer { self.update_irq(true); } - const fn read(&self, addr: hwaddr, _size: u32) -> u64 { - let shift: u64 = (addr & 4) * 8; - - match addr & !4 { - HPET_TN_CFG_REG => self.config >> shift, // including interrupt capabilities - HPET_TN_CMP_REG => self.cmp >> shift, // comparator register - HPET_TN_FSB_ROUTE_REG => self.fsb >> shift, - _ => { - // TODO: Add trace point - trace_hpet_ram_read_invalid() - // Reserved. - 0 - } + const fn read(&self, reg: TimerRegister) -> u64 { + use TimerRegister::*; + match reg { + CFG => self.config, // including interrupt capabilities + CMP => self.cmp, // comparator register + ROUTE => self.fsb, } } - fn write(&mut self, addr: hwaddr, value: u64, size: u32) { - let shift = ((addr & 4) * 8) as u32; - let len = std::cmp::min(size * 8, 64 - shift); - - match addr & !4 { - HPET_TN_CFG_REG => self.set_tn_cfg_reg(shift, len, value), - HPET_TN_CMP_REG => self.set_tn_cmp_reg(shift, len, value), - HPET_TN_FSB_ROUTE_REG => self.set_tn_fsb_route_reg(shift, len, value), - _ => { - // TODO: Add trace point - trace_hpet_ram_write_invalid() - // Reserved. - } + fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) { + use TimerRegister::*; + match reg { + CFG => self.set_tn_cfg_reg(shift, len, value), + CMP => self.set_tn_cmp_reg(shift, len, value), + ROUTE => self.set_tn_fsb_route_reg(shift, len, value), } } } @@ -749,77 +765,73 @@ impl HPETState { self.rtc_irq_level.set(0); } - fn timer_and_addr(&self, addr: hwaddr) -> Option<(&BqlRefCell, hwaddr)> { - let timer_id: usize = ((addr - 0x100) / 0x20) as usize; - - // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) - if timer_id > self.num_timers.get() { - // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) - None - } else { - // Keep the complete address so that HPETTimer's read and write could - // detect the invalid access. - Some((&self.timers[timer_id], addr & 0x1F)) - } - } - - fn read(&self, addr: hwaddr, size: u32) -> u64 { - let shift: u64 = (addr & 4) * 8; - - // address range of all TN regs - // TODO: Add trace point - trace_hpet_ram_read(addr) - if (0x100..=0x3ff).contains(&addr) { - match self.timer_and_addr(addr) { - None => 0, // Reserved, - Some((timer, tn_addr)) => timer.borrow_mut().read(tn_addr, size), - } - } else { - match addr & !4 { - HPET_CAP_REG => self.capability.get() >> shift, /* including HPET_PERIOD 0x004 */ - // (CNT_CLK_PERIOD field) - HPET_CFG_REG => self.config.get() >> shift, - HPET_COUNTER_REG => { - let cur_tick: u64 = if self.is_hpet_enabled() { - self.get_ticks() - } else { - self.counter.get() - }; - - // TODO: Add trace point - trace_hpet_ram_read_reading_counter(addr & 4, - // cur_tick) - cur_tick >> shift - } - HPET_INT_STATUS_REG => self.int_status.get() >> shift, - _ => { - // TODO: Add trace point- trace_hpet_ram_read_invalid() - // Reserved. - 0 - } - } - } - } - - fn write(&self, addr: hwaddr, value: u64, size: u32) { + fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode { let shift = ((addr & 4) * 8) as u32; let len = std::cmp::min(size * 8, 64 - shift); - // TODO: Add trace point - trace_hpet_ram_write(addr, value) - if (0x100..=0x3ff).contains(&addr) { - match self.timer_and_addr(addr) { - None => (), // Reserved. - Some((timer, tn_addr)) => timer.borrow_mut().write(tn_addr, value, size), - } + addr &= !4; + let reg = if (0..=0xff).contains(&addr) { + GlobalRegister::try_from(addr).map(HPETRegister::Global) } else { - match addr & !0x4 { - HPET_CAP_REG => {} // General Capabilities and ID Register: Read Only - HPET_CFG_REG => self.set_cfg_reg(shift, len, value), - HPET_INT_STATUS_REG => self.set_int_status_reg(shift, len, value), - HPET_COUNTER_REG => self.set_counter_reg(shift, len, value), - _ => { - // TODO: Add trace point - trace_hpet_ram_write_invalid() - // Reserved. + let timer_id: usize = ((addr - 0x100) / 0x20) as usize; + if timer_id <= self.num_timers.get() { + // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) + TimerRegister::try_from(addr) + .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) + } else { + // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) + Err(addr) + } + }; + + // reg is now a Result + // convert the Err case into HPETRegister as well + let reg = reg.unwrap_or_else(HPETRegister::Unknown); + HPETAddrDecode { shift, len, reg } + } + + fn read(&self, addr: hwaddr, size: u32) -> u64 { + // TODO: Add trace point - trace_hpet_ram_read(addr) + let HPETAddrDecode { shift, reg, .. } = self.decode(addr, size); + + use GlobalRegister::*; + use HPETRegister::*; + (match reg { + Timer(timer, tn_reg) => timer.borrow_mut().read(tn_reg), + Global(CAP) => self.capability.get(), /* including HPET_PERIOD 0x004 */ + Global(CFG) => self.config.get(), + Global(INT_STATUS) => self.int_status.get(), + Global(COUNTER) => { + // TODO: Add trace point + // trace_hpet_ram_read_reading_counter(addr & 4, cur_tick) + if self.is_hpet_enabled() { + self.get_ticks() + } else { + self.counter.get() } } + Unknown(_) => { + // TODO: Add trace point- trace_hpet_ram_read_invalid() + 0 + } + }) >> shift + } + + fn write(&self, addr: hwaddr, value: u64, size: u32) { + let HPETAddrDecode { shift, len, reg } = self.decode(addr, size); + + // TODO: Add trace point - trace_hpet_ram_write(addr, value) + use GlobalRegister::*; + use HPETRegister::*; + match reg { + Timer(timer, tn_reg) => timer.borrow_mut().write(tn_reg, value, shift, len), + Global(CAP) => {} // General Capabilities and ID Register: Read Only + Global(CFG) => self.set_cfg_reg(shift, len, value), + Global(INT_STATUS) => self.set_int_status_reg(shift, len, value), + Global(COUNTER) => self.set_counter_reg(shift, len, value), + Unknown(_) => { + // TODO: Add trace point - trace_hpet_ram_write_invalid() + } } } } From 5778ce99971f7e09952a1efbac91d0c97d7a0fee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 14:10:38 +0100 Subject: [PATCH 2238/2892] rust: cell: add full example of declaring a SysBusDevice Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/cell.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 448638e896..ab0785a269 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -73,6 +73,34 @@ //! QEMU device implementations is usually incorrect and can lead to //! thread-safety issues. //! +//! ### Example +//! +//! ``` +//! # use qemu_api::prelude::*; +//! # use qemu_api::{c_str, cell::BqlRefCell, irq::InterruptSource, irq::IRQState}; +//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; +//! # const N_GPIOS: usize = 8; +//! # struct PL061Registers { /* ... */ } +//! # unsafe impl ObjectType for PL061State { +//! # type Class = ::Class; +//! # const TYPE_NAME: &'static std::ffi::CStr = c_str!("pl061"); +//! # } +//! struct PL061State { +//! parent_obj: ParentField, +//! +//! // Configuration is read-only after initialization +//! pullups: u32, +//! pulldowns: u32, +//! +//! // Single values shared with C code use BqlCell, in this case via InterruptSource +//! out: [InterruptSource; N_GPIOS], +//! interrupt: InterruptSource, +//! +//! // Larger state accessed by device methods uses BqlRefCell or Mutex +//! registers: BqlRefCell, +//! } +//! ``` +//! //! ### `BqlCell` //! //! [`BqlCell`] implements interior mutability by moving values in and out of From 094cd35913bd66228a9a3239e66b1f6f5d667d4b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Dec 2024 17:54:33 +0100 Subject: [PATCH 2239/2892] rust: qom: remove operations on &mut The dubious casts of mutable references to objects are not used anymore: the wrappers for qdev_init_clock_in and for IRQ and MMIO initialization can be called directly on the subclasses, without casts, plus they take a shared reference so they can just use "upcast()" instead of "upcast_mut()". Remove them. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/memory.rs | 5 --- rust/qemu-api/src/prelude.rs | 1 - rust/qemu-api/src/qom.rs | 83 ------------------------------------ rust/qemu-api/tests/tests.rs | 34 +-------------- 4 files changed, 2 insertions(+), 121 deletions(-) diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index eff9f09fd7..fdb1ea11fc 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -175,11 +175,6 @@ impl MemoryRegion { ) { unsafe { Self::do_init_io( - // self.0.as_mut_ptr() needed because Rust tries to call - // ObjectDeref::as_mut_ptr() on "&mut Self", instead of coercing - // to "&Self" and then calling MemoryRegion::as_mut_ptr(). - // Revisit if/when ObjectCastMut is not needed anymore; it is - // only used in a couple places for initialization. self.0.as_mut_ptr(), owner.cast::(), &ops.0, diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 634acf37a8..43bfcd5fca 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -17,7 +17,6 @@ pub use crate::qom::InterfaceType; pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; -pub use crate::qom::ObjectCastMut; pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectClassMethods; pub use crate::qom::ObjectMethods; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 2defbd2351..34d7bc0ef9 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -463,90 +463,7 @@ where impl ObjectDeref for &T {} impl ObjectCast for &T {} -/// Trait for mutable type casting operations in the QOM hierarchy. -/// -/// This trait provides the mutable counterparts to [`ObjectCast`]'s conversion -/// functions. Unlike `ObjectCast`, this trait returns `Result` for fallible -/// conversions to preserve the original smart pointer if the cast fails. This -/// is necessary because mutable references cannot be copied, so a failed cast -/// must return ownership of the original reference. For example: -/// -/// ```ignore -/// let mut dev = get_device(); -/// // If this fails, we need the original `dev` back to try something else -/// match dev.dynamic_cast_mut::() { -/// Ok(foodev) => /* use foodev */, -/// Err(dev) => /* still have ownership of dev */ -/// } -/// ``` -pub trait ObjectCastMut: Sized + ObjectDeref + DerefMut -where - Self::Target: ObjectType, -{ - /// Safely convert from a derived type to one of its parent types. - /// - /// This is always safe; the [`IsA`] trait provides static verification - /// that `Self` dereferences to `U` or a child of `U`. - fn upcast_mut<'a, U: ObjectType>(self) -> &'a mut U - where - Self::Target: IsA, - Self: 'a, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait - unsafe { self.unsafe_cast_mut::() } - } - - /// Attempt to convert to a derived type. - /// - /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the - /// object if the conversion failed. This is verified at runtime by - /// checking the object's type information. - fn downcast_mut<'a, U: IsA>(self) -> Result<&'a mut U, Self> - where - Self: 'a, - { - self.dynamic_cast_mut::() - } - - /// Attempt to convert between any two types in the QOM hierarchy. - /// - /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the - /// object if the conversion failed. This is verified at runtime by - /// checking the object's type information. - fn dynamic_cast_mut<'a, U: ObjectType>(self) -> Result<&'a mut U, Self> - where - Self: 'a, - { - unsafe { - // SAFETY: upcasting to Object is always valid, and the - // return type is either NULL or the argument itself - let result: *mut U = - object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); - - result.as_mut().ok_or(self) - } - } - - /// Convert to any QOM type without verification. - /// - /// # Safety - /// - /// What safety? You need to know yourself that the cast is correct; only - /// use when performance is paramount. It is still better than a raw - /// pointer `cast()`, which does not even check that you remain in the - /// realm of QOM `ObjectType`s. - /// - /// `unsafe_cast::()` is always safe. - unsafe fn unsafe_cast_mut<'a, U: ObjectType>(self) -> &'a mut U - where - Self: 'a, - { - unsafe { &mut *self.as_mut_ptr::().cast::() } - } -} - impl ObjectDeref for &mut T {} -impl ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. pub trait ObjectImpl: ObjectType + IsA { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index e3985782a3..269122e7ec 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -2,13 +2,10 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ - ffi::{c_void, CStr}, - ptr::{addr_of, addr_of_mut}, -}; +use std::{ffi::CStr, ptr::addr_of}; use qemu_api::{ - bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool}, + bindings::{module_call_init, module_init_type, qdev_prop_bool}, c_str, cell::{self, BqlCell}, declare_properties, define_property, @@ -182,30 +179,3 @@ fn test_cast() { assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); } } - -#[test] -#[allow(clippy::shadow_unrelated)] -/// Test casts on mutable references. -fn test_cast_mut() { - init_qom(); - let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; - - let p_ref: &mut DummyState = unsafe { &mut *p }; - let obj_ref: &mut Object = p_ref.upcast_mut(); - assert_eq!(addr_of_mut!(*obj_ref), p.cast()); - - let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut(); - let obj_ref = sbd_ref.unwrap_err(); - - let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut(); - let dev_ref = dev_ref.unwrap(); - assert_eq!(addr_of_mut!(*dev_ref), p.cast()); - - // SAFETY: the cast is wrong, but the value is only used for comparison - unsafe { - let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut(); - assert_eq!(addr_of_mut!(*sbd_ref), p.cast()); - - object_unref(p_ref.as_object_mut_ptr().cast::()); - } -} From 82c4d8a3b4e390dbb2601e11e07d291e760def7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 22:00:47 +0100 Subject: [PATCH 2240/2892] qemu/compiler: Absorb 'clang-tsa.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have "qemu/compiler.h" for compiler-specific arrangements, automatically included by "qemu/osdep.h" for each source file. No need to explicitly include a header for a Clang particularity. Suggested-by: Pierrick Bouvier Reviewed-by: Pierrick Bouvier Reviewed-by: Alex Bennée Reviewed-by: Kevin Wolf Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250117170201.91182-1-philmd@linaro.org> --- block/create.c | 1 - bsd-user/qemu.h | 1 - include/block/block_int-common.h | 1 - include/block/graph-lock.h | 2 - include/exec/page-protection.h | 2 - include/qemu/clang-tsa.h | 114 ------------------------------- include/qemu/compiler.h | 96 ++++++++++++++++++++++++++ include/qemu/thread.h | 1 - tests/unit/test-bdrv-drain.c | 1 - tests/unit/test-block-iothread.c | 1 - util/qemu-thread-posix.c | 1 - 11 files changed, 96 insertions(+), 125 deletions(-) delete mode 100644 include/qemu/clang-tsa.h diff --git a/block/create.c b/block/create.c index 72abafb4c1..6b23a21675 100644 --- a/block/create.c +++ b/block/create.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "block/block_int.h" -#include "qemu/clang-tsa.h" #include "qemu/job.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-block-core.h" diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 3eaa14f3f5..4e97c79631 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -40,7 +40,6 @@ extern char **environ; #include "target.h" #include "exec/gdbstub.h" #include "exec/page-protection.h" -#include "qemu/clang-tsa.h" #include "accel/tcg/vcpu-state.h" #include "qemu-os.h" diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index bb91a0f62f..ebb4e56a50 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -28,7 +28,6 @@ #include "block/block-common.h" #include "block/block-global-state.h" #include "block/snapshot.h" -#include "qemu/clang-tsa.h" #include "qemu/iov.h" #include "qemu/rcu.h" #include "qemu/stats64.h" diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h index dc8d949184..2c26c72108 100644 --- a/include/block/graph-lock.h +++ b/include/block/graph-lock.h @@ -20,8 +20,6 @@ #ifndef GRAPH_LOCK_H #define GRAPH_LOCK_H -#include "qemu/clang-tsa.h" - /** * Graph Lock API * This API provides a rwlock used to protect block layer diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h index bae3355f62..3e0a8a0333 100644 --- a/include/exec/page-protection.h +++ b/include/exec/page-protection.h @@ -40,8 +40,6 @@ #ifdef CONFIG_USER_ONLY -#include "qemu/clang-tsa.h" - void TSA_NO_TSA mmap_lock(void); void TSA_NO_TSA mmap_unlock(void); bool have_mmap_lock(void); diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h deleted file mode 100644 index ba06fb8c92..0000000000 --- a/include/qemu/clang-tsa.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef CLANG_TSA_H -#define CLANG_TSA_H - -/* - * Copyright 2018 Jarkko Hietaniemi - * - * 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. - */ - -/* http://clang.llvm.org/docs/ThreadSafetyAnalysis.html - * - * TSA is available since clang 3.6-ish. - */ -#ifdef __clang__ -# define TSA(x) __attribute__((x)) -#else -# define TSA(x) /* No TSA, make TSA attributes no-ops. */ -#endif - -/* TSA_CAPABILITY() is used to annotate typedefs: - * - * typedef pthread_mutex_t TSA_CAPABILITY("mutex") tsa_mutex; - */ -#define TSA_CAPABILITY(x) TSA(capability(x)) - -/* TSA_GUARDED_BY() is used to annotate global variables, - * the data is guarded: - * - * Foo foo TSA_GUARDED_BY(mutex); - */ -#define TSA_GUARDED_BY(x) TSA(guarded_by(x)) - -/* TSA_PT_GUARDED_BY() is used to annotate global pointers, the data - * behind the pointer is guarded. - * - * Foo* ptr TSA_PT_GUARDED_BY(mutex); - */ -#define TSA_PT_GUARDED_BY(x) TSA(pt_guarded_by(x)) - -/* The TSA_REQUIRES() is used to annotate functions: the caller of the - * function MUST hold the resource, the function will NOT release it. - * - * More than one mutex may be specified, comma-separated. - * - * void Foo(void) TSA_REQUIRES(mutex); - */ -#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__)) -#define TSA_REQUIRES_SHARED(...) TSA(requires_shared_capability(__VA_ARGS__)) - -/* TSA_EXCLUDES() is used to annotate functions: the caller of the - * function MUST NOT hold resource, the function first acquires the - * resource, and then releases it. - * - * More than one mutex may be specified, comma-separated. - * - * void Foo(void) TSA_EXCLUDES(mutex); - */ -#define TSA_EXCLUDES(...) TSA(locks_excluded(__VA_ARGS__)) - -/* TSA_ACQUIRE() is used to annotate functions: the caller of the - * function MUST NOT hold the resource, the function will acquire the - * resource, but NOT release it. - * - * More than one mutex may be specified, comma-separated. - * - * void Foo(void) TSA_ACQUIRE(mutex); - */ -#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__)) -#define TSA_ACQUIRE_SHARED(...) TSA(acquire_shared_capability(__VA_ARGS__)) - -/* TSA_RELEASE() is used to annotate functions: the caller of the - * function MUST hold the resource, but the function will then release it. - * - * More than one mutex may be specified, comma-separated. - * - * void Foo(void) TSA_RELEASE(mutex); - */ -#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__)) -#define TSA_RELEASE_SHARED(...) TSA(release_shared_capability(__VA_ARGS__)) - -/* TSA_NO_TSA is used to annotate functions. Use only when you need to. - * - * void Foo(void) TSA_NO_TSA; - */ -#define TSA_NO_TSA TSA(no_thread_safety_analysis) - -/* - * TSA_ASSERT() is used to annotate functions: This function will assert that - * the lock is held. When it returns, the caller of the function is assumed to - * already hold the resource. - * - * More than one mutex may be specified, comma-separated. - */ -#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__)) -#define TSA_ASSERT_SHARED(...) TSA(assert_shared_capability(__VA_ARGS__)) - -#endif /* #ifndef CLANG_TSA_H */ diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index d904408e5e..496dac5ac1 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -207,6 +207,102 @@ # define QEMU_USED #endif +/* + * http://clang.llvm.org/docs/ThreadSafetyAnalysis.html + * + * TSA is available since clang 3.6-ish. + */ +#ifdef __clang__ +# define TSA(x) __attribute__((x)) +#else +# define TSA(x) /* No TSA, make TSA attributes no-ops. */ +#endif + +/* + * TSA_CAPABILITY() is used to annotate typedefs: + * + * typedef pthread_mutex_t TSA_CAPABILITY("mutex") tsa_mutex; + */ +#define TSA_CAPABILITY(x) TSA(capability(x)) + +/* + * TSA_GUARDED_BY() is used to annotate global variables, + * the data is guarded: + * + * Foo foo TSA_GUARDED_BY(mutex); + */ +#define TSA_GUARDED_BY(x) TSA(guarded_by(x)) + +/* + * TSA_PT_GUARDED_BY() is used to annotate global pointers, the data + * behind the pointer is guarded. + * + * Foo* ptr TSA_PT_GUARDED_BY(mutex); + */ +#define TSA_PT_GUARDED_BY(x) TSA(pt_guarded_by(x)) + +/* + * The TSA_REQUIRES() is used to annotate functions: the caller of the + * function MUST hold the resource, the function will NOT release it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_REQUIRES(mutex); + */ +#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__)) +#define TSA_REQUIRES_SHARED(...) TSA(requires_shared_capability(__VA_ARGS__)) + +/* + * TSA_EXCLUDES() is used to annotate functions: the caller of the + * function MUST NOT hold resource, the function first acquires the + * resource, and then releases it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_EXCLUDES(mutex); + */ +#define TSA_EXCLUDES(...) TSA(locks_excluded(__VA_ARGS__)) + +/* + * TSA_ACQUIRE() is used to annotate functions: the caller of the + * function MUST NOT hold the resource, the function will acquire the + * resource, but NOT release it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_ACQUIRE(mutex); + */ +#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__)) +#define TSA_ACQUIRE_SHARED(...) TSA(acquire_shared_capability(__VA_ARGS__)) + +/* + * TSA_RELEASE() is used to annotate functions: the caller of the + * function MUST hold the resource, but the function will then release it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_RELEASE(mutex); + */ +#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__)) +#define TSA_RELEASE_SHARED(...) TSA(release_shared_capability(__VA_ARGS__)) + +/* + * TSA_NO_TSA is used to annotate functions. Use only when you need to. + * + * void Foo(void) TSA_NO_TSA; + */ +#define TSA_NO_TSA TSA(no_thread_safety_analysis) + +/* + * TSA_ASSERT() is used to annotate functions: This function will assert that + * the lock is held. When it returns, the caller of the function is assumed to + * already hold the resource. + * + * More than one mutex may be specified, comma-separated. + */ +#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__)) +#define TSA_ASSERT_SHARED(...) TSA(assert_shared_capability(__VA_ARGS__)) + /* * Ugly CPP trick that is like "defined FOO", but also works in C * code. Useful to replace #ifdef with "if" statements; assumes diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 7eba27a704..6f800aad31 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -3,7 +3,6 @@ #include "qemu/processor.h" #include "qemu/atomic.h" -#include "qemu/clang-tsa.h" typedef struct QemuCond QemuCond; typedef struct QemuSemaphore QemuSemaphore; diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 98ad89b390..7410e6f352 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -28,7 +28,6 @@ #include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" -#include "qemu/clang-tsa.h" #include "iothread.h" static QemuEvent done_event; diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 7324ea4a68..2b358eaaa8 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -29,7 +29,6 @@ #include "system/block-backend.h" #include "qapi/error.h" #include "qobject/qdict.h" -#include "qemu/clang-tsa.h" #include "qemu/main-loop.h" #include "iothread.h" diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 6fff4162ac..b2e26e2120 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -17,7 +17,6 @@ #include "qemu-thread-common.h" #include "qemu/tsan.h" #include "qemu/bitmap.h" -#include "qemu/clang-tsa.h" #ifdef CONFIG_PTHREAD_SET_NAME_NP #include From 46a2cfc448931aeeb86bd721fa64e48ec0594299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:12:05 +0100 Subject: [PATCH 2241/2892] gdbstub: Clarify no more than @gdb_num_core_regs can be accessed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both CPUClass::gdb_read_register() and CPUClass::gdb_write_register() handlers are called from common gdbstub code, and won't be called with register index over CPUClass::gdb_num_core_regs: int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); if (reg < cc->gdb_num_core_regs) { return cc->gdb_read_register(cpu, buf, reg); } ... } static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); if (reg < cc->gdb_num_core_regs) { return cc->gdb_write_register(cpu, mem_buf, reg); } ... } Clarify that in CPUClass docstring, and remove unreachable code on the microblaze and openrisc implementations. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20250122093028.52416-3-philmd@linaro.org> --- include/hw/core/cpu.h | 2 ++ target/microblaze/gdbstub.c | 5 ----- target/openrisc/gdbstub.c | 5 ----- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index fb397cdfc5..7b6b22c431 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -124,7 +124,9 @@ struct SysemuCPUOps; * @get_pc: Callback for getting the Program Counter register. * As above, with the semantics of the target architecture. * @gdb_read_register: Callback for letting GDB read a register. + * No more than @gdb_num_core_regs registers can be read. * @gdb_write_register: Callback for letting GDB write a register. + * No more than @gdb_num_core_regs registers can be written. * @gdb_adjust_breakpoint: Callback for adjusting the address of a * breakpoint. Used by AVR to handle a gdb mis-feature with * its Harvard architecture split code and data. diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 09d74e164d..d493681d38 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -110,14 +110,9 @@ int mb_cpu_gdb_read_stack_protect(CPUState *cs, GByteArray *mem_buf, int n) int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - CPUClass *cc = CPU_GET_CLASS(cs); CPUMBState *env = cpu_env(cs); uint32_t tmp; - if (n > cc->gdb_num_core_regs) { - return 0; - } - tmp = ldl_p(mem_buf); switch (n) { diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index c2a77d5d4d..45bba80d87 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -47,14 +47,9 @@ int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int openrisc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - CPUClass *cc = CPU_GET_CLASS(cs); CPUOpenRISCState *env = cpu_env(cs); uint32_t tmp; - if (n > cc->gdb_num_core_regs) { - return 0; - } - tmp = ldl_p(mem_buf); if (n < 32) { From 270dbee10cd31b86f0a3a8a2691026bdb0b9a071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 14:09:19 +0100 Subject: [PATCH 2242/2892] gdbstub: Check for TCG before calling tb_flush() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the tcg_enabled() check so the compiler can elide the call when TCG isn't available, allowing to remove the tb_flush() stub. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-4-philmd@linaro.org> --- accel/stubs/tcg-stub.c | 4 ---- gdbstub/system.c | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index 7f4208fddf..b2b9881bdf 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -14,10 +14,6 @@ #include "exec/tb-flush.h" #include "exec/exec-all.h" -void tb_flush(CPUState *cpu) -{ -} - G_NORETURN void cpu_loop_exit(CPUState *cpu) { g_assert_not_reached(); diff --git a/gdbstub/system.c b/gdbstub/system.c index 8ce79fa88c..7f047a285c 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -22,6 +22,7 @@ #include "system/cpus.h" #include "system/runstate.h" #include "system/replay.h" +#include "system/tcg.h" #include "hw/core/cpu.h" #include "hw/cpu/cluster.h" #include "hw/boards.h" @@ -171,7 +172,9 @@ static void gdb_vm_state_change(void *opaque, bool running, RunState state) } else { trace_gdbstub_hit_break(); } - tb_flush(cpu); + if (tcg_enabled()) { + tb_flush(cpu); + } ret = GDB_SIGNAL_TRAP; break; case RUN_STATE_PAUSED: From 0e86d7a71e8b5af201f2066a56071d3e23f4693c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:38:45 +0100 Subject: [PATCH 2243/2892] cpus: Cache CPUClass early in instance_init() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cache CPUClass as early as possible, when the instance is initialized. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-5-philmd@linaro.org> --- cpu-target.c | 3 --- hw/core/cpu-common.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 667688332c..89874496a4 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -134,9 +134,6 @@ const VMStateDescription vmstate_cpu_common = { bool cpu_exec_realizefn(CPUState *cpu, Error **errp) { - /* cache the cpu class for the hotpath */ - cpu->cc = CPU_GET_CLASS(cpu); - if (!accel_cpu_common_realize(cpu, errp)) { return false; } diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index cb79566cc5..ff605059c1 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -238,6 +238,9 @@ static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); + /* cache the cpu class for the hotpath */ + cpu->cc = CPU_GET_CLASS(cpu); + gdb_init_cpu(cpu); cpu->cpu_index = UNASSIGNED_CPU_INDEX; cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; From 6042c47cddae04d0c1f0c750968f66a553611f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 18:45:41 +0100 Subject: [PATCH 2244/2892] cpus: Keep default fields initialization in cpu_common_initfn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_common_initfn() is our target agnostic initializer, while cpu_exec_initfn() is the target specific one. The %as and %num_ases fields are not target specific, so initialize them in the common helper. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-6-philmd@linaro.org> --- cpu-target.c | 3 --- hw/core/cpu-common.c | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 89874496a4..75501a909d 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -234,9 +234,6 @@ void cpu_class_init_props(DeviceClass *dc) void cpu_exec_initfn(CPUState *cpu) { - cpu->as = NULL; - cpu->num_ases = 0; - #ifndef CONFIG_USER_ONLY cpu->memory = get_system_memory(); object_ref(OBJECT(cpu->memory)); diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index ff605059c1..71425cb742 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -244,6 +244,8 @@ static void cpu_common_initfn(Object *obj) gdb_init_cpu(cpu); cpu->cpu_index = UNASSIGNED_CPU_INDEX; cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; + cpu->as = NULL; + cpu->num_ases = 0; /* user-mode doesn't have configurable SMP topology */ /* the default value is changed by qemu_init_vcpu() for system-mode */ cpu->nr_threads = 1; From e92a883ffee93a8b811992b92b8a31ed54400c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 28 Jan 2020 18:28:59 +0100 Subject: [PATCH 2245/2892] accel/accel: Make TYPE_ACCEL abstract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no generic acceleration, we have to use specific implementations. Make the base class abstract. Fixes: b14a0b7469fa ("accel: Use QOM classes for accel types") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cornelia Huck Message-Id: <20200129212345.20547-3-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- accel/accel-target.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/accel-target.c b/accel/accel-target.c index 08626c00c2..3236d6335b 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -38,6 +38,7 @@ static const TypeInfo accel_type = { .parent = TYPE_OBJECT, .class_size = sizeof(AccelClass), .instance_size = sizeof(AccelState), + .abstract = true, }; /* Lookup AccelClass from opt_name. Returns NULL if not found */ From de5a43192b55b5744ed7ec0e263d4d208c834617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 29 Nov 2023 16:55:37 +0100 Subject: [PATCH 2246/2892] accel/tcg: Remove pointless initialization of cflags_next_tb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cflags_next_tb is always re-initialized in the CPU Reset() handler in cpu_common_reset_hold(), no need to initialize it in cpu_common_initfn(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-13-philmd@linaro.org> --- hw/core/cpu-common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 71425cb742..d5cd227fe6 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -249,7 +249,6 @@ static void cpu_common_initfn(Object *obj) /* user-mode doesn't have configurable SMP topology */ /* the default value is changed by qemu_init_vcpu() for system-mode */ cpu->nr_threads = 1; - cpu->cflags_next_tb = -1; /* allocate storage for thread info, initialise condition variables */ cpu->thread = g_new0(QemuThread, 1); From b28378850444547f8a8d64fe8d81e46d1e0bfeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 16:19:49 +0100 Subject: [PATCH 2247/2892] accel/tcg: Build tcg_flags helpers as common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While cpu-exec.c is build for each target,tcg_flags helpers aren't target specific. Move them to cpu-exec-common.c to build them once. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-8-philmd@linaro.org> --- accel/tcg/cpu-exec-common.c | 33 +++++++++++++++++++++++++++++++++ accel/tcg/cpu-exec.c | 32 -------------------------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index 6ecfc4e7c2..100746d555 100644 --- a/accel/tcg/cpu-exec-common.c +++ b/accel/tcg/cpu-exec-common.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "exec/log.h" #include "system/cpus.h" #include "system/tcg.h" #include "qemu/plugin.h" @@ -25,6 +26,38 @@ bool tcg_allowed; +bool tcg_cflags_has(CPUState *cpu, uint32_t flags) +{ + return cpu->tcg_cflags & flags; +} + +void tcg_cflags_set(CPUState *cpu, uint32_t flags) +{ + cpu->tcg_cflags |= flags; +} + +uint32_t curr_cflags(CPUState *cpu) +{ + uint32_t cflags = cpu->tcg_cflags; + + /* + * Record gdb single-step. We should be exiting the TB by raising + * EXCP_DEBUG, but to simplify other tests, disable chaining too. + * + * For singlestep and -d nochain, suppress goto_tb so that + * we can log -d cpu,exec after every TB. + */ + if (unlikely(cpu->singlestep_enabled)) { + cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1; + } else if (qatomic_read(&one_insn_per_tb)) { + cflags |= CF_NO_GOTO_TB | 1; + } else if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { + cflags |= CF_NO_GOTO_TB; + } + + return cflags; +} + /* exit the current TB, but without causing any exception to be raised */ void cpu_loop_exit_noexc(CPUState *cpu) { diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8b773d8847..be2ba199d3 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -148,38 +148,6 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ -bool tcg_cflags_has(CPUState *cpu, uint32_t flags) -{ - return cpu->tcg_cflags & flags; -} - -void tcg_cflags_set(CPUState *cpu, uint32_t flags) -{ - cpu->tcg_cflags |= flags; -} - -uint32_t curr_cflags(CPUState *cpu) -{ - uint32_t cflags = cpu->tcg_cflags; - - /* - * Record gdb single-step. We should be exiting the TB by raising - * EXCP_DEBUG, but to simplify other tests, disable chaining too. - * - * For singlestep and -d nochain, suppress goto_tb so that - * we can log -d cpu,exec after every TB. - */ - if (unlikely(cpu->singlestep_enabled)) { - cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1; - } else if (qatomic_read(&one_insn_per_tb)) { - cflags |= CF_NO_GOTO_TB | 1; - } else if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { - cflags |= CF_NO_GOTO_TB; - } - - return cflags; -} - struct tb_desc { vaddr pc; uint64_t cs_base; From cbaae5338b62a6e005e9a97f419b968e04a7004f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 18:39:31 +0100 Subject: [PATCH 2248/2892] accel/tcg: Restrict tlb_init() / destroy() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to accel/tcg/ scope, in "internal-common.h". Suggested-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-9-philmd@linaro.org> --- accel/tcg/internal-common.h | 11 +++++++++++ accel/tcg/user-exec-stub.c | 11 +++++++++++ include/exec/exec-all.h | 16 ---------------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index c8d714256c..d318672183 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -53,6 +53,17 @@ TranslationBlock *tb_link_page(TranslationBlock *tb); void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, uintptr_t host_pc); +/** + * tlb_init - initialize a CPU's TLB + * @cpu: CPU whose TLB should be initialized + */ +void tlb_init(CPUState *cpu); +/** + * tlb_destroy - destroy a CPU's TLB + * @cpu: CPU whose TLB should be destroyed + */ +void tlb_destroy(CPUState *cpu); + bool tcg_exec_realizefn(CPUState *cpu, Error **errp); void tcg_exec_unrealizefn(CPUState *cpu); diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index 4fbe2dbdc8..1d52f48226 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -1,6 +1,7 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" #include "exec/replay-core.h" +#include "internal-common.h" void cpu_resume(CPUState *cpu) { @@ -18,6 +19,16 @@ void cpu_exec_reset_hold(CPUState *cpu) { } +/* User mode emulation does not support softmmu yet. */ + +void tlb_init(CPUState *cpu) +{ +} + +void tlb_destroy(CPUState *cpu) +{ +} + /* User mode emulation does not support record/replay yet. */ bool replay_exception(void) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index d9045c9ac4..8eb0df48f9 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -29,16 +29,6 @@ #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) /* cputlb.c */ -/** - * tlb_init - initialize a CPU's TLB - * @cpu: CPU whose TLB should be initialized - */ -void tlb_init(CPUState *cpu); -/** - * tlb_destroy - destroy a CPU's TLB - * @cpu: CPU whose TLB should be destroyed - */ -void tlb_destroy(CPUState *cpu); /** * tlb_flush_page: * @cpu: CPU whose TLB should be flushed @@ -223,12 +213,6 @@ void tlb_set_page(CPUState *cpu, vaddr addr, hwaddr paddr, int prot, int mmu_idx, vaddr size); #else -static inline void tlb_init(CPUState *cpu) -{ -} -static inline void tlb_destroy(CPUState *cpu) -{ -} static inline void tlb_flush_page(CPUState *cpu, vaddr addr) { } From fb26a3fd0e7b80c2b5bf6b90a36d5214153d0c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 13:57:20 +0100 Subject: [PATCH 2249/2892] accel/tcg: Restrict 'icount_align_option' global to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 740b1759734 ("cpu-timers, icount: new modules") we don't need to expose icount_align_option to all the system code, we can restrict it to TCG. Since it is used as a boolean, declare it as 'bool' type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-10-philmd@linaro.org> --- accel/tcg/icount-common.c | 2 ++ accel/tcg/internal-common.h | 2 ++ include/system/cpus.h | 2 -- system/globals.c | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/accel/tcg/icount-common.c b/accel/tcg/icount-common.c index b178dccec4..402d3e3f4e 100644 --- a/accel/tcg/icount-common.c +++ b/accel/tcg/icount-common.c @@ -48,6 +48,8 @@ static bool icount_sleep = true; /* Arbitrarily pick 1MIPS as the minimum allowable speed. */ #define MAX_ICOUNT_SHIFT 10 +bool icount_align_option; + /* Do not count executed instructions */ ICountMode use_icount = ICOUNT_DISABLED; diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index d318672183..7ef620d963 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -17,6 +17,8 @@ extern int64_t max_advance; extern bool one_insn_per_tb; +extern bool icount_align_option; + /* * Return true if CS is not running in parallel with other cpus, either * because there are no other cpus or we are within an exclusive context. diff --git a/include/system/cpus.h b/include/system/cpus.h index 3d8fd368f3..1cffeaaf5c 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -38,8 +38,6 @@ void resume_all_vcpus(void); void pause_all_vcpus(void); void cpu_stop_current(void); -extern int icount_align_option; - /* Unblock cpu */ void qemu_cpu_kick_self(void); diff --git a/system/globals.c b/system/globals.c index 316623bd20..9640c9511e 100644 --- a/system/globals.c +++ b/system/globals.c @@ -58,7 +58,6 @@ unsigned int nb_prom_envs; const char *prom_envs[MAX_PROM_ENVS]; uint8_t *boot_splash_filedata; int only_migratable; /* turn it off unless user states otherwise */ -int icount_align_option; /* The bytes in qemu_uuid are in the order specified by RFC4122, _not_ in the * little-endian "wire format" described in the SMBIOS 2.6 specification. From 1501743654692ae6acf98ed8ec162b256eb54a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 24 Jan 2025 00:03:40 +0100 Subject: [PATCH 2250/2892] accel/tcg: Rename 'hw/core/tcg-cpu-ops.h' -> 'accel/tcg/cpu-ops.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCGCPUOps structure makes more sense in the accelerator context rather than hardware emulation. Move it under the accel/tcg/ scope. Mechanical change doing: $ sed -i -e 's,hw/core/tcg-cpu-ops.h,accel/tcg/cpu-ops.h,g' \ $(git grep -l hw/core/tcg-cpu-ops.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-11-philmd@linaro.org> --- MAINTAINERS | 2 +- accel/tcg/cpu-exec.c | 2 +- accel/tcg/cputlb.c | 2 +- accel/tcg/translate-all.c | 2 +- accel/tcg/user-exec.c | 2 +- accel/tcg/watchpoint.c | 2 +- bsd-user/signal.c | 2 +- hw/mips/jazz.c | 2 +- include/{hw/core/tcg-cpu-ops.h => accel/tcg/cpu-ops.h} | 0 linux-user/signal.c | 2 +- system/physmem.c | 2 +- target/alpha/cpu.c | 2 +- target/arm/cpu.c | 2 +- target/arm/tcg/cpu-v7m.c | 2 +- target/arm/tcg/cpu32.c | 2 +- target/arm/tcg/mte_helper.c | 2 +- target/arm/tcg/sve_helper.c | 2 +- target/avr/cpu.c | 2 +- target/avr/helper.c | 2 +- target/hexagon/cpu.c | 2 +- target/hppa/cpu.c | 2 +- target/i386/tcg/tcg-cpu.c | 2 +- target/loongarch/cpu.c | 2 +- target/m68k/cpu.c | 2 +- target/microblaze/cpu.c | 2 +- target/mips/cpu.c | 2 +- target/openrisc/cpu.c | 2 +- target/ppc/cpu_init.c | 2 +- target/riscv/cpu_helper.c | 2 +- target/riscv/tcg/tcg-cpu.c | 2 +- target/rx/cpu.c | 2 +- target/s390x/cpu.c | 2 +- target/s390x/tcg/mem_helper.c | 2 +- target/sh4/cpu.c | 2 +- target/sparc/cpu.c | 2 +- target/tricore/cpu.c | 2 +- target/xtensa/cpu.c | 2 +- 37 files changed, 36 insertions(+), 36 deletions(-) rename include/{hw/core/tcg-cpu-ops.h => accel/tcg/cpu-ops.h} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 692628cd78..2d9ba81085 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -175,7 +175,7 @@ F: include/exec/helper-info.c.inc F: include/exec/page-protection.h F: include/system/cpus.h F: include/system/tcg.h -F: include/hw/core/tcg-cpu-ops.h +F: include/accel/tcg/cpu-ops.h F: host/include/*/host/cpuinfo.h F: util/cpuinfo-*.c F: include/tcg/ diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index be2ba199d3..3a3c45f52e 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "qapi/type-helpers.h" #include "hw/core/cpu.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "trace.h" #include "disas/disas.h" #include "exec/cpu-common.h" diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index ad158050a1..c8761683a0 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/memory.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index d4189c7386..786e2f6f1a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -58,7 +58,7 @@ #include "system/cpu-timers.h" #include "system/tcg.h" #include "qapi/error.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 0561c4f6dc..c4454100ad 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -17,7 +17,7 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg.h" diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index af57d182d5..40112b2b2e 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -26,7 +26,7 @@ #include "tb-internal.h" #include "system/tcg.h" #include "system/replay.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "hw/core/cpu.h" #include "internal-common.h" diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ff2ccbbf60..ab1d9ddd50 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -29,7 +29,7 @@ #include "gdbstub/user.h" #include "signal-common.h" #include "trace.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "host-signal.h" /* target_siginfo_t must fit in gdbstub's siginfo save area. */ diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index c89610639a..1700c3765d 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -50,7 +50,7 @@ #include "qemu/error-report.h" #include "qemu/help_option.h" #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #endif /* CONFIG_TCG */ #include "cpu.h" diff --git a/include/hw/core/tcg-cpu-ops.h b/include/accel/tcg/cpu-ops.h similarity index 100% rename from include/hw/core/tcg-cpu-ops.h rename to include/accel/tcg/cpu-ops.h diff --git a/linux-user/signal.c b/linux-user/signal.c index 81a98c6d02..4799b79ded 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -21,7 +21,7 @@ #include "qemu/cutils.h" #include "gdbstub/user.h" #include "exec/page-protection.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include #include diff --git a/system/physmem.c b/system/physmem.c index eff8b55c2d..8c1736f84e 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -28,7 +28,7 @@ #include "qemu/lockable.h" #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #endif /* CONFIG_TCG */ #include "exec/exec-all.h" diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index f5dd744987..57e41fcd78 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -227,7 +227,7 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 656070afb5..ac1ceec211 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -29,7 +29,7 @@ #include "cpu.h" #ifdef CONFIG_TCG #include "exec/translation-block.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #endif /* CONFIG_TCG */ #include "internals.h" #include "cpu-features.h" diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 03acdf83e0..29a41fde69 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "internals.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 0f1c5bc87e..2c45b7eddd 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "internals.h" #include "target/arm/idau.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index f72ce2ae0d..5d6d8a17ae 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -31,7 +31,7 @@ #endif #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "qapi/error.h" #include "qemu/guest-random.h" #include "mte_helper.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index c206ca65ce..d786b4b111 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -28,7 +28,7 @@ #include "tcg/tcg.h" #include "vec_internal.h" #include "sve_ldst_internal.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" #endif diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 8a126ff322..5a0e21465e 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -203,7 +203,7 @@ static const struct SysemuCPUOps avr_sysemu_ops = { .get_phys_page_debug = avr_cpu_get_phys_page_debug, }; -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps avr_tcg_ops = { .initialize = avr_cpu_tcg_init, diff --git a/target/avr/helper.c b/target/avr/helper.c index 345708a1b3..9ea6870e44 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -22,7 +22,7 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "cpu.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 0b7fc98f6c..238e63bcea 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -321,7 +321,7 @@ static void hexagon_cpu_init(Object *obj) { } -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps hexagon_tcg_ops = { .initialize = hexagon_translate_init, diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 5655677431..4bb5cff624 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -245,7 +245,7 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps hppa_tcg_ops = { .initialize = hppa_translate_init, diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 14ee038079..f09ee813ac 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -105,7 +105,7 @@ static bool x86_debug_check_breakpoint(CPUState *cs) } #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps x86_tcg_ops = { .initialize = tcg_x86_init, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ac514a15fb..b4b82425b1 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -857,7 +857,7 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) } #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps loongarch_tcg_ops = { .initialize = loongarch_translate_init, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 2617d8f6ed..eedda07c2a 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -582,7 +582,7 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { }; #endif /* !CONFIG_USER_ONLY */ -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index f114789abd..13d194cef8 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -419,7 +419,7 @@ static const struct SysemuCPUOps mb_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps mb_tcg_ops = { .initialize = mb_tcg_init, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 47cd7cfdce..0b267d2e50 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -544,7 +544,7 @@ static const Property mips_cpu_properties[] = { }; #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, .translate_code = mips_translate_code, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index b7bab0d7ab..0669ba2fd1 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -232,7 +232,7 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps openrisc_tcg_ops = { .initialize = openrisc_translate_init, diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 062a6e85fb..425049ab09 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7430,7 +7430,7 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { #endif #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps ppc_tcg_ops = { .initialize = ppc_translate_init, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 3f5fd861a8..34092f372d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -27,7 +27,7 @@ #include "exec/page-protection.h" #include "instmap.h" #include "tcg/tcg-op.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "trace.h" #include "semihosting/common-semi.h" #include "system/cpu-timers.h" diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index f1d971eec1..70f4c7984a 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -31,7 +31,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "hw/core/accel-cpu.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" #ifndef CONFIG_USER_ONLY #include "hw/boards.h" diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 37a6fdd569..7d5fcbf76a 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -200,7 +200,7 @@ static const struct SysemuCPUOps rx_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps rx_tcg_ops = { .initialize = rx_translate_init, diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 97d41c23de..3bea014f9e 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -322,7 +322,7 @@ static const Property s390x_cpu_properties[] = { #endif #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, uint64_t *cs_base, uint32_t *pflags) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index c6ab2901e5..ea9fa64d6b 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -28,7 +28,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "qemu/int128.h" #include "qemu/atomic128.h" diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index ccfe222bdf..22cdf9b4e1 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -256,7 +256,7 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps superh_tcg_ops = { .initialize = sh4_translate_init, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index fbd38ec334..e3b4613717 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -992,7 +992,7 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { #endif #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps sparc_tcg_ops = { .initialize = sparc_tcg_init, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 95202fadbf..eb794674c8 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -168,7 +168,7 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { .get_phys_page_debug = tricore_cpu_get_phys_page_debug, }; -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps tricore_tcg_ops = { .initialize = tricore_tcg_init, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 4eb699d1f4..efbfe73fcf 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -228,7 +228,7 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" static const TCGCPUOps xtensa_tcg_ops = { .initialize = xtensa_translate_init, From b12a0f856691264bc1a8f0ed1e5e62649cea7fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 11:11:24 +0100 Subject: [PATCH 2251/2892] accel: Rename 'hw/core/accel-cpu.h' -> 'accel/accel-cpu-target.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AccelCPUClass is for accelerator to initialize target specific features of a vCPU. Not really related to hardware emulation, rename "hw/core/accel-cpu.h" as "accel/accel-cpu-target.h" (using the explicit -target suffix). More importantly, target specific header often access the target specific definitions which are in each target/FOO/cpu.h header, usually included generically as "cpu.h" relative to target/FOO/. However, there is already a "cpu.h" in hw/core/ which takes precedence. This change allows "accel-cpu-target.h" to include a target "cpu.h". Mechanical change doing: $ git mv include/hw/core/accel-cpu.h \ include/accel/accel-cpu-target.h $ sed -i -e 's,hw/core/accel-cpu.h,accel/accel-cpu-target.h,' \ $(git grep -l hw/core/accel-cpu.h) and renaming header guard 'ACCEL_CPU_TARGET_H'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-12-philmd@linaro.org> --- MAINTAINERS | 2 +- accel/accel-target.c | 2 +- cpu-target.c | 2 +- include/{hw/core/accel-cpu.h => accel/accel-cpu-target.h} | 4 ++-- target/i386/hvf/hvf-cpu.c | 2 +- target/i386/kvm/kvm-cpu.c | 2 +- target/i386/tcg/tcg-cpu.c | 2 +- target/ppc/kvm.c | 2 +- target/riscv/kvm/kvm-cpu.c | 2 +- target/riscv/tcg/tcg-cpu.c | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) rename include/{hw/core/accel-cpu.h => accel/accel-cpu-target.h} (95%) diff --git a/MAINTAINERS b/MAINTAINERS index 2d9ba81085..a0e462d03c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -499,7 +499,7 @@ R: Paolo Bonzini S: Maintained F: include/qemu/accel.h F: include/system/accel-*.h -F: include/hw/core/accel-cpu.h +F: include/accel/accel-cpu-target.h F: accel/accel-*.c F: accel/Makefile.objs F: accel/stubs/Makefile.objs diff --git a/accel/accel-target.c b/accel/accel-target.c index 3236d6335b..8358727462 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -27,7 +27,7 @@ #include "qemu/accel.h" #include "cpu.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #ifndef CONFIG_USER_ONLY #include "accel-system.h" diff --git a/cpu-target.c b/cpu-target.c index 75501a909d..f97f3a1475 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -44,7 +44,7 @@ #include "exec/tb-flush.h" #include "exec/translation-block.h" #include "exec/log.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #include "trace/trace-root.h" #include "qemu/accel.h" diff --git a/include/hw/core/accel-cpu.h b/include/accel/accel-cpu-target.h similarity index 95% rename from include/hw/core/accel-cpu.h rename to include/accel/accel-cpu-target.h index 24dad45ab9..0a8e518600 100644 --- a/include/hw/core/accel-cpu.h +++ b/include/accel/accel-cpu-target.h @@ -8,8 +8,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef ACCEL_CPU_H -#define ACCEL_CPU_H +#ifndef ACCEL_CPU_TARGET_H +#define ACCEL_CPU_TARGET_H /* * This header is used to define new accelerator-specific target-specific diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index 560b5a0594..b5f4c80028 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -14,7 +14,7 @@ #include "system/system.h" #include "hw/boards.h" #include "system/hvf.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #include "hvf-i386.h" static void hvf_cpu_max_instance_init(X86CPU *cpu) diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 1bda403f88..6269fa8045 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -15,7 +15,7 @@ #include "hw/boards.h" #include "kvm_i386.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" static void kvm_set_guest_phys_bits(CPUState *cs) { diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index f09ee813ac..b8aff825ee 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "helper-tcg.h" #include "qemu/accel.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #include "exec/translation-block.h" #include "tcg-cpu.h" diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 966c2c6572..216638dee4 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -49,7 +49,7 @@ #include "elf.h" #include "system/kvm_int.h" #include "system/kvm.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #include CONFIG_DEVICES diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 7f3b59cb72..4ffeeaa1c9 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -32,7 +32,7 @@ #include "system/kvm_int.h" #include "cpu.h" #include "trace.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 70f4c7984a..5aef9eef36 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -30,7 +30,7 @@ #include "qemu/accel.h" #include "qemu/error-report.h" #include "qemu/log.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.h" #include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" #ifndef CONFIG_USER_ONLY From 0f66536a012b2d1b02818bbb2d24485205fc2f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 13:39:05 +0100 Subject: [PATCH 2252/2892] accel: Forward-declare AccelOpsClass in 'qemu/typedefs.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The heavily imported "system/cpus.h" header includes "accel-ops.h" to get AccelOpsClass type declaration. Reduce headers pressure by forward declaring it in "qemu/typedefs.h", where we already declare the AccelCPUState type. Reduce "system/cpus.h" inclusions by only including "system/accel-ops.h" when necessary. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-14-philmd@linaro.org> --- accel/accel-system.c | 1 + accel/hvf/hvf-accel-ops.c | 1 + accel/kvm/kvm-accel-ops.c | 1 + accel/qtest/qtest.c | 1 + accel/tcg/cpu-exec-common.c | 1 - accel/tcg/cpu-exec.c | 1 - accel/tcg/monitor.c | 1 - accel/tcg/tcg-accel-ops.c | 1 + accel/tcg/translate-all.c | 1 - accel/xen/xen-all.c | 1 + cpu-common.c | 1 - cpu-target.c | 1 + gdbstub/system.c | 1 + include/qemu/typedefs.h | 1 + include/system/accel-ops.h | 1 - include/system/cpus.h | 2 -- system/cpus.c | 1 + target/i386/nvmm/nvmm-accel-ops.c | 1 + target/i386/whpx/whpx-accel-ops.c | 1 + 19 files changed, 12 insertions(+), 8 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index a7596aef59..5df49fbe83 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "hw/boards.h" +#include "system/accel-ops.h" #include "system/cpus.h" #include "qemu/error-report.h" #include "accel-system.h" diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 945ba72051..12fc30c276 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -54,6 +54,7 @@ #include "exec/exec-all.h" #include "gdbstub/enums.h" #include "hw/boards.h" +#include "system/accel-ops.h" #include "system/cpus.h" #include "system/hvf.h" #include "system/hvf_int.h" diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index a81e8f3b03..54ea60909e 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -16,6 +16,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "system/accel-ops.h" #include "system/kvm.h" #include "system/kvm_int.h" #include "system/runstate.h" diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index ad7e3441a5..7fae80f6a1 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -18,6 +18,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/accel.h" +#include "system/accel-ops.h" #include "system/qtest.h" #include "system/cpus.h" #include "qemu/guest-random.h" diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index 100746d555..c5c513f1e4 100644 --- a/accel/tcg/cpu-exec-common.c +++ b/accel/tcg/cpu-exec-common.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "exec/log.h" -#include "system/cpus.h" #include "system/tcg.h" #include "qemu/plugin.h" #include "internal-common.h" diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 3a3c45f52e..ef3d967e3a 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -33,7 +33,6 @@ #include "qemu/rcu.h" #include "exec/log.h" #include "qemu/main-loop.h" -#include "system/cpus.h" #include "exec/cpu-all.h" #include "system/cpu-timers.h" #include "exec/replay-core.h" diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index ae1dbeb79f..eeb38a4d9c 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -13,7 +13,6 @@ #include "qapi/type-helpers.h" #include "qapi/qapi-commands-machine.h" #include "monitor/monitor.h" -#include "system/cpus.h" #include "system/cpu-timers.h" #include "system/tcg.h" #include "tcg/tcg.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 6e3f1fa92b..132c5d1461 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "system/accel-ops.h" #include "system/tcg.h" #include "system/replay.h" #include "system/cpu-timers.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 786e2f6f1a..0914d6e98b 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -54,7 +54,6 @@ #include "qemu/cacheinfo.h" #include "qemu/timer.h" #include "exec/log.h" -#include "system/cpus.h" #include "system/cpu-timers.h" #include "system/tcg.h" #include "qapi/error.h" diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 852e9fbe5f..7aa28b9ab9 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -18,6 +18,7 @@ #include "hw/xen/xen_igd.h" #include "chardev/char.h" #include "qemu/accel.h" +#include "system/accel-ops.h" #include "system/cpus.h" #include "system/xen.h" #include "system/runstate.h" diff --git a/cpu-common.c b/cpu-common.c index 4248b2d727..f5dcc2d136 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -21,7 +21,6 @@ #include "qemu/main-loop.h" #include "exec/cpu-common.h" #include "hw/core/cpu.h" -#include "system/cpus.h" #include "qemu/lockable.h" #include "trace/trace-root.h" diff --git a/cpu-target.c b/cpu-target.c index f97f3a1475..20933bde7d 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -35,6 +35,7 @@ #include "exec/address-spaces.h" #include "exec/memory.h" #endif +#include "system/accel-ops.h" #include "system/cpus.h" #include "system/tcg.h" #include "exec/tswap.h" diff --git a/gdbstub/system.c b/gdbstub/system.c index 7f047a285c..416c1dbe1e 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -19,6 +19,7 @@ #include "gdbstub/commands.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" +#include "system/accel-ops.h" #include "system/cpus.h" #include "system/runstate.h" #include "system/replay.h" diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 3d84efcac4..465cc50177 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -22,6 +22,7 @@ * Please keep this list in case-insensitive alphabetical order. */ typedef struct AccelCPUState AccelCPUState; +typedef struct AccelOpsClass AccelOpsClass; typedef struct AccelState AccelState; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index 137fb96d44..4c99d25aef 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -17,7 +17,6 @@ #define TYPE_ACCEL_OPS "accel" ACCEL_OPS_SUFFIX #define ACCEL_OPS_NAME(name) (name "-" TYPE_ACCEL_OPS) -typedef struct AccelOpsClass AccelOpsClass; DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS) /** diff --git a/include/system/cpus.h b/include/system/cpus.h index 1cffeaaf5c..3226c765d0 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -1,8 +1,6 @@ #ifndef QEMU_CPUS_H #define QEMU_CPUS_H -#include "system/accel-ops.h" - /* register accel-specific operations */ void cpus_register_accel(const AccelOpsClass *i); diff --git a/system/cpus.c b/system/cpus.c index 37e5892c24..2cc5f887ab 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -31,6 +31,7 @@ #include "qapi/qapi-events-run-state.h" #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" +#include "system/accel-ops.h" #include "system/hw_accel.h" #include "exec/cpu-common.h" #include "qemu/thread.h" diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index e7b56662fe..4e4e63de78 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "system/kvm_int.h" #include "qemu/main-loop.h" +#include "system/accel-ops.h" #include "system/cpus.h" #include "qemu/guest-random.h" diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index ab2e014c9e..81fdd06e48 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "system/kvm_int.h" #include "qemu/main-loop.h" +#include "system/accel-ops.h" #include "system/cpus.h" #include "qemu/guest-random.h" From 217e72024c12c95e7b5f74fde74206951e706b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 10:37:05 +0100 Subject: [PATCH 2253/2892] accel/accel-cpu-target.h: Include missing 'cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU_RESOLVING_TYPE is declared per target in "cpu.h". Include it (along with "qom/object.h") to avoid when moving code around: include/accel/accel-cpu-target.h:26:50: error: expected ')' 26 | DECLARE_CLASS_CHECKERS(AccelCPUClass, ACCEL_CPU, TYPE_ACCEL_CPU) | ^ include/accel/accel-cpu-target.h:23:33: note: expanded from macro 'TYPE_ACCEL_CPU' 23 | #define TYPE_ACCEL_CPU "accel-" CPU_RESOLVING_TYPE | ^ include/accel/accel-cpu-target.h:26:1: note: to match this '(' 26 | DECLARE_CLASS_CHECKERS(AccelCPUClass, ACCEL_CPU, TYPE_ACCEL_CPU) | ^ include/qom/object.h:196:14: note: expanded from macro 'DECLARE_CLASS_CHECKERS' 196 | { return OBJECT_GET_CLASS(ClassType, obj, TYPENAME); } \ | ^ include/qom/object.h:558:5: note: expanded from macro 'OBJECT_GET_CLASS' 558 | OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name) | ^ include/qom/object.h:544:74: note: expanded from macro 'OBJECT_CLASS_CHECK' 544 | ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \ | ^ Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250123234415.59850-13-philmd@linaro.org> --- include/accel/accel-cpu-target.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/accel/accel-cpu-target.h b/include/accel/accel-cpu-target.h index 0a8e518600..37dde7fae3 100644 --- a/include/accel/accel-cpu-target.h +++ b/include/accel/accel-cpu-target.h @@ -20,6 +20,9 @@ * subclasses in target/, or the accel implementation itself in accel/ */ +#include "qom/object.h" +#include "cpu.h" + #define TYPE_ACCEL_CPU "accel-" CPU_RESOLVING_TYPE #define ACCEL_CPU_NAME(name) (name "-" TYPE_ACCEL_CPU) typedef struct AccelCPUClass AccelCPUClass; From a523b62c8573c09d96472e06486bb58740945215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Feb 2025 12:21:42 +0100 Subject: [PATCH 2254/2892] accel/tcg: Include missing bswap headers in user-exec.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 35c653c4029 ("tcg: Add 128-bit guest memory primitives") introduced the use of bswap128() which is declared in "qemu/int128.h", commit de95016dfbf ("accel/tcg: Implement helper_{ld,st}*_mmu for user-only") introduced the other bswap*() uses, which are declared in "qemu/bswap.h". Include the missing headers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250217130610.18313-3-philmd@linaro.org> --- accel/tcg/user-exec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index c4454100ad..9d53c9440e 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -30,6 +30,8 @@ #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" +#include "qemu/bswap.h" +#include "qemu/int128.h" #include "trace.h" #include "tcg/tcg-ldst.h" #include "internal-common.h" From 964a4f2c2972ec9c7574b87541d6070ef5d22f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Feb 2025 11:53:47 +0100 Subject: [PATCH 2255/2892] accel/tcg: Take mmap lock in the whole cpu_memory_rw_debug() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify user implementation of cpu_memory_rw_debug() by taking the mmap lock globally. See commit 87ab2704296 ("linux-user: Allow gdbstub to ignore page protection") for why this lock is necessary. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250217130610.18313-4-philmd@linaro.org> --- cpu-target.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 20933bde7d..b5230ce183 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -380,6 +380,8 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, int ret = -1; int fd = -1; + mmap_lock(); + while (len > 0) { page = addr & TARGET_PAGE_MASK; l = (page + TARGET_PAGE_SIZE) - addr; @@ -414,11 +416,9 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, * be under mmap_lock() in order to prevent the creation of * another TranslationBlock in between. */ - mmap_lock(); tb_invalidate_phys_range(addr, addr + l - 1); written = pwrite(fd, buf, l, (off_t)(uintptr_t)g2h_untagged(addr)); - mmap_unlock(); if (written != l) { goto out_close; } @@ -454,6 +454,8 @@ out_close: close(fd); } out: + mmap_unlock(); + return ret; } #endif From eacd1f8445fd033c3ce927e543be2818d0564130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Feb 2025 12:01:09 +0100 Subject: [PATCH 2256/2892] accel/tcg: Avoid using lock_user() in cpu_memory_rw_debug() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We checked the page flags with page_get_flags(), so locking the page is superfluous. Remove the lock_user() calls and directly use g2h() in place. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250217130610.18313-5-philmd@linaro.org> --- cpu-target.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index b5230ce183..3892ce1222 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -374,7 +374,6 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, { int flags; vaddr l, page; - void * p; uint8_t *buf = ptr; ssize_t written; int ret = -1; @@ -393,13 +392,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, } if (is_write) { if (flags & PAGE_WRITE) { - /* XXX: this code should not depend on lock_user */ - p = lock_user(VERIFY_WRITE, addr, l, 0); - if (!p) { - goto out_close; - } - memcpy(p, buf, l); - unlock_user(p, addr, l); + memcpy(g2h(cpu, addr), buf, l); } else { /* Bypass the host page protection using ptrace. */ if (fd == -1) { @@ -424,13 +417,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, } } } else if (flags & PAGE_READ) { - /* XXX: this code should not depend on lock_user */ - p = lock_user(VERIFY_READ, addr, l, 1); - if (!p) { - goto out_close; - } - memcpy(buf, p, l); - unlock_user(p, addr, 0); + memcpy(buf, g2h(cpu, addr), l); } else { /* Bypass the host page protection using ptrace. */ if (fd == -1) { From 585d4b122914b7be170eef147a0269cc233f0adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Feb 2025 12:13:16 +0100 Subject: [PATCH 2257/2892] accel/tcg: Move cpu_memory_rw_debug() user implementation to user-exec.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_memory_rw_debug() system implementation is defined in system/physmem.c. Move the user one to accel/tcg/user-exec.c to simplify cpu-target.c maintenance. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250217130610.18313-6-philmd@linaro.org> --- accel/tcg/user-exec.c | 80 ++++++++++++++++++++++++++++++++++++++ cpu-target.c | 90 +------------------------------------------ 2 files changed, 82 insertions(+), 88 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 9d53c9440e..2322181b15 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "accel/tcg/cpu-ops.h" #include "disas/disas.h" +#include "exec/vaddr.h" #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/bitops.h" @@ -971,6 +972,85 @@ static void *cpu_mmu_lookup(CPUState *cpu, vaddr addr, return ret; } +/* physical memory access (slow version, mainly for debug) */ +int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, + void *ptr, size_t len, bool is_write) +{ + int flags; + vaddr l, page; + uint8_t *buf = ptr; + ssize_t written; + int ret = -1; + int fd = -1; + + mmap_lock(); + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) { + l = len; + } + flags = page_get_flags(page); + if (!(flags & PAGE_VALID)) { + goto out_close; + } + if (is_write) { + if (flags & PAGE_WRITE) { + memcpy(g2h(cpu, addr), buf, l); + } else { + /* Bypass the host page protection using ptrace. */ + if (fd == -1) { + fd = open("/proc/self/mem", O_WRONLY); + if (fd == -1) { + goto out; + } + } + /* + * If there is a TranslationBlock and we weren't bypassing the + * host page protection, the memcpy() above would SEGV, + * ultimately leading to page_unprotect(). So invalidate the + * translations manually. Both invalidation and pwrite() must + * be under mmap_lock() in order to prevent the creation of + * another TranslationBlock in between. + */ + tb_invalidate_phys_range(addr, addr + l - 1); + written = pwrite(fd, buf, l, + (off_t)(uintptr_t)g2h_untagged(addr)); + if (written != l) { + goto out_close; + } + } + } else if (flags & PAGE_READ) { + memcpy(buf, g2h(cpu, addr), l); + } else { + /* Bypass the host page protection using ptrace. */ + if (fd == -1) { + fd = open("/proc/self/mem", O_RDONLY); + if (fd == -1) { + goto out; + } + } + if (pread(fd, buf, l, + (off_t)(uintptr_t)g2h_untagged(addr)) != l) { + goto out_close; + } + } + len -= l; + buf += l; + addr += l; + } + ret = 0; +out_close: + if (fd != -1) { + close(fd); + } +out: + mmap_unlock(); + + return ret; +} + #include "ldst_atomicity.c.inc" static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, diff --git a/cpu-target.c b/cpu-target.c index 3892ce1222..83688f1d50 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -19,18 +19,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" - -#include "exec/target_page.h" -#include "exec/page-protection.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "migration/vmstate.h" -#ifdef CONFIG_USER_ONLY -#include "qemu.h" -#include "user/page-protection.h" -#else +#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" #include "exec/address-spaces.h" #include "exec/memory.h" @@ -43,11 +37,11 @@ #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" -#include "exec/translation-block.h" #include "exec/log.h" #include "accel/accel-cpu-target.h" #include "trace/trace-root.h" #include "qemu/accel.h" +#include "hw/core/cpu.h" #ifndef CONFIG_USER_ONLY static int cpu_common_post_load(void *opaque, int version_id) @@ -367,86 +361,6 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) abort(); } -/* physical memory access (slow version, mainly for debug) */ -#if defined(CONFIG_USER_ONLY) -int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, - void *ptr, size_t len, bool is_write) -{ - int flags; - vaddr l, page; - uint8_t *buf = ptr; - ssize_t written; - int ret = -1; - int fd = -1; - - mmap_lock(); - - while (len > 0) { - page = addr & TARGET_PAGE_MASK; - l = (page + TARGET_PAGE_SIZE) - addr; - if (l > len) - l = len; - flags = page_get_flags(page); - if (!(flags & PAGE_VALID)) { - goto out_close; - } - if (is_write) { - if (flags & PAGE_WRITE) { - memcpy(g2h(cpu, addr), buf, l); - } else { - /* Bypass the host page protection using ptrace. */ - if (fd == -1) { - fd = open("/proc/self/mem", O_WRONLY); - if (fd == -1) { - goto out; - } - } - /* - * If there is a TranslationBlock and we weren't bypassing the - * host page protection, the memcpy() above would SEGV, - * ultimately leading to page_unprotect(). So invalidate the - * translations manually. Both invalidation and pwrite() must - * be under mmap_lock() in order to prevent the creation of - * another TranslationBlock in between. - */ - tb_invalidate_phys_range(addr, addr + l - 1); - written = pwrite(fd, buf, l, - (off_t)(uintptr_t)g2h_untagged(addr)); - if (written != l) { - goto out_close; - } - } - } else if (flags & PAGE_READ) { - memcpy(buf, g2h(cpu, addr), l); - } else { - /* Bypass the host page protection using ptrace. */ - if (fd == -1) { - fd = open("/proc/self/mem", O_RDONLY); - if (fd == -1) { - goto out; - } - } - if (pread(fd, buf, l, - (off_t)(uintptr_t)g2h_untagged(addr)) != l) { - goto out_close; - } - } - len -= l; - buf += l; - addr += l; - } - ret = 0; -out_close: - if (fd != -1) { - close(fd); - } -out: - mmap_unlock(); - - return ret; -} -#endif - bool target_words_bigendian(void) { return TARGET_BIG_ENDIAN; From 6eeff37b43d496deadd4cb4697f9f3f5b50d0926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 13:51:14 +0100 Subject: [PATCH 2258/2892] accel/kvm: Remove unused 'system/cpus.h' header in kvm-cpus.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in commit b86f59c7155 ("accel: replace struct CpusAccel with AccelOpsClass") which removed the single CpusAccel use. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-7-philmd@linaro.org> --- accel/kvm/kvm-cpus.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index b5435286e4..688511151c 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -10,8 +10,6 @@ #ifndef KVM_CPUS_H #define KVM_CPUS_H -#include "system/cpus.h" - int kvm_init_vcpu(CPUState *cpu, Error **errp); int kvm_cpu_exec(CPUState *cpu); void kvm_destroy_vcpu(CPUState *cpu); From c90476325cf669d9bba15f4fd8d8637926f272a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 19:55:50 +0100 Subject: [PATCH 2259/2892] cpus: Fix style in cpu-target.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix style on code we are going to modify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-16-philmd@linaro.org> --- cpu-target.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 83688f1d50..b925b9391e 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -48,12 +48,15 @@ static int cpu_common_post_load(void *opaque, int version_id) { CPUState *cpu = opaque; - /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the - version_id is increased. */ + /* + * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + * version_id is increased. + */ cpu->interrupt_request &= ~0x01; tlb_flush(cpu); - /* loadvm has just updated the content of RAM, bypassing the + /* + * loadvm has just updated the content of RAM, bypassing the * usual mechanisms that ensure we flush TBs for writes to * memory we've translated code from. So we must flush all TBs, * which will now be stale. From 530c7139f64aa0e45b61ce9abecb7df8c55b3f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 23:30:04 +0100 Subject: [PATCH 2260/2892] cpus: Restrict cpu_common_post_load() code to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU_INTERRUPT_EXIT was removed in commit 3098dba01c7 ("Use a dedicated function to request exit from execution loop"), tlb_flush() and tb_flush() are related to TCG accelerator. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-17-philmd@linaro.org> --- cpu-target.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index b925b9391e..48446c9021 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -46,22 +46,25 @@ #ifndef CONFIG_USER_ONLY static int cpu_common_post_load(void *opaque, int version_id) { - CPUState *cpu = opaque; + if (tcg_enabled()) { + CPUState *cpu = opaque; - /* - * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the - * version_id is increased. - */ - cpu->interrupt_request &= ~0x01; - tlb_flush(cpu); + /* + * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + * version_id is increased. + */ + cpu->interrupt_request &= ~0x01; - /* - * loadvm has just updated the content of RAM, bypassing the - * usual mechanisms that ensure we flush TBs for writes to - * memory we've translated code from. So we must flush all TBs, - * which will now be stale. - */ - tb_flush(cpu); + tlb_flush(cpu); + + /* + * loadvm has just updated the content of RAM, bypassing the + * usual mechanisms that ensure we flush TBs for writes to + * memory we've translated code from. So we must flush all TBs, + * which will now be stale. + */ + tb_flush(cpu); + } return 0; } From e3a575f5609569400da628d384b32f5e3cf58745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 10:14:17 +0100 Subject: [PATCH 2261/2892] cpus: Have cpu_class_init_props() per user / system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than maintaining a mix of system / user code for CPU class properties, move system properties to cpu-system.c and user ones to the new cpu-user.c unit. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-18-philmd@linaro.org> --- MAINTAINERS | 1 + cpu-target.c | 58 -------------------------------------------- hw/core/cpu-system.c | 40 ++++++++++++++++++++++++++++++ hw/core/cpu-user.c | 27 +++++++++++++++++++++ hw/core/meson.build | 5 +++- 5 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 hw/core/cpu-user.c diff --git a/MAINTAINERS b/MAINTAINERS index a0e462d03c..1d1fadc3bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3791,6 +3791,7 @@ Overall usermode emulation M: Riku Voipio S: Maintained F: accel/tcg/user-exec*.c +F: hw/core/cpu-user.c F: include/user/ F: common-user/ diff --git a/cpu-target.c b/cpu-target.c index 48446c9021..f4c834fd26 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -19,15 +19,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "hw/qdev-core.h" -#include "hw/qdev-properties.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "migration/vmstate.h" #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" #include "exec/address-spaces.h" -#include "exec/memory.h" #endif #include "system/accel-ops.h" #include "system/cpus.h" @@ -178,61 +175,6 @@ void cpu_exec_unrealizefn(CPUState *cpu) accel_cpu_common_unrealize(cpu); } -/* - * This can't go in hw/core/cpu.c because that file is compiled only - * once for both user-mode and system builds. - */ -static const Property cpu_common_props[] = { -#ifdef CONFIG_USER_ONLY - /* - * Create a property for the user-only object, so users can - * adjust prctl(PR_SET_UNALIGN) from the command-line. - * Has no effect if the target does not support the feature. - */ - DEFINE_PROP_BOOL("prctl-unalign-sigbus", CPUState, - prctl_unalign_sigbus, false), -#else - /* - * Create a memory property for system CPU object, so users can - * wire up its memory. The default if no link is set up is to use - * the system address space. - */ - DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, - MemoryRegion *), -#endif -}; - -#ifndef CONFIG_USER_ONLY -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; -} -#endif - -void cpu_class_init_props(DeviceClass *dc) -{ -#ifndef CONFIG_USER_ONLY - ObjectClass *oc = OBJECT_CLASS(dc); - - /* - * 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); -#endif - - device_class_set_props(dc, cpu_common_props); -} - void cpu_exec_initfn(CPUState *cpu) { #ifndef CONFIG_USER_ONLY diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 6e307c8959..1310b4203f 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -20,7 +20,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "exec/memory.h" #include "exec/tswap.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #include "hw/core/sysemu-cpu-ops.h" bool cpu_paging_enabled(const CPUState *cpu) @@ -147,3 +150,40 @@ GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) } return res; } + +static const Property cpu_system_props[] = { + /* + * Create a memory property for system CPU object, so users can + * wire up its memory. The default if no link is set up is to use + * the system address space. + */ + DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), +}; + +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); + + /* + * 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); + + device_class_set_props(dc, cpu_system_props); +} diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c new file mode 100644 index 0000000000..e5ccf6bf13 --- /dev/null +++ b/hw/core/cpu-user.c @@ -0,0 +1,27 @@ +/* + * QEMU CPU model (user specific) + * + * Copyright (c) Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/core/cpu.h" + +static const Property cpu_user_props[] = { + /* + * Create a property for the user-only object, so users can + * adjust prctl(PR_SET_UNALIGN) from the command-line. + * Has no effect if the target does not support the feature. + */ + DEFINE_PROP_BOOL("prctl-unalign-sigbus", CPUState, + prctl_unalign_sigbus, false), +}; + +void cpu_class_init_props(DeviceClass *dc) +{ + device_class_set_props(dc, cpu_user_props); +} diff --git a/hw/core/meson.build b/hw/core/meson.build index 65a1698ed1..b5a545a0ed 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -46,4 +46,7 @@ system_ss.add(files( 'vm-change-state-handler.c', 'clock-vmstate.c', )) -user_ss.add(files('qdev-user.c')) +user_ss.add(files( + 'cpu-user.c', + 'qdev-user.c', +)) From a86cf967a1afe8ccbf58d34983816bc2985d65d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 10:19:44 +0100 Subject: [PATCH 2262/2892] cpus: Have cpu_exec_initfn() per user / system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slighly simplify cpu-target.c again by extracting cpu_exec_initfn() to cpu-{system,user}.c, adding an empty stub for user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-19-philmd@linaro.org> --- cpu-target.c | 9 --------- hw/core/cpu-system.c | 7 +++++++ hw/core/cpu-user.c | 5 +++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index f4c834fd26..5aa6c4b0c6 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -24,7 +24,6 @@ #include "migration/vmstate.h" #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" -#include "exec/address-spaces.h" #endif #include "system/accel-ops.h" #include "system/cpus.h" @@ -175,14 +174,6 @@ void cpu_exec_unrealizefn(CPUState *cpu) accel_cpu_common_unrealize(cpu); } -void cpu_exec_initfn(CPUState *cpu) -{ -#ifndef CONFIG_USER_ONLY - cpu->memory = get_system_memory(); - object_ref(OBJECT(cpu->memory)); -#endif -} - char *cpu_model_from_type(const char *typename) { const char *suffix = "-" CPU_RESOLVING_TYPE; diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 1310b4203f..e511507e13 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "exec/address-spaces.h" #include "exec/memory.h" #include "exec/tswap.h" #include "hw/qdev-core.h" @@ -187,3 +188,9 @@ void cpu_class_init_props(DeviceClass *dc) device_class_set_props(dc, cpu_system_props); } + +void cpu_exec_initfn(CPUState *cpu) +{ + cpu->memory = get_system_memory(); + object_ref(OBJECT(cpu->memory)); +} diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c index e5ccf6bf13..cdd8de2fef 100644 --- a/hw/core/cpu-user.c +++ b/hw/core/cpu-user.c @@ -25,3 +25,8 @@ void cpu_class_init_props(DeviceClass *dc) { device_class_set_props(dc, cpu_user_props); } + +void cpu_exec_initfn(CPUState *cpu) +{ + /* nothing to do */ +} From f821d894de2025611f2b19598fc4191ac4167ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:52:57 +0100 Subject: [PATCH 2263/2892] cpus: Restrict cpu_get_memory_mapping() to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121142341.17001-5-philmd@linaro.org> --- include/hw/core/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 7b6b22c431..9dd6ac7c76 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -616,6 +616,8 @@ extern bool mttcg_enabled; */ bool cpu_paging_enabled(const CPUState *cpu); +#if !defined(CONFIG_USER_ONLY) + /** * cpu_get_memory_mapping: * @cpu: The CPU whose memory mappings are to be obtained. @@ -627,8 +629,6 @@ bool cpu_paging_enabled(const CPUState *cpu); bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, Error **errp); -#if !defined(CONFIG_USER_ONLY) - /** * cpu_write_elf64_note: * @f: pointer to a function that writes memory to a file From 2beb871dc20b57fc4b1ec2285dfc2145baf9df80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:12:21 +0100 Subject: [PATCH 2264/2892] hw/core/generic-loader: Do not open-code cpu_set_pc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Directly call cpu_set_pc() instead of open-coding it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-2-philmd@linaro.org> --- hw/core/generic-loader.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index d9f5c2e832..d3a426a1a2 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -47,11 +47,8 @@ static void generic_loader_reset(void *opaque) GenericLoaderState *s = GENERIC_LOADER(opaque); if (s->set_pc) { - CPUClass *cc = CPU_GET_CLASS(s->cpu); cpu_reset(s->cpu); - if (cc) { - cc->set_pc(s->cpu, s->addr); - } + cpu_set_pc(s->cpu, s->addr); } if (s->data_len) { From 607854ae7c55bb00fbe58e80aa661887cd3eb256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Sep 2024 23:17:05 +0200 Subject: [PATCH 2265/2892] target/microblaze: Explode MO_TExx -> MO_TE | MO_xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the implicit MO_TE definition in order to replace it by runtime variable in the next commit. Mechanical change using: $ for n in UW UL UQ UO SW SL SQ; do \ sed -i -e "s/MO_TE$n/MO_TE | MO_$n/" \ $(git grep -l MO_TE$n target/microblaze); \ done Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20241105130431.22564-14-philmd@linaro.org> --- target/microblaze/translate.c | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 24005f05b2..86efabb83b 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -780,13 +780,13 @@ static bool trans_lbui(DisasContext *dc, arg_typeb *arg) static bool trans_lhu(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); } static bool trans_lhur(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); + return do_load(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, true); } static bool trans_lhuea(DisasContext *dc, arg_typea *arg) @@ -798,26 +798,26 @@ static bool trans_lhuea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); + return do_load(dc, arg->rd, addr, MO_TE | MO_UW, MMU_NOMMU_IDX, false); #endif } static bool trans_lhui(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); } static bool trans_lw(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); } static bool trans_lwr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); + return do_load(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, true); } static bool trans_lwea(DisasContext *dc, arg_typea *arg) @@ -829,14 +829,14 @@ static bool trans_lwea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); + return do_load(dc, arg->rd, addr, MO_TE | MO_UL, MMU_NOMMU_IDX, false); #endif } static bool trans_lwi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); } static bool trans_lwx(DisasContext *dc, arg_typea *arg) @@ -846,7 +846,7 @@ static bool trans_lwx(DisasContext *dc, arg_typea *arg) /* lwx does not throw unaligned access errors, so force alignment */ tcg_gen_andi_tl(addr, addr, ~3); - tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TEUL); + tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TE | MO_UL); tcg_gen_mov_tl(cpu_res_addr, addr); if (arg->rd) { @@ -930,13 +930,13 @@ static bool trans_sbi(DisasContext *dc, arg_typeb *arg) static bool trans_sh(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); } static bool trans_shr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); + return do_store(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, true); } static bool trans_shea(DisasContext *dc, arg_typea *arg) @@ -948,26 +948,26 @@ static bool trans_shea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); + return do_store(dc, arg->rd, addr, MO_TE | MO_UW, MMU_NOMMU_IDX, false); #endif } static bool trans_shi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); } static bool trans_sw(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); } static bool trans_swr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); + return do_store(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, true); } static bool trans_swea(DisasContext *dc, arg_typea *arg) @@ -979,14 +979,14 @@ static bool trans_swea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); + return do_store(dc, arg->rd, addr, MO_TE | MO_UL, MMU_NOMMU_IDX, false); #endif } static bool trans_swi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); } static bool trans_swx(DisasContext *dc, arg_typea *arg) @@ -1015,7 +1015,7 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) tcg_gen_atomic_cmpxchg_i32(tval, cpu_res_addr, cpu_res_val, reg_for_write(dc, arg->rd), - dc->mem_index, MO_TEUL); + dc->mem_index, MO_TE | MO_UL); tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_val, tval, swx_fail); From 401bd7d340a4558995e8f61bd2050174e043ef20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Sep 2024 23:42:38 +0200 Subject: [PATCH 2266/2892] target/microblaze: Set MO_TE once in do_load() / do_store() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers of do_load() / do_store() set MO_TE flag. Set it once in the callees. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241105130431.22564-15-philmd@linaro.org> --- target/microblaze/translate.c | 36 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 86efabb83b..0d51b2c468 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -713,6 +713,8 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, { MemOp size = mop & MO_SIZE; + mop |= MO_TE; + /* * When doing reverse accesses we need to do two things. * @@ -780,13 +782,13 @@ static bool trans_lbui(DisasContext *dc, arg_typeb *arg) static bool trans_lhu(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_lhur(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, true); + return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, true); } static bool trans_lhuea(DisasContext *dc, arg_typea *arg) @@ -798,26 +800,26 @@ static bool trans_lhuea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TE | MO_UW, MMU_NOMMU_IDX, false); + return do_load(dc, arg->rd, addr, MO_UW, MMU_NOMMU_IDX, false); #endif } static bool trans_lhui(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_load(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_lw(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_lwr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, true); + return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, true); } static bool trans_lwea(DisasContext *dc, arg_typea *arg) @@ -829,14 +831,14 @@ static bool trans_lwea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TE | MO_UL, MMU_NOMMU_IDX, false); + return do_load(dc, arg->rd, addr, MO_UL, MMU_NOMMU_IDX, false); #endif } static bool trans_lwi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_load(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_lwx(DisasContext *dc, arg_typea *arg) @@ -863,6 +865,8 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, { MemOp size = mop & MO_SIZE; + mop |= MO_TE; + /* * When doing reverse accesses we need to do two things. * @@ -930,13 +934,13 @@ static bool trans_sbi(DisasContext *dc, arg_typeb *arg) static bool trans_sh(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_shr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, true); + return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, true); } static bool trans_shea(DisasContext *dc, arg_typea *arg) @@ -948,26 +952,26 @@ static bool trans_shea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TE | MO_UW, MMU_NOMMU_IDX, false); + return do_store(dc, arg->rd, addr, MO_UW, MMU_NOMMU_IDX, false); #endif } static bool trans_shi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_store(dc, arg->rd, addr, MO_TE | MO_UW, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_sw(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_swr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, true); + return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, true); } static bool trans_swea(DisasContext *dc, arg_typea *arg) @@ -979,14 +983,14 @@ static bool trans_swea(DisasContext *dc, arg_typea *arg) return true; #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TE | MO_UL, MMU_NOMMU_IDX, false); + return do_store(dc, arg->rd, addr, MO_UL, MMU_NOMMU_IDX, false); #endif } static bool trans_swi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_store(dc, arg->rd, addr, MO_TE | MO_UL, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_swx(DisasContext *dc, arg_typea *arg) From 2c9e8ddd769959e899206b4cdea466ba5845e0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Sep 2024 23:45:35 +0200 Subject: [PATCH 2267/2892] target/microblaze: Introduce mo_endian() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mo_endian() returns the target endianness, currently static. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241105130431.22564-16-philmd@linaro.org> --- target/microblaze/translate.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 0d51b2c468..b5389d65b2 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -708,12 +708,17 @@ static void record_unaligned_ess(DisasContext *dc, int rd, } #endif +static inline MemOp mo_endian(DisasContext *dc) +{ + return MO_TE; +} + static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, int mem_index, bool rev) { MemOp size = mop & MO_SIZE; - mop |= MO_TE; + mop |= mo_endian(dc); /* * When doing reverse accesses we need to do two things. @@ -848,7 +853,8 @@ static bool trans_lwx(DisasContext *dc, arg_typea *arg) /* lwx does not throw unaligned access errors, so force alignment */ tcg_gen_andi_tl(addr, addr, ~3); - tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TE | MO_UL); + tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, + mo_endian(dc) | MO_UL); tcg_gen_mov_tl(cpu_res_addr, addr); if (arg->rd) { @@ -865,7 +871,7 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, { MemOp size = mop & MO_SIZE; - mop |= MO_TE; + mop |= mo_endian(dc); /* * When doing reverse accesses we need to do two things. @@ -1019,7 +1025,7 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) tcg_gen_atomic_cmpxchg_i32(tval, cpu_res_addr, cpu_res_val, reg_for_write(dc, arg->rd), - dc->mem_index, MO_TE | MO_UL); + dc->mem_index, mo_endian(dc) | MO_UL); tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_val, tval, swx_fail); From 415aae543edad19eda8f66955dde386c7fd7c680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Sep 2024 23:45:54 +0200 Subject: [PATCH 2268/2892] target/microblaze: Consider endianness while translating code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consider the CPU ENDI bit, swap instructions when the CPU endianness doesn't match the binary one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241105130431.22564-17-philmd@linaro.org> --- target/microblaze/cpu.h | 7 +++++++ target/microblaze/translate.c | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index f6879eee35..e44ddd5307 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -414,6 +414,13 @@ void mb_translate_code(CPUState *cs, TranslationBlock *tb, /* Ensure there is no overlap between the two masks. */ QEMU_BUILD_BUG_ON(MSR_TB_MASK & IFLAGS_TB_MASK); +static inline bool mb_cpu_is_big_endian(CPUState *cs) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + + return !cpu->cfg.endi; +} + static inline void cpu_get_tb_cpu_state(CPUMBState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index b5389d65b2..b54e5ac4b2 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -710,7 +710,7 @@ static void record_unaligned_ess(DisasContext *dc, int rd, static inline MemOp mo_endian(DisasContext *dc) { - return MO_TE; + return dc->cfg->endi ? MO_LE : MO_BE; } static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, @@ -1647,7 +1647,8 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) dc->tb_flags_to_set = 0; - ir = translator_ldl(cpu_env(cs), &dc->base, dc->base.pc_next); + ir = translator_ldl_swap(cpu_env(cs), &dc->base, dc->base.pc_next, + mb_cpu_is_big_endian(cs) != TARGET_BIG_ENDIAN); if (!decode(dc, ir)) { trap_illegal(dc, true); } From 40b839cb840ce032c8f048325b486d3284f1b68f Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Mon, 9 Dec 2024 21:36:26 +0100 Subject: [PATCH 2269/2892] target/i386/hvf: Variable type fixup in decoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit decode_bytes reads 1, 2, 4, or 8 bytes at a time. The destination variable should therefore be a uint64_t, not a target_ulong. Signed-off-by: Phil Dennis-Jordan Fixes: ff2de1668c9 ("i386: hvf: remove addr_t") Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241209203629.74436-9-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé --- target/i386/hvf/x86_decode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index d6d5894e54..5fea2dd3cc 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -61,8 +61,8 @@ uint64_t sign(uint64_t val, int size) static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode, int size) { - target_ulong val = 0; - + uint64_t val = 0; + switch (size) { case 1: case 2: From befd818b58c8522f573b8831df820f121bfe642a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Jan 2025 00:07:23 +0100 Subject: [PATCH 2270/2892] target/openrisc: Call cpu_openrisc_clock_init() in cpu_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenRISC timer is architecturally tied to the CPU. It doesn't belong to the machine init() code to instanciate it: move its creation when a vCPU is realized (after being created). Reported-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250114231304.77150-1-philmd@linaro.org> --- hw/openrisc/openrisc_sim.c | 2 -- hw/openrisc/virt.c | 2 -- target/openrisc/cpu.c | 4 ++++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index d9e0744922..83d7c2a8af 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -306,8 +306,6 @@ static void openrisc_sim_init(MachineState *machine) exit(1); } - cpu_openrisc_clock_init(cpus[n]); - qemu_register_reset(main_cpu_reset, cpus[n]); } diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 9afe407b00..3055306783 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -487,8 +487,6 @@ static void openrisc_virt_init(MachineState *machine) exit(1); } - cpu_openrisc_clock_init(cpus[n]); - qemu_register_reset(main_cpu_reset, cpus[n]); } diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 0669ba2fd1..785b065b51 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -165,6 +165,10 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) qemu_init_vcpu(cs); cpu_reset(cs); +#ifndef CONFIG_USER_ONLY + cpu_openrisc_clock_init(OPENRISC_CPU(dev)); +#endif + occ->parent_realize(dev, errp); } From a770b10bafc3e67b16c258167e15f228298c2d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 14:33:33 +0100 Subject: [PATCH 2271/2892] target/hexagon: Ensure not being build on system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently only user emulation is supported. Assert no target code is built for system emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Brian Cain Message-Id: <20250121142341.17001-2-philmd@linaro.org> --- target/hexagon/cpu.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 79e60d4bfa..f78c8f9c2a 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -26,6 +26,10 @@ #include "mmvec/mmvec.h" #include "hw/registerfields.h" +#ifndef CONFIG_USER_ONLY +#error "Hexagon does not support system emulation" +#endif + #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 From edee3da2e62ffa741083e1caa78203f91a7f1a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:16:26 +0100 Subject: [PATCH 2272/2892] target/rx: Ensure not being build on user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently only system emulation is supported. Assert no target code is built for user emulation. Remove #ifdef'ry since more work is required before being able to emulate a user process. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121142341.17001-3-philmd@linaro.org> --- target/rx/cpu.c | 6 ------ target/rx/cpu.h | 6 ++++-- target/rx/helper.c | 4 ---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 7d5fcbf76a..17ede51cd1 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -192,13 +192,11 @@ static void rx_cpu_init(Object *obj) qdev_init_gpio_in(DEVICE(cpu), rx_cpu_set_irq, 2); } -#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps rx_sysemu_ops = { .get_phys_page_debug = rx_cpu_get_phys_page_debug, }; -#endif #include "accel/tcg/cpu-ops.h" @@ -209,11 +207,9 @@ static const TCGCPUOps rx_tcg_ops = { .restore_state_to_opc = rx_restore_state_to_opc, .tlb_fill = rx_cpu_tlb_fill, -#ifndef CONFIG_USER_ONLY .cpu_exec_interrupt = rx_cpu_exec_interrupt, .cpu_exec_halt = rx_cpu_has_work, .do_interrupt = rx_cpu_do_interrupt, -#endif /* !CONFIG_USER_ONLY */ }; static void rx_cpu_class_init(ObjectClass *klass, void *data) @@ -235,9 +231,7 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) cc->set_pc = rx_cpu_set_pc; cc->get_pc = rx_cpu_get_pc; -#ifndef CONFIG_USER_ONLY cc->sysemu_ops = &rx_sysemu_ops; -#endif cc->gdb_read_register = rx_cpu_gdb_read_register; cc->gdb_write_register = rx_cpu_gdb_write_register; cc->disas_set_info = rx_cpu_disas_set_info; diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 5ba1874bd7..349d61c4e4 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -26,6 +26,10 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +#ifdef CONFIG_USER_ONLY +#error "RX does not support user mode emulation" +#endif + /* PSW define */ REG32(PSW, 0) FIELD(PSW, C, 0, 1) @@ -129,11 +133,9 @@ struct RXCPUClass { #define CPU_RESOLVING_TYPE TYPE_RX_CPU const char *rx_crname(uint8_t cr); -#ifndef CONFIG_USER_ONLY void rx_cpu_do_interrupt(CPUState *cpu); bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req); hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -#endif /* !CONFIG_USER_ONLY */ void rx_cpu_dump_state(CPUState *cpu, FILE *f, int flags); int rx_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int rx_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target/rx/helper.c b/target/rx/helper.c index 80912e8dcb..7f28e72989 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -40,8 +40,6 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) env->psw_c = FIELD_EX32(psw, PSW, C); } -#ifndef CONFIG_USER_ONLY - #define INT_FLAGS (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR) void rx_cpu_do_interrupt(CPUState *cs) { @@ -146,5 +144,3 @@ hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { return addr; } - -#endif /* !CONFIG_USER_ONLY */ From ff3779a543954f7c3e7f3a604eefcc7c15726940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:16:31 +0100 Subject: [PATCH 2273/2892] target/tricore: Ensure not being build on user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently only system emulation is supported. Assert no target code is built for user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250121142341.17001-4-philmd@linaro.org> --- target/tricore/cpu.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index 8e431d7922..cf9dbc6df8 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -26,6 +26,10 @@ #include "qemu/cpu-float.h" #include "tricore-defs.h" +#ifdef CONFIG_USER_ONLY +#error "TriCore does not support user mode emulation" +#endif + typedef struct CPUArchState { /* GPR Register */ uint32_t gpr_a[16]; From 96adf9b404e51b9acdf9592595ad935905de1f4e Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Fri, 24 Jan 2025 15:26:32 +0300 Subject: [PATCH 2274/2892] target/mips: Fix possible MSA int overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix possible overflow in 1 << (DF_BITS(df) - 2) when DF_BITS(df) is 64 by using a 64-bit integer for the shift operation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Reported-by: Dmitriy Fedin Signed-off-by: Denis Rastyogin Reviewed-by: Peter Maydell Message-ID: <20250124122707.54264-1-gerben@altlinux.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/msa_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index ec38d9fde5..74fb80cc25 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -5577,7 +5577,7 @@ static inline int64_t msa_mulr_q_df(uint32_t df, int64_t arg1, int64_t arg2) { int64_t q_min = DF_MIN_INT(df); int64_t q_max = DF_MAX_INT(df); - int64_t r_bit = 1 << (DF_BITS(df) - 2); + int64_t r_bit = 1LL << (DF_BITS(df) - 2); if (arg1 == q_min && arg2 == q_min) { return q_max; @@ -5685,7 +5685,7 @@ static inline int64_t msa_maddr_q_df(uint32_t df, int64_t dest, int64_t arg1, int64_t q_max = DF_MAX_INT(df); int64_t q_min = DF_MIN_INT(df); - int64_t r_bit = 1 << (DF_BITS(df) - 2); + int64_t r_bit = 1LL << (DF_BITS(df) - 2); q_prod = arg1 * arg2; q_ret = ((dest << (DF_BITS(df) - 1)) + q_prod + r_bit) >> (DF_BITS(df) - 1); @@ -5700,7 +5700,7 @@ static inline int64_t msa_msubr_q_df(uint32_t df, int64_t dest, int64_t arg1, int64_t q_max = DF_MAX_INT(df); int64_t q_min = DF_MIN_INT(df); - int64_t r_bit = 1 << (DF_BITS(df) - 2); + int64_t r_bit = 1LL << (DF_BITS(df) - 2); q_prod = arg1 * arg2; q_ret = ((dest << (DF_BITS(df) - 1)) - q_prod + r_bit) >> (DF_BITS(df) - 1); From b8b37affc74e24f1c11f7dfbba416965b805e123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:36:56 +0100 Subject: [PATCH 2275/2892] target: Set disassemble_info::endian value for little-endian targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field for little-endian targets. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-2-philmd@linaro.org> --- target/alpha/cpu.c | 1 + target/avr/cpu.c | 1 + target/hexagon/cpu.c | 1 + target/i386/cpu.c | 1 + target/loongarch/cpu.c | 1 + target/rx/cpu.c | 1 + 6 files changed, 6 insertions(+) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 57e41fcd78..2eabd7724d 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -85,6 +85,7 @@ static int alpha_cpu_mmu_index(CPUState *cs, bool ifetch) static void alpha_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->mach = bfd_mach_alpha_ev6; info->print_insn = print_insn_alpha; } diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 5a0e21465e..2871d30540 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -102,6 +102,7 @@ static void avr_cpu_reset_hold(Object *obj, ResetType type) static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->mach = bfd_arch_avr; info->print_insn = avr_print_insn; } diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 238e63bcea..a9beb9a175 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -293,6 +293,7 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info) { info->print_insn = print_insn_hexagon; + info->endian = BFD_ENDIAN_LITTLE; } static void hexagon_cpu_realize(DeviceState *dev, Error **errp) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0cd9b70938..ab328485ac 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8691,6 +8691,7 @@ static void x86_disas_set_info(CPUState *cs, disassemble_info *info) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + info->endian = BFD_ENDIAN_LITTLE; info->mach = (env->hflags & HF_CS64_MASK ? bfd_mach_x86_64 : env->hflags & HF_CS32_MASK ? bfd_mach_i386_i386 : bfd_mach_i386_i8086); diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index b4b82425b1..d2e739a029 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -624,6 +624,7 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->print_insn = print_insn_loongarch; } diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 17ede51cd1..1c40c8977e 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -168,6 +168,7 @@ static void rx_cpu_set_irq(void *opaque, int no, int request) static void rx_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->mach = bfd_mach_rx; info->print_insn = print_insn_rx; } From 2136f7f1f95b26129922d05d233b2056b8cbff5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:37:18 +0100 Subject: [PATCH 2276/2892] target: Set disassemble_info::endian value for big-endian targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field for big-endian targets. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-3-philmd@linaro.org> --- target/hppa/cpu.c | 1 + target/m68k/cpu.c | 1 + target/openrisc/cpu.c | 1 + target/s390x/cpu.c | 1 + target/sparc/cpu.c | 1 + 5 files changed, 5 insertions(+) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 4bb5cff624..d15f8c9c21 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -150,6 +150,7 @@ static int hppa_cpu_mmu_index(CPUState *cs, bool ifetch) static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) { info->mach = bfd_mach_hppa20; + info->endian = BFD_ENDIAN_BIG; info->print_insn = print_insn_hppa; } diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index eedda07c2a..df8b9c53fc 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -157,6 +157,7 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) static void m68k_cpu_disas_set_info(CPUState *s, disassemble_info *info) { info->print_insn = print_insn_m68k; + info->endian = BFD_ENDIAN_BIG; info->mach = 0; } diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 785b065b51..e8c357ae83 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -83,6 +83,7 @@ static int openrisc_cpu_mmu_index(CPUState *cs, bool ifetch) static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = BFD_ENDIAN_BIG; info->print_insn = print_insn_or1k; } diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 3bea014f9e..972d265478 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -243,6 +243,7 @@ static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; info->cap_arch = CS_ARCH_SYSZ; + info->endian = BFD_ENDIAN_BIG; info->cap_insn_unit = 2; info->cap_insn_split = 6; } diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index e3b4613717..9fd222e4c8 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -106,6 +106,7 @@ static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info) { info->print_insn = print_insn_sparc; + info->endian = BFD_ENDIAN_BIG; #ifdef TARGET_SPARC64 info->mach = bfd_mach_sparc_v9b; #endif From 4b7d6557efaa3c0e291ab02319364f139ac400d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:30:35 +0100 Subject: [PATCH 2277/2892] target/arm: Set disassemble_info::endian value in disas_set_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-4-philmd@linaro.org> --- target/arm/cpu.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ac1ceec211..948defa3f5 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1171,7 +1171,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) { ARMCPU *ac = ARM_CPU(cpu); CPUARMState *env = &ac->env; - bool sctlr_b; + bool sctlr_b = arm_sctlr_b(env); if (is_a64(env)) { info->cap_arch = CS_ARCH_ARM64; @@ -1198,13 +1198,9 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) info->cap_mode = cap_mode; } - sctlr_b = arm_sctlr_b(env); + info->endian = BFD_ENDIAN_LITTLE; if (bswap_code(sctlr_b)) { -#if TARGET_BIG_ENDIAN - info->endian = BFD_ENDIAN_LITTLE; -#else - info->endian = BFD_ENDIAN_BIG; -#endif + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG; } info->flags &= ~INSN_ARM_BE32; #ifndef CONFIG_USER_ONLY From 840e0862c52899258ae6743de2fc3826f9a4e4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:31:15 +0100 Subject: [PATCH 2278/2892] target/microblaze: Set disassemble_info::endian value in disas_set_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-5-philmd@linaro.org> --- target/microblaze/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 13d194cef8..d5ee1244ca 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -224,6 +224,8 @@ static void mb_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_arch_microblaze; info->print_insn = print_insn_microblaze; + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; } static void mb_cpu_realizefn(DeviceState *dev, Error **errp) From 7bb1a717cb2fd11962d1ad72933e6235c2d638c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:31:20 +0100 Subject: [PATCH 2279/2892] target/mips: Set disassemble_info::endian value in disas_set_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-6-philmd@linaro.org> --- target/mips/cpu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 0b267d2e50..e76298699a 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -428,13 +428,13 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type) static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) { if (!(cpu_env(s)->insn_flags & ISA_NANOMIPS32)) { -#if TARGET_BIG_ENDIAN - info->print_insn = print_insn_big_mips; -#else - info->print_insn = print_insn_little_mips; -#endif + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; + info->print_insn = TARGET_BIG_ENDIAN ? print_insn_big_mips + : print_insn_little_mips; } else { info->print_insn = print_insn_nanomips; + info->endian = BFD_ENDIAN_LITTLE; } } From 724bac41906752aafd432714d13fc78da2265f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:31:38 +0100 Subject: [PATCH 2280/2892] target/ppc: Set disassemble_info::endian value in disas_set_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback always set\ the disassemble_info::endian field. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-7-philmd@linaro.org> --- target/ppc/cpu_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 425049ab09..b9772c53ec 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7401,6 +7401,8 @@ static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) if ((env->hflags >> MSR_LE) & 1) { info->endian = BFD_ENDIAN_LITTLE; + } else { + info->endian = BFD_ENDIAN_BIG; } info->mach = env->bfd_mach; if (!env->bfd_mach) { From 0a8bfcbe7ca32f160c47faa9d611173b0697a698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:31:44 +0100 Subject: [PATCH 2281/2892] target/riscv: Set disassemble_info::endian value in disas_set_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-8-philmd@linaro.org> --- target/riscv/cpu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 47424fd5e2..6da391738f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1152,6 +1152,15 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) CPURISCVState *env = &cpu->env; info->target_info = &cpu->cfg; + /* + * A couple of bits in MSTATUS set the endianness: + * - MSTATUS_UBE (User-mode), + * - MSTATUS_SBE (Supervisor-mode), + * - MSTATUS_MBE (Machine-mode) + * but we don't implement that yet. + */ + info->endian = BFD_ENDIAN_LITTLE; + switch (env->xl) { case MXL_RV32: info->print_insn = print_insn_riscv32; From 35e9b36d6e724160d8f33ab9b61dd8b660e4df1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:32:03 +0100 Subject: [PATCH 2282/2892] target/sh4: Set disassemble_info::endian value in disas_set_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-9-philmd@linaro.org> --- target/sh4/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 22cdf9b4e1..c2aaa40a03 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -143,6 +143,8 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; info->mach = bfd_mach_sh4; info->print_insn = print_insn_sh; } From 059eb605fd67f15c0ca7f07c7a02c319035b095b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:35:24 +0100 Subject: [PATCH 2283/2892] target/xtensa: Set disassemble_info::endian value in disas_set_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the CPUClass::disas_set_info() callback set the disassemble_info::endian field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20250210212931.62401-10-philmd@linaro.org> --- target/xtensa/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index efbfe73fcf..f9e298ace4 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -159,6 +159,8 @@ static void xtensa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) info->private_data = cpu->env.config->isa; info->print_insn = print_insn_xtensa; + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; } static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) From ae24e13a9f46128fb815957dc234020351797c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 14:34:19 +0100 Subject: [PATCH 2284/2892] disas: Remove target_words_bigendian() call in initialize_debug_target() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All CPUClass implementating disas_set_info() must set the disassemble_info::endian value. Ensure that by setting %endian to BFD_ENDIAN_UNKNOWN before calling the CPUClass::disas_set_info() handler, then asserting %endian is not BFD_ENDIAN_UNKNOWN after the call. This allows removing the target_words_bigendian() call in disas/. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250210212931.62401-11-philmd@linaro.org> --- disas/disas-common.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/disas/disas-common.c b/disas/disas-common.c index de61f6d8a1..ae3f9e46ea 100644 --- a/disas/disas-common.c +++ b/disas/disas-common.c @@ -7,7 +7,6 @@ #include "disas/disas.h" #include "disas/capstone.h" #include "hw/core/cpu.h" -#include "exec/tswap.h" #include "disas-internal.h" @@ -61,15 +60,12 @@ void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) s->cpu = cpu; s->info.print_address_func = print_address; - if (target_words_bigendian()) { - s->info.endian = BFD_ENDIAN_BIG; - } else { - s->info.endian = BFD_ENDIAN_LITTLE; - } + s->info.endian = BFD_ENDIAN_UNKNOWN; CPUClass *cc = CPU_GET_CLASS(cpu); if (cc->disas_set_info) { cc->disas_set_info(cpu, &s->info); + g_assert(s->info.endian != BFD_ENDIAN_UNKNOWN); } } From 0048035a870312f2fd0f3dd28115398d26e419bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 9 Feb 2025 22:11:32 +0100 Subject: [PATCH 2285/2892] target/i386: Constify X86CPUModel uses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210133134.90879-2-philmd@linaro.org> --- target/i386/cpu.c | 8 ++++---- target/i386/cpu.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ab328485ac..b3e1c2bca4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6563,7 +6563,7 @@ void x86_cpu_apply_props(X86CPU *cpu, PropValue *props) * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model) +static void x86_cpu_apply_version_props(X86CPU *cpu, const X86CPUModel *model) { const X86CPUVersionDefinition *vdef; X86CPUVersion version = x86_cpu_model_resolve_version(model); @@ -6592,7 +6592,7 @@ static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model) } static const CPUCaches *x86_cpu_get_versioned_cache_info(X86CPU *cpu, - X86CPUModel *model) + const X86CPUModel *model) { const X86CPUVersionDefinition *vdef; X86CPUVersion version = x86_cpu_model_resolve_version(model); @@ -6620,7 +6620,7 @@ static const CPUCaches *x86_cpu_get_versioned_cache_info(X86CPU *cpu, * Load data from X86CPUDefinition into a X86CPU object. * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model) +static void x86_cpu_load_model(X86CPU *cpu, const X86CPUModel *model) { const X86CPUDefinition *def = model->cpudef; CPUX86State *env = &cpu->env; @@ -6690,7 +6690,7 @@ static const gchar *x86_gdb_arch_name(CPUState *cs) static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) { - X86CPUModel *model = data; + const X86CPUModel *model = data; X86CPUClass *xcc = X86_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 10ce019e3f..7882b63b9b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2327,7 +2327,7 @@ struct X86CPUClass { * CPU definition, automatically loaded by instance_init if not NULL. * Should be eventually replaced by subclass-specific property defaults. */ - X86CPUModel *model; + const X86CPUModel *model; bool host_cpuid_required; int ordering; From 1e6fbd637bbbfdd8ff13ed665b5294fab4771862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 9 Feb 2025 22:11:38 +0100 Subject: [PATCH 2286/2892] target/sparc: Constify SPARCCPUClass::cpu_def MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210133134.90879-3-philmd@linaro.org> --- target/sparc/cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index dda811503b..462bcb6c0e 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -574,7 +574,7 @@ struct SPARCCPUClass { DeviceRealize parent_realize; ResettablePhases parent_phases; - sparc_def_t *cpu_def; + const sparc_def_t *cpu_def; }; #ifndef CONFIG_USER_ONLY From 05769aae6288a69ba04b0162ed0a15b08b2b7878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 9 Feb 2025 22:11:46 +0100 Subject: [PATCH 2287/2892] target/xtensa: Finalize config in xtensa_register_core() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make XtensaConfigList::config not const. Only modify XtensaConfig within xtensa_register_core(), when the class is registered, not when it is initialized. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Max Filippov Message-Id: <20250210133134.90879-4-philmd@linaro.org> --- target/xtensa/cpu.h | 2 +- target/xtensa/helper.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 0e6302c5bd..8d70bfc0cd 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -490,7 +490,7 @@ typedef struct XtensaConfig { } XtensaConfig; typedef struct XtensaConfigList { - const XtensaConfig *config; + XtensaConfig *config; struct XtensaConfigList *next; } XtensaConfigList; diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index 2978c471c1..f64699b116 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -173,9 +173,8 @@ static void xtensa_core_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc); - XtensaConfig *config = data; + const XtensaConfig *config = data; - xtensa_finalize_config(config); xcc->config = config; /* @@ -195,6 +194,8 @@ void xtensa_register_core(XtensaConfigList *node) .class_data = (void *)node->config, }; + xtensa_finalize_config(node->config); + node->next = xtensa_cores; xtensa_cores = node; type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name); From 3bbcc0f732a173f164628243c6345b659c08900d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Feb 2025 10:11:16 +0100 Subject: [PATCH 2288/2892] target/riscv: Declare RISCVCPUClass::misa_mxl_max as RISCVMXL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210133134.90879-5-philmd@linaro.org> --- target/riscv/cpu.c | 2 +- target/riscv/cpu.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6da391738f..d4f01965df 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3056,7 +3056,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); - mcc->misa_mxl_max = (uint32_t)(uintptr_t)data; + mcc->misa_mxl_max = (RISCVMXL)(uintptr_t)data; riscv_cpu_validate_misa_mxl(mcc); } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 616c3bdc1c..7de19b4183 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -539,7 +539,7 @@ struct RISCVCPUClass { DeviceRealize parent_realize; ResettablePhases parent_phases; - uint32_t misa_mxl_max; /* max mxl for this cpu */ + RISCVMXL misa_mxl_max; /* max mxl for this cpu */ }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) From 2101c85aeab4236c7b569bcf4ccaaecf318c231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Feb 2025 10:11:52 +0100 Subject: [PATCH 2289/2892] target/riscv: Convert misa_mxl_max using GLib macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use GLib conversion macros to pass misa_mxl_max as riscv_cpu_class_init() class data. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250210133134.90879-6-philmd@linaro.org> --- target/riscv/cpu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d4f01965df..6db2252aac 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3056,7 +3056,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); - mcc->misa_mxl_max = (RISCVMXL)(uintptr_t)data; + mcc->misa_mxl_max = (RISCVMXL)GPOINTER_TO_UINT(data); riscv_cpu_validate_misa_mxl(mcc); } @@ -3158,7 +3158,7 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .parent = TYPE_RISCV_DYNAMIC_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ + .class_data = GUINT_TO_POINTER(misa_mxl_max) \ } #define DEFINE_VENDOR_CPU(type_name, misa_mxl_max, initfn) \ @@ -3167,7 +3167,7 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .parent = TYPE_RISCV_VENDOR_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ + .class_data = GUINT_TO_POINTER(misa_mxl_max) \ } #define DEFINE_BARE_CPU(type_name, misa_mxl_max, initfn) \ @@ -3176,7 +3176,7 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .parent = TYPE_RISCV_BARE_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ + .class_data = GUINT_TO_POINTER(misa_mxl_max) \ } #define DEFINE_PROFILE_CPU(type_name, misa_mxl_max, initfn) \ @@ -3185,7 +3185,7 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .parent = TYPE_RISCV_BARE_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ + .class_data = GUINT_TO_POINTER(misa_mxl_max) \ } static const TypeInfo riscv_cpu_type_infos[] = { From 35487a6dc0e53b998217b0963d10c18e84c5bb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Feb 2025 17:15:26 +0100 Subject: [PATCH 2290/2892] target/alpha: Do not mix exception flags and FPCR bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_float_exception_flags() returns exception flags, which are distinct from the FPCR bits used as error code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250211162604.83446-1-philmd@linaro.org> --- target/alpha/fpu_helper.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c index f810a9b6a4..6aefb9b851 100644 --- a/target/alpha/fpu_helper.c +++ b/target/alpha/fpu_helper.c @@ -455,29 +455,28 @@ static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode) { float64 fa; int64_t ret; - uint32_t exc; + uint32_t exc = 0; + int flags; fa = t_to_float64(a); ret = float64_to_int64_modulo(fa, roundmode, &FP_STATUS); - exc = get_float_exception_flags(&FP_STATUS); - if (unlikely(exc)) { + flags = get_float_exception_flags(&FP_STATUS); + if (unlikely(flags)) { set_float_exception_flags(0, &FP_STATUS); /* We need to massage the resulting exceptions. */ - if (exc & float_flag_invalid_cvti) { + if (flags & float_flag_invalid_cvti) { /* Overflow, either normal or infinity. */ if (float64_is_infinity(fa)) { exc = FPCR_INV; } else { exc = FPCR_IOV | FPCR_INE; } - } else if (exc & float_flag_invalid) { + } else if (flags & float_flag_invalid) { exc = FPCR_INV; - } else if (exc & float_flag_inexact) { + } else if (flags & float_flag_inexact) { exc = FPCR_INE; - } else { - exc = 0; } } env->error_code = exc; From df9ae6aa84b92bb73c84194dc60f938e2495594c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Feb 2025 09:50:11 +0100 Subject: [PATCH 2291/2892] target/i386: Mark WHPX APIC region as little-endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device is only used by the x86 targets, which are only built as little-endian. Therefore the DEVICE_NATIVE_ENDIAN definition expand to DEVICE_LITTLE_ENDIAN (besides, the DEVICE_BIG_ENDIAN case isn't tested). Simplify directly using DEVICE_LITTLE_ENDIAN. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250212113938.38692-6-philmd@linaro.org> --- target/i386/whpx/whpx-apic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index 4245ab68a2..630a9616d7 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -231,7 +231,7 @@ static void whpx_apic_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps whpx_apic_io_ops = { .read = whpx_apic_mem_read, .write = whpx_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void whpx_apic_reset(APICCommonState *s) From f2d4df439e0b2c2c3cebf792a7966466c9d97b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Mar 2025 22:26:08 +0100 Subject: [PATCH 2292/2892] system: Open-code qemu_init_arch_modules() using target_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly revert commit c80cafa0c73 ("system: Add qemu_init_arch_modules") but using target_name() instead of the target specific 'TARGET_NAME' definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250305005225.95051-3-philmd@linaro.org> --- include/system/arch_init.h | 2 -- system/arch_init.c | 9 --------- system/vl.c | 7 ++++++- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/system/arch_init.h b/include/system/arch_init.h index 5b1c1026f3..d8b7744048 100644 --- a/include/system/arch_init.h +++ b/include/system/arch_init.h @@ -27,6 +27,4 @@ enum { extern const uint32_t arch_type; -void qemu_init_arch_modules(void); - #endif diff --git a/system/arch_init.c b/system/arch_init.c index d2c32f8488..b1baed18a3 100644 --- a/system/arch_init.c +++ b/system/arch_init.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "qemu/module.h" #include "system/arch_init.h" #ifdef TARGET_SPARC @@ -40,11 +39,3 @@ int graphic_depth = 32; #endif const uint32_t arch_type = QEMU_ARCH; - -void qemu_init_arch_modules(void) -{ -#ifdef CONFIG_MODULES - module_init_info(qemu_modinfo); - module_allow_arch(TARGET_NAME); -#endif -} diff --git a/system/vl.c b/system/vl.c index 8f776684ec..04f78466c4 100644 --- a/system/vl.c +++ b/system/vl.c @@ -26,6 +26,7 @@ #include "qemu/help-texts.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "qemu/module.h" #include "exec/cpu-common.h" #include "exec/page-vary.h" #include "hw/qdev-properties.h" @@ -78,6 +79,7 @@ #include "hw/block/block.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" +#include "hw/core/cpu.h" #include "migration/cpr.h" #include "migration/misc.h" #include "migration/snapshot.h" @@ -2885,7 +2887,10 @@ void qemu_init(int argc, char **argv) os_setup_limits(); - qemu_init_arch_modules(); +#ifdef CONFIG_MODULES + module_init_info(qemu_modinfo); + module_allow_arch(target_name()); +#endif qemu_init_subsystems(); From 92941c94e7f4858fdd61b4c1b85f6d1c6f164359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 5 Mar 2025 12:33:14 +0100 Subject: [PATCH 2293/2892] include: Poison TARGET_PHYS_ADDR_SPACE_BITS definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure common code never use this target specific definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250305153929.43687-4-philmd@linaro.org> --- include/exec/poison.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/exec/poison.h b/include/exec/poison.h index f4283f693a..d6d4832854 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -48,6 +48,7 @@ #pragma GCC poison TARGET_PAGE_MASK #pragma GCC poison TARGET_PAGE_BITS #pragma GCC poison TARGET_PAGE_ALIGN +#pragma GCC poison TARGET_PHYS_ADDR_SPACE_BITS #pragma GCC poison CPU_INTERRUPT_HARD #pragma GCC poison CPU_INTERRUPT_EXITTB From 089fa3d7302b38285ae146de8bbe5cf6ecc04f34 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 5 Mar 2025 14:33:10 +0800 Subject: [PATCH 2294/2892] target/loongarch: fix 'make check-functional' failed some tlb instructions get the tlb_ps from tlb->misc but the value may has been initialized to 0,just check the tlb_ps skip the function and write a log. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20250305063311.830674-2-gaosong@loongson.cn> --- target/loongarch/tcg/tlb_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index a323606e5a..27c729b5b5 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -123,7 +123,11 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (!tlb_e) { + return; + } if (index >= LOONGARCH_STLB) { tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); } else { @@ -427,7 +431,11 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); uint64_t vpn, tlb_vppn; uint8_t tlb_ps, compare_shift; + uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (!tlb_e) { + continue; + } if (i >= LOONGARCH_STLB) { tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); } else { @@ -456,7 +464,11 @@ void helper_invtlb_page_asid_or_g(CPULoongArchState *env, uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); uint64_t vpn, tlb_vppn; uint8_t tlb_ps, compare_shift; + uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (!tlb_e) { + continue; + } if (i >= LOONGARCH_STLB) { tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); } else { From d882c284a3d4472d827e49a7357198b611900b08 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 5 Mar 2025 14:33:11 +0800 Subject: [PATCH 2295/2892] target/loongarch: check tlb_ps For LoongArch th min tlb_ps is 12(4KB), for TLB code, the tlb_ps may be 0,this may case UndefinedBehavior Add a check-tlb_ps fuction to check tlb_ps, to make sure the tlb_ps is avalablie. we check tlb_ps when get the tlb_ps from tlb->misc or CSR bits. 1. cpu reset set CSR_PWCL.PTBASE and CSR_STLBPS.PS bits a default value from CSR_PRCFG2; 2. tlb instructions. some tlb instructions get the tlb_ps from tlb->misc but the value may has been initialized to 0. we need just check the tlb_ps skip the function and write a guest log. 3. csrwr instructions. to make sure CSR_PWCL.PTBASE and CSR_STLBPS.PS bits are avalable, cheke theses bits and set a default value from CSR_PRCFG2. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20250305063311.830674-3-gaosong@loongson.cn> --- target/loongarch/cpu.c | 11 +++++--- target/loongarch/helper.h | 1 + target/loongarch/internals.h | 2 ++ target/loongarch/tcg/csr_helper.c | 26 ++++++++++++++++--- .../tcg/insn_trans/trans_privileged.c.inc | 1 + target/loongarch/tcg/tlb_helper.c | 23 ++++++++++++++-- 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ac514a15fb..0486853048 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -544,6 +544,7 @@ static void loongarch_max_initfn(Object *obj) static void loongarch_cpu_reset_hold(Object *obj, ResetType type) { + uint8_t tlb_ps; CPUState *cs = CPU(obj); LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj); CPULoongArchState *env = cpu_env(cs); @@ -592,13 +593,17 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) */ env->CSR_PGDH = 0; env->CSR_PGDL = 0; - env->CSR_PWCL = 0; env->CSR_PWCH = 0; - env->CSR_STLBPS = 0; env->CSR_EENTRY = 0; env->CSR_TLBRENTRY = 0; env->CSR_MERRENTRY = 0; - + /* set CSR_PWCL.PTBASE and CSR_STLBPS.PS bits from CSR_PRCFG2 */ + if (env->CSR_PRCFG2 == 0) { + env->CSR_PRCFG2 = 0x3fffff000; + } + tlb_ps = ctz32(env->CSR_PRCFG2); + env->CSR_STLBPS = FIELD_DP64(env->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps); + env->CSR_PWCL = FIELD_DP64(env->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps); for (n = 0; n < 4; n++) { env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0); diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h index 943517b5f2..1d5cb0198c 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -100,6 +100,7 @@ DEF_HELPER_1(rdtime_d, i64, env) DEF_HELPER_1(csrrd_pgd, i64, env) DEF_HELPER_1(csrrd_cpuid, i64, env) DEF_HELPER_1(csrrd_tval, i64, env) +DEF_HELPER_2(csrwr_stlbps, i64, env, tl) DEF_HELPER_2(csrwr_estat, i64, env, tl) DEF_HELPER_2(csrwr_asid, i64, env, tl) DEF_HELPER_2(csrwr_tcfg, i64, env, tl) diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 7b254c5f49..1cd959a766 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -43,6 +43,8 @@ enum { TLBRET_PE = 7, }; +bool check_ps(CPULoongArchState *ent, int ps); + extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 6c95be9910..289d89266e 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -17,6 +17,22 @@ #include "hw/irq.h" #include "cpu-csr.h" +target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) +{ + int64_t old_v = env->CSR_STLBPS; + + /* + * The real hardware only supports the min tlb_ps is 12 + * tlb_ps=0 may cause undefined-behavior. + */ + uint8_t tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + if (!check_ps(env, tlb_ps)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attempted set ps %d\n", tlb_ps); + } + return old_v; +} + target_ulong helper_csrrd_pgd(CPULoongArchState *env) { int64_t v; @@ -99,7 +115,7 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) { - int shift; + int shift, ptbase; int64_t old_v = env->CSR_PWCL; /* @@ -107,12 +123,16 @@ target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) * treated as illegal. */ shift = FIELD_EX64(val, CSR_PWCL, PTEWIDTH); + ptbase = FIELD_EX64(val, CSR_PWCL, PTBASE); if (shift) { qemu_log_mask(LOG_GUEST_ERROR, "Attempted set pte width with %d bit\n", 64 << shift); val = FIELD_DP64(val, CSR_PWCL, PTEWIDTH, 0); } - - env->CSR_PWCL = val; + if (!check_ps(env, ptbase)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attrmpted set ptbase 2^%d\n", ptbase); + } + env->CSR_PWCL =val; return old_v; } diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 3afa23af79..ecbfe23b63 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -74,6 +74,7 @@ static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn, void loongarch_csr_translate_init(void) { + SET_CSR_FUNC(STLBPS, NULL, gen_helper_csrwr_stlbps); SET_CSR_FUNC(ESTAT, NULL, gen_helper_csrwr_estat); SET_CSR_FUNC(ASID, NULL, gen_helper_csrwr_asid); SET_CSR_FUNC(PGD, gen_helper_csrrd_pgd, NULL); diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 27c729b5b5..5a426691bc 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -18,6 +18,14 @@ #include "exec/log.h" #include "cpu-csr.h" +bool check_ps(CPULoongArchState *env, int tlb_ps) +{ + if (tlb_ps > 64) { + return false; + } + return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); +} + void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level) { @@ -191,8 +199,10 @@ static void fill_tlb_entry(CPULoongArchState *env, int index) lo1 = env->CSR_TLBELO1; } - if (csr_ps == 0) { - qemu_log_mask(CPU_LOG_MMU, "page size is 0\n"); + /*check csr_ps */ + if (!check_ps(env, csr_ps)) { + qemu_log_mask(LOG_GUEST_ERROR, "csr_ps %d is illegal\n", csr_ps); + return; } /* Only MTLB has the ps fields */ @@ -302,7 +312,16 @@ void helper_tlbfill(CPULoongArchState *env) pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); } + if (!check_ps(env, pagesize)) { + qemu_log_mask(LOG_GUEST_ERROR, "pagesize %d is illegal\n", pagesize); + return; + } + stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + if (!check_ps(env, stlb_ps)) { + qemu_log_mask(LOG_GUEST_ERROR, "stlb_ps %d is illegal\n", stlb_ps); + return; + } if (pagesize == stlb_ps) { /* Only write into STLB bits [47:13] */ From 42ea7f782a32df4ac58e7d9d73e736def3057ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 28 Feb 2025 10:27:36 +0000 Subject: [PATCH 2296/2892] tests/functional: skip memaddr tests on 32-bit builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the QEMU binary was built for a 32-bit ELF target we cannot run the memory address space tests as they all require ability to address more RAM that can be represented on 32-bit. We can't use a decorator to skip the tests as we need setUp() to run to pick the QEMU binary, thus we must call a method at the start of each test to check and skip it. The functional result is effectively the same as using a decorator, just less pretty. This code will go away when 32-bit hosts are full dropped from QEMU. The code allows any non-ELF target since all macOS versions supported at 64-bit only and we already dropped support for 32-bit Windows. Signed-off-by: Daniel P. Berrangé Message-ID: <20250228102738.3064045-6-berrange@redhat.com> [thuth: Add missing byteorder='little' to from_bytes()] Signed-off-by: Thomas Huth --- tests/functional/test_mem_addr_space.py | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/test_mem_addr_space.py index bb0cf062ca..2d9d31efb5 100755 --- a/tests/functional/test_mem_addr_space.py +++ b/tests/functional/test_mem_addr_space.py @@ -20,6 +20,25 @@ class MemAddrCheck(QemuSystemTest): # this reason. DELAY_Q35_BOOT_SEQUENCE = 1 + # This helper can go away when the 32-bit host deprecation + # turns into full & final removal of support. + def ensure_64bit_binary(self): + with open(self.qemu_bin, "rb") as fh: + ident = fh.read(4) + + # "\x7fELF" + if ident != bytes([0x7f, 0x45, 0x4C, 0x46]): + # Non-ELF file implies macOS or Windows which + # we already assume to be 64-bit only + return + + # bits == 1 -> 32-bit; bits == 2 -> 64-bit + bits = int.from_bytes(fh.read(1), byteorder='little') + if bits != 2: + # 32-bit ELF builds won't be able to address sufficient + # RAM to run the tests + self.skipTest("64-bit build host is required") + # first, lets test some 32-bit processors. # for all 32-bit cases, pci64_hole_size is 0. def test_phybits_low_pse36(self): @@ -38,6 +57,7 @@ class MemAddrCheck(QemuSystemTest): If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU should start fine. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'q35', '-m', '512,slots=1,maxmem=59.6G', '-cpu', 'pentium,pse36=on', '-display', 'none', @@ -55,6 +75,7 @@ class MemAddrCheck(QemuSystemTest): access up to a maximum of 64GiB of memory. Rest is the same as the case with pse36 above. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'q35', '-m', '512,slots=1,maxmem=59.6G', '-cpu', 'pentium,pae=on', '-display', 'none', @@ -71,6 +92,7 @@ class MemAddrCheck(QemuSystemTest): Setting maxmem to 59.5G and making sure that QEMU can start with the same options as the failing case above with pse36 cpu feature. """ + self.ensure_64bit_binary() self.vm.add_args('-machine', 'q35', '-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium,pse36=on', '-display', 'none', @@ -88,6 +110,7 @@ class MemAddrCheck(QemuSystemTest): Setting maxmem to 59.5G and making sure that QEMU can start fine with the same options as the case above. """ + self.ensure_64bit_binary() self.vm.add_args('-machine', 'q35', '-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium,pae=on', '-display', 'none', @@ -104,6 +127,7 @@ class MemAddrCheck(QemuSystemTest): Pentium2 has 36 bits of addressing, so its same as pentium with pse36 ON. """ + self.ensure_64bit_binary() self.vm.add_args('-machine', 'q35', '-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium2', '-display', 'none', @@ -123,6 +147,7 @@ class MemAddrCheck(QemuSystemTest): message because the region for memory hotplug is always placed above 4 GiB due to the PCI hole and simplicity. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'q35', '-m', '512,slots=1,maxmem=4G', '-cpu', 'pentium', '-display', 'none', @@ -150,6 +175,7 @@ class MemAddrCheck(QemuSystemTest): which is equal to 987.5 GiB. Setting the value to 988 GiB should make QEMU fail with the error message. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', '512,slots=1,maxmem=988G', '-display', 'none', @@ -170,6 +196,7 @@ class MemAddrCheck(QemuSystemTest): Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less than 988 GiB). """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', '512,slots=1,maxmem=976G', '-display', 'none', @@ -186,6 +213,7 @@ class MemAddrCheck(QemuSystemTest): Same as q35-7.0 AMD case except that here we check that QEMU can successfully start when maxmem is < 988G. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', '512,slots=1,maxmem=987.5G', '-display', 'none', @@ -202,6 +230,7 @@ class MemAddrCheck(QemuSystemTest): Same as q35-7.1 AMD case except that here we check that QEMU can successfully start when maxmem is < 976G. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', '512,slots=1,maxmem=975.5G', '-display', 'none', @@ -219,6 +248,7 @@ class MemAddrCheck(QemuSystemTest): Intel cpu instead. QEMU should start fine in this case as "above_4G" memory starts at 4G. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-cpu', 'Skylake-Server', '-machine', 'pc-q35-7.1', '-m', '512,slots=1,maxmem=976G', @@ -243,6 +273,7 @@ class MemAddrCheck(QemuSystemTest): memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should fail to start. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', '-machine', 'pc-q35-7.1', '-m', '512,slots=1,maxmem=992G', @@ -261,6 +292,7 @@ class MemAddrCheck(QemuSystemTest): Same as above but by setting maxram between 976 GiB and 992 Gib, QEMU should start fine. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', '-machine', 'pc-q35-7.1', '-m', '512,slots=1,maxmem=990G', @@ -281,6 +313,7 @@ class MemAddrCheck(QemuSystemTest): So maxmem here should be at most 986 GiB considering all memory boundary alignment constraints with 40 bits (1 TiB) of processor physical bits. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', '-machine', 'q35,cxl=on', '-m', '512,slots=1,maxmem=987G', @@ -299,6 +332,7 @@ class MemAddrCheck(QemuSystemTest): with the exact same parameters as above, QEMU should start fine even with cxl enabled. """ + self.ensure_64bit_binary() self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', '-machine', 'q35,cxl=on', '-m', '512,slots=1,maxmem=987G', From 5ad2c8f357a76bbc502452c60076a4b36708f46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 28 Feb 2025 10:27:37 +0000 Subject: [PATCH 2297/2892] tests/functional: drop unused 'get_tag' method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250228102738.3064045-7-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/tuxruntest.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index 41a4945a14..ad74156f9c 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -24,17 +24,6 @@ class TuxRunBaselineTest(QemuSystemTest): # Tests are ~10-40s, allow for --debug/--enable-gcov overhead timeout = 100 - def get_tag(self, tagname, default=None): - """ - Get the metadata tag or return the default. - """ - utag = self._get_unique_tag_val(tagname) - print(f"{tagname}/{default} -> {utag}") - if utag: - return utag - - return default - def setUp(self): super().setUp() From 981395889201f556c37e18c7a896d2555ffa4373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 28 Feb 2025 10:27:38 +0000 Subject: [PATCH 2298/2892] tests/functional: stop output from zstd command when uncompressing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The zstd command will print incremental decompression progress to stderr when running. Fortunately it is not on stdout as that would confuse the TAP parsing, but we should still not have this printed. By switching from 'check_call' to 'run' with the check=True and capture_output=True we'll get the desired silence on success, and on failure the raised exception will automatically include stdout/stderr data for diagnosis purposes. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250228102738.3064045-8-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/uncompress.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py index 76dcf22385..ce79da1b68 100644 --- a/tests/functional/qemu_test/uncompress.py +++ b/tests/functional/qemu_test/uncompress.py @@ -13,7 +13,7 @@ import os import stat import shutil from urllib.parse import urlparse -from subprocess import check_call, CalledProcessError +from subprocess import run, CalledProcessError, DEVNULL from .asset import Asset @@ -46,8 +46,8 @@ def zstd_uncompress(zstd_path, output_path): return try: - check_call(['zstd', "-f", "-d", zstd_path, - "-o", output_path]) + run(['zstd', "-f", "-d", zstd_path, + "-o", output_path], capture_output=True, check=True) except CalledProcessError as e: os.remove(output_path) raise Exception( From 2c92ecb678cddf4bf3ced98f94acd2f3691c21bc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 27 Feb 2025 11:39:10 +0100 Subject: [PATCH 2299/2892] tests/functional: Move the code for testing HTTP downloads to a common function We are going to use this code in other tests, too, so let's move it to the qemu_test module to be able to re-use it more easily. Message-ID: <20250227103915.19795-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/linuxkernel.py | 26 ++++++++++++++++++++++- tests/functional/test_intel_iommu.py | 22 +------------------ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py index 2c9598102d..2aca0ee3cd 100644 --- a/tests/functional/qemu_test/linuxkernel.py +++ b/tests/functional/qemu_test/linuxkernel.py @@ -3,8 +3,12 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +import hashlib +import urllib.request + +from .cmd import wait_for_console_pattern, exec_command_and_wait_for_pattern from .testcase import QemuSystemTest -from .cmd import wait_for_console_pattern +from .utils import get_usernet_hostfwd_port class LinuxKernelTest(QemuSystemTest): @@ -26,3 +30,23 @@ class LinuxKernelTest(QemuSystemTest): self.vm.launch() if wait_for: self.wait_for_console_pattern(wait_for) + + def check_http_download(self, filename, hashsum, guestport=8080, + pythoncmd='python3 -m http.server'): + exec_command_and_wait_for_pattern(self, + f'{pythoncmd} {guestport} & sleep 1', + f'Serving HTTP on 0.0.0.0 port {guestport}') + hl = hashlib.sha256() + hostport = get_usernet_hostfwd_port(self.vm) + url = f'http://localhost:{hostport}{filename}' + self.log.info(f'Downloading {url} ...') + with urllib.request.urlopen(url) as response: + while True: + chunk = response.read(1 << 20) + if not chunk: + break + hl.update(chunk) + + digest = hl.hexdigest() + self.log.info(f'sha256sum of download is {digest}.') + self.assertEqual(digest, hashsum) diff --git a/tests/functional/test_intel_iommu.py b/tests/functional/test_intel_iommu.py index a9e8f82ab5..62268d6f27 100755 --- a/tests/functional/test_intel_iommu.py +++ b/tests/functional/test_intel_iommu.py @@ -10,11 +10,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import hashlib -import urllib.request - from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern -from qemu_test.utils import get_usernet_hostfwd_port class IntelIOMMU(LinuxKernelTest): @@ -125,23 +121,7 @@ class IntelIOMMU(LinuxKernelTest): # Check virtio-net via HTTP: exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) - exec_command_and_wait_for_pattern(self, - f'python3 -m http.server {self.GUEST_PORT} & sleep 1', - f'Serving HTTP on 0.0.0.0 port {self.GUEST_PORT}') - hl = hashlib.sha256() - hostport = get_usernet_hostfwd_port(self.vm) - url = f'http://localhost:{hostport}{filename}' - self.log.info(f'Downloading {url} ...') - with urllib.request.urlopen(url) as response: - while True: - chunk = response.read(1 << 20) - if not chunk: - break - hl.update(chunk) - - digest = hl.hexdigest() - self.log.info(f'sha256sum of download is {digest}.') - self.assertEqual(digest, hashsum) + self.check_http_download(filename, hashsum, self.GUEST_PORT) def test_intel_iommu(self): self.common_vm_setup() From 7b7f98efa7628a58fae594d3aa51b3c8a10293b3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 27 Feb 2025 11:39:11 +0100 Subject: [PATCH 2300/2892] tests/functional/test_mips_malta: Add a network test via the pcnet NIC The kernel has a driver for the pcnet NIC included, and the initrd has a "tftp" command, so we can test a simple network transfer here, too. Message-ID: <20250227103915.19795-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_mips_malta.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index eaf372255b..9697c7d63f 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -45,12 +45,15 @@ class MaltaMachineConsole(LinuxKernelTest): 'dcfe3a7fe3200da3a00d176b95caaa086495eb158f2bff64afc67d7e1eb2cddc') def test_mips_malta_cpio(self): + self.require_netdev('user') + self.set_machine('malta') + self.require_device('pcnet') + kernel_path = self.archive_extract( self.ASSET_KERNEL_4_5_0, member='boot/vmlinux-4.5.0-2-4kc-malta') initrd_path = self.uncompress(self.ASSET_INITRD) - self.set_machine('malta') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 console=tty ' @@ -58,6 +61,8 @@ class MaltaMachineConsole(LinuxKernelTest): self.vm.add_args('-kernel', kernel_path, '-initrd', initrd_path, '-append', kernel_command_line, + '-netdev', 'user,id=n1,tftp=' + self.scratch_file('boot'), + '-device', 'pcnet,netdev=n1', '-no-reboot') self.vm.launch() self.wait_for_console_pattern('Boot successful.') @@ -66,6 +71,19 @@ class MaltaMachineConsole(LinuxKernelTest): 'BogoMIPS') exec_command_and_wait_for_pattern(self, 'uname -a', 'Debian') + + exec_command_and_wait_for_pattern(self, 'ip link set eth0 up', + 'eth0: link up') + exec_command_and_wait_for_pattern(self, + 'ip addr add 10.0.2.15 dev eth0', + '#') + exec_command_and_wait_for_pattern(self, 'route add default eth0', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r vmlinux-4.5.0-2-4kc-malta 10.0.2.2', '#') + exec_command_and_wait_for_pattern(self, + 'md5sum vmlinux-4.5.0-2-4kc-malta', + 'a98218a7efbdefb2dfdf9ecd08c98318') + exec_command_and_wait_for_pattern(self, 'reboot', 'reboot: Restarting system') # Wait for VM to shut down gracefully From a31001b1c807cc59b2a9aa99845783cb40a27d0c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 5 Mar 2025 08:43:53 +0100 Subject: [PATCH 2301/2892] tests/functional: Increase the timeout of the mips64el_replay test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We run the gitlab-CI with the untrusted tests enabled, and the test_replay_mips64el_malta_5KEc_cpio subtest is rather slow, so this already hit the standard 90 seconds timeout in the CI. Increase the timeout for more headroom. Reported-by: Stefan Hajnoczi Message-ID: <20250305074353.52552-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Signed-off-by: Thomas Huth --- tests/functional/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 3fd2652c07..97c3f4ad4e 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -36,6 +36,7 @@ test_timeouts = { 'intel_iommu': 300, 'mips_malta' : 120, 'mipsel_replay' : 480, + 'mips64el_replay' : 180, 'netdev_ethtool' : 180, 'ppc_40p' : 240, 'ppc64_hv' : 1000, From 842721581fdbed45fa4738d02df8d28b1eaf28dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 4 Mar 2025 18:33:40 +0000 Subject: [PATCH 2302/2892] tests/functional: fix race in virtio balloon test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two race conditions in the recently added virtio balloon test * The /dev/vda device node is not ready * The virtio-balloon driver has not issued the first stats refresh To fix the former, monitor dmesg for a line about 'vda'. To fix the latter, retry the stats query until seeing fresh data. Adding 'quiet' to the kernel command line reduces serial output which otherwise slows boot, making it less likely to hit the former race too. Signed-off-by: Daniel P. Berrangé Message-ID: <20250304183340.3749797-1-berrange@redhat.com> Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand [thuth: Break long line to avoid checkpatch error] Signed-off-by: Thomas Huth --- tests/functional/test_virtio_balloon.py | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/test_virtio_balloon.py index 67b48e1b4e..082bf08c4e 100755 --- a/tests/functional/test_virtio_balloon.py +++ b/tests/functional/test_virtio_balloon.py @@ -32,7 +32,7 @@ class VirtioBalloonx86(QemuSystemTest): 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' - 'rd.rescue') + 'rd.rescue quiet') def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern( @@ -47,6 +47,11 @@ class VirtioBalloonx86(QemuSystemTest): prompt = '# ' self.wait_for_console_pattern(prompt) + # Synchronize on virtio-block driver creating the root device + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", + "vda1") + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', prompt) exec_command_and_wait_for_pattern(self, 'chroot /sysroot', @@ -65,10 +70,21 @@ class VirtioBalloonx86(QemuSystemTest): assert val == UNSET_STATS_VALUE def assert_running_stats(self, then): - ret = self.vm.qmp('qom-get', - {'path': '/machine/peripheral/balloon', - 'property': 'guest-stats'})['return'] - when = ret.get('last-update') + # We told the QEMU to refresh stats every 100ms, but + # there can be a delay between virtio-ballon driver + # being modprobed and seeing the first stats refresh + # Retry a few times for robustness under heavy load + retries = 10 + when = 0 + while when == 0 and retries: + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + if when == 0: + retries = retries - 1 + time.sleep(0.5) + now = time.time() assert when > then and when < now From 4dc11ee468df3ffdaa312a16b4ded3378133bb39 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 7 Mar 2025 07:25:37 +0100 Subject: [PATCH 2303/2892] tests/functional/test_virtio_balloon: Only use KVM for running this test The virtio_balloon test is currently hanging for unknown reasons when being run on the shared gitlab CI runners (which don't provide KVM, thus it's running in TCG mode there). All other functional tests that use the same asset (the Fedora 31 kernel) have already been marked to work only with KVM in the past, so those other tests are skipped on the shared gitlab CI runners. As long as the problem isn't fully understood and fixed, let's do the same with the virtio_balloon test to avoid that the CI is failing here. Message-ID: <20250307063904.1081961-1-thuth@redhat.com> Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- tests/functional/test_virtio_balloon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/test_virtio_balloon.py index 082bf08c4e..5877b6c408 100755 --- a/tests/functional/test_virtio_balloon.py +++ b/tests/functional/test_virtio_balloon.py @@ -110,6 +110,7 @@ class VirtioBalloonx86(QemuSystemTest): def test_virtio_balloon_stats(self): self.set_machine('q35') + self.require_accelerator("kvm") kernel_path = self.ASSET_KERNEL.fetch() initrd_path = self.ASSET_INITRD.fetch() diskimage_path = self.ASSET_DISKIMAGE.fetch() @@ -122,7 +123,7 @@ class VirtioBalloonx86(QemuSystemTest): # reset, we can reliably catch the clean stats again in BIOS # phase before the guest OS launches self.vm.add_args("-boot", "menu=on") - self.vm.add_args("-machine", "q35,accel=kvm:tcg") + self.vm.add_args("-accel", "kvm") self.vm.add_args("-device", "virtio-balloon,id=balloon") self.vm.add_args('-drive', f'file={diskimage_path},if=none,id=drv0,snapshot=on') From e7f091d0c1750a57fd7ab39db50d1aae1c7647b0 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 6 Mar 2025 11:37:06 +0530 Subject: [PATCH 2304/2892] doc: add missing 'Asset' type in function test doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seems 'Asset' got missed in the documentation by mistake. Also fix the one spellcheck issue pointed by spellcheck Signed-off-by: Aditya Gupta Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250306060706.1982992-1-adityag@linux.ibm.com> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index bcb5509512..a9fa45eac1 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -251,7 +251,7 @@ Many functional tests download assets (e.g. Linux kernels, initrds, firmware images, etc.) from the internet to be able to run tests with them. This imposes additional challenges to the test framework. -First there is the the problem that some people might not have an +First there is the problem that some people might not have an unconstrained internet connection, so such tests should not be run by default when running ``make check``. To accomplish this situation, the tests that download files should only be added to the "thorough" @@ -274,7 +274,9 @@ the tests are run. This pre-caching is done with the qemu_test.Asset class. To use it in your test, declare an asset in your test class with its URL and SHA256 checksum like this:: - ASSET_somename = ( + from qemu_test import Asset + + ASSET_somename = Asset( ('https://www.qemu.org/assets/images/qemu_head_200.png'), '34b74cad46ea28a2966c1d04e102510daf1fd73e6582b6b74523940d5da029dd') From 9cbff6f29ee099e7cb331802a1bf9b179c4c3934 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 6 Mar 2025 11:51:23 +0100 Subject: [PATCH 2305/2892] MAINTAINERS: Add docs/devel/testing/functional.rst to the functional section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an entry for docs/devel/testing/functional.rst to get notified on patches that change this file. Message-ID: <20250306105124.702131-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 692628cd78..51f424ee84 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4228,6 +4228,7 @@ Functional testing framework M: Thomas Huth R: Philippe Mathieu-Daudé R: Daniel P. Berrange +F: docs/devel/testing/functional.rst F: tests/functional/qemu_test/ Windows Hosted Continuous Integration From dfcee1ea4c52ac60e0a06221eafb7b6253eb10c3 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Wed, 26 Feb 2025 16:00:12 -0500 Subject: [PATCH 2306/2892] s390x/pci: add support for guests that request direct mapping When receiving a guest mpcifc(4) or mpcifc(6) instruction without the T bit set, treat this as a request to perform direct mapping instead of address translation. In order to facilitate this, pin the entirety of guest memory into the host iommu. Pinning for the direct mapping case is handled via vfio and its memory listener. Additionally, ram discard settings are inherited from vfio: coordinated discards (e.g. virtio-mem) are allowed while uncoordinated discards (e.g. virtio-balloon) are disabled. Subsequent guest DMA operations are all expected to be of the format guest_phys+sdma, allowing them to be used as lookup into the host iommu table. Signed-off-by: Matthew Rosato Reviewed-by: David Hildenbrand Message-ID: <20250226210013.238349-2-mjrosato@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-bus.c | 39 +++++++++++++++++++++++++++++++-- hw/s390x/s390-pci-inst.c | 13 +++++++++-- hw/s390x/s390-pci-vfio.c | 25 ++++++++++++++++----- hw/s390x/s390-virtio-ccw.c | 5 +++++ include/hw/s390x/s390-pci-bus.h | 3 +++ 5 files changed, 76 insertions(+), 9 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 913d72cc74..9d7b0f7540 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -18,6 +18,8 @@ #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-kvm.h" #include "hw/s390x/s390-pci-vfio.h" +#include "hw/s390x/s390-virtio-ccw.h" +#include "hw/boards.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" #include "hw/pci/pci_bridge.h" @@ -724,12 +726,42 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu) g_free(name); } +void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + S390CcwMachineState *s390ms = S390_CCW_MACHINE(ms); + + /* + * For direct-mapping we must map the entire guest address space. Rather + * than using an iommu, create a memory region alias that maps GPA X to + * IOVA X + SDMA. VFIO will handle pinning via its memory listener. + */ + g_autofree char *name = g_strdup_printf("iommu-dm-s390-%04x", + iommu->pbdev->uid); + + iommu->dm_mr = g_malloc0(sizeof(*iommu->dm_mr)); + memory_region_init_alias(iommu->dm_mr, OBJECT(&iommu->mr), name, + get_system_memory(), 0, + s390_get_memory_limit(s390ms)); + iommu->enabled = true; + memory_region_add_subregion(&iommu->mr, iommu->pbdev->zpci_fn.sdma, + iommu->dm_mr); +} + void s390_pci_iommu_disable(S390PCIIOMMU *iommu) { iommu->enabled = false; g_hash_table_remove_all(iommu->iotlb); - memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr)); - object_unparent(OBJECT(&iommu->iommu_mr)); + if (iommu->dm_mr) { + memory_region_del_subregion(&iommu->mr, iommu->dm_mr); + object_unparent(OBJECT(iommu->dm_mr)); + g_free(iommu->dm_mr); + iommu->dm_mr = NULL; + } else { + memory_region_del_subregion(&iommu->mr, + MEMORY_REGION(&iommu->iommu_mr)); + object_unparent(OBJECT(&iommu->iommu_mr)); + } } static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn) @@ -1145,6 +1177,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, /* Always intercept emulated devices */ pbdev->interp = false; pbdev->forwarding_assist = false; + pbdev->rtr_avail = false; } if (s390_pci_msix_init(pbdev) && !pbdev->interp) { @@ -1510,6 +1543,8 @@ static const Property s390_pci_device_properties[] = { DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true), DEFINE_PROP_BOOL("forwarding-assist", S390PCIBusDevice, forwarding_assist, true), + DEFINE_PROP_BOOL("relaxed-translation", S390PCIBusDevice, rtr_avail, + true), }; static const VMStateDescription s390_pci_device_vmstate = { diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index e386d75d58..8cdeb6cb7f 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -16,6 +16,7 @@ #include "exec/memory.h" #include "qemu/error-report.h" #include "system/hw_accel.h" +#include "hw/boards.h" #include "hw/pci/pci_device.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" @@ -1008,17 +1009,25 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib, } /* currently we only support designation type 1 with translation */ - if (!(dt == ZPCI_IOTA_RTTO && t)) { + if (t && dt != ZPCI_IOTA_RTTO) { error_report("unsupported ioat dt %d t %d", dt, t); s390_program_interrupt(env, PGM_OPERAND, ra); return -EINVAL; + } else if (!t && !pbdev->rtr_avail) { + error_report("relaxed translation not allowed"); + s390_program_interrupt(env, PGM_OPERAND, ra); + return -EINVAL; } iommu->pba = pba; iommu->pal = pal; iommu->g_iota = g_iota; - s390_pci_iommu_enable(iommu); + if (t) { + s390_pci_iommu_enable(iommu); + } else { + s390_pci_iommu_direct_map_enable(iommu); + } return 0; } diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 7dbbc76823..443e222912 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -132,12 +132,27 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, pbdev->pft = cap->pft; /* - * If appropriate, reduce the size of the supported DMA aperture reported - * to the guest based upon the vfio DMA limit. + * If the device is a passthrough ISM device, disallow relaxed + * translation. */ - vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS; - if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) { - pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1; + if (pbdev->pft == ZPCI_PFT_ISM) { + pbdev->rtr_avail = false; + } + + /* + * If appropriate, reduce the size of the supported DMA aperture reported + * to the guest based upon the vfio DMA limit. This is applicable for + * devices that are guaranteed to not use relaxed translation. If the + * device is capable of relaxed translation then we must advertise the + * full aperture. In this case, if translation is used then we will + * rely on the vfio DMA limit counting and use RPCIT CC1 / status 16 + * to request that the guest free DMA mappings as necessary. + */ + if (!pbdev->rtr_avail) { + vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS; + if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) { + pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1; + } } } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 51ae0c133d..a9b3db19f6 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -936,8 +936,13 @@ static void ccw_machine_9_2_instance_options(MachineState *machine) static void ccw_machine_9_2_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_S390_PCI_DEVICE, "relaxed-translation", "off", }, + }; + ccw_machine_10_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_CCW_MACHINE(9, 2); diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h index 2c43ea123f..04944d4fed 100644 --- a/include/hw/s390x/s390-pci-bus.h +++ b/include/hw/s390x/s390-pci-bus.h @@ -277,6 +277,7 @@ struct S390PCIIOMMU { AddressSpace as; MemoryRegion mr; IOMMUMemoryRegion iommu_mr; + MemoryRegion *dm_mr; bool enabled; uint64_t g_iota; uint64_t pba; @@ -362,6 +363,7 @@ struct S390PCIBusDevice { bool interp; bool forwarding_assist; bool aif; + bool rtr_avail; QTAILQ_ENTRY(S390PCIBusDevice) link; }; @@ -389,6 +391,7 @@ int pci_chsc_sei_nt2_have_event(void); void s390_pci_sclp_configure(SCCB *sccb); void s390_pci_sclp_deconfigure(SCCB *sccb); void s390_pci_iommu_enable(S390PCIIOMMU *iommu); +void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu); void s390_pci_iommu_disable(S390PCIIOMMU *iommu); void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, uint64_t faddr, uint32_t e); From d9b5dfc7122559e5b5959ecf534788b90c3dd102 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Wed, 26 Feb 2025 16:00:13 -0500 Subject: [PATCH 2307/2892] s390x/pci: indicate QEMU supports relaxed translation for passthrough Specifying this bit in the guest CLP response indicates that the guest can optionally choose to skip translation and instead use identity-mapped operations. Tested-by: Niklas Schnelle Reviewed-by: Niklas Schnelle Signed-off-by: Matthew Rosato Message-ID: <20250226210013.238349-3-mjrosato@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-vfio.c | 5 ++++- include/hw/s390x/s390-pci-clp.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 443e222912..6236ac7f1e 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -238,8 +238,11 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, pbdev->pci_group = s390_group_create(pbdev->zpci_fn.pfgid, start_gid); resgrp = &pbdev->pci_group->zpci_group; + if (pbdev->rtr_avail) { + resgrp->fr |= CLP_RSP_QPCIG_MASK_RTR; + } if (cap->flags & VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH) { - resgrp->fr = 1; + resgrp->fr |= CLP_RSP_QPCIG_MASK_REFRESH; } resgrp->dasm = cap->dasm; resgrp->msia = cap->msi_addr; diff --git a/include/hw/s390x/s390-pci-clp.h b/include/hw/s390x/s390-pci-clp.h index 03b7f9ba5f..6a635d693b 100644 --- a/include/hw/s390x/s390-pci-clp.h +++ b/include/hw/s390x/s390-pci-clp.h @@ -158,6 +158,7 @@ typedef struct ClpRspQueryPciGrp { #define CLP_RSP_QPCIG_MASK_NOI 0xfff uint16_t i; uint8_t version; +#define CLP_RSP_QPCIG_MASK_RTR 0x20 #define CLP_RSP_QPCIG_MASK_FRAME 0x2 #define CLP_RSP_QPCIG_MASK_REFRESH 0x1 uint8_t fr; From a674db604db3fc4ef5267243dc991852f1f1bebc Mon Sep 17 00:00:00 2001 From: JianChunfu Date: Fri, 7 Mar 2025 10:08:18 +0000 Subject: [PATCH 2308/2892] hw/arm/smmu-common: Remove the repeated ttb field SMMUTransCfg->ttb is never used in QEMU, TT base address can be accessed by SMMUTransCfg->tt[i]->ttb. Signed-off-by: JianChunfu Reviewed-by: Eric Auger Message-id: 20250221031034.69822-1-jansef.jian@hj-micro.com Signed-off-by: Peter Maydell --- include/hw/arm/smmu-common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index d1a4a64551..e5ad55bbae 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -110,7 +110,6 @@ typedef struct SMMUTransCfg { /* Used by stage-1 only. */ bool aa64; /* arch64 or aarch32 translation table */ bool record_faults; /* record fault events */ - uint64_t ttb; /* TT base address */ uint8_t oas; /* output address width */ uint8_t tbi; /* Top Byte Ignore */ int asid; From 3b2e22c0bbe2ce07123d93961d52f17644562cd7 Mon Sep 17 00:00:00 2001 From: Patrick Venture Date: Fri, 7 Mar 2025 10:08:19 +0000 Subject: [PATCH 2309/2892] hw/gpio: npcm7xx: fixup out-of-bounds access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reg isn't validated to be a possible register before it's dereferenced for one case. The mmio space registered for the gpio device is 4KiB but there aren't that many registers in the struct. Cc: qemu-stable@nongnu.org Fixes: 526dbbe0874 ("hw/gpio: Add GPIO model for Nuvoton NPCM7xx") Signed-off-by: Patrick Venture Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250226024603.493148-1-venture@google.com Signed-off-by: Peter Maydell --- hw/gpio/npcm7xx_gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 23e67424c9..2916056fae 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -220,8 +220,6 @@ static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, return; } - diff = s->regs[reg] ^ value; - switch (reg) { case NPCM7XX_GPIO_TLOCK1: case NPCM7XX_GPIO_TLOCK2: @@ -242,6 +240,7 @@ static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, case NPCM7XX_GPIO_PU: case NPCM7XX_GPIO_PD: case NPCM7XX_GPIO_IEM: + diff = s->regs[reg] ^ value; s->regs[reg] = value; npcm7xx_gpio_update_pins(s, diff); break; From 5f6b9b0564b69bd9548860419a70e79579d64aeb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 7 Mar 2025 10:08:19 +0000 Subject: [PATCH 2310/2892] tests/functional/test_arm_sx1: Check whether the serial console is working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel that is used in the sx1 test prints the usual Linux log onto the serial console, but this test currently ignores it. To make sure that the serial device is working properly, let's check for some strings in the output here. While we're at it, also add the test to the corresponding section in the MAINTAINERS file. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250226104833.1176253-1-thuth@redhat.com Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + tests/functional/test_arm_sx1.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5df6020ed5..699cf59e0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2010,6 +2010,7 @@ S: Maintained F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst +F: tests/functional/test_arm_sx1.py IPack M: Alberto Garcia diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py index 4dd1e1859f..25800b388c 100755 --- a/tests/functional/test_arm_sx1.py +++ b/tests/functional/test_arm_sx1.py @@ -43,7 +43,8 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-append', f'kunit.enable=0 rdinit=/sbin/init {self.CONSOLE_ARGS}') self.vm.add_args('-no-reboot') self.launch_kernel(zimage_path, - initrd=initrd_path) + initrd=initrd_path, + wait_for='Boot successful') self.vm.wait(timeout=120) def test_arm_sx1_sd(self): @@ -54,7 +55,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-no-reboot') self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}') - self.launch_kernel(zimage_path) + self.launch_kernel(zimage_path, wait_for='Boot successful') self.vm.wait(timeout=120) def test_arm_sx1_flash(self): @@ -65,7 +66,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-no-reboot') self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}') - self.launch_kernel(zimage_path) + self.launch_kernel(zimage_path, wait_for='Boot successful') self.vm.wait(timeout=120) if __name__ == '__main__': From db6c2192839ee0282d38f6f6666a87e0629fcd13 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:19 +0000 Subject: [PATCH 2311/2892] target/arm: Apply correct timer offset when calculating deadlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are calculating timer deadlines, the correct definition of whether or not to apply an offset to the physical count is described in the Arm ARM DDI4087 rev L.a section D12.2.4.1. This is different from when the offset should be applied for a direct read of the counter sysreg. We got this right for the EL1 physical timer and for the EL1 virtual timer, but got all the rest wrong: they should be using a zero offset always. Factor the offset calculation out into a function that has a comment documenting exactly which offset it is calculating and which gets the HYP, SEC, and HYPVIRT cases right. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-2-peter.maydell@linaro.org --- target/arm/helper.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 71dead7241..7f341d753c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2460,6 +2460,32 @@ static uint64_t gt_phys_cnt_offset(CPUARMState *env) return gt_phys_raw_cnt_offset(env); } +static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) +{ + /* + * Return the timer offset to use for indirect accesses to the timer. + * This is the Offset value as defined in D12.2.4.1 "Operation of the + * CompareValue views of the timers". + * + * The condition here is not always the same as the condition for + * whether to apply an offset register when doing a direct read of + * the counter sysreg; those conditions are described in the + * access pseudocode for each counter register. + */ + switch (timeridx) { + case GTIMER_PHYS: + return gt_phys_raw_cnt_offset(env); + case GTIMER_VIRT: + return env->cp15.cntvoff_el2; + case GTIMER_HYP: + case GTIMER_SEC: + case GTIMER_HYPVIRT: + return 0; + default: + g_assert_not_reached(); + } +} + static void gt_recalc_timer(ARMCPU *cpu, int timeridx) { ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; @@ -2469,8 +2495,7 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) * Timer enabled: calculate and set current ISTATUS, irq, and * reset timer to when ISTATUS next has to change */ - uint64_t offset = timeridx == GTIMER_VIRT ? - cpu->env.cp15.cntvoff_el2 : gt_phys_raw_cnt_offset(&cpu->env); + uint64_t offset = gt_indirect_access_timer_offset(&cpu->env, timeridx); uint64_t count = gt_get_countervalue(&cpu->env); /* Note that this must be unsigned 64 bit arithmetic: */ int istatus = count - offset >= gt->cval; From 5709038aa8b4d58b8c201ed53c327074173a35c6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:20 +0000 Subject: [PATCH 2312/2892] target/arm: Don't apply CNTVOFF_EL2 for EL2_VIRT timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CNTVOFF_EL2 offset register should only be applied for accessses to CNTVCT_EL0 and for the EL1 virtual timer (CNTV_*). We were incorrectly applying it for the EL2 virtual timer (CNTHV_*). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-3-peter.maydell@linaro.org --- target/arm/helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7f341d753c..5729b313f8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2604,7 +2604,6 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, switch (timeridx) { case GTIMER_VIRT: - case GTIMER_HYPVIRT: offset = gt_virt_cnt_offset(env); break; case GTIMER_PHYS: @@ -2624,7 +2623,6 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, switch (timeridx) { case GTIMER_VIRT: - case GTIMER_HYPVIRT: offset = gt_virt_cnt_offset(env); break; case GTIMER_PHYS: From bdd641541fbef0a27bf9f60e7eba6f8a31d4706c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:20 +0000 Subject: [PATCH 2313/2892] target/arm: Make CNTPS_* UNDEF from Secure EL1 when Secure EL2 is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we added Secure EL2 support, we missed that this needs an update to the access code for the EL3 physical timer registers. These are supposed to UNDEF from Secure EL1 when Secure EL2 is enabled. (Note for stable backporting: for backports to branches where CP_ACCESS_UNDEFINED is not defined, the old name to use instead is CP_ACCESS_TRAP_UNCATEGORIZED.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-4-peter.maydell@linaro.org --- target/arm/helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5729b313f8..5b6de446ac 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2387,6 +2387,9 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, if (!arm_is_secure(env)) { return CP_ACCESS_UNDEFINED; } + if (arm_is_el2_enabled(env)) { + return CP_ACCESS_UNDEFINED; + } if (!(env->cp15.scr_el3 & SCR_ST)) { return CP_ACCESS_TRAP_EL3; } From 4aecd4b442d7abb4355896d878ffc9b028625b01 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:20 +0000 Subject: [PATCH 2314/2892] target/arm: Always apply CNTVOFF_EL2 for CNTV_TVAL_EL02 accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we handle CNTV_TVAL_EL02 by calling gt_tval_read() for the EL1 virt timer. This is almost correct, but the underlying CNTV_TVAL_EL0 register behaves slightly differently. CNTV_TVAL_EL02 always applies the CNTVOFF_EL2 offset; CNTV_TVAL_EL0 doesn't do so if we're at EL2 and HCR_EL2.E2H is 1. We were getting this wrong, because we ended up in gt_virt_cnt_offset() and did the E2H check. Factor out the tval read/write calculation from the selection of the offset, so that we can special case gt_virt_tval_read() and gt_virt_tval_write() to unconditionally pass CNTVOFF_EL2. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-5-peter.maydell@linaro.org --- target/arm/helper.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5b6de446ac..acf77793c7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2600,6 +2600,12 @@ static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_recalc_timer(env_archcpu(env), timeridx); } +static uint64_t do_tval_read(CPUARMState *env, int timeridx, uint64_t offset) +{ + return (uint32_t)(env->cp15.c14_timer[timeridx].cval - + (gt_get_countervalue(env) - offset)); +} + static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx) { @@ -2614,8 +2620,16 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, break; } - return (uint32_t)(env->cp15.c14_timer[timeridx].cval - - (gt_get_countervalue(env) - offset)); + return do_tval_read(env, timeridx, offset); +} + +static void do_tval_write(CPUARMState *env, int timeridx, uint64_t value, + uint64_t offset) +{ + trace_arm_gt_tval_write(timeridx, value); + env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + + sextract64(value, 0, 32); + gt_recalc_timer(env_archcpu(env), timeridx); } static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2632,11 +2646,7 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, offset = gt_phys_cnt_offset(env); break; } - - trace_arm_gt_tval_write(timeridx, value); - env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + - sextract64(value, 0, 32); - gt_recalc_timer(env_archcpu(env), timeridx); + do_tval_write(env, timeridx, value, offset); } static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2768,13 +2778,21 @@ static void gt_virt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_virt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_tval_read(env, ri, GTIMER_VIRT); + /* + * This is CNTV_TVAL_EL02; unlike the underlying CNTV_TVAL_EL0 + * we always apply CNTVOFF_EL2. Special case that here rather + * than going into the generic gt_tval_read() and then having + * to re-detect that it's this register. + * Note that the accessfn/perms mean we know we're at EL2 or EL3 here. + */ + return do_tval_read(env, GTIMER_VIRT, env->cp15.cntvoff_el2); } static void gt_virt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - gt_tval_write(env, ri, GTIMER_VIRT, value); + /* Similarly for writes to CNTV_TVAL_EL02 */ + do_tval_write(env, GTIMER_VIRT, value, env->cp15.cntvoff_el2); } static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, From 02c648a0a103a1a7b2c077ec5a81da9907f45544 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2315/2892] target/arm: Refactor handling of timer offset for direct register accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading or writing the timer registers, sometimes we need to apply one of the timer offsets. Specifically, this happens for direct reads of the counter registers CNTPCT_EL0 and CNTVCT_EL0 (and their self-synchronized variants CNTVCTSS_EL0 and CNTPCTSS_EL0). It also applies for direct reads and writes of the CNT*_TVAL_EL* registers that provide the 32-bit downcounting view of each timer. We currently do this with duplicated code in gt_tval_read() and gt_tval_write() and a special-case in gt_virt_cnt_read() and gt_cnt_read(). Refactor this so that we handle it all in a single function gt_direct_access_timer_offset(), to parallel how we handle the offset for indirect accesses. The call in the WFIT helper previously to gt_virt_cnt_offset() is now to gt_direct_access_timer_offset(); this is the correct behaviour, but it's not immediately obvious that it shouldn't be considered an indirect access, so we add an explanatory comment. This commit should make no behavioural changes. (Cc to stable because the following bugfix commit will depend on this one.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-6-peter.maydell@linaro.org --- target/arm/helper.c | 103 +++++++++++++++++++------------------ target/arm/internals.h | 5 +- target/arm/tcg/op_helper.c | 8 ++- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index acf77793c7..54147d9761 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2455,14 +2455,6 @@ static uint64_t gt_phys_raw_cnt_offset(CPUARMState *env) return 0; } -static uint64_t gt_phys_cnt_offset(CPUARMState *env) -{ - if (arm_current_el(env) >= 2) { - return 0; - } - return gt_phys_raw_cnt_offset(env); -} - static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) { /* @@ -2489,6 +2481,52 @@ static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) } } +uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx) +{ + /* + * Return the timer offset to use for direct accesses to the + * counter registers CNTPCT and CNTVCT, and for direct accesses + * to the CNT*_TVAL registers. + * + * This isn't exactly the same as the indirect-access offset, + * because here we also care about what EL the register access + * is being made from. + * + * This corresponds to the access pseudocode for the registers. + */ + uint64_t hcr; + + switch (timeridx) { + case GTIMER_PHYS: + if (arm_current_el(env) >= 2) { + return 0; + } + return gt_phys_raw_cnt_offset(env); + case GTIMER_VIRT: + switch (arm_current_el(env)) { + case 2: + hcr = arm_hcr_el2_eff(env); + if (hcr & HCR_E2H) { + return 0; + } + break; + case 0: + hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + return 0; + } + break; + } + return env->cp15.cntvoff_el2; + case GTIMER_HYP: + case GTIMER_SEC: + case GTIMER_HYPVIRT: + return 0; + default: + g_assert_not_reached(); + } +} + static void gt_recalc_timer(ARMCPU *cpu, int timeridx) { ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; @@ -2561,34 +2599,14 @@ static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env) - gt_phys_cnt_offset(env); -} - -uint64_t gt_virt_cnt_offset(CPUARMState *env) -{ - uint64_t hcr; - - switch (arm_current_el(env)) { - case 2: - hcr = arm_hcr_el2_eff(env); - if (hcr & HCR_E2H) { - return 0; - } - break; - case 0: - hcr = arm_hcr_el2_eff(env); - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - return 0; - } - break; - } - - return env->cp15.cntvoff_el2; + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_PHYS); + return gt_get_countervalue(env) - offset; } static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env) - gt_virt_cnt_offset(env); + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_VIRT); + return gt_get_countervalue(env) - offset; } static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2609,16 +2627,7 @@ static uint64_t do_tval_read(CPUARMState *env, int timeridx, uint64_t offset) static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx) { - uint64_t offset = 0; - - switch (timeridx) { - case GTIMER_VIRT: - offset = gt_virt_cnt_offset(env); - break; - case GTIMER_PHYS: - offset = gt_phys_cnt_offset(env); - break; - } + uint64_t offset = gt_direct_access_timer_offset(env, timeridx); return do_tval_read(env, timeridx, offset); } @@ -2636,16 +2645,8 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx, uint64_t value) { - uint64_t offset = 0; + uint64_t offset = gt_direct_access_timer_offset(env, timeridx); - switch (timeridx) { - case GTIMER_VIRT: - offset = gt_virt_cnt_offset(env); - break; - case GTIMER_PHYS: - offset = gt_phys_cnt_offset(env); - break; - } do_tval_write(env, timeridx, value, offset); } diff --git a/target/arm/internals.h b/target/arm/internals.h index a6ff228f9f..bb96238919 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1819,9 +1819,10 @@ int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); uint64_t gt_get_countervalue(CPUARMState *env); /* * Return the currently applicable offset between the system counter - * and CNTVCT_EL0 (this will be either 0 or the value of CNTVOFF_EL2). + * and the counter for the specified timer, as used for direct register + * accesses. */ -uint64_t gt_virt_cnt_offset(CPUARMState *env); +uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx); /* * Return mask of ARMMMUIdxBit values corresponding to an "invalidate diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 02c375d196..30786fd1ff 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -427,7 +427,13 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) int target_el = check_wfx_trap(env, false, &excp); /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ uint64_t cntval = gt_get_countervalue(env); - uint64_t offset = gt_virt_cnt_offset(env); + /* + * We want the value that we would get if we read CNTVCT_EL0 from + * the current exception level, so the direct_access offset, not + * the indirect_access one. Compare the pseudocode LocalTimeoutEvent(), + * which calls VirtualCounterTimer(). + */ + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_VIRT); uint64_t cntvct = cntval - offset; uint64_t nexttick; From f9f99d7ca522339c1de2292f132bb8ddc3471c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2316/2892] target/arm: Implement SEL2 physical and virtual timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When FEAT_SEL2 was implemented the SEL2 timers were missed. This shows up when building the latest Hafnium with SPMC_AT_EL=2. The actual implementation utilises the same logic as the rest of the timers so all we need to do is: - define the timers and their access functions - conditionally add the correct system registers - create a new accessfn as the rules are subtly different to the existing secure timer Fixes: e9152ee91c (target/arm: add ARMv8.4-SEL2 system registers) Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell Message-id: 20250204125009.2281315-7-peter.maydell@linaro.org Cc: qemu-stable@nongnu.org Cc: Andrei Homescu Cc: Arve Hjønnevåg Cc: Rémi Denis-Courmont [PMM: CP_ACCESS_TRAP_UNCATEGORIZED -> CP_ACCESS_UNDEFINED; offset logic now in gt_{indirect,direct}_access_timer_offset() ] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- include/hw/arm/bsa.h | 2 + target/arm/cpu.c | 4 ++ target/arm/cpu.h | 2 + target/arm/gtimer.h | 4 +- target/arm/helper.c | 163 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 1 deletion(-) diff --git a/include/hw/arm/bsa.h b/include/hw/arm/bsa.h index 8eaab603c0..13ed2d2ac1 100644 --- a/include/hw/arm/bsa.h +++ b/include/hw/arm/bsa.h @@ -22,6 +22,8 @@ #define QEMU_ARM_BSA_H /* These are architectural INTID values */ +#define ARCH_TIMER_S_EL2_VIRT_IRQ 19 +#define ARCH_TIMER_S_EL2_IRQ 20 #define VIRTUAL_PMU_IRQ 23 #define ARCH_GIC_MAINT_IRQ 25 #define ARCH_TIMER_NS_EL2_IRQ 26 diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 948defa3f5..cacbbc615a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2069,6 +2069,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) arm_gt_stimer_cb, cpu); cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, arm_gt_hvtimer_cb, cpu); + cpu->gt_timer[GTIMER_S_EL2_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_sel2timer_cb, cpu); + cpu->gt_timer[GTIMER_S_EL2_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_sel2vtimer_cb, cpu); } #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 215845c7e2..8f52380c88 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1171,6 +1171,8 @@ void arm_gt_vtimer_cb(void *opaque); void arm_gt_htimer_cb(void *opaque); void arm_gt_stimer_cb(void *opaque); void arm_gt_hvtimer_cb(void *opaque); +void arm_gt_sel2timer_cb(void *opaque); +void arm_gt_sel2vtimer_cb(void *opaque); unsigned int gt_cntfrq_period_ns(ARMCPU *cpu); void gt_rme_post_el_change(ARMCPU *cpu, void *opaque); diff --git a/target/arm/gtimer.h b/target/arm/gtimer.h index b992941bef..0e89b8e58d 100644 --- a/target/arm/gtimer.h +++ b/target/arm/gtimer.h @@ -15,7 +15,9 @@ enum { GTIMER_HYP = 2, GTIMER_SEC = 3, GTIMER_HYPVIRT = 4, -#define NUM_GTIMERS 5 + GTIMER_S_EL2_PHYS = 5, /* CNTHPS_* ; only if FEAT_SEL2 */ + GTIMER_S_EL2_VIRT = 6, /* CNTHVS_* ; only if FEAT_SEL2 */ +#define NUM_GTIMERS 7 }; #endif diff --git a/target/arm/helper.c b/target/arm/helper.c index 54147d9761..407efe9427 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2404,6 +2404,45 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, } } +static CPAccessResult gt_sel2timer_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* + * The AArch64 register view of the secure EL2 timers are mostly + * accessible from EL3 and EL2 although can also be trapped to EL2 + * from EL1 depending on nested virt config. + */ + switch (arm_current_el(env)) { + case 0: /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + case 1: + if (!arm_is_secure(env)) { + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + } else if (arm_hcr_el2_eff(env) & HCR_NV) { + /* Aarch64.SystemAccessTrap(EL2, 0x18) */ + return CP_ACCESS_TRAP_EL2; + } + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + case 2: + if (!arm_is_secure(env)) { + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + } + return CP_ACCESS_OK; + case 3: + if (env->cp15.scr_el3 & SCR_EEL2) { + return CP_ACCESS_OK; + } else { + return CP_ACCESS_UNDEFINED; + } + default: + g_assert_not_reached(); + } +} + uint64_t gt_get_countervalue(CPUARMState *env) { ARMCPU *cpu = env_archcpu(env); @@ -2475,6 +2514,8 @@ static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) case GTIMER_HYP: case GTIMER_SEC: case GTIMER_HYPVIRT: + case GTIMER_S_EL2_PHYS: + case GTIMER_S_EL2_VIRT: return 0; default: g_assert_not_reached(); @@ -2521,6 +2562,8 @@ uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx) case GTIMER_HYP: case GTIMER_SEC: case GTIMER_HYPVIRT: + case GTIMER_S_EL2_PHYS: + case GTIMER_S_EL2_VIRT: return 0; default: g_assert_not_reached(); @@ -2953,6 +2996,62 @@ static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_ctl_write(env, ri, GTIMER_SEC, value); } +static void gt_sec_pel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_S_EL2_PHYS); +} + +static void gt_sec_pel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static uint64_t gt_sec_pel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_S_EL2_PHYS); +} + +static void gt_sec_pel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static void gt_sec_pel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static void gt_sec_vel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_S_EL2_VIRT); +} + +static void gt_sec_vel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + +static uint64_t gt_sec_vel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_S_EL2_VIRT); +} + +static void gt_sec_vel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + +static void gt_sec_vel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + static void gt_hv_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) { gt_timer_reset(env, ri, GTIMER_HYPVIRT); @@ -3009,6 +3108,20 @@ void arm_gt_stimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_SEC); } +void arm_gt_sel2timer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_S_EL2_PHYS); +} + +void arm_gt_sel2vtimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_S_EL2_VIRT); +} + void arm_gt_hvtimer_cb(void *opaque) { ARMCPU *cpu = opaque; @@ -5733,6 +5846,56 @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = { .access = PL2_RW, .accessfn = sel2_access, .nv2_redirect_offset = 0x48, .fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) }, +#ifndef CONFIG_USER_ONLY + /* Secure EL2 Physical Timer */ + { .name = "CNTHPS_TVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .readfn = gt_sec_pel2_tval_read, + .writefn = gt_sec_pel2_tval_write, + .resetfn = gt_sec_pel2_timer_reset, + }, + { .name = "CNTHPS_CTL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 1, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].ctl), + .resetvalue = 0, + .writefn = gt_sec_pel2_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTHPS_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 2, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].cval), + .writefn = gt_sec_pel2_cval_write, .raw_writefn = raw_write, + }, + /* Secure EL2 Virtual Timer */ + { .name = "CNTHVS_TVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .readfn = gt_sec_vel2_tval_read, + .writefn = gt_sec_vel2_tval_write, + .resetfn = gt_sec_vel2_timer_reset, + }, + { .name = "CNTHVS_CTL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 1, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].ctl), + .resetvalue = 0, + .writefn = gt_sec_vel2_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTHVS_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 2, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].cval), + .writefn = gt_sec_vel2_cval_write, .raw_writefn = raw_write, + }, +#endif }; static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, From 47e2c5510f8c13310bfe738ebaa913bb52feca2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2317/2892] target/arm: Document the architectural names of our GTIMERs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are about to add more physical and virtual timers let's make it clear what each timer does. Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell Message-id: 20250204125009.2281315-8-peter.maydell@linaro.org [PMM: Add timer register name prefix to each comment] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/gtimer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/gtimer.h b/target/arm/gtimer.h index 0e89b8e58d..d49c63cbf8 100644 --- a/target/arm/gtimer.h +++ b/target/arm/gtimer.h @@ -10,11 +10,11 @@ #define TARGET_ARM_GTIMER_H enum { - GTIMER_PHYS = 0, - GTIMER_VIRT = 1, - GTIMER_HYP = 2, - GTIMER_SEC = 3, - GTIMER_HYPVIRT = 4, + GTIMER_PHYS = 0, /* CNTP_* ; EL1 physical timer */ + GTIMER_VIRT = 1, /* CNTV_* ; EL1 virtual timer */ + GTIMER_HYP = 2, /* CNTHP_* ; EL2 physical timer */ + GTIMER_SEC = 3, /* CNTPS_* ; EL3 physical timer */ + GTIMER_HYPVIRT = 4, /* CNTHV_* ; EL2 virtual timer ; only if FEAT_VHE */ GTIMER_S_EL2_PHYS = 5, /* CNTHPS_* ; only if FEAT_SEL2 */ GTIMER_S_EL2_VIRT = 6, /* CNTHVS_* ; only if FEAT_SEL2 */ #define NUM_GTIMERS 7 From 5dcaea8bcd82972add29eef350547f922fb4caa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2318/2892] hw/arm: enable secure EL2 timers for virt machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell Message-id: 20250204125009.2281315-9-peter.maydell@linaro.org Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 904c698b14..a96452f17a 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -882,6 +882,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, + [GTIMER_S_EL2_PHYS] = ARCH_TIMER_S_EL2_IRQ, + [GTIMER_S_EL2_VIRT] = ARCH_TIMER_S_EL2_VIRT_IRQ, }; for (unsigned irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { From 9a9d9e82093efa22e3e2bdaac0f24c823f8786f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2319/2892] hw/arm: enable secure EL2 timers for sbsa machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250204125009.2281315-10-peter.maydell@linaro.org Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index e720de3064..aa09d7a091 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -484,6 +484,8 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, + [GTIMER_S_EL2_PHYS] = ARCH_TIMER_S_EL2_IRQ, + [GTIMER_S_EL2_VIRT] = ARCH_TIMER_S_EL2_VIRT_IRQ, }; for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { From cde3247651dc998da5dc1005148302a90d72f21f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2320/2892] target/arm: Correct LDRD atomicity and fault behaviour Our LDRD implementation is wrong in two respects: * if the address is 4-aligned and the load crosses a page boundary and the second load faults and the first load was to the base register (as in cases like "ldrd r2, r3, [r2]", then we must not update the base register before taking the fault * if the address is 8-aligned the access must be a 64-bit single-copy atomic access, not two 32-bit accesses Rewrite the handling of the loads in LDRD to use a single tcg_gen_qemu_ld_i64() and split the result into the destination registers. This allows us to get the atomicity requirements right, and also implicitly means that we won't update the base register too early for the page-crossing case. Note that because we no longer increment 'addr' by 4 in the course of performing the LDRD we must change the adjustment value we pass to op_addr_ri_post() and op_addr_rr_post(): it no longer needs to subtract 4 to get the correct value to use if doing base register writeback. STRD has the same problem with not getting the atomicity right; we will deal with that in the following commit. Cc: qemu-stable@nongnu.org Reported-by: Stu Grossman Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250227142746.1698904-2-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 70 +++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index d8225b77c8..93772da39a 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -5003,10 +5003,49 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, return true; } +static void do_ldrd_load(DisasContext *s, TCGv_i32 addr, int rt, int rt2) +{ + /* + * LDRD is required to be an atomic 64-bit access if the + * address is 8-aligned, two atomic 32-bit accesses if + * it's only 4-aligned, and to give an alignment fault + * if it's not 4-aligned. This is MO_ALIGN_4 | MO_ATOM_SUBALIGN. + * Rt is always the word from the lower address, and Rt2 the + * data from the higher address, regardless of endianness. + * So (like gen_load_exclusive) we avoid gen_aa32_ld_i64() + * so we don't get its SCTLR_B check, and instead do a 64-bit access + * using MO_BE if appropriate and then split the two halves. + * + * For M-profile, and for A-profile before LPAE, the 64-bit + * atomicity is not required. We could model that using + * the looser MO_ATOM_IFALIGN_PAIR, but providing a higher + * level of atomicity than required is harmless (we would not + * currently generate better code for IFALIGN_PAIR here). + * + * This also gives us the correct behaviour of not updating + * rt if the load of rt2 faults; this is required for cases + * like "ldrd r2, r3, [r2]" where rt is also the base register. + */ + int mem_idx = get_mem_index(s); + MemOp opc = MO_64 | MO_ALIGN_4 | MO_ATOM_SUBALIGN | s->be_data; + TCGv taddr = gen_aa32_addr(s, addr, opc); + TCGv_i64 t64 = tcg_temp_new_i64(); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 tmp2 = tcg_temp_new_i32(); + + tcg_gen_qemu_ld_i64(t64, taddr, mem_idx, opc); + if (s->be_data == MO_BE) { + tcg_gen_extr_i64_i32(tmp2, tmp, t64); + } else { + tcg_gen_extr_i64_i32(tmp, tmp2, t64); + } + store_reg(s, rt, tmp); + store_reg(s, rt2, tmp2); +} + static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; if (!ENABLE_ARCH_5TE) { return false; @@ -5017,18 +5056,10 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) } addr = op_addr_rr_pre(s, a); - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt, tmp); - - tcg_gen_addi_i32(addr, addr, 4); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt + 1, tmp); + do_ldrd_load(s, addr, a->rt, a->rt + 1); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_rr_post(s, a, addr, -4); + op_addr_rr_post(s, a, addr, 0); return true; } @@ -5152,23 +5183,14 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; addr = op_addr_ri_pre(s, a); - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt, tmp); - - tcg_gen_addi_i32(addr, addr, 4); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, rt2, tmp); + do_ldrd_load(s, addr, a->rt, rt2); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_ri_post(s, a, addr, -4); + op_addr_ri_post(s, a, addr, 0); return true; } From ee786ca115045a2b7e86ac3073b0761cb99e0d49 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 2321/2892] target/arm: Correct STRD atomicity Our STRD implementation doesn't correctly implement the requirement: * if the address is 8-aligned the access must be a 64-bit single-copy atomic access, not two 32-bit accesses Rewrite the handling of STRD to use a single tcg_gen_qemu_st_i64() of a value produced by concatenating the two 32 bit source registers. This allows us to get the atomicity right. As with the LDRD change, now that we don't update 'addr' in the course of performing the store we need to adjust the offset we pass to op_addr_ri_post() and op_addr_rr_post(). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250227142746.1698904-3-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 59 +++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 93772da39a..404a254678 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -5063,10 +5063,42 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) return true; } +static void do_strd_store(DisasContext *s, TCGv_i32 addr, int rt, int rt2) +{ + /* + * STRD is required to be an atomic 64-bit access if the + * address is 8-aligned, two atomic 32-bit accesses if + * it's only 4-aligned, and to give an alignment fault + * if it's not 4-aligned. + * Rt is always the word from the lower address, and Rt2 the + * data from the higher address, regardless of endianness. + * So (like gen_store_exclusive) we avoid gen_aa32_ld_i64() + * so we don't get its SCTLR_B check, and instead do a 64-bit access + * using MO_BE if appropriate, using a value constructed + * by putting the two halves together in the right order. + * + * As with LDRD, the 64-bit atomicity is not required for + * M-profile, or for A-profile before LPAE, and we provide + * the higher guarantee always for simplicity. + */ + int mem_idx = get_mem_index(s); + MemOp opc = MO_64 | MO_ALIGN_4 | MO_ATOM_SUBALIGN | s->be_data; + TCGv taddr = gen_aa32_addr(s, addr, opc); + TCGv_i32 t1 = load_reg(s, rt); + TCGv_i32 t2 = load_reg(s, rt2); + TCGv_i64 t64 = tcg_temp_new_i64(); + + if (s->be_data == MO_BE) { + tcg_gen_concat_i32_i64(t64, t2, t1); + } else { + tcg_gen_concat_i32_i64(t64, t1, t2); + } + tcg_gen_qemu_st_i64(t64, taddr, mem_idx, opc); +} + static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; if (!ENABLE_ARCH_5TE) { return false; @@ -5077,15 +5109,9 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) } addr = op_addr_rr_pre(s, a); - tmp = load_reg(s, a->rt); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + do_strd_store(s, addr, a->rt, a->rt + 1); - tcg_gen_addi_i32(addr, addr, 4); - - tmp = load_reg(s, a->rt + 1); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - - op_addr_rr_post(s, a, addr, -4); + op_addr_rr_post(s, a, addr, 0); return true; } @@ -5213,20 +5239,13 @@ static bool trans_LDRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; addr = op_addr_ri_pre(s, a); - tmp = load_reg(s, a->rt); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + do_strd_store(s, addr, a->rt, rt2); - tcg_gen_addi_i32(addr, addr, 4); - - tmp = load_reg(s, rt2); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - - op_addr_ri_post(s, a, addr, -4); + op_addr_ri_post(s, a, addr, 0); return true; } From 5be4419c573e78c21be953a4c31947f3087931a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:22 +0000 Subject: [PATCH 2322/2892] target/arm: Drop unused address_offset from op_addr_{rr, ri}_post() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the callers of op_addr_rr_post() and op_addr_ri_post() now pass in zero for the address_offset, so we can remove that argument. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250227142746.1698904-4-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 404a254678..d280018138 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -4941,7 +4941,7 @@ static TCGv_i32 op_addr_rr_pre(DisasContext *s, arg_ldst_rr *a) } static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, - TCGv_i32 addr, int address_offset) + TCGv_i32 addr) { if (!a->p) { TCGv_i32 ofs = load_reg(s, a->rm); @@ -4954,7 +4954,6 @@ static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, } else if (!a->w) { return; } - tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, a->rn, addr); } @@ -4974,7 +4973,7 @@ static bool op_load_rr(DisasContext *s, arg_ldst_rr *a, * Perform base writeback before the loaded value to * ensure correct behavior with overlapping index registers. */ - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); store_reg_from_load(s, a->rt, tmp); return true; } @@ -4999,7 +4998,7 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); disas_set_da_iss(s, mop, issinfo); - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); return true; } @@ -5059,7 +5058,7 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) do_ldrd_load(s, addr, a->rt, a->rt + 1); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); return true; } @@ -5111,7 +5110,7 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) do_strd_store(s, addr, a->rt, a->rt + 1); - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); return true; } @@ -5147,13 +5146,14 @@ static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) } static void op_addr_ri_post(DisasContext *s, arg_ldst_ri *a, - TCGv_i32 addr, int address_offset) + TCGv_i32 addr) { + int address_offset = 0; if (!a->p) { if (a->u) { - address_offset += a->imm; + address_offset = a->imm; } else { - address_offset -= a->imm; + address_offset = -a->imm; } } else if (!a->w) { return; @@ -5178,7 +5178,7 @@ static bool op_load_ri(DisasContext *s, arg_ldst_ri *a, * Perform base writeback before the loaded value to * ensure correct behavior with overlapping index registers. */ - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); store_reg_from_load(s, a->rt, tmp); return true; } @@ -5203,7 +5203,7 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); disas_set_da_iss(s, mop, issinfo); - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); return true; } @@ -5216,7 +5216,7 @@ static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) do_ldrd_load(s, addr, a->rt, rt2); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); return true; } @@ -5245,7 +5245,7 @@ static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) do_strd_store(s, addr, a->rt, rt2); - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); return true; } From cc503abf4ba30ed34bbf18b3fd8eaa8046fae48b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 16:24:24 +0000 Subject: [PATCH 2323/2892] target/arm: Make dummy debug registers RAZ, not NOP In debug_helper.c we provide a few dummy versions of debug registers: * DBGVCR (AArch32 only): enable bits for vector-catch debug events * MDCCINT_EL1: interrupt enable bits for the DCC debug communications channel * DBGVCR32_EL2: the AArch64 accessor for the state in DBGVCR We implemented these only to stop Linux crashing on startup, but we chose to implement them as ARM_CP_NOP. This worked for Linux where it only cares about trying to write to these registers, but is very confusing behaviour for anything that wants to read the registers (perhaps for context state switches), because the destination register will be left with whatever random value it happened to have before the read. Model these registers instead as RAZ. Fixes: 5e8b12ffbb8c68 ("target-arm: Implement minimal DBGVCR, OSDLR_EL1, MDCCSR_EL0") Fixes: 5dbdc4342f479d ("target-arm: Implement dummy MDCCINT_EL1") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2708 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250228162424.1917269-1-peter.maydell@linaro.org --- target/arm/debug_helper.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 36bffde74e..a9a619ba6b 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -1037,7 +1037,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "DBGVCR", .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, .access = PL1_RW, .accessfn = access_tda, - .type = ARM_CP_NOP }, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * Dummy MDCCINT_EL1, since we don't implement the Debug Communications * Channel but Linux may try to access this register. The 32-bit @@ -1046,7 +1046,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tdcc, - .type = ARM_CP_NOP }, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * Dummy DBGCLAIM registers. * "The architecture does not define any functionality for the CLAIM tag bits.", @@ -1075,7 +1075,8 @@ static const ARMCPRegInfo debug_aa32_el1_reginfo[] = { { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, .access = PL2_RW, .accessfn = access_dbgvcr32, - .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, + .type = ARM_CP_CONST | ARM_CP_EL3_NO_EL2_KEEP, + .resetvalue = 0 }, }; static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { From 02ae315467cee589d02dfb89e13a2a6a8de09fc5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Feb 2025 13:58:04 +0000 Subject: [PATCH 2324/2892] util/qemu-timer.c: Don't warp timer from timerlist_rearm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we call icount_start_warp_timer() from timerlist_rearm(). This produces incorrect behaviour, because timerlist_rearm() is called, for instance, when a timer callback modifies its timer. We cannot decide here to warp the timer forwards to the next timer deadline merely because all_cpu_threads_idle() is true, because the timer callback we were called from (or some other callback later in the list of callbacks being invoked) may be about to raise a CPU interrupt and move a CPU from idle to ready. The only valid place to choose to warp the timer forward is from the main loop, when we know we have no outstanding IO or timer callbacks that might be about to wake up a CPU. For Arm guests, this bug was mostly latent until the refactoring commit f6fc36deef6abc ("target/arm/helper: Implement CNTHCTL_EL2.CNT[VP]MASK"), which exposed it because it refactored a timer callback so that it happened to call timer_mod() first and raise the interrupt second, when it had previously raised the interrupt first and called timer_mod() afterwards. This call seems to have originally derived from the pre-record-and-replay icount code, which (as of e.g. commit db1a49726c3c in 2010) in this location did a call to qemu_notify_event(), necessary to get the icount code in the vCPU round-robin thread to stop and recalculate the icount deadline when a timer was reprogrammed from the IO thread. In current QEMU, everything is done on the vCPU thread when we are in icount mode, so there's no need to try to notify another thread here. I suspect that the other reason why this call was doing icount timer warping is that it pre-dates commit efab87cf79077a from 2015, which added a call to icount_start_warp_timer() to main_loop_wait(). Once the call in timerlist_rearm() has been removed, if the timer callbacks don't cause any CPU to be woken up then we will end up calling icount_start_warp_timer() from main_loop_wait() when the rr main loop code calls rr_wait_io_event(). Remove the incorrect call from timerlist_rearm(). Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2703 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20250210135804.3526943-1-peter.maydell@linaro.org --- util/qemu-timer.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 3243d2c515..788466fe22 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -409,10 +409,6 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, static void timerlist_rearm(QEMUTimerList *timer_list) { - /* Interrupt execution to force deadline recalculation. */ - if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { - icount_start_warp_timer(); - } timerlist_notify(timer_list); } From 84e5ce68c0c2d52d97a66ff2f53697f9ced190a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 10:32:22 +0000 Subject: [PATCH 2325/2892] include/exec/memop.h: Expand comment for MO_ATOM_SUBALIGN Expand the example in the comment documenting MO_ATOM_SUBALIGN, to be clearer about the atomicity guarantees it represents. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250228103222.1838913-1-peter.maydell@linaro.org --- include/exec/memop.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/exec/memop.h b/include/exec/memop.h index acdb40a9b3..407a47d82c 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -91,8 +91,12 @@ typedef enum MemOp { * Depending on alignment, one or both will be single-copy atomic. * This is the atomicity e.g. of Arm FEAT_LSE2 LDP. * MO_ATOM_SUBALIGN: the operation is single-copy atomic by parts - * by the alignment. E.g. if the address is 0 mod 4, then each - * 4-byte subobject is single-copy atomic. + * by the alignment. E.g. if an 8-byte value is accessed at an + * address which is 0 mod 8, then the whole 8-byte access is + * single-copy atomic; otherwise, if it is accessed at 0 mod 4 + * then each 4-byte subobject is single-copy atomic; otherwise + * if it is accessed at 0 mod 2 then the four 2-byte subobjects + * are single-copy atomic. * This is the atomicity e.g. of IBM Power. * MO_ATOM_NONE: the operation has no atomicity requirements. * From 8881b691d2d3613e9d7ff596a46a451b393377a5 Mon Sep 17 00:00:00 2001 From: JianChunfu Date: Fri, 28 Feb 2025 11:14:38 +0800 Subject: [PATCH 2326/2892] hw/arm/smmu: Introduce smmu_configs_inv_sid_range() helper Use a similar terminology smmu_hash_remove_by_sid_range() as the one being used for other hash table matching functions since smmuv3_invalidate_ste() name is not self explanatory, and introduce a helper that invokes the g_hash_table_foreach_remove. No functional change intended. Signed-off-by: JianChunfu Reviewed-by: Eric Auger Message-id: 20250228031438.3916-1-jansef.jian@hj-micro.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 21 +++++++++++++++++++++ hw/arm/smmu-internal.h | 5 ----- hw/arm/smmuv3.c | 19 ++----------------- hw/arm/trace-events | 3 ++- include/hw/arm/smmu-common.h | 6 ++++++ 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 8c1b407b82..6e720e1b9a 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -225,6 +225,27 @@ static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value, ((entry->iova & ~info->mask) == info->iova); } +static gboolean +smmu_hash_remove_by_sid_range(gpointer key, gpointer value, gpointer user_data) +{ + SMMUDevice *sdev = (SMMUDevice *)key; + uint32_t sid = smmu_get_sid(sdev); + SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data; + + if (sid < sid_range->start || sid > sid_range->end) { + return false; + } + trace_smmu_config_cache_inv(sid); + return true; +} + +void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range) +{ + trace_smmu_configs_inv_sid_range(sid_range.start, sid_range.end); + g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sid_range, + &sid_range); +} + void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl) { diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h index 843bebb185..d143d296f3 100644 --- a/hw/arm/smmu-internal.h +++ b/hw/arm/smmu-internal.h @@ -141,9 +141,4 @@ typedef struct SMMUIOTLBPageInvInfo { uint64_t mask; } SMMUIOTLBPageInvInfo; -typedef struct SMMUSIDRange { - uint32_t start; - uint32_t end; -} SMMUSIDRange; - #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b40acbe024..1a96287ba9 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -903,7 +903,7 @@ static void smmuv3_flush_config(SMMUDevice *sdev) SMMUv3State *s = sdev->smmu; SMMUState *bc = &s->smmu_state; - trace_smmuv3_config_cache_inv(smmu_get_sid(sdev)); + trace_smmu_config_cache_inv(smmu_get_sid(sdev)); g_hash_table_remove(bc->configs, sdev); } @@ -1277,20 +1277,6 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage) } } -static gboolean -smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data) -{ - SMMUDevice *sdev = (SMMUDevice *)key; - uint32_t sid = smmu_get_sid(sdev); - SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data; - - if (sid < sid_range->start || sid > sid_range->end) { - return false; - } - trace_smmuv3_config_cache_inv(sid); - return true; -} - static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUState *bs = ARM_SMMU(s); @@ -1373,8 +1359,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) sid_range.end = sid_range.start + mask; trace_smmuv3_cmdq_cfgi_ste_range(sid_range.start, sid_range.end); - g_hash_table_foreach_remove(bs->configs, smmuv3_invalidate_ste, - &sid_range); + smmu_configs_inv_sid_range(bs, sid_range); break; } case SMMU_CMD_CFGI_CD: diff --git a/hw/arm/trace-events b/hw/arm/trace-events index f49cae1656..f3386bd7ae 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -22,6 +22,8 @@ smmu_iotlb_inv_asid_vmid(int asid, int vmid) "IOTLB invalidate asid=%d vmid=%d" smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_vmid_s1(int vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_iova(int asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_configs_inv_sid_range(uint32_t start, uint32_t end) "Config cache INV SID range from 0x%x to 0x%x" +smmu_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" smmu_iotlb_lookup_hit(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" smmu_iotlb_lookup_miss(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" @@ -59,7 +61,6 @@ smmuv3_cmdq_tlbi_nh(int vmid) "vmid=%d" smmuv3_cmdq_tlbi_nsnh(void) "" smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d" smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d" -smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index e5ad55bbae..e5e2d09294 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -142,6 +142,11 @@ typedef struct SMMUIOTLBKey { uint8_t level; } SMMUIOTLBKey; +typedef struct SMMUSIDRange { + uint32_t start; + uint32_t end; +} SMMUSIDRange; + struct SMMUState { /* */ SysBusDevice dev; @@ -219,6 +224,7 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, uint64_t num_pages, uint8_t ttl); +void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range); /* Unmap the range of all the notifiers registered to any IOMMU mr */ void smmu_inv_notifiers_all(SMMUState *s); From 7610317f45159b620475aafc7a94c93b66eda7e2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 18 Feb 2025 13:21:00 -0800 Subject: [PATCH 2327/2892] target/rx: Set exception vector base to 0xffffff80 The documentation says the vector is at 0xffffff80, instead of the previous value of 0xffffffc0. That value must have been a bug because the standard vector values (20, 21, 23, 25, 30) were all past the end of the array. Signed-off-by: Keith Packard Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/rx/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/rx/helper.c b/target/rx/helper.c index 7f28e72989..e8aabf40ff 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -88,7 +88,7 @@ void rx_cpu_do_interrupt(CPUState *cs) cpu_stl_data(env, env->isp, env->pc); if (vec < 0x100) { - env->pc = cpu_ldl_data(env, 0xffffffc0 + vec * 4); + env->pc = cpu_ldl_data(env, 0xffffff80 + vec * 4); } else { env->pc = cpu_ldl_data(env, env->intb + (vec & 0xff) * 4); } From 0ce0739d46983e5e88fa9c149cb305689c9d8c6f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 18 Feb 2025 13:21:01 -0800 Subject: [PATCH 2328/2892] target/rx: Remove TCG_CALL_NO_WG from helpers which write env Functions which modify TCG globals must not be marked TCG_CALL_NO_WG, as that tells the optimizer that TCG global values already loaded in machine registers are still valid, and so any changes which these helpers make to the CPU state may be ignored. The target/rx code chooses to put (among other things) all the PSW bits and also ACC into globals, so the NO_WG flag on various functions that touch the PSW or ACC is incorrect and must be removed. This includes all the floating point helper functions, because update_fpsw() will update PSW Z and S. Signed-off-by: Keith Packard [PMM: Clarified commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/rx/helper.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/target/rx/helper.h b/target/rx/helper.h index ebb4739474..8cc38b0cb7 100644 --- a/target/rx/helper.h +++ b/target/rx/helper.h @@ -4,27 +4,27 @@ DEF_HELPER_1(raise_privilege_violation, noreturn, env) DEF_HELPER_1(wait, noreturn, env) DEF_HELPER_2(rxint, noreturn, env, i32) DEF_HELPER_1(rxbrk, noreturn, env) -DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fsub, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_WG, void, env, f32, f32) -DEF_HELPER_FLAGS_2(ftoi, TCG_CALL_NO_WG, i32, env, f32) -DEF_HELPER_FLAGS_2(round, TCG_CALL_NO_WG, i32, env, f32) -DEF_HELPER_FLAGS_2(itof, TCG_CALL_NO_WG, f32, env, i32) +DEF_HELPER_3(fadd, f32, env, f32, f32) +DEF_HELPER_3(fsub, f32, env, f32, f32) +DEF_HELPER_3(fmul, f32, env, f32, f32) +DEF_HELPER_3(fdiv, f32, env, f32, f32) +DEF_HELPER_3(fcmp, void, env, f32, f32) +DEF_HELPER_2(ftoi, i32, env, f32) +DEF_HELPER_2(round, i32, env, f32) +DEF_HELPER_2(itof, f32, env, i32) DEF_HELPER_2(set_fpsw, void, env, i32) -DEF_HELPER_FLAGS_2(racw, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(set_psw_rte, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(set_psw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_2(racw, void, env, i32) +DEF_HELPER_2(set_psw_rte, void, env, i32) +DEF_HELPER_2(set_psw, void, env, i32) DEF_HELPER_1(pack_psw, i32, env) -DEF_HELPER_FLAGS_3(div, TCG_CALL_NO_WG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(scmpu, TCG_CALL_NO_WG, void, env) +DEF_HELPER_3(div, i32, env, i32, i32) +DEF_HELPER_3(divu, i32, env, i32, i32) +DEF_HELPER_1(scmpu, void, env) DEF_HELPER_1(smovu, void, env) DEF_HELPER_1(smovf, void, env) DEF_HELPER_1(smovb, void, env) DEF_HELPER_2(sstr, void, env, i32) -DEF_HELPER_FLAGS_2(swhile, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(suntil, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(rmpa, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_2(swhile, void, env, i32) +DEF_HELPER_2(suntil, void, env, i32) +DEF_HELPER_2(rmpa, void, env, i32) DEF_HELPER_1(satr, void, env) From 563b1a35ed1f1151505d4fe5f723827d1b3fd4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 6 Mar 2025 16:16:31 +0000 Subject: [PATCH 2329/2892] meson.build: default to -gsplit-dwarf for debug info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option is supported by both gcc (since 4.7) and clang (since 7.0). Not only does this make the linkers job easier by reducing the amount of ELF it needs to parse it also reduces the total build size quite considerably. In my case a default build went from 5.8G to 3.9G (vs 1.9G for --disable-debug-info). The --disable-split-debug option allows distros to keep all the info together for ease of packaging. Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250306161631.2477685-1-alex.bennee@linaro.org Signed-off-by: Paolo Bonzini --- meson.build | 6 ++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 2 ++ 3 files changed, 10 insertions(+) diff --git a/meson.build b/meson.build index 6da4eb317c..4899d896de 100644 --- a/meson.build +++ b/meson.build @@ -601,6 +601,10 @@ if get_option('tsan') qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags endif +if get_option('debug') and get_option('split_debug') + qemu_cflags += '-gsplit-dwarf' +endif + # Detect support for PT_GNU_RELRO + DT_BIND_NOW. # The combination is known as "full relro", because .got.plt is read-only too. qemu_ldflags += cc.get_supported_link_arguments('-Wl,-z,relro', '-Wl,-z,now') @@ -4583,6 +4587,8 @@ if have_rust summary_info += {'bindgen': bindgen.full_path()} summary_info += {'bindgen version': bindgen.version()} endif +# option_cflags is purely for the summary display, meson will pass +# -g/-O options directly option_cflags = (get_option('debug') ? ['-g'] : []) if get_option('optimization') != 'plain' option_cflags += ['-O' + get_option('optimization')] diff --git a/meson_options.txt b/meson_options.txt index 59d973bca0..3432123fee 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -362,6 +362,8 @@ option('debug_mutex', type: 'boolean', value: false, description: 'mutex debugging support') option('debug_stack_usage', type: 'boolean', value: false, description: 'measure coroutine stack usage') +option('split_debug', type: 'boolean', value: true, + description: 'split debug info from object files') option('qom_cast_debug', type: 'boolean', value: true, description: 'cast debugging support') option('slirp_smbd', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 3e8e00852b..aca6e68830 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -504,6 +504,8 @@ _meson_option_parse() { --disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;; --enable-strip) printf "%s" -Dstrip=true ;; --disable-strip) printf "%s" -Dstrip=false ;; + --enable-split-debug) printf "%s" -Dsplit_debug=true ;; + --disable-split-debug) printf "%s" -Dsplit_debug=false ;; --sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;; --enable-tcg) printf "%s" -Dtcg=enabled ;; --disable-tcg) printf "%s" -Dtcg=disabled ;; From 44ed2fd1ea0e5c62dca2f26bee871652a93c6837 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 30 Jul 2024 23:54:57 +0200 Subject: [PATCH 2330/2892] linux-user/main: Allow setting tb-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While qemu-system can set tb-size using -accel tcg,tb-size=n, there is no similar knob for qemu-user. Add one in a way similar to how one-insn-per-tb is already handled. Signed-off-by: Ilya Leoshkevich Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240730215532.1442-1-iii@linux.ibm.com> --- linux-user/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index 5c74c52cc5..e2ec5970be 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -72,6 +72,7 @@ char *exec_path; char real_exec_path[PATH_MAX]; static bool opt_one_insn_per_tb; +static unsigned long opt_tb_size; static const char *argv0; static const char *gdbstub; static envlist_t *envlist; @@ -425,6 +426,13 @@ static void handle_arg_one_insn_per_tb(const char *arg) opt_one_insn_per_tb = true; } +static void handle_arg_tb_size(const char *arg) +{ + if (qemu_strtoul(arg, NULL, 0, &opt_tb_size)) { + usage(EXIT_FAILURE); + } +} + static void handle_arg_strace(const char *arg) { enable_strace = true; @@ -517,6 +525,8 @@ static const struct qemu_argument arg_table[] = { {"one-insn-per-tb", "QEMU_ONE_INSN_PER_TB", false, handle_arg_one_insn_per_tb, "", "run with one guest instruction per emulated TB"}, + {"tb-size", "QEMU_TB_SIZE", true, handle_arg_tb_size, + "size", "TCG translation block cache size"}, {"strace", "QEMU_STRACE", false, handle_arg_strace, "", "log system calls"}, {"seed", "QEMU_RAND_SEED", true, handle_arg_seed, @@ -808,6 +818,8 @@ int main(int argc, char **argv, char **envp) accel_init_interfaces(ac); object_property_set_bool(OBJECT(accel), "one-insn-per-tb", opt_one_insn_per_tb, &error_abort); + object_property_set_int(OBJECT(accel), "tb-size", + opt_tb_size, &error_abort); ac->init_machine(NULL); } From 3504f104ea97ffaa89f509db8059ec1047bd62ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 5 Mar 2025 20:18:59 +0100 Subject: [PATCH 2331/2892] accel/tcg: Restrict CPU_TLB_DYN_*_BITS definitions to accel/tcg/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU_TLB_DYN_*_BITS definitions are only used by accel/tcg/cputlb.c and accel/tcg/translate-all.c. Move them to accel/tcg/tb-internal.h. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-ID: <20250305191859.71608-1-philmd@linaro.org> --- accel/tcg/tb-internal.h | 27 +++++++++++++++++++++++++++ include/exec/cpu-defs.h | 26 -------------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index 90be61f296..abd423fcf5 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -13,6 +13,33 @@ #include "exec/exec-all.h" #include "exec/translation-block.h" +#ifdef CONFIG_SOFTMMU + +#define CPU_TLB_DYN_MIN_BITS 6 +#define CPU_TLB_DYN_DEFAULT_BITS 8 + +# if HOST_LONG_BITS == 32 +/* Make sure we do not require a double-word shift for the TLB load */ +# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) +# else /* HOST_LONG_BITS == 64 */ +/* + * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == + * 2**34 == 16G of address space. This is roughly what one would expect a + * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel + * Skylake's Level-2 STLB has 16 1G entries. + * Also, make sure we do not size the TLB past the guest's address space. + */ +# ifdef TARGET_PAGE_BITS_VARY +# define CPU_TLB_DYN_MAX_BITS \ + MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) +# else +# define CPU_TLB_DYN_MAX_BITS \ + MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) +# endif +# endif + +#endif /* CONFIG_SOFTMMU */ + #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" /* diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index ae18398fa9..9f955f53fd 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -46,30 +46,4 @@ #include "exec/target_long.h" -#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG) -#define CPU_TLB_DYN_MIN_BITS 6 -#define CPU_TLB_DYN_DEFAULT_BITS 8 - -# if HOST_LONG_BITS == 32 -/* Make sure we do not require a double-word shift for the TLB load */ -# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) -# else /* HOST_LONG_BITS == 64 */ -/* - * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == - * 2**34 == 16G of address space. This is roughly what one would expect a - * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel - * Skylake's Level-2 STLB has 16 1G entries. - * Also, make sure we do not size the TLB past the guest's address space. - */ -# ifdef TARGET_PAGE_BITS_VARY -# define CPU_TLB_DYN_MAX_BITS \ - MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# else -# define CPU_TLB_DYN_MAX_BITS \ - MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# endif -# endif - -#endif /* CONFIG_SOFTMMU && CONFIG_TCG */ - #endif From 58d00538ceeef9900802bcd4b7ad613ca78c8583 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Mar 2025 13:32:36 -0800 Subject: [PATCH 2332/2892] include/exec: Move TARGET_PAGE_{SIZE,MASK,BITS} to target_page.h Re-use the TARGET_PAGE_BITS_VARY mechanism to define TARGET_PAGE_SIZE and friends when not compiling per-target. Inline qemu_target_page_{size,mask,bits} as they are now trivial. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/cpu-all.h | 21 +------------- include/exec/poison.h | 4 --- include/exec/target_page.h | 58 ++++++++++++++++++++++++++++++++++---- page-target.c | 18 ------------ page-vary-target.c | 2 -- 5 files changed, 53 insertions(+), 50 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 09f537d06f..8f7aebb088 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -105,26 +105,7 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val /* page related stuff */ #include "exec/cpu-defs.h" -#ifdef TARGET_PAGE_BITS_VARY -# include "exec/page-vary.h" -extern const TargetPageBits target_page; -# ifdef CONFIG_DEBUG_TCG -# define TARGET_PAGE_BITS ({ assert(target_page.decided); \ - target_page.bits; }) -# define TARGET_PAGE_MASK ({ assert(target_page.decided); \ - (target_long)target_page.mask; }) -# else -# define TARGET_PAGE_BITS target_page.bits -# define TARGET_PAGE_MASK ((target_long)target_page.mask) -# endif -# define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) -#else -# define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS -# define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) -# define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS) -#endif - -#define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) +#include "exec/target_page.h" CPUArchState *cpu_copy(CPUArchState *env); diff --git a/include/exec/poison.h b/include/exec/poison.h index d6d4832854..35721366d7 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -44,10 +44,6 @@ #pragma GCC poison TARGET_FMT_ld #pragma GCC poison TARGET_FMT_lu -#pragma GCC poison TARGET_PAGE_SIZE -#pragma GCC poison TARGET_PAGE_MASK -#pragma GCC poison TARGET_PAGE_BITS -#pragma GCC poison TARGET_PAGE_ALIGN #pragma GCC poison TARGET_PHYS_ADDR_SPACE_BITS #pragma GCC poison CPU_INTERRUPT_HARD diff --git a/include/exec/target_page.h b/include/exec/target_page.h index 98ffbb5c23..8e89e5cbe6 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -14,10 +14,56 @@ #ifndef EXEC_TARGET_PAGE_H #define EXEC_TARGET_PAGE_H -size_t qemu_target_page_size(void); -int qemu_target_page_mask(void); -int qemu_target_page_bits(void); -int qemu_target_page_bits_min(void); - -size_t qemu_target_pages_to_MiB(size_t pages); +/* + * If compiling per-target, get the real values. + * For generic code, reuse the mechanism for variable page size. + */ +#ifdef COMPILING_PER_TARGET +#include "cpu-param.h" +#include "exec/target_long.h" +#define TARGET_PAGE_TYPE target_long +#else +#define TARGET_PAGE_BITS_VARY +#define TARGET_PAGE_TYPE int +#endif + +#ifdef TARGET_PAGE_BITS_VARY +# include "exec/page-vary.h" +extern const TargetPageBits target_page; +# ifdef CONFIG_DEBUG_TCG +# define TARGET_PAGE_BITS ({ assert(target_page.decided); \ + target_page.bits; }) +# define TARGET_PAGE_MASK ({ assert(target_page.decided); \ + (TARGET_PAGE_TYPE)target_page.mask; }) +# else +# define TARGET_PAGE_BITS target_page.bits +# define TARGET_PAGE_MASK ((TARGET_PAGE_TYPE)target_page.mask) +# endif +# define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) +#else +# define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS +# define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) +# define TARGET_PAGE_MASK ((TARGET_PAGE_TYPE)-1 << TARGET_PAGE_BITS) +#endif + +#define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) + +static inline size_t qemu_target_page_size(void) +{ + return TARGET_PAGE_SIZE; +} + +static inline int qemu_target_page_mask(void) +{ + return TARGET_PAGE_MASK; +} + +static inline int qemu_target_page_bits(void) +{ + return TARGET_PAGE_BITS; +} + +int qemu_target_page_bits_min(void); +size_t qemu_target_pages_to_MiB(size_t pages); + #endif diff --git a/page-target.c b/page-target.c index 82211c8593..321e43d06f 100644 --- a/page-target.c +++ b/page-target.c @@ -8,24 +8,6 @@ #include "qemu/osdep.h" #include "exec/target_page.h" -#include "exec/cpu-defs.h" -#include "cpu.h" -#include "exec/cpu-all.h" - -size_t qemu_target_page_size(void) -{ - return TARGET_PAGE_SIZE; -} - -int qemu_target_page_mask(void) -{ - return TARGET_PAGE_MASK; -} - -int qemu_target_page_bits(void) -{ - return TARGET_PAGE_BITS; -} int qemu_target_page_bits_min(void) { diff --git a/page-vary-target.c b/page-vary-target.c index 343b4adb95..3f81144cda 100644 --- a/page-vary-target.c +++ b/page-vary-target.c @@ -35,7 +35,5 @@ bool set_preferred_target_page_bits(int bits) void finalize_target_page_bits(void) { -#ifdef TARGET_PAGE_BITS_VARY finalize_target_page_bits_common(TARGET_PAGE_BITS_MIN); -#endif } From b9e3bf884aaa2d243fa3554c4dafea293187aa02 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Mar 2025 18:09:00 -0800 Subject: [PATCH 2333/2892] include/exec: Split out exec/cpu-interrupt.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of these bits are actually common to all cpus; while the reset have common reservations for target-specific usage. While generic code cannot know what the target-specific usage is, common code can know what to do with the bits, e.g. single-step. Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/cpu-all.h | 53 +-------------------------- include/exec/cpu-interrupt.h | 70 ++++++++++++++++++++++++++++++++++++ include/exec/poison.h | 13 ------- 3 files changed, 71 insertions(+), 65 deletions(-) create mode 100644 include/exec/cpu-interrupt.h diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 8f7aebb088..9e6724097c 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -21,6 +21,7 @@ #include "exec/page-protection.h" #include "exec/cpu-common.h" +#include "exec/cpu-interrupt.h" #include "exec/memory.h" #include "exec/tswap.h" #include "hw/core/cpu.h" @@ -109,58 +110,6 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val CPUArchState *cpu_copy(CPUArchState *env); -/* Flags for use in ENV->INTERRUPT_PENDING. - - The numbers assigned here are non-sequential in order to preserve - binary compatibility with the vmstate dump. Bit 0 (0x0001) was - previously used for CPU_INTERRUPT_EXIT, and is cleared when loading - the vmstate dump. */ - -/* External hardware interrupt pending. This is typically used for - interrupts from devices. */ -#define CPU_INTERRUPT_HARD 0x0002 - -/* Exit the current TB. This is typically used when some system-level device - makes some change to the memory mapping. E.g. the a20 line change. */ -#define CPU_INTERRUPT_EXITTB 0x0004 - -/* Halt the CPU. */ -#define CPU_INTERRUPT_HALT 0x0020 - -/* Debug event pending. */ -#define CPU_INTERRUPT_DEBUG 0x0080 - -/* Reset signal. */ -#define CPU_INTERRUPT_RESET 0x0400 - -/* Several target-specific external hardware interrupts. Each target/cpu.h - should define proper names based on these defines. */ -#define CPU_INTERRUPT_TGT_EXT_0 0x0008 -#define CPU_INTERRUPT_TGT_EXT_1 0x0010 -#define CPU_INTERRUPT_TGT_EXT_2 0x0040 -#define CPU_INTERRUPT_TGT_EXT_3 0x0200 -#define CPU_INTERRUPT_TGT_EXT_4 0x1000 - -/* Several target-specific internal interrupts. These differ from the - preceding target-specific interrupts in that they are intended to - originate from within the cpu itself, typically in response to some - instruction being executed. These, therefore, are not masked while - single-stepping within the debugger. */ -#define CPU_INTERRUPT_TGT_INT_0 0x0100 -#define CPU_INTERRUPT_TGT_INT_1 0x0800 -#define CPU_INTERRUPT_TGT_INT_2 0x2000 - -/* First unused bit: 0x4000. */ - -/* The set of all bits that should be masked when single-stepping. */ -#define CPU_INTERRUPT_SSTEP_MASK \ - (CPU_INTERRUPT_HARD \ - | CPU_INTERRUPT_TGT_EXT_0 \ - | CPU_INTERRUPT_TGT_EXT_1 \ - | CPU_INTERRUPT_TGT_EXT_2 \ - | CPU_INTERRUPT_TGT_EXT_3 \ - | CPU_INTERRUPT_TGT_EXT_4) - #include "cpu.h" #ifdef CONFIG_USER_ONLY diff --git a/include/exec/cpu-interrupt.h b/include/exec/cpu-interrupt.h new file mode 100644 index 0000000000..40715193ca --- /dev/null +++ b/include/exec/cpu-interrupt.h @@ -0,0 +1,70 @@ +/* + * Flags for use with cpu_interrupt() + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef CPU_INTERRUPT_H +#define CPU_INTERRUPT_H + +/* + * The numbers assigned here are non-sequential in order to preserve binary + * compatibility with the vmstate dump. Bit 0 (0x0001) was previously used + * for CPU_INTERRUPT_EXIT, and is cleared when loading the vmstate dump. + */ + +/* + * External hardware interrupt pending. + * This is typically used for interrupts from devices. + */ +#define CPU_INTERRUPT_HARD 0x0002 + +/* + * Exit the current TB. This is typically used when some system-level device + * makes some change to the memory mapping. E.g. the a20 line change. + */ +#define CPU_INTERRUPT_EXITTB 0x0004 + +/* Halt the CPU. */ +#define CPU_INTERRUPT_HALT 0x0020 + +/* Debug event pending. */ +#define CPU_INTERRUPT_DEBUG 0x0080 + +/* Reset signal. */ +#define CPU_INTERRUPT_RESET 0x0400 + +/* + * Several target-specific external hardware interrupts. Each target/cpu.h + * should define proper names based on these defines. + */ +#define CPU_INTERRUPT_TGT_EXT_0 0x0008 +#define CPU_INTERRUPT_TGT_EXT_1 0x0010 +#define CPU_INTERRUPT_TGT_EXT_2 0x0040 +#define CPU_INTERRUPT_TGT_EXT_3 0x0200 +#define CPU_INTERRUPT_TGT_EXT_4 0x1000 + +/* + * Several target-specific internal interrupts. These differ from the + * preceding target-specific interrupts in that they are intended to + * originate from within the cpu itself, typically in response to some + * instruction being executed. These, therefore, are not masked while + * single-stepping within the debugger. + */ +#define CPU_INTERRUPT_TGT_INT_0 0x0100 +#define CPU_INTERRUPT_TGT_INT_1 0x0800 +#define CPU_INTERRUPT_TGT_INT_2 0x2000 + +/* First unused bit: 0x4000. */ + +/* The set of all bits that should be masked when single-stepping. */ +#define CPU_INTERRUPT_SSTEP_MASK \ + (CPU_INTERRUPT_HARD \ + | CPU_INTERRUPT_TGT_EXT_0 \ + | CPU_INTERRUPT_TGT_EXT_1 \ + | CPU_INTERRUPT_TGT_EXT_2 \ + | CPU_INTERRUPT_TGT_EXT_3 \ + | CPU_INTERRUPT_TGT_EXT_4) + +#endif /* CPU_INTERRUPT_H */ diff --git a/include/exec/poison.h b/include/exec/poison.h index 35721366d7..8ed04b3108 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -46,19 +46,6 @@ #pragma GCC poison TARGET_PHYS_ADDR_SPACE_BITS -#pragma GCC poison CPU_INTERRUPT_HARD -#pragma GCC poison CPU_INTERRUPT_EXITTB -#pragma GCC poison CPU_INTERRUPT_HALT -#pragma GCC poison CPU_INTERRUPT_DEBUG -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_0 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_1 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_2 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_3 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_4 -#pragma GCC poison CPU_INTERRUPT_TGT_INT_0 -#pragma GCC poison CPU_INTERRUPT_TGT_INT_1 -#pragma GCC poison CPU_INTERRUPT_TGT_INT_2 - #pragma GCC poison CONFIG_ALPHA_DIS #pragma GCC poison CONFIG_HPPA_DIS #pragma GCC poison CONFIG_I386_DIS From 5469933810b00589d5db36408dd4e0236eec95ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Mar 2025 18:58:53 -0800 Subject: [PATCH 2334/2892] accel/tcg: Compile watchpoint.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move tb_check_watchpoint declaration from tb-internal.h, which is still target-specific, to internal-common.h, which isn't. Otherwise, all that is required to build watchpoint.c once is to include the new exec/cpu-interrupt.h instead of exec/exec-all.h. Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/internal-common.h | 2 ++ accel/tcg/meson.build | 2 +- accel/tcg/tb-internal.h | 2 -- accel/tcg/watchpoint.c | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 7ef620d963..9b6ab3a8cc 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -72,4 +72,6 @@ void tcg_exec_unrealizefn(CPUState *cpu); /* current cflags for hashing/comparison */ uint32_t curr_cflags(CPUState *cpu); +void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); + #endif diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 69f4808ac4..979ce90eb0 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -20,7 +20,6 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', - 'watchpoint.c', 'tcg-accel-ops.c', 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-icount.c', @@ -30,4 +29,5 @@ specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'icount-common.c', 'monitor.c', + 'watchpoint.c', )) diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index abd423fcf5..62a59a5307 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -75,6 +75,4 @@ void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); -void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); - #endif diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index 40112b2b2e..ba8c9859cf 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -19,11 +19,10 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "qemu/error-report.h" -#include "exec/exec-all.h" +#include "exec/breakpoint.h" +#include "exec/cpu-interrupt.h" #include "exec/page-protection.h" #include "exec/translation-block.h" -#include "tb-internal.h" #include "system/tcg.h" #include "system/replay.h" #include "accel/tcg/cpu-ops.h" From e7d269adb260417149e4bb85cc1882fbb11074fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 02:12:58 +0100 Subject: [PATCH 2335/2892] exec: Declare tlb_reset_dirty*() in 'exec/cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to "exec/cputlb.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-ID: <20241114011310.3615-14-philmd@linaro.org> Signed-off-by: Richard Henderson --- include/exec/cputlb.h | 7 +++++++ include/exec/exec-all.h | 3 --- include/exec/ram_addr.h | 1 + system/physmem.c | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index ef18642a32..6cac7d530f 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -32,4 +32,11 @@ void tlb_unprotect_code(ram_addr_t ram_addr); #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY + +void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); +void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); + +#endif + #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8eb0df48f9..f24256fb5e 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -486,9 +486,6 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, #if !defined(CONFIG_USER_ONLY) -void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); -void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); - MemoryRegionSection * address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, hwaddr *xlat, hwaddr *plen, diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 94bb3ccbe4..3d8df4edf1 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -23,6 +23,7 @@ #include "cpu.h" #include "system/xen.h" #include "system/tcg.h" +#include "exec/cputlb.h" #include "exec/ramlist.h" #include "exec/ramblock.h" #include "exec/exec-all.h" diff --git a/system/physmem.c b/system/physmem.c index 8c1736f84e..a6af555f4b 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -32,6 +32,7 @@ #endif /* CONFIG_TCG */ #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/translation-block.h" From a9f5ab9279bba8832712197dcf6053941d3d5d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 02:13:00 +0100 Subject: [PATCH 2336/2892] exec: Declare tlb_set_page_full() in 'exec/cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to "exec/cputlb.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20241114011310.3615-16-philmd@linaro.org> --- include/exec/cputlb.h | 23 +++++++++++++++++++++++ include/exec/exec-all.h | 22 ---------------------- target/sparc/mmu_helper.c | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 6cac7d530f..733ef012d1 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -21,6 +21,7 @@ #define CPUTLB_H #include "exec/cpu-common.h" +#include "exec/vaddr.h" #ifdef CONFIG_TCG @@ -39,4 +40,26 @@ void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); #endif +/** + * tlb_set_page_full: + * @cpu: CPU context + * @mmu_idx: mmu index of the tlb to modify + * @addr: virtual address of the entry to add + * @full: the details of the tlb entry + * + * Add an entry to @cpu tlb index @mmu_idx. All of the fields of + * @full must be filled, except for xlat_section, and constitute + * the complete description of the translated page. + * + * This is generally called by the target tlb_fill function after + * having performed a successful page table walk to find the physical + * address and attributes for the translation. + * + * At most one entry for a given virtual address is permitted. Only a + * single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only + * used by tlb_flush_page. + */ +void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, + CPUTLBEntryFull *full); + #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index f24256fb5e..f43c67366b 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -156,28 +156,6 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap, unsigned bits); -/** - * tlb_set_page_full: - * @cpu: CPU context - * @mmu_idx: mmu index of the tlb to modify - * @addr: virtual address of the entry to add - * @full: the details of the tlb entry - * - * Add an entry to @cpu tlb index @mmu_idx. All of the fields of - * @full must be filled, except for xlat_section, and constitute - * the complete description of the translated page. - * - * This is generally called by the target tlb_fill function after - * having performed a successful page table walk to find the physical - * address and attributes for the translation. - * - * At most one entry for a given virtual address is permitted. Only a - * single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only - * used by tlb_flush_page. - */ -void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, - CPUTLBEntryFull *full); - /** * tlb_set_page_with_attrs: * @cpu: CPU to add this TLB entry for diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 9ff06026b8..7548d01777 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "qemu/qemu-print.h" #include "trace.h" From 2809e2d6c4570ee9c04c3f846893c8cc2b966bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 02:13:01 +0100 Subject: [PATCH 2337/2892] exec: Declare tlb_set_page_with_attrs() in 'exec/cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to "exec/cputlb.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20241114011310.3615-17-philmd@linaro.org> --- include/exec/cputlb.h | 28 ++++++++++++++++++++++++++++ include/exec/exec-all.h | 25 ------------------------- target/i386/tcg/system/excp_helper.c | 2 +- target/microblaze/helper.c | 2 +- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 733ef012d1..56dd05a148 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -21,6 +21,8 @@ #define CPUTLB_H #include "exec/cpu-common.h" +#include "exec/hwaddr.h" +#include "exec/memattrs.h" #include "exec/vaddr.h" #ifdef CONFIG_TCG @@ -62,4 +64,30 @@ void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, CPUTLBEntryFull *full); +/** + * tlb_set_page_with_attrs: + * @cpu: CPU to add this TLB entry for + * @addr: virtual address of page to add entry for + * @paddr: physical address of the page + * @attrs: memory transaction attributes + * @prot: access permissions (PAGE_READ/PAGE_WRITE/PAGE_EXEC bits) + * @mmu_idx: MMU index to insert TLB entry for + * @size: size of the page in bytes + * + * Add an entry to this CPU's TLB (a mapping from virtual address + * @addr to physical address @paddr) with the specified memory + * transaction attributes. This is generally called by the target CPU + * specific code after it has been called through the tlb_fill() + * entry point and performed a successful page table walk to find + * the physical address and attributes for the virtual address + * which provoked the TLB miss. + * + * At most one entry for a given virtual address is permitted. Only a + * single TARGET_PAGE_SIZE region is mapped; the supplied @size is only + * used by tlb_flush_page. + */ +void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, + hwaddr paddr, MemTxAttrs attrs, + int prot, int mmu_idx, vaddr size); + #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index f43c67366b..62d6300752 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -156,31 +156,6 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap, unsigned bits); -/** - * tlb_set_page_with_attrs: - * @cpu: CPU to add this TLB entry for - * @addr: virtual address of page to add entry for - * @paddr: physical address of the page - * @attrs: memory transaction attributes - * @prot: access permissions (PAGE_READ/PAGE_WRITE/PAGE_EXEC bits) - * @mmu_idx: MMU index to insert TLB entry for - * @size: size of the page in bytes - * - * Add an entry to this CPU's TLB (a mapping from virtual address - * @addr to physical address @paddr) with the specified memory - * transaction attributes. This is generally called by the target CPU - * specific code after it has been called through the tlb_fill() - * entry point and performed a successful page table walk to find - * the physical address and attributes for the virtual address - * which provoked the TLB miss. - * - * At most one entry for a given virtual address is permitted. Only a - * single TARGET_PAGE_SIZE region is mapped; the supplied @size is only - * used by tlb_flush_page. - */ -void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, - hwaddr paddr, MemTxAttrs attrs, - int prot, int mmu_idx, vaddr size); /* tlb_set_page: * * This function is equivalent to calling tlb_set_page_with_attrs() diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index 864e3140e3..6876329de2 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "tcg/helper-tcg.h" diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 5d3259ce31..27fc929bee 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "qemu/host-utils.h" #include "exec/log.h" From eb9b25c6565d8c49a0db40f65a8a1f7932e81ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 02:13:02 +0100 Subject: [PATCH 2338/2892] exec: Declare tlb_set_page() in 'exec/cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to "exec/cputlb.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20241114011310.3615-18-philmd@linaro.org> --- include/exec/cputlb.h | 11 +++++++++++ include/exec/exec-all.h | 9 --------- target/alpha/helper.c | 2 +- target/avr/helper.c | 2 +- target/loongarch/tcg/tlb_helper.c | 1 + target/m68k/helper.c | 1 + target/mips/tcg/system/tlb_helper.c | 1 + target/openrisc/mmu.c | 2 +- target/ppc/mmu_helper.c | 1 + target/riscv/cpu_helper.c | 1 + target/rx/cpu.c | 2 +- target/s390x/tcg/excp_helper.c | 1 + target/sh4/helper.c | 1 + target/tricore/helper.c | 2 +- target/xtensa/helper.c | 2 +- 15 files changed, 24 insertions(+), 15 deletions(-) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 56dd05a148..cdfaf17403 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -90,4 +90,15 @@ void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, hwaddr paddr, MemTxAttrs attrs, int prot, int mmu_idx, vaddr size); +/** + * tlb_set_page: + * + * This function is equivalent to calling tlb_set_page_with_attrs() + * with an @attrs argument of MEMTXATTRS_UNSPECIFIED. It's provided + * as a convenience for CPUs which don't use memory transaction attributes. + */ +void tlb_set_page(CPUState *cpu, vaddr addr, + hwaddr paddr, int prot, + int mmu_idx, vaddr size); + #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 62d6300752..a3aa8448d0 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -156,15 +156,6 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap, unsigned bits); -/* tlb_set_page: - * - * This function is equivalent to calling tlb_set_page_with_attrs() - * with an @attrs argument of MEMTXATTRS_UNSPECIFIED. It's provided - * as a convenience for CPUs which don't use memory transaction attributes. - */ -void tlb_set_page(CPUState *cpu, vaddr addr, - hwaddr paddr, int prot, - int mmu_idx, vaddr size); #else static inline void tlb_flush_page(CPUState *cpu, vaddr addr) { diff --git a/target/alpha/helper.c b/target/alpha/helper.c index 2f1000c99f..57cefcba14 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "fpu/softfloat-types.h" #include "exec/helper-proto.h" diff --git a/target/avr/helper.c b/target/avr/helper.c index 9ea6870e44..3412312ad5 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -23,7 +23,7 @@ #include "qemu/error-report.h" #include "cpu.h" #include "accel/tcg/cpu-ops.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "exec/address-spaces.h" diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index a323606e5a..f6b63c7224 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -12,6 +12,7 @@ #include "cpu.h" #include "internals.h" #include "exec/helper-proto.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" diff --git a/target/m68k/helper.c b/target/m68k/helper.c index beefeb7069..0bf574830f 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/gdbstub.h" diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index e98bb95951..ca4d6b27bc 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "internal.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index c632d5230b..47ac783c52 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "gdbstub/helpers.h" #include "qemu/host-utils.h" diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index a802bc9c62..ad9ba8294c 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -24,6 +24,7 @@ #include "kvm_ppc.h" #include "mmu-hash64.h" #include "mmu-hash32.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/log.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 34092f372d..6c4391d96b 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "internals.h" #include "pmu.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "instmap.h" diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 1c40c8977e..f01e069a90 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "cpu.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/translation-block.h" #include "hw/loader.h" diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 4c0b692c9e..f969850f87 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -22,6 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "s390x-internal.h" #include "tcg_s390x.h" diff --git a/target/sh4/helper.c b/target/sh4/helper.c index b8774e046e..7567e6c8b6 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/log.h" diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 9898752eb0..a64412e6bd 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -19,7 +19,7 @@ #include "qemu/log.h" #include "hw/registerfields.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "fpu/softfloat-helpers.h" #include "qemu/qemu-print.h" diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index f64699b116..4824b97e37 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "gdbstub/helpers.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" From bcde46f57dccb3a5d7d669cabef7da0b506c319b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 02:13:04 +0100 Subject: [PATCH 2339/2892] exec: Declare tlb_hit*() in 'exec/cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to "exec/cputlb.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20241114011310.3615-20-philmd@linaro.org> --- accel/tcg/cputlb.c | 23 +++++++++++++++++++++++ include/exec/cpu-all.h | 23 ----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index c8761683a0..fb22048876 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1201,6 +1201,29 @@ void tlb_set_page(CPUState *cpu, vaddr addr, prot, mmu_idx, size); } +/** + * tlb_hit_page: return true if page aligned @addr is a hit against the + * TLB entry @tlb_addr + * + * @addr: virtual address to test (must be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) + */ +static inline bool tlb_hit_page(uint64_t tlb_addr, vaddr addr) +{ + return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); +} + +/** + * tlb_hit: return true if @addr is a hit against the TLB entry @tlb_addr + * + * @addr: virtual address to test (need not be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) + */ +static inline bool tlb_hit(uint64_t tlb_addr, vaddr addr) +{ + return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK); +} + /* * Note: tlb_fill_align() can trigger a resize of the TLB. * This means that all of the caller's prior references to the TLB table diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 9e6724097c..8cd6c00cf8 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -179,29 +179,6 @@ static inline int cpu_mmu_index(CPUState *cs, bool ifetch) /* The two sets of flags must not overlap. */ QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); -/** - * tlb_hit_page: return true if page aligned @addr is a hit against the - * TLB entry @tlb_addr - * - * @addr: virtual address to test (must be page aligned) - * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) - */ -static inline bool tlb_hit_page(uint64_t tlb_addr, vaddr addr) -{ - return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); -} - -/** - * tlb_hit: return true if @addr is a hit against the TLB entry @tlb_addr - * - * @addr: virtual address to test (need not be page aligned) - * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) - */ -static inline bool tlb_hit(uint64_t tlb_addr, vaddr addr) -{ - return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK); -} - #endif /* !CONFIG_USER_ONLY */ /* Validate correct placement of CPUArchState. */ From 6ff5da16000f908140723e164d33a0b51a6c4162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Nov 2024 02:13:03 +0100 Subject: [PATCH 2340/2892] exec: Declare tlb_flush*() in 'exec/cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CPU TLB related methods to "exec/cputlb.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-ID: <20241114011310.3615-19-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/tcg-accel-ops.c | 2 +- cpu-target.c | 1 + hw/intc/armv7m_nvic.c | 2 +- hw/ppc/spapr_nested.c | 1 + hw/sh4/sh7750.c | 1 + include/exec/cputlb.h | 200 +++++++++++++++++++++++++-- include/exec/exec-all.h | 184 ------------------------ system/watchpoint.c | 3 +- target/alpha/sys_helper.c | 2 +- target/arm/helper.c | 1 + target/arm/tcg/tlb-insns.c | 2 +- target/hppa/mem_helper.c | 1 + target/i386/helper.c | 2 +- target/i386/machine.c | 2 +- target/i386/tcg/fpu_helper.c | 2 +- target/i386/tcg/misc_helper.c | 2 +- target/i386/tcg/system/misc_helper.c | 2 +- target/i386/tcg/system/svm_helper.c | 2 +- target/loongarch/tcg/csr_helper.c | 2 +- target/microblaze/mmu.c | 2 +- target/mips/system/cp0.c | 2 +- target/mips/tcg/system/cp0_helper.c | 2 +- target/openrisc/sys_helper.c | 1 + target/ppc/helper_regs.c | 2 +- target/ppc/misc_helper.c | 1 + target/riscv/csr.c | 1 + target/riscv/op_helper.c | 1 + target/riscv/pmp.c | 2 +- target/s390x/gdbstub.c | 2 +- target/s390x/sigp.c | 1 + target/s390x/tcg/mem_helper.c | 1 + target/s390x/tcg/misc_helper.c | 1 + target/sparc/ldst_helper.c | 1 + target/xtensa/mmu_helper.c | 1 + 34 files changed, 224 insertions(+), 211 deletions(-) diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 132c5d1461..53e580d128 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -33,7 +33,7 @@ #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "qemu/timer.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/translation-block.h" diff --git a/cpu-target.c b/cpu-target.c index 5aa6c4b0c6..b6e66d5ac0 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -31,6 +31,7 @@ #include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/cpu-common.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" #include "exec/log.h" diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 5fd0760982..7212c87c68 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -22,7 +22,7 @@ #include "system/runstate.h" #include "target/arm/cpu.h" #include "target/arm/cpu-features.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/memop.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c index 7def8eb73b..23958c6383 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -1,6 +1,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 8892eaddcb..6faf0e3ca8 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -36,6 +36,7 @@ #include "hw/sh4/sh_intc.h" #include "hw/timer/tmu012.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "trace.h" typedef struct SH7750State { diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index cdfaf17403..8125f6809c 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -25,21 +25,14 @@ #include "exec/memattrs.h" #include "exec/vaddr.h" -#ifdef CONFIG_TCG - -#if !defined(CONFIG_USER_ONLY) -/* cputlb.c */ +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) void tlb_protect_code(ram_addr_t ram_addr); void tlb_unprotect_code(ram_addr_t ram_addr); #endif -#endif /* CONFIG_TCG */ - #ifndef CONFIG_USER_ONLY - void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); - #endif /** @@ -101,4 +94,193 @@ void tlb_set_page(CPUState *cpu, vaddr addr, hwaddr paddr, int prot, int mmu_idx, vaddr size); -#endif +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +/** + * tlb_flush_page: + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * + * Flush one page from the TLB of the specified CPU, for all + * MMU indexes. + */ +void tlb_flush_page(CPUState *cpu, vaddr addr); + +/** + * tlb_flush_page_all_cpus_synced: + * @cpu: src CPU of the flush + * @addr: virtual address of page to be flushed + * + * Flush one page from the TLB of all CPUs, for all + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. + */ +void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); + +/** + * tlb_flush: + * @cpu: CPU whose TLB should be flushed + * + * Flush the entire TLB for the specified CPU. Most CPU architectures + * allow the implementation to drop entries from the TLB at any time + * so this is generally safe. If more selective flushing is required + * use one of the other functions for efficiency. + */ +void tlb_flush(CPUState *cpu); + +/** + * tlb_flush_all_cpus_synced: + * @cpu: src CPU of the flush + * + * Flush the entire TLB for all CPUs, for all MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. + */ +void tlb_flush_all_cpus_synced(CPUState *src_cpu); + +/** + * tlb_flush_page_by_mmuidx: + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * @idxmap: bitmap of MMU indexes to flush + * + * Flush one page from the TLB of the specified CPU, for the specified + * MMU indexes. + */ +void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, + uint16_t idxmap); + +/** + * tlb_flush_page_by_mmuidx_all_cpus_synced: + * @cpu: Originating CPU of the flush + * @addr: virtual address of page to be flushed + * @idxmap: bitmap of MMU indexes to flush + * + * Flush one page from the TLB of all CPUs, for the specified + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. + */ +void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, + uint16_t idxmap); + +/** + * tlb_flush_by_mmuidx: + * @cpu: CPU whose TLB should be flushed + * @wait: If true ensure synchronisation by exiting the cpu_loop + * @idxmap: bitmap of MMU indexes to flush + * + * Flush all entries from the TLB of the specified CPU, for the specified + * MMU indexes. + */ +void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); + +/** + * tlb_flush_by_mmuidx_all_cpus_synced: + * @cpu: Originating CPU of the flush + * @idxmap: bitmap of MMU indexes to flush + * + * Flush all entries from the TLB of all CPUs, for the specified + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. + */ +void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); + +/** + * tlb_flush_page_bits_by_mmuidx + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * @idxmap: bitmap of mmu indexes to flush + * @bits: number of significant bits in address + * + * Similar to tlb_flush_page_mask, but with a bitmap of indexes. + */ +void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, + uint16_t idxmap, unsigned bits); + +/* Similarly, with broadcast and syncing. */ +void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, + uint16_t idxmap, + unsigned bits); + +/** + * tlb_flush_range_by_mmuidx + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of the start of the range to be flushed + * @len: length of range to be flushed + * @idxmap: bitmap of mmu indexes to flush + * @bits: number of significant bits in address + * + * For each mmuidx in @idxmap, flush all pages within [@addr,@addr+@len), + * comparing only the low @bits worth of each virtual page. + */ +void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, + vaddr len, uint16_t idxmap, + unsigned bits); + +/* Similarly, with broadcast and syncing. */ +void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, + vaddr addr, + vaddr len, + uint16_t idxmap, + unsigned bits); +#else +static inline void tlb_flush_page(CPUState *cpu, vaddr addr) +{ +} +static inline void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr) +{ +} +static inline void tlb_flush(CPUState *cpu) +{ +} +static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu) +{ +} +static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, + vaddr addr, uint16_t idxmap) +{ +} + +static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) +{ +} +static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, + vaddr addr, + uint16_t idxmap) +{ +} +static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, + uint16_t idxmap) +{ +} +static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, + vaddr addr, + uint16_t idxmap, + unsigned bits) +{ +} +static inline void +tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, + uint16_t idxmap, unsigned bits) +{ +} +static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, + vaddr len, uint16_t idxmap, + unsigned bits) +{ +} +static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, + vaddr addr, + vaddr len, + uint16_t idxmap, + unsigned bits) +{ +} +#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ +#endif /* CPUTLB_H */ diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index a3aa8448d0..a758b7a843 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -27,190 +27,6 @@ #include "exec/mmu-access-type.h" #include "exec/translation-block.h" -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) -/* cputlb.c */ -/** - * tlb_flush_page: - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of page to be flushed - * - * Flush one page from the TLB of the specified CPU, for all - * MMU indexes. - */ -void tlb_flush_page(CPUState *cpu, vaddr addr); -/** - * tlb_flush_page_all_cpus_synced: - * @cpu: src CPU of the flush - * @addr: virtual address of page to be flushed - * - * Flush one page from the TLB of all CPUs, for all - * MMU indexes. - * - * When this function returns, no CPUs will subsequently perform - * translations using the flushed TLBs. - */ -void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); -/** - * tlb_flush: - * @cpu: CPU whose TLB should be flushed - * - * Flush the entire TLB for the specified CPU. Most CPU architectures - * allow the implementation to drop entries from the TLB at any time - * so this is generally safe. If more selective flushing is required - * use one of the other functions for efficiency. - */ -void tlb_flush(CPUState *cpu); -/** - * tlb_flush_all_cpus_synced: - * @cpu: src CPU of the flush - * - * Flush the entire TLB for all CPUs, for all MMU indexes. - * - * When this function returns, no CPUs will subsequently perform - * translations using the flushed TLBs. - */ -void tlb_flush_all_cpus_synced(CPUState *src_cpu); -/** - * tlb_flush_page_by_mmuidx: - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of MMU indexes to flush - * - * Flush one page from the TLB of the specified CPU, for the specified - * MMU indexes. - */ -void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, - uint16_t idxmap); -/** - * tlb_flush_page_by_mmuidx_all_cpus_synced: - * @cpu: Originating CPU of the flush - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of MMU indexes to flush - * - * Flush one page from the TLB of all CPUs, for the specified - * MMU indexes. - * - * When this function returns, no CPUs will subsequently perform - * translations using the flushed TLBs. - */ -void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap); -/** - * tlb_flush_by_mmuidx: - * @cpu: CPU whose TLB should be flushed - * @wait: If true ensure synchronisation by exiting the cpu_loop - * @idxmap: bitmap of MMU indexes to flush - * - * Flush all entries from the TLB of the specified CPU, for the specified - * MMU indexes. - */ -void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); -/** - * tlb_flush_by_mmuidx_all_cpus_synced: - * @cpu: Originating CPU of the flush - * @idxmap: bitmap of MMU indexes to flush - * - * Flush all entries from the TLB of all CPUs, for the specified - * MMU indexes. - * - * When this function returns, no CPUs will subsequently perform - * translations using the flushed TLBs. - */ -void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); - -/** - * tlb_flush_page_bits_by_mmuidx - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of mmu indexes to flush - * @bits: number of significant bits in address - * - * Similar to tlb_flush_page_mask, but with a bitmap of indexes. - */ -void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits); - -/* Similarly, with broadcast and syncing. */ -void tlb_flush_page_bits_by_mmuidx_all_cpus_synced - (CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); - -/** - * tlb_flush_range_by_mmuidx - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of the start of the range to be flushed - * @len: length of range to be flushed - * @idxmap: bitmap of mmu indexes to flush - * @bits: number of significant bits in address - * - * For each mmuidx in @idxmap, flush all pages within [@addr,@addr+@len), - * comparing only the low @bits worth of each virtual page. - */ -void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits); - -/* Similarly, with broadcast and syncing. */ -void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits); - -#else -static inline void tlb_flush_page(CPUState *cpu, vaddr addr) -{ -} -static inline void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr) -{ -} -static inline void tlb_flush(CPUState *cpu) -{ -} -static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu) -{ -} -static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, - vaddr addr, uint16_t idxmap) -{ -} - -static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) -{ -} -static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, - vaddr addr, - uint16_t idxmap) -{ -} -static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, - uint16_t idxmap) -{ -} -static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, - vaddr addr, - uint16_t idxmap, - unsigned bits) -{ -} -static inline void -tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits) -{ -} -static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits) -{ -} -static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits) -{ -} -#endif - #if defined(CONFIG_TCG) /** diff --git a/system/watchpoint.c b/system/watchpoint.c index 2aa2a9ea63..08dbd8483d 100644 --- a/system/watchpoint.c +++ b/system/watchpoint.c @@ -19,7 +19,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/target_page.h" #include "hw/core/cpu.h" /* Add a watchpoint. */ diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c index 54ee93f34c..51e3254428 100644 --- a/target/alpha/sys_helper.c +++ b/target/alpha/sys_helper.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/tb-flush.h" #include "exec/helper-proto.h" #include "system/runstate.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index 71dead7241..e786c8df5f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -18,6 +18,7 @@ #include "qemu/timer.h" #include "qemu/bitops.h" #include "qemu/qemu-print.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/translation-block.h" #include "hw/irq.h" diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index fadc61a76e..630a481f0f 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" #include "qemu/log.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "cpu.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 304f0b61e2..fb1d93ef1f 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "hw/core/cpu.h" diff --git a/target/i386/helper.c b/target/i386/helper.c index 3bc15fba6e..c07b1b16ea 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/qapi-events-run-state.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/translation-block.h" #include "system/runstate.h" #ifndef CONFIG_USER_ONLY diff --git a/target/i386/machine.c b/target/i386/machine.c index d9d4f25d1a..70f632a36f 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "hw/isa/isa.h" #include "migration/cpu.h" #include "kvm/hyperv.h" diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 4858ae9a5f..c1184ca219 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -21,7 +21,7 @@ #include #include "cpu.h" #include "tcg-cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index ed4cda8001..2b5f092a23 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -21,7 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "helper-tcg.h" /* diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index c9c4d42f84..ce18c75b9f 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -23,7 +23,7 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "tcg/helper-tcg.h" #include "hw/i386/apic.h" diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index 5f95b5227b..f9982b72d1 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -21,7 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/cpu_ldst.h" #include "tcg/helper-tcg.h" diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 6c95be9910..84f7ff25f6 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -12,7 +12,7 @@ #include "internals.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/cpu_ldst.h" #include "hw/irq.h" #include "cpu-csr.h" diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 2423ac6172..f8587d5ac4 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" static unsigned int tlb_decode_size(unsigned int f) diff --git a/target/mips/system/cp0.c b/target/mips/system/cp0.c index bae37f515b..ff7d3db00c 100644 --- a/target/mips/system/cp0.c +++ b/target/mips/system/cp0.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" /* Called for updates to CP0_Status. */ void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 79a5c833ce..01a07a169f 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -27,7 +27,7 @@ #include "internal.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" /* SMP helpers. */ diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 77567afba4..21bc137ccc 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/helper-proto.h" #include "exception.h" #ifndef CONFIG_USER_ONLY diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 3ad4273c16..f211bc9830 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/main-loop.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "system/kvm.h" #include "system/tcg.h" #include "helper_regs.h" diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index f0ca80153b..e379da6010 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 0ebcca4597..49566d3c08 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -25,6 +25,7 @@ #include "pmu.h" #include "time_helper.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/tb-flush.h" #include "system/cpu-timers.h" #include "qemu/guest-random.h" diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index f156bfab12..0d4220ba93 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "internals.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "trace.h" diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 85ab270dad..b0841d44f4 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -24,7 +24,7 @@ #include "qapi/error.h" #include "cpu.h" #include "trace.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index 6879430adc..6bca376f2b 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/gdbstub.h" #include "gdbstub/helpers.h" #include "qemu/bitops.h" diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index cf53b23291..6a4d9c5081 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -15,6 +15,7 @@ #include "system/hw_accel.h" #include "system/runstate.h" #include "exec/address-spaces.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "system/tcg.h" #include "trace.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index ea9fa64d6b..8187b917ba 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -26,6 +26,7 @@ #include "exec/helper-proto.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "accel/tcg/cpu-ops.h" diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 0245451472..31266aeda4 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -27,6 +27,7 @@ #include "exec/helper-proto.h" #include "qemu/timer.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/cpu_ldst.h" #include "qapi/error.h" #include "tcg_s390x.h" diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 4c54e45655..b559afc9a9 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -24,6 +24,7 @@ #include "tcg/tcg.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" #ifdef CONFIG_USER_ONLY diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 29b84d5dbf..63be741a42 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -32,6 +32,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" +#include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" From 5516b44bea9ce7fcc5d8eddd061ffc8dacc31b51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Mar 2025 21:49:50 -0800 Subject: [PATCH 2341/2892] system: Build watchpoint.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that watchpoint.c uses cputlb.h instead of exec-all.h, it can be built once. Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- system/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/meson.build b/system/meson.build index 4952f4b2c7..c83d80fa24 100644 --- a/system/meson.build +++ b/system/meson.build @@ -3,7 +3,6 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'ioport.c', 'memory.c', 'physmem.c', - 'watchpoint.c', )]) system_ss.add(files( @@ -24,6 +23,7 @@ system_ss.add(files( 'runstate.c', 'tpm-hmp-cmds.c', 'vl.c', + 'watchpoint.c', ), sdl, libpmem, libdaxctl) if have_tpm From ab6d72979ace98257141ba36da1c5297b71ddf61 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Mar 2025 21:50:29 -0800 Subject: [PATCH 2342/2892] accel/tcg: Build tcg-accel-ops.c once Now that tcg-accel-ops.c uses cputlb.h instead of exec-all.h, it can be built once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 979ce90eb0..70ada21f42 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -20,7 +20,6 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', - 'tcg-accel-ops.c', 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-icount.c', 'tcg-accel-ops-rr.c', @@ -29,5 +28,6 @@ specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'icount-common.c', 'monitor.c', + 'tcg-accel-ops.c', 'watchpoint.c', )) From 29172ec53658a035742d2d94fb0d9a3d169e90a1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 07:51:15 -0800 Subject: [PATCH 2343/2892] accel/tcg: Build tcg-accel-ops-icount.c once All that is required is to avoid including exec-all.h. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/tcg-accel-ops-icount.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 70ada21f42..891b724eb6 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -21,7 +21,6 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', 'tcg-accel-ops-mttcg.c', - 'tcg-accel-ops-icount.c', 'tcg-accel-ops-rr.c', )) @@ -29,5 +28,6 @@ system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'icount-common.c', 'monitor.c', 'tcg-accel-ops.c', + 'tcg-accel-ops-icount.c', 'watchpoint.c', )) diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index d6b472a0b0..27cf1044c7 100644 --- a/accel/tcg/tcg-accel-ops-icount.c +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -28,7 +28,7 @@ #include "system/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" -#include "exec/exec-all.h" +#include "hw/core/cpu.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-icount.h" From 3b9aec101b38a824441b65a6f93e3c16e04c0914 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 08:49:06 -0800 Subject: [PATCH 2344/2892] accel/tcg: Build tcg-accel-ops-rr.c once All that is required is to use cpu-common.h instead of exec-all.h. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/tcg-accel-ops-rr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 891b724eb6..87c1394b62 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -21,7 +21,6 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', 'tcg-accel-ops-mttcg.c', - 'tcg-accel-ops-rr.c', )) system_ss.add(when: ['CONFIG_TCG'], if_true: files( @@ -29,5 +28,6 @@ system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'monitor.c', 'tcg-accel-ops.c', 'tcg-accel-ops-icount.c', + 'tcg-accel-ops-rr.c', 'watchpoint.c', )) diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 028b385af9..f62cf24e1d 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -31,7 +31,7 @@ #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" -#include "exec/exec-all.h" +#include "exec/cpu-common.h" #include "tcg/startup.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-rr.h" From 3fd60df2a6e59133f58074f5d6e0e57f99c769cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 08:52:20 -0800 Subject: [PATCH 2345/2892] accel/tcg: Build tcg-accel-ops-mttcg.c once All that is required is to avoid including exec-all.h. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 87c1394b62..81fb25da5c 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -20,7 +20,6 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', - 'tcg-accel-ops-mttcg.c', )) system_ss.add(when: ['CONFIG_TCG'], if_true: files( @@ -28,6 +27,7 @@ system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'monitor.c', 'tcg-accel-ops.c', 'tcg-accel-ops-icount.c', + 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-rr.c', 'watchpoint.c', )) diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index ba7cf6819d..bdcc385ae9 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -30,7 +30,6 @@ #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" -#include "exec/exec-all.h" #include "hw/boards.h" #include "tcg/startup.h" #include "tcg-accel-ops.h" From bf4a155b7a847c9290b1b5ab4208ca361f5a469c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 8 Mar 2025 08:23:46 +0100 Subject: [PATCH 2346/2892] accel/tcg: Restrict GETPC_ADJ to 'tb-internal.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GETPC_ADJ is only used within accel/tcg/, no need to expose it to all the code base. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250308072348.65723-2-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/tb-internal.h | 11 +++++++++++ include/exec/exec-all.h | 9 --------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index 62a59a5307..68aa8d17f4 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -13,6 +13,17 @@ #include "exec/exec-all.h" #include "exec/translation-block.h" +/* + * The true return address will often point to a host insn that is part of + * the next translated guest insn. Adjust the address backward to point to + * the middle of the call insn. Subtracting one would do the job except for + * several compressed mode architectures (arm, mips) which set the low bit + * to indicate the compressed mode; subtracting two works around that. It + * is also the case that there are no host isas that contain a call insn + * smaller than 4 bytes, so we don't worry about special-casing this. + */ +#define GETPC_ADJ 2 + #ifdef CONFIG_SOFTMMU #define CPU_TLB_DYN_MIN_BITS 6 diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index a758b7a843..2ac98e56c4 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -186,15 +186,6 @@ extern __thread uintptr_t tci_tb_ptr; ((uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0))) #endif -/* The true return address will often point to a host insn that is part of - the next translated guest insn. Adjust the address backward to point to - the middle of the call insn. Subtracting one would do the job except for - several compressed mode architectures (arm, mips) which set the low bit - to indicate the compressed mode; subtracting two works around that. It - is also the case that there are no host isas that contain a call insn - smaller than 4 bytes, so we don't worry about special-casing this. */ -#define GETPC_ADJ 2 - #if !defined(CONFIG_USER_ONLY) /** From b73f58496dc7e14b56521c9eeec7aa1225861fc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Mar 2025 08:23:47 +0100 Subject: [PATCH 2347/2892] accel/tcg: Split out getpc.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out GETPC to a target-independent header. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250308072348.65723-3-philmd@linaro.org> Signed-off-by: Richard Henderson --- include/accel/tcg/getpc.h | 24 ++++++++++++++++++++++++ include/exec/exec-all.h | 10 +--------- 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 include/accel/tcg/getpc.h diff --git a/include/accel/tcg/getpc.h b/include/accel/tcg/getpc.h new file mode 100644 index 0000000000..8a97ce34e7 --- /dev/null +++ b/include/accel/tcg/getpc.h @@ -0,0 +1,24 @@ +/* + * Get host pc for helper unwinding. + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_GETPC_H +#define ACCEL_TCG_GETPC_H + +#ifndef CONFIG_TCG +#error Can only include this header with TCG +#endif + +/* GETPC is the true target of the return instruction that we'll execute. */ +#ifdef CONFIG_TCG_INTERPRETER +extern __thread uintptr_t tci_tb_ptr; +# define GETPC() tci_tb_ptr +#else +# define GETPC() \ + ((uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0))) +#endif + +#endif /* ACCEL_TCG_GETPC_H */ diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 2ac98e56c4..dd5c40f223 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -28,6 +28,7 @@ #include "exec/translation-block.h" #if defined(CONFIG_TCG) +#include "accel/tcg/getpc.h" /** * probe_access: @@ -177,15 +178,6 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); -/* GETPC is the true target of the return instruction that we'll execute. */ -#if defined(CONFIG_TCG_INTERPRETER) -extern __thread uintptr_t tci_tb_ptr; -# define GETPC() tci_tb_ptr -#else -# define GETPC() \ - ((uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0))) -#endif - #if !defined(CONFIG_USER_ONLY) /** From 15606965400b8f3038d6e85cfe5956d5a6ac33a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 15:10:16 +0100 Subject: [PATCH 2348/2892] qemu/atomic: Rename atomic128-cas.h headers using .h.inc suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented in the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore rename 'atomic128-cas.h' as 'atomic128-cas.h.inc'. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20241212141018.59428-2-philmd@linaro.org> --- host/include/aarch64/host/atomic128-cas.h | 2 +- .../generic/host/{atomic128-cas.h => atomic128-cas.h.inc} | 0 include/qemu/atomic128.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename host/include/generic/host/{atomic128-cas.h => atomic128-cas.h.inc} (100%) diff --git a/host/include/aarch64/host/atomic128-cas.h b/host/include/aarch64/host/atomic128-cas.h index 58630107bc..991da4ef54 100644 --- a/host/include/aarch64/host/atomic128-cas.h +++ b/host/include/aarch64/host/atomic128-cas.h @@ -13,7 +13,7 @@ /* Through gcc 10, aarch64 has no support for 128-bit atomics. */ #if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128) -#include "host/include/generic/host/atomic128-cas.h" +#include "host/include/generic/host/atomic128-cas.h.inc" #else static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) { diff --git a/host/include/generic/host/atomic128-cas.h b/host/include/generic/host/atomic128-cas.h.inc similarity index 100% rename from host/include/generic/host/atomic128-cas.h rename to host/include/generic/host/atomic128-cas.h.inc diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h index 88af6d4ea3..03c27022f0 100644 --- a/include/qemu/atomic128.h +++ b/include/qemu/atomic128.h @@ -58,7 +58,7 @@ * Therefore, special case each platform. */ -#include "host/atomic128-cas.h" +#include "host/atomic128-cas.h.inc" #include "host/atomic128-ldst.h" #endif /* QEMU_ATOMIC128_H */ From 883cc6c5789b4210284241e7a2742c67f98e8843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 15:10:17 +0100 Subject: [PATCH 2349/2892] qemu/atomic: Rename atomic128-ldst.h headers using .h.inc suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented in the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore rename 'atomic128-ldst.h' as 'atomic128-ldst.h.inc'. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20241212141018.59428-3-philmd@linaro.org> --- .../aarch64/host/{atomic128-ldst.h => atomic128-ldst.h.inc} | 0 .../generic/host/{atomic128-ldst.h => atomic128-ldst.h.inc} | 0 .../loongarch64/host/{atomic128-ldst.h => atomic128-ldst.h.inc} | 0 .../x86_64/host/{atomic128-ldst.h => atomic128-ldst.h.inc} | 2 +- host/include/x86_64/host/load-extract-al16-al8.h.inc | 2 +- include/qemu/atomic128.h | 2 +- 6 files changed, 3 insertions(+), 3 deletions(-) rename host/include/aarch64/host/{atomic128-ldst.h => atomic128-ldst.h.inc} (100%) rename host/include/generic/host/{atomic128-ldst.h => atomic128-ldst.h.inc} (100%) rename host/include/loongarch64/host/{atomic128-ldst.h => atomic128-ldst.h.inc} (100%) rename host/include/x86_64/host/{atomic128-ldst.h => atomic128-ldst.h.inc} (96%) diff --git a/host/include/aarch64/host/atomic128-ldst.h b/host/include/aarch64/host/atomic128-ldst.h.inc similarity index 100% rename from host/include/aarch64/host/atomic128-ldst.h rename to host/include/aarch64/host/atomic128-ldst.h.inc diff --git a/host/include/generic/host/atomic128-ldst.h b/host/include/generic/host/atomic128-ldst.h.inc similarity index 100% rename from host/include/generic/host/atomic128-ldst.h rename to host/include/generic/host/atomic128-ldst.h.inc diff --git a/host/include/loongarch64/host/atomic128-ldst.h b/host/include/loongarch64/host/atomic128-ldst.h.inc similarity index 100% rename from host/include/loongarch64/host/atomic128-ldst.h rename to host/include/loongarch64/host/atomic128-ldst.h.inc diff --git a/host/include/x86_64/host/atomic128-ldst.h b/host/include/x86_64/host/atomic128-ldst.h.inc similarity index 96% rename from host/include/x86_64/host/atomic128-ldst.h rename to host/include/x86_64/host/atomic128-ldst.h.inc index 8d6f909d3c..4c698e3246 100644 --- a/host/include/x86_64/host/atomic128-ldst.h +++ b/host/include/x86_64/host/atomic128-ldst.h.inc @@ -69,7 +69,7 @@ static inline void atomic16_set(Int128 *ptr, Int128 val) } #else /* Provide QEMU_ERROR stubs. */ -#include "host/include/generic/host/atomic128-ldst.h" +#include "host/include/generic/host/atomic128-ldst.h.inc" #endif #endif /* X86_64_ATOMIC128_LDST_H */ diff --git a/host/include/x86_64/host/load-extract-al16-al8.h.inc b/host/include/x86_64/host/load-extract-al16-al8.h.inc index baa506b7b5..b837c37868 100644 --- a/host/include/x86_64/host/load-extract-al16-al8.h.inc +++ b/host/include/x86_64/host/load-extract-al16-al8.h.inc @@ -9,7 +9,7 @@ #define X86_64_LOAD_EXTRACT_AL16_AL8_H #ifdef CONFIG_INT128_TYPE -#include "host/atomic128-ldst.h" +#include "host/atomic128-ldst.h.inc" /** * load_atom_extract_al16_or_al8: diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h index 03c27022f0..448fb64479 100644 --- a/include/qemu/atomic128.h +++ b/include/qemu/atomic128.h @@ -59,6 +59,6 @@ */ #include "host/atomic128-cas.h.inc" -#include "host/atomic128-ldst.h" +#include "host/atomic128-ldst.h.inc" #endif /* QEMU_ATOMIC128_H */ From 67ba7439481d61bc8482ff2ea436f74a575e371a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Dec 2024 15:10:18 +0100 Subject: [PATCH 2350/2892] qemu/atomic128: Include missing 'qemu/atomic.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qatomic_cmpxchg__nocheck() is declared in "qemu/atomic.h". Include it in order to avoid when refactoring unrelated headers: In file included from ../../accel/tcg/tcg-runtime-gvec.c:22: In file included from include/exec/helper-proto-common.h:10: In file included from include/qemu/atomic128.h:61: host/include/generic/host/atomic128-cas.h.inc:23:11: error: call to undeclared function 'qatomic_cmpxchg__nocheck'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 23 | r.i = qatomic_cmpxchg__nocheck(ptr_align, c.i, n.i); | ^ 1 error generated. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20241212141018.59428-4-philmd@linaro.org> --- include/qemu/atomic128.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h index 448fb64479..31e5c48d8f 100644 --- a/include/qemu/atomic128.h +++ b/include/qemu/atomic128.h @@ -13,6 +13,7 @@ #ifndef QEMU_ATOMIC128_H #define QEMU_ATOMIC128_H +#include "qemu/atomic.h" #include "qemu/int128.h" /* From 6e9f90021178cbf08f841206942946c67b515317 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 10:31:49 -0800 Subject: [PATCH 2351/2892] accel/tcg: Build tcg-runtime.c once Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/tcg-runtime.c | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 81fb25da5c..411fe28dea 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -1,5 +1,6 @@ common_ss.add(when: 'CONFIG_TCG', if_true: files( 'cpu-exec-common.c', + 'tcg-runtime.c', )) tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( @@ -7,7 +8,6 @@ tcg_specific_ss.add(files( 'cpu-exec.c', 'tb-maint.c', 'tcg-runtime-gvec.c', - 'tcg-runtime.c', 'translate-all.c', 'translator.c', )) diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 9fa539ad3d..fa7ed9739c 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -23,13 +23,9 @@ */ #include "qemu/osdep.h" #include "qemu/host-utils.h" -#include "cpu.h" +#include "exec/cpu-common.h" #include "exec/helper-proto-common.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" -#include "disas/disas.h" -#include "exec/log.h" -#include "tcg/tcg.h" +#include "accel/tcg/getpc.h" #define HELPER_H "accel/tcg/tcg-runtime.h" #include "exec/helper-info.c.inc" From 9e2080766f037857fc366012aaefd6fead0a75f9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 10:34:13 -0800 Subject: [PATCH 2352/2892] accel/tcg: Build tcg-runtime-gvec.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/tcg-runtime-gvec.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 411fe28dea..38ff227eb0 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -1,13 +1,13 @@ common_ss.add(when: 'CONFIG_TCG', if_true: files( 'cpu-exec-common.c', 'tcg-runtime.c', + 'tcg-runtime-gvec.c', )) tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( 'tcg-all.c', 'cpu-exec.c', 'tb-maint.c', - 'tcg-runtime-gvec.c', 'translate-all.c', 'translator.c', )) diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index afca89baa1..ff927c5dd8 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" -#include "cpu.h" #include "exec/helper-proto-common.h" #include "tcg/tcg-gvec-desc.h" From 816945364f698ae750aa665fce3d121c98e37a6f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 19:00:51 +0000 Subject: [PATCH 2353/2892] rust: pl011: Allow NULL chardev argument to pl011_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's valid for the caller to pass a NULL chardev to pl011_create(); this means "don't set the chardev property on the device", which in turn means "act like there's no chardev". All the chardev frontend APIs (in C, at least) accept a NULL pointer to mean "do nothing". This fixes some failures in 'make check-functional' when Rust support is enabled. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250307190051.3274226-1-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index af93ae8beb..f137b49fea 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -648,10 +648,12 @@ pub unsafe extern "C" fn pl011_create( // SAFETY: The callers promise that they have owned references. // They do not gift them to pl011_create, so use `Owned::from`. let irq = unsafe { Owned::::from(&*irq) }; - let chr = unsafe { Owned::::from(&*chr) }; let dev = PL011State::new(); - dev.prop_set_chr("chardev", &chr); + if !chr.is_null() { + let chr = unsafe { Owned::::from(&*chr) }; + dev.prop_set_chr("chardev", &chr); + } dev.sysbus_realize(); dev.mmio_map(0, addr); dev.connect_irq(0, &irq); From ae139d6e9248526dcfe5d522061910509809a778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 29 Jan 2025 08:18:16 +0100 Subject: [PATCH 2354/2892] tests/functional: Introduce a new test routine for OpenBMC images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OpenBMC images currently used by QEMU to test the Aspeed machines are rather old. To prepare an update to the latest builds, we need to adjust the console patterns. Introduce a new routine to preserve the current tests. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250129071820.1258133-2-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index b52358bb8c..ea75939e05 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -23,6 +23,24 @@ class AspeedTest(LinuxKernelTest): self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") self.wait_for_console_pattern("systemd[1]: Set hostname to") + def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04', + cpu_id='0x0', soc='AST2500 rev A1'): + hostname = machine.removesuffix('-bmc') + + self.set_machine(machine) + self.vm.set_console() + self.vm.add_args('-drive', f'file={image},if=mtd,format=raw', + '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern(f'U-Boot {uboot}') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu_id}') + self.wait_for_console_pattern(f'ASPEED {soc}') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern(f'systemd[1]: Hostname set to <{hostname}>.') + def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): self.require_netdev('user') self.vm.set_console() From 6664b3e255ed89b93e4311361ae62d05313f2ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 29 Jan 2025 08:18:17 +0100 Subject: [PATCH 2355/2892] tests/functional: Update OpenBMC image of palmetto machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new do_test_arm_aspeed_openbmc() routine to run the latest OpenBMC firmware build of the palmetto BMC. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250129071820.1258133-3-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/test_arm_aspeed_palmetto.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/test_arm_aspeed_palmetto.py index 6588c02aad..35d832bc98 100755 --- a/tests/functional/test_arm_aspeed_palmetto.py +++ b/tests/functional/test_arm_aspeed_palmetto.py @@ -7,18 +7,19 @@ from qemu_test import Asset from aspeed import AspeedTest + class PalmettoMachine(AspeedTest): ASSET_PALMETTO_FLASH = Asset( - ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-palmetto.static.mtd'), - '3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d'); + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/palmetto-bmc/openbmc-20250128071432/obmc-phosphor-image-palmetto-20250128071432.static.mtd', + 'bce7c392eec75c707a91cfc8fad7ca9a69d7e4f10df936930d65c1cb9897ac81'); - def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): + def test_arm_ast2400_palmetto_openbmc(self): image_path = self.ASSET_PALMETTO_FLASH.fetch() - self.do_test_arm_aspeed('palmetto-bmc', image_path) - + self.do_test_arm_aspeed_openbmc('palmetto-bmc', image=image_path, + uboot='2019.04', cpu_id='0x0', + soc='AST2400 rev A1'); if __name__ == '__main__': AspeedTest.main() From 01050d97e1f342590f5f2db4a6d2b557a8244f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 29 Jan 2025 08:18:18 +0100 Subject: [PATCH 2356/2892] tests/functional: Update OpenBMC image of romulus machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new do_test_arm_aspeed_openbmc() routine to run the latest OpenBMC firmware build of the romulus BMC. Remove the older routine which is now unused. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250129071820.1258133-4-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 16 ---------------- tests/functional/test_arm_aspeed_romulus.py | 13 +++++++------ 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index ea75939e05..77dc8930fa 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -7,22 +7,6 @@ from qemu_test import LinuxKernelTest class AspeedTest(LinuxKernelTest): - def do_test_arm_aspeed(self, machine, image): - self.set_machine(machine) - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-snapshot') - self.vm.launch() - - self.wait_for_console_pattern("U-Boot 2016.07") - self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") - self.wait_for_console_pattern("Starting kernel ...") - self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") - self.wait_for_console_pattern( - "aspeed-smc 1e620000.spi: read control register: 203b0641") - self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") - self.wait_for_console_pattern("systemd[1]: Set hostname to") - def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04', cpu_id='0x0', soc='AST2500 rev A1'): hostname = machine.removesuffix('-bmc') diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/test_arm_aspeed_romulus.py index 747b616201..b97ed951b1 100755 --- a/tests/functional/test_arm_aspeed_romulus.py +++ b/tests/functional/test_arm_aspeed_romulus.py @@ -7,18 +7,19 @@ from qemu_test import Asset from aspeed import AspeedTest + class RomulusMachine(AspeedTest): ASSET_ROMULUS_FLASH = Asset( - ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-romulus.static.mtd'), - '820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/romulus-bmc/openbmc-20250128071340/obmc-phosphor-image-romulus-20250128071340.static.mtd', + '6d031376440c82ed9d087d25e9fa76aea75b42f80daa252ec402c0bc3cf6cf5b'); - def test_arm_ast2500_romulus_openbmc_v2_9_0(self): + def test_arm_ast2500_romulus_openbmc(self): image_path = self.ASSET_ROMULUS_FLASH.fetch() - self.do_test_arm_aspeed('romulus-bmc', image_path) - + self.do_test_arm_aspeed_openbmc('romulus-bmc', image=image_path, + uboot='2019.04', cpu_id='0x0', + soc='AST2500 rev A1'); if __name__ == '__main__': AspeedTest.main() From 3058b634f2785454504328e1710b7cab66a5acf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 29 Jan 2025 08:18:19 +0100 Subject: [PATCH 2357/2892] tests/functional: Introduce a witherspoon machine test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use do_test_arm_aspeed_openbmc() routine to run the latest OpenBMC firmware build of the witherspoon BMC. Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250129071820.1258133-5-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 2 ++ .../functional/test_arm_aspeed_witherspoon.py | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/functional/test_arm_aspeed_witherspoon.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 97c3f4ad4e..0573f0091d 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -22,6 +22,7 @@ test_timeouts = { 'acpi_bits' : 420, 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, + 'arm_aspeed_witherspoon' : 120, 'arm_aspeed_ast2500' : 720, 'arm_aspeed_ast2600' : 1200, 'arm_aspeed_rainier' : 480, @@ -104,6 +105,7 @@ tests_arm_system_thorough = [ 'arm_aspeed_ast1030', 'arm_aspeed_palmetto', 'arm_aspeed_romulus', + 'arm_aspeed_witherspoon', 'arm_aspeed_ast2500', 'arm_aspeed_ast2600', 'arm_aspeed_rainier', diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/test_arm_aspeed_witherspoon.py new file mode 100644 index 0000000000..ea1ce89b05 --- /dev/null +++ b/tests/functional/test_arm_aspeed_witherspoon.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class WitherspoonMachine(AspeedTest): + + ASSET_WITHERSPOON_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/witherspoon-bmc/openbmc-20240618035022/obmc-phosphor-image-witherspoon-20240618035022.ubi.mtd', + '937d9ed449ea6c6cbed983519088a42d0cafe276bcfe4fce07772ca6673f9213'); + + def test_arm_ast2500_witherspoon_openbmc(self): + image_path = self.ASSET_WITHERSPOON_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('witherspoon-bmc', image=image_path, + uboot='2016.07', cpu_id='0x0', + soc='AST2500 rev A1'); + +if __name__ == '__main__': + AspeedTest.main() From b91a1d31106f042df294410214656608e5b2fe2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 29 Jan 2025 08:18:20 +0100 Subject: [PATCH 2358/2892] tests/functional: Introduce a bletchley machine test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use do_test_arm_aspeed_openbmc() to run the latest OpenBMC firmware build of the bletchley BMC. Reviewed-by: Patrick Williams Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250129071820.1258133-6-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 2 ++ tests/functional/test_arm_aspeed_bletchley.py | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/functional/test_arm_aspeed_bletchley.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 0573f0091d..5dc66c03fc 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -25,6 +25,7 @@ test_timeouts = { 'arm_aspeed_witherspoon' : 120, 'arm_aspeed_ast2500' : 720, 'arm_aspeed_ast2600' : 1200, + 'arm_aspeed_bletchley' : 120, 'arm_aspeed_rainier' : 480, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -108,6 +109,7 @@ tests_arm_system_thorough = [ 'arm_aspeed_witherspoon', 'arm_aspeed_ast2500', 'arm_aspeed_ast2600', + 'arm_aspeed_bletchley', 'arm_aspeed_rainier', 'arm_bpim2u', 'arm_canona1100', diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/test_arm_aspeed_bletchley.py new file mode 100644 index 0000000000..0da856c5ed --- /dev/null +++ b/tests/functional/test_arm_aspeed_bletchley.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class BletchleyMachine(AspeedTest): + + ASSET_BLETCHLEY_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/bletchley-bmc/openbmc-20250128071329/obmc-phosphor-image-bletchley-20250128071329.static.mtd.xz', + 'db21d04d47d7bb2a276f59d308614b4dfb70b9c7c81facbbca40a3977a2d8844'); + + def test_arm_ast2600_bletchley_openbmc(self): + image_path = self.uncompress(self.ASSET_BLETCHLEY_FLASH) + + self.do_test_arm_aspeed_openbmc('bletchley-bmc', image=image_path, + uboot='2019.04', cpu_id='0xf00', + soc='AST2600 rev A3'); + +if __name__ == '__main__': + AspeedTest.main() From 136367e567771b7aef49e734817667950413deba Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Feb 2025 14:09:55 +0800 Subject: [PATCH 2359/2892] aspeed/soc: Support Non-maskable Interrupt for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU supports GICv3 Non-maskable Interrupt, adds to support Non-maskable Interrupt for AST2700. Reference: https://github.com/qemu/qemu/commit/b36a32ead Signed-off-by: Jamin Lin Suggested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250204060955.3546022-1-jamin_lin@aspeedtech.com --- hw/arm/aspeed_ast27x0.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 2d0c99f159..3e373f966b 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -470,6 +470,10 @@ static bool aspeed_soc_ast2700_gic_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); sysbus_connect_irq(gicbusdev, i + 3 * sc->num_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + sysbus_connect_irq(gicbusdev, i + 4 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_NMI)); + sysbus_connect_irq(gicbusdev, i + 5 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VINMI)); } return true; From a5b9621024f25b374c270e8f7216e35f911720e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 18 Feb 2025 08:35:34 +0100 Subject: [PATCH 2360/2892] aspeed: Remove duplicate typename in AspeedSoCClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoC type name is stored under AspeedSoCClass which is redundant. Use object_get_typename() instead where needed. Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/qemu-devel/20250218073534.585066-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 3 +-- hw/arm/aspeed_ast2400.c | 4 +--- hw/arm/aspeed_ast2600.c | 3 +-- hw/arm/aspeed_ast27x0.c | 3 +-- include/hw/arm/aspeed_soc.h | 1 - 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index e76c7100a1..ec329f4991 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -116,7 +116,7 @@ static void aspeed_soc_ast1030_init(Object *obj) char typename[64]; int i; - if (sscanf(sc->name, "%7s", socname) != 1) { + if (sscanf(object_get_typename(obj), "%7s", socname) != 1) { g_assert_not_reached(); } @@ -428,7 +428,6 @@ static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; dc->realize = aspeed_soc_ast1030_realize; - sc->name = "ast1030-a1"; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST1030_A1_SILICON_REV; sc->sram_size = 0xc0000; diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index 8784b6e793..0158f6e9c2 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -151,7 +151,7 @@ static void aspeed_ast2400_soc_init(Object *obj) char socname[8]; char typename[64]; - if (sscanf(sc->name, "%7s", socname) != 1) { + if (sscanf(object_get_typename(obj), "%7s", socname) != 1) { g_assert_not_reached(); } @@ -515,7 +515,6 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) /* Reason: Uses serial_hds and nd_table in realize() directly */ dc->user_creatable = false; - sc->name = "ast2400-a1"; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2400_A1_SILICON_REV; sc->sram_size = 0x8000; @@ -544,7 +543,6 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) /* Reason: Uses serial_hds and nd_table in realize() directly */ dc->user_creatable = false; - sc->name = "ast2500-a1"; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2500_A1_SILICON_REV; sc->sram_size = 0x9000; diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 07210483bb..1f994ba26c 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -157,7 +157,7 @@ static void aspeed_soc_ast2600_init(Object *obj) char socname[8]; char typename[64]; - if (sscanf(sc->name, "%7s", socname) != 1) { + if (sscanf(object_get_typename(obj), "%7s", socname) != 1) { g_assert_not_reached(); } @@ -666,7 +666,6 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) /* Reason: The Aspeed SoC can only be instantiated from a board */ dc->user_creatable = false; - sc->name = "ast2600-a3"; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2600_A3_SILICON_REV; sc->sram_size = 0x16400; diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 3e373f966b..6506bdfdff 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -316,7 +316,7 @@ static void aspeed_soc_ast2700_init(Object *obj) char socname[8]; char typename[64]; - if (sscanf(sc->name, "%7s", socname) != 1) { + if (sscanf(object_get_typename(obj), "%7s", socname) != 1) { g_assert_not_reached(); } @@ -757,7 +757,6 @@ static void aspeed_soc_ast2700_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; dc->realize = aspeed_soc_ast2700_realize; - sc->name = "ast2700-a0"; sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2700_A0_SILICON_REV; sc->sram_size = 0x20000; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 689f52dae8..4a8881ca8b 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -148,7 +148,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(Aspeed10x0SoCState, ASPEED10X0_SOC) struct AspeedSoCClass { DeviceClass parent_class; - const char *name; /** valid_cpu_types: NULL terminated array of a single CPU type. */ const char * const *valid_cpu_types; uint32_t silicon_rev; From cde8182b8772fc35dc44cd688166c59e7b9c9530 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 25 Feb 2025 15:56:18 +0800 Subject: [PATCH 2361/2892] hw/misc/aspeed_hace: Fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250225075622.305515-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index e3f7df2e86..18b85081c7 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -75,9 +75,12 @@ static const struct { { HASH_ALGO_SHA1, QCRYPTO_HASH_ALGO_SHA1 }, { HASH_ALGO_SHA224, QCRYPTO_HASH_ALGO_SHA224 }, { HASH_ALGO_SHA256, QCRYPTO_HASH_ALGO_SHA256 }, - { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA512, QCRYPTO_HASH_ALGO_SHA512 }, - { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA384, QCRYPTO_HASH_ALGO_SHA384 }, - { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA256, QCRYPTO_HASH_ALGO_SHA256 }, + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA512, + QCRYPTO_HASH_ALGO_SHA512 }, + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA384, + QCRYPTO_HASH_ALGO_SHA384 }, + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA256, + QCRYPTO_HASH_ALGO_SHA256 }, }; static int hash_algo_lookup(uint32_t reg) @@ -201,7 +204,8 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, haddr = address_space_map(&s->dram_as, addr, &plen, false, MEMTXATTRS_UNSPECIFIED); if (haddr == NULL) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: qcrypto failed\n", __func__); return; } iov[i].iov_base = haddr; From 393c908afb39df5ca44b67edf3acb16bb8835cf7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 25 Feb 2025 15:56:19 +0800 Subject: [PATCH 2362/2892] hw/misc/aspeed_hace: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new ast2700 class to support AST2700. Signed-off-by: Jamin Lin Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/qemu-devel/20250225075622.305515-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 20 ++++++++++++++++++++ include/hw/misc/aspeed_hace.h | 1 + 2 files changed, 21 insertions(+) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 18b85081c7..86422cb3be 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -552,12 +552,32 @@ static const TypeInfo aspeed_ast1030_hace_info = { .class_init = aspeed_ast1030_hace_class_init, }; +static void aspeed_ast2700_hace_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); + + dc->desc = "AST2700 Hash and Crypto Engine"; + + ahc->src_mask = 0x7FFFFFFF; + ahc->dest_mask = 0x7FFFFFF8; + ahc->key_mask = 0x7FFFFFF8; + ahc->hash_mask = 0x00147FFF; +} + +static const TypeInfo aspeed_ast2700_hace_info = { + .name = TYPE_ASPEED_AST2700_HACE, + .parent = TYPE_ASPEED_HACE, + .class_init = aspeed_ast2700_hace_class_init, +}; + static void aspeed_hace_register_types(void) { type_register_static(&aspeed_ast2400_hace_info); type_register_static(&aspeed_ast2500_hace_info); type_register_static(&aspeed_ast2600_hace_info); type_register_static(&aspeed_ast1030_hace_info); + type_register_static(&aspeed_ast2700_hace_info); type_register_static(&aspeed_hace_info); } diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index 4af9919195..d13fd3da07 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -18,6 +18,7 @@ #define TYPE_ASPEED_AST2500_HACE TYPE_ASPEED_HACE "-ast2500" #define TYPE_ASPEED_AST2600_HACE TYPE_ASPEED_HACE "-ast2600" #define TYPE_ASPEED_AST1030_HACE TYPE_ASPEED_HACE "-ast1030" +#define TYPE_ASPEED_AST2700_HACE TYPE_ASPEED_HACE "-ast2700" OBJECT_DECLARE_TYPE(AspeedHACEState, AspeedHACEClass, ASPEED_HACE) From 7b5d6b47a6c99e3e3c843000c821e6549c8feb01 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 25 Feb 2025 15:56:20 +0800 Subject: [PATCH 2363/2892] hw/arm/aspeed_ast27x0: Add HACE support for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HACE controller between AST2600 and AST2700 are almost identical. The HACE controller registers base address starts at 0x1207_0000 and its alarm interrupt is connected to GICINT4. Signed-off-by: Jamin Lin Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/qemu-devel/20250225075622.305515-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 6506bdfdff..56e43d45ad 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -67,6 +67,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_RTC] = 0x12C0F000, [ASPEED_DEV_SDHCI] = 0x14080000, [ASPEED_DEV_TIMER1] = 0x12C10000, + [ASPEED_DEV_HACE] = 0x12070000, }; #define AST2700_MAX_IRQ 256 @@ -401,6 +402,9 @@ static void aspeed_soc_ast2700_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); + + snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); + object_initialize_child(obj, "hace", &s->hace, typename); } /* @@ -737,6 +741,17 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } + /* HACE */ + object_property_set_link(OBJECT(&s->hace), "dram", OBJECT(s->dram_mr), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + sc->memmap[ASPEED_DEV_HACE]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); From 8e002a693198865632f6bed072c5473a6bb9cf45 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 25 Feb 2025 15:56:21 +0800 Subject: [PATCH 2364/2892] hw/misc/aspeed_hace: Fix boot issue in the Crypto Manager Self Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, it does not support the CRYPT command. Instead, it only sends an interrupt to notify the firmware that the crypt command has completed. It is a temporary workaround to resolve the boot issue in the Crypto Manager Self Test. Introduce a new "use_crypt_workaround" class attribute and set it to true in the AST2700 HACE model to enable this workaround by default for AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250225075622.305515-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 23 +++++++++++++++++++++++ include/hw/misc/aspeed_hace.h | 1 + 2 files changed, 24 insertions(+) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 86422cb3be..32a5dbded3 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -59,6 +59,7 @@ /* Other cmd bits */ #define HASH_IRQ_EN BIT(9) #define HASH_SG_EN BIT(18) +#define CRYPT_IRQ_EN BIT(12) /* Scatter-gather data list */ #define SG_LIST_LEN_SIZE 4 #define SG_LIST_LEN_MASK 0x0FFFFFFF @@ -343,6 +344,15 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, qemu_irq_lower(s->irq); } } + if (ahc->raise_crypt_interrupt_workaround) { + if (data & CRYPT_IRQ) { + data &= ~CRYPT_IRQ; + + if (s->regs[addr] & CRYPT_IRQ) { + qemu_irq_lower(s->irq); + } + } + } break; case R_HASH_SRC: data &= ahc->src_mask; @@ -388,6 +398,12 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_CRYPT_CMD: qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n", __func__); + if (ahc->raise_crypt_interrupt_workaround) { + s->regs[R_STATUS] |= CRYPT_IRQ; + if (data & CRYPT_IRQ_EN) { + qemu_irq_raise(s->irq); + } + } break; default: break; @@ -563,6 +579,13 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, void *data) ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; ahc->hash_mask = 0x00147FFF; + + /* + * Currently, it does not support the CRYPT command. Instead, it only + * sends an interrupt to notify the firmware that the crypt command + * has completed. It is a temporary workaround. + */ + ahc->raise_crypt_interrupt_workaround = true; } static const TypeInfo aspeed_ast2700_hace_info = { diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index d13fd3da07..5d4aa19cfe 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -50,6 +50,7 @@ struct AspeedHACEClass { uint32_t dest_mask; uint32_t key_mask; uint32_t hash_mask; + bool raise_crypt_interrupt_workaround; }; #endif /* ASPEED_HACE_H */ From 2d082fea485ee455a70ed3e963cdf9a70f34858a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Mar 2025 14:47:03 +0800 Subject: [PATCH 2365/2892] hw/misc/aspeed_scu: Skipping dram_init in u-boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting BIT6 in VGA0 SCRATCH register will indicate that the ddr traning is done, therefore skipping the u-boot-spl dram_init() process. Signed-off-by: Jamin Lin Signed-off-by: Troy Lee Reviewed-by: Cédric Le Goater Tested-by: Nabih Estefan Link: https://lore.kernel.org/qemu-devel/20250304064710.2128993-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index bac1441b06..50f74fbabd 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -157,6 +157,7 @@ #define AST2700_SCU_FREQ_CNTR TO_REG(0x3b0) #define AST2700_SCU_CPU_SCRATCH_0 TO_REG(0x780) #define AST2700_SCU_CPU_SCRATCH_1 TO_REG(0x784) +#define AST2700_SCU_VGA_SCRATCH_0 TO_REG(0x900) #define AST2700_SCUIO_CLK_STOP_CTL_1 TO_REG(0x240) #define AST2700_SCUIO_CLK_STOP_CLR_1 TO_REG(0x244) @@ -930,6 +931,7 @@ static const uint32_t ast2700_a0_resets[ASPEED_AST2700_SCU_NR_REGS] = { [AST2700_SCU_FREQ_CNTR] = 0x000375eb, [AST2700_SCU_CPU_SCRATCH_0] = 0x00000000, [AST2700_SCU_CPU_SCRATCH_1] = 0x00000004, + [AST2700_SCU_VGA_SCRATCH_0] = 0x00000040, }; static void aspeed_ast2700_scu_reset(DeviceState *dev) From 801e0dad6ad4c4e078f907d825113251e374f6b2 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Mar 2025 14:47:04 +0800 Subject: [PATCH 2366/2892] hw/misc/aspeed_scu: Fix the revision ID cannot be set in the SOC layer for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the design of the AST2600, it has a Silicon Revision ID Register, specifically SCU004 and SCU014, to set the Revision ID for the AST2600. For the AST2600 A3, SCU004 is set to 0x05030303 and SCU014 is set to 0x05030303. In the "aspeed_ast2600_scu_reset" function, the hardcoded value "AST2600_A3_SILICON_REV" is set in SCU004, and "s->silicon_rev" is set in SCU014. The value of "s->silicon_rev" is set by the SOC layer via the "silicon-rev" property. However, the design of the AST2700 is different. There are two SCU controllers: SCU0 (CPU Die) and SCU1 (IO Die). In the AST2700, the firmware reads the SCU Silicon Revision ID register (SCU0_000) and the SCUIO Silicon Revision ID register (SCU1_000), combining them into a single 64-bit value. The upper 32 bits represent the SCUIO, while the lower 32 bits correspond to the SCU. For example, the AST2700-A1 revision is represented as 0x0601010306010103. SCUIO_000 occupies bits [63:32] with a value of 0x06010103 and SCU_000 occupies bits [31:0] with a value of 0x06010103. Reference: https://github.com/AspeedTech-BMC/u-boot/blob/aspeed-master-v2023.10/arch/arm/mach-aspeed/ast2700/cpu-info.c Signed-off-by: Jamin Lin Tested-by: Nabih Estefan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250304064710.2128993-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 50f74fbabd..545d004749 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -910,7 +910,6 @@ static const MemoryRegionOps aspeed_ast2700_scu_ops = { }; static const uint32_t ast2700_a0_resets[ASPEED_AST2700_SCU_NR_REGS] = { - [AST2700_SILICON_REV] = AST2700_A0_SILICON_REV, [AST2700_HW_STRAP1] = 0x00000800, [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, @@ -940,6 +939,7 @@ static void aspeed_ast2700_scu_reset(DeviceState *dev) AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); memcpy(s->regs, asc->resets, asc->nr_regs * 4); + s->regs[AST2700_SILICON_REV] = s->silicon_rev; } static void aspeed_2700_scu_class_init(ObjectClass *klass, void *data) @@ -1032,7 +1032,6 @@ static const MemoryRegionOps aspeed_ast2700_scuio_ops = { }; static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { - [AST2700_SILICON_REV] = 0x06000003, [AST2700_HW_STRAP1] = 0x00000504, [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, From 172329c6b281e11feb3d6e60df6754008e23a089 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Mar 2025 14:47:05 +0800 Subject: [PATCH 2367/2892] hw/arm/aspeed Update HW Strap Default Values for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate HW Strap Registers for SCU and SCUIO. AST2700_EVB_HW_STRAP1 is used for the SCU (CPU Die) hw-strap1. AST2700_EVB_HW_STRAP2 is used for the SCUIO (IO Die) hw-strap1. Additionally, both default values are updated based on the dump from the EVB. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Tested-by: Nabih Estefan Link: https://lore.kernel.org/qemu-devel/20250304064710.2128993-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 98bf071139..c6c18596d6 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -181,8 +181,10 @@ struct AspeedMachineState { #ifdef TARGET_AARCH64 /* AST2700 evb hardware value */ -#define AST2700_EVB_HW_STRAP1 0x000000C0 -#define AST2700_EVB_HW_STRAP2 0x00000003 +/* SCU HW Strap1 */ +#define AST2700_EVB_HW_STRAP1 0x00000800 +/* SCUIO HW Strap1 */ +#define AST2700_EVB_HW_STRAP2 0x00000700 #endif /* Rainier hardware value: (QEMU prototype) */ From 720e850f83ef6497d9992c43716fb5d7cc9a2ad2 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Mar 2025 14:47:06 +0800 Subject: [PATCH 2368/2892] hw/misc/aspeed_scu: Fix the hw-strap1 cannot be set in the SOC layer for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one hw_strap1 register in the SCU (CPU DIE) and another hw_strap1 register in the SCUIO (IO DIE). In the "ast2700_a0_resets" function, the hardcoded value "0x00000800" is set in SCU hw-strap1 (CPU DIE), and in "ast2700_a0_resets_io" the hardcoded value "0x00000504" is set in SCUIO hw-strap1 (IO DIE). Both values cannot be set via the SOC layer. The value of "s->hw_strap1" is set by the SOC layer via the "hw-strap1" property. Update the "aspeed_ast2700_scu_reset" function to set the value of "s->hw_strap1" in both the SCU and SCUIO hw-strap1 registers. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Tested-by: Nabih Estefan Link: https://lore.kernel.org/qemu-devel/20250304064710.2128993-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 545d004749..0581c744f1 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -910,7 +910,6 @@ static const MemoryRegionOps aspeed_ast2700_scu_ops = { }; static const uint32_t ast2700_a0_resets[ASPEED_AST2700_SCU_NR_REGS] = { - [AST2700_HW_STRAP1] = 0x00000800, [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, [AST2700_HW_STRAP1_SEC1] = 0x000000FF, @@ -940,6 +939,7 @@ static void aspeed_ast2700_scu_reset(DeviceState *dev) memcpy(s->regs, asc->resets, asc->nr_regs * 4); s->regs[AST2700_SILICON_REV] = s->silicon_rev; + s->regs[AST2700_HW_STRAP1] = s->hw_strap1; } static void aspeed_2700_scu_class_init(ObjectClass *klass, void *data) @@ -1032,7 +1032,6 @@ static const MemoryRegionOps aspeed_ast2700_scuio_ops = { }; static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { - [AST2700_HW_STRAP1] = 0x00000504, [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, [AST2700_HW_STRAP1_SEC1] = 0x000000FF, From b741ab395b398058198ba3f055d9d8c4d631122f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Mar 2025 14:47:07 +0800 Subject: [PATCH 2369/2892] hw/arm/aspeed_ast27x0.c Separate HW Strap Registers for SCU and SCUIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one hw-strap1 register in the SCU (CPU DIE) and another hw-strap1 register in the SCUIO (IO DIE). The values of these two registers should not be the same. To reuse the current design of hw-strap, hw-strap1 is assigned to the SCU and sets the value in the SCU hw-strap1 register, while hw-strap2 is assigned to the SCUIO and sets the value in the SCUIO hw-strap1 register. Signed-off-by: Jamin Lin Tested-by: Nabih Estefan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250304064710.2128993-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 56e43d45ad..4cee6ddc0b 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -333,14 +333,21 @@ static void aspeed_soc_ast2700_init(Object *obj) sc->silicon_rev); object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), "hw-strap1"); - object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), - "hw-strap2"); object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key"); object_initialize_child(obj, "scuio", &s->scuio, TYPE_ASPEED_2700_SCUIO); qdev_prop_set_uint32(DEVICE(&s->scuio), "silicon-rev", sc->silicon_rev); + /* + * There is one hw-strap1 register in the SCU (CPU DIE) and another + * hw-strap1 register in the SCUIO (IO DIE). To reuse the current design + * of hw-strap, hw-strap1 is assigned to the SCU and sets the value in the + * SCU hw-strap1 register, while hw-strap2 is assigned to the SCUIO and + * sets the value in the SCUIO hw-strap1 register. + */ + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scuio), + "hw-strap1"); snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); From 8dd163f915cf26277fa175476c0af6898b8fd864 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Mar 2025 14:47:08 +0800 Subject: [PATCH 2370/2892] hw/arm/aspeed_ast27x0.c Fix boot issue for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, ASPEED_DEV_SPI_BOOT is set to "0x400000000", which is the DRAM start address, and the QEMU loader is used to load the U-Boot binary into this address. However, if users want to install FMC flash contents as a boot ROM, the DRAM address 0x400000000 would be overwritten with Boot ROM data. This causes the AST2700 to fail to boot because the U-Boot data becomes incorrect. To fix this, change the ASPEED_DEV_SPI_BOOT address to "0x100000000", which is the FMC0 memory-mapped start address in the AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Tested-by: Nabih Estefan Link: https://lore.kernel.org/qemu-devel/20250304064710.2128993-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 4cee6ddc0b..f14d2ea175 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -24,7 +24,7 @@ #include "qemu/log.h" static const hwaddr aspeed_soc_ast2700_memmap[] = { - [ASPEED_DEV_SPI_BOOT] = 0x400000000, + [ASPEED_DEV_SPI_BOOT] = 0x100000000, [ASPEED_DEV_SRAM] = 0x10000000, [ASPEED_DEV_SDMC] = 0x12C00000, [ASPEED_DEV_SCU] = 0x12C02000, From c5728c3488b936e1cef5d4d83d26df853f8fac6b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:10 +0800 Subject: [PATCH 2371/2892] hw/intc/aspeed: Support setting different memory size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the AST2700 datasheet, the INTC(CPU DIE) controller has 16KB (0x4000) of register space, and the INTCIO (I/O DIE) controller has 1KB (0x400) of register space. Introduced a new class attribute "mem_size" to set different memory sizes for the INTC models in AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 9 ++++++++- include/hw/intc/aspeed_intc.h | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 126b711b94..033b574c1e 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -302,10 +302,16 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); int i; + memory_region_init(&s->iomem_container, OBJECT(s), + TYPE_ASPEED_INTC ".container", aic->mem_size); + + sysbus_init_mmio(sbd, &s->iomem_container); + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); - sysbus_init_mmio(sbd, &s->iomem); + memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); for (i = 0; i < aic->num_ints; i++) { @@ -344,6 +350,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; aic->num_ints = 9; + aic->mem_size = 0x4000; } static const TypeInfo aspeed_2700_intc_info = { diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 18cb43476c..03324f05ab 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -25,6 +25,8 @@ struct AspeedINTCState { /*< public >*/ MemoryRegion iomem; + MemoryRegion iomem_container; + uint32_t regs[ASPEED_INTC_NR_REGS]; OrIRQState orgates[ASPEED_INTC_NR_INTS]; qemu_irq output_pins[ASPEED_INTC_NR_INTS]; @@ -39,6 +41,7 @@ struct AspeedINTCClass { uint32_t num_lines; uint32_t num_ints; + uint64_t mem_size; }; #endif /* ASPEED_INTC_H */ From 0cffaace0565b68a354c67f147bf8f0f438726e1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:11 +0800 Subject: [PATCH 2372/2892] hw/intc/aspeed: Rename status_addr and addr to status_reg and reg for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the variables "status_addr" to "status_reg" and "addr" to "reg" because they are used as register index. This change makes the code more appropriate and improves readability. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 033b574c1e..465f41e4fd 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -60,7 +60,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { AspeedINTCState *s = (AspeedINTCState *)opaque; AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); - uint32_t status_addr = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); + uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); uint32_t select = 0; uint32_t enable; int i; @@ -92,7 +92,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) trace_aspeed_intc_select(select); - if (s->mask[irq] || s->regs[status_addr]) { + if (s->mask[irq] || s->regs[status_reg]) { /* * a. mask is not 0 means in ISR mode * sources interrupt routine are executing. @@ -108,8 +108,8 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) * notify firmware which source interrupt are coming * by setting status register */ - s->regs[status_addr] = select; - trace_aspeed_intc_trigger_irq(irq, s->regs[status_addr]); + s->regs[status_reg] = select; + trace_aspeed_intc_trigger_irq(irq, s->regs[status_reg]); aspeed_intc_update(s, irq, 1); } } @@ -117,17 +117,17 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); - uint32_t addr = offset >> 2; + uint32_t reg = offset >> 2; uint32_t value = 0; - if (addr >= ASPEED_INTC_NR_REGS) { + if (reg >= ASPEED_INTC_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, offset); return 0; } - value = s->regs[addr]; + value = s->regs[reg]; trace_aspeed_intc_read(offset, size, value); return value; @@ -138,12 +138,12 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, { AspeedINTCState *s = ASPEED_INTC(opaque); AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); - uint32_t addr = offset >> 2; + uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; uint32_t irq; - if (addr >= ASPEED_INTC_NR_REGS) { + if (reg >= ASPEED_INTC_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -152,7 +152,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, trace_aspeed_intc_write(offset, size, data); - switch (addr) { + switch (reg) { case R_GICINT128_EN: case R_GICINT129_EN: case R_GICINT130_EN: @@ -177,7 +177,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, /* disable all source interrupt */ if (!data && !s->enable[irq]) { - s->regs[addr] = data; + s->regs[reg] = data; return; } @@ -187,12 +187,12 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, /* enable new source interrupt */ if (old_enable != s->enable[irq]) { trace_aspeed_intc_enable(s->enable[irq]); - s->regs[addr] = data; + s->regs[reg] = data; return; } /* mask and unmask source interrupt */ - change = s->regs[addr] ^ data; + change = s->regs[reg] ^ data; if (change & data) { s->mask[irq] &= ~change; trace_aspeed_intc_unmask(change, s->mask[irq]); @@ -200,7 +200,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, s->mask[irq] |= change; trace_aspeed_intc_mask(change, s->mask[irq]); } - s->regs[addr] = data; + s->regs[reg] = data; break; case R_GICINT128_STATUS: case R_GICINT129_STATUS: @@ -220,7 +220,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } /* clear status */ - s->regs[addr] &= ~data; + s->regs[reg] &= ~data; /* * These status registers are used for notify sources ISR are executed. @@ -233,7 +233,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } /* All source ISR execution are done */ - if (!s->regs[addr]) { + if (!s->regs[reg]) { trace_aspeed_intc_all_isr_done(irq); if (s->pending[irq]) { /* @@ -241,9 +241,9 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, * notify firmware which source interrupt are pending * by setting status register */ - s->regs[addr] = s->pending[irq]; + s->regs[reg] = s->pending[irq]; s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[addr]); + trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); aspeed_intc_update(s, irq, 1); } else { /* clear irq */ @@ -253,7 +253,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } break; default: - s->regs[addr] = data; + s->regs[reg] = data; break; } From 563afea0aebd15eac74b89467204f4b76b2ee6fa Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:12 +0800 Subject: [PATCH 2373/2892] hw/intc/aspeed: Introduce dynamic allocation for regs array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the size of the "regs" array is 0x2000, which is too large. To save code size and avoid mapping large unused gaps, will update it to only map the useful set of registers. This update will support multiple sub-regions with different sizes. To address the redundant size issue, replace the static "regs" array with a dynamically allocated "regs" memory. Introduce a new "aspeed_intc_unrealize" function to free the allocated "regs" memory. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 12 +++++++++++- include/hw/intc/aspeed_intc.h | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 465f41e4fd..558901570f 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -289,7 +289,7 @@ static void aspeed_intc_reset(DeviceState *dev) { AspeedINTCState *s = ASPEED_INTC(dev); - memset(s->regs, 0, sizeof(s->regs)); + memset(s->regs, 0, ASPEED_INTC_NR_REGS << 2); memset(s->enable, 0, sizeof(s->enable)); memset(s->mask, 0, sizeof(s->mask)); memset(s->pending, 0, sizeof(s->pending)); @@ -307,6 +307,7 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem_container); + s->regs = g_new(uint32_t, ASPEED_INTC_NR_REGS); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); @@ -322,12 +323,21 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) } } +static void aspeed_intc_unrealize(DeviceState *dev) +{ + AspeedINTCState *s = ASPEED_INTC(dev); + + g_free(s->regs); + s->regs = NULL; +} + static void aspeed_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "ASPEED INTC Controller"; dc->realize = aspeed_intc_realize; + dc->unrealize = aspeed_intc_unrealize; device_class_set_legacy_reset(dc, aspeed_intc_reset); dc->vmsd = NULL; } diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 03324f05ab..47ea0520b5 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -27,7 +27,7 @@ struct AspeedINTCState { MemoryRegion iomem; MemoryRegion iomem_container; - uint32_t regs[ASPEED_INTC_NR_REGS]; + uint32_t *regs; OrIRQState orgates[ASPEED_INTC_NR_INTS]; qemu_irq output_pins[ASPEED_INTC_NR_INTS]; From b008465d655ff3ff314fe1ef81031293b582ebaf Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:13 +0800 Subject: [PATCH 2374/2892] hw/intc/aspeed: Support setting different register size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the size of the regs array is 0x2000, which is too large. So far, it only use GICINT128 - GICINT134, and the offsets from 0 to 0x1000 are unused. To save code size, introduce a new class attribute "reg_size" to set the different register sizes for the INTC models in AST2700 and add a regs sub-region in the memory container. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 22 +++++----------------- include/hw/intc/aspeed_intc.h | 2 +- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 558901570f..134922e46f 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -120,13 +120,6 @@ static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) uint32_t reg = offset >> 2; uint32_t value = 0; - if (reg >= ASPEED_INTC_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - return 0; - } - value = s->regs[reg]; trace_aspeed_intc_read(offset, size, value); @@ -143,13 +136,6 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, uint32_t change; uint32_t irq; - if (reg >= ASPEED_INTC_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - return; - } - trace_aspeed_intc_write(offset, size, data); switch (reg) { @@ -288,8 +274,9 @@ static void aspeed_intc_instance_init(Object *obj) static void aspeed_intc_reset(DeviceState *dev) { AspeedINTCState *s = ASPEED_INTC(dev); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); - memset(s->regs, 0, ASPEED_INTC_NR_REGS << 2); + memset(s->regs, 0, aic->nr_regs << 2); memset(s->enable, 0, sizeof(s->enable)); memset(s->mask, 0, sizeof(s->mask)); memset(s->pending, 0, sizeof(s->pending)); @@ -307,9 +294,9 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem_container); - s->regs = g_new(uint32_t, ASPEED_INTC_NR_REGS); + s->regs = g_new(uint32_t, aic->nr_regs); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, - TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); + TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); @@ -361,6 +348,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) aic->num_lines = 32; aic->num_ints = 9; aic->mem_size = 0x4000; + aic->nr_regs = 0x2000 >> 2; } static const TypeInfo aspeed_2700_intc_info = { diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 47ea0520b5..ec4936b3f4 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -16,7 +16,6 @@ #define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) -#define ASPEED_INTC_NR_REGS (0x2000 >> 2) #define ASPEED_INTC_NR_INTS 9 struct AspeedINTCState { @@ -42,6 +41,7 @@ struct AspeedINTCClass { uint32_t num_lines; uint32_t num_ints; uint64_t mem_size; + uint64_t nr_regs; }; #endif /* ASPEED_INTC_H */ From 7ffee511fcf1487e016ae1d11c5e191557a8b804 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:14 +0800 Subject: [PATCH 2375/2892] hw/intc/aspeed: Reduce regs array size by adding a register sub-region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the size of the "regs" array is 0x2000, which is too large. So far, it only uses "GICINT128 to `GICINT134", and the offsets from 0 to 0x1000 are unused. To save code size and avoid mapping large unused gaps, update to only map the useful set of registers: INTC register [0x1000 – 0x1804] Update "reg_size" to 0x808. Introduce a new class attribute "reg_offset" to set the start offset of a "INTC" sub-region. Set the "reg_offset" to 0x1000 for INTC registers. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 50 ++++++++++++++++++++--------------- include/hw/intc/aspeed_intc.h | 1 + 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 134922e46f..d684b4bb4f 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -14,25 +14,31 @@ #include "hw/registerfields.h" #include "qapi/error.h" -/* INTC Registers */ -REG32(GICINT128_EN, 0x1000) -REG32(GICINT128_STATUS, 0x1004) -REG32(GICINT129_EN, 0x1100) -REG32(GICINT129_STATUS, 0x1104) -REG32(GICINT130_EN, 0x1200) -REG32(GICINT130_STATUS, 0x1204) -REG32(GICINT131_EN, 0x1300) -REG32(GICINT131_STATUS, 0x1304) -REG32(GICINT132_EN, 0x1400) -REG32(GICINT132_STATUS, 0x1404) -REG32(GICINT133_EN, 0x1500) -REG32(GICINT133_STATUS, 0x1504) -REG32(GICINT134_EN, 0x1600) -REG32(GICINT134_STATUS, 0x1604) -REG32(GICINT135_EN, 0x1700) -REG32(GICINT135_STATUS, 0x1704) -REG32(GICINT136_EN, 0x1800) -REG32(GICINT136_STATUS, 0x1804) +/* + * INTC Registers + * + * values below are offset by - 0x1000 from datasheet + * because its memory region is start at 0x1000 + * + */ +REG32(GICINT128_EN, 0x000) +REG32(GICINT128_STATUS, 0x004) +REG32(GICINT129_EN, 0x100) +REG32(GICINT129_STATUS, 0x104) +REG32(GICINT130_EN, 0x200) +REG32(GICINT130_STATUS, 0x204) +REG32(GICINT131_EN, 0x300) +REG32(GICINT131_STATUS, 0x304) +REG32(GICINT132_EN, 0x400) +REG32(GICINT132_STATUS, 0x404) +REG32(GICINT133_EN, 0x500) +REG32(GICINT133_STATUS, 0x504) +REG32(GICINT134_EN, 0x600) +REG32(GICINT134_STATUS, 0x604) +REG32(GICINT135_EN, 0x700) +REG32(GICINT135_STATUS, 0x704) +REG32(GICINT136_EN, 0x800) +REG32(GICINT136_STATUS, 0x804) #define GICINT_STATUS_BASE R_GICINT128_STATUS @@ -298,7 +304,8 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); - memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + memory_region_add_subregion(&s->iomem_container, aic->reg_offset, + &s->iomem); qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); @@ -348,7 +355,8 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) aic->num_lines = 32; aic->num_ints = 9; aic->mem_size = 0x4000; - aic->nr_regs = 0x2000 >> 2; + aic->nr_regs = 0x808 >> 2; + aic->reg_offset = 0x1000; } static const TypeInfo aspeed_2700_intc_info = { diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index ec4936b3f4..208e764c4a 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -42,6 +42,7 @@ struct AspeedINTCClass { uint32_t num_ints; uint64_t mem_size; uint64_t nr_regs; + uint64_t reg_offset; }; #endif /* ASPEED_INTC_H */ From 3d6e15eafb3a3977f6659211e08d112807f20626 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:15 +0800 Subject: [PATCH 2376/2892] hw/intc/aspeed: Introduce helper functions for enable and status registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The behavior of the enable and status registers is almost identical between INTC(CPU Die) and INTCIO(IO Die). To reduce duplicated code, adds "aspeed_intc_enable_handler" functions to handle enable register write behavior and "aspeed_intc_status_handler" functions to handle status register write behavior. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 191 ++++++++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 83 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index d684b4bb4f..b58a7ee712 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -120,6 +120,112 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) } } +static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t reg = offset >> 2; + uint32_t old_enable; + uint32_t change; + uint32_t irq; + + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* + * The enable registers are used to enable source interrupts. + * They also handle masking and unmasking of source interrupts + * during the execution of the source ISR. + */ + + /* disable all source interrupt */ + if (!data && !s->enable[irq]) { + s->regs[reg] = data; + return; + } + + old_enable = s->enable[irq]; + s->enable[irq] |= data; + + /* enable new source interrupt */ + if (old_enable != s->enable[irq]) { + trace_aspeed_intc_enable(s->enable[irq]); + s->regs[reg] = data; + return; + } + + /* mask and unmask source interrupt */ + change = s->regs[reg] ^ data; + if (change & data) { + s->mask[irq] &= ~change; + trace_aspeed_intc_unmask(change, s->mask[irq]); + } else { + s->mask[irq] |= change; + trace_aspeed_intc_mask(change, s->mask[irq]); + } + + s->regs[reg] = data; +} + +static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t reg = offset >> 2; + uint32_t irq; + + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); + return; + } + + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* clear status */ + s->regs[reg] &= ~data; + + /* + * These status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + if (data == 0xffffffff) { + return; + } + + /* All source ISR execution are done */ + if (!s->regs[reg]) { + trace_aspeed_intc_all_isr_done(irq); + if (s->pending[irq]) { + /* + * handle pending source interrupt + * notify firmware which source interrupt are pending + * by setting status register + */ + s->regs[reg] = s->pending[irq]; + s->pending[irq] = 0; + trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); + aspeed_intc_update(s, irq, 1); + } else { + /* clear irq */ + trace_aspeed_intc_clear_irq(irq, 0); + aspeed_intc_update(s, irq, 0); + } + } +} + static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); @@ -136,11 +242,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, unsigned size) { AspeedINTCState *s = ASPEED_INTC(opaque); - AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); uint32_t reg = offset >> 2; - uint32_t old_enable; - uint32_t change; - uint32_t irq; trace_aspeed_intc_write(offset, size, data); @@ -154,45 +256,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT134_EN: case R_GICINT135_EN: case R_GICINT136_EN: - irq = (offset & 0x0f00) >> 8; - - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", - __func__, irq); - return; - } - - /* - * These registers are used for enable sources interrupt and - * mask and unmask source interrupt while executing source ISR. - */ - - /* disable all source interrupt */ - if (!data && !s->enable[irq]) { - s->regs[reg] = data; - return; - } - - old_enable = s->enable[irq]; - s->enable[irq] |= data; - - /* enable new source interrupt */ - if (old_enable != s->enable[irq]) { - trace_aspeed_intc_enable(s->enable[irq]); - s->regs[reg] = data; - return; - } - - /* mask and unmask source interrupt */ - change = s->regs[reg] ^ data; - if (change & data) { - s->mask[irq] &= ~change; - trace_aspeed_intc_unmask(change, s->mask[irq]); - } else { - s->mask[irq] |= change; - trace_aspeed_intc_mask(change, s->mask[irq]); - } - s->regs[reg] = data; + aspeed_intc_enable_handler(s, offset, data); break; case R_GICINT128_STATUS: case R_GICINT129_STATUS: @@ -203,46 +267,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT134_STATUS: case R_GICINT135_STATUS: case R_GICINT136_STATUS: - irq = (offset & 0x0f00) >> 8; - - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", - __func__, irq); - return; - } - - /* clear status */ - s->regs[reg] &= ~data; - - /* - * These status registers are used for notify sources ISR are executed. - * If one source ISR is executed, it will clear one bit. - * If it clear all bits, it means to initialize this register status - * rather than sources ISR are executed. - */ - if (data == 0xffffffff) { - return; - } - - /* All source ISR execution are done */ - if (!s->regs[reg]) { - trace_aspeed_intc_all_isr_done(irq); - if (s->pending[irq]) { - /* - * handle pending source interrupt - * notify firmware which source interrupt are pending - * by setting status register - */ - s->regs[reg] = s->pending[irq]; - s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); - aspeed_intc_update(s, irq, 1); - } else { - /* clear irq */ - trace_aspeed_intc_clear_irq(irq, 0); - aspeed_intc_update(s, irq, 0); - } - } + aspeed_intc_status_handler(s, offset, data); break; default: s->regs[reg] = data; From 49da40cf5ffca090f6918ba882db8fb9536792a7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:16 +0800 Subject: [PATCH 2377/2892] hw/intc/aspeed: Add object type name to trace events for better debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, these trace events only refer to INTC. To simplify the INTC model, both INTC(CPU Die) and INTCIO(IO Die) will share the same helper functions. However, it is difficult to recognize whether these trace events are comes from INTC or INTCIO. To make these trace events more readable, adds object type name to the INTC trace events. Update trace events to include the "name" field for better identification. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 32 +++++++++++++++++++------------- hw/intc/trace-events | 24 ++++++++++++------------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index b58a7ee712..d06e697ecc 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -45,6 +45,7 @@ REG32(GICINT136_STATUS, 0x804) static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); if (irq >= aic->num_ints) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", @@ -52,7 +53,7 @@ static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) return; } - trace_aspeed_intc_update_irq(irq, level); + trace_aspeed_intc_update_irq(name, irq, level); qemu_set_irq(s->output_pins[irq], level); } @@ -66,6 +67,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { AspeedINTCState *s = (AspeedINTCState *)opaque; AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); uint32_t select = 0; uint32_t enable; @@ -77,7 +79,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) return; } - trace_aspeed_intc_set_irq(irq, level); + trace_aspeed_intc_set_irq(name, irq, level); enable = s->enable[irq]; if (!level) { @@ -96,7 +98,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) return; } - trace_aspeed_intc_select(select); + trace_aspeed_intc_select(name, select); if (s->mask[irq] || s->regs[status_reg]) { /* @@ -108,14 +110,14 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) * save source interrupt to pending variable. */ s->pending[irq] |= select; - trace_aspeed_intc_pending_irq(irq, s->pending[irq]); + trace_aspeed_intc_pending_irq(name, irq, s->pending[irq]); } else { /* * notify firmware which source interrupt are coming * by setting status register */ s->regs[status_reg] = select; - trace_aspeed_intc_trigger_irq(irq, s->regs[status_reg]); + trace_aspeed_intc_trigger_irq(name, irq, s->regs[status_reg]); aspeed_intc_update(s, irq, 1); } } @@ -124,6 +126,7 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, uint64_t data) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; @@ -154,7 +157,7 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, /* enable new source interrupt */ if (old_enable != s->enable[irq]) { - trace_aspeed_intc_enable(s->enable[irq]); + trace_aspeed_intc_enable(name, s->enable[irq]); s->regs[reg] = data; return; } @@ -163,10 +166,10 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, change = s->regs[reg] ^ data; if (change & data) { s->mask[irq] &= ~change; - trace_aspeed_intc_unmask(change, s->mask[irq]); + trace_aspeed_intc_unmask(name, change, s->mask[irq]); } else { s->mask[irq] |= change; - trace_aspeed_intc_mask(change, s->mask[irq]); + trace_aspeed_intc_mask(name, change, s->mask[irq]); } s->regs[reg] = data; @@ -176,6 +179,7 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, uint64_t data) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; uint32_t irq; @@ -207,7 +211,7 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, /* All source ISR execution are done */ if (!s->regs[reg]) { - trace_aspeed_intc_all_isr_done(irq); + trace_aspeed_intc_all_isr_done(name, irq); if (s->pending[irq]) { /* * handle pending source interrupt @@ -216,11 +220,11 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, */ s->regs[reg] = s->pending[irq]; s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); + trace_aspeed_intc_trigger_irq(name, irq, s->regs[reg]); aspeed_intc_update(s, irq, 1); } else { /* clear irq */ - trace_aspeed_intc_clear_irq(irq, 0); + trace_aspeed_intc_clear_irq(name, irq, 0); aspeed_intc_update(s, irq, 0); } } @@ -229,11 +233,12 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; uint32_t value = 0; value = s->regs[reg]; - trace_aspeed_intc_read(offset, size, value); + trace_aspeed_intc_read(name, offset, size, value); return value; } @@ -242,9 +247,10 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, unsigned size) { AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; - trace_aspeed_intc_write(offset, size, data); + trace_aspeed_intc_write(name, offset, size, data); switch (reg) { case R_GICINT128_EN: diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 3dcf147198..e9ca34755e 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -80,18 +80,18 @@ aspeed_vic_update_irq(int flags) "Raising IRQ: %d" aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 # aspeed_intc.c -aspeed_intc_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 -aspeed_intc_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 -aspeed_intc_set_irq(int irq, int level) "Set IRQ %d: %d" -aspeed_intc_clear_irq(int irq, int level) "Clear IRQ %d: %d" -aspeed_intc_update_irq(int irq, int level) "Update IRQ: %d: %d" -aspeed_intc_pending_irq(int irq, uint32_t value) "Pending IRQ: %d: 0x%x" -aspeed_intc_trigger_irq(int irq, uint32_t value) "Trigger IRQ: %d: 0x%x" -aspeed_intc_all_isr_done(int irq) "All source ISR execution are done: %d" -aspeed_intc_enable(uint32_t value) "Enable: 0x%x" -aspeed_intc_select(uint32_t value) "Select: 0x%x" -aspeed_intc_mask(uint32_t change, uint32_t value) "Mask: 0x%x: 0x%x" -aspeed_intc_unmask(uint32_t change, uint32_t value) "UnMask: 0x%x: 0x%x" +aspeed_intc_read(const char *s, uint64_t offset, unsigned size, uint32_t value) "%s: From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_write(const char *s, uint64_t offset, unsigned size, uint32_t data) "%s: To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_set_irq(const char *s, int irq, int level) "%s: Set IRQ %d: %d" +aspeed_intc_clear_irq(const char *s, int irq, int level) "%s: Clear IRQ %d: %d" +aspeed_intc_update_irq(const char *s, int irq, int level) "%s: Update IRQ: %d: %d" +aspeed_intc_pending_irq(const char *s, int irq, uint32_t value) "%s: Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(const char *s, int irq, uint32_t value) "%s: Trigger IRQ: %d: 0x%x" +aspeed_intc_all_isr_done(const char *s, int irq) "%s: All source ISR execution are done: %d" +aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" +aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" +aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" +aspeed_intc_unmask(const char *s, uint32_t change, uint32_t value) "%s: UnMask: 0x%x: 0x%x" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" From de4e979973ce5c09d0b9a6d8a7aa17ab77c869c8 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:17 +0800 Subject: [PATCH 2378/2892] hw/arm/aspeed: Rename IRQ table and machine name for AST2700 A0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, AST2700 SoC only supports A0. To support AST2700 A1, rename its IRQ table and machine name. To follow the machine deprecation rule, the initial machine "ast2700-evb" is aliased to "ast2700a0-evb." In the future, we will alias "ast2700-evb" to new SoCs, such as "ast2700a1-evb." Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 9 +++++---- hw/arm/aspeed_ast27x0.c | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index c6c18596d6..18f7c450da 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1673,12 +1673,13 @@ static void ast2700_evb_i2c_init(AspeedMachineState *bmc) TYPE_TMP105, 0x4d); } -static void aspeed_machine_ast2700_evb_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - mc->desc = "Aspeed AST2700 EVB (Cortex-A35)"; + mc->alias = "ast2700-evb"; + mc->desc = "Aspeed AST2700 A0 EVB (Cortex-A35)"; amc->soc_name = "ast2700-a0"; amc->hw_strap1 = AST2700_EVB_HW_STRAP1; amc->hw_strap2 = AST2700_EVB_HW_STRAP2; @@ -1817,9 +1818,9 @@ static const TypeInfo aspeed_machine_types[] = { .class_init = aspeed_minibmc_machine_ast1030_evb_class_init, #ifdef TARGET_AARCH64 }, { - .name = MACHINE_TYPE_NAME("ast2700-evb"), + .name = MACHINE_TYPE_NAME("ast2700a0-evb"), .parent = TYPE_ASPEED_MACHINE, - .class_init = aspeed_machine_ast2700_evb_class_init, + .class_init = aspeed_machine_ast2700a0_evb_class_init, #endif }, { .name = TYPE_ASPEED_MACHINE, diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index f14d2ea175..44ddfdd2c6 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -73,7 +73,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { #define AST2700_MAX_IRQ 256 /* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ -static const int aspeed_soc_ast2700_irqmap[] = { +static const int aspeed_soc_ast2700a0_irqmap[] = { [ASPEED_DEV_UART0] = 132, [ASPEED_DEV_UART1] = 132, [ASPEED_DEV_UART2] = 132, @@ -766,7 +766,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) create_unimplemented_device("ast2700.io", 0x0, 0x4000000); } -static void aspeed_soc_ast2700_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a35"), @@ -788,7 +788,7 @@ static void aspeed_soc_ast2700_class_init(ObjectClass *oc, void *data) sc->uarts_num = 13; sc->num_cpus = 4; sc->uarts_base = ASPEED_DEV_UART0; - sc->irqmap = aspeed_soc_ast2700_irqmap; + sc->irqmap = aspeed_soc_ast2700a0_irqmap; sc->memmap = aspeed_soc_ast2700_memmap; sc->get_irq = aspeed_soc_ast2700_get_irq; } @@ -803,7 +803,7 @@ static const TypeInfo aspeed_soc_ast27x0_types[] = { .name = "ast2700-a0", .parent = TYPE_ASPEED27X0_SOC, .instance_init = aspeed_soc_ast2700_init, - .class_init = aspeed_soc_ast2700_class_init, + .class_init = aspeed_soc_ast2700a0_class_init, }, }; From 617cacefb7f7062f026994a256f8798d767cff73 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:18 +0800 Subject: [PATCH 2379/2892] hw/arm/aspeed_ast27x0: Sort the IRQ table by IRQ number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve readability, sort the IRQ table by IRQ number. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 44ddfdd2c6..642a8f5521 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -74,27 +74,13 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { /* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ static const int aspeed_soc_ast2700a0_irqmap[] = { - [ASPEED_DEV_UART0] = 132, - [ASPEED_DEV_UART1] = 132, - [ASPEED_DEV_UART2] = 132, - [ASPEED_DEV_UART3] = 132, - [ASPEED_DEV_UART4] = 8, - [ASPEED_DEV_UART5] = 132, - [ASPEED_DEV_UART6] = 132, - [ASPEED_DEV_UART7] = 132, - [ASPEED_DEV_UART8] = 132, - [ASPEED_DEV_UART9] = 132, - [ASPEED_DEV_UART10] = 132, - [ASPEED_DEV_UART11] = 132, - [ASPEED_DEV_UART12] = 132, - [ASPEED_DEV_FMC] = 131, [ASPEED_DEV_SDMC] = 0, - [ASPEED_DEV_SCU] = 12, - [ASPEED_DEV_ADC] = 130, + [ASPEED_DEV_HACE] = 4, [ASPEED_DEV_XDMA] = 5, - [ASPEED_DEV_EMMC] = 15, - [ASPEED_DEV_GPIO] = 130, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_SCU] = 12, [ASPEED_DEV_RTC] = 13, + [ASPEED_DEV_EMMC] = 15, [ASPEED_DEV_TIMER1] = 16, [ASPEED_DEV_TIMER2] = 17, [ASPEED_DEV_TIMER3] = 18, @@ -103,19 +89,33 @@ static const int aspeed_soc_ast2700a0_irqmap[] = { [ASPEED_DEV_TIMER6] = 21, [ASPEED_DEV_TIMER7] = 22, [ASPEED_DEV_TIMER8] = 23, - [ASPEED_DEV_WDT] = 131, - [ASPEED_DEV_PWM] = 131, + [ASPEED_DEV_DP] = 28, [ASPEED_DEV_LPC] = 128, [ASPEED_DEV_IBT] = 128, + [ASPEED_DEV_KCS] = 128, + [ASPEED_DEV_ADC] = 130, + [ASPEED_DEV_GPIO] = 130, [ASPEED_DEV_I2C] = 130, - [ASPEED_DEV_PECI] = 133, + [ASPEED_DEV_FMC] = 131, + [ASPEED_DEV_WDT] = 131, + [ASPEED_DEV_PWM] = 131, + [ASPEED_DEV_I3C] = 131, + [ASPEED_DEV_UART0] = 132, + [ASPEED_DEV_UART1] = 132, + [ASPEED_DEV_UART2] = 132, + [ASPEED_DEV_UART3] = 132, + [ASPEED_DEV_UART5] = 132, + [ASPEED_DEV_UART6] = 132, + [ASPEED_DEV_UART7] = 132, + [ASPEED_DEV_UART8] = 132, + [ASPEED_DEV_UART9] = 132, + [ASPEED_DEV_UART10] = 132, + [ASPEED_DEV_UART11] = 132, + [ASPEED_DEV_UART12] = 132, [ASPEED_DEV_ETH1] = 132, [ASPEED_DEV_ETH2] = 132, [ASPEED_DEV_ETH3] = 132, - [ASPEED_DEV_HACE] = 4, - [ASPEED_DEV_KCS] = 128, - [ASPEED_DEV_DP] = 28, - [ASPEED_DEV_I3C] = 131, + [ASPEED_DEV_PECI] = 133, [ASPEED_DEV_SDHCI] = 133, }; From 28194d5d15b92f0b3f6628236f93001c3fdd0d39 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:19 +0800 Subject: [PATCH 2380/2892] hw/intc/aspeed: Support different memory region ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation set the "aspeed_intc_ops" struct, containing read and write callbacks, to be used when I/O is performed on the INTC region. Both "aspeed_intc_read" and "aspeed_intc_write" callback functions were used for INTC (CPU Die). To support the INTCIO (IO Die) model, introduces a new "reg_ops" class attribute. This allows setting different memory region operations to support different INTC models. Will introduce "aspeed_intcio_read" and "aspeed_intcio_write" callback functions are used for INTCIO. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 5 ++++- include/hw/intc/aspeed_intc.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index d06e697ecc..d8ee6e1c04 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -332,7 +332,7 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem_container); s->regs = g_new(uint32_t, aic->nr_regs); - memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, + memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s, TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); memory_region_add_subregion(&s->iomem_container, aic->reg_offset, @@ -359,12 +359,15 @@ static void aspeed_intc_unrealize(DeviceState *dev) static void aspeed_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); dc->desc = "ASPEED INTC Controller"; dc->realize = aspeed_intc_realize; dc->unrealize = aspeed_intc_unrealize; device_class_set_legacy_reset(dc, aspeed_intc_reset); dc->vmsd = NULL; + + aic->reg_ops = &aspeed_intc_ops; } static const TypeInfo aspeed_intc_info = { diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 208e764c4a..3433277d87 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -43,6 +43,7 @@ struct AspeedINTCClass { uint64_t mem_size; uint64_t nr_regs; uint64_t reg_offset; + const MemoryRegionOps *reg_ops; }; #endif /* ASPEED_INTC_H */ From 63f3618f9be0f28ff36cd4b5685877715b97e669 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:20 +0800 Subject: [PATCH 2381/2892] hw/intc/aspeed: Rename num_ints to num_inpins for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support AST2700 A1, some registers of the INTC(CPU Die) support one input pin to multiple output pins. Renamed "num_ints" to "num_inpins" in the INTC controller code for better clarity and consistency in naming conventions. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 2 +- hw/intc/aspeed_intc.c | 31 +++++++++++++++++-------------- include/hw/intc/aspeed_intc.h | 11 ++++++----- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 642a8f5521..1a3eb02af3 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -535,7 +535,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_INTC]); /* GICINT orgates -> INTC -> GIC */ - for (i = 0; i < ic->num_ints; i++) { + for (i = 0; i < ic->num_inpins; i++) { qdev_connect_gpio_out(DEVICE(&a->intc.orgates[i]), 0, qdev_get_gpio_in(DEVICE(&a->intc), i)); sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc), i, diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index d8ee6e1c04..217fda6fe0 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -47,8 +47,9 @@ static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -60,7 +61,7 @@ static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) /* * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. * Utilize "address & 0x0f00" to get the irq and irq output pin index - * The value of irq should be 0 to num_ints. + * The value of irq should be 0 to num_inpins. * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. */ static void aspeed_intc_set_irq(void *opaque, int irq, int level) @@ -73,8 +74,8 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) uint32_t enable; int i; - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -134,8 +135,9 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, irq = (offset & 0x0f00) >> 8; - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -190,8 +192,9 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, irq = (offset & 0x0f00) >> 8; - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -299,8 +302,8 @@ static void aspeed_intc_instance_init(Object *obj) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); int i; - assert(aic->num_ints <= ASPEED_INTC_NR_INTS); - for (i = 0; i < aic->num_ints; i++) { + assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS); + for (i = 0; i < aic->num_inpins; i++) { object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i], TYPE_OR_IRQ); object_property_set_int(OBJECT(&s->orgates[i]), "num-lines", @@ -338,9 +341,9 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->iomem_container, aic->reg_offset, &s->iomem); - qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins); - for (i = 0; i < aic->num_ints; i++) { + for (i = 0; i < aic->num_inpins; i++) { if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { return; } @@ -387,7 +390,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; - aic->num_ints = 9; + aic->num_inpins = 9; aic->mem_size = 0x4000; aic->nr_regs = 0x808 >> 2; aic->reg_offset = 0x1000; diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 3433277d87..58be5b3e13 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -17,6 +17,7 @@ OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) #define ASPEED_INTC_NR_INTS 9 +#define ASPEED_INTC_MAX_INPINS 9 struct AspeedINTCState { /*< private >*/ @@ -27,19 +28,19 @@ struct AspeedINTCState { MemoryRegion iomem_container; uint32_t *regs; - OrIRQState orgates[ASPEED_INTC_NR_INTS]; + OrIRQState orgates[ASPEED_INTC_MAX_INPINS]; qemu_irq output_pins[ASPEED_INTC_NR_INTS]; - uint32_t enable[ASPEED_INTC_NR_INTS]; - uint32_t mask[ASPEED_INTC_NR_INTS]; - uint32_t pending[ASPEED_INTC_NR_INTS]; + uint32_t enable[ASPEED_INTC_MAX_INPINS]; + uint32_t mask[ASPEED_INTC_MAX_INPINS]; + uint32_t pending[ASPEED_INTC_MAX_INPINS]; }; struct AspeedINTCClass { SysBusDeviceClass parent_class; uint32_t num_lines; - uint32_t num_ints; + uint32_t num_inpins; uint64_t mem_size; uint64_t nr_regs; uint64_t reg_offset; From 35c909cd80d4095690bb1c98c263b01d9617de65 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:21 +0800 Subject: [PATCH 2382/2892] hw/intc/aspeed: Add support for multiple output pins in INTC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for multiple output pins in the INTC controller to accommodate the AST2700 A1. Introduced "num_outpins" to represent the number of output pins. Updated the IRQ handling logic to initialize and connect output pins separately from input pins. Modified the "aspeed_soc_ast2700_realize" function to connect source orgates to INTC and INTC to GIC128 - GIC136. Updated the "aspeed_intc_realize" function to initialize output pins. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 6 +++++- hw/intc/aspeed_intc.c | 4 ++++ include/hw/intc/aspeed_intc.h | 5 +++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 1a3eb02af3..3cb95210a0 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -534,10 +534,14 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc), 0, sc->memmap[ASPEED_DEV_INTC]); - /* GICINT orgates -> INTC -> GIC */ + /* source orgates -> INTC */ for (i = 0; i < ic->num_inpins; i++) { qdev_connect_gpio_out(DEVICE(&a->intc.orgates[i]), 0, qdev_get_gpio_in(DEVICE(&a->intc), i)); + } + + /* INTC -> GIC128 - GIC136 */ + for (i = 0; i < ic->num_outpins; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc), i, qdev_get_gpio_in(DEVICE(&a->gic), aspeed_soc_ast2700_gic_intcmap[i].irq)); diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 217fda6fe0..6f37afc17e 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -347,6 +347,9 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { return; } + } + + for (i = 0; i < aic->num_outpins; i++) { sysbus_init_irq(sbd, &s->output_pins[i]); } } @@ -391,6 +394,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; aic->num_inpins = 9; + aic->num_outpins = 9; aic->mem_size = 0x4000; aic->nr_regs = 0x808 >> 2; aic->reg_offset = 0x1000; diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 58be5b3e13..2a22e30846 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -16,8 +16,8 @@ #define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) -#define ASPEED_INTC_NR_INTS 9 #define ASPEED_INTC_MAX_INPINS 9 +#define ASPEED_INTC_MAX_OUTPINS 9 struct AspeedINTCState { /*< private >*/ @@ -29,7 +29,7 @@ struct AspeedINTCState { uint32_t *regs; OrIRQState orgates[ASPEED_INTC_MAX_INPINS]; - qemu_irq output_pins[ASPEED_INTC_NR_INTS]; + qemu_irq output_pins[ASPEED_INTC_MAX_OUTPINS]; uint32_t enable[ASPEED_INTC_MAX_INPINS]; uint32_t mask[ASPEED_INTC_MAX_INPINS]; @@ -41,6 +41,7 @@ struct AspeedINTCClass { uint32_t num_lines; uint32_t num_inpins; + uint32_t num_outpins; uint64_t mem_size; uint64_t nr_regs; uint64_t reg_offset; From c6c5e63d46add459732d8d8d3b84bd5d26dff0ad Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:22 +0800 Subject: [PATCH 2383/2892] hw/intc/aspeed: Refactor INTC to support separate input and output pin indices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactors the INTC to distinguish between input and output pin indices, improving interrupt handling clarity and accuracy. Updated the functions to handle both input and output pin indices. Added detailed logging for input and output pin indices in trace events. These changes ensure that the INTC controller can handle multiple input and output pins, improving support for the AST2700 A1. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-14-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 97 +++++++++++++++++++++++++++---------------- hw/intc/trace-events | 12 +++--- 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 6f37afc17e..1cbee0e17a 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -42,20 +42,32 @@ REG32(GICINT136_STATUS, 0x804) #define GICINT_STATUS_BASE R_GICINT128_STATUS -static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) +/* + * Update the state of an interrupt controller pin by setting + * the specified output pin to the given level. + * The input pin index should be between 0 and the number of input pins. + * The output pin index should be between 0 and the number of output pins. + */ +static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, + int outpin_idx, int level) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - if (irq >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid input pin index: %d\n", - __func__, irq); + if (inpin_idx >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", + __func__, inpin_idx); return; } - trace_aspeed_intc_update_irq(name, irq, level); - qemu_set_irq(s->output_pins[irq], level); + if (outpin_idx >= aic->num_outpins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid output pin index: %d\n", + __func__, outpin_idx); + return; + } + + trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level); + qemu_set_irq(s->output_pins[outpin_idx], level); } /* @@ -72,23 +84,28 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); uint32_t select = 0; uint32_t enable; + int outpin_idx; + int inpin_idx; int i; + outpin_idx = irq; + inpin_idx = irq; + if (irq >= aic->num_inpins) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", __func__, irq); return; } - trace_aspeed_intc_set_irq(name, irq, level); - enable = s->enable[irq]; + trace_aspeed_intc_set_irq(name, inpin_idx, level); + enable = s->enable[inpin_idx]; if (!level) { return; } for (i = 0; i < aic->num_lines; i++) { - if (s->orgates[irq].levels[i]) { + if (s->orgates[inpin_idx].levels[i]) { if (enable & BIT(i)) { select |= BIT(i); } @@ -101,7 +118,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) trace_aspeed_intc_select(name, select); - if (s->mask[irq] || s->regs[status_reg]) { + if (s->mask[inpin_idx] || s->regs[status_reg]) { /* * a. mask is not 0 means in ISR mode * sources interrupt routine are executing. @@ -110,16 +127,17 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) * * save source interrupt to pending variable. */ - s->pending[irq] |= select; - trace_aspeed_intc_pending_irq(name, irq, s->pending[irq]); + s->pending[inpin_idx] |= select; + trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]); } else { /* * notify firmware which source interrupt are coming * by setting status register */ s->regs[status_reg] = select; - trace_aspeed_intc_trigger_irq(name, irq, s->regs[status_reg]); - aspeed_intc_update(s, irq, 1); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); } } @@ -131,14 +149,16 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; + int inpin_idx; uint32_t irq; irq = (offset & 0x0f00) >> 8; + inpin_idx = irq; - if (irq >= aic->num_inpins) { + if (inpin_idx >= aic->num_inpins) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, irq); + __func__, inpin_idx); return; } @@ -149,17 +169,17 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, */ /* disable all source interrupt */ - if (!data && !s->enable[irq]) { + if (!data && !s->enable[inpin_idx]) { s->regs[reg] = data; return; } - old_enable = s->enable[irq]; - s->enable[irq] |= data; + old_enable = s->enable[inpin_idx]; + s->enable[inpin_idx] |= data; /* enable new source interrupt */ - if (old_enable != s->enable[irq]) { - trace_aspeed_intc_enable(name, s->enable[irq]); + if (old_enable != s->enable[inpin_idx]) { + trace_aspeed_intc_enable(name, s->enable[inpin_idx]); s->regs[reg] = data; return; } @@ -167,11 +187,11 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, /* mask and unmask source interrupt */ change = s->regs[reg] ^ data; if (change & data) { - s->mask[irq] &= ~change; - trace_aspeed_intc_unmask(name, change, s->mask[irq]); + s->mask[inpin_idx] &= ~change; + trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]); } else { - s->mask[irq] |= change; - trace_aspeed_intc_mask(name, change, s->mask[irq]); + s->mask[inpin_idx] |= change; + trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]); } s->regs[reg] = data; @@ -183,6 +203,8 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; + int outpin_idx; + int inpin_idx; uint32_t irq; if (!data) { @@ -191,11 +213,13 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, } irq = (offset & 0x0f00) >> 8; + outpin_idx = irq; + inpin_idx = irq; - if (irq >= aic->num_inpins) { + if (inpin_idx >= aic->num_inpins) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, irq); + __func__, inpin_idx); return; } @@ -214,21 +238,22 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, /* All source ISR execution are done */ if (!s->regs[reg]) { - trace_aspeed_intc_all_isr_done(name, irq); - if (s->pending[irq]) { + trace_aspeed_intc_all_isr_done(name, inpin_idx); + if (s->pending[inpin_idx]) { /* * handle pending source interrupt * notify firmware which source interrupt are pending * by setting status register */ - s->regs[reg] = s->pending[irq]; - s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(name, irq, s->regs[reg]); - aspeed_intc_update(s, irq, 1); + s->regs[reg] = s->pending[inpin_idx]; + s->pending[inpin_idx] = 0; + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); } else { /* clear irq */ - trace_aspeed_intc_clear_irq(name, irq, 0); - aspeed_intc_update(s, irq, 0); + trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0); + aspeed_intc_update(s, inpin_idx, outpin_idx, 0); } } } diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e9ca34755e..e97eea820b 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -82,12 +82,12 @@ aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 # aspeed_intc.c aspeed_intc_read(const char *s, uint64_t offset, unsigned size, uint32_t value) "%s: From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_intc_write(const char *s, uint64_t offset, unsigned size, uint32_t data) "%s: To 0x%" PRIx64 " of size %u: 0x%" PRIx32 -aspeed_intc_set_irq(const char *s, int irq, int level) "%s: Set IRQ %d: %d" -aspeed_intc_clear_irq(const char *s, int irq, int level) "%s: Clear IRQ %d: %d" -aspeed_intc_update_irq(const char *s, int irq, int level) "%s: Update IRQ: %d: %d" -aspeed_intc_pending_irq(const char *s, int irq, uint32_t value) "%s: Pending IRQ: %d: 0x%x" -aspeed_intc_trigger_irq(const char *s, int irq, uint32_t value) "%s: Trigger IRQ: %d: 0x%x" -aspeed_intc_all_isr_done(const char *s, int irq) "%s: All source ISR execution are done: %d" +aspeed_intc_set_irq(const char *s, int inpin_idx, int level) "%s: Set IRQ %d: %d" +aspeed_intc_clear_irq(const char *s, int inpin_idx, int outpin_idx, int level) "%s: Clear IRQ %d-%d: %d" +aspeed_intc_update_irq(const char *s, int inpin_idx, int outpin_idx, int level) "%s: Update IRQ: %d-%d: %d" +aspeed_intc_pending_irq(const char *s, int inpin_idx, uint32_t value) "%s: Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(const char *s, int inpin_idx, int outpin_idx, uint32_t value) "%s: Trigger IRQ: %d-%d: 0x%x" +aspeed_intc_all_isr_done(const char *s, int inpin_idx) "%s: All source ISR execution are done: %d" aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" From ab24c6a2df8e6c8055b6f1dfe80697320b327c50 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:23 +0800 Subject: [PATCH 2384/2892] hw/intc/aspeed: Introduce AspeedINTCIRQ structure to save the irq index and register address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The INTC controller supports GICINT128 to GICINT136, mapping 1:1 to input and output IRQs 0 to 8. Previously, the formula "address & 0x0f00" was used to derive the IRQ index numbers. However, the INTC controller also supports GICINT192_201, mapping 1 input IRQ pin to 10 output IRQ pins. The pin numbers for input and output are different. It is difficult to use a formula to determine the index number of INTC model supported input and output IRQs. To simplify and improve readability, introduces the AspeedINTCIRQ structure to save the input/output IRQ index and its enable/status register address. Introduce the "aspeed_2700_intc_irqs" table to store IRQ information for INTC. Introduce the "aspeed_intc_get_irq" function to retrieve the input/output IRQ pin index from the provided status/enable register address. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-15-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 87 +++++++++++++++++++---------------- include/hw/intc/aspeed_intc.h | 10 ++++ 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 1cbee0e17a..be24516ec9 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -40,7 +40,23 @@ REG32(GICINT135_STATUS, 0x704) REG32(GICINT136_EN, 0x800) REG32(GICINT136_STATUS, 0x804) -#define GICINT_STATUS_BASE R_GICINT128_STATUS +static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, + uint32_t reg) +{ + int i; + + for (i = 0; i < aic->irq_table_count; i++) { + if (aic->irq_table[i].enable_reg == reg || + aic->irq_table[i].status_reg == reg) { + return &aic->irq_table[i]; + } + } + + /* + * Invalid reg. + */ + g_assert_not_reached(); +} /* * Update the state of an interrupt controller pin by setting @@ -54,17 +70,7 @@ static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - if (inpin_idx >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, inpin_idx); - return; - } - - if (outpin_idx >= aic->num_outpins) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid output pin index: %d\n", - __func__, outpin_idx); - return; - } + assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins)); trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level); qemu_set_irq(s->output_pins[outpin_idx], level); @@ -81,21 +87,20 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) AspeedINTCState *s = (AspeedINTCState *)opaque; AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); + const AspeedINTCIRQ *intc_irq; + uint32_t status_reg; uint32_t select = 0; uint32_t enable; int outpin_idx; int inpin_idx; int i; - outpin_idx = irq; - inpin_idx = irq; + assert(irq < aic->num_inpins); - if (irq >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, irq); - return; - } + intc_irq = &aic->irq_table[irq]; + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; trace_aspeed_intc_set_irq(name, inpin_idx, level); enable = s->enable[inpin_idx]; @@ -146,21 +151,16 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; int inpin_idx; - uint32_t irq; - irq = (offset & 0x0f00) >> 8; - inpin_idx = irq; + intc_irq = aspeed_intc_get_irq(aic, reg); + inpin_idx = intc_irq->inpin_idx; - if (inpin_idx >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid input pin index: %d\n", - __func__, inpin_idx); - return; - } + assert(inpin_idx < aic->num_inpins); /* * The enable registers are used to enable source interrupts. @@ -202,26 +202,21 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; uint32_t reg = offset >> 2; int outpin_idx; int inpin_idx; - uint32_t irq; if (!data) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); return; } - irq = (offset & 0x0f00) >> 8; - outpin_idx = irq; - inpin_idx = irq; + intc_irq = aspeed_intc_get_irq(aic, reg); + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; - if (inpin_idx >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid input pin index: %d\n", - __func__, inpin_idx); - return; - } + assert(inpin_idx < aic->num_inpins); /* clear status */ s->regs[reg] &= ~data; @@ -411,6 +406,18 @@ static const TypeInfo aspeed_intc_info = { .abstract = true, }; +static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS}, + {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS}, + {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS}, + {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS}, + {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS}, + {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS}, + {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS}, + {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS}, + {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS}, +}; + static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -423,6 +430,8 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) aic->mem_size = 0x4000; aic->nr_regs = 0x808 >> 2; aic->reg_offset = 0x1000; + aic->irq_table = aspeed_2700_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs); } static const TypeInfo aspeed_2700_intc_info = { diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 2a22e30846..e6c3a27264 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -19,6 +19,14 @@ OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) #define ASPEED_INTC_MAX_INPINS 9 #define ASPEED_INTC_MAX_OUTPINS 9 +typedef struct AspeedINTCIRQ { + int inpin_idx; + int outpin_idx; + int num_outpins; + uint32_t enable_reg; + uint32_t status_reg; +} AspeedINTCIRQ; + struct AspeedINTCState { /*< private >*/ SysBusDevice parent_obj; @@ -46,6 +54,8 @@ struct AspeedINTCClass { uint64_t nr_regs; uint64_t reg_offset; const MemoryRegionOps *reg_ops; + const AspeedINTCIRQ *irq_table; + int irq_table_count; }; #endif /* ASPEED_INTC_H */ From 5824e8bf6beb226aa5dee94d4e92b671e9b082f1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:24 +0800 Subject: [PATCH 2385/2892] hw/intc/aspeed: Introduce IRQ handler function to reduce code duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The behavior of the INTC set IRQ is almost identical between INTC and INTCIO. To reduce duplicated code, introduce the "aspeed_intc_set_irq_handler" function to handle both INTC and INTCIO IRQ behavior. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-16-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 82 ++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index be24516ec9..3aa97add8b 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -76,53 +76,19 @@ static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, qemu_set_irq(s->output_pins[outpin_idx], level); } -/* - * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. - * Utilize "address & 0x0f00" to get the irq and irq output pin index - * The value of irq should be 0 to num_inpins. - * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. - */ -static void aspeed_intc_set_irq(void *opaque, int irq, int level) +static void aspeed_intc_set_irq_handler(AspeedINTCState *s, + const AspeedINTCIRQ *intc_irq, + uint32_t select) { - AspeedINTCState *s = (AspeedINTCState *)opaque; - AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - const AspeedINTCIRQ *intc_irq; uint32_t status_reg; - uint32_t select = 0; - uint32_t enable; int outpin_idx; int inpin_idx; - int i; - assert(irq < aic->num_inpins); - - intc_irq = &aic->irq_table[irq]; status_reg = intc_irq->status_reg; outpin_idx = intc_irq->outpin_idx; inpin_idx = intc_irq->inpin_idx; - trace_aspeed_intc_set_irq(name, inpin_idx, level); - enable = s->enable[inpin_idx]; - - if (!level) { - return; - } - - for (i = 0; i < aic->num_lines; i++) { - if (s->orgates[inpin_idx].levels[i]) { - if (enable & BIT(i)) { - select |= BIT(i); - } - } - } - - if (!select) { - return; - } - - trace_aspeed_intc_select(name, select); - if (s->mask[inpin_idx] || s->regs[status_reg]) { /* * a. mask is not 0 means in ISR mode @@ -146,6 +112,48 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) } } +/* + * GICINT128 to GICINT136 map 1:1 to input and output IRQs 0 to 8. + * The value of input IRQ should be between 0 and the number of inputs. + */ +static void aspeed_intc_set_irq(void *opaque, int irq, int level) +{ + AspeedINTCState *s = (AspeedINTCState *)opaque; + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; + uint32_t select = 0; + uint32_t enable; + int inpin_idx; + int i; + + assert(irq < aic->num_inpins); + + intc_irq = &aic->irq_table[irq]; + inpin_idx = intc_irq->inpin_idx; + trace_aspeed_intc_set_irq(name, inpin_idx, level); + enable = s->enable[inpin_idx]; + + if (!level) { + return; + } + + for (i = 0; i < aic->num_lines; i++) { + if (s->orgates[inpin_idx].levels[i]) { + if (enable & BIT(i)) { + select |= BIT(i); + } + } + } + + if (!select) { + return; + } + + trace_aspeed_intc_select(name, select); + aspeed_intc_set_irq_handler(s, intc_irq, select); +} + static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, uint64_t data) { From 9178ff91f3105d25fef8e595014fbdfba7d9e278 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:25 +0800 Subject: [PATCH 2386/2892] hw/intc/aspeed: Add Support for Multi-Output IRQ Handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This update introduces support for handling multi-output IRQs in the AST2700 interrupt controller (INTC), specifically for GICINT192_201. GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9. Each status bit corresponds to a specific IRQ. Implemented "aspeed_intc_set_irq_handler_multi_outpins" to handle IRQs with multiple output pins. Introduced "aspeed_intc_status_handler_multi_outpins" for managing status registers associated with multi-output IRQs. Added new IRQ definitions for GICINT192_201 in INTC. Adjusted the IRQ array to accommodate 10 input pins and 19 output pins, aligning with the new GICINT192_201 mappings. |------------------------------| | INTC | |inpin[0:0]--------->outpin[0] | |inpin[0:1]--------->outpin[1] | |inpin[0:2]--------->outpin[2] | |inpin[0:3]--------->outpin[3] | orgates[0]-------> |inpin[0:4]--------->outpin[4] | |inpin[0:5]--------->outpin[5] | |inpin[0:6]--------->outpin[6] | |inpin[0:7]--------->outpin[7] | |inpin[0:8]--------->outpin[8] | |inpin[0:9]--------->outpin[9] | | | orgates[1]------> |inpin[1]----------->outpin[10]| orgates[2]------> |inpin[2]----------->outpin[11]| orgates[3]------> |inpin[3]----------->outpin[12]| orgates[4]------> |inpin[4]----------->outpin[13]| orgates[5]------> |inpin[5]----------->outpin[14]| orgates[6]------> |inpin[6]----------->outpin[15]| orgates[7]------> |inpin[7]----------->outpin[16]| orgates[8]------> |inpin[8]----------->outpin[17]| orgates[9]------> |inpin[9]----------->outpin[18]| |------------------------------| Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-17-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 149 ++++++++++++++++++++++++++++++---- hw/intc/trace-events | 1 + include/hw/intc/aspeed_intc.h | 4 +- 3 files changed, 137 insertions(+), 17 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 3aa97add8b..f2ca9237ea 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -39,6 +39,8 @@ REG32(GICINT135_EN, 0x700) REG32(GICINT135_STATUS, 0x704) REG32(GICINT136_EN, 0x800) REG32(GICINT136_STATUS, 0x804) +REG32(GICINT192_201_EN, 0xB00) +REG32(GICINT192_201_STATUS, 0xB04) static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, uint32_t reg) @@ -112,9 +114,55 @@ static void aspeed_intc_set_irq_handler(AspeedINTCState *s, } } +static void aspeed_intc_set_irq_handler_multi_outpins(AspeedINTCState *s, + const AspeedINTCIRQ *intc_irq, uint32_t select) +{ + const char *name = object_get_typename(OBJECT(s)); + uint32_t status_reg; + int num_outpins; + int outpin_idx; + int inpin_idx; + int i; + + num_outpins = intc_irq->num_outpins; + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + + for (i = 0; i < num_outpins; i++) { + if (select & BIT(i)) { + if (s->mask[inpin_idx] & BIT(i) || + s->regs[status_reg] & BIT(i)) { + /* + * a. mask bit is not 0 means in ISR mode sources interrupt + * routine are executing. + * b. status bit is not 0 means previous source interrupt + * does not be executed, yet. + * + * save source interrupt to pending bit. + */ + s->pending[inpin_idx] |= BIT(i); + trace_aspeed_intc_pending_irq(name, inpin_idx, + s->pending[inpin_idx]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status bit + */ + s->regs[status_reg] |= BIT(i); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); + } + } + } +} + /* - * GICINT128 to GICINT136 map 1:1 to input and output IRQs 0 to 8. - * The value of input IRQ should be between 0 and the number of inputs. + * GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9. + * GICINT128 to GICINT136 map 1:1 to input IRQs 1 to 9 and output + * IRQs 10 to 18. The value of input IRQ should be between 0 and + * the number of input pins. */ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { @@ -124,12 +172,14 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) const AspeedINTCIRQ *intc_irq; uint32_t select = 0; uint32_t enable; + int num_outpins; int inpin_idx; int i; assert(irq < aic->num_inpins); intc_irq = &aic->irq_table[irq]; + num_outpins = intc_irq->num_outpins; inpin_idx = intc_irq->inpin_idx; trace_aspeed_intc_set_irq(name, inpin_idx, level); enable = s->enable[inpin_idx]; @@ -151,7 +201,11 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) } trace_aspeed_intc_select(name, select); - aspeed_intc_set_irq_handler(s, intc_irq, select); + if (num_outpins > 1) { + aspeed_intc_set_irq_handler_multi_outpins(s, intc_irq, select); + } else { + aspeed_intc_set_irq_handler(s, intc_irq, select); + } } static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, @@ -261,6 +315,66 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, } } +static void aspeed_intc_status_handler_multi_outpins(AspeedINTCState *s, + hwaddr offset, uint64_t data) +{ + const char *name = object_get_typename(OBJECT(s)); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const AspeedINTCIRQ *intc_irq; + uint32_t reg = offset >> 2; + int num_outpins; + int outpin_idx; + int inpin_idx; + int i; + + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); + return; + } + + intc_irq = aspeed_intc_get_irq(aic, reg); + num_outpins = intc_irq->num_outpins; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + assert(inpin_idx < aic->num_inpins); + + /* clear status */ + s->regs[reg] &= ~data; + + /* + * The status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + if (data == 0xffffffff) { + return; + } + + for (i = 0; i < num_outpins; i++) { + /* All source ISR executions are done from a specific bit */ + if (data & BIT(i)) { + trace_aspeed_intc_all_isr_done_bit(name, inpin_idx, i); + if (s->pending[inpin_idx] & BIT(i)) { + /* + * Handle pending source interrupt. + * Notify firmware which source interrupt is pending + * by setting the status bit. + */ + s->regs[reg] |= BIT(i); + s->pending[inpin_idx] &= ~BIT(i); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, + s->regs[reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); + } else { + /* clear irq for the specific bit */ + trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx + i, 0); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 0); + } + } + } +} + static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); @@ -293,6 +407,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT134_EN: case R_GICINT135_EN: case R_GICINT136_EN: + case R_GICINT192_201_EN: aspeed_intc_enable_handler(s, offset, data); break; case R_GICINT128_STATUS: @@ -306,6 +421,9 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT136_STATUS: aspeed_intc_status_handler(s, offset, data); break; + case R_GICINT192_201_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; default: s->regs[reg] = data; break; @@ -415,15 +533,16 @@ static const TypeInfo aspeed_intc_info = { }; static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { - {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS}, - {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS}, - {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS}, - {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS}, - {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS}, - {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS}, - {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS}, - {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS}, - {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS}, + {0, 0, 10, R_GICINT192_201_EN, R_GICINT192_201_STATUS}, + {1, 10, 1, R_GICINT128_EN, R_GICINT128_STATUS}, + {2, 11, 1, R_GICINT129_EN, R_GICINT129_STATUS}, + {3, 12, 1, R_GICINT130_EN, R_GICINT130_STATUS}, + {4, 13, 1, R_GICINT131_EN, R_GICINT131_STATUS}, + {5, 14, 1, R_GICINT132_EN, R_GICINT132_STATUS}, + {6, 15, 1, R_GICINT133_EN, R_GICINT133_STATUS}, + {7, 16, 1, R_GICINT134_EN, R_GICINT134_STATUS}, + {8, 17, 1, R_GICINT135_EN, R_GICINT135_STATUS}, + {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS}, }; static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) @@ -433,10 +552,10 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; - aic->num_inpins = 9; - aic->num_outpins = 9; + aic->num_inpins = 10; + aic->num_outpins = 19; aic->mem_size = 0x4000; - aic->nr_regs = 0x808 >> 2; + aic->nr_regs = 0xB08 >> 2; aic->reg_offset = 0x1000; aic->irq_table = aspeed_2700_intc_irqs; aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs); diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e97eea820b..913197a181 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -92,6 +92,7 @@ aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" aspeed_intc_unmask(const char *s, uint32_t change, uint32_t value) "%s: UnMask: 0x%x: 0x%x" +aspeed_intc_all_isr_done_bit(const char *s, int inpin_idx, int bit) "%s: All source ISR execution are done from specific bit: %d-%d" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index e6c3a27264..85df8c6be9 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -16,8 +16,8 @@ #define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) -#define ASPEED_INTC_MAX_INPINS 9 -#define ASPEED_INTC_MAX_OUTPINS 9 +#define ASPEED_INTC_MAX_INPINS 10 +#define ASPEED_INTC_MAX_OUTPINS 19 typedef struct AspeedINTCIRQ { int inpin_idx; From 38ba38d87df3421ee0b28f9dfaf393456861a8e0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:26 +0800 Subject: [PATCH 2387/2892] hw/intc/aspeed: Add Support for AST2700 INTCIO Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new ast2700 INTCIO class to support AST2700 INTCIO. Added new register definitions for INTCIO, including enable and status registers for IRQs GICINT192 through GICINT197. Created a dedicated IRQ array for INTCIO, supporting six input pins and six output pins, aligning with the newly defined registers. Implemented "aspeed_intcio_read" and "aspeed_intcio_write" to handle INTCIO-specific register access. To GICINT196 | ETH1 |-----------| |--------------------------| -------->|0 | | INTCIO | ETH2 | 4| orgates[0]------>|inpin[0]-------->outpin[0]| -------->|1 5| orgates[1]------>|inpin[1]-------->outpin[1]| ETH3 | 6| orgates[2]------>|inpin[2]-------->outpin[2]| -------->|2 19| orgates[3]------>|inpin[3]-------->outpin[3]| UART0 | 20|-->orgates[4]------>|inpin[4]-------->outpin[4]| -------->|7 21| orgates[5]------>|inpin[5]-------->outpin[5]| UART1 | 22| |--------------------------| -------->|8 23| UART2 | 24| -------->|9 25| UART3 | 26| ---------|10 27| UART5 | 28| -------->|11 29| UART6 | | -------->|12 30| UART7 | 31| -------->|13 | UART8 | OR[0:31] | -------->|14 | UART9 | | -------->|15 | UART10 | | -------->|16 | UART11 | | -------->|17 | UART12 | | -------->|18 | |-----------| Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-18-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 112 ++++++++++++++++++++++++++++++++++ include/hw/intc/aspeed_intc.h | 1 + 2 files changed, 113 insertions(+) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index f2ca9237ea..3fd417084f 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -42,6 +42,26 @@ REG32(GICINT136_STATUS, 0x804) REG32(GICINT192_201_EN, 0xB00) REG32(GICINT192_201_STATUS, 0xB04) +/* + * INTCIO Registers + * + * values below are offset by - 0x100 from datasheet + * because its memory region is start at 0x100 + * + */ +REG32(GICINT192_EN, 0x00) +REG32(GICINT192_STATUS, 0x04) +REG32(GICINT193_EN, 0x10) +REG32(GICINT193_STATUS, 0x14) +REG32(GICINT194_EN, 0x20) +REG32(GICINT194_STATUS, 0x24) +REG32(GICINT195_EN, 0x30) +REG32(GICINT195_STATUS, 0x34) +REG32(GICINT196_EN, 0x40) +REG32(GICINT196_STATUS, 0x44) +REG32(GICINT197_EN, 0x50) +REG32(GICINT197_STATUS, 0x54) + static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, uint32_t reg) { @@ -432,6 +452,55 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, return; } +static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, + unsigned int size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + uint32_t value = 0; + + value = s->regs[reg]; + trace_aspeed_intc_read(name, offset, size, value); + + return value; +} + +static void aspeed_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_GICINT192_EN: + case R_GICINT193_EN: + case R_GICINT194_EN: + case R_GICINT195_EN: + case R_GICINT196_EN: + case R_GICINT197_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_GICINT192_STATUS: + case R_GICINT193_STATUS: + case R_GICINT194_STATUS: + case R_GICINT195_STATUS: + case R_GICINT196_STATUS: + case R_GICINT197_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } + + return; +} + + static const MemoryRegionOps aspeed_intc_ops = { .read = aspeed_intc_read, .write = aspeed_intc_write, @@ -442,6 +511,16 @@ static const MemoryRegionOps aspeed_intc_ops = { } }; +static const MemoryRegionOps aspeed_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + static void aspeed_intc_instance_init(Object *obj) { AspeedINTCState *s = ASPEED_INTC(obj); @@ -567,10 +646,43 @@ static const TypeInfo aspeed_2700_intc_info = { .class_init = aspeed_2700_intc_class_init, }; +static AspeedINTCIRQ aspeed_2700_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_GICINT192_EN, R_GICINT192_STATUS}, + {1, 1, 1, R_GICINT193_EN, R_GICINT193_STATUS}, + {2, 2, 1, R_GICINT194_EN, R_GICINT194_STATUS}, + {3, 3, 1, R_GICINT195_EN, R_GICINT195_STATUS}, + {4, 4, 1, R_GICINT196_EN, R_GICINT196_STATUS}, + {5, 5, 1, R_GICINT197_EN, R_GICINT197_STATUS}, +}; + +static void aspeed_2700_intcio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x58 >> 2; + aic->reg_offset = 0x100; + aic->reg_ops = &aspeed_intcio_ops; + aic->irq_table = aspeed_2700_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intcio_irqs); +} + +static const TypeInfo aspeed_2700_intcio_info = { + .name = TYPE_ASPEED_2700_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700_intcio_class_init, +}; + static void aspeed_intc_register_types(void) { type_register_static(&aspeed_intc_info); type_register_static(&aspeed_2700_intc_info); + type_register_static(&aspeed_2700_intcio_info); } type_init(aspeed_intc_register_types); diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 85df8c6be9..3727ba24be 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -14,6 +14,7 @@ #define TYPE_ASPEED_INTC "aspeed.intc" #define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" +#define TYPE_ASPEED_2700_INTCIO TYPE_ASPEED_INTC "io-ast2700" OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) #define ASPEED_INTC_MAX_INPINS 10 From d3b38cbbed845a650819af3ec59d5e463b3fe47a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:27 +0800 Subject: [PATCH 2388/2892] hw/misc/aspeed_scu: Add Support for AST2700/AST2750 A1 Silicon Revisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added new definitions for AST2700_A1_SILICON_REV and AST2750_A1_SILICON_REV to identify the A1 silicon revisions. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-19-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 2 ++ include/hw/misc/aspeed_scu.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 0581c744f1..76cfd91671 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -560,6 +560,8 @@ static uint32_t aspeed_silicon_revs[] = { AST2700_A0_SILICON_REV, AST2720_A0_SILICON_REV, AST2750_A0_SILICON_REV, + AST2700_A1_SILICON_REV, + AST2750_A1_SILICON_REV, }; bool is_supported_silicon_rev(uint32_t silicon_rev) diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 356be95e45..684b48b722 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -54,6 +54,8 @@ struct AspeedSCUState { #define AST2700_A0_SILICON_REV 0x06000103U #define AST2720_A0_SILICON_REV 0x06000203U #define AST2750_A0_SILICON_REV 0x06000003U +#define AST2700_A1_SILICON_REV 0x06010103U +#define AST2750_A1_SILICON_REV 0x06010003U #define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) From d2c8093567b3681e4439702f129e89156f59afb5 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:28 +0800 Subject: [PATCH 2389/2892] hw/arm/aspeed_ast27x0.c Support AST2700 A1 GIC Interrupt Mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, these IRQ tables support from GIC 128 - 136 for AST2700 A0. These IRQ tables can be reused for AST2700 A1 from GIC 192 - 197. Updates the interrupt mapping to include support for AST2700 A1 by extending the existing mappings to the new GIC range. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-20-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 77 ++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 3cb95210a0..9e5f4a2084 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -120,21 +120,27 @@ static const int aspeed_soc_ast2700a0_irqmap[] = { }; /* GICINT 128 */ -static const int aspeed_soc_ast2700_gic128_intcmap[] = { +/* GICINT 192 */ +static const int ast2700_gic128_gic192_intcmap[] = { [ASPEED_DEV_LPC] = 0, [ASPEED_DEV_IBT] = 2, [ASPEED_DEV_KCS] = 4, }; +/* GICINT 129 */ +/* GICINT 193 */ + /* GICINT 130 */ -static const int aspeed_soc_ast2700_gic130_intcmap[] = { +/* GICINT 194 */ +static const int ast2700_gic130_gic194_intcmap[] = { [ASPEED_DEV_I2C] = 0, [ASPEED_DEV_ADC] = 16, [ASPEED_DEV_GPIO] = 18, }; /* GICINT 131 */ -static const int aspeed_soc_ast2700_gic131_intcmap[] = { +/* GICINT 195 */ +static const int ast2700_gic131_gic195_intcmap[] = { [ASPEED_DEV_I3C] = 0, [ASPEED_DEV_WDT] = 16, [ASPEED_DEV_FMC] = 25, @@ -142,7 +148,8 @@ static const int aspeed_soc_ast2700_gic131_intcmap[] = { }; /* GICINT 132 */ -static const int aspeed_soc_ast2700_gic132_intcmap[] = { +/* GICINT 196 */ +static const int ast2700_gic132_gic196_intcmap[] = { [ASPEED_DEV_ETH1] = 0, [ASPEED_DEV_ETH2] = 1, [ASPEED_DEV_ETH3] = 2, @@ -161,24 +168,26 @@ static const int aspeed_soc_ast2700_gic132_intcmap[] = { }; /* GICINT 133 */ -static const int aspeed_soc_ast2700_gic133_intcmap[] = { +/* GICINT 197 */ +static const int ast2700_gic133_gic197_intcmap[] = { [ASPEED_DEV_SDHCI] = 1, [ASPEED_DEV_PECI] = 4, }; /* GICINT 128 ~ 136 */ +/* GICINT 192 ~ 201 */ struct gic_intc_irq_info { int irq; const int *ptr; }; -static const struct gic_intc_irq_info aspeed_soc_ast2700_gic_intcmap[] = { - {128, aspeed_soc_ast2700_gic128_intcmap}, +static const struct gic_intc_irq_info ast2700_gic_intcmap[] = { + {128, ast2700_gic128_gic192_intcmap}, {129, NULL}, - {130, aspeed_soc_ast2700_gic130_intcmap}, - {131, aspeed_soc_ast2700_gic131_intcmap}, - {132, aspeed_soc_ast2700_gic132_intcmap}, - {133, aspeed_soc_ast2700_gic133_intcmap}, + {130, ast2700_gic130_gic194_intcmap}, + {131, ast2700_gic131_gic195_intcmap}, + {132, ast2700_gic132_gic196_intcmap}, + {133, ast2700_gic133_gic197_intcmap}, {134, NULL}, {135, NULL}, {136, NULL}, @@ -190,11 +199,11 @@ static qemu_irq aspeed_soc_ast2700_get_irq(AspeedSoCState *s, int dev) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int i; - for (i = 0; i < ARRAY_SIZE(aspeed_soc_ast2700_gic_intcmap); i++) { - if (sc->irqmap[dev] == aspeed_soc_ast2700_gic_intcmap[i].irq) { - assert(aspeed_soc_ast2700_gic_intcmap[i].ptr); + for (i = 0; i < ARRAY_SIZE(ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_gic_intcmap[i].irq) { + assert(ast2700_gic_intcmap[i].ptr); return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), - aspeed_soc_ast2700_gic_intcmap[i].ptr[dev]); + ast2700_gic_intcmap[i].ptr[dev]); } } @@ -208,16 +217,17 @@ static qemu_irq aspeed_soc_ast2700_get_irq_index(AspeedSoCState *s, int dev, AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int i; - for (i = 0; i < ARRAY_SIZE(aspeed_soc_ast2700_gic_intcmap); i++) { - if (sc->irqmap[dev] == aspeed_soc_ast2700_gic_intcmap[i].irq) { - assert(aspeed_soc_ast2700_gic_intcmap[i].ptr); + for (i = 0; i < ARRAY_SIZE(ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_gic_intcmap[i].irq) { + assert(ast2700_gic_intcmap[i].ptr); return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), - aspeed_soc_ast2700_gic_intcmap[i].ptr[dev] + index); + ast2700_gic_intcmap[i].ptr[dev] + index); } } /* - * Invalid orgate index, device irq should be 128 to 136. + * Invalid OR gate index, device IRQ should be between 128 to 136 + * and 192 to 201. */ g_assert_not_reached(); } @@ -534,17 +544,18 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc), 0, sc->memmap[ASPEED_DEV_INTC]); - /* source orgates -> INTC */ + /* irq sources -> orgates -> INTC */ for (i = 0; i < ic->num_inpins; i++) { qdev_connect_gpio_out(DEVICE(&a->intc.orgates[i]), 0, - qdev_get_gpio_in(DEVICE(&a->intc), i)); + qdev_get_gpio_in(DEVICE(&a->intc), i)); } + /* INTC -> GIC192 - GIC201 */ /* INTC -> GIC128 - GIC136 */ for (i = 0; i < ic->num_outpins; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc), i, qdev_get_gpio_in(DEVICE(&a->gic), - aspeed_soc_ast2700_gic_intcmap[i].irq)); + ast2700_gic_intcmap[i].irq)); } /* SRAM */ @@ -695,10 +706,22 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { /* * The AST2700 I2C controller has one source INTC per bus. - * I2C buses interrupt are connected to GICINT130_INTC - * from bit 0 to bit 15. - * I2C bus 0 is connected to GICINT130_INTC at bit 0. - * I2C bus 15 is connected to GICINT130_INTC at bit 15. + * + * For AST2700 A0: + * I2C bus interrupts are connected to the OR gate from bit 0 to bit + * 15, and the OR gate output pin is connected to the input pin of + * GICINT130 of INTC (CPU Die). Then, the output pin is connected to + * the GIC. + * + * For AST2700 A1: + * I2C bus interrupts are connected to the OR gate from bit 0 to bit + * 15, and the OR gate output pin is connected to the input pin of + * GICINT194 of INTCIO (IO Die). Then, the output pin is connected + * to the INTC (CPU Die) input pin, and its output pin is connected + * to the GIC. + * + * I2C bus 0 is connected to the OR gate at bit 0. + * I2C bus 15 is connected to the OR gate at bit 15. */ irq = aspeed_soc_ast2700_get_irq_index(s, ASPEED_DEV_I2C, i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); From cd99eda62a5129305d186468f8056618d0b3bd87 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:29 +0800 Subject: [PATCH 2390/2892] hw/arm/aspeed_ast27x0: Define an Array of AspeedINTCState with Two Instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated Aspeed27x0SoCState to include an intc[2] array instead of a single AspeedINTCState instance. Modified aspeed_soc_ast2700_get_irq and aspeed_soc_ast2700_get_irq_index to correctly reference the corresponding interrupt controller instance and OR gate index. Currently, only GIC 192 to 201 are supported, and their source interrupts are from INTCIO and connected to INTC at input pin 0 and output pins 0 to 9 for GIC 192-201. To support both AST2700 A1 and A0, INTC input pins 1 to 9 and output pins 10 to 18 remain to support GIC 128-136, which source interrupts from INTC. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-21-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 58 +++++++++++++++++++++++++------------ include/hw/arm/aspeed_soc.h | 2 +- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 9e5f4a2084..6cffa5b9a0 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -178,32 +178,48 @@ static const int ast2700_gic133_gic197_intcmap[] = { /* GICINT 192 ~ 201 */ struct gic_intc_irq_info { int irq; + int intc_idx; + int orgate_idx; const int *ptr; }; static const struct gic_intc_irq_info ast2700_gic_intcmap[] = { - {128, ast2700_gic128_gic192_intcmap}, - {129, NULL}, - {130, ast2700_gic130_gic194_intcmap}, - {131, ast2700_gic131_gic195_intcmap}, - {132, ast2700_gic132_gic196_intcmap}, - {133, ast2700_gic133_gic197_intcmap}, - {134, NULL}, - {135, NULL}, - {136, NULL}, + {192, 1, 0, ast2700_gic128_gic192_intcmap}, + {193, 1, 1, NULL}, + {194, 1, 2, ast2700_gic130_gic194_intcmap}, + {195, 1, 3, ast2700_gic131_gic195_intcmap}, + {196, 1, 4, ast2700_gic132_gic196_intcmap}, + {197, 1, 5, ast2700_gic133_gic197_intcmap}, + {198, 1, 6, NULL}, + {199, 1, 7, NULL}, + {200, 1, 8, NULL}, + {201, 1, 9, NULL}, + {128, 0, 1, ast2700_gic128_gic192_intcmap}, + {129, 0, 2, NULL}, + {130, 0, 3, ast2700_gic130_gic194_intcmap}, + {131, 0, 4, ast2700_gic131_gic195_intcmap}, + {132, 0, 5, ast2700_gic132_gic196_intcmap}, + {133, 0, 6, ast2700_gic133_gic197_intcmap}, + {134, 0, 7, NULL}, + {135, 0, 8, NULL}, + {136, 0, 9, NULL}, }; static qemu_irq aspeed_soc_ast2700_get_irq(AspeedSoCState *s, int dev) { Aspeed27x0SoCState *a = ASPEED27X0_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int or_idx; + int idx; int i; for (i = 0; i < ARRAY_SIZE(ast2700_gic_intcmap); i++) { if (sc->irqmap[dev] == ast2700_gic_intcmap[i].irq) { assert(ast2700_gic_intcmap[i].ptr); - return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), - ast2700_gic_intcmap[i].ptr[dev]); + or_idx = ast2700_gic_intcmap[i].orgate_idx; + idx = ast2700_gic_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + ast2700_gic_intcmap[i].ptr[dev]); } } @@ -215,12 +231,16 @@ static qemu_irq aspeed_soc_ast2700_get_irq_index(AspeedSoCState *s, int dev, { Aspeed27x0SoCState *a = ASPEED27X0_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int or_idx; + int idx; int i; for (i = 0; i < ARRAY_SIZE(ast2700_gic_intcmap); i++) { if (sc->irqmap[dev] == ast2700_gic_intcmap[i].irq) { assert(ast2700_gic_intcmap[i].ptr); - return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), + or_idx = ast2700_gic_intcmap[i].orgate_idx; + idx = ast2700_gic_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), ast2700_gic_intcmap[i].ptr[dev] + index); } } @@ -390,7 +410,7 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "sli", &s->sli, TYPE_ASPEED_2700_SLI); object_initialize_child(obj, "sliio", &s->sliio, TYPE_ASPEED_2700_SLIIO); - object_initialize_child(obj, "intc", &a->intc, TYPE_ASPEED_2700_INTC); + object_initialize_child(obj, "intc", &a->intc[0], TYPE_ASPEED_2700_INTC); snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); object_initialize_child(obj, "adc", &s->adc, typename); @@ -506,7 +526,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc); + AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc[0]); g_autofree char *sram_name = NULL; qemu_irq irq; @@ -537,23 +557,23 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) } /* INTC */ - if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, sc->memmap[ASPEED_DEV_INTC]); /* irq sources -> orgates -> INTC */ for (i = 0; i < ic->num_inpins; i++) { - qdev_connect_gpio_out(DEVICE(&a->intc.orgates[i]), 0, - qdev_get_gpio_in(DEVICE(&a->intc), i)); + qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[0]), i)); } /* INTC -> GIC192 - GIC201 */ /* INTC -> GIC128 - GIC136 */ for (i = 0; i < ic->num_outpins; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc), i, + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[0]), i, qdev_get_gpio_in(DEVICE(&a->gic), ast2700_gic_intcmap[i].irq)); } diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 4a8881ca8b..066d2fcc20 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -128,7 +128,7 @@ struct Aspeed27x0SoCState { AspeedSoCState parent; ARMCPU cpu[ASPEED_CPUS_NUM]; - AspeedINTCState intc; + AspeedINTCState intc[2]; GICv3State gic; MemoryRegion dram_empty; }; From 8107448de709a64362c56687764fbd41587c9de9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:30 +0800 Subject: [PATCH 2391/2892] hw/arm/aspeed_ast27x0: Support two levels of INTC controllers for AST2700 A1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The design of INTC controllers has significantly changed in AST2700 A1. There are a total of 480 interrupt sources in AST2700 A1. For interrupt numbers from 0 to 127, they can route directly to PSP, SSP, and TSP. Due to the limitation of interrupt numbers of processors, the interrupts are merged every 32 sources for interrupt numbers greater than 127. There are two levels of interrupt controllers, INTC(CPUD Die) and INTCIO (IO Die). The interrupt sources of INTC are the interrupt numbers from INTC_0 to INTC_127 and interrupts from INTCIO. The interrupt sources of INTCIO are the interrupt numbers greater than INTC_127. INTC_IO controls the interrupts INTC_128 to INTC_319 only. Currently, only GIC 192 to 201 are supported, and their source interrupts are from INTCIO and connected to INTC at input pin 0 and output pins 0 to 9 for GIC 192-201. The design of the orgates for GICINT 196 is as follows: It has interrupt sources ranging from 0 to 31, with its output pin connected to INTCIO "T0 GICINT_196". The output pin is then connected to INTC "GIC_192_201" at bit 4, and its bit 4 output should be connected to GIC 196. The design of INTC GIC_192_201 have 10 output pins, mapped as following: Bit 0 -> GIC 192 Bit 1 -> GIC 193 Bit 2 -> GIC 194 Bit 3 -> GIC 195 Bit 4 -> GIC 196 To support both AST2700 A1 and A0, INTC input pins 1 to 9 and output pins 10 to 18 remain to support GIC 128-136, which source interrupts from INTC. These will be removed if we decide not to support AST2700 A0 in the future. |-------------------------------------------------------------------------------------------------------| | AST2700 A1 Design | | To GICINT196 | | | | ETH1 |-----------| |--------------------------| |--------------| | | -------->|0 | | INTCIO | | orgates[0] | | | ETH2 | 4| orgates[0]------>|inpin[0]-------->outpin[0]|------->| 0 | | | -------->|1 5| orgates[1]------>|inpin[1]-------->outpin[1]|------->| 1 | | | ETH3 | 6| orgates[2]------>|inpin[2]-------->outpin[2]|------->| 2 | | | -------->|2 19| orgates[3]------>|inpin[3]-------->outpin[3]|------->| 3 OR[0:9] |-----| | | UART0 | 20|-->orgates[4]------>|inpin[4]-------->outpin[4]|------->| 4 | | | | -------->|7 21| orgates[5]------>|inpin[5]-------->outpin[5]|------->| 5 | | | | UART1 | 22| orgates[6]------>|inpin[6]-------->outpin[6]|------->| 6 | | | | -------->|8 23| orgates[7]------>|inpin[7]-------->outpin[7]|------->| 7 | | | | UART2 | 24| orgates[8]------>|inpin[8]-------->outpin[8]|------->| 8 | | | | -------->|9 25| orgates[9]------>|inpin[9]-------->outpin[9]|------->| 9 | | | | UART3 | 26| |--------------------------| |--------------| | | | ---------|10 27| | | | UART5 | 28| | | | -------->|11 29| | | | UART6 | | | | | -------->|12 30| |-----------------------------------------------------------------------| | | UART7 | 31| | | | -------->|13 | | | | UART8 | OR[0:31] | | |------------------------------| |----------| | | -------->|14 | | | INTC | | GIC | | | UART9 | | | |inpin[0:0]--------->outpin[0] |---------->|192 | | | -------->|15 | | |inpin[0:1]--------->outpin[1] |---------->|193 | | | UART10 | | | |inpin[0:2]--------->outpin[2] |---------->|194 | | | -------->|16 | | |inpin[0:3]--------->outpin[3] |---------->|195 | | | UART11 | | |--------------> |inpin[0:4]--------->outpin[4] |---------->|196 | | | -------->|17 | |inpin[0:5]--------->outpin[5] |---------->|197 | | | UART12 | | |inpin[0:6]--------->outpin[6] |---------->|198 | | | -------->|18 | |inpin[0:7]--------->outpin[7] |---------->|199 | | | |-----------| |inpin[0:8]--------->outpin[8] |---------->|200 | | | |inpin[0:9]--------->outpin[9] |---------->|201 | | |-------------------------------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------------------------| | ETH1 |-----------| orgates[1]------->|inpin[1]----------->outpin[10]|---------->|128 | | | -------->|0 | orgates[2]------->|inpin[2]----------->outpin[11]|---------->|129 | | | ETH2 | 4| orgates[3]------->|inpin[3]----------->outpin[12]|---------->|130 | | | -------->|1 5| orgates[4]------->|inpin[4]----------->outpin[13]|---------->|131 | | | ETH3 | 6|---->orgates[5]------->|inpin[5]----------->outpin[14]|---------->|132 | | | -------->|2 19| orgates[6]------->|inpin[6]----------->outpin[15]|---------->|133 | | | UART0 | 20| orgates[7]------->|inpin[7]----------->outpin[16]|---------->|134 | | | -------->|7 21| orgates[8]------->|inpin[8]----------->outpin[17]|---------->|135 | | | UART1 | 22| orgates[9]------->|inpin[9]----------->outpin[18]|---------->|136 | | | -------->|8 23| |------------------------------| |----------| | | UART2 | 24| | | -------->|9 25| AST2700 A0 Design | | UART3 | 26| | | -------->|10 27| | | UART5 | 28| | | -------->|11 29| GICINT132 | | UART6 | | | | -------->|12 30| | | UART7 | 31| | | -------->|13 | | | UART8 | OR[0:31] | | | -------->|14 | | | UART9 | | | | -------->|15 | | | UART10 | | | | -------->|16 | | | UART11 | | | | -------->|17 | | | UART12 | | | | -------->|18 | | | |-----------| | | | |-------------------------------------------------------------------------------------------------------| Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-22-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 24 ++++++++++++++++++++++++ include/hw/arm/aspeed_soc.h | 1 + 2 files changed, 25 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 6cffa5b9a0..8ed57d23ef 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -57,6 +57,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_ETH3] = 0x14070000, [ASPEED_DEV_EMMC] = 0x12090000, [ASPEED_DEV_INTC] = 0x12100000, + [ASPEED_DEV_INTCIO] = 0x14C18000, [ASPEED_DEV_SLI] = 0x12C17000, [ASPEED_DEV_SLIIO] = 0x14C1E000, [ASPEED_GIC_DIST] = 0x12200000, @@ -411,6 +412,8 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "sli", &s->sli, TYPE_ASPEED_2700_SLI); object_initialize_child(obj, "sliio", &s->sliio, TYPE_ASPEED_2700_SLIIO); object_initialize_child(obj, "intc", &a->intc[0], TYPE_ASPEED_2700_INTC); + object_initialize_child(obj, "intcio", &a->intc[1], + TYPE_ASPEED_2700_INTCIO); snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); object_initialize_child(obj, "adc", &s->adc, typename); @@ -527,6 +530,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc[0]); + AspeedINTCClass *icio = ASPEED_INTC_GET_CLASS(&a->intc[1]); g_autofree char *sram_name = NULL; qemu_irq irq; @@ -564,6 +568,14 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, sc->memmap[ASPEED_DEV_INTC]); + /* INTCIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[1]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + sc->memmap[ASPEED_DEV_INTCIO]); + /* irq sources -> orgates -> INTC */ for (i = 0; i < ic->num_inpins; i++) { qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, @@ -578,6 +590,18 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) ast2700_gic_intcmap[i].irq)); } + /* irq source -> orgates -> INTCIO */ + for (i = 0; i < icio->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[1].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[1]), i)); + } + + /* INTCIO -> INTC */ + for (i = 0; i < icio->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, + qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); + } + /* SRAM */ sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 066d2fcc20..f899356ed9 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -194,6 +194,7 @@ enum { ASPEED_DEV_EHCI2, ASPEED_DEV_VIC, ASPEED_DEV_INTC, + ASPEED_DEV_INTCIO, ASPEED_DEV_SDMC, ASPEED_DEV_SCU, ASPEED_DEV_ADC, From 6de4aa8dc54451e5902658648fd3d268284c45e9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:31 +0800 Subject: [PATCH 2392/2892] hw/arm/aspeed_ast27x0: Add SoC Support for AST2700 A1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The memory map for AST2700 A1 remains compatible with AST2700 A0. However, the IRQ mapping has been updated for AST2700 A1, with GIC interrupts now ranging from 192 to 201. Add a new IRQ map table for AST2700 A1. Add "aspeed_soc_ast2700a1_class_init" to initialize the AST2700 A1 SoC. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-23-jamin_lin@aspeedtech.com [ clg: Removed sc->name ] Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 79 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 8ed57d23ef..682ab9bf8a 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -120,6 +120,52 @@ static const int aspeed_soc_ast2700a0_irqmap[] = { [ASPEED_DEV_SDHCI] = 133, }; +static const int aspeed_soc_ast2700a1_irqmap[] = { + [ASPEED_DEV_SDMC] = 0, + [ASPEED_DEV_HACE] = 4, + [ASPEED_DEV_XDMA] = 5, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_RTC] = 13, + [ASPEED_DEV_EMMC] = 15, + [ASPEED_DEV_TIMER1] = 16, + [ASPEED_DEV_TIMER2] = 17, + [ASPEED_DEV_TIMER3] = 18, + [ASPEED_DEV_TIMER4] = 19, + [ASPEED_DEV_TIMER5] = 20, + [ASPEED_DEV_TIMER6] = 21, + [ASPEED_DEV_TIMER7] = 22, + [ASPEED_DEV_TIMER8] = 23, + [ASPEED_DEV_DP] = 28, + [ASPEED_DEV_LPC] = 192, + [ASPEED_DEV_IBT] = 192, + [ASPEED_DEV_KCS] = 192, + [ASPEED_DEV_I2C] = 194, + [ASPEED_DEV_ADC] = 194, + [ASPEED_DEV_GPIO] = 194, + [ASPEED_DEV_FMC] = 195, + [ASPEED_DEV_WDT] = 195, + [ASPEED_DEV_PWM] = 195, + [ASPEED_DEV_I3C] = 195, + [ASPEED_DEV_UART0] = 196, + [ASPEED_DEV_UART1] = 196, + [ASPEED_DEV_UART2] = 196, + [ASPEED_DEV_UART3] = 196, + [ASPEED_DEV_UART5] = 196, + [ASPEED_DEV_UART6] = 196, + [ASPEED_DEV_UART7] = 196, + [ASPEED_DEV_UART8] = 196, + [ASPEED_DEV_UART9] = 196, + [ASPEED_DEV_UART10] = 196, + [ASPEED_DEV_UART11] = 196, + [ASPEED_DEV_UART12] = 196, + [ASPEED_DEV_ETH1] = 196, + [ASPEED_DEV_ETH2] = 196, + [ASPEED_DEV_ETH3] = 196, + [ASPEED_DEV_PECI] = 197, + [ASPEED_DEV_SDHCI] = 197, +}; + /* GICINT 128 */ /* GICINT 192 */ static const int ast2700_gic128_gic192_intcmap[] = { @@ -864,6 +910,33 @@ static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, void *data) sc->get_irq = aspeed_soc_ast2700_get_irq; } +static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, void *data) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a35"), + NULL + }; + DeviceClass *dc = DEVICE_CLASS(oc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; + dc->realize = aspeed_soc_ast2700_realize; + + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A1_SILICON_REV; + sc->sram_size = 0x20000; + sc->spis_num = 3; + sc->wdts_num = 8; + sc->macs_num = 3; + sc->uarts_num = 13; + sc->num_cpus = 4; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast2700a1_irqmap; + sc->memmap = aspeed_soc_ast2700_memmap; + sc->get_irq = aspeed_soc_ast2700_get_irq; +} + static const TypeInfo aspeed_soc_ast27x0_types[] = { { .name = TYPE_ASPEED27X0_SOC, @@ -876,6 +949,12 @@ static const TypeInfo aspeed_soc_ast27x0_types[] = { .instance_init = aspeed_soc_ast2700_init, .class_init = aspeed_soc_ast2700a0_class_init, }, + { + .name = "ast2700-a1", + .parent = TYPE_ASPEED27X0_SOC, + .instance_init = aspeed_soc_ast2700_init, + .class_init = aspeed_soc_ast2700a1_class_init, + }, }; DEFINE_TYPES(aspeed_soc_ast27x0_types) From 498c519eb7761b09adf2d2e863cf4a70b186005f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:32 +0800 Subject: [PATCH 2393/2892] hw/arm/aspeed: Add Machine Support for AST2700 A1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce "aspeed_machine_ast2700a1_evb_class_init" to initialize the AST2700 A1 EVB. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-24-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 18f7c450da..82f42582fa 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1693,6 +1693,26 @@ static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); } + +static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2700 A1 EVB (Cortex-A35)"; + amc->soc_name = "ast2700-a1"; + amc->hw_strap1 = AST2700_EVB_HW_STRAP1; + amc->hw_strap2 = AST2700_EVB_HW_STRAP2; + amc->fmc_model = "w25q01jvq"; + amc->spi_model = "w25q512jv"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; + amc->uart_default = ASPEED_DEV_UART12; + amc->i2c_init = ast2700_evb_i2c_init; + mc->auto_create_sdcard = true; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); +} #endif static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, @@ -1821,6 +1841,10 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("ast2700a0-evb"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_ast2700a0_evb_class_init, + }, { + .name = MACHINE_TYPE_NAME("ast2700a1-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2700a1_evb_class_init, #endif }, { .name = TYPE_ASPEED_MACHINE, From ecc1a4e966b5d4ce1732ee223eaf40ef37e9a30b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:33 +0800 Subject: [PATCH 2394/2892] hw/arm/aspeed_ast27x0: Sort the memmap table by mapping address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve readability, sort the memmap table by mapping address Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-25-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 54 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 682ab9bf8a..dce7255a2c 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -24,16 +24,40 @@ #include "qemu/log.h" static const hwaddr aspeed_soc_ast2700_memmap[] = { - [ASPEED_DEV_SPI_BOOT] = 0x100000000, [ASPEED_DEV_SRAM] = 0x10000000, + [ASPEED_DEV_HACE] = 0x12070000, + [ASPEED_DEV_EMMC] = 0x12090000, + [ASPEED_DEV_INTC] = 0x12100000, + [ASPEED_GIC_DIST] = 0x12200000, + [ASPEED_GIC_REDIST] = 0x12280000, [ASPEED_DEV_SDMC] = 0x12C00000, [ASPEED_DEV_SCU] = 0x12C02000, + [ASPEED_DEV_RTC] = 0x12C0F000, + [ASPEED_DEV_TIMER1] = 0x12C10000, + [ASPEED_DEV_SLI] = 0x12C17000, + [ASPEED_DEV_UART4] = 0X12C1A000, + [ASPEED_DEV_FMC] = 0x14000000, + [ASPEED_DEV_SPI0] = 0x14010000, + [ASPEED_DEV_SPI1] = 0x14020000, + [ASPEED_DEV_SPI2] = 0x14030000, + [ASPEED_DEV_MII1] = 0x14040000, + [ASPEED_DEV_MII2] = 0x14040008, + [ASPEED_DEV_MII3] = 0x14040010, + [ASPEED_DEV_ETH1] = 0x14050000, + [ASPEED_DEV_ETH2] = 0x14060000, + [ASPEED_DEV_ETH3] = 0x14070000, + [ASPEED_DEV_SDHCI] = 0x14080000, + [ASPEED_DEV_ADC] = 0x14C00000, [ASPEED_DEV_SCUIO] = 0x14C02000, + [ASPEED_DEV_GPIO] = 0x14C0B000, + [ASPEED_DEV_I2C] = 0x14C0F000, + [ASPEED_DEV_INTCIO] = 0x14C18000, + [ASPEED_DEV_SLIIO] = 0x14C1E000, + [ASPEED_DEV_VUART] = 0X14C30000, [ASPEED_DEV_UART0] = 0X14C33000, [ASPEED_DEV_UART1] = 0X14C33100, [ASPEED_DEV_UART2] = 0X14C33200, [ASPEED_DEV_UART3] = 0X14C33300, - [ASPEED_DEV_UART4] = 0X12C1A000, [ASPEED_DEV_UART5] = 0X14C33400, [ASPEED_DEV_UART6] = 0X14C33500, [ASPEED_DEV_UART7] = 0X14C33600, @@ -43,32 +67,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_UART11] = 0X14C33A00, [ASPEED_DEV_UART12] = 0X14C33B00, [ASPEED_DEV_WDT] = 0x14C37000, - [ASPEED_DEV_VUART] = 0X14C30000, - [ASPEED_DEV_FMC] = 0x14000000, - [ASPEED_DEV_SPI0] = 0x14010000, - [ASPEED_DEV_SPI1] = 0x14020000, - [ASPEED_DEV_SPI2] = 0x14030000, + [ASPEED_DEV_SPI_BOOT] = 0x100000000, [ASPEED_DEV_SDRAM] = 0x400000000, - [ASPEED_DEV_MII1] = 0x14040000, - [ASPEED_DEV_MII2] = 0x14040008, - [ASPEED_DEV_MII3] = 0x14040010, - [ASPEED_DEV_ETH1] = 0x14050000, - [ASPEED_DEV_ETH2] = 0x14060000, - [ASPEED_DEV_ETH3] = 0x14070000, - [ASPEED_DEV_EMMC] = 0x12090000, - [ASPEED_DEV_INTC] = 0x12100000, - [ASPEED_DEV_INTCIO] = 0x14C18000, - [ASPEED_DEV_SLI] = 0x12C17000, - [ASPEED_DEV_SLIIO] = 0x14C1E000, - [ASPEED_GIC_DIST] = 0x12200000, - [ASPEED_GIC_REDIST] = 0x12280000, - [ASPEED_DEV_ADC] = 0x14C00000, - [ASPEED_DEV_I2C] = 0x14C0F000, - [ASPEED_DEV_GPIO] = 0x14C0B000, - [ASPEED_DEV_RTC] = 0x12C0F000, - [ASPEED_DEV_SDHCI] = 0x14080000, - [ASPEED_DEV_TIMER1] = 0x12C10000, - [ASPEED_DEV_HACE] = 0x12070000, }; #define AST2700_MAX_IRQ 256 From 827eecb0e8d3cda71c5529909345acda266b2e6d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:34 +0800 Subject: [PATCH 2395/2892] tests/functional/aspeed: Introduce start_ast2700_test API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a new method "start_ast2700_test" to the "AST2x00MachineSDK" class and this method centralizes the logic for starting the AST2700 test, making it reusable for different test cases. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-26-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 9595498ace..e1ad7fd470 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -31,33 +31,29 @@ class AST2x00MachineSDK(QemuSystemTest): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.03/ast2700-default-obmc.tar.gz', '91225f50d255e2905ba8d8e0c80b71b9d157c3609770c7a740cd786370d85a77') - def test_aarch64_ast2700_evb_sdk_v09_03(self): - self.set_machine('ast2700-evb') - - self.archive_extract(self.ASSET_SDK_V903_AST2700) - + def start_ast2700_test(self, name): num_cpu = 4 - uboot_size = os.path.getsize(self.scratch_file('ast2700-default', + uboot_size = os.path.getsize(self.scratch_file(name, 'u-boot-nodtb.bin')) uboot_dtb_load_addr = hex(0x400000000 + uboot_size) load_images_list = [ { 'addr': '0x400000000', - 'file': self.scratch_file('ast2700-default', + 'file': self.scratch_file(name, 'u-boot-nodtb.bin') }, { 'addr': str(uboot_dtb_load_addr), - 'file': self.scratch_file('ast2700-default', 'u-boot.dtb') + 'file': self.scratch_file(name, 'u-boot.dtb') }, { 'addr': '0x430000000', - 'file': self.scratch_file('ast2700-default', 'bl31.bin') + 'file': self.scratch_file(name, 'bl31.bin') }, { 'addr': '0x430080000', - 'file': self.scratch_file('ast2700-default', 'optee', + 'file': self.scratch_file(name, 'optee', 'tee-raw.bin') } ] @@ -76,13 +72,12 @@ class AST2x00MachineSDK(QemuSystemTest): self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') self.do_test_aarch64_aspeed_sdk_start( - self.scratch_file('ast2700-default', 'image-bmc')) + self.scratch_file(name, 'image-bmc')) - wait_for_console_pattern(self, 'ast2700-default login:') + wait_for_console_pattern(self, f'{name} login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, - '0penBmc', 'root@ast2700-default:~#') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', @@ -94,6 +89,12 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon20/temp1_input', '18000') + def test_aarch64_ast2700_evb_sdk_v09_03(self): + self.set_machine('ast2700-evb') + + self.archive_extract(self.ASSET_SDK_V903_AST2700) + self.start_ast2700_test('ast2700-default') + if __name__ == '__main__': QemuSystemTest.main() From 8241d6f8d5b7a423dd4289b8140934d96819d019 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:35 +0800 Subject: [PATCH 2396/2892] tests/functional/aspeed: Update temperature hwmon path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified the temperature hwmon path to use a wildcard to handle different SDK versions: "cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input". Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-27-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index e1ad7fd470..07b0c7c1fd 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -83,11 +83,11 @@ class AST2x00MachineSDK(QemuSystemTest): 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d'); exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon20/temp1_input', '0') + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', property='temperature', value=18000) exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon20/temp1_input', '18000') + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') def test_aarch64_ast2700_evb_sdk_v09_03(self): self.set_machine('ast2700-evb') From c77ec95a4b51ed36156c94e06fac056d3dd620b2 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:36 +0800 Subject: [PATCH 2397/2892] tests/functional/aspeed: Update test ASPEED SDK v09.05 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ASPEED SDK v09.05, the naming convention for pre-built images has been updated. The pre-built image for AST2700 A0 has been renamed to ast2700-a0-default, while ast2700-default is now used for AST2700 A1. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-28-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 07b0c7c1fd..8df6a97a28 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -27,9 +27,9 @@ class AST2x00MachineSDK(QemuSystemTest): wait_for_console_pattern(self, '## Loading kernel from FIT Image') wait_for_console_pattern(self, 'Starting kernel ...') - ASSET_SDK_V903_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.03/ast2700-default-obmc.tar.gz', - '91225f50d255e2905ba8d8e0c80b71b9d157c3609770c7a740cd786370d85a77') + ASSET_SDK_V905_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.05/ast2700-a0-default-obmc.tar.gz', + 'cfbbd1cce72f2a3b73b9080c41eecdadebb7077fba4f7806d72ac99f3e84b74a') def start_ast2700_test(self, name): num_cpu = 4 @@ -89,11 +89,11 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') - def test_aarch64_ast2700_evb_sdk_v09_03(self): + def test_aarch64_ast2700_evb_sdk_v09_05(self): self.set_machine('ast2700-evb') - self.archive_extract(self.ASSET_SDK_V903_AST2700) - self.start_ast2700_test('ast2700-default') + self.archive_extract(self.ASSET_SDK_V905_AST2700) + self.start_ast2700_test('ast2700-a0-default') if __name__ == '__main__': From 2cab06e930cb724bd8ea7ce7ff0e1f362b41344d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:37 +0800 Subject: [PATCH 2398/2892] tests/functional/aspeed: Add test case for AST2700 A1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-29-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 8df6a97a28..c25c966278 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -31,6 +31,10 @@ class AST2x00MachineSDK(QemuSystemTest): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.05/ast2700-a0-default-obmc.tar.gz', 'cfbbd1cce72f2a3b73b9080c41eecdadebb7077fba4f7806d72ac99f3e84b74a') + ASSET_SDK_V905_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.05/ast2700-default-obmc.tar.gz', + 'c1f4496aec06743c812a6e9a1a18d032f34d62f3ddb6956e924fef62aa2046a5') + def start_ast2700_test(self, name): num_cpu = 4 uboot_size = os.path.getsize(self.scratch_file(name, @@ -95,6 +99,12 @@ class AST2x00MachineSDK(QemuSystemTest): self.archive_extract(self.ASSET_SDK_V905_AST2700) self.start_ast2700_test('ast2700-a0-default') + def test_aarch64_ast2700a1_evb_sdk_v09_05(self): + self.set_machine('ast2700a1-evb') + + self.archive_extract(self.ASSET_SDK_V905_AST2700A1) + self.start_ast2700_test('ast2700-default') + if __name__ == '__main__': QemuSystemTest.main() From 5ab179db11ca297c9e89a6d57f954d31965cbd7b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:38 +0800 Subject: [PATCH 2399/2892] docs/specs: Add aspeed-intc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AST2700 INTC design guidance and its block diagram. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-30-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/specs/aspeed-intc.rst | 136 +++++++++++++++++++++++++++++++++++++ docs/specs/index.rst | 1 + 2 files changed, 137 insertions(+) create mode 100644 docs/specs/aspeed-intc.rst diff --git a/docs/specs/aspeed-intc.rst b/docs/specs/aspeed-intc.rst new file mode 100644 index 0000000000..9cefd7f37f --- /dev/null +++ b/docs/specs/aspeed-intc.rst @@ -0,0 +1,136 @@ +=========================== +ASPEED Interrupt Controller +=========================== + +AST2700 +------- +There are a total of 480 interrupt sources in AST2700. Due to the limitation of +interrupt numbers of processors, the interrupts are merged every 32 sources for +interrupt numbers greater than 127. + +There are two levels of interrupt controllers, INTC (CPU Die) and INTCIO +(I/O Die). + +Interrupt Mapping +----------------- +- INTC: Handles interrupt sources 0 - 127 and integrates signals from INTCIO. +- INTCIO: Handles interrupt sources 128 - 319 independently. + +QEMU Support +------------ +Currently, only GIC 192 to 201 are supported, and their source interrupts are +from INTCIO and connected to INTC at input pin 0 and output pins 0 to 9 for +GIC 192-201. + +Design for GICINT 196 +--------------------- +The orgate has interrupt sources ranging from 0 to 31, with its output pin +connected to INTCIO "T0 GICINT_196". The output pin is then connected to INTC +"GIC_192_201" at bit 4, and its bit 4 output pin is connected to GIC 196. + +INTC GIC_192_201 Output Pin Mapping +----------------------------------- +The design of INTC GIC_192_201 have 10 output pins, mapped as following: + +==== ==== +Bit GIC +==== ==== +0 192 +1 193 +2 194 +3 195 +4 196 +5 197 +6 198 +7 199 +8 200 +9 201 +==== ==== + +AST2700 A0 +---------- +It has only one INTC controller, and currently, only GIC 128-136 is supported. +To support both AST2700 A1 and AST2700 A0, there are 10 OR gates in the INTC, +with gates 1 to 9 supporting GIC 128-136. + +Design for GICINT 132 +--------------------- +The orgate has interrupt sources ranging from 0 to 31, with its output pin +connected to INTC. The output pin is then connected to GIC 132. + +Block Diagram of GICINT 196 for AST2700 A1 and GICINT 132 for AST2700 A0 +------------------------------------------------------------------------ + +.. code-block:: + + |-------------------------------------------------------------------------------------------------------| + | AST2700 A1 Design | + | To GICINT196 | + | | + | ETH1 |-----------| |--------------------------| |--------------| | + | -------->|0 | | INTCIO | | orgates[0] | | + | ETH2 | 4| orgates[0]------>|inpin[0]-------->outpin[0]|------->| 0 | | + | -------->|1 5| orgates[1]------>|inpin[1]-------->outpin[1]|------->| 1 | | + | ETH3 | 6| orgates[2]------>|inpin[2]-------->outpin[2]|------->| 2 | | + | -------->|2 19| orgates[3]------>|inpin[3]-------->outpin[3]|------->| 3 OR[0:9] |-----| | + | UART0 | 20|-->orgates[4]------>|inpin[4]-------->outpin[4]|------->| 4 | | | + | -------->|7 21| orgates[5]------>|inpin[5]-------->outpin[5]|------->| 5 | | | + | UART1 | 22| orgates[6]------>|inpin[6]-------->outpin[6]|------->| 6 | | | + | -------->|8 23| orgates[7]------>|inpin[7]-------->outpin[7]|------->| 7 | | | + | UART2 | 24| orgates[8]------>|inpin[8]-------->outpin[8]|------->| 8 | | | + | -------->|9 25| orgates[9]------>|inpin[9]-------->outpin[9]|------->| 9 | | | + | UART3 | 26| |--------------------------| |--------------| | | + | ---------|10 27| | | + | UART5 | 28| | | + | -------->|11 29| | | + | UART6 | | | | + | -------->|12 30| |-----------------------------------------------------------------------| | + | UART7 | 31| | | + | -------->|13 | | | + | UART8 | OR[0:31] | | |------------------------------| |----------| | + | -------->|14 | | | INTC | | GIC | | + | UART9 | | | |inpin[0:0]--------->outpin[0] |---------->|192 | | + | -------->|15 | | |inpin[0:1]--------->outpin[1] |---------->|193 | | + | UART10 | | | |inpin[0:2]--------->outpin[2] |---------->|194 | | + | -------->|16 | | |inpin[0:3]--------->outpin[3] |---------->|195 | | + | UART11 | | |--------------> |inpin[0:4]--------->outpin[4] |---------->|196 | | + | -------->|17 | |inpin[0:5]--------->outpin[5] |---------->|197 | | + | UART12 | | |inpin[0:6]--------->outpin[6] |---------->|198 | | + | -------->|18 | |inpin[0:7]--------->outpin[7] |---------->|199 | | + | |-----------| |inpin[0:8]--------->outpin[8] |---------->|200 | | + | |inpin[0:9]--------->outpin[9] |---------->|201 | | + |-------------------------------------------------------------------------------------------------------| + |-------------------------------------------------------------------------------------------------------| + | ETH1 |-----------| orgates[1]------->|inpin[1]----------->outpin[10]|---------->|128 | | + | -------->|0 | orgates[2]------->|inpin[2]----------->outpin[11]|---------->|129 | | + | ETH2 | 4| orgates[3]------->|inpin[3]----------->outpin[12]|---------->|130 | | + | -------->|1 5| orgates[4]------->|inpin[4]----------->outpin[13]|---------->|131 | | + | ETH3 | 6|---->orgates[5]------->|inpin[5]----------->outpin[14]|---------->|132 | | + | -------->|2 19| orgates[6]------->|inpin[6]----------->outpin[15]|---------->|133 | | + | UART0 | 20| orgates[7]------->|inpin[7]----------->outpin[16]|---------->|134 | | + | -------->|7 21| orgates[8]------->|inpin[8]----------->outpin[17]|---------->|135 | | + | UART1 | 22| orgates[9]------->|inpin[9]----------->outpin[18]|---------->|136 | | + | -------->|8 23| |------------------------------| |----------| | + | UART2 | 24| | + | -------->|9 25| AST2700 A0 Design | + | UART3 | 26| | + | -------->|10 27| | + | UART5 | 28| | + | -------->|11 29| GICINT132 | + | UART6 | | | + | -------->|12 30| | + | UART7 | 31| | + | -------->|13 | | + | UART8 | OR[0:31] | | + | -------->|14 | | + | UART9 | | | + | -------->|15 | | + | UART10 | | | + | -------->|16 | | + | UART11 | | | + | -------->|17 | | + | UART12 | | | + | -------->|18 | | + | |-----------| | + | | + |-------------------------------------------------------------------------------------------------------| diff --git a/docs/specs/index.rst b/docs/specs/index.rst index d7675cebc2..f19d73c9f6 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -38,3 +38,4 @@ guest hardware that is specific to QEMU. rocker riscv-iommu riscv-aia + aspeed-intc From c9ce8a1ffdedbc55d107c4b5629eca5d1e219165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Jan 2025 18:55:03 +0100 Subject: [PATCH 2400/2892] linux-user: Only include 'exec/tb-flush.h' header when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Very few source files require to access "exec/tb-flush.h" declarations, and except a pair, they all include it explicitly. No need to overload the generic "user-internals.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ilya Leoshkevich Reviewed-by: Pierrick Bouvier Message-Id: <20250102182521.65428-2-philmd@linaro.org> --- linux-user/mmap.c | 1 + linux-user/syscall.c | 1 + linux-user/user-internals.h | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 6828b17a63..d1f36e6f16 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -21,6 +21,7 @@ #include "trace.h" #include "exec/log.h" #include "exec/page-protection.h" +#include "exec/tb-flush.h" #include "exec/translation-block.h" #include "qemu.h" #include "user/page-protection.h" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 02ea4221c9..b32de763f7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -26,6 +26,7 @@ #include "tcg/startup.h" #include "target_mman.h" #include "exec/page-protection.h" +#include "exec/tb-flush.h" #include "exec/translation-block.h" #include #include diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index b9b05c1d11..4aa253b566 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -20,7 +20,6 @@ #include "user/thunk.h" #include "exec/exec-all.h" -#include "exec/tb-flush.h" #include "qemu/log.h" extern char *exec_path; From 019b4e84eda27a006f94ed0faa024babd0a97e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 14:02:21 +0100 Subject: [PATCH 2401/2892] bsd-user: Always use mmap_find_vma_aligned() in target_mmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Massage target_mmap(): calculate alignment once, then unconditionally call mmap_find_vma_aligned(). Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Message-Id: <20250308122842.76377-2-philmd@linaro.org> --- bsd-user/mmap.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 346f2cefd3..dfa6e728ab 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -489,13 +489,12 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * before we truncate the length for mapping files below. */ if (!(flags & MAP_FIXED)) { + abi_ulong alignment; + host_len = len + offset - host_offset; host_len = HOST_PAGE_ALIGN(host_len); - if ((flags & MAP_ALIGNMENT_MASK) != 0) - start = mmap_find_vma_aligned(real_start, host_len, - (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT); - else - start = mmap_find_vma(real_start, host_len); + alignment = (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT; + start = mmap_find_vma_aligned(real_start, host_len, alignment); if (start == (abi_ulong)-1) { errno = ENOMEM; goto fail; From 84d66261bef3cdfea3bd1fb052a7ba38abb34b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 13:47:21 +0100 Subject: [PATCH 2402/2892] bsd-user: Propagate alignment argument to mmap_find_vma() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate the alignment to mmap_find_vma(), effectively embedding mmap_find_vma_aligned() within mmap_find_vma(). Add a comment in do_bsd_shmat() to clarify alignment above page size is not required. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Message-Id: <20250308122842.76377-3-philmd@linaro.org> --- bsd-user/bsd-mem.h | 4 +++- bsd-user/mmap.c | 10 ++-------- bsd-user/qemu.h | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h index f5ec0de24c..90ca0e3377 100644 --- a/bsd-user/bsd-mem.h +++ b/bsd-user/bsd-mem.h @@ -370,9 +370,11 @@ static inline abi_long do_bsd_shmat(int shmid, abi_ulong shmaddr, int shmflg) if (shmaddr) { host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg); } else { + abi_ulong alignment; abi_ulong mmap_start; - mmap_start = mmap_find_vma(0, shm_info.shm_segsz); + alignment = 0; /* alignment above page size not required */ + mmap_start = mmap_find_vma(0, shm_info.shm_segsz, alignment); if (mmap_start == -1) { return -TARGET_ENOMEM; diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index dfa6e728ab..3f0df79c37 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -275,8 +275,7 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, * It must be called with mmap_lock() held. * Return -1 if error. */ -static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, - abi_ulong alignment) +abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong alignment) { void *ptr, *prev; abi_ulong addr; @@ -395,11 +394,6 @@ static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, } } -abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) -{ - return mmap_find_vma_aligned(start, size, 0); -} - /* NOTE: all the constants are the HOST ones */ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, int flags, int fd, off_t offset) @@ -494,7 +488,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, host_len = len + offset - host_offset; host_len = HOST_PAGE_ALIGN(host_len); alignment = (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT; - start = mmap_find_vma_aligned(real_start, host_len, alignment); + start = mmap_find_vma(real_start, host_len, alignment); if (start == (abi_ulong)-1) { errno = ENOMEM; goto fail; diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 4e97c79631..0b3bd65b18 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -242,7 +242,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, abi_ulong new_addr); int target_msync(abi_ulong start, abi_ulong len, int flags); extern abi_ulong mmap_next_start; -abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size); +abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong alignment); void mmap_reserve(abi_ulong start, abi_ulong size); void TSA_NO_TSA mmap_fork_start(void); void TSA_NO_TSA mmap_fork_end(int child); From 1405d7e60d8c98a28b29885f70da4f2e4407fbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Jan 2025 17:33:13 +0100 Subject: [PATCH 2403/2892] user: Extract common MMAP API to 'user/mmap.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep common MMAP-related declarations in a single place. Note, this disable ThreadSafetyAnalysis on Linux for: - mmap_fork_start() - mmap_fork_end(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Reviewed-by: Richard Henderson Message-Id: <20250308122842.76377-4-philmd@linaro.org> --- bsd-user/qemu.h | 12 +----------- include/user/mmap.h | 32 ++++++++++++++++++++++++++++++++ linux-user/user-mmap.h | 19 ++----------------- 3 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 include/user/mmap.h diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 0b3bd65b18..c1c508281a 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -32,6 +32,7 @@ extern char **environ; #include "user/thunk.h" +#include "user/mmap.h" #include "target_arch.h" #include "syscall_defs.h" #include "target_syscall.h" @@ -233,19 +234,8 @@ void print_taken_signal(int target_signum, const target_siginfo_t *tinfo); extern int do_strace; /* 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, - int flags, int fd, off_t offset); -int target_munmap(abi_ulong start, abi_ulong len); -abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, - abi_ulong new_size, unsigned long flags, - abi_ulong new_addr); int target_msync(abi_ulong start, abi_ulong len, int flags); -extern abi_ulong mmap_next_start; -abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong alignment); void mmap_reserve(abi_ulong start, abi_ulong size); -void TSA_NO_TSA mmap_fork_start(void); -void TSA_NO_TSA mmap_fork_end(int child); /* main.c */ extern char qemu_proc_pathname[]; diff --git a/include/user/mmap.h b/include/user/mmap.h new file mode 100644 index 0000000000..4d5e9aac70 --- /dev/null +++ b/include/user/mmap.h @@ -0,0 +1,32 @@ +/* + * MMAP declarations for QEMU user emulation + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef USER_MMAP_H +#define USER_MMAP_H + +#include "user/abitypes.h" + +/* + * mmap_next_start: The base address for the next mmap without hint, + * increased after each successful map, starting at task_unmapped_base. + * This is an optimization within QEMU and not part of ADDR_COMPAT_LAYOUT. + */ +extern abi_ulong mmap_next_start; + +int target_mprotect(abi_ulong start, abi_ulong len, int prot); + +abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, int fd, off_t offset); +int target_munmap(abi_ulong start, abi_ulong len); +abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, + abi_ulong new_size, unsigned long flags, + abi_ulong new_addr); + +abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong alignment); + +void TSA_NO_TSA mmap_fork_start(void); +void TSA_NO_TSA mmap_fork_end(int child); + +#endif diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h index b94bcdcf83..dfc4477a72 100644 --- a/linux-user/user-mmap.h +++ b/linux-user/user-mmap.h @@ -18,6 +18,8 @@ #ifndef LINUX_USER_USER_MMAP_H #define LINUX_USER_USER_MMAP_H +#include "user/mmap.h" + /* * Guest parameters for the ADDR_COMPAT_LAYOUT personality * (at present this is the only layout supported by QEMU). @@ -39,24 +41,7 @@ extern abi_ulong task_unmapped_base; extern abi_ulong elf_et_dyn_base; -/* - * mmap_next_start: The base address for the next mmap without hint, - * increased after each successful map, starting at task_unmapped_base. - * This is an optimization within QEMU and not part of ADDR_COMPAT_LAYOUT. - */ -extern abi_ulong mmap_next_start; - -int target_mprotect(abi_ulong start, abi_ulong len, int prot); -abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, - int flags, int fd, off_t offset); -int target_munmap(abi_ulong start, abi_ulong len); -abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, - abi_ulong new_size, unsigned long flags, - abi_ulong new_addr); abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice); -abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong); -void mmap_fork_start(void); -void mmap_fork_end(int child); abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, abi_ulong shmaddr, int shmflg); From ca05578fc80f4253ed19f4c4128a4cbd5b83f0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 19:56:20 +0100 Subject: [PATCH 2404/2892] cpus: Register VMState per user / system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify cpu-target.c by extracting mixed vmstate code into the cpu_vmstate_register() / cpu_vmstate_unregister() helpers, implemented in cpu-user.c and cpu-system.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-20-philmd@linaro.org> --- cpu-target.c | 121 +----------------------------------------- hw/core/cpu-system.c | 115 +++++++++++++++++++++++++++++++++++++++ hw/core/cpu-user.c | 12 +++++ include/hw/core/cpu.h | 2 + 4 files changed, 131 insertions(+), 119 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index b6e66d5ac0..bc9c537c57 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -21,115 +21,17 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" -#include "migration/vmstate.h" -#ifndef CONFIG_USER_ONLY -#include "hw/core/sysemu-cpu-ops.h" -#endif #include "system/accel-ops.h" #include "system/cpus.h" -#include "system/tcg.h" #include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/cpu-common.h" -#include "exec/cputlb.h" -#include "exec/exec-all.h" -#include "exec/tb-flush.h" #include "exec/log.h" #include "accel/accel-cpu-target.h" #include "trace/trace-root.h" #include "qemu/accel.h" #include "hw/core/cpu.h" -#ifndef CONFIG_USER_ONLY -static int cpu_common_post_load(void *opaque, int version_id) -{ - if (tcg_enabled()) { - CPUState *cpu = opaque; - - /* - * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the - * version_id is increased. - */ - cpu->interrupt_request &= ~0x01; - - tlb_flush(cpu); - - /* - * loadvm has just updated the content of RAM, bypassing the - * usual mechanisms that ensure we flush TBs for writes to - * memory we've translated code from. So we must flush all TBs, - * which will now be stale. - */ - tb_flush(cpu); - } - - return 0; -} - -static int cpu_common_pre_load(void *opaque) -{ - CPUState *cpu = opaque; - - cpu->exception_index = -1; - - return 0; -} - -static bool cpu_common_exception_index_needed(void *opaque) -{ - CPUState *cpu = opaque; - - return tcg_enabled() && cpu->exception_index != -1; -} - -static const VMStateDescription vmstate_cpu_common_exception_index = { - .name = "cpu_common/exception_index", - .version_id = 1, - .minimum_version_id = 1, - .needed = cpu_common_exception_index_needed, - .fields = (const VMStateField[]) { - VMSTATE_INT32(exception_index, CPUState), - VMSTATE_END_OF_LIST() - } -}; - -static bool cpu_common_crash_occurred_needed(void *opaque) -{ - CPUState *cpu = opaque; - - return cpu->crash_occurred; -} - -static const VMStateDescription vmstate_cpu_common_crash_occurred = { - .name = "cpu_common/crash_occurred", - .version_id = 1, - .minimum_version_id = 1, - .needed = cpu_common_crash_occurred_needed, - .fields = (const VMStateField[]) { - VMSTATE_BOOL(crash_occurred, CPUState), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_cpu_common = { - .name = "cpu_common", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = cpu_common_pre_load, - .post_load = cpu_common_post_load, - .fields = (const VMStateField[]) { - VMSTATE_UINT32(halted, CPUState), - VMSTATE_UINT32(interrupt_request, CPUState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription * const []) { - &vmstate_cpu_common_exception_index, - &vmstate_cpu_common_crash_occurred, - NULL - } -}; -#endif - bool cpu_exec_realizefn(CPUState *cpu, Error **errp) { if (!accel_cpu_common_realize(cpu, errp)) { @@ -139,33 +41,14 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp) /* Wait until cpu initialization complete before exposing cpu. */ cpu_list_add(cpu); -#ifdef CONFIG_USER_ONLY - assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || - qdev_get_vmsd(DEVICE(cpu))->unmigratable); -#else - if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { - vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu); - } - if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { - vmstate_register(NULL, cpu->cpu_index, cpu->cc->sysemu_ops->legacy_vmsd, cpu); - } -#endif /* CONFIG_USER_ONLY */ + cpu_vmstate_register(cpu); return true; } void cpu_exec_unrealizefn(CPUState *cpu) { -#ifndef CONFIG_USER_ONLY - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->legacy_vmsd != NULL) { - vmstate_unregister(NULL, cc->sysemu_ops->legacy_vmsd, cpu); - } - if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { - vmstate_unregister(NULL, &vmstate_cpu_common, cpu); - } -#endif + cpu_vmstate_unregister(cpu); cpu_list_remove(cpu); /* diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index e511507e13..6c89d76e49 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -21,11 +21,15 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/address-spaces.h" +#include "exec/cputlb.h" #include "exec/memory.h" +#include "exec/tb-flush.h" #include "exec/tswap.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/core/sysemu-cpu-ops.h" +#include "migration/vmstate.h" +#include "system/tcg.h" bool cpu_paging_enabled(const CPUState *cpu) { @@ -194,3 +198,114 @@ void cpu_exec_initfn(CPUState *cpu) cpu->memory = get_system_memory(); object_ref(OBJECT(cpu->memory)); } + +static int cpu_common_post_load(void *opaque, int version_id) +{ + if (tcg_enabled()) { + CPUState *cpu = opaque; + + /* + * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + * version_id is increased. + */ + cpu->interrupt_request &= ~0x01; + + tlb_flush(cpu); + + /* + * loadvm has just updated the content of RAM, bypassing the + * usual mechanisms that ensure we flush TBs for writes to + * memory we've translated code from. So we must flush all TBs, + * which will now be stale. + */ + tb_flush(cpu); + } + + return 0; +} + +static int cpu_common_pre_load(void *opaque) +{ + CPUState *cpu = opaque; + + cpu->exception_index = -1; + + return 0; +} + +static bool cpu_common_exception_index_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return tcg_enabled() && cpu->exception_index != -1; +} + +static const VMStateDescription vmstate_cpu_common_exception_index = { + .name = "cpu_common/exception_index", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_exception_index_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(exception_index, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool cpu_common_crash_occurred_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return cpu->crash_occurred; +} + +static const VMStateDescription vmstate_cpu_common_crash_occurred = { + .name = "cpu_common/crash_occurred", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_crash_occurred_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(crash_occurred, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cpu_common = { + .name = "cpu_common", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = cpu_common_pre_load, + .post_load = cpu_common_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(halted, CPUState), + VMSTATE_UINT32(interrupt_request, CPUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_cpu_common_exception_index, + &vmstate_cpu_common_crash_occurred, + NULL + } +}; + +void cpu_vmstate_register(CPUState *cpu) +{ + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu); + } + if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_register(NULL, cpu->cpu_index, + cpu->cc->sysemu_ops->legacy_vmsd, cpu); + } +} + +void cpu_vmstate_unregister(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_unregister(NULL, cc->sysemu_ops->legacy_vmsd, cpu); + } + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_unregister(NULL, &vmstate_cpu_common, cpu); + } +} diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c index cdd8de2fef..1892acdee0 100644 --- a/hw/core/cpu-user.c +++ b/hw/core/cpu-user.c @@ -10,6 +10,7 @@ #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/core/cpu.h" +#include "migration/vmstate.h" static const Property cpu_user_props[] = { /* @@ -30,3 +31,14 @@ void cpu_exec_initfn(CPUState *cpu) { /* nothing to do */ } + +void cpu_vmstate_register(CPUState *cpu) +{ + assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || + qdev_get_vmsd(DEVICE(cpu))->unmigratable); +} + +void cpu_vmstate_unregister(CPUState *cpu) +{ + /* nothing to do */ +} diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 9dd6ac7c76..bc0c946834 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1165,6 +1165,8 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_initfn(CPUState *cpu); +void cpu_vmstate_register(CPUState *cpu); +void cpu_vmstate_unregister(CPUState *cpu); bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); void cpu_exec_reset_hold(CPUState *cpu); From 43610f3184f846da948e8ab9dbc0c5de1e9bde79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Jan 2025 13:46:03 +0100 Subject: [PATCH 2405/2892] cpus: Build cpu_exec_[un]realizefn() methods once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that cpu_exec_realizefn() and cpu_exec_unrealizefn() methods don't use any target specific definition anymore, we can move them to cpu-common.c to be able to build them once. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250123234415.59850-21-philmd@linaro.org> --- cpu-target.c | 29 ----------------------------- hw/core/cpu-common.c | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index bc9c537c57..cae77374b3 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -25,38 +25,9 @@ #include "system/cpus.h" #include "exec/tswap.h" #include "exec/replay-core.h" -#include "exec/cpu-common.h" #include "exec/log.h" #include "accel/accel-cpu-target.h" #include "trace/trace-root.h" -#include "qemu/accel.h" -#include "hw/core/cpu.h" - -bool cpu_exec_realizefn(CPUState *cpu, Error **errp) -{ - if (!accel_cpu_common_realize(cpu, errp)) { - return false; - } - - /* Wait until cpu initialization complete before exposing cpu. */ - cpu_list_add(cpu); - - cpu_vmstate_register(cpu); - - return true; -} - -void cpu_exec_unrealizefn(CPUState *cpu) -{ - cpu_vmstate_unregister(cpu); - - cpu_list_remove(cpu); - /* - * Now that the vCPU has been removed from the RCU list, we can call - * accel_cpu_common_unrealize, which may free fields using call_rcu. - */ - accel_cpu_common_unrealize(cpu); -} char *cpu_model_from_type(const char *typename) { diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index d5cd227fe6..5671d8d4f5 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -193,6 +193,20 @@ static void cpu_common_parse_features(const char *typename, char *features, } } +bool cpu_exec_realizefn(CPUState *cpu, Error **errp) +{ + if (!accel_cpu_common_realize(cpu, errp)) { + return false; + } + + /* Wait until cpu initialization complete before exposing cpu. */ + cpu_list_add(cpu); + + cpu_vmstate_register(cpu); + + return true; +} + static void cpu_common_realizefn(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); @@ -234,6 +248,18 @@ static void cpu_common_unrealizefn(DeviceState *dev) cpu_exec_unrealizefn(cpu); } +void cpu_exec_unrealizefn(CPUState *cpu) +{ + cpu_vmstate_unregister(cpu); + + cpu_list_remove(cpu); + /* + * Now that the vCPU has been removed from the RCU list, we can call + * accel_cpu_common_unrealize, which may free fields using call_rcu. + */ + accel_cpu_common_unrealize(cpu); +} + static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); From 30e76638eb35ffe88e95cca2b5af952c14dc336d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:12:35 +0100 Subject: [PATCH 2406/2892] cpus: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-5-philmd@linaro.org> --- cpu-common.c | 10 +++---- hw/core/cpu-common.c | 13 +++------ hw/core/cpu-system.c | 61 ++++++++++++++++--------------------------- include/hw/core/cpu.h | 10 +++---- 4 files changed, 33 insertions(+), 61 deletions(-) diff --git a/cpu-common.c b/cpu-common.c index f5dcc2d136..ef5757d23b 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -388,11 +388,10 @@ void process_queued_cpu_work(CPUState *cpu) int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, CPUBreakpoint **breakpoint) { - CPUClass *cc = CPU_GET_CLASS(cpu); CPUBreakpoint *bp; - if (cc->gdb_adjust_breakpoint) { - pc = cc->gdb_adjust_breakpoint(cpu, pc); + if (cpu->cc->gdb_adjust_breakpoint) { + pc = cpu->cc->gdb_adjust_breakpoint(cpu, pc); } bp = g_malloc(sizeof(*bp)); @@ -418,11 +417,10 @@ int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, /* Remove a specific breakpoint. */ int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) { - CPUClass *cc = CPU_GET_CLASS(cpu); CPUBreakpoint *bp; - if (cc->gdb_adjust_breakpoint) { - pc = cc->gdb_adjust_breakpoint(cpu, pc); + if (cpu->cc->gdb_adjust_breakpoint) { + pc = cpu->cc->gdb_adjust_breakpoint(cpu, pc); } QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 5671d8d4f5..ba0f02e49d 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -40,9 +40,7 @@ CPUState *cpu_by_arch_id(int64_t id) CPUState *cpu; CPU_FOREACH(cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->get_arch_id(cpu) == id) { + if (cpu->cc->get_arch_id(cpu) == id) { return cpu; } } @@ -101,11 +99,9 @@ static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) void cpu_dump_state(CPUState *cpu, FILE *f, int flags) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->dump_state) { + if (cpu->cc->dump_state) { cpu_synchronize_state(cpu); - cc->dump_state(cpu, f, flags); + cpu->cc->dump_state(cpu, f, flags); } } @@ -119,11 +115,10 @@ void cpu_reset(CPUState *cpu) static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); - CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cc->reset_dump_flags); + log_cpu_state(cpu, cpu->cc->reset_dump_flags); } cpu->interrupt_request = 0; diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 6c89d76e49..e29664d39b 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -33,10 +33,8 @@ bool cpu_paging_enabled(const CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_paging_enabled) { - return cc->sysemu_ops->get_paging_enabled(cpu); + if (cpu->cc->sysemu_ops->get_paging_enabled) { + return cpu->cc->sysemu_ops->get_paging_enabled(cpu); } return false; @@ -45,10 +43,8 @@ bool cpu_paging_enabled(const CPUState *cpu) bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, Error **errp) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_memory_mapping) { - return cc->sysemu_ops->get_memory_mapping(cpu, list, errp); + if (cpu->cc->sysemu_ops->get_memory_mapping) { + return cpu->cc->sysemu_ops->get_memory_mapping(cpu, list, errp); } error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); @@ -58,15 +54,15 @@ bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs) { - CPUClass *cc = CPU_GET_CLASS(cpu); hwaddr paddr; - if (cc->sysemu_ops->get_phys_page_attrs_debug) { - paddr = cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, attrs); + if (cpu->cc->sysemu_ops->get_phys_page_attrs_debug) { + paddr = cpu->cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, + attrs); } else { /* Fallback for CPUs which don't implement the _attrs_ hook */ *attrs = MEMTXATTRS_UNSPECIFIED; - paddr = cc->sysemu_ops->get_phys_page_debug(cpu, addr); + paddr = cpu->cc->sysemu_ops->get_phys_page_debug(cpu, addr); } /* Indicate that this is a debug access. */ attrs->debug = 1; @@ -94,64 +90,53 @@ int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf32_qemunote) { + if (!cpu->cc->sysemu_ops->write_elf32_qemunote) { return 0; } - return (*cc->sysemu_ops->write_elf32_qemunote)(f, cpu, opaque); + return (*cpu->cc->sysemu_ops->write_elf32_qemunote)(f, cpu, opaque); } int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, int cpuid, void *opaque) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf32_note) { + if (!cpu->cc->sysemu_ops->write_elf32_note) { return -1; } - return (*cc->sysemu_ops->write_elf32_note)(f, cpu, cpuid, opaque); + return (*cpu->cc->sysemu_ops->write_elf32_note)(f, cpu, cpuid, opaque); } int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf64_qemunote) { + if (!cpu->cc->sysemu_ops->write_elf64_qemunote) { return 0; } - return (*cc->sysemu_ops->write_elf64_qemunote)(f, cpu, opaque); + return (*cpu->cc->sysemu_ops->write_elf64_qemunote)(f, cpu, opaque); } int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, int cpuid, void *opaque) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf64_note) { + if (!cpu->cc->sysemu_ops->write_elf64_note) { return -1; } - return (*cc->sysemu_ops->write_elf64_note)(f, cpu, cpuid, opaque); + return (*cpu->cc->sysemu_ops->write_elf64_note)(f, cpu, cpuid, opaque); } bool cpu_virtio_is_big_endian(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->virtio_is_big_endian) { - return cc->sysemu_ops->virtio_is_big_endian(cpu); + if (cpu->cc->sysemu_ops->virtio_is_big_endian) { + return cpu->cc->sysemu_ops->virtio_is_big_endian(cpu); } return target_words_bigendian(); } GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); GuestPanicInformation *res = NULL; - if (cc->sysemu_ops->get_crash_info) { - res = cc->sysemu_ops->get_crash_info(cpu); + if (cpu->cc->sysemu_ops->get_crash_info) { + res = cpu->cc->sysemu_ops->get_crash_info(cpu); } return res; } @@ -300,10 +285,8 @@ void cpu_vmstate_register(CPUState *cpu) void cpu_vmstate_unregister(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->legacy_vmsd != NULL) { - vmstate_unregister(NULL, cc->sysemu_ops->legacy_vmsd, cpu); + if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_unregister(NULL, cpu->cc->sysemu_ops->legacy_vmsd, cpu); } if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { vmstate_unregister(NULL, &vmstate_cpu_common, cpu); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index bc0c946834..c6df426c94 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -826,10 +826,8 @@ const char *parse_cpu_option(const char *cpu_option); */ static inline bool cpu_has_work(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - g_assert(cc->has_work); - return cc->has_work(cpu); + g_assert(cpu->cc->has_work); + return cpu->cc->has_work(cpu); } /** @@ -968,9 +966,7 @@ void cpu_interrupt(CPUState *cpu, int mask); */ static inline void cpu_set_pc(CPUState *cpu, vaddr addr) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - cc->set_pc(cpu, addr); + cpu->cc->set_pc(cpu, addr); } /** From e27fa95fb9d1dd4668e8b1411b47df14253e47fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:11:09 +0100 Subject: [PATCH 2407/2892] accel: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-6-philmd@linaro.org> --- accel/accel-target.c | 12 +++++------- accel/tcg/tcg-accel-ops.c | 3 +-- accel/tcg/translate-all.c | 2 +- accel/tcg/watchpoint.c | 9 ++++----- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/accel/accel-target.c b/accel/accel-target.c index 8358727462..33a539b4cb 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -113,22 +113,20 @@ void accel_init_interfaces(AccelClass *ac) void accel_cpu_instance_init(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->accel_cpu && cc->accel_cpu->cpu_instance_init) { - cc->accel_cpu->cpu_instance_init(cpu); + if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) { + cpu->cc->accel_cpu->cpu_instance_init(cpu); } } bool accel_cpu_common_realize(CPUState *cpu, Error **errp) { - CPUClass *cc = CPU_GET_CLASS(cpu); AccelState *accel = current_accel(); AccelClass *acc = ACCEL_GET_CLASS(accel); /* target specific realization */ - if (cc->accel_cpu && cc->accel_cpu->cpu_target_realize - && !cc->accel_cpu->cpu_target_realize(cpu, errp)) { + if (cpu->cc->accel_cpu + && cpu->cc->accel_cpu->cpu_target_realize + && !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) { return false; } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 53e580d128..d9b662efe3 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -121,10 +121,9 @@ static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, }; - CPUClass *cc = CPU_GET_CLASS(cpu); int cputype = xlat[gdbtype]; - if (cc->gdb_stop_before_watchpoint) { + if (cpu->cc->gdb_stop_before_watchpoint) { cputype |= BP_STOP_BEFORE_ACCESS; } return cputype; diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 0914d6e98b..82bc16bd53 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -630,7 +630,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * to account for the re-execution of the branch. */ n = 1; - cc = CPU_GET_CLASS(cpu); + cc = cpu->cc; if (cc->tcg_ops->io_recompile_replay_branch && cc->tcg_ops->io_recompile_replay_branch(cpu, tb)) { cpu->neg.icount_decr.u16.low++; diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index ba8c9859cf..65b21884ce 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -68,7 +68,6 @@ int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, MemTxAttrs attrs, int flags, uintptr_t ra) { - CPUClass *cc = CPU_GET_CLASS(cpu); CPUWatchpoint *wp; assert(tcg_enabled()); @@ -84,9 +83,9 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, return; } - if (cc->tcg_ops->adjust_watchpoint_address) { + if (cpu->cc->tcg_ops->adjust_watchpoint_address) { /* this is currently used only by ARM BE32 */ - addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); + addr = cpu->cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); } assert((flags & ~BP_MEM_ACCESS) == 0); @@ -118,8 +117,8 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, wp->hitattrs = attrs; if (wp->flags & BP_CPU - && cc->tcg_ops->debug_check_watchpoint - && !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { + && cpu->cc->tcg_ops->debug_check_watchpoint + && !cpu->cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { wp->flags &= ~BP_WATCHPOINT_HIT; continue; } From 18b3abb7224f84364bee200a10413d6be2a1c4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:11:17 +0100 Subject: [PATCH 2408/2892] user: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-7-philmd@linaro.org> --- bsd-user/signal.c | 4 ++-- linux-user/alpha/target_proc.h | 2 +- linux-user/signal.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ab1d9ddd50..a8cfcca130 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -1034,7 +1034,7 @@ void process_pending_signals(CPUArchState *env) void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { - const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); @@ -1050,7 +1050,7 @@ 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) { - const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; if (tcg_ops->record_sigbus) { tcg_ops->record_sigbus(cpu, addr, access_type, ra); diff --git a/linux-user/alpha/target_proc.h b/linux-user/alpha/target_proc.h index dac37dffc9..da437ee0e5 100644 --- a/linux-user/alpha/target_proc.h +++ b/linux-user/alpha/target_proc.h @@ -15,7 +15,7 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) const char *p, *q; int t; - p = object_class_get_name(OBJECT_CLASS(CPU_GET_CLASS(env_cpu(cpu_env)))); + p = object_class_get_name(OBJECT_CLASS(env_cpu(cpu_env)->cc)); q = strchr(p, '-'); t = q - p; assert(t < sizeof(model)); diff --git a/linux-user/signal.c b/linux-user/signal.c index 4799b79ded..4dafc2c3a2 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -753,7 +753,7 @@ void force_sigsegv(int oldsig) void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { - const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); @@ -769,7 +769,7 @@ 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) { - const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; if (tcg_ops->record_sigbus) { tcg_ops->record_sigbus(cpu, addr, access_type, ra); From 4c5c410ceb4e039a49a120a436cc6183831a778b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:11:25 +0100 Subject: [PATCH 2409/2892] disas: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-8-philmd@linaro.org> --- disas/disas-common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/disas/disas-common.c b/disas/disas-common.c index ae3f9e46ea..21c2f03430 100644 --- a/disas/disas-common.c +++ b/disas/disas-common.c @@ -62,9 +62,8 @@ void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) s->info.print_address_func = print_address; s->info.endian = BFD_ENDIAN_UNKNOWN; - CPUClass *cc = CPU_GET_CLASS(cpu); - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s->info); + if (cpu->cc->disas_set_info) { + cpu->cc->disas_set_info(cpu, &s->info); g_assert(s->info.endian != BFD_ENDIAN_UNKNOWN); } } From 0368d8d189af0444cd818f4f695beb1d94706f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:11:35 +0100 Subject: [PATCH 2410/2892] gdbstub: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Alex Bennée Message-Id: <20250122093028.52416-9-philmd@linaro.org> --- gdbstub/gdbstub.c | 26 +++++++++----------------- gdbstub/system.c | 7 ++----- gdbstub/user-target.c | 6 ++---- gdbstub/user.c | 7 ++----- 4 files changed, 15 insertions(+), 31 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index e366df12d4..282e13e163 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -354,7 +354,6 @@ static const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { CPUState *cpu = gdb_get_first_cpu_in_process(process); - CPUClass *cc = CPU_GET_CLASS(cpu); GDBRegisterState *r; size_t len; @@ -377,11 +376,11 @@ static const char *get_feature_xml(const char *p, const char **newp, "" "")); - if (cc->gdb_arch_name) { + if (cpu->cc->gdb_arch_name) { g_ptr_array_add( xml, g_markup_printf_escaped("%s", - cc->gdb_arch_name(cpu))); + cpu->cc->gdb_arch_name(cpu))); } for (guint i = 0; i < cpu->gdb_regs->len; i++) { r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); @@ -520,11 +519,10 @@ GArray *gdb_get_register_list(CPUState *cpu) int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { - CPUClass *cc = CPU_GET_CLASS(cpu); GDBRegisterState *r; - if (reg < cc->gdb_num_core_regs) { - return cc->gdb_read_register(cpu, buf, reg); + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->cc->gdb_read_register(cpu, buf, reg); } for (guint i = 0; i < cpu->gdb_regs->len; i++) { @@ -538,11 +536,10 @@ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { - CPUClass *cc = CPU_GET_CLASS(cpu); GDBRegisterState *r; - if (reg < cc->gdb_num_core_regs) { - return cc->gdb_write_register(cpu, mem_buf, reg); + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->cc->gdb_write_register(cpu, mem_buf, reg); } for (guint i = 0; i < cpu->gdb_regs->len; i++) { @@ -570,7 +567,7 @@ static void gdb_register_feature(CPUState *cpu, int base_reg, void gdb_init_cpu(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); + CPUClass *cc = cpu->cc; const GDBFeature *feature; cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); @@ -1646,11 +1643,8 @@ void gdb_extend_qsupported_features(char *qflags) static void handle_query_supported(GArray *params, void *user_ctx) { - CPUClass *cc; - g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); - cc = CPU_GET_CLASS(first_cpu); - if (cc->gdb_core_xml_file) { + if (first_cpu->cc->gdb_core_xml_file) { g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } @@ -1697,7 +1691,6 @@ static void handle_query_supported(GArray *params, void *user_ctx) static void handle_query_xfer_features(GArray *params, void *user_ctx) { GDBProcess *process; - CPUClass *cc; unsigned long len, total_len, addr; const char *xml; const char *p; @@ -1708,8 +1701,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) } process = gdb_get_cpu_process(gdbserver_state.g_cpu); - cc = CPU_GET_CLASS(gdbserver_state.g_cpu); - if (!cc->gdb_core_xml_file) { + if (!gdbserver_state.g_cpu->cc->gdb_core_xml_file) { gdb_put_packet(""); return; } diff --git a/gdbstub/system.c b/gdbstub/system.c index 416c1dbe1e..dd22ff0fb3 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -456,8 +456,6 @@ static int phy_memory_mode; int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc; - if (phy_memory_mode) { if (is_write) { cpu_physical_memory_write(addr, buf, len); @@ -467,9 +465,8 @@ int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, return 0; } - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c index 4bfcf78aaa..43231e695e 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -233,10 +233,8 @@ void gdb_handle_query_offsets(GArray *params, void *user_ctx) static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc; - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } diff --git a/gdbstub/user.c b/gdbstub/user.c index 3730f32c41..67403e5a25 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -743,11 +743,8 @@ int gdb_continue_partial(char *newstates) int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc; - - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } From bd7d74283464491b461bb1136e69963962fd05aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:11:43 +0100 Subject: [PATCH 2411/2892] hw/acpi: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-10-philmd@linaro.org> --- hw/acpi/cpu.c | 4 ++-- hw/acpi/cpu_hotplug.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index f70a2c045e..6f1ae79edb 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -235,8 +235,8 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, static AcpiCpuStatus *get_cpu_status(CPUHotplugState *cpu_st, DeviceState *dev) { - CPUClass *k = CPU_GET_CLASS(dev); - uint64_t cpu_arch_id = k->get_arch_id(CPU(dev)); + CPUState *cpu = CPU(dev); + uint64_t cpu_arch_id = cpu->cc->get_arch_id(cpu); int i; for (i = 0; i < cpu_st->dev_count; i++) { diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index 83b8bc5deb..aa0e1e3efa 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -62,10 +62,9 @@ static const MemoryRegionOps AcpiCpuHotplug_ops = { static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, bool *swtchd_to_modern) { - CPUClass *k = CPU_GET_CLASS(cpu); int64_t cpu_id; - cpu_id = k->get_arch_id(cpu); + cpu_id = cpu->cc->get_arch_id(cpu); if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) { object_property_set_bool(g->device, "cpu-hotplug-legacy", false, &error_abort); From 0ebdf989c32031019aa0974dbb6b840fca52991e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:11:55 +0100 Subject: [PATCH 2412/2892] target/arm: Prefer cached CpuClass over CPU_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CpuState caches its CPUClass since commit 6fbdff87062 ("cpu: cache CPUClass in CPUState for hot code paths"), use it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250122093028.52416-11-philmd@linaro.org> --- target/arm/cpu.c | 3 +-- target/arm/tcg/cpu-v7m.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index cacbbc615a..d7e61d08bb 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -830,7 +830,6 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - CPUClass *cc = CPU_GET_CLASS(cs); CPUARMState *env = cpu_env(cs); uint32_t cur_el = arm_current_el(env); bool secure = arm_is_secure(env); @@ -930,7 +929,7 @@ static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) found: cs->exception_index = excp_idx; env->exception.target_el = target_el; - cc->tcg_ops->do_interrupt(cs); + cs->cc->tcg_ops->do_interrupt(cs); return true; } diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 29a41fde69..c4dd309272 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -19,7 +19,6 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - CPUClass *cc = CPU_GET_CLASS(cs); ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; bool ret = false; @@ -35,7 +34,7 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) if (interrupt_request & CPU_INTERRUPT_HARD && (armv7m_nvic_can_take_pending_exception(env->nvic))) { cs->exception_index = EXCP_IRQ; - cc->tcg_ops->do_interrupt(cs); + cs->cc->tcg_ops->do_interrupt(cs); ret = true; } return ret; From c0ee4dd1552b73bdde90875ce62e036a3ca8a007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:53:40 +0100 Subject: [PATCH 2413/2892] cpus: Restrict cpu_has_work() to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method is not used on user emulation, because there is always work to do there. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-2-philmd@linaro.org> --- include/hw/core/cpu.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c6df426c94..2d4ebb7990 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -750,6 +750,20 @@ int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs); */ bool cpu_virtio_is_big_endian(CPUState *cpu); +/** + * cpu_has_work: + * @cpu: The vCPU to check. + * + * Checks whether the CPU has work to do. + * + * Returns: %true if the CPU has work, %false otherwise. + */ +static inline bool cpu_has_work(CPUState *cpu) +{ + g_assert(cpu->cc->has_work); + return cpu->cc->has_work(cpu); +} + #endif /* CONFIG_USER_ONLY */ /** @@ -816,20 +830,6 @@ CPUState *cpu_create(const char *typename); */ const char *parse_cpu_option(const char *cpu_option); -/** - * cpu_has_work: - * @cpu: The vCPU to check. - * - * Checks whether the CPU has work to do. - * - * Returns: %true if the CPU has work, %false otherwise. - */ -static inline bool cpu_has_work(CPUState *cpu) -{ - g_assert(cpu->cc->has_work); - return cpu->cc->has_work(cpu); -} - /** * qemu_cpu_is_self: * @cpu: The vCPU to check against. From 8f8dbe04bdafdbe265e9ae25737bb18daacc6ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:56:10 +0100 Subject: [PATCH 2414/2892] cpus: Un-inline cpu_has_work() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to expand cpu_has_work(), un-inline it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-3-philmd@linaro.org> --- hw/core/cpu-system.c | 6 ++++++ include/hw/core/cpu.h | 6 +----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index e29664d39b..c10e3c9ba6 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -31,6 +31,12 @@ #include "migration/vmstate.h" #include "system/tcg.h" +bool cpu_has_work(CPUState *cpu) +{ + g_assert(cpu->cc->has_work); + return cpu->cc->has_work(cpu); +} + bool cpu_paging_enabled(const CPUState *cpu) { if (cpu->cc->sysemu_ops->get_paging_enabled) { diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 2d4ebb7990..a54dd2cf69 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -758,11 +758,7 @@ bool cpu_virtio_is_big_endian(CPUState *cpu); * * Returns: %true if the CPU has work, %false otherwise. */ -static inline bool cpu_has_work(CPUState *cpu) -{ - g_assert(cpu->cc->has_work); - return cpu->cc->has_work(cpu); -} +bool cpu_has_work(CPUState *cpu); #endif /* CONFIG_USER_ONLY */ From 72eacd623170dd680557ece6957575c30774cdef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:57:16 +0100 Subject: [PATCH 2415/2892] cpus: Introduce SysemuCPUOps::has_work() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SysemuCPUOps::has_work() is similar to CPUClass::has_work(), but only exposed on system emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-4-philmd@linaro.org> --- hw/core/cpu-system.c | 4 ++++ include/accel/tcg/cpu-ops.h | 2 +- include/hw/core/sysemu-cpu-ops.h | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index c10e3c9ba6..601335fd76 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -33,6 +33,10 @@ bool cpu_has_work(CPUState *cpu) { + if (cpu->cc->sysemu_ops->has_work) { + return cpu->cc->sysemu_ops->has_work(cpu); + } + g_assert(cpu->cc->has_work); return cpu->cc->has_work(cpu); } diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 2e3f1690f1..f60e5303f2 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -141,7 +141,7 @@ struct TCGCPUOps { * * This method must be provided. If the target does not need to * do anything special for halt, the same function used for its - * CPUClass::has_work method can be used here, as they have the + * SysemuCPUOps::has_work method can be used here, as they have the * same function signature. */ bool (*cpu_exec_halt)(CPUState *cpu); diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h index 0df5b058f5..dee8a62ca9 100644 --- a/include/hw/core/sysemu-cpu-ops.h +++ b/include/hw/core/sysemu-cpu-ops.h @@ -16,6 +16,10 @@ * struct SysemuCPUOps: System operations specific to a CPU class */ typedef struct SysemuCPUOps { + /** + * @has_work: Callback for checking if there is work to do. + */ + bool (*has_work)(CPUState *cpu); /** * @get_memory_mapping: Callback for obtaining the memory mappings. */ From 35e0769d3f341f9a3fc1de104ac57a3d1080d3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:58:10 +0100 Subject: [PATCH 2416/2892] target/alpha: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-5-philmd@linaro.org> --- target/alpha/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 2eabd7724d..584c2aa76b 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -63,6 +63,7 @@ static void alpha_restore_state_to_opc(CPUState *cs, } } +#ifndef CONFIG_USER_ONLY static bool alpha_cpu_has_work(CPUState *cs) { /* Here we are checking to see if the CPU should wake up from HALT. @@ -77,6 +78,7 @@ static bool alpha_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_SMP | CPU_INTERRUPT_MCHK); } +#endif /* !CONFIG_USER_ONLY */ static int alpha_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -224,6 +226,7 @@ static void alpha_cpu_initfn(Object *obj) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps alpha_sysemu_ops = { + .has_work = alpha_cpu_has_work, .get_phys_page_debug = alpha_cpu_get_phys_page_debug, }; #endif @@ -259,7 +262,6 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_realize); cc->class_by_name = alpha_cpu_class_by_name; - cc->has_work = alpha_cpu_has_work; cc->mmu_index = alpha_cpu_mmu_index; cc->dump_state = alpha_cpu_dump_state; cc->set_pc = alpha_cpu_set_pc; From f5e67b6ddad2262fd692caeb6090fa138241306f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:59:17 +0100 Subject: [PATCH 2417/2892] target/arm: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-6-philmd@linaro.org> --- target/arm/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d7e61d08bb..01786ac787 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -123,6 +123,7 @@ void arm_restore_state_to_opc(CPUState *cs, } #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY /* * With SCTLR_ELx.NMI == 0, IRQ with Superpriority is masked identically with * IRQ without Superpriority. Moreover, if the GIC is configured so that @@ -141,6 +142,7 @@ static bool arm_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR | CPU_INTERRUPT_EXITTB); } +#endif /* !CONFIG_USER_ONLY */ static int arm_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -2655,6 +2657,7 @@ static const gchar *arm_gdb_arch_name(CPUState *cs) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps arm_sysemu_ops = { + .has_work = arm_cpu_has_work, .get_phys_page_attrs_debug = arm_cpu_get_phys_page_attrs_debug, .asidx_from_attrs = arm_asidx_from_attrs, .write_elf32_note = arm_cpu_write_elf32_note, @@ -2705,7 +2708,6 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_phases); cc->class_by_name = arm_cpu_class_by_name; - cc->has_work = arm_cpu_has_work; cc->mmu_index = arm_cpu_mmu_index; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; From 6c8d41eab6d1774105562329eea3a731dd267c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:59:48 +0100 Subject: [PATCH 2418/2892] target/avr: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-7-philmd@linaro.org> --- target/avr/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 2871d30540..834c7082aa 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -201,6 +201,7 @@ static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps avr_sysemu_ops = { + .has_work = avr_cpu_has_work, .get_phys_page_debug = avr_cpu_get_phys_page_debug, }; @@ -233,7 +234,6 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = avr_cpu_class_by_name; - cc->has_work = avr_cpu_has_work; cc->mmu_index = avr_cpu_mmu_index; cc->dump_state = avr_cpu_dump_state; cc->set_pc = avr_cpu_set_pc; From 7ec1c634215527782ff508dd88f5bb4e324a3d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 12:59:57 +0100 Subject: [PATCH 2419/2892] target/hexagon: Remove CPUClass:has_work() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove as unreachable code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Brian Cain Message-Id: <20250125170125.32855-8-philmd@linaro.org> --- target/hexagon/cpu.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index a9beb9a175..766b678651 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -262,11 +262,6 @@ static void hexagon_cpu_synchronize_from_tb(CPUState *cs, cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } -static bool hexagon_cpu_has_work(CPUState *cs) -{ - return true; -} - static void hexagon_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -346,7 +341,6 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = hexagon_cpu_class_by_name; - cc->has_work = hexagon_cpu_has_work; cc->dump_state = hexagon_dump_state; cc->set_pc = hexagon_cpu_set_pc; cc->get_pc = hexagon_cpu_get_pc; From 91231d99acec93dc62a524868199801395c9f69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:00:16 +0100 Subject: [PATCH 2420/2892] target/hppa: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-9-philmd@linaro.org> --- target/hppa/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index d15f8c9c21..2a85495d02 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -131,10 +131,12 @@ static void hppa_restore_state_to_opc(CPUState *cs, env->psw_n = 0; } +#ifndef CONFIG_USER_ONLY static bool hppa_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } +#endif /* !CONFIG_USER_ONLY */ static int hppa_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -242,6 +244,7 @@ static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps hppa_sysemu_ops = { + .has_work = hppa_cpu_has_work, .get_phys_page_debug = hppa_cpu_get_phys_page_debug, }; #endif @@ -278,7 +281,6 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_phases); cc->class_by_name = hppa_cpu_class_by_name; - cc->has_work = hppa_cpu_has_work; cc->mmu_index = hppa_cpu_mmu_index; cc->dump_state = hppa_cpu_dump_state; cc->set_pc = hppa_cpu_set_pc; From f0bef005712d55463904760533a12480880d9a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:02:29 +0100 Subject: [PATCH 2421/2892] target/i386: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move has_work() from CPUClass to SysemuCPUOps, restrict x86_cpu_pending_interrupt() to system. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-10-philmd@linaro.org> --- target/i386/cpu.c | 8 +++----- target/i386/cpu.h | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b3e1c2bca4..1b64ceaaba 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8604,16 +8604,15 @@ static vaddr x86_cpu_get_pc(CPUState *cs) return cpu->env.eip + cpu->env.segs[R_CS].base; } +#if !defined(CONFIG_USER_ONLY) int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; -#if !defined(CONFIG_USER_ONLY) if (interrupt_request & CPU_INTERRUPT_POLL) { return CPU_INTERRUPT_POLL; } -#endif if (interrupt_request & CPU_INTERRUPT_SIPI) { return CPU_INTERRUPT_SIPI; } @@ -8634,14 +8633,12 @@ int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) (env->eflags & IF_MASK && !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { return CPU_INTERRUPT_HARD; -#if !defined(CONFIG_USER_ONLY) } else if (env->hflags2 & HF2_VGIF_MASK) { if((interrupt_request & CPU_INTERRUPT_VIRQ) && (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) { return CPU_INTERRUPT_VIRQ; } -#endif } } @@ -8652,6 +8649,7 @@ static bool x86_cpu_has_work(CPUState *cs) { return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0; } +#endif /* !CONFIG_USER_ONLY */ int x86_mmu_index_pl(CPUX86State *env, unsigned pl) { @@ -8893,6 +8891,7 @@ static const Property x86_cpu_properties[] = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps i386_sysemu_ops = { + .has_work = x86_cpu_has_work, .get_memory_mapping = x86_cpu_get_memory_mapping, .get_paging_enabled = x86_cpu_get_paging_enabled, .get_phys_page_attrs_debug = x86_cpu_get_phys_page_attrs_debug, @@ -8926,7 +8925,6 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->class_by_name = x86_cpu_class_by_name; cc->parse_features = x86_cpu_parse_featurestr; - cc->has_work = x86_cpu_has_work; cc->mmu_index = x86_cpu_mmu_index; cc->dump_state = x86_cpu_dump_state; cc->set_pc = x86_cpu_set_pc; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7882b63b9b..76f24446a5 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2349,8 +2349,6 @@ struct X86CPUClass { extern const VMStateDescription vmstate_x86_cpu; #endif -int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); - int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, int cpuid, DumpState *s); int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, @@ -2373,6 +2371,8 @@ void x86_cpu_list(void); int cpu_x86_support_mca_broadcast(CPUX86State *env); #ifndef CONFIG_USER_ONLY +int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); + hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); int cpu_get_pic_interrupt(CPUX86State *s); From 87969d6681ac2837cd6c8ce1724a305011f770d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:03:30 +0100 Subject: [PATCH 2422/2892] target/loongarch: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-11-philmd@linaro.org> --- target/loongarch/cpu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 49f603149d..ea1665e270 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -350,11 +350,9 @@ static void loongarch_restore_state_to_opc(CPUState *cs, } #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY static bool loongarch_cpu_has_work(CPUState *cs) { -#ifdef CONFIG_USER_ONLY - return true; -#else bool has_work = false; if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && @@ -363,8 +361,8 @@ static bool loongarch_cpu_has_work(CPUState *cs) } return has_work; -#endif } +#endif /* !CONFIG_USER_ONLY */ static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -885,6 +883,7 @@ static const TCGCPUOps loongarch_tcg_ops = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps loongarch_sysemu_ops = { + .has_work = loongarch_cpu_has_work, .write_elf64_note = loongarch_cpu_write_elf64_note, .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, }; @@ -920,7 +919,6 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) &lacc->parent_phases); cc->class_by_name = loongarch_cpu_class_by_name; - cc->has_work = loongarch_cpu_has_work; cc->mmu_index = loongarch_cpu_mmu_index; cc->dump_state = loongarch_cpu_dump_state; cc->set_pc = loongarch_cpu_set_pc; From 4a119cfc6cd7affc07d4b76c1340cf96b6ff0268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:03:41 +0100 Subject: [PATCH 2423/2892] target/m68k: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-12-philmd@linaro.org> --- target/m68k/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index df8b9c53fc..0065e1c1ca 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -51,10 +51,12 @@ static void m68k_restore_state_to_opc(CPUState *cs, } } +#ifndef CONFIG_USER_ONLY static bool m68k_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +#endif /* !CONFIG_USER_ONLY */ static int m68k_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -579,6 +581,7 @@ static const VMStateDescription vmstate_m68k_cpu = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps m68k_sysemu_ops = { + .has_work = m68k_cpu_has_work, .get_phys_page_debug = m68k_cpu_get_phys_page_debug, }; #endif /* !CONFIG_USER_ONLY */ @@ -612,7 +615,6 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = m68k_cpu_class_by_name; - cc->has_work = m68k_cpu_has_work; cc->mmu_index = m68k_cpu_mmu_index; cc->dump_state = m68k_cpu_dump_state; cc->set_pc = m68k_cpu_set_pc; From 55f29126b6f980f759e0bdab2e83361b98232305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:03:51 +0100 Subject: [PATCH 2424/2892] target/microblaze: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-13-philmd@linaro.org> --- target/microblaze/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index d5ee1244ca..f3bebea856 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -115,10 +115,12 @@ static void mb_restore_state_to_opc(CPUState *cs, cpu->env.iflags = data[1]; } +#ifndef CONFIG_USER_ONLY static bool mb_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } +#endif /* !CONFIG_USER_ONLY */ static int mb_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -417,6 +419,7 @@ static ObjectClass *mb_cpu_class_by_name(const char *cpu_model) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps mb_sysemu_ops = { + .has_work = mb_cpu_has_work, .get_phys_page_attrs_debug = mb_cpu_get_phys_page_attrs_debug, }; #endif @@ -452,7 +455,6 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) &mcc->parent_phases); cc->class_by_name = mb_cpu_class_by_name; - cc->has_work = mb_cpu_has_work; cc->mmu_index = mb_cpu_mmu_index; cc->dump_state = mb_cpu_dump_state; cc->set_pc = mb_cpu_set_pc; From 85edafe385f538ce3a84ed478faea12e28ab1720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:04:01 +0100 Subject: [PATCH 2425/2892] target/mips: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move has_work() from CPUClass to SysemuCPUOps and cpu_mips_hw_interrupts_enabled() to system. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-14-philmd@linaro.org> --- target/mips/cpu.c | 4 +++- target/mips/internal.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index e76298699a..b207106dd7 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -132,6 +132,7 @@ static vaddr mips_cpu_get_pc(CPUState *cs) return cpu->env.active_tc.PC; } +#if !defined(CONFIG_USER_ONLY) static bool mips_cpu_has_work(CPUState *cs) { CPUMIPSState *env = cpu_env(cs); @@ -177,6 +178,7 @@ static bool mips_cpu_has_work(CPUState *cs) } return has_work; } +#endif /* !CONFIG_USER_ONLY */ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) { @@ -534,6 +536,7 @@ static ObjectClass *mips_cpu_class_by_name(const char *cpu_model) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps mips_sysemu_ops = { + .has_work = mips_cpu_has_work, .get_phys_page_debug = mips_cpu_get_phys_page_debug, .legacy_vmsd = &vmstate_mips_cpu, }; @@ -577,7 +580,6 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = mips_cpu_class_by_name; - cc->has_work = mips_cpu_has_work; cc->mmu_index = mips_cpu_mmu_index; cc->dump_state = mips_cpu_dump_state; cc->set_pc = mips_cpu_set_pc; diff --git a/target/mips/internal.h b/target/mips/internal.h index 91c786cff8..28eb28936b 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -162,8 +162,6 @@ void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val); extern const VMStateDescription vmstate_mips_cpu; -#endif /* !CONFIG_USER_ONLY */ - static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env) { return (env->CP0_Status & (1 << CP0St_IE)) && @@ -206,6 +204,8 @@ static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env) return r; } +#endif /* !CONFIG_USER_ONLY */ + void msa_reset(CPUMIPSState *env); /* cp0_timer.c */ From 6a2b2943145c1dcb44091afa993de6d01824190b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:04:11 +0100 Subject: [PATCH 2426/2892] target/openrisc: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-15-philmd@linaro.org> --- target/openrisc/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index e8c357ae83..e8abf1f8b5 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -63,11 +63,13 @@ static void openrisc_restore_state_to_opc(CPUState *cs, } } +#ifndef CONFIG_USER_ONLY static bool openrisc_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER); } +#endif /* !CONFIG_USER_ONLY */ static int openrisc_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -233,6 +235,7 @@ static void openrisc_any_initfn(Object *obj) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps openrisc_sysemu_ops = { + .has_work = openrisc_cpu_has_work, .get_phys_page_debug = openrisc_cpu_get_phys_page_debug, }; #endif @@ -266,7 +269,6 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) &occ->parent_phases); cc->class_by_name = openrisc_cpu_class_by_name; - cc->has_work = openrisc_cpu_has_work; cc->mmu_index = openrisc_cpu_mmu_index; cc->dump_state = openrisc_cpu_dump_state; cc->set_pc = openrisc_cpu_set_pc; From 71e950afe2b9cc43d1c4186c40c1aa0dced1077d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:04:18 +0100 Subject: [PATCH 2427/2892] target/ppc: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-16-philmd@linaro.org> --- target/ppc/cpu_init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index b9772c53ec..1780cabfc6 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7177,10 +7177,12 @@ static void ppc_restore_state_to_opc(CPUState *cs, } #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY static bool ppc_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +#endif /* !CONFIG_USER_ONLY */ static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -7423,6 +7425,7 @@ static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps ppc_sysemu_ops = { + .has_work = ppc_cpu_has_work, .get_phys_page_debug = ppc_cpu_get_phys_page_debug, .write_elf32_note = ppc32_cpu_write_elf32_note, .write_elf64_note = ppc64_cpu_write_elf64_note, @@ -7474,7 +7477,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) &pcc->parent_phases); cc->class_by_name = ppc_cpu_class_by_name; - cc->has_work = ppc_cpu_has_work; cc->mmu_index = ppc_cpu_mmu_index; cc->dump_state = ppc_cpu_dump_state; cc->set_pc = ppc_cpu_set_pc; From 3810e17173c3c1848f44c4fccf0160477a399dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:06:54 +0100 Subject: [PATCH 2428/2892] target/riscv: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-17-philmd@linaro.org> --- target/riscv/cpu.c | 8 +++----- target/riscv/internals.h | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1c000c30f8..09ded6829a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1006,9 +1006,9 @@ static vaddr riscv_cpu_get_pc(CPUState *cs) return env->pc; } +#ifndef CONFIG_USER_ONLY bool riscv_cpu_has_work(CPUState *cs) { -#ifndef CONFIG_USER_ONLY RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; /* @@ -1018,10 +1018,8 @@ bool riscv_cpu_has_work(CPUState *cs) return riscv_cpu_all_pending(env) != 0 || riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; -#else - return true; -#endif } +#endif /* !CONFIG_USER_ONLY */ static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -3029,6 +3027,7 @@ static int64_t riscv_get_arch_id(CPUState *cs) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps riscv_sysemu_ops = { + .has_work = riscv_cpu_has_work, .get_phys_page_debug = riscv_cpu_get_phys_page_debug, .write_elf64_note = riscv_cpu_write_elf64_note, .write_elf32_note = riscv_cpu_write_elf32_note, @@ -3050,7 +3049,6 @@ static void riscv_cpu_common_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = riscv_cpu_class_by_name; - cc->has_work = riscv_cpu_has_work; cc->mmu_index = riscv_cpu_mmu_index; cc->dump_state = riscv_cpu_dump_state; cc->set_pc = riscv_cpu_set_pc; diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 67291933f8..213aff31d8 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -142,8 +142,10 @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) } } -/* Our implementation of CPUClass::has_work */ +#ifndef CONFIG_USER_ONLY +/* Our implementation of SysemuCPUOps::has_work */ bool riscv_cpu_has_work(CPUState *cs); +#endif /* Zjpm addr masking routine */ static inline target_ulong adjust_addr_body(CPURISCVState *env, From 52df41e3536865ee3bea1bd22426b3cb84186165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:07:26 +0100 Subject: [PATCH 2429/2892] target/rx: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-18-philmd@linaro.org> --- target/rx/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index f01e069a90..0ba0d55ab5 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -196,6 +196,7 @@ static void rx_cpu_init(Object *obj) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps rx_sysemu_ops = { + .has_work = rx_cpu_has_work, .get_phys_page_debug = rx_cpu_get_phys_page_debug, }; @@ -226,7 +227,6 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) &rcc->parent_phases); cc->class_by_name = rx_cpu_class_by_name; - cc->has_work = rx_cpu_has_work; cc->mmu_index = riscv_cpu_mmu_index; cc->dump_state = rx_cpu_dump_state; cc->set_pc = rx_cpu_set_pc; From 0df9781074cab07dcb526b6e83bd0570eab3afe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:08:53 +0100 Subject: [PATCH 2430/2892] target/s390x: Restrict I/O handler installers to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-19-philmd@linaro.org> --- target/s390x/s390x-internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index a750e7a343..6e2c98de97 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -356,6 +356,7 @@ void cpu_inject_stop(S390CPU *cpu); /* ioinst.c */ +#ifndef CONFIG_USER_ONLY void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); @@ -373,6 +374,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra); +#endif /* CONFIG_USER_ONLY */ /* mem_helper.c */ From f54c047e866802aa9c82f226dbb6b2542c4ff245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:07:44 +0100 Subject: [PATCH 2431/2892] target/s390x: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move has_work() from CPUClass to SysemuCPUOps, move s390_cpu_has_work() to cpu-system.c so it is only build for system emulation binaries, restrict functions not used anymore on user emulation in interrupt.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-20-philmd@linaro.org> --- target/s390x/cpu-system.c | 18 ++++++++++++++++++ target/s390x/cpu.c | 18 ------------------ target/s390x/interrupt.c | 8 ++------ target/s390x/s390x-internal.h | 3 +++ 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c index e9f8e7cc72..9b380e343c 100644 --- a/target/s390x/cpu-system.c +++ b/target/s390x/cpu-system.c @@ -39,6 +39,23 @@ #include "system/tcg.h" #include "hw/core/sysemu-cpu-ops.h" +bool s390_cpu_has_work(CPUState *cs) +{ + S390CPU *cpu = S390_CPU(cs); + + /* STOPPED cpus can never wake up */ + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && + s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { + return false; + } + + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + + return s390_cpu_has_int(cpu); +} + /* S390CPUClass::load_normal() */ static void s390_cpu_load_normal(CPUState *s) { @@ -158,6 +175,7 @@ void s390_cpu_finalize(Object *obj) } static const struct SysemuCPUOps s390_sysemu_ops = { + .has_work = s390_cpu_has_work, .get_phys_page_debug = s390_cpu_get_phys_page_debug, .get_crash_info = s390_cpu_get_crash_info, .write_elf64_note = s390_cpu_write_elf64_note, diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 972d265478..d73142600b 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -126,23 +126,6 @@ static vaddr s390_cpu_get_pc(CPUState *cs) return cpu->env.psw.addr; } -static bool s390_cpu_has_work(CPUState *cs) -{ - S390CPU *cpu = S390_CPU(cs); - - /* STOPPED cpus can never wake up */ - if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && - s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { - return false; - } - - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { - return false; - } - - return s390_cpu_has_int(cpu); -} - static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) { return s390x_env_mmu_index(cpu_env(cs), ifetch); @@ -395,7 +378,6 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = s390_cpu_class_by_name, - cc->has_work = s390_cpu_has_work; cc->mmu_index = s390x_cpu_mmu_index; cc->dump_state = s390_cpu_dump_state; cc->query_cpu_fast = s390_query_cpu_fast; diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index d68d8955b1..4ae6e2ddea 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -30,6 +30,7 @@ void trigger_pgm_exception(CPUS390XState *env, uint32_t code) /* env->int_pgm_ilen is already set, or will be set during unwinding */ } +#if !defined(CONFIG_USER_ONLY) void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra) { if (kvm_enabled()) { @@ -41,7 +42,6 @@ void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra) } } -#if !defined(CONFIG_USER_ONLY) void cpu_inject_clock_comparator(S390CPU *cpu) { CPUS390XState *env = &cpu->env; @@ -225,11 +225,9 @@ bool s390_cpu_has_stop_int(S390CPU *cpu) return env->pending_int & INTERRUPT_STOP; } -#endif bool s390_cpu_has_int(S390CPU *cpu) { -#ifndef CONFIG_USER_ONLY if (!tcg_enabled()) { return false; } @@ -238,7 +236,5 @@ bool s390_cpu_has_int(S390CPU *cpu) s390_cpu_has_io_int(cpu) || s390_cpu_has_restart_int(cpu) || s390_cpu_has_stop_int(cpu); -#else - return false; -#endif } +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 6e2c98de97..a4ba6227ab 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -245,6 +245,7 @@ bool s390_cpu_system_realize(DeviceState *dev, Error **errp); void s390_cpu_finalize(Object *obj); void s390_cpu_system_class_init(CPUClass *cc); void s390_cpu_machine_reset_cb(void *opaque); +bool s390_cpu_has_work(CPUState *cs); #else static inline unsigned int s390_cpu_halt(S390CPU *cpu) @@ -341,6 +342,7 @@ void cpu_unmap_lowcore(LowCore *lowcore); /* interrupt.c */ void trigger_pgm_exception(CPUS390XState *env, uint32_t code); +#ifndef CONFIG_USER_ONLY void cpu_inject_clock_comparator(S390CPU *cpu); void cpu_inject_cpu_timer(S390CPU *cpu); void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr); @@ -353,6 +355,7 @@ bool s390_cpu_has_restart_int(S390CPU *cpu); bool s390_cpu_has_stop_int(S390CPU *cpu); void cpu_inject_restart(S390CPU *cpu); void cpu_inject_stop(S390CPU *cpu); +#endif /* CONFIG_USER_ONLY */ /* ioinst.c */ From 644a8119bd54b075029acf7fad7740afb4fe9e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:09:23 +0100 Subject: [PATCH 2432/2892] target/sh4: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-21-philmd@linaro.org> --- target/sh4/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index c2aaa40a03..ce84bdf539 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -82,12 +82,12 @@ static bool superh_io_recompile_replay_branch(CPUState *cs, } return false; } -#endif static bool superh_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +#endif /* !CONFIG_USER_ONLY */ static int sh4_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -254,6 +254,7 @@ static const VMStateDescription vmstate_sh_cpu = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps sh4_sysemu_ops = { + .has_work = superh_cpu_has_work, .get_phys_page_debug = superh_cpu_get_phys_page_debug, }; #endif @@ -290,7 +291,6 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = superh_cpu_class_by_name; - cc->has_work = superh_cpu_has_work; cc->mmu_index = sh4_cpu_mmu_index; cc->dump_state = superh_cpu_dump_state; cc->set_pc = superh_cpu_set_pc; From 82f0f44d62b3faacc447a68d05f3b58d3567ec5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:09:35 +0100 Subject: [PATCH 2433/2892] target/sparc: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-22-philmd@linaro.org> --- target/sparc/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index e27b1fa294..5716120117 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -777,11 +777,13 @@ static void sparc_restore_state_to_opc(CPUState *cs, } } +#ifndef CONFIG_USER_ONLY static bool sparc_cpu_has_work(CPUState *cs) { return (cs->interrupt_request & CPU_INTERRUPT_HARD) && cpu_interrupts_enabled(cpu_env(cs)); } +#endif /* !CONFIG_USER_ONLY */ static int sparc_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -988,6 +990,7 @@ static const Property sparc_cpu_properties[] = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps sparc_sysemu_ops = { + .has_work = sparc_cpu_has_work, .get_phys_page_debug = sparc_cpu_get_phys_page_debug, .legacy_vmsd = &vmstate_sparc_cpu, }; @@ -1029,7 +1032,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = sparc_cpu_class_by_name; cc->parse_features = sparc_cpu_parse_features; - cc->has_work = sparc_cpu_has_work; cc->mmu_index = sparc_cpu_mmu_index; cc->dump_state = sparc_cpu_dump_state; #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) From d55ea95f256f1300edea837136f2a3841d20c588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:10:07 +0100 Subject: [PATCH 2434/2892] target/tricore: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-23-philmd@linaro.org> --- target/tricore/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index eb794674c8..16acc4ecb9 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -165,6 +165,7 @@ static bool tricore_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps tricore_sysemu_ops = { + .has_work = tricore_cpu_has_work, .get_phys_page_debug = tricore_cpu_get_phys_page_debug, }; @@ -193,7 +194,6 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) resettable_class_set_parent_phases(rc, NULL, tricore_cpu_reset_hold, NULL, &mcc->parent_phases); cc->class_by_name = tricore_cpu_class_by_name; - cc->has_work = tricore_cpu_has_work; cc->mmu_index = tricore_cpu_mmu_index; cc->gdb_read_register = tricore_cpu_gdb_read_register; From f37799c6c1d2e8fb43f5e1f100f26c5401d9b3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Jan 2025 13:10:30 +0100 Subject: [PATCH 2435/2892] target/xtensa: Move has_work() from CPUClass to SysemuCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move has_work() from CPUClass to SysemuCPUOps, simplifying xtensa_cpu_has_work() by directly using CPU env. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250125170125.32855-24-philmd@linaro.org> --- target/xtensa/cpu.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index f9e298ace4..7663b62d01 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -63,16 +63,14 @@ static void xtensa_restore_state_to_opc(CPUState *cs, cpu->env.pc = data[0]; } +#ifndef CONFIG_USER_ONLY static bool xtensa_cpu_has_work(CPUState *cs) { -#ifndef CONFIG_USER_ONLY - XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = cpu_env(cs); - return !cpu->env.runstall && cpu->env.pending_irq_level; -#else - return true; -#endif + return !env->runstall && env->pending_irq_level; } +#endif /* !CONFIG_USER_ONLY */ static int xtensa_cpu_mmu_index(CPUState *cs, bool ifetch) { @@ -226,6 +224,7 @@ static const VMStateDescription vmstate_xtensa_cpu = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps xtensa_sysemu_ops = { + .has_work = xtensa_cpu_has_work, .get_phys_page_debug = xtensa_cpu_get_phys_page_debug, }; #endif @@ -263,7 +262,6 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) &xcc->parent_phases); cc->class_by_name = xtensa_cpu_class_by_name; - cc->has_work = xtensa_cpu_has_work; cc->mmu_index = xtensa_cpu_mmu_index; cc->dump_state = xtensa_cpu_dump_state; cc->set_pc = xtensa_cpu_set_pc; From d0a4ccae953b7482a682b9b9f8619804059ecc89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Jan 2025 08:17:59 +0100 Subject: [PATCH 2436/2892] cpus: Remove CPUClass::has_work() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All handlers have been converted to SysemuCPUOps::has_work(). Remove CPUClass::has_work along with cpu_common_has_work() and simplify cpu_has_work(), making SysemuCPUOps::has_work handler mandatory. Note, since cpu-common.c is in meson's common_ss[] source set, we must define cpu_exec_class_post_init() in cpu-target.c (which is in the specific_ss[] source set) to have CONFIG_USER_ONLY defined. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250125170125.32855-25-philmd@linaro.org> --- hw/core/cpu-common.c | 8 ++------ hw/core/cpu-system.c | 13 +++++++------ hw/core/cpu-user.c | 5 +++++ include/hw/core/cpu.h | 3 +-- include/hw/core/sysemu-cpu-ops.h | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index ba0f02e49d..9064dd24f8 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -134,11 +134,6 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) cpu_exec_reset_hold(cpu); } -static bool cpu_common_has_work(CPUState *cs) -{ - return false; -} - ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { ObjectClass *oc; @@ -259,6 +254,8 @@ static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); + cpu_exec_class_post_init(CPU_GET_CLASS(obj)); + /* cache the cpu class for the hotpath */ cpu->cc = CPU_GET_CLASS(cpu); @@ -331,7 +328,6 @@ static void cpu_common_class_init(ObjectClass *klass, void *data) k->parse_features = cpu_common_parse_features; k->get_arch_id = cpu_common_get_arch_id; - k->has_work = cpu_common_has_work; k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; set_bit(DEVICE_CATEGORY_CPU, dc->categories); diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 601335fd76..aed5076ec7 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -33,12 +33,7 @@ bool cpu_has_work(CPUState *cpu) { - if (cpu->cc->sysemu_ops->has_work) { - return cpu->cc->sysemu_ops->has_work(cpu); - } - - g_assert(cpu->cc->has_work); - return cpu->cc->has_work(cpu); + return cpu->cc->sysemu_ops->has_work(cpu); } bool cpu_paging_enabled(const CPUState *cpu) @@ -188,6 +183,12 @@ void cpu_class_init_props(DeviceClass *dc) device_class_set_props(dc, cpu_system_props); } +void cpu_exec_class_post_init(CPUClass *cc) +{ + /* Check mandatory SysemuCPUOps handlers */ + g_assert(cc->sysemu_ops->has_work); +} + void cpu_exec_initfn(CPUState *cpu) { cpu->memory = get_system_memory(); diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c index 1892acdee0..7176791851 100644 --- a/hw/core/cpu-user.c +++ b/hw/core/cpu-user.c @@ -27,6 +27,11 @@ void cpu_class_init_props(DeviceClass *dc) device_class_set_props(dc, cpu_user_props); } +void cpu_exec_class_post_init(CPUClass *cc) +{ + /* nothing to do */ +} + void cpu_exec_initfn(CPUState *cpu) { /* nothing to do */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index a54dd2cf69..5d11d26556 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -104,7 +104,6 @@ struct SysemuCPUOps; * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. - * @has_work: Callback for checking if there is work to do. * @mmu_index: Callback for choosing softmmu mmu index; * may be used internally by memory_rw_debug without TCG. * @memory_rw_debug: Callback for GDB memory access. @@ -153,7 +152,6 @@ struct CPUClass { ObjectClass *(*class_by_name)(const char *cpu_model); void (*parse_features)(const char *typename, char *str, Error **errp); - bool (*has_work)(CPUState *cpu); int (*mmu_index)(CPUState *cpu, bool ifetch); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, uint8_t *buf, int len, bool is_write); @@ -1156,6 +1154,7 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); +void cpu_exec_class_post_init(CPUClass *cc); void cpu_exec_initfn(CPUState *cpu); void cpu_vmstate_register(CPUState *cpu); void cpu_vmstate_unregister(CPUState *cpu); diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h index dee8a62ca9..877892373f 100644 --- a/include/hw/core/sysemu-cpu-ops.h +++ b/include/hw/core/sysemu-cpu-ops.h @@ -19,7 +19,7 @@ typedef struct SysemuCPUOps { /** * @has_work: Callback for checking if there is work to do. */ - bool (*has_work)(CPUState *cpu); + bool (*has_work)(CPUState *cpu); /* MANDATORY NON-NULL */ /** * @get_memory_mapping: Callback for obtaining the memory mappings. */ From 8ff6ff09b9890ba390395d7510eca1025f7284df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Feb 2025 09:18:21 +0100 Subject: [PATCH 2437/2892] MAINTAINERS: Consolidate core exec/vCPU handling section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some common cpu/exec files are listed under the 'TCG CPUs' section. Move them to the generic 'Overall Guest CPU Cores' one where they belong. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250308134938.77267-1-philmd@linaro.org> --- MAINTAINERS | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7ac04f3520..618d75f087 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -152,10 +152,7 @@ Overall TCG CPUs M: Richard Henderson R: Paolo Bonzini S: Maintained -F: system/cpus.c F: system/watchpoint.c -F: cpu-common.c -F: cpu-target.c F: page-vary-target.c F: page-vary-common.c F: accel/tcg/ @@ -165,15 +162,11 @@ F: util/cacheflush.c F: scripts/decodetree.py F: docs/devel/decodetree.rst F: docs/devel/tcg* -F: include/exec/cpu*.h -F: include/exec/exec-all.h F: include/exec/tb-flush.h -F: include/exec/target_long.h F: include/exec/helper*.h F: include/exec/helper*.h.inc F: include/exec/helper-info.c.inc F: include/exec/page-protection.h -F: include/system/cpus.h F: include/system/tcg.h F: include/accel/tcg/cpu-ops.h F: host/include/*/host/cpuinfo.h @@ -497,12 +490,19 @@ Overall M: Richard Henderson R: Paolo Bonzini S: Maintained +F: include/exec/cpu*.h +F: include/exec/exec-all.h +F: include/exec/target_long.h F: include/qemu/accel.h F: include/system/accel-*.h +F: include/system/cpus.h F: include/accel/accel-cpu-target.h F: accel/accel-*.c F: accel/Makefile.objs F: accel/stubs/Makefile.objs +F: cpu-common.c +F: cpu-target.c +F: system/cpus.c Apple Silicon HVF CPUs M: Alexander Graf From db0d4017f9b9e87f962b35dd19a4912bbfcd3cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Mon, 6 Jan 2025 10:57:34 -0500 Subject: [PATCH 2438/2892] net: parameterize the removing client from nc list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change is used in later commits so we can avoid the removal of the netclient if it is delayed. No functional change intended. Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jason Wang --- net/net.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/net.c b/net/net.c index a3996d5c62..4eb78a1299 100644 --- a/net/net.c +++ b/net/net.c @@ -381,9 +381,12 @@ NetClientState *qemu_get_peer(NetClientState *nc, int queue_index) return ncs->peer; } -static void qemu_cleanup_net_client(NetClientState *nc) +static void qemu_cleanup_net_client(NetClientState *nc, + bool remove_from_net_clients) { - QTAILQ_REMOVE(&net_clients, nc, next); + if (remove_from_net_clients) { + QTAILQ_REMOVE(&net_clients, nc, next); + } if (nc->info->cleanup) { nc->info->cleanup(nc); @@ -442,14 +445,14 @@ void qemu_del_net_client(NetClientState *nc) } for (i = 0; i < queues; i++) { - qemu_cleanup_net_client(ncs[i]); + qemu_cleanup_net_client(ncs[i], true); } return; } for (i = 0; i < queues; i++) { - qemu_cleanup_net_client(ncs[i]); + qemu_cleanup_net_client(ncs[i], true); qemu_free_net_client(ncs[i]); } } @@ -474,7 +477,7 @@ void qemu_del_nic(NICState *nic) for (i = queues - 1; i >= 0; i--) { NetClientState *nc = qemu_get_subqueue(nic, i); - qemu_cleanup_net_client(nc); + qemu_cleanup_net_client(nc, true); qemu_free_net_client(nc); } From e7891c575fb294618b172119a91c892b8f4384a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Mon, 6 Jan 2025 10:57:35 -0500 Subject: [PATCH 2439/2892] net: move backend cleanup to NIC cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a0d7215e33 ("vhost-vdpa: do not cleanup the vdpa/vhost-net structures if peer nic is present") effectively delayed the backend cleanup, allowing the frontend or the guest to access it resources as long as the frontend is still visible to the guest. However it does not clean up the resources until the qemu process is over. This causes an effective leak if the device is deleted with device_del, as there is no way to close the vdpa device. This makes impossible to re-add that device to this or other QEMU instances until the first instance of QEMU is finished. Move the cleanup from qemu_cleanup to the NIC deletion and to net_cleanup. Fixes: a0d7215e33 ("vhost-vdpa: do not cleanup the vdpa/vhost-net structures if peer nic is present") Reported-by: Lei Yang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Signed-off-by: Jason Wang --- net/net.c | 33 +++++++++++++++++++++++++++------ net/vhost-vdpa.c | 8 -------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/net/net.c b/net/net.c index 4eb78a1299..39d6f28158 100644 --- a/net/net.c +++ b/net/net.c @@ -428,7 +428,13 @@ void qemu_del_net_client(NetClientState *nc) object_unparent(OBJECT(nf)); } - /* If there is a peer NIC, delete and cleanup client, but do not free. */ + /* + * If there is a peer NIC, transfer ownership to it. Delete the client + * from net_client list but do not cleanup nor free. This way NIC can + * still access to members of the backend. + * + * The cleanup and free will be done when the NIC is free. + */ if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) { NICState *nic = qemu_get_nic(nc->peer); if (nic->peer_deleted) { @@ -438,16 +444,13 @@ void qemu_del_net_client(NetClientState *nc) for (i = 0; i < queues; i++) { ncs[i]->peer->link_down = true; + QTAILQ_REMOVE(&net_clients, ncs[i], next); } if (nc->peer->info->link_status_changed) { nc->peer->info->link_status_changed(nc->peer); } - for (i = 0; i < queues; i++) { - qemu_cleanup_net_client(ncs[i], true); - } - return; } @@ -465,8 +468,12 @@ void qemu_del_nic(NICState *nic) for (i = 0; i < queues; i++) { NetClientState *nc = qemu_get_subqueue(nic, i); - /* If this is a peer NIC and peer has already been deleted, free it now. */ + /* + * If this is a peer NIC and peer has already been deleted, clean it up + * and free it now. + */ if (nic->peer_deleted) { + qemu_cleanup_net_client(nc->peer, false); qemu_free_net_client(nc->peer); } else if (nc->peer) { /* if there are RX packets pending, complete them */ @@ -1681,6 +1688,9 @@ void net_cleanup(void) * of the latest NET_CLIENT_DRIVER_NIC, and operate on *p as we walk * the list. * + * However, the NIC may have peers that trust to be clean beyond this + * point. For example, if they have been removed with device_del. + * * The 'nc' variable isn't part of the list traversal; it's purely * for convenience as too much '(*p)->' has a tendency to make the * readers' eyes bleed. @@ -1688,6 +1698,17 @@ void net_cleanup(void) while (*p) { nc = *p; if (nc->info->type == NET_CLIENT_DRIVER_NIC) { + NICState *nic = qemu_get_nic(nc); + + if (nic->peer_deleted) { + int queues = MAX(nic->conf->peers.queues, 1); + + for (int i = 0; i < queues; i++) { + nc = qemu_get_subqueue(nic, i); + qemu_cleanup_net_client(nc->peer, false); + } + } + /* Skip NET_CLIENT_DRIVER_NIC entries */ p = &QTAILQ_NEXT(nc, next); } else { diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index bd01866878..f7a54f46aa 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -224,14 +224,6 @@ static void vhost_vdpa_cleanup(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); - /* - * If a peer NIC is attached, do not cleanup anything. - * Cleanup will happen as a part of qemu_cleanup() -> net_cleanup() - * when the guest is shutting down. - */ - if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) { - return; - } munmap(s->cvq_cmd_out_buffer, vhost_vdpa_net_cvq_cmd_page_len()); munmap(s->status, vhost_vdpa_net_cvq_cmd_page_len()); if (s->vhost_net) { From 9dc64bd5a4bebdc820e7e8484cb30e02befdc774 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 20:11:22 +0900 Subject: [PATCH 2440/2892] util/iov: Do not assert offset is in iov iov_from_buf(), iov_to_buf(), iov_memset(), and iov_copy() asserts that the given offset fits in the iov while tolerating the specified number of bytes to operate with to be greater than the size of iov. This is inconsistent so remove the assertions. Asserting the offset fits in the iov makes sense if it is expected that there are other operations that process the content before the offset and the content is processed in order. Under this expectation, the offset should point to the end of bytes that are previously processed and fit in the iov. However, this expectation depends on the details of the caller, and did not hold true at least one case and required code to check iov_size(), which is added with commit 83ddb3dbba2e ("hw/net/net_tx_pkt: Fix overrun in update_sctp_checksum()"). Adding such a check is inefficient and error-prone. These functions already tolerate the specified number of bytes to operate with to be greater than the size of iov to avoid such checks so remove the assertions to tolerate invalid offset as well. They return the number of bytes they operated with so their callers can still check the returned value to ensure there are sufficient space at the given offset. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- include/qemu/iov.h | 5 +++-- util/iov.c | 5 ----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/include/qemu/iov.h b/include/qemu/iov.h index 44f9db5cee..9535673c13 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -31,7 +31,7 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); * only part of data will be copied, up to the end of the iovec. * Number of bytes actually copied will be returned, which is * min(bytes, iov_size(iov)-offset) - * `Offset' must point to the inside of iovec. + * Returns 0 when `offset' points to the outside of iovec. */ size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, size_t offset, const void *buf, size_t bytes); @@ -67,11 +67,12 @@ iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, /** * Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements, * starting at byte offset `start', to value `fillc', repeating it - * `bytes' number of times. `Offset' must point to the inside of iovec. + * `bytes' number of times. * If `bytes' is large enough, only last bytes portion of iovec, * up to the end of it, will be filled with the specified value. * Function return actual number of bytes processed, which is * min(size, iov_size(iov) - offset). + * Returns 0 when `offset' points to the outside of iovec. */ size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, size_t offset, int fillc, size_t bytes); diff --git a/util/iov.c b/util/iov.c index 7777116123..f8536f0474 100644 --- a/util/iov.c +++ b/util/iov.c @@ -37,7 +37,6 @@ size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, offset -= iov[i].iov_len; } } - assert(offset == 0); return done; } @@ -56,7 +55,6 @@ size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, offset -= iov[i].iov_len; } } - assert(offset == 0); return done; } @@ -75,7 +73,6 @@ size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, offset -= iov[i].iov_len; } } - assert(offset == 0); return done; } @@ -277,7 +274,6 @@ unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, bytes -= len; offset = 0; } - assert(offset == 0); return j; } @@ -348,7 +344,6 @@ size_t qemu_iovec_concat_iov(QEMUIOVector *dst, soffset -= src_iov[i].iov_len; } } - assert(soffset == 0); /* offset beyond end of src */ return done; } From 2938c36937559554a2408b008470c9a76cb9271a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 20:11:23 +0900 Subject: [PATCH 2441/2892] Revert "hw/net/net_tx_pkt: Fix overrun in update_sctp_checksum()" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 83ddb3dbba2ee0f1767442ae6ee665058aeb1093. The added check is no longer necessary due to a change of iov_from_buf(). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/net_tx_pkt.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index 1f79b82b77..903238dca2 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -141,10 +141,6 @@ bool net_tx_pkt_update_sctp_checksum(struct NetTxPkt *pkt) uint32_t csum = 0; struct iovec *pl_start_frag = pkt->vec + NET_TX_PKT_PL_START_FRAG; - if (iov_size(pl_start_frag, pkt->payload_frags) < 8 + sizeof(csum)) { - return false; - } - if (iov_from_buf(pl_start_frag, pkt->payload_frags, 8, &csum, sizeof(csum)) < sizeof(csum)) { return false; } From ac2ff9b840ce82cc7d5fd9ce4fd3019a434d7dc9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 8 Oct 2024 15:52:06 +0900 Subject: [PATCH 2442/2892] tap-linux: Open ipvtap and macvtap ipvtap and macvtap create a file for each interface unlike tuntap, which creates one file shared by all interfaces. Try to open a file dedicated to the interface first for ipvtap and macvtap. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- net/tap-linux.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/net/tap-linux.c b/net/tap-linux.c index 1226d5fda2..22ec2f45d2 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -45,10 +45,21 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int len = sizeof(struct virtio_net_hdr); unsigned int features; - fd = RETRY_ON_EINTR(open(PATH_NET_TUN, O_RDWR)); + + ret = if_nametoindex(ifname); + if (ret) { + g_autofree char *file = g_strdup_printf("/dev/tap%d", ret); + fd = open(file, O_RDWR); + } else { + fd = -1; + } + if (fd < 0) { - error_setg_errno(errp, errno, "could not open %s", PATH_NET_TUN); - return -1; + fd = RETRY_ON_EINTR(open(PATH_NET_TUN, O_RDWR)); + if (fd < 0) { + error_setg_errno(errp, errno, "could not open %s", PATH_NET_TUN); + return -1; + } } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; From 9a72282560fd478e69326617cbe891461375c93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:08 +0000 Subject: [PATCH 2443/2892] tests/functional: move aarch64 GPU test into own file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I want to expand the number of tests to cover a wide range of configurations. That starts with splitting off from the normal virt test from which it doesn't really share much code. We can also reduce the timeout of the original virt test now it is now longer burdened with testing the GPU. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-2-alex.bennee@linaro.org> --- MAINTAINERS | 1 + tests/functional/meson.build | 4 +- tests/functional/test_aarch64_virt.py | 71 ----------------- tests/functional/test_aarch64_virt_gpu.py | 94 +++++++++++++++++++++++ 4 files changed, 98 insertions(+), 72 deletions(-) create mode 100755 tests/functional/test_aarch64_virt_gpu.py diff --git a/MAINTAINERS b/MAINTAINERS index 618d75f087..758cc461be 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2631,6 +2631,7 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h F: docs/system/devices/virtio-gpu.rst +F: tests/functional/test_aarch64_virt_gpu.py vhost-user-blk M: Raphael Norwitz diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 5dc66c03fc..7fcc4731d5 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -18,7 +18,8 @@ test_timeouts = { 'aarch64_sbsaref_alpine' : 1200, 'aarch64_sbsaref_freebsd' : 720, 'aarch64_tuxrun' : 240, - 'aarch64_virt' : 720, + 'aarch64_virt' : 360, + 'aarch64_virt_gpu' : 480, 'acpi_bits' : 420, 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, @@ -84,6 +85,7 @@ tests_aarch64_system_thorough = [ 'aarch64_tcg_plugins', 'aarch64_tuxrun', 'aarch64_virt', + 'aarch64_virt_gpu', 'aarch64_xen', 'aarch64_xlnx_versal', 'multiprocess', diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 95f5ce8b4c..884aad7af6 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -134,77 +134,6 @@ class Aarch64VirtMachine(QemuSystemTest): self.common_aarch64_virt("virt,gic-version=2") - ASSET_VIRT_GPU_KERNEL = Asset( - 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' - 'download?path=%2F&files=' - 'Image', - '89e5099d26166204cc5ca4bb6d1a11b92c217e1f82ec67e3ba363d09157462f6') - - ASSET_VIRT_GPU_ROOTFS = Asset( - 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' - 'download?path=%2F&files=' - 'rootfs.ext4.zstd', - '792da7573f5dc2913ddb7c638151d4a6b2d028a4cb2afb38add513c1924bdad4') - - @skipIfMissingCommands('zstd') - def test_aarch64_virt_with_gpu(self): - # This tests boots with a buildroot test image that contains - # vkmark and other GPU exercising tools. We run a headless - # weston that nevertheless still exercises the virtio-gpu - # backend. - - self.set_machine('virt') - self.require_accelerator("tcg") - - kernel_path = self.ASSET_VIRT_GPU_KERNEL.fetch() - image_path = self.uncompress(self.ASSET_VIRT_GPU_ROOTFS, format="zstd") - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0 root=/dev/vda') - - self.vm.add_args("-accel", "tcg") - self.vm.add_args("-cpu", "neoverse-v1,pauth-impdef=on") - self.vm.add_args("-machine", "virt,gic-version=max", - '-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.add_args("-smp", "2", "-m", "2048") - self.vm.add_args("-device", - "virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") - self.vm.add_args("-display", "egl-headless") - self.vm.add_args("-display", "dbus,gl=on") - self.vm.add_args("-device", "virtio-blk-device,drive=hd0") - self.vm.add_args("-blockdev", - "driver=raw,file.driver=file," - "node-name=hd0,read-only=on," - f"file.filename={image_path}") - self.vm.add_args("-snapshot") - - try: - self.vm.launch() - except VMLaunchFailure as excp: - if "old virglrenderer, blob resources unsupported" in excp.output: - self.skipTest("No blob support for virtio-gpu") - elif "old virglrenderer, venus unsupported" in excp.output: - self.skipTest("No venus support for virtio-gpu") - elif "egl: no drm render node available" in excp.output: - self.skipTest("Can't access host DRM render node") - elif "'type' does not accept value 'egl-headless'" in excp.output: - self.skipTest("egl-headless support is not available") - else: - self.log.info(f"unhandled launch failure: {excp.output}") - raise excp - - self.wait_for_console_pattern('buildroot login:') - exec_command(self, 'root') - exec_command(self, 'export XDG_RUNTIME_DIR=/tmp') - exec_command_and_wait_for_pattern(self, - "weston -B headless " - "--renderer gl " - "--shell kiosk " - "-- vkmark -b:duration=1.0", - "vkmark Score") - if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py new file mode 100755 index 0000000000..32af941cd5 --- /dev/null +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# +# Functional tests for the various graphics modes we can support. +# +# Copyright (c) 2024, 2025 Linaro Ltd. +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu.machine.machine import VMLaunchFailure + +from qemu_test import Asset +from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import skipIfMissingCommands + +from qemu_test.linuxkernel import LinuxKernelTest + +class Aarch64VirtGPUMachine(LinuxKernelTest): + + ASSET_VIRT_GPU_KERNEL = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'Image', + '89e5099d26166204cc5ca4bb6d1a11b92c217e1f82ec67e3ba363d09157462f6') + + ASSET_VIRT_GPU_ROOTFS = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'rootfs.ext4.zstd', + '792da7573f5dc2913ddb7c638151d4a6b2d028a4cb2afb38add513c1924bdad4') + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_vulkan_gpu(self): + # This tests boots with a buildroot test image that contains + # vkmark and other GPU exercising tools. We run a headless + # weston that nevertheless still exercises the virtio-gpu + # backend. + + self.set_machine('virt') + self.require_accelerator("tcg") + + kernel_path = self.ASSET_VIRT_GPU_KERNEL.fetch() + image_path = self.uncompress(self.ASSET_VIRT_GPU_ROOTFS, format="zstd") + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0 root=/dev/vda') + + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "neoverse-v1,pauth-impdef=on") + self.vm.add_args("-machine", "virt,gic-version=max", + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.add_args("-smp", "2", "-m", "2048") + self.vm.add_args("-device", + "virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") + self.vm.add_args("-display", "egl-headless") + self.vm.add_args("-display", "dbus,gl=on") + self.vm.add_args("-device", "virtio-blk-device,drive=hd0") + self.vm.add_args("-blockdev", + "driver=raw,file.driver=file," + "node-name=hd0,read-only=on," + f"file.filename={image_path}") + self.vm.add_args("-snapshot") + + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "old virglrenderer, blob resources unsupported" in excp.output: + self.skipTest("No blob support for virtio-gpu") + elif "old virglrenderer, venus unsupported" in excp.output: + self.skipTest("No venus support for virtio-gpu") + elif "egl: no drm render node available" in excp.output: + self.skipTest("Can't access host DRM render node") + elif "'type' does not accept value 'egl-headless'" in excp.output: + self.skipTest("egl-headless support is not available") + else: + self.log.info(f"unhandled launch failure: {excp.output}") + raise excp + + self.wait_for_console_pattern('buildroot login:') + exec_command(self, 'root') + exec_command(self, 'export XDG_RUNTIME_DIR=/tmp') + exec_command_and_wait_for_pattern(self, + "weston -B headless " + "--renderer gl " + "--shell kiosk " + "-- vkmark -b:duration=1.0", + "vkmark Score") + +if __name__ == '__main__': + LinuxKernelTest.main() From 868c7703e052a14f9c612c38f7171581cb626f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:09 +0000 Subject: [PATCH 2444/2892] tests/functional: factor out common code in gpu test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for handling more tests split out the common machine setup details from the test specific stuff and add a helper for launching the weston test. Instead of searching for "vkmark score" we set a custom PS1 and wait for a successful completion. This ensures we capture the score in the console log which otherwise wouldn't log anything. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-3-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt_gpu.py | 38 +++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 32af941cd5..b4679c0460 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -12,7 +12,7 @@ from qemu.machine.machine import VMLaunchFailure from qemu_test import Asset -from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait from qemu_test import skipIfMissingCommands from qemu_test.linuxkernel import LinuxKernelTest @@ -31,12 +31,7 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): 'rootfs.ext4.zstd', '792da7573f5dc2913ddb7c638151d4a6b2d028a4cb2afb38add513c1924bdad4') - @skipIfMissingCommands('zstd') - def test_aarch64_virt_with_vulkan_gpu(self): - # This tests boots with a buildroot test image that contains - # vkmark and other GPU exercising tools. We run a headless - # weston that nevertheless still exercises the virtio-gpu - # backend. + def _launch_virt_gpu(self, gpu_device): self.set_machine('virt') self.require_accelerator("tcg") @@ -54,10 +49,10 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): '-kernel', kernel_path, '-append', kernel_command_line) self.vm.add_args("-smp", "2", "-m", "2048") - self.vm.add_args("-device", - "virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") + self.vm.add_args("-device", gpu_device) self.vm.add_args("-display", "egl-headless") self.vm.add_args("-display", "dbus,gl=on") + self.vm.add_args("-device", "virtio-blk-device,drive=hd0") self.vm.add_args("-blockdev", "driver=raw,file.driver=file," @@ -81,14 +76,23 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): raise excp self.wait_for_console_pattern('buildroot login:') - exec_command(self, 'root') - exec_command(self, 'export XDG_RUNTIME_DIR=/tmp') - exec_command_and_wait_for_pattern(self, - "weston -B headless " - "--renderer gl " - "--shell kiosk " - "-- vkmark -b:duration=1.0", - "vkmark Score") + ec_and_wait(self, 'root', '#') + + def _run_virt_weston_test(self, cmd): + + # make it easier to detect successful return to shell + PS1 = 'RES=[$?] # ' + OK_CMD = 'RES=[0] # ' + + ec_and_wait(self, 'export XDG_RUNTIME_DIR=/tmp', '#') + ec_and_wait(self, f"export PS1='{PS1}'", OK_CMD) + full_cmd = f"weston -B headless --renderer gl --shell kiosk -- {cmd}" + ec_and_wait(self, full_cmd, OK_CMD) + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_vulkan_gpu(self): + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") + self._run_virt_weston_test("vkmark -b:duration=1.0") if __name__ == '__main__': LinuxKernelTest.main() From 94634fbc66869b31491e95c26eab731d67c13e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:10 +0000 Subject: [PATCH 2445/2892] tests/functional: ensure we have a GPU device for tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's possible to build QEMU without support for the GL enabled GPU devices and we can catch that earlier with an explicit check. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-4-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt_gpu.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index b4679c0460..9a1ee2befc 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -91,6 +91,9 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): @skipIfMissingCommands('zstd') def test_aarch64_virt_with_vulkan_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") self._run_virt_weston_test("vkmark -b:duration=1.0") From 4e4e6986b62d0bb29282e4aeb09ca5b959b0afe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:11 +0000 Subject: [PATCH 2446/2892] tests/functional: bail early if vkmark hangs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The message: MESA-VIRTIO: debug: stuck in fence wait with iter at %d Seems to occur more often on debug builds. Rather than waiting for our long timeout to hit we might as well bail as soon as we see the message. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-5-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt_gpu.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 9a1ee2befc..eea1e8c973 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -78,7 +78,7 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): self.wait_for_console_pattern('buildroot login:') ec_and_wait(self, 'root', '#') - def _run_virt_weston_test(self, cmd): + def _run_virt_weston_test(self, cmd, fail = None): # make it easier to detect successful return to shell PS1 = 'RES=[$?] # ' @@ -87,7 +87,7 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): ec_and_wait(self, 'export XDG_RUNTIME_DIR=/tmp', '#') ec_and_wait(self, f"export PS1='{PS1}'", OK_CMD) full_cmd = f"weston -B headless --renderer gl --shell kiosk -- {cmd}" - ec_and_wait(self, full_cmd, OK_CMD) + ec_and_wait(self, full_cmd, OK_CMD, fail) @skipIfMissingCommands('zstd') def test_aarch64_virt_with_vulkan_gpu(self): @@ -95,7 +95,9 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): self.require_device('virtio-gpu-gl-pci') self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") - self._run_virt_weston_test("vkmark -b:duration=1.0") + self._run_virt_weston_test("vkmark -b:duration=1.0", + "debug: stuck in fence wait with iter at") + if __name__ == '__main__': LinuxKernelTest.main() From 9f7e493d117c852be4af529c1670c293eab063b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:12 +0000 Subject: [PATCH 2447/2892] tests/functional: skip vulkan tests with nVidia MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While running the new GPU tests it was noted that the proprietary nVidia driver barfed when run under the sanitiser: 2025-02-20 11:13:08,226: [11:13:07.782] Output 'headless' attempts EOTF mode SDR and colorimetry mode default. 2025-02-20 11:13:08,227: [11:13:07.784] Output 'headless' using color profile: stock sRGB color profile and that's the last thing it outputs. The sanitizer reports that when the framework sends the SIGTERM because of the timeout we get a write to a NULL pointer (but interesting not this time in an atexit callback): UndefinedBehaviorSanitizer:DEADLYSIGNAL ==471863==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7a18ceaafe80 bp 0x000000000000 sp 0x7ffe8e3ff6d0 T471863) ==471863==The signal is caused by a WRITE memory access. ==471863==Hint: address points to the zero page. #0 0x7a18ceaafe80 (/lib/x86_64-linux-gnu/libnvidia-eglcore.so.535.183.01+0x16afe80) (BuildId: 24b0d0b90369112e3de888a93eb8d7e00304a6db) #1 0x7a18ce9e72c0 (/lib/x86_64-linux-gnu/libnvidia-eglcore.so.535.183.01+0x15e72c0) (BuildId: 24b0d0b90369112e3de888a93eb8d7e00304a6db) #2 0x7a18ce9f11bb (/lib/x86_64-linux-gnu/libnvidia-eglcore.so.535.183.01+0x15f11bb) (BuildId: 24b0d0b90369112e3de888a93eb8d7e00304a6db) #3 0x7a18ce6dc9d1 (/lib/x86_64-linux-gnu/libnvidia-eglcore.so.535.183.01+0x12dc9d1) (BuildId: 24b0d0b90369112e3de888a93eb8d7e00304a6db) #4 0x7a18e7d15326 in vrend_renderer_create_fence /usr/src/virglrenderer-1.0.0-1ubuntu2/obj-x86_64-linux-gnu/../src/vrend_renderer.c:10883:26 #5 0x55bfb6621871 in virtio_gpu_virgl_process_cmd The #dri-devel channel confirmed: stsquad: nv driver is known to not work with venus, don't use it for testing So lets skip running the test to avoid known failures. As we now use vulkaninfo to probe we also need to handle the case where there is no Vulkan driver configured for the hardware. Reviewed-by: Thomas Huth Reported-by: Peter Maydell Cc: Dmitry Osipenko [AJB: also skip if vulkaninfo can't find environment] Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-6-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt_gpu.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index eea1e8c973..8e6f081544 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -17,6 +17,9 @@ from qemu_test import skipIfMissingCommands from qemu_test.linuxkernel import LinuxKernelTest +from re import search +from subprocess import check_output, CalledProcessError + class Aarch64VirtGPUMachine(LinuxKernelTest): ASSET_VIRT_GPU_KERNEL = Asset( @@ -72,7 +75,7 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): elif "'type' does not accept value 'egl-headless'" in excp.output: self.skipTest("egl-headless support is not available") else: - self.log.info(f"unhandled launch failure: {excp.output}") + self.log.info("unhandled launch failure: %s", excp.output) raise excp self.wait_for_console_pattern('buildroot login:') @@ -94,6 +97,15 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): self.require_device('virtio-gpu-gl-pci') + try: + vk_info = check_output(["vulkaninfo", "--summary"], + encoding="utf-8") + except CalledProcessError as excp: + self.skipTest(f"Miss-configured host Vulkan: {excp.output}") + + if search(r"driverID\s+=\s+DRIVER_ID_NVIDIA_PROPRIETARY", vk_info): + self.skipTest("Test skipped on NVIDIA proprietary driver") + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") self._run_virt_weston_test("vkmark -b:duration=1.0", "debug: stuck in fence wait with iter at") From 8233f4f26d831c72970d00164f8f58a63c73ecfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:13 +0000 Subject: [PATCH 2448/2892] tests/functional: expand tests to cover virgl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two more test modes using glmark2-wayland to exercise the OpenGL pass-through modes with virgl. Virgl can run with or without the hostmem blob support. To avoid repeating ourselves too much we make the initial pass a simple --validate pass. We might want to eventually add more directed tests and individual features later on but the glmark/vkmark tests are a good general smoke test for accelerated 3D. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-7-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt_gpu.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 8e6f081544..56a3ed3193 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -92,6 +92,28 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): full_cmd = f"weston -B headless --renderer gl --shell kiosk -- {cmd}" ec_and_wait(self, full_cmd, OK_CMD, fail) + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_virgl_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + self._launch_virt_gpu("virtio-gpu-gl-pci") + + # subset of the glmark tests + tests = " ".join([f"-b {test}" for test in + ["build", "texture", "shading", + "bump", "desktop", "buffer"]]) + + self._run_virt_weston_test("glmark2-wayland --validate " + tests) + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_virgl_blobs_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on") + self._run_virt_weston_test("glmark2-wayland -b:duration=1.0") + @skipIfMissingCommands('zstd') def test_aarch64_virt_with_vulkan_gpu(self): From d69178d3705994c1e4f7f919dfb6dd31b8b11d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:14 +0000 Subject: [PATCH 2449/2892] tests/functional: update the aarch64_virg_gpu images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update to the most recent aarch64_virt_gpu image. The principle differences are: - target a v8.0 baseline CPU - latest vkmark (2025.1) - actually uses the rootfs (previously was initrd) - rootfs includes more testing tools for interactive use See README.md in https://fileserver.linaro.org/s/ce5jXBFinPxtEdx for details about the image creation and the buildroot config. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-8-alex.bennee@linaro.org> --- tests/functional/test_aarch64_virt_gpu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 56a3ed3193..f19a47f8b6 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -25,14 +25,14 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): ASSET_VIRT_GPU_KERNEL = Asset( 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' 'download?path=%2F&files=' - 'Image', - '89e5099d26166204cc5ca4bb6d1a11b92c217e1f82ec67e3ba363d09157462f6') + 'Image.6.12.16.aarch64', + '7888c51c55d37e86bbbdeb5acea9f08c34e6b0f03c1f5b2463285f6a6f6eec8b') ASSET_VIRT_GPU_ROOTFS = Asset( 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' 'download?path=%2F&files=' - 'rootfs.ext4.zstd', - '792da7573f5dc2913ddb7c638151d4a6b2d028a4cb2afb38add513c1924bdad4') + 'rootfs.aarch64.ext2.zstd', + 'd45118c899420b7e673f1539a37a35480134b3e36e3a59e2cb69b1781cbb14ef') def _launch_virt_gpu(self, gpu_device): @@ -47,7 +47,7 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): 'console=ttyAMA0 root=/dev/vda') self.vm.add_args("-accel", "tcg") - self.vm.add_args("-cpu", "neoverse-v1,pauth-impdef=on") + self.vm.add_args("-cpu", "cortex-a72") self.vm.add_args("-machine", "virt,gic-version=max", '-kernel', kernel_path, '-append', kernel_command_line) From 892b06c40e46656b07579b9f4fb7a8f2652cacf2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 4 Mar 2025 22:24:15 +0000 Subject: [PATCH 2450/2892] plugins: add explicit dependency in functional tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ./tests/functional/test_aarch64_tcg_plugins.py needs to have plugin libinsn built. However, it's not listed as a dependency, so meson can't know it needs to be built. Thus, we keep track of all plugins, and add them as an explicit dependency. Fixes: 4c134d07b9e ("tests: add a new set of tests to exercise plugins") Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-9-alex.bennee@linaro.org> --- contrib/plugins/meson.build | 2 ++ meson.build | 1 + tests/functional/meson.build | 2 +- tests/tcg/plugins/meson.build | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index 484b9a808c..fa8a426c8b 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -26,3 +26,5 @@ if t.length() > 0 else run_target('contrib-plugins', command: find_program('true')) endif + +plugin_modules += t diff --git a/meson.build b/meson.build index 4899d896de..9d9c11731f 100644 --- a/meson.build +++ b/meson.build @@ -3668,6 +3668,7 @@ qtest_module_ss = ss.source_set() modules = {} target_modules = {} +plugin_modules = [] hw_arch = {} target_arch = {} target_system_arch = {} diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7fcc4731d5..e78560a901 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -385,7 +385,7 @@ foreach speed : ['quick', 'thorough'] # 'run_target' logic below & in Makefile.include test('func-' + testname, python, - depends: [test_deps, test_emulator, emulator_modules], + depends: [test_deps, test_emulator, emulator_modules, plugin_modules], env: test_env, args: [testpath], protocol: 'tap', diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index 87a17d67bd..c8cb0626a6 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -19,3 +19,5 @@ if t.length() > 0 else run_target('test-plugins', command: find_program('true')) endif + +plugin_modules += t From 3a7b9054b8d761e61628e67e90b118d05b4a4697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Mar 2025 22:24:16 +0000 Subject: [PATCH 2451/2892] tests/functional: Introduce the dso_suffix() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a helper to get the default shared library suffix used on the host. Suggested-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Thomas Huth Message-Id: <20250220080215.49165-3-philmd@linaro.org> [AJB: dropped whitespace cmd.py damage] Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-10-alex.bennee@linaro.org> --- tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/config.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 5c972843a6..45f7befa37 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -7,7 +7,7 @@ from .asset import Asset -from .config import BUILD_DIR +from .config import BUILD_DIR, dso_suffix from .cmd import is_readable_executable_file, \ interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which diff --git a/tests/functional/qemu_test/config.py b/tests/functional/qemu_test/config.py index edd75b7fd0..6d4c9c3ce1 100644 --- a/tests/functional/qemu_test/config.py +++ b/tests/functional/qemu_test/config.py @@ -13,6 +13,7 @@ import os from pathlib import Path +import platform def _source_dir(): @@ -34,3 +35,14 @@ def _build_dir(): raise Exception("Cannot identify build dir, set QEMU_BUILD_ROOT") BUILD_DIR = _build_dir() + +def dso_suffix(): + '''Return the dynamic libraries suffix for the current platform''' + + if platform.system() == "Darwin": + return "dylib" + + if platform.system() == "Windows": + return "dll" + + return "so" From ed557cc544b2162eec8ec977af65cdd09277d44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Mar 2025 22:24:17 +0000 Subject: [PATCH 2452/2892] tests/functional: Allow running TCG plugins tests on non-Linux/BSD hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all platforms use the '.so' suffix for shared libraries, which is how plugins are built. Use the recently introduced dso_suffix() helper to get the proper host suffix. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2804 Suggested-by: Pierrick Bouvier Suggested-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Thomas Huth Message-Id: <20250220080215.49165-4-philmd@linaro.org> [AJB: moved plugin_file into testcase.py] Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-11-alex.bennee@linaro.org> --- tests/functional/qemu_test/testcase.py | 12 +++++++++++- tests/functional/test_aarch64_tcg_plugins.py | 5 +++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 058bf270ec..50d232a7c6 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -27,7 +27,7 @@ from qemu.utils import kvm_available, tcg_available from .archive import archive_extract from .asset import Asset -from .config import BUILD_DIR +from .config import BUILD_DIR, dso_suffix from .uncompress import uncompress @@ -183,6 +183,16 @@ class QemuBaseTest(unittest.TestCase): def log_file(self, *args): return str(Path(self.outputdir, *args)) + ''' + @params plugin name + + Return the full path to the plugin taking into account any host OS + specific suffixes. + ''' + def plugin_file(self, plugin_name): + sfx = dso_suffix() + return os.path.join('tests', 'tcg', 'plugins', f'{plugin_name}.{sfx}') + def assets_available(self): for name, asset in vars(self.__class__).items(): if name.startswith("ASSET_") and type(asset) == Asset: diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py index 7e8beacc83..4ea71f5f88 100755 --- a/tests/functional/test_aarch64_tcg_plugins.py +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -13,6 +13,7 @@ import tempfile import mmap +import os import re from qemu.machine.machine import VMLaunchFailure @@ -74,7 +75,7 @@ class PluginKernelNormal(PluginKernelBase): suffix=".log") self.run_vm(kernel_path, kernel_command_line, - "tests/tcg/plugins/libinsn.so", plugin_log.name, + self.plugin_file('libinsn'), plugin_log.name, console_pattern) with plugin_log as lf, \ @@ -100,7 +101,7 @@ class PluginKernelNormal(PluginKernelBase): suffix=".log") self.run_vm(kernel_path, kernel_command_line, - "tests/tcg/plugins/libinsn.so", plugin_log.name, + self.plugin_file('libinsn'), plugin_log.name, console_pattern, args=('-icount', 'shift=1')) From 4748be5e9df56e13045c0f76fe9f60fa7655fed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:18 +0000 Subject: [PATCH 2453/2892] libvirt-ci: bump to latest for vulkan-tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The alpine baseline has also been updated in the meantime so we need to address that while we are at it. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-12-alex.bennee@linaro.org> --- .gitlab-ci.d/cirrus/freebsd-14.vars | 2 +- .gitlab-ci.d/cirrus/macos-14.vars | 2 +- scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 1 + scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 1 + tests/docker/dockerfiles/alpine.docker | 5 +++-- tests/docker/dockerfiles/centos9.docker | 1 + tests/docker/dockerfiles/debian-amd64-cross.docker | 3 ++- tests/docker/dockerfiles/debian-arm64-cross.docker | 3 ++- tests/docker/dockerfiles/debian-armhf-cross.docker | 3 ++- tests/docker/dockerfiles/debian-i686-cross.docker | 3 ++- tests/docker/dockerfiles/debian-mips64el-cross.docker | 3 ++- tests/docker/dockerfiles/debian-mipsel-cross.docker | 3 ++- tests/docker/dockerfiles/debian-ppc64el-cross.docker | 3 ++- tests/docker/dockerfiles/debian-s390x-cross.docker | 3 ++- tests/docker/dockerfiles/debian.docker | 3 ++- tests/docker/dockerfiles/fedora-rust-nightly.docker | 1 + tests/docker/dockerfiles/fedora-win64-cross.docker | 1 + tests/docker/dockerfiles/fedora.docker | 1 + tests/docker/dockerfiles/opensuse-leap.docker | 1 + tests/docker/dockerfiles/ubuntu2204.docker | 1 + tests/lcitool/libvirt-ci | 2 +- tests/lcitool/projects/qemu.yml | 1 + tests/lcitool/refresh | 2 +- tests/vm/generated/freebsd.json | 1 + 24 files changed, 35 insertions(+), 15 deletions(-) diff --git a/.gitlab-ci.d/cirrus/freebsd-14.vars b/.gitlab-ci.d/cirrus/freebsd-14.vars index 0997c47af5..19ca0d3663 100644 --- a/.gitlab-ci.d/cirrus/freebsd-14.vars +++ b/.gitlab-ci.d/cirrus/freebsd-14.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip' -PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 vulkan-tools xorriso zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-14.vars b/.gitlab-ci.d/cirrus/macos-14.vars index 25dff322e6..b039465f56 100644 --- a/.gitlab-ci.d/cirrus/macos-14.vars +++ b/.gitlab-ci.d/cirrus/macos-14.vars @@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake' NINJA='/opt/homebrew/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/opt/homebrew/bin/pip3' -PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' +PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' PYTHON='/opt/homebrew/bin/python3' diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml index 288156d1e4..dbcd2e076d 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -123,6 +123,7 @@ packages: - tar - tesseract-ocr - tesseract-ocr-eng + - vulkan-tools - xorriso - zlib1g-dev - zstd diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml index d497139ef3..4b8ee3d885 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -121,6 +121,7 @@ packages: - tar - tesseract-ocr - tesseract-ocr-eng + - vulkan-tools - xorriso - zlib1g-dev - zstd diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index f87c40fbfe..bf3bd5a30d 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all alpine-319 qemu +# $ lcitool dockerfile --layers all alpine-321 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/alpine:3.19 +FROM docker.io/library/alpine:3.21 RUN apk update && \ apk upgrade && \ @@ -111,6 +111,7 @@ RUN apk update && \ vde2-dev \ virglrenderer-dev \ vte3-dev \ + vulkan-tools \ which \ xen-dev \ xorriso \ diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index a9681c8a96..a942835a1d 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -115,6 +115,7 @@ RUN dnf distro-sync -y && \ usbredir-devel \ util-linux \ vte291-devel \ + vulkan-tools \ which \ xorriso \ zlib-devel \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 644fd3734d..0535585428 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -135,7 +136,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:amd64 \ libspice-protocol-dev:amd64 \ libspice-server-dev:amd64 \ - libssh-gcrypt-dev:amd64 \ + libssh-dev:amd64 \ libsystemd-dev:amd64 \ libtasn1-6-dev:amd64 \ libubsan1:amd64 \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 060da53796..6b1e4fc827 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -134,7 +135,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:arm64 \ libspice-protocol-dev:arm64 \ libspice-server-dev:arm64 \ - libssh-gcrypt-dev:arm64 \ + libssh-dev:arm64 \ libsystemd-dev:arm64 \ libtasn1-6-dev:arm64 \ libubsan1:arm64 \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index a481fc9695..cf0fe63af9 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -134,7 +135,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:armhf \ libspice-protocol-dev:armhf \ libspice-server-dev:armhf \ - libssh-gcrypt-dev:armhf \ + libssh-dev:armhf \ libsystemd-dev:armhf \ libtasn1-6-dev:armhf \ libubsan1:armhf \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 61bc361e85..1c84dfb945 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -134,7 +135,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:i386 \ libspice-protocol-dev:i386 \ libspice-server-dev:i386 \ - libssh-gcrypt-dev:i386 \ + libssh-dev:i386 \ libsystemd-dev:i386 \ libtasn1-6-dev:i386 \ libubsan1:i386 \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 9f6c4763c5..257204eae4 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -133,7 +134,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:mips64el \ libspice-protocol-dev:mips64el \ libspice-server-dev:mips64el \ - libssh-gcrypt-dev:mips64el \ + libssh-dev:mips64el \ libsystemd-dev:mips64el \ libtasn1-6-dev:mips64el \ libudev-dev:mips64el \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 2e979111e0..395c84d65b 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -133,7 +134,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:mipsel \ libspice-protocol-dev:mipsel \ libspice-server-dev:mipsel \ - libssh-gcrypt-dev:mipsel \ + libssh-dev:mipsel \ libsystemd-dev:mipsel \ libtasn1-6-dev:mipsel \ libudev-dev:mipsel \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 8ee450dba0..1ae227ccde 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -134,7 +135,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev:ppc64el \ libspice-protocol-dev:ppc64el \ libspice-server-dev:ppc64el \ - libssh-gcrypt-dev:ppc64el \ + libssh-dev:ppc64el \ libsystemd-dev:ppc64el \ libtasn1-6-dev:ppc64el \ libubsan1:ppc64el \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index f451a07c4c..afa81a57ba 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -58,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -133,7 +134,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsnappy-dev:s390x \ libsndio-dev:s390x \ libspice-protocol-dev:s390x \ - libssh-gcrypt-dev:s390x \ + libssh-dev:s390x \ libsystemd-dev:s390x \ libtasn1-6-dev:s390x \ libubsan1:s390x \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 505330a9e2..5b3bac43cc 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -87,7 +87,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev \ libspice-protocol-dev \ libspice-server-dev \ - libssh-gcrypt-dev \ + libssh-dev \ libsystemd-dev \ libtasn1-6-dev \ libubsan1 \ @@ -131,6 +131,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zlib1g-dev \ zstd && \ diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index a8e4fb279a..fe4a6ed48d 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -132,6 +132,7 @@ exec "$@"\n' > /usr/bin/nosync && \ util-linux \ virglrenderer-devel \ vte291-devel \ + vulkan-tools \ which \ xen-devel \ xorriso \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 7dc3eb03f5..a950344402 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -61,6 +61,7 @@ exec "$@"\n' > /usr/bin/nosync && \ tesseract \ tesseract-langpack-eng \ util-linux \ + vulkan-tools \ which \ xorriso \ zstd && \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index b64399af66..014e3ccf17 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -132,6 +132,7 @@ exec "$@"\n' > /usr/bin/nosync && \ util-linux \ virglrenderer-devel \ vte291-devel \ + vulkan-tools \ which \ xen-devel \ xorriso \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 4d5fb3e3a1..e90225dc23 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -115,6 +115,7 @@ RUN zypper update -y && \ util-linux \ virglrenderer-devel \ vte-devel \ + vulkan-tools \ which \ xen-devel \ xorriso \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index e1b70b536d..88ce4ef9a9 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -130,6 +130,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zlib1g-dev \ zstd && \ diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index b6a65806bc..18c4bfe02c 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit b6a65806bc9b2b56985f5e97c936b77c7e7a99fc +Subproject commit 18c4bfe02c467e5639bf9a687139735ccd7a3fff diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 80bcac0902..c07242f272 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -122,6 +122,7 @@ packages: - usbredir - virglrenderer - vte + - vulkan-tools - which - xen - xorriso diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 53f8d2585f..aa551aca9b 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -163,7 +163,7 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-319") + generate_dockerfile("alpine", "alpine-321") generate_dockerfile("centos9", "centos-stream-9") generate_dockerfile("debian", "debian-12", trailer="".join(debian12_extras)) diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 81fc38d798..c03e1cd586 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -73,6 +73,7 @@ "usbredir", "virglrenderer", "vte3", + "vulkan-tools", "xorriso", "zstd" ], From 830bbfb965274c2ac577420d28c7adecf8a39fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:19 +0000 Subject: [PATCH 2454/2892] tests/vm: bump timeout for shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On my fairly beefy machine the timeout was triggering leaving a corrupted disk image due to power being pulled before the disk had synced. Triple the timeout to avoid this. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-13-alex.bennee@linaro.org> --- tests/vm/basevm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 6d41ac7574..9e879e966a 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -83,7 +83,7 @@ class BaseVM(object): # command to halt the guest, can be overridden by subclasses poweroff = "poweroff" # Time to wait for shutdown to finish. - shutdown_timeout_default = 30 + shutdown_timeout_default = 90 # enable IPv6 networking ipv6 = True # This is the timeout on the wait for console bytes. From 33629b82aeadb070a803e5887d3a5de59b1c95c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:20 +0000 Subject: [PATCH 2455/2892] tests/tcg: mark test-vma as a linux-only test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main multiarch tests should compile for any POSIX system, however test-vma's usage of MAP_NORESERVE makes it a linux-only test. Simply moving the source file is enough for the build logic to skip on BSD's. Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-14-alex.bennee@linaro.org> --- tests/tcg/multiarch/{ => linux}/test-vma.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/tcg/multiarch/{ => linux}/test-vma.c (100%) diff --git a/tests/tcg/multiarch/test-vma.c b/tests/tcg/multiarch/linux/test-vma.c similarity index 100% rename from tests/tcg/multiarch/test-vma.c rename to tests/tcg/multiarch/linux/test-vma.c From 0cee7cfc2495c2c7d108c38a9b07162af78553b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:21 +0000 Subject: [PATCH 2456/2892] tests/tcg: add message to _Static_assert in test-avx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for enabling clang and avoiding: error: '_Static_assert' with no message is a C2x extension [-Werror,-Wc2x-extensions] let us just add the message to silence the warning. Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-15-alex.bennee@linaro.org> --- tests/tcg/i386/test-avx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/i386/test-avx.c b/tests/tcg/i386/test-avx.c index 230e6d84b8..80fe363cfc 100644 --- a/tests/tcg/i386/test-avx.c +++ b/tests/tcg/i386/test-avx.c @@ -244,7 +244,7 @@ v4di indexd = {0x00000002ffffffcdull, 0xfffffff500000010ull, 0x0000003afffffff0ull, 0x000000000000000eull}; v4di gather_mem[0x20]; -_Static_assert(sizeof(gather_mem) == 1024); +_Static_assert(sizeof(gather_mem) == 1024, "gather_mem not expected size"); void init_f16reg(v4di *r) { From 77f005f954ea3cd0a161618a9f6c3f9d874c5b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:22 +0000 Subject: [PATCH 2457/2892] tests/tcg: fix constraints in test-i386-adcox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang complains: clang -O2 -m64 -mcx16 /home/alex/lsrc/qemu.git/tests/tcg/i386/test-i386-adcox.c -o test-i386-adcox -static /home/alex/lsrc/qemu.git/tests/tcg/i386/test-i386-adcox.c:32:26: error: invalid input constraint '0' in asm : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); ^ /home/alex/lsrc/qemu.git/tests/tcg/i386/test-i386-adcox.c:57:26: error: invalid input constraint '0' in asm : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); ^ 2 errors generated. Pointing out a numbered input constraint can't point to a read/write output [1]. Convert to a read-only input constraint to allow this. [1] https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20101101/036036.html Suggested-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-16-alex.bennee@linaro.org> --- tests/tcg/i386/test-i386-adcox.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tcg/i386/test-i386-adcox.c b/tests/tcg/i386/test-i386-adcox.c index 16169efff8..a717064adb 100644 --- a/tests/tcg/i386/test-i386-adcox.c +++ b/tests/tcg/i386/test-i386-adcox.c @@ -29,7 +29,7 @@ void test_adox_adcx(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_ope "adcx %3, %1;" "pushf; pop %0" : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) - : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + : "r" ((REG) - 1), "0" (flags), "1" (out_adcx), "2" (out_adox)); assert(out_adcx == in_c + adcx_operand - 1); assert(out_adox == in_o + adox_operand - 1); @@ -53,8 +53,8 @@ void test_adcx_adox(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_ope "adcx %3, %1;" "adox %3, %2;" "pushf; pop %0" - : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) - : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + : "+r"(flags), "+r"(out_adcx), "+r"(out_adox) + : "r" ((REG)-1)); assert(out_adcx == in_c + adcx_operand - 1); assert(out_adox == in_o + adox_operand - 1); From 874c712dc148142dfdaf2589d66a17391fe331f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:23 +0000 Subject: [PATCH 2458/2892] tests/tcg: enable -fwrapv for test-i386-bmi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We allow things like: tests/tcg/i386/test-i386-bmi2.c:124:35: warning: shifting a negative signed value is undefined [-Wshift-negative-value] assert(result == (mask & ~(-1 << 30))); in the main code, so allow it for the test. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-17-alex.bennee@linaro.org> --- tests/tcg/i386/Makefile.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index bbe2c44b2a..f1df40411b 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -22,7 +22,7 @@ run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max test-i386-pcmpistri: CFLAGS += -msse4.2 run-test-i386-pcmpistri: QEMU_OPTS += -cpu max -test-i386-bmi2: CFLAGS=-O2 +test-i386-bmi2: CFLAGS=-O2 -fwrapv run-test-i386-bmi2: QEMU_OPTS += -cpu max test-i386-adcox: CFLAGS=-O2 From c05aec9d4a9cc0ab6615efad209acff6c750c40a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 4 Mar 2025 22:24:24 +0000 Subject: [PATCH 2459/2892] tests/tcg: Suppress compiler false-positive warning on sha1.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC versions at least 12 through 15 incorrectly report a warning about code in sha1.c: tests/tcg/multiarch/sha1.c:161:13: warning: ‘SHA1Transform’ reading 64 bytes from a region of size 0 [-Wstringop-overread] 161 | SHA1Transform(context->state, &data[i]); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a piece of stock library code for doing SHA1 which we've simply copied, rather than writing ourselves. The bug has been reported to upstream GCC (about a different library's use of this code): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106709 For our test case, since this isn't our original code and there isn't actually a bug in it, suppress the incorrect warning rather than trying to modify the code to work around the compiler issue. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2328 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20250227141343.1675415-1-peter.maydell@linaro.org> [AJB: -Wno-unknown-warning-option for clang's sake] Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-18-alex.bennee@linaro.org> --- tests/tcg/aarch64/Makefile.target | 3 ++- tests/tcg/arm/Makefile.target | 3 ++- tests/tcg/hexagon/Makefile.target | 2 +- tests/tcg/multiarch/Makefile.target | 8 ++++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 9efe2f81ad..16ddcf4f88 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -83,7 +83,8 @@ test-aes: CFLAGS += -O -march=armv8-a+aes test-aes: test-aes-main.c.inc # Vector SHA1 -sha1-vector: CFLAGS=-O3 +# Work around compiler false-positive warning, as we do for the 'sha1' test +sha1-vector: CFLAGS=-O3 -Wno-stringop-overread sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 99a953b667..6189d7a0e2 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -61,7 +61,8 @@ endif ARM_TESTS += commpage # Vector SHA1 -sha1-vector: CFLAGS=-O3 +# Work around compiler false-positive warning, as we do for the 'sha1' test +sha1-vector: CFLAGS=-O3 -Wno-stringop-overread sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index e5182c01d8..4dfc39bc98 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -18,7 +18,7 @@ # Hexagon doesn't support gdb, so skip the EXTRA_RUNS EXTRA_RUNS = -CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal +CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal -Wno-unknown-warning-option CFLAGS += -fno-unroll-loops -fno-stack-protector HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 688a6be203..c769a7d69d 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -45,6 +45,14 @@ vma-pthread: LDFLAGS+=-pthread sigreturn-sigmask: CFLAGS+=-pthread sigreturn-sigmask: LDFLAGS+=-pthread +# GCC versions 12/13/14/15 at least incorrectly complain about +# "'SHA1Transform' reading 64 bytes from a region of size 0"; see the gcc bug +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106709 +# Since this is just a standard piece of library code we've borrowed for a +# TCG test case, suppress the warning rather than trying to modify the +# code to work around the compiler. +sha1: CFLAGS+=-Wno-stringop-overread + # The vma-pthread seems very sensitive on gitlab and we currently # don't know if its exposing a real bug or the test is flaky. ifneq ($(GITLAB_CI),) From 3df360cb03d56cecdc23abc5855ffceedfcbfa0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:25 +0000 Subject: [PATCH 2460/2892] gitlab: add a new build_unit job to track build size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to reduce the total number of build units in the system to get on our way to a single binary. It will help to have some numbers so lets add a job to gitlab to track our progress. Cc: Pierrick Bouvier Cc: Philippe Mathieu-Daudé Cc: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-19-alex.bennee@linaro.org> --- .gitlab-ci.d/check-units.py | 66 ++++++++++++++++++++++++++++++++++ .gitlab-ci.d/static_checks.yml | 22 ++++++++++++ 2 files changed, 88 insertions(+) create mode 100755 .gitlab-ci.d/check-units.py diff --git a/.gitlab-ci.d/check-units.py b/.gitlab-ci.d/check-units.py new file mode 100755 index 0000000000..268a4118d5 --- /dev/null +++ b/.gitlab-ci.d/check-units.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# check-units.py: check the number of compilation units and identify +# those that are rebuilt multiple times +# +# Copyright (C) 2025 Linaro Ltd. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from os import access, R_OK, path +from sys import argv, exit +import json +from collections import Counter + + +def extract_build_units(cc_path): + """ + Extract the build units and their counds from compile_commands.json file. + + Returns: + Hash table of ["unit"] = count + """ + + j = json.load(open(cc_path, 'r')) + files = [f['file'] for f in j] + build_units = Counter(files) + + return build_units + + +def analyse_units(build_units): + """ + Analyse the build units and report stats and the top 10 rebuilds + """ + + print(f"Total source files: {len(build_units.keys())}") + print(f"Total build units: {sum(units.values())}") + + # Create a sorted list by number of rebuilds + sorted_build_units = sorted(build_units.items(), + key=lambda item: item[1], + reverse=True) + + print("Most rebuilt units:") + for unit, count in sorted_build_units[:20]: + print(f" {unit} built {count} times") + + print("Least rebuilt units:") + for unit, count in sorted_build_units[-10:]: + print(f" {unit} built {count} times") + + +if __name__ == "__main__": + if len(argv) != 2: + script_name = path.basename(argv[0]) + print(f"Usage: {script_name} ") + exit(1) + + cc_path = argv[1] + if path.isfile(cc_path) and access(cc_path, R_OK): + units = extract_build_units(cc_path) + analyse_units(units) + exit(0) + else: + print(f"{cc_path} doesn't exist or isn't readable") + exit(1) diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index c0ba453382..c3ed6de453 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -70,3 +70,25 @@ check-rust-tools-nightly: expire_in: 2 days paths: - rust/target/doc + +check-build-units: + extends: .base_job_template + stage: build + image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG + needs: + job: amd64-debian-container + before_script: + - source scripts/ci/gitlab-ci-section + - section_start setup "Install Tools" + - apt install --assume-yes --no-install-recommends jq + - section_end setup + script: + - mkdir build + - cd build + - section_start configure "Running configure" + - ../configure + - cd .. + - section_end configure + - section_start analyse "Analyse" + - .gitlab-ci.d/check-units.py build/compile_commands.json + - section_end analyse From 2a8e8544b285ace2a6f21b72e18d823738fc8167 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 4 Mar 2025 22:24:26 +0000 Subject: [PATCH 2461/2892] tests/functional: add boot error detection for RME tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was identified that those tests randomly fail with a synchronous exception at boot (reported by EDK2). While we solve this problem, report failure immediately so tests don't timeout in CI. Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250303185745.2504842-1-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-20-alex.bennee@linaro.org> --- tests/functional/test_aarch64_rme_sbsaref.py | 3 ++- tests/functional/test_aarch64_rme_virt.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py index 93bb528338..ddcc9493a6 100755 --- a/tests/functional/test_aarch64_rme_sbsaref.py +++ b/tests/functional/test_aarch64_rme_sbsaref.py @@ -60,7 +60,8 @@ class Aarch64RMESbsaRefMachine(QemuSystemTest): self.vm.launch() # Wait for host VM boot to complete. - wait_for_console_pattern(self, 'Welcome to Buildroot') + wait_for_console_pattern(self, 'Welcome to Buildroot', + failure_message='Synchronous Exception at') exec_command_and_wait_for_pattern(self, 'root', '#') test_realms_guest(self) diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py index 42b9229b4c..38e01721a4 100755 --- a/tests/functional/test_aarch64_rme_virt.py +++ b/tests/functional/test_aarch64_rme_virt.py @@ -89,7 +89,8 @@ class Aarch64RMEVirtMachine(QemuSystemTest): self.vm.launch() # Wait for host VM boot to complete. - wait_for_console_pattern(self, 'Welcome to Buildroot') + wait_for_console_pattern(self, 'Welcome to Buildroot', + failure_message='Synchronous Exception at') exec_command_and_wait_for_pattern(self, 'root', '#') test_realms_guest(self) From 9d06b0ccb1089f8713c4244109958bed8374ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:27 +0000 Subject: [PATCH 2462/2892] plugins/api: use qemu_target_page_mask() to get value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Requiring TARGET_PAGE_MASK to be defined gets in the way of building this unit once. qemu_target_page_mask() will tell us what it is. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-21-alex.bennee@linaro.org> --- plugins/api.c | 3 ++- tests/tcg/hexagon/Makefile.target | 2 +- tests/tcg/multiarch/Makefile.target | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/api.c b/plugins/api.c index cf8cdf076a..fa4d495277 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -43,6 +43,7 @@ #include "tcg/tcg.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" +#include "exec/target_page.h" #include "exec/translation-block.h" #include "exec/translator.h" #include "disas/disas.h" @@ -287,7 +288,7 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) { const DisasContextBase *db = tcg_ctx->plugin_db; - vaddr page0_last = db->pc_first | ~TARGET_PAGE_MASK; + vaddr page0_last = db->pc_first | ~qemu_target_page_mask(); if (db->fake_insn) { return NULL; diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 4dfc39bc98..e5182c01d8 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -18,7 +18,7 @@ # Hexagon doesn't support gdb, so skip the EXTRA_RUNS EXTRA_RUNS = -CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal -Wno-unknown-warning-option +CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal CFLAGS += -fno-unroll-loops -fno-stack-protector HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index c769a7d69d..45c9cfe18c 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -51,7 +51,7 @@ sigreturn-sigmask: LDFLAGS+=-pthread # Since this is just a standard piece of library code we've borrowed for a # TCG test case, suppress the warning rather than trying to modify the # code to work around the compiler. -sha1: CFLAGS+=-Wno-stringop-overread +sha1: CFLAGS+=-Wno-stringop-overread -Wno-unknown-warning-option # The vma-pthread seems very sensitive on gitlab and we currently # don't know if its exposing a real bug or the test is flaky. From 1d9a9743589e3622a93a46b593820ffb22cbda1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:28 +0000 Subject: [PATCH 2463/2892] plugins/loader: populate target_name with target_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a function we can call for this, lets not rely on macros that stop us building once. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-22-alex.bennee@linaro.org> --- plugins/loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/loader.c b/plugins/loader.c index 99686b5466..827473c8b6 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -297,7 +297,7 @@ int qemu_plugin_load_list(QemuPluginList *head, Error **errp) struct qemu_plugin_desc *desc, *next; g_autofree qemu_info_t *info = g_new0(qemu_info_t, 1); - info->target_name = TARGET_NAME; + info->target_name = target_name(); info->version.min = QEMU_PLUGIN_MIN_VERSION; info->version.cur = QEMU_PLUGIN_VERSION; #ifndef CONFIG_USER_ONLY From 4752ed52727acf7f0e798de75ec024fc92e92af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:29 +0000 Subject: [PATCH 2464/2892] include/qemu: plugin-memory.h doesn't need cpu-defs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hwaddr is a fixed size on all builds. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-23-alex.bennee@linaro.org> --- include/qemu/plugin-memory.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/qemu/plugin-memory.h b/include/qemu/plugin-memory.h index 71c1123308..6065ec7aaf 100644 --- a/include/qemu/plugin-memory.h +++ b/include/qemu/plugin-memory.h @@ -9,7 +9,6 @@ #ifndef PLUGIN_MEMORY_H #define PLUGIN_MEMORY_H -#include "exec/cpu-defs.h" #include "exec/hwaddr.h" struct qemu_plugin_hwaddr { From f85a28dedfde2d530b3fb6cf3dc12e4b67e02909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:30 +0000 Subject: [PATCH 2465/2892] plugins/api: clean-up the includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to re-factoring and clean-up work (especially to exec-all) we no longer need such broad headers for the api. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-24-alex.bennee@linaro.org> --- plugins/api.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/api.c b/plugins/api.c index fa4d495277..c3ba1e98e8 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -39,9 +39,7 @@ #include "qemu/main-loop.h" #include "qemu/plugin.h" #include "qemu/log.h" -#include "qemu/timer.h" #include "tcg/tcg.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "exec/target_page.h" #include "exec/translation-block.h" @@ -51,7 +49,6 @@ #ifndef CONFIG_USER_ONLY #include "qapi/error.h" #include "migration/blocker.h" -#include "exec/ram_addr.h" #include "qemu/plugin-memory.h" #include "hw/boards.h" #else From 5dd09b8157a132468547ad8a392ec9f8d836c169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:31 +0000 Subject: [PATCH 2466/2892] plugins/plugin.h: include queue.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headers should bring in what they need so don't rely on getting queue.h by side effects. This will help with clean-ups in the following patches. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-25-alex.bennee@linaro.org> --- plugins/plugin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/plugin.h b/plugins/plugin.h index 30e2299a54..9ed20b5c41 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -13,6 +13,7 @@ #define PLUGIN_H #include +#include "qemu/queue.h" #include "qemu/qht.h" #define QEMU_PLUGIN_MIN_VERSION 2 From 8c15f6e435a15e90456d26ff26199861369c9d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:32 +0000 Subject: [PATCH 2467/2892] plugins/loader: compile loader only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is very little in loader that is different between builds save for a tiny user/system mode difference in the plugin_info structure. Create two new files, user and system to hold mode specific helpers and move loader into common_ss. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-26-alex.bennee@linaro.org> --- plugins/loader.c | 13 ++----------- plugins/meson.build | 7 ++++++- plugins/plugin.h | 6 ++++++ plugins/system.c | 24 ++++++++++++++++++++++++ plugins/user.c | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 plugins/system.c create mode 100644 plugins/user.c diff --git a/plugins/loader.c b/plugins/loader.c index 827473c8b6..7523d554f0 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -31,9 +31,6 @@ #include "qemu/memalign.h" #include "hw/core/cpu.h" #include "exec/tb-flush.h" -#ifndef CONFIG_USER_ONLY -#include "hw/boards.h" -#endif #include "plugin.h" @@ -300,14 +297,8 @@ int qemu_plugin_load_list(QemuPluginList *head, Error **errp) info->target_name = target_name(); info->version.min = QEMU_PLUGIN_MIN_VERSION; info->version.cur = QEMU_PLUGIN_VERSION; -#ifndef CONFIG_USER_ONLY - MachineState *ms = MACHINE(qdev_get_machine()); - info->system_emulation = true; - info->system.smp_vcpus = ms->smp.cpus; - info->system.max_vcpus = ms->smp.max_cpus; -#else - info->system_emulation = false; -#endif + + qemu_plugin_fillin_mode_info(info); QTAILQ_FOREACH_SAFE(desc, head, entry, next) { int err; diff --git a/plugins/meson.build b/plugins/meson.build index d60be2a4d6..f7820806d3 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -57,8 +57,13 @@ if host_os == 'windows' command: dlltool_cmd ) endif + +user_ss.add(files('user.c')) +system_ss.add(files('system.c')) + +common_ss.add(files('loader.c')) + specific_ss.add(files( - 'loader.c', 'core.c', 'api.c', )) diff --git a/plugins/plugin.h b/plugins/plugin.h index 9ed20b5c41..6fbc443b96 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -119,4 +119,10 @@ struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size); void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score); +/** + * qemu_plugin_fillin_mode_info() - populate mode specific info + * info: pointer to qemu_info_t structure + */ +void qemu_plugin_fillin_mode_info(qemu_info_t *info); + #endif /* PLUGIN_H */ diff --git a/plugins/system.c b/plugins/system.c new file mode 100644 index 0000000000..b3ecc33ba5 --- /dev/null +++ b/plugins/system.c @@ -0,0 +1,24 @@ +/* + * QEMU Plugin system-emulation helpers + * + * Helpers that are specific to system emulation. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "hw/boards.h" + +#include "plugin.h" + +void qemu_plugin_fillin_mode_info(qemu_info_t *info) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + info->system_emulation = true; + info->system.smp_vcpus = ms->smp.cpus; + info->system.max_vcpus = ms->smp.max_cpus; +} diff --git a/plugins/user.c b/plugins/user.c new file mode 100644 index 0000000000..250d542502 --- /dev/null +++ b/plugins/user.c @@ -0,0 +1,19 @@ +/* + * QEMU Plugin user-mode helpers + * + * Helpers that are specific to user-mode. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "plugin.h" + +void qemu_plugin_fillin_mode_info(qemu_info_t *info) +{ + info->system_emulation = false; +} From 903e870f2453731f9b44ce9734cfcb5509304677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:33 +0000 Subject: [PATCH 2468/2892] plugins/api: split out binary path/start/end/entry code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To move the main api.c to a single build compilation object we need to start splitting out user and system specific code. As we need to grob around host headers we move these particular helpers into the *-user mode directories. The binary/start/end/entry helpers are all NOPs for system mode. While using the plugin-api.c.inc trick means we build for both linux-user and bsd-user the BSD user-mode command line is still missing -plugin. This can be enabled once we have reliable check-tcg tests working for the BSDs. Reviewed-by: Richard Henderson Reviewed-by: Warner Losh Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-27-alex.bennee@linaro.org> --- bsd-user/meson.build | 1 + bsd-user/plugin-api.c | 15 +++++++++++++ common-user/plugin-api.c.inc | 43 ++++++++++++++++++++++++++++++++++++ linux-user/meson.build | 1 + linux-user/plugin-api.c | 15 +++++++++++++ plugins/api-system.c | 39 ++++++++++++++++++++++++++++++++ plugins/api.c | 43 ------------------------------------ plugins/meson.build | 2 +- 8 files changed, 115 insertions(+), 44 deletions(-) create mode 100644 bsd-user/plugin-api.c create mode 100644 common-user/plugin-api.c.inc create mode 100644 linux-user/plugin-api.c create mode 100644 plugins/api-system.c diff --git a/bsd-user/meson.build b/bsd-user/meson.build index 39bad0ae33..37b7cd6de8 100644 --- a/bsd-user/meson.build +++ b/bsd-user/meson.build @@ -13,6 +13,7 @@ bsd_user_ss.add(files( 'elfload.c', 'main.c', 'mmap.c', + 'plugin-api.c', 'signal.c', 'strace.c', 'uaccess.c', diff --git a/bsd-user/plugin-api.c b/bsd-user/plugin-api.c new file mode 100644 index 0000000000..6ccef7eaa0 --- /dev/null +++ b/bsd-user/plugin-api.c @@ -0,0 +1,15 @@ +/* + * QEMU Plugin API - bsd-user-mode only implementations + * + * Common user-mode only APIs are in plugins/api-user. These helpers + * are only specific to bsd-user. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "common-user/plugin-api.c.inc" diff --git a/common-user/plugin-api.c.inc b/common-user/plugin-api.c.inc new file mode 100644 index 0000000000..5b8a1396b6 --- /dev/null +++ b/common-user/plugin-api.c.inc @@ -0,0 +1,43 @@ +/* + * QEMU Plugin API - *-user-mode only implementations + * + * Common user-mode only APIs are in plugins/api-user. These helpers + * are only specific to the *-user frontends. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/plugin.h" +#include "qemu.h" + +/* + * Binary path, start and end locations. Host specific due to TaskState. + */ +const char *qemu_plugin_path_to_binary(void) +{ + TaskState *ts = get_task_state(current_cpu); + return g_strdup(ts->bprm->filename); +} + +uint64_t qemu_plugin_start_code(void) +{ + TaskState *ts = get_task_state(current_cpu); + return ts->info->start_code; +} + +uint64_t qemu_plugin_end_code(void) +{ + TaskState *ts = get_task_state(current_cpu); + return ts->info->end_code; +} + +uint64_t qemu_plugin_entry_code(void) +{ + TaskState *ts = get_task_state(current_cpu); + return ts->info->entry; +} diff --git a/linux-user/meson.build b/linux-user/meson.build index f75b4fe0e3..f47a213ca3 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -27,6 +27,7 @@ linux_user_ss.add(libdw) linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c')) linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c')) linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c')) +linux_user_ss.add(when: 'CONFIG_TCG_PLUGINS', if_true: files('plugin-api.c')) syscall_nr_generators = {} diff --git a/linux-user/plugin-api.c b/linux-user/plugin-api.c new file mode 100644 index 0000000000..66755df526 --- /dev/null +++ b/linux-user/plugin-api.c @@ -0,0 +1,15 @@ +/* + * QEMU Plugin API - linux-user-mode only implementations + * + * Common user-mode only APIs are in plugins/api-user. These helpers + * are only specific to linux-user. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "common-user/plugin-api.c.inc" diff --git a/plugins/api-system.c b/plugins/api-system.c new file mode 100644 index 0000000000..cb0dd8f730 --- /dev/null +++ b/plugins/api-system.c @@ -0,0 +1,39 @@ +/* + * QEMU Plugin API - System specific implementations + * + * This provides the APIs that have a specific system implementation + * or are only relevant to system-mode. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/plugin.h" + +/* + * In system mode we cannot trace the binary being executed so the + * helpers all return NULL/0. + */ +const char *qemu_plugin_path_to_binary(void) +{ + return NULL; +} + +uint64_t qemu_plugin_start_code(void) +{ + return 0; +} + +uint64_t qemu_plugin_end_code(void) +{ + return 0; +} + +uint64_t qemu_plugin_entry_code(void) +{ + return 0; +} diff --git a/plugins/api.c b/plugins/api.c index c3ba1e98e8..ffccd71e4b 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -471,49 +471,6 @@ 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 = get_task_state(current_cpu); - 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 = get_task_state(current_cpu); - 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 = get_task_state(current_cpu); - 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 = get_task_state(current_cpu); - entry = ts->info->entry; -#endif - return entry; -} - /* * Create register handles. * diff --git a/plugins/meson.build b/plugins/meson.build index f7820806d3..9c9bc9e5bb 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -59,7 +59,7 @@ if host_os == 'windows' endif user_ss.add(files('user.c')) -system_ss.add(files('system.c')) +system_ss.add(files('system.c', 'api-system.c')) common_ss.add(files('loader.c')) From 455a2d265cf6c3947243d772b9e5d1b8dd13a9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:34 +0000 Subject: [PATCH 2469/2892] plugins/api: split out the vaddr/hwaddr helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These only work for system-mode and are NOPs for user-mode. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-28-alex.bennee@linaro.org> --- plugins/api-system.c | 58 ++++++++++++++++++++++++++++++++++++ plugins/api-user.c | 40 +++++++++++++++++++++++++ plugins/api.c | 70 -------------------------------------------- plugins/meson.build | 2 +- 4 files changed, 99 insertions(+), 71 deletions(-) create mode 100644 plugins/api-user.c diff --git a/plugins/api-system.c b/plugins/api-system.c index cb0dd8f730..38560de342 100644 --- a/plugins/api-system.c +++ b/plugins/api-system.c @@ -12,6 +12,10 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "qapi/error.h" +#include "migration/blocker.h" +#include "hw/boards.h" +#include "qemu/plugin-memory.h" #include "qemu/plugin.h" /* @@ -37,3 +41,57 @@ uint64_t qemu_plugin_entry_code(void) { return 0; } + +/* + * Virtual Memory queries + */ + +static __thread struct qemu_plugin_hwaddr hwaddr_info; + +struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, + uint64_t vaddr) +{ + CPUState *cpu = current_cpu; + unsigned int mmu_idx = get_mmuidx(info); + enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); + hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; + + assert(mmu_idx < NB_MMU_MODES); + + if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, + hwaddr_info.is_store, &hwaddr_info)) { + error_report("invalid use of qemu_plugin_get_hwaddr"); + return NULL; + } + + return &hwaddr_info; +} + +bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) +{ + return haddr->is_io; +} + +uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) +{ + if (haddr) { + return haddr->phys_addr; + } + return 0; +} + +const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) +{ + if (h && h->is_io) { + MemoryRegion *mr = h->mr; + if (!mr->name) { + unsigned maddr = (uintptr_t)mr; + g_autofree char *temp = g_strdup_printf("anon%08x", maddr); + return g_intern_string(temp); + } else { + return g_intern_string(mr->name); + } + } else { + return g_intern_static_string("RAM"); + } +} diff --git a/plugins/api-user.c b/plugins/api-user.c new file mode 100644 index 0000000000..867b420339 --- /dev/null +++ b/plugins/api-user.c @@ -0,0 +1,40 @@ +/* + * QEMU Plugin API - user-mode only implementations + * + * This provides the APIs that have a user-mode specific + * implementations or are only relevant to user-mode. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/plugin.h" + +/* + * Virtual Memory queries - these are all NOPs for user-mode which + * only ever has visibility of virtual addresses. + */ + +struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, + uint64_t vaddr) +{ + return NULL; +} + +bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) +{ + return false; +} + +uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) +{ + return 0; +} + +const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) +{ + return g_intern_static_string("Invalid"); +} diff --git a/plugins/api.c b/plugins/api.c index ffccd71e4b..82241699a5 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -383,76 +383,6 @@ qemu_plugin_mem_value qemu_plugin_mem_get_value(qemu_plugin_meminfo_t info) return value; } -/* - * Virtual Memory queries - */ - -#ifdef CONFIG_SOFTMMU -static __thread struct qemu_plugin_hwaddr hwaddr_info; -#endif - -struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, - uint64_t vaddr) -{ -#ifdef CONFIG_SOFTMMU - CPUState *cpu = current_cpu; - unsigned int mmu_idx = get_mmuidx(info); - enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); - hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; - - assert(mmu_idx < NB_MMU_MODES); - - if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, - hwaddr_info.is_store, &hwaddr_info)) { - error_report("invalid use of qemu_plugin_get_hwaddr"); - return NULL; - } - - return &hwaddr_info; -#else - return NULL; -#endif -} - -bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) -{ -#ifdef CONFIG_SOFTMMU - return haddr->is_io; -#else - return false; -#endif -} - -uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) -{ -#ifdef CONFIG_SOFTMMU - if (haddr) { - return haddr->phys_addr; - } -#endif - return 0; -} - -const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) -{ -#ifdef CONFIG_SOFTMMU - if (h && h->is_io) { - MemoryRegion *mr = h->mr; - if (!mr->name) { - unsigned maddr = (uintptr_t)mr; - g_autofree char *temp = g_strdup_printf("anon%08x", maddr); - return g_intern_string(temp); - } else { - return g_intern_string(mr->name); - } - } else { - return g_intern_static_string("RAM"); - } -#else - return g_intern_static_string("Invalid"); -#endif -} - int qemu_plugin_num_vcpus(void) { return plugin_num_vcpus(); diff --git a/plugins/meson.build b/plugins/meson.build index 9c9bc9e5bb..942b59e904 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -58,7 +58,7 @@ if host_os == 'windows' ) endif -user_ss.add(files('user.c')) +user_ss.add(files('user.c', 'api-user.c')) system_ss.add(files('system.c', 'api-system.c')) common_ss.add(files('loader.c')) From 1d3e745f7ada921d544f29fbf84c6f0f63025e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:35 +0000 Subject: [PATCH 2470/2892] plugins/api: split out time control helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are only usable in system mode where we control the timer. For user-mode make them NOPs. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-29-alex.bennee@linaro.org> --- plugins/api-system.c | 34 ++++++++++++++++++++++++++++++++++ plugins/api-user.c | 17 +++++++++++++++++ plugins/api.c | 41 ----------------------------------------- 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/plugins/api-system.c b/plugins/api-system.c index 38560de342..cc190b167e 100644 --- a/plugins/api-system.c +++ b/plugins/api-system.c @@ -95,3 +95,37 @@ const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) return g_intern_static_string("RAM"); } } + +/* + * Time control + */ +static bool has_control; +static Error *migration_blocker; + +const void *qemu_plugin_request_time_control(void) +{ + if (!has_control) { + has_control = true; + error_setg(&migration_blocker, + "TCG plugin time control does not support migration"); + migrate_add_blocker(&migration_blocker, NULL); + return &has_control; + } + return NULL; +} + +static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data) +{ + int64_t new_time = data.host_ulong; + qemu_clock_advance_virtual_time(new_time); +} + +void qemu_plugin_update_ns(const void *handle, int64_t new_time) +{ + if (handle == &has_control) { + /* Need to execute out of cpu_exec, so bql can be locked. */ + async_run_on_cpu(current_cpu, + advance_virtual_time__async, + RUN_ON_CPU_HOST_ULONG(new_time)); + } +} diff --git a/plugins/api-user.c b/plugins/api-user.c index 867b420339..28704a89e8 100644 --- a/plugins/api-user.c +++ b/plugins/api-user.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/plugin.h" +#include "exec/log.h" /* * Virtual Memory queries - these are all NOPs for user-mode which @@ -38,3 +39,19 @@ const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) { return g_intern_static_string("Invalid"); } + +/* + * Time control - for user mode the only real time is wall clock time + * so realistically all you can do in user mode is slow down execution + * which doesn't require the ability to mess with the clock. + */ + +const void *qemu_plugin_request_time_control(void) +{ + return NULL; +} + +void qemu_plugin_update_ns(const void *handle, int64_t new_time) +{ + qemu_log_mask(LOG_UNIMP, "user-mode can't control time"); +} diff --git a/plugins/api.c b/plugins/api.c index 82241699a5..832bf6ee5e 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -526,44 +526,3 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) return total; } -/* - * Time control - */ -static bool has_control; -#ifdef CONFIG_SOFTMMU -static Error *migration_blocker; -#endif - -const void *qemu_plugin_request_time_control(void) -{ - if (!has_control) { - has_control = true; -#ifdef CONFIG_SOFTMMU - error_setg(&migration_blocker, - "TCG plugin time control does not support migration"); - migrate_add_blocker(&migration_blocker, NULL); -#endif - return &has_control; - } - return NULL; -} - -#ifdef CONFIG_SOFTMMU -static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data) -{ - int64_t new_time = data.host_ulong; - qemu_clock_advance_virtual_time(new_time); -} -#endif - -void qemu_plugin_update_ns(const void *handle, int64_t new_time) -{ -#ifdef CONFIG_SOFTMMU - if (handle == &has_control) { - /* Need to execute out of cpu_exec, so bql can be locked. */ - async_run_on_cpu(current_cpu, - advance_virtual_time__async, - RUN_ON_CPU_HOST_ULONG(new_time)); - } -#endif -} From 40988ed9dfca934791bd2aeb1cc2d11e3a239265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:36 +0000 Subject: [PATCH 2471/2892] plugins/api: build only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now all the softmmu/user-mode stuff has been split out we can build this compilation unit only once. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-30-alex.bennee@linaro.org> --- plugins/api.c | 11 ----------- plugins/meson.build | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/plugins/api.c b/plugins/api.c index 832bf6ee5e..604ce06802 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -46,17 +46,6 @@ #include "exec/translator.h" #include "disas/disas.h" #include "plugin.h" -#ifndef CONFIG_USER_ONLY -#include "qapi/error.h" -#include "migration/blocker.h" -#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 */ diff --git a/plugins/meson.build b/plugins/meson.build index 942b59e904..d27220d5ff 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -61,9 +61,8 @@ endif user_ss.add(files('user.c', 'api-user.c')) system_ss.add(files('system.c', 'api-system.c')) -common_ss.add(files('loader.c')) +common_ss.add(files('loader.c', 'api.c')) specific_ss.add(files( 'core.c', - 'api.c', )) From 606ad7fe17a0baee43a29d71469fee246a09a7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:37 +0000 Subject: [PATCH 2472/2892] plugins/core: make a single build unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trim through the includes and remove everything not needed for the core. Only include tcg-op-common.h to remove the need to TARGET_LONG_BITS and move the build unit into the common set. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-31-alex.bennee@linaro.org> --- plugins/core.c | 10 +--------- plugins/meson.build | 5 +---- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/plugins/core.c b/plugins/core.c index bb105e8e68..eb9281fe54 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -12,22 +12,14 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/config-file.h" -#include "qapi/error.h" #include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/plugin.h" #include "qemu/queue.h" #include "qemu/rcu_queue.h" -#include "qemu/xxhash.h" #include "qemu/rcu.h" -#include "hw/core/cpu.h" - -#include "exec/exec-all.h" #include "exec/tb-flush.h" -#include "tcg/tcg.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "plugin.h" struct qemu_plugin_cb { diff --git a/plugins/meson.build b/plugins/meson.build index d27220d5ff..3be8245a69 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -61,8 +61,5 @@ endif user_ss.add(files('user.c', 'api-user.c')) system_ss.add(files('system.c', 'api-system.c')) -common_ss.add(files('loader.c', 'api.c')) +common_ss.add(files('loader.c', 'api.c', 'core.c')) -specific_ss.add(files( - 'core.c', -)) From 0d3dea7d7a49c22897e7435e8e09d9f587bc56c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 4 Mar 2025 22:24:38 +0000 Subject: [PATCH 2473/2892] MAINTAINERS: remove widely sanctioned entities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following organisations appear on the US sanctions list: Yadro: https://sanctionssearch.ofac.treas.gov/Details.aspx?id=41125 ISPRAS: https://sanctionssearch.ofac.treas.gov/Details.aspx?id=50890 As a result maintainers interacting with such entities would face legal risk in a number of jurisdictions. To reduce the risk of inadvertent non-compliance remove entries from these organisations from the MAINTAINERS file. Mark the pcf8574 system as orphaned until someone volunteers to step up as a maintainer. Add myself as a second reviewer to record/replay so I can help with what odd fixes I can. Reviewed-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Acked-by: Paolo Bonzini Signed-off-by: Alex Bennée Message-Id: <20250304222439.2035603-32-alex.bennee@linaro.org> --- MAINTAINERS | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 758cc461be..0e5db7a574 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2547,8 +2547,7 @@ F: hw/i2c/i2c_mux_pca954x.c F: include/hw/i2c/i2c_mux_pca954x.h pcf8574 -M: Dmitrii Sharikhin -S: Maintained +S: Orphaned F: hw/gpio/pcf8574.c F: include/gpio/pcf8574.h @@ -3660,10 +3659,10 @@ F: net/filter-mirror.c F: tests/qtest/test-filter* Record/replay -M: Pavel Dovgalyuk R: Paolo Bonzini +R: Alex Bennée W: https://wiki.qemu.org/Features/record-replay -S: Supported +S: Odd Fixes F: replay/* F: block/blkreplay.c F: net/filter-replay.c From cfcacbab38e43200264c06135946b1c5096c393a Mon Sep 17 00:00:00 2001 From: Jiqian Chen Date: Wed, 6 Nov 2024 14:14:18 +0800 Subject: [PATCH 2474/2892] xen/passthrough: use gsi to map pirq when dom0 is PVH In PVH dom0, when passthrough a device to domU, QEMU code xen_pt_realize->xc_physdev_map_pirq wants to use gsi, but in current codes the gsi number is got from file /sys/bus/pci/devices//irq, that is wrong, because irq is not equal with gsi, they are in different spaces, so pirq mapping fails. To solve above problem, use new interface of Xen, xc_pcidev_get_gsi to get gsi and use xc_physdev_map_pirq_gsi to map pirq when dom0 is PVH. Signed-off-by: Jiqian Chen Signed-off-by: Huang Rui Signed-off-by: Jiqian Chen Acked-by: Anthony PERARD Reviewed-by: Stewart Hildebrand Message-Id: <20241106061418.3655304-1-Jiqian.Chen@amd.com> Signed-off-by: Anthony PERARD --- hw/xen/xen_pt.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 4 +++ 2 files changed, 64 insertions(+) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index e2bd4c7d41..9487f68f2e 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -766,6 +766,57 @@ static void xen_pt_destroy(PCIDevice *d) { } /* init */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 42000 +static bool xen_pt_need_gsi(void) +{ + FILE *fp; + int len; + /* + * The max length of guest_type is "PVH"+'\n'+'\0', it is 5, + * so here set the length of type to be twice. + */ + char type[10]; + const char *guest_type = "/sys/hypervisor/guest_type"; + + fp = fopen(guest_type, "r"); + if (!fp) { + error_report("Cannot open %s: %s", guest_type, strerror(errno)); + return false; + } + + if (fgets(type, sizeof(type), fp)) { + len = strlen(type); + if (len) { + type[len - 1] = '\0'; + if (!strcmp(type, "PVH")) { + fclose(fp); + return true; + } + } + } + + fclose(fp); + return false; +} + +static int xen_pt_map_pirq_for_gsi(PCIDevice *d, int *pirq) +{ + int gsi; + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); + + gsi = xc_pcidev_get_gsi(xen_xc, + PCI_SBDF(s->real_device.domain, + s->real_device.bus, + s->real_device.dev, + s->real_device.func)); + if (gsi >= 0) { + return xc_physdev_map_pirq_gsi(xen_xc, xen_domid, gsi, pirq); + } + + return gsi; +} +#endif + static void xen_pt_realize(PCIDevice *d, Error **errp) { ERRP_GUARD(); @@ -847,7 +898,16 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) goto out; } +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 42000 + if (xen_pt_need_gsi()) { + rc = xen_pt_map_pirq_for_gsi(d, &pirq); + } else { + rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); + } +#else rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); +#endif + if (rc < 0) { XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n", machine_irq, pirq, errno); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index c220cc8449..822fbacdf0 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -23,6 +23,10 @@ extern bool pci_available; #define PCI_SLOT_MAX 32 #define PCI_FUNC_MAX 8 +#define PCI_SBDF(seg, bus, dev, func) \ + ((((uint32_t)(seg)) << 16) | \ + (PCI_BUILD_BDF(bus, PCI_DEVFN(dev, func)))) + /* Class, Vendor and Device IDs from Linux's pci_ids.h */ #include "hw/pci/pci_ids.h" From 4173b3d83752d9547c188db0d99ade08a1adb1fc Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 7 Feb 2025 14:37:24 +0000 Subject: [PATCH 2475/2892] hw/xen: Add "mode" parameter to xen-block devices Block devices don't work in PV Grub (0.9x) if there is no mode specified. It complains: "Error ENOENT when reading the mode" Signed-off-by: David Woodhouse Message-Id: <20250207143724.30792-2-dwmw2@infradead.org> Signed-off-by: Anthony PERARD --- hw/block/xen-block.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 2098286b5f..ec04102b66 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -408,6 +408,8 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) } xen_device_backend_printf(xendev, "info", "%u", blockdev->info); + xen_device_backend_printf(xendev, "mode", + (blockdev->info & VDISK_READONLY) ? "r" : "w"); xen_device_frontend_printf(xendev, "virtual-device", "%lu", vdev->number); From 68adcc784bad13421ac7211c316a751fb99fcb94 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 6 Feb 2025 20:49:15 +0100 Subject: [PATCH 2476/2892] xen: No need to flush the mapcache for grants On IOREQ_TYPE_INVALIDATE we need to invalidate the mapcache for regular mappings. Since recently we started reusing the mapcache also to keep track of grants mappings. However, there is no need to remove grant mappings on IOREQ_TYPE_INVALIDATE requests, we shouldn't do that. So remove the function call. Fixes: 9ecdd4bf08 (xen: mapcache: Add support for grant mappings) Cc: qemu-stable@nongnu.org Reported-by: Olaf Hering Reviewed-by: Edgar E. Iglesias Signed-off-by: Stefano Stabellini Signed-off-by: Edgar E. Iglesias Reviewed-by: Anthony PERARD Message-Id: <20250206194915.3357743-2-edgar.iglesias@gmail.com> Signed-off-by: Anthony PERARD --- hw/xen/xen-mapcache.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 00bfbcc6fb..698b5c53ed 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -700,7 +700,6 @@ void xen_invalidate_map_cache(void) bdrv_drain_all(); xen_invalidate_map_cache_single(mapcache); - xen_invalidate_map_cache_single(mapcache_grants); } static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, From d657a14de5d597bbfe7b54e4c4f0646f440e98ad Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 20 Feb 2025 08:24:59 -0500 Subject: [PATCH 2477/2892] migration: Fix UAF for incoming migration on MigrationState On the incoming migration side, QEMU uses a coroutine to load all the VM states. Inside, it may reference MigrationState on global states like migration capabilities, parameters, error state, shared mutexes and more. However there's nothing yet to make sure MigrationState won't get destroyed (e.g. after migration_shutdown()). Meanwhile there's also no API available to remove the incoming coroutine in migration_shutdown(), avoiding it to access the freed elements. There's a bug report showing this can happen and crash dest QEMU when migration is cancelled on source. When it happens, the dest main thread is trying to cleanup everything: #0 qemu_aio_coroutine_enter #1 aio_dispatch_handler #2 aio_poll #3 monitor_cleanup #4 qemu_cleanup #5 qemu_default_main Then it found the migration incoming coroutine, schedule it (even after migration_shutdown()), causing crash: #0 __pthread_kill_implementation #1 __pthread_kill_internal #2 __GI_raise #3 __GI_abort #4 __assert_fail_base #5 __assert_fail #6 qemu_mutex_lock_impl #7 qemu_lockable_mutex_lock #8 qemu_lockable_lock #9 qemu_lockable_auto_lock #10 migrate_set_error #11 process_incoming_migration_co #12 coroutine_trampoline To fix it, take a refcount after an incoming setup is properly done when qmp_migrate_incoming() succeeded the 1st time. As it's during a QMP handler which needs BQL, it means the main loop is still alive (without going into cleanups, which also needs BQL). Releasing the refcount now only until the incoming migration coroutine finished or failed. Hence the refcount is valid for both (1) setup phase of incoming ports, mostly IO watches (e.g. qio_channel_add_watch_full()), and (2) the incoming coroutine itself (process_incoming_migration_co()). Note that we can't unref in migration_incoming_state_destroy(), because both qmp_xen_load_devices_state() and load_snapshot() will use it without an incoming migration. Those hold BQL so they're not prone to this issue. PS: I suspect nobody uses Xen's command at all, as it didn't register yank, hence AFAIU the command should crash on master when trying to unregister yank in migration_incoming_state_destroy().. but that's another story. Also note that in some incoming failure cases we may not always unref the MigrationState refcount, which is a trade-off to keep things simple. We could make it accurate, but it can be an overkill. Some examples: - Unlike most of the rest protocols, socket_start_incoming_migration() may create net listener after incoming port setup sucessfully. It means we can't unref in migration_channel_process_incoming() as a generic path because socket protocol might keep using MigrationState. - For either socket or file, multiple IO watches might be created, it means logically each IO watch needs to take one refcount for MigrationState so as to be 100% accurate on ownership of refcount taken. In general, we at least need per-protocol handling to make it accurate, which can be an overkill if we know incoming failed after all. Add a short comment to explain that when taking the refcount in qmp_migrate_incoming(). Bugzilla: https://issues.redhat.com/browse/RHEL-69775 Tested-by: Yan Fu Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Message-ID: <20250220132459.512610-1-peterx@redhat.com> Signed-off-by: Fabiano Rosas --- migration/migration.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 1833cfe358..d46e776e24 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -116,6 +116,27 @@ static void migration_downtime_start(MigrationState *s) s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } +/* + * This is unfortunate: incoming migration actually needs the outgoing + * migration state (MigrationState) to be there too, e.g. to query + * capabilities, parameters, using locks, setup errors, etc. + * + * NOTE: when calling this, making sure current_migration exists and not + * been freed yet! Otherwise trying to access the refcount is already + * an use-after-free itself.. + * + * TODO: Move shared part of incoming / outgoing out into separate object. + * Then this is not needed. + */ +static void migrate_incoming_ref_outgoing_state(void) +{ + object_ref(migrate_get_current()); +} +static void migrate_incoming_unref_outgoing_state(void) +{ + object_unref(migrate_get_current()); +} + static void migration_downtime_end(MigrationState *s) { int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); @@ -863,7 +884,7 @@ process_incoming_migration_co(void *opaque) * postcopy thread. */ trace_process_incoming_migration_co_postcopy_end_main(); - return; + goto out; } /* Else if something went wrong then just fall out of the normal exit */ } @@ -879,7 +900,8 @@ process_incoming_migration_co(void *opaque) } migration_bh_schedule(process_incoming_migration_bh, mis); - return; + goto out; + fail: migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); @@ -896,6 +918,9 @@ fail: exit(EXIT_FAILURE); } +out: + /* Pairs with the refcount taken in qmp_migrate_incoming() */ + migrate_incoming_unref_outgoing_state(); } /** @@ -1901,6 +1926,17 @@ void qmp_migrate_incoming(const char *uri, bool has_channels, return; } + /* + * Making sure MigrationState is available until incoming migration + * completes. + * + * NOTE: QEMU _might_ leak this refcount in some failure paths, but + * that's OK. This is the minimum change we need to at least making + * sure success case is clean on the refcount. We can try harder to + * make it accurate for any kind of failures, but it might be an + * overkill and doesn't bring us much benefit. + */ + migrate_incoming_ref_outgoing_state(); once = false; } From 094a3dbc55df1bbd2169eaf784cb75b594a72941 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Thu, 27 Feb 2025 06:48:01 -0800 Subject: [PATCH 2478/2892] migration: ram block cpr blockers Unlike cpr-reboot mode, cpr-transfer mode cannot save volatile ram blocks in the migration stream file and recreate them later, because the physical memory for the blocks is pinned and registered for vfio. Add a blocker for volatile ram blocks. Also add a blocker for RAM_GUEST_MEMFD. Preserving guest_memfd may be sufficient for CPR, but it has not been tested yet. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Reviewed-by: David Hildenbrand Message-ID: <1740667681-257312-1-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- include/exec/memory.h | 3 ++ include/exec/ramblock.h | 1 + migration/savevm.c | 2 ++ system/physmem.c | 66 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index 78c4e0aec8..d09af58c97 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3203,6 +3203,9 @@ bool ram_block_discard_is_disabled(void); */ bool ram_block_discard_is_required(void); +void ram_block_add_cpr_blocker(RAMBlock *rb, Error **errp); +void ram_block_del_cpr_blocker(RAMBlock *rb); + #endif #endif diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h index 0babd105c0..64484cd821 100644 --- a/include/exec/ramblock.h +++ b/include/exec/ramblock.h @@ -39,6 +39,7 @@ struct RAMBlock { /* RCU-enabled, writes protected by the ramlist lock */ QLIST_ENTRY(RAMBlock) next; QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; + Error *cpr_blocker; int fd; uint64_t fd_offset; int guest_memfd; diff --git a/migration/savevm.c b/migration/savevm.c index 5c4fdfd95e..ce158c3512 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -3514,12 +3514,14 @@ void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev) qemu_ram_set_idstr(mr->ram_block, memory_region_name(mr), dev); qemu_ram_set_migratable(mr->ram_block); + ram_block_add_cpr_blocker(mr->ram_block, &error_fatal); } void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev) { qemu_ram_unset_idstr(mr->ram_block); qemu_ram_unset_migratable(mr->ram_block); + ram_block_del_cpr_blocker(mr->ram_block); } void vmstate_register_ram_global(MemoryRegion *mr) diff --git a/system/physmem.c b/system/physmem.c index a6af555f4b..e97de3ef65 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -71,7 +71,10 @@ #include "qemu/pmem.h" +#include "qapi/qapi-types-migration.h" +#include "migration/blocker.h" #include "migration/cpr.h" +#include "migration/options.h" #include "migration/vmstate.h" #include "qemu/range.h" @@ -1904,6 +1907,14 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) qemu_mutex_unlock_ramlist(); goto out_free; } + + error_setg(&new_block->cpr_blocker, + "Memory region %s uses guest_memfd, " + "which is not supported with CPR.", + memory_region_name(new_block->mr)); + migrate_add_blocker_modes(&new_block->cpr_blocker, errp, + MIG_MODE_CPR_TRANSFER, + -1); } ram_size = (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS; @@ -4095,3 +4106,58 @@ bool ram_block_discard_is_required(void) return qatomic_read(&ram_block_discard_required_cnt) || qatomic_read(&ram_block_coordinated_discard_required_cnt); } + +/* + * Return true if ram is compatible with CPR. Do not exclude rom, + * because the rom file could change in new QEMU. + */ +static bool ram_is_cpr_compatible(RAMBlock *rb) +{ + MemoryRegion *mr = rb->mr; + + if (!mr || !memory_region_is_ram(mr)) { + return true; + } + + /* Ram device is remapped in new QEMU */ + if (memory_region_is_ram_device(mr)) { + return true; + } + + /* + * A file descriptor is passed to new QEMU and remapped, or its backing + * file is reopened and mapped. It must be shared to avoid COW. + */ + if (rb->fd >= 0 && qemu_ram_is_shared(rb)) { + return true; + } + + return false; +} + +/* + * Add a blocker for each volatile ram block. This function should only be + * called after we know that the block is migratable. Non-migratable blocks + * are either re-created in new QEMU, or are handled specially, or are covered + * by a device-level CPR blocker. + */ +void ram_block_add_cpr_blocker(RAMBlock *rb, Error **errp) +{ + assert(qemu_ram_is_migratable(rb)); + + if (ram_is_cpr_compatible(rb)) { + return; + } + + error_setg(&rb->cpr_blocker, + "Memory region %s is not compatible with CPR. share=on is " + "required for memory-backend objects, and aux-ram-share=on is " + "required.", memory_region_name(rb->mr)); + migrate_add_blocker_modes(&rb->cpr_blocker, errp, MIG_MODE_CPR_TRANSFER, + -1); +} + +void ram_block_del_cpr_blocker(RAMBlock *rb) +{ + migrate_del_blocker(&rb->cpr_blocker); +} From baa41af1c083446971feac39b0da845e547ca068 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 5 Mar 2025 14:28:20 +0800 Subject: [PATCH 2479/2892] migration: Prioritize RDMA in ram_save_target_page() Address an error in RDMA-based migration by ensuring RDMA is prioritized when saving pages in `ram_save_target_page()`. Previously, the RDMA protocol's page-saving step was placed after other protocols due to a refactoring in commit bc38dc2f5f3. This led to migration failures characterized by unknown control messages and state loading errors destination: (qemu) qemu-system-x86_64: Unknown control message QEMU FILE qemu-system-x86_64: error while loading state section id 1(ram) qemu-system-x86_64: load of migration failed: Operation not permitted source: (qemu) qemu-system-x86_64: RDMA is in an error state waiting migration to abort! qemu-system-x86_64: failed to save SaveStateEntry with id(name): 1(ram): -1 qemu-system-x86_64: rdma migration: recv polling control error! qemu-system-x86_64: warning: Early error. Sending error. qemu-system-x86_64: warning: rdma migration: send polling control error RDMA migration implemented its own protocol/method to send pages to destination side, hand over to RDMA first to prevent pages being saved by other protocol. Fixes: bc38dc2f5f3 ("migration: refactor ram_save_target_page functions") Reviewed-by: Peter Xu Signed-off-by: Li Zhijian Message-ID: <20250305062825.772629-2-lizhijian@fujitsu.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 589b6505eb..424df6d9f1 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1964,6 +1964,11 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; int res; + /* Hand over to RDMA first */ + if (control_save_page(pss, offset, &res)) { + return res; + } + if (!migrate_multifd() || migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) { if (save_zero_page(rs, pss, offset)) { @@ -1976,10 +1981,6 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) return ram_save_multifd_page(block, offset); } - if (control_save_page(pss, offset, &res)) { - return res; - } - return ram_save_page(rs, pss); } From a506a1b16702aae69a43e782f225bdc0ec6545fc Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 4 Mar 2025 21:07:16 -0800 Subject: [PATCH 2480/2892] trace/control-target: cleanup headers and make compilation unit common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Alex Bennée Tested-by: Alex Bennée Reviewed-by: Richard Henderson Message-ID: <20250305050716.3460989-1-pierrick.bouvier@linaro.org> Signed-off-by: Stefan Hajnoczi --- trace/control-target.c | 2 -- trace/meson.build | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/trace/control-target.c b/trace/control-target.c index d58e84f6dd..57ceac2108 100644 --- a/trace/control-target.c +++ b/trace/control-target.c @@ -8,8 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu/lockable.h" -#include "cpu.h" #include "trace/control.h" diff --git a/trace/meson.build b/trace/meson.build index c3412dc0ba..3df4549355 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,6 +1,4 @@ -system_ss.add(files('trace-hmp-cmds.c')) - -specific_ss.add(files('control-target.c')) +system_ss.add(files('control-target.c', 'trace-hmp-cmds.c')) trace_events_files = [] foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events From ecf92e3618ab6e6cd7ae151f9c12b9e2a6ead198 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:01 -0400 Subject: [PATCH 2481/2892] docs/sphinx: create QAPI domain extension stub A Sphinx domain is a collection of directive and role extensions meant to facilitate the documentation of a specific language. For instance, Sphinx ships with "python" and "cpp" domains. This patch introduces a stub for the "qapi" language domain. Please see https://www.sphinx-doc.org/en/master/usage/domains/index.html for more information. This stub doesn't really do anything yet, we'll get to it brick-by-brick in the forthcoming commits to keep the series breezy and the git history informative. Signed-off-by: John Snow Message-ID: <20250311034303.75779-4-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/conf.py | 9 +++++- docs/sphinx/qapi_domain.py | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 docs/sphinx/qapi_domain.py diff --git a/docs/conf.py b/docs/conf.py index 31bb9a3789..49d9de894c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -60,7 +60,14 @@ needs_sphinx = '3.4.3' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'depfile', 'qapidoc'] +extensions = [ + 'depfile', + 'hxtool', + 'kerneldoc', + 'qapi_domain', + 'qapidoc', + 'qmp_lexer', +] if sphinx.version_info[:3] > (4, 0, 0): tags.add('sphinx4') diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py new file mode 100644 index 0000000000..a1983d9444 --- /dev/null +++ b/docs/sphinx/qapi_domain.py @@ -0,0 +1,56 @@ +""" +QAPI domain extension. +""" + +from __future__ import annotations + +from typing import ( + TYPE_CHECKING, + AbstractSet, + Any, + Dict, + Tuple, +) + +from sphinx.domains import Domain, ObjType +from sphinx.util import logging + + +if TYPE_CHECKING: + from sphinx.application import Sphinx + +logger = logging.getLogger(__name__) + + +class QAPIDomain(Domain): + """QAPI language domain.""" + + name = "qapi" + label = "QAPI" + + object_types: Dict[str, ObjType] = {} + directives = {} + roles = {} + initial_data: Dict[str, Dict[str, Tuple[Any]]] = {} + indices = [] + + def merge_domaindata( + self, docnames: AbstractSet[str], otherdata: Dict[str, Any] + ) -> None: + pass + + def resolve_any_xref(self, *args: Any, **kwargs: Any) -> Any: + # pylint: disable=unused-argument + return [] + + +def setup(app: Sphinx) -> Dict[str, Any]: + app.setup_extension("sphinx.directives") + app.add_domain(QAPIDomain) + + return { + "version": "1.0", + "env_version": 1, + "parallel_read_safe": True, + "parallel_write_safe": True, + } From abf6bedc38aea51ecce988a499a0b277bbd3c267 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:02 -0400 Subject: [PATCH 2482/2892] docs/sphinx: add compat.py module and nested_parse helper Create a compat module that handles sphinx cross-version compatibility issues. For the inaugural function, add a nested_parse_with_titles() helper that handles differences in line number tracking for nested directive body parsing. Spoilers: there are more cross-version hacks to come throughout the series. Signed-off-by: John Snow Message-ID: <20250311034303.75779-5-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/compat.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/sphinx/compat.py diff --git a/docs/sphinx/compat.py b/docs/sphinx/compat.py new file mode 100644 index 0000000000..39b859a25e --- /dev/null +++ b/docs/sphinx/compat.py @@ -0,0 +1,35 @@ +""" +Sphinx cross-version compatibility goop +""" + +from docutils.nodes import Element + +from sphinx.util import nodes +from sphinx.util.docutils import SphinxDirective, switch_source_input + + +def nested_parse_with_titles( + directive: SphinxDirective, content_node: Element +) -> None: + """ + This helper preserves error parsing context across sphinx versions. + """ + + # necessary so that the child nodes get the right source/line set + content_node.document = directive.state.document + + try: + # Modern sphinx (6.2.0+) supports proper offsetting for + # nested parse error context management + nodes.nested_parse_with_titles( + directive.state, + directive.content, + content_node, + content_offset=directive.content_offset, + ) + except TypeError: + # No content_offset argument. Fall back to SSI method. + with switch_source_input(directive.state, directive.content): + nodes.nested_parse_with_titles( + directive.state, directive.content, content_node + ) From 36ceafad9e4e61138b08dc371c42248dc5289a57 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:03 -0400 Subject: [PATCH 2483/2892] docs/qapi-domain: add QAPI domain object registry This is the first step towards QAPI domain cross-references and a QAPI reference index. This patch just creates the object registry, and updates the merge_domaindata stub method now that we have actual data we may need to merge. Note that how to handle merge conflict resolution is unhandled, as the Sphinx python domain itself does not handle it either. I do not know how to intentionally trigger it, so I've left an assertion instead if it should ever come up ... Signed-off-by: John Snow Message-ID: <20250311034303.75779-6-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 77 +++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index a1983d9444..f3ece42bc2 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -9,10 +9,12 @@ from typing import ( AbstractSet, Any, Dict, + NamedTuple, Tuple, ) from sphinx.domains import Domain, ObjType +from sphinx.locale import __ from sphinx.util import logging @@ -22,22 +24,93 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) +class ObjectEntry(NamedTuple): + docname: str + node_id: str + objtype: str + aliased: bool + + class QAPIDomain(Domain): """QAPI language domain.""" name = "qapi" label = "QAPI" + # This table associates cross-reference object types (key) with an + # ObjType instance, which defines the valid cross-reference roles + # for each object type. + + # Actual table entries for module, command, event, etc will come in + # forthcoming commits. object_types: Dict[str, ObjType] = {} + directives = {} roles = {} - initial_data: Dict[str, Dict[str, Tuple[Any]]] = {} + + # Moved into the data property at runtime; + # this is the internal index of reference-able objects. + initial_data: Dict[str, Dict[str, Tuple[Any]]] = { + "objects": {}, # fullname -> ObjectEntry + } + indices = [] + @property + def objects(self) -> Dict[str, ObjectEntry]: + ret = self.data.setdefault("objects", {}) + return ret # type: ignore[no-any-return] + + def note_object( + self, + name: str, + objtype: str, + node_id: str, + aliased: bool = False, + location: Any = None, + ) -> None: + """Note a QAPI object for cross reference.""" + if name in self.objects: + other = self.objects[name] + if other.aliased and aliased is False: + # The original definition found. Override it! + pass + elif other.aliased is False and aliased: + # The original definition is already registered. + return + else: + # duplicated + logger.warning( + __( + "duplicate object description of %s, " + "other instance in %s, use :no-index: for one of them" + ), + name, + other.docname, + location=location, + ) + self.objects[name] = ObjectEntry( + self.env.docname, node_id, objtype, aliased + ) + + def clear_doc(self, docname: str) -> None: + for fullname, obj in list(self.objects.items()): + if obj.docname == docname: + del self.objects[fullname] + def merge_domaindata( self, docnames: AbstractSet[str], otherdata: Dict[str, Any] ) -> None: - pass + for fullname, obj in otherdata["objects"].items(): + if obj.docname in docnames: + # Sphinx's own python domain doesn't appear to bother to + # check for collisions. Assert they don't happen and + # we'll fix it if/when the case arises. + assert fullname not in self.objects, ( + "bug - collision on merge?" + f" {fullname=} {obj=} {self.objects[fullname]=}" + ) + self.objects[fullname] = obj def resolve_any_xref(self, *args: Any, **kwargs: Any) -> Any: # pylint: disable=unused-argument From e93d29d27e93a25e0bd59d44299fc15486c62246 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:04 -0400 Subject: [PATCH 2484/2892] docs/qapi-domain: add QAPI index Use the QAPI object registry to generate a special index just for QAPI definitions. The index can show entries both by definition type and all together, alphabetically. The index can be linked from anywhere in the QEMU manual by using the reference `qapi-index`. Signed-off-by: John Snow Message-ID: <20250311034303.75779-7-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 73 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index f3ece42bc2..3e7718d32d 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -9,12 +9,20 @@ from typing import ( AbstractSet, Any, Dict, + Iterable, + List, NamedTuple, + Optional, Tuple, ) -from sphinx.domains import Domain, ObjType -from sphinx.locale import __ +from sphinx.domains import ( + Domain, + Index, + IndexEntry, + ObjType, +) +from sphinx.locale import _, __ from sphinx.util import logging @@ -31,6 +39,62 @@ class ObjectEntry(NamedTuple): aliased: bool +class QAPIIndex(Index): + """ + Index subclass to provide the QAPI definition index. + """ + + # pylint: disable=too-few-public-methods + + name = "index" + localname = _("QAPI Index") + shortname = _("QAPI Index") + + def generate( + self, + docnames: Optional[Iterable[str]] = None, + ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]: + assert isinstance(self.domain, QAPIDomain) + 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: + if docnames and obj.docname not in docnames: + continue + + # Strip the module name out: + objname = objname.split(".")[-1] + + # Add an alphabetical entry: + entries = content.setdefault(objname[0].upper(), []) + entries.append( + IndexEntry( + objname, 0, obj.docname, obj.node_id, obj.objtype, "", "" + ) + ) + + # Add a categorical entry: + category = obj.objtype.title() + "s" + entries = content.setdefault(category, []) + entries.append( + IndexEntry(objname, 0, obj.docname, obj.node_id, "", "", "") + ) + + # alphabetically sort categories; type names first, ABC entries last. + sorted_content = sorted( + content.items(), + key=lambda x: (len(x[0]) == 1, x[0]), + ) + return sorted_content, collapse + + class QAPIDomain(Domain): """QAPI language domain.""" @@ -54,7 +118,10 @@ class QAPIDomain(Domain): "objects": {}, # fullname -> ObjectEntry } - indices = [] + # Index pages to generate; each entry is an Index class. + indices = [ + QAPIIndex, + ] @property def objects(self) -> Dict[str, ObjectEntry]: From dca2f3c47137b2bc276f443b3c269215a1ef9166 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:05 -0400 Subject: [PATCH 2485/2892] docs/qapi-domain: add resolve_any_xref() Add the ability to resolve cross-references using the `any` cross-reference syntax. Adding QAPI-specific cross-reference roles will be added in a forthcoming commit, and will share the same find_obj() helper. (There's less code needed for the generic cross-reference resolver, so it comes first in this series.) Once again, this code is based very heavily on sphinx.domains.python. Signed-off-by: John Snow Message-ID: <20250311034303.75779-8-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 96 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 3e7718d32d..f05c2cadf0 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -16,6 +16,9 @@ from typing import ( Tuple, ) +from docutils import nodes + +from sphinx.addnodes import pending_xref from sphinx.domains import ( Domain, Index, @@ -24,10 +27,15 @@ from sphinx.domains import ( ) from sphinx.locale import _, __ from sphinx.util import logging +from sphinx.util.nodes import make_refnode if TYPE_CHECKING: + from docutils.nodes import Element + from sphinx.application import Sphinx + from sphinx.builders import Builder + from sphinx.environment import BuildEnvironment logger = logging.getLogger(__name__) @@ -179,9 +187,91 @@ class QAPIDomain(Domain): ) self.objects[fullname] = obj - def resolve_any_xref(self, *args: Any, **kwargs: Any) -> Any: - # pylint: disable=unused-argument - return [] + def find_obj( + self, modname: str, name: str, typ: Optional[str] + ) -> list[tuple[str, ObjectEntry]]: + """ + Find a QAPI object for "name", perhaps using the given module. + + 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.) + """ + if not name: + return [] + + names: list[str] = [] + matches: list[tuple[str, ObjectEntry]] = [] + + 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}" + + if typ is None: + # type isn't specified, this is a generic xref. + # search *all* qapi-specific object types. + objtypes: List[str] = list(self.object_types) + else: + # type is specified and will be a role (e.g. obj, mod, cmd) + # convert this to eligible object types (e.g. command, module) + # 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 + ] + + matches = [(oname, self.objects[oname]) for oname in names] + if len(matches) > 1: + matches = [m for m in matches if not m[1].aliased] + return matches + + def resolve_any_xref( + self, + env: BuildEnvironment, + fromdocname: str, + builder: Builder, + target: str, + node: pending_xref, + contnode: Element, + ) -> List[Tuple[str, nodes.reference]]: + results: List[Tuple[str, nodes.reference]] = [] + matches = self.find_obj(node.get("qapi:module"), target, None) + for name, obj in matches: + rolename = self.role_for_objtype(obj.objtype) + assert rolename is not None + role = f"qapi:{rolename}" + refnode = make_refnode( + builder, fromdocname, obj.docname, obj.node_id, contnode, name + ) + results.append((role, refnode)) + return results def setup(app: Sphinx) -> Dict[str, Any]: From 760b37e1df0cd4129127e1e381fb25b98ceecc79 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:06 -0400 Subject: [PATCH 2486/2892] docs/qapi-domain: add QAPI xref roles Add domain-specific cross-reference syntax. As of this commit, that means new :qapi:any:`block-core` referencing syntax. The :any: role will find anything registered to the QAPI domain, including modules, commands, events, etc. Creating the cross-references is powered by the QAPIXRefRole class; resolving them is handled by QAPIDomain.resolve_xref(). QAPIXrefRole is based heavily on Sphinx's own PyXrefRole, with modifications necessary for QAPI features. Signed-off-by: John Snow Message-ID: <20250311034303.75779-9-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 88 +++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index f05c2cadf0..49d42c0921 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -26,6 +26,7 @@ from sphinx.domains import ( ObjType, ) from sphinx.locale import _, __ +from sphinx.roles import XRefRole from sphinx.util import logging from sphinx.util.nodes import make_refnode @@ -47,6 +48,54 @@ class ObjectEntry(NamedTuple): aliased: bool +class QAPIXRefRole(XRefRole): + + def process_link( + self, + env: BuildEnvironment, + refnode: Element, + has_explicit_title: bool, + title: str, + target: str, + ) -> tuple[str, str]: + refnode["qapi:module"] = env.ref_context.get("qapi:module") + + # Cross-references that begin with a tilde adjust the title to + # only show the reference without a leading module, even if one + # was provided. This is a Sphinx-standard syntax; give it + # priority over QAPI-specific type markup below. + hide_module = False + if target.startswith("~"): + hide_module = True + target = target[1:] + + # Type names that end with "?" are considered optional + # arguments and should be documented as such, but it's not + # part of the xref itself. + if target.endswith("?"): + refnode["qapi:optional"] = True + target = target[:-1] + + # Type names wrapped in brackets denote lists. strip the + # brackets and remember to add them back later. + if target.startswith("[") and target.endswith("]"): + refnode["qapi:array"] = True + target = target[1:-1] + + if has_explicit_title: + # Don't mess with the title at all if it was explicitly set. + # Explicit title syntax for references is e.g. + # :qapi:type:`target ` + # and this explicit title overrides everything else here. + return title, target + + title = target + if hide_module: + title = target.split(".")[-1] + + return title, target + + class QAPIIndex(Index): """ Index subclass to provide the QAPI definition index. @@ -118,7 +167,13 @@ class QAPIDomain(Domain): object_types: Dict[str, ObjType] = {} directives = {} - roles = {} + + # These are all cross-reference roles; e.g. + # :qapi:cmd:`query-block`. The keys correlate to the names used in + # the object_types table values above. + roles = { + "any": QAPIXRefRole(), # reference *any* type of QAPI object. + } # Moved into the data property at runtime; # this is the internal index of reference-able objects. @@ -252,6 +307,37 @@ class QAPIDomain(Domain): matches = [m for m in matches if not m[1].aliased] return matches + def resolve_xref( + self, + env: BuildEnvironment, + fromdocname: str, + builder: Builder, + typ: str, + target: str, + node: pending_xref, + contnode: Element, + ) -> nodes.reference | None: + modname = node.get("qapi:module") + matches = self.find_obj(modname, target, typ) + + if not matches: + return None + + if len(matches) > 1: + logger.warning( + __("more than one target found for cross-reference %r: %s"), + target, + ", ".join(match[0] for match in matches), + type="ref", + subtype="qapi", + location=node, + ) + + name, obj = matches[0] + return make_refnode( + builder, fromdocname, obj.docname, obj.node_id, contnode, name + ) + def resolve_any_xref( self, env: BuildEnvironment, From 6d64a27cd367d9d8639b560dc4ed13cf05b5f43b Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:07 -0400 Subject: [PATCH 2487/2892] docs/qapi-domain: add compatibility node classes Sphinx prior to v4.0 uses different classes for rendering elements of documentation objects; add some compatibility classes to use the right node classes conditionally. Signed-off-by: John Snow Message-ID: <20250311034303.75779-10-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/compat.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/compat.py b/docs/sphinx/compat.py index 39b859a25e..6bc698c5ad 100644 --- a/docs/sphinx/compat.py +++ b/docs/sphinx/compat.py @@ -2,12 +2,27 @@ Sphinx cross-version compatibility goop """ -from docutils.nodes import Element +from typing import Callable +from docutils.nodes import Element, Node, Text + +import sphinx +from sphinx import addnodes from sphinx.util import nodes from sphinx.util.docutils import SphinxDirective, switch_source_input +SpaceNode: Callable[[str], Node] +KeywordNode: Callable[[str, str], Node] + +if sphinx.version_info[:3] >= (4, 0, 0): + SpaceNode = addnodes.desc_sig_space + KeywordNode = addnodes.desc_sig_keyword +else: + SpaceNode = Text + KeywordNode = addnodes.desc_annotation + + def nested_parse_with_titles( directive: SphinxDirective, content_node: Element ) -> None: From 1ea664862ad097f25d722c1c733d16c5b971e99b Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:08 -0400 Subject: [PATCH 2488/2892] docs/qapi-domain: Add QAPIDescription abstract class This class is a generic, top-level directive for documenting some kind of QAPI thingamajig that we expect to go into the Index. This class doesn't do much by itself, and it isn't yet associated with any particular directive. handle_signature(), _object_hierarchy_parts() and _toc_entry_name() are defined in the base class. get_index_text() and add_target_and_index() are new methods defined here; they are based heavily on the layout and format of the Python domain's general object class. Signed-off-by: John Snow Message-ID: <20250311034303.75779-11-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 101 ++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 49d42c0921..0ee36b4644 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -14,11 +14,13 @@ from typing import ( NamedTuple, Optional, Tuple, + cast, ) from docutils import nodes -from sphinx.addnodes import pending_xref +from sphinx.addnodes import desc_signature, pending_xref +from sphinx.directives import ObjectDescription from sphinx.domains import ( Domain, Index, @@ -28,7 +30,7 @@ from sphinx.domains import ( from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging -from sphinx.util.nodes import make_refnode +from sphinx.util.nodes import make_id, make_refnode if TYPE_CHECKING: @@ -96,6 +98,101 @@ class QAPIXRefRole(XRefRole): return title, target +Signature = str + + +class QAPIDescription(ObjectDescription[Signature]): + """ + Generic QAPI description. + + This is meant to be an abstract class, not instantiated + directly. This class handles the abstract details of indexing, the + TOC, and reference targets for QAPI descriptions. + """ + + def handle_signature(self, sig: str, signode: desc_signature) -> Signature: + # Do nothing. The return value here is the "name" of the entity + # being documented; for QAPI, this is the same as the + # "signature", which is just a name. + + # Normally this method must also populate signode with nodes to + # render the signature; here we do nothing instead - the + # subclasses will handle this. + return sig + + def get_index_text(self, name: Signature) -> Tuple[str, str]: + """Return the text for the index entry of the object.""" + + # NB: this is used for the global index, not the QAPI index. + return ("single", f"{name} (QMP {self.objtype})") + + def add_target_and_index( + self, name: Signature, sig: str, signode: desc_signature + ) -> None: + # name is the return value of handle_signature. + # sig is the original, raw text argument to handle_signature. + # For QAPI, these are identical, currently. + + 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 + + node_id = make_id( + self.env, self.state.document, self.objtype, fullname + ) + signode["ids"].append(node_id) + + self.state.document.note_explicit_target(signode) + domain = cast(QAPIDomain, self.env.get_domain("qapi")) + domain.note_object(fullname, self.objtype, node_id, location=signode) + + if "no-index-entry" not in self.options: + arity, indextext = self.get_index_text(name) + assert self.indexnode is not None + if indextext: + self.indexnode["entries"].append( + (arity, indextext, node_id, "", None) + ) + + 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(".")) + + def _toc_entry_name(self, sig_node: desc_signature) -> str: + # This controls the name in the TOC and on the sidebar. + + # This is the return type of _object_hierarchy_parts(). + toc_parts = cast(Tuple[str, ...], sig_node.get("_toc_parts", ())) + if not toc_parts: + return "" + + config = self.env.app.config + *parents, name = toc_parts + if config.toc_object_entries_show_parents == "domain": + return sig_node.get("fullname", name) + if config.toc_object_entries_show_parents == "hide": + return name + if config.toc_object_entries_show_parents == "all": + return ".".join(parents + [name]) + return "" + + class QAPIIndex(Index): """ Index subclass to provide the QAPI definition index. From 7320feeb9663c2fba1f70ed263f4c18080c75e3e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:09 -0400 Subject: [PATCH 2489/2892] docs/qapi-domain: add qapi:module directive This adds the qapi:module directive, which just notes the current module being documented and performs a nested parse of the content block, if present. This code is based pretty heavily on Sphinx's PyModule directive, but with unnecessary features excised. For example: .. qapi:module:: block-core Hello, and welcome to block-core! ================================= lorem ipsum, dolor sit amet ... Signed-off-by: John Snow Message-ID: <20250311034303.75779-12-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 71 ++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 0ee36b4644..e623d1f867 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -19,6 +19,7 @@ from typing import ( from docutils import nodes +from sphinx import addnodes from sphinx.addnodes import desc_signature, pending_xref from sphinx.directives import ObjectDescription from sphinx.domains import ( @@ -34,7 +35,7 @@ from sphinx.util.nodes import make_id, make_refnode if TYPE_CHECKING: - from docutils.nodes import Element + from docutils.nodes import Element, Node from sphinx.application import Sphinx from sphinx.builders import Builder @@ -193,6 +194,60 @@ class QAPIDescription(ObjectDescription[Signature]): return "" +class QAPIModule(QAPIDescription): + """ + Directive to mark description of a new module. + + This directive doesn't generate any special formatting, and is just + a pass-through for the content body. Named section titles are + allowed in the content body. + + Use this directive to create entries for the QAPI module in the + global index and the QAPI index; as well as to associate subsequent + definitions with the module they are defined in for purposes of + search and QAPI index organization. + + :arg: The name of the module. + :opt no-index: Don't add cross-reference targets or index entries. + :opt no-typesetting: Don't render the content body (but preserve any + cross-reference target IDs in the squelched output.) + + Example:: + + .. qapi:module:: block-core + :no-index: + :no-typesetting: + + Lorem ipsum, dolor sit amet ... + """ + + def run(self) -> List[Node]: + modname = self.arguments[0].strip() + self.env.ref_context["qapi:module"] = modname + ret = super().run() + + # ObjectDescription always creates a visible signature bar. We + # want module items to be "invisible", however. + + # Extract the content body of the directive: + assert isinstance(ret[-1], addnodes.desc) + desc_node = ret.pop(-1) + assert isinstance(desc_node.children[1], addnodes.desc_content) + ret.extend(desc_node.children[1].children) + + # Re-home node_ids so anchor refs still work: + node_ids: List[str] + if node_ids := [ + node_id + for el in desc_node.children[0].traverse(nodes.Element) + for node_id in cast(List[str], el.get("ids", ())) + ]: + target_node = nodes.target(ids=node_ids) + ret.insert(1, target_node) + + return ret + + class QAPIIndex(Index): """ Index subclass to provide the QAPI definition index. @@ -258,17 +313,21 @@ class QAPIDomain(Domain): # This table associates cross-reference object types (key) with an # ObjType instance, which defines the valid cross-reference roles # for each object type. + object_types: Dict[str, ObjType] = { + "module": ObjType(_("module"), "mod", "any"), + } - # Actual table entries for module, command, event, etc will come in - # forthcoming commits. - object_types: Dict[str, ObjType] = {} - - directives = {} + # Each of these provides a rST directive, + # e.g. .. qapi:module:: block-core + directives = { + "module": QAPIModule, + } # These are all cross-reference roles; e.g. # :qapi:cmd:`query-block`. The keys correlate to the names used in # the object_types table values above. roles = { + "mod": QAPIXRefRole(), "any": QAPIXRefRole(), # reference *any* type of QAPI object. } From 8799c3641a869bb04387ccc4922a66bbd5fcd67e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:10 -0400 Subject: [PATCH 2490/2892] docs/qapi-domain: add QAPIObject class This patch adds another abstract class that describes "a QAPI thingie". The main difference here is that this class will be generating visible documentation, unlike the QAPIDescription class. Signed-off-by: John Snow Message-ID: <20250311034303.75779-13-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index e623d1f867..3109c0cb90 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -18,7 +18,9 @@ from typing import ( ) from docutils import nodes +from docutils.parsers.rst import directives +from compat import KeywordNode, SpaceNode from sphinx import addnodes from sphinx.addnodes import desc_signature, pending_xref from sphinx.directives import ObjectDescription @@ -40,6 +42,7 @@ if TYPE_CHECKING: 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__) @@ -99,6 +102,8 @@ class QAPIXRefRole(XRefRole): return title, target +# Alias for the return of handle_signature(), which is used in several places. +# (In the Python domain, this is Tuple[str, str] instead.) Signature = str @@ -194,6 +199,65 @@ class QAPIDescription(ObjectDescription[Signature]): return "" +class QAPIObject(QAPIDescription): + """ + Description of a generic QAPI object. + + It's not used directly, but is instead subclassed by specific directives. + """ + + # Inherit some standard options from Sphinx's ObjectDescription + option_spec: OptionSpec = ( # type:ignore[misc] + ObjectDescription.option_spec.copy() + ) + option_spec.update( + { + # Borrowed from the Python domain: + "module": directives.unchanged, # Override contextual module name + } + ) + + def get_signature_prefix(self) -> List[nodes.Node]: + """Return a prefix to put before the object name in the signature.""" + assert self.objtype + return [ + KeywordNode("", self.objtype.title()), + SpaceNode(" "), + ] + + def get_signature_suffix(self) -> List[nodes.Node]: + """Return a suffix to put after the object name in the signature.""" + return [] + + def handle_signature(self, sig: str, signode: desc_signature) -> Signature: + """ + Transform a QAPI definition name into RST nodes. + + This method was originally intended for handling function + signatures. In the QAPI domain, however, we only pass the + definition name as the directive argument and handle everything + else in the content body with field lists. + + 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") + ) + + signode["fullname"] = sig + signode["module"] = modname + sig_prefix = self.get_signature_prefix() + if sig_prefix: + signode += addnodes.desc_annotation( + str(sig_prefix), "", *sig_prefix + ) + signode += addnodes.desc_name(sig, sig) + signode += self.get_signature_suffix() + + return sig + + class QAPIModule(QAPIDescription): """ Directive to mark description of a new module. From 758bbdcd1212bb0b7369fea249114ad02d4b7fd0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:11 -0400 Subject: [PATCH 2491/2892] docs/qapi-domain: add qapi:command directive This commit adds a stubbed version of QAPICommand that utilizes the QAPIObject class, the qapi:command directive, the :qapi:cmd: cross-reference role, and the "command" object type in the QAPI object registry. They don't do anything *particularly* interesting yet, but that will come in forthcoming commits. Signed-off-by: John Snow Message-ID: <20250311034303.75779-14-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 3109c0cb90..547040f75a 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -258,6 +258,12 @@ class QAPIObject(QAPIDescription): return sig +class QAPICommand(QAPIObject): + """Description of a QAPI Command.""" + + # Nothing unique for now! Changed in later commits O:-) + + class QAPIModule(QAPIDescription): """ Directive to mark description of a new module. @@ -379,12 +385,14 @@ class QAPIDomain(Domain): # for each object type. object_types: Dict[str, ObjType] = { "module": ObjType(_("module"), "mod", "any"), + "command": ObjType(_("command"), "cmd", "any"), } # Each of these provides a rST directive, # e.g. .. qapi:module:: block-core directives = { "module": QAPIModule, + "command": QAPICommand, } # These are all cross-reference roles; e.g. @@ -392,6 +400,7 @@ class QAPIDomain(Domain): # the object_types table values above. roles = { "mod": QAPIXRefRole(), + "cmd": QAPIXRefRole(), "any": QAPIXRefRole(), # reference *any* type of QAPI object. } From 700d51a45c614068393f9754d3ef366e2c05a884 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:12 -0400 Subject: [PATCH 2492/2892] docs/qapi-domain: add :since: directive option Add a little special markup for registering "Since:" information. Adding it as an option instead of generic content lets us hoist the information into the Signature bar, optionally put it in the index, etc. Signed-off-by: John Snow Message-ID: <20250311034303.75779-15-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 547040f75a..222b420d2a 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -214,6 +214,8 @@ class QAPIObject(QAPIDescription): { # Borrowed from the Python domain: "module": directives.unchanged, # Override contextual module name + # These are QAPI originals: + "since": directives.unchanged, } ) @@ -227,7 +229,17 @@ class QAPIObject(QAPIDescription): def get_signature_suffix(self) -> List[nodes.Node]: """Return a suffix to put after the object name in the signature.""" - return [] + ret: List[nodes.Node] = [] + + if "since" in self.options: + ret += [ + SpaceNode(" "), + addnodes.desc_sig_element( + "", f"(Since: {self.options['since']})" + ), + ] + + return ret def handle_signature(self, sig: str, signode: desc_signature) -> Signature: """ From 618379701b4127877d858aa6a792c8a329ec48bc Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:13 -0400 Subject: [PATCH 2493/2892] docs/qapi-domain: add "Arguments:" field lists This adds special rendering for Sphinx's typed info field lists. This patch does not add any QAPI-aware markup, rendering, or cross-referencing for the type names, yet. That feature requires a subclass to TypedField which will happen in its own commit quite a bit later in this series; after all the basic fields and objects have been established first. The syntax for this field is: :arg type name: description description cont'd You can omit the type or the description. You should not omit the name; if you do so, it degenerates into a "normal field list" entry, and probably isn't what you want. Signed-off-by: John Snow Message-ID: <20250311034303.75779-16-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 222b420d2a..b4289db6d8 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -33,6 +33,7 @@ from sphinx.domains import ( from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging +from sphinx.util.docfields import TypedField from sphinx.util.nodes import make_id, make_refnode @@ -273,7 +274,18 @@ class QAPIObject(QAPIDescription): class QAPICommand(QAPIObject): """Description of a QAPI Command.""" - # Nothing unique for now! Changed in later commits O:-) + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :arg TypeName ArgName: descr + TypedField( + "argument", + label=_("Arguments"), + names=("arg",), + can_collapse=False, + ), + ] + ) class QAPIModule(QAPIDescription): From 3d9a23f92f35afd16dfa5aaf52ede850de54560c Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:14 -0400 Subject: [PATCH 2494/2892] docs/qapi-domain: add "Features:" field lists Add support for Features field lists. There is no QAPI-specific functionality here, but this could be changed if desired (if we wanted the feature names to link somewhere, for instance.) This feature list doesn't have any restrictions, so it can be used to document object-wide features or per-member features as deemed appropriate. It's essentially free-form text. The syntax for this field is: :feat name: description description cont'd Signed-off-by: John Snow Message-ID: <20250311034303.75779-17-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index b4289db6d8..8ec4482b29 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -33,7 +33,7 @@ from sphinx.domains import ( from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging -from sphinx.util.docfields import TypedField +from sphinx.util.docfields import GroupedField, TypedField from sphinx.util.nodes import make_id, make_refnode @@ -220,6 +220,16 @@ class QAPIObject(QAPIDescription): } ) + doc_field_types = [ + # :feat name: descr + GroupedField( + "feature", + label=_("Features"), + names=("feat",), + can_collapse=False, + ), + ] + def get_signature_prefix(self) -> List[nodes.Node]: """Return a prefix to put before the object name in the signature.""" assert self.objtype From 9605c2047766367160304645b2db5464275d83d3 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:15 -0400 Subject: [PATCH 2495/2892] docs/qapi-domain: add "Errors:" field lists ``:error: descr`` can now be used to document error conditions. The format of the description is not defined here; so the ability to name specific types is left to the document writer. Signed-off-by: John Snow Message-ID: <20250311034303.75779-18-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 8ec4482b29..7535009078 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -33,7 +33,7 @@ from sphinx.domains import ( from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging -from sphinx.util.docfields import GroupedField, TypedField +from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.nodes import make_id, make_refnode @@ -294,6 +294,13 @@ class QAPICommand(QAPIObject): names=("arg",), can_collapse=False, ), + # :error: descr + Field( + "error", + label=_("Errors"), + names=("error", "errors"), + has_arg=False, + ), ] ) From 8b77f8d5730003d868b5748af5438c43d17f8c3a Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:16 -0400 Subject: [PATCH 2496/2892] docs/qapi-domain: add "Return:" field lists Add "Return:" field list syntax to QAPI Commands. Like "Arguments:" and "Errors:", the type name isn't currently processed for cross-referencing, but this will be addressed in a forthcoming commit. The syntax of the new field is: :return TypeName: description description cont'd This patch adds "Return" as a GroupedField, which means that multiple return values can be annotated - this is only done because Sphinx does not support mandatory type arguments to Ungrouped fields. Because we want to cross-reference this type information later, we want to make the type argument mandatory. As a result, you can technically add multiple :return: fields, though I'm not aware of any circumstance in which you'd need or want to. Recommendation: "Don't do that, then." The forthcoming QAPIDoc transmogrifier does not, in fact, ever "do that". Signed-off-by: John Snow Message-ID: <20250311034303.75779-19-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 7535009078..45e69689d1 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -301,6 +301,13 @@ class QAPICommand(QAPIObject): names=("error", "errors"), has_arg=False, ), + # :return TypeName: descr + GroupedField( + "returnvalue", + label=_("Return"), + names=("return",), + can_collapse=True, + ), ] ) From 902c9b0e34049283628c4bd00a6841e13b754fec Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:17 -0400 Subject: [PATCH 2497/2892] docs/qapi-domain: add qapi:enum directive Add the .. qapi:enum:: directive, object, and :qapi:enum:`name` cross-reference role. Add the :value name: field list for documenting Enum values. Of note, also introduce a new "type" role that is intended to be used by other QAPI object directives to cross-reference arbitrary QAPI type names, but will exclude commands, events, and modules from consideration. Signed-off-by: John Snow Message-ID: <20250311034303.75779-20-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 45e69689d1..e399474dc5 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -312,6 +312,23 @@ class QAPICommand(QAPIObject): ) +class QAPIEnum(QAPIObject): + """Description of a QAPI Enum.""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :value name: descr + GroupedField( + "value", + label=_("Values"), + names=("value",), + can_collapse=False, + ) + ] + ) + + class QAPIModule(QAPIDescription): """ Directive to mark description of a new module. @@ -431,9 +448,14 @@ class QAPIDomain(Domain): # This table associates cross-reference object types (key) with an # ObjType instance, which defines the valid cross-reference roles # for each object type. + # + # e.g., the :qapi:type: cross-reference role can refer to enum, + # struct, union, or alternate objects; but :qapi:obj: can refer to + # anything. Each object also gets its own targeted cross-reference role. object_types: Dict[str, ObjType] = { "module": ObjType(_("module"), "mod", "any"), "command": ObjType(_("command"), "cmd", "any"), + "enum": ObjType(_("enum"), "enum", "type", "any"), } # Each of these provides a rST directive, @@ -441,6 +463,7 @@ class QAPIDomain(Domain): directives = { "module": QAPIModule, "command": QAPICommand, + "enum": QAPIEnum, } # These are all cross-reference roles; e.g. @@ -449,6 +472,9 @@ class QAPIDomain(Domain): roles = { "mod": QAPIXRefRole(), "cmd": QAPIXRefRole(), + "enum": QAPIXRefRole(), + # reference any data type (excludes modules, commands, events) + "type": QAPIXRefRole(), "any": QAPIXRefRole(), # reference *any* type of QAPI object. } From bac3f1313c2d1dca49bd5499965d8cee0d7bb98f Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:18 -0400 Subject: [PATCH 2498/2892] docs/qapi-domain: add qapi:alternate directive Add the .. qapi:alternate:: directive, object, and qapi:alt:`name` cross-reference role. Add the "Alternatives:" field list for describing alternate choices. Like other field lists that reference QAPI types, a forthcoming commit will add cross-referencing support to this field. Signed-off-by: John Snow Message-ID: <20250311034303.75779-21-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index e399474dc5..506ed92700 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -329,6 +329,23 @@ class QAPIEnum(QAPIObject): ) +class QAPIAlternate(QAPIObject): + """Description of a QAPI Alternate.""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :alt type name: descr + TypedField( + "alternative", + label=_("Alternatives"), + names=("alt",), + can_collapse=False, + ), + ] + ) + + class QAPIModule(QAPIDescription): """ Directive to mark description of a new module. @@ -456,6 +473,7 @@ class QAPIDomain(Domain): "module": ObjType(_("module"), "mod", "any"), "command": ObjType(_("command"), "cmd", "any"), "enum": ObjType(_("enum"), "enum", "type", "any"), + "alternate": ObjType(_("alternate"), "alt", "type", "any"), } # Each of these provides a rST directive, @@ -464,6 +482,7 @@ class QAPIDomain(Domain): "module": QAPIModule, "command": QAPICommand, "enum": QAPIEnum, + "alternate": QAPIAlternate, } # These are all cross-reference roles; e.g. @@ -473,6 +492,7 @@ class QAPIDomain(Domain): "mod": QAPIXRefRole(), "cmd": QAPIXRefRole(), "enum": QAPIXRefRole(), + "alt": QAPIXRefRole(), # reference any data type (excludes modules, commands, events) "type": QAPIXRefRole(), "any": QAPIXRefRole(), # reference *any* type of QAPI object. From 6d5f6f69ca90557ec8a317a737e7b1e4ad1fff0d Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:19 -0400 Subject: [PATCH 2499/2892] docs/qapi-domain: add qapi:event directive Adds the .. qapi:event:: directive, object, and :qapi:event:`name` cross-referencing role. Adds the :memb type name: field list syntax for documenting event data members. As this syntax and phrasing will be shared with Structs and Unions as well, add the field list definition to a shared abstract class. As per usual, QAPI cross-referencing for types in the member field list will be added in a forthcoming commit. Signed-off-by: John Snow Message-ID: <20250311034303.75779-22-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 506ed92700..3ffb3eb72d 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -346,6 +346,27 @@ class QAPIAlternate(QAPIObject): ) +class QAPIObjectWithMembers(QAPIObject): + """Base class for Events/Structs/Unions""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :member type name: descr + TypedField( + "member", + label=_("Members"), + names=("memb",), + can_collapse=False, + ), + ] + ) + + +class QAPIEvent(QAPIObjectWithMembers): + """Description of a QAPI Event.""" + + class QAPIModule(QAPIDescription): """ Directive to mark description of a new module. @@ -472,6 +493,7 @@ class QAPIDomain(Domain): object_types: Dict[str, ObjType] = { "module": ObjType(_("module"), "mod", "any"), "command": ObjType(_("command"), "cmd", "any"), + "event": ObjType(_("event"), "event", "any"), "enum": ObjType(_("enum"), "enum", "type", "any"), "alternate": ObjType(_("alternate"), "alt", "type", "any"), } @@ -481,6 +503,7 @@ class QAPIDomain(Domain): directives = { "module": QAPIModule, "command": QAPICommand, + "event": QAPIEvent, "enum": QAPIEnum, "alternate": QAPIAlternate, } @@ -491,6 +514,7 @@ class QAPIDomain(Domain): roles = { "mod": QAPIXRefRole(), "cmd": QAPIXRefRole(), + "event": QAPIXRefRole(), "enum": QAPIXRefRole(), "alt": QAPIXRefRole(), # reference any data type (excludes modules, commands, events) From 3fe3349d232cdd8ffd3eef31f8c5ba8e7e08094a Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:20 -0400 Subject: [PATCH 2500/2892] docs/qapi-domain: add qapi:object directive Adds the .. qapi:object:: directive, object, and :qapi:obj:`name` cross-referencing role. This directive is meant to document both structs and unions. As per usual, QAPI cross-referencing for types in the member field list will be added in a forthcoming commit. Signed-off-by: John Snow Message-ID: <20250311034303.75779-23-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 3ffb3eb72d..b11300bc85 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -367,6 +367,10 @@ class QAPIEvent(QAPIObjectWithMembers): """Description of a QAPI Event.""" +class QAPIJSONObject(QAPIObjectWithMembers): + """Description of a QAPI Object: structs and unions.""" + + class QAPIModule(QAPIDescription): """ Directive to mark description of a new module. @@ -495,6 +499,7 @@ class QAPIDomain(Domain): "command": ObjType(_("command"), "cmd", "any"), "event": ObjType(_("event"), "event", "any"), "enum": ObjType(_("enum"), "enum", "type", "any"), + "object": ObjType(_("object"), "obj", "type", "any"), "alternate": ObjType(_("alternate"), "alt", "type", "any"), } @@ -505,6 +510,7 @@ class QAPIDomain(Domain): "command": QAPICommand, "event": QAPIEvent, "enum": QAPIEnum, + "object": QAPIJSONObject, "alternate": QAPIAlternate, } @@ -516,6 +522,7 @@ class QAPIDomain(Domain): "cmd": QAPIXRefRole(), "event": QAPIXRefRole(), "enum": QAPIXRefRole(), + "obj": QAPIXRefRole(), # specifically structs and unions. "alt": QAPIXRefRole(), # reference any data type (excludes modules, commands, events) "type": QAPIXRefRole(), From 1a0c090a5bb3f7bd526224cd166703d6c80ab1ee Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:21 -0400 Subject: [PATCH 2501/2892] docs/qapi-domain: add :deprecated: directive option Although "deprecated" is a feature (and *will* appear in the features list), add a special :deprecated: option to generate an eye-catch that makes this information very hard to miss. The forthcoming Transmogrifier in qapidoc.py will add this option whenever it detects that the features list attached to a definition contains the "deprecated" entry. P.S., I outsourced the CSS ;) Signed-off-by: Harmonie Snow Signed-off-by: John Snow Message-ID: <20250311034303.75779-24-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx-static/theme_overrides.css | 25 +++++++++++++++++++++++++ docs/sphinx/qapi_domain.py | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index 965ecac54f..3765cab1b2 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -208,3 +208,28 @@ div[class^="highlight"] pre { color: inherit; } } + +/* QAPI domain theming */ + +.qapi-infopips { + margin-bottom: 1em; +} + +.qapi-infopip { + display: inline-block; + padding: 0em 0.5em 0em 0.5em; + margin: 0.25em; +} + +.qapi-deprecated { + background-color: #fffef5; + border: solid #fff176 6px; + font-weight: bold; + padding: 8px; + border-radius: 15px; + margin: 5px; +} + +.qapi-deprecated::before { + content: '⚠️ '; +} diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index b11300bc85..b672ae6c50 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -217,6 +217,7 @@ class QAPIObject(QAPIDescription): "module": directives.unchanged, # Override contextual module name # These are QAPI originals: "since": directives.unchanged, + "deprecated": directives.flag, } ) @@ -280,6 +281,31 @@ class QAPIObject(QAPIDescription): return sig + def _add_infopips(self, contentnode: addnodes.desc_content) -> None: + # Add various eye-catches and things that go below the signature + # bar, but precede the user-defined content. + infopips = nodes.container() + infopips.attributes["classes"].append("qapi-infopips") + + def _add_pip(source: str, content: str, classname: str) -> None: + node = nodes.container(source) + node.append(nodes.Text(content)) + node.attributes["classes"].extend(["qapi-infopip", classname]) + infopips.append(node) + + if "deprecated" in self.options: + _add_pip( + ":deprecated:", + f"This {self.objtype} is deprecated.", + "qapi-deprecated", + ) + + if infopips.children: + contentnode.insert(0, infopips) + + def transform_content(self, content_node: addnodes.desc_content) -> None: + self._add_infopips(content_node) + class QAPICommand(QAPIObject): """Description of a QAPI Command.""" From d25808c2bc7921e5cd245111212ad7e3b6da3849 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:22 -0400 Subject: [PATCH 2502/2892] docs/qapi-domain: add :unstable: directive option Although "unstable" is a feature (and *will* appear in the features list), add a special :unstable: option to generate an eye-catch that makes this information very hard to miss. The forthcoming Transmogrifier in qapidoc.py will add this option whenever it detects that the features list attached to a definition contains the "unstable" entry. Signed-off-by: Harmonie Snow Signed-off-by: John Snow Message-ID: <20250311034303.75779-25-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx-static/theme_overrides.css | 6 +++++- docs/sphinx/qapi_domain.py | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index 3765cab1b2..5f58f1d524 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -221,7 +221,7 @@ div[class^="highlight"] pre { margin: 0.25em; } -.qapi-deprecated { +.qapi-deprecated,.qapi-unstable { background-color: #fffef5; border: solid #fff176 6px; font-weight: bold; @@ -230,6 +230,10 @@ div[class^="highlight"] pre { margin: 5px; } +.qapi-unstable::before { + content: '🚧 '; +} + .qapi-deprecated::before { content: '⚠️ '; } diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index b672ae6c50..00fd11ebf7 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -218,6 +218,7 @@ class QAPIObject(QAPIDescription): # These are QAPI originals: "since": directives.unchanged, "deprecated": directives.flag, + "unstable": directives.flag, } ) @@ -300,6 +301,13 @@ class QAPIObject(QAPIDescription): "qapi-deprecated", ) + if "unstable" in self.options: + _add_pip( + ":unstable:", + f"This {self.objtype} is unstable/experimental.", + "qapi-unstable", + ) + if infopips.children: contentnode.insert(0, infopips) From 6a41330206e0df32b93c371b551f89d393eda2c3 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:23 -0400 Subject: [PATCH 2503/2892] docs/qapi-domain: add :ifcond: directive option Add a special :ifcond: option that allows us to annotate the definition-level conditionals. The syntax of the argument is currently undefined, but it's possible we can apply better formatting in the future. Currently, we just display the ifcond string as preformatted text. Signed-off-by: Harmonie Snow Signed-off-by: John Snow Message-ID: <20250311034303.75779-26-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx-static/theme_overrides.css | 13 +++++++++++++ docs/sphinx/qapi_domain.py | 23 +++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index 5f58f1d524..3fd326613d 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -237,3 +237,16 @@ div[class^="highlight"] pre { .qapi-deprecated::before { content: '⚠️ '; } + +.qapi-ifcond::before { + /* gaze ye into the crystal ball to determine feature availability */ + content: '🔮 '; +} + +.qapi-ifcond { + background-color: #f9f5ff; + border: solid #dac2ff 6px; + padding: 8px; + border-radius: 15px; + margin: 5px; +} diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 00fd11ebf7..4531b5d857 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -14,6 +14,7 @@ from typing import ( NamedTuple, Optional, Tuple, + Union, cast, ) @@ -217,6 +218,7 @@ class QAPIObject(QAPIDescription): "module": directives.unchanged, # Override contextual module name # These are QAPI originals: "since": directives.unchanged, + "ifcond": directives.unchanged, "deprecated": directives.flag, "unstable": directives.flag, } @@ -288,9 +290,14 @@ class QAPIObject(QAPIDescription): infopips = nodes.container() infopips.attributes["classes"].append("qapi-infopips") - def _add_pip(source: str, content: str, classname: str) -> None: + def _add_pip( + source: str, content: Union[str, List[nodes.Node]], classname: str + ) -> None: node = nodes.container(source) - node.append(nodes.Text(content)) + if isinstance(content, str): + node.append(nodes.Text(content)) + else: + node.extend(content) node.attributes["classes"].extend(["qapi-infopip", classname]) infopips.append(node) @@ -308,6 +315,18 @@ class QAPIObject(QAPIDescription): "qapi-unstable", ) + if self.options.get("ifcond", ""): + ifcond = self.options["ifcond"] + _add_pip( + f":ifcond: {ifcond}", + [ + nodes.emphasis("", "Availability"), + nodes.Text(": "), + nodes.literal(ifcond, ifcond), + ], + "qapi-ifcond", + ) + if infopips.children: contentnode.insert(0, infopips) From ef137a224192cf145c8ae207fe96fd77b63596a9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:24 -0400 Subject: [PATCH 2504/2892] docs/qapi-domain: add warnings for malformed field lists Normally, Sphinx will silently fall back to its standard field list processing if it doesn't match one of your defined fields. A lot of the time, that's not what we want - we want to be warned if we goof something up. For instance, the canonical argument field list form is: :arg type name: descr This form is captured by Sphinx and transformed so that the field label will become "Arguments:". It's possible to omit the type name and descr and still have it be processed correctly. However, if you omit the type name, Sphinx no longer recognizes it: :arg: this is not recognized. This will turn into an arbitrary field list entry whose label is "Arg:", and it otherwise silently fails. You may also see failures for doing things like using :values: instead of :value:, or :errors: instead of :error:, and so on. It's also case sensitive, and easy to trip up. Add a validator that guarantees all field list entries that are the direct child of an ObjectDescription use only recognized forms of field lists, and emit a warning (treated as error by default in most build configurations) whenever we detect one that is goofed up. However, there's still benefit to allowing arbitrary fields -- they are after all not a Sphinx invention, but perfectly normal docutils syntax. Create an allow list for known spellings we don't mind letting through, but warn against anything else. Signed-off-by: John Snow Message-ID: <20250311034303.75779-27-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/conf.py | 9 +++++ docs/sphinx/qapi_domain.py | 74 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 49d9de894c..a3f9fa63d9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -153,6 +153,15 @@ rst_epilog = ".. |CONFDIR| replace:: ``" + confdir + "``\n" with open(os.path.join(qemu_docdir, 'defs.rst.inc')) as f: rst_epilog += f.read() + +# Normally, the QAPI domain is picky about what field lists you use to +# describe a QAPI entity. If you'd like to use arbitrary additional +# fields in source documentation, add them here. +qapi_allowed_fields = { + "see also", +} + + # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 4531b5d857..9fe006eef3 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -49,6 +49,19 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) +def _unpack_field( + field: nodes.Node, +) -> Tuple[nodes.field_name, nodes.field_body]: + """ + docutils helper: unpack a field node in a type-safe manner. + """ + assert isinstance(field, nodes.field) + assert len(field.children) == 2 + assert isinstance(field.children[0], nodes.field_name) + assert isinstance(field.children[1], nodes.field_body) + return (field.children[0], field.children[1]) + + class ObjectEntry(NamedTuple): docname: str node_id: str @@ -330,9 +343,64 @@ class QAPIObject(QAPIDescription): if infopips.children: contentnode.insert(0, infopips) + def _validate_field(self, field: nodes.field) -> None: + """Validate field lists in this QAPI Object Description.""" + name, _ = _unpack_field(field) + allowed_fields = set(self.env.app.config.qapi_allowed_fields) + + field_label = name.astext() + if field_label in allowed_fields: + # Explicitly allowed field list name, OK. + return + + try: + # split into field type and argument (if provided) + # e.g. `:arg type name: descr` is + # field_type = "arg", field_arg = "type name". + field_type, field_arg = field_label.split(None, 1) + except ValueError: + # No arguments provided + field_type = field_label + field_arg = "" + + typemap = self.get_field_type_map() + if field_type in typemap: + # This is a special docfield, yet-to-be-processed. Catch + # correct names, but incorrect arguments. This mismatch WILL + # cause Sphinx to render this field incorrectly (without a + # warning), which is never what we want. + typedesc = typemap[field_type][0] + if typedesc.has_arg != bool(field_arg): + msg = f"docfield field list type {field_type!r} " + if typedesc.has_arg: + msg += "requires an argument." + else: + msg += "takes no arguments." + logger.warning(msg, location=field) + else: + # This is unrecognized entirely. It's valid rST to use + # arbitrary fields, but let's ensure the documentation + # writer has done this intentionally. + valid = ", ".join(sorted(set(typemap) | allowed_fields)) + msg = ( + f"Unrecognized field list name {field_label!r}.\n" + f"Valid fields for qapi:{self.objtype} are: {valid}\n" + "\n" + "If this usage is intentional, please add it to " + "'qapi_allowed_fields' in docs/conf.py." + ) + logger.warning(msg, location=field) + def transform_content(self, content_node: addnodes.desc_content) -> None: self._add_infopips(content_node) + # Validate field lists. + for child in content_node: + if isinstance(child, nodes.field_list): + for field in child.children: + assert isinstance(field, nodes.field) + self._validate_field(field) + class QAPICommand(QAPIObject): """Description of a QAPI Command.""" @@ -769,6 +837,12 @@ class QAPIDomain(Domain): def setup(app: Sphinx) -> Dict[str, Any]: app.setup_extension("sphinx.directives") + app.add_config_value( + "qapi_allowed_fields", + set(), + "env", # Setting impacts parsing phase + types=set, + ) app.add_domain(QAPIDomain) return { From 03947c80ce81702ea0ba85933bd380ec978c7635 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:25 -0400 Subject: [PATCH 2505/2892] docs/qapi-domain: add type cross-refs to field lists This commit, finally, adds cross-referencing support to various field lists; modeled tightly after Sphinx's own Python domain code. Cross-referencing support is added to type names provided to :arg:, :memb:, :returns: and :choice:. :feat:, :error: and :value:, which do not take type names, do not support this syntax. The general syntax is simple: :arg TypeName ArgName: Lorem Ipsum ... The domain will transform TypeName into :qapi:type:`TypeName` in this basic case, and also apply the ``literal`` decoration to indicate that this is a type cross-reference. For optional arguments, the special "?" suffix is used. Because "*" has special meaning in rST that would cause parsing errors, we elect to use "?" instead. The special syntax processing strips this character from the end of any type name argument and will append ", optional" to the rendered output, applying the cross-reference only to the actual type name. The intent here is that the actual syntax in doc-blocks need not change; but e.g. qapidoc.py will need to process and transform "@arg foo lorem ipsum" into ":arg type? foo: lorem ipsum" based on the schema information. Therefore, nobody should ever actually witness this intermediate syntax unless they are writing manual documentation or the doc transmogrifier breaks. For array arguments, type names can similarly be surrounded by "[]", which are stripped off and then re-appended outside of the cross-reference. Signed-off-by: John Snow Message-ID: <20250311034303.75779-28-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 9fe006eef3..06fe78ce0b 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -2,6 +2,9 @@ QAPI domain extension. """ +# The best laid plans of mice and men, ... +# pylint: disable=too-many-lines + from __future__ import annotations from typing import ( @@ -116,6 +119,28 @@ class QAPIXRefRole(XRefRole): return title, target + def result_nodes( + self, + document: nodes.document, + env: BuildEnvironment, + node: Element, + is_ref: bool, + ) -> Tuple[List[nodes.Node], List[nodes.system_message]]: + + # node here is the pending_xref node (or whatever nodeclass was + # configured at XRefRole class instantiation time). + results: List[nodes.Node] = [node] + + if node.get("qapi:array"): + results.insert(0, nodes.literal("[", "[")) + results.append(nodes.literal("]", "]")) + + if node.get("qapi:optional"): + results.append(nodes.Text(", ")) + results.append(nodes.emphasis("?", "optional")) + + return results, [] + # Alias for the return of handle_signature(), which is used in several places. # (In the Python domain, this is Tuple[str, str] instead.) @@ -413,6 +438,7 @@ class QAPICommand(QAPIObject): "argument", label=_("Arguments"), names=("arg",), + typerolename="type", can_collapse=False, ), # :error: descr @@ -426,6 +452,7 @@ class QAPICommand(QAPIObject): GroupedField( "returnvalue", label=_("Return"), + rolename="type", names=("return",), can_collapse=True, ), @@ -461,6 +488,7 @@ class QAPIAlternate(QAPIObject): "alternative", label=_("Alternatives"), names=("alt",), + typerolename="type", can_collapse=False, ), ] @@ -478,6 +506,7 @@ class QAPIObjectWithMembers(QAPIObject): "member", label=_("Members"), names=("memb",), + typerolename="type", can_collapse=False, ), ] From 282c8d256efba2c348ddafaa6fcfa7cca371c309 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:26 -0400 Subject: [PATCH 2506/2892] docs/qapi-domain: add CSS styling Improve the general look and feel of generated QAPI docs. Attempt to limit line lengths to offer a more comfortable measure on maximized windows, and improve some margin and spacing for field lists. Signed-off-by: Harmonie Snow Signed-off-by: John Snow Message-ID: <20250311034303.75779-29-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx-static/theme_overrides.css | 56 +++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index 3fd326613d..b225bf706f 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -18,8 +18,8 @@ h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend { .rst-content dl:not(.docutils) dt { border-top: none; - border-left: solid 3px #ccc; - background-color: #f0f0f0; + border-left: solid 5px #bcc6d2; + background-color: #eaedf1; color: black; } @@ -211,6 +211,18 @@ div[class^="highlight"] pre { /* QAPI domain theming */ +/* most content in a QAPI object definition should not eclipse about + 80ch, but nested field lists are explicitly exempt due to their + two-column nature */ +.qapi dd *:not(dl) { + max-width: 80ch; +} + +/* but the content column itself should still be less than ~80ch. */ +.qapi .field-list dd { + max-width: 80ch; +} + .qapi-infopips { margin-bottom: 1em; } @@ -250,3 +262,43 @@ div[class^="highlight"] pre { border-radius: 15px; margin: 5px; } + +/* code blocks */ +.qapi div[class^="highlight"] { + width: fit-content; + background-color: #fffafd; + border: 2px solid #ffe1f3; +} + +/* note, warning, etc. */ +.qapi .admonition { + width: fit-content; +} + +/* pad the top of the field-list so the text doesn't start directly at + the top border; primarily for the field list labels, but adjust the + field bodies as well for parity. */ +dl.field-list > dt:first-of-type, dl.field-list > dd:first-of-type { + padding-top: 0.3em; +} + +dl.field-list > dt:last-of-type, dl.field-list > dd:last-of-type { + padding-bottom: 0.3em; +} + +/* pad the field list labels so they don't crash into the border */ +dl.field-list > dt { + padding-left: 0.5em; + padding-right: 0.5em; +} + +/* Add a little padding between field list sections */ +dl.field-list > dd:not(:last-child) { + padding-bottom: 1em; +} + +/* Sphinx 3.x: unresolved xrefs */ +.rst-content *:not(a) > code.xref { + font-weight: 400; + color: #333333; +} From a1fe2cd443e6ae9d5211053d2ac0ca83264dc950 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:27 -0400 Subject: [PATCH 2507/2892] docs/qapi-domain: add XREF compatibility goop for Sphinx < 4.1 Sphinx < 4.1 handles cross-references ... differently. Factor out and isolate the compatibility goop we need to make cross references work properly in old versions of Sphinx. Yes, it's ugly. Yes, it works. No, I don't want to talk about it. Understand that this patch exists because of the overflowing love in my heart. Signed-off-by: John Snow Message-ID: <20250311034303.75779-30-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/compat.py | 136 +++++++++++++++++++++++++++++++++++-- docs/sphinx/qapi_domain.py | 23 ++++--- 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/docs/sphinx/compat.py b/docs/sphinx/compat.py index 6bc698c5ad..f068d70388 100644 --- a/docs/sphinx/compat.py +++ b/docs/sphinx/compat.py @@ -2,14 +2,31 @@ Sphinx cross-version compatibility goop """ -from typing import Callable +import re +from typing import ( + Any, + Callable, + Optional, + Type, +) +from docutils import nodes from docutils.nodes import Element, Node, Text import sphinx -from sphinx import addnodes -from sphinx.util import nodes -from sphinx.util.docutils import SphinxDirective, switch_source_input +from sphinx import addnodes, util +from sphinx.environment import BuildEnvironment +from sphinx.roles import XRefRole +from sphinx.util import docfields +from sphinx.util.docutils import ( + ReferenceRole, + SphinxDirective, + switch_source_input, +) +from sphinx.util.typing import TextlikeNode + + +MAKE_XREF_WORKAROUND = sphinx.version_info[:3] < (4, 1, 0) SpaceNode: Callable[[str], Node] @@ -36,7 +53,7 @@ def nested_parse_with_titles( try: # Modern sphinx (6.2.0+) supports proper offsetting for # nested parse error context management - nodes.nested_parse_with_titles( + util.nodes.nested_parse_with_titles( directive.state, directive.content, content_node, @@ -45,6 +62,113 @@ def nested_parse_with_titles( except TypeError: # No content_offset argument. Fall back to SSI method. with switch_source_input(directive.state, directive.content): - nodes.nested_parse_with_titles( + util.nodes.nested_parse_with_titles( directive.state, directive.content, content_node ) + + +# ########################################### +# xref compatibility hacks for Sphinx < 4.1 # +# ########################################### + +# When we require >= Sphinx 4.1, the following function and the +# subsequent 3 compatibility classes can be removed. Anywhere in +# qapi_domain that uses one of these Compat* types can be switched to +# using the garden-variety lib-provided classes with no trickery. + + +def _compat_make_xref( # pylint: disable=unused-argument + self: sphinx.util.docfields.Field, + rolename: str, + domain: str, + target: str, + innernode: Type[TextlikeNode] = addnodes.literal_emphasis, + contnode: Optional[Node] = None, + env: Optional[BuildEnvironment] = None, + inliner: Any = None, + location: Any = None, +) -> Node: + """ + Compatibility workaround for Sphinx versions prior to 4.1.0. + + Older sphinx versions do not use the domain's XRefRole for parsing + and formatting cross-references, so we need to perform this magick + ourselves to avoid needing to write the parser/formatter in two + separate places. + + This workaround isn't brick-for-brick compatible with modern Sphinx + versions, because we do not have access to the parent directive's + state during this parsing like we do in more modern versions. + + It's no worse than what pre-Sphinx 4.1.0 does, so... oh well! + """ + + # Yes, this function is gross. Pre-4.1 support is a miracle. + # pylint: disable=too-many-locals + + assert env + # Note: Sphinx's own code ignores the type warning here, too. + if not rolename: + return contnode or innernode(target, target) # type: ignore[call-arg] + + # Get the role instance, but don't *execute it* - we lack the + # correct state to do so. Instead, we'll just use its public + # methods to do our reference formatting, and emulate the rest. + role = env.get_domain(domain).roles[rolename] + assert isinstance(role, XRefRole) + + # XRefRole features not supported by this compatibility shim; + # these were not supported in Sphinx 3.x either, so nothing of + # value is really lost. + assert not target.startswith("!") + assert not re.match(ReferenceRole.explicit_title_re, target) + assert not role.lowercase + assert not role.fix_parens + + # Code below based mostly on sphinx.roles.XRefRole; run() and + # create_xref_node() + options = { + "refdoc": env.docname, + "refdomain": domain, + "reftype": rolename, + "refexplicit": False, + "refwarn": role.warn_dangling, + } + refnode = role.nodeclass(target, **options) + title, target = role.process_link(env, refnode, False, target, target) + refnode["reftarget"] = target + classes = ["xref", domain, f"{domain}-{rolename}"] + refnode += role.innernodeclass(target, title, classes=classes) + + # This is the very gross part of the hack. Normally, + # result_nodes takes a document object to which we would pass + # self.inliner.document. Prior to Sphinx 4.1, we don't *have* an + # inliner to pass, so we have nothing to pass here. However, the + # actual implementation of role.result_nodes in this case + # doesn't actually use that argument, so this winds up being + # ... fine. Rest easy at night knowing this code only runs under + # old versions of Sphinx, so at least it won't change in the + # future on us and lead to surprising new failures. + # Gross, I know. + result_nodes, _messages = role.result_nodes( + None, # type: ignore + env, + refnode, + is_ref=True, + ) + return nodes.inline(target, "", *result_nodes) + + +class CompatField(docfields.Field): + if MAKE_XREF_WORKAROUND: + make_xref = _compat_make_xref + + +class CompatGroupedField(docfields.GroupedField): + if MAKE_XREF_WORKAROUND: + make_xref = _compat_make_xref + + +class CompatTypedField(docfields.TypedField): + if MAKE_XREF_WORKAROUND: + make_xref = _compat_make_xref diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 06fe78ce0b..3b1490e29a 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -24,7 +24,13 @@ from typing import ( from docutils import nodes from docutils.parsers.rst import directives -from compat import KeywordNode, SpaceNode +from compat import ( + CompatField, + CompatGroupedField, + CompatTypedField, + KeywordNode, + SpaceNode, +) from sphinx import addnodes from sphinx.addnodes import desc_signature, pending_xref from sphinx.directives import ObjectDescription @@ -37,7 +43,6 @@ from sphinx.domains import ( from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging -from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.nodes import make_id, make_refnode @@ -264,7 +269,7 @@ class QAPIObject(QAPIDescription): doc_field_types = [ # :feat name: descr - GroupedField( + CompatGroupedField( "feature", label=_("Features"), names=("feat",), @@ -434,7 +439,7 @@ class QAPICommand(QAPIObject): doc_field_types.extend( [ # :arg TypeName ArgName: descr - TypedField( + CompatTypedField( "argument", label=_("Arguments"), names=("arg",), @@ -442,14 +447,14 @@ class QAPICommand(QAPIObject): can_collapse=False, ), # :error: descr - Field( + CompatField( "error", label=_("Errors"), names=("error", "errors"), has_arg=False, ), # :return TypeName: descr - GroupedField( + CompatGroupedField( "returnvalue", label=_("Return"), rolename="type", @@ -467,7 +472,7 @@ class QAPIEnum(QAPIObject): doc_field_types.extend( [ # :value name: descr - GroupedField( + CompatGroupedField( "value", label=_("Values"), names=("value",), @@ -484,7 +489,7 @@ class QAPIAlternate(QAPIObject): doc_field_types.extend( [ # :alt type name: descr - TypedField( + CompatTypedField( "alternative", label=_("Alternatives"), names=("alt",), @@ -502,7 +507,7 @@ class QAPIObjectWithMembers(QAPIObject): doc_field_types.extend( [ # :member type name: descr - TypedField( + CompatTypedField( "member", label=_("Members"), names=("memb",), From d48a8f8de3f2c5609ecd362f1d1c5c1ba60161fc Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:28 -0400 Subject: [PATCH 2508/2892] docs/qapi-domain: warn when QAPI domain xrefs fail to resolve This patch adds a warning (which is a build failure under our current build settings) whenever a QAPI cross-reference fails to resolve. This applies to any cross-references of the form :qapi:{role}:`foo`, which covers all of the automatically generated references by the qapi domain, and any such references that are manually written into the documentation rst files. Cross-references of the form `foo` do not use this system, but are already configured to issue a warning (Again, a build failure) if the cross-reference isn't found anywhere. Adds warnings that look like the following: docs/qapi/index.rst:48: WARNING: qapi:type reference target not found: 'footype' [ref.qapi] docs/qapi/index.rst:50: WARNING: qapi:mod reference target not found: 'foomod' [ref.qapi] Signed-off-by: John Snow Message-ID: <20250311034303.75779-31-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 3b1490e29a..b23db1eba2 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -830,6 +830,29 @@ class QAPIDomain(Domain): matches = self.find_obj(modname, target, typ) if not matches: + # Normally, we could pass warn_dangling=True to QAPIXRefRole(), + # but that will trigger on references to these built-in types, + # which we'd like to ignore instead. + + # Take care of that warning here instead, so long as the + # reference isn't to one of our built-in core types. + if target not in ( + "string", + "number", + "int", + "boolean", + "null", + "value", + "q_empty", + ): + logger.warning( + __("qapi:%s reference target not found: %r"), + typ, + target, + type="ref", + subtype="qapi", + location=node, + ) return None if len(matches) > 1: From 707f2bbb7899297884095a76a1237c8dbfce09fd Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:29 -0400 Subject: [PATCH 2509/2892] docs/qapi-domain: Fix error context reporting in Sphinx 5.x and 6.x Sphinx 5.3.0 to Sphinx 6.2.0 has a bug where nested content in an ObjectDescription content block has its error position reported incorrectly due to an oversight when they added nested section support to this directive. (This bug is present in Sphinx's own Python and C domains; test it yourself by creating a py:func directive and creating a syntax error in the directive's content block. The reporting will be incorrect.) To avoid overriding and re-implementing the entirety of the run() method, a workaround is employed where we parse the content block ourselves in before_content(), then null the content block to make Sphinx's own parsing a no-op. Then, in transform_content (which occurs after Sphinx's nested parse), we simply swap our own parsed content tree back in for Sphinx's. It appears a little tricky, but it's the nicest solution I can find. Signed-off-by: John Snow Message-ID: <20250311034303.75779-32-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/compat.py | 56 ++++++++++++++++++++++++++++++++++++++ docs/sphinx/qapi_domain.py | 15 ++++++---- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/docs/sphinx/compat.py b/docs/sphinx/compat.py index f068d70388..9cf7fe006e 100644 --- a/docs/sphinx/compat.py +++ b/docs/sphinx/compat.py @@ -4,6 +4,7 @@ Sphinx cross-version compatibility goop import re from typing import ( + TYPE_CHECKING, Any, Callable, Optional, @@ -12,9 +13,11 @@ from typing import ( from docutils import nodes from docutils.nodes import Element, Node, Text +from docutils.statemachine import StringList import sphinx from sphinx import addnodes, util +from sphinx.directives import ObjectDescription from sphinx.environment import BuildEnvironment from sphinx.roles import XRefRole from sphinx.util import docfields @@ -172,3 +175,56 @@ class CompatGroupedField(docfields.GroupedField): class CompatTypedField(docfields.TypedField): if MAKE_XREF_WORKAROUND: make_xref = _compat_make_xref + + +# ################################################################ +# Nested parsing error location fix for Sphinx 5.3.0 < x < 6.2.0 # +# ################################################################ + +# When we require Sphinx 4.x, the TYPE_CHECKING hack where we avoid +# subscripting ObjectDescription at runtime can be removed in favor of +# just always subscripting the class. + +# When we require Sphinx > 6.2.0, the rest of this compatibility hack +# can be dropped and QAPIObject can just inherit directly from +# ObjectDescription[Signature]. + +SOURCE_LOCATION_FIX = (5, 3, 0) <= sphinx.version_info[:3] < (6, 2, 0) + +Signature = str + + +if TYPE_CHECKING: + _BaseClass = ObjectDescription[Signature] +else: + _BaseClass = ObjectDescription + + +class ParserFix(_BaseClass): + + _temp_content: StringList + _temp_offset: int + _temp_node: Optional[addnodes.desc_content] + + def before_content(self) -> None: + # Work around a sphinx bug and parse the content ourselves. + self._temp_content = self.content + self._temp_offset = self.content_offset + self._temp_node = None + + if SOURCE_LOCATION_FIX: + self._temp_node = addnodes.desc_content() + self.state.nested_parse( + self.content, self.content_offset, self._temp_node + ) + # Sphinx will try to parse the content block itself, + # Give it nothingness to parse instead. + self.content = StringList() + self.content_offset = 0 + + def transform_content(self, content_node: addnodes.desc_content) -> None: + # Sphinx workaround: Inject our parsed content and restore state. + if self._temp_node: + content_node += self._temp_node.children + self.content = self._temp_content + self.content_offset = self._temp_offset diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index b23db1eba2..ca3f3a7e2d 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -29,6 +29,8 @@ from compat import ( CompatGroupedField, CompatTypedField, KeywordNode, + ParserFix, + Signature, SpaceNode, ) from sphinx import addnodes @@ -147,12 +149,7 @@ class QAPIXRefRole(XRefRole): return results, [] -# Alias for the return of handle_signature(), which is used in several places. -# (In the Python domain, this is Tuple[str, str] instead.) -Signature = str - - -class QAPIDescription(ObjectDescription[Signature]): +class QAPIDescription(ParserFix): """ Generic QAPI description. @@ -422,6 +419,10 @@ class QAPIObject(QAPIDescription): logger.warning(msg, location=field) def transform_content(self, content_node: addnodes.desc_content) -> None: + # This hook runs after before_content and the nested parse, but + # before the DocFieldTransformer is executed. + super().transform_content(content_node) + self._add_infopips(content_node) # Validate field lists. @@ -519,10 +520,12 @@ class QAPIObjectWithMembers(QAPIObject): class QAPIEvent(QAPIObjectWithMembers): + # pylint: disable=too-many-ancestors """Description of a QAPI Event.""" class QAPIJSONObject(QAPIObjectWithMembers): + # pylint: disable=too-many-ancestors """Description of a QAPI Object: structs and unions.""" From faeacf858bd9529cab10a13ff9d2137c8f2ae17c Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:30 -0400 Subject: [PATCH 2510/2892] qapi/parser: adjust info location for doc body section Instead of using the info object for the doc block as a whole (which always points to the very first line of the block), update the info pointer for each call to ensure_untagged_section when the existing section is otherwise empty. This way, Sphinx error information will match precisely to where the text actually starts. For example, this patch will move the info pointer for the "Hello!" untagged section ... > ## <-- from here ... > # Hello! <-- ... to here. > ## This doesn't seem to improve error reporting now. It will with the forthcoming QAPI doc transmogrifier. If I stick bad rST into qapi/block-core.json like this: > ## > # @SnapshotInfo: > # > +# rST syntax error: *ahh! > +# > # @id: unique shapshot id > # > # @name: user chosen name The existing code's error message will point to the beginning of the doc comment, which is less than helpful. The transmogrifier's message will point to the erroneous line, but to accomplish this, it needs this patch. Signed-off-by: John Snow Message-ID: <20250311034303.75779-33-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 64f0bb824a..97def9f0e4 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -686,7 +686,11 @@ class QAPIDoc: def ensure_untagged_section(self, info: QAPISourceInfo) -> None: if self.all_sections and not self.all_sections[-1].tag: # extend current section - self.all_sections[-1].text += '\n' + section = self.all_sections[-1] + if not section.text: + # Section is empty so far; update info to start *here*. + section.info = info + section.text += '\n' return # start new section section = self.Section(info) From 323c668934a650673548088c6718c633b57b6ce5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:31 -0400 Subject: [PATCH 2511/2892] qapi: clean up encoding of section kinds We have several kinds of sections, and to tell them apart, we use Section attribute @tag and also the section object's Python type: type @tag untagged Section None @foo: ArgSection 'foo' Returns: Section 'Returns' Errors: Section 'Errors' Since: Section 'Since' TODO: Section 'TODO' Note: * @foo can be a member or a feature description, depending on context. * tag == 'Since' can be a Since: section or a member or feature description. If it's a Section, it's the former, and if it's an ArgSection, it's the latter. Clean this up as follows. Move the member or feature name to new ArgSection attribute @name, and replace @tag by enum @kind like this: type kind name untagged Section PLAIN @foo: ArgSection MEMBER 'foo' if member or argument ArgSection FEATURE 'foo' if feature Returns: Section RETURNS Errors: Section ERRORS Since: Section SINCE TODO: Section TODO The qapi-schema tests are updated to account for the new section names; "TODO" becomes "Todo" and `None` becomes "Plain" there. Signed-off-by: John Snow Message-ID: <20250311034303.75779-34-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 7 +-- scripts/qapi/parser.py | 97 ++++++++++++++++++++++++---------- tests/qapi-schema/doc-good.out | 10 ++-- tests/qapi-schema/test-qapi.py | 2 +- 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 61997fd21a..d622398f1d 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -35,6 +35,7 @@ from docutils.parsers.rst import Directive, directives from docutils.statemachine import ViewList from qapi.error import QAPIError, QAPISemError from qapi.gen import QAPISchemaVisitor +from qapi.parser import QAPIDoc from qapi.schema import QAPISchema from sphinx import addnodes @@ -258,11 +259,11 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): """Return list of doctree nodes for additional sections""" nodelist = [] for section in doc.sections: - if section.tag and section.tag == 'TODO': + if section.kind == QAPIDoc.Kind.TODO: # Hide TODO: sections continue - if not section.tag: + if section.kind == QAPIDoc.Kind.PLAIN: # Sphinx cannot handle sectionless titles; # Instead, just append the results to the prior section. container = nodes.container() @@ -270,7 +271,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): nodelist += container.children continue - snode = self._make_section(section.tag) + snode = self._make_section(section.kind.name.title()) self._parse_text_into_node(dedent(section.text), snode) nodelist.append(snode) return nodelist diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 97def9f0e4..94d5322f8a 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -14,6 +14,7 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. +import enum import os import re from typing import ( @@ -574,7 +575,10 @@ class QAPISchemaParser: ) raise QAPIParseError(self, emsg) - doc.new_tagged_section(self.info, match.group(1)) + doc.new_tagged_section( + self.info, + QAPIDoc.Kind.from_string(match.group(1)) + ) text = line[match.end():] if text: doc.append_line(text) @@ -585,7 +589,7 @@ class QAPISchemaParser: self, "unexpected '=' markup in definition documentation") else: - # tag-less paragraph + # plain paragraph doc.ensure_untagged_section(self.info) doc.append_line(line) line = self.get_doc_paragraph(doc) @@ -634,14 +638,33 @@ class QAPIDoc: Free-form documentation blocks consist only of a body section. """ + class Kind(enum.Enum): + PLAIN = 0 + MEMBER = 1 + FEATURE = 2 + RETURNS = 3 + ERRORS = 4 + SINCE = 5 + TODO = 6 + + @staticmethod + def from_string(kind: str) -> 'QAPIDoc.Kind': + return QAPIDoc.Kind[kind.upper()] + + def __str__(self) -> str: + return self.name.title() + class Section: # pylint: disable=too-few-public-methods - def __init__(self, info: QAPISourceInfo, - tag: Optional[str] = None): + def __init__( + self, + info: QAPISourceInfo, + kind: 'QAPIDoc.Kind', + ): # section source info, i.e. where it begins self.info = info - # section tag, if any ('Returns', '@name', ...) - self.tag = tag + # section kind + self.kind = kind # section text without tag self.text = '' @@ -649,8 +672,14 @@ class QAPIDoc: self.text += line + '\n' class ArgSection(Section): - def __init__(self, info: QAPISourceInfo, tag: str): - super().__init__(info, tag) + def __init__( + self, + info: QAPISourceInfo, + kind: 'QAPIDoc.Kind', + name: str + ): + super().__init__(info, kind) + self.name = name self.member: Optional['QAPISchemaMember'] = None def connect(self, member: 'QAPISchemaMember') -> None: @@ -662,7 +691,9 @@ class QAPIDoc: # definition doc's symbol, None for free-form doc self.symbol: Optional[str] = symbol # the sections in textual order - self.all_sections: List[QAPIDoc.Section] = [QAPIDoc.Section(info)] + self.all_sections: List[QAPIDoc.Section] = [ + QAPIDoc.Section(info, QAPIDoc.Kind.PLAIN) + ] # the body section self.body: Optional[QAPIDoc.Section] = self.all_sections[0] # dicts mapping parameter/feature names to their description @@ -679,12 +710,14 @@ class QAPIDoc: def end(self) -> None: for section in self.all_sections: section.text = section.text.strip('\n') - if section.tag is not None and section.text == '': + if section.kind != QAPIDoc.Kind.PLAIN and section.text == '': raise QAPISemError( - section.info, "text required after '%s:'" % section.tag) + section.info, "text required after '%s:'" % section.kind) def ensure_untagged_section(self, info: QAPISourceInfo) -> None: - if self.all_sections and not self.all_sections[-1].tag: + kind = QAPIDoc.Kind.PLAIN + + if self.all_sections and self.all_sections[-1].kind == kind: # extend current section section = self.all_sections[-1] if not section.text: @@ -692,46 +725,56 @@ class QAPIDoc: section.info = info section.text += '\n' return + # start new section - section = self.Section(info) + section = self.Section(info, kind) self.sections.append(section) self.all_sections.append(section) - def new_tagged_section(self, info: QAPISourceInfo, tag: str) -> None: - section = self.Section(info, tag) - if tag == 'Returns': + def new_tagged_section( + self, + info: QAPISourceInfo, + kind: 'QAPIDoc.Kind', + ) -> None: + section = self.Section(info, kind) + if kind == QAPIDoc.Kind.RETURNS: if self.returns: raise QAPISemError( - info, "duplicated '%s' section" % tag) + info, "duplicated '%s' section" % kind) self.returns = section - elif tag == 'Errors': + elif kind == QAPIDoc.Kind.ERRORS: if self.errors: raise QAPISemError( - info, "duplicated '%s' section" % tag) + info, "duplicated '%s' section" % kind) self.errors = section - elif tag == 'Since': + elif kind == QAPIDoc.Kind.SINCE: if self.since: raise QAPISemError( - info, "duplicated '%s' section" % tag) + info, "duplicated '%s' section" % kind) self.since = section self.sections.append(section) self.all_sections.append(section) - def _new_description(self, info: QAPISourceInfo, name: str, - desc: Dict[str, ArgSection]) -> None: + def _new_description( + self, + info: QAPISourceInfo, + name: str, + kind: 'QAPIDoc.Kind', + desc: Dict[str, ArgSection] + ) -> None: if not name: raise QAPISemError(info, "invalid parameter name") if name in desc: raise QAPISemError(info, "'%s' parameter name duplicated" % name) - section = self.ArgSection(info, '@' + name) + section = self.ArgSection(info, kind, name) self.all_sections.append(section) desc[name] = section def new_argument(self, info: QAPISourceInfo, name: str) -> None: - self._new_description(info, name, self.args) + self._new_description(info, name, QAPIDoc.Kind.MEMBER, self.args) def new_feature(self, info: QAPISourceInfo, name: str) -> None: - self._new_description(info, name, self.features) + self._new_description(info, name, QAPIDoc.Kind.FEATURE, self.features) def append_line(self, line: str) -> None: self.all_sections[-1].append_line(line) @@ -744,7 +787,7 @@ class QAPIDoc: "%s '%s' lacks documentation" % (member.role, member.name)) self.args[member.name] = QAPIDoc.ArgSection( - self.info, '@' + member.name) + self.info, QAPIDoc.Kind.MEMBER, member.name) self.args[member.name].connect(member) def connect_feature(self, feature: 'QAPISchemaFeature') -> None: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 0a9da3efde..5773f1dd6d 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -113,7 +113,7 @@ The _one_ {and only}, description on the same line Also _one_ {and only} feature=enum-member-feat a member feature - section=None + section=Plain @two is undocumented doc symbol=Base body= @@ -171,15 +171,15 @@ description starts on the same line a feature feature=cmd-feat2 another feature - section=None + section=Plain .. note:: @arg3 is undocumented section=Returns @Object section=Errors some - section=TODO + section=Todo frobnicate - section=None + section=Plain .. admonition:: Notes - Lorem ipsum dolor sit amet @@ -212,7 +212,7 @@ If you're bored enough to read this, go see a video of boxed cats a feature feature=cmd-feat2 another feature - section=None + section=Plain .. qmp-example:: -> "this example" diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 8fe951c880..4be930228c 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -122,7 +122,7 @@ def test_frontend(fname): for feat, section in doc.features.items(): print(' feature=%s\n%s' % (feat, section.text)) for section in doc.sections: - print(' section=%s\n%s' % (section.tag, section.text)) + print(' section=%s\n%s' % (section.kind, section.text)) def open_test_result(dir_name, file_name, update): From d7ca9a3a4c55cba910a3556544e90aadd92c4efe Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:32 -0400 Subject: [PATCH 2512/2892] qapi/schema: add __repr__ to QAPIDoc.Section Makes debugging far more pleasant when you can just print(section) and get something reasonable to display. Signed-off-by: John Snow Message-ID: <20250311034303.75779-35-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 94d5322f8a..11c11bb09e 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -668,6 +668,9 @@ class QAPIDoc: # section text without tag self.text = '' + def __repr__(self) -> str: + return f"" + def append_line(self, line: str) -> None: self.text += line + '\n' From 7d125c339d32cbef65404b621d3ca02cc67f3090 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:33 -0400 Subject: [PATCH 2513/2892] docs/qapidoc: add transmogrifier stub This commit adds a stubbed option to the qapi-doc directive that opts-in to the new rST generator; the implementation of which will follow in subsequent commits. Once all QAPI documents have been converted, this option and the old qapidoc implementation can be dropped. Note that moving code outside of the try...except block has no impact because the code moved outside of that block does not ever raise a QAPIError. Signed-off-by: John Snow Message-ID: <20250311034303.75779-36-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index d622398f1d..dc72f3fd3f 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -452,9 +452,9 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line) self._sphinx_directive.do_parse(rstlist, node) - def get_document_nodes(self): - """Return the list of docutils nodes which make up the document""" - return self._top_node.children + def get_document_node(self): + """Return the root docutils node which makes up the document""" + return self._top_node # Turn the black formatter on for the rest of the file. @@ -503,7 +503,10 @@ class QAPIDocDirective(NestedDirective): required_argument = 1 optional_arguments = 1 - option_spec = {"qapifile": directives.unchanged_required} + option_spec = { + "qapifile": directives.unchanged_required, + "transmogrify": directives.flag, + } has_content = False def new_serialno(self): @@ -511,10 +514,24 @@ class QAPIDocDirective(NestedDirective): env = self.state.document.settings.env return "qapidoc-%d" % env.new_serialno("qapidoc") + def transmogrify(self, schema) -> nodes.Element: + raise NotImplementedError + + def legacy(self, schema) -> nodes.Element: + vis = QAPISchemaGenRSTVisitor(self) + vis.visit_begin(schema) + for doc in schema.docs: + if doc.symbol: + vis.symbol(doc, schema.lookup_entity(doc.symbol)) + else: + vis.freeform(doc) + return vis.get_document_node() + def run(self): env = self.state.document.settings.env qapifile = env.config.qapidoc_srctree + "/" + self.arguments[0] qapidir = os.path.dirname(qapifile) + transmogrify = "transmogrify" in self.options try: schema = QAPISchema(qapifile) @@ -522,20 +539,18 @@ class QAPIDocDirective(NestedDirective): # First tell Sphinx about all the schema files that the # output documentation depends on (including 'qapifile' itself) schema.visit(QAPISchemaGenDepVisitor(env, qapidir)) - - vis = QAPISchemaGenRSTVisitor(self) - vis.visit_begin(schema) - for doc in schema.docs: - if doc.symbol: - vis.symbol(doc, schema.lookup_entity(doc.symbol)) - else: - vis.freeform(doc) - return vis.get_document_nodes() except QAPIError as err: # Launder QAPI parse errors into Sphinx extension errors # so they are displayed nicely to the user raise ExtensionError(str(err)) from err + if transmogrify: + contentnode = self.transmogrify(schema) + else: + contentnode = self.legacy(schema) + + return contentnode.children + class QMPExample(CodeBlock, NestedDirective): """ From 7640410d5c2f7a37c8b6409f2d288807c99afd1e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:34 -0400 Subject: [PATCH 2514/2892] docs/qapidoc: split old implementation into qapidoc_legacy.py This is being done primarily to be able to type check and delint the new implementation without needing to worry about fixing up the old implementation. I'm adding the new implementation into the existing file instead of into a new file so that when the dust settles, qapidoc.py will contain the full history of development on this generative module. This patch *should* be pure motion, give or take the import statements. Signed-off-by: John Snow Message-ID: <20250311034303.75779-37-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 420 +------------------------------- docs/sphinx/qapidoc_legacy.py | 439 ++++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+), 418 deletions(-) create mode 100644 docs/sphinx/qapidoc_legacy.py diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index dc72f3fd3f..f4abf42e7b 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -25,19 +25,16 @@ https://www.sphinx-doc.org/en/master/development/index.html """ import os -import re import sys -import textwrap from typing import List from docutils import nodes from docutils.parsers.rst import Directive, directives -from docutils.statemachine import ViewList -from qapi.error import QAPIError, QAPISemError +from qapi.error import QAPIError from qapi.gen import QAPISchemaVisitor -from qapi.parser import QAPIDoc from qapi.schema import QAPISchema +from qapidoc_legacy import QAPISchemaGenRSTVisitor from sphinx import addnodes from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError @@ -48,419 +45,6 @@ from sphinx.util.nodes import nested_parse_with_titles __version__ = "1.0" -def dedent(text: str) -> str: - # Adjust indentation to make description text parse as paragraph. - - lines = text.splitlines(True) - if re.match(r"\s+", lines[0]): - # First line is indented; description started on the line after - # the name. dedent the whole block. - return textwrap.dedent(text) - - # Descr started on same line. Dedent line 2+. - return lines[0] + textwrap.dedent("".join(lines[1:])) - - -# Disable black auto-formatter until re-enabled: -# fmt: off - - -class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): - """A QAPI schema visitor which generates docutils/Sphinx nodes - - This class builds up a tree of docutils/Sphinx nodes corresponding - to documentation for the various QAPI objects. To use it, first - create a QAPISchemaGenRSTVisitor object, and call its - visit_begin() method. Then you can call one of the two methods - 'freeform' (to add documentation for a freeform documentation - chunk) or 'symbol' (to add documentation for a QAPI symbol). These - will cause the visitor to build up the tree of document - nodes. Once you've added all the documentation via 'freeform' and - 'symbol' method calls, you can call 'get_document_nodes' to get - the final list of document nodes (in a form suitable for returning - from a Sphinx directive's 'run' method). - """ - def __init__(self, sphinx_directive): - self._cur_doc = None - self._sphinx_directive = sphinx_directive - self._top_node = nodes.section() - self._active_headings = [self._top_node] - - def _make_dlitem(self, term, defn): - """Return a dlitem node with the specified term and definition. - - term should be a list of Text and literal nodes. - defn should be one of: - - a string, which will be handed to _parse_text_into_node - - a list of Text and literal nodes, which will be put into - a paragraph node - """ - dlitem = nodes.definition_list_item() - dlterm = nodes.term('', '', *term) - dlitem += dlterm - if defn: - dldef = nodes.definition() - if isinstance(defn, list): - dldef += nodes.paragraph('', '', *defn) - else: - self._parse_text_into_node(defn, dldef) - dlitem += dldef - return dlitem - - def _make_section(self, title): - """Return a section node with optional title""" - section = nodes.section(ids=[self._sphinx_directive.new_serialno()]) - if title: - section += nodes.title(title, title) - return section - - def _nodes_for_ifcond(self, ifcond, with_if=True): - """Return list of Text, literal nodes for the ifcond - - Return a list which gives text like ' (If: condition)'. - If with_if is False, we don't return the "(If: " and ")". - """ - - doc = ifcond.docgen() - if not doc: - return [] - doc = nodes.literal('', doc) - if not with_if: - return [doc] - - nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')] - nodelist.append(doc) - nodelist.append(nodes.Text(')')) - return nodelist - - def _nodes_for_one_member(self, member): - """Return list of Text, literal nodes for this member - - Return a list of doctree nodes which give text like - 'name: type (optional) (If: ...)' suitable for use as the - 'term' part of a definition list item. - """ - term = [nodes.literal('', member.name)] - if member.type.doc_type(): - term.append(nodes.Text(': ')) - term.append(nodes.literal('', member.type.doc_type())) - if member.optional: - term.append(nodes.Text(' (optional)')) - if member.ifcond.is_present(): - term.extend(self._nodes_for_ifcond(member.ifcond)) - return term - - def _nodes_for_variant_when(self, branches, variant): - """Return list of Text, literal nodes for variant 'when' clause - - Return a list of doctree nodes which give text like - 'when tagname is variant (If: ...)' suitable for use in - the 'branches' part of a definition list. - """ - term = [nodes.Text(' when '), - nodes.literal('', branches.tag_member.name), - nodes.Text(' is '), - nodes.literal('', '"%s"' % variant.name)] - if variant.ifcond.is_present(): - term.extend(self._nodes_for_ifcond(variant.ifcond)) - return term - - def _nodes_for_members(self, doc, what, base=None, branches=None): - """Return list of doctree nodes for the table of members""" - dlnode = nodes.definition_list() - for section in doc.args.values(): - term = self._nodes_for_one_member(section.member) - # TODO drop fallbacks when undocumented members are outlawed - if section.text: - defn = dedent(section.text) - else: - defn = [nodes.Text('Not documented')] - - dlnode += self._make_dlitem(term, defn) - - if base: - dlnode += self._make_dlitem([nodes.Text('The members of '), - nodes.literal('', base.doc_type())], - None) - - if branches: - for v in branches.variants: - if v.type.name == 'q_empty': - continue - assert not v.type.is_implicit() - term = [nodes.Text('The members of '), - nodes.literal('', v.type.doc_type())] - term.extend(self._nodes_for_variant_when(branches, v)) - dlnode += self._make_dlitem(term, None) - - if not dlnode.children: - return [] - - section = self._make_section(what) - section += dlnode - return [section] - - def _nodes_for_enum_values(self, doc): - """Return list of doctree nodes for the table of enum values""" - seen_item = False - dlnode = nodes.definition_list() - for section in doc.args.values(): - termtext = [nodes.literal('', section.member.name)] - if section.member.ifcond.is_present(): - termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) - # TODO drop fallbacks when undocumented members are outlawed - if section.text: - defn = dedent(section.text) - else: - defn = [nodes.Text('Not documented')] - - dlnode += self._make_dlitem(termtext, defn) - seen_item = True - - if not seen_item: - return [] - - section = self._make_section('Values') - section += dlnode - return [section] - - def _nodes_for_arguments(self, doc, arg_type): - """Return list of doctree nodes for the arguments section""" - if arg_type and not arg_type.is_implicit(): - assert not doc.args - section = self._make_section('Arguments') - dlnode = nodes.definition_list() - dlnode += self._make_dlitem( - [nodes.Text('The members of '), - nodes.literal('', arg_type.name)], - None) - section += dlnode - return [section] - - return self._nodes_for_members(doc, 'Arguments') - - def _nodes_for_features(self, doc): - """Return list of doctree nodes for the table of features""" - seen_item = False - dlnode = nodes.definition_list() - for section in doc.features.values(): - dlnode += self._make_dlitem( - [nodes.literal('', section.member.name)], dedent(section.text)) - seen_item = True - - if not seen_item: - return [] - - section = self._make_section('Features') - section += dlnode - return [section] - - def _nodes_for_sections(self, doc): - """Return list of doctree nodes for additional sections""" - nodelist = [] - for section in doc.sections: - if section.kind == QAPIDoc.Kind.TODO: - # Hide TODO: sections - continue - - if section.kind == QAPIDoc.Kind.PLAIN: - # Sphinx cannot handle sectionless titles; - # Instead, just append the results to the prior section. - container = nodes.container() - self._parse_text_into_node(section.text, container) - nodelist += container.children - continue - - snode = self._make_section(section.kind.name.title()) - self._parse_text_into_node(dedent(section.text), snode) - nodelist.append(snode) - return nodelist - - def _nodes_for_if_section(self, ifcond): - """Return list of doctree nodes for the "If" section""" - nodelist = [] - if ifcond.is_present(): - snode = self._make_section('If') - snode += nodes.paragraph( - '', '', *self._nodes_for_ifcond(ifcond, with_if=False) - ) - nodelist.append(snode) - return nodelist - - def _add_doc(self, typ, sections): - """Add documentation for a command/object/enum... - - We assume we're documenting the thing defined in self._cur_doc. - typ is the type of thing being added ("Command", "Object", etc) - - sections is a list of nodes for sections to add to the definition. - """ - - doc = self._cur_doc - snode = nodes.section(ids=[self._sphinx_directive.new_serialno()]) - snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol), - nodes.Text(' (' + typ + ')')]) - self._parse_text_into_node(doc.body.text, snode) - for s in sections: - if s is not None: - snode += s - self._add_node_to_current_heading(snode) - - def visit_enum_type(self, name, info, ifcond, features, members, prefix): - doc = self._cur_doc - self._add_doc('Enum', - self._nodes_for_enum_values(doc) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_object_type(self, name, info, ifcond, features, - base, members, branches): - doc = self._cur_doc - if base and base.is_implicit(): - base = None - self._add_doc('Object', - self._nodes_for_members(doc, 'Members', base, branches) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_alternate_type(self, name, info, ifcond, features, - alternatives): - doc = self._cur_doc - self._add_doc('Alternate', - self._nodes_for_members(doc, 'Members') - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_command(self, name, info, ifcond, features, arg_type, - ret_type, gen, success_response, boxed, allow_oob, - allow_preconfig, coroutine): - doc = self._cur_doc - self._add_doc('Command', - self._nodes_for_arguments(doc, arg_type) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_event(self, name, info, ifcond, features, arg_type, boxed): - doc = self._cur_doc - self._add_doc('Event', - self._nodes_for_arguments(doc, arg_type) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def symbol(self, doc, entity): - """Add documentation for one symbol to the document tree - - This is the main entry point which causes us to add documentation - nodes for a symbol (which could be a 'command', 'object', 'event', - etc). We do this by calling 'visit' on the schema entity, which - will then call back into one of our visit_* methods, depending - on what kind of thing this symbol is. - """ - self._cur_doc = doc - entity.visit(self) - self._cur_doc = None - - def _start_new_heading(self, heading, level): - """Start a new heading at the specified heading level - - Create a new section whose title is 'heading' and which is placed - in the docutils node tree as a child of the most recent level-1 - heading. Subsequent document sections (commands, freeform doc chunks, - etc) will be placed as children of this new heading section. - """ - if len(self._active_headings) < level: - raise QAPISemError(self._cur_doc.info, - 'Level %d subheading found outside a ' - 'level %d heading' - % (level, level - 1)) - snode = self._make_section(heading) - self._active_headings[level - 1] += snode - self._active_headings = self._active_headings[:level] - self._active_headings.append(snode) - return snode - - def _add_node_to_current_heading(self, node): - """Add the node to whatever the current active heading is""" - self._active_headings[-1] += node - - def freeform(self, doc): - """Add a piece of 'freeform' documentation to the document tree - - A 'freeform' document chunk doesn't relate to any particular - symbol (for instance, it could be an introduction). - - If the freeform document starts with a line of the form - '= Heading text', this is a section or subsection heading, with - the heading level indicated by the number of '=' signs. - """ - - # QAPIDoc documentation says free-form documentation blocks - # must have only a body section, nothing else. - assert not doc.sections - assert not doc.args - assert not doc.features - self._cur_doc = doc - - text = doc.body.text - if re.match(r'=+ ', text): - # Section/subsection heading (if present, will always be - # the first line of the block) - (heading, _, text) = text.partition('\n') - (leader, _, heading) = heading.partition(' ') - node = self._start_new_heading(heading, len(leader)) - if text == '': - return - else: - node = nodes.container() - - self._parse_text_into_node(text, node) - self._cur_doc = None - - def _parse_text_into_node(self, doctext, node): - """Parse a chunk of QAPI-doc-format text into the node - - The doc comment can contain most inline rST markup, including - bulleted and enumerated lists. - As an extra permitted piece of markup, @var will be turned - into ``var``. - """ - - # Handle the "@var means ``var`` case - doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext) - - rstlist = ViewList() - for line in doctext.splitlines(): - # The reported line number will always be that of the start line - # of the doc comment, rather than the actual location of the error. - # Being more precise would require overhaul of the QAPIDoc class - # to track lines more exactly within all the sub-parts of the doc - # comment, as well as counting lines here. - rstlist.append(line, self._cur_doc.info.fname, - self._cur_doc.info.line) - # Append a blank line -- in some cases rST syntax errors get - # attributed to the line after one with actual text, and if there - # isn't anything in the ViewList corresponding to that then Sphinx - # 1.6's AutodocReporter will then misidentify the source/line location - # in the error message (usually attributing it to the top-level - # .rst file rather than the offending .json file). The extra blank - # line won't affect the rendered output. - rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line) - self._sphinx_directive.do_parse(rstlist, node) - - def get_document_node(self): - """Return the root docutils node which makes up the document""" - return self._top_node - - -# Turn the black formatter on for the rest of the file. -# fmt: on - - class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module diff --git a/docs/sphinx/qapidoc_legacy.py b/docs/sphinx/qapidoc_legacy.py new file mode 100644 index 0000000000..679f38356b --- /dev/null +++ b/docs/sphinx/qapidoc_legacy.py @@ -0,0 +1,439 @@ +# coding=utf-8 +# +# QEMU qapidoc QAPI file parsing extension +# +# Copyright (c) 2020 Linaro +# +# This work is licensed under the terms of the GNU GPLv2 or later. +# See the COPYING file in the top-level directory. + +""" +qapidoc is a Sphinx extension that implements the qapi-doc directive + +The purpose of this extension is to read the documentation comments +in QAPI schema files, and insert them all into the current document. + +It implements one new rST directive, "qapi-doc::". +Each qapi-doc:: directive takes one argument, which is the +pathname of the schema file to process, relative to the source tree. + +The docs/conf.py file must set the qapidoc_srctree config value to +the root of the QEMU source tree. + +The Sphinx documentation on writing extensions is at: +https://www.sphinx-doc.org/en/master/development/index.html +""" + +import re +import textwrap + +from docutils import nodes +from docutils.statemachine import ViewList +from qapi.error import QAPISemError +from qapi.gen import QAPISchemaVisitor +from qapi.parser import QAPIDoc + + +def dedent(text: str) -> str: + # Adjust indentation to make description text parse as paragraph. + + lines = text.splitlines(True) + if re.match(r"\s+", lines[0]): + # First line is indented; description started on the line after + # the name. dedent the whole block. + return textwrap.dedent(text) + + # Descr started on same line. Dedent line 2+. + return lines[0] + textwrap.dedent("".join(lines[1:])) + + +class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): + """A QAPI schema visitor which generates docutils/Sphinx nodes + + This class builds up a tree of docutils/Sphinx nodes corresponding + to documentation for the various QAPI objects. To use it, first + create a QAPISchemaGenRSTVisitor object, and call its + visit_begin() method. Then you can call one of the two methods + 'freeform' (to add documentation for a freeform documentation + chunk) or 'symbol' (to add documentation for a QAPI symbol). These + will cause the visitor to build up the tree of document + nodes. Once you've added all the documentation via 'freeform' and + 'symbol' method calls, you can call 'get_document_nodes' to get + the final list of document nodes (in a form suitable for returning + from a Sphinx directive's 'run' method). + """ + def __init__(self, sphinx_directive): + self._cur_doc = None + self._sphinx_directive = sphinx_directive + self._top_node = nodes.section() + self._active_headings = [self._top_node] + + def _make_dlitem(self, term, defn): + """Return a dlitem node with the specified term and definition. + + term should be a list of Text and literal nodes. + defn should be one of: + - a string, which will be handed to _parse_text_into_node + - a list of Text and literal nodes, which will be put into + a paragraph node + """ + dlitem = nodes.definition_list_item() + dlterm = nodes.term('', '', *term) + dlitem += dlterm + if defn: + dldef = nodes.definition() + if isinstance(defn, list): + dldef += nodes.paragraph('', '', *defn) + else: + self._parse_text_into_node(defn, dldef) + dlitem += dldef + return dlitem + + def _make_section(self, title): + """Return a section node with optional title""" + section = nodes.section(ids=[self._sphinx_directive.new_serialno()]) + if title: + section += nodes.title(title, title) + return section + + def _nodes_for_ifcond(self, ifcond, with_if=True): + """Return list of Text, literal nodes for the ifcond + + Return a list which gives text like ' (If: condition)'. + If with_if is False, we don't return the "(If: " and ")". + """ + + doc = ifcond.docgen() + if not doc: + return [] + doc = nodes.literal('', doc) + if not with_if: + return [doc] + + nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')] + nodelist.append(doc) + nodelist.append(nodes.Text(')')) + return nodelist + + def _nodes_for_one_member(self, member): + """Return list of Text, literal nodes for this member + + Return a list of doctree nodes which give text like + 'name: type (optional) (If: ...)' suitable for use as the + 'term' part of a definition list item. + """ + term = [nodes.literal('', member.name)] + if member.type.doc_type(): + term.append(nodes.Text(': ')) + term.append(nodes.literal('', member.type.doc_type())) + if member.optional: + term.append(nodes.Text(' (optional)')) + if member.ifcond.is_present(): + term.extend(self._nodes_for_ifcond(member.ifcond)) + return term + + def _nodes_for_variant_when(self, branches, variant): + """Return list of Text, literal nodes for variant 'when' clause + + Return a list of doctree nodes which give text like + 'when tagname is variant (If: ...)' suitable for use in + the 'branches' part of a definition list. + """ + term = [nodes.Text(' when '), + nodes.literal('', branches.tag_member.name), + nodes.Text(' is '), + nodes.literal('', '"%s"' % variant.name)] + if variant.ifcond.is_present(): + term.extend(self._nodes_for_ifcond(variant.ifcond)) + return term + + def _nodes_for_members(self, doc, what, base=None, branches=None): + """Return list of doctree nodes for the table of members""" + dlnode = nodes.definition_list() + for section in doc.args.values(): + term = self._nodes_for_one_member(section.member) + # TODO drop fallbacks when undocumented members are outlawed + if section.text: + defn = dedent(section.text) + else: + defn = [nodes.Text('Not documented')] + + dlnode += self._make_dlitem(term, defn) + + if base: + dlnode += self._make_dlitem([nodes.Text('The members of '), + nodes.literal('', base.doc_type())], + None) + + if branches: + for v in branches.variants: + if v.type.name == 'q_empty': + continue + assert not v.type.is_implicit() + term = [nodes.Text('The members of '), + nodes.literal('', v.type.doc_type())] + term.extend(self._nodes_for_variant_when(branches, v)) + dlnode += self._make_dlitem(term, None) + + if not dlnode.children: + return [] + + section = self._make_section(what) + section += dlnode + return [section] + + def _nodes_for_enum_values(self, doc): + """Return list of doctree nodes for the table of enum values""" + seen_item = False + dlnode = nodes.definition_list() + for section in doc.args.values(): + termtext = [nodes.literal('', section.member.name)] + if section.member.ifcond.is_present(): + termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) + # TODO drop fallbacks when undocumented members are outlawed + if section.text: + defn = dedent(section.text) + else: + defn = [nodes.Text('Not documented')] + + dlnode += self._make_dlitem(termtext, defn) + seen_item = True + + if not seen_item: + return [] + + section = self._make_section('Values') + section += dlnode + return [section] + + def _nodes_for_arguments(self, doc, arg_type): + """Return list of doctree nodes for the arguments section""" + if arg_type and not arg_type.is_implicit(): + assert not doc.args + section = self._make_section('Arguments') + dlnode = nodes.definition_list() + dlnode += self._make_dlitem( + [nodes.Text('The members of '), + nodes.literal('', arg_type.name)], + None) + section += dlnode + return [section] + + return self._nodes_for_members(doc, 'Arguments') + + def _nodes_for_features(self, doc): + """Return list of doctree nodes for the table of features""" + seen_item = False + dlnode = nodes.definition_list() + for section in doc.features.values(): + dlnode += self._make_dlitem( + [nodes.literal('', section.member.name)], dedent(section.text)) + seen_item = True + + if not seen_item: + return [] + + section = self._make_section('Features') + section += dlnode + return [section] + + def _nodes_for_sections(self, doc): + """Return list of doctree nodes for additional sections""" + nodelist = [] + for section in doc.sections: + if section.kind == QAPIDoc.Kind.TODO: + # Hide TODO: sections + continue + + if section.kind == QAPIDoc.Kind.PLAIN: + # Sphinx cannot handle sectionless titles; + # Instead, just append the results to the prior section. + container = nodes.container() + self._parse_text_into_node(section.text, container) + nodelist += container.children + continue + + snode = self._make_section(section.kind.name.title()) + self._parse_text_into_node(dedent(section.text), snode) + nodelist.append(snode) + return nodelist + + def _nodes_for_if_section(self, ifcond): + """Return list of doctree nodes for the "If" section""" + nodelist = [] + if ifcond.is_present(): + snode = self._make_section('If') + snode += nodes.paragraph( + '', '', *self._nodes_for_ifcond(ifcond, with_if=False) + ) + nodelist.append(snode) + return nodelist + + def _add_doc(self, typ, sections): + """Add documentation for a command/object/enum... + + We assume we're documenting the thing defined in self._cur_doc. + typ is the type of thing being added ("Command", "Object", etc) + + sections is a list of nodes for sections to add to the definition. + """ + + doc = self._cur_doc + snode = nodes.section(ids=[self._sphinx_directive.new_serialno()]) + snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol), + nodes.Text(' (' + typ + ')')]) + self._parse_text_into_node(doc.body.text, snode) + for s in sections: + if s is not None: + snode += s + self._add_node_to_current_heading(snode) + + def visit_enum_type(self, name, info, ifcond, features, members, prefix): + doc = self._cur_doc + self._add_doc('Enum', + self._nodes_for_enum_values(doc) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def visit_object_type(self, name, info, ifcond, features, + base, members, branches): + doc = self._cur_doc + if base and base.is_implicit(): + base = None + self._add_doc('Object', + self._nodes_for_members(doc, 'Members', base, branches) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def visit_alternate_type(self, name, info, ifcond, features, + alternatives): + doc = self._cur_doc + self._add_doc('Alternate', + self._nodes_for_members(doc, 'Members') + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def visit_command(self, name, info, ifcond, features, arg_type, + ret_type, gen, success_response, boxed, allow_oob, + allow_preconfig, coroutine): + doc = self._cur_doc + self._add_doc('Command', + self._nodes_for_arguments(doc, arg_type) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def visit_event(self, name, info, ifcond, features, arg_type, boxed): + doc = self._cur_doc + self._add_doc('Event', + self._nodes_for_arguments(doc, arg_type) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def symbol(self, doc, entity): + """Add documentation for one symbol to the document tree + + This is the main entry point which causes us to add documentation + nodes for a symbol (which could be a 'command', 'object', 'event', + etc). We do this by calling 'visit' on the schema entity, which + will then call back into one of our visit_* methods, depending + on what kind of thing this symbol is. + """ + self._cur_doc = doc + entity.visit(self) + self._cur_doc = None + + def _start_new_heading(self, heading, level): + """Start a new heading at the specified heading level + + Create a new section whose title is 'heading' and which is placed + in the docutils node tree as a child of the most recent level-1 + heading. Subsequent document sections (commands, freeform doc chunks, + etc) will be placed as children of this new heading section. + """ + if len(self._active_headings) < level: + raise QAPISemError(self._cur_doc.info, + 'Level %d subheading found outside a ' + 'level %d heading' + % (level, level - 1)) + snode = self._make_section(heading) + self._active_headings[level - 1] += snode + self._active_headings = self._active_headings[:level] + self._active_headings.append(snode) + return snode + + def _add_node_to_current_heading(self, node): + """Add the node to whatever the current active heading is""" + self._active_headings[-1] += node + + def freeform(self, doc): + """Add a piece of 'freeform' documentation to the document tree + + A 'freeform' document chunk doesn't relate to any particular + symbol (for instance, it could be an introduction). + + If the freeform document starts with a line of the form + '= Heading text', this is a section or subsection heading, with + the heading level indicated by the number of '=' signs. + """ + + # QAPIDoc documentation says free-form documentation blocks + # must have only a body section, nothing else. + assert not doc.sections + assert not doc.args + assert not doc.features + self._cur_doc = doc + + text = doc.body.text + if re.match(r'=+ ', text): + # Section/subsection heading (if present, will always be + # the first line of the block) + (heading, _, text) = text.partition('\n') + (leader, _, heading) = heading.partition(' ') + node = self._start_new_heading(heading, len(leader)) + if text == '': + return + else: + node = nodes.container() + + self._parse_text_into_node(text, node) + self._cur_doc = None + + def _parse_text_into_node(self, doctext, node): + """Parse a chunk of QAPI-doc-format text into the node + + The doc comment can contain most inline rST markup, including + bulleted and enumerated lists. + As an extra permitted piece of markup, @var will be turned + into ``var``. + """ + + # Handle the "@var means ``var`` case + doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext) + + rstlist = ViewList() + for line in doctext.splitlines(): + # The reported line number will always be that of the start line + # of the doc comment, rather than the actual location of the error. + # Being more precise would require overhaul of the QAPIDoc class + # to track lines more exactly within all the sub-parts of the doc + # comment, as well as counting lines here. + rstlist.append(line, self._cur_doc.info.fname, + self._cur_doc.info.line) + # Append a blank line -- in some cases rST syntax errors get + # attributed to the line after one with actual text, and if there + # isn't anything in the ViewList corresponding to that then Sphinx + # 1.6's AutodocReporter will then misidentify the source/line location + # in the error message (usually attributing it to the top-level + # .rst file rather than the offending .json file). The extra blank + # line won't affect the rendered output. + rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line) + self._sphinx_directive.do_parse(rstlist, node) + + def get_document_node(self): + """Return the root docutils node which makes up the document""" + return self._top_node From 45d483a851051bcea3a32b1ce7367180a8cf7bae Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:35 -0400 Subject: [PATCH 2515/2892] docs/qapidoc: Fix static typing on qapidoc.py Now that the legacy code is factored out, fix up the typing on the remaining code in qapidoc.py. Add a type ignore to qapi_legacy.py to prevent the errors there from bleeding out into qapidoc.py. Signed-off-by: John Snow Message-ID: <20250311034303.75779-38-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 40 ++++++++++++++++++++++------------- docs/sphinx/qapidoc_legacy.py | 1 + 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index f4abf42e7b..5246832b68 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -24,17 +24,18 @@ The Sphinx documentation on writing extensions is at: https://www.sphinx-doc.org/en/master/development/index.html """ +from __future__ import annotations + import os import sys -from typing import List +from typing import TYPE_CHECKING from docutils import nodes from docutils.parsers.rst import Directive, directives from qapi.error import QAPIError -from qapi.gen import QAPISchemaVisitor -from qapi.schema import QAPISchema +from qapi.schema import QAPISchema, QAPISchemaVisitor -from qapidoc_legacy import QAPISchemaGenRSTVisitor +from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore from sphinx import addnodes from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError @@ -42,6 +43,15 @@ from sphinx.util.docutils import switch_source_input from sphinx.util.nodes import nested_parse_with_titles +if TYPE_CHECKING: + from typing import Any, List, Sequence + + from docutils.statemachine import StringList + + from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata + + __version__ = "1.0" @@ -53,11 +63,11 @@ class QAPISchemaGenDepVisitor(QAPISchemaVisitor): schema file associated with each module in the QAPI input. """ - def __init__(self, env, qapidir): + def __init__(self, env: Any, qapidir: str) -> None: self._env = env self._qapidir = qapidir - def visit_module(self, name): + def visit_module(self, name: str) -> None: if name != "./builtin": qapifile = self._qapidir + "/" + name self._env.note_dependency(os.path.abspath(qapifile)) @@ -65,10 +75,10 @@ class QAPISchemaGenDepVisitor(QAPISchemaVisitor): class NestedDirective(Directive): - def run(self): + def run(self) -> Sequence[nodes.Node]: raise NotImplementedError - def do_parse(self, rstlist, node): + def do_parse(self, rstlist: StringList, node: nodes.Node) -> None: """ Parse rST source lines and add them to the specified node @@ -93,15 +103,15 @@ class QAPIDocDirective(NestedDirective): } has_content = False - def new_serialno(self): + def new_serialno(self) -> str: """Return a unique new ID string suitable for use as a node's ID""" env = self.state.document.settings.env return "qapidoc-%d" % env.new_serialno("qapidoc") - def transmogrify(self, schema) -> nodes.Element: + def transmogrify(self, schema: QAPISchema) -> nodes.Element: raise NotImplementedError - def legacy(self, schema) -> nodes.Element: + def legacy(self, schema: QAPISchema) -> nodes.Element: vis = QAPISchemaGenRSTVisitor(self) vis.visit_begin(schema) for doc in schema.docs: @@ -109,9 +119,9 @@ class QAPIDocDirective(NestedDirective): vis.symbol(doc, schema.lookup_entity(doc.symbol)) else: vis.freeform(doc) - return vis.get_document_node() + return vis.get_document_node() # type: ignore - def run(self): + def run(self) -> Sequence[nodes.Node]: env = self.state.document.settings.env qapifile = env.config.qapidoc_srctree + "/" + self.arguments[0] qapidir = os.path.dirname(qapifile) @@ -185,7 +195,7 @@ class QMPExample(CodeBlock, NestedDirective): ) return node - def admonition_wrap(self, *content) -> List[nodes.Node]: + def admonition_wrap(self, *content: nodes.Node) -> List[nodes.Node]: title = "Example:" if "title" in self.options: title = f"{title} {self.options['title']}" @@ -231,7 +241,7 @@ class QMPExample(CodeBlock, NestedDirective): return self.admonition_wrap(*content_nodes) -def setup(app): +def setup(app: Sphinx) -> ExtensionMetadata: """Register qapi-doc directive with Sphinx""" app.add_config_value("qapidoc_srctree", None, "env") app.add_directive("qapi-doc", QAPIDocDirective) diff --git a/docs/sphinx/qapidoc_legacy.py b/docs/sphinx/qapidoc_legacy.py index 679f38356b..13520f4c26 100644 --- a/docs/sphinx/qapidoc_legacy.py +++ b/docs/sphinx/qapidoc_legacy.py @@ -1,4 +1,5 @@ # coding=utf-8 +# type: ignore # # QEMU qapidoc QAPI file parsing extension # From bd7c1496aa695f7b7a560a2caf29f4b84f8752af Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:37 -0400 Subject: [PATCH 2516/2892] docs/qapidoc: add transmogrifier class stub Add the beginnings of the Transmogrifier class by adding the rST conversion helpers that will be used to build the virtual rST document. This version of the class does not actually "do anything" yet; each individual feature is added one-at-a-time in the forthcoming commits. Signed-off-by: John Snow Message-ID: <20250311034303.75779-40-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 73 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 5246832b68..c243bb6faa 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -26,14 +26,17 @@ https://www.sphinx-doc.org/en/master/development/index.html from __future__ import annotations +from contextlib import contextmanager import os import sys from typing import TYPE_CHECKING from docutils import nodes from docutils.parsers.rst import Directive, directives +from docutils.statemachine import StringList from qapi.error import QAPIError from qapi.schema import QAPISchema, QAPISchemaVisitor +from qapi.source import QAPISourceInfo from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore from sphinx import addnodes @@ -44,9 +47,12 @@ from sphinx.util.nodes import nested_parse_with_titles if TYPE_CHECKING: - from typing import Any, List, Sequence - - from docutils.statemachine import StringList + from typing import ( + Any, + Generator, + List, + Sequence, + ) from sphinx.application import Sphinx from sphinx.util.typing import ExtensionMetadata @@ -55,6 +61,67 @@ if TYPE_CHECKING: __version__ = "1.0" +class Transmogrifier: + def __init__(self) -> None: + self._result = StringList() + self.indent = 0 + + # General-purpose rST generation functions + + def get_indent(self) -> str: + return " " * self.indent + + @contextmanager + def indented(self) -> Generator[None]: + self.indent += 1 + try: + yield + finally: + self.indent -= 1 + + def add_line_raw(self, line: str, source: str, *lineno: int) -> None: + """Append one line of generated reST to the output.""" + + # NB: Sphinx uses zero-indexed lines; subtract one. + lineno = tuple((n - 1 for n in lineno)) + + if line.strip(): + # not a blank line + self._result.append( + self.get_indent() + line.rstrip("\n"), source, *lineno + ) + else: + self._result.append("", source, *lineno) + + def add_line(self, content: str, info: QAPISourceInfo) -> None: + # NB: We *require* an info object; this works out OK because we + # don't document built-in objects that don't have + # one. Everything else should. + self.add_line_raw(content, info.fname, info.line) + + def add_lines( + self, + content: str, + info: QAPISourceInfo, + ) -> None: + lines = content.splitlines(True) + for i, line in enumerate(lines): + self.add_line_raw(line, info.fname, info.line + i) + + def ensure_blank_line(self) -> None: + # Empty document -- no blank line required. + if not self._result: + return + + # Last line isn't blank, add one. + if self._result[-1].strip(): # pylint: disable=no-member + fname, line = self._result.info(-1) + assert isinstance(line, int) + # New blank line is credited to one-after the current last line. + # +2: correct for zero/one index, then increment by one. + self.add_line_raw("", fname, line + 2) + + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module From 5edd7411c4f25a43400c5b8d6e5647603942f36b Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:38 -0400 Subject: [PATCH 2517/2892] docs/qapidoc: add visit_module() method This method annotates the start of a new module, crediting the source location to the first line of the module file. Signed-off-by: John Snow Message-ID: <20250311034303.75779-41-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index c243bb6faa..6de8c90054 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -28,6 +28,7 @@ from __future__ import annotations from contextlib import contextmanager import os +from pathlib import Path import sys from typing import TYPE_CHECKING @@ -121,6 +122,14 @@ class Transmogrifier: # +2: correct for zero/one index, then increment by one. self.add_line_raw("", fname, line + 2) + # Transmogrification core methods + + def visit_module(self, path: str) -> None: + name = Path(path).stem + # module directives are credited to the first line of a module file. + self.add_line_raw(f".. qapi:module:: {name}", path, 1) + self.ensure_blank_line() + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module From 36e4182f4086edf3e7bbc5202bd692678d454793 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:39 -0400 Subject: [PATCH 2518/2892] qapi/source: allow multi-line QAPISourceInfo advancing This is for the sake of the new rST generator (the "transmogrifier") so we can advance multiple lines on occasion while keeping the generated<-->source mappings accurate. next_line now simply takes an optional n parameter which chooses the number of lines to advance. The next patch will use this when converting section syntax in free-form documentation to more traditional rST section header syntax, which does not always line up 1:1 for line counts. For example: ``` ## # = Section <-- Info is pointing here, "L1" # # Lorem Ipsum ## ``` would be transformed to rST as: ``` ======= <-- L1 Section <-- L1 ======= <-- L1 <-- L2 Lorem Ipsum <-- L3 ``` After consuming the single "Section" line from the source, we want to advance the source pointer to the next non-empty line which requires jumping by more than one line. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-ID: <20250311034303.75779-42-jsnow@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/source.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/source.py b/scripts/qapi/source.py index 7b379fdc92..ffdc3f482a 100644 --- a/scripts/qapi/source.py +++ b/scripts/qapi/source.py @@ -47,9 +47,9 @@ class QAPISourceInfo: self.defn_meta = meta self.defn_name = name - def next_line(self: T) -> T: + def next_line(self: T, n: int = 1) -> T: info = copy.copy(self) - info.line += 1 + info.line += n return info def loc(self) -> str: From f0b2fe99f63023e25b827449d54ae3623339217e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:40 -0400 Subject: [PATCH 2519/2892] docs/qapidoc: add visit_freeform() method Add the transmogrifier implementation for converting freeform doc blocks to rST. Signed-off-by: John Snow Message-ID: <20250311034303.75779-43-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 6de8c90054..ddad604145 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -29,6 +29,7 @@ from __future__ import annotations from contextlib import contextmanager import os from pathlib import Path +import re import sys from typing import TYPE_CHECKING @@ -55,6 +56,8 @@ if TYPE_CHECKING: Sequence, ) + from qapi.parser import QAPIDoc + from sphinx.application import Sphinx from sphinx.util.typing import ExtensionMetadata @@ -130,6 +133,47 @@ class Transmogrifier: self.add_line_raw(f".. qapi:module:: {name}", path, 1) self.ensure_blank_line() + def visit_freeform(self, doc: QAPIDoc) -> None: + # TODO: Once the old qapidoc transformer is deprecated, freeform + # sections can be updated to pure rST, and this transformed removed. + # + # For now, translate our micro-format into rST. Code adapted + # from Peter Maydell's freeform(). + + assert len(doc.all_sections) == 1, doc.all_sections + body = doc.all_sections[0] + text = body.text + info = doc.info + + if re.match(r"=+ ", text): + # Section/subsection heading (if present, will always be the + # first line of the block) + (heading, _, text) = text.partition("\n") + (leader, _, heading) = heading.partition(" ") + # Implicit +1 for heading in the containing .rst doc + level = len(leader) + 1 + + # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections + markers = ' #*=_^"' + overline = level <= 2 + marker = markers[level] + + self.ensure_blank_line() + # This credits all 2 or 3 lines to the single source line. + if overline: + self.add_line(marker * len(heading), info) + self.add_line(heading, info) + self.add_line(marker * len(heading), info) + self.ensure_blank_line() + + # Eat blank line(s) and advance info + trimmed = text.lstrip("\n") + text = trimmed + info = info.next_line(len(text) - len(trimmed) + 1) + + self.add_lines(text, info) + self.ensure_blank_line() + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module From 803df114fd68b5e7a3d6c60b162c0013cf6966e6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:41 -0400 Subject: [PATCH 2520/2892] docs/qapidoc: add preamble() method This method adds the options/preamble to each definition block. Notably, :since: and :ifcond: are added, as are any "special features" such as :deprecated: and :unstable:. If conditionals, if attached to special features, are currently unhandled in this patch and will be addressed at a future date. We currently do not have any if conditionals attached to special features. Signed-off-by: John Snow Message-ID: <20250311034303.75779-44-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index ddad604145..f56aa6d1fd 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -37,7 +37,12 @@ from docutils import nodes from docutils.parsers.rst import Directive, directives from docutils.statemachine import StringList from qapi.error import QAPIError -from qapi.schema import QAPISchema, QAPISchemaVisitor +from qapi.parser import QAPIDoc +from qapi.schema import ( + QAPISchema, + QAPISchemaDefinition, + QAPISchemaVisitor, +) from qapi.source import QAPISourceInfo from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore @@ -56,8 +61,6 @@ if TYPE_CHECKING: Sequence, ) - from qapi.parser import QAPIDoc - from sphinx.application import Sphinx from sphinx.util.typing import ExtensionMetadata @@ -125,6 +128,38 @@ class Transmogrifier: # +2: correct for zero/one index, then increment by one. self.add_line_raw("", fname, line + 2) + # Transmogrification helpers + + def preamble(self, ent: QAPISchemaDefinition) -> None: + """ + Generate option lines for QAPI entity directives. + """ + if ent.doc and ent.doc.since: + assert ent.doc.since.kind == QAPIDoc.Kind.SINCE + # Generated from the entity's docblock; info location is exact. + self.add_line(f":since: {ent.doc.since.text}", ent.doc.since.info) + + if ent.ifcond.is_present(): + doc = ent.ifcond.docgen() + assert ent.info + # Generated from entity definition; info location is approximate. + self.add_line(f":ifcond: {doc}", ent.info) + + # Hoist special features such as :deprecated: and :unstable: + # into the options block for the entity. If, in the future, new + # special features are added, qapi-domain will chirp about + # unrecognized options and fail until they are handled in + # qapi-domain. + for feat in ent.features: + if feat.is_special(): + # FIXME: handle ifcond if present. How to display that + # information is TBD. + # Generated from entity def; info location is approximate. + assert feat.info + self.add_line(f":{feat.name}:", feat.info) + + self.ensure_blank_line() + # Transmogrification core methods def visit_module(self, path: str) -> None: From 56e1adf293037ec3324a7f20d54d50cd671dc281 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:42 -0400 Subject: [PATCH 2521/2892] docs/qapidoc: add visit_paragraph() method This transforms "formerly known as untagged sections" into our pure intermediate rST format. These sections are already pure rST, so this method doesn't do a whole lot except ensure appropriate newlines. Signed-off-by: John Snow Message-ID: <20250311034303.75779-45-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index f56aa6d1fd..a9f98d4657 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -130,6 +130,15 @@ class Transmogrifier: # Transmogrification helpers + def visit_paragraph(self, section: QAPIDoc.Section) -> None: + # Squelch empty paragraphs. + if not section.text: + return + + self.ensure_blank_line() + self.add_lines(section.text, section.info) + self.ensure_blank_line() + def preamble(self, ent: QAPISchemaDefinition) -> None: """ Generate option lines for QAPI entity directives. From e9fbf1a0c6c2df9c53bb577b4245cf48914687fe Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:43 -0400 Subject: [PATCH 2522/2892] docs/qapidoc: add visit_errors() method Notably, this method does not currently address the formatting issues present with the "errors" section in QAPIDoc and just vomits the text verbatim into the rST doc, with somewhat inconsistent results. To be addressed in a future patch. Signed-off-by: John Snow Message-ID: <20250311034303.75779-46-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index a9f98d4657..c17cb9f9b1 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -139,6 +139,12 @@ class Transmogrifier: self.add_lines(section.text, section.info) self.ensure_blank_line() + def visit_errors(self, section: QAPIDoc.Section) -> None: + # FIXME: the formatting for errors may be inconsistent and may + # or may not require different newline placement to ensure + # proper rendering as a nested list. + self.add_lines(f":error:\n{section.text}", section.info) + def preamble(self, ent: QAPISchemaDefinition) -> None: """ Generate option lines for QAPI entity directives. From 3a396a865be6a55322baa854c470c43c0a9f64e6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:44 -0400 Subject: [PATCH 2523/2892] docs/qapidoc: add format_type() method This method is responsible for generating a type name for a given member with the correct annotations for the QAPI domain. Features and enums do not *have* types, so they return None. Everything else returns the type name with a "?" suffix if that type is optional, and ensconced in [brackets] if it's an array type. Signed-off-by: John Snow Message-ID: <20250311034303.75779-47-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index c17cb9f9b1..5144bb965a 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -40,7 +40,13 @@ from qapi.error import QAPIError from qapi.parser import QAPIDoc from qapi.schema import ( QAPISchema, + QAPISchemaArrayType, QAPISchemaDefinition, + QAPISchemaEnumMember, + QAPISchemaFeature, + QAPISchemaMember, + QAPISchemaObjectTypeMember, + QAPISchemaType, QAPISchemaVisitor, ) from qapi.source import QAPISourceInfo @@ -58,7 +64,9 @@ if TYPE_CHECKING: Any, Generator, List, + Optional, Sequence, + Union, ) from sphinx.application import Sphinx @@ -128,6 +136,30 @@ class Transmogrifier: # +2: correct for zero/one index, then increment by one. self.add_line_raw("", fname, line + 2) + def format_type( + self, ent: Union[QAPISchemaDefinition | QAPISchemaMember] + ) -> Optional[str]: + if isinstance(ent, (QAPISchemaEnumMember, QAPISchemaFeature)): + return None + + qapi_type = ent + optional = False + if isinstance(ent, QAPISchemaObjectTypeMember): + qapi_type = ent.type + optional = ent.optional + + if isinstance(qapi_type, QAPISchemaArrayType): + ret = f"[{qapi_type.element_type.doc_type()}]" + else: + assert isinstance(qapi_type, QAPISchemaType) + tmp = qapi_type.doc_type() + assert tmp + ret = tmp + if optional: + ret += "?" + + return ret + # Transmogrification helpers def visit_paragraph(self, section: QAPIDoc.Section) -> None: From 604df9bb001fc66f77d349ce63e870af84b42d96 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:45 -0400 Subject: [PATCH 2524/2892] docs/qapidoc: add add_field() and generate_field() helper methods These are simple rST generation methods that assist in getting the types and formatting correct for a field list entry. add_field() is a more raw, direct call while generate_field() is intended to be used for generating the correct field from a member object. Signed-off-by: John Snow Message-ID: <20250311034303.75779-48-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 5144bb965a..2f85fe0bc3 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -136,6 +136,20 @@ class Transmogrifier: # +2: correct for zero/one index, then increment by one. self.add_line_raw("", fname, line + 2) + def add_field( + self, + kind: str, + name: str, + body: str, + info: QAPISourceInfo, + typ: Optional[str] = None, + ) -> None: + if typ: + text = f":{kind} {typ} {name}: {body}" + else: + text = f":{kind} {name}: {body}" + self.add_lines(text, info) + def format_type( self, ent: Union[QAPISchemaDefinition | QAPISchemaMember] ) -> Optional[str]: @@ -160,6 +174,16 @@ class Transmogrifier: return ret + def generate_field( + self, + kind: str, + member: QAPISchemaMember, + body: str, + info: QAPISourceInfo, + ) -> None: + typ = self.format_type(member) + self.add_field(kind, member.name, body, info, typ) + # Transmogrification helpers def visit_paragraph(self, section: QAPIDoc.Section) -> None: From 6c43b008c4b6338f49b6dffb82437285bf98b97a Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:46 -0400 Subject: [PATCH 2525/2892] docs/qapidoc: add visit_feature() method This adds a simple ":feat name: lorem ipsum ..." line to the generated rST document, so at the moment it's only for "top level" features. Features not attached directly to a QAPI definition are not currently handled! This is a small regression over the prior documentation generator that will be addressed in a future patch. Signed-off-by: John Snow Message-ID: <20250311034303.75779-49-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 2f85fe0bc3..208d7ca144 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -195,6 +195,15 @@ class Transmogrifier: self.add_lines(section.text, section.info) self.ensure_blank_line() + def visit_feature(self, section: QAPIDoc.ArgSection) -> None: + # FIXME - ifcond for features is not handled at all yet! + # Proposal: decorate the right-hand column with some graphical + # element to indicate conditional availability? + assert section.text # Guaranteed by parser.py + assert section.member + + self.generate_field("feat", section.member, section.text, section.info) + def visit_errors(self, section: QAPIDoc.Section) -> None: # FIXME: the formatting for errors may be inconsistent and may # or may not require different newline placement to ensure From 38a349ff5b9ae583fe8a66e3e507ea9954b1aeb1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:47 -0400 Subject: [PATCH 2526/2892] docs/qapidoc: prepare to record entity being transmogrified Prepare to keep a record of which entity we're working on documenting for the purposes of being able to change certain generative features conditionally and create stronger assertions. If you find yourself asking: "Wait, but where does the current entity actually get recorded?!", you're right! That part comes with the visit_entity() implementation, which gets added later. This patch is front-loaded for the sake of type checking in the forthcoming commits before visit_entity() is ready to be added. Signed-off-by: John Snow Message-ID: <20250311034303.75779-50-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 208d7ca144..47c2eeef87 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -78,9 +78,15 @@ __version__ = "1.0" class Transmogrifier: def __init__(self) -> None: + self._curr_ent: Optional[QAPISchemaDefinition] = None self._result = StringList() self.indent = 0 + @property + def entity(self) -> QAPISchemaDefinition: + assert self._curr_ent is not None + return self._curr_ent + # General-purpose rST generation functions def get_indent(self) -> str: From 52c806cad08bfed53bf11c1a49bb203cc53deea0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:48 -0400 Subject: [PATCH 2527/2892] docs/qapidoc: add visit_returns() method Generates :return: fields for explicit returns statements. Note that this does not presently handle undocumented returns, which is handled in a later commit. Signed-off-by: John Snow Message-ID: <20250311034303.75779-51-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 47c2eeef87..eb8841099c 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -41,6 +41,7 @@ from qapi.parser import QAPIDoc from qapi.schema import ( QAPISchema, QAPISchemaArrayType, + QAPISchemaCommand, QAPISchemaDefinition, QAPISchemaEnumMember, QAPISchemaFeature, @@ -210,6 +211,20 @@ class Transmogrifier: self.generate_field("feat", section.member, section.text, section.info) + def visit_returns(self, section: QAPIDoc.Section) -> None: + assert isinstance(self.entity, QAPISchemaCommand) + rtype = self.entity.ret_type + # q_empty can produce None, but we won't be documenting anything + # without an explicit return statement in the doc block, and we + # should not have any such explicit statements when there is no + # return value. + assert rtype + + typ = self.format_type(rtype) + assert typ + assert section.text + self.add_field("return", typ, section.text, section.info) + def visit_errors(self, section: QAPIDoc.Section) -> None: # FIXME: the formatting for errors may be inconsistent and may # or may not require different newline placement to ensure From dbf51d15fdbb5410e21540d47c16f505413ce1eb Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:49 -0400 Subject: [PATCH 2528/2892] docs/qapidoc: add visit_member() method This method is used for generating the "members" of a wide variety of things, including structs, unions, enums, alternates, etc. The field name it uses to do so is dependent on the type of entity the "member" belongs to. Currently, IF conditionals for individual members are not handled or rendered, a small regression from the prior documentation generator. This will be fixed in a future patch. Signed-off-by: John Snow Message-ID: <20250311034303.75779-52-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index eb8841099c..a8e19487d0 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -78,6 +78,16 @@ __version__ = "1.0" class Transmogrifier: + # Field names used for different entity types: + field_types = { + "enum": "value", + "struct": "memb", + "union": "memb", + "event": "memb", + "command": "arg", + "alternate": "alt", + } + def __init__(self) -> None: self._curr_ent: Optional[QAPISchemaDefinition] = None self._result = StringList() @@ -88,6 +98,10 @@ class Transmogrifier: assert self._curr_ent is not None return self._curr_ent + @property + def member_field_type(self) -> str: + return self.field_types[self.entity.meta] + # General-purpose rST generation functions def get_indent(self) -> str: @@ -202,6 +216,19 @@ class Transmogrifier: self.add_lines(section.text, section.info) self.ensure_blank_line() + def visit_member(self, section: QAPIDoc.ArgSection) -> None: + # FIXME: ifcond for members + # TODO: features for members (documented at entity-level, + # but sometimes defined per-member. Should we add such + # information to member descriptions when we can?) + assert section.text and section.member + self.generate_field( + self.member_field_type, + section.member, + section.text, + section.info, + ) + def visit_feature(self, section: QAPIDoc.ArgSection) -> None: # FIXME - ifcond for features is not handled at all yet! # Proposal: decorate the right-hand column with some graphical From 8cb0a41490364400bc6b1c361b8e1abf9c21bd76 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:50 -0400 Subject: [PATCH 2529/2892] docs/qapidoc: add visit_sections() method Implement the actual main dispatch method that processes and handles the list of doc sections for a given QAPI entity. Process doc sections in strict source order. This is good; reordering doc text is undesirable. Improvement over the old doc generator, which can reorder doc comments that don't adhere to (largely unspoken) conventions. Signed-off-by: John Snow Message-ID: <20250311034303.75779-53-jsnow@redhat.com> Acked-by: Markus Armbruster [Commit message extended] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index a8e19487d0..83022b15ca 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -288,6 +288,31 @@ class Transmogrifier: self.ensure_blank_line() + def visit_sections(self, ent: QAPISchemaDefinition) -> None: + sections = ent.doc.all_sections if ent.doc else [] + + # Add sections in source order: + for section in sections: + if section.kind == QAPIDoc.Kind.PLAIN: + self.visit_paragraph(section) + elif section.kind == QAPIDoc.Kind.MEMBER: + assert isinstance(section, QAPIDoc.ArgSection) + self.visit_member(section) + elif section.kind == QAPIDoc.Kind.FEATURE: + assert isinstance(section, QAPIDoc.ArgSection) + self.visit_feature(section) + elif section.kind in (QAPIDoc.Kind.SINCE, QAPIDoc.Kind.TODO): + # Since is handled in preamble, TODO is skipped intentionally. + pass + elif section.kind == QAPIDoc.Kind.RETURNS: + self.visit_returns(section) + elif section.kind == QAPIDoc.Kind.ERRORS: + self.visit_errors(section) + else: + assert False + + self.ensure_blank_line() + # Transmogrification core methods def visit_module(self, path: str) -> None: From c05de7235a24dd1719ee6132fc45802b15ce49df Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:51 -0400 Subject: [PATCH 2530/2892] docs/qapidoc: add visit_entity() Finally, the core entry method for a qapi entity. Signed-off-by: John Snow Message-ID: <20250311034303.75779-54-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 83022b15ca..aaf5b6e22b 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -78,6 +78,8 @@ __version__ = "1.0" class Transmogrifier: + # pylint: disable=too-many-public-methods + # Field names used for different entity types: field_types = { "enum": "value", @@ -362,6 +364,25 @@ class Transmogrifier: self.add_lines(text, info) self.ensure_blank_line() + def visit_entity(self, ent: QAPISchemaDefinition) -> None: + assert ent.info + + try: + self._curr_ent = ent + + # Squish structs and unions together into an "object" directive. + meta = ent.meta + if meta in ("struct", "union"): + meta = "object" + + # This line gets credited to the start of the /definition/. + self.add_line(f".. qapi:{meta}:: {ent.name}", ent.info) + with self.indented(): + self.preamble(ent) + self.visit_sections(ent) + finally: + self._curr_ent = None + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module From 5c1636f7cc4bfbe5737e3acca009a97dfa188879 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:52 -0400 Subject: [PATCH 2531/2892] docs/qapidoc: implement transmogrify() method This is the true top-level processor for the new transmogrifier; responsible both for generating the intermediate rST and then running the nested parse on that generated document to produce the final docutils tree that is then - very finally - postprocessed by sphinx for final rendering to HTML &c. Signed-off-by: John Snow Message-ID: <20250311034303.75779-55-jsnow@redhat.com> Acked-by: Markus Armbruster [Use the opportunity to move the __version__ assignment to where PEP 8 wants it] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 49 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index aaf5b6e22b..a0016f8853 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -2,6 +2,7 @@ # # QEMU qapidoc QAPI file parsing extension # +# Copyright (c) 2024-2025 Red Hat # Copyright (c) 2020 Linaro # # This work is licensed under the terms of the GNU GPLv2 or later. @@ -26,6 +27,8 @@ https://www.sphinx-doc.org/en/master/development/index.html from __future__ import annotations +__version__ = "2.0" + from contextlib import contextmanager import os from pathlib import Path @@ -56,6 +59,7 @@ from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore from sphinx import addnodes from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError +from sphinx.util import logging from sphinx.util.docutils import switch_source_input from sphinx.util.nodes import nested_parse_with_titles @@ -74,7 +78,7 @@ if TYPE_CHECKING: from sphinx.util.typing import ExtensionMetadata -__version__ = "1.0" +logger = logging.getLogger(__name__) class Transmogrifier: @@ -95,6 +99,10 @@ class Transmogrifier: self._result = StringList() self.indent = 0 + @property + def result(self) -> StringList: + return self._result + @property def entity(self) -> QAPISchemaDefinition: assert self._curr_ent is not None @@ -438,7 +446,43 @@ class QAPIDocDirective(NestedDirective): return "qapidoc-%d" % env.new_serialno("qapidoc") def transmogrify(self, schema: QAPISchema) -> nodes.Element: - raise NotImplementedError + logger.info("Transmogrifying QAPI to rST ...") + vis = Transmogrifier() + modules = set() + + for doc in schema.docs: + module_source = doc.info.fname + if module_source not in modules: + vis.visit_module(module_source) + modules.add(module_source) + + if doc.symbol: + ent = schema.lookup_entity(doc.symbol) + assert isinstance(ent, QAPISchemaDefinition) + vis.visit_entity(ent) + else: + vis.visit_freeform(doc) + + logger.info("Transmogrification complete.") + + contentnode = nodes.section() + content = vis.result + titles_allowed = True + + logger.info("Transmogrifier running nested parse ...") + with switch_source_input(self.state, content): + if titles_allowed: + node: nodes.Element = nodes.section() + node.document = self.state.document + nested_parse_with_titles(self.state, content, contentnode) + else: + node = nodes.paragraph() + node.document = self.state.document + self.state.nested_parse(content, 0, contentnode) + logger.info("Transmogrifier's nested parse completed.") + sys.stdout.flush() + + return contentnode def legacy(self, schema: QAPISchema) -> nodes.Element: vis = QAPISchemaGenRSTVisitor(self) @@ -572,6 +616,7 @@ class QMPExample(CodeBlock, NestedDirective): def setup(app: Sphinx) -> ExtensionMetadata: """Register qapi-doc directive with Sphinx""" + app.setup_extension("qapi_domain") app.add_config_value("qapidoc_srctree", None, "env") app.add_directive("qapi-doc", QAPIDocDirective) app.add_directive("qmp-example", QMPExample) From c9b6f988037b3d6f042871b149c7fd307bed8475 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:53 -0400 Subject: [PATCH 2532/2892] docs/qapidoc: process @foo into ``foo`` Add support for the special QAPI doc syntax to process @references as ``preformatted text``. At the moment, there are no actual cross-references for individual members, so there is nothing to link against. For now, process it identically to how we did in the old qapidoc system. Signed-off-by: John Snow Message-ID: <20250311034303.75779-56-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index a0016f8853..ebdf709594 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -303,6 +303,9 @@ class Transmogrifier: # Add sections in source order: for section in sections: + # @var is translated to ``var``: + section.text = re.sub(r"@([\w-]+)", r"``\1``", section.text) + if section.kind == QAPIDoc.Kind.PLAIN: self.visit_paragraph(section) elif section.kind == QAPIDoc.Kind.MEMBER: From 7f6f24aaf55b567df2729d283cf8eac57c399493 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:54 -0400 Subject: [PATCH 2533/2892] docs/qapidoc: add intermediate output debugger Add debugging output for the qapidoc transmogrifier - setting DEBUG=1 will produce .ir files (one for each qapidoc directive) that write the generated rst file to disk to allow for easy debugging and verification of the generated document. Signed-off-by: John Snow Message-ID: <20250311034303.75779-57-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index ebdf709594..8ddebf73f2 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -37,7 +37,7 @@ import sys from typing import TYPE_CHECKING from docutils import nodes -from docutils.parsers.rst import Directive, directives +from docutils.parsers.rst import directives from docutils.statemachine import StringList from qapi.error import QAPIError from qapi.parser import QAPIDoc @@ -60,7 +60,7 @@ from sphinx import addnodes from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError from sphinx.util import logging -from sphinx.util.docutils import switch_source_input +from sphinx.util.docutils import SphinxDirective, switch_source_input from sphinx.util.nodes import nested_parse_with_titles @@ -414,7 +414,7 @@ class QAPISchemaGenDepVisitor(QAPISchemaVisitor): super().visit_module(name) -class NestedDirective(Directive): +class NestedDirective(SphinxDirective): def run(self) -> Sequence[nodes.Node]: raise NotImplementedError @@ -483,10 +483,43 @@ class QAPIDocDirective(NestedDirective): node.document = self.state.document self.state.nested_parse(content, 0, contentnode) logger.info("Transmogrifier's nested parse completed.") - sys.stdout.flush() + if self.env.app.verbosity >= 2 or os.environ.get("DEBUG"): + argname = "_".join(Path(self.arguments[0]).parts) + name = Path(argname).stem + ".ir" + self.write_intermediate(content, name) + + sys.stdout.flush() return contentnode + def write_intermediate(self, content: StringList, filename: str) -> None: + logger.info( + "writing intermediate rST for '%s' to '%s'", + self.arguments[0], + filename, + ) + + srctree = Path(self.env.app.config.qapidoc_srctree).resolve() + outlines = [] + lcol_width = 0 + + for i, line in enumerate(content): + src, lineno = content.info(i) + srcpath = Path(src).resolve() + srcpath = srcpath.relative_to(srctree) + + lcol = f"{srcpath}:{lineno:04d}" + lcol_width = max(lcol_width, len(lcol)) + outlines.append((lcol, line)) + + with open(filename, "w", encoding="UTF-8") as outfile: + for lcol, rcol in outlines: + outfile.write(lcol.rjust(lcol_width)) + outfile.write(" |") + if rcol: + outfile.write(f" {rcol}") + outfile.write("\n") + def legacy(self, schema: QAPISchema) -> nodes.Element: vis = QAPISchemaGenRSTVisitor(self) vis.visit_begin(schema) From 1884492e64da659323a8da4de98b344bc689f62a Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:55 -0400 Subject: [PATCH 2534/2892] docs/qapidoc: Add "the members of" pointers Add "the members of ..." pointers to Members and Arguments lists where appropriate, with clickable cross-references - so it's a slight improvement over the old system :) This patch is meant to be a temporary solution until we can review and merge the inliner. The implementation of this patch is a little bit of a hack: Sphinx is not designed to allow you to mix fields of different "type"; i.e. mixing member descriptions and free-form text under the same heading. To accomplish this with a minimum of hackery, we technically document a "dummy field" and then just strip off the documentation for that dummy field in a post-processing step. We use the "q_dummy" variable for this purpose, then strip it back out before final processing. If this processing step should fail, you'll see warnings for a bad cross-reference. (So if you don't see any, it must be working!) Signed-off-by: John Snow Message-ID: <20250311034303.75779-58-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 22 +++++++++++++-- docs/sphinx/qapidoc.py | 58 +++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index ca3f3a7e2d..7ff618d8cd 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -433,6 +433,24 @@ class QAPIObject(QAPIDescription): self._validate_field(field) +class SpecialTypedField(CompatTypedField): + def make_field(self, *args: Any, **kwargs: Any) -> nodes.field: + ret = super().make_field(*args, **kwargs) + + # Look for the characteristic " -- " text node that Sphinx + # inserts for each TypedField entry ... + for node in ret.traverse(lambda n: str(n) == " -- "): + par = node.parent + if par.children[0].astext() != "q_dummy": + continue + + # If the first node's text is q_dummy, this is a dummy + # field we want to strip down to just its contents. + del par.children[:-1] + + return ret + + class QAPICommand(QAPIObject): """Description of a QAPI Command.""" @@ -440,7 +458,7 @@ class QAPICommand(QAPIObject): doc_field_types.extend( [ # :arg TypeName ArgName: descr - CompatTypedField( + SpecialTypedField( "argument", label=_("Arguments"), names=("arg",), @@ -508,7 +526,7 @@ class QAPIObjectWithMembers(QAPIObject): doc_field_types.extend( [ # :member type name: descr - CompatTypedField( + SpecialTypedField( "member", label=_("Members"), names=("memb",), diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 8ddebf73f2..a2d6f648a2 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -47,8 +47,10 @@ from qapi.schema import ( QAPISchemaCommand, QAPISchemaDefinition, QAPISchemaEnumMember, + QAPISchemaEvent, QAPISchemaFeature, QAPISchemaMember, + QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, QAPISchemaVisitor, @@ -298,11 +300,61 @@ class Transmogrifier: self.ensure_blank_line() + def _insert_member_pointer(self, ent: QAPISchemaDefinition) -> None: + + def _get_target( + ent: QAPISchemaDefinition, + ) -> Optional[QAPISchemaDefinition]: + if isinstance(ent, (QAPISchemaCommand, QAPISchemaEvent)): + return ent.arg_type + if isinstance(ent, QAPISchemaObjectType): + return ent.base + return None + + target = _get_target(ent) + if target is not None and not target.is_implicit(): + assert ent.info + self.add_field( + self.member_field_type, + "q_dummy", + f"The members of :qapi:type:`{target.name}`.", + ent.info, + "q_dummy", + ) + + if isinstance(ent, QAPISchemaObjectType) and ent.branches is not None: + for variant in ent.branches.variants: + if variant.type.name == "q_empty": + continue + assert ent.info + self.add_field( + self.member_field_type, + "q_dummy", + f" When ``{ent.branches.tag_member.name}`` is " + f"``{variant.name}``: " + f"The members of :qapi:type:`{variant.type.name}`.", + ent.info, + "q_dummy", + ) + def visit_sections(self, ent: QAPISchemaDefinition) -> None: sections = ent.doc.all_sections if ent.doc else [] + # Determine the index location at which we should generate + # documentation for "The members of ..." pointers. This should + # go at the end of the members section(s) if any. Note that + # index 0 is assumed to be a plain intro section, even if it is + # empty; and that a members section if present will always + # immediately follow the opening PLAIN section. + gen_index = 1 + if len(sections) > 1: + while sections[gen_index].kind == QAPIDoc.Kind.MEMBER: + gen_index += 1 + if gen_index >= len(sections): + break + # Add sections in source order: - for section in sections: + for i, section in enumerate(sections): # @var is translated to ``var``: section.text = re.sub(r"@([\w-]+)", r"``\1``", section.text) @@ -324,6 +376,10 @@ class Transmogrifier: else: assert False + # Generate "The members of ..." entries if necessary: + if i == gen_index - 1: + self._insert_member_pointer(ent) + self.ensure_blank_line() # Transmogrification core methods From 565274da10fe46bf8c30a8175e39753e4fedb60e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:56 -0400 Subject: [PATCH 2535/2892] docs/qapidoc: generate entries for undocumented members Presently, we never have any empty text entries for members. The next patch will explicitly generate such sections, so enable support for it in advance. The parser will generate placeholder sections to indicate undocumented members, but it's the qapidoc generator that's responsible for deciding what to do with that stub section. Signed-off-by: John Snow Message-ID: <20250311034303.75779-59-jsnow@redhat.com> Acked-by: Markus Armbruster [Tweak the stub section text] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index a2d6f648a2..432fef04b0 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -233,11 +233,12 @@ class Transmogrifier: # TODO: features for members (documented at entity-level, # but sometimes defined per-member. Should we add such # information to member descriptions when we can?) - assert section.text and section.member + assert section.member self.generate_field( self.member_field_type, section.member, - section.text, + # TODO drop fallbacks when undocumented members are outlawed + section.text if section.text else "Not documented", section.info, ) From 4d7d30b405a16d85ede6184595e8001fe2c412c4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:57 -0400 Subject: [PATCH 2536/2892] qapi/parser: add undocumented stub members to all_sections Parser and doc generator cooperate on generating stub documentation for undocumented members. The parser makes up an ArgSection with an empty description, and the doc generator makes up a description. Right now, the made-up ArgSections go into doc.args. However, the new doc generator uses .all_sections, not .args. So put them into .all_sections, too. Insert them right after existing 'member' sections. If there are none, insert directly after the leading section. Doesn't affect the old generator, because that one doesn't use .all_sections. Signed-off-by: John Snow Message-ID: <20250311034303.75779-60-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Commit message rewritten] Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 11c11bb09e..949d9e8bff 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -789,8 +789,23 @@ class QAPIDoc: raise QAPISemError(member.info, "%s '%s' lacks documentation" % (member.role, member.name)) - self.args[member.name] = QAPIDoc.ArgSection( + # Insert stub documentation section for missing member docs. + # TODO: drop when undocumented members are outlawed + + section = QAPIDoc.ArgSection( self.info, QAPIDoc.Kind.MEMBER, member.name) + self.args[member.name] = section + + # Determine where to insert stub doc - it should go at the + # end of the members section(s), if any. Note that index 0 + # is assumed to be an untagged intro section, even if it is + # empty. + index = 1 + if len(self.all_sections) > 1: + while self.all_sections[index].kind == QAPIDoc.Kind.MEMBER: + index += 1 + self.all_sections.insert(index, section) + self.args[member.name].connect(member) def connect_feature(self, feature: 'QAPISchemaFeature') -> None: From 30ab96cf84dae15c727408b7c7361edd0d9d5b1e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:58 -0400 Subject: [PATCH 2537/2892] docs: disambiguate cross-references The next patch will engage the qapidoc transmogrifier, which creates a lot of cross-reference targets. Some of the existing targets ("migration", "qom", "replay") will become ambiguous as a result. Nail them down more explicitly to prevent ambiguous cross-reference warnings. Signed-off-by: John Snow Message-ID: <20250311034303.75779-61-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/codebase.rst | 6 +++--- docs/glossary.rst | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst index 4039875ee0..1b09953197 100644 --- a/docs/devel/codebase.rst +++ b/docs/devel/codebase.rst @@ -23,7 +23,7 @@ Some of the main QEMU subsystems are: - `Devices` & Board models - `Documentation ` - `GDB support` -- `Migration` +- :ref:`Migration` - `Monitor` - :ref:`QOM (QEMU Object Model)` - `System mode` @@ -112,7 +112,7 @@ yet, so sometimes the source code is all you have. * `libdecnumber `_: Import of gcc library, used to implement decimal number arithmetic. * `migration `__: - `Migration framework `. + :ref:`Migration framework `. * `monitor `_: `Monitor ` implementation (HMP & QMP). * `nbd `_: @@ -193,7 +193,7 @@ yet, so sometimes the source code is all you have. - `lcitool `_: Generate dockerfiles for CI containers. - `migration `_: - Test scripts and data for `Migration framework `. + Test scripts and data for :ref:`Migration framework `. - `multiboot `_: Test multiboot functionality for x86_64/i386. - `qapi-schema `_: diff --git a/docs/glossary.rst b/docs/glossary.rst index 693d9855dd..4fa044bfb6 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -120,7 +120,7 @@ Migration --------- QEMU can save and restore the execution of a virtual machine between different -host systems. This is provided by the `Migration framework`. +host systems. This is provided by the :ref:`Migration framework`. NBD --- @@ -212,14 +212,14 @@ machine emulator and virtualizer. QOM --- -`QEMU Object Model ` is an object oriented API used to define various -devices and hardware in the QEMU codebase. +:ref:`QEMU Object Model ` is an object oriented API used to define +various devices and hardware in the QEMU codebase. Record/replay ------------- -`Record/replay ` is a feature of QEMU allowing to have a deterministic -and reproducible execution of a virtual machine. +:ref:`Record/replay ` is a feature of QEMU allowing to have a +deterministic and reproducible execution of a virtual machine. Rust ---- From a377f39f38fefe7dd6835728fea96a21c658d804 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:42:59 -0400 Subject: [PATCH 2538/2892] docs: enable qapidoc transmogrifier for QEMU QMP Reference We are not enabling the transmogrifier for QSD or QGA yet because we don't (yet) have a way to create separate indices, and all of the definitions will bleed together, which isn't so nice. For now, QMP is better than nothing at all! Signed-off-by: John Snow Message-ID: <20250311034303.75779-62-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/interop/qemu-qmp-ref.rst | 1 + qapi/qapi-schema.json | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst index f94614a0b2..e95eeac45e 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -7,3 +7,4 @@ QEMU QMP Reference Manual :depth: 3 .. qapi-doc:: qapi/qapi-schema.json + :transmogrify: diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 2877aff73d..4475e81cc3 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -5,6 +5,8 @@ # # This document describes all commands currently supported by QMP. # +# For locating a particular item, please see the `qapi-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 # commands (the manpage, QEMU's manual, etc) can and should be From 42b633dbd1b5d3f7ce5c9b8e4417267399f641e1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:43:00 -0400 Subject: [PATCH 2539/2892] docs: add qapi-domain syntax documentation Who documents the documentation? Me, I guess. Signed-off-by: John Snow Message-ID: <20250311034303.75779-63-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/index-build.rst | 1 + docs/devel/qapi-domain.rst | 670 +++++++++++++++++++++++++++++++++++++ 2 files changed, 671 insertions(+) create mode 100644 docs/devel/qapi-domain.rst diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 0745c81a26..3f3cb21b9b 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -12,4 +12,5 @@ some of the basics if you are adding new files and targets to the build. kconfig docs qapi-code-gen + qapi-domain control-flow-integrity diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst new file mode 100644 index 0000000000..1475870ca6 --- /dev/null +++ b/docs/devel/qapi-domain.rst @@ -0,0 +1,670 @@ +====================== +The Sphinx QAPI Domain +====================== + +An extension to the `rST syntax +`_ +in Sphinx is provided by the QAPI Domain, located in +``docs/sphinx/qapi_domain.py``. This extension is analogous to the +`Python Domain +`_ +included with Sphinx, but provides special directives and roles +speciically for annotating and documenting QAPI definitions +specifically. + +A `Domain +`_ +provides a set of special rST directives and cross-referencing roles to +Sphinx for understanding rST markup written to document a specific +language. By itself, this QAPI extension is only sufficient to parse rST +markup written by hand; the `autodoc +`_ +functionality is provided elsewhere, in ``docs/sphinx/qapidoc.py``, by +the "Transmogrifier". + +It is not expected that any developer nor documentation writer would +never need to write *nor* read these special rST forms. However, in the +event that something needs to be debugged, knowing the syntax of the +domain is quite handy. This reference may also be useful as a guide for +understanding the QAPI Domain extension code itself. Although most of +these forms will not be needed for documentation writing purposes, +understanding the cross-referencing syntax *will* be helpful when +writing rST documentation elsewhere, or for enriching the body of +QAPIDoc blocks themselves. + + +Concepts +======== + +The QAPI Domain itself provides no mechanisms for reading the QAPI +Schema or generating documentation from code that exists. It is merely +the rST syntax used to describe things. For instance, the Sphinx Python +domain adds syntax like ``:py:func:`` for describing Python functions in +documentation, but it's the autodoc module that is responsible for +reading python code and generating such syntax. QAPI is analagous here: +qapidoc.py is responsible for reading the QAPI Schema and generating rST +syntax, and qapi_domain.py is responsible for translating that special +syntax and providing APIs for Sphinx internals. + +In other words: + +qapi_domain.py adds syntax like ``.. qapi:command::`` to Sphinx, and +qapidoc.py transforms the documentation in ``qapi/*.json`` into rST +using directives defined by the domain. + +Or even shorter: + +``:py:`` is to ``:qapi:`` as *autodoc* is to *qapidoc*. + + +Info Field Lists +================ + +`Field lists +`_ +are a standard syntax in reStructuredText. Sphinx `extends that syntax +`_ +to give certain field list entries special meaning and parsing to, for +example, add cross-references. The QAPI Domain takes advantage of this +field list extension to document things like Arguments, Members, Values, +and so on. + +The special parsing and handling of info field lists in Sphinx is provided by +three main classes; Field, GroupedField, and TypedField. The behavior +and formatting for each configured field list entry in the domain +changes depending on which class is used. + +Field: + * Creates an ungrouped field: i.e., each entry will create its own + section and they will not be combined. + * May *optionally* support an argument. + * May apply cross-reference roles to *either* the argument *or* the + content body, both, or neither. + +This is used primarily for entries which are not expected to be +repeated, i.e., items that may only show up at most once. The QAPI +domain uses this class for "Errors" section. + +GroupedField: + * Creates a grouped field: i.e. multiple adjacent entries will be + merged into one section, and the content will form a bulleted list. + * *Must* take an argument. + * May optionally apply a cross-reference role to the argument, but not + the body. + * Can be configured to remove the bulleted list if there is only a + single entry. + * All items will be generated with the form: "argument -- body" + +This is used for entries which are expected to be repeated, but aren't +expected to have two arguments, i.e. types without names, or names +without types. The QAPI domain uses this class for features, returns, +and enum values. + +TypedField: + * Creates a grouped, typed field. Multiple adjacent entres will be + merged into one section, and the content will form a bulleted list. + * *Must* take at least one argument, but supports up to two - + nominally, a name and a type. + * May optionally apply a cross-reference role to the type or the name + argument, but not the body. + * Can be configured to remove the bulleted list if there is only a + single entry. + * All items will be generated with the form "name (type) -- body" + +This is used for entries that are expected to be repeated and will have +a name, a type, and a description. The QAPI domain uses this class for +arguments, alternatives, and members. Wherever type names are referenced +below, They must be a valid, documented type that will be +cross-referenced in the HTML output; or one of the built-in JSON types +(string, number, int, boolean, null, value, q_empty). + + +``:feat:`` +---------- + +Document a feature attached to a QAPI definition. + +:availability: This field list is available in the body of Command, + Event, Enum, Object and Alternate directives. +:syntax: ``:feat name: Lorem ipsum, dolor sit amet...`` +:type: `sphinx.util.docfields.GroupedField + `_ + +Example:: + + .. qapi:object:: BlockdevOptionsVirtioBlkVhostVdpa + :since: 7.2 + :ifcond: CONFIG_BLKIO + + Driver specific block device options for the virtio-blk-vhost-vdpa + backend. + + :memb string path: path to the vhost-vdpa character device. + :feat fdset: Member ``path`` supports the special "/dev/fdset/N" path + (since 8.1) + + +``:arg:`` +--------- + +Document an argument to a QAPI command. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:arg type name: description`` +:type: `sphinx.util.docfields.TypedField + `_ + + +Example:: + + .. qapi:command:: job-pause + :since: 3.0 + + Pause an active job. + + This command returns immediately after marking the active job for + pausing. Pausing an already paused job is an error. + + The job will pause as soon as possible, which means transitioning + into the PAUSED state if it was RUNNING, or into STANDBY if it was + READY. The corresponding JOB_STATUS_CHANGE event will be emitted. + + Cancelling a paused job automatically resumes it. + + :arg string id: The job identifier. + + +``:error:`` +----------- + +Document the error condition(s) of a QAPI command. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:error: Lorem ipsum dolor sit amet ...`` +:type: `sphinx.util.docfields.Field + `_ + +The format of the :errors: field list description is free-form rST. The +alternative spelling ":errors:" is also permitted, but strictly +analogous. + +Example:: + + .. qapi:command:: block-job-set-speed + :since: 1.1 + + Set maximum speed for a background block operation. + + This command can only be issued when there is an active block job. + + Throttling can be disabled by setting the speed to 0. + + :arg string device: The job identifier. This used to be a device + name (hence the name of the parameter), but since QEMU 2.7 it + can have other values. + :arg int speed: the maximum speed, in bytes per second, or 0 for + unlimited. Defaults to 0. + :error: + - If no background operation is active on this device, + DeviceNotActive + + +``:return:`` +------------- + +Document the return type(s) and value(s) of a QAPI command. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:return type: Lorem ipsum dolor sit amet ...`` +:type: `sphinx.util.docfields.GroupedField + `_ + + +Example:: + + .. qapi:command:: query-replay + :since: 5.2 + + Retrieve the record/replay information. It includes current + instruction count which may be used for ``replay-break`` and + ``replay-seek`` commands. + + :return ReplayInfo: record/replay information. + + .. qmp-example:: + + -> { "execute": "query-replay" } + <- { "return": { + "mode": "play", "filename": "log.rr", "icount": 220414 } + } + + +``:value:`` +----------- + +Document a possible value for a QAPI enum. + +:availability: This field list is only available in the body of the Enum + directive. +:syntax: ``:value name: Lorem ipsum, dolor sit amet ...`` +:type: `sphinx.util.docfields.GroupedField + `_ + +Example:: + + .. qapi:enum:: QapiErrorClass + :since: 1.2 + + QEMU error classes + + :value GenericError: this is used for errors that don't require a specific + error class. This should be the default case for most errors + :value CommandNotFound: the requested command has not been found + :value DeviceNotActive: a device has failed to be become active + :value DeviceNotFound: the requested device has not been found + :value KVMMissingCap: the requested operation can't be fulfilled because a + required KVM capability is missing + + +``:alt:`` +------------ + +Document a possible branch for a QAPI alternate. + +:availability: This field list is only available in the body of the + Alternate directive. +:syntax: ``:alt type name: Lorem ipsum, dolor sit amet ...`` +:type: `sphinx.util.docfields.TypedField + `_ + +As a limitation of Sphinx, we must document the "name" of the branch in +addition to the type, even though this information is not visible on the +wire in the QMP protocol format. This limitation *may* be lifted at a +future date. + +Example:: + + .. qapi:alternate:: StrOrNull + :since: 2.10 + + This is a string value or the explicit lack of a string (null + pointer in C). Intended for cases when 'optional absent' already + has a different meaning. + + :alt string s: the string value + :alt null n: no string value + + +``:memb:`` +---------- + +Document a member of an Event or Object. + +:availability: This field list is available in the body of Event or + Object directives. +:syntax: ``:memb type name: Lorem ipsum, dolor sit amet ...`` +:type: `sphinx.util.docfields.TypedField + `_ + +This is fundamentally the same as ``:arg:`` and ``:alt:``, but uses the +"Members" phrasing for Events and Objects (Structs and Unions). + +Example:: + + .. qapi:event:: JOB_STATUS_CHANGE + :since: 3.0 + + Emitted when a job transitions to a different status. + + :memb string id: The job identifier + :memb JobStatus status: The new job status + + +Arbitrary field lists +--------------------- + +Other field list names, while valid rST syntax, are prohibited inside of +QAPI directives to help prevent accidental misspellings of info field +list names. If you want to add a new arbitrary "non-value-added" field +list to QAPI documentation, you must add the field name to the allow +list in ``docs/conf.py`` + +For example:: + + qapi_allowed_fields = { + "see also", + } + +Will allow you to add arbitrary field lists in QAPI directives:: + + .. qapi:command:: x-fake-command + + :see also: Lorem ipsum, dolor sit amet ... + + +Cross-references +================ + +Cross-reference `roles +`_ +in the QAPI domain are modeled closely after the `Python +cross-referencing syntax +`_. + +QAPI definitions can be referenced using the standard `any +`_ +role cross-reference syntax, such as with ```query-blockstats```. In +the event that disambiguation is needed, cross-references can also be +written using a number of explicit cross-reference roles: + +* ``:qapi:mod:`block-core``` -- Reference a QAPI module. The link will + take you to the beginning of that section in the documentation. +* ``:qapi:cmd:`query-block``` -- Reference a QAPI command. +* ``:qapi:event:`JOB_STATUS_CHANGE``` -- Reference a QAPI event. +* ``:qapi:enum:`QapiErrorClass``` -- Reference a QAPI enum. +* ``:qapi:obj:`BlockdevOptionsVirtioBlkVhostVdpa`` -- Reference a QAPI + object (struct or union) +* ``:qapi:alt:`StrOrNull``` -- Reference a QAPI alternate. +* ``:qapi:type:`BlockDirtyInfo``` -- Reference *any* QAPI type; this + excludes modules, commands, and events. +* ``:qapi:any:`block-job-set-speed``` -- Reference absolutely any QAPI entity. + +Type arguments in info field lists are converted into references as if +you had used the ``:qapi:type:`` role. All of the special syntax below +applies to both info field lists and standalone explicit +cross-references. + + +Type decorations +---------------- + +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]` + +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?` + + +Namespaces +---------- + +Mimicking the `Python domain target specification syntax +`_, +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. + +* A module can be explicitly provided; + ``:qapi:type:`block-core.BitmapSyncMode``` will render to: + :qapi:type:`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` + + +Custom link text +---------------- + +The name of a cross-reference link can be explicitly overridden like +`most stock Sphinx references +`_ +using the ``custom text `` syntax. + +For example, ``:qapi:cmd:`Merge dirty bitmaps +``` will render as: :qapi:cmd:`Merge dirty +bitmaps ` + + +Directives +========== + +The QAPI domain adds a number of custom directives for documenting +various QAPI/QMP entities. The syntax is plain rST, and follows this +general format:: + + .. qapi:directive:: argument + :option: + :another-option: with an argument + + Content body, arbitrary rST is allowed here. + + +Sphinx standard options +----------------------- + +All QAPI directives inherit a number of `standard options +`_ +from Sphinx's ObjectDescription class. + +The dashed spellings of the below options were added in Sphinx 7.2, the +undashed spellings are currently retained as aliases, but will be +removed in a future version. + +* ``:no-index:`` and ``:noindex:`` -- Do not add this item into the + Index, and do not make it available for cross-referencing. +* ``no-index-entry:`` and ``:noindexentry:`` -- Do not add this item + into the Index, but allow it to be cross-referenced. +* ``no-contents-entry`` and ``:nocontentsentry:`` -- Exclude this item + from the Table of Contents. +* ``no-typesetting`` -- Create TOC, Index and cross-referencing + entities, but don't actually display the content. + + +QAPI standard options +--------------------- + +All QAPI directives -- *except* for module -- support these common options. + +* ``: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 + displayed in the signature bar. +* ``:ifcond: CONDITION`` -- Allows the documenting of conditional availability + information, which is displayed in an eyecatch just below the + signature bar. +* ``:deprecated:`` -- Adds an eyecatch just below the signature bar that + advertises that this definition is deprecated and should be avoided. +* ``:unstable:`` -- Adds an eyecatch just below the signature bar that + advertises that this definition is unstable and should not be used in + production code. + + +qapi:module +----------- + +The ``qapi:module`` directive marks the start of a QAPI module. It may have +a content body, but it can be omitted. All subsequent QAPI directives +are associated with the most recent module; this effects their "fully +qualified" name, but has no other effect. + +Example:: + + .. qapi:module:: block-core + + Welcome to the block-core module! + +Will be rendered as: + +.. qapi:module:: block-core + :noindex: + + Welcome to the block-core module! + + +qapi:command +------------ + +This directive documents a QMP command. It may use any of the standard +Sphinx or QAPI options, and the documentation body may contain +``:arg:``, ``:feat:``, ``:error:``, or ``:return:`` info field list +entries. + +Example:: + + .. qapi:command:: x-fake-command + :since: 42.0 + :unstable: + + This command is fake, so it can't hurt you! + + :arg int foo: Your favorite number. + :arg string? bar: Your favorite season. + :return [string]: A lovely computer-written poem for you. + + +Will be rendered as: + + .. qapi:command:: x-fake-command + :noindex: + :since: 42.0 + :unstable: + + This command is fake, so it can't hurt you! + + :arg int foo: Your favorite number. + :arg string? bar: Your favorite season. + :return [string]: A lovely computer-written poem for you. + + +qapi:event +---------- + +This directive documents a QMP event. It may use any of the standard +Sphinx or QAPI options, and the documentation body may contain +``:memb:`` or ``:feat:`` info field list entries. + +Example:: + + .. qapi:event:: COMPUTER_IS_RUINED + :since: 0.1 + :deprecated: + + This event is emitted when your computer is *extremely* ruined. + + :memb string reason: Diagnostics as to what caused your computer to + be ruined. + :feat sadness: When present, the diagnostic message will also + explain how sad the computer is as a result of your wrongdoings. + +Will be rendered as: + +.. qapi:event:: COMPUTER_IS_RUINED + :noindex: + :since: 0.1 + :deprecated: + + This event is emitted when your computer is *extremely* ruined. + + :memb string reason: Diagnostics as to what caused your computer to + be ruined. + :feat sadness: When present, the diagnostic message will also explain + how sad the computer is as a result of your wrongdoings. + + +qapi:enum +--------- + +This directive documents a QAPI enum. It may use any of the standard +Sphinx or QAPI options, and the documentation body may contain +``:value:`` or ``:feat:`` info field list entries. + +Example:: + + .. qapi:enum:: Mood + :ifcond: LIB_PERSONALITY + + This enum represents your virtual machine's current mood! + + :value Happy: Your VM is content and well-fed. + :value Hungry: Your VM needs food. + :value Melancholic: Your VM is experiencing existential angst. + :value Petulant: Your VM is throwing a temper tantrum. + +Will be rendered as: + +.. qapi:enum:: Mood + :noindex: + :ifcond: LIB_PERSONALITY + + This enum represents your virtual machine's current mood! + + :value Happy: Your VM is content and well-fed. + :value Hungry: Your VM needs food. + :value Melancholic: Your VM is experiencing existential angst. + :value Petulant: Your VM is throwing a temper tantrum. + + +qapi:object +----------- + +This directive documents a QAPI structure or union and represents a QMP +object. It may use any of the standard Sphinx or QAPI options, and the +documentation body may contain ``:memb:`` or ``:feat:`` info field list +entries. + +Example:: + + .. qapi:object:: BigBlobOfStuff + + This object has a bunch of disparate and unrelated things in it. + + :memb int Birthday: Your birthday, represented in seconds since the + UNIX epoch. + :memb [string] Fav-Foods: A list of your favorite foods. + :memb boolean? Bizarre-Docs: True if the documentation reference + should be strange. + +Will be rendered as: + +.. qapi:object:: BigBlobOfStuff + :noindex: + + This object has a bunch of disparate and unrelated things in it. + + :memb int Birthday: Your birthday, represented in seconds since the + UNIX epoch. + :memb [string] Fav-Foods: A list of your favorite foods. + :memb boolean? Bizarre-Docs: True if the documentation reference + should be strange. + + +qapi:alternate +-------------- + +This directive documents a QAPI alternate. It may use any of the +standard Sphinx or QAPI options, and the documentation body may contain +``:alt:`` or ``:feat:`` info field list entries. + +Example:: + + .. qapi:alternate:: ErrorCode + + This alternate represents an Error Code from the VM. + + :alt int ec: An error code, like the type you're used to. + :alt string em: An expletive-laced error message, if your + computer is feeling particularly cranky and tired of your + antics. + +Will be rendered as: + +.. qapi:alternate:: ErrorCode + :noindex: + + This alternate represents an Error Code from the VM. + + :alt int ec: An error code, like the type you're used to. + :alt string em: An expletive-laced error message, if your + computer is feeling particularly cranky and tired of your + antics. From e95ffabbde1e1ea76ffc48bd04a0138b12b08a6e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 10 Mar 2025 23:43:01 -0400 Subject: [PATCH 2540/2892] MAINTAINERS: Add jsnow as maintainer for Sphinx documentation Since I've just about rewritten the entirety of the QAPI documentation system, it's probably fair that I be the contact point for if it goes awry. Signed-off-by: John Snow Message-ID: <20250311034303.75779-64-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0e5db7a574..55af2f8d17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4325,6 +4325,7 @@ S: Orphan F: po/*.po Sphinx documentation configuration and build machinery +M: John Snow M: Peter Maydell S: Maintained F: docs/conf.py From 93db9c84fc40b82d6bc3a944cb8eb8443980824c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 11 Mar 2025 07:53:52 +0100 Subject: [PATCH 2541/2892] scripts/qapi/backend: Clean up create_backend()'s failure mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit create_backend()'s caller catches QAPIError, and returns non-zero exit code on catch. The caller's caller passes the exit code to sys.exit(). create_backend() doesn't care: it reports errors to stderr and sys.exit()s. Change it to raise QAPIError instead. Signed-off-by: Markus Armbruster Message-ID: <20250311065352.992307-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- scripts/qapi/main.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index 5b4679abcf..0e2a6ae3f0 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -31,34 +31,28 @@ def create_backend(path: str) -> QAPIBackend: module_path, dot, class_name = path.rpartition('.') if not dot: - print("argument of -B must be of the form MODULE.CLASS", - file=sys.stderr) - sys.exit(1) + raise QAPIError("argument of -B must be of the form MODULE.CLASS") try: mod = import_module(module_path) except Exception as ex: - print(f"unable to import '{module_path}': {ex}", file=sys.stderr) - sys.exit(1) + raise QAPIError(f"unable to import '{module_path}': {ex}") from ex try: klass = getattr(mod, class_name) - except AttributeError: - print(f"module '{module_path}' has no class '{class_name}'", - file=sys.stderr) - sys.exit(1) + except AttributeError as ex: + raise QAPIError( + f"module '{module_path}' has no class '{class_name}'") from ex try: backend = klass() except Exception as ex: - print(f"backend '{path}' cannot be instantiated: {ex}", - file=sys.stderr) - sys.exit(1) + raise QAPIError( + f"backend '{path}' cannot be instantiated: {ex}") from ex if not isinstance(backend, QAPIBackend): - print(f"backend '{path}' must be an instance of QAPIBackend", - file=sys.stderr) - sys.exit(1) + raise QAPIError( + f"backend '{path}' must be an instance of QAPIBackend") return backend From e5029e28f0f5ef7e7d5b927aa61a32232be99438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 4 Feb 2025 09:06:47 +0100 Subject: [PATCH 2542/2892] ppc/ppc405: Remove tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we are about to remove all support for PPC 405, start by removing the tests referring to the ref405ep machine. Link: https://lore.kernel.org/qemu-devel/20250110141800.1587589-2-clg@redhat.com Signed-off-by: Cédric Le Goater Reviewed-by: Nicholas Piggin Message-ID: <20250204080649.836155-2-clg@redhat.com> Signed-off-by: Nicholas Piggin --- tests/functional/meson.build | 1 - tests/functional/test_ppc_405.py | 37 -------------------------------- tests/qtest/m48t59-test.c | 5 ----- tests/qtest/meson.build | 1 - 4 files changed, 44 deletions(-) delete mode 100755 tests/functional/test_ppc_405.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e78560a901..74f8414a0c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -207,7 +207,6 @@ tests_ppc_system_quick = [ ] tests_ppc_system_thorough = [ - 'ppc_405', 'ppc_40p', 'ppc_amiga', 'ppc_bamboo', diff --git a/tests/functional/test_ppc_405.py b/tests/functional/test_ppc_405.py deleted file mode 100755 index 9851c03ee9..0000000000 --- a/tests/functional/test_ppc_405.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# -# Test that the U-Boot firmware boots on ppc 405 machines and check the console -# -# Copyright (c) 2021 Red Hat, Inc. -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern -from qemu_test import exec_command_and_wait_for_pattern - -class Ppc405Machine(QemuSystemTest): - - timeout = 90 - - ASSET_UBOOT = Asset( - ('https://gitlab.com/huth/u-boot/-/raw/taihu-2021-10-09/' - 'u-boot-taihu.bin'), - 'a076bb6cdeaafa406330e51e074b66d8878d9036d67d4caa0137be03ee4c112c') - - def do_test_ppc405(self): - file_path = self.ASSET_UBOOT.fetch() - self.vm.set_console(console_index=1) - self.vm.add_args('-bios', file_path) - self.vm.launch() - wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') - exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') - - def test_ppc_ref405ep(self): - self.require_accelerator("tcg") - self.set_machine('ref405ep') - self.do_test_ppc405() - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index 605797ab78..1e39a0e8f0 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -247,11 +247,6 @@ static void base_setup(void) base_year = 1968; base_machine = "SS-5"; use_mmio = true; - } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { - base = 0xF0000000; - base_year = 1968; - base_machine = "ref405ep"; - use_mmio = true; } else { g_assert_not_reached(); } diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 8a6243382a..b23fe67db7 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -171,7 +171,6 @@ qtests_mips64el = qtests_mips qtests_ppc = \ qtests_filter + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ - (config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \ (config_all_accel.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \ (config_all_accel.has_key('CONFIG_TCG') ? ['boot-serial-test'] : []) + \ ['boot-order-test'] From e7dba30e827d0ab59b23de444a4d7f7412430223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 4 Feb 2025 09:06:48 +0100 Subject: [PATCH 2543/2892] ppc/ppc405: Remove boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ref405ep machine is the only PPC 405 machine. Drop all support by removing the SoC and associated devices as-well as the machine. Link: https://lore.kernel.org/qemu-devel/20250110141800.1587589-3-clg@redhat.com Signed-off-by: Cédric Le Goater Reviewed-by: Nicholas Piggin Message-ID: <20250204080649.836155-3-clg@redhat.com> Signed-off-by: Nicholas Piggin --- MAINTAINERS | 6 - docs/about/deprecated.rst | 8 - docs/about/removed-features.rst | 7 + docs/system/ppc/embedded.rst | 1 - hw/ppc/Kconfig | 9 - hw/ppc/meson.build | 3 - hw/ppc/ppc405.h | 186 ----- hw/ppc/ppc405_boards.c | 520 ------------- hw/ppc/ppc405_uc.c | 1216 ------------------------------- 9 files changed, 7 insertions(+), 1949 deletions(-) delete mode 100644 hw/ppc/ppc405.h delete mode 100644 hw/ppc/ppc405_boards.c delete mode 100644 hw/ppc/ppc405_uc.c diff --git a/MAINTAINERS b/MAINTAINERS index 0e5db7a574..e2f538fc16 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1407,12 +1407,6 @@ F: hw/openrisc/openrisc_sim.c PowerPC Machines ---------------- -405 (ref405ep) -L: qemu-ppc@nongnu.org -S: Orphan -F: hw/ppc/ppc405* -F: tests/functional/test_ppc_405.py - Bamboo L: qemu-ppc@nongnu.org S: Orphan diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 589951b136..3d39d2a9da 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -277,14 +277,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -PPC 405 ``ref405ep`` machine (since 9.1) -'''''''''''''''''''''''''''''''''''''''' - -The ``ref405ep`` machine and PPC 405 CPU have no known users, firmware -images are not available, OpenWRT dropped support in 2019, U-Boot in -2017, Linux also is dropping support in 2024. It is time to let go of -this ancient hardware and focus on newer CPUs and platforms. - Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (since 9.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 156c0c253c..2527a91795 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1064,6 +1064,13 @@ for all machine types using the PXA2xx and OMAP2 SoCs. We are also dropping the ``cheetah`` OMAP1 board, because we don't have any test images for it and don't know of anybody who does. +ppc ``ref405ep`` machine (removed in 10.0) +'''''''''''''''''''''''''''''''''''''''''' + +This machine was removed because PPC 405 CPU have no known users, +firmware images are not available, OpenWRT dropped support in 2019, +U-Boot in 2017, and Linux in 2024. + linux-user mode CPUs -------------------- diff --git a/docs/system/ppc/embedded.rst b/docs/system/ppc/embedded.rst index af3b3d9fa4..5cb7d98b45 100644 --- a/docs/system/ppc/embedded.rst +++ b/docs/system/ppc/embedded.rst @@ -4,6 +4,5 @@ Embedded family boards - ``bamboo`` bamboo - ``mpc8544ds`` mpc8544ds - ``ppce500`` generic paravirt e500 platform -- ``ref405ep`` ref405ep - ``sam460ex`` aCube Sam460ex - ``virtex-ml507`` Xilinx Virtex ML507 reference design diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index b44d91bebb..ced6bbc740 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -44,15 +44,6 @@ config POWERNV select SSI_M25P80 select PNV_SPI -config PPC405 - bool - default y - depends on PPC - select M48T59 - select PFLASH_CFI02 - select PPC4XX - select SERIAL_MM - config PPC440 bool default y diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 7cd9189869..9893f8adeb 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -57,9 +57,6 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv_n1_chiplet.c', )) # PowerPC 4xx boards -ppc_ss.add(when: 'CONFIG_PPC405', if_true: files( - 'ppc405_boards.c', - 'ppc405_uc.c')) ppc_ss.add(when: 'CONFIG_PPC440', if_true: files( 'ppc440_bamboo.c', 'ppc440_uc.c')) diff --git a/hw/ppc/ppc405.h b/hw/ppc/ppc405.h deleted file mode 100644 index 9a4312691e..0000000000 --- a/hw/ppc/ppc405.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * QEMU PowerPC 405 shared definitions - * - * Copyright (c) 2007 Jocelyn Mayer - * - * 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 PPC405_H -#define PPC405_H - -#include "qom/object.h" -#include "hw/ppc/ppc4xx.h" -#include "hw/intc/ppc-uic.h" -#include "hw/i2c/ppc4xx_i2c.h" - -/* PLB to OPB bridge */ -#define TYPE_PPC405_POB "ppc405-pob" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405PobState, PPC405_POB); -struct Ppc405PobState { - Ppc4xxDcrDeviceState parent_obj; - - uint32_t bear; - uint32_t besr0; - uint32_t besr1; -}; - -/* OPB arbitrer */ -#define TYPE_PPC405_OPBA "ppc405-opba" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OpbaState, PPC405_OPBA); -struct Ppc405OpbaState { - SysBusDevice parent_obj; - - MemoryRegion io; - uint8_t cr; - uint8_t pr; -}; - -/* DMA controller */ -#define TYPE_PPC405_DMA "ppc405-dma" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405DmaState, PPC405_DMA); -struct Ppc405DmaState { - Ppc4xxDcrDeviceState parent_obj; - - qemu_irq irqs[4]; - uint32_t cr[4]; - uint32_t ct[4]; - uint32_t da[4]; - uint32_t sa[4]; - uint32_t sg[4]; - uint32_t sr; - uint32_t sgc; - uint32_t slp; - uint32_t pol; -}; - -/* GPIO */ -#define TYPE_PPC405_GPIO "ppc405-gpio" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GpioState, PPC405_GPIO); -struct Ppc405GpioState { - SysBusDevice parent_obj; - - MemoryRegion io; - uint32_t or; - uint32_t tcr; - uint32_t osrh; - uint32_t osrl; - uint32_t tsrh; - uint32_t tsrl; - uint32_t odr; - uint32_t ir; - uint32_t rr1; - uint32_t isr1h; - uint32_t isr1l; -}; - -/* On Chip Memory */ -#define TYPE_PPC405_OCM "ppc405-ocm" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OcmState, PPC405_OCM); -struct Ppc405OcmState { - Ppc4xxDcrDeviceState parent_obj; - - MemoryRegion ram; - MemoryRegion isarc_ram; - MemoryRegion dsarc_ram; - uint32_t isarc; - uint32_t isacntl; - uint32_t dsarc; - uint32_t dsacntl; -}; - -/* General purpose timers */ -#define TYPE_PPC405_GPT "ppc405-gpt" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GptState, PPC405_GPT); -struct Ppc405GptState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - int64_t tb_offset; - uint32_t tb_freq; - QEMUTimer *timer; - qemu_irq irqs[5]; - uint32_t oe; - uint32_t ol; - uint32_t im; - uint32_t is; - uint32_t ie; - uint32_t comp[5]; - uint32_t mask[5]; -}; - -#define TYPE_PPC405_CPC "ppc405-cpc" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405CpcState, PPC405_CPC); - -enum { - PPC405EP_CPU_CLK = 0, - PPC405EP_PLB_CLK = 1, - PPC405EP_OPB_CLK = 2, - PPC405EP_EBC_CLK = 3, - PPC405EP_MAL_CLK = 4, - PPC405EP_PCI_CLK = 5, - PPC405EP_UART0_CLK = 6, - PPC405EP_UART1_CLK = 7, - PPC405EP_CLK_NB = 8, -}; - -struct Ppc405CpcState { - Ppc4xxDcrDeviceState parent_obj; - - uint32_t sysclk; - clk_setup_t clk_setup[PPC405EP_CLK_NB]; - uint32_t boot; - uint32_t epctl; - uint32_t pllmr[2]; - uint32_t ucr; - uint32_t srr; - uint32_t jtagid; - uint32_t pci; - /* Clock and power management */ - uint32_t er; - uint32_t fr; - uint32_t sr; -}; - -#define TYPE_PPC405_SOC "ppc405-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405SoCState, PPC405_SOC); - -struct Ppc405SoCState { - /* Private */ - DeviceState parent_obj; - - /* Public */ - PowerPCCPU cpu; - PPCUIC uic; - Ppc405CpcState cpc; - Ppc405GptState gpt; - Ppc405OcmState ocm; - Ppc405GpioState gpio; - Ppc405DmaState dma; - PPC4xxI2CState i2c; - Ppc4xxEbcState ebc; - Ppc405OpbaState opba; - Ppc405PobState pob; - Ppc4xxPlbState plb; - Ppc4xxMalState mal; - Ppc4xxSdramDdrState sdram; -}; - -#endif /* PPC405_H */ diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c deleted file mode 100644 index 969cac345a..0000000000 --- a/hw/ppc/ppc405_boards.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * QEMU PowerPC 405 evaluation boards emulation - * - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qapi/error.h" -#include "qemu/datadir.h" -#include "cpu.h" -#include "hw/ppc/ppc.h" -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" -#include "ppc405.h" -#include "hw/rtc/m48t59.h" -#include "hw/block/flash.h" -#include "system/qtest.h" -#include "system/reset.h" -#include "system/block-backend.h" -#include "hw/boards.h" -#include "qemu/error-report.h" -#include "hw/loader.h" -#include "qemu/cutils.h" -#include "elf.h" - -#define BIOS_FILENAME "ppc405_rom.bin" -#define BIOS_SIZE (2 * MiB) - -#define KERNEL_LOAD_ADDR 0x01000000 -#define INITRD_LOAD_ADDR 0x01800000 - -#define PPC405EP_SDRAM_BASE 0x00000000 -#define PPC405EP_SRAM_BASE 0xFFF00000 -#define PPC405EP_SRAM_SIZE (512 * KiB) - -#define USE_FLASH_BIOS - -#define TYPE_PPC405_MACHINE MACHINE_TYPE_NAME("ppc405") -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405MachineState, PPC405_MACHINE); - -struct Ppc405MachineState { - /* Private */ - MachineState parent_obj; - /* Public */ - - Ppc405SoCState soc; -}; - -/* CPU reset handler when booting directly from a loaded kernel */ -static struct boot_info { - uint32_t entry; - uint32_t bdloc; - uint32_t initrd_base; - uint32_t initrd_size; - uint32_t cmdline_base; - uint32_t cmdline_size; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - struct boot_info *bi = env->load_info; - - cpu_reset(CPU(cpu)); - - /* stack: top of sram */ - env->gpr[1] = PPC405EP_SRAM_BASE + PPC405EP_SRAM_SIZE - 8; - - /* Tune our boot state */ - env->gpr[3] = bi->bdloc; - env->gpr[4] = bi->initrd_base; - env->gpr[5] = bi->initrd_base + bi->initrd_size; - env->gpr[6] = bi->cmdline_base; - env->gpr[7] = bi->cmdline_size; - - env->nip = bi->entry; -} - -/* Bootinfo as set-up by u-boot */ -typedef struct { - uint32_t bi_memstart; - uint32_t bi_memsize; - uint32_t bi_flashstart; - uint32_t bi_flashsize; - uint32_t bi_flashoffset; /* 0x10 */ - uint32_t bi_sramstart; - uint32_t bi_sramsize; - uint32_t bi_bootflags; - uint32_t bi_ipaddr; /* 0x20 */ - uint8_t bi_enetaddr[6]; - uint16_t bi_ethspeed; - uint32_t bi_intfreq; - uint32_t bi_busfreq; /* 0x30 */ - uint32_t bi_baudrate; - uint8_t bi_s_version[4]; - uint8_t bi_r_version[32]; - uint32_t bi_procfreq; - uint32_t bi_plb_busfreq; - uint32_t bi_pci_busfreq; - uint8_t bi_pci_enetaddr[6]; - uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */ - uint32_t bi_opbfreq; - uint32_t bi_iic_fast[2]; -} ppc4xx_bd_info_t; - -static void ppc405_set_default_bootinfo(ppc4xx_bd_info_t *bd, - ram_addr_t ram_size) -{ - memset(bd, 0, sizeof(*bd)); - - bd->bi_memstart = PPC405EP_SDRAM_BASE; - bd->bi_memsize = ram_size; - bd->bi_sramstart = PPC405EP_SRAM_BASE; - bd->bi_sramsize = PPC405EP_SRAM_SIZE; - bd->bi_bootflags = 0; - bd->bi_intfreq = 133333333; - bd->bi_busfreq = 33333333; - bd->bi_baudrate = 115200; - bd->bi_s_version[0] = 'Q'; - bd->bi_s_version[1] = 'M'; - bd->bi_s_version[2] = 'U'; - bd->bi_s_version[3] = '\0'; - bd->bi_r_version[0] = 'Q'; - bd->bi_r_version[1] = 'E'; - bd->bi_r_version[2] = 'M'; - bd->bi_r_version[3] = 'U'; - bd->bi_r_version[4] = '\0'; - bd->bi_procfreq = 133333333; - bd->bi_plb_busfreq = 33333333; - bd->bi_pci_busfreq = 33333333; - bd->bi_opbfreq = 33333333; -} - -static ram_addr_t __ppc405_set_bootinfo(CPUPPCState *env, ppc4xx_bd_info_t *bd) -{ - CPUState *cs = env_cpu(env); - ram_addr_t bdloc; - int i, n; - - /* We put the bd structure at the top of memory */ - if (bd->bi_memsize >= 0x01000000UL) { - bdloc = 0x01000000UL - sizeof(ppc4xx_bd_info_t); - } else { - bdloc = bd->bi_memsize - sizeof(ppc4xx_bd_info_t); - } - stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart); - stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize); - stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart); - stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize); - stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset); - stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart); - stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize); - stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags); - stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]); - } - stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed); - stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq); - stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq); - stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate); - for (i = 0; i < 4; i++) { - stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]); - } - for (i = 0; i < 32; i++) { - stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]); - } - stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_procfreq); - stl_be_phys(cs->as, bdloc + 0x60, bd->bi_plb_busfreq); - stl_be_phys(cs->as, bdloc + 0x64, bd->bi_pci_busfreq); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x68 + i, bd->bi_pci_enetaddr[i]); - } - n = 0x70; /* includes 2 bytes hole */ - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]); - } - stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq); - n += 4; - for (i = 0; i < 2; i++) { - stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]); - n += 4; - } - - return bdloc; -} - -static ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size) -{ - ppc4xx_bd_info_t bd; - - memset(&bd, 0, sizeof(bd)); - - ppc405_set_default_bootinfo(&bd, ram_size); - - return __ppc405_set_bootinfo(env, &bd); -} - -static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - hwaddr boot_entry; - hwaddr kernel_base; - int kernel_size; - hwaddr initrd_base; - int initrd_size; - ram_addr_t bdloc; - int len; - - bdloc = ppc405_set_bootinfo(env, machine->ram_size); - boot_info.bdloc = bdloc; - - kernel_size = load_elf(machine->kernel_filename, NULL, NULL, NULL, - &boot_entry, &kernel_base, NULL, NULL, - ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); - if (kernel_size < 0) { - error_report("Could not load kernel '%s' : %s", - machine->kernel_filename, load_elf_strerror(kernel_size)); - exit(1); - } - boot_info.entry = boot_entry; - - /* load initrd */ - if (machine->initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } - - boot_info.initrd_base = initrd_base; - boot_info.initrd_size = initrd_size; - } - - if (machine->kernel_cmdline) { - len = strlen(machine->kernel_cmdline); - bdloc -= ((len + 255) & ~255); - cpu_physical_memory_write(bdloc, machine->kernel_cmdline, len + 1); - boot_info.cmdline_base = bdloc; - boot_info.cmdline_size = bdloc + len; - } - - /* Install our custom reset handler to start from Linux */ - qemu_register_reset(main_cpu_reset, cpu); - env->load_info = &boot_info; -} - -static void ppc405_init(MachineState *machine) -{ - Ppc405MachineState *ppc405 = PPC405_MACHINE(machine); - const char *kernel_filename = machine->kernel_filename; - MemoryRegion *sysmem = get_system_memory(); - - object_initialize_child(OBJECT(machine), "soc", &ppc405->soc, - TYPE_PPC405_SOC); - object_property_set_link(OBJECT(&ppc405->soc), "dram", - OBJECT(machine->ram), &error_abort); - object_property_set_uint(OBJECT(&ppc405->soc), "sys-clk", 33333333, - &error_abort); - qdev_realize(DEVICE(&ppc405->soc), NULL, &error_fatal); - - /* allocate and load BIOS */ - if (machine->firmware) { - MemoryRegion *bios = g_new(MemoryRegion, 1); - g_autofree char *filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware); - long bios_size; - - memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE, - &error_fatal); - - if (!filename) { - error_report("Could not find firmware '%s'", machine->firmware); - exit(1); - } - - bios_size = load_image_size(filename, - memory_region_get_ram_ptr(bios), - BIOS_SIZE); - if (bios_size < 0) { - error_report("Could not load PowerPC BIOS '%s'", machine->firmware); - exit(1); - } - - bios_size = (bios_size + 0xfff) & ~0xfff; - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); - } - - /* Load kernel and initrd using U-Boot images */ - if (kernel_filename && machine->firmware) { - target_ulong kernel_base, initrd_base; - long kernel_size, initrd_size; - - kernel_base = KERNEL_LOAD_ADDR; - kernel_size = load_image_targphys(kernel_filename, kernel_base, - machine->ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - - /* load initrd */ - if (machine->initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(machine->initrd_filename, - initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } - } - - /* Load ELF kernel and rootfs.cpio */ - } else if (kernel_filename && !machine->firmware) { - ppc4xx_sdram_ddr_enable(&ppc405->soc.sdram); - boot_from_kernel(machine, &ppc405->soc.cpu); - } -} - -static void ppc405_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "PPC405 generic machine"; - mc->init = ppc405_init; - mc->default_ram_size = 128 * MiB; - mc->default_ram_id = "ppc405.ram"; - mc->deprecation_reason = "machine is old and unmaintained"; -} - -static const TypeInfo ppc405_machine_type = { - .name = TYPE_PPC405_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(Ppc405MachineState), - .class_init = ppc405_machine_class_init, - .abstract = true, -}; - -/*****************************************************************************/ -/* PPC405EP reference board (IBM) */ -/* - * Standalone board with: - * - PowerPC 405EP CPU - * - SDRAM (0x00000000) - * - Flash (0xFFF80000) - * - SRAM (0xFFF00000) - * - NVRAM (0xF0000000) - * - FPGA (0xF0300000) - */ - -#define PPC405EP_NVRAM_BASE 0xF0000000 -#define PPC405EP_FPGA_BASE 0xF0300000 -#define PPC405EP_FLASH_BASE 0xFFF80000 - -#define TYPE_REF405EP_FPGA "ref405ep-fpga" -OBJECT_DECLARE_SIMPLE_TYPE(Ref405epFpgaState, REF405EP_FPGA); -struct Ref405epFpgaState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint8_t reg0; - uint8_t reg1; -}; - -static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size) -{ - Ref405epFpgaState *fpga = opaque; - uint32_t ret; - - switch (addr) { - case 0x0: - ret = fpga->reg0; - break; - case 0x1: - ret = fpga->reg1; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - Ref405epFpgaState *fpga = opaque; - - switch (addr) { - case 0x0: - /* Read only */ - break; - case 0x1: - fpga->reg1 = value; - break; - default: - break; - } -} - -static const MemoryRegionOps ref405ep_fpga_ops = { - .read = ref405ep_fpga_readb, - .write = ref405ep_fpga_writeb, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static void ref405ep_fpga_reset(DeviceState *dev) -{ - Ref405epFpgaState *fpga = REF405EP_FPGA(dev); - - fpga->reg0 = 0x00; - fpga->reg1 = 0x0F; -} - -static void ref405ep_fpga_realize(DeviceState *dev, Error **errp) -{ - Ref405epFpgaState *s = REF405EP_FPGA(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &ref405ep_fpga_ops, s, - "fpga", 0x00000100); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); -} - -static void ref405ep_fpga_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ref405ep_fpga_realize; - device_class_set_legacy_reset(dc, ref405ep_fpga_reset); - /* Reason: only works as part of a ppc405 board */ - dc->user_creatable = false; -} - -static const TypeInfo ref405ep_fpga_type = { - .name = TYPE_REF405EP_FPGA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ref405epFpgaState), - .class_init = ref405ep_fpga_class_init, -}; - -static void ref405ep_init(MachineState *machine) -{ - DeviceState *dev; - SysBusDevice *s; - MemoryRegion *sram = g_new(MemoryRegion, 1); - - ppc405_init(machine); - - /* allocate SRAM */ - memory_region_init_ram(sram, NULL, "ref405ep.sram", PPC405EP_SRAM_SIZE, - &error_fatal); - memory_region_add_subregion(get_system_memory(), PPC405EP_SRAM_BASE, sram); - - /* Register FPGA */ - dev = qdev_new(TYPE_REF405EP_FPGA); - object_property_add_child(OBJECT(machine), "fpga", OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, PPC405EP_FPGA_BASE); - - /* Register NVRAM */ - dev = qdev_new("sysbus-m48t08"); - qdev_prop_set_int32(dev, "base-year", 1968); - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE); -} - -static void ref405ep_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "ref405ep"; - mc->init = ref405ep_init; -} - -static const TypeInfo ref405ep_type = { - .name = MACHINE_TYPE_NAME("ref405ep"), - .parent = TYPE_PPC405_MACHINE, - .class_init = ref405ep_class_init, -}; - -static void ppc405_machine_init(void) -{ - type_register_static(&ppc405_machine_type); - type_register_static(&ref405ep_type); - type_register_static(&ref405ep_fpga_type); -} - -type_init(ppc405_machine_init) diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c deleted file mode 100644 index 8250824a1a..0000000000 --- a/hw/ppc/ppc405_uc.c +++ /dev/null @@ -1,1216 +0,0 @@ -/* - * QEMU PowerPC 405 embedded processors emulation - * - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "cpu.h" -#include "hw/ppc/ppc.h" -#include "hw/i2c/ppc4xx_i2c.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "ppc405.h" -#include "hw/char/serial-mm.h" -#include "qemu/timer.h" -#include "system/reset.h" -#include "system/system.h" -#include "exec/address-spaces.h" -#include "hw/intc/ppc-uic.h" -#include "trace.h" - -/*****************************************************************************/ -/* Shared peripherals */ - -/*****************************************************************************/ -/* PLB to OPB bridge */ -enum { - POB0_BESR0 = 0x0A0, - POB0_BESR1 = 0x0A2, - POB0_BEAR = 0x0A4, -}; - -static uint32_t dcr_read_pob(void *opaque, int dcrn) -{ - Ppc405PobState *pob = opaque; - uint32_t ret; - - switch (dcrn) { - case POB0_BEAR: - ret = pob->bear; - break; - case POB0_BESR0: - ret = pob->besr0; - break; - case POB0_BESR1: - ret = pob->besr1; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_pob(void *opaque, int dcrn, uint32_t val) -{ - Ppc405PobState *pob = opaque; - - switch (dcrn) { - case POB0_BEAR: - /* Read only */ - break; - case POB0_BESR0: - /* Write-clear */ - pob->besr0 &= ~val; - break; - case POB0_BESR1: - /* Write-clear */ - pob->besr1 &= ~val; - break; - } -} - -static void ppc405_pob_reset(DeviceState *dev) -{ - Ppc405PobState *pob = PPC405_POB(dev); - - /* No error */ - pob->bear = 0x00000000; - pob->besr0 = 0x0000000; - pob->besr1 = 0x0000000; -} - -static void ppc405_pob_realize(DeviceState *dev, Error **errp) -{ - Ppc405PobState *pob = PPC405_POB(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - - ppc4xx_dcr_register(dcr, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob); - ppc4xx_dcr_register(dcr, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob); - ppc4xx_dcr_register(dcr, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob); -} - -static void ppc405_pob_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_pob_realize; - device_class_set_legacy_reset(dc, ppc405_pob_reset); - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* OPB arbitrer */ -static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size) -{ - Ppc405OpbaState *opba = opaque; - uint32_t ret; - - switch (addr) { - case 0x00: - ret = opba->cr; - break; - case 0x01: - ret = opba->pr; - break; - default: - ret = 0x00; - break; - } - - trace_opba_readb(addr, ret); - return ret; -} - -static void opba_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - Ppc405OpbaState *opba = opaque; - - trace_opba_writeb(addr, value); - - switch (addr) { - case 0x00: - opba->cr = value & 0xF8; - break; - case 0x01: - opba->pr = value & 0xFF; - break; - default: - break; - } -} -static const MemoryRegionOps opba_ops = { - .read = opba_readb, - .write = opba_writeb, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static void ppc405_opba_reset(DeviceState *dev) -{ - Ppc405OpbaState *opba = PPC405_OPBA(dev); - - opba->cr = 0x00; /* No dynamic priorities - park disabled */ - opba->pr = 0x11; -} - -static void ppc405_opba_realize(DeviceState *dev, Error **errp) -{ - Ppc405OpbaState *s = PPC405_OPBA(dev); - - memory_region_init_io(&s->io, OBJECT(s), &opba_ops, s, "opba", 2); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io); -} - -static void ppc405_opba_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_opba_realize; - device_class_set_legacy_reset(dc, ppc405_opba_reset); - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* Code decompression controller */ -/* XXX: TODO */ - -/*****************************************************************************/ -/* DMA controller */ -enum { - DMA0_CR0 = 0x100, - DMA0_CT0 = 0x101, - DMA0_DA0 = 0x102, - DMA0_SA0 = 0x103, - DMA0_SG0 = 0x104, - DMA0_CR1 = 0x108, - DMA0_CT1 = 0x109, - DMA0_DA1 = 0x10A, - DMA0_SA1 = 0x10B, - DMA0_SG1 = 0x10C, - DMA0_CR2 = 0x110, - DMA0_CT2 = 0x111, - DMA0_DA2 = 0x112, - DMA0_SA2 = 0x113, - DMA0_SG2 = 0x114, - DMA0_CR3 = 0x118, - DMA0_CT3 = 0x119, - DMA0_DA3 = 0x11A, - DMA0_SA3 = 0x11B, - DMA0_SG3 = 0x11C, - DMA0_SR = 0x120, - DMA0_SGC = 0x123, - DMA0_SLP = 0x125, - DMA0_POL = 0x126, -}; - -static uint32_t dcr_read_dma(void *opaque, int dcrn) -{ - return 0; -} - -static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) -{ -} - -static void ppc405_dma_reset(DeviceState *dev) -{ - Ppc405DmaState *dma = PPC405_DMA(dev); - int i; - - for (i = 0; i < 4; i++) { - dma->cr[i] = 0x00000000; - dma->ct[i] = 0x00000000; - dma->da[i] = 0x00000000; - dma->sa[i] = 0x00000000; - dma->sg[i] = 0x00000000; - } - dma->sr = 0x00000000; - dma->sgc = 0x00000000; - dma->slp = 0x7C000000; - dma->pol = 0x00000000; -} - -static void ppc405_dma_realize(DeviceState *dev, Error **errp) -{ - Ppc405DmaState *dma = PPC405_DMA(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(dma->irqs); i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dma), &dma->irqs[i]); - } - - ppc4xx_dcr_register(dcr, DMA0_CR0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CR1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CR2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CR3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SR, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SGC, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SLP, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_POL, dma, &dcr_read_dma, &dcr_write_dma); -} - -static void ppc405_dma_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_dma_realize; - device_class_set_legacy_reset(dc, ppc405_dma_reset); - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* GPIO */ -static uint64_t ppc405_gpio_read(void *opaque, hwaddr addr, unsigned size) -{ - trace_ppc405_gpio_read(addr, size); - return 0; -} - -static void ppc405_gpio_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - trace_ppc405_gpio_write(addr, size, value); -} - -static const MemoryRegionOps ppc405_gpio_ops = { - .read = ppc405_gpio_read, - .write = ppc405_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc405_gpio_realize(DeviceState *dev, Error **errp) -{ - Ppc405GpioState *s = PPC405_GPIO(dev); - - memory_region_init_io(&s->io, OBJECT(s), &ppc405_gpio_ops, s, "gpio", - 0x38); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io); -} - -static void ppc405_gpio_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_gpio_realize; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* On Chip Memory */ -enum { - OCM0_ISARC = 0x018, - OCM0_ISACNTL = 0x019, - OCM0_DSARC = 0x01A, - OCM0_DSACNTL = 0x01B, -}; - -static void ocm_update_mappings(Ppc405OcmState *ocm, - uint32_t isarc, uint32_t isacntl, - uint32_t dsarc, uint32_t dsacntl) -{ - trace_ocm_update_mappings(isarc, isacntl, dsarc, dsacntl, ocm->isarc, - ocm->isacntl, ocm->dsarc, ocm->dsacntl); - - if (ocm->isarc != isarc || - (ocm->isacntl & 0x80000000) != (isacntl & 0x80000000)) { - if (ocm->isacntl & 0x80000000) { - /* Unmap previously assigned memory region */ - trace_ocm_unmap("ISA", ocm->isarc); - memory_region_del_subregion(get_system_memory(), &ocm->isarc_ram); - } - if (isacntl & 0x80000000) { - /* Map new instruction memory region */ - trace_ocm_map("ISA", isarc); - memory_region_add_subregion(get_system_memory(), isarc, - &ocm->isarc_ram); - } - } - if (ocm->dsarc != dsarc || - (ocm->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) { - if (ocm->dsacntl & 0x80000000) { - /* Beware not to unmap the region we just mapped */ - if (!(isacntl & 0x80000000) || ocm->dsarc != isarc) { - /* Unmap previously assigned memory region */ - trace_ocm_unmap("DSA", ocm->dsarc); - memory_region_del_subregion(get_system_memory(), - &ocm->dsarc_ram); - } - } - if (dsacntl & 0x80000000) { - /* Beware not to remap the region we just mapped */ - if (!(isacntl & 0x80000000) || dsarc != isarc) { - /* Map new data memory region */ - trace_ocm_map("DSA", dsarc); - memory_region_add_subregion(get_system_memory(), dsarc, - &ocm->dsarc_ram); - } - } - } -} - -static uint32_t dcr_read_ocm(void *opaque, int dcrn) -{ - Ppc405OcmState *ocm = opaque; - uint32_t ret; - - switch (dcrn) { - case OCM0_ISARC: - ret = ocm->isarc; - break; - case OCM0_ISACNTL: - ret = ocm->isacntl; - break; - case OCM0_DSARC: - ret = ocm->dsarc; - break; - case OCM0_DSACNTL: - ret = ocm->dsacntl; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_ocm(void *opaque, int dcrn, uint32_t val) -{ - Ppc405OcmState *ocm = opaque; - uint32_t isarc, dsarc, isacntl, dsacntl; - - isarc = ocm->isarc; - dsarc = ocm->dsarc; - isacntl = ocm->isacntl; - dsacntl = ocm->dsacntl; - switch (dcrn) { - case OCM0_ISARC: - isarc = val & 0xFC000000; - break; - case OCM0_ISACNTL: - isacntl = val & 0xC0000000; - break; - case OCM0_DSARC: - isarc = val & 0xFC000000; - break; - case OCM0_DSACNTL: - isacntl = val & 0xC0000000; - break; - } - ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl); - ocm->isarc = isarc; - ocm->dsarc = dsarc; - ocm->isacntl = isacntl; - ocm->dsacntl = dsacntl; -} - -static void ppc405_ocm_reset(DeviceState *dev) -{ - Ppc405OcmState *ocm = PPC405_OCM(dev); - uint32_t isarc, dsarc, isacntl, dsacntl; - - isarc = 0x00000000; - isacntl = 0x00000000; - dsarc = 0x00000000; - dsacntl = 0x00000000; - ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl); - ocm->isarc = isarc; - ocm->dsarc = dsarc; - ocm->isacntl = isacntl; - ocm->dsacntl = dsacntl; -} - -static void ppc405_ocm_realize(DeviceState *dev, Error **errp) -{ - Ppc405OcmState *ocm = PPC405_OCM(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - - /* XXX: Size is 4096 or 0x04000000 */ - memory_region_init_ram(&ocm->isarc_ram, OBJECT(ocm), "ppc405.ocm", 4 * KiB, - &error_fatal); - memory_region_init_alias(&ocm->dsarc_ram, OBJECT(ocm), "ppc405.dsarc", - &ocm->isarc_ram, 0, 4 * KiB); - - ppc4xx_dcr_register(dcr, OCM0_ISARC, ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc4xx_dcr_register(dcr, OCM0_ISACNTL, ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc4xx_dcr_register(dcr, OCM0_DSARC, ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc4xx_dcr_register(dcr, OCM0_DSACNTL, ocm, &dcr_read_ocm, &dcr_write_ocm); -} - -static void ppc405_ocm_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_ocm_realize; - device_class_set_legacy_reset(dc, ppc405_ocm_reset); - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* General purpose timers */ -static int ppc4xx_gpt_compare(Ppc405GptState *gpt, int n) -{ - /* XXX: TODO */ - return 0; -} - -static void ppc4xx_gpt_set_output(Ppc405GptState *gpt, int n, int level) -{ - /* XXX: TODO */ -} - -static void ppc4xx_gpt_set_outputs(Ppc405GptState *gpt) -{ - uint32_t mask; - int i; - - mask = 0x80000000; - for (i = 0; i < 5; i++) { - if (gpt->oe & mask) { - /* Output is enabled */ - if (ppc4xx_gpt_compare(gpt, i)) { - /* Comparison is OK */ - ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask); - } else { - /* Comparison is KO */ - ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask ? 0 : 1); - } - } - mask = mask >> 1; - } -} - -static void ppc4xx_gpt_set_irqs(Ppc405GptState *gpt) -{ - uint32_t mask; - int i; - - mask = 0x00008000; - for (i = 0; i < 5; i++) { - if (gpt->is & gpt->im & mask) { - qemu_irq_raise(gpt->irqs[i]); - } else { - qemu_irq_lower(gpt->irqs[i]); - } - mask = mask >> 1; - } -} - -static void ppc4xx_gpt_compute_timer(Ppc405GptState *gpt) -{ - /* XXX: TODO */ -} - -static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size) -{ - Ppc405GptState *gpt = opaque; - uint32_t ret; - int idx; - - trace_ppc4xx_gpt_read(addr, size); - - switch (addr) { - case 0x00: - /* Time base counter */ - ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset, - gpt->tb_freq, NANOSECONDS_PER_SECOND); - break; - case 0x10: - /* Output enable */ - ret = gpt->oe; - break; - case 0x14: - /* Output level */ - ret = gpt->ol; - break; - case 0x18: - /* Interrupt mask */ - ret = gpt->im; - break; - case 0x1C: - case 0x20: - /* Interrupt status */ - ret = gpt->is; - break; - case 0x24: - /* Interrupt enable */ - ret = gpt->ie; - break; - case 0x80 ... 0x90: - /* Compare timer */ - idx = (addr - 0x80) >> 2; - ret = gpt->comp[idx]; - break; - case 0xC0 ... 0xD0: - /* Compare mask */ - idx = (addr - 0xC0) >> 2; - ret = gpt->mask[idx]; - break; - default: - ret = -1; - break; - } - - return ret; -} - -static void ppc4xx_gpt_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - Ppc405GptState *gpt = opaque; - int idx; - - trace_ppc4xx_gpt_write(addr, size, value); - - switch (addr) { - case 0x00: - /* Time base counter */ - gpt->tb_offset = muldiv64(value, NANOSECONDS_PER_SECOND, gpt->tb_freq) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ppc4xx_gpt_compute_timer(gpt); - break; - case 0x10: - /* Output enable */ - gpt->oe = value & 0xF8000000; - ppc4xx_gpt_set_outputs(gpt); - break; - case 0x14: - /* Output level */ - gpt->ol = value & 0xF8000000; - ppc4xx_gpt_set_outputs(gpt); - break; - case 0x18: - /* Interrupt mask */ - gpt->im = value & 0x0000F800; - break; - case 0x1C: - /* Interrupt status set */ - gpt->is |= value & 0x0000F800; - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x20: - /* Interrupt status clear */ - gpt->is &= ~(value & 0x0000F800); - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x24: - /* Interrupt enable */ - gpt->ie = value & 0x0000F800; - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x80 ... 0x90: - /* Compare timer */ - idx = (addr - 0x80) >> 2; - gpt->comp[idx] = value & 0xF8000000; - ppc4xx_gpt_compute_timer(gpt); - break; - case 0xC0 ... 0xD0: - /* Compare mask */ - idx = (addr - 0xC0) >> 2; - gpt->mask[idx] = value & 0xF8000000; - ppc4xx_gpt_compute_timer(gpt); - break; - } -} - -static const MemoryRegionOps gpt_ops = { - .read = ppc4xx_gpt_read, - .write = ppc4xx_gpt_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc4xx_gpt_cb(void *opaque) -{ - Ppc405GptState *gpt = opaque; - - ppc4xx_gpt_set_irqs(gpt); - ppc4xx_gpt_set_outputs(gpt); - ppc4xx_gpt_compute_timer(gpt); -} - -static void ppc405_gpt_reset(DeviceState *dev) -{ - Ppc405GptState *gpt = PPC405_GPT(dev); - int i; - - timer_del(gpt->timer); - gpt->oe = 0x00000000; - gpt->ol = 0x00000000; - gpt->im = 0x00000000; - gpt->is = 0x00000000; - gpt->ie = 0x00000000; - for (i = 0; i < 5; i++) { - gpt->comp[i] = 0x00000000; - gpt->mask[i] = 0x00000000; - } -} - -static void ppc405_gpt_realize(DeviceState *dev, Error **errp) -{ - Ppc405GptState *s = PPC405_GPT(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, s); - memory_region_init_io(&s->iomem, OBJECT(s), &gpt_ops, s, "gpt", 0xd4); - sysbus_init_mmio(sbd, &s->iomem); - - for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { - sysbus_init_irq(sbd, &s->irqs[i]); - } -} - -static void ppc405_gpt_finalize(Object *obj) -{ - /* timer will be NULL if the GPT wasn't realized */ - if (PPC405_GPT(obj)->timer) { - timer_del(PPC405_GPT(obj)->timer); - } -} - -static void ppc405_gpt_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_gpt_realize; - device_class_set_legacy_reset(dc, ppc405_gpt_reset); - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* PowerPC 405EP */ -/* CPU control */ -enum { - PPC405EP_CPC0_PLLMR0 = 0x0F0, - PPC405EP_CPC0_BOOT = 0x0F1, - PPC405EP_CPC0_EPCTL = 0x0F3, - PPC405EP_CPC0_PLLMR1 = 0x0F4, - PPC405EP_CPC0_UCR = 0x0F5, - PPC405EP_CPC0_SRR = 0x0F6, - PPC405EP_CPC0_JTAGID = 0x0F7, - PPC405EP_CPC0_PCI = 0x0F9, -#if 0 - PPC405EP_CPC0_ER = xxx, - PPC405EP_CPC0_FR = xxx, - PPC405EP_CPC0_SR = xxx, -#endif -}; - -static void ppc405ep_compute_clocks(Ppc405CpcState *cpc) -{ - uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk; - uint32_t UART0_clk, UART1_clk; - uint64_t VCO_out, PLL_out; - int M, D; - - VCO_out = 0; - if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) { - M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */ - trace_ppc405ep_clocks_compute("FBMUL", (cpc->pllmr[1] >> 20) & 0xF, M); - D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */ - trace_ppc405ep_clocks_compute("FWDA", (cpc->pllmr[1] >> 16) & 0x7, D); - VCO_out = (uint64_t)cpc->sysclk * M * D; - if (VCO_out < 500000000UL || VCO_out > 1000000000UL) { - /* Error - unlock the PLL */ - qemu_log_mask(LOG_GUEST_ERROR, "VCO out of range %" PRIu64 "\n", - VCO_out); -#if 0 - cpc->pllmr[1] &= ~0x80000000; - goto pll_bypass; -#endif - } - PLL_out = VCO_out / D; - /* Pretend the PLL is locked */ - cpc->boot |= 0x00000001; - } else { -#if 0 - pll_bypass: -#endif - PLL_out = cpc->sysclk; - if (cpc->pllmr[1] & 0x40000000) { - /* Pretend the PLL is not locked */ - cpc->boot &= ~0x00000001; - } - } - /* Now, compute all other clocks */ - D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */ - trace_ppc405ep_clocks_compute("CCDV", (cpc->pllmr[0] >> 20) & 0x3, D); - CPU_clk = PLL_out / D; - D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */ - trace_ppc405ep_clocks_compute("CBDV", (cpc->pllmr[0] >> 16) & 0x3, D); - PLB_clk = CPU_clk / D; - D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */ - trace_ppc405ep_clocks_compute("OPDV", (cpc->pllmr[0] >> 12) & 0x3, D); - OPB_clk = PLB_clk / D; - D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */ - trace_ppc405ep_clocks_compute("EPDV", (cpc->pllmr[0] >> 8) & 0x3, D); - EBC_clk = PLB_clk / D; - D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */ - trace_ppc405ep_clocks_compute("MPDV", (cpc->pllmr[0] >> 4) & 0x3, D); - MAL_clk = PLB_clk / D; - D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */ - trace_ppc405ep_clocks_compute("PPDV", cpc->pllmr[0] & 0x3, D); - PCI_clk = PLB_clk / D; - D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */ - trace_ppc405ep_clocks_compute("U0DIV", cpc->ucr & 0x7F, D); - UART0_clk = PLL_out / D; - D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */ - trace_ppc405ep_clocks_compute("U1DIV", (cpc->ucr >> 8) & 0x7F, D); - UART1_clk = PLL_out / D; - - if (trace_event_get_state_backends(TRACE_PPC405EP_CLOCKS_SETUP)) { - g_autofree char *trace = g_strdup_printf( - "Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64 - " PLL out %" PRIu64 " Hz\n" - "CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32 - " MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32 - " UART1 %" PRIu32 "\n", - cpc->sysclk, VCO_out, PLL_out, - CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk, - UART0_clk, UART1_clk); - trace_ppc405ep_clocks_setup(trace); - } - - /* Setup CPU clocks */ - clk_setup(&cpc->clk_setup[PPC405EP_CPU_CLK], CPU_clk); - /* Setup PLB clock */ - clk_setup(&cpc->clk_setup[PPC405EP_PLB_CLK], PLB_clk); - /* Setup OPB clock */ - clk_setup(&cpc->clk_setup[PPC405EP_OPB_CLK], OPB_clk); - /* Setup external clock */ - clk_setup(&cpc->clk_setup[PPC405EP_EBC_CLK], EBC_clk); - /* Setup MAL clock */ - clk_setup(&cpc->clk_setup[PPC405EP_MAL_CLK], MAL_clk); - /* Setup PCI clock */ - clk_setup(&cpc->clk_setup[PPC405EP_PCI_CLK], PCI_clk); - /* Setup UART0 clock */ - clk_setup(&cpc->clk_setup[PPC405EP_UART0_CLK], UART0_clk); - /* Setup UART1 clock */ - clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk); -} - -static uint32_t dcr_read_epcpc(void *opaque, int dcrn) -{ - Ppc405CpcState *cpc = opaque; - uint32_t ret; - - switch (dcrn) { - case PPC405EP_CPC0_BOOT: - ret = cpc->boot; - break; - case PPC405EP_CPC0_EPCTL: - ret = cpc->epctl; - break; - case PPC405EP_CPC0_PLLMR0: - ret = cpc->pllmr[0]; - break; - case PPC405EP_CPC0_PLLMR1: - ret = cpc->pllmr[1]; - break; - case PPC405EP_CPC0_UCR: - ret = cpc->ucr; - break; - case PPC405EP_CPC0_SRR: - ret = cpc->srr; - break; - case PPC405EP_CPC0_JTAGID: - ret = cpc->jtagid; - break; - case PPC405EP_CPC0_PCI: - ret = cpc->pci; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_epcpc(void *opaque, int dcrn, uint32_t val) -{ - Ppc405CpcState *cpc = opaque; - - switch (dcrn) { - case PPC405EP_CPC0_BOOT: - /* Read-only register */ - break; - case PPC405EP_CPC0_EPCTL: - /* Don't care for now */ - cpc->epctl = val & 0xC00000F3; - break; - case PPC405EP_CPC0_PLLMR0: - cpc->pllmr[0] = val & 0x00633333; - ppc405ep_compute_clocks(cpc); - break; - case PPC405EP_CPC0_PLLMR1: - cpc->pllmr[1] = val & 0xC0F73FFF; - ppc405ep_compute_clocks(cpc); - break; - case PPC405EP_CPC0_UCR: - /* UART control - don't care for now */ - cpc->ucr = val & 0x003F7F7F; - break; - case PPC405EP_CPC0_SRR: - cpc->srr = val; - break; - case PPC405EP_CPC0_JTAGID: - /* Read-only */ - break; - case PPC405EP_CPC0_PCI: - cpc->pci = val; - break; - } -} - -static void ppc405_cpc_reset(DeviceState *dev) -{ - Ppc405CpcState *cpc = PPC405_CPC(dev); - - cpc->boot = 0x00000010; /* Boot from PCI - IIC EEPROM disabled */ - cpc->epctl = 0x00000000; - cpc->pllmr[0] = 0x00021002; - cpc->pllmr[1] = 0x80a552be; - cpc->ucr = 0x00004646; - cpc->srr = 0x00040000; - cpc->pci = 0x00000000; - cpc->er = 0x00000000; - cpc->fr = 0x00000000; - cpc->sr = 0x00000000; - cpc->jtagid = 0x20267049; - ppc405ep_compute_clocks(cpc); -} - -/* XXX: sysclk should be between 25 and 100 MHz */ -static void ppc405_cpc_realize(DeviceState *dev, Error **errp) -{ - Ppc405CpcState *cpc = PPC405_CPC(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - - assert(dcr->cpu); - cpc->clk_setup[PPC405EP_CPU_CLK].cb = - ppc_40x_timers_init(&dcr->cpu->env, cpc->sysclk, PPC_INTERRUPT_PIT); - cpc->clk_setup[PPC405EP_CPU_CLK].opaque = &dcr->cpu->env; - - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_BOOT, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_EPCTL, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PLLMR0, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PLLMR1, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_UCR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_SRR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_JTAGID, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PCI, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); -} - -static const Property ppc405_cpc_properties[] = { - DEFINE_PROP_UINT32("sys-clk", Ppc405CpcState, sysclk, 0), -}; - -static void ppc405_cpc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_cpc_realize; - device_class_set_legacy_reset(dc, ppc405_cpc_reset); - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; - device_class_set_props(dc, ppc405_cpc_properties); -} - -/* PPC405_SOC */ - -static void ppc405_soc_instance_init(Object *obj) -{ - Ppc405SoCState *s = PPC405_SOC(obj); - - object_initialize_child(obj, "cpu", &s->cpu, - POWERPC_CPU_TYPE_NAME("405ep")); - - object_initialize_child(obj, "uic", &s->uic, TYPE_PPC_UIC); - - object_initialize_child(obj, "cpc", &s->cpc, TYPE_PPC405_CPC); - object_property_add_alias(obj, "sys-clk", OBJECT(&s->cpc), "sys-clk"); - - object_initialize_child(obj, "gpt", &s->gpt, TYPE_PPC405_GPT); - - object_initialize_child(obj, "ocm", &s->ocm, TYPE_PPC405_OCM); - - object_initialize_child(obj, "gpio", &s->gpio, TYPE_PPC405_GPIO); - - object_initialize_child(obj, "dma", &s->dma, TYPE_PPC405_DMA); - - object_initialize_child(obj, "i2c", &s->i2c, TYPE_PPC4xx_I2C); - - object_initialize_child(obj, "ebc", &s->ebc, TYPE_PPC4xx_EBC); - - object_initialize_child(obj, "opba", &s->opba, TYPE_PPC405_OPBA); - - object_initialize_child(obj, "pob", &s->pob, TYPE_PPC405_POB); - - object_initialize_child(obj, "plb", &s->plb, TYPE_PPC4xx_PLB); - - object_initialize_child(obj, "mal", &s->mal, TYPE_PPC4xx_MAL); - - object_initialize_child(obj, "sdram", &s->sdram, TYPE_PPC4xx_SDRAM_DDR); - object_property_add_alias(obj, "dram", OBJECT(&s->sdram), "dram"); -} - -static void ppc405_reset(void *opaque) -{ - cpu_reset(CPU(opaque)); -} - -static void ppc405_soc_realize(DeviceState *dev, Error **errp) -{ - Ppc405SoCState *s = PPC405_SOC(dev); - CPUPPCState *env; - SysBusDevice *sbd; - int i; - - /* init CPUs */ - if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { - return; - } - qemu_register_reset(ppc405_reset, &s->cpu); - - env = &s->cpu.env; - - ppc_dcr_init(env, NULL, NULL); - - /* CPU control */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->cpc), &s->cpu, errp)) { - return; - } - - /* PLB arbitrer */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->plb), &s->cpu, errp)) { - return; - } - - /* PLB to OPB bridge */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->pob), &s->cpu, errp)) { - return; - } - - /* OBP arbitrer */ - sbd = SYS_BUS_DEVICE(&s->opba); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600600); - - /* Universal interrupt controller */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->uic), &s->cpu, errp)) { - return; - } - sbd = SYS_BUS_DEVICE(&s->uic); - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, - qdev_get_gpio_in(DEVICE(&s->cpu), PPC40x_INPUT_INT)); - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT, - qdev_get_gpio_in(DEVICE(&s->cpu), PPC40x_INPUT_CINT)); - - /* SDRAM controller */ - /* - * We use the 440 DDR SDRAM controller which has more regs and features - * but it's compatible enough for now - */ - object_property_set_int(OBJECT(&s->sdram), "nbanks", 2, &error_abort); - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->sdram), &s->cpu, errp)) { - return; - } - /* XXX 405EP has no ECC interrupt */ - sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdram), 0, - qdev_get_gpio_in(DEVICE(&s->uic), 17)); - - /* External bus controller */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->ebc), &s->cpu, errp)) { - return; - } - - /* DMA controller */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->dma), &s->cpu, errp)) { - return; - } - sbd = SYS_BUS_DEVICE(&s->dma); - for (i = 0; i < ARRAY_SIZE(s->dma.irqs); i++) { - sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 5 + i)); - } - - /* I2C controller */ - sbd = SYS_BUS_DEVICE(&s->i2c); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600500); - sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(&s->uic), 2)); - - /* GPIO */ - sbd = SYS_BUS_DEVICE(&s->gpio); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600700); - - /* Serial ports */ - if (serial_hd(0) != NULL) { - serial_mm_init(get_system_memory(), 0xef600300, 0, - qdev_get_gpio_in(DEVICE(&s->uic), 0), - PPC_SERIAL_MM_BAUDBASE, serial_hd(0), - DEVICE_BIG_ENDIAN); - } - if (serial_hd(1) != NULL) { - serial_mm_init(get_system_memory(), 0xef600400, 0, - qdev_get_gpio_in(DEVICE(&s->uic), 1), - PPC_SERIAL_MM_BAUDBASE, serial_hd(1), - DEVICE_BIG_ENDIAN); - } - - /* OCM */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->ocm), &s->cpu, errp)) { - return; - } - - /* GPT */ - sbd = SYS_BUS_DEVICE(&s->gpt); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600000); - for (i = 0; i < ARRAY_SIZE(s->gpt.irqs); i++) { - sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 19 + i)); - } - - /* MAL */ - object_property_set_int(OBJECT(&s->mal), "txc-num", 4, &error_abort); - object_property_set_int(OBJECT(&s->mal), "rxc-num", 2, &error_abort); - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->mal), &s->cpu, errp)) { - return; - } - sbd = SYS_BUS_DEVICE(&s->mal); - for (i = 0; i < ARRAY_SIZE(s->mal.irqs); i++) { - sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 11 + i)); - } - - /* Ethernet */ - /* Uses UIC IRQs 9, 15, 17 */ -} - -static void ppc405_soc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_soc_realize; - /* Reason: only works as part of a ppc405 board/machine */ - dc->user_creatable = false; -} - -static const TypeInfo ppc405_types[] = { - { - .name = TYPE_PPC405_POB, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405PobState), - .class_init = ppc405_pob_class_init, - }, { - .name = TYPE_PPC405_OPBA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ppc405OpbaState), - .class_init = ppc405_opba_class_init, - }, { - .name = TYPE_PPC405_DMA, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405DmaState), - .class_init = ppc405_dma_class_init, - }, { - .name = TYPE_PPC405_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ppc405GpioState), - .class_init = ppc405_gpio_class_init, - }, { - .name = TYPE_PPC405_OCM, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405OcmState), - .class_init = ppc405_ocm_class_init, - }, { - .name = TYPE_PPC405_GPT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ppc405GptState), - .instance_finalize = ppc405_gpt_finalize, - .class_init = ppc405_gpt_class_init, - }, { - .name = TYPE_PPC405_CPC, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405CpcState), - .class_init = ppc405_cpc_class_init, - }, { - .name = TYPE_PPC405_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(Ppc405SoCState), - .instance_init = ppc405_soc_instance_init, - .class_init = ppc405_soc_class_init, - } -}; - -DEFINE_TYPES(ppc405_types) From 52f0b59ec6b780f2a3e162d5862b90b406fa4697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 4 Feb 2025 09:06:49 +0100 Subject: [PATCH 2544/2892] hw/ppc: Deprecate 405 CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ref405ep machine is scheduled for removal in QEMU 10.0. Keep the 405 CPU implementation for a while because it is theoretically possible to model the power management (OCC) co-processor found on the IBM POWER [8-11] processors. Signed-off-by: Cédric Le Goater Reviewed-by: Nicholas Piggin Message-ID: <20250204080649.836155-4-clg@redhat.com> Signed-off-by: Nicholas Piggin --- docs/about/deprecated.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 3d39d2a9da..e2b4f077d4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -266,6 +266,15 @@ in the QEMU object model anymore. ``Sun-UltraSparc-IIIi+`` and but for consistency these will get removed in a future release, too. Use ``Sun-UltraSparc-IIIi-plus`` and ``Sun-UltraSparc-IV-plus`` instead. +PPC 405 CPUs (since 10.0) +''''''''''''''''''''''''' + +The PPC 405 CPU has no known users and the ``ref405ep`` machine was +removed in QEMU 10.0. Since the IBM POWER [8-11] processors uses an +embedded 405 for power management (OCC) and other internal tasks, it +is theoretically possible to use QEMU to model them. Let's keep the +CPU implementation for a while before removing all support. + System emulator machines ------------------------ From 9cbbe72924b4be1870563b5101548ee2241b279d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 11 Mar 2025 14:54:49 +1000 Subject: [PATCH 2545/2892] ppc/pnv: Update skiboot to 7.1-106 This skiboot firmware importantly contains updates for HOMER/OCC bugs. These subsystems have bitrotted in QEMU and skiboot and this update allows new QEMU models to be exercised. Power11 support is also added. This model is not yet merged in QEMU, but firmware support will make development and testing simpler. Signed-off-by: Nicholas Piggin --- pc-bios/skiboot.lid | Bin 2527328 -> 2592960 bytes roms/skiboot | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid index 906bd5127175d4f9fa8a1e160f7a648902bdedd4..ffc77ee11126c9b70e6c5691f0604ca8579bd0b7 100644 GIT binary patch literal 2592960 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 s`crIu#n=}Y6h4!fr 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�e!^<$&4!lc$ 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�Xd+|INPtwPRyypU8iQ=#QAZ{qK*t0IM*`R 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 z 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

7>kS+gkM|zkN)d{@}G?P@bcQw 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

lsTqqGzT04C!K#bmZ%o2h%5Gt-fRHdrP2qdUyw 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

itqY6peN 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 zM1urKfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c 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

}BX7Y`5Vm3OxJF06g*& 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�T*nH4IH{?3-`nu_AKDlg9i6+P;t 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

=6 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

Q}%=UVt0lP z>$3f17LhJE@6gqNx0$zbJvm+?@FHE5*$+Qg^$YY$zrF+T>n()LhEpwz!Pk{JY$}sI;YI9f9NW+9Q2TK z+1x2Rufo`K?A}JFgKoR{#XbOaq@h4;|G>vhkfZQEY2XrP#SOiE%dERL>c8@S3*s8j zu~(_n<3{(rIal{ZzstqOf%brx;kuJ*3BDyh1mDzcPqwKQS-f0g_#^UIh#ZbRO!lt} z^2v9T#GU33zkWpUp!KhlQw0Ul(!zp4(DB>p!Z#ZM&l|wQWcpMm_FO+@!Iy5qSty}5 z@Yu)W1|fCj3dyfSx~~W$|2I4xRT2&A+d-P=@w{moT;bxMb@qAPde2|CV!uz%6CL zx%k>|KQIT!kWFZFwgwbz z&mTHDG@uH0P3HQp>l=G|y2EE~3vBD@L5z?;9bOJU(YkekvUTgmS154`!E^gx?` zeA_3Bd)i;dIhxMm`o<4^D6s8AA7c5Q)&I~y`)beC(+$kelp477`0PH89@4SGoOt_# zSYe4Dwy*g;IWYI$sXX_z-R+NgE=F{8&{aM6zk{#PFA7^6WM}-o3cpJzGo=T>=LfKd zgtae)_CAi2e#wE2tj=DomgBn+-_{h)2*T%I3f$87Uom@ZEPuLL%CZh* z$BvGRsprUDh~+i%+7#$j{L5JX#@|<30A7 zm(F|*y3KcC2gG_k`b)L!XhxM|{Oef{au5s@!rx(E{-U1u>Dz*F+B&@Rw zRpFUe@ZF4jti-`0iG3)=^9nq(aK9Ay^Wnf6U)5%h8}<&IV*t*8kLCQ$wr1na89#WV zJ^rbMs`M6|=f(J!z5a??9zP{A@a2#%7b4cZ0{oh?>?Ft7ratl9X~2{&wa7PlPEq&^ z#+bxkmd`kL+E2K3-$i1lgnosxW1t<$V+Nl}`JT)(OD8g~q&Q_b|Zy5$<6&&M`)u z8wcOKM0BC$c)o=S05PjW(H28k8p=N4AeXq83V0Fk&I_4>hN6tv#AI9?u_$i@%!mH{r&Kw`@5;8B=ercOT(9qwl6_@CkaD+BiDXK z$9UPt#9v##1iDfobUV_fhT(~hM~;7NtVCirZb7}Q?rLxu{n5q3@v-NT0|M^`cVKuG z`5{U*4k|h>fQKj3|5leWxCjG=$7bUK<2y&(GdA5lpO9II|4YaNv-A0}9jOP8m(s_A zw&u`AO{-}O_D6SHkXedx_WNA=hlrV{ZPIQ#hwd(Wv!`tG%I*h`e=Pmr_LPR3xO?yG z=xfgUo)2#Jg?Mw=-w}JTSX~O7>2c)tHLb^S4(}YCJB~J0d|uWsFZ(a4sA2( z^L5}MXYQjs!ud$Ac!A%#j|SlCHd#l<(Z+lA_0)YQkrNI%vNZkB_WRPFUmyP%�yu ze>;u*r4PdI!n{a`W5Dwh&K#MwT(c_w9AT`>hzHJHew-HH+dX~!W8Du8$?BO@Fs! zX{fkR=t}QTb7|-g9_vRR!}%%;a9Q<`JrQq-ym5zcX0rf!7OL&MB3zqdGbK(4Yx+UV z>4U)hK+zdLj01oXu{6XnNp`lU`HzKP#`f_;L{XVW+PILsX32Y+z9 z6mxzH`#?Ma*Z)a}woLnLr_}+$L_ecnwyo?JcorqMHoFz%0X+^BPue8H^yWoYLT*oP>aoLGbSA>nI(u9i1_ zRjmT78_@Q{_^!h9$~wC$MsQs*W&sxzH4wj+5PLsKGIXR5bFs0b4%v`;X1DE0cV|dPeq1eH#;qSz2YI^ZMrvDG{4&YYws7ZCLy%z(XpJw4% z_U7#R=_8*><>SqOx4Qem$3D_M{RHf^>r$!TpbyY+`rn#`mVh7lnco?6wS5<0EFv!l zoO9OPtTo@{*8j2BE%W1CEYItq-)C^1zEplsswYn~-eLu0KFETnGO7^$DaQ7kg^r77 z)|r=83im6{1YnPxfL{!@=JB$|4%|P^*h`c<3VLA9*t^ftX4IsPADvQn_Y^hUhIv^5 z+&`Xv=rfWB3)deVD;&HE_Z|iurMPAe@H_DRk+Dk_Q)c@Jt~t&R!rlY^i*rsz$Q6Mc zyP%mj{D1*C`O;(neN3nd>H@QE5~x49{mpmU`!Erxy9d`D_`V+B*W>#-wDSPoHL?iv zcnR9QPPci6wD>5lm*M+fe6PUwT?VF)nts24dxu?h_u%?z*Y)+drjPVQoBH6X*>qs& z=0k>Vjv>B4&aI&QymsF4N%bl6k7Ia``f8lrMY|t{-u+{dMUfj$i^e0Z@Iu;%uXwGpsq= zR*QCSe+2fB)&G>5*!WMW^2-p{_=t`<0v{pocgF!Tua3YN@WM8L z*v4hZ`}=dkoW2Y>QK3WCos_D`Kp9=RdJzBGN`N3&yn;z8Ccy}Y0T{S@Q7 z0pCkYFJJr|`0oz*WEP`7^N;bp4~uo~==*l*@jMY`UG&ogdV9ug`#$)6p5Ivb_Vzd5 z{;8BX30vfq{3nNq}g+t=eSN{d@Y(P8W%YhE_EOWDpJyhbpel0O3923Wx{(ReN#;9XVm!r&K z-S(?84_wF4xt<|z$7>N22Oi7yhkK9j`TVg@-u5%ZV&3pisat=99I7}Av*&ZZ$M@X( z;%$fi26F9_ucXpfz%K}!e0F?ay8^H^;(c6SBJ8|>@N)%y{^_rUHLZVh+`0b{aM*fj_{f)KhY419Yk5CimUa z@!l@b*UY0hk5>iy)iiY+vi9`XzGd1kVEcft1%2F|Ud;137{{ezA6$h#mZ6Vg|92rX zBMzADF*g(Cdae_$S&f?v^G2WdOWVhn=(Zo1^^dkUqwP(shdNQhhP66Gx=Vio_FSjG z%t^utzv&aqKU0H!5QcNb6=M6B&Re0!37`|n@uLuX}Q1<(3| zdFRMUv@cXHTGvs5>-^cQ`?>KyHQ`dSns59swlDoofAwCD1O2ukPxv=^pS;Np_x8+BWL+5^*v)>&{gMyp?$tPV-E^d& zu<48HChRAS$9Z){X2CK!(};X4xZc5j^xpE9W=(u!r_=vQ?5`&f4||-p_P_j&#_2a? z{D29%!OVLrG6l=(GF_6dXX5m`#phne{%{iIPvGx3@_uzv=z6N4n((HX(|P)Vpux>$co)DuYzOcR zYj}bJ&v@CpWlua;<>sv+>YqriEJ zWe#RmtlFMAc?J8UPbq!mjLqw~FHY)qetF)&b4J<%5A;v}RhQ{Q@Su5Y=3&#VDJIy@BnTI{(YG5ybeO*7qLxxSpfOj<8&tDtOp67mF|#eCH(a`q~mc zmwBC3mzntyUYNzTi<3JcXNN33=}Y* zsnACqzfWR4;}3EZ&ScR?+Q^vEx6{)r@qIONS#hpZOVbw$UaDUJ+hJkmnS(z>j9=+C zoRdes62zAxw$xH!(F&m<_c8%Y`6PgI(|(TbtT3QP^Rv#Tmipv;n`(PUn~e}x+*xT>FT}5Om6XK z;P1DPV?3>MvMiXCIXX`M^x&uTJpJ9veE_;{l0Lw5kD>i#S^V1T=G4tBqjCNA5%WD| zzQ@e>EyO$aDap&)IJLmfzRWuL7wiu&FeVz$Vb@-8Io84xHb2lXpL@?erk=vWO}H*~ zV98m>zY+SBIdo{}bV(M*-~L*|^xL1C@1GjjE-`EPUKecVxOWL&X7PL2gBqE09dvWt zoE2CFeByjz!48ej0{gvQ%Ye^blzU8`Pw~COnNzdsSSByddu9$;=Wf(Vn>tw2nNz0= z3JOo--En6@i)U9;Mtb*h9ZL>B1bmmL19;YpXA|gqr+7z@SY zzJbv2a9mv-58oOxzfnbn`$x)0qk&2%_)vF9t+gArA&;+(hhpKtaAUG{SmHH_@ySteQJ|tdffA;gq!{ zFt9HX3k-y6ii@TK@aJ$p_RWe{H%jff1?U6(%7E4AGRz<1=MFI)CFrK0^%Me^^ zATT^^B}Vs!BDJO{?}TDjXQkZGJr&9#4)CBr{Z!Nz{I5suC5XV6H_F+O0{ee=HG-Tcs6Ny_JmYi;NzK zSkYLB<;v{3tvj|_Lm^a*S(pF}Cm9K@v7GL(gsuK?BoG^O#=oK3*N=hkXtiMphT{XH z`$Mu~H0rm?M1WeYz2S&6J)uYDFGZaoT3=ynA&%;0D!9{Dt9esrHT z9Nq_PVOY_D4b>I~V;5dm^}bM=BKZNZ+j*Z(*HyVY-4zNahJZ=Hfd|AVegUq{MTQRw z{RMqOy1BPPk)Xj#yF;V%OsH9ndI8q1_g5-Xa3D4qH!U=ej*J9=cktejwKq08V(q%F zvuP)20z6D3-^Gyi?X6AS{mRoy+ z31MPUtHcoCEO;xyz`+ziHRQ~_J6n4@+ubascKy0_Sole>f`PCv6dLk@w)#ioA=S~g zrPjJmUs$cJZI)*4Sn((s@?i=4Fywu{cr+9W>e4cBD;`Q%cuIuqv*amh^P1LOC1I)S zBcCA;(wufEG_ViLEp7$FL7ByoKw@A>ZwJ63b_kh3R1mhniNN*+)+zfe2y@UH4($&O z*IHmHqoDZ`tjb_4jB((nX$L6o7(KvMV{I9;b^x+a(6=KTPgvcdfuYDKTH%^oJ1~Ty zM^rG;YkqP248#V8u=@@qlCcobGvHfKzBz(rsz&$8HWg*te&29(Kw))`aO+FNM!8|g z;}mbA@n|xxi^oO>APS6PCr^yVz=C}HLSu2&8jFDi0(~;sTiZLWolRZat=K?qNzlR; z(b3*nTLLDf%f-M)NEFSxJ6n6Z+d5mDT6(*>o4Q-)R@l+e)ai>taG<W)}~F?hBC#Q@f*Xzo4GBPmhK5`jp0G*S4 zOO*~XZ!j!w+QWgUv+}I|P~t!+6fq-?LwJ?)n1&m5!#@3WvsD@i9e@lm&ttPJ+$dEs z5egx(VKr!snj3sFj80wmahYdQ{8dJ3b+=pVUstVw12B96CK`x|xDLf86x$E7HVa#} z!3g;bN!$Feh5~VL8Nvg>XcYV?F;=d{e@uLyQPfseY^*_T>@`uR5iRnO)wt#Y$dPrP z18ZeP&3d&OBG~@k1VyA6xOQm2uVE+{TeCM73aORZYbhl+SA(B+_H=ZwRwYrZ6vAXl zyo^hOG9Y$n77ULjro|Q{i}TY%~~11WIjAS*G^7C%{}}u^$Pg626A)^xfxz%S zD>y0$5s?nFGIx>sHyR5=&eE-^sFE$$-k0artEsNkEQpjf+$&w9iFzOm>qq*_?k13C z^0ctNLPb%8hiiOR}#T3*;2j*UQZh7u|3(wD#N1`Sa2XmVH!vS3hLdxo??pm{M+ zS-HMi?M^1RzEPSIEGiOsv$>(Z;jRE70Ira?um}TIG%!Xz2I2$-R|vBSYAb8)7JRyc zi(uYPjYwc56kqF$1tNnX(vI-$a0p=J7Rg@fqDt^Ub;h8p+zGZexP;-d>*-duD?cqdDhO zh62EYu>mhA++N(ydZ*IK$m#=}d znXg)PP2TxiUu`VCS^5(DZ*|^jUB5AZX-w6e{WRNPuii+xY8`H)<$Ef;Vnq{CHPmU5bf*`^8+4U({k zKqqvwCAMfCfKEP2b~!YP?IRe9!gx;@<3R9m5X8$1G;AA;H6B@&w8+&eif%LsY^;Q( z!PgLu^oE1*9XMC<;ZP`=y~OJ8)wi-$Yu8s8>O~W1czqQF@EA?WF!iG`t3lhG<)Sw_ zVyiy*WlT)idu(GclC4p#II1O|peFmOUZf{7>zSuZLk@8Pi`TJvkI8%rwu zshh2~_Ld4L$lP(Xq)cnR4?V*kK+C@ED(DQsA|ta#ivgcjn`aPxd$o@OWK~}Ru?b`_ z8rd5jgb7Du#XV-|39TuBmE5n5BbYsm*ouZ@POFA)td-Wb(SA~89AgP4VF4$LHT|co z14H3~At9>(NCV4=5L&=AONA40>y~6F2~#}`VQ5@T6>(VK1`@XE9&^093e|_j{XIMi zuftfa)zQ&;V@GHE&Gp0AfLoR}7KCqT~y=8Iwl*Nt9TmcX4Qxu_Lr}{Mcrj~t7mK-ouUu#LIX&RY4LQrPa$}`lDc!L! zMgq31>B$gSG7{JiO_!Elnb)Xw(jmi`| z3<)kUFn-++Yv(~m4zl_~oPZeq*9Nm&F;)}+LB0@-#{lla3C8B7oGiLr z+*Ym6!EKe_SU5DQ*H^Ds9ojI<-homq^0COkP;4|J3V-fAtv8l+&8K?F;Bw}1JzS^R z5kq(2U^G$f9S#ga>eNz{&CI$$Z(tpmQp%lhqGFc_!#uaUYi^1D@Syv#YkDg;Kn~7! zi!M@vZJXwD5mLFHE?=xfN_vr`5it$rt@r0(t8yczkkSDdm!`q2q9cAMR6|!3lfxWE z464k28o@9lwbsr+Y@f_cKOBf|nh~46_AK=N-up#|-x>@>LNPJIQkl?Jb*q%_hZ3w0 zA(c2>-?0#MYk{Q4xaK;p>nyhdwxh4olr@DqYJUHQJ1Q&wfJ(dfw08QfmX=BjE($Fm zM5CiIID&0ZSfXavg^FyU-Oe9QGZmh2kL=r0fbE z5SI-b;NFb+Bk33rf*~xf5%>%t7Gd{>Rl-&Ed%(9BYY#hXd~IUH7uOPwkG5p^ zM*?FS?}Sgxf-T*IlluUT`(3mf#;)ciE@*47XX^qLt3*c==ot1>h?bBzsU(Xc=xSnT z)Ae?1AUE!-o3tk!RM1OmCDmdKbWs|ym};IlLo`K42e3L#WPncu-`LeK!IN;iH3{YP zEkYtuWyWC041g1fS@iJHjv_-|Bua7mni2APb{TAd(sP&bvLvIfB18$;TLQY1 z6lw-y$gQxx@hiIo;Al>TQ1Mr2>mDo zL8&*nzgMQ$&3XJ)>lM}$th%-tPVAGlB}q#0Md~wX5!<5L+Jd+O?ZP59=rE-LuSmg{ z+1n3GS_Huh!B8nK;m3mo5QHirKN!5(p>9mux$XPC;=9-2Ft9=U?sZAs zh^{G9gEpug)YRxM2OQJX2&iRrfC879VO_GSVx!)~%q4^oy_s}F^0REOm`y+;A;A+) zS`iDfh}^d%VI6|?IWWw9dJGZ@uR)|mD(5*#nb8Hp^%yK+R}}gN4^N^?ikH^4J?*Vs z&8@v{yLYr$z?psB*}AQ@xf_gK#>&P;_whBDma-~9ZqM2ek+3@y1G@!q5WFFsLd+Nh z6ZBQ0X%62m$TGFUwc?2+1a-5i&MK5e^1d(%aRAb`;a+Ue)JRZJ!{UfqG>(8K`@z~M z1K9WNou8{bqy<-t8>j>((VQY;5fQnwy=!MvcXJ!`6LLlX2?3N#naVT7GOTFcOzEoi zsEUa<;RldSgtwyy0!PMTHFbBl?(FEcx_4Wht-jV>-L0J#9MU_X2DSb^U9>X(C=?kO zBiACz;N}M@#YV>%JEOxLz#`%BPKIDC=!y6O13(~{F<67Kx+3rZp)3Mc&n{n6bMx*V zz|-QuV*$L*8!jFyh$ab`ABhC)G%~?tYGk9cWPWgoxp>R1-UV$Qo*~)}N5IMBHc@EG z<$df)dcLqLRYK$x3kzt~`Qh8Y&h@)WXw^3oAM^z%e)=NGk?bw;JCM?IgPP=ypvj4I z(uJ_g=iY(#ln9T6bO@mhiVyJt6WX;p$tX zL;XN%oK|XavP(kT3&BvT4K2c3C>7aOD!Wf`y$)#wQ!U0LY>Rey-WtOY=PzI{+OAuY zf%p)+-J7Iv1W|^Nx!N?bcwjpXl^bX)>olsZE*Z~~qu79`0?9Bjhh~`WBDiZI`iZm4 zAxglH(6o;DL-SA?2L}=S36Lmi=kU$i4YsCfu&}|_jvOM0gS%3SKxj-js;nCw!R2Pv z-g#}UMek7@QWV=Kuc2=f9A`9YOn9_Gg3Xn2%ab{Ew_J-LNt$QbfQ9C)_-GO#qK?-b zV~1xnbO7v<4Wn5Y#mw(~&8@Fd;dq{IV1-Ra@^v&4j*HjGQ8W>10}e%A#D26(=|Vjs zh#`N=lFV!#n^v*cBy<_Vy5x{LUorx zId)J{)Gve%Yhf=H2Uz67(K7h7p-ebH7-hkD$K;ur3ZqL4%;4pe!fbbe>mKB3UZDV^ zs-W;=>0q=HFCxq3Mm&=6Zo~&b$^u35hB$id8;A*a-?-VethtM@=gN)QZqR&SL|332 zJyiyiE@9|JS@s=3tS_C-`r+Khs?99AMp5l*eiwol92=CF10W?KOjG;^Fm1#k)Qjv1 zo!trgaJ2Yy=);=gq)_i5V!FVa-x$6WW*FxF}0jaD$UsXP%Xq2>l zn*fyaw!ucWTEIG1MKt$h`oxA13E=`+JRp5 z_k|SlyG)$4q8l}8^ZQnn(z1ua{s$oD>&>3;i!I;57*W&i&}bq$oE#h!OYMa^0Y){& zhAx(5w}w;f1XcrT3LQJHhqU7mAuUVuQ+sn~mxvFf;(3wW5gSF6XgoPUTbrdr-b#WN z>Kb05iJ%IW`uwQQnrsapKx^X55E%*M)zdsb1lb0tVA}EY^m(w3i}2P}%(q6iGVtEI zuDwO8b1kjc=^x!UbTHO-=T_`uTRQPy>-F5s@(;u&0QZ8Aixqdi8e1U&iID~v1Y1P= z+6a&fHp3xZqu78=B0m6KTq2sdhY|3p-D+4wCBZ%OL9kn!(E5{5yOSgRh#8t6&s$K4 z?2~d_RSFI-Fcy%O7>)7+WOeP>4ckcf4$(E|gJDZ73}M0m4z7pQUT)c*Jc!yC>R?Cv zuI(4wfg_)4zC{K*3iFLbHE@XlVbaxS^a25sRwyt$HV?K9UxmX3<~-rp1e4kLML=>3UUs|ccZM`Qa`YA~SI(U@!t&vbv|*wmOpTs4(3cyWB1r70Z> z<1QNy`WU3;z7>MzrbY$=IA$?25DutFl6?;ZVnKfMnA-s~9uJL#d5|K4P)|z7KCoSk zgciVlMBhObkrPP1epzf97IJ?|tgxo5{;)5I6MJ@)I|V;zr^6~V5cO5ANm1t3H^QM% zsItmm?FP}gXT~)c4fiXkPiUTS0*7!CM@T+8INm{x+4tOSak#$VNI>Cl&i(ip^uZAy z(Fij>3dRyRVWDp#emES(`5#(RB2k=obWed+_$7EBAgF2tJJC=BzqASrM~8r1?#4K- zF@j?QfY(RG7V|tBQ!s0U4&YDd0@ihyuX5T>#`}HAAz+(7M(rOV`$x+DVQ_;|XtEzh zHhKyX7Yao6tMEuPj5!<_<;+Gnv3g#F&e;1fiOw~mIF=%S!5L7~uAx7e?gnCGQ6gFa zP*~ps;Rt;-?gvyQ>=A+usr{hY&G;y+&n^6~MU})GpKErz*H?eeg_U{A^GzInE$%BCJ4h=cq4P zN9B?6KfTSnTUzV!J3n8!u_S)8*ckMGI2OYlsu^D!a2!;w%&s;6_I<%NN=quMCGH!X zRr4^&(IQa-Ac#GX95M||2UqFAS z^bcr5>np^oIHD>ytatt1;QGDM^_%4lyVDh%3)XNhSjD;UyDw_87yA+F20T)3RKRU0 zIy4#?Qz0He#Nf4s*T)%!HzycYfy4;-RWPEkM+^t|!d`~4j0|GD@i0nYcv$}+pdhCr zpjtaDOYOL(`3BXwdv~|$+SAj4|5|pb*6!WctLwXWx2n$eUEQkny6$FWr7H1Pg}-Y2 z`BkcNgG#NdQtPVv)rRVRtYB=PvYjHJ3KoMBgjAWsCsFLdV&k?SWOrlV{&0-WA{b!V zfDsh+j)hH<_xgi-BVvVsjvE)Dif2CNHc05B;@HW1(g=v$;i96RF3^#%; z8vIoFtFg#0E%1l;}`=0hr*7eUDeJk&tF5}ec#d){$VGRKqZ>UvD$>4&H_Uhqai97 zfL$S04hIta-xpCq#PgUiqKm8~!njy&F0w*Cr4hn>DUl0z<6IQ6hgz%036|7`5jPpn zt%Jj$U9Ir43#zD~%$X{?L;0eX3C4IsiG7Gfxr6TM6nA|eA%5WLGL7b=W%c#I8*!LXYXCqR7#TD5}N z7_zcy3<9LxRy$LkQ2OJe{16xr+a&^0+n`-#OM)US<{8IcCm95YK$f;`20;{tEOV_s z*>ln!q1;Ee8{F9T9%zYFnEw&tb#NXA_E-eaLYulE2=1;d7aZ_(&Yc0#mBkp5V^A{@ zRD>R;E%Dj%B)DlHd$tE*_0X1wA)4$9!Pw!(4+9$D>Yy_ctXdBCQ13TQbm*h}DT|GN?G|XA`l$10|x#OQgQqw-jD!E5sVrMfZyJK}lk2L=-ml1pYLlnhc1&Wo7iZ9!`gj2p#3H#`}e zJ4b#vAaX+uL1zMq43@4>R2d2n2L%G{5y%o93+cT;8tE`~5f{ghsv=by9gSO`u_9#* zHr3P4j1)L%QIXnfKFE`mOo*#hX&Ud11X#REHE(U|X`lH6DR6pxe3rv?4Fo7jQN?6uY0o%^`ie(Y-EdW3*U z74EdxbaX-$U<8I4!t5K!QqtMfZS5US#)m+`5Ej%f9F^KDzgGW#A2KOQ8(m`fvpVV1 zr1{(K8@e;de`j;+tech~)|67s0T>(rF!HP*iJCr&fzV2@LdZ?%K8)QPi*MBJ8=|07ivAM*3_m(@iZ; zSL;<2i((tVLPnaR{k{IxehE+yZK&=|NEWlw1RMm=;mVX0ls>?Na{6#;TdOU<3;NZl zg5X$WO_8)il0q+Mw6&EcrIQb~PCXK1^c3%g+%bVfHsDoYcGShQ1za8orw{^~%mgqF zll;aBe85!ey;$2I1_nla$dRn`>_U!J2A*Bsuh10Ldl@%C-4q$>U5Pb|~KxB%cB#g{p;h{JjjL6^B z+S%T0p%FwCG<9#?-HCiSt5-{dI8nQLHNt$r-e?gGeL8NfMoy{z&}xL(um(=I!qSVj zkrN^@idpJzYwxn~k2^unuCAVrj@_LI>g8oQT<>VGr^D*$YPELn+HnKQvrDovlO&OC zI=KlcMQ}X=+T`8<({ur~`uvHmw$uGUpLW>2YfhWs?{<^=nI0mwkzULfWAi0?4o9^E zIKT_L>RPO6a8| zRD`)gVLkW*olMEavr>L=N4z73}Ve7LKEb0 z=Zr{4jIt=CiQYf_Qv}JyYT;RGsGf04qh?g(4azlW92rah&`(y-G2#!tZp!|g^($X# zDZXdVtdhLUTN?K_{fTuG(6Kh1Q)j-?HaKf9t~8pqtlr$h^FqQV#}DQ-%nN5yWuBer z-y(%W2R!?oo1e}QK&L(+3JXT~R<5v8%2--1_0upcwtt=M%%+)UEEz6&>4pT0SVc${ zU^bLyA-R*5*o=KGo*yyW$ek*&n2n6Ub9}({v73j=lWh@ZQ3~B6#YoZw1YX%TAPpt_ zulx1UL$+&1b-^}Z$wW2-Na9unwKyny?_Uq;p&$q{X~P%})`?-Ch6?giatqUKGvW_` zi7ZZ>NpKbF(%6JP8s1MM;vZTXtDVimhG4xTZe zQ=(#EAh^}HpNkUKd;~<{463#pmh-@n#%9y9Msz0nXVTwZ| z5`K#GYg$y6YlZ)LT0gGgx5JfjSB_eP+d2RiDQzz3HC0@#AQ*YQIr1T`)*^GyMyQ4f ztH}-<*6rW8V|0KF>Sp8)U`!sI`$4N4|KUvL)}5Vjm@pLtQ?nU_UZ8?grZY4ULfCxJ z0@5I-V!oIR8luonD7RGIjTQ*`gdr!8#|FX(x){^v6?2Bc07pT+{MWq&&FP#*TxFbd zV$#I-IRfBH&TKuMvK)h)TIDFr2(lX#kDp1C7UXoX>WRZ=R&|X6qr4exuEQ4DVaKvy zlh+!og`}2AthL}84RbSL+p~MCd(3uWU`M21geu6;$kK?wJgv6C^2o#1ab#01Ta&MZ z-LDlGM6`sFU=U7UB`#zM#iIwgJBggL7E_opFmaTN2nnV|0_NEdoT*7@Na_5rk9TtgQNx&nTCg#msD;OaiK(dlhinRJZt6gm&SoBxWFi)kNOJoB>J2S~t(#;ohRns@I6^AT*#DG#CEoD>%O0?v`d_qhpezFJMq-R-+~ zRb-2nTl!XI_Lk{W0B}f6XNBE$y(EL}>FVulz2?H@T6Q+|w(h#7eHYHNws&5*Y{!;% zU5NXUIN6XD1j%OLjS87iA-dG`P1q=*WFqoo*3-%ggqJ0KvmOf~G&XW(U{^=a+K#54 z9K2&fWHXbI8;DIgWRbBshh`iY6DRN@W?{Y-evhe`Us8iuAcNavwI`q+X`>7o{z@(| zeqdaZ67vgWb{o39h&JWDz2)YTUv?AO5~ZV~Wt1MxommBNw-n|pJ}lFM+uQ6${AgiBCg1Oxe1V-7Fp)$T z(WlrHz(1v!eSzY*s||Rlhh-nzA4V)6KrzS2grnKEy^tI=prgp%vLtMK|)l z=`;qSEw0ICGqW?hOU8VdQRGCO>cNpR;j|La4@{``tvyJi(}hIf03!QH;IK;Do12>3 zS}AgHbCH{fA4m+WDTBKc^}3slbcH;^rm4=*AcA`6p`F#*tmlGgEt+L3?-(5n56oLt zKb=+ax-Jf&Jz|Vc7UE!&cBbIDFbLG0_HL86sV>A5hpcXWwpxZ#+SR?aEFV3}a zY33-{QDp`C5k@5>6K9exQx=sWYr;uQty1koc4W-ojxBk{rJr@-nI=bl-F$ubyk+yw zjIPH2jej*e96f3CVb1+@7UpFB+9TMVAQfo~31b}1L^Ht={WgQ#E;WUs!kpfB2r}oR z`Ih-<-c*FpgA=?k@dTB=Ge6DeKA$xhjUdi}gkZ}_FTL<=al}j$r()hBx(o4Q%v(m# zwskaylf2^C5(Jsq)%E9_3-{Am;Oj@m?w_4o>8o4Pks!esRK$5!VP2Zim9g7w3B+OC zv&Gt3t+%Psv$$ zk}>5eHis^yAcZvOUnVZrNK3gun>|_cbWW~V$KWzC)wg#?feA} z^76Fl6zb~eY_`@~_-b+Ur|dS8yJ^UFQI^a5&6N;&oDO=vqELDm*y;O1DU2`yMKL^vn1^n+-uO>%UyKlrUu?__WgmM zZ@rA!XCuryMY>?KD@!~UJeE|+N8Fo8S#A}p6ueNM77*3n$wE-3xCgA|OC9i<1)a+m zdWyDkh-jc0zpGS~n!hCc;0_D+dXe?ADDv?j>zsBSRJ$JIUOP6c7`LbP)*YB@eejM} zBjN6W5phb)X^ec*EvvYP>F`ijM;ss4HCTgDvF5@zcig{kc^DiJy2KOfIbFG7yNDvN zk%gGJTtYu7j{6w6b-@Yx1F8TJyOzO-yNf(e-BYs{g5l7G$YO5cITMz@i0o^wd7E1* zFPY6PmXF%zmYO@~7g!^6tIs!^b4%tSrMZRXLX)SOUdlNSBrA(a3TWv1Vj91A=r4*K zY;ww_+lv7te@#8myw9MsWz!)3ej~Qqvx)x3lJ&z$94D()>s2H9pa&}jfJB$Xetp{ zAp3+EUr?cFE^gE6!U3SR(Y_5y2Q zBq(Maz>gglVs#WASQx7`KChQ+^l&SRRBJVQ@czFlWl-&F5U(9`LS%0-19a{k8ENh(++#umi|0k1?W@o|w5+D=Yk_Unh0}G|cncdM4?r z&lyfK3Yhq2CNrW7jHHKU%z(3_MB*Q9^PnFBY8`oJgsdHQYPS?oXo|L=0es#1rt~>UE}T*%^K{AeGf7gNeCBKAe^K z=!OxJfHshy1wI3wlZWw|zc4R28_6&D&6su)YI9%=3C$$EK18npliVz4Sb9{bp92n2 zDa8!vbUwOZcLJ4MBdCI#UoLSP7pO#b57*T?6B>$;tN4(_1eo#JH>!|l7kLxA+G;K1 zu9J70+Q-oQ86d5X43*Q}2;Zl)?o{7kh{w4BFI8@PvO~}XgC+gG&Eeo&PW$VTk=`_i zwBmH?+|MpG`pDcx!0s5pVnE7yf{WJXX6G6Bqn!Bw zZ@`;4v5v%u3+{zr4W~!PbB^U=PmB{PFBC?e08;=A9pV-jE1|nvgw+kWhva@n$m7sE z==?IRQ<M*y9S-b^_yKO{~Y4vusHgCZ(W4>|+qVz>F%1UJU%3(2;$flmZ+24zwSK=&1 zDV*FmQicFu)1WHKVd%C1BIB5v9%8u&3rQbdYp*x#<%QZhp_lmTUzk zzDOt5&|}J(>uMy2oHaG*-`$ptA6JJkDM=y(?wGREoA?~NB^ci1c9u!a-hgE10zPQ6 zHd7*e#)UC*+WeX*G@y0`Vhbsj4 z>>FcTcg&$C=Z-BJIX7&j$GKsL@;Eo_B%;|HS`3%PqHk(IsBxff{E6qPxnP z4K&jMYtR`Jr8{V``%PX}=lTNOQ%#p6K{h}pwoeem285XiJWjt1>NoUg2N{7P!7vfO z1RF*ahoqvEb@4w=Zs30&;NT%8Q_1XUIy_JdJ>VUX{X`)r-*k!!9oZYGuF$=^St4_2 zCAPia|37c<+SNvur-;i!R59Vk z__v$CH>?~r=&gQNA=J>{EeDfw8(1xJD2wqHL-#jZuY+S|{z4%!35h}E^d6S~TS|KY z0gvYH5byJ2-6IOL%K5ot%i#TDeENVV3RV@Mf$%ZBzB*M>X-}+Vx1{ug%HUUiS$(Hy zVz+63>Vy1j-4cR~UcT=ouCqGyNE5YUQR!A5iFTTd>yOogw_XgIswg67#K^Kh7`77? zoXpnHGb+lG$v(Rqjy;+es|BywIkJElY4o(OI2bB62d1j9j6Y(orc?bJR~}3Wl(f(7 zyq}HnLJWoLM^T8DLO?Wi(pD*OCOF#=>6CxuQaQ7#MBK{I@X*MsQZ_SY!HuIB zqzr9h7kzp(L&1`P%S_1Dpfn7&H%eWrFI-Ho-!&N8&Tfxn5X2258NlIQ|GO#)JqVrr zuA-s+PlF@EUe#!2>COX(cps@%>N8oaqEqo;(i1o^tDKNX9qD3rWKn7$%q92=O6j6zF?d2uWg_z^ zY)|Oak*}%{uZljF7Ji9iKY*H%Zg+nvHSW(p_PcFc)Y^|^-s~S8_WoA>IBg>nQ?pnb zGv%M?TFrU*#8&>!dZyrNn291fjO|j*euxi~&qrmk&qDAjV`io-cR$pAnrBloj!a(O zcmseeo<26PDwC(<@-iBbSV^(@{0tZmF9|?3Wy&{5f}Bh_V&6$(U!1GD-NduCL=B`g zxXNvxiz7Mh3xYuoRojk71Pj~RF}^_?sOZ4_^@J*R58fPpXAN^5eC#ho%QMcs6yQ5M zU}wSqm3b&nLXM&kIgjroD`Swz1Ot!RLXNUX2(G@3NA{7MGMXFyADVK*1=Vw5hPwK^ za`#*${}y;MjRe#ak6|sD8b0K;J3&n*oHNMUW|-R04&)0xRikS;3p?TO!G}Hnwb6C7 zWe71HplmLpp9mRB_>yM|j8T#;i$^0AXgrK{1QV*3&Oj-SToa{^c> zhhOvhu5nJd2|eq2KDf3_x=nIv5S}?e12Fmh^H>k&&}eO5fS|oI4{R3JCsYL_smPGBrHDx^D#AI}O{i<#N$> zxql;s%S3p5LHuBgvWp9{Lix%Mo~w9+yELxG%9@4Zv1t+kXcrft=J1i^Mfc=6SMmhz zu%AcFmBrgdMPxNL%!wiGP4xw-sieBcDo)z1m`SSWpC>G{$Qo+(3!NfZr3ECuX^{Wu z3@jwVFXn(2H+$HSaRoowR9ErBFi{&BnW%-p7ap;ZkjTVqxl?S8>oMN_jGFv5ZaG%_ zQu(yZKFD%poe=_CS=Abz)opSNOs3m%<{gUW(OQ`B>EV79^}x-WbGqz`R3%CeOLclv zK$D5*>tll$*iCf5p9$4#4haN+#_fP89ymx5Tgrn#(6u3<3eYq&ap4mx=L?VFT%gPu zG1>@$jhd7lbK8(#Xal*Ph=etC2eVN$sVNYMr4~~eQHul@EzT-&Sc7UC>@~Bj7PzgOz38|9uZKR zsro`?S^P6l*RvfoL%LWC^KDugg;Wt3g7OjKP@4%fsCpIjHUWaIUxLU>oP*vXs~(=k z3Rd6bWy`Q}ztC3LIfO$-gR45ii!Uo#=~H}s_yt6r^?f?Yvg(7 zmj#)`+(J(j@}qGD}KmX<@($s>D^RI(Y4Oxd2yjOcO_9&{b&%d$;)|SL=3g)4N<)ou_2l8f2NcUqF zUCJZ4j3Dfk@XTUyOh{SXvmW7Bd-SjqyJUFS2_6KC;97>A_=96h!QM5Lz%X ziMn&SEZjroy%4vTl;b}uD5M+_T(F|H^ zCtK7x$O!XfK{#>TX^c-Gb%B&VUR^dmTfq69_xm{Q#w<8Ovh=!N9o z;5r?%|4<-HQyp(+L7yDUY||N8T*uGc9MBfUucaE%!VqT|lR-6YyxTNL%n9>!g*k9u z>HbdtC^!Oe^Hp4~*xdfk&Xg}jM89Cy6V2^oKf)9ga7wXL8tdO{j^6V)d!~B&eXh$%yFTeS>zZdM16lleu;xPN3@b9T&ah77Sxbl@5 zh^PmAoO?tL4#ZTZN_Axgecciq!TQ+ zWw%397|LK3`nS>wD&*2jg?udSfG|}isxEaqjT>bjNFQY;r};tQE1n~38^Z=mZK*8S z7B_)UB?_#bmES0UPJXVCOyw5$ucmEoZ&G_MblCL0W{ewbUGkK2fv}|GTqW0wkF%gm z6ST_#tX_CGvmiFx2?@*3(A>>f7EiF8qkLSXh<6G!5K+RD+gA41`~`$|bpu?E0}OZj z*qm=rr%_B)K1o=`aH}iX2yyB4DcE8aIBQO&uE^`jH$^_mi9x^n*zFx|6HTx!CRX&G ztB?npl2&H&vtO1>Ws`qQie$$KUUA18jnC2XaS-M}JITGwb{I8jEZ-#kiv`m2F)!H- z79iNfEi&NzeXy7F!r#>K{NndxwvADrhJ(m7DY6Y)k5drI0e@(ih-ed{<)R#21B0;f zMRsvIa$|dP3!V%_S7c?+31BimGrg~^z-&q9EdKfH%SXkl;5Hq);?W=79#!b<*Lp(e z$nDw1hv#mq7<|f@-%xb>VT{8$-V;UYHje}b%TZM2QnBlsuZ!8yjR*Qxw6Jl;W@mIL z1VeSC0>G>|xUf%|jk>vreK$A0+|^X{w0=e*FL{f>OnwSrI`Q((h$p@eNri$GHq_UX z4V=%GX5gL?mWTKeAAPyZB9OqsuLM!7%;wt0t8#b&a3P6dto0+Cv=~3sn2bhULzV4j z-PkDzWjJT!^iK9Xlm)veoizM`{G7u%AGwuaMt|5SX|j(I z4QUBX9X|y~tO~nFcyqLke+1houV6RIKYVN0ZlCcvoukr3_d>KLLkz2>X1X2Gd~rio z5`}q6+Djqg=Iicm5B9hEM;#)gP=n)BBJA<{UP|i5%U{o9T*2^)V!Og1n`0&dB^&ZkZ5L;sWK;P(hSS*{RKqQvJQH51pql z%4RHqd;(Dt$mw`et#ihD6(gE8W&HxoXDpkTveJyfk%^tTzd4ir6Gq`Lga0>av7rCPJ&TOXAj5D) z^MGmU8DgA#N=n-pcXn0{0*0PXxgcL6o^_@rDWNLENTOpcEl3*Y!+fger!&~vl0`S>q6%giRu`r|nfmte<>><&E>M5Jz1L{7-6%zne!KYeFNo4*1juTk@#6t!wME6N2j zDOqM(+HzNhdLkwdu^S0hik5lDlHDQ-STG|==RM|TLae4;cIp7j|(Ha|ZVP!)@9{nL%q>ZXYf!1Zh5PdEp>pObdl?$sLWa zTb|6sPx(gJW_3^54mp%Dupms`&i)bq^vN}FFzx?bBSClqd) z17*gycAFG3dog~235XIvT>@MPZjk*W62n0CgP6qtI~I2Ug%SzXi|e|5>Zx>CJngik z;&KmmwvM(YyYSJ(H`Ve5YFg*swFhrL;uraLu)EVIYDMJe^Y#jOZ}Yng(CdL0>g^1+ z+U-Gq_y2Tist-F zO^|mx?>A#;voE9LY=3|E&{JsT=e*TnPtyMj4{1bRYe%^>?rLWm>sjTvSx+Lz-K5?*3nQ+;pOwn&>3czYj;Ouj~o=<$-$ zCag%_PAKG)*nB|lE;T8+{@!o(u;}Y**I{`Tc(_QC@{JcuEN?_Y6yL03ZK3L9EQYy= zRxZ>TSw}vMk$HyI5s?mIPBcyB4z1z&T745YUEUzL;GOQ6e}W9sg+Gc-)5QMs=HY_8 z3FTT=Hmdcm5G17xLvr*`uV_>Zr_K^F3D;xGm)@|L@z_UUaSqSmy?AdP@%n8`kA5ul z?)dIf5?n+nPT+qGAKdR8G?A%?YIpeJNQ-9!sw)r3UI4lKwc#j7vPsyn#b>X09fvsM z$2bV%47w635RN;9_m*8@lv)gozt2fjdL#u$V?jvPtW|8>jz>g&-+VTXAs~Njbo;xq z4ZsHxdJLNtvV(dMle28EziT_vY=oF|YCv-re{61mgw((lDK{UV&{pemiG!q|p4oI; zlsB+V!Z%xNt8{ajSU9ELbOFiQrHer6dsqwS7gZ_CVa@FEJ7=<*O*Ef`vB9O7EIdhj zpo~HHJLm|EQAdNtCB-T!=ES*7QL3~FO1|43kXdlSHYbk1_v7a?Y5_hWlIftbg)OIc zfE29?{_5h&jC_JM?w@1>Ozf<+Ki6+X9DR znGu#lnd@iQY;>Pxbx_8)- zI!=(>SP1UCNfx&BI^P`2gz>Ugc4Prgb?Sz5Na7v%WOWpaUVL|BG$7t>gJaW?vnUZI zAS7g~E0uSug@)BS^$z-Fo$*~$^rC$^JPx^cb2^im9IJDf{gF`>t0@ps^zdI@zBnZISe_UBbzo~sIkXp2#F7vx$@AQ zwldo2JpG%K5l9sxL8~xK_BlnF!q2A-#88B-V4pQ4n$n(bTwLCqgm=IY&Ek%|GPrtp zTvD0jdAai3syt&hJ$@kT`^P-ld!fniZf^;PVB{jltD|uytkgxnR0^P)%`5T?MUX!E z2QM-80}hvB7i5Lb+DCg=$L)8AWaN=qC+viA)aqZB+3RdqDdfa`1QD4FCy+Yqa~XCJ z1gwBWL29u0`cdL_rH0TUss;IsXTx37V5ywp&#qbk^Hn^){F+yL*lrtzWu~{dv`y=` zz@SLVTXih3r}~aXEIg>-n}QdEVZ+f~{ncvekq}eww#D|^U4%9CIWh%9P&s1w<+@8I z&_I*!Oo20OX$O_ONRQlz#G0F4R9pF7Z~qWFWq<{{C(>+RY`aTBN#>Noz2q?ZbakARZd zlTV2Ei`&c><=DWz#;tu21A`iN`6F+&1s8cKyqAH{OQU1WtBc658=VJnVODkIE0bA% zp!hkUqG68Ap`>|vl^HU<5&OyC*s!Cx-iBGc@GMc|FvWbuqj*SrB+15y$RV3|UNf zwh()%3!HRwiqvs;3ZcTs)9T{3ZL76o!-?erboiE5=_E%!aOTD1U35P)O>E_ZbOGj? z*2Jr#<~uktlJnZPDc|vVxTr07JH~CUu15Rdb5+wGk%^`h(+4kR_n6_CInNF7+LBe6 z2B_b~vaQ48VY)z36&ab`izq^ry% zXM~GxZP$dS738Tom5PZ{WOl?3>_iL)wE2@%!Mc4B zpu(L)eXau}Jv5W9uilSPPdwxJ*fg7UV^1w|d2lU4-yLM^1t?bO4^;Iv6cxu>D{O(_#~eCi~?(S*Ur zo7jRxf$*dc_Yl`ox6iW8&|l*)ZG84CdlpzuuyY_)m6v;J0X_tWE|xT`W8s?v3GZ%% z_Md`V&sVG{A7;gqdJvbH;p7fvVq?R!xFb>kw#0^DUzbg?fFzd~UtL0cv7nOb`czy7 z#Ekh7y{JL6hXtNMCmr8sK+w1Y+{^j1Ppx_)I)<@TJcI^lg-Dku;p3E85C0!U2}Tm+ zt&N3P6o>?1bDijgNZg4CR=gtSA+MyX_MUehiKHM|aj3((9Ht%0g&>3eW8bV;+X+bM zmfx$QyM=#uF393?(Vb+|juiLArmMD5AzR+!0^*ZOli8nhPqMDl4O;=0Ku4mt&G8+A ze;EXx(XQD?vfjM<{Ld7U#Y`W=!lkcRv|{2mmW-isI=kV=WO%>@geg50co`W;c24F@ z@7W|OB)vAkv}xkK-5ob4Uh8n%39IABIzp+$OLA8k_$8I8%vlfel=eGE1;c?LWm5-qqM49v<=wVRwiJxfZk)_| zkoL;^>JqsvX>RLREQgHT4L1<$jx-=qNU4kPs>!tX-_V|9|Bul*zy+dUvrAH>ea~dg z@Ixbo&Fr1-vvxF@L1p?~rMidY7>SB6x{-eGiRm0aSVRZ>K!Y=v=cTBC%GkT5`r7+D zg#Ael{j|ay$}BIQgxWK(r|LSzKfC>8U4q)53#$Jl)aSWc6x7o#3TkSLsU4g)p-4)c z=qRm39Fb13eh*j(uy_mS4H=!NRKx)ta9*oe{y_!wtkX_yuW^HHjPKKp8=_a+VtG=!vcBooR z8*X{N>qZr)e81E0Z7)^e{iS7ouYPg8J6u$rQC5nFbakyY{A}R;ed?>>VA7f6>=GX5=9Ii}^@9lAXp?WT z9>6;?{d>Hg)tA;j1Y1^%$1+|MiPeC0?cGT7^ZKYAu#A$y}rX{B!k@fferwc|5 zjO9q2*_9~&`%516-go9J8D}WAY0SXW4OWZI-_>f#qO0&iC}_kcfvkv6)?S-QM)}=_ zF`%V)uTaKU$~JV(RUoQSFu@&{He`M^7Q<2yrmZ73e)7Cic{ak3eBbKyyD9uuXFJQ1 z@BMb4`r4g?qpkPwgm5!P6|~G@1LPcy&_!s1WH|xv5zPQ;hH%ePpc>vgwSE|z(Xq&E zTEf`TGWjI3-kHcn3TFM#N({>#iD#O(W^T>%6pU*sLr^H^(=DY1P?XPK0CuYeeArWlLDREVx(jV@+HSXs#oX*q1q^|2>@n)0+iBB>tlYlh>>5thaJO^^^DXbfY*ys`YokF}LNe&MAG zEJGycU9{IGL^6f99?KRK1M9bd@CE?1(B&Pa19~$v?iWD>jGWSBy)XG ztoF)l3ss)?(ZwsF_okBbzDd^v}Z-2n>e6iC2jt=B+=P|ikS^M9HcY(U|Hnv^HIcN z?KfNd0SzT?4dScg91klE`LqVVbQ^xq|2W#(nvAZb!euEDMmBc$C1+06sVK6JZ%4Ky ze8fRt5d0(FR(dRb^l${mJ0nCjo;QY$M79cVs}vVjFwjyD`%T4$9y2p>nG@{(XpFQP9Fl zeiyU{WXRKeOs(1?00wjK*^ms&O%}B?r#m+oH3UC zGKh0S94Ri_vo@hX6@F<1`Sr%H41GvKiDu_+D+mu?y!Tw^IQ5GflxGZ}E0q-Ena{m;_u&DZcvZ{Zm z2JV(t_;$n_)E z8K#3SofK8Wo9@P^LFd!pp#6TZv(-Bon7N>S$+#-J-GF}MAYq3KRDvz&0ihto=_fXG zEFBhFx%BMBv<(>y;po}Hr>uAI_P1w`e@BgTI^5(wF(TR5C$i$@I|LY6k#J1{t{F$HO@^4+^h{G=vCcjpN%F&;ViFu7xJ(S;Q#oBcJeF=avE|s943XF;xypQT zG5;AsCpsA#b}N`t)1SZI5t7tY7`A+R^4_=azTp=h?7m=OYsR#*za=Jm#+X7+62QrE zXPG{SLF5ftxaN)Q`Z2`iHD=j@X~k%Sl`ch^Hgm=Lf7^ee$B4m!X&>dbxa4c}q32&h z!jJEPcwtN_fvzsJcFF-+suSsmuc}V9%9p2F+eOVGj8Y7D!;EJSmDch)!)k;9k}I-} zrhw3$zJzrvW1#-Ix`u2;BI(7=&;7tdPbAwuiDg}wfQx{H1jjNeS~C`*^s-FZ`av=A zet)Y?9IZEpMvW(@upX{DqPXMIN*TxK{2M&JB+*Y)T!Jksk0I9^caOvZJHLtHrSoUm z7Gqe=Et<$}VSwwat%92SMK7oOI;rDU3w^c;_^{P?vv^Km<%x|jH6LT|USyA_=cYq4 zbx7&h5TTz|<>a@gYb24e2iN)7CL*E-c!Wvd6gJ+W)V6iG-#OUH1Y9$m(zmDE#-}7* z)xIgFbQM+{@Tbl0_Q{=Pf~lw0;mZu# zISpbut+o=iq&j?gszB1nV&$eIRYz^r#fPSN8_j808z>l14ieCldQFT%e6eQ0p7d6@ojsW@xe z|C^Ux{`5m*8^?w*c0Ss(2<9x?YkVLLZ1^d0$#^WM5Q_s&rIKLUq@Lg#+#uaESr)Dl0-C+s_}*as8xt0_x>f<^W0FiNVyvF?S>AlLVn|}M*+L*N_u3sl zj;=5>c55MK@&YG_;o~W8$&zBsTa6T13d?QtZFLo0TU#^roi8*7Y7qfe*VnE7gPW5Z zt_I}tq=MrU8Fd~r$Y3t5``vZ6ee_bPgQF4C!qaF8_jSicAerpaBiWVb9Z1xTsQrNu zOOrfzVRdo8|CXo(*U*Zh*kAve?;d{o-udfakvW$O#F>${rYJmn^Ys*6PFv56X>zOb zd5Z^!+sO)IU)Jz%x1`A@_tqz94ZG6)VSI>*_oWO*I7tX#7`N*zM{@OXS2B9DzYY{k zE^9C3aW1N(l&gX=kM8ULz@C|F1v<^Ty~kFm01vMntHZW3A#P1cFE#1UzT4vLD9!-8 z(1?@}9I=Bn(p%(zWeAt<&jiUU`A8-{_@>@3wKeIQtb_37<;_>}sxd``HT&H?fMzr+ zKARW=aG5W%hOlhpNf~%D3q7`jn|JU+VQN5{jBSfdhe#gz8@E&8#qk}UlYifs6sX(%B+ROyG)*5!Q-RlI4i$cID7Ds zMG$Fow4A-*G;2ms>H+^L=XqdUFCYITwN3J<;S`U+Bd1N@{E4Z*Dgg9o4rkHv#aiu{ zS(F_2*@^d%_Hp`uMoVwko1>Q4f#nt0tI*e`$;%|Ok(sO|q^ba~A{S;a$I=Z1z5|Oo zVacw8K~eK6*aTyzguPN`t6%=|c6}gJ7!04C-99^$ zFu3%bVHUQT{pH!%blbnb`{xf0DR>PxC0Bf&_H=Vu2ZW8Y*o2W0i_uU#L5`Wun(mVG z3pM&r)nY`f$wFsES01x+Lu;r8tfAaRr5$wO!J_FWYzC4^)EzyPm_+}!BH(Es?0^0z z>lo0=7ud`}3XwjjFJJJmY?YxJspxz<^6vv)rT;y_z;BYY&r&HuM30 z9B)5LO;EF7$pxJStAcj23d9j}ad+&D-AqE70!}5{wuMmasL9;ox!NR?iA3CniT__R zwZ$;UF@D3twV{CN3~gd_@uyji!R0LEv%F~Z>3MY_lUXe{$5|%lzl8EU1Gj5aM}saSyRyqi!Sm!`!mhxHh6QsFr~ev z=gps`g?XO+=kulPNBR}IWgRhn86TeWUupZS5^K!ntuIO~>ZM_^Wt@&X(KohdA<}r$ zwhQxer_h4pnH{**R}c!{WWK63+w3DL+|;gXFDhk4EqJw0tQK~1bV3Liad>{N)0xiN zIRvTJ-KhM}1oF&?19zJCxG0Xyysr2GM;I*?deXcI{;ExE4AJ{5Y?z}n&S)Ow1gN++joH;1Y;)3#aM zK2E{xBhygSRj^Dc!cFKI?uPja=Hm(`M83XxSjG~`ZLOFUQ9MjJ zikDV>yuD$~Q5?McLVR5=ur@VEu?NOSiRM1sarFsb<7U>$Z1==C@x>qm!5Us;%T7m~uyYoN+9L^eRkb;|D?Uwd*;MwHup1xXVnUgs;dE0?kV^9+480Fa1W$(e z^ZaIDwm3Y!jN7Kd^}PWItrI3qgfc@i$)V^QU$$2^X|ZZGAUT#G$;j&1*X&kzHnScFds$ib}ul|0hE-r3T*&JjLzf}7>B7D(@oW;tc2$)ZgE@Sd5aj8 z)`!3_T67aT+bWDv%e5Cp$z|+b%5|^5_NsK4>KxQz%4QBiz@Yo)T)V3Bm{(q|NTw`f z6LAR;I|9~PcR+Q1>P`|P1De5Nn89+#(__()%4zb3~mJ4C5`pwnXCWQ$P>*jSH9>VfjYpbLZ%qv(`bhUER57+P*zh~BX z+LnUv8K?rDkOEABp@LvS)_ppf2oj{a5&#KYhzu9xv}^ zqe*r#X5JF0*DLmgm(9!Lli_7!=ctd|%7i0@^C+oEZxOJ!;X(*|Ad*+~!DUs)7P&fK zQ@U|R?1a6Iz-90ocvSJzlP&G9o>uWZ-sNE(Mjjy}s=pbYGm{a%~k%6%X3ibfZYt18NkC@uWNK z!E#G@)AHgG`)uhL&K|SBWDAeYpTFEfGtZpi$o^BFQ3yKO3zTE0& za@zb^PmNEzA3DEfxLz7FpZz^E7zMlK>x-<@C)8#Cz~u*Gc0u<$DK>%)^7j<){A#vB zK}GXK$?s}Zz{ocUo2OI{&`RfW7@C-xe8x6cm&l|s(XjLW+P%&OhB&;EVobnttvtE> zU6(%QT)%LV&%WT9pdbr6|2&ed-7nT3;rvR?udlFS#?7B^ZV@m1SN88V*NVsH!RrRR zZ#+U`FVw)&Wb58jvHKG}{m>qK=#$50YkRLV*xK3YHA-*f0@5I6cm<~PCc+%Olh^5> zbD*ol(xAQ9Q_0F(oU13_dXFs%6;kZKCvxOm+@NXZxTw^(T-(YrA=D>ZvEx}Y2Z31K zzzA5CwU0g>kz8YEw`T`Q_}pr-p_zxyuWfQz9K6|mtEabnhufCa!d?{Efdaymkg@J1 zs88{Abp-`v^0>iXq~6AdZhLZ}dP6x@Z~_KJQ_x=N`t|iQ`js>DwK$gPy~)8sg47bS zEFtMC+FRKmBGm*s#`w`_lyjNrm@qtY=Qu{TBYsRK?xUORbAe)Mroda8ivUG5fuZpE z*u$h!sIqNjKkOAoP=j#>-%k)G){Docx2-F=W2q-25^+!!S?5D&2EqXVG zyGRO&jCg$8ux;rR0kvHCa*GgY;mZvZBDv0s?BAih{CjhEHoPl+GJ#J(pXDV`vsk~? z7D~X0_3M|`3K0u--RG`=x7!Psql|(CI@O21;RLw?-F_9ZhyGv#YGQI@SU zqijuo(rC2I0p;N}AUCFuukRmiuRdmcvoM?pen)p#${C8fX`(*`tmMRa1KR0;Eby1z zy*;HU{6z#lJWm#{^p2US_6~|qX{-i&he@5)w+Sw;ED;gz8%MDMd=3{34|U?} zEn9v$`^#S-9548vbxC4|;r)vkmq}7Oo0;yrzx5m4!^6D>A=b0T`+ldFG_;0juaRYc z!NYL)b4vTMhIy@EjAUHk7{#hxoXqe_8GS@J#nW>6O8`!9Z;9A1ne5g1UHB!rmcPTr z>C2!1xmOC_0c^)uX@FqIlZu$X-JMTFqC}(ri~hWoyo7!PgmTnElU06UkBnH!s)}y! zc6YO%>{Itw)#bbqeKP;vC*l73*HUeCjR3&e$0e&E`?&0XwcGWf2DP^QY%*6SnZ!^7 z%0}(IKx0eH;RgD!3G75n`(-qy1|3jEsS-XUD@wrWl!YL`amuY&ykj*n^*o*bH1!Z_ z0p3s3gp?`t;b=TStkhb-kL2RIbTX|IacN?t?>?fi;m`z?Ia2Yu5&dTE^HYQ%oT_PP zEH)o}HfpB*-A4t$Q7U$?@fgF;yjE%6FOT7W&qCa|Fa*U-F9xu@!IgEQi^8Y>OMO&T z|56VTEiGd_favDu|H@z;m?n?o_wWCnE!g`%BZ|No^SOc<1^~iINl}#yw6#X;DGf9$ z`O1{tXlx?bS4JxFiAlX`oLSIX$vvmvCG;dVwDPQREl)A0tyi4%bi|l6X+!^#L4Hhb z#-VwN-4Bsg&2s2`>A77%xJA}1g4^7U{;3_|44?8RJH1O=E3rT1b$O-6HBEjOK-34T zl@@?O5(IN(i)`N~aNwOJUurtROCwKA&{% zof9Fv^k4fmV@EXbYECrz!;W(^N}-9kUx$`_0?Qu~5$MS9~qX&@|69KX}_Y(6y?+onNvhns(do5N^q6PdqAoHO30N&ag(hZHPt(NGlG@Jqev2| zn#MZ6j;f7S2SXZ~i9Xh}=B(cA);@G=TbrnUE1bpb+*{0M$2wZLjqxw)_zztK_~E?a zH*8`u^>QVhsII=Zs<~!HK6GiOyu9VreT|rZo+kXRsUz$h8#T;~!x6i%uuNbzSi%;R z^JTV*e69A=f7Z$4xZ+44H_?7jZIAvLoUsj;jjJ>parfgCBSM-76&BNJQaNM_$iNBb zd^hBj=44GG1+(0S z(#l+-4IYz#!1~(^T7v>SXQi52CabLOqx3lA$=T3?%W27-Bgh#de(dIsTZKvOWSte| zb;U_pV2NL?(<3s)u@%J~{BdEyuaBIK$mTiLmmbgkKW>%wlWXNQ1 zsGb`ABYZmHjXivPkk!H(tT(#vw&9XjAIWcunB0=Gi9z&B-B+$Nm_te;QVX|@$WtQZ zmqxgZL%WjrN1XjjXYXsv8MuTq#{}9?Ky|iZ3ly>oL2U`OVi(?`CGcxjJ6+w1lC)S$ zCXJHm47zK}-DH?NRQ8Xvp?M_BBU++uHWqHt3(vFv{ol+VRu`UY2BLc5EOSpOUagTf zqWvq#-^Y2~jL@{#!G?P9)oI`?u{Xc@s)fXf(qToiPrr3Le-Dv~UUE3-^259K?&QnP z(fjz)IuNTUbe9@Bb<1sN(R1XODmrNnYhz35<7=PiCy`u%>iw=6m+C`^JZ$P@buAp} zn%U9K+3`w~G1)$|Wmb-s(-Cu>(2(MV#p`H_dv%r1)z#>x8G7L*NGe0{-LPI(2XDBK zp8lSHx2FW+^TSDu8gt40zKPsN1?B7BG`D|yL{i!mW`yKm0b`8rUZ0m)Z=EDb@a|QMW^#F`kqdQ?1XbjhXnFoG_I2TiWU(QyMw9OZ_usL z-wSTGBhvy_zp)cTPfV^$Ke!bhedzqOYzHfO?CzTPH*5(As7_k++*2qup9*BiMxSi?0% z_aF;2WuHTnUx~JVHpCnjf1QkZXQKypc%skI4_U;8qh$O5hgeX0l5y1>z%_@tlL;B< ziwsY+B7^zTe{`|m5E8Tg(O^s0Sz_MIjA z!_&D-D1&21R+)2`K-+)!JtZElzqdrPh>ZVq-W9ZB6wpZKT|vs+U*5huyIcP560Pqp zvGUy|R_8C_>g(Mi`t3|?k5hd8hCEBc_9%s@9)uAv{#5~NIe-7ILYYc^Jbd$Ag&@S; z%klX8x|d}XYkzm4%i-1cOa$Pz1HOMEu#dQ!ezcw$4(EEHBYwzYS2 zu{8HOC53zgJ<$^IM~}n#$K>#TY)na8^N*=q=;<*5-+LTx-OasDd4V_Iy-s;io4+~7 zG*M{YG40=7%)3tB1_l1;m||PsHKwG{ykj~#og4RjOceN|V~TBk*O-z*PmhU>OE0@8 zm-DVug#W|)DYiA|n93cb&>UmZh`1c@T;4`3|3iiRaGpVpD(xiP&x}Gopx*Fe?(MD> z68s;U@=!KT#C&AAQfQ9Z3kLvybS*=XLl*4f1Soiegkx{XCcDZi@FII-H@%l{5|I

!JR%dh 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 GyXF5Q`0cg; diff --git a/roms/SLOF b/roms/SLOF index 3a259df244..ee03aec2c1 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 3a259df2449fc4a4e43ab5f33f0b2c66484b4bc3 +Subproject commit ee03aec2c106a699aaddd2d3dd52cbd7b7e8d544 From 6b56bb6dbce5cfa185c34c0519ab8015f30699f7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 16 Nov 2024 20:19:19 +1000 Subject: [PATCH 2547/2892] ppc/pnv/phb4: Add pervasive chiplet support to PHB4/5 Each non-core chiplet on a chip has a "pervasive chiplet" unit and its xscom register set. This adds support for PHB4/5. skiboot reads the CPLT_CONF1 register in __phb4/5_get_max_link_width(), which shows up as unimplemented xscom reads. Set a value in PCI CONF1 register's link-width field to demonstrate skiboot doing something interesting with it. In the bigger picture, it might be better to model the pervasive chiplet type as parent that each non-core chiplet model derives from. For now this is enough to get the PHB registers implemented and working for skiboot, and provides a second example (after the N1 chiplet) that will help if the design is reworked as such. Signed-off-by: Nicholas Piggin --- hw/pci-host/pnv_phb4_pec.c | 55 +++++++++++++++++++++++++++++++++- hw/ppc/pnv.c | 8 +++++ include/hw/pci-host/pnv_phb4.h | 5 ++++ include/hw/ppc/pnv_xscom.h | 4 +++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index a156839caf..cb8a7e3afa 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -197,6 +197,9 @@ static PnvPHB *pnv_pec_default_phb_realize(PnvPhb4PecState *pec, return phb; } +#define XPEC_P9_PCI_LANE_CFG PPC_BITMASK(10, 11) +#define XPEC_P10_PCI_LANE_CFG PPC_BITMASK(0, 1) + static void pnv_pec_realize(DeviceState *dev, Error **errp) { PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); @@ -211,6 +214,43 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp) pec->num_phbs = pecc->num_phbs[pec->index]; + /* Pervasive chiplet */ + object_initialize_child(OBJECT(pec), "nest-pervasive-common", + &pec->nest_pervasive, + TYPE_PNV_NEST_CHIPLET_PERVASIVE); + if (!qdev_realize(DEVICE(&pec->nest_pervasive), NULL, errp)) { + return; + } + + /* Set up pervasive chiplet registers */ + /* + * Most registers are not set up, this just sets the PCI CONF1 link-width + * field because skiboot probes it. + */ + if (pecc->version == PNV_PHB4_VERSION) { + /* + * On P9, PEC2 has configurable 1/2/3-furcation). + * Make it trifurcated (x8, x4, x4) to match pnv_pec_num_phbs. + */ + if (pec->index == 2) { + pec->nest_pervasive.control_regs.cplt_cfg1 = + SETFIELD(XPEC_P9_PCI_LANE_CFG, + pec->nest_pervasive.control_regs.cplt_cfg1, + 0b10); + } + } else if (pecc->version == PNV_PHB5_VERSION) { + /* + * On P10, both PECs are configurable 1/2/3-furcation). + * Both are trifurcated to match pnv_phb5_pec_num_stacks. + */ + pec->nest_pervasive.control_regs.cplt_cfg1 = + SETFIELD(XPEC_P10_PCI_LANE_CFG, + pec->nest_pervasive.control_regs.cplt_cfg1, + 0b10); + } else { + g_assert_not_reached(); + } + /* Create PHBs if running with defaults */ if (defaults_enabled()) { g_assert(pec->num_phbs <= MAX_PHBS_PER_PEC); @@ -290,9 +330,16 @@ static const Property pnv_pec_properties[] = { PnvChip *), }; +#define XPEC_PCI_CPLT_OFFSET 0x1000000ULL + +static uint32_t pnv_pec_xscom_cplt_base(PnvPhb4PecState *pec) +{ + return PNV9_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index; +} + static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) { - return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; + return PNV9_XSCOM_PEC_PCI_BASE + XPEC_PCI_CPLT_OFFSET * pec->index; } static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) @@ -321,6 +368,7 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, pnv_pec_properties); dc->user_creatable = false; + pecc->xscom_cplt_base = pnv_pec_xscom_cplt_base; pecc->xscom_nest_base = pnv_pec_xscom_nest_base; pecc->xscom_pci_base = pnv_pec_xscom_pci_base; pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE; @@ -349,6 +397,10 @@ static const TypeInfo pnv_pec_type_info = { /* * POWER10 definitions */ +static uint32_t pnv_phb5_pec_xscom_cplt_base(PnvPhb4PecState *pec) +{ + return PNV10_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index; +} static uint32_t pnv_phb5_pec_xscom_pci_base(PnvPhb4PecState *pec) { @@ -373,6 +425,7 @@ static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data) static const char compat[] = "ibm,power10-pbcq"; static const char stk_compat[] = "ibm,power10-phb-stack"; + pecc->xscom_cplt_base = pnv_phb5_pec_xscom_cplt_base; pecc->xscom_nest_base = pnv_phb5_pec_xscom_nest_base; pecc->xscom_pci_base = pnv_phb5_pec_xscom_pci_base; pecc->xscom_nest_size = PNV10_XSCOM_PEC_NEST_SIZE; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 87607508c7..4407b3a1a2 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1753,6 +1753,7 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) for (i = 0; i < chip->num_pecs; i++) { PnvPhb4PecState *pec = &chip9->pecs[i]; PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_cplt_base; uint32_t pec_nest_base; uint32_t pec_pci_base; @@ -1765,9 +1766,12 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) return; } + pec_cplt_base = pecc->xscom_cplt_base(pec); pec_nest_base = pecc->xscom_nest_base(pec); pec_pci_base = pecc->xscom_pci_base(pec); + pnv_xscom_add_subregion(chip, pec_cplt_base, + &pec->nest_pervasive.xscom_ctrl_regs_mr); pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); } @@ -2027,6 +2031,7 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp) for (i = 0; i < chip->num_pecs; i++) { PnvPhb4PecState *pec = &chip10->pecs[i]; PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_cplt_base; uint32_t pec_nest_base; uint32_t pec_pci_base; @@ -2039,9 +2044,12 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp) return; } + pec_cplt_base = pecc->xscom_cplt_base(pec); pec_nest_base = pecc->xscom_nest_base(pec); pec_pci_base = pecc->xscom_pci_base(pec); + pnv_xscom_add_subregion(chip, pec_cplt_base, + &pec->nest_pervasive.xscom_ctrl_regs_mr); pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); } diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 8abee78e4d..8a80c0c667 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -13,6 +13,7 @@ #include "hw/pci-host/pnv_phb.h" #include "hw/pci/pci_bus.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_nest_pervasive.h" #include "hw/ppc/xive.h" #include "qom/object.h" @@ -174,6 +175,9 @@ struct PnvPhb4PecState { uint32_t index; uint32_t chip_id; + /* Pervasive chiplet control */ + PnvNestChipletPervasive nest_pervasive; + /* Nest registers, excuding per-stack */ #define PHB4_PEC_NEST_REGS_COUNT 0xf uint64_t nest_regs[PHB4_PEC_NEST_REGS_COUNT]; @@ -196,6 +200,7 @@ struct PnvPhb4PecState { struct PnvPhb4PecClass { DeviceClass parent_class; + uint32_t (*xscom_cplt_base)(PnvPhb4PecState *pec); uint32_t (*xscom_nest_base)(PnvPhb4PecState *pec); uint32_t xscom_nest_size; uint32_t (*xscom_pci_base)(PnvPhb4PecState *pec); diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 648388a599..a927aea1c0 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -126,6 +126,8 @@ struct PnvXScomInterfaceClass { #define PNV9_XSCOM_PEC_PCI_BASE 0xd010800 #define PNV9_XSCOM_PEC_PCI_SIZE 0x200 +#define PNV9_XSCOM_PEC_NEST_CPLT_BASE 0x0d000000 + /* XSCOM PCI "pass-through" window to PHB SCOM */ #define PNV9_XSCOM_PEC_PCI_STK0 0x100 #define PNV9_XSCOM_PEC_PCI_STK1 0x140 @@ -197,6 +199,8 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PEC_NEST_BASE 0x3011800 /* index goes downwards ... */ #define PNV10_XSCOM_PEC_NEST_SIZE 0x100 +#define PNV10_XSCOM_PEC_NEST_CPLT_BASE 0x08000000 + #define PNV10_XSCOM_PEC_PCI_BASE 0x8010800 /* index goes upwards ... */ #define PNV10_XSCOM_PEC_PCI_SIZE 0x200 From f24ff35af9b242163ac0d209a70240f13fd9f163 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Dec 2024 23:16:35 +1000 Subject: [PATCH 2548/2892] ppc/pnv/homer: Fix OCC registers The HOMER OCC registers seem to have bitrotted and fail for various reasons on powernv8, 9, and 10. The major problems are that POWER8 has the wrong version value and its pstate ordering is incorrect. POWER9/10 have not set the OCC state to active. Non-zero chips are also set to OCC slaves for POWER9/10. Unfortunately skiboot has also bitrotted and requires fixes that are not yet in the bios files to run. With a patched skiboot, before this change, powernv9/10 report: [ 0.262050394,3] OCC: Chip: 0: OCC not active [ 0.262128603,3] OCC: Initialization on all chips did not complete(timed out) powernv8 reports: [ 0.173572100,3] OCC: Unknown OCC-OPAL interface version. [ 0.173812059,3] OCC: Initialization on all chips did not complete(timed out) After this patch, all report: [ 0.176815668,5] OCC: All Chip Rdy after 0 ms Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_homer.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index a1d83c8149..acd2f7b3a6 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -70,21 +70,24 @@ static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, PnvHomer *homer = PNV_HOMER(opaque); switch (addr) { - case PNV8_OCC_PSTATE_VERSION: - case PNV8_OCC_PSTATE_MIN: - case PNV8_OCC_PSTATE_ID_ZERO: - return 0; case PNV8_OCC_PSTATE_VALID: + return 1; case PNV8_OCC_PSTATE_THROTTLE: + return 0; + case PNV8_OCC_PSTATE_VERSION: + return 0x02; + case PNV8_OCC_PSTATE_MIN: + return -2; case PNV8_OCC_PSTATE_NOM: case PNV8_OCC_PSTATE_TURBO: - case PNV8_OCC_PSTATE_ID_ONE: + return -1; + case PNV8_OCC_PSTATE_ULTRA_TURBO: + return 0; + case PNV8_OCC_PSTATE_ID_ZERO: + return 0; case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER: case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER: return 1; - case PNV8_OCC_PSTATE_ULTRA_TURBO: - case PNV8_OCC_PSTATE_ID_TWO: - return 2; case PNV8_OCC_PSTATE_DATA: return 0x1000000000000000; /* P8 frequency for 0, 1, and 2 pstates */ @@ -92,6 +95,10 @@ static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, case PNV8_OCC_PSTATE_ONE_FREQUENCY: case PNV8_OCC_PSTATE_TWO_FREQUENCY: return 3000; + case PNV8_OCC_PSTATE_ID_ONE: + return -1; + case PNV8_OCC_PSTATE_ID_TWO: + return -2; } /* pstate table core max array */ if (core_max_array(homer, addr)) { @@ -192,11 +199,12 @@ static const TypeInfo pnv_homer_power8_type_info = { /* P9 Pstate table */ +#define PNV9_OCC_PSTATE_VALID 0xe2000 #define PNV9_OCC_PSTATE_ID_ZERO 0xe2018 #define PNV9_OCC_PSTATE_ID_ONE 0xe2020 #define PNV9_OCC_PSTATE_ID_TWO 0xe2028 #define PNV9_OCC_PSTATE_DATA 0xe2000 -#define PNV9_OCC_PSTATE_DATA_AREA 0xe2008 +#define PNV9_OCC_PSTATE_MINOR_VERSION 0xe2008 #define PNV9_OCC_PSTATE_MIN 0xe2003 #define PNV9_OCC_PSTATE_NOM 0xe2004 #define PNV9_OCC_PSTATE_TURBO 0xe2005 @@ -211,7 +219,7 @@ static const TypeInfo pnv_homer_power8_type_info = { #define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c #define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002 #define PNV9_CORE_MAX_BASE 0xe2819 - +#define PNV9_DYNAMIC_DATA_STATE 0xe2b80 static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, unsigned size) @@ -219,11 +227,17 @@ static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, PnvHomer *homer = PNV_HOMER(opaque); switch (addr) { + case PNV9_OCC_PSTATE_VALID: + return 1; case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO: case PNV9_OCC_PSTATE_ID_ZERO: return 0; - case PNV9_OCC_PSTATE_DATA: case PNV9_OCC_ROLE_MASTER_OR_SLAVE: + if (homer->chip->chip_id == 0) { + return 0x1; /* master */ + } else { + return 0x0; /* slave */ + } case PNV9_OCC_PSTATE_NOM: case PNV9_OCC_PSTATE_TURBO: case PNV9_OCC_PSTATE_ID_ONE: @@ -241,10 +255,13 @@ static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, return 3000; case PNV9_OCC_PSTATE_MAJOR_VERSION: return 0x90; + case PNV9_OCC_PSTATE_MINOR_VERSION: + return 0x01; case PNV9_CHIP_HOMER_BASE: - case PNV9_OCC_PSTATE_DATA_AREA: case PNV9_CHIP_HOMER_IMAGE_POINTER: return 0x1000000000000000; + case PNV9_DYNAMIC_DATA_STATE: + return 0x03; /* active */ } /* pstate table core max array */ if (core_max_array(homer, addr)) { From 634cf61e463a8020cb8eb835fae4e2939f387975 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 10 Dec 2024 12:08:35 +1000 Subject: [PATCH 2549/2892] ppc/pnv/homer: Make dummy reads return 0 HOMER memory implements some dummy registers that return a nonsense value to satisfy skiboot accesses caused by "SLW" init and register save/restore programming that has never worked under QEMU: [ 0.265000943,3] SLW: Failed to set HRMOR for CPU 0,RC=0x1 [ 0.265356988,3] Disabling deep stop states To simplify a later change to implement HOMER as a RAM area, make these return zero, which has the same result. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_homer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index acd2f7b3a6..75b0ee7964 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -89,7 +89,7 @@ static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER: return 1; case PNV8_OCC_PSTATE_DATA: - return 0x1000000000000000; + return 0; /* P8 frequency for 0, 1, and 2 pstates */ case PNV8_OCC_PSTATE_ZERO_FREQUENCY: case PNV8_OCC_PSTATE_ONE_FREQUENCY: @@ -259,7 +259,7 @@ static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, return 0x01; case PNV9_CHIP_HOMER_BASE: case PNV9_CHIP_HOMER_IMAGE_POINTER: - return 0x1000000000000000; + return 0; case PNV9_DYNAMIC_DATA_STATE: return 0x03; /* active */ } From 29c041ca7f8d6910c894788482efff892789dcd2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Dec 2024 19:55:58 +1000 Subject: [PATCH 2550/2892] ppc/pnv/occ: Fix common area sensor offsets The commit to fix the OCC common area sensor mappings didn't update the register offsets to match. Before this change, skiboot reports: [ 0.347100086,3] OCC: Chip 0 sensor data invalid Afterward, there is no error and the sensor_groups directory appears under /sys/firmware/opal/. The SLW_IMAGE_BASE address looks like a workaround to intercept firmware memory accesses, but that does not seem to be required now (and would have been broken by the OCC common area region mapping change anyway). So it can be removed. Fixes: 3a1b70b66b5cb4 ("ppc/pnv: Fix OCC common area region mapping") Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_occ.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 48123ceae1..c6681a035a 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -32,22 +32,21 @@ #define OCB_OCI_OCCMISC_OR 0x4022 /* OCC sensors */ -#define OCC_SENSOR_DATA_BLOCK_OFFSET 0x580000 -#define OCC_SENSOR_DATA_VALID 0x580001 -#define OCC_SENSOR_DATA_VERSION 0x580002 -#define OCC_SENSOR_DATA_READING_VERSION 0x580004 -#define OCC_SENSOR_DATA_NR_SENSORS 0x580008 -#define OCC_SENSOR_DATA_NAMES_OFFSET 0x580010 -#define OCC_SENSOR_DATA_READING_PING_OFFSET 0x580014 -#define OCC_SENSOR_DATA_READING_PONG_OFFSET 0x58000c -#define OCC_SENSOR_DATA_NAME_LENGTH 0x58000d -#define OCC_SENSOR_NAME_STRUCTURE_TYPE 0x580023 -#define OCC_SENSOR_LOC_CORE 0x580022 -#define OCC_SENSOR_LOC_GPU 0x580020 -#define OCC_SENSOR_TYPE_POWER 0x580003 -#define OCC_SENSOR_NAME 0x580005 -#define HWMON_SENSORS_MASK 0x58001e -#define SLW_IMAGE_BASE 0x0 +#define OCC_SENSOR_DATA_BLOCK_OFFSET 0x0000 +#define OCC_SENSOR_DATA_VALID 0x0001 +#define OCC_SENSOR_DATA_VERSION 0x0002 +#define OCC_SENSOR_DATA_READING_VERSION 0x0004 +#define OCC_SENSOR_DATA_NR_SENSORS 0x0008 +#define OCC_SENSOR_DATA_NAMES_OFFSET 0x0010 +#define OCC_SENSOR_DATA_READING_PING_OFFSET 0x0014 +#define OCC_SENSOR_DATA_READING_PONG_OFFSET 0x000c +#define OCC_SENSOR_DATA_NAME_LENGTH 0x000d +#define OCC_SENSOR_NAME_STRUCTURE_TYPE 0x0023 +#define OCC_SENSOR_LOC_CORE 0x0022 +#define OCC_SENSOR_LOC_GPU 0x0020 +#define OCC_SENSOR_TYPE_POWER 0x0003 +#define OCC_SENSOR_NAME 0x0005 +#define HWMON_SENSORS_MASK 0x001e static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) { @@ -129,8 +128,6 @@ static uint64_t pnv_occ_common_area_read(void *opaque, hwaddr addr, case HWMON_SENSORS_MASK: case OCC_SENSOR_LOC_GPU: return 0x8e00; - case SLW_IMAGE_BASE: - return 0x1000000000000000; } return 0; } From 2935a3fb03e0e835ab13dfa67ea6ad475edb5665 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Dec 2024 18:47:20 +1000 Subject: [PATCH 2551/2892] ppc/pnv/homer: class-based base and size Put HOMER memory region base and size into the class, to allow more code-reuse between different machines in later changes. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_homer.c | 46 +++++++++++++++++++++++++++----------- include/hw/ppc/pnv.h | 6 ++--- include/hw/ppc/pnv_homer.h | 7 +++++- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index 75b0ee7964..67a1fd77ba 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -138,16 +138,16 @@ static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr, unsigned size) { PnvHomer *homer = PNV_HOMER(opaque); - PnvChip *chip = homer->chip; + PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); uint32_t reg = addr >> 3; uint64_t val = 0; switch (reg) { case PBA_BAR0: - val = PNV_HOMER_BASE(chip); + val = homer->base; break; case PBA_BARMASK0: /* P8 homer region mask */ - val = (PNV_HOMER_SIZE - 1) & 0x300000; + val = (hmrc->size - 1) & 0x300000; break; case PBA_BAR3: /* P8 occ common area */ val = PNV_OCC_COMMON_AREA_BASE; @@ -179,13 +179,19 @@ static const MemoryRegionOps pnv_homer_power8_pba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static hwaddr pnv_homer_power8_get_base(PnvChip *chip) +{ + return PNV_HOMER_BASE(chip); +} + static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->get_base = pnv_homer_power8_get_base; + homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power8_pba_ops; - homer->homer_size = PNV_HOMER_SIZE; homer->homer_ops = &pnv_power8_homer_ops; homer->core_max_base = PNV8_CORE_MAX_BASE; } @@ -291,16 +297,16 @@ static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr, unsigned size) { PnvHomer *homer = PNV_HOMER(opaque); - PnvChip *chip = homer->chip; + PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); uint32_t reg = addr >> 3; uint64_t val = 0; switch (reg) { case PBA_BAR0: - val = PNV9_HOMER_BASE(chip); + val = homer->base; break; case PBA_BARMASK0: /* P9 homer region mask */ - val = (PNV9_HOMER_SIZE - 1) & 0x300000; + val = (hmrc->size - 1) & 0x300000; break; case PBA_BAR2: /* P9 occ common area */ val = PNV9_OCC_COMMON_AREA_BASE; @@ -332,13 +338,19 @@ static const MemoryRegionOps pnv_homer_power9_pba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static hwaddr pnv_homer_power9_get_base(PnvChip *chip) +{ + return PNV9_HOMER_BASE(chip); +} + static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->get_base = pnv_homer_power9_get_base; + homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV9_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power9_pba_ops; - homer->homer_size = PNV9_HOMER_SIZE; homer->homer_ops = &pnv_power9_homer_ops; homer->core_max_base = PNV9_CORE_MAX_BASE; } @@ -354,16 +366,16 @@ static uint64_t pnv_homer_power10_pba_read(void *opaque, hwaddr addr, unsigned size) { PnvHomer *homer = PNV_HOMER(opaque); - PnvChip *chip = homer->chip; + PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); uint32_t reg = addr >> 3; uint64_t val = 0; switch (reg) { case PBA_BAR0: - val = PNV10_HOMER_BASE(chip); + val = homer->base; break; case PBA_BARMASK0: /* P10 homer region mask */ - val = (PNV10_HOMER_SIZE - 1) & 0x300000; + val = (hmrc->size - 1) & 0x300000; break; case PBA_BAR2: /* P10 occ common area */ val = PNV10_OCC_COMMON_AREA_BASE; @@ -395,13 +407,19 @@ static const MemoryRegionOps pnv_homer_power10_pba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static hwaddr pnv_homer_power10_get_base(PnvChip *chip) +{ + return PNV10_HOMER_BASE(chip); +} + static void pnv_homer_power10_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->get_base = pnv_homer_power10_get_base; + homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV10_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power10_pba_ops; - homer->homer_size = PNV10_HOMER_SIZE; homer->homer_ops = &pnv_power9_homer_ops; /* TODO */ homer->core_max_base = PNV9_CORE_MAX_BASE; } @@ -424,9 +442,11 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) homer, "xscom-pba", hmrc->pba_size); /* homer region */ + homer->base = hmrc->get_base(homer->chip); + memory_region_init_io(&homer->regs, OBJECT(dev), hmrc->homer_ops, homer, "homer-main-memory", - hmrc->homer_size); + hmrc->size); } static const Property pnv_homer_properties[] = { diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index fcb6699150..d8fca079f2 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -205,9 +205,8 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV9_OCC_SENSOR_BASE(chip) (PNV9_OCC_COMMON_AREA_BASE + \ PNV_OCC_SENSOR_DATA_BLOCK_BASE((chip)->chip_id)) -#define PNV9_HOMER_SIZE 0x0000000000400000ull #define PNV9_HOMER_BASE(chip) \ - (0x203ffd800000ull + ((uint64_t)(chip)->chip_id) * PNV9_HOMER_SIZE) + (0x203ffd800000ull + ((uint64_t)(chip)->chip_id) * PNV_HOMER_SIZE) /* * POWER10 MMIO base addresses - 16TB stride per chip @@ -250,8 +249,7 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV10_OCC_SENSOR_BASE(chip) (PNV10_OCC_COMMON_AREA_BASE + \ PNV_OCC_SENSOR_DATA_BLOCK_BASE((chip)->chip_id)) -#define PNV10_HOMER_SIZE 0x0000000000400000ull #define PNV10_HOMER_BASE(chip) \ - (0x300ffd800000ll + ((uint64_t)(chip)->chip_id) * PNV10_HOMER_SIZE) + (0x300ffd800000ll + ((uint64_t)(chip)->chip_id) * PNV_HOMER_SIZE) #endif /* PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_homer.h b/include/hw/ppc/pnv_homer.h index b1c5d498dc..5ffc0c97af 100644 --- a/include/hw/ppc/pnv_homer.h +++ b/include/hw/ppc/pnv_homer.h @@ -42,15 +42,20 @@ struct PnvHomer { PnvChip *chip; MemoryRegion pba_regs; MemoryRegion regs; + hwaddr base; }; struct PnvHomerClass { DeviceClass parent_class; + /* Get base address of HOMER memory */ + hwaddr (*get_base)(PnvChip *chip); + /* Size of HOMER memory */ + int size; + int pba_size; const MemoryRegionOps *pba_ops; - int homer_size; const MemoryRegionOps *homer_ops; hwaddr core_max_base; From 84c085342f1e98bd2b277654209b6ff5ce5d2f0e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Dec 2024 20:36:55 +1000 Subject: [PATCH 2552/2892] ppc/pnv/occ: Better document OCCMISC bits Use defines for the OCCMISC register bits, and add a comment about the IRQ request bit, which QEMU may not model quite correctly. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_occ.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index c6681a035a..5424d87ee9 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -30,6 +30,7 @@ #define OCB_OCI_OCCMISC 0x4020 #define OCB_OCI_OCCMISC_AND 0x4021 #define OCB_OCI_OCCMISC_OR 0x4022 +#define OCCMISC_PSI_IRQ PPC_BIT(0) /* OCC sensors */ #define OCC_SENSOR_DATA_BLOCK_OFFSET 0x0000 @@ -50,13 +51,16 @@ static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) { - bool irq_state; - - val &= 0xffff000000000000ull; + val &= PPC_BITMASK(0, 18); /* Mask out unimplemented bits */ occ->occmisc = val; - irq_state = !!(val >> 63); - qemu_set_irq(occ->psi_irq, irq_state); + + /* + * OCCMISC IRQ bit triggers the interrupt on a 0->1 edge, but not clear + * how that is handled in PSI so it is level-triggered here, which is not + * really correct (but skiboot is okay with it). + */ + qemu_set_irq(occ->psi_irq, !!(val & OCCMISC_PSI_IRQ)); } static uint64_t pnv_occ_power8_xscom_read(void *opaque, hwaddr addr, From 70bc5c2498f464b63515984f1996031010476c25 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 17 Nov 2024 00:29:13 +1000 Subject: [PATCH 2553/2892] ppc/pnv: Make HOMER memory a RAM region The HOMER is a region of memory used by host and firmware and microconrollers. It has very little logic by itself, just some BAR registers. Users of this memory should operate on it rather than have HOMER implement them with MMIO registers, which is not the right model. This change switches the implementation of HOMER from MMIO to RAM, and moves the OCC register implementation to in-memory structure accesses performed by the OCC model. This has the downside that access to unimplemented regions of HOMER are no longer flagged. Perhaps that could be done by adding a memory region for HOMER, and ram subregions under that for each implemented part. But for now this takes the simpler approach. Note: This brings some data structure definitions from skiboot, which does not match QEMU coding style but is not changed to make comparisons and updates simpler. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv.c | 89 ++++---- hw/ppc/pnv_homer.c | 203 +---------------- hw/ppc/pnv_occ.c | 434 ++++++++++++++++++++++++++++++++++++- include/hw/ppc/pnv_homer.h | 5 +- include/hw/ppc/pnv_occ.h | 6 + roms/SLOF | 2 +- 6 files changed, 495 insertions(+), 244 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4407b3a1a2..8c0a2d0573 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1555,7 +1555,21 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) return; } + /* HOMER (must be created before OCC) */ + object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip8->homer.base, + &chip8->homer.mem); + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip8->occ), "homer", + OBJECT(&chip8->homer), &error_abort); if (!qdev_realize(DEVICE(&chip8->occ), NULL, errp)) { return; } @@ -1567,19 +1581,6 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip), &chip8->occ.sram_regs); - /* HOMER */ - object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip), - &error_abort); - if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) { - return; - } - /* Homer Xscom region */ - pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs); - - /* Homer mmio region */ - memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip), - &chip8->homer.regs); - /* PHB controllers */ for (i = 0; i < chip8->num_phbs; i++) { PnvPHB *phb = chip8->phbs[i]; @@ -1863,18 +1864,6 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV9_XSCOM_CHIPTOD_BASE, &chip9->chiptod.xscom_regs); - /* Create the simplified OCC model */ - if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) { - return; - } - pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); - qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in( - DEVICE(psi9), PSIHB9_IRQ_OCC)); - - /* OCC SRAM model */ - memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), - &chip9->occ.sram_regs); - /* SBE */ if (!qdev_realize(DEVICE(&chip9->sbe), NULL, errp)) { return; @@ -1886,7 +1875,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out(DEVICE(&chip9->sbe), 0, qdev_get_gpio_in( DEVICE(psi9), PSIHB9_IRQ_PSU)); - /* HOMER */ + /* HOMER (must be created before OCC) */ object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip), &error_abort); if (!qdev_realize(DEVICE(&chip9->homer), NULL, errp)) { @@ -1894,10 +1883,23 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) } /* Homer Xscom region */ pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip9->homer.base, + &chip9->homer.mem); - /* Homer mmio region */ - memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip), - &chip9->homer.regs); + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip9->occ), "homer", + OBJECT(&chip9->homer), &error_abort); + if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in( + DEVICE(psi9), PSIHB9_IRQ_OCC)); + + /* OCC SRAM model */ + memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), + &chip9->occ.sram_regs); /* PEC PHBs */ pnv_chip_power9_pec_realize(chip, &local_err); @@ -2144,7 +2146,22 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV10_XSCOM_CHIPTOD_BASE, &chip10->chiptod.xscom_regs); + /* HOMER (must be created before OCC) */ + object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE, + &chip10->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip10->homer.base, + &chip10->homer.mem); + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip10->occ), "homer", + OBJECT(&chip10->homer), &error_abort); if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) { return; } @@ -2169,20 +2186,6 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out(DEVICE(&chip10->sbe), 0, qdev_get_gpio_in( DEVICE(&chip10->psi), PSIHB9_IRQ_PSU)); - /* HOMER */ - object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip), - &error_abort); - if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) { - return; - } - /* Homer Xscom region */ - pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE, - &chip10->homer.pba_regs); - - /* Homer mmio region */ - memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip), - &chip10->homer.regs); - /* N1 chiplet */ if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) { return; diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index 67a1fd77ba..18a53a80c1 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -29,101 +29,6 @@ #include "hw/ppc/pnv_homer.h" #include "hw/ppc/pnv_xscom.h" - -static bool core_max_array(PnvHomer *homer, hwaddr addr) -{ - int i; - PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); - - for (i = 0; i <= homer->chip->nr_cores; i++) { - if (addr == (hmrc->core_max_base + i)) { - return true; - } - } - return false; -} - -/* P8 Pstate table */ - -#define PNV8_OCC_PSTATE_VERSION 0x1f8001 -#define PNV8_OCC_PSTATE_MIN 0x1f8003 -#define PNV8_OCC_PSTATE_VALID 0x1f8000 -#define PNV8_OCC_PSTATE_THROTTLE 0x1f8002 -#define PNV8_OCC_PSTATE_NOM 0x1f8004 -#define PNV8_OCC_PSTATE_TURBO 0x1f8005 -#define PNV8_OCC_PSTATE_ULTRA_TURBO 0x1f8006 -#define PNV8_OCC_PSTATE_DATA 0x1f8008 -#define PNV8_OCC_PSTATE_ID_ZERO 0x1f8010 -#define PNV8_OCC_PSTATE_ID_ONE 0x1f8018 -#define PNV8_OCC_PSTATE_ID_TWO 0x1f8020 -#define PNV8_OCC_VDD_VOLTAGE_IDENTIFIER 0x1f8012 -#define PNV8_OCC_VCS_VOLTAGE_IDENTIFIER 0x1f8013 -#define PNV8_OCC_PSTATE_ZERO_FREQUENCY 0x1f8014 -#define PNV8_OCC_PSTATE_ONE_FREQUENCY 0x1f801c -#define PNV8_OCC_PSTATE_TWO_FREQUENCY 0x1f8024 -#define PNV8_CORE_MAX_BASE 0x1f8810 - - -static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, - unsigned size) -{ - PnvHomer *homer = PNV_HOMER(opaque); - - switch (addr) { - case PNV8_OCC_PSTATE_VALID: - return 1; - case PNV8_OCC_PSTATE_THROTTLE: - return 0; - case PNV8_OCC_PSTATE_VERSION: - return 0x02; - case PNV8_OCC_PSTATE_MIN: - return -2; - case PNV8_OCC_PSTATE_NOM: - case PNV8_OCC_PSTATE_TURBO: - return -1; - case PNV8_OCC_PSTATE_ULTRA_TURBO: - return 0; - case PNV8_OCC_PSTATE_ID_ZERO: - return 0; - case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER: - case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER: - return 1; - case PNV8_OCC_PSTATE_DATA: - return 0; - /* P8 frequency for 0, 1, and 2 pstates */ - case PNV8_OCC_PSTATE_ZERO_FREQUENCY: - case PNV8_OCC_PSTATE_ONE_FREQUENCY: - case PNV8_OCC_PSTATE_TWO_FREQUENCY: - return 3000; - case PNV8_OCC_PSTATE_ID_ONE: - return -1; - case PNV8_OCC_PSTATE_ID_TWO: - return -2; - } - /* pstate table core max array */ - if (core_max_array(homer, addr)) { - return 1; - } - return 0; -} - -static void pnv_power8_homer_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* callback function defined to homer write */ - return; -} - -static const MemoryRegionOps pnv_power8_homer_ops = { - .read = pnv_power8_homer_read, - .write = pnv_power8_homer_write, - .valid.min_access_size = 1, - .valid.max_access_size = 8, - .impl.min_access_size = 1, - .impl.max_access_size = 8, - .endianness = DEVICE_BIG_ENDIAN, -}; - /* P8 PBA BARs */ #define PBA_BAR0 0x00 #define PBA_BAR1 0x01 @@ -192,8 +97,6 @@ static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power8_pba_ops; - homer->homer_ops = &pnv_power8_homer_ops; - homer->core_max_base = PNV8_CORE_MAX_BASE; } static const TypeInfo pnv_homer_power8_type_info = { @@ -203,96 +106,6 @@ static const TypeInfo pnv_homer_power8_type_info = { .class_init = pnv_homer_power8_class_init, }; -/* P9 Pstate table */ - -#define PNV9_OCC_PSTATE_VALID 0xe2000 -#define PNV9_OCC_PSTATE_ID_ZERO 0xe2018 -#define PNV9_OCC_PSTATE_ID_ONE 0xe2020 -#define PNV9_OCC_PSTATE_ID_TWO 0xe2028 -#define PNV9_OCC_PSTATE_DATA 0xe2000 -#define PNV9_OCC_PSTATE_MINOR_VERSION 0xe2008 -#define PNV9_OCC_PSTATE_MIN 0xe2003 -#define PNV9_OCC_PSTATE_NOM 0xe2004 -#define PNV9_OCC_PSTATE_TURBO 0xe2005 -#define PNV9_OCC_PSTATE_ULTRA_TURBO 0xe2818 -#define PNV9_OCC_MAX_PSTATE_ULTRA_TURBO 0xe2006 -#define PNV9_OCC_PSTATE_MAJOR_VERSION 0xe2001 -#define PNV9_OCC_OPAL_RUNTIME_DATA 0xe2b85 -#define PNV9_CHIP_HOMER_IMAGE_POINTER 0x200008 -#define PNV9_CHIP_HOMER_BASE 0x0 -#define PNV9_OCC_PSTATE_ZERO_FREQUENCY 0xe201c -#define PNV9_OCC_PSTATE_ONE_FREQUENCY 0xe2024 -#define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c -#define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002 -#define PNV9_CORE_MAX_BASE 0xe2819 -#define PNV9_DYNAMIC_DATA_STATE 0xe2b80 - -static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, - unsigned size) -{ - PnvHomer *homer = PNV_HOMER(opaque); - - switch (addr) { - case PNV9_OCC_PSTATE_VALID: - return 1; - case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO: - case PNV9_OCC_PSTATE_ID_ZERO: - return 0; - case PNV9_OCC_ROLE_MASTER_OR_SLAVE: - if (homer->chip->chip_id == 0) { - return 0x1; /* master */ - } else { - return 0x0; /* slave */ - } - case PNV9_OCC_PSTATE_NOM: - case PNV9_OCC_PSTATE_TURBO: - case PNV9_OCC_PSTATE_ID_ONE: - case PNV9_OCC_PSTATE_ULTRA_TURBO: - case PNV9_OCC_OPAL_RUNTIME_DATA: - return 1; - case PNV9_OCC_PSTATE_MIN: - case PNV9_OCC_PSTATE_ID_TWO: - return 2; - - /* 3000 khz frequency for 0, 1, and 2 pstates */ - case PNV9_OCC_PSTATE_ZERO_FREQUENCY: - case PNV9_OCC_PSTATE_ONE_FREQUENCY: - case PNV9_OCC_PSTATE_TWO_FREQUENCY: - return 3000; - case PNV9_OCC_PSTATE_MAJOR_VERSION: - return 0x90; - case PNV9_OCC_PSTATE_MINOR_VERSION: - return 0x01; - case PNV9_CHIP_HOMER_BASE: - case PNV9_CHIP_HOMER_IMAGE_POINTER: - return 0; - case PNV9_DYNAMIC_DATA_STATE: - return 0x03; /* active */ - } - /* pstate table core max array */ - if (core_max_array(homer, addr)) { - return 1; - } - return 0; -} - -static void pnv_power9_homer_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* callback function defined to homer write */ - return; -} - -static const MemoryRegionOps pnv_power9_homer_ops = { - .read = pnv_power9_homer_read, - .write = pnv_power9_homer_write, - .valid.min_access_size = 1, - .valid.max_access_size = 8, - .impl.min_access_size = 1, - .impl.max_access_size = 8, - .endianness = DEVICE_BIG_ENDIAN, -}; - static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr, unsigned size) { @@ -351,8 +164,6 @@ static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV9_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power9_pba_ops; - homer->homer_ops = &pnv_power9_homer_ops; - homer->core_max_base = PNV9_CORE_MAX_BASE; } static const TypeInfo pnv_homer_power9_type_info = { @@ -420,8 +231,6 @@ static void pnv_homer_power10_class_init(ObjectClass *klass, void *data) homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV10_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power10_pba_ops; - homer->homer_ops = &pnv_power9_homer_ops; /* TODO */ - homer->core_max_base = PNV9_CORE_MAX_BASE; } static const TypeInfo pnv_homer_power10_type_info = { @@ -435,18 +244,22 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) { PnvHomer *homer = PNV_HOMER(dev); PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); + char homer_str[32]; assert(homer->chip); pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops, homer, "xscom-pba", hmrc->pba_size); - /* homer region */ + /* Homer RAM region */ homer->base = hmrc->get_base(homer->chip); - memory_region_init_io(&homer->regs, OBJECT(dev), - hmrc->homer_ops, homer, "homer-main-memory", - hmrc->size); + snprintf(homer_str, sizeof(homer_str), "homer-chip%d-memory", + homer->chip->chip_id); + if (!memory_region_init_ram(&homer->mem, OBJECT(homer), + homer_str, hmrc->size, errp)) { + return; + } } static const Property pnv_homer_properties[] = { diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 5424d87ee9..22b07a415a 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -24,9 +24,13 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_occ.h" +#define P8_HOMER_OPAL_DATA_OFFSET 0x1F8000 +#define P9_HOMER_OPAL_DATA_OFFSET 0x0E2000 + #define OCB_OCI_OCCMISC 0x4020 #define OCB_OCI_OCCMISC_AND 0x4021 #define OCB_OCI_OCCMISC_OR 0x4022 @@ -166,7 +170,11 @@ const MemoryRegionOps pnv_occ_sram_ops = { static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "PowerNV OCC Controller (POWER8)"; + poc->opal_shared_memory_offset = P8_HOMER_OPAL_DATA_OFFSET; + poc->opal_shared_memory_version = 0x02; poc->xscom_size = PNV_XSCOM_OCC_SIZE; poc->xscom_ops = &pnv_occ_power8_xscom_ops; } @@ -239,8 +247,11 @@ static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PowerNV OCC Controller (POWER9)"; + poc->opal_shared_memory_offset = P9_HOMER_OPAL_DATA_OFFSET; + poc->opal_shared_memory_version = 0x90; poc->xscom_size = PNV9_XSCOM_OCC_SIZE; poc->xscom_ops = &pnv_occ_power9_xscom_ops; + assert(!dc->user_creatable); } static const TypeInfo pnv_occ_power9_type_info = { @@ -263,10 +274,19 @@ static const TypeInfo pnv_occ_power10_type_info = { .class_init = pnv_occ_power10_class_init, }; +static bool occ_init_homer_memory(PnvOCC *occ, Error **errp); + static void pnv_occ_realize(DeviceState *dev, Error **errp) { PnvOCC *occ = PNV_OCC(dev); PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + + assert(homer); + + if (!occ_init_homer_memory(occ, errp)) { + return; + } occ->occmisc = 0; @@ -282,12 +302,16 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, &occ->psi_irq, 1); } +static const Property pnv_occ_properties[] = { + DEFINE_PROP_LINK("homer", PnvOCC, homer, TYPE_PNV_HOMER, PnvHomer *), +}; + static void pnv_occ_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_occ_realize; - dc->desc = "PowerNV OCC Controller"; + device_class_set_props(dc, pnv_occ_properties); dc->user_creatable = false; } @@ -309,3 +333,411 @@ static void pnv_occ_register_types(void) } type_init(pnv_occ_register_types); + +/* From skiboot/hw/occ.c with tab to space conversion */ +/* OCC Communication Area for PStates */ + +#define OPAL_DYNAMIC_DATA_OFFSET 0x0B80 +/* relative to HOMER_OPAL_DATA_OFFSET */ + +#define MAX_PSTATES 256 +#define MAX_P8_CORES 12 +#define MAX_P9_CORES 24 +#define MAX_P10_CORES 32 + +#define MAX_OPAL_CMD_DATA_LENGTH 4090 +#define MAX_OCC_RSP_DATA_LENGTH 8698 + +#define P8_PIR_CORE_MASK 0xFFF8 +#define P9_PIR_QUAD_MASK 0xFFF0 +#define P10_PIR_CHIP_MASK 0x0000 +#define FREQ_MAX_IN_DOMAIN 0 +#define FREQ_MOST_RECENTLY_SET 1 + +#define u8 uint8_t +#define s8 int8_t +#define u16 uint16_t +#define s16 int16_t +#define u32 uint32_t +#define s32 int32_t +#define u64 uint64_t +#define s64 int64_t +#define __be16 uint16_t +#define __be32 uint32_t +#define __packed QEMU_PACKED + +/** + * OCC-OPAL Shared Memory Region + * + * Reference document : + * https://github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf + * + * Supported layout versions: + * - 0x01, 0x02 : P8 + * https://github.com/open-power/occ/blob/master_p8/src/occ/proc/proc_pstate.h + * + * - 0x90 : P9 + * https://github.com/open-power/occ/blob/master/src/occ_405/proc/proc_pstate.h + * In 0x90 the data is separated into :- + * -- Static Data (struct occ_pstate_table): Data is written once by OCC + * -- Dynamic Data (struct occ_dynamic_data): Data is updated at runtime + * + * struct occ_pstate_table - Pstate table layout + * @valid: Indicates if data is valid + * @version: Layout version [Major/Minor] + * @v2.throttle: Reason for limiting the max pstate + * @v9.occ_role: OCC role (Master/Slave) + * @v#.pstate_min: Minimum pstate ever allowed + * @v#.pstate_nom: Nominal pstate + * @v#.pstate_turbo: Maximum turbo pstate + * @v#.pstate_ultra_turbo: Maximum ultra turbo pstate and the maximum + * pstate ever allowed + * @v#.pstates: Pstate-id and frequency list from Pmax to Pmin + * @v#.pstates.id: Pstate-id + * @v#.pstates.flags: Pstate-flag(reserved) + * @v2.pstates.vdd: Voltage Identifier + * @v2.pstates.vcs: Voltage Identifier + * @v#.pstates.freq_khz: Frequency in KHz + * @v#.core_max[1..N]: Max pstate with N active cores + * @spare/reserved/pad: Unused data + */ +struct occ_pstate_table { + u8 valid; + u8 version; + union __packed { + struct __packed { /* Version 0x01 and 0x02 */ + u8 throttle; + s8 pstate_min; + s8 pstate_nom; + s8 pstate_turbo; + s8 pstate_ultra_turbo; + u8 spare; + u64 reserved; + struct __packed { + s8 id; + u8 flags; + u8 vdd; + u8 vcs; + __be32 freq_khz; + } pstates[MAX_PSTATES]; + s8 core_max[MAX_P8_CORES]; + u8 pad[100]; + } v2; + struct __packed { /* Version 0x90 */ + u8 occ_role; + u8 pstate_min; + u8 pstate_nom; + u8 pstate_turbo; + u8 pstate_ultra_turbo; + u8 spare; + u64 reserved1; + u64 reserved2; + struct __packed { + u8 id; + u8 flags; + u16 reserved; + __be32 freq_khz; + } pstates[MAX_PSTATES]; + u8 core_max[MAX_P9_CORES]; + u8 pad[56]; + } v9; + struct __packed { /* Version 0xA0 */ + u8 occ_role; + u8 pstate_min; + u8 pstate_fixed_freq; + u8 pstate_base; + u8 pstate_ultra_turbo; + u8 pstate_fmax; + u8 minor; + u8 pstate_bottom_throttle; + u8 spare; + u8 spare1; + u32 reserved_32; + u64 reserved_64; + struct __packed { + u8 id; + u8 valid; + u16 reserved; + __be32 freq_khz; + } pstates[MAX_PSTATES]; + u8 core_max[MAX_P10_CORES]; + u8 pad[48]; + } v10; + }; +} __packed; + +/** + * OPAL-OCC Command Response Interface + * + * OPAL-OCC Command Buffer + * + * --------------------------------------------------------------------- + * | OPAL | Cmd | OPAL | | Cmd Data | Cmd Data | OPAL | + * | Cmd | Request | OCC | Reserved | Length | Length | Cmd | + * | Flags | ID | Cmd | | (MSB) | (LSB) | Data... | + * --------------------------------------------------------------------- + * | ….OPAL Command Data up to max of Cmd Data Length 4090 bytes | + * | | + * --------------------------------------------------------------------- + * + * OPAL Command Flag + * + * ----------------------------------------------------------------- + * | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + * | (msb) | | | | | | | (lsb) | + * ----------------------------------------------------------------- + * |Cmd | | | | | | | | + * |Ready | | | | | | | | + * ----------------------------------------------------------------- + * + * struct opal_command_buffer - Defines the layout of OPAL command buffer + * @flag: Provides general status of the command + * @request_id: Token to identify request + * @cmd: Command sent + * @data_size: Command data length + * @data: Command specific data + * @spare: Unused byte + */ +struct opal_command_buffer { + u8 flag; + u8 request_id; + u8 cmd; + u8 spare; + __be16 data_size; + u8 data[MAX_OPAL_CMD_DATA_LENGTH]; +} __packed; + +/** + * OPAL-OCC Response Buffer + * + * --------------------------------------------------------------------- + * | OCC | Cmd | OPAL | Response | Rsp Data | Rsp Data | OPAL | + * | Rsp | Request | OCC | Status | Length | Length | Rsp | + * | Flags | ID | Cmd | | (MSB) | (LSB) | Data... | + * --------------------------------------------------------------------- + * | ….OPAL Response Data up to max of Rsp Data Length 8698 bytes | + * | | + * --------------------------------------------------------------------- + * + * OCC Response Flag + * + * ----------------------------------------------------------------- + * | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + * | (msb) | | | | | | | (lsb) | + * ----------------------------------------------------------------- + * | | | | | | |OCC in | Rsp | + * | | | | | | |progress|Ready | + * ----------------------------------------------------------------- + * + * struct occ_response_buffer - Defines the layout of OCC response buffer + * @flag: Provides general status of the response + * @request_id: Token to identify request + * @cmd: Command requested + * @status: Indicates success/failure status of + * the command + * @data_size: Response data length + * @data: Response specific data + */ +struct occ_response_buffer { + u8 flag; + u8 request_id; + u8 cmd; + u8 status; + __be16 data_size; + u8 data[MAX_OCC_RSP_DATA_LENGTH]; +} __packed; + +/** + * OCC-OPAL Shared Memory Interface Dynamic Data Vx90 + * + * struct occ_dynamic_data - Contains runtime attributes + * @occ_state: Current state of OCC + * @major_version: Major version number + * @minor_version: Minor version number (backwards compatible) + * Version 1 indicates GPU presence populated + * @gpus_present: Bitmask of GPUs present (on systems where GPU + * presence is detected through APSS) + * @cpu_throttle: Reason for limiting the max pstate + * @mem_throttle: Reason for throttling memory + * @quick_pwr_drop: Indicates if QPD is asserted + * @pwr_shifting_ratio: Indicates the current percentage of power to + * take away from the CPU vs GPU when shifting + * power to maintain a power cap. Value of 100 + * means take all power from CPU. + * @pwr_cap_type: Indicates type of power cap in effect + * @hard_min_pwr_cap: Hard minimum system power cap in Watts. + * Guaranteed unless hardware failure + * @max_pwr_cap: Maximum allowed system power cap in Watts + * @cur_pwr_cap: Current system power cap + * @soft_min_pwr_cap: Soft powercap minimum. OCC may or may not be + * able to maintain this + * @spare/reserved: Unused data + * @cmd: Opal Command Buffer + * @rsp: OCC Response Buffer + */ +struct occ_dynamic_data { + u8 occ_state; + u8 major_version; + u8 minor_version; + u8 gpus_present; + union __packed { + struct __packed { /* Version 0x90 */ + u8 spare1; + } v9; + struct __packed { /* Version 0xA0 */ + u8 wof_enabled; + } v10; + }; + u8 cpu_throttle; + u8 mem_throttle; + u8 quick_pwr_drop; + u8 pwr_shifting_ratio; + u8 pwr_cap_type; + __be16 hard_min_pwr_cap; + __be16 max_pwr_cap; + __be16 cur_pwr_cap; + __be16 soft_min_pwr_cap; + u8 pad[110]; + struct opal_command_buffer cmd; + struct occ_response_buffer rsp; +} __packed; + +enum occ_response_status { + OCC_RSP_SUCCESS = 0x00, + OCC_RSP_INVALID_COMMAND = 0x11, + OCC_RSP_INVALID_CMD_DATA_LENGTH = 0x12, + OCC_RSP_INVALID_DATA = 0x13, + OCC_RSP_INTERNAL_ERROR = 0x15, +}; + +#define OCC_ROLE_SLAVE 0x00 +#define OCC_ROLE_MASTER 0x01 + +#define OCC_FLAG_RSP_READY 0x01 +#define OCC_FLAG_CMD_IN_PROGRESS 0x02 +#define OPAL_FLAG_CMD_READY 0x80 + +#define PCAP_MAX_POWER_W 100 +#define PCAP_SOFT_MIN_POWER_W 20 +#define PCAP_HARD_MIN_POWER_W 10 + +static bool occ_write_static_data(PnvOCC *occ, + struct occ_pstate_table *static_data, + Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + hwaddr static_addr = homer->base + poc->opal_shared_memory_offset; + MemTxResult ret; + + ret = address_space_write(&address_space_memory, static_addr, + MEMTXATTRS_UNSPECIFIED, static_data, + sizeof(*static_data)); + if (ret != MEMTX_OK) { + error_setg(errp, "OCC: cannot write OCC-OPAL static data"); + return false; + } + + return true; +} + +static bool occ_write_dynamic_data(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data, + Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + hwaddr static_addr = homer->base + poc->opal_shared_memory_offset; + hwaddr dynamic_addr = static_addr + OPAL_DYNAMIC_DATA_OFFSET; + MemTxResult ret; + + ret = address_space_write(&address_space_memory, dynamic_addr, + MEMTXATTRS_UNSPECIFIED, dynamic_data, + sizeof(*dynamic_data)); + if (ret != MEMTX_OK) { + error_setg(errp, "OCC: cannot write OCC-OPAL dynamic data"); + return false; + } + + return true; +} + +static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + PnvChip *chip = homer->chip; + struct occ_pstate_table static_data; + struct occ_dynamic_data dynamic_data; + int i; + + memset(&static_data, 0, sizeof(static_data)); + static_data.valid = 1; + static_data.version = poc->opal_shared_memory_version; + switch (poc->opal_shared_memory_version) { + case 0x02: + static_data.v2.throttle = 0; + static_data.v2.pstate_min = -2; + static_data.v2.pstate_nom = -1; + static_data.v2.pstate_turbo = -1; + static_data.v2.pstate_ultra_turbo = 0; + static_data.v2.pstates[0].id = 0; + static_data.v2.pstates[1].freq_khz = cpu_to_be32(3000); + static_data.v2.pstates[1].id = -1; + static_data.v2.pstates[1].freq_khz = cpu_to_be32(3000); + static_data.v2.pstates[2].id = -2; + static_data.v2.pstates[2].freq_khz = cpu_to_be32(3000); + for (i = 0; i < chip->nr_cores; i++) { + static_data.v2.core_max[i] = 1; + } + break; + case 0x90: + if (chip->chip_id == 0) { + static_data.v9.occ_role = OCC_ROLE_MASTER; + } else { + static_data.v9.occ_role = OCC_ROLE_SLAVE; + } + static_data.v9.pstate_min = 2; + static_data.v9.pstate_nom = 1; + static_data.v9.pstate_turbo = 1; + static_data.v9.pstate_ultra_turbo = 0; + static_data.v9.pstates[0].id = 0; + static_data.v9.pstates[0].freq_khz = cpu_to_be32(3000); + static_data.v9.pstates[1].id = 1; + static_data.v9.pstates[1].freq_khz = cpu_to_be32(3000); + static_data.v9.pstates[2].id = 2; + static_data.v9.pstates[2].freq_khz = cpu_to_be32(3000); + for (i = 0; i < chip->nr_cores; i++) { + static_data.v9.core_max[i] = 1; + } + break; + default: + g_assert_not_reached(); + } + if (!occ_write_static_data(occ, &static_data, errp)) { + return false; + } + + memset(&dynamic_data, 0, sizeof(dynamic_data)); + dynamic_data.occ_state = 0x3; /* active */ + dynamic_data.major_version = 0x0; + dynamic_data.hard_min_pwr_cap = cpu_to_be16(PCAP_HARD_MIN_POWER_W); + dynamic_data.max_pwr_cap = cpu_to_be16(PCAP_MAX_POWER_W); + dynamic_data.cur_pwr_cap = cpu_to_be16(PCAP_MAX_POWER_W); + dynamic_data.soft_min_pwr_cap = cpu_to_be16(PCAP_SOFT_MIN_POWER_W); + switch (poc->opal_shared_memory_version) { + case 0x90: + dynamic_data.minor_version = 0x1; + break; + case 0x02: + dynamic_data.minor_version = 0x0; + break; + default: + g_assert_not_reached(); + } + if (!occ_write_dynamic_data(occ, &dynamic_data, errp)) { + return false; + } + + return true; +} diff --git a/include/hw/ppc/pnv_homer.h b/include/hw/ppc/pnv_homer.h index 5ffc0c97af..a6f2710fa1 100644 --- a/include/hw/ppc/pnv_homer.h +++ b/include/hw/ppc/pnv_homer.h @@ -41,7 +41,7 @@ struct PnvHomer { PnvChip *chip; MemoryRegion pba_regs; - MemoryRegion regs; + MemoryRegion mem; hwaddr base; }; @@ -56,9 +56,6 @@ struct PnvHomerClass { int pba_size; const MemoryRegionOps *pba_ops; - const MemoryRegionOps *homer_ops; - - hwaddr core_max_base; }; #endif /* PPC_PNV_HOMER_H */ diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index df321244e3..f994860980 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -46,6 +46,9 @@ struct PnvOCC { qemu_irq psi_irq; + /* OCCs operate on regions of HOMER memory */ + PnvHomer *homer; + MemoryRegion xscom_regs; MemoryRegion sram_regs; }; @@ -53,6 +56,9 @@ struct PnvOCC { struct PnvOCCClass { DeviceClass parent_class; + hwaddr opal_shared_memory_offset; /* offset in HOMER */ + uint8_t opal_shared_memory_version; + int xscom_size; const MemoryRegionOps *xscom_ops; }; diff --git a/roms/SLOF b/roms/SLOF index ee03aec2c1..3a259df244 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit ee03aec2c106a699aaddd2d3dd52cbd7b7e8d544 +Subproject commit 3a259df2449fc4a4e43ab5f33f0b2c66484b4bc3 From fedbab2c512e8685c2ae87a05f8862e5e83f81ed Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 10 Dec 2024 00:47:48 +1000 Subject: [PATCH 2554/2892] ppc/pnv/occ: Update pstate frequency tables OCC pstate frequencies are in kHz, so the OCC data was 3-4MHz. Upgrade to GHz. Make each pstate have a different frequency. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_occ.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 22b07a415a..19ccfe1bbf 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -682,11 +682,11 @@ static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) static_data.v2.pstate_turbo = -1; static_data.v2.pstate_ultra_turbo = 0; static_data.v2.pstates[0].id = 0; - static_data.v2.pstates[1].freq_khz = cpu_to_be32(3000); + static_data.v2.pstates[1].freq_khz = cpu_to_be32(4000000); static_data.v2.pstates[1].id = -1; - static_data.v2.pstates[1].freq_khz = cpu_to_be32(3000); + static_data.v2.pstates[1].freq_khz = cpu_to_be32(3000000); static_data.v2.pstates[2].id = -2; - static_data.v2.pstates[2].freq_khz = cpu_to_be32(3000); + static_data.v2.pstates[2].freq_khz = cpu_to_be32(2000000); for (i = 0; i < chip->nr_cores; i++) { static_data.v2.core_max[i] = 1; } @@ -702,11 +702,11 @@ static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) static_data.v9.pstate_turbo = 1; static_data.v9.pstate_ultra_turbo = 0; static_data.v9.pstates[0].id = 0; - static_data.v9.pstates[0].freq_khz = cpu_to_be32(3000); + static_data.v9.pstates[0].freq_khz = cpu_to_be32(4000000); static_data.v9.pstates[1].id = 1; - static_data.v9.pstates[1].freq_khz = cpu_to_be32(3000); + static_data.v9.pstates[1].freq_khz = cpu_to_be32(3000000); static_data.v9.pstates[2].id = 2; - static_data.v9.pstates[2].freq_khz = cpu_to_be32(3000); + static_data.v9.pstates[2].freq_khz = cpu_to_be32(2000000); for (i = 0; i < chip->nr_cores; i++) { static_data.v9.core_max[i] = 1; } From 028b1803fbe79aaf598b3254338d7010960d1b44 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Dec 2024 23:00:39 +1000 Subject: [PATCH 2555/2892] ppc/pnv/occ: Add POWER10 OCC-OPAL data format Add POWER10 OCC-OPAL data format. POWER10 changes major version and adds a few fields. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_occ.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 19ccfe1bbf..34decb1700 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -263,14 +263,20 @@ static const TypeInfo pnv_occ_power9_type_info = { static void pnv_occ_power10_class_init(ObjectClass *klass, void *data) { + PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PowerNV OCC Controller (POWER10)"; + poc->opal_shared_memory_offset = P9_HOMER_OPAL_DATA_OFFSET; + poc->opal_shared_memory_version = 0xA0; + poc->xscom_size = PNV9_XSCOM_OCC_SIZE; + poc->xscom_ops = &pnv_occ_power9_xscom_ops; + assert(!dc->user_creatable); } static const TypeInfo pnv_occ_power10_type_info = { .name = TYPE_PNV10_OCC, - .parent = TYPE_PNV9_OCC, + .parent = TYPE_PNV_OCC, .class_init = pnv_occ_power10_class_init, }; @@ -711,6 +717,37 @@ static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) static_data.v9.core_max[i] = 1; } break; + case 0xA0: + if (chip->chip_id == 0) { + static_data.v10.occ_role = OCC_ROLE_MASTER; + } else { + static_data.v10.occ_role = OCC_ROLE_SLAVE; + } + static_data.v10.pstate_min = 4; + static_data.v10.pstate_fixed_freq = 3; + static_data.v10.pstate_base = 2; + static_data.v10.pstate_ultra_turbo = 0; + static_data.v10.pstate_fmax = 1; + static_data.v10.minor = 0x01; + static_data.v10.pstates[0].valid = 1; + static_data.v10.pstates[0].id = 0; + static_data.v10.pstates[0].freq_khz = cpu_to_be32(4200000); + static_data.v10.pstates[1].valid = 1; + static_data.v10.pstates[1].id = 1; + static_data.v10.pstates[1].freq_khz = cpu_to_be32(4000000); + static_data.v10.pstates[2].valid = 1; + static_data.v10.pstates[2].id = 2; + static_data.v10.pstates[2].freq_khz = cpu_to_be32(3800000); + static_data.v10.pstates[3].valid = 1; + static_data.v10.pstates[3].id = 3; + static_data.v10.pstates[3].freq_khz = cpu_to_be32(3000000); + static_data.v10.pstates[4].valid = 1; + static_data.v10.pstates[4].id = 4; + static_data.v10.pstates[4].freq_khz = cpu_to_be32(2000000); + for (i = 0; i < chip->nr_cores; i++) { + static_data.v10.core_max[i] = 1; + } + break; default: g_assert_not_reached(); } @@ -726,6 +763,10 @@ static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) dynamic_data.cur_pwr_cap = cpu_to_be16(PCAP_MAX_POWER_W); dynamic_data.soft_min_pwr_cap = cpu_to_be16(PCAP_SOFT_MIN_POWER_W); switch (poc->opal_shared_memory_version) { + case 0xA0: + dynamic_data.minor_version = 0x1; + dynamic_data.v10.wof_enabled = 0x1; + break; case 0x90: dynamic_data.minor_version = 0x1; break; From a1750b2cba9c70191221f206bfbed277251dd644 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Dec 2024 20:00:45 +1000 Subject: [PATCH 2556/2892] ppc/pnv/occ: Implement a basic dynamic OCC model The OCC is an On Chip Controller that handles various thermal and power management. It is a PPC405 microcontroller that runs its own firmware which is out of scope of the powernv machine model. Some dynamic behaviour and interfaces that are important for host CPU testing can be implemented with a much simpler state machine. This change adds a 100ms timer that ticks through a simple state machine that looks for "OCC command requests" coming from host firmware, and responds to them. For now the powercap command is implemented because that is used by OPAL and exported to Linux and is easy to test. $ F=/sys/firmware/opal/powercap/system-powercap/powercap-current $ cat $F 100 $ echo 50 | sudo tee $F 50 $ cat $F 50 Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_occ.c | 146 +++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv_occ.h | 3 + 2 files changed, 149 insertions(+) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 34decb1700..d9ce35a4d6 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -35,6 +35,7 @@ #define OCB_OCI_OCCMISC_AND 0x4021 #define OCB_OCI_OCCMISC_OR 0x4022 #define OCCMISC_PSI_IRQ PPC_BIT(0) +#define OCCMISC_IRQ_SHMEM PPC_BIT(3) /* OCC sensors */ #define OCC_SENSOR_DATA_BLOCK_OFFSET 0x0000 @@ -67,6 +68,11 @@ static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) qemu_set_irq(occ->psi_irq, !!(val & OCCMISC_PSI_IRQ)); } +static void pnv_occ_raise_msg_irq(PnvOCC *occ) +{ + pnv_occ_set_misc(occ, occ->occmisc | OCCMISC_PSI_IRQ | OCCMISC_IRQ_SHMEM); +} + static uint64_t pnv_occ_power8_xscom_read(void *opaque, hwaddr addr, unsigned size) { @@ -281,6 +287,20 @@ static const TypeInfo pnv_occ_power10_type_info = { }; static bool occ_init_homer_memory(PnvOCC *occ, Error **errp); +static bool occ_model_tick(PnvOCC *occ); + +/* Relatively arbitrary */ +#define OCC_POLL_MS 100 + +static void occ_state_machine_timer(void *opaque) +{ + PnvOCC *occ = opaque; + uint64_t next = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + OCC_POLL_MS; + + if (occ_model_tick(occ)) { + timer_mod(&occ->state_machine_timer, next); + } +} static void pnv_occ_realize(DeviceState *dev, Error **errp) { @@ -306,6 +326,10 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) PNV_OCC_SENSOR_DATA_BLOCK_SIZE); qdev_init_gpio_out(dev, &occ->psi_irq, 1); + + timer_init_ms(&occ->state_machine_timer, QEMU_CLOCK_VIRTUAL, + occ_state_machine_timer, occ); + timer_mod(&occ->state_machine_timer, OCC_POLL_MS); } static const Property pnv_occ_properties[] = { @@ -647,6 +671,27 @@ static bool occ_write_static_data(PnvOCC *occ, return true; } +static bool occ_read_dynamic_data(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data, + Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + hwaddr static_addr = homer->base + poc->opal_shared_memory_offset; + hwaddr dynamic_addr = static_addr + OPAL_DYNAMIC_DATA_OFFSET; + MemTxResult ret; + + ret = address_space_read(&address_space_memory, dynamic_addr, + MEMTXATTRS_UNSPECIFIED, dynamic_data, + sizeof(*dynamic_data)); + if (ret != MEMTX_OK) { + error_setg(errp, "OCC: cannot read OCC-OPAL dynamic data"); + return false; + } + + return true; +} + static bool occ_write_dynamic_data(PnvOCC *occ, struct occ_dynamic_data *dynamic_data, Error **errp) @@ -668,6 +713,107 @@ static bool occ_write_dynamic_data(PnvOCC *occ, return true; } +static bool occ_opal_send_response(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data, + enum occ_response_status status, + uint8_t *data, uint16_t datalen) +{ + struct opal_command_buffer *cmd = &dynamic_data->cmd; + struct occ_response_buffer *rsp = &dynamic_data->rsp; + + rsp->request_id = cmd->request_id; + rsp->cmd = cmd->cmd; + rsp->status = status; + rsp->data_size = cpu_to_be16(datalen); + if (datalen) { + memcpy(rsp->data, data, datalen); + } + if (!occ_write_dynamic_data(occ, dynamic_data, NULL)) { + return false; + } + /* Would be a memory barrier here */ + rsp->flag = OCC_FLAG_RSP_READY; + cmd->flag = 0; + if (!occ_write_dynamic_data(occ, dynamic_data, NULL)) { + return false; + } + + pnv_occ_raise_msg_irq(occ); + + return true; +} + +/* Returns error status */ +static bool occ_opal_process_command(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data) +{ + struct opal_command_buffer *cmd = &dynamic_data->cmd; + struct occ_response_buffer *rsp = &dynamic_data->rsp; + + if (rsp->flag == 0) { + /* Spend one "tick" in the in-progress state */ + rsp->flag = OCC_FLAG_CMD_IN_PROGRESS; + return occ_write_dynamic_data(occ, dynamic_data, NULL); + } else if (rsp->flag != OCC_FLAG_CMD_IN_PROGRESS) { + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_INTERNAL_ERROR, + NULL, 0); + } + + switch (cmd->cmd) { + case 0xD1: { /* SET_POWER_CAP */ + uint16_t data; + if (be16_to_cpu(cmd->data_size) != 2) { + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_INVALID_CMD_DATA_LENGTH, + (uint8_t *)&dynamic_data->cur_pwr_cap, + 2); + } + data = be16_to_cpu(*(uint16_t *)cmd->data); + if (data == 0) { /* clear power cap */ + dynamic_data->pwr_cap_type = 0x00; /* none */ + data = PCAP_MAX_POWER_W; + } else { + dynamic_data->pwr_cap_type = 0x02; /* user set in-band */ + if (data < PCAP_HARD_MIN_POWER_W) { + data = PCAP_HARD_MIN_POWER_W; + } else if (data > PCAP_MAX_POWER_W) { + data = PCAP_MAX_POWER_W; + } + } + dynamic_data->cur_pwr_cap = cpu_to_be16(data); + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_SUCCESS, + (uint8_t *)&dynamic_data->cur_pwr_cap, 2); + } + + default: + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_INVALID_COMMAND, + NULL, 0); + } + g_assert_not_reached(); +} + +static bool occ_model_tick(PnvOCC *occ) +{ + struct occ_dynamic_data dynamic_data; + + if (!occ_read_dynamic_data(occ, &dynamic_data, NULL)) { + /* Can't move OCC state field to safe because we can't map it! */ + qemu_log("OCC: failed to read HOMER data, shutting down OCC\n"); + return false; + } + if (dynamic_data.cmd.flag == OPAL_FLAG_CMD_READY) { + if (!occ_opal_process_command(occ, &dynamic_data)) { + qemu_log("OCC: failed to write HOMER data, shutting down OCC\n"); + return false; + } + } + + return true; +} + static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) { PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index f994860980..3ec42de0ff 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -41,6 +41,9 @@ DECLARE_INSTANCE_CHECKER(PnvOCC, PNV10_OCC, TYPE_PNV10_OCC) struct PnvOCC { DeviceState xd; + /* OCC dynamic model is driven by this timer. */ + QEMUTimer state_machine_timer; + /* OCC Misc interrupt */ uint64_t occmisc; From d3ce7dc9e282e1785c7a6da73b3ebb1da5c34971 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 18 Dec 2024 23:28:47 +1000 Subject: [PATCH 2557/2892] target/ppc: Add Power9/10 power management SPRs Linux power management code accesses these registers for pstate management. Wire up a very simple implementation. Signed-off-by: Nicholas Piggin --- After OCC fixes in QEMU pnv model and skiboot (since they have suffered some bitrot), Linux will start performing PM SPR accesses. This is a very simple implementation that makes it a bit happier. Thanks, Nick --- target/ppc/cpu.h | 2 ++ target/ppc/cpu_init.c | 11 +++++++++ target/ppc/helper.h | 2 ++ target/ppc/misc_helper.c | 53 ++++++++++++++++++++++++++++++++++++++++ target/ppc/spr_common.h | 2 ++ target/ppc/translate.c | 16 ++++++++++++ 6 files changed, 86 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0b8b4c0517..682583d1d1 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2091,6 +2091,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_VTB (0x351) #define SPR_LDBAR (0x352) #define SPR_MMCRC (0x353) +#define SPR_PMSR (0x355) #define SPR_PSSCR (0x357) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) @@ -2098,6 +2099,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_440_INV2 (0x372) #define SPR_TRIG2 (0x372) #define SPR_440_INV3 (0x373) +#define SPR_PMCR (0x374) #define SPR_440_ITV0 (0x374) #define SPR_440_ITV1 (0x375) #define SPR_440_ITV2 (0x376) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1780cabfc6..54035c7bbb 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6451,6 +6451,17 @@ static void register_power9_common_sprs(CPUPPCState *env) spr_read_generic, spr_write_generic, KVM_REG_PPC_PSSCR, 0); + spr_register_hv(env, SPR_PMSR, "PMSR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_pmsr, SPR_NOACCESS, + 0); + spr_register_hv(env, SPR_PMCR, "PMCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pmcr, + PPC_BIT(63)); /* Version 1 (POWER9/10) */ + } static void init_proc_POWER9(CPUPPCState *env) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5a77e761bd..11b914e640 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -733,6 +733,8 @@ DEF_HELPER_2(store_tfmr, void, env, tl) DEF_HELPER_FLAGS_2(store_sprc, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(load_sprd, TCG_CALL_NO_RWG_SE, tl, env) DEF_HELPER_FLAGS_2(store_sprd, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_1(load_pmsr, TCG_CALL_NO_RWG_SE, tl, env) +DEF_HELPER_FLAGS_2(store_pmcr, TCG_CALL_NO_RWG, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index e379da6010..190e9091fc 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -377,6 +377,59 @@ void helper_store_sprd(CPUPPCState *env, target_ulong val) break; } } + +target_ulong helper_load_pmsr(CPUPPCState *env) +{ + target_ulong lowerps = extract64(env->spr[SPR_PMCR], PPC_BIT_NR(15), 8); + target_ulong val = 0; + + val |= PPC_BIT(63); /* verion 0x1 (POWER9/10) */ + /* Pmin = 0 */ + /* XXX: POWER9 should be 3 */ + val |= 4ULL << PPC_BIT_NR(31); /* Pmax */ + val |= lowerps << PPC_BIT_NR(15); /* Local actual Pstate */ + val |= lowerps << PPC_BIT_NR(7); /* Global actual Pstate */ + + return val; +} + +static void ppc_set_pmcr(PowerPCCPU *cpu, target_ulong val) +{ + cpu->env.spr[SPR_PMCR] = val; +} + +void helper_store_pmcr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + CPUState *ccs; + + /* Leave version field unchanged (0x1) */ + val &= ~PPC_BITMASK(60, 63); + val |= PPC_BIT(63); + + val &= ~PPC_BITMASK(0, 7); /* UpperPS ignored */ + if (val & PPC_BITMASK(16, 59)) { + qemu_log_mask(LOG_GUEST_ERROR, "Non-zero PMCR reserved bits " + TARGET_FMT_lx"\n", val); + val &= ~PPC_BITMASK(16, 59); + } + + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { + ppc_set_pmcr(cpu, val); + return; + } + + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + ppc_set_pmcr(ccpu, val); + } + bql_unlock(); +} + #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 01aff449bc..8e3117b463 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -204,6 +204,8 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn); void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); +void spr_read_pmsr(DisasContext *ctx, int gprn, int sprn); +void spr_write_pmcr(DisasContext *ctx, int sprn, int gprn); void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 80638ab535..b0cc8bf283 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1326,6 +1326,22 @@ void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) translator_io_start(&ctx->base); gen_helper_store_lpcr(tcg_env, cpu_gpr[gprn]); } + +void spr_read_pmsr(DisasContext *ctx, int gprn, int sprn) +{ + translator_io_start(&ctx->base); + gen_helper_load_pmsr(cpu_gpr[gprn], tcg_env); +} + +void spr_write_pmcr(DisasContext *ctx, int sprn, int gprn) +{ + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); + gen_helper_store_pmcr(tcg_env, cpu_gpr[gprn]); +} + #endif /* !defined(CONFIG_USER_ONLY) */ void spr_read_tar(DisasContext *ctx, int gprn, int sprn) From a2dea722cd0df663696fc8bb136b1fd29da18742 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 1 Mar 2025 03:16:29 +1000 Subject: [PATCH 2558/2892] ppc/pnv: Support LPC host controller irqs other than serirqs The LPC model has only supported serirqs (ISA device IRQs), however there are internal sources that can raise other interrupts. Update the device to handle these interrupt sources. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_lpc.c | 64 +++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 0480a60f3f..d0fccc165d 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -456,46 +456,18 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc) { uint32_t active_irqs = 0; - if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) { - qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: " - "0x%08"PRIx32"\n", lpc->lpc_hc_irqstat); - } - - if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { - active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; + active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; + if (!(lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN)) { + active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL; } /* Reflect the interrupt */ - if (!lpc->psi_has_serirq) { + if (lpc->psi_has_serirq) { /* - * POWER8 ORs all irqs together (also with LPCHC internal interrupt - * sources) and outputs a single line that raises the PSI LPCHC irq - * which then latches an OPB IRQ status register that sends the irq - * to PSI. - * - * We don't honor the polarity register, it's pointless and unused - * anyway - */ - if (active_irqs) { - lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; - } else { - lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; - } - - /* Update OPB internal latch */ - lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; - - qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); - } else { - /* - * POWER9 and POWER10 have routing fields in OPB master registers that + * POWER9 and later have routing fields in OPB master registers that * send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs. * These don't appear to get latched into an OPB register like the * LPCHC irqs. - * - * POWER9 LPC controller internal irqs still go via the OPB - * and LPCHC PSI irqs like P8, but we have no such internal sources - * modelled yet. */ bool serirq_out[4] = { false, false, false, false }; int irq; @@ -510,7 +482,33 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc) qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]); qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]); qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]); + + /* + * POWER9 and later LPC controller internal irqs still go via the OPB + * and LPCHC PSI irqs like P8, so take the SERIRQs out and continue. + */ + active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL; } + + /* + * POWER8 ORs all irqs together (also with LPCHC internal interrupt + * sources) and outputs a single line that raises the PSI LPCHC irq + * which then latches an OPB IRQ status register that sends the irq + * to PSI. + * + * We don't honor the polarity register, it's pointless and unused + * anyway + */ + if (active_irqs) { + lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; + } else { + lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; + } + + /* Update OPB internal latch */ + lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; + + qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) From f27f31b552df88641fe711846c94f0c3b86d2907 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 1 Mar 2025 03:06:43 +1000 Subject: [PATCH 2559/2892] ppc/pnv: raise no-response errors if an LPC transaction fails If nothing responds to an LPC access, the LPC host controller should set an IRQSTAT error. Model this behaviour. skiboot uses this error to "probe" LPC accesses, among other things to determine if a SuperIO chip is present. After this change it recognizes there is no SuperIO present and does not keep trying to access it. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_lpc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d0fccc165d..0e02ce6e94 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -353,6 +353,8 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void pnv_lpc_opb_noresponse(PnvLpcController *lpc); + static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size) { PnvLpcController *lpc = PNV_LPC(opaque); @@ -376,6 +378,7 @@ static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size) } if (result != MEMTX_OK) { + pnv_lpc_opb_noresponse(lpc); qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%" HWADDR_PRIx "\n", addr); } @@ -406,6 +409,7 @@ static void pnv_lpc_mmio_write(void *opaque, hwaddr addr, } if (result != MEMTX_OK) { + pnv_lpc_opb_noresponse(lpc); qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%" HWADDR_PRIx "\n", addr); } @@ -511,6 +515,12 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc) qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); } +static void pnv_lpc_opb_noresponse(PnvLpcController *lpc) +{ + lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SYNC_NORESP_ERR; + pnv_lpc_eval_irqs(lpc); +} + static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) { PnvLpcController *lpc = opaque; From b9ece4a70c12d749af4b5213f11961aac47ea890 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 1 Mar 2025 00:36:37 +1000 Subject: [PATCH 2560/2892] ppc/pnv: Implement LPC FW address space IDSEL LPC FW address space is a 256MB (28-bit) region to one of 16-devices that are selected with the IDSEL register. Implement this by making the ISA FW address space 4GB, and move the 256MB OPB alias within that space according to IDSEL. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_lpc.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 0e02ce6e94..d812dc8268 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -85,7 +85,7 @@ enum { #define ISA_IO_SIZE 0x00010000 #define ISA_MEM_SIZE 0x10000000 -#define ISA_FW_SIZE 0x10000000 +#define ISA_FW_SIZE 0x100000000 #define LPC_IO_OPB_ADDR 0xd0010000 #define LPC_IO_OPB_SIZE 0x00010000 #define LPC_MEM_OPB_ADDR 0xe0000000 @@ -561,10 +561,13 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, switch (addr) { case LPC_HC_FW_SEG_IDSEL: - /* XXX Actually figure out how that works as this impact - * memory regions/aliases + /* + * ISA FW "devices" are modeled as 16x256MB windows into a + * 4GB LPC FW address space. */ + val &= 0xf; /* Selects device 0-15 */ lpc->lpc_hc_fw_seg_idsel = val; + memory_region_set_alias_offset(&lpc->opb_isa_fw, val * LPC_FW_OPB_SIZE); break; case LPC_HC_FW_RD_ACC_SIZE: lpc->lpc_hc_fw_rd_acc_size = val; @@ -798,9 +801,9 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); - /* Create ISA IO and Mem space regions which are the root of - * the ISA bus (ie, ISA address spaces). We don't create a - * separate one for FW which we alias to memory. + /* + * Create ISA IO, Mem, and FW space regions which are the root of + * the ISA bus (ie, ISA address spaces). */ memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); From b899de9a3d959652bf61d671ffa2c2ba23259b15 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 3 Mar 2025 10:27:01 +1000 Subject: [PATCH 2561/2892] ppc/pnv: Move PNOR to offset 0 in the ISA FW space skiboot has a bug that does not handle ISA FW access correctly for IDSEL devices > 0, and the current PNOR default address and size puts 64MB in device 0 and 64MB in device 1, which causes skiboot to hit this bug and breaks PNOR accesses. Move the PNOR address down to 0 for now, so a 256MB PNOR can be accessed via device 0. Signed-off-by: Nicholas Piggin --- include/hw/ppc/pnv_pnor.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index 2e37ac88bf..19c2d642e8 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -13,9 +13,11 @@ #include "hw/sysbus.h" /* - * PNOR offset on the LPC FW address space + * PNOR offset on the LPC FW address space. For now this should be 0 because + * skiboot 7.1 has a bug where IDSEL > 0 (LPC FW address > 256MB) access is + * not performed correctly. */ -#define PNOR_SPI_OFFSET 0x0c000000UL +#define PNOR_SPI_OFFSET 0x00000000UL #define TYPE_PNV_PNOR "pnv-pnor" OBJECT_DECLARE_SIMPLE_TYPE(PnvPnor, PNV_PNOR) From 4c84a0a4a6e5c4a374493d99e7eb702def7e7eae Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 1 Mar 2025 01:07:57 +1000 Subject: [PATCH 2562/2892] ppc/pnv: Add a PNOR address and size sanity checks The BMC HIOMAP PNOR access protocol has certain limits on PNOR addresses and sizes. Add some sanity checks for these so we don't get strange behaviour. Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_bmc.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index 0c1274df21..811ba3d7a4 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -251,10 +251,38 @@ static const IPMINetfn hiomap_netfn = { void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor) { + uint32_t pnor_size = pnor->size; + uint32_t pnor_addr = PNOR_SPI_OFFSET; + if (!pnv_bmc_is_simulator(bmc)) { return; } + /* + * The HIOMAP protocol uses block units and 16-bit addressing. + * Prevent overflow or misalign. + */ + if (pnor_addr >= 1U << (BLOCK_SHIFT + 16)) { + warn_report("PNOR address is larger than 2^%d, disabling PNOR", + BLOCK_SHIFT + 16); + return; + } + if (pnor_addr & ((1U << BLOCK_SHIFT) - 1)) { + warn_report("PNOR address is not aligned to 2^%d, disabling PNOR", + BLOCK_SHIFT); + return; + } + if (pnor_size > 1U << (BLOCK_SHIFT + 16)) { + warn_report("PNOR size is larger than 2^%d, disabling PNOR", + BLOCK_SHIFT + 16); + return; + } + if (pnor_size & ((1U << BLOCK_SHIFT) - 1)) { + warn_report("PNOR size is not aligned to 2^%d, disabling PNOR", + BLOCK_SHIFT); + return; + } + object_ref(OBJECT(pnor)); object_property_add_const_link(OBJECT(bmc), "pnor", OBJECT(pnor)); From 80f9321308720fa30651e7803347268a7c12c63a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 1 Mar 2025 01:07:57 +1000 Subject: [PATCH 2563/2892] ppc/pnv: Add a default formatted PNOR image The default PNOR image is erased and not recognised by skiboot, so NVRAM gets disabled. This change adds a tiny pnor file that is a proper FFS image with a formatted NVRAM partition. This is recognised by skiboot and will persist across machine reboots. Signed-off-by: Nicholas Piggin --- MAINTAINERS | 1 + docs/system/ppc/powernv.rst | 7 +++++++ hw/ppc/pnv.c | 16 +++++++++++++++- pc-bios/README | 13 +++++++++++++ pc-bios/meson.build | 1 + pc-bios/pnv-pnor.bin | Bin 0 -> 139264 bytes 6 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 pc-bios/pnv-pnor.bin diff --git a/MAINTAINERS b/MAINTAINERS index e2f538fc16..cdb3041bb2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1537,6 +1537,7 @@ F: include/hw/ppc/pnv* F: include/hw/pci-host/pnv* F: include/hw/ssi/pnv_spi* F: pc-bios/skiboot.lid +F: pc-bios/pnv-pnor.bin F: tests/qtest/pnv* F: tests/functional/test_ppc64_powernv.py diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst index de7a807ac7..f3ec2cc69c 100644 --- a/docs/system/ppc/powernv.rst +++ b/docs/system/ppc/powernv.rst @@ -195,6 +195,13 @@ Use a MTD drive to add a PNOR to the machine, and get a NVRAM : -drive file=./witherspoon.pnor,format=raw,if=mtd +If no mtd drive is provided, the powernv platform will create a default +PNOR device using a tiny formatted PNOR in pc-bios/pnv-pnor.bin opened +read-only (PNOR changes will be persistent across reboots but not across +invocations of QEMU). If no defaults are used, an erased 128MB PNOR is +provided (which skiboot will probably not recognize since it is not +formatted). + Maintainer contact information ------------------------------ diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 8c0a2d0573..6fec455ff9 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -64,6 +64,8 @@ #define FW_LOAD_ADDR 0x0 #define FW_MAX_SIZE (16 * MiB) +#define PNOR_FILE_NAME "pnv-pnor.bin" + #define KERNEL_LOAD_ADDR 0x20000000 #define KERNEL_MAX_SIZE (128 * MiB) #define INITRD_LOAD_ADDR 0x28000000 @@ -941,7 +943,7 @@ static void pnv_init(MachineState *machine) uint64_t chip_ram_start = 0; int i; char *chip_typename; - DriveInfo *pnor = drive_get(IF_MTD, 0, 0); + DriveInfo *pnor; DeviceState *dev; if (kvm_enabled()) { @@ -971,6 +973,18 @@ static void pnv_init(MachineState *machine) * Create our simple PNOR device */ dev = qdev_new(TYPE_PNV_PNOR); + pnor = drive_get(IF_MTD, 0, 0); + if (!pnor && defaults_enabled()) { + fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PNOR_FILE_NAME); + if (!fw_filename) { + warn_report("Could not find PNOR '%s'", PNOR_FILE_NAME); + } else { + QemuOpts *opts; + opts = drive_add(IF_MTD, -1, fw_filename, "format=raw,readonly=on"); + pnor = drive_new(opts, IF_MTD, &error_fatal); + g_free(fw_filename); + } + } if (pnor) { qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor)); } diff --git a/pc-bios/README b/pc-bios/README index a08e034fc3..f0f13e15f2 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -43,6 +43,19 @@ run an hypervisor OS or simply a host OS on the "baremetal" platform, also known as the PowerNV (Non-Virtualized) platform. +- pnv-pnor.bin is a non-volatile RAM image used by PowerNV, which stores + NVRAM BIOS settings among other things. This image was created with the + following command (the ffspart tool can be found in the skiboot source tree): + + ffspart -s 0x1000 -c 34 -i pnv-pnor.in -p pnv-pnor.bin + + Where pnv-pnor.in contains the two lines (no leading whitespace): + + NVRAM,0x01000,0x00020000,,,/dev/zero + VERSION,0x21000,0x00001000,,,/dev/zero + + skiboot is then booted once to format the NVRAM partition. + - QemuMacDrivers (https://github.com/ozbenh/QemuMacDrivers) is a project to provide virtualised drivers for PPC MacOS guests. diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 51e95cc903..34d6616c32 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -70,6 +70,7 @@ blobs = [ 's390-ccw.img', 'slof.bin', 'skiboot.lid', + 'pnv-pnor.bin', 'palcode-clipper', 'u-boot.e500', 'u-boot-sam460-20100605.bin', diff --git a/pc-bios/pnv-pnor.bin b/pc-bios/pnv-pnor.bin new file mode 100644 index 0000000000000000000000000000000000000000..3e6f70014408e76d5aeca758c31113f3eee2da84 GIT binary patch literal 139264 zcmeI#F-pWh6adg4Z3XuTYGvUiB3M|su2Hsktrl6cs9>qBjb1>#fLMAT?;zU=IwKJx z*a&F???dwcWXPYH*UhM`jv}IHo|}}HBL*qOMt-$pRBkWk$LE*rZ%ti%rbu<}lm5^7 zyGJwKO}c-2yd93Ka_@J$yyjZ7{!*&*I3iaa$H()_!+57U+}$6xJFlm~&-t6P=jrax z|F(F)%jXmX2oNAZfB*pk1PBlyK;XXu2d_m;C$p`K)9IwH|GL_@uexdi1PBlyK!5-N z0t5&UAV8px0`rwYoYb>feb&d_+cN~(+J_HC5AV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U jAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly&@X{6e@Sm% literal 0 HcmV?d00001 From 19db3b5a247c57a40d7e8a545a8dee9faf4db150 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:21 +1000 Subject: [PATCH 2564/2892] ppc/xive2: Update NVP save/restore for group attributes If the 'H' attribute is set on the NVP structure, the hardware automatically saves and restores some attributes from the TIMA in the NVP structure. The group-specific attributes LSMFB, LGS and T have an extra flag to individually control what is saved/restored. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/xive2.c | 23 ++++++++++++++++++----- include/hw/ppc/xive2_regs.h | 10 +++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index fc5aed3315..cd075e4db9 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC XIVE2 interrupt controller model (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation.. + * Copyright (c) 2019-2024, IBM Corporation.. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" @@ -313,7 +312,19 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, regs[TM_IPB]); nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, regs[TM_CPPR]); - nvp.w2 = xive_set_field32(NVP2_W2_LSMFB, nvp.w2, regs[TM_LSMFB]); + if (nvp.w0 & NVP2_W0_L) { + /* + * Typically not used. If LSMFB is restored with 0, it will + * force a backlog rescan + */ + nvp.w2 = xive_set_field32(NVP2_W2_LSMFB, nvp.w2, regs[TM_LSMFB]); + } + if (nvp.w0 & NVP2_W0_G) { + nvp.w2 = xive_set_field32(NVP2_W2_LGS, nvp.w2, regs[TM_LGS]); + } + if (nvp.w0 & NVP2_W0_T) { + nvp.w2 = xive_set_field32(NVP2_W2_T, nvp.w2, regs[TM_T]); + } xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); nvp.w1 = xive_set_field32(NVP2_W1_CO, nvp.w1, 0); @@ -527,7 +538,9 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 2); tctx->regs[TM_QW1_OS + TM_CPPR] = cppr; - /* we don't model LSMFB */ + tctx->regs[TM_QW1_OS + TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2); + tctx->regs[TM_QW1_OS + TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2); + tctx->regs[TM_QW1_OS + TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2); nvp->w1 = xive_set_field32(NVP2_W1_CO, nvp->w1, 1); nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID_VALID, nvp->w1, 1); diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 1d00c8df64..e88d6eab1e 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -1,10 +1,9 @@ /* * QEMU PowerPC XIVE2 internal structure definitions (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation. + * Copyright (c) 2019-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE2_REGS_H @@ -152,6 +151,9 @@ typedef struct Xive2Nvp { uint32_t w0; #define NVP2_W0_VALID PPC_BIT32(0) #define NVP2_W0_HW PPC_BIT32(7) +#define NVP2_W0_L PPC_BIT32(8) +#define NVP2_W0_G PPC_BIT32(9) +#define NVP2_W0_T PPC_BIT32(10) #define NVP2_W0_ESC_END PPC_BIT32(25) /* 'N' bit 0:ESB 1:END */ #define NVP2_W0_PGOFIRST PPC_BITMASK32(26, 31) uint32_t w1; @@ -163,6 +165,8 @@ typedef struct Xive2Nvp { #define NVP2_W2_CPPR PPC_BITMASK32(0, 7) #define NVP2_W2_IPB PPC_BITMASK32(8, 15) #define NVP2_W2_LSMFB PPC_BITMASK32(16, 23) +#define NVP2_W2_T PPC_BIT32(27) +#define NVP2_W2_LGS PPC_BITMASK32(28, 31) uint32_t w3; uint32_t w4; #define NVP2_W4_ESC_ESB_BLOCK PPC_BITMASK32(0, 3) /* N:0 */ From a45580ad03f034c84689c6bee5f875432dbd73ba Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Tue, 11 Mar 2025 11:51:21 +1000 Subject: [PATCH 2565/2892] ppc/xive: Rename ipb_to_pipr() to xive_ipb_to_pipr() Rename to follow the convention of the other function names. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/xive.c | 22 ++++++---------------- include/hw/ppc/xive.h | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 139cfdf9bf..f603d9f1a5 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -3,8 +3,7 @@ * * Copyright (c) 2017-2018, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" @@ -27,15 +26,6 @@ * XIVE Thread Interrupt Management context */ -/* - * Convert an Interrupt Pending Buffer (IPB) register to a Pending - * Interrupt Priority Register (PIPR), which contains the priority of - * the most favored pending notification. - */ -static uint8_t ipb_to_pipr(uint8_t ibp) -{ - return ibp ? clz32((uint32_t)ibp << 24) : 0xff; -} static uint8_t exception_mask(uint8_t ring) { @@ -159,7 +149,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) * Recompute the PIPR based on local pending interrupts. The PHYS * ring must take the minimum of both the PHYS and POOL PIPR values. */ - pipr_min = ipb_to_pipr(regs[TM_IPB]); + pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); ring_min = ring; /* PHYS updates also depend on POOL values */ @@ -169,7 +159,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) /* POOL values only matter if POOL ctx is valid */ if (pool_regs[TM_WORD2] & 0x80) { - uint8_t pool_pipr = ipb_to_pipr(pool_regs[TM_IPB]); + uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]); /* * Determine highest priority interrupt and @@ -193,7 +183,7 @@ void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb) uint8_t *regs = &tctx->regs[ring]; regs[TM_IPB] |= ipb; - regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); + regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); xive_tctx_notify(tctx, ring); } @@ -841,9 +831,9 @@ void xive_tctx_reset(XiveTCTX *tctx) * CPPR is first set. */ tctx->regs[TM_QW1_OS + TM_PIPR] = - ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]); + xive_ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]); tctx->regs[TM_QW3_HV_PHYS + TM_PIPR] = - ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]); + xive_ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]); } static void xive_tctx_realize(DeviceState *dev, Error **errp) diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index ea5d03a346..75276b9ba6 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -130,11 +130,9 @@ * TCTX Thread interrupt Context * * - * Copyright (c) 2017-2018, IBM Corporation. - * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * Copyright (c) 2017-2024, IBM Corporation. * + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE_H @@ -510,6 +508,16 @@ static inline uint8_t xive_priority_to_ipb(uint8_t priority) 0 : 1 << (XIVE_PRIORITY_MAX - priority); } +/* + * Convert an Interrupt Pending Buffer (IPB) register to a Pending + * Interrupt Priority Register (PIPR), which contains the priority of + * the most favored pending notification. + */ +static inline uint8_t xive_ipb_to_pipr(uint8_t ibp) +{ + return ibp ? clz32((uint32_t)ibp << 24) : 0xff; +} + /* * XIVE Thread Interrupt Management Aera (TIMA) * From 9d2b6058c5b0601779a65e7db4176073940a713d Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:21 +1000 Subject: [PATCH 2566/2892] ppc/xive2: Add grouping level to notification The NSR has a (so far unused) grouping level field. When a interrupt is presented, that field tells the hypervisor or OS if the interrupt is for an individual VP or for a VP-group/crowd. This patch reworks the presentation API to allow to set/unset the level when raising/accepting an interrupt. It also renames xive_tctx_ipb_update() to xive_tctx_pipr_update() as the IPB is only used for VP-specific target, whereas the PIPR always needs to be updated. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/trace-events | 2 +- hw/intc/xive.c | 88 ++++++++++++++++++++++---------------- hw/intc/xive2.c | 19 ++++---- include/hw/ppc/xive.h | 9 +++- include/hw/ppc/xive_regs.h | 25 ++++++++--- 5 files changed, 90 insertions(+), 53 deletions(-) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 913197a181..e9fe1978f9 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -283,7 +283,7 @@ xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "EN xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x" xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64 xive_tctx_tm_read(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64 -xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring) "found NVT 0x%x/0x%x ring=0x%x" +xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring, uint8_t group_level) "found NVT 0x%x/0x%x ring=0x%x group_level=%d" xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64 # pnv_xive.c diff --git a/hw/intc/xive.c b/hw/intc/xive.c index f603d9f1a5..1e362f987a 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -26,19 +26,6 @@ * XIVE Thread Interrupt Management context */ - -static uint8_t exception_mask(uint8_t ring) -{ - switch (ring) { - case TM_QW1_OS: - return TM_QW1_NSR_EO; - case TM_QW3_HV_PHYS: - return TM_QW3_NSR_HE; - default: - g_assert_not_reached(); - } -} - static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) { switch (ring) { @@ -58,11 +45,10 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) { uint8_t *regs = &tctx->regs[ring]; uint8_t nsr = regs[TM_NSR]; - uint8_t mask = exception_mask(ring); qemu_irq_lower(xive_tctx_output(tctx, ring)); - if (regs[TM_NSR] & mask) { + if (regs[TM_NSR] != 0) { uint8_t cppr = regs[TM_PIPR]; uint8_t alt_ring; uint8_t *alt_regs; @@ -77,11 +63,18 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) regs[TM_CPPR] = cppr; - /* Reset the pending buffer bit */ - alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); + /* + * If the interrupt was for a specific VP, reset the pending + * buffer bit, otherwise clear the logical server indicator + */ + if (regs[TM_NSR] & TM_NSR_GRP_LVL) { + regs[TM_NSR] &= ~TM_NSR_GRP_LVL; + } else { + alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); + } - /* Drop Exception bit */ - regs[TM_NSR] &= ~mask; + /* Drop the exception bit and any group/crowd */ + regs[TM_NSR] = 0; trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring, alt_regs[TM_IPB], regs[TM_PIPR], @@ -91,7 +84,7 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) return ((uint64_t)nsr << 8) | regs[TM_CPPR]; } -static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring) +void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) { /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; @@ -101,13 +94,13 @@ static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring) if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) { switch (ring) { case TM_QW1_OS: - regs[TM_NSR] |= TM_QW1_NSR_EO; + regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); break; case TM_QW2_HV_POOL: - alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6); + alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F); break; case TM_QW3_HV_PHYS: - regs[TM_NSR] |= (TM_QW3_NSR_HE_PHYS << 6); + regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F); break; default: g_assert_not_reached(); @@ -175,17 +168,27 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) regs[TM_PIPR] = pipr_min; /* CPPR has changed, check if we need to raise a pending exception */ - xive_tctx_notify(tctx, ring_min); + xive_tctx_notify(tctx, ring_min, 0); } -void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb) -{ +void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level) + { + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *alt_regs = &tctx->regs[alt_ring]; uint8_t *regs = &tctx->regs[ring]; - regs[TM_IPB] |= ipb; - regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); - xive_tctx_notify(tctx, ring); -} + if (group_level == 0) { + /* VP-specific */ + regs[TM_IPB] |= xive_priority_to_ipb(priority); + alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + } else { + /* VP-group */ + alt_regs[TM_PIPR] = xive_priority_to_pipr(priority); + } + xive_tctx_notify(tctx, ring, group_level); + } /* * XIVE Thread Interrupt Management Area (TIMA) @@ -401,13 +404,13 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx, } /* - * Adjust the IPB to allow a CPU to process event queues of other + * Adjust the PIPR to allow a CPU to process event queues of other * priorities during one physical interrupt cycle. */ static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - xive_tctx_ipb_update(tctx, TM_QW1_OS, xive_priority_to_ipb(value & 0xff)); + xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0); } static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk, @@ -485,16 +488,20 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, /* Reset the NVT value */ nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, 0); xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4); + + uint8_t *regs = &tctx->regs[TM_QW1_OS]; + regs[TM_IPB] |= ipb; } + /* - * Always call xive_tctx_ipb_update(). Even if there were no + * Always call xive_tctx_pipr_update(). Even if there were no * escalation triggered, there could be a pending interrupt which * was saved when the context was pulled and that we need to take * into account by recalculating the PIPR (which is not * saved/restored). * It will also raise the External interrupt signal if needed. */ - xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */ } /* @@ -1648,6 +1655,12 @@ static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f)); } +static uint8_t xive_get_group_level(uint32_t nvp_index) +{ + /* FIXME add crowd encoding */ + return ctz32(~nvp_index) + 1; +} + /* * The thread context register words are in big-endian format. */ @@ -1733,6 +1746,7 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); XiveTCTXMatch match = { .tctx = NULL, .ring = 0 }; + uint8_t group_level; int count; /* @@ -1746,9 +1760,9 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, /* handle CPU exception delivery */ if (count) { - trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring); - xive_tctx_ipb_update(match.tctx, match.ring, - xive_priority_to_ipb(priority)); + group_level = cam_ignore ? xive_get_group_level(nvt_idx) : 0; + trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level); + xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); } return !!count; diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index cd075e4db9..62c3f05af9 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -563,8 +563,11 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - Xive2Nvp nvp; uint8_t ipb; + uint8_t backlog_level; + uint8_t backlog_prio; + uint8_t *regs = &tctx->regs[TM_QW1_OS]; + Xive2Nvp nvp; /* * Grab the associated thread interrupt context registers in the @@ -593,15 +596,15 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); } + regs[TM_IPB] |= ipb; + backlog_prio = xive_ipb_to_pipr(ipb); + backlog_level = 0; + /* - * Always call xive_tctx_ipb_update(). Even if there were no - * escalation triggered, there could be a pending interrupt which - * was saved when the context was pulled and that we need to take - * into account by recalculating the PIPR (which is not - * saved/restored). - * It will also raise the External interrupt signal if needed. + * Compute the PIPR based on the restored state. + * It will raise the External interrupt signal if needed. */ - xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + xive_tctx_pipr_update(tctx, TM_QW1_OS, backlog_prio, backlog_level); } /* diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 75276b9ba6..805b1d7b80 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -508,6 +508,11 @@ static inline uint8_t xive_priority_to_ipb(uint8_t priority) 0 : 1 << (XIVE_PRIORITY_MAX - priority); } +static inline uint8_t xive_priority_to_pipr(uint8_t priority) +{ + return priority > XIVE_PRIORITY_MAX ? 0xFF : priority; +} + /* * Convert an Interrupt Pending Buffer (IPB) register to a Pending * Interrupt Priority Register (PIPR), which contains the priority of @@ -540,8 +545,10 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf); Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp); void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); -void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb); +void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); +void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); /* * KVM XIVE device helpers diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index 326327fc79..54bc6c53b4 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -7,10 +7,9 @@ * access to the different fields. * * - * Copyright (c) 2016-2018, IBM Corporation. + * Copyright (c) 2016-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE_REGS_H @@ -146,7 +145,14 @@ #define TM_SPC_PULL_PHYS_CTX_OL 0xc38 /* Pull phys ctx to odd cache line */ /* XXX more... */ -/* NSR fields for the various QW ack types */ +/* + * NSR fields for the various QW ack types + * + * P10 has an extra bit in QW3 for the group level instead of the + * reserved 'i' bit. Since it is not used and we don't support group + * interrupts on P9, we use the P10 definition for the group level so + * that we can have common macros for the NSR + */ #define TM_QW0_NSR_EB PPC_BIT8(0) #define TM_QW1_NSR_EO PPC_BIT8(0) #define TM_QW3_NSR_HE PPC_BITMASK8(0, 1) @@ -154,8 +160,15 @@ #define TM_QW3_NSR_HE_POOL 1 #define TM_QW3_NSR_HE_PHYS 2 #define TM_QW3_NSR_HE_LSI 3 -#define TM_QW3_NSR_I PPC_BIT8(2) -#define TM_QW3_NSR_GRP_LVL PPC_BIT8(3, 7) +#define TM_NSR_GRP_LVL PPC_BITMASK8(2, 7) +/* + * On P10, the format of the 6-bit group level is: 2 bits for the + * crowd size and 4 bits for the group size. Since group/crowd size is + * always a power of 2, we encode the log. For example, group_level=4 + * means crowd size = 0 and group size = 16 (2^4) + * Same encoding is used in the NVP and NVGC structures for + * PGoFirst and PGoNext fields + */ /* * EAS (Event Assignment Structure) From 9cb7f6ebed60739ff6e5f09e8ce85c9602d33f2a Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:21 +1000 Subject: [PATCH 2567/2892] ppc/xive2: Support group-matching when looking for target If an END has the 'i' bit set (ignore), then it targets a group of VPs. The size of the group depends on the VP index of the target (first 0 found when looking at the least significant bits of the index) so a mask is applied on the VP index of a running thread to know if we have a match. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/pnv_xive2.c | 38 +++++++++++++++--------- hw/intc/xive.c | 56 +++++++++++++++++++++++++----------- hw/intc/xive2.c | 65 ++++++++++++++++++++++++++++++------------ include/hw/ppc/xive.h | 5 +++- include/hw/ppc/xive2.h | 7 ++--- 5 files changed, 118 insertions(+), 53 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 9ed759417e..8429ccbd51 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC XIVE2 interrupt controller model (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation. + * Copyright (c) 2019-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" @@ -660,21 +659,34 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, logic_serv); } - /* - * Save the context and follow on to catch duplicates, - * that we don't support yet. - */ if (ring != -1) { - if (match->tctx) { + /* + * For VP-specific match, finding more than one is a + * problem. For group notification, it's possible. + */ + if (!cam_ignore && match->tctx) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a " "thread context NVT %x/%x\n", nvt_blk, nvt_idx); - return false; + /* Should set a FIR if we ever model it */ + return -1; + } + /* + * For a group notification, we need to know if the + * match is precluded first by checking the current + * thread priority. If the interrupt can be delivered, + * we always notify the first match (for now). + */ + if (cam_ignore && + xive2_tm_irq_precluded(tctx, ring, priority)) { + match->precluded = true; + } else { + if (!match->tctx) { + match->ring = ring; + match->tctx = tctx; + } + count++; } - - match->ring = ring; - match->tctx = tctx; - count++; } } } diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 1e362f987a..3e4c932f19 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1655,6 +1655,16 @@ static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f)); } +uint32_t xive_get_vpgroup_size(uint32_t nvp_index) +{ + /* + * Group size is a power of 2. The position of the first 0 + * (starting with the least significant bits) in the NVP index + * gives the size of the group. + */ + return 1 << (ctz32(~nvp_index) + 1); +} + static uint8_t xive_get_group_level(uint32_t nvp_index) { /* FIXME add crowd encoding */ @@ -1727,30 +1737,39 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, /* * This is our simple Xive Presenter Engine model. It is merged in the * Router as it does not require an extra object. - * - * It receives notification requests sent by the IVRE to find one - * matching NVT (or more) dispatched on the processor threads. In case - * of a single NVT notification, the process is abbreviated and the - * thread is signaled if a match is found. In case of a logical server - * notification (bits ignored at the end of the NVT identifier), the - * IVPE and IVRE select a winning thread using different filters. This - * involves 2 or 3 exchanges on the PowerBus that the model does not - * support. - * - * The parameters represent what is sent on the PowerBus */ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint8_t priority, - uint32_t logic_serv) + uint32_t logic_serv, bool *precluded) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); - XiveTCTXMatch match = { .tctx = NULL, .ring = 0 }; + XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false }; uint8_t group_level; int count; /* - * Ask the machine to scan the interrupt controllers for a match + * Ask the machine to scan the interrupt controllers for a match. + * + * For VP-specific notification, we expect at most one match and + * one call to the presenters is all we need (abbreviated notify + * sequence documented by the architecture). + * + * For VP-group notification, match_nvt() is the equivalent of the + * "histogram" and "poll" commands sent to the power bus to the + * presenters. 'count' could be more than one, but we always + * select the first match for now. 'precluded' tells if (at least) + * one thread matches but can't take the interrupt now because + * it's running at a more favored priority. We return the + * information to the router so that it can take appropriate + * actions (backlog, escalation, broadcast, etc...) + * + * If we were to implement a better way of dispatching the + * interrupt in case of multiple matches (instead of the first + * match), we would need a heuristic to elect a thread (for + * example, the hardware keeps track of an 'age' in the TIMA) and + * a new command to the presenters (the equivalent of the "assign" + * power bus command in the documented full notify sequence. */ count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore, priority, logic_serv, &match); @@ -1763,6 +1782,8 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, group_level = cam_ignore ? xive_get_group_level(nvt_idx) : 0; trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level); xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); + } else { + *precluded = match.precluded; } return !!count; @@ -1802,7 +1823,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) uint8_t nvt_blk; uint32_t nvt_idx; XiveNVT nvt; - bool found; + bool found, precluded; uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w); uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); @@ -1885,8 +1906,9 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx, xive_get_field32(END_W7_F0_IGNORE, end.w7), priority, - xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7)); - + xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), + &precluded); + /* we don't support VP-group notification on P9, so precluded is not used */ /* TODO: Auto EOI. */ if (found) { diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 62c3f05af9..41dbbdbb68 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -739,6 +739,12 @@ int xive2_router_write_nvgc(Xive2Router *xrtr, bool crowd, return xrc->write_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, nvgc); } +static bool xive2_vp_match_mask(uint32_t cam1, uint32_t cam2, + uint32_t vp_mask) +{ + return (cam1 & vp_mask) == (cam2 & vp_mask); +} + /* * The thread context register words are in big-endian format. */ @@ -753,44 +759,50 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); uint32_t qw0w2 = xive_tctx_word2(&tctx->regs[TM_QW0_USER]); - /* - * TODO (PowerNV): ignore mode. The low order bits of the NVT - * identifier are ignored in the "CAM" match. - */ + uint32_t vp_mask = 0xFFFFFFFF; if (format == 0) { - if (cam_ignore == true) { - /* - * F=0 & i=1: Logical server notification (bits ignored at - * the end of the NVT identifier) - */ - qemu_log_mask(LOG_UNIMP, "XIVE: no support for LS NVT %x/%x\n", - nvt_blk, nvt_idx); - return -1; + /* + * i=0: Specific NVT notification + * i=1: VP-group notification (bits ignored at the end of the + * NVT identifier) + */ + if (cam_ignore) { + vp_mask = ~(xive_get_vpgroup_size(nvt_idx) - 1); } - /* F=0 & i=0: Specific NVT notification */ + /* For VP-group notifications, threads with LGS=0 are excluded */ /* PHYS ring */ if ((be32_to_cpu(qw3w2) & TM2_QW3W2_VT) && - cam == xive2_tctx_hw_cam_line(xptr, tctx)) { + !(cam_ignore && tctx->regs[TM_QW3_HV_PHYS + TM_LGS] == 0) && + xive2_vp_match_mask(cam, + xive2_tctx_hw_cam_line(xptr, tctx), + vp_mask)) { return TM_QW3_HV_PHYS; } /* HV POOL ring */ if ((be32_to_cpu(qw2w2) & TM2_QW2W2_VP) && - cam == xive_get_field32(TM2_QW2W2_POOL_CAM, qw2w2)) { + !(cam_ignore && tctx->regs[TM_QW2_HV_POOL + TM_LGS] == 0) && + xive2_vp_match_mask(cam, + xive_get_field32(TM2_QW2W2_POOL_CAM, qw2w2), + vp_mask)) { return TM_QW2_HV_POOL; } /* OS ring */ if ((be32_to_cpu(qw1w2) & TM2_QW1W2_VO) && - cam == xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2)) { + !(cam_ignore && tctx->regs[TM_QW1_OS + TM_LGS] == 0) && + xive2_vp_match_mask(cam, + xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2), + vp_mask)) { return TM_QW1_OS; } } else { /* F=1 : User level Event-Based Branch (EBB) notification */ + /* FIXME: what if cam_ignore and LGS = 0 ? */ /* USER ring */ if ((be32_to_cpu(qw1w2) & TM2_QW1W2_VO) && (cam == xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2)) && @@ -802,6 +814,22 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, return -1; } +bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) +{ + uint8_t *regs = &tctx->regs[ring]; + + /* + * The xive2_presenter_tctx_match() above tells if there's a match + * but for VP-group notification, we still need to look at the + * priority to know if the thread can take the interrupt now or if + * it is precluded. + */ + if (priority < regs[TM_CPPR]) { + return false; + } + return true; +} + static void xive2_router_realize(DeviceState *dev, Error **errp) { Xive2Router *xrtr = XIVE2_ROUTER(dev); @@ -841,7 +869,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, Xive2End end; uint8_t priority; uint8_t format; - bool found; + bool found, precluded; Xive2Nvp nvp; uint8_t nvp_blk; uint32_t nvp_idx; @@ -922,7 +950,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, xive2_end_is_ignore(&end), priority, - xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7)); + xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), + &precluded); /* TODO: Auto EOI. */ diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 805b1d7b80..e78adaffa5 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -422,6 +422,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas); typedef struct XiveTCTXMatch { XiveTCTX *tctx; uint8_t ring; + bool precluded; } XiveTCTXMatch; #define TYPE_XIVE_PRESENTER "xive-presenter" @@ -450,7 +451,9 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint8_t priority, - uint32_t logic_serv); + uint32_t logic_serv, bool *precluded); + +uint32_t xive_get_vpgroup_size(uint32_t nvp_index); /* * XIVE Fabric (Interface between Interrupt Controller and Machine) diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 5bccf41159..65154f78d8 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -1,11 +1,9 @@ /* * QEMU PowerPC XIVE2 interrupt controller model (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation. - * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * Copyright (c) 2019-2024, IBM Corporation. * + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE2_H @@ -121,6 +119,7 @@ uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); void xive2_tm_pull_os_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, From 58fa4433e02a394872ff83c28edea174c43e6afb Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:21 +1000 Subject: [PATCH 2568/2892] ppc/xive2: Add undelivered group interrupt to backlog When a group interrupt cannot be delivered, we need to: - increment the backlog counter for the group in the NVG table (if the END is configured to keep a backlog). - start a broadcast operation to set the LSMFB field on matching CPUs which can't take the interrupt now because they're running at too high a priority. [npiggin: squash in fixes from milesg] [milesg: only load the NVP if the END is !ignore] [milesg: always broadcast backlog, not only when there are precluded VPs] Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/pnv_xive2.c | 42 +++++++++++++ hw/intc/xive2.c | 134 ++++++++++++++++++++++++++++++++--------- hw/ppc/pnv.c | 22 ++++++- include/hw/ppc/xive.h | 5 ++ include/hw/ppc/xive2.h | 1 + 5 files changed, 174 insertions(+), 30 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 8429ccbd51..e7a7d1b50f 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -705,6 +705,47 @@ static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr) return cfg; } +static int pnv_xive2_broadcast(XivePresenter *xptr, + uint8_t nvt_blk, uint32_t nvt_idx, + uint8_t priority) +{ + PnvXive2 *xive = PNV_XIVE2(xptr); + PnvChip *chip = xive->chip; + int i, j; + bool gen1_tima_os = + xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + for (j = 0; j < cc->nr_threads; j++) { + PowerPCCPU *cpu = pc->threads[j]; + XiveTCTX *tctx; + int ring; + + if (!pnv_xive2_is_cpu_enabled(xive, cpu)) { + continue; + } + + tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + + if (gen1_tima_os) { + ring = xive_presenter_tctx_match(xptr, tctx, 0, nvt_blk, + nvt_idx, true, 0); + } else { + ring = xive2_presenter_tctx_match(xptr, tctx, 0, nvt_blk, + nvt_idx, true, 0); + } + + if (ring != -1) { + xive2_tm_set_lsmfb(tctx, ring, priority); + } + } + } + return 0; +} + static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr) { return pnv_xive2_block_id(PNV_XIVE2(xrtr)); @@ -2444,6 +2485,7 @@ static void pnv_xive2_class_init(ObjectClass *klass, void *data) xpc->match_nvt = pnv_xive2_match_nvt; xpc->get_config = pnv_xive2_presenter_get_config; + xpc->broadcast = pnv_xive2_broadcast; }; static const TypeInfo pnv_xive2_info = { diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 41dbbdbb68..44d891f1f6 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -53,7 +53,8 @@ static uint32_t xive2_nvgc_get_backlog(Xive2Nvgc *nvgc, uint8_t priority) /* * The per-priority backlog counters are 24-bit and the structure - * is stored in big endian + * is stored in big endian. NVGC is 32-bytes long, so 24-bytes from + * w2, which fits 8 priorities * 24-bits per priority. */ ptr = (uint8_t *)&nvgc->w2 + priority * 3; for (i = 0; i < 3; i++, ptr++) { @@ -62,6 +63,30 @@ static uint32_t xive2_nvgc_get_backlog(Xive2Nvgc *nvgc, uint8_t priority) return val; } +static void xive2_nvgc_set_backlog(Xive2Nvgc *nvgc, uint8_t priority, + uint32_t val) +{ + uint8_t *ptr, i; + uint32_t shift; + + if (priority > 7) { + return; + } + + if (val > 0xFFFFFF) { + val = 0xFFFFFF; + } + /* + * The per-priority backlog counters are 24-bit and the structure + * is stored in big endian + */ + ptr = (uint8_t *)&nvgc->w2 + priority * 3; + for (i = 0; i < 3; i++, ptr++) { + shift = 8 * (2 - i); + *ptr = (val >> shift) & 0xFF; + } +} + void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) { if (!xive2_eas_is_valid(eas)) { @@ -830,6 +855,19 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) return true; } +void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority) +{ + uint8_t *regs = &tctx->regs[ring]; + + /* + * Called by the router during a VP-group notification when the + * thread matches but can't take the interrupt because it's + * already running at a more favored priority. It then stores the + * new interrupt priority in the LSMFB field. + */ + regs[TM_LSMFB] = priority; +} + static void xive2_router_realize(DeviceState *dev, Error **errp) { Xive2Router *xrtr = XIVE2_ROUTER(dev); @@ -870,7 +908,6 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, uint8_t priority; uint8_t format; bool found, precluded; - Xive2Nvp nvp; uint8_t nvp_blk; uint32_t nvp_idx; @@ -934,19 +971,6 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); - /* NVP cache lookup */ - if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVP %x/%x\n", - nvp_blk, nvp_idx); - return; - } - - if (!xive2_nvp_is_valid(&nvp)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is invalid\n", - nvp_blk, nvp_idx); - return; - } - found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, xive2_end_is_ignore(&end), priority, @@ -962,10 +986,9 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, /* * If no matching NVP is dispatched on a HW thread : * - specific VP: update the NVP structure if backlog is activated - * - logical server : forward request to IVPE (not supported) + * - VP-group: update the backlog counter for that priority in the NVG */ if (xive2_end_is_backlog(&end)) { - uint8_t ipb; if (format == 1) { qemu_log_mask(LOG_GUEST_ERROR, @@ -974,19 +997,72 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - /* - * Record the IPB in the associated NVP structure for later - * use. The presenter will resend the interrupt when the vCPU - * is dispatched again on a HW thread. - */ - ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2) | - xive_priority_to_ipb(priority); - nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); - xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + if (!xive2_end_is_ignore(&end)) { + uint8_t ipb; + Xive2Nvp nvp; - /* - * On HW, follows a "Broadcast Backlog" to IVPEs - */ + /* NVP cache lookup */ + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is invalid\n", + nvp_blk, nvp_idx); + return; + } + + /* + * Record the IPB in the associated NVP structure for later + * use. The presenter will resend the interrupt when the vCPU + * is dispatched again on a HW thread. + */ + ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2) | + xive_priority_to_ipb(priority); + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + } else { + Xive2Nvgc nvg; + uint32_t backlog; + + /* For groups, the per-priority backlog counters are in the NVG */ + if (xive2_router_get_nvgc(xrtr, false, nvp_blk, nvp_idx, &nvg)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVG %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvgc_is_valid(&nvg)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVG %x/%x is invalid\n", + nvp_blk, nvp_idx); + return; + } + + /* + * Increment the backlog counter for that priority. + * We only call broadcast the first time the counter is + * incremented. broadcast will set the LSMFB field of the TIMA of + * relevant threads so that they know an interrupt is pending. + */ + backlog = xive2_nvgc_get_backlog(&nvg, priority) + 1; + xive2_nvgc_set_backlog(&nvg, priority, backlog); + xive2_router_write_nvgc(xrtr, false, nvp_blk, nvp_idx, &nvg); + + if (backlog == 1) { + XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb); + xfc->broadcast(xrtr->xfb, nvp_blk, nvp_idx, priority); + + if (!xive2_end_is_precluded_escalation(&end)) { + /* + * The interrupt will be picked up when the + * matching thread lowers its priority level + */ + return; + } + } + } } do_escalation: diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 6fec455ff9..af836c1388 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1,7 +1,9 @@ /* * QEMU PowerPC PowerNV machine model * - * Copyright (c) 2016, IBM Corporation. + * Copyright (c) 2016-2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2662,6 +2664,23 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, return total_count; } +static int pnv10_xive_broadcast(XiveFabric *xfb, + uint8_t nvt_blk, uint32_t nvt_idx, + uint8_t priority) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + + xpc->broadcast(xptr, nvt_blk, nvt_idx, priority); + } + return 0; +} + static bool pnv_machine_get_big_core(Object *obj, Error **errp) { PnvMachineState *pnv = PNV_MACHINE(obj); @@ -2795,6 +2814,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) pmc->dt_power_mgt = pnv_dt_power_mgt; xfc->match_nvt = pnv10_xive_match_nvt; + xfc->broadcast = pnv10_xive_broadcast; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index e78adaffa5..6a410c6f1a 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -442,6 +442,9 @@ struct XivePresenterClass { uint32_t logic_serv, XiveTCTXMatch *match); bool (*in_kernel)(const XivePresenter *xptr); uint32_t (*get_config)(XivePresenter *xptr); + int (*broadcast)(XivePresenter *xptr, + uint8_t nvt_blk, uint32_t nvt_idx, + uint8_t priority); }; int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, @@ -472,6 +475,8 @@ struct XiveFabricClass { uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match); + int (*broadcast)(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, + uint8_t priority); }; /* diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 65154f78d8..ebf301bb5b 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -120,6 +120,7 @@ uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, void xive2_tm_pull_os_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); +void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, From 071456d0c7d4783e3a2053e67cdf58d5f0610fdf Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:22 +1000 Subject: [PATCH 2569/2892] ppc/xive2: Process group backlog when pushing an OS context When pushing an OS context, we were already checking if there was a pending interrupt in the IPB and sending a notification if needed. We also need to check if there is a pending group interrupt stored in the NVG table. To avoid useless backlog scans, we only scan if the NVP belongs to a group. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/xive2.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 44d891f1f6..2fa7b90669 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -279,6 +279,85 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); } +/* + * Scan the group chain and return the highest priority and group + * level of pending group interrupts. + */ +static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, + uint8_t nvp_blk, uint32_t nvp_idx, + uint8_t first_group, + uint8_t *out_level) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t nvgc_idx, mask; + uint32_t current_level, count; + uint8_t prio; + Xive2Nvgc nvgc; + + for (prio = 0; prio <= XIVE_PRIORITY_MAX; prio++) { + current_level = first_group & 0xF; + + while (current_level) { + mask = (1 << current_level) - 1; + nvgc_idx = nvp_idx & ~mask; + nvgc_idx |= mask >> 1; + qemu_log("fxb %s checking backlog for prio %d group idx %x\n", + __func__, prio, nvgc_idx); + + if (xive2_router_get_nvgc(xrtr, false, nvp_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVG %x/%x\n", + nvp_blk, nvgc_idx); + return 0xFF; + } + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", + nvp_blk, nvgc_idx); + return 0xFF; + } + + count = xive2_nvgc_get_backlog(&nvgc, prio); + if (count) { + *out_level = current_level; + return prio; + } + current_level = xive_get_field32(NVGC2_W0_PGONEXT, nvgc.w0) & 0xF; + } + } + return 0xFF; +} + +static void xive2_presenter_backlog_decr(XivePresenter *xptr, + uint8_t nvp_blk, uint32_t nvp_idx, + uint8_t group_prio, + uint8_t group_level) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t nvgc_idx, mask, count; + Xive2Nvgc nvgc; + + group_level &= 0xF; + mask = (1 << group_level) - 1; + nvgc_idx = nvp_idx & ~mask; + nvgc_idx |= mask >> 1; + + if (xive2_router_get_nvgc(xrtr, false, nvp_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVG %x/%x\n", + nvp_blk, nvgc_idx); + return; + } + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", + nvp_blk, nvgc_idx); + return; + } + count = xive2_nvgc_get_backlog(&nvgc, group_prio); + if (!count) { + return; + } + xive2_nvgc_set_backlog(&nvgc, group_prio, count - 1); + xive2_router_write_nvgc(xrtr, false, nvp_blk, nvgc_idx, &nvgc); +} + /* * XIVE Thread Interrupt Management Area (TIMA) - Gen2 mode * @@ -588,9 +667,13 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { + XivePresenter *xptr = XIVE_PRESENTER(xrtr); uint8_t ipb; uint8_t backlog_level; + uint8_t group_level; + uint8_t first_group; uint8_t backlog_prio; + uint8_t group_prio; uint8_t *regs = &tctx->regs[TM_QW1_OS]; Xive2Nvp nvp; @@ -625,6 +708,20 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, backlog_prio = xive_ipb_to_pipr(ipb); backlog_level = 0; + first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); + if (first_group && regs[TM_LSMFB] < backlog_prio) { + group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx, + first_group, &group_level); + regs[TM_LSMFB] = group_prio; + if (regs[TM_LGS] && group_prio < backlog_prio) { + /* VP can take a group interrupt */ + xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx, + group_prio, group_level); + backlog_prio = group_prio; + backlog_level = group_level; + } + } + /* * Compute the PIPR based on the restored state. * It will raise the External interrupt signal if needed. From 26c55b99418173107897b28ffdb8171e913339e9 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:22 +1000 Subject: [PATCH 2570/2892] ppc/xive2: Process group backlog when updating the CPPR When the hypervisor or OS pushes a new value to the CPPR, if the LSMFB value is lower than the new CPPR value, there could be a pending group interrupt in the backlog, so it needs to be scanned. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/xive.c | 4 +- hw/intc/xive2.c | 173 ++++++++++++++++++++++++++++++++++++++++- include/hw/ppc/xive2.h | 4 + 3 files changed, 177 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 3e4c932f19..535e59646f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -589,7 +589,7 @@ static const XiveTmOp xive2_tm_operations[] = { * MMIOs below 2K : raw values and special operations without side * effects */ - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_tm_set_os_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx, NULL }, @@ -597,7 +597,7 @@ static const XiveTmOp xive2_tm_operations[] = { NULL }, { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive2_tm_set_hv_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL }, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 2fa7b90669..017c0f8bb4 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -17,6 +17,7 @@ #include "hw/ppc/xive.h" #include "hw/ppc/xive2.h" #include "hw/ppc/xive2_regs.h" +#include "trace.h" uint32_t xive2_router_get_config(Xive2Router *xrtr) { @@ -768,6 +769,172 @@ void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } +static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, + uint32_t *nvp_blk, uint32_t *nvp_idx) +{ + uint32_t w2, cam; + + w2 = xive_tctx_word2(&tctx->regs[ring]); + switch (ring) { + case TM_QW1_OS: + if (!(be32_to_cpu(w2) & TM2_QW1W2_VO)) { + return -1; + } + cam = xive_get_field32(TM2_QW1W2_OS_CAM, w2); + break; + case TM_QW2_HV_POOL: + if (!(be32_to_cpu(w2) & TM2_QW2W2_VP)) { + return -1; + } + cam = xive_get_field32(TM2_QW2W2_POOL_CAM, w2); + break; + case TM_QW3_HV_PHYS: + if (!(be32_to_cpu(w2) & TM2_QW3W2_VT)) { + return -1; + } + cam = xive2_tctx_hw_cam_line(tctx->xptr, tctx); + break; + default: + return -1; + } + *nvp_blk = xive2_nvp_blk(cam); + *nvp_idx = xive2_nvp_idx(cam); + return 0; +} + +static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) +{ + uint8_t *regs = &tctx->regs[ring]; + Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); + uint8_t old_cppr, backlog_prio, first_group, group_level = 0; + uint8_t pipr_min, lsmfb_min, ring_min; + bool group_enabled; + uint32_t nvp_blk, nvp_idx; + Xive2Nvp nvp; + int rc; + + trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, + regs[TM_IPB], regs[TM_PIPR], + cppr, regs[TM_NSR]); + + if (cppr > XIVE_PRIORITY_MAX) { + cppr = 0xff; + } + + old_cppr = regs[TM_CPPR]; + regs[TM_CPPR] = cppr; + + /* + * Recompute the PIPR based on local pending interrupts. It will + * be adjusted below if needed in case of pending group interrupts. + */ + pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); + group_enabled = !!regs[TM_LGS]; + lsmfb_min = (group_enabled) ? regs[TM_LSMFB] : 0xff; + ring_min = ring; + + /* PHYS updates also depend on POOL values */ + if (ring == TM_QW3_HV_PHYS) { + uint8_t *pregs = &tctx->regs[TM_QW2_HV_POOL]; + + /* POOL values only matter if POOL ctx is valid */ + if (pregs[TM_WORD2] & 0x80) { + + uint8_t pool_pipr = xive_ipb_to_pipr(pregs[TM_IPB]); + uint8_t pool_lsmfb = pregs[TM_LSMFB]; + + /* + * Determine highest priority interrupt and + * remember which ring has it. + */ + if (pool_pipr < pipr_min) { + pipr_min = pool_pipr; + if (pool_pipr < lsmfb_min) { + ring_min = TM_QW2_HV_POOL; + } + } + + /* Values needed for group priority calculation */ + if (pregs[TM_LGS] && (pool_lsmfb < lsmfb_min)) { + group_enabled = true; + lsmfb_min = pool_lsmfb; + if (lsmfb_min < pipr_min) { + ring_min = TM_QW2_HV_POOL; + } + } + } + } + regs[TM_PIPR] = pipr_min; + + rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); + if (rc) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n"); + return; + } + + if (cppr < old_cppr) { + /* + * FIXME: check if there's a group interrupt being presented + * and if the new cppr prevents it. If so, then the group + * interrupt needs to be re-added to the backlog and + * re-triggered (see re-trigger END info in the NVGC + * structure) + */ + } + + if (group_enabled && + lsmfb_min < cppr && + lsmfb_min < regs[TM_PIPR]) { + /* + * Thread has seen a group interrupt with a higher priority + * than the new cppr or pending local interrupt. Check the + * backlog + */ + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); + if (!first_group) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + backlog_prio = xive2_presenter_backlog_scan(tctx->xptr, + nvp_blk, nvp_idx, + first_group, &group_level); + tctx->regs[ring_min + TM_LSMFB] = backlog_prio; + if (backlog_prio != 0xFF) { + xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx, + backlog_prio, group_level); + regs[TM_PIPR] = backlog_prio; + } + } + /* CPPR has changed, check if we need to raise a pending exception */ + xive_tctx_notify(tctx, ring_min, group_level); +} + +void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_set_cppr(tctx, TM_QW3_HV_PHYS, value & 0xff); +} + +void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); +} + static void xive2_tctx_set_target(XiveTCTX *tctx, uint8_t ring, uint8_t target) { uint8_t *regs = &tctx->regs[ring]; @@ -938,7 +1105,9 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) { - uint8_t *regs = &tctx->regs[ring]; + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *alt_regs = &tctx->regs[alt_ring]; /* * The xive2_presenter_tctx_match() above tells if there's a match @@ -946,7 +1115,7 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) * priority to know if the thread can take the interrupt now or if * it is precluded. */ - if (priority < regs[TM_CPPR]) { + if (priority < alt_regs[TM_CPPR]) { return false; } return true; diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index ebf301bb5b..fc7422fea7 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -113,6 +113,10 @@ typedef struct Xive2EndSource { * XIVE2 Thread Interrupt Management Area (POWER10) */ +void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, From c2b7fade9f9bca1cc0ca91ab614ea5cee3629e2e Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:51:22 +1000 Subject: [PATCH 2571/2892] qtest/xive: Add group-interrupt test Add XIVE2 tests for group interrupts and group interrupts that have been backlogged. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- tests/qtest/pnv-xive2-test.c | 160 +++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c index dd19e88861..a4d06550ee 100644 --- a/tests/qtest/pnv-xive2-test.c +++ b/tests/qtest/pnv-xive2-test.c @@ -2,6 +2,8 @@ * QTest testcase for PowerNV 10 interrupt controller (xive2) * - Test irq to hardware thread * - Test 'Pull Thread Context to Odd Thread Reporting Line' + * - Test irq to hardware group + * - Test irq to hardware group going through backlog * * Copyright (c) 2024, IBM Corporation. * @@ -315,6 +317,158 @@ static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) word2 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); g_assert_cmphex(xive_get_field32(TM_QW3W2_VT, word2), ==, 0); } + +static void test_hw_group_irq(QTestState *qts) +{ + uint32_t irq = 100; + uint32_t irq_data = 0xdeadbeef; + uint32_t end_index = 23; + uint32_t chosen_one; + uint32_t target_nvp = 0x81; /* group size = 4 */ + uint8_t priority = 6; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr; + + printf("# ============================================================\n"); + printf("# Testing irq %d to hardware group of size 4\n", irq); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, true /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + /* find the targeted vCPU */ + for (chosen_one = 0; chosen_one < SMT; chosen_one++) { + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + if (nsr == 0x82) { + break; + } + } + g_assert_cmphex(chosen_one, <, SMT); + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, 0xFF); + + /* ack the irq */ + reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + +static void test_hw_group_irq_backlog(QTestState *qts) +{ + uint32_t irq = 31; + uint32_t irq_data = 0x01234567; + uint32_t end_index = 129; + uint32_t target_nvp = 0x81; /* group size = 4 */ + uint32_t chosen_one = 3; + uint8_t blocking_priority, priority = 3; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr, lsmfb, i; + + printf("# ============================================================\n"); + printf("# Testing irq %d to hardware group of size 4 going through " \ + "backlog\n", + irq); + + /* + * set current priority of all threads in the group to something + * higher than what we're about to trigger + */ + blocking_priority = priority - 1; + for (i = 0; i < SMT; i++) { + set_tima8(qts, i, TM_QW3_HV_PHYS + TM_CPPR, blocking_priority); + } + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, true /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + /* check no interrupt is pending on the 2 possible targets */ + for (i = 0; i < SMT; i++) { + reg32 = get_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + lsmfb = reg32 & 0xFF; + g_assert_cmphex(nsr, ==, 0x0); + g_assert_cmphex(cppr, ==, blocking_priority); + g_assert_cmphex(lsmfb, ==, priority); + } + + /* lower priority of one thread */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, priority + 1); + + /* check backlogged interrupt is presented */ + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority + 1); + + /* ack the irq */ + reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + lsmfb = reg32 & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); + g_assert_cmphex(lsmfb, ==, 0xFF); +} + static void test_xive(void) { QTestState *qts; @@ -330,6 +484,12 @@ static void test_xive(void) /* omit reset_state here and use settings from test_hw_irq */ test_pull_thread_ctx_to_odd_thread_cl(qts); + reset_state(qts); + test_hw_group_irq(qts); + + reset_state(qts); + test_hw_group_irq_backlog(qts); + reset_state(qts); test_flush_sync_inject(qts); From 96a2132ce95dab3e61002412839a118aecca0be0 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:53:45 +1000 Subject: [PATCH 2572/2892] ppc/xive2: Add support for MMIO operations on the NVPG/NVC BAR Add support for the NVPG and NVC BARs. Access to the BAR pages will cause backlog counter operations to either increment or decriment the counter. Also added qtests for the same. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/pnv_xive2.c | 80 +++++++++++++--- hw/intc/trace-events | 4 + hw/intc/xive2.c | 87 ++++++++++++++++++ include/hw/ppc/xive2.h | 9 ++ include/hw/ppc/xive2_regs.h | 3 + tests/qtest/meson.build | 3 +- tests/qtest/pnv-xive2-common.h | 1 + tests/qtest/pnv-xive2-nvpg_bar.c | 153 +++++++++++++++++++++++++++++++ tests/qtest/pnv-xive2-test.c | 3 + 9 files changed, 328 insertions(+), 15 deletions(-) create mode 100644 tests/qtest/pnv-xive2-nvpg_bar.c diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index e7a7d1b50f..c55d596e48 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2202,21 +2202,40 @@ static const MemoryRegionOps pnv_xive2_tm_ops = { }, }; -static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr offset, +static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr addr, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvpg_shift; + uint16_t op = addr & 0xFFF; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVC: invalid read @%"HWADDR_PRIx, offset); - return -1; + if (size != 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc load size %d\n", + size); + return -1; + } + + return xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, 1); } -static void pnv_xive2_nvc_write(void *opaque, hwaddr offset, +static void pnv_xive2_nvc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvc_shift; + uint16_t op = addr & 0xFFF; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVC: invalid write @%"HWADDR_PRIx, offset); + if (size != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc write size %d\n", + size); + return; + } + + (void)xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, val); } static const MemoryRegionOps pnv_xive2_nvc_ops = { @@ -2224,30 +2243,63 @@ static const MemoryRegionOps pnv_xive2_nvc_ops = { .write = pnv_xive2_nvc_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, .impl = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, }; -static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr offset, +static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr addr, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvpg_shift; + uint16_t op = addr & 0xFFF; + uint32_t index = page >> 1; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVPG: invalid read @%"HWADDR_PRIx, offset); - return -1; + if (size != 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg load size %d\n", + size); + return -1; + } + + if (page % 2) { + /* odd page - NVG */ + return xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, 1); + } else { + /* even page - NVP */ + return xive2_presenter_nvp_backlog_op(xptr, blk, index, op); + } } -static void pnv_xive2_nvpg_write(void *opaque, hwaddr offset, +static void pnv_xive2_nvpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvpg_shift; + uint16_t op = addr & 0xFFF; + uint32_t index = page >> 1; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVPG: invalid write @%"HWADDR_PRIx, offset); + if (size != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg write size %d\n", + size); + return; + } + + if (page % 2) { + /* odd page - NVG */ + (void)xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, val); + } else { + /* even page - NVP */ + (void)xive2_presenter_nvp_backlog_op(xptr, blk, index, op); + } } static const MemoryRegionOps pnv_xive2_nvpg_ops = { @@ -2255,11 +2307,11 @@ static const MemoryRegionOps pnv_xive2_nvpg_ops = { .write = pnv_xive2_nvpg_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, .impl = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, }; diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e9fe1978f9..0ba9a02e73 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -286,6 +286,10 @@ xive_tctx_tm_read(uint32_t index, uint64_t offset, unsigned int size, uint64_t v xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring, uint8_t group_level) "found NVT 0x%x/0x%x ring=0x%x group_level=%d" xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64 +# xive2.c +xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d" +xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d" + # pnv_xive.c pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64 diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 017c0f8bb4..34628f2922 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -88,6 +88,93 @@ static void xive2_nvgc_set_backlog(Xive2Nvgc *nvgc, uint8_t priority, } } +uint64_t xive2_presenter_nvgc_backlog_op(XivePresenter *xptr, + bool crowd, + uint8_t blk, uint32_t idx, + uint16_t offset, uint16_t val) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t priority = GETFIELD(NVx_BACKLOG_PRIO, offset); + uint8_t op = GETFIELD(NVx_BACKLOG_OP, offset); + Xive2Nvgc nvgc; + uint32_t count, old_count; + + if (xive2_router_get_nvgc(xrtr, crowd, blk, idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No %s %x/%x\n", + crowd ? "NVC" : "NVG", blk, idx); + return -1; + } + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", blk, idx); + return -1; + } + + old_count = xive2_nvgc_get_backlog(&nvgc, priority); + count = old_count; + /* + * op: + * 0b00 => increment + * 0b01 => decrement + * 0b1- => read + */ + if (op == 0b00 || op == 0b01) { + if (op == 0b00) { + count += val; + } else { + if (count > val) { + count -= val; + } else { + count = 0; + } + } + xive2_nvgc_set_backlog(&nvgc, priority, count); + xive2_router_write_nvgc(xrtr, crowd, blk, idx, &nvgc); + } + trace_xive_nvgc_backlog_op(crowd, blk, idx, op, priority, old_count); + return old_count; +} + +uint64_t xive2_presenter_nvp_backlog_op(XivePresenter *xptr, + uint8_t blk, uint32_t idx, + uint16_t offset) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t priority = GETFIELD(NVx_BACKLOG_PRIO, offset); + uint8_t op = GETFIELD(NVx_BACKLOG_OP, offset); + Xive2Nvp nvp; + uint8_t ipb, old_ipb, rc; + + if (xive2_router_get_nvp(xrtr, blk, idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", blk, idx); + return -1; + } + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVP %x/%x\n", blk, idx); + return -1; + } + + old_ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); + ipb = old_ipb; + /* + * op: + * 0b00 => set priority bit + * 0b01 => reset priority bit + * 0b1- => read + */ + if (op == 0b00 || op == 0b01) { + if (op == 0b00) { + ipb |= xive_priority_to_ipb(priority); + } else { + ipb &= ~xive_priority_to_ipb(priority); + } + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); + xive2_router_write_nvp(xrtr, blk, idx, &nvp, 2); + } + rc = !!(old_ipb & xive_priority_to_ipb(priority)); + trace_xive_nvp_backlog_op(blk, idx, op, priority, rc); + return rc; +} + void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) { if (!xive2_eas_is_valid(eas)) { diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index fc7422fea7..c07e23e1d3 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -90,6 +90,15 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint32_t logic_serv); +uint64_t xive2_presenter_nvp_backlog_op(XivePresenter *xptr, + uint8_t blk, uint32_t idx, + uint16_t offset); + +uint64_t xive2_presenter_nvgc_backlog_op(XivePresenter *xptr, + bool crowd, + uint8_t blk, uint32_t idx, + uint16_t offset, uint16_t val); + /* * XIVE2 END ESBs (POWER10) */ diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index e88d6eab1e..9bcf7a8a6f 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -233,4 +233,7 @@ typedef struct Xive2Nvgc { void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, GString *buf); +#define NVx_BACKLOG_OP PPC_BITMASK(52, 53) +#define NVx_BACKLOG_PRIO PPC_BITMASK(57, 59) + #endif /* PPC_XIVE2_REGS_H */ diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index b23fe67db7..3ecb23e610 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -368,7 +368,8 @@ qtests = { 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': migration_files + migration_tls_files, 'pxe-test': files('boot-sector.c'), - 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c'), + 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c', + 'pnv-xive2-nvpg_bar.c'), 'qos-test': [chardev, io, qos_test_ss.apply({}).sources()], 'tpm-crb-swtpm-test': [io, tpmemu_files], 'tpm-crb-test': [io, tpmemu_files], diff --git a/tests/qtest/pnv-xive2-common.h b/tests/qtest/pnv-xive2-common.h index 9ae34771aa..2077c05ebc 100644 --- a/tests/qtest/pnv-xive2-common.h +++ b/tests/qtest/pnv-xive2-common.h @@ -107,5 +107,6 @@ extern void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, void test_flush_sync_inject(QTestState *qts); +void test_nvpg_bar(QTestState *qts); #endif /* TEST_PNV_XIVE2_COMMON_H */ diff --git a/tests/qtest/pnv-xive2-nvpg_bar.c b/tests/qtest/pnv-xive2-nvpg_bar.c new file mode 100644 index 0000000000..028512bddc --- /dev/null +++ b/tests/qtest/pnv-xive2-nvpg_bar.c @@ -0,0 +1,153 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test NVPG BAR MMIO operations + * + * Copyright (c) 2024, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" + +#define NVPG_BACKLOG_OP_SHIFT 10 +#define NVPG_BACKLOG_PRIO_SHIFT 4 + +#define XIVE_PRIORITY_MAX 7 + +enum NVx { + NVP, + NVG, + NVC +}; + +typedef enum { + INCR_STORE = 0b100, + INCR_LOAD = 0b000, + DECR_STORE = 0b101, + DECR_LOAD = 0b001, + READ_x = 0b010, + READ_y = 0b011, +} backlog_op; + +static uint32_t nvpg_backlog_op(QTestState *qts, backlog_op op, + enum NVx type, uint64_t index, + uint8_t priority, uint8_t delta) +{ + uint64_t addr, offset; + uint32_t count = 0; + + switch (type) { + case NVP: + addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)); + break; + case NVG: + addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)) + + (1 << XIVE_PAGE_SHIFT); + break; + case NVC: + addr = XIVE_NVC_ADDR + (index << XIVE_PAGE_SHIFT); + break; + default: + g_assert_not_reached(); + } + + offset = (op & 0b11) << NVPG_BACKLOG_OP_SHIFT; + offset |= priority << NVPG_BACKLOG_PRIO_SHIFT; + if (op >> 2) { + qtest_writeb(qts, addr + offset, delta); + } else { + count = qtest_readw(qts, addr + offset); + } + return count; +} + +void test_nvpg_bar(QTestState *qts) +{ + uint32_t nvp_target = 0x11; + uint32_t group_target = 0x17; /* size 16 */ + uint32_t vp_irq = 33, group_irq = 47; + uint32_t vp_end = 3, group_end = 97; + uint32_t vp_irq_data = 0x33333333; + uint32_t group_irq_data = 0x66666666; + uint8_t vp_priority = 0, group_priority = 5; + uint32_t vp_count[XIVE_PRIORITY_MAX + 1] = { 0 }; + uint32_t group_count[XIVE_PRIORITY_MAX + 1] = { 0 }; + uint32_t count, delta; + uint8_t i; + + printf("# ============================================================\n"); + printf("# Testing NVPG BAR operations\n"); + + set_nvg(qts, group_target, 0); + set_nvp(qts, nvp_target, 0x04); + set_nvp(qts, group_target, 0x04); + + /* + * Setup: trigger a VP-specific interrupt and a group interrupt + * so that the backlog counters are initialized to something else + * than 0 for at least one priority level + */ + set_eas(qts, vp_irq, vp_end, vp_irq_data); + set_end(qts, vp_end, nvp_target, vp_priority, false /* group */); + + set_eas(qts, group_irq, group_end, group_irq_data); + set_end(qts, group_end, group_target, group_priority, true /* group */); + + get_esb(qts, vp_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, vp_irq, XIVE_TRIGGER_PAGE, 0, 0); + vp_count[vp_priority]++; + + get_esb(qts, group_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, group_irq, XIVE_TRIGGER_PAGE, 0, 0); + group_count[group_priority]++; + + /* check the initial counters */ + for (i = 0; i <= XIVE_PRIORITY_MAX; i++) { + count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, i, 0); + g_assert_cmpuint(count, ==, vp_count[i]); + + count = nvpg_backlog_op(qts, READ_y, NVG, group_target, i, 0); + g_assert_cmpuint(count, ==, group_count[i]); + } + + /* do a few ops on the VP. Counter can only be 0 and 1 */ + vp_priority = 2; + delta = 7; + nvpg_backlog_op(qts, INCR_STORE, NVP, nvp_target, vp_priority, delta); + vp_count[vp_priority] = 1; + count = nvpg_backlog_op(qts, INCR_LOAD, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + count = nvpg_backlog_op(qts, READ_y, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + + count = nvpg_backlog_op(qts, DECR_LOAD, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + vp_count[vp_priority] = 0; + nvpg_backlog_op(qts, DECR_STORE, NVP, nvp_target, vp_priority, delta); + count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + + /* do a few ops on the group */ + group_priority = 2; + delta = 9; + /* can't go negative */ + nvpg_backlog_op(qts, DECR_STORE, NVG, group_target, group_priority, delta); + count = nvpg_backlog_op(qts, READ_y, NVG, group_target, group_priority, 0); + g_assert_cmpuint(count, ==, 0); + nvpg_backlog_op(qts, INCR_STORE, NVG, group_target, group_priority, delta); + group_count[group_priority] += delta; + count = nvpg_backlog_op(qts, INCR_LOAD, NVG, group_target, + group_priority, delta); + g_assert_cmpuint(count, ==, group_count[group_priority]); + group_count[group_priority]++; + + count = nvpg_backlog_op(qts, DECR_LOAD, NVG, group_target, + group_priority, delta); + g_assert_cmpuint(count, ==, group_count[group_priority]); + group_count[group_priority]--; + count = nvpg_backlog_op(qts, READ_x, NVG, group_target, group_priority, 0); + g_assert_cmpuint(count, ==, group_count[group_priority]); +} diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c index a4d06550ee..a0e9f19313 100644 --- a/tests/qtest/pnv-xive2-test.c +++ b/tests/qtest/pnv-xive2-test.c @@ -493,6 +493,9 @@ static void test_xive(void) reset_state(qts); test_flush_sync_inject(qts); + reset_state(qts); + test_nvpg_bar(qts); + qtest_quit(qts); } From 1a3cc1209b4ca541e3b8f4fd360704e071e1e7bc Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:53:52 +1000 Subject: [PATCH 2573/2892] ppc/xive2: Support crowd-matching when looking for target XIVE crowd sizes are encoded into a 2-bit field as follows: 0: 0b00 2: 0b01 4: 0b10 16: 0b11 A crowd size of 8 is not supported. If an END is defined with the 'crowd' bit set, then a target can be running on different blocks. It means that some bits from the block VP are masked when looking for a match. It is similar to groups, but on the block instead of the VP index. Most of the changes are due to passing the extra argument 'crowd' all the way to the function checking for matches. Signed-off-by: Frederic Barrat Signed-off-by: Glenn Miles Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/pnv_xive.c | 10 +++--- hw/intc/pnv_xive2.c | 12 +++---- hw/intc/spapr_xive.c | 8 ++--- hw/intc/xive.c | 45 +++++++++++++++++++++---- hw/intc/xive2.c | 76 +++++++++++++++++++++++++++++++++--------- hw/ppc/pnv.c | 15 +++++---- hw/ppc/spapr.c | 7 ++-- include/hw/ppc/xive.h | 10 +++--- include/hw/ppc/xive2.h | 3 +- 9 files changed, 134 insertions(+), 52 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index b755ddf0ff..ccbe95a58e 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC XIVE interrupt controller model * - * Copyright (c) 2017-2019, IBM Corporation. + * Copyright (c) 2017-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" @@ -473,7 +472,7 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu) static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive *xive = PNV_XIVE(xptr); @@ -500,7 +499,8 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, * Check the thread context CAM lines and record matches. */ ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk, - nvt_idx, cam_ignore, logic_serv); + nvt_idx, cam_ignore, + logic_serv); /* * Save the context and follow on to catch duplicates, that we * don't support yet. diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index c55d596e48..0b81dad6ba 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -624,7 +624,7 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu) static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive2 *xive = PNV_XIVE2(xptr); @@ -655,8 +655,8 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, logic_serv); } else { ring = xive2_presenter_tctx_match(xptr, tctx, format, nvt_blk, - nvt_idx, cam_ignore, - logic_serv); + nvt_idx, crowd, cam_ignore, + logic_serv); } if (ring != -1) { @@ -707,7 +707,7 @@ static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr) static int pnv_xive2_broadcast(XivePresenter *xptr, uint8_t nvt_blk, uint32_t nvt_idx, - uint8_t priority) + bool crowd, bool ignore, uint8_t priority) { PnvXive2 *xive = PNV_XIVE2(xptr); PnvChip *chip = xive->chip; @@ -732,10 +732,10 @@ static int pnv_xive2_broadcast(XivePresenter *xptr, if (gen1_tima_os) { ring = xive_presenter_tctx_match(xptr, tctx, 0, nvt_blk, - nvt_idx, true, 0); + nvt_idx, ignore, 0); } else { ring = xive2_presenter_tctx_match(xptr, tctx, 0, nvt_blk, - nvt_idx, true, 0); + nvt_idx, crowd, ignore, 0); } if (ring != -1) { diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index a764c0bb57..ce734b03ab 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC sPAPR XIVE interrupt controller model * - * Copyright (c) 2017-2018, IBM Corporation. + * Copyright (c) 2017-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" @@ -431,7 +430,8 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, + uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { CPUState *cs; diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 535e59646f..c77df2c1f8 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1665,10 +1665,42 @@ uint32_t xive_get_vpgroup_size(uint32_t nvp_index) return 1 << (ctz32(~nvp_index) + 1); } -static uint8_t xive_get_group_level(uint32_t nvp_index) +static uint8_t xive_get_group_level(bool crowd, bool ignore, + uint32_t nvp_blk, uint32_t nvp_index) { - /* FIXME add crowd encoding */ - return ctz32(~nvp_index) + 1; + uint8_t level; + + if (!ignore) { + g_assert(!crowd); + return 0; + } + + level = (ctz32(~nvp_index) + 1) & 0b1111; + if (crowd) { + uint32_t blk; + + /* crowd level is bit position of first 0 from the right in nvp_blk */ + blk = ctz32(~nvp_blk) + 1; + + /* + * Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported. + * HW will encode level 4 as the value 3. See xive2_pgofnext(). + */ + switch (level) { + case 1: + case 2: + break; + case 4: + blk = 3; + break; + default: + g_assert_not_reached(); + } + + /* Crowd level bits reside in upper 2 bits of the 6 bit group level */ + level |= blk << 4; + } + return level; } /* @@ -1740,7 +1772,7 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, */ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, bool *precluded) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); @@ -1771,7 +1803,7 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, * a new command to the presenters (the equivalent of the "assign" * power bus command in the documented full notify sequence. */ - count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore, + count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore, priority, logic_serv, &match); if (count < 0) { return false; @@ -1779,7 +1811,7 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, /* handle CPU exception delivery */ if (count) { - group_level = cam_ignore ? xive_get_group_level(nvt_idx) : 0; + group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx); trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level); xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); } else { @@ -1904,6 +1936,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) } found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx, + false /* crowd */, xive_get_field32(END_W7_F0_IGNORE, end.w7), priority, xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 34628f2922..5fa8e1b3fb 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1121,13 +1121,40 @@ static bool xive2_vp_match_mask(uint32_t cam1, uint32_t cam2, return (cam1 & vp_mask) == (cam2 & vp_mask); } +static uint8_t xive2_get_vp_block_mask(uint32_t nvt_blk, bool crowd) +{ + uint8_t size, block_mask = 0b1111; + + /* 3 supported crowd sizes: 2, 4, 16 */ + if (crowd) { + size = xive_get_vpgroup_size(nvt_blk); + if (size == 8) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd size of 8n"); + return block_mask; + } + block_mask &= ~(size - 1); + } + return block_mask; +} + +static uint32_t xive2_get_vp_index_mask(uint32_t nvt_index, bool cam_ignore) +{ + uint32_t index_mask = 0xFFFFFF; /* 24 bits */ + + if (cam_ignore) { + index_mask &= ~(xive_get_vpgroup_size(nvt_index) - 1); + } + return index_mask; +} + /* * The thread context register words are in big-endian format. */ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint32_t logic_serv) + bool crowd, bool cam_ignore, + uint32_t logic_serv) { uint32_t cam = xive2_nvp_cam_line(nvt_blk, nvt_idx); uint32_t qw3w2 = xive_tctx_word2(&tctx->regs[TM_QW3_HV_PHYS]); @@ -1135,7 +1162,8 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); uint32_t qw0w2 = xive_tctx_word2(&tctx->regs[TM_QW0_USER]); - uint32_t vp_mask = 0xFFFFFFFF; + uint32_t index_mask, vp_mask; + uint8_t block_mask; if (format == 0) { /* @@ -1143,9 +1171,9 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, * i=1: VP-group notification (bits ignored at the end of the * NVT identifier) */ - if (cam_ignore) { - vp_mask = ~(xive_get_vpgroup_size(nvt_idx) - 1); - } + block_mask = xive2_get_vp_block_mask(nvt_blk, crowd); + index_mask = xive2_get_vp_index_mask(nvt_idx, cam_ignore); + vp_mask = xive2_nvp_cam_line(block_mask, index_mask); /* For VP-group notifications, threads with LGS=0 are excluded */ @@ -1277,6 +1305,12 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } + if (xive2_end_is_crowd(&end) & !xive2_end_is_ignore(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid END, 'crowd' bit requires 'ignore' bit\n"); + return; + } + if (xive2_end_is_enqueue(&end)) { xive2_end_enqueue(&end, end_data); /* Enqueuing event data modifies the EQ toggle and index */ @@ -1325,7 +1359,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, - xive2_end_is_ignore(&end), + xive2_end_is_crowd(&end), xive2_end_is_ignore(&end), priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), &precluded); @@ -1377,17 +1411,24 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); } else { - Xive2Nvgc nvg; + Xive2Nvgc nvgc; uint32_t backlog; + bool crowd; - /* For groups, the per-priority backlog counters are in the NVG */ - if (xive2_router_get_nvgc(xrtr, false, nvp_blk, nvp_idx, &nvg)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVG %x/%x\n", - nvp_blk, nvp_idx); + crowd = xive2_end_is_crowd(&end); + + /* + * For groups and crowds, the per-priority backlog + * counters are stored in the NVG/NVC structures + */ + if (xive2_router_get_nvgc(xrtr, crowd, + nvp_blk, nvp_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no %s %x/%x\n", + crowd ? "NVC" : "NVG", nvp_blk, nvp_idx); return; } - if (!xive2_nvgc_is_valid(&nvg)) { + if (!xive2_nvgc_is_valid(&nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVG %x/%x is invalid\n", nvp_blk, nvp_idx); return; @@ -1399,13 +1440,16 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, * incremented. broadcast will set the LSMFB field of the TIMA of * relevant threads so that they know an interrupt is pending. */ - backlog = xive2_nvgc_get_backlog(&nvg, priority) + 1; - xive2_nvgc_set_backlog(&nvg, priority, backlog); - xive2_router_write_nvgc(xrtr, false, nvp_blk, nvp_idx, &nvg); + backlog = xive2_nvgc_get_backlog(&nvgc, priority) + 1; + xive2_nvgc_set_backlog(&nvgc, priority, backlog); + xive2_router_write_nvgc(xrtr, crowd, nvp_blk, nvp_idx, &nvgc); if (backlog == 1) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb); - xfc->broadcast(xrtr->xfb, nvp_blk, nvp_idx, priority); + xfc->broadcast(xrtr->xfb, nvp_blk, nvp_idx, + xive2_end_is_crowd(&end), + xive2_end_is_ignore(&end), + priority); if (!xive2_end_is_precluded_escalation(&end)) { /* diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index af836c1388..d60574d601 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2608,7 +2608,7 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { @@ -2622,8 +2622,8 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, - priority, logic_serv, match); + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); if (count < 0) { return count; @@ -2637,7 +2637,7 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { @@ -2651,8 +2651,8 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, - priority, logic_serv, match); + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); if (count < 0) { return count; @@ -2666,6 +2666,7 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, static int pnv10_xive_broadcast(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority) { PnvMachineState *pnv = PNV_MACHINE(xfb); @@ -2676,7 +2677,7 @@ static int pnv10_xive_broadcast(XiveFabric *xfb, XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - xpc->broadcast(xptr, nvt_blk, nvt_idx, priority); + xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority); } return 0; } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index c15340a58d..c7cf04e063 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4,6 +4,9 @@ * Copyright (c) 2004-2007 Fabrice Bellard * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2010 David Gibson, IBM Corporation. + * Copyright (c) 2010-2024, IBM Corporation.. + * + * SPDX-License-Identifier: GPL-2.0-or-later * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -4436,7 +4439,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf) */ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { SpaprMachineState *spapr = SPAPR_MACHINE(xfb); @@ -4444,7 +4447,7 @@ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, priority, logic_serv, match); if (count < 0) { return count; diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 6a410c6f1a..538f438681 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -438,13 +438,13 @@ struct XivePresenterClass { InterfaceClass parent; int (*match_nvt)(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match); bool (*in_kernel)(const XivePresenter *xptr); uint32_t (*get_config)(XivePresenter *xptr); int (*broadcast)(XivePresenter *xptr, uint8_t nvt_blk, uint32_t nvt_idx, - uint8_t priority); + bool crowd, bool cam_ignore, uint8_t priority); }; int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, @@ -453,7 +453,7 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, bool cam_ignore, uint32_t logic_serv); bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, bool *precluded); uint32_t xive_get_vpgroup_size(uint32_t nvp_index); @@ -473,10 +473,10 @@ struct XiveFabricClass { InterfaceClass parent; int (*match_nvt)(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match); int (*broadcast)(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, - uint8_t priority); + bool crowd, bool cam_ignore, uint8_t priority); }; /* diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index c07e23e1d3..8cdf819174 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -88,7 +88,8 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint32_t logic_serv); + bool crowd, bool cam_ignore, + uint32_t logic_serv); uint64_t xive2_presenter_nvp_backlog_op(XivePresenter *xptr, uint8_t blk, uint32_t idx, From 7988ac082603164937becea2910ea4fec8e9d3de Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 11 Mar 2025 12:26:33 +1000 Subject: [PATCH 2574/2892] pnv/xive2: Rename nvp_ to nvx_ if they can refer to NVP or NVGC The blk/index in some paths may refer to an NVP or an NVGC. When it is not known ahead of time, use the nvx_ prefix to prevent confusion. [npiggin: split out of larger fix patch and reworded] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/xive2.c | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 5fa8e1b3fb..e925307d0f 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -226,8 +226,8 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); uint32_t qentries = 1 << (qsize + 10); - uint32_t nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); - uint32_t nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); + uint32_t nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); + uint32_t nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); uint8_t priority = xive_get_field32(END2_W7_F0_PRIORITY, end->w7); uint8_t pq; @@ -256,7 +256,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) xive2_end_is_firmware2(end) ? 'F' : '-', xive2_end_is_ignore(end) ? 'i' : '-', xive2_end_is_crowd(end) ? 'c' : '-', - priority, nvp_blk, nvp_idx); + priority, nvx_blk, nvx_idx); if (qaddr_base) { g_string_append_printf(buf, " eq:@%08"PRIx64"% 6d/%5d ^%d", @@ -372,7 +372,7 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) * level of pending group interrupts. */ static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, - uint8_t nvp_blk, uint32_t nvp_idx, + uint8_t nvx_blk, uint32_t nvx_idx, uint8_t first_group, uint8_t *out_level) { @@ -387,19 +387,19 @@ static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, while (current_level) { mask = (1 << current_level) - 1; - nvgc_idx = nvp_idx & ~mask; + nvgc_idx = nvx_idx & ~mask; nvgc_idx |= mask >> 1; qemu_log("fxb %s checking backlog for prio %d group idx %x\n", __func__, prio, nvgc_idx); - if (xive2_router_get_nvgc(xrtr, false, nvp_blk, nvgc_idx, &nvgc)) { + if (xive2_router_get_nvgc(xrtr, false, nvx_blk, nvgc_idx, &nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVG %x/%x\n", - nvp_blk, nvgc_idx); + nvx_blk, nvgc_idx); return 0xFF; } if (!xive2_nvgc_is_valid(&nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", - nvp_blk, nvgc_idx); + nvx_blk, nvgc_idx); return 0xFF; } @@ -415,7 +415,7 @@ static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, } static void xive2_presenter_backlog_decr(XivePresenter *xptr, - uint8_t nvp_blk, uint32_t nvp_idx, + uint8_t nvx_blk, uint32_t nvx_idx, uint8_t group_prio, uint8_t group_level) { @@ -425,17 +425,17 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr, group_level &= 0xF; mask = (1 << group_level) - 1; - nvgc_idx = nvp_idx & ~mask; + nvgc_idx = nvx_idx & ~mask; nvgc_idx |= mask >> 1; - if (xive2_router_get_nvgc(xrtr, false, nvp_blk, nvgc_idx, &nvgc)) { + if (xive2_router_get_nvgc(xrtr, false, nvx_blk, nvgc_idx, &nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVG %x/%x\n", - nvp_blk, nvgc_idx); + nvx_blk, nvgc_idx); return; } if (!xive2_nvgc_is_valid(&nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", - nvp_blk, nvgc_idx); + nvx_blk, nvgc_idx); return; } count = xive2_nvgc_get_backlog(&nvgc, group_prio); @@ -443,7 +443,7 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr, return; } xive2_nvgc_set_backlog(&nvgc, group_prio, count - 1); - xive2_router_write_nvgc(xrtr, false, nvp_blk, nvgc_idx, &nvgc); + xive2_router_write_nvgc(xrtr, false, nvx_blk, nvgc_idx, &nvgc); } /* @@ -1289,8 +1289,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, uint8_t priority; uint8_t format; bool found, precluded; - uint8_t nvp_blk; - uint32_t nvp_idx; + uint8_t nvx_blk; + uint32_t nvx_idx; /* END cache lookup */ if (xive2_router_get_end(xrtr, end_blk, end_idx, &end)) { @@ -1355,10 +1355,10 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, /* * Follows IVPE notification */ - nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); - nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); + nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); + nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); - found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, + found = xive_presenter_notify(xrtr->xfb, format, nvx_blk, nvx_idx, xive2_end_is_crowd(&end), xive2_end_is_ignore(&end), priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), @@ -1389,15 +1389,15 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, Xive2Nvp nvp; /* NVP cache lookup */ - if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + if (xive2_router_get_nvp(xrtr, nvx_blk, nvx_idx, &nvp)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVP %x/%x\n", - nvp_blk, nvp_idx); + nvx_blk, nvx_idx); return; } if (!xive2_nvp_is_valid(&nvp)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is invalid\n", - nvp_blk, nvp_idx); + nvx_blk, nvx_idx); return; } @@ -1409,7 +1409,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2) | xive_priority_to_ipb(priority); nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); - xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + xive2_router_write_nvp(xrtr, nvx_blk, nvx_idx, &nvp, 2); } else { Xive2Nvgc nvgc; uint32_t backlog; @@ -1422,15 +1422,15 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, * counters are stored in the NVG/NVC structures */ if (xive2_router_get_nvgc(xrtr, crowd, - nvp_blk, nvp_idx, &nvgc)) { + nvx_blk, nvx_idx, &nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no %s %x/%x\n", - crowd ? "NVC" : "NVG", nvp_blk, nvp_idx); + crowd ? "NVC" : "NVG", nvx_blk, nvx_idx); return; } if (!xive2_nvgc_is_valid(&nvgc)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVG %x/%x is invalid\n", - nvp_blk, nvp_idx); + nvx_blk, nvx_idx); return; } @@ -1442,11 +1442,11 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, */ backlog = xive2_nvgc_get_backlog(&nvgc, priority) + 1; xive2_nvgc_set_backlog(&nvgc, priority, backlog); - xive2_router_write_nvgc(xrtr, crowd, nvp_blk, nvp_idx, &nvgc); + xive2_router_write_nvgc(xrtr, crowd, nvx_blk, nvx_idx, &nvgc); if (backlog == 1) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb); - xfc->broadcast(xrtr->xfb, nvp_blk, nvp_idx, + xfc->broadcast(xrtr->xfb, nvx_blk, nvx_idx, xive2_end_is_crowd(&end), xive2_end_is_ignore(&end), priority); From 0b266ae15e8c00bd8e72a55bc0bfc1a89c20bc34 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Tue, 11 Mar 2025 11:53:53 +1000 Subject: [PATCH 2575/2892] ppc/xive2: Check crowd backlog when scanning group backlog When processing a backlog scan for group interrupts, also take into account crowd interrupts. Signed-off-by: Frederic Barrat Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- hw/intc/xive2.c | 80 +++++++++++++++++++++++++------------ include/hw/ppc/xive2_regs.h | 4 ++ 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index e925307d0f..f8ef615487 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -367,6 +367,35 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); } +static void xive2_pgofnext(uint8_t *nvgc_blk, uint32_t *nvgc_idx, + uint8_t next_level) +{ + uint32_t mask, next_idx; + uint8_t next_blk; + + /* + * Adjust the block and index of a VP for the next group/crowd + * size (PGofFirst/PGofNext field in the NVP and NVGC structures). + * + * The 6-bit group level is split into a 2-bit crowd and 4-bit + * group levels. Encoding is similar. However, we don't support + * crowd size of 8. So a crowd level of 0b11 is bumped to a crowd + * size of 16. + */ + next_blk = NVx_CROWD_LVL(next_level); + if (next_blk == 3) { + next_blk = 4; + } + mask = (1 << next_blk) - 1; + *nvgc_blk &= ~mask; + *nvgc_blk |= mask >> 1; + + next_idx = NVx_GROUP_LVL(next_level); + mask = (1 << next_idx) - 1; + *nvgc_idx &= ~mask; + *nvgc_idx |= mask >> 1; +} + /* * Scan the group chain and return the highest priority and group * level of pending group interrupts. @@ -377,29 +406,28 @@ static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, uint8_t *out_level) { Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint32_t nvgc_idx, mask; + uint32_t nvgc_idx; uint32_t current_level, count; - uint8_t prio; + uint8_t nvgc_blk, prio; Xive2Nvgc nvgc; for (prio = 0; prio <= XIVE_PRIORITY_MAX; prio++) { - current_level = first_group & 0xF; + current_level = first_group & 0x3F; + nvgc_blk = nvx_blk; + nvgc_idx = nvx_idx; while (current_level) { - mask = (1 << current_level) - 1; - nvgc_idx = nvx_idx & ~mask; - nvgc_idx |= mask >> 1; - qemu_log("fxb %s checking backlog for prio %d group idx %x\n", - __func__, prio, nvgc_idx); + xive2_pgofnext(&nvgc_blk, &nvgc_idx, current_level); - if (xive2_router_get_nvgc(xrtr, false, nvx_blk, nvgc_idx, &nvgc)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVG %x/%x\n", - nvx_blk, nvgc_idx); + if (xive2_router_get_nvgc(xrtr, NVx_CROWD_LVL(current_level), + nvgc_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVGC %x/%x\n", + nvgc_blk, nvgc_idx); return 0xFF; } if (!xive2_nvgc_is_valid(&nvgc)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", - nvx_blk, nvgc_idx); + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVGC %x/%x\n", + nvgc_blk, nvgc_idx); return 0xFF; } @@ -408,7 +436,7 @@ static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, *out_level = current_level; return prio; } - current_level = xive_get_field32(NVGC2_W0_PGONEXT, nvgc.w0) & 0xF; + current_level = xive_get_field32(NVGC2_W0_PGONEXT, nvgc.w0) & 0x3F; } } return 0xFF; @@ -420,22 +448,23 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr, uint8_t group_level) { Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint32_t nvgc_idx, mask, count; + uint32_t nvgc_idx, count; + uint8_t nvgc_blk; Xive2Nvgc nvgc; - group_level &= 0xF; - mask = (1 << group_level) - 1; - nvgc_idx = nvx_idx & ~mask; - nvgc_idx |= mask >> 1; + nvgc_blk = nvx_blk; + nvgc_idx = nvx_idx; + xive2_pgofnext(&nvgc_blk, &nvgc_idx, group_level); - if (xive2_router_get_nvgc(xrtr, false, nvx_blk, nvgc_idx, &nvgc)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVG %x/%x\n", - nvx_blk, nvgc_idx); + if (xive2_router_get_nvgc(xrtr, NVx_CROWD_LVL(group_level), + nvgc_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVGC %x/%x\n", + nvgc_blk, nvgc_idx); return; } if (!xive2_nvgc_is_valid(&nvgc)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", - nvx_blk, nvgc_idx); + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVGC %x/%x\n", + nvgc_blk, nvgc_idx); return; } count = xive2_nvgc_get_backlog(&nvgc, group_prio); @@ -443,7 +472,8 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr, return; } xive2_nvgc_set_backlog(&nvgc, group_prio, count - 1); - xive2_router_write_nvgc(xrtr, false, nvx_blk, nvgc_idx, &nvgc); + xive2_router_write_nvgc(xrtr, NVx_CROWD_LVL(group_level), + nvgc_blk, nvgc_idx, &nvgc); } /* diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 9bcf7a8a6f..b11395c563 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -236,4 +236,8 @@ void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, #define NVx_BACKLOG_OP PPC_BITMASK(52, 53) #define NVx_BACKLOG_PRIO PPC_BITMASK(57, 59) +/* split the 6-bit crowd/group level */ +#define NVx_CROWD_LVL(level) ((level >> 4) & 0b11) +#define NVx_GROUP_LVL(level) (level & 0b1111) + #endif /* PPC_XIVE2_REGS_H */ From c4b50387b41fa86217743fe5b182eb5730e1a9c8 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 11 Mar 2025 13:36:14 +1000 Subject: [PATCH 2576/2892] qtest/xive: Change printf to g_test_message Change all printf() in pnv-xive2-* qtests to g_test_message() [npiggin: split from pool qtest] Signed-off-by: Glenn Miles Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- tests/qtest/pnv-xive2-flush-sync.c | 6 +++--- tests/qtest/pnv-xive2-nvpg_bar.c | 7 +++---- tests/qtest/pnv-xive2-test.c | 22 ++++++++++++---------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/qtest/pnv-xive2-flush-sync.c b/tests/qtest/pnv-xive2-flush-sync.c index 3b32446adb..142826bad0 100644 --- a/tests/qtest/pnv-xive2-flush-sync.c +++ b/tests/qtest/pnv-xive2-flush-sync.c @@ -178,14 +178,14 @@ void test_flush_sync_inject(QTestState *qts) int test_nr; uint8_t byte; - printf("# ============================================================\n"); - printf("# Starting cache flush/queue sync injection tests...\n"); + g_test_message("========================================================="); + g_test_message("Starting cache flush/queue sync injection tests..."); for (test_nr = 0; test_nr < sizeof(xive_inject_tests); test_nr++) { int op_type = xive_inject_tests[test_nr]; - printf("# Running test %d\n", test_nr); + g_test_message("Running test %d", test_nr); /* start with status byte set to 0 */ clr_sync(qts, src_pir, ic_topo_id, op_type); diff --git a/tests/qtest/pnv-xive2-nvpg_bar.c b/tests/qtest/pnv-xive2-nvpg_bar.c index 028512bddc..6ac8d36c82 100644 --- a/tests/qtest/pnv-xive2-nvpg_bar.c +++ b/tests/qtest/pnv-xive2-nvpg_bar.c @@ -4,8 +4,7 @@ * * Copyright (c) 2024, IBM Corporation. * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" #include "libqtest.h" @@ -78,8 +77,8 @@ void test_nvpg_bar(QTestState *qts) uint32_t count, delta; uint8_t i; - printf("# ============================================================\n"); - printf("# Testing NVPG BAR operations\n"); + g_test_message("========================================================="); + g_test_message("Testing NVPG BAR operations"); set_nvg(qts, group_target, 0); set_nvp(qts, nvp_target, 0x04); diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c index a0e9f19313..7e7b1e79c0 100644 --- a/tests/qtest/pnv-xive2-test.c +++ b/tests/qtest/pnv-xive2-test.c @@ -4,6 +4,7 @@ * - Test 'Pull Thread Context to Odd Thread Reporting Line' * - Test irq to hardware group * - Test irq to hardware group going through backlog + * - Test irq to pool thread * * Copyright (c) 2024, IBM Corporation. * @@ -220,8 +221,8 @@ static void test_hw_irq(QTestState *qts) uint16_t reg16; uint8_t pq, nsr, cppr; - printf("# ============================================================\n"); - printf("# Testing irq %d to hardware thread %d\n", irq, target_pir); + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware thread %d", irq, target_pir); /* irq config */ set_eas(qts, irq, end_index, irq_data); @@ -278,8 +279,9 @@ static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) uint32_t cl_word; uint32_t word2; - printf("# ============================================================\n"); - printf("# Testing 'Pull Thread Context to Odd Thread Reporting Line'\n"); + g_test_message("========================================================="); + g_test_message("Testing 'Pull Thread Context to Odd Thread Reporting " \ + "Line'"); /* clear odd cache line prior to pull operation */ memset(cl_pair, 0, sizeof(cl_pair)); @@ -330,8 +332,8 @@ static void test_hw_group_irq(QTestState *qts) uint16_t reg16; uint8_t pq, nsr, cppr; - printf("# ============================================================\n"); - printf("# Testing irq %d to hardware group of size 4\n", irq); + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware group of size 4", irq); /* irq config */ set_eas(qts, irq, end_index, irq_data); @@ -395,10 +397,10 @@ static void test_hw_group_irq_backlog(QTestState *qts) uint16_t reg16; uint8_t pq, nsr, cppr, lsmfb, i; - printf("# ============================================================\n"); - printf("# Testing irq %d to hardware group of size 4 going through " \ - "backlog\n", - irq); + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware group of size 4 going " \ + "through backlog", + irq); /* * set current priority of all threads in the group to something From ffc2cabeb536bc595543845c5fe6147d4f9718d3 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 11 Mar 2025 11:55:00 +1000 Subject: [PATCH 2577/2892] qtest/xive: Add test of pool interrupts Added new test for pool interrupts. Removed all printfs from pnv-xive2-* qtests. Signed-off-by: Glenn Miles Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Signed-off-by: Nicholas Piggin --- tests/qtest/pnv-xive2-test.c | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c index 7e7b1e79c0..5313d4ef18 100644 --- a/tests/qtest/pnv-xive2-test.c +++ b/tests/qtest/pnv-xive2-test.c @@ -267,6 +267,79 @@ static void test_hw_irq(QTestState *qts) g_assert_cmphex(cppr, ==, 0xFF); } +static void test_pool_irq(QTestState *qts) +{ + uint32_t irq = 2; + uint32_t irq_data = 0x600d0d06; + uint32_t end_index = 5; + uint32_t target_pir = 1; + uint32_t target_nvp = 0x100 + target_pir; + uint8_t priority = 5; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr, ipb; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to pool thread %d", irq, target_pir); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, false /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + /* check TIMA values in the PHYS ring (shared by POOL ring) */ + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x40); + g_assert_cmphex(cppr, ==, 0xFF); + + /* check TIMA values in the POOL ring */ + reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + ipb = (reg32 >> 8) & 0xFF; + g_assert_cmphex(nsr, ==, 0); + g_assert_cmphex(cppr, ==, 0); + g_assert_cmphex(ipb, ==, 0x80 >> priority); + + /* ack the irq */ + reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x40); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* check IPB is cleared in the POOL ring */ + reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); + ipb = (reg32 >> 8) & 0xFF; + g_assert_cmphex(ipb, ==, 0); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + #define XIVE_ODD_CL 0x80 static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) { @@ -486,6 +559,9 @@ static void test_xive(void) /* omit reset_state here and use settings from test_hw_irq */ test_pull_thread_ctx_to_odd_thread_cl(qts); + reset_state(qts); + test_pool_irq(qts); + reset_state(qts); test_hw_group_irq(qts); From 17befecda85d585c5b0186af3c4e74fb8b82cbce Mon Sep 17 00:00:00 2001 From: Chalapathi V Date: Mon, 3 Mar 2025 08:13:25 -0600 Subject: [PATCH 2578/2892] hw/ssi/pnv_spi: Replace PnvXferBuffer with Fifo8 structure In PnvXferBuffer dynamically allocating and freeing is a process overhead. Hence used an existing Fifo8 buffer with capacity of 16 bytes. Signed-off-by: Chalapathi V Message-ID: <20250303141328.23991-2-chalapathi.v@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ssi/pnv_spi.c | 264 ++++++++++++++++----------------------- include/hw/ssi/pnv_spi.h | 3 + 2 files changed, 108 insertions(+), 159 deletions(-) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 15e25bd1be..388b425157 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -19,6 +19,7 @@ #define PNV_SPI_OPCODE_LO_NIBBLE(x) (x & 0x0F) #define PNV_SPI_MASKED_OPCODE(x) (x & 0xF0) +#define PNV_SPI_FIFO_SIZE 16 /* * Macro from include/hw/ppc/fdt.h @@ -35,48 +36,14 @@ } \ } while (0) -/* PnvXferBuffer */ -typedef struct PnvXferBuffer { - - uint32_t len; - uint8_t *data; - -} PnvXferBuffer; - -/* pnv_spi_xfer_buffer_methods */ -static PnvXferBuffer *pnv_spi_xfer_buffer_new(void) -{ - PnvXferBuffer *payload = g_malloc0(sizeof(*payload)); - - return payload; -} - -static void pnv_spi_xfer_buffer_free(PnvXferBuffer *payload) -{ - g_free(payload->data); - g_free(payload); -} - -static uint8_t *pnv_spi_xfer_buffer_write_ptr(PnvXferBuffer *payload, - uint32_t offset, uint32_t length) -{ - if (payload->len < (offset + length)) { - payload->len = offset + length; - payload->data = g_realloc(payload->data, payload->len); - } - return &payload->data[offset]; -} - static bool does_rdr_match(PnvSpi *s) { /* * According to spec, the mask bits that are 0 are compared and the * bits that are 1 are ignored. */ - uint16_t rdr_match_mask = GETFIELD(SPI_MM_RDR_MATCH_MASK, - s->regs[SPI_MM_REG]); - uint16_t rdr_match_val = GETFIELD(SPI_MM_RDR_MATCH_VAL, - s->regs[SPI_MM_REG]); + uint16_t rdr_match_mask = GETFIELD(SPI_MM_RDR_MATCH_MASK, s->regs[SPI_MM_REG]); + uint16_t rdr_match_val = GETFIELD(SPI_MM_RDR_MATCH_VAL, s->regs[SPI_MM_REG]); if ((~rdr_match_mask & rdr_match_val) == ((~rdr_match_mask) & GETFIELD(PPC_BITMASK(48, 63), s->regs[SPI_RCV_DATA_REG]))) { @@ -107,8 +74,8 @@ static uint8_t get_from_offset(PnvSpi *s, uint8_t offset) return byte; } -static uint8_t read_from_frame(PnvSpi *s, uint8_t *read_buf, uint8_t nr_bytes, - uint8_t ecc_count, uint8_t shift_in_count) +static uint8_t read_from_frame(PnvSpi *s, uint8_t nr_bytes, uint8_t ecc_count, + uint8_t shift_in_count) { uint8_t byte; int count = 0; @@ -118,20 +85,24 @@ static uint8_t read_from_frame(PnvSpi *s, uint8_t *read_buf, uint8_t nr_bytes, if ((ecc_count != 0) && (shift_in_count == (PNV_SPI_REG_SIZE + ecc_count))) { shift_in_count = 0; - } else { - byte = read_buf[count]; + } else if (!fifo8_is_empty(&s->rx_fifo)) { + byte = fifo8_pop(&s->rx_fifo); trace_pnv_spi_shift_rx(byte, count); s->regs[SPI_RCV_DATA_REG] = (s->regs[SPI_RCV_DATA_REG] << 8) | byte; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: Reading empty RX_FIFO\n"); } count++; } /* end of while */ return shift_in_count; } -static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) +static void spi_response(PnvSpi *s) { uint8_t ecc_count; uint8_t shift_in_count; + uint32_t rx_len; + int i; /* * Processing here must handle: @@ -144,13 +115,14 @@ static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) * First check that the response payload is the exact same * number of bytes as the request payload was */ - if (rsp_payload->len != (s->N1_bytes + s->N2_bytes)) { + rx_len = fifo8_num_used(&s->rx_fifo); + if (rx_len != (s->N1_bytes + s->N2_bytes)) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in " "bytes, expected %d, got %d\n", - (s->N1_bytes + s->N2_bytes), rsp_payload->len); + (s->N1_bytes + s->N2_bytes), rx_len); } else { uint8_t ecc_control; - trace_pnv_spi_rx_received(rsp_payload->len); + trace_pnv_spi_rx_received(rx_len); trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); /* @@ -175,15 +147,23 @@ static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) /* Handle the N1 portion of the frame first */ if (s->N1_rx != 0) { trace_pnv_spi_rx_read_N1frame(); - shift_in_count = read_from_frame(s, &rsp_payload->data[0], - s->N1_bytes, ecc_count, shift_in_count); + shift_in_count = read_from_frame(s, s->N1_bytes, ecc_count, shift_in_count); } /* Handle the N2 portion of the frame */ if (s->N2_rx != 0) { + /* pop out N1_bytes from rx_fifo if not already */ + if (s->N1_rx == 0) { + for (i = 0; i < s->N1_bytes; i++) { + if (!fifo8_is_empty(&s->rx_fifo)) { + fifo8_pop(&s->rx_fifo); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: Reading empty" + " RX_FIFO\n"); + } + } + } trace_pnv_spi_rx_read_N2frame(); - shift_in_count = read_from_frame(s, - &rsp_payload->data[s->N1_bytes], s->N2_bytes, - ecc_count, shift_in_count); + shift_in_count = read_from_frame(s, s->N2_bytes, ecc_count, shift_in_count); } if ((s->N1_rx + s->N2_rx) > 0) { /* @@ -210,36 +190,41 @@ static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) } /* end of else */ } /* end of spi_response() */ -static void transfer(PnvSpi *s, PnvXferBuffer *payload) +static void transfer(PnvSpi *s) { - uint32_t tx; - uint32_t rx; - PnvXferBuffer *rsp_payload = NULL; + uint32_t tx, rx, payload_len; + uint8_t rx_byte; - rsp_payload = pnv_spi_xfer_buffer_new(); - if (!rsp_payload) { - return; - } - for (int offset = 0; offset < payload->len; offset += s->transfer_len) { + payload_len = fifo8_num_used(&s->tx_fifo); + for (int offset = 0; offset < payload_len; offset += s->transfer_len) { tx = 0; for (int i = 0; i < s->transfer_len; i++) { - if ((offset + i) >= payload->len) { + if ((offset + i) >= payload_len) { tx <<= 8; + } else if (!fifo8_is_empty(&s->tx_fifo)) { + tx = (tx << 8) | fifo8_pop(&s->tx_fifo); } else { - tx = (tx << 8) | payload->data[offset + i]; + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO underflow\n"); } } rx = ssi_transfer(s->ssi_bus, tx); for (int i = 0; i < s->transfer_len; i++) { - if ((offset + i) >= payload->len) { + if ((offset + i) >= payload_len) { + break; + } + rx_byte = (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF; + if (!fifo8_is_full(&s->rx_fifo)) { + fifo8_push(&s->rx_fifo, rx_byte); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: RX_FIFO is full\n"); break; } - *(pnv_spi_xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) = - (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF; } } - spi_response(s, s->N1_bits, rsp_payload); - pnv_spi_xfer_buffer_free(rsp_payload); + spi_response(s); + /* Reset fifo for next frame */ + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); } static inline uint8_t get_seq_index(PnvSpi *s) @@ -310,13 +295,11 @@ static void calculate_N1(PnvSpi *s, uint8_t opcode) * If Forced Implicit mode and count control doesn't * indicate transmit then reset the tx count to 0 */ - if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, - s->regs[SPI_CTR_CFG_REG]) == 0) { + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 0) { s->N1_tx = 0; } /* If rx count control for N1 is set, load the rx value */ - if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, - s->regs[SPI_CTR_CFG_REG]) == 1) { + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { s->N1_rx = s->N1_bytes; } } @@ -328,8 +311,7 @@ static void calculate_N1(PnvSpi *s, uint8_t opcode) * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM * error bit. */ - uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, - s->regs[SPI_CLK_CFG_REG]); + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, s->regs[SPI_CLK_CFG_REG]); if (ecc_control == 0 || ecc_control == 2) { if (s->N1_bytes > (PNV_SPI_REG_SIZE + 1)) { qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when " @@ -340,8 +322,7 @@ static void calculate_N1(PnvSpi *s, uint8_t opcode) } } else if (s->N1_bytes > PNV_SPI_REG_SIZE) { qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, " - "bytes = 0x%x, bits = 0x%x\n", - s->N1_bytes, s->N1_bits); + "bytes = 0x%x, bits = 0x%x\n", s->N1_bytes, s->N1_bits); s->N1_bytes = PNV_SPI_REG_SIZE; s->N1_bits = s->N1_bytes * 8; } @@ -350,19 +331,10 @@ static void calculate_N1(PnvSpi *s, uint8_t opcode) /* * Shift_N1 operation handler method */ -static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, - PnvXferBuffer **payload, bool send_n1_alone) +static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, bool send_n1_alone) { uint8_t n1_count; bool stop = false; - - /* - * If there isn't a current payload left over from a stopped sequence - * create a new one. - */ - if (*payload == NULL) { - *payload = pnv_spi_xfer_buffer_new(); - } /* * Use a combination of N1 counters to build the N1 portion of the * transmit payload. @@ -413,9 +385,13 @@ static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, */ uint8_t n1_byte = 0x00; n1_byte = get_from_offset(s, n1_count); - trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count); - *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = - n1_byte; + if (!fifo8_is_full(&s->tx_fifo)) { + trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count); + fifo8_push(&s->tx_fifo, n1_byte); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; + } } else { /* * We hit a shift_n1 opcode TX but the TDR is empty, tell the @@ -436,16 +412,17 @@ static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, * - we are receiving and the RDR is empty so we allow the operation * to proceed. */ - if ((s->N1_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL, - s->status) == 1)) { + if ((s->N1_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) { trace_pnv_spi_sequencer_stop_requested("shift N1" "set for receive but RDR is full"); stop = true; break; - } else { + } else if (!fifo8_is_full(&s->tx_fifo)) { trace_pnv_spi_tx_append_FF("n1_byte"); - *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) - = 0xff; + fifo8_push(&s->tx_fifo, 0xff); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; } } n1_count++; @@ -486,15 +463,13 @@ static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, */ if (send_n1_alone && !stop) { /* We have a TX and a full TDR or an RX and an empty RDR */ - trace_pnv_spi_tx_request("Shifting N1 frame", (*payload)->len); - transfer(s, *payload); + trace_pnv_spi_tx_request("Shifting N1 frame", fifo8_num_used(&s->tx_fifo)); + transfer(s); /* The N1 frame shift is complete so reset the N1 counters */ s->N2_bits = 0; s->N2_bytes = 0; s->N2_tx = 0; s->N2_rx = 0; - pnv_spi_xfer_buffer_free(*payload); - *payload = NULL; } return stop; } /* end of operation_shiftn1() */ @@ -552,13 +527,11 @@ static void calculate_N2(PnvSpi *s, uint8_t opcode) * If Forced Implicit mode and count control doesn't * indicate a receive then reset the rx count to 0 */ - if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, - s->regs[SPI_CTR_CFG_REG]) == 0) { + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 0) { s->N2_rx = 0; } /* If tx count control for N2 is set, load the tx value */ - if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, - s->regs[SPI_CTR_CFG_REG]) == 1) { + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { s->N2_tx = s->N2_bytes; } } @@ -571,8 +544,7 @@ static void calculate_N2(PnvSpi *s, uint8_t opcode) * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM * error bit. */ - uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, - s->regs[SPI_CLK_CFG_REG]); + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, s->regs[SPI_CLK_CFG_REG]); if (ecc_control == 0 || ecc_control == 2) { if (s->N2_bytes > (PNV_SPI_REG_SIZE + 1)) { /* Unsupported N2 shift size when ECC enabled */ @@ -590,19 +562,10 @@ static void calculate_N2(PnvSpi *s, uint8_t opcode) * Shift_N2 operation handler method */ -static bool operation_shiftn2(PnvSpi *s, uint8_t opcode, - PnvXferBuffer **payload) +static bool operation_shiftn2(PnvSpi *s, uint8_t opcode) { uint8_t n2_count; bool stop = false; - - /* - * If there isn't a current payload left over from a stopped sequence - * create a new one. - */ - if (*payload == NULL) { - *payload = pnv_spi_xfer_buffer_new(); - } /* * Use a combination of N2 counters to build the N2 portion of the * transmit payload. @@ -629,44 +592,47 @@ static bool operation_shiftn2(PnvSpi *s, uint8_t opcode, * code continue will end up building the payload twice in the same * buffer since RDR full causes a sequence stop and restart. */ - if ((s->N2_rx != 0) && - (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) { + if ((s->N2_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) { trace_pnv_spi_sequencer_stop_requested("shift N2 set" "for receive but RDR is full"); stop = true; break; } - if ((s->N2_tx != 0) && ((s->N1_tx + n2_count) < - PNV_SPI_REG_SIZE)) { + if ((s->N2_tx != 0) && ((s->N1_tx + n2_count) < PNV_SPI_REG_SIZE)) { /* Always append data for the N2 segment if it is set for TX */ uint8_t n2_byte = 0x00; n2_byte = get_from_offset(s, (s->N1_tx + n2_count)); - trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count)); - *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) - = n2_byte; - } else { + if (!fifo8_is_full(&s->tx_fifo)) { + trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count)); + fifo8_push(&s->tx_fifo, n2_byte); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; + } + } else if (!fifo8_is_full(&s->tx_fifo)) { /* * Regardless of whether or not N2 is set for TX or RX, we need * the number of bytes in the payload to match the overall length * of the operation. */ trace_pnv_spi_tx_append_FF("n2_byte"); - *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) - = 0xff; + fifo8_push(&s->tx_fifo, 0xff); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; } n2_count++; } /* end of while */ if (!stop) { /* We have a TX and a full TDR or an RX and an empty RDR */ - trace_pnv_spi_tx_request("Shifting N2 frame", (*payload)->len); - transfer(s, *payload); + trace_pnv_spi_tx_request("Shifting N2 frame", fifo8_num_used(&s->tx_fifo)); + transfer(s); /* * If we are doing an N2 TX and the TDR is full we need to clear the * TDR_full status. Do this here instead of up in the loop above so we * don't log the message in every loop iteration. */ - if ((s->N2_tx != 0) && - (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + if ((s->N2_tx != 0) && (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); } /* @@ -682,8 +648,6 @@ static bool operation_shiftn2(PnvSpi *s, uint8_t opcode, s->N1_bytes = 0; s->N1_tx = 0; s->N1_rx = 0; - pnv_spi_xfer_buffer_free(*payload); - *payload = NULL; } return stop; } /* end of operation_shiftn2()*/ @@ -701,19 +665,6 @@ static void operation_sequencer(PnvSpi *s) uint8_t opcode = 0; uint8_t masked_opcode = 0; - /* - * PnvXferBuffer for containing the payload of the SPI frame. - * This is a static because there are cases where a sequence has to stop - * and wait for the target application to unload the RDR. If this occurs - * during a sequence where N1 is not sent alone and instead combined with - * N2 since the N1 tx length + the N2 tx length is less than the size of - * the TDR. - */ - static PnvXferBuffer *payload; - - if (payload == NULL) { - payload = pnv_spi_xfer_buffer_new(); - } /* * Clear the sequencer FSM error bit - general_SPI_status[3] * before starting a sequence. @@ -775,10 +726,8 @@ static void operation_sequencer(PnvSpi *s) s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_DONE); } else if (s->responder_select != 1) { qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 " - "not supported, select = 0x%x\n", - s->responder_select); - trace_pnv_spi_sequencer_stop_requested("invalid " - "responder select"); + "not supported, select = 0x%x\n", s->responder_select); + trace_pnv_spi_sequencer_stop_requested("invalid responder select"); s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); stop = true; } else { @@ -840,9 +789,8 @@ static void operation_sequencer(PnvSpi *s) == SEQ_OP_SHIFT_N2) { send_n1_alone = false; } - s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, - FSM_SHIFT_N1); - stop = operation_shiftn1(s, opcode, &payload, send_n1_alone); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_SHIFT_N1); + stop = operation_shiftn1(s, opcode, send_n1_alone); if (stop) { /* * The operation code says to stop, this can occur if: @@ -858,7 +806,7 @@ static void operation_sequencer(PnvSpi *s) if (GETFIELD(SPI_STS_TDR_UNDERRUN, s->status)) { s->shift_n1_done = true; s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, - FSM_SHIFT_N2); + FSM_SHIFT_N2); s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, (get_seq_index(s) + 1)); } else { @@ -866,8 +814,7 @@ static void operation_sequencer(PnvSpi *s) * This is case (1) or (2) so the sequencer needs to * wait and NOT go to the next sequence yet. */ - s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, - FSM_WAIT); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT); } } else { /* Ok to move on to the next index */ @@ -890,21 +837,18 @@ static void operation_sequencer(PnvSpi *s) * error bit 3 (general_SPI_status[3]) in status reg. */ s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); - trace_pnv_spi_sequencer_stop_requested("shift_n2 " - "w/no shift_n1 done"); + trace_pnv_spi_sequencer_stop_requested("shift_n2 w/no shift_n1 done"); stop = true; } else { /* Ok to do a Shift_N2 */ - s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, - FSM_SHIFT_N2); - stop = operation_shiftn2(s, opcode, &payload); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_SHIFT_N2); + stop = operation_shiftn2(s, opcode); /* * If the operation code says to stop set the shifter state to * wait and stop */ if (stop) { - s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, - FSM_WAIT); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT); } else { /* Ok to move on to the next index */ next_sequencer_fsm(s); @@ -988,8 +932,7 @@ static void operation_sequencer(PnvSpi *s) case SEQ_OP_BRANCH_IFNEQ_INC_2: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", get_seq_index(s)); - uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, - s->regs[SPI_CTR_CFG_REG]); + uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, s->regs[SPI_CTR_CFG_REG]); /* * The spec says the loop should execute count compare + 1 times. * However we learned from engineering that we really only loop @@ -1209,6 +1152,9 @@ static void pnv_spi_realize(DeviceState *dev, Error **errp) s->cs_line = g_new0(qemu_irq, 1); qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1); + fifo8_create(&s->tx_fifo, PNV_SPI_FIFO_SIZE); + fifo8_create(&s->rx_fifo, PNV_SPI_FIFO_SIZE); + /* spi scoms */ pnv_xscom_region_init(&s->xscom_spic_regs, OBJECT(s), &pnv_spi_xscom_ops, s, "xscom-spi", PNV10_XSCOM_PIB_SPIC_SIZE); diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h index 8815f67d45..9878d9a25f 100644 --- a/include/hw/ssi/pnv_spi.h +++ b/include/hw/ssi/pnv_spi.h @@ -23,6 +23,7 @@ #include "hw/ssi/ssi.h" #include "hw/sysbus.h" +#include "qemu/fifo8.h" #define TYPE_PNV_SPI "pnv-spi" OBJECT_DECLARE_SIMPLE_TYPE(PnvSpi, PNV_SPI) @@ -37,6 +38,8 @@ typedef struct PnvSpi { SSIBus *ssi_bus; qemu_irq *cs_line; MemoryRegion xscom_spic_regs; + Fifo8 tx_fifo; + Fifo8 rx_fifo; /* SPI object number */ uint32_t spic_num; uint8_t transfer_len; From f1f756f305981c24a37a0be3113c2326a28594a5 Mon Sep 17 00:00:00 2001 From: Chalapathi V Date: Mon, 3 Mar 2025 08:13:26 -0600 Subject: [PATCH 2579/2892] hw/ssi/pnv_spi: Use local var seq_index instead of get_seq_index(). Use a local variable seq_index instead of repeatedly calling get_seq_index() method and open-code next_sequencer_fsm(). Signed-off-by: Chalapathi V Reviewed-by: Nicholas Piggin Message-ID: <20250303141328.23991-3-chalapathi.v@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ssi/pnv_spi.c | 97 ++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 388b425157..de33542c35 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -227,18 +227,6 @@ static void transfer(PnvSpi *s) fifo8_reset(&s->rx_fifo); } -static inline uint8_t get_seq_index(PnvSpi *s) -{ - return GETFIELD(SPI_STS_SEQ_INDEX, s->status); -} - -static inline void next_sequencer_fsm(PnvSpi *s) -{ - uint8_t seq_index = get_seq_index(s); - s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, (seq_index + 1)); - s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); -} - /* * Calculate the N1 counters based on passed in opcode and * internal register values. @@ -664,6 +652,7 @@ static void operation_sequencer(PnvSpi *s) bool stop = false; /* Flag to stop the sequencer */ uint8_t opcode = 0; uint8_t masked_opcode = 0; + uint8_t seq_index; /* * Clear the sequencer FSM error bit - general_SPI_status[3] @@ -677,12 +666,17 @@ static void operation_sequencer(PnvSpi *s) if (GETFIELD(SPI_STS_SEQ_FSM, s->status) == SEQ_STATE_IDLE) { s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); } + /* + * SPI_STS_SEQ_INDEX of status register is kept in seq_index variable and + * updated back to status register at the end of operation_sequencer(). + */ + seq_index = GETFIELD(SPI_STS_SEQ_INDEX, s->status); /* * There are only 8 possible operation IDs to iterate through though * some operations may cause more than one frame to be sequenced. */ - while (get_seq_index(s) < NUM_SEQ_OPS) { - opcode = s->seq_op[get_seq_index(s)]; + while (seq_index < NUM_SEQ_OPS) { + opcode = s->seq_op[seq_index]; /* Set sequencer state to decode */ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_DECODE); /* @@ -699,7 +693,7 @@ static void operation_sequencer(PnvSpi *s) case SEQ_OP_STOP: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); /* A stop operation in any position stops the sequencer */ - trace_pnv_spi_sequencer_op("STOP", get_seq_index(s)); + trace_pnv_spi_sequencer_op("STOP", seq_index); stop = true; s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); @@ -710,7 +704,7 @@ static void operation_sequencer(PnvSpi *s) case SEQ_OP_SELECT_SLAVE: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); - trace_pnv_spi_sequencer_op("SELECT_SLAVE", get_seq_index(s)); + trace_pnv_spi_sequencer_op("SELECT_SLAVE", seq_index); /* * This device currently only supports a single responder * connection at position 0. De-selecting a responder is fine @@ -721,8 +715,7 @@ static void operation_sequencer(PnvSpi *s) if (s->responder_select == 0) { trace_pnv_spi_shifter_done(); qemu_set_irq(s->cs_line[0], 1); - s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, - (get_seq_index(s) + 1)); + seq_index++; s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_DONE); } else if (s->responder_select != 1) { qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 " @@ -747,13 +740,15 @@ static void operation_sequencer(PnvSpi *s) * applies once a valid responder select has occurred. */ s->shift_n1_done = false; - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); } break; case SEQ_OP_SHIFT_N1: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); - trace_pnv_spi_sequencer_op("SHIFT_N1", get_seq_index(s)); + trace_pnv_spi_sequencer_op("SHIFT_N1", seq_index); /* * Only allow a shift_n1 when the state is not IDLE or DONE. * In either of those two cases the sequencer is not in a proper @@ -785,8 +780,9 @@ static void operation_sequencer(PnvSpi *s) * transmission to the responder without requiring a refill of * the TDR between the two operations. */ - if (PNV_SPI_MASKED_OPCODE(s->seq_op[get_seq_index(s) + 1]) - == SEQ_OP_SHIFT_N2) { + if ((seq_index != 7) && + PNV_SPI_MASKED_OPCODE(s->seq_op[(seq_index + 1)]) == + SEQ_OP_SHIFT_N2) { send_n1_alone = false; } s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_SHIFT_N1); @@ -806,9 +802,8 @@ static void operation_sequencer(PnvSpi *s) if (GETFIELD(SPI_STS_TDR_UNDERRUN, s->status)) { s->shift_n1_done = true; s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, - FSM_SHIFT_N2); - s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, - (get_seq_index(s) + 1)); + FSM_SHIFT_N2); + seq_index++; } else { /* * This is case (1) or (2) so the sequencer needs to @@ -819,14 +814,16 @@ static void operation_sequencer(PnvSpi *s) } else { /* Ok to move on to the next index */ s->shift_n1_done = true; - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); } } break; case SEQ_OP_SHIFT_N2: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); - trace_pnv_spi_sequencer_op("SHIFT_N2", get_seq_index(s)); + trace_pnv_spi_sequencer_op("SHIFT_N2", seq_index); if (!s->shift_n1_done) { qemu_log_mask(LOG_GUEST_ERROR, "Shift_N2 is not allowed if a " "Shift_N1 is not done, shifter state = 0x%llx", @@ -851,14 +848,16 @@ static void operation_sequencer(PnvSpi *s) s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT); } else { /* Ok to move on to the next index */ - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); } } break; case SEQ_OP_BRANCH_IFNEQ_RDR: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); - trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_RDR", get_seq_index(s)); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_RDR", seq_index); /* * The memory mapping register RDR match value is compared against * the 16 rightmost bytes of the RDR (potentially with masking). @@ -874,15 +873,16 @@ static void operation_sequencer(PnvSpi *s) if (rdr_matched) { trace_pnv_spi_RDR_match("success"); /* A match occurred, increment the sequencer index. */ - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); } else { trace_pnv_spi_RDR_match("failed"); /* * Branch the sequencer to the index coded into the op * code. */ - s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, - PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); } /* * Regardless of where the branch ended up we want the @@ -901,12 +901,13 @@ static void operation_sequencer(PnvSpi *s) case SEQ_OP_TRANSFER_TDR: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\n"); - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); break; case SEQ_OP_BRANCH_IFNEQ_INC_1: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); - trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_1", get_seq_index(s)); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_1", seq_index); /* * The spec says the loop should execute count compare + 1 times. * However we learned from engineering that we really only loop @@ -920,19 +921,21 @@ static void operation_sequencer(PnvSpi *s) * mask off all but the first three bits so we don't try to * access beyond the sequencer_operation_reg boundary. */ - s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, - PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); s->loop_counter_1++; } else { /* Continue to next index if loop counter is reached */ - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); } break; case SEQ_OP_BRANCH_IFNEQ_INC_2: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); - trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", get_seq_index(s)); - uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, s->regs[SPI_CTR_CFG_REG]); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", seq_index); + uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, + s->regs[SPI_CTR_CFG_REG]); /* * The spec says the loop should execute count compare + 1 times. * However we learned from engineering that we really only loop @@ -945,19 +948,21 @@ static void operation_sequencer(PnvSpi *s) * mask off all but the first three bits so we don't try to * access beyond the sequencer_operation_reg boundary. */ - s->status = SETFIELD(SPI_STS_SEQ_INDEX, - s->status, PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); s->loop_counter_2++; } else { /* Continue to next index if loop counter is reached */ - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); } break; default: s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); /* Ignore unsupported operations. */ - next_sequencer_fsm(s); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); break; } /* end of switch */ /* @@ -965,10 +970,10 @@ static void operation_sequencer(PnvSpi *s) * we need to go ahead and end things as if there was a STOP at the * end. */ - if (get_seq_index(s) == NUM_SEQ_OPS) { + if (seq_index == NUM_SEQ_OPS) { /* All 8 opcodes completed, sequencer idling */ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); - s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + seq_index = 0; s->loop_counter_1 = 0; s->loop_counter_2 = 0; s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); @@ -979,6 +984,8 @@ static void operation_sequencer(PnvSpi *s) break; } } /* end of while */ + /* Update sequencer index field in status.*/ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, seq_index); return; } /* end of operation_sequencer() */ From 7192d7b7fea15f2226a896f02b360bf7cfce1ab1 Mon Sep 17 00:00:00 2001 From: Chalapathi V Date: Mon, 3 Mar 2025 08:13:27 -0600 Subject: [PATCH 2580/2892] hw/ssi/pnv_spi: Make bus names distinct for each controllers of a socket Create a spi buses with distinct names on each socket so that responders are attached to correct SPI controllers. Change the bus name to chipX.spi. where X = 0.. QOM tree on a 2 socket machine: (qemu) info qom-tree /machine (powernv10-machine) /chip[0] (power10_v2.0-pnv-chip) /pib_spic[0] (pnv-spi) /chip0.spi.0 (SSI) /xscom-spi[0] (memory-region) /chip[1] (power10_v2.0-pnv-chip) /pib_spic[0] (pnv-spi) /chip1.spi.0 (SSI) /xscom-spi[0] (memory-region) Signed-off-by: Chalapathi V Message-ID: <20250303141328.23991-4-chalapathi.v@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ppc/pnv.c | 2 ++ hw/ssi/pnv_spi.c | 5 +++-- include/hw/ssi/pnv_spi.h | 3 ++- tests/qtest/pnv-spi-seeprom-test.c | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index d60574d601..59365370c3 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2252,6 +2252,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) /* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */ object_property_set_int(OBJECT(&chip10->pib_spic[i]), "transfer_len", (i == 2) ? 1 : 4, &error_fatal); + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "chip-id", + chip->chip_id, &error_fatal); if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT (&chip10->pib_spic[i])), errp)) { return; diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index de33542c35..83221607c9 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -1147,14 +1147,15 @@ static const MemoryRegionOps pnv_spi_xscom_ops = { static const Property pnv_spi_properties[] = { DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), + DEFINE_PROP_UINT32("chip-id", PnvSpi, chip_id, 0), DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), }; static void pnv_spi_realize(DeviceState *dev, Error **errp) { PnvSpi *s = PNV_SPI(dev); - g_autofree char *name = g_strdup_printf(TYPE_PNV_SPI_BUS ".%d", - s->spic_num); + g_autofree char *name = g_strdup_printf("chip%d." TYPE_PNV_SPI_BUS ".%d", + s->chip_id, s->spic_num); s->ssi_bus = ssi_create_bus(dev, name); s->cs_line = g_new0(qemu_irq, 1); qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1); diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h index 9878d9a25f..6adb72dbb2 100644 --- a/include/hw/ssi/pnv_spi.h +++ b/include/hw/ssi/pnv_spi.h @@ -31,7 +31,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvSpi, PNV_SPI) #define PNV_SPI_REG_SIZE 8 #define PNV_SPI_REGS 7 -#define TYPE_PNV_SPI_BUS "pnv-spi-bus" +#define TYPE_PNV_SPI_BUS "spi" typedef struct PnvSpi { SysBusDevice parent_obj; @@ -42,6 +42,7 @@ typedef struct PnvSpi { Fifo8 rx_fifo; /* SPI object number */ uint32_t spic_num; + uint32_t chip_id; uint8_t transfer_len; uint8_t responder_select; /* To verify if shift_n1 happens prior to shift_n2 */ diff --git a/tests/qtest/pnv-spi-seeprom-test.c b/tests/qtest/pnv-spi-seeprom-test.c index 57f20af76e..600493c425 100644 --- a/tests/qtest/pnv-spi-seeprom-test.c +++ b/tests/qtest/pnv-spi-seeprom-test.c @@ -92,7 +92,7 @@ static void test_spi_seeprom(const void *data) qts = qtest_initf("-machine powernv10 -smp 2,cores=2," "threads=1 -accel tcg,thread=single -nographic " "-blockdev node-name=pib_spic2,driver=file," - "filename=%s -device 25csm04,bus=pnv-spi-bus.2,cs=0," + "filename=%s -device 25csm04,bus=chip0.spi.2,cs=0," "drive=pib_spic2", tmp_path); spi_seeprom_transaction(qts, chip); qtest_quit(qts); From a613b9d321f5536943e29aa5847553df46dc2340 Mon Sep 17 00:00:00 2001 From: Chalapathi V Date: Mon, 3 Mar 2025 08:13:28 -0600 Subject: [PATCH 2581/2892] hw/ssi/pnv_spi: Put a limit to RDR match failures There is a possibility that SPI controller can get into loop due to indefinite RDR match failures. Hence put a limit to failures and stop the sequencer. Signed-off-by: Chalapathi V Reviewed-by: Nicholas Piggin Message-ID: <20250303141328.23991-5-chalapathi.v@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ssi/pnv_spi.c | 10 ++++++++++ include/hw/ssi/pnv_spi.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 83221607c9..126070393e 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -20,6 +20,7 @@ #define PNV_SPI_OPCODE_LO_NIBBLE(x) (x & 0x0F) #define PNV_SPI_MASKED_OPCODE(x) (x & 0xF0) #define PNV_SPI_FIFO_SIZE 16 +#define RDR_MATCH_FAILURE_LIMIT 16 /* * Macro from include/hw/ppc/fdt.h @@ -872,18 +873,27 @@ static void operation_sequencer(PnvSpi *s) rdr_matched = does_rdr_match(s); if (rdr_matched) { trace_pnv_spi_RDR_match("success"); + s->fail_count = 0; /* A match occurred, increment the sequencer index. */ seq_index++; s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); } else { trace_pnv_spi_RDR_match("failed"); + s->fail_count++; /* * Branch the sequencer to the index coded into the op * code. */ seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); } + if (s->fail_count >= RDR_MATCH_FAILURE_LIMIT) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: RDR match failure" + " limit crossed %d times hence requesting " + "sequencer to stop.\n", + RDR_MATCH_FAILURE_LIMIT); + stop = true; + } /* * Regardless of where the branch ended up we want the * sequencer to continue shifting so we have to clear diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h index 6adb72dbb2..c591a0663d 100644 --- a/include/hw/ssi/pnv_spi.h +++ b/include/hw/ssi/pnv_spi.h @@ -40,6 +40,7 @@ typedef struct PnvSpi { MemoryRegion xscom_spic_regs; Fifo8 tx_fifo; Fifo8 rx_fifo; + uint8_t fail_count; /* RDR Match failure counter */ /* SPI object number */ uint32_t spic_num; uint32_t chip_id; From 3e84d0381578467931d1145a32a55b7e1c3b2b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:05 +0100 Subject: [PATCH 2582/2892] hw/ppc/spapr: Restrict CONFER hypercall to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM handles H_CONFER and does not pass it along to QEMU, so only vhyp (as used by TCG spapr) needs to handle it. [npiggin: Add changelog] Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250127102620.39159-2-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr_hcall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f987ff323f..4f1933b8da 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -580,6 +580,8 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, CPUState *cs = CPU(cpu); SpaprCpuState *spapr_cpu; + assert(tcg_enabled()); /* KVM will have handled this */ + /* * -1 means confer to all other CPUs without dispatch counter check, * otherwise it's a targeted confer. From ffb6440cc5e755d3cdf0cf317880576517cb0de7 Mon Sep 17 00:00:00 2001 From: dan tan Date: Thu, 16 Jan 2025 09:42:26 -0600 Subject: [PATCH 2583/2892] ppc/pnv: Add new PowerPC Special Purpose Registers (RWMR) Register RWMR - Region Weighted Mode Register for privileged access in Power9 and Power10 It controls what the SPURR register produces. Specs: - Power10: https://files.openpower.foundation/s/EgCy7C43p2NSRfR TCG does not model SMT priority, timing, resource controls and status so this register has no effect for now. [npiggin: adjust changelog] Signed-off-by: dan tan Message-ID: <20250116154226.13376-1-dantan@linux.vnet.ibm.com> Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 1 + target/ppc/cpu_init.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 682583d1d1..25b1e6d6b0 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2102,6 +2102,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_PMCR (0x374) #define SPR_440_ITV0 (0x374) #define SPR_440_ITV1 (0x375) +#define SPR_RWMR (0x375) #define SPR_440_ITV2 (0x376) #define SPR_440_ITV3 (0x377) #define SPR_440_CCR1 (0x378) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 54035c7bbb..8d73e11540 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5773,6 +5773,11 @@ static void register_power9_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_WORT, 0); + spr_register_hv(env, SPR_RWMR, "RWMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); #endif } From 215b2ee8f1546ed96a2b3b0f871e18c045317c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:07 +0100 Subject: [PATCH 2584/2892] target/ppc: Make ppc_ldl_code() declaration public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to move code calling ppc_ldl_code() out of excp_helper.c where it is defined. Expose its declaration for few commits, until eventually making it static again once everything is moved. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-4-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 2 +- target/ppc/internal.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index fde9912230..7ed4bbec03 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -144,7 +144,7 @@ static inline bool insn_need_byteswap(CPUArchState *env) return !!(env->msr & ((target_ulong)1 << MSR_LE)); } -static uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) +uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) { uint32_t insn = cpu_ldl_code(env, addr); diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 20fb2ec593..46db6adfcf 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -268,6 +268,8 @@ static inline void pte_invalidate(target_ulong *pte0) #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) +uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr); + #ifdef CONFIG_USER_ONLY void ppc_cpu_record_sigsegv(CPUState *cs, vaddr addr, MMUAccessType access_type, From 0fc76338fe4f587cfe1f582ef0594b32690b7c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:08 +0100 Subject: [PATCH 2585/2892] target/ppc: Move TCG specific exception handlers to tcg-excp_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the TCGCPUOps handlers to a new unit: tcg-excp_helper.c, only built when TCG is selected. See in target/ppc/cpu_init.c: #ifdef CONFIG_TCG static const TCGCPUOps ppc_tcg_ops = { ... .do_unaligned_access = ppc_cpu_do_unaligned_access, .do_transaction_failed = ppc_cpu_do_transaction_failed, .debug_excp_handler = ppc_cpu_debug_excp_handler, .debug_check_breakpoint = ppc_cpu_debug_check_breakpoint, .debug_check_watchpoint = ppc_cpu_debug_check_watchpoint, }; #endif /* CONFIG_TCG */ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-5-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 173 ------------------------------ target/ppc/meson.build | 1 + target/ppc/tcg-excp_helper.c | 202 +++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 173 deletions(-) create mode 100644 target/ppc/tcg-excp_helper.c diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 7ed4bbec03..b05eb7f5ae 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -3144,178 +3144,5 @@ void helper_book3s_trace(CPUPPCState *env, target_ulong prev_ip) raise_exception_err(env, POWERPC_EXCP_TRACE, error_code); } -void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - CPUPPCState *env = cpu_env(cs); - uint32_t insn; - - /* Restore state and reload the insn we executed, for filling in DSISR. */ - cpu_restore_state(cs, retaddr); - insn = ppc_ldl_code(env, env->nip); - - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_4xx: - env->spr[SPR_40x_DEAR] = vaddr; - break; - case POWERPC_MMU_BOOKE: - case POWERPC_MMU_BOOKE206: - env->spr[SPR_BOOKE_DEAR] = vaddr; - break; - default: - env->spr[SPR_DAR] = vaddr; - break; - } - - cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = insn & 0x03FF0000; - cpu_loop_exit(cs); -} - -void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, - vaddr vaddr, unsigned size, - MMUAccessType access_type, - int mmu_idx, MemTxAttrs attrs, - MemTxResult response, uintptr_t retaddr) -{ - CPUPPCState *env = cpu_env(cs); - - switch (env->excp_model) { -#if defined(TARGET_PPC64) - case POWERPC_EXCP_POWER8: - case POWERPC_EXCP_POWER9: - case POWERPC_EXCP_POWER10: - case POWERPC_EXCP_POWER11: - /* - * Machine check codes can be found in processor User Manual or - * Linux or skiboot source. - */ - if (access_type == MMU_DATA_LOAD) { - env->spr[SPR_DAR] = vaddr; - env->spr[SPR_DSISR] = PPC_BIT(57); - env->error_code = PPC_BIT(42); - - } else if (access_type == MMU_DATA_STORE) { - /* - * MCE for stores in POWER is asynchronous so hardware does - * not set DAR, but QEMU can do better. - */ - env->spr[SPR_DAR] = vaddr; - env->error_code = PPC_BIT(36) | PPC_BIT(43) | PPC_BIT(45); - env->error_code |= PPC_BIT(42); - - } else { /* Fetch */ - /* - * is_prefix_insn_excp() tests !PPC_BIT(42) to avoid fetching - * the instruction, so that must always be clear for fetches. - */ - env->error_code = PPC_BIT(36) | PPC_BIT(44) | PPC_BIT(45); - } - break; -#endif - default: - /* - * TODO: Check behaviour for other CPUs, for now do nothing. - * Could add a basic MCE even if real hardware ignores. - */ - return; - } - - cs->exception_index = POWERPC_EXCP_MCHECK; - cpu_loop_exit_restore(cs, retaddr); -} - -void ppc_cpu_debug_excp_handler(CPUState *cs) -{ -#if defined(TARGET_PPC64) - CPUPPCState *env = cpu_env(cs); - - if (env->insns_flags2 & PPC2_ISA207S) { - if (cs->watchpoint_hit) { - if (cs->watchpoint_hit->flags & BP_CPU) { - env->spr[SPR_DAR] = cs->watchpoint_hit->hitaddr; - env->spr[SPR_DSISR] = PPC_BIT(41); - cs->watchpoint_hit = NULL; - raise_exception(env, POWERPC_EXCP_DSI); - } - cs->watchpoint_hit = NULL; - } else if (cpu_breakpoint_test(cs, env->nip, BP_CPU)) { - raise_exception_err(env, POWERPC_EXCP_TRACE, - PPC_BIT(33) | PPC_BIT(43)); - } - } -#endif -} - -bool ppc_cpu_debug_check_breakpoint(CPUState *cs) -{ -#if defined(TARGET_PPC64) - CPUPPCState *env = cpu_env(cs); - - if (env->insns_flags2 & PPC2_ISA207S) { - target_ulong priv; - - priv = env->spr[SPR_CIABR] & PPC_BITMASK(62, 63); - switch (priv) { - case 0x1: /* problem */ - return env->msr & ((target_ulong)1 << MSR_PR); - case 0x2: /* supervisor */ - return (!(env->msr & ((target_ulong)1 << MSR_PR)) && - !(env->msr & ((target_ulong)1 << MSR_HV))); - case 0x3: /* hypervisor */ - return (!(env->msr & ((target_ulong)1 << MSR_PR)) && - (env->msr & ((target_ulong)1 << MSR_HV))); - default: - g_assert_not_reached(); - } - } -#endif - - return false; -} - -bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) -{ -#if defined(TARGET_PPC64) - CPUPPCState *env = cpu_env(cs); - - if (env->insns_flags2 & PPC2_ISA207S) { - if (wp == env->dawr0_watchpoint) { - uint32_t dawrx = env->spr[SPR_DAWRX0]; - bool wt = extract32(dawrx, PPC_BIT_NR(59), 1); - bool wti = extract32(dawrx, PPC_BIT_NR(60), 1); - bool hv = extract32(dawrx, PPC_BIT_NR(61), 1); - bool sv = extract32(dawrx, PPC_BIT_NR(62), 1); - bool pr = extract32(dawrx, PPC_BIT_NR(62), 1); - - if ((env->msr & ((target_ulong)1 << MSR_PR)) && !pr) { - return false; - } else if ((env->msr & ((target_ulong)1 << MSR_HV)) && !hv) { - return false; - } else if (!sv) { - return false; - } - - if (!wti) { - if (env->msr & ((target_ulong)1 << MSR_DR)) { - if (!wt) { - return false; - } - } else { - if (wt) { - return false; - } - } - } - - return true; - } - } -#endif - - return false; -} - #endif /* !CONFIG_USER_ONLY */ #endif /* CONFIG_TCG */ diff --git a/target/ppc/meson.build b/target/ppc/meson.build index db3b7a0c33..8eed1fa40c 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -14,6 +14,7 @@ ppc_ss.add(when: 'CONFIG_TCG', if_true: files( 'int_helper.c', 'mem_helper.c', 'misc_helper.c', + 'tcg-excp_helper.c', 'timebase_helper.c', 'translate.c', 'power8-pmu.c', diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c new file mode 100644 index 0000000000..3402dbe05e --- /dev/null +++ b/target/ppc/tcg-excp_helper.c @@ -0,0 +1,202 @@ +/* + * PowerPC exception emulation helpers for QEMU (TCG specific) + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#include "qemu/osdep.h" +#include "exec/cpu_ldst.h" + +#include "hw/ppc/ppc.h" +#include "internal.h" +#include "cpu.h" +#include "trace.h" + +#ifndef CONFIG_USER_ONLY + +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + CPUPPCState *env = cpu_env(cs); + uint32_t insn; + + /* Restore state and reload the insn we executed, for filling in DSISR. */ + cpu_restore_state(cs, retaddr); + insn = ppc_ldl_code(env, env->nip); + + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_4xx: + env->spr[SPR_40x_DEAR] = vaddr; + break; + case POWERPC_MMU_BOOKE: + case POWERPC_MMU_BOOKE206: + env->spr[SPR_BOOKE_DEAR] = vaddr; + break; + default: + env->spr[SPR_DAR] = vaddr; + break; + } + + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = insn & 0x03FF0000; + cpu_loop_exit(cs); +} + +void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr vaddr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) +{ + CPUPPCState *env = cpu_env(cs); + + switch (env->excp_model) { +#if defined(TARGET_PPC64) + case POWERPC_EXCP_POWER8: + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + case POWERPC_EXCP_POWER11: + /* + * Machine check codes can be found in processor User Manual or + * Linux or skiboot source. + */ + if (access_type == MMU_DATA_LOAD) { + env->spr[SPR_DAR] = vaddr; + env->spr[SPR_DSISR] = PPC_BIT(57); + env->error_code = PPC_BIT(42); + + } else if (access_type == MMU_DATA_STORE) { + /* + * MCE for stores in POWER is asynchronous so hardware does + * not set DAR, but QEMU can do better. + */ + env->spr[SPR_DAR] = vaddr; + env->error_code = PPC_BIT(36) | PPC_BIT(43) | PPC_BIT(45); + env->error_code |= PPC_BIT(42); + + } else { /* Fetch */ + /* + * is_prefix_insn_excp() tests !PPC_BIT(42) to avoid fetching + * the instruction, so that must always be clear for fetches. + */ + env->error_code = PPC_BIT(36) | PPC_BIT(44) | PPC_BIT(45); + } + break; +#endif + default: + /* + * TODO: Check behaviour for other CPUs, for now do nothing. + * Could add a basic MCE even if real hardware ignores. + */ + return; + } + + cs->exception_index = POWERPC_EXCP_MCHECK; + cpu_loop_exit_restore(cs, retaddr); +} + +void ppc_cpu_debug_excp_handler(CPUState *cs) +{ +#if defined(TARGET_PPC64) + CPUPPCState *env = cpu_env(cs); + + if (env->insns_flags2 & PPC2_ISA207S) { + if (cs->watchpoint_hit) { + if (cs->watchpoint_hit->flags & BP_CPU) { + env->spr[SPR_DAR] = cs->watchpoint_hit->hitaddr; + env->spr[SPR_DSISR] = PPC_BIT(41); + cs->watchpoint_hit = NULL; + raise_exception(env, POWERPC_EXCP_DSI); + } + cs->watchpoint_hit = NULL; + } else if (cpu_breakpoint_test(cs, env->nip, BP_CPU)) { + raise_exception_err(env, POWERPC_EXCP_TRACE, + PPC_BIT(33) | PPC_BIT(43)); + } + } +#endif +} + +bool ppc_cpu_debug_check_breakpoint(CPUState *cs) +{ +#if defined(TARGET_PPC64) + CPUPPCState *env = cpu_env(cs); + + if (env->insns_flags2 & PPC2_ISA207S) { + target_ulong priv; + + priv = env->spr[SPR_CIABR] & PPC_BITMASK(62, 63); + switch (priv) { + case 0x1: /* problem */ + return env->msr & ((target_ulong)1 << MSR_PR); + case 0x2: /* supervisor */ + return (!(env->msr & ((target_ulong)1 << MSR_PR)) && + !(env->msr & ((target_ulong)1 << MSR_HV))); + case 0x3: /* hypervisor */ + return (!(env->msr & ((target_ulong)1 << MSR_PR)) && + (env->msr & ((target_ulong)1 << MSR_HV))); + default: + g_assert_not_reached(); + } + } +#endif + + return false; +} + +bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ +#if defined(TARGET_PPC64) + CPUPPCState *env = cpu_env(cs); + + if (env->insns_flags2 & PPC2_ISA207S) { + if (wp == env->dawr0_watchpoint) { + uint32_t dawrx = env->spr[SPR_DAWRX0]; + bool wt = extract32(dawrx, PPC_BIT_NR(59), 1); + bool wti = extract32(dawrx, PPC_BIT_NR(60), 1); + bool hv = extract32(dawrx, PPC_BIT_NR(61), 1); + bool sv = extract32(dawrx, PPC_BIT_NR(62), 1); + bool pr = extract32(dawrx, PPC_BIT_NR(62), 1); + + if ((env->msr & ((target_ulong)1 << MSR_PR)) && !pr) { + return false; + } else if ((env->msr & ((target_ulong)1 << MSR_HV)) && !hv) { + return false; + } else if (!sv) { + return false; + } + + if (!wti) { + if (env->msr & ((target_ulong)1 << MSR_DR)) { + if (!wt) { + return false; + } + } else { + if (wt) { + return false; + } + } + } + + return true; + } + } +#endif + + return false; +} + +#endif /* !CONFIG_USER_ONLY */ From 720c2f2d53d45bae18cbc9eaa39b56c2f00fefb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:09 +0100 Subject: [PATCH 2586/2892] target/ppc: Move ppc_ldl_code() to tcg-excp_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-6-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 21 --------------------- target/ppc/tcg-excp_helper.c | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b05eb7f5ae..8956466db1 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -136,27 +136,6 @@ static void dump_hcall(CPUPPCState *env) env->nip); } -#ifdef CONFIG_TCG -/* Return true iff byteswap is needed to load instruction */ -static inline bool insn_need_byteswap(CPUArchState *env) -{ - /* SYSTEM builds TARGET_BIG_ENDIAN. Need to swap when MSR[LE] is set */ - return !!(env->msr & ((target_ulong)1 << MSR_LE)); -} - -uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) -{ - uint32_t insn = cpu_ldl_code(env, addr); - - if (insn_need_byteswap(env)) { - insn = bswap32(insn); - } - - return insn; -} - -#endif - static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) { const char *es; diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 3402dbe05e..6950b78774 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -199,4 +199,22 @@ bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) return false; } +/* Return true iff byteswap is needed to load instruction */ +static inline bool insn_need_byteswap(CPUArchState *env) +{ + /* SYSTEM builds TARGET_BIG_ENDIAN. Need to swap when MSR[LE] is set */ + return !!(env->msr & ((target_ulong)1 << MSR_LE)); +} + +uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) +{ + uint32_t insn = cpu_ldl_code(env, addr); + + if (insn_need_byteswap(env)) { + insn = bswap32(insn); + } + + return insn; +} + #endif /* !CONFIG_USER_ONLY */ From 30de74bda77fd5eb7c7dc9c50ed4ea39bbc367b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:10 +0100 Subject: [PATCH 2587/2892] target/ppc: Ensure powerpc_mcheck_checkstop() is only called under TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250127102620.39159-7-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 8956466db1..b08cd53688 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "system/tcg.h" #include "system/system.h" #include "system/runstate.h" #include "cpu.h" @@ -30,7 +31,6 @@ #include "trace.h" #ifdef CONFIG_TCG -#include "system/tcg.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #endif @@ -443,13 +443,11 @@ void helper_attn(CPUPPCState *env) static void powerpc_mcheck_checkstop(CPUPPCState *env) { /* KVM guests always have MSR[ME] enabled */ -#ifdef CONFIG_TCG if (FIELD_EX64(env->msr, MSR, ME)) { return; } - + assert(tcg_enabled()); powerpc_checkstop(env, "machine check with MSR[ME]=0"); -#endif } static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) From 2f96c00b61a48803f3963214eac0ae04a7b7ee82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:11 +0100 Subject: [PATCH 2588/2892] target/ppc: Restrict powerpc_checkstop() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose powerpc_checkstop() prototype, and move it to tcg-excp_helper.c, only built when TCG is available. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-8-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 26 -------------------------- target/ppc/internal.h | 4 +++- target/ppc/tcg-excp_helper.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b08cd53688..236e5078f5 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -400,32 +400,6 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, } #ifdef CONFIG_TCG -/* - * This stops the machine and logs CPU state without killing QEMU (like - * cpu_abort()) because it is often a guest error as opposed to a QEMU error, - * so the machine can still be debugged. - */ -static G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason) -{ - CPUState *cs = env_cpu(env); - FILE *f; - - f = qemu_log_trylock(); - if (f) { - fprintf(f, "Entering checkstop state: %s\n", reason); - cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); - qemu_log_unlock(f); - } - - /* - * This stops the machine and logs CPU state without killing QEMU - * (like cpu_abort()) so the machine can still be debugged (because - * it is often a guest error). - */ - qemu_system_guest_panicked(NULL); - cpu_loop_exit_noexc(cs); -} - #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) void helper_attn(CPUPPCState *env) { diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 46db6adfcf..62186bc1e6 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -289,7 +289,9 @@ void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, void ppc_cpu_debug_excp_handler(CPUState *cs); bool ppc_cpu_debug_check_breakpoint(CPUState *cs); bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); -#endif + +G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason); +#endif /* !CONFIG_USER_ONLY */ FIELD(GER_MSK, XMSK, 0, 4) FIELD(GER_MSK, YMSK, 4, 4) diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 6950b78774..93c2d6b5a0 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -17,7 +17,9 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "exec/cpu_ldst.h" +#include "system/runstate.h" #include "hw/ppc/ppc.h" #include "internal.h" @@ -199,6 +201,32 @@ bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) return false; } +/* + * This stops the machine and logs CPU state without killing QEMU (like + * cpu_abort()) because it is often a guest error as opposed to a QEMU error, + * so the machine can still be debugged. + */ +G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason) +{ + CPUState *cs = env_cpu(env); + FILE *f; + + f = qemu_log_trylock(); + if (f) { + fprintf(f, "Entering checkstop state: %s\n", reason); + cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_unlock(f); + } + + /* + * This stops the machine and logs CPU state without killing QEMU + * (like cpu_abort()) so the machine can still be debugged (because + * it is often a guest error). + */ + qemu_system_guest_panicked(NULL); + cpu_loop_exit_noexc(cs); +} + /* Return true iff byteswap is needed to load instruction */ static inline bool insn_need_byteswap(CPUArchState *env) { From 94a37684a59e320a32ba948e9f8d75810c6dcdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:12 +0100 Subject: [PATCH 2589/2892] target/ppc: Remove raise_exception_ra() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced in commit db789c6cd33 ("ppc: Provide basic raise_exception_* functions"), raise_exception_ra() has never been used. Remove as dead code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-9-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 2 -- target/ppc/excp_helper.c | 6 ------ 2 files changed, 8 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 25b1e6d6b0..505b589714 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2756,8 +2756,6 @@ static inline void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, #endif G_NORETURN void raise_exception(CPUPPCState *env, uint32_t exception); -G_NORETURN void raise_exception_ra(CPUPPCState *env, uint32_t exception, - uintptr_t raddr); G_NORETURN void raise_exception_err(CPUPPCState *env, uint32_t exception, uint32_t error_code); G_NORETURN void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 236e5078f5..9e1a2ecc36 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2528,12 +2528,6 @@ void raise_exception(CPUPPCState *env, uint32_t exception) raise_exception_err_ra(env, exception, 0, 0); } -void raise_exception_ra(CPUPPCState *env, uint32_t exception, - uintptr_t raddr) -{ - raise_exception_err_ra(env, exception, 0, raddr); -} - #ifdef CONFIG_TCG void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, uint32_t error_code) From 1d0b82f86db20f875fe44c0df1ed246bf7fb5fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:13 +0100 Subject: [PATCH 2590/2892] target/ppc: Restrict exception helpers to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move exception helpers to tcg-excp_helper.c so they are only built when TCG is selected. Preprocessor guards are added for some helpers unused when CONFIG_USER_ONLY. [npiggin: mention USER_ONLY change] Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250127102620.39159-10-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 34 -------------------------------- target/ppc/tcg-excp_helper.c | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 9e1a2ecc36..6a12402b23 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2504,41 +2504,7 @@ bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #endif /* !CONFIG_USER_ONLY */ -/*****************************************************************************/ -/* Exceptions processing helpers */ - -void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, - uint32_t error_code, uintptr_t raddr) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = exception; - env->error_code = error_code; - cpu_loop_exit_restore(cs, raddr); -} - -void raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code) -{ - raise_exception_err_ra(env, exception, error_code, 0); -} - -void raise_exception(CPUPPCState *env, uint32_t exception) -{ - raise_exception_err_ra(env, exception, 0, 0); -} - #ifdef CONFIG_TCG -void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code) -{ - raise_exception_err_ra(env, exception, error_code, 0); -} - -void helper_raise_exception(CPUPPCState *env, uint32_t exception) -{ - raise_exception_err_ra(env, exception, 0, 0); -} #ifndef CONFIG_USER_ONLY void helper_store_msr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 93c2d6b5a0..268a161459 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -19,15 +19,53 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" #include "system/runstate.h" +#include "helper_regs.h" #include "hw/ppc/ppc.h" #include "internal.h" #include "cpu.h" #include "trace.h" +/*****************************************************************************/ +/* Exceptions processing helpers */ + +void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, + uint32_t error_code, uintptr_t raddr) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = exception; + env->error_code = error_code; + cpu_loop_exit_restore(cs, raddr); +} + +void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ + raise_exception_err_ra(env, exception, error_code, 0); +} + +void helper_raise_exception(CPUPPCState *env, uint32_t exception) +{ + raise_exception_err_ra(env, exception, 0, 0); +} + #ifndef CONFIG_USER_ONLY +void raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ + raise_exception_err_ra(env, exception, error_code, 0); +} + +void raise_exception(CPUPPCState *env, uint32_t exception) +{ + raise_exception_err_ra(env, exception, 0, 0); +} + void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) From ad8ad893a3f563b7ed8df6665d48f47c6337c7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:15 +0100 Subject: [PATCH 2591/2892] target/ppc: Restrict various common helpers to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move helpers common to system/user emulation to tcg-excp_helper.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-12-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 141 ---------------------------------- target/ppc/tcg-excp_helper.c | 143 +++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 141 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 6a12402b23..511e27f726 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2711,148 +2711,7 @@ void helper_rfmci(CPUPPCState *env) /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); } -#endif /* !CONFIG_USER_ONLY */ -void helper_TW(CPUPPCState *env, target_ulong arg1, target_ulong arg2, - uint32_t flags) -{ - if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || - ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || - ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || - ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || - ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_TRAP, GETPC()); - } -} - -#ifdef TARGET_PPC64 -void helper_TD(CPUPPCState *env, target_ulong arg1, target_ulong arg2, - uint32_t flags) -{ - if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || - ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || - ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || - ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || - ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_TRAP, GETPC()); - } -} -#endif /* TARGET_PPC64 */ - -static uint32_t helper_SIMON_LIKE_32_64(uint32_t x, uint64_t key, uint32_t lane) -{ - const uint16_t c = 0xfffc; - const uint64_t z0 = 0xfa2561cdf44ac398ULL; - uint16_t z = 0, temp; - uint16_t k[32], eff_k[32], xleft[33], xright[33], fxleft[32]; - - for (int i = 3; i >= 0; i--) { - k[i] = key & 0xffff; - key >>= 16; - } - xleft[0] = x & 0xffff; - xright[0] = (x >> 16) & 0xffff; - - for (int i = 0; i < 28; i++) { - z = (z0 >> (63 - i)) & 1; - temp = ror16(k[i + 3], 3) ^ k[i + 1]; - k[i + 4] = c ^ z ^ k[i] ^ temp ^ ror16(temp, 1); - } - - for (int i = 0; i < 8; i++) { - eff_k[4 * i + 0] = k[4 * i + ((0 + lane) % 4)]; - eff_k[4 * i + 1] = k[4 * i + ((1 + lane) % 4)]; - eff_k[4 * i + 2] = k[4 * i + ((2 + lane) % 4)]; - eff_k[4 * i + 3] = k[4 * i + ((3 + lane) % 4)]; - } - - for (int i = 0; i < 32; i++) { - fxleft[i] = (rol16(xleft[i], 1) & - rol16(xleft[i], 8)) ^ rol16(xleft[i], 2); - xleft[i + 1] = xright[i] ^ fxleft[i] ^ eff_k[i]; - xright[i + 1] = xleft[i]; - } - - return (((uint32_t)xright[32]) << 16) | xleft[32]; -} - -static uint64_t hash_digest(uint64_t ra, uint64_t rb, uint64_t key) -{ - uint64_t stage0_h = 0ULL, stage0_l = 0ULL; - uint64_t stage1_h, stage1_l; - - for (int i = 0; i < 4; i++) { - stage0_h |= ror64(rb & 0xff, 8 * (2 * i + 1)); - stage0_h |= ((ra >> 32) & 0xff) << (8 * 2 * i); - stage0_l |= ror64((rb >> 32) & 0xff, 8 * (2 * i + 1)); - stage0_l |= (ra & 0xff) << (8 * 2 * i); - rb >>= 8; - ra >>= 8; - } - - stage1_h = (uint64_t)helper_SIMON_LIKE_32_64(stage0_h >> 32, key, 0) << 32; - stage1_h |= helper_SIMON_LIKE_32_64(stage0_h, key, 1); - stage1_l = (uint64_t)helper_SIMON_LIKE_32_64(stage0_l >> 32, key, 2) << 32; - stage1_l |= helper_SIMON_LIKE_32_64(stage0_l, key, 3); - - return stage1_h ^ stage1_l; -} - -static void do_hash(CPUPPCState *env, target_ulong ea, target_ulong ra, - target_ulong rb, uint64_t key, bool store) -{ - uint64_t calculated_hash = hash_digest(ra, rb, key), loaded_hash; - - if (store) { - cpu_stq_data_ra(env, ea, calculated_hash, GETPC()); - } else { - loaded_hash = cpu_ldq_data_ra(env, ea, GETPC()); - if (loaded_hash != calculated_hash) { - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_TRAP, GETPC()); - } - } -} - -#include "qemu/guest-random.h" - -#ifdef TARGET_PPC64 -#define HELPER_HASH(op, key, store, dexcr_aspect) \ -void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ - target_ulong rb) \ -{ \ - if (env->msr & R_MSR_PR_MASK) { \ - if (!(env->spr[SPR_DEXCR] & R_DEXCR_PRO_##dexcr_aspect##_MASK || \ - env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ - return; \ - } else if (!(env->msr & R_MSR_HV_MASK)) { \ - if (!(env->spr[SPR_DEXCR] & R_DEXCR_PNH_##dexcr_aspect##_MASK || \ - env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ - return; \ - } else if (!(env->msr & R_MSR_S_MASK)) { \ - if (!(env->spr[SPR_HDEXCR] & R_HDEXCR_HNU_##dexcr_aspect##_MASK)) \ - return; \ - } \ - \ - do_hash(env, ea, ra, rb, key, store); \ -} -#else -#define HELPER_HASH(op, key, store, dexcr_aspect) \ -void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ - target_ulong rb) \ -{ \ - do_hash(env, ea, ra, rb, key, store); \ -} -#endif /* TARGET_PPC64 */ - -HELPER_HASH(HASHST, env->spr[SPR_HASHKEYR], true, NPHIE) -HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false, NPHIE) -HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true, PHIE) -HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false, PHIE) - -#ifndef CONFIG_USER_ONLY /* Embedded.Processor Control */ static int dbell2irq(target_ulong rb) { diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 268a161459..2459d2d095 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -66,6 +66,149 @@ void raise_exception(CPUPPCState *env, uint32_t exception) raise_exception_err_ra(env, exception, 0, 0); } +#endif /* !CONFIG_USER_ONLY */ + +void helper_TW(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || + ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || + ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || + ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || + ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } +} + +#ifdef TARGET_PPC64 +void helper_TD(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || + ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || + ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || + ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || + ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } +} +#endif /* TARGET_PPC64 */ + +static uint32_t helper_SIMON_LIKE_32_64(uint32_t x, uint64_t key, uint32_t lane) +{ + const uint16_t c = 0xfffc; + const uint64_t z0 = 0xfa2561cdf44ac398ULL; + uint16_t z = 0, temp; + uint16_t k[32], eff_k[32], xleft[33], xright[33], fxleft[32]; + + for (int i = 3; i >= 0; i--) { + k[i] = key & 0xffff; + key >>= 16; + } + xleft[0] = x & 0xffff; + xright[0] = (x >> 16) & 0xffff; + + for (int i = 0; i < 28; i++) { + z = (z0 >> (63 - i)) & 1; + temp = ror16(k[i + 3], 3) ^ k[i + 1]; + k[i + 4] = c ^ z ^ k[i] ^ temp ^ ror16(temp, 1); + } + + for (int i = 0; i < 8; i++) { + eff_k[4 * i + 0] = k[4 * i + ((0 + lane) % 4)]; + eff_k[4 * i + 1] = k[4 * i + ((1 + lane) % 4)]; + eff_k[4 * i + 2] = k[4 * i + ((2 + lane) % 4)]; + eff_k[4 * i + 3] = k[4 * i + ((3 + lane) % 4)]; + } + + for (int i = 0; i < 32; i++) { + fxleft[i] = (rol16(xleft[i], 1) & + rol16(xleft[i], 8)) ^ rol16(xleft[i], 2); + xleft[i + 1] = xright[i] ^ fxleft[i] ^ eff_k[i]; + xright[i + 1] = xleft[i]; + } + + return (((uint32_t)xright[32]) << 16) | xleft[32]; +} + +static uint64_t hash_digest(uint64_t ra, uint64_t rb, uint64_t key) +{ + uint64_t stage0_h = 0ULL, stage0_l = 0ULL; + uint64_t stage1_h, stage1_l; + + for (int i = 0; i < 4; i++) { + stage0_h |= ror64(rb & 0xff, 8 * (2 * i + 1)); + stage0_h |= ((ra >> 32) & 0xff) << (8 * 2 * i); + stage0_l |= ror64((rb >> 32) & 0xff, 8 * (2 * i + 1)); + stage0_l |= (ra & 0xff) << (8 * 2 * i); + rb >>= 8; + ra >>= 8; + } + + stage1_h = (uint64_t)helper_SIMON_LIKE_32_64(stage0_h >> 32, key, 0) << 32; + stage1_h |= helper_SIMON_LIKE_32_64(stage0_h, key, 1); + stage1_l = (uint64_t)helper_SIMON_LIKE_32_64(stage0_l >> 32, key, 2) << 32; + stage1_l |= helper_SIMON_LIKE_32_64(stage0_l, key, 3); + + return stage1_h ^ stage1_l; +} + +static void do_hash(CPUPPCState *env, target_ulong ea, target_ulong ra, + target_ulong rb, uint64_t key, bool store) +{ + uint64_t calculated_hash = hash_digest(ra, rb, key), loaded_hash; + + if (store) { + cpu_stq_data_ra(env, ea, calculated_hash, GETPC()); + } else { + loaded_hash = cpu_ldq_data_ra(env, ea, GETPC()); + if (loaded_hash != calculated_hash) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } + } +} + +#include "qemu/guest-random.h" + +#ifdef TARGET_PPC64 +#define HELPER_HASH(op, key, store, dexcr_aspect) \ +void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ + target_ulong rb) \ +{ \ + if (env->msr & R_MSR_PR_MASK) { \ + if (!(env->spr[SPR_DEXCR] & R_DEXCR_PRO_##dexcr_aspect##_MASK || \ + env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ + return; \ + } else if (!(env->msr & R_MSR_HV_MASK)) { \ + if (!(env->spr[SPR_DEXCR] & R_DEXCR_PNH_##dexcr_aspect##_MASK || \ + env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ + return; \ + } else if (!(env->msr & R_MSR_S_MASK)) { \ + if (!(env->spr[SPR_HDEXCR] & R_HDEXCR_HNU_##dexcr_aspect##_MASK)) \ + return; \ + } \ + \ + do_hash(env, ea, ra, rb, key, store); \ +} +#else +#define HELPER_HASH(op, key, store, dexcr_aspect) \ +void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ + target_ulong rb) \ +{ \ + do_hash(env, ea, ra, rb, key, store); \ +} +#endif /* TARGET_PPC64 */ + +HELPER_HASH(HASHST, env->spr[SPR_HASHKEYR], true, NPHIE) +HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false, NPHIE) +HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true, PHIE) +HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false, PHIE) + +#ifndef CONFIG_USER_ONLY + void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) From b8d6a858fef723616f5a0e244ef0802700ab88d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:16 +0100 Subject: [PATCH 2592/2892] target/ppc: Fix style in excp_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix style in do_rfi() before moving the code around. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-13-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 511e27f726..659852543f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2558,8 +2558,9 @@ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) msr &= ~(1ULL << MSR_POW); /* MSR:TGPR cannot be set by any form of rfi */ - if (env->flags & POWERPC_FLAG_TGPR) + if (env->flags & POWERPC_FLAG_TGPR) { msr &= ~(1ULL << MSR_TGPR); + } #ifdef TARGET_PPC64 /* Switching to 32-bit ? Crop the nip */ From 92c787de34d6103613fa7453765603c94a0494e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:17 +0100 Subject: [PATCH 2593/2892] target/ppc: Make powerpc_excp() prototype public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to move TCG specific code dependent on powerpc_excp() in the next commit, expose its prototype in "internal.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-14-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 2 +- target/ppc/internal.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 659852543f..9ba5335698 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1571,7 +1571,7 @@ static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp) } #endif /* TARGET_PPC64 */ -static void powerpc_excp(PowerPCCPU *cpu, int excp) +void powerpc_excp(PowerPCCPU *cpu, int excp) { CPUPPCState *env = &cpu->env; diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 62186bc1e6..9012d3809c 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -291,6 +291,8 @@ bool ppc_cpu_debug_check_breakpoint(CPUState *cs); bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason); +void powerpc_excp(PowerPCCPU *cpu, int excp); + #endif /* !CONFIG_USER_ONLY */ FIELD(GER_MSK, XMSK, 0, 4) From c2c687013dcc998b02570e02bff373738a636a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jan 2025 11:26:18 +0100 Subject: [PATCH 2594/2892] target/ppc: Restrict ATTN / SCV / PMINSN helpers to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move helper_attn(), helper_scv() and helper_pminsn() to tcg-excp_helper.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20250127102620.39159-15-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 3 - target/ppc/excp_helper.c | 434 ----------------------------------- target/ppc/tcg-excp_helper.c | 423 +++++++++++++++++++++++++++++++++- 3 files changed, 421 insertions(+), 439 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 505b589714..8d43983fe1 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2755,9 +2755,6 @@ static inline void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, } #endif -G_NORETURN void raise_exception(CPUPPCState *env, uint32_t exception); -G_NORETURN void raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code); G_NORETURN void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, uint32_t error_code, uintptr_t raddr); diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 9ba5335698..44e19aacd8 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -30,11 +30,6 @@ #include "trace.h" -#ifdef CONFIG_TCG -#include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#endif - /*****************************************************************************/ /* Exception processing */ #ifndef CONFIG_USER_ONLY @@ -399,21 +394,6 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, env->reserve_addr = -1; } -#ifdef CONFIG_TCG -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -void helper_attn(CPUPPCState *env) -{ - /* POWER attn is unprivileged when enabled by HID, otherwise illegal */ - if ((*env->check_attn)(env)) { - powerpc_checkstop(env, "host executed attn"); - } else { - raise_exception_err(env, POWERPC_EXCP_HV_EMU, - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } -} -#endif -#endif /* CONFIG_TCG */ - static void powerpc_mcheck_checkstop(CPUPPCState *env) { /* KVM guests always have MSR[ME] enabled */ @@ -2503,417 +2483,3 @@ bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif /* !CONFIG_USER_ONLY */ - -#ifdef CONFIG_TCG - -#ifndef CONFIG_USER_ONLY -void helper_store_msr(CPUPPCState *env, target_ulong val) -{ - uint32_t excp = hreg_store_msr(env, val, 0); - - if (excp != 0) { - cpu_interrupt_exittb(env_cpu(env)); - raise_exception(env, excp); - } -} - -void helper_ppc_maybe_interrupt(CPUPPCState *env) -{ - ppc_maybe_interrupt(env); -} - -#ifdef TARGET_PPC64 -void helper_scv(CPUPPCState *env, uint32_t lev) -{ - if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) { - raise_exception_err(env, POWERPC_EXCP_SYSCALL_VECTORED, lev); - } else { - raise_exception_err(env, POWERPC_EXCP_FU, FSCR_IC_SCV); - } -} - -void helper_pminsn(CPUPPCState *env, uint32_t insn) -{ - CPUState *cs = env_cpu(env); - - cs->halted = 1; - - /* Condition for waking up at 0x100 */ - env->resume_as_sreset = (insn != PPC_PM_STOP) || - (env->spr[SPR_PSSCR] & PSSCR_EC); - - /* HDECR is not to wake from PM state, it may have already fired */ - if (env->resume_as_sreset) { - PowerPCCPU *cpu = env_archcpu(env); - ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); - } - - ppc_maybe_interrupt(env); -} -#endif /* TARGET_PPC64 */ - -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); - } - -#ifdef TARGET_PPC64 - /* Switching to 32-bit ? Crop the nip */ - if (!msr_is_64bit(env, msr)) { - nip = (uint32_t)nip; - } -#else - nip = (uint32_t)nip; -#endif - /* XXX: beware: this is false if VLE is supported */ - env->nip = nip & ~((target_ulong)0x00000003); - hreg_store_msr(env, msr, 1); - trace_ppc_excp_rfi(env->nip, env->msr); - /* - * No need to raise an exception here, as rfi is always the last - * insn of a TB - */ - cpu_interrupt_exittb(env_cpu(env)); - /* Reset the reservation */ - env->reserve_addr = -1; - - /* Context synchronizing: check if TCG TLB needs flush */ - check_tlb_flush(env, false); -} - -void helper_rfi(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); -} - -#ifdef TARGET_PPC64 -void helper_rfid(CPUPPCState *env) -{ - /* - * The architecture defines a number of rules for which bits can - * change but in practice, we handle this in hreg_store_msr() - * which will be called by do_rfi(), so there is no need to filter - * here - */ - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); -} - -void helper_rfscv(CPUPPCState *env) -{ - do_rfi(env, env->lr, env->ctr); -} - -void helper_hrfid(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); -} - -void helper_rfebb(CPUPPCState *env, target_ulong s) -{ - target_ulong msr = env->msr; - - /* - * Handling of BESCR bits 32:33 according to PowerISA v3.1: - * - * "If BESCR 32:33 != 0b00 the instruction is treated as if - * the instruction form were invalid." - */ - if (env->spr[SPR_BESCR] & BESCR_INVALID) { - raise_exception_err(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } - - env->nip = env->spr[SPR_EBBRR]; - - /* Switching to 32-bit ? Crop the nip */ - if (!msr_is_64bit(env, msr)) { - env->nip = (uint32_t)env->spr[SPR_EBBRR]; - } - - if (s) { - env->spr[SPR_BESCR] |= BESCR_GE; - } else { - env->spr[SPR_BESCR] &= ~BESCR_GE; - } -} - -/* - * Triggers or queues an 'ebb_excp' EBB exception. All checks - * but FSCR, HFSCR and msr_pr must be done beforehand. - * - * PowerISA v3.1 isn't clear about whether an EBB should be - * postponed or cancelled if the EBB facility is unavailable. - * Our assumption here is that the EBB is cancelled if both - * FSCR and HFSCR EBB facilities aren't available. - */ -static void do_ebb(CPUPPCState *env, int ebb_excp) -{ - PowerPCCPU *cpu = env_archcpu(env); - - /* - * FSCR_EBB and FSCR_IC_EBB are the same bits used with - * HFSCR. - */ - helper_fscr_facility_check(env, FSCR_EBB, 0, FSCR_IC_EBB); - helper_hfscr_facility_check(env, FSCR_EBB, "EBB", FSCR_IC_EBB); - - if (ebb_excp == POWERPC_EXCP_PERFM_EBB) { - env->spr[SPR_BESCR] |= BESCR_PMEO; - } else if (ebb_excp == POWERPC_EXCP_EXTERNAL_EBB) { - env->spr[SPR_BESCR] |= BESCR_EEO; - } - - if (FIELD_EX64(env->msr, MSR, PR)) { - powerpc_excp(cpu, ebb_excp); - } else { - ppc_set_irq(cpu, PPC_INTERRUPT_EBB, 1); - } -} - -void raise_ebb_perfm_exception(CPUPPCState *env) -{ - bool perfm_ebb_enabled = env->spr[SPR_POWER_MMCR0] & MMCR0_EBE && - env->spr[SPR_BESCR] & BESCR_PME && - env->spr[SPR_BESCR] & BESCR_GE; - - if (!perfm_ebb_enabled) { - return; - } - - do_ebb(env, POWERPC_EXCP_PERFM_EBB); -} -#endif /* TARGET_PPC64 */ - -/*****************************************************************************/ -/* Embedded PowerPC specific helpers */ -void helper_40x_rfci(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); -} - -void helper_rfci(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); -} - -void helper_rfdi(CPUPPCState *env) -{ - /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); -} - -void helper_rfmci(CPUPPCState *env) -{ - /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); -} - -/* Embedded.Processor Control */ -static int dbell2irq(target_ulong rb) -{ - int msg = rb & DBELL_TYPE_MASK; - int irq = -1; - - switch (msg) { - case DBELL_TYPE_DBELL: - irq = PPC_INTERRUPT_DOORBELL; - break; - case DBELL_TYPE_DBELL_CRIT: - irq = PPC_INTERRUPT_CDOORBELL; - break; - case DBELL_TYPE_G_DBELL: - case DBELL_TYPE_G_DBELL_CRIT: - case DBELL_TYPE_G_DBELL_MC: - /* XXX implement */ - default: - break; - } - - return irq; -} - -void helper_msgclr(CPUPPCState *env, target_ulong rb) -{ - int irq = dbell2irq(rb); - - if (irq < 0) { - return; - } - - ppc_set_irq(env_archcpu(env), irq, 0); -} - -void helper_msgsnd(target_ulong rb) -{ - int irq = dbell2irq(rb); - int pir = rb & DBELL_PIRTAG_MASK; - CPUState *cs; - - if (irq < 0) { - return; - } - - bql_lock(); - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *cenv = &cpu->env; - - if ((rb & DBELL_BRDCAST_MASK) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { - ppc_set_irq(cpu, irq, 1); - } - } - bql_unlock(); -} - -/* Server Processor Control */ - -static bool dbell_type_server(target_ulong rb) -{ - /* - * A Directed Hypervisor Doorbell message is sent only if the - * message type is 5. All other types are reserved and the - * instruction is a no-op - */ - return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; -} - -static inline bool dbell_bcast_core(target_ulong rb) -{ - return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_CORE; -} - -static inline bool dbell_bcast_subproc(target_ulong rb) -{ - return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; -} - -/* - * Send an interrupt to a thread in the same core as env). - */ -static void msgsnd_core_tir(CPUPPCState *env, uint32_t target_tir, int irq) -{ - PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (ppc_cpu_lpar_single_threaded(cs)) { - if (target_tir == 0) { - ppc_set_irq(cpu, irq, 1); - } - } else { - CPUState *ccs; - - /* Does iothread need to be locked for walking CPU list? */ - bql_lock(); - THREAD_SIBLING_FOREACH(cs, ccs) { - PowerPCCPU *ccpu = POWERPC_CPU(ccs); - if (target_tir == ppc_cpu_tir(ccpu)) { - ppc_set_irq(ccpu, irq, 1); - break; - } - } - bql_unlock(); - } -} - -void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) -{ - if (!dbell_type_server(rb)) { - return; - } - - ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); -} - -void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) -{ - int pir = rb & DBELL_PROCIDTAG_MASK; - bool brdcast = false; - CPUState *cs, *ccs; - PowerPCCPU *cpu; - - if (!dbell_type_server(rb)) { - return; - } - - /* POWER8 msgsnd is like msgsndp (targets a thread within core) */ - if (!(env->insns_flags2 & PPC2_ISA300)) { - msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_HDOORBELL); - return; - } - - /* POWER9 and later msgsnd is a global (targets any thread) */ - cpu = ppc_get_vcpu_by_pir(pir); - if (!cpu) { - return; - } - cs = CPU(cpu); - - if (dbell_bcast_core(rb) || (dbell_bcast_subproc(rb) && - (env->flags & POWERPC_FLAG_SMT_1LPAR))) { - brdcast = true; - } - - if (ppc_cpu_core_single_threaded(cs) || !brdcast) { - ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); - return; - } - - /* - * Why is bql needed for walking CPU list? Answer seems to be because ppc - * irq handling needs it, but ppc_set_irq takes the lock itself if needed, - * so could this be removed? - */ - bql_lock(); - THREAD_SIBLING_FOREACH(cs, ccs) { - ppc_set_irq(POWERPC_CPU(ccs), PPC_INTERRUPT_HDOORBELL, 1); - } - bql_unlock(); -} - -#ifdef TARGET_PPC64 -void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) -{ - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); - - if (!dbell_type_server(rb)) { - return; - } - - ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_DOORBELL, 0); -} - -/* - * sends a message to another thread on the same - * multi-threaded processor - */ -void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) -{ - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); - - if (!dbell_type_server(rb)) { - return; - } - - msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_DOORBELL); -} -#endif /* TARGET_PPC64 */ - -/* Single-step tracing */ -void helper_book3s_trace(CPUPPCState *env, target_ulong prev_ip) -{ - uint32_t error_code = 0; - if (env->insns_flags2 & PPC2_ISA207S) { - /* Load/store reporting, SRR1[35, 36] and SDAR, are not implemented. */ - env->spr[SPR_POWER_SIAR] = prev_ip; - error_code = PPC_BIT(33); - } - raise_exception_err(env, POWERPC_EXCP_TRACE, error_code); -} - -#endif /* !CONFIG_USER_ONLY */ -#endif /* CONFIG_TCG */ diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 2459d2d095..4b859a8ffa 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "qemu/log.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" @@ -55,13 +56,13 @@ void helper_raise_exception(CPUPPCState *env, uint32_t exception) #ifndef CONFIG_USER_ONLY -void raise_exception_err(CPUPPCState *env, uint32_t exception, +static G_NORETURN void raise_exception_err(CPUPPCState *env, uint32_t exception, uint32_t error_code) { raise_exception_err_ra(env, exception, error_code, 0); } -void raise_exception(CPUPPCState *env, uint32_t exception) +static G_NORETURN void raise_exception(CPUPPCState *env, uint32_t exception) { raise_exception_err_ra(env, exception, 0, 0); } @@ -426,4 +427,422 @@ uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) return insn; } +#if defined(TARGET_PPC64) +void helper_attn(CPUPPCState *env) +{ + /* POWER attn is unprivileged when enabled by HID, otherwise illegal */ + if ((*env->check_attn)(env)) { + powerpc_checkstop(env, "host executed attn"); + } else { + raise_exception_err(env, POWERPC_EXCP_HV_EMU, + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); + } +} + +void helper_scv(CPUPPCState *env, uint32_t lev) +{ + if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) { + raise_exception_err(env, POWERPC_EXCP_SYSCALL_VECTORED, lev); + } else { + raise_exception_err(env, POWERPC_EXCP_FU, FSCR_IC_SCV); + } +} + +void helper_pminsn(CPUPPCState *env, uint32_t insn) +{ + CPUState *cs = env_cpu(env); + + cs->halted = 1; + + /* Condition for waking up at 0x100 */ + env->resume_as_sreset = (insn != PPC_PM_STOP) || + (env->spr[SPR_PSSCR] & PSSCR_EC); + + /* HDECR is not to wake from PM state, it may have already fired */ + if (env->resume_as_sreset) { + PowerPCCPU *cpu = env_archcpu(env); + ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); + } + + ppc_maybe_interrupt(env); +} + +#endif /* TARGET_PPC64 */ +void helper_store_msr(CPUPPCState *env, target_ulong val) +{ + uint32_t excp = hreg_store_msr(env, val, 0); + + if (excp != 0) { + cpu_interrupt_exittb(env_cpu(env)); + raise_exception(env, excp); + } +} + +void helper_ppc_maybe_interrupt(CPUPPCState *env) +{ + ppc_maybe_interrupt(env); +} + +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); + } + +#ifdef TARGET_PPC64 + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { + nip = (uint32_t)nip; + } +#else + nip = (uint32_t)nip; +#endif + /* XXX: beware: this is false if VLE is supported */ + env->nip = nip & ~((target_ulong)0x00000003); + hreg_store_msr(env, msr, 1); + trace_ppc_excp_rfi(env->nip, env->msr); + /* + * No need to raise an exception here, as rfi is always the last + * insn of a TB + */ + cpu_interrupt_exittb(env_cpu(env)); + /* Reset the reservation */ + env->reserve_addr = -1; + + /* Context synchronizing: check if TCG TLB needs flush */ + check_tlb_flush(env, false); +} + +void helper_rfi(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); +} + +#ifdef TARGET_PPC64 +void helper_rfid(CPUPPCState *env) +{ + /* + * The architecture defines a number of rules for which bits can + * change but in practice, we handle this in hreg_store_msr() + * which will be called by do_rfi(), so there is no need to filter + * here + */ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); +} + +void helper_rfscv(CPUPPCState *env) +{ + do_rfi(env, env->lr, env->ctr); +} + +void helper_hrfid(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); +} + +void helper_rfebb(CPUPPCState *env, target_ulong s) +{ + target_ulong msr = env->msr; + + /* + * Handling of BESCR bits 32:33 according to PowerISA v3.1: + * + * "If BESCR 32:33 != 0b00 the instruction is treated as if + * the instruction form were invalid." + */ + if (env->spr[SPR_BESCR] & BESCR_INVALID) { + raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); + } + + env->nip = env->spr[SPR_EBBRR]; + + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { + env->nip = (uint32_t)env->spr[SPR_EBBRR]; + } + + if (s) { + env->spr[SPR_BESCR] |= BESCR_GE; + } else { + env->spr[SPR_BESCR] &= ~BESCR_GE; + } +} + +/* + * Triggers or queues an 'ebb_excp' EBB exception. All checks + * but FSCR, HFSCR and msr_pr must be done beforehand. + * + * PowerISA v3.1 isn't clear about whether an EBB should be + * postponed or cancelled if the EBB facility is unavailable. + * Our assumption here is that the EBB is cancelled if both + * FSCR and HFSCR EBB facilities aren't available. + */ +static void do_ebb(CPUPPCState *env, int ebb_excp) +{ + PowerPCCPU *cpu = env_archcpu(env); + + /* + * FSCR_EBB and FSCR_IC_EBB are the same bits used with + * HFSCR. + */ + helper_fscr_facility_check(env, FSCR_EBB, 0, FSCR_IC_EBB); + helper_hfscr_facility_check(env, FSCR_EBB, "EBB", FSCR_IC_EBB); + + if (ebb_excp == POWERPC_EXCP_PERFM_EBB) { + env->spr[SPR_BESCR] |= BESCR_PMEO; + } else if (ebb_excp == POWERPC_EXCP_EXTERNAL_EBB) { + env->spr[SPR_BESCR] |= BESCR_EEO; + } + + if (FIELD_EX64(env->msr, MSR, PR)) { + powerpc_excp(cpu, ebb_excp); + } else { + ppc_set_irq(cpu, PPC_INTERRUPT_EBB, 1); + } +} + +void raise_ebb_perfm_exception(CPUPPCState *env) +{ + bool perfm_ebb_enabled = env->spr[SPR_POWER_MMCR0] & MMCR0_EBE && + env->spr[SPR_BESCR] & BESCR_PME && + env->spr[SPR_BESCR] & BESCR_GE; + + if (!perfm_ebb_enabled) { + return; + } + + do_ebb(env, POWERPC_EXCP_PERFM_EBB); +} +#endif /* TARGET_PPC64 */ + +/*****************************************************************************/ +/* Embedded PowerPC specific helpers */ +void helper_40x_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); +} + +void helper_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); +} + +void helper_rfdi(CPUPPCState *env) +{ + /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); +} + +void helper_rfmci(CPUPPCState *env) +{ + /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); +} + +/* Embedded.Processor Control */ +static int dbell2irq(target_ulong rb) +{ + int msg = rb & DBELL_TYPE_MASK; + int irq = -1; + + switch (msg) { + case DBELL_TYPE_DBELL: + irq = PPC_INTERRUPT_DOORBELL; + break; + case DBELL_TYPE_DBELL_CRIT: + irq = PPC_INTERRUPT_CDOORBELL; + break; + case DBELL_TYPE_G_DBELL: + case DBELL_TYPE_G_DBELL_CRIT: + case DBELL_TYPE_G_DBELL_MC: + /* XXX implement */ + default: + break; + } + + return irq; +} + +void helper_msgclr(CPUPPCState *env, target_ulong rb) +{ + int irq = dbell2irq(rb); + + if (irq < 0) { + return; + } + + ppc_set_irq(env_archcpu(env), irq, 0); +} + +void helper_msgsnd(target_ulong rb) +{ + int irq = dbell2irq(rb); + int pir = rb & DBELL_PIRTAG_MASK; + CPUState *cs; + + if (irq < 0) { + return; + } + + bql_lock(); + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *cenv = &cpu->env; + + if ((rb & DBELL_BRDCAST_MASK) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { + ppc_set_irq(cpu, irq, 1); + } + } + bql_unlock(); +} + +/* Server Processor Control */ + +static bool dbell_type_server(target_ulong rb) +{ + /* + * A Directed Hypervisor Doorbell message is sent only if the + * message type is 5. All other types are reserved and the + * instruction is a no-op + */ + return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; +} + +static inline bool dbell_bcast_core(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_CORE; +} + +static inline bool dbell_bcast_subproc(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; +} + +/* + * Send an interrupt to a thread in the same core as env). + */ +static void msgsnd_core_tir(CPUPPCState *env, uint32_t target_tir, int irq) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (ppc_cpu_lpar_single_threaded(cs)) { + if (target_tir == 0) { + ppc_set_irq(cpu, irq, 1); + } + } else { + CPUState *ccs; + + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + if (target_tir == ppc_cpu_tir(ccpu)) { + ppc_set_irq(ccpu, irq, 1); + break; + } + } + bql_unlock(); + } +} + +void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) +{ + if (!dbell_type_server(rb)) { + return; + } + + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); +} + +void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) +{ + int pir = rb & DBELL_PROCIDTAG_MASK; + bool brdcast = false; + CPUState *cs, *ccs; + PowerPCCPU *cpu; + + if (!dbell_type_server(rb)) { + return; + } + + /* POWER8 msgsnd is like msgsndp (targets a thread within core) */ + if (!(env->insns_flags2 & PPC2_ISA300)) { + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_HDOORBELL); + return; + } + + /* POWER9 and later msgsnd is a global (targets any thread) */ + cpu = ppc_get_vcpu_by_pir(pir); + if (!cpu) { + return; + } + cs = CPU(cpu); + + if (dbell_bcast_core(rb) || (dbell_bcast_subproc(rb) && + (env->flags & POWERPC_FLAG_SMT_1LPAR))) { + brdcast = true; + } + + if (ppc_cpu_core_single_threaded(cs) || !brdcast) { + ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); + return; + } + + /* + * Why is bql needed for walking CPU list? Answer seems to be because ppc + * irq handling needs it, but ppc_set_irq takes the lock itself if needed, + * so could this be removed? + */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + ppc_set_irq(POWERPC_CPU(ccs), PPC_INTERRUPT_HDOORBELL, 1); + } + bql_unlock(); +} + +#ifdef TARGET_PPC64 +void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) +{ + helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); + + if (!dbell_type_server(rb)) { + return; + } + + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_DOORBELL, 0); +} + +/* + * sends a message to another thread on the same + * multi-threaded processor + */ +void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) +{ + helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); + + if (!dbell_type_server(rb)) { + return; + } + + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_DOORBELL); +} +#endif /* TARGET_PPC64 */ + +/* Single-step tracing */ +void helper_book3s_trace(CPUPPCState *env, target_ulong prev_ip) +{ + uint32_t error_code = 0; + if (env->insns_flags2 & PPC2_ISA207S) { + /* Load/store reporting, SRR1[35, 36] and SDAR, are not implemented. */ + env->spr[SPR_POWER_SIAR] = prev_ip; + error_code = PPC_BIT(33); + } + raise_exception_err(env, POWERPC_EXCP_TRACE, error_code); +} #endif /* !CONFIG_USER_ONLY */ From c894bdf78b32ed825903f4f05c765eecf2e4f1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2024 22:30:58 +0100 Subject: [PATCH 2595/2892] hw/ppc/spapr: Convert HPTE() macro as hpte_get_ptr() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert HPTE() macro as hpte_get_ptr() method. Reviewed-by: Nicholas Piggin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20241220213103.6314-2-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index c7cf04e063..0cae4853db 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1402,7 +1402,13 @@ static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, } } -#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) +static uint64_t *hpte_get_ptr(SpaprMachineState *s, unsigned index) +{ + uint64_t *table = s->htab; + + return &table[2 * index]; +} + #define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID) #define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) @@ -1617,7 +1623,7 @@ int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) spapr->htab_shift = shift; for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { - DIRTY_HPTE(HPTE(spapr->htab, i)); + DIRTY_HPTE(hpte_get_ptr(spapr, i)); } } /* We're setting up a hash table, so that means we're not radix */ @@ -2174,7 +2180,7 @@ static void htab_save_chunk(QEMUFile *f, SpaprMachineState *spapr, qemu_put_be32(f, chunkstart); qemu_put_be16(f, n_valid); qemu_put_be16(f, n_invalid); - qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), + qemu_put_buffer(f, (void *)hpte_get_ptr(spapr, chunkstart), HASH_PTE_SIZE_64 * n_valid); } @@ -2200,16 +2206,16 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume invalid HPTEs */ while ((index < htabslots) - && !HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && !HPTE_VALID(hpte_get_ptr(spapr, index))) { + CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; } /* Consume valid HPTEs */ chunkstart = index; while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && HPTE_VALID(hpte_get_ptr(spapr, index))) { + CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; } @@ -2249,7 +2255,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume non-dirty HPTEs */ while ((index < htabslots) - && !HPTE_DIRTY(HPTE(spapr->htab, index))) { + && !HPTE_DIRTY(hpte_get_ptr(spapr, index))) { index++; examined++; } @@ -2257,9 +2263,9 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, chunkstart = index; /* Consume valid dirty HPTEs */ while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_DIRTY(HPTE(spapr->htab, index)) - && HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && HPTE_DIRTY(hpte_get_ptr(spapr, index)) + && HPTE_VALID(hpte_get_ptr(spapr, index))) { + CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; examined++; } @@ -2267,9 +2273,9 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, invalidstart = index; /* Consume invalid dirty HPTEs */ while ((index < htabslots) && (index - invalidstart < USHRT_MAX) - && HPTE_DIRTY(HPTE(spapr->htab, index)) - && !HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && HPTE_DIRTY(hpte_get_ptr(spapr, index)) + && !HPTE_VALID(hpte_get_ptr(spapr, index))) { + CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; examined++; } @@ -2451,11 +2457,11 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) if (spapr->htab) { if (n_valid) { - qemu_get_buffer(f, HPTE(spapr->htab, index), + qemu_get_buffer(f, (void *)hpte_get_ptr(spapr, index), HASH_PTE_SIZE_64 * n_valid); } if (n_invalid) { - memset(HPTE(spapr->htab, index + n_valid), 0, + memset(hpte_get_ptr(spapr, index + n_valid), 0, HASH_PTE_SIZE_64 * n_invalid); } } else { From c5411a065370ae2b056107ae68727c5cbb998233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2024 22:30:59 +0100 Subject: [PATCH 2596/2892] hw/ppc/spapr: Convert HPTE_VALID() macro as hpte_is_valid() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert HPTE_VALID() macro as hpte_is_valid() method. sPAPR data structures including the hash page table are big-endian regardless of current CPU endian mode, so use the big-endian LD/ST API to access the hash PTEs. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Nicholas Piggin Reviewed-by: Harsh Prateek Bora Message-ID: <20241220213103.6314-3-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 0cae4853db..daf997cea1 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1409,7 +1409,11 @@ static uint64_t *hpte_get_ptr(SpaprMachineState *s, unsigned index) return &table[2 * index]; } -#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID) +static bool hpte_is_valid(SpaprMachineState *s, unsigned index) +{ + return ldq_be_p(hpte_get_ptr(s, index)) & HPTE64_V_VALID; +} + #define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) @@ -2206,7 +2210,7 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume invalid HPTEs */ while ((index < htabslots) - && !HPTE_VALID(hpte_get_ptr(spapr, index))) { + && !hpte_is_valid(spapr, index)) { CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; } @@ -2214,7 +2218,7 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume valid HPTEs */ chunkstart = index; while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_VALID(hpte_get_ptr(spapr, index))) { + && hpte_is_valid(spapr, index)) { CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; } @@ -2264,7 +2268,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume valid dirty HPTEs */ while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && HPTE_DIRTY(hpte_get_ptr(spapr, index)) - && HPTE_VALID(hpte_get_ptr(spapr, index))) { + && hpte_is_valid(spapr, index)) { CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; examined++; @@ -2274,7 +2278,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume invalid dirty HPTEs */ while ((index < htabslots) && (index - invalidstart < USHRT_MAX) && HPTE_DIRTY(hpte_get_ptr(spapr, index)) - && !HPTE_VALID(hpte_get_ptr(spapr, index))) { + && !hpte_is_valid(spapr, index)) { CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; examined++; From 9087929887fa51cbd11418e40d6f9fee09e63169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2024 22:31:00 +0100 Subject: [PATCH 2597/2892] hw/ppc/spapr: Convert HPTE_DIRTY() macro as hpte_is_dirty() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert HPTE_DIRTY() macro as hpte_is_dirty() method. sPAPR data structures including the hash page table are big-endian regardless of current CPU endian mode, so use the big-endian LD/ST API to access the hash PTEs. Reviewed-by: Nicholas Piggin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20241220213103.6314-4-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index daf997cea1..dd81398445 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1414,7 +1414,11 @@ static bool hpte_is_valid(SpaprMachineState *s, unsigned index) return ldq_be_p(hpte_get_ptr(s, index)) & HPTE64_V_VALID; } -#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) +static bool hpte_is_dirty(SpaprMachineState *s, unsigned index) +{ + return ldq_be_p(hpte_get_ptr(s, index)) & HPTE64_V_HPTE_DIRTY; +} + #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) @@ -2259,7 +2263,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume non-dirty HPTEs */ while ((index < htabslots) - && !HPTE_DIRTY(hpte_get_ptr(spapr, index))) { + && !hpte_is_dirty(spapr, index)) { index++; examined++; } @@ -2267,7 +2271,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, chunkstart = index; /* Consume valid dirty HPTEs */ while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_DIRTY(hpte_get_ptr(spapr, index)) + && hpte_is_dirty(spapr, index) && hpte_is_valid(spapr, index)) { CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; @@ -2277,7 +2281,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, invalidstart = index; /* Consume invalid dirty HPTEs */ while ((index < htabslots) && (index - invalidstart < USHRT_MAX) - && HPTE_DIRTY(hpte_get_ptr(spapr, index)) + && hpte_is_dirty(spapr, index) && !hpte_is_valid(spapr, index)) { CLEAN_HPTE(hpte_get_ptr(spapr, index)); index++; From 735f9c878a3e97b1257f2345579734ea2877c46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2024 22:31:01 +0100 Subject: [PATCH 2598/2892] hw/ppc/spapr: Convert CLEAN_HPTE() macro as hpte_set_clean() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert CLEAN_HPTE() macro as hpte_set_clean() method. sPAPR data structures including the hash page table are big-endian regardless of current CPU endian mode, so use the big-endian LD/ST API to access the hash PTEs. Reviewed-by: Nicholas Piggin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20241220213103.6314-5-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index dd81398445..3568a97045 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1419,7 +1419,12 @@ static bool hpte_is_dirty(SpaprMachineState *s, unsigned index) return ldq_be_p(hpte_get_ptr(s, index)) & HPTE64_V_HPTE_DIRTY; } -#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) +static void hpte_set_clean(SpaprMachineState *s, unsigned index) +{ + stq_be_p(hpte_get_ptr(s, index), + ldq_be_p(hpte_get_ptr(s, index)) & ~HPTE64_V_HPTE_DIRTY); +} + #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) /* @@ -2215,7 +2220,7 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume invalid HPTEs */ while ((index < htabslots) && !hpte_is_valid(spapr, index)) { - CLEAN_HPTE(hpte_get_ptr(spapr, index)); + hpte_set_clean(spapr, index); index++; } @@ -2223,7 +2228,7 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, chunkstart = index; while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && hpte_is_valid(spapr, index)) { - CLEAN_HPTE(hpte_get_ptr(spapr, index)); + hpte_set_clean(spapr, index); index++; } @@ -2273,7 +2278,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && hpte_is_dirty(spapr, index) && hpte_is_valid(spapr, index)) { - CLEAN_HPTE(hpte_get_ptr(spapr, index)); + hpte_set_clean(spapr, index); index++; examined++; } @@ -2283,7 +2288,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, while ((index < htabslots) && (index - invalidstart < USHRT_MAX) && hpte_is_dirty(spapr, index) && !hpte_is_valid(spapr, index)) { - CLEAN_HPTE(hpte_get_ptr(spapr, index)); + hpte_set_clean(spapr, index); index++; examined++; } From c2ac9f4c297c53aa96d2b500e8db03e57f97d97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2024 22:31:02 +0100 Subject: [PATCH 2599/2892] hw/ppc/spapr: Convert DIRTY_HPTE() macro as hpte_set_dirty() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert DIRTY_HPTE() macro as hpte_set_dirty() method. sPAPR data structures including the hash page table are big-endian regardless of current CPU endian mode, so use the big-endian LD/ST API to access the hash PTEs. Reviewed-by: Nicholas Piggin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Message-ID: <20241220213103.6314-6-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3568a97045..0acf3c53dc 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1425,7 +1425,11 @@ static void hpte_set_clean(SpaprMachineState *s, unsigned index) ldq_be_p(hpte_get_ptr(s, index)) & ~HPTE64_V_HPTE_DIRTY); } -#define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) +static void hpte_set_dirty(SpaprMachineState *s, unsigned index) +{ + stq_be_p(hpte_get_ptr(s, index), + ldq_be_p(hpte_get_ptr(s, index)) | HPTE64_V_HPTE_DIRTY); +} /* * Get the fd to access the kernel htab, re-opening it if necessary @@ -1636,7 +1640,7 @@ int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) spapr->htab_shift = shift; for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { - DIRTY_HPTE(hpte_get_ptr(spapr, i)); + hpte_set_dirty(spapr, i); } } /* We're setting up a hash table, so that means we're not radix */ From 0829b6f0a8c5829b8ed8dc77e1570a8a97e0484d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2024 22:31:03 +0100 Subject: [PATCH 2600/2892] hw/ppc/epapr: Do not swap ePAPR magic value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ePAPR magic value in $r6 doesn't need to be byte swapped. See ePAPR-v1.1.pdf chapter 5.4.1 "Boot CPU Initial Register State" and the following mailing-list threads: https://lore.kernel.org/qemu-devel/CAFEAcA_NR4XW5DNL4nq7vnH4XRH5UWbhQCxuLyKqYk6_FCBrAA@mail.gmail.com/ https://lore.kernel.org/qemu-devel/D6F93NM6OW2L.2FDO88L38PABR@gmail.com/ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Nicholas Piggin Tested-by: BALATON Zoltan Reviewed-by: Harsh Prateek Bora Message-ID: <20241220213103.6314-7-philmd@linaro.org> Signed-off-by: Nicholas Piggin --- hw/ppc/sam460ex.c | 2 +- hw/ppc/virtex_ml507.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 3ecae6a950..7dc3b309c8 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -234,7 +234,7 @@ static void main_cpu_reset(void *opaque) /* Create a mapping for the kernel. */ booke_set_tlb(&env->tlb.tlbe[0], 0, 0, 1 << 31); - env->gpr[6] = tswap32(EPAPR_MAGIC); + env->gpr[6] = EPAPR_MAGIC; env->gpr[7] = (16 * MiB) - 8; /* bi->ima_size; */ } else { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index a01354d991..17115be74d 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -119,7 +119,7 @@ static void main_cpu_reset(void *opaque) /* Create a mapping spanning the 32bit addr space. */ booke_set_tlb(&env->tlb.tlbe[0], 0, 0, 1U << 31); booke_set_tlb(&env->tlb.tlbe[1], 0x80000000, 0x80000000, 1U << 31); - env->gpr[6] = tswap32(EPAPR_MAGIC); + env->gpr[6] = EPAPR_MAGIC; env->gpr[7] = bi->ima_size; } From 7ea6e125297050a239b4028cae5ea3ffbc374861 Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat Date: Fri, 17 Jan 2025 04:06:51 +0000 Subject: [PATCH 2601/2892] ppc: Enable 2nd DAWR support on Power10 PowerNV machine Extend the existing watchpoint facility from TCG DAWR0 emulation to DAWR1 on POWER10. Reviewed-by: Nicholas Piggin Reviewed-by: Harsh Prateek Bora Signed-off-by: Shivaprasad G Bhat Message-ID: <173708680684.1678.13237334676438770057.stgit@linux.ibm.com> Signed-off-by: Nicholas Piggin --- target/ppc/cpu.c | 45 ++++++++++++++++++++-------- target/ppc/cpu.h | 6 ++-- target/ppc/cpu_init.c | 15 ++++++++++ target/ppc/helper.h | 2 ++ target/ppc/machine.c | 3 +- target/ppc/misc_helper.c | 10 +++++++ target/ppc/spr_common.h | 2 ++ target/ppc/tcg-excp_helper.c | 57 +++++++++++++++++++----------------- target/ppc/translate.c | 12 ++++++++ 9 files changed, 109 insertions(+), 43 deletions(-) diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index d148cd76b4..bfcc695de7 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -130,11 +130,13 @@ void ppc_store_ciabr(CPUPPCState *env, target_ulong val) ppc_update_ciabr(env); } -void ppc_update_daw0(CPUPPCState *env) +void ppc_update_daw(CPUPPCState *env, int rid) { CPUState *cs = env_cpu(env); - target_ulong deaw = env->spr[SPR_DAWR0] & PPC_BITMASK(0, 60); - uint32_t dawrx = env->spr[SPR_DAWRX0]; + int spr_dawr = rid ? SPR_DAWR1 : SPR_DAWR0; + int spr_dawrx = rid ? SPR_DAWRX1 : SPR_DAWRX0; + target_ulong deaw = env->spr[spr_dawr] & PPC_BITMASK(0, 60); + uint32_t dawrx = env->spr[spr_dawrx]; int mrd = extract32(dawrx, PPC_BIT_NR(48), 54 - 48); bool dw = extract32(dawrx, PPC_BIT_NR(57), 1); bool dr = extract32(dawrx, PPC_BIT_NR(58), 1); @@ -144,9 +146,9 @@ void ppc_update_daw0(CPUPPCState *env) vaddr len; int flags; - if (env->dawr0_watchpoint) { - cpu_watchpoint_remove_by_ref(cs, env->dawr0_watchpoint); - env->dawr0_watchpoint = NULL; + if (env->dawr_watchpoint[rid]) { + cpu_watchpoint_remove_by_ref(cs, env->dawr_watchpoint[rid]); + env->dawr_watchpoint[rid] = NULL; } if (!dr && !dw) { @@ -166,28 +168,45 @@ void ppc_update_daw0(CPUPPCState *env) flags |= BP_MEM_WRITE; } - cpu_watchpoint_insert(cs, deaw, len, flags, &env->dawr0_watchpoint); + cpu_watchpoint_insert(cs, deaw, len, flags, &env->dawr_watchpoint[rid]); } void ppc_store_dawr0(CPUPPCState *env, target_ulong val) { env->spr[SPR_DAWR0] = val; - ppc_update_daw0(env); + ppc_update_daw(env, 0); } -void ppc_store_dawrx0(CPUPPCState *env, uint32_t val) +static void ppc_store_dawrx(CPUPPCState *env, uint32_t val, int rid) { int hrammc = extract32(val, PPC_BIT_NR(56), 1); if (hrammc) { /* This might be done with a second watchpoint at the xor of DEAW[0] */ - qemu_log_mask(LOG_UNIMP, "%s: DAWRX0[HRAMMC] is unimplemented\n", - __func__); + qemu_log_mask(LOG_UNIMP, "%s: DAWRX%d[HRAMMC] is unimplemented\n", + __func__, rid); } - env->spr[SPR_DAWRX0] = val; - ppc_update_daw0(env); + env->spr[rid ? SPR_DAWRX1 : SPR_DAWRX0] = val; + ppc_update_daw(env, rid); } + +void ppc_store_dawrx0(CPUPPCState *env, uint32_t val) +{ + ppc_store_dawrx(env, val, 0); +} + +void ppc_store_dawr1(CPUPPCState *env, target_ulong val) +{ + env->spr[SPR_DAWR1] = val; + ppc_update_daw(env, 1); +} + +void ppc_store_dawrx1(CPUPPCState *env, uint32_t val) +{ + ppc_store_dawrx(env, val, 1); +} + #endif #endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 8d43983fe1..efab54a068 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1260,7 +1260,7 @@ struct CPUArchState { #if defined(TARGET_PPC64) ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ struct CPUBreakpoint *ciabr_breakpoint; - struct CPUWatchpoint *dawr0_watchpoint; + struct CPUWatchpoint *dawr_watchpoint[2]; #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ @@ -1589,9 +1589,11 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value); void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); void ppc_update_ciabr(CPUPPCState *env); void ppc_store_ciabr(CPUPPCState *env, target_ulong value); -void ppc_update_daw0(CPUPPCState *env); +void ppc_update_daw(CPUPPCState *env, int rid); void ppc_store_dawr0(CPUPPCState *env, target_ulong value); void ppc_store_dawrx0(CPUPPCState *env, uint32_t value); +void ppc_store_dawr1(CPUPPCState *env, target_ulong value); +void ppc_store_dawrx1(CPUPPCState *env, uint32_t value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr(CPUPPCState *env, target_ulong value); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 8d73e11540..9dc5ace828 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5172,6 +5172,20 @@ static void register_book3s_207_dbg_sprs(CPUPPCState *env) KVM_REG_PPC_CIABR, 0x00000000); } +static void register_book3s_310_dbg_sprs(CPUPPCState *env) +{ + spr_register_kvm_hv(env, SPR_DAWR1, "DAWR1", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_dawr1, + KVM_REG_PPC_DAWR1, 0x00000000); + spr_register_kvm_hv(env, SPR_DAWRX1, "DAWRX1", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_dawrx1, + KVM_REG_PPC_DAWRX1, 0x00000000); +} + static void register_970_dbg_sprs(CPUPPCState *env) { /* Breakpoints */ @@ -6584,6 +6598,7 @@ static void init_proc_POWER10(CPUPPCState *env) { register_power9_common_sprs(env); register_HEIR64_spr(env); + register_book3s_310_dbg_sprs(env); register_power10_hash_sprs(env); register_power10_dexcr_sprs(env); register_power10_pmu_sup_sprs(env); diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 11b914e640..ca414f2f43 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -28,6 +28,8 @@ DEF_HELPER_2(store_pcr, void, env, tl) DEF_HELPER_2(store_ciabr, void, env, tl) DEF_HELPER_2(store_dawr0, void, env, tl) DEF_HELPER_2(store_dawrx0, void, env, tl) +DEF_HELPER_2(store_dawr1, void, env, tl) +DEF_HELPER_2(store_dawrx1, void, env, tl) DEF_HELPER_2(store_mmcr0, void, env, tl) DEF_HELPER_2(store_mmcr1, void, env, tl) DEF_HELPER_2(store_mmcrA, void, env, tl) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 0bd7ae6c0c..98df5b4a3a 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -264,7 +264,8 @@ static int cpu_post_load(void *opaque, int version_id) /* Re-set breaks based on regs */ #if defined(TARGET_PPC64) ppc_update_ciabr(env); - ppc_update_daw0(env); + ppc_update_daw(env, 0); + ppc_update_daw(env, 1); #endif /* * TCG needs to re-start the decrementer timer and/or raise the diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 190e9091fc..2d9512c116 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -234,6 +234,16 @@ void helper_store_dawrx0(CPUPPCState *env, target_ulong value) ppc_store_dawrx0(env, value); } +void helper_store_dawr1(CPUPPCState *env, target_ulong value) +{ + ppc_store_dawr1(env, value); +} + +void helper_store_dawrx1(CPUPPCState *env, target_ulong value) +{ + ppc_store_dawrx1(env, value); +} + /* * DPDES register is shared. Each bit reflects the state of the * doorbell interrupt of a thread of the same core. diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 8e3117b463..84c910c440 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -165,6 +165,8 @@ void spr_write_cfar(DisasContext *ctx, int sprn, int gprn); void spr_write_ciabr(DisasContext *ctx, int sprn, int gprn); void spr_write_dawr0(DisasContext *ctx, int sprn, int gprn); void spr_write_dawrx0(DisasContext *ctx, int sprn, int gprn); +void spr_write_dawr1(DisasContext *ctx, int sprn, int gprn); +void spr_write_dawrx1(DisasContext *ctx, int sprn, int gprn); void spr_write_ureg(DisasContext *ctx, int sprn, int gprn); void spr_read_purr(DisasContext *ctx, int gprn, int sprn); void spr_write_purr(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 4b859a8ffa..5a189dc3d7 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -345,39 +345,42 @@ bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) { #if defined(TARGET_PPC64) CPUPPCState *env = cpu_env(cs); + bool wt, wti, hv, sv, pr; + uint32_t dawrx; - if (env->insns_flags2 & PPC2_ISA207S) { - if (wp == env->dawr0_watchpoint) { - uint32_t dawrx = env->spr[SPR_DAWRX0]; - bool wt = extract32(dawrx, PPC_BIT_NR(59), 1); - bool wti = extract32(dawrx, PPC_BIT_NR(60), 1); - bool hv = extract32(dawrx, PPC_BIT_NR(61), 1); - bool sv = extract32(dawrx, PPC_BIT_NR(62), 1); - bool pr = extract32(dawrx, PPC_BIT_NR(62), 1); + if ((env->insns_flags2 & PPC2_ISA207S) && + (wp == env->dawr_watchpoint[0])) { + dawrx = env->spr[SPR_DAWRX0]; + } else if ((env->insns_flags2 & PPC2_ISA310) && + (wp == env->dawr_watchpoint[1])) { + dawrx = env->spr[SPR_DAWRX1]; + } else { + return false; + } - if ((env->msr & ((target_ulong)1 << MSR_PR)) && !pr) { - return false; - } else if ((env->msr & ((target_ulong)1 << MSR_HV)) && !hv) { - return false; - } else if (!sv) { - return false; - } + wt = extract32(dawrx, PPC_BIT_NR(59), 1); + wti = extract32(dawrx, PPC_BIT_NR(60), 1); + hv = extract32(dawrx, PPC_BIT_NR(61), 1); + sv = extract32(dawrx, PPC_BIT_NR(62), 1); + pr = extract32(dawrx, PPC_BIT_NR(62), 1); - if (!wti) { - if (env->msr & ((target_ulong)1 << MSR_DR)) { - if (!wt) { - return false; - } - } else { - if (wt) { - return false; - } - } - } + if ((env->msr & ((target_ulong)1 << MSR_PR)) && !pr) { + return false; + } else if ((env->msr & ((target_ulong)1 << MSR_HV)) && !hv) { + return false; + } else if (!sv) { + return false; + } - return true; + if (!wti) { + if (env->msr & ((target_ulong)1 << MSR_DR)) { + return wt; + } else { + return !wt; } } + + return true; #endif return false; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index b0cc8bf283..a52cbc869a 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -637,6 +637,18 @@ void spr_write_dawrx0(DisasContext *ctx, int sprn, int gprn) translator_io_start(&ctx->base); gen_helper_store_dawrx0(tcg_env, cpu_gpr[gprn]); } + +void spr_write_dawr1(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_dawr1(tcg_env, cpu_gpr[gprn]); +} + +void spr_write_dawrx1(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_dawrx1(tcg_env, cpu_gpr[gprn]); +} #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ /* CTR */ From 5f361ea187ba5f876757d4c90952ff23810f24f5 Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat Date: Fri, 17 Jan 2025 04:07:01 +0000 Subject: [PATCH 2602/2892] ppc: spapr: Enable 2nd DAWR on Power10 pSeries machine As per the PAPR, bit 0 of byte 64 in pa-features property indicates availability of 2nd DAWR registers. i.e. If this bit is set, 2nd DAWR is present, otherwise not. Use KVM_CAP_PPC_DAWR1 capability to find whether kvm supports 2nd DAWR or not. If it's supported, allow user to set the pa-feature bit in guest DT using cap-dawr1 machine capability. Reviewed-by: Nicholas Piggin Reviewed-by: Harsh Prateek Bora Signed-off-by: Ravi Bangoria Signed-off-by: Shivaprasad G Bhat Message-ID: <173708681866.1678.11128625982438367069.stgit@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 7 ++++++- hw/ppc/spapr_caps.c | 43 ++++++++++++++++++++++++++++++++++++++++++ hw/ppc/spapr_hcall.c | 27 +++++++++++++++++--------- include/hw/ppc/spapr.h | 6 +++++- target/ppc/kvm.c | 12 ++++++++++++ target/ppc/kvm_ppc.h | 12 ++++++++++++ 6 files changed, 96 insertions(+), 11 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 0acf3c53dc..fcd2ca515c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -246,7 +246,7 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ /* 54: DecFP, 56: DecI, 58: SHA */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ - /* 60: NM atomic, 62: RNG */ + /* 60: NM atomic, 62: RNG, 64: DAWR1 (ISA 3.1) */ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ /* 68: DEXCR[SBHE|IBRTPDUS|SRAPD|NPHIE|PHIE] */ 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 66 - 71 */ @@ -295,6 +295,9 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, * in pa-features. So hide it from them. */ pa_features[40 + 2] &= ~0x80; /* Radix MMU */ } + if (spapr_get_cap(spapr, SPAPR_CAP_DAWR1)) { + pa_features[66] |= 0x80; + } _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); } @@ -2163,6 +2166,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_rpt_invalidate, &vmstate_spapr_cap_ail_mode_3, &vmstate_spapr_cap_nested_papr, + &vmstate_spapr_cap_dawr1, NULL } }; @@ -4680,6 +4684,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_RPT_INVALIDATE] = SPAPR_CAP_OFF; + smc->default_caps.caps[SPAPR_CAP_DAWR1] = SPAPR_CAP_ON; /* * This cap specifies whether the AIL 3 mode for diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 904bff87ce..9f4fd0cb5e 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -696,6 +696,34 @@ static void cap_ail_mode_3_apply(SpaprMachineState *spapr, } } +static void cap_dawr1_apply(SpaprMachineState *spapr, uint8_t val, + Error **errp) +{ + ERRP_GUARD(); + + if (!val) { + return; /* Disable by default */ + } + + if (!ppc_type_check_compat(MACHINE(spapr)->cpu_type, + CPU_POWERPC_LOGICAL_3_10, 0, + spapr->max_compat_pvr)) { + error_setg(errp, "DAWR1 supported only on POWER10 and later CPUs"); + error_append_hint(errp, "Try appending -machine cap-dawr1=off\n"); + return; + } + + if (kvm_enabled()) { + if (!kvmppc_has_cap_dawr1()) { + error_setg(errp, "DAWR1 not supported by KVM."); + error_append_hint(errp, "Try appending -machine cap-dawr1=off"); + } else if (kvmppc_set_cap_dawr1(val) < 0) { + error_setg(errp, "Error enabling cap-dawr1 with KVM."); + error_append_hint(errp, "Try appending -machine cap-dawr1=off"); + } + } +} + SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -831,6 +859,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_ail_mode_3_apply, }, + [SPAPR_CAP_DAWR1] = { + .name = "dawr1", + .description = "Allow 2nd Data Address Watchpoint Register (DAWR1)", + .index = SPAPR_CAP_DAWR1, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_dawr1_apply, + }, }; static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, @@ -841,6 +878,11 @@ static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, caps = smc->default_caps; + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_3_10, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_DAWR1] = SPAPR_CAP_OFF; + } + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_3_00, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; @@ -975,6 +1017,7 @@ SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); SPAPR_CAP_MIG_STATE(rpt_invalidate, SPAPR_CAP_RPT_INVALIDATE); SPAPR_CAP_MIG_STATE(ail_mode_3, SPAPR_CAP_AIL_MODE_3); +SPAPR_CAP_MIG_STATE(dawr1, SPAPR_CAP_DAWR1); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 4f1933b8da..406aea4ecb 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -822,11 +822,12 @@ static target_ulong h_set_mode_resource_set_ciabr(PowerPCCPU *cpu, return H_SUCCESS; } -static target_ulong h_set_mode_resource_set_dawr0(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong mflags, - target_ulong value1, - target_ulong value2) +static target_ulong h_set_mode_resource_set_dawr(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong mflags, + target_ulong resource, + target_ulong value1, + target_ulong value2) { CPUPPCState *env = &cpu->env; @@ -839,8 +840,15 @@ static target_ulong h_set_mode_resource_set_dawr0(PowerPCCPU *cpu, return H_P4; } - ppc_store_dawr0(env, value1); - ppc_store_dawrx0(env, value2); + if (resource == H_SET_MODE_RESOURCE_SET_DAWR0) { + ppc_store_dawr0(env, value1); + ppc_store_dawrx0(env, value2); + } else if (resource == H_SET_MODE_RESOURCE_SET_DAWR1) { + ppc_store_dawr1(env, value1); + ppc_store_dawrx1(env, value2); + } else { + g_assert_not_reached(); + } return H_SUCCESS; } @@ -919,8 +927,9 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, SpaprMachineState *spapr, args[3]); break; case H_SET_MODE_RESOURCE_SET_DAWR0: - ret = h_set_mode_resource_set_dawr0(cpu, spapr, args[0], args[2], - args[3]); + case H_SET_MODE_RESOURCE_SET_DAWR1: + ret = h_set_mode_resource_set_dawr(cpu, spapr, args[0], args[1], + args[2], args[3]); break; case H_SET_MODE_RESOURCE_LE: ret = h_set_mode_resource_le(cpu, spapr, args[0], args[2], args[3]); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index a6c0547e31..d227f0b94b 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -83,8 +83,10 @@ typedef enum { #define SPAPR_CAP_AIL_MODE_3 0x0C /* Nested PAPR */ #define SPAPR_CAP_NESTED_PAPR 0x0D +/* DAWR1 */ +#define SPAPR_CAP_DAWR1 0x0E /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_NESTED_PAPR + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_DAWR1 + 1) /* * Capability Values @@ -406,6 +408,7 @@ struct SpaprMachineState { #define H_SET_MODE_RESOURCE_SET_DAWR0 2 #define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE 3 #define H_SET_MODE_RESOURCE_LE 4 +#define H_SET_MODE_RESOURCE_SET_DAWR1 5 /* Flags for H_SET_MODE_RESOURCE_LE */ #define H_SET_MODE_ENDIAN_BIG 0 @@ -1003,6 +1006,7 @@ extern const VMStateDescription vmstate_spapr_cap_fwnmi; extern const VMStateDescription vmstate_spapr_cap_rpt_invalidate; extern const VMStateDescription vmstate_spapr_cap_ail_mode_3; extern const VMStateDescription vmstate_spapr_wdt; +extern const VMStateDescription vmstate_spapr_cap_dawr1; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 216638dee4..992356cb75 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -92,6 +92,7 @@ static int cap_large_decr; static int cap_fwnmi; static int cap_rpt_invalidate; static int cap_ail_mode_3; +static int cap_dawr1; #ifdef CONFIG_PSERIES static int cap_papr; @@ -152,6 +153,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_ppc_nested_kvm_hv = kvm_vm_check_extension(s, KVM_CAP_PPC_NESTED_HV); cap_large_decr = kvmppc_get_dec_bits(); cap_fwnmi = kvm_vm_check_extension(s, KVM_CAP_PPC_FWNMI); + cap_dawr1 = kvm_vm_check_extension(s, KVM_CAP_PPC_DAWR1); /* * Note: setting it to false because there is not such capability * in KVM at this moment. @@ -2114,6 +2116,16 @@ int kvmppc_set_fwnmi(PowerPCCPU *cpu) return kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_FWNMI, 0); } +bool kvmppc_has_cap_dawr1(void) +{ + return !!cap_dawr1; +} + +int kvmppc_set_cap_dawr1(int enable) +{ + return kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_DAWR1, 0, enable); +} + int kvmppc_smt_threads(void) { return cap_ppc_smt ? cap_ppc_smt : 1; diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 1d8cb76a6b..a8768c1dfd 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -68,6 +68,8 @@ bool kvmppc_has_cap_htm(void); bool kvmppc_has_cap_mmu_radix(void); bool kvmppc_has_cap_mmu_hash_v3(void); bool kvmppc_has_cap_xive(void); +bool kvmppc_has_cap_dawr1(void); +int kvmppc_set_cap_dawr1(int enable); int kvmppc_get_cap_safe_cache(void); int kvmppc_get_cap_safe_bounds_check(void); int kvmppc_get_cap_safe_indirect_branch(void); @@ -377,6 +379,16 @@ static inline bool kvmppc_has_cap_xive(void) return false; } +static inline bool kvmppc_has_cap_dawr1(void) +{ + return false; +} + +static inline int kvmppc_set_cap_dawr1(int enable) +{ + abort(); +} + static inline int kvmppc_get_cap_safe_cache(void) { return 0; From 5f7d861e65d90e0446b8f22a0bc859a5d8058ea6 Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 21 Feb 2025 21:24:48 +0530 Subject: [PATCH 2603/2892] spapr: nested: Add support for reporting Hostwide state counter Add support for reporting Hostwide state counters for nested KVM pseries guests running with 'cap-nested-papr' on Qemu-TCG acting as L0-hypervisor. The Hostwide state counters are statistics about state that L0-hypervisor maintains for the L2-guests and represent the state of all L2-guests, not just a specific one. These stats counters are exposed to L1-Hypervisor by the L0-Hypervisor via a new bit-flag named 'getHostWideState' for the H_GUEST_GET_STATE hcall which is documented at [1]. Once this flag is set the hcall should populate the Guest-State-Elements in the requested GSB with the stat counter values. Currently following five counters are supported: * l0_guest_heap_size_inuse * l0_guest_heap_size_max * l0_guest_pagetable_size_inuse * l0_guest_pagetable_size_max * l0_guest_pagetable_reclaimed At the moment '0' is being reported for all these counters as these counters doesn't align with how L0-Qemu manages Guest memory. The patch implements support for these counters by adding new members to the 'struct SpaprMachineStateNested'. These new members are then plugged into the existing 'guest_state_element_types[]' with the help of a new macro 'GSBE_NESTED_MACHINE_DW' together with a new helper 'get_machine_ptr()'. guest_state_request_check() is updated to ensure correctness of the requested GSB and finally h_guest_getset_state() is updated to handle the newly introduced flag 'GUEST_STATE_REQUEST_HOST_WIDE'. This patch is tested with the proposed linux-kernel implementation to expose these stat-counter as perf-events at [2]. [1] https://lore.kernel.org/all/20241222140247.174998-2-vaibhav@linux.ibm.com [2] https://lore.kernel.org/all/20241222140247.174998-1-vaibhav@linux.ibm.com Signed-off-by: Vaibhav Jain Reviewed-by: Harsh Prateek Bora Message-ID: <20250221155449.530645-1-vaibhav@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr_nested.c | 119 ++++++++++++++++++++++++---------- include/hw/ppc/spapr_nested.h | 67 +++++++++++++++++-- 2 files changed, 147 insertions(+), 39 deletions(-) diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c index 23958c6383..201f629203 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -65,10 +65,9 @@ static SpaprMachineStateNestedGuest *spapr_get_nested_guest(SpaprMachineState *spapr, target_ulong guestid) { - SpaprMachineStateNestedGuest *guest; - - guest = g_hash_table_lookup(spapr->nested.guests, GINT_TO_POINTER(guestid)); - return guest; + return spapr->nested.guests ? + g_hash_table_lookup(spapr->nested.guests, + GINT_TO_POINTER(guestid)) : NULL; } bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, @@ -594,26 +593,37 @@ static bool spapr_nested_vcpu_check(SpaprMachineStateNestedGuest *guest, return false; } -static void *get_vcpu_state_ptr(SpaprMachineStateNestedGuest *guest, - target_ulong vcpuid) +static void *get_vcpu_state_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) { assert(spapr_nested_vcpu_check(guest, vcpuid, false)); return &guest->vcpus[vcpuid].state; } -static void *get_vcpu_ptr(SpaprMachineStateNestedGuest *guest, - target_ulong vcpuid) +static void *get_vcpu_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) { assert(spapr_nested_vcpu_check(guest, vcpuid, false)); return &guest->vcpus[vcpuid]; } -static void *get_guest_ptr(SpaprMachineStateNestedGuest *guest, +static void *get_guest_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, target_ulong vcpuid) { return guest; /* for GSBE_NESTED */ } +static void *get_machine_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + /* ignore guest and vcpuid for this */ + return &spapr->nested; +} + /* * set=1 means the L1 is trying to set some state * set=0 means the L1 is trying to get some state @@ -1013,7 +1023,15 @@ struct guest_state_element_type guest_state_element_types[] = { GSBE_NESTED_VCPU(GSB_VCPU_OUT_BUFFER, 0x10, runbufout, copy_state_runbuf), GSBE_NESTED_VCPU(GSB_VCPU_OUT_BUF_MIN_SZ, 0x8, runbufout, out_buf_min_size), GSBE_NESTED_VCPU(GSB_VCPU_HDEC_EXPIRY_TB, 0x8, hdecr_expiry_tb, - copy_state_hdecr) + copy_state_hdecr), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_HEAP_INUSE, l0_guest_heap_inuse), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_HEAP_MAX, l0_guest_heap_max), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_PGTABLE_SIZE_INUSE, + l0_guest_pgtable_size_inuse), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_PGTABLE_SIZE_MAX, + l0_guest_pgtable_size_max), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_PGTABLE_RECLAIMED, + l0_guest_pgtable_reclaimed), }; void spapr_nested_gsb_init(void) @@ -1031,8 +1049,13 @@ void spapr_nested_gsb_init(void) else if (type->id >= GSB_VCPU_IN_BUFFER) /* 0x0c00 - 0xf000 Thread + RW */ type->flags = 0; + else if (type->id >= GSB_L0_GUEST_HEAP_INUSE) + + /*0x0800 - 0x0804 Hostwide Counters + RO */ + type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_HOST_WIDE | + GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY; else if (type->id >= GSB_VCPU_LPVR) - /* 0x0003 - 0x0bff Guest + RW */ + /* 0x0003 - 0x07ff Guest + RW */ type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE; else if (type->id >= GSB_HV_VCPU_STATE_SIZE) /* 0x0001 - 0x0002 Guest + RO */ @@ -1139,18 +1162,26 @@ static bool guest_state_request_check(struct guest_state_request *gsr) return false; } - if (type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE) { + if (type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_HOST_WIDE) { + /* Hostwide elements cant be clubbed with other types */ + if (!(gsr->flags & GUEST_STATE_REQUEST_HOST_WIDE)) { + qemu_log_mask(LOG_GUEST_ERROR, "trying to get/set a host wide " + "Element ID:%04x.\n", id); + return false; + } + } else if (type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE) { /* guest wide element type */ if (!(gsr->flags & GUEST_STATE_REQUEST_GUEST_WIDE)) { - qemu_log_mask(LOG_GUEST_ERROR, "trying to set a guest wide " + qemu_log_mask(LOG_GUEST_ERROR, "trying to get/set a guest wide " "Element ID:%04x.\n", id); return false; } } else { /* thread wide element type */ - if (gsr->flags & GUEST_STATE_REQUEST_GUEST_WIDE) { - qemu_log_mask(LOG_GUEST_ERROR, "trying to set a thread wide " - "Element ID:%04x.\n", id); + if (gsr->flags & (GUEST_STATE_REQUEST_GUEST_WIDE | + GUEST_STATE_REQUEST_HOST_WIDE)) { + qemu_log_mask(LOG_GUEST_ERROR, "trying to get/set a thread wide" + " Element ID:%04x.\n", id); return false; } } @@ -1419,7 +1450,8 @@ static target_ulong h_guest_create_vcpu(PowerPCCPU *cpu, return H_SUCCESS; } -static target_ulong getset_state(SpaprMachineStateNestedGuest *guest, +static target_ulong getset_state(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, uint64_t vcpuid, struct guest_state_request *gsr) { @@ -1452,7 +1484,7 @@ static target_ulong getset_state(SpaprMachineStateNestedGuest *guest, /* Get pointer to guest data to get/set */ if (type->location && type->copy) { - ptr = type->location(guest, vcpuid); + ptr = type->location(spapr, guest, vcpuid); assert(ptr); if (!~(type->mask) && is_gsr_invalid(gsr, element, type)) { return H_INVALID_ELEMENT_VALUE; @@ -1469,6 +1501,7 @@ next_element: } static target_ulong map_and_getset_state(PowerPCCPU *cpu, + SpaprMachineState *spapr, SpaprMachineStateNestedGuest *guest, uint64_t vcpuid, struct guest_state_request *gsr) @@ -1492,7 +1525,7 @@ static target_ulong map_and_getset_state(PowerPCCPU *cpu, goto out1; } - rc = getset_state(guest, vcpuid, gsr); + rc = getset_state(spapr, guest, vcpuid, gsr); out1: address_space_unmap(CPU(cpu)->as, gsr->gsb, len, is_write, len); @@ -1510,27 +1543,46 @@ static target_ulong h_guest_getset_state(PowerPCCPU *cpu, target_ulong buf = args[3]; target_ulong buflen = args[4]; struct guest_state_request gsr; - SpaprMachineStateNestedGuest *guest; + SpaprMachineStateNestedGuest *guest = NULL; - guest = spapr_get_nested_guest(spapr, lpid); - if (!guest) { - return H_P2; - } gsr.buf = buf; assert(buflen <= GSB_MAX_BUF_SIZE); gsr.len = buflen; gsr.flags = 0; - if (flags & H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE) { + + /* Works for both get/set state */ + if ((flags & H_GUEST_GET_STATE_FLAGS_GUEST_WIDE) || + (flags & H_GUEST_SET_STATE_FLAGS_GUEST_WIDE)) { gsr.flags |= GUEST_STATE_REQUEST_GUEST_WIDE; } - if (flags & ~H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE) { - return H_PARAMETER; /* flag not supported yet */ - } if (set) { + if (flags & ~H_GUEST_SET_STATE_FLAGS_MASK) { + return H_PARAMETER; + } gsr.flags |= GUEST_STATE_REQUEST_SET; + } else { + /* + * No reserved fields to be set in flags nor both + * GUEST/HOST wide bits + */ + if ((flags & ~H_GUEST_GET_STATE_FLAGS_MASK) || + (flags == H_GUEST_GET_STATE_FLAGS_MASK)) { + return H_PARAMETER; + } + + if (flags & H_GUEST_GET_STATE_FLAGS_HOST_WIDE) { + gsr.flags |= GUEST_STATE_REQUEST_HOST_WIDE; + } } - return map_and_getset_state(cpu, guest, vcpuid, &gsr); + + if (!(gsr.flags & GUEST_STATE_REQUEST_HOST_WIDE)) { + guest = spapr_get_nested_guest(spapr, lpid); + if (!guest) { + return H_P2; + } + } + return map_and_getset_state(cpu, spapr, guest, vcpuid, &gsr); } static target_ulong h_guest_set_state(PowerPCCPU *cpu, @@ -1641,7 +1693,8 @@ static int get_exit_ids(uint64_t srr0, uint16_t ids[16]) return nr; } -static void exit_process_output_buffer(PowerPCCPU *cpu, +static void exit_process_output_buffer(SpaprMachineState *spapr, + PowerPCCPU *cpu, SpaprMachineStateNestedGuest *guest, target_ulong vcpuid, target_ulong *r3) @@ -1679,7 +1732,7 @@ static void exit_process_output_buffer(PowerPCCPU *cpu, gsr.gsb = gsb; gsr.len = VCPU_OUT_BUF_MIN_SZ; gsr.flags = 0; /* get + never guest wide */ - getset_state(guest, vcpuid, &gsr); + getset_state(spapr, guest, vcpuid, &gsr); address_space_unmap(CPU(cpu)->as, gsb, len, true, len); return; @@ -1705,7 +1758,7 @@ void spapr_exit_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, int excp) exit_nested_store_l2(cpu, excp, vcpu); /* do the output buffer for run_vcpu*/ - exit_process_output_buffer(cpu, guest, vcpuid, &r3_return); + exit_process_output_buffer(spapr, cpu, guest, vcpuid, &r3_return); assert(env->spr[SPR_LPIDR] != 0); nested_load_state(cpu, spapr_cpu->nested_host_state); @@ -1820,7 +1873,7 @@ static target_ulong h_guest_run_vcpu(PowerPCCPU *cpu, gsr.buf = vcpu->runbufin.addr; gsr.len = vcpu->runbufin.size; gsr.flags = GUEST_STATE_REQUEST_SET; /* Thread wide + writing */ - rc = map_and_getset_state(cpu, guest, vcpuid, &gsr); + rc = map_and_getset_state(cpu, spapr, guest, vcpuid, &gsr); if (rc == H_SUCCESS) { nested_papr_run_vcpu(cpu, lpid, vcpu); } else { diff --git a/include/hw/ppc/spapr_nested.h b/include/hw/ppc/spapr_nested.h index e420220484..f7be0d5a95 100644 --- a/include/hw/ppc/spapr_nested.h +++ b/include/hw/ppc/spapr_nested.h @@ -11,7 +11,13 @@ #define GSB_TB_OFFSET 0x0004 /* Timebase Offset */ #define GSB_PART_SCOPED_PAGETBL 0x0005 /* Partition Scoped Page Table */ #define GSB_PROCESS_TBL 0x0006 /* Process Table */ - /* RESERVED 0x0007 - 0x0BFF */ + /* RESERVED 0x0007 - 0x07FF */ +#define GSB_L0_GUEST_HEAP_INUSE 0x0800 /* Guest Management Heap Size */ +#define GSB_L0_GUEST_HEAP_MAX 0x0801 /* Guest Management Heap Max Size */ +#define GSB_L0_GUEST_PGTABLE_SIZE_INUSE 0x0802 /* Guest Pagetable Size */ +#define GSB_L0_GUEST_PGTABLE_SIZE_MAX 0x0803 /* Guest Pagetable Max Size */ +#define GSB_L0_GUEST_PGTABLE_RECLAIMED 0x0804 /* Pagetable Reclaim in bytes */ + /* RESERVED 0x0805 - 0xBFF */ #define GSB_VCPU_IN_BUFFER 0x0C00 /* Run VCPU Input Buffer */ #define GSB_VCPU_OUT_BUFFER 0x0C01 /* Run VCPU Out Buffer */ #define GSB_VCPU_VPA 0x0C02 /* HRA to Guest VCPU VPA */ @@ -196,6 +202,38 @@ typedef struct SpaprMachineStateNested { #define NESTED_API_PAPR 2 bool capabilities_set; uint32_t pvr_base; + + /** + * l0_guest_heap_inuse: The currently used bytes in the Hypervisor's Guest + * Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_heap_inuse; + + /** + * host_heap_max: The maximum bytes available in the Hypervisor's Guest + * Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_heap_max; + + /** + * host_pagetable: The currently used bytes in the Hypervisor's Guest + * Page Table Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_pgtable_size_inuse; + + /** + * host_pagetable_max: The maximum bytes available in the Hypervisor's Guest + * Page Table Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_pgtable_size_max; + + /** + * host_pagetable_reclaim: The amount of space in bytes that has been + * reclaimed due to overcommit in the Hypervisor's Guest Page Table + * Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_pgtable_reclaimed; + GHashTable *guests; } SpaprMachineStateNested; @@ -229,9 +267,15 @@ typedef struct SpaprMachineStateNestedGuest { #define HVMASK_HDEXCR 0x00000000FFFFFFFF #define HVMASK_TB_OFFSET 0x000000FFFFFFFFFF #define GSB_MAX_BUF_SIZE (1024 * 1024) -#define H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE 0x8000000000000000 -#define GUEST_STATE_REQUEST_GUEST_WIDE 0x1 -#define GUEST_STATE_REQUEST_SET 0x2 +#define H_GUEST_GET_STATE_FLAGS_MASK 0xC000000000000000ULL +#define H_GUEST_SET_STATE_FLAGS_MASK 0x8000000000000000ULL +#define H_GUEST_SET_STATE_FLAGS_GUEST_WIDE 0x8000000000000000ULL +#define H_GUEST_GET_STATE_FLAGS_GUEST_WIDE 0x8000000000000000ULL +#define H_GUEST_GET_STATE_FLAGS_HOST_WIDE 0x4000000000000000ULL + +#define GUEST_STATE_REQUEST_GUEST_WIDE 0x1 +#define GUEST_STATE_REQUEST_HOST_WIDE 0x2 +#define GUEST_STATE_REQUEST_SET 0x4 /* * As per ISA v3.1B, following bits are reserved: @@ -251,6 +295,15 @@ typedef struct SpaprMachineStateNestedGuest { .copy = (c) \ } +#define GSBE_NESTED_MACHINE_DW(i, f) { \ + .id = (i), \ + .size = 8, \ + .location = get_machine_ptr, \ + .offset = offsetof(struct SpaprMachineStateNested, f), \ + .copy = copy_state_8to8, \ + .mask = HVMASK_DEFAULT \ +} + #define GSBE_NESTED(i, sz, f, c) { \ .id = (i), \ .size = (sz), \ @@ -509,9 +562,11 @@ struct guest_state_element_type { uint16_t id; int size; #define GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE 0x1 -#define GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY 0x2 +#define GUEST_STATE_ELEMENT_TYPE_FLAG_HOST_WIDE 0x2 +#define GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY 0x4 uint16_t flags; - void *(*location)(SpaprMachineStateNestedGuest *, target_ulong); + void *(*location)(struct SpaprMachineState *, SpaprMachineStateNestedGuest *, + target_ulong); size_t offset; void (*copy)(void *, void *, bool); uint64_t mask; From e8291ec16da80566c121c68d9112be458954d90b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 19 Dec 2024 13:40:31 +1000 Subject: [PATCH 2604/2892] target/ppc: fix timebase register reset state (H)DEC and PURR get reset before icount does, which causes them to be skewed and not match the init state. This can cause replay to not match the recorded trace exactly. For DEC and HDEC this is usually not noticable since they tend to get programmed before affecting the target machine. PURR has been observed to cause replay bugs when running Linux. Fix this by resetting using a time of 0. Message-ID: <20241219034035.1826173-2-npiggin@gmail.com> Signed-off-by: Nicholas Piggin --- hw/ppc/ppc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 90e3db5cfe..3a80931538 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -1123,16 +1123,21 @@ void cpu_ppc_tb_reset(CPUPPCState *env) timer_del(tb_env->hdecr_timer); ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); tb_env->hdecr_next = 0; + _cpu_ppc_store_hdecr(cpu, 0, 0, 0, 64); } /* * There is a bug in Linux 2.4 kernels: * if a decrementer exception is pending when it enables msr_ee at startup, * it's not ready to handle it... + * + * On machine reset, this is called before icount is reset, so for + * icount-mode, setting TB registers using now == qemu_clock_get_ns() + * results in them being garbage after icount is reset. Use an + * explicit now == 0 to get a consistent reset state. */ - cpu_ppc_store_decr(env, -1); - cpu_ppc_store_hdecr(env, -1); - cpu_ppc_store_purr(env, 0x0000000000000000ULL); + _cpu_ppc_store_decr(cpu, 0, 0, -1, 64); + _cpu_ppc_store_purr(env, 0, 0); } void cpu_ppc_tb_free(CPUPPCState *env) From d8a624515a9773cb9d7afd47926bd64ae8fa0fc6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 19 Dec 2024 13:40:35 +1000 Subject: [PATCH 2605/2892] target/ppc: Wire up BookE ATB registers for e500 family From the Freescale PowerPC Architecture Primer: Alternate time base APU. This APU, implemented on the e500v2, defines a 64-bit time base counter that differs from the PowerPC defined time base in that it is not writable and counts at a different, and typically much higher, frequency. The alternate time base always counts up, wrapping when the 64-bit count overflows. This implementation of ATB uses the same frequency as the TB. The existing spr_read_atbu/l functions are unused without this patch to wire them into the SPR. RTEMS uses this SPR on the e6500, though this hasn't been tested. Message-ID: <20241219034035.1826173-6-npiggin@gmail.com> Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9dc5ace828..8b590e7f17 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -922,6 +922,18 @@ static void register_BookE206_sprs(CPUPPCState *env, uint32_t mas_mask, #endif } +static void register_atb_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_ATBL, "ATBL", + &spr_read_atbl, SPR_NOACCESS, + &spr_read_atbl, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_ATBU, "ATBU", + &spr_read_atbu, SPR_NOACCESS, + &spr_read_atbu, SPR_NOACCESS, + 0x00000000); +} + /* SPR specific to PowerPC 440 implementation */ static void register_440_sprs(CPUPPCState *env) { @@ -2911,6 +2923,11 @@ static void init_proc_e500(CPUPPCState *env, int version) register_BookE206_sprs(env, 0x000000DF, tlbncfg, mmucfg); register_usprgh_sprs(env); + if (version != fsl_e500v1) { + /* e500v1 has no support for alternate timebase */ + register_atb_sprs(env); + } + spr_register(env, SPR_HID0, "HID0", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, From b4aa82dc3a698abdcdef342fc1f4620f888c3cf7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 19 Dec 2024 13:40:34 +1000 Subject: [PATCH 2606/2892] target/ppc: Avoid warning message for zero process table entries A translation that encounters a process table entry that is zero is something that Linux does to cause certain kernel NULL pointer dereferences to fault. It is not itself a programming error, so avoid the guest error log. Message-ID: <20241219034035.1826173-5-npiggin@gmail.com> Signed-off-by: Nicholas Piggin --- target/ppc/mmu-radix64.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 1d3d9e1be7..461eda4a3d 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -571,6 +571,20 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, prtbe0 = ldq_phys(cs->as, h_raddr); } + /* + * Some Linux uses a zero process table entry in PID!=0 for kernel context + * without userspace in order to fault on NULL dereference, because using + * PIDR=0 for the kernel causes the Q0 page table to be used to translate + * Q3 as well. Check for that case here to avoid the invalid configuration + * message. + */ + if (unlikely(!prtbe0)) { + if (guest_visible) { + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_R_BADCONFIG); + } + return 1; + } + /* Walk Radix Tree from Process Table Entry to Convert EA to RA */ *g_page_size = PRTBE_R_GET_RTS(prtbe0); base_addr = prtbe0 & PRTBE_R_RPDB; From d91b101da1075f57dda0f30f6802129328716da1 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 19 Dec 2024 13:40:33 +1000 Subject: [PATCH 2607/2892] spapr: Generate random HASHPKEYR for spapr machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hypervisor is expected to create a value for the HASHPKEY SPR for each partition. Currently it uses zero for all partitions, use a random number instead, which in theory might make kernel ROP protection more secure. Signed-of-by: Nicholas Piggin Reviewed-by: Harsh Prateek Bora Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20241219034035.1826173-4-npiggin@gmail.com> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 3 +++ hw/ppc/spapr_cpu_core.c | 2 ++ include/hw/ppc/spapr.h | 1 + 3 files changed, 6 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index fcd2ca515c..a415e51d07 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2917,6 +2917,9 @@ static void spapr_machine_init(MachineState *machine) spapr_ovec_set(spapr->ov5, OV5_XIVE_EXPLOIT); } + qemu_guest_getrandom_nofail(&spapr->hashpkey_val, + sizeof(spapr->hashpkey_val)); + /* init CPUs */ spapr_init_cpus(spapr); diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 9e0e0648a7..0671d9e44b 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -273,6 +273,8 @@ static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, env->spr_cb[SPR_PIR].default_value = cs->cpu_index; env->spr_cb[SPR_TIR].default_value = thread_index; + env->spr_cb[SPR_HASHPKEYR].default_value = spapr->hashpkey_val; + cpu_ppc_set_1lpar(cpu); /* Set time-base frequency to 512 MHz. vhyp must be set first. */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index d227f0b94b..39bd5bd5ed 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -203,6 +203,7 @@ struct SpaprMachineState { uint32_t fdt_initial_size; void *fdt_blob; uint8_t fdt_rng_seed[32]; + uint64_t hashpkey_val; long kernel_size; bool kernel_le; uint64_t kernel_addr; From 222d37d38999546d8407139410ee45df98fac805 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 27 Feb 2025 17:39:15 +0100 Subject: [PATCH 2608/2892] ppc/amigaone: Simplify replacement dummy_fw There's no need to do shift in a loop, doing it in one instruction works just as well, only the result is used. Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Message-ID: <446bf740cbb99422be2cc5a31e51a1034eddded7.1740673173.git.balaton@eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index b02792221c..4290d58613 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -41,10 +41,7 @@ /* AmigaOS calls this routine from ROM, use this if no firmware loaded */ static const char dummy_fw[] = { - 0x38, 0x00, 0x00, 0x08, /* li r0,8 */ - 0x7c, 0x09, 0x03, 0xa6, /* mtctr r0 */ - 0x54, 0x63, 0xf8, 0x7e, /* srwi r3,r3,1 */ - 0x42, 0x00, 0xff, 0xfc, /* bdnz 0x8 */ + 0x54, 0x63, 0xc2, 0x3e, /* srwi r3,r3,8 */ 0x7c, 0x63, 0x18, 0xf8, /* not r3,r3 */ 0x4e, 0x80, 0x00, 0x20, /* blr */ }; From addff513a10ea7cf3bab27b65ec492e7619eadc5 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 27 Feb 2025 17:39:16 +0100 Subject: [PATCH 2609/2892] ppc/amigaone: Implement NVRAM emulation The board has a battery backed NVRAM where U-Boot environment is stored which is also accessed by AmigaOS and e.g. C:NVGetVar command crashes without it having at least a valid checksum. [npiggin: 32-bit compile fix] Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Message-ID: <7e4c0107ef6bdc2b20fb1e780a188275c7dc1e49.1740673173.git.balaton@eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 113 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 4290d58613..feb2cf452c 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -21,10 +21,13 @@ #include "hw/ide/pci.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/ppc/ppc.h" +#include "system/block-backend.h" #include "system/qtest.h" #include "system/reset.h" #include "kvm_ppc.h" +#include /* for crc32 */ + #define BUS_FREQ_HZ 100000000 /* @@ -46,6 +49,100 @@ static const char dummy_fw[] = { 0x4e, 0x80, 0x00, 0x20, /* blr */ }; +#define NVRAM_ADDR 0xfd0e0000 +#define NVRAM_SIZE (4 * KiB) + +#define TYPE_A1_NVRAM "a1-nvram" +OBJECT_DECLARE_SIMPLE_TYPE(A1NVRAMState, A1_NVRAM) + +struct A1NVRAMState { + SysBusDevice parent_obj; + + MemoryRegion mr; + BlockBackend *blk; +}; + +static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned int size) +{ + /* read callback not used because of romd mode */ + g_assert_not_reached(); +} + +static void nvram_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + A1NVRAMState *s = opaque; + uint8_t *p = memory_region_get_ram_ptr(&s->mr); + + p[addr] = val; + if (s->blk) { + blk_pwrite(s->blk, addr, 1, &val, 0); + } +} + +static const MemoryRegionOps nvram_ops = { + .read = nvram_read, + .write = nvram_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void nvram_realize(DeviceState *dev, Error **errp) +{ + A1NVRAMState *s = A1_NVRAM(dev); + void *p; + uint32_t *c; + + memory_region_init_rom_device(&s->mr, NULL, &nvram_ops, s, "nvram", + NVRAM_SIZE, &error_fatal); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); + c = p = memory_region_get_ram_ptr(&s->mr); + if (s->blk) { + if (blk_getlength(s->blk) != NVRAM_SIZE) { + error_setg(errp, "NVRAM backing file size must be %" PRId64 "bytes", + NVRAM_SIZE); + return; + } + blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, + BLK_PERM_ALL, &error_fatal); + if (blk_pread(s->blk, 0, NVRAM_SIZE, p, 0) < 0) { + error_setg(errp, "Cannot read NVRAM contents from backing file"); + return; + } + } + if (*c == 0) { + *c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4)); + if (s->blk) { + blk_pwrite(s->blk, 0, 4, p, 0); + } + } +} + +static const Property nvram_properties[] = { + DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk), +}; + +static void nvram_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = nvram_realize; + device_class_set_props(dc, nvram_properties); +} + +static const TypeInfo nvram_types[] = { + { + .name = TYPE_A1_NVRAM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A1NVRAMState), + .class_init = nvram_class_init, + }, +}; +DEFINE_TYPES(nvram_types) + static void amigaone_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -72,7 +169,7 @@ static void amigaone_init(MachineState *machine) DeviceState *dev; I2CBus *i2c_bus; uint8_t *spd_data; - int i; + DriveInfo *di; /* init CPU */ cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); @@ -97,6 +194,16 @@ static void amigaone_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), 0x40000000, mr); } + /* nvram */ + dev = qdev_new(TYPE_A1_NVRAM); + di = drive_get(IF_MTD, 0, 0); + if (di) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di)); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(get_system_memory(), NVRAM_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); + /* allocate and load firmware */ rom = g_new(MemoryRegion, 1); memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal); @@ -136,7 +243,7 @@ static void amigaone_init(MachineState *machine) pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); mr = g_new(MemoryRegion, 1); memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem, - 0, 0x1000000); + 0, 0xe0000); memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); mr = g_new(MemoryRegion, 1); memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, @@ -153,7 +260,7 @@ static void amigaone_init(MachineState *machine) qdev_connect_gpio_out_named(DEVICE(via), "intr", 0, qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); - for (i = 0; i < PCI_NUM_PINS; i++) { + for (int i = 0; i < PCI_NUM_PINS; i++) { qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via), "pirq", i)); } From c2bed9957a7c30cec8dacc9581700088434889b6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 27 Feb 2025 17:39:17 +0100 Subject: [PATCH 2610/2892] ppc/amigaone: Add default environment Initialise empty NVRAM with default values. This also enables IDE UDMA mode in AmigaOS that is faster but has to be enabled in environment due to problems with real hardware but that does not affect emulation so we can use faster defaults here. Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Message-ID: <4d63f88191612329e0ca8102c7c0d4fc626dc372.1740673173.git.balaton@eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index feb2cf452c..1c6f2a944d 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -52,6 +52,28 @@ static const char dummy_fw[] = { #define NVRAM_ADDR 0xfd0e0000 #define NVRAM_SIZE (4 * KiB) +static char default_env[] = + "baudrate=115200\0" + "stdout=vga\0" + "stdin=ps2kbd\0" + "bootcmd=boota; menu; run menuboot_cmd\0" + "boot1=ide\0" + "boot2=cdrom\0" + "boota_timeout=3\0" + "ide_doreset=on\0" + "pci_irqa=9\0" + "pci_irqa_select=level\0" + "pci_irqb=10\0" + "pci_irqb_select=level\0" + "pci_irqc=11\0" + "pci_irqc_select=level\0" + "pci_irqd=7\0" + "pci_irqd_select=level\0" + "a1ide_irq=1111\0" + "a1ide_xfer=FFFF\0"; +#define CRC32_DEFAULT_ENV 0xb5548481 +#define CRC32_ALL_ZEROS 0x603b0489 + #define TYPE_A1_NVRAM "a1-nvram" OBJECT_DECLARE_SIMPLE_TYPE(A1NVRAMState, A1_NVRAM) @@ -94,7 +116,7 @@ static void nvram_realize(DeviceState *dev, Error **errp) { A1NVRAMState *s = A1_NVRAM(dev); void *p; - uint32_t *c; + uint32_t crc, *c; memory_region_init_rom_device(&s->mr, NULL, &nvram_ops, s, "nvram", NVRAM_SIZE, &error_fatal); @@ -113,12 +135,25 @@ static void nvram_realize(DeviceState *dev, Error **errp) return; } } + crc = crc32(0, p + 4, NVRAM_SIZE - 4); + if (crc == CRC32_ALL_ZEROS) { /* If env is uninitialized set default */ + *c = cpu_to_be32(CRC32_DEFAULT_ENV); + /* Also copies terminating \0 as env is terminated by \0\0 */ + memcpy(p + 4, default_env, sizeof(default_env)); + if (s->blk) { + blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0); + } + return; + } if (*c == 0) { *c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4)); if (s->blk) { blk_pwrite(s->blk, 0, 4, p, 0); } } + if (be32_to_cpu(*c) != crc) { + warn_report("NVRAM checksum mismatch"); + } } static const Property nvram_properties[] = { From 34f053d86b2835c038d75e3d6ac967d73594e157 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 27 Feb 2025 17:39:18 +0100 Subject: [PATCH 2611/2892] ppc/amigaone: Add kernel and initrd support Add support for -kernel, -initrd and -append command line options. Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Message-ID: <489b1be5d95d5153e924c95b0691b8b53f9ffb9e.1740673173.git.balaton@eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 113 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 1c6f2a944d..359f5fa125 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -25,11 +25,14 @@ #include "system/qtest.h" #include "system/reset.h" #include "kvm_ppc.h" +#include "elf.h" #include /* for crc32 */ #define BUS_FREQ_HZ 100000000 +#define INITRD_MIN_ADDR 0x600000 + /* * Firmware binary available at * https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28 @@ -178,12 +181,68 @@ static const TypeInfo nvram_types[] = { }; DEFINE_TYPES(nvram_types) +struct boot_info { + hwaddr entry; + hwaddr stack; + hwaddr bd_info; + hwaddr initrd_start; + hwaddr initrd_end; + hwaddr cmdline_start; + hwaddr cmdline_end; +}; + +/* Board info struct from U-Boot */ +struct bd_info { + uint32_t bi_memstart; + uint32_t bi_memsize; + uint32_t bi_flashstart; + uint32_t bi_flashsize; + uint32_t bi_flashoffset; + uint32_t bi_sramstart; + uint32_t bi_sramsize; + uint32_t bi_bootflags; + uint32_t bi_ip_addr; + uint8_t bi_enetaddr[6]; + uint16_t bi_ethspeed; + uint32_t bi_intfreq; + uint32_t bi_busfreq; + uint32_t bi_baudrate; +} QEMU_PACKED; + +static void create_bd_info(hwaddr addr, ram_addr_t ram_size) +{ + struct bd_info *bd = g_new0(struct bd_info, 1); + + bd->bi_memsize = cpu_to_be32(ram_size); + bd->bi_flashstart = cpu_to_be32(PROM_ADDR); + bd->bi_flashsize = cpu_to_be32(1); /* match what U-Boot detects */ + bd->bi_bootflags = cpu_to_be32(1); + bd->bi_intfreq = cpu_to_be32(11.5 * BUS_FREQ_HZ); + bd->bi_busfreq = cpu_to_be32(BUS_FREQ_HZ); + bd->bi_baudrate = cpu_to_be32(115200); + + cpu_physical_memory_write(addr, bd, sizeof(*bd)); +} + static void amigaone_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; cpu_reset(CPU(cpu)); - cpu_ppc_tb_reset(&cpu->env); + if (env->load_info) { + struct boot_info *bi = env->load_info; + + env->gpr[1] = bi->stack; + env->gpr[2] = 1024; + env->gpr[3] = bi->bd_info; + env->gpr[4] = bi->initrd_start; + env->gpr[5] = bi->initrd_end; + env->gpr[6] = bi->cmdline_start; + env->gpr[7] = bi->cmdline_end; + env->nip = bi->entry; + } + cpu_ppc_tb_reset(env); } static void fix_spd_data(uint8_t *spd) @@ -205,6 +264,8 @@ static void amigaone_init(MachineState *machine) I2CBus *i2c_bus; uint8_t *spd_data; DriveInfo *di; + hwaddr loadaddr; + struct boot_info *bi = NULL; /* init CPU */ cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); @@ -301,6 +362,56 @@ static void amigaone_init(MachineState *machine) } pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide"))); pci_vga_init(pci_bus); + + if (!machine->kernel_filename) { + return; + } + + /* handle -kernel, -initrd, -append options and emulate U-Boot */ + bi = g_new0(struct boot_info, 1); + cpu->env.load_info = bi; + + loadaddr = MIN(machine->ram_size, 256 * MiB); + bi->bd_info = loadaddr - 8 * MiB; + create_bd_info(bi->bd_info, machine->ram_size); + bi->stack = bi->bd_info - 64 * KiB - 8; + + if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { + size_t len = strlen(machine->kernel_cmdline); + + loadaddr = bi->bd_info + 1 * MiB; + cpu_physical_memory_write(loadaddr, machine->kernel_cmdline, len + 1); + bi->cmdline_start = loadaddr; + bi->cmdline_end = loadaddr + len + 1; /* including terminating '\0' */ + } + + sz = load_elf(machine->kernel_filename, NULL, NULL, NULL, + &bi->entry, &loadaddr, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); + if (sz <= 0) { + sz = load_uimage(machine->kernel_filename, &bi->entry, &loadaddr, + NULL, NULL, NULL); + } + if (sz <= 0) { + error_report("Could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + loadaddr += sz; + + if (machine->initrd_filename) { + loadaddr = ROUND_UP(loadaddr + 4 * MiB, 4 * KiB); + loadaddr = MAX(loadaddr, INITRD_MIN_ADDR); + sz = load_image_targphys(machine->initrd_filename, loadaddr, + bi->bd_info - loadaddr); + if (sz <= 0) { + error_report("Could not load initrd '%s'", + machine->initrd_filename); + exit(1); + } + bi->initrd_start = loadaddr; + bi->initrd_end = loadaddr + sz; + } } static void amigaone_machine_init(MachineClass *mc) From e6521e41bada87e32ffe58f2b2e6cb69be799136 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 27 Feb 2025 17:39:19 +0100 Subject: [PATCH 2612/2892] ppc/amigaone: Add #defines for memory map constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Nicholas Piggin Message-ID: <3b8e54ad9220d57e7b0a33f3570e880f26677ce8.1740673173.git.balaton@eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 359f5fa125..483512125f 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -32,6 +32,14 @@ #define BUS_FREQ_HZ 100000000 #define INITRD_MIN_ADDR 0x600000 +#define INIT_RAM_ADDR 0x40000000 + +#define PCI_HIGH_ADDR 0x80000000 +#define PCI_HIGH_SIZE 0x7d000000 +#define PCI_LOW_ADDR 0xfd000000 +#define PCI_LOW_SIZE 0xe0000 + +#define ARTICIA_ADDR 0xfe000000 /* * Firmware binary available at @@ -287,7 +295,7 @@ static void amigaone_init(MachineState *machine) /* Firmware uses this area for startup */ mr = g_new(MemoryRegion, 1); memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal); - memory_region_add_subregion(get_system_memory(), 0x40000000, mr); + memory_region_add_subregion(get_system_memory(), INIT_RAM_ADDR, mr); } /* nvram */ @@ -322,7 +330,7 @@ static void amigaone_init(MachineState *machine) } /* Articia S */ - dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL); + dev = sysbus_create_simple(TYPE_ARTICIA, ARTICIA_ADDR, NULL); i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus")); if (machine->ram_size > 512 * MiB) { @@ -339,12 +347,12 @@ static void amigaone_init(MachineState *machine) pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); mr = g_new(MemoryRegion, 1); memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem, - 0, 0xe0000); - memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); + 0, PCI_LOW_SIZE); + memory_region_add_subregion(get_system_memory(), PCI_LOW_ADDR, mr); mr = g_new(MemoryRegion, 1); memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, - 0x80000000, 0x7d000000); - memory_region_add_subregion(get_system_memory(), 0x80000000, mr); + PCI_HIGH_ADDR, PCI_HIGH_SIZE); + memory_region_add_subregion(get_system_memory(), PCI_HIGH_ADDR, mr); pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); /* VIA VT82c686B South Bridge (multifunction PCI device) */ From 0f17ae24b53eaab4bbe9cfab267c536e2f7fdbd7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 4 Mar 2025 21:59:26 +0100 Subject: [PATCH 2613/2892] docs/system/ppc/amigang.rst: Update for NVRAM emulation Add NVRAM and hint on how to make it persistent. Also update Linux boot section which should now boot automatically with the new NVRAM defaults so manual settings in menu may not be needed normally. Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Message-ID: <20250304205926.87E364E6010@zero.eik.bme.hu> Signed-off-by: Nicholas Piggin --- docs/system/ppc/amigang.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/system/ppc/amigang.rst b/docs/system/ppc/amigang.rst index e2c9cb74b7..21bb14ed09 100644 --- a/docs/system/ppc/amigang.rst +++ b/docs/system/ppc/amigang.rst @@ -21,6 +21,7 @@ Emulated devices * VIA VT82C686B south bridge * PCI VGA compatible card (guests may need other card instead) * PS/2 keyboard and mouse + * 4 KiB NVRAM (use ``-drive if=mtd,format=raw,file=nvram.bin`` to keep contents persistent) Firmware -------- @@ -54,14 +55,14 @@ To boot the system run: -cdrom "A1 Linux Net Installer.iso" \ -device ati-vga,model=rv100,romfile=VGABIOS-lgpl-latest.bin -From the firmware menu that appears select ``Boot sequence`` → -``Amiga Multiboot Options`` and set ``Boot device 1`` to -``Onboard VIA IDE CDROM``. Then hit escape until the main screen appears again, -hit escape once more and from the exit menu that appears select either -``Save settings and exit`` or ``Use settings for this session only``. It may -take a long time loading the kernel into memory but eventually it boots and the -installer becomes visible. The ``ati-vga`` RV100 emulation is not -complete yet so only frame buffer works, DRM and 3D is not available. +If a firmware menu appears, select ``Boot sequence`` → ``Amiga Multiboot Options`` +and set ``Boot device 1`` to ``Onboard VIA IDE CDROM``. Then hit escape until +the main screen appears again, hit escape once more and from the exit menu that +appears select either ``Save settings and exit`` or ``Use settings for this +session only``. It may take a long time loading the kernel into memory but +eventually it boots and the installer becomes visible. The ``ati-vga`` RV100 +emulation is not complete yet so only frame buffer works, DRM and 3D is not +available. Genesi/bPlan Pegasos II (``pegasos2``) ====================================== From 000a41b69c3c34ae132ebc737dbe56f08fab50a9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 6 Feb 2025 17:53:31 +0100 Subject: [PATCH 2614/2892] block: Remove unused blk_op_is_blocked() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit fc4e394b28 removed the last caller of blk_op_is_blocked(). Remove the now unused function. Signed-off-by: Kevin Wolf Message-ID: <20250206165331.379033-1-kwolf@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/block-backend.c | 12 ------------ include/system/block-backend-global-state.h | 1 - 2 files changed, 13 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 9288f7e1c6..a402db13f2 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2357,18 +2357,6 @@ void *blk_blockalign(BlockBackend *blk, size_t size) return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); } -bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) -{ - BlockDriverState *bs = blk_bs(blk); - GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); - - if (!bs) { - return false; - } - - return bdrv_op_is_blocked(bs, op, errp); -} /** * Return BB's current AioContext. Note that this context may change diff --git a/include/system/block-backend-global-state.h b/include/system/block-backend-global-state.h index 9cc9b008ec..35b5e8380b 100644 --- a/include/system/block-backend-global-state.h +++ b/include/system/block-backend-global-state.h @@ -86,7 +86,6 @@ bool blk_supports_write_perm(BlockBackend *blk); bool blk_is_sg(BlockBackend *blk); void blk_set_enable_write_cache(BlockBackend *blk, bool wce); int blk_get_flags(BlockBackend *blk); -bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, Error **errp); void blk_add_aio_context_notifier(BlockBackend *blk, From b75c5f9879166b86ed7c48b772fdcd0693e8a9a3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 10 Mar 2025 11:48:58 +0100 Subject: [PATCH 2615/2892] block: Zero block driver state before reopening Block drivers assume in their .bdrv_open() implementation that their state in bs->opaque has been zeroed; it is initially allocated with g_malloc0() in bdrv_open_driver(). bdrv_snapshot_goto() needs to make sure that it is zeroed again before calling drv->bdrv_open() to avoid that block drivers use stale values. One symptom of this bug is VMDK running into a double free when the user tries to apply an internal snapshot like 'qemu-img snapshot -a test test.vmdk'. This should be a graceful error because VMDK doesn't support internal snapshots. ==25507== Invalid free() / delete / delete[] / realloc() ==25507== at 0x484B347: realloc (vg_replace_malloc.c:1801) ==25507== by 0x54B592A: g_realloc (gmem.c:171) ==25507== by 0x1B221D: vmdk_add_extent (../block/vmdk.c:570) ==25507== by 0x1B1084: vmdk_open_sparse (../block/vmdk.c:1059) ==25507== by 0x1AF3D8: vmdk_open (../block/vmdk.c:1371) ==25507== by 0x1A2AE0: bdrv_snapshot_goto (../block/snapshot.c:299) ==25507== by 0x205C77: img_snapshot (../qemu-img.c:3500) ==25507== by 0x58FA087: (below main) (libc_start_call_main.h:58) ==25507== Address 0x832f3e0 is 0 bytes inside a block of size 272 free'd ==25507== at 0x4846B83: free (vg_replace_malloc.c:989) ==25507== by 0x54AEAC4: g_free (gmem.c:208) ==25507== by 0x1AF629: vmdk_close (../block/vmdk.c:2889) ==25507== by 0x1A2A9C: bdrv_snapshot_goto (../block/snapshot.c:290) ==25507== by 0x205C77: img_snapshot (../qemu-img.c:3500) ==25507== by 0x58FA087: (below main) (libc_start_call_main.h:58) This error was discovered by fuzzing qemu-img. Cc: qemu-stable@nongnu.org Closes: https://gitlab.com/qemu-project/qemu/-/issues/2853 Closes: https://gitlab.com/qemu-project/qemu/-/issues/2851 Reported-by: Denis Rastyogin Signed-off-by: Kevin Wolf Message-ID: <20250310104858.28221-1-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- block/snapshot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/snapshot.c b/block/snapshot.c index 9c44780e96..22567f1fb9 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -296,6 +296,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, bdrv_graph_wrunlock(); ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); + memset(bs->opaque, 0, drv->instance_size); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); qobject_unref(options); if (open_ret < 0) { From 5aed8b0f0be25d2554f8bd76211e43b51e58f736 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:21 +0800 Subject: [PATCH 2616/2892] vfio/igd: Remove GTT write quirk in IO BAR 4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IO BAR4 of IGD devices contains a pair of 32-bit address/data registers, MMIO_Index (0x0) and MMIO_Data (0x4), which provide access to the MMIO BAR0 (GTTMMADR) from IO space. These registers are probably only used by the VBIOS, and are not documented by intel. The observed layout of MMIO_Index register is: 31 2 1 0 +-------------------------------------------------------------------+ | Offset | Rsvd | Sel | +-------------------------------------------------------------------+ - Offset: Byte offset in specified region, 4-byte aligned. - Sel: Region selector 0: MMIO register region (first half of MMIO BAR0) 1: GTT region (second half of MMIO BAR0). Pre Gen11 only. Currently, QEMU implements a quirk that adjusts the guest Data Stolen Memory (DSM) region address to be (addr - host BDSM + guest BDSM) when programming GTT entries via IO BAR4, assuming guest still programs GTT with host DSM address, which is not the case. Guest's BDSM register is emulated and initialized to 0 at startup by QEMU, then SeaBIOS programs its value[1]. As result, the address programmed to GTT entries by VBIOS running in guest are valid GPA, and this unnecessary adjustment brings inconsistency. [1] https://gitlab.com/qemu-project/seabios/-/blob/1.12-stable/src/fw/pciinit.c#L319-332 Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-2-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 191 +------------------------------------------------- 1 file changed, 1 insertion(+), 190 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index b1a237edd6..ca3a32f4f2 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -106,12 +106,6 @@ static int igd_gen(VFIOPCIDevice *vdev) return -1; } -typedef struct VFIOIGDQuirk { - struct VFIOPCIDevice *vdev; - uint32_t index; - uint64_t bdsm; -} VFIOIGDQuirk; - #define IGD_GMCH 0x50 /* Graphics Control Register */ #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ #define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ @@ -300,129 +294,6 @@ static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, return ret; } -/* - * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE - * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore - * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index - * for programming the GTT. - * - * See linux:include/drm/i915_drm.h for shift and mask values. - */ -static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) -{ - uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - int gen = igd_gen(vdev); - uint64_t ggms_size = igd_gtt_memory_size(gen, gmch); - - return (ggms_size / (4 * KiB)) * (gen < 8 ? 4 : 8); -} - -/* - * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes. - * Somehow the host stolen memory range is used for this, but how the ROM gets - * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it - * reprograms the GTT through the IOBAR where we can trap it and transpose the - * programming to the VM allocated buffer. That buffer gets reserved by the VM - * firmware via the fw_cfg entry added below. Here we're just monitoring the - * IOBAR address and data registers to detect a write sequence targeting the - * GTTADR. This code is developed by observed behavior and doesn't have a - * direct spec reference, unfortunately. - */ -static uint64_t vfio_igd_quirk_data_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - - igd->index = ~0; - - return vfio_region_read(&vdev->bars[4].region, addr + 4, size); -} - -static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - uint64_t val = data; - int gen = igd_gen(vdev); - - /* - * Programming the GGMS starts at index 0x1 and uses every 4th index (ie. - * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE - * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so - * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points - * to a 4k page, which we translate to a page from the VM allocated region, - * pointed to by the BDSM register. If this is not set, we fail. - * - * We trap writes to the full configured GTT size, but we typically only - * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often - * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous - * write of that last entry does work, but is hopefully unnecessary since - * we clear the previous GTT on initialization. - */ - if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) { - if (gen < 8 || (igd->index % 8 == 1)) { - uint64_t base; - - if (gen < 11) { - base = pci_get_long(vdev->pdev.config + IGD_BDSM); - } else { - base = pci_get_quad(vdev->pdev.config + IGD_BDSM_GEN11); - } - if (!base) { - hw_error("vfio-igd: Guest attempted to program IGD GTT before " - "BIOS reserved stolen memory. Unsupported BIOS?"); - } - - val = data - igd->bdsm + base; - } else { - val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */ - } - - trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name, - igd->index, data, val); - } - - vfio_region_write(&vdev->bars[4].region, addr + 4, val, size); - - igd->index = ~0; -} - -static const MemoryRegionOps vfio_igd_data_quirk = { - .read = vfio_igd_quirk_data_read, - .write = vfio_igd_quirk_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t vfio_igd_quirk_index_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - - igd->index = ~0; - - return vfio_region_read(&vdev->bars[4].region, addr, size); -} - -static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - - igd->index = data; - - vfio_region_write(&vdev->bars[4].region, addr, data, size); -} - -static const MemoryRegionOps vfio_igd_index_quirk = { - .read = vfio_igd_quirk_index_read, - .write = vfio_igd_quirk_index_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - #define IGD_GGC_MMIO_OFFSET 0x108040 #define IGD_BDSM_MMIO_OFFSET 0x1080C0 @@ -494,14 +365,11 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) g_autofree struct vfio_region_info *opregion = NULL; g_autofree struct vfio_region_info *host = NULL; g_autofree struct vfio_region_info *lpc = NULL; - VFIOQuirk *quirk; - VFIOIGDQuirk *igd; PCIDevice *lpc_bridge; - int i, ret, gen; + int ret, gen; uint64_t ggms_size, gms_size; uint64_t *bdsm_size; uint32_t gmch; - uint16_t cmd_orig, cmd; Error *err = NULL; /* @@ -634,32 +502,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ - quirk = vfio_quirk_alloc(2); - igd = quirk->data = g_malloc0(sizeof(*igd)); - igd->vdev = vdev; - igd->index = ~0; - if (gen < 11) { - igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); - } else { - igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM_GEN11, 4); - igd->bdsm |= - (uint64_t)vfio_pci_read_config(&vdev->pdev, IGD_BDSM_GEN11 + 4, 4) << 32; - } - igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ - - memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, - igd, "vfio-igd-index-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, - 0, &quirk->mem[0], 1); - - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk, - igd, "vfio-igd-data-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, - 4, &quirk->mem[1], 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - /* * Allow user to override dsm size using x-igd-gms option, in multiples of * 32MiB. This option should only be used when the desired size cannot be @@ -717,37 +559,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0); } - /* - * This IOBAR gives us access to GTTADR, which allows us to write to - * the GTT itself. So let's go ahead and write zero to all the GTT - * entries to avoid spurious DMA faults. Be sure I/O access is enabled - * before talking to the device. - */ - if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { - error_report("IGD device %s - failed to read PCI command register", - vdev->vbasedev.name); - } - - cmd = cmd_orig | PCI_COMMAND_IO; - - if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) { - error_report("IGD device %s - failed to write PCI command register", - vdev->vbasedev.name); - } - - for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) { - vfio_region_write(&vdev->bars[4].region, 0, i, 4); - vfio_region_write(&vdev->bars[4].region, 4, 0, 4); - } - - if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { - error_report("IGD device %s - failed to restore PCI command register", - vdev->vbasedev.name); - } - trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (ggms_size + gms_size) / MiB); } From 5faec6a5e2202d3460b6b0550ec4ad897be3c76f Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:22 +0800 Subject: [PATCH 2617/2892] vfio/igd: Do not include GTT stolen size in etc/igd-bdsm-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Though GTT Stolen Memory (GSM) is right below Data Stolen Memory (DSM) in host address space, direct access to GSM is prohibited, and it is not mapped to guest address space. Both host and guest accesses GSM indirectly through the second half of MMIO BAR0 (GTTMMADR). Guest firmware only need to reserve a memory region for DSM and program the BDSM register with the base address of that region, that's actually what both SeaBIOS[1] and IgdAssignmentDxe does now. [1] https://gitlab.com/qemu-project/seabios/-/blob/1.12-stable/src/fw/pciinit.c#L319-332 Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-3-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index ca3a32f4f2..dda4c7bb5d 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -112,28 +112,8 @@ static int igd_gen(VFIOPCIDevice *vdev) #define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */ #define IGD_GMCH_GEN6_GMS_MASK 0x1f -#define IGD_GMCH_GEN6_GGMS_SHIFT 8 -#define IGD_GMCH_GEN6_GGMS_MASK 0x3 #define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */ #define IGD_GMCH_GEN8_GMS_MASK 0xff -#define IGD_GMCH_GEN8_GGMS_SHIFT 6 -#define IGD_GMCH_GEN8_GGMS_MASK 0x3 - -static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch) -{ - uint64_t ggms; - - if (gen < 8) { - ggms = (gmch >> IGD_GMCH_GEN6_GGMS_SHIFT) & IGD_GMCH_GEN6_GGMS_MASK; - } else { - ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK; - if (ggms != 0) { - ggms = 1ULL << ggms; - } - } - - return ggms * MiB; -} static uint64_t igd_stolen_memory_size(int gen, uint32_t gmch) { @@ -367,7 +347,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) g_autofree struct vfio_region_info *lpc = NULL; PCIDevice *lpc_bridge; int ret, gen; - uint64_t ggms_size, gms_size; + uint64_t gms_size; uint64_t *bdsm_size; uint32_t gmch; Error *err = NULL; @@ -527,7 +507,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } } - ggms_size = igd_gtt_memory_size(gen, gmch); gms_size = igd_stolen_memory_size(gen, gmch); /* @@ -539,7 +518,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * config offset 0x5C. */ bdsm_size = g_malloc(sizeof(*bdsm_size)); - *bdsm_size = cpu_to_le64(ggms_size + gms_size); + *bdsm_size = cpu_to_le64(gms_size); fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", bdsm_size, sizeof(*bdsm_size)); @@ -559,6 +538,5 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0); } - trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, - (ggms_size + gms_size) / MiB); + trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB)); } From ae9d9ec4a643aae8704b4252b402488bde7b4be4 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:23 +0800 Subject: [PATCH 2618/2892] vfio/igd: Consolidate OpRegion initialization into a single function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both x-igd-opregion option and legacy mode require identical steps to set up OpRegion for IGD devices. Consolidate these steps into a single vfio_pci_igd_setup_opregion function. The function call in pci.c is wrapped with ifdef temporarily to prevent build error for non-x86 archs, it will be removed after we decouple it from legacy mode. Additionally, move vfio_pci_igd_opregion_init to igd.c to prevent it from being compiled in non-x86 builds. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-4-tomitamoeko@gmail.com [ clg: Fixed spelling in vfio_pci_igd_setup_opregion() ] Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 101 +++++++++++++++++++++++++++++++++++-------- hw/vfio/pci-quirks.c | 50 --------------------- hw/vfio/pci.c | 22 ++-------- hw/vfio/pci.h | 4 +- 4 files changed, 88 insertions(+), 89 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index dda4c7bb5d..113ad56ad4 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -106,6 +106,7 @@ static int igd_gen(VFIOPCIDevice *vdev) return -1; } +#define IGD_ASLS 0xfc /* ASL Storage Register */ #define IGD_GMCH 0x50 /* Graphics Control Register */ #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ #define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ @@ -138,6 +139,82 @@ static uint64_t igd_stolen_memory_size(int gen, uint32_t gmch) return 0; } +/* + * The OpRegion includes the Video BIOS Table, which seems important for + * telling the driver what sort of outputs it has. Without this, the device + * may work in the guest, but we may not get output. This also requires BIOS + * support to reserve and populate a section of guest memory sufficient for + * the table and to write the base address of that memory to the ASLS register + * of the IGD device. + */ +static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info, + Error **errp) +{ + int ret; + + vdev->igd_opregion = g_malloc0(info->size); + ret = pread(vdev->vbasedev.fd, vdev->igd_opregion, + info->size, info->offset); + if (ret != info->size) { + error_setg(errp, "failed to read IGD OpRegion"); + g_free(vdev->igd_opregion); + vdev->igd_opregion = NULL; + return false; + } + + /* + * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to + * allocate 32bit reserved memory for, copy these contents into, and write + * the reserved memory base address to the device ASLS register at 0xFC. + * Alignment of this reserved region seems flexible, but using a 4k page + * alignment seems to work well. This interface assumes a single IGD + * device, which may be at VM address 00:02.0 in legacy mode or another + * address in UPT mode. + * + * NB, there may be future use cases discovered where the VM should have + * direct interaction with the host OpRegion, in which case the write to + * the ASLS register would trigger MemoryRegion setup to enable that. + */ + fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion", + vdev->igd_opregion, info->size); + + trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name); + + pci_set_long(vdev->pdev.config + IGD_ASLS, 0); + pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); + pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); + + return true; +} + +bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp) +{ + g_autofree struct vfio_region_info *opregion = NULL; + int ret; + + /* Hotplugging is not supported for opregion access */ + if (vdev->pdev.qdev.hotplugged) { + error_setg(errp, "IGD OpRegion is not supported on hotplugged device"); + return false; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); + if (ret) { + error_setg_errno(errp, -ret, + "Device does not supports IGD OpRegion feature"); + return false; + } + + if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) { + return false; + } + + return true; +} + /* * The rather short list of registers that we copy from the host devices. * The LPC/ISA bridge values are definitely needed to support the vBIOS, the @@ -342,7 +419,6 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) { g_autofree struct vfio_region_info *rom = NULL; - g_autofree struct vfio_region_info *opregion = NULL; g_autofree struct vfio_region_info *host = NULL; g_autofree struct vfio_region_info *lpc = NULL; PCIDevice *lpc_bridge; @@ -418,15 +494,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * Check whether we have all the vfio device specific regions to * support legacy mode (added in Linux v4.6). If not, bail. */ - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); - if (ret) { - error_report("IGD device %s does not support OpRegion access," - "legacy mode disabled", vdev->vbasedev.name); - return; - } - ret = vfio_get_dev_region_info(&vdev->vbasedev, VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); @@ -459,6 +526,13 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } + /* Setup OpRegion access */ + if (!vfio_pci_igd_setup_opregion(vdev, &err)) { + error_append_hint(&err, "IGD legacy mode disabled\n"); + error_report_err(err); + return; + } + /* Create our LPC/ISA bridge */ ret = vfio_pci_igd_lpc_init(vdev, lpc); if (ret) { @@ -475,13 +549,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - /* Setup OpRegion access */ - if (!vfio_pci_igd_opregion_init(vdev, opregion, &err)) { - error_append_hint(&err, "IGD legacy mode disabled\n"); - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - return; - } - /* * Allow user to override dsm size using x-igd-gms option, in multiples of * 32MiB. This option should only be used when the desired size cannot be diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index c53591fe2b..37966e17f0 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1114,56 +1114,6 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name); } -#define IGD_ASLS 0xfc /* ASL Storage Register */ - -/* - * The OpRegion includes the Video BIOS Table, which seems important for - * telling the driver what sort of outputs it has. Without this, the device - * may work in the guest, but we may not get output. This also requires BIOS - * support to reserve and populate a section of guest memory sufficient for - * the table and to write the base address of that memory to the ASLS register - * of the IGD device. - */ -bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info, Error **errp) -{ - int ret; - - vdev->igd_opregion = g_malloc0(info->size); - ret = pread(vdev->vbasedev.fd, vdev->igd_opregion, - info->size, info->offset); - if (ret != info->size) { - error_setg(errp, "failed to read IGD OpRegion"); - g_free(vdev->igd_opregion); - vdev->igd_opregion = NULL; - return false; - } - - /* - * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to - * allocate 32bit reserved memory for, copy these contents into, and write - * the reserved memory base address to the device ASLS register at 0xFC. - * Alignment of this reserved region seems flexible, but using a 4k page - * alignment seems to work well. This interface assumes a single IGD - * device, which may be at VM address 00:02.0 in legacy mode or another - * address in UPT mode. - * - * NB, there may be future use cases discovered where the VM should have - * direct interaction with the host OpRegion, in which case the write to - * the ASLS register would trigger MemoryRegion setup to enable that. - */ - fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion", - vdev->igd_opregion, info->size); - - trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name); - - pci_set_long(vdev->pdev.config + IGD_ASLS, 0); - pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); - pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); - - return true; -} - /* * Common quirk probe entry points. */ diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index fdbc15885d..419dc2c4c8 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3136,30 +3136,14 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bar_quirk_setup(vdev, i); } +#ifdef CONFIG_VFIO_IGD if (!vdev->igd_opregion && vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) { - g_autofree struct vfio_region_info *opregion = NULL; - - if (vdev->pdev.qdev.hotplugged) { - error_setg(errp, - "cannot support IGD OpRegion feature on hotplugged " - "device"); - goto out_unset_idev; - } - - ret = vfio_get_dev_region_info(vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); - if (ret) { - error_setg_errno(errp, -ret, - "does not support requested IGD OpRegion feature"); - goto out_unset_idev; - } - - if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) { + if (!vfio_pci_igd_setup_opregion(vdev, errp)) { goto out_unset_idev; } } +#endif /* QEMU emulates all of MSI & MSIX */ if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index d638c781f6..f660b0d80f 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -227,9 +227,7 @@ int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); -bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info, - Error **errp); +bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp); void vfio_display_reset(VFIOPCIDevice *vdev); bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); From eabaa7b468eac63cb9acf47717f030c679532e6c Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:24 +0800 Subject: [PATCH 2619/2892] vfio/igd: Move LPC bridge initialization to a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new option will soon be introduced to decouple the LPC bridge/Host bridge ID quirk from legacy mode. To prepare for this, move the LPC bridge initialization into a separate function. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-5-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 122 +++++++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 113ad56ad4..7feed7dfa9 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -351,6 +351,72 @@ static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, return ret; } +static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) +{ + g_autofree struct vfio_region_info *host = NULL; + g_autofree struct vfio_region_info *lpc = NULL; + PCIDevice *lpc_bridge; + int ret; + + /* + * Copying IDs or creating new devices are not supported on hotplug + */ + if (vdev->pdev.qdev.hotplugged) { + error_setg(errp, "IGD LPC is not supported on hotplugged device"); + return false; + } + + /* + * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we + * can stuff host values into, so if there's already one there and it's not + * one we can hack on, this quirk is no-go. Sorry Q35. + */ + lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x1f, 0)); + if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), + "vfio-pci-igd-lpc-bridge")) { + error_setg(errp, + "Cannot create LPC bridge due to existing device at 1f.0"); + return false; + } + + /* + * Check whether we have all the vfio device specific regions to + * support LPC quirk (added in Linux v4.6). + */ + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); + if (ret) { + error_setg(errp, "IGD LPC bridge access is not supported by kernel"); + return false; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); + if (ret) { + error_setg(errp, "IGD host bridge access is not supported by kernel"); + return false; + } + + /* Create/modify LPC bridge */ + ret = vfio_pci_igd_lpc_init(vdev, lpc); + if (ret) { + error_setg(errp, "Failed to create/modify LPC bridge for IGD"); + return false; + } + + /* Stuff some host values into the VM PCI host bridge */ + ret = vfio_pci_igd_host_init(vdev, host); + if (ret) { + error_setg(errp, "Failed to modify host bridge for IGD"); + return false; + } + + return true; +} + #define IGD_GGC_MMIO_OFFSET 0x108040 #define IGD_BDSM_MMIO_OFFSET 0x1080C0 @@ -419,9 +485,6 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) { g_autofree struct vfio_region_info *rom = NULL; - g_autofree struct vfio_region_info *host = NULL; - g_autofree struct vfio_region_info *lpc = NULL; - PCIDevice *lpc_bridge; int ret, gen; uint64_t gms_size; uint64_t *bdsm_size; @@ -440,20 +503,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - /* - * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we - * can stuff host values into, so if there's already one there and it's not - * one we can hack on, legacy mode is no-go. Sorry Q35. - */ - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x1f, 0)); - if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), - "vfio-pci-igd-lpc-bridge")) { - error_report("IGD device %s cannot support legacy mode due to existing " - "devices at address 1f.0", vdev->vbasedev.name); - return; - } - /* * IGD is not a standard, they like to change their specs often. We * only attempt to support back to SandBridge and we hope that newer @@ -490,28 +539,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - /* - * Check whether we have all the vfio device specific regions to - * support legacy mode (added in Linux v4.6). If not, bail. - */ - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); - if (ret) { - error_report("IGD device %s does not support host bridge access," - "legacy mode disabled", vdev->vbasedev.name); - return; - } - - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); - if (ret) { - error_report("IGD device %s does not support LPC bridge access," - "legacy mode disabled", vdev->vbasedev.name); - return; - } - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); /* @@ -533,19 +560,10 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - /* Create our LPC/ISA bridge */ - ret = vfio_pci_igd_lpc_init(vdev, lpc); - if (ret) { - error_report("IGD device %s failed to create LPC bridge, " - "legacy mode disabled", vdev->vbasedev.name); - return; - } - - /* Stuff some host values into the VM PCI host bridge */ - ret = vfio_pci_igd_host_init(vdev, host); - if (ret) { - error_report("IGD device %s failed to modify host bridge, " - "legacy mode disabled", vdev->vbasedev.name); + /* Setup LPC bridge / Host bridge PCI IDs */ + if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { + error_append_hint(&err, "IGD legacy mode disabled\n"); + error_report_err(err); return; } From b22ab580d284558e298228e40f11be057cd3576c Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:25 +0800 Subject: [PATCH 2620/2892] vfio/pci: Add placeholder for device-specific config space quirks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IGD devices require device-specific quirk to be applied to their PCI config space. Currently, it is put in the BAR4 quirk that does nothing to BAR4 itself. Add a placeholder for PCI config space quirks to hold that quirk later. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-6-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 5 +++++ hw/vfio/pci.c | 4 ++++ hw/vfio/pci.h | 1 + 3 files changed, 10 insertions(+) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 37966e17f0..78aef7d60e 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1117,6 +1117,11 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) /* * Common quirk probe entry points. */ +bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp) +{ + return true; +} + void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) { vfio_vga_probe_ati_3c3_quirk(vdev); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 419dc2c4c8..ff1e720dba 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3128,6 +3128,10 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto out_unset_idev; } + if (!vfio_config_quirk_setup(vdev, errp)) { + goto out_unset_idev; + } + if (vdev->vga) { vfio_vga_quirk_setup(vdev); } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index f660b0d80f..d125e73865 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -204,6 +204,7 @@ uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); +bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp); void vfio_vga_quirk_setup(VFIOPCIDevice *vdev); void vfio_vga_quirk_exit(VFIOPCIDevice *vdev); void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev); From 9267f96ad6f26100e17a34f169a79d1ad9c1c7aa Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:26 +0800 Subject: [PATCH 2621/2892] vfio/igd: Refactor vfio_probe_igd_bar4_quirk into pci config quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The actual IO BAR4 write quirk in vfio_probe_igd_bar4_quirk was removed in previous change, leaving the function not matching its name, so move it into the newly introduced vfio_config_quirk_setup. There is no functional change in this commit. For now, to align with current legacy mode behavior, it returns and proceeds on error. Later it will fail on error after decoupling the quirks from legacy mode. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-7-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 21 ++++++++++++--------- hw/vfio/pci-quirks.c | 6 +++++- hw/vfio/pci.h | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 7feed7dfa9..65a3dbb5af 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -482,7 +482,8 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); } -void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) +bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, + Error **errp G_GNUC_UNUSED) { g_autofree struct vfio_region_info *rom = NULL; int ret, gen; @@ -497,10 +498,10 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * PCI bus address. */ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 4 || + !vfio_is_vga(vdev) || &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), 0, PCI_DEVFN(0x2, 0))) { - return; + return true; } /* @@ -512,7 +513,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (gen == -1) { error_report("IGD device %s is unsupported in legacy mode, " "try SandyBridge or newer", vdev->vbasedev.name); - return; + return true; } /* @@ -525,7 +526,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if ((ret || !rom->size) && !vdev->pdev.romfile) { error_report("IGD device %s has no ROM, legacy mode disabled", vdev->vbasedev.name); - return; + return true; } /* @@ -536,7 +537,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) error_report("IGD device %s hotplugged, ROM disabled, " "legacy mode disabled", vdev->vbasedev.name); vdev->rom_read_failed = true; - return; + return true; } gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); @@ -550,21 +551,21 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); error_report("IGD device %s failed to enable VGA access, " "legacy mode disabled", vdev->vbasedev.name); - return; + return true; } /* Setup OpRegion access */ if (!vfio_pci_igd_setup_opregion(vdev, &err)) { error_append_hint(&err, "IGD legacy mode disabled\n"); error_report_err(err); - return; + return true; } /* Setup LPC bridge / Host bridge PCI IDs */ if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { error_append_hint(&err, "IGD legacy mode disabled\n"); error_report_err(err); - return; + return true; } /* @@ -624,4 +625,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB)); + + return true; } diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 78aef7d60e..f998761abc 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1119,6 +1119,11 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) */ bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp) { +#ifdef CONFIG_VFIO_IGD + if (!vfio_probe_igd_config_quirk(vdev, errp)) { + return false; + } +#endif return true; } @@ -1170,7 +1175,6 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) vfio_probe_rtl8168_bar2_quirk(vdev, nr); #ifdef CONFIG_VFIO_IGD vfio_probe_igd_bar0_quirk(vdev, nr); - vfio_probe_igd_bar4_quirk(vdev, nr); #endif } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index d125e73865..fc7ead7727 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -216,7 +216,7 @@ bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); void vfio_quirk_reset(VFIOPCIDevice *vdev); VFIOQuirk *vfio_quirk_alloc(int nr_mem); void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr); -void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr); +bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; From 2fedccf03c889562855bfbe0628ac577938c50a2 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:27 +0800 Subject: [PATCH 2622/2892] vfio/igd: Decouple common quirks from legacy mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far, IGD-specific quirks all require enabling legacy mode, which is toggled by assigning IGD to 00:02.0. However, some quirks, like the BDSM and GGC register quirks, should be applied to all supported IGD devices. A new config option, x-igd-legacy-mode=[on|off|auto], is introduced to control the legacy mode only quirks. The default value is "auto", which keeps current behavior that enables legacy mode implicitly and continues on error when all following conditions are met. * Machine type is i440fx * IGD device is at guest BDF 00:02.0 If any one of the conditions above is not met, the default behavior is equivalent to "off", QEMU will fail immediately if any error occurs. Users can also use "on" to force enabling legacy mode. It checks if all the conditions above are met and set up legacy mode. QEMU will also fail immediately on error in this case. Additionally, the hotplug check in legacy mode is removed as hotplugging IGD device is never supported, and it will be checked when enabling the OpRegion quirk. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-8-tomitamoeko@gmail.com [ clg: - Changed warn_report() by info_report() in vfio_probe_igd_config_quirk() as suggested by Alex W. - Fixed spelling in vfio_probe_igd_config_quirk () ] Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 127 +++++++++++++++++++++++++++++--------------------- hw/vfio/pci.c | 2 + hw/vfio/pci.h | 1 + 3 files changed, 77 insertions(+), 53 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 65a3dbb5af..ee36875310 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -15,6 +15,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" +#include "hw/boards.h" #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" #include "pci.h" @@ -432,9 +433,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) * bus address. */ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 0 || - &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x2, 0))) { + !vfio_is_vga(vdev) || nr != 0) { return; } @@ -482,14 +481,13 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); } -bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, - Error **errp G_GNUC_UNUSED) +bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { - g_autofree struct vfio_region_info *rom = NULL; int ret, gen; uint64_t gms_size; uint64_t *bdsm_size; uint32_t gmch; + bool legacy_mode_enabled = false; Error *err = NULL; /* @@ -498,9 +496,7 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, * PCI bus address. */ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || - &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x2, 0))) { + !vfio_is_vga(vdev)) { return true; } @@ -516,56 +512,67 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, return true; } - /* - * Most of what we're doing here is to enable the ROM to run, so if - * there's no ROM, there's no point in setting up this quirk. - * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support. - */ - ret = vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, &rom); - if ((ret || !rom->size) && !vdev->pdev.romfile) { - error_report("IGD device %s has no ROM, legacy mode disabled", - vdev->vbasedev.name); - return true; - } - - /* - * Ignore the hotplug corner case, mark the ROM failed, we can't - * create the devices we need for legacy mode in the hotplug scenario. - */ - if (vdev->pdev.qdev.hotplugged) { - error_report("IGD device %s hotplugged, ROM disabled, " - "legacy mode disabled", vdev->vbasedev.name); - vdev->rom_read_failed = true; - return true; - } - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); /* - * If IGD VGA Disable is clear (expected) and VGA is not already enabled, - * try to enable it. Probably shouldn't be using legacy mode without VGA, - * but also no point in us enabling VGA if disabled in hardware. + * For backward compatibility, enable legacy mode when + * - Machine type is i440fx (pc_piix) + * - IGD device is at guest BDF 00:02.0 + * - Not manually disabled by x-igd-legacy-mode=off */ - if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - error_report("IGD device %s failed to enable VGA access, " - "legacy mode disabled", vdev->vbasedev.name); - return true; - } + if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && + !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && + (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x2, 0)))) { + /* + * IGD legacy mode requires: + * - VBIOS in ROM BAR or file + * - VGA IO/MMIO ranges are claimed by IGD + * - OpRegion + * - Same LPC bridge and Host bridge VID/DID/SVID/SSID as host + */ + g_autofree struct vfio_region_info *rom = NULL; - /* Setup OpRegion access */ - if (!vfio_pci_igd_setup_opregion(vdev, &err)) { - error_append_hint(&err, "IGD legacy mode disabled\n"); - error_report_err(err); - return true; - } + legacy_mode_enabled = true; + info_report("IGD legacy mode enabled, " + "use x-igd-legacy-mode=off to disable it if unwanted."); - /* Setup LPC bridge / Host bridge PCI IDs */ - if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { - error_append_hint(&err, "IGD legacy mode disabled\n"); - error_report_err(err); - return true; + /* + * Most of what we're doing here is to enable the ROM to run, so if + * there's no ROM, there's no point in setting up this quirk. + * NB. We only seem to get BIOS ROMs, so UEFI VM would need CSM support. + */ + ret = vfio_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, &rom); + if ((ret || !rom->size) && !vdev->pdev.romfile) { + error_setg(&err, "Device has no ROM"); + goto error; + } + + /* + * If IGD VGA Disable is clear (expected) and VGA is not already + * enabled, try to enable it. Probably shouldn't be using legacy mode + * without VGA, but also no point in us enabling VGA if disabled in + * hardware. + */ + if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { + error_setg(&err, "Unable to enable VGA access"); + goto error; + } + + /* Setup OpRegion access */ + if (!vfio_pci_igd_setup_opregion(vdev, &err)) { + goto error; + } + + /* Setup LPC bridge / Host bridge PCI IDs */ + if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { + goto error; + } + } else if (vdev->igd_legacy_mode == ON_OFF_AUTO_ON) { + error_setg(&err, + "Machine is not i440fx or assigned BDF is not 00:02.0"); + goto error; } /* @@ -627,4 +634,18 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB)); return true; + +error: + /* + * When legacy mode is implicity enabled, continue on error, + * to keep compatibility + */ + if (legacy_mode_enabled && (vdev->igd_legacy_mode == ON_OFF_AUTO_AUTO)) { + error_report_err(err); + error_report("IGD legacy mode disabled"); + return true; + } + + error_propagate(errp, err); + return false; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index ff1e720dba..444a33d94b 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3369,6 +3369,8 @@ static const Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_REQ_BIT, true), DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), + DEFINE_PROP_ON_OFF_AUTO("x-igd-legacy-mode", VFIOPCIDevice, + igd_legacy_mode, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, vbasedev.enable_migration, ON_OFF_AUTO_AUTO), DEFINE_PROP("x-migration-multifd-transfer", VFIOPCIDevice, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index fc7ead7727..3e66b19d8f 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -158,6 +158,7 @@ struct VFIOPCIDevice { uint32_t display_xres; uint32_t display_yres; int32_t bootindex; + OnOffAuto igd_legacy_mode; uint32_t igd_gms; OffAutoPCIBAR msix_relo; uint8_t nv_gpudirect_clique; From 434ac62ef2443f25956dafcfcbf29663fd0cd3e1 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:28 +0800 Subject: [PATCH 2623/2892] vfio/igd: Handle x-igd-opregion option in config quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both enable OpRegion option (x-igd-opregion) and legacy mode require setting up OpRegion copy for IGD devices. As the config quirk no longer depends on legacy mode, we can now handle x-igd-opregion option there instead of in vfio_realize. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-9-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 14 +++++++++----- hw/vfio/pci.c | 9 --------- hw/vfio/pci.h | 2 -- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index ee36875310..12e07517b4 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -189,7 +189,7 @@ static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, return true; } -bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp) { g_autofree struct vfio_region_info *opregion = NULL; int ret; @@ -560,10 +560,8 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) goto error; } - /* Setup OpRegion access */ - if (!vfio_pci_igd_setup_opregion(vdev, &err)) { - goto error; - } + /* Enable OpRegion quirk */ + vdev->features |= VFIO_FEATURE_ENABLE_IGD_OPREGION; /* Setup LPC bridge / Host bridge PCI IDs */ if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { @@ -575,6 +573,12 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) goto error; } + /* Setup OpRegion access */ + if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) && + !vfio_pci_igd_setup_opregion(vdev, errp)) { + goto error; + } + /* * Allow user to override dsm size using x-igd-gms option, in multiples of * 32MiB. This option should only be used when the desired size cannot be diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 444a33d94b..e2897bdcd5 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3140,15 +3140,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bar_quirk_setup(vdev, i); } -#ifdef CONFIG_VFIO_IGD - if (!vdev->igd_opregion && - vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) { - if (!vfio_pci_igd_setup_opregion(vdev, errp)) { - goto out_unset_idev; - } - } -#endif - /* QEMU emulates all of MSI & MSIX */ if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 3e66b19d8f..816bdbf844 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -229,8 +229,6 @@ int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); -bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp); - void vfio_display_reset(VFIOPCIDevice *vdev); bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); void vfio_display_finalize(VFIOPCIDevice *vdev); From 674f61117d3652c969bd9d04201615bb69614fa2 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:29 +0800 Subject: [PATCH 2624/2892] vfio/igd: Introduce x-igd-lpc option for LPC bridge ID quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LPC bridge/Host bridge IDs quirk is also not dependent on legacy mode. Recent Windows driver no longer depends on these IDs, as well as Linux i915 driver, while UEFI GOP seems still needs them. Make it an option to allow users enabling and disabling it as needed. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-10-tomitamoeko@gmail.com [ clg: - Fixed spelling in vfio_probe_igd_config_quirk() ] Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 14 ++++++++------ hw/vfio/pci.c | 2 ++ hw/vfio/pci.h | 3 +++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 12e07517b4..8a88dbab13 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -560,13 +560,9 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) goto error; } - /* Enable OpRegion quirk */ + /* Enable OpRegion and LPC bridge quirk */ vdev->features |= VFIO_FEATURE_ENABLE_IGD_OPREGION; - - /* Setup LPC bridge / Host bridge PCI IDs */ - if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { - goto error; - } + vdev->features |= VFIO_FEATURE_ENABLE_IGD_LPC; } else if (vdev->igd_legacy_mode == ON_OFF_AUTO_ON) { error_setg(&err, "Machine is not i440fx or assigned BDF is not 00:02.0"); @@ -579,6 +575,12 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) goto error; } + /* Setup LPC bridge / Host bridge PCI IDs */ + if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_LPC) && + !vfio_pci_igd_setup_lpc_bridge(vdev, errp)) { + goto error; + } + /* * Allow user to override dsm size using x-igd-gms option, in multiples of * 32MiB. This option should only be used when the desired size cannot be diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index e2897bdcd5..3cb7806f2f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3360,6 +3360,8 @@ static const Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_REQ_BIT, true), DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), + DEFINE_PROP_BIT("x-igd-lpc", VFIOPCIDevice, features, + VFIO_FEATURE_ENABLE_IGD_LPC_BIT, false), DEFINE_PROP_ON_OFF_AUTO("x-igd-legacy-mode", VFIOPCIDevice, igd_legacy_mode, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 816bdbf844..d94ecaba68 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -154,6 +154,9 @@ struct VFIOPCIDevice { #define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2 #define VFIO_FEATURE_ENABLE_IGD_OPREGION \ (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT) +#define VFIO_FEATURE_ENABLE_IGD_LPC_BIT 3 +#define VFIO_FEATURE_ENABLE_IGD_LPC \ + (1 << VFIO_FEATURE_ENABLE_IGD_LPC_BIT) OnOffAuto display; uint32_t display_xres; uint32_t display_yres; From e46b7af508c27a4e830818ac05fa3e2e4c33b416 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Fri, 7 Mar 2025 02:01:30 +0800 Subject: [PATCH 2625/2892] vfio/igd: Fix broken KVMGT OpRegion support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The KVMGT/GVT-g vGPU also exposes OpRegion. But unlike IGD passthrough, it only needs the OpRegion quirk. A previous change moved x-igd-opregion handling to config quirk breaks KVMGT functionality as it brings extra checks and applied other quirks. Here we check if the device is mdev (KVMGT) or not (passthrough), and then applies corresponding quirks. As before, users must manually specify x-igd-opregion=on to enable it on KVMGT devices. In the future, we may check the VID/DID and enable OpRegion automatically. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Tested-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-11-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 8a88dbab13..265fffc2aa 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -481,7 +481,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); } -bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { int ret, gen; uint64_t gms_size; @@ -655,3 +655,28 @@ error: error_propagate(errp, err); return false; } + +/* + * KVMGT/GVT-g vGPU exposes an emulated OpRegion. So far, users have to specify + * x-igd-opregion=on to enable the access. + * TODO: Check VID/DID and enable opregion access automatically + */ +static bool vfio_pci_kvmgt_config_quirk(VFIOPCIDevice *vdev, Error **errp) +{ + if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) && + !vfio_pci_igd_setup_opregion(vdev, errp)) { + return false; + } + + return true; +} + +bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) +{ + /* KVMGT/GVT-g vGPU is exposed as mdev */ + if (vdev->vbasedev.mdev) { + return vfio_pci_kvmgt_config_quirk(vdev, errp); + } + + return vfio_pci_igd_config_quirk(vdev, errp); +} From cbb2e105268d0c5b22ba6fc936af1ae6bae3f959 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 10 Mar 2025 13:53:10 +0100 Subject: [PATCH 2626/2892] vfio/migration: Use BE byte order for device state wire packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire data commonly use BE byte order (including in the existing migration protocol), use it also for for VFIO device state packets. This will allow VFIO multifd device state transfer between hosts with different endianness. Although currently there is no such use case, it's good to have it now for completeness. Reviewed-by: Avihai Horon Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/dcfc04cc1a50655650dbac8398e2742ada84ee39.1741611079.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration-multifd.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 233724710b..378f6f3bf0 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -13,6 +13,7 @@ #include "hw/vfio/vfio-common.h" #include "migration/misc.h" #include "qapi/error.h" +#include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" @@ -155,12 +156,16 @@ bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, return false; } + packet->version = be32_to_cpu(packet->version); if (packet->version != VFIO_DEVICE_STATE_PACKET_VER_CURRENT) { error_setg(errp, "%s: packet has unknown version %" PRIu32, vbasedev->name, packet->version); return false; } + packet->idx = be32_to_cpu(packet->idx); + packet->flags = be32_to_cpu(packet->flags); + if (packet->idx == UINT32_MAX) { error_setg(errp, "%s: packet index is invalid", vbasedev->name); return false; @@ -558,9 +563,9 @@ vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev, packet_len = sizeof(*packet) + bioc->usage; packet = g_malloc0(packet_len); - packet->version = VFIO_DEVICE_STATE_PACKET_VER_CURRENT; - packet->idx = idx; - packet->flags = VFIO_DEVICE_STATE_CONFIG_STATE; + packet->version = cpu_to_be32(VFIO_DEVICE_STATE_PACKET_VER_CURRENT); + packet->idx = cpu_to_be32(idx); + packet->flags = cpu_to_be32(VFIO_DEVICE_STATE_CONFIG_STATE); memcpy(&packet->data, bioc->data, bioc->usage); if (!multifd_queue_device_state(idstr, instance_id, @@ -610,7 +615,7 @@ vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, } packet = g_malloc0(sizeof(*packet) + migration->data_buffer_size); - packet->version = VFIO_DEVICE_STATE_PACKET_VER_CURRENT; + packet->version = cpu_to_be32(VFIO_DEVICE_STATE_PACKET_VER_CURRENT); for (idx = 0; ; idx++) { ssize_t data_size; @@ -631,7 +636,7 @@ vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, break; } - packet->idx = idx; + packet->idx = cpu_to_be32(idx); packet_size = sizeof(*packet) + data_size; if (!multifd_queue_device_state(d->idstr, d->instance_id, From c6cd30feadb6907cf69d35babec74274e690164f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:36 +0100 Subject: [PATCH 2627/2892] system: Declare qemu_[min/max]rampagesize() in 'system/hostmem.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both qemu_minrampagesize() and qemu_maxrampagesize() are related to host memory backends, having the following call stack: qemu_minrampagesize() -> find_min_backend_pagesize() -> object_dynamic_cast(obj, TYPE_MEMORY_BACKEND) qemu_maxrampagesize() -> find_max_backend_pagesize() -> object_dynamic_cast(obj, TYPE_MEMORY_BACKEND) Having TYPE_MEMORY_BACKEND defined in "system/hostmem.h": include/system/hostmem.h:23:#define TYPE_MEMORY_BACKEND "memory-backend" Move their prototype declaration to "system/hostmem.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Eric Auger Message-Id: <20250308230917.18907-7-philmd@linaro.org> Acked-by: David Hildenbrand Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-2-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_caps.c | 1 + hw/s390x/s390-virtio-ccw.c | 1 + hw/vfio/spapr.c | 1 + include/exec/ram_addr.h | 3 --- include/system/hostmem.h | 3 +++ 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 904bff87ce..9e53d0c1fd 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -34,6 +34,7 @@ #include "kvm_ppc.h" #include "migration/vmstate.h" #include "system/tcg.h" +#include "system/hostmem.h" #include "hw/ppc/spapr.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a9b3db19f6..75b32182eb 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -41,6 +41,7 @@ #include "hw/s390x/tod.h" #include "system/system.h" #include "system/cpus.h" +#include "system/hostmem.h" #include "target/s390x/kvm/pv.h" #include "migration/blocker.h" #include "qapi/visitor.h" diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index ad4c499eaf..237f96dd3f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -15,6 +15,7 @@ #include #endif #include "system/kvm.h" +#include "system/hostmem.h" #include "exec/address-spaces.h" #include "hw/vfio/vfio-common.h" diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 3d8df4edf1..e4c28fbec9 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -102,9 +102,6 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, bool ramblock_is_pmem(RAMBlock *rb); -long qemu_minrampagesize(void); -long qemu_maxrampagesize(void); - /** * qemu_ram_alloc_from_file, * qemu_ram_alloc_from_fd: Allocate a ram block from the specified backing diff --git a/include/system/hostmem.h b/include/system/hostmem.h index 5c21ca55c0..62642e602c 100644 --- a/include/system/hostmem.h +++ b/include/system/hostmem.h @@ -93,4 +93,7 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend); size_t host_memory_backend_pagesize(HostMemoryBackend *memdev); char *host_memory_backend_get_name(HostMemoryBackend *backend); +long qemu_minrampagesize(void); +long qemu_maxrampagesize(void); + #endif From a19ce97ed16c2892c6340fb46e693aa934ab7ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:37 +0100 Subject: [PATCH 2628/2892] hw/vfio/spapr: Do not include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit is already included by "system/kvm.h" in the next line. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Message-Id: <20250307180337.14811-3-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-3-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 237f96dd3f..1a5d1611f2 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -11,9 +11,6 @@ #include "qemu/osdep.h" #include #include -#ifdef CONFIG_KVM -#include -#endif #include "system/kvm.h" #include "system/hostmem.h" #include "exec/address-spaces.h" From 514c29678715c6b27e09fef8e3594dc1dba6820b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:38 +0100 Subject: [PATCH 2629/2892] hw/vfio/common: Include missing 'system/tcg.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always include necessary headers explicitly, to avoid when refactoring unrelated ones: hw/vfio/common.c:1176:45: error: implicit declaration of function ‘tcg_enabled’; 1176 | tcg_enabled() ? DIRTY_CLIENTS_ALL : | ^~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Message-Id: <20250307180337.14811-2-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-4-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 7a4010ef4e..b1596b6bf6 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -42,6 +42,7 @@ #include "migration/misc.h" #include "migration/blocker.h" #include "migration/qemu-file.h" +#include "system/tcg.h" #include "system/tpm.h" VFIODeviceList vfio_device_list = From 80ce7bb5cf0157fa835ecaabeaed90fc282830c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:39 +0100 Subject: [PATCH 2630/2892] hw/vfio/common: Get target page size using runtime helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer runtime helpers to get target page size. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250305153929.43687-3-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-5-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index b1596b6bf6..1a0d9290f8 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -30,6 +30,7 @@ #include "exec/address-spaces.h" #include "exec/memory.h" #include "exec/ram_addr.h" +#include "exec/target_page.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -393,13 +394,14 @@ static void vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + int target_page_size = qemu_target_page_size(); VFIORamDiscardListener *vrdl; /* Ignore some corner cases not relevant in practice. */ - g_assert(QEMU_IS_ALIGNED(section->offset_within_region, TARGET_PAGE_SIZE)); + g_assert(QEMU_IS_ALIGNED(section->offset_within_region, target_page_size)); g_assert(QEMU_IS_ALIGNED(section->offset_within_address_space, - TARGET_PAGE_SIZE)); - g_assert(QEMU_IS_ALIGNED(int128_get64(section->size), TARGET_PAGE_SIZE)); + target_page_size)); + g_assert(QEMU_IS_ALIGNED(int128_get64(section->size), target_page_size)); vrdl = g_new0(VFIORamDiscardListener, 1); vrdl->bcontainer = bcontainer; From 5731baee6c3ca1fbd23ce9dfe1a825aafd473e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:40 +0100 Subject: [PATCH 2631/2892] hw/vfio: Compile some common objects once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some files don't rely on any target-specific knowledge and can be compiled once: - helpers.c - container-base.c - migration.c (removing unnecessary "exec/ram_addr.h") - migration-multifd.c - cpr.c Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Message-Id: <20250308230917.18907-4-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-6-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/meson.build | 13 ++++++++----- hw/vfio/migration.c | 1 - 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 260d65febd..8e376cfcbf 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -1,12 +1,7 @@ vfio_ss = ss.source_set() vfio_ss.add(files( - 'helpers.c', 'common.c', - 'container-base.c', 'container.c', - 'migration.c', - 'migration-multifd.c', - 'cpr.c', )) vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) vfio_ss.add(when: 'CONFIG_IOMMUFD', if_true: files( @@ -25,3 +20,11 @@ vfio_ss.add(when: 'CONFIG_VFIO_AP', if_true: files('ap.c')) vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) + +system_ss.add(when: 'CONFIG_VFIO', if_true: files( + 'helpers.c', + 'container-base.c', + 'migration.c', + 'migration-multifd.c', + 'cpr.c', +)) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 416643ddd6..fbff46cfc3 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -27,7 +27,6 @@ #include "qapi/error.h" #include "qapi/qapi-events-vfio.h" #include "exec/ramlist.h" -#include "exec/ram_addr.h" #include "pci.h" #include "trace.h" #include "hw/hw.h" From 761d63ccaf6768dc528f2e0e1a422caa12fbe216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:41 +0100 Subject: [PATCH 2632/2892] hw/vfio: Compile more objects once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files depend on the VFIO symbol in their Kconfig definition. They don't rely on target specific definitions, move them to system_ss[] to build them once. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Message-Id: <20250308230917.18907-5-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-7-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 8e376cfcbf..784eae4b55 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -14,13 +14,13 @@ vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( )) vfio_ss.add(when: 'CONFIG_VFIO_CCW', if_true: files('ccw.c')) vfio_ss.add(when: 'CONFIG_VFIO_PLATFORM', if_true: files('platform.c')) -vfio_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) -vfio_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) vfio_ss.add(when: 'CONFIG_VFIO_AP', if_true: files('ap.c')) vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) +system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) +system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'helpers.c', 'container-base.c', From d5c0be1a94fb3733ee04b8d5cde6641ad54ee2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:42 +0100 Subject: [PATCH 2633/2892] hw/vfio: Compile iommufd.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing unused "exec/ram_addr.h" header allow to compile iommufd.c once for all targets. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Message-Id: <20250308230917.18907-6-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-8-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 1 - hw/vfio/meson.build | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index df61edffc0..42c8412bbf 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -25,7 +25,6 @@ #include "qemu/cutils.h" #include "qemu/chardev_open.h" #include "pci.h" -#include "exec/ram_addr.h" static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 784eae4b55..5c9ec7e897 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -4,9 +4,6 @@ vfio_ss.add(files( 'container.c', )) vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) -vfio_ss.add(when: 'CONFIG_IOMMUFD', if_true: files( - 'iommufd.c', -)) vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'display.c', 'pci-quirks.c', @@ -28,3 +25,6 @@ system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'migration-multifd.c', 'cpr.c', )) +system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( + 'iommufd.c', +)) From 28ea52dd639b75e212ad32cc704a1dd212286914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Mar 2025 09:57:43 +0100 Subject: [PATCH 2634/2892] hw/vfio: Compile display.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit display.c doesn't rely on target specific definitions, move it to system_ss[] to build it once. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Message-Id: <20250308230917.18907-8-philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20250311085743.21724-9-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/vfio/meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 5c9ec7e897..a8939c8386 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -5,7 +5,6 @@ vfio_ss.add(files( )) vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( - 'display.c', 'pci-quirks.c', 'pci.c', )) @@ -28,3 +27,6 @@ system_ss.add(when: 'CONFIG_VFIO', if_true: files( system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( 'iommufd.c', )) +system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( + 'display.c', +)) From 63316f973ad9f1465b1cb820c5be954260663c8a Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Tue, 11 Mar 2025 00:58:33 +0100 Subject: [PATCH 2635/2892] vfio/pci-quirks: Exclude non-ioport BAR from ATI quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ATI BAR4 quirk is targeting an ioport BAR. Older devices may have a BAR4 which is not an ioport, causing a segfault here. Test the BAR type to skip these devices. Similar to "8f419c5b: vfio/pci-quirks: Exclude non-ioport BAR from NVIDIA quirk" Untested, as I don't have the card to test. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2856 Signed-off-by: Vasilis Liaskovitis Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250310235833.41026-1-vliaskovitis@suse.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index f998761abc..3f002252ac 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -403,7 +403,7 @@ static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr) /* This windows doesn't seem to be used except by legacy VGA code */ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) || - !vdev->vga || nr != 4) { + !vdev->vga || nr != 4 || !vdev->bars[4].ioport) { return; } From 4d9607481560e6c8e1508a0aafe94f86a0503c8c Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 11 Mar 2025 17:48:07 +0000 Subject: [PATCH 2636/2892] vfio/pci: Drop debug commentary from x-device-dirty-page-tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intent behind the x-device-dirty-page-tracking option is twofold: 1) development/testing in the presence of VFs with VF dirty page tracking 2) deliberately choosing platform dirty tracker over the VF one. Item 2) scenario is useful when VF dirty tracker is not as fast as IOMMU, or there's some limitations around it (e.g. number of them is limited; aggregated address space under tracking is limited), efficiency/scalability (e.g. 1 pagetable in IOMMU dirty tracker to scan vs N VFs) or just troubleshooting. Given item 2 it is not restricted to debugging, hence drop the debug parenthesis from the option description. Signed-off-by: Joao Martins Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250311174807.79825-1-joao.m.martins@oracle.com [ clg: Fixed subject spelling ] Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 3cb7806f2f..7f1532fbed 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3532,7 +3532,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) object_class_property_set_description(klass, /* 9.1 */ "x-device-dirty-page-tracking", "Disable device dirty page tracking and use " - "container-based dirty page tracking (DEBUG)"); + "container-based dirty page tracking"); object_class_property_set_description(klass, /* 9.1 */ "migration-events", "Emit VFIO migration QAPI event when a VFIO device " From d060b2789f71e8cd1d07c4374e0c96c299423952 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 10 Feb 2025 17:03:29 +0100 Subject: [PATCH 2637/2892] hw/sd/sdhci: Set reset value of interrupt registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interrupt enable registers are not reset to 0 on Freescale eSDHC but some bits are enabled on reset. At least some U-Boot versions seem to expect this and not initialise these registers before expecting interrupts. Use existing vendor property for Freescale eSDHC and set the reset value of the interrupt registers to match Freescale documentation. Signed-off-by: BALATON Zoltan Message-ID: <20250210160329.DDA7F4E600E@zero.eik.bme.hu> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/e500.c | 1 + hw/sd/sdhci.c | 4 ++++ include/hw/sd/sdhci.h | 1 + 3 files changed, 6 insertions(+) diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index fe8b9f7962..69269aa24c 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1043,6 +1043,7 @@ void ppce500_init(MachineState *machine) dev = qdev_new(TYPE_SYSBUS_SDHCI); qdev_prop_set_uint8(dev, "sd-spec-version", 2); qdev_prop_set_uint8(dev, "endianness", DEVICE_BIG_ENDIAN); + qdev_prop_set_uint8(dev, "vendor", SDHCI_VENDOR_FSL); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(mpicdev, MPC85XX_ESDHC_IRQ)); diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 1f45a77566..fe87e18d5d 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -307,6 +307,10 @@ static void sdhci_reset(SDHCIState *s) s->data_count = 0; s->stopped_state = sdhc_not_stopped; s->pending_insert_state = false; + if (s->vendor == SDHCI_VENDOR_FSL) { + s->norintstsen = 0x013f; + s->errintstsen = 0x117f; + } } static void sdhci_poweron_reset(DeviceState *dev) diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index 38c08e2859..f722d8eb1c 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -110,6 +110,7 @@ typedef struct SDHCIState SDHCIState; #define SDHCI_VENDOR_NONE 0 #define SDHCI_VENDOR_IMX 1 +#define SDHCI_VENDOR_FSL 2 /* * Controller does not provide transfer-complete interrupt when not From 822405b1fea4f0f404df418bd70da0588b9207ce Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 23 Feb 2025 12:47:08 +0100 Subject: [PATCH 2638/2892] hw/rtc: Add Ricoh RS5C372 RTC emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation just allows Linux to determine date and time. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Acked-by: Fabiano Rosas Message-ID: <20250223114708.1780-19-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 + hw/rtc/Kconfig | 5 + hw/rtc/meson.build | 1 + hw/rtc/rs5c372.c | 236 +++++++++++++++++++++++++++++++++++++ hw/rtc/trace-events | 4 + tests/qtest/meson.build | 1 + tests/qtest/rs5c372-test.c | 43 +++++++ 7 files changed, 292 insertions(+) create mode 100644 hw/rtc/rs5c372.c create mode 100644 tests/qtest/rs5c372-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 0e5db7a574..e34de420f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -827,10 +827,12 @@ F: hw/arm/imx8mp-evk.c F: hw/arm/fsl-imx8mp.c F: hw/misc/imx8mp_*.c F: hw/pci-host/fsl_imx8m_phy.c +F: hw/rtc/rs5c372.c F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h F: include/hw/pci-host/fsl_imx8m_phy.h F: docs/system/arm/imx8mp-evk.rst +F: tests/qtest/rs5c372-test.c MPS2 / MPS3 M: Peter Maydell diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 2fe04ec1d0..315b0e4ecc 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -26,3 +26,8 @@ config GOLDFISH_RTC config LS7A_RTC bool + +config RS5C372_RTC + bool + depends on I2C + default y if I2C_DEVICES diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index 8ecc2d792c..6c87864dc0 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c')) system_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c')) system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c')) system_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c')) +system_ss.add(when: 'CONFIG_RS5C372_RTC', if_true: files('rs5c372.c')) diff --git a/hw/rtc/rs5c372.c b/hw/rtc/rs5c372.c new file mode 100644 index 0000000000..5542f74085 --- /dev/null +++ b/hw/rtc/rs5c372.c @@ -0,0 +1,236 @@ +/* + * Ricoh RS5C372, R222x I2C RTC + * + * Copyright (c) 2025 Bernhard Beschow + * + * Based on hw/rtc/ds1338.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/qdev-properties.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" +#include "qemu/bcd.h" +#include "qom/object.h" +#include "system/rtc.h" +#include "trace.h" + +#define NVRAM_SIZE 0x10 + +/* Flags definitions */ +#define SECONDS_CH 0x80 +#define HOURS_PM 0x20 +#define CTRL2_24 0x20 + +#define TYPE_RS5C372 "rs5c372" +OBJECT_DECLARE_SIMPLE_TYPE(RS5C372State, RS5C372) + +struct RS5C372State { + I2CSlave parent_obj; + + int64_t offset; + uint8_t wday_offset; + uint8_t nvram[NVRAM_SIZE]; + uint8_t ptr; + uint8_t tx_format; + bool addr_byte; +}; + +static void capture_current_time(RS5C372State *s) +{ + /* + * Capture the current time into the secondary registers which will be + * actually read by the data transfer operation. + */ + struct tm now; + qemu_get_timedate(&now, s->offset); + s->nvram[0] = to_bcd(now.tm_sec); + s->nvram[1] = to_bcd(now.tm_min); + if (s->nvram[0xf] & CTRL2_24) { + s->nvram[2] = to_bcd(now.tm_hour); + } else { + int tmp = now.tm_hour; + if (tmp % 12 == 0) { + tmp += 12; + } + if (tmp <= 12) { + s->nvram[2] = to_bcd(tmp); + } else { + s->nvram[2] = HOURS_PM | to_bcd(tmp - 12); + } + } + s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1; + s->nvram[4] = to_bcd(now.tm_mday); + s->nvram[5] = to_bcd(now.tm_mon + 1); + s->nvram[6] = to_bcd(now.tm_year - 100); +} + +static void inc_regptr(RS5C372State *s) +{ + s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); +} + +static int rs5c372_event(I2CSlave *i2c, enum i2c_event event) +{ + RS5C372State *s = RS5C372(i2c); + + switch (event) { + case I2C_START_RECV: + /* + * In h/w, capture happens on any START condition, not just a + * START_RECV, but there is no need to actually capture on + * START_SEND, because the guest can't get at that data + * without going through a START_RECV which would overwrite it. + */ + capture_current_time(s); + s->ptr = 0xf; + break; + case I2C_START_SEND: + s->addr_byte = true; + break; + default: + break; + } + + return 0; +} + +static uint8_t rs5c372_recv(I2CSlave *i2c) +{ + RS5C372State *s = RS5C372(i2c); + uint8_t res; + + res = s->nvram[s->ptr]; + + trace_rs5c372_recv(s->ptr, res); + + inc_regptr(s); + return res; +} + +static int rs5c372_send(I2CSlave *i2c, uint8_t data) +{ + RS5C372State *s = RS5C372(i2c); + + if (s->addr_byte) { + s->ptr = data >> 4; + s->tx_format = data & 0xf; + s->addr_byte = false; + return 0; + } + + trace_rs5c372_send(s->ptr, data); + + if (s->ptr < 7) { + /* Time register. */ + struct tm now; + qemu_get_timedate(&now, s->offset); + switch (s->ptr) { + case 0: + now.tm_sec = from_bcd(data & 0x7f); + break; + case 1: + now.tm_min = from_bcd(data & 0x7f); + break; + case 2: + if (s->nvram[0xf] & CTRL2_24) { + now.tm_hour = from_bcd(data & 0x3f); + } else { + int tmp = from_bcd(data & (HOURS_PM - 1)); + if (data & HOURS_PM) { + tmp += 12; + } + if (tmp % 12 == 0) { + tmp -= 12; + } + now.tm_hour = tmp; + } + break; + case 3: + { + /* + * The day field is supposed to contain a value in the range + * 1-7. Otherwise behavior is undefined. + */ + int user_wday = (data & 7) - 1; + s->wday_offset = (user_wday - now.tm_wday + 7) % 7; + } + break; + case 4: + now.tm_mday = from_bcd(data & 0x3f); + break; + case 5: + now.tm_mon = from_bcd(data & 0x1f) - 1; + break; + case 6: + now.tm_year = from_bcd(data) + 100; + break; + } + s->offset = qemu_timedate_diff(&now); + } else { + s->nvram[s->ptr] = data; + } + inc_regptr(s); + return 0; +} + +static void rs5c372_reset_hold(Object *obj, ResetType type) +{ + RS5C372State *s = RS5C372(obj); + + /* The clock is running and synchronized with the host */ + s->offset = 0; + s->wday_offset = 0; + memset(s->nvram, 0, NVRAM_SIZE); + s->ptr = 0; + s->addr_byte = false; +} + +static const VMStateDescription rs5c372_vmstate = { + .name = "rs5c372", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, RS5C372State), + VMSTATE_INT64(offset, RS5C372State), + VMSTATE_UINT8_V(wday_offset, RS5C372State, 2), + VMSTATE_UINT8_ARRAY(nvram, RS5C372State, NVRAM_SIZE), + VMSTATE_UINT8(ptr, RS5C372State), + VMSTATE_UINT8(tx_format, RS5C372State), + VMSTATE_BOOL(addr_byte, RS5C372State), + VMSTATE_END_OF_LIST() + } +}; + +static void rs5c372_init(Object *obj) +{ + qdev_prop_set_uint8(DEVICE(obj), "address", 0x32); +} + +static void rs5c372_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + k->event = rs5c372_event; + k->recv = rs5c372_recv; + k->send = rs5c372_send; + dc->vmsd = &rs5c372_vmstate; + rc->phases.hold = rs5c372_reset_hold; +} + +static const TypeInfo rs5c372_types[] = { + { + .name = TYPE_RS5C372, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(RS5C372State), + .instance_init = rs5c372_init, + .class_init = rs5c372_class_init, + }, +}; + +DEFINE_TYPES(rs5c372_types) diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index 8012afe102..b9f2852d35 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -35,3 +35,7 @@ m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem write addr:0x%04x val # goldfish_rtc.c goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 + +# rs5c372.c +rs5c372_recv(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] -> 0x%02" PRIx8 +rs5c372_send(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] <- 0x%02" PRIx8 diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 8a6243382a..9e5380ba7a 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -298,6 +298,7 @@ qos_test_ss.add( 'pca9552-test.c', 'pci-test.c', 'pcnet-test.c', + 'rs5c372-test.c', 'sdhci-test.c', 'spapr-phb-test.c', 'tmp105-test.c', diff --git a/tests/qtest/rs5c372-test.c b/tests/qtest/rs5c372-test.c new file mode 100644 index 0000000000..0f6a9b68b9 --- /dev/null +++ b/tests/qtest/rs5c372-test.c @@ -0,0 +1,43 @@ +/* + * QTest testcase for the RS5C372 RTC + * + * Copyright (c) 2025 Bernhard Beschow + * + * Based on ds1338-test.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bcd.h" +#include "libqos/i2c.h" + +#define RS5C372_ADDR 0x32 + +static void rs5c372_read_date(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = obj; + + uint8_t resp[0x10]; + time_t now = time(NULL); + struct tm *utc = gmtime(&now); + + i2c_read_block(i2cdev, 0, resp, sizeof(resp)); + + /* check retrieved time against local time */ + g_assert_cmpuint(from_bcd(resp[5]), == , utc->tm_mday); + g_assert_cmpuint(from_bcd(resp[6]), == , 1 + utc->tm_mon); + g_assert_cmpuint(2000 + from_bcd(resp[7]), == , 1900 + utc->tm_year); +} + +static void rs5c372_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { }; + add_qi2c_address(&opts, &(QI2CAddress) { RS5C372_ADDR }); + + qos_node_create_driver("rs5c372", i2c_device_create); + qos_node_consumes("rs5c372", "i2c-bus", &opts); + qos_add_test("read_date", "rs5c372", rs5c372_read_date, NULL); +} + +libqos_init(rs5c372_register_nodes); From 2fa3a5b9469615d06091cf473d172794148e1248 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 17:47:59 +0000 Subject: [PATCH 2639/2892] hw/net/smc91c111: Sanitize packet numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The smc91c111 uses packet numbers as an index into its internal s->data[][] array. Valid packet numbers are between 0 and 3, but the code does not generally check this, and there are various places where the guest can hand us an arbitrary packet number and cause an out-of-bounds access to the data array. Add validation of packet numbers. The datasheet is not very helpful about how guest errors like this should be handled: it says nothing on the subject, and none of the documented error conditions are relevant. We choose to log the situation with LOG_GUEST_ERROR and silently ignore the attempted operation. In the places where we are about to access the data[][] array using a packet number and we know the number is valid because we got it from somewhere that has already validated, we add an assert() to document that belief. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250228174802.1945417-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/smc91c111.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 0e13dfa18b..2295c6acf2 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -118,6 +118,11 @@ static const VMStateDescription vmstate_smc91c111 = { #define RS_TOOSHORT 0x0400 #define RS_MULTICAST 0x0001 +static inline bool packetnum_valid(int packet_num) +{ + return packet_num >= 0 && packet_num < NUM_PACKETS; +} + /* Update interrupt status. */ static void smc91c111_update(smc91c111_state *s) { @@ -218,6 +223,17 @@ static void smc91c111_pop_tx_fifo_done(smc91c111_state *s) /* Release the memory allocated to a packet. */ static void smc91c111_release_packet(smc91c111_state *s, int packet) { + if (!packetnum_valid(packet)) { + /* + * Data sheet doesn't document behaviour in this guest error + * case, and there is no error status register to report it. + * Log and ignore the attempt. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to release invalid packet %d\n", + packet); + return; + } s->allocated &= ~(1 << packet); if (s->tx_alloc == 0x80) smc91c111_tx_alloc(s); @@ -239,6 +255,8 @@ static void smc91c111_do_tx(smc91c111_state *s) return; for (i = 0; i < s->tx_fifo_len; i++) { packetnum = s->tx_fifo[i]; + /* queue_tx checked the packet number was valid */ + assert(packetnum_valid(packetnum)); p = &s->data[packetnum][0]; /* Set status word. */ *(p++) = 0x01; @@ -287,6 +305,17 @@ static void smc91c111_do_tx(smc91c111_state *s) /* Add a packet to the TX FIFO. */ static void smc91c111_queue_tx(smc91c111_state *s, int packet) { + if (!packetnum_valid(packet)) { + /* + * Datasheet doesn't document behaviour in this error case, and + * there's no error status register we could report it in. + * Log and ignore. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to queue invalid packet %d\n", + packet); + return; + } if (s->tx_fifo_len == NUM_PACKETS) return; s->tx_fifo[s->tx_fifo_len++] = packet; @@ -457,6 +486,13 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, n = s->rx_fifo[0]; else n = s->packet_num; + if (!packetnum_valid(n)) { + /* Datasheet doesn't document what to do here */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to write data to invalid packet %d\n", + n); + return; + } p = s->ptr & 0x07ff; if (s->ptr & 0x4000) { s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); @@ -605,6 +641,13 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) n = s->rx_fifo[0]; else n = s->packet_num; + if (!packetnum_valid(n)) { + /* Datasheet doesn't document what to do here */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to read data from invalid packet %d\n", + n); + return 0; + } p = s->ptr & 0x07ff; if (s->ptr & 0x4000) { s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); @@ -713,6 +756,8 @@ static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t return -1; s->rx_fifo[s->rx_fifo_len++] = packetnum; + /* allocate_packet() will not hand us back an invalid packet number */ + assert(packetnum_valid(packetnum)); p = &s->data[packetnum][0]; /* ??? Multicast packets? */ status = 0; From aad6f264add3f2be72acb660816588fe09110069 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 17:48:00 +0000 Subject: [PATCH 2640/2892] hw/net/smc91c111: Sanitize packet length on tx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the smc91c111 transmits a packet, it must read a control byte which is at the end of the data area and CRC. However, we don't sanitize the length field in the packet buffer, so if the guest sets the length field to something large we will try to read past the end of the packet data buffer when we access the control byte. As usual, the datasheet says nothing about the behaviour of the hardware if the guest misprograms it in this way. It says only that the maximum valid length is 2048 bytes. We choose to log the guest error and silently drop the packet. This requires us to factor out the "mark the tx packet as complete" logic, so we can call it for this "drop packet" case as well as at the end of the loop when we send a valid packet. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2742 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250228174802.1945417-3-peter.maydell@linaro.org> [PMD: Update smc91c111_do_tx() as len > MAX_PACKET_SIZE] Signed-off-by: Philippe Mathieu-Daudé --- hw/net/smc91c111.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 2295c6acf2..72ce5d8f4d 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -22,6 +22,13 @@ /* Number of 2k memory pages available. */ #define NUM_PACKETS 4 +/* + * Maximum size of a data frame, including the leading status word + * and byte count fields and the trailing CRC, last data byte + * and control byte (per figure 8-1 in the Microchip Technology + * LAN91C111 datasheet). + */ +#define MAX_PACKET_SIZE 2048 #define TYPE_SMC91C111 "smc91c111" OBJECT_DECLARE_SIMPLE_TYPE(smc91c111_state, SMC91C111) @@ -240,6 +247,16 @@ static void smc91c111_release_packet(smc91c111_state *s, int packet) smc91c111_flush_queued_packets(s); } +static void smc91c111_complete_tx_packet(smc91c111_state *s, int packetnum) +{ + if (s->ctr & CTR_AUTO_RELEASE) { + /* Race? */ + smc91c111_release_packet(s, packetnum); + } else if (s->tx_fifo_done_len < NUM_PACKETS) { + s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + } +} + /* Flush the TX FIFO. */ static void smc91c111_do_tx(smc91c111_state *s) { @@ -263,6 +280,17 @@ static void smc91c111_do_tx(smc91c111_state *s) *(p++) = 0x40; len = *(p++); len |= ((int)*(p++)) << 8; + if (len > MAX_PACKET_SIZE) { + /* + * Datasheet doesn't say what to do here, and there is no + * relevant tx error condition listed. Log, and drop the packet. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: tx packet with bad length %d, dropping\n", + len); + smc91c111_complete_tx_packet(s, packetnum); + continue; + } len -= 6; control = p[len + 1]; if (control & 0x20) @@ -291,11 +319,7 @@ static void smc91c111_do_tx(smc91c111_state *s) } } #endif - if (s->ctr & CTR_AUTO_RELEASE) - /* Race? */ - smc91c111_release_packet(s, packetnum); - else if (s->tx_fifo_done_len < NUM_PACKETS) - s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + smc91c111_complete_tx_packet(s, packetnum); qemu_send_packet(qemu_get_queue(s->nic), p, len); } s->tx_fifo_len = 0; From e21fe8fb15e70dd8110fd5530521a2e41dc2c201 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 17:48:01 +0000 Subject: [PATCH 2641/2892] hw/net/smc91c111: Use MAX_PACKET_SIZE instead of magic numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a constant for the maximum packet size, we can use it to replace various hardcoded 2048 values. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250228174802.1945417-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/smc91c111.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 72ce5d8f4d..b05970d5e1 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -58,7 +58,7 @@ struct smc91c111_state { int tx_fifo_done_len; int tx_fifo_done[NUM_PACKETS]; /* Packet buffer memory. */ - uint8_t data[NUM_PACKETS][2048]; + uint8_t data[NUM_PACKETS][MAX_PACKET_SIZE]; uint8_t int_level; uint8_t int_mask; MemoryRegion mmio; @@ -86,7 +86,8 @@ static const VMStateDescription vmstate_smc91c111 = { VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS), VMSTATE_INT32(tx_fifo_done_len, smc91c111_state), VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS), - VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048), + VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, + NUM_PACKETS * MAX_PACKET_SIZE), VMSTATE_UINT8(int_level, smc91c111_state), VMSTATE_UINT8(int_mask, smc91c111_state), VMSTATE_END_OF_LIST() @@ -773,8 +774,9 @@ static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t if (crc) packetsize += 4; /* TODO: Flag overrun and receive errors. */ - if (packetsize > 2048) + if (packetsize > MAX_PACKET_SIZE) { return -1; + } packetnum = smc91c111_allocate_packet(s); if (packetnum == 0x80) return -1; From 700d3d6dd41de3bd3f1153e3cfe00b93f99b1441 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 19:16:51 +0000 Subject: [PATCH 2642/2892] hw/net/smc91c111: Don't allow data register access to overrun buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For accesses to the 91c111 data register, the address within the packet's data frame is determined by a combination of the pointer register and the offset used to access the data register, so that you can access data at effectively wider than byte width. The pointer register's pointer field is 11 bits wide, which is exactly the size to index a 2048-byte data frame. We weren't quite getting the logic right for ensuring that we end up with a pointer value to use in the s->data[][] array that isn't out of bounds: * we correctly mask when getting the initial pointer value * for the "autoincrement the pointer register" case, we correctly mask after adding 1 so that the pointer register wraps back around at the 2048 byte mark * but for the non-autoincrement case where we have to add the low 2 bits of the data register offset, we don't account for the possibility that the pointer register is 0x7ff and the addition should wrap Fix this bug by factoring out the "get the p value to use as an array index" into a function, making it use FIELD macro names rather than hard-coded constants, and having a utility function that does "add a value and wrap it" that we can use both for the "autoincrement" and "add the offset bits" codepaths. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2758 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250228191652.1957208-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/smc91c111.c | 65 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index b05970d5e1..9ce42b5615 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -13,6 +13,7 @@ #include "net/net.h" #include "hw/irq.h" #include "hw/net/smc91c111.h" +#include "hw/registerfields.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/log.h" @@ -126,6 +127,13 @@ static const VMStateDescription vmstate_smc91c111 = { #define RS_TOOSHORT 0x0400 #define RS_MULTICAST 0x0001 +FIELD(PTR, PTR, 0, 11) +FIELD(PTR, NOT_EMPTY, 11, 1) +FIELD(PTR, RESERVED, 12, 1) +FIELD(PTR, READ, 13, 1) +FIELD(PTR, AUTOINCR, 14, 1) +FIELD(PTR, RCV, 15, 1) + static inline bool packetnum_valid(int packet_num) { return packet_num >= 0 && packet_num < NUM_PACKETS; @@ -372,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev) #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8) +/* + * The pointer register's pointer is an 11 bit value (so it exactly + * indexes a 2048-byte data frame). Add the specified offset to it, + * wrapping around at the 2048 byte mark, and return the resulting + * wrapped value. There are flag bits in the top part of the register, + * but we can ignore them here as the mask will mask them out. + */ +static int ptr_reg_add(smc91c111_state *s, int offset) +{ + return (s->ptr + offset) & R_PTR_PTR_MASK; +} + +/* + * For an access to the Data Register at @offset, return the + * required offset into the packet's data frame. This will + * perform the pointer register autoincrement if required, and + * guarantees to return an in-bounds offset. + */ +static int data_reg_ptr(smc91c111_state *s, int offset) +{ + int p; + + if (s->ptr & R_PTR_AUTOINCR_MASK) { + /* + * Autoincrement: use the current pointer value, and + * increment the pointer register's pointer field. + */ + p = FIELD_EX32(s->ptr, PTR, PTR); + s->ptr = FIELD_DP32(s->ptr, PTR, PTR, ptr_reg_add(s, 1)); + } else { + /* + * No autoincrement: register offset determines which + * byte we're addressing. Setting the pointer to the top + * of the data buffer and then using the pointer wrapping + * to read the bottom byte of the buffer is not something + * sensible guest software will do, but the datasheet + * doesn't say what the behaviour is, so we don't forbid it. + */ + p = ptr_reg_add(s, offset & 3); + } + return p; +} + static void smc91c111_writeb(void *opaque, hwaddr offset, uint32_t value) { @@ -518,12 +569,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, n); return; } - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); - } else { - p += (offset & 3); - } + p = data_reg_ptr(s, offset); s->data[n][p] = value; } return; @@ -673,12 +719,7 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) n); return 0; } - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); - } else { - p += (offset & 3); - } + p = data_reg_ptr(s, offset); return s->data[n][p]; } case 12: /* Interrupt status. */ From 3a11b653a63fee0e43f4ab84b93f068b961d8fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 5 Mar 2025 12:36:32 +0100 Subject: [PATCH 2643/2892] hw/xen/hvm: Fix Aarch64 typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no TARGET_ARM_64 definition. Luckily enough, when TARGET_AARCH64 is defined, TARGET_ARM also is. Fixes: 733766cd373 ("hw/arm: introduce xenpvh machine") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20250305153929.43687-2-philmd@linaro.org> --- include/hw/xen/arch_hvm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h index c7c515220d..df39c819c8 100644 --- a/include/hw/xen/arch_hvm.h +++ b/include/hw/xen/arch_hvm.h @@ -1,5 +1,5 @@ #if defined(TARGET_I386) || defined(TARGET_X86_64) #include "hw/i386/xen_arch_hvm.h" -#elif defined(TARGET_ARM) || defined(TARGET_ARM_64) +#elif defined(TARGET_ARM) || defined(TARGET_AARCH64) #include "hw/arm/xen_arch_hvm.h" #endif From 5dc4337f7908606865f48e476e36482579e1183f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Mar 2025 23:44:42 +0100 Subject: [PATCH 2644/2892] system: Extract target-specific globals to their own compilation unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We shouldn't use target specific globals for machine properties. These ones could be desugarized, as explained in [*]. While certainly doable, not trivial nor my priority for now. Just move them to a different file to clarify they are *globals*, like the generic globals residing in system/globals.c. Since arch_init.c was introduced using the MIT license (see commit ad96090a01d), retain the same license for the new globals-target.c file. [*] https://lore.kernel.org/qemu-devel/e514d6db-781d-4afe-b057-9046c70044dc@redhat.com/ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20250305005225.95051-2-philmd@linaro.org> --- system/arch_init.c | 14 -------------- system/globals-target.c | 24 ++++++++++++++++++++++++ system/meson.build | 1 + 3 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 system/globals-target.c diff --git a/system/arch_init.c b/system/arch_init.c index b1baed18a3..b9147af93c 100644 --- a/system/arch_init.c +++ b/system/arch_init.c @@ -24,18 +24,4 @@ #include "qemu/osdep.h" #include "system/arch_init.h" -#ifdef TARGET_SPARC -int graphic_width = 1024; -int graphic_height = 768; -int graphic_depth = 8; -#elif defined(TARGET_M68K) -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 8; -#else -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 32; -#endif - const uint32_t arch_type = QEMU_ARCH; diff --git a/system/globals-target.c b/system/globals-target.c new file mode 100644 index 0000000000..989720591e --- /dev/null +++ b/system/globals-target.c @@ -0,0 +1,24 @@ +/* + * Global variables that should not exist (target specific) + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "system/system.h" + +#ifdef TARGET_SPARC +int graphic_width = 1024; +int graphic_height = 768; +int graphic_depth = 8; +#elif defined(TARGET_M68K) +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 8; +#else +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 32; +#endif diff --git a/system/meson.build b/system/meson.build index c83d80fa24..eec07a9451 100644 --- a/system/meson.build +++ b/system/meson.build @@ -1,6 +1,7 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'arch_init.c', 'ioport.c', + 'globals-target.c', 'memory.c', 'physmem.c', )]) From 44ac8eaff00735655401e7d899282875da568fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 4 Mar 2025 23:59:27 +0100 Subject: [PATCH 2645/2892] system: Replace arch_type global by qemu_arch_available() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_arch_available() is a bit simpler to understand while reviewing than the undocumented arch_type variable. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250305005225.95051-5-philmd@linaro.org> --- hw/scsi/scsi-disk.c | 2 +- include/system/arch_init.h | 2 +- system/arch_init.c | 5 ++++- system/qdev-monitor.c | 4 ++-- system/vl.c | 6 +++--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e7f738b484..7c87b20e69 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3165,7 +3165,7 @@ static void scsi_property_add_specifics(DeviceClass *dc) ObjectClass *oc = OBJECT_CLASS(dc); /* The loadparm property is only supported on s390x */ - if (arch_type & QEMU_ARCH_S390X) { + if (qemu_arch_available(QEMU_ARCH_S390X)) { object_class_property_add_str(oc, "loadparm", scsi_property_get_loadparm, scsi_property_set_loadparm); diff --git a/include/system/arch_init.h b/include/system/arch_init.h index d8b7744048..51e24c3091 100644 --- a/include/system/arch_init.h +++ b/include/system/arch_init.h @@ -25,6 +25,6 @@ enum { QEMU_ARCH_LOONGARCH = (1 << 23), }; -extern const uint32_t arch_type; +bool qemu_arch_available(unsigned qemu_arch_mask); #endif diff --git a/system/arch_init.c b/system/arch_init.c index b9147af93c..e85736884c 100644 --- a/system/arch_init.c +++ b/system/arch_init.c @@ -24,4 +24,7 @@ #include "qemu/osdep.h" #include "system/arch_init.h" -const uint32_t arch_type = QEMU_ARCH; +bool qemu_arch_available(unsigned qemu_arch_mask) +{ + return qemu_arch_mask & QEMU_ARCH; +} diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 856c9e8c32..5588ed2047 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -132,7 +132,7 @@ static const char *qdev_class_get_alias(DeviceClass *dc) for (i = 0; qdev_alias_table[i].typename; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } @@ -218,7 +218,7 @@ static const char *find_typename_by_alias(const char *alias) for (i = 0; qdev_alias_table[i].alias; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } diff --git a/system/vl.c b/system/vl.c index 04f78466c4..ec93988a03 100644 --- a/system/vl.c +++ b/system/vl.c @@ -878,11 +878,11 @@ static void help(int exitcode) g_get_prgname()); #define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ - if ((arch_mask) & arch_type) \ + if (qemu_arch_available(arch_mask)) \ fputs(opt_help, stdout); #define ARCHHEADING(text, arch_mask) \ - if ((arch_mask) & arch_type) \ + if (qemu_arch_available(arch_mask)) \ puts(stringify(text)); #define DEFHEADING(text) ARCHHEADING(text, QEMU_ARCH_ALL) @@ -2929,7 +2929,7 @@ void qemu_init(int argc, char **argv) const QEMUOption *popt; popt = lookup_opt(argc, argv, &optarg, &optind); - if (!(popt->arch_mask & arch_type)) { + if (!qemu_arch_available(popt->arch_mask)) { error_report("Option not supported for this target"); exit(1); } From e6ffea40e221d26a213a59cba2598e15f01facf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 16:01:31 +0100 Subject: [PATCH 2646/2892] hw/acpi: Introduce acpi_builtin() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit acpi_builtin() can be used to check at runtime whether the ACPI subsystem is built in a qemu-system binary. Reviewed-by: Ani Sinha Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250307223949.54040-3-philmd@linaro.org> --- hw/acpi/acpi-stub.c | 5 +++++ hw/acpi/core.c | 5 +++++ include/hw/acpi/acpi.h | 3 +++ 3 files changed, 13 insertions(+) diff --git a/hw/acpi/acpi-stub.c b/hw/acpi/acpi-stub.c index e268ce9b1a..790bf509e5 100644 --- a/hw/acpi/acpi-stub.c +++ b/hw/acpi/acpi-stub.c @@ -25,3 +25,8 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) { g_assert_not_reached(); } + +bool acpi_builtin(void) +{ + return false; +} diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 870391ed7c..58f8964e13 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -78,6 +78,11 @@ static void acpi_register_config(void) opts_init(acpi_register_config); +bool acpi_builtin(void) +{ + return true; +} + static int acpi_checksum(const uint8_t *data, int len) { int sum, i; diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index e0e51e85b4..d1a4fa2af8 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -150,6 +150,9 @@ struct ACPIREGS { Notifier wakeup; }; +/* Return whether ACPI subsystem is built in */ +bool acpi_builtin(void); + /* PM_TMR */ void acpi_pm_tmr_update(ACPIREGS *ar, bool enable); void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar); From bb99b92a6b1828fe7e37497ad1aac157cdcb36b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 16:01:59 +0100 Subject: [PATCH 2647/2892] hw/i386/fw_cfg: Check ACPI availability with acpi_builtin() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define acpi_tables / acpi_tables_len stubs, then replace the compile-time CONFIG_ACPI check in fw_cfg.c by a runtime one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ani Sinha Message-Id: <20250307223949.54040-4-philmd@linaro.org> --- hw/acpi/acpi-stub.c | 3 +++ hw/i386/fw_cfg.c | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/acpi/acpi-stub.c b/hw/acpi/acpi-stub.c index 790bf509e5..fd0b62fad9 100644 --- a/hw/acpi/acpi-stub.c +++ b/hw/acpi/acpi-stub.c @@ -21,6 +21,9 @@ #include "qemu/osdep.h" #include "hw/acpi/acpi.h" +char unsigned *acpi_tables; +size_t acpi_tables_len; + void acpi_table_add(const QemuOpts *opts, Error **errp) { g_assert_not_reached(); diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d08aefa029..a7f1b60b98 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -145,10 +145,10 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, */ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, apic_id_limit); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, ms->ram_size); -#ifdef CONFIG_ACPI - fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, - acpi_tables, acpi_tables_len); -#endif + if (acpi_builtin()) { + fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, + acpi_tables, acpi_tables_len); + } fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_fw_cfg, sizeof(hpet_fw_cfg)); From f410d702e4b4925e178cf1effa314e01323c488b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Mar 2025 16:02:23 +0100 Subject: [PATCH 2648/2892] hw/virtio/virtio-mem: Remove CONFIG_DEVICES include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than checking ACPI availability at compile time by checking the CONFIG_ACPI definition from CONFIG_DEVICES, check at runtime via acpi_builtin(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Reviewed-by: Pierrick Bouvier Message-Id: <20250307223949.54040-5-philmd@linaro.org> --- hw/virtio/virtio-mem.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 7b140add76..5f57eccbb6 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -28,7 +28,7 @@ #include "migration/misc.h" #include "hw/boards.h" #include "hw/qdev-properties.h" -#include CONFIG_DEVICES +#include "hw/acpi/acpi.h" #include "trace.h" static const VMStateDescription vmstate_virtio_mem_device_early; @@ -883,10 +883,8 @@ static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features, MachineState *ms = MACHINE(qdev_get_machine()); VirtIOMEM *vmem = VIRTIO_MEM(vdev); - if (ms->numa_state) { -#if defined(CONFIG_ACPI) + if (ms->numa_state && acpi_builtin()) { virtio_add_feature(&features, VIRTIO_MEM_F_ACPI_PXM); -#endif } assert(vmem->unplugged_inaccessible != ON_OFF_AUTO_AUTO); if (vmem->unplugged_inaccessible == ON_OFF_AUTO_ON) { From 003d35ad6c612d13ebf0a78f828b0c3ee4f44e3d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 7 Mar 2025 13:56:20 -0800 Subject: [PATCH 2649/2892] hw/hyperv/hyperv-proto: Move SYNDBG definitions from target/i386 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows SYNDBG definitions to be available for common compilation units. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-ID: <20250307215623.524987-5-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/hyperv/hyperv-proto.h | 12 ++++++++++++ target/i386/kvm/hyperv-proto.h | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 4a2297307b..fffc5ce342 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -61,6 +61,18 @@ #define HV_MESSAGE_X64_APIC_EOI 0x80010004 #define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 +#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + /* * Message flags */ diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 464fbf09e3..a9f056f2f3 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -151,18 +151,6 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 #define HV_X64_MSR_STIMER3_COUNT 0x400000B7 -/* - * Hyper-V Synthetic debug options MSR - */ -#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 -#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 -#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 -#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 -#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 -#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF - -#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) - /* * Guest crash notification MSRs */ From 7f2a5272ff3893ef412c093aae66b7ed34ab3dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 8 Mar 2025 16:12:13 +0100 Subject: [PATCH 2650/2892] hw/sd/sdhci: Remove need for SDHCI_VENDOR_IMX definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All instances of TYPE_IMX_USDHC set vendor=SDHCI_VENDOR_IMX. No need to special-case it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: BALATON Zoltan Reviewed-by: Bernhard Beschow Message-Id: <20250308213640.13138-3-philmd@linaro.org> --- hw/arm/fsl-imx25.c | 2 -- hw/arm/fsl-imx6.c | 2 -- hw/arm/fsl-imx6ul.c | 2 -- hw/arm/fsl-imx7.c | 2 -- hw/arm/fsl-imx8mp.c | 2 -- hw/sd/sdhci.c | 14 ++++---------- include/hw/sd/sdhci.h | 1 - 7 files changed, 4 insertions(+), 21 deletions(-) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 5359a6d8d3..02214ca1a1 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -243,8 +243,6 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_uint(OBJECT(&s->esdhc[i]), "capareg", IMX25_ESDHC_CAPABILITIES, &error_abort); - object_property_set_uint(OBJECT(&s->esdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->esdhc[i]), errp)) { return; } diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index dc86338b3a..a114dc0d63 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -327,8 +327,6 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_uint(OBJECT(&s->esdhc[i]), "capareg", IMX6_ESDHC_CAPABILITIES, &error_abort); - object_property_set_uint(OBJECT(&s->esdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->esdhc[i]), errp)) { return; } diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 34c4aa15cd..ce8d3ef535 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -531,8 +531,6 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_USDHC2_IRQ, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 3374018cde..ed1f10bca2 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -471,8 +471,6 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_USDHC3_IRQ, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 1ea98e1463..c3f6da6322 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -524,8 +524,6 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC3].addr, FSL_IMX8MP_USDHC3_IRQ }, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { return; } diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index fe87e18d5d..69baf73ae9 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1735,16 +1735,10 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) case USDHC_VENDOR_SPEC: s->vendor_spec = value; - switch (s->vendor) { - case SDHCI_VENDOR_IMX: - if (value & USDHC_IMX_FRC_SDCLK_ON) { - s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; - } else { - s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; - } - break; - default: - break; + if (value & USDHC_IMX_FRC_SDCLK_ON) { + s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; + } else { + s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; } break; diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index f722d8eb1c..51fb30ea52 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -109,7 +109,6 @@ struct SDHCIState { typedef struct SDHCIState SDHCIState; #define SDHCI_VENDOR_NONE 0 -#define SDHCI_VENDOR_IMX 1 #define SDHCI_VENDOR_FSL 2 /* From 48170c2d865a5937092b1384421b01cd38113042 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 6 Mar 2025 18:41:13 +0100 Subject: [PATCH 2651/2892] docs: Rename default-configs to configs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was missed at the time. Fixes: 812b31d3f91 ("configs: rename default-configs to configs and reorganise") Signed-off-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250306174113.427116-1-groug@kaod.org> Signed-off-by: Thomas Huth --- docs/devel/build-system.rst | 10 +++++----- docs/devel/kconfig.rst | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index d42045a232..a759982f45 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -260,7 +260,7 @@ Target-dependent emulator sourcesets: Each emulator also includes sources for files in the ``hw/`` and ``target/`` subdirectories. The subdirectory used for each emulator comes from the target's definition of ``TARGET_BASE_ARCH`` or (if missing) - ``TARGET_ARCH``, as found in ``default-configs/targets/*.mak``. + ``TARGET_ARCH``, as found in ``configs/targets/*.mak``. Each subdirectory in ``hw/`` adds one sourceset to the ``hw_arch`` dictionary, for example:: @@ -317,8 +317,8 @@ Utility sourcesets: The following files concur in the definition of which files are linked into each emulator: -``default-configs/devices/*.mak`` - The files under ``default-configs/devices/`` control the boards and devices +``configs/devices/*.mak`` + The files under ``configs/devices/`` control the boards and devices that are built into each QEMU system emulation targets. They merely contain a list of config variable definitions such as:: @@ -327,11 +327,11 @@ into each emulator: CONFIG_XLNX_VERSAL=y ``*/Kconfig`` - These files are processed together with ``default-configs/devices/*.mak`` and + These files are processed together with ``configs/devices/*.mak`` and describe the dependencies between various features, subsystems and device models. They are described in :ref:`kconfig` -``default-configs/targets/*.mak`` +``configs/targets/*.mak`` These files mostly define symbols that appear in the ``*-config-target.h`` file for each emulator\ [#cfgtarget]_. However, the ``TARGET_ARCH`` and ``TARGET_BASE_ARCH`` will also be used to select the ``hw/`` and diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 52d4b905f6..493b76c4fb 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -38,7 +38,7 @@ originated in the Linux kernel, though it was heavily simplified and the handling of dependencies is stricter in QEMU. Unlike Linux, there is no user interface to edit the configuration, which -is instead specified in per-target files under the ``default-configs/`` +is instead specified in per-target files under the ``configs/`` directory of the QEMU source tree. This is because, unlike Linux, configuration and dependencies can be treated as a black box when building QEMU; the default configuration that QEMU ships with should be okay in @@ -103,7 +103,7 @@ directives can be included: **default value**: ``default [if ]`` Default values are assigned to the config symbol if no other value was - set by the user via ``default-configs/*.mak`` files, and only if + set by the user via ``configs/*.mak`` files, and only if ``select`` or ``depends on`` directives do not force the value to true or false respectively. ```` can be ``y`` or ``n``; it cannot be an arbitrary Boolean expression. However, a condition for applying @@ -119,7 +119,7 @@ directives can be included: This is similar to ``select`` as it applies a lower limit of ``y`` to another symbol. However, the lower limit is only a default and the "implied" symbol's value may still be set to ``n`` from a - ``default-configs/*.mak`` files. The following two examples are + ``configs/*.mak`` files. The following two examples are equivalent:: config FOO @@ -146,7 +146,7 @@ declares its dependencies in different ways: bool Subsystems always default to false (they have no ``default`` directive) - and are never visible in ``default-configs/*.mak`` files. It's + and are never visible in ``configs/*.mak`` files. It's up to other symbols to ``select`` whatever subsystems they require. They sometimes have ``select`` directives to bring in other required @@ -238,7 +238,7 @@ declares its dependencies in different ways: include libraries (such as ``FDT``) or ``TARGET_BIG_ENDIAN`` (possibly negated). - Boards are listed for convenience in the ``default-configs/*.mak`` + Boards are listed for convenience in the ``configs/*.mak`` for the target they apply to. **internal elements** @@ -251,18 +251,18 @@ declares its dependencies in different ways: Internal elements group code that is useful in several boards or devices. They are usually enabled with ``select`` and in turn select - other elements; they are never visible in ``default-configs/*.mak`` + other elements; they are never visible in ``configs/*.mak`` files, and often not even in the Makefile. Writing and modifying default configurations -------------------------------------------- In addition to the Kconfig files under hw/, each target also includes -a file called ``default-configs/TARGETNAME-softmmu.mak``. These files +a file called ``configs/TARGETNAME-softmmu.mak``. These files initialize some Kconfig variables to non-default values and provide the starting point to turn on devices and subsystems. -A file in ``default-configs/`` looks like the following example:: +A file in ``configs/`` looks like the following example:: # Default configuration for alpha-softmmu From 533b33d04bff604863deff3f7d41396d110b57e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Sat, 8 Mar 2025 08:13:28 +0100 Subject: [PATCH 2652/2892] tests/functional: Require 'user' netdev for ppc64 e500 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When commit 72cdd672e18c extended the ppc64 e500 test to add network support, it forgot to require the 'user' netdev backend. Fix that. Fixes: 72cdd672e18c ("tests/functional: Replace the ppc64 e500 advent calendar test") Signed-off-by: Cédric Le Goater Reviewed-by: Thomas Huth Acked-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250308071328.193694-1-clg@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_e500.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py index 9ce7ae6c47..f5fcad9f6b 100755 --- a/tests/functional/test_ppc64_e500.py +++ b/tests/functional/test_ppc64_e500.py @@ -20,6 +20,7 @@ class E500Test(LinuxKernelTest): def test_ppc64_e500_buildroot(self): self.set_machine('ppce500') + self.require_netdev('user') self.cpu = 'e5500' uimage_path = self.ASSET_BR2_E5500_UIMAGE.fetch() From 8c63f9aa3f885f06edac3d1e4f21bde939f8c517 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Mar 2025 10:28:30 +0000 Subject: [PATCH 2653/2892] tests/functional: Bump up arm_replay timeout On my machine the arm_replay test takes over 2 minutes to run in a config with Rust enabled and debug enabled: $ time (cd build/rust ; PYTHONPATH=../../python:../../tests/functional QEMU_TEST_QEMU_BINARY=./qemu-system-arm ./pyvenv/bin/python3 ../../tests/functional/test_arm_replay.py) TAP version 13 ok 1 test_arm_replay.ArmReplay.test_cubieboard ok 2 test_arm_replay.ArmReplay.test_vexpressa9 ok 3 test_arm_replay.ArmReplay.test_virt 1..3 real 2m16.564s user 2m13.461s sys 0m3.523s Bump up the timeout to 4 minutes. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-ID: <20250310102830.3752440-1-peter.maydell@linaro.org> Signed-off-by: Thomas Huth --- tests/functional/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e78560a901..e181ed1ea5 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -34,6 +34,7 @@ test_timeouts = { 'arm_orangepi' : 540, 'arm_quanta_gsj' : 240, 'arm_raspi2' : 120, + 'arm_replay' : 240, 'arm_tuxrun' : 240, 'arm_sx1' : 360, 'intel_iommu': 300, From 15ef93dd48c2635d0e9f9e9a4f6dd92a40e23bff Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 11 Mar 2025 17:08:47 +0100 Subject: [PATCH 2654/2892] docs/system: Fix the information on how to run certain functional tests The tests have been converted to the functional framework, so we should not talk about Avocado here anymore. Fixes: f7d6b772200 ("tests/functional: Convert BananaPi tests to the functional framework") Fixes: 380f7268b7b ("tests/functional: Convert the OrangePi tests to the functional framework") Fixes: 4c0a2df81c9 ("tests/functional: Convert some tests that download files via fetch_asset()") Message-ID: <20250311160847.388670-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- docs/system/arm/bananapi_m2u.rst | 5 ++--- docs/system/arm/orangepi.rst | 6 +++--- docs/system/devices/igb.rst | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/system/arm/bananapi_m2u.rst b/docs/system/arm/bananapi_m2u.rst index 587b488655..d30db8d04c 100644 --- a/docs/system/arm/bananapi_m2u.rst +++ b/docs/system/arm/bananapi_m2u.rst @@ -135,6 +135,5 @@ provide the following command: .. code-block:: bash $ cd qemu-build-dir - $ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \ - --verbose --show=app,console run -t machine:bpim2u \ - ../tests/avocado/boot_linux_console.py + $ QEMU_TEST_ALLOW_LARGE_STORAGE=1 \ + meson test --suite thorough func-arm-arm_bpim2u diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index db87e81fec..8b9448ca7b 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -257,9 +257,9 @@ Orange Pi PC integration tests The Orange Pi PC machine has several integration tests included. To run the whole set of tests, build QEMU from source and simply -provide the following command: +provide the following command from the build directory: .. code-block:: bash - $ AVOCADO_ALLOW_LARGE_STORAGE=yes avocado --show=app,console run \ - -t machine:orangepi-pc tests/avocado/boot_linux_console.py + $ QEMU_TEST_ALLOW_LARGE_STORAGE=1 \ + meson test --suite thorough func-arm-arm_orangepi diff --git a/docs/system/devices/igb.rst b/docs/system/devices/igb.rst index 04e79dfe54..9145af5c75 100644 --- a/docs/system/devices/igb.rst +++ b/docs/system/devices/igb.rst @@ -57,11 +57,11 @@ directory: meson test qtest-x86_64/qos-test ethtool can test register accesses, interrupts, etc. It is automated as an -Avocado test and can be ran with the following command: +functional test and can be ran with the following command: .. code:: shell - make check-avocado AVOCADO_TESTS=tests/avocado/netdev-ethtool.py + meson test --suite thorough func-x86_64-netdev_ethtool References ========== From a5e8299d1a119b9d757ae28a57612f633894d2f6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Mar 2025 23:00:00 +1000 Subject: [PATCH 2655/2892] tests/functional/asset: Fail assert fetch when retries are exceeded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the fetch code does not fail gracefully when retry limit is exceeded, it just falls through the loop with no file, which ends up hitting other errors. Add a check for non-existing file, which indicates the retry limit was exceeded. Reviewed-by: Daniel P. Berrangé Signed-off-by: Nicholas Piggin Message-ID: <20250312130002.945508-2-npiggin@gmail.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index f0730695f0..27dd839e70 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -138,6 +138,9 @@ class Asset: tmp_cache_file.unlink() raise + if not os.path.exists(tmp_cache_file): + raise Exception("Retries exceeded downloading %s", self.url) + try: # Set these just for informational purposes os.setxattr(str(tmp_cache_file), "user.qemu-asset-url", From 7524e1b33679dc1356e8bb4efdd18e83fc50f5cc Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Mar 2025 23:00:01 +1000 Subject: [PATCH 2656/2892] tests/functional/asset: Verify downloaded size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the server provides a Content-Length header, use that to verify the size of the downloaded file. This catches cases where the connection terminates early, and gives the opportunity to retry. Without this, the checksum will likely mismatch and fail without retry. Reviewed-by: Daniel P. Berrangé Signed-off-by: Nicholas Piggin Message-ID: <20250312130002.945508-3-npiggin@gmail.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 27dd839e70..6bbfb9e1ca 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -121,6 +121,20 @@ class Asset: with tmp_cache_file.open("xb") as dst: with urllib.request.urlopen(self.url) as resp: copyfileobj(resp, dst) + length_hdr = resp.getheader("Content-Length") + + # Verify downloaded file size against length metadata, if + # available. + if length_hdr is not None: + length = int(length_hdr) + fsize = tmp_cache_file.stat().st_size + if fsize != length: + self.log.error("Unable to download %s: " + "connection closed before " + "transfer complete (%d/%d)", + self.url, fsize, length) + tmp_cache_file.unlink() + continue break except FileExistsError: self.log.debug("%s already exists, " From 28adad0a4d9f1f64f9f04748c6348a64ba7ad990 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Mar 2025 23:00:02 +1000 Subject: [PATCH 2657/2892] tests/functional/asset: Add AssetError exception class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assets are uniquely identified by human-readable-ish url, so make an AssetError exception class that prints url with error message. A property 'transient' is used to capture whether the client may retry or try again later, or if it is a serious and likely permanent error. This is used to retain the existing behaviour of treating HTTP errors other than 404 as 'transient' and not causing precache step to fail. Additionally, partial-downloads and stale asset caches that fail to resolve after the retry limit are now treated as transient and do not cause precache step to fail. For background: The NetBSD archive is, at the time of writing, failing with short transfer. Retrying the fetch at that position (as wget does) results in a "503 backend unavailable" error. We would like to get that error code directly, but I have not found a way to do that with urllib, so treating the short-copy as a transient failure covers that case (and seems like a reasonable way to handle it in general). Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Nicholas Piggin Message-ID: <20250312130002.945508-4-npiggin@gmail.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 45 +++++++++++++++++++---------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 6bbfb9e1ca..704b84d0ea 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -17,6 +17,14 @@ from pathlib import Path from shutil import copyfileobj from urllib.error import HTTPError +class AssetError(Exception): + def __init__(self, asset, msg, transient=False): + self.url = asset.url + self.msg = msg + self.transient = transient + + def __str__(self): + return "%s: %s" % (self.url, self.msg) # Instances of this class must be declared as class level variables # starting with a name "ASSET_". This enables the pre-caching logic @@ -51,7 +59,7 @@ class Asset: elif len(self.hash) == 128: hl = hashlib.sha512() else: - raise Exception("unknown hash type") + raise AssetError(self, "unknown hash type") # Calculate the hash of the file: with open(cache_file, 'rb') as file: @@ -111,7 +119,8 @@ class Asset: return str(self.cache_file) if not self.fetchable(): - raise Exception("Asset cache is invalid and downloads disabled") + raise AssetError(self, + "Asset cache is invalid and downloads disabled") self.log.info("Downloading %s to %s...", self.url, self.cache_file) tmp_cache_file = self.cache_file.with_suffix(".download") @@ -147,13 +156,23 @@ class Asset: tmp_cache_file) tmp_cache_file.unlink() continue - except Exception as e: - self.log.error("Unable to download %s: %s", self.url, e) + except HTTPError as e: tmp_cache_file.unlink() - raise + self.log.error("Unable to download %s: HTTP error %d", + self.url, e.code) + # Treat 404 as fatal, since it is highly likely to + # indicate a broken test rather than a transient + # server or networking problem + if e.code == 404: + raise AssetError(self, "Unable to download: " + "HTTP error %d" % e.code) + continue + except Exception as e: + tmp_cache_file.unlink() + raise AssetError(self, "Unable to download: " % e) if not os.path.exists(tmp_cache_file): - raise Exception("Retries exceeded downloading %s", self.url) + raise AssetError(self, "Download retries exceeded", transient=True) try: # Set these just for informational purposes @@ -167,8 +186,7 @@ class Asset: if not self._check(tmp_cache_file): tmp_cache_file.unlink() - raise Exception("Hash of %s does not match %s" % - (self.url, self.hash)) + raise AssetError(self, "Hash does not match %s" % self.hash) tmp_cache_file.replace(self.cache_file) # Remove write perms to stop tests accidentally modifying them os.chmod(self.cache_file, stat.S_IRUSR | stat.S_IRGRP) @@ -190,15 +208,10 @@ class Asset: log.info("Attempting to cache '%s'" % asset) try: asset.fetch() - except HTTPError as e: - # Treat 404 as fatal, since it is highly likely to - # indicate a broken test rather than a transient - # server or networking problem - if e.code == 404: + except AssetError as e: + if not e.transient: raise - - log.debug(f"HTTP error {e.code} from {asset.url} " + - "skipping asset precache") + log.error("%s: skipping asset precache" % e) log.removeHandler(handler) From b3c03666fb10b4900e5bbff0a2b403731730e637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 12 Mar 2025 19:03:13 +0000 Subject: [PATCH 2658/2892] tests/functional: skip vulkan test if missing vulkaninfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I could have sworn I had this is a previous iteration of the patches but I guess it got lost in a re-base. As we are going to call vulkaninfo to probe for "bad" drivers we need to skip if the binary isn't available. Fixes: 9f7e493d11 (tests/functional: skip vulkan tests with nVidia) Signed-off-by: Alex Bennée Message-ID: <20250312190314.1632357-1-alex.bennee@linaro.org> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_virt_gpu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index f19a47f8b6..314d994a7a 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -115,6 +115,7 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): self._run_virt_weston_test("glmark2-wayland -b:duration=1.0") @skipIfMissingCommands('zstd') + @skipIfMissingCommands('vulkaninfo') def test_aarch64_virt_with_vulkan_gpu(self): self.require_device('virtio-gpu-gl-pci') From 984a32f17e8dab0dc3d2328c46cb3e0c0a472a73 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Mar 2025 23:16:30 +0100 Subject: [PATCH 2659/2892] file-posix: Support FUA writes Until now, FUA was always emulated with a separate flush after the write for file-posix. The overhead of processing a second request can reduce performance significantly for a guest disk that has disabled the write cache, especially if the host disk is already write through, too, and the flush isn't actually doing anything. Advertise support for REQ_FUA in write requests and implement it for Linux AIO and io_uring using the RWF_DSYNC flag for write requests. The thread pool still performs a separate fdatasync() call. This can be improved later by using the pwritev2() syscall if available. As an example, this is how fio numbers can be improved in some scenarios with this patch (all using virtio-blk with cache=directsync on an nvme block device for the VM, fio with ioengine=libaio,direct=1,sync=1): | old | with FUA support ------------------------------+---------------+------------------- bs=4k, iodepth=1, numjobs=1 | 45.6k iops | 56.1k iops bs=4k, iodepth=1, numjobs=16 | 183.3k iops | 236.0k iops bs=4k, iodepth=16, numjobs=1 | 258.4k iops | 311.1k iops However, not all scenarios are clear wins. On another slower disk I saw little to no improvment. In fact, in two corner case scenarios, I even observed a regression, which I however consider acceptable: 1. On slow host disks in a write through cache mode, when the guest is using virtio-blk in a separate iothread so that polling can be enabled, and each completion is quickly followed up with a new request (so that polling gets it), it can happen that enabling FUA makes things slower - the additional very fast no-op flush we used to have gave the adaptive polling algorithm a success so that it kept polling. Without it, we only have the slow write request, which disables polling. This is a problem in the polling algorithm that will be fixed later in this series. 2. With a high queue depth, it can be beneficial to have flush requests for another reason: The optimisation in bdrv_co_flush() that flushes only once per write generation acts as a synchronisation mechanism that lets all requests complete at the same time. This can result in better batching and if the disk is very fast (I only saw this with a null_blk backend), this can make up for the overhead of the flush and improve throughput. In theory, we could optionally introduce a similar artificial latency in the normal completion path to achieve the same kind of completion batching. This is not implemented in this series. Compatibility is not a concern for the kernel side of io_uring, it has supported RWF_DSYNC from the start. However, io_uring_prep_writev2() is not available before liburing 2.2. Linux AIO started supporting it in Linux 4.13 and libaio 0.3.111. The kernel is not a problem for any supported build platform, so it's not necessary to add runtime checks. However, openSUSE is still stuck with an older libaio version that would break the build. We must detect the presence of the writev2 functions in the user space libraries at build time to avoid build failures. Signed-off-by: Kevin Wolf Message-ID: <20250307221634.71951-2-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/file-posix.c | 29 +++++++++++++++++++++-------- block/io_uring.c | 25 ++++++++++++++++++++++--- block/linux-aio.c | 25 ++++++++++++++++++++++--- include/block/raw-aio.h | 19 +++++++++++++++++-- meson.build | 8 ++++++++ 5 files changed, 90 insertions(+), 16 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 44e16dda87..56d1972d15 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -194,6 +194,7 @@ static int fd_open(BlockDriverState *bs) } static int64_t raw_getlength(BlockDriverState *bs); +static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs); typedef struct RawPosixAIOData { BlockDriverState *bs; @@ -804,6 +805,13 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, #endif s->needs_alignment = raw_needs_alignment(bs); + bs->supported_write_flags = BDRV_REQ_FUA; + if (s->use_linux_aio && !laio_has_fua()) { + bs->supported_write_flags &= ~BDRV_REQ_FUA; + } else if (s->use_linux_io_uring && !luring_has_fua()) { + bs->supported_write_flags &= ~BDRV_REQ_FUA; + } + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; if (S_ISREG(st.st_mode)) { /* When extending regular files, we get zeros from the OS */ @@ -2477,7 +2485,8 @@ static inline bool raw_check_linux_aio(BDRVRawState *s) #endif static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, - uint64_t bytes, QEMUIOVector *qiov, int type) + uint64_t bytes, QEMUIOVector *qiov, int type, + int flags) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; @@ -2508,13 +2517,13 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, #ifdef CONFIG_LINUX_IO_URING } else if (raw_check_linux_io_uring(s)) { assert(qiov->size == bytes); - ret = luring_co_submit(bs, s->fd, offset, qiov, type); + ret = luring_co_submit(bs, s->fd, offset, qiov, type, flags); goto out; #endif #ifdef CONFIG_LINUX_AIO } else if (raw_check_linux_aio(s)) { assert(qiov->size == bytes); - ret = laio_co_submit(s->fd, offset, qiov, type, + ret = laio_co_submit(s->fd, offset, qiov, type, flags, s->aio_max_batch); goto out; #endif @@ -2534,6 +2543,10 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, assert(qiov->size == bytes); ret = raw_thread_pool_submit(handle_aiocb_rw, &acb); + if (ret == 0 && (flags & BDRV_REQ_FUA)) { + /* TODO Use pwritev2() instead if it's available */ + ret = raw_co_flush_to_disk(bs); + } goto out; /* Avoid the compiler err of unused label */ out: @@ -2571,14 +2584,14 @@ static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ, flags); } static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE, flags); } static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) @@ -2600,12 +2613,12 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) #ifdef CONFIG_LINUX_IO_URING if (raw_check_linux_io_uring(s)) { - return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); + return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); } #endif #ifdef CONFIG_LINUX_AIO if (s->has_laio_fdsync && raw_check_linux_aio(s)) { - return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); + return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0, 0); } #endif return raw_thread_pool_submit(handle_aiocb_flush, &acb); @@ -3540,7 +3553,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, } trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); - return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); + return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND, 0); } #endif diff --git a/block/io_uring.c b/block/io_uring.c index f52b66b340..dd4f304910 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -335,15 +335,24 @@ static void luring_deferred_fn(void *opaque) * */ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, - uint64_t offset, int type) + uint64_t offset, int type, BdrvRequestFlags flags) { int ret; struct io_uring_sqe *sqes = &luringcb->sqeq; switch (type) { case QEMU_AIO_WRITE: +#ifdef HAVE_IO_URING_PREP_WRITEV2 + { + int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0; + io_uring_prep_writev2(sqes, fd, luringcb->qiov->iov, + luringcb->qiov->niov, offset, luring_flags); + } +#else + assert(flags == 0); io_uring_prep_writev(sqes, fd, luringcb->qiov->iov, luringcb->qiov->niov, offset); +#endif break; case QEMU_AIO_ZONE_APPEND: io_uring_prep_writev(sqes, fd, luringcb->qiov->iov, @@ -380,7 +389,8 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, } int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, - QEMUIOVector *qiov, int type) + QEMUIOVector *qiov, int type, + BdrvRequestFlags flags) { int ret; AioContext *ctx = qemu_get_current_aio_context(); @@ -393,7 +403,7 @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, }; trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0, type); - ret = luring_do_submit(fd, &luringcb, s, offset, type); + ret = luring_do_submit(fd, &luringcb, s, offset, type, flags); if (ret < 0) { return ret; @@ -448,3 +458,12 @@ void luring_cleanup(LuringState *s) trace_luring_cleanup_state(s); g_free(s); } + +bool luring_has_fua(void) +{ +#ifdef HAVE_IO_URING_PREP_WRITEV2 + return true; +#else + return false; +#endif +} diff --git a/block/linux-aio.c b/block/linux-aio.c index 194c8f434f..407369f5c9 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -368,7 +368,8 @@ static void laio_deferred_fn(void *opaque) } static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, - int type, uint64_t dev_max_batch) + int type, BdrvRequestFlags flags, + uint64_t dev_max_batch) { LinuxAioState *s = laiocb->ctx; struct iocb *iocbs = &laiocb->iocb; @@ -376,7 +377,15 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, switch (type) { case QEMU_AIO_WRITE: +#ifdef HAVE_IO_PREP_PWRITEV2 + { + int laio_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0; + io_prep_pwritev2(iocbs, fd, qiov->iov, qiov->niov, offset, laio_flags); + } +#else + assert(flags == 0); io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); +#endif break; case QEMU_AIO_ZONE_APPEND: io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); @@ -409,7 +418,8 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, } int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, - int type, uint64_t dev_max_batch) + int type, BdrvRequestFlags flags, + uint64_t dev_max_batch) { int ret; AioContext *ctx = qemu_get_current_aio_context(); @@ -422,7 +432,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, .qiov = qiov, }; - ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch); + ret = laio_do_submit(fd, &laiocb, offset, type, flags, dev_max_batch); if (ret < 0) { return ret; } @@ -505,3 +515,12 @@ bool laio_has_fdsync(int fd) io_destroy(ctx); return (ret == -EINVAL) ? false : true; } + +bool laio_has_fua(void) +{ +#ifdef HAVE_IO_PREP_PWRITEV2 + return true; +#else + return false; +#endif +} diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 626706827f..6570244496 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -17,6 +17,7 @@ #define QEMU_RAW_AIO_H #include "block/aio.h" +#include "block/block-common.h" #include "qemu/iov.h" /* AIO request types */ @@ -58,11 +59,18 @@ void laio_cleanup(LinuxAioState *s); /* laio_co_submit: submit I/O requests in the thread's current AioContext. */ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, - int type, uint64_t dev_max_batch); + int type, BdrvRequestFlags flags, + uint64_t dev_max_batch); bool laio_has_fdsync(int); +bool laio_has_fua(void); void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); +#else +static inline bool laio_has_fua(void) +{ + return false; +} #endif /* io_uring.c - Linux io_uring implementation */ #ifdef CONFIG_LINUX_IO_URING @@ -71,9 +79,16 @@ void luring_cleanup(LuringState *s); /* luring_co_submit: submit I/O requests in the thread's current AioContext. */ int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, - QEMUIOVector *qiov, int type); + QEMUIOVector *qiov, int type, + BdrvRequestFlags flags); void luring_detach_aio_context(LuringState *s, AioContext *old_context); void luring_attach_aio_context(LuringState *s, AioContext *new_context); +bool luring_has_fua(void); +#else +static inline bool luring_has_fua(void) +{ + return false; +} #endif #ifdef _WIN32 diff --git a/meson.build b/meson.build index 9d9c11731f..2f43fd81bf 100644 --- a/meson.build +++ b/meson.build @@ -2727,6 +2727,14 @@ config_host_data.set('HAVE_OPTRESET', cc.has_header_symbol('getopt.h', 'optreset')) config_host_data.set('HAVE_IPPROTO_MPTCP', cc.has_header_symbol('netinet/in.h', 'IPPROTO_MPTCP')) +if libaio.found() + config_host_data.set('HAVE_IO_PREP_PWRITEV2', + cc.has_header_symbol('libaio.h', 'io_prep_pwritev2')) +endif +if linux_io_uring.found() + config_host_data.set('HAVE_IO_URING_PREP_WRITEV2', + cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2')) +endif # has_member config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', From 2f3b6e61f692bade441230dd25c1c0f101bd2eef Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Mar 2025 23:16:31 +0100 Subject: [PATCH 2660/2892] block/io: Ignore FUA with cache.no-flush=on For block drivers that don't advertise FUA support, we already call bdrv_co_flush(), which considers BDRV_O_NO_FLUSH. However, drivers that do support FUA still see the FUA flag with BDRV_O_NO_FLUSH and get the associated performance penalty that cache.no-flush=on was supposed to avoid. Clear FUA for write requests if BDRV_O_NO_FLUSH is set. Signed-off-by: Kevin Wolf Message-ID: <20250307221634.71951-3-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/io.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/io.c b/block/io.c index d369b994df..1ba8d1aeea 100644 --- a/block/io.c +++ b/block/io.c @@ -1058,6 +1058,10 @@ bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, return -ENOMEDIUM; } + if (bs->open_flags & BDRV_O_NO_FLUSH) { + flags &= ~BDRV_REQ_FUA; + } + if ((flags & BDRV_REQ_FUA) && (~bs->supported_write_flags & BDRV_REQ_FUA)) { flags &= ~BDRV_REQ_FUA; From 518db1013cb0384dc19134585f227dbb7bf65e39 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Mar 2025 23:16:32 +0100 Subject: [PATCH 2661/2892] aio: Create AioPolledEvent As a preparation for having multiple adaptive polling states per AioContext, move the 'ns' field into a separate struct. Signed-off-by: Kevin Wolf Message-ID: <20250307221634.71951-4-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- include/block/aio.h | 6 +++++- util/aio-posix.c | 31 ++++++++++++++++--------------- util/async.c | 3 ++- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/include/block/aio.h b/include/block/aio.h index b2ab3514de..c9fcfe7ccf 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -123,6 +123,10 @@ struct BHListSlice { typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; +typedef struct AioPolledEvent { + int64_t ns; /* current polling time in nanoseconds */ +} AioPolledEvent; + struct AioContext { GSource source; @@ -229,7 +233,7 @@ struct AioContext { int poll_disable_cnt; /* Polling mode parameters */ - int64_t poll_ns; /* current polling time in nanoseconds */ + AioPolledEvent poll; int64_t poll_max_ns; /* maximum polling time in nanoseconds */ int64_t poll_grow; /* polling time growth factor */ int64_t poll_shrink; /* polling time shrink factor */ diff --git a/util/aio-posix.c b/util/aio-posix.c index 06bf9f456c..95bddb9e4b 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -585,7 +585,7 @@ static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, return false; } - max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); + max_ns = qemu_soonest_timeout(*timeout, ctx->poll.ns); if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { /* * Enable poll mode. It pairs with the poll_set_started() in @@ -683,40 +683,40 @@ bool aio_poll(AioContext *ctx, bool blocking) if (ctx->poll_max_ns) { int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; - if (block_ns <= ctx->poll_ns) { + if (block_ns <= ctx->poll.ns) { /* This is the sweet spot, no adjustment needed */ } else if (block_ns > ctx->poll_max_ns) { /* We'd have to poll for too long, poll less */ - int64_t old = ctx->poll_ns; + int64_t old = ctx->poll.ns; if (ctx->poll_shrink) { - ctx->poll_ns /= ctx->poll_shrink; + ctx->poll.ns /= ctx->poll_shrink; } else { - ctx->poll_ns = 0; + ctx->poll.ns = 0; } - trace_poll_shrink(ctx, old, ctx->poll_ns); - } else if (ctx->poll_ns < ctx->poll_max_ns && + trace_poll_shrink(ctx, old, ctx->poll.ns); + } else if (ctx->poll.ns < ctx->poll_max_ns && block_ns < ctx->poll_max_ns) { /* There is room to grow, poll longer */ - int64_t old = ctx->poll_ns; + int64_t old = ctx->poll.ns; int64_t grow = ctx->poll_grow; if (grow == 0) { grow = 2; } - if (ctx->poll_ns) { - ctx->poll_ns *= grow; + if (ctx->poll.ns) { + ctx->poll.ns *= grow; } else { - ctx->poll_ns = 4000; /* start polling at 4 microseconds */ + ctx->poll.ns = 4000; /* start polling at 4 microseconds */ } - if (ctx->poll_ns > ctx->poll_max_ns) { - ctx->poll_ns = ctx->poll_max_ns; + if (ctx->poll.ns > ctx->poll_max_ns) { + ctx->poll.ns = ctx->poll_max_ns; } - trace_poll_grow(ctx, old, ctx->poll_ns); + trace_poll_grow(ctx, old, ctx->poll.ns); } } @@ -770,8 +770,9 @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, /* No thread synchronization here, it doesn't matter if an incorrect value * is used once. */ + ctx->poll.ns = 0; + ctx->poll_max_ns = max_ns; - ctx->poll_ns = 0; ctx->poll_grow = grow; ctx->poll_shrink = shrink; diff --git a/util/async.c b/util/async.c index 47e3d35a26..fc8a78aa79 100644 --- a/util/async.c +++ b/util/async.c @@ -609,7 +609,8 @@ AioContext *aio_context_new(Error **errp) qemu_rec_mutex_init(&ctx->lock); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); - ctx->poll_ns = 0; + ctx->poll.ns = 0; + ctx->poll_max_ns = 0; ctx->poll_grow = 0; ctx->poll_shrink = 0; From cf2e226fc654072acc185c5d7fb1ff77774f4563 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Mar 2025 23:16:33 +0100 Subject: [PATCH 2662/2892] aio-posix: Factor out adjust_polling_time() Signed-off-by: Kevin Wolf Message-ID: <20250307221634.71951-5-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- util/aio-posix.c | 77 ++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/util/aio-posix.c b/util/aio-posix.c index 95bddb9e4b..259827c7ad 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -600,6 +600,46 @@ static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, return false; } +static void adjust_polling_time(AioContext *ctx, AioPolledEvent *poll, + int64_t block_ns) +{ + if (block_ns <= poll->ns) { + /* This is the sweet spot, no adjustment needed */ + } else if (block_ns > ctx->poll_max_ns) { + /* We'd have to poll for too long, poll less */ + int64_t old = poll->ns; + + if (ctx->poll_shrink) { + poll->ns /= ctx->poll_shrink; + } else { + poll->ns = 0; + } + + trace_poll_shrink(ctx, old, poll->ns); + } else if (poll->ns < ctx->poll_max_ns && + block_ns < ctx->poll_max_ns) { + /* There is room to grow, poll longer */ + int64_t old = poll->ns; + int64_t grow = ctx->poll_grow; + + if (grow == 0) { + grow = 2; + } + + if (poll->ns) { + poll->ns *= grow; + } else { + poll->ns = 4000; /* start polling at 4 microseconds */ + } + + if (poll->ns > ctx->poll_max_ns) { + poll->ns = ctx->poll_max_ns; + } + + trace_poll_grow(ctx, old, poll->ns); + } +} + bool aio_poll(AioContext *ctx, bool blocking) { AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); @@ -682,42 +722,7 @@ bool aio_poll(AioContext *ctx, bool blocking) /* Adjust polling time */ if (ctx->poll_max_ns) { int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; - - if (block_ns <= ctx->poll.ns) { - /* This is the sweet spot, no adjustment needed */ - } else if (block_ns > ctx->poll_max_ns) { - /* We'd have to poll for too long, poll less */ - int64_t old = ctx->poll.ns; - - if (ctx->poll_shrink) { - ctx->poll.ns /= ctx->poll_shrink; - } else { - ctx->poll.ns = 0; - } - - trace_poll_shrink(ctx, old, ctx->poll.ns); - } else if (ctx->poll.ns < ctx->poll_max_ns && - block_ns < ctx->poll_max_ns) { - /* There is room to grow, poll longer */ - int64_t old = ctx->poll.ns; - int64_t grow = ctx->poll_grow; - - if (grow == 0) { - grow = 2; - } - - if (ctx->poll.ns) { - ctx->poll.ns *= grow; - } else { - ctx->poll.ns = 4000; /* start polling at 4 microseconds */ - } - - if (ctx->poll.ns > ctx->poll_max_ns) { - ctx->poll.ns = ctx->poll_max_ns; - } - - trace_poll_grow(ctx, old, ctx->poll.ns); - } + adjust_polling_time(ctx, &ctx->poll, block_ns); } progress |= aio_bh_poll(ctx); From ee416407b3c0f45253779e98404acb41231a9279 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Mar 2025 23:16:34 +0100 Subject: [PATCH 2663/2892] aio-posix: Separate AioPolledEvent per AioHandler Adaptive polling has a big problem: It doesn't consider that an event loop can wait for many different events that may have very different typical latencies. For example, think of a guest that tends to send a new I/O request soon after the previous I/O request completes, but the storage on the host is rather slow. In this case, getting the new request from guest quickly means that polling is enabled, but the next thing is performing the I/O request on the backend, which is slow and disables polling again for the next guest request. This means that in such a scenario, polling could help for every other event, but is only ever enabled when it can't succeed. In order to fix this, keep a separate AioPolledEvent for each AioHandler. We will then know that the backend file descriptor always has a high latency and isn't worth polling for, but we also know that the guest is always fast and we should poll for it. This solves at least half of the problem, we can now keep polling for those cases where it makes sense and get the improved performance from it. Since the event loop doesn't know which event will be next, we still do some unnecessary polling while we're waiting for the slow disk. I made some attempts to be more clever than just randomly growing and shrinking the polling time, and even to let callers be explicit about when they expect a new event, but so far this hasn't resulted in improved performance or even caused performance regressions. For now, let's just fix the part that is easy enough to fix, we can revisit the rest later. Signed-off-by: Kevin Wolf Message-ID: <20250307221634.71951-6-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- include/block/aio.h | 1 - util/aio-posix.c | 26 ++++++++++++++++++++++---- util/aio-posix.h | 1 + util/async.c | 2 -- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/include/block/aio.h b/include/block/aio.h index c9fcfe7ccf..99ff48420b 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -233,7 +233,6 @@ struct AioContext { int poll_disable_cnt; /* Polling mode parameters */ - AioPolledEvent poll; int64_t poll_max_ns; /* maximum polling time in nanoseconds */ int64_t poll_grow; /* polling time growth factor */ int64_t poll_shrink; /* polling time shrink factor */ diff --git a/util/aio-posix.c b/util/aio-posix.c index 259827c7ad..80785c29d2 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -579,13 +579,19 @@ static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, int64_t *timeout) { + AioHandler *node; int64_t max_ns; if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) { return false; } - max_ns = qemu_soonest_timeout(*timeout, ctx->poll.ns); + max_ns = 0; + QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { + max_ns = MAX(max_ns, node->poll.ns); + } + max_ns = qemu_soonest_timeout(*timeout, max_ns); + if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { /* * Enable poll mode. It pairs with the poll_set_started() in @@ -721,8 +727,14 @@ bool aio_poll(AioContext *ctx, bool blocking) /* Adjust polling time */ if (ctx->poll_max_ns) { + AioHandler *node; int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; - adjust_polling_time(ctx, &ctx->poll, block_ns); + + QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { + if (QLIST_IS_INSERTED(node, node_ready)) { + adjust_polling_time(ctx, &node->poll, block_ns); + } + } } progress |= aio_bh_poll(ctx); @@ -772,11 +784,17 @@ void aio_context_use_g_source(AioContext *ctx) void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, int64_t grow, int64_t shrink, Error **errp) { + AioHandler *node; + + qemu_lockcnt_inc(&ctx->list_lock); + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + node->poll.ns = 0; + } + qemu_lockcnt_dec(&ctx->list_lock); + /* No thread synchronization here, it doesn't matter if an incorrect value * is used once. */ - ctx->poll.ns = 0; - ctx->poll_max_ns = max_ns; ctx->poll_grow = grow; ctx->poll_shrink = shrink; diff --git a/util/aio-posix.h b/util/aio-posix.h index 4264c518be..82a0201ea4 100644 --- a/util/aio-posix.h +++ b/util/aio-posix.h @@ -38,6 +38,7 @@ struct AioHandler { #endif int64_t poll_idle_timeout; /* when to stop userspace polling */ bool poll_ready; /* has polling detected an event? */ + AioPolledEvent poll; }; /* Add a handler to a ready list */ diff --git a/util/async.c b/util/async.c index fc8a78aa79..863416dee9 100644 --- a/util/async.c +++ b/util/async.c @@ -609,8 +609,6 @@ AioContext *aio_context_new(Error **errp) qemu_rec_mutex_init(&ctx->lock); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); - ctx->poll.ns = 0; - ctx->poll_max_ns = 0; ctx->poll_grow = 0; ctx->poll_shrink = 0; From f76d3bee754a2f8d73373d5959dc983169a93eee Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 11 Mar 2025 15:19:12 +0100 Subject: [PATCH 2664/2892] aio-posix: Adjust polling time also for new handlers aio_dispatch_handler() adds handlers to ctx->poll_aio_handlers if polling should be enabled. If we call adjust_polling_time() for all polling handlers before this, new polling handlers are still left at poll->ns = 0 and polling is only actually enabled after the next event. Move the adjust_polling_time() call after aio_dispatch_handler(). This fixes test-nested-aio-poll, which expects that polling becomes effective the first time around. Signed-off-by: Kevin Wolf Message-ID: <20250311141912.135657-1-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- util/aio-posix.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/util/aio-posix.c b/util/aio-posix.c index 80785c29d2..2e0a5dadc4 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -28,6 +28,9 @@ /* Stop userspace polling on a handler if it isn't active for some time */ #define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) +static void adjust_polling_time(AioContext *ctx, AioPolledEvent *poll, + int64_t block_ns); + bool aio_poll_disabled(AioContext *ctx) { return qatomic_read(&ctx->poll_disable_cnt); @@ -392,7 +395,8 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) * scanning all handlers with aio_dispatch_handlers(). */ static bool aio_dispatch_ready_handlers(AioContext *ctx, - AioHandlerList *ready_list) + AioHandlerList *ready_list, + int64_t block_ns) { bool progress = false; AioHandler *node; @@ -400,6 +404,14 @@ static bool aio_dispatch_ready_handlers(AioContext *ctx, while ((node = QLIST_FIRST(ready_list))) { QLIST_REMOVE(node, node_ready); progress = aio_dispatch_handler(ctx, node) || progress; + + /* + * Adjust polling time only after aio_dispatch_handler(), which can + * add the handler to ctx->poll_aio_handlers. + */ + if (ctx->poll_max_ns && QLIST_IS_INSERTED(node, node_poll)) { + adjust_polling_time(ctx, &node->poll, block_ns); + } } return progress; @@ -653,6 +665,7 @@ bool aio_poll(AioContext *ctx, bool blocking) bool use_notify_me; int64_t timeout; int64_t start = 0; + int64_t block_ns = 0; /* * There cannot be two concurrent aio_poll calls for the same AioContext (or @@ -725,20 +738,13 @@ bool aio_poll(AioContext *ctx, bool blocking) aio_notify_accept(ctx); - /* Adjust polling time */ + /* Calculate blocked time for adaptive polling */ if (ctx->poll_max_ns) { - AioHandler *node; - int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; - - QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { - if (QLIST_IS_INSERTED(node, node_ready)) { - adjust_polling_time(ctx, &node->poll, block_ns); - } - } + block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; } progress |= aio_bh_poll(ctx); - progress |= aio_dispatch_ready_handlers(ctx, &ready_list); + progress |= aio_dispatch_ready_handlers(ctx, &ready_list, block_ns); aio_free_deleted_handlers(ctx); From 71e1369bad01d441113ede02334175647275652d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 24 Feb 2025 22:40:58 +0100 Subject: [PATCH 2665/2892] iotests: Limit qsd-migrate to working formats qsd-migrate is currently only working for raw, qcow2 and qed. Other formats are failing, e.g. because they don't support migration. Thus let's limit this test to the three usable formats now. Suggested-by: Kevin Wolf Signed-off-by: Thomas Huth Message-ID: <20250224214058.205889-1-thuth@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/qsd-migrate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/tests/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate index de17562cb0..a4c6592420 100755 --- a/tests/qemu-iotests/tests/qsd-migrate +++ b/tests/qemu-iotests/tests/qsd-migrate @@ -22,7 +22,7 @@ import iotests from iotests import filter_qemu_io, filter_qtest -iotests.script_initialize(supported_fmts=['generic'], +iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'], supported_protocols=['file'], supported_platforms=['linux']) From b2e3659d0d769c84f5b15239a93a722c8012bffa Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:04 +0800 Subject: [PATCH 2666/2892] scsi-disk: drop unused SCSIDiskState->bh field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 71544d30a6f8 ("scsi: push request restart to SCSIDevice") removed the only user of SCSIDiskState->bh. Signed-off-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-2-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/scsi-disk.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e7f738b484..caf6c1437f 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -106,7 +106,6 @@ struct SCSIDiskState { uint64_t max_unmap_size; uint64_t max_io_size; uint32_t quirks; - QEMUBH *bh; char *version; char *serial; char *vendor; From a89c3c9b2cc4107658c7260ecf329d869888fd51 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:05 +0800 Subject: [PATCH 2667/2892] dma: use current AioContext for dma_blk_io() In the past a single AioContext was used for block I/O and it was fetched using blk_get_aio_context(). Nowadays the block layer supports running I/O from any AioContext and multiple AioContexts at the same time. Remove the dma_blk_io() AioContext argument and use the current AioContext instead. This makes calling the function easier and enables multiple IOThreads to use dma_blk_io() concurrently for the same block device. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-3-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/ide/core.c | 3 +-- hw/ide/macio.c | 3 +-- hw/scsi/scsi-disk.c | 6 ++---- include/system/dma.h | 3 +-- system/dma-helpers.c | 8 ++++---- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index f9baba59e9..b14983ec54 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -968,8 +968,7 @@ static void ide_dma_cb(void *opaque, int ret) BDRV_SECTOR_SIZE, ide_dma_cb, s); break; case IDE_DMA_TRIM: - s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), - &s->sg, offset, BDRV_SECTOR_SIZE, + s->bus->dma->aiocb = dma_blk_io(&s->sg, offset, BDRV_SECTOR_SIZE, ide_issue_trim, s, ide_dma_cb, s, DMA_DIRECTION_TO_DEVICE); break; diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 5fe764b49b..c8e8e44cc9 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -187,8 +187,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: - s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), &s->sg, - offset, 0x1, ide_issue_trim, s, + s->bus->dma->aiocb = dma_blk_io(&s->sg, offset, 0x1, ide_issue_trim, s, pmac_ide_transfer_cb, io, DMA_DIRECTION_TO_DEVICE); break; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index caf6c1437f..f049a20275 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -487,8 +487,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret) if (r->req.sg) { dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), - r->req.sg, r->sector << BDRV_SECTOR_BITS, + r->req.aiocb = dma_blk_io(r->req.sg, r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, sdc->dma_readv, r, scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); @@ -650,8 +649,7 @@ static void scsi_write_data(SCSIRequest *req) if (r->req.sg) { dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE); r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), - r->req.sg, r->sector << BDRV_SECTOR_BITS, + r->req.aiocb = dma_blk_io(r->req.sg, r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, sdc->dma_writev, r, scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); diff --git a/include/system/dma.h b/include/system/dma.h index 5a49a30628..e142f7efa6 100644 --- a/include/system/dma.h +++ b/include/system/dma.h @@ -290,8 +290,7 @@ typedef BlockAIOCB *DMAIOFunc(int64_t offset, QEMUIOVector *iov, BlockCompletionFunc *cb, void *cb_opaque, void *opaque); -BlockAIOCB *dma_blk_io(AioContext *ctx, - QEMUSGList *sg, uint64_t offset, uint32_t align, +BlockAIOCB *dma_blk_io(QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, void *opaque, DMADirection dir); BlockAIOCB *dma_blk_read(BlockBackend *blk, diff --git a/system/dma-helpers.c b/system/dma-helpers.c index f6403242f5..6bad75876f 100644 --- a/system/dma-helpers.c +++ b/system/dma-helpers.c @@ -211,7 +211,7 @@ static const AIOCBInfo dma_aiocb_info = { .cancel_async = dma_aio_cancel, }; -BlockAIOCB *dma_blk_io(AioContext *ctx, +BlockAIOCB *dma_blk_io( QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, @@ -223,7 +223,7 @@ BlockAIOCB *dma_blk_io(AioContext *ctx, dbs->acb = NULL; dbs->sg = sg; - dbs->ctx = ctx; + dbs->ctx = qemu_get_current_aio_context(); dbs->offset = offset; dbs->align = align; dbs->sg_cur_index = 0; @@ -251,7 +251,7 @@ BlockAIOCB *dma_blk_read(BlockBackend *blk, QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + return dma_blk_io(sg, offset, align, dma_blk_read_io_func, blk, cb, opaque, DMA_DIRECTION_FROM_DEVICE); } @@ -269,7 +269,7 @@ BlockAIOCB *dma_blk_write(BlockBackend *blk, QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + return dma_blk_io(sg, offset, align, dma_blk_write_io_func, blk, cb, opaque, DMA_DIRECTION_TO_DEVICE); } From 7eecba37788f48d34c015954f1207cc7b52728f5 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:06 +0800 Subject: [PATCH 2668/2892] scsi: track per-SCSIRequest AioContext Until now, a SCSIDevice's I/O requests have run in a single AioContext. In order to support multiple IOThreads it will be necessary to move to the concept of a per-SCSIRequest AioContext. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-4-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/scsi-bus.c | 1 + hw/scsi/scsi-disk.c | 17 ++++++----------- include/hw/scsi/scsi.h | 1 + 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 7d4546800f..846bbbf0ec 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -868,6 +868,7 @@ invalid_opcode: } } + req->ctx = qemu_get_current_aio_context(); req->cmd = cmd; req->residual = req->cmd.xfer; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index f049a20275..7cf8c31b98 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -328,9 +328,8 @@ static void scsi_aio_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - /* The request must only run in the BlockBackend's AioContext */ - assert(blk_get_aio_context(s->qdev.conf.blk) == - qemu_get_current_aio_context()); + /* The request must run in its AioContext */ + assert(r->req.ctx == qemu_get_current_aio_context()); assert(r->req.aiocb != NULL); r->req.aiocb = NULL; @@ -430,12 +429,10 @@ static void scsi_dma_complete(void *opaque, int ret) static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; - /* The request must only run in the BlockBackend's AioContext */ - assert(blk_get_aio_context(s->qdev.conf.blk) == - qemu_get_current_aio_context()); + /* The request must run in its AioContext */ + assert(r->req.ctx == qemu_get_current_aio_context()); assert(r->req.aiocb == NULL); if (scsi_disk_req_check_error(r, ret, ret > 0)) { @@ -562,12 +559,10 @@ static void scsi_read_data(SCSIRequest *req) static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; - /* The request must only run in the BlockBackend's AioContext */ - assert(blk_get_aio_context(s->qdev.conf.blk) == - qemu_get_current_aio_context()); + /* The request must run in its AioContext */ + assert(r->req.ctx == qemu_get_current_aio_context()); assert (r->req.aiocb == NULL); if (scsi_disk_req_check_error(r, ret, ret > 0)) { diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index c3d5e17e38..ffc48203f9 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -24,6 +24,7 @@ struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; const SCSIReqOps *ops; + AioContext *ctx; uint32_t refcount; uint32_t tag; uint32_t lun; From 1cf18cc9bf5e9f88ad92f89886652e0361e2f41f Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:07 +0800 Subject: [PATCH 2669/2892] scsi: introduce requests_lock SCSIDevice keeps track of in-flight requests for device reset and Task Management Functions (TMFs). The request list requires protection so that multi-threaded SCSI emulation can be implemented in commits that follow. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-5-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/scsi-bus.c | 120 +++++++++++++++++++++++++++++------------ include/hw/scsi/scsi.h | 7 ++- 2 files changed, 88 insertions(+), 39 deletions(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 846bbbf0ec..ece1107ee8 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -100,8 +100,15 @@ static void scsi_device_for_each_req_sync(SCSIDevice *s, assert(!runstate_is_running()); assert(qemu_in_main_thread()); - QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) { - fn(req, opaque); + /* + * Locking is not necessary because the guest is stopped and no other + * threads can be accessing the requests list, but take the lock for + * consistency. + */ + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) { + fn(req, opaque); + } } } @@ -115,21 +122,29 @@ static void scsi_device_for_each_req_async_bh(void *opaque) { g_autofree SCSIDeviceForEachReqAsyncData *data = opaque; SCSIDevice *s = data->s; - AioContext *ctx; - SCSIRequest *req; - SCSIRequest *next; + g_autoptr(GList) reqs = NULL; /* - * The BB cannot have changed contexts between this BH being scheduled and - * now: BBs' AioContexts, when they have a node attached, can only be - * changed via bdrv_try_change_aio_context(), in a drained section. While - * we have the in-flight counter incremented, that drain must block. + * Build a list of requests in this AioContext so fn() can be invoked later + * outside requests_lock. */ - ctx = blk_get_aio_context(s->conf.blk); - assert(ctx == qemu_get_current_aio_context()); + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + AioContext *ctx = qemu_get_current_aio_context(); + SCSIRequest *req; + SCSIRequest *next; - QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { - data->fn(req, data->fn_opaque); + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { + if (req->ctx == ctx) { + scsi_req_ref(req); /* dropped after calling fn() */ + reqs = g_list_prepend(reqs, req); + } + } + } + + /* Call fn() on each request */ + for (GList *elem = g_list_first(reqs); elem; elem = g_list_next(elem)) { + data->fn(elem->data, data->fn_opaque); + scsi_req_unref(elem->data); } /* Drop the reference taken by scsi_device_for_each_req_async() */ @@ -139,9 +154,35 @@ static void scsi_device_for_each_req_async_bh(void *opaque) blk_dec_in_flight(s->conf.blk); } +static void scsi_device_for_each_req_async_do_ctx(gpointer key, gpointer value, + gpointer user_data) +{ + AioContext *ctx = key; + SCSIDeviceForEachReqAsyncData *params = user_data; + SCSIDeviceForEachReqAsyncData *data; + + data = g_new(SCSIDeviceForEachReqAsyncData, 1); + data->s = params->s; + data->fn = params->fn; + data->fn_opaque = params->fn_opaque; + + /* + * Hold a reference to the SCSIDevice until + * scsi_device_for_each_req_async_bh() finishes. + */ + object_ref(OBJECT(data->s)); + + /* Paired with scsi_device_for_each_req_async_bh() */ + blk_inc_in_flight(data->s->conf.blk); + + aio_bh_schedule_oneshot(ctx, scsi_device_for_each_req_async_bh, data); +} + /* * Schedule @fn() to be invoked for each enqueued request in device @s. @fn() - * runs in the AioContext that is executing the request. + * must be thread-safe because it runs concurrently in each AioContext that is + * executing a request. + * * Keeps the BlockBackend's in-flight counter incremented until everything is * done, so draining it will settle all scheduled @fn() calls. */ @@ -151,24 +192,26 @@ static void scsi_device_for_each_req_async(SCSIDevice *s, { assert(qemu_in_main_thread()); - SCSIDeviceForEachReqAsyncData *data = - g_new(SCSIDeviceForEachReqAsyncData, 1); + /* The set of AioContexts where the requests are being processed */ + g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL); + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + SCSIRequest *req; + QTAILQ_FOREACH(req, &s->requests, next) { + g_hash_table_add(aio_contexts, req->ctx); + } + } - data->s = s; - data->fn = fn; - data->fn_opaque = opaque; - - /* - * Hold a reference to the SCSIDevice until - * scsi_device_for_each_req_async_bh() finishes. - */ - object_ref(OBJECT(s)); - - /* Paired with blk_dec_in_flight() in scsi_device_for_each_req_async_bh() */ - blk_inc_in_flight(s->conf.blk); - aio_bh_schedule_oneshot(blk_get_aio_context(s->conf.blk), - scsi_device_for_each_req_async_bh, - data); + /* Schedule a BH for each AioContext */ + SCSIDeviceForEachReqAsyncData params = { + .s = s, + .fn = fn, + .fn_opaque = opaque, + }; + g_hash_table_foreach( + aio_contexts, + scsi_device_for_each_req_async_do_ctx, + ¶ms + ); } static void scsi_device_realize(SCSIDevice *s, Error **errp) @@ -349,6 +392,7 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp) dev->lun = lun; } + qemu_mutex_init(&dev->requests_lock); QTAILQ_INIT(&dev->requests); scsi_device_realize(dev, &local_err); if (local_err) { @@ -369,6 +413,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev) scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE)); + qemu_mutex_destroy(&dev->requests_lock); + scsi_device_unrealize(dev); blockdev_mark_auto_del(dev->conf.blk); @@ -965,7 +1011,10 @@ static void scsi_req_enqueue_internal(SCSIRequest *req) req->sg = NULL; } req->enqueued = true; - QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + + WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) { + QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + } } int32_t scsi_req_enqueue(SCSIRequest *req) @@ -985,7 +1034,9 @@ static void scsi_req_dequeue(SCSIRequest *req) trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); req->retry = false; if (req->enqueued) { - QTAILQ_REMOVE(&req->dev->requests, req, next); + WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) { + QTAILQ_REMOVE(&req->dev->requests, req, next); + } req->enqueued = false; scsi_req_unref(req); } @@ -1962,8 +2013,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) static void scsi_dev_instance_init(Object *obj) { - DeviceState *dev = DEVICE(obj); - SCSIDevice *s = SCSI_DEVICE(dev); + SCSIDevice *s = SCSI_DEVICE(obj); device_add_bootindex_property(obj, &s->conf.bootindex, "bootindex", NULL, diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index ffc48203f9..90ee192b4d 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -49,6 +49,8 @@ struct SCSIRequest { bool dma_started; BlockAIOCB *aiocb; QEMUSGList *sg; + + /* Protected by SCSIDevice->requests_lock */ QTAILQ_ENTRY(SCSIRequest) next; }; @@ -77,10 +79,7 @@ struct SCSIDevice uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; - /* - * The requests list is only accessed from the AioContext that executes - * requests or from the main loop when IOThread processing is stopped. - */ + QemuMutex requests_lock; /* protects the requests list */ QTAILQ_HEAD(, SCSIRequest) requests; uint32_t channel; From b348ca2e043c0f7c9ecc1bbbd7dd87db47887e9f Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:08 +0800 Subject: [PATCH 2670/2892] virtio-scsi: introduce event and ctrl virtqueue locks Virtqueues are not thread-safe. Until now this was not a major issue since all virtqueue processing happened in the same thread. The ctrl queue's Task Management Function (TMF) requests sometimes need the main loop, so a BH was used to schedule the virtqueue completion back in the thread that has virtqueue access. When IOThread Virtqueue Mapping is introduced in later commits, event and ctrl virtqueue accesses from other threads will become necessary. Introduce an optional per-virtqueue lock so the event and ctrl virtqueues can be protected in the commits that follow. The addition of the ctrl virtqueue lock makes virtio_scsi_complete_req_from_main_loop() and its BH unnecessary. Instead, take the ctrl virtqueue lock from the main loop thread. The cmd virtqueue does not have a lock because the entirety of SCSI command processing happens in one thread. Only one thread accesses the cmd virtqueue and a lock is unnecessary. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-6-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/virtio-scsi.c | 90 ++++++++++++++++++--------------- include/hw/virtio/virtio-scsi.h | 3 ++ 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 7d094e1881..073ccd3d5b 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -102,13 +102,18 @@ static void virtio_scsi_free_req(VirtIOSCSIReq *req) g_free(req); } -static void virtio_scsi_complete_req(VirtIOSCSIReq *req) +static void virtio_scsi_complete_req(VirtIOSCSIReq *req, QemuMutex *vq_lock) { VirtIOSCSI *s = req->dev; VirtQueue *vq = req->vq; VirtIODevice *vdev = VIRTIO_DEVICE(s); qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); + + if (vq_lock) { + qemu_mutex_lock(vq_lock); + } + virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); if (s->dataplane_started && !s->dataplane_fenced) { virtio_notify_irqfd(vdev, vq); @@ -116,6 +121,10 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) virtio_notify(vdev, vq); } + if (vq_lock) { + qemu_mutex_unlock(vq_lock); + } + if (req->sreq) { req->sreq->hba_private = NULL; scsi_req_unref(req->sreq); @@ -123,34 +132,20 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) virtio_scsi_free_req(req); } -static void virtio_scsi_complete_req_bh(void *opaque) -{ - VirtIOSCSIReq *req = opaque; - - virtio_scsi_complete_req(req); -} - -/* - * Called from virtio_scsi_do_one_tmf_bh() in main loop thread. The main loop - * thread cannot touch the virtqueue since that could race with an IOThread. - */ -static void virtio_scsi_complete_req_from_main_loop(VirtIOSCSIReq *req) -{ - VirtIOSCSI *s = req->dev; - - if (!s->ctx || s->ctx == qemu_get_aio_context()) { - /* No need to schedule a BH when there is no IOThread */ - virtio_scsi_complete_req(req); - } else { - /* Run request completion in the IOThread */ - aio_wait_bh_oneshot(s->ctx, virtio_scsi_complete_req_bh, req); - } -} - -static void virtio_scsi_bad_req(VirtIOSCSIReq *req) +static void virtio_scsi_bad_req(VirtIOSCSIReq *req, QemuMutex *vq_lock) { virtio_error(VIRTIO_DEVICE(req->dev), "wrong size for virtio-scsi headers"); + + if (vq_lock) { + qemu_mutex_lock(vq_lock); + } + virtqueue_detach_element(req->vq, &req->elem, 0); + + if (vq_lock) { + qemu_mutex_unlock(vq_lock); + } + virtio_scsi_free_req(req); } @@ -235,12 +230,21 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req, return 0; } -static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) +static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq, QemuMutex *vq_lock) { VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; VirtIOSCSIReq *req; + if (vq_lock) { + qemu_mutex_lock(vq_lock); + } + req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size); + + if (vq_lock) { + qemu_mutex_unlock(vq_lock); + } + if (!req) { return NULL; } @@ -305,7 +309,7 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) trace_virtio_scsi_tmf_resp(virtio_scsi_get_lun(req->req.tmf.lun), req->req.tmf.tag, req->resp.tmf.response); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, &req->dev->ctrl_lock); } g_free(n); } @@ -361,7 +365,7 @@ static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) out: object_unref(OBJECT(d)); - virtio_scsi_complete_req_from_main_loop(req); + virtio_scsi_complete_req(req, &s->ctrl_lock); } /* Some TMFs must be processed from the main loop thread */ @@ -408,7 +412,7 @@ static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) /* SAM-6 6.3.2 Hard reset */ req->resp.tmf.response = VIRTIO_SCSI_S_TARGET_FAILURE; - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, &req->dev->ctrl_lock); } } @@ -562,7 +566,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, &type, sizeof(type)) < sizeof(type)) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->ctrl_lock); return; } @@ -570,7 +574,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) if (type == VIRTIO_SCSI_T_TMF) { if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), sizeof(VirtIOSCSICtrlTMFResp)) < 0) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->ctrl_lock); return; } else { r = virtio_scsi_do_tmf(s, req); @@ -580,7 +584,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq), sizeof(VirtIOSCSICtrlANResp)) < 0) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->ctrl_lock); return; } else { req->req.an.event_requested = @@ -600,7 +604,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) type == VIRTIO_SCSI_T_AN_SUBSCRIBE) trace_virtio_scsi_an_resp(virtio_scsi_get_lun(req->req.an.lun), req->resp.an.response); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, &s->ctrl_lock); } else { assert(r == -EINPROGRESS); } @@ -610,7 +614,7 @@ static void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) { VirtIOSCSIReq *req; - while ((req = virtio_scsi_pop_req(s, vq))) { + while ((req = virtio_scsi_pop_req(s, vq, &s->ctrl_lock))) { virtio_scsi_handle_ctrl_req(s, req); } } @@ -654,7 +658,7 @@ static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) * in virtio_scsi_command_complete. */ req->resp_size = sizeof(VirtIOSCSICmdResp); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, NULL); } static void virtio_scsi_command_failed(SCSIRequest *r) @@ -788,7 +792,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) virtio_scsi_fail_cmd_req(req); return -ENOTSUP; } else { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, NULL); return -EINVAL; } } @@ -843,7 +847,7 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) virtio_queue_set_notification(vq, 0); } - while ((req = virtio_scsi_pop_req(s, vq))) { + while ((req = virtio_scsi_pop_req(s, vq, NULL))) { ret = virtio_scsi_handle_cmd_req_prepare(s, req); if (!ret) { QTAILQ_INSERT_TAIL(&reqs, req, next); @@ -973,7 +977,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, return; } - req = virtio_scsi_pop_req(s, vs->event_vq); + req = virtio_scsi_pop_req(s, vs->event_vq, &s->event_lock); if (!req) { s->events_dropped = true; return; @@ -985,7 +989,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, } if (virtio_scsi_parse_req(req, 0, sizeof(VirtIOSCSIEvent))) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->event_lock); return; } @@ -1005,7 +1009,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, } trace_virtio_scsi_event(virtio_scsi_get_lun(evt->lun), event, reason); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, &s->event_lock); } static void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) @@ -1236,6 +1240,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) Error *err = NULL; QTAILQ_INIT(&s->tmf_bh_list); + qemu_mutex_init(&s->ctrl_lock); + qemu_mutex_init(&s->event_lock); qemu_mutex_init(&s->tmf_bh_lock); virtio_scsi_common_realize(dev, @@ -1280,6 +1286,8 @@ static void virtio_scsi_device_unrealize(DeviceState *dev) qbus_set_hotplug_handler(BUS(&s->bus), NULL); virtio_scsi_common_unrealize(dev); qemu_mutex_destroy(&s->tmf_bh_lock); + qemu_mutex_destroy(&s->event_lock); + qemu_mutex_destroy(&s->ctrl_lock); } static const Property virtio_scsi_properties[] = { diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index be230cd4bf..4ee98ebf63 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -84,6 +84,9 @@ struct VirtIOSCSI { int resetting; /* written from main loop thread, read from any thread */ bool events_dropped; + QemuMutex ctrl_lock; /* protects ctrl_vq */ + QemuMutex event_lock; /* protects event_vq */ + /* * TMFs deferred to main loop BH. These fields are protected by * tmf_bh_lock. From 7d8ab5b2f77d84a21dbeb5b254e26320e1943af4 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:09 +0800 Subject: [PATCH 2671/2892] virtio-scsi: protect events_dropped field The block layer can invoke the resize callback from any AioContext that is processing requests. The virtqueue is already protected but the events_dropped field also needs to be protected against races. Cover it using the event virtqueue lock because it is closely associated with accesses to the virtqueue. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-7-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/virtio-scsi.c | 29 ++++++++++++++++++++--------- include/hw/virtio/virtio-scsi.h | 3 ++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 073ccd3d5b..2d796a861b 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -948,7 +948,10 @@ static void virtio_scsi_reset(VirtIODevice *vdev) vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - s->events_dropped = false; + + WITH_QEMU_LOCK_GUARD(&s->event_lock) { + s->events_dropped = false; + } } typedef struct { @@ -978,14 +981,16 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, } req = virtio_scsi_pop_req(s, vs->event_vq, &s->event_lock); - if (!req) { - s->events_dropped = true; - return; - } + WITH_QEMU_LOCK_GUARD(&s->event_lock) { + if (!req) { + s->events_dropped = true; + return; + } - if (s->events_dropped) { - event |= VIRTIO_SCSI_T_EVENTS_MISSED; - s->events_dropped = false; + if (s->events_dropped) { + event |= VIRTIO_SCSI_T_EVENTS_MISSED; + s->events_dropped = false; + } } if (virtio_scsi_parse_req(req, 0, sizeof(VirtIOSCSIEvent))) { @@ -1014,7 +1019,13 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, static void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) { - if (s->events_dropped) { + bool events_dropped; + + WITH_QEMU_LOCK_GUARD(&s->event_lock) { + events_dropped = s->events_dropped; + } + + if (events_dropped) { VirtIOSCSIEventInfo info = { .event = VIRTIO_SCSI_T_NO_EVENT, }; diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 4ee98ebf63..7b7e3ced7a 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -82,10 +82,11 @@ struct VirtIOSCSI { SCSIBus bus; int resetting; /* written from main loop thread, read from any thread */ + + QemuMutex event_lock; /* protects event_vq and events_dropped */ bool events_dropped; QemuMutex ctrl_lock; /* protects ctrl_vq */ - QemuMutex event_lock; /* protects event_vq */ /* * TMFs deferred to main loop BH. These fields are protected by From da6eebb33b08131d2dc7c2594f0998012fe69e2f Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:10 +0800 Subject: [PATCH 2672/2892] virtio-scsi: perform TMFs in appropriate AioContexts With IOThread Virtqueue Mapping there will be multiple AioContexts processing SCSI requests. scsi_req_cancel() and other SCSI request operations must be performed from the AioContext where the request is running. Introduce a virtio_scsi_defer_tmf_to_aio_context() function and the necessary VirtIOSCSIReq->remaining refcount infrastructure to move the TMF code into the AioContext where the request is running. For the time being there is still just one AioContext: the main loop or the IOThread. When the iothread-vq-mapping parameter is added in a later patch this will be changed to per-virtqueue AioContexts. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-8-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/virtio-scsi.c | 266 ++++++++++++++++++++++++++++++++---------- 1 file changed, 204 insertions(+), 62 deletions(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 2d796a861b..2045d27289 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -47,7 +47,7 @@ typedef struct VirtIOSCSIReq { /* Used for two-stage request submission and TMFs deferred to BH */ QTAILQ_ENTRY(VirtIOSCSIReq) next; - /* Used for cancellation of request during TMFs */ + /* Used for cancellation of request during TMFs. Atomic. */ int remaining; SCSIRequest *sreq; @@ -298,19 +298,23 @@ typedef struct { VirtIOSCSIReq *tmf_req; } VirtIOSCSICancelNotifier; +static void virtio_scsi_tmf_dec_remaining(VirtIOSCSIReq *tmf) +{ + if (qatomic_fetch_dec(&tmf->remaining) == 1) { + trace_virtio_scsi_tmf_resp(virtio_scsi_get_lun(tmf->req.tmf.lun), + tmf->req.tmf.tag, tmf->resp.tmf.response); + + virtio_scsi_complete_req(tmf, &tmf->dev->ctrl_lock); + } +} + static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) { VirtIOSCSICancelNotifier *n = container_of(notifier, VirtIOSCSICancelNotifier, notifier); - if (--n->tmf_req->remaining == 0) { - VirtIOSCSIReq *req = n->tmf_req; - - trace_virtio_scsi_tmf_resp(virtio_scsi_get_lun(req->req.tmf.lun), - req->req.tmf.tag, req->resp.tmf.response); - virtio_scsi_complete_req(req, &req->dev->ctrl_lock); - } + virtio_scsi_tmf_dec_remaining(n->tmf_req); g_free(n); } @@ -416,7 +420,7 @@ static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) } } -static void virtio_scsi_defer_tmf_to_bh(VirtIOSCSIReq *req) +static void virtio_scsi_defer_tmf_to_main_loop(VirtIOSCSIReq *req) { VirtIOSCSI *s = req->dev; @@ -430,6 +434,137 @@ static void virtio_scsi_defer_tmf_to_bh(VirtIOSCSIReq *req) } } +static void virtio_scsi_tmf_cancel_req(VirtIOSCSIReq *tmf, SCSIRequest *r) +{ + VirtIOSCSICancelNotifier *notifier; + + assert(r->ctx == qemu_get_current_aio_context()); + + /* Decremented in virtio_scsi_cancel_notify() */ + qatomic_inc(&tmf->remaining); + + notifier = g_new(VirtIOSCSICancelNotifier, 1); + notifier->notifier.notify = virtio_scsi_cancel_notify; + notifier->tmf_req = tmf; + scsi_req_cancel_async(r, ¬ifier->notifier); +} + +/* Execute a TMF on the requests in the current AioContext */ +static void virtio_scsi_do_tmf_aio_context(void *opaque) +{ + AioContext *ctx = qemu_get_current_aio_context(); + VirtIOSCSIReq *tmf = opaque; + VirtIOSCSI *s = tmf->dev; + SCSIDevice *d = virtio_scsi_device_get(s, tmf->req.tmf.lun); + SCSIRequest *r; + bool match_tag; + + if (!d) { + tmf->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; + virtio_scsi_tmf_dec_remaining(tmf); + return; + } + + /* + * This function could handle other subtypes that need to be processed in + * the request's AioContext in the future, but for now only request + * cancelation subtypes are performed here. + */ + switch (tmf->req.tmf.subtype) { + case VIRTIO_SCSI_T_TMF_ABORT_TASK: + match_tag = true; + break; + case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: + case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: + match_tag = false; + break; + default: + g_assert_not_reached(); + } + + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + QTAILQ_FOREACH(r, &d->requests, next) { + VirtIOSCSIReq *cmd_req = r->hba_private; + assert(cmd_req); /* request has hba_private while enqueued */ + + if (r->ctx != ctx) { + continue; + } + if (match_tag && cmd_req->req.cmd.tag != tmf->req.tmf.tag) { + continue; + } + virtio_scsi_tmf_cancel_req(tmf, r); + } + } + + /* Incremented by virtio_scsi_do_tmf() */ + virtio_scsi_tmf_dec_remaining(tmf); + + object_unref(d); +} + +static void dummy_bh(void *opaque) +{ + /* Do nothing */ +} + +/* + * Wait for pending virtio_scsi_defer_tmf_to_aio_context() BHs. + */ +static void virtio_scsi_flush_defer_tmf_to_aio_context(VirtIOSCSI *s) +{ + GLOBAL_STATE_CODE(); + + assert(!s->dataplane_started); + + if (s->ctx) { + /* Our BH only runs after previously scheduled BHs */ + aio_wait_bh_oneshot(s->ctx, dummy_bh, NULL); + } +} + +/* + * Run the TMF in a specific AioContext, handling only requests in that + * AioContext. This is necessary because requests can run in different + * AioContext and it is only possible to cancel them from the AioContext where + * they are running. + */ +static void virtio_scsi_defer_tmf_to_aio_context(VirtIOSCSIReq *tmf, + AioContext *ctx) +{ + /* Decremented in virtio_scsi_do_tmf_aio_context() */ + qatomic_inc(&tmf->remaining); + + /* See virtio_scsi_flush_defer_tmf_to_aio_context() cleanup during reset */ + aio_bh_schedule_oneshot(ctx, virtio_scsi_do_tmf_aio_context, tmf); +} + +/* + * Returns the AioContext for a given TMF's tag field or NULL. Note that the + * request identified by the tag may have completed by the time you can execute + * a BH in the AioContext, so don't assume the request still exists in your BH. + */ +static AioContext *find_aio_context_for_tmf_tag(SCSIDevice *d, + VirtIOSCSIReq *tmf) +{ + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + SCSIRequest *r; + SCSIRequest *next; + + QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { + VirtIOSCSIReq *cmd_req = r->hba_private; + + /* hba_private is non-NULL while the request is enqueued */ + assert(cmd_req); + + if (cmd_req->req.cmd.tag == tmf->req.tmf.tag) { + return r->ctx; + } + } + } + return NULL; +} + /* Return 0 if the request is ready to be completed and return to guest; * -EINPROGRESS if the request is submitted and will be completed later, in the * case of async cancellation. */ @@ -437,6 +572,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) { SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); SCSIRequest *r, *next; + AioContext *ctx; int ret = 0; virtio_scsi_ctx_check(s, d); @@ -454,7 +590,22 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) req->req.tmf.tag, req->req.tmf.subtype); switch (req->req.tmf.subtype) { - case VIRTIO_SCSI_T_TMF_ABORT_TASK: + case VIRTIO_SCSI_T_TMF_ABORT_TASK: { + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + goto incorrect_lun; + } + + ctx = find_aio_context_for_tmf_tag(d, req); + if (ctx) { + virtio_scsi_defer_tmf_to_aio_context(req, ctx); + ret = -EINPROGRESS; + } + break; + } + case VIRTIO_SCSI_T_TMF_QUERY_TASK: if (!d) { goto fail; @@ -462,44 +613,49 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - VirtIOSCSIReq *cmd_req = r->hba_private; - if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) { - break; - } - } - if (r) { - /* - * Assert that the request has not been completed yet, we - * check for it in the loop above. - */ - assert(r->hba_private); - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { - /* "If the specified command is present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - } else { - VirtIOSCSICancelNotifier *notifier; - req->remaining = 1; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->tmf_req = req; - notifier->notifier.notify = virtio_scsi_cancel_notify; - scsi_req_cancel_async(r, ¬ifier->notifier); - ret = -EINPROGRESS; + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + QTAILQ_FOREACH(r, &d->requests, next) { + VirtIOSCSIReq *cmd_req = r->hba_private; + assert(cmd_req); /* request has hba_private while enqueued */ + + if (cmd_req->req.cmd.tag == req->req.tmf.tag) { + /* + * "If the specified command is present in the task set, + * then return a service response set to FUNCTION + * SUCCEEDED". + */ + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + } } } break; case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - virtio_scsi_defer_tmf_to_bh(req); + virtio_scsi_defer_tmf_to_main_loop(req); ret = -EINPROGRESS; break; case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: - case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: + case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: { + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + goto incorrect_lun; + } + + qatomic_inc(&req->remaining); + + ctx = s->ctx ?: qemu_get_aio_context(); + virtio_scsi_defer_tmf_to_aio_context(req, ctx); + + virtio_scsi_tmf_dec_remaining(req); + ret = -EINPROGRESS; + break; + } + case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: if (!d) { goto fail; @@ -508,34 +664,19 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) goto incorrect_lun; } - /* Add 1 to "remaining" until virtio_scsi_do_tmf returns. - * This way, if the bus starts calling back to the notifiers - * even before we finish the loop, virtio_scsi_cancel_notify - * will not complete the TMF too early. - */ - req->remaining = 1; - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - if (r->hba_private) { - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { - /* "If there is any command present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - break; - } else { - VirtIOSCSICancelNotifier *notifier; + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { + /* Request has hba_private while enqueued */ + assert(r->hba_private); - req->remaining++; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->notifier.notify = virtio_scsi_cancel_notify; - notifier->tmf_req = req; - scsi_req_cancel_async(r, ¬ifier->notifier); - } + /* + * "If there is any command present in the task set, then + * return a service response set to FUNCTION SUCCEEDED". + */ + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + break; } } - if (--req->remaining > 0) { - ret = -EINPROGRESS; - } break; case VIRTIO_SCSI_T_TMF_CLEAR_ACA: @@ -941,6 +1082,7 @@ static void virtio_scsi_reset(VirtIODevice *vdev) assert(!s->dataplane_started); virtio_scsi_reset_tmf_bh(s); + virtio_scsi_flush_defer_tmf_to_aio_context(s); qatomic_inc(&s->resetting); bus_cold_reset(BUS(&s->bus)); From 366b5811d6170f4ed74329a60dc77b1633e13798 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:11 +0800 Subject: [PATCH 2673/2892] virtio-blk: extract cleanup_iothread_vq_mapping() function This is the cleanup function that must be called after apply_iothread_vq_mapping() succeeds. virtio-scsi will need this function too, so extract it. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-9-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/block/virtio-blk.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 5135b4d8f1..21b1b768ed 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1495,6 +1495,9 @@ validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list, * Fill in the AioContext for each virtqueue in the @vq_aio_context array given * the iothread-vq-mapping parameter in @iothread_vq_mapping_list. * + * cleanup_iothread_vq_mapping() must be called to free IOThread object + * references after this function returns success. + * * Returns: %true on success, %false on failure. **/ static bool apply_iothread_vq_mapping( @@ -1545,6 +1548,23 @@ static bool apply_iothread_vq_mapping( return true; } +/** + * cleanup_iothread_vq_mapping: + * @list: The mapping of virtqueues to IOThreads. + * + * Release IOThread object references that were acquired by + * apply_iothread_vq_mapping(). + */ +static void cleanup_iothread_vq_mapping(IOThreadVirtQueueMappingList *list) +{ + IOThreadVirtQueueMappingList *node; + + for (node = list; node; node = node->next) { + IOThread *iothread = iothread_by_id(node->value->iothread); + object_unref(OBJECT(iothread)); + } +} + /* Context: BQL held */ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp) { @@ -1611,12 +1631,7 @@ static void virtio_blk_vq_aio_context_cleanup(VirtIOBlock *s) assert(!s->ioeventfd_started); if (conf->iothread_vq_mapping_list) { - IOThreadVirtQueueMappingList *node; - - for (node = conf->iothread_vq_mapping_list; node; node = node->next) { - IOThread *iothread = iothread_by_id(node->value->iothread); - object_unref(OBJECT(iothread)); - } + cleanup_iothread_vq_mapping(conf->iothread_vq_mapping_list); } if (conf->iothread) { From 2fa67a7b1d7957dc0cc482136ba58c460463ecb6 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:12 +0800 Subject: [PATCH 2674/2892] virtio-blk: tidy up iothread_vq_mapping functions Use noun_verb() function naming instead of verb_noun() because the former is the most common naming style for APIs. The next commit will move these functions into a header file so that virtio-scsi can call them. Shorten iothread_vq_mapping_apply()'s iothread_vq_mapping_list argument to just "list" like in the other functions. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-10-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/block/virtio-blk.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 21b1b768ed..6bf7b50520 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1424,8 +1424,8 @@ static const BlockDevOps virtio_block_ops = { }; static bool -validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list, - uint16_t num_queues, Error **errp) +iothread_vq_mapping_validate(IOThreadVirtQueueMappingList *list, uint16_t + num_queues, Error **errp) { g_autofree unsigned long *vqs = bitmap_new(num_queues); g_autoptr(GHashTable) iothreads = @@ -1486,22 +1486,22 @@ validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list, } /** - * apply_iothread_vq_mapping: - * @iothread_vq_mapping_list: The mapping of virtqueues to IOThreads. + * iothread_vq_mapping_apply: + * @list: The mapping of virtqueues to IOThreads. * @vq_aio_context: The array of AioContext pointers to fill in. * @num_queues: The length of @vq_aio_context. * @errp: If an error occurs, a pointer to the area to store the error. * * Fill in the AioContext for each virtqueue in the @vq_aio_context array given - * the iothread-vq-mapping parameter in @iothread_vq_mapping_list. + * the iothread-vq-mapping parameter in @list. * - * cleanup_iothread_vq_mapping() must be called to free IOThread object + * iothread_vq_mapping_cleanup() must be called to free IOThread object * references after this function returns success. * * Returns: %true on success, %false on failure. **/ -static bool apply_iothread_vq_mapping( - IOThreadVirtQueueMappingList *iothread_vq_mapping_list, +static bool iothread_vq_mapping_apply( + IOThreadVirtQueueMappingList *list, AioContext **vq_aio_context, uint16_t num_queues, Error **errp) @@ -1510,16 +1510,15 @@ static bool apply_iothread_vq_mapping( size_t num_iothreads = 0; size_t cur_iothread = 0; - if (!validate_iothread_vq_mapping_list(iothread_vq_mapping_list, - num_queues, errp)) { + if (!iothread_vq_mapping_validate(list, num_queues, errp)) { return false; } - for (node = iothread_vq_mapping_list; node; node = node->next) { + for (node = list; node; node = node->next) { num_iothreads++; } - for (node = iothread_vq_mapping_list; node; node = node->next) { + for (node = list; node; node = node->next) { IOThread *iothread = iothread_by_id(node->value->iothread); AioContext *ctx = iothread_get_aio_context(iothread); @@ -1549,13 +1548,13 @@ static bool apply_iothread_vq_mapping( } /** - * cleanup_iothread_vq_mapping: + * iothread_vq_mapping_cleanup: * @list: The mapping of virtqueues to IOThreads. * * Release IOThread object references that were acquired by - * apply_iothread_vq_mapping(). + * iothread_vq_mapping_apply(). */ -static void cleanup_iothread_vq_mapping(IOThreadVirtQueueMappingList *list) +static void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list) { IOThreadVirtQueueMappingList *node; @@ -1597,7 +1596,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp) s->vq_aio_context = g_new(AioContext *, conf->num_queues); if (conf->iothread_vq_mapping_list) { - if (!apply_iothread_vq_mapping(conf->iothread_vq_mapping_list, + if (!iothread_vq_mapping_apply(conf->iothread_vq_mapping_list, s->vq_aio_context, conf->num_queues, errp)) { @@ -1631,7 +1630,7 @@ static void virtio_blk_vq_aio_context_cleanup(VirtIOBlock *s) assert(!s->ioeventfd_started); if (conf->iothread_vq_mapping_list) { - cleanup_iothread_vq_mapping(conf->iothread_vq_mapping_list); + iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list); } if (conf->iothread) { From b50629c335804e193b51936867d6cb7ea3735d72 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:13 +0800 Subject: [PATCH 2675/2892] virtio: extract iothread-vq-mapping.h API The code that builds an array of AioContext pointers indexed by the virtqueue is not specific to virtio-blk. virtio-scsi will need to do the same thing, so extract the functions. Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Message-ID: <20250311132616.1049687-11-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/block/virtio-blk.c | 142 +----------------------- hw/virtio/iothread-vq-mapping.c | 131 ++++++++++++++++++++++ hw/virtio/meson.build | 1 + include/hw/virtio/iothread-vq-mapping.h | 45 ++++++++ 4 files changed, 178 insertions(+), 141 deletions(-) create mode 100644 hw/virtio/iothread-vq-mapping.c create mode 100644 include/hw/virtio/iothread-vq-mapping.h diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 6bf7b50520..5077793e5e 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -33,6 +33,7 @@ #endif #include "hw/virtio/virtio-bus.h" #include "migration/qemu-file-types.h" +#include "hw/virtio/iothread-vq-mapping.h" #include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-blk-common.h" #include "qemu/coroutine.h" @@ -1423,147 +1424,6 @@ static const BlockDevOps virtio_block_ops = { .drained_end = virtio_blk_drained_end, }; -static bool -iothread_vq_mapping_validate(IOThreadVirtQueueMappingList *list, uint16_t - num_queues, Error **errp) -{ - g_autofree unsigned long *vqs = bitmap_new(num_queues); - g_autoptr(GHashTable) iothreads = - g_hash_table_new(g_str_hash, g_str_equal); - - for (IOThreadVirtQueueMappingList *node = list; node; node = node->next) { - const char *name = node->value->iothread; - uint16List *vq; - - if (!iothread_by_id(name)) { - error_setg(errp, "IOThread \"%s\" object does not exist", name); - return false; - } - - if (!g_hash_table_add(iothreads, (gpointer)name)) { - error_setg(errp, - "duplicate IOThread name \"%s\" in iothread-vq-mapping", - name); - return false; - } - - if (node != list) { - if (!!node->value->vqs != !!list->value->vqs) { - error_setg(errp, "either all items in iothread-vq-mapping " - "must have vqs or none of them must have it"); - return false; - } - } - - for (vq = node->value->vqs; vq; vq = vq->next) { - if (vq->value >= num_queues) { - error_setg(errp, "vq index %u for IOThread \"%s\" must be " - "less than num_queues %u in iothread-vq-mapping", - vq->value, name, num_queues); - return false; - } - - if (test_and_set_bit(vq->value, vqs)) { - error_setg(errp, "cannot assign vq %u to IOThread \"%s\" " - "because it is already assigned", vq->value, name); - return false; - } - } - } - - if (list->value->vqs) { - for (uint16_t i = 0; i < num_queues; i++) { - if (!test_bit(i, vqs)) { - error_setg(errp, - "missing vq %u IOThread assignment in iothread-vq-mapping", - i); - return false; - } - } - } - - return true; -} - -/** - * iothread_vq_mapping_apply: - * @list: The mapping of virtqueues to IOThreads. - * @vq_aio_context: The array of AioContext pointers to fill in. - * @num_queues: The length of @vq_aio_context. - * @errp: If an error occurs, a pointer to the area to store the error. - * - * Fill in the AioContext for each virtqueue in the @vq_aio_context array given - * the iothread-vq-mapping parameter in @list. - * - * iothread_vq_mapping_cleanup() must be called to free IOThread object - * references after this function returns success. - * - * Returns: %true on success, %false on failure. - **/ -static bool iothread_vq_mapping_apply( - IOThreadVirtQueueMappingList *list, - AioContext **vq_aio_context, - uint16_t num_queues, - Error **errp) -{ - IOThreadVirtQueueMappingList *node; - size_t num_iothreads = 0; - size_t cur_iothread = 0; - - if (!iothread_vq_mapping_validate(list, num_queues, errp)) { - return false; - } - - for (node = list; node; node = node->next) { - num_iothreads++; - } - - for (node = list; node; node = node->next) { - IOThread *iothread = iothread_by_id(node->value->iothread); - AioContext *ctx = iothread_get_aio_context(iothread); - - /* Released in virtio_blk_vq_aio_context_cleanup() */ - object_ref(OBJECT(iothread)); - - if (node->value->vqs) { - uint16List *vq; - - /* Explicit vq:IOThread assignment */ - for (vq = node->value->vqs; vq; vq = vq->next) { - assert(vq->value < num_queues); - vq_aio_context[vq->value] = ctx; - } - } else { - /* Round-robin vq:IOThread assignment */ - for (unsigned i = cur_iothread; i < num_queues; - i += num_iothreads) { - vq_aio_context[i] = ctx; - } - } - - cur_iothread++; - } - - return true; -} - -/** - * iothread_vq_mapping_cleanup: - * @list: The mapping of virtqueues to IOThreads. - * - * Release IOThread object references that were acquired by - * iothread_vq_mapping_apply(). - */ -static void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list) -{ - IOThreadVirtQueueMappingList *node; - - for (node = list; node; node = node->next) { - IOThread *iothread = iothread_by_id(node->value->iothread); - object_unref(OBJECT(iothread)); - } -} - /* Context: BQL held */ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp) { diff --git a/hw/virtio/iothread-vq-mapping.c b/hw/virtio/iothread-vq-mapping.c new file mode 100644 index 0000000000..15909eb933 --- /dev/null +++ b/hw/virtio/iothread-vq-mapping.c @@ -0,0 +1,131 @@ +/* + * IOThread Virtqueue Mapping + * + * Copyright Red Hat, Inc + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "qemu/osdep.h" +#include "system/iothread.h" +#include "hw/virtio/iothread-vq-mapping.h" + +static bool +iothread_vq_mapping_validate(IOThreadVirtQueueMappingList *list, uint16_t + num_queues, Error **errp) +{ + g_autofree unsigned long *vqs = bitmap_new(num_queues); + g_autoptr(GHashTable) iothreads = + g_hash_table_new(g_str_hash, g_str_equal); + + for (IOThreadVirtQueueMappingList *node = list; node; node = node->next) { + const char *name = node->value->iothread; + uint16List *vq; + + if (!iothread_by_id(name)) { + error_setg(errp, "IOThread \"%s\" object does not exist", name); + return false; + } + + if (!g_hash_table_add(iothreads, (gpointer)name)) { + error_setg(errp, + "duplicate IOThread name \"%s\" in iothread-vq-mapping", + name); + return false; + } + + if (node != list) { + if (!!node->value->vqs != !!list->value->vqs) { + error_setg(errp, "either all items in iothread-vq-mapping " + "must have vqs or none of them must have it"); + return false; + } + } + + for (vq = node->value->vqs; vq; vq = vq->next) { + if (vq->value >= num_queues) { + error_setg(errp, "vq index %u for IOThread \"%s\" must be " + "less than num_queues %u in iothread-vq-mapping", + vq->value, name, num_queues); + return false; + } + + if (test_and_set_bit(vq->value, vqs)) { + error_setg(errp, "cannot assign vq %u to IOThread \"%s\" " + "because it is already assigned", vq->value, name); + return false; + } + } + } + + if (list->value->vqs) { + for (uint16_t i = 0; i < num_queues; i++) { + if (!test_bit(i, vqs)) { + error_setg(errp, + "missing vq %u IOThread assignment in iothread-vq-mapping", + i); + return false; + } + } + } + + return true; +} + +bool iothread_vq_mapping_apply( + IOThreadVirtQueueMappingList *list, + AioContext **vq_aio_context, + uint16_t num_queues, + Error **errp) +{ + IOThreadVirtQueueMappingList *node; + size_t num_iothreads = 0; + size_t cur_iothread = 0; + + if (!iothread_vq_mapping_validate(list, num_queues, errp)) { + return false; + } + + for (node = list; node; node = node->next) { + num_iothreads++; + } + + for (node = list; node; node = node->next) { + IOThread *iothread = iothread_by_id(node->value->iothread); + AioContext *ctx = iothread_get_aio_context(iothread); + + /* Released in virtio_blk_vq_aio_context_cleanup() */ + object_ref(OBJECT(iothread)); + + if (node->value->vqs) { + uint16List *vq; + + /* Explicit vq:IOThread assignment */ + for (vq = node->value->vqs; vq; vq = vq->next) { + assert(vq->value < num_queues); + vq_aio_context[vq->value] = ctx; + } + } else { + /* Round-robin vq:IOThread assignment */ + for (unsigned i = cur_iothread; i < num_queues; + i += num_iothreads) { + vq_aio_context[i] = ctx; + } + } + + cur_iothread++; + } + + return true; +} + +void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list) +{ + IOThreadVirtQueueMappingList *node; + + for (node = list; node; node = node->next) { + IOThread *iothread = iothread_by_id(node->value->iothread); + object_unref(OBJECT(iothread)); + } +} + diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index a5f9f7999d..19b04c4d9c 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -1,5 +1,6 @@ system_virtio_ss = ss.source_set() system_virtio_ss.add(files('virtio-bus.c')) +system_virtio_ss.add(files('iothread-vq-mapping.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('virtio-pci.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_MMIO', if_true: files('virtio-mmio.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) diff --git a/include/hw/virtio/iothread-vq-mapping.h b/include/hw/virtio/iothread-vq-mapping.h new file mode 100644 index 0000000000..57335c3703 --- /dev/null +++ b/include/hw/virtio/iothread-vq-mapping.h @@ -0,0 +1,45 @@ +/* + * IOThread Virtqueue Mapping + * + * Copyright Red Hat, Inc + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef HW_VIRTIO_IOTHREAD_VQ_MAPPING_H +#define HW_VIRTIO_IOTHREAD_VQ_MAPPING_H + +#include "qapi/error.h" +#include "qapi/qapi-types-virtio.h" + +/** + * iothread_vq_mapping_apply: + * @list: The mapping of virtqueues to IOThreads. + * @vq_aio_context: The array of AioContext pointers to fill in. + * @num_queues: The length of @vq_aio_context. + * @errp: If an error occurs, a pointer to the area to store the error. + * + * Fill in the AioContext for each virtqueue in the @vq_aio_context array given + * the iothread-vq-mapping parameter in @list. + * + * iothread_vq_mapping_cleanup() must be called to free IOThread object + * references after this function returns success. + * + * Returns: %true on success, %false on failure. + **/ +bool iothread_vq_mapping_apply( + IOThreadVirtQueueMappingList *list, + AioContext **vq_aio_context, + uint16_t num_queues, + Error **errp); + +/** + * iothread_vq_mapping_cleanup: + * @list: The mapping of virtqueues to IOThreads. + * + * Release IOThread object references that were acquired by + * iothread_vq_mapping_apply(). + */ +void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list); + +#endif /* HW_VIRTIO_IOTHREAD_VQ_MAPPING_H */ From 2e8e18c2e46307a355e547129b5a7a7000a0cf0d Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:14 +0800 Subject: [PATCH 2676/2892] virtio-scsi: add iothread-vq-mapping parameter Allow virtio-scsi virtqueues to be assigned to different IOThreads. This makes it possible to take advantage of host multi-queue block layer scalability by assigning virtqueues that have affinity with vCPUs to different IOThreads that have affinity with host CPUs. The same feature was introduced for virtio-blk in the past: https://developers.redhat.com/articles/2024/09/05/scaling-virtio-blk-disk-io-iothread-virtqueue-mapping Here are fio randread 4k iodepth=64 results from a 4 vCPU guest with an Intel P4800X SSD: iothreads IOPS ------------------------------ 1 189576 2 312698 4 346744 Signed-off-by: Stefan Hajnoczi Message-ID: <20250311132616.1049687-12-stefanha@redhat.com> Tested-by: Peter Krempa [kwolf: Updated 051 output, virtio-scsi can now use any iothread] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/scsi/virtio-scsi-dataplane.c | 90 ++++++++++++++++++++++++--------- hw/scsi/virtio-scsi.c | 63 ++++++++++++++--------- include/hw/virtio/virtio-scsi.h | 5 +- tests/qemu-iotests/051.pc.out | 2 +- 4 files changed, 108 insertions(+), 52 deletions(-) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index f49ab98ecc..6bb368c8a5 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -18,6 +18,7 @@ #include "system/block-backend.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" +#include "hw/virtio/iothread-vq-mapping.h" #include "hw/virtio/virtio-bus.h" /* Context: BQL held */ @@ -27,8 +28,16 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(s); BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + uint16_t num_vqs = vs->conf.num_queues + VIRTIO_SCSI_VQ_NUM_FIXED; - if (vs->conf.iothread) { + if (vs->conf.iothread && vs->conf.iothread_vq_mapping_list) { + error_setg(errp, + "iothread and iothread-vq-mapping properties cannot be set " + "at the same time"); + return; + } + + if (vs->conf.iothread || vs->conf.iothread_vq_mapping_list) { if (!k->set_guest_notifiers || !k->ioeventfd_assign) { error_setg(errp, "device is incompatible with iothread " @@ -39,15 +48,50 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) error_setg(errp, "ioeventfd is required for iothread"); return; } - s->ctx = iothread_get_aio_context(vs->conf.iothread); - } else { - if (!virtio_device_ioeventfd_enabled(vdev)) { + } + + s->vq_aio_context = g_new(AioContext *, num_vqs); + + if (vs->conf.iothread_vq_mapping_list) { + if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list, + s->vq_aio_context, num_vqs, errp)) { + g_free(s->vq_aio_context); + s->vq_aio_context = NULL; return; } - s->ctx = qemu_get_aio_context(); + } else if (vs->conf.iothread) { + AioContext *ctx = iothread_get_aio_context(vs->conf.iothread); + for (uint16_t i = 0; i < num_vqs; i++) { + s->vq_aio_context[i] = ctx; + } + + /* Released in virtio_scsi_dataplane_cleanup() */ + object_ref(OBJECT(vs->conf.iothread)); + } else { + AioContext *ctx = qemu_get_aio_context(); + for (unsigned i = 0; i < num_vqs; i++) { + s->vq_aio_context[i] = ctx; + } } } +/* Context: BQL held */ +void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s) +{ + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + + if (vs->conf.iothread_vq_mapping_list) { + iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list); + } + + if (vs->conf.iothread) { + object_unref(OBJECT(vs->conf.iothread)); + } + + g_free(s->vq_aio_context); + s->vq_aio_context = NULL; +} + static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); @@ -66,31 +110,20 @@ static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) } /* Context: BH in IOThread */ -static void virtio_scsi_dataplane_stop_bh(void *opaque) +static void virtio_scsi_dataplane_stop_vq_bh(void *opaque) { - VirtIOSCSI *s = opaque; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + AioContext *ctx = qemu_get_current_aio_context(); + VirtQueue *vq = opaque; EventNotifier *host_notifier; - int i; - virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx); - host_notifier = virtio_queue_get_host_notifier(vs->ctrl_vq); + virtio_queue_aio_detach_host_notifier(vq, ctx); + host_notifier = virtio_queue_get_host_notifier(vq); /* * Test and clear notifier after disabling event, in case poll callback * didn't have time to run. */ virtio_queue_host_notifier_read(host_notifier); - - virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx); - host_notifier = virtio_queue_get_host_notifier(vs->event_vq); - virtio_queue_host_notifier_read(host_notifier); - - for (i = 0; i < vs->conf.num_queues; i++) { - virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx); - host_notifier = virtio_queue_get_host_notifier(vs->cmd_vqs[i]); - virtio_queue_host_notifier_read(host_notifier); - } } /* Context: BQL held */ @@ -154,11 +187,14 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) smp_wmb(); /* paired with aio_notify_accept() */ if (s->bus.drain_count == 0) { - virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx); - virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx); + virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, + s->vq_aio_context[0]); + virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, + s->vq_aio_context[1]); for (i = 0; i < vs->conf.num_queues; i++) { - virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx); + AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; + virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], ctx); } } return 0; @@ -207,7 +243,11 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) s->dataplane_stopping = true; if (s->bus.drain_count == 0) { - aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); + for (i = 0; i < vs->conf.num_queues + VIRTIO_SCSI_VQ_NUM_FIXED; i++) { + VirtQueue *vq = virtio_get_queue(&vs->parent_obj, i); + AioContext *ctx = s->vq_aio_context[i]; + aio_wait_bh_oneshot(ctx, virtio_scsi_dataplane_stop_vq_bh, vq); + } } blk_drain_all(); /* ensure there are no in-flight requests */ diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 2045d27289..e7d9703608 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -27,6 +27,7 @@ #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" +#include "hw/virtio/iothread-vq-mapping.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" #include "trace.h" @@ -318,13 +319,6 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) g_free(n); } -static inline void virtio_scsi_ctx_check(VirtIOSCSI *s, SCSIDevice *d) -{ - if (s->dataplane_started && d && blk_is_available(d->conf.blk)) { - assert(blk_get_aio_context(d->conf.blk) == s->ctx); - } -} - static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) { VirtIOSCSI *s = req->dev; @@ -517,9 +511,11 @@ static void virtio_scsi_flush_defer_tmf_to_aio_context(VirtIOSCSI *s) assert(!s->dataplane_started); - if (s->ctx) { + for (uint32_t i = 0; i < s->parent_obj.conf.num_queues; i++) { + AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; + /* Our BH only runs after previously scheduled BHs */ - aio_wait_bh_oneshot(s->ctx, dummy_bh, NULL); + aio_wait_bh_oneshot(ctx, dummy_bh, NULL); } } @@ -575,7 +571,6 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) AioContext *ctx; int ret = 0; - virtio_scsi_ctx_check(s, d); /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ req->resp.tmf.response = VIRTIO_SCSI_S_OK; @@ -639,6 +634,8 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: { + g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL); + if (!d) { goto fail; } @@ -648,8 +645,15 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) qatomic_inc(&req->remaining); - ctx = s->ctx ?: qemu_get_aio_context(); - virtio_scsi_defer_tmf_to_aio_context(req, ctx); + for (uint32_t i = 0; i < s->parent_obj.conf.num_queues; i++) { + ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; + + if (!g_hash_table_add(aio_contexts, ctx)) { + continue; /* skip previously added AioContext */ + } + + virtio_scsi_defer_tmf_to_aio_context(req, ctx); + } virtio_scsi_tmf_dec_remaining(req); ret = -EINPROGRESS; @@ -770,9 +774,12 @@ static void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) */ static bool virtio_scsi_defer_to_dataplane(VirtIOSCSI *s) { - if (!s->ctx || s->dataplane_started) { + if (s->dataplane_started) { return false; } + if (s->vq_aio_context[0] == qemu_get_aio_context()) { + return false; /* not using IOThreads */ + } virtio_device_start_ioeventfd(&s->parent_obj.parent_obj); return !s->dataplane_fenced; @@ -946,7 +953,6 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) virtio_scsi_complete_cmd_req(req); return -ENOENT; } - virtio_scsi_ctx_check(s, d); req->sreq = scsi_req_new(d, req->req.cmd.tag, virtio_scsi_get_lun(req->req.cmd.lun), req->req.cmd.cdb, vs->cdb_size, req); @@ -1218,14 +1224,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, { VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); VirtIOSCSI *s = VIRTIO_SCSI(vdev); + AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED]; SCSIDevice *sd = SCSI_DEVICE(dev); - int ret; - if (s->ctx && !s->dataplane_fenced) { - ret = blk_set_aio_context(sd->conf.blk, s->ctx, errp); - if (ret < 0) { - return; - } + if (ctx != qemu_get_aio_context() && !s->dataplane_fenced) { + /* + * Try to make the BlockBackend's AioContext match ours. Ignore failure + * because I/O will still work although block jobs and other users + * might be slower when multiple AioContexts use a BlockBackend. + */ + blk_set_aio_context(sd->conf.blk, ctx, NULL); } if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { @@ -1260,7 +1268,7 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); - if (s->ctx) { + if (s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED] != qemu_get_aio_context()) { /* If other users keep the BlockBackend in the iothread, that's ok */ blk_set_aio_context(sd->conf.blk, qemu_get_aio_context(), NULL); } @@ -1294,7 +1302,7 @@ static void virtio_scsi_drained_begin(SCSIBus *bus) for (uint32_t i = 0; i < total_queues; i++) { VirtQueue *vq = virtio_get_queue(vdev, i); - virtio_queue_aio_detach_host_notifier(vq, s->ctx); + virtio_queue_aio_detach_host_notifier(vq, s->vq_aio_context[i]); } } @@ -1320,10 +1328,12 @@ static void virtio_scsi_drained_end(SCSIBus *bus) for (uint32_t i = 0; i < total_queues; i++) { VirtQueue *vq = virtio_get_queue(vdev, i); + AioContext *ctx = s->vq_aio_context[i]; + if (vq == vs->event_vq) { - virtio_queue_aio_attach_host_notifier_no_poll(vq, s->ctx); + virtio_queue_aio_attach_host_notifier_no_poll(vq, ctx); } else { - virtio_queue_aio_attach_host_notifier(vq, s->ctx); + virtio_queue_aio_attach_host_notifier(vq, ctx); } } } @@ -1430,12 +1440,13 @@ void virtio_scsi_common_unrealize(DeviceState *dev) virtio_cleanup(vdev); } +/* main loop */ static void virtio_scsi_device_unrealize(DeviceState *dev) { VirtIOSCSI *s = VIRTIO_SCSI(dev); virtio_scsi_reset_tmf_bh(s); - + virtio_scsi_dataplane_cleanup(s); qbus_set_hotplug_handler(BUS(&s->bus), NULL); virtio_scsi_common_unrealize(dev); qemu_mutex_destroy(&s->tmf_bh_lock); @@ -1460,6 +1471,8 @@ static const Property virtio_scsi_properties[] = { VIRTIO_SCSI_F_CHANGE, true), DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread, TYPE_IOTHREAD, IOThread *), + DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST("iothread-vq-mapping", VirtIOSCSI, + parent_obj.conf.iothread_vq_mapping_list), }; static const VMStateDescription vmstate_virtio_scsi = { diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 7b7e3ced7a..086201efa2 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -22,6 +22,7 @@ #include "hw/virtio/virtio.h" #include "hw/scsi/scsi.h" #include "chardev/char-fe.h" +#include "qapi/qapi-types-virtio.h" #include "system/iothread.h" #define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common" @@ -60,6 +61,7 @@ struct VirtIOSCSIConf { CharBackend chardev; uint32_t boot_tpgt; IOThread *iothread; + IOThreadVirtQueueMappingList *iothread_vq_mapping_list; }; struct VirtIOSCSI; @@ -97,7 +99,7 @@ struct VirtIOSCSI { QTAILQ_HEAD(, VirtIOSCSIReq) tmf_bh_list; /* Fields for dataplane below */ - AioContext *ctx; /* one iothread per virtio-scsi-pci for now */ + AioContext **vq_aio_context; /* per-virtqueue AioContext pointer */ bool dataplane_started; bool dataplane_starting; @@ -115,6 +117,7 @@ void virtio_scsi_common_realize(DeviceState *dev, void virtio_scsi_common_unrealize(DeviceState *dev); void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp); +void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s); int virtio_scsi_dataplane_start(VirtIODevice *s); void virtio_scsi_dataplane_stop(VirtIODevice *s); diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index 7e10c5fa1b..f19b532eb9 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -181,7 +181,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on: Cannot change iothread of active block backend +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information From bcede51d2d1ae03f99ccb2569e52b5062033d40d Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:15 +0800 Subject: [PATCH 2677/2892] virtio-scsi: handle ctrl virtqueue in main loop Previously the ctrl virtqueue was handled in the AioContext where SCSI requests are processed. When IOThread Virtqueue Mapping was added things become more complicated because SCSI requests could run in other AioContexts. Simplify by handling the ctrl virtqueue in the main loop where reset operations can be performed. Note that BHs are still used canceling SCSI requests in their AioContexts but at least the mean loop activity doesn't need BHs anymore. Signed-off-by: Stefan Hajnoczi Message-ID: <20250311132616.1049687-13-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/virtio-scsi-dataplane.c | 6 ++ hw/scsi/virtio-scsi.c | 144 ++++++-------------------------- include/hw/virtio/virtio-scsi.h | 8 -- 3 files changed, 33 insertions(+), 125 deletions(-) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 6bb368c8a5..2d37fa6712 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -73,6 +73,12 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) s->vq_aio_context[i] = ctx; } } + + /* + * Always handle the ctrl virtqueue in the main loop thread where device + * resets can be performed. + */ + s->vq_aio_context[0] = qemu_get_aio_context(); } /* Context: BQL held */ diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index e7d9703608..f5a3aa2366 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -319,115 +319,6 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) g_free(n); } -static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) -{ - VirtIOSCSI *s = req->dev; - SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); - BusChild *kid; - int target; - - switch (req->req.tmf.subtype) { - case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - if (!d) { - req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; - goto out; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; - goto out; - } - qatomic_inc(&s->resetting); - device_cold_reset(&d->qdev); - qatomic_dec(&s->resetting); - break; - - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf.lun[1]; - qatomic_inc(&s->resetting); - - rcu_read_lock(); - QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *d1 = SCSI_DEVICE(kid->child); - if (d1->channel == 0 && d1->id == target) { - device_cold_reset(&d1->qdev); - } - } - rcu_read_unlock(); - - qatomic_dec(&s->resetting); - break; - - default: - g_assert_not_reached(); - } - -out: - object_unref(OBJECT(d)); - virtio_scsi_complete_req(req, &s->ctrl_lock); -} - -/* Some TMFs must be processed from the main loop thread */ -static void virtio_scsi_do_tmf_bh(void *opaque) -{ - VirtIOSCSI *s = opaque; - QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - VirtIOSCSIReq *req; - VirtIOSCSIReq *tmp; - - GLOBAL_STATE_CODE(); - - WITH_QEMU_LOCK_GUARD(&s->tmf_bh_lock) { - QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { - QTAILQ_REMOVE(&s->tmf_bh_list, req, next); - QTAILQ_INSERT_TAIL(&reqs, req, next); - } - - qemu_bh_delete(s->tmf_bh); - s->tmf_bh = NULL; - } - - QTAILQ_FOREACH_SAFE(req, &reqs, next, tmp) { - QTAILQ_REMOVE(&reqs, req, next); - virtio_scsi_do_one_tmf_bh(req); - } -} - -static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) -{ - VirtIOSCSIReq *req; - VirtIOSCSIReq *tmp; - - GLOBAL_STATE_CODE(); - - /* Called after ioeventfd has been stopped, so tmf_bh_lock is not needed */ - if (s->tmf_bh) { - qemu_bh_delete(s->tmf_bh); - s->tmf_bh = NULL; - } - - QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { - QTAILQ_REMOVE(&s->tmf_bh_list, req, next); - - /* SAM-6 6.3.2 Hard reset */ - req->resp.tmf.response = VIRTIO_SCSI_S_TARGET_FAILURE; - virtio_scsi_complete_req(req, &req->dev->ctrl_lock); - } -} - -static void virtio_scsi_defer_tmf_to_main_loop(VirtIOSCSIReq *req) -{ - VirtIOSCSI *s = req->dev; - - WITH_QEMU_LOCK_GUARD(&s->tmf_bh_lock) { - QTAILQ_INSERT_TAIL(&s->tmf_bh_list, req, next); - - if (!s->tmf_bh) { - s->tmf_bh = qemu_bh_new(virtio_scsi_do_tmf_bh, s); - qemu_bh_schedule(s->tmf_bh); - } - } -} - static void virtio_scsi_tmf_cancel_req(VirtIOSCSIReq *tmf, SCSIRequest *r) { VirtIOSCSICancelNotifier *notifier; @@ -627,11 +518,35 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) break; case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - virtio_scsi_defer_tmf_to_main_loop(req); - ret = -EINPROGRESS; + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + goto incorrect_lun; + } + qatomic_inc(&s->resetting); + device_cold_reset(&d->qdev); + qatomic_dec(&s->resetting); break; + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: { + BusChild *kid; + int target = req->req.tmf.lun[1]; + qatomic_inc(&s->resetting); + + rcu_read_lock(); + QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *d1 = SCSI_DEVICE(kid->child); + if (d1->channel == 0 && d1->id == target) { + device_cold_reset(&d1->qdev); + } + } + rcu_read_unlock(); + + qatomic_dec(&s->resetting); + break; + } + case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: { g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL); @@ -1087,7 +1002,6 @@ static void virtio_scsi_reset(VirtIODevice *vdev) assert(!s->dataplane_started); - virtio_scsi_reset_tmf_bh(s); virtio_scsi_flush_defer_tmf_to_aio_context(s); qatomic_inc(&s->resetting); @@ -1402,10 +1316,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) VirtIOSCSI *s = VIRTIO_SCSI(dev); Error *err = NULL; - QTAILQ_INIT(&s->tmf_bh_list); qemu_mutex_init(&s->ctrl_lock); qemu_mutex_init(&s->event_lock); - qemu_mutex_init(&s->tmf_bh_lock); virtio_scsi_common_realize(dev, virtio_scsi_handle_ctrl, @@ -1445,11 +1357,9 @@ static void virtio_scsi_device_unrealize(DeviceState *dev) { VirtIOSCSI *s = VIRTIO_SCSI(dev); - virtio_scsi_reset_tmf_bh(s); virtio_scsi_dataplane_cleanup(s); qbus_set_hotplug_handler(BUS(&s->bus), NULL); virtio_scsi_common_unrealize(dev); - qemu_mutex_destroy(&s->tmf_bh_lock); qemu_mutex_destroy(&s->event_lock); qemu_mutex_destroy(&s->ctrl_lock); } diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 086201efa2..31e852ed6c 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -90,14 +90,6 @@ struct VirtIOSCSI { QemuMutex ctrl_lock; /* protects ctrl_vq */ - /* - * TMFs deferred to main loop BH. These fields are protected by - * tmf_bh_lock. - */ - QemuMutex tmf_bh_lock; - QEMUBH *tmf_bh; - QTAILQ_HEAD(, VirtIOSCSIReq) tmf_bh_list; - /* Fields for dataplane below */ AioContext **vq_aio_context; /* per-virtqueue AioContext pointer */ From 40aa38a651a8d4ca99c70e591176a97abcae5295 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Mar 2025 21:26:16 +0800 Subject: [PATCH 2678/2892] virtio-scsi: only expose cmd vqs via iothread-vq-mapping Peter Krempa and Kevin Wolf observed that iothread-vq-mapping is confusing to use because the control and event virtqueues have a fixed location before the command virtqueues but need to be treated differently. Only expose the command virtqueues via iothread-vq-mapping so that the command-line parameter is intuitive: it controls where SCSI requests are processed. The control virtqueue needs to be hardcoded to the main loop thread for technical reasons anyway. Kevin also pointed out that it's better to place the event virtqueue in the main loop thread since its no poll behavior would prevent polling if assigned to an IOThread. This change is its own commit to avoid squashing the previous commit. Suggested-by: Kevin Wolf Suggested-by: Peter Krempa Signed-off-by: Stefan Hajnoczi Message-ID: <20250311132616.1049687-14-stefanha@redhat.com> Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/scsi/virtio-scsi-dataplane.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 2d37fa6712..95f13fb7c2 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -28,7 +28,6 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(s); BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - uint16_t num_vqs = vs->conf.num_queues + VIRTIO_SCSI_VQ_NUM_FIXED; if (vs->conf.iothread && vs->conf.iothread_vq_mapping_list) { error_setg(errp, @@ -50,35 +49,43 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) } } - s->vq_aio_context = g_new(AioContext *, num_vqs); + s->vq_aio_context = g_new(AioContext *, vs->conf.num_queues + + VIRTIO_SCSI_VQ_NUM_FIXED); + + /* + * Handle the ctrl virtqueue in the main loop thread where device resets + * can be performed. + */ + s->vq_aio_context[0] = qemu_get_aio_context(); + + /* + * Handle the event virtqueue in the main loop thread where its no_poll + * behavior won't stop IOThread polling. + */ + s->vq_aio_context[1] = qemu_get_aio_context(); if (vs->conf.iothread_vq_mapping_list) { if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list, - s->vq_aio_context, num_vqs, errp)) { + &s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED], + vs->conf.num_queues, errp)) { g_free(s->vq_aio_context); s->vq_aio_context = NULL; return; } } else if (vs->conf.iothread) { AioContext *ctx = iothread_get_aio_context(vs->conf.iothread); - for (uint16_t i = 0; i < num_vqs; i++) { - s->vq_aio_context[i] = ctx; + for (uint16_t i = 0; i < vs->conf.num_queues; i++) { + s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx; } /* Released in virtio_scsi_dataplane_cleanup() */ object_ref(OBJECT(vs->conf.iothread)); } else { AioContext *ctx = qemu_get_aio_context(); - for (unsigned i = 0; i < num_vqs; i++) { - s->vq_aio_context[i] = ctx; + for (unsigned i = 0; i < vs->conf.num_queues; i++) { + s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx; } } - - /* - * Always handle the ctrl virtqueue in the main loop thread where device - * resets can be performed. - */ - s->vq_aio_context[0] = qemu_get_aio_context(); } /* Context: BQL held */ From df957115c46845e2c0ccc29ac0a75eb9700a9a0d Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 30 Jul 2024 16:15:52 +0200 Subject: [PATCH 2679/2892] scripts/qcow2-to-stdout.py: Add script to write qcow2 images to stdout This tool converts a disk image to qcow2, writing the result directly to stdout. This can be used for example to send the generated file over the network. This is equivalent to using qemu-img to convert a file to qcow2 and then writing the result to stdout, with the difference that this tool does not need to create this temporary qcow2 file and therefore does not need any additional disk space. Implementing this directly in qemu-img is not really an option because it expects the output file to be seekable and it is also meant to be a generic tool that supports all combinations of file formats and image options. Instead, this tool can only produce qcow2 files with the basic options, without compression, encryption or other features. The input file is read twice. The first pass is used to determine which clusters contain non-zero data and that information is used to create the qcow2 header, refcount table and blocks, and L1 and L2 tables. After all that metadata is created then the second pass is used to write the guest data. By default qcow2-to-stdout.py expects the input to be a raw file, but if qemu-storage-daemon is available then it can also be used to read images in other formats. Alternatively the user can also run qemu-nbd or qemu-storage-daemon manually instead. Signed-off-by: Alberto Garcia Signed-off-by: Madeeha Javed Message-ID: <20240730141552.60404-1-berto@igalia.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- scripts/qcow2-to-stdout.py | 449 +++++++++++++++++++++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100755 scripts/qcow2-to-stdout.py diff --git a/scripts/qcow2-to-stdout.py b/scripts/qcow2-to-stdout.py new file mode 100755 index 0000000000..06b7c13ccb --- /dev/null +++ b/scripts/qcow2-to-stdout.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python3 + +# This tool reads a disk image in any format and converts it to qcow2, +# writing the result directly to stdout. +# +# Copyright (C) 2024 Igalia, S.L. +# +# Authors: Alberto Garcia +# Madeeha Javed +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# qcow2 files produced by this script are always arranged like this: +# +# - qcow2 header +# - refcount table +# - refcount blocks +# - L1 table +# - L2 tables +# - Data clusters +# +# A note about variable names: in qcow2 there is one refcount table +# and one (active) L1 table, although each can occupy several +# clusters. For the sake of simplicity the code sometimes talks about +# refcount tables and L1 tables when referring to those clusters. + +import argparse +import errno +import math +import os +import signal +import struct +import subprocess +import sys +import tempfile +import time +from contextlib import contextmanager + +QCOW2_DEFAULT_CLUSTER_SIZE = 65536 +QCOW2_DEFAULT_REFCOUNT_BITS = 16 +QCOW2_FEATURE_NAME_TABLE = 0x6803F857 +QCOW2_DATA_FILE_NAME_STRING = 0x44415441 +QCOW2_V3_HEADER_LENGTH = 112 # Header length in QEMU 9.0. Must be a multiple of 8 +QCOW2_INCOMPAT_DATA_FILE_BIT = 2 +QCOW2_AUTOCLEAR_DATA_FILE_RAW_BIT = 1 +QCOW_OFLAG_COPIED = 1 << 63 +QEMU_STORAGE_DAEMON = "qemu-storage-daemon" + + +def bitmap_set(bitmap, idx): + bitmap[idx // 8] |= 1 << (idx % 8) + + +def bitmap_is_set(bitmap, idx): + return (bitmap[idx // 8] & (1 << (idx % 8))) != 0 + + +def bitmap_iterator(bitmap, length): + for idx in range(length): + if bitmap_is_set(bitmap, idx): + yield idx + + +def align_up(num, d): + return d * math.ceil(num / d) + + +# Holes in the input file contain only zeroes so we can skip them and +# save time. This function returns the indexes of the clusters that +# are known to contain data. Those are the ones that we need to read. +def clusters_with_data(fd, cluster_size): + data_to = 0 + while True: + try: + data_from = os.lseek(fd, data_to, os.SEEK_DATA) + data_to = align_up(os.lseek(fd, data_from, os.SEEK_HOLE), cluster_size) + for idx in range(data_from // cluster_size, data_to // cluster_size): + yield idx + except OSError as err: + if err.errno == errno.ENXIO: # End of file reached + break + raise err + + +# write_qcow2_content() expects a raw input file. If we have a different +# format we can use qemu-storage-daemon to make it appear as raw. +@contextmanager +def get_input_as_raw_file(input_file, input_format): + if input_format == "raw": + yield input_file + return + try: + temp_dir = tempfile.mkdtemp() + pid_file = os.path.join(temp_dir, "pid") + raw_file = os.path.join(temp_dir, "raw") + open(raw_file, "wb").close() + ret = subprocess.run( + [ + QEMU_STORAGE_DAEMON, + "--daemonize", + "--pidfile", pid_file, + "--blockdev", f"driver=file,node-name=file0,driver=file,filename={input_file},read-only=on", + "--blockdev", f"driver={input_format},node-name=disk0,file=file0,read-only=on", + "--export", f"type=fuse,id=export0,node-name=disk0,mountpoint={raw_file},writable=off", + ], + capture_output=True, + ) + if ret.returncode != 0: + sys.exit("[Error] Could not start the qemu-storage-daemon:\n" + + ret.stderr.decode().rstrip('\n')) + yield raw_file + finally: + # Kill the storage daemon on exit + # and remove all temporary files + if os.path.exists(pid_file): + with open(pid_file, "r") as f: + pid = int(f.readline()) + os.kill(pid, signal.SIGTERM) + while os.path.exists(pid_file): + time.sleep(0.1) + os.unlink(raw_file) + os.rmdir(temp_dir) + + +def write_features(cluster, offset, data_file_name): + if data_file_name is not None: + encoded_name = data_file_name.encode("utf-8") + padded_name_len = align_up(len(encoded_name), 8) + struct.pack_into(f">II{padded_name_len}s", cluster, offset, + QCOW2_DATA_FILE_NAME_STRING, + len(encoded_name), + encoded_name) + offset += 8 + padded_name_len + + qcow2_features = [ + # Incompatible + (0, 0, "dirty bit"), + (0, 1, "corrupt bit"), + (0, 2, "external data file"), + (0, 3, "compression type"), + (0, 4, "extended L2 entries"), + # Compatible + (1, 0, "lazy refcounts"), + # Autoclear + (2, 0, "bitmaps"), + (2, 1, "raw external data"), + ] + struct.pack_into(">I", cluster, offset, QCOW2_FEATURE_NAME_TABLE) + struct.pack_into(">I", cluster, offset + 4, len(qcow2_features) * 48) + offset += 8 + for feature_type, feature_bit, feature_name in qcow2_features: + struct.pack_into(">BB46s", cluster, offset, + feature_type, feature_bit, feature_name.encode("ascii")) + offset += 48 + + +def write_qcow2_content(input_file, cluster_size, refcount_bits, data_file_name, data_file_raw): + # Some basic values + l1_entries_per_table = cluster_size // 8 + l2_entries_per_table = cluster_size // 8 + refcounts_per_table = cluster_size // 8 + refcounts_per_block = cluster_size * 8 // refcount_bits + + # Virtual disk size, number of data clusters and L1 entries + disk_size = align_up(os.path.getsize(input_file), 512) + total_data_clusters = math.ceil(disk_size / cluster_size) + l1_entries = math.ceil(total_data_clusters / l2_entries_per_table) + allocated_l1_tables = math.ceil(l1_entries / l1_entries_per_table) + + # Max L1 table size is 32 MB (QCOW_MAX_L1_SIZE in block/qcow2.h) + if (l1_entries * 8) > (32 * 1024 * 1024): + sys.exit("[Error] The image size is too large. Try using a larger cluster size.") + + # Two bitmaps indicating which L1 and L2 entries are set + l1_bitmap = bytearray(allocated_l1_tables * l1_entries_per_table // 8) + l2_bitmap = bytearray(l1_entries * l2_entries_per_table // 8) + allocated_l2_tables = 0 + allocated_data_clusters = 0 + + if data_file_raw: + # If data_file_raw is set then all clusters are allocated and + # we don't need to read the input file at all. + allocated_l2_tables = l1_entries + for idx in range(l1_entries): + bitmap_set(l1_bitmap, idx) + for idx in range(total_data_clusters): + bitmap_set(l2_bitmap, idx) + else: + # Open the input file for reading + fd = os.open(input_file, os.O_RDONLY) + zero_cluster = bytes(cluster_size) + # Read all the clusters that contain data + for idx in clusters_with_data(fd, cluster_size): + cluster = os.pread(fd, cluster_size, cluster_size * idx) + # If the last cluster is smaller than cluster_size pad it with zeroes + if len(cluster) < cluster_size: + cluster += bytes(cluster_size - len(cluster)) + # If a cluster has non-zero data then it must be allocated + # in the output file and its L2 entry must be set + if cluster != zero_cluster: + bitmap_set(l2_bitmap, idx) + allocated_data_clusters += 1 + # Allocated data clusters also need their corresponding L1 entry and L2 table + l1_idx = math.floor(idx / l2_entries_per_table) + if not bitmap_is_set(l1_bitmap, l1_idx): + bitmap_set(l1_bitmap, l1_idx) + allocated_l2_tables += 1 + + # Total amount of allocated clusters excluding the refcount blocks and table + total_allocated_clusters = 1 + allocated_l1_tables + allocated_l2_tables + if data_file_name is None: + total_allocated_clusters += allocated_data_clusters + + # Clusters allocated for the refcount blocks and table + allocated_refcount_blocks = math.ceil(total_allocated_clusters / refcounts_per_block) + allocated_refcount_tables = math.ceil(allocated_refcount_blocks / refcounts_per_table) + + # Now we have a problem because allocated_refcount_blocks and allocated_refcount_tables... + # (a) increase total_allocated_clusters, and + # (b) need to be recalculated when total_allocated_clusters is increased + # So we need to repeat the calculation as long as the numbers change + while True: + new_total_allocated_clusters = total_allocated_clusters + allocated_refcount_tables + allocated_refcount_blocks + new_allocated_refcount_blocks = math.ceil(new_total_allocated_clusters / refcounts_per_block) + if new_allocated_refcount_blocks > allocated_refcount_blocks: + allocated_refcount_blocks = new_allocated_refcount_blocks + allocated_refcount_tables = math.ceil(allocated_refcount_blocks / refcounts_per_table) + else: + break + + # Now that we have the final numbers we can update total_allocated_clusters + total_allocated_clusters += allocated_refcount_tables + allocated_refcount_blocks + + # At this point we have the exact number of clusters that the output + # image is going to use so we can calculate all the offsets. + current_cluster_idx = 1 + + refcount_table_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_refcount_tables + + refcount_block_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_refcount_blocks + + l1_table_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_l1_tables + + l2_table_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_l2_tables + + data_clusters_offset = current_cluster_idx * cluster_size + + # Calculate some values used in the qcow2 header + if allocated_l1_tables == 0: + l1_table_offset = 0 + + hdr_cluster_bits = int(math.log2(cluster_size)) + hdr_refcount_bits = int(math.log2(refcount_bits)) + hdr_length = QCOW2_V3_HEADER_LENGTH + hdr_incompat_features = 0 + if data_file_name is not None: + hdr_incompat_features |= 1 << QCOW2_INCOMPAT_DATA_FILE_BIT + hdr_autoclear_features = 0 + if data_file_raw: + hdr_autoclear_features |= 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BIT + + ### Write qcow2 header + cluster = bytearray(cluster_size) + struct.pack_into(">4sIQIIQIIQQIIQQQQII", cluster, 0, + b"QFI\xfb", # QCOW magic string + 3, # version + 0, # backing file offset + 0, # backing file sizes + hdr_cluster_bits, + disk_size, + 0, # encryption method + l1_entries, + l1_table_offset, + refcount_table_offset, + allocated_refcount_tables, + 0, # number of snapshots + 0, # snapshot table offset + hdr_incompat_features, + 0, # compatible features + hdr_autoclear_features, + hdr_refcount_bits, + hdr_length, + ) + + write_features(cluster, hdr_length, data_file_name) + + sys.stdout.buffer.write(cluster) + + ### Write refcount table + cur_offset = refcount_block_offset + remaining_refcount_table_entries = allocated_refcount_blocks # Each entry is a pointer to a refcount block + while remaining_refcount_table_entries > 0: + cluster = bytearray(cluster_size) + to_write = min(remaining_refcount_table_entries, refcounts_per_table) + remaining_refcount_table_entries -= to_write + for idx in range(to_write): + struct.pack_into(">Q", cluster, idx * 8, cur_offset) + cur_offset += cluster_size + sys.stdout.buffer.write(cluster) + + ### Write refcount blocks + remaining_refcount_block_entries = total_allocated_clusters # One entry for each allocated cluster + for tbl in range(allocated_refcount_blocks): + cluster = bytearray(cluster_size) + to_write = min(remaining_refcount_block_entries, refcounts_per_block) + remaining_refcount_block_entries -= to_write + # All refcount entries contain the number 1. The only difference + # is their bit width, defined when the image is created. + for idx in range(to_write): + if refcount_bits == 64: + struct.pack_into(">Q", cluster, idx * 8, 1) + elif refcount_bits == 32: + struct.pack_into(">L", cluster, idx * 4, 1) + elif refcount_bits == 16: + struct.pack_into(">H", cluster, idx * 2, 1) + elif refcount_bits == 8: + cluster[idx] = 1 + elif refcount_bits == 4: + cluster[idx // 2] |= 1 << ((idx % 2) * 4) + elif refcount_bits == 2: + cluster[idx // 4] |= 1 << ((idx % 4) * 2) + elif refcount_bits == 1: + cluster[idx // 8] |= 1 << (idx % 8) + sys.stdout.buffer.write(cluster) + + ### Write L1 table + cur_offset = l2_table_offset + for tbl in range(allocated_l1_tables): + cluster = bytearray(cluster_size) + for idx in range(l1_entries_per_table): + l1_idx = tbl * l1_entries_per_table + idx + if bitmap_is_set(l1_bitmap, l1_idx): + struct.pack_into(">Q", cluster, idx * 8, cur_offset | QCOW_OFLAG_COPIED) + cur_offset += cluster_size + sys.stdout.buffer.write(cluster) + + ### Write L2 tables + cur_offset = data_clusters_offset + for tbl in range(l1_entries): + # Skip the empty L2 tables. We can identify them because + # there is no L1 entry pointing at them. + if bitmap_is_set(l1_bitmap, tbl): + cluster = bytearray(cluster_size) + for idx in range(l2_entries_per_table): + l2_idx = tbl * l2_entries_per_table + idx + if bitmap_is_set(l2_bitmap, l2_idx): + if data_file_name is None: + struct.pack_into(">Q", cluster, idx * 8, cur_offset | QCOW_OFLAG_COPIED) + cur_offset += cluster_size + else: + struct.pack_into(">Q", cluster, idx * 8, (l2_idx * cluster_size) | QCOW_OFLAG_COPIED) + sys.stdout.buffer.write(cluster) + + ### Write data clusters + if data_file_name is None: + for idx in bitmap_iterator(l2_bitmap, total_data_clusters): + cluster = os.pread(fd, cluster_size, cluster_size * idx) + # If the last cluster is smaller than cluster_size pad it with zeroes + if len(cluster) < cluster_size: + cluster += bytes(cluster_size - len(cluster)) + sys.stdout.buffer.write(cluster) + + if not data_file_raw: + os.close(fd) + + +def main(): + # Command-line arguments + parser = argparse.ArgumentParser( + description="This program converts a QEMU disk image to qcow2 " + "and writes it to the standard output" + ) + parser.add_argument("input_file", help="name of the input file") + parser.add_argument( + "-f", + dest="input_format", + metavar="input_format", + help="format of the input file (default: raw)", + default="raw", + ) + parser.add_argument( + "-c", + dest="cluster_size", + metavar="cluster_size", + help=f"qcow2 cluster size (default: {QCOW2_DEFAULT_CLUSTER_SIZE})", + default=QCOW2_DEFAULT_CLUSTER_SIZE, + type=int, + choices=[1 << x for x in range(9, 22)], + ) + parser.add_argument( + "-r", + dest="refcount_bits", + metavar="refcount_bits", + help=f"width of the reference count entries (default: {QCOW2_DEFAULT_REFCOUNT_BITS})", + default=QCOW2_DEFAULT_REFCOUNT_BITS, + type=int, + choices=[1 << x for x in range(7)], + ) + parser.add_argument( + "-d", + dest="data_file", + help="create an image with input_file as an external data file", + action="store_true", + ) + parser.add_argument( + "-R", + dest="data_file_raw", + help="enable data_file_raw on the generated image (implies -d)", + action="store_true", + ) + args = parser.parse_args() + + if args.data_file_raw: + args.data_file = True + + if not os.path.isfile(args.input_file): + sys.exit(f"[Error] {args.input_file} does not exist or is not a regular file.") + + if args.data_file and args.input_format != "raw": + sys.exit("[Error] External data files can only be used with raw input images") + + # A 512 byte header is too small for the data file name extension + if args.data_file and args.cluster_size == 512: + sys.exit("[Error] External data files require a larger cluster size") + + if sys.stdout.isatty(): + sys.exit("[Error] Refusing to write to a tty. Try redirecting stdout.") + + if args.data_file: + data_file_name = args.input_file + else: + data_file_name = None + + with get_input_as_raw_file(args.input_file, args.input_format) as raw_file: + write_qcow2_content( + raw_file, + args.cluster_size, + args.refcount_bits, + data_file_name, + args.data_file_raw, + ) + + +if __name__ == "__main__": + main() From cbb698a2ba568f9746dee00ed59c442787103674 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 11 Mar 2025 14:17:15 +0100 Subject: [PATCH 2680/2892] qapi/block-core: Improve x-blockdev-change documentation The description of feature @unstable is three paragraphs. The second and third became part of the description by accident in commit 9fb49daabfb (qapi: Mark unstable QMP parts with feature 'unstable'). The second paragraph describes a defect in terms of the implementation. Fine, but doesn't belong into user-facing documentation. Turn it into a TODO section. Rewrite everything else for clarity and completeness. Signed-off-by: Markus Armbruster Message-ID: <20250311131715.1296101-1-armbru@redhat.com> Acked-by: Alberto Garcia --- qapi/block-core.json | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) 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:: From 8fad36626009ef51b52f4cdf08e29cc66284e41d Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:02 -0400 Subject: [PATCH 2681/2892] docs/qapi_domain: isolate TYPE_CHECKING imports When using the annotations feature, type hints do not need to be imported at runtime, only at type check time. Move type-check-only imports into a conditional to reduce the number of imports needed at runtime. Signed-off-by: John Snow Message-ID: <20250313044312.189276-2-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 7ff618d8cd..d52e7df7bc 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -9,15 +9,9 @@ from __future__ import annotations from typing import ( TYPE_CHECKING, - AbstractSet, - Any, - Dict, - Iterable, List, NamedTuple, - Optional, Tuple, - Union, cast, ) @@ -34,7 +28,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, @@ -49,13 +42,24 @@ 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__) From e36afc7bcc193da144f5d45c7c37eb62835b3ab1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:03 -0400 Subject: [PATCH 2682/2892] docs/qapi-domain: always store fully qualified name in signode Currently, only the definition name is stored in the tree metadata; but the node property is confusingly called "fullname". Rectify this by always storing the FQN in the tree metadata. ... While we're here, re-organize the code in preparation for namespace support to make it a bit easier to add additional components of the FQN. With this change, there is now extremely little code left that's taken directly from the Python domain :) Signed-off-by: John Snow Message-ID: <20250313044312.189276-3-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 64 +++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index d52e7df7bc..6b23fc73ef 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -178,6 +178,25 @@ 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) -> str: + modname = self.options.get( + "module", self.env.ref_context.get("qapi:module", "") + ) + assert isinstance(modname, str) + return modname + + def _get_fqn(self, name: Signature) -> str: + 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}" + return name + def add_target_and_index( self, name: Signature, sig: str, signode: desc_signature ) -> None: @@ -187,14 +206,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 @@ -213,18 +226,21 @@ class QAPIDescription(ParserFix): (arity, indextext, node_id, "", None) ) + @staticmethod + def split_fqn(name: str) -> Tuple[str, str]: + if "." in name: + module, name = name.split(".") + else: + module = "" + + return (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. @@ -235,13 +251,19 @@ class QAPIDescription(ParserFix): return "" config = self.env.app.config - *parents, name = toc_parts + 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}" + 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 "" @@ -312,11 +334,9 @@ 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") - ) + modname = self._get_context() - signode["fullname"] = sig + signode["fullname"] = self._get_fqn(sig) signode["module"] = modname sig_prefix = self.get_signature_prefix() if sig_prefix: From 74d40b011c27dc343ad56022a322d212135c96ed Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:04 -0400 Subject: [PATCH 2683/2892] docs/qapi_domain: add namespace support to FQN This patch adds a namespace component to the "Fully Qualified Name", in the form of "domain:module.name". As there are no namespace directives or options yet, this component will simply be empty as of this patch. Signed-off-by: John Snow Message-ID: <20250313044312.189276-4-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapi_domain.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 6b23fc73ef..48a082d489 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -178,15 +178,18 @@ 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) -> str: + 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", "") ) - assert isinstance(modname, str) - return modname + + return namespace, modname def _get_fqn(self, name: Signature) -> str: - modname = self._get_context() + 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! @@ -195,6 +198,8 @@ class QAPIDescription(ParserFix): if modname: name = f"{modname}.{name}" + if namespace: + name = f"{namespace}:{name}" return name def add_target_and_index( @@ -227,13 +232,18 @@ class QAPIDescription(ParserFix): ) @staticmethod - def split_fqn(name: str) -> Tuple[str, str]: + 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 (module, name) + return (ns, module, name) def _object_hierarchy_parts( self, sig_node: desc_signature @@ -251,7 +261,7 @@ class QAPIDescription(ParserFix): return "" config = self.env.app.config - modname, name = toc_parts + namespace, modname, name = toc_parts if config.toc_object_entries_show_parents == "domain": ret = name @@ -259,6 +269,10 @@ class QAPIDescription(ParserFix): "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 @@ -334,10 +348,15 @@ class QAPIObject(QAPIDescription): As such, the only argument here is "sig", which is just the QAPI definition name. """ - modname = self._get_context() + # No module or domain info allowed in the signature! + assert ":" not in sig + assert "." not in 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( From 9ca404f0043d63043bfed3af8da3adedc062cb13 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:05 -0400 Subject: [PATCH 2684/2892] docs/qapi-domain: add :namespace: override option Akin to the :module: override option, the :namespace: options allows you to forcibly override the contextual namespace associatied with a definition. We don't necessarily actually need this, but I felt compelled to stick close to how the Python domain works that offers context overrides. As of this commit, it is possible to add e.g. ":namespace: QMP" to any QAPI directive to forcibly associate that definition with a given namespace. Signed-off-by: John Snow Message-ID: <20250313044312.189276-5-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/qapi-domain.rst | 2 ++ docs/sphinx/qapi_domain.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index 1475870ca6..51b283277e 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -466,6 +466,8 @@ QAPI standard options All QAPI directives -- *except* for 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 diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 48a082d489..6485c43206 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -294,8 +294,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, From 7c7247b252dd8b3911b96451c0eaaebbc6ac0af0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:06 -0400 Subject: [PATCH 2685/2892] docs/qapi-domain: add qapi:namespace directive Add a new directive that marks the beginning of a QAPI "namespace", for example; "QMP", "QGA" or "QSD". This directive will associate all subsequent QAPI directives in a document with the specified namespace. This does not change the visual display of any of the definitions or index entries, but does change the "Fully Qualified Name" inside the QAPI domain's object table. This allows for two different "namespaces" to define entities with otherwise identical names -- which will come in handy for documenting both QEMU QMP and the QEMU Storage Daemon. Signed-off-by: John Snow Message-ID: <20250313044312.189276-6-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/qapi-domain.rst | 20 +++++++++++++++++++- docs/sphinx/qapi_domain.py | 13 +++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index 51b283277e..73e13ab45c 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -464,7 +464,8 @@ 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. @@ -482,6 +483,23 @@ 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. + +Example:: + + .. qapi:namespace:: QMP + + +This directive has no visible effect. + + qapi:module ----------- diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 6485c43206..a204af9b06 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -38,6 +38,7 @@ 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 @@ -645,6 +646,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. @@ -726,6 +738,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, From b1df602ebbd06d56311a77d195284216263b13f8 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:07 -0400 Subject: [PATCH 2686/2892] docs/qapidoc: add :namespace: option to qapi-doc directive Add a :namespace: option to the qapi-doc directive, which inserts a qapi:namespace directive into the start of the generated document. This, in turn, associates all auto-generated definitions by this directive with the specified namespace. The source info for these generated lines are credited to the start of the qapi-doc directive, which isn't precisely correct, but I wasn't sure how to get it more accurate without some re-parsing shenanigans. Signed-off-by: John Snow Message-ID: <20250313044312.189276-7-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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: From 7127e14f15fc52b436eb63e482a9f117bd4346d2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:08 -0400 Subject: [PATCH 2687/2892] docs/qapi_domain: add namespace support to cross-references This patch does three things: 1. Record the current namespace context in pending_xrefs so it can be used for link resolution later, 2. Pass that recorded namespace context to find_obj() when resolving a reference, and 3. Wildly and completely rewrite find_obj(). cross-reference support is expanded to tolerate the presence or absence of either namespace or module, and to cope with the presence or absence of contextual information for either. References now work like this: 1. If the explicit reference target is recorded in the domain's object registry, we link to that target and stop looking. We do this lookup regardless of how fully qualified the target is, which allows direct references to modules (which don't have a module component to their names) or direct references to definitions that may or may not belong to a namespace or module. 2. If contextual information is available from qapi:namespace or qapi:module directives, try using those components to find a direct match to the implied target name. 3. If both prior lookups fail, generate a series of regular expressions looking for wildcard matches in order from most to least specific. Any explicitly provided components (namespace, module) *must* match exactly, but both contextual and entirely omitted components are allowed to differ from the search result. Note that if more than one result is found, Sphinx will emit a warning (a build error for QEMU) and list all of the candidate references. The practical upshot is that in the large majority of cases, namespace and module information is not required when creating simple `references` to definitions from within the same context -- even when identical definitions exist in other contexts. Even when using simple `references` from elsewhere in the QEMU documentation manual, explicit namespace info is not required if there is only one definition by that name. Disambiguation *will* be required from outside of the QAPI documentation when referencing e.g. block-core definitions, which are shared between QEMU QMP and the QEMU Storage Daemon. In that case, there are two options: A: References can be made partially or fully explicit, e.g. `QMP:block-dirty-bitmap-add` will link to the QEMU version of the definition, while `QSD:block-dirty-bitmap-add` would link to the QSD version. B: If all of the references in a document are intended to go to the same place, you can insert a "qapi:namespace:: QMP" directive to influence the fuzzy-searching for later references. Signed-off-by: John Snow Message-ID: <20250313044312.189276-8-jsnow@redhat.com> Acked-by: Markus Armbruster [Commit message typo fixed] Signed-off-by: Markus Armbruster --- docs/devel/qapi-domain.rst | 34 ++++++++-- docs/sphinx/qapi_domain.py | 127 ++++++++++++++++++++++++------------- 2 files changed, 114 insertions(+), 47 deletions(-) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index 73e13ab45c..4705ba255e 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -400,11 +400,10 @@ Namespaces Mimicking the `Python domain target specification syntax `_, 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` @@ -413,6 +412,28 @@ different utilities; i.e. QEMU Storage Daemon vs QMP. will render to: :qapi:type:`~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 ---------------- @@ -492,6 +513,11 @@ 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 diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index a204af9b06..a8a85a2de3 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -7,6 +7,7 @@ QAPI domain extension. from __future__ import annotations +import re from typing import ( TYPE_CHECKING, List, @@ -94,6 +95,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 @@ -830,40 +832,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) @@ -871,25 +877,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 @@ -904,8 +942,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(), @@ -958,7 +997,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 From 25d44f57e17b088fdc4e38042e04c4e9da2c1088 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:09 -0400 Subject: [PATCH 2688/2892] docs/qapi-domain: add namespaced index support Generate an index-per-namespace for the QAPI domain. Due to a limitation with Sphinx's architecture, these indices must be defined during setup time and cannot be dynamically created on-demand when a namespace directive is encountered. Owing to that limitation, add a configuration value to conf.py that specifies which QAPI namespaces we'll generate indices for. Indices will be named after their namespace, e.g. the "QMP" namespace will generate to "qapi-qmp-index.html" and can be referenced using `qapi-qmp-index`. Signed-off-by: John Snow Message-ID: <20250313044312.189276-9-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/conf.py | 3 +++ docs/sphinx/qapi_domain.py | 51 +++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a3f9fa63d9..175491148c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -161,6 +161,9 @@ 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 = set() # -- Options for HTML output ---------------------------------------------- diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index a8a85a2de3..c94af5719c 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -8,11 +8,13 @@ QAPI domain extension. from __future__ import annotations import re +import types from typing import ( TYPE_CHECKING, List, NamedTuple, Tuple, + Type, cast, ) @@ -669,6 +671,7 @@ class QAPIIndex(Index): name = "index" localname = _("QAPI Index") shortname = _("QAPI Index") + namespace = "" def generate( self, @@ -678,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, "", "" ) ) @@ -704,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]), @@ -780,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, @@ -1019,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 { From 602c90beaedd9abbbf1535c8630293267e6b29c0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:10 -0400 Subject: [PATCH 2689/2892] docs: add QAPI namespace "QMP" to qemu-qmp-ref This also creates the qapi-qmp-index.html index and cross-reference target. Signed-off-by: John Snow Message-ID: <20250313044312.189276-10-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/conf.py | 4 +++- docs/interop/qemu-qmp-ref.rst | 1 + qapi/qapi-schema.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 175491148c..9a86e84a80 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -163,7 +163,9 @@ qapi_allowed_fields = { # 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 = set() +qapi_namespaces = { + "QMP", +} # -- Options for HTML output ---------------------------------------------- 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/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 From d85f7efe1f16c51b9c016ebc79f7c4081486642e Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:11 -0400 Subject: [PATCH 2690/2892] docs: disambiguate references in qapi-domain.rst Before we enable the QGA and QSD namespaces, we need to disambiguate some of the references that would become ambiguous as a result! Signed-off-by: John Snow Message-ID: <20250313044312.189276-11-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/qapi-domain.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index 4705ba255e..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 @@ -405,11 +405,11 @@ 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 @@ -444,7 +444,7 @@ using the ``custom text `` syntax. For example, ``:qapi:cmd:`Merge dirty bitmaps ``` will render as: :qapi:cmd:`Merge dirty -bitmaps ` +bitmaps ` Directives From a6af54434400099b8afd59ba036cf9a662006d1e Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 13 Mar 2025 00:43:12 -0400 Subject: [PATCH 2691/2892] docs: enable transmogrifier for QSD and QGA This also creates the `qapi-qsd-index` and `qapi-qga-index` QMP indices. Signed-off-by: John Snow Message-ID: <20250313044312.189276-12-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/conf.py | 2 ++ docs/interop/qemu-ga-ref.rst | 2 ++ docs/interop/qemu-storage-daemon-qmp-ref.rst | 2 ++ qga/qapi-schema.json | 3 +++ storage-daemon/qapi/qapi-schema.json | 8 ++++++++ 5 files changed, 17 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 9a86e84a80..7b5712e122 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -164,7 +164,9 @@ qapi_allowed_fields = { # 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/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-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/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 From eae0c3b659fbad5168c9bb9784b49d255185e35c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:05 +0000 Subject: [PATCH 2692/2892] target/arm: Move A32_BANKED_REG_{GET,SET} macros to cpregs.h The A32_BANKED_REG_{GET,SET} macros are only used inside target/arm; move their definitions to cpregs.h. There's no need to have them defined in all the code that includes cpu.h. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpregs.h | 28 ++++++++++++++++++++++++++++ target/arm/cpu.h | 27 --------------------------- 2 files changed, 28 insertions(+), 27 deletions(-) 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..15d3a79b0a 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2684,33 +2684,6 @@ static inline bool access_secure_reg(CPUARMState *env) 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)) - uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure); From 23560ada94bd22cb9e8d27b7e9389f6369f6d74d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:05 +0000 Subject: [PATCH 2693/2892] target/arm: Un-inline access_secure_reg() We would like to move arm_el_is_aa64() to internals.h; however, it is used by access_secure_reg(). Make that function not be inline, so that it can stay in cpu.h. access_secure_reg() is used only in two places: * in hflags.c * in the user-mode arm emulators, to decide whether to store the TLS value in the secure or non-secure banked field The second of these is not on a super-hot path that would care about the inlining (and incidentally will always use the NS banked field because our user-mode CPUs never set ARM_FEATURE_EL3); put the definition of access_secure_reg() in hflags.c, near its only use inside target/arm. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.h | 12 +++--------- target/arm/tcg/hflags.c | 9 +++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 15d3a79b0a..12d2706f2b 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2668,21 +2668,15 @@ static inline bool arm_el_is_aa64(CPUARMState *env, int el) 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; -} +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); 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) From fe0f88ab87632075ae9404685672a8f172a3ae6f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:05 +0000 Subject: [PATCH 2694/2892] linux-user/aarch64: Remove unused get/put_user macros At the top of linux-user/aarch64/cpu_loop.c we define a set of macros for reading and writing data and code words, but we never use these macros. Delete them. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 48 ----------------------------------- 1 file changed, 48 deletions(-) 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) { From 63d8b11d0aeb84ba53510cdf66612940a372451f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:06 +0000 Subject: [PATCH 2695/2892] linux-user/arm: Remove unused get_put_user macros In linux-user/arm/cpu_loop.c we define a full set of get/put macros for both code and data (since the endianness handling is different between the two). However the only one we actually use is get_user_code_u32(). Remove the rest. We leave a comment noting how data-side accesses should be handled for big-endian, because that's a subtle point and we just removed the macros that were effectively documenting it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- linux-user/arm/cpu_loop.c | 43 ++++----------------------------------- 1 file changed, 4 insertions(+), 39 deletions(-) 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. From fefc1220ad68d816627aebc393cbf2cb34ff6924 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:06 +0000 Subject: [PATCH 2696/2892] target/arm: Move arm_cpu_data_is_big_endian() etc to internals.h The arm_cpu_data_is_big_endian() and related functions are now used only in target/arm; they can be moved to internals.h. The motivation here is that we would like to move arm_current_el() to internals.h. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.h | 48 ------------------------------------------ target/arm/internals.h | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 12d2706f2b..8a59f70516 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3032,47 +3032,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" /* @@ -3258,13 +3217,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/internals.h b/target/arm/internals.h index bb96238919..c2c59e6030 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -392,6 +392,54 @@ static inline FloatRoundMode arm_rmode_to_sf(ARMFPRounding rmode) return arm_rmode_to_sf_map[rmode]; } +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) { From 2beb051191b526608e0f269559962f4d2f618850 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:07 +0000 Subject: [PATCH 2697/2892] target/arm: Move arm_current_el() and arm_el_is_aa64() to internals.h The functions arm_current_el() and arm_el_is_aa64() are used only in target/arm and in hw/intc/arm_gicv3_cpuif.c. They're functions that query internal state of the CPU. Move them out of cpu.h and into internals.h. This means we need to include internals.h in arm_gicv3_cpuif.c, but this is justifiable because that file is implementing the GICv3 CPU interface, which really is part of the CPU proper; we just ended up implementing it in code in hw/intc/ for historical reasons. The motivation for this move is that we'd like to change arm_el_is_aa64() to add a condition that uses cpu_isar_feature(); but we don't want to include cpu-features.h in cpu.h. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/intc/arm_gicv3_cpuif.c | 1 + target/arm/arch_dump.c | 1 + target/arm/cpu.h | 66 -------------------------------------- target/arm/internals.h | 67 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 66 deletions(-) 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/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/cpu.h b/target/arm/cpu.h index 8a59f70516..a8177c6c2e 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2635,39 +2635,6 @@ 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 * access the secure or non-secure bank of a cp register. When EL3 is @@ -2699,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 diff --git a/target/arm/internals.h b/target/arm/internals.h index c2c59e6030..d161a3e396 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -392,6 +392,73 @@ static inline FloatRoundMode arm_rmode_to_sf(ARMFPRounding rmode) return arm_rmode_to_sf_map[rmode]; } +/* 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; +} + +/* + * 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) { From 5d71c6820f3b91763b5807311969cc0362d457d9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:07 +0000 Subject: [PATCH 2698/2892] target/arm: SCR_EL3.RW should be treated as 1 if EL2 doesn't support AArch32 The definition of SCR_EL3.RW says that its effective value is 1 if: - EL2 is implemented and does not support AArch32, and SCR_EL3.NS is 1 - the effective value of SCR_EL3.{EEL2,NS} is {1,0} (i.e. we are Secure and Secure EL2 is disabled) We implement the second of these in arm_el_is_aa64(), but forgot the first. Provide a new function arm_scr_rw_eff() to return the effective value of SCR_EL3.RW, and use it in arm_el_is_aa64() and the other places that currently look directly at the bit value. (scr_write() enforces that the RW bit is RAO/WI if neither EL1 nor EL2 have AArch32 support, but if EL1 does but EL2 does not then the bit must still be writeable.) This will mean that if code at EL3 attempts to perform an exception return to AArch32 EL2 when EL2 is AArch64-only we will correctly handle this as an illegal exception return: it will be caught by the "return to an EL which is configured for a different register width" check in HELPER(exception_return). We do already have some CPU types which don't implement AArch32 above EL0, so this is technically a bug; it doesn't seem worth backporting to stable because no sensible guest code will be deliberately attempting to set the RW bit to a value corresponding to an unimplemented execution state and then checking that we did the right thing. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper.c | 4 ++-- target/arm/internals.h | 26 +++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index f0ead22937..3df7d5347c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9818,7 +9818,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 +10627,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 d161a3e396..28585c0755 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -392,6 +392,27 @@ 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) { @@ -411,9 +432,8 @@ static inline bool arm_el_is_aa64(CPUARMState *env, int el) 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 (arm_feature(env, ARM_FEATURE_EL3)) { + aa64 = aa64 && arm_scr_rw_eff(env); } if (el == 2) { From 1632a2017f682a9dc6ce51756b9765af07977873 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:51 -0800 Subject: [PATCH 2699/2892] migration: cpr_is_incoming Define the cpr_is_incoming helper, to be used in several cpr fix patches. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-2-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- include/migration/cpr.h | 1 + migration/cpr.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 3a6deb7933..7561fc75ad 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -21,6 +21,7 @@ int cpr_find_fd(const char *name, int id); MigMode cpr_get_incoming_mode(void); void cpr_set_incoming_mode(MigMode mode); +bool cpr_is_incoming(void); int cpr_state_save(MigrationChannel *channel, Error **errp); int cpr_state_load(MigrationChannel *channel, Error **errp); diff --git a/migration/cpr.c b/migration/cpr.c index 180faab247..42c46563e5 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -128,6 +128,11 @@ void cpr_set_incoming_mode(MigMode mode) incoming_mode = mode; } +bool cpr_is_incoming(void) +{ + return incoming_mode != MIG_MODE_NONE; +} + int cpr_state_save(MigrationChannel *channel, Error **errp) { int ret; From e56ba1878fefe7babff76ff399311ae5e399c5c5 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:52 -0800 Subject: [PATCH 2700/2892] pflash: fix cpr During normal migration, new QEMU creates and initializes memory regions, then loads the preserved contents of the region from vmstate. During CPR, memory regions are preserved in place, then the realize method initializes the regions contents, losing the old contents. To fix, skip the re-init during CPR. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-3-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- hw/block/block.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/block/block.c b/hw/block/block.c index 1d405e02bf..2e10611d95 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -12,6 +12,7 @@ #include "system/blockdev.h" #include "system/block-backend.h" #include "hw/block/block.h" +#include "migration/cpr.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" @@ -66,6 +67,10 @@ bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, int ret; g_autofree char *dev_id = NULL; + if (cpr_is_incoming()) { + return true; + } + blk_len = blk_getlength(blk); if (blk_len < 0) { error_setg_errno(errp, -blk_len, From b42f28111e081ab1fd370e92ee78a461027590f0 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:53 -0800 Subject: [PATCH 2701/2892] hw/loader: fix roms during cpr During normal migration, new QEMU creates and initializes memory regions, then loads the preserved contents of the region from vmstate. During CPR, memory regions are preserved in place, then the realize method initializes the regions contents, losing the old contents. To fix, skip the re-init during CPR. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-4-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- hw/core/loader.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 332b879a0b..ce6ff1b52e 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -51,6 +51,7 @@ #include "trace.h" #include "hw/hw.h" #include "disas/disas.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "monitor/monitor.h" #include "system/reset.h" @@ -1029,7 +1030,9 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) vmstate_register_ram_global(rom->mr); data = memory_region_get_ram_ptr(rom->mr); - memcpy(data, rom->data, rom->datasize); + if (!cpr_is_incoming()) { + memcpy(data, rom->data, rom->datasize); + } return data; } From 8ffe0623a1f40803feb4280fce13549baa4b0b47 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:54 -0800 Subject: [PATCH 2702/2892] hw/qxl: fix cpr During normal migration, new QEMU creates and initializes memory regions, then loads the preserved contents of the region from vmstate. During CPR, memory regions are preserved in place, then the realize method initializes the regions contents, losing the old contents. To fix, skip writes to the qxl memory regions during CPR load. Reported-by: andrey.drobyshev@virtuozzo.com Tested-by: andrey.drobyshev@virtuozzo.com Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-5-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- hw/display/qxl.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 2efdc77e61..da14da5209 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -30,6 +30,7 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "system/runstate.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "trace.h" @@ -333,6 +334,10 @@ static void init_qxl_rom(PCIQXLDevice *d) uint32_t fb; int i, n; + if (cpr_is_incoming()) { + goto skip_init; + } + memset(rom, 0, d->rom_size); rom->magic = cpu_to_le32(QXL_ROM_MAGIC); @@ -390,6 +395,7 @@ static void init_qxl_rom(PCIQXLDevice *d) sizeof(rom->client_monitors_config)); } +skip_init: d->shadow_rom = *rom; d->rom = rom; d->modes = modes; @@ -403,6 +409,9 @@ static void init_qxl_ram(PCIQXLDevice *d) buf = d->vga.vram_ptr; d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); + if (cpr_is_incoming()) { + return; + } d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); d->ram->int_pending = cpu_to_le32(0); d->ram->int_mask = cpu_to_le32(0); @@ -539,6 +548,10 @@ static void interface_set_compression_level(QXLInstance *sin, int level) trace_qxl_interface_set_compression_level(qxl->id, level); qxl->shadow_rom.compression_level = cpu_to_le32(level); + if (cpr_is_incoming()) { + assert(qxl->rom->compression_level == cpu_to_le32(level)); + return; + } qxl->rom->compression_level = cpu_to_le32(level); qxl_rom_set_dirty(qxl); } @@ -997,7 +1010,8 @@ static void interface_set_client_capabilities(QXLInstance *sin, } if (runstate_check(RUN_STATE_INMIGRATE) || - runstate_check(RUN_STATE_POSTMIGRATE)) { + runstate_check(RUN_STATE_POSTMIGRATE) || + cpr_is_incoming()) { return; } @@ -1200,6 +1214,10 @@ static void qxl_reset_state(PCIQXLDevice *d) { QXLRom *rom = d->rom; + if (cpr_is_incoming()) { + return; + } + qxl_check_state(d); d->shadow_rom.update_id = cpu_to_le32(0); *rom = d->shadow_rom; @@ -1370,8 +1388,11 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, memslot.virt_start = virt_start + (guest_start - pci_start); memslot.virt_end = virt_start + (guest_end - pci_start); memslot.addr_delta = memslot.virt_start - delta; - memslot.generation = d->rom->slot_generation = 0; - qxl_rom_set_dirty(d); + if (!cpr_is_incoming()) { + d->rom->slot_generation = 0; + qxl_rom_set_dirty(d); + } + memslot.generation = d->rom->slot_generation; qemu_spice_add_memslot(&d->ssd, &memslot, async); d->guest_slots[slot_id].mr = mr; From 39ec3fc030166c594a64d1d197e29fa9d100d4c5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:07 +0000 Subject: [PATCH 2703/2892] target/arm: HCR_EL2.RW should be RAO/WI if EL1 doesn't support AArch32 When EL1 doesn't support AArch32, the HCR_EL2.RW bit is supposed to be RAO/WI. Enforce the RAO/WI behaviour. Note that we handle "reset value should honour RES1 bits" in the same way that SCR_EL3 does, via a reset function. We do already have some CPU types which don't implement AArch32 above EL0, so this is technically a bug; it doesn't seem worth backporting to stable because no sensible guest code will be deliberately attempting to set the RW bit to a value corresponding to an unimplemented execution state and then checking that we did the right thing. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3df7d5347c..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, From 44ab8c248dee2d899dfe858ce1962fedcd3398a1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:08 +0000 Subject: [PATCH 2704/2892] target/arm: Add cpu local variable to exception_return helper We already call env_archcpu() multiple times within the exception_return helper function, and we're about to want to add another use of the ARMCPU pointer. Add a local variable cpu so we can call env_archcpu() just once. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-a64.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 32f0647ca4..e2bdf07833 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]; @@ -682,7 +683,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) } 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 +711,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 +750,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; From 097d68ac2fd9bd40d0b6a3b3992c86a1f79d7187 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2025 13:25:08 +0000 Subject: [PATCH 2705/2892] target/arm: Forbid return to AArch32 when CPU is AArch64-only In the Arm ARM, rule R_TYTWB states that returning to AArch32 is an illegal exception return if: * AArch32 is not supported at any exception level * the target EL is configured for AArch64 via SCR_EL3.RW or HCR_EL2.RW or via CPU state at reset We check the second of these, but not the first (which can only be relevant for the case of a return to EL0, because if AArch32 is not supported at one of the higher ELs then the RW bits will have an effective value of 1 and the the "configured for AArch64" condition will hold also). Add the missing condition. Although this is technically a bug (because we have one AArch64-only CPU: a64fx) it isn't worth backporting to stable because no sensible guest code will deliberately try to return to a nonexistent execution state to check that it gets an illegal exception return. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/helper-a64.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index e2bdf07833..9244848efe 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -678,6 +678,11 @@ 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; } From adb478a584dcf9c112fe8a6f9a7369162d3239fb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 15:28:38 +0000 Subject: [PATCH 2706/2892] MAINTAINERS: Fix status for Arm boards I "maintain" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm down as the only listed maintainer for quite a lot of Arm SoC and board types. In some cases this is only as the "maintainer of last resort" and I'm not in practice doing anything beyond patch review and the odd bit of tidyup. Move these entries in MAINTAINERS from "Maintained" to "Odd Fixes", to better represent reality. Entries for other boards and SoCs where I do more actively care (or where there is a listed co-maintainer) remain as they are. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250307152838.3226398-1-peter.maydell@linaro.org --- MAINTAINERS | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 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 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 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 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 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 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 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 From 5b14454d37854f5c4227d642133a477a07e49759 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Mar 2025 16:37:17 +0100 Subject: [PATCH 2708/2892] Revert "hw/char/pl011: Warn when using disabled receiver" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest does not control whether characters are sent on the UART. Sending them before the guest happens to boot will now result in a "guest error" log entry that is only because of timing, even if the guest _would_ later setup the receiver correctly. This reverts the bulk of commit abf2b6a028670bd2890bb3aee7e103fe53e4b0df, and instead adds a comment about why we don't check the enable bits. Cc: Philippe Mathieu-Daudé Cc: Peter Maydell Signed-off-by: Paolo Bonzini Message-id: 20250311153717.206129-1-pbonzini@redhat.com [PMM: expanded comment] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/pl011.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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; } From e6c38d2ab55d66c74ceade5699e22cabe9058d22 Mon Sep 17 00:00:00 2001 From: Joe Komlodi Date: Mon, 10 Mar 2025 20:36:22 +0000 Subject: [PATCH 2709/2892] util/cacheflush: Make first DSB unconditional on aarch64 On ARM hosts with CTR_EL0.DIC and CTR_EL0.IDC set, this would only cause an ISB to be executed during cache maintenance, which could lead to QEMU executing TBs containing garbage instructions. This seems to be because the ISB finishes executing instructions and flushes the pipeline, but the ISB doesn't guarantee that writes from the executed instructions are committed. If a small enough TB is created, it's possible that the writes setting up the TB aren't committed by the time the TB is executed. This function is intended to be a port of the gcc implementation (https://github.com/gcc-mirror/gcc/blob/85b46d0795ac76bc192cb8f88b646a647acf98c1/libgcc/config/aarch64/sync-cache.c#L67) which makes the first DSB unconditional, so we can fix the synchronization issue by doing that as well. Cc: qemu-stable@nongnu.org Fixes: 664a79735e4deb1 ("util: Specialize flush_idcache_range for aarch64") Signed-off-by: Joe Komlodi Message-id: 20250310203622.1827940-2-komlodi@google.com Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- util/cacheflush.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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. From 298a04998fa4a6dc977abe9234d98dfcdab98423 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 11:04:14 -0800 Subject: [PATCH 2710/2892] target/arm: Make DisasContext.{fp, sve}_access_checked tristate The check for fp_excp_el in assert_fp_access_checked is incorrect. For SME, with StreamingMode enabled, the access is really against the streaming mode vectors, and access to the normal fp registers is allowed to be disabled. C.f. sme_enabled_check. Convert sve_access_checked to match, even though we don't currently check the exception state. Cc: qemu-stable@nongnu.org Fixes: 3d74825f4d6 ("target/arm: Add SME enablement checks") Signed-off-by: Richard Henderson Message-id: 20250307190415.982049-2-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 17 +++++++++-------- target/arm/tcg/translate-a64.h | 2 +- target/arm/tcg/translate.h | 10 +++++++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8bef391bb0..48e0ac75b1 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; } @@ -1465,13 +1465,13 @@ bool sve_access_check(DisasContext *s) syn_sve_access_trap(), s->sve_excp_el); goto fail_exit; } - 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; + s->sve_access_checked = -1; 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). */ From cc7abc35dfa790ba6c20473c03745428c1c626b6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 7 Mar 2025 11:04:15 -0800 Subject: [PATCH 2711/2892] target/arm: Simplify pstate_sm check in sve_access_check In StreamingMode, fp_access_checked is handled already. We cannot fall through to fp_access_check lest we fall foul of the double-check assertion. Cc: qemu-stable@nongnu.org Fixes: 285b1d5fcef ("target/arm: Handle SME in sve_access_check") Signed-off-by: Richard Henderson Message-id: 20250307190415.982049-3-richard.henderson@linaro.org Reviewed-by: Peter Maydell [PMM: move declaration of 'ret' to top of block] Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 48e0ac75b1..39014325df 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -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 = 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 = -1; - return false; } /* From a019e15edfd62beae1e2f6adc0fa7415ba20b14c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Mar 2025 10:29:50 +0000 Subject: [PATCH 2712/2892] meson.build: Set RUST_BACKTRACE for all tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to capture potential Rust backtraces on panics in our test logs, which isn't Rust's default behaviour. Set RUST_BACKTRACE=1 in the add_test_setup environments, so that all our tests get run with this environment variable set. This makes the setting of that variable in the gitlab CI template redundant, so we can remove it. Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250310102950.3752908-1-peter.maydell@linaro.org --- .gitlab-ci.d/buildtest-template.yml | 1 - meson.build | 9 ++++++--- 2 files changed, 6 insertions(+), 4 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/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')) From b027f55a994af885a7a498a40373a2dcc2d8b15e Mon Sep 17 00:00:00 2001 From: Konstantin Shkolnyy Date: Fri, 21 Feb 2025 13:07:33 -0600 Subject: [PATCH 2713/2892] vdpa: Allow vDPA to work on big-endian machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add .set_vnet_le() function that always returns success, assuming that vDPA h/w always implements LE data format. Otherwise, QEMU disables vDPA and outputs the message: "backend does not support LE vnet headers; falling back on userspace virtio" Reviewed-by: Michael S. Tsirkin Acked-by: Eugenio Pérez Signed-off-by: Konstantin Shkolnyy Signed-off-by: Jason Wang --- net/vhost-vdpa.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index f7a54f46aa..7ca8b46eee 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -262,6 +262,18 @@ static bool vhost_vdpa_has_ufo(NetClientState *nc) } +/* + * FIXME: vhost_vdpa doesn't have an API to "set h/w endianness". But it's + * reasonable to assume that h/w is LE by default, because LE is what + * virtio 1.0 and later ask for. So, this function just says "yes, the h/w is + * LE". Otherwise, on a BE machine, higher-level code would mistakely think + * the h/w is BE and can't support VDPA for a virtio 1.0 client. + */ +static int vhost_vdpa_set_vnet_le(NetClientState *nc, bool enable) +{ + return 0; +} + static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc, Error **errp) { @@ -429,6 +441,7 @@ static NetClientInfo net_vhost_vdpa_info = { .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, .has_ufo = vhost_vdpa_has_ufo, + .set_vnet_le = vhost_vdpa_set_vnet_le, .check_peer_type = vhost_vdpa_check_peer_type, .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, }; From d18591157e5adf0e4491eed9b2c99828ba52bd80 Mon Sep 17 00:00:00 2001 From: Tigran Sogomonian Date: Fri, 27 Dec 2024 13:46:18 +0300 Subject: [PATCH 2714/2892] hw/misc: use extract64 instead of 1 << i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 << i is casted to uint64_t while bitwise and with val. So this value may become 0xffffffff80000000 but only 31th "start" bit is required. Use the bitfield extract() API instead. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Tigran Sogomonian Reviewed-by: Alex Bennée Link: https://lore.kernel.org/r/20241227104618.2526-1-tsogomonian@astralinux.ru Signed-off-by: Paolo Bonzini --- hw/misc/mps2-fpgaio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index d07568248d..04a3da5db0 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -198,7 +198,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, s->led0 = value & MAKE_64BIT_MASK(0, s->num_leds); for (i = 0; i < s->num_leds; i++) { - led_set_state(s->led[i], value & (1 << i)); + led_set_state(s->led[i], extract64(value, i, 1)); } } break; From f35432a4f699d8e450f65e44ddcd5911f2d8c146 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Mar 2025 08:53:45 +0100 Subject: [PATCH 2715/2892] Revert "meson.build: default to -gsplit-dwarf for debug info" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 563b1a35ed1f1151505d4fe5f723827d1b3fd4bc. Split debug info support is broken when cross compiling (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99973). People that would like to use it can add it via --extra-cflags. Reported-by: Konstantin Kostiuk Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- meson.build | 6 ------ meson_options.txt | 2 -- scripts/meson-buildoptions.sh | 2 -- 3 files changed, 10 deletions(-) diff --git a/meson.build b/meson.build index 7f75256acf..41f68d3806 100644 --- a/meson.build +++ b/meson.build @@ -604,10 +604,6 @@ if get_option('tsan') qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags endif -if get_option('debug') and get_option('split_debug') - qemu_cflags += '-gsplit-dwarf' -endif - # Detect support for PT_GNU_RELRO + DT_BIND_NOW. # The combination is known as "full relro", because .got.plt is read-only too. qemu_ldflags += cc.get_supported_link_arguments('-Wl,-z,relro', '-Wl,-z,now') @@ -4599,8 +4595,6 @@ if have_rust summary_info += {'bindgen': bindgen.full_path()} summary_info += {'bindgen version': bindgen.version()} endif -# option_cflags is purely for the summary display, meson will pass -# -g/-O options directly option_cflags = (get_option('debug') ? ['-g'] : []) if get_option('optimization') != 'plain' option_cflags += ['-O' + get_option('optimization')] diff --git a/meson_options.txt b/meson_options.txt index 3432123fee..59d973bca0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -362,8 +362,6 @@ option('debug_mutex', type: 'boolean', value: false, description: 'mutex debugging support') option('debug_stack_usage', type: 'boolean', value: false, description: 'measure coroutine stack usage') -option('split_debug', type: 'boolean', value: true, - description: 'split debug info from object files') option('qom_cast_debug', type: 'boolean', value: true, description: 'cast debugging support') option('slirp_smbd', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index aca6e68830..3e8e00852b 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -504,8 +504,6 @@ _meson_option_parse() { --disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;; --enable-strip) printf "%s" -Dstrip=true ;; --disable-strip) printf "%s" -Dstrip=false ;; - --enable-split-debug) printf "%s" -Dsplit_debug=true ;; - --disable-split-debug) printf "%s" -Dsplit_debug=false ;; --sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;; --enable-tcg) printf "%s" -Dtcg=enabled ;; --disable-tcg) printf "%s" -Dtcg=disabled ;; From 1dae461a913f9da88df05de6e2020d3134356f2e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 18 Mar 2025 10:18:14 -0400 Subject: [PATCH 2716/2892] Update version for v10.0.0-rc0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a318b6e843..b770373350 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.50 +9.2.90 From 672cb29d1e811180bf1aeefbcb0936ecd5bd3853 Mon Sep 17 00:00:00 2001 From: Santiago Monserrat Campanello Date: Wed, 5 Mar 2025 11:26:32 +0100 Subject: [PATCH 2717/2892] docs/about/emulation: Fix broken link semihosting link to risc-v changed Signed-off-by: Santiago Monserrat Campanello Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2717 Reviewed-by: Alistair Francis Reviewed-by: Thomas Huth Message-ID: <20250305102632.91376-1-santimonserr@gmail.com> Signed-off-by: Alistair Francis --- docs/about/emulation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 3bc3579434..a72591ee4d 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -171,7 +171,7 @@ for that architecture. - Unified Hosting Interface (MD01069) * - RISC-V - System and User-mode - - https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc + - https://github.com/riscv-non-isa/riscv-semihosting/blob/main/riscv-semihosting.adoc * - Xtensa - System - Tensilica ISS SIMCALL From 17288e38bebf20121c4aa20b264e661a7fa50ed8 Mon Sep 17 00:00:00 2001 From: Paolo Savini Date: Fri, 21 Feb 2025 15:53:20 +0000 Subject: [PATCH 2718/2892] optimize the memory probing for vector fault-only-first loads. Fault-only-first loads in the RISC-V vector extension need to update the vl with the element index that causes an exception. In order to ensure this the emulation of this instruction used to probe the memory covered by the load operation with a loop that iterated over each element so that when a flag was raised it was possible to set the vl to the corresponding element index. This loop was executed every time whether an exception happened or not. This commit removes the per element memory probing from the main execution path and adds a broad memory probing first. If this probing raises any flag that is not a watchpoint flag (that per standard is allowed by this instruction) we proceed with the per element probing to find the index of the element causing the exception and set vl to such index. Signed-off-by: Paolo Savini Reviewed-by: Daniel Henrique Barboza Message-ID: <20250221155320.59159-2-paolo.savini@embecosm.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 97 ++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7773df6a7c..71b823d5d4 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -633,47 +633,69 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, uint32_t esz = 1 << log2_esz; uint32_t msize = nf * esz; uint32_t vma = vext_vma(desc); - target_ulong addr, offset, remain, page_split, elems; + target_ulong addr, addr_probe, addr_i, offset, remain, page_split, elems; int mmu_index = riscv_env_mmu_index(env, false); + int flags; + void *host; VSTART_CHECK_EARLY_EXIT(env); - /* probe every access */ - for (i = env->vstart; i < env->vl; i++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - addr = adjust_addr(env, base + i * (nf << log2_esz)); - if (i == 0) { - /* Allow fault on first element. */ - probe_pages(env, addr, nf << log2_esz, ra, MMU_DATA_LOAD); - } else { - remain = nf << log2_esz; - while (remain > 0) { - void *host; - int flags; + addr = base + ((env->vstart * nf) << log2_esz); + page_split = -(addr | TARGET_PAGE_MASK); + /* Get number of elements */ + elems = page_split / msize; + if (unlikely(env->vstart + elems >= env->vl)) { + elems = env->vl - env->vstart; + } - offset = -(addr | TARGET_PAGE_MASK); + /* Check page permission/pmp/watchpoint/etc. */ + flags = probe_access_flags(env, adjust_addr(env, addr), elems * msize, + MMU_DATA_LOAD, mmu_index, true, &host, ra); - /* Probe nonfault on subsequent elements. */ - flags = probe_access_flags(env, addr, offset, MMU_DATA_LOAD, - mmu_index, true, &host, 0); + /* If we are crossing a page check also the second page. */ + if (env->vl > elems) { + addr_probe = addr + (elems << log2_esz); + flags |= probe_access_flags(env, adjust_addr(env, addr_probe), + elems * msize, MMU_DATA_LOAD, mmu_index, + true, &host, ra); + } - /* - * Stop if invalid (unmapped) or mmio (transaction may fail). - * Do not stop if watchpoint, as the spec says that - * first-fault should continue to access the same - * elements regardless of any watchpoint. - */ - if (flags & ~TLB_WATCHPOINT) { - vl = i; - goto ProbeSuccess; + if (flags & ~TLB_WATCHPOINT) { + /* probe every access */ + for (i = env->vstart; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, i)) { + continue; + } + addr_i = adjust_addr(env, base + i * (nf << log2_esz)); + if (i == 0) { + /* Allow fault on first element. */ + probe_pages(env, addr_i, nf << log2_esz, ra, MMU_DATA_LOAD); + } else { + remain = nf << log2_esz; + while (remain > 0) { + offset = -(addr_i | TARGET_PAGE_MASK); + + /* Probe nonfault on subsequent elements. */ + flags = probe_access_flags(env, addr_i, offset, + MMU_DATA_LOAD, mmu_index, true, + &host, 0); + + /* + * Stop if invalid (unmapped) or mmio (transaction may + * fail). Do not stop if watchpoint, as the spec says that + * first-fault should continue to access the same + * elements regardless of any watchpoint. + */ + if (flags & ~TLB_WATCHPOINT) { + vl = i; + goto ProbeSuccess; + } + if (remain <= offset) { + break; + } + remain -= offset; + addr_i = adjust_addr(env, addr_i + offset); } - if (remain <= offset) { - break; - } - remain -= offset; - addr = adjust_addr(env, addr + offset); } } } @@ -685,15 +707,6 @@ ProbeSuccess: if (env->vstart < env->vl) { if (vm) { - /* Calculate the page range of first page */ - addr = base + ((env->vstart * nf) << log2_esz); - page_split = -(addr | TARGET_PAGE_MASK); - /* Get number of elements */ - elems = page_split / msize; - if (unlikely(env->vstart + elems >= env->vl)) { - elems = env->vl - env->vstart; - } - /* Load/store elements in the first page */ if (likely(elems)) { vext_page_ldst_us(env, vd, addr, elems, nf, max_elems, From 86c78b280607fcff787866a03374047c65037a90 Mon Sep 17 00:00:00 2001 From: Deepak Gupta Date: Wed, 5 Mar 2025 22:46:35 -0800 Subject: [PATCH 2719/2892] target/riscv: fix access permission checks for CSR_SSP Commit:8205bc1 ("target/riscv: introduce ssp and enabling controls for zicfiss") introduced CSR_SSP but it mis-interpreted the spec on access to CSR_SSP in M-mode. Gated to CSR_SSP is not gated via `xSSE`. But rather rules clearly specified in section "22.2.1. Shadow Stack Pointer (ssp) CSR access contr" in the priv spec. Fixes: 8205bc127a83 ("target/riscv: introduce ssp and enabling controls for zicfiss". Thanks to Adam Zabrocki for bringing this to attention. Reported-by: Adam Zabrocki Signed-off-by: Deepak Gupta Reviewed-by: Alistair Francis Message-ID: <20250306064636.452396-1-debug@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 49566d3c08..8225e9bb4b 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -192,6 +192,11 @@ static RISCVException cfi_ss(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } + /* If ext implemented, M-mode always have access to SSP CSR */ + if (env->priv == PRV_M) { + return RISCV_EXCP_NONE; + } + /* if bcfi not active for current env, access to csr is illegal */ if (!cpu_get_bcfien(env)) { #if !defined(CONFIG_USER_ONLY) From d2c5759c8dd4c00195d4ebecc7d009e41df6baef Mon Sep 17 00:00:00 2001 From: Deepak Gupta Date: Wed, 5 Mar 2025 22:46:36 -0800 Subject: [PATCH 2720/2892] target/riscv: fixes a bug against `ssamoswap` behavior in M-mode Commit f06bfe3dc38c ("target/riscv: implement zicfiss instructions") adds `ssamoswap` instruction. `ssamoswap` takes the code-point from existing reserved encoding (and not a zimop like other shadow stack instructions). If shadow stack is not enabled (via xenvcfg.SSE) and effective priv is less than M then `ssamoswap` must result in an illegal instruction exception. However if effective priv is M, then `ssamoswap` results in store/AMO access fault. See Section "22.2.3. Shadow Stack Memory Protection" of priv spec. Fixes: f06bfe3dc38c ("target/riscv: implement zicfiss instructions") Reported-by: Ved Shanbhogue Signed-off-by: Deepak Gupta Reviewed-by: Alistair Francis Message-ID: <20250306064636.452396-2-debug@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvzicfiss.c.inc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index e3ebc4977c..b0096adcd0 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -15,6 +15,13 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ + +#define REQUIRE_ZICFISS(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicfiss) { \ + return false; \ + } \ +} while (0) + static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) { if (!ctx->bcfi_enabled) { @@ -77,6 +84,11 @@ static bool trans_ssrdp(DisasContext *ctx, arg_ssrdp *a) static bool trans_ssamoswap_w(DisasContext *ctx, arg_amoswap_w *a) { REQUIRE_A_OR_ZAAMO(ctx); + REQUIRE_ZICFISS(ctx); + if (ctx->priv == PRV_M) { + generate_exception(ctx, RISCV_EXCP_STORE_AMO_ACCESS_FAULT); + } + if (!ctx->bcfi_enabled) { return false; } @@ -97,6 +109,11 @@ static bool trans_ssamoswap_d(DisasContext *ctx, arg_amoswap_w *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); + REQUIRE_ZICFISS(ctx); + if (ctx->priv == PRV_M) { + generate_exception(ctx, RISCV_EXCP_STORE_AMO_ACCESS_FAULT); + } + if (!ctx->bcfi_enabled) { return false; } From 3ea8fb521d6161a64879b6f43fac46b4d80d2e39 Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Sun, 2 Mar 2025 01:37:51 +0800 Subject: [PATCH 2721/2892] hw/riscv/riscv-iommu: Fix process directory table walk The PPN field in a non-leaf PDT entry is positioned differently from that in a leaf PDT entry. The original implementation incorrectly used the leaf entry's PPN mask to extract the PPN from a non-leaf entry, leading to an erroneous page table walk. This commit introduces new macros to properly define the fields for non-leaf PDT entries and corrects the page table walk. Signed-off-by: Jason Chien Reviewed-by: Daniel Henrique Barboza Message-ID: <20250301173751.9446-1-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-bits.h | 6 +++++- hw/riscv/riscv-iommu.c | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index b7cb1bc736..1017d73fc6 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -415,12 +415,16 @@ enum riscv_iommu_fq_causes { #define RISCV_IOMMU_DC_MSIPTP_MODE_OFF 0 #define RISCV_IOMMU_DC_MSIPTP_MODE_FLAT 1 +/* 2.2 Process Directory Table */ +#define RISCV_IOMMU_PDTE_VALID BIT_ULL(0) +#define RISCV_IOMMU_PDTE_PPN RISCV_IOMMU_PPN_FIELD + /* Translation attributes fields */ #define RISCV_IOMMU_PC_TA_V BIT_ULL(0) #define RISCV_IOMMU_PC_TA_RESERVED GENMASK_ULL(63, 32) /* First stage context fields */ -#define RISCV_IOMMU_PC_FSC_PPN GENMASK_ULL(43, 0) +#define RISCV_IOMMU_PC_FSC_PPN RISCV_IOMMU_ATP_PPN_FIELD #define RISCV_IOMMU_PC_FSC_RESERVED GENMASK_ULL(59, 44) enum riscv_iommu_fq_ttypes { diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index d46beb2d64..76e0fcd873 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -1042,10 +1042,10 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } le64_to_cpus(&de); - if (!(de & RISCV_IOMMU_PC_TA_V)) { + if (!(de & RISCV_IOMMU_PDTE_VALID)) { return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; } - addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN)); + addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PDTE_PPN)); } riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); From ec6411a5251de3479d44c6e539d0e9596c68909b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 7 Mar 2025 09:46:02 -0300 Subject: [PATCH 2722/2892] target/riscv/csr.c: fix OVERFLOW_BEFORE_WIDEN in rmw_sctrdepth() Coverity found the following issue: >>> CID 1593156: Integer handling issues (OVERFLOW_BEFORE_WIDEN) >>> Potentially overflowing expression "0x10 << depth" with type "int" (32 bits, signed) is evaluated using 32-bit arithmetic, and then used in a context that expects an expression of type "uint64_t" (64 bits, unsigned). 4299 depth = 16 << depth; Fix it by forcing the expression to be 64 bits wide by using '16ULL'. Resolves: Coverity CID 1593156 Fixes: c48bd18eae ("target/riscv: Add support for Control Transfer Records extension CSRs.") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250307124602.1905754-1-dbarboza@ventanamicro.com> 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 8225e9bb4b..7948188356 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -4302,7 +4302,7 @@ static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno, } /* Update sctrstatus.WRPTR with a legal value */ - depth = 16 << depth; + depth = 16ULL << depth; env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1)); } From e83845316abcea9024eb5402a6c5eb8b092c79d5 Mon Sep 17 00:00:00 2001 From: Chao Liu Date: Mon, 10 Mar 2025 10:35:24 +0800 Subject: [PATCH 2723/2892] target/riscv: refactor VSTART_CHECK_EARLY_EXIT() to accept vl as a parameter Some vector instructions are special, such as the vlm.v instruction, where setting its vl actually sets evl = (vl + 7) >> 3. To improve maintainability, we will uniformly use VSTART_CHECK_EARLY_EXIT() to check for the condition vstart >= vl. This function will also handle cases involving evl. Fixes: df4252b2ec ("target/riscv/vector_helpers: do early exit when vstart >= vl") Signed-off-by: Chao Liu Reviewed-by: Daniel Henrique Barboza Message-ID: Signed-off-by: Alistair Francis --- target/riscv/vcrypto_helper.c | 32 +++++++-------- target/riscv/vector_helper.c | 69 ++++++++++++++++----------------- target/riscv/vector_internals.c | 4 +- target/riscv/vector_internals.h | 12 +++--- 4 files changed, 57 insertions(+), 60 deletions(-) diff --git a/target/riscv/vcrypto_helper.c b/target/riscv/vcrypto_helper.c index f7423df226..1526de96f5 100644 --- a/target/riscv/vcrypto_helper.c +++ b/target/riscv/vcrypto_helper.c @@ -222,7 +222,7 @@ static inline void xor_round_key(AESState *round_state, AESState *round_key) uint32_t total_elems = vext_get_total_elems(env, desc, 4); \ uint32_t vta = vext_vta(desc); \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { \ AESState round_key; \ @@ -248,7 +248,7 @@ static inline void xor_round_key(AESState *round_state, AESState *round_key) uint32_t total_elems = vext_get_total_elems(env, desc, 4); \ uint32_t vta = vext_vta(desc); \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { \ AESState round_key; \ @@ -309,7 +309,7 @@ void HELPER(vaeskf1_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, uint32_t total_elems = vext_get_total_elems(env, desc, 4); uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, vl); uimm &= 0b1111; if (uimm > 10 || uimm == 0) { @@ -357,7 +357,7 @@ void HELPER(vaeskf2_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, uint32_t total_elems = vext_get_total_elems(env, desc, 4); uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, vl); uimm &= 0b1111; if (uimm > 14 || uimm < 2) { @@ -465,7 +465,7 @@ void HELPER(vsha2ms_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { if (sew == MO_32) { @@ -582,7 +582,7 @@ void HELPER(vsha2ch32_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { vsha2c_32(((uint32_t *)vs2) + 4 * i, ((uint32_t *)vd) + 4 * i, @@ -602,7 +602,7 @@ void HELPER(vsha2ch64_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { vsha2c_64(((uint64_t *)vs2) + 4 * i, ((uint64_t *)vd) + 4 * i, @@ -622,7 +622,7 @@ void HELPER(vsha2cl32_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { vsha2c_32(((uint32_t *)vs2) + 4 * i, ((uint32_t *)vd) + 4 * i, @@ -642,7 +642,7 @@ void HELPER(vsha2cl64_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { vsha2c_64(((uint64_t *)vs2) + 4 * i, ((uint64_t *)vd) + 4 * i, @@ -676,7 +676,7 @@ void HELPER(vsm3me_vv)(void *vd_vptr, void *vs1_vptr, void *vs2_vptr, uint32_t *vs1 = vs1_vptr; uint32_t *vs2 = vs2_vptr; - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (int i = env->vstart / 8; i < env->vl / 8; i++) { uint32_t w[24]; @@ -777,7 +777,7 @@ void HELPER(vsm3c_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, uint32_t *vs2 = vs2_vptr; uint32_t v1[8], v2[8], v3[8]; - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (int i = env->vstart / 8; i < env->vl / 8; i++) { for (int k = 0; k < 8; k++) { @@ -802,7 +802,7 @@ void HELPER(vghsh_vv)(void *vd_vptr, void *vs1_vptr, void *vs2_vptr, uint32_t vta = vext_vta(desc); uint32_t total_elems = vext_get_total_elems(env, desc, 4); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { uint64_t Y[2] = {vd[i * 2 + 0], vd[i * 2 + 1]}; @@ -841,7 +841,7 @@ void HELPER(vgmul_vv)(void *vd_vptr, void *vs2_vptr, CPURISCVState *env, uint32_t vta = vext_vta(desc); uint32_t total_elems = vext_get_total_elems(env, desc, 4); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { uint64_t Y[2] = {brev8(vd[i * 2 + 0]), brev8(vd[i * 2 + 1])}; @@ -879,7 +879,7 @@ void HELPER(vsm4k_vi)(void *vd, void *vs2, uint32_t uimm5, CPURISCVState *env, uint32_t esz = sizeof(uint32_t); uint32_t total_elems = vext_get_total_elems(env, desc, esz); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = group_start; i < group_end; ++i) { uint32_t vstart = i * egs; @@ -937,7 +937,7 @@ void HELPER(vsm4r_vv)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) uint32_t esz = sizeof(uint32_t); uint32_t total_elems = vext_get_total_elems(env, desc, esz); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = group_start; i < group_end; ++i) { uint32_t vstart = i * egs; @@ -973,7 +973,7 @@ void HELPER(vsm4r_vs)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) uint32_t esz = sizeof(uint32_t); uint32_t total_elems = vext_get_total_elems(env, desc, esz); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (uint32_t i = group_start; i < group_end; ++i) { uint32_t vstart = i * egs; diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 71b823d5d4..217d2f60a5 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -260,7 +260,7 @@ vext_ldst_stride(void *vd, void *v0, target_ulong base, target_ulong stride, uint32_t esz = 1 << log2_esz; uint32_t vma = vext_vma(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); for (i = env->vstart; i < env->vl; env->vstart = ++i) { k = 0; @@ -383,10 +383,7 @@ vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, uint32_t msize = nf * esz; int mmu_index = riscv_env_mmu_index(env, false); - if (env->vstart >= evl) { - env->vstart = 0; - return; - } + VSTART_CHECK_EARLY_EXIT(env, evl); #if defined(CONFIG_USER_ONLY) /* @@ -544,7 +541,7 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, uint32_t esz = 1 << log2_esz; uint32_t vma = vext_vma(desc); - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); /* load bytes from guest memory */ for (i = env->vstart; i < env->vl; env->vstart = ++i) { @@ -638,7 +635,7 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, int flags; void *host; - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, env->vl); addr = base + ((env->vstart * nf) << log2_esz); page_split = -(addr | TARGET_PAGE_MASK); @@ -1116,7 +1113,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ @@ -1150,7 +1147,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -1187,7 +1184,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ @@ -1227,7 +1224,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -1325,7 +1322,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -1374,7 +1371,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -1438,7 +1435,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ @@ -1505,7 +1502,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -2054,7 +2051,7 @@ void HELPER(NAME)(void *vd, void *vs1, CPURISCVState *env, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ @@ -2080,7 +2077,7 @@ void HELPER(NAME)(void *vd, uint64_t s1, CPURISCVState *env, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ *((ETYPE *)vd + H(i)) = (ETYPE)s1; \ @@ -2105,7 +2102,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE *vt = (!vext_elem_mask(v0, i) ? vs2 : vs1); \ @@ -2131,7 +2128,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -2178,7 +2175,7 @@ vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, uint32_t vl, uint32_t vm, int vxrm, opivv2_rm_fn *fn, uint32_t vma, uint32_t esz) { - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, vl); for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { @@ -2305,7 +2302,7 @@ vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, uint32_t vl, uint32_t vm, int vxrm, opivx2_rm_fn *fn, uint32_t vma, uint32_t esz) { - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, vl); for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { @@ -3104,7 +3101,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -3149,7 +3146,7 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -3737,7 +3734,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ if (vl == 0) { \ return; \ @@ -4260,7 +4257,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ @@ -4302,7 +4299,7 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -4497,7 +4494,7 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -4827,7 +4824,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t i; \ int a, b; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ a = vext_elem_mask(vs1, i); \ @@ -5022,7 +5019,7 @@ void HELPER(NAME)(void *vd, void *v0, CPURISCVState *env, uint32_t desc) \ uint32_t vma = vext_vma(desc); \ int i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -5059,7 +5056,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vma = vext_vma(desc); \ target_ulong offset = s1, i_min, i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ i_min = MAX(env->vstart, offset); \ for (i = i_min; i < vl; i++) { \ @@ -5094,7 +5091,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vma = vext_vma(desc); \ target_ulong i_max, i_min, i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ i_min = MIN(s1 < vlmax ? vlmax - s1 : 0, vl); \ i_max = MAX(i_min, env->vstart); \ @@ -5138,7 +5135,7 @@ static void vslide1up_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -5189,7 +5186,7 @@ static void vslide1down_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -5266,7 +5263,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint64_t index; \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -5311,7 +5308,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint64_t index = s1; \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -5407,7 +5404,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ diff --git a/target/riscv/vector_internals.c b/target/riscv/vector_internals.c index 05b2d01e58..b490b1d398 100644 --- a/target/riscv/vector_internals.c +++ b/target/riscv/vector_internals.c @@ -66,7 +66,7 @@ void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, uint32_t vma = vext_vma(desc); uint32_t i; - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, vl); for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { @@ -92,7 +92,7 @@ void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, uint32_t vma = vext_vma(desc); uint32_t i; - VSTART_CHECK_EARLY_EXIT(env); + VSTART_CHECK_EARLY_EXIT(env, vl); for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { diff --git a/target/riscv/vector_internals.h b/target/riscv/vector_internals.h index a11cc8366d..8eee7e5c31 100644 --- a/target/riscv/vector_internals.h +++ b/target/riscv/vector_internals.h @@ -25,11 +25,11 @@ #include "tcg/tcg-gvec-desc.h" #include "internals.h" -#define VSTART_CHECK_EARLY_EXIT(env) do { \ - if (env->vstart >= env->vl) { \ - env->vstart = 0; \ - return; \ - } \ +#define VSTART_CHECK_EARLY_EXIT(env, vl) do { \ + if (env->vstart >= vl) { \ + env->vstart = 0; \ + return; \ + } \ } while (0) static inline uint32_t vext_nf(uint32_t desc) @@ -159,7 +159,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ - VSTART_CHECK_EARLY_EXIT(env); \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ From 4e9e2478dfd26480bbf50367a67b9be0edafef2b Mon Sep 17 00:00:00 2001 From: Chao Liu Date: Mon, 10 Mar 2025 10:35:25 +0800 Subject: [PATCH 2724/2892] target/riscv: fix handling of nop for vstart >= vl in some vector instruction Recently, when I was writing a RISCV test, I found that when VL is set to 0, the instruction should be nop, but when I tested it, I found that QEMU will treat all elements as tail elements, and in the case of VTA=1, write all elements to 1. After troubleshooting, it was found that the vext_vx_rm_1 function was called in the vext_vx_rm_2, and then the vext_set_elems_1s function was called to process the tail element, but only VSTART >= vl was checked in the vext_vx_rm_1 function, which caused the tail element to still be processed even if it was returned in advance. So I've made the following change: Put VSTART_CHECK_EARLY_EXIT(env) at the beginning of the vext_vx_rm_2 function, so that the VSTART register is checked correctly. Fixes: df4252b2ec ("target/riscv/vector_helpers: do early exit when vstart >= vl") Signed-off-by: Chao Liu Reviewed-by: Daniel Henrique Barboza Message-ID: Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 217d2f60a5..67b3bafebb 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -2175,8 +2175,6 @@ vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, uint32_t vl, uint32_t vm, int vxrm, opivv2_rm_fn *fn, uint32_t vma, uint32_t esz) { - VSTART_CHECK_EARLY_EXIT(env, vl); - for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { /* set masked-off elements to 1s */ @@ -2200,6 +2198,8 @@ vext_vv_rm_2(void *vd, void *v0, void *vs1, void *vs2, uint32_t vta = vext_vta(desc); uint32_t vma = vext_vma(desc); + VSTART_CHECK_EARLY_EXIT(env, vl); + switch (env->vxrm) { case 0: /* rnu */ vext_vv_rm_1(vd, v0, vs1, vs2, @@ -2302,8 +2302,6 @@ vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, uint32_t vl, uint32_t vm, int vxrm, opivx2_rm_fn *fn, uint32_t vma, uint32_t esz) { - VSTART_CHECK_EARLY_EXIT(env, vl); - for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { /* set masked-off elements to 1s */ @@ -2327,6 +2325,8 @@ vext_vx_rm_2(void *vd, void *v0, target_long s1, void *vs2, uint32_t vta = vext_vta(desc); uint32_t vma = vext_vma(desc); + VSTART_CHECK_EARLY_EXIT(env, vl); + switch (env->vxrm) { case 0: /* rnu */ vext_vx_rm_1(vd, v0, s1, vs2, @@ -4662,6 +4662,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t i; \ TD s1 = *((TD *)vs1 + HD(0)); \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -4750,6 +4752,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t i; \ TD s1 = *((TD *)vs1 + HD(0)); \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -4914,6 +4918,8 @@ static void vmsetm(void *vd, void *v0, void *vs2, CPURISCVState *env, int i; bool first_mask_bit = false; + VSTART_CHECK_EARLY_EXIT(env, vl); + for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { /* set masked-off elements to 1s */ @@ -4986,6 +4992,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, CPURISCVState *env, \ uint32_t sum = 0; \ int i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -5344,6 +5352,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vta = vext_vta(desc); \ uint32_t num = 0, i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vext_elem_mask(vs1, i)) { \ continue; \ From c1a6bff276ca52ffde472532d92bb5bb122dab3f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 13 Mar 2025 07:35:22 +0100 Subject: [PATCH 2725/2892] hw/virtio: Also include md stubs in case CONFIG_VIRTIO_PCI is not set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the s390x target, it's possible to build the QEMU binary without CONFIG_VIRTIO_PCI and only have the virtio-mem device via the ccw transport. In that case, QEMU currently fails to link correctly: /usr/bin/ld: libqemu-s390x-softmmu.a.p/hw_s390x_s390-virtio-ccw.c.o: in function `s390_machine_device_pre_plug': ../hw/s390x/s390-virtio-ccw.c:579:(.text+0x1e96): undefined reference to `virtio_md_pci_pre_plug' /usr/bin/ld: libqemu-s390x-softmmu.a.p/hw_s390x_s390-virtio-ccw.c.o: in function `s390_machine_device_plug': ../hw/s390x/s390-virtio-ccw.c:608:(.text+0x21a4): undefined reference to `virtio_md_pci_plug' /usr/bin/ld: libqemu-s390x-softmmu.a.p/hw_s390x_s390-virtio-ccw.c.o: in function `s390_machine_device_unplug_request': ../hw/s390x/s390-virtio-ccw.c:622:(.text+0x2334): undefined reference to `virtio_md_pci_unplug_request' /usr/bin/ld: libqemu-s390x-softmmu.a.p/hw_s390x_s390-virtio-ccw.c.o: in function `s390_machine_device_unplug': ../hw/s390x/s390-virtio-ccw.c:633:(.text+0x2436): undefined reference to `virtio_md_pci_unplug' clang: error: linker command failed with exit code 1 (use -v to see invocation) We also need to include the stubs when CONFIG_VIRTIO_PCI is missing. Fixes: aa910c20ec5 ("s390x: virtio-mem support") Message-ID: <20250313063522.1348288-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/virtio/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 19b04c4d9c..164f6fd995 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -90,7 +90,8 @@ specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: system_virtio_ss) system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) -system_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-md-stubs.c')) +system_ss.add(when: ['CONFIG_VIRTIO_MD', 'CONFIG_VIRTIO_PCI'], + if_false: files('virtio-md-stubs.c')) system_ss.add(files('virtio-hmp-cmds.c')) From 6d19d095470221684afe065cffd40adebe644d04 Mon Sep 17 00:00:00 2001 From: Niek Linnenbank Date: Sun, 16 Mar 2025 22:02:31 +0100 Subject: [PATCH 2726/2892] tests/functional/test_arm_orangepi: rename test class to 'OrangePiMachine' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test class in this file contains all functional test cases for testing the Orange Pi PC board. It should be given a name matching the Qemu machine it covers. This commit sets the test class name to 'OrangePiMachine'. Signed-off-by: Niek Linnenbank Fixes: 380f7268b7b ("tests/functional: Convert the OrangePi tests to the functional framework") Message-ID: <20250316210232.46298-1-nieklinnenbank@gmail.com> Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- tests/functional/test_arm_orangepi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index 18ee50216b..28919391e5 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -14,7 +14,7 @@ from qemu_test import wait_for_console_pattern, skipBigDataTest from qemu_test.utils import image_pow2ceil_expand -class BananaPiMachine(LinuxKernelTest): +class OrangePiMachine(LinuxKernelTest): ASSET_DEB = Asset( ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' From 23686dfb763d22d95c05c49382116d77133637b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 17 Mar 2025 12:43:00 +0000 Subject: [PATCH 2727/2892] tests/functional: remove all class level fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of fields are set at the class level on QemuBaseTest, even though the exact same named field is then set at the object level later in most cases. The 'self.logger' initialization in ACPI bits test needs to be removed since 'self.log' won't exist at that point in the flow. It already initialized 'self.logger' later in the setUp() method, so the __init__ method was redundant. Signed-off-by: Daniel P. Berrangé Message-ID: <20250317124300.84266-1-berrange@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 6 ------ tests/functional/test_acpi_bits.py | 1 - 2 files changed, 7 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 50d232a7c6..50c401b8c3 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -33,12 +33,6 @@ from .uncompress import uncompress class QemuBaseTest(unittest.TestCase): - arch = None - - workdir = None - log = None - logdir = None - ''' @params compressed: filename, Asset, or file-like object to uncompress @params format: optional compression format (gzip, lzma) diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index 20da435687..8e0563a97b 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -119,7 +119,6 @@ class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attribute self._debugcon_addr = '0x403' self._debugcon_log = 'debugcon-log.txt' - self.logger = self.log def _print_log(self, log): self.logger.info('\nlogs from biosbits follows:') From cef6da5bc9c63871814f544e0af2be22fa60aec3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Mar 2025 06:54:15 +0100 Subject: [PATCH 2728/2892] docs/system: Use the meson binary from the pyvenv To avoid problems with the meson installation from the host system, we should always use the meson from our venv instead. Thus use this in the documentation, too. While we're at it, also mention that it has to be run from the build folder (in the igb.rst file; the other two files were already fine). Suggested-by: Niek Linnenbank Message-ID: <20250318055415.16501-1-thuth@redhat.com> Reviewed-by: Akihiko Odaki Reviewed-by: Niek Linnenbank Signed-off-by: Thomas Huth --- docs/system/arm/bananapi_m2u.rst | 2 +- docs/system/arm/orangepi.rst | 2 +- docs/system/devices/igb.rst | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/system/arm/bananapi_m2u.rst b/docs/system/arm/bananapi_m2u.rst index d30db8d04c..6efa222c16 100644 --- a/docs/system/arm/bananapi_m2u.rst +++ b/docs/system/arm/bananapi_m2u.rst @@ -136,4 +136,4 @@ provide the following command: $ cd qemu-build-dir $ QEMU_TEST_ALLOW_LARGE_STORAGE=1 \ - meson test --suite thorough func-arm-arm_bpim2u + pyvenv/bin/meson test --suite thorough func-arm-arm_bpim2u diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index 8b9448ca7b..716062fca9 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -262,4 +262,4 @@ provide the following command from the build directory: .. code-block:: bash $ QEMU_TEST_ALLOW_LARGE_STORAGE=1 \ - meson test --suite thorough func-arm-arm_orangepi + pyvenv/bin/meson test --suite thorough func-arm-arm_orangepi diff --git a/docs/system/devices/igb.rst b/docs/system/devices/igb.rst index 9145af5c75..71f31cb116 100644 --- a/docs/system/devices/igb.rst +++ b/docs/system/devices/igb.rst @@ -57,11 +57,12 @@ directory: meson test qtest-x86_64/qos-test ethtool can test register accesses, interrupts, etc. It is automated as an -functional test and can be ran with the following command: +functional test and can be run from the build directory with the following +command: .. code:: shell - meson test --suite thorough func-x86_64-netdev_ethtool + pyvenv/bin/meson test --suite thorough func-x86_64-netdev_ethtool References ========== From a6836b6d49a76b71c43e9571f8bcc6a73b542a77 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Mar 2025 07:14:20 +0100 Subject: [PATCH 2729/2892] docs/system/arm: Use "functional tests" instead of "integration tests" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't use the term "integration tests" for these kind of tests anymore, it's "functional tests" nowadays. Suggested-by: Niek Linnenbank Message-ID: <20250318061420.20378-1-thuth@redhat.com> Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Niek Linnenbank Signed-off-by: Thomas Huth --- docs/system/arm/bananapi_m2u.rst | 6 +++--- docs/system/arm/orangepi.rst | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/system/arm/bananapi_m2u.rst b/docs/system/arm/bananapi_m2u.rst index 6efa222c16..03cc5618c3 100644 --- a/docs/system/arm/bananapi_m2u.rst +++ b/docs/system/arm/bananapi_m2u.rst @@ -125,10 +125,10 @@ And then boot it. $ qemu-system-arm -M bpim2u -nographic -sd sd.img -Banana Pi M2U integration tests -""""""""""""""""""""""""""""""" +Banana Pi M2U functional tests +"""""""""""""""""""""""""""""" -The Banana Pi M2U machine has several integration tests included. +The Banana Pi M2U machine has several functional tests included. To run the whole set of tests, build QEMU from source and simply provide the following command: diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index 716062fca9..d81f6c3bfd 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -252,10 +252,10 @@ and set the following environment variables before booting: Optionally you may save the environment variables to SD card with 'saveenv'. To continue booting simply give the 'boot' command and NetBSD boots. -Orange Pi PC integration tests -"""""""""""""""""""""""""""""" +Orange Pi PC functional tests +""""""""""""""""""""""""""""" -The Orange Pi PC machine has several integration tests included. +The Orange Pi PC machine has several functional tests included. To run the whole set of tests, build QEMU from source and simply provide the following command from the build directory: From 1a010d22b7adecf0fb1c069e1e535af1aa51e9cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Mar 2025 14:58:40 -0800 Subject: [PATCH 2730/2892] linux-user/riscv: Fix handling of cpu mask in riscv_hwprobe syscall The third argument of the syscall contains the size of the cpu mask in bytes, not bits. Nor is the size rounded up to a multiple of sizeof(abi_ulong). Cc: qemu-stable@nongnu.org Reported-by: Andreas Schwab Fixes: 9e1c7d982d7 ("linux-user/riscv: Add syscall riscv_hwprobe") Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Message-ID: <20250308225902.1208237-3-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- linux-user/syscall.c | 55 +++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b32de763f7..8bfe4912e1 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9119,35 +9119,38 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, } } -static int cpu_set_valid(abi_long arg3, abi_long arg4) +/* + * If the cpumask_t of (target_cpus, cpusetsize) cannot be read: -EFAULT. + * If the cpumast_t has no bits set: -EINVAL. + * Otherwise the cpumask_t contains some bit set: 0. + * Unlike the kernel, we do not mask cpumask_t by the set of online cpus, + * nor bound the search by cpumask_size(). + */ +static int nonempty_cpu_set(abi_ulong cpusetsize, abi_ptr target_cpus) { - int ret, i, tmp; - size_t host_mask_size, target_mask_size; - unsigned long *host_mask; + unsigned char *p = lock_user(VERIFY_READ, target_cpus, cpusetsize, 1); + int ret = -TARGET_EFAULT; - /* - * cpu_set_t represent CPU masks as bit masks of type unsigned long *. - * arg3 contains the cpu count. - */ - tmp = (8 * sizeof(abi_ulong)); - target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong); - host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) & - ~(sizeof(*host_mask) - 1); - - host_mask = alloca(host_mask_size); - - ret = target_to_host_cpu_mask(host_mask, host_mask_size, - arg4, target_mask_size); - if (ret != 0) { - return ret; - } - - for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) { - if (host_mask[i] != 0) { - return 0; + if (p) { + ret = -TARGET_EINVAL; + /* + * Since we only care about the empty/non-empty state of the cpumask_t + * not the individual bits, we do not need to repartition the bits + * from target abi_ulong to host unsigned long. + * + * Note that the kernel does not round up cpusetsize to a multiple of + * sizeof(abi_ulong). After bounding cpusetsize by cpumask_size(), + * it copies exactly cpusetsize bytes into a zeroed buffer. + */ + for (abi_ulong i = 0; i < cpusetsize; ++i) { + if (p[i]) { + ret = 0; + break; + } } + unlock_user(p, target_cpus, 0); } - return -TARGET_EINVAL; + return ret; } static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, @@ -9164,7 +9167,7 @@ static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, /* check cpu_set */ if (arg3 != 0) { - ret = cpu_set_valid(arg3, arg4); + ret = nonempty_cpu_set(arg3, arg4); if (ret != 0) { return ret; } From bcbd8c0edf712a7d55d9bf020be4b0f21ce3c35c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Mar 2025 10:20:21 +0100 Subject: [PATCH 2731/2892] docs/devel/testing/functional: Add a section about logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should tell the users where to find the log file. While we're at it, also rename the "Overview" heading to a more accurate "Introduction to writing tests" instead. Reported-by: Aditya Gupta Message-ID: <20250318092021.53719-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index a9fa45eac1..9bc973392a 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -72,8 +72,26 @@ files around on disk by setting ```QEMU_TEST_KEEP_SCRATCH=1``` as an env variable. Any preserved files will be deleted the next time the test is run without this variable set. -Overview --------- +Logging +------- + +The framework collects log files for each test in the build directory +in the following subfolder:: + + /tests/functional//../ + +There are usually three log files: + +* ``base.log`` contains the generic logging information that is written + by the calls to the logging functions in the test code (e.g. by calling + the ``self.log.info()`` or ``self.log.debug()`` functions). +* ``console.log`` contains the output of the serial console of the guest. +* ``default.log`` contains the output of QEMU. This file could be named + differently if the test chooses to use a different identifier for + the guest VM (e.g. when the test spins up multiple VMs). + +Introduction to writing tests +----------------------------- The ``tests/functional/qemu_test`` directory provides the ``qemu_test`` Python module, containing the ``qemu_test.QemuSystemTest`` class. From 1ddd4d3f6ed39ffd1e0cc03ada36b0766d5be9fb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Mar 2025 18:15:30 +0100 Subject: [PATCH 2732/2892] tests/functional/test_x86_64_kvm_xen: Remove avocado tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They have been forgotten to be removed when converting the test to the functional framework. Since they are of no use anymore, let's remove them now. Message-ID: <20250318171530.94966-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/test_x86_64_kvm_xen.py | 28 ------------------------- 1 file changed, 28 deletions(-) diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/test_x86_64_kvm_xen.py index 0298c96c2e..3bedef6c98 100755 --- a/tests/functional/test_x86_64_kvm_xen.py +++ b/tests/functional/test_x86_64_kvm_xen.py @@ -80,10 +80,6 @@ class KVMXenGuest(QemuSystemTest): wait_for_console_pattern(self, '#', 'Oops') def test_kvm_xen_guest(self): - """ - :avocado: tags=kvm_xen_guest - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + @@ -94,10 +90,6 @@ class KVMXenGuest(QemuSystemTest): 'virtio0-output') def test_kvm_xen_guest_nomsi(self): - """ - :avocado: tags=kvm_xen_guest_nomsi - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + @@ -108,10 +100,6 @@ class KVMXenGuest(QemuSystemTest): 'virtio0') def test_kvm_xen_guest_noapic_nomsi(self): - """ - :avocado: tags=kvm_xen_guest_noapic_nomsi - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + @@ -122,10 +110,6 @@ class KVMXenGuest(QemuSystemTest): 'virtio0') def test_kvm_xen_guest_vapic(self): - """ - :avocado: tags=kvm_xen_guest_vapic - """ - self.common_vm_setup() self.vm.add_args('-cpu', 'host,+xen-vapic') self.kernel_params = (self.KERNEL_DEFAULT + @@ -140,10 +124,6 @@ class KVMXenGuest(QemuSystemTest): 'virtio0-output') def test_kvm_xen_guest_novector(self): - """ - :avocado: tags=kvm_xen_guest_novector - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks' + @@ -154,10 +134,6 @@ class KVMXenGuest(QemuSystemTest): 'fasteoi') def test_kvm_xen_guest_novector_nomsi(self): - """ - :avocado: tags=kvm_xen_guest_novector_nomsi - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + @@ -169,10 +145,6 @@ class KVMXenGuest(QemuSystemTest): 'IO-APIC') def test_kvm_xen_guest_novector_noapic(self): - """ - :avocado: tags=kvm_xen_guest_novector_noapic - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks' + From 1c89dfefc4c33295126208225f202f39b5a234c3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 12 Mar 2025 11:11:31 +0100 Subject: [PATCH 2733/2892] cryptodev: Fix error handling in cryptodev_lkcf_execute_task() When cryptodev_lkcf_set_op_desc() fails, we report an error, but continue anyway. This is wrong. We then pass a non-null @local_error to various functions, which could easily fail error_setv()'s assertion on failure. Fail the function instead. When qcrypto_akcipher_new() fails, we fail the function without reporting the error. This leaks the Error object. Add the missing error reporting. This also frees the Error object. Signed-off-by: Markus Armbruster Message-ID: <20250312101131.1615777-1-armbru@redhat.com> Reviewed-by: zhenwei pi --- backends/cryptodev-lkcf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c index 41cf24b737..352c3e8958 100644 --- a/backends/cryptodev-lkcf.c +++ b/backends/cryptodev-lkcf.c @@ -330,6 +330,8 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc, sizeof(op_desc), &local_error) != 0) { error_report_err(local_error); + status = -VIRTIO_CRYPTO_ERR; + goto out; } else { key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key", p8info, p8info_len, KCTL_KEY_RING); @@ -346,6 +348,7 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) session->key, session->keylen, &local_error); if (!akcipher) { + error_report_err(local_error); status = -VIRTIO_CRYPTO_ERR; goto out; } From 1dd24ccf829db234e23156c68b013e038040bf94 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 12 Mar 2025 15:35:04 +0100 Subject: [PATCH 2734/2892] error: Strip trailing '\n' from an error string argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tracked down with scripts/coccinelle/err-bad-newline.cocci. Signed-off-by: Markus Armbruster Message-ID: <20250312143504.1659061-1-armbru@redhat.com> Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Song Gao --- net/vmnet-common.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 54d900ba67..ab33ce2b0c 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -94,7 +94,7 @@ ssize_t vmnet_receive_common(NetClientState *nc, if_status = vmnet_write(s->vmnet_if, &packet, &pkt_cnt); if (if_status != VMNET_SUCCESS) { - error_report("vmnet: write error: %s\n", + error_report("vmnet: write error: %s", vmnet_status_map_str(if_status)); return -1; } From ffe4db11f8aed79c7ec7d3ebd92674a1cfab4fe7 Mon Sep 17 00:00:00 2001 From: Yu-Ming Chang Date: Thu, 13 Mar 2025 14:07:58 +0800 Subject: [PATCH 2735/2892] target/riscv: Add check for 16-bit aligned PC for different priv versions. For privilege version 1.12 or newer, C always implies Zca. We can only check ext_zca to allow 16-bit aligned PC addresses. For older privilege versions, we only check C. Signed-off-by: Yu-Ming Chang Reviewed-by: Alistair Francis Message-ID: <174184718265.10540.10120024221661781046-0@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 12 ++++++++++++ target/riscv/insn_trans/trans_rvi.c.inc | 8 ++++++-- target/riscv/op_helper.c | 8 ++++++-- target/riscv/translate.c | 4 +++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7de19b4183..51e49e03de 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -765,6 +765,18 @@ static inline RISCVMXL riscv_cpu_sxl(CPURISCVState *env) } #endif +static inline bool riscv_cpu_allow_16bit_insn(const RISCVCPUConfig *cfg, + target_long priv_ver, + uint32_t misa_ext) +{ + /* In priv spec version 1.12 or newer, C always implies Zca */ + if (priv_ver >= PRIV_VERSION_1_12_0) { + return cfg->ext_zca; + } else { + return misa_ext & RVC; + } +} + /* * Encode LMUL to lmul as follows: * LMUL vlmul lmul diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index b55f56a5eb..b9c7160468 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -151,7 +151,9 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) tcg_gen_ext32s_tl(target_pc, target_pc); } - if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if (!riscv_cpu_allow_16bit_insn(ctx->cfg_ptr, + ctx->priv_ver, + ctx->misa_ext)) { TCGv t0 = tcg_temp_new(); misaligned = gen_new_label(); @@ -300,7 +302,9 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) gen_set_label(l); /* branch taken */ - if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca && + if (!riscv_cpu_allow_16bit_insn(ctx->cfg_ptr, + ctx->priv_ver, + ctx->misa_ext) && (a->imm & 0x3)) { /* misaligned */ TCGv target_pc = tcg_temp_new(); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 0d4220ba93..72dc48e58d 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -279,7 +279,9 @@ target_ulong helper_sret(CPURISCVState *env) } target_ulong retpc = env->sepc; - if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { + if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, + env->priv_ver, + env->misa_ext) && (retpc & 0x3)) { riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } @@ -357,7 +359,9 @@ static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { + if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, + env->priv_ver, + env->misa_ext) && (retpc & 0x3)) { riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index eaa5d86eae..d6651f244f 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -606,7 +606,9 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) TCGv succ_pc = dest_gpr(ctx, rd); /* check misaligned: */ - if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if (!riscv_cpu_allow_16bit_insn(ctx->cfg_ptr, + ctx->priv_ver, + ctx->misa_ext)) { if ((imm & 0x3) != 0) { TCGv target_pc = tcg_temp_new(); gen_pc_plus_diff(target_pc, ctx, imm); From de7b18083bfed4e1a01bb40b4ad050c47d2011fa Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 14 Mar 2025 15:34:59 +0100 Subject: [PATCH 2736/2892] hw/xen: Fix xen_bus_realize() error handling The Error ** argument must be NULL, &error_abort, &error_fatal, or a pointer to a variable containing NULL. Passing an argument of the latter kind twice without clearing it in between is wrong: if the first call sets an error, it no longer points to NULL for the second call. xen_bus_realize() is wrong that way: it passes &local_err to xs_node_watch() in a loop. If this fails in more than one iteration, it can trip error_setv()'s assertion. Fix by clearing @local_err. Fixes: c4583c8c394e (xen-bus: reduce scope of backend watch) Signed-off-by: Markus Armbruster Message-ID: <20250314143500.2449658-2-armbru@redhat.com> Reviewed-by: Stefano Stabellini --- hw/xen/xen-bus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 8260f1e1bb..2aacc1436f 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -357,6 +357,7 @@ static void xen_bus_realize(BusState *bus, Error **errp) error_reportf_err(local_err, "failed to set up '%s' enumeration watch: ", type[i]); + local_err = NULL; } g_free(node); From 6121c55db9c972950b28eb9ac84858271579f25c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 14 Mar 2025 15:35:00 +0100 Subject: [PATCH 2737/2892] hw/xen: Downgrade a xen_bus_realize() non-error to warning xen_bus_realize() reports a failure to set up a watch as error, but it doesn't treat it as one: it simply continues. Report a warning instead. Signed-off-by: Markus Armbruster Message-ID: <20250314143500.2449658-3-armbru@redhat.com> Reviewed-by: Stefano Stabellini --- hw/xen/xen-bus.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 2aacc1436f..f808a01813 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -353,10 +353,9 @@ static void xen_bus_realize(BusState *bus, Error **errp) xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, xenbus, &local_err); if (local_err) { - /* This need not be treated as a hard error so don't propagate */ - error_reportf_err(local_err, - "failed to set up '%s' enumeration watch: ", - type[i]); + warn_reportf_err(local_err, + "failed to set up '%s' enumeration watch: ", + type[i]); local_err = NULL; } From f700abbbeb6ab68a3446d1fb168a934d6f284eb5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 19 Mar 2025 11:45:45 +0100 Subject: [PATCH 2738/2892] tests/functional/test_migration: Use "ncat" instead of "nc" in the exec test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "nc" can either be GNU netcat, OpenBSD netcat or NMap ncat. At least GNU netcat currently does not work with this test anymore, though the comment in the test says otherwise. GNU netcat seems to be quite unmaintained nowadays, according to its website (https://netcat.sourceforge.net/), the last public release is from 2004, so we should rather avoid that binary. In our CI, we are only using "ncat" in the containers (it's the only flavor that lcitool supports), thus to avoid silent regressions with the other netcats, let's limit this test to "ncat" only now. Reported-by: Matheus Tavares Bernardino Message-ID: <20250319105617.133191-1-thuth@redhat.com> Reviewed-by: Matheus Tavares Bernardino Reviewed-by: Philippe Mathieu-Daudé Acked-by: Fabiano Rosas Signed-off-by: Thomas Huth --- tests/functional/test_migration.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py index 44804113cf..181223a69e 100755 --- a/tests/functional/test_migration.py +++ b/tests/functional/test_migration.py @@ -87,13 +87,12 @@ class MigrationTest(QemuSystemTest): dest_uri = 'unix:%s/qemu-test.sock' % socket_path self.do_migrate(dest_uri) - @skipIfMissingCommands('nc') + @skipIfMissingCommands('ncat') def test_migration_with_exec(self): - """The test works for both netcat-traditional and netcat-openbsd packages.""" with Ports() as ports: free_port = self._get_free_port(ports) - dest_uri = 'exec:nc -l localhost %u' % free_port - src_uri = 'exec:nc localhost %u' % free_port + dest_uri = 'exec:ncat -l localhost %u' % free_port + src_uri = 'exec:ncat localhost %u' % free_port self.do_migrate(dest_uri, src_uri) if __name__ == '__main__': From fb802acdc8b162084e9e60d42aeba79097d14d2b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 18 Mar 2025 15:03:48 +1000 Subject: [PATCH 2739/2892] ppc/spapr: Fix RTAS stopped state This change takes the CPUPPCState 'quiesced' field added for powernv hardware CPU core controls (used to stop and start cores), and extends it to spapr to model the "RTAS stopped" state. This prevents the schedulers attempting to run stopped CPUs unexpectedly, which can cause hangs and possibly other unexpected behaviour. The detail of the problematic situation is this: A KVM spapr guest boots with all secondary CPUs defined to be in the "RTAS stopped" state. In this state, the CPU is only responsive to the start-cpu RTAS call. This behaviour is modeled in QEMU with the start_powered_off feature, which sets ->halted on secondary CPUs at boot. ->halted=true looks like an idle / sleep / power-save state which typically is responsive to asynchronous interrupts, but spapr clears wake-on-interrupt bits in the LPCR SPR. This more-or-less works. Commit e8291ec16da8 ("target/ppc: fix timebase register reset state") recently caused the decrementer to expire sooner at boot, causing a decrementer exception on secondary CPUs in RTAS stopped state. This was not a problem on TCG, but KVM limits how a guest can modify LPCR, in particular it prevents the clearing of wake-on-interrupt bits, and so in the course of CPU register synchronisation, the LPCR as set by spapr to model the RTAS stopped state is overwritten with KVM's LPCR value, and that then causes QEMU's interrupt code to notice the expired decrementer exception, turn that into an interrupt, and set CPU_INTERRUPT_HARD. That causes the CPU to be kicked, and the KVM vCPU thread to loop calling kvm_cpu_exec(). kvm_cpu_exec() calls kvm_arch_process_async_events(), which on ppc just returns ->halted. This is still true, so it returns immediately with EXCP_HLT, and the vCPU never goes to sleep because qemu_wait_io_event() sees CPU_INTERRUPT_HARD is set. All this while the vCPU holds the bql. This causes the boot CPU to eventually lock up when it needs the bql. So make 'quiesced' represent the "RTAS stopped" state, and have it explicitly not respond to exceptions (interrupt conditions) rather than rely on machine register state to model that state. This matches the powernv quiesced state very well because it essentially turns off the CPU core via a side-band control unit. There are still issues with QEMU and KVM idea of LPCR diverging and that is quite ugly and fragile that should be fixed. spapr should synchronize its LPCR properly with KVM, and not try to use values that KVM does not support. Reported-by: Misbah Anjum N Tested-by: Misbah Anjum N Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_core.c | 6 +++++- hw/ppc/spapr_cpu_core.c | 6 ++++++ hw/ppc/spapr_rtas.c | 5 ++++- target/ppc/cpu.h | 11 +++++++++++ target/ppc/excp_helper.c | 4 ++++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 99d9644ee3..a33977da18 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -248,21 +248,25 @@ static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr, if (val & PPC_BIT(7 + 8 * i)) { /* stop */ val &= ~PPC_BIT(7 + 8 * i); - cpu_pause(cs); env->quiesced = true; + ppc_maybe_interrupt(env); + cpu_pause(cs); } if (val & PPC_BIT(6 + 8 * i)) { /* start */ val &= ~PPC_BIT(6 + 8 * i); env->quiesced = false; + ppc_maybe_interrupt(env); cpu_resume(cs); } if (val & PPC_BIT(4 + 8 * i)) { /* sreset */ val &= ~PPC_BIT(4 + 8 * i); env->quiesced = false; + ppc_maybe_interrupt(env); pnv_cpu_do_nmi_resume(cs); } if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */ env->quiesced = false; + ppc_maybe_interrupt(env); /* * Hardware has very particular cases for where clear maint * must be used and where start must be used to resume a diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 0671d9e44b..faf9170ba6 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -37,6 +37,9 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) cpu_reset(cs); + env->quiesced = true; /* set "RTAS stopped" state. */ + ppc_maybe_interrupt(env); + /* * "PowerPC Processor binding to IEEE 1275" defines the initial MSR state * as 32bit (MSR_SF=0) with MSR_ME=1 and MSR_FP=1 in "8.2.1. Initial @@ -98,6 +101,9 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); + + env->quiesced = false; /* clear "RTAS stopped" state. */ + ppc_maybe_interrupt(env); } /* diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 503d441b48..78309dbb09 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -110,7 +110,8 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, id = rtas_ld(args, 0); cpu = spapr_find_cpu(id); if (cpu != NULL) { - if (CPU(cpu)->halted) { + CPUPPCState *env = &cpu->env; + if (env->quiesced) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -215,6 +216,8 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, * For the same reason, set PSSCR_EC. */ env->spr[SPR_PSSCR] |= PSSCR_EC; + env->quiesced = true; /* set "RTAS stopped" state. */ + ppc_maybe_interrupt(env); cs->halted = 1; ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index efab54a068..3ee83517dc 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1356,6 +1356,17 @@ struct CPUArchState { * special way (such as routing some resume causes to 0x100, i.e. sreset). */ bool resume_as_sreset; + + /* + * On powernv, quiesced means the CPU has been stopped using PC direct + * control xscom registers. + * + * On spapr, quiesced means it is in the "RTAS stopped" state. + * + * The core halted/stopped variables aren't sufficient for this, because + * they can be changed with various side-band operations like qmp cont, + * powersave interrupts, etc. + */ bool quiesced; #endif diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 44e19aacd8..c941c89806 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1951,6 +1951,10 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env) target_ulong lpcr = env->spr[SPR_LPCR]; bool async_deliver; + if (unlikely(env->quiesced)) { + return 0; + } + #ifdef TARGET_PPC64 switch (env->excp_model) { case POWERPC_EXCP_POWER7: From d7ffc17de706e6d0178fcdbc3a7d302f5a246c3c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 20 Mar 2025 11:21:53 +0800 Subject: [PATCH 2740/2892] target/loongarch: Fix error handling of KVM feature checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some paravirt KVM features, if user forces to enable it however KVM does not support, qemu should fail to run and exit immediately, rather than continue to run. Here set error message and return directly in function kvm_arch_init_vcpu(). Fixes: 6edd2a9bec90 (target/loongarch/kvm: Implement LoongArch PMU extension) Fixes: 936c3f4d7916 (target/loongarch: Use auto method with LSX feature) Fixes: 5e360dabedb1 (target/loongarch: Use auto method with LASX feature) Fixes: 620d9bd0022e (target/loongarch: Add paravirt ipi feature detection) Signed-off-by: Bibo Mao Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250320032158.1762751-2-maobibo@loongson.cn> Signed-off-by: Markus Armbruster --- target/loongarch/kvm/kvm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 28735c80be..7f63e7c8fe 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1081,7 +1081,6 @@ int kvm_arch_init_vcpu(CPUState *cs) int ret; Error *local_err = NULL; - ret = 0; qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { @@ -1091,29 +1090,34 @@ int kvm_arch_init_vcpu(CPUState *cs) ret = kvm_cpu_check_lsx(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_lasx(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_lbt(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_pmu(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_pv_features(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } - return ret; + return 0; } static bool loongarch_get_lbt(Object *obj, Error **errp) From 0973b505fadedd208e4c3fa1698ffa0b2293006c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 20 Mar 2025 11:21:56 +0800 Subject: [PATCH 2741/2892] hw/loongarch/virt: Eliminate error_propagate() When there is an error, it is put into a local variable and then propagated to somewhere else. Instead the error can be set right away, error propagation can be removed. Signed-off-by: Bibo Mao Message-ID: <20250320032158.1762751-5-maobibo@loongson.cn> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- hw/loongarch/virt.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a5840ff968..4674bd9163 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -859,30 +859,29 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, LoongArchCPU *cpu = LOONGARCH_CPU(dev); CPUState *cs = CPU(dev); CPUArchId *cpu_slot; - Error *err = NULL; LoongArchCPUTopo topo; int arch_id; if (lvms->acpi_ged) { if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { - error_setg(&err, + error_setg(errp, "Invalid thread-id %u specified, must be in range 1:%u", cpu->thread_id, ms->smp.threads - 1); - goto out; + return; } if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { - error_setg(&err, + error_setg(errp, "Invalid core-id %u specified, must be in range 1:%u", cpu->core_id, ms->smp.cores - 1); - goto out; + return; } if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { - error_setg(&err, + error_setg(errp, "Invalid socket-id %u specified, must be in range 1:%u", cpu->socket_id, ms->smp.sockets - 1); - goto out; + return; } topo.socket_id = cpu->socket_id; @@ -891,11 +890,11 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, arch_id = virt_get_arch_id_from_topo(ms, &topo); cpu_slot = virt_find_cpu_slot(ms, arch_id); if (CPU(cpu_slot->cpu)) { - error_setg(&err, + error_setg(errp, "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", cs->cpu_index, cpu->socket_id, cpu->core_id, cpu->thread_id, cpu_slot->arch_id); - goto out; + return; } } else { /* For cold-add cpu, find empty cpu slot */ @@ -911,33 +910,24 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, cpu->env.address_space_iocsr = &lvms->as_iocsr; cpu->phy_id = cpu_slot->arch_id; cs->cpu_index = cpu_slot - ms->possible_cpus->cpus; - numa_cpu_pre_plug(cpu_slot, dev, &err); -out: - if (err) { - error_propagate(errp, err); - } + numa_cpu_pre_plug(cpu_slot, dev, errp); } static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - Error *err = NULL; LoongArchCPU *cpu = LOONGARCH_CPU(dev); CPUState *cs = CPU(dev); if (cs->cpu_index == 0) { - error_setg(&err, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", + error_setg(errp, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", cs->cpu_index, cpu->socket_id, cpu->core_id, cpu->thread_id); - error_propagate(errp, err); return; } - hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); - if (err) { - error_propagate(errp, err); - } + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); } static void virt_cpu_unplug(HotplugHandler *hotplug_dev, From daf78a9d51402e8f70d89f86a8c5f12d02aee667 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 20 Mar 2025 11:21:57 +0800 Subject: [PATCH 2742/2892] target/loongarch: Remove unnecessary temporary variable assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temporary variable ret is assigned at last line and return, it can be removed and return directly. Signed-off-by: Bibo Mao Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250320032158.1762751-6-maobibo@loongson.cn> Signed-off-by: Markus Armbruster --- target/loongarch/tcg/tlb_helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 646dbf59de..182881a237 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -543,7 +543,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, target_ulong level, uint32_t mem_idx) { CPUState *cs = env_cpu(env); - target_ulong badvaddr, index, phys, ret; + target_ulong badvaddr, index, phys; uint64_t dir_base, dir_width; if (unlikely((level == 0) || (level > 4))) { @@ -571,8 +571,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, get_dir_base_width(env, &dir_base, &dir_width, level); index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); phys = base | index << 3; - ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; - return ret; + return ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; } void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, From a725bc970e3091499be8be52798c21259f91b2cd Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 20 Mar 2025 11:21:58 +0800 Subject: [PATCH 2743/2892] target/loongarch: Clean up virt_cpu_irq_init() error handling The Error ** argument must be NULL, &error_abort, &error_fatal, or a pointer to a variable containing NULL. Passing an argument of the latter kind twice without clearing it in between is wrong: if the first call sets an error, it no longer points to NULL for the second call. virt_cpu_irq_init() is wrong that way: it passes &err to hotplug_handler_plug() twice. If both calls failed, this could trip error_setv()'s assertion. Moreover, if just one fails, the Error object leaks. Fortunately, these calls can't actually fail. Messed up in commit 50ebc3fc47f7 (hw/intc/loongarch_ipi: Notify ipi object when cpu is plugged) and commit 087a23a87c57 (hw/intc/loongarch_extioi: Use cpu plug notification). Clean this up by passing &error_abort instead. Signed-off-by: Bibo Mao Acked-by: Markus Armbruster Message-ID: <20250320032158.1762751-7-maobibo@loongson.cn> Signed-off-by: Markus Armbruster --- hw/loongarch/virt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 4674bd9163..63477de5e4 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -327,7 +327,6 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus; CPUState *cs; - Error *err = NULL; /* cpu nodes */ possible_cpus = mc->possible_cpu_arch_ids(ms); @@ -337,8 +336,10 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) continue; } - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), &err); - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), &err); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), + &error_abort); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), + &error_abort); } } From f88c9cd804804360fa4b3586d7d2f84505ab8c26 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 19 Mar 2025 19:31:09 +0000 Subject: [PATCH 2744/2892] rust: Kconfig: Factor out whether PL011 is Rust or C MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently every board that uses the PL011 duplicates the logic that selects the Rust implementation if Rust was enabled and the C implementation if it does not. Factor this out into a separate Kconfig stanza, so that boards can go back to simply doing "select PL011" and get whichever implementation is correct for the build. This fixes a compilation failure if CONFIG_VMAPPLE is enabled in a Rust build, because hw/vmapple/Kconfig didn't have the "pick the Rust PL011 if Rust is enabled" logic in it. Fixes: 59f4d65584bd33 ("hw/vmapple/vmapple: Add vmapple machine type") Reported-by: Tanish Desai Analyzed-by: Tanish Desai Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250319193110.1565578-2-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- hw/arm/Kconfig | 30 ++++++++++-------------------- hw/char/Kconfig | 6 ++++++ hw/char/meson.build | 2 +- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 15200a2d7e..a55b44d7bd 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -21,8 +21,7 @@ config ARM_VIRT select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL061 # GPIO select GPIO_PWR @@ -75,8 +74,7 @@ config HIGHBANK select AHCI_SYSBUS select ARM_TIMER # sp804 select ARM_V7M - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL022 # SPI select PL031 # RTC select PL061 # GPIO @@ -89,8 +87,7 @@ config INTEGRATOR depends on TCG && ARM select ARM_TIMER select INTEGRATOR_DEBUG - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL041 # audio select PL050 # keyboard/mouse @@ -108,8 +105,7 @@ config MUSCA default y depends on TCG && ARM select ARMSSE - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 select SPLIT_IRQ select UNIMP @@ -173,8 +169,7 @@ config REALVIEW select WM8750 # audio codec select LSI_SCSI_PCI select PCI - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL041 # audio codec select PL050 # keyboard/mouse @@ -199,8 +194,7 @@ config SBSA_REF select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL061 # GPIO select USB_XHCI_SYSBUS @@ -224,8 +218,7 @@ config STELLARIS select ARM_V7M select CMSDK_APB_WATCHDOG select I2C - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL022 # SPI select PL061 # GPIO select SSD0303 # OLED display @@ -285,8 +278,7 @@ config VEXPRESS select ARM_TIMER # sp804 select LAN9118 select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL041 # audio codec select PL181 # display select REALVIEW @@ -371,8 +363,7 @@ config RASPI default y depends on TCG && ARM select FRAMEBUFFER - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select SDHCI select USB_DWC2 select BCM2835_SPI @@ -448,8 +439,7 @@ config XLNX_VERSAL select ARM_GIC select CPU_CLUSTER select DEVICE_TREE - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select CADENCE select VIRTIO_MMIO select UNIMP diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 3f702565e6..9d517f3e28 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -11,6 +11,12 @@ config PARALLEL config PL011 bool + # The PL011 has both a Rust and a C implementation + select PL011_C if !HAVE_RUST + select X_PL011_RUST if HAVE_RUST + +config PL011_C + bool config SERIAL bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 86ee808cae..4e439da8b9 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -9,7 +9,7 @@ system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) system_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) -system_ss.add(when: 'CONFIG_PL011', if_true: files('pl011.c')) +system_ss.add(when: 'CONFIG_PL011_C', if_true: files('pl011.c')) system_ss.add(when: 'CONFIG_SCLPCONSOLE', if_true: files('sclpconsole.c', 'sclpconsole-lm.c')) system_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c')) system_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c')) From d1368344bc9bb251080507940f2bad16048d2687 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 19 Mar 2025 19:31:10 +0000 Subject: [PATCH 2745/2892] rust: Kconfig: Factor out whether HPET is Rust or C Currently we require everywhere that wants to know if there is an HPET device to check for "CONFIG_HPET || CONFIG_X_HPET_RUST". Factor out whether the HPET device is Rust or C into a separate Kconfig stanza, so that CONFIG_HPET means "there is an HPET", and whether this has pulled in CONFIG_X_HPET_RUST or CONFIG_HPET_C is something the rest of QEMU can ignore. Signed-off-by: Peter Maydell Link: https://lore.kernel.org/r/20250319193110.1565578-3-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- configs/devices/i386-softmmu/default.mak | 1 - hw/i386/fw_cfg.c | 2 +- hw/i386/pc.c | 2 +- hw/timer/Kconfig | 8 +++++++- hw/timer/meson.build | 2 +- rust/hw/timer/Kconfig | 1 - tests/qtest/meson.build | 3 +-- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index 9ef343cace..4faf2f0315 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -6,7 +6,6 @@ #CONFIG_APPLESMC=n #CONFIG_FDC=n #CONFIG_HPET=n -#CONFIG_X_HPET_RUST=n #CONFIG_HYPERV=n #CONFIG_ISA_DEBUG=n #CONFIG_ISA_IPMI_BT=n diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index a7f1b60b98..5c0bcd5f8a 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -26,7 +26,7 @@ #include CONFIG_DEVICES #include "target/i386/cpu.h" -#if !defined(CONFIG_HPET) && !defined(CONFIG_X_HPET_RUST) +#if !defined(CONFIG_HPET) struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; #endif diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 63a96cd23f..01d0581f62 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1704,7 +1704,7 @@ static void pc_machine_initfn(Object *obj) pcms->sata_enabled = true; pcms->i8042_enabled = true; pcms->max_fw_size = 8 * MiB; -#if defined(CONFIG_HPET) || defined(CONFIG_X_HPET_RUST) +#if defined(CONFIG_HPET) pcms->hpet_enabled = true; #endif pcms->fd_bootchk = true; diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 9ac0084534..b3d823ce2c 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -11,7 +11,13 @@ config A9_GTIMER config HPET bool - default y if PC && !HAVE_RUST + default y if PC + # The HPET has both a Rust and a C implementation + select HPET_C if !HAVE_RUST + select X_HPET_RUST if HAVE_RUST + +config HPET_C + bool config I8254 bool diff --git a/hw/timer/meson.build b/hw/timer/meson.build index f5f9eed2d0..178321c029 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -13,7 +13,7 @@ system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) -system_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) +system_ss.add(when: 'CONFIG_HPET_C', if_true: files('hpet.c')) system_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig index 42e421317a..afd9803350 100644 --- a/rust/hw/timer/Kconfig +++ b/rust/hw/timer/Kconfig @@ -1,3 +1,2 @@ config X_HPET_RUST bool - default y if PC && HAVE_RUST diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5a8c1f102c..3136d15e0f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -103,8 +103,7 @@ qtests_i386 = \ config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \ slirp.found() ? ['virtio-net-failover'] : []) + \ (unpack_edk2_blobs and \ - (config_all_devices.has_key('CONFIG_HPET') or \ - config_all_devices.has_key('CONFIG_X_HPET_RUST')) and \ + config_all_devices.has_key('CONFIG_HPET') and \ config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \ qtests_pci + \ qtests_cxl + \ From 033a5649b45690d09bde5cdf15cb83453f6ac811 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 17 Mar 2025 13:20:02 +1000 Subject: [PATCH 2746/2892] ppc/xive: Fix typo in crowd block level calculation I introduced this bug when "tidying" the original patch, not Frederic. Paper bag for me. Fixes: 9cb7f6ebed60 ("ppc/xive2: Support group-matching when looking for target") Signed-off-by: Nicholas Piggin --- hw/intc/xive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index c77df2c1f8..e86f274932 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1686,7 +1686,7 @@ static uint8_t xive_get_group_level(bool crowd, bool ignore, * Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported. * HW will encode level 4 as the value 3. See xive2_pgofnext(). */ - switch (level) { + switch (blk) { case 1: case 2: break; From 344921309d933547974c2e85c52e2294513d9c45 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 17 Mar 2025 13:18:29 +1000 Subject: [PATCH 2747/2892] pnv/xive: Fix possible undefined shift error in group size calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity discovered a potential shift overflow in group size calculation in the case of a guest error. Add checks and logs to ensure a issues are caught. Make the group and crowd error checking code more similar to one another while here. Resolves: Coverity CID 1593724 Fixes: 9cb7f6ebed60 ("ppc/xive2: Support group-matching when looking for target") Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- hw/intc/xive.c | 27 ++++++++++++++++++++++++--- hw/intc/xive2.c | 19 ++++++++++++++----- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index e86f274932..3eb28c2265 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1662,12 +1662,20 @@ uint32_t xive_get_vpgroup_size(uint32_t nvp_index) * (starting with the least significant bits) in the NVP index * gives the size of the group. */ - return 1 << (ctz32(~nvp_index) + 1); + int first_zero = cto32(nvp_index); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group index 0x%08x", + nvp_index); + return 0; + } + + return 1U << (first_zero + 1); } static uint8_t xive_get_group_level(bool crowd, bool ignore, uint32_t nvp_blk, uint32_t nvp_index) { + int first_zero; uint8_t level; if (!ignore) { @@ -1675,12 +1683,25 @@ static uint8_t xive_get_group_level(bool crowd, bool ignore, return 0; } - level = (ctz32(~nvp_index) + 1) & 0b1111; + first_zero = cto32(nvp_index); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group index 0x%08x", + nvp_index); + return 0; + } + + level = (first_zero + 1) & 0b1111; if (crowd) { uint32_t blk; /* crowd level is bit position of first 0 from the right in nvp_blk */ - blk = ctz32(~nvp_blk) + 1; + first_zero = cto32(nvp_blk); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd block 0x%08x", + nvp_blk); + return 0; + } + blk = first_zero + 1; /* * Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported. diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f8ef615487..311b42e15d 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1153,13 +1153,15 @@ static bool xive2_vp_match_mask(uint32_t cam1, uint32_t cam2, static uint8_t xive2_get_vp_block_mask(uint32_t nvt_blk, bool crowd) { - uint8_t size, block_mask = 0b1111; + uint8_t block_mask = 0b1111; /* 3 supported crowd sizes: 2, 4, 16 */ if (crowd) { - size = xive_get_vpgroup_size(nvt_blk); - if (size == 8) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd size of 8n"); + uint32_t size = xive_get_vpgroup_size(nvt_blk); + + if (size != 2 && size != 4 && size != 16) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd size of %d", + size); return block_mask; } block_mask &= ~(size - 1); @@ -1172,7 +1174,14 @@ static uint32_t xive2_get_vp_index_mask(uint32_t nvt_index, bool cam_ignore) uint32_t index_mask = 0xFFFFFF; /* 24 bits */ if (cam_ignore) { - index_mask &= ~(xive_get_vpgroup_size(nvt_index) - 1); + uint32_t size = xive_get_vpgroup_size(nvt_index); + + if (size < 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group size of %d", + size); + return index_mask; + } + index_mask &= ~(size - 1); } return index_mask; } From e0b9357337e4005d7915d8c746eb3ce66c61fac0 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 17 Mar 2025 13:20:49 +1000 Subject: [PATCH 2748/2892] ppc/xive2: Fix logical / bitwise comparison typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comparison as written is always false (perhaps confusingly, because the functions/macros are not really booleans but return 0 or the tested bit value). Change to use logical-and. Resolves: Coverity CID 1593721 Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- hw/intc/xive2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 311b42e15d..7d584dfafa 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1344,7 +1344,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - if (xive2_end_is_crowd(&end) & !xive2_end_is_ignore(&end)) { + if (xive2_end_is_crowd(&end) && !xive2_end_is_ignore(&end)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid END, 'crowd' bit requires 'ignore' bit\n"); return; From 965797d19a0d0b5dbe73f1afa110576589d25003 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 17 Mar 2025 13:49:36 +1000 Subject: [PATCH 2749/2892] ppc/spapr: Fix possible pa_features memory overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reports a possible memory overflow in spapr_dt_pa_features(). This should not be a true bug since DAWR1 cap is only be true for CPU_POWERPC_LOGICAL_3_10. Add an assertion to ensure any bug there is caught. Resolves: Coverity CID 1593722 Fixes: 5f361ea187ba ("ppc: spapr: Enable 2nd DAWR on Power10 pSeries machine") Reviewed-By: Shivaprasad G Bhat Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a415e51d07..9865d7147f 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -296,6 +296,7 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, pa_features[40 + 2] &= ~0x80; /* Radix MMU */ } if (spapr_get_cap(spapr, SPAPR_CAP_DAWR1)) { + g_assert(pa_size > 66); pa_features[66] |= 0x80; } From ce5a32d18054fe468e3536f0a63026d5b196057b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 17 Mar 2025 14:01:25 +1000 Subject: [PATCH 2750/2892] ppc/pnv: Move the PNOR LPC address into struct PnvPnor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than use the hardcoded define throughout the tree for the PNOR LPC address, keep it within the PnvPnor object. This should solve a dead code issue in the BMC HIOMAP checks where Coverity (correctly) reported that the sanity checks are dead code. We would like to keep the sanity checks without turning them into a compile time assert in case we would like to make them configurable in future. Fixes: 4c84a0a4a6e5 ("ppc/pnv: Add a PNOR address and size sanity checks") Resolves: Coverity CID 1593723 Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- hw/ppc/pnv.c | 2 +- hw/ppc/pnv_bmc.c | 4 ++-- hw/ppc/pnv_pnor.c | 2 ++ include/hw/ppc/pnv_pnor.h | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 59365370c3..63f2232f32 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1191,7 +1191,7 @@ static void pnv_init(MachineState *machine) * Since we can not reach the remote BMC machine with LPC memops, * map it always for now. */ - memory_region_add_subregion(pnv->chips[0]->fw_mr, PNOR_SPI_OFFSET, + memory_region_add_subregion(pnv->chips[0]->fw_mr, pnv->pnor->lpc_address, &pnv->pnor->mmio); /* diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index 811ba3d7a4..fb70a8c1f2 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -174,8 +174,8 @@ static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, { PnvPnor *pnor = PNV_PNOR(object_property_get_link(OBJECT(ibs), "pnor", &error_abort)); + uint32_t pnor_addr = pnor->lpc_address; uint32_t pnor_size = pnor->size; - uint32_t pnor_addr = PNOR_SPI_OFFSET; bool readonly = false; rsp_buffer_push(rsp, cmd[2]); @@ -251,8 +251,8 @@ static const IPMINetfn hiomap_netfn = { void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor) { + uint32_t pnor_addr = pnor->lpc_address; uint32_t pnor_size = pnor->size; - uint32_t pnor_addr = PNOR_SPI_OFFSET; if (!pnv_bmc_is_simulator(bmc)) { return; diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 863e2e70ac..9db44ca21d 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -108,6 +108,8 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) memset(s->storage, 0xFF, s->size); } + s->lpc_address = PNOR_SPI_OFFSET; + memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, TYPE_PNV_PNOR, s->size); } diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index 19c2d642e8..b44cafe918 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -28,6 +28,7 @@ struct PnvPnor { BlockBackend *blk; uint8_t *storage; + uint32_t lpc_address; /* Offset within LPC FW space */ int64_t size; MemoryRegion mmio; }; From d8b1c3eaed5cf11d2db702a415df082dc1754b2c Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 17 Mar 2025 14:12:45 +1000 Subject: [PATCH 2751/2892] ppc/pnv: Fix system symbols in HOMER structure definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These definitions were taken from skiboot firmware. I naively thought it would be nicer to keep the code similar by using the preprocessor, but it was pointed out that system headers might still use those symbols and cause something unexpected. Also just nicer to keep the QEMU tree clean. Cc: "Philippe Mathieu-Daudé" Cc: "Stefan Hajnoczi" Reviewed-by: Thomas Huth Fixes: 70bc5c2498f46 ("ppc/pnv: Make HOMER memory a RAM region") Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_occ.c | 201 ++++++++++++++++++++++------------------------- 1 file changed, 96 insertions(+), 105 deletions(-) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index bda6b23ad3..177c5e514b 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -364,7 +364,12 @@ static void pnv_occ_register_types(void) type_init(pnv_occ_register_types); -/* From skiboot/hw/occ.c with tab to space conversion */ +/* + * From skiboot/hw/occ.c with following changes: + * - tab to space conversion + * - Type conversions u8->uint8_t s8->int8_t __be16->uint16_t etc + * - __packed -> QEMU_PACKED + */ /* OCC Communication Area for PStates */ #define OPAL_DYNAMIC_DATA_OFFSET 0x0B80 @@ -384,20 +389,6 @@ type_init(pnv_occ_register_types); #define FREQ_MAX_IN_DOMAIN 0 #define FREQ_MOST_RECENTLY_SET 1 -#define u8 uint8_t -#define s8 int8_t -#define u16 uint16_t -#define s16 int16_t -#define u32 uint32_t -#define s32 int32_t -#define u64 uint64_t -#define s64 int64_t -#define __be16 uint16_t -#define __be32 uint32_t -#ifndef __packed -#define __packed QEMU_PACKED -#endif /* !__packed */ - /** * OCC-OPAL Shared Memory Region * @@ -434,69 +425,69 @@ type_init(pnv_occ_register_types); * @spare/reserved/pad: Unused data */ struct occ_pstate_table { - u8 valid; - u8 version; - union __packed { - struct __packed { /* Version 0x01 and 0x02 */ - u8 throttle; - s8 pstate_min; - s8 pstate_nom; - s8 pstate_turbo; - s8 pstate_ultra_turbo; - u8 spare; - u64 reserved; - struct __packed { - s8 id; - u8 flags; - u8 vdd; - u8 vcs; - __be32 freq_khz; + uint8_t valid; + uint8_t version; + union QEMU_PACKED { + struct QEMU_PACKED { /* Version 0x01 and 0x02 */ + uint8_t throttle; + int8_t pstate_min; + int8_t pstate_nom; + int8_t pstate_turbo; + int8_t pstate_ultra_turbo; + uint8_t spare; + uint64_t reserved; + struct QEMU_PACKED { + int8_t id; + uint8_t flags; + uint8_t vdd; + uint8_t vcs; + uint32_t freq_khz; } pstates[MAX_PSTATES]; - s8 core_max[MAX_P8_CORES]; - u8 pad[100]; + int8_t core_max[MAX_P8_CORES]; + uint8_t pad[100]; } v2; - struct __packed { /* Version 0x90 */ - u8 occ_role; - u8 pstate_min; - u8 pstate_nom; - u8 pstate_turbo; - u8 pstate_ultra_turbo; - u8 spare; - u64 reserved1; - u64 reserved2; - struct __packed { - u8 id; - u8 flags; - u16 reserved; - __be32 freq_khz; + struct QEMU_PACKED { /* Version 0x90 */ + uint8_t occ_role; + uint8_t pstate_min; + uint8_t pstate_nom; + uint8_t pstate_turbo; + uint8_t pstate_ultra_turbo; + uint8_t spare; + uint64_t reserved1; + uint64_t reserved2; + struct QEMU_PACKED { + uint8_t id; + uint8_t flags; + uint16_t reserved; + uint32_t freq_khz; } pstates[MAX_PSTATES]; - u8 core_max[MAX_P9_CORES]; - u8 pad[56]; + uint8_t core_max[MAX_P9_CORES]; + uint8_t pad[56]; } v9; - struct __packed { /* Version 0xA0 */ - u8 occ_role; - u8 pstate_min; - u8 pstate_fixed_freq; - u8 pstate_base; - u8 pstate_ultra_turbo; - u8 pstate_fmax; - u8 minor; - u8 pstate_bottom_throttle; - u8 spare; - u8 spare1; - u32 reserved_32; - u64 reserved_64; - struct __packed { - u8 id; - u8 valid; - u16 reserved; - __be32 freq_khz; + struct QEMU_PACKED { /* Version 0xA0 */ + uint8_t occ_role; + uint8_t pstate_min; + uint8_t pstate_fixed_freq; + uint8_t pstate_base; + uint8_t pstate_ultra_turbo; + uint8_t pstate_fmax; + uint8_t minor; + uint8_t pstate_bottom_throttle; + uint8_t spare; + uint8_t spare1; + uint32_t reserved_32; + uint64_t reserved_64; + struct QEMU_PACKED { + uint8_t id; + uint8_t valid; + uint16_t reserved; + uint32_t freq_khz; } pstates[MAX_PSTATES]; - u8 core_max[MAX_P10_CORES]; - u8 pad[48]; + uint8_t core_max[MAX_P10_CORES]; + uint8_t pad[48]; } v10; }; -} __packed; +} QEMU_PACKED; /** * OPAL-OCC Command Response Interface @@ -531,13 +522,13 @@ struct occ_pstate_table { * @spare: Unused byte */ struct opal_command_buffer { - u8 flag; - u8 request_id; - u8 cmd; - u8 spare; - __be16 data_size; - u8 data[MAX_OPAL_CMD_DATA_LENGTH]; -} __packed; + uint8_t flag; + uint8_t request_id; + uint8_t cmd; + uint8_t spare; + uint16_t data_size; + uint8_t data[MAX_OPAL_CMD_DATA_LENGTH]; +} QEMU_PACKED; /** * OPAL-OCC Response Buffer @@ -571,13 +562,13 @@ struct opal_command_buffer { * @data: Response specific data */ struct occ_response_buffer { - u8 flag; - u8 request_id; - u8 cmd; - u8 status; - __be16 data_size; - u8 data[MAX_OCC_RSP_DATA_LENGTH]; -} __packed; + uint8_t flag; + uint8_t request_id; + uint8_t cmd; + uint8_t status; + uint16_t data_size; + uint8_t data[MAX_OCC_RSP_DATA_LENGTH]; +} QEMU_PACKED; /** * OCC-OPAL Shared Memory Interface Dynamic Data Vx90 @@ -608,31 +599,31 @@ struct occ_response_buffer { * @rsp: OCC Response Buffer */ struct occ_dynamic_data { - u8 occ_state; - u8 major_version; - u8 minor_version; - u8 gpus_present; - union __packed { - struct __packed { /* Version 0x90 */ - u8 spare1; + uint8_t occ_state; + uint8_t major_version; + uint8_t minor_version; + uint8_t gpus_present; + union QEMU_PACKED { + struct QEMU_PACKED { /* Version 0x90 */ + uint8_t spare1; } v9; - struct __packed { /* Version 0xA0 */ - u8 wof_enabled; + struct QEMU_PACKED { /* Version 0xA0 */ + uint8_t wof_enabled; } v10; }; - u8 cpu_throttle; - u8 mem_throttle; - u8 quick_pwr_drop; - u8 pwr_shifting_ratio; - u8 pwr_cap_type; - __be16 hard_min_pwr_cap; - __be16 max_pwr_cap; - __be16 cur_pwr_cap; - __be16 soft_min_pwr_cap; - u8 pad[110]; + uint8_t cpu_throttle; + uint8_t mem_throttle; + uint8_t quick_pwr_drop; + uint8_t pwr_shifting_ratio; + uint8_t pwr_cap_type; + uint16_t hard_min_pwr_cap; + uint16_t max_pwr_cap; + uint16_t cur_pwr_cap; + uint16_t soft_min_pwr_cap; + uint8_t pad[110]; struct opal_command_buffer cmd; struct occ_response_buffer rsp; -} __packed; +} QEMU_PACKED; enum occ_response_status { OCC_RSP_SUCCESS = 0x00, From 7bda68e8e2b0b836639557ddb13d761bdd15a104 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Feb 2025 14:02:03 +0100 Subject: [PATCH 2752/2892] qdev, rust/hpet: fix type of HPET "timers" property Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 37 ++++++++++++++++++++++++++++++++++ include/hw/qdev-properties.h | 1 + rust/hw/timer/hpet/src/hpet.rs | 6 +++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index c04df3b337..147b3ffd16 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -442,6 +442,43 @@ const PropertyInfo qdev_prop_uint64_checkmask = { .set = set_uint64_checkmask, }; +/* --- pointer-size integer --- */ + +static void get_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +static void set_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +const PropertyInfo qdev_prop_usize = { + .type = "usize", + .get = get_usize, + .set = set_usize, + .set_default_value = qdev_propinfo_set_default_value_uint, +}; + /* --- string --- */ static void release_string(Object *obj, const char *name, void *opaque) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 15fcec5260..2c99856caa 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -52,6 +52,7 @@ extern const PropertyInfo qdev_prop_bool; extern const PropertyInfo qdev_prop_uint8; extern const PropertyInfo qdev_prop_uint16; extern const PropertyInfo qdev_prop_uint32; +extern const PropertyInfo qdev_prop_usize; extern const PropertyInfo qdev_prop_int32; extern const PropertyInfo qdev_prop_uint64; extern const PropertyInfo qdev_prop_uint64_checkmask; diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 20e0afdfca..63c1971f0b 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -12,7 +12,7 @@ use std::{ use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_uint8, + qdev_prop_uint32, qdev_prop_usize, }, c_str, cell::{BqlCell, BqlRefCell}, @@ -859,8 +859,8 @@ qemu_api::declare_properties! { c_str!("timers"), HPETState, num_timers, - unsafe { &qdev_prop_uint8 }, - u8, + unsafe { &qdev_prop_usize }, + usize, default = HPET_MIN_TIMERS ), qemu_api::define_property!( From ca2737d6eca7fcc62ecb7a27246837b7c18830fc Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Fri, 14 Mar 2025 03:31:51 +0000 Subject: [PATCH 2753/2892] host/include/loongarch64: Fix inline assembly compatibility with Clang Clang on LoongArch only accepts fp register names in the dollar-prefixed form, while GCC allows omitting the dollar. Change registers in ASM clobbers to the dollar-prefixed form to make user emulators buildable with Clang on loongarch64. No functional change invovled. Cc: qemu-stable@nongnu.org Fixes: adc8467e697 ("host/include/loongarch64: Add atomic16 load and store") Signed-off-by: Yao Zi Reviewed-by: Richard Henderson Reviewed-by: Bibo Mao Signed-off-by: Bibo Mao --- host/include/loongarch64/host/atomic128-ldst.h.inc | 4 ++-- host/include/loongarch64/host/bufferiszero.c.inc | 6 ++++-- host/include/loongarch64/host/load-extract-al16-al8.h.inc | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/host/include/loongarch64/host/atomic128-ldst.h.inc b/host/include/loongarch64/host/atomic128-ldst.h.inc index 9a4a8f8b9e..754d2143f0 100644 --- a/host/include/loongarch64/host/atomic128-ldst.h.inc +++ b/host/include/loongarch64/host/atomic128-ldst.h.inc @@ -28,7 +28,7 @@ static inline Int128 atomic16_read_ro(const Int128 *ptr) asm("vld $vr0, %2, 0\n\t" "vpickve2gr.d %0, $vr0, 0\n\t" "vpickve2gr.d %1, $vr0, 1" - : "=r"(l), "=r"(h) : "r"(ptr), "m"(*ptr) : "f0"); + : "=r"(l), "=r"(h) : "r"(ptr), "m"(*ptr) : "$f0"); return int128_make128(l, h); } @@ -46,7 +46,7 @@ static inline void atomic16_set(Int128 *ptr, Int128 val) asm("vinsgr2vr.d $vr0, %1, 0\n\t" "vinsgr2vr.d $vr0, %2, 1\n\t" "vst $vr0, %3, 0" - : "=m"(*ptr) : "r"(l), "r"(h), "r"(ptr) : "f0"); + : "=m"(*ptr) : "r"(l), "r"(h), "r"(ptr) : "$f0"); } #endif /* LOONGARCH_ATOMIC128_LDST_H */ diff --git a/host/include/loongarch64/host/bufferiszero.c.inc b/host/include/loongarch64/host/bufferiszero.c.inc index 69891eac80..bb2598fdc3 100644 --- a/host/include/loongarch64/host/bufferiszero.c.inc +++ b/host/include/loongarch64/host/bufferiszero.c.inc @@ -61,7 +61,8 @@ static bool buffer_is_zero_lsx(const void *buf, size_t len) "2:" : "=&r"(ret), "+r"(p) : "r"(buf), "r"(e), "r"(l) - : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + : "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", + "$fcc0"); return ret; } @@ -119,7 +120,8 @@ static bool buffer_is_zero_lasx(const void *buf, size_t len) "3:" : "=&r"(ret), "+r"(p) : "r"(buf), "r"(e), "r"(l) - : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + : "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", + "$fcc0"); return ret; } diff --git a/host/include/loongarch64/host/load-extract-al16-al8.h.inc b/host/include/loongarch64/host/load-extract-al16-al8.h.inc index d1fb59d8af..9528521e7d 100644 --- a/host/include/loongarch64/host/load-extract-al16-al8.h.inc +++ b/host/include/loongarch64/host/load-extract-al16-al8.h.inc @@ -31,7 +31,7 @@ static inline uint64_t load_atom_extract_al16_or_al8(void *pv, int s) asm("vld $vr0, %2, 0\n\t" "vpickve2gr.d %0, $vr0, 0\n\t" "vpickve2gr.d %1, $vr0, 1" - : "=r"(l), "=r"(h) : "r"(ptr_align), "m"(*ptr_align) : "f0"); + : "=r"(l), "=r"(h) : "r"(ptr_align), "m"(*ptr_align) : "$f0"); return (l >> shr) | (h << (-shr & 63)); } From 1267e1ddeb65db5405405adb711272133fe9c670 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 12 Mar 2025 14:26:20 +0800 Subject: [PATCH 2754/2892] docs/system: Add entry for LoongArch system Add index entry for LoongArch system and do some small modification with LoongArch document with rst syntax. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- docs/system/loongarch/virt.rst | 31 ++++++++++--------------------- docs/system/target-loongarch.rst | 19 +++++++++++++++++++ docs/system/targets.rst | 1 + 3 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 docs/system/target-loongarch.rst diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst index 172fba079e..7845878469 100644 --- a/docs/system/loongarch/virt.rst +++ b/docs/system/loongarch/virt.rst @@ -12,14 +12,15 @@ Supported devices ----------------- The ``virt`` machine supports: -- Gpex host bridge -- Ls7a RTC device -- Ls7a IOAPIC device -- ACPI GED device -- Fw_cfg device -- PCI/PCIe devices -- Memory device -- CPU device. Type: la464. + +* Gpex host bridge +* Ls7a RTC device +* Ls7a IOAPIC device +* ACPI GED device +* Fw_cfg device +* PCI/PCIe devices +* Memory device +* CPU device. Type: la464. CPU and machine Type -------------------- @@ -39,13 +40,7 @@ can be accessed by following steps. .. code-block:: bash - ./configure --disable-rdma --prefix=/usr \ - --target-list="loongarch64-softmmu" \ - --disable-libiscsi --disable-libnfs --disable-libpmem \ - --disable-glusterfs --enable-libusb --enable-usb-redir \ - --disable-opengl --disable-xen --enable-spice \ - --enable-debug --disable-capstone --disable-kvm \ - --enable-profiler + ./configure --target-list="loongarch64-softmmu" make -j8 (2) Set cross tools: @@ -53,9 +48,7 @@ can be accessed by following steps. .. code-block:: bash wget https://github.com/loongson/build-tools/releases/download/2022.09.06/loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz - tar -vxf loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz -C /opt - export PATH=/opt/cross-tools/bin:$PATH export LD_LIBRARY_PATH=/opt/cross-tools/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=/opt/cross-tools/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH @@ -74,13 +67,9 @@ Note: To build the release version of the bios, set --buildtarget=RELEASE, .. code-block:: bash git clone https://github.com/loongson/linux.git - cd linux - git checkout loongarch-next - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- loongson3_defconfig - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- -j32 Note: The branch of linux source code is loongarch-next. diff --git a/docs/system/target-loongarch.rst b/docs/system/target-loongarch.rst new file mode 100644 index 0000000000..316c604b91 --- /dev/null +++ b/docs/system/target-loongarch.rst @@ -0,0 +1,19 @@ +.. _LoongArch-System-emulator: + +LoongArch System emulator +------------------------- + +QEMU can emulate loongArch 64 bit systems via the +``qemu-system-loongarch64`` binary. Only one machine type ``virt`` is +supported. + +When using KVM as accelerator, QEMU can emulate la464 cpu model. And when +using the default cpu model with TCG as accelerator, QEMU will emulate a +subset of la464 cpu features that should be enough to run distributions +built for the la464. + +Board-specific documentation +============================ + +.. toctree:: + loongarch/virt diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 224fadae71..38e2418801 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -18,6 +18,7 @@ Contents: target-arm target-avr + target-loongarch target-m68k target-mips target-ppc From b8d5503a3ecf8bcf75e4960d04215f71dbfd5dd2 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 21 Mar 2025 09:13:58 +0800 Subject: [PATCH 2755/2892] target/loongarch: fix bad shift in check_ps() In expression 1ULL << tlb_ps, left shifting by more than 63 bits has undefined behavior. The shift amount, tlb_ps, is as much as 64. check "tlb_ps >=64" to fix. Resolves: Coverity CID 1593475 Fixes: d882c284a3 ("target/loongarch: check tlb_ps") Suggested-by: Peter Maydell Signed-off-by: Song Gao Reviewed-by: Bibo Mao Signed-off-by: Bibo Mao --- target/loongarch/internals.h | 2 +- target/loongarch/tcg/csr_helper.c | 2 +- target/loongarch/tcg/tlb_helper.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 1cd959a766..9fdc3059d8 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -43,7 +43,7 @@ enum { TLBRET_PE = 7, }; -bool check_ps(CPULoongArchState *ent, int ps); +bool check_ps(CPULoongArchState *ent, uint8_t ps); extern const VMStateDescription vmstate_loongarch_cpu; diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 379c71e741..6a7a65c860 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -115,7 +115,7 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) { - int shift, ptbase; + uint8_t shift, ptbase; int64_t old_v = env->CSR_PWCL; /* diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 646dbf59de..bd8081e886 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -19,12 +19,12 @@ #include "exec/log.h" #include "cpu-csr.h" -bool check_ps(CPULoongArchState *env, int tlb_ps) +bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) { - if (tlb_ps > 64) { - return false; - } - return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); + if (tlb_ps >= 64) { + return false; + } + return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); } void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, From 0cb6498b4ce8d0e800e85a43429919c208537405 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 20 Mar 2025 14:40:23 +1000 Subject: [PATCH 2756/2892] ppc/amigaone: Check blk_pwrite return value Coverity reported that return value of blk_pwrite() maybe should not be ignored. We can't do much if this happens other than report an error but let's do that to silence this report. Resolves: Coverity CID 1593725 Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Message-ID: <20250314200140.2DBE74E6069@zero.eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 483512125f..5d787c3059 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -108,8 +108,8 @@ static void nvram_write(void *opaque, hwaddr addr, uint64_t val, uint8_t *p = memory_region_get_ram_ptr(&s->mr); p[addr] = val; - if (s->blk) { - blk_pwrite(s->blk, addr, 1, &val, 0); + if (s->blk && blk_pwrite(s->blk, addr, 1, &val, 0) < 0) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); } } @@ -151,15 +151,17 @@ static void nvram_realize(DeviceState *dev, Error **errp) *c = cpu_to_be32(CRC32_DEFAULT_ENV); /* Also copies terminating \0 as env is terminated by \0\0 */ memcpy(p + 4, default_env, sizeof(default_env)); - if (s->blk) { - blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0); + if (s->blk && + blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0) < 0 + ) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); } return; } if (*c == 0) { *c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4)); - if (s->blk) { - blk_pwrite(s->blk, 0, 4, p, 0); + if (s->blk && blk_pwrite(s->blk, 0, 4, p, 0) < 0) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); } } if (be32_to_cpu(*c) != crc) { From 667413f5bfe3c1c4f082c9534b84e70c1ef3ff3a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 20 Mar 2025 14:41:31 +1000 Subject: [PATCH 2757/2892] ppc/amigaone: Constify default_env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable holding default env is not supposed to be written. Signed-off-by: BALATON Zoltan Reviewed-by: Nicholas Piggin Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250314200145.08E0F4E6067@zero.eik.bme.hu> Signed-off-by: Nicholas Piggin --- hw/ppc/amigaone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 5d787c3059..e9407a51b5 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -63,7 +63,7 @@ static const char dummy_fw[] = { #define NVRAM_ADDR 0xfd0e0000 #define NVRAM_SIZE (4 * KiB) -static char default_env[] = +static const char default_env[] = "baudrate=115200\0" "stdout=vga\0" "stdin=ps2kbd\0" From 1490d0bcdfcb78b4503cae42353d3dd50f4e9d96 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Thu, 20 Mar 2025 14:50:24 +1000 Subject: [PATCH 2758/2892] ppc/spapr: fix default cpu for pre-9.0 machines. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When POWER10 CPU was made as default, we missed keeping POWER9 as default for older pseries releases (pre-9.0) at that time. This caused breakge in default cpu evaluation for older pseries machines and hence this fix. Fixes: 51113013f3 ("ppc/spapr: change pseries machine default to POWER10 CPU") Cc: qemu-stable@nongnu.org Signed-off-by: Harsh Prateek Bora Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250313094705.2361997-1-harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 9865d7147f..b0a0f8c689 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4816,6 +4816,7 @@ static void spapr_machine_8_2_class_options(MachineClass *mc) { spapr_machine_9_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.2"); } DEFINE_SPAPR_MACHINE(8, 2); From 8defe9da08135d03e054f20cb8fea4389be96e18 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 20 Mar 2025 21:39:59 +1000 Subject: [PATCH 2759/2892] target/ppc: Fix facility interrupt checks for VSX Facility interrupt checks in general should come after the ISA version check, because the facility interrupt and facility type themselves are ISA dependent and should not appear on CPUs where the instruction does not exist at all. This resolves a QEMU crash booting NetBSD/macppc due to qemu: fatal: Raised an exception without defined vector 94 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2741 Cc: Chinmay Rath Cc: qemu-stable@nongnu.org Debugged-by: Richard Henderson Reviewed-by: Richard Henderson Fixes: aa0f34ec3fc7 ("target/ppc: implement vrlq") Fixes: 7419dc5b2b5b ("target/ppc: Move VSX vector storage access insns to decodetree.") Signed-off-by: Nicholas Piggin --- target/ppc/translate/vmx-impl.c.inc | 2 +- target/ppc/translate/vsx-impl.c.inc | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 70d0ad2e71..92d6e8c603 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -994,8 +994,8 @@ static bool do_vector_rotl_quad(DisasContext *ctx, arg_VX *a, bool mask, { TCGv_i64 ah, al, vrb, n, t0, t1, zero = tcg_constant_i64(0); - REQUIRE_VECTOR(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); ah = tcg_temp_new_i64(); al = tcg_temp_new_i64(); diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index a869f30e86..00ad57c628 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -61,8 +61,8 @@ static bool trans_LXVD2X(DisasContext *ctx, arg_LXVD2X *a) TCGv EA; TCGv_i64 t0; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); @@ -80,8 +80,8 @@ static bool trans_LXVW4X(DisasContext *ctx, arg_LXVW4X *a) TCGv EA; TCGv_i64 xth, xtl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); @@ -113,12 +113,12 @@ static bool trans_LXVWSX(DisasContext *ctx, arg_LXVWSX *a) TCGv EA; TCGv_i32 data; + REQUIRE_INSNS_FLAGS2(ctx, ISA300); if (a->rt < 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); } - REQUIRE_INSNS_FLAGS2(ctx, ISA300); gen_set_access_type(ctx, ACCESS_INT); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); @@ -133,8 +133,8 @@ static bool trans_LXVDSX(DisasContext *ctx, arg_LXVDSX *a) TCGv EA; TCGv_i64 data; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); gen_set_access_type(ctx, ACCESS_INT); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); @@ -185,8 +185,8 @@ static bool trans_LXVH8X(DisasContext *ctx, arg_LXVH8X *a) TCGv EA; TCGv_i64 xth, xtl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); @@ -208,8 +208,8 @@ static bool trans_LXVB16X(DisasContext *ctx, arg_LXVB16X *a) TCGv EA; TCGv_i128 data; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); @@ -312,8 +312,8 @@ static bool trans_STXVD2X(DisasContext *ctx, arg_STXVD2X *a) TCGv EA; TCGv_i64 t0; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); @@ -331,8 +331,8 @@ static bool trans_STXVW4X(DisasContext *ctx, arg_STXVW4X *a) TCGv EA; TCGv_i64 xsh, xsl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); @@ -364,8 +364,8 @@ static bool trans_STXVH8X(DisasContext *ctx, arg_STXVH8X *a) TCGv EA; TCGv_i64 xsh, xsl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); @@ -394,8 +394,8 @@ static bool trans_STXVB16X(DisasContext *ctx, arg_STXVB16X *a) TCGv EA; TCGv_i128 data; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); From 73c0c904fc99e2ceecbbded84ec76d40d3f2daae Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 20 Mar 2025 22:24:40 +1000 Subject: [PATCH 2760/2892] target/ppc: Fix e200 duplicate SPRs DSRR0/1 registers are in the BookE ISA not e200 specific, so remove the duplicate e200 register definitions. Cc: Roman Kapl Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2768 Fixes: 0e3bf4890906 ("ppc: add DBCR based debugging") Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 8b590e7f17..7decc09aec 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2744,14 +2744,6 @@ static void init_proc_e200(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); /* TOFIX */ - spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register(env, SPR_BOOKE_DSRR1, "DSRR1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); init_tlbs_emb(env); init_excp_e200(env, 0xFFFF0000UL); From 14fb6dbbc50f43057202c685c3aa017287cca37f Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Wed, 19 Feb 2025 15:58:39 +0300 Subject: [PATCH 2761/2892] Makefile: "make dist" generates a .xz, not .bz2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 9bc9e9511944 (make-release: switch to .xz format by default) Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b65b0bd41a..c92a3cf785 100644 --- a/Makefile +++ b/Makefile @@ -207,10 +207,10 @@ clean: recurse-clean VERSION = $(shell cat $(SRC_PATH)/VERSION) -dist: qemu-$(VERSION).tar.bz2 +dist: qemu-$(VERSION).tar.xz -qemu-%.tar.bz2: - $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" +qemu-%.tar.xz: + $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.xz,%,$@)" distclean: clean recurse-distclean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : From 94a9471ceb656ad45e86aa07e98d8d0c3b8afe82 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 11 Mar 2025 14:27:14 +0100 Subject: [PATCH 2762/2892] docs: Explain how to use passt Add a chapter to explain how to use passt(1) instead of '-net user'. passt(1) can be connected to QEMU using UNIX socket or vhost-user. With vhost-user, migration of the VM is allowed and internal state of passt(1) is transfered from one side to the other Bug: https://gitlab.com/qemu-project/qemu/-/issues/2827 Signed-off-by: Laurent Vivier Reviewed-by: David Gibson Reviewed-by: Stefano Brivio Signed-off-by: Michael Tokarev --- docs/system/devices/net.rst | 100 ++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index 2ab516d4b0..a3efbdcabd 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -77,6 +77,106 @@ When using the ``'-netdev user,hostfwd=...'`` option, TCP or UDP connections can be redirected from the host to the guest. It allows for example to redirect X11, telnet or SSH connections. +Using passt as the user mode network stack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +passt_ can be used as a simple replacement for SLIRP (``-net user``). +passt doesn't require any capability or privilege. passt has +better performance than ``-net user``, full IPv6 support and better security +as it's a daemon that is not executed in QEMU context. + +passt can be connected to QEMU either by using a socket +(``-netdev stream``) or using the vhost-user interface (``-netdev vhost-user``). +See `passt(1)`_ for more details on passt. + +.. _passt: https://passt.top/ +.. _passt(1): https://passt.top/builds/latest/web/passt.1.html + +To use socket based passt interface: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Start passt as a daemon:: + + passt --socket ~/passt.socket + +If ``--socket`` is not provided, passt will print the path of the UNIX domain socket QEMU can connect to (``/tmp/passt_1.socket``, ``/tmp/passt_2.socket``, +...). Then you can connect your QEMU instance to passt: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -device virtio-net-pci,netdev=netdev0 -netdev stream,id=netdev0,server=off,addr.type=unix,addr.path=~/passt.socket + +Where ``~/passt.socket`` is the UNIX socket created by passt to +communicate with QEMU. + +To use vhost-based interface: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Start passt with ``--vhost-user``:: + + passt --vhost-user --socket ~/passt.socket + +Then to connect QEMU: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -m $RAMSIZE -chardev socket,id=chr0,path=~/passt.socket -netdev vhost-user,id=netdev0,chardev=chr0 -device virtio-net,netdev=netdev0 -object memory-backend-memfd,id=memfd0,share=on,size=$RAMSIZE -numa node,memdev=memfd0 + +Where ``$RAMSIZE`` is the memory size of your VM ``-m`` and ``-object memory-backend-memfd,size=`` must match. + +Migration of passt: +^^^^^^^^^^^^^^^^^^^ + +When passt is connected to QEMU using the vhost-user interface it can +be migrated with QEMU and the network connections are not interrupted. + +As passt runs with no privileges, it relies on passt-repair to save and +load the TCP connections state, using the TCP_REPAIR socket option. +The passt-repair helper needs to have the CAP_NET_ADMIN capability, or run as root. If passt-repair is not available, TCP connections will not be preserved. + +Example of migration of a guest on the same host +________________________________________________ + +Before being able to run passt-repair, the CAP_NET_ADMIN capability must be set +on the file, run as root:: + + setcap cap_net_admin+eip ./passt-repair + +Start passt for the source side:: + + passt --vhost-user --socket ~/passt_src.socket --repair-path ~/passt-repair_src.socket + +Where ``~/passt-repair_src.socket`` is the UNIX socket created by passt to +communicate with passt-repair. The default value is the ``--socket`` path +appended with ``.repair``. + +Start passt-repair:: + + passt-repair ~/passt-repair_src.socket + +Start source side QEMU with a monitor to be able to send the migrate command: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] [...VHOST USER OPTIONS...] -monitor stdio + +Start passt for the destination side:: + + passt --vhost-user --socket ~/passt_dst.socket --repair-path ~/passt-repair_dst.socket + +Start passt-repair:: + + passt-repair ~/passt-repair_dst.socket + +Start QEMU with the ``-incoming`` parameter: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] [...VHOST USER OPTIONS...] -incoming tcp:localhost:4444 + +Then in the source guest monitor the migration can be started:: + + (qemu) migrate tcp:localhost:4444 + +A separate passt-repair instance must be started for every migration. In the case of a failed migration, passt-repair also needs to be restarted before trying +again. + Hubs ~~~~ From a028e04c89ea782f03c78db438239cfb7a47b4e9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 20 Mar 2025 15:37:10 +0900 Subject: [PATCH 2763/2892] mailmap: Update email address for Akihiko Odaki It's too late but better than never. Signed-off-by: Akihiko Odaki Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 5f6df414e1..33fe75400f 100644 --- a/.mailmap +++ b/.mailmap @@ -67,6 +67,7 @@ Andrey Drobyshev Andrey Drobyshev via BALATON Zoltan via # Next, replace old addresses by a more recent one. +Akihiko Odaki Aleksandar Markovic Aleksandar Markovic Aleksandar Markovic From 560429fd746adf04b57237c3c4a38ecfd906c592 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Mar 2025 15:11:53 +0100 Subject: [PATCH 2764/2892] hw/uefi: flush variable store to disk in post load Make live migration more robust. Commit 4c0cfc72b31a ("pflash_cfi01: write flash contents to bdrv on incoming migration") elaborates in detail on the motivation. Cc: Peter Krempa Reviewed-by: Peter Krempa Signed-off-by: Gerd Hoffmann Message-ID: <20250319141159.1461621-2-kraxel@redhat.com> --- hw/uefi/var-service-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index 8ed8378ab9..4836a0cb81 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -29,6 +29,7 @@ static int uefi_vars_post_load(void *opaque, int version_id) uefi_vars_state *uv = opaque; uefi_vars_update_storage(uv); + uefi_vars_json_save(uv); uv->buffer = g_malloc(uv->buf_size); return 0; } From ae24cf139ba681f8ce3dc809f3f1119b16c73043 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Mar 2025 15:11:54 +0100 Subject: [PATCH 2765/2892] hw/uefi: fix error handling in uefi_vars_json_save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catch lseek errors. Return on errors. Use autoptr for the GString to simplify cleanup. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250319141159.1461621-3-kraxel@redhat.com> --- hw/uefi/var-service-json.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index 761082c11f..f1c20a6b8c 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -178,7 +178,7 @@ void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) void uefi_vars_json_save(uefi_vars_state *uv) { - GString *gstr; + g_autoptr(GString) gstr = NULL; int rc; if (uv->jsonfd == -1) { @@ -187,18 +187,25 @@ void uefi_vars_json_save(uefi_vars_state *uv) gstr = uefi_vars_to_json(uv); - lseek(uv->jsonfd, 0, SEEK_SET); + rc = lseek(uv->jsonfd, 0, SEEK_SET); + if (rc < 0) { + warn_report("%s: lseek error", __func__); + return; + } + rc = ftruncate(uv->jsonfd, 0); if (rc != 0) { warn_report("%s: ftruncate error", __func__); + return; } + rc = write(uv->jsonfd, gstr->str, gstr->len); if (rc != gstr->len) { warn_report("%s: write error", __func__); + return; } - fsync(uv->jsonfd); - g_string_free(gstr, true); + fsync(uv->jsonfd); } void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) From 761d0b5fb7e0f0a6a36c5fc449c6feda2b78af79 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Mar 2025 15:11:55 +0100 Subject: [PATCH 2766/2892] hw/uefi: fix error handling in uefi_vars_json_load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catch lseek errors. Return on read errors. Fixes: CID 1593154 Fixes: CID 1593157 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250319141159.1461621-4-kraxel@redhat.com> --- hw/uefi/var-service-json.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index f1c20a6b8c..ad3462cd15 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -214,7 +214,7 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) QObject *qobj; Visitor *v; char *str; - size_t len; + ssize_t len; int rc; if (uv->jsonfd == -1) { @@ -222,7 +222,12 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) } len = lseek(uv->jsonfd, 0, SEEK_END); + if (len < 0) { + warn_report("%s: lseek error", __func__); + return; + } if (len == 0) { + /* empty file */ return; } @@ -231,6 +236,8 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) rc = read(uv->jsonfd, str, len); if (rc != len) { warn_report("%s: read error", __func__); + g_free(str); + return; } str[len] = 0; From 5807508fad5344706f7d644e4232d065cb1ac2ea Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Mar 2025 15:11:56 +0100 Subject: [PATCH 2767/2892] hw/uefi-vars-sysbus: allow for riscv virt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the device being added to riscv virt VMs. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Daniel P. Berrangé Signed-off-by: Gerd Hoffmann Message-ID: <20250319141159.1461621-5-kraxel@redhat.com> --- hw/riscv/virt.c | 2 ++ hw/uefi/Kconfig | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index dae46f4733..e517002fdf 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -57,6 +57,7 @@ #include "hw/acpi/aml-build.h" #include "qapi/qapi-visit-common.h" #include "hw/virtio/virtio-iommu.h" +#include "hw/uefi/var-service-api.h" /* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) @@ -1935,6 +1936,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->plug = virt_machine_device_plug_cb; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/uefi/Kconfig b/hw/uefi/Kconfig index ca6c2bc46a..f139008b63 100644 --- a/hw/uefi/Kconfig +++ b/hw/uefi/Kconfig @@ -1,3 +1,3 @@ config UEFI_VARS bool - default y if X86_64 || AARCH64 + default y if X86_64 || AARCH64 || RISCV64 From e1092f765d9c0bf33762a03fe45e3d0de86c86a6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Mar 2025 15:11:57 +0100 Subject: [PATCH 2768/2892] hw/uefi-vars-sysbus: allow for loongarch virt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the device being added to loongarch virt VMs. Reviewed-by: Daniel P. Berrangé Signed-off-by: Gerd Hoffmann Message-ID: <20250319141159.1461621-6-kraxel@redhat.com> --- hw/loongarch/virt.c | 2 ++ hw/uefi/Kconfig | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a5840ff968..b6f5f6a3b5 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -38,6 +38,7 @@ #include "hw/mem/nvdimm.h" #include "hw/platform-bus.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/mem/pc-dimm.h" #include "system/tpm.h" #include "system/block-backend.h" @@ -1207,6 +1208,7 @@ static void virt_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "v-eiointc", "Enable Virt Extend I/O Interrupt Controller."); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/uefi/Kconfig b/hw/uefi/Kconfig index f139008b63..046d55320e 100644 --- a/hw/uefi/Kconfig +++ b/hw/uefi/Kconfig @@ -1,3 +1,3 @@ config UEFI_VARS bool - default y if X86_64 || AARCH64 || RISCV64 + default y if X86_64 || AARCH64 || RISCV64 || LOONGARCH64 From 5e5d18d2ccd674778715f828df80fdddac73bdea Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 19 Mar 2025 15:11:58 +0100 Subject: [PATCH 2769/2892] docs/firmware: add feature flag for host uefi variable store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Daniel P. Berrangé Signed-off-by: Gerd Hoffmann Message-ID: <20250319141159.1461621-7-kraxel@redhat.com> --- docs/interop/firmware.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 57f55f6c54..745d21d822 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -214,13 +214,23 @@ # PL011 UART. @verbose-static is mutually exclusive # with @verbose-dynamic. # +# @host-uefi-vars: The firmware expects the host to provide an uefi +# variable store. qemu supports that via +# "uefi-vars-sysbus" (aarch64, riscv64, loongarch64) +# or "uefi-vars-x64" (x86_64) devices. The firmware +# will not use flash for nvram. When loading the +# firmware into flash the 'stateless' setup should be +# used. It is recommened to load the firmware into +# memory though. +# # Since: 3.0 ## { 'enum' : 'FirmwareFeature', 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'amd-sev-es', 'amd-sev-snp', 'intel-tdx', - 'enrolled-keys', 'requires-smm', 'secure-boot', + 'enrolled-keys', 'requires-smm', + 'secure-boot', 'host-uefi-vars', 'verbose-dynamic', 'verbose-static' ] } ## From 9d116f42a38cb95a33da837e0b0b50d91e28906b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 21 Mar 2025 11:25:21 +0000 Subject: [PATCH 2770/2892] rust: assertions: add static_assert Add a new assertion that is similar to "const { assert!(...) }" but can be used outside functions and with older versions of Rust. A similar macro is found in Linux, whereas the "static_assertions" crate has a const_assert macro that produces worse error messages. Suggested-by: Peter Maydell Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Link: https://lore.kernel.org/r/20250321112523.1774131-2-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/assertions.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index 104dec3977..bba38cfda1 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -120,3 +120,25 @@ macro_rules! assert_match { ); }; } + +/// Assert at compile time that an expression is true. This is similar +/// to `const { assert!(...); }` but it works outside functions, as well as +/// on versions of Rust before 1.79. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::static_assert; +/// static_assert!("abc".len() == 3); +/// ``` +/// +/// ```compile_fail +/// # use qemu_api::static_assert; +/// static_assert!("abc".len() == 2); // does not compile +/// ``` +#[macro_export] +macro_rules! static_assert { + ($x:expr) => { + const _: () = assert!($x); + }; +} From 5b87a07e76816ed61e5968eb370859a5901b8516 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Mar 2025 11:25:22 +0000 Subject: [PATCH 2771/2892] hw/char/pl011: Pad PL011State struct to same size as Rust impl We have some users of the PL011 struct which embed it directly into their own state structs. This means that the Rust version of the device must have a state struct that is the same size or smaller than the C struct. In commit 9b642097d6b7 ("rust: pl011: switch to safe chardev operation") the Rust PL011 state struct changed from having a bindings::CharBackend to a chardev::CharBackend, which made it grow larger than the C version. This results in an assertion at startup when QEMU was built with Rust enabled: $ qemu-system-arm -M raspi2b -display none ERROR:../../qom/object.c:562:object_initialize_with_type: assertion failed: (size >= type->instance_size) The long-term better approach to this problem would be to move our C device code patterns away from "embed a struct" and (back) to "have a pointer to the device", so we can make the C PL011State struct a private implementation detail rather than exposed to its users. For the short term, add a padding field at the end of the C struct so it's big enough that the Rust state struct can fit. Fixes: 9b642097d6b7 ("rust: pl011: switch to safe chardev operation") Reviewed-by: Zhao Liu Signed-off-by: Peter Maydell Link: https://lore.kernel.org/r/20250321112523.1774131-3-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- include/hw/char/pl011.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index 4fcaf3d7d3..299ca9b18b 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -52,6 +52,11 @@ struct PL011State { Clock *clk; bool migrate_clk; const unsigned char *id; + /* + * Since some users embed this struct directly, we must + * ensure that the C struct is at least as big as the Rust one. + */ + uint8_t padding_for_rust[16]; }; DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr); From cc3d262aa93a42e19c38f6acb6d0f6012a71eb4b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Mar 2025 11:25:23 +0000 Subject: [PATCH 2772/2892] rust: pl011: Check size of state struct at compile time The PL011 device's C implementation exposes its PL011State struct to users of the device, and one common usage pattern is to embed that struct into the user's own state struct. (The internals of the struct are technically visible to the C user of the device, but in practice are treated as implementation details.) This means that the Rust version of the state struct must not be larger than the C version's struct; otherwise it will trip a runtime assertion in object_initialize_type() when the C user attempts to in-place initialize the type. Add a compile-time assertion on the Rust side, so that if we accidentally make the Rust device state larger we know immediately that we need to expand the padding in the C version of the struct. Reviewed-by: Zhao Liu Signed-off-by: Peter Maydell Link: https://lore.kernel.org/r/20250321112523.1774131-4-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 9 ++++++++- rust/wrapper.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index f137b49fea..bf88e0b00a 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,7 +2,7 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, ptr::addr_of_mut}; +use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, @@ -12,6 +12,7 @@ use qemu_api::{ prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField}, + static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, vmstate::VMStateDescription, }; @@ -124,6 +125,12 @@ pub struct PL011State { pub migrate_clock: bool, } +// Some C users of this device embed its state struct into their own +// structs, so the size of the Rust version must not be any larger +// than the size of the C one. If this assert triggers you need to +// expand the padding_for_rust[] array in the C PL011State struct. +static_assert!(size_of::() <= size_of::()); + qom_isa!(PL011State : SysBusDevice, DeviceState, Object); #[repr(C)] diff --git a/rust/wrapper.h b/rust/wrapper.h index d927ad6799..d4fec54657 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -65,3 +65,4 @@ typedef enum memory_order { #include "exec/memattrs.h" #include "qemu/timer.h" #include "exec/address-spaces.h" +#include "hw/char/pl011.h" From 134ab17fffb32a3f86debb4eec9df12f7f833a3b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 19 Mar 2025 12:54:31 +0100 Subject: [PATCH 2773/2892] load_aout: replace bswap_needed with big_endian Targets know whether they are big-endian more than they know if the endianness is different from the host: the former is mostly a constant, at least in machine creation code, while the latter has to be computed with TARGET_BIG_ENDIAN != HOST_BIG_ENDIAN or something like that. load_aout, however, takes a "bswap_needed" argument. Replace it with a "big_endian" argument; even though all users are big-endian, it is cheap enough to keep the optional swapping functionality even for little-endian boards. Signed-off-by: Paolo Bonzini --- hw/core/loader.c | 4 ++-- hw/ppc/mac_newworld.c | 7 +------ hw/ppc/mac_oldworld.c | 7 +------ hw/sparc/sun4m.c | 9 +-------- hw/sparc64/sun4u.c | 9 +-------- include/hw/loader.h | 2 +- 6 files changed, 7 insertions(+), 31 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index ce6ff1b52e..2e35f0aa90 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -226,7 +226,7 @@ static void bswap_ahdr(struct exec *e) ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size) + bool big_endian, hwaddr target_page_size) { int fd; ssize_t size, ret; @@ -241,7 +241,7 @@ ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, if (size < 0) goto fail; - if (bswap_needed) { + if (big_endian != HOST_BIG_ENDIAN) { bswap_ahdr(&e); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index cb3dc3ab48..2d5309d6f5 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -197,11 +197,6 @@ static void ppc_core99_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed = 0; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, @@ -209,7 +204,7 @@ static void ppc_core99_init(MachineState *machine) if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, - bswap_needed, TARGET_PAGE_SIZE); + true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 0dbcea035c..b5814690f5 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -153,11 +153,6 @@ static void ppc_heathrow_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed = 0; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, @@ -165,7 +160,7 @@ static void ppc_heathrow_init(MachineState *machine) if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, - bswap_needed, TARGET_PAGE_SIZE); + true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index a48d3622c5..5aaafb40da 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -233,20 +233,13 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, NULL, NULL, ELFDATA2MSB, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + RAM_size - KERNEL_LOAD_ADDR, true, TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 8ab5cf0461..d3cb7270ff 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -168,13 +168,6 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry, kernel_addr, &kernel_top, NULL, ELFDATA2MSB, EM_SPARCV9, 0, 0); @@ -182,7 +175,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + RAM_size - KERNEL_LOAD_ADDR, true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { diff --git a/include/hw/loader.h b/include/hw/loader.h index 784a93d6c1..d280dc33e9 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -190,7 +190,7 @@ ssize_t load_elf(const char *filename, void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size); + bool big_endian, hwaddr target_page_size); #define LOAD_UIMAGE_LOADADDR_INVALID (-1) From e16354b7f2e70d5ece97854d00141e3df4625cf6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 20 Mar 2025 15:29:33 -0700 Subject: [PATCH 2774/2892] exec/cpu-all: remove BSWAP_NEEDED This identifier is poisoned, so it can't be used from common code anyway. We replace all occurrences with its definition directly. Signed-off-by: Pierrick Bouvier Link: https://lore.kernel.org/r/20250320223002.2915728-2-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- bsd-user/elfload.c | 6 +++--- include/exec/cpu-all.h | 12 ------------ include/exec/poison.h | 1 - linux-user/elfload.c | 8 ++++---- linux-user/syscall_defs.h | 2 +- 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 833fa3bd05..3bca0cc9ed 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -44,7 +44,7 @@ static inline void memcpy_fromfs(void *to, const void *from, unsigned long n) memcpy(to, from, n); } -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_ehdr(struct elfhdr *ehdr) { bswap16s(&ehdr->e_type); /* Object file type */ @@ -111,7 +111,7 @@ static void bswap_note(struct elf_note *en) bswap32s(&en->n_type); } -#else /* ! BSWAP_NEEDED */ +#else static void bswap_ehdr(struct elfhdr *ehdr) { } static void bswap_phdr(struct elf_phdr *phdr, int phnum) { } @@ -119,7 +119,7 @@ static void bswap_shdr(struct elf_shdr *shdr, int shnum) { } static void bswap_sym(struct elf_sym *sym) { } static void bswap_note(struct elf_note *en) { } -#endif /* ! BSWAP_NEEDED */ +#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */ #include "elfcore.c" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 8cd6c00cf8..47b14446b8 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -26,18 +26,6 @@ #include "exec/tswap.h" #include "hw/core/cpu.h" -/* some important defines: - * - * HOST_BIG_ENDIAN : whether the host cpu is big endian and - * otherwise little endian. - * - * TARGET_BIG_ENDIAN : same for the target cpu - */ - -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN -#define BSWAP_NEEDED -#endif - /* Target-endianness CPU memory access functions. These fit into the * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. */ diff --git a/include/exec/poison.h b/include/exec/poison.h index 8ed04b3108..2c151fd1e0 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -37,7 +37,6 @@ #pragma GCC poison TARGET_NAME #pragma GCC poison TARGET_SUPPORTS_MTTCG #pragma GCC poison TARGET_BIG_ENDIAN -#pragma GCC poison BSWAP_NEEDED #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8799e4ea27..fa83d78667 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2121,7 +2121,7 @@ static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) memcpy(to, from, n); } -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_ehdr(struct elfhdr *ehdr) { bswap16s(&ehdr->e_type); /* Object file type */ @@ -3143,7 +3143,7 @@ static bool parse_elf_properties(const ImageSource *src, * The contents of a valid PT_GNU_PROPERTY is a sequence of uint32_t. * Swap most of them now, beyond the header and namesz. */ -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN for (int i = 4; i < n / 4; i++) { bswap32s(note.data + i); } @@ -3999,7 +3999,7 @@ struct target_elf_prpsinfo { char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ }; -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_prstatus(struct target_elf_prstatus *prstatus) { prstatus->pr_info.si_signo = tswap32(prstatus->pr_info.si_signo); @@ -4038,7 +4038,7 @@ static void bswap_note(struct elf_note *en) static inline void bswap_prstatus(struct target_elf_prstatus *p) { } static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {} static inline void bswap_note(struct elf_note *en) { } -#endif /* BSWAP_NEEDED */ +#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */ /* * Calculate file (dump) size of given memory region. diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 86d773add7..5d22759992 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -462,7 +462,7 @@ typedef struct { abi_ulong sig[TARGET_NSIG_WORDS]; } target_sigset_t; -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s) { int i; From ea8a7ceba3aafe4de9e7306df663698809e8381a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:05 +0800 Subject: [PATCH 2775/2892] rust/vmstate: Remove unnecessary unsafe Remove the `unsafe` block of vmsd, because vmsd (passed to vmstate_struct) is defined in Rust side now, and it doesn't need `unsafe`. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index f0510ae769..6698dfe7ae 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -447,7 +447,7 @@ macro_rules! vmstate_struct { }, size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - vmsd: unsafe { $vmsd }, + vmsd: $vmsd, ..$crate::zeroable::Zeroable::ZERO $( .with_varray_flag($crate::call_func_with_field!( $crate::vmstate::vmstate_varray_flag, From 6ca5c3bedf3265358cf9033577dd30ed865d1cb3 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:06 +0800 Subject: [PATCH 2776/2892] rust/vmstate: Fix num_offset in vmstate macros `num_offset` is a member of `VMStateField`, and there's no need to use "." to access this field in a `VMStateField` instance. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 6698dfe7ae..9533b1250f 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -208,7 +208,7 @@ macro_rules! vmstate_of { .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, offset: $crate::offset_of!($struct_name, $field_name), - $(.num_offset: $crate::offset_of!($struct_name, $num),)? + $(num_offset: $crate::offset_of!($struct_name, $num),)? // The calls to `call_func_with_field!` are the magic that // computes most of the VMStateField from the type of the field. info: $crate::info_enum_to_ref!($crate::call_func_with_field!( @@ -440,7 +440,7 @@ macro_rules! vmstate_struct { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, - $(.num_offset: $crate::offset_of!($struct_name, $num),)? + $(num_offset: $crate::offset_of!($struct_name, $num),)? offset: { $crate::assert_field_type!($struct_name, $field_name, $type); $crate::offset_of!($struct_name, $field_name) From c3d80af5ecf4f00db4a8d3ac5eca6edc0c9f061e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:07 +0800 Subject: [PATCH 2777/2892] rust/vmstate: Fix num field when varray flags are set Array type vmstate has the VMStateField with `num` equals its length. When the varray vmstate is built based a array type, the `num` field should be cleaned to 0, because varray uses `num_offset` instead of `num` to store elements number information. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 9533b1250f..e3233303f2 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -275,6 +275,7 @@ impl VMStateField { assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); self.flags = VMStateFlags(self.flags.0 | flag.0); + self.num = 0; // varray uses num_offset instead of num. self } From 20797069c71a90582078448b81de28f227a8403b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:08 +0800 Subject: [PATCH 2778/2892] rust/vmstate: Fix size field of VMStateField with VMS_ARRAY_OF_POINTER flag The `size` field of the VMStateField with VMS_ARRAY_OF_POINTER flag should stores the size of pointer, which depends on platform. Currently, `*const`, `*mut`, `NonNull`, `Box<>` and their wrapper are supported, and they have the same size as `usize`. Store the size (of `usize`) when VMS_ARRAY_OF_POINTER flag is set. The size may be changed when more smart pointers are supported, but now the size of "usize" is enough. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index e3233303f2..e2a1f7a97a 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -256,6 +256,10 @@ impl VMStateField { if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); + // VMS_ARRAY_OF_POINTER flag stores the size of pointer. + // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. + // Resize if more smart pointers are supported. + self.size = std::mem::size_of::(); } self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); From 618258256e6c60957100c5a01ab70f5473020cb9 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:09 +0800 Subject: [PATCH 2779/2892] rust/vmstate: Fix type check for varray in vmstate_struct When pass a varray to vmstate_struct, the `type` parameter should be the type of the element in the varray, for example: vmstate_struct!(HPETState, timers, [0 .. num_timers], VMSTATE_HPET_TIMER, BqlRefCell).with_version_id(0) But this breaks current type check, because it checks the type of `field`, which is an array type (for the above example, type of timers is [BqlRefCell; 32], not BqlRefCell). But the current assert_field_type() can no longer be extended to include new arguments, so a variant of it (a second macro containing the `num = $num:ident` parameter) had to be added to handle array cases. In this new macro, it not only checks the type of element, but also checks whether the `num` (number of elements in varray) is out of range. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/assertions.rs | 15 +++++++++++++++ rust/qemu-api/src/vmstate.rs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index bba38cfda1..eb12e9499a 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -91,6 +91,21 @@ macro_rules! assert_field_type { } }; }; + + ($t:ty, $i:tt, $ti:ty, num = $num:ident) => { + const _: () = { + #[allow(unused)] + fn assert_field_type(v: $t) { + fn types_must_be_equal(_: T) + where + T: $crate::assertions::EqType, + { + } + let index: usize = v.$num.try_into().unwrap(); + types_must_be_equal::<_, &$ti>(&v.$i[index]); + } + }; + }; } /// Assert that an expression matches a pattern. This can also be diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index e2a1f7a97a..9d9cdda993 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -447,7 +447,7 @@ macro_rules! vmstate_struct { .as_ptr() as *const ::std::os::raw::c_char, $(num_offset: $crate::offset_of!($struct_name, $num),)? offset: { - $crate::assert_field_type!($struct_name, $field_name, $type); + $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?); $crate::offset_of!($struct_name, $field_name) }, size: ::core::mem::size_of::<$type>(), From 42c814b1395c39659270248d205deaaaa47a84f2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:10 +0800 Subject: [PATCH 2780/2892] rust/vmstate: Fix "cannot infer type" error in vmstate_struct Rust cannot infer the type (it should be VMStateField) after Zeroable::ZERO, which cause the compiling error. To fix this error, call with_varray_flag() after VMStateField's initialization. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 9d9cdda993..3be3a7260f 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -453,13 +453,15 @@ macro_rules! vmstate_struct { size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, vmsd: $vmsd, - ..$crate::zeroable::Zeroable::ZERO $( - .with_varray_flag($crate::call_func_with_field!( - $crate::vmstate::vmstate_varray_flag, - $struct_name, - $num)) - $(.with_varray_multiply($factor))?)? - } + ..$crate::zeroable::Zeroable::ZERO + } $(.with_varray_flag( + $crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num + ) + ) + $(.with_varray_multiply($factor))?)? }; } From e5655e92a8b984129aed12f24fc50d6e3f63429d Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:11 +0800 Subject: [PATCH 2781/2892] rust/vmstate: Fix unnecessary VMState bound of with_varray_flag() The VMState type bound is not used in with_varray_flag(). And for vmstate_struct, Rust cannot infer the type of `num` from the call_func_with_field(), so this causes the compiling error because it complains "cannot satisfy `_: VMState`" in with_varray_flag(). Note Rust can infer the type in vmstate_of macro so that with_varray_flag() can work at there. It is possible that the different initialization ways in the two macros cause differences in Rust's type inference. But in fact, the VMState type bound is not used in with_varray_flag() and vmstate_varray_flag() has already checked the VMState type, it's safe to drop VMState bound of with_varray_flag(), which can fix the above compiling error. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 3be3a7260f..792a74fdfc 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -275,7 +275,7 @@ impl VMStateField { } #[must_use] - pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); self.flags = VMStateFlags(self.flags.0 | flag.0); From 5006e39cfacbf37e6925239059ae6945e36cf74e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:12 +0800 Subject: [PATCH 2782/2892] rust/vmstate: Relax array check when build varray in vmstate_struct The varry of structure created by vmstate_struct is different with vmstate_of. This is because vmstate_struct uses the `vmsd` to traverse the vmstates of structure's fields, rather than treating the structure directly as a well-defined vmstate. Therefore, there's no need to check array flag when building varray by vmstate_struct. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-9-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 792a74fdfc..0b5af1c90b 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -275,14 +275,20 @@ impl VMStateField { } #[must_use] - pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { - assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField { self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); self.flags = VMStateFlags(self.flags.0 | flag.0); self.num = 0; // varray uses num_offset instead of num. self } + #[must_use] + #[allow(unused_mut)] + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.with_varray_flag_unchecked(flag) + } + #[must_use] pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { assert!(num <= 0x7FFF_FFFFu32); @@ -454,7 +460,7 @@ macro_rules! vmstate_struct { flags: $crate::bindings::VMStateFlags::VMS_STRUCT, vmsd: $vmsd, ..$crate::zeroable::Zeroable::ZERO - } $(.with_varray_flag( + } $(.with_varray_flag_unchecked( $crate::call_func_with_field!( $crate::vmstate::vmstate_varray_flag, $struct_name, From 3baf82e0a17bc037c9c564958a8b90814119d738 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:13 +0800 Subject: [PATCH 2783/2892] rust/vmstate: Re-implement VMState trait for timer binding At present, Rust side has a timer binding "timer::Timer", so the vmstate for timer should base on that binding instead of the raw "binding::QEMUTimer". It's possible to apply impl_vmstate_transparent for cell::Opaque and then impl_vmstate_forward for timer::Timer. But binding::QEMUTimer shouldn't be used directly, so that vmstate for such raw timer type is useless. Thus, apply impl_vmstate_scalar for timer::Timer. And since Opaque<> is useful, apply impl_vmstate_transparent for cell::Opaque as well. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-10-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 0b5af1c90b..01f06ed737 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -27,12 +27,7 @@ use core::{marker::PhantomData, mem, ptr::NonNull}; pub use crate::bindings::{VMStateDescription, VMStateField}; -use crate::{ - bindings::{self, VMStateFlags}, - prelude::*, - qom::Owned, - zeroable::Zeroable, -}; +use crate::{bindings::VMStateFlags, prelude::*, qom::Owned, zeroable::Zeroable}; /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a @@ -344,6 +339,7 @@ impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); +impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); #[macro_export] macro_rules! impl_vmstate_bitsized { @@ -390,7 +386,7 @@ impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); impl_vmstate_scalar!(vmstate_info_uint64, u64); -impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer); +impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); // Pointer types using the underlying type's VMState plus VMS_POINTER // Note that references are not supported, though references to cells From b13100372180fdb052aa6bbce663eea0c59e5db4 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:14 +0800 Subject: [PATCH 2784/2892] rust/vmstate: Support vmstate_validate In C version, VMSTATE_VALIDATE accepts the function pointer, which is used to check if some conditions of structure could meet, although the C version macro doesn't accept any structure as the opaque type. But it's hard to integrate VMSTATE_VALIDAE into vmstate_struct, a new macro has to be introduced to specifically handle the case corresponding to VMSTATE_VALIDATE. One of the difficulties is inferring the type of a callback by its name `test_fn`. We can't directly use `test_fn` as a parameter of test_cb_builder__() to get its type "F", because in this way, Rust compiler will be too conservative on drop check and complain "the destructor for this type cannot be evaluated in constant functions". Fortunately, PhantomData could help in this case, because it is considered to never have a destructor, no matter its field type [*]. The `phantom__()` in the `call_func_with_field` macro provides a good example of using PhantomData to infer type. So copy this idea and apply it to the `vmstate_validate` macro. [*]: https://doc.rust-lang.org/std/ops/trait.Drop.html#drop-check Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-11-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 52 +++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 01f06ed737..9740931fb1 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -25,9 +25,12 @@ //! functionality that is missing from `vmstate_of!`. use core::{marker::PhantomData, mem, ptr::NonNull}; +use std::os::raw::{c_int, c_void}; pub use crate::bindings::{VMStateDescription, VMStateField}; -use crate::{bindings::VMStateFlags, prelude::*, qom::Owned, zeroable::Zeroable}; +use crate::{ + bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable, +}; /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a @@ -508,6 +511,53 @@ macro_rules! vmstate_fields { }} } +pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( + opaque: *mut c_void, + version_id: c_int, +) -> bool { + let owner: &T = unsafe { &*(opaque.cast::()) }; + let version: u8 = version_id.try_into().unwrap(); + // SAFETY: the opaque was passed as a reference to `T`. + F::call((owner, version)) +} + +pub type VMSFieldExistCb = unsafe extern "C" fn( + opaque: *mut std::os::raw::c_void, + version_id: std::os::raw::c_int, +) -> bool; + +#[doc(alias = "VMSTATE_VALIDATE")] +#[macro_export] +macro_rules! vmstate_validate { + ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { + $crate::bindings::VMStateField { + name: ::std::ffi::CStr::as_ptr($test_name), + field_exists: { + const fn test_cb_builder__< + T, + F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>, + >( + _phantom: ::core::marker::PhantomData, + ) -> $crate::vmstate::VMSFieldExistCb { + let _: () = F::ASSERT_IS_SOME; + $crate::vmstate::rust_vms_test_field_exists:: + } + + const fn phantom__(_: &T) -> ::core::marker::PhantomData { + ::core::marker::PhantomData + } + Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) + }, + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 + | $crate::bindings::VMStateFlags::VMS_ARRAY.0, + ), + num: 0, // 0 elements: no data, only run test_fn callback + ..$crate::zeroable::Zeroable::ZERO + } + }; +} + /// A transparent wrapper type for the `subsections` field of /// [`VMStateDescription`]. /// From 1998502196ad81fde58a48aac2256731bf6d0022 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:15 +0800 Subject: [PATCH 2785/2892] rust/vmstate: Add unit test for vmstate_of macro The vmstate has too many combinations of VMStateFlags and VMStateField. Currently, the best way to test is to ensure that the Rust vmstate definition is consistent with the (possibly corresponding) C version. Add a unit test to cover some patterns accepted by vmstate_of macro, which correspond to the following C version macros: * VMSTATE_U16 * VMSTATE_UNUSED * VMSTATE_VARRAY_UINT16_UNSAFE * VMSTATE_VARRAY_MULTIPLY Note: Because vmstate_info_* are defined in vmstate-types.c, it's necessary to link libmigration to rust unit tests. In the future, maybe it's possible to spilt libmigration from rust_qemu_api_objs. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-12-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 5 +- rust/qemu-api/tests/tests.rs | 2 + rust/qemu-api/tests/vmstate_tests.rs | 134 +++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 rust/qemu-api/tests/vmstate_tests.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a3f226ccc2..858685ddd4 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -58,7 +58,8 @@ rust_qemu_api_objs = static_library( libchardev.extract_all_objects(recursive: false), libcrypto.extract_all_objects(recursive: false), libauthz.extract_all_objects(recursive: false), - libio.extract_all_objects(recursive: false)]) + libio.extract_all_objects(recursive: false), + libmigration.extract_all_objects(recursive: false)]) rust_qemu_api_deps = declare_dependency( dependencies: [ qom_ss.dependencies(), @@ -71,7 +72,7 @@ rust_qemu_api_deps = declare_dependency( test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', - 'tests/tests.rs', + files('tests/tests.rs', 'tests/vmstate_tests.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 269122e7ec..99a7aab6fe 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -17,6 +17,8 @@ use qemu_api::{ zeroable::Zeroable, }; +mod vmstate_tests; + // Test that macros can compile. pub static VMSTATE: VMStateDescription = VMStateDescription { name: c_str!("name").as_ptr(), diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs new file mode 100644 index 0000000000..eb1bb2f8a0 --- /dev/null +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) 2025 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ffi::CStr, mem::size_of, slice}; + +use qemu_api::{ + bindings::{vmstate_info_int8, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags}, + c_str, + vmstate::{VMStateDescription, VMStateField}, + vmstate_fields, vmstate_of, vmstate_unused, + zeroable::Zeroable, +}; + +const FOO_ARRAY_MAX: usize = 3; + +// =========================== Test VMSTATE_FOOA =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOA: +// - VMSTATE_U16 +// - VMSTATE_UNUSED +// - VMSTATE_VARRAY_UINT16_UNSAFE +// - VMSTATE_VARRAY_MULTIPLY +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +struct FooA { + arr: [u8; FOO_ARRAY_MAX], + num: u16, + arr_mul: [i8; FOO_ARRAY_MAX], + num_mul: u32, + elem: i8, +} + +static VMSTATE_FOOA: VMStateDescription = VMStateDescription { + name: c_str!("foo_a").as_ptr(), + version_id: 1, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooA, elem), + vmstate_unused!(size_of::()), + vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), + vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_uint16() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"elem\0" + ); + assert_eq!(foo_fields[0].offset, 16); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_unused() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"unused\0" + ); + assert_eq!(foo_fields[1].offset, 0); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_uint16_unsafe() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_UINT16_UNSAFE) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 4); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, 1); + assert_eq!(foo_fields[2].num, 0); + assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_multiply() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_MULTIPLY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_mul\0" + ); + assert_eq!(foo_fields[3].offset, 6); + assert_eq!(foo_fields[3].num_offset, 12); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, 1); + assert_eq!(foo_fields[3].num, 16); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOA. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} From 57c327f3a044ebd6da9efda07e4d264996688110 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:16 +0800 Subject: [PATCH 2786/2892] rust/vmstate: Add unit test for vmstate_{of|struct} macro Add a unit test to cover some patterns accepted by vmstate_of and vmstate_struct macros, which correspond to the following C version macros: * VMSTATE_BOOL_V * VMSTATE_U64 * VMSTATE_STRUCT_VARRAY_UINT8 * (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 * VMSTATE_ARRAY Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-13-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/vmstate_tests.rs | 158 ++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index eb1bb2f8a0..2c3301e02a 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -5,10 +5,14 @@ use std::{ffi::CStr, mem::size_of, slice}; use qemu_api::{ - bindings::{vmstate_info_int8, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags}, + bindings::{ + vmstate_info_bool, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, + vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, + }, c_str, + cell::BqlCell, vmstate::{VMStateDescription, VMStateField}, - vmstate_fields, vmstate_of, vmstate_unused, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, zeroable::Zeroable, }; @@ -132,3 +136,153 @@ fn test_vmstate_varray_multiply() { // The last VMStateField in VMSTATE_FOOA. assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); } + +// =========================== Test VMSTATE_FOOB =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOB: +// - VMSTATE_BOOL_V +// - VMSTATE_U64 +// - VMSTATE_STRUCT_VARRAY_UINT8 +// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 +// - VMSTATE_ARRAY +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +struct FooB { + arr_a: [FooA; FOO_ARRAY_MAX], + num_a: u8, + arr_a_mul: [FooA; FOO_ARRAY_MAX], + num_a_mul: u32, + wrap: BqlCell, + val: bool, + // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. + arr_i64: [i64; FOO_ARRAY_MAX], +} + +static VMSTATE_FOOB: VMStateDescription = VMStateDescription { + name: c_str!("foo_b").as_ptr(), + version_id: 2, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooB, val).with_version_id(2), + vmstate_of!(FooB, wrap), + vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), + vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), + vmstate_of!(FooB, arr_i64), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_bool_v() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"val\0" + ); + assert_eq!(foo_fields[0].offset, 136); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_uint64() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"wrap\0" + ); + assert_eq!(foo_fields[1].offset, 128); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_a\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 60); + assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[2].version_id, 1); + assert_eq!(foo_fields[2].size, 20); + assert_eq!(foo_fields[2].num, 0); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 + ); + assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint32_multiply() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to + // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_a_mul\0" + ); + assert_eq!(foo_fields[3].offset, 64); + assert_eq!(foo_fields[3].num_offset, 124); + assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[3].version_id, 2); + assert_eq!(foo_fields[3].size, 20); + assert_eq!(foo_fields[3].num, 32); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_STRUCT.0 + | VMStateFlags::VMS_VARRAY_UINT32.0 + | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA); + assert!(foo_fields[3].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to + // VMSTATE_ARRAY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), + b"arr_i64\0" + ); + assert_eq!(foo_fields[4].offset, 144); + assert_eq!(foo_fields[4].num_offset, 0); + assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); + assert_eq!(foo_fields[4].version_id, 0); + assert_eq!(foo_fields[4].size, 8); + assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); + assert!(foo_fields[4].vmsd.is_null()); + assert!(foo_fields[4].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOB. + assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); +} From 8df1b0012aba2501bb1654cb4fbdf1b52ce22222 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:17 +0800 Subject: [PATCH 2787/2892] rust/vmstate: Add unit test for pointer case Add a unit test to cover some patterns accepted by vmstate_of macro, which correspond to the following C version macros: * VMSTATE_POINTER * VMSTATE_ARRAY_OF_POINTER Note: Currently, vmstate_struct can't handle the pointer to structure case. Leave this case as a FIXME and use vmstate_unused as a place holder. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-14-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/vmstate_tests.rs | 119 ++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 4 deletions(-) diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 2c3301e02a..027b226675 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -2,15 +2,16 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, mem::size_of, slice}; +use std::{ffi::CStr, mem::size_of, ptr::NonNull, slice}; use qemu_api::{ bindings::{ - vmstate_info_bool, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, - vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, + vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, + vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, c_str, - cell::BqlCell, + cell::{BqlCell, Opaque}, + impl_vmstate_forward, vmstate::{VMStateDescription, VMStateField}, vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, zeroable::Zeroable, @@ -286,3 +287,113 @@ fn test_vmstate_macro_array() { // The last VMStateField in VMSTATE_FOOB. assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); } + +// =========================== Test VMSTATE_FOOC =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOC: +// - VMSTATE_POINTER +// - VMSTATE_ARRAY_OF_POINTER +struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. + +impl_vmstate_forward!(FooCWrapper); + +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +struct FooC { + ptr: *const i32, + ptr_a: NonNull, + arr_ptr: [Box; FOO_ARRAY_MAX], + arr_ptr_wrap: FooCWrapper, +} + +static VMSTATE_FOOC: VMStateDescription = VMStateDescription { + name: c_str!("foo_c").as_ptr(), + version_id: 3, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooC, ptr).with_version_id(2), + // FIXME: Currently vmstate_struct doesn't support the pointer to structure. + // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull) + vmstate_unused!(size_of::>()), + vmstate_of!(FooC, arr_ptr), + vmstate_of!(FooC, arr_ptr_wrap), + }, + ..Zeroable::ZERO +}; + +const PTR_SIZE: usize = size_of::<*mut ()>(); + +#[test] +fn test_vmstate_pointer() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"ptr\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 4); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_ptr\0" + ); + assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); + assert_eq!(foo_fields[2].num_offset, 0); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, PTR_SIZE); + assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer_wrapped() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_ptr_wrap\0" + ); + assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); + assert_eq!(foo_fields[3].num_offset, 0); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, PTR_SIZE); + assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOC. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} From 9bd7e6f7f2f0f97178fe6884b39f40e686567f52 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:18 +0800 Subject: [PATCH 2788/2892] rust/vmstate: Add unit test for vmstate_validate Add a unit test for vmstate_validate, which corresponds to the C version macro: VMSTATE_VALIDATE. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-15-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/vmstate_tests.rs | 82 +++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 027b226675..b8d8b45b19 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -2,7 +2,7 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, mem::size_of, ptr::NonNull, slice}; +use std::{ffi::CStr, mem::size_of, os::raw::c_void, ptr::NonNull, slice}; use qemu_api::{ bindings::{ @@ -13,7 +13,7 @@ use qemu_api::{ cell::{BqlCell, Opaque}, impl_vmstate_forward, vmstate::{VMStateDescription, VMStateField}, - vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate, zeroable::Zeroable, }; @@ -397,3 +397,81 @@ fn test_vmstate_macro_array_of_pointer_wrapped() { // The last VMStateField in VMSTATE_FOOC. assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); } + +// =========================== Test VMSTATE_FOOD =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOD: +// - VMSTATE_VALIDATE + +// Add more member fields when vmstate_of/vmstate_struct support "test" +// parameter. +struct FooD; + +impl FooD { + fn validate_food_0(&self, _version_id: u8) -> bool { + true + } + + fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { + false + } +} + +fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { + true +} + +static VMSTATE_FOOD: VMStateDescription = VMStateDescription { + name: c_str!("foo_d").as_ptr(), + version_id: 3, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0), + vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1), + vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_validate() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) }; + let mut foo_d = FooD; + let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::(); + + // 1st VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"foo_d_0\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert!(foo_fields[0].info.is_null()); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 0); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); + + // 2nd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"foo_d_1\0" + ); + assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); + + // 3rd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"foo_d_2\0" + ); + assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); + + // The last VMStateField in VMSTATE_FOOD. + assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); +} From f7b87e464c8e9d30661fb9f519ed14fb053cd415 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 18 Mar 2025 21:02:19 +0800 Subject: [PATCH 2789/2892] rust/vmstate: Include complete crate path of VMStateFlags in vmstate_clock The use of "bindings::*" masks incomplete path of VMStateFlags. Include complete crate path of VMStateFlags in vmstate_clock, and clean up "bindings::*" in device_class.rs of pl011. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250318130219.1799170-16-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device_class.rs | 8 ++++++-- rust/qemu-api/src/vmstate.rs | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 0b2076ddaa..b4d4a7eb43 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -8,8 +8,12 @@ use std::{ }; use qemu_api::{ - bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, - vmstate_subsections, vmstate_unused, zeroable::Zeroable, + bindings::{qdev_prop_bool, qdev_prop_chr}, + c_str, + prelude::*, + vmstate::VMStateDescription, + vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, + zeroable::Zeroable, }; use crate::device::{PL011Registers, PL011State}; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 9740931fb1..1b2b12eadd 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -487,7 +487,10 @@ macro_rules! vmstate_clock { $crate::offset_of!($struct_name, $field_name) }, size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), - flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_STRUCT.0 + | $crate::bindings::VMStateFlags::VMS_POINTER.0, + ), vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, ..$crate::zeroable::Zeroable::ZERO } From 64acc23c9793e86f2811345f3c122bf3ece8088b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 21 Mar 2025 14:17:52 +0100 Subject: [PATCH 2790/2892] rust: hpet: fix decoding of timer registers Due to a missing "& 0x18", timer registers are not decoded correctly. This breaks the tests/functional/test_x86_64_tuxrun.py functional test. Fixes: 519088b7cf6 ("rust: hpet: decode HPET registers into enums", 2025-03-06) Reported-by: Peter Maydell Tested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 63c1971f0b..3ae3ec25f1 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -776,7 +776,7 @@ impl HPETState { let timer_id: usize = ((addr - 0x100) / 0x20) as usize; if timer_id <= self.num_timers.get() { // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) - TimerRegister::try_from(addr) + TimerRegister::try_from(addr & 0x18) .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) } else { // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) From d4dfb4ffd4008d0d7d3bc9b1dca3e5c5afcc4336 Mon Sep 17 00:00:00 2001 From: Troy Lee Date: Mon, 17 Mar 2025 14:59:37 +0800 Subject: [PATCH 2791/2892] aspeed: Fix maximum number of spi controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 6de4aa8dc544 ("hw/arm/aspeed_ast27x0: Add SoC Support for AST2700 A1") extends ast2700a1 spis_num to 3, but ASPEED_SPIS_NUM defines the maximum number of spi controller to 2, result in ehci[0] is being overwritten in runtime. Signed-off-by: Troy Lee Fixes: 6de4aa8dc544 ("hw/arm/aspeed_ast27x0: Add SoC Support for AST2700 A1") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250317065938.1902272-1-troy_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- include/hw/arm/aspeed_soc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index f899356ed9..f069d17d16 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -42,7 +42,7 @@ #include "hw/char/serial-mm.h" #include "hw/intc/arm_gicv3.h" -#define ASPEED_SPIS_NUM 2 +#define ASPEED_SPIS_NUM 3 #define ASPEED_EHCIS_NUM 2 #define ASPEED_WDTS_NUM 8 #define ASPEED_CPUS_NUM 4 From 7b8cbe5162e69ad629c5326bf3c158b81857955d Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Thu, 20 Mar 2025 17:25:43 +0800 Subject: [PATCH 2792/2892] hw/intc/aspeed: Fix IRQ handler mask check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated the IRQ handler mask check to AND with select variable. This ensures that the interrupt service routine is correctly triggered for the interrupts within the same irq group. For example, both `eth0` and the debug UART are handled in `GICINT132`. Without this fix, the debug console may hang if the `eth0` ISR is not handled. Signed-off-by: Steven Lee Change-Id: Ic3609eb72218dfd68be6057d78b8953b18828709 Reviewed-by: Cédric Le Goater Fixes: d831c5fd8682 ("aspeed/intc: Add AST2700 support") Link: https://lore.kernel.org/qemu-devel/20250320092543.4040672-2-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 3fd417084f..f17bf43925 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -111,7 +111,7 @@ static void aspeed_intc_set_irq_handler(AspeedINTCState *s, outpin_idx = intc_irq->outpin_idx; inpin_idx = intc_irq->inpin_idx; - if (s->mask[inpin_idx] || s->regs[status_reg]) { + if ((s->mask[inpin_idx] & select) || (s->regs[status_reg] & select)) { /* * a. mask is not 0 means in ISR mode * sources interrupt routine are executing. From 78877b2e06464f49f777e086845e094ea7bc82ef Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 21 Mar 2025 17:25:58 +0800 Subject: [PATCH 2793/2892] hw/misc/aspeed_hace: Fix buffer overflow in has_padding function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The maximum padding size is either 64 or 128 bytes and should always be smaller than "req_len". If "padding_size" exceeds "req_len", then "req_len - padding_size" underflows due to "uint32_t" data type, leading to a large incorrect value (e.g., `0xFFXXXXXX`). This causes an out-of-bounds memory access, potentially leading to a buffer overflow. Added a check to ensure "padding_size" does not exceed "req_len" before computing "pad_offset". This prevents "req_len - padding_size" from underflowing and avoids accessing invalid memory. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Fixes: 5cd7d8564a8b563da724b9e6264c967f0a091afa ("aspeed/hace: Support AST2600 HACE ") Link: https://lore.kernel.org/qemu-devel/20250321092623.2097234-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 32a5dbded3..d75da33353 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -128,6 +128,11 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, if (*total_msg_len <= s->total_req_len) { uint32_t padding_size = s->total_req_len - *total_msg_len; uint8_t *padding = iov->iov_base; + + if (padding_size > req_len) { + return false; + } + *pad_offset = req_len - padding_size; if (padding[*pad_offset] == 0x80) { return true; From b876e721f1c939f3e83ac85bd3c1c2821e12b3fa Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 25 Mar 2025 13:58:14 -0400 Subject: [PATCH 2794/2892] Update version for v10.0.0-rc1 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b770373350..cb6a54cd71 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.90 +9.2.91 From 3b9731020d8288fe1081090e7b6fa2566d26320f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 20 Mar 2025 07:36:03 +0100 Subject: [PATCH 2795/2892] tests/functional/test_ppc64_replay: Mark the e500 test as flaky MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test_ppc64_e500 occasionally fails (it just hangs and finally times out). The same issue could also be reproduced with the former Avocado test already (especially if the host system is under heavy load), so it's not a new regression. It's very likely the issue that has been filed at https://gitlab.com/qemu-project/qemu/-/issues/2523 instead (e.g. I could not reproduce the issue in older commits before commit 578912ad), so use this URL for the reasoning in the the decorator. Message-ID: <20250320065012.309520-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_replay.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/test_ppc64_replay.py index 48ce1b7f1e..e8c9c4bcbf 100755 --- a/tests/functional/test_ppc64_replay.py +++ b/tests/functional/test_ppc64_replay.py @@ -5,7 +5,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset +from qemu_test import Asset, skipFlakyTest from replay_kernel import ReplayKernelBase @@ -16,6 +16,7 @@ class Ppc64Replay(ReplayKernelBase): 'day19.tar.xz'), '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2523') def test_ppc64_e500(self): self.set_machine('ppce500') self.cpu = 'e5500' From f0a6b3ec6d8dcaf2c4c1b159904a271e9eebd0df Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 21 Mar 2025 15:58:06 +0000 Subject: [PATCH 2796/2892] tests/functional/meson.build: Bump arm_aspeed_bletchley timeout The arm_aspeed_bletchley timeout takes more than its current 120s timeout on a debug Rust config: $ time (cd build/rust; PYTHONPATH=../../python:../../tests/functional QEMU_TEST_QEMU_BINARY=./qemu-system-arm ./pyvenv/bin/python3 ../../tests/functional/test_arm_aspeed_bletchley.py) TAP version 13 ok 1 test_arm_aspeed_bletchley.BletchleyMachine.test_arm_ast2600_bletchley_openbmc 1..1 real 2m15.536s user 3m21.444s sys 0m11.558s Bump it up to 480s, same as arm_aspeed_rainier. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-ID: <20250321155806.1888867-1-peter.maydell@linaro.org> Signed-off-by: Thomas Huth --- tests/functional/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 96d2828927..0f8be30fe2 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -26,7 +26,7 @@ test_timeouts = { 'arm_aspeed_witherspoon' : 120, 'arm_aspeed_ast2500' : 720, 'arm_aspeed_ast2600' : 1200, - 'arm_aspeed_bletchley' : 120, + 'arm_aspeed_bletchley' : 480, 'arm_aspeed_rainier' : 480, 'arm_bpim2u' : 500, 'arm_collie' : 180, From dba0752f2c16f63825a06e9ebe4ac651bdc95b99 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 24 Mar 2025 13:34:50 +0100 Subject: [PATCH 2797/2892] tests/functional: Add missing require_netdev('user') statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bunch of tests are using "-netdev user" but fail to check for the availability of SLIRP in the binary, so these tests fail if QEMU has been configured with "--disable-slirp" (most of the tests are disabled by default with a decorator, that's likely why nobody noticed this problem yet). Add the missing self.require_netdev('user') statements to skip the tests if SLIRP is not available. Message-ID: <20250324123450.111307-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_rme_sbsaref.py | 1 + tests/functional/test_aarch64_rme_virt.py | 4 +++- tests/functional/test_arm_bpim2u.py | 2 ++ tests/functional/test_arm_cubieboard.py | 2 ++ tests/functional/test_arm_orangepi.py | 4 ++++ tests/functional/test_ppc64_hv.py | 3 +++ tests/functional/test_x86_64_kvm_xen.py | 1 + 7 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py index ddcc9493a6..0f4f6103a1 100755 --- a/tests/functional/test_aarch64_rme_sbsaref.py +++ b/tests/functional/test_aarch64_rme_sbsaref.py @@ -33,6 +33,7 @@ class Aarch64RMESbsaRefMachine(QemuSystemTest): def test_aarch64_rme_sbsaref(self): self.set_machine('sbsa-ref') self.require_accelerator('tcg') + self.require_netdev('user') self.vm.set_console() diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py index 38e01721a4..f4ad4d33d5 100755 --- a/tests/functional/test_aarch64_rme_virt.py +++ b/tests/functional/test_aarch64_rme_virt.py @@ -60,8 +60,10 @@ class Aarch64RMEVirtMachine(QemuSystemTest): # and launching a nested VM using it. def test_aarch64_rme_virt(self): self.set_machine('virt') - self.vm.set_console() self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() self.archive_extract(stack_path_tar_gz, format="tar") diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index 12cd359746..8de6ccba88 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -140,6 +140,8 @@ class BananaPiMachine(LinuxKernelTest): @skipBigDataTest() def test_arm_bpim2u_openwrt_22_03_3(self): self.set_machine('bpim2u') + self.require_netdev('user') + # This test download a 8.9 MiB compressed image and expand it # to 127 MiB. image_path = self.uncompress(self.ASSET_SD_IMAGE) diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index 423db710e8..b87a28154d 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -107,6 +107,8 @@ class CubieboardMachine(LinuxKernelTest): # This test download a 7.5 MiB compressed image and expand it # to 126 MiB. self.set_machine('cubieboard') + self.require_netdev('user') + image_path = self.uncompress(self.ASSET_OPENWRT) image_pow2ceil_expand(image_path) diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index 28919391e5..1815f56e02 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -147,6 +147,8 @@ class OrangePiMachine(LinuxKernelTest): @skipBigDataTest() def test_arm_orangepi_armbian(self): self.set_machine('orangepi-pc') + self.require_netdev('user') + # This test download a 275 MiB compressed image and expand it # to 1036 MiB, but the underlying filesystem is 1552 MiB... # As we expand it to 2 GiB we are safe. @@ -181,6 +183,8 @@ class OrangePiMachine(LinuxKernelTest): @skipBigDataTest() def test_arm_orangepi_uboot_netbsd9(self): self.set_machine('orangepi-pc') + self.require_netdev('user') + # This test download a 304MB compressed image and expand it to 2GB # We use the common OrangePi PC 'plus' build of U-Boot for our secondary # program loader (SPL). We will then set the path to the more specific diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 62f996adf6..1920e91f18 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -125,6 +125,7 @@ class HypervisorTest(QemuSystemTest): def test_hv_pseries(self): self.require_accelerator("tcg") + self.require_netdev('user') self.set_machine('pseries') self.vm.add_args("-accel", "tcg,thread=multi") self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') @@ -136,6 +137,7 @@ class HypervisorTest(QemuSystemTest): def test_hv_pseries_kvm(self): self.require_accelerator("kvm") + self.require_netdev('user') self.set_machine('pseries') self.vm.add_args("-accel", "kvm") self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') @@ -147,6 +149,7 @@ class HypervisorTest(QemuSystemTest): def test_hv_powernv(self): self.require_accelerator("tcg") + self.require_netdev('user') self.set_machine('powernv') self.vm.add_args("-accel", "tcg,thread=multi") self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0', diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/test_x86_64_kvm_xen.py index 3bedef6c98..c6abf6bba3 100755 --- a/tests/functional/test_x86_64_kvm_xen.py +++ b/tests/functional/test_x86_64_kvm_xen.py @@ -41,6 +41,7 @@ class KVMXenGuest(QemuSystemTest): def common_vm_setup(self): # We also catch lack of KVM_XEN support if we fail to launch self.require_accelerator("kvm") + self.require_netdev('user') self.vm.set_console() From 6a93b1c7b4cfa4f5e3c0b8a17177ce14aaa2346c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Mar 2025 17:53:56 +0100 Subject: [PATCH 2798/2892] target/s390x: Fix a typo in s390_cpu_class_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the comma at the end of the line by a semicolon. Fixes: 41868f846d2 ("s390x/cpumodel: "host" and "qemu" as CPU subclasses") Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250324165356.39540-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index d73142600b..1f75629ddc 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -377,7 +377,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) resettable_class_set_parent_phases(rc, NULL, s390_cpu_reset_hold, NULL, &scc->parent_phases); - cc->class_by_name = s390_cpu_class_by_name, + cc->class_by_name = s390_cpu_class_by_name; cc->mmu_index = s390x_cpu_mmu_index; cc->dump_state = s390_cpu_dump_state; cc->query_cpu_fast = s390_query_cpu_fast; From c3612d0b6a400f6d1179fd2f6860b0e31521083e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 25 Mar 2025 07:16:09 +0100 Subject: [PATCH 2799/2892] tests/functional/test_aarch64_virt_gpu: Skip if "dbus" display isn't available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test currently fails if the "dbus" display has not been compiled into the binary (which can happen when CFI has been enabled, for example). Check for the error message to skip the test in that case. While we're at it, also make sure that this test is covered in the right section in the MAINTAINERS file. Message-ID: <20250325061609.272847-1-thuth@redhat.com> Reviewed-by: Alex Bennée Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/functional/test_aarch64_virt_gpu.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8f470a1c9b..c1ccb0acd1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1018,7 +1018,7 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/functional/test_aarch64_virt.py +F: tests/functional/test_aarch64_virt*.py F: tests/functional/test_aarch64_tuxrun.py F: tests/functional/test_arm_tuxrun.py diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 314d994a7a..3844727857 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -74,6 +74,8 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): self.skipTest("Can't access host DRM render node") elif "'type' does not accept value 'egl-headless'" in excp.output: self.skipTest("egl-headless support is not available") + elif "'type' does not accept value 'dbus'" in excp.output: + self.skipTest("dbus display support is not available") else: self.log.info("unhandled launch failure: %s", excp.output) raise excp From 5a8d024f8d3f55846cb8385b025d795fee4afd35 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 25 Mar 2025 07:47:15 +0100 Subject: [PATCH 2800/2892] tests/functional/test_vnc: Skip test if VNC support is not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests currently fail if VNC support has not been compiled into the QEMU binary. Let's add some checks to skip the tests in that case instead. Message-ID: <20250325064715.278876-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/test_vnc.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index 1916be0103..8c9953bdb0 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -12,6 +12,7 @@ import socket from typing import List +from qemu.machine.machine import VMLaunchFailure from qemu_test import QemuSystemTest from qemu_test.ports import Ports @@ -32,7 +33,14 @@ class Vnc(QemuSystemTest): def test_no_vnc_change_password(self): self.vm.add_args('-nodefaults', '-S') self.vm.launch() - self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) + + query_vnc_response = self.vm.qmp('query-vnc') + if 'error' in query_vnc_response: + self.assertEqual(query_vnc_response['error']['class'], + 'CommandNotFound') + self.skipTest('VNC support not available') + self.assertFalse(query_vnc_response['return']['enabled']) + set_password_response = self.vm.qmp('change-vnc-password', password='new_password') self.assertIn('error', set_password_response) @@ -41,9 +49,19 @@ class Vnc(QemuSystemTest): self.assertEqual(set_password_response['error']['desc'], 'Could not set password') + def launch_guarded(self): + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "-vnc: invalid option" in excp.output: + self.skipTest("VNC support not available") + else: + self.log.info("unhandled launch failure: %s", excp.output) + raise excp + def test_change_password_requires_a_password(self): self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999') - self.vm.launch() + self.launch_guarded() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) set_password_response = self.vm.qmp('change-vnc-password', password='new_password') @@ -55,7 +73,7 @@ class Vnc(QemuSystemTest): def test_change_password(self): self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999,password=on') - self.vm.launch() + self.launch_guarded() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) self.vm.cmd('change-vnc-password', password='new_password') @@ -66,7 +84,7 @@ class Vnc(QemuSystemTest): self.assertFalse(check_connect(c)) self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}') - self.vm.launch() + self.launch_guarded() self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a)) self.assertTrue(check_connect(a)) self.assertFalse(check_connect(b)) From 0c346576e401d83becb075e70fef207a09afc3e6 Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Mon, 24 Mar 2025 20:33:28 +0800 Subject: [PATCH 2801/2892] target/loongarch: Fix the cpu unplug resource leak When the cpu is created, qemu_add_vm_change_state_handler is called in the kvm_arch_init_vcpu function to create the VMChangeStateEntry resource. However, the resource is not released when the cpu is destroyed. This results in a qemu process segment error when the virtual machine restarts after the cpu is unplugged. This patch solves the problem by adding the corresponding resource release process to the kvm_arch_destroy_vcpu function. Signed-off-by: Xianglai Li Reviewed-by: Bibo Mao Message-Id: <20250324123328.518076-1-lixianglai@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index eae874c67b..254e4fbdcd 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -426,6 +426,7 @@ struct ArchCPU { const char *dtb_compatible; /* used by KVM_REG_LOONGARCH_COUNTER ioctl to access guest time counters */ uint64_t kvm_state_counter; + VMChangeStateEntry *vmsentry; }; /** diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 7f63e7c8fe..f0e3cfef03 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1080,8 +1080,10 @@ int kvm_arch_init_vcpu(CPUState *cs) uint64_t val; int ret; Error *local_err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(cs); - qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + cpu->vmsentry = qemu_add_vm_change_state_handler( + kvm_loongarch_vm_stage_change, cs); if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { brk_insn = val; @@ -1197,6 +1199,9 @@ void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) int kvm_arch_destroy_vcpu(CPUState *cs) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + qemu_del_vm_change_state_handler(cpu->vmsentry); return 0; } From 694b5a913d03fd7233edf6961c54edac78684519 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Thu, 27 Mar 2025 07:52:10 -0700 Subject: [PATCH 2802/2892] migration: Avoid SNP guest crash due to duplicate cpr blocker With aux-ram-share=off, booting an SNP guest fails with: ../util/error.c:68: error_setv: Assertion `*errp == NULL' failed. This is because a CPR blocker for the guest_memfd ramblock is added twice, once in ram_block_add_cpr_blocker because aux-ram-share=off so rb->fd < 0, and once in ram_block_add for a specific guest_memfd blocker. To fix, add the guest_memfd blocker iff a generic one would not be added by ram_block_add_cpr_blocker. Fixes: 094a3dbc55df ("migration: ram block cpr blockers") Reported-by: Tom Lendacky Reported-by: Michael Roth Tested-by: Tom Lendacky Signed-off-by: Steve Sistare Message-ID: <1743087130-429075-1-git-send-email-steven.sistare@oracle.com> [reword subject line] Signed-off-by: Fabiano Rosas --- system/physmem.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index e97de3ef65..333a5eb94d 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -158,6 +158,7 @@ static void io_mem_init(void); static void memory_map_init(void); static void tcg_log_global_after_sync(MemoryListener *listener); static void tcg_commit(MemoryListener *listener); +static bool ram_is_cpr_compatible(RAMBlock *rb); /** * CPUAddressSpace: all the information a CPU needs about an AddressSpace @@ -1908,13 +1909,18 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) goto out_free; } - error_setg(&new_block->cpr_blocker, - "Memory region %s uses guest_memfd, " - "which is not supported with CPR.", - memory_region_name(new_block->mr)); - migrate_add_blocker_modes(&new_block->cpr_blocker, errp, - MIG_MODE_CPR_TRANSFER, - -1); + /* + * Add a specific guest_memfd blocker if a generic one would not be + * added by ram_block_add_cpr_blocker. + */ + if (ram_is_cpr_compatible(new_block)) { + error_setg(&new_block->cpr_blocker, + "Memory region %s uses guest_memfd, " + "which is not supported with CPR.", + memory_region_name(new_block->mr)); + migrate_add_blocker_modes(&new_block->cpr_blocker, errp, + MIG_MODE_CPR_TRANSFER, -1); + } } ram_size = (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS; From 4a7b8c3f5c7ecf673eabdd8505c7738bda40f813 Mon Sep 17 00:00:00 2001 From: "hemanshu.khilari.foss" Date: Sun, 23 Mar 2025 19:31:11 +0530 Subject: [PATCH 2803/2892] docs: Added docs/specs/riscv-iommu.rst in MAINTAINERS file. Added docs/specs/riscv-iommu.rst under `RISC-V TCG CPUs` section in in MAINTAINERS file since `scripts/get_maintainer.pl -f docs/specs/riscv-iommu.rst` doesn't list any maintainers. Signed-off-by: hemanshu.khilari.foss Reviewed-by: Alistair Francis Message-ID: <20250323140151.9994-1-hemanshu.khilari.foss@gmail.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8f470a1c9b..27f2cfd833 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -319,6 +319,7 @@ L: qemu-riscv@nongnu.org S: Supported F: configs/targets/riscv* F: docs/system/target-riscv.rst +F: docs/specs/riscv-iommu.rst F: target/riscv/ F: hw/char/riscv_htif.c F: hw/riscv/ From e768f0246ce2625880800a2bdce78438b5e9282c Mon Sep 17 00:00:00 2001 From: "hemanshu.khilari.foss" Date: Sun, 23 Mar 2025 12:04:00 +0530 Subject: [PATCH 2804/2892] docs/specs/riscv-iommu: Fixed broken link to external risv iommu document The links to riscv iommu specification document are incorrect. This patch updates all the said link to point to correct location. Cc: qemu-stable@nongnu.org Cc: qemu-riscv@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2808 Signed-off-by: hemanshu.khilari.foss Reviewed-by: Alistair Francis Message-ID: <20250323063404.13206-1-hemanshu.khilari.foss@gmail.com> Signed-off-by: Alistair Francis --- docs/specs/riscv-iommu.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst index 000c7e1f57..991d376fdc 100644 --- a/docs/specs/riscv-iommu.rst +++ b/docs/specs/riscv-iommu.rst @@ -4,7 +4,7 @@ RISC-V IOMMU support for RISC-V machines ======================================== QEMU implements a RISC-V IOMMU emulation based on the RISC-V IOMMU spec -version 1.0 `iommu1.0`_. +version 1.0 `iommu1.0.0`_. The emulation includes a PCI reference device (riscv-iommu-pci) and a platform bus device (riscv-iommu-sys) that QEMU RISC-V boards can use. The 'virt' @@ -14,7 +14,7 @@ riscv-iommu-pci reference device -------------------------------- This device implements the RISC-V IOMMU emulation as recommended by the section -"Integrating an IOMMU as a PCIe device" of `iommu1.0`_: a PCI device with base +"Integrating an IOMMU as a PCIe device" of `iommu1.0.0`_: a PCI device with base class 08h, sub-class 06h and programming interface 00h. As a reference device it doesn't implement anything outside of the specification, @@ -109,7 +109,7 @@ riscv-iommu options: - "s-stage": enabled - "g-stage": enabled -.. _iommu1.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0/riscv-iommu.pdf +.. _iommu1.0.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0.0/riscv-iommu.pdf .. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/ From 897c68fb795cf03b89b6688a6f945d68a765c3e4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 27 Mar 2025 12:20:52 -0300 Subject: [PATCH 2805/2892] Revert "target/riscv/kvm: add missing KVM CSRs" This commit breaks KVM boot on older kernels, like reported in [1], due to senvcfg not being available in them. There's also another problem related to scounteren. Using a recent enough guest buildroot, 'ping' will be build with rdtime support. In this case, doing a ping in a KVM guest while exposing scounteren will result in an error. The root cause relates to how KVM handles scounteren, but QEMU can work around it by initializing scounteren with the host value during init(). Fixing these issues in a non-rushed-bandaid manner results in an amount of design changes that I don't feel comfortable pushing during code freeze, so for 10.0 we'll remove the CSRs and re-introduce them in 10.1 with the adequate support. This reverts commit 4db19d5b21e058e6eb3474b6be470d1184afaa9e. [1] https://lore.kernel.org/qemu-riscv/CABJz62OfUDHYkQ0T3rGHStQprf1c7_E0qBLbLKhfv=+jb0SYAw@mail.gmail.com/ Reported-by: Andrea Bolognani Signed-off-by: Daniel Henrique Barboza Message-ID: <20250327152052.707657-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 4ffeeaa1c9..0f4997a918 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -624,8 +624,6 @@ static void kvm_riscv_reset_regs_csr(CPURISCVState *env) env->stval = 0; env->mip = 0; env->satp = 0; - env->scounteren = 0; - env->senvcfg = 0; } static int kvm_riscv_get_regs_csr(CPUState *cs) @@ -641,8 +639,6 @@ static int kvm_riscv_get_regs_csr(CPUState *cs) KVM_RISCV_GET_CSR(cs, env, stval, env->stval); KVM_RISCV_GET_CSR(cs, env, sip, env->mip); KVM_RISCV_GET_CSR(cs, env, satp, env->satp); - KVM_RISCV_GET_CSR(cs, env, scounteren, env->scounteren); - KVM_RISCV_GET_CSR(cs, env, senvcfg, env->senvcfg); return 0; } @@ -660,8 +656,6 @@ static int kvm_riscv_put_regs_csr(CPUState *cs) KVM_RISCV_SET_CSR(cs, env, stval, env->stval); KVM_RISCV_SET_CSR(cs, env, sip, env->mip); KVM_RISCV_SET_CSR(cs, env, satp, env->satp); - KVM_RISCV_SET_CSR(cs, env, scounteren, env->scounteren); - KVM_RISCV_SET_CSR(cs, env, senvcfg, env->senvcfg); return 0; } From c0b32426ce56182c1ce2a12904f3a702c2ecc460 Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Wed, 26 Mar 2025 17:22:30 +0100 Subject: [PATCH 2806/2892] migration: fix SEEK_CUR offset calculation in qio_channel_block_seek MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SEEK_CUR case in qio_channel_block_seek was incorrectly using the 'whence' parameter instead of the 'offset' parameter when calculating the new position. Fixes: 65cf200a51 ("migration: introduce a QIOChannel impl for BlockDriverState VMState") Signed-off-by: Marco Cavenati Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Message-ID: <20250326162230.3323199-1-Marco.Cavenati@eurecom.fr> Signed-off-by: Fabiano Rosas --- migration/channel-block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/channel-block.c b/migration/channel-block.c index fff8d87094..b0477f5b6d 100644 --- a/migration/channel-block.c +++ b/migration/channel-block.c @@ -123,7 +123,7 @@ qio_channel_block_seek(QIOChannel *ioc, bioc->offset = offset; break; case SEEK_CUR: - bioc->offset += whence; + bioc->offset += offset; break; case SEEK_END: error_setg(errp, "Size of VMstate region is unknown"); From 8dcfb54090330c877ad5a05be5e555714eeb870c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Sep 2024 20:34:09 +0200 Subject: [PATCH 2807/2892] hw/arm/armv7m: Expose and access System Control Space as little endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only build ARM system emulators using little endianness, so the MO_TE definition always expands to MO_LE, and DEVICE_TARGET_ENDIAN to DEVICE_LITTLE_ENDIAN. Replace the definitions by their expanded value, making it closer to the Armv7-M Architecture Reference Manual (ARM DDI 0403E) description: The System Control Space (SCS, address range 0xE000E000 to 0xE000EFFF) is a memory-mapped 4KB address space that provides 32-bit registers for configuration, status reporting and control. All accesses to the SCS are little endian. Fixes: d5d680cacc ("memory: Access MemoryRegion with endianness") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20250312104821.1012-1-philmd@linaro.org> --- hw/arm/armv7m.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 98a6984611..64009174b9 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -140,7 +140,7 @@ static MemTxResult v7m_sysreg_ns_write(void *opaque, hwaddr addr, /* S accesses to the alias act like NS accesses to the real region */ attrs.secure = 0; return memory_region_dispatch_write(mr, addr, value, - size_memop(size) | MO_TE, attrs); + size_memop(size) | MO_LE, attrs); } else { /* NS attrs are RAZ/WI for privileged, and BusFault for user */ if (attrs.user) { @@ -160,7 +160,7 @@ static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr, /* S accesses to the alias act like NS accesses to the real region */ attrs.secure = 0; return memory_region_dispatch_read(mr, addr, data, - size_memop(size) | MO_TE, attrs); + size_memop(size) | MO_LE, attrs); } else { /* NS attrs are RAZ/WI for privileged, and BusFault for user */ if (attrs.user) { @@ -174,7 +174,7 @@ static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr, static const MemoryRegionOps v7m_sysreg_ns_ops = { .read_with_attrs = v7m_sysreg_ns_read, .write_with_attrs = v7m_sysreg_ns_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static MemTxResult v7m_systick_write(void *opaque, hwaddr addr, @@ -187,7 +187,7 @@ static MemTxResult v7m_systick_write(void *opaque, hwaddr addr, /* Direct the access to the correct systick */ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); return memory_region_dispatch_write(mr, addr, value, - size_memop(size) | MO_TE, attrs); + size_memop(size) | MO_LE, attrs); } static MemTxResult v7m_systick_read(void *opaque, hwaddr addr, @@ -199,14 +199,14 @@ static MemTxResult v7m_systick_read(void *opaque, hwaddr addr, /* Direct the access to the correct systick */ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); - return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE, - attrs); + return memory_region_dispatch_read(mr, addr, data, + size_memop(size) | MO_LE, attrs); } static const MemoryRegionOps v7m_systick_ops = { .read_with_attrs = v7m_systick_read, .write_with_attrs = v7m_systick_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; /* From 02e521462405d9fd84b49787f6d8ae9b93d9b13c Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 18 Mar 2025 21:57:07 +0100 Subject: [PATCH 2808/2892] hw/arm/imx8mp-evk: Fix reference count of SoC object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_FSL_IMX8MP is created using object_new(), so must be realized with qdev_realize_and_unref() to keep the reference counting intact. Fixes: a4eefc69b237 "hw/arm: Add i.MX 8M Plus EVK board" Signed-off-by: Bernhard Beschow Reviewed-by: Peter Maydell Message-ID: <20250318205709.28862-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/imx8mp-evk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index e1a7892fd7..e1a21e52f9 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -37,7 +37,7 @@ static void imx8mp_evk_init(MachineState *machine) s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); - qdev_realize(DEVICE(s), NULL, &error_fatal); + qdev_realize_and_unref(DEVICE(s), NULL, &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, machine->ram); From 26c1c41e8ca2d510a3bdb888d9341a07ab13b20c Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 18 Mar 2025 21:57:08 +0100 Subject: [PATCH 2809/2892] hw/arm/fsl-imx8mp: Derive struct FslImx8mpState from TYPE_SYS_BUS_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deriving from TYPE_SYS_BUS_DEVICE fixes the SoC object to be reset upon machine reset. It also makes the SoC implementation not user-creatable which can trigger the following crash: $ ./qemu-system-aarch64 -M virt -device fsl-imx8mp ** ERROR:../../devel/qemu/tcg/tcg.c:1006:tcg_register_thread: assertion failed: (n < tcg_max_ctxs) Bail out! ERROR:../../devel/qemu/tcg/tcg.c:1006:tcg_register_thread: assertion failed: (n < tcg_max_ctxs) Aborted (core dumped) Fixes: a4eefc69b237 "hw/arm: Add i.MX 8M Plus EVK board" Reported-by: Thomas Huth Suggested-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-ID: <20250318205709.28862-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/fsl-imx8mp.c | 2 +- hw/arm/imx8mp-evk.c | 2 +- include/hw/arm/fsl-imx8mp.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index c3f6da6322..82edf61082 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -702,7 +702,7 @@ static void fsl_imx8mp_class_init(ObjectClass *oc, void *data) static const TypeInfo fsl_imx8mp_types[] = { { .name = TYPE_FSL_IMX8MP, - .parent = TYPE_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(FslImx8mpState), .instance_init = fsl_imx8mp_init, .class_init = fsl_imx8mp_class_init, diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index e1a21e52f9..f17d5db466 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -37,7 +37,7 @@ static void imx8mp_evk_init(MachineState *machine) s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); - qdev_realize_and_unref(DEVICE(s), NULL, &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, machine->ram); diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index bc97fc416e..22fdc0d67c 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -26,6 +26,7 @@ #include "hw/timer/imx_gpt.h" #include "hw/usb/hcd-dwc3.h" #include "hw/watchdog/wdt_imx2.h" +#include "hw/sysbus.h" #include "qom/object.h" #include "qemu/units.h" @@ -49,7 +50,7 @@ enum FslImx8mpConfiguration { }; struct FslImx8mpState { - DeviceState parent_obj; + SysBusDevice parent_obj; ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; GICv3State gic; From f32d678252134779d1f129d80435e827877136f5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 18 Mar 2025 21:57:09 +0100 Subject: [PATCH 2810/2892] hw/arm/fsl-imx8mp: Remove unused define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoC has three SPI controllers, not four. Remove the extra define of an SPI IRQ. Fixes: 06908a84f036 "hw/arm/fsl-imx8mp: Add SPI controllers" Reviewed-by: Peter Maydell Signed-off-by: Bernhard Beschow Message-ID: <20250318205709.28862-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/arm/fsl-imx8mp.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index 22fdc0d67c..d016f7d337 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -238,7 +238,6 @@ enum FslImx8mpIrqs { FSL_IMX8MP_ECSPI1_IRQ = 31, FSL_IMX8MP_ECSPI2_IRQ = 32, FSL_IMX8MP_ECSPI3_IRQ = 33, - FSL_IMX8MP_ECSPI4_IRQ = 34, FSL_IMX8MP_I2C1_IRQ = 35, FSL_IMX8MP_I2C2_IRQ = 36, From 581ca58246c1906701680292dfa04af1d129308d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 21 Mar 2025 13:32:31 -0700 Subject: [PATCH 2811/2892] hw/core/cpu: Use size_t for memory_rw_debug len argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match the prototype of cpu_memory_rw_debug(). Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-Id: <20250325224403.4011975-4-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/cpu.h | 2 +- target/sparc/cpu.h | 2 +- target/sparc/mmu_helper.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5d11d26556..abd8764e83 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -154,7 +154,7 @@ struct CPUClass { int (*mmu_index)(CPUState *cpu, bool ifetch); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, - uint8_t *buf, int len, bool is_write); + uint8_t *buf, size_t len, bool is_write); void (*dump_state)(CPUState *cpu, FILE *, int flags); void (*query_cpu_fast)(CPUState *cpu, CpuInfoFast *value); int64_t (*get_arch_id)(CPUState *cpu); diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 462bcb6c0e..68f8c21e7c 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -604,7 +604,7 @@ void dump_mmu(CPUSPARCState *env); #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr, - uint8_t *buf, int len, bool is_write); + uint8_t *buf, size_t len, bool is_write); #endif /* translate.c */ diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 7548d01777..3821cd91ec 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -389,7 +389,7 @@ void dump_mmu(CPUSPARCState *env) * that the sparc ABI is followed. */ int sparc_cpu_memory_rw_debug(CPUState *cs, vaddr address, - uint8_t *buf, int len, bool is_write) + uint8_t *buf, size_t len, bool is_write) { CPUSPARCState *env = cpu_env(cs); target_ulong addr = address; From 82bdce7b9453df2ede67d2b7f01b6e9e4491f408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Mar 2025 23:20:15 +0100 Subject: [PATCH 2812/2892] hw/block/m25p80: Categorize and add description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250325224310.8785-3-philmd@linaro.org> --- hw/block/m25p80.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index b84c6afb32..0887c103e4 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1870,7 +1870,9 @@ static void m25p80_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_m25p80; device_class_set_props(dc, m25p80_properties); device_class_set_legacy_reset(dc, m25p80_reset); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); mc->pi = data; + dc->desc = "Serial Flash"; } static const TypeInfo m25p80_info = { From 43b815eae1bd7ef11a5985e1f52fe65ea698f75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Mar 2025 23:20:22 +0100 Subject: [PATCH 2813/2892] hw/display/dm163: Add description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250325224310.8785-4-philmd@linaro.org> --- hw/display/dm163.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/dm163.c b/hw/display/dm163.c index 75a91f62bd..f6f0ec0c63 100644 --- a/hw/display/dm163.c +++ b/hw/display/dm163.c @@ -330,7 +330,7 @@ static void dm163_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->desc = "DM163"; + dc->desc = "DM163 8x3-channel constant current LED driver"; dc->vmsd = &vmstate_dm163; dc->realize = dm163_realize; rc->phases.hold = dm163_reset_hold; From c0a1dabd0b5ea8da520957c23ebdb243d955991d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Mar 2025 23:36:52 +0100 Subject: [PATCH 2814/2892] hw/dma/i82374: Categorize and add description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250325224310.8785-5-philmd@linaro.org> --- hw/dma/i82374.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 9652d47adc..0bf69ef399 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -150,6 +150,8 @@ static void i82374_class_init(ObjectClass *klass, void *data) dc->realize = i82374_realize; dc->vmsd = &vmstate_i82374; device_class_set_props(dc, i82374_properties); + dc->desc = "Intel 82374 DMA controller"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo i82374_info = { From facfc943cb943ae05997a22642334558751c2bdb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 21 Jan 2025 11:36:55 +0100 Subject: [PATCH 2815/2892] hw/mips: Mark the "mipssim" machine as deprecated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are not aware of anybody still using this machine, support for it has been withdrawn from the Linux kernel (i.e. there also won't be any future development anymore), and we are not aware of any binaries online that could be used for regression testing to avoid that the machine bitrots ... thus let's mark it as deprecated now. Signed-off-by: Thomas Huth Acked-by: Philippe Mathieu-Daudé Message-ID: <20250121103655.1285596-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/about/deprecated.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index e2b4f077d4..76291fdfd6 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -292,6 +292,19 @@ Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` ma Both ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` were added for little endian CPUs. Big endian support is not tested. +Mips ``mipssim`` machine (since 10.0) +''''''''''''''''''''''''''''''''''''' + +Linux dropped support for this virtual machine type in kernel v3.7, and +there does not seem to be anybody around who is still using this board +in QEMU: Most former MIPS-related people are working on other architectures +in their everyday job nowadays, and we are also not aware of anybody still +using old binaries with this board (i.e. there is also no binary available +online to check that this board did not completely bitrot yet). It is +recommended to use another MIPS machine for future MIPS code development +instead. + + Backend options --------------- From 2542d5cf471a38c4ceb9717708178938b96ded47 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 21 Mar 2025 23:12:48 +0100 Subject: [PATCH 2816/2892] hw/rtc/goldfish: keep time offset when resetting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently resetting the leads to resynchronizing the Goldfish RTC with the system clock of the host. In real hardware an RTC reset would not change the wall time. Other RTCs like pl031 do not show this behavior. Move the synchronization of the RTC with the system clock to the instance realization. Cc: qemu-stable@nongnu.org Reported-by: Frederik Du Toit Lotter Fixes: 9a5b40b8427 ("hw: rtc: Add Goldfish RTC device") Signed-off-by: Heinrich Schuchardt Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250321221248.17764-1-heinrich.schuchardt@canonical.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/rtc/goldfish_rtc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index 0f1b53e0e4..d83cc26481 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -239,15 +239,8 @@ static const VMStateDescription goldfish_rtc_vmstate = { static void goldfish_rtc_reset(DeviceState *dev) { GoldfishRTCState *s = GOLDFISH_RTC(dev); - struct tm tm; timer_del(s->timer); - - qemu_get_timedate(&tm, 0); - s->tick_offset = mktimegm(&tm); - s->tick_offset *= NANOSECONDS_PER_SECOND; - s->tick_offset -= qemu_clock_get_ns(rtc_clock); - s->tick_offset_vmstate = 0; s->alarm_next = 0; s->alarm_running = 0; s->irq_pending = 0; @@ -258,6 +251,7 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) { SysBusDevice *dev = SYS_BUS_DEVICE(d); GoldfishRTCState *s = GOLDFISH_RTC(d); + struct tm tm; memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_rtc_ops[s->big_endian], s, @@ -267,6 +261,11 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) sysbus_init_irq(dev, &s->irq); s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); + + qemu_get_timedate(&tm, 0); + s->tick_offset = mktimegm(&tm); + s->tick_offset *= NANOSECONDS_PER_SECOND; + s->tick_offset -= qemu_clock_get_ns(rtc_clock); } static const Property goldfish_rtc_properties[] = { From 490aaae935b6461cfe30660e819317521b255321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Mar 2025 23:21:17 +0100 Subject: [PATCH 2817/2892] hw/misc/pll: Do not expose as user-creatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All these devices are part of SoC components and can not be created manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250325224310.8785-9-philmd@linaro.org> --- hw/misc/bcm2835_cprman.c | 8 ++++++++ hw/misc/npcm_clk.c | 6 ++++++ hw/misc/stm32l4x5_rcc.c | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index aa14cd931f..0c4d4b7de5 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -137,6 +137,8 @@ static void pll_class_init(ObjectClass *klass, void *data) device_class_set_legacy_reset(dc, pll_reset); dc->vmsd = &pll_vmstate; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_pll_info = { @@ -241,6 +243,8 @@ static void pll_channel_class_init(ObjectClass *klass, void *data) device_class_set_legacy_reset(dc, pll_channel_reset); dc->vmsd = &pll_channel_vmstate; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_pll_channel_info = { @@ -362,6 +366,8 @@ static void clock_mux_class_init(ObjectClass *klass, void *data) device_class_set_legacy_reset(dc, clock_mux_reset); dc->vmsd = &clock_mux_vmstate; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_clock_mux_info = { @@ -416,6 +422,8 @@ static void dsi0hsck_mux_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &dsi0hsck_mux_vmstate; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_dsi0hsck_mux_info = { diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index 0e85974cf9..b6a893ffb2 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -1108,6 +1108,8 @@ static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data) dc->desc = "NPCM7xx Clock PLL Module"; dc->vmsd = &vmstate_npcm7xx_clk_pll; + /* Reason: Part of NPCMCLKState component */ + dc->user_creatable = false; } static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) @@ -1116,6 +1118,8 @@ static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) dc->desc = "NPCM7xx Clock SEL Module"; dc->vmsd = &vmstate_npcm7xx_clk_sel; + /* Reason: Part of NPCMCLKState component */ + dc->user_creatable = false; } static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) @@ -1124,6 +1128,8 @@ static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) dc->desc = "NPCM7xx Clock Divider Module"; dc->vmsd = &vmstate_npcm7xx_clk_divider; + /* Reason: Part of NPCMCLKState component */ + dc->user_creatable = false; } static void npcm_clk_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index fd8466dff3..158b743cae 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -150,6 +150,8 @@ static void clock_mux_class_init(ObjectClass *klass, void *data) rc->phases.hold = clock_mux_reset_hold; rc->phases.exit = clock_mux_reset_exit; dc->vmsd = &clock_mux_vmstate; + /* Reason: Part of Stm32l4x5RccState component */ + dc->user_creatable = false; } static void clock_mux_set_enable(RccClockMuxState *mux, bool enabled) @@ -302,6 +304,8 @@ static void pll_class_init(ObjectClass *klass, void *data) rc->phases.hold = pll_reset_hold; rc->phases.exit = pll_reset_exit; dc->vmsd = &pll_vmstate; + /* Reason: Part of Stm32l4x5RccState component */ + dc->user_creatable = false; } static void pll_set_vco_multiplier(RccPllState *pll, uint32_t vco_multiplier) From b2e72fadc8119aa1ad3de9528d991be4d348cca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Mar 2025 23:21:28 +0100 Subject: [PATCH 2818/2892] hw/nvram/xlnx-efuse: Do not expose as user-creatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device is part of SoC components thus can not be created manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20250325224310.8785-10-philmd@linaro.org> --- hw/nvram/xlnx-efuse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index 29e7dd539e..176e88fcd1 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -280,6 +280,8 @@ static void efuse_class_init(ObjectClass *klass, void *data) dc->realize = efuse_realize; device_class_set_props(dc, efuse_properties); + /* Reason: Part of Xilinx SoC */ + dc->user_creatable = false; } static const TypeInfo efuse_info = { From 48ca224250444150f21cbded5745a0e36703b5e7 Mon Sep 17 00:00:00 2001 From: Zheng Huang Date: Fri, 28 Mar 2025 11:21:49 +0800 Subject: [PATCH 2819/2892] hw/scsi/lsi53c895a: fix memory leak in lsi_scsi_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address a memory leak bug in the usages of timer_del(). The issue arises from the incorrect use of the ambiguous timer API timer_del(), which does not free the timer object. The LeakSanitizer report this issue during fuzzing. The correct API timer_free() freed the timer object instead. ================================================================= ==2586273==ERROR: LeakSanitizer: detected memory leaks Direct leak of 48 byte(s) in 1 object(s) allocated from: #0 0x55f2afd89879 in calloc /llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:75:3 #1 0x7f443b93ac50 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x5ec50) #2 0x55f2b053962e in timer_new include/qemu/timer.h:542:12 #3 0x55f2b0514771 in timer_new_us include/qemu/timer.h:582:12 #4 0x55f2b0514288 in lsi_scsi_realize hw/scsi/lsi53c895a.c:2350:24 #5 0x55f2b0452d26 in pci_qdev_realize hw/pci/pci.c:2174:9 Signed-off-by: Zheng Huang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <73cd69f9-ff9b-4cd4-b8aa-265f9d6067b9@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/lsi53c895a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index d85e384ad6..6689ebba25 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -2372,7 +2372,7 @@ static void lsi_scsi_exit(PCIDevice *dev) LSIState *s = LSI53C895A(dev); address_space_destroy(&s->pci_io_as); - timer_del(s->scripts_timer); + timer_free(s->scripts_timer); } static void lsi_class_init(ObjectClass *klass, void *data) From 1c2d03bb0889b7a9a677d53126fb035190683af4 Mon Sep 17 00:00:00 2001 From: Zheng Huang Date: Fri, 28 Mar 2025 17:49:35 +0800 Subject: [PATCH 2820/2892] hw/sd/sdhci: free irq on exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak bug in sdhci_pci_realize() due to s->irq not being freed in sdhci_pci_exit(). Signed-off-by: Zheng Huang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <09ddf42b-a6db-42d5-954b-148d09d8d6cc@gmail.com> [PMD: Moved qemu_free_irq() call before sdhci_common_unrealize()] Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sdhci-pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index 5268c0dee5..bca149e811 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/sd/sdhci.h" #include "sdhci-internal.h" @@ -48,6 +49,7 @@ static void sdhci_pci_exit(PCIDevice *dev) { SDHCIState *s = PCI_SDHCI(dev); + qemu_free_irq(s->irq); sdhci_common_unrealize(s); sdhci_uninitfn(s); } From 70fe5ae121ce3013ac3a29809ed86c3837ad43ee Mon Sep 17 00:00:00 2001 From: Chung-Yi Chen Date: Fri, 28 Mar 2025 20:37:25 +0800 Subject: [PATCH 2821/2892] hw/char/bcm2835_aux: Fix incorrect interrupt ID when RX disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a misconfiguration issue in the read implementation of the AUX_MU_IIR_REG register. This issue can lead to a transmit interrupt being incorrectly interpreted as a receive interrupt when the receive interrupt is disabled and the receive FIFO holds valid bytes. The AUX_MU_IIR_REG register (interrupt ID bits [2:1]) indicates the status of mini UART interrupts: - 00: No interrupts - 01: Transmit FIFO is empty - 10: Receive FIFO is not empty - 11: When the transmit interrupt is enabled and the receive interrupt is disabled, the original code incorrectly sets the interrupt ID bits. Specifically: 1. Transmit FIFO empty, receive FIFO empty - Expected 0b01, returned 0b01 (correct) 2. Transmit FIFO empty, receive FIFO not empty - Expected 0b01, returned 0b10 (incorrect) In the second case, the code sets the interrupt ID to 0b10 (receive FIFO is not empty) even if the receive interrupt is disabled. To fix this, the patch adds additional condition for setting the interrupt ID bits to also check if the receive interrupt is enabled. Reference: BCM2835 ARM Peripherals, page 13. Available on https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf Fixes: 97398d900ca ("bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block") Signed-off-by: Chung-Yi Chen Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250328123725.94176-1-yeechen0207@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/bcm2835_aux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index c6e7eccf7d..9b073fc330 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -98,7 +98,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) * interrupts are active, besides that this cannot occur. At * present, we choose to prioritise the rx interrupt, since * the tx fifo is always empty. */ - if (s->read_count != 0) { + if ((s->iir & RX_INT) && s->read_count != 0) { res |= 0x4; } else { res |= 0x2; From c458f9474d6574505ce9144ab1a90b951e69c1bd Mon Sep 17 00:00:00 2001 From: Zheng Huang Date: Sat, 29 Mar 2025 19:47:19 +0800 Subject: [PATCH 2822/2892] hw/ufs: free irq on exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak bug in ufs_init_pci() due to u->irq not being freed in ufs_exit(). Signed-off-by: Zheng Huang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <43ceb427-87aa-44ee-9007-dbaecc499bba@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ufs/ufs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 857de6e9c2..ee13edacd8 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -25,6 +25,7 @@ #include "qapi/error.h" #include "migration/vmstate.h" #include "scsi/constants.h" +#include "hw/irq.h" #include "trace.h" #include "ufs.h" @@ -1808,6 +1809,8 @@ static void ufs_exit(PCIDevice *pci_dev) { UfsHc *u = UFS(pci_dev); + qemu_free_irq(u->irq); + qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->complete_bh); From 04e99f9eb7920b0f0fcce65686c3bedf5e32a1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 31 Mar 2025 16:46:13 +0200 Subject: [PATCH 2823/2892] hw/pci-host/designware: Fix ATU_UPPER_TARGET register access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix copy/paste error writing to the ATU_UPPER_TARGET register, we want to update the upper 32 bits. Cc: qemu-stable@nongnu.org Reported-by: Joey Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2861 Fixes: d64e5eabc4c ("pci: Add support for Designware IP block") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gustavo Romero Message-Id: <20250331152041.74533-2-philmd@linaro.org> --- hw/pci-host/designware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index c07740bfaa..5598d18f47 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -371,7 +371,7 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, case DESIGNWARE_PCIE_ATU_UPPER_TARGET: viewport->target &= 0x00000000FFFFFFFFULL; - viewport->target |= val; + viewport->target |= (uint64_t)val << 32; break; case DESIGNWARE_PCIE_ATU_LIMIT: From fb5bc76cae61b7c65e71ccf1c6027bf878f5b7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 11:24:35 +0100 Subject: [PATCH 2824/2892] target/hppa: Remove duplicated CPU_RESOLVING_TYPE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPU_RESOLVING_TYPE definition was added in commit 0dacec874fa ("cpu: add CPU_RESOLVING_TYPE macro"), but then added again in commit d3ae32d4d20. Remove the duplication. Fixes: d3ae32d4d20 ("target/hppa: Implement cpu_list") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250321184200.4329-1-philmd@linaro.org> --- target/hppa/cpu.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 7be4a1d380..8b36642b59 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -391,6 +391,4 @@ void hppa_cpu_alarm_timer(void *); #endif G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); -#define CPU_RESOLVING_TYPE TYPE_HPPA_CPU - #endif /* HPPA_CPU_H */ From 070a500cc0da70c1b4c62a6c95e41f0a1b19dc0b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 22 Mar 2025 18:43:36 -0700 Subject: [PATCH 2825/2892] target/avr: Fix buffer read in avr_print_insn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not unconditionally attempt to read 4 bytes, as there may only be 2 bytes remaining in the translator cache. Cc: qemu-stable@nongnu.org Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250325224403.4011975-2-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- target/avr/disas.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/target/avr/disas.c b/target/avr/disas.c index b7689e8d7c..d341030174 100644 --- a/target/avr/disas.c +++ b/target/avr/disas.c @@ -68,28 +68,35 @@ static bool decode_insn(DisasContext *ctx, uint16_t insn); int avr_print_insn(bfd_vma addr, disassemble_info *info) { - DisasContext ctx; + DisasContext ctx = { info }; DisasContext *pctx = &ctx; bfd_byte buffer[4]; uint16_t insn; int status; - ctx.info = info; - - status = info->read_memory_func(addr, buffer, 4, info); + status = info->read_memory_func(addr, buffer, 2, info); if (status != 0) { info->memory_error_func(status, addr, info); return -1; } insn = bfd_getl16(buffer); - ctx.next_word = bfd_getl16(buffer + 2); - ctx.next_word_used = false; + + status = info->read_memory_func(addr + 2, buffer + 2, 2, info); + if (status == 0) { + ctx.next_word = bfd_getl16(buffer + 2); + } if (!decode_insn(&ctx, insn)) { output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]); } - return ctx.next_word_used ? 4 : 2; + if (!ctx.next_word_used) { + return 2; + } else if (status == 0) { + return 4; + } + info->memory_error_func(status, addr + 2, info); + return -1; } From 8001d22b0c67b2fbf8f2cb7b2f44bd7b46b360c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Mar 2025 13:10:33 +0100 Subject: [PATCH 2826/2892] target/sparc: Log unimplemented ASI load/store accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the cache-controller feature is not implemented, log potential ASI access as unimplemented. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Clément Chigot Message-Id: <20250325123927.74939-4-philmd@linaro.org> --- target/sparc/ldst_helper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index b559afc9a9..45882e25db 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -600,6 +600,9 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 0x0C: /* Leon3 Date Cache config */ if (env->def.features & CPU_FEATURE_CACHE_CTRL) { ret = leon3_cache_control_ld(env, addr, size); + } else { + qemu_log_mask(LOG_UNIMP, "0x" TARGET_FMT_lx ": unimplemented" + " address, size: %d\n", addr, size); } break; case 0x01c00a00: /* MXCC control register */ @@ -816,6 +819,9 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case 0x0C: /* Leon3 Date Cache config */ if (env->def.features & CPU_FEATURE_CACHE_CTRL) { leon3_cache_control_st(env, addr, val, size); + } else { + qemu_log_mask(LOG_UNIMP, "0x" TARGET_FMT_lx ": unimplemented" + " address, size: %d\n", addr, size); } break; From fca2817fdcb00e65020c2dcfcb0b23b2a20ea3c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 28 Mar 2025 12:55:24 -0500 Subject: [PATCH 2827/2892] target/mips: Revert TARGET_PAGE_BITS_VARY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert ee3863b9d41 and a08d60bc6c2b. The logic behind changing the system page size because of what the Loongson kernel "prefers" is flawed. In the Loongson-2E manual, section 5.5, it is clear that the cpu supports a 4k page size (along with many others). Similarly for the Loongson-3 series CPUs, the 4k page size is mentioned in the section 7.7 (PageMask Register). Therefore we must continue to support a 4k page size. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250328175526.368121-2-richard.henderson@linaro.org> [PMD: Mention Loongson-3 series CPUs] Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/fuloong2e.c | 1 - hw/mips/loongson3_virt.c | 1 - target/mips/cpu-param.h | 5 ----- target/mips/tcg/system/cp0_helper.c | 7 +------ target/mips/tcg/system/tlb_helper.c | 2 +- 5 files changed, 2 insertions(+), 14 deletions(-) diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 646044e274..2a8507b8b0 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -334,7 +334,6 @@ static void mips_fuloong2e_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("Loongson-2E"); mc->default_ram_size = 256 * MiB; mc->default_ram_id = "fuloong2e.ram"; - mc->minimum_page_bits = 14; machine_add_audiodev_property(mc); } diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index db1cc51314..1da20dccec 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -677,7 +677,6 @@ static void loongson3v_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = LOONGSON_MAX_VCPUS; mc->default_ram_id = "loongson3.highram"; mc->default_ram_size = 1600 * MiB; - mc->minimum_page_bits = 14; mc->default_nic = "virtio-net-pci"; } diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index 11b3ac0ac6..8fcb1b4f5f 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -18,12 +18,7 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif #endif -#ifdef CONFIG_USER_ONLY #define TARGET_PAGE_BITS 12 -#else -#define TARGET_PAGE_BITS_VARY -#define TARGET_PAGE_BITS_MIN 12 -#endif #define TCG_GUEST_DEFAULT_MO (0) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 01a07a169f..8c2114c58a 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -877,18 +877,13 @@ void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) if ((mask >> maskbits) != 0) { goto invalid; } - /* We don't support VTLB entry smaller than target page */ - if ((maskbits + TARGET_PAGE_BITS_MIN) < TARGET_PAGE_BITS) { - goto invalid; - } env->CP0_PageMask = mask << CP0PM_MASK; return; invalid: /* When invalid, set to default target page size. */ - mask = (~TARGET_PAGE_MASK >> TARGET_PAGE_BITS_MIN); - env->CP0_PageMask = mask << CP0PM_MASK; + env->CP0_PageMask = 0; } void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index ca4d6b27bc..123639fa18 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -875,7 +875,7 @@ refill: break; } } - pw_pagemask = m >> TARGET_PAGE_BITS_MIN; + pw_pagemask = m >> TARGET_PAGE_BITS; update_pagemask(env, pw_pagemask << CP0PM_MASK, &pw_pagemask); pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); { From d89b9899babcc01d7ee75f2917da861dc2afbc27 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 28 Mar 2025 12:55:25 -0500 Subject: [PATCH 2828/2892] target/mips: Require even maskbits in update_pagemask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The number of bits set in PageMask must be even. Fixes: d40b55bc1b86 ("target/mips: Fix PageMask with variable page size") Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250328175526.368121-3-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé Cc: qemu-stable@nongnu.org --- target/mips/tcg/system/cp0_helper.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 8c2114c58a..5db8166d45 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -866,24 +866,17 @@ void helper_mtc0_memorymapid(CPUMIPSState *env, target_ulong arg1) void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) { - uint32_t mask; - int maskbits; - /* Don't care MASKX as we don't support 1KB page */ - mask = extract32((uint32_t)arg1, CP0PM_MASK, 16); - maskbits = cto32(mask); + uint32_t mask = extract32((uint32_t)arg1, CP0PM_MASK, 16); + int maskbits = cto32(mask); - /* Ensure no more set bit after first zero */ - if ((mask >> maskbits) != 0) { - goto invalid; + /* Ensure no more set bit after first zero, and maskbits even. */ + if ((mask >> maskbits) == 0 && maskbits % 2 == 0) { + env->CP0_PageMask = mask << CP0PM_MASK; + } else { + /* When invalid, set to default target page size. */ + env->CP0_PageMask = 0; } - env->CP0_PageMask = mask << CP0PM_MASK; - - return; - -invalid: - /* When invalid, set to default target page size. */ - env->CP0_PageMask = 0; } void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) From 256ba7715b109c080c0c77a3923df9e69736ba17 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 28 Mar 2025 12:55:26 -0500 Subject: [PATCH 2829/2892] target/mips: Simplify and fix update_pagemask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When update_pagemask was split from helper_mtc0_pagemask, we failed to actually write to the new parameter but continue to write to env->CP0_PageMask. Thus the use within page_table_walk_refill modifies cpu state and not the local variable as expected. Simplify by renaming to compute_pagemask and returning the value directly. No need for either env or pointer return. Fixes: 074cfcb4dae ("target/mips: Implement hardware page table walker for MIPS32") Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250328175526.368121-4-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé Cc: qemu-stable@nongnu.org --- target/mips/tcg/system/cp0_helper.c | 10 +++++----- target/mips/tcg/system/tlb_helper.c | 2 +- target/mips/tcg/tcg-internal.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 5db8166d45..78e422b0ca 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -864,24 +864,24 @@ void helper_mtc0_memorymapid(CPUMIPSState *env, target_ulong arg1) } } -void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) +uint32_t compute_pagemask(uint32_t val) { /* Don't care MASKX as we don't support 1KB page */ - uint32_t mask = extract32((uint32_t)arg1, CP0PM_MASK, 16); + uint32_t mask = extract32(val, CP0PM_MASK, 16); int maskbits = cto32(mask); /* Ensure no more set bit after first zero, and maskbits even. */ if ((mask >> maskbits) == 0 && maskbits % 2 == 0) { - env->CP0_PageMask = mask << CP0PM_MASK; + return mask << CP0PM_MASK; } else { /* When invalid, set to default target page size. */ - env->CP0_PageMask = 0; + return 0; } } void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) { - update_pagemask(env, arg1, &env->CP0_PageMask); + env->CP0_PageMask = compute_pagemask(arg1); } void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index 123639fa18..df80301a41 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -876,7 +876,7 @@ refill: } } pw_pagemask = m >> TARGET_PAGE_BITS; - update_pagemask(env, pw_pagemask << CP0PM_MASK, &pw_pagemask); + pw_pagemask = compute_pagemask(pw_pagemask << CP0PM_MASK); pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); { target_ulong tmp_entryhi = env->CP0_EntryHi; diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index 74fc1309a7..950e6afc3f 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -47,7 +47,7 @@ bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mmu_init(CPUMIPSState *env, const mips_def_t *def); -void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask); +uint32_t compute_pagemask(uint32_t val); void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra); uint32_t cpu_mips_get_random(CPUMIPSState *env); From f0095c8ad93de7652aba36c4c713d9035417bea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 01:04:43 +0200 Subject: [PATCH 2830/2892] hw/misc/aspeed_scu: Set MemoryRegionOps::impl::access_size to 32-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All MemoryRegionOps::read/write() handlers switch over a 32-bit aligned value, because converted using TO_REG(), which is defined as: #define TO_REG(offset) ((offset) >> 2) So all implementations are 32-bit. Set min/max access_size accordingly. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/qemu-devel/20250331230444.88295-2-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 76cfd91671..6703f3f969 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -427,6 +427,10 @@ static const MemoryRegionOps aspeed_ast2400_scu_ops = { .read = aspeed_scu_read, .write = aspeed_ast2400_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .valid = { .min_access_size = 1, .max_access_size = 4, @@ -437,6 +441,8 @@ static const MemoryRegionOps aspeed_ast2500_scu_ops = { .read = aspeed_scu_read, .write = aspeed_ast2500_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .valid.min_access_size = 4, .valid.max_access_size = 4, .valid.unaligned = false, @@ -779,6 +785,8 @@ static const MemoryRegionOps aspeed_ast2600_scu_ops = { .read = aspeed_ast2600_scu_read, .write = aspeed_ast2600_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .valid.min_access_size = 4, .valid.max_access_size = 4, .valid.unaligned = false, @@ -906,6 +914,8 @@ static const MemoryRegionOps aspeed_ast2700_scu_ops = { .read = aspeed_ast2700_scu_read, .write = aspeed_ast2700_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .valid.min_access_size = 1, .valid.max_access_size = 8, .valid.unaligned = false, @@ -1028,6 +1038,8 @@ static const MemoryRegionOps aspeed_ast2700_scuio_ops = { .read = aspeed_ast2700_scuio_read, .write = aspeed_ast2700_scuio_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .valid.min_access_size = 1, .valid.max_access_size = 8, .valid.unaligned = false, From 20ab88a9066bcacc28acbd7cbe2c617d90bfb27e Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 1 Apr 2025 01:04:44 +0200 Subject: [PATCH 2831/2892] hw/misc/aspeed_scu: Correct minimum access size for AST2500 / AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guest code was performing a byte load to the SCU MMIO region, leading to the guest code crashing (it should be using proper accessors, but that is not Qemu's bug). Hardware and the documentation[1] both agree that byte loads are okay, so change all of the aspeed SCU devices to accept a minimum access size of 1. [1] See the 'ARM Address Space Mapping' table in the ASPEED docs. This is section 6.1 in the ast2400 and ast2700, and 7.1 in the ast2500 and ast2600 datasheets. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2636 Signed-off-by: Joel Stanley Reviewed-by: Troy Lee Message-ID: <20241118021820.4928-1-joel@jms.id.au> [PMD: Rebased, only including SCU changes] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/qemu-devel/20250331230444.88295-3-philmd@linaro.org Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 6703f3f969..1af1a35a08 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -443,7 +443,7 @@ static const MemoryRegionOps aspeed_ast2500_scu_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, - .valid.min_access_size = 4, + .valid.min_access_size = 1, .valid.max_access_size = 4, .valid.unaligned = false, }; @@ -787,7 +787,7 @@ static const MemoryRegionOps aspeed_ast2600_scu_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, - .valid.min_access_size = 4, + .valid.min_access_size = 1, .valid.max_access_size = 4, .valid.unaligned = false, }; From 0adf626718bc0ca9c46550249a76047f8e45da15 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 1 Apr 2025 13:15:45 -0400 Subject: [PATCH 2832/2892] Update version for v10.0.0-rc2 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index cb6a54cd71..ce4b6dd20e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.91 +9.2.92 From c17ad4b11bd268a35506cd976884562df6ca69d7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 8 Jan 2025 21:13:29 +0900 Subject: [PATCH 2833/2892] virtio-net: Fix num_buffers for version 1 The specification says the device MUST set num_buffers to 1 if VIRTIO_NET_F_MRG_RXBUF has not been negotiated. Fixes: df91055db5c9 ("virtio-net: enable virtio 1.0") Signed-off-by: Akihiko Odaki Message-Id: <20250108-buffers-v1-1-a0c85ff31aeb@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index de87cfadff..340c6b6422 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1999,6 +1999,8 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, offsetof(typeof(hdr), virtio_net.hdr.num_buffers), sizeof(hdr.virtio_net.hdr.num_buffers)); + } else { + hdr.virtio_net.hdr.num_buffers = cpu_to_le16(1); } guest_offset = n->has_vnet_hdr ? From 719255486df2fcbe1b8599786b37f4bb80272f1a Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Tue, 25 Mar 2025 02:11:40 +0000 Subject: [PATCH 2834/2892] hw/i386/amd_iommu: Assign pci-id 0x1419 for the AMD IOMMU device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the QEMU-emulated AMD IOMMU device use PCI vendor id 0x1022 (AMD) with device id zero (undefined). Eventhough this does not cause any functional issue for AMD IOMMU driver since it normally uses information in the ACPI IVRS table to probe and initialize the device per recommendation in the AMD IOMMU specification, the device id zero causes the Windows Device Manager utility to show the device as an unknown device. Since Windows only recognizes AMD IOMMU device with device id 0x1419 as listed in the machine.inf file, modify the QEMU AMD IOMMU model to use the id 0x1419 to avoid the issue. This advertise the IOMMU as the AMD IOMMU device for Family 15h (Models 10h-1fh). Signed-off-by: Suravee Suthikulpanit Message-Id: <20250325021140.5676-1-suravee.suthikulpanit@amd.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Yan Vugenfirer Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 5b21cf134a..5f9b952799 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1706,6 +1706,7 @@ static void amdvi_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_AMD; + k->device_id = 0x1419; k->class_id = 0x0806; k->realize = amdvi_pci_realize; From 961841472d25ab70642d182e486b36afeb3fef26 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 26 Mar 2025 15:35:33 +0100 Subject: [PATCH 2835/2892] Revert "iotests: Stop NBD server in test 162 before starting the next one" This reverts commit e2668ba1ed44ad56f2f1653ff5f53b277d534fac. This commit made test 162 fail occasionally with: 162 fail [13:06:40] [13:06:40] 0.2s (last: 0.2s) output mismatch --- tests/qemu-iotests/162.out +++ tests/qemu-iotests/scratch/qcow2-file-162/162.out.bad @@ -3,6 +3,7 @@ === NBD === qemu-img: Could not open 'json:{"driver": "nbd", "host": -1}': address resolution failed for -1:10809: Name or service not known image: nbd://localhost:PORT +./common.rc: line 371: kill: (891116) - No such process image: nbd+unix://?socket=42 The nbd server should normally terminate automatically, so trying to kill it here now seems to cause a race that will cause a test failure when the server terminated before the kill command has been executed. The "Stop NBD server" patch has originally been written to solve another problem with a hanging nbd server, but since that problem has been properly solved by commit 3e1683485656, we now don't need the "_stop_nbd_server" here anymore. Reviewed-by: Hanna Czenczek Signed-off-by: Thomas Huth Message-ID: <20250326143533.932899-1-thuth@redhat.com> Signed-off-by: Eric Blake --- tests/qemu-iotests/162 | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index 956c2c5f33..94dae60d30 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -65,7 +65,6 @@ done $QEMU_IMG info "json:{'driver': 'nbd', 'host': 'localhost', 'port': $port}" \ | grep '^image' | sed -e "s/$port/PORT/" -_stop_nbd_server # This is a test for NBD's bdrv_refresh_filename() implementation: It expects # either host or path to be set, but it must not assume that they are set to From e139bc4b1772575e1f2dcf8e3dbe1df2b684ef1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 16:43:32 +0200 Subject: [PATCH 2836/2892] tcg: Allocate TEMP_VAL_MEM frame in temp_load() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Be sure to allocate the temp frame if it wasn't. In the resolved issues, incomplete dead code elimination left a load at the top of an unreachable loop. We simply need to allocate the stack slot to avoid crashing. Fixes: c896fe29d6c ("TCG code generator") Reported-by: Michael Tokarev Reported-by: Helge Konetzka Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2891 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2899 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401144332.41615-1-philmd@linaro.org> --- tcg/tcg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tcg/tcg.c b/tcg/tcg.c index e8950df2ad..dfd48b8264 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -4671,6 +4671,9 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, ts->mem_coherent = 0; break; case TEMP_VAL_MEM: + if (!ts->mem_allocated) { + temp_allocate_frame(s, ts); + } reg = tcg_reg_alloc(s, desired_regs, allocated_regs, preferred_regs, ts->indirect_base); tcg_out_ld(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset); From 4412d713822b6a0d87efd428ae164e80618ca2d2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 3 Apr 2025 22:32:37 +0200 Subject: [PATCH 2837/2892] tests/functional/test_aarch64_rme_virt: fix sporadic failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test was randomly failing on our CI, and on dev machines, especially with QEMU debug builds. >From the information collected, it's related to an implementation choice in edk2 QEMU virt support. The workaround is to disable KASLR, to avoid accessing protected memory. Note: this is *not* needed for the similar test_aarch64_rme_sbsaref. More information is available on the associated GitLab issue. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2823 Signed-off-by: Pierrick Bouvier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-ID: <20250328183816.2687925-1-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin Signed-off-by: Richard Henderson Message-ID: <20250403203241.46692-2-philmd@linaro.org> --- tests/functional/test_aarch64_rme_virt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py index f4ad4d33d5..a1abf584f0 100755 --- a/tests/functional/test_aarch64_rme_virt.py +++ b/tests/functional/test_aarch64_rme_virt.py @@ -87,7 +87,9 @@ class Aarch64RMEVirtMachine(QemuSystemTest): self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') self.vm.add_args('-netdev', 'user,id=net0') - self.vm.add_args('-append', 'root=/dev/vda') + # We need to add nokaslr to avoid triggering this sporadic bug: + # https://gitlab.com/qemu-project/qemu/-/issues/2823 + self.vm.add_args('-append', 'root=/dev/vda nokaslr') self.vm.launch() # Wait for host VM boot to complete. From 00f119f4c4f776e5e36e8ea99bb3ff865cc52c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Apr 2025 22:32:38 +0200 Subject: [PATCH 2838/2892] tests/functional: Add a decorator for skipping tests on particular OS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since tests might be failing on some operating systems, introduce the skipIfOperatingSystem() decorator. Acked-by: Michael S. Tsirkin Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20250403203241.46692-3-philmd@linaro.org> --- tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/decorators.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index 45f7befa37..af41c2c6a2 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -15,6 +15,6 @@ from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ - skipIfMissingImports + skipIfMissingImports, skipIfOperatingSystem from .archive import archive_extract from .uncompress import uncompress diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index 1651eb739a..50d29de533 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -5,7 +5,7 @@ import importlib import os import platform -from unittest import skipUnless +from unittest import skipIf, skipUnless from .cmd import which @@ -26,6 +26,19 @@ def skipIfMissingCommands(*args): return skipUnless(has_cmds, 'required command(s) "%s" not installed' % ", ".join(args)) +''' +Decorator to skip execution of a test if the current +host operating system does match one of the prohibited +ones. +Example + + @skipIfOperatingSystem("Linux", "Darwin") +''' +def skipIfOperatingSystem(*args): + return skipIf(platform.system() in args, + 'running on an OS (%s) that is not able to run this test' % + ", ".join(args)) + ''' Decorator to skip execution of a test if the current host machine does not match one of the permitted From bd20bc46fe88e6ade710b688e97515d4ed9d1d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Apr 2025 22:32:39 +0200 Subject: [PATCH 2839/2892] tests/functional: Skip aarch64_replay test on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of v10.0.0-rc2 this test is still failing on macos: $ make check-functional-aarch64 V=1 ... ERROR:../../replay/replay-internal.c:235:replay_mutex_unlock: assertion failed: (replay_mutex_locked()) Bail out! ERROR:../../replay/replay-internal.c:235:replay_mutex_unlock: assertion failed: (replay_mutex_locked()) This is tracked as https://gitlab.com/qemu-project/qemu/-/issues/2907 Signed-off-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin Acked-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250403203241.46692-4-philmd@linaro.org> --- tests/functional/test_aarch64_replay.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/test_aarch64_replay.py index 04cde433bc..029fef3cbf 100755 --- a/tests/functional/test_aarch64_replay.py +++ b/tests/functional/test_aarch64_replay.py @@ -5,7 +5,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset +from qemu_test import Asset, skipIfOperatingSystem from replay_kernel import ReplayKernelBase @@ -16,6 +16,8 @@ class Aarch64Replay(ReplayKernelBase): 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + # Failing on Darwin: https://gitlab.com/qemu-project/qemu/-/issues/2907 + @skipIfOperatingSystem('Darwin') def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' From 51514a34b3dbc2cccf0c70aee07001ebbb804d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Apr 2025 22:32:40 +0200 Subject: [PATCH 2840/2892] tests/qtest: Skip Aarch64 VMapple machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First, the VMapple machine only works with the ARM 'host' CPU type, which isn't accepted for QTest: $ qemu-system-aarch64 -M vmapple -accel qtest qemu-system-aarch64: The 'host' CPU type can only be used with KVM or HVF Second, the QTest framework expects machines to be createable without specifying optional arguments, however the VMapple machine requires few of them: $ qemu-system-aarch64 -M vmapple -accel qtest qemu-system-aarch64: No firmware specified $ qemu-system-aarch64 -M vmapple -accel qtest -bios /dev/null qemu-system-aarch64: No AUX device. Please specify one as pflash drive. Restrict this machine with QTest so we can at least run check-qtest, otherwise we get: $ make check-qtest-aarch64 qemu-system-aarch64: The 'host' CPU type can only be used with KVM or HVF Broken pipe ../tests/qtest/libqtest.c:199: kill_qemu() tried to terminate QEMU process but encountered exit status 1 (expected 0) ... 7/26 qemu:qtest+qtest-aarch64 / qtest-aarch64/test-hmp ERROR 24.71s killed by signal 6 SIGABRT 2/26 qemu:qtest+qtest-aarch64 / qtest-aarch64/qom-test ERROR 71.23s killed by signal 6 SIGABRT Suggested-by: Fabiano Rosas Signed-off-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson Message-ID: <20250403203241.46692-5-philmd@linaro.org> --- tests/qtest/libqtest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 2750067861..fad307d125 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1788,6 +1788,7 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), if (!strncmp("xenfv", machines[i].name, 5) || g_str_equal("xenpv", machines[i].name) || g_str_equal("xenpvh", machines[i].name) || + g_str_equal("vmapple", machines[i].name) || g_str_equal("nitro-enclave", machines[i].name)) { continue; } From 49551752e860f5e403cdacac11ee1d218141fd3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Apr 2025 22:32:41 +0200 Subject: [PATCH 2841/2892] hw/arm: Do not build VMapple machine by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately as of v10.0.0-rc2 the VMapple machine is unusable: $ qemu-system-aarch64 -M vmapple [...] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PGIOSurfaceHostDeviceDescriptor setMapMemory:]: unrecognized selector sent to instance 0x600001ede820' *** First throw call stack: ( 0 CoreFoundation 0x000000019c759df0 __exceptionPreprocess + 176 1 libobjc.A.dylib 0x000000019c21eb60 objc_exception_throw + 88 2 CoreFoundation 0x000000019c816ce0 -[NSObject(NSObject) __retain_OA] + 0 3 CoreFoundation 0x000000019c6c7efc ___forwarding___ + 1500 4 CoreFoundation 0x000000019c6c7860 _CF_forwarding_prep_0 + 96 5 qemu-system-aarch64 0x000000010486dbd0 apple_gfx_mmio_realize + 200 6 qemu-system-aarch64 0x0000000104e6ab5c device_set_realized + 352 7 qemu-system-aarch64 0x0000000104e7250c property_set_bool + 100 8 qemu-system-aarch64 0x0000000104e7023c object_property_set + 136 9 qemu-system-aarch64 0x0000000104e74870 object_property_set_qobject + 60 10 qemu-system-aarch64 0x0000000104e70748 object_property_set_bool + 60 11 qemu-system-aarch64 0x0000000104e69bd8 qdev_realize_and_unref + 20 12 qemu-system-aarch64 0x0000000104e258e0 mach_vmapple_init + 1728 13 qemu-system-aarch64 0x000000010481b0ac machine_run_board_init + 1892 14 qemu-system-aarch64 0x0000000104a4def8 qmp_x_exit_preconfig + 260 15 qemu-system-aarch64 0x0000000104a51ba8 qemu_init + 14460 16 qemu-system-aarch64 0x0000000104f7cef8 main + 36 17 dyld 0x000000019c25eb4c start + 6000 ) libc++abi: terminating due to uncaught exception of type NSException Abort trap: 6 Disable the machine so it isn't built by default. This is tracked as https://gitlab.com/qemu-project/qemu/-/issues/2913 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250403203241.46692-6-philmd@linaro.org> --- configs/devices/aarch64-softmmu/default.mak | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak index 93f4022ad6..ad8028cfd4 100644 --- a/configs/devices/aarch64-softmmu/default.mak +++ b/configs/devices/aarch64-softmmu/default.mak @@ -9,3 +9,4 @@ include ../arm-softmmu/default.mak # CONFIG_XLNX_VERSAL=n # CONFIG_SBSA_REF=n # CONFIG_NPCM8XX=n +CONFIG_VMAPPLE=n From d31d37fded414959713678d2e9e6bc1afab5f376 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 4 Apr 2025 09:28:58 -0300 Subject: [PATCH 2842/2892] docs: deprecate RISC-V default machine option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5b4beba124 ("RISC-V Spike Machines") added the Spike machine and made it default for qemu-system-riscv32/64. It was the first RISC-V machine added in QEMU so setting it as default was sensible. Today we have 7 riscv64 and 6 riscv32 machines and having 'spike' as default machine is not intuitive. For example, [1] is a bug that was opened with the 'virt' board in mind, but given that the user didn't pass a '-machine' option, the user was using 'spike' without knowing. Being explicit in the command line is desirable when we have a handful of boards available, so deprecate the default machine setting from RISC-V. [1] https://gitlab.com/qemu-project/qemu/-/issues/2467 Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20250404122858.241598-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- docs/about/deprecated.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 76291fdfd6..0f41a99c67 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -304,6 +304,23 @@ online to check that this board did not completely bitrot yet). It is recommended to use another MIPS machine for future MIPS code development instead. +RISC-V default machine option (since 10.0) +'''''''''''''''''''''''''''''''''''''''''' + +RISC-V defines ``spike`` as the default machine if no machine option is +given in the command line. This happens because ``spike`` is the first +RISC-V machine implemented in QEMU and setting it as default was +convenient at that time. Now we have 7 riscv64 and 6 riscv32 machines +and having ``spike`` as a default is no longer justified. This default +will also promote situations where users think they're running ``virt`` +(the most used RISC-V machine type in 10.0) when in fact they're +running ``spike``. + +Removing the default machine option forces users to always set the machine +they want to use and avoids confusion. Existing users of the ``spike`` +machine must ensure that they're setting the ``spike`` machine in the +command line (``-M spike``). + Backend options --------------- From 988ad4ccebb625a47a73977bfac291341a951c6e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 24 Mar 2025 11:01:44 +0800 Subject: [PATCH 2843/2892] hw/loongarch/virt: Fix cpuslot::cpu set at last in virt_cpu_plug() In function virt_cpu_plug(), Object cpuslot::cpu is set at last only when there is no any error, otherwise it is problematic that cpuslot::cpu is set in advance however it returns because of error. Fixes: ab9935d2991e (hw/loongarch/virt: Implement cpu plug interface) Signed-off-by: Bibo Mao Reviewed-by: Markus Armbruster Message-Id: <20250324030145.3037408-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index e25864214f..504f8755a0 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -973,8 +973,6 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); Error *err = NULL; - cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); - cpu_slot->cpu = CPU(dev); if (lvms->ipi) { hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); if (err) { @@ -998,6 +996,8 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, } } + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = CPU(dev); return; } From fa0dde12db045ddc84f69b1aa0a4c98d176c903d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 24 Mar 2025 11:01:45 +0800 Subject: [PATCH 2844/2892] hw/loongarch/virt: Replace destination error with error_abort In function virt_cpu_plug() and virt_cpu_unplug(), the error is impossile. Destination error is not propagated and replaced with error_abort. With this, the logic is simple. Signed-off-by: Bibo Mao Acked-by: Markus Armbruster Message-Id: <20250324030145.3037408-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 504f8755a0..65c9027feb 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -936,29 +936,15 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *cpu_slot; - Error *err = NULL; LoongArchCPU *cpu = LOONGARCH_CPU(dev); LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ - hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); - if (err) { - error_propagate(errp, err); - return; - } - - hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &err); - if (err) { - error_propagate(errp, err); - return; - } + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); /* Notify acpi ged CPU removed */ - hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); - if (err) { - error_propagate(errp, err); - return; - } + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); cpu_slot->cpu = NULL; @@ -971,29 +957,18 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, CPUArchId *cpu_slot; LoongArchCPU *cpu = LOONGARCH_CPU(dev); LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - Error *err = NULL; if (lvms->ipi) { - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); - if (err) { - error_propagate(errp, err); - return; - } + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); } if (lvms->extioi) { - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &err); - if (err) { - error_propagate(errp, err); - return; - } + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); } if (lvms->acpi_ged) { - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); - if (err) { - error_propagate(errp, err); - } + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, + &error_abort); } cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); From 23d017ca84c82ebae6988b73f0675a0a4f539bca Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:03 +0200 Subject: [PATCH 2845/2892] docs/devel/qapi-code-gen: Tidy up whitespace Consistently use two spaces to separate sentences. Put "::" on a line of its own when it's preceded by whitespace. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-2-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/devel/qapi-code-gen.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index f9cfe8721f..ad517349fc 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -763,8 +763,8 @@ Names beginning with ``x-`` used to signify "experimental". This convention has been replaced by special feature "unstable". Pragmas ``command-name-exceptions`` and ``member-name-exceptions`` let -you violate naming rules. Use for new code is strongly discouraged. See -`Pragma directives`_ for details. +you violate naming rules. Use for new code is strongly discouraged. +See `Pragma directives`_ for details. Downstream extensions @@ -1013,7 +1013,7 @@ like this:: document the success and the error response, respectively. "Errors" sections should be formatted as an rST list, each entry -detailing a relevant error condition. For example:: +detailing a relevant error condition. For example:: # Errors: # - If @device does not exist, DeviceNotFound @@ -1026,13 +1026,13 @@ definition. QMP). In other sections, the text is formatted, and rST markup can be used. -QMP Examples can be added by using the ``.. qmp-example::`` -directive. In its simplest form, this can be used to contain a single -QMP code block which accepts standard JSON syntax with additional server +QMP Examples can be added by using the ``.. qmp-example::`` directive. +In its simplest form, this can be used to contain a single QMP code +block which accepts standard JSON syntax with additional server directionality indicators (``->`` and ``<-``), and elisions (``...``). Optionally, a plaintext title may be provided by using the ``:title:`` -directive option. If the title is omitted, the example title will +directive option. If the title is omitted, the example title will default to "Example:". A simple QMP example:: @@ -1043,10 +1043,10 @@ A simple QMP example:: # -> { "execute": "query-block" } # <- { ... } -More complex or multi-step examples where exposition is needed before or -between QMP code blocks can be created by using the ``:annotated:`` -directive option. When using this option, nested QMP code blocks must be -entered explicitly with rST's ``::`` syntax. +More complex or multi-step examples where exposition is needed before +or between QMP code blocks can be created by using the ``:annotated:`` +directive option. When using this option, nested QMP code blocks must +be entered explicitly with rST's ``::`` syntax. Highlighting in non-QMP languages can be accomplished by using the ``.. code-block:: lang`` directive, and non-highlighted text can be @@ -1466,7 +1466,9 @@ As an example, we'll use the following schema, which describes a single complex user-defined type, along with command which takes a list of that type as a parameter, and returns a single element of that type. The user is responsible for writing the implementation of -qmp_my_command(); everything else is produced by the generator. :: +qmp_my_command(); everything else is produced by the generator. + +:: $ cat example-schema.json { 'struct': 'UserDefOne', From 2f0bcc65a8e7d2e6ff28884e596355d70b03acc6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:04 +0200 Subject: [PATCH 2846/2892] qapi/rocker: Tidy up query-rocker-of-dpa-flows example The command can return any number of RockerOfDpaFlow objects. The example shows it returning exactly two, with the second object's members elided. Tweak it so it elides elements after the first instead. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-3-armbru@redhat.com> Reviewed-by: Eric Blake [Commit message typo fixed] --- qapi/rocker.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/rocker.json b/qapi/rocker.json index 51aa5b4930..0c7ef1f77c 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -254,7 +254,7 @@ # "action": {"goto-tbl": 10}, # "mask": {"in-pport": 4294901760} # }, -# {...}, +# ... # ]} ## { 'command': 'query-rocker-of-dpa-flows', From ae75c37e50f37b224bef4c6191da6a577afedf1e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:05 +0200 Subject: [PATCH 2847/2892] docs/interop: Delete "QEMU Guest Agent Protocol Reference" TOC The "QEMU Guest Agent Protocol Reference" starts with the following table of contents: Contents * QEMU Guest Agent Protocol Reference * QEMU guest agent protocol commands and structs This is useless. Delete the entire TOC. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-4-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/interop/qemu-ga-ref.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/interop/qemu-ga-ref.rst b/docs/interop/qemu-ga-ref.rst index 19b5c7a549..25f6e24b03 100644 --- a/docs/interop/qemu-ga-ref.rst +++ b/docs/interop/qemu-ga-ref.rst @@ -1,9 +1,6 @@ QEMU Guest Agent Protocol Reference =================================== -.. contents:: - :depth: 3 - .. qapi-doc:: qga/qapi-schema.json :transmogrify: :namespace: QGA From 0d4c7ea0f8b8655f55cb33f6a75243a2dcb64447 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:06 +0200 Subject: [PATCH 2848/2892] docs/interop: Sanitize QMP reference manuals TOC The "QEMU QMP Reference Manual" and the "QEMU Storage Daemon QMP Reference Manual" start with a table of contents that looks like this: Contents * Title of the manual * Title of first first-level section * Title of its first second-level section * Title of its second second-level section ... * Title of second first-level section ... The first level is useless. Drop it. While there, delete the option that limits the TOC to depth 3. Its actual depth was 3 before the patch, and is now 2. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-5-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/interop/qemu-qmp-ref.rst | 2 +- docs/interop/qemu-storage-daemon-qmp-ref.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst index ef8792b53f..3bc1ca12b1 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -4,7 +4,7 @@ QEMU QMP Reference Manual ========================= .. contents:: - :depth: 3 + :local: .. qapi-doc:: qapi/qapi-schema.json :transmogrify: diff --git a/docs/interop/qemu-storage-daemon-qmp-ref.rst b/docs/interop/qemu-storage-daemon-qmp-ref.rst index d0228d63b8..dc7bde262a 100644 --- a/docs/interop/qemu-storage-daemon-qmp-ref.rst +++ b/docs/interop/qemu-storage-daemon-qmp-ref.rst @@ -2,7 +2,7 @@ QEMU Storage Daemon QMP Reference Manual ======================================== .. contents:: - :depth: 3 + :local: .. qapi-doc:: storage-daemon/qapi/qapi-schema.json :transmogrify: From e27608d05370a5c11f641bd96095afb2d06c5880 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:07 +0200 Subject: [PATCH 2849/2892] docs/devel/qapi-code-gen: Improve the part on qmp-example directive Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-6-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/devel/qapi-code-gen.rst | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index ad517349fc..25a46fafb6 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1038,20 +1038,15 @@ default to "Example:". A simple QMP example:: # .. qmp-example:: - # :title: Using query-block # - # -> { "execute": "query-block" } - # <- { ... } + # -> { "execute": "query-name" } + # <- { "return": { "name": "Fred" } } More complex or multi-step examples where exposition is needed before or between QMP code blocks can be created by using the ``:annotated:`` directive option. When using this option, nested QMP code blocks must be entered explicitly with rST's ``::`` syntax. -Highlighting in non-QMP languages can be accomplished by using the -``.. code-block:: lang`` directive, and non-highlighted text can be -achieved by omitting the language argument. - For example:: # .. qmp-example:: @@ -1061,11 +1056,21 @@ For example:: # This is a more complex example that can use # ``arbitrary rST syntax`` in its exposition:: # - # -> { "execute": "query-block" } - # <- { ... } + # -> { "execute": "query-block" } + # <- { "return": [ + # { + # "device": "ide0-hd0", + # ... + # } + # ... + # ] } # # Above, lengthy output has been omitted for brevity. +Highlighting in non-QMP languages can be accomplished by using the +``.. code-block:: lang`` directive, and non-highlighted text can be +achieved by omitting the language argument. + Examples of complete definition documentation:: From bc361f2f9bc13455311d7ab296c60fe9dc93cff7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:08 +0200 Subject: [PATCH 2850/2892] docs/sphinx/qmp_lexer: Generalize elision syntax Accept "... lorem ipsum ..." in addition to "...". Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-7-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/devel/qapi-code-gen.rst | 6 ++++-- docs/sphinx/qmp_lexer.py | 2 +- tests/qapi-schema/doc-good.json | 2 +- tests/qapi-schema/doc-good.out | 2 +- tests/qapi-schema/doc-good.txt | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 25a46fafb6..231cc0fecf 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1029,7 +1029,9 @@ used. QMP Examples can be added by using the ``.. qmp-example::`` directive. In its simplest form, this can be used to contain a single QMP code block which accepts standard JSON syntax with additional server -directionality indicators (``->`` and ``<-``), and elisions (``...``). +directionality indicators (``->`` and ``<-``), and elisions. An +elision is commonly ``...``, but it can also be or a pair of ``...`` +with text in between. Optionally, a plaintext title may be provided by using the ``:title:`` directive option. If the title is omitted, the example title will @@ -1062,7 +1064,7 @@ For example:: # "device": "ide0-hd0", # ... # } - # ... + # ... more ... # ] } # # Above, lengthy output has been omitted for brevity. diff --git a/docs/sphinx/qmp_lexer.py b/docs/sphinx/qmp_lexer.py index a59de8a079..1bd1b81b70 100644 --- a/docs/sphinx/qmp_lexer.py +++ b/docs/sphinx/qmp_lexer.py @@ -24,7 +24,7 @@ class QMPExampleMarkersLexer(RegexLexer): 'root': [ (r'-> ', token.Generic.Prompt), (r'<- ', token.Generic.Prompt), - (r' ?\.{3} ?', token.Generic.Prompt), + (r'\.{3}( .* \.{3})?', token.Generic.Prompt), ] } diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 0a4f139f83..14b808f909 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -212,7 +212,7 @@ # # -> "this example" # -# <- "has no title" +# <- ... has no title ... ## { 'command': 'cmd-boxed', 'boxed': true, 'data': 'Object', diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 5773f1dd6d..dc8352eed4 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -217,7 +217,7 @@ another feature -> "this example" - <- "has no title" + <- ... has no title ... doc symbol=EVT_BOXED body= diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index cb37db606a..17a1d56ef1 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -264,7 +264,7 @@ Example:: -> "this example" - <- "has no title" + <- ... has no title ... "EVT_BOXED" (Event) From 6d7b3efc3fa83aec4cb09000893064907def811d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:09 +0200 Subject: [PATCH 2851/2892] docs/sphinx/qmp_lexer: Highlight elisions like comments, not prompts Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-8-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/sphinx/qmp_lexer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/qmp_lexer.py b/docs/sphinx/qmp_lexer.py index 1bd1b81b70..7b3b808d12 100644 --- a/docs/sphinx/qmp_lexer.py +++ b/docs/sphinx/qmp_lexer.py @@ -24,7 +24,7 @@ class QMPExampleMarkersLexer(RegexLexer): 'root': [ (r'-> ', token.Generic.Prompt), (r'<- ', token.Generic.Prompt), - (r'\.{3}( .* \.{3})?', token.Generic.Prompt), + (r'\.{3}( .* \.{3})?', token.Comment.Multiline), ] } From d0ae5a3058603d4b2c25984a3acb2ac83e025424 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:10 +0200 Subject: [PATCH 2852/2892] qapi/qapi-schema: Update introduction for example notation The introduction explains example notation. The series merged in merge commit e6485190f77e (in 9.1) improved how they look in generated docs, but neglected to update the introduction accordingly. Do that now. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-9-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/qapi-schema.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index c41c01eb2a..0d027d5017 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -26,10 +26,10 @@ # # Example: # -# :: +# .. qmp-example:: # -# -> data issued by the Client -# <- Server data response +# -> ... text sent by client (commands) ... +# <- ... text sent by server (command responses and events) ... # # Please refer to the # :doc:`QEMU Machine Protocol Specification ` From 9199d324a85cd3d4697b5907e804b903b5ce46e3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:11 +0200 Subject: [PATCH 2853/2892] qapi/qapi-schema: Address the introduction's bit rot Cut the crap that stopped making sense years ago. Adjust the remainder. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-10-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/qapi-schema.json | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 0d027d5017..7bc600bb76 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -3,37 +3,24 @@ ## # = Introduction # -# This document describes all commands currently supported by QMP. +# This manual describes the commands and events supported by the QEMU +# Monitor Protocol (QMP). # # 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 -# commands (the manpage, QEMU's manual, etc) can and should be -# consulted. -# -# QMP has two types of commands: regular and query commands. Regular -# commands usually change the Virtual Machine's state someway, while -# query commands just return information. The sections below are -# divided accordingly. -# -# It's important to observe that all communication examples are -# formatted in a reader-friendly way, so that they're easier to -# understand. However, in real protocol usage, they're emitted as a -# single line. -# -# Also, the following notation is used to denote data flow: -# -# Example: +# The following notation is used in examples: # # .. qmp-example:: # # -> ... text sent by client (commands) ... # <- ... text sent by server (command responses and events) ... # +# Example text is formatted for readability. However, in real +# protocol usage, its commonly emitted as a single line. +# # Please refer to the # :doc:`QEMU Machine Protocol Specification ` -# for detailed information on the Server command and response formats. +# for the general format of commands, responses, and events. ## { 'include': 'pragma.json' } From 5e03548bf222b0f8038dc61dbf0c93bd062025aa Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:12 +0200 Subject: [PATCH 2854/2892] storage-daemon/qapi/qapi-schema: Add a proper introduction Contents adapted from qapi/qapi-schema.json. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-11-armbru@redhat.com> Reviewed-by: Eric Blake --- storage-daemon/qapi/qapi-schema.json | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json index 2a562ee32e..0427594c98 100644 --- a/storage-daemon/qapi/qapi-schema.json +++ b/storage-daemon/qapi/qapi-schema.json @@ -14,10 +14,26 @@ # storage daemon. ## -# = QEMU storage daemon protocol commands and structs +# = Introduction # -# For a concise listing of all commands, events, and types in the QEMU -# storage daemon, please consult the `qapi-qsd-index`. +# This manual describes the commands and events supported by the QEMU +# storage daemon QMP. +# +# For locating a particular item, please see the `qapi-qsd-index`. +# +# The following notation is used in examples: +# +# .. qmp-example:: +# +# -> ... text sent by client (commands) ... +# <- ... text sent by server (command responses and events) ... +# +# Example text is formatted for readability. However, in real +# protocol usage, its commonly emitted as a single line. +# +# Please refer to the +# :doc:`QEMU Machine Protocol Specification ` +# for the general format of commands, responses, and events. ## From 8d41a7dfc2a8f21228b7f29314dd68ad0aa96d10 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 Apr 2025 14:14:13 +0200 Subject: [PATCH 2855/2892] qga/qapi-schema: Add a proper introduction Contents adapted from qapi/qapi-schema.json. Signed-off-by: Markus Armbruster Message-ID: <20250404121413.1743790-12-armbru@redhat.com> Reviewed-by: Eric Blake --- qga/qapi-schema.json | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 35ec0e7db3..5316bfacbf 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -2,10 +2,24 @@ # vim: filetype=python ## -# = QEMU guest agent protocol commands and structs +# This manual describes the commands supported by the QEMU Guest +# Agent Protocol. # -# For a concise listing of all commands, events, and types in the QEMU -# guest agent, please consult the `qapi-qga-index`. +# For locating a particular item, please see the `qapi-qga-index`. +# +# The following notation is used in examples: +# +# .. qmp-example:: +# +# -> ... text sent by client (commands) ... +# <- ... text sent by server (command responses and events) ... +# +# Example text is formatted for readability. However, in real +# protocol usage, its commonly emitted as a single line. +# +# Please refer to the +# :doc:`QEMU Machine Protocol Specification ` +# for the general format of commands, responses, and events. ## { 'pragma': { 'doc-required': true } } From 6b36a578316e3b14a53ae7699571d01b00fc2f8a Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Tue, 18 Mar 2025 13:19:00 +0300 Subject: [PATCH 2856/2892] qemu-img: fix division by zero in bench_cb() for zero-sized images This error was discovered by fuzzing qemu-img. This commit fixes a division by zero error in the bench_cb() function that occurs when using the bench command with a zero-sized image. The issue arises because b->image_size can be zero, leading to a division by zero in the modulo operation (b->offset %= b->image_size). This patch adds a check for b->image_size == 0 and resets b->offset to 0 in such cases, preventing the error. Signed-off-by: Denis Rastyogin Message-ID: <20250318101933.255617-1-gerben@altlinux.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index 89c93c1eb5..2044c22a4c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4488,7 +4488,11 @@ static void bench_cb(void *opaque, int ret) */ b->in_flight++; b->offset += b->step; - b->offset %= b->image_size; + if (b->image_size == 0) { + b->offset = 0; + } else { + b->offset %= b->image_size; + } if (b->write) { acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b); } else { From 8e4ffb4ef434af3667a90276d75d301b582bf78c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 18 Mar 2025 21:11:43 +0100 Subject: [PATCH 2857/2892] qcow2: Don't crash qemu-img info with missing crypto header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qcow2_refresh_limits() assumes that s->crypto is non-NULL whenever bs->encrypted is true. This is actually not the case: qcow2_do_open() allows to open an image with a missing crypto header for BDRV_O_NO_IO, and then bs->encrypted is true, but s->crypto is still NULL. It doesn't make sense to open an invalid image, so remove the exception for BDRV_O_NO_IO. This catches the problem early and any code that makes the same assumption is safe now. At the same time, in the name of defensive programming, we shouldn't make the assumption in the first place. Let qcow2_refresh_limits() check s->crypto rather than bs->encrypted. If s->crypto is NULL, it also can't make any requirement on request alignment. Finally, start a qcow2-encryption test case that only serves as a regression test for this crash for now. Reported-by: Leonid Reviakin Reported-by: Denis Rastyogin Signed-off-by: Kevin Wolf Message-ID: <20250318201143.70657-1-kwolf@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Kevin Wolf --- block/qcow2.c | 4 +- tests/qemu-iotests/tests/qcow2-encryption | 75 +++++++++++++++++++ tests/qemu-iotests/tests/qcow2-encryption.out | 32 ++++++++ 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100755 tests/qemu-iotests/tests/qcow2-encryption create mode 100644 tests/qemu-iotests/tests/qcow2-encryption.out diff --git a/block/qcow2.c b/block/qcow2.c index dd6bcafbd8..7774e7f090 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1721,7 +1721,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - } else if (!(flags & BDRV_O_NO_IO)) { + } else { error_setg(errp, "Missing CRYPTO header for crypt method %d", s->crypt_method_header); ret = -EINVAL; @@ -1976,7 +1976,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; - if (bs->encrypted) { + if (s->crypto) { /* Encryption works on a sector granularity */ bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); } diff --git a/tests/qemu-iotests/tests/qcow2-encryption b/tests/qemu-iotests/tests/qcow2-encryption new file mode 100755 index 0000000000..95f6195ab8 --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-encryption @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test case for encryption support in qcow2 +# +# Copyright (C) 2025 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter + +# This tests qcow2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_require_working_luks + +IMG_SIZE=64M + +echo +echo "=== Create an encrypted image ===" +echo + +_make_test_img --object secret,id=sec0,data=123456 -o encrypt.format=luks,encrypt.key-secret=sec0 $IMG_SIZE +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts +_img_info +$QEMU_IMG check \ + --object secret,id=sec0,data=123456 \ + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 \ + | _filter_qemu_img_check + +echo +echo "=== Remove the header extension ===" +echo + +$PYTHON ../qcow2.py "$TEST_IMG" del-header-ext 0x0537be77 +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts +_img_info +$QEMU_IMG check \ + --object secret,id=sec0,data=123456 \ + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 2>&1 \ + | _filter_qemu_img_check \ + | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/qcow2-encryption.out b/tests/qemu-iotests/tests/qcow2-encryption.out new file mode 100644 index 0000000000..9b549dc2ab --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-encryption.out @@ -0,0 +1,32 @@ +QA output created by qcow2-encryption + +=== Create an encrypted image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Header extension: +magic 0x537be77 (Crypto header) +length 16 +data + +Header extension: +magic 0x6803f857 (Feature table) +length 384 +data + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +encrypted: yes +cluster_size: 65536 +No errors were found on the image. + +=== Remove the header extension === + +Header extension: +magic 0x6803f857 (Feature table) +length 384 +data + +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Missing CRYPTO header for crypt method 2 +qemu-img: Could not open 'file.filename=TEST_DIR/t.qcow2,encrypt.key-secret=sec0': Missing CRYPTO header for crypt method 2 +*** done From 9808ce6d5cb75a4f9db76a3d9b508560efdf5ac2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 6 Sep 2024 08:13:51 +1000 Subject: [PATCH 2858/2892] target/ppc: Big-core scratch register fix The per-core SCRATCH0-7 registers are shared between big cores, which was missed in the big-core implementation. It is difficult to model well with the big-core == 2xPnvCore scheme we moved to, this fix uses the even PnvCore to store the scrach data. Also remove a stray log message that came in with the same patch that introduced patch. Fixes: c26504afd5f5c ("ppc/pnv: Add a big-core mode that joins two regular cores") Cc: qemu-stable@nongnu.org Signed-off-by: Nicholas Piggin --- target/ppc/misc_helper.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 2d9512c116..46ae454afd 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -332,6 +332,10 @@ target_ulong helper_load_sprd(CPUPPCState *env) PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; target_ulong sprc = env->spr[SPR_POWER_SPRC]; + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + switch (sprc & 0x3e0) { case 0: /* SCRATCH0-3 */ case 1: /* SCRATCH4-7 */ @@ -368,6 +372,10 @@ void helper_store_sprd(CPUPPCState *env, target_ulong val) PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; int nr; + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + switch (sprc & 0x3e0) { case 0: /* SCRATCH0-3 */ case 1: /* SCRATCH4-7 */ @@ -378,7 +386,6 @@ void helper_store_sprd(CPUPPCState *env, target_ulong val) * information. Could also dump these upon checkstop. */ nr = (sprc >> 3) & 0x7; - qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); pc->scratch[nr] = val; break; default: From b3d47c8303b8be2c3693c5704012b3334741b7ed Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 25 Oct 2024 01:18:12 +1000 Subject: [PATCH 2859/2892] target/ppc: Fix SPRC/SPRD SPRs for P9/10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 60d30cff847 ("target/ppc: Move SPR indirect registers into PnvCore") was mismerged and moved the SPRs to power8-only, instead of power9/10-only. Fixes: 60d30cff847 ("target/ppc: Move SPR indirect registers into PnvCore") Reviewed-by: Philippe Mathieu-Daudé Cc: qemu-stable@nongnu.org Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 7decc09aec..f81cb680fc 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5801,6 +5801,18 @@ static void register_power9_book4_sprs(CPUPPCState *env) &spr_read_generic, &spr_write_generic, &spr_read_generic, &spr_write_generic, 0x00000000); + + /* SPRC/SPRD exist in earlier CPUs but only tested on POWER9/10 */ + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sprc, + 0x00000000); + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sprd, &spr_write_sprd, + 0x00000000); #endif } @@ -5822,17 +5834,6 @@ static void register_power8_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_WORT, 0); - /* SPRC/SPRD exist in earlier CPUs but only tested on POWER9/10 */ - spr_register_hv(env, SPR_POWER_SPRC, "SPRC", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_sprc, - 0x00000000); - spr_register_hv(env, SPR_POWER_SPRD, "SPRD", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_sprd, &spr_write_sprd, - 0x00000000); #endif } From 61b6d9b749ba233784c7214cfe9585ea321159dc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 7 Apr 2025 17:59:49 +0200 Subject: [PATCH 2860/2892] scsi-disk: Apply error policy for host_status errors again Originally, all failed SG_IO requests called scsi_handle_rw_error() to apply the configured error policy. However, commit f3126d65, which was supposed to be a mere refactoring for scsi-disk.c, broke this and accidentally completed the SCSI request without considering the error policy any more if the error was signalled in the host_status field. Apart from the commit message not describing the change as intended, errors indicated in host_status are also obviously backend errors and not something the guest must deal with independently of the error policy. This behaviour means that some recoverable errors (such as a path error in multipath configurations) were reported to the guest anyway, which might not expect it and might consider its disk broken. Make sure that we apply the error policy again for host_status errors, too. This addresses an existing FIXME comment and allows us to remove some comments warning that callbacks weren't always called. With this fix, they are called in all cases again. The return value passed to the request callback doesn't have more free values that could be used to indicate host_status errors as well as SAM status codes and negative errno. Store the value in the host_status field of the SCSIRequest instead and use -ENODEV as the return value (if a path hasn't been reachable for a while, blk_aio_ioctl() will return -ENODEV instead of just setting host_status, so just reuse it here - it's not necessarily entirely accurate, but it's as good as any errno). Cc: qemu-stable@nongnu.org Fixes: f3126d65b393 ('scsi: move host_status handling into SCSI drivers') Signed-off-by: Kevin Wolf Message-ID: <20250407155949.44736-1-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek Signed-off-by: Kevin Wolf --- hw/scsi/scsi-disk.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 8da1d5a77c..e59632e9b1 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -68,10 +68,9 @@ struct SCSIDiskClass { SCSIDeviceClass parent_class; /* * Callbacks receive ret == 0 for success. Errors are represented either as - * negative errno values, or as positive SAM status codes. - * - * Beware: For errors returned in host_status, the function may directly - * complete the request and never call the callback. + * negative errno values, or as positive SAM status codes. For host_status + * errors, the function passes ret == -ENODEV and sets the host_status field + * of the SCSIRequest. */ DMAIOFunc *dma_readv; DMAIOFunc *dma_writev; @@ -225,11 +224,26 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); SCSISense sense = SENSE_CODE(NO_SENSE); + int16_t host_status; int error; bool req_has_sense = false; BlockErrorAction action; int status; + /* + * host_status should only be set for SG_IO requests that came back with a + * host_status error in scsi_block_sgio_complete(). This error path passes + * -ENODEV as the return value. + * + * Reset host_status in the request because we may still want to complete + * the request successfully with the 'stop' or 'ignore' error policy. + */ + host_status = r->req.host_status; + if (host_status != -1) { + assert(ret == -ENODEV); + r->req.host_status = -1; + } + if (ret < 0) { status = scsi_sense_from_errno(-ret, &sense); error = -ret; @@ -289,6 +303,10 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) if (acct_failed) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } + if (host_status != -1) { + scsi_req_complete_failed(&r->req, host_status); + return true; + } if (req_has_sense) { sdc->update_sense(&r->req); } else if (status == CHECK_CONDITION) { @@ -409,7 +427,6 @@ done: scsi_req_unref(&r->req); } -/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -448,7 +465,6 @@ done: scsi_req_unref(&r->req); } -/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_read_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -585,7 +601,6 @@ done: scsi_req_unref(&r->req); } -/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_write_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -2846,14 +2861,10 @@ static void scsi_block_sgio_complete(void *opaque, int ret) sg_io_hdr_t *io_hdr = &req->io_header; if (ret == 0) { - /* FIXME This skips calling req->cb() and any cleanup in it */ if (io_hdr->host_status != SCSI_HOST_OK) { - scsi_req_complete_failed(&r->req, io_hdr->host_status); - scsi_req_unref(&r->req); - return; - } - - if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { + r->req.host_status = io_hdr->host_status; + ret = -ENODEV; + } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { ret = BUSY; } else { ret = io_hdr->status; From f8222bfba3409a3ce09c191941127a8cf2c7e623 Mon Sep 17 00:00:00 2001 From: Vitalii Mordan Date: Wed, 2 Apr 2025 13:21:19 +0300 Subject: [PATCH 2861/2892] test-bdrv-drain: Fix data races This patch addresses potential data races involving access to Job fields in the test-bdrv-drain test. Fixes: 7253220de4 ("test-bdrv-drain: Test drain vs. block jobs") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2900 Signed-off-by: Vitalii Mordan Message-ID: <20250402102119.3345626-1-mordan@ispras.ru> [kwolf: Fixed up coding style and one missing atomic access] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/qemu/job.h | 3 +++ job.c | 6 ++++++ tests/unit/test-bdrv-drain.c | 32 +++++++++++++++++++------------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/include/qemu/job.h b/include/qemu/job.h index 2b873f2576..a5a04155ea 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -545,6 +545,9 @@ bool job_is_ready(Job *job); /* Same as job_is_ready(), but called with job lock held. */ bool job_is_ready_locked(Job *job); +/** Returns whether the job is paused. Called with job_mutex *not* held. */ +bool job_is_paused(Job *job); + /** * Request @job to pause at the next pause point. Must be paired with * job_resume(). If the job is supposed to be resumed by user action, call diff --git a/job.c b/job.c index 660ce22c56..0653bc2ba6 100644 --- a/job.c +++ b/job.c @@ -251,6 +251,12 @@ bool job_is_cancelled_locked(Job *job) return job->force_cancel; } +bool job_is_paused(Job *job) +{ + JOB_LOCK_GUARD(); + return job->paused; +} + bool job_is_cancelled(Job *job) { JOB_LOCK_GUARD(); diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 7410e6f352..290cd2a70e 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -632,6 +632,8 @@ typedef struct TestBlockJob { BlockDriverState *bs; int run_ret; int prepare_ret; + + /* Accessed with atomics */ bool running; bool should_complete; } TestBlockJob; @@ -667,10 +669,10 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) /* We are running the actual job code past the pause point in * job_co_entry(). */ - s->running = true; + qatomic_set(&s->running, true); job_transition_to_ready(&s->common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { /* Avoid job_sleep_ns() because it marks the job as !busy. We want to * emulate some actual activity (probably some I/O) here so that drain * has to wait for this activity to stop. */ @@ -685,7 +687,7 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) static void test_job_complete(Job *job, Error **errp) { TestBlockJob *s = container_of(job, TestBlockJob, common.job); - s->should_complete = true; + qatomic_set(&s->should_complete, true); } BlockJobDriver test_job_driver = { @@ -791,7 +793,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, /* job_co_entry() is run in the I/O thread, wait for the actual job * code to start (we don't want to catch the job in the pause point in * job_co_entry(). */ - while (!tjob->running) { + while (!qatomic_read(&tjob->running)) { aio_poll(qemu_get_aio_context(), false); } } @@ -799,7 +801,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, WITH_JOB_LOCK_GUARD() { g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_true(tjob->running); + g_assert_true(qatomic_read(&tjob->running)); g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ } @@ -825,7 +827,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, * * paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { + while (job_is_paused(&job->job)) { aio_poll(qemu_get_aio_context(), false); } } @@ -858,7 +860,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, * * paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { + while (job_is_paused(&job->job)) { aio_poll(qemu_get_aio_context(), false); } } @@ -1411,10 +1413,12 @@ static void test_set_aio_context(void) typedef struct TestDropBackingBlockJob { BlockJob common; - bool should_complete; bool *did_complete; BlockDriverState *detach_also; BlockDriverState *bs; + + /* Accessed with atomics */ + bool should_complete; } TestDropBackingBlockJob; static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) @@ -1422,7 +1426,7 @@ static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) TestDropBackingBlockJob *s = container_of(job, TestDropBackingBlockJob, common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { job_sleep_ns(job, 0); } @@ -1541,7 +1545,7 @@ static void test_blockjob_commit_by_drained_end(void) job_start(&job->common.job); - job->should_complete = true; + qatomic_set(&job->should_complete, true); bdrv_drained_begin(bs_child); g_assert(!job_has_completed); bdrv_drained_end(bs_child); @@ -1557,15 +1561,17 @@ static void test_blockjob_commit_by_drained_end(void) typedef struct TestSimpleBlockJob { BlockJob common; - bool should_complete; bool *did_complete; + + /* Accessed with atomics */ + bool should_complete; } TestSimpleBlockJob; static int coroutine_fn test_simple_job_run(Job *job, Error **errp) { TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { job_sleep_ns(job, 0); } @@ -1700,7 +1706,7 @@ static void test_drop_intermediate_poll(void) job->did_complete = &job_has_completed; job_start(&job->common.job); - job->should_complete = true; + qatomic_set(&job->should_complete, true); g_assert(!job_has_completed); ret = bdrv_drop_intermediate(chain[1], chain[0], NULL, false); From a7a05f5f6a4085afbede315e749b1c67e78c966b Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sun, 23 Mar 2025 22:35:54 +0100 Subject: [PATCH 2862/2892] smbios: Fix buffer overrun when using path= option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have to make sure the array of bytes read from the path= file is null-terminated, otherwise we run into a buffer overrun later on. Fixes: bb99f4772f54017490e3356ecbb3df25c5d4537f ("hw/smbios: support loading OEM strings values from a file") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2879 Signed-off-by: Daan De Meyer Reviewed-by: Daniel P. Berrangé Tested-by: Valentin David Message-ID: <20250323213622.2581013-1-daan.j.demeyer@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/smbios/smbios.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 02a09eb9cd..ad4cd6721e 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1285,6 +1285,9 @@ static int save_opt_one(void *opaque, g_byte_array_append(data, (guint8 *)buf, ret); } + buf[0] = '\0'; + g_byte_array_append(data, (guint8 *)buf, 1); + qemu_close(fd); *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); From 15a9fe6e35369d6e488233f689a3dbdd7a525546 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 1 Apr 2025 14:45:08 +1030 Subject: [PATCH 2863/2892] hw/core/machine: Fix -machine dumpdtb=file.dtb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 8fd2518ef2f8 ("hw: Centralize handling of -machine dumpdtb option") the call to dump was moved with respect to the init of the machine. This resulted in the device tree missing parts of the machine description, depending on how they construct their device tree. The arm virt machine is missing some PSCI nodes, while the riscv one is missing most of its content. Move the dump to after the notifiers have been run, allowing virt_machine_done to be called and the device tree to be fully populated. Fixes: 8fd2518ef2f8 ("hw: Centralize handling of -machine dumpdtb option") Signed-off-by: Joel Stanley Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250401041509.719153-1-joel@jms.id.au> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index f52a4f2273..63c6ef93d2 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1731,12 +1731,6 @@ void qdev_machine_creation_done(void) phase_advance(PHASE_MACHINE_READY); qdev_assert_realized_properly(); - /* - * If the user used -machine dumpdtb=file.dtb to request that we - * dump the DTB to a file, do it now, and exit. - */ - handle_machine_dumpdtb(current_machine); - /* TODO: once all bus devices are qdevified, this should be done * when bus is created by qdev.c */ /* @@ -1750,6 +1744,12 @@ void qdev_machine_creation_done(void) notifier_list_notify(&machine_init_done_notifiers, NULL); + /* + * If the user used -machine dumpdtb=file.dtb to request that we + * dump the DTB to a file, do it now, and exit. + */ + handle_machine_dumpdtb(current_machine); + if (rom_check_and_register_reset() != 0) { exit(1); } From 2ba700a501820f7a59c030f6f49a631a3d2c06f5 Mon Sep 17 00:00:00 2001 From: Zhang Chen Date: Tue, 1 Apr 2025 16:31:02 +0800 Subject: [PATCH 2864/2892] docs/arm: Add apple HVF host for supported guest CPU type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In my test, latest QEMU already support Apple HVF for -cpu host and max. From guest VM lscpu: Architecture: aarch64 CPU op-mode(s): 64-bit Byte Order: Little Endian CPU(s): 11 On-line CPU(s) list: 0-10 Vendor ID: Apple Model name: - Model: 0 Thread(s) per core: 1 Core(s) per socket: 11 Socket(s): 1 Stepping: 0x0 BogoMIPS: 48.00 Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asim dfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint Signed-off-by: Zhang Chen Reviewed-by: Alex Bennée Message-ID: <20250401083102.72845-1-zhangckid@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/system/arm/virt.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index adf446c0a2..6a719b9586 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -70,11 +70,11 @@ Supported guest CPU types: - ``cortex-a76`` (64-bit) - ``cortex-a710`` (64-bit) - ``a64fx`` (64-bit) -- ``host`` (with KVM only) +- ``host`` (with KVM and HVF only) - ``neoverse-n1`` (64-bit) - ``neoverse-v1`` (64-bit) - ``neoverse-n2`` (64-bit) -- ``max`` (same as ``host`` for KVM; best possible emulation with TCG) +- ``max`` (same as ``host`` for KVM and HVF; best possible emulation with TCG) Note that the default is ``cortex-a15``, so for an AArch64 guest you must specify a CPU type. From 535ef195663e134d704a3c4f375bf5c488705808 Mon Sep 17 00:00:00 2001 From: Keoseong Park Date: Thu, 3 Apr 2025 18:21:40 +0900 Subject: [PATCH 2865/2892] hw/ufs: Fix incorrect comment for segment_size and allocation_unit_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comments for segment_size and allocation_unit_size incorrectly described them as 4KB. According to the UFS specification, segment_size is expressed in units of 512 bytes. Given segment_size = 0x2000 (8192), the actual size is 4MB. Similarly, allocation_unit_size = 1 means 1 segment = 4MB. This patch updates the comments to reflect the correct size. Signed-off-by: Keoseong Park Reviewed-by: Jeuk Kim Message-ID: <20250403092140epcms2p355a7f039871b3e5b409754ef450b9158@epcms2p3> Signed-off-by: Philippe Mathieu-Daudé --- hw/ufs/ufs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index ee13edacd8..542f13b10e 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1753,8 +1753,8 @@ static void ufs_init_hc(UfsHc *u) u->geometry_desc.length = sizeof(GeometryDescriptor); u->geometry_desc.descriptor_idn = UFS_QUERY_DESC_IDN_GEOMETRY; u->geometry_desc.max_number_lu = (UFS_MAX_LUS == 32) ? 0x1 : 0x0; - u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4KB */ - u->geometry_desc.allocation_unit_size = 0x1; /* 4KB */ + u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4MB: 8192 * 512B */ + u->geometry_desc.allocation_unit_size = 0x1; /* 4MB: 1 segment */ u->geometry_desc.min_addr_block_size = 0x8; /* 4KB */ u->geometry_desc.max_in_buffer_size = 0x8; u->geometry_desc.max_out_buffer_size = 0x8; From 764ca3ec8999fbeb64e2ddb3cd2df0f7004fc59b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 5 Apr 2025 23:48:59 +0200 Subject: [PATCH 2866/2892] hw/arm/imx8mp-evk: Remove unimplemented cpu-idle-states properties from devicetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cpu-idle-states property causes a hard boot hang. Rather than documenting the workaround, perform the removal from the devicetree automatically. Signed-off-by: Guenter Roeck Signed-off-by: Bernhard Beschow [Bernhard: split patch, update documentation, adapt commit message] Signed-off-by: Bernhard Beschow Message-ID: <20250405214900.7114-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/system/arm/imx8mp-evk.rst | 12 ++---------- hw/arm/imx8mp-evk.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst index 00527b0cbe..b2f7d29ade 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8mp-evk.rst @@ -35,7 +35,7 @@ Direct Linux Kernel Boot Probably the easiest way to get started with a whole Linux system on the machine is to generate an image with Buildroot. Version 2024.11.1 is tested at the time -of writing and involves three steps. First run the following commands in the +of writing and involves two steps. First run the following commands in the toplevel directory of the Buildroot source tree: .. code-block:: bash @@ -50,14 +50,6 @@ it and resize the SD card image to a power of two: $ qemu-img resize sdcard.img 256M -Finally, the device tree needs to be patched with the following commands which -will remove the ``cpu-idle-states`` properties from CPU nodes: - -.. code-block:: bash - - $ dtc imx8mp-evk.dtb | sed '/cpu-idle-states/d' > imx8mp-evk-patched.dts - $ dtc imx8mp-evk-patched.dts -o imx8mp-evk-patched.dtb - Now that everything is prepared the machine can be started as follows: .. code-block:: bash @@ -65,6 +57,6 @@ Now that everything is prepared the machine can be started as follows: $ qemu-system-aarch64 -M imx8mp-evk -smp 4 -m 3G \ -display none -serial null -serial stdio \ -kernel Image \ - -dtb imx8mp-evk-patched.dtb \ + -dtb imx8mp-evk.dtb \ -append "root=/dev/mmcblk2p2" \ -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index f17d5db466..3bbf2bfbea 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -15,6 +15,19 @@ #include "system/qtest.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include + +static void imx8mp_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) +{ + int offset; + + /* Remove cpu-idle-states property from CPU nodes */ + offset = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a53"); + while (offset >= 0) { + fdt_nop_property(fdt, offset, "cpu-idle-states"); + offset = fdt_node_offset_by_compatible(fdt, offset, "arm,cortex-a53"); + } +} static void imx8mp_evk_init(MachineState *machine) { @@ -32,6 +45,7 @@ static void imx8mp_evk_init(MachineState *machine) .board_id = -1, .ram_size = machine->ram_size, .psci_conduit = QEMU_PSCI_CONDUIT_SMC, + .modify_dtb = imx8mp_evk_modify_dtb, }; s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); From f978f410aa4f4b5ce9a4c18c815507bcbfb75b9d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 5 Apr 2025 23:49:00 +0200 Subject: [PATCH 2867/2892] hw/arm/imx8mp-evk: Temporarily remove unimplemented imx8mp-fspi node from devicetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nxp,imx8mp-fspi node triggers a warning backtrace. Remove it from the devicetree file. Signed-off-by: Guenter Roeck Inspired-by: commit bf1da4b308 ("hw/arm/raspi4b: Temporarily disable unimplemented rpi4b devices") Signed-off-by: Bernhard Beschow [Bernhard: split patch, adapt commit message] Message-ID: <20250405214900.7114-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/imx8mp-evk.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index 3bbf2bfbea..b5aec06ec5 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -19,7 +19,22 @@ static void imx8mp_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) { - int offset; + int i, offset; + + /* Temporarily disable following nodes until they are implemented */ + const char *nodes_to_remove[] = { + "nxp,imx8mp-fspi", + }; + + for (i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { + const char *dev_str = nodes_to_remove[i]; + + offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + while (offset >= 0) { + fdt_nop_node(fdt, offset); + offset = fdt_node_offset_by_compatible(fdt, offset, dev_str); + } + } /* Remove cpu-idle-states property from CPU nodes */ offset = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a53"); From 8c996e32712820d6d1e62ba6436424d0ba837d96 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 8 Apr 2025 12:20:46 +0200 Subject: [PATCH 2868/2892] hw/nvme: fix attachment of private namespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix regression when attaching private namespaces that gets attached to the wrong controller. Keep track of the original controller "owner" of private namespaces, and only attach if this matches on controller enablement. Fixes: 6ccca4b6bb9f ("hw/nvme: rework csi handling") Reported-by: Alan Adamson Suggested-by: Alan Adamson Signed-off-by: Klaus Jensen Tested-by: Alan Adamson Reviewed-by: Alan Adamson Reviewed-by: Keith Busch Message-ID: <20250408-fix-private-ns-v1-1-28e169b6b60b@samsung.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/nvme/ctrl.c | 7 ++++++- hw/nvme/ns.c | 4 ++++ hw/nvme/nvme.h | 3 +++ hw/nvme/subsys.c | 9 +-------- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 518d02dc66..d6b77d4fbc 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -7755,7 +7755,11 @@ static int nvme_start_ctrl(NvmeCtrl *n) for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { NvmeNamespace *ns = nvme_subsys_ns(n->subsys, i); - if (ns && nvme_csi_supported(n, ns->csi) && !ns->params.detached) { + if (!ns || (!ns->params.shared && ns->ctrl != n)) { + continue; + } + + if (nvme_csi_supported(n, ns->csi) && !ns->params.detached) { if (!ns->attached || ns->params.shared) { nvme_attach_ns(n, ns); } @@ -8988,6 +8992,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) if (n->namespace.blkconf.blk) { ns = &n->namespace; ns->params.nsid = 1; + ns->ctrl = n; if (nvme_ns_setup(ns, errp)) { return; diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 98c1e75a5d..4ab8ba74f5 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -763,6 +763,10 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) ns->id_ns.endgid = cpu_to_le16(0x1); ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); + + if (!ns->params.shared) { + ns->ctrl = n; + } } static const Property nvme_ns_props[] = { diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 6f782ba188..b5c9378ea4 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -268,6 +268,9 @@ typedef struct NvmeNamespace { NvmeSubsystem *subsys; NvmeEnduranceGroup *endgrp; + /* NULL for shared namespaces; set to specific controller if private */ + NvmeCtrl *ctrl; + struct { uint32_t err_rec; } features; diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 2ae56f12a5..b617ac3892 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -56,7 +56,7 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) { NvmeSubsystem *subsys = n->subsys; NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); - int cntlid, nsid, num_rsvd, num_vfs = n->params.sriov_max_vfs; + int cntlid, num_rsvd, num_vfs = n->params.sriov_max_vfs; if (pci_is_vf(&n->parent_obj)) { cntlid = le16_to_cpu(sctrl->scid); @@ -92,13 +92,6 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) subsys->ctrls[cntlid] = n; - for (nsid = 1; nsid < ARRAY_SIZE(subsys->namespaces); nsid++) { - NvmeNamespace *ns = subsys->namespaces[nsid]; - if (ns && ns->params.shared && !ns->params.detached) { - nvme_attach_ns(n, ns); - } - } - return cntlid; } From ae81527b431e235b7f81a400b9a6a27b798c40b3 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 9 Apr 2025 00:27:02 +0800 Subject: [PATCH 2869/2892] scripts/checkpatch: Fix typo in SPDX-License-Identifier keyword MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the typo in the error message to help `grep` the example: ERROR: New file '***' requires 'SPDX-License-Identifer' Fixes: fa4d79c64dae ("scripts: mandate that new files have SPDX-License-Identifier") Signed-off-by: Zhao Liu Reviewed-by: Alex Bennée Message-ID: <20250408162702.2350565-1-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 6ae9d7febe..365892de04 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1688,11 +1688,11 @@ sub process { /\.(c|h|py|pl|sh|json|inc|Makefile)$/) { # source code files MUST have SPDX license declared ERROR("New file '$expect_spdx_file' requires " . - "'SPDX-License-Identifer'"); + "'SPDX-License-Identifier'"); } else { # Other files MAY have SPDX license if appropriate WARN("Does new file '$expect_spdx_file' need " . - "'SPDX-License-Identifer'?"); + "'SPDX-License-Identifier'?"); } } $expect_spdx = 1; From 56c6e249b6988c1b6edc2dd34ebb0f1e570a1365 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 9 Apr 2025 04:33:10 -0400 Subject: [PATCH 2870/2892] Update version for v10.0.0-rc3 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ce4b6dd20e..ab1d7a925c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.92 +9.2.93 From 9edb9c2a6a0ceec9ca41705ba4990041bee1d690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Apr 2025 12:51:12 +0100 Subject: [PATCH 2871/2892] tests/tcg: fix semihosting SYS_EXIT for aarch64 in boot.S MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't expect to hit exceptions in our testing so currently all the vectors report an un-expected exception and then attempt to exit. However for aarch64 we should always use the extended information block as we do in _exit. Rather than duplicate the code on the error handler just branch to the _exit handler with a failing status code. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20250404115641.258048-1-alex.bennee@linaro.org> --- tests/tcg/aarch64/system/boot.S | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index 4eb1b35b88..a5df9c173d 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -73,9 +73,8 @@ lower_a32_serror: mov x0, SYS_WRITE0 adr x1, .error semihosting_call - mov x0, SYS_EXIT - mov x1, 1 - semihosting_call + mov x0, 1 /* EXIT_FAILURE */ + bl _exit /* never returns */ .section .rodata From c07cd110a1824e2d046581af7375f16dac26e96f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 3 Apr 2025 20:20:26 -0700 Subject: [PATCH 2872/2892] plugins/loader: fix deadlock when resetting/uninstalling a plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported and fixed by Dmitry Kurakin. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/2901 Signed-off-by: Pierrick Bouvier Message-Id: <20250404032027.430575-2-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- plugins/loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/loader.c b/plugins/loader.c index 7523d554f0..0d6e082e17 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -370,7 +370,7 @@ static void plugin_reset_destroy(struct qemu_plugin_reset_data *data) { qemu_rec_mutex_lock(&plugin.lock); plugin_reset_destroy__locked(data); - qemu_rec_mutex_lock(&plugin.lock); + qemu_rec_mutex_unlock(&plugin.lock); } static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg) From d832ff9d0248c2f1895352ced3b3bf7a71f42702 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 3 Apr 2025 20:20:27 -0700 Subject: [PATCH 2873/2892] tests/tcg/plugins: add plugin to test reset and uninstall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We perform a plugin reset, uninstall, and make sure we went through those steps. Signed-off-by: Pierrick Bouvier Message-Id: <20250404032027.430575-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée --- tests/tcg/plugins/meson.build | 2 +- tests/tcg/plugins/reset.c | 73 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/plugins/reset.c diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index c8cb0626a6..41f02f2c7f 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -1,6 +1,6 @@ t = [] if get_option('plugins') - foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall'] if host_os == 'windows' t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', diff --git a/tests/tcg/plugins/reset.c b/tests/tcg/plugins/reset.c new file mode 100644 index 0000000000..1be8be2a4b --- /dev/null +++ b/tests/tcg/plugins/reset.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Linaro Ltd + * + * Test the reset/uninstall cycle of a plugin. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +static qemu_plugin_id_t plugin_id; +static bool was_reset; +static bool was_uninstalled; + +static void after_uninstall(qemu_plugin_id_t id) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_outs("uninstall done\n"); + was_uninstalled = true; +} + +static void tb_exec_after_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_uninstall(plugin_id, after_uninstall); +} + +static void tb_trans_after_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_after_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +static void after_reset(qemu_plugin_id_t id) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_outs("reset done\n"); + was_reset = true; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_after_reset); +} + +static void tb_exec_before_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_reset(plugin_id, after_reset); +} + +static void tb_trans_before_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_before_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + plugin_id = id; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_before_reset); + return 0; +} + +/* Since we uninstall the plugin, we can't use qemu_plugin_register_atexit_cb, + * so we use destructor attribute instead. */ +static void __attribute__((destructor)) on_plugin_exit(void) +{ + g_assert(was_reset && was_uninstalled); + qemu_plugin_outs("plugin exit\n"); +} From 22e6d702946c82c6d19326b81430c83ed9ee16a9 Mon Sep 17 00:00:00 2001 From: Stefan Weil via Date: Sat, 12 Apr 2025 20:11:47 +0200 Subject: [PATCH 2874/2892] docs: Fix some typos (found by codespell and typos) Signed-off-by: Stefan Weil Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/about/deprecated.rst | 4 ++-- docs/devel/codebase.rst | 6 +++--- docs/devel/qapi-domain.rst | 4 ++-- include/exec/memory.h | 4 ++-- qapi/qdev.json | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 0f41a99c67..05381441a9 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -452,7 +452,7 @@ Backend ``memory`` (since 9.0) ``reconnect`` (since 9.2) ^^^^^^^^^^^^^^^^^^^^^^^^^ -The ``reconnect`` option only allows specifiying second granularity timeouts, +The ``reconnect`` option only allows specifying second granularity timeouts, which is not enough for all types of use cases, use ``reconnect-ms`` instead. @@ -462,7 +462,7 @@ Net device options Stream ``reconnect`` (since 9.2) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The ``reconnect`` option only allows specifiying second granularity timeouts, +The ``reconnect`` option only allows specifying second granularity timeouts, which is not enough for all types of use cases, use ``reconnect-ms`` instead. VFIO device options diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst index 1b09953197..ef98578296 100644 --- a/docs/devel/codebase.rst +++ b/docs/devel/codebase.rst @@ -5,7 +5,7 @@ Codebase This section presents the various parts of QEMU and how the codebase is organized. -Beyond giving succint descriptions, the goal is to offer links to various +Beyond giving succinct descriptions, the goal is to offer links to various parts of the documentation/codebase. Subsystems @@ -67,7 +67,7 @@ yet, so sometimes the source code is all you have. * `chardev `_: Various backends used by char devices. * `common-user `_: - User-mode assembly code for dealing with signals occuring during syscalls. + User-mode assembly code for dealing with signals occurring during syscalls. * `configs `_: Makefiles defining configurations to build QEMU. * `contrib `_: @@ -102,7 +102,7 @@ yet, so sometimes the source code is all you have. * `.gitlab-ci.d `_: `CI ` yaml and scripts. * `include `_: - All headers associated to different subsystems in QEMU. The hierachy used + All headers associated to different subsystems in QEMU. The hierarchy used mirrors source code organization and naming. * `hw `_: `Devices ` and boards emulation. Devices are categorized by diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index a748529f51..11238723c2 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -41,7 +41,7 @@ Schema or generating documentation from code that exists. It is merely the rST syntax used to describe things. For instance, the Sphinx Python domain adds syntax like ``:py:func:`` for describing Python functions in documentation, but it's the autodoc module that is responsible for -reading python code and generating such syntax. QAPI is analagous here: +reading Python code and generating such syntax. QAPI is analogous here: qapidoc.py is responsible for reading the QAPI Schema and generating rST syntax, and qapi_domain.py is responsible for translating that special syntax and providing APIs for Sphinx internals. @@ -514,7 +514,7 @@ 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 +references that do not explicitly specify a namespace, so this directive can be used to nudge references into preferring targets from within that namespace. diff --git a/include/exec/memory.h b/include/exec/memory.h index d09af58c97..e1c196a0c2 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2162,7 +2162,7 @@ void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size); * only useful on RAM regions. * * @mr: the region being updated. - * @readonly: whether rhe region is to be ROM or RAM. + * @readonly: whether the region is to be ROM or RAM. */ void memory_region_set_readonly(MemoryRegion *mr, bool readonly); @@ -2173,7 +2173,7 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly); * only useful on RAM regions. * * @mr: the region being updated. - * @nonvolatile: whether rhe region is to be non-volatile. + * @nonvolatile: whether the region is to be non-volatile. */ void memory_region_set_nonvolatile(MemoryRegion *mr, bool nonvolatile); diff --git a/qapi/qdev.json b/qapi/qdev.json index 25cbcf977b..32c7d10046 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -173,7 +173,7 @@ # configuration changed. # # The command may be used to notify the guest about block device -# capcity change. Currently only vhost-user-blk device supports +# capacity change. Currently only vhost-user-blk device supports # this. # # @id: the device's ID or QOM path From b0b5af62ef9eaf25246cdd433a4eb69361298ee4 Mon Sep 17 00:00:00 2001 From: Arthur Sengileyev Date: Sat, 12 Apr 2025 21:08:30 +0300 Subject: [PATCH 2875/2892] Fix objdump output parser in "nsis.py" In msys2 distribution objdump from gcc is using single tab character prefix, but objdump from clang is using 4 white space characters instead. The script will not identify any dll dependencies for a QEMU build generated with clang. This in turn will fail the build, because there will be no files inside dlldir and no setup file will be created. Instead of checking for whitespace in prefix use lstrip to accommodate for differences in outputs. Signed-off-by: Arthur Sengileyev Reviewed-by: Stefan Weil Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- scripts/nsis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nsis.py b/scripts/nsis.py index af4e064819..8f469634eb 100644 --- a/scripts/nsis.py +++ b/scripts/nsis.py @@ -23,7 +23,7 @@ def find_deps(exe_or_dll, search_path, analyzed_deps): output = subprocess.check_output(["objdump", "-p", exe_or_dll], text=True) output = output.split("\n") for line in output: - if not line.startswith("\tDLL Name: "): + if not line.lstrip().startswith("DLL Name: "): continue dep = line.split("DLL Name: ")[1].strip() From f3ca7ca22271ac7c1b997092d72ad36517618fb5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 12 Apr 2025 09:43:15 -0700 Subject: [PATCH 2876/2892] docs: Document removal of 64-bit on 32-bit emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With acce728cbc6c we disallowed configuring 64-bit guests on 32-bit hosts, but forgot to document that in removed-features. Signed-off-by: Richard Henderson Reviewed-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Signed-off-by: Michael Tokarev --- docs/about/removed-features.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 2527a91795..790a5e481c 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -858,6 +858,15 @@ QEMU. Since all recent x86 hardware from the past >10 years is capable of the 64-bit x86 extensions, a corresponding 64-bit OS should be used instead. +32-bit hosts for 64-bit guests (removed in 10.0) +'''''''''''''''''''''''''''''''''''''''''''''''' + +In general, 32-bit hosts cannot support the memory space or atomicity +requirements of 64-bit guests. Prior to 10.0, QEMU attempted to +work around the atomicity issues in system mode by running all vCPUs +in a single thread context; in user mode atomicity was simply broken. +From 10.0, QEMU has disabled configuration of 64-bit guests on 32-bit hosts. + Guest Emulator ISAs ------------------- From fa3f3a33f3c4aba56138b1ca78c81a3e15f91971 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 10 Apr 2025 15:55:49 -0700 Subject: [PATCH 2877/2892] system/main: transfer replay mutex ownership from main thread to main loop thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On MacOS, UI event loop has to be ran in the main thread of a process. Because of that restriction, on this platform, qemu main event loop is ran on another thread [1]. This breaks record/replay feature, which expects thread running qemu_init to initialize hold this lock, breaking associated functional tests on MacOS. Thus, as a generalization, and similar to how BQL is handled, we release it after init, and reacquire the lock before entering main event loop, avoiding a special case if a separate thread is used. Tested on MacOS with: $ meson test -C build --setup thorough --print-errorlogs \ func-x86_64-x86_64_replay func-arm-arm_replay func-aarch64-aarch64_replay $ ./build/qemu-system-x86_64 -nographic -icount shift=auto,rr=record,rrfile=replay.log $ ./build/qemu-system-x86_64 -nographic -icount shift=auto,rr=replay,rrfile=replay.log [1] https://gitlab.com/qemu-project/qemu/-/commit/f5ab12caba4f1656479c1feb5248beac1c833243 Fixes: https://gitlab.com/qemu-project/qemu/-/issues/2907 Signed-off-by: Pierrick Bouvier Reviewed-by: Nicholas Piggin Tested-by: Philippe Mathieu-Daudé Message-ID: <20250410225550.46807-2-pierrick.bouvier@linaro.org> Signed-off-by: Stefan Hajnoczi --- system/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/main.c b/system/main.c index ecb12fd397..1c02206734 100644 --- a/system/main.c +++ b/system/main.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu-main.h" #include "qemu/main-loop.h" +#include "system/replay.h" #include "system/system.h" #ifdef CONFIG_SDL @@ -44,10 +45,12 @@ static void *qemu_default_main(void *opaque) { int status; + replay_mutex_lock(); bql_lock(); status = qemu_main_loop(); qemu_cleanup(status); bql_unlock(); + replay_mutex_unlock(); exit(status); } @@ -67,6 +70,7 @@ int main(int argc, char **argv) { qemu_init(argc, argv); bql_unlock(); + replay_mutex_unlock(); if (qemu_main) { QemuThread main_loop_thread; qemu_thread_create(&main_loop_thread, "qemu_main", From 8bdd3a0308ba8e8d20240ac06de8615959bcf00e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 10 Apr 2025 15:55:50 -0700 Subject: [PATCH 2878/2892] tests/functional/test_aarch64_replay: reenable on macos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Message-ID: <20250410225550.46807-3-pierrick.bouvier@linaro.org> Signed-off-by: Stefan Hajnoczi --- tests/functional/test_aarch64_replay.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/test_aarch64_replay.py index 029fef3cbf..bd6609d914 100755 --- a/tests/functional/test_aarch64_replay.py +++ b/tests/functional/test_aarch64_replay.py @@ -16,8 +16,6 @@ class Aarch64Replay(ReplayKernelBase): 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') - # Failing on Darwin: https://gitlab.com/qemu-project/qemu/-/issues/2907 - @skipIfOperatingSystem('Darwin') def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' From e28fbd1c525db21f0502b85517f49504c9f9dcd8 Mon Sep 17 00:00:00 2001 From: Antoine Damhet Date: Tue, 8 Apr 2025 16:53:33 +0200 Subject: [PATCH 2879/2892] Revert "virtio-net: Copy received header to buffer" This reverts commit 7987d2be5a8bc3a502f89ba8cf3ac3e09f64d1ce. The goal was to remove the need to patch the (const) input buffer with a recomputed UDP checksum by copying headers to a RW region and inject the checksum there. The patch computed the checksum only from the header fields (missing the rest of the payload) producing an invalid one and making guests fail to acquire a DHCP lease. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2727 Cc: qemu-stable@nongnu.org Signed-off-by: Antoine Damhet Acked-by: Michael S. Tsirkin Message-ID: <20250408145345.142947-1-adamhet@scaleway.com> Signed-off-by: Stefan Hajnoczi --- hw/net/virtio-net.c | 87 +++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 340c6b6422..bd37651dab 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1702,44 +1702,41 @@ static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) * cache. */ static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, - size_t *hdr_len, const uint8_t *buf, - size_t buf_size, size_t *buf_offset) + uint8_t *buf, size_t size) { size_t csum_size = ETH_HLEN + sizeof(struct ip_header) + sizeof(struct udp_header); - buf += *buf_offset; - buf_size -= *buf_offset; - if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ - (buf_size >= csum_size && buf_size < 1500) && /* normal sized MTU */ + (size >= csum_size && size < 1500) && /* normal sized MTU */ (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ (buf[23] == 17) && /* ip.protocol == UDP */ (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ - memcpy((uint8_t *)hdr + *hdr_len, buf, csum_size); - net_checksum_calculate((uint8_t *)hdr + *hdr_len, csum_size, CSUM_UDP); + net_checksum_calculate(buf, size, CSUM_UDP); hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; - *hdr_len += csum_size; - *buf_offset += csum_size; } } -static size_t receive_header(VirtIONet *n, struct virtio_net_hdr *hdr, - const void *buf, size_t buf_size, - size_t *buf_offset) +static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, + const void *buf, size_t size) { - size_t hdr_len = n->guest_hdr_len; + if (n->has_vnet_hdr) { + /* FIXME this cast is evil */ + void *wbuf = (void *)buf; + work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, + size - n->host_hdr_len); - memcpy(hdr, buf, sizeof(struct virtio_net_hdr)); - - *buf_offset = n->host_hdr_len; - work_around_broken_dhclient(hdr, &hdr_len, buf, buf_size, buf_offset); - - if (n->needs_vnet_hdr_swap) { - virtio_net_hdr_swap(VIRTIO_DEVICE(n), hdr); + if (n->needs_vnet_hdr_swap) { + virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); + } + iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); + } else { + struct virtio_net_hdr hdr = { + .flags = 0, + .gso_type = VIRTIO_NET_HDR_GSO_NONE + }; + iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); } - - return hdr_len; } static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) @@ -1907,13 +1904,6 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, return (index == new_index) ? -1 : new_index; } -typedef struct Header { - struct virtio_net_hdr_v1_hash virtio_net; - struct eth_header eth; - struct ip_header ip; - struct udp_header udp; -} Header; - static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, size_t size) { @@ -1923,15 +1913,15 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; size_t lens[VIRTQUEUE_MAX_SIZE]; struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; - Header hdr; + struct virtio_net_hdr_v1_hash extra_hdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset, j; ssize_t err; - memset(&hdr.virtio_net, 0, sizeof(hdr.virtio_net)); + memset(&extra_hdr, 0, sizeof(extra_hdr)); if (n->rss_data.enabled && n->rss_data.enabled_software_rss) { - int index = virtio_net_process_rss(nc, buf, size, &hdr.virtio_net); + int index = virtio_net_process_rss(nc, buf, size, &extra_hdr); if (index >= 0) { nc = qemu_get_subqueue(n->nic, index % n->curr_queue_pairs); } @@ -1996,20 +1986,23 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, if (n->mergeable_rx_bufs) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), sg, elem->in_num, - offsetof(typeof(hdr), - virtio_net.hdr.num_buffers), - sizeof(hdr.virtio_net.hdr.num_buffers)); + offsetof(typeof(extra_hdr), hdr.num_buffers), + sizeof(extra_hdr.hdr.num_buffers)); } else { - hdr.virtio_net.hdr.num_buffers = cpu_to_le16(1); + extra_hdr.hdr.num_buffers = cpu_to_le16(1); } - guest_offset = n->has_vnet_hdr ? - receive_header(n, (struct virtio_net_hdr *)&hdr, - buf, size, &offset) : - n->guest_hdr_len; - - iov_from_buf(sg, elem->in_num, 0, &hdr, guest_offset); - total += guest_offset; + receive_header(n, sg, elem->in_num, buf, size); + if (n->rss_data.populate_hash) { + offset = offsetof(typeof(extra_hdr), hash_value); + iov_from_buf(sg, elem->in_num, offset, + (char *)&extra_hdr + offset, + sizeof(extra_hdr.hash_value) + + sizeof(extra_hdr.hash_report)); + } + offset = n->host_hdr_len; + total += n->guest_hdr_len; + guest_offset = n->guest_hdr_len; } else { guest_offset = 0; } @@ -2035,11 +2028,11 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, } if (mhdr_cnt) { - virtio_stw_p(vdev, &hdr.virtio_net.hdr.num_buffers, i); + virtio_stw_p(vdev, &extra_hdr.hdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, - &hdr.virtio_net.hdr.num_buffers, - sizeof hdr.virtio_net.hdr.num_buffers); + &extra_hdr.hdr.num_buffers, + sizeof extra_hdr.hdr.num_buffers); } for (j = 0; j < i; j++) { From a9cd5bc6399a80fcf233ed0fffe6067b731227d8 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 15 Apr 2025 11:54:04 -0400 Subject: [PATCH 2880/2892] Update version for v10.0.0-rc4 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ab1d7a925c..b55306c36d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.93 +9.2.94 From 7c949c53e936aa3a658d84ab53bae5cadaa5d59c Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 22 Apr 2025 09:32:33 -0400 Subject: [PATCH 2881/2892] Update version for the v10.0.0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b55306c36d..a13e7b9c87 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.2.94 +10.0.0 From 7633d5fce5315caba5361867127e9375d1c8f29b Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 29 Apr 2025 14:26:42 +0200 Subject: [PATCH 2882/2892] reorganize stuff --- accel/tcg/cpu-exec.c | 23 +-- accel/tcg/internal-common.h | 1 + accel/tcg/tcg-runtime.c | 18 -- accel/tcg/tcg-runtime.h | 10 -- accel/tcg/translate-all.c | 208 +-------------------- hw/core/cpu-system.c | 19 +- include/exec/cpu-all.h | 4 - include/exec/helper-gen-common.h | 6 + include/exec/helper-proto-common.h | 6 + include/libafl/exit.h | 3 +- include/libafl/tcg-helper.h | 4 + include/libafl/tcg.h | 4 +- include/user/cpu_loop.h | 5 +- include/user/page-protection.h | 4 + libafl/cpu.c | 4 +- libafl/exit.c | 2 - libafl/meson.build | 2 + libafl/tcg-helper.c | 22 +++ libafl/tcg.c | 279 +++++++++++++++++++++++++++++ linux-user/i386/cpu_loop.c | 4 + linux-user/user-internals.h | 1 - 21 files changed, 367 insertions(+), 262 deletions(-) create mode 100644 include/libafl/tcg-helper.h create mode 100644 libafl/tcg-helper.c create mode 100644 libafl/tcg.c diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 95bf5f0199..6403eb1191 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -45,6 +45,15 @@ #include "internal-common.h" #include "internal-target.h" +//// --- Begin LibAFL code --- + +#include "libafl/exit.h" +#include "libafl/tcg.h" +#include "libafl/hooks/tcg/edge.h" + +//// --- End LibAFL code --- + + /* -icount align implementation. */ typedef struct SyncClocks { @@ -702,12 +711,6 @@ static inline void cpu_handle_debug_exception(CPUState *cpu) } } -//// --- Begin LibAFL code --- - -#include "libafl/exit.h" - -//// --- End LibAFL code --- - static inline bool cpu_handle_exception(CPUState *cpu, int *ret) { //// --- Begin LibAFL code --- @@ -958,14 +961,6 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, #endif } -//// --- Begin LibAFL code --- - -TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, - target_ulong dst_block, int exit_n, target_ulong cs_base, - uint32_t flags, int cflags); - -//// --- End LibAFL code --- - /* main execution loop */ static int __attribute__((noinline)) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 9b6ab3a8cc..c3fa9ab0e0 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -54,6 +54,7 @@ void tb_reset_jump(TranslationBlock *tb, int n); TranslationBlock *tb_link_page(TranslationBlock *tb); void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, uintptr_t host_pc); +int encode_search(TranslationBlock *tb, uint8_t *block); /** * tlb_init - initialize a CPU's TLB diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 5f029d9686..fa7ed9739c 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -31,24 +31,6 @@ #include "exec/helper-info.c.inc" #undef HELPER_H -//// --- Begin LibAFL code --- - -#include "libafl/exit.h" - -void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env, uint64_t pc) -{ - CPUState* cpu = env_cpu(env); - libafl_exit_request_breakpoint(cpu, (target_ulong) pc); -} - -void HELPER(libafl_qemu_handle_custom_insn)(CPUArchState *env, uint64_t pc, uint32_t kind) -{ - CPUState* cpu = env_cpu(env); - libafl_exit_request_custom_insn(cpu, (target_ulong) pc, (enum libafl_custom_insn_kind) kind); -} - -//// --- End LibAFL code --- - /* 32-bit helpers */ int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2) diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 3f154ccf76..c23b5e66c4 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -323,13 +323,3 @@ DEF_HELPER_FLAGS_4(gvec_leus32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(gvec_leus64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -//// --- Begin LibAFL code --- - -DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, - void, env, i64) - -DEF_HELPER_FLAGS_3(libafl_qemu_handle_custom_insn, TCG_CALL_NO_RWG, - void, env, i64, i32) - -//// --- End LibAFL code --- diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index ec73612f3c..6274798869 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -132,8 +132,10 @@ static int64_t decode_sleb128(const uint8_t **pp) line. The seed for the first line is { tb->pc, 0..., tb->tc.ptr }. That is, the first column is seeded with the guest pc, the last column with the host pc, and the middle columns with zeros. */ - -static int encode_search(TranslationBlock *tb, uint8_t *block) +/* +static +*/ +int encode_search(TranslationBlock *tb, uint8_t *block) { uint8_t *highwater = tcg_ctx->code_gen_highwater; uint64_t *insn_data = tcg_ctx->gen_insn_data; @@ -299,208 +301,6 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, return tcg_gen_code(tcg_ctx, tb, pc); } - -/* Called with mmap_lock held for user mode emulation. */ -TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, - target_ulong dst_block, int exit_n, - target_ulong cs_base, uint32_t flags, - int cflags) -{ - CPUArchState *env = cpu_env(cpu); - TranslationBlock *tb; - tb_page_addr_t phys_pc; - tcg_insn_unit *gen_code_buf; - int gen_code_size, search_size, max_insns; - int64_t ti; - void *host_pc; - - // edge hooks generation callbacks - // early check if it should be skipped or not - bool no_exec_hook = libafl_qemu_hook_edge_gen(src_block, dst_block); - if (no_exec_hook) { - // no exec hooks to run for edges, not point in generating a TB - return NULL; - } - - target_ulong pc = src_block ^ reverse_bits((target_ulong)exit_n); - - assert_memory_lock(); - qemu_thread_jit_write(); - - // TODO: this (get_page_addr_code_hostp) is a bottleneck in systemmode, investigate why - phys_pc = get_page_addr_code_hostp(env, src_block, &host_pc); - phys_pc ^= reverse_bits((tb_page_addr_t)exit_n); - - // if (phys_pc == -1) { - // /* Generate a one-shot TB with 1 insn in it */ - // cflags = (cflags & ~CF_COUNT_MASK) | 1; - // } - - /* Generate a one-shot TB with max 16 insn in it */ - cflags = (cflags & ~CF_COUNT_MASK) | LIBAFL_MAX_INSNS; - QEMU_BUILD_BUG_ON(LIBAFL_MAX_INSNS > TCG_MAX_INSNS); - - max_insns = cflags & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = TCG_MAX_INSNS; - } - QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); - - buffer_overflow: - assert_no_pages_locked(); - tb = tcg_tb_alloc(tcg_ctx); - if (unlikely(!tb)) { - /* flush must be done */ - tb_flush(cpu); - mmap_unlock(); - /* Make the execution loop process the flush as soon as possible. */ - cpu->exception_index = EXCP_INTERRUPT; - cpu_loop_exit(cpu); - } - - gen_code_buf = tcg_ctx->code_gen_ptr; - tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); - - if (!(cflags & CF_PCREL)) { - tb->pc = pc; - } - - tb->cs_base = cs_base; - tb->flags = flags; - tb->cflags = cflags | CF_IS_EDGE; - tb_set_page_addr0(tb, phys_pc); - tb_set_page_addr1(tb, -1); - // if (phys_pc != -1) { - // tb_lock_page0(phys_pc); - // } - - tcg_ctx->gen_tb = tb; - tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; -#ifdef CONFIG_SOFTMMU - tcg_ctx->page_bits = TARGET_PAGE_BITS; - tcg_ctx->page_mask = TARGET_PAGE_MASK; - tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; -#endif - tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; -#ifdef TCG_GUEST_DEFAULT_MO - tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO; -#else - tcg_ctx->guest_mo = TCG_MO_ALL; -#endif - - restart_translate: - trace_translate_block(tb, pc, tb->tc.ptr); - - gen_code_size = libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); - if (unlikely(gen_code_size < 0)) { - switch (gen_code_size) { - case -1: - /* - * Overflow of code_gen_buffer, or the current slice of it. - * - * TODO: We don't need to re-do gen_intermediate_code, nor - * should we re-do the tcg optimization currently hidden - * inside tcg_gen_code. All that should be required is to - * flush the TBs, allocate a new TB, re-initialize it per - * above, and re-do the actual code generation. - */ - qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, - "Restarting code generation for " - "code_gen_buffer overflow\n"); - tb_unlock_pages(tb); - tcg_ctx->gen_tb = NULL; - goto buffer_overflow; - - case -2: - assert(false && "This should never happen for edge code. There must be a bug."); - /* - * The code generated for the TranslationBlock is too large. - * The maximum size allowed by the unwind info is 64k. - * There may be stricter constraints from relocations - * in the tcg backend. - * - * Try again with half as many insns as we attempted this time. - * If a single insn overflows, there's a bug somewhere... - */ - assert(max_insns > 1); - max_insns /= 2; - qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, - "Restarting code generation with " - "smaller translation block (max %d insns)\n", - max_insns); - - /* - * The half-sized TB may not cross pages. - * TODO: Fix all targets that cross pages except with - * the first insn, at which point this can't be reached. - */ - // phys_p2 = tb_page_addr1(tb); - // if (unlikely(phys_p2 != -1)) { - // tb_unlock_page1(phys_pc, phys_p2); - // tb_set_page_addr1(tb, -1); - // } - goto restart_translate; - - case -3: - /* - * We had a page lock ordering problem. In order to avoid - * deadlock we had to drop the lock on page0, which means - * that everything we translated so far is compromised. - * Restart with locks held on both pages. - */ - qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, - "Restarting code generation with re-locked pages"); - goto restart_translate; - - default: - g_assert_not_reached(); - } - } - tcg_ctx->gen_tb = NULL; - - search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); - if (unlikely(search_size < 0)) { - tb_unlock_pages(tb); - goto buffer_overflow; - } - tb->tc.size = gen_code_size; - - /* - * For CF_PCREL, attribute all executions of the generated code - * to its first mapping. - */ - perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf)); - - qatomic_set(&tcg_ctx->code_gen_ptr, (void *) - ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, - CODE_GEN_ALIGN)); - - /* init jump list */ - qemu_spin_init(&tb->jmp_lock); - tb->jmp_list_head = (uintptr_t)NULL; - tb->jmp_list_next[0] = (uintptr_t)NULL; - tb->jmp_list_next[1] = (uintptr_t)NULL; - tb->jmp_dest[0] = (uintptr_t)NULL; - tb->jmp_dest[1] = (uintptr_t)NULL; - - /* init original jump addresses which have been set during tcg_gen_code() */ - if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { - tb_reset_jump(tb, 0); - } - if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { - tb_reset_jump(tb, 1); - } - - assert_no_pages_locked(); - -#ifndef CONFIG_USER_ONLY - tb->page_addr[0] = tb->page_addr[1] = -1; -#endif - return tb; -} - -//// --- End LibAFL code --- - /* Called with mmap_lock held for user mode emulation. */ TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc, uint64_t cs_base, diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index aed5076ec7..b0333dc2c5 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -31,6 +31,14 @@ #include "migration/vmstate.h" #include "system/tcg.h" +//// --- Begin LibAFL code --- + +#ifndef CONFIG_USER_ONLY +#include "libafl/syx-snapshot/device-save.h" +#endif + +//// --- End LibAFL code --- + bool cpu_has_work(CPUState *cpu) { return cpu->cc->sysemu_ops->has_work(cpu); @@ -214,7 +222,16 @@ static int cpu_common_post_load(void *opaque, int version_id) * memory we've translated code from. So we must flush all TBs, * which will now be stale. */ - tb_flush(cpu); + //tb_flush(cpu); + //// --- Begin LibAFL code --- + + // flushing the TBs every restore makes it really slow + // TODO handle writes to X code with specific calls to tb_invalidate_phys_addr + if (!libafl_devices_is_restoring()) { + tb_flush(cpu); + } + + //// --- End LibAFL code --- } return 0; diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index a1430528ec..5de58da40e 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -100,10 +100,6 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val #include "exec/cpu-defs.h" #include "exec/target_page.h" -//// --- Begin LibAFL code --- -IntervalTreeRoot* pageflags_get_root(void); -//// --- End LibAFL code --- - CPUArchState *cpu_copy(CPUArchState *env); #include "cpu.h" diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h index 834590dc4e..a6fa47c20b 100644 --- a/include/exec/helper-gen-common.h +++ b/include/exec/helper-gen-common.h @@ -11,4 +11,10 @@ #include "exec/helper-gen.h.inc" #undef HELPER_H +//// --- Begin LibAFL code --- +#define HELPER_H "libafl/tcg-helper.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H +//// --- End LibAFL code --- + #endif /* HELPER_GEN_COMMON_H */ diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h index 16782ef46c..959771ac29 100644 --- a/include/exec/helper-proto-common.h +++ b/include/exec/helper-proto-common.h @@ -13,4 +13,10 @@ #include "exec/helper-proto.h.inc" #undef HELPER_H +//// --- Begin LibAFL code --- +#define HELPER_H "libafl/tcg-helper.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H +//// --- End LibAFL code --- + #endif /* HELPER_PROTO_COMMON_H */ diff --git a/include/libafl/exit.h b/include/libafl/exit.h index b5716f1d41..5af1be547b 100644 --- a/include/libafl/exit.h +++ b/include/libafl/exit.h @@ -1,7 +1,8 @@ #pragma once #include "qemu/osdep.h" -#include "exec/cpu-defs.h" +#include "exec/cpu_ldst.h" +#include "hw/core/cpu.h" #define EXCP_LIBAFL_EXIT 0xf4775747 diff --git a/include/libafl/tcg-helper.h b/include/libafl/tcg-helper.h new file mode 100644 index 0000000000..050356a460 --- /dev/null +++ b/include/libafl/tcg-helper.h @@ -0,0 +1,4 @@ +DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, + void, env, i64) +DEF_HELPER_FLAGS_3(libafl_qemu_handle_custom_insn, TCG_CALL_NO_RWG, + void, env, i64, i32) diff --git a/include/libafl/tcg.h b/include/libafl/tcg.h index 2320fb2f93..372419c1a7 100644 --- a/include/libafl/tcg.h +++ b/include/libafl/tcg.h @@ -1,10 +1,8 @@ #pragma once #include "qemu/osdep.h" -#include "qapi/error.h" - #include "tcg/tcg.h" -#include "tcg/helper-info.h" void tcg_gen_callN(void* func, TCGHelperInfo* info, TCGTemp* ret, TCGTemp** args); + diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index 589c66543f..1ef11f720c 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -77,7 +77,10 @@ G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, MMUAccessType access_type, uintptr_t ra); -G_NORETURN void cpu_loop(CPUArchState *env); +//// --- Begin LibAFL code --- +/* G_NORETURN */ +//// --- End LibAFL code --- +void cpu_loop(CPUArchState *env); void target_exception_dump(CPUArchState *env, const char *fmt, int code); #define EXCP_DUMP(env, fmt, code) \ diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 51daa18648..6f7902fc80 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -96,4 +96,8 @@ int walk_memory_regions(void *, walk_memory_regions_fn); void page_dump(FILE *f); +//// --- Begin LibAFL code --- +IntervalTreeRoot* pageflags_get_root(void); +//// --- End LibAFL code --- + #endif diff --git a/libafl/cpu.c b/libafl/cpu.c index 36e748e70b..409fcb3aab 100644 --- a/libafl/cpu.c +++ b/libafl/cpu.c @@ -2,18 +2,16 @@ #ifdef CONFIG_USER_ONLY #include "qemu.h" -#include "user-internals.h" +#include "user/cpu_loop.h" #endif #include "exec/gdbstub.h" -#include "exec/cpu-defs.h" #include "exec/tb-flush.h" #include "exec/exec-all.h" #include "hw/core/sysemu-cpu-ops.h" #include "libafl/cpu.h" -#include "libafl/exit.h" #include "libafl/hook.h" int gdb_write_register(CPUState* cpu, uint8_t* mem_buf, int reg); diff --git a/libafl/exit.c b/libafl/exit.c index 92190eef4b..2a6efeaf6d 100644 --- a/libafl/exit.c +++ b/libafl/exit.c @@ -2,8 +2,6 @@ #include "tcg/tcg.h" #include "tcg/tcg-op.h" -#include "tcg/tcg-temp-internal.h" -#include "sysemu/runstate.h" #include "cpu.h" #include "libafl/cpu.h" diff --git a/libafl/meson.build b/libafl/meson.build index b6d1dc52e0..8eb288257f 100644 --- a/libafl/meson.build +++ b/libafl/meson.build @@ -5,6 +5,8 @@ specific_ss.add(files( 'jit.c', 'utils.c', 'gdb.c', + 'tcg.c', + 'tcg-helper.c', # TCG-related hooks 'hooks/tcg/backdoor.c', diff --git a/libafl/tcg-helper.c b/libafl/tcg-helper.c new file mode 100644 index 0000000000..48e522403b --- /dev/null +++ b/libafl/tcg-helper.c @@ -0,0 +1,22 @@ +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "exec/cpu-common.h" +#include "exec/helper-proto-common.h" + +#include "libafl/exit.h" + +#define HELPER_H "libafl/tcg-helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + +void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env, uint64_t pc) +{ + CPUState* cpu = env_cpu(env); + libafl_exit_request_breakpoint(cpu, (target_ulong) pc); +} + +void HELPER(libafl_qemu_handle_custom_insn)(CPUArchState *env, uint64_t pc, uint32_t kind) +{ + CPUState* cpu = env_cpu(env); + libafl_exit_request_custom_insn(cpu, (target_ulong) pc, (enum libafl_custom_insn_kind) kind); +} diff --git a/libafl/tcg.c b/libafl/tcg.c new file mode 100644 index 0000000000..a8ac8aaa25 --- /dev/null +++ b/libafl/tcg.c @@ -0,0 +1,279 @@ +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "exec/cpu-common.h" + +#include "accel/tcg/internal-common.h" +#include "accel/tcg/internal-target.h" +#include "accel/tcg/trace.h" + +#include "tcg/insn-start-words.h" +#include "tcg/perf.h" + +#include "libafl/tcg.h" +#include "libafl/hooks/tcg/edge.h" + +static target_ulong reverse_bits(target_ulong num) +{ + unsigned int count = sizeof(num) * 8 - 1; + target_ulong reverse_num = num; + + num >>= 1; + while(num) + { + reverse_num <<= 1; + reverse_num |= num & 1; + num >>= 1; + count--; + } + reverse_num <<= count; + return reverse_num; +} + +/* + * Isolate the portion of code gen which can setjmp/longjmp. + * Return the size of the generated code, or negative on error. + */ +static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, + vaddr pc, void *host_pc, + int *max_insns, int64_t *ti) +{ + int ret = sigsetjmp(tcg_ctx->jmp_trans, 0); + if (unlikely(ret != 0)) { + return ret; + } + + tcg_func_start(tcg_ctx); + + tcg_ctx->cpu = env_cpu(env); + + // -- start gen_intermediate_code + const int num_insns = 1; // do "as-if" we were translating a single target instruction + +#ifndef TARGET_INSN_START_EXTRA_WORDS + tcg_gen_insn_start(pc); +#elif TARGET_INSN_START_EXTRA_WORDS == 1 + tcg_gen_insn_start(pc, 0); +#elif TARGET_INSN_START_EXTRA_WORDS == 2 + tcg_gen_insn_start(pc, 0, 0); +#else +#error Unhandled TARGET_INSN_START_EXTRA_WORDS value +#endif + + // run edge hooks + libafl_qemu_hook_edge_run(); + + tcg_gen_goto_tb(0); + tcg_gen_exit_tb(tb, 0); + + // This is obviously wrong, but it is required that the number / size of target instruction translated + // is at least 1. For now, we make it so that no problem occurs later on. + tb->icount = num_insns; // number of target instructions translated in the TB. + tb->size = num_insns; // size (in target bytes) of target instructions translated in the TB. + // -- end gen_intermediate_code + + assert(tb->size != 0); + tcg_ctx->cpu = NULL; + *max_insns = tb->icount; + + return tcg_gen_code(tcg_ctx, tb, pc); +} + +/* Called with mmap_lock held for user mode emulation. */ +TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, + target_ulong dst_block, int exit_n, + target_ulong cs_base, uint32_t flags, + int cflags) +{ + CPUArchState *env = cpu_env(cpu); + TranslationBlock *tb; + tb_page_addr_t phys_pc; + tcg_insn_unit *gen_code_buf; + int gen_code_size, search_size, max_insns; + int64_t ti; + void *host_pc; + + // edge hooks generation callbacks + // early check if it should be skipped or not + bool no_exec_hook = libafl_qemu_hook_edge_gen(src_block, dst_block); + if (no_exec_hook) { + // no exec hooks to run for edges, not point in generating a TB + return NULL; + } + + target_ulong pc = src_block ^ reverse_bits((target_ulong)exit_n); + + assert_memory_lock(); + qemu_thread_jit_write(); + + // TODO: this (get_page_addr_code_hostp) is a bottleneck in systemmode, investigate why + phys_pc = get_page_addr_code_hostp(env, src_block, &host_pc); + phys_pc ^= reverse_bits((tb_page_addr_t)exit_n); + + // if (phys_pc == -1) { + // /* Generate a one-shot TB with 1 insn in it */ + // cflags = (cflags & ~CF_COUNT_MASK) | 1; + // } + + /* Generate a one-shot TB with max 16 insn in it */ + cflags = (cflags & ~CF_COUNT_MASK) | LIBAFL_MAX_INSNS; + QEMU_BUILD_BUG_ON(LIBAFL_MAX_INSNS > TCG_MAX_INSNS); + + max_insns = cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = TCG_MAX_INSNS; + } + QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); + + buffer_overflow: + assert_no_pages_locked(); + tb = tcg_tb_alloc(tcg_ctx); + if (unlikely(!tb)) { + /* flush must be done */ + tb_flush(cpu); + mmap_unlock(); + /* Make the execution loop process the flush as soon as possible. */ + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(cpu); + } + + gen_code_buf = tcg_ctx->code_gen_ptr; + tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); + + if (!(cflags & CF_PCREL)) { + tb->pc = pc; + } + + tb->cs_base = cs_base; + tb->flags = flags; + tb->cflags = cflags | CF_IS_EDGE; + tb_set_page_addr0(tb, phys_pc); + tb_set_page_addr1(tb, -1); + // if (phys_pc != -1) { + // tb_lock_page0(phys_pc); + // } + + tcg_ctx->gen_tb = tb; + tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; +#ifdef CONFIG_SOFTMMU + tcg_ctx->page_bits = TARGET_PAGE_BITS; + tcg_ctx->page_mask = TARGET_PAGE_MASK; + tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; +#endif + tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; +#ifdef TCG_GUEST_DEFAULT_MO + tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO; +#else + tcg_ctx->guest_mo = TCG_MO_ALL; +#endif + + restart_translate: + trace_translate_block(tb, pc, tb->tc.ptr); + + gen_code_size = libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); + if (unlikely(gen_code_size < 0)) { + switch (gen_code_size) { + case -1: + /* + * Overflow of code_gen_buffer, or the current slice of it. + * + * TODO: We don't need to re-do gen_intermediate_code, nor + * should we re-do the tcg optimization currently hidden + * inside tcg_gen_code. All that should be required is to + * flush the TBs, allocate a new TB, re-initialize it per + * above, and re-do the actual code generation. + */ + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation for " + "code_gen_buffer overflow\n"); + tb_unlock_pages(tb); + tcg_ctx->gen_tb = NULL; + goto buffer_overflow; + + case -2: + assert(false && "This should never happen for edge code. There must be a bug."); + /* + * The code generated for the TranslationBlock is too large. + * The maximum size allowed by the unwind info is 64k. + * There may be stricter constraints from relocations + * in the tcg backend. + * + * Try again with half as many insns as we attempted this time. + * If a single insn overflows, there's a bug somewhere... + */ + assert(max_insns > 1); + max_insns /= 2; + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation with " + "smaller translation block (max %d insns)\n", + max_insns); + + /* + * The half-sized TB may not cross pages. + * TODO: Fix all targets that cross pages except with + * the first insn, at which point this can't be reached. + */ + // phys_p2 = tb_page_addr1(tb); + // if (unlikely(phys_p2 != -1)) { + // tb_unlock_page1(phys_pc, phys_p2); + // tb_set_page_addr1(tb, -1); + // } + goto restart_translate; + + case -3: + /* + * We had a page lock ordering problem. In order to avoid + * deadlock we had to drop the lock on page0, which means + * that everything we translated so far is compromised. + * Restart with locks held on both pages. + */ + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation with re-locked pages"); + goto restart_translate; + + default: + g_assert_not_reached(); + } + } + tcg_ctx->gen_tb = NULL; + + search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); + if (unlikely(search_size < 0)) { + tb_unlock_pages(tb); + goto buffer_overflow; + } + tb->tc.size = gen_code_size; + + /* + * For CF_PCREL, attribute all executions of the generated code + * to its first mapping. + */ + perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf)); + + qatomic_set(&tcg_ctx->code_gen_ptr, (void *) + ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, + CODE_GEN_ALIGN)); + + /* init jump list */ + qemu_spin_init(&tb->jmp_lock); + tb->jmp_list_head = (uintptr_t)NULL; + tb->jmp_list_next[0] = (uintptr_t)NULL; + tb->jmp_list_next[1] = (uintptr_t)NULL; + tb->jmp_dest[0] = (uintptr_t)NULL; + tb->jmp_dest[1] = (uintptr_t)NULL; + + /* init original jump addresses which have been set during tcg_gen_code() */ + if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { + tb_reset_jump(tb, 0); + } + if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { + tb_reset_jump(tb, 1); + } + + assert_no_pages_locked(); + +#ifndef CONFIG_USER_ONLY + tb->page_addr[0] = tb->page_addr[1] = -1; +#endif + return tb; +} diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 0008780d19..afca47ab34 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -25,6 +25,10 @@ #include "signal-common.h" #include "user-mmap.h" +//// --- Begin LibAFL code --- +#include "libafl/exit.h" +//// --- End LibAFL code --- + /***********************************************************/ /* CPUX86 core interface */ diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 02a6edb0c4..4aa253b566 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -64,7 +64,6 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8); extern __thread CPUState *thread_cpu; -/* G_NORETURN */ void cpu_loop(CPUArchState *env); abi_long get_errno(abi_long ret); const char *target_strerror(int err); int get_osversion(void); From 93663809156a33475ade972cdd5b1301b9310687 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 29 Apr 2025 14:29:00 +0200 Subject: [PATCH 2883/2892] format --- include/libafl/tcg-helper.h | 8 +- include/libafl/tcg.h | 1 - libafl/tcg-helper.c | 14 +-- libafl/tcg.c | 174 +++++++++++++++++++----------------- 4 files changed, 103 insertions(+), 94 deletions(-) diff --git a/include/libafl/tcg-helper.h b/include/libafl/tcg-helper.h index 050356a460..b5da9e6273 100644 --- a/include/libafl/tcg-helper.h +++ b/include/libafl/tcg-helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, - void, env, i64) -DEF_HELPER_FLAGS_3(libafl_qemu_handle_custom_insn, TCG_CALL_NO_RWG, - void, env, i64, i32) +DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, void, env, + i64) +DEF_HELPER_FLAGS_3(libafl_qemu_handle_custom_insn, TCG_CALL_NO_RWG, void, env, + i64, i32) diff --git a/include/libafl/tcg.h b/include/libafl/tcg.h index 372419c1a7..5bdb373c4c 100644 --- a/include/libafl/tcg.h +++ b/include/libafl/tcg.h @@ -5,4 +5,3 @@ void tcg_gen_callN(void* func, TCGHelperInfo* info, TCGTemp* ret, TCGTemp** args); - diff --git a/libafl/tcg-helper.c b/libafl/tcg-helper.c index 48e522403b..484dfd46fb 100644 --- a/libafl/tcg-helper.c +++ b/libafl/tcg-helper.c @@ -5,18 +5,20 @@ #include "libafl/exit.h" -#define HELPER_H "libafl/tcg-helper.h" +#define HELPER_H "libafl/tcg-helper.h" #include "exec/helper-info.c.inc" -#undef HELPER_H +#undef HELPER_H -void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env, uint64_t pc) +void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState* env, uint64_t pc) { CPUState* cpu = env_cpu(env); - libafl_exit_request_breakpoint(cpu, (target_ulong) pc); + libafl_exit_request_breakpoint(cpu, (target_ulong)pc); } -void HELPER(libafl_qemu_handle_custom_insn)(CPUArchState *env, uint64_t pc, uint32_t kind) +void HELPER(libafl_qemu_handle_custom_insn)(CPUArchState* env, uint64_t pc, + uint32_t kind) { CPUState* cpu = env_cpu(env); - libafl_exit_request_custom_insn(cpu, (target_ulong) pc, (enum libafl_custom_insn_kind) kind); + libafl_exit_request_custom_insn(cpu, (target_ulong)pc, + (enum libafl_custom_insn_kind)kind); } diff --git a/libafl/tcg.c b/libafl/tcg.c index a8ac8aaa25..da66e6543c 100644 --- a/libafl/tcg.c +++ b/libafl/tcg.c @@ -19,12 +19,11 @@ static target_ulong reverse_bits(target_ulong num) target_ulong reverse_num = num; num >>= 1; - while(num) - { - reverse_num <<= 1; - reverse_num |= num & 1; - num >>= 1; - count--; + while (num) { + reverse_num <<= 1; + reverse_num |= num & 1; + num >>= 1; + count--; } reverse_num <<= count; return reverse_num; @@ -34,9 +33,9 @@ static target_ulong reverse_bits(target_ulong num) * Isolate the portion of code gen which can setjmp/longjmp. * Return the size of the generated code, or negative on error. */ -static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, - vaddr pc, void *host_pc, - int *max_insns, int64_t *ti) +static int libafl_setjmp_gen_code(CPUArchState* env, TranslationBlock* tb, + vaddr pc, void* host_pc, int* max_insns, + int64_t* ti) { int ret = sigsetjmp(tcg_ctx->jmp_trans, 0); if (unlikely(ret != 0)) { @@ -48,7 +47,8 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, tcg_ctx->cpu = env_cpu(env); // -- start gen_intermediate_code - const int num_insns = 1; // do "as-if" we were translating a single target instruction + const int num_insns = + 1; // do "as-if" we were translating a single target instruction #ifndef TARGET_INSN_START_EXTRA_WORDS tcg_gen_insn_start(pc); @@ -66,10 +66,13 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, tcg_gen_goto_tb(0); tcg_gen_exit_tb(tb, 0); - // This is obviously wrong, but it is required that the number / size of target instruction translated - // is at least 1. For now, we make it so that no problem occurs later on. - tb->icount = num_insns; // number of target instructions translated in the TB. - tb->size = num_insns; // size (in target bytes) of target instructions translated in the TB. + // This is obviously wrong, but it is required that the number / size of + // target instruction translated is at least 1. For now, we make it so that + // no problem occurs later on. + tb->icount = + num_insns; // number of target instructions translated in the TB. + tb->size = num_insns; // size (in target bytes) of target instructions + // translated in the TB. // -- end gen_intermediate_code assert(tb->size != 0); @@ -80,18 +83,18 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, } /* Called with mmap_lock held for user mode emulation. */ -TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, +TranslationBlock* libafl_gen_edge(CPUState* cpu, target_ulong src_block, target_ulong dst_block, int exit_n, target_ulong cs_base, uint32_t flags, int cflags) { - CPUArchState *env = cpu_env(cpu); - TranslationBlock *tb; + CPUArchState* env = cpu_env(cpu); + TranslationBlock* tb; tb_page_addr_t phys_pc; - tcg_insn_unit *gen_code_buf; + tcg_insn_unit* gen_code_buf; int gen_code_size, search_size, max_insns; int64_t ti; - void *host_pc; + void* host_pc; // edge hooks generation callbacks // early check if it should be skipped or not @@ -106,7 +109,8 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, assert_memory_lock(); qemu_thread_jit_write(); - // TODO: this (get_page_addr_code_hostp) is a bottleneck in systemmode, investigate why + // TODO: this (get_page_addr_code_hostp) is a bottleneck in systemmode, + // investigate why phys_pc = get_page_addr_code_hostp(env, src_block, &host_pc); phys_pc ^= reverse_bits((tb_page_addr_t)exit_n); @@ -125,7 +129,7 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, } QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); - buffer_overflow: +buffer_overflow: assert_no_pages_locked(); tb = tcg_tb_alloc(tcg_ctx); if (unlikely(!tb)) { @@ -167,77 +171,80 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, tcg_ctx->guest_mo = TCG_MO_ALL; #endif - restart_translate: +restart_translate: trace_translate_block(tb, pc, tb->tc.ptr); - gen_code_size = libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); + gen_code_size = + libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); if (unlikely(gen_code_size < 0)) { switch (gen_code_size) { - case -1: - /* - * Overflow of code_gen_buffer, or the current slice of it. - * - * TODO: We don't need to re-do gen_intermediate_code, nor - * should we re-do the tcg optimization currently hidden - * inside tcg_gen_code. All that should be required is to - * flush the TBs, allocate a new TB, re-initialize it per - * above, and re-do the actual code generation. - */ - qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, - "Restarting code generation for " - "code_gen_buffer overflow\n"); - tb_unlock_pages(tb); - tcg_ctx->gen_tb = NULL; - goto buffer_overflow; + case -1: + /* + * Overflow of code_gen_buffer, or the current slice of it. + * + * TODO: We don't need to re-do gen_intermediate_code, nor + * should we re-do the tcg optimization currently hidden + * inside tcg_gen_code. All that should be required is to + * flush the TBs, allocate a new TB, re-initialize it per + * above, and re-do the actual code generation. + */ + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation for " + "code_gen_buffer overflow\n"); + tb_unlock_pages(tb); + tcg_ctx->gen_tb = NULL; + goto buffer_overflow; - case -2: - assert(false && "This should never happen for edge code. There must be a bug."); - /* - * The code generated for the TranslationBlock is too large. - * The maximum size allowed by the unwind info is 64k. - * There may be stricter constraints from relocations - * in the tcg backend. - * - * Try again with half as many insns as we attempted this time. - * If a single insn overflows, there's a bug somewhere... - */ - assert(max_insns > 1); - max_insns /= 2; - qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, - "Restarting code generation with " - "smaller translation block (max %d insns)\n", - max_insns); + case -2: + assert( + false && + "This should never happen for edge code. There must be a bug."); + /* + * The code generated for the TranslationBlock is too large. + * The maximum size allowed by the unwind info is 64k. + * There may be stricter constraints from relocations + * in the tcg backend. + * + * Try again with half as many insns as we attempted this time. + * If a single insn overflows, there's a bug somewhere... + */ + assert(max_insns > 1); + max_insns /= 2; + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation with " + "smaller translation block (max %d insns)\n", + max_insns); - /* - * The half-sized TB may not cross pages. - * TODO: Fix all targets that cross pages except with - * the first insn, at which point this can't be reached. - */ - // phys_p2 = tb_page_addr1(tb); - // if (unlikely(phys_p2 != -1)) { - // tb_unlock_page1(phys_pc, phys_p2); - // tb_set_page_addr1(tb, -1); - // } - goto restart_translate; + /* + * The half-sized TB may not cross pages. + * TODO: Fix all targets that cross pages except with + * the first insn, at which point this can't be reached. + */ + // phys_p2 = tb_page_addr1(tb); + // if (unlikely(phys_p2 != -1)) { + // tb_unlock_page1(phys_pc, phys_p2); + // tb_set_page_addr1(tb, -1); + // } + goto restart_translate; - case -3: - /* - * We had a page lock ordering problem. In order to avoid - * deadlock we had to drop the lock on page0, which means - * that everything we translated so far is compromised. - * Restart with locks held on both pages. - */ - qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, - "Restarting code generation with re-locked pages"); - goto restart_translate; + case -3: + /* + * We had a page lock ordering problem. In order to avoid + * deadlock we had to drop the lock on page0, which means + * that everything we translated so far is compromised. + * Restart with locks held on both pages. + */ + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation with re-locked pages"); + goto restart_translate; - default: - g_assert_not_reached(); + default: + g_assert_not_reached(); } } tcg_ctx->gen_tb = NULL; - search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); + search_size = encode_search(tb, (void*)gen_code_buf + gen_code_size); if (unlikely(search_size < 0)) { tb_unlock_pages(tb); goto buffer_overflow; @@ -250,9 +257,10 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, */ perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf)); - qatomic_set(&tcg_ctx->code_gen_ptr, (void *) - ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, - CODE_GEN_ALIGN)); + qatomic_set( + &tcg_ctx->code_gen_ptr, + (void*)ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, + CODE_GEN_ALIGN)); /* init jump list */ qemu_spin_init(&tb->jmp_lock); From e0b8df96550eeab77900993e8bc081a7a35fd4a8 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 30 Apr 2025 13:47:55 +0200 Subject: [PATCH 2884/2892] fix systemmode compilation --- accel/tcg/cpu-exec.c | 1 + accel/tcg/tcg-accel-ops-mttcg.c | 13 +++++++------ accel/tcg/tcg-accel-ops-rr.c | 12 ++++++------ include/libafl/defs.h | 3 +++ include/libafl/exit.h | 4 +--- include/libafl/system.h | 3 --- include/libafl/syx-snapshot/syx-snapshot.h | 5 ----- libafl/cpu.c | 2 +- libafl/exit.c | 5 +++++ libafl/qemu_snapshot.c | 15 ++++++++------- libafl/system.c | 6 +++++- libafl/syx-snapshot/syx-cow-cache.c | 5 +++-- libafl/syx-snapshot/syx-snapshot.c | 4 +--- 13 files changed, 41 insertions(+), 37 deletions(-) create mode 100644 include/libafl/defs.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 6403eb1191..ff7aeecc1e 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -47,6 +47,7 @@ //// --- Begin LibAFL code --- +#include "libafl/defs.h" #include "libafl/exit.h" #include "libafl/tcg.h" #include "libafl/hooks/tcg/edge.h" diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index ebc5d29394..ba81363c45 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -35,6 +35,13 @@ #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" +//// --- Begin LibAFL code --- + +#include "libafl/defs.h" + +//// --- End LibAFL code --- + + typedef struct MttcgForceRcuNotifier { Notifier notifier; CPUState *cpu; @@ -55,12 +62,6 @@ static void mttcg_force_rcu(Notifier *notify, void *data) async_run_on_cpu(cpu, do_nothing, RUN_ON_CPU_NULL); } -//// --- Begin LibAFL code --- - -#include "libafl/exit.h" - -//// --- End LibAFL code --- - /* * In the multi-threaded case each vCPU has its own thread. The TLS * variable current_cpu can be used deep in the code to find the diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 465151cbc3..92f6e5ba7f 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -37,6 +37,12 @@ #include "tcg-accel-ops-rr.h" #include "tcg-accel-ops-icount.h" +//// --- Begin LibAFL code --- + +#include "libafl/defs.h" + +//// --- End LibAFL code --- + /* Kick all RR vCPUs */ void rr_kick_vcpu_thread(CPUState *unused) { @@ -169,12 +175,6 @@ static int rr_cpu_count(void) return cpu_count; } -//// --- Begin LibAFL code --- - -#include "libafl/exit.h" - -//// --- End LibAFL code --- - /* * In the single-threaded case each vCPU is simulated in turn. If * there is more than a single vCPU we create a simple timer to kick diff --git a/include/libafl/defs.h b/include/libafl/defs.h new file mode 100644 index 0000000000..b0b0d04dcb --- /dev/null +++ b/include/libafl/defs.h @@ -0,0 +1,3 @@ +#pragma once + +#define EXCP_LIBAFL_EXIT 0xf4775747 diff --git a/include/libafl/exit.h b/include/libafl/exit.h index 5af1be547b..5ad5ddac68 100644 --- a/include/libafl/exit.h +++ b/include/libafl/exit.h @@ -1,10 +1,8 @@ #pragma once #include "qemu/osdep.h" -#include "exec/cpu_ldst.h" #include "hw/core/cpu.h" - -#define EXCP_LIBAFL_EXIT 0xf4775747 +#include "exec/cpu-defs.h" struct libafl_breakpoint { target_ulong addr; diff --git a/include/libafl/system.h b/include/libafl/system.h index 9a6e9c6d4d..6e69ebf430 100644 --- a/include/libafl/system.h +++ b/include/libafl/system.h @@ -1,9 +1,6 @@ #pragma once #include "hw/core/cpu.h" -#include "gdbstub/enums.h" -#include "sysemu/accel-ops.h" -#include "sysemu/cpus.h" int libafl_qemu_set_hw_breakpoint(vaddr addr); int libafl_qemu_remove_hw_breakpoint(vaddr addr); diff --git a/include/libafl/syx-snapshot/syx-snapshot.h b/include/libafl/syx-snapshot/syx-snapshot.h index 5a6cbbc85c..fd021f4691 100644 --- a/include/libafl/syx-snapshot/syx-snapshot.h +++ b/include/libafl/syx-snapshot/syx-snapshot.h @@ -10,14 +10,9 @@ #include "qemu/osdep.h" -#include "qom/object.h" -#include "sysemu/sysemu.h" - #include "device-save.h" #include "syx-cow-cache.h" -#include "libafl/syx-misc.h" - #define SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE 64 #define SYX_SNAPSHOT_COW_CACHE_DEFAULT_MAX_BLOCKS (1024 * 1024) diff --git a/libafl/cpu.c b/libafl/cpu.c index 409fcb3aab..112e15a7ca 100644 --- a/libafl/cpu.c +++ b/libafl/cpu.c @@ -11,7 +11,7 @@ #include "hw/core/sysemu-cpu-ops.h" #include "libafl/cpu.h" - +#include "libafl/exit.h" #include "libafl/hook.h" int gdb_write_register(CPUState* cpu, uint8_t* mem_buf, int reg); diff --git a/libafl/exit.c b/libafl/exit.c index 2a6efeaf6d..7dfbcf9712 100644 --- a/libafl/exit.c +++ b/libafl/exit.c @@ -4,8 +4,13 @@ #include "tcg/tcg-op.h" #include "cpu.h" +#include "libafl/defs.h" #include "libafl/cpu.h" +#ifndef CONFIG_USER_ONLY +#include "system/runstate.h" +#endif + #ifdef CONFIG_USER_ONLY #define THREAD_MODIFIER __thread #else diff --git a/libafl/qemu_snapshot.c b/libafl/qemu_snapshot.c index b742465fa6..190f701c93 100644 --- a/libafl/qemu_snapshot.c +++ b/libafl/qemu_snapshot.c @@ -1,14 +1,15 @@ -#include "libafl/qemu_snapshot.h" - -#include "sysemu/runstate.h" -#include "migration/snapshot.h" +#include "qemu/osdep.h" #include "qapi/error.h" + +#include +#include + +#include "migration/snapshot.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" -#include -#include +#include "system/runstate.h" +#include "libafl/qemu_snapshot.h" static void save_snapshot_cb(void* opaque) { diff --git a/libafl/system.c b/libafl/system.c index 66b8e38e1f..09a9a2d383 100644 --- a/libafl/system.c +++ b/libafl/system.c @@ -1,5 +1,9 @@ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "exec/vaddr.h" +#include "system/system.h" +#include "system/accel-ops.h" +#include "system/cpus.h" +#include "gdbstub/enums.h" #include "libafl/system.h" diff --git a/libafl/syx-snapshot/syx-cow-cache.c b/libafl/syx-snapshot/syx-cow-cache.c index 10f5de0fe2..f89fa4832a 100644 --- a/libafl/syx-snapshot/syx-cow-cache.c +++ b/libafl/syx-snapshot/syx-cow-cache.c @@ -1,6 +1,7 @@ -#include "libafl/syx-snapshot/syx-cow-cache.h" +#include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "libafl/syx-snapshot/syx-cow-cache.h" +#include "system/block-backend-io.h" #define IS_POWER_OF_TWO(x) ((x != 0) && ((x & (x - 1)) == 0)) diff --git a/libafl/syx-snapshot/syx-snapshot.c b/libafl/syx-snapshot/syx-snapshot.c index 7eb4ad79e8..a9f3f60b52 100644 --- a/libafl/syx-snapshot/syx-snapshot.c +++ b/libafl/syx-snapshot/syx-snapshot.c @@ -1,16 +1,14 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "sysemu/sysemu.h" -#include "migration/vmstate.h" #include "cpu.h" #include "exec/ramlist.h" #include "exec/ram_addr.h" -#include "exec/exec-all.h" #include "libafl/syx-snapshot/syx-snapshot.h" #include "libafl/syx-snapshot/device-save.h" +#include "libafl/syx-misc.h" #define SYX_SNAPSHOT_LIST_INIT_SIZE 4096 #define SYX_SNAPSHOT_LIST_GROW_FACTOR 2 From a7e9d70e141d3db44ca1916bb277857976a1b63c Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 30 Apr 2025 13:51:55 +0200 Subject: [PATCH 2885/2892] forgot --- include/libafl/exit.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/libafl/exit.h b/include/libafl/exit.h index 5ad5ddac68..86ee3a3436 100644 --- a/include/libafl/exit.h +++ b/include/libafl/exit.h @@ -4,6 +4,8 @@ #include "hw/core/cpu.h" #include "exec/cpu-defs.h" +#include "libafl/defs.h" + struct libafl_breakpoint { target_ulong addr; struct libafl_breakpoint* next; From d744fc4f425fbf8f14fa80afdbdc9fec705588ec Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 11:13:43 +0200 Subject: [PATCH 2886/2892] adapt to new decoder --- target/arm/tcg/translate-a64.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 788cdc2e72..35cef7bd22 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8370,8 +8370,8 @@ static bool do_addsub_ext(DisasContext *s, arg_addsub_ext *a, //// --- Begin LibAFL code --- - if (rd == 31 && sub_op) // cmp xX, xY - libafl_gen_cmp(s->pc_curr, tcg_rn, tcg_rm, sf ? MO_64 : MO_32); + if (a->rd == 31 && sub_op) // cmp xX, xY + libafl_gen_cmp(s->pc_curr, tcg_rn, tcg_rm, a->sf ? MO_64 : MO_32); //// --- End LibAFL code --- @@ -8420,8 +8420,8 @@ static bool do_addsub_reg(DisasContext *s, arg_addsub_shift *a, //// --- Begin LibAFL code --- - if (rd == 31 && sub_op) // cmp xX, xY - libafl_gen_cmp(s->pc_curr, tcg_rn, tcg_rm, sf ? MO_64 : MO_32); + if (a->rd == 31 && sub_op) // cmp xX, xY + libafl_gen_cmp(s->pc_curr, tcg_rn, tcg_rm, a->sf ? MO_64 : MO_32); //// --- End LibAFL code --- @@ -8616,7 +8616,7 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) //// --- Begin LibAFL code --- - libafl_gen_cmp(s->pc_curr, tcg_rn, tcg_y, sf ? MO_64 : MO_32); + libafl_gen_cmp(s->pc_curr, tcg_rn, tcg_y, a->sf ? MO_64 : MO_32); //// --- End LibAFL code --- From 94ef85a39abb4d2f216424a727c2742cfb631252 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 11:26:24 +0200 Subject: [PATCH 2887/2892] enable manual action trigger --- .github/workflows/build_and_test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index dd6fb06d7b..1339d82868 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -5,6 +5,7 @@ on: branches: [ main ] pull_request: branches: [ main ] + workflow_dispatch: env: CARGO_TERM_COLOR: always From 92e59e05877f74dbcc57b91bb5ea8750c85b7378 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 13:19:44 +0200 Subject: [PATCH 2888/2892] forgot include --- linux-user/aarch64/cpu_loop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 06224888ac..07d9860037 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -27,6 +27,10 @@ #include "target/arm/syndrome.h" #include "target/arm/cpu-features.h" +//// --- Begin LibAFL code --- +#include "libafl/exit.h" +//// --- End LibAFL code --- + /* AArch64 main loop */ void cpu_loop(CPUARMState *env) { From 4b35c57b0886be50e6740b7b5ea327344de84657 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 14:34:51 +0200 Subject: [PATCH 2889/2892] forgot include --- linux-user/arm/cpu_loop.c | 6 ++++++ linux-user/hexagon/cpu_loop.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 773e95f415..1b9ebad0c5 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -28,6 +28,12 @@ #include "user/page-protection.h" #include "target/arm/syndrome.h" +//// --- Begin LibAFL code --- +/// +#include "libafl/exit.h" + +//// --- End LibAFL code --- + #define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ if (!__r && bswap_code(arm_sctlr_b(env))) { \ diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 696f70591e..f3fe881442 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -25,6 +25,12 @@ #include "signal-common.h" #include "internal.h" +//// --- Begin LibAFL code --- +/// +#include "libafl/exit.h" + +//// --- End LibAFL code --- + void cpu_loop(CPUHexagonState *env) { CPUState *cs = env_cpu(env); From d4a6db3260152317b8d2fed5db9ad1626f474f50 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 14:36:38 +0200 Subject: [PATCH 2890/2892] forgot include --- linux-user/aarch64/cpu_loop.c | 2 ++ linux-user/arm/cpu_loop.c | 2 +- linux-user/mips/cpu_loop.c | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 07d9860037..7892f2ee7d 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -28,7 +28,9 @@ #include "target/arm/cpu-features.h" //// --- Begin LibAFL code --- + #include "libafl/exit.h" + //// --- End LibAFL code --- /* AArch64 main loop */ diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 1b9ebad0c5..a4b9c85926 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -29,7 +29,7 @@ #include "target/arm/syndrome.h" //// --- Begin LibAFL code --- -/// + #include "libafl/exit.h" //// --- End LibAFL code --- diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 93df555d07..e350286e92 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -26,6 +26,12 @@ #include "internal.h" #include "fpu_helper.h" +//// --- Begin LibAFL code --- + +#include "libafl/exit.h" + +//// --- End LibAFL code --- + # ifdef TARGET_ABI_MIPSO32 # define MIPS_SYSCALL_NUMBER_UNUSED -1 static const int8_t mips_syscall_args[] = { From 5feca574fc1beee0a9bf0768e7b63c1c8f39930b Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 14:43:34 +0200 Subject: [PATCH 2891/2892] forgot include --- linux-user/ppc/cpu_loop.c | 6 ++++++ linux-user/riscv/cpu_loop.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 78a397219c..0ad95b68a9 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -24,6 +24,12 @@ #include "user/cpu_loop.h" #include "signal-common.h" +//// --- Begin LibAFL code --- + +#include "libafl/exit.h" + +//// --- End LibAFL code --- + static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) { return cpu_get_host_ticks(); diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 20d617e56f..4e98929e71 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -26,6 +26,12 @@ #include "elf.h" #include "semihosting/common-semi.h" +//// --- Begin LibAFL code --- + +#include "libafl/exit.h" + +//// --- End LibAFL code --- + void cpu_loop(CPURISCVState *env) { CPUState *cs = env_cpu(env); From 13757ee0e957747b3d891e961e754d2885ee6432 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Fri, 2 May 2025 19:43:45 +0200 Subject: [PATCH 2892/2892] expose main symbol --- system/main.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/system/main.c b/system/main.c index 7b56454025..8ca5dbb554 100644 --- a/system/main.c +++ b/system/main.c @@ -41,20 +41,6 @@ #include #endif -static void *qemu_default_main(void *opaque) -{ - int status; - - replay_mutex_lock(); - bql_lock(); - status = qemu_main_loop(); - qemu_cleanup(status); - bql_unlock(); - replay_mutex_unlock(); - - exit(status); -} - int (*qemu_main)(void); #ifdef CONFIG_DARWIN @@ -69,6 +55,21 @@ int (*qemu_main)(void) = os_darwin_cfrunloop_main; //// --- Begin LibAFL code --- #ifndef AS_LIB //// --- End LibAFL code --- + +static void *qemu_default_main(void *opaque) +{ + int status; + + replay_mutex_lock(); + bql_lock(); + status = qemu_main_loop(); + qemu_cleanup(status); + bql_unlock(); + replay_mutex_unlock(); + + exit(status); +} + int main(int argc, char **argv) { qemu_init(argc, argv); @@ -84,6 +85,7 @@ int main(int argc, char **argv) g_assert_not_reached(); } } + //// --- Begin LibAFL code --- #endif //// --- End LibAFL code ---

J?YPiMnGS17yY-ItMhR4j9sWbw|kg>n0LS*6SWg89p0ntoH$0D)Tv@ zGqf-nMdpK+xZ;UX8psw|(^2E#0X|RNR-}J$c-Vak+8|LN9rU}0Sl-XmZ!fa9y*mfA z!D{|I-u;X@84f=yOBK?Ffw?@&R@P(=c!$3umFxs!VP}bfBk_vl5a{yCvLq-1AQ1E` z;*3g^vHAE8jkC-A-+f$LTV7dj%_zZb9>;jaOu4gLJ64HbuV=t-jWLG7KWgeYc817s z>OtOL6Zh1!c3CRFu}oN|U5#a4Z-mEe-!kmK%-+>*@K&&<57Q++^#Y%XLFDQd2}JlB z>~|`%xt-zpF`2Hi@i)TxUj=PMDJAhl#RtQ$WpM4t*+oKM)D3dZTOuQ@`V;ByL?e5< z2fw6lqI*bMQ9#s8R25Q^GRUM9S{jamWYv_4uA{=0oxN_>ed}Uq$dH9MgLUYI)5D{q~{lKhg@&Z`9qxl$-giZl`xUkPE(vxME zn_Ol6UVE^M2hVgW>L!qG5}R>gG>Ooc#PXR~w-Wpoq;V#6j-##;j1yloGZn~lQy$j9 zId;t;K@nhn@)Aoxw4!SzqPy#{>~l^1I5`QNAe)NqAALqK?odRP3*^~#WKkqpp}Y%( z$_pmwST|ZL*hceEG$s5zYZ8A4|CfoL?U^7mFD7 zsiu)~^)nXL$6fBeiz=toin6RUR@}xea>?t|GErihk674a(IzIw>^*{3zjP*+eBBpC z(37bxa8StB8!WCBGkdKEQchYtYRD7oYMCDBK^?s)N(5pt9mscFbm}E=mZiHU{+5s~ z4taX2u4j#y)pZr2Rib-s8_n8D`+I%e7aN-G#=DFRLs zf*G5tI`CJkQ^AWFc(oKl`=Je$aQP0El8Edc{67VmBO;| z{EouLm_9R)7#K2aN#Rp@q?U13Gn@obF=-@<0ql&MeuO}O&wfJBIx+qGM2d4Z5p^3r z(I~W@jb6wN$6tRPo#7MyY*bTp$D-Mjb^-SgMF!AH1${Nt2bcTLn3zcGjvK(&W3#ZR zRlqrm5hCw-$1$kQ_pvM=w8`quwInt8OqQfriQLwNsmu)`GZ)9xLV?0qs}gv=`SbnB z<>2n#GIVlT$JBy+R9JzAFSo_x@t`8&v;uDydqPU)t>+!f8qF%ty@z~C0}#DUs#M;R`?ljv5u@p(2!F!!-kgHZe3f9W}XjHl;MS2RWL3Yl0RsJ80fL zHmL>j`>3t5M$pROkJkI-iAB(&x+zsu`hwtS-i7xNAWdQSIxi#OEs^lpG7MEWcvv!KuXW6r?MLzCvgJUb|u=C$g z0{y<#ZooQ0zvF-UE&7Fuep9hX^_O>oyyvfHS>T7FAb2%SAbGX7WPqo#RQ#srW&P9Q zACBL=f)K!)x0g3Jx3U4?=Y!jwO_J7NP*q+@A8Mu($|W6jOmqM;M%xl&h4k-dN?kHR z!UJ^lpbY-B;o5(A%SJoK&phb<8`*zO*5Ef>7*Y~_g~_RD$DyW~CDati2U)txK5hBn z4RO06+02QKy|~ylZ8+?nn=0TGIu)e8Gj{aFtvt1C9dl|WXSAm~zf~M%1heniK90PT zc)ccAacIeug0s75d#tKOR4d9#-KF5Z2oLKT%;_zWncu^?07t<`*V=>iG?Ec#8M^ z5VB3Pkjq z(GtXuL@dyiUFF|_X-`B#-jbXuSMAj$e*{ zAIT~6<>>nK^5INS^zyS^Wq#&9Gq6BX1Wt*+2LKGdwBj%XKHx4m_#JVcMwRQFj@ljf z>{_^B`)M{fLV`X@Pv8VAY!pa+j0^|NK}#G$>v541wX<~M3B-fuWMZDOw(nN)DjZ32 z+pbJtPZZU}#!*8Ah zFSiG&0g5+_Qi2sBvJY`F`eWsrb8~qeC+PHX*)w36SH|)EnEXM>giHue11dOG=j?{+ zYB?$J3lP+|n4+JD?npi=mkM)j<1jJ z&YP#VSL$TzC(&D*c+FGy?&f5;xVyN2m>C%F2tSKG{+W@}vpd}TU?q-86hS(d&vr_> z>X}m+LBZS0n-gsA_xDL1!2aFLqH2=3RA>VBw;^njCrrQvPK}OUdy*OX3bjO_Te-uK z(73?z!Z*mUSo_pPYKfCjGH*nJp6S>#j>ft#q4iK|}o6`|k#YIs; zJCCowY5e(0x!7L?`tM}pA=f+zvM_IZN0z-jF0B}e3zf@+Aff;J{hrH>^C~L<0de!s zpN{&2?(Pm@L%K3bI+Ib78dMmW?_kaf7y}}#z8hX1KZ+AkfF!-pLP?AdzaXuM#Rp9JkWKvU;xy*f z6`Hm`QXkQ>D%~T5)x-Ju2(E_PDrf7>!=19%!W#x!NJkqK(6VX zgLY@&ra>`hjYW54f>`lN$=<`Y1rmWpB!sc$;8X7HA;Ez1na{@$Z76^Wt#GBv5|plh zOz{H@X27-P=daDYH9{&jaJ2k_Kwl9#i7{tGLG~~>`dha%IP5YSw3-c-n5XX(1B3@= zsafXeA|_>dAflwE8D0?k&O=TAg|{uDC8qDpKXP5Bk zpVSNg*6VkteG;@eKu#+s=g7KmJKIy66VaJ$&yW?TWxELwzR7mGZd|a%%AsIF;fbkJ z%qD}h!R+t$+sQua$bNu1iMxr+bcz&CsOw~U(supKzb>z|D2^8KMYU|+IF<%6g_u7e**eY#9|Xs;nnTK$o{CermNL} z>~rqao}Ui41PIXP5L54+AWD|@qQuYTGR91J-WI;gQmeJ}_T(j#@Fd*Y;$t0ToCn*b zRx^wgp&CzrAHYDLNlggnKDc`vL|JltK05`S>>)6DgL^`CKE{{MOPAjPARP1>Ol@OV zy217RW#gJKK&&LwW5Q?KFCv@)`JLMcwz?6nf7Jd(d(BJ%wMA;7FlMHney)n;VM(A(BKSbyQN&8f|3V9Zk!+QREV`5_#e;W4%gc{JS$y>YneLJQml) z3@b`i%}X*>68;I|Lz0xp$Lm+=q8(as=9{*3cd>Z4R_3`yRACrgFZ%7n{Y<7Wb^$5z z$V&3-i@in!sCZqrw#**L2DGO$lgh&#T}zNl$_yafE2KR;ME-7qbO$zx|M>U{QI-0e zte!%E=b1b-RW94J`U=rGhn2-IO^_`&pNN!d5s2GSh2}VlyB5~So}K&J;NY37(aS{2 z)-ik!*J+c&LUzl5Q9l@fWJtvS$DqcVIt~H0=#mV_IcqzR>3f2X>B!9pRpQ?cZkp3S zFV_`a_M*#hO?5@X=T=?OmKSYJDH=Yn)fHXwqH9x%hR>^YMOVG(>Xf44^GaROH7~j{ zrKmn1X@+fX?e}u>yOsQ2Nq(;;zt_TVi5|TcSg7!;{cHFk-Y5J#*x!E-t>C=KJ}Kc` z14ZHW7}7MfFv@X#nacVPOjP{`rlI}=gFIxACG?^CiFedI@s3I--cjGD{$za<@2GF$ z9raDTqdv(a)X>tRW@?d0&YV%Wz1TUmq%X@;zl1Wt5XX8y1x{ZDDC#}z4f?xp;jwp< zdMDo3z#mS5xA(O@UrtJt$Hc8wqvbg6FSa#UPV@eeylawVm*#rk(Jmrk(Jmrkya-n(>4$HSL5i zHSJ8Fx0R{m314d3314d3314c)vpTh%@TI1m@TI1m@TI1mwW;ldFE#CiFE#CiFE#DF zn%YkIQqxZOQqxZOlD1r?rQ)n#*9^M1L3JRxbI({B4cV)C`ZIMzU0^~HN5*Lh){pqGBt zOm$T*Bp`$!H=%+{NB4RyZVge_!jDoINZ;_IOfxN(*g|||Z@8JAxDsZHW|52xUR{Mn z>kY-li|q?6D+a^SG`q~qeYPuKnJ=v}eB3;By)E&semyWP!sGa|Nwj8Tp7R%G@fkV$ z*h{Wd>m)WR_n>2Uu_%xe7wXMPmVAtwG{35o_C&mExaPU6*LX@ebIWK~mRljUiptdL zfK-nr$5_&dkIKXzW6qt=`id^EJQTOJR0qvpvi$KHPc)fzIZHmA5I{sjO``H%p2?=b zr@@2|u9>Zw&W+WLN*~@|OQ+p{wLBO%-^fT$KW$eR+?#t)~;h^0a7qcC6HIUH540JJZDeryK?V%HTxR-+9Wcc}* z!U$8wM7hGPKxW4qT5A{XnXt%!w5myk%yW3kd}Iz9_aBT;d6cbv(_?cK?ksOE}U z?^w8*Uri&7&9+&A9en6*DH5_UwtR2Tm4h~uo48`$bAbmd$bi9?Bvj-vxizeyll}_8 z@&tfM5VH%{h>ARxinnjB&qo(->~07f^kxVNqU(D;QbIiXyCw<|5SCRh-am(0F`} zy?C()eHOS$#yJjl7)cFVA<5N|X43=2PJxc|ZYu2%^Nu+UWGVY|{Dr~Jwc_R3shB9Q zbF9_3qzBXsW^KKBpDW9Y`N-dbLRvs=arU5?hh(=SC?uL*GJw*zfHR>Dp9!htlU7W7 zD-_3g7!*-g>5n4G5z>LW))k2@YqoI-d~7D^v%nEiSHvyTdg;%uO)Y$d;k0!Jsl($~xu{jq}oar9K(y1#zHcZsF)@YN1lYMzn|3iJ4Z? z{h;EJpxH7ZCEg$-l7+na2(hymhIIQM55(CoQ8=sG6*LoDdGX3JiW@>xA}YjIGqIRMn#^{LLB<$_9GY zIDXWLQ*=mx7H~2#!ZCjV>w*prg1UoLyMoz5K3GH6id^}e00{WruB=u2SsO*TWh&Oj=S}##`BTbro$)xlh4KtjQ;_b^*5)&e}4u`&FeDsK8EZ21w5+8&|ltP$VB?<$BSbj~!79 zD2wvEO0&)4ngu6k=SOIwylvL-mF6OqZzICc_V^`GrIn7S(k_T!V18Q9 zYtr*DhntyvGSFaqs`hgTw#ZYAV0+lb`a@1%uvGY>S%JUZJYZJF9=#m>K4dSzP;<#8 z-U-#{IxMY{3^?q)EHmyf5mJhA%#fK=8H@bSNJnI>a6fK7dLk?N^2}kdaAowxxrm38 z;;}qa3O$>SaG8uL*5SVNIoxG1_}lO9cH=3Rv|~gg46i@{qYN|4X&=kl+9ZrQDZm`hncXqcDa`chHssoQb@=%t)w0FIijqhH?7pfSYW(m)pVB(UX*)s?8>+J zfb~uV+F;XBzDDPqKChC*Hhq3Y8k+QZ9SMMSE3Wzki!jj_PZLqI5VbS7v0_PG<2;1M zj?FD39urPgy&K7wU_NQl1wc+4`hY;pmrA4U75Q#~{~zH4{jD&+gGg5<+i8qkDntz@ zKD%E)e<|6$WxNk1x_5YJVrFbzuXNB_+_ktCvh_6Neo=B7142~NNsi|fvO;=)29Xl? z89th+D0d@`TDwsD_Qn;| zm{?Efm$C4+JtLoQI28REm?IOeCFeJbmtri4k5MFx6e>CJCh5n$Zuj`ex7PTU%b6XZ z?h-{MBr!6}nl1*3I7f-V`8S=d{;Ak*NEZa8X)%*V6X>O*C|5!Ci>x7x8!6e6!E7$$ z*3*8s+kG~IYm#SN-kso~WeWc~msH7H6X?&_vaw-8p<6!y0WCseysav$Kpp{^OZkulWb^I3^5~;1&&hW zR8|iArt@=76~eJgv|-sfFdf1+t${^$=hKt8?I2H6Ar^RP}mt+Q1Bw9#d&GZo)7n4)XE^&1G|Px=Ai z)yAz^v<$C@cNgC#YJ0V8=Sr#2E5d8MjH~!E`dYk%2Vp(NhSsBN`HT<3h#K9Gy*?== z`xbsqPj9vkNT+_SpSy?l`RK6MY4pjt$NfMrFr?R?t!}^He%H_+J$8GCZT4zB##$vF8ti@>^Jt^r>FhiVC)ay5BH8J((fGfJ1WO*rTm z+fmm6vjY#N^TdA{<0G6&EU&&CkIA2uzaT$K<;{j(Q~~T+fH*sWHv9r#R$X#o56RoL zrN#&Kt+1%iNq@My$}&t0tmwQ&zB615mBNmwA@0C;SZ8rmcx?rN$ZIRu>S)`picg^g zGVTw9$)@3kRBuMv`Uxhg;=Riiw%jSCXah;L*UXmaRK zMSN_i-saCdHg_6p*vaD3u`M{^tBEkmGtHHt|MU*E>sJ$B9-;1L z(suI+VFK80m{ogPzDhfPdJk1|4TXHCQIw(Sf}#wO4*iBSmkB(}xa-w5N>aPsT#isK zxHPk%WrnAR>JN2rmN4!FNjRqwnXYihJ?RxRFJ681igbS&R48+ z;NEh9Yn;;3ZvVi@mtB=Atpr3Vk`*?F$MBs_$t(#sq!&%vG!WQFMEUpNQ9u33uRy2+1p!Y_Wgh_D!4 zY4Bv%C(os)*oV5ji9_YZLa1WMGQBIS zua-*a8A7c^^juwq3s+AX)Dg#>QobIMNf9J*GCYp19>(AUpIzdW;mu*aYp z#wI|b!I3v~Wlt=bWGgnhvf5e?ZAnWQuOSdHLv~J#h;#a!y!JSRArYkC#>B?eZ2W>7 z)E*iW(-aBf#W6uO?lO@LBRkq9N$s^2O(~kh^)U5UGUcl+a%Xy%j6`NvBmA#9SJ4uF z(;)}@ZT_wF1Dsrd5{$_*EsAYS)pJQqEHSlc1e%|8{WcJhK9UX1xa@pBmr&g$Jy=PS zt{cX!ayZvaQW9xHbE4^RqVrOnu{oNcJ8Mg?LRBB+2uy-$ltqh+D>@Ow{w{vS87)9p z7Z+bo0Zj++IwUWy5Y2vR9pP|LoCOxtMDZ}sc7DZnOLU=^+oYg3vjacIbqj!zwHuqo5J2~Mm& zW_-24fn1kD7zJ)fHG?XkrC>Dso*Jl1M>G@Fo}4_Ko#DL3iV#_ILF+uwX{)y{oag)e z)!v}g2rrznkB~5Y9-ZHN8e(of%1CcBy684!(eA+eg$Rq7Ey0e%L$6y2V@^jL$r2dp zy;ew(eL^_YIQqy>Oeu6jls`$Fq5{+#En)XoZmHr5GR+1Iolq?7kTRdLj`pag8ayjK zVy<)J>^Dmo;*b)p4Qvwaift1oiySx?zv;$}v_W8wWIJzQsZe@cO&?cRmQ3Cs3%8aC zuUyl$cOTstfn`9SfT~&a3Ixn=`A8x|@>3`*AAH<#ic7Qg?xRBgShS8HEugMguWZmR zk8cSD`VDCuKA|~C=~QbeM{-R+cZq^+CGraYg^sGSf3cgdHsVV6_dZJhU)X(N8FQ`( zX2`~zXDsJ~_QC02PN$pGfpq2riMBPfpzyurugE93V3!K$F{;IZUEcy0YA(`NP`&+^ zt{kzJn7cq5MRZhjUCNB^uc$jLw$5UfI~$+eTwr}wx|!kq$YmnMuh#4JrP_6hq8g#P z4bnLZlMPLyV|2-^oDE(kDwv}Q+v3hl(7d(2Y>KoTuh+qs()apP`JDu+>QjQ)cG4~` z+Ihl_48 z|GxK{I;|jkwwbL@yWe&m@8Nr(N5)m5SSs$T5qU~`JhGLxREh8)mEY%+_U3IPkmLrT zduk^z>>H=$5f5h27~fT$!(E~BGd2R7Mt6tW&PE(&c-kT7FqH@%*_K=gVetLp(th){ z$hM>Vf-VQVtm`h{0z;luZA_8{fA+&jtPC0g;>+CK9e-QQk~)iqXda`bWi_;1*V5N= zo*j&~9%L&}NFZzkc@;&)j(ZtvK-Gip_5d?Lso~>ZD!%Xc-}X7r^k4El*-Ojo=*nww z`Hjmb*`a}ko8rE91*A}Qg`OKa!cPS^inxK7*ITs{P}2xEx!OiLTSreevO4cb0>gux z2?17!3|lh?fQ_mHLnil=Ea$luVcWqiq%@anwYPkR0lqEM-zjd=s-=!w*pH8 zDO6q>(_#9_PE3jqT~a`YgO%T9{$qI^-EvKh^+QnQwX5}^`^-4{XSX>@xPrLAh>dyM zv52HLjPhh}APktVr*1?c=0vH9YCC+ug1E^aB*!9hDI5#oVFPV5yS}AmB4@I6 z+lfNiw|0=!^hU(wkJd}ssnxQl|8TVjJJYJ&Ma7vtTVA~lnJhHKdYS1Frug$gGCQ1y z^VG?B=Y>4jnG^Ex!<#as#uH-~E-Vz{OVW_6lX#?N33G*(pI_%j7>&zGReeHiw10MV zHHSANN#?}DMJBPXW^F1tX2f&GZVoqhUy*_9r)h3;Y%O@iVlQ%*a+OS(!qOI5ksZd5 z3k#3q=FgIdAhUh0q$^90=l&la{%?oZ%YLLC?e1@7hyCiCci(QSuP8IT!|sJo+zv<5 zfE#DAR0V9`-F(H1%DKO0?w*Y z{`GU!SOQ%Bu7It##}6~(wer1isbmq{mY)Dph}G&nB`ZK!!|0l%OHXdLg5I!PQ&}Wl z1$574 z<;KPB?0b){Blb_>RB%!{JHLh-49^pFY)e&Xmb}u4$Po`&jOSaFq%3vStaP^Gt6BY8 zan_vi`Ff&LeKX%IQZSTP$uK*5l3>T@u8rBko?IUL7HgjhQl~<=o=W=$o76GY@v*L4 zKFhM*)7r$;sO5fI8-9R@bXaS%T^}or&Efm_2Sz>7qrx+?(!LEKqD2_xbl1r5>x#(K z(I=VzoCPOs^W`st^oFt&@W~5sB`~wu!j1R6b+9Q1N!I0L1e0Ms%WSXW>Le6aLIo z>X|hgDNhB7cWbHRho*Gfmt?E4?>w8?Y5sJ{MlU@i-*3+Fd6$uD4)B2d${ucRoB3}V zYbxHHM^~ojv!Z$N**Gm7Qs^bQiEs_HhXz`+fjYaO3V+rw_%1TqVOD`>Nu&p?p$Ef~ zay5Nxrc7d3;z^RWvzFB0OY2liAvmMIRWM1@XqXhwl$SS9Efiv;mAhd6l6e=LlqErc z^U5@aNhv{riFxp=yo5L3#;^lTJ6@G!ol?|<7_YhtHzk#yP(W^)E2Ic~$oN5%hy=`DGfl$Yh8OQ;IYnOhJSB)((C9Pa$Z^B0JMi=9Ws^;+ft*d)j znD#Vpu9d?~c_Av0%p3P85p(Wh=A1tPl70*qHZ#eRSgnrYm*DICXe(XFC*p2MC!`xP z4jU*WDMVTHMk1BjL`0sGoXr&v?(W2BWfi}+NM?MJC%s#9OAyXelfv5&^4izP@K>qB zr^q&v$9_k0o{}zaN#BDdY7iU7I}4YkdiU2#J5lzW)FcPbq_*$%M)he~YniG{6r&o) zn8_%`5=J2EGf{;kCyZ(5YAV@Wer+bIb)Q-rc=wf8WOnrC3UD`#-YgfD5SMut7>0m4 zE|Jd7f^x9D~6W|*AKtK>iEIPCCvk0)PnPEDZ6|1%Vd_k z0-{KdSM>Y+j2I~eR8J`lgP~WrKjkA2iMcBGjLPSybG#9x)AFawSxVZsG+Wu!1aGm> ze5uhQw4MEkE7M$S@mpQ> zPvTJooW!H}r4CEvf?Dt=+-Cg_o%ecnLNE7aa<9?q(ndd4V+1_qQaT;(LuEGOlexHJ zy#+HhB?(sqbay8SE@Hkb(W}oVEAXN#uUBy~&PS9sTqIONN2Z|V$5bJ+F2M1;pX_fQ zRVG{zljuLy7IIK--{`PIy7i)4%das_C<+xr&9zH*GP648H?jaKKpo|x%DnW+)LNOz zNyV-_jC;73o1CLG7z4Y*L$3`@e00cZV7T&(w=PibJOQRUt;g+4+_S8e&)44+0PVmRzJz0 zx%6Z|1@vA0%xitJpDE-M{al`>pUdCdPp@^Je&&#~`f2#o#fwpSgNT-s<5ICllM`0a z1$Oqg8WP{={FK|-to&}p@$=^H=ARg(cRN2}2)lqiI?gh(3n(CV%vN2@1OmViYEHIk zx&h`?B3G2^KF@0xnBSA@ASJJuaL0STGCn5tJ9!3ss-tF;DW3B3fqA zlcIz*7wndvD%F-PpW^G8ld^Oj>F9Qb=f@A1=!(o=Hi!U%S->lo0OUhGAct8APAfSD zmR`FDgp8fUk_H-(uWT~IACuI_x+`?w26-%E;&|h$%zE8Lpj5QPQTaMp;HRNd-8DOi zhJ*QAl4Kc1^jKjp1K?JtR|1oOZl%@4%B*?v?H2y1nJd_m!PpMOR5a#24e8^xvRY@G zt_gaW9=m@?-o7_BYhs)yU9~Od9*$h<(MIH=!Sq>yW1WIp>wdcz1OKg7Dkz#01_73cA6B> zCD2TfxQ!?z+Qsa*)!(scDD|1Um8MaG6A5^HiDTEauKFU2umsJEmx#@hx|Gxvj7PL7${n+KdCPM{(@D(Q%5_f;)TGre*Ci^ZDLu{bIvvkJ=c8Aa z^s-bF`Aqvbam{GUAogcM?Dco`y)+jtjrX8}oc6AQP`DsMgpfWg=p;_a45>}Sy{vhB zLUv5q2d7y1>Q#Ds3aAyrIcTND=-YsPvP9&$ZXx^c5aKTVm}UDbtz?lF7jrc5^$L{$ z_iA-2T(W0gOXQdnzipHkP8lToJC*6aHiEetmZ?5T$K5EwvgeRwxpQFt(0NrJ-Lz?` zautt3b7VT2;XgdP-`6`u)xbqbD&NvR4&GbLxo*Wc{kixrf3ye+`Bwt>;7E6Wayb}% ziK?OF%O(k6n@RxNum!Xwx*CsU-b3nAtgFxLmBr1VT+=MF(pST#ELF~i#k;$c;{aAn z*dgbWB?r^cGEDSyFL>=<30cMjbt~YrV%rwKQUDVSonpO4RJUN7zo|S;2o9f}HGh6a zpwh3FK;ECyqHUGZUN?Za-2+0@oh4pvXlyL$Sz6tbB~bRJ@OkrAZKe5advw3N;S?>C z?X3EFOt#c(kM5Jq&>j}vE$n4iOGo%{_<0~dqJItt9)1t}bh`?X%Thlo;;RDLTWcjj zh6pv8gv`Q8-Qn3Shf1{>kkRxg{lSBYYnk>1OI$vJ4Om||2O_gYATT>{gDF+fOu1<2 zHRQiW9#!Ji>K3M^OlcIM_8xv%X{d%em{}6VVlG!-k#)UTHGXN#`4lRoV%Pu#gpvma z@*Q_c1<~$@igc*ArSK2(!Pc3p#XzvH4FV1rT=r~Dd9HE}%U&Ms_hjEPy89Ltu4?ul zi4BK=8M7iKOi}y&)m?_9NYWitMxl*+3otfMM7iw$YAiX;Ltk<1oG#uFPKCZ zU`cfp(aTq(2sC06=uYN~K9{wl6&Bs)d^k*~&=&W;yO5kRwo6x-P-Bus!YyL^dQ)M_ zY`%y#c8aE&%7{%e(bH-V(&_dNOok+b!ht(Cmk;j!WsxMnyYVF{EY}t0fL;V%fw(J| zqSGK$X#3r^@?4Tb8hWXZt}rOFvHMXLW~+ZW9D1&($a^4I1(6{wmd-IIe)^$tPPDb= zSl~Xy$&!@mt}89sA}A~Hfq@T(k6e2k%blCuG~XYOuYz^Kf5FKj1sn3WTu#c=Qo)7g z=YPCx>t86lGPA7j#N5YMJZmYI)U5}uFcVm&=FL|BsK3|gKzXC0K*J}@fQhlKw#pT- zyFYl_T?%VfeDfP;>0@^vKs#i>=bSN`C>gd!=4BVnj+Hd#kR3ayIXsMk6XWV-ggI>+jOS`zwIuEk7`EYim}_9)rcj7{ax9G@yhYb4(H64<03F4efCt1@BZU zK=YxLz(RL+tr}AdTZ~hyC(`o(v;8o-yvGnIDP1!3x62Y$ibb_CTD~k@CMP;x;Eyns zm{Ny5Q`SfPw7hU3;r19GJPr+Vab1ce0WTLn&)AFZg%L>l{@>3Y#qJZS8scQdb+h%% zrpio@Oh&fRN}kuh5Sp%HcS>IN(;-wI_KoQmaZ{Gmau`B(^Uvu;_sF(M5G{)ekgVHAKh zDrnp7WEa@aWARjuZrjemiQt>Nrw%g(kRfKHRmO4QIXXypkf#MS zcdl;7kEF6>fI^T{g%r`kjPu-tad`-Ch%*yA%?Yw@I~HeF*0>}ZSd`&Z_GgB5w`d4S zdU-4&Ebst@@L~edD4_oWC*rnMzbL`F&LzJrf(m)|^l)DCDw)_<%j=C>YF1*i0io8= z&yauS%b7+U1M3+UNEp!G4N4O$?|12W^lf|KC>i!0I(evyZ5pUi8 zm+TWlMSWKk4{#UDPY8wNR!U59gNNP~vz|}XD)C!v2_CMSS|qV#z>+~OsxI}L7oV_?eiDPcXOJeXd=1oN8MMkS;2tB=+VF?myVD&+IVn?6VK7W*4tj@`qq^P<<_ACeL6Cec=4@StO=?Up(o4zFyMS##Nn zh;Jf5Aw(&MQxhyZaqJ1YzVLO)<){ebzAnd~>>QyF{-@dLz48#5Uf+6y$=$UZuY!l2|FD&`=|Vyd3*ROtfgp`TFnI`aFk@vA2N<0w`pbT^HyUhdQkOS2UKe04LCw|Q z!D=dt)q8I|2}T@4t5XTzMv(7@3Wo^E(Np>fNi%)dnGy;S0t&^o6`r_ zS5=@&Wxr}?1m{Q7^R4^zX?cu_nrW>X&xJgdVXxuoSDJ^`E4RhUv1_ySicm(xwmy|I zouv#ItS!T$zsPjkKg$=sQa|+Ayu*{~h=EdRam{-j121!%J{?|~RT+`rKsu<7g7Roo zqG-B#v)YxGS{qAV%|}HRvkRsUbf#&i*X{+p61oOgBMYB#zwq3gMG>DH-dU`Fzd>u^ zoF@qPdPjSt>#ASrJ2C1FS2()FdKPvS$!M2XEev(~=Jm2Sru&ghRqT3GgwtV@*;L!g zTCaLB*1PdfnueTCi3i>cR#-M)X&!~XWC<-t1yIR=`)c~g1Qnt*8yN-+JMPCu{WUHM_RXzm>$ zK^1x`2kq`8UwJ5h?YDbdN9}ithxOL_9$qM%Rp~{m`eMail<;$nkYo(d=dOYv&#!>_HHOg<`O(ovNA2s(jaj+p`mu8CjL=e8faVR&`Hn5sb`?tW0*kHY2khtp_0p zSb*R~pg;&Z=tM|_01^Tr5WGp$LC{S)=%kYlk^t$X7X%6r2O(bX9*m0?7!tiAK^&$YdYjE%})LtOpt=Broju9w?@ z_Z?SCh53n392mc4JB_+e_5~i?IksMBd#b2~%_>S2-9L2Jd2s>bTQiWbNxaN=uweG0GYP7G ze#!S|?E*-`L`cB8_VgwxfL+!!j30N`y)x|Qj3#cXxA+LEmWtdfs0D*5zX&ZbznGLMkR8S=N}u933rUHBqB&& zJol>&dKB0r{`aCvGXZTg)Jp0(oDA6|^cx_9@Wc&Y!$-*>2` z;A%qX@OcV zA2pFF;&~31Z9Q~lp&>2Kb|SmbM4?KGs852B&rY?V9~GO;jsyi{{lqLmWmlWh6M{tR z5sVL9IPdKCIhLMB8UbWH{4_4Zg~LjOsaZg;c?boU9`r)7GgJCn1`I$w-&VcDCT`I& z?aNHYII+{3W?v!9O3bj4-qP{}Y-qfAX(=xE`}3=Dd#PzNgiE&V(30W*eTV;Yk$?#;4O)tkwRTx(Y%(* zi9lWc8SuYFD0GcYOqQmsN$DL}R|}>s1qE{2AIoy>Pn1;AqM7+PpK-1 z)>akPGjrJLpI<1z$_T~&l zZw^KWYHw~V(+)=PB6m+aGX7I#l8Y;ZpQWe%tPY{8^M`#ZBxdv%Y03s$Cg_!C=7q-0 zM{V8eymtHST-ECc$5cIl8?>%H}-;b zxuK^XvN&{R?%;wqP#~=X=@4t4G@5`z!TwBGMQ}h87TeVArd5LS_{Jn|k&Vs!6Z|UZ zPFO7n+~e`Z!3nqCI=j5#KoLsD^=OqAOFr#Wu^gdkgOF2!^v$FKRt&rt=8FZap6*-Dnw(q5u z`;$w1r%5NvAfs05PlTcwbahG`NDVcqTJ=FRYJ)KPtW@a~Q3KkY3R+j5TkO$Ewxs6>YA0fKc{xo4*A3!!QMg6Elxl!OBf5Aa zvB;04O`6h!#f4m8$!2N=rXFX!r4`Q-wRLO=OHq4kL7dv0A5CfZDc2LV1Jt{l>u-7k zf`jRwjkgW$elU&U*;rc;?_8-Hsc%6N37xYrW=vy#mr~4n;6(S)gpaS%@#I3k*l8IG zdqBV`iwSM}G%|ihu-@90?-YWF7K zw0MhGxkC2%iDTv$#kz~~aH_zs6#@oZ1-hmH_I9_PFf|6}@@uFmN6FRo0lRR9BTIOp zM-}=Vo>AiJu=W@%xfFfo?s`0X@pwZGyi*u&hxOY70SCp|y+*gc)}(+)Al~eovZs{8 zMF(m&-STEiqXbcCseRq3I+LZHpy_*=OZvzbFfW`?8j}^&#rma6_(R`iZDlGbZj9+M zO8VCNU}u+qf4QfJ#7KJ)mY508@sMrZNNV8M|KdD(5UUrYJbXje=)QqDE zLKz;I`LAvp)$OiCf*>YzN0Oy8=m=t3naEp8^AbAxLJ?Xr+D*xetLpWO*u12Ar<+IC zy6;r3$O)Zjo0VH)r#>(d+^qcU;h!jW#xC&YTHXZ@453#^+20TKHnR68)b41Ci~IWK zDn>1heXJSeLbMg|!r9`Gmk62?=MBNk0a4&F&k3F1+Pf#OXd$r;;P5h5=(bV&XQ@D{0=I%hf#$ji$~Ig@IE&; z143n$jJ2%AUs5TE`4qdJ`i2&W);A@`--)jo^y1T8?>!eA0v(IbmJJ=wU|3VJGvPqu z#5=7oODm zHf82sETgv>-|#vUOj_n_4OAcmuRO=1Ue>a_Cw_T2$G!Qe8d(-9g6rdO zYtxMpOL-Ce>Uy6DkTT1_CWd#CFer}o&navbuDDEYIqh9Ok}f}7GTL@$)=Cvn~{h)j%mX))COOWkm* zqfFdScyuU(3*DaC%F|d+iKp2sZ!?%$)(F}RA7tTwnfGC4w+yemid7 z_3DX?Lzbt7ZDvYdEU48NJTL2q+?275|HdO_(UVW{BZHT8z>_FLUMtsGkkf%`zD+yG9kbG^NF z7YuBqwRnhph8~}_!23@~8rXRT@2!jPhdkB|I=eNRQOKN5>QuNZ57i!owjgOlTIMa)lJ}er@c=T{z z_oPuob8O{hCK?dY8y1`cM^bdR(7a^qo0hYYg|C0_@aKpurDe3R#5H1d(CK8G9dU|P zizwIL>Fwe*=m%|gw5*)La}Z%uZ^Q&+2}~(=;wgLvSaw!-sL-5jn2m^BEzg4>kiq=o zH|}G~ot@%|NDz5}KAu&c?IdFk7#CNtxq%R?LGV}n0W~`#W6J1R2Nks(_N#iN| zO6*CE0jakj+tH)Z75rk>ydP%so)B)l!!Yhu5$~a4k=#ay4PGoR$T8Y&CqiPX5brM) ztx1>g*I3A%j;t=L1*hxtcXLO>^Yl9d+$Y!z2PB@Z7+&G=ig&PoUvltm z4A1uGuJH*Vw9wo>37pW+F(%vmW*aNm!s-(7Xt2G;p4Y=|r1h$~CBHmpKK_c(y~fME zS9uBfXt~RRA5fYPcDPTJm_r>0YtzwKUOW#f4YFB6&Eoqd`a-ZA zv#gigZ)vN|S*h3B401CEA2q2230m>QVdonI?aD6#`aU%n9W|W6G{2Mj)>``$n0-dK zu{}bw)4|kUymKixGg)q7)!$K$$}6v~rhx_7a@3VaABG3>>41_0t!;IKAxOoJE*lXW zETMa)XF3aU6uxvdcQI&jVIH6D40`=mf3G*_?X9h~dp+X^?wu4JFp4s3?KM@2OYiWK zrfA8mJ6U%YgpdF@5gWvh61o1!4ZAU`(toGgc}SjHnjim&MSF%iRYq4oa{1RkiaOdm zYg>10t~wY@o7QVa1cqW%fY?zhxxKqS=yZDn#x_`Q^;;C>&DG#&Q!oL1f{h=tsl?(C z{ps*iv%Z!MqPX?{Kd9%_ZwN~VoM$owxe9a<8sE!b_V2q;f zP;H335Z`~K|C&Y|YhP*KtdtO?UZ4p<%<%#~vv-5ds*Omm*&xVrVn%M(EQ+YxO{opO zV*7PJPttSsy>D%^3)U_ARd-6T+We4cY-1B=Gqe?~yYCKeuEOcfJ)oTNiG{=wXp`Oc zcPl2O&F*kUhEr`1W;Aiex#>I#?PoDt9yxi&y5AbSxWwda=4!(^e=r=`0PXHqYo z&YN$j4PRu>o?FPfMr{)#s>9;b#l=Q}vM7>&IM$sTbw`e6+$)i8^HaE8^{Hge*EoX7 zSAc}U7SUtUYMWf?1tOE=6(_$`!@t&4a^Q-aJ8xPD|Hc)H=_{%#6Vd0^lV9;+7)0k0 zJWbP}&?4G+WRe;tDAEqrY{2@dRr?g-DF@&W(EEtg>_-oak)1{?fD%nX%p7wCAjM-J8@YB30(|QmMf_A%2af>xz^o zzT8j-Um;OvOOu$2b6ZTr1U4dVc@-sW{CY%s=ZeIvn8Fxy7SZ=B-ci2#)5qf<6zPi} zrog@u|qm6ZRW)`9l6BE4TNEjlVjq!&59zx~*w#vA zY1qCaheX4ktz$_zCDav@lc39UIR#owLEJ`~8F8E2rt)^IvBnLjEb(6xhly-5GTZKN zBgovs3In{z*5Icu;c5#Sy-L)q_A2PwR(G(uv(_KjZa;(OLuUlW*>)s+}!0o*fima3s7~C0Aq9T(Dw{~~qZgPj_H1bVEm%&zRyEEux z=sBxUVs|BOA<9Iczax(XMRBv6QX&&_%4Z`MeEqp-k(kfor0}#No3d3+_9nW zJo;qa(e__5ZwSa*c!qW&R{v`sIwsM*@O_bOwjn6U=_#mVT?xkMokb8v8aqLYTg#o;x_k+Wz{(_<%| z!iA#(w%y)lFzJ~nH&U?+T{RSxn|@fHG@T(^&-0;HD2voZ`oerk*3L5AP%k!L?CQSL z+{oDRjCh9Opl+9^E{??_-DsX*I;NZWDRRujJ$RvxEz=+)zApgLvq8fjIrV|#KH1xPLsgwDr@x***@q!ul^6Jw@ zdutcXWMQIX_qOpOBEwc*PVRY<%jR3mhhXjUG2P9lO^q8w9HZGJFz$2@f<*X=n*= ztm+8SVDn~0vUxIu`WPK@N7gAPQWGArOfjygEG#w1)=vk*eiQ9L@*Zvh#FWB{y-g9Z9-r1eGqYyltgga%`h`gB zs>N@jq>E*3B}FU7>tc@t2(uF3;tAY)cu#xTXD54=V3)Mb-?q_ z!l)EnBV4o;4B^&Um|E}3B)!EkDxB&xf-_=+W22&RKd`AZmuiFi&8+RN^{_(jP)~n% z@Orn`59c{%k}`8`PKI0@oxCGVnzYqtCu56jR3<_$;8+Ow#}!4`rAJrV5Ojmq!~}qN zqvsbm5PArlP;u*Q-(=aDKs|rX34Um2*qQUet9QN?p1#ye^2(;zW?N~i3?k_ zGJ40oniVyWVdi6_Rx7 zE?Q)=Vvdgjuvi;$mku_9VW)Qd8N8u7I_SeLNQ|N7{64V?8>*wDYCbuu}2Y?$a3`NX{Ix+I(7e!i=e9qtB4K0_ME&4V!W$ zi^0le>ygj3%Tg0SVOi3{@UYwhBbnBE0{q0*%F3ZL@VIH zWGJ1}z#94^dywmkYhHU2Dr+%+iQuVoNO++RgD|GZ6=Aj*Zl4M6>jQ!pIQ4C>_lU?8 z)O_}6RCD{~?l)2A5SB``FL&s^DTnNkXtC|{RH(Ry20dsZ0wMg|j?`f3<}ZZjHm*At09G8X;{KgHEO6V6A?e8go9+xLJYlB^I2I zzQATW8w7=i^hDO-@E!1Hn5NPCdI|J=mwIcuucy{@?jj1IR0jownMZN!WkGtz<9Nu%L3=W&zQCpSe?1JCpU>w*1?> zT>=Z1jWw8~mo=%Suwn#36PC>?Q9mtWG~qL3rRZ5VSLXWdo!&D4x+LX`4jEb0got}! zmdxuUQd4+|Zgk0Yb(KXLcG)H1#zetzO^5|jXRFmW(}BcFqk6ziCvnRfyc=Dc1=m0} z{SQcaL{w^X8vZ!Hv^0;y^?YhQSlY@m)|B)~1CcJ)!xI>1*d%e~kwqtiIWaztDHa4; zgEPHV>UWq&B+f)|v9BIiU+8rk8jt?eeuRcJ zq*x+8bKvSFD;>0Cd8I`UfQ92zNSM&|IA}O7 zhC%_ycN=x7$ge$YdnOPHHP>kCxYc_X3Yk8?UTK`*8jkCzT3A|Yynfk5x)Nr2!|IaP z7ow__kgg}!$5E>;$_?%5RX-N1imW# zPY8`i>_oQw0gG2XVgivgBrly0{kar9xWUnaTJRN|+dn(7e@`x`XaNGKOJ7=e+TbO$ z!6pa((@T$K^mx2);-l0lx;`)^+uNXQy_djS+!PXzoIaSk-0e?^tpQ?HLfYU0SarMF z=yNN#QZ8L7a!0B54mQoa=0#1oyI9`TTn*u?1t4|3}P)Cmk{6(Y4+g&|7j>$R<~pWOGSMVrl^1~YrORZ}@! zGoxbBZgoCxuIqUv*`*?ha35{8P=Yv1?=E;IgT3IA2#qRQvXRecI_rmD&`-(ajAPYf zm1cGGo+*sy{D7|bP!THk^&aqt&<}ss#b9l&36BFt@TwympN}vAVy|dwS#C?WhX~f^ zCm+pl$eJs7w^iaBQ*0q!CRrXAH%BM$ZhS@pNUX5g>%Lgyg4iV+dvj`{lDB&GS>kaj ztrv69m*sdOlKcU77oosfxFvTankgpBWhx%u z5)yNtKrICxb0nV#$wgKL#oLyRzY7j#>vFx}jZ6)N_I!$+oNabWut~uBtU_V|abCh; zcVQo5gC)ueOycSO?)#v3+8xGB6 z69fi#lR&oC@Lgb4EcCLo!@GoAoi)XnhQ1xkV(e3SD#EW_TCIx@!bIF>To0jU*JbnI9`F*VwsIW$dc*VyZ#oS|G+X4tm= zO`mV#G(^rpJ?Jkn6|!G|zuXpDbZW6bb4OQtijnm@;?3Z%SaoQK>}5*5Vp9XkF8w?- z`chF&d1ZjzwFveQF6nht6-9dYY=*+d7*MAzD$6uqohk&;MX#G`CB6p{71p7SQVpr_ zDZ!D8+T(R;Z5LuMOBf-(hlCMs)4YE@*(4M*qdP>BqEbDBku#8+TmbV9tbB*oj3$u4 z(?qQ+J}Oxwt5st&ds9ej|K{DhPfxfS(ncOf7PT=VhpYFl4n~jXjTUmU`A?W6=w5j; zQbYGBPjIpHT5xeZbb_{JWJU85y^AAUe8Rg-k$MZ%+5=8f=ib0N1!g2@uiFuJihgx-f37Z+5cq^fpTiPgsEYa|wU{<6|MX!3lLP z`p%~?4RvMQsj1jlLsbj@N}6c= zf(*lAbE95X15kF3^R*2ie&kgt z+r`xv4#cU2R8lw02DSdOfNKWzqy8$2FL^!OS0c+-vb;0T=(+6&2fLX5o*s%Sn=^=$yW*4F#dAUv( zwS1(Cg6L#nBMFeebn-`EWVPN7MD)emf$)cLMPd(@e1fUbhvD~x7}Ap*Jep&K0CLS| zP`5SO1x-y*TL^WygzwKRbwHsjVIei-3QTWo{$70t^R_t1T45;2`?ZB&E6q9#!VhX9 zXw9pRg#m;u#nj(Nb~+;f!o*T9V?8U^^QmB+D4d@~Dnmo&mLWRbou*b=hDKeB7-(K& z?1;J}ZHH9+HP_<`7GNrSSi@~ye4#gpeR$iWGX`&W)jzpE{zaE7|sfvh%4w-1~^rQW7OH2IpyM0uXk zR_A3a#8qQl^0(HyLR=58RVQxaKIwcL@xumr99aDTYfVLYpZmEQqez6|DnJ2QtPdoE z>f?(u6uLpQ;H6aV%OvYuoDYU<#6^Z*_prU)Ys~%s@L%k(Jq0J5IVPw;fH9!RqNP{x ztXd4%6)9s)EI#5OW`G2fTIQF{V8yG7nPXAoL40N?@4l`8KO}pC*&@b6gN1sNxJ@7__>pyBrC#L4olvF$~IZ!ma;lb#5_~hE|CQiA4;NmXe za28Er9V38QLfdOfwZ*lC&e;wcFWVE3jx{6}t3g$O6Gtxk&SvbU4TQ}lz-fHeXtHpu ze=Hu)_aCkhw-N?5s5v#O<|O!q0tHAdYt$XkwHSFXoz7Mmi-6Tp9|;Arzav zZK!uCQB)c`GJ*K0!;G)A>X?yheX4DxPKmZSiOrpUoYUE}VTY`@t?mxbndEMyVvbFn zwc!5_6kG*OEt(n9ZhgaA*HPh~gQf3um89*}nh|eif4~33O*~6Xt~AbybAD1>U9DM& zMK<=+R%wCX)Ma*;x-F|HyMWPfrEr?k%*4*>F}TXxhoo5mL*K&eKx1IZ1VLTVUJ?Lr z>CNH_vAejq{8f97_a`~+&CB)kWAha*18pum_X^z(ycaKK7r9GiQqv|9Z7#|h!KHM> zMmv8a>q#|>on#&b}Ke< z7re!8#i8^hzq23K_B{gWylnx^Z-MJz$bzUaW)Ts2P3QJo3alNEFoMby^0&bE5oF{c zl3!KV5Jz>8=HY?daAxOZ%|w_r&Q!Ow=uROIbT)s;bYQ}!heEg{{H$_EN3q~-?S0N1 zcGy+f1ZGZQfVpi316Y{ATjwu;`@wVyCIZY|2Gk1*ITj*UZQMf0{XA|)CCXgK2wjY3 zgUH6`geTVfwW&y@Bd_0fn!T-l^UXTa_xcI%i!3A>O?xRB9B6@^|bae6&#gHkz<~WS9tWbKS zzg!KKN^7Z(Oj%$keAvW4=5%H~)f!q9kdv>?fHjVFJVR6uDvbrMQFzpIaej{5wjDT1 z-Aa({kHNRjS=iY-`Cn%~X1C1<8*|;+n7aZ|cbHltIS|P)M=J9+zBzG6gA{kb?tob| z80Omu!=y5oe;^7q`yu=DSWe4p+)}Qg1?eoz^;hlhQ~UeO{;rs~|HBV+3**I`;`eg; zy_lshj*IjKe*X;V&0j>J0s&SMa)_hT(dhaEc3*R6&)s33NF&&&HGcK0axl>bP75bz zk5-;b2ZYbl-0{V=^v8{p#RI)8Dkcc%Ih^(ZQY>1!6yEZz*ak$ygf1LKlvdT9BvL_r(7Zmh6$z zU!2_k*tk5u?Cf+Nt;{ckkrooD-Vke=QN+>daD2?J8ZB`mRw}S|D6;~GS0^xq*vpBU zCjqfM;zsU8U*WUKPC?{|=!FxQ#o4jEb9Q3Z8M$X%2%R=NIpo1#6YaIz@r>?Qk9}|Kpe^kP&5R zteWgjjtwIxQj{CA^#ux@lnjn^oxgu=RLZm7?-{>tlfta}&Lfiz?K@Z(H($00$LCYN zaq>~AY(*i%t`4-&kwJj_%IDpI_slHz{mIq!4R;%Rt!_V7<2k(>3?VmnOF0l;lccyC zjB_tcR^HkE#YgM6w;<>q;MphuV!Vs1^YbsX%ow*J+&Gh>p(C0@7_@3rvf|(; zW>YhZ;)7g6IaiApfnJ533N$@}2|cz)_0aDe5|-a?{e63b-gkLya;??boO{*TTPl4m zl)i8b$RKm|ZJ;aZ;YU|!HH^5unk32=KmRaP`0yc>Vomq(p<(R&y^dIOn6+N9^(`Kp{bDF*oF`#7{Kayh^NM-b z4rZ5jHuE89n~;c?jlA*11ifG2XMA)(jrO0arG-U4QVa7>Mk7ps&wfFSrQ!A2==9Oz z@_`=R=JFqVc_}sU#9&_V?LM?6C}EDBx*#KXhM0>%vEt#BbSI4^mM(a&B%7K_(qcT< z`&Q@8x5Yfd9(inA-l&0WB5W_1*`Nk#Mx@wv!p@s55c`P0LZfr_9=?E{K3$uOO>{?x z`!`??3Yq)4+d2jkk?|wKluHdM(urx1)QB7V5CuzvP{`}p8KpxD{evTEjSk`bGfoq1I(D+*aNI%H=h{AaWY7ag5ugrR}}RHarYys8E=O8h@WN zSWl0?KXDg$mU@MY1?RNWDziPg0*gvgCr_J(eFY$jw#5Q74o7e`2hxP|^*TX|B z@eU6OPI7pNGs@v15pxa?iH~x4h@0TyAy&hOhd65-9&(eTzgC8oqu*8v*<8P^oV@e- zZ6z11DADk=XjywVr;-7~zb~DwG~420_(wKk!d~xX`rU19t~b^?dyCB;ymhJhfJjEg z_hR$G!fKwdP$Vo^i44LMfKU}I5P9!mr2pvh`Qi9x;btOPnjh8(Jh9O)4Au6D!DS4NXnYkq=I27oyuso3`WE(nnVO+S6zY&Gf?_R6d*b9{{rC*Y;zdRS)^>A91lNy&-qE5P^&%MQAli)-{Io~(RJ0W zD8Q`Nx|_XrcVoa?^4)Kn56ss$C<$c0XSz7-azgc!CEzM2<6}(aPdLW-L-kTJOVtpB zV?N;(u?8oVW>?^I8_j~kA^_*@m}z!Ia`G63q?QXPH4@I5bufrH{?YirF%u>4qwpD6 zQnAHx6s_OlVplX;AD9AQpe9TA&>FG6E!(93bCLqPQtuDDh8lqBt2{w~_zCHuQ19s{MUx zf1lak=k_-i_R{b}tIFZveD>v#*j(sqF|^~5#8$FIZwf|g1;UrN3@;VuJiGY7lWF$T zPE*h4B$HQqp=qOQTpf%b;oERAZuIT1z7;UzVqf=;Bi=h9VgHnh z_D`-38!wArfjRehz`;cCMX4^^&E?1Q&2PXU68l1y5<1GlKe}%8>~CQOZJVZTkFIqp zPTi4o5byo;V+UO=E8k6-p>Re+jPYCiAOT+SWHs*r1=-~mfvwZpOX8X~A}?#I9^aW# z0*awau5;0m*$)m-CDHaw!Ro^V%Dx6q50;aCI1kqN~6X7{yFh~)31Erdv2&+FW;G@9kXs8C#x+)^{a-Ev8>f>DNL7d3AB3 z9)hxl?NR-}b&;im9RBQCul;4PDuvHrd^p-#lHIfAY=B%G0@4Kb2=& zYjSp8PT@^y#4X@yKME!jge8lKaJ>c{6!yL znpVxGFnwvl(}!pweGpZS$1G5INH`7huyl*}!uI@rG{&f_(b`i48??&@%s8DCGguyc zTv#0-oFQ5Axj6XvOkc>Dd@ZG~Qc*U?StZFf;d?s-n+d;^W`%aqz@OM(Gl|X*-_^X_#IKr{ z+oD$UmQ$w+Ri8yfD7yEk+1}YD6fo)+4J9|9NWM7*G%~{J31VNki|O;;4=iX~CC%f7 ze6$Z|pT;LxZ(iOs@1MN5e}ck?8-c5v%j>4=?YcVFHmsD#jS)AtPtE@rQSDyim+kGn zrd<;=eZ|M~?DsSKZJO`LEB+7nJ#0*m!HNFi-2Gvmha~ME-Mpe;CPBZ@Ifq}|H3-%< z=#S?cPf$r>YCCTHk;iLr-^V8&_~Pe{KSIlAQUyPwPfz&XSSRv)w>#L`?QiVv?W{Lq zLi-z{`{S#g@_QX(*{AQ9t@YiF4FdJ&DVWOTH-yOSsND7n_(>z3-?YJI!AS9EA|r{C`Gw6^kcJ8SK&tzuNFytTDg zs%4kvs411&+3ol+O2v5U@l}z~+S+`zv)$h5hg*p7k3R+eWI(iv0~{&+)@m?lkdpr% ze}WWG$DTn{$19xb2ZJX9OJ9wD^7Myqe%1K)R}K9BKmPFK;}7_Y93T_RvYxg0^!c+X zInM}_Uti`KQGH7}r$3wx7qzhEr!pmn!|b%2sb!|OLu{uhwGmu(N)84zTbnQKt<4ur z^P86r{OxAP3@+ec($ph46+msY2U@O()7%0)emw90VAGbv*lT9kw8=|N(C&vV#TFF~ zmiKe$w?#D9XiGCM`vvzANWFU#uS+ZG+t`0eU5F_?5edA8y!syRU}79WBis`VuZCy7 zeCt8fR~Xu?!8H-{Tz!5isU&sTY~U+>Bg7ji#vV9amGJ0U;~|gB)})^kX(IHwlq+1t zfbr;L}ub#vownZ87Pp0G0o6o(|ha_pkMyw&P zO4iS{>D>I*rLU|^NhcU$9sDyc^@}cT*gYJ&dSO8`?8ZbtZoc*44wXsUl#!%Bc&Ieo zpjGQqxJ{iV&6Cak=-M7FQBGAHkR6|j_%<%jhYJaQ+F+3Z0DWu+cocN!H|_xRb^TO4 zLm5`>Q+SRd#?C&n9Y60+aiC6iZbZvvZFro_N(cS!QW{qDSLRvGx>#7<<^X-sT;30l zYh;}$Bn()FUwzhqwIfH!4rt3St+vWE6x|gM%E4zf?k+D=n_oW}2ac3icuV_UXI`MD z2l1}Ah+L8amex`z_I?@itn|-8?@K%zB8sNq#bE#FD+flx3Py_b(9^--}5( z4a+{4tM&?z?>`Pfgor~K4H!%}Y)!gl4%zJINgbw}o%xR&8|>C_eIhTiX@0u6NjsYH zD=cM1Vt*W9HsMeBKwKZIQ=?P17!MOvG*PS!%1n-O3BklVw_%j){VlFRAEOJkM@G@Q zOoEL*926-4q*(3bP*zcM za@CP&_y;saRSCt`*edaelUf~7lX&CuX0kCTYN@2pb0UfKcX9WrY3G^A&o-aM!hO0_ zw{V|6t;i$Rfu~RL4ucSCDt}7lPCtgw%m%0@;i@RFm~#57{agCk0)sU?CFR2(>st8ZiFk{P6IvG8P_U zzPw14vF~b$RTFp;{wTKRAdELAWMot_t4oul1IPE!*)c{0`z zv%!03<5-+RH4O=#!^nyFSl9}!#~!)HL8g(R+&k6XeKeTTzQ$lr#Zk{23*O8+`LkxH z)|QJfC9JLF+_0Pj+fxrh3qI1g-!vQQ$hg^X8kIp@L!vg`0QK{>icWZ1&Vsc=th=C@ zL4aJ7FGf`pW8fLLm z9>lc1J|*&`e9q^BZpCgCqjwk-8dZnUxl86DTta3gWF)xzHEhfyaNNhGuas>*_`%)G z%$&{XN2Tl+t{+o@{X3i8T}ZIn$3v&i$q)8LjE`upS9FE*4LVpNU(R6oi+45w@3 z(sZkw`Emq>=T@0QwK}g|`u^z+RUV*8&O4=CRI5$=jXJwK>-efgK%FO4R~f#}FJ_`2 z#VK=iZq9#C##$TK)Dz+3%|p zmdrHJWqy=R>GcKn3onZLFem)|D0uKTO@gFinLKM6)rqtb^Y;@4B1 z2P?)LBx5h4g*lT>O{>h4V>wQIRGMY+t^0q#Ugb+kzsilrGa_)#%d){wM0(8Ih#`WD z_`ucZz&W({$Q6&bb?I$T%>7zj@+OWzsl_aw)u%G$9Adou=`$#2i_@5h&L)9rH6wMM zOt1r%U$sJ(jE}KS0E?32AFX{ooa#DLKoSAx7+Mh%eDMBTu%nnBGDd zyW=FTqOyy{3KSyERcIi>bRtS6i3YY4^Hg5z1ENIu3HuROCP#{mkmh1~p(A_E$4Or@ z{w*azI+yqi)OGFk%}l}KQ^GIQXElEOz~swu^ADTs88;bv@40}q&NUZX7_AiBG!fCr-atqy6Xvwy3j=8_@!>%#)YS}wK~6=+WIW!96OjozE9sQrZm0u zv=}B~yI6(BT&=?JFj)n@FnJv|A`=W05zEDq zu*m_(;6n_0gY(NrOMe9a8ol4Tc(+BcCBW==k8W5fx`?OP*9?Ud*dWj)Mry(~_k(sJ zZ~i;h&0z9nX2Pzz)r8T3h~Gs{%ab)O;snrsGw8KDt!}Ho+pPmZYi5lyjt7?qR|@2% zGvao;)x)A?{c)39*nVf5Juk*5<|%M)%n}(;kQhofZpOdCa}sSXcdaT?`y9)qRQ*^& zpqFvPmP_Pl2I`EUeQdfGO2eys;Ajt3O900tInDqJ8Ew9eXYCEE%ER_Fe}N}iGb|_5 zSvG9m~^xZ|CZ6R}w`9>m5U|ddAdwu+2 zj+m))22ybTYU)?_f6z%%M@e&zv$0MEBAVf6PBNj0Q#X7pSwcaIq1NLMgV<*dHWrwN z&4Or7hs_7kIa)oAY%I~cab>IlH#auh>vCltqiH%A5u{n$P9V|H$Q=t6v|`=-glr_- zbJIhY;w}%`eWgvNgh%G%Qefj0r#E#;TqfE|icSZ2br-!J%WUSW`vmAm*sw{8h#^s) zkBop`U>-|??+tFS$VtM!@0*_nz3SpZ`2*v^aT?DCW$tLHAOs8 zcS=0f*HkE(Evnd-;lKBf{{Dab>aYF%-T(Rz z|HJ?2AODq4|HI$-U;pjD`w#!8)qnM$`~|1;NP{JT;TF=oTbVE_!>mv^l{CwuN-Mmj z5C&=oN~BX?|z#&2E2pha1B`y5zBA7Q1{mnWq3 zGPHaB+7Hbh1{vNYtP)UYnK z>lw>FM17yp3b7XmU0{X>59?Yql-9QPIV}le?-)^a7!zn!OvaXia7@BhaGF7ui69L& zSu??4jYOsCV2s*h9yRyH`}yb8x13a})TLYJp@8;#A&x7l=Ru(MC^cg{zp=&!=Qr=U zpd$9jixs~j*70I0kWS5do`Hbt#p>g9R&z|QoN?2LB;-m;*CRVKPsVU5^uu<@E(*gH z!g*9rC+bN-#hJ)hL6l<<98d}prG|$*PcT2gYb+fqrVh^nh2vK@_j9xG8t9JTd&$rb z9vBDu>BISl#kh4c6)%#5@$kwVhZM|9vAN(`+7APAADv$wT(1&ssPs*giNqm-I$`pN zKE11}#3j=NYDPl6jb3~0O^a}oJrkSV(Hrz&YQf@5BDXezAZcl3B8lM<4$BXP{eF~F zU9>#FoARX1e!TNH$plNXh-gN{$mc%rJ2Qwj&Q8I2F9OrO14u{ z{E)bf=-u#}Yk4YTux|z=S97#RcKpH!F#kL|dgq^zi#@$#YiJ`83+{U(+=)j`7D5l@ ze=>fRc?hTWi<>dlKX&>tlABovuM_sWyIY@CO~2P`O6fi#kU4tSK*lhAbqMmuOz;sW z48_YSpSQu$6J5LDmm;basgu{Z=y^fTVnDZDk?g*RwmGdbRN z*DskDB=R#>@?%A7LqIN6n~@3D5&nx6Zj(w46w^UvxDv9;jBbTnv-gTU2oH~!3Wuc# zk=P}ZraywG4kyy7@W}e^@m9p$;8x1RDCX57UOPQ1QY;oZX6!6u1eWb*^=1_fVrjgc zV?23Tqlj53bY&Ibuh&T9hgw@~Qv3hT8*g|tMiGehlz{!kwm8+5SuP47i;36j1{SlZ zEa>3y8hXJrk7s$B9#Xe?Inf(&5WR6zslJ$FP1EHKK8Ocyp7D7LwyyLYmP+U9;=Kiy zWLQi=h8{-ik=p+0&{r&aV~r&gYJ|a{n~ULy!wP~*gZMS;C?mNJy_H6%6)1J4QpPr( zNL-OtIOMSB1T7)#$65EC$mA|-?djr#d{eu2pDvmIPcBDIf#Z%U-+$M2Ibv!`w+M8u z@kIu@gjd`|FjKf0$r_=_Is4{r_1PX__lIsee8=kO#y}US@I%$$md2?D z(N{Vl?d!-gF85~F3&a@j}x4&*2?(@HojrRK2i-Wb@Z5-cq{LpLK zrcLzoLMit(AzeQV*+7Ib(_H1ZDe1KKxcPP5X~dO+iKAJ0%6ieb6nex`)&BgmZ3a4i z)#ZhQj2NHKobxWu6$&X_S4N=18}iwS?jBv#2!*{+nJ|O=`($+Z{UVn`3WliLI~)N@ z9eNY%^P%bV5Avl~KpH-~3?oLL<+5!}_u$hJ&$^JG*i`E~vPh7jQb*c$52Ru4_B6FJ zG}7c@CGhs^*JaoP;zy&`v4B0p(RH)3xBz*8jvkWsyxV6Mo&>b_Flg)#Bz3`*m+w!@lY7WuJsEQ(MHH`Ht+z}`J z>M^ti<#U1LL<1`o6nwKX|Gar1qopI+;+or)Ip~)r{Z{hInpngMdyR)wDuT)Eel0Fl zkF+!48^as6`yZFpBA?UA<)vq3vg!;mj)nj)zdsm1EmQnw)r;L^iL^J;xey5tuQuCX zGjyZn=zP7_dx|Dk?`1)QpEse)_<)ugztE!P^ljX|u z&%+PFct5K@GHDdoA=L2E5jL;?6wGH4bFdQnW)q)Y<0^rR`s}=8#Co4&o3t0K#=;83w{ovAtjzaKf-omrSK4&hUE>}od zH5(b_XA_f$y;DxOL9Ju*?&mc3nOoH=54e0*^-q&Is2?z%%;q_(*esTohsG#Us*3(f z*Q{zgtcUp8rmS=!kt|PSTvU|)Tt@nor)NJ03C}Qf3-AY5pDwR2p5VxFczrsSSNqlQ z>eG|q_x^L|X)=hY&ND40OMW<9;fD#RGc8@dA zv;*B*tGm2P=DHp9d1IbvY$sB#5j;T?{P4y zB9yovuysL->*o-~Muh2nP1m>GtTQxI1T=40uB>L$DID|GchW>De1y7;8c|f~e1u)2 zy|&(KiQ{?MTSsawbm_mlGwQsR55cnc&eK(;@lyD#JSV+{$VauNuZjA?&`i7Hxmw3b zd=)R?wUTH}+k9YKT$lbzq0r_P500EE`=Qs+;T0Dh#8kh8GcI@WP#VhXo6XUpXgJ_2 z+_~_3tOJ*;w$lMhYSa5Cr5>T`gugT#Q4uQ6>S)yUlVGiLr*5mBV&FMqvZ}n}Z63*` z2M6+`#v!y8Njro*^!&=*aljIxd*j~^ z9|WI54RCZJyrJ-d^Cr8IFg;(-ByTk~*SwVq9c?MHpIT86_Rz-dB}AA3mQ^IeGFi~}YHdnpZ=gcgU?_0<&{!Z5 zX~9J%a!1H__Z%uC#u;Eb@$%$Cjrfw)#l@f)R+ub_H3jJkBVu!>dT0ukaB=hQI4g>k z`yuxi0U3$+h<(IYP$_;sCx_#4RxbGj`tZ^!BAf~mzYrs@P4`qu!+T^zHu{Lh?3C-v zeV*Y}_!=;2nk?b={mBJpaZCztC0@~KZe_)G0`tY-^!iiih30A&_cV3Q9ItuKqJ9bR z5CfhHajDA+b02#ZTlS%j5Fj2R-{l!UTZ9(NsuA)v>*#TwtaZ1Zto6DYC70;}3?v%X zn?jrr{QyH5d!r&A*r!z!cqyTv%=n$;NZ8c!|?^QwGP9yc1l( zG0K%XK6kH;g^KfcfsO}QELt3wtoRIgq77fz znlekJ$2_$&8a4lwgXW7DLHrwhL{&IA;Mm8g2dX^QwwCF$YSwh16X@tsbFHNnP@v7Z zP7oi$1Z+5=X?^QeU{pESV{>!J%S&CA?zFdK%g#UpJ;ubAU(5y`=n_nnK0*`I_x}LE z9Si0W&ISSpg7DKJJ2geUI%@iPhR5B}-=RiFViB|8l_J!8p`6|UQ}31W{^|&Zi}a)8 zMeN$%&euD;Z+Du60N(9lim={RP{`$P9`fMYj-EOSHzT%Pd^pzDA9R+Gh=Ev6(j&%V zmL;N-SFbh&5ma2IL39Z%xZ#!69l}q*F?Hw2Gb9+asO#M9yyJ|_K;lN_DXBFNFcrX@ zH=rfZb#iL6Tw+~z69rd+@-*|XPzWsaYpS>y@?W}2CQVP%h9HvcBX`axeS{PDE1w|tijT>rC&e|B=K!!N| zc{@H%#ME~;a+P;c4DW9i27(&{;u4IPEes~PAsv?yf-{8W;8z1>UHQS0cmc)u~p^XEs-;5 ze`fjnE-mbs(yigiWictAReP)f_aqT$y7H_$krCT6fT<4!5-MlPbWo90EE;n)#}$Q$!G$2YPGYE^Jj-r5Inz4PhnuX z>bkAA!G|l0sIgpJ_fI+UpB*MxOd#)C?+V(~JHzti)&0ZA_YWIC{j=A9_K$z}H~xI% z_-uHf8ChI5Vw~gmoIV1;j}idSKWP;>03QJWS2nLt_aI@D}T`y!^%QcU}ekfzFG+HZz5)#nX<@x>R7tMRa%WM4_v%KNXr z`VFgfbkH%29u7o}R|H|;`9U4Ubwd2vzxm}C|BPxJ9jtThY>CFLP%^wu_5X#2clIq; zThv`{$@LOtEPnCD?^uzQ)!T|BynaDHXoaEPQl!B27hn9Yihzk*isZQBu~?qt>9yuM zyRn>lBSfiIaE1r{3Xb0$5Kq!X8Z{KGCBcnbjF=3Mp80`k{Y8ur-Wvf5(@lzO(g}S zxbMlLK{ZQz-b`Ns@<#!3cdgfnt?GB`!rt%yom5!ljh=sU zse0YC1TBF-`@dd7?sSY-sm2Gx%Mm@iy4E?Y!Lu`#(#)lEY1SHhUmC`T*D?*^%EM|~ zv!7n`-j{z)bs7K;NLQZ%g#D)YZ--pjKib)K?@L291!P7|U+wSSmsns}H5XU!hDh-y zPFr2OSppUQP7U1flo&lebf7u$o?JI}_u?Y_(H9LPgVy08BFH$>02{DL+uaCX}J@}CPp#12D>k9BqIOPhJ9%VvbZIhaVZ5nx2J^xr5`qrbVb;q`8= z&TY2JuP|KMN4S&0x$6J?0%TCht)!Ely1I?KtlwGV(mB9?OR%hkg)gVT&V2k`~($BVGuwFZ6#zT z0*xrtcD@!Tf4pOV+qdj;m*2fARPJ`QQJS?mzt5 zla;^p)!M7g-QKsu(Yx=@{`9Z?TYvp;{H=fY-~Zcx=kNU||KLCS&;N`6^1u1-{`>#& zfBs+oxBvT=Ce(3mN9K82eRf|GV7hvmA|52b+xGhi$ToSmrk~xh>80g6Hob%u%$=HE zdVWXrEYU~lA)4BW79TIH@;J}-%l0~-gV(J$?Ln)zof39-2Yc%;w+8E*Z#M09=>* z2^K#IrQ_;=Xe7P%U@g__2hBXePL)}LX#}$GbQ;Ohw%4-{P6pXm=j-xEwF7$7P4a&R zg;0=vNj_ZR6|K}|5)dM?gov~P#1OJq8mQx_+n>Nd$k<95vuWoe6T#YAUQGrZQ&vm_ z#D<^4S>yirtHvMy@mCE(SATPV+~B>4$E2A`TNmH_Mnxs1w4F=5UUR{f{2^IMfS|{znDSxS-Vdi~`-!`wIoH(uNMZ zmu=qog=+F0EydSsaA12cM+E7+)E~NiTW6$yqiPStuMypja1?)XYYVZ}W?Wnf=@EGO zwhPf5*x!-3W6ihT^=36MEq+EfxM#gPHWuMZM?>B;Xxz)CERhCMta~`3$0BCY{juip zaYkKEU&<*>C=?cHtW0-BdJqgFL6+eUn~I+h{<{Mypp?@ih@V%eOg}}>?$0eevv^|7 zec~mHSWQ}%t$h@ip|-Ua(M%eTWSc&~GWi&FAg;$o+Cj#}H!Y0LSDWL`t{2kCKwJ-7 z6F*sm59B+RFCT542>5r`MCc^N-ye5FV4+yL6@ls11iaE1142AY1t~tXZS!+~jFIDs zbiX<;*xftjcffsjba`{=!FoxNWX}olT)}cMr{q6j~p`YF+?3C)pZs#*bnFWcfH;BpVUf{pG*ZqvG>CPQl{Tz%e zEWq`o&ePfn`-fk1>q1f&OTO|CCxDc+pX8e^Cj)}w}`#qevDh(p%Oia0TqBdpf6 z4p};k#6h&L3TUjS1kg(EG8nUfu~j6)Ma(jET_Lrol`8IbCefAd+c|M6Fc_L(A?N5u zrr-JKlD%@sGW9?exhl$1_dM49=-lj7rotM?;ph-{bLTSWW1&l$1Ew&xplQe{i|Ghc z_Cbz#cE?nxAB!>DQO(*Keg?ztOn=(=p<8xCta4DBqrDKOSQf9zb}lj!;6t;;uJnZM_;W z-KFpTdb?Lj-~h5vO3;z0l+db_*nYK9F2U)nz5cq@dtEB_&2G2!?MI(#5=z%<66c{( z($3!2Rw<#ky)@tyRY@z?yyw^^Ju=uY=v_fVb7ES>-x2*oFCLC0KhqBqM^}x7%x*9p0cS zL9Mm6c{X;l+zhi+gIp!n%+%7LI#+8|;#upL8{OR?U~Sg5wU=dLTdU;btyzNS=FUt6 zbuv5t$l<^YZ4wA0=h5Gtb~yn9xIt&(d4WFzvs7+MYG8BfDqwRA6saqV#cELM%bh`| zT3%_ZOT{D!4jP#RT-)ouY0n&?2KZ*M7b0Wy6zHKR2ex6c?g42N)rXi@x&tkxzs7H9 zcy)06sV1*lZNdxnt>G>AZq)7+izOv?Dv9m|9;R4ON~mdWO`(kOF3p=Mx@Ya_;v>(d zhwJ8~Zo|%V2(dN@#wytO7mX{OHdImP0;@Yh(`1>QfkZAYtT8e#l7Yu?l=nD4O%}0E zcTo+r?BS}s*czvS>rp6~XVB|crx*Lf)3=HwqCg^f?yC{=WSwYn*12|nuN;8lo7}+J zai)?^PhWC6tfaQ>Dx{KVZP%q$`?C(=Ag3{Uo@l_PM)^AGmFB4m)Jdk$l_s2Ss`IE> zY3uET99oGJ4fVns2%rd)!0Xc7YF?vv8^80q_zXzZ6@wvG7byyWUCeXB>hQETIz8%R zr<7;crb*JgA*5bWOYivRIzDb>yDlW(yFR%hAPcJe9I^#sWsVG?m=9lh#kwF}rca~T zonnj7YBIU{lMx%!l-Iw0L%b>}xAH{DOV@Ojq#hSNl%7s;GPB6bK!i%F5{oMZrcAQ+ zzK4x;d2YHRUE}AK+h9sv)FtuYS6nIQU?sr>RBUo1a#fl#c`fE8etE;qW}d)?4+k+3 zE>AWsFa%PrEz;hesTyc~w3#t!O-OHl+fKImP7nn2Rm1UYQ;6%Xj^}GUF@IvghJl3G zG@UDUEdu3~`e{5VGDBed9S@2Z9!hdv--HOC`fFcNL|V7yU8T%X3OSi~ zL!-3KTVPdTdu?l%8@#n$yM^m*?Y_+p7@oJ?Ufk4_i)-6%-ht?V{dNa;&$Mm|(X`$uBqiy>_Vsq^xbVTgBRM??!ag9|!ZVtrd`Ju}4O)Ban?=uy{7~Q2{Izzs zzq#>k-i1)}FZXu)1$K%Wb*o(izSiX$o6*!{!OYe-U-8I!o>vDSBbeuKt-bzhFa>z2 zxb!J@MLr2@M3*Jcp0K?^d4hX;gI=E}*ozAGW>?T2lq4@MqC2}=n`;?L+k<|0vsEZ* zdc_eI)zk#rXmzBTDyU+O1lIfdR;Hd&TA2DWU}s9Y(Z`e`Yu$1kQjT@HyB(26rA_Tw zQ?j&K+xfg)na!VJq*~V-j2CuObp^f{Tf%q^TkS#)-LKR1@QE)&#_~w$w%_c2UCbaU z{cdZA$2hwM4XGR>)N?I)U9I))&4PvjYL}WStzvTbcCcwI6I{19=)BFV6m#C)E46GR z@3ptvJEi1kACZ@iX{a?)vQq;(9=)KAj*|&%iDq`YoBh`W;vSAao$-tk_NKMjvvb*g zB}h)zdSO?bwOP_+bFEREhmc`zpi{Ex=kbLO0n*{>8aa%~-@^SkI_zKSnCQ6#Di8V- z|2{ea0GY$j$^k}K6Y?6+?dR?lcc&5!JdewhE7`1u6SpT05Aiuss>=Wg`21pB%0CAR zqZl1#(oxDploGNnGE0CD9)t-*7_twWtH70C=)M}eI6Djkgr}^b^c_Sq<8?j;Q&ZLe zB*k6VWajOcn+zkVDPt8D>CkO!YqS6D3v-*OOSkwL4A>edz^^|LEVCy0m|N$%44e$E z>e4w064=`0w9p0%mVK2~^P+k20W(EEQV=+MT$ImXEG+ieR71Bwo}$LVzu^|B;OuKO zIIYc-b7=vnYZ?HNNX-}=!SoP~VF`?#r`z9yg-hn(ai`<9JpnZfW_$;j(H&r7TSUCf zv03R69^xZCDV~FDx3_n@PzfgCsBXHg?HRe1+G9~?Eo&E-gc<8!bcbi<7kMfL`e>Ezg z2uY3OsAyJZf6(Wg5ag%uGDS*{GEp^M`)W+{wYD|jJaV9i9IX8VSDTs=%;4FjE6@&0F-u2rVvTi+n3$GHQzkLmAZi=kVUX0I1eFK z6w8u`e^=orn}veCdOFa&>7BSws53~=HnjLa7itc(_EUBCUIrs{+1tAyqd%vVQgSr6 zK9s)3^Gz`KLeQ9>!voT((@!2Q?>msOb;AtS1m3*Ql&INgb6TNL_cWM5VU-?~P8{BY zw2KAx59W>m1vS89SH70o4r{|}b7oQCBd#3JMW1RGic*F6=%h=@F>P_lt#~A)Dg<8m z?5agCtNG{eO^GO<48u)2;7n_17S#Fsh`_+MilY{{CFj>=&ZJvC8L*UClaFIBoC!;u zYzBNU!k-P_;aB34m-W=?H(BT(nxgi~#~Sk3o3DnFuoC5kjCy(xllmI1zdQxRJMCZ) zD32kbimFMM%U70+m2`&#b9(|lWmo~nhnYp|NN?=$Bf;}RUpMXQpIhH_%qZWL{#1r| zx;a7|jcXN;@J+5BPlx-XQ}0knkpu@a8WJrfKB$dickB(g*+$ME*d=(z1iwE#3ccaH z=^)8Y;Pwf`@P0fV%3I+Qmz2@LAs^$jMSk@TjtO@jM@rqBuR{H|*NXo&#*9|qapPh* zx=cN6fvh`MQ^o!yGEJ|$ySdYEeso+G39>V6LLZx8ESx8y%+marVa?`x=p2L;4?ctS zcAsZ4RZ_M}J$B?`0&Afz18vpSFr1OMKO^tmH?uOoosoGyBQNkWoi)u{K$a^qgz_Rq zvc|5nhUwC~Gjn@sdQ2*L_0lb33_46|qpt)OcrWpo%Bb6kXOdeP)is@__v z#s+PtZw<;Q#d7rpnb?tFZ_wF$iMC~cAHwHx%bqYOKY;C?34n?nL=8p~zX5~j-kH0%aU&HAoB?$k%2XX}+LeC3TEcQ0K)joVmVnAn?-|dLU z`b@ery`))IPpwz;6%kd_ZXC%pd799%TtE0h-FyXUs9y}gqR`o`i29dj5G>@-GraZ1 zmsu$Z;kM+-!w0h@4)VQq!d*nnr{0!qMLM0gs7<6T3WrS6OrtcJtSxIY)zy8a^ubPl zu-jeV+-Y^C*RqP0wzj*srygnkQ0) z<%vWe%|tR<^q{1P(+S$!-K~Umn_}x@ZC_R46inb-RqlUqUNUjBj0@FLt)8PNv}MeR z_gXjg7EUf=6`8|id=us(FL{WBRo=(+2mv(1u=_1+dap(B z#tl+$WcXoV%(XgPxsc2vTo7$|R6Xo>Nz%5)i^IpIY2YMM7e@+kqU!+fX8{m8T9X@{ zMYEGmxjl3$%EdLUYH_07H&p0cI(+y^?h|U#C^ny*BdwvGGBS(jmUPo@3o6wg zNiijSgZI3C$|GL)NBtw_IYh+95*Gf z)kUR%asMaB8s`sirn({jm=&Ro#G^tQ6i}KbP=<$o1Qx&^{PpP5CN4cc9VgvCE47$a zM5?l(MZihf!X{62>myKm0S`);tz6Z|0)7LK403{Qx^V(qWOm|mu+g+ju)1oGmB=72 z+BOun4=|xH*vQo?b5J(*B%mN(mZ+5@iemL;)1V`i?$s4jF*zXyLNGs37kT%9s_}P8 z_ZKmxB!k!xRZ)#eBLw$;co2C}>=(evp5>=wd#|Agr>E0t1{ky}t=v#D)Y{S=M+Gxf zEgCDTDA{=hw1>OR_*G+Hl4WI8Mg3-O{04c(6S5(qFlS|nyCrU@EZVM>2sjA6VKE;@ zb3Q1j_vx&MSeS58Z@L)jt{8h|sRWTYXZ_0vF;!JH0W0>11j1IHY<0p;ue^LOiVCG{ zzEtX1JXk0x5Jz&^h8NnhATrwaN?^&LD2#xTlXkR4UcyPKl5SzOSb!=qnRbpXr~)ecI`^C4v*4wXBN)^Ua7&db|ijM zVm+1_t+cWNLPyaxK?RNQ3gRrwpRvni;&J2$}wzG|OTo+~QfV((}WY{==f?Bs4jAuv9#-e{qE^^*C;3neAEH)J$c2a?XY5 z;m9YxQW%Ejs?=&1*`=cuWT~}DjHwMNDA-HGuSy%4x!DJ|J7eZ6FlJG}x>ZG6>^9Zz zKn)W~g*jZQhw)vz%v!IM4k(%yjC4Fmqd5om-@YocaeZDxrWeOfm?l5 zAq%5A1vr96qIuNfLtl8TX}EjTI|D2nY8Xyc=b~ zj#6d~$BKs37pK8Opb(EFbfoPg*1=_6FmgY_OH9U$sqIOzqbTG@sbmBlMk97mHopJq zc$g&=a)RxIfM+YrHgL>0%^=@yZ*;6Gnw;>zh>a#&`*HYXnw3vr z*Q`awae@$WPtRW?H8u$5d)P2KS|dK;=&BL+lSvzp@x!KDeR75L+8I7zm(iy$1h>_K$nZU z9%F~*E)jJgft3|=DVk^L+&bvuyRf&mhS{Dzn+uN@ab3=UmAZ&$uD!+`A3H2VG}9R% z#MNjvh;?Q-EE0U2>6>S8)igFlgWZjR-bfQC36P*#~-a^n%wTQQ<3~X((ih z7;eTodwq(?nbxBfDlRCV)mE)edDt8z$ONpa@Ng!cws{W68~( z-rmN>=GrEv8iSYoB%Us@OJhV(IXRyYt^SB;BZ}Z=k?)4!`w~Tl5B4P*m0)Q0ZD>47 z)QUz6bGG=X_=`Ihh^tBPvzo$w|6HwtB|s!7h7ck^#ggiHM|NVI_qm^FcQ;yVaqyaQ zDO|3Oa4K`WhR~f63iSF*s4t4>$XP;|sFe!LlGs5$8#gZddEEAx>?+ViWiB*;L zd`ii)GeUhR`U-{3^|_`vGp6`DbUs~(3GImlafic<-<-^e34RC`&j~DnJx37=Gjv3B zAniC#kYK?kPvJU=;K2*J3ICeNoMLu%`-9Ex&X!rWwe9H$o`{a&$D=EKc6XVN+q7tA zEuiK+x-Vz)!*EMiME@?u_>DDeY;Lt>(I<3^%cm*r>8MuY7VCX6kyi%5N|q%!t8+y%+x5R53rgVIMc=! zaFRA^;kuaLa5I@I#1#%fuTo=o=auP{i8BSH@WX;pOv!^;aHjVP2{B?0o&Fw zk);0!@%Sw|1`;j1jVmRJ_{XubqGz#Nn!L~L(mM9jC^1oCy2UxY+Sa?)_R1qyUoa-~ zv5XHNpyc$UMj+gv1f5P@h+RM zHtJ*85XEELi9f~SqOI6`x(5aaapOIPs4b90PAai8Nn=(#I+E8L`*r7fV=rgaiz9v- zuWo{T)sh;As9Ux2LRFDC#Bz}^j-trDA8j6mh+J{nPZf0i=33rVC?k2{lucrE{vOVI zaqPMsqC?suv$sqx?DZFnWD4exDsiErkcI+U)J+pF z&Q5qm{rZ4=Z!fF?a+Cb3?U!Q(N|5Lx`zpp5OGW!4f~UWn5Ce4EC!lh6hW1YdtC(7N|5V>F4Emv9C+el%6(YE0J}Sj ziLfg1tP%^V5TJT9gzV}4)-esdN&KNdk~vASAW?8R`eHV4<)Nxbr?t*)sUoEdoPenC z0DqNc5;l-ffDNoLuBtM4iZHL>`RN^%%FBlHT5Aj$jne595Y*?Gy`vf5@~ zI8Gu(c66n_x8F(}`XyN%>(TC(WrwWHRllUZRzI%$c1tZ!0=<|FWDSeNyb}4r;0&)k zNH7E#^M@G}41@XqFeCv717Qe(e@4UDU|57Pkb(dE_TE*e&b{Z{ZrKnP|Ki%}I<;%p zuJ^90UAuO%iG3cMv~!a2hQ@aKfcKn8FXqx6#h8c78cwZ4-pn~4CWkyP=7N_)`S2QE zU50?whBY@e_0c#eH;ShnnU`X4v&zOcleEd*mNguyVtuSGuNb3;QzI6GI(x+OL<6Ql zj^n1tv0w#tM0Q#B_QJyCc`nM4mfAT)OV7Fsi|qQYtmMn+wJ5pnBXHYTx*~Q44h0jP zV_CQrC|fpQqt8&s%*}I-hv8UAw@zBoiCT~#4@rFpPQ@-M|BkS@d3ZJbdj`3$?n%Ux9xt}&{i6wBdVFQ zW7joLvzV?z>!bRuwk4(-?v~I&7t6AM94z7xBCgTqh?Gb+i)>`ic^Wch_qtFGE(wzb zXp`0uFzMD(@xm&e497-BPN!%EY!=1FTWCm^@6@XhVF3?{0!NOMx!X~(h1Fg|a#m+a zYMfte3?&6oENqtXbkD39n&5f~|F8>~TB0^;4PUxDbSXlsLT-tfC@MFqCX z+eZ%{gXvm?GDU8!xrnQ+qO@wlsUbhH3I?6_&I2YnSFkI94b^=3$neQ@HMLt(+PzE^ISHVq?|E(KF=!*O72pGr=Jd1{Z6^V8fUY$;Rz zEf0H>XJ3RBFPp+ooOaBanuS>4f>tVre9PP|S2*XX>0l5W8RV>HuZ_r)tz`HV(({E- zOaZc%UEyUV_xNLwNlY*xV5&lROmqgTN}cg=cYd53Lx;?%PAGFXi*)69Mnc^>nV4>3 z78+QDss*ar5oyySnWHJwlB5y6FIVA?p*z82BmXo{mZJ~kvTewJ;?a* zQ8uK%KIq~?u88`^vMHAu?x#3P+?SeYfx;7an!`%+wdQbP%@XM?OxDh)ge-2elpTjJ z3L+AZ&(Kl)>Nj0uE`?KXA9JH^g{EH-gQfSi#Uz4S_0fO^OBA(YamVrRMU(H-1VN)(K#@$#t z12*ijJf-b;sT4HN*qCcOy}49Zi|m7tX)jb!i)1j=E77})vK>>ImVY0SJ(wjAg<6`Q zaF~a!lp#1}zjs}EzZ>xNodze4D_q~Z%%2*hyop&A#t;bj@rI>C(%ZdwCX+oygp+dZ zg=OU7p~ZFB;khSbuw0x$*bP?oCO5e4>LQFMlG$O=uPqTUbL{7EC^#!aMPD0^mta^R zvEQu6xB613OV*2fEX_o;W2^(aRVK1`ot*$q+mL6dcj9^lnU+^p&%``6iu(whDqY7_eGykgoM#?}?az_2T<9xc zez2KC6wibWL1vBGP&qbx3|i{9VKmln?Jww~=U`V5FJH=e?-cYMeATE`)q*{eo?_|OyYxjvSg!09 zkQkcM%{;a}ciVp@;`!U?3EW4596&V)Fce7LJy^lVb}_x9;!3ayDW)Sib|>d~bPA!n zy=wt9mkV||mHb=vD3hO>b<46U*|-}{tP4_4glxuG4&Qwbx9RL`3#wJX?k#nPI2kj(9$ICfygudX`l@%=tOGHN%r7>>rn`OqG+Ed4gv9Yt92|;#-sLHo8lXm!wQF+) z(gA_eShsiM4TE7}65GsVdBC<$9-1Z5F;N**F6rqFlNm#YE?^@;nAt|-T9Y_)xG=!y zk)a)e@dAcTA12g59(2#xo#TVDVqsqqwyvBJujpXRVBCgl=~E|9mSKZLPB?fGhv(aY z=+e*3V5=)A;96y2b-@n@sY}=lI*oqJ{(!hF=4sJJVeykP8+Qm^Uq`s-#@KS8l|9|@ z!$T)UT09xkFa<2);+`8hgbk~Gasx`<=ytuuRoOi!a9N8IF(-=)cOdQ*s21i$kW?&R15PtV z(<5?4aB0X(*2$<5^{o^)a}n4KmLYOq%>cPY*pDS|$W^#n4+*&2R%k=VJaR^Ys_Rg{ zNe|gYWC2kl^Ctb1npk1dXlFZqSB52fQe9VKyQRwHZ*t4_Fn_*7Achriocs(o9pacF`Db@5IdK0E>=CA8sEh-*s_BqSRf9A024 zj=qA)H0cE=sY-UwF=Z_@E|T zLaZaV2Zddfrl@G$sU^&soT{jQN+!?m(t(a9Q4!0K_Oo)>M;w7AyzQhh$6&uRv15Hm zGNLel7voTfDbW2Gv9<@@AM2Hr{m`qCiy`Z~47a2JGYSw@~vHg5SI zXZ1GZd#oND+YUl6Zo7R92bV6Ol-dZkp;W!6D%&B91Nz*wL3tj0X3zz7d6W-lYhI86 zqVnR)q4@Cf^3w8t@R`_-lCdce`HFFsgu+RYQI5$?q?bTqb4^j~Dt5SD7v9G^q$i$Q z*q~VGBAFJEX?s8y@mW#omHC@tYk4PDaLn3JTj`ZqLycKoSzHU2O&d-lDMZHJOhz)_ zq|s7qYMeD15`&4nyr!{)G};E~Y_>P!X|W#H66mz4QJ5N~&9tOy?5UfU$k%$bF(a=tGmZD=j;j2G`mV2czDxxI_x;u1dFs&dy9p@SP$NUkyYc;@*%7t zs-eyou0pc7MzGA4a}9RTCRS#vaq&ok8@-m`v zwQz8Lo}VxZX6rZQ8pGZ!A+x*zG80G!lS8I^D9&a=NLYip83=e-_B5TQm|uyVhcM}5 zn?8)a5pO+-(cRoC@AgG@`o~)5HZE#qUc6M*Z9i0w$b^OBdNpdTViAkDW@tvh_`Xhj z8FbR!$&&Z;k4O)&uF>b{RO*=MxHjoBJzlp}b#ARIrdY!43VERQ#~#1xN@_6^+xi&q zz@frRxG2pE&qS@%+yFm;O&RvV8{+%nytxQgxtpn_%nVZCQT10=b30Bi>V|J^s@Nkn z6q^n4K3KlB9c3UKZ_;S}IGbGIk$~RY+jK%juqu~QcI;qij9r|pT8Ay`%|jbRdboxz z$|1_*2hC{~J_Q9d_bd%6_^6tx~6#G6ss^v94Np{S!IJA>uAsJ^h9iU^kEn;^Nepmh`SiNk6j#6X8)&uE+mxQ(-1MpI9q zp3&6P(JYKx=W=(DH_d4_H$fLWI{ah48*EsCT5?bMZj7p%(!8nJOIEayd1%!os4-fz zF5!~@hGpUi#NN7CRLKXqlhH=Ki2^vcKs1v{-f>(P2&bsRfQ5mq3jCClNJ%)nbUKiu z13f~O8yP7w8^!eO$g6q8Sww81A{{>2guBRzdb&TzuFFI!zQ2vJf)}McL(C;|k2@i* zTS@sd7e7+2nM67#8`FU(XLwO6@s<7&En7?)ZZF*_}~iTP|bLEbPOW4*?>8>-w@iO+?v^5wcJcO53F^Wf+?f;zKrh4lVivPb2B=WodjXH33ep|2esc$$e7TLhLXwTU z`Wr3VDLiekNMZNL1AO|elDtGts?c!TuaLw*hSZB%)6k$%d09}ud3nkjCIMLIRX|`v zECA3bgY$;A#}pQrY%rvhS+Fb^gt%-!iX!X_Yjpi$U&VM?5(BeTHE_>QCWtDtO-Q7q z847kFHT98(VbIF82!&<}B{j$lMWsBv>y-jXW5}5XOC)wuG#mx7b&r?`Nlc`dI3A62 zWChVUk0k;2Qn9WVT5Ho#UDr-H8yX6v*S>rroRuXZhyq}yM1yJNNRI#lKg}#9UiRX| zhb=X(g{Y<^tRB^yQA!ZO7deB7z#@TS9L+k~qA0+e>d~{ap`?azEQ;W$X_^pnay;}Q z7oqo9aBsyCRRv$?W5&$>QW#O>0<-#FV;b%ce?oA*p`S2pSS$&J5bpDno~~TQ4Ony> zn6fcV*M>}nRO+i#>c{OoLqv40u&-%)-V#!X>8t}OEG7L@P;$hAtq^R!DHQE%GD4+o z(II{m80v9{72JNisr%B~{d11CH6jGTG47i0k5sxh{>sp6RqC@NN0g z^Iu8?Q-FlOhsPU=651(L2*-*%C22IM*_|XoUt)(-QOE=mvxLGAIWM6Q3zq-%36#dF z7w~68P3-Rj>*pg-kpn@G;Acw@33&t1Q5Kqy^TGa<(L?I&*?D-vML>ogjMDQyw0>>_ zj$^y1w8{B7>}H|`C|E2+2WMt49K;#0N;Z)p3UEhYD1h$lfhy? zH$&{`umlY2K{(6Hsza={&Ot!rwik*&Smjm2Yl)%k?Al}819izx2u3!aw+T5TY*M0* zwX^tXduVYsj8b0-yl#l4z!)s0aPPuHz3$L&#D2c{EmNf@#%F*b$U6lvmog%?N5 zr;IRO+Av{2V~8ahOCN0vM)6(NDIusifm#kGwEHo%Ho(NnLPd!uZEJfVW-`!G=efF# zhW*&~ttFdKH~0{olT)yjSt_^8^4Q(?#>RZQ)SHumRM;{Sza^dn6Qub?a;8RtL$Z0s z2%3ra?OBE19yX7<8THwp;J6WeaG=ZsXJ%>fjZ|QS364CnE}xJK%-{fn@^8t&VZ7{k zN{{9#?PeoSTexrLW;pjOo?pcCZkntj9)p1rM~7;iZd=;~C&)?|TkL*D@ty;>yAFrz z>7z5UW>02v)CNwy?c~bO15h7L6clIVwJRCa`@x;hVGX!4w}4u}yA3wdt>TgdToJs2 ze$elmU}>{9BGCLI$L{bvWbuJ$mUFcg$)^YqR`)c&alC>TVu0 zHmAA@KDa0yt!d&adpUX5w}x8Bmad%?%!iVoE2VFSQJ$G}*%?bvz|2_MlA1Bt|Eo*L z(N?p5G{f7p(Gs+z1D48R56tPmX(a1iqE75tg^YzvJg@>^_Mqe$5oCV@E|C$WL&HX2 zEIzf8br?pmRK-{xxQggMiD#;)a7)iUCwRM$U$9>>C`Rw-NxpOt@T7;s5*M~;-G-hG*mN}HyL z`a+wA@Ts>)@~EqrPT3zXyC=uOl_3r^YPvMQy0p6^b<&Uy>xtkmFJ35fS}RvY3n?ER z%6YlT)Twc%k|{zXEjJ>Sq=HN|LF`Uzl%|t_az}m=$hakOkp=+sF-x6Ia$4%@PLU~d z_t=D1Z!=nbo|Y;_mqpt;pIT>UlLB`3bV<%&FuOb}#<%kcbrrjs1a>y51vW{eyQ4{^b$4i2!8W9fB`;)=Wu4~o6YK6yAM>R;O?P*b4&Bqyq@H@vquHad zPAb<2VjoVu)N|pMQpsP81mMhlABxP2eUpfc&R!1@n>4_jd zoS)$eTvqKm7bBW2^mTG354~egx-$nfHSu}u;x4swf}=u$&x<@rWJrKNcAB9plDqkjWBRB zi5TMb_cX$IXxxBUEcQhcxPXawdTHfLZXi-|smdck>{!KsA&d8sw+!~IrB|7Bq*xbfa{gh2#!0}_Tc>}Y+2@Ju+Bb+ z*UgqEQS%dHSkH|__f6mqlvd3!uwg=rSn_~rb?#C%nuZbd2x357fIAl-#p_K64jizO zPiU1DV5XNUTL3RrC+Ev-_XrjR^aJZBye}2ep-JEg90lw3CG_%{7+{*UUf_l3SQ-E? zX;kN{NnDGBQct{nA~AuD;?yqH3RNDW)*@WT*iSES+s%h*UFcZr?TDSA0#`W}D)W~P zQn{Ba@q?_DAZKp<3Q{l`ZrCY|W3%V*&`RvMpP`UeybrO;qN|`vjx3$j{}XMbaNLAxVc^6ZM*_^nHc|av^!Nfw;v{? za4`o;k!WP_Vh)EOCMw?=3Wo=jXx|W>tyK$XC=fzpFV*B*?mC`Zt;~SReC6ESN;GoM z@iF$;#rXIbDVWW#cfbl3c8091_zvlx@4UfqkHZA<@+A?nNMUH=!-Y8&fa4?M?j32= zJa%fD%n0g5dm_Xu35q6=Dh~>dE8uwi3fy2>@5fJK6N<|$L7^uU1GvkzWk_pfcP?^U z_8PVZ>pa&yombbjT%aMQ;7#S=vsFm z@nVm>kg~E+FiU6B*$Za9Bf?XZr^ozg#t;Mlml37`H@I;=IfV`)SH#HY#*Pn7IEh0% zAROBG0h(OCa4QoEdl;NV-H)qv>>*(KlQ^#SM3vthy*CE;5N@+?Y}h_;16hD1@2s;b z4VU*9go8;X3ZU3w5L?0TuT`Uq>=mZKzwp8>+T<)l*2IYVa6Fvd~+qgw+u&6o`F4EMx+*^KzaeS08nc6Zv8kj??* zwcxvpu<#ziQ2!wqWEDHJ7{k)~B7|d;DYr)^F!QndLhv^=TkI*XES0CLtNT_8Z?(DZ zwg_#9`#`kCSb@Z186;3aF-(52?f28r9r_mStU~iz6Xno2#4JKqaURp%%x%z%qTC!N zib#jjbIF}Y96bjHzMUMk}VOc(-p1| zPTmy-7tW*GZ+Dh7ZofTx2W9Z@jH5fzyr{Inm>v7%_~Cc#sl4-$m~F%jOwKYCH7jRH zf{)(I2O9YD?j3t7v3#;v=07~l8B~`nNt|S(AWgS2C#%~IrmNqz_$_zDeL*I$=DUN{ z!O3|~jf*ftL>Eiwk`2- z`U;ilYBWYJI5u41u_iCv6h_I!xNt7ub^|%Ia-|d*+`}*<78B8llcx%>7;_5GY(RQO zAsS^w(b197Bg#Tls?YI`@yklmAni(+)55FY$`uau)auZtxYd^>0_YWw4JWW+r+bbh{sS->&jIT*Gmz_<{#4@2N` z8mQbha@Ix)YP1&ft|(**_aJK{L!d_hQ8EurW1wPJ?|JQ>+JG(xc#tNBm2i=S?*w7N zVwb}w=f$fwbECxzGnIt}YbZ?|O{MD^YAH%KZm0*iks1W(?h3n57nc>aP@9}~xW=el z@bL*)L&xfj`@|sD5IM#)wk&CG;bNT{##du39Qe_m2R6uyXDjEhCPbf{nZaO(a;#e* zIT8*R^G8kWVC7;i;+_gS1Qa}zY(nfh=*x(GE{cdYh}kt9Q}%raBfzD)r-}_P5%*MN zLnCZ63=RolJZbr}DWy@Vi<61XoXXsYouQ^}bxl7OS=?_zPv6Ukxa!azfRPVdh;C~| zt4%TsCAzq4@_L#QV2inu`{^hN%3=SUFRib;U7POXP+^*J=&2-!dgv`E4W4jPL-m-z z$S=qG$6)q^+ey~5FYn3W<3sWBNtlIwq8^Mo@P&sAlecg;3E~ zi9$|z4r>f%K*!qAA#9njBzK0}(Co1U!F<9yY;#dC7aYPDnP=Lkw(cc*jr1I2N%Z(v1$_ zCV+`dx{D<@7YKvH0^K4J8FV@Q?c1|lQsGKzJVcHjU8w0_bA^{^MhATKMpvM8&YYVZx6UT8ud zBK(GcbMmT`HLf%$Y6o2&k{|18+@-t0i~X8o!aSH))0h_7R+%tKj;eGEZ!vZT>C`z0 zT7^1jr4Tr1I7S?~Lo(q&SYU|bno9h}J+(Jt`o#{fAb;b1+}9r@XHO&{m}O|s5B-Nj zm~#Q-RUBYd+2x@EAhie!gIxg>O304bjUio1ih4xv2ckD7I7#R!50r{NAp7P5s#d?S zS|ozXO{BodOl9>J&;{QwgslJ65>&Nf3|)vb!J-;Dx3Ma#c$%HzT1kg9#04UD10%*( zX<}?*;@||lma3iI52Rf=dRXRf#l16%G?i6GGjVy+y=cjVL~&osFC){ zFdG@!GjqUXo;;YGT9{Ue+mUe`$Hoz@tDGE%tnRq(`XK%}J{r?z+|qIlTa9WJdpB5o zp_CNDK!?rwlzSB{F%(sa`f#UbipfC2$&pYbbb9sIp}S#Uo1dQ44S0|iyC+8$NcjEp z#2DrZO87Yca+^DJ9Cs}tekvRr8;(a$jt!nV3Q^Y_Lh(O_`4lP@hNqU7&d)+;WWGAR z5J9&Og`!k0LL>|riKW~La|lE+$gYGyMl&=xj%kWNz+dkI(5;e?FaYd6JBBR(Xc zVT|K=ia|v%Ng6V2RE)wL7nZ711?>K?fzT0E7c{vHJBJNn1 zJvid#_6?keYYxc^Mm9Jo2l#C|iAx6K>G|^vUd19Dm;B_@DrV>u<`m+ihrXUW)rA_4 zGjI!AkkQr5!!;}Cckr$w6JrO_SUS|v^D$Ox{8L`GRV9UOl(KoAoO~j#4yj;TK~1x< zZFZG~8mKnIYxKCDGWprSdzC>J;Wmi2-5B1v58TUgzS&+yzl6ctjkat zL5v`yBCHaID6_T5IG7c#rbVO?G7p^lxQSJm$)OUCHC^dXv0IyXA-QqKjxHK&t~?kN z(%qv$JSGSw;~~83w_0UsqT$2n@BHzs9lU4X#IfN)k$%dQ#ci|0kdR||Gt%=be_sfh z_h?5~r(&=aj&qMF=LfzIL-JRhn<`)ez^StG$;#5)>J&JoP{A4p>yELZ;k!Be$*p6! zf{yW>By?{~34#a?_LBobByv)wA2!wK$)ooS$-#q@ie@Z33~7ulCRGEzP?^EPRU|7M z^<%hQ95h`94JQG@27dWc8B2+MxGO(<8!`|h2bx__U|ophahAQyRg$nV^inC*=*a?N zNagJT6DMaiPG<;7sAG0k8DZaLhxN5|6sOno*l*B27*+Kl4Ov<}Ho4nrGN(DECMFFNIVaZQ5wm$tayOMT#LlPqgZhTfm7SbyP5=Pb94Ji5qd7-iEtm8 zi*?VUPN5!QjF~_`$0`REJ&I+fNci7j;-`1AU0#nflCaw!U&T^sDX5A#K0S-4HD^?B8gxU zleKUb>E5!wmR39t`902W*a0c$v_+O-N{x^UX<$T*0c-2m$3XgyN;?6PVqH@IU? z$D=3jLI=Yj_mZ=)OIj!eQ{+7&gEiI=$58r$b;dj6TZ3mJiy#=2;;pydDwWNiML=Dm zf};XAjCit_yeuG(*cswVEBGw&9DS@n?i=rjPQyNALK`6L#{C53M+v7oge~$Q6z&~7 zwN*>{_h1$V_g%0`un7Y6SynG1A?TN^PNlh_l|rM09p})op60rz?vEQKFKp9lbXiK85~+ z%^CY=Gz5wE#R6>MV()~tB(~=j?9PXWp>2V(Ye9xbPvBs%rb~j9;UX?3QU!=+R?0ld z<0?Rov@dk($sX4m*nQBk#Xq^oFl0o*yeq*f&*j1nJySD)0VwC!B59-?Nd=AGej|%3 zr8(%*ri&^^^a#oYbWXyJP8~jsavJI7@=!RV3O41#-Chwi9l~xy`0*-2_Rt zvPRPl(coY-i4iHIk4w#YXa`i5aioh89}S+4upP$DKpne7gJ{26VRB{?G8hLNj)q1A zJTr+oJb@n;AT&z&A`~`0GWv(cqAAxEsZxM6hVBbMsyzoNbO_Et+k|IlSouW5eCZrz zt4yKz+%b@AE&+`P14?>$M7v26I19rocz%P7+u;u%^OUjJi-s{aPeu@3hbUtdyU-M< z6dMnr!AP+tu?$8R(3-$zk!SZI?4D?~HWdIyhN79t+DyThI+|IY6i&xsY2QhA9~pHp z>`Z2tA$CPwj2w%qy%n&2Iao|GFmejYEz|-jknc+Uj+}<{Xr?k*Fv^r?A0CaskMTk? zI2klJW&)oao-1r&YL*M!LTwV~)2`yQg$|#NjvU7rWp`uOt*xNavF)bL%ZeMfM~5Pc zebA_w3|w~RpnFlMRbl@}vv>4(G`BDvR1YguW`!9{55AQ~MQpYAiYXkaqLu7#Ri~YI{*XT2hEA;LhaHbx(a*8*u9aZ?YqeMozeJR5hei~tS`>+ zodaYDgr_g^vQLmE8l-^wT9me6SyrJNbE!s#JAwL}u3_YOsuLqPeL{aq0HcZm-6BH4 z6WJmY7?9j5VvQu7(3UqomhFA9&M`hhM!~~&ElzFe>`Fl=?r7pP7`_|EmakJI5j5!v z!rP)#qwvU^L>yLFOQL!mK!+y>D^5kB07Rh}ZjeT}3<`ZL<8`vK4*h6jR_q41ssCMW1RBhzEW2NypqdlE*|6?!0X-+4~BTVdy0b_ z<8>Va!7j}ATGhD;mr6kF>F{-#i&zxI;&3*_k%_)O9RGI~4|hbxo;!!);gQkf$La6m zpJLQm3W5CumZF}n5IBf>E{>?qKs+Ifg}A??JNRB7sAid!TSEOP)AmZrg>b>&%`tf*vJ*UZp& z0#{jBSXxBRb=JZAJh;G!l>t}CURG;-twn>}3x$ZOFr3jES_m_5Hzi&RG*dY-GYNU;+X zw`{7-p5sH@qJ*dlKAsg~3Oa{kVYyr%fLAIiYlXov&eu!S+*AZ8lohb6XI>J(d)d}=! z_1s4GUIkPYtRibs(VQzjndH-;+QfpuPuKLK^kP}F4iJDGmS){`5GjXwuv+w~YiCLz zk{CH@gs%$pcJ)cvN$%Ux9V?toSx`|4vc-j8Ee-5Bz+19zrG$@eO)>&E?Jbmw$Eal# zD}y!mHql4+q~Z9;2q&M@aB#+~aDe+teLaOPaMz4*FlY%bl`@0Dmo;`ya96~WX=jRW z0b7j%1UcBeLAV7o2&h;Y;T?UHpR?c0O=0mXcxPu;EHb`gw9yn!>*6^aTG%CN$gd;@ z*2lIGsAWXJY8n$@62)ZDrD-q+4xci{9JPJice&ahzw2Gzn-0RzA1y(MF6X9pDPDJ~ zRC!ulatCv_#)GxmDvEI~zEFfPgmEr_(aA}tYBf<@;SV7WQ1M+Z}d9UYyB z@&8~vbkCjPv9ddX6`ni0XQqmTalndcncH;9x@1T)6B|N7w$p3zO!W|N;POBSqAzL_ z39dg@*0Z!2vL)WE7kML={9MJkl`^*}b4x4L`BiLX&MhtBygh)pUN7hJH7t}^?;;$i zP0bc&XW?Cdf;AUN0)@ga^wz7Dg45K+dfOlimFeXr2+_dIv4q!M93^5{7UB`6*}_H4 zH%k|Bd=C#)6DR?ki9{))0PRkeoTjnfB}cti#lEv{AfSEncYySt0flvtRZ z-(TvQE|&U&fKu??8GLsI-`z=M+`;mMI!nnnZsmA5?hi6y8~_%JtDW7nU^0IA*rAv& zQ|xz|00)&bqW6Kdb}D$a!F}5@}Q~=3OLk*oJ{o# ziCkT(u41z@i&5wzKm@KbX8=Ux<@-0ZiYZNmATGgHBwQi#NlS-?M6ikl17&vma8xt3 zdJcWirI08^=f+Bqc@d(FS}GX2eL3RQ&MiVW%nK zb(DP0l@m>0a8*q6uQ6j7W72`2WGxI50WsJi-}*4)2FwnK@$v!pH@vF_VfXAo43Y90 zt7ieTJ>`TH6y^s)s|bjdX>8zmT&3pwfWLUY%)?*EVjxVSLZk=|^^?hf@!O$;wX{1%W%mt#? zC&fy4bYY~_rSFMB(8o+16V*SqAc{rXU_25aN$OuLLp`XTgh#`yj$S21C^wv*Gxc)H z%2yX9qhv$SlYs&!$^qRMD0t$ab@m)QC@we>DG(n51IU_G^_GdB!&Zy$ocSy?l!_`( zLD4XWO6H3_T*|==xCp8!2S$EzwFL?-P|j36AZR)+&&JpcbnTD(_Q%B@*$jdMu|9${ zL-M5#p=+rmf3tB(L@j zk@fL@f_1Y4u?-o~Wv78A$r#YG~Y zaWd+O`HL`u$O=DFSpx&S^$yW71bBRM!9wZZd<3EylMWIO^^n zpRIuRvEKqrVPhCW(?M3$uCl5dWkiSmJQPzg9Z(yR=&pF&F_5s%fI)eNFCV21knzKJmQSNHPmY&Q;cg2r z4uwTa=?X&(`QU@7hGlG3Q9n)v5U=CKNk$LEywS+P#p9%q*7ddO@&ri4`_QK`;&9uD zXC?E~x1qlVg}$Ddo4_+s$lC;O<{Yqu*{=gU>e7iJ>swXaP%B&T zWFC(9addF)P?@cz8a}~8m*jG=^(ZQWH}mSS28`# zaW{cuXv64VIOhFIH!Lyd$g{k5g`LPbutuH!l`HuOVWuZ|?ORp{!Ue+wa+u1VnK$p5 z!R11iVzFxgX*tqo9R zk)mAmgzPak%OHb@$4VTuFC}8UJi=twd6?h3wf4$}w2U9g6A->LCI zoE`)|93EJFOw@OJYEZ_O6pv4gm+_BCr;d(~2!YO&uPd@2_ruG$M5niCGSQ0%@+HOjr8#cb0BJ|+6 zv?)S2#DPr_U~g;#62RzfPNTOI0?uYt)dO>6_}NBR9;c)q(u}g+ye`)!QR|n`LU=~K z#xVY9$3*0CB1$|@7~u_>I5>w_p${%r zak03vco0TY4n8ouuzC=CwAw+_j6Yas0BY_~XJ1}*L3YRe7`C4rr!ei*kf1QQI<&l@ zltzQXi`AiRl3LzKO;Ibs_TVOp&;_djn?UQtL(`i;gKoiQ(2BWJ zO{xf8n`?!x&duemYf~ld>fThnbwllD6BWYuH#UQo%b!yf(!IG(-kr-K($qHBd%Jr# zHDoY9Y_9tjd1RG2O{8*Hgrdsk@Ss1lDS9y4w;8;S&GnOF2h4bEhF&Rma!!}Gl*_l% zc%5CFtG~{kP4)L;SMGdMmo6;oZKk|ky_?HlSKsEiVmI*QN3(!Xhnp``s>k^S>qq7P z8m`&pnb)iU7Vsb%aA(zrbqAIaD%DBw#THGOd))yAe z`KT;P14Nd4)@2+@W0A80F&HB%9l^t~Dvt@utGIlG<1qbUmAbqldC}69r}-R7f)m7N z6<~Se6XN-8NPiJWA~}sl6>ZF$7>xWX>*<EO`t$l)W|<>!tU1E~bdv`rB}Yy$-9lVr}L zmP9dk;x>fN+1#lZ6!a;53GXwK^5Vj@K4Gi;wjyrx}qqYomTd( zm`4QSy9a%!4TgQkOo^1Cc{bNxP7;KY-YCLVpEf38sRRJ&kyW6KQ;S_Z3;oA=~3 zSQ-t+jW!q-Y{dvqKh1Qk3RZpRTWpAo7u=dtpwGRy~ zd*rfczm~yG^k}ABS$+VjEj;B1YBn^AsN<1^PXpe#Hdn5KhmjfjbU;*8Ov|*p5Jkj; zSpY0Lz&F|Q_4xum?jsm>#y(uO#Zan4hk@89kIgs)*b2|?D~iyUySa7Y&EsRX>b82F zu9Rn%%J(l#>5xEts&%L}tnxzD4HSV>LSzdguF;?V}8ZE>4_2Zf6ClnGWXZb{Y`V9HTU<-{qN@fh`Ap#_b1K0*IZrp zwGKaE{%|Xh{>b*A&pI!E7NxDY#uP zV*ug2JmVtB(JbdMbUr8JU3~8>SAih=UFO%6MT2TJI2{(7ZsMzdWn^2EY}|52u`JpIhL*uRnKc&~Q9X zmeGASGkxPB%lXxr*@M-|)dO=lF3)KGYas(m{Uy>tCunG|mqe!@wk|5=^RWF-RllXh z8R)R9T%L99f31sOztwy2(Yp8+-9Q+yb@6T2r^8RbGVPzy1)ImO-kc79mZm=6DfF7SN#AL+u|`(J#03V-QUd(!?}bpz?}mkMu6hwFaS!CKEflNv*uF>6~A&&H@q%> z{{Orw?fpuvzrQ2hzW3jCOB%oO zb!q>5bm8vfKap<#`+rq8wl4hq)^vY3zbj7BJKvS=ALs8*_mA@{>Hcy4gKtR3|5Uoa zoc}G|Nci->a$5?2erLM>%)haa4u9Lhw0}ltZAWkZ-*%_NKhc}^|GI9VJpTXaPlYes zcqr}fdvn@9sT(gBf8jlMq{Dyst!e*ns6fTzzlb#jP7Fkc*XDfX6E-fkSby7U{cPJV zjk?47%}(ntvGtPy>mTddFSbP5AGSuy2ew7pzB{5==!f~g?--HfTbHG{ zKHLxH9q+O5LApNN4_+TWlM45P^?FUIb@|_=!mVeVUUUD{bXu$bSpCy2CtLws|2*j`px$j*KZ{tZQk4xs?lF*s z7xBz*Qv#*@V>C^nlzWh5NtALGsysp$%S4}P^w!QfKQ>3Jv*m@_IZ^SEq6?mfTf7-Q zwy2nb&HFXiSqR~ z(^m`ea+N33^okwDJU-iim^TvAh<&*l$aQ$RYN~a3x!Trsc)fWeq#jS0I_iJNj(C9W zAirJjIhN-_?n-%XKt`j4(W8v`nUPvQ_)CcWcS<3mh=L2vEq@pMEKHr&5B@qnt_A=1 z0(>rdWg#1oceEV+s{#IY!*7Vnd!SUy+|fMKbN^Z4e>XqpUd24 z{|)}}*N`8kqe1q!_3|lvIoKx3d%1j#_zzwq{&xa@J&In%73gO2^EmJ}&VTD=<^%h> z%b&V7{73&+fbZ**q^vGgd_VwyH+^OXe_eh;eCb07)0K6r&_#Y8`^GGM*B=n3eMIRp z|NnO#KD?S2%a4j_68vYI;aeP`A%9vQ{jqDr|HEeZ2)n`PvL2cL=kPbHez@`Ezm%9L zYJHeu$t?VBK^+0beytC`5noO;8rJ`HR@Ri*3H0BhF<<#4T&-(-INH8`lrHlBa5!E< z`uyfUqlYj4S|2`#zghX`eu4iE;a~D=z5F6P4u2E<|KV>1KI@IY&EP)*ylbKVUx4rG zb2IeW->wD!53UiP`~Pd8|41lyX8z^Z(7rC9^^rH>)6w6=eDMgUPlwO;mtPb59G?z9 zzdl<($aw|*=aE7D{c03R|IEi1WWm4d8u9N9@HdnHa)7^?{BVEn^1qq-{4U_X>l(`Y zAArAp4fq`A*Fyg{l>TPtqeuP}_^!OJogMF|)W$8h@QGjRqpTB$@9L8wH@H4TkN>qk z`Z|0&{7tmiqwH_tco$NRHeFAfAFYp44(RAJKk{q9Zxy=4|5re8_zn3%Sn8Mo_}m8M z;%DTCa@LLUTOYj)Jm%*YHi>WZ(WC!yllY$_dC>psP2v0b_pz{j8|ufGxAieSMreJE z#{;?LMOY91As<4QF6Xmc{Di*boA@U-hcELS@n<%L4G_*@ z8~R&<{{#43xAmc{h2i(>b9|e$1z-HNKF0CBZuqQ!&XxT4YX0-}1CRV#ANvg4s__%H z9n)xB4-m*->tp{Df8)*I+y19)+fvyYQ>nUcUxa-zES}E5NUEKSXYYeWnp8MjSzfpZ ztANftHI-J1*uwIs@+RA@oxKPPGHMP|0$YFJ+tvN|FRJ^mZd3OMSvOe6KCHZ}^#^wu z{OinpgSc(`f6V;*%-wHpUqgQ9E*=!apU7L*#p58jKkEWQDm-J;9foI#g;L=e;)^gm zOI(r)&yWtJ!ZU9 z9dZ)xA&Q;|LKDB~R>5#AqUo>oBUk04^&>Bs`xoZ^Z*y(k@=+N#QR_#qQ}{=(&*~Rc z-omR6?6rMaB5>4Ag3_IJu`Y#C?8Bv7`Q7xS?#o)AT+#HOykzdTnEP+)@CFU8^83~& z-Eb&q{JoYTy>tqo^Xqvv?}(IF&MquBN*|F`*aK!+OIerPgWxPvA3<=I$&)ZR zyOX=LvjoLS?9QzFkwGx7o7m5I^q*_J{O4b;^Z&m2f8WCYcRKtRUTc2Nr{JT%u(vLJ z2>I1x239c4dJ7)@)WQ2gxIZ?Sp0D7lm;`0@JKWzvJ9Og(hWyYH40Ys(c4q0#p`Mg- zKcsp1b>QC_{F}o+%qh_t{;lI5`0Edy6}R;Vm7li$%AcwKSH5EI->Cbvj-Sor+dUME z*ZTA=M#uR3(^`+MPmc<`tz|^r@A>zJ_jcZGs`i)ptR^l!tCrskuT}{H3WD$&4F6Y_ zR$;dVZ@^j*;kA0 z_cWzk>gd;6(FXi%6m7$Q__bW^imvcu%jdXmlhp}f1B7k;JlAJ3(|EiVY}f+4D-kM1 z6op|Jp671z3?n1Uuk{N$541k>)9QcbN6fYH_{^`H|Gyc&%>!SUH~$CKz3#u8`xV!& zcyp4P$foz4C^@Qef(zbI53~PwFaF&N5+8qu(W}}_OLrwa&G`n4oj|rx!px%s^JBnFf=cT;=&_7tWvc zXE3iRtn*?8hh1E$V8P2@>+}Co-RJ(&T;q4oix=UxZg}40htI!O@t)shuJN_!ZTJ?4J1xn*-_%&nNaXs#Bd_4zgPf2+CQ zZti!P`#t7bKY9K!^MAEDPYAj1tiMgaAr!GZsHeI|Put07hJ1S@o zK)-vfJbm_Dd3g<&xwsxwg7U>mXVZ zz{y&mW0Cmf0#bRdjQ9R7DKtA@)xH9r$UxqTr7S~pNI$^HVblpmcb3_KkVb#b`qq=^ z%F;^@DZmCs_7vwy6gy#1h(FxZM%jeL<34zKnM`-4a-qU!--2*4A3+Th@>fQ1HEZ$N z-mttoE0a#v3s*jmnFeHIdx;iu^nlecu&rD#iD0hiAqxM&oZPE24{Cdqh9LIx_FmFM|k!Y z!o|;9sabem&M@-989G5p>U~+d8cA@eKg%FjDm;51X{mQ*nl4Dm7x+Bgm-TczdL6du=GpaS(Xb!`h88yU+Tt!ze)Y~=kR}*d{3UJPMf#X zlXcB2$X-vLD9**lqfJe+SL(^qFAM2s*=0$EH?N+&&6I0?&VEhuhuJaXW?+(jaf8uW z?9DP?m&EUc469lCxIw*9c+=t6+0lI3=qzG*>W?)dv;T#v0K_jcBWRb2;g&i);9FG4 zCw}CNe7FCHo7Mf0?kCzmqzlLP$6qw~fVua{y?mc2B_4cQOXRZ52}DpAqw62KVdwQb zU$JxR>$aA*wr-ia{_+hsUw`u}Zr=L(t({xjwoH?jkf53%?hsPqxmd>+|=q>r7x=u9Iqa}R-Ip}@PQTt$q({nEr;bv ze4_4{&S(Iaj0S$>6ZK+Sy#Ow`bpZnB4PIT(z&z8k)PcagAP=NWMK%p5sYG{{8Fvdj z$v*sok1}wf2MbtY(w$inxFBB;gJcNh!;T8~f}qM0E&F+;&s03rr}JZXkCi5E4l2;W zeZ4nq|1(Do%;j`poz_$8$R4#8-rb+2{fANM!aMR-=1M=)m~Rk0^TK=(o;N4DT%w;j;6rx)#^mA zUe^y5k3ubPJxQk|j6?j((-^>d&LV#HPkC`o$2e&ST>FWVxf6Uw;SNa#S-k8NZ zqwa^DYji$l`TJOh;XSMFM~%+MZnk(9{$a!ac-7zh9^*vPW$%0QF1k-eoLO_fiz$w($SEzZNUW66y{6*r)DKG3*K#Hm9E_ze@ z;zwoS>Amu~g((ep^B?SU;>pPwE(ucc1k;T$xb2_3Pkim495C0$`6n^-@N5667tQ^W zxOsotf9h$)`^jhXP*U*rPd=IQ=Xu&c`FJX*{il_F`%kZ>;2U|`f4UU4XR6|zYDQnq3r@mpO-MtI@ixr+r%%hR)XfpAu>rerVuGm-mq zwt+dw85e_I0{<=LY#uWc#a1Ynr!Td&1l~b00VNA#=yfeL-EL=jiTz+rSbfrr=4i@FAC{1P_0f zS7Dgj^%MA6O5uaprh)f?|6P)`R~fHIbAv{|vM&V)K3{|8d(wz;$p%6mzo!HrueM`Z zo%vXRtGWa$XYnenkzi$6Wo@9bX?gqMj$VEnYPE z2h9B`b3bRUp4+r7QHK=qm$W_GmhLqFgt>Zd)wZOk25n15cS+AV+m_yMcn_QVL+1Xt zx&P7Jr_KEXb5&2Ht=gvks_J{SRR_#}%3SR?ZPj;~U;9m4^@q*>q;6$Vs{+XR)BbY< z64L&2S5p}6KX;$=OMjq*-W}?qGp*qDI2WGVFI*p@U_5)fB~2cnm5N#BqFt+}5%X7e z@{~C{FA~nYnD-JmS*9)CjEv<7E9(nN{i)5=8x*aw#aHzNi&^(pGxFDz$p#f%?9CDQ zHzeHa?ArPpoVIJ^){@)GsYz`5@$=y(D+7ctU4Y>;TJJ3r5QQI>}`< zn31)=pPOHrf)rw50fGfh+1W04Ydz)*HntXP6oix)R&m^j`FnY3UKa_l(=JafEzQge z^8@B7~<+1FFmqNuu{ZPqv)zA<*f0K*|r8n6TZO3({1_TzAm6# zoBCVzw`&Og?*69B_#5~~ABSJ(417ep?~d`;7NZ34*D@fEsscbC!TaL?jx+q0Gv<%Y zrRqQn{$DqLY%byW*YddeV{-{d+AUYiADc@!el1@TCyHWokr({7n=gt6?2GU%fIGi) z3Ev9%I$Zt+j1hxy`MIK8!0B%gF8R32 z>u~uSkiStx`lz3+SIj?PZkxE+7o!_~ngZ#Ak3|Y*tGyJWdlGeb#@No6J zmi&4TzgfOm8vedP{(^psVz5^DQ=Kfr6~n=qAJhZjim~OZUl#mW{%&tks_?7}Ad2Cj zo?Eu9i)rF z;e@|}aPvDj=@9>waOJnI4&lUqrTHCx4EPSfUj*~aCt|#354xe<0uj3vb3n!fT z?r_1uW590!T*oCm>-dLXjDN2rd>tSBt;=6&U*hX{O~Z-*TElbrF;HFwxa+kc{MCfl z;WMAa*YORHg%eKv8_n-pkH`eh z{{NgGNeWlpQ^MT{P?z-C&!6)xlh9##pYt`Ez}at~^EHvc*>9g~*=+?#($)MszLccP z{`p)>oWds-`{#2luWNv_zdhITh6XtM+jA{@Q*f47>t~cAGvAM)Ylees!KQ@+#d zXzP05l}{pU6>8q=KU$J%@N{(k*e*EyGhknYqhEP{2uHg|>xSpxN&)LtgF6JmN$1MC z1(-`X>3rJ2AwI^}Kd)o0exAA#TMa2^=O|9Q=+c@FTHSop>ASMX8)TfTch{fk%B<#++yQSsx3aPa@F z1915pkiWviINxnuH@|~3Kg7RM2fx;JubVf0hO8oV_Q3knRg4v^UD6{wwv^Q+ob$`p zEdwdI&NhUfF}S8gxZ+PWz&T%R-7?bvN4)`lwgJw0bL*D51~})+)A@3}wwP#3%p!wBd#L=x@<4YV;Hx^oQsd>+p470Y2>TWBnkU`FYCx zj&Bgo{5&;)5B_5Li?zS;^E2uX;mprd>y2>c=c&ijaNV~6e#PJ>Cm~$&Qzs`OT=H`z zwg}2O31|J(&o6{aey$8O;7fiskdwT{$(@+4@Ug90kC91g^7sbvQ7(*Yhp+4BJUIjL zQC{G0Ue1u>8{57t`G14_1szS!i8o|k6TacH^CyBB*}U1Z)#N*_9u#eRON*kRP4&lo za^JDd{O>V$pSasw-fMoBJmKgMEe~bD(T**bGvKV}$1~up&ktw7S#M8dz*#>}X24kw zS2Ex%*V7qr?x#MR0q1_|*$g=MQ(vls-zNU;EmsZhDwJ^6&sA5!<`T~Ox$6310B8MN zt!o(Kvwp6+h6(UlKUcX>;m_b~N0#gAc7fuLMtqZa33r4v1B6RHx4Q@mzun{SkT2Ka zNT2m{b-VVYkiL{_yORVY^rc+e)B9i2*L1n^;7`*beb)2U?Ju5RxzctW53>gx8c6dlrMzq-1;Cj}>cEmxnx6Z(|HT-`ok z@Pt15;nnRU22bio+v`XQPWoEjqXtjtQ$BNb`-DmKG zKIJ@Dx1SMs+h08{?(5`l<2q!I!Zj1V7eY%#;dQ+;ir%R3bY}yM?Y=YGu9*lH9)#Z* zZLb%<36+LFS?682>R+h#4Gy6b1LCAXh4hP&Gq0imQ#(hEnO=$(WFu&N5PN3QM zH*o~bvp0Ep%h`@5th_qSC+FptRSw*BEZ|-tu0kSqsViGKGKJKg^};&JQ-{=T)!6p_ z-;=Ml_y3W(|I6GzH}@~q{Z-Q|dB4hp+8)wIZhJ^&s%;Pbq2fKHoS^L?-I}#M#I}qg z_s(P6L+qQ-e|e~6u8y#_hwd={pt(8%+8#P-euu9;I>CRZh2L-Pzc%;1=00q$i~k|> zyYxPy{x;o%wLSC`7XCBl{$J)kW$x4Jdi*bl|E8ZEw(#FK*VV`WF#n(O-m13c#&!WN zOi~fA3pPs7pXPD;Uj{@7W%sX{%98f?oLf;zc(!Vv56>AcNqDx}h!4-v?nuH*95?VL zBSt;;7BjEkRBH0nW*R;-n}ldZWQ`Zz4Jo1KdUI5$U3i|AoFqI$Q4yoah4*%B6rQcZ z?c$euo80JS0ZP5a?7{9SbT+wQM+%+ACKm*yj&d}?T?#!pcUfF`caC1A3-51o0h8(H z8H{rA`*Jw53orFIxnd?Y(2-|O%z8;zF{it`Bzl|Nab*hq#q8O|#qV!&$&m3&nXgv{ z{R}UrZqN-nvh0M16fz$&Oe1X2SBrQ>vDsiN6?3%uT{Qq9n{9Z6(nh7|$x$=*6nZuo z4oG2xQZ&2FEsfQiqweeS(AVS|z7oh$S9AnQy?yz2o|VEzrR>YOTjG=GD`n3B0YdXU z6iZoe9|j!?2-!N6E{VRZ2Yf>U{W;p2jzDKI=N_6z=xB0znH7oasYRPk_1+-wiEVKI z6!_~dPSo~s&hG<(|10>86aOc;sCBy~tam=a^a#^m+b2%&L$?F?X#2z&`fWc)|GQWi zxfeUDQ-8f*?|GK|j9H$oL6y%xM?!LqzxH3&by52-S5txQzx=rP+kg2 z!W%BaCUn`Lt~ML*$6EEQi`({zA2k1f`9Ed;*!*8MzxK(tPy7Y_VgF;g(uiN<`?d{J z%eAOcH+d!%9EG8L1vpzvTtkaFbG*gPnKdkw*EAaPpCp`j+gGwx?9MR{V)^RF`iDPw zdB1{p%e;0nv~8oExB_$|{>IbS7^NOnk(se0vta=J?BsR|8rGB z+JF6_6h`}J*3$l8Q=ZoTnHLoQGtXMQGv*#Ow?o{#Kkc8NF-QuS~5^yJ=5*e<4&y*ztXmA*kNr++oxM#A1N_;D(6(@DzJ z{&cBanUA@tPEMzJViuH-;axpB>pK_Toiks% z@UEP@nMru|LfnVvETfa~F5chll=)Wl@B@WvtDi0Fs<-(q@BS5oG-Z1kQ~_gPE-3s0;6bC%8*o>2cY z&#L=*qx*%+>i@#Jxb=V9KYz1^f8OYR{;2xDFsAP3EZxtiW41qY#qi>EK;qN-%e;@? zsndDJef0KcEZ@(pC6qSt)&8vI`-Trxv-~Nokvq(^k`5KJH*I>GR4dlnKfsFSx zki$Zvi+``cza98@J^tN*f3L*9SK;5Q@sIz#7XNnQ-;Ma!ihphRw+sJn!oS`4*N%TT z1TUatJRQotLx?47|H;xN3w1QF2G-z6`_F37o=;8Jb zh!soOTL%p-^=2D1@^Q0=umyMYH=|uF=GaWN*gZM&AQzk?2e9C-<~i@qzj#RAhn&d| z#MBgp82nDwb%gVtlD}Gv$JJ)bkPcgYyYRZ}=DdF?@oXwVgvqJOd}ZZQ(oVb@?rtn$ zg{wctQ!^YGIRMcgOh<~;{ySIYqy2Y2Yp#vY-+A2p?={zDQ@jm-+J9$2@qYWohA;x! zzc`nIxBu>kQ~o?p`xl=`1+{3+JE&dIfZ-|kZ!ul5ihBjt?ChIIlYDe~hDT;9wTOVJ9# z3C`VB`P_T=83#(ad+*lRd+*+w155uK1 z$4EnhQOpqqB^doVa+U-GG%_Soo7s3wPfso$oQH^7{v4Qt_0nv)Qjl0Nhd~4=y*bv{ zLX@04paDu>&if20lx*Ql3MHFSCn%+^4ch>((PnRr5-Pb&JB6}QUW$Fa8BK{jSU{x@ z2Pw_F9p=G}vRmp=6Um^)U3zL z*_R`2>5CpO=d7_FuQSK0W+Ps<$gUBuOX^4Rp)ExbmozkRVTXxA4mVN-9N<%STdO>1 z`WFelaywVg&FZsusfOnOq)J&dpRTkcMA#CTY8}Oic3uA=ER@Itw~_hz->&OvFpi*Y zPG}=xyRQFFfVUD}hySa9@6Le#4&dLA0e=qg*8~14GGqVSbpur(-vGZb>;S$C@I4vu zJ%Cf+ryjox@HYb9;qu9Pb@fq}3Q_7F&SnVUu1>{T`f7ECyLT}$S<$ls8c|nzR{3V- zv1vQ4E?=0f%wxGbEpLGI_3OB*0q(j-v-s4~(X3$b6k%ozzrX&<-$p$2nStG|3fAZI zm2-1pL0CT3_fWGrZs>xCtjgNK-qoIm+ zc7}>8#h%XQ||vg2C{;BkwC{K@a{*I(?CKyEZ0nRDf;U_|MS0y z{?A`Dt-nfjf!1Ym&eK6rxUOM!<`~;+K+9MuyyUHpmY+G`bQrQWme=4iSHcd;B2NPA z*Tbk=-jn4Tj*>C1>t5adTknZ>fAcG^?E2#OuHSO%s{_0L@Jw{m>h004?SB#7eD?={ zBD|6;i{n99OIAN*`Um-^aCTWFQkeDe1)F*Eu)4CmG_yLr;+npB{Mzh18iJ4BNSVN& z@GcmD+5JCBSH}sWVzaPO3Z~ab8$0+(I?`c9;jg^7x-dnKCvamV;jv9~>g?Yfi5(e@ zkIhk%VTahKr%gdVrtstoL;bpEirGx^iT4y>eGs<7mX=n)k@0jxYJJp@zbcH!;7oa; zcCa$FP+%{eU%OD12Nr0PL_Bu>TAN?e#T3%p1f991+DdJ9SuFCg>a$J@8dI-}Rf6Uu ze{)7jwpH^>ld>2L;hLdn%eHvyKorH>*0pm+S-+dNU3K71lmpK}&MrPEGgOxPS7Q;v z2pM~e<`FXFtxY3jsR1>OkY%m2d4!D3e)9+!TmPmJvev!LBV20=5W(or+J;XKShJY|XaqIsr%9ba9|-MU9mthpmZVHA!!> zwt_)^&>P1qL9Yh+BM`i977TNttjiuv4|=@Idf?N<*}8@ISFVFH$=DT% z6CJ`*lfOgXjDPq(ggNEVH*Z{C;V!})n~oW)9gl5qQ_`>Ijb()@-efg_^&plkL=jfb z4-0I_*JkG6%Hs6=>ddSf_npJzb(_iY#R~OmF#n46#I7s>Wzy%Zl<7EpI}vi{k|-XS zjnWz0_y(9XSt>RlVG7k-?*m`lvSsVmSehp9&&@lo1VQZ9Rq^k-=&mjW!I1TU&uaLZ z4TUgR!uMIY_NP>^gzHG!wWj*&jp2P3zcn2sKBm8B;q5u$XDr;^_zKY&f6l^h4Fl|( z;g8Grt~Ktd>VFJ>Qh>YGbiJR(VEBNAYdIUk@3U}OYovpve%3AgEp=h^lKy3Z?^+uY zH~ojuwyVg#ngT%-1_gXl{dc%+54+(VF+L?w>&Hslj z{A@<}MxZSX2z$?k^~~@z&fas?AP&RQ{dVu!7gFIn0v|v3o_!*KY{3a}9sRRu`a2rv zchtqVsluRpXT$UZ)SS;<7yqk-?EbDFHW&RN;lIMGqu2hH`9JA061=yP zE@wkMSDx^(HH>{B`R$#nk6+gga~%n?)Nx%soqM4!+@@ZG?md@Izo8xH((xPGajrgo zUH&Q^LHZkgHrC(n9|+?xJdLyau`2-r!_xJ)`?2-9@O1umKQ>Sop02;$j|IV!cDnv{ zKYArVU_Vd)c0alv02;@^?neje!t3(?Xb_C_>&MCNN3Mhj8+@nhj>x+oSr6i5)bAq$ zb>VgSdn5>!`l+kmhpz+(q+eIR53kpS*VXUC1F7(a`h7SEmikGTarfmb0Rro%!~ue|exTye|JbtpqsDKGS(4=I##+1Q9aoUt|Xkh2eGi|Nbj35H7>(^8fwob>VgS z|Neox@Vff{{vcTDH}oU_-*+VdWc2UvTdxbR%m4Qc)P>jO|NDYqNxv@t-+LuQ*zkMT zr7sE2?(cov+$WUxzv6Vl`;&P=aKbv@b?J``7Tx7OHg=OT@4ECS4*jLY|JL7^_`5%_ z{?g)Kdhz9@|DbQ5Ye?V6iFQ4hwSIM3%5%f`x8xx;frs(;G{MUY3fk{McZ~WH+uya{ z2M4a9{=)R%>@xWh;~$pzyFYN{8sjVH-SyzHjP#km0fFy&5aURH0eyxa73i)9CoT9T z*58Ac{|~MQ^c~4Om+8CwpLa+>{AwtBr_pU!VR&h8&sCz|~jKV2WYf9Q$} zgv)h)`nUIdSe}f1z>l5@A~3v;{trJMgm3iu63X{u0|C*-^6j0kZ?6CypBt9%!)f~U z<=cDyi8R4{pXstw8`B{reRtQpdG1{QW4Ntv-kr6+X82BtwCmk%^^xIc_{|ocwZ3L} zY~kN1Zu$?y_gVO!bddNMuJya?-EYbX?-Tg0cl-5q23{VB^!2yv-M#KJa2f9E=Z*mG zJ{j)nXT$*mm*K8{u3_5p6vF)dI}c4h!u)-A0`^~F{w{lH@)73m2NUq7zry_K`B*w? zn7_BDgTxo+?@Ug(tG{zO;jzGXy?Zey-1V=l`HShh{`Ib$_^yBbn~d;{Kxtk@?A<@S zQJgS5EB-);u)%jw|NkF%Zvz%p^}P+xz#!j=Ix~)nIw~p_DJm)|*{D=hmZ(^ylqjgE zC@83CSfiq$VqubzQc+=HlA@vUGcq(%EGsNDEGtS>EGjH3D#~Z?v(_+&nYE;T|Nr|w z*YzB)aqs(H_g;JNwLi|8!_1kL!w9Xk!=YdClP&u%QmTEm|FiNt&Htjjs(1JImWDq_Pv`Usjph%*ys#6_tKv5}}s%2(YjCU+Uk>`9wV&MfImJ`u(rR{~!Gw&g7M>)NkA+Q`uj=g5f{ndU0W~gT1w;-$L+bFE3+Y zKl&{UwgcsQyLF8FB{n+Pbt){c$JO<>G7+G4+oYghlDfRKx7PGaa;)E>{fivSJG6gM zvZ@`@w?q3E1v}W?R9LicVe9%mT3KcLTbFnGyW%3p@(%4wN_OygJ=i)nQrT~@x;zoT z#jV??{u_W&KDl-K9N#bXOD+p_;4mE~cE>K{E%=%w9H z;q{MRVeEK)`{*^sZie#K4urieN*!P7`k}4z#QZ4ER}~`W$6kwr-QEUiFz!{E+sxX_ z{!gz@Tjs}JYn!#&1;qT=TW&YEnUn3u`C+BrUrPTL#f#n=uB5I1llBO(v-2ks?dMw; zRgkRTs^s|V$4s>UH|tL|KIHX_;`%!O5%cSHt4dGIuh%WD>=0c$WqjS9hx<*f>oD%C zRSD$%BzqsE!MHD9Wp68MFZ;i?f9*H7RjJBO%+LL?DtlX5*?yd#u{2H9XkvaJY;v&M+W_T5TiKoF|G`oR zd+RnB54860H2)8%%d1?G+19?L@^{;|i?t~1b?U88D6H{~e=~0O z@-~;oH`MdO#&W4_KkkdxRv%Qu0@8nklBYkHBJFrywiaL+bt3K23ZY*~-8Yx^Sg>37 zE9t-cPZfV_`M)cws8!`xw*1}qU-%*Rg~JJ|^3vW~V|**n!ESGZG#Jb39qckj_QP0K zXa|b#*6%xwWf86Ht!tP5Jsj+<6~;Fm?GEibROwLOp?!xk9qbP6I~1t0w~@7d=c7et z_oYvHwuQB4q#fF?Pg!lJ|8dUVSK5DDj@0GkM0_O>Wh{8z(uPFp2f+nwjM@f~O{=%l^1#`q4z z%Wk)~L2|~Um0(i-mt|Xzk@0AzgWW#IrNMYKu$8@a?ZSSf-of5lVJvsFJG8I7(4o9T z`^pm>><;ZK_fXl}%G%4)`>%Tau5Qa2VYl~<_D>XPMXAgGH}%_N*K(icNMNfPt`F?x zZ93x-b^L8Cm&)=m9{hbeT)*u5T$AGbfBruAzqt>injim9?(eGkMgAR)8&?~zEkgtR;OuPAh|JNU0i zP}$qc+ROfr@3WBik^V*f3F|3%o_qSW)_zuBK!&s|xY@qP9Dl=jvd<9mUsMoF8! z4bosdp^h)Heohp&D(|#@PPDEcqH8Z=Jkh#-Kgae}wyxjLv3-@T>xa=%`LAqUzsbqJ zx_)~(I%kiKe$CCalPM9b@~71evi8Rw6-cE?CSFBxDry5QkPF771t-# zc^2eSIbU2cn(&JF8;4&VTCopSq5YGqv8kq^e&^u1f5@zx7LrcCg#qpp;)y z<6xJ4v>*MJIQq9=14>oDC64|bJ}0omWOvqP&fYeC#$$w9whhNWV+&!I<#GHob`bV| zPU$ngSH${^zad=*pIg?i>yCI3rf{CpuY=E}>es~~zZBE;m_CE)9!$$Ig?XS~cOEf( zzEHm&K1Z!z5BC}L>u=H=HJfk(^i;I+KmF#n1>Xe&r{=2#-xUW-&0iLLHwdCe&Ht_W z(PH!8-tPbLj)EurKOUelA2OqV`?aqA@!^8E>D7FMriQo#}RXZO~*StFQRYL#RoH;4-nO@>Il2VthpEiV@gJ|keKFrhr2R1;uCfPXzK2NV9FjidYovH}!@TUL zjhE?dD%=6{9YrelAe(NK$QuzGRJ_eD*TZ1s$N)y1XqIzdiXX&>OAm-i^Sa*~(x_o|{yBsL1z2ESIOvr!AF#cadvYVmWPXyiDcZsK&OuzlxWC7a5NDMinpb z|Fu(LTUm7~_hL2vZ}PVaA9B6Qvhq;-=;eUbHko%-*+W!$M;(Xd9?02jeAqwG*tBxH zvgO-MXNlkGT97E7JDfOU$;{;~j|JAKdGXZZR#@m-o;a-1wQ!<-wSM*z{4F;JK4qbN ztT%be za_12T5TbwhlW>TYM>ebMGvgL0a+_X0eoJ}s7V0Hds@hbAY1w?rINSx--gxC(S&kw;@Pe<;z-1x;Wkq;D3QRa(y)stVW)xz&qZXptNT8!zViJh5l3oGpGMQu(T*cl7X}w;OIa z`Sz}{IhUWRAN}d3$wf1hE{@y!=Cl>hpNxHIvn(wR?}yL06#Wts6*K0u3;XM~P8|N{ zqu(C>Bkb?*=XdjRH^_zCJU*EIc?(y3mU9@iK^p_jsQmRm=Bpz*rWR}>- z#g9=8njODji1_`5g~>BhZ^p+%%WaAJafH@`(S`%+7s~&Y1MwTZ${|lE$qV9AMu;a* z%Yl)2=F-#z@gT&MxFz$F#k?P)>9{j-jo(Kn?%Ma*VeOrB2ER3Q@*?k;mrsP}xS!p< z_U+2I`WY*ZOo)8aQnYLA@58r_4|Jb0=33SBCnDBe*?2lJxrcU7`!SvsFGo&%zFT#V zn>HNT{=x(0JL_^@x$Ch5yA3fnH7`@4KP>XvP{y;SyD)eUoJCy92B9XCGww%hNBxbv>NC){)I#K=jLr$kM? z@BZiqrp=rc7e9N>+=O}a7bGrRl$5-9NlNO{Wy{l6tPCDJWN65+;Uhvv-ZbdZe}0S+ z(p#b2sL^4!2xJj2jn+l^iAoPoFK-{;8`}GI=-5ebFm^U|0nc5#`FHQp)7-0fpMbvo z`UeiU@ur}g2M&@=Q0$@$@_Jidd&%odH}SF^__k3%{rMB(e~Wlo#Y(~6 z9!p}lPp7ZjE0C-g&SCnxa*>yZP4}LH^mX5eRQB7Z`&r@by1zhoLB3a%aYx;Seyw#E zAinww5rX$n=^k*<#VJ^S0aB z(gg?IWzf~b9Msp#_oAYI_456@JaF9V<=m0{08DK-RKYF%t(Wg}q;4Xn<1l?tN$t9s z$S=ioE~Xg{I+&mO`e)GoIHo%tbi0s;xajNOMtsykcMSP6n0}3EorA6(`QI@80a6H; z$m2Y?tV6yBrt%(x=>KJTA3`2B-3SHgFOSDm-Y<~4JC)R~y9fChm`=eo-9cwXJ{!~Z zm~K~6xYr^5L!9)NOBKG;jy|Y6fjV3JK0qGgpucQ8`=hLlM1OHZYkwDEf6!knuq|Ei zfAOG$?hx`a-VjSk=r207?;^JE;#am?t61(QC2fh{#b1HHB=5PH(I)&|g8AAqpD)2a zqQ5j6ca|3EKg#GYDA!;@EB74X|H5)ialT%7S;2O@+!v*;9MdnA)Na3o{O^!nY}eX; z5!dC#zG#Q})KdPU1$mq=7h!$b?YK`}w9O-Q&m{V~o6+J4e(P}l*4-wswxyK5?oQ0h zJzDgyZW5-kB6U&n@;`lD66W2MR9}bdzHXJkFb7faj}ra)FHnc=JCE_Jlk*(vkOKYr zD-OCp6|ArOPNcRN%l6sI*!qj{z982%^i3Aj*L9JJIBYsxPZwmrVJ;}c{yqg}3Y8Rj~ei zl}P_>Kh*Q#HpP!A41AbI!~SFnOF%vnQ#fy!!pwk2H)2|YX*s5a<2DY;T|6Ru%YpJg zKe~M2^PMB_J39S=D;=&pdFz^j-Tn`qu5o`gx8vIB+JM394o~%2_0E#R52jkb-1qm6 zbM0O^vg1l>-Hv%jems1~`u0bjJr>~e!pWO22PNH=n(=B(@vd{re$V_Ycg&>8g@cbR zEOzZ*`St^k9zRy={qyNpw%pO~y|_vG0R!gtf5U6tz;3-l?+IGiJSEh((`>JlsAcoL z-duh3#K0$)1g(4V?fMrAyUw}$=-r!=k0(xg>C}_;UCunyF5v17!`AuD&rj}BaKzYS zM50Ikh~*PPipvg+^!IuF-kMkMnx6OG#DsxKKc_yi=7+SMaY-4KY5J_=E*{y3ul-RF znfTq|-(ULe*y6OGWdA=6+|ygepwEx9Kag=~xM%br8H?2$J};B;_&MT0sEo-$x2i5O zE_=I7+#zH0*}~N(8K0M2UB<~6-IknmSjOqzy8T%)R;RC(d?Vxa)59B*WXwhzx9yX0 z>wCQ8Fd4g#=Jm^w@q6ydT_4LBhED8zUdC}#*69N>mdlIsX3Ka!aFfeY8Pi2C*R7Rt zeQWHW)iSngJ4`tsy^GT z?zOIKl4R_?ZdjBocxaS6t)e7?q6+*2;0JYI=H$9IJLwhl7>z`f;*Jj@i`NyZq$1 z-M`p;qa3>-Z=NcZ<5&7|RizxmNn>|Uk>mK)c}kNcbD7?1BfC{~X13yr&%$g%DkKe9oNcb~W(fpW~hT=Ujt zIqv(9tuV;3Kfh$kP4D#|^WnGsX6Bt*UwXV(`BC?8KU;O?gWB(U{IY7|qdzPxHVUI_;=qu-4=Tf>*Rk&x4_Sr|I$adDr3ldyC1e357-*^ z++i=niNj^%yU(r2^YfiB;oa)P?~fUG@PS7MNWb`#C3qZdV$4x%-rH$;05aH1s2zt_UZ-xP+h%7 z-_%)My} z){0bU6)et;vJUz3!17+gr>i!WS2ms?XBViRD54mzMm@JU+S@JpejE=mA_Gyze$x3Qsr+} zmFWt*r814fV?V5X>j5G>cXFC8~UiDxa##FOzv?C(&q^tN1jTheK<- zW*1Nk_h}S^R`niCE8qX%p?$=b6Z%JG9@}YE;t5mxcMwDqg-9!9)9m%%CP)mEWS|6@&Ik zna6f+wQFQf9$JlA?F!Y$f_V-bS5b<{u)fwLqBFl^OpRIg|Rbcx$-4OD&z}_~q z#xTrNARiUJRbXEgP7~Nqg%1hbL505+SYA`XVHoZwusBz>;820}HmETSe@I|)&Tg@< z7uaZn!oEjfvkKP;9BjuUr2oMxY!P^f3a=E{qQZp&k5%E{1RiI{q2U6LSK%!JN2>6r z0#CBzk(~vetisU(EBl|q8Ad)Wu(AgtJ|nQQ4+0)FP+(;*L@fKS?1zZ|Be1e30v`RK zz_$13V*ZTYF7PzPCjT>xt`a!bj>B{UC#&!%W5co+u9xK*{r&fi_0Gdz$W@ZBo=UV$g5u&iH<86JjdA~ssl<`yi=PqsmgVOpWUBBm|& zT7gyj{qzn3PqmpfhUtq$Ud(ws40jiZoEXCv{H@5XwLy(xf?OLJDlC1kQ{f#V|CkDk z`GmhoCrQISqJGT=nN<$MJuoHmEU7oF(#NOz|*GlyyICgTgNB&$Z)7*&ned z;$euqRpew0;4nnW_UGB4#t>O2^3U4wq`o3A)*v2+Nph_7ZBS#FB+o@JsIb_-G%u>~ zXCg1w3?7Ebaxao&2!~iMGo& zUFT3=|5GyUvKH2~sj4L}Gp5HaqWxBxBF>Hu2=hhL8{<=9cfZXt@6I|I(9jSu5)O&utw8nt7xBnKO%*y{f4&RN75~%Hnk;7 zX0#8GbPEaZlUtxuMz!BL*(9M&Bk2}WYu{g5ICVxFuSU*!?N(<_&8Xkgs(D*pwLjaa zR;l{Eaw`J@)%l4sYqZlzT9T&CVsFFu!Ia3fxmeg^D3hJjjm6GAeoV?#=U3VuP0}r-_MVn3nb8g<=@wFT{%+ya8Ms%VW?JX9_VZb510uMe zLYfp(`yg+V7OkZX`q$}w_l-!CLTVTCHfhl&wqdtl-;qMCyr#U)p-sMyQJCO< z8?VvM=WWuWUBKE+j|U2S)^+TD!fwAlC53SBjx;HxHk-Fei|YM|qtLXuQrPY9ryT7H zR=xkB?R`nQg;c#iYvI%x)%!Cw-I7#ozH957Q=#LYcB^gngc)xdodx z$@Mv+1v_R;o8$a|{rW|8+&;par&TZP{cc^?&iU*vsN z_;ZoJL505&`SvO-#!LP8fehJ}4t9Hne81dLg>SUB0Fg0d+$OLpUK#fbtcq90Qh`4-2$uPm2q5PRlG7T3apA(#%}_v;ZDlg_z3uEB1+;BTA}Y z_^2$Olsq$WM*Px+$*P)c^^5PN;rAZF*t$N^5{LR)+u_@6&h51QSff&_(iB-PEk%4Y z4BOzM_0+hDOEXukI0cw(pQllzB0lf_q2xLqdl1uPC@a4An;f?!9%Q=0&XqFf_Hp+^ zGHs{X^GcN~{`tn~D||2ylH=wn%WW__4Nx(xEx~ZHorpvBORVwPs?dt~Z-Z-o!?sjK zWZ1Sw>HoaAlo|8ll9h5P@_W{*?-@h;65{90k=sEiJHc8M#1|tSe8>xri10gamtR0m z#Go;8SvY%P(vlUT=8@F5O~G%AX4nXJEw*Svs4p5j_z4kfq^@$(kSZxGKBat&(5B1=)$B-6e* zDe^ovR8~w6Bnm>3>v6ra1Yb4XF2jKF@-lohCNRpei71rC6K;$7Wti+ z9>BCzN%cR)B9_;&qMv&{!xZ~l^gH68F#QYCy-K3duj`7qJ*K@e?TabI!?1TUVvNUL zh=*bCZOBJJ`q2f%v6#X#HSArCcnzlOFx`sjHYK%vU&^p|r-JocGSI#c)1#Qai|MDB zV!!wPh#31@>;kyh4O1hgU6fS+i2-qcNY^SSB#ogM=255Zozaf{E50A_J{vy`?Qotm z6yf>17{|Xj9qo@oT5=BY7EE8jv_MHaT2?8kz6OS_%NggZ$o?%(#<$O_nkrhOH9AU6#Kak#$TWDC-D1m z{P*KL-`^2=e@x|j35}-HYdCNA2O>Wd)8Uwo!t@qQabE1dTfv>Oi;$lIY4!%hNtiwY z>Fe1_+9}?G{Ax^XdfXR^UPpclrumq@1nKfuA>CtZ7t9mGe%vSaA4mOrnATwWIi}w! zslE%WgN{Df&OLk4j{DvID+=zE@eJ_LEb;xU+Y!V3(Xaj$xUFideB9EeB!7^XPC58(C6fdk0n{&fJ?^+6Zl54vO82h%`IaeW@NVT|iR zT%QLcP-nyUAjWxj5aWH&hB0mj=b|nhQ(T`1pGKUADaP+0#_b@^kAr7y@6W`AMXR?K z%=Je~Vx026MqItaA&D8niM$?5nK`e;2U9*Sd8vGC$3o?aGef8c{J^z)E8XDWks7>~ zi<=pjfbW4!nqO}{UT&?jVoNj{EB>=E7MlW68?u#Br{n&|9&D|WaZ%p4L9d~$hC?_` zhc+sN;m`}1;<`9=1o3;AevRq3kiMxT8pE4#kIe99PvircxQj>gi0j57!(bi?v2v|E zLq4%5e)h5y@&1Hek5L*#!)y^Qg#;wATz4&wFySHG_Pf7~zOUwKxf ztzTl_E-w>x3K|bX`BC`FRtx?}TH&9x;$BS4 zVK}1I_G*Rww7py5ZrVUG9c+i;NIz{TV4GJTZ!p?!*otwJgq%a!A~l&6;kU7H7r7Sh zW>2`=B5ZJO5E+f(qa#8;QuKX*gaUsk@FXF=5wW-)ag7of&f|uU>ID{a%Vh`JuL>;Y zpo^R^G0GHfrZB9dBkE4x{7nO%U6g4#N#&wCtu<)R1FdNF7Q&YMYJba z=yBaw-7D}I;p2pq3;i^?HsGb;*&5Bg;#?$O3KlE{B@eqg04e^M70xZAqAP&Q+SS58ah(3zLa4KI(U5t2sJ0;>P4i{s# zk{V8Z1K3;u_+ywYQ%8fb_ohcybJ6> z=McXScp$dt;|9RP#5jt>@JTV?;n@FAP9cW47(T)NhDIs8p}G^;#gvhUp&IsAmytP$ zrzok*s6fOb{^D@4Br2()x=Bf0#$kU?k3*aW_S5qKkB>$SadR18iTDk$!{x2Ua2l>h zTyBp5`x&usio@l0Z2y@cz!8y3YB+=KyBp(m2Clzc?#@E{3!uLn;$%1j^}0;3p#4X% z--G2p6??5XT<*zGQp2ZkJ>zmuJ>qyJb-CB3-voGKKHB#qu0wnla3sd3Mu8fa$aJvR z^auN-WW-`Uh{I)4DPp)Tb(stW4K;aSpVo->cK}Z>MEo7#m>9rkI{|($0`X|T58?Qo zjRQOb+jkcGGXvXq_I0#lf6j{iLmVzMtx9V64CnhSJ>r{`)FrMMF^*4sE#im4KHG+0 z0zBUz?e8PbK>Rb{1^IwK?*=$24e&WD;AQcMptOvFzC&bJ^w2>8Vu#Ge9w1^e@56X4gaVE+p7LFk8}R_xW{aCrmc zUpoqLDfGioJ00+U&|fXg6PLrW3U8==7VJlI5g!J8Gz>AECtTi{0Qei2moD$*BTiCM zmv;*hZv%YHA8{GrZS^B!RT;3U}34+Q*iIpV2+KPf>Ds%-@|_7x)Iy|{oi1hpGOgg;fLOctx9V60Zufoo5~T-S5nuf5H~`*U7yDI zHb#T}=~U1+!uiGZ=@P_wXupaW=DX`N5r}KS{!B4oyexO!j{dJ;`%3g^&jo!6q=qYS zz2W)>%xlAyZ^3@35%fQG0$di4I1TXIMTpVg2iV@9{sZ<8aX!HIs&V}=9rQm#yIs%L z0{%M_^k1U>??S+}SpVNA0Dq1B`5W8!bpn*X=8g7z#5n%-&`!g(O0ZwX`Ejit@Q=+( zYQ*1r&~-B*j#X0KfC9wO9^H*r#LynyO)x)<8fcF$C=l#gA0^cVWg&+BL3eW);z+RH zoQD|Zqi&!XaSqrAW*~R!co89o5}d$nLUrU8CG74g%6KhPn@@&6zU z@oBJs7>F3!rTeHBaA#Prx~d$+LzGlkUjo>qBpTh1*k4l*u>XSbHbFhQYk8=L@pMz3 zWiNh-pzAP&ck{w{cZGht`PPHJ>oTx+EkF!$a5IC>*cGl9-1jm2d!l`rA7Hat zN!^|Voe|sZwyg>6OTqq3HsU7%zg&m75%4P^fO~ZWY#aYxz`N~HXpOPgePG{*{(3D3 zd^A%@jlFUKzmtmi5a3E|U+)CKpT>f{_jJh&PxH%c|Q6+8HF#z$`fc3$E1GI>7y$6^9n*-4f_W|0O(ORTJ_)oBpFaz$_4RELx@o2>8uitdQBXK43+CDXFpl!+^&Y zBi;dcoRVma{b7E#8<&sv&%i!jhxiY~QGf%xBF;rT3UGKR;%SI6-hnFt--iAIw*#Jt z;~jViF~%qGGr*H={{BFW^$q9>IJ!wmjRQsjexMNXG{kj?R|1}f^JTzx#3#{y2=KfH zz(LTicBvS@Ac$MLjH_s`0{u478G{-DpUnsR&FukymWOx{;PWQLuphQNpM^LH?O6ZK zPbk>k5Dxa>V8C6?h@rfDcZ_c^oPXSV;P?c?`NzF?8tUQvXq z{pEfq_%RN-2kemvs80m^5Vmg^)Z;!Y5bVQXzjvR7@f!yFoqJp@+CPHw@tJ^!cSc-} z7|OXrH5%h^>~CTy+Mfgc8Xe;I0dGP)q84$!k{UzX0e;+ug8*+ihjxtbQ}KXDHi3N` z`Ww{~aWdFPVSk^_L5%Ht8p;_*Jp=k3xoC&|+&!-v@MsUP?Z0qXlUH81TtDz_+5m^L~J*nZbTO8}V4cb?9%}EWnqV(OwMrN-5%cz`w-; zp6-jd2=NfWe?7?baWvpw*xrZI5JSI=59I<5s78G$;Jz52ht2}-Z`1z)cu>BQ8fSC^ z9E|Oo5e9fL)N7mp?erLujQVu2-)=^n2RLE^;xfdUh|dAO6WVQ@(Tunp?6KVeNBJNg z19%$r!x#(K10FHh|5&&l@OTjX8e`#lz$3O2%fAifXZjDyc_80OGq5S0GLV zoLGZ+GvI|mh+jjD_09Sa@SKcPi@_cT*V`V+*#0=!Pdrvx(VhbORp>u1 z2e7pX?FRs_hBz7H;C$hcnTqyb!M*|g$HRWv(`F^IAL$8fzQ&jtIYT(qwTysZlHPQcqk z0H*{3e$@vt=sb5fApQyLui^NoLh896+nYKZ>~GlYaQ^Uo1M5$H3GDA;e^ZYEJ{GK` z#?;G*ivcf%c6c5`|4Rpf{dg$aVSalaZ$u2|d(RW~h@oE3Q`LyyR8r4VwTQn2`~|jm z8SE#XUqpj_8O#IE3x$a906n59#9Jc?_QotYOfZdAqox=86VSn;El@9jRa6a<-#0PP>l6rd;0A6DPY$`z< z4Y)@pVyNG{H}-c8yg%^nmj?E=SUwQ^8rP16@`JEFYfAx-(SbfgNi^PL>JYVX-!tfcgCE$RuK@do zaKQTzKW9;}PuWSZe**RUyx#=48sh77vKDcplKOlAsqr+NpL|Y3J;u`z7oXF0pg+A5 z^fea5I{=@<`cFgre7@A7{VTA4Sq%70F5sV%0e@-&d^G~`y-MnffA2AVnh&_0Kic7X z&bM6*;F^wT2fxM|xPI`(&lebLGQi#$>#KqG`SvV?@@K2S-W%ilSqH#@SHb?-WF__M zT&AR*eQE&@$OG)_2N8VYtlct7Yj75sMgdk5^(1Hs?a@a7E-VJUz6S6cz<2H*4YU-k+ zI#mbup1`}foK$cZxSUb)@;?{X42G|wc^#wI)ie5Lns1jy^X?Eor*?XpX}xDT&BHKh zocwsj(|T_YhWBUqOq$2*2B&&`eQ3RJA;VYF{0*Tr-=5L?nQ1+2Th8(65Jc-c<VRIe5gV@&#TZ(T0b&`;VWr=6x04uCmDS+&EJyD z@K%N|rFp#X?=&CAoTK%(*3-O2p`GJqiDL9jf8lnFv!8G??dLYe&mBy?cQEt+4yIob z%=zN3K)OA5`_TMErahCGbuo$Q@02jw|J18Ae_u4ir!jmk&EL<=!{{1X{{Yj@2Wn~k zi~^dU$@rfWMC<3q(ENf_n#cP!PV+x8o7OL6#%rOO(KCJ)Rnz(;#!pfaqi4o_F*6Sr zhthr)H_?1bEX}7{8NQC@@%^V$oR_B4`ejV}@qUYwpJm~+pXE&4R+wo0%0`-hIFsR- zeIwnE)~6>kd@0SZV)mI;m9+j5W_%yXr}d9A>*7&nJzAOZTAe}rU(M9Jx{T4+(EOS- zhR>$?webwkjN>{*?tI=^XQlnDk79Tx4(plzu5Y0IJQmLI(F~tY^O;!;&%`;CY0pL` zZktMJ|C^b4ySa(hKhCu0i5Ob{1QXBf3A8>tpW*9i{z)eO+nDxj%cK2lD`xmcntxiy z@R1B3&+t_Y&-6Dpl-B1a()=@4n%{0=_*$CZ;ZO6=MbrHAxinwE%!i#7w0>t3&A-g7 zmzSCI?5+UX&n~9kU5uaIv9zDv5VB{C~n3o{2+Q0j+H=hONlW|}{$WB5Xv ze}`$$JB_ryg3(tb)B1PoX#Q9>%~zUf{sc2mPB4DnW9C2JM|2vOlT5!pVElZ@jQgo# zy8Wk^dHXTb-;ZNyKOZyu&nIcL{u5?iRp-$9YG%AnhtT@dOngpP(fTvYe*USM)_=9d1ayTFVu{=M8O4i~P{^#Z)>-{G%d--{Wq-i*GFmGMg&GP}JwEjjN!_)hQ>y1pkH|5fP zg6bK*ndWb1=JUW1T0gLs<_D$IJbsScDLzB;X?+M2pJ8RRenchBhcaGCMH_`gAY?{9%kl`C>eoPk4-x|j7%sy<%qxEAMKVwhQ`f=$r zKVDDs;Q=&%8#6y|W8!c-Gtch`qW#~&v_B$|*5B#R@Fg^VS1rxoT~6~8nEm0NELtBK z%jmrjRK5sPBel{}eI*XYnn;Ack zTWJ4}N74Kfu{57!rTM3redejFv>v~g=oFuAO#HVs(SEi!(tI8hpXZqQ@EkLLp4Zd< zpD(BRe8$fUezg9De42mBgW;L|?WHnWUl76YjD9CGj<1B%eqJe|`NANEXV%fKC|bXZ z8OK+z()!)e4A1nph^e=TiSuhQwEsP^3|~X@dvj>Mm}&n$j!&lj>~ElXd~UPTx_iBz z)*q~<`8N`2{y#dJe>0fjnR)dl)899ldf&7%{!3^czlZD;hqunr`Xfxgjxh7NI-B-$ znpt0`^B6rd|Czs!xH5kSajhw!{ns?n{8=W>XRB!aXUuqg9zg3qXV%^44YdAT2F-ur z!|+UhzhuU@R&nEe9a5V{`>$oz#n)G9{Wp4sw=g^t|MQHW^Gv_$nE6(3qU)_^;(ysp z>%R?RcxIk_$Luo=3ACSvMwmde7K3$-=0kKcjwalJ#{ocIfLQLX+DbaAJt6jneX-J z?yI2n59ZPQ%y62Y70B?Z4A0EZxJp_-JB;C(c{S%Gtxxcw`T4;NpH1@%Vi=z3Z&E3( zUmVNuR)(*l`6bZ|&#aeJM!&3__LIiMX9Y7)R-B{#tkg4nHO;R|WOybHRtv4SCeu7~ z|6I4)%;+sNzs8^8OK3i`jOOt$e3mJhNXt&BXti5Zcd< zESk?__Pu9wY5jB249~Rld8S{_*V2CS6Bs^^;Tb>7_cC;^1kiqp>S%s%0nHaP^I<;| zhmt1R&jCh{&#`h^@1?c0pF`O+&)nzKm6g)^w<>7Lzrum}^?fkyr zsGiX?^Xh0Aqi5FL(PTznL-X%M(|pA_nm^9?sbt3e#01*UhdDI=Q2@gi(>(r8kJG&R zgz?XOFF?oKuhyLort4+ySL@Dr(E2l1Y5r4YzpY{7P*YC(Im^WVvk+SUxsK+)urfU3 z|EqXfU(59CYi6E*T}J!)x|ZRaX#N|fzvr2FUSQ_ug(BMjMP__2W-@wa9bK~0`g*3F zm-V#%GUMl4Mee-+d>cjkX<+90_Yt(d(L(cAN@)HkW_*9Hr}bAu89tri8)^O*CT_nb z(E4A?8NPw$uQB`KHM%`+8oFLL?N!phTRS@bZtbFJy}Oy=={UQ2=F$2Nj30wPtv967 zyfKF6J6jl@@zc4Q)|=@4$IX;M>$@g0JfrWHP3xKau5SLxjJ|;3>3QYW-9qcTmofZF znh!S7{NQ4mAHwiMEwnx)h~|ee<1)O6){pREcni&kGVL7A)Eh?cCvLZd)BeXW^L+dS zS`TiV_FK2`5?X&d6Sq5JX?+B+jOjJo9;Mw|hcp{k_b%Ow`f(iOf2k!t{5F zmG(2GlIEkBafxE$Jk?D5nc7J6_j}NMG~?%iDq0`I%(n-FY5hZCG(WqP=I8j){5(C) z&o8F=Ma(#+GI36=p#3am{HG<;`iC26ewCT#na`uTS<7hsYQ}$th1O><`gKvXeqA=h zpQQQqg*5*d6Q4|GK4ezWel}QXej_t~Hu=!{%}l+U(`fzUO#2_NqxIQLz1abbzKG$Q zXnso#%|CgL=C_&{K7;0Sn0`IQ#O*0&+_y#3{-4gH`CMjQ=VsCRyn32{mYL7bmeBfq zW?b@__UAL}BEN$6{{mC*i#fFZC1$*K#?$(p|3^UVD&x1)))p1JSjR$->~6-;~HW!B4a zCeFu8Y5$eXym~K|*1va-=1=+4{3jNguV&V7bv~nK)=PCIqi5P#-AL0&41=Y^Piuj`L7h(`Fi`C7+U{LEzO^|GJFBeUkIi7ix!%%PhfZ^&h_Pt zo*A#p7FvHfkLJH+_JQx1_9MVXXbN{Zr@vJKR*P}{Eri8zLDuy zV>7M4l27wLG4t)`C|Z9tp5}jH_}>^mP0W1wy^i+(Msv-i7X0JJ&K=?-oe&%>A);?g_LWe`mz;?+ER@nrOXuIL-T{()G~XeN<_*CN zpHB0gV;H`O=1mrwXYP-+^Y^3mJwq9uUhnOCGV$z_L;JZYk>RUoJ}8prgPAxC38MAG zlW9JbiSwulw0=|x&4>9jyoKRoY5o?5A5%f=Z)N&xiK6wEY=&q0HNBG7PiNM9j32F! zVb)7bKBKRp`3IT#`4BT79=b~V!QX##+8<^x{f%YfGc%a>KQoo#%V~aAA;UB4E6#`3 z$20rMY-T>sPNV(IH8FfS!!vPCh^O@lxir5xisqLv^L9xUtxpMMcqY!NR$8B0OY_Tg z3|~$2%bEF*7D?+@FmYIs#pp|DekBuU<~h&p(i>?%tFF@gBegXDXgJMVnf=yUP3u=P zd`20qU(eLLKA+Juam!@Z7js{(-3BIZ8^h>&ndc0*+sO2HlbQCD&G^qQX7m*dUqkax zhS2;|Q8d4e@xR@J)^87Bcni(%VA}JnmDWF-NAu4!aelsu*1vF)=3lI(`ImGwU&zec z{U%y}por$*2xRzrnm<%d^KWL;d>ONEl$9`gX8s(`r}ZED)BM>;n*WxWKi@I!xspNq z`8|i`{|F^{ch^FiZ)awBE6uyp{dISz$JgDxiT2~c`1d+T>%HlD=r+Mh@wrO# zzVx`bw~wOr?UNav9xwOy^!U2BXWHWzOZ)G@@SXB$y`E{0o{6&|i1uS(+G(ty^`=0E zk7amzow}QtaqPm(^DgWPi>(2=rWq0 z&ctncHlt_yHJuq3=KZGogG@Ug%Ao6=snAaMrQK&S>ta?j?I*67=HtyYKRcJ^=a?9t zsW-u&)+gl9{QOdyU%==WCer$3X1*me zA%fvE8NP_&neojoq4it*8J?+k3p4IpnR&i7pZ33%8ON9urh?Je(foED!!!Nbp~#)j3p=7{KY6({|7aqK9S)YXg;6Ozre)dMP@#{*hu?-DUId}OKASJK$_pf^mk7rqfcOXE6rCh z>-Sv~tv{Ab^T(OEo$#ae?^|j9BvbDP9G_47`H+djhn2McqjZM9O7o}kY5rqoysE=! z{ijVd&wMY&{WCMI|BPw>XN>>PnSPyP;`0UL=SznFs+ewPEi-RxnR>s@p#6Nq__?6S zo%gGY<+PuROdKvT^Y(HK?WZA-;hFX~RMYzJ{TV)o<{K+${^tUk|C8z0pG-TOJxD(u zF4;8iO8fV4O=a{oG_R|p`F6=P@7_%Fo)I+f&9uM$1X|xdpXU9T{`xWf?Vz}Enx7sW zifR8H!)U%!Ce7>33|~m|hIoc=qFD{xOWclIFVyGd$Ct?##G!ze@Y*VPW_xn(xWXLvtyu?;S|-<(eK1DSCd6hiBn&trHDHqrVa{xm<7@gKs(e;A`59!dKj z5l!=<*)%`0oaRR{;}XWic}zI%XAD#CtxUaR8J_vQ36DFO`FVE$-JW~uY5v|!nxE)F z^OKnQIVFPDPh6Ns97dmWlJ@gd2F-7apn2v#n8(x1`pPY({V>nZ@pzV*56^LYKJEWG zrrzhOX#MjRhHs+z{92kXVD_=Y%sx|Yru~#NevUA4IGRTLd8e4>-)*G%V`Vg7$*hYL zA+-KPKFz;prTO)O!Mcm zY5pr_+`r1D^%>qw77-?6-C0wEki+%`^AGJnEBa{rAi`{>04x zpBrgEzcBup-$nKKooW9c`LuuLKA6X!%=&6(#^tXj(!ZyhiRRsNXx`JG;puVoY=4s0 z`EcKGVV)!H*|nb5ccafQp53gBo{3wJSX$qc zX=l$UT0bz5=Er3-{5hJx&C2kNG(RDg;hDJItH_=9dCz+bXg`r2G(RbW<|oI}`~!(J zKaJ5(x6t|rnK;bQ)B4yfnx7d!^RqH(J}#E#na`PcCX_RJX515)e$8X{hj}ry|M|?i zn9t0U1wpi*1@R1@$MDrOpBTpQ%zRtuL+g{6{w`(qnPo+^pXI?cpO(z1Kv!;+bAd>sK-3`v}t>=6CTtw=()13tjJ1(KNq}nLp2@)B0y>8J-#U z?LM@AJF~90m(ltiOx$)%p!IpRH2-WA!!z|h=TGaO%Vqd;H2=Jr;Tvc^pJ~qv4F4h% zALerro&{FA-U6omJ40#x%ds@Si&@vZnE3Bv;<<~7&#P9t-dCCU?AFuz-Ap@oC(`<& z2{gYajN$8Oes2uT7uV2y3DcgEdRl)VfaVW|Fnlx3zmZDwr3nmQP4oZpVfbiwTAr^SjJ=9m}Tu9IvGL6BRW7 z9y8z0dC>ZE%zQp~mDYd3tc%(}T3^ew=W8>q|GJdnne|o2te3hH+Rp`MTrMec=j-`P zOq?$>`pbEAy_cDIGT&G7YzU|Qe6M48CT>@2Y5lJuH2)jJ|DHzc|EQyR=JPO~&1JOy zuL7F?yPD*^H1Ra=LdV%lr>FI9^nCDY*GTI_&AIY?T zG&66*>S#Y>n0StDp!MUMX#VyIG=Fy<&EM-o^AnkQ$lN#cn#9bX$vL$DsTDMTKNHWG zleGT97>3WK`G=VK`H+>?&nTz)SZ1Eg^r!VRnfW;@jn>CoXnt-q!!z+oNT>Dlnfb7Q zndeE&xG>-U@>;^wo6fA?^k%x9tBPr!xu4^;rjph(--q(bD5Ul4@@YPkY3ByU5A%5y zuPkQVHwV%6GS7GQdV-nHPn6PrvYC0CT}$h?gfe_N!!z-DlG*pRGW*X~X1?Vl)AcgH zf8_OaB&~lYhUT~XF+9_rJjVaCO#Gj1qWwRYL-Wrw`{WBu++L`n{k)h+^DhO{d_e@w z?<}MFml^+s$+Uh~9>X*7+0DeWh-v?8ihJklvDcV!-#dYB&)yP-Z=m_&FotKwaUZiD z_noBu><_2;*8>>7n&uBM{d&Wn*1wU#@QpP8pB$P$l*aH(Jl|x-{jFHq&s$8K%b9UM zQbqeY%FO=?KU!ZA&+yGOe~g*8$Cx-&GX75#(*94>(foT%z07?oulH(bKkp~d{7F5{ zf6zqpALi5iN8vPI70K|GH2-lK&3}?g^VNY2&&1&j)88}Ew4XD@G+&cT^ItIQ=u2iC zzp~JNYJ(U)jpn~DV)%NR|AuMLHzl1*QEl+Pc_Xmzdz+w&x}`n1?}fDGcK1CX#KY)hA(7zW}o>kmDYb(%kWH`8<=r# zh@ky^Z=v}g@)(|p+mH3MzA=X8ulO*02+jY(%+FtPX#KBIH2<5P=9`&uyjDi)y)_nw zFQs`c<44;>>s>Nw-j$x8-mc}eUYADmZaRiwV~X;>|p# z%iEXn<7=h;ccAw@?+#3VJD#Kc=o4t(5JvOHJeoJ9(|p$un(r3C@brG-?H^0){qq@~ zi9`1YTHl@C2fTYQ^PxvQ?WZR*uX@qz(Ytpt?Wa!_%?D)Cd|wOA_hb6oKbqF}XZkgO ziNk zhG+bYWab<59Bl7VOx#A(=P&PDI6jMR=a^!ezm=K)w=(<2t+litOAN!;)BLyunjarX z^WhCNf18fx?_m5yOrZ657Sa6OCu#m3ADX|H@iQ@;)<-gaA`@x-W@R z6IDq2pPEMV_Z2Wav#zHx`^JMgw4WKN3|~w0386GUKbGN{{?0FB^vt?mR7dL?F-E4`X;{{yeOs^$#=So6htro$VRKe0Ds;Gk&&2(R$|l zr{2u((0OmQ(0+25d6E-N>!0#x_$->=7D)3unDNSE{4k%7^?r#NmzNUhdYRvG^M09` zhp#Ykcr}*x|7sP@7g-s;g68*}r1`zfyxrSG>-RBn*w5$>GW)|Dd9?phCO)N=wEj>C z!xzx}o6LGC^P%-+%sMJNN9*6pr1`_lI35k9^+(HT{+$S#e>aupk14d%-*>!^G4tVg zFzx3!GmgiZaX-Pd^L;bz|70@Ff5`axu!z<(&vWzssFv1ORnz<_6T=5Fd>X@N(fr4Z z|Bq{E{U-qoA4T)0BN?8V&u5tUpQ)t%e9Gu+{AqnnG{cuMd>zf74QBXqn*U7C@P!P| z`C-Q8vqsv_=N=5t#OHISzvq~7KgW#w7fiptWa9r-KHbi*nrXh4@$)suGwX}_zO(l? zwRFAbV;Me`;p=I>E{EX@7`}w&FND(k#cYOW{9iKD`b!x!U!O?xmvb5ZD$Rcz!SMMs z{~fd6HZb$Ep_KOXy_w-FX#NLgKL6-P>wj#Z`NnjbXZ{}HeZ@-af68NcCeA-kp!L7# zY5rF;!!zUin<95UzyDT4`)Nw1`QMp+>Gw)n|3@*+|H+J3b1Bi^&G!hU`JM{x9Oqt4e0nqUp%2r3=66MX28Ph>8OY3&feo~N5ThSdN9%)` zxP^w%dgkvwJ|kg*I{vPu&&WpFPgo$$-(F7h_vX|5M5e!yOgtmAX+M!nJSQ>!Co}V5 zaun@J9Fh^co*5bb9MGv8vF_|I;l{mhxb z@Ju|J-<|Q9$IQ?9jGy_{biE5q3~yz4CJqalX?>!F;qz&J2{SHBifDaG0mC!xNxe$z zmq*h43T8g6D5Ui(8U4yiTK}*@JMYiwv9x{_Gv8J*Ee{>LI|eP%GtZ(_zJYXYrjzW3p?C7af7E2a5oDj1#_uVz~&#JmcquR9gR149yoX zD3>GwtVXMt`J?*1yY)`|)I2e}eIIqM6pe zXJPnantz|szkia}pJezC%(VUk#{UQLj6R*=b7=mfb2MKSP4lPxY5o%?{+}@Ozj^}g z=X3$hGv8D2`80yof1XeCUzlkAOJ?8uvX0h&RnG7=H2)0~hdO5dUudBHT&$=0%L?s$ zy>vN^(O1y?w_!B@9W!1HdRqT|5Y1m<=Hb<5TK{_#&HrI$_-dN})57qTG|${G^=W3> z|Cf&T^A|He|6<1F??BqmH9v-rVR$+YzM6DW@2jQ9*ViS1*6UJf-i@9=zU{(ky?Y7G zdqmT`mzCzd&(XZ^Nt!oYrFrJLCBDq(#(lel(0=@xIP{>`yKm1Z+7I(PGQQ>lTHlM| zd&koH-qkeUpBeZ5CA2=UhUNz_`kU$f!S`lHKQN2-KZss0z9Cm>{qSO%9}&;+%=`(> zp!Fm38NQC@M=|qZbS$kO&GaiQkJjI!r};6={JfRkmwYYs{^M&&rTtr~Xnt%c&5yUx z{B1D|&y36M6KMTi^)%1?PL1!~5sbc+<|ic6{5?!O?~SGP_cqY{#B!RCtfYD7ex~mv zX1+~k#)Y|`={tqdPf4WP!+gHlcPg`f?_=6|KeO(lW9fPyD5CjkB{V-lyZ_a1<5}JQJkm2)a{t0G1K4GQxTNpo2GV^U~A?+uJ ziStuowEih(eLYn}>$fSi^SEzg+PQ54<0qZr%W3{;ABMLud@92iFnl%5=Xx+a6VKd4 zMxV#GoPP{W%M}=&&+@3zPB%P-`jV44DEk=70vI6X82l~&r4uGEETd<}_f;l7 zuQL0|Za>=p?jo9hEuQB0h0*+e#{cWFwElpF<_|LQKgf*lL1x|_Wa4lrnXb2tsrPUe ztv^yq^G6$LzJi%=#}a5g^Zg6o%5qx&J~KZ*VCKn(Wwf78Axw=7oq6*PGh(J=&QK#WQ^Y+e!W2P~aSe)~MvNjxwdqC0WlT`SJ*W!P z674BbMd&TG_~-2P+c}=cTJMba_J7~k=M#IcZ+?09+H0@9vy*e$s`IO7YCNO%HJW*% z`FneITsvJ|XRW6G+U4r}dz$=vId#6K*=}uyI{#^0<4Lt|NNYT+_KgvZ$26|1)2+^L z@~C~Yrq1TPI=@vjo?8p*{5H+J-KIW{1N%%-)*0Ams@k_3pz&cEpQQ1q##3r{cr`v- z?JWU~tIw~2n!i71V4M2-JkS|e*YD@m_&l}upP}{vnsL}seLWdCaF)8xz?B-WseMqZ z+S}b4KSb>vn(-N;p8o@fsOSH{A=}jThsM-C>`IN-)jnJ^KE9+nulajj2JTi+=f`UL zInIiI>wbRVo?&&JJ?Ch=r1rg>YM)e4yXJfKfx&<#uNluD`qlXZHFXZ`R_71WoEJZd zs`CeHj{CtmP5uRqYx;AjW*#1PlDf`sH0P1#d)I-H>FPSao2T~QhcvF~=P^^&`C~QX zaNI_9{`lo;KcTGl6Dw*zSu;;g39Iv`x-_nt4>8U7pXO25Ic=uKHT!kCX8ccIqONns zc#Y3h`;1{4pRM*YHRE&UEOq`Yi@$ySIV+*bYv#k*DRn;HrE$&poa0vK&xxu1T+Q)4 zSC=oT>--_C@toRcu2;L}_j?DPpI7HEn5^-68n3Bc^Y^O^Oe|ODFN|rtr1p#a8lR)~ zi#5mP;*&J_s@i8wSNkOywO^_^UoK6n^OtG1o78MKSy9*d<1~$D)PA{Be)Uw1Yv#{28FfB2L*uhFURL|HQ#7vW|8+~$`Rm;pFQ|R? zc#W@A`wdZzYv$pNnsL5aGtRff)b($N<1dYQIZU|1M3PyS?frp`VT#-_J=k7f4HvBKN8b;Rqc;P)c#k^b>OdQb^b9;{;`B6UsLOZcj|3pAt=Sio=6KY@P(|AVhPigx3l%}6gd(?HF){OHrQFZ>A zirR~sd{I;XSN+nM)Lu$x zyrTA3Vj9bW>{#DI>y_QtxU(?iIt~p+>r`2^{*KAkw=UE17{@sm%Z)oPj8}roL zEuW-u&HGw;y*mG9RPAqB{O#{C-qQ5*EzR}+@0#)dyJkH9o>lkf?|F?EHD1i#r}2Qs=c;{`W}H`fHF>|rgBlNOT=TyCwkEIn^HT%g-l(qsPOHY<8Xv9k zNgCH2-*+^}x1u?|70vOjXpV1Xwz@wR&GD@)(d3tDe7VNEHNH{p?ykA83y6x(yrw zO+Kdf4IYha&btko^G@^ctqN`Hr>gz) zpxVC(seP+vzqWccdB57X%~boBqcyI1zx!&sI{)=}jjNCQpgwby`9Xc=seQX)8ZW8c zu}tkPT^f&SJg#=l-)lFhRnyPbq`FSqOttq_AICv`HT$)_X1})A<(I3syM4FDHOJA} zs?Iyz8Xv9kNg6Mzy`N^>`iIr|9W>kBA+OF4I7#E0?e3`A?m*4>4AhLzKutdfYQ|^a z6m>rbPSyByjnB~dERE0B_*{)I(fBfrFV}dt#y6^6^ZP}E2Dvr)(Hftm@hKXgs`2R> zpP}(t8lSE4xf)-h@nsrcuJLY-Z&bUhRpV}rkJk7kjZe||REgO~ z+B=*Y_h{Uw@q*gjQ`K&0>IlvKhEU&+4HA>p^~E6?*Nn3$s`F+<i!ScXbyLdHzrN--OA2mV1BP+e!Ym1-ZWsXsQO$>-EQPP4z`H2oYmSyM+dZ^zG6 z=O?%{zD(`Ar`0~ORpV}rCp4}Z&xsjLUNfE(YwEl|qVbr<6B^g_)1T4gHU0D#)cHL$ z=jk3vb$(CHeAx34O@5l%_X?|hlIFTMsjSX_Z=Txsj;MW~85*CZam_s3M>GHT$!qEq zHLlsOeX5##UG0G>8rNL^_tl(7`)bakeKq5=?`-vU_tVthPcuIIX~t(i&G_u68K3<$ zA^YO=3b^c!- z)%Xi)4@K1euTwQXUE`X0@)J#+gMI2chYZlTW_%7AugOnV`%g8;<)_{1{LkVVPij1^ z_Cq!E{LpFYd{{Hj51XRSAEvnuAEw!_pHEWP`S~o3*VXqm8rK|`UoO|=HOKw%E_MF!^=kiBMB~fU z-Zfg|OVs{rr^Y=R*Noe*1DbqDiiLNHD1v8N{wrd%Mshu`6D&s zcBE$9j?|3XkyADGHP@wSPIZ2oW*m+Rs`E!_#^E<{b^bSHwI8k7ucHge2>@E zIbL&pK0d47?(yAfKS49kPuQl;pXk!KX8cc_tjW*O_&kk2s__@pe$oJqYxeh~geITT zct+znjTbat(s)JV8`XYttHw3w(aEDV`AHg2Xk0U|PSMmkWs17aDVlkE$}&xUxyCj9 zIaRaWQ#IGIQ#I$~sa5rMV~42yG|lzrbdNfJx=-T)jcd+}(>3SC=_@sL)@yv5+RvDy z_8As``{x}qT$;RB<1;m`IUmo|jL(^x`Eb^Fb^WskXgsa)B^uY9*Kw~pAFrtWoaq`* zsQp~c{Q1K$b^Z?-jcexld46^NyrjnGXplNp+p8=BoYbtlF=ctoD>k?bkl4_UqTH{RYi(yg^gv#<051 zjhgYlNi+Uw&2=xGQP;nDlG<;buJ+qB$LlstKmW8$T_@vJ`}~U97fjW7M&p`!zF?a= zpYv*5Gf#3kb$+pC-Y(JPmuluuzNW7KP(|&Uf0t^|!^_qAN9L&guPL=ZrkQVl3#jvd zi>Uo^&HQ;%GtSF2+kHyoPfu5G_Zf@7eLwR|UY#$7H2#9xpVeGvG=FY!&~t~V>pZ8a z^Soxkyc*xA z_Ht3}nm@-k=&g!6|98K}HT73cQRi39RQuarwZD^A`@1oeEUF!U&n(OU`h&sQap!SVkjVIK;$>MKce>R0R`Hb2(Yx0|G z>U=$@_Rmvl->UI#C3XJGu-d<}_}lyYm8Q@HR{Ks7 zjnC6~MeXgH{c68bo$s*tTl3S^5!U22^PyvoCa=DZx!juLZp>EKF=uMLrgqKm&AL1V zb$;+@jZank5cT_lYpBjws_P8XYIzOVQ_K|+Ido{;>=d3!v zi>CiS)10S=PFB}BR5P!Br5T4`71VXQG~@7V&G~yoP+jK;%{U*iUY(zoQ~ObxasG|w zygquPy3Sei)PD9hwO?%Ux32@qsycu9FtuN;na|fm)%j~f8eggQJEmy7r1n4O)qbaD zyV-;~f1gL~^Jc33{(#ySN7TMVGd@d}sq+tbHLf|o9xSW#4>{Fd&}>)p_y4$-Y4T5L z=JPXC)!ThmGtQdNgD%bILD$Q9b^TIG?XPRL`+7{1uc&>+3u-Svs`iyWwZE-7zuuXp z&c7Q``)bWNudb@|Yl0dts=a%<#=F(NHmLUZ#;g7PxY|Dqt9^Z0?H_BdC$*e9|7k|! znsN9{lix5+U1vj5<2AK!T(9=eBO1@D{fkz$e>qv>1+{;r8HcY=Qs#H+GfC~+ximgo z?GCTT)#u$#EpyfRR!u)!H>&e}of@yGeS7u%+-ZCDxb5T&sp~jrsl8u7?fs)_A8@7G zchro}AS?c@`+=QY)75ocntr;fn*27k@1&UzJB`-lHRG_8X8!E7OjAeGpZ0`0ulaM7 zJGE=-bZCxCN4L7ZTa$Me)Oo|Lam{fN1Jrqu()dcXn<0(QRl8@B#^f{=MxkdHyw@?@BkG z`%~kA?Hy5TtoQpyyWCiJHy+sD5rxKj&zCD`AJx;p!@to^HP(OGc%a^KiN^X}-)Q%2 zzvmpXpYQ4GZH-53pgw4 z>sK1=<8qDlJ$l>6_Ke4pZ?wye^~K+47aHpm8jrTd zqtX9kvyK1voMYSfG#l-n@$fYsa>AI$_VjJnZ?q$g^)ZbHwtsA>v3`EzVdwpg^#d9Y z?9Z{D#=5uhz<3v#-+|4{Yy7Ph_0CG}h;R zqaAIm-`99x|CjaT?`S-*|H}f6^-SY|dds|x_1hZ{fpDZ-i zXEz>pzUR5?8xQ;WRAc?R-si2quk-hfwyftF=W47kYCi87TW_o{Y&_)aJ!314^<3j2 z>$^|>S!2ETzs}zltw!f>)7DbVl1ua9LT{+C=4ta=ZBLuWTAFJsvfU?urB^OnGuG1n zpQkksFJYOqpR$(b`vok^Sk|ni{XdT!=04`975RKOw%;xLV}3339&64w`f0rv^em$? zzggC`>zSYUtW{4-+FH6#`L+F&wdCVLp8Q6`(^jxrp0k;`PTI9 zgYYh!WV@f7j~@K{clMK>eolM!-&}f}a-8ju`DM{sHD}sOyPsamJ=tKYhQ^s89z`6tK2uIK3|x7TbtQ}*h2TI$BbF7odlk9gcF zp_?CeeZKVct6TJ)$HN&Rr9A)B#^aosR{zBHNz%`M)bR8VS`klw7m0c_=ZhWku<@|{ zyDtCB=Eu2LTV46i^P_*4^}7G<^MfA;_sne0jhA-N!@TH+=W=MUEpG#D7QQ zKWl#QO}j5xYLU2%UKj0L=V>{6)mnN6c;#}3GL{8vY5(sZT~F=Ye{}g@Hy$roT@#nl z>;6CLuV+BOihTF^5h11R^To5hi}*ik{AZ2Fx6YTu?$)Ni`*?({*ZprD56|{7tN-5~ zkE8#c;}QK|Jswu)8~^+Ecw8pOs+t3|g=I50A%=KGgXia{n=_=Vx)>{Xfq@ z`5MnaKY6idpog@-Ys1XR`(!hhv0iHC^8aQD{{5*g?AcM)@eB;s2Re!jKq*h}}Uf3a4-`*@_S*Ztock6&OsO7;-_ zQ%ldFgq8chHy-kS{hu0yZ-hL9qW`1E;~QNitNiCL({o}t`WZqaNPjwz7ulMaA z-6x-4P$$pZjgFYSpEDnh*kmm`50&p59oLPp+U67TI!Qu}M#s_%3+?|FD$iou=MvNc{oyXSXzdj9jsh4s4=ShW^% zfAI6~Q@{RtYmdqzjbCK=`fJ&rJ`;O>=jFfRzXXa3YuVW+Xf2Jk32P~T*Q0S<4_XgT z67M+SD|m`{`$3<=)66%)GtA|F8f)!wmi;jX?~QzpOpN z{AGBV`Eq!Lxy%`BtISs+Ut|6ryw3bC>GJ8uy4~8`E;voyys<$Kx7LXgsO$l*Z+FG>>C*yBSSBtMQ!1^BON`yr}V##>>%Z^Rr$Np9HTl55VipCre)kw&(wp zAl!*3W1Ww=+@7&c?qBmhHh325$o=@X2U#85hp&_U zYUZcNKjCAn3$uLBpJ`~!|8-r=<@dG5x(M@~t+uf)%DfXEV?Iv$h8*=%P=7;#IgZPQ z67!!}ZDT`)`Qh*ybA0~V2)2*!DYAdYMis`wqxgKcvBcbKwT+Dx=0SLk zdC#9^YRs!mV0-+de}KF2gs_(F(Hr4j=F8xI=I_FT)Q`vg>ZUOBiO5HoV_t2FF`tQi zg85bO6!VAR8Rjp-bIj!%ud%7X9N*7uDp5bdu-e9^3Ufca#vGsDH-qi*KjCSIVde|rG3J;L`1|9U$K?!s|M_{2=4XsTof31L zcc0g&pAnS#FI>!zf%}=yfrqKjz&!aP#(W9#S?Z_C@A-`{3d}!4zD)hJ)8RGd*Gu0j z?^BxN6B`Tn(tHf_f2*8dy?I=JwzB<-;r!jo?ki%8Q9r@zzY0$?UkA@oKkaFFk-5A; z8r#73d1}wYZC;u`9rJKokU8ezwg_{~!)*!Xn1|ak%rOtQ6{w#fthTYO!aOMbOR(Ml zGcd2d^x_Hm`gZ)i_{Nt(=1*8{a*Mp=^uZJ`q(|BCt360DBu>0A5(=sb3+KMFKev^EE5p8wmt7N{f zk9if|#avEI(Kkswy&dv-=0oAKUbCO+{iSd3Vfm@>5c8wqapvd3v&^r6mzigzJHht( zl|~(>{RGahw7lOCPCu(-=L6Jl{Sft|#Elo^^+YuPTo>v$^J$WApEDA53dDQs6p6Q| zpF_Szyfo=EqrW(LYN)U5A&c-+Y1e9h}T>fd`n&_am`Gl)0=c zc1SaS7j=rvH^b}HZ;|(VVt|kNIGG>N#e5Px$$Ve9yv{X`%S|}12jp2^zCVfqRptlF zIy-up{|X*rj`MfNICJ@aA$H6%m!HeTj%DV5w%TH#lez4-7#LtK?^ngZDDzKHCr$m< zR(O&5F7P^YjOQR9^PkB4pfGbg-^Khl$S0Yf1)fTQ2bGuHN`441Xo;Hnj z`+AFh?&M_opCRw3elxBoI|ZoE!S#HnDD(4BC(S$wFEYOkUT2<{-tJ>A@Bc-67xNEf zzJnd#+b)HBX#P)fd!oZj{WrTI&$fHJ{2oAb(D5;DmfwGhjsUH5yBl?a%!j~3%tyn+ z#5>00{iuWO@9naGq9a1`?YECdohWnJKVh)#-dmM>;6b=8Q`{9katWW<@bp-zH1-Zu@|FMGR29`{#9o#yj1WF0x}8|#?=`E%e-mOmfv zVtz5)L%g|N;U(Ul$NPowF~@zl@H0QkY6}q{-djIN{p=%=4-s$AC#9Pa>W@7IXXF3a zJMaX{<2afr>VF%Ae1>@^JjeV%=^k3gxbs+en&xlE{P$$2Kl3T_S?2P4E#YC?eP%22 zd73X`UU>?{d;3!)-rnp_iF&g?W#)E&D$MQvRH+x`_tL^sV{Z4e&U~=d7IM*8$9yj0 zdNtU|@_3&e>|*|}sN*5t(J}?@rGDL?;6CE*#b3kY#LcU{@C5S!Jjpx^Pf@=n2G3Bx zIsq>*ufU7cujzu9sb8Ij*O^zO%gc0QU5>M9*BL^`)3oah^N>32D{$Qy=B57n600qS zvGIKUF1VlNG5?1Jn7@pCkonv25cL(fZVU@EKMDCR<~PA3)Z5;JN10>(4|h^u-B;%E z_rKfY*}htis~FD4Z8feZ!@aD|-l)UIZS{}gY}{5K4o}fKAB}*gsjuG*&oIaNJ3LGM zBiR(gbIfu64$m|1Z?(nn0&`qfhZmWH^RyCI4&bx%<;Ktgok<9YKswG=D&o?{p;QC5pX}t<8#)C z0CU{0jtDY8-fD{xA?ByRWxsm2dj`CV<>T-O^9$fn=9j`_%<;Kzgsj)Q-RqD~usqJw z5lQBAkWVqc1DdaS39|>;G|Fv)@%gYOd80lh;>*Poe^Dj}y zOZ{V9Pe%Hv+t;g+e&)DdjSNt?uU8|3%|t z-ls+;n0H%kF*4139X!XpCVea&PxGz_+-oiE`Dy-H=EPV!p5`60EymLEH1BYsPJq_Q z)ZszucgVIFOUKi^qf6#@kI?*r1e}hKIX?xD(foW_SL_~V?m<35{r(a>$vh92+i8x^ zJ+`N5eqJ5<9Q6e8w>i0U~vcJtb3v4gb{C#%)3ibP4s8gjr zFAA5hYu3Lv46oDteG%ys!S;P%)^^#iW}PhdYogqK)9(wRjt7P0`=)uHAMRx?_ft&t zQU8mqD<;a<_12el#l!&3XYKky=5~MNvRUW;n3WR~!z^#tk1&@_F)>O#XV;O-X8n0y zD<>w#X?~s)o}m60A3RC@-ZZ?#T<*V^Sf+ko7WpdmzXYZ4)kS@=Y>T}j%!5{2>=kA1 zgR}iz?1IN>eo-BsU|xY|m>1z$>I=(oHV%vPaJil4_%F^#pX9WbO<#gKlVpE;J&t^k z<^Aw3>Wh=o|XL*c+{JNy^oSe5Er{eSbzD}0EM%LNa#r$TthxwgwFY|eDx!=9p zeGu+v`64{P{9SmE`4{jI^;icy%pBhv@7u*ZhRzk%x5AWXP$y5n9qeL zndjj-;^sp^xP0CoPxGNFyvXu$KgIs6&ZDTaKdbYoPuAJLKhx`f6NOn zGk3!4)E|jT{{h&(4|xRje<1tS+}}sLkPovu6?mGtSNZ`l;^F|A69*)y|7*r-ivv>B z5A#YtQf}YwPscU*+;n7w*Lf7Z67liFc&NAzxyS z?{khUGv6Ee3iJKpRpvj0*O-Ukb>@dkp9Z$CpDFZbnv>-ZN8ZI8{h8)reiZUv<|n{? z%>MxQGf%<;%yHeG7G!SsU+#DBe7FO3!Ypt1zl-_3$VaGm81N|d)B<>nIle!c7H9r2 z@(Jco!sUMV_P+>E(R{N%Y3ivJ$Y+QfOAmr)iFc&lf#;axe3_PK{yy>r=ATHP&iZ*j z>P*kj{QdY|WjfpLd?)G@Xny`4@Dg+U9DI6(dBkdq={4qfUppp5+ z8F-Yr94~R4mw3ng+hzVZKl4Z6LFTLBv_Bp5H^L(YU)B{$kx~ zixUFWUrNFg)L)25KQT(Y5pNzPD zU$GqhKiSFZc#vn~u)GSF&-d0Z!M)b9>95%Jebmc#ec9h;{tY`HWcifU7AJ?8$Khe> zE28i&>aW=zpaUmKY3i@a zoH#i{eTB@4le5edR$H80p#G`{F8kZ;|Eqp@iRD9Z*{^1PMHpV8`B(DrD)m=oQ=D9< z{(8Y`3;AQ1#=5ZD<_a%dZl~$5r{G=Ive)DAB=s`-e~NrvZ$5&2n&w}%>tv|Ejyk8s zsJ|umQ=BGW*R20m$;ye-5-gvGvwpsnfwO+TC3E65*3Y+OKg4OWUhj5;s8g_(O@Ffv zFH>I`h0FbD=Kr3Re!7e0b8s*7BHTy)?{a(M^e*NRxAY0h%+)QFPq|wEcI~BYKt>+%=2)$ zY}P+43@^}pv;T7Yz4?gD&n%I=yC3dvXI7cdm-+L+I3L~p{tEZ7{IhUB^S9t3=Ih}R z>hi}>eZ+Zj=J=d-UW)k`t1Zr>?d8XhFxan)IHI?l!-{666YVUl+bo-XsV z^29x@Rq4&Yhh1KW-KYN?PRCzdX6MtSj!4RJ>HYflCRcM{2%3b*yo-5?60I>F6%Wt-U9bo z%U;KJFAp)t_Y#*!nYW`(lDP@bG9LynGT#|qW!@?M3MX^S^DBJJ{m6%y?+uSKpA1hj z$L|wfk!3FHiYtoDaUNY!Wj@tviz}VXkAVA_N8lmm$HG&@JMno;T$y7YL%u}a`gbWC zNBhbebNPC4m5X^y*15{hT(-qkVd}@!kWUbIpZyX%NnBht7oK9ChRgHPo}Zogd?>E= zkbLLyIhnsYKs_3RcQNJO~dkmrarCqJB%kYKv5wc>f%ce-xsoz|%+TvOd^8`G=JP7Y%E}P=oIQ4YFYKv>r%oFfD^B}y;TsFmZ9_lv+ zt+u!>z+5)PbzRhNC|GTAU7UFWo@O3|=b6i*~xC(y#Y055fb?Wm8<= zMg97M)$aXw&G5c_P6eK_md*KpeFmOpj`QpKB60UQ-7-Jh$^2tDyRKgF4m?cq;>HA= zUa#Ee)ZuKq=i>eJ2DaUE@xFNj+wQseJ*691{c~|Yc>}MH?+j}&IRsv{miF;-pL@9Uo1Dyl3lA~Jc5hn|XcAhnrb_dp_L4>tnxe;q@^- zxA6Kn?ziyz7>8SVeT>7ctiHX!xAOYf-&=Wo?C%_2ANxCp)wlO|4zG{>ox|&6e{bXU zvA?&`dDVGhOwNbf|rv&wr zVpd!HDMMV`m4Fwh-znSTPZi>wC!+rCV0_*bcSkG`w|nt~ybg=G8F++vr`@016U4=yV!eb;a?#{q-%qwvDI(xs|Gry3258Li} z^5x>5uw1dPKldb%PcbjRi^QA#zlUwtJ}z0dU3Y`fWnte<7u%@*KnyY_x%*>>&Y zh2JA?j)T3w_eQLGwtM==Wc_=i#Kpa~)BbdxoI_qN?K+(&+xZfy(|NKV`5JZnd`|qu z#XKkTe+g4R)%FLp9`xb-<+QdYsAGOudK6(^=DB8&c^}!yC`Wr|E+Oe z#QI~;!$n1w$M`RHQn$x{afrG-uNEhnSFN^KTp}*=aX8!GrT9K!iHqd}GQWh4!_quF zO!E)ebz;oze2%)^pCu*g&HmJ==h2@BT-5FUJm6;@x7y-?Fmt<3g1FoM{^tQYp5lQt z>ZC|sJdlMKh`a6YVU~h%zu`U?pL3T6S>E;p^A@WumKK=fbMAv+yME#tnSanp^5Q}4 z?}H)c7@r3d#NCNothRVCL*4!!>A?c^guIaz4_2t#-!J9C&3-PjK;(lopD4f+)X&A| z=zNj5cr1#1mHJS7Y%$>+5sXt;=xnmYeiKlKZT$ohW`5Ep-q z!sE=daK7E~@DjCd_26_*j{_6ZtyzCyLVl z=3$B6+ucpYS;Ak9|Mmy!p9)S&ljG>z381+xOK^`I+NB{3%x7eqVknPV(X@^z*4abL`hsY`Yh} zZso+&E|Pa&i2KH;S${5+uNO~;S^i7ZNl~}w&(me%;(3hQ({<|4`DC4EJk*~>|DOpk z&m!N&9QB`xGq>xfsTb|~dFFQgGIP6rojK|k*|=Ra!ODqZjO5)H?GMi}$NgBb#{3AG zf0oxrf1c&_15c5>d)8&}9P?}8CFZwEf6>MKBe>j-yA8eJk0z|I2||lC6~e@EPpdx&d28dUNRpZWBCF+&K&dP#RPL)4_{0& z$M@SWrl?u=6k~<%w&uM{J$;M~Rv+y8ZAb(Eh3CR5sD?`lXxQLZu<`Jtc zR(3I$uNN!j^Y(o91my7(_`UL8moFD9W3|yfnYJ?1xw-U)Rjo@l+!tz1nqs(Rh#40%+y>;yUiqm{^zY@$VR$Hu+{j%%0yRU+$ zN#5O!`;%1}<_nO|F)zU7vbX+o@S?Tsb^Q6hRb}QIkgqbweebFo^|gk~zYVspCu=+5 za-5pmUF(N?@B(=~ajy-*eavxPeLKJ$@277EH6Ew_{x7Yzcst45uAgFl0`h6eE&YUljR?TyNI{v{{WZUZyxve{|fh!y!-vf;eO_FKgBx%;>LpSAs-~}et#`I z%pCU#?{rbG_C-Fzd=Na!9QP0J#F-C6KEZr9c#^rDPcfJKDc;F2pKP_oJ6Yz3!gI{= zd++b$nIDCGk@;`oCFWe}z|={}e9Asd+v5;AnV_ z<$nRMGoLB_T_^Jd+{Ijuqj=ZD{8Hq-%x{7FnPc9*8(=QSQM?-@-f`%)8UjZ3U{vHWMU{%SAt&2S&}_wjR+)dA+XZm$k8$9>M~FmwFeZFQ3R z+Ec8ySk1WSqHNQrk3eB(i9lXl?WO$u9?*G^F@xNH+*Sc69_djdB%x^{B&m7m`wE^b1 z4&ZZD^E$caQPg4Mzvd};gw>JbBGzW8cVnKf%@X%amisBz=BR%^WwpiHJoU*@c!7Bs zUSux!Q>>N!viHj~*^7Lc=D%NqSD4G3SX*UYu-am+d|k8t_p{PJNLWkT-J1@8r^IySf|J|E$-B^dXeI4G6&z0+3tPbvn*Lj)C zx?-K5`WC!zt_w29b#h&p`H5Crtcx(m=Yn-H<}V?ipk6oODdxw+Gt6&<=a|1C{WCvt zW6wG8AoEw?Vd{JB2#+xT7kG^MAK?k+Ps3BxC&{MxEW_NcpJSe|+TybUb9@f^tVI2L z{gAIP$2|Y6#yl?b8^D-X#`kc)x535oasrACUg~?xoY>%J9TW87Wo=;%-fA% zd;Iq`WqzZJ=J)M_dzs$^_cLDs4^rQ6cX*gN#(!gk`9sLZsDJ+`c!K#Y@D%eBJVSkQ zD?G;>-xqBxFh3f3Se+=py((fs~DwAx~mm-!61 zpLqrzWd1Td%v??cu_;3R2VSc!HpQ6#1fF1y@9{RJm}jiE*d&+Dar?oO@SL@5`T>36 z1?KpCw5i1WG~_GH?}gWx<9>BB*dG7jPBOpQMe{+-x6NMW39BtO`|581JWBg3K?p+G0zXxjYfYmI(EK!TxTEF@M2o zi!BN2p^5Mm^XuRl=Js}T)c+OZzoo$ZcB?J6l&Js20k1Is8N9|E*XKIe9{+=Jyy`BR zKlpH2r|zZxi!j{J9Q~{ZnXg4&zOH$`PsMew9-;ZEm?!lZbG#qb6U;BN+M=FfUV>++ z{}Sh8J;(eP$QPL3377Sn{r~0P;1!m~b+ulje)ujj|2fzm|HF@lyI3CYBcFSje}=rD z`mYXy2bn(r4>Nxc9--c~D?G;hMtFjG5uT#{Yux95o?$NgEk4gN$Mx#-0`mn{TYO$( zZuh@J{RkiOHRh=Q1=t?{BeF98g^T4Ya4+>E@qY1zpZO`s2dN)C7anGg``a%f)DIEJ z$CyurCz#{&{ue3cIPbp5Q2(jj{~YrlS#9w}f%zHm67!|-3iI{w8ug#yer7A!9{-=k zWPYoQQNlRkuc%qyJlFznbTJ7~{4zLG$4QthU&i zVvh0InqiLj&8<1=hvEHhYk|4!x7b=@ey-IPTPw_OhS!+CC0!0*V_nWOhRREq~9)@R_<9z=r z$UI@S%|6`I@QAgv$H#M34xXWYWdvSe9+KYXrGB*!9%Sx<7pPxVl-^hNtJ$9`GSasX zTFa(ij{No!>Q~0Cwz++RxgVaPewA0cQ;uu1zC2y}n9c~zUlBt-!MqF3`yYg7tYvTg zGQ2=tT4y8^#j}U=L*}sntV{>5$c!Mt+v^(z`O#N{cZODilp@ZUTbN)aq)1t zpZNrMkokV_F!L$!2yu5-URO>0x%1xb%KHJce}d$Vi;uM0X8#oPC_Kab5AYoGtKbFZ ze}b2o%ljF#e}(z;$k&+vUAnxDXsqLUb@6Jri{){=cJNZ4-@C5VA;VI_#!86RCf#;aZ`yX?M0(1ER%iN(v{gO7TZSGKEJ_=rA zzOVEFV0-*8IYjy(Ij;6`X@9^4m&;y9{XrfSlE<Q5g94>6bhFkNzfHT!wvi83#53mWT~pEu5c)A2EGyhwUS z&RRC}b7kA?$WzC^KW=susLy@MYMUKJ;_eGSm+tlvH|+XuKXaUqZaO~3+{aKSLG$x4 zf80sxOXT~$=}u9fCkMuKr3w%VpUOa0y>;B?&FXM7({$F0{3w9b7Skf-Bj z+`9^1VtILEVj5uk_|Bgs^M;e==kEh|5pTcfW4MR7F<*WUZyH|axNaLh=JNjEH2lmX zR@*e#ah(5qc+grl=i7YDPa{O!J@^O6hlv~W&xUs~KNlWhE^ib}Bg*_5GvxS#sVYvBRv3nK6!bNM;l6tZ6Lew~JVnB{TZ7G2CQ zK|aF#dU%xijqn(8`}ep^5hrdexC{9Nard>e;7Q`ff`#xD^C#eG=1;*h%-?`#nPZ-d z9P>|*&ojq-76s}#2l7ScZSWFvIqytSWprqKQi;o?QttGUvITdQ*Nhs{Ba(cC6@mR`7-r| z+rcZ$cY;@$yWutFJHzYDaX;n(WB#`vkIye2C(G|4>v&wue+ah=;eBM`6u6h=r^0>A zaUOa6#5=@c$OniU3r~XwnJ3^O=9pKWF!LLb?_!>YN2t%sz@yX`&WFc{yVoBMj}tc* z;y&DypuY4+$S0{Ud;y-Keih!|J+i;e`L=Kc@)??6_%=Mt{9SmCIp(b=&m7l(Pl335 z)>f-+dWyu2MY#TZO3ZQJ;wdw4N1Y1uVel&Rk?%@)u3uK)k;O6|t=h-1nnqTxO@-E^Xvu=iah#T@S_A!TeiMzXh zjJ%Jyu^8`PL;S?u7k-3%fVi<3?`uPX%qJorV!kIl%pC7uL%Nvb{cA{s`OmGkIV8&b zm+%;KnKOsPnV*P!g1Ec-H+Yh`v3LeNMSbqm@HF+s^7ZDB4E56Qkk3+Id<#5B-2LO< z!1Kh7#klVpQeckrcSw=A`%I_RHiwjm8;h61%gi5wSD54c8d7DB^J_?r`Esjm4yiML zL;6s#eg8jdBV6{kc|I+j+Tv&H1oYj=MQ5&+@px9adl-vfAdbB6B-mV*XR) z%gpgUJFLPS*OOsY=61fu9Ovn2N=DyuS<&FkfM{%@H~352K$W^2{$szQFtrI6Gbs;#Q76qjCVl5Haij1k zJi`15xO`pnd?~yPPmp}aTXy{vaig#j`3&>-;W_4?!3)$M-ws}4?t)jC%Nrka=Nj`) zncpQu{bd~YUAmaRgnX3w-{EoQRX9B^#>*Sw^tjkRE-`n>(mJI9$mf}R;6>)RFWRNd zd{5+MznaIf6ol7V{%6uhIhh{;m)q~Ha}3Rc;5#o*=EAS|BqlDu)Dn{Hf5bv9#;>3*-=HaLWafdO1ZoG>5HmXA0vFk9aZH}rEH(tek^{5(g$F9qeuM;<3JyyEhOk-W%zd6P&hdWvR zGPsMlWBe4jhq&?Ty>Ksa$9PQ zjMs9=hiQIq%>U6{#EsYRzBxKV+_87kYMY~@#EsW5pGU`tI|5UYj}teRcfb?G9sA0r zIa+R~+5hFZ?;4$A`Po+69Gxa^EdK>OL)@{S51u7%EXVa^v>YdUyN=*V$mdzU4lfXQ z9EkZmx=7r3{XpbP#2p99aWO}ii5ssUk9>u=ULi@4**A=L2@H&(R4y~G_qPQrb}jTPhJe&UW$ z7#<*QtibUd6D00980X^{*{|mQuE6KVF|wZRj)Sj6eL1eS8!PUBM_7HFcVnW&jTJ@Y zW5gYY1mSVw#)?ni3F3~Q+Wk)wH{OufCv!}SxZ|g@thPBOP26|`pBKhth&z5-wc6&G zEOFzF>F^wJ$ItBkrR=Mr$J*K^Z$fl za3{+@4|frFoG=USA#S|20q!O4I57kF5jXxm6z(VPI5iFr5I0sP;6dV!v!=sC#Eq30 z=Uv0Z9cS6zMci0fLY)Y4$5|WUQR2qR8azhaadr_d_ro4%W0eb@p!qoF&#p=0#wtJZ zDdLW}?6eDisT8*k(M^?8XqZrf(HO`pACyZ#KgpXUFRgvrf7AN3w;s*X6 zL(?apxA)7jG=;p}Z`+N^V7PqVcE^Lb{`k_geq}%8GsGSF5avNN3zuEuCWxq{dndR~G1z&}@4oTk)Y|sBEGjJ!K!2Dc&D%?ff@l+P>A#SX`5$+}KczPJzN8DKbDBMrn@r=xw zy9J0Ft5+i*B<^^|?oWugfxjQk+$~Jp@!|_s+uW^-xUpt;c!ap))dD<9+*tEVc#OE? zwYl&(abpeU&u$6gj^&e(PZBrQ;Qe~H6mds)1o<>^qdNi5Sj*;o!`~Zh?k2~{cB31g zcXrFs{Cg3rZSIyQZgf8ZFA#UUHwRuMZgktnt3 z?K(c<#)nC(ZH|@o?BmjcfA`ZI8({hSkPi}XiFx56;>L$B!NbH`PRqc%h#Mc`{d8=E zc+2T_|D(i>^=_+ej*Ss-nUR6Vi5u(pfhUN!oQdneSUDc{I2h~k{qWcn&7XzyWo(+b zvHm=(ZH~B#zfOA93TOywx_x`H8pu5$EH$0CD3Zd|n$DB;Im4=FhkgapR+} zthPBWOuXd^*$;DE7jfg`U679uZ@D4?j}kXN{xLj8yyc1lJWkyB_yl-@c*_k1c#^nL z^T1QYTW*r$WsXY|H)?W!%yAjwEjP`u+UB?{aifOs$HwJ|x7@S@`8;u>HV0lH-g47M zc#*hK!~M*-67iOsL&%qj8?_2tF70{MayVfIZT9D3xj*Lk0L_2a zfqam7%cIlbA>zhoad?<`%ird~yNDZ~rQs3cEiYm|jE@pGKEu!7$H$1byoC3Q@pAk2 zei`!Xfj;K=1k2B~+UEEqabttrA9H+)c*~m=nc5vTcsf z5pSu~thPBmPu%$GS$KhX%e#Jfk+|`deSAyATi%T#UnXvRy#u^Lyye{tyh_~o8rT2v zvcL8?v{dD|o8#*&FZ*Xs0NeBb1H8XXaI(DIo;krqyycT^R@uz)50a8cw$-;xgTgN8gA>yJBj@N`R@z!y8f0@ulT=c>5nh+u0 ziu2Z-5G5|Q8(_7~2{GcW`N3?|DUYh?E=G*SFUT;2+yr1T~4uJ=ViDB`!J^z-z=?FG;}b#6`!;(kFuL`G3iBxRd2SfxC#e zUh09%WpjVsgWz74p9c347jD@RbE2Pk>!ms51H^^!lmvA>zV)Dm+ZQ^)gx4 zoY+NNxUWGzLcH}}nKLIwi3=l%e2jSOy?JtBk<%X-c6G+uhS;>+llI zzvP3Li3?#L#|rV*mu1_WSS2n@+z(Hz5pONcw%X>zI&op{DcujY=YMqx+)48u*);tw z;;o-xKKl_s9pM>)I$oOJ6o$+FZjO%!iHjk5c!GGF z*WPZDxES&V@+snN_&pEPpC&GbY(hRmylo8TgFj1L48{EM=ZLrMD)-0q%l_KqCWhku z(O;nX@pk{xWef^kl1T`YXiSCgT0wUnMSvUV(g#c-xP{@H%mU z-!C`iZZw{g^R4YLIWFcNPMRMcl6CfQ5pO%T0QV3VBa3h^@wT(%>&-oU#D!Pp%su?X z+pe>>8z3(1d9p{4c-xI~Kg~Vl>+Jm!-ZQP7xrZDN+uLqV!MjKu;k^zXA>MX(H#|yQ zcx&(&@wR&=!Q;fm&h797@izPVzekd|*x9Zl``hgQd>J$MNVELmR@>YoL%eOFy}w!F zV&}7w&k=8XLiWSlBTroHEVpOwQ6S#-U=ec-tEh zc$K)=KBw zhDV6EZHvL9#Kov5;PQEUJo~nfhR0d{U3h|cU;I5>=AKF7V)S;%r-=7;<9>e6G;uN7 zi+qN7U#}0IB`!uE0M8Nc>$UecPh5;X8u#UB4MaWVRK zqi^NYVPf`?}HxG#YRpZOp5 zGI;3VD0>Aw`0OXxtKgwTk-Y{UeD+ZGI(R5y&bI*`e0Bl%P4Lhme82ts7I^U4Rou71 zL$f>V`lESWg3sQ^-o^e|yZG$rbhpcyPep>{0MgMjry)8L^q^-Kq4z=H$s9?WR7SHVM@_2cOvy|2Ul-4~bFI>vr2M_()?Ee5f z_`+?vpT*4Azqi*sb{~8GeHoe+2KV-kavuQ?;(YwG^ykg_di(JGVODIUznj;IgL|Lk z``fGpco2Vq`y{yc=>mHSJcz%=o(A{$?~~B140sUd-#?j^1@~flrdc`g;J|5WrdfG# z@AEs@^>LYf2oBWWc+#vQ_A{n)Ujh#fJej==?#cb zb!k=|+&gf)nrT)8JUCEyG^+{j9i{i3X0^bB1KVn*S^A^d2k%&OJ#g4`w#k|8aG_&cXgjef`j^1OU)<3b=!M&SJ-vAE|?sDG*_inDTx4?r$h`kN&-CAYufCq^g z>|Jp0_IKEO;6Z}#4+rn#J->zf!bpDxdlB5*HkG{u9vpf-dl}r@&euso zpSOA5;LsGku`Az^UuFIhFy zArbIk!S3u)aPKc$*<;|rf-kYh!M*=CpFIH{EbOu;!F|W{De&MdecW_N8r*lPYNkUn z;K5n?IOvcpxbO1);gB47aF$+|4#|W2?mRWqAqDW@EZxx|MQ}e<=Dq|ToMl{pG}pNw zYI9#1>FexOaQ~AF*=yjz*%5aAxZ(Pr+{NA)=@aZtaDU$zdkZ``JHxJzcewti%=OS2 z=?mO= z0v??6KKE5{|F|Z54Lr#1!(IpX=TBvCfCt&b*qh+~nYyFdE$|@Ae_k-V4el@G>z{ug zba=j5{_}#_UF^>?`_Kaqvi#=-v-{wFRv!n=9)JhgZ>gCMWj5FUqUr4ZNZ(}-gZqp4 zetT#HJXrKM+(*Iv#oO3p;K8C#v&X^xCHgq%&;)p}=qT=!;QkUl)1fKwV9`=F)1hf_ z|6G2356yrFi`H?U1^3U@>(Ze)@Ljl^QN&Ez=K8KWiNvJ=e@~Z0uL7T*vsJl z`BC-?c(8a9dllS2zsX($4;Fupy$9ui^cf zlK~HwY~nr(?ickw(wrQ4u;d%u=fVB!`F%I103IxPi~AzDUs}#y0uPq_nY|3|Z!r5) z0S}h)ugB+9!Tk;T{Ao@NJXo4gGtH@k`x_nh26%9Om%XVz4qq>Q8`zEJw7`Sh{@l00 z{m1ma(3}o%)zO?j_D>Gj^+)Ni*FKkXbNxRxkKN}_ z@UJ&>RrWBr|NKVw2zZd=KaZXp1@~X!>uqifJjne_%``U-?tgbRdjdSTNRKo(2|npF zc{S7A6nJnE@7vro_@sk#+-Japi)!py@JR{X(Omtw;eKA!;$A<$@kw*`V=uru!9_jx zBKV}aK6?o~SZ2HoK8cTm=2pOiWiei-3O;FWi@gROEK9N1!6%(#ya66un&iF-KIsD8 z5kHUFhu~6u+%&h1z4>{txgGG}QvHKXG`9;rY1I}r)7&0-aH+Yj`rwn+47k@H<#_&} zU4I+*!GG)Pe3JQj?8AImC&*9cJ`6r-U5Pyc9^?;TkAhEHzmPo!9^_N(aqvkuPG?Vm z2l?~ali-tXuCVL%%y|X*_3UZvH@Dd{;6eUT_AL0MduOuez=JFF`Osl`@JUZj<-PzO zTw$KK2tMhlUEG(zgDd`ny$n9-X}(s!5<+Ir9;FDf7>omZF zq(s%Uv z&|&(#&GQB;_;op)b94QFM|X6%59YY#|_VSWm3ET@WFrU`~T!AId)%t zG#;!xk39@Nc`Bc8G6Ei~G#&+?{1Lq`l#GD~E1y#{CF9_eKU!c`Cy+AKk&8 z0uNUHFMAq%@-+Q?l+1t!SMzm}%z{rQzW$Rr@L-i$Cl5ZE-c&Os^?3~UbCrHRN*1x- zX9o8r@L-khC|L%d{7G}174TqHP0f_7f=~WrmR)~dj;GCDAL*;?4e(&qfV~Mm`2c+! zlx%?qtBY!;q<$Xrypsc(j(l@vdgHQhcCiVz;P}Jv1M?}FV|A1fLBVyn|@y}|eBjVtbf3TW80Ulhx z4|@`P@{gvnr@({j=dq{3r%Zl?Jp&%B(dR=)WWlFIwsEib+q{m!8vS@WB9HwiOWYU0 zgEeLLBKVX~^Ywg$ew?iT68HLX#;1H%uS-W%V4dKG2=`U+DKWDTHSpjDeIXG4Jm7Hs zFPhh@G1BXqj%b2UIm}Tr9nk_0*6NXtXoF8V>|O3V;K5q{e(Z=Y_>?Q6YNjK4;K5q` zd~`$~e99GDxgUTBYyVsOk<5JkPq|_jyN~@k{{68d!{Aev&*we@9;{oy9tEGGzs=c= zj*Ni^>o#&92cNPc!=3;S*1f`>1fQ}(Z!{g50uR>x8~17ODJ$M#*Uw{K_h9|L>{;xu zaCbuIP^cyMEq zy$U|1++(kS2cetxqLQ|?&C-Wci6W^aN|xqBwNJ}$FPP%_7<&%^kXd(8fHV4Yw? znAhooPkC?$yFL%IPOxDfdmsB}O+Nq+9!Tn5|2Fyu|E<6OnY#T=c3*uo9{fljHyx#q z%lOoP((BSu5$tz1c%3Nt)Hl2AG4NogK5jZH4nFmtjVHi^xA^z>j!J@0eM=u79hCwP z-pZ(%j!J`1ed}Fz{dsdm8*TlcZ&!Kc2%`+rmcJa~)Wzeg3pr~ceg zGaXd|58nC(dl`J{&+F_J@ZewcOh;A0r~ZdNK02xf9{f0>W;#l*XZB(0dwL(~sK!YD z74DnhyPJQ{?Wh)b@Z+kQ>8LjN?xE@I9q_>X`_D&p!FLZWR}`EO_{UE$lgP+C9vk2M_Bn zo4e7x0yyo?_sMxh@bKr&`XzAMT^}FKD}#r>!0(rN6>!?!>{}H){Dq{NX;rK62!G#lG&5iS;X|^zKiVJZ3+!R=aEj0O=mc`X3b@1>-+tf@)>*tZ<-_G8|{$jIE3!L`ss+o>%gNH9M$Jqg=k2>6U!NZsAWbc8~ zM`P@L@bIO&qoW7l^wAS^e+;v^{;y85`}_(0eGz@M#U2I^o8Ol>CIU_$<=>AzCJG+D zhR^pHeZ0f{{McmG(J^uCuV2WX0H=?Ao;?X3Uejbxfz!v#`KH0cYjfOZ!0BTbvuDA> z>-cp!Mn6uD|5onv*x$^r*D(cfnx>DBjwynNYjri#F(q)C)?qJ$hhH@7RKRH;eLi$d z6+GPV)lA3Kz-b@-csfQuzd5h)OA+oHBYl%ye;oGzWR9nW{k~!D+u-3JyvyDJr+v*j zU2w~Kllva{$MqjK?MBB8z%6^N?)7gUfAHV>dLH)2>f#bGdS>6OkLdHHRDDd}1h@7u>+6qZ9a+DFeK@LSN_D}d?}J5JgjUKKUdaV2o+_2-AjzgL6%>X^O`Zte9B_xf>W9a+DHeGucm4K95b+zL9}_rRqe zfLj#S{qfAB_y6Pd=go0iks9|ASZBEY@%ng&{m1qB)A8{!eG=T-yP#$|J_Ro8XTYt! zc|VWOf=izVw?2_nGaav=#~i2hrICIU_hoSDtKimjhkLzG6W4EGe*nL~j&Fj?`fYIQ zfPtFn_zt-AJ#g!D3%T!u4}1OF93Om+@3%9%y8p62rjLMI2d(Bl3NGu%!L5U~bDsd0 zJ_T-ZMqf^YOP>X|5+ya$mvi9K7r?DUW^!Kym%a>c9n#{y0xo?G+?vh%|K&Qk^i6PU z_M2*^FYD(quZ#4Zk^X(|yWrCI!L7M@?g!w*{sbS~np@QU31M*Qqu|!u2KO;==@a0V z`S&PJNP^`ktWoY2xv;u;<@@rxV)XvVIrb;@?lC6ZCmcTz`Q5(R@57GLPQ>PYh#!ET8X* z5%A&hoEQVQj@9Q+C&t00Pl8*=>f@#pQ{d8Pz^&9;HPeY%aOv~l7Jq%B6AR$dm%yzr zPgOIWSO%BA3T~Zb_NN9eeFNP3Du2B=u?a4H8{GP8RLyi^2V8o+f97?!PMO1f|HFIz z+w>oNj_?1czNh=I_+$DAxOG~F`zZMEc)k(`w@xc?p8%IW1#X>I;XVy6eHPq0o$rTV z$$?9+pMQA1nU3n{E5$K=8QeO9_wy_IxXe1Ter=@hs+qo02baDHZk_W8_bqVg_3@hL zwHBGUCi&Vlk(uw7s0KIqiUv;O5oC0z^#kT@l?U3uY+6W_a{$kfJ@&3 zw=RvUnNDhhOWy^zF5Sv~4_x{IxOFMN-_y*a_y2Sl`^)%#n2vxC*H6d5t;-T>rgR)! z`Xsn@nLa*Br@*DpfLm8&)l6yq(Y!CD&tt#5$bA7^`VzRc{9W$L;L=yYE%Wmm=^D87 z4RCA4YBf{32`+sb+`7{2e+OLp9=K(Gjx^l|ANF5mHuwK4=P97C`eXVCxOEj@Z(ogq z%ldI}Yo&fXeKi3teG1&VS|2xkH4QF(7Tj7@QZs!u2QGa9+*;M+z6dUT8QfYO=Dq?h zeGS}No#(y|E`1Z+THWBj1ulID+`1;leHUE%KDc#_UYEW)03Y@z`{35K2{qHn`n)H+ zzfO*h^i}TlaZT*?dd98m`0K^VNmxgYCk<|0$6p^$&VWmw1GkF&{yI4iE`1T)x_+&i z>Esf)^c8UH`YQMOqd70>>m&Vk?i=9Jx4^CIJKVRyrSF1UYYN==z@;C6Tju8sPhlRt z|DO`Z{)VM$rc)x|!}U*zfm=7|eWX+3;L<0-t#x{&Q&Qm4XTYs>MK#kYS#as|;MV&2 z-0OXs@Oqu1kJq@hp09^f%CL^CUj?_;^Xq#`4P5#LxV3(ln(34#xb$ss>&AK9cfh6Z zfm=7~{i0L);KN>T#|NL|@BeSCs*dLSWBLfVRocjX6kOJigIgti+%!J{E`18zD)Iiz zPlHRJ1-EW0shQ^Iz@;yMTQ@hjFM>;72DfgBabE$Kz6NgHGT^=rE`1Z+y0yc73tW2r z`Qhtz+d}TUWBNY0bsJxw^Yw9=b%yJo>SJF%LCth(7+m@&xK*yInNHRFG;#gJNZ;c= z2`=lW!L2*^`ad-TE`1K%+RXcNY93trBDl4Ai<;@w61emgaBFj&`zpBf`txQVtj&Gy z8)NzwxOKO~eH&cX?}A%*r@7brYMxj6!AQSU_op$B-v3VvkM#OD=(GstwSHZ1& zyK1J>YT(j0z^(hHbKeA)UhlU#PU``E{&ZSrOt1HQ*gsg|_4^;*pU!OV|Bvwf;dFmY z9|5->-JxbWJqkWN{?p^&)}sUN6X4RPz^%vl>&xkBaOtz))?@tZ+0%32(igz3C-ze_ zon8c&z6@?XVa~S#E`1H$dcqt}9bEb*xb;+4&2)MTT>1{U^;DDlF1Yl4aO-KkFLe3< zeAs7vaO)X9{!AEL`Y5>db>5#$3|#sIxK-EZNtq)G4sLxTsb9ehS`68T*jsE!aCLq zdVeX?1DAdPZoSx1GwHvs{K0?g?*oVZ8DZ?dIgk4Yxb!h_>zjN%oDm0?J_&AZe?-l6 zMhaZ|47l}5g8MAE^m%a0{QDAT6u_k~fm^TYT6>#bG{teH! z6;&ObSs&9k!L5Jb`^lLta9O_tZvBHfo-VlbeQ@g^^}f)V1Mp$Lzz4U?&nqqngG(O; zx897anfT|L%|1w}`XadX1HRub zD1poR6>#f^Q8m+oD!BCe`G?2z!z}lWF?|c%`XPV+v!D$w>vzGe9~IP03wq$v55TP- z>6!E|lRo&|$o+p|82g=@)JzK_;KTJ7#=xze{Qg=P2bVqxZoM^A&9pEDE`0{vdMn3$ z7F_x~xb>FaM_O0_m%apU{R@A8zOW1~eHGmLaa7H;um&!D1Kj$tIsPWN^!j+s>tb~h zs-uOSF?|o*>gX4i7WTo1>z~DJ?*E;l0y@hd(?`Iqjy_L1D+(^_$HA?RJ|8+upNBaw z=~LLh&DXjSY;MUu#)l6sQz@;yMTW{~+z6dUT8Ql75Qq6Q$1zdW)ALhI)^Y4M2 zRUgwg!L6S+)J$i!z-9doxb=%7_g!%5`{33u-sFA&KJ3r-!L4pY_h*N}rH_JJ-KE^e zz@<-sTfa2u)Luk^mt*?Dm3i{RF~e7&7r0++r5Zv93-ADvwV zmtH^r@Ot=7O?7m3V@%%yw|?^;_ib=lzYA{tHmPPhy9X})0Nnc5cXWRa^XUEmoG|vi z821tI;ri$3&kxV{cPZ}UWBMex_4_pU`f+9*SwDmQAE$Aj1(!Y#ZvD|5e*s+j61eqm zrZ0m_Uj?`NeE&J81}=RA+%o^3_Bl;(>D%Dee?`?y=XAiO?}1x?=J(?{eehwQWj6Q! zzid}PS$|9)0k;PFJSiImm-XY|_TTXFWE0@hr@(D{zM3hU2A4hyZaYaeQ#J=KeF5Bd z-{rmtE`1r?4)OQr*$TMydOyr{Ylr5jjvV zAAk@0MS8vA@kgfXeo=T#9|gA~y#AsXxU8Q5wC52u=gj$5z@@K&+n?7REvbV` z-vqa3^wdmCTHw-m!0j3DbKeD*z7KAHf$z^t2H?YfsSj@d-Apyp(lEI6QE>b3x@xAS zF>vV<;Pw|+bDsp4J`HZi0%4()_ z^~Z_FQ^kI6jQbk6tlt2)=kDUZ2`+sb+&*k0_Z@KQd*JrrbGYw=5Bu|&&HexIcXfZB zKc+eyA3o)-m|_2c075qv!7CBUUmf!jyCsb)Ga4K95a+&+@;C+FqBr7wWnM@>^R zomT{xz6@^9+mHJSxb!t}`)Iy@o>vE#z6ow0&DXSvE`1-|K6Zwh z>AV5>us`1iw~yuhKR*mEeH7eIEmJd{9|M;@0d5~Ro%E3R5P7l0++r5Zl9o!kIt`xOJ4`KPu!?xI==xfeGA;?pPQ%i+u+i7!EOFI zZ#usRF8u)9{wnXM*|yR9e=dyu$ptl2E&@JWKNkbHPv+|<7YCO<32yVxDN-&4E`0{v zK21NKa#?Wc^WgUB9W_%3J&&G$9BpaL#^4ctCopC?^V z2baDHZl7eg4-9q&wUA8`U<$c zOz#U_SOu594sKt}-#=W~0GGZ6ZeOfBy08r{eHYxmL?0hr*aMe-0B&E(`*{)b=>7j9 zeSX8&D?e3*bWsG>8Lod(4BWnqub+$J;L<0-?aTT4xhMrLeFogV;$1b2w^wZ8z6maU8{EEz8?7RWt!Jr`UtFJUzJibEsKIn9|yOu()&Wo65!J7_003y1)b3{ zy|2cl&%!!(VWFC7Sq@x!UeKKH$~yPOF?|`_zIq$?6>wQ!pPxBSdsT#c{XE8{Z^AnE zss{HhaOpeX_UbtIU2y69;Pz^Le6(x;KI|{{!R>4Ganr?NaOtDq_BBm4)5S4x=@a1g zwR}IhI0-I&8r;549|v8W0hc}pZeO=a&2(`dT>2uoUF7%I#U*g*E8uof9|v7r1(&`K zZWp(znfSgw;d;2Zh5hwJ0&vVIrbHvb;r#XWH82jKSgWi`_!%%k`JOTr_4m-`6# zaQ#bS;Px8c&r9Oq(kH>~HEA`|B`I*}^?sXuv)4SreRfQr2e;So*NaOE;Ie)R+`b{H zX1b&dE`1f8|2e(g=#m<^^bK%(ZBxy3iT-HLOZxUmzmxk8xb!`6d%fA8KKQV|l-b-5 zZ=9)sF7?Os5pes)9QRRhSw9YL-&o;30WN(C+`h5JeHvW)EVy0T!M#4N3Hy0z0ekcB zqF-7Bm-Wlw_J)R<=~8_j6W6a{e^ZqEI=HOg1h;SEuh*CA^PaeV2m6hDKe@CEF6;Nf z?OUR1rb`Fl!#?kW+qWilpAUme9|gB>ozHy?T>1pKeOs3MB)IfxaC=ix&6Ll8OP>R` z@8a)g@_BISi{SQM@2Q#cC2;90;Pz&R`zpBfb#QyLemvzH;L^9i?ac!z^)HKo+Yj*l=dw7s^ht30!D(uy z%TnOdXTa?To7`u?rO$)gRdYNAaOq3n_Cxyo>9R7o^i^>CAwAP&HE`)0;P%75n(4A8 zxb$ss`(eEGkuQ=d~Z7r#iYkJ*Lls+mCPKJ_j!A7r^b>4DO5I(wD*QT8sM%xb!t} z`-%D7*TJQ4g4<8<`|)!9JZ2xH?_mGrdupc3yWrCI!R@Cq+z-Hq{S`jA{S;q6SA@Z( zkAmA<`FyX4flHqNx3}v3qAQZ%(x<`grx&W3uF&T(VSld3VgF2)`#iX;Uj(2O~H zm%ajSe|;18RdDI+;C5XfH(k*Hm%asVKWq9nxb$6c`&oUSbVUzb`T@B8+%7fKa^}(d z|MD>Q&-43nc?5j8{_+^O{k%R7S{?_NJ_&BWuv5*nJOwVj-VgJ-+b=HTK0Bt@`!Vdl z>2O~d)0e>Qm-TVe@-ny_PZivL+3ZgZT>1vM{jxqETHXYgz71}_Y|gg>F1_CG;rYH? zR2?nve|XRTUXk?MbicwM(?`JV?K8NKf)9^ph2Fp6`memhePT?X0=Hk|*NcB&&8#Eq zXGi*$nrTH2T>1jIy;Hxgw4w+uz5cv;Ui&Tn`n94mrmun9Z*5UCt*C>``b}{At*zYa z^DxgVeFys=>+_@)U2y69;I{euAS(vo!~RMi-0nQ0X1X#AE`1c-?&x*t${4ux32^&u zzMij4f=izUx8H87nXb%$ORx9C?3?`)-O-i#F?|u-{^@>drYlR}vVH~J{^@+~tKibt z!R?>!;=TbceGA-vXD#>oyv=z@-^KoCQ@QVfOFsa&f5xxxRm`LJ|Et2-|D4~yS4F^w z>t7WEx4U|ztK#6&C&BG*LCwU!uRP&=ugZ+{{Pp6hEV!(n2e*Iuo|@?@{X7%b$JgEd z)imzQu#T)>1-E~dR5S7Kb5C5qG1Bw(e^nD))^CH`zc$Cy0hb(KtU)!Yw0ykF^q+y9=|{mL--aQ&50aQokPavuYiJ^^m`E8Hi+rB8#~eZ4QV zG6ODs4&3hZ`+cQ853>)_7qS17em+`R0+(JNuUW_bQ&V-cvO1=(gWLb1_k~tAz-4`X ze#7JWqVBD1-Jim3->*6S$_a-|5r`-S2K^^|E~^X|L1w!N5F^cU#-t?c>I6S z8C@M8(-dD2^(l@Ywe zt9#(m55S%2@2Z)uVIIBzUlYcDpZ&OxfDhNdCI;^8vz+@lxb#VIXP+J1r@*DpfIFYm zGhLGfmp%{f?3+_FT~h#;z69<>XL4T#m%a+_d`h1uT~h;>z5(uh%A9W#T>3V+^J(4D zH63v2d*IG~bv4s9eehv_Ei=FF&S$#1zt$hqN5CEP_r3P)6Q9Aoex8ZPlN;$f+~>h%{UW$?V3_+7xbzip=fEuYRdDI+ z;Leveao+%!z6I{gpVk;Lc&&)l9_>xb!`6=PNooiA6o&w)!{0C!G& zm-`~P^ks185JgbV!bc4rUWj11>E7kr%h|B;L_K@opa}?nbtJGrEh^d=kon(O&eVLF1T~v zn`)*tJ#gvu`I&ula#MAG1Lvdn{~N-vj&ot0`v~}O{TpK7&V^;}_2bO*N}t63qJ`Y2 zz@^WCJLdm4bVC+g`aHOEsor`0#l2?d*fk>96C?a$bL}KcJ6ATAT%`U1FfHGjQcTLhQB4DMXLQ_Zxt z0xo?G+_`2M_jPdTo8ZnhZSGs((s#fe{{D&9cEP3ZgFD5%nrZC-eAut^!5#DS=j+1Y z(nrCa>-9|QV&Kvzz@6)pYNmDiqqz>HPh-DkI`2u)Dnr+XTx&t+u+i7!JQ3FHPgBtxby>X=O(>hw4QnN z{=Ytq{ml_I(|Y}J!hWugVZSlXz5Y0{Phx*dg8LNs@c7qfz@1x)+-Je1&x1R+yw80B zT>28Yb8Cb9GPv|raHqV9`x?0PdOfobPPxT>b4=d`cW&qFXMG1;*6)Ekw^!9n>-*rt z{zhhV|G$H;{~P@=eFWUOQ=cc@7zLO07=5>@_?}u^cZhiiAV;$C!z6tK!GgHmPfA7Jp zBR#(E&V3uX@4`CL_raYl{QcgI1Mp#A^1+?^D{7`v7+m@&xbr}Ud%dp{j=z)`>3Kg( zNpM*|4emV1uUAPw&&2g}BYjuRRLX5iaQzK2 zaHn45J`OH@65M%?uZImOaOpGP&Nh8Mv>^*FeIDHThFQM=E`15yd9k8q+E50Uz6$O% zx@x8k`n=6`B7FnCU$-arZS2j@b8hH>%lbWVXS;qr+Rz6d_BSz``~NFD6wpon zm_7pTyt!$qE`1i|fU--BbjZ z^~>PS>(kUsH&wu;$JgC?J;r@~Oy2}|UZ2B#3tZOkfIF}2eW9DW;L`WOo!9efrke)f z!~SL;+<9F;AKe@Vmp%&aypd5e-K@`h!v5c!z<$R&+$X_heSLmr9p}5|c=Yoamp%vU zI4yp^->lDjVqe7mA5Ks+-CP2f^()}c_cn1~1(&`K?tFhg?i=9Jx4@n6>&Mg0ZE)$k z;LZflEICcYegjzma+L{=YFi((A|5#t8Uu{f#kj$NYRX|2km8{%=fT|5jYh zv@r!P>u10n^Yhgkv*6O_!JU8c)l3@;;L?}Coqu_c`!cxnRdDCW{Qd358o2ZgaHqr9 z!^S4K^lfmbqdVHz0hhi9?!3*{|HeM}u)l@b-2Z9|f27 z77FQzJbe|1D{7Sw9Qz{9>t^>6RS0^!U0vU4Fgz*O3#Crwr>jzl`zv z6>wR<2JZZFJNI>P>6_rrFS}}{TUy}K-Nrn6|GzCf($7~j-4+2Ku76t$+}Rc9UVoghpSLBk|6_`K{c&QS!M?8_ zPq$^k<#_Vo&Y!lanQkk9OJ4$a{)6AYx0S)AuYx=O*;6y!Rs)y50q(q);JyhieH+|) zZ#DNFaOw5)n|*WM>vOL^8XxxNw9NhgzxaA9`yal3IRftdIj7ezN5N(NIJooYrkbg& z&wJwWq(=H(+^4~1{Vcfie|lf2tdDEr`h}6cuVyM2!Dan2xVxJ<-wL?&HE{QD-cvJ` z>)_Hi!QGHPf8yU4oOt~D{EWMy46omXb!2^ge#732azFU+{&pYS^?JI$Jq$iv|Mn=j zJ828|F>vV<;O?GL?vvosr@`I5rgN{)+nks5xsiT3_jz#Xi{Ngskoyw2^c8TIlH6Cp zrLTj#{1M=?CC$WUZR%4(8GO{~clM_g=<* z1bn#u9WiisZ?k?JT>2!q`-!re>5de*^cis1{JiTOS#as|;O=xi(;WqH=}X}5^heZ8 zcj)7qaJ}789qIXgbw>?c)^C8j`y|y&cQnDJZ-cx0Z0EiME`1N&-RDj2`{2X=PG)}H z-FQUzclu*`{duzw?tyjgqhtCwxcep3C&1-+QsD02C)G@MropApg1a+kaGwL0z5wnX z#P63oi{R3i!QEL~)l7F*z@@K&y9ZC@UZ1zwH|d)reUkeYxbz)xH(~a(3od;h+&$Es z?*M$*Z}P$2ISDn>rZBklQE+$eJnm!Q((CoiKDdYR{d`k$OrHjKk2*okv?&8F>*v5- zeK@<(raZXxMR0fCHZ{|x61emgaQ7I#-ZoXirLTj#$7a<`n;PKKx4_+$J|EiD2A94I z?jBcGGi~aDOFsa2kDsRdyO>As|96G4|8kT22>5XQyJFz(i8Hy6gG-+TcfZ2#m%CEn z(r3WklN>eEU0HDH^Wg4DJGn1_OJ4$aPvO`1F1@ejx|O~<(ihcCch$h9Z-Bd}w774A zOWy`}=kxpJt`4~LJ#cqEU(a{-!H4~3W^?~PHL8F%`(yeDxO=Kzmp1FiP1v8!aqLgm z>(b@~xExOk+|B59X>%G}`YgD6MqACaIR`F%0o+}vkApTB!KE*QyJzY1A^vx~CZ2B% z`?Ke$nKswKW&I|&dv1gK7P#~saQD10_g!%5`{3>c)3_gi5Bs})aQ8xe+;q2oo(boB zcNF_e`2Bcy3|!XN`)$^7m-F>}cXCXh26vb5P&3_~0hjf2;4c5W8+3ObT>2uoThJZd zT>_WB0`9KbrDnRj3NC#e+`T5MX1copE`1B!HUB>9-EDB`yWnndyPE0l9=P-aaQAvX zesfx*_y0;5`?X0mQzZgET)z?nch{QrQ{e6`1vS$>X>jSY z;4c3@FWr*^mtL=DUKjV)cU4FC6vy;saJOvEw*oHf*TCKKerl$B>fq8h!QJv!?pxr} zcfj59fcq}E^nGymcKvv|M?cQ&!?3^C$Nr9Y)J*q=!KIIayLU#okAX{{0C(?P%Y71D z`ZTz^iC?dKGvLzaz}>s}db>9dE`1T)xX6J`TFK3NC#e+`W5?n(5vK zxb!V>x58hq?`?xi-vxIo8`Vtr_Q0hdfV*3CNB1$0-v93lV}C#2pYMx+57)mh2JYU! zP0e&)99;S&xLf7-%Y7+u>Ggh_>(;I6neNMu>GR-jwX9~kuK+IVm%!cX4(|2i%=1cL z#r`34zBO>^8{qEOHmRBJYl2JP26rEhaNhx!z6b6;x|I7q_^{WP;|HG$)aTtto9ZKN z@zqD;((C6p?mjk;`{jY;XVN_>!-lor}_Q6B@Hfp7To>1qh{KY1DC!4?tZ<_ zeGy#xGPqk0b6)|Mz6S0-tB;$u=;tx}CVdn8=lK4=r3Eg12i$E$)l6Hu;L`WOUGwv4 zTL$36{(c|a{iZ%{x<3pqeH7f?K2yzfe+*pu1i0ILpZg@Z^l5PSwYA)5z@^WDyXNP+ z?$3iuUj%nwU#4cdzXUFQ1>7}1?{t3^Tzb8K=5=>>L{&%kH^%fWaCe84gee-B*x0l53!u$t)s=F$8A1N!sBkQX_AO`NXbVm=w!KF`vyZ@L~ zGd++3mp%jT{^L8`XThbuOYNiL%;L>No-45SBAIyPEUjTRC)-yd=1ed-H?!LWK&GcXeT>2Wg z`;)2M*TJQ4g1bL`llvC9^c`^b7kZ=zyWrCI!QEdiR5Lv|03Y^MAKd*_SNGL0xb#tQ z_t!-=Q#A%IeFEJ5^&{LT!KF`wyT8$oC;q-j3(ig$qo_;)4OW@L1 zz}+5S&($ip^mTBz*HklA8{pEnz};S-`!=}rU2ykzNi|co2QK{p-2L4a-9N-UdjEeY zjQ#KR`OrfV@ZtIo#lYP^@b&*t99;S&xci5Kn(3hwxbzut_YYm}v*6O_!QEX^HPb`- zc_v&x50yrG{e1LL8C=${g1fu;dVZ(|E`0;s-PKn!J=6r3z76jFv8ZNxr~@v258VB? z{kYf9GvR!{#<{uw|N9BL|C$f$4A=i!1l;WxxQ~KM9|w2)?{l93mp%pV{;A4+8eIA; zxceV`fBRYvTzY-HW*^-DoUc0iT5(KY26x|E%6$b~)~|uP|7H3*xb#hM_b=vnTHw-m zz}-P#&GfY{xb*t-!}I;`cex*Yc>k~u?*3Ju4?P?PAFls!6x@AZ&-8E%T>1pK`~OUz z1eZPy?*6YnK6*FKt2>5XQNA&ZX=MDKA zxsQ+Oli;Dr`uK?dKO|-ySwDmQlzD2VN3!73=fOi$mvdhLm%aoZ`p8=D%iz*i!9#n_ z;l2hgeFHqSm);k8qzNv48$7hvYBkd%9dPM;;2~PbeII<-Kg!Indx*B`{!xES9{~?V zlH5nZWqo~q=5-92pC5ZPF{V#}ho*fTz5pKDH=|~HvAT>exmh*SV?A)` z2jHQ@cIf_b=F$8A<6-O%=hye~2>5XQ$7A53o>qd3;FBI<4ths+u)&v zRW;M&9dPOO@tWfdEz})7-v9949HzPdpOw(-*ZeVk1Uz&$-ydpG@Zs^);^3jP-%~Ty z^z)eGls<+1IsAUFrNO1of``s&shMgyaOn%+p)6lNwIaCmW$@4c<8eEYNjW8;L;DkLzkCz|0MJ1{r}1E zNUx8Bo{WGG*MBkw9=al-W_mIXE`1U_bVZH(6u9&m@X+!i_gQf1^WdT7T{Y8_1#sz0 z;GruW?#tlPSHVM9?&Myd$AtIqlMU>z;`_ssO>kMi4IV0_)l5%zz@_hjhgS0S@MIr+ z*gwT=?*A({DxjzQF?|F)v~nBwQE*v54j#I?!hHf<`V@F*Rf&6j-V^rosVw%Z^?A}$ zIdECO03N#LJvGx)MR4iM;Gt`~YNn_3c}zT>8ur)Co>teMgDsCR0~}C4tS`z zQ_b{L7hHP1ALcki*PDLu;r&)0JhXPZnrUkoe7OGBD0pZ+pYPTfxbz9|(E6&HX=@T( z`ZRcGJzsBIGvLzaz(b{}YNoAuaOsQSA@lQcTT9^5SHMI3@2b$&D!BA@@X$?j)J$6& z;L^9iLpSN;psj6i>AT>eoA~wJ+5?w<03NzIs%CncdG!AObQt?GAOF)4@ZtJT$G}7U z_a5o#IJopl@KAY&n(65jxbzwDP`Sf>7F_x~c<7Fq+!w&5*ZVzuzuYyS`|_B+3Le^Q zj=u&j>o>qd{PTA7bQ4_qHhAdX{nSiP>+?3pDSdCGPjcS}ANJ2MoBRKLyg$$QWBLeq z=>DRb>6s|FtRDvt-M@wV1i17m@X&*NJwKBMmp%&~;y>4*@pp%-Ji|9Ti)`Y3ql#UA(iaTBi3uP3m7smXm3T-Hy6hh8pop8=OX2Oip9;XV&8 zeGxpgeH-^BaOw4W=Db4NJKR^t^mXvitGc7FH^6267I^4;)74C0Z-YzU1rNQQ=Dr6m z{Qx}ldtdkFFh}qI{PS+Z^Zf(wPdx(b4A-y6z(app%j?I%rB8x~{(BSmDRAjC;Gw@J zxX*%1p9lBs8utZo=}X{VD9U{qT>2`w7b>cm_}{%V`zCz@dyik=dJ|mwHn`{U`?uZ! zm%az?c>^_5y$?R@pJnFP-SdmOf7Tz1{Uw+Fw!p6!B5-v{^h;P>yd1Mp%0oIXFZ&)y!qKhK57^igmxoK-VD7Xz2| z6X4#SrcZ)Pp9c3nR#h`SmjRbP2kuSd{drFBt2r;}iz7W>&(D>>rLTZ{d(BWYJy!*n zz7Fo~W%jcHE`1B!3wEfPo@;|k-v##~df(}}9=P-aa4(WoGd<5ddjEeuJksm)r{^Q! z!}Xun&u{kGi|FH~=i_7gB)IqSgqrF36u7LP0rx&$hO_deC+J`FB?7To)EhWi}2 z^aXJ5(;e=M;L?}Dy}#9ur)?E*>1*KLXJ)FIw$;IsH{x^JZFBaGRH^Si3N5Q=>^7a3X7`XHaa4*jL^Nl39^l5PK__mtq z8yRrvbKu^|N$&ID(ig$K`3>$%;L=yXy;FB_Uj>)G4(?^PbFZJrypGbhuwPK%UZ3~G zzB|&Fx$l9?`U7xp0l!`^Fpu8Grgeqb;AC?P#x*t=e`Cm>o>r?C3Wtb z;L^9jy`@n#(+eGN>3iVbxfSmF;KTk!W^?~PFQWSw^?6S?-xu}gjeF5Jgr)uu0jOJ4!^R!>zk zHLBp!*TKEj?{MD$m%at=U8^5YjW)RSU2xC*c~heYF8u)9D{fRXeUo|g{{PJ|_G=Q{ zN5F^ce=`Q|t=YnT99;S&xVL6I_bG7cGvMBu_qor4OP>e#ZqSdXZx+C%FM)eE^wmt? zEQ3p51^3p@=e`CmeFNNE+vC0oE`1x^Tc^*5zS#knUax1~M_$RCuRags!~P{+#@zom z?5BWU@;`k2mm=Wah7|Wva9KYN?rkV>p8%I$uV;?a+pv}U^q4*i?rq@r?@KvwS-$}8 zmA9#xUMhl1Uk3MXpU!;+T>2Wgce^>?I=J*raPM~A(Mv6G={w+_`S%lE>Viw(2lwu* zs+nFIfDikZeQ@tiem}k(2A4hx?%m1z|8fjm`UJRlXIIViauQtnG`P1(9|ygh0hc}p z?rnNc&Gd2}T>2uoch^krOW@L1z`eWp`h2+xE`1%`VgCxVx&J@F`~QkRrjLMo{P%e2l_^sd3fy~e7x!s!>9gQo)vTWbm%ae*J;c|;D@AbW%i!ME`2O%p1zh?Xxc4=^ z@AOI?T>2)s_pm-bdZh&}eFxmDZBsM7(gl}ZKfie&dHnMk^vdAF`&WH%@0o;}>D4g! zaQ#=K;2!@uJ-r$OmtLaU`|s#=iT_-8VqeGpyL`R9+5ngJTj1V5gw;&1w!x+Ef_wi|R5QKW1DAdP z?!C$9`z_|t`~SD}^PBVX{@L{Uqw(ST--_Y-|6ElweJc(weG=UJUXJ?|xbzut?|Uuo zv*6O_!M*RV=Dq+feF@zA{(Ib)!KJT)dq2=KeX9m8eFNP4VM5LHttPniZE$br9PT^d z()Yl<9}l?igAe;Av$_A9|6fegAJa#`y`MzYOilf9!u8OMkMz9%O?}=I`_xF!$KOnY z%kgKyy`S>y)y#oQUjX;sX{ni-MR4iM;NH*7@l?R2uYr3%H+>yk`X;#d3*AvuAJ@eF z>0sa0`$EkwxUAm?_qyhM^>IyD|1}@`U#?X%y%q)^uK!vT-1`+D&ucMo=@a1IZ}dp7 zCBdcF>zU*9e%n+Xy_QiQjZ2>c_kPRQ!)tkP>5Jgrzv}a(*Gk~hSHQjBEmSkTRt1;7 z4({#J`$DfZz@=}2d%Mj3x51_Ff_uAms+nHvflEIC_x?Ct_pdXL-v3|M`(gIk`{O3= zBe2eJ{nz#R4g3BM?&D+nB)IpVk8qy?m-RE?-v8+1qt~sYiOF3od;h+}~3l z2fZ->AND(ZaQ~xo)J!|{d6<2WK8pRvO5Df5rB8tS)1urb!KF`w`_pRNXTYV;f%|(c ztIxeYkBR47!9L($Z|AT?m-un1x zM-N>30l2^S4mH!anMd#c-wu!Tyg&TwqzU`;?HKl-h^m>s9S0vC&$pBRpRW6le{;U~ zKduzl&-W)N*BeDp6IKLeK@k)|5mlB^gmq;2p5^THoMl^gPQIISPG;Q}TWvu{wsl)< zJ!jdOZQ0iCo9z@SEh$B4X|zSq5)?u4b$h>G@9TAYCi2&3ulwimdF8sUB-iIdKPzx+ z-#qmxaPBkUmic{~d$Zu&>wV@pE&c6ohv44An7#yVO`WIa;NCJg@2`SeQ(M&6z`1XL zTl?wj65QJa=e`YY?WfNtxVHn&eGlB)Paj8cZy!AD?;|$%|NXWp2=24S^b*|KKS_NQ zocHVV3$OqF`h0@>5@Y%lxOD*C5AREZ^ZqQj71h@_xGx9JeF5A$D5mD%z9KmHWpL|Y z`hIa=1)TdDxOH$}&B1+jaPFJn*0g2Rx4^mYfLn*?Ik>M2&b@y8@cMtk9M9n6`}-|$ zE0$1mKz~o9*%R)+KZ^a~^!?&~eO*U;z0bHceG2VQ!XDnA2Dgq_NPPyJ`y9A+WS)Bc z(L65qMeL87L466F`wF;a{(g-6tKi(%!L6gEnuGfr;M}*stz)KA-v;Nt3vL~2j=u-a z{Q%sW5mR$u9(MTt--=*=9POvS*K_20w)8&Z*70fDABR2R@wAfQ)~7Pmr@*<_pAYvZ z%G77a^m%aWgr=H0Y;M}Lct@Ea-Iarwn=RON=ov+V7SeXOoz5s4rP*QWSvIx$-zTW1#Sr=N=SH|=; zaO)y{p25mGIPY(QTNkI)9IR}CbKe2CE;Gl|1?RpGZe6xS&B4k6c-TLn_l4&>kDlKL zB4hd}xRouaId~uj&inQC2=`}~QlA{tr@^gj%G77Td4CSvnqO3N@IW4%d;R(Fc;@%0 zFOBId;MTQssjq_b{yMmIt+_r8aPC{+*0uWaf(P2*+;_pP>rAiD!<-lQ1MGA3&le98 z58wYEj12Ykd_5?^!~GA&z^%{gE_g5w&V3Tx`g~E%!GkGq?la)l^~N61a5(egA#1493>xL%v`s2v+^*gD&FM@Ml2DiSojQR>V_cd_qTU)5FgLB^mw-(X;WK|2C`wqDE?Ub5> zRb6oI`{35M^>qnW4Zy>GwFPc{yQJn|bp)LID7f|Q8uc-7?i1kFck~#nPJ(lv2DiSG zQFE|be;j$gtj=No-38R=!FhiX+$vktm%zENfLrG8Ls(q}=e`bZm5XW)RyV-8Z-HCp z_tRFl!MX2(Teq0&(*x&z0B+qvujhw|hwuLnMX>*#=_Po$|DhPT_5Imu4jzhwbDspa z%aZyf3)#WB*tBdFEk#TqAwk!k+$I zKWIn5!~N|jxYeZNX~)31Pk>wW=d3|H3C?{Q+@e3X3)&fQ?sMSQ|LW@=wDaKH7r`z1 z_oP9)1kQa0+`4bMnuB%~oO}KF<~m!gnbbGN^eu2}CA}ZpZE)V-1-DjK)f}{Y;M@H_upqj_GqFAep5>dWBVSHZ1^CR1Mn=e_}MJw&gEHBE5t+u+ti`uKu19dPb@ z;MT+Q)f}wR=P`1=k5F#z|81%JM=aPA?teso-t4j3^!k4!I;M|z>iis0Or!L7%pt2ub20?vI6+@TfkIk?ZqlZm5q^p9kmtMR03nB&V3x*dPmQ}V+nBXQ{dLSRW%2XrNOz+f?MYI!5+(jb6)_r z-ZRHv1n0gCZf&Rc%VQOA?rY%I`vWxxkJZ7sZ-U!9OX^$T+;_n3T^Cc|1?RpGZrf9- z*T-d^_ppE58tUo&_;>`I`zW|Qu}aOs<1ujV6X13vN_`TX`!u*6nN58LockQOy~k$i z^WfYU!R;wcH3yHEz`3u0+k4iiuYz-52e$*dKR@08=e`AQ2Lm++kGH|O?}FQVN2u?C zbFcTA=iT1Bqx&Z)AHM%Tq4$OTK6-EPg#7sZPsG6OeP_`AICywGPb9(ZeT!-io=Aan zp8>b`rPtdNS#a+2;P(Ey3!W%|b6*0t51{M$L>Zj>D!6^XTr~$z)WErKfZIpv>lHlF z1n0gDZlA7?BY2_%&V3KuK7GBKgD3jnVQ<#Z^KPG6)P2Vq(@SvsOuaYgM8SD~9NeD0 zK+QoX0nU92+@7t^C+MWXxzB>z7w9qQv_cd_) zqMOtlbn4*TH^J>WSv3cp7C84EaQibiQr`vVz7K9+mQ{1m8GwiVlluI^_xt5Z-9H%_ z(?`MWEA-<9PsYG`e*)aTYKoeJCzIgZr@`&)BI+~X+~>gUtEW+)2j{*BZeLTPUVk*# zf%^*fpUqNVRUb$DI=F5AK9eUK;Jm*DZs(e64xVg-bKeEGbM$_HvIoxn0Nl>?)f_xU zJbeFuDuVsz>F0;1BzU<0sTjEZ`FUy%o{EEWp9HtBpFw>Jocj#8eLdYzp2~uAp9i;Z zn6BpFsRB6nC2;$O`P7%exvzrTH!PvP2F`s0+`eHg^-XZ@+u-(%GpX-@bKe8EZ>0O% zQ+@ETUq@{2|NqBR5UjJt^b*|u0zJR$qTswg4sQSRK+VCr1UUC8aQk1DQJ)6qJ_~Mt zNk3k&E(gwi0o*R+)f}uVf^%O6w+q{-uYhx31Gm4NrM?c%eG}aN%4F(W;M{k>?XS@N za9tOi`#!k+RegPfbp!CQUvGii3!`cd)+R|Cn7#^b z-!WUw!P7Nx-roSXmvpIbf^**nw|_L3`VKhvJ#hO+=6w6$VgC%Vx&PleRYCBKHKv!~ z_MH{#qu{(h4sPGMp85nh_bG7ut|;|saPG6l8UjVnObbX#Ff^%O6w|{JU zectAE!hH?p}seKiNqw7|LVfZI!pY7U<1s*j_`qxTuNmzwK8`1pQ< z1#bT=q2^#i1Ux*R4N-9W=UFue8)D$x>wV$z+#RPrIi^p8+jrCJe?tbG_vgUvyDMrA zHsry%FM`{5Z=t>f&b{6j9{(@(*9RM_WBNL{{Y!fNY-oV<{ua3Xt0`&@HnhRH?}FRE zic{YM=Y9Zg|LR8FcZrAZ|J?}o%VtwA!NdLC7`Xl4GpX0dWv(0d$)SET^(k=fGvM~G z=TV;p=ROZ^*E`e~z_~Ah+xM93QwHb03U1${uY1s~fpgygw|_TH%|W*b&V3u){{0N< z^?8h5PyP7D?cZ;wz7KoC{m&Ac`~M#n=>A!2OfSLhMvnR@IPZ^x+l{rF<;M|wN?LX-*c(wx0eGT0Hb5za2vvqLpo8b2H z2=y&+?mOW2-{|%5Y!{sSKDhn2#cB?o9e{`ZMho1g|NnomQ6JaH_1qX8>N9E%Hpakt ze*)b8JG~w@Cc(K+gWD_OY7RE)#~D4I+)!VmJ`c|Oi{SRX`s;&@C2;O5;P$=r{@Pdt z=e`bZ-&a<1u(1KoeGA;aU(dnDHaPcPaC_wnH3u7e;M@u&D{oJwEUDvmMn1oAk%gWm`T=;@Kc|n^TsM1LSNG3F#`IBe`>h$&$G~}i0^EMv^ht2; z)8O_y1vLlHWx%=5f!puu7kKbo9-R9kxNZJ^nCD91+*iQu?aS31JXZzhz7B4`zf{e^ za}99rTj2HwYpHL8bKeDbc9>6n51jh}xU(a@o}VWkzW+ZT!G7`-H3!d2@Nob0F>q(+ z9QAQ}Dy{P&VxuwYNP{{;!| z?73FW!3$Av?&IK&`R^IMkO1dC1?~jP)EvB!2IoEt?wG$X<%JwL_XTigZ+(4(7mDEA zm%$zL`wuTvz`3u1J5wjCIe4KC&V3WyncAbi1FfUf^(10yAzwPx}et>)3?B#!}R$Cy*4=S?}9sr>oMr{ zz_}lQJBKe+b3i{gja;7>@p*TSSU|mmJ>mWrW8lt_x(i;6gL9t*caEa-eK7^jeFof# z>%GB?S#a+2;7+`(=HSHwIQJ!R=jgfA>#rNVK2_|GF~?H_=lu@aNeH|eIPotc}}9K0L>=ROMV zoYJ6PpNF|_+$XRfZvu3L~csUQweG%L_Zwd7!aPBMM z&IKOzRdDX>;Le4T`UW`nEpW&DzS+xdaPGU{&P8!G2QT-)xgUT#a~A7fKMf!KZ~gB9 z!hTBx`%6|(ug_z&k7565eH_6SeO#k`68m|pXnzWvk0%4}TrrROEI9XhaOa8&^#yS5 zOW@9xbpPB^2IsyC?p#?^bFifb&V2*ixvELM{y2Jl+Sq65ezK(l&inQCHhY|_r_%oZ z$M;)_&HevseV)NqYfLY}oon=U47NtW!{gZ+2Y2Qt)f{Y1fODS$cdpA&p9beX3+`M; zub-_saPIZ-hSw*zP<6r9;+S4vkFdXN(r3%3b=DqP0hh8RdDY0`I+nB z{JZXgR~ln_eY{~`GS{;`rtg9~-!#Y51LxxzfIBzG)EvA@JbeFuHG=)30`(F+-2bZH z7asq&r&Avv(*v)zc-R{@_y6zF z&wG7qOfSKm?|W(v`cZJ+9|w27ua7V2>+?3(f%_Epi}Pv@`e|_Pv*6CHx(oU_aPAA> z&JRsr1n0gC?%Xy{%|X8c&V3EsxjjOC9i00nxO2O?J}q$WJK)X|bNpR!?)%`*j~Z$Y z`UCK=f6W4Sex$qLHGLi<&(~{F?C)Hx=HRs$IPXt@J9p8~ldmPgx!31s9@qIXz208S zjOlaW&X3z_4qnTH^Zp{Z^OGB?FM)Gk0e5~Hr@ji#eI49cnxeh|&V38q`Pp*n^?8`< zzvQ@{dmFaad7UF;Lfk~ zbqQYA*J)nM z8aVg*{KDh;^*z)#$MkJ*=hs`P?|}3E9=KDVNqrwY?B5_Z_y0P*AK$RX^b*|p4Sj!n zBMQ#@2<>RaI4cfg%L^r`QHbFV)izTW=0nEJuT_uDLR=TG{2 z1=}Lv;r?w=aOY3-`rH-+=RN`M{JEj#U|SNL`!u-om#mtDZ5eRxbKuTj==HoU56*oN z+-cJLeOn2f`wF=8x3-#tZB=mY>)_7cQ`GD8HqQ(9E$mlBsMn7(+INTg`PBEodH(?1 zS+QOBZxRpR|KHS)ZywjVFHXILJ>mX0W8ltx^m=$xf1TOGeG>Z?9sis9I*s-j>{r&* z9K4wY=lywbXXR$<3*g+Bz?}!o`If=CuYx<~_o?30$2EHVjiJ7y=HSgHIPY(RI}hfl z*IzffzlZ&*Db)AD!~Jg&oBRLjf$rb3#`F^0d8kT#6rA_#>t~MBd3YA}i7|Z&+-aw& z*B{Lu-k%-nTh!;kxi5e_k1VIY2+n;O+<9~X^%ZdLYv2z3eJH_Ob#U&R;12!09>H7s zJVwvAgZ<<5{Jzx%=ly+f=kbi1gSQ6YVgI%T?mVuKJ9s++&V3Zzq5ofW@OBKG`vkc2 zG<`pMI|ETtsIP)^Uk7)d zT}FKaock8I^K6g$HaPcPaA%{rKKkoMug_qpzemmV*Gzo$zlZ+){~&_>Cb~WY2_7EL zAO`Mi(&rfr^l^>uPhwBMcNh#(;JiNr?rdJ9=3tNo=RObaJV!r|4GQ4gm%yFp%=wnV zxvzpd&&^bGFsOlZudj!>4$gDisBezx+u+U%`uu}I2b}l!z?~Q9{Wa)=hy6Rm=KlY} zQU$>~)|g&`J1?xIJ_^qJf;OE zDURvO;LeMBZ}3h9ocGtj9s2X~;GH@+_f2r;CH?imJ1ubTJK)Yso7Eh=(*@_g5AM9Y zfcgP=*uQImJ1=*2|84}F`zW~cGQFPPje&EY0C%?N#|z#~f^(k+cebR|9K4$W=RODS zY)w+12j{*B?!2;u`Vu(z6>#SjdcD!#Q)OOP+}E*xwMY9K;M}*so&G%P+u+=H!JXGU z>hLVXsTdwkxV?fSTb_X=bB61el>ay19yORcL4&Lj7hx@k^)AR1`sLwy3|L&!E-no~s$K5He_ivAabFV*d z_P9GWsZWgQQ{e980`+Nd-k$|`cb-gr4xD?vFFc-|^>qoh7svEvaCet&Y7VwnzuLWo8Yd~qrL^seFxliW7K!Sx!30xp078L`oYKd?_1!m*Vg^}5%6&T`%!S$ zr{j5Fe>9KFePXCDsyTQ+3C?{Q+}(|S-g`d-&V3Ht-A$i=@O~bg`y#lz`+7A8@0Y;2 zuYkK#^y3BZSHZcjgS&gqRCDlt1DyL7xVzU9>f7Mlcfnn`koq1t_XBWupBcLUfOz=+ z|3L)%eWy_`!NdI@#K7Hs>F1LV;^5pT!QH7@H3uK0z`4(WyHl&wXTiD8gS%7v)EB_H zFM+%J>Ej4KD1&of1$XzOp9emufpgygclW3J!v{@p?%Uw*{yjAZA9TRE*Pl1Gt}$*o1Byd=RObao;X|0a#8`D`x3Z& zBE8-wmBG2Mg1aYqYL=5~;M_OB-IGn<1n0gD?w-6z&2mx)ockWQdrF=9K6u#gNNn!^ zr%K)LXpQOh=gsrto~nP|kUK`l^l@>I?l=Gs`<*Os_nc+A-zfsleH7e1Zx!|WxXkN>`vmsqd(NQ&0_VO0?xt^4v)ri)&V3!+oim^M1~~UEaQ9+!zHM;syWs9z zbNoGU?g!xRT)IB`W%tqN@ZbN-$&sPHre--=f`|Jj$H3i73e?BJxle+-moB9~12O}Vo^4|87J zr?CHr&1#lAr@^_;g1i4PpgsrAeF5D4f<8XEa}k{TGPp~>pD%Z=fOD^}pE)l#Pd}gR zTp!an!QC%TRkPf=17qHiGXt-1$V!y zZzOV;7&!L{aQCbF_~b50aPHIK?!vg5D%D$qAk>SzF8%jUrBwvyz6|buZ@rqORRQO|2JU`8Nqrrh`zE-%nC>T53!M87 zxVt!`W@&Z7x$lF!w@#&g03PPz6O^<8l8`{3^1^>vr-06gqH3*7yCQ_a$gfO8)OcmFp`*Yy#eRO~D^5EPT!QK1gYL;FJocjv6dq2G&y(&2O zb#V87ech$k0O!61?ygL!S<;`wo9o7X7yAdJ)c3%-*N<=ZxU2Makv`?a_kTYEd)$ZU zczg*S?x#Qh5BEQ`Om))N*VQ=pN!a5)Oz$sWKh9{M8S1-gmVOqT_v`aBd))R~>I-A~ z61clYpQrT8;Jm*I?yi}xX6e_!xo?2GYxHqQzX{HL8{A#9NX^pkfOFpicONmw-v1xcgLx`YJg0b#V8oKJ^W7?pxsQI*a-?IQLy}cOAWcCVJr9 z55V1Z^!!GMhwuLp^9lO*1MBJi9+9vo+#iX7yX)!w9*KiDRAyH;O;Ye zmXRzt_jz!a{yPmaQUK?^1nzFwu4Wl2gL7X6cf00zYT(>Az};s}-vsBr4emZm-@hUq zaPE8HF8%kNWuy-t_PY_A`~SwS0=b(trkCLErmULfZc+7dEO+mLb3Xugx319r9>l}<|2-nu zzfz-Kf`|L}h=IGW>aUl3#KE~wg1fJ7QM2451e}vql0M300 z+rF~J?zmK>bFtf1n2#2aQBVb)OWzS?}58-PNBXJ9`^dv zAAOF#zrPjL{S<3VFTvfnD%3~8d4C+-eOr%mN&=kw6uA4gK2JF%4bFWQ+#TfAEa~^M z&3SQO!2TWjd1*=!ocl7kOaH$tIYpoM=ze_O-FNHi>*W;vakOvZe)|87$SEywJ|2BN z%pP}poc4Fe^nGx5`zq=O;Nku~@p*UO&*^^8$e2C~?tY+Wxn~TV_b0&J57w($?wJJV zJ`L`EIFI@aIQRN^!|U_mcIxwE`XacugGYS{ocCA2y&dTFvu72Y`#QL{LtM>r&jvX6 zEpTrKeH?PnHaPcPaBq^nzH-kVIQIi^kAAOF>bL(#pBwt~zP%#Y?-*0F+)ILo`}fkH zH+#Gt*HW*~+c@`0*yHV}k6Z4Q0_Q#h?oFoWYp*Og_jzz{@^UrHy$aymm%zQ9>HfJ_ z8JznnxVJO?JhoR2ocjj2N55|&_iBQ3-v;+~ouOvAR|lMXef`XJ_IAC8`u@lF0Wm%A zo}JWvV2$Y|xM!!SkAjEC6U4ziJF8|HB*3{(fqV7>>hy+hAekOSww0PfMB zqsX8L&V3o&bF*reK?R)q8n{RQo?QlYaPFJnp0`%bGH8Kw-vRgN-;>Iq3(kEX+?$~H zOE3Trduf4t6Z(9ljDT|=1@|IJHA@)-=RN`M?G~jz3C?{Q+}k}xeFmKS9Joh6H%plZ z=e`K;>6gzAQkKBEuYh|~mZ@3FDmeFbaF6brQZ~T3Z-IMz(d%Ef!MX2(djUPavIoxn z0NmSKs#)$$JbeG(J2KSM{gb|KFvsZ~W%k#{ z^i6Q@sLg7Y`?SD$zrKFq{&eYKL8K+?`wg334I)L-v~JOQE=}BeFK&I z#=yBxfO{wCQ*;M~{2y)?Z(_icc4-vam2Wi`uv+u+=H!M!xSKKJc`b3Xw0E~4Yt{}}Vp=Z1d% zpBlk_4&5K7O7L+1)EKxoXRey%)HpczNpNpYoqBy-qsNoM{^AAHXTf=Y9^9K-pk99* z-Cx4~(rMI}!Fhib+`Du=^)+zr8{pn&W>Mb+=e`Z@&9kWQfOFpi_pUI{S06m=_aip< z|10Ty_tTFva((ubu*bVG({C)fpZ+?thxa$J|2*A4_iKT3-vRfo zH^^WfYU!Mz)+YL@$#z`3u0dtapY%l`W7Mz3dms9&aL zxqkzk_qV{ke_Bd?8=U(txc5)y`t-oLAAoxcwy9at&xs?)e?SELFQuuM;NkuQV&LAF z>Hcs)9Gv?kxcB9nn&km0aPBkU-dD{2EI9XhaPKQEHOm7E;M|wMy|3y~9#977z6$Ow z%&J))pwDCUdN#2Cn%*l9XoBj_TtYx&F}t_TQMVW*IGl z^Zqip_l-976>#or;NDGj>h;Ib<7s05pK0n_;Jm*B?v>)ycfq;ugL~hi_e*pD9`*-X z;NGIyYL*8^z`2itd-UJ?kO#)Vxle$5x0KZ^4@`n{p9c4S(4am8&V3HtyKM#ad2sHF z;NESUsV{+ZUjg@Squ1MkRdDX>;NI=}y2u0dd5pXs4r~qe`nctRZE)V-1@~?@=i3A4 zegN*>u4j1=@$k?82Su>IgI*5@N$_z0LHhj6d3kpfRVNRMkLi=(-W^M*Pl5CP47hhk zo%$>|_jzz{iAQ|_ocj{Ew?rR@Jg5xLeHGmMQBKX0{=JB~4%|1ezf&KFJg5oIeH+}n zYqFZk z`(2azDmeFbaPRl}eB`tSIQK1Z?+=UAET^@>x$lB|4RicGaPIZ>GtZ0n$C~QoA(Ri_ z{||}49`8?8>Lqx%|Bx8C_h3eF5BC9aXdZL=l|(GPt)osb=|!3OM&QaBuZ|>h*ORdHsB%In*ztz6H+vJK)~x z#ngAfx$lE}53i?w03P;-THszgqx(Z6;M_;Sy){#*kAZWa0QVlvQ=bIqJ`L_Ys*hV9 zs*h{rdLEj?e(h{E%R}|!jP^zBA6rU&37n6o0`Ad&PeC491?RpF?mdxGvplo`&V38q zTUVvN4bFWR+Nq} z9#1R@?rrE$p91GT1MY3q=ObfTaPITq-sZBJWvl?seF@xqZVUDL>qgJFiv9CF>TBS< zzX9&Os7D!Vf^**n_g>V;En^*U?)BrFx`ZPHAS#a+)x*r~v1Ls~}zwr8O+pao!SaD2W z2KU~XOML~L_t(I^ccawT!MSgOd+)BLz6H*G2i$uvNqrZb`#!k$p57}D8-R!X;TE`O ze!uka2srmqaBn+Z|HEV8+$X@j_b01a9-ai}J`L`@pQ1hk&V3Htd%r+^9-R9kxc7lR zZh3eKocjv6_o2Qn^6)A+_jPd3{Qm0U4RG#T;NFMydN{ld&V3i$-(jJe<>5VW?g!xh zBzpgTl6d(3|4Ds*=KbRDnA7_|DPd2z|C2FrfARwAXmbDszI&F@2gvH;F~3Ea2zighfuWzp(eNKPB_Jd`*pKgumCAd$2-?*F}1?T;7aDVR^YL?Rz;M}Lc z{i*bNpx*;B$H{#b`~CIvB&X-Vxi5hG2h3HooL&UyUhgxH>mNX`=joL(eGS}?E>*Lf zUI*v>O>qA})3?C6?|}OUWz{UFcfq;ugZl@qrG5Y&_D5LYKK(o`kBESC9|iXhy+_UR zh!{Bc32>i&j*&+s!MRU^`-hqR8F21%;QkT%1ul=sgL7X5_m439OW@pB!2Kf^saYOT z1?RpF?jNPEi#(zM&V38qpRrBNlK!4-^E%|di~aHX@#GObaP9}-{-@@vSsqC|eE&Z( zGSsi4UV?}FkJRU9j?+)vM16crp9J?$sH$1g@8y|2ygxJ4Z>ByA&V3%-Ke0@G0i631 zxPMZXdi`~y$5S2Z>G?Xc2G08%;Qq`dYL-Vf!MSgP`zKGKz5~vE58OY6?w?2Y!NdM2 zVsrmLRbLl*lr^T8;C?cqW_eT;ocG7U{p14b6X4vZ!2P5?e|c0Iock=ef12JakII2_ zug}jskNz1+)yboZWBM|uE8x7p2JW9p@0X+M;M_OC{j+APSsv8_=e`5(pWCCp z3(kEX+&^y`^#ky*r@x;*yguipbRUn5>7(HOc@64g;JiNp?$6Fsp9JSV4ep;`pgsf6 zeGc5eAWOY|9CKdW7l-;S)R(}yuYmg(HmR?Ib6*Gd(=F;7;N0uao8$D&?`Ou_WBM+* ze^FV@GTsB{{R41+PD1xb6A$12kB$uW`h4Wk5J|Cc9qe~dMzm*D=qI`vU--X90|uV_)9 z0Ovjh?q5l-hhx&<+-Jf4t0HQa$K=4dFM#`3(f8kDis0Or!ToGZ&GMKEIQKPh|LPUg z*TK1Og8SF#>mrY7fpgyh_vh>DB9G~UbKeK|uV1QWdCUMj?2om;{Tt%CKQ;o+eH7d` zzn^)mzD^_Wmt*z$8TW6j)BYsv;r(fF{|of<&#@VB?sMS&KhIaQJT?!`eG%OM5`BL^ zwgk?71>7&pQnNg^3eLSgzwms&rmwF&wlSt}f&1T>s%Cj?8=Uud!ToQ{rM?Hw{Q%tm z_i4JHK|Fl_pAi}Abtq>@@NoZ(7`Xo*dXzKb;M^y{{r@z_lLF^H1MZjfamX22aPITq zerdXz<%|M2_a$(@l%&24&V3c!FVXd%Q3L0`0q%dZr)D{$3C?{R+`n0maz+Q7d%Z7w zz1>W&&l&xX?~fxk_y3#e^?#f-rq|ay+;4uL@VMxhJ`V1GE30OCTmn2i{^L^M{6_hoSZ+f_Bo<0|0X*TDU6-$Q*JockuY|D721 zEpYBT;Qn{$``2+@aPIry{&yp4md6di!~S>+-2ZM-_s8q6GtWEsQS5J_<2hcR$7r9x z{(E$PJ3a}{`_th5_iJjF$7jH~&w=}k>Ggbk9-R9kxPR+RHOu2m;M`Zh{afqQSHZd0 zpEuXRzqLbsV@%%y_kW;Ad3+n3_jkelADHXe1LuAK?*Cx6n&qd6hwuNNiVXD&sh8m4 z{!hig{U0=`kAric1owaFQJ(_mJ_GLmFiCwDoclbu|HB2;7r?nMf%`vPN_`oe`zpBq z!?o1cz`1XL`xT4&COG$PaKA#Y&rfy0x$lAdl^fM8Kh*~h`vkGM|5ujiKB14xyl%Od zu*a`-sgHtl9|!kuqx*BhJh1+A2kuiteOApfkp|~pf8HFYf7@c}b7T4fxNm+xJW&Ma z{bg|9{JwXh0?vI6+&91Pov4Fz-vsw>pQmP-Xn}Lz0rzijQ{M&Wz7Ou-k)eJ79`+|# z;Qk$5-JcKv=ROMVFVWXso)81)J^}78q3^#ZB*D2)gZn>l+>5NxvzlxchdXogeo}qb#VXAZEBV$G{CuUf%|u*sBeRF-v#&Y(w#h^ z2hRNf+^=p{vpkV_`2K%l1p6QBQJ$!;lex~}{u5(EJzbv@CX*DkEc4+)BE?t8aVH7fcs17et2RN zoclJozm#4-Cw9QO?}7V2i>X-tnR#&Ti{SpRmZ@3J zEP-=h0r&rFKJ`^_?(5+GZ)Q^80O!61?*ETD{x&%GU2y-mSvAX}1UUC8aQ{KN zf1Z*C=ROPWuhz#WPtoTwdcFnhA6l+vc}fwS_m{!_hxK)or&PeXuYvpR8ETfN)WNxL zg8S{+)VIL7?|}R5EcIP*?)CAS>)@}MO#R^F`%^7&e@#R8r$)fT{ijC3{YPd|9|Pw; z0q#GVp*{)DeHz?aR2B#eEL@byYRXQ}f{57s35?E2uAlb6)}XpJ`KH1?RpF z?mx4Q`UW`nEpWd}ug_EU6u#)WE@N#f!AKmC4(d0c;Em3m2E zF!b|bG6wE%%26K&5059A1oxktPJIfT`wY1M+uQ$CCOG$PaKCTPw*$_758UrBP_s<-!NdMEVsrn0OQ;QlsqJo-G$9^S7%Z`|LunfBLV5BE)Q|4n^;&)p&V3Zzf3K-#d3p?-`vkbZo$j}%C&9T-gZta*{djr? zockQO*kP8M<>`5F?u+1Jhq=_3z`3u0i%A~!RdDY0^)s&rF=;CGjWK--TuholeH)zj zcfrLZeg5+F9ys>{a51T@W_bqj@csXc2=+Thsh8m4{xf3WV#meQ$HBQzf{UFJ)Th9? z&wz`a==;|hS#a+2;9{qyn&lY+58mNA9cGTY9fNqXy1>16lzCY6f7yE42{h1N)aQ~T6 zaItTm`WQI(32-rW5%o!M?$h96sy=RcW(J)59JttzUO#8*<1*KQ`y%$SlA7h2`r~L{ z!TvCMf1Ozc=lykXaadE$^2`P}_bqU7xVfHfaPGU{g8qM8^2{DM_xk#o^Ag9TRVUA) zeE9xJSG zp4A5r`?HD7{r?PlJ)doj=_R;0H>YNKb`+fV>+=g=Z|Bkb{cL?6#<@?y9&uis_NT$Q z&w`8dR#C5yYjl5MsHf|5b`hNSm%+t_bU!@10?vI6TwHXcn&sL0JVuYFiT#!I{pjo# zIPdR(3-kM~XLrH5?}Ll%d^O9n2jF3!vcSbP%XFWLfO8)O7oSa09|Pw;0WLmE*E5v_ z=ROTCJ~vm*GL-@6J_jzYrPo6$56*oNTwJ$J%`#O2=e`0iKEIawDmeFbaB+Qy`UW`n zEpTx|m-;q1_xkhZbtV2!oci9FegH21A+7s!h==e0=R~mof}Z6$5dWBVSHZ;> z^>NE{YT(>Az{MBuQL{X!3C?{RT>R5?>h*DrUeDf8zm)nuc)0&uVsrmrFje>GT4Q<% zE*31NJ_^qJ9gSCU-T&H`;yti`wK&TQO)vP{c*G} z5A}3^IJW}M`|)`fU(#PM&#jN?o8Y35QL{X^1-xCm>@qm-uY!xOFHy6cT?6O70WQ8l@0Zz4aPIZ-hR6Sn z8&xM~cgFNRaPbX2%h`SKaR2$l=Kg=vd2`V_eMkBXY*`Dt+8p9L5HN#}ch4xIY}xG0(PErN4j1{bBSn&tWWxXgKRU&H>J z^Qfj-xp5FrJz5_09wy5udbKeISH|MDzfQS7BdY?Hj@vUjPzaTQEkAjPD zRn;snh=KF|1h`n#pgsxCeHvVRJE~@RK?a=r9Ju(lemr@BK5uhg+!u%Xx|-z$C2;O5 z;Nm-Tsjq@_ulJe972j!7ug}{!_bu2XzN`1j3-s5G_Fe4D3)Czx=z;V80k|mNr27kr zhwuLvMuz$h^%6YXe_;$<+%k*$I5_u7aB)kW`V=_#8F2Bvd#KNXbDswn-=9K#0i631 zxcGjP`Z758RdBI*I`uVh?i=7@@p|f;;M}*t#jQE&JK)^+z{Rb4mKXNH!#+)H?*Bia z*K^t$(@Su1n?637j)L?4IJmeyuV$G}fODS$7q>5>-W=%A?;E7EL;W`DbKtzc050w* zQ(pw)^b<2`+v_-!IZFaPB+c;!g8?b-}ssgNwT? zHOuq>JnS#hpAXOXuC=SN%%KLIYPi>Xh7bDstmKlZ54fODS%7eBs{ z`aC%IMR4)s9`z+~?knKpCt2#N;M~{2#ZN8j8{pixz{O9qYL*wZ!MX2(i=Q^tEHBdM zVV+0s2iPwy=zb3I@cn;I1pA-S@z0Uq;r=->aPhOIn&q50IQL0#@$;yf<(w2a_Ze{U zbA4UpoGdu^d2sOy`hGV@pZCc1nNu3-H>+9BDTDL=D!8c8_q#bYaPAx6qPASka!wPR zdwqW9IK|ylsqc*Gd*I@3`u;Mf4<7EnnAqI^&F@!VY>nw9xcC))zq>dJ&imuw;#YLO z7bn2EPl1bNbiNm-!MV?Zi)D2+%Zqd1+!w%w`TgySi{RXs!Nq^AP_w+a0?vI6T>Q8G zdU>%vF7rHc-^Bjc^!mBD1P5u&lh*Wx$lFE`g}FZiwEFgKi2{mb$b2Gjev6> z1sA`e`|aEqIQI!~@te(RmUENf+^50C|16+B1I~R8T>Msda&8`+`y#mbZC%ZBZV8*tabIQJQF(V*AQC0TIp^Wfr- z^VBRaDS&fd0vCT=PJJ1i`zpBjQ;zx?IQI>3@uvaxO>pkp;Ns8veB>n^aPE8H;?H`P zm-NBI{!(Id|6iU}ATPDX^b%bBMR)SjC^+wrgNwgZ)GRMefODS$7k@ST)8O1^!Np&T zYL=Jgz_~Agi@)mYA}`fnXWpmWm$CnU`uOCf`aDMa+EA}&d1)P-_cy^sQ=g~2v<1$6 z2V69DCok=SbFa_O9H;nON_Fzm!N>QXw!p>TYP$b)1U%gT=_t7PdxH8HIQRPV;qm;h zemwc<2u&>g+31X={z_ePZ3vk^;Nr20n&o9laPHIK;xT>P^0Ev#_c?I!xbEa-d2sHF;No#} z{3USiE8yaZh??bPRdDY0K69PL6ZC$$tTCo2`-+}k0&orfpeb$7f;d8 z|CeXMxzB@(btN^+%M0M#>(7VhyRJfgc}!mg7wc-&>yKs+?{5tCdX|?r!MSgPi*+qE z%ga09-1or6I^D_3``}@(|8eM}&(ZhOb^7b&JZnrZ!Nt0Pn&rGGIPZ^xi}iZ1oR&KJx(%{@@!NvNRn&rG4IQIo`v0fjCoL2tbFIBUgR{`g~1}>hCP+te< zz6ma#j;dMCYk_m$0T)llsqcbw-v<{@C)F(H4Zy?x3JY93o!0#o5peFK;Nofe=jAJ6 z;M^y`#WQAq5}bSe`SA7m487m4$c*W8;KKa>ZLi3K^Zp{Z*f3em@`@5T_Z4ulVG8wC zaPI5iV#74*8{pixz{Q3c)VIO8?}CdBJ?iyGbKSTf4E1`JR}v53|F4W--?jAqDG+=;}^h83*S+2`;+&>*bXxaPBkUqPtkl^2#hY_jz#9rT6cZ1#s?5;G%nvn&p*c zaPF(%qPv3n8aVe2aM4{$eG{DfHn`|+roIEteGgo8w^82*5BsZ#&Hevbeg5()YfLY} z#j_PP%d4W`ygv>up4HbyUX=jnJ_Rl|>Tgi;sx&zFS#YtDeqOpN2hM!~Tx^=6W_eW+ zocl7k*wmq3pZCc7>neSI#>M6;^>x_8`l=P$3;$7NoJ;r^>**uR=ov%ESE&b|J;*&|-n$1Sf; zjp;Ms;#GY<^6D%&@6UsaepAiz>H;|TC2-L<*QX55eHC21R#CIOx(3dD16;hOJ9%{z zoO}KF;q`ei`Ua?mRI+|!~NF~oBRLkS>0b_jp-%0cs);j6rA_R!NnW; z>*X~GaPCv!;tl$Kca1&|a~-(P4)yf??wTAp_XTjVt*d5vO%a^?GPu~*r@jKteGOc^ zsYiKD9i00nxOg+7W_e8uocj*Acr!|U7o2;2yym)zw_?-}KED5~1uovA^Zl$oF0&`x z|Jf+^Z|mzKKdT>Sv`=6^n5Aa<*(5mcPlJoWTf>J4x#E;M^C%#k(o$OW@q= z^E2lq-qY7bezrQMuY-&CqH30(ZGiLs7PxpXPJJ7k`!2ZHzL5GJIQIi^@%}X3&nF(f z|IgRw7oP9?^v`$mCF}|J&yRtN5A z)Azt9cGA~N&hLYV`#(oa&-=tqIR)}_)|g&`PwZ5nJ_^qJy-9+;_kyb~XFE;N17YCw5({X8E}Rc-UWSflutZS@+jQz`2itPwYzX$7^HY z+$X>%tjTJY*CxTaPlHcbQ>fS1$vltT=Z5-e)aSvuFM>~4GpH|tb6)|Uuv*kt!MU%4 zPuTQ&yS4$&z20ZeYr@gzFRyKn>AT<)t~uWxIPV{TPq_Lx z!~NIAz$ZL9-|OPw+$X^&d^+FjQsCTYz$XN~ey+=cbDsyF5cK-Ft^m$`34B7(^}MbO z&V3bpLeT5yx*9n5`g(`Yub8Ded0lf%-v*x$bE)ru^Zp+AgqTl#A3W@H#OD4l7U(`_ zjp-%$g!%h?a#3*J9|xZhi`6W132^RH;1gmg^=WYKv)~ir9_r1B=|9)tzA)6UpuPys zeHna0tfjsJ&V3DhLTskK4$gfOeB%ET_AYR4UDdrmkkAMS>@)Vrvw@TfZtg(^YUJ@t zra;9dEz^*PObjTA6KNhAYw$c2&BGeUvE{@`lsJi%x5xt&62gR}L_i+O%TW?1Q6Bu6 z+e=&B{|%s)(9UgBIpAVJS_k7Pfd#|(4KKmTW&bxlWegnAi zo4|$s_lq(u;KpADE<~@2Wu^_>_s0Ujg*cV`F$cKuL%@aD#C{mK@uR?nIF0=naO1~; z3$cU!1aRYLfD19geipd#3&4e#VZR96_+{Wi9Adu$-1t@CLfpiD4Y=_ez=gPn{YBu$ zZvhwLA@-Mm8@~-)h-2h`iLpKZf61}@AFwZh`}w~V1}?_>nbKL%WgU$8#_-1rIL z!a0%s6ma8bfeWXX{XB5v7l8}sRQ5~2jb8ySoK5U605^UOxNuHmzYg5^i@=4mgZ(CO z<1Yah&ItR_>qce*m~}?qNR;-1sTr z!g+}O3~=M;feYst`vu^}F98?M57;jQH~s=};k?d%6}a*1z=iV*_8Y*B-vlmtPGr9Y z-1y7DMNcpLZQ#B?9sn+SP9=Zb0dD*daM81g{V;IjM}dnqbUZm818)2{aIq$@VmY1w zZu|^zv8Kp=7P#>Xz{Q#}`?Q~4rx~BNYcAF-uwQ{Z#;*bwYijJ%r_N*ihUHT%#}|Pc zzXe>ZX{uO`F9A1x8@O0Qo}6H8&;JvS<G)H+}`U2wqyE=ajxbdmI{&;>LUyoCZUHles@xHo><hV14_w3k0&wHkfQt{b*{=gP{vvSkUmjq;3EcQgz{RPO{bk_3 ze-UFn|DPHr|Dr$_pYr+R^VBEV4|VY)z{RPrvL6L*+A{!L{MQ)!ap1;J0T*t9{S0v9 z=Yb2CJb6(8xbaKC#Rqx*zo-n{_zS?r2S1`>c~KR(@$10F2Y0aF0B-yyaPdKEpS-9A z-1y7DMOe3|4czy00pQ~IV=9(82e|P=z{T&s#(o&M@uR@Shq(Q@7;xjqfr}4SR4j7| z;Kt7Y7ayv!p9OCG0&wwRKEJslaO0POiw|=>a~0smr{nwA%ZER%JejL?@f*O!hZopi z1a9)TfQygpV}A*_@!P<~A2{SsGq&gdY1;0${|}C_FCmYge>x0YtfzX(=?HM+$AF8E zo~U9uJpkPJ3E<+Rz3iue8$SzNe6*lqIh_Y?{33Ai(GvUgsq0|;ise%*rx$=5zXn`< zw5nn`T?cObMd0G2%j`FS8-EG7h@{x3{Z_PphIKvvN3!J41bBn>{(MFP7m)({A>e*{ zW+K2vq{My{xbX*oi%5n2IB?^qfQv|#{S0v9=Yfkzo&5rE<5PQe-NXj!znm#|@fU!L z4Mi2p8ETi#WAfM0@8|hprUBgeP2i&c3>C|n7I5P)0~h@}*>3~){n-F;u`y2mtOMNm zA>d*YulHudz>OaTE;dnr7f{?RC z;KnZl7h4ahSk6{}8@~!%Y^D9>Yz?^a8^FcZ1r^KLMc~G70T){-ubf>1Zu~ZIv6YS| z=NQ}b|D0p_O%=;I3Ea;=7X~i2Qaj{a1i0~Iz{OT-pPU;2Zu|sr5j{=CaxMkj_*vj0 z`Vjkh;KnZk7u)!LG*<#{{0eZftyjfzZUMOQYrw@e9zSz+;KpABF1FpIVma3YZhWe@ zf4zK6w|{wce-C3l|9_0yC-(%p_!7AISVP5fPYAf*o;?xZg8#e4a!(Yv@dtp5k5PZ* zo;Yyhr+|x(wN)(lWPlq#4_xqn=UDD305^UKxcEcrzuZ#>Zu|w{;t#_rmV2tejb8^Y z{xHgZ1Gw>oY7ynfCXfg8UJTzv8x70Y}DxbdsN z#V7AzzXsg+R1aM@@kzelD z$zK63{@Xq5F90__)!T2+r>Q@3zTU-O1TH>ZQL&tF0yp`WfQ!={_NgEGxW2!abv^%| z79xLdfHzp{|Gg5p_~TR94*@s%Bf!O<@cnUb6u9vRfQvt&{>Z&?;Kok@7oT}j#d2>3 zxbgGA#b;h+zX06$CE#KprDD0a4BYq&z{Q{LV8067_;ujoPe<5q05^UUxcF0^PxiKe z8-E$NI9<1=4czxH4geSY@A%7$9pJ_f0T*YStYUd_7`XAHz{MFnelCsyH+~$rIO8KK zmKP_08$SbFobhq?v%rmC04~m?e#(oBz>Qx9F3x;i#q#0`aN}2ji?j0V*MJ+p0bKCE zLoF{}1aABmaB&v3PhPwP-1u$a;%uJJ_c6BT|9w;sJ#UD!cPdftlaR;Hzb_12#Ix*2 zfEzysTx>s${Q=;{PXHI&c|E)@1>E>_e82tM`F!omckzqB#pgDuSnewUH~A~T#pe#O zzX06$HQ?fNZT9QHjlT$7oI~xC`@ceK|0l4u?z{L)} zUM?vEH~s=}v4gLdORB()Uk5IBjH_5)(g1FJs)s+GckE-o)x}>1E_N)k-v;jI-yZ-j zJ|8B3zXRO(A>iWkGwjoTx}C<4T0UPd`(wb39|tZz{{t1v{R!a4&j1&n=j(lc7P#>X zz{M9RuiRe*Zu~NE@r5^3EcaJ{8@~!%d@-eBxxWV7_zmFVi^tes1aABmaFIBb{UzYW zZvz*JhsZy`*q;9nIOv}nU|$0F^B)KU7w3{E4@7_)KL%W!OZ&?M1Hg@+04~mrs#qRK z0XKdYxHy;c$^&`e#xDXF=hE@yff8`zSAdIiGb)w`7JwVS23(xW*YANkaN{om7w5%P zEDtn+8-EG7IPV_zmx23!fw7+d&#NmS3jy9>t^W%WxH#`M_Cvr;{s?e!9$znoC~)Hs z02k-|TE(&u2X6cnaB)7hPZl!3jh_cD&JU|t77DQxAF3!)g-vDm>CUCJczJu@k7AH1w0-O zhJl;>R6jpI|2r)5V62ND2QDt8{>y_2;3j_txZr{EN>f;)}VEJ_w%Y#MW#xDaG zDV}c+R)8D73S6WpmIrIVjo$z+20yN1d2kW9@ms*fAnh*?E&(@w8@L$a@p&m@d;Y)F zvHWEf%S$D2KmVm+;9~d?`w`&Aj{z4Wz3dMFH+}-R7^$$I0&e^)aFPB9`+4BTF9H|o z8v7;S#;*Vuqo=XI0NnVLPme<}`UCdsUHnDhVi)yaUfKk1@-G1wyPj0BymT43?_b7P z&;Oa7OaTE_Oe_ehj$rVPm02kvYvtI;m{4#JcPI={_3UK3Bfs65iishjiaN{?Ci}4csi@=TF0xrfY zDwc#I_1^>MTd3gl5@ngWncuU3d@&Vw+ zPXHI=zhFNF-1u4GV&X*h^T3T?1TH4{esp;WxbZ8%#l)#9mX|L8H+~JcnApUA9k}rq zfs2WPisj`^;KpA9E+%-sx_lY9?_a@K&;OI$o+|=fdQx5E~a#U%fO9K{nPWLm})3b zUQzAh*MWH+~Dan7)JkCE&(y1Lwc){3fZpYtR2h$MPwb zMG4%`Ukn2mGa(hrVg$JHW5C7CKK2KI8$SVD%#_$q0XKdYxR{Nyp9gMyT?l&bA!d1g zDAIBCb!Yqv*Q^3W1LB;Z_3~=M; zfs4Ii_6xv`Uji;JK85`OaVF7}fruTB6r zeg?SU_w4fOEO6r&fQtha70auOz>Qx9E(+8Rc{S~~V*FoSMgJgQk5|`#oBR#n;?fgU zEU%`zuFT)Ee98V2aFf3cTwF?Zk%t-E^Z#MT@~M6DFdb(_dk%-u=l25ga0IyDp2IQV z;3L~#!mnjmr-5h;S_M=Q~z{3#UW~+JWR*Y-1tSvBMuc*EDx7}8@~cvTt3eJ z0&wHkfQu`rKk{%Lxbdlfx}D;RCFRM(%`QG|_x&pmvA?{!e+^?j{}=gwcTJ#+FM*4z z@+y|sgn;|)za|1)96pu(C~)Hs02haEVm}Vt_$lDx8opkx$pAM#wb!rDH8aYS*A%+= zCE((kBKu|FCjSC(aqT_qSAiS94qRME`^#$@z>VJoE{=R$#qydKaN{on7bS=NHgMm+ zHUL~)|0Ma>(tf&b#t)%?;~VUUfg3*xT-;2ayfy~h_;KLkX5F3yaN}oyi(6=ad2JTB z@e9DkEj<3OEdn=w8MwGLuVQ&^1-S96z(x6F_G`e6-vBPk74{c_8@~lye0dZ5OTdla z1}<(NA^$qY_WXaHgZ`a-eO)Jk`}wa60~dF4J+F%ZH+~Gb_{uRA%j*V!8$SVDR6^{h zfEzyxTzspgVtHL2xbcg?#e+N8F9A1x1-N*SV#(L>itFXNn&s1ZlGoLNoBWHw#e=_A zvAnJc-1wAFA6HaE>@TnGA7QNL|0>TXM*>}Z30zdGDwan=!2R|di2xVh<@xqV6u9vR zfQyH8{y1>sr+|w`c>Xz(0dD*}a8X-Uu{=@$Zu}B(@mNd6@<0v9jq_T+&ZzX)8s!sG4w z5^&>JfQwh2RI$9CjPetDwa1; zU03>f%h&BM05|O^0T=&+VtGRuxbYW&i@&IQx9E`AhZzXII&Rp8>sw7g8MyD?%vjI=|57La=0F!; z0vEpsu}`16&L)4v@(b)ofg67SxcDW7^5!^j(p&Hy)l9=P~rMaA;w0&wG( zfQw)0{AJ+AUjQzCwX9-!a}~Jp>%c`jreb+>1Gw>+>T1kM`TE z`?myui(fCQSl;3Q_w(Np0xtd|&VCrU@uR@Se^MxKi2*l0<@4MBpSnGXE`A2M_)S5@ z@|G-clb^Qh{LY(7>=(QEW#GDwem@fE&L7+&Lk}KJ{C-)A%jR zA7Ot9xbfS-ofD{^@>a(7{C}%s`MkcpRRZ_(-x>z)obYQE%UdJBjUNN4%@?wm;HN#0t5JSKkyxbr(ZIsXE1<5NCA z|4B{u>s|as;7;IV_Gv$z$K+qK{4o2=zb`2yo}^ zA7MWV-1q~)owvWvejK>*Q^1`bie)(i-1vFmPR|1>mgNF)av8Ys7l1o! z_Iy@UNQaN|dTJ8P*O^2;&c#;5k`x;edky?;5;#m@kDg0!%eU(NzI`3u0EzI`f| zU#5QR;~Kwg`OEBAfE&LG+>yGTHQ>f?0C!|v#q!IGz>VJm?!5a{_LqPgpX#mq<-B_b z`M0rd&;PeMkjHt?CiW$8KmToE;Ldxs9|3Os7;xvkuc=txHUQlC3E<8t+~3<$z>S{; z?woQ+#qzd1aN`$&JE#1B{St8FSAaXeOZAesEdV!u4Y>2Wx;=H^#$N>PyzeFz%iEg3 zjlTrkd0&J5W#GPlJ7Ycn|6YXr+XG#E3ETw-Z$ntqS-w_6G z@<)L?`tJkY5d&`gIB;ht)luG&0B-yYaOZ-Eiscl!GPdXcJ00|| zK1IdyP6^!4e`grDa~;pmcSe94KL*_4@9pHB1Hg@+0PgVj1oF-laN}ozJJ-LaVtHpC zxbcg?ox4tBzXaU)72wWYi|j7|H+~Jc^EHa)ops>Gr+ohSxtr=D?`(GQmw-D91r^IX zmx25FzrtA0|MyGszY^%;({{f-5A0(<)Wwegcm6EOK7Hyuraja@&7JS2*pEXVvqhxbaKCorgmzmR~6YH~s=}=V88oeWeQA_;ujUqfHgduQY%g zpSJ6MIgbU{Z*}pPfjf2G-!^bR|5quWpZ^)^kNm3B#SZ~@j&c64hJl;>QQ*$=5f#g? z#(*0?4%~Th2m1-&#?JtEUXtu*fg8U7+|l0;ezgeP_+{XZ{(kVQ72w9N0(ZV&Q?dLi zed;-qmD4f5{_ zbnzu{=k*cxL%{v^+!X=ty#6}-QQ*cO0Pg%W#(o^Q@l(K^zx@dN8Q{jx19$$8VtH2q zxbaKCoqwQq$h*qGjlTfgd84Ufc~=#<@$0~ypYi(St_E=9Q$GLvE>m84SF4M^4BYv7 zMaA;2HgG@x*8;$upHn;J*Bs!+4*_@nxuRnEwJ>nwM}a&4vXlK7aO1~;JO5H-KLOnM z8Q{*pQ2*uEvcQdB0Pg(a4He6;(Q$O0jbBFp-%{*XfE&LG-1)Z!_G`e6-vI9X`-$u? z0ylmOxbq($XMYK}@!Pm! zOUTpnJ1zD@z>OaP-g8o%{U~tb4*>5uslh(&x3WDc^aHfNye9+P%eCyF}y6QR`zm5Kyf{Nw6jP3dVUaFtY)3b)!C-0Sz$IpLn7u$TP> z;Kr{3?+Nn!b8j8E@fU&jtUFo7^4=zJ<1Yd4Sx4=b_bvnX{ckYV^Z&Zn6p-Htbnzwd zp1uM0L%>b`2=Jahp0BQD! z^V{?88vErg{sQox_fY@kH>$u*{yOlU_t5d=HyXf=-vr+C-ZxY%ztIA2{AJ)hr+h@k z@*8d7zP}Iv-g64iR|^hs!0-8kQ&cSP!~VYccSw`>#lYv_ zP4d10;2-#hFREDHr+Y%{H2&F<=cmB$`&@?oEb`CO{_?&8^6e$|=~IvMz8$2=`zq+~ z$g^KXo(M6oTU;M^(c)AWd0z|px#Y?FmXRm8U*BZR^HpD>tbqKcgZ_E6zx-y%;!XA= z7OyjpA-{n8`^~t;OYEnR?f7Pg%UgJZo`2kM|cWo>H;ApZ4?XKRUp?qCO3eFt1u%GOt^lLV5op^7N95<^3&- zlPB+Aws?*F2N>)6rz^}I-azw1U%JRVWbrKX2=ZNJ<}u{y1oODX$&(MHEFM;|e1MMQ z*DX_FUQnNg>vooqrycey$g=_FRphw{^SZ_Pygaao{GuiHTga#5%$F@5CH`lOb^T}a z%pLS+OUy&a_i}&#EQ0)E?Z+(MRI&WCxW)BxQ^*gr+0RhgVO~N1 zkZw=a;tTB8krzYE7m;5dVBSK0i)6lR@d)v6G1m3JCB@v~4LtsDjWZ7+KUPq&{1)Z& z`+F?IJf=Pk*LmpE@F@E!%MUTnB7ZK=yny@|*Wp_wVrEWcH;IM?}GRg2U9@>_M} z|5NvC5xD%YqhiT_A6oZIeyGiS8T}8nh(E}f>)$(Z7IVs{^SFZz=5#&{FEbCRPtDgI z(mV{j@A0~d<%3b+@~si(F^kjjykO0H{X9f z!h8|^-{|8~yRmqr}MajI?n+3@~w3h%PRFl`|_QPHj=K{B{Dk)bG{5ow9gH#q!%(+>D@^y~RH+FyPr3wh*8Va{K$c#L_`;&eRuoig&bQ7pexK_1AfSbnF9 z{A~f|P2_=P=1ai){-eR1w(EZN?bE!C{ysXM{4Qf2C-Q9}%Jba-`0}I*^APa9-$>>W z$QjYq4KIuFvClOBRo*Sbn!` z@dR_KlWwPcn{Iy{{Q%{a-)#f;&&xxM_4TOF%R>S5^?7+n0{83xkX2`0|A%7W_v!jS z6bIg$&#PEIME%qK@~@+Zir~w)QGet^)IaU}*Tq8>^iPVZSU$91aXOxSsE$04Qn7q! z(c*MG`LIxr*Ygpp4!UlSguwUf_6XLePmjY#qTu^= zd&H`b9*2*l!1u@DBU#|Rd#_Wmd?XLtABT@D0GDs){yt*YhuSS4siUvQ;Uf);>pU&w ze%+QWUQ)4q)T)oJ=c5tu{dzu%b@S`_=m7YBJs(X0_s8L*1>n8={#wQI(IRlaZjV-w z-xpJ{d~^Z0KYu=211{fAv3#^{aXOxSbP>6(GyksBj9aerqgFlNPWwxJSbg8o^Jgsz z`ThB`7DKM*&l=X#uXBx#Z`xB)`^#Du^XPF~D*^AlU7qbKEfV z7W%qwwIz#FyX9ky{raR7kdHa&>$*J_Mg9&7z_WSjGECaqj zZXYWE_s8vHSWkZ*c&q|`?-+m-%NCERSU%RaIQ3IL z?jYCmz~do{Q-9>+R(*6mA5Ver*Yk0#n_thz^WgjSe4LKs*I$oM{&%)~uJ1>WS0PXD ze)n9q({dzvp1}@*h{d$tI zU$?LV@<|7MUC$?_#S`pDkn87#C#^c@x;=(~SRcP`PnN;=>-MBoA3YAATm--O zz#}S_Pd0(C<9}aXKDi9sy@>M4rx@${oE%a>J{168zJv10ryPsd*$*Mt^?Zuzt*;lU z$LCX49dzBEDnTB19bMdC*#aCvf+ zc?h|l_l`y^9#gSAO8c#-!_heU`Z_(DvN+X69?c*>$x*R9YSl;A^JoQpzn(|2Zhk$F z*1-4cd2|uDU(cg0B0~SxH zSU#OXuIv1C*5W1h^T>6bpSJ3z>-=;Te80|5V?F&kKivS|uk+I_;C`K-UIH#pqIS!t zmx25D_c~*~u6qkf1!O${zF+@(6u3M&!+s38u75plaq?t6VR4FOo!aYrL?e`q!;`>iPB=*14X3ou6?aPjBH$I_@(P z__~J+%m;wWlgrHQdWM;&(AV?oGZ~8~*e@X0b$+H~af;H@5u%B zQ{emWRi35e`@XLKvw6#>yzzy{M&P`CJCLJh{OK|3UXcl=dAkZdOo)dzF*Jhux|dm!QZ2r@j%Cu$3mD#kLP1y;C?-iVcqK3QG@)#Xo z*T-l^gxvuB)ix#Kj$>*EM_5ARHRX07JU!eB-_1ATN0qg14 z`Gv5RzoKIKLJYV+KfDk}uGf1nq=5Hc>Z(}2kOA(G+ZW2fhN-yhE}TJ_Z9`Nb6Y z{(j}+8C*;qt= zQcT6N(X@Di`7&}n-d@7G`Rl8fV&MDZ=OyZ&Zl_RmmzFHf^?8{wk1MI`^Rl#jN5%5xIB>r{FH=5U z2fscqXTbOC^Kt>WKaag!0p5G)92Lu#7l8Zo)yqxbQWV&4Sv<;o8M&^{%WaEOf8;CF zZ{07yKCgr=pX>8V3bN>m{K>sAJ!>b|WdYrsU`Aid{F{W`pg{q^hcY6X3Ly}VinzUF67 zt609;0N!^?lR16r_PC$c?P-C(<{$58AM4ilV4L{&Sm*kz&GP#Bdsv@7ey=URCn3Lk zc9`>ofUmvbeavaQZfD=KMdmT|A34B$0C?Z`xxe2_0C&$OPkt{2e9b@Jt77@R4Di0c zsxdDB58m)^%&A>|J6|JDeyy-el`b6X;Pz4M_Ejxkb844A4*PUF z>z1#1!{QXXeONcQPxGebQ|$J&R_8L0oMTrOy6~dKDRyP43ol!oVpmqW@S4Rbc4d8a z{)rawzH6x+?kAR!UsF-B`$-9W%`=CYhk*B8OP>45u*Fj|5=;ExML4DhvMl-E7oYJV-nejfb3T7Y>0`J<9~(c;U@OTgET zUBJ8yypMkmRP3JK1m0Jp{NEM8Etd-{^aGt8HPuN}LCc^i1&qb1^>Wz6e=KK|XC``Liy zr`UIpKayZBfv+8-^{xBa5b(Z-TPk)x8wS4Swh`tL;C&A_m`8!H&Gj)Ku=ob%)Gj@4 z*XF3B?q@TWpJqRAaa!lQpDkMa%j}mePCra`Kf7RYdT?<+TeJAzah?Y9={GZPTKv7t zmn^=SdE4S=6F(zhabB;UAuWCx`(caU$vkRt8tCpB0~Y`9>?bV#56m;jXWq&@Z}Hz_ zUbOf&=4Ff1dkOc91&dFxU$gj?%o`TJi+R)HPcUDy_+Kz@Tl}AhpBX?tdlGYLaqjP# zVT*5NKWcG4zh@3ud>8u(i}QFsGh^{v+0R@2LFPq^zsS67@gFl^u=u|+uOXik%o`Tx z=c6;57U$=(GnXuWKIds$e46-K0gGSFTw0vZ`&nU&KgNF4;#`Nb29RI$H2VpQ^Ef#x zV{yJN&dMXt@jP%=5qXxcud~V)*LfC@U&PnfSv8AG6}x9OEdF8UO^b8=&swrL*Z-`x z#ku}x2P}SA#qQa3e1Cqv=!^4ec;AmC<=M_yzkk!~sOLfN>28l%ys2V$I~`x=>HATgc^v$;Kj!&udjfdh4=e1afcxv8?HS;G zKcxP++q1y^_0RS^@V*~9Dt6DS1J|*8UZV?dS)5|`yrtFo`61waKc#lN=MNx%o$Gmi z%HmBGyXRZ&e|>>@3H-JH=R3?R!25n0Vov+{=jEqS=2gp&Gp`|kosQ?8-$MSkVe)rU zK0p88Q9s?Cl+SRkgM6u6Gv!B`ib0N(enAr-rWbR69t9lL`C@C`2m zmnXzIPX+l~O3W9KpFr()2dl{6Tw%WkyzgH#%<+e9=R*PD^37otyF+w*U4QvzI-WZevivOjw4d=4%p;axWFAGX z&&yEE;!?%#(167$bcf=|-@M2^9pAJk#yq7yef}28>keg*>+?QD{qy}d*C@|$9{m$& ze|NYHymxXl`?R0V(>wkx=1t50FU;E(|2**#Y4J}mk0KxE^~Oj7`N22X&m*5alX=ORJO95kxAGtSH|AFUe1Ukz%AdcUxs^Z9@82?3{`@!DxAQ+ve9X@O3UfO@*K^Fy z|Bvk3`MI56vhwe}nfxzV`S^(o&~XC6|Y+V4HY->2kK7T>L6H&?MZ zHN?%eEIzMdH;4Ulr@8&pAm%RaAWnY`#``nZ^8?gwcP3!@w7)xJ)nB(~Mp}MB#qLa~3#a4w z$My3>y7+v(wusFr;%p!8XK27BBZ>!jyX<59$e5ngx?!qbGit#hc zx<8(|ZnFW*qw6{A0CzcdXJr>22HyK9-`{5=z~%1`sMwv2BBy2Yo7~wL@(&l;r~UkX zeRz?1-11}0Q^;?uGS67N!8~j67W2HtslM)P0r^d|zdK9q_1k$9#qMkg{Ts_Fc4z6+ z_dhbgyn_BmBFq;oo?>3LxMW@f-unWtTW0IP*ZolEZyv|YD<-D_dy zE%m9nyjka20^a)u-Fj4LegX5W#VK~@@)qCEe!=2Lm=`TxVP3NMcbJzg{yg)F#ec-S3Ox8Jeco%p*91Sv zejWJQN6%*70N#6O5A#LfYvTMoKG#J4PTl?%@ZPzTRP4?zA;0pi%$I=&Z@z|k8+h-Z zbHAur-Zol?ta0+#GuaOV@4fPU%%i~N zc@(>Q29Tdi{dD&vES^%ayC;Ku2gUB5BJ%S>?3a-zvdkBdZ*MWLA>Tp!yL%cIFR(d zKZo{r=V`m|pOYtlUZQ^<*JnPA{G2-bQHw7#A3%O?iFv}}`nVb7=TPX*({{g|@h0af zqR-#QyYpp>*V$h{o>*dDLw=5KXT#zt_UZV3d(NS}?mXr5`F3iDJKx4UpQGcsdjrVN zV}Gx-IC<{gF!FQjDt7lqk$)k@e8A!qyL%HB&$FMgc!W7^_xtsQ1?EM|uQM-Ooa?!l z_Ve>3D(u(LPf%WWZ^Pn?>^Cj$FkiBGn0Xud7pufC4p^Ly=Uyx=PM&))<@4MB#W?4Q zT0X_@#RJF_3HIsJGa<4R|ouWL^g@&tGERK%R;+r~OR7UT5Aye?-@Z@@ZeD;_SD< z59a=zc!9AVKe-n30B=~QK4mJyTmlbHpUgZ2T;?ct3t{A`f{NV&^-s4mIQ?GcG4-jr z+(rAlg#qNLGW!YO!D)U!P)GrndrRz7Km7cuD)T(}!Rd|63&15m|GR}E@>GL;YM0I* zoIZ_t1$}=0cMA*1Q!N#{g&OeS^yit^fy8d$oE~M~0xtRc8@I58 ze9&Q^^6C7+=~?0jS=Zxdr(_=B4Lp7Z!^~;B$#WI+5c<=6+=H~A_T^xV{V4ds>D!po zcHbZ6^Luard^wn4pFXu8oaXPT52nx`ZLps~KA2^m2OgY$j(GvNh{Y)^4xLoUB&LD3E;t*4a`%(;4)49aSsh3AEy1? zLkZx)*$Y+d9!de1QyJ#;srw~|xekZ&;0I^N*)IT>Qxv<0ipYm`{xb03EPuanr~+J$ zQ0yM60T0gdb$X}{T&8)P9BKj&&fc$L_fQMC+?itD1|FP!ocQI8^?96km+&kH?pudyyx|i302j||;ejT_>rAcRlkIa5>8Lzk=Fj z>UlT&1x6mzPdu7ezIFfW52+%wF)0$g68k6QyC+%wO99k@(Km^Xn3 z_Z(r~0xmDm?Qa7Q?)eJ&y7~HiU65feE&tD$hk(lq3e2OxgL`VsW5DGFW#$Rs!96cC zPXU*s-2P%7cu@cDx>x`%FQ}^6Ez)uHc?ss59ZHh9s({e)_KCnhc%A^ z59Yt5V)v>TaH;>k-cV@L- zJnipZEiM0V*bf1h7nhkwfd})yVjcr7FQhuUS0{i6=TBxo1zcWOVV(yboacY{;OYW! zd0~xxI*#A3kr?wb_`!LeSFf%Bm!sslS5rQnCpdpP=c%KA;Ue=U@ZkJT<}KhdO`&^r z8+dSjw~F1vjP-fBkk89uY55nk9|A6u6uO6_z=QMGs@Odo11^)4*FBs79-RL=`zhcu z8DXA5K0^Duhx5RL^ZdJ@!v)|nIiO#*M9H4KV)74zxUDY%qzgxo^Tp-tefGB;G6b0kspCA-(}=SPbHs*o%d&& zXZ-xv1}s0qJ{@PJAF_PDey^qdwD0%pT0CEVov)2Up5FOO`M4?ICQlx?$x}dnl#b`} zzbB{LWBfAsy)*wu#qPBWz>Qx;esl->b>w>dT-!i?G^Jwq+9q<}Zy`TA&i*oRzYf<0 zx^M@0@F>;Gy)K0ODT>|eA{PIsirwpC7XK9UIB3HsuMT`GA`%UD}_Azf+oZm|vS+e+k_LnXG2=lhZ zdEKbXsmJYeLGnuh^q=GPRmrjVLH4D^pJX0Fe(Xf%VT<#8Qi@poD)yrm|NofBEYA1$ z(g5=3Kf`|9;s?C=&EEe`V1E2ZhwJ|x&*9Gz>;FPKLdj$%Jvj1qYeS^7WqM{XH)Czt z(AhFNGLuYC&gAB;jiZs!OlW9oY9=|Ip0KxV(%VMI2Y1;Uqh8j*nVHGtOm1w~u5`}c zKJ0DJjOA9}ydly#?sP6aot{a~rH5yxto=7cJ3?k;aJ6P{NUPnG>HKgyJ2N&l3CG?r zx+&`I>u;ofCr78IK-=im&ugOU`_$;D*ARPir-y3u%EE1oq&vqLot;kOiP*ToEB8!j z*Hki_8=Dxk>b}uZDjh$wCBO{$lP7Yc{*}A2ZWGXv2zRE?nF*?%ORb8sd z-Raz9dK{Y&RTpKFPS~o(woRRrdwhnC{g`8$I`X41+pxvbRP8#KYd2+6=Yl#dG&wjtc>J#3rLOpNb}VOY@b>U_P#ZG1%NDgo&2wG_ zVeD^+MAgP@ZfZEG&ZRd=VB*dpy~Jwc#E3WMW(J4GVO(zT=9iAr>C5oUc(**;HXbic zdt)}~4IArV!;on@n^d!6$J+p%>ZexeLmn@}$=lFRb={(Fd-j@E(FdP}1{;er!EP1CsGd2>j(nmWZF{x*H)pyI- z)-i_1(}R<<*<@#`8c)uQ4_VSsXAo*>*Z9=XYSsqtbj*aZ6UphpxwJQt?1F2zKeAzv zYSOtTv(aQnoUtZGPfD8$H@mGPjgQTwS2agFjiO7O%vI9$Z%|U+n^R`zk`q%SFwXiT z{oduJC+ew*mD2$koe~bu4R@xc%^UiYb1-sHnM>!!M(0;5!<}xeU+relAKB=grJ2xr zH6aa-jO3DYgX3dxRe8=xr?~6KCMVJp$vru*CU}ZAQI3(>iHZ57H_;*$)VZN zQLp6Q^)YPkq;8Yzai`Jo*=e}m`y-o1JG1!uWKwO@>9GM@qRPQs*{N}uJ^LeD)%|0= z*UhBruWst(ic_=AyBT{W!-SpvI;u6awhb${GeUE#H*Zi~kn8^+9T?owxf0gv2CY)# zp-5*itzT`+hE+Phnx|%SZy4YLu|G1rTJ=_HBM~)>bQ_1KCbHvc@A6pXoQ({s#;RKa z4F^15Bg6jo<4*)i>5WQKQ+0Q1lu~&kv(vC-=Nx&{e>R<)8M98*hKO2Wc_V3PaCrAj z&Kr@*VXvFkX!cfV>U{ndNzIV!jpVKACUF+suyLCznD0RMHf&Ni>fNzF!H zbG@l$c(*r!?@o>mj^U8ruz5>o4p~2*-m)n?j5h&$izsgz=-hAcO4+bw zM9H1Im%g{0`Nzr6{b_P)(mL!`wN&=6hh5$cW;{KIt08ZC^@f?(I6BnqL^3z!4Fl}| zHgCo-g?+;;>wj(|AxV>YP1bcW#^M!A3i)yEzrD%*_wB5{1q%VVQ3T0 z51n1ilLtN7SOpqVH^lX{0QN>`*4jQCQPYI##NgyeQZ1KP=}rGO8lKtVvE<}jZg4`a zVxU?5+te*=J>N4{maczL-P_ks4315zh24rro&JrRycNDzU2ll$1!OmK^Hy~&{B}=0 zym+@7@22T3Tww9szp=A2^N!~&7u8_)wkNH7)y62za$4yuTy16BMpZ>t)PtHgZ?7iO z+`=M{Z#x^8Y0_Int(!w%Bk-8qzj1`F?8)?=&T`0l67Jua_8wAZLf+Y(AM#c>-qQ?DUK`VN;dk!% zbZ7HsfL(Z%ZXBgsc*mhbu==GbT*Im5zjv_Ccc~fMwT8@m5c^A`2dOSJm z4NU0trifQj$c3A|XLr>v)u!omZVubLX%pQ9j%#yvFgM*%GZFn#@qOSDm>D5j39KYT>V`|OYc}uanN97&zW~|Ozopf$h zE2@^+>pLCtmMh*P#<;hTwyHYl&C=dL^PNs7uxvwgbMF``0d5`j`o{aO*md&;nw0!q z>8W^ZFCM{LyQ*uqqj|Mnv3a98>k(h>tL?CrZyxGwcY3c@X5O1deVsucw+wBy+uxbg&>8ktz%zWe?_3bs&f5q} zQEwu=bLVx+dyLryZ~ywYjL=B+Hm^LZTSjPnde0g5#j#~Xr}Y*%ovRBEnjTSYPbT-S zvT>BA8*iuG$#gDFtn-jO-x+YbSG&ZwjC!Nr>v?DTREcuwi)Ou-GSCcf z#iVW~O30+gy~V-2R|d8`TS}^|O;51JiHv?nmCZ z=Qa+{=BCHIYizY7o6@Rg>dx2EalYERneOvyK2w{$2XVWv2&FyTfB;R zWt*Px7LQYVRupVYzpAQwm%^_g>~-kYE#4yCOF<9%VmOFMMCeH@QuON5qd1}tf|S-v0bxj zapjf3dSTbUweuwC)yTVDc6m#(weuLrRh;&APmX2ButjO_)s824m&9y3>Gj%snpp81 zv~|>*VD!20o)YK02dU1xt<^5Kt)0o#b7@)DS$wGn8#rh*;;m;rM-_8&Vq(@?0ejCE zGs8F`MK{pRxibXlDQ;+LX2x34MCsj>I?W`_OyRmWy1`o&@1T*Co=E3*r6-5Irxf*A zxYDJ`y>m$K9x#~4cK7pRj<>b*exy?`OQ5-~v%8w@R|p&VRfQ+08+9iIUcOOp5w4rt zc@>+Uo?b=bYuvv&Rvc(E%_hF!Ee8kTJF5QZ7Fw668*Rt)raW)4jvd-Ur$DX1J!Qqa zp6C`Tu9d_Sy*CQ8)-!!{3q6$6YTtU;j&7w#`q8nRw|e#3gALhAE0p7pqn4ajd$-bX zRwK(B)82id(=+d6&&*n7j(YEHyiuXr;*aQ+$MshuomTz7Jg;lsknS8!wb45v-rI3+ zu;Goyd+6o`QpfbKI_&EZ%@W7WN*#H%3#l_x@X+>ib*|u*=V`>dF?sDt?mM=&0O31>2}3ayq>^t*lgQET~@=!dn4ja zsw)TUwk`DTiiUD`i{7rNRI`)2yyqV83o)-VP`qth)c1T|Pdfv!yS9~9XP9i2uS)y3 zdFzADn6VPBuF`B}UnA;i-@8rNZy2_1^I0(t0cP zRgU4!k9x?fS&4Fwq;aa=7Tw}c`nmMPl(&*vIXwFJxqwLd@ z>Q(Y+a(aGZXbPWl2Zy}H_l$bs*7=&td!WDu4tZZic$>Z7WX#N@CzIae)`}rBI6`lX zy)Tv2yKhgz6~JJcd)a04sMi7S7;28`4BwIT+?e+!ea3!j^4=(@`C5szQka?AoyM~? zM8Ec+Z0aR+X3Co+=!XE%x1kNR7*^?Kyv4k?LY>4Xv7rt0J(bt(ds63);azdogYeJ@z3WtoQC{&#J(Tvouhq3zC(B!m+FeQ0 z6-@cOcY&P+tL==^T~s-_*-3VACK>L$deTEyt$I}#EXn((5n7}Mq8gIk?`+0K$E+g{ z_tSS=e(QdlzfpZdz25usnU@2}p|KgO4V_=acn#IU#NaeOVGnPjBtxC2U2k4pvDtfk z^QxNkE+85k$+6r;$?2)toHewEH`4?7Z}qm&iMN?o63pqE34{LY5AT4d-tw{p-$e6Q1Ic4YXwHth>~*%gcxl zdn4YD)Ks&Fv)+x%d*Dq^;REkT=a*ugetCj-ug-eE0rP&-fbSMY`spoA$JssB`S#FS zlzGc;HTn6&(K~H4s)oF`hS0T3(*4tV^{=HqzC4e8OyI$osCme^!=j3^cY8tj`Gk=TYJKoVOgHBuWxQk$AF*nnK&s--fl`Y4uc)Fd| zt#nRi#wNU9K6KuaPEM`({lUmqYLG8sb9q_lZ%s~ELnlfL_v3|C4-=(Dq4BJh)kx=i zQ*QtrFRY%3YSZziv+3c^iLe$6Bm7<3lF_N`FBw%?drbUG6TQQ`5Dx z`kuZoS}{7+m4jNT^iW@a_XfZSf@H|&p_a?m4sZfN5EJz}nPGZdO)+?Zi1wKw2FxFt zq4TsIK0%h&UFDZ*P=`>A@Ii5_exb&{7~%u*0Kwe^?HrE;eW@S*pf2Sa>X#C)Y7h~t zIYfhRl#1Rueazvi*T7L35H#ryc0^%OsLcL&={ML@HuCN{zuWc}ZNUD@{3rhDeya}o zFlY%vS+7dUS5Lhunq+}4cSYzf>n`TWibmFg^J-+a`cCI+6Cj6 zrN#x+I{)$@0n!kSFfk+0DT^xWjVamv^75DoWCXyp>i1FKtC55Vy;arcx;epTo5r{d zv`gM8@5&mtK8STSEY&hYQgBp0WuX_tR+S~?CyZTUKaFh~_>#O(8g_cYCOzA{5(h<% zFYy2ld%e&RY8P72a}zt5WEy*y0M?78?wJvgEZussR+AtBb~8zJ_Vr?;dcAhU6s3%i zX-EV`+D`_flffoj4oH6ptrt7>^GS!Vh>rPk`twh!EI8L5{W7l?d$qt3D|!UNHf;>4 z7QbHZG#)HqBb=L30CC)cS9Lvwd&sg0sL~^j)G~HoFDpB-@#`!By}pn@Ns@tvsRsa* zAR&jLij0VBco3f$M3+XftXJD8( zfpDqaysRoUuX0|-Hk14GdUXIuM~es@9y20f%Et`QlVIms%pa)hRgJ5zO=DQL;wcT} zbkGH4@sI{G?JVy%VuoUTOMWh{SB-`yVh(J0{31F<;S+Qhy7j76U+3%>evVrW9G=j* zWLtGA948E?7}hh*iSd!4>s3FhN6~`Z9AhsjFx*bj7s82|qOe{K>QnS!wY|f@mVbc5 zF0gu-!ACKG2(P=z;F^10dU-s;t%)i3xXs8K6G@!q8E!kC<%Y{6Y1iw$#m|kS4<<*o zB>9%B$dQdLSO`R4uX)=5<8qN?Llkb}0I7ihC$$6x_*M?;+ZF8}6K#iu==ku!Bn!T- zRS!(jDq{<99D?Xx8Gc_s4973e7cU9cjrO+uO#zMrCvKwe?BWnsr z8=33o(q1n^}5&4@)0asoF5=A=nJIcvp><+ zgGMEec&JD)PAdH(9%ig~I)uJ6U#vF^l}sZ|#XMZyRcpOjrcJ}tk(gcp(u?0n>xzx} z$H~W{tv9PEIDpg67HP=BP~2Q@*0`^R?vtq>(Wbv8t`V@9V|*a!dP7@Cpu+}j+PcTO zG|QW9vV<9&`@wl0)*DkJb>vt^ZKthU9zQe;k>nTVWgN^qSo7m8k$Fh%q%HA6;GP& zGMOsD&uAiB#+YEr?2adbT9rAkJa1RZ0&-8K4O7lVB!V%r9eu&=)GhDUtm9*+WoG9( zVMc=qLo1eFsx5sk=xAuSfT*JlnjROUKtDEqTW=dZM8}TmjYJO_iuC*gHTjMokOkQD z#2|}po5Sr$doieZza8BiAe)w)h#_o4q<$9d5C@&IxbXbz1yqN2T}CJ zOc(%g|HM2iNzd%U=%_}k)eSVN~7l2J32u?>Ya>Lv#nG~-)f;sU-tk|E@h%*Yr_6>TDZ z1MhkbBKSUVQ2T~9mlN7A^ipTPud%F-~0Dy=@6n(g-`ZeBfHADt5W>MH(HpS>A6qxY446f}HeP+K?vw8}I zU)^`!f+=DgrC3gIKQ4#F@OUcRXMqEg8z2I1!*Am>=gv6zt1N!l7$^+Sw?lWzmEm(= z#_@ZA<&I%E6jfy+brbj^#^AEXh!sx`5e#8hMC6W7z$U5;;>kp z6smKVL$$LBx>G;pv4@j@UV%S|p9r zR7@1+yjqN$OEJY93;LGo>nTb4htWtaRiG%GHNkQjSuGIutsKf>$fim6LS|XEh3r7;{V$cGlHO!$_jW z@{L}tMv*sCBNOLc$2Bd;&m@ot3F?~CC9KHAOFyX1#DV#5nwo(=_FvCbE3@;i+Kt0d zh$p_s#Y$J23;6W<*d#h+Wj5d%nx+a-cNamuf&T3y`uM&hDF2b04e9-Oc_kih!%0%Mw< z+}T)=ZESNJ_GTfK02NWjX6x?XuZa7C&cR@_QW(a%>LVBN7_x$_+oK*6UeZ=Q+z2=V zB+mrsWU>d1p_p=ZC3wrtH5`S)w+V)M5gK&t(OW(D3g|~4ptC-hf0U{>l+i5t6IL!& z5;c#Vq91CQi-!Xwim{YXO#vqvZ-A*2W2EH!%q$hK-1sZiGHzx+SqEE9@ee6hOFtwq zGog;Wt4r@~VuxP$BnO2!%IEe3f&H0D<(Gbdfip?8UmpgQYs3q;$Unn0><}Ytxk0$ zeW1KY3&DZ38@ctudggD)^%_=L@Uz_cZs9h1l^KxEk2xi4_$C>88`CLa$jdoqP9fCr?L9ME(q614BZd?T#tbVARl&5eZIC zZtu1E7OA;5kCRUdj@RHAGAhllV|2N}kRQU*lEKX0O7WdIwaueLDe-z3IZsQn% zKQmE=N$$GBCk$`VA_0DcovO!zQG>Ts67Zm2MWogk5ax%tPatY!Q3y6L)~8O`ZBT74 zK+vC3{Pnsq9ZhlLM39$Ulg(16Q*qQJ3+&b`)e51Y%=Xx<^d21glmlRyCGWNOmoujC zh#baO?}=<+{U+jQB^VHoAP!FHodo@XORR8e1HLI{Hd4&pt+D-59gR$R0taImNi&+AMH$MAd8i^#v<0hB7|~ z=pLsMRFLC${|L)GgOE$clcrUfK+px@ll`B^&)E<<^~IZaU(yeF5L}Utn`h~+Va|}& zNm63-anv@i{&v?cRlpcY>0&5BSn6iSf^!&=Y_I7fFFw0i#!etNHQhFHZ%h~lZzVWW zw{ZO6CY0(M5}Szrd_G?up8e1|i#x>F^Plv5)M;^<5GYQeYQ^b{X%jQ zgoq&xEiZ2V?qGqMBqv%%My}RvFXSHo$bN3rT)Cj&~$xOY7vVM~P#^N;On~rJT;P-4KozA*|+DB-9N#61r zXRGMezIVtd@fi~+(5QwI!DV%v@Uuq#%Fi#4$eU$aNm^ACFui68Y*e~g_6YmW4X+Qe z&0IwFZ2P02=M@!f*tE=FPbQ~F^*jCu{ZC?o+s<#7ex(wJDGd_a%6L^TlY4sHu)Nnt zQV2lb$FMN`VN#y%do`dEqT8|{h0Js4qAWp zuO5{^)WoZ{Iice3V*?|0!2@zjp9Yt?g>7&(4v)I!`(EpsM8D^P5>@6@ zphjb3a)zhGdQ2GC<%(MxOQ{_-y~=}ny)90lR7MPzfaefpSv6S55KfdARA;kFUxTY= z%;HNj3o$5*!P_jBDtTj+UR`)H$3xgG=y40vs(4?pV6!mQS44{e47R=>bYuoi9p+|RuGM0)=ax=UDyPqw7=TELC_XdH| zGHVJ#KJ(=n&l@nd>}iAS3^yv3kLNa;H6XR@>!n0X#p#r0Cw*O~Q zQP9pv3#Fe=1N{B4g8P$wy^HL4wY5i5(B3(9c;xg zE{|(eB|>5eOWzg%E%rTzT(;?K*365-&*$X=Y9g#JaL#_h&CTl?>rQ6`MYeB%dbL+; zOZl<4?sed7%Bo{w9hvX%8-uDOyBLk#gfU$KJ?~f^9{Eu&Vh~!522|;<+)jSpHak6o z^>-{9Q!7s>C6hl(u-Pz1{?1y`IImmiPR~N+F;u?;(U)&15g-*8lWuHRdd^EX=BE}x zdZIB{2_2fspo1}?!ZE{kqaBT4;Ifmi<-OUmmN$kIq4GGgO(^BqAx@yBvk^gI&kwAn zlE~%l4ks9AM*}qADjQJE8MVf|;L~z@*b7BCpWvT z1~s!D4g9%&UQjV}5=o&&xfcte<73xdr2_Mn?ml1cbhFz<@w>&3Uv7L{As!@5oPM0& zlr}rK#kjoDD5bc6i(3MoH29R(l)(oYClP&Mry1kT9l!1xyF0!dwf%zYc@wHqM)Wcs z+zqP7t0M?6hjd@ky|K4r$)ppL!&8IZX0MOh;4TmIH|4YI?cNHTC=~R33g$A%i=hCahRNApc6z&%1)dv8r=~Qckq@L=L?H)Cqy$o-AN`({ z@7RQxp{XZ6fD{0Yg@~wR2#uqeNFXN><-fW6z4>H=Xdqbtg@&q7U$gV9S~W7wipw@Y zRjFT6e`c*&nra}l`3Qtt(dwYp}MlJSauObH5W&~8Y%SYu=J~|OLQ#t>cDu3s8?dk#e$xdZ222~@@ixr zG6RQ?xZ{Ke4;D-zKa4SLR%7?!3{yD-3t|L$hlJV3v39cv7u*KZpXeBkipZexcj71}zZFO*M<#s5-2;Lb9vDO3ld56ymnA~F@FlDD3rZZI@R3@=36l4D{aNu zLUn6ZOypN{ZhmE!k-z0hO|n3Q-cQs-eDzBj;&6nL>;hq{*>}q}-Bu4!`9Qo5*HX(C zm7}Qi#k5Vw>MfyTvh4TX^4PJ|XaH!Bpb5Lh==j7!fI#0a?5M6%uZQ%)4&?c!8GDihWttrSthU-O}p+*4vUR!RY;AG@NuykMOOz9P<9Zmw4C?QXk zr6o z^|{AWwcJcXf~sJIOeG59X2cFC*pjKD$6HJ!h6(@=D0r$4mc(4vuXR_>{K_&Y=rk+* zq3#|#l-6F{puSg(p$N<|#@a@QA`($Ums5j-(~G`-rw$P!35KOQ@2Pi z&8yt>$oCpC2a(D-#mYY;s)0jM!-o^S_FGvKAAm6BEdJz#aY>D)sPCfGgA?@r3}y&0uB=g~H-Jq8&xl z3zL~J7>X^HYHe-?-oWmZd-VP>ym(b^U`T)=RAS3G_0H~_+*lR7CpVOqF3 zP(@C_78J&2QzQ2WDn zwhIf;yT?m&g;4__X>u#67X9_TdZLpB&aAn zjZkhNs0vG-!cT;wk@MBMI__dYG;kzF1KoW z(qs7`_(|6Y4S}^sz381>iMkl~`yTc&!BCXWTZv@3lr+!eu87r%tZzvR3aB ze`V93ur`d{VSNct3DN7^odH;Ko?=ndD?<>uU6JIet?}yMqXt3_I6z2(qx>*glMVZ=Y^U5JXu4Y*T`8kt|@J`AEx45R&uNNGBc;yhDs#2u>cEcy`F_|g2&}gN{ zQHsoFbYo?~15g(_b%d2>cViV?{FiwHR<*~&3O(*r&!$W>tYZ#5h$?Fk=mbs8cD-1T z{Rhy)$o%00Q)h1A)uk2i*-~v$CXchv9-a{YBI`sypp*&!&uhXKTb4^i`!K1%UWY`} zlY50^3ucxRKagvkhhV#9Jbcue*7-TosO^TiC3{6U-z%CvJ=}`W(iwGR@g@#)&KJGK z>yW~5a@=yvlAjFkeiodu-R||`U4r7$lT$FfoNNxIaKMC1KXC{% z;JpxBu-$gBGZ=1)eMDTbeBwLGw_FXHwYS@U17g>(K$68FPtE1JSm--V)VDSbO;fZ(okw8(W1=52pbId9D|)h2S}NVJew* zw*3q)Rx_IG-W~;+I6s82?CV!>xsS8m?kiOurV)f-<3i_{`g0x0Rh!ijh1PtFOj`F4 zA2YYzo1j30w&g*`(Wn-7r8*pMfR=W3h(VwJ^cRe6#e&D^&dXrNY2OeZ8n<6V3Mae^ zS@O%a!geQ+wmXEzh;;O&Dgc=*+<;KdW(YOaurK{rpbvPQfijkW)M`*0+svrvHPgD$B_!Zz>MuCY-}2!C zm7P9!RjwC$5b%nD1Rb0LpE8g7whor82=oPp0;Ho~oE2?bTZpiclfx(9T(xo9*eGr{ zSXv|oV+NArrREg}Lo}@3324np+!0&E-pWV~7_`A$Z$#{OOp$ZWqa9b{M2kT=1DRc; z7=Vr`Af$yUc&Fn8romWS)BuTF>z_WI&{k$115WQzzWZRN(nIJtl{Kh4G^=~Qq{Snn zKc1Fw?LZH3qxF8#ZL=~XwXU<;8d0>vuW@6_o%K{N#zk`G*fpTJ)ZO$oEEG1au11om zf8OI-^^q7U?U|w-u3cE=YU9*&ueE$tc51K0M=gD+6NGirOM`1IW0vM$tUIgc%Z(rB zWLhVE8$6ZP`|@4B-Drf`2uciytwjLa?_%C%s|Xp(IJcz_?QYyO6lqDOAj)AX2jPhy zG3}cD6Lt~5+U;J?Y0(sQtM&)Q?Ckjks*u?S=g?C4`pK0$M7Pvq9aEF^JaBv>qq<SW#Z1%-eRk%osY-Nr?Y0@;`Z@Xm%h3zW(DtB2vdBAXE=$-fvV9j5)G^9yWYIg}0( zX9j^x3+x6p>qHp4g7Nz0+wE6smKL}C8J-C0mHENe<7Fatuc&q6nKSt@BRUvLPZwMRq};lr3a$W~BKPu5HgJOwgP86fQvY=ru?og$och zG)OIP>W)4QdW-M~nH>2&_}k$}%H&aRPdfYIHfqly`Q|Yes@BJ0!P# zqbwW~O?{;RDFX=J;oqp$_!ppn%WY(mXAB!BL8}tPXS;J^(E|U;<$z7 zDW6b&!hyI=KiHuzb1B0Nlp*@@UX2G^5bOj-#(+4rP(Q>YZBHZ6p0Ur6W8Uk5A_3E2 zO(rGNHn8Swm{wHiVN#fm*0*Fj?9?X?Ko8SQ;T`tctOtLlk2X<_hj>rn!l^lt;yUzB z`R#DfyVyiiZg@p#9pVNd<=kZF?Hm726X+swwu6~+gbMHYpI^Km4?&-zG}!zTVrsj_ zuKP)3sXEN{GzRfD_$np41q-T#6-k3#cZ($)qmD)nSw$AnvFDG2e=m5quzGPec8za- zX)mwMoLJv2kT>QVpSU3NmF$8$cp5IzV1ojbg*4r$^pfwH0Kp}Ce|25PHLi8a$8N!g znZgYA|AFCWrXIs?!NOOrU9OR<|>n5yQRgT3G6c@C_6Sg!roQ^JEwx{}@eCkG*3-waCrH~)hxz}ZAs78NcZ`Wf94H1XLt(%y{`4D(9UkF~J}w z!8J~c*g;vGaO9JakCfgDfow$mmxhT{wLiN);@v&VZ{oQa0n?lS09C&fipBw{#s-ik z$qQ(_Envv{fuC-KcaDP|u}Huwg8OzF5BVy8%YL^W)Qv8U7hNz=k#XRLi6)txtMCv` z+^ux^H;{y+APmly+?ep*lkAx0GQ0+qmth7~d*3Re`h)^sxdh*~YHSwD62F@iYS_W^CPNiO z>4BdebaUA$I)?g;SgQ+i;Qx(m<$T(yPI6m~L&+cc*=+~CnK_KFAOei8fM7Vm-o?7( zNjA2lzb9`T!PRFuX`H2p8dp<@0WtH7*7b!CPPk>jsUS3EJ-^{nYd(Swb?f}QP@rv= zOVXw(^!16Gh=e^!zMCGVgO9wZ5JfjwTEQDYb|EQR#*8@zVOC4yOUuS% zoT#plupok2rfqfq%=+Uj{+93ay%lp68<%j_D=k32VT5Nk zuR~0dgD{B++GK<xnS;XF zK>>}9$4HJMZXuDQ)vlR{Mo1jfZoTlv{4^uz;@lH}lrmwjp@Vz~l(k-<;}`rq0PDS# ziUbt{Vdmn~q`RtCpOu3a-zEjr*wCm_kj)XbwTX~3G#fR0$?90tQ)-yRJ5JU|G9Wve zKXM0*Igf;yry~walPCa4w=OOXj+_h`={Ta#ow(&QFXHaNw2^Qk3MQ9p$XcE-mpl+* zhy3FSU>aR!Ro)$HHIvAM#i)b<54Bq7GTd_K7!VJ62EN)H(XDY;dQ=k)a0X&l!>qx| zQ(_gFFjdz7)KtAYSP9aYmU5e?Wfw|?-F}~Vhu6BMhE-Y1wc6Tj_G!b(q@kk9v{Z7Q z;{#Dae35U&-DnDv(4&wNGZo~WzqU22NUa$d-0csrCL|0ck~#~iMyVCGD@lWt)#DqF zZswZ3-84urf4_EzqQetcz%kXsKXgVUV5wC2u3_wXSW>oW*&M@jNj5cYbohK$^wgTD5K zZy_)PYvX^m;_E^lMyZrMp8bmk3}>~>qa!gK%xA}CL%A!7n1h+r!U)7L;6JEU88k~O zPPY63&;26S1nmGU!FKACmMExljnqBzV&rZdVX(4=)YJXK-Y$!fnXm&$FNiEa_t-pt zW!kCJ6o9apsDMZUst~8D=cas~hU)AW=3kaylVF`aP87MxkFEDlVxf#@7!mX8{TxBq z+_?TB?vDlt7Vl2rS_UVF<96hHIiQUD$sK%cVQAbeL{VSzL_07ZkUEMaY-qn2^x%0q zzpg}mlkJ&pIN&r8PH;JY;&<>TS_!}R$i^lWFY*A>eLFUnyzB~q-n#^Va$xmW<`Z8! z6azrC>!_}{5iS=RB_Q9ZYMzo^T6ri)E0k9K}epjrmy6V?M{50G9)+SrCPXVe9RxTI-)j!IVMlX4g8T^xkVUNTATmyJ45g#u-vzq&zaMch4q>p-P3wfG1f z_Tz=;C-b-bD%(3-0%LMlb2)W*+5i=bC5k9JCHBjBd*z1|QUasZ%TMt&Ig7M^z4>qvTH4*oPRrezrur*BOiqA zlZt_rz2PlHRZ={*Nk`2TArJ~xEBDECN4GjFo_K-rvJQHv)nXT#YNv*AXv5-<;4_N~c0j_f!d&VG!iUNdxaKJ5Jh6s%d0^Xr( z?_UpvOBzvN0cBNY=|;+dd>}mlIe!l*0pJ_7s%O$OPMhO454Dhsw6Geo5>)+s@&!ck z@4A)gB7DUA?RaUJPfaI8O?{-gC5m_HNZXnfU3dkl5qL}hckja4w?$+C8Rl7H@$kyn zAF#Bdp0IDz+?lesk58z5itxRp#3!DL)xbjob`MhfvU-u8Y96WPJe(0?!pBJ z6+6Ek|77K-0?6Vu*&cFC7;%!?*WlgOfIp4;gaGW2(R6yb)V{I=a2lx;c8(=wKvbysD5zaEh`i&Y60RFAZ1wHWCEmKj4pSYJ?U>Du)yvp08k zg!GhHUuR@GvTwOZbqdSa3Fd!K$+y#LSfxlFZh-PW+9N1z6k!gAErH&7nzVv~5rifa zRph?az>R!-MLmji_f!iFIQ4K&Otw_j3TjeDS(YQ~MYYm+-<<6E(!8vfuFI%ODU&2% zx>n0hntf&RWW=T30#qus0C}lm&3Mi9no_M{l?;OrXG(tKt5Dq7V=^te+L-NPYzUpo z8$*gB`RL{T`HK{4N{uf2s?rY2kkmw5uvzb`gO+;{m`VVjo&*G{IfQYEpv(X;oCZK) z%=QSPw2%+iss^cRi5RzluzOAurrk@O?)w9Sq+RfT9H>Obk_ABrh}%mI*I? z)g%!xAPiQxl%&4QJ#>2!eF7f$gPx-HRelXcY02qi@*l}-ouw}#9v;825_GDF<#G7v zoJtVixfzZ4fXBJWO&-k1qVgs9uu<>4E=a1Q9yZYpcN zDjdu{NO4KeY1ZIgS$!mt#X{{5$fQ)cJKdlt?@*-i?8^3_ik}uQ@pHkZle^NPDy?K> z%JYm2OLRWP5jwf1BJ}q*KDk96EbP&DJEHGzl(=&i$lti}9cr~zdHRJL7_wXnyi~Z# z9r%V`U^=F4h$AnPV+S*+a$l(may~tkR3b#x3q7;v)?Iz3ohm=a4m|YGaO62J(qmdA zU`Fq`<0H7~nRp1XWTW@INNUh%E{=h8f|gWLvJZnQ*#TS7!%-M?)P7Z~=k)|tY)ft9 zWnlp;5Et-dbJgbD@e3>bJ|mE73mf%cu)lJZ%OzMH-)tv=kk2Ok2@^|$~)*;0qUt zw%8tUMr1&W@63+BM~%`MZ0XL@BD0N@@ubK-qK$a-2J`uGbqcuwfba&55s>Mmtt}^# z-k85c_Qs#SmEK~l6vCw4qBh^!`)fu@%^t-hjeTp@V}pv;{_>)M#;)zoV*d?@!q46m z(@~|qVu0E={4CBnPh+EwXH2A`%6;n031J`$*MnIJMg=Iso@7wxlwgeYxLUm}Mw6;W zl4iH>YKy~CQV=V6ypH)ADE0x-;TgGOBsbSn!P&d6`>s)Ug8?)aJ8QCa3(DT3zya>q zd|uJ_zf;FdG$(S@B)#fguOuWWYtu@d8YO-6F$JOIq_XR+bwaI(cn-c(c7CY8u`qYcx2Wi%^LBo{z*q9HIzFs5D97DZtA*vC zNrD9qaG5#mh{@sTsSbKw8R4R76ztk~Uqo4BMBXrh101Eguoopmcm=OaxD;p@H|9L* zXp$}m_-MYRUg5!RR7;lYzFMXfoBs}ctubEX(5UKUY7q)#sA<^2^w`ccB7mbTGIk+X zhN@+p6(_S)(t`kt60!meGnJZct0f**l3}-xaEG$^%Xt6r(_=MnpFJ*9kCD-?#ySCJ z+h%DIo%5-)6>b@#y3eQ10-~dvPo1rn7B43$43q@PTay5&-Ae#WI)zyFBR02)0B$7L z!p^QNDL~8TDC$2zYFKBCEZY$QRRto3rlv$hc4~aRfWDJB3ve`Gvfz`vM(V^cK+1wb zv6MLrgxX{#M4mZYEzMmkAkMx|0mbj?@s{6bGiR%%y#*jJF|%2?*7zRZ<=PFfAD?Ixt zcbzet&Sy7QYrbKHrJjCw$-C7W)k^uhG{jbt+RT~OT{&?e-*4pGf=kp%JW=JWDxYNbm}+hBTzNESIE@jg)+Vzj zD6eYSsv?U{X3%vx%eSr8y_)D`9#_<117RRQn%u7l7j{ zEOQCBaM+~$Dicu3cC7Fel<=7hPt_}^1Bifncs^lflO#|??rRLQO&WX5eNd29n@)W~ z5&ucnlpR|>5tR-k;nr!*Zjm0;*FJ&MywGa9Oob5XmE;QM%5Ovf=i+0ktpznrEoTNn zU^JXs1}-v1Bml@diHoX3!hRDe#THj-$65?`y4y8k@T`iuBZ=H_xhb&am71)Mh1At* zXP2B9M@J)8(jR1~i7vMx1#)}NC9fi51Iz7tIFr3vF$=AT%w#0;!6KT*IH@ZQ^bqkd zwj8cH9C+cJ+wsMt99+&|Me!BYFm?PR>6tSW*%-;d2+T(Xr{2_sa$anOF-0?9OpHb5 ziK=t!~n#@?~uutSZB zF$pzw_iNLc{$9}qd&RZ5ew7N7Xbv1Y>khA~gL%thJXv-olnftOY)ke5_cZ;d`O#9< zdTP5)eaEd?RkfbP?xUN5@%Y3%_i^9Kj!zH!5x`B;x47`;%w!jOSIfIsy2oVEPz>ZEzJZR``vI(*TuS{-g|_`*b* zuIUL!PS`~H972{-%mMRGRUAZxfyQ_*e-5-z;Jjpagx6EadKfGpFm7M;wNSUrZ=#Tb z7sD+w5%y^>dE{Nq^pwSIA=MM=jfR3~$P>EinrxV+WkVQQbKK^>#dcv{w77dTz}D9n z#Iq*h$f#VWN|jrzvIP__R#8tf=$sW4j~?b96fB`FBXiT?6}`eZ1Ysm8K}#0V7OGM> zj~O_~O^P7O{w0|)tMbip5hfg+#V%H@?H5~WWJ~)MQOtxOBu{!xs|GACI2gSV??!BZUx9O1sd(&E|mhXsU##TLL2H7Stu_8 zP__1+U+4mgxguwruVEiu4GZM*m^NyC6<|_h3Ls;$&~je=pz7?lJWf(x$OV|O&ca&U zHpC>$+rb(;P9Ebo-+j=a47bQwV^yTsBbK?CF-&)sn*vSmo3Ok4$IBlCRPZAfd~*9WExEpNL}e z6a(~D^XKQdw2xLsGi^D`#IL$3`9E&k_penmmryI571(hT7tEw}<0 zrJ3%_zTT=|MeV3ighH9tZ~^RAu^9Xub%Y8mwbGmjy}dy!kkQ1oo|)LQeFc~Y_FKMoQ7c=#2C65Y^ zh)>KPt2jx+gE=nZ;mV37;~gwSTyTXu*sza;S=^pisfI%|ZP9cjs9MiMX-xN7A=Xe^^ztCmmxAc|rwr)7aUc9M(bBfZcZ77UpSYe_OQuzg6D zL5nIx<9;6&2aE7vkHS>(p@DuVoK1j73{D${XnEK%FNOC>4k!$(3J!@|9~lTGug$wa z5(M6b%qBo()1ZvcoE;WuyIILTV+D{*5jiYjxgRGeHn(^~EFJ=iwjvc#CrLaERr}*w`yCDo79;Y>%3Ha$B1*VFSICq6W-lXh_hJc57oyEvc|7Xt;~C%azD$ z^#Ehb&t@s)N`eVb#zdN%7zUhAAHUlzkyN5BCl@f+aj1Zk^oo$VRpbCUH|=9e)nfgW z!*&(%L~fQlY*EZ-6bCn|ogs0<2;r${1ks9s)2K>3SnFAA+h}qw-h3eRJJE}WE$~S_*fG7 z)OXs7Zj%bVp2cLK1D<~qW*{n8Gw`!U&q$BCQAGnyJvoP+y$nwnHW!nIk*=8~kOQY1 zPzp!ftQ&FIHDf)BNw#`r3K&%)*z8O(2w>C+=`ab$G+GfXKvXvS$!ztC8$-OthE1hg zK87Uibei-@gqRJU&ys_B{0wh83bNy5@{L->&Ox3p;baf z!^k*wzgN9+MCAM>&*@zymw=`Ik69i!UeZt%iA3hq#l!xfLWUrUzp|10fD#5G`vfKx z)Cw9ZPu1!muj*ZYGB~@Z?#pKxrcjt_i_ppyxhN}&W zW9gVf`h+MkBuCiD$7$i0!@iq#L_j8Cwn&tlCSbZSJFeOfrU%O@j%MAjNQ)fh!R+Y= zbN<-pf+me>a&n{J^u;>6*AX{1CYDYm3G_{<#%occV|0u1=Uc7-p%aDWBS z4nc(QvPv$!>80g9SYg52P9@OOTMzQXL}8p&*bgSxM*F3okVe&c*>21vI{NAgNGuGg zkXrY^e{CP$5iT(!194pWcTy=m+?f$*kiCfx8!+_?5(nu@tUs8bj$Wf9TL9t>De zKDj8Iw^Ak%a!7+l8(`Vo*H*YmMw%VzwY{aoUvE}2IMgT=9rleJHKjrvhnm&_+g%Q& zqww=<1!(ya)0dPZ>VtBGJm{4FsJ?UlXrV9=;Q1cLdmbWHh`T=-qEufT*{r&NM|l@& zI6XWhDL2@SA*7AeIzD`SdcvY4vFgC-Ij58)?kFdv>M{v|^Kf^{5Up-nBF@O@%M-p1 zNMIC}n|eIcA4^+R9FLdwv1=dTo%V}ingLrhU7JcGl0g^)=&{X0ufD``E2LRps0o}7 zP~JK#3B$(98?cBthI&4_UNtDu!;1x5l1*~-??4=yo!*xTcsRkwm1PG3qOmB_0w3FC zK(ueg>MN5-*k;+1G<+@!oJ~-1Rhvqaer!SV47LOEIP@L?64am5Rd4v{qBU_s-8+{l zg0Iz=DZQ)_LzA&+6R|t5`4UB(t67cNp|ztS>a2jXn)_^UE^+VBF0laC=Xp}?5*mV` zRGbNOxm9Rauqha(v;kWd+_K-Ze7-}w(%ENci^+V*pI6aD1j;5pzcO?f4**MiJgTGl z&M=9?8Y`vg9op^04iHaP1M%UMJopEkW&*L**Qc9E6!i^FvRE6mP2Z)x`eR6G=<#%o zFuW+Y?ft9IhDMTnn`|8Ugw=}V3+xq7+yb4HC+IV9KX;bbrE^!(lRHa_AVMy+1sc%Z zZAv*lR{HlN>^ik7`Vod5={nReG*6FEHMvl5ClCmsz)yVtJloR!Qr+bwk{#(i>}%n3 z^?douFgz&Z@6TsHUHwLjsE}~o{reT7jl${WWNRN~4!I6jGm)wd^#i zByR2g(c7a2tNu@nI>bK1*?me3*h6$4h0zJxvF5O!POX5ViNJ&G-N<;Nv|&iCfr-dx zs1E*&?m+u^2oad;;X<+9i)C;~i-YfaFm4cxBFNsIQ^g zI3d^bGb9ef@*mF+C;}AE9hg>44}q2?QH$dfr;>js1@BX1`(rUgUb5*P77u+E)+t=| z!a`O_b_8dOi~Hx)%@LOJRlOJ{ok~zTBF}yG(gGC6CW*?MTv}8@81U@ONR3%rV-7WA zf+e1Q4V?K|>n$hxQN7$jTw%W0m?C6;85QMLVQF=gCtP7oCBxAXBc1ZH!q+q2Bcg^2 z(_x;TspT&)!j(7$IVZ_j@Yw7OQltMTnyG8NUNW%8CfE8`EJIyOIaLSD$FM?5a+u|p zZxAw<`cccp`71-cvL`9nu@NX2jM9Kt7EwU7upKV7O1n7f)mja)BOHWp!8g-+5vU_@ z2wiuav9iihuFEN#tr$9Hlf>a*Y$a02nhVn;EfQL25k_?q8z+b&%Pv@Qbv^kP{puCO zq5FVWmfIhwLCcLP+g?cCn^yJp@)^N!u6|CvLj8v@o)-e{Q9<4vldwrll2Ra_o=ZbU zsn&6~2rNB4@Seb=>Iwp~(Z8Bpuk8&X85O%z*uv{otkFX?A*~*FcixWNy02Gi2#^XA zHklgrl^G52w23MMxZOiaK{q*lx-SlMtp))+J$*W(-VKyHw@%InU$56{xQctArVHOu zyAH>&;{HJSL78NfV7=YTol1KyRZJ9Xa%z(<@&a=G%U@+xkrsm-)W;*#AlyWr@PMq- z66&?xs%e_)5YK>F_>315lAJbFda?siE-V-ch+Y1G@{*{TDBM5sXWUPJ39i=%br{DG zP<_PWm#68VzSmKV=vc6-s2<~Ip5^Z`dT~0a&Hkx7K_5qU<0FP&h`~U9?WAvs3^3Lz zP`9|{>Eb4uezDJ+VdRBZS6e!BHjt$;>;Pj4nb1_apwjtOj+4{kMZ6qmI)4C@$au6pO z)8m!_b(p!pY?ta;rfY3i(W#~%2)4Ka+ig3Zr?LV9)&4l9+45l#n<`EZJAuA`nW{*(8{QMqfnbC43bs<`MV zcs7ZYbBl++Y~Br>vL5PqiMC{ZRt^kq2Zkf%*Fteq+GZs%lV{NAWJXPr=+rZo9s_iI z6pH`qY>I7rUYu1{55^=P;?n6x%_JK0p92>&c%7hBQdHGsOa$in0wj- z$Po(&o%9JBr0DtOIS)CMve=4yKI>O?T8seRaNTVGE5dXy+k^ve^^N>YvQh*fBe_ry z{DqHmwfD~+ZQ(m3TC-1pO<_c3r=#Ty@Bq}UwFpoZ8PlH#C4h!{=lFtPL z-MKuK2#l2TKu~1ed!9$Rj)@SNak0OTowkH5k$jNd0TD|lLRfyi^b&s0*4@m&J2!4G z0V-39R+0cGy-9$oE!QkfBC1?YdZY2rqu8JY2XI`fJDQy_x8A4$pZ+)p)40-W3G(Jf zlyoHB69C~7e;S!DL)OLlD z;oL}cwc7Fsq-id}4LMV<)F402XU4?1CYd;(A&lEGoAP=S3Z?}}$}%-xomQCw#P_%m zZfyZeV;q1^wwvFYq5bhH>majbhR=H3T{1{24LzcHL<0DP)Vj7X61EwRaAJMS&19Gs zLZ!pCZl`Oofbx!6-Wb~&JJ!LVN6D1-i`wW_wJAc1Ewa>{O$af0gZ6BXN)63cq#xN2 zE~;#7q3*4E<1*c-u_rO5^D3<6i-NF5I?kw!3S5BXG?xwscmAZbdYtPdYBDE3i!^mUhw;@ptfi@)6S6dKc&egAQsjx? zso$mgSAz5j=hrk6Td4dZX>R93` zsvFxz<7AeFe$#1)3%AK|I^Srx1CIs%=X)2~259Z+Uu*Fb<_(G|WLhRcTa%!+Mvr)W z-rkcc9!yZA$Ac;R#BpwGMfo`zp=$8NqSC@D;8rm5Qb)Z{NUKV698**{KCW(Ie3oW4 z%AU~&K1+RvWwqIjrX!zYd#Rk1S$*#MJX%(-&#-SjfAVS^a&*VbdcfyIt!_Svp3aXa z$)EFshxsy0m>?L+k*VsfIn+lwT7f9lUfMberD1dK?lBkgSGUb%iIqMersvot#g~!p zY!=Ee79$R&FVP^7$vbkD(6h@5ysnv|*ILT{II`vzG@D@?ytGkiV@(FCW* zA&HGv7c55!3DZJGF&}B88nyIp5R2)3`{2EdeWRunVrb>UDEzZbbQzeLjEFiwCxbpQB!HbVesruzNHVIxAFg=gw%D z={5941R~CB*U(`4B0*~XOX}^{$hZ{I5)iV3Jq?Cp+RVKSxai@6kJ<|&4PHum_CTR9 zeIVKal(NJ$sBSy)hiOo#SX@81DTbteZa>Fy0; z7Da=R-i_^M3yI6Fp16W51Dy&MIQh=!IdG}0N=j${bUIC-CzDHtt!;totuSO?Mvqrum<(tg?Uux#II$ZT{dd_CP1NCfH>?p z8i_EH@||ea`(mA#X@>E2VZ>}plTwAEr^XVDF)*JzS`e33C|Dssp>_KDQYUtUceG==(tI|k z)k^K8ve?CoO|x1@8;nBu z)=a}mXyOLsZ^zBb^i?4nI&9%XNKLo!jKnmCucD=x8ET=egDO$H65-|pReh{$=|^D3 zjBt<47o)XpZWm4A)f4t#vc*;w?i&cDne9j}RHtJAwD6Q&dajr6P^alBF-(t+?)i(0 z&umsb%1e^@tZT;q*#W#_vLA4qi&5m~$N8#Pf%xzh0WVgX`Q_pH>fBAuYEb*8q8Qs$ zIZ`aX-R>iJ#QON_O%|Rm~cy z!xNmGD^PrQ#+jL+D>a7Pv)~%t-MB(JCupteQV~)ojSA5rhZpw9>v|6NzEuo*x(jv_U+b??%bo z29!ENDML@s_peR9Sx^Q>L=+*mj;>eRwaWFd#xN!%A&7Y5RW~n7<%oRadpC$h^sxbe znLq7x>7Q_g9pU8txeLkG0&Nh~C?D~?W*VJSyLhwRntv&!e(Sok-C6ye@6{q|wjo<2 zB@elY7W|aemRXQuA@mb1WNY@?Fg%$PGSuk_)jkk;<-8rumUXYed>&5w6UwRhK2RVf z$Z+uG$J&lh&?IP{R;75uW@q(&{`?brlSSrb&GS=thfHlzhfO2ky~e^5Tvw$m|Mf3_ z1-(q+k#hX{8xBqXG-tE3Ea;$e%r?Lm8bKt?THeZ_CSzVjAlexn3^vo4vpKUcGLTg$ z`;VH}m)S4&c+HLd@L!;!1k~#JkL+EW(D{5C_B-`vU+(*t*O!tShcYd412x2q2Di5i zn}EvZdvDRy2FRvy-{@5ex+L@b6hGzi9V}ON_iv=x6`4zpgmx}1|;!v3bu8r9TV$^vvZX?Yq12a$Z-p6d^%5CSNujLy58f|J3k|q;O1#@*uZ~; z`TxWcJ~E%rN7zDq!)Du#iN5shXOv9JXLPGqg&0fb68TwHgbzCnzK%bcyI~(1jh3Kx zA%)kv{)*_tWD?p!A2P8T%G$)uqNL9%1U-W?vRvInAd^Za3#a}@d@^EqK6SIF2vU25 zyW=)q?&0^g8);U9t*Pxb%MgT}Jk8!9R9(98XMMtz!9~T_qN%o2ge$iUxsF{PA>y~cbb|?x8vjU zmroCjU@7l1NN(+*!U`QULoiA;JeL-?sZ|4S9uQyFoRBY@Pm-1In;?3b4N>4(JUc22 z>xE?cqR_F{`hz233tjCusz7>bc`#&(B`{scz%6TT zcnd*+^nT_J(r%?^+Jwo}{BEW3SPID(1*MWjw(IY-Y0q4By0yBsjZnyo&9-o7=?sc1 zdq29Z3X*6>^N#OS|HTU<57&D*O|Px@mvuXnqcW3-kG&#}LM#g`IO|t%ugs}^dO3Y! z4b}JykH&Mwa)JoqP+}wVQFcMxJ6JxBN`3Vq@9K9etyBuxJ3mSWXs>VN{53gZX(m zD8cAef`n9^S8^W22j7g)^Du}(?dtu{9p7*>h}5FG`fS&xzz`L!vaG@y)4O81K(vy>7fSJW2-rXvZ;_h@By zH-OU8=}phV&sSNteARo#uIi5xEp&Nt?*-yxwKuVUG9kWQyi03e?>1nlW9cT^+oMz% zfwVsZY62xu>)95ZRo{3UFRlTrP1?$e{wcHKI~S^;VN!I`lAo>a5Pl{F#!6+Q-&?wBBA^}8#@~CDu zu_4t$i3x6|kNqTijG}LmZx+Nu&R_X$dZr|e>jT^RWBnEOa-=;b=DWd+Fgix)xU>Wp zfehP=TCE>W@OYG_4PM5vf3O(d-(t66rLi|`KEk^34xwI_ErYb|5q!5X>KiF}z*d9R zbrWD~iy*GAU4I_?=Rep@t14JQCXi{TARq{aI2))(g$j1)ybv;Rf~6N0K+MIfH>eJK ztH)RPqO^b^rw?$IGg2RvF?6`3dE3muiuN{74x2{R35L0D66jUTv8K^ADRb8hd^9!; z)PLvuk9VX3?ul93c0eQ`3-buwrcC``-aX$b!S&3Tx z-Gb~NymO>QDc*5YVqzdn5ba(-O&Wk)aSIT>v|+ou9v6wMc)6rCbC8&;c5UuxU^&S;d2=o?V6F#uqLi#x$WHBLD zQ3w`ghkPs{D{0Fy7e2&L;cCq!=V~?Wp#Vj83U&5E_Yt}~Zh(Lm!j30ap&|yCT;5e3 zUVSm?cGYX13fp;wMG9sq2)FdTTENt7$_jU%A5Oj%B;If;QFW;QTJSZu(0ygDSxEc{ z1DW55t3hodU;o5Dw2Veae{IHs0C;t#qIS(!gTY4jb7~93@2Usd?~nzaI^M-&?@=*{ zhwVA3v)CfwQr7+9Pdt^ET!`=ljCvmB$4BU@jx-AI3xiwB1g(yjr2BYEEC&^If(<8e z8m19TW0Q7Q!X8vxscrAzgV{{NVg3O7_$(PqazKiKa{;gLN{Zd9UrR# zv)U+8vRw;9RAlm_Y4BsS$#oHLp4(|XA`lktY1pHY$K!p6Y-$|Cg=BWn zQTfPjz{j0XFq13V2sAvPHLVMP!W1*n#^x?t!~M#b44*zcKmWmCMKHG`zh9Y>1Pp)7 zclv&9HwGY({O>EYxBKN0lzoj67jTWep!51FVW?}<7W$umls9G}L#4*_ zh%dEf56byrS!?lES#x&wBp>n=EY-Tg#MWMvYB1N$a>3wUsoOLmb??;V5&rW#_TTZEe-TK?L@d`Wvp5U!E_1;p#)iE=8$T zoBb>r9QU6em<4`Cm|T|DgX-ZCh?YG^s4?9(!eq)3bUWGQTDe5C(mfF?L?A)khGG?l z)`i!%%?4r{$Ce?CS0stmliR4G6_d-Bt-&oLA|};?B#kXC9E*xoY8bUO3#h_T`Lv zL24>Y9k5oEbb*Hc&}n#cJGdE?blj8t7=^)1%_B@t2p8i!czJPL~~i<-sD&N7EiBpiHXXV7RA{_&Ek)&w*2Jt zZCV5gjvpi11ZE!>t=6KTr~v-s8yve=2r} zbmX*;7_44WdAjYa@&jV3QOsCsB=pELhl#|`3A5WkDuT&2EM*>{)SsFNfk4rR=@UadJIdBjaC zDf--o_v?haH6xS-9&#P4xA)cv)OvNQaA!AH)Pxeoih!#2=19B{LKb{_{S3YBNu&aA zivIBLO`#^eQJs``hV!y{2vk+uF~M>lXkHnq*5Wx!Z&&E;ZEpfmq2TESC8XUF5B4S} z0gNUOorI3*lPB9@W0G4V`!l2Sz=wRX#FEcXjzX4=Z>m6|@jB{zvvW zLW_FB!bKP|iv|>_G;06oZa^$RLd(X_w1kLo4#osfJ{CSFh~Ke&{^(JEpZ+XASS$V_ z0Ufa3fAT>!khZuOfPl^uMnYcX_RdY7L$BU_gL2#OB*+{&gM~K5B(wUAZ3v%;P@!67 z39M^kV0Go-mFO2#0Y-Tx`ty@VmQZ6_qnjnc$iBnb6;DpLSgP`wU?h^#1FbYc4D6p& zztmoP_(mfl(X2=qGBqA}?Xil;(B<0{-bIFEO}A$J0oC#}4YETsu^>MR^7q^sRL#9*#t>O*}#ON z#*v2sE=+}Uw`4uX1lsY0w0V#RboEV2*Q4+ZU)+Rfec8lGl_<2(cYmxP%6M#rsygnV z>z2qTWLIJ!E&Qhy1Yywrn zy6Xe(pD#~KxlJTKuRd#48SjZ42RQgUHw!)oKBYkdtkZ+{TLj4c^D_*?jnz83?!Uqi;8%QC* z%p}g*Zo|CqXt4~5Xp0Pq$JQt@vD|voJ$>?uYPZ?yQAGs8oR0So&M@DFXYKeq5`**K zt#TxPZ%%Mj;e9pe;>bdSlrP)}yA7)jMxEz}*{O)9yG4x)b3ssA7z>otNfCoSUA+0e z+gQbe81!-v8-&*`Hl{bmFlx+>Ave9>P3+an7yr(Bw_)^5GQO93=xJ&}0W}m8{p8H& z9rS;Ko@ZwCL-PS@p zVp^b+io@+XCzTqOx-HY>llJsN24}FZ#nVYSqXcW3UI9$ATmpW%XXde&uv^QNi3g)N zclkojp+5zrMMl+|E4&KtF+zSvdyFq zaT&b=!wUlz4#OQPtEFvsCp@Z~tR3nwWo;9ewcV%_Ii4E??Bw)V`XX67;xc-LHP#JM z8jQUH7dPI21tUH??@YI6S&F3bPi=zPYf3>(OJW9LG3lONNfC%&i$*YY3X_L{U=q`I zakGot#SpU4*sfZu-83|O|5&4p)TKcY`J`$EplV*pp{4;}l1p~A_2<__yKr-^wi>;c zQ%5Nuluv#28V`hoIpcz~U(ifglU_m1m z%HK1Lheh5~Ne=~iW;8rvbVOuU*rC0j812n1igFG-+PLUIUO3rU@j2p%C8OEP`K_{- z!1EzxdPap;(PFyr`eoOqs!mv)+YnWB<@#ROW|hT&gyunW!n0PfA6cTt3pzQing~dUR~B?tup2lJiO)Y`pmLv0Y`RxM(&? zn~CoQ_>SdCBwdxBED4hg*)$2Cfa}v#$X0LPU;gW5$q3vq_naxHJc(JK((K{R1VA z5PcYoJ}|^3O7p=U&o-PeWeH`aVw4(qEoboqW3;Chi4mi=pEagtOrX3wdq^((eY-1k zga!4d?{$?+Ff}&WsO&%~w5FycgDEE#s8u-mrcTYn%=UVZXWws4Idia;9C33~USSVJ zsN$4YT5}iu7fR(RXTat}D4zt&+yW8!@(QE3SMIEl*(o>ok-=uC+^Q}cY<4a!$jmx5 z57S%rGfME%$_4v`3VzyzN_XG`D{0+LW*L4c_Pgs91tjjD5qO(#rAB?lVQYqOz9RVl z$jXKJCgpQ5g;vy)4E2L9me=$->F!{)&C`a$$Y@{myD2#^LeK02kL|o}8?gKn>Br?y zys;~Ma{hx8?WwcOU}3~Yz4(Q{uLR;a&vN44L>8b%+`l=~fx(Jh0bz=J-|eBy4ydmH zKLGr3S>QWPUvDeK&@$B)FU`932PV@9Y8F0=W=!Y=y(QzQ+Ps>9HGs}lgPQe&JjoU& zq0Z&Lb@j@xDoB~Z6$c^DVj8^NETMo|N;mh2cM^r?TQ|C3drxoUl_3s>Aw;w7n-w}9 zd}_<76Y~OS!YX(9c21h+bL760F!SXwOGAuWG*@AL@@9ydrDr`sGxwxQdA;}M7wKX1 z>i1EN81@3WOeWQPZ|efFqPkG?@vkd_xnF9OsLXkR>c0Kl^;u`^sXcy7{>{&Zx>D~P z5$eItj+dJCjY1D3bMA~eEReEeh@|Fb&`u&kY}E>M_)ge3Bw5-LNDo(164uMR-RPs* zL>L|qgQ zuT7!thShfNrqeF&gX<&8PB6CZl(4iMK z0sSZH?Iqd!9ZEmCy~nlc`tB7D4b~k_T;q6t*NxGmKQj^?<`&$^5>HNJMPh<15RQPW z9Nr3;mJ>fH3hZQE8hxTzs`}AeCz%5rPh`}olr)k*$BhKo3kIuf^m(~0`6^mIRA3uH zg%?8(A&X0XPvnSY{Wv|Ip%1xrm%@b6y0g0qV~U4|R^GTyD1G4c0D2&pFP#D|1XQt% zrp1=UOQBFtvDdiG9G0yYqDL~O$d2>QGC^Z{G^Cti2T5N1!fP>_JrZLn&eGEW0T@}s zcEsc|q(jPoIzb7^2ih_X|hmaO_fyW*49e}k* z#IWPm?8xmbwlJPJ{i1Y0cTJmhI&GzN{zE)u(;kFzt_C2`q|}PvdOSbe4D^ef{j`k& z)L#_1N${U%a%8D!}a!-YG@X>vlVXX*@o#86Sd*; zMiXt!aJgly-)h)a(y(0%Q$umT^8+@riuOx2rf_H|f!IW)nxu}bWYHO{n0eBdGSvI8 zts{0fPP;v@mT0sQ9olspHML4eyP9Wo*IJq?>2Se9HY64@>p@0v<7Jn^d=EGNumyz1 zg^W`MK5pX2OkBn(!^Ypbj9$(uc>J?OaE4QcU9tHBrwo>t7bY?06!y5d{Eb)UtD6eN5^!5^oO?fAmNlA0Uu5sQkbV?k@jI? zA;9nm^OM{G@|OTX(ff5`cz#<4+>5mweoPB_;2VEI4&Ue*Iv{5;3C6~o-0#-WO3G+BTyI(MODcT1D)Tt+@C17Y5 zIoQmLIlA|O2tt~dJlgnCoAv<(2K}7b`+sh9oLMFroe>(DpuL7F-gX9)Dx5qPEr51*?FpbFrJYr=89~37D49E91eM0QvwAiG^!CkfB~n{eLj&r^ zH^2PwQ<0pR=>rgJ_J#}c>xcJuKmJ^zj3O<8M+E%q-~Q{}+tTdq4ys}s={LuZ?>>Hf z|LZR$qD;Ow#uK7gmp4DXzx!7SYd*x0>~HTr{QTyZcfS<-V9vb}{PFJ1hr8c@JHGwt z-J1_3>b;88@q?e={ru~PVm~}{ygB~->ksd2gE^+#v6#a&xcA3@0rpFYl~4pZp4;D# zKfWn+ac^&PTNm%yts(;iU$>= z%S*gpHSyT)j(7ikeEZ?uo4XnKT3un|AOZj9yASXG{+?r?5OZ@JYCy&CH^1NgSb}ae zFeZln_~zq}Z-0CTSx_L&^7+&9{d6o!mi<;85JvTq#ap4Zu|xmo_h0Ykdvs8{%ZPsa>(7uHKOEou z{=@tEKGkkRejG*EcfLy73Vc2{bPK*1JNL^!zdL$7;Ok}>pV)$bZ{DIK9DMTrh$XR= zDX<@(Q=As^_Zvvo{+f^Z9SCY+pb8{H<_v6QZ{d@~A?}1LEyc7Hn@BUucxfS1BDnVxt%nJ2+qc1)l ze}KR%Dm&Fub@FjaKEC_q$#uK{!{P6c1w0CN99!LC!3^(8LeKOeZz$-V-yW@wC(7JR^3*%&vT-66n2L;(q)6Z%{5v9k(Z* ztmC-+D6)9#!-Kc+f_J5tKVkPiKa`&p9L?~^gqtI%=8@P zWisDkM&|ITQv5yqS}l{-8En(J89%Jm!eU6?!)Kuubv7H}-xPjYy|7bXTTMtf+` zEazymyx~L}^FH3WX1`E1{0-7?J|l+VEmCn1`GeXI$M;W#t$pb9Sa^%d2e7{aj@QrO zrYeI?#29!6WCipL^gpy~m#Gb^VZB8qw@>}M-#(UMwMD5Y?6-NC-t*p~>eb~fB7!cc zyI5=&QLJ+03vv#kL9B0CW8?#-@lE${)I*vBE4vH$BV1qQR#^Z7OZgU08KN#s8Dkb$ zb{{tCpPo_pd|rId&afz*(i*n`A71%(3WhNQv*cPi1inXd zuQwv-%0C|eyZ`o&er8wHmS`xz|91nX-nVyekMGX+Jp!@k_GE1jSiRE--XApHcV8LmR9!eGZympw!-7gWTU_1t?zr|;2D~&>fHb6 zuW##f-JBjycx0r^50+_4C^inV?%uq1r|S=>3oyGK8JLQG;FAxMr8h&_UaAA^BkIvW z=6#m)KmPpY?eQby&gXC{{RCH})R<-ZN4OfoV%;FvER+)nFE=S~ zlB54W;!$fparR9`>KoR0DfeKhTVVR&mRxOCs`$N^_{tfI7H#()tc@*z*V`UG_7B6| z>}ob^Rmmup-l1UOg;7l9&TdrGo0po+MyLGe`j79Pi;abP(UhY{<4mg-U`m{vV>f1V zrx!Lre~?O7ALW6t@?iFA{soq!MxBQ;tkRNfp&a&{P2p<+^GhSiu996 z5$!Y0_iAAl*e#~&H8O)ZuKf%(OGi=^!-mUiZV!JEhV|s``0W`-^o+d6`*E^B5&WNg zJF-k1Wx{pfa0uz?FvBdU!l*!Lnw_{sb*aG{+E0R?F9>UZ9I)QId7fA z?KYl92oU5J%pLQs~y2}ncEU_bY@(8mCOo;t+Tw) zRZNl`Is=P)`5S_sQPg~96SaJPaO~x8R6K9CltB#}*0KGf6yYh_|@AQJ#p5xy|B7tTN; zGm%&r)Vb@rcOMQE2J|0CxtmiQmVTlGNE!S11p@2x@Pz+YDjRH301^=-`Y#mB%S%Oy z_JKhd$%5^t16wI%rjcHs9$qfI4^=~jdSl8}5aBr$QGF?mgSuWp&vn-nG#(@>cUE|O z>eFR^x@IE`T+>ufVSzr?`Xxl$|M85cgDcu;ztpAp-xB7=1t#IPmHOdvAkJVhCZ``B z5ad?@hG);|+4=*n`PJrE5V1dC$27<5AkC2c2)l-ghlR{+vZ&;?j?O^hK&@FWH4vhN zQtPmI^v>+SP*=$=o>0EQ`;R{=uvVuXOHC$0ImcSi1s$F9 ziVl9}GCIVVvl*jzxPpu)Io{)3Nf3`Qy>pYs_2?6=q}JOZte8*zcb4ZaN`QibP$@D> zf#A&>+{{HhuFi-C$trpmU?o8!1)O)Wk|0Wf)Yo1C$FQ%07r&AwN&&1B>zk~kh(S*! zpF$;3Hy#-IM2YvJk~T)AAno7SD7JyugX1EGRdBpfp{NuQmkbhMz6?&7GCbVb3;7#I zNhLwtbdi6-Z&gVax8Kn6dK0EG)*)cI#;)MiEygyCezC+e`TNTSPNt`lYPC&{bTMbB zDPk$VJYF_e6UEItQ){oK3r0KZsb?@(OCb?&*q+2GdM)R`PRg8(tMzIcbs;cKG7+z( z;G0pQMuh{)YbjSz06O>W)l|x7$r=VF=^A<+mw)D>yOyp|+ck~~dk|-6*U|*;C8imi zFWajL!ZWSf*uQp?JKCeSYlFDEjlE`5O|K`5JtrQoD;2{tG2=EYM@v_+QKUw# z{c3_})gYq77znUg?h4;K%OUBnN!#+h<&DCm!(|>jtZBspfxt4jLq8$=& z>%3aPI?6N_FrEcnE2D{VLu$hXm+rMRWQH=Z<6W`qiW`kh@)1oxTdX-dh6AM#vJdY?Rn08~w zX6jhQc-h^C_yX3gEdJ{$qgNB%6;IvEl?(U5Sx&MORx`iTb9%x{-t%%9$xWwx@2nRU z{hz^oBL|n|BIa>TDcGMemXj^w`W1zwTA4*0!y(Y6oZ~XyI2yM5c-&D*67!CNw_1~B zG@OYJlXg4?t`reB_|QG$k!mGDT%w`#lT*ral2yENp-Iv+wMvrce2RWE6Ma%i7sVkZ z!rw^|&8}fsMlGz`)vM^qMloYRM7d7uI0LOpT(j19w@q|V+9VS8RxT#FMd0kmYBR!P zFe2u}Cp;Kx=9Ii#To)Y*_K>LpkDU+?8OzW1Vae)=3noA zd-w6pXI$N^z~j6kJX{E`VB?G;mR6cx{iwiA`2ObO+gcS&)OffH8E%GGa*q49ROI(s z&gMD;uiI7fGj|v^pZ=)fqGOWf;tE_Pzvzu1ju*u%!03Y{5VwCSxF|0Jj)Jg)jnb0X zaR0o5jY1OG^!j-PA7!NA!-cvsIPu5}c({aKL7RE|W;5PHufXlR#Z;`|%$$A0E5sF` znXThh@e0^X+4mcS8DH-TGhqdfyYrvkfBMYJ^OZ8p z^=Dki#ii|6#>mWTNhPX5r9x(M3p!kZui(ujmj(>?H!2CtOqT?o-u!n})a}e<$1S>_ zH$T6JaQyi8=W3st`7TM`{r2Xczu;DPu}UD>9u;5Ac~^$?a3!6Y{Vv_LUYp#Sg&-+E z{PO1WPrrZswN^*7BtoO^fB*Oi*Gp@&5p17X!#;fc{lDJ}twg0wZjqqBmHVLrcWVT2 zs36z+%qybf1EyY(@k zuSpWzf3f2*Bq9YytsJ*ZnB>23Rkl(qw@Rddcn!Cb$gLBq2GmpD{rfHUpMTYA7C2<7 zrpao-20dIx2WLj5f)lq?SV0Nc*W0$`3W6@|COAl>Bqn(srfi0^7 zbd2Vx8fBC4P668+%-Av9vs{S3c%L%FR8mBz85%DQxiaMzhIT^Gkte27Ks?Bk-sJ+v z28F3wQ7Bj--J zMuFu|dPV#J8Yo@?M+ZQfb71qSq=~zxIJCw)8fADFJpodLxV2>V)MY^zCm!K)_E7E0 zE(%tH$AgDzfl+nNRX%Rq{SWmI0r#K$Vk`G&rhBs8>n^RY?-#LNGd?HVGG0C4t$DQASP&RZciFM;!3R z2U5F)5vrW*#oZB0VH~^Dr>w0+xn+2P`C5BG+A$)1^@Y9OF}^l&H=~2Z=RR zHFc(zDJ^nDl`{_ynX*H(P_CNj17~)+L}C%wthQ;m>CKV`PqbVMDA%hoJ1xeJt!Cd= z;4zjszKn4^ts3bMBH`b3fdL@OKz+(zBmsJv&Nen^a$;&Fq zVnhiR%IAdL=vsqj;aQy6ebF0&_&M@L6^onQ#&9MPstK7H(tW_}sH8J&lIV_D9)_%5 zobUIT?9;E)T3Np9W1@F&(JR0G{;@I$vs*I=zM8d}HA~{?RHDZc%iZBQSCj6jB#4&5 z5IIp>OtWc}I*b4*@>G;d3H-i)ie^H}mQ!@CDse?R@te_&0P-|NiD(_0y!XBA3z3Q>hVT z+dWcz`mnma-OLv?iL;C&>(bWNzhyZb>j(GnP#V1EwVE15{txjfhb}4iVG?eK>OuZS zzQ#%dW5qx{rXqB;h_yR^p<&7(=j(}#-@$M7FT4Nm!g$P?_C4q6|>gcm#SLaw8FE(QCr@Wb! zLX5$0s52`G;@HdD$IK5*@(FupIcd1SpDH*Bfo3I*D=y>EtRyotPLzgNR9z+N&6G=% zvE$1{ZH*hxrm1UD+ZfKHB6K7Zy`nCI7Sc z?j_(;cF_txj;)WpuI>1D;f+a_q5i`VwOmawi7En`&t>6L(R(*BwgqlgK`36sPt8$rkW#``TCPa}6mMyppVHzch4-0l*`lXSq4 zq+GRKJi$qCWK_p_7p-2+-7w}B%a~iA2?l%g)z_ObPk5r0Nu_+@7w>RJ4Lq={~?}lQ#+XttNA zt7xN?reV6(6!FFm>iuRAm^FA30V43>XswDj4x1!cx0)c%eJS9|+z9U~hel7ZL8ufhYr;Q#<11|bl_C+QIU|A(04=pE5o^Vmi3TK;`*v(8S`4} zMt4*dZ;n$#TcYQ>n#`T6LdiooQWb5Mxr7_mpp83)#B*g;sIfG#rG}8hD&B-!5YM2) zDmv1WVbFlqu)`|ctR%qYq`8WY91xm$5*}Dfleu%42HIB$5UeIMlMfXeLIkS`%(O!S z1PNBLUZ<8Y!7AFF8*v|rfr7PE35^%}%7zM7)0n9tX~vh{YH4~m^AP)YbqvGvvO%1? zSiV{zRtv_XB55>VHIY#jVr1SB&sR&gGb0((jon@y*FY{A+7?slLw}X>7B*x^A_1#V zbH+w!btNt)Myt{Iyjs2!qkoN&b~X6a%-=EAt_nB4kP#jN=xV8EMn&+?_|;O~89#xx zdW;IICNiFpax7Xc?c7+GV_GiKSIeCn1v!mJtChBhVR=~9v9#+mY!O39k;=uf)rv~4 z@gh+k$yQAl9V(hG#sJSuhs8)b|Hz(q+D^m-;QJ$W}`iP%eaR~thCIQALx+|`! z4~y_b#VA3;8Ok-ia*;&}=?SU&=c^SoZm^*6`GA&c;&|>YQ+%i;Lt3h7%#0mRRvFY% zO%ffAC}b%LORl zerhdUZ%)k7zAWB=5dhWHX8SFrX0$>zwXt2t)Dj(0O%wfXvPhC3f@%^oS54eOHHi_o zHu9jBW@~DuB8JC6p+~F9%ocB=52{Jb?g44*&h4w|oO6M-8&B@5iHtIV!t5b@HJRzb z^ccRDBD}s6_JW}xIf$<&F?y_ad2$qAO=h%%^f11f!Z;VraeO7oGC2{IO`~`qUrRhO znxaUZcurnhPs^$CxVThTWb$%qtY4S#$~LeJ4`{^wJ2@_|?u~em+-Vs}sw;vV@#9z0 zA>y#sn^WVYNSk*>gfFMYSCOWQ2lUkvjUI)qd~9JY#mp!u=^=eB#a*O(!vy1c@h9lj zl*utKeWqew>!XU*B<8GlJb%VnTs4L9X60{*R?|8CfI>=-xoaur&VUaB3N}nNg%M6; zYpxfwFqYqVf?X?PnH_Up?_nF4NTD8Lq^boNCoU4igVAadqm{_fXbrkD1D?LpSxsS7 z7t|*nkJggdV{IfEP9U!t4d|#eqJuYx^p($ADUFkE_Dr#s%3gk8-OTxAH5s(jixSI& zP55}g!xF+o|FQdg_>7k!)7Kq#%kWGU<)=Yk4xcV~IrWQ0_sZRs+zI`B2tVHAA+7HY zttY}?#<=F6@uuD*YJ;bU98&EYAWxynb*RoDK*QUHpYXnt%`H4>1xN^3EI`zW7^}ux z${!DpKBu?~YOIDWD0$lW9#0=#Z0gl*^x41rzU^N8_X*Zd7%3&G3p{@RLQwSM;quer zq`hDY#j+SvefS-3SB>H>+zl0mtDTQBaXyA*7R#XXLe?Rg!D3g~Wn#YQ=W0i+Pw=jVMJ{{tpPFn~#S(7&Z9lYa1{<0p;1P4wv6)?rA8J_i{cBHvCNDI(}Cl@Nx3RT2WnXhT*J%`xuzq}rk7Uix<%+* z0nZmPig-FiVy(kv`0x~*tx0csOOX^D!c1wQTUg#P)2kw;xs94v*te#3u{7JLX}y2m zi?Aw9`eql;ZCJ0u9@->(C?SP?v`Jnd5eYiJu49_m?4rp-*zun)yWRF=Fm2Mbm{m>J zXm8_*7VB2p#M|T+T1f`Cvlyu|YHYSn;f*q?VC36)!ZXE}hqJ{jqA7@ae>@)xm{mOW zifs!@l05Wcvs}k9j5Hs0coUtU$eqP!>QdW8Pfe^rVJB(ZXlt@L#i8n>8AeRd3xh^i zXisuvi}noodV`WvSA9q119k;6v2XcQ+umxZZFugFgLDnoC5%$2MS%V=J>SjZwEDH- z>#X*3cv4+e^1}CdjL?LR{?c7uPTS!Mg3W1Rf_7;^U`w-*rMKbV9FKT1*ny%NtcHGJ zA$Kbi(Za$+g#NLAzRadJzm;*L0IIe|Uc0sTGs4(7HYclR4=+`+!!T2nLOfIXyX2*f z&!^`349fTqcz$wsI6c|gHA$fX7;GGiYZJ$Hz&SwlGJy*G>}uV`(4712*UeBTn+3Io z&uYvORLEwh`>KYM{*uHTx{P#p(^%3gFvyUwO(<=aXm6i?9Jj1sXPeO9M%kw5U-r-C zsn}0q9Ag44OT>N}-SP>zT5G&5Iojh1zEI&=lquA>-uKG5U#Z4C9^0hoLcYsn$0Xa> zbW`t%WjrU5$_8wu4hbJ}imvxXc(#>`Ax%)IbQ3oK?X_{NKbMUEzKz7)<1{L5d3_t5 z;^VX|Mq7iYU<@`)D~r|@Gw$Kh!|c4A&ztS(bU1A;`JCM9P!RL{bpsL9Z;oOBnDx-_ zG!i+1neW?h%O`yQ#;4IOATY0`fDJ^sHLtgKdL$E2y*%B%IrHsyo7-GtPl!8qrw!aR zUI=ePc*Fo<%WNJi)qe>1g4bIilKR8b=K1BRbz0E2X{FnVJOjyBcqs-q4R&>#XQz?i zL!R1zo;|yN?)Tl+swvxW2Y_%uUc%ezr3UZRH}nXld9(exeZD*%tgmS|Q6F*B_nXV1 zhEcVsec3;TY)ticGrbUVzMSv^y8EQZs{Hg|mBd}RR)kN5Z|eGEvZtfXF}!}lPi~jp z&GjxlK0R)qT`JY9@aZ}GMw8%@?;)?-st{pDiwss6B0;F!J_OtHKeV zvif)Gbkk3p-3jxm+uPKETO5$5!&f`IcX;2A?O<}>*V~x0@6SL1f+lo$#MX8Icy4dC zy3zrq?^7)l-!F%BRU+wh9Q*zy9YlPZY1i$~{r-#50r^eUsr~r~&uH3t{HF7HfA)); z-&7CSpTCzf4XgI>c=%@4DH4W>csaa4DN6^E-&AxqmqR)b{Gc-wA0Cqt;s@2@q(j`N zg~mh4rx*7v-07z{vH8)u#;86vZ7Ky&&`RCiUw6_^JC_QZDrC5P+Uzff<{$g>sWILd zWU)qFM--}61T{Dh89}@K<6{}6M=f8^&xPtt!Y$BJRVNemN@JmXZC`9NCKKH}@h$r_ z)qdF>UoNR;Ke?Su^_DI^*dCkAbgnXq|Dqy*c|A0C3QWSBL(8|%2h+=waF|za^9({A z{kA`E9=CXt{KwX39Ol-Srwdi*mvPN$dS17aqHDms(!@s9U~#+`(&T>(s%NMs@qkh8rLq zmM~3!Y+e?3rAa;sHv`Yd{izB4dNiUR1@>CtMzn*6e(z@VR>zg9!}oIs3BR}NeX>=x zZik&eAfPm$u)XV(9rQ$0o%QC&%K`cV#<(;cz(Xi9#^TFK|DU=9Ky7{ndre> zBB(a!29gl7b@R0;Zplqx^p0t|4NQRkVCNjfvMhX-&E-_8-b$4n>$ z;>sA(@u5p4he9{>8sWjT`PX(ZGakD}W`Ojx>8AEhy*=jvBDL_H+1_zP8@feuE7RfK!tmFBHwYOhPe)v-)^ah z2bPA6nC9Ykqb^AG5uv5+pLbT1=|Y#_6!n45B?8dQ!=FxZ@8;^AcnHha|2P8w z{`vh=^JQC-FFm9*CyR=4VBb-^gakI%g}Qr>Yt->@gm{23?~d?VrxyR@wqL28WN_3( zY;K)e7Y3!dVJ4yu&g|*8%KI_kMNX7T-{~f3aOdsgPU>r_unk57`t&Upz??O*kVP z%J_dTc$48REjC-*nVuM+7C36dghU}FO@DW%3go%nZh(adJv}v#UZvfwgTpSM_LM%G zO!M7^bD<$K^eG&fg)R#GbpwvNkga{MQ5mQa&djN_v;H#a9WL0MpLpEaaF^J?JzxUy$N&?!_zRp^i@UT5 zw4v1F7}`niaH)174tmIruww0IdcQZtm1=iq`$W9$n^N`eRN)`;gZJ)n56>>9NIv=# z(B_JW7|;PT#TIUKSO9}FC$yS?X)(uTaLA^>hr`*-gUM}R&>YGopipQyFC9=g%FwmG zfk&XX-acG_!RDkZ^{4%F^Z2RM+`vOHWY|kYV!3CUppAxhH2%yNGp18t)sP!!{Kd)- z?AUgAq3+8PvQr9A15;2B84aMRL17G}f$aLt<9X9#uS7$=&_lhnBLsIm9K{1OwcDzBhIYSh97?63}n&{$)F96&!u6&aWTsx3z9M zlJMj2zqsD#Q2^wJeD&WNHTv|M8>C|Hgg}d~Wjj z(_6ee=04}^6+`yiJ#2RS@77Gku>m@1nC-v){-L0Q1Ebp^j7cf-%l0{#OU!U<$9WoP zw^J~c;1X1Q9AeLWIi7Aelci}0RxQ=^ntb=;D^_^&-H$KCgel+mpO`{Pde?tF^wBiEAKzZw z-Z8x&-(EZ!8{9K7w|FKv>4uK=@#b)B9ydYsT$x^C7U!KTH<`0fFlfW{`ovhT;%r8Uq2F@;YJ$waX#QRG9a?YT^)PG64af zgF~3%1_GBf9KuW(Am9#j2-hDoh)joY{V{`3atPNScdsKLSX>^i9z5H32-jnGSlkZb zdh8C%$01x>+I(y$E|WIzRM+h-)Hqj+S!eUUg=@zlCcY;595RayndH4X@ioc2b>jN9 z&HL55Yfw<$u|_Tc0lDVd<0cw!;W>n>2m*^e!VPejFHJW#eDu$i;<~lVC%l`i3k(k7=GhMW zx+Zi+!hj4k!UTq7bXj~qy zOG#$#5U#bnspt@P+z~*UA?zI9-sP*>^%%`tSBmQoUh;Yck=MhO!Wc*9v3q&$pg6de z&ht9Bmd<4@zWww+pWpuckI9`+58d{3Gk$eFJ(sZ>srNi%-6QfAAC zgkrgK*Kg!kO}@H0w3JP1$a(I@Z7FhN{OWpRB{F0D>iT*m^LPB}`f4q*;<0&#A#ghX z!zCd*WcW7v9>e{$#-<8er&h70GYUv+`(e-1-YrP z%XRjNn@hWVb-VT!c3aov!fxwU*)G=|oYsa0=l$kpXJNl}Yn^Z8x<2u}mMd+()AGH{ z_gTK*`kEF-*zA~ad}qzID*`VmzJ@56Q+7q};9K@e}-Ola;cKsk2@>=H>G-5Xu z3L3GQP75rZY*w2o2}CaVd>(lpctmbSIs^|yT|b(>0GV#C8JX4sk=MiYHV|Pf632^~ zus{@~YbtGk$c3wK%ay$w>19>@=VgETbCY?G7Polq(07Czkiz5wTXf{85%%_QK*<4mRVnsGPyyc6~rgNbW z5&!>6A5Ivr;ijAIAsm;zmIv>XyF6@P;Vz~kf^4*tadKysAKOur!Alg<+Mb%+1p_a9 zHEPCU!W&YF$gxv=G7sibNNbnbgyD{^Rdx&}Iz6t;uE|{dR}y5-Kvt*vE+)Kh<;t*g ze4^&SwK8~3?{xEc?1n3fUe+%r!`75cRo4{Vo=h>WB78T~Rn<+Dl`Pofc`xCMr~mL4 z-S_?1lUY-fmB<9`4L2SunI)0|HaKJ;D4Tp;@Z>l|-lwjy>s)hiGm`8yr<B54z=3@V~jv;u)D6 zcQ-TCLpMmWg-?%@+(d@@w7KYTlcJLa-s2P>O4=7^TrLm{&XF2F>y)t;CP4X)>H4|P zcRP;=yIl|#{;SSv*uR_Olf7NAn5a3_Z`+qIneHATFv&%^e@wBV0(c@(^Dt{w$%n_ZmKS2d(*K|`_|(zKu< z*?Bir?Zm7+UsJoO`If%gTjWFWW8#Wf=k81EIapx-&PHYG8rz8C3Q>%hB8Z{$;pGG) z!b_uRlM7C}6;Bz(5H#g zz6Pu_2c22))c8u~<7&@vkW0Q`t%n4Vf>Pz$OM-dV+4fjtF(pBQhq!)eb4%N90n@?( zn$@DFg#)yyFDADIrmo`(=VMbhJ#;U#YtAjp!r_;7mZ4Yj?)P1r_r9xa;q=SZmA3+1 z-@B@Ixm0$`yURT(Zq63=XD4&}FsqA7ENFnUTKYNP=VHip*ETnNU3YC??Tl^Mf>J+ zC7exjP04oTtgxq82kc@-rT{msoOqc{a|vXZ^sHdA*||BJ<-3LxQ8V(5_?p;moz3!L zx1wWK2#T;i+Qm$xBA#K%`cB7nysQdV?#v3Nr=6O!LKuQw$+Nq>Kdt9{F>_X?>xQ{> zW^*~?J{;m}RZq3^3xORRSGJOuH?}=3b~}BRxeDqks>eGE> zu1gti?q`N=i<*MLB)D^xBqNAR?h)dYKxa)i`(wTsXL{abI>HK+S;6x0E3F{nT{XJg z1K<})AqK*&`NA&Xy7SdtU`FOH*JEz$#Dy#E)F5ANq~c=6NJXE6n;Bi08AA@fGs2(` z0^BY1F6<)1XSYsC>%;Pg&#sfmCXjx16Tc4wF{E}i=L2nNYBv}9u*(jqUBd>`FMM|G z>TSO|LEGovGuLq4o@FY7QQ?_KA-S9zj`urR`eNrzzK7WPF@M#^GH-%|GV!0;9-ONZjni*-pcU-%E*F8=!xXX7bCs-G{Ym2#w3m&_@jq3qLq?H9zyQbh0%|(@6R101) zzAnucd?Dd<^P0`OP%pgQNqk}X1hUd=G0&xd+b0%$2hZ2ob^hE5SUY-)d2ZD>O&+dd zm4nGNwbE>%fho4RR@y8U1)I{1kOr`rTwI@#j@&iZ=TX?wd}RwZrR(`cVOz7i>tc~x z{ci7EEDDRuO4$X;&s>9Sl*eMR%;&AE$};+4un#ot{t>JdP7Pcv^Nw*Nw9Na()s_!y z_}Ur;ZBbD2+XIPJ)h+7I!kSO$^Qu!b?E?PLHb6qx@Tf_moZ`y zj~;#-Xo`iIuNONec$Fp_S1YcTw^n(zoeid?tTSv=mxXH#t`4A1^Brmh;u2?-9%0wZ zk|YprP^>syl0#IDOh37og>zmzqIhbGRiuSV2S{lsmLw%|HLxpwIb5VDYO>411qjzq zymEQfOkVOXqN{@qg@Xjm_hUQvmxWEib@rm*hjDdX+23O4{S2|p87EB5^@TF4( z+o5f~9Nn5G5tUmkZY30a9ImxpKE2%BDO~e#^%!=uq)%Oskn|H z3hcM(SKH*(a2#IA*{*&&4_CJ~4AHh|Mf$6xR=!;8LGxejAo{Pe`-T}Zw<2jZDeQS> z`macC<`%ju0Kp(0;hIijz0(c2$u=E$WV**cH~rpHg)LSS+-N=BNZ8qF`=9e>I=9jO zK!7pDAnrDID;JO&0r3dmdaX^l0L={kkV<$L7m%fAE27t7U%DgdZWi^Q?3pg z=4KF1CcJu9vs%l>{cV4F*q-qCBPLK5@4r}SsLKNgBirW3<_vOByM5!)3Cf;e}0+Zz^~XIkXlki320gcO~t7cK%m^KWk%XGZ57{EkXg(1a(3%#Jrn&|^4qYN z9=@5@uV*rvq5tyKvWM8(tTuNtpf(@A)H^mV)O)p=m5_3Hxu5b z#IyC(0AZWw?Pgv=7V3FhY`fko^m&%rJ}=bsc2jD*x^{h@rM9bc)+0-8U-Mp z?;A@Y10S2O<^c4}=8N^x_tHxxPqikKY7W3WPiTs9E^z^rrySp)7^j@VN_|cOUk597 zc!)*j64uqZ+LU@*eW0NkT4<@O*S>D$ImTyxP2I{9Y@Ln=3%xC_tj70Sd1{fc+Z9@B zX_MEj)KZP0lMYeQS6=mMx*Aw%6f}Cxz)E#%W8H>AOLc2wzbULd3K~0*6j~mIF814y zTWP-O&Sr8ewY1CYR%)r96q?*hE!7k8=Y59Q)a?khpS;<8Uu{18^6wSc+BP3`Zuqw9mQ+LMkd^z=k3=$C@kV!M{m}tJiw`xQIz2FWbL00-@ZKJeos-NG`f>q8hB@G5S7&J zBV4!b{seCOa!y@1)_Z)~UT}-3hLRZo4yqAV+~FDxAF{ZDaBX^5YOV_5l@1xiu}DA) z;%lozJ)*cn7sNC?yv*&PX~CRusy35+rWRG$KpMSKpXOC+qF#uPs6rDp64pTSCbqZI z5fQ~EW)h{E)K6jcVYB>^=K4pP{vqz1Qo!NdUz?QHdqNCM0o+Ugxxiz&2J z9YqO@3TUaZ8tZC+pgb(}igI{4U@=GCnpXX#Zb!CZFCnOEM^rC-KPN`wxKBeYU5h3kr=n&xCM9#D)$oY~RBBVJ{tPKiaw-kHvc83! z3LV_4=R`9pcW~#+DJ?Gz%^jPr5Z82#iXB^3w6xkzoI(kx)bu{-Xf(Y%x0G!mZTiMJ z4%=RqV1&r@%1~PkGZ{L%xc{_U$?6NeMeI5q}bYQ>2S&2;PMI61wN9ooy9rRO$r4TrcI}8Ut>ejAElXt`$4u|9P)yJ z2(I58SC~JDG|zE`)zk42BinDq!N;*KaV!^*alldq@1&m{ zu+-7Y&0v5<1+P=01qUn@9k#F7=E7vrK+7bv@@!e;-B<4Cg^cgl?fH3MU1EH|@^lk1 zZ%TSYw~`f>qDsBClJyl)>*DI-3u0-B>tcy(SFd$R@#*wFeJruK0e5^tBQ3GG19xjz zukwKFrWH#p3e!%ns}88bP%C>_0?4=OPA@+Wn94TXVT(lL{mI^USo!f3>k7u0?!7HyMbT>FfT^_LZx%dXL6hxjNtFQ6_FA?riLW z4MywlZ0v#?C$%ERvfW4w94txvj_Vb^xODZv9mX}stNV%r&+ss!j}Yq@B5tmnNA`u# zbJxWKrAm3i73fr4Q`MX>b#McrlHVX)XOw~m?L^SnK|J|_dw}f$ZzX1ZF+>K2cDNE= zy$aZ`^4lJ6x~=n@L$0M1{~vEI$0xk4{uQrK^6ui->`BH&zeZ%=fD2EeRiT)p15~o~ z4?H3_cmoE=kXr!-i8r7mBmZdAqA$ehxdsmOk;%Mvoz%2FX6?J5-f!N%`}leD>E}0- z&ApW)_<5^n2pvsE#_oyXx;R3X3i*5LjS^-DqE?YJDFH!#n5YaDMJZt>>k{tNs>)&T2y z&;H}hQ@iaic&6w4(6EL6)keHM0tbhIvBL^5?1Tn3MgQk8z8Gs^7|sGrwx9!oUC;Y3 zxE5}n7hLpPk;nY}9Zq_QTFl;oU2G>3vY;poi=)~{DE?RoALRLSi#M6ic%;n8ldZ;0 zGn3<=Sw-NEr0C_>Cp_Efs<~DAt?=~$?_S!h=@N>4aXRxYWBX?2VyhmV|FQ|ynd1Ja zAt&EC0XsZw3C_j+4iO>pf|wn7JW7=g!k7dyh!2qC#im{JVqZjCYe!=U< zt*vH*=+7%jp0fer+2zNZvphqJH|pOY7YF0beWKuCWMEp?LxUlWp^iKT!+TIuOAt?dK6 zFP^E8M(aU(IiMNR&ur`P3|=yeCo|hS@_SBgLephYpyzhg z(8I{YB0Q(NgKx(^Qn{So;@z^3H^035*PFNhyZL{Ayt&_edi(pY zn@@jy`0)G3&+m+q4m1w9NA%m@KfZefeFyZfZ~ndc^xfo<&-blf4sSW z_tTp{e);@*HA%o7;jh*c+^VH|Za&ysPfMJLviTq1|MsuVr+50=(tC8+`~UBq>2=^( zzQZ{PgrWA$AMbzr3+t=*KiqF<1bykuL&kTMe~<5%6JL9!u!Je#UI|;?@wHr{I=p6n zhOucLB=n!F>2b_~s2BUjXH1;bATI#>S+6hEG`T zhQ8mErS$0s-;6&qEKeUDZeMmgD8{(h))|v-dB=DqpJs=kbE`k!@os0&i*x)mWbR6b z;PW{h508%y1ts1BVNnX z!Ep72c6xuqhpUSn!Rs9QIqiLy1r(2aOtSsO43PuD?7Y9hJY+E&jtdNC*j}D#0+D^N z7dUfpa{ni$KPW--cX)w<&2SGl&NU3~G8oJe?}1-zwfN*}l8yTa36gx=oUAHwNB<%P z0M>T*=car3bF*vqZu;^Ns=s-pI|>nFxS{5XfJVp9vuXJ=oE-fP4nevN-uLbv+?3}f zm;NTabol(@m(Ci#$o_@~?&U#VIzKlUBrJ8lUhkF!ufp_lgRbA7MdJPX4%^7aQ7$d- z)*^L=na{V~A7O;MRyQJdv>ZI5B_}wx;XEqC#G+KiOn?k*sG6Rhvz$Z}^J(SHS@6C0m5e)pkG5fSnLvy*0g;c!IkUe55g z%2Os;P9lavSz$6~7!gDCiLh{}Zl zDZ@m=X(5mvu>EdNWspc5HdQEKuMRqiXdT9`N;Y?fC!L~H%D)?`{od$z@&(WZ_p|o zNMy)H#^bTn3*^`37Rv$SeDqz&vkWL|dq`d_Dqm)NPfdqGR?udD9%r1Zj0ljJO+iHS z$+VD{ENJ8eB@%P^<(b0EGNlC8Iud57wbZ@0eQd+d}}77Ii1h{9264=w)Hnbn368b57)S)a zKS7&g#jOOHPs9}BBXH!aL`;UZ-FpJ&d``B%!5J-j?&YgI=3z^t^GsWRMq;uTyYHf? zVD7(cH)t*fE;Ci;e7!>RK(m^!uyk1C(yE&C8H+)0#DHl(x)Mco5u@_?3E^q9K#!Ns zDo}{D~8g8YOYLB7}$!_v#B6HHe0Fke8yS(C{5< zTw|?-ISE9`!EO$v%pPB71wNr|5~Us*H*YixKlTm^>KXizNbH&Uthb>M_pVyikV1v1P2$HJFGS zR!i!KQGlNibIWZVWUl4zMc}x)Y!@&(&PW|%ODl$faymK2oVZT&!QnxWh!!g~M5ej2 zMV(=RTzOW6d@7) zOm{1~6qQvHp7Ui_t;@YvT>~seSF+n z<8ICiXYkygA+;~LXLP<4d3HHG_z~t?5O7XCi@SEWIsbTS57zvg^O7?Zp3S+z1VCWA ztDC4zh)KU(w$C((a;dRYjVZX6fpL2|iIUKpFiGGx&%cVgB+3h?Y7dSoxT!DRI-T=A zU63}q3Y%Ml3bP=&QHdICkTBNrU8Du=<1_vA!%?u*aMtkA~S9b*BWW`zxHNl9k zDz*0*5jcWjZDiib417LZ_B*`1Y=?lfElJH@g^MkBT#1~A*^)ggy_`2{(W&PcLh)PL0zl-G0HD>-;Kk7u1l`NgO;z4p#ooi(98Q}p&WOT8dbYq;G%1erzB?D>VGBv6 z3O~OewM>_VxPF+0RBNn{n^wG-#DhyVoZG{`bLJSi7Et5v2&EIY==R`jQ9j6~-?8l; zrCHwC=Ywnt`E!rSk!{F)jSai@F!|6qI*SKf2oab$LNuTR0eVB1hp0X*1FCNi=bg+)D7kLOAaRM- zw?7oD&MhR1ewI#mCw+4U7sl2~OvY?lRGoOO(IF>`G7|D7AV+KbDJvNA#bviD=Eg^r z6sYnwlMQ5e9EL+6wjW8xqUm{~RDEu64#U#&0G#nO_s4qE66da;tyK~#hg8T5^S}Ucvz}*hFA|!H-8DT)dcw?n7u$ z3@P0e==yY`S`d)zd))*EsF4c`@)z~8!oKcltMfRuShgxc$Wd^^zOp$r_V9PX*Rr** zMxe$O=1A3uhAdgrQpqIWPBI)u2xhl!$#j%jW5L|{9B33>BPfr+!=>M0yag5KugeV5 zOZ!fS_ym*eY)>fg^zSIZD0ZpVHjq)=zhE9^VI7O9=uI4A$x=#YvSPg4f}G;oHHyy8 zQ1Nuu(0PJj=h-&!CViR(1_4%=!wj+v!76Np2$9O>ftN{#7IThB=ax6f#SHRIyKk6H zk8@g7)Uvk(WF9WO)A-dojyfUc`W)qw=u2@m% zM485FD}_=o$TiT*k(f;Xl8G!&2%@WD9MWlFi@@18(n}FCiDerQwHzmKPWf7pVkQTx z2rZyRt@Bwc`k{H;ZN42&ZpF_bCu;>1&=8mjdb@p)Fpj?vhd}MgH578Q49h~k{UqOfmcv- zFTqJJn!qJ@w*z7C&moT&YZb|JhDk@VNQfVPA<14c3^5VYMin;!E199#_@j z6`@m9^ejXpF+3X;feq<$wc=BHv|0m^n-M~{eF-KZaDF?^!NV`yc2oY9PI{)B0dM%=n zPGM0c)QiKz&V#(tVMm3JbRu3YTt-L^BppmlXfYdNs6WsAc--V(F~uW&VoldG6GJ-r zEVjv{gQ>T)`` zF63i)y~M@m9s{zYVcg+vX4pJ9i0pWi?_WN5PdGq$X_0S#kB3}%bs$))9wJf+km0j5tJ`|5 zrWL8=aX86#^%m1Ng;B=k+41Rw&`XHxvUzl3`yvktH1|nGQ0%#)(oY7HI0Qq(Tu!2; zaYk3Zi{L>eD99=hao;%z1Sw-dTw&tL^T=mF9S(v7hYS=XX|d9OZb^0G#>=4?5s!p) zDP^h^bzqXH1h7oba?8n+E*-26+hKOu#(;NEK@8yrV|S$&dvWri#Se`hgVu0^6k?Th z<&?UbtOrsxiXq-C;DJwb*&{I4Rd0|<($0v&F_MeIS@eT&lA=XU`*{yqDJ?dIVA3poseYRG|*{L%_In|xOJa^@2urZzHV z3uO*sD+|Q{Bh$Ev^46eh&LE`MAYR+MOegS=$Ta#qH z)Cnb94xgxE``x z;F4F&ajGu&w2_ait_YyEMZIHg}Nl2LyKf$U&$5KYetu)9CVIE=7o(9>|i4kg3Z?{9Qw}A+QcFC-}U0wc1^bS@MkxP6llu ztjXjS`H;|MF8;3}1z($hYoKrodpQ;JE(2oQawa+6uICU?;~XBaV3w{}kz$fqp13F= zM1K9PCWs6hnpf->lCgtzV(@%i$BJ47?FKeB`or#lOR}eoG7zzya%tVRZ5F*->T51x zL}2NScbVuu&HxTI4y7WnPSJynn_e@~odyKzQjADml0FOpI&!&w>;X<5S8LIo6hJQ zT9zW2zMnDH5GZ~SNoUsIXk^sG6MW~Sfh@T(aH)`eP0R_=hTFe3U$ffJMK*=gbcwxZ z1j%LBAnG^sZ{Z%%kfFqpj<@}O(Gj(8Ydg)B9$D*P6$3C z5LRr=CcCA!e4J%H=^=+LNB4hSPbiX)? ze2^N>QdwyMaFj!sb$Nl4LK`8A%uJMpJTnnC0(kl}|0=dx{0h~X7aLrKMb-#tjJp#W z00&duh*cS34k2!G$%xwZ6ZBTF(u6 zSZqf)tumGb9JVlZxCcT=wl2u$gk$Ez;gP;9oAXNSH2Ege|IlWKn@bWph4lGWvJx8& zWr~Led@cDX@HwV?HCDi)^QAojqd2vEc z$6Rwol}RAk^i|d-B6RVU1gx?)Nj>upAzu@BM!BevpRFv{B2UOAAa}z`2$doXd@g+z z5mL=vLB6p9>PUL2S!6vV=Xhv#aYwq75$8CGA1HiALQB?07BtnJCMvx10V*~I*d4XG!uwsagyZYMH;6erva6B zA(@;!s?abJISsI3&vlg&5u}x3z{Svu`5E1^6FD?=!WC;9j|7dS{vAV=JWux=YX#Rv zoZL>qSiLeYl-)_j8)0Z?oacXm0dgz_|19~IU6`u|n@1@v$!Kh$qJMffSr3_&!)%u; z&yxz`P;5TH%JmdJ=ldETaE;~EOxzwKE1*Q1LUvBo>c#b=!jVcMTt_zYXxWRnlH>~W zw>lBO5hoe^q=;4J9U=R+!g9G4Sp}NL{e|;PMY>lSwxR%~kiYn%EdF4-a4SksisQ4a zIZT9AfQjQJ%SfbEKy;6%LLMTl0^$N!a9)ats`jPt165+-+UJr?P|#UDD0@K*9VG%w zk#N;*zf%*<&JC7Zky#3Z5$eKkabKB>ML!osn$kNdAUp3^lG!Ou?)qP8oiuSFmU78= z1cv0L6MY2nusax;t2gZmG2Bb?Jke<1pYRsQ`9i1J*DsBo2s_B60fTF|r^9)RSqyI+ z-blBhd&`|jC5E8|b60Y9*+CM81w$)L+@SuV%2GL?f`qAnRwRM)IpNut{SI_na5EoF zxvSe$5QQwSphXtR3KF+A+&S1L?YdpviuNQibZ9bt)tE+%fpR5_`h?3cV7wxIy zS6PuH3IVK>TlI0{K=TXq9V=s2w<3j(>#G=%Lt0F}kFEHKrj`>pmck8mSCZ02p``05 zkwkrWfdS_keO41um-Hjojq5JbM!_2*X)!@~GjI>al5Yr|aRdu1GDroeMo3)+N2?5P z@H`{S^AFrv7b?E24vlwhpz|4C17{o z_{>g5&b=bf)BuebHaBzTu9Rp>mM%<=Qrz}x3ego7?$dF9e56?cmY_>x{ac6`92dCa z2K}l46+tGYDwO6`KC0(e5xzclztU1LHJB4 z8Nk(VXBrvP?lS5ak7X`5`wvR(y>!g}|E#FOs)UR(z5( zmKAceR0=C{Nkz=agMh&V1FG#^a!N(0kgDLSAKp%O+XxAzB98uOU#3W%IDK3(B6fzJ z4bPvmH4+IlAjP$bK|E@v$Z`G|z!@2?iD zNI?}dReu_15DxQyEnhk?iXyA`R4jVNo$i1NscK@(NxI#sMwEP+Cn(5px495EXGH6lx1; z7i+5n`v^iT+or%|6>6!9ExkhCExm#!hwMZ~>u2#yXDlZxl3Lx5vgEa9limu)FAy>G zMMw`}|A9VS>Qho&kvm3l2znvk4NhXcd?L|R$aIip5|dNRQ^cZFK-Km-Qv6{<*AKO* zh_aPfp945Vm6gY7CGM~RnoD@)zZ$;7JjI(cxK_fR&)wE5Qeh!3{|J{fyiDTU$MK>g zUs#b1i-Xys*S)qtA}sw@SXZRJ((FUfie6T>bBdf-3StRGLxfyc3W2u{wS*HETmmi| z5ru3D)-POoMT`hUVi=~PmtsjCeEN@Dk4kw(pjMn5X|6aCgy0efChryUTTzD^z9N1B zo-rpN_~>LkVAs!lx%6IUDE9+0NI-h+JJ*#;T*XYFe1l6IVXWf@?1om!s|e-dsl!At zDXhemwQ5Bc5m1_GN?gUSG>NNq5z#Z$#SXqQSMl3bMNQeO2=i*1@>cQ7uqi@U+h-lz zipKRqzid7@t>{?tF`Ny4=M5NVhmoqPzY!E=4LH4C+$fT&Dj;M}kXeL7y~?{#t4^ef zzrD?N~Y`~^OjrSis3h>SdrLq!OCg4e96?;;i7h^uI zB#}3g0oAzB_@d0{Meqo;=s6&x6=JEI(<5jv_1MB45MVE+ic$M0DiE3Az`w@IWW;={ zErblL4~0;^6(A5ZBZT4)@iT>|n3t=qxTctwm{;7)ahuupPN1~X@|an6p>Gvvtq56* zxGg}${*zj-#U{qxKlN&W5YTVS%5EiR5M;%3^%2<s6gaJb z8!@&-a$4O>j}MnY33M0{J}3-Vw@1lc3btF3)=CBkNOo6|RdGdfs}Th%z6uKRNNc91 z!tQMEcCP}n722kvSX!v2w5va)lIA3*0EGrZCMiHC{sv`Ls|D$#Sa$doB(CZXxpE|> z5vE@2KyZZyuso?gsGG67IT&P9QhhShGEfOxKDAnh6;~AJMWzX_mF((__mCu5#qW=6 zKBWvd6uu9J5wBl^pamXDt6r>c#gr4#du*HR8vR=7t{x6dU#N&0YvUbXcPzXntrc8{ zkKf4C65?x)Yu8XQ&m6|RehOutEfOJ2B$o=dew&d}ThYhN>m2g>1?}tcikrXXyq<_6 z#QgG>k>v^+Y}YZiP=xIZwtrWk$R*ukiA1EK14LiBd`Gw#E<-ttILXz|k?+H6(pbTG z#jTm6G{&lYYjRf&0lqBagz4v_$<|6+MIlVcz`je?hSJhsPt^4dDD(zAB!SqgY&Tjf za}^xaQfhW!w4RDI;zySI1p~rZ7V_iR<||>;`d14)Dl63RQCt-=E{ zPv*~%MfMr`kfsii2O2vehOn!zNFc4t^(c$A(o+%2Ihkhc22q)*2n=Ifn2*X$B~mXq zv3RWx_r2h4L`qvp<*8zZoV*$z#$N@=H4~8}Wk2IMl^uNehU+3OLlH(qp&1Bil$TJ; z(tb^*D(y>>vle%RHA$)hrna}NXCj%Nc94LWLasidFjbs`YvrAilwx;#RcbvGu_OSw zkapa+QzB)BxspUWjd^7{BAf(VfdbIoEo4?)TFNms=2lqmccM25A{?jW=N#)hG0;Mp z&TX+KgB0GcV!xI07rRWp=0fjIWKjo0s(CqKE5lQ9ILTlfS{8-9CT$c1lf#xuR42Iu z7CKV>;{m5*>ra%*ovPk)lQ7R-d8LN>R^=@jrIg?~K<#vxLxF-*FO)SXT48PHFOROt z9~JRIC%FO_DK=!E571mJJ4)0&qC#YJc$6meE&qA(Gl8^GP&arp8)nsRZ(oR5&&9+W zGV82Dy;g##;j8k;Z0I_69U>;)0JAls2hpOH7m7za8!`d0fz{*Nb;1zE*2*nd9E9h= z3u`dN!%P~Y8theuE^Cu`fT69On8ah?2hD7AelqKya=e}2c5cF}fgvObw= zg&5jW#YEVJ%vN{P4d}UCPl$+15bjmQH94N7w6L&|i_1Y=+URd&QpKnNm}0*C<*fl_ zbUN=u1#%HLYvpm`$KmQAP7U%)8tauVzsXcp$YR+=W}18P}Y9_K| zMC9vVX_Hz6Y6QwB2~nk1SoTW>`B&iXJ zQ7UDLy@ov4oq)OOiojrXq0VRB(ulAKUx)1TD{<>a)FFp+1&JmENN#pDqU+$3_eQNr zkR;8Mvh$R!C^kDV$(M;eZh^Eum!Ou+pQy`_p`!0RAT#u|m1WAYSVeqMKCa}8zcx-RP6ofu%D@WDVp@vt@BapJlYNi#s@&0Nqt zxN#+M(pby!LQ^3#F~>q?;po|R8@ZCkoHV{d&(IxAUD!G+!<~dY7-LPkq%pUAQIRHT z{9K$ky-1y*!XUja_L1@<(a(yZ=7le@!LjLOq}S^nQs{nIP8IW7sgXjV`FdAcr0`i> z3gM1fK2MbzDL^UrfOH)x5(&?&uy+Q3BU}`2fXR#$RCFEQBghytAxeTIZ0Phc2ybG1 zc?uSnEqAM~Y~+$3?X1a;6iO?XBO)t&f)?`(?g8)J+ltGG7PRs@oa>`j2ZM^;ZK#&+ZY#D)i62e6c)s*q(y z)e>zFB{K!8cSMIc)kdk5h<#{DeXo8-sMUA_%WbqfaTXDpPraQuhxlzgxn+0Av-Svr zJ$MC%#7aG)rq7!{@zM;AWSkAV6M@C=;if*tEq>a$LMA26MXs5mY@Rf3pPb8mpi+W~ zsW+(d&2T5?70VlpcwpdSrh()O6;UO}*GG8xsSxIS6{0CvwW7AzC}q-c-@9rkH)Kx= zF(g6j^tZbkvL_9ZD4{Z0bike$d zkc6C>TA#aV+uVwBB%gG^Z6w5`v5c_yx#9>wG;(`?{qT%HP37ixTDB2f7%{erXW!7Qd zOhs;K76{V=g^2Gi*j;#)sqipzL++%okqMM7)crHlWt*ueS~8tR>{p(CI;FRn zin1jbSd40Yv6bGYXr>~cSrDtfJ8< z`;uU`|A9ZGcJsfvi`v*YusVrLH57>#Yse0*&3KYC4boO4Bk^D_tUX-DdvHfs4N02P zK9ZKBlL|75G)*DfuMn138YUvo*0s3e?!?TDz_4?5$D@-QO4t;EY<&S0!z-mjRqpvo zr+iH@FaK~tqZCbHRyVa3{l?|dAxkBNa~c% zI7ODH9QmE@Rl`u&1A-EK>~5pXPSRzH;tTTynCNi=t*qtTPRyx#6kpyqB)TYZ+kaN`6Stu zk3Je@dlCdQDNG|}d{Mm#V*(d|QsZOu{yTYLTn769i~Z}c0>gx2)9Eq~_U`5iEq zvwGNY-C5px;MB&mK_z!Yqcl;7p1b2ylsrbs@9;Dvi-K2!AkH6lgBWOd@uk4U+mJab zz(bzTppYtv_N6-8n6HGsdPV_o){i+AUvX(jA~h7%91+oU!VReZ!^PXeP?1zBX+_~B zmxi2D4&ox18Zt`-2>aC?e>P;5GN1hmHVrwY0^+fOaSBNxj(SnMzVY1vJD6mbq6-9L z_ybmLaPs$HL37XjG%Iziu{-JFe0dgW3&G$rRM#3|eAGkbE9xNh-%h(C?^LKPx)#}z zhl{sU{fs2}R0g!=Dh1Uzi{!pWt_~^@m7pFGu~4(C*6L_uBV-s5m55rEUejsT>Tx5K zvv&wPDrrM&)&1nux?r2HMf;PwvCzs(GYxsF`sAjh>jqK%Bon=6aKtM#4kEgqK*Tcv zxd`G1dn(hArYiLK0J6C#N0sElb{F3HX_TdEgwz^DeyU;0VoY)imJC$^fC#W2#PhBy z>Rua$0A3r7i3AgX^RhLK$}U3VQ|X1(+=%)no-CP3FEpxLsZngH9K?YI9Ik_R+D&y* zR)sdm&|^gudc8?R76b8#vZa!>YUqnY-(Y&W^`~T2(&?Nrj) zz$u#uNsO2mlBBc>StdYix^|GrX%%4TkLr_cI5-U1LAwE=y}aKGoxXHF({w7jpb~J8 zmkC~eFR~%K6-rN$7CX=m9VxBC&mhGbXa zGY*rsAJSlXaA5$H3@e@b>|r;5lnra_*U%-U5!k6D z<4B1^!Ci}MNQ4z?h0r!^077FMA4YLxvdsBkF5ByN@VO2;+NyH%bmZ7GjQvx_V3 zEp)DXwYk8pB*AEKZH7aAG4@@7BB8>0J~peuvx@C)luq38YA6UO@CeBxXhI5~Dd{cg zt_%d-U%FJ;lJd#`MIyMGi;yC#*;9M?p2r`xN_sT{eCQGaT{smbadsl#PDCilM>s}o z$>g}YzZo;$K8s(`VIYbU3|25KG(^+Up+M4RhlWThp<%|@Lzs2~fs`P*h6u{^$#w#% zgz#;4O|}`fB)95so{uk6v2A1(<_oJOyH$W;z4)2lDzBB~QeRFjiLH{d`~#b~Y&_Nl zlvB-jUXDHWxW0~f(egIdlHDp~DH9{RZq$<0s=t9W0vVWULMYeD&{oN;r21ny+1;B~ z`K$!h_B;AbJXIVbqQ>eJTaay3?Nrn%$&}1;EFPDtm@^iaof=ANg~tmxSOmA^MZ!i? zsD}t90YXIRJ#U6b7L6N+9eBUlJ** z93(49gw$A>T)MX;s1ljX0TSTz*IYO%NtJvq`h{Ahs1krE_tr9OXQDpIXWBLu6QNax zDuJ;MXKshQx?~4-Cfbvrc&({D?RyZ`gR)ivcM8i*xvL~WVc%($v`Rh~HtAM5tK@S< z#cau16-J^sk9C)@szysLD@lZNM?AxZ1~jMcp?VJzt8- zex+l|r@gBgiCy zvMVAOcL0Dv_tc;b*pTUOw>aglIzE8n@%!d4G7S|l!NQZg5bus|^X{#7psNBC80U76 z8$J8;sd4lNtjpIsC@dYg8w`qTp>}iaD%8U7^ROA z`D0L8GnbA|QUP>y!L(R&`N2^3rBP?^qw@Nal#C?o@^d4%k?TnSGoOHCft})bGAi3tdRu7aA z4zd(fs8Es!r}M+%n{WeV?vHjIbtm$CR^*p--B_b5 z1}bT)`Wu}gnQlpzDqk-4bI8_$HrI+M6ODj&2s+Cf`lPJVQ&}E6=B0`2Y0MyxDQZbs zWepw5Gcf!eEpbe%q*b;PxS6>#qgMH=EG}_l+Ig!qR#mV!?Ud813ioE7Qd(8v&a~{J zQ8ZuKeu71xmNfye!GQLj-Ftl&O{ zGyh0uktA^=Ad)Y+Bvs5!*|0)xhn~-fV(e{BN`<94QTmlL60bX}&s;_|u%sFHVm%Jf zB0^*$C!zSP#gqWYX}MrA6BP&3*smjH!LoxSv%4mAlp4z>U|nmjuRdCgvdLsGn3ZCeP*dne_o;&5|w z6_1AH*mE43_H@8gbq*Wt%ZNSl@EiP;4jN5N0}Ujj3>tL_qGC8E;FAS@9gu#@w~WHM z;4I%3G($p*HpW&W$?!771LALJ6=S;D8j_0Zo(g;IyJ&&q_gtggX@JFAF7j|T<3f)s zLWCqPgz>5mZ=;rV@W8*NFzwOu6$b1Uvi~Sjlx;kjUhsdDiVORc;=lpy93wkb1H)%T zOk}ZII{Q+(|3$>B<^rP2om~V3(B$ zFZn9CJnE<;K^GWaSQ=Myc6ip2qALL6rtOLm`6PMNTcxoQI1NDzAzm4El)D`Xxo~n4 znWi$%jxKU?)$X@-k&a6tg_le^(sA`SUvYJ*Vor8bc=hovI}ju9g>~#g!DOcpkx9pH zD#j?ku|pree#1#2l{Bw)cT)%>&4j>jU5kejoddH|60RXL+$b+VAc|x~^e7~+jxulQ zw_vV9UKAd8=#+NLB5@#A#)^q*vC4L2J+R$O1W|l`oW#p^_9)@j@#TbLh{x&Xai)V)*{`8RDU0Z$YSSrKmqFFEnvRrRN%t2NnvTR>38bvc zfEB=GR8wO*?oOnb;PNGMC!rHpE{1k@BE7C$4DFPk zDbRu@H7AL!_crO(E5o4tUP`DSG zVG8#`JLTyb$HiTA?3A2K>q-(II9PK3NVgCxR8Zm&(`!gC@@AYhJ~}1l8q32efgR_C zNV@SmFEv%JkancxO0Ve14qmp(qhF{D_tlBYe`-~j%2Q1EyXn_g`))&?Chjm@mz|JZoEY1ZtQmC@{+fqxL~kn z+k2@UUi-vnDTPJ&d#N2>b{xKo&t^Opf~(4fJ9bLurFr@1MLH$(iXEO^Iqj6jD>Ote zCtkly+9|D9%#+hxX9nFp&KOyV__JVkYym0qFCuAlWtWIMrTQ|}%`cO7D={pkPcVE} zE?ss?`V~v%aMT+kLa+qRCz6ta4MWU#HmbKtJ5q$D6Iq&GyxP~1F)Yj+RimKVAZY0E zut$&jtB##gh=tx_(>Uo>7+SHRRAN$Fop4v~T6W;YGu6GCv6G3Jh2JTwnC;Y}@BnQR ziXE36O0CA1bZZfVF;i)jYjTu>A~8U!GHl@p%9SO*WytK^X?aYomWCM;1v3fld97{1rB5U_EyGG1ZtziPpWgtn)hO&bq zlHJgRM2Qs~j2AY!w0R*+e9UmfR_WW+{hdo(X z;MSinbm3TT5g63c@)l0z7J*4Eg@yNTVrWuoZW)qpO~*{^fEXS?qtp(YCb$HX*-_U;kqqSZ5yw3fB=PQ(BfGpTCSR*ywe*=)heX(wiPCcb=#xggh@t8IE}Xa^R_ z-Uh&4$&m1E0PeL6Ip7B1PR)=3?n>0CjO|Q!Dg&GuIbwHpgj%j7Vgl%bLEK*B3n$47Qo;cy5kn}3%MbcjOCR%#6WV5nqyMg=5lRo~Y`;U8rw`0+$PMQNxQ35$iWrQ84Ev}umJKt2IXFHTe z0A&$=jS|U)?!{GJ7sA;nlbmjz#Mf-Pu2BLx{R|!C$P*iq$7wT3U?QV8vsjHriQ|;> z!`(4$lq#;q`4K+XI6oRCk;4>zm-C}h4mlVhM|I8*1eM4rCWs1$bs6CNXp~bfm09Ne zXhy0mCG&R}I~rw>6QQE8>m(12(#UoBI?7)8f{h|`IMiKmo(8E=9=Vt;{XcH4 zXp}-uAZ(oimq;^KCD=6}A->1Qs`vR)t2%;|f6EEXp0{fw-AuGgjg_5A&q` zjS|QeV7`3HBv$}tgF645A)oP4g`MsAK&r^!eyUJ-4SM6Px~+$dw5 zWTF}o_F_gO(kNA2`@M0dtXcYvM&N^)5u z6~aTHabR&Mhz*Cyu`9h=(J1kov_>c&BaGy+Zc8Q7koK+Nt?whHn859ts)w{8`&$Yw z*c+#bszNd8l?HzR3Sqm8yl+7gO-%554e`x1q<^cyo#0-H1;I7GnT9-YK`829Wj$$5 zWlQX$bV?iR=2W)Bc!O0mQrF;e@a^nJGQlD5Ku{tRs7z(jkP*%y%UmW6nc;dQxArxW z8rmlVPah4}?OlP!qETKr86ROvw2Q3`|7gy7od-*Fg&P7}5NZ1v8j0U9(ir~f$Pt>r z`KG%Vl+E^Qpm+-nrmmx6A4H0`BdwGkv|C}3}wG|<@|s5g=84dz=vxnv*Z%G@iu1X%E6G)nd+{Y#x$rtxSZ(HkOr z3X^`)E4m>HttODW;l*CWl!^w^n8f9>aYKsiZdZI`fXMga{~8j#MYIeqk!#z8Ja1rz z;2n5wd5W)d;x%EHNVcNO*$xr1&*Xn^f4X?VM%muvv`0_LoOmZtwxWVgo)OLSmGcxL z`0|0ju-7Qz8$*G{F!JwRP{HjGj zAaQ<<(kSH{QB=mYi!zN8zX_?qFsDH?6N8NcBgKF>h?M%xK)f-etZxS5hR96zmmNaY zuI+OrdP@;mE@GLb2$zX+yb&OK{q;ufR8GI+q#C^4SZ-{N%J`PSvEH-=u=v_KFdM7F zDACMChY=~ZbUrTUvcHUgf~ruGH$m)P^|dfpbWvRd)QvqQcq3-WDprCwK~-7c8l`t5 z=&#rz%JfDwy8)_@kt1mR*?jekc-Z2Cq_l5LLj_y&yEe-H)`^BMFsMD-I?DXkq2j>K z$VVDweoKKqFtt(EH$wU?x64hfwC*aR`zSUUlh@ZXrFJ8R>zy}%mClV{v$l6xMHN-# zLTDCptlMRyuJAlsq){Tbj35B)kuR8*ujlP^$j28G)mNlt59O> z)otvKtu0(Iw+*3GZ8ELo{;RwG-!#G3MOUDU`DvQ4NyN9GQ@P~rWQW~he|I~(PmRMN znwacMIy;ex^N_T`R(j{|dec#|4c}dFI##w=BwON+j!kOR9VR)5gq_fn+f^D%3m$;X zRaeKwmLQ8aUYd@MEddssvmF^5P;|1HXry{S9~v|K$u`@1HMGOj70;WM44sM-$noM`|}Ft zN%@VKR%zEnJYPs;NU#;5ylWKEP0hR?NWTU%ADCiCwo1VkdZBPfs~y3}Q2}&bWc@sX ziKCxa;7)UORE{+%;0)m>*?4Tpt)>%zF((p8n;S{3lB{VNyWp%6OuSIct$HV|Ri-t; zY**jD6|V-jBwEutsj`x>WSgE`YoS#N^Q;tr?dz?U0#`^G@;d9N(wiB<jeFOkRpK;JhD5iZKhu{xoxP+SYRo)h3mtkYwva*P14-%BB1kLa zHfF67su@rzKiQrUrB5~1nuM%s+7ra({eC7}G|Kt@DrCEvg`<3GL?}R-l@URNueZ+0)E zJ;OFFMFa!aZ?W66Xh*4}Lh+SqjitilDu}6?y(saRx9=xWmqnax28vakM4hIN<#w-< z%k@?6H5Nh85IcyDnyr(KhhI@_I~XRtdh4o{jVqF|X)CfOd|Rd@NOp8-;fg3xaIs~m zl)^Sh_LVF5^jbR+Wf$?>T@2%+-ivASnM77L9f`ZQ?RSKUIdhLxY{~bhIlP6o)s%ou z1_LF0TVr39x2^K8MO3~jkbg}ZRtf^u=N_?bNx+sAr8HHQfQ`AxE|HR2So++?xI)W5 zPiPH78|pP_*?KytZ27R|hD_KPt8Y)`3aV!8KWmw3tv3(fJ;x}rUx!czD!}vVBheH2Rz-1KNIH^ zy+aKS{qX|wcaY!v`&cdM+|YNjyFeu%YWpXQkjzFTp_}$`T1LM^C8t}~FfXtvEA1l= zsyH3B8t6(em$(KO8%`KS)hGVitYAgsHr@*=UJ6oZmCY^64uh#6TN&K?J&0K6@x1)A5 zaC5G4-P7MeK)7zF-=#_rp?n}XL3a6xMx-FKsDOVao6Jhq#=6NGh$NNf6qVTJ+sQ3s zm9mX8H`v8ZWp?1QPv$vM+BU(_0SxvfsS@$FK8+|v0;R(vs!~>^Z^K;~Rtez3$V2oJ zv<_Mum$IJMSX;mo;(G*PB*26gq?qhwx>mW{STH$)00w&&c`Cl>u2kl>ZgNqitWlu# zn7z8!lv`zO(~0&D(yBy&F^Ev}Y*;938%v8%D~txr^cOU{f5{o#V4dcs9FC)MM=MdA zRDZL;`%9s@12`LNx(-C zD7A~HwQcUDXu_DYUZ?=np`v-%RU+?TC1;cR{@MP7!h~N7e#!NIwe$syT2UaddMi!1 z6sMhlI8vWvMd0y-D_uMmcpnkuDXWvFmN<=S7=ZU&E3lZd#DON`51;JQU4Hp;8Jz z0hWhxODx~DY|Gi|8G6>$C)$XDtgq+G!|2@W3r4t;7dOVeylikSN#>Tcwoa&lF@Xui z?vae4+-RMOcx)mVUS^9xHn)J4wiaYTH!aEQ7RE*OxMZosZW_L*naa4u@qx3qmgIN~ z`LbuUQboOui*Guo>2Kn+%E#H^xMivx^54gcNeq{qPf?AJCh1nUY(Q0}lH~7KH3Ihz!=#t<1z( zj(fvT6|p2JC}P}@?3zkyDL9U-v29UO!fA_Im_zA4n1!oq^d*Vla#3!bU6Ks0IS9q6 zZ7i2c2FHODTEwfCqo~ySa_0(2$=`y}*aM60Wi?vJR&<8IX`y)=YN=dsaePanZ0acm z9Gy^F>xQ-wyS8MNVu9~kkuESvQ7Z0nbO45u;vg6%l|O}9Dg|7?VAEArK_q|EIkQ}C zhH`~gQ=C@CN1o!MZR}kho8r8N&a{GlRQ9*fGcS0IJG}B!DPJ`6vJ`fpk_Ar3pvVkp z=jB)`9bD|TO7`&@JSS(*Yp3y!Vwbj=#s5 zrM~tDr`4?Fjaw>bT$oQO+LcW#zSaCi;*-ksxJ%N-X~E=$!MtEf6oFgpaTH7CgzHe7w>pj1Qpw>usDC?isZ4O)XO@qvy3bCwth{wWK(Rm)PtqYu zBE)QyiyI`>rR#~W%()E=N#CyU+9M`NiF$WQsyF?vLblFcoxoh{zh`5Cgl_toeGL!4 zOw^~<+Q-@~N#|ye^8RKK2~*7?XIRm52ltXhZVCiB-;BXYSgF_&6m16$soVmT{Q1+B zu8yY*D+ zOOmhk?IH8jNf2!ma#?S%@y2z2nen@#aZJv&ST9-TVS{bUP}xof(KH*juJmMGi?Rs# zVWTyd0*tw1rBWJxNu0Dw;&Pv(O2 z7>_*SWfogWrB9Rm9IjSC(MOViexll?nWa*vEw_P%sU}Ugl0dCq<&p6n_EN9*&}d1r zv``ny%Wam0>>t_wuqC(q>@&>u;zAPt7RYR)&0HQL5yf*N04E0aMw4 zD1De@%35?vN6Ls*h@Ivva%B*UDQKV4nN~vfx(U5P7eNPhH4&4kB(bn>C5Kq3Qdigz zRfGG_A&Mb$SuA^%RjFkur|1U576rz2BsX-WH~YLoAIKK3+Xu?nTw$i!*@S!cu&7Y4 zuwC?*r|}&IRsVefzv#AQFQtV6O168Q6ryxRXx2ZB7Y9h$@pVWYU=V57Q^4YeFY5Aa zZ3aq=tmmHy2MM&hyXW=ZoZtW?rj~*j?la@vxVcq0K#5*cl*}s#a&;8NrU<;Ij7vuO zQ!bDrs!dUy7oCL?H`U7C`~ezj?&?6%9H@dsGQ29j&%mS;YYv{sxd5gYpTRy5yrL90mNw)0Wacjau9J_MF@KM89^KlBRjx|`GGem ztc8aviS-pBud~;+T=RE=#QKUT)~iq}F}@;5X*#VOr2>nP{=%q(iO1f;@*%8*U_FZG zj8F3|slgDc@ATW`P|3j}oMVtni7>2Tv*Su2F8m1A-GSH*G|3%SoK+E-r*ekVpv-mfGPyE@2`(kNCa zX~fD<`}X+ckW6AghPjqMK?H=Tybi&ovhQA(tEjw)G-Cj_Kiu3p5*sc@=gvx$K|Ea% zTXJ^+BxW5|-rU2cNcw!EM%8SvK&!he47OB4ttai zl-P8L%xhn<#f)f<^veEWa;R)$T^c@eO9!v_KLe*nmtOY3-UMl6``JaOg$Iw{4{)WEm9t+&aP`RE$D{nGkDP;s~dhOyK z$plRLFo*}zsO{HvbD;sC7Mp|ZQ@eZHVJhU7m~xf{K+OnKsqy_C!irlLI1 zfu%&=>DWBG^_l!(5uGr7=COF^K!8FwMQkc-Qfwh7K<_lgz# zSlrLz=v_7S#o=5wvS|*xC!7|CU1RoUJ(>Y35m!lcjU#1%%U=!;{x@4;DH#|?N=z=tU4%>H(XIJt=Y;wwmENhxzl!XsN7@vbqfc@mbbd0RLk23|0+}PqQC?zB7cCc`QKlF{!L$)uvU-v5>k{5Y=>Akb$ z`1VW!!^(V8?y-O>6mBk|X5au=7yDB=#^%q6vYM}OXESdcB^m1xa&l1*(4HJB=UAVh zR46gOc0X22>=yFbr-Wm>7g%I^8g1y=kb@Xj+ZhUrAyz1|+Ew9uDFe}0QGghX;iZql zHb5!GQuF{;lvRf%V|};=P|_JnFSeOIzqETC8WswwykbwYIYQ{SWrXZ7X&r-U3nWn` zA*z}t&Hil!zQ4S^7ov+c1dPjoVcnz5Vo3j5PA#Tbb$XFb4AGjiEv5t(qeA%_@s)v5 z4g3%7uVNjQRP1}Zdqvptdn<}kjdkF>J(OY$Qm}uU{+QWw>&c;Vi_O>HFg$Tq{-=&n zt4NhsEWu>QxsW3#mI;QsBey4%W$a~!Gxq*0tl&RY#xce505LmaM2@O~L{#bMa{`Aa zR~Z#j=m}*U)6YYWVPzZ(V!E4|`9xL5F@+V+7EmDB{Uwc*?qQJ%Vynn)3;(lR{ zpCCfCjpl+N&^>|;WR{aT)b^T^jRoZD+bcrI(*zDIT1dei@q<6;L*}B3EUHiiaeG-* zq5sH0hFz0{*5GC;M?}ck7EKArUa+Tm-7noIF_He;zLeJQrPxODQ97Rs_lphPoE$3S zSPydYMcMRU)f@Es-KlWp9Lr(nPMRp595y<1II(_~&Hf1QVY@9E*~SoV1|z%EpoU5d z6%xNa0SsHjin3$Wgvnq;zA+V&U>rolP9a9kZv{Gx?Eje6dbt#T*4~`RAv{dOOo0%Z zqP@LsiSM%GqXf-`cepxXE-&>-MI?F@@qL=DL#jWj%hO*AL#FD70sspQqqpAX1(5om zIw@rg6p%KfY#=oWoEhQl=NC80#fB&I*p&=TF&ncu36xq~>=c+s zQQ)ii)w}E%MU_`uGz%fvwUt{Ykd1{x`-ku*qrf)CGHAbdl_B)m#_0ep{v;`}LE8oo z9AuEGBPuqPwvz+v`625-2h1>5^iS6$5gLl>hFYK{3pJ16*j=s|;*_e1c35$=DmCz+lvv!9@c#sxg*IvbIy ztP=&plwQ{|E-EXI@7ePTX9>kYa};$ITy;-_<0~+ip_U2M)ikQBn5_duRYd^hxbIO- z?cOkhMaRzeO}mL@v-m)0#sNxoEI@~lAPSBL#K^!47OLn#;)o(h!~~Ur-j>0ExP&+B z!W;!IvqFMWuLW8&pjDR$^&=z5uu-y3l2M>G^H3LVG8vKEOb0?DatHt7!nG**%`zYw z-!(wgcG3X4!d3jYBQl+-NY`4`1W^@Y<&`E~z2ZtRPTxE2fWFx~p0f$=D0ca{WVY<2 zL+grz%gI6YJ51D#Qe++x71sqaqV=+fb#i3W7oHNsc(F|DD6(z}Wtqgr(%ZQR2@qcr zTzdG5^!5cLq?Zk$2=?aGh@@zZtXVq=E<(hND_m!biGQ}Z#FGup$pG#RF&IviYz;Xf zzHMiCvxiJ@4ic!)00|pRy3`5>mLt-jX`3q0=0plK{fe!Ddjo4SBJr7irQesE2b|=S zMLYLrkH~wbSm@9d7$SPO%QEV`Ij(lS%A9wIXqR!M;~pVrSXW1Xz1 z@;a>h9#x(e+kPd@8Jv%C!1w{DG-v&HKcE81S}@NY zoMWLW_S^iBaYW8B{T`H0`?7(#53FncKs|<1fTq};NQ}rhrczK#XbXgO7eG<1$nLa+ zi29`O7&h^C>-kqEBhrrz&1e0NR>MB4w1Aw;Q6L^2+JaJ}v;Cw$OZNM@ILJG6poWzd zFj$Sw`ZyOiRfi9kB~2J?{hMmiRNM^Z*VHXvjpA7r#_t=jby-C{P*XT`y@&W%r@=M z()sQv(2;>cZVLaFn~-E7!<+H{gJBx(?+@CJHKi&3t%ZN?_%~XXpbf zTFJz~$HM@)jicn7jFdp^`F}4nG>6M~F3u?V_5usl$rUXWawTRw-THznL72gmmVWYs z#R>#bSuD_q)L;&kS5|ZpGQ^u*m^g*Dk{w?Ma5Yj}hg@%HDi_5$BH>qapmb;)Mwy#< z6sW!!>Q6E-up(Ib3$<|Q(F8-F5U=r3CI?bgM7AE^9SbO+t^~O=8+;hD)=BZX1Rc+d zPL#m%C9vGVIa-Kb(Q57xkAWKl+m+?8nT*Klh3OVs3SLT_3@rBcwjpq8(g;pUEEm0P zg~{gC$&##U!bs=U0h5wd;i@+zZ^S9XRyoF-cbOqiGOtc*K3vG-)qTx^xkqI1O26ip zq9&t2;kD#_OmM1*xz94WM$pY!M1jtFOQ_oJ4y#)BK_|hm{xDCl4uIM; zOfB6GE5PPY5-lyF)P2Q8$;GaY0(({zBBKIR+euQjVo_OVDs@)hnXj$6**h66)x3#f zs6rT%pYO%Uoz)jT7o>Y82nu#XMT#gIjEQfpIY{_AGunH|Hoz#wnt)`{D2i} ziKtL4aR|MaCeKQ2reZQ(%u9JGiPpP`vdolF%cbKz}5GC8< zHJI$S%2?^OsEgK!;>wpw+^UJgOS=j-l6}c`GFD2hq&%c#F4%?RBPdGKmf1TS5GEQj z5f-||?!?Aq)w)XOQf7XQl~N0Rfd(!;3yr*F&!(PuXvr+Rd#rR?5CPZ4>=(9#Kh|68 zffyf&qbVtGS#K$ERszd*5M!myN+1PIO0q0dZ93%a=pa#+fn@g2SUIwkOhd0!I}x(6 zldGBmM7n^54L%*1JVY8$0E=$;s zhjZUvVKWuieR5ynvZ4^F1Z`KCs3)SR(lQs;O=ZAJL4|Rs3|J{BAJ62!3U_!z!Sazv zwyUcHgzl4vC7g7*P0E;LSBlxpehC>7DAZR@Cz9w2EJJ#|9pWmoNOGmJcs3HmWhU&H zELRx}dlb7Z8M%WZo0B9K z9i?zwYe=I*h(Q+-TXLp5gqU(^N%H;pSh=hS%6pSER#yl7M(W*HcjaVE5-a_TAOZMh zY!uj7NvxP!e96L2Sw5y4pNWE6dzIvWTF+Mv9-e(?s@@DTWJ(5-fzSb?SB^Tg)`Y3wBJ7{TI~~1tmjSZ#t4cuV5sR z^s13zO{X1VO2tafHlAaWUm2X5xH*tCSa@Lf?K{k_SDE_U?74NE3|J~HT7vRatCqob z$GI3*tce!wjWJ*gyc}qK0b^2NsZ3m`f8Bve*vS#As%w$1fG`i;&A_qpU!nhbha_|U zjsxoz@1S!C0p#)#Z3Wjr`KxFuVQNBH^`9B>{bYPDnhJHT=O9~7JWg9hklgC(KsmxI z*^u4}o(xqnc`Zn8B@^Y(79{IDShBSwwH4?F_&g|l=igim%XlX4tI&pYr|5LboseU) zTgg=FZtPIOXXhqkvRp-&2$bRK3MCpT$+e?6EMY}M1+2z;;OXEUm|?-`!&7tj7|vv@ z1XyynCM3gh#D#f+0Y8q;Z9a=0b*eH9S-US(I*T(<%n@55aS9>vyhKdnhVw2MPrg>HB`v4yg^yya%DHz zA(c|YEu z*+bIkYIj)35l!k!1y$D^#D{kZ0NFrN0Q~;A1-Sz2_I~D02**mMMbzfc#qKOSj2kPX zmaegI$|5v=j|Hzj0F_+p?UiTheWu0D1-Z4Zx|c|xf>v_369F-Kmy%cON{GE@Z;0~2 zcv);o-Me*$4cWE8fq=Ork0P<@s3&dHHKHJceyX$!DYl@+DD z_V9d6My^uEt9{{wYpk4HnbdiQMj*92-XAMDSC(W)Dq$>Qh%9gg*E0m)VzXyU$pSKz zV!?qc6he8rOd*8$j*&HBd3Ns(O~z#Ha;@UNYk+~$H4+3?9nFv zBy_x#y=qtb07Se(4it@uKGv=1>YKMR$C>|fh8Qo^qlVA8)y2l%|7r4$1(#c+kfM07Awiz$pMG>3_5OkvME>vKFJDVMDRq`Gk)X>$gRNG=8qFwe{KQMNR)NA}?nUkdX7@=)Bcm57ZNV2Q=GO8# zQra$l#$B*IW{qwBkvQ4XcBK@+9n9#wE;FywLDl3Nj_X}kCY*M;M&o49VMfO>Q%cLq zM!GKB1(cSeVRD$7wi^{vo~dkRJuH_dyV?{c1Fo>Z9gSssn?jq_ z_d>>*W+N=iSZTOYX$W)4r6~uOhvm}5!b@Q~8A|my5-Tsowm&iO);8ARh#XwDbH6P- zBvW-uM$*BF(X1`i#gP(lrJ3@QaGt4AZzz2pJ$iIx)Z5d`47^3#IUg+Y8jh4)eHD6S z^c6adNOG<^-p)&sa+R=PlL6P}o3&hyNX=D3(`Y!}aTqS&x*xD^IAS1;-=UnLx|9 zRu*SqEs?_-h1!=aHyTLe)mI#cXxT0Yb0lpWKzM?yH0Px`MJg{Nv4@kz))tiHUJmR> zE;+soB(KDY)v7oNe0DFAOpZ>hN~KTUQ%)U`)5~PVc{E+@4f#GqS$!&@l3PbJdURs7 zY4wfElEsXd-8h8XPk|dK8qtSl(gE=B=L%QR2zCC`_>?<+JwsA z1r!TZN68*`4T4s>o9GS@cPU*orA_J)DZKiL!gQP|2sHxN!P6Q6sQILs7`@dc`FV19 zE+lyQWF)ayo`)XA!jho7>dimKJ2@h`SDvb?6&-wb;*jC1ig!?B9(Fb0zqnBwa&Re( zn(I<*{$vX~q|tUAGRQ6_Fq`8p0hDtt+E|VqGKjWMhM@V32JFB==er^s8?u9f6aZd8 zE;;^<1_(D+Bx;o#rlbSwy91DXc_$~Bfecv`qy#hI!a7f0FoRjZMbp?@T}Lxx5~5(i z*@?xCiz{8sws}YqW~3I*laELgRseBEIBCY*L-ur+(0;9CZY;h4cM^@M2MBy)+g}@( zFFC_Z0pV&Zpp20RS>f<)wsH;vl7|^_!T*vO)1(nIz^0wQxj923F@q_u)}#;1V7a=P zi#pxzW}rCCyI&c^__b6J(VqOs(OOXZbMXNZpy#{!x-o}#1am8Mm$QKlnyw%yFoywF zYq$mUp9%^+Rgwaora`_S2#(FHrRMR$ZI5i+8tvgsg zBKKGg{GB`;aDia=}9s7isCf}hct>$%77ze z9;!K<(R;+P{b5k$5{>rjkxrgbemx2|3VmJ7ZkNb>btavet**-HKpW7o%d+!NFn_yIjfMoa{2vY|TRp z)CPyLzB#c9wnbLrHwUZ@jQkoR<}+5^fZ+{@LJAx;LA*E1l7y&}Vqu^i`I^ z{7tsW@z^NO6kXU(ACHX!&C%10HsW|}jM*-UE~873DfpPu~ zNmE?5H#t5sDl}sKKL7a0C{V#QGG45ajFYf*d}V#`~9OY59DR@S%z(48I~ zD{EW^%+>I*F<+&m%v$Gw-*qB9bfhZ+imO(!Fm7OjFRtCTF)0NS#q|m_h z;4mPVYlLjTaSu%#xS2=p_|)iOO;)It(ZY)2W+Q|sh7soqVzjYI!!Y=A8l#ytP2P0m zwd;q8qAKl1io2_W$A^dM9dF}ZJU@S0fBQrh1Y1~A-C3LsggPeQodq}nqIH?k7-1X(<^z!TdAJ;{w_zF5GkWx#Rt!UYZ`E+~v_vyve z|NHvzSVW7jphI8Z7BOPLc9^mpWxtDoAuWcSS+s4Cp`~FTPxT zDhm_E+TqYq7wm@H`-*0aU#`ED3m}>^Vjmta?jOJ2O)o!PU)&dEi^`1n&)1)C?~8>{ zGQA#ndvzV=k{xAAsuyrK(|-f?r8symN&^Vx%6x)K3+V0y!?2LIbRU0qxTTzvXk9*DD4dp=+WP~Y8r`A{5;B;bqf zg&}nF`MQox^=QCeJU+76Rq?4ZMOm)zA8$VVyADtF>cjLOUvD4l#Zd;o(9Dp1`x)Bd zYI^bY>ZV>G68k0f?ex?;iNqt@=hThsTCGU|m}K(hk9R9RPxUes#~2EiWMdeGL!Pw$orw{-BJPa3OHnhZ}z^|@9lxC^Y4`#^vLb#A)v5??ldIhakwp>U| z7>oP>Mf~OA=8;%Yo5l7ZSz$~96YDRfv1LDg0lz#WhqJ z4dZ|cVD!`V6}4`$J*_71u_19`)F{_spUchfU9n2wXzpThMb8d{qs=Sezh8YAk{s<{ zS+*}Y#s&(VYIjV^chISdf1KVwTnvd$^}B+vt}pNZeOI)LnpDBxi0Gk-)L0!)+xzbJ zrfhOGsdC3~90sSV{YJ1=qh2`_8kKlulMl0yOj|#RF50xYM(4q9gcy~FBP|+?{2Yd6jzI7s>(4$ zVyeOrS*GT=Sy@WpRELYPa(D9u>;Bg-kJnfyF77WrU*o6J06DRaR`tG5I5|yo}yE;m&?n`hcZ7&f+vjr1hB~vlC1z1CfCcY$@#XXwuqWKS~ zb!u4A!h(H(_pp+ssNYLGGl#nx6^LkEmK;9fPiv4{S8$?m1MXq=y{+NIc$U7bLd0HH z2y=vUvYaQmB(Dw>h|w%Bh*yxJSFiLY6YhXs?DwEX_{4^k8~>G5qG5fiJBDE)7HPYy z6-zYj)f=C|U{F{^xF}dzL*ErB1~ZJ^kJG=mzkY9PQ-Iu(R|kUozX6me)a%RX_2#KX z{Bd>mky{etG=}v`172&D5nXDb;|i9nlMwqIGyfr6(Z>js@UB`4v4*d|qx*)Z5IHEX z4nE-ZCc;;mWjuW&iNI`c?Pd3Ask z_i}-;CRbD^P!HfbfmZtqaz%F{#UqalE2R~M`SJ4wOiczm*I=S61y+$Git;1e`&}0e ziBsZAqAU3+ZRJP27tFV*eyPg5Ac;shdvaJ_9rXA?y@``QfNvz?uqICw=M6?O20UKe zdVXl1myh)>JQn3i-E$2xyDdY0Dg_k|Q__wPn|4vRTa@|I zr>SKix)b2{+EZyTJ77hcl3?D>{{#NVbT+f12w$xQeCW+43j|7+qJyK&=l0r zFgg_lNf`UPCQJ0Af*3bj?&nj{iA13jITh~KSafT;{8Y4Kg8qY#`qe~>j=aO!iz@!w z_)yMF&6gz@lBpb;J$AiCqzF2+TsfuGbm$sZG^S!hfiFScQbUW%?9gBmY`0ugwl%n@ z&j8;H=y}p@UL7c~vwyI*LEB<9)`W`+)ihY#2A5W4+*B;|1Q@2O>-L#wQ-#)BSafLs z!KfRmnkdr%{m!WhMOmqm5^^dQF2{23Dif!II3z15(V+o_uzhe;*Jg}zat3H%^1DN< z1wx{0<3JfYH3mGtsLGd<4NFE`L;uDTZi(t`>jR zGZ$6*k2k!&P-#!Hb6y?L2QzHYlAu#@!Kkgt6N{62Y;ZjyHz&lky!KO*B+Bz0!$he$ zJDcnsvssBcJMhJ+KM zMooLO1{U-4;puJ9%RLp4a(q7UYF{}^W%v}sm5MnS+BL8!Pyyr3n2JDAn-BZdcF?p} z?oXPR*ebr%8boXmzVY1?qTgz`=otM9fB0nj z88^wEund}$>Jw6Zs>^5y6wkFNYIItWMXpbkMHpV@DrY&5uFpYKbb3-4*vu-zqOGz7jcxIK3$_h66In&u(!A9?iLFpc5l^zTc*gcBCn2qEKMA$ z3cmA7lDITrURWn#B|%&uu-h=Mg1UxKW>mJ3Pm{P8D%r(#14;kNN}wdEAur>QsbmmW z4rG8q(s@YAXr)RG(v2UZf{)7wq!+wNl~i#F36-ultum#C0xE1cUxPvL;iji)f2UElaBef`pQYZl|>)W4U z4dVMXg8@GMG1_3FxNxMKrNI<&`LG9EgMBxO3y4iL9!?ck5phKgmMZQtaS-J;SY4%?WrufZqDx~aJEfnMaZ)@*984S6knWge5FPg+ z0WF%BK?sAX;%btH8Vsh0TM~6%4PxW!f!-{f9uB6BO9;)H{6upwZCp{f((w#(5FHm4 zW`pp8L2z77%yw@mi4xy(40!*@V5+#9h~H=s8rKkRDfQT65FeKj-Gbs|V-Oyf55nW| z%V3hYeCS?^RX}XSV3N2c2}#s-JD4P{Bbo%A9enbGsp5j7wn-O`F_ucs7ZWq2YHpCzBVnX8sb>2@i)c*A2y9@>#ItFt1XqxPpk4 zQI{T9i!M~8A}_8C-7?-EHdIo?)uC6e^wDI0*?2k%V?!DriuZY_gO2jsI^6C9T#;qaPd-eMP}5}f+2{+$8~)cgHL6Xv25#`oK7k#=Br~c}*@e4tI^x7>CBj<;#y#S?(*PiF&!q zAzak1z@t*`3g2BnDC3pVIF<#i|8Vg~Rh=ojaYWyv}Qjj0sJX^QJF7r%YN z734~JQeBLDXK4=7w3TG3R(8n-+e?X%)AHSYx_JC>d;hssz*J33PS;DfAi() z`d^hsQYPh?0oOgb*;y{W(kUA>?>$z)sdCp_X#@}I3?*64GXN(;Etgc`Yc9OzSt(Mg z@C%NjODEy#dPTQfIx;!O zbz9S1-rRk}HLcI}HY2%nOe6#q72)aPARHAHT)Hq|PaUWZD&TZkAeeWvD$sN}0QAf4 zm&;m%lK?u_3D|*2MF!{;dW&_5!~&HKZ}mp?mbrAC;mgHFOXBf=hliXsY^yi$rKf$OTfa zsPaPx>6SdCk|5gSx@U>u5%s;45=1i(P4XB6A}d*>%3jJ8DM~5{Qe7_z21XfaqT?tN za8FcH7O!N{p^d>h<)4o72lpMy zKOM1y%2Mg4iPa&kG1KLuEBUmS8)#%INuq)mXH&eIP(i0^{*xmNsw9cZo*B^5T%W$m zMJ;s&Hv0XmZLTELecLdHIylw&Iso38Tgz1Mf9j?Q)`0gWlDOc7r)(rY3rtDvY#nObx475ur9^F2NvNoC9Uog^=w1MEWJ?N zhbr)NKTzNts-%jm13CmXNoj{FDbn45VIHdB)5QTf>C)u_ z`E?h}g@UM@M2W9sOVbDXemf`$c1V=+303k)7mG+KRKcbzL}V4JVB;#GR#hN1s(|CF zAiXV%Zr34|6zReMvlvq%lA=^nq+0_=FjPqqmkN|hQ`BwlGv&?>?cOsIRYb>~Ko$m7 z({Q*FW$X=ZwMcn1wB0%z$BjVe5|-fja-oz;JJbMnCv#wyH^uyK`; z{tc8$6}jo20Q2kLpTmAfnL{dx+~%OC%Bb%?&c^AI!I|v%n<`nQ%LZHNFD8qNhMp3K zn5lYi$F(8^5mb?+-c~y_jq0hxqYJ-}G^&H67hgIqaQM_62xrR19aUCHfSf>9Rc_Ct zbbZhbZcjrK(pOU2HKHpVx73vcc8LfHNR3s&r%MDBfbP@#(p6HW3&bO5P$1J9u8dtC z651;SospiXoSE`__wC&~JCN)SW@RN=>PEuQ?~|dc>Y1f$g-P}Bd0flMt{EmLG{~3R z`&#QCrz?obZD=?t7kFq8^jTlCN=u{*iJ=>-;NvpV1tU}^k8*>vDCLNwdcx>i>cWlc z(72HJD6MJ|vW2U@NJF&h@VJD;%Z25ZIf;7>l)4a{RnH-=B;Ad+N(ONip(UiuIModS zP7*t{D5~rFOQayXy|}KNSDh)lc$`j2B21^M*{%HIF%{R$FxN%JR%(;3BWk_GM4$gr zapIgQy?D&3Qz^fH7**z<5{vr;@Su*S4|g~_oSvN4bc+&=7n0WI`e|HR($yl#3*GqoJd|ut{8z!Js|?Is+BRL|l^l0xr|IIN%Mzo1hts6%iy;B5 z6e?X``aC)=z)B*!ys%L3B#0{uu^|efK5vwnoGmM&d+wWZae8)g^f}C-!1Kj9!1R?w z=|X~23;>xxxIrr^(q)9%fRq!5{gP}TpL7X9D!H)Nv76P(X7?hJMEDac1xweDE{h?2 zTtCoW+ULw-a9o|G8K z=->fUYLc!sVibi&uQWp3tDq55YRU>cZdH6da&^h?DcRNS8RB{C_;h9AEc+sKsW=^f z0CDPd<+Sw75Vh-M3;L;C6T{h_QfF4+>7pV_ct@Kn)RL7%>B6E)c4%koNZf-4gG;O; zJzY{_4F)r?>q*DcI8=kUpqPvP(f}PY@Rj3CXVG;!4pIc2x?St`Vr&f$L$7`6wn~J9dv~mN_XKct;n@20Ja)~ z1Iy62qZsevqo)c;$4*u$P^$HjLl;H#Zu6%~KS?*>5u2xqpFVMASFMyQs{QmvLUs5X zg7?J#Jy4v5)B4e8K9nYUtd!)Va(VTvXL%0{9#vpe@`1yn3XF<9@N-mwahsqF9aUVa z%h7>VT&lr)L%Is@)i=(%LLh7nY^d9lP}nLybu2M@u?kGL0F0Wg0;A@}Jm>QWRbW)y z0=%vRqrxVT@9|VcMwLw@!dI)nRAUp!)4(b?)z@7JYZaX8=roA6njqEE@xh-O+7Pv4 zdmB!(s_@i$znI2Q)+#<#(icPH!LYN(IMu4$R7u}V`$*Mlg49Hp1R?OWnjq?FbiG7| z%Bj@54U{Tt$){7I)r?X#Eh#kYvx-l3v_t^*fuGehQB_N25Xy|$r9{+1gohQpnktP;>NAm3xoVoIzhh4H7FFRYc8SlcB2%S~wIT$aRN(OYCC*X88KYMPr#c;Ajnb>4Q^gLn zOq(h=Rqg;M@pRQBshST-Fq%U|T{Tsz^O35HsjK2s%?~`H>Z;IG_X8?%byaYx`+a0x zHBq_-*x0&il5`cYp>MS$%KRQ(s(8?Au)_J z1nC0N=QTX4#&)5I;am&=t7eog8Q5mI5U^^pbp1fGJ_xLqLb>e2un2Ko63bLg;FggP z5mbez>xh49tePlYPhcRVVL{b2>4F0HNr-xp!-#5{bUD#9Lq^EtC|yqa;GnAbbV=#q z!`9K{$gVA@h>4u3W|polSigF|!k|@fl&&!FUu$s0kc~J|vivw?LK1b7*Gi}S`5}#( zd|5bErRz)~Or)AFU25dqpvx2IU_1+{ zCQMfzpO8l{)r9FfWD>s9b?Kty(+%a9E>A8Unpe&_YGqY6|9*(|;U7F!sisO-E12&t zR-l?JU9U_sbj5&E_0+`O_45I_E5i;L?03`>V(I~p)ISa(xuBB>{nBys~41N@F91{ z;?-n!{bJGL!FV-Ix_%+6a73g}cvbsHUm}PTMH0!ebv2uG3G1>MPLZx+s=ArYdu^Sr zV(DRbwP5M`WsbY6DeTf!Jn*ii)prbHyY*+i?5k>`bgv{D%^rGJ)42sK9{3`&Ayt3o zRo&?G~8S|33uG=6^oUd=CE#IQ`qCaES! zSFiLCyqX|gwm3oj8Fek8zL{WjQcxGw6zO^uPkC_yR!v}6s@{Uqli_MIyHIh6zmvc& zQ9%{KBUa5JU6x`^-pM3gjp8wJHHUN|>NRl{o~}ZKheOCJK3#)=?~fsCDfAtN(A{zn zSw*JHjy}_UqH zlG7ZpZNWvmS>uHPc?#x!@wk3$f7&yznNzNCg-xsB-b4u*Wo`m zcpT}c1Iq&QEnbiOi09CrQ4}y#S)e}T0N^$hw~mx01jz7s#{-_vak6Bw03w9=6%ev4 z^u^+p+xzvimlM|k4S;aDM0J0Kr)~B=ZPe=5KikDy_8D6c?H(mW3lE3Yw;_W$2vl$iVSZX2 zhlA!-9PX~7T!tSza9z6;P`asPlfprvZc}IyPvC%1vuP=ME-iDiOdIgIQ~K^2Zd721&1&M3+3YtQ7YQE+5`(IQw8e4Ee*8= z^k)!uYslMOo=}<*R;1~3c<9TB_rzol3SAkYNnC|PK{*DLJ_Yx%UvJyLEuirBo9%k@ zw?l`IYB9{X%k}2h033=@#i#xHVcSB5$@Fv5p&DbE|I!d&KtF1VFCd$nGCZhW8a};{ zfZwTV13eiFlk(}?GB;3`5yt%8XJ|_Exr<(MFsRB1t7(7+8Z&~@%U@0wRA+?stc(s6 z6)1FJMy%+tMsIFO)E%fv9WH9lw71zN#ktu;6`I1Skc)6Wh7hZz!Jgd%9kv*4s!$b3 z{UBtA4jEM{8ev$Gw9$YK>(SWFj5ZpqH?J#ENW~%EMl+RVDy@tCg9b#D*2Xp(qM{+? zNmW`Wu5!`-Bx>{9+j7}%9VD8Zy`ZYy&_7SWaurwQWi&I38g?0t%4l`j^7Ic5kPzYp z@5Jt(b`BCPMilvew<$rQmDtxR*g8UQ?gU2juqUAyKu1pmWHayF>RCo(u+P4pvTMDfeAZoc3TyKS*wLZlc3Vak5oB)-7o*n#H+L)goMHLrEPH7oCKZ-goj>BSH z^Z8NKbZJ3Rv9egP?)JOgwB2sk+vz?Zgh!(N2zmWJg(_;M8?oB$=syzGm!RBd(R<(WNf0&1 z@89s4CX`sSewn_$z0930bABwUj=&v&d>m+K~8#d_bwOK7%Yj5Dq zQLd>(1MwDKuP#>eOrZ-{)_>0y_2D zef~ve-g5J{cNX|W<{f_T+G!%QjtTq+nRFENG; z=N2zL|7tU-GU>j(?D>9<&rzn`%b)z_a+LLTvEFXqHv7N9px*nAJeOJbyxQ%ji>DS( z-M#uv^|^9G_YjwbOhJr+MdqMCKNsi7?#Ikc+jH@POs9OL*-BFy=O?zo=56OzF=cJm zk83AV`I+V0D%;4-WS!KHOSdJO$vl*AOZezqaWEO1gJwVF5=P%3_KLrzZ>Pt_u{_fW zx>;>!p)ogJjVpulR2KH9X5~iiOr|Rr{hzx9)b+|u=b22~B^K=fz;chAR<12bi`*Qr z$o#fnW2Hx@7Q0^=kvEH#tk8sKnJNcz2idPTP=Mcl0cMYF&XxTbV+>D7KjF!6+mdrk zWhjirYDSyG9r>J-*tj{6itT63%q5liEJ;PW`zmw#R_UUDE_lTLi>QYLA%5H0nka2CAdepuh0l_Y+^Nrar`Zg zQ3r*BXb#@=A@`lUw(bC;CpcoB=8&^%x8K`>CQue(0a)j12~#oO3@vwwe&WO^ywl~Sfl`M4gir^`!p}lR#N}=sPp2C zChKpHnUpc2Tx9I-a1J+x9gNn=)u?j(qEv-SHKJ#}Z<^@~G~X-S`nJ;K?u~s-Jv80T zpye<$r{8Dgg|!irpjTasDJ=A6?S@z*8iyHk-V}=_ItOy7R;NVfRa0u-kXHpF27{z8 z)V~$8oGD8m)iQrx%%snyeQoo!YN2-sDM8kTty&Bns2e&Zb=0Z_K`3Q$7UHTX z5hcULvEvh6I{c0buK)Dr^->hjQ<%#;-9zNMeJQrdW-?uk7t&ZSvmG9jDpYYPmdbRR zU^naSzJ!#`!e79N{UW1AvTxe&&Qx29QsSc8>o&|YQDU|?mD85Pd%D}tOGA<5wao!! zJ#4o3?KcTtxM=?Kw%fm8SUPEt51_^j0bEXuv zn{$*;bO5yUVgrA$-FDBEl-9s#PdHhkw+u*SVdS9zZeKDx6g#Pdi!vx?E!!6Z+qKV2 z=qtCuoE?g_gtolIXuo)E9-lH#$m|eGtT_m6h#J7b&$C)IJsxLr)qy7ai(NB2gieDx zZMk$jKYoQR>9%3()mlGMmcBoUR#QWGyh!Q5NztsiHmG@@yaG-Gi5BKui422VuORlzv5 zS-fp`+1jD}p`~PM+PlsZJy=BhgZY`Vg_f}MH|J%aktj3_iqQTNG=N!d^H)~TROSj1 zZj2y6#YPUR#$eLM3HHcg)e)&nToT>fBru%&FKB>jVPxrT&@(W`ug(nrYEZVh4q)rGbBD7m?4S0ZQEJmZZMpS+b!n1Z4uieHFDz6zlpg2zO~z5tT(gXzj>MBDvC0tbOPQN z_E&W6Cz5H0VFOLS9b)R8I$^+ng5CXkT}-L2pvG5QGoCOcm?ybhfQ1YW3m%f#mY>Op zjM=h%ey;pRmQ;#~L|SkKka!ZKByA83#|^FlWb4dKwh$4_Y+rV6k58t>Bv-h{b;!;a z|DEpNUSHei>D=4OJnL|2D0?%Q#*WdL&)6P1%w~3wMjV_ji`iy1eerJE8IMa`X{8{Ry7ad^q1ikHn$?4wvDde2`m zfjBpP7IqLb2?xBbsvh1LxvD=L%noD9V$N>-k`ywn6xxfGT5s|($73D`WqTE&b}Tb* zn!yMnYhGs4AJ{jpf1oroUo$(Zi^*Ii?HFg#Uv{ghHxTD`Sa&dQZIWy;f!2IMzVH9NZ0M^j-pA z;Mm+P)p{Mp7A28iSUMN^xW*o0x%xKM{q~Rb)|nWKd`O!VaB3{4g=BVIb0(N%^Iu+d zEbI_ZQizj+SzxE;S}Y&$As0y#%a$k12Y;HdP&zva;EzT;0n{wEQSj^V_MYz>I~x}5 zB3Kbli7)J2=y9&5b}kU6dOiy zrm_&|5v!}09lI1uJVu<~hVoXElgv@JZJ%e@V@{!Jy*g{{C>Gi8q_Cq{ngb@(Gdx?a z*mm#|NH^eijU8a&I^nFfgP3^Td=|lWCcB&C3 zt^#&w!*9$KY)~5e4r>Q?kF_wpm1Q>JKE`_1VY;Ab&ZBH*5Y!y}?|(6L+T=6WW7*|u z+rGjg=_FYUi5#1Q?NhsZL%?_GqRE`sD4K&GtNjzE^Jcw%t>Ac)fzr%YRY}J(<`KyI z_FTm#8zP7Yto)4qj$2^IH4xO_%22zb*V| zVu-zdA01X2>0Cxp3O%nN)nAYz;RYYWA6I5Qk<|2S#lA9h%zOj0+lrefsY+>k?ZqHm(1>nY}sb z=*Q&q)BMPNM;G>(14`LCAY^nSW#5n_q}jTQNAmYt{t=)=U#-t)q8`dm%*t zMR46^R(Uq(V`YPx@&qpb+#Hk1$%t^70D;j*#O~LN_%O1n5HcbJS{Hldf&lp%1vmg~ zFZA-&0n%c1H9gpUn(lnaDwZYDFLbNwl_(p@4o+DGK;*RWiN{0Y$maKE3x{QDR4_FZ z)73%q_VV&eJ=p*zT}%W97ZC0I4bC+* zoHVUF5f~tdZuoO0m1Ov}DZoer1Q_MCUSb=N?#__m?r`&h^X#{Y6;mboXS;eIAFMyfsx`$Dh(t9 zitftnJFgibNOt?*>2CeD#ci@wk3s&abXM}+ir04!5pVbOKleeFJvhGMox?S?*Karj zHydHfmW7WP`i)8eS@h_Gy1l#j#HTVYegg;QXa0Qs+wJY6%TrcAoGZ0kC^p0=yU%hA zCt=I^y29efl<1OoYlD?Rm4g9-(1It}57QQ>jW*^Ua?Wt%4Z8_@O4u?y6Ul3ShLgPx zQur!EhW-PLjZ$hHfWv^H-@x4dW8N}=)de6Yf|xbZuTwGdU1cPa4!ahZ^CTmI3(^86 z5}eUX!v?C8=Z4utX6sCL)8cN)YsFXJV0i4l{nuq8Z2>jMBYSE@9|MJI@%c9~*>?60 zjC?xACmG;4MczE47C)!^**9N6X;V2vEEG)|G#zLEdcY&>d?(`O{`wNMm;ZESZkTDF z;QspI+6~HyY@u@KZ(kq&?S!_AvQVG7OzBQBbay(eEUv9k9^nQFZ;?RN+a@_`WGr_W zT;Y0|ZPU1n@dyysvx8tuw|;F3Aa-n8!%ugYn4lNwX)=Y)e4`Qf&-@?E8$xrt1}C1v07P+HoKMUUJ}6A z9PS=&y4N3^2w5D{#(DVkPxN3K;(%%~2%oVla2lTD0C(3mxSS?I%!$jvm4Z>|Aq{pB z);r;$GOmMoT~mkf#~Q60@PkMOTH2;y{rprt9?Jlw{=i}W7RS$jJ2mYDfR}R%R?)kM zN4`*(b=~ne8=!=T_cxqm8J^hDY<9eDjerREU54eR!3I!bec>0vJuDYTJ02a6McX{> zUxraKK67d3HdFH4V>j)Dc~CeB;&C`G5#q81E@y3VujSAQlfClm&&lx^{?;b{%*p)v z^V|P0(~IBP^z%K=#TN)fNPXL=Hi--5D<|ec=A_GA*7kIS-;B`}_ZMFtK3v~>tk=WD z=G{wj13RjS%a?%TqfGG;h*5(He}DZCm%AvxtfV)euW!Fv)o^??XF(aRkQ;`_q{K(` zJscs^_P6r|8eSd%J`uZ5H zeRTk)g#RHr8FAV#j6!ww6U!kAL-8&@{r&0&&#~QI{qu{%$}~-}upd5@fMR+@AgWRq zO0I;Y+AP2$YK?b`9`J@+wJao3u*p7Me7T-pd;ybfR`3%s*JF8*hk)^xQVA?8P6VUy z0xn3Eevd#%z5n_}z_!^=WMPRo2)zFnde(u&TuDJ+zkIsAxMCCF(Wi%t-#)qVaw6(F zP4VI8!);+EpU7N^K$P?9vOKa*#J*0EtdXEyX+_1Rz~837U3~ew(9F6hK+FN%hNbYk z`|0)7@7L5(mq_*H(KQFzA8hv`#fN`RZ$DVe=a^g&39y?rZsd{|qJX-ywIWA^McqAI zV~E^egY(%zv&kBv57(byR6BS!Wfgvld6tP56&V`~HR`cN*<#*j_|F&jzu(}uxqMkf zYxt`iFNRA2fAb~7lB2@5;FnqplCq)#E!C1V7A1J81<7ks!xm+W(>Sf6l)Bo2^zuciWuNKye)GLgW=ju$B9+MDj7Q9=F9c{;|-q7{Oo4gBvZ(j zAAX;H;`_>WA9So+|FqB-5c`P9etCOwkJ9{papPvZ9x0_szzP0N604z60ou{Sxn#N; zk83`HqpDD#Oj&~vuL8}~^>qzOijZW4pv<8F+Ay{|$r@t8p$J8=)zs1&Qr;riDr|sd z5zgES$LCgO19)>=Fj6&~IrbjrNf~DKG^AmCmc&^dt+?O5e!iPNe438Frs3>Z{?0QE z{o%v)6dt~U_SdN6SWrm5zr@_+^3P}cZc&=dc2LfeGz}g?y1e~*_vzX?DULP5I7L3( zU0;`V1u0FE&P9`5-Ghdi4@%zgiLd)F)88-eKR?)Q^fB2^uqDR*$+jUKWC$nT^4_W4 z5+7?UaSz=*!6gs(mK>EDo^~|iOgcIPHw&1yc2sJ(uSGViokYa%vHSg*rw|2gQ)md` zw30yj-zET(X0|Jf?rdXgNEKZ$EGws5)(GQi0Nj>)CykUSY1_V<@EgdP>;0Q3#|7vR zA+wMtdb)?kNy}#X?Rh=-C0ECb1HOL8#jEtrC?t|X7kgVPagA`QlvN72rIc-o*pT6^ zwE9$d`)tTRcDy{|_7Hb1_LD#xy)@~_V1&_&HD_%FyT64>$Lp|9Nrmwk1n3V8m}V8T5#q97-d`eAJ@sDG)(F@DEaVk+0Z*p3Co-tCx?0nm>j*7TxNiIUkPEqJ16+bp)}aLqvb>? z1FY7l>ARCdSu`?muJ29`o${cL8Ps=pOU>$NrWn$9Cm~!U>J=s^>ARDW#)BB3C^Iu6 zPe!u5_mvC!juB*5<(-f?U`XYijKp~FMP(`vo&t&>XV2oLS>RmBE<-1J9fRMOElAUF z41`Ouw!C@+BOQJp9`2@hx1aFhXTf)MGHW<+BuiY@Z2#+|$qp=L$m|LSSSPwD_)I_l zb_+Z5-g+cM0phyLcv|HGygmH^NWC~J;Xh2cBUx9+>x zfZca^c-?1NZ_-NOC7Op@STi3lzF5=Ygfv~D)sU6C+NMwLpz}oOy1EPl>bozM=nDrj zmmzg_2rNI3%sy?=Nt)e`*>xj$h>3=*|z60^V6%7#mHAyh2}bTA?tlE z^x@|J?2>V^kTJr}k}&pK&GkkDVc%1n9d8R+X?MR6K#OpN5cUbz=YF+B^qm`=i#SSR zf`WthWRb5HU+@2bJ$~sX)goUn1jr+luMe)EvS_5u^B~wlL&}QEdhg!0->a=7mZqL& z7seK1vM~74>@L6J!S~0T4|aV&(OU=(=&gL9{4jvHLeXWgQoo66#o4!c76D^^ktr?+ z!s+y9cT#wwuZ6gBJz#qeU$t%7W>lyuyeNOaz6aV(UcJ`f#D4g6@$k_Jm4%I+Ao0N* zPrn?Z*Io!Vu60u*>sgu~pXJ3fwE3)TPT9u0&Ek~@Aaaq=v zSB=rYdJbO5@Iua#(bU)`x-)7d^4%LsnRjs`yEB~;>}3&#*TFQ@b&KQ_TG0| zrbG$-Wc@ZLji^v*60GH7p1ZfISYugyvs`(M>53=bFc19gtMf6R=*uXqBF-hrSwd>d z{21;;?2ULPyU0-~{L*EV_ z$7d*ExenR*yu-HQQz4MyL@y&C3=V_O#R#0V(zLRyB8W)BRQK)Q?7jpbjxx4uf;o1j z7UNbTktFg)xT2YabStGrwAJ;Ip-Yk=1ZY{Q_IeGcR2EQFtL4lm}6nZSkp}{ z%R{vwNLg!~ZqUu9eAa7SI0csr>a|+v2DFf%TpbODV0EH+mZ=Pu6;eclfd`ryK1DPU z3NBY}N~$Lr7W!U?X0nMCDcORAr-{1_2+WMBNGqt1)5BPY+N0Qrip9TJ!W!bPK!xr9o6^*}y&3 zt-g%P5a3m8tjs36x%+%WC23C)PV+r{0RF=a_fy@(r}Y-x)#nRpUs<3t(a|tlZC-Fs zHeV7>57jC|l@mzbr=l%cWkHpKKu-AzjWysHZo?yDTaVEZy95^`5H0AxP{Mk#fVze5 z#O=*I`tnp6{nBP&Hn|J@lyBLn@}|U6-F0Wqh*ZQrP7%=uhhBQ2e2nm0F~GsD*WJan z20FrXeWl@fwg)q(BY3xT7uN-~g4x$9^3xF%gU9TwfAVys!A0{e0^pz)v&H*VS^S;| zpjLE6)kkhXT8j9frG0q=6+MbO4M{KNT4A^x$*7Lkx<~^6-d>lJUY7A7Wv!X+=dRaJ zL`%K2xJo7*z;in&PDP#cpHs#olv}T3ZQ5f=?2@Vzyge#?Q=e1|FEc7&(~*k^W@|et zeN%5vdszA=`E^&!_$&t~$Fe}^(=*B1!&+W(do?%rQ9V76^w_tZYov*k%ENF>;f9$8 z@^&~lG`0Fiw*C`Sz=#@K)VZ`$Cd>M2PnFImL209US*GQXqoTYlDOb39*EMg}7D_4O zP~X3~aierD4z=4gE=#>)m&2^D{!~3-4wiMM(u8y<1kyntytXb!CFJlRm!DqJ+Kdoa zRvO%7S;qk8=h8S;c8ASjCAIP;$me2e>8o&cvDypD0pL*OF|HT#9n@bRI3)!*;@tGc zWS8y3#e*G`8oL}&oY}5*H8fZ+RdzzMtaOWGLFJ+ZJ8 z6r|o`gnCYQ&RKjmlHFhkx3=%0pxC-*hp*)eX=h`x%H_f)2fW?Fo{hx{ieK&K^-LqP z<5xS}&c@=*(61mRvP5Mlo!%ttz8fk4eziHf2Gr=?n6uSyXGfBApg-A7zq^9zmEHcc zlYJ~6pZ>G!W1KE=WN%(_6YA_l{2$MFk;_^s_&`ICiFh&S_3ir_mzPTy1kaQP6qhyh z3L;Vzt`VN;!&s1q;8%YpadsjWW83^W77r4LJV=)J-BnY=CNCfU`FQ#9Z@!LVE5eV$ zV|qvShQ2vbdmbc&?E>c~>hmYBe~CgLRpJE_MDce3+>FEvH||vd z!1TF*l=*m&FVF2`0(YogXwN=4lU)wBqaHE{gh1{ zCPZJ&xqV!M$KmO3sb-L`BYedhD{c6m&I|e8$#!oP@*B>4=kfCI4plWCCqXbZZb1k) zF7C>iX?KD;p`itkNgo};`y`+2rDG&wAmjXCIm4@0U`W9wQsb)>0Hwl)#wF?nJm{vm zMiXgCy(a7L?lG~nX1pC+LP(1S0k}?OZlO^ER3F*}uk3sW5!u?DQ87kBK8NH-pVRSsf+!UDH^|qYk zEZgxhKf@mg_op4X_{IW|H3n`-_{%Y7ToGhE@a|hW_aK#FVL__gj00EZ*4&>K3>%F- z7A|>X`-{5`E9=4~5R>k=1n4g+69OkgDoaKt9sqlF1)fdLfunD(#Wj!A5cvP?oePv5 zM|tP(wFHQl8P81XHU~n+f(#-MkYtS>yfT)tEeG2g>t)%NG&6S|n!(m+eCH|g$dC}D z1V{vg#CXY`;bgvSHC?AMu`pjRGbptR1u*FkUzt@BjP0s_yFQ zJ4Ryg@t*kK=iFQM)%R6(^{=a|tE#K3HIV_Ah%NIo_Krh^FWZR;I`_68GihewbCOx$ z(@TYs21y6A)o_W*GB~B{V0}MEI#8IU9>VHyG67Evi{$(9tfk&DhLeI1nKjenCQ|Rf zoz>=q6swl?{ew~mV0np#j5pf_72Y}}hlA^2GGVhi7D+_1IWju9rWap|iHc4??lw+| zr!AO~5oI5DR~$bU?rfeko+@n7+E_|9zIYbJM#EbG$tBx_pG>ifj&4&if%;l02L1(A zd>*Uxei8`C>Xz;h#3D*TFX@K9^FD#^GGb0eeBssC$xh_mv?*Mcc<-#~q1M;=L$wuj zag|LKzV#wGLH)3$LmNvt-BCtRNoZx?Uq>FIdJ!?Dn4o4UEqf}-qGoWlxm(M&^e{%P z*2h-(u3lly2ep!H=))qP7MAV61C=PZS}&+s^qnV3LvIa3(@oDQ?hRWkefQX)ypXRX zq4j{K@0SnJPsc{plgt`%xwZG-i(liFgwS}v^`i?CpngJ8k(a-tdnv30uUobND_l;F zXmRYF>2F|tn*$Tk&2PQTM#0I#S_b8-zOI*qW?hCB%LX#T13Mdu>ZC6DA~0%c^q=G< zI@ARPvvhA9H4syFdA7u5D}^M_TyIv(e)KZ28)$!m%N)6)uLh7 zSaR4e6$yXClh2s2_igTB|sD@jqEMeM@RX^k-CxvfF$qq0J07o&a)e>%V zXeUI+p?4V=$jZP&V+Z7m5*SpEjij)_XuLk!Oe-gOk!LoN2ff$mvJto}qm(f+ff6bU zG?tG~UVp3G^XdL+v|OW(Ad;4)Nt{KK=z1mDBi=)WuVqP0Jc_`rMHldB5>?M`u)Igy zu>>bVZGi#BJJ3GFvUOPe$)k+P=C7VK+m0=?kU z?YC>Ao>LU^CU>VUS++&%LK#4d3d2Ud3SCpN%uv43P?v6uUp}Rnd+VIpeNUH1iIzse z8MXeATRde*KH*yU2&-CXfx%*%(v-uK>1ZE%dmHoWsH4>-{Tz4qvz;{AfnJV5E@h~o znOPwpg^9M3O)XBgk7lhw7-Tuo2?XT=BhRs1Uey90V~pxMc`>fuVe88_IU8Cz%*qsN z`^s-bN(+H3HQPHmd@xQ~8%2Jx6~C@aJX9mcslAWPu=sse)k_w=zMM%}946OKjY(e! z1*lwwZpQrBI(n$O7>e8=O1DvY8O|(ZsUjKsvKC2nr`oKID_wY8S2Bqf+eMx?YddOD zrPP=5qS2xFCboGkhV?}?JQdfn^6p*T)@yNkUFf%XMmB3*sQYaK%8Dh1>||LYnLQXq z)`pf0>4_e_{HVRQS)1G={pdm#7L$?V?UK8@v{esx~}5C&{9wl)O3U%*xf?W^INU zUs*I~!cByT+pMzKD4O3`eP2gfwBLZ%UGaMzLPzhdnVsHbqhrN2hNh#W6!$y2HtUVE z5IY|I>yq~ikrV5i*r%ZeUmvy2He+oz)b)4+OHOXHOlWswyxuS0@d%$S$RTK($La93 z)muw?!7eMkj9|nqKFPyUZgd%#+h(EzAC`h6DxGfO5VMA2#cL3CMn3y_hBt^-%hbHDbl^*GI1Dt)XP zSZ-IyTa&1nk!t+5jO=o~jJOQT`ZKTJ80}%bjI>C+aXk(jRvp#PF_zA2|N6^dTxfGg zC#T=KYE4U*Jkm#dys%zQlo`FE;)Qo28@-&az&pCV*Ib7;TX$c1{Z4&7r5BUSw#T+o zRAh+=Gj3v`JsAx7%U4a%1&|m0qBOKKBk%D6>_oE#*aWa%o}p2ch+dYmI{9q45TL}= zTOJmZ#hQDvz+3sE?AMDDYY0kHU#_JnX}ul+wP~r)K0-Zekk|kOo^jkZdSC>VbNmG! zWUH=6tsRr%+efOC+YO4$@NH3-q;9nomO38Q8cMF-a=)o=brh^*s9|&rD`gcAsjgi+ z%)#nyR>jHO3UplX#qYQ&DAOuXJSC!}T&l1K^p0m)D{~&uqH9{Y6yjcoyw8e?4~aKz zJjt5vK3h%BqAU((v2!%Vqn3r%%|bFSbNNaWfdOA$mMvOg8eWu;mTAdocrfMjFr?>L z?8^70Aw84Qg(`y@vJqnFqL;ZOd)oHWmtyM@Q&#JemR2!`!`j|aD^X6M4-VsAJer8A z+hJ|?SaMT?gNK{sMrKB%!6~aeO^@T8!z$XDp0^-NO>Xk?m2Z~_MqJ7`hP9 z`mAFw2v=Tu~`+BX@y)!Dj8bOckD zi%VyA;X$T#UC~0w#{n9L4oDRn<;=RUnPKUeFh5uFWXaef{k;Rx7&hkS%K4QBiH=la zNJ27P`1qt`N-HL@vecT4A#aemFP4(o;1zRBnT+zRJr&arOg6CQW;AxQVT}&ON=svG zXa>*mOJmirkfXv&@Z-Hxx^8duNLsRsM${HD8I5Krhz?bu)0K(I%7Avycir()e*=?+ zw?4~q&4J=k%6wvX%`ASYZ@?5uZ}pUyIt=BmqL=B&j9$v|m@FpyN~UB@dNGS~ zWBpF42DA=nQh3*DAy~Yo*dU+!E@YuxQ@|$6radyjOke$;OOzcgCc3X?70QOST0{!f z#oBdo64{=NZ?oKJIg*#DCuF3y$*IRL!XQ;v!bcS!#}c5ykuT|E2VW6x)gd4YJBRstVB* zyU%!#;0vKOxk^K(u<$3$EaJAnMw@9RG+h$Wd;M6tL|*JN;EOcuHp({*LW>#PxrIsL z#KB4iEc0C{c!3%X6~nN&Lo+O{D~q*K;-kl^!1B%4!cI5E$5(z}2ou?zzMNjFF*fCg zrUsan-{iGK$&BVXvnf|jn`{D^S~S&WnXoEZ7XXNkrAvyX0Mt~;O8=nbb+eSL}tXUeEd@7u7+jU%grMTa=0&H(p>mh{3sNFoAL3)XxGj})q~}U+SN=`* zXumo?t1*NL0>#cS9tY^b$dUl^{(F;bSyRs$L@2BQCwVSMlrMn+1-kLtU6HrnnjV=o z-uxTnrF1aBD(T?)P!pB*IG~D$Jo@ED2aROUA>)s6W8tJM*@?zL0ao4W4SNe6DNl?? zWAAmlZ1k3>x0zDL>Q+3}qS@F`-HNB`?S1XhBgO6uETp95`*16)7jr7BE`>%-3QisK zSiMd5CbzyQUe2`@Y~5$kv}wB9APbC}q^DJ1U?MBEqpED0oOTNX+vW$X9X_=h@5d`w z?&b6*yVh1w$*}33wLz_Km`gdu*F2@2u4kk zy1aT~(?H8*9Mk^f{MiUlJd9Dd4w623EzbjnR-&r5$yjh*zQ%?-ldMpMVtx>>#uQ7} zI=#9ydg=C{-}%FQ8F}lvj78pIK@%G9=c+-otZC8EwDT#q(eAZG=u5t`qF|{ZRbQut zLHAH@>_aPgkO`ZM=0W>=a%N;$JKDKci<6_}xukV@$p-bBH2X0U9#3$q%NGSOZs*=n zF0-)?k;LxD^f;^mP^?lhxl-=-<71cTSj+AgEG4O+MUjTp$o?!wE@DASi&n?^diQTrDv*||J&YE0V$46I;Z z>|w!1vM`RsaXl86@Icyis|aM*a>}DQj&*t45>=k4^^*?KoJQzv|B`7Od(=~bIy-sN z(Jy=nOm@CeIx>20%Y!S>%BfK;p8ZIRF&;yJi9BAP!4S{n=)kS<^ma_+l-!#-epFev zNuJgyDK-J?gLRo%=u~)~f#Z(`4`3okh7#MPjvK&CR4C;W6+a-~ zz;ek*QyXVbS4)tGgV9n$SfWurh6Q7}d`4vwDP~{CPxuC8^>2*DVo4~Z7qQg=AE&}| zi%6?YOs_4~r~@0dnD})7M)9Uwsp22lsN)HtO?+2^i5!DG3yNA!0~@WiiAh^{4utQP zl%~+YLgI$skDZL?>%c~oGVrG2|5Jn-oEt?x$YiD>B*^I6x1o@(Qd7os@GdvLgu zP8XY~2$q~7SlXvpt81wEiqU{g&K7;?(G_$O7jyjNRSvwP)Fiex3?>c@}F;b78W9j{u_A(If{t zeZlLS#g`ciS~0n64AZb?@d=*jLb8Xyi=8xiiExWBl+Qb3@>&C3*yu2Dqsc7F%dg0( z#Z{bHH0ve6Leju4Mx{_;W=vtBWJvlQyU&_s>vFx!LlREmAS0r`w@rf*VEa4Bw) zH)v50pbkZ0z&%$w>(P?~$;c%9hDd#vsiwpcBta%^^T=b>BUT1(#I-y$m= z#`USZ16r4tSY`0L*?m`*RaOIx@HEwHZ6}jA%e9=W6oo^^>qdo-;o`IpYrTXv1{#po z(ki?XEEYQa0gKZ7SacbIQoaJ~U+K@@BP?72i`tb2W6W|3UPFyuDa{r5{8WV-36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@Jktb**NhGwz>urtW39`~+Lt_et|GrmrNsr& zApsH~0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* 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 Date: Wed, 6 Nov 2024 15:32:28 +1100 Subject: [PATCH 2546/2892] pseries: Update SLOF firmware image This adds TPM pass through API. Also, moves SLOF from github to gitlab. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Nicholas Piggin --- pc-bios/README | 4 ++-- pc-bios/slof.bin | Bin 995000 -> 996184 bytes roms/SLOF | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pc-bios/README b/pc-bios/README index 700dcaab52..a08e034fc3 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -13,8 +13,8 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at - https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20230918. + https://gitlab.com/slof/slof, and the image currently in qemu is + built from git tag qemu-slof-20241106. - VOF (Virtual Open Firmware) is a minimalistic firmware to work with -machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 27fed09f49a79eaba394a961cce5ddf065902259..4314e17b9ddc964c905a7b107d9aa7a651a43890 100644 GIT binary patch delta 217628 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 ewBp>*H+|uGYR%fZ8P^tdE-_Os7_MVwFYu2pS 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